xorp/0000775000076400007640000000000011703613112011653 5ustar greearbgreearbxorp/cli/0000775000076400007640000000000011703345405012431 5ustar greearbgreearbxorp/cli/xrl_cli_node.cc0000664000076400007640000002476711421137511015413 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "cli_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvx.hh" #include "libxorp/status_codes.h" #include "libxorp/token.hh" #include "xrl_cli_node.hh" #include "cli_node.hh" XrlCliNode::XrlCliNode(EventLoop& eventloop, const string& class_name, const string& finder_hostname, uint16_t finder_port, const string& finder_target, CliNode& cli_node) : XrlStdRouter(eventloop, class_name.c_str(), finder_hostname.c_str(), finder_port), XrlCliTargetBase(&xrl_router()), _eventloop(eventloop), _cli_node(cli_node), _xrl_cli_processor_client(&xrl_router()), _is_finder_alive(false) { _cli_node.set_send_process_command_callback( callback(this, &XrlCliNode::send_process_command)); UNUSED(finder_target); } // // XrlCliNode front-end interface // int XrlCliNode::enable_cli() { int ret_code = XORP_OK; cli_node().enable(); return (ret_code); } int XrlCliNode::disable_cli() { int ret_code = XORP_OK; cli_node().disable(); return (ret_code); } int XrlCliNode::start_cli() { int ret_code = XORP_OK; if (cli_node().start() != XORP_OK) ret_code = XORP_ERROR; return (ret_code); } int XrlCliNode::stop_cli() { int ret_code = XORP_OK; if (cli_node().stop() != XORP_OK) ret_code = XORP_ERROR; return (ret_code); } // // Finder-related events // /** * Called when Finder connection is established. * * Note that this method overwrites an XrlRouter virtual method. */ void XrlCliNode::finder_connect_event() { _is_finder_alive = true; } /** * Called when Finder disconnect occurs. * * Note that this method overwrites an XrlRouter virtual method. */ void XrlCliNode::finder_disconnect_event() { XLOG_ERROR("Finder disconnect event. Exiting immediately..."); _is_finder_alive = false; stop_cli(); } XrlCmdError XrlCliNode::common_0_1_get_target_name( // Output values, string& name) { name = xrl_router().class_name(); return XrlCmdError::OKAY(); } XrlCmdError XrlCliNode::common_0_1_get_version( // Output values, string& version) { version = XORP_MODULE_VERSION; return XrlCmdError::OKAY(); } XrlCmdError XrlCliNode::common_0_1_get_status( // Output values, uint32_t& status, string& reason) { // XXX: Default to PROC_READY status because this probably won't be used. status = PROC_READY; reason = "Ready"; return XrlCmdError::OKAY(); } XrlCmdError XrlCliNode::common_0_1_shutdown() { // TODO: XXX: PAVPAVPAV: implement it!! return XrlCmdError::COMMAND_FAILED("Not implemented yet"); } XrlCmdError XrlCliNode::cli_manager_0_1_enable_cli( // Input values, const bool& enable) { string error_msg; int ret_value; if (enable) ret_value = enable_cli(); else ret_value = disable_cli(); if (ret_value != XORP_OK) { if (enable) error_msg = "Failed to enable CLI"; else error_msg = "Failed to disable CLI"; return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlCliNode::cli_manager_0_1_start_cli() { if (start_cli() != XORP_OK) { return XrlCmdError::COMMAND_FAILED("Failed to start CLI"); } return XrlCmdError::OKAY(); } XrlCmdError XrlCliNode::cli_manager_0_1_stop_cli() { string error_msg; if (stop_cli() != XORP_OK) { error_msg = c_format("Failed to stop CLI"); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlCliNode::cli_manager_0_1_add_enable_cli_access_from_subnet4( // Input values, const IPv4Net& subnet_addr) { // // XXX: we don't need to verify the address family, because we are // handling both IPv4 and IPv6. // cli_node().add_enable_cli_access_from_subnet(IPvXNet(subnet_addr)); return XrlCmdError::OKAY(); } XrlCmdError XrlCliNode::cli_manager_0_1_add_enable_cli_access_from_subnet6( // Input values, const IPv6Net& subnet_addr) { // // XXX: we don't need to verify the address family, because we are // handling both IPv4 and IPv6. // cli_node().add_enable_cli_access_from_subnet(IPvXNet(subnet_addr)); return XrlCmdError::OKAY(); } XrlCmdError XrlCliNode::cli_manager_0_1_delete_enable_cli_access_from_subnet4( // Input values, const IPv4Net& subnet_addr) { string error_msg; // // XXX: we don't need to verify the address family, because we are // handling both IPv4 and IPv6. // if (cli_node().delete_enable_cli_access_from_subnet(IPvXNet(subnet_addr)) != XORP_OK) { error_msg = c_format("Failed to delete enabled CLI access from subnet %s", cstring(subnet_addr)); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlCliNode::cli_manager_0_1_delete_enable_cli_access_from_subnet6( // Input values, const IPv6Net& subnet_addr) { string error_msg; // // XXX: we don't need to verify the address family, because we are // handling both IPv4 and IPv6. // if (cli_node().delete_enable_cli_access_from_subnet(IPvXNet(subnet_addr)) != XORP_OK) { error_msg = c_format("Failed to delete enabled CLI access from subnet %s", cstring(subnet_addr)); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlCliNode::cli_manager_0_1_add_disable_cli_access_from_subnet4( // Input values, const IPv4Net& subnet_addr) { // // XXX: we don't need to verify the address family, because we are // handling both IPv4 and IPv6. // cli_node().add_disable_cli_access_from_subnet(IPvXNet(subnet_addr)); return XrlCmdError::OKAY(); } XrlCmdError XrlCliNode::cli_manager_0_1_add_disable_cli_access_from_subnet6( // Input values, const IPv6Net& subnet_addr) { // // XXX: we don't need to verify the address family, because we are // handling both IPv4 and IPv6. // cli_node().add_disable_cli_access_from_subnet(IPvXNet(subnet_addr)); return XrlCmdError::OKAY(); } XrlCmdError XrlCliNode::cli_manager_0_1_delete_disable_cli_access_from_subnet4( // Input values, const IPv4Net& subnet_addr) { string error_msg; // // XXX: we don't need to verify the address family, because we are // handling both IPv4 and IPv6. // if (cli_node().delete_disable_cli_access_from_subnet(IPvXNet(subnet_addr)) != XORP_OK) { error_msg = c_format("Failed to delete disabled CLI access from subnet %s", cstring(subnet_addr)); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlCliNode::cli_manager_0_1_delete_disable_cli_access_from_subnet6( // Input values, const IPv6Net& subnet_addr) { string error_msg; // // XXX: we don't need to verify the address family, because we are // handling both IPv4 and IPv6. // if (cli_node().delete_disable_cli_access_from_subnet(IPvXNet(subnet_addr)) != XORP_OK) { error_msg = c_format("Failed to delete disabled CLI access from subnet %s", cstring(subnet_addr)); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlCliNode::cli_manager_0_1_add_cli_command( // Input values, const string& processor_name, const string& command_name, const string& command_help, const bool& is_command_cd, const string& command_cd_prompt, const bool& is_command_processor) { string error_msg; if (cli_node().add_cli_command(processor_name, command_name, command_help, is_command_cd, command_cd_prompt, is_command_processor, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlCliNode::cli_manager_0_1_delete_cli_command( // Input values, const string& processor_name, const string& command_name ) { string error_msg; if (cli_node().delete_cli_command(processor_name, command_name, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } // // The CLI client-side (i.e., the CLI sending XRLs) // // // Send a request to a CLI processor // void XrlCliNode::send_process_command(const string& target, const string& processor_name, const string& cli_term_name, uint32_t cli_session_id, const vector& command_global_name, const vector& command_argv) { if (! _is_finder_alive) return; // The Finder is dead string command_line = token_vector2line(command_global_name); string args_line = token_vector2line(command_argv); // // Send the request // _xrl_cli_processor_client.send_process_command( target.c_str(), processor_name, cli_term_name, cli_session_id, command_line, args_line, callback(this, &XrlCliNode::recv_process_command_output)); return; } // // Process the response of a command processed by a remote CLI processor // void XrlCliNode::recv_process_command_output(const XrlError& xrl_error, const string *processor_name, const string *cli_term_name, const uint32_t *cli_session_id, const string *command_output) { if (xrl_error == XrlError::OKAY()) { cli_node().recv_process_command_output(processor_name, cli_term_name, cli_session_id, command_output); return; } // // TODO: if the command failed because of transport error, // then we should retransmit it. // XLOG_ERROR("Failed to process a command: %s", xrl_error.str().c_str()); } xorp/cli/cli_node.hh0000664000076400007640000003657211540224220014531 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/cli/cli_node.hh,v 1.35 2008/10/02 21:56:29 bms Exp $ #ifndef __CLI_CLI_NODE_HH__ #define __CLI_CLI_NODE_HH__ // // CLI node definition. // #include "libxorp/xorpfd.hh" #include "libxorp/eventloop.hh" #include "libproto/proto_node.hh" #include "cli_command.hh" // // Constants definitions // // // Structures/classes, typedefs and macros // class EventLoop; class CliClient; class CliPipe; class IPvXNet; /** * @short The class for the CLI node. * * There should one node per CLI instance. There should be * one CLI instance per router. */ class CliNode : public ProtoNode { public: /** * Constructor for a given address family, module ID, and event loop. * * @param init_family the address family (AF_INET or AF_INET6 for * IPv4 and IPv6 respectively). Note that this argument may disappear * in the future, and a single Cli node would provide access for * both IPv4 and IPv6. * @param init_module_id the module ID (@ref xorp_module_id). Should be * equal to XORP_MODULE_CLI. * @param init_eventloop the event loop to use. */ CliNode(int init_family, xorp_module_id init_module_id, EventLoop& init_eventloop); /** * Destructor */ virtual ~CliNode(); /** * Start the node operation. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int start(); /** * Stop the node operation. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int stop(); /** * Enable node operation. * * If an unit is not enabled, it cannot be start, or pending-start. */ void enable(); /** * Disable 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 disable(); /** * Set the CLI access port. * * The access port is the TCP port the CLI node listens to for * network access (e.g., telnet xorp_host ). * * @param v the access port number (in host order). */ void set_cli_port(unsigned short v) { _cli_port = htons(v); } /** * Add a subnet address to the list of subnet addresses enabled * for CLI access. * * This method can be called more than once to add a number of * subnet addresses. * * @param subnet_addr the subnet address to add. */ void add_enable_cli_access_from_subnet(const IPvXNet& subnet_addr); /** * Delete a subnet address from the list of subnet addresses enabled * for CLI access. * * @param subnet_addr the subnet address to delete. * @return XORP_OK on success, otherwise XORP_ERROR (e.g., if the subnet * address was not added before). */ int delete_enable_cli_access_from_subnet(const IPvXNet& subnet_addr); /** * Add a subnet address to the list of subnet addresses disabled * for CLI access. * * This method can be called more than once to add a number of * subnet addresses. * * @param subnet_addr the subnet address to add. */ void add_disable_cli_access_from_subnet(const IPvXNet& subnet_addr); /** * Delete a subnet address from the list of subnet addresses disabled * for CLI access. * * @param subnet_addr the subnet address to delete. * @return XORP_OK on success, otherwise XORP_ERROR (e.g., if the subnet * address was not added before). */ int delete_disable_cli_access_from_subnet(const IPvXNet& subnet_addr); /** * Get the @ref CliCommand entry for the CLI root command. * * @return a pointer to the @ref CliCommand entry for the CLI root command. */ CliCommand *cli_command_root() { return (&_cli_command_root); } /** * Output a log message to a @ref CliClient object. * * @param obj the @ref CliClient object to apply this method to. * @param level the XLOG level. * @param msg a C-style string with the message to output. * @return on success, the number of characters printed, * otherwise %XORP_ERROR. */ static int xlog_output(void *obj, xlog_level_t level, const char *msg); /** * Find a CLI client @ref CliClient for a given terminal name. * * @param term_name the CLI terminal name to search for. * @return the CLI client @ref CliClient with name of @ref term_name * on success, otherwise NULL. */ CliClient *find_cli_by_term_name(const string& term_name) const; /** * Find a CLI client @ref CliClient for a given session ID. * * @param session_id the CLI session ID to search for. * @return the CLI client @ref CliClient with session ID of @ref session_id * on success, otherwise NULL. */ CliClient *find_cli_by_session_id(uint32_t session_id) const; /** * Get the list of CLI clients (see @ref CliClient). * * @return a reference to the list of pointers to CLI clients * (see @ref CliClient). */ list& client_list() { return (_client_list); } /** * Add a CLI command to the CLI manager. * * @param processor_name the name of the module that will process * that command. * @param command_name the name of the command to add. * @param command_help the help for the command to add. * @param is_command_cd if true, this is a command that allows * "change directory" inside the CLI command-tree. * @param command_cd_prompt if @ref is_command_cd is true, * the string that will replace the CLI prompt after we * "cd" to that level of the CLI command-tree. * @param is_command_processor if true, this is a processing command * that would be performed by @processor_name. * @param error_msg the error message (if error). * * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_cli_command( // Input values, const string& processor_name, const string& command_name, const string& command_help, const bool& is_command_cd, const string& command_cd_prompt, const bool& is_command_processor, // Output values, string& error_msg); /** * Delete a CLI command from the CLI manager. * * @param processor_name the name of the module that is processing * that command. * @param command_name the name of the command to delete. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int delete_cli_command( // Input values, const string& processor_name, const string& command_name, // Output values, string& error_msg); /** * Process the response of a command processed by a remote node. * * @param processor_name the name of the module that has processed * that command. * @param cli_term_name the terminal name the command was entered from. * @param cli_session_id the CLI session ID the command was entered from. * @param command_output the command output to process. */ void recv_process_command_output(const string *processor_name, const string *cli_term_name, const uint32_t *cli_session_id, const string *command_output); // // Protocol message and kernel signal send/recv: not used by the CLI. // /** * UNUSED */ int proto_recv(const string& , // if_name, const string& , // vif_name, const IPvX& , // src_address, const IPvX& , // dst_address, uint8_t , // ip_protocol, int32_t , // ip_ttl, int32_t , // ip_tos, bool , // ip_router_alert, bool , // ip_internet_control, const vector& , // payload, string& // error_msg ) { assert (false); return (XORP_ERROR); } /** * UNUSED */ int proto_send(const string& , // if_name, const string& , // vif_name, const IPvX& , // src_address, const IPvX& , // dst_address, uint8_t , // ip_protocol, int32_t , // ip_ttl, int32_t , // ip_tos, bool , // ip_router_alert, bool , // ip_internet_control, const uint8_t* , // sndbuf, size_t , // sndlen, string& // error_msg ) { assert (false); return (XORP_ERROR); } /** * UNUSED */ int signal_message_recv(const string& , // src_module_instance_name, int , // message_type, uint32_t , // vif_index, const IPvX& , // src, const IPvX& , // dst, const uint8_t * , // rcvbuf, size_t // rcvlen ) { assert (false); return (XORP_ERROR); } /** * UNUSED */ int signal_message_send(const string& , // dst_module_instance_name, int , // message_type, uint32_t , // vif_index, const IPvX& , // src, const IPvX& , // dst, const uint8_t * , // sndbuf, size_t // sndlen ) { assert (false); return (XORP_ERROR); } typedef XorpCallback6&, // command_global_name const vector& // argv >::RefPtr SenderProcessCallback; /** * Set a callback to send a CLI command to a processing module. * * @param v the @ref SenderProcessCallback callback to set. */ void set_send_process_command_callback(const SenderProcessCallback& v) { _send_process_command_callback = v; } /** * Add a CLI client (@ref CliClient) to the CLI with enabled access * from a file descriptor. * * @param input_fd the file descriptor for the CLI client to read * data from. * @param output_fd the file descriptor for the CLI client to write * data to. * @param is_network if true, this client is associated with a * network connection. * @param startup_cli_prompt the startup CLI prompt. * @param error_msg the error message (if error). * @return a pointer to the CLI client (@ref CliClient) with enabled * CLI access on success, otherwise NULL. */ CliClient *add_client(XorpFd input_fd, XorpFd output_fd, bool is_network, const string& startup_cli_prompt, string& error_msg); /** * Remove a CLI client (@ref CliClient) from the CLI. * * Note that the CLI client object itself is not deleted. * * @param cli_client the CLI client (@ref CliClient) to remove. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int remove_client(CliClient *cli_client, string& error_msg); typedef XorpCallback1::RefPtr CliClientDeleteCallback; /** * Set the callback method that is invoked whenever a CliClient is deleted * * @param v the @ref CliClientDeleteCallback callback to set. */ void set_cli_client_delete_callback(const CliClientDeleteCallback& v) { _cli_client_delete_callback = v; } // // Debug-related methods // /** * Test if trace log is enabled. * * This method is used to test whether to output trace log debug messges. * * @return true if trace log is enabled, otherwise false. */ bool is_log_trace() const { return (_is_log_trace); } /** * Enable/disable trace log. * * This method is used to enable/disable trace log debug messages output. * * @param is_enabled if true, trace log is enabled, otherwise is disabled. */ void set_log_trace(bool is_enabled) { _is_log_trace = is_enabled; } private: friend class CliClient; // // Internal CLI commands // int add_internal_cli_commands(string& error_msg); int cli_show_log(const string& server_name, const string& cli_term_name, uint32_t cli_session_id, const vector& command_global_name, const vector& argv); int cli_show_log_user(const string& server_name, const string& cli_term_name, uint32_t cli_session_id, const vector& command_global_name, const vector& argv); int cli_set_log_output_cli(const string& server_name, const string& cli_term_name, uint32_t cli_session_id, const vector& command_global_name, const vector& argv); int cli_set_log_output_file(const string& server_name, const string& cli_term_name, uint32_t cli_session_id, const vector& command_global_name, const vector& argv); int cli_set_log_output_remove_cli(const string& server_name, const string& cli_term_name, uint32_t cli_session_id, const vector& command_global_name, const vector& argv); int cli_set_log_output_remove_file(const string& server_name, const string& cli_term_name, uint32_t cli_session_id, const vector& command_global_name, const vector& argv); int send_process_command(const string& server_name, const string& cli_term_name, const uint32_t cli_session_id, const vector& command_global_name, const vector& argv); XorpFd sock_serv_open(); int sock_serv_close(); CliClient *add_connection(XorpFd input_fd, XorpFd output_fd, bool is_network, const string& startup_cli_prompt, string& error_msg); int delete_connection(CliClient *cli_client, string& error_msg); void accept_connection(XorpFd fd, IoEventType type); bool is_allow_cli_access(const IPvX& ipvx) const; XorpFd _cli_socket; // The CLI listening socket unsigned short _cli_port; // The CLI port (network-order) to // listen on for new connections. list _client_list; // The list with the CLI clients uint32_t _next_session_id; // Used to assign unique session IDs. string _startup_cli_prompt; // The CLI prompt on startup CliCommand _cli_command_root; // The root of the CLI commands SenderProcessCallback _send_process_command_callback; // The callback pethod that is invoked whenever a CliClient is deleted CliClientDeleteCallback _cli_client_delete_callback; list _enable_cli_access_subnet_list; list _disable_cli_access_subnet_list; // // Debug and test-related state // bool _is_log_trace; // If true, enable XLOG_TRACE() }; // // Global variables // // // Global functions prototypes // #endif // __CLI_CLI_NODE_HH__ xorp/cli/cli_command_pipe.hh0000664000076400007640000001656111540224220016233 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/cli/cli_command_pipe.hh,v 1.18 2008/10/02 21:56:29 bms Exp $ #ifndef __CLI_CLI_COMMAND_PIPE_HH__ #define __CLI_CLI_COMMAND_PIPE_HH__ // // CLI command "pipe" ("|") definition. // #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_REGEX_H # include #else // ! HAVE_REGEX_H # ifdef HAVE_PCRE_H # include # endif # ifdef HAVE_PCREPOSIX_H # include # endif #endif // ! HAVE_REGEX_H #include "cli_command.hh" // // Constants definitions // // // Structures/classes, typedefs and macros // /** * @short The class for the "pipe" ("|") command. */ class CliPipe : public CliCommand { public: /** * Constructor for a given pipe name. * * Currently, the list of recognized pipe names are: * count * except * find * hold * match * no-more * resolve * save * trim * * @param init_pipe_name the pipe name (see above about the list of * recogined pipe names). */ CliPipe(const string& init_pipe_name); /** * Destructor */ virtual ~CliPipe(); private: friend class CliClient; bool is_invalid() { return (_pipe_type == CLI_PIPE_MAX); } void add_pipe_arg(const string& v) { _pipe_args_list.push_back(v); } void set_cli_client(CliClient *v) { _cli_client = v; } int start_func(string& input_line, string& error_msg) { return (this->*_start_func_ptr)(input_line, error_msg); } int stop_func(string& error_msg) { return (this->*_stop_func_ptr)(error_msg); } int process_func(string& input_line) { return (this->*_process_func_ptr)(input_line); } int eof_func(string& input_line) { return (this->*_eof_func_ptr)(input_line); } // The "pipe" types enum cli_pipe_t { CLI_PIPE_COMPARE = 0, CLI_PIPE_COMPARE_ROLLBACK = 1, CLI_PIPE_COUNT = 2, CLI_PIPE_DISPLAY = 3, CLI_PIPE_DISPLAY_DETAIL = 4, CLI_PIPE_DISPLAY_INHERITANCE = 5, CLI_PIPE_DISPLAY_XML = 6, CLI_PIPE_EXCEPT = 7, CLI_PIPE_FIND = 8, CLI_PIPE_HOLD = 9, CLI_PIPE_MATCH = 10, CLI_PIPE_NOMORE = 11, CLI_PIPE_RESOLVE = 12, CLI_PIPE_SAVE = 13, CLI_PIPE_TRIM = 14, CLI_PIPE_MAX }; string name2help(const string& pipe_name); cli_pipe_t name2pipe_type(const string& pipe_name); cli_pipe_t pipe_type() { return (_pipe_type); } // The line processing functions typedef int (CliPipe::*StartPipe)(string& input_line, string& error_msg); typedef int (CliPipe::*StopPipe)(string& error_msg); typedef int (CliPipe::*LineProcess)(string& input_line); StartPipe _start_func_ptr; StopPipe _stop_func_ptr; LineProcess _process_func_ptr; LineProcess _eof_func_ptr; int pipe_compare_start(string& input_line, string& error_msg); int pipe_compare_stop(string& error_msg); int pipe_compare_process(string& input_line); int pipe_compare_eof(string& input_line); int pipe_compare_rollback_start(string& input_line, string& error_msg); int pipe_compare_rollback_stop(string& error_msg); int pipe_compare_rollback_process(string& input_line); int pipe_compare_rollback_eof(string& input_line); int pipe_count_start(string& input_line, string& error_msg); int pipe_count_stop(string& error_msg); int pipe_count_process(string& input_line); int pipe_count_eof(string& input_line); int pipe_display_start(string& input_line, string& error_msg); int pipe_display_stop(string& error_msg); int pipe_display_process(string& input_line); int pipe_display_eof(string& input_line); int pipe_display_detail_start(string& input_line, string& error_msg); int pipe_display_detail_stop(string& error_msg); int pipe_display_detail_process(string& input_line); int pipe_display_detail_eof(string& input_line); int pipe_display_inheritance_start(string& input_line, string& error_msg); int pipe_display_inheritance_stop(string& error_msg); int pipe_display_inheritance_process(string& input_line); int pipe_display_inheritance_eof(string& input_line); int pipe_display_xml_start(string& input_line, string& error_msg); int pipe_display_xml_stop(string& error_msg); int pipe_display_xml_process(string& input_line); int pipe_display_xml_eof(string& input_line); int pipe_except_start(string& input_line, string& error_msg); int pipe_except_stop(string& error_msg); int pipe_except_process(string& input_line); int pipe_except_eof(string& input_line); int pipe_find_start(string& input_line, string& error_msg); int pipe_find_stop(string& error_msg); int pipe_find_process(string& input_line); int pipe_find_eof(string& input_line); int pipe_hold_start(string& input_line, string& error_msg); int pipe_hold_stop(string& error_msg); int pipe_hold_process(string& input_line); int pipe_hold_eof(string& input_line); int pipe_match_start(string& input_line, string& error_msg); int pipe_match_stop(string& error_msg); int pipe_match_process(string& input_line); int pipe_match_eof(string& input_line); int pipe_nomore_start(string& input_line, string& error_msg); int pipe_nomore_stop(string& error_msg); int pipe_nomore_process(string& input_line); int pipe_nomore_eof(string& input_line); int pipe_resolve_start(string& input_line, string& error_msg); int pipe_resolve_stop(string& error_msg); int pipe_resolve_process(string& input_line); int pipe_resolve_eof(string& input_line); int pipe_save_start(string& input_line, string& error_msg); int pipe_save_stop(string& error_msg); int pipe_save_process(string& input_line); int pipe_save_eof(string& input_line); int pipe_trim_start(string& input_line, string& error_msg); int pipe_trim_stop(string& error_msg); int pipe_trim_process(string& input_line); int pipe_trim_eof(string& input_line); int pipe_unknown_start(string& input_line, string& error_msg); int pipe_unknown_stop(string& error_msg); int pipe_unknown_process(string& input_line); int pipe_unknown_eof(string& input_line); cli_pipe_t _pipe_type; vector _pipe_args_list; // The arguments for the pipe command bool _is_running; // True if pipe is running int _counter; // Internal counter to keep state regex_t _preg; // Regular expression (internal form) bool _bool_flag; // Internal bool flag to keep state CliClient *_cli_client; // The CliClient I belong to, or NULL }; // // Global variables // // // Global functions prototypes // #endif // __CLI_CLI_COMMAND_PIPE_HH__ xorp/cli/cli_command.cc0000664000076400007640000006110711540225521015205 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // CLI (Command-Line Interface) commands structuring implementation // #include "cli_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvx.hh" #include "libxorp/token.hh" #include "libxorp/utils.hh" #include "cli_command_pipe.hh" // // Exported variables // // // Local constants definitions // // // Local structures/classes, typedefs and macros // // // Local variables // static string EXECUTE_THIS_COMMAND_STRING = "<[Enter]> Execute this command\r\n"; // // Local functions prototypes // CliCommand::CliCommand(CliCommand *init_parent_command, const string& init_command_name, const string& init_command_help) : _parent_command(init_parent_command), _name(init_command_name), _help(init_command_help), _default_nomore_mode(false), _is_command_argument(false), _is_argument_expected(false) { if (_parent_command != NULL) _root_command = _parent_command->root_command(); else _root_command = this; set_allow_cd(false, ""); set_can_pipe(false); // XXX: default set_cli_command_pipe(NULL); // Set the command-completion help string // TODO: parameterize the hard-coded number _help_completion = c_format(" %*s%s\r\n", (int)(20 - _name.size()), " ", _help.c_str()); // XXX: set the CLI completion function to its default value set_cli_completion_func(cli_attempt_command_completion_byname); _has_dynamic_children = false; } CliCommand::~CliCommand() { // Delete recursively all child commands delete_pointers_list(_child_command_list); delete_pipes(); } // // Enable/disable "cd" to this command, and set the "cd prompt" // void CliCommand::set_allow_cd(bool v, const string& init_cd_prompt) { _allow_cd = v; if (init_cd_prompt.size()) _cd_prompt = init_cd_prompt; } // // Return %XORP_OK, on success, otherwise %XORP_ERROR // int CliCommand::add_command(CliCommand *child_command, string& error_msg) { list::iterator iter, insert_pos; insert_pos = child_command_list().begin(); // Check if command already installed, as well as find the // position to install the command (based on lexicographical ordering). for (iter = child_command_list().begin(); iter != child_command_list().end(); ++iter) { CliCommand *cli_command = *iter; if (cli_command->is_same_command(child_command->name())) { // Command already installed error_msg = c_format("Command '%s' already installed", child_command->name().c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } if (cli_command->name() < child_command->name()) { insert_pos = iter; ++insert_pos; } } if (insert_pos == child_command_list().end()) _child_command_list.push_back(child_command); else _child_command_list.insert(insert_pos, child_command); child_command->set_root_command(this->root_command()); return (XORP_OK); } // // Create a new command. // Return the new child command on success, otherwise NULL. // XXX: By default, we CANNOT "cd" to this command. // XXX: If @is_multilevel_command is true, then @init_command_name can // include more than one command levels in the middle. // E.g. "show version pim". However, commands "show" and "show version" must // have been installed first. // CliCommand * CliCommand::add_command(const string& init_command_name, const string& init_command_help, bool is_multilevel_command, string& error_msg) { CliCommand *parent_cli_command = NULL; CliCommand *cli_command = NULL; vector command_tokens; string token; string token_line = init_command_name; string command_name_string; if (is_multilevel_command) { // Create a vector of all takens in the command for (token = pop_token(token_line); ! token.empty(); token = pop_token(token_line)) { command_tokens.push_back(token); } } else { if (token_line.empty()) { error_msg = c_format("Empty token line for command %s", init_command_name.c_str()); return (NULL); } command_tokens.push_back(token_line); } if (command_tokens.empty()) { error_msg = c_format("Empty command tokens for command %s", init_command_name.c_str()); return (NULL); } command_name_string = command_tokens[command_tokens.size() - 1]; // Traverse all tokens and find the parent command where to install // the new command parent_cli_command = this; for (size_t i = 0; i < command_tokens.size() - 1; i++) { parent_cli_command = parent_cli_command->command_find(command_tokens[i]); if (parent_cli_command == NULL) break; } if (parent_cli_command == NULL) { error_msg = c_format("Cannot find parent command"); goto error_label_missing; } cli_command = new CliCommand(parent_cli_command, command_name_string, init_command_help); if (parent_cli_command->add_command(cli_command, error_msg) != XORP_OK) { delete cli_command; goto error_label_failed; } cli_command->set_allow_cd(false, ""); return (cli_command); error_label_missing: error_msg = c_format("Error installing '%s' on non-existent node '%s': %s", init_command_name.c_str(), (this->name().size() > 0) ? this->name().c_str() : "", error_msg.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (NULL); // Invalid path to the command error_label_failed: error_msg = c_format("Error installing '%s' on '%s': %s", init_command_name.c_str(), (this->name().size() > 0) ? this->name().c_str() : "", error_msg.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (NULL); // Invalid path to the command } // // Create a command and assign a processing callback to it. // Return the new child command on success, otherwise NULL. // CliCommand * CliCommand::add_command(const string& init_command_name, const string& init_command_help, bool is_multilevel_command, const CLI_PROCESS_CALLBACK& init_cli_process_callback, string& error_msg) { CliCommand *cli_command = add_command(init_command_name, init_command_help, is_multilevel_command, error_msg); if (cli_command == NULL) return (NULL); cli_command->set_cli_process_callback(init_cli_process_callback); cli_command->set_allow_cd(false, ""); if (! init_cli_process_callback.is_empty()) { // XXX: by default, enable pipe processing if there is a callback func cli_command->set_can_pipe(true); } return (cli_command); } // // Create a command and assign a processing and an interrupt callbacks to it. // Return the new child command on success, otherwise NULL. // CliCommand * CliCommand::add_command(const string& init_command_name, const string& init_command_help, bool is_multilevel_command, const CLI_PROCESS_CALLBACK& init_cli_process_callback, const CLI_INTERRUPT_CALLBACK& init_cli_interrupt_callback, string& error_msg) { CliCommand *cli_command = add_command(init_command_name, init_command_help, is_multilevel_command, init_cli_process_callback, error_msg); if (cli_command == NULL) return (NULL); cli_command->set_cli_interrupt_callback(init_cli_interrupt_callback); return (cli_command); } // // Return the new child command on success, otherwise NULL. // Setup a command we can "cd" to. If @cd_prompt is not-NULL, // then the CLI prompt will be set to @cd_prompt; // otherwise, it will remain unchanged. // CliCommand * CliCommand::add_command(const string& init_command_name, const string& init_command_help, const string& init_cd_prompt, bool is_multilevel_command, string& error_msg) { CliCommand *cli_command = add_command(init_command_name, init_command_help, is_multilevel_command, error_msg); if (cli_command == NULL) return (NULL); cli_command->set_allow_cd(true, init_cd_prompt); return (cli_command); } // // Delete a command, and all sub-commands below it // Return: %XORP_OK on success, otherwise %XORP_ERROR // int CliCommand::delete_command(CliCommand *child_command) { list::iterator iter; iter = find(_child_command_list.begin(), _child_command_list.end(), child_command); if (iter == _child_command_list.end()) return (XORP_ERROR); _child_command_list.erase(iter); delete child_command; return (XORP_OK); } // // Delete a command, and all sub-commands below it // Return: %XORP_OK on success, otherwise %XORP_ERROR // XXX: @delete_command_name can be the full path-name for that command // int CliCommand::delete_command(const string& delete_command_name) { CliCommand *parent_cli_command = NULL; CliCommand *delete_cli_command = NULL; vector command_tokens; string token; string token_line = delete_command_name; // Create a vector of all takens in the command for (token = pop_token(token_line); ! token.empty(); token = pop_token(token_line)) { command_tokens.push_back(token); } if (command_tokens.empty()) return (XORP_ERROR); // Traverse all tokens and find the command to delete parent_cli_command = this; delete_cli_command = NULL; for (size_t i = 0; i < command_tokens.size(); i++) { if (delete_cli_command != NULL) parent_cli_command = delete_cli_command; delete_cli_command = parent_cli_command->command_find(command_tokens[i]); if (delete_cli_command == NULL) break; } if (delete_cli_command == NULL) goto error_label; if (parent_cli_command->delete_command(delete_cli_command) != XORP_OK) goto error_label; return (XORP_OK); error_label: XLOG_ERROR("Error deleting %s on %s", delete_command_name.c_str(), this->name().c_str()); return (XORP_ERROR); } // Recursively delete all the children of this command. void CliCommand::delete_all_commands() { list ::iterator iter; for (iter = _child_command_list.begin(); iter != _child_command_list.end(); ++iter) { (*iter)->delete_all_commands(); delete *iter; } while (! _child_command_list.empty()) _child_command_list.pop_front(); } /** * CliCommand::create_default_cli_commands: * @: * * Create the default CLI commands at each level of the command tree. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int CliCommand::create_default_cli_commands() { // TODO: add commands like "help", "list" (all available subcommands, etc return (XORP_OK); } /** * CliCommand::add_pipes: * @: * * Create and add the default CLI pipe commands. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int CliCommand::add_pipes(string& error_msg) { CliPipe *cli_pipe; CliCommand *com0; com0 = new CliCommand(this, "|", "Pipe through a command"); // com0 = add_command("|", "Pipe through a command", false, error_msg); if (com0 == NULL) { return (XORP_ERROR); } delete_pipes(); // be sure to not leak memory if one is already set. set_cli_command_pipe(com0); cli_pipe = new CliPipe("count"); if (com0->add_command(cli_pipe, error_msg) != XORP_OK) { delete_pipes(); return (XORP_ERROR); } cli_pipe = new CliPipe("except"); if (com0->add_command(cli_pipe, error_msg) != XORP_OK) { delete_pipes(); return (XORP_ERROR); } cli_pipe = new CliPipe("find"); if (com0->add_command(cli_pipe, error_msg) != XORP_OK) { delete_pipes(); return (XORP_ERROR); } cli_pipe = new CliPipe("hold"); if (com0->add_command(cli_pipe, error_msg) != XORP_OK) { delete_pipes(); return (XORP_ERROR); } cli_pipe = new CliPipe("match"); if (com0->add_command(cli_pipe, error_msg) != XORP_OK) { delete_pipes(); return (XORP_ERROR); } cli_pipe = new CliPipe("no-more"); if (com0->add_command(cli_pipe, error_msg) != XORP_OK) { delete_pipes(); return (XORP_ERROR); } cli_pipe = new CliPipe("resolve"); if (com0->add_command(cli_pipe, error_msg) != XORP_OK) { delete_pipes(); return (XORP_ERROR); } cli_pipe = new CliPipe("save"); if (com0->add_command(cli_pipe, error_msg) != XORP_OK) { delete_pipes(); return (XORP_ERROR); } cli_pipe = new CliPipe("trim"); if (com0->add_command(cli_pipe, error_msg) != XORP_OK) { delete_pipes(); return (XORP_ERROR); } return (XORP_OK); } /** * CliCommand::delete_pipes: * @: * * Delete the default CLI pipe commands. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int CliCommand::delete_pipes() { if (_cli_command_pipe != NULL) delete _cli_command_pipe; return (XORP_OK); // return (delete_command("|")); } // // Attempts to complete the current command. // Return: true if the attempt was successful, otherwise false. // bool CliCommand::cli_attempt_command_completion_byname(void *obj, WordCompletion *cpl, void *data, const char *line, int word_end, list& cli_command_match_list) { CliCommand *cli_command = reinterpret_cast(obj); int word_start = 0; // XXX: complete from the beginning of 'line' const char *type_suffix = NULL; const char *cont_suffix = " "; // XXX: a space after a command is completed string token, token_line; const string name_string = cli_command->name(); bool is_command_completed; // 'true' if complete command typed if ((cpl == NULL) || (line == NULL) || (word_end < 0)) { return (false); } token_line = string(line, word_end); token = pop_token(token_line); if ((! cli_command->is_same_prefix(token)) && (! cli_command->has_type_match_cb())) { return (false); } if (token_line.length() && (is_token_separator(token_line[0]) || (token == "|"))) is_command_completed = true; else is_command_completed = false; // Check if a potential sub-prefix if (! is_command_completed) { string name_complete; if (cli_command->has_type_match_cb()) { // // XXX: Nothing to complete, we just need to print // the help with the command type. // cli_command_match_list.push_back(cli_command); return (true); } name_complete = name_string.substr(token.length()); if (cli_command->help_completion().size() > 0) type_suffix = cli_command->help_completion().c_str(); // Add two empty spaces in front string line_string = " "; if (token.empty()) { // XXX: ignore the rest of the empty spaces word_end = 0; } else { line_string += line; } cpl_add_completion(cpl, line_string.c_str(), word_start, word_end + 2, name_complete.c_str(), type_suffix, cont_suffix); cli_command_match_list.push_back(cli_command); return (true); } // Must be a complete command bool is_token_match = false; if (cli_command->has_type_match_cb()) { string errmsg; is_token_match = cli_command->type_match_cb()->dispatch(token, errmsg); } else { is_token_match = cli_command->is_same_command(token); } if (! is_token_match) { return (false); } bool is_child_completion = false; if (cli_command->can_complete() && (! has_more_tokens(token_line)) && (! cli_command->is_argument_expected())) { // Add the appropriate completion info if the command can be run string line_string1 = " "; type_suffix = EXECUTE_THIS_COMMAND_STRING.c_str(); cpl_add_completion(cpl, line_string1.c_str(), word_start, line_string1.size(), "", type_suffix, cont_suffix); is_child_completion = true; } if (cli_command->can_pipe() && (cli_command->cli_command_pipe() != NULL)) { // Add the pipe completions if (cli_command->_cli_completion_func(cli_command->cli_command_pipe(), cpl, data, token_line.c_str(), token_line.length(), cli_command_match_list)) { is_child_completion = true; } } // Add completions for the sub-commands (if any) list::iterator iter; for (iter = cli_command->child_command_list().begin(); iter != cli_command->child_command_list().end(); ++iter) { CliCommand *cli_command_child = *iter; if (! cli_command_child->has_cli_completion_func()) continue; if (cli_command_child->_cli_completion_func(cli_command_child, cpl, data, token_line.c_str(), token_line.length(), cli_command_match_list)) { is_child_completion = true; } } return (is_child_completion); } CliCommand * CliCommand::command_find(const string& token) { list::iterator iter; for (iter = child_command_list().begin(); iter != child_command_list().end(); ++iter) { CliCommand *cli_command = *iter; if (cli_command->has_type_match_cb()) { string errmsg; if (cli_command->type_match_cb()->dispatch(token, errmsg)) return (cli_command); continue; } if (cli_command->is_same_command(token)) { // XXX: assume that no two subcommands have the same name return (cli_command); } } return (NULL); } // // Test if the command line is a prefix for a multi-level command. // Note that if there is an exact match, then the return value is false. // bool CliCommand::is_multi_command_prefix(const string& command_line) { string token; string token_line = command_line; CliCommand *parent_cli_command = this; CliCommand *child_cli_command = NULL; for (token = pop_token(token_line); ! token.empty(); token = pop_token(token_line)) { child_cli_command = parent_cli_command->command_find(token); if (child_cli_command != NULL) { parent_cli_command = child_cli_command; continue; } // Test if the token is a prefix for a child command list::const_iterator iter; for (iter = parent_cli_command->child_command_list().begin(); iter != parent_cli_command->child_command_list().end(); ++iter) { child_cli_command = *iter; if (child_cli_command->is_same_prefix(token)) return true; } break; } return (false); } // Tests if the string in @token can be a prefix for this command bool CliCommand::is_same_prefix(const string& token) { size_t s = token.length(); if (s > _name.length()) return (false); return (token.substr(0, s) == _name.substr(0, s)); } // Tests if the string in @token matches this command bool CliCommand::is_same_command(const string& token) { return (token == _name); } bool CliCommand::find_command_help(const char *line, int word_end, set& help_strings) { string token, token_line; bool ret_value = false; bool is_no_space_at_end; if ((line == NULL) || (word_end < 0)) { return (false); } token_line = string(line, word_end); token = pop_token(token_line); if ((! is_same_prefix(token)) && (! has_type_match_cb())) return (false); // // Test if the token matches the command // bool is_token_match = false; if (has_type_match_cb()) { string errmsg; is_token_match = type_match_cb()->dispatch(token, errmsg); } else { is_token_match = is_same_command(token); } if (token_line.length() && is_token_separator(token_line[0]) && (! is_token_match)) { // Not a match return (false); } is_no_space_at_end = (token_line.empty()) ? (true) : (false); // Get the token for the child's command (if any) token = pop_token(token_line); if ((token.length() == 0) && is_no_space_at_end) { // The last token, and there is no space, so print my help. help_strings.insert(c_format(" %-19s %s\r\n", name().c_str(), help().c_str())); return (true); } if ((token.length() == 0) && can_complete() && (! is_argument_expected())) { // The last token, and there is space at the end, // so print the "default" help. help_strings.insert(c_format(" %-19s %s\r\n", "<[Enter]>", "Execute this command")); ret_value = true; } // Not the last token, so search down for help list::iterator iter; for (iter = child_command_list().begin(); iter != child_command_list().end(); ++iter) { CliCommand *cli_command = *iter; string tmp_token_line = copy_token(token) + token_line; ret_value |= cli_command->find_command_help(tmp_token_line.c_str(), tmp_token_line.length(), help_strings); } if (can_pipe() && (cli_command_pipe() != NULL)) { // Add the pipe completions string tmp_token_line = copy_token(token) + token_line; ret_value |= cli_command_pipe()->find_command_help( tmp_token_line.c_str(), tmp_token_line.length(), help_strings); } return (ret_value); } bool CliCommand::can_complete() { if (has_cli_process_callback() || allow_cd()) return true; return false; } CliCommand * CliCommand::cli_command_pipe() { if (_root_command != this) return (_root_command->cli_command_pipe()); else return (_cli_command_pipe); } void CliCommand::set_dynamic_children_callback(DYNAMIC_CHILDREN_CALLBACK v) { XLOG_ASSERT(!_global_name.empty()); _dynamic_children_callback = v; _has_dynamic_children = true; } bool CliCommand::has_dynamic_process_callback() { return (!_dynamic_process_callback.is_empty()); } bool CliCommand::has_dynamic_interrupt_callback() { return (!_dynamic_interrupt_callback.is_empty()); } bool CliCommand::has_cli_process_callback() { if (_has_dynamic_children) { // // Force the children to be evaluated, which forces the // cli_process_callback to be set. // child_command_list(); } return (!_cli_process_callback.is_empty()); } bool CliCommand::has_cli_interrupt_callback() { return (!_cli_interrupt_callback.is_empty()); } bool CliCommand::has_type_match_cb() const { return (! _type_match_cb.is_empty()); } list& CliCommand::child_command_list() { string error_msg; if (_has_dynamic_children) XLOG_ASSERT(_child_command_list.empty()); // Handle dynamic children generation if (_child_command_list.empty() && _has_dynamic_children) { // Now we've run this, we won't need to run it again. _has_dynamic_children = false; // Add dynamic children XLOG_ASSERT(global_name().size() > 0); map dynamic_children; map::iterator iter; dynamic_children = _dynamic_children_callback->dispatch(global_name()); CliCommand *new_cmd; for (iter = dynamic_children.begin(); iter != dynamic_children.end(); ++iter) { const CliCommandMatch& ccm = iter->second; const string& command_name = ccm.command_name(); const string& help_string = ccm.help_string(); bool is_executable = ccm.is_executable(); bool can_pipe = ccm.can_pipe(); bool default_nomore_mode = ccm.default_nomore_mode(); bool is_command_argument = ccm.is_command_argument(); bool is_argument_expected = ccm.is_argument_expected(); new_cmd = add_command(command_name, help_string, false, error_msg); if (new_cmd == NULL) { XLOG_FATAL("Cannot add command '%s' to parent '%s': %s", command_name.c_str(), name().c_str(), error_msg.c_str()); } vector child_global_name = global_name(); child_global_name.push_back(command_name); new_cmd->set_global_name(child_global_name); new_cmd->set_can_pipe(can_pipe); new_cmd->set_default_nomore_mode(default_nomore_mode); new_cmd->set_is_command_argument(is_command_argument); new_cmd->set_is_argument_expected(is_argument_expected); new_cmd->set_type_match_cb(ccm.type_match_cb()); new_cmd->set_dynamic_children_callback(_dynamic_children_callback); new_cmd->set_dynamic_process_callback(_dynamic_process_callback); new_cmd->set_dynamic_interrupt_callback(_dynamic_interrupt_callback); if (is_executable) { new_cmd->set_cli_process_callback(_dynamic_process_callback); new_cmd->set_cli_interrupt_callback(_dynamic_interrupt_callback); } } } return (_child_command_list); } xorp/cli/TODO0000664000076400007640000000545111421137511013120 0ustar greearbgreearb# # $XORP: xorp/cli/TODO,v 1.8 2005/03/24 02:10:24 pavlin Exp $ # * Update libtecla to the lastest version (currently, 1.6, while we are using 1.4). * Currently, the functions of 'j' and 'k' in page mode are: - 'j' scroll down one line - 'k' scroll up one line In some router vendors (e.g., Juniper), the key binding is just the opposite. Hence, add hooks so the CLI behaves similar as Juniper (if the user desires so). * If SPACE is pressed on empty line at the bottom of the terminal xterm window, and then Ctrl-D is pressed, the command completion help output overlaps with the current line. * For consistency with other modules, rename init_family argument to family in the CliNode constructor. Do similar renaming for other arguments. * Implement CliCommand::create_default_cli_commands() and use it as appropriate. * Don't return any error if a command with exactly same help, etc was installed already. (??) * Add output paging for generated help. * Change cli_command->server_name() and friends to 'processor_name' * When disconnecting a CliClient during initialization of a connection, due to an error, print the error. * When asking for help '?' or on a command that supports optional arguments, print those optional arguments as well. * cli_command->name() for the root command is empty string. When printing error with cli_command->name(), use '' around the command name to make it clear where the command is. * Implement the missing pipe filters. * Add support for \" escaping of a quotation. * Parse the pipe commands and return error if the (number of) arguments do not match. * Change/fix pop_token() to return an error when no pair of quotations found. * Check if the cli_client->_buffer_line magic indeed works if the very last cli_printf() doesn't have '\n' at the end. * Commbine the "Enter" help message to be defined at a single place. * Help messages for following commands are not same as in Juniper: - "| compare rollback" - "| display detail" - "| display inheritance" - "| display xml" * If no command is typed, the pressing "SPACE" should or should not list the available commands? Right now, pressing the SPACE when nothing typed at all will NOT list the available commands; also, pressing it after another command will not list the sub-commands. * Implement name completion for filenames and for usernames (when appropriate). * In Juniper, we have: "Delete all characters on the command line" : Ctrl-u or Ctrl-x "Delete the word before cursor" : Ctrl-w, Esc-Backspace, or Alt-Backspace In libtecla, all the above combinations works except "Ctrl-x". Do we want to add this combination as well? Other keys that don't work are: "Search the CLI history in reverse order..." : Ctrl-r "Search the CLI history by typing ..." : Esc-/ xorp/cli/cli_node.cc0000664000076400007640000003667311540224220014521 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // CLI (Command-Line Interface) implementation for XORP. // #include "cli_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvx.hh" #include "libxorp/ipvxnet.hh" #include "libxorp/token.hh" #include "libxorp/utils.hh" #include "libcomm/comm_api.h" #include "cli_client.hh" #include "cli_node.hh" #include "cli_private.hh" // // Exported variables // // // Local constants definitions // // // Local structures/classes, typedefs and macros // // // Local variables // // // Local functions prototypes // /** * CliNode::CliNode: * @init_family: The address family (%AF_INET or %AF_INET6 * for IPv4 and IPv6 respectively). * @init_module_id: The module ID (must be %XORP_MODULE_CLI). * @init_eventloop: The event loop. * * CLI node constructor. **/ CliNode::CliNode(int init_family, xorp_module_id module_id, EventLoop& init_eventloop) : ProtoNode(init_family, module_id, init_eventloop), _cli_port(0), // XXX: not defined yet _next_session_id(0), _startup_cli_prompt(XORP_CLI_PROMPT), _cli_command_root(NULL, "", ""), _is_log_trace(false) { string error_msg; if (module_id != XORP_MODULE_CLI) { XLOG_FATAL("Invalid module ID = %d (must be 'XORP_MODULE_CLI' = %d)", module_id, XORP_MODULE_CLI); } cli_command_root()->set_allow_cd(true, _startup_cli_prompt); cli_command_root()->create_default_cli_commands(); if (cli_command_root()->add_pipes(error_msg) != XORP_OK) { XLOG_FATAL("Cannot add command pipes: %s", error_msg.c_str()); } } /** * CliNode::~CliNode: * @: * * CLI node destructor. **/ CliNode::~CliNode() { stop(); // TODO: perform CLI-specific operations. } int CliNode::start() { string error_msg; if (! is_enabled()) return (XORP_OK); if (is_up() || is_pending_up()) return (XORP_OK); if (ProtoNode::start() != XORP_OK) return (XORP_ERROR); // Perform misc. CLI-specific start operations // // Start accepting connections // if (_cli_port != 0) { if (sock_serv_open().is_valid()) { eventloop().add_ioevent_cb(_cli_socket, IOT_ACCEPT, callback(this, &CliNode::accept_connection), XorpTask::PRIORITY_HIGHEST); } } if (add_internal_cli_commands(error_msg) != XORP_OK) { XLOG_ERROR("Cannot add internal CLI commands: %s", error_msg.c_str()); return (XORP_ERROR); } XLOG_TRACE(is_log_trace(), "CLI started"); return (XORP_OK); } /** * CliNode::stop: * @: * * Stop the CLI operation. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int CliNode::stop() { if (is_down()) return (XORP_OK); if (! is_up()) return (XORP_ERROR); if (ProtoNode::pending_stop() != XORP_OK) return (XORP_ERROR); // Perform misc. CLI-specific stop operations // TODO: add as necessary delete_pointers_list(_client_list); if (_cli_socket.is_valid()) eventloop().remove_ioevent_cb(_cli_socket, IOT_ACCEPT); sock_serv_close(); if (ProtoNode::stop() != XORP_OK) return (XORP_ERROR); XLOG_TRACE(is_log_trace(), "CLI stopped"); return (XORP_OK); } /** * Enable the node operation. * * If an unit is not enabled, it cannot be start, or pending-start. */ void CliNode::enable() { ProtoUnit::enable(); XLOG_TRACE(is_log_trace(), "CLI 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 CliNode::disable() { stop(); ProtoUnit::disable(); XLOG_TRACE(is_log_trace(), "CLI disabled"); } /** * CliNode::add_enable_cli_access_from_subnet: * @subnet_addr: The subnet address to add. * * Add a subnet address to the list of subnet addresses enabled * for CLI access. * This method can be called more than once to add a number of * subnet addresses. **/ void CliNode::add_enable_cli_access_from_subnet(const IPvXNet& subnet_addr) { list::iterator iter; // Test if we have this subnet already for (iter = _enable_cli_access_subnet_list.begin(); iter != _enable_cli_access_subnet_list.end(); ++iter) { const IPvXNet& tmp_ipvxnet = *iter; if (tmp_ipvxnet == subnet_addr) return; // Subnet address already added } _enable_cli_access_subnet_list.push_back(subnet_addr); } /** * CliNode::delete_enable_cli_access_from_subnet: * @subnet_addr: The subnet address to delete. * * Delete a subnet address from the list of subnet addresses enabled * for CLI access. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int CliNode::delete_enable_cli_access_from_subnet(const IPvXNet& subnet_addr) { list::iterator iter; // Test if we have this subnet already for (iter = _enable_cli_access_subnet_list.begin(); iter != _enable_cli_access_subnet_list.end(); ++iter) { const IPvXNet& tmp_ipvxnet = *iter; if (tmp_ipvxnet == subnet_addr) { _enable_cli_access_subnet_list.erase(iter); return (XORP_OK); // Entry erased } } return (XORP_ERROR); // No entry found } /** * CliNode::add_disable_cli_access_from_subnet: * @subnet_addr: The subnet address to add. * * Add a subnet address to the list of subnet addresses disabled * for CLI access. * This method can be called more than once to add a number of * subnet addresses. **/ void CliNode::add_disable_cli_access_from_subnet(const IPvXNet& subnet_addr) { list::iterator iter; // Test if we have this subnet already for (iter = _disable_cli_access_subnet_list.begin(); iter != _disable_cli_access_subnet_list.end(); ++iter) { const IPvXNet& tmp_ipvxnet = *iter; if (tmp_ipvxnet == subnet_addr) return; // Subnet address already added } _disable_cli_access_subnet_list.push_back(subnet_addr); } /** * CliNode::delete_disable_cli_access_from_subnet: * @subnet_addr: The subnet address to delete. * * Delete a subnet address from the list of subnet addresses disabled * for CLI access. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int CliNode::delete_disable_cli_access_from_subnet(const IPvXNet& subnet_addr) { list::iterator iter; // Test if we have this subnet already for (iter = _disable_cli_access_subnet_list.begin(); iter != _disable_cli_access_subnet_list.end(); ++iter) { const IPvXNet& tmp_ipvxnet = *iter; if (tmp_ipvxnet == subnet_addr) { _disable_cli_access_subnet_list.erase(iter); return (XORP_OK); // Entry erased } } return (XORP_ERROR); // No entry found } bool CliNode::is_allow_cli_access(const IPvX& ipvx) const { list::const_iterator iter; IPvXNet best_enable = IPvXNet(IPvX::ZERO(ipvx.af()), 0); IPvXNet best_disable = IPvXNet(IPvX::ZERO(ipvx.af()), 0); bool best_enable_found = false; bool best_disable_found = false; // Find the longest-match subnet address that may enable access for (iter = _enable_cli_access_subnet_list.begin(); iter != _enable_cli_access_subnet_list.end(); ++iter) { const IPvXNet& ipvxnet = *iter; if (ipvx.af() != ipvxnet.masked_addr().af()) continue; if (! ipvxnet.contains(ipvx)) continue; if (best_enable.contains(ipvxnet)) best_enable = ipvxnet; best_enable_found = true; } // Find the longest-match subnet address that may disable access for (iter = _disable_cli_access_subnet_list.begin(); iter != _disable_cli_access_subnet_list.end(); ++iter) { const IPvXNet& ipvxnet = *iter; if (ipvx.af() != ipvxnet.masked_addr().af()) continue; if (! ipvxnet.contains(ipvx)) continue; if (best_disable.contains(ipvxnet)) best_disable = ipvxnet; best_disable_found = true; } if (! best_disable_found) { // XXX: no disable match, so enable access by default return (true); } if (! best_enable_found) { // XXX: no enable match, so definitely disable return (false); } // Both enable and disable match if (best_enable.prefix_len() > best_disable.prefix_len()) return (true); return (false); // XXX: disable even if conflicting config } /** * CliNode::find_cli_by_term_name: * @term_name: The CLI terminal name to search for. * * Find a CLI client #CliClient for a given terminal name. * * Return value: The CLI client with name of @term_name on success, * otherwise NULL. **/ CliClient * CliNode::find_cli_by_term_name(const string& term_name) const { list::const_iterator iter; for (iter = _client_list.begin(); iter != _client_list.end(); ++ iter) { CliClient *cli_client = *iter; if (term_name == cli_client->cli_session_term_name()) { return (cli_client); } } return (NULL); } /** * CliNode::find_cli_by_session_id: * @session_id: The CLI session ID to search for. * * Find a CLI client #CliClient for a given session ID. * * Return value: The CLI client with session ID of @session_id on success, * otherwise NULL. **/ CliClient * CliNode::find_cli_by_session_id(uint32_t session_id) const { list::const_iterator iter; for (iter = _client_list.begin(); iter != _client_list.end(); ++ iter) { CliClient *cli_client = *iter; if (cli_client->cli_session_session_id() == session_id) return (cli_client); } return (NULL); } /** * CliNode::xlog_output: * @obj: The #CliClient object to apply this function to. * @msg: A C-style string with the message to output. * * Output a log message to a #CliClient object. * * Return value: On success, the number of characters printed, * otherwise %XORP_ERROR. **/ int CliNode::xlog_output(void *obj, xlog_level_t level, const char *msg) { CliClient *cli_client = static_cast(obj); int ret_value = cli_client->cli_print(msg); if (ret_value >= 0 && cli_client->cli_print("") >= 0 && cli_client->cli_flush() == 0) { return ret_value; } return XORP_ERROR; UNUSED(level); } // // CLI add_cli_command // int CliNode::add_cli_command( // Input values, const string& processor_name, const string& command_name, const string& command_help, const bool& is_command_cd, const string& command_cd_prompt, const bool& is_command_processor, // Output values, string& error_msg) { // Reset the error message error_msg = ""; // // Check the request // if (command_name.empty()) { error_msg = "ERROR: command name is empty"; return (XORP_ERROR); } CliCommand *c0 = cli_command_root(); CliCommand *c1 = NULL; if (! is_command_processor) { if (is_command_cd) { c1 = c0->add_command(command_name, command_help, command_cd_prompt, true, error_msg); } else { c1 = c0->add_command(command_name, command_help, true, error_msg); } } else { // Command processor c1 = c0->add_command(command_name, command_help, true, callback(this, &CliNode::send_process_command), error_msg); if (c1 != NULL) c1->set_can_pipe(true); } // // TODO: return the result of the command installation // if (c1 == NULL) { error_msg = c_format("Cannot install command '%s': %s", command_name.c_str(), error_msg.c_str()); return (XORP_ERROR); } c1->set_global_name(token_line2vector(command_name)); c1->set_server_name(processor_name); return (XORP_OK); } // // CLI delete_cli_command // int CliNode::delete_cli_command( // Input values, const string& processor_name, const string& command_name, // Output values, string& error_msg) { // Reset the error message error_msg = ""; // // Check the request // if (command_name.empty()) { error_msg = "ERROR: command name is empty"; return (XORP_ERROR); } CliCommand *c0 = cli_command_root(); if (c0->delete_command(command_name) != XORP_OK) { error_msg = c_format("Cannot delete command '%s'", command_name.c_str()); return (XORP_ERROR); } UNUSED(processor_name); return (XORP_OK); } // // Send a command request to a remote node // int CliNode::send_process_command(const string& server_name, const string& cli_term_name, uint32_t cli_session_id, const vector& command_global_name, const vector& argv) { if (server_name.empty()) return (XORP_ERROR); if (cli_term_name.empty()) return (XORP_ERROR); if (command_global_name.empty()) return (XORP_ERROR); CliClient *cli_client = find_cli_by_session_id(cli_session_id); if (cli_client == NULL) return (XORP_ERROR); if (cli_client != find_cli_by_term_name(cli_term_name)) return (XORP_ERROR); // // Send the request // if (! _send_process_command_callback.is_empty()) { (_send_process_command_callback)->dispatch(server_name, server_name, cli_term_name, cli_session_id, command_global_name, argv); } cli_client->set_is_waiting_for_data(true); return (XORP_OK); } // // Process the response of a command processed by a remote node // void CliNode::recv_process_command_output(const string * , // processor_name, const string *cli_term_name, const uint32_t *cli_session_id, const string *command_output) { // // Find if a client is waiting for that command // if ((cli_term_name == NULL) || (cli_session_id == NULL)) return; CliClient *cli_client = find_cli_by_session_id(*cli_session_id); if (cli_client == NULL) return; if (cli_client != find_cli_by_term_name(*cli_term_name)) return; if (! cli_client->is_waiting_for_data()) { // ERROR: client is not waiting for that data (e.g., probably too late) return; } // // Print the result and reset client status // if (command_output != NULL) { cli_client->cli_print(c_format("%s", command_output->c_str())); } cli_client->cli_flush(); cli_client->set_is_waiting_for_data(false); cli_client->post_process_command(); } CliClient * CliNode::add_client(XorpFd input_fd, XorpFd output_fd, bool is_network, const string& startup_cli_prompt, string& error_msg) { return (add_connection(input_fd, output_fd, is_network, startup_cli_prompt, error_msg)); } int CliNode::remove_client(CliClient *cli_client, string& error_msg) { if (delete_connection(cli_client, error_msg) != XORP_OK) return (XORP_ERROR); // XXX: Remove the client itself if it is still around list::iterator iter; iter = find(_client_list.begin(), _client_list.end(), cli_client); if (iter != _client_list.end()) { _client_list.erase(iter); } return (XORP_OK); } xorp/cli/xrl_cli_shell_funcs.sh0000664000076400007640000001105711421137511017004 0ustar greearbgreearb#!/bin/sh # # $XORP: xorp/cli/xrl_cli_shell_funcs.sh,v 1.10 2003/12/20 01:43:34 pavlin Exp $ # # # Library of functions to sent XRLs to a running CLI process. # # Conditionally set ${srcdir} if it wasn't assigned (e.g., by `gmake check`) if [ "X${srcdir}" = "X" ] ; then srcdir=`dirname $0` ; fi . ${srcdir}/../utils/xrl_shell_lib.sh # # Conditionally set the target name # IP_VERSION=${IP_VERSION:?"IP_VERSION undefined. Must be defined to either IPV4 or IPV6"} case "${IP_VERSION}" in IPV4) CLI_TARGET=${CLI_TARGET:="CLI"} ;; IPV6) CLI_TARGET=${CLI_TARGET:="CLI"} ;; *) echo "Error: invalid IP_VERSION = ${IP_VERSION}. Must be either IPV4 or IPV6" exit 1 ;; esac cli_enable_cli() { if [ $# -lt 1 ] ; then echo "Usage: cli_enable_cli " exit 1 fi enable=$1 echo "cli_enable_cli" $* XRL="finder://$CLI_TARGET/cli_manager/0.1/enable_cli" XRL_ARGS="?enable:bool=$enable" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } cli_start_cli() { echo "cli_start_cli" $* XRL="finder://$CLI_TARGET/cli_manager/0.1/start_cli" XRL_ARGS="" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } cli_stop_cli() { echo "cli_stop_cli" $* XRL="finder://$CLI_TARGET/cli_manager/0.1/stop_cli" XRL_ARGS="" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } cli_add_enable_cli_access_from_subnet4() { if [ $# -lt 1 ] ; then echo "Usage: cli_add_enable_cli_access_from_subnet4 " exit 1 fi subnet_addr=$1 echo "cli_add_enable_cli_access_from_subnet4" $* XRL="finder://$CLI_TARGET/cli_manager/0.1/add_enable_cli_access_from_subnet4" XRL_ARGS="?subnet_addr:ipv4net=$subnet_addr" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } cli_add_enable_cli_access_from_subnet6() { if [ $# -lt 1 ] ; then echo "Usage: cli_add_enable_cli_access_from_subnet6 " exit 1 fi subnet_addr=$1 echo "cli_add_enable_cli_access_from_subnet6" $* XRL="finder://$CLI_TARGET/cli_manager/0.1/add_enable_cli_access_from_subnet6" XRL_ARGS="?subnet_addr:ipv6net=$subnet_addr" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } cli_delete_enable_cli_access_from_subnet4() { if [ $# -lt 1 ] ; then echo "Usage: cli_delete_enable_cli_access_from_subnet4 " exit 1 fi subnet_addr=$1 echo "cli_delete_enable_cli_access_from_subnet4" $* XRL="finder://$CLI_TARGET/cli_manager/0.1/delete_enable_cli_access_from_subnet4" XRL_ARGS="?subnet_addr:ipv4net=$subnet_addr" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } cli_delete_enable_cli_access_from_subnet6() { if [ $# -lt 1 ] ; then echo "Usage: cli_delete_enable_cli_access_from_subnet6 " exit 1 fi subnet_addr=$1 echo "cli_delete_enable_cli_access_from_subnet6" $* XRL="finder://$CLI_TARGET/cli_manager/0.1/delete_enable_cli_access_from_subnet6" XRL_ARGS="?subnet_addr:ipv6net=$subnet_addr" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } cli_add_disable_cli_access_from_subnet4() { if [ $# -lt 1 ] ; then echo "Usage: cli_add_disable_cli_access_from_subnet4 " exit 1 fi subnet_addr=$1 echo "cli_add_disable_cli_access_from_subnet4" $* XRL="finder://$CLI_TARGET/cli_manager/0.1/add_disable_cli_access_from_subnet4" XRL_ARGS="?subnet_addr:ipv4net=$subnet_addr" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } cli_add_disable_cli_access_from_subnet6() { if [ $# -lt 1 ] ; then echo "Usage: cli_add_disable_cli_access_from_subnet6 " exit 1 fi subnet_addr=$1 echo "cli_add_disable_cli_access_from_subnet6" $* XRL="finder://$CLI_TARGET/cli_manager/0.1/add_disable_cli_access_from_subnet6" XRL_ARGS="?subnet_addr:ipv6net=$subnet_addr" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } cli_delete_disable_cli_access_from_subnet4() { if [ $# -lt 1 ] ; then echo "Usage: cli_delete_disable_cli_access_from_subnet4 " exit 1 fi subnet_addr=$1 echo "cli_delete_disable_cli_access_from_subnet4" $* XRL="finder://$CLI_TARGET/cli_manager/0.1/delete_disable_cli_access_from_subnet4" XRL_ARGS="?subnet_addr:ipv4net=$subnet_addr" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } cli_delete_disable_cli_access_from_subnet6() { if [ $# -lt 1 ] ; then echo "Usage: cli_delete_disable_cli_access_from_subnet6 " exit 1 fi subnet_addr=$1 echo "cli_delete_disable_cli_access_from_subnet6" $* XRL="finder://$CLI_TARGET/cli_manager/0.1/delete_disable_cli_access_from_subnet6" XRL_ARGS="?subnet_addr:ipv6net=$subnet_addr" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } xorp/cli/cli_module.h0000664000076400007640000000230011421137511014703 0ustar greearbgreearb/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- */ /* * Copyright (c) 2001-2009 XORP, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 2, June * 1991 as published by the Free Software Foundation. Redistribution * and/or modification of this program under the terms of any other * version of the GNU General Public License is not permitted. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, * see the GNU General Public License, Version 2, a copy of which can be * found in the XORP LICENSE.gpl file. * * XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; * http://xorp.net */ /* * $XORP: xorp/cli/cli_module.h,v 1.11 2008/10/02 21:56:29 bms Exp $ */ /* * Module definitions. */ #ifndef __CLI_CLI_MODULE_H__ #define __CLI_CLI_MODULE_H__ #ifndef XORP_MODULE_NAME #define XORP_MODULE_NAME "CLI" #endif #ifndef XORP_MODULE_VERSION #define XORP_MODULE_VERSION "0.1" #endif #endif /* __CLI_CLI_MODULE_H__ */ xorp/cli/cli_client.hh0000664000076400007640000004527311540224220015060 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/cli/cli_client.hh,v 1.38 2008/10/02 21:56:29 bms Exp $ #ifndef __CLI_CLI_CLIENT_HH__ #define __CLI_CLI_CLIENT_HH__ // // CLI client definition. // #include "libxorp/xorp.h" #include "libxorp/buffer.hh" #include "libxorp/ipvx.hh" #include "libxorp/xorpfd.hh" #include "libxorp/eventloop.hh" #include "libxorp/timer.hh" #include "cli_node.hh" // // Constants definitions // // // Structures/classes, typedefs and macros // class CliNode; class CliCommand; /** * @short The class for the CLI client. * * There is one CLI client per CLI user (e.g., telnet connection). */ class CliClient { public: /** * Constructor for a given CLI node and file descriptor. * * @param init_cli_node the @ref CliNode CLI node this client belongs to. * @param input_fd the file descriptor for the CLI client to read * data from. * @param output_fd the file descriptor for the CLI client to write * data to. * @param startup_cli_prompt the startup CLI prompt. */ CliClient(CliNode& init_cli_node, XorpFd input_fd, XorpFd output_fd, const string& startup_cli_prompt); /** * Destructor */ virtual ~CliClient(); /** * Test if the processing of any pending commands or pending data has * complated. * * @return true if processing of any pending commands or pending data * has completed, otherwise false. */ bool done() const; /** * Start the connection. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int start_connection(string& error_msg); /** * Stop the connection. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int stop_connection(string& error_msg); /** * Print a message to the CLI user. * * @param msg C++ string with the message to display. * @return the number of characters printed (not including * the trailing '\0'). */ int cli_print(const string& msg); /** * Flush the CLI output. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int cli_flush(); /** * Add/remove this client for displaying log messages. * * @param v if true, add this client for displaying messages, otherwise * remove it. * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_log_output(bool v); /** * Test if this client has been added for displaying log messages. * * @return true if this client has been added for displaying log messages, * otherwise false. */ bool is_log_output() const { return (_is_log_output); } /** * Get the file descriptor for reading the input from the user. * * @return the file descriptor for reading the input from the user. */ XorpFd input_fd() { return (_input_fd); } /** * Get the file descriptor for writing the output to the user. * * @return the file descriptor for writing the output to the user. */ XorpFd output_fd() { return (_output_fd); } /** * Test if this client is associated with a terminal type input device. * * @return true if this client is associated with a terminal type input * device, otherwise false. */ bool is_input_tty() const; /** * Test if this client is associated with a terminal type output device. * * @return true if this client is associated with a terminal type output * device, otherwise false. */ bool is_output_tty() const; /** * Test if this client is associated with a network connection. * * @return true if this client is associated with a network connection, * otherwise false. */ bool is_network() const; /** * Set the flag that associates the client with a network connection. * * @param v true if this client is associated with a network connection. */ void set_network_client(bool v); /** * Test if this client is associated with a telnet connection. * * @return true if this client is associated with a telnet connection, * otherwise false. */ bool is_telnet() const; /** * Test if this client is running in interactive mode. * * @return true if this client is running in interactive mode, * otherwise false. */ bool is_interactive() const; // // Session info // /** * Get the user name. * * @return the user name. */ const string& cli_session_user_name() const { return (_cli_session_user_name); } /** * Get the terminal name. * * @return the terminal name. */ const string& cli_session_term_name() const { return (_cli_session_term_name); } /* * Get the user network address. * * @return the user network address. */ const IPvX& cli_session_from_address() const { return (_cli_session_from_address); } /** * Get the start time for this connection. * * @return a reference to @ref TimeVal with the starting time for * this connection. */ const TimeVal& cli_session_start_time() const { return (_cli_session_start_time); } /** * Get the stop time for this connection. * * @return a reference to @ref TimeVal with the end time for * this connection. */ const TimeVal& cli_session_stop_time() const { return (_cli_session_stop_time); } /** * Test if the CLI session is active (i.e., it has been setup and ready * to use). * * @return true if the CLI session is active, otherwise false. */ bool is_cli_session_active() const { return (_is_cli_session_active); } /** * Get the session ID. * * @return the session ID for this client. */ uint32_t cli_session_session_id() const { return (_cli_session_session_id); } /** * Set the user name. * * @param v the user name to set. */ void set_cli_session_user_name(const string& v) { _cli_session_user_name = v; } /** * Set the terminal name. * * @param v the terminal name to set. */ void set_cli_session_term_name(const string& v) { _cli_session_term_name = v; } /** * Set the user network address. * * @param v the user network address to set. */ void set_cli_session_from_address(const IPvX& v) { _cli_session_from_address = v; } /** * Set the start time for this connection. * * @param v the start time for this connection. */ void set_cli_session_start_time(const TimeVal& v) { _cli_session_start_time = v; } /** * Set the stop time for this connection. * * @param v the stop time for this connection. */ void set_cli_session_stop_time(const TimeVal& v) { _cli_session_stop_time = v; } /** * Set if this session is active. * * @param v if true, the session is set as active, otherwise is * set as non-active. */ void set_is_cli_session_active(bool v) { _is_cli_session_active = v; } /** * Set the session ID. * * @param v the session ID value to set. */ void set_cli_session_session_id(uint32_t v) { _cli_session_session_id = v; } /** * Perform final processing of a command. */ void post_process_command(); /** * Flush the output of a command while it is still running */ void flush_process_command_output(); // // Server communication state // /** * Test if waiting for data from command processor. * * @return true if waiting for data from command processor, otherwise * false. */ bool is_waiting_for_data() const { return (_is_waiting_for_data); } /** * Set a flag whether is waiting for data from command processor. * * @param v if true, the session is set as waiting for data, otherwise * is set as non-waiting. */ void set_is_waiting_for_data(bool v); /** * Get the current CLI prompt. * * @return the current CLI prompt. */ const string& current_cli_prompt() const { return (_current_cli_prompt); } /** * Set the current CLI prompt. * * @param cli_prompt the current CLI prompt to set. */ void set_current_cli_prompt(const string& cli_prompt); /** * Update the terminal-related information after it has been resized. */ void terminal_resized(); private: friend class CliPipe; CliNode& cli_node() { return (_cli_node); } GetLine *gl() { return (_gl); } /** * Process octet that may be part of a telnet option. * * @param val the value of the next octet. * @param is_telnet_option return-by-reference result: if true, * then @ref val is part of a telnet option and it was processed. * If it is false, then @ref value should be processed as input data. * @return XORP_OK on success, otherwise XORP_ERROR. */ int process_telnet_option(int val, bool& is_telnet_option); bool telnet_iac() { return (_telnet_iac); } void set_telnet_iac(bool v) { _telnet_iac = v; } bool telnet_sb() { return (_telnet_sb); } void set_telnet_sb(bool v) {_telnet_sb = v; } bool telnet_dont() { return (_telnet_dont); } void set_telnet_dont(bool v) { _telnet_dont = v; } bool telnet_do() { return (_telnet_do); } void set_telnet_do(bool v) { _telnet_do = v; } bool telnet_wont() { return (_telnet_wont); } void set_telnet_wont(bool v) { _telnet_wont = v; } bool telnet_will() { return (_telnet_will); } void set_telnet_will(bool v) { _telnet_will = v; } bool telnet_binary() { return (_telnet_binary); } void set_telnet_binary(bool v) { _telnet_binary = v; } void update_terminal_size(); size_t window_width() { return ((size_t)_window_width); } void set_window_width(uint16_t v) { _window_width = v; } size_t window_height() { return ((size_t)_window_height); } void set_window_height(uint16_t v) { _window_height = v; } Buffer& command_buffer() { return (_command_buffer); } Buffer& telnet_sb_buffer() { return (_telnet_sb_buffer); } CliCommand *cli_command_root() { return (cli_node().cli_command_root());} int buff_curpos() { return (_buff_curpos); } void set_buff_curpos(int v) { _buff_curpos = v; } CliCommand *current_cli_command() { return (_current_cli_command); } void set_current_cli_command(CliCommand *v); int process_command(const string& command_line); void interrupt_command(); CliPipe *add_pipe(const string& pipe_name); CliPipe *add_pipe(const string& pipe_name, const list& args_list); void delete_pipe_all(); bool is_pipe_mode() { return (_is_pipe_mode); } void set_pipe_mode(bool v) { _is_pipe_mode = v; } int block_connection(bool is_blocked); void client_read(XorpFd fd, IoEventType type); void process_input_data(); void schedule_process_input_data(); static CPL_MATCH_FN(command_completion_func); int process_char(const string& line, uint8_t val, bool& stop_processing); int process_char_page_mode(uint8_t val); int preprocess_char(uint8_t val, bool& stop_processing); void command_line_help(const string& line, int word_end, bool remove_last_input_char); bool is_multi_command_prefix(const string& command_line); void process_line_through_pipes(string& pipe_line); // Output paging related functions bool is_page_mode() { return (_is_page_mode); } void set_page_mode(bool v); bool is_page_buffer_mode() { return (*_is_page_buffer_mode); } void set_page_buffer_mode(bool v) { *_is_page_buffer_mode = v; } vector& page_buffer() { return (*_page_buffer); } const string& page_buffer_line(size_t line_n) const; void reset_page_buffer() { page_buffer().clear(); set_page_buffer_last_line_n(0); } size_t page_buffer_lines_n() { return (page_buffer().size()); } void append_page_buffer_line(const string& buffer_line); void concat_page_buffer_line(const string& buffer_line, size_t pos); size_t page_buffer_last_line_n() { return (*_page_buffer_last_line_n); } void set_page_buffer_last_line_n(size_t v) { *_page_buffer_last_line_n = v; } void incr_page_buffer_last_line_n() { (*_page_buffer_last_line_n)++; } void decr_page_buffer_last_line_n() { (*_page_buffer_last_line_n)--; } size_t page_buffer_window_lines_n(); size_t page_buffer_last_window_line_n(); size_t page_buffer2window_line_n(size_t buffer_line_n); size_t window_lines_n(size_t buffer_line_n); size_t calculate_first_page_buffer_line_by_window_size( size_t last_buffer_line_n, size_t max_window_size); bool is_help_mode() { return (_is_help_mode); } void set_help_mode(bool v) { _is_help_mode = v; } bool is_nomore_mode() { return (_is_nomore_mode); } void set_nomore_mode(bool v) { _is_nomore_mode = v; } bool is_hold_mode() { return (_is_hold_mode); } void set_hold_mode(bool v) { _is_hold_mode = v; } bool is_prompt_flushed() const { return _is_prompt_flushed; } void set_prompt_flushed(bool v) { _is_prompt_flushed = v; } CliNode& _cli_node; // The CLI node I belong to XorpFd _input_fd; // File descriptor to read the input XorpFd _output_fd; // File descriptor to write the output FILE *_input_fd_file; // The FILE version of _input_fd FILE *_output_fd_file; // The FILE version of _output_fd enum ClientType{ CLIENT_MIN = 0, CLIENT_TERMINAL = 0, CLIENT_FILE = 1, CLIENT_MAX }; ClientType _client_type; // Type of the client: term, file, etc. GetLine *_gl; // The GetLine for libtecla bool _telnet_iac; // True if the last octet was IAC bool _telnet_sb; // True if subnegotiation has began bool _telnet_dont; // True if the last octet was DONT bool _telnet_do; // True if the last octet was DO bool _telnet_wont; // True if the last octet was WONT bool _telnet_will; // True if the last octet was WILL bool _telnet_binary; // True if the client "do" binary mode uint16_t _window_width; // The CLI client window width uint16_t _window_height; // The CLI client window height Buffer _command_buffer; Buffer _telnet_sb_buffer; // The modified terminal flags bool _is_modified_stdio_termios_icanon; bool _is_modified_stdio_termios_echo; bool _is_modified_stdio_termios_isig; // // The original VMIN and VTIME members of the termios.c_cc[] array. // TODO: those should have type cc_t, but we are using 'int' instead // to avoid complicated conditional declarations. // int _saved_stdio_termios_vmin; int _saved_stdio_termios_vtime; // The command we are currently executing and its arguments CliCommand *_executed_cli_command; // The command currently executed vector _executed_cli_command_name; // The command name vector _executed_cli_command_args; // The arguments CliCommand *_current_cli_command; // The command we have "cd" to string _current_cli_prompt; // The CLI prompt int _buff_curpos; // The cursor position in the buffer string _buffer_line; // To buffer data when pipe-processing list _pipe_list; // A list with the CLI pipe commands bool _is_pipe_mode; // True if pipe processing enabled bool _is_nomore_mode; // True if disabled "more" mode bool _is_hold_mode; // True if enabled "hold" mode // Output paging related stuff bool _is_page_mode; // True if the output is paged bool *_is_page_buffer_mode; // True if enabled page_buffer_mode vector *_page_buffer; size_t *_page_buffer_last_line_n; bool _is_output_buffer_mode; // True if enabled output_buffer_mode vector _output_buffer; // The output buffer: line per element size_t _output_buffer_last_line_n; // The current last (visable) line bool _is_help_buffer_mode; // True if enabled help_buffer_mode vector _help_buffer; // The help buffer: line per element size_t _help_buffer_last_line_n; // The current last (visable) line bool _is_help_mode; // True if enabled help mode bool _is_prompt_flushed; // True if we have flushed the prompt // The strings to save the action names that some keys were bind to string _action_name_up_arrow; string _action_name_down_arrow; string _action_name_tab; string _action_name_newline_n; string _action_name_newline_r; string _action_name_spacebar; string _action_name_ctrl_a; string _action_name_ctrl_b; string _action_name_ctrl_c; string _action_name_ctrl_d; string _action_name_ctrl_e; string _action_name_ctrl_f; string _action_name_ctrl_h; string _action_name_ctrl_k; string _action_name_ctrl_l; string _action_name_ctrl_m; string _action_name_ctrl_n; string _action_name_ctrl_p; string _action_name_ctrl_u; string _action_name_ctrl_x; // // Session info state // string _cli_session_user_name; string _cli_session_term_name; IPvX _cli_session_from_address; TimeVal _cli_session_start_time; TimeVal _cli_session_stop_time; bool _is_cli_session_active; uint32_t _cli_session_session_id; // The unique session ID. bool _is_network; // // Log-related state // bool _is_log_output; // // Server communication state // bool _is_waiting_for_data; // True if waiting for external data // // Misc state // vector _pending_input_data; XorpTimer _process_pending_input_data_timer; }; // // Global variables // // // Global functions prototypes // #endif // __CLI_CLI_CLIENT_HH__ xorp/cli/cli_node_internal_commands.cc0000664000076400007640000003142011421137511020263 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // CLI internal commands. // #include "cli_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvx.hh" #include "cli_node.hh" #include "cli_client.hh" #include "cli_command.hh" // // Exported variables // // // Local constants definitions // // // Local structures/classes, typedefs and macros // // // Local variables // // // Local functions prototypes // /** * CliNode::add_internal_cli_commands: * @error_msg: The error message (if error). * * Add the internal default CLI commands from the top. * XXX: used by the CLI itself for internal processing of a command. * TODO: for consistency, even the internal commands should use XRLs instead. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int CliNode::add_internal_cli_commands(string& error_msg) { CliCommand *c0; c0 = cli_command_root(); if (c0 == NULL) { error_msg = c_format("Cannot find root CLI command"); return (XORP_ERROR); } if (c0->add_command("show", "Display information", true, error_msg) == NULL) { return (XORP_ERROR); } if (c0->add_command("show log", "Display information about log files and users", true, callback(this, &CliNode::cli_show_log), error_msg) == NULL) { return (XORP_ERROR); } if (c0->add_command("show log user", "Display information about users", true, callback(this, &CliNode::cli_show_log_user), error_msg) == NULL) { return (XORP_ERROR); } if (c0->add_command("set", "Set variable", true, error_msg) == NULL) { return (XORP_ERROR); } if (c0->add_command("set log", "Set log-related state", true, error_msg) == NULL) { return (XORP_ERROR); } if (c0->add_command("set log output", "Set output destination for log messages", true, error_msg) == NULL) { return (XORP_ERROR); } if (c0->add_command("set log output cli", "Set output CLI terminal for log messages", true, callback(this, &CliNode::cli_set_log_output_cli), error_msg) == NULL) { return (XORP_ERROR); } if (c0->add_command("set log output file", "Set output file for log messages", true, callback(this, &CliNode::cli_set_log_output_file), error_msg) == NULL) { return (XORP_ERROR); } if (c0->add_command("set log output remove", "Remove output destination for log messages", true, error_msg) == NULL) { return (XORP_ERROR); } if (c0->add_command("set log output remove cli", "Remove output CLI terminal for log messages", true, callback(this, &CliNode::cli_set_log_output_remove_cli), error_msg) == NULL) { return (XORP_ERROR); } if (c0->add_command("set log output remove file", "Remove output file for log messages", true, callback(this, &CliNode::cli_set_log_output_remove_file), error_msg) == NULL) { return (XORP_ERROR); } return (XORP_OK); } // // CLI COMMAND: "show log [filename]" // // Display information about log files // TODO: probably should change the command name to "show log file" ?? int CliNode::cli_show_log(const string& , // server_name const string& cli_term_name, uint32_t , // cli_session_id const vector& , // command_global_name const vector& argv) { CliClient *cli_client = find_cli_by_term_name(cli_term_name); if (cli_client == NULL) return (XORP_ERROR); for (size_t i = 0; i < argv.size(); i++) { // Show log information about optional files cli_client->cli_print(c_format("Showing information about file '%s'\n", argv[i].c_str())); } return (XORP_OK); } // // CLI COMMAND: "show log user [username]" // // Display information about users // TODO: add the missing info at the end to make it 'who'-like int CliNode::cli_show_log_user(const string& , // server_name const string& cli_term_name, uint32_t , // cli_session_id const vector& , // command_global_name const vector&argv) { CliClient *cli_client = find_cli_by_term_name(cli_term_name); if (cli_client == NULL) return (XORP_ERROR); string user_name; // The optional user name to return info for bool user_name_found = false; if (argv.size()) { user_name = argv[0]; cli_client->cli_print(c_format("Showing information about user '%s'\n", user_name.c_str())); } else { user_name_found = true; } list::iterator iter; for (iter = client_list().begin(); iter != client_list().end(); ++iter) { CliClient *tmp_cli_client = *iter; if (user_name.size() && (user_name != tmp_cli_client->cli_session_user_name())) { continue; } user_name_found = true; // Get the start time TimeVal start_time_tv = tmp_cli_client->cli_session_start_time(); string start_time; { char buf[sizeof("999999999/99/99 99/99/99.999999999 ")]; size_t maxlen = sizeof(buf); time_t time_clock = start_time_tv.sec(); struct tm *local_time = localtime(&time_clock); if (strftime(buf, sizeof(buf), "%Y/%m/%d %H:%M:%S", local_time) == 0) { snprintf(buf, maxlen / sizeof(buf[0]), "strftime ERROR"); } start_time = buf; } cli_client->cli_print( c_format("%-16s%-16s%-16s%-16s\n", tmp_cli_client->cli_session_user_name().c_str(), tmp_cli_client->cli_session_term_name().c_str(), cstring(tmp_cli_client->cli_session_from_address()), start_time.c_str()) ); } if (user_name.size() && (! user_name_found)) { cli_client->cli_print( c_format("No such user '%s'\n", user_name.c_str()) ); } #if 0 list::iterator iter; for (iter = client_list().begin(); iter != client_list().end(); ++iter) { CliClient *tmp_cli_client = *iter; cli_client->cli_print( c_format("%-16s%-16s%-16s%-16s - %-16s (%s)\n", tmp_cli_client->cli_session_user_name().c_str(), tmp_cli_client->cli_session_term_name().c_str(), cstring(tmp_cli_client->cli_session_from_address()), tmp_cli_client->cli_session_start_time().c_str(), tmp_cli_client->cli_session_stop_time().c_str(), tmp_cli_client->cli_session_duration_time().c_str()) ); } #endif return (XORP_OK); } // // CLI COMMAND: "set log output cli | 'all' " // // Add CLI terminal to the set of output destinations of log messages // TODO: this is a home-brew own command // TODO: this command should set state in the appropriate protocols and // start getting the logs from each of them (through XRLs) instead // of applying it only to the local process logs. int CliNode::cli_set_log_output_cli(const string& , // server_name const string& cli_term_name, uint32_t , // cli_session_id const vector& , // command_global_name const vector& argv) { CliClient *cli_client = find_cli_by_term_name(cli_term_name); if (cli_client == NULL) return (XORP_ERROR); CliClient *tmp_cli_client; string term_name; uint32_t cli_n = 0; if (argv.empty()) { cli_client->cli_print("ERROR: missing CLI terminal name\n"); return (XORP_ERROR); } term_name = argv[0]; if (term_name == "all") { list::iterator iter; for (iter = client_list().begin(); iter != client_list().end(); ++iter) { tmp_cli_client = *iter; if (! tmp_cli_client->is_log_output()) { if (tmp_cli_client->set_log_output(true) == XORP_OK) { cli_n++; } else { cli_client->cli_print( c_format("ERROR: cannot add CLI terminal " "'%s' as log output\n", tmp_cli_client->cli_session_term_name().c_str())); } } } } else { tmp_cli_client = find_cli_by_term_name(term_name); if (tmp_cli_client == NULL) { cli_client->cli_print( c_format("ERROR: cannot find CLI terminal '%s'\n", term_name.c_str())); return (XORP_ERROR); } if (! tmp_cli_client->is_log_output()) { if (tmp_cli_client->set_log_output(true) == XORP_OK) { cli_n++; } else { cli_client->cli_print( c_format("ERROR: cannot add CLI terminal " "'%s' as log output\n", tmp_cli_client->cli_session_term_name().c_str()) ); return (XORP_ERROR); } } } cli_client->cli_print(c_format("Added %u CLI terminal(s)\n", XORP_UINT_CAST(cli_n))); return (XORP_OK); } // // CLI COMMAND: "set log output cli remove | 'all' " // // Remove CLI terminal from the set of output destinations of log messages // TODO: this is a home-brew own command // TODO: merge this function with "set log output cli" int CliNode::cli_set_log_output_remove_cli(const string& , // server_name const string& cli_term_name, uint32_t , // cli_session_id const vector& , // command_global_name const vector& argv) { CliClient *cli_client = find_cli_by_term_name(cli_term_name); if (cli_client == NULL) return (XORP_ERROR); CliClient *tmp_cli_client; string term_name; uint32_t cli_n = 0; if (argv.empty()) { cli_client->cli_print("ERROR: missing CLI terminal name\n"); return (XORP_ERROR); } term_name = argv[0]; if (term_name == "all") { list::iterator iter; for (iter = client_list().begin(); iter != client_list().end(); ++iter) { tmp_cli_client = *iter; if (tmp_cli_client->is_log_output()) { if (tmp_cli_client->set_log_output(false) == XORP_OK) { cli_n++; } else { cli_client->cli_print( c_format("ERROR: cannot remove CLI terminal " "'%s' as log output\n", tmp_cli_client->cli_session_term_name().c_str())); } } } } else { tmp_cli_client = find_cli_by_term_name(term_name); if (tmp_cli_client == NULL) { cli_client->cli_print( c_format("ERROR: cannot find CLI terminal '%s'\n", term_name.c_str())); return (XORP_ERROR); } if (tmp_cli_client->is_log_output()) { if (tmp_cli_client->set_log_output(false) == XORP_OK) { cli_n++; } else { cli_client->cli_print( c_format("ERROR: cannot remove CLI terminal " "'%s' from log output\n", tmp_cli_client->cli_session_term_name().c_str())); return (XORP_ERROR); } } } cli_client->cli_print(c_format("Removed %u CLI terminal(s)\n", XORP_UINT_CAST(cli_n))); return (XORP_OK); } // // CLI COMMAND: "set log output file " // // Add a file to the set of output destinations of log messages // TODO: this is a home-brew own command // TODO: this command should set state in the appropriate protocols and // start getting the logs from each of them (through XRLs) instead // of applying it only to the local process logs. int CliNode::cli_set_log_output_file(const string& , // server_name const string& cli_term_name, uint32_t , // cli_session_id const vector& , // command_global_name const vector& argv) { CliClient *cli_client = find_cli_by_term_name(cli_term_name); if (cli_client == NULL) return (XORP_ERROR); string file_name; if (argv.empty()) { cli_client->cli_print("ERROR: missing file name\n"); return (XORP_ERROR); } file_name = argv[0]; cli_client->cli_print("TODO: function not implemented yet\n"); return (XORP_OK); } // // CLI COMMAND: "set log output remove file " // // Remove a file from the set of output destinations of log messages // TODO: this is a home-brew own command int CliNode::cli_set_log_output_remove_file(const string& , // server_name const string& cli_term_name, uint32_t , // cli_session_id const vector& , // command_global_name const vector& argv) { CliClient *cli_client = find_cli_by_term_name(cli_term_name); if (cli_client == NULL) return (XORP_ERROR); string file_name; if (argv.empty()) { cli_client->cli_print("ERROR: missing file name\n"); return (XORP_ERROR); } file_name = argv[0]; cli_client->cli_print("TODO: function not implemented yet\n"); return (XORP_OK); } xorp/cli/cli_node_net.cc0000664000076400007640000006743611703345405015402 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2012 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // CLI network-related functions // #include "cli_module.h" #include "libxorp/xorp.h" #include #ifdef HAVE_NETDB_H #include #endif #ifdef HAVE_TERMIOS_H #include #endif #ifdef HAVE_SYS_IOCTL_H #include #endif #ifdef HAVE_FCNTL_H #include #endif #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvx.hh" #include "libxorp/c_format.hh" #include "libxorp/time_slice.hh" #include "libxorp/token.hh" #include "libcomm/comm_api.h" #include "cli_client.hh" #include "cli_private.hh" #include "libxorp/build_info.hh" #ifdef HAVE_ARPA_TELNET_H #include #endif #ifdef HOST_OS_WINDOWS #include "libxorp/win_io.h" #define DEFAULT_TERM_TYPE "ansi-nt" #define FILENO(x) ((HANDLE)_get_osfhandle(_fileno(x))) #define FDOPEN(x,y) _fdopen(_open_osfhandle((x),_O_RDWR|_O_TEXT),(y)) #else // ! HOST_OS_WINDOWS #define DEFAULT_TERM_TYPE "vt100" #define FILENO(x) fileno(x) #define FDOPEN(x,y) fdopen((x), (y)) #endif // HOST_OS_WINDOWS static set local_cli_clients_; #ifndef HOST_OS_WINDOWS static void sigwinch_handler(int signo) { XLOG_ASSERT(signo == SIGWINCH); set::iterator iter; for (iter = local_cli_clients_.begin(); iter != local_cli_clients_.end(); ++iter) { CliClient *cli_client = *iter; cli_client->terminal_resized(); } } #endif // ! HOST_OS_WINDOWS /** * CliNode::sock_serv_open: * * Open a socket for the CLI to listen on for connections. * * Return value: The new socket to listen on success, othewise a XockFd * that contains an invalid socket. **/ XorpFd CliNode::sock_serv_open() { // Open the socket switch (family()) { case AF_INET: _cli_socket = comm_bind_tcp4(NULL, _cli_port, COMM_SOCK_NONBLOCKING); break; #ifdef HAVE_IPV6 case AF_INET6: _cli_socket = comm_bind_tcp6(NULL, 0, _cli_port, COMM_SOCK_NONBLOCKING); break; #endif // HAVE_IPV6 default: XLOG_UNREACHABLE(); _cli_socket.clear(); break; } if (comm_listen(_cli_socket, COMM_LISTEN_DEFAULT_BACKLOG) != XORP_OK) { _cli_socket.clear(); } return (_cli_socket); } /** * CliNode::sock_serv_close: * @: * * Close the socket that is used by the CLI to listen on for connections. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int CliNode::sock_serv_close() { int ret_value = XORP_OK; if (!_cli_socket.is_valid()) return (XORP_OK); // Nothing to do if (comm_close(_cli_socket) != XORP_OK) ret_value = XORP_ERROR; _cli_socket.clear(); return (ret_value); } void CliNode::accept_connection(XorpFd fd, IoEventType type) { string error_msg; debug_msg("Received connection on socket = %s, family = %d\n", fd.str().c_str(), family()); XLOG_ASSERT(type == IOT_ACCEPT); XorpFd client_socket = comm_sock_accept(fd); if (client_socket.is_valid()) { if (add_connection(client_socket, client_socket, true, _startup_cli_prompt, error_msg) == NULL) { XLOG_ERROR("Cannot accept CLI connection: %s", error_msg.c_str()); } } } CliClient * CliNode::add_connection(XorpFd input_fd, XorpFd output_fd, bool is_network, const string& startup_cli_prompt, string& error_msg) { string dummy_error_msg; CliClient *cli_client = NULL; debug_msg("Added connection with input_fd = %s, output_fd = %s, " "family = %d\n", input_fd.str().c_str(), output_fd.str().c_str(), family()); cli_client = new CliClient(*this, input_fd, output_fd, startup_cli_prompt); cli_client->set_network_client(is_network); _client_list.push_back(cli_client); #ifdef HOST_OS_WINDOWS if (cli_client->is_interactive()) { BOOL retval; #if 0 // XXX: This always fails, so it's commented out. retval = SetConsoleMode(input_fd, ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT); if (retval == 0) { XLOG_WARNING("SetConsoleMode(input) failed: %s", win_strerror(GetLastError())); } #endif retval = SetConsoleMode(output_fd, ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT); if (retval == 0) { XLOG_WARNING("SetConsoleMode(output) failed: %s", win_strerror(GetLastError())); } } #endif // // Set peer address (for network connection only) // if (cli_client->is_network()) { struct sockaddr_storage ss; socklen_t len = sizeof(ss); // XXX if (getpeername(cli_client->input_fd(), (struct sockaddr *)&ss, &len) < 0) { error_msg = c_format("Cannot get peer name"); // Error getting peer address delete_connection(cli_client, dummy_error_msg); return (NULL); } IPvX peer_addr = IPvX::ZERO(family()); // XXX: The fandango below can go away once the IPvX // constructors are fixed to do the right thing. switch (ss.ss_family) { case AF_INET: { struct sockaddr_in *s_in = (struct sockaddr_in *)&ss; peer_addr.copy_in(*s_in); } break; #ifdef HAVE_IPV6 case AF_INET6: { struct sockaddr_in6 *s_in6 = (struct sockaddr_in6 *)&ss; peer_addr.copy_in(*s_in6); } break; #endif // HAVE_IPV6 default: // Invalid address family error_msg = c_format("Cannot set peer address: " "invalid address family (%d)", ss.ss_family); delete_connection(cli_client, dummy_error_msg); return (NULL); } cli_client->set_cli_session_from_address(peer_addr); } // // Check access control for this peer address // if (! is_allow_cli_access(cli_client->cli_session_from_address())) { error_msg = c_format("CLI access from address %s is not allowed", cli_client->cli_session_from_address().str().c_str()); delete_connection(cli_client, dummy_error_msg); return (NULL); } if (cli_client->start_connection(error_msg) != XORP_OK) { // Error connecting to the client delete_connection(cli_client, dummy_error_msg); return (NULL); } // // Connection OK // // // Set user name // cli_client->set_cli_session_user_name("guest"); // TODO: get user name // // Set terminal name // { string term_name = "cli_unknown"; uint32_t i = 0; for (i = 0; i < CLI_MAX_CONNECTIONS; i++) { term_name = c_format("cli%u", XORP_UINT_CAST(i)); if (find_cli_by_term_name(term_name) == NULL) break; } if (i >= CLI_MAX_CONNECTIONS) { // Too many connections error_msg = c_format("Too many CLI connections (max is %u)", XORP_UINT_CAST(CLI_MAX_CONNECTIONS)); delete_connection(cli_client, dummy_error_msg); return (NULL); } cli_client->set_cli_session_term_name(term_name); } // // Set session id // { uint32_t session_id = ~0U; // XXX: ~0U has no particular meaning uint32_t i = 0; for (i = 0; i < CLI_MAX_CONNECTIONS; i++) { session_id = _next_session_id++; if (find_cli_by_session_id(session_id) == NULL) break; } if (i >= CLI_MAX_CONNECTIONS) { // This should not happen: there are available session slots, // but no available session IDs. XLOG_FATAL("Cannot assign CLI session ID"); return (NULL); } cli_client->set_cli_session_session_id(session_id); } // // Set session start time // { TimeVal now; eventloop().current_time(now); cli_client->set_cli_session_start_time(now); } cli_client->set_is_cli_session_active(true); return (cli_client); } int CliNode::delete_connection(CliClient *cli_client, string& error_msg) { list::iterator iter; iter = find(_client_list.begin(), _client_list.end(), cli_client); if (iter == _client_list.end()) { error_msg = c_format("Cannot delete CLI connection: invalid client"); return (XORP_ERROR); } debug_msg("Delete connection on input fd = %s, output fd = %s, " "family = %d\n", cli_client->input_fd().str().c_str(), cli_client->output_fd().str().c_str(), family()); cli_client->cli_flush(); // The callback when deleting this client if (! _cli_client_delete_callback.is_empty()) _cli_client_delete_callback->dispatch(cli_client); if (cli_client->is_network()) { // XXX: delete the client only if this was a network connection _client_list.erase(iter); delete cli_client; } else { eventloop().remove_ioevent_cb(cli_client->input_fd(), IOT_READ); } return (XORP_OK); } int CliClient::start_connection(string& error_msg) { if (cli_node().eventloop().add_ioevent_cb( input_fd(), IOT_READ, callback(this, &CliClient::client_read), XorpTask::PRIORITY_HIGHEST) == false) { return (XORP_ERROR); } #ifdef HAVE_ARPA_TELNET_H // // Setup the telnet options // if (is_telnet()) { uint8_t will_echo_cmd[] = { IAC, WILL, TELOPT_ECHO, '\0' }; uint8_t will_sga_cmd[] = { IAC, WILL, TELOPT_SGA, '\0' }; uint8_t dont_linemode_cmd[] = { IAC, DONT, TELOPT_LINEMODE, '\0' }; uint8_t do_window_size_cmd[] = { IAC, DO, TELOPT_NAWS, '\0' }; uint8_t do_transmit_binary_cmd[] = { IAC, DO, TELOPT_BINARY, '\0' }; uint8_t will_transmit_binary_cmd[] = { IAC, WILL, TELOPT_BINARY, '\0' }; send(input_fd(), will_echo_cmd, sizeof(will_echo_cmd), 0); send(input_fd(), will_sga_cmd, sizeof(will_sga_cmd), 0); send(input_fd(), dont_linemode_cmd, sizeof(dont_linemode_cmd), 0); send(input_fd(), do_window_size_cmd, sizeof(do_window_size_cmd), 0); send(input_fd(), do_transmit_binary_cmd, sizeof(do_transmit_binary_cmd), 0); send(input_fd(), will_transmit_binary_cmd, sizeof(will_transmit_binary_cmd), 0); } #endif #ifndef HOST_OS_WINDOWS if (! is_network()) { signal(SIGWINCH, sigwinch_handler); } #endif #ifdef HAVE_TERMIOS_H // // Put the terminal in non-canonical and non-echo mode. // In addition, disable signals INTR, QUIT, [D]SUSP // (i.e., force their value to be received when read from the terminal). // if (is_output_tty()) { struct termios termios; // // Get the parameters associated with the terminal // while (tcgetattr(output_fd(), &termios) != 0) { if (errno != EINTR) { error_msg = c_format("start_connection(): " "tcgetattr() error: %s", strerror(errno)); return (XORP_ERROR); } } // // Save state regarding any terminal-related modifications we may do // if (termios.c_lflag & ICANON) _is_modified_stdio_termios_icanon = true; if (termios.c_lflag & ECHO) _is_modified_stdio_termios_echo = true; if (termios.c_lflag & ISIG) _is_modified_stdio_termios_isig = true; _saved_stdio_termios_vmin = termios.c_cc[VMIN]; _saved_stdio_termios_vtime = termios.c_cc[VTIME]; // // Change the termios: // - Reset the flags. // - Set VMIN and VTIME to values that allow us to read one // character at a time. // termios.c_lflag &= ~(ICANON | ECHO | ISIG); termios.c_cc[VMIN] = 1; termios.c_cc[VTIME] = 0; // // Modify the terminal // while (tcsetattr(output_fd(), TCSADRAIN, &termios) != 0) { if (errno != EINTR) { error_msg = c_format("start_connection(): " "tcsetattr() error: %s", strerror(errno)); return (XORP_ERROR); } } } #endif // HAVE_TERMIOS_H // // Setup the read/write file descriptors // if (input_fd() == XorpFd(FILENO(stdin))) { _input_fd_file = stdin; } else { _input_fd_file = FDOPEN(input_fd(), "r"); if (_input_fd_file == NULL) { error_msg = c_format("Cannot associate a stream with the " "input file descriptor: %s", strerror(errno)); return (XORP_ERROR); } } if (output_fd() == XorpFd(FILENO(stdout))) { _output_fd_file = stdout; } else { _output_fd_file = FDOPEN(output_fd(), "w"); if (_output_fd_file == NULL) { error_msg = c_format("Cannot associate a stream with the " "output file descriptor: %s", strerror(errno)); return (XORP_ERROR); } } _gl = new_GetLine(1024, 2048); // TODO: hardcoded numbers if (_gl == NULL) { error_msg = c_format("Cannot create a new GetLine instance"); return (XORP_ERROR); } // XXX: always set to network type gl_set_is_net(_gl, 1); // Set the terminal string term_name = DEFAULT_TERM_TYPE; if (is_output_tty()) { #ifdef HOST_OS_WINDOWS // // Do not ask the environment what kind of terminal we use // under Windows, as MSYS is known to lie to us and say 'cygwin' // when in fact we're using an 'ansi-nt'. We've hard-coded // appropriate control sequences in our fork of libtecla to // reflect this fact. // XXX: We need a better way of figuring out when we're in // this situation. ; // do nothing #else char *term = getenv("TERM"); if ((term != NULL) && (! string(term).empty())) term_name = string(term); #endif } // // Change the input and output streams for libtecla // // Note that it must happen before gl_terminal_size(), // because gl_change_terminal() resets internally the terminal // size to its default value. // if (gl_change_terminal(_gl, _input_fd_file, _output_fd_file, term_name.c_str()) != 0) { error_msg = c_format("Cannot change the I/O streams"); _gl = del_GetLine(_gl); return (XORP_ERROR); } // Update the terminal size update_terminal_size(); // Add the command completion hook if (gl_customize_completion(_gl, this, command_completion_func) != 0) { error_msg = c_format("Cannot customize command-line completion"); _gl = del_GetLine(_gl); return (XORP_ERROR); } // // Key bindings // // Bind Ctrl-C to no-op gl_configure_getline(_gl, "bind ^C user-event4", NULL, NULL); // Bind Ctrl-W to delete the word before the cursor, because // the default libtecla behavior is to delete the whole line. gl_configure_getline(_gl, "bind ^W backward-delete-word", NULL, NULL); // Add ourselves to the local set of clients local_cli_clients_.insert(this); // Print the welcome message char hostname[MAXHOSTNAMELEN]; if (gethostname(hostname, sizeof(hostname)) < 0) { XLOG_ERROR("gethostname() failed: %s", strerror(errno)); // XXX: if gethostname() fails, then default to "xorp" strncpy(hostname, "xorp", sizeof(hostname) - 1); } hostname[sizeof(hostname) - 1] = '\0'; cli_print(c_format("Welcome to XORP v%s on %s\n", BuildInfo::getXorpVersion(), hostname)); #ifdef XORP_BUILDINFO const char* bits = "32-bit"; if (sizeof(void*) == 8) bits = "64-bit"; cli_print(c_format("Version tag: %s Build Date: %s %s\n", BuildInfo::getGitVersion(), BuildInfo::getShortBuildDate(), bits)); #endif // Show the prompt cli_print(current_cli_prompt()); return (XORP_OK); } int CliClient::stop_connection(string& error_msg) { // Delete ourselves from the local set of clients local_cli_clients_.erase(this); #ifdef HAVE_TERMIOS_H // // Restore the terminal settings // if (is_output_tty()) { struct termios termios; // // Get the parameters associated with the terminal // while (tcgetattr(output_fd(), &termios) != 0) { if (errno != EINTR) { XLOG_ERROR("stop_connection(): tcgetattr() error: %s", strerror(errno)); return (XORP_ERROR); } } // // Restore the termios changes // if (_is_modified_stdio_termios_icanon) termios.c_lflag |= ICANON; if (_is_modified_stdio_termios_echo) termios.c_lflag |= ECHO; if (_is_modified_stdio_termios_isig) termios.c_lflag |= ISIG; _saved_stdio_termios_vmin = termios.c_cc[VMIN]; _saved_stdio_termios_vtime = termios.c_cc[VTIME]; // // Modify the terminal // while (tcsetattr(output_fd(), TCSADRAIN, &termios) != 0) { if (errno != EINTR) { error_msg = c_format("stop_connection(): " "tcsetattr() error: %s", strerror(errno)); return (XORP_ERROR); } } } #endif // HAVE_TERMIOS_H error_msg = ""; return (XORP_OK); } void CliClient::terminal_resized() { update_terminal_size(); } void CliClient::update_terminal_size() { #ifdef HAVE_TERMIOS_H // Get the terminal size if (is_output_tty()) { struct winsize window_size; if (ioctl(output_fd(), TIOCGWINSZ, &window_size) < 0) { XLOG_ERROR("Cannot get window size (ioctl(TIOCGWINSZ) failed): %s", strerror(errno)); } else { // Set the window width and height uint16_t new_window_width, new_window_height; new_window_width = window_size.ws_col; new_window_height = window_size.ws_row; if (new_window_width > 0) { set_window_width(new_window_width); } else { cli_print(c_format("Invalid window width (%u); " "window width unchanged (%u)\n", new_window_width, XORP_UINT_CAST(window_width()))); } if (new_window_height > 0) { set_window_height(new_window_height); } else { cli_print(c_format("Invalid window height (%u); " "window height unchanged (%u)\n", new_window_height, XORP_UINT_CAST(window_height()))); } gl_terminal_size(gl(), window_width(), window_height()); debug_msg("Client window size changed to width = %u " "height = %u\n", XORP_UINT_CAST(window_width()), XORP_UINT_CAST(window_height())); } } #endif // HAVE_TERMIOS_H } // // If @v is true, block the client terminal, otherwise unblock it. // void CliClient::set_is_waiting_for_data(bool v) { _is_waiting_for_data = v; // block_connection(v); } // // If @is_blocked is true, block the connection (by removing its I/O // event hook), otherwise add its socket back to the event loop. // // Return: %XORP_OK on success, otherwise %XORP_ERROR. int CliClient::block_connection(bool is_blocked) { if (!input_fd().is_valid()) return (XORP_ERROR); if (is_blocked) { cli_node().eventloop().remove_ioevent_cb(input_fd(), IOT_READ); return (XORP_OK); } if (cli_node().eventloop().add_ioevent_cb(input_fd(), IOT_READ, callback(this, &CliClient::client_read), XorpTask::PRIORITY_HIGHEST) == false) return (XORP_ERROR); return (XORP_OK); } void CliClient::client_read(XorpFd fd, IoEventType type) { string dummy_error_msg; char buf[1024]; // TODO: 1024 size must be #define int n; XLOG_ASSERT(type == IOT_READ); #ifdef HOST_OS_WINDOWS if (!is_interactive()) { n = recv(fd, buf, sizeof(buf), 0); } else { // // A 0-byte interactive read is not an error; it may simply // mean the read routine filtered out an event which we // weren't interested in. // n = win_con_read(fd, buf, sizeof(buf)); if (n == 0) { return; } } #else /* !HOST_OS_WINDOWS */ n = read(fd, buf, sizeof(buf) - 1); #endif /* HOST_OS_WINDOWS */ debug_msg("client_read %d octet(s)\n", n); if (n <= 0) { cli_node().delete_connection(this, dummy_error_msg); return; } // Add the new data to the buffer with the pending data size_t old_size = _pending_input_data.size(); _pending_input_data.resize(old_size + n); memcpy(&_pending_input_data[old_size], buf, n); process_input_data(); } void CliClient::process_input_data() { int ret_value; string dummy_error_msg; vector input_data = _pending_input_data; bool stop_processing = false; // // XXX: Remove the stored input data. Later we will add-back // only the data which we couldn't process. // _pending_input_data.clear(); TimeSlice time_slice(1000000, 1); // 1s, test every iteration // Process the input data vector::iterator iter; for (iter = input_data.begin(); iter != input_data.end(); ++iter) { uint8_t val = *iter; bool ignore_current_character = false; if (is_telnet()) { // Filter-out the Telnet commands bool is_telnet_option = false; int ret = process_telnet_option(val, is_telnet_option); if (ret != XORP_OK) { // Kick-out the client // TODO: print more informative message about the client: // E.g. where it came from, etc. XLOG_WARNING("Removing client (socket = %s family = %d): " "error processing telnet option", input_fd().str().c_str(), cli_node().family()); cli_node().delete_connection(this, dummy_error_msg); return; } if (is_telnet_option) { // Telnet option processed continue; } } if (val == CHAR_TO_CTRL('c')) { // // Interrupt current command // interrupt_command(); _pending_input_data.clear(); return; } if (stop_processing) continue; preprocess_char(val, stop_processing); if (is_waiting_for_data() && (! is_page_mode())) { stop_processing = true; ignore_current_character = true; } if (! stop_processing) { // // Get a character and process it // do { char *line; line = gl_get_line_net(gl(), current_cli_prompt().c_str(), (char *)command_buffer().data(), buff_curpos(), val); ret_value = XORP_ERROR; if (line == NULL) { ret_value = XORP_ERROR; break; } if (is_page_mode()) { ret_value = process_char_page_mode(val); break; } ret_value = process_char(string(line), val, stop_processing); break; } while (false); if (ret_value != XORP_OK) { // Either error or end of input cli_print("\nEnd of connection.\n"); cli_node().delete_connection(this, dummy_error_msg); return; } } if (time_slice.is_expired()) { stop_processing = true; } if (stop_processing) { // // Stop processing and save the remaining input data for later // processing. // However we continue scanning the rest of the data // primarily to look for Ctrl-C input. // vector::iterator iter2 = iter; if (! ignore_current_character) ++iter2; if (iter2 != input_data.end()) _pending_input_data.assign(iter2, input_data.end()); } } if (! _pending_input_data.empty()) schedule_process_input_data(); cli_flush(); // Flush-out the output } // // Schedule a timer to process (pending) input data // void CliClient::schedule_process_input_data() { EventLoop& eventloop = cli_node().eventloop(); OneoffTimerCallback cb = callback(this, &CliClient::process_input_data); // // XXX: Schedule the processing after 10ms to avoid increasing // the CPU usage. // _process_pending_input_data_timer = eventloop.new_oneoff_after( TimeVal(0, 10), cb, XorpTask::PRIORITY_LOWEST); } // // Preprocess a character before 'libtecla' get its hand on it // int CliClient::preprocess_char(uint8_t val, bool& stop_processing) { stop_processing = false; if (is_page_mode()) return (XORP_OK); if ((val == '\n') || (val == '\r')) { // New command if (is_waiting_for_data()) stop_processing = true; return (XORP_OK); } // // XXX: Bind/unbind the 'SPACE' to complete-word so it can // complete a half-written command. // TODO: if the beginning of the line, shall we explicitly unbind as well? // if (val == ' ') { int tmp_buff_curpos = buff_curpos(); char *tmp_line = (char *)command_buffer().data(); string command_line = string(tmp_line, tmp_buff_curpos); if (! is_multi_command_prefix(command_line)) { // Un-bind the "SPACE" to complete-word // Don't ask why we need six '\' to specify the ASCII value // of 'SPACE'... gl_configure_getline(gl(), "bind \\\\\\040 ", NULL, NULL); } else { // Bind the "SPACE" to complete-word gl_configure_getline(gl(), "bind \\\\\\040 complete-word", NULL, NULL); } return (XORP_OK); } return (XORP_OK); } int CliClient::process_telnet_option(int val, bool& is_telnet_option) { #ifdef HOST_OS_WINDOWS UNUSED(val); is_telnet_option = false; return (XORP_OK); #else is_telnet_option = true; if (val == IAC) { // Probably a telnet command if (! telnet_iac()) { set_telnet_iac(true); return (XORP_OK); } set_telnet_iac(false); } if (telnet_iac()) { switch (val) { case SB: // Begin subnegotiation of the indicated option. telnet_sb_buffer().reset(); set_telnet_sb(true); break; case SE: // End subnegotiation of the indicated option. if (! telnet_sb()) break; switch(telnet_sb_buffer().data(0)) { case TELOPT_NAWS: // Telnet Window Size Option if (telnet_sb_buffer().data_size() < 5) break; { uint16_t new_window_width, new_window_height; new_window_width = 256*telnet_sb_buffer().data(1); new_window_width += telnet_sb_buffer().data(2); new_window_height = 256*telnet_sb_buffer().data(3); new_window_height += telnet_sb_buffer().data(4); if (new_window_width > 0) { set_window_width(new_window_width); } else { cli_print(c_format("Invalid window width (%u); " "window width unchanged (%u)\n", new_window_width, XORP_UINT_CAST(window_width()))); } if (new_window_height > 0) { set_window_height(new_window_height); } else { cli_print(c_format("Invalid window height (%u); " "window height unchanged (%u)\n", new_window_height, XORP_UINT_CAST(window_height()))); } gl_terminal_size(gl(), window_width(), window_height()); debug_msg("Client window size changed to width = %u " "height = %u\n", XORP_UINT_CAST(window_width()), XORP_UINT_CAST(window_height())); } break; default: break; } telnet_sb_buffer().reset(); set_telnet_sb(false); break; case DONT: // you are not to use option set_telnet_dont(true); break; case DO: // please, you use option set_telnet_do(true); break; case WONT: // I won't use option set_telnet_wont(true); break; case WILL: // I will use option set_telnet_will(true); break; case GA: // you may reverse the line break; case EL: // erase the current line break; case EC: // erase the current character break; case AYT: // are you there break; case AO: // abort output--but let prog finish break; case IP: // interrupt process--permanently break; case BREAK: // break break; case DM: // data mark--for connect. cleaning break; case NOP: // nop break; case EOR: // end of record (transparent mode) break; case ABORT: // Abort process break; case SUSP: // Suspend process break; case xEOF: // End of file: EOF is already used... break; case TELOPT_BINARY: if (telnet_do()) set_telnet_binary(true); else set_telnet_binary(false); break; default: break; } set_telnet_iac(false); return (XORP_OK); } // // Cleanup the telnet options state // if (telnet_sb()) { // A negotiated option value if (telnet_sb_buffer().add_data(val) != XORP_OK) { // This client is sending too much options. Kick it out! return (XORP_ERROR); } return (XORP_OK); } if (telnet_dont()) { // Telnet DONT option code set_telnet_dont(false); return (XORP_OK); } if (telnet_do()) { // Telnet DO option code set_telnet_do(false); return (XORP_OK); } if (telnet_wont()) { // Telnet WONT option code set_telnet_wont(false); return (XORP_OK); } if (telnet_will()) { // Telnet WILL option code set_telnet_will(false); return (XORP_OK); } // XXX: Not a telnet option is_telnet_option = false; return (XORP_OK); #endif // HOST_OS_WINDOWS } xorp/cli/cli_command.hh0000664000076400007640000005575411540224220015225 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/cli/cli_command.hh,v 1.30 2008/10/02 21:56:29 bms Exp $ #ifndef __CLI_CLI_COMMAND_HH__ #define __CLI_CLI_COMMAND_HH__ // // CLI command definition. // #include "libxorp/callback.hh" #include "cli/libtecla/libtecla.h" // // Constants definitions // // // Structures/classes, typedefs and macros // class CliCommand; class CliClient; class CliCommandMatch; // // The callback to print-out the return result from a processing function // typedef XorpCallback5&, /* command_global_name */ const vector& /* command_args */ >::RefPtr CLI_PROCESS_CALLBACK; typedef XorpCallback5&, /* command_global_name */ const vector& /* command_args */ >::RefPtr CLI_INTERRUPT_CALLBACK; typedef XorpCallback1, /* return value */ const vector& /* global_name */ >::RefPtr DYNAMIC_CHILDREN_CALLBACK; // // The type of a processing function that handles CLI commands // typedef int (* CLI_PROCESS_FUNC)(const string& server_name, const string& cli_term_name, uint32_t cli_session_id, const vector& command_global_name, const vector& command_args); typedef void (* CLI_INTERRUPT_FUNC)(const string& server_name, const string& cli_term_name, uint32_t cli_session_id, const vector& command_global_name, const vector& command_args); // // The type of a function that handles CLI command completions // #define CLI_COMPLETION_FUNC_(func) bool (func)(void *obj, \ WordCompletion *cpl, \ void *data, \ const char *line, \ int word_end, \ list& cli_command_match_list) typedef CLI_COMPLETION_FUNC_(CLI_COMPLETION_FUNC); /** * @short The class for the CLI command. */ class CliCommand { public: typedef XorpCallback2::RefPtr TypeMatchCb; /** * Constructor for a given parent command, command name, and command help. * * @param init_parent_command the parent @ref CliCommand command. * @param init_command_name the command name (this name should not * include the command name of the parent command and its ancestors). * @param init_command_help the command help. */ CliCommand(CliCommand *init_parent_command, const string& init_command_name, const string& init_command_help); /** * Destructor */ virtual ~CliCommand(); /** * Enable/disable whether this command allows "change directory" to it. * * @param v if true, enable "change directory", otherwise disable it. * @param init_cd_prompt if @ref v is true, the CLI prompt to display * when "cd" to this command. If an empty string, the CLI prompt will * not be changed. */ void set_allow_cd(bool v, const string& init_cd_prompt); /** * Create the default CLI commands at each level of the command tree. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int create_default_cli_commands(); /** * Create and add the default CLI pipe commands. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_pipes(string& error_msg); /** * Add a CLI command. * * By default, we cannot "cd" to this command. * * @param init_command_name the command name to add. * If @ref is_multilevel_command is true, then it may include * more than one command levels in the middle. E.g., "show version pim". * However, commands "show" and "show version" must have been installed * first. * @param init_command_help the command help. * @param is_multilevel_command if true, then @ref init_command_name * may include more than one command levels in the middle. * @param error_msg the error message (if error). * @return the new child command on success, otherwise NULL. */ CliCommand *add_command(const string& init_command_name, const string& init_command_help, bool is_multilevel_command, string& error_msg); /** * Add a CLI command we can "cd" to it. * * By default, we can "cd" to this command. * * @param init_command_name the command name to add. * If @ref is_multilevel_command is true, then it may include * more than one command levels in the middle. E.g., "set pim bsr". * However, commands "set" and "set pim" must have been installed first. * @param init_command_help the command help. * @param init_cd_prompt if not an empty string, the CLI prompt * when "cd" to this command. * @param is_multilevel_command if true, then @ref init_command_name * may include more than one command levels in the middle. * @param error_msg the error message (if error). * @return the new child command on success, otherwise NULL. */ CliCommand *add_command(const string& init_command_name, const string& init_command_help, const string& init_cd_prompt, bool is_multilevel_command, string& error_msg); /** * Add a CLI command with a processing callback. * * @param init_command_name the command name to add. * If @ref is_multilevel_command is true, then it may include * more than one command levels in the middle. E.g., "show version pim". * However, commands "show" and "show version" must have been installed * first. * @param init_command_help the command help. * @param is_multilevel_command if true, then @ref init_command_name * may include more than one command levels in the middle. * @param init_cli_process_callback the callback to call when the * command is entered for execution from the command-line. * @param error_msg the error message (if error). * @return the new child command on success, otherwise NULL. */ CliCommand *add_command(const string& init_command_name, const string& init_command_help, bool is_multilevel_command, const CLI_PROCESS_CALLBACK& init_cli_process_callback, string& error_msg); /** * Add a CLI command with a processing and an interrupt callbacks. * * @param init_command_name the command name to add. * If @ref is_multilevel_command is true, then it may include * more than one command levels in the middle. E.g., "show version pim". * However, commands "show" and "show version" must have been installed * first. * @param init_command_help the command help. * @param is_multilevel_command if true, then @ref init_command_name * may include more than one command levels in the middle. * @param init_cli_process_callback the callback to call when the * command is entered for execution from the command-line. * @param init_cli_interrupt_callback the callback to call when the * user has interrupted the command (e.g., by typing Ctrl-C). * @param error_msg the error message (if error). * @return the new child command on success, otherwise NULL. */ CliCommand *add_command(const string& init_command_name, const string& init_command_help, bool is_multilevel_command, const CLI_PROCESS_CALLBACK& init_cli_process_callback, const CLI_INTERRUPT_CALLBACK& init_cli_interrupt_callback, string& error_msg); /** * Add a CLI command with a processing function. * * @param init_command_name the command name to add. * If @ref is_multilevel_command is true, then it may include * more than one command levels in the middle. E.g., "show version pim". * However, commands "show" and "show version" must have been installed * first. * @param init_command_help the command help. * @param is_multilevel_command if true, then @ref init_command_name * may include more than one command levels in the middle. * @param init_cli_process_func the processing function to call when the * command is entered for execution from the command-line. * @param error_msg the error message (if error). * @return the new child command on success, otherwise NULL. */ CliCommand *add_command(const string& init_command_name, const string& init_command_help, bool is_multilevel_command, CLI_PROCESS_FUNC init_cli_process_func, string& error_msg) { CLI_PROCESS_CALLBACK cb = callback(init_cli_process_func); return (add_command(init_command_name, init_command_help, is_multilevel_command, cb, error_msg)); } /** * Add a CLI command with a processing function and an interrupt * handler. * * @param init_command_name the command name to add. * If @ref is_multilevel_command is true, then it may include * more than one command levels in the middle. E.g., "show version pim". * However, commands "show" and "show version" must have been installed * first. * @param init_command_help the command help. * @param is_multilevel_command if true, then @ref init_command_name * may include more than one command levels in the middle. * @param init_cli_process_func the processing function to call when the * command is entered for execution from the command-line. * @param init_cli_interrupt_func the function to call when the * user has interrupted the command (e.g., by typing Ctrl-C). * @param error_msg the error message (if error). * @return the new child command on success, otherwise NULL. */ CliCommand *add_command(const string& init_command_name, const string& init_command_help, bool is_multilevel_command, CLI_PROCESS_FUNC init_cli_process_func, CLI_INTERRUPT_FUNC init_cli_interrupt_func, string& error_msg) { CLI_PROCESS_CALLBACK cb1 = callback(init_cli_process_func); CLI_INTERRUPT_CALLBACK cb2 = callback(init_cli_interrupt_func); return (add_command(init_command_name, init_command_help, is_multilevel_command, cb1, cb2, error_msg)); } /** * Add a child CLI command. * * @param child_command the child command to add. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_command(CliCommand *child_command, string& error_msg); /** * Delete a child command and all sub-commands below it. * * @param child_command the child command to delete. * @return XORP_OK on success, otherwise XORP_ERROR. */ int delete_command(CliCommand *child_command); /** * Delete a child command and all sub-commands below it. * * @param delete_command_name the name of the child command to delete. * The name can be the full path-name for that command. * @return XORP_OK on success, otherwise XORP_ERROR. */ int delete_command(const string& delete_command_name); /** * Recursively delete all children of this command. */ void delete_all_commands(); /** * Set whether the output of this command can be piped. * * @param v if true, then the the output of this command can be piped. */ void set_can_pipe(bool v) { _can_pipe = v; } /** * Test if "no-more" (i.e., unpaged) is the default output mode. * * @return true if "no-more" (i.e., unpaged) is the default output mode, * otherwise false. */ bool default_nomore_mode() const { return (_default_nomore_mode); } /** * Set the default paging mode. * * @param v if true, then "no-more" (i.e., unpaged) is the default * output mode. */ void set_default_nomore_mode(bool v) { _default_nomore_mode = v; } /** * Test if the command actually represents a command argument. * * @return true if the command actually represents a command argument. */ bool is_command_argument() const { return (_is_command_argument); } /** * Set a flag whether the command actually represents a command argument. * * @param v true if the command represents a command argument, otherwise * false. */ void set_is_command_argument(bool v) { _is_command_argument = v; } /** * Test if the command expects an argument. * * @return true if the command expects an argument, otherwise false. */ bool is_argument_expected() const { return (_is_argument_expected); } /** * Set a flag whether the command expects an argument. * * @param v true if the command expectes an argument, otherwise false. */ void set_is_argument_expected(bool v) { _is_argument_expected = v; } /** * Get the callback for type matching. * * @return the callback for type matching. */ const TypeMatchCb& type_match_cb() const { return (_type_match_cb); } /** * Set the callback for type matching. * * @param cb the callback for type matching. */ void set_type_match_cb(const TypeMatchCb& cb) { _type_match_cb = cb; } /** * Test if there is a callback for type matching. * * @return true if there is a callback for type matching, otherwise false. */ bool has_type_match_cb() const; /** * Get the global name of this command (i.e., the full name starting from * the root). * * @return the global (full) name of this command. */ const vector& global_name() const { return (_global_name); } /** * Set the global name for this command. * * @param v the global name value to set. */ void set_global_name(const vector& v) { _global_name = v; } /** * Get the server (i.e., processor) name for this command. * * @return the server name for this command. */ const string& server_name() const { return (_server_name); } /** * Set the server (i.e., processor) name for this command. * * @param v the server name value to set. */ void set_server_name(const string& v) { _server_name = v; } /** * Set the callback for dynamic generation of children commands. * * @param v the callback for dynamic generation of children commands. */ void set_dynamic_children_callback(DYNAMIC_CHILDREN_CALLBACK v); /** * Set the callback for command processing for a dynamically generated * child command. * * @param v the callback for command processing. */ void set_dynamic_process_callback(const CLI_PROCESS_CALLBACK& v) { _dynamic_process_callback = v; } /** * Set the callback for command interrupt for a dynamically generated * child command. * * @param v the callback for command processing. */ void set_dynamic_interrupt_callback(const CLI_INTERRUPT_CALLBACK& v) { _dynamic_interrupt_callback = v; } /** * Set the callback for command processing. * * @param v the callback for command processing. */ void set_cli_process_callback(const CLI_PROCESS_CALLBACK& v) { _cli_process_callback = v; } /** * Set the callback for command interrupt. * * @param v the callback for command processing. */ void set_cli_interrupt_callback(const CLI_INTERRUPT_CALLBACK& v) { _cli_interrupt_callback = v; } private: friend class CliClient; const string& name() const { return (_name); } const string& cd_prompt() { return (_cd_prompt); } list& child_command_list(); static bool cli_attempt_command_completion_byname(void *obj, WordCompletion *cpl, void *data, const char *line, int word_end, list& cli_command_match_list); const string& help() const { return (_help); } const string& help_completion() const { return (_help_completion); } int delete_pipes(); bool is_same_prefix(const string& token); bool is_same_command(const string& token); CliCommand *command_find(const string& token); bool is_multi_command_prefix(const string& command_line); bool find_command_help(const char *line, int word_end, set& help_strings); bool allow_cd() { return (_allow_cd); } bool has_cli_process_callback(); bool has_cli_interrupt_callback(); bool has_dynamic_process_callback(); bool has_dynamic_interrupt_callback(); bool has_cli_completion_func() { return (_cli_completion_func != NULL); } void set_cli_completion_func (CLI_COMPLETION_FUNC *v) { _cli_completion_func = v; } CLI_PROCESS_CALLBACK _cli_process_callback; // The processing callback CLI_INTERRUPT_CALLBACK _cli_interrupt_callback; // The interrupt callback CLI_COMPLETION_FUNC *_cli_completion_func; // The function to call // to complete a command // Store the callback to generate dynamic children DYNAMIC_CHILDREN_CALLBACK _dynamic_children_callback; bool _has_dynamic_children; // The cli_process_callback to copy to dynamic children CLI_PROCESS_CALLBACK _dynamic_process_callback; CLI_INTERRUPT_CALLBACK _dynamic_interrupt_callback; bool can_complete(); bool can_pipe() const { return (_can_pipe); } CliCommand *cli_command_pipe(); void set_cli_command_pipe(CliCommand *v) { _cli_command_pipe = v; } CliCommand *root_command() { return (_root_command); } void set_root_command(CliCommand *v) { _root_command = v; } CliCommand *_root_command; // The root command CliCommand *_parent_command; // Parent command or NULL if // the root list _child_command_list; // A list with child commands const string _name; // The command name const string _help; // The command help vector _global_name; // The command global name string _server_name; // The server to process this command string _help_completion; // The command help completion bool _allow_cd; // True if we can "cd" to this string _cd_prompt; // The prompt if we can "cd" bool _can_pipe; // True if accepts "|" after it bool _default_nomore_mode; // True if "no-more" (i.e., unpaged) mode is default bool _is_command_argument; // True if this is a command argument bool _is_argument_expected; // True if an argument is expected CliCommand *_cli_command_pipe; // The "|" pipe command TypeMatchCb _type_match_cb; // The type match callback }; class CliCommandMatch { public: CliCommandMatch(const string& command_name, const string& help_string, bool is_executable, bool can_pipe) : _command_name(command_name), _help_string(help_string), _is_executable(is_executable), _can_pipe(can_pipe), _default_nomore_mode(false), _is_command_argument(false), _is_argument_expected(false) {} #ifdef XORP_USE_USTL CliCommandMatch() { } #endif /** * Get the command name. * * @return the command name. */ const string& command_name() const { return (_command_name); } /** * Get the help string for the command. * * @return the help string for the command. */ const string& help_string() const { return (_help_string); } /** * Test if the command is executable. * * @return true if the command is executable, otherwise false. */ bool is_executable() const { return (_is_executable); } /** * Test if the command supports pipes. * * @return true if the command supports pipes, otherwise false. */ bool can_pipe() const { return (_can_pipe); } /** * Test if "no-more" (i.e., unpaged) is the default output mode. * * @return true if "no-more" (i.e., unpaged) is the default output mode, * otherwise false. */ bool default_nomore_mode() const { return (_default_nomore_mode); } /** * Set the default paging mode. * * @param v if true, then "no-more" (i.e., unpaged) is the default * output mode. */ void set_default_nomore_mode(bool v) { _default_nomore_mode = v; } /** * Test if the command actually represents a command argument. * * @return true if the command actually represents a command argument. */ bool is_command_argument() const { return (_is_command_argument); } /** * Set a flag whether the command actually represents a command argument. * * @param v true if the command represents a command argument, otherwise * false. */ void set_is_command_argument(bool v) { _is_command_argument = v; } /** * Test if the command expects an argument. * * @return true if the command expects an argument, otherwise false. */ bool is_argument_expected() const { return (_is_argument_expected); } /** * Set a flag whether the command expects an argument. * * @param v true if the command expectes an argument, otherwise false. */ void set_is_argument_expected(bool v) { _is_argument_expected = v; } /** * Get a reference to the type matching callback. * * @return a reference to the type matching callback. */ const CliCommand::TypeMatchCb& type_match_cb() const { return (_type_match_cb); } /** * Set the type matching callback. * * @param cb the type matching callback. */ void set_type_match_cb(const CliCommand::TypeMatchCb& cb) { _type_match_cb = cb; } private: string _command_name; // The command name string _help_string; // The help string for the command bool _is_executable; // True if the command is executable bool _can_pipe; // True if the command supports pipes bool _default_nomore_mode; // True if "no-more" (i.e., unpaged) mode is default bool _is_command_argument; // True if this is a command argument bool _is_argument_expected; // True if an argument is expected CliCommand::TypeMatchCb _type_match_cb; // The type matching callback }; // // Global variables // // // Global functions prototypes // #endif // __CLI_CLI_COMMAND_HH__ xorp/cli/SConscript0000664000076400007640000000451311631506007014443 0ustar greearbgreearb # Copyright (c) 2009-2011 XORP, Inc and Others # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # $XORP$ import os Import('env') subdirs = [ 'tests', 'tools', ] if not env['disable_libtecla']: subdirs.append('libtecla') SConscript(dirs = subdirs, exports = 'env') env = env.Clone() is_shared = env.has_key('SHAREDLIBS') env.AppendUnique(CPPPATH = [ '#', '$BUILDDIR', ]) env.PrependUnique(LIBPATH = [ '$BUILDDIR/xrl/interfaces', '$BUILDDIR/xrl/targets', '$BUILDDIR/libxipc', '$BUILDDIR/libproto', '$BUILDDIR/libcomm', '$BUILDDIR/libxorp', '$BUILDDIR/cli/libtecla', ]) if not env['disable_libtecla']: env.AppendUnique(LIBPATH = [ '$BUILDDIR/cli/libtecla', ]) # Internal libraries. env.AppendUnique(LIBS = [ 'xif_cli_processor', 'xst_cli', 'xorp_proto', 'xorp_ipc', 'xorp_comm', 'xorp_core', ]) if (env.has_key('mingw') and env['mingw']): env.AppendUnique(LIBS = [ 'winmm' ]) if not env['disable_libtecla']: env.AppendUnique(LIBS = [ 'xorp_tecla', ]) env.Replace(RPATH = [ env.Literal(env['xorp_tool_rpath']) ]) libxorp_cli_srcs = [ 'cli_client.cc', 'cli_command.cc', 'cli_command_pipe.cc', 'cli_node.cc', 'cli_node_internal_commands.cc', 'cli_node_net.cc', 'xrl_cli_node.cc' ] if is_shared: libxorp_cli = env.SharedLibrary(target = 'libxorp_cli', source = libxorp_cli_srcs) env.Alias('install', env.InstallLibrary(env['xorp_libdir'], libxorp_cli)) else: libxorp_cli = env.StaticLibrary(target = 'libxorp_cli', source = libxorp_cli_srcs) Default(libxorp_cli) xorp/cli/README0000664000076400007640000000160011421137511013300 0ustar greearbgreearb# # $XORP: xorp/cli/README,v 1.14 2007/03/14 02:10:38 pavlin Exp $ # Command-Line Interface Implementation ============================================ This directory contains the XORP implementation of the CLI library. Startup ======= The CLI library is used as part of the "xorpsh" process (inside ${XORP}/rtrmgr/). Documentation ============= Please refer to ${XORP}/rtrmgr/ about information how to use the CLI as part of the xorpsh. Currently, the only available development documentation is the source code itself. In the future documentation will be added to describe the CLI architecture and API. Testing ======= Currently, the CLI testing is performed manually. See file ``TODO'' for a list of known problems. Status ====== Currently (July 2008), the CLI implementation is reasonably stable, but more tests are needed and there are still several minor issues that need fixing. xorp/cli/tests/0000775000076400007640000000000011421137511013565 5ustar greearbgreearbxorp/cli/tests/SConscript0000664000076400007640000000307211421137511015601 0ustar greearbgreearb# Copyright (c) 2009 XORP, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # # $XORP$ import os Import("env") env = env.Clone() env.AppendUnique(CPPPATH = [ '#', '$BUILDDIR', '$BUILDDIR/cli', '$BUILDDIR/cli/libtecla', ]) env.AppendUnique(LIBPATH = [ '$BUILDDIR/cli', '$BUILDDIR/cli/libtecla', '$BUILDDIR/xrl/interfaces', '$BUILDDIR/xrl/targets', '$BUILDDIR/libxipc', '$BUILDDIR/libproto', '$BUILDDIR/libxorp', '$BUILDDIR/libcomm', ]) env.AppendUnique(LIBS = [ 'xorp_cli', 'xif_cli_processor', 'xst_cli', 'xorp_finder', 'xorp_tecla', 'xorp_ipc', 'xorp_proto', 'xorp_core', 'xorp_comm', ]) simple_cpp_tests = [ #'cli', # Not fully automated unit test; stubbed out. ] cpp_test_targets = [] for ct in simple_cpp_tests: cpp_test_targets.append(env.AutoTest(target = 'test_%s' % ct, source = 'test_%s.cc' % ct)) xorp/cli/tests/test_cli.cc0000664000076400007640000004120211421137511015701 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // CLI (Command-Line Interface) test program. // #include "cli_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/eventloop.hh" #include "libxorp/exceptions.hh" #include "libxorp/token.hh" #include "libxorp/xlog.h" #include "libxipc/finder_server.hh" #include "cli_client.hh" #include "xrl_cli_node.hh" #ifdef HAVE_NETDB_H #include #endif #ifdef HAVE_GETOPT_H #include #endif // // Local variables // CliNode *global_cli_node = NULL; // // Local functions prototypes // static bool wakeup_hook(); static bool wakeup_hook2(int, int); static CliNode& cli_node(); static void usage(const char *argv0, int exit_value); /** * usage: * @argv0: Argument 0 when the program was called (the program name itself). * @exit_value: The exit value of the program. * * Print the program usage. * If @exit_value is 0, the usage will be printed to the standart output, * otherwise to the standart error. **/ static void usage(const char *argv0, int exit_value) { FILE *output; const char *progname = strrchr(argv0, '/'); if (progname != NULL) progname++; // Skip the last '/' if (progname == NULL) progname = argv0; // // If the usage is printed because of error, output to stderr, otherwise // output to stdout. // if (exit_value == 0) output = stdout; else output = stderr; fprintf(output, "Usage: %s [-F [:]]\n", progname); fprintf(output, " -F [:] : finder hostname and port\n"); fprintf(output, " -h : usage (this message)\n"); fprintf(output, "\n"); fprintf(output, "Program name: %s\n", progname); fprintf(output, "Module name: %s\n", XORP_MODULE_NAME); fprintf(output, "Module version: %s\n", XORP_MODULE_VERSION); exit (exit_value); // NOTREACHED } class Foo { public: Foo() {} bool print() { printf("Hello %p\n", this); return (1); } }; int add_my_cli_commands(CliNode& cli_node); static CliNode& cli_node() { return (*global_cli_node); } static void cli_main(const string& finder_hostname, uint16_t finder_port, bool start_finder) { string error_msg; EventLoop eventloop; // // Init stuff // // // Start our own finder // FinderServer *finder = NULL; if (start_finder) { try { finder = new FinderServer(eventloop, IPv4(finder_hostname.c_str()), finder_port); } catch (const InvalidPort&) { XLOG_FATAL("Could not start in-process Finder"); } } // // The main body // Foo f; // // CLI // // XXX: we use a single CLI node to handle both IPv4 and IPv6 CliNode cli_node(AF_INET, XORP_MODULE_CLI, eventloop); cli_node.set_cli_port(12000); // // CLI access // IPvXNet enable_ipvxnet1("127.0.0.1/32"); // IPvXNet enable_ipvxnet2("192.150.187.0/25"); IPvXNet disable_ipvxnet1("0.0.0.0/0"); // Disable everything else // cli_node.add_enable_cli_access_from_subnet(enable_ipvxnet1); // cli_node.add_enable_cli_access_from_subnet(enable_ipvxnet2); cli_node.add_disable_cli_access_from_subnet(disable_ipvxnet1); // // Create and configure the CLI XRL interface // XrlCliNode xrl_cli_node(eventloop, cli_node.module_name(), finder_hostname, finder_port, "finder", cli_node); wait_until_xrl_router_is_ready(eventloop, xrl_cli_node.xrl_router()); #if 0 #ifdef HAVE_IPV6 CliNode cli_node6(AF_INET6, XORP_MODULE_CLI, eventloop); cli_node6.set_cli_port(12000); XrlCliNode xrl_cli_node(eventloop, cli_node6.module_name(), finder_hostname, finder_port, "finder", cli_node6); wait_until_xrl_router_is_ready(eventloop, xrl_cli_node.xrl_router()); #endif // HAVE_IPV6 #endif // 0 // // XXX: CLI test-specific setup // global_cli_node = &cli_node; add_my_cli_commands(cli_node); // add_my_cli_commands(cli_node6); // // Start the nodes // cli_node.enable(); cli_node.start(); // cli_node6.enable(); // cli_node6.start(); // Test timer XorpTimer wakeywakey = eventloop.new_periodic_ms( 1000, callback(wakeup_hook)); XorpTimer wakeywakey2 = eventloop.new_periodic_ms( 5000, callback(wakeup_hook2, 3, 5)); XorpTimer wakeywakey3 = eventloop.new_periodic_ms( 2000, callback(f, &Foo::print)); // // Main loop // for (;;) { eventloop.run(); } } static bool wakeup_hook() { fprintf(stdout, "%s\n", xlog_localtime2string()); fflush(stdout); return (true); } static bool wakeup_hook2(int a, int b) { fprintf(stdout, "%s ARGS = %d %d\n", xlog_localtime2string(), a, b); fflush(stdout); return (true); } int cli_myset_func(const string& , // server_name const string& cli_term_name, uint32_t , // cli_session_id const vector& command_global_name, const vector& argv) { CliClient *cli_client = cli_node().find_cli_by_term_name(cli_term_name); if (cli_client == NULL) return (XORP_ERROR); string command_line = token_vector2line(command_global_name); if (command_global_name.size() > 0) cli_client->cli_print(c_format("MYSET_FUNC command_global_name = %s\n", command_line.c_str())); for (size_t i = 0; i < argv.size(); i++) cli_client->cli_print(c_format("MYSET_FUNC arg = %s\n", argv[i].c_str())); return (XORP_OK); } int cli_print(const string& , // server_name const string& cli_term_name, uint32_t , // cli_session_id const vector& , // command_global_name, const vector& argv) { CliClient *cli_client = cli_node().find_cli_by_term_name(cli_term_name); if (cli_client == NULL) return (XORP_ERROR); if (argv.size() > 0) { cli_client->cli_print("Error: unexpected arguments\n"); return (XORP_ERROR); } // // Test to print a number of lines at once // string my_string = ""; for (int i = 0; i < 100; i++) my_string += c_format("This is my multi-line number %u\n", i); cli_client->cli_print(my_string); // // Test to print a number of lines one-by-one // for (int i = 0; i < 100; i++) cli_client->cli_print(c_format("This is my line number %u\n", i)); return (XORP_OK); } int cli_print2(const string& , // server_name const string& cli_term_name, uint32_t , // cli_session_id const vector& , // command_global_name, const vector& argv) { CliClient *cli_client = cli_node().find_cli_by_term_name(cli_term_name); if (cli_client == NULL) return (XORP_ERROR); if (argv.size() > 0) { cli_client->cli_print("Error: unexpected arguments\n"); return (XORP_ERROR); } for (int i = 0; i < 10; i++) cli_client->cli_print(c_format("This is my line number %d\n", i)); return (XORP_OK); } int cli_print2_newline(const string& , // server_name const string& cli_term_name, uint32_t , // cli_session_id const vector& , // command_global_name, const vector& argv) { CliClient *cli_client = cli_node().find_cli_by_term_name(cli_term_name); if (cli_client == NULL) return (XORP_ERROR); if (argv.size() > 0) { cli_client->cli_print("Error: unexpected arguments\n"); return (XORP_ERROR); } for (int i = 0; i < 10; i++) cli_client->cli_print(c_format("This is my line number %d\n", i)); cli_client->cli_print(c_format("\n")); return (XORP_OK); } int cli_print_wide(const string& , // server_name const string& cli_term_name, uint32_t , // cli_session_id const vector& , // command_global_name, const vector& argv) { CliClient *cli_client = cli_node().find_cli_by_term_name(cli_term_name); if (cli_client == NULL) return (XORP_ERROR); if (argv.size() > 0) { cli_client->cli_print("Error: unexpected arguments\n"); return (XORP_ERROR); } string trail = "111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999"; #if 1 for (int i = 0; i < 200; i++) cli_client->cli_print(c_format("This is my line number %d %s %d\n", i, trail.c_str(), i)); #endif return (XORP_OK); } int cli_print_test(const string& , // server_name const string& cli_term_name, uint32_t , // cli_session_id const vector& , // command_global_name, const vector& argv) { CliClient *cli_client = cli_node().find_cli_by_term_name(cli_term_name); if (cli_client == NULL) return (XORP_ERROR); if (argv.size() > 0) { cli_client->cli_print("Error: unexpected arguments\n"); return (XORP_ERROR); } string s = "\ Group Source RP Flags\n\ ff0e::8320:1 :: ffff:fff:ffff:300:1:: WC \n\ Upstream interface (RP): gif0\n\ Upstream MRIB next hop (RP): fe80::ffff:19\n\ Upstream RPF'(*,G): fe80::ffff:19\n\ Upstream state: Joined \n\ Join timer: 31\n\ Local receiver include WC: .........O..\n\ Joins RP: ............\n\ Joins WC: ............\n\ Join state: ............\n\ Prune state: ............\n\ Prune pending state: ............\n\ I am assert winner state: .........O..\n\ I am assert loser state: ............\n\ Assert winner WC: .........O..\n\ Assert lost WC: ............\n\ Assert tracking WC: .....O...O..\n\ Could assert WC: .........O..\n\ I am DR: .....O...O..\n\ Immediate olist RP: ............\n\ Immediate olist WC: .........O..\n\ Inherited olist SG: .........O..\n\ Inherited olist SG_RPT: .........O..\n\ PIM include WC: .........O..\n\ ff0e::8320:1 ffff:700:0:fff2::20 ffff:fff:ffff:300:1:: SG_RPT DirectlyConnectedS \n\ Upstream interface (S): rl0\n\ Upstream interface (RP): gif0\n\ Upstream MRIB next hop (RP): fe80::ffff:19\n\ Upstream RPF'(S,G,rpt): fe80::ffff:19\n\ Upstream state: Pruned \n\ Override timer: -1\n\ Local receiver include WC: .........O..\n\ Joins RP: ............\n\ Joins WC: ............\n\ Prunes SG_RPT: ............\n\ Join state: ............\n\ Prune state: ............\n\ Prune pending state: ............\n\ Prune tmp state: ............\n\ Prune pending tmp state: ............\n\ Assert winner WC: .........O..\n\ Assert lost WC: ............\n\ Assert lost SG_RPT: ............\n\ Could assert WC: .........O..\n\ Could assert SG: ...........O\n\ I am DR: .....O...O..\n\ Immediate olist RP: ............\n\ Immediate olist WC: .........O..\n\ Inherited olist SG: .........O..\n\ Inherited olist SG_RPT: .........O..\n\ PIM include WC: .........O..\n\ ff0e::8320:1 ffff:700:0:fff2::20 ffff:fff:ffff:300:1:: SG SPT DirectlyConnectedS \n\ Upstream interface (S): rl0\n\ Upstream interface (RP): gif0\n\ Upstream MRIB next hop (RP): fe80::ffff:19\n\ Upstream MRIB next hop (S): UNKNOWN\n\ Upstream RPF'(S,G): UNKNOWN\n\ Upstream state: Joined \n\ Register state: RegisterJoin RegisterCouldRegister \n\ Join timer: 39\n\ Local receiver include WC: .........O..\n\ Local receiver include SG: ............\n\ Local receiver exclude SG: ............\n\ Joins RP: ............\n\ Joins WC: ............\n\ Joins SG: ...........O\n\ Join state: ...........O\n\ Prune state: ............\n\ Prune pending state: ............\n\ I am assert winner state: ............\n\ I am assert loser state: ............\n\ Assert winner WC: .........O..\n\ Assert winner SG: ............\n\ Assert lost WC: ............\n\ Assert lost SG: ............\n\ Assert lost SG_RPT: ............\n\ Assert tracking SG: .........O.O\n\ Could assert WC: .........O..\n\ Could assert SG: ...........O\n\ I am DR: .....O...O..\n\ Immediate olist RP: ............\n\ Immediate olist WC: .........O..\n\ Immediate olist SG: ...........O\n\ Inherited olist SG: .........O.O\n\ Inherited olist SG_RPT: .........O..\n\ PIM include WC: .........O..\n\ PIM include SG: ............\n\ PIM exclude SG: ............\n\ "; cli_client->cli_print(s); return (XORP_OK); } int add_my_cli_commands(CliNode& cli_node) { CliCommand *com0, *com1, *com2, *com3; string error_msg; com0 = cli_node.cli_command_root(); com2 = com0->add_command("myset", "Set my variable", true, cli_myset_func, error_msg); com1 = com0->add_command("myshow", "Show my information", "Myshow> ", true, error_msg); com2 = com0->add_command("myshow version", "Show my information about system", true, error_msg); com3 = com0->add_command("myshow version pim", "Show my information about PIM", true, error_msg); com3 = com0->add_command("myshow version igmp", "Show my information about IGMP", true, error_msg); com1 = com0->add_command("myset2", "Set my variable2", true, cli_myset_func, error_msg); com1 = com0->add_command("myshow2", "Show my information2", "Myshow2> ", true, error_msg); com1 = com0->add_command("print", "Print numbers", true, cli_print, error_msg); com1 = com0->add_command("print2", "Print few numbers", true, cli_print2, error_msg); com1 = com0->add_command("print2 newline", "Print few numbers with an extra newline", true, cli_print2_newline, error_msg); com1 = com0->add_command("print_wide", "Print wide lines", true, cli_print_wide, error_msg); com1 = com0->add_command("print_test", "Print test lines", true, cli_print_test, error_msg); return (XORP_OK); } int main(int argc, char *argv[]) { int ch; string::size_type idx; const char *argv0 = argv[0]; string finder_hostname = FinderConstants::FINDER_DEFAULT_HOST().str(); uint16_t finder_port = FinderConstants::FINDER_DEFAULT_PORT(); // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); // // Get the program options // while ((ch = getopt(argc, argv, "F:h")) != -1) { switch (ch) { case 'F': // Finder hostname and port idx = finder_hostname.find(':'); if (idx != string::npos) { if (idx + 1 >= finder_hostname.length()) { // No port number usage(argv0, 1); // NOTREACHED } char* p = &finder_hostname[idx + 1]; finder_port = static_cast(atoi(p)); finder_hostname = finder_hostname.substr(0, idx); } break; case 'h': case '?': // Help usage(argv0, 0); break; default: usage(argv0, 1); break; } } argc -= optind; argv += optind; if (argc != 0) { usage(argv0, 1); // NOTREACHED } // // Run everything // try { cli_main(finder_hostname, finder_port, true); } catch(...) { xorp_catch_standard_exceptions(); } // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); exit (0); } xorp/cli/tools/0000775000076400007640000000000011631504654013574 5ustar greearbgreearbxorp/cli/tools/send_cli_processor_xrl.cc0000664000076400007640000001647111421137511020647 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "pim/pim_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/callback.hh" #include "libxorp/eventloop.hh" #include "libxorp/exceptions.hh" #include "libxipc/xrl_std_router.hh" #include "xrl/interfaces/cli_processor_xif.hh" #ifdef HAVE_GETOPT_H #include #endif // // A program for sending a CLI request to a module that has built-in CLI // support. // static const char* STD_ROUTER_NAME = "send_cli_processor_xrl"; // // Local functions prototypes // static void usage(const char* argv0, int exit_value); static void recv_process_command_output(const XrlError& xrl_error, const string* processor_name, const string* cli_term_name, const uint32_t* cli_session_id, const string* command_output, bool* done_flag, bool* success_flag); /** * usage: * @argv0: Argument 0 when the program was called (the program name itself). * @exit_value: The exit value of the program. * * Print the program usage. * If @exit_value is 0, the usage will be printed to the standart output, * otherwise to the standart error. **/ static void usage(const char* argv0, int exit_value) { FILE *output; const char* progname = strrchr(argv0, '/'); if (progname != NULL) progname++; // Skip the last '/' if (progname == NULL) progname = argv0; // // If the usage is printed because of error, output to stderr, otherwise // output to stdout. // if (exit_value == 0) output = stdout; else output = stderr; fprintf(output, "Usage: %s [-F [:]] -t \n", progname); fprintf(output, " -F [:] : finder hostname and port\n"); fprintf(output, " -t : target name\n"); fprintf(output, " -h : usage (this message)\n"); fprintf(output, " : CLI command\n"); fprintf(output, "\n"); fprintf(output, "Program name: %s\n", progname); fprintf(output, "Module name: %s\n", XORP_MODULE_NAME); fprintf(output, "Module version: %s\n", XORP_MODULE_VERSION); exit (exit_value); // NOTREACHED } // // Send a request to a CLI processor // static void send_process_command(XrlRouter* xrl_router, const string& target, const string& processor_name, const string& cli_term_name, uint32_t cli_session_id, const string& command_name, const string& command_args, bool* done_flag, bool* success_flag) { XrlCliProcessorV0p1Client _xrl_cli_processor_client(xrl_router); // // Send the request // _xrl_cli_processor_client.send_process_command( target.c_str(), processor_name, cli_term_name, cli_session_id, command_name, command_args, callback(&recv_process_command_output, done_flag, success_flag)); } // // Process the response of a command processed by a remote CLI processor // static void recv_process_command_output(const XrlError& xrl_error, const string* processor_name, const string* cli_term_name, const uint32_t* cli_session_id, const string* command_output, bool* done_flag, bool* success_flag) { *done_flag = true; if (xrl_error != XrlError::OKAY()) { *success_flag = false; fprintf(stderr, "Error: %s\n", xrl_error.str().c_str()); return; } *success_flag = true; fprintf(stdout, "%s", command_output->c_str()); UNUSED(processor_name); UNUSED(cli_term_name); UNUSED(cli_session_id); } static int send_cli_processor_xrl_main(const string& finder_hostname, uint16_t finder_port, const string& target, const string& command_name, const string& command_args) { bool done_flag = false; bool success_flag = false; EventLoop eventloop; XrlStdRouter xrl_std_router(eventloop, STD_ROUTER_NAME, finder_hostname.c_str(), finder_port); xrl_std_router.finalize(); wait_until_xrl_router_is_ready(eventloop, xrl_std_router); send_process_command(&xrl_std_router, target, string(""), // processor_name, string(""), // cli_term_name, 0, // cli_session_id, command_name, command_args, &done_flag, &success_flag); while (xrl_std_router.pending() || !done_flag) { eventloop.run(); } if (success_flag) return (XORP_OK); else return (XORP_ERROR); } int main(int argc, char* const argv[]) { int ch; string::size_type idx; const char* argv0 = argv[0]; string finder_hostname = FinderConstants::FINDER_DEFAULT_HOST().str(); uint16_t finder_port = FinderConstants::FINDER_DEFAULT_PORT(); string arguments, command, target; int ret_value; // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); // // Get the program options // while ((ch = getopt(argc, argv, "F:t:h")) != -1) { switch (ch) { case 'F': // Finder hostname and port finder_hostname = optarg; idx = finder_hostname.find(':'); if (idx != string::npos) { if (idx + 1 >= finder_hostname.length()) { // No port number usage(argv0, 1); // NOTREACHED } char* p = &finder_hostname[idx + 1]; finder_port = static_cast(atoi(p)); finder_hostname = finder_hostname.substr(0, idx); } break; case 't': target = string(optarg); break; case 'h': case '?': // Help usage(argv0, 0); // NOTREACHED break; default: usage(argv0, 1); // NOTREACHED break; } } argc -= optind; argv += optind; // Get the command itself if (argc == 0) { usage(argv0, 1); // NOTREACHED } while (argc != 0) { if (! command.empty()) command += " "; command += string(argv[0]); argc--; argv++; } if (command.empty() || target.empty()) { usage(argv0, 1); // NOTREACHED } try { ret_value = send_cli_processor_xrl_main(finder_hostname, finder_port, target, command, arguments); } catch(...) { xorp_catch_standard_exceptions(); ret_value = XORP_ERROR; } // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); if (ret_value != XORP_OK) exit(1); exit (0); } xorp/cli/tools/SConscript0000664000076400007640000000412611631504654015611 0ustar greearbgreearb# Copyright (c) 2009-2011 XORP, Inc and Others # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # $XORP$ import os Import('env') env = env.Clone() is_shared = env.has_key('SHAREDLIBS') env.AppendUnique(CPPPATH = [ '#', '$BUILDDIR', ]) env.AppendUnique(LIBPATH = [ '$BUILDDIR/libxorp', '$BUILDDIR/libcomm', '$BUILDDIR/libxipc', '$BUILDDIR/libproto', '$BUILDDIR/xrl/interfaces', '$BUILDDIR/xrl/targets', '.', ]) env.AppendUnique(LIBS = [ 'xif_cli_processor', 'xorp_ipc', 'xorp_core', 'xorp_proto', 'xorp_comm', ]) if (env.has_key('mingw') and env['mingw']): env.AppendUnique(LIBS = [ 'winmm' ]) if not is_shared: env.AppendUnique(LIBS = [ "crypto", ]) if (env.has_key('mingw') and env['mingw']): env.AppendUnique(LIBS = [ 'ws2_32', 'iphlpapi', 'winmm', ]) env.Append(LIBS = [ 'xorp_core', ]) env.Replace(RPATH = [ env.Literal(env['xorp_tool_rpath']) ]) cligensrcs = [ 'cli_generic.cc' ] sendclisrcs = [ 'send_cli_processor_xrl.cc' ] cligen = env.Program(target = 'cli_generic', source = cligensrcs) sendcli = env.Program(target = 'cli_send_processor_xrl', source = sendclisrcs) env.Alias('install', env.InstallProgram(env['xorp_tooldir'], cligen)) env.Alias('install', env.InstallProgram(env['xorp_tooldir'], sendcli)) Default(cligen, sendcli) xorp/cli/tools/cli_generic.cc0000664000076400007640000002551711540224220016343 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2008-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "libxorp/xorp.h" #include "cli/cli_module.h" #include "libxorp/xlog.h" #include "libxorp/exceptions.hh" #include "libxipc/xrl_std_router.hh" #ifdef HAVE_GETOPT_H #include #endif namespace { class CGException : public XorpReasonedException { public: CGException(const char* file, size_t line, const string& why = "") : XorpReasonedException("CGException", file, line, why) {} }; class CliGeneric { public: CliGeneric(XrlRouter& rtr); string str() const; void set_xrl(const string& xrl); void set_fmt(const string& fmt); void add_arg(const string& arg); void add_iterator(const string& iter); void set_separator(const string& sep); bool running(); void own(); string unescape(const string& in); string replace(const string& in, char old, char n); private: typedef map ITERATORS; // varname -> XRL typedef set VARS; typedef map SYMBOLS; // varname -> value void send_xrl(const string& xrl); void xrl_cb(const XrlError& error, XrlArgs* args); void print(const XrlArgs& args); void iterate(const VARS& vars); void wait_xrl(); bool iterator_resolved(const string& var); void grab_keys(XrlAtomList& keys, const string& var); void do_grab_keys(const XrlArgs& args); string set_variables(const string& xrl); string atom_val(const XrlAtom& atom); void get_vars(VARS& vars, const string& in); bool _first; string _separator; string* _last; string _xrl; string _fmt; XrlRouter& _rtr; bool _running; ITERATORS _iterators; bool _xrl_pending; SYMBOLS _symbols; XrlAtomList* _keys; // XXX }; CliGeneric::CliGeneric(XrlRouter& rtr) : _first(true), _last(NULL), _rtr(rtr), _running(false), _xrl_pending(false), _keys(NULL) { } bool CliGeneric::running() { return _running || _xrl_pending; } void CliGeneric::set_separator(const string& sep) { _separator = sep; } void CliGeneric::add_iterator(const string& iter) { string::size_type i = iter.find('='); if (i == string::npos) xorp_throw(CGException, "bad iterator spec"); string var = iter.substr(0, i); string xrl = iter.substr(i + 1); if (_iterators.find(var) != _iterators.end()) xorp_throw(CGException, "iterator already present"); string* x = new string(xrl); _iterators[var] = x; _last = x; } void CliGeneric::set_xrl(const string& xrl) { _xrl = xrl; _last = &_xrl; } void CliGeneric::set_fmt(const string& fmt) { _fmt = unescape(fmt); _fmt = replace(_fmt, '_', ' '); _last = &_fmt; } void CliGeneric::wait_xrl() { while (_xrl_pending) _rtr.eventloop().run(); } string CliGeneric::replace(const string& in, char old, char n) { ostringstream oss; for (string::const_iterator i = in.begin(); i != in.end(); ++i) { char x = *i; if (x == old) oss << n; else oss << x; } return oss.str(); } string CliGeneric::unescape(const string& in) { ostringstream oss; string x = in; while (!x.empty()) { string::size_type i = x.find('\\'); if (i == string::npos) { oss << x; break; } oss << x.substr(0, i); switch (x.at(i + 1)) { case '\\': oss << "\\"; break; case 't': oss << "\t"; break; case 'n': oss << "\n"; break; default: xorp_throw(CGException, "Unknown escape sequence"); } x = x.substr(i + 2); } return oss.str(); } void CliGeneric::add_arg(const string& arg) { if (!_last) xorp_throw(CGException, "Specify XRL first"); string::size_type i = _last->find('$'); if (i == string::npos) xorp_throw(CGException, "XRL has no unresolved parameters"); ostringstream result; result << _last->substr(0, i); result << arg; result << _last->substr(i + 2); // XXX assume one digit only _last->assign(result.str()); } string CliGeneric::str() const { ostringstream oss; oss << "XRL [" << _xrl << "]" << endl << "FMT [" << _fmt << "]" << endl ; return oss.str(); } void CliGeneric::own() { _running = true; VARS v; get_vars(v, _xrl); // XXX I gotta think too much if I gotta return to the caller so lets do it // the old school way iterate(v); _running = false; } void CliGeneric::iterate(const VARS& vars) { VARS v = vars; // all variables resolved - pwn it. if (v.empty()) { send_xrl(_xrl); return; } // choose an iterator. bool found = false; string var; for (VARS::iterator i = v.begin(); i != v.end(); ++i) { var = *i; // see if we can use this iterator if (iterator_resolved(var)) { v.erase(i); found = true; break; } } if (!found) xorp_throw(CGException, "Cannot find an iterator"); // grab keys XrlAtomList keys; grab_keys(keys, var); // iterate through keys for (unsigned i = 0; i < keys.size(); ++i) { const XrlAtom& atom = keys.get(i); _symbols[var] = &atom; iterate(v); } _symbols.erase(var); } void CliGeneric::grab_keys(XrlAtomList& keys, const string& var) { ITERATORS::iterator i = _iterators.find(var); XLOG_ASSERT(i != _iterators.end()); XLOG_ASSERT(!_keys); _keys = &keys; send_xrl(*(i->second)); _keys = NULL; } bool CliGeneric::iterator_resolved(const string& var) { ITERATORS::iterator i = _iterators.find(var); if (i == _iterators.end()) xorp_throw(CGException, c_format("cannot find iterator for var %s", var.c_str())); VARS v; get_vars(v, *(i->second)); // all vars must be resolved for (VARS::iterator j = v.begin(); j != v.end(); ++j) { const string& var = *j; if (_symbols.find(var) == _symbols.end()) return false; } return true; } void CliGeneric::get_vars(VARS& vars, const string& in) { string::size_type i = 0; while ((i = in.find('%', i)) != string::npos) { i++; char x = in.at(i); string var = string("%") + x; vars.insert(var); } } string CliGeneric::set_variables(const string& xrl) { ostringstream oss; string x = xrl; while (!x.empty()) { string::size_type i = x.find('%'); if (i == string::npos) { oss << x; break; } oss << x.substr(0, i); string var = x.substr(i, 2); if (xorp_isdigit(var.at(1))) oss << var; else { SYMBOLS::iterator i = _symbols.find(var); XLOG_ASSERT(i != _symbols.end()); const XrlAtom* atom = i->second; oss << atom_val(*atom); } x = x.substr(i + 2); } return oss.str(); } void CliGeneric::send_xrl(const string& xrl) { string xrl_fixed = set_variables(xrl); Xrl x(xrl_fixed.c_str()); XLOG_ASSERT(!_xrl_pending); if (!_rtr.send(x, callback(this, &CliGeneric::xrl_cb))) xorp_throw(CGException, "can't send XRL"); _xrl_pending = true; wait_xrl(); } void CliGeneric::xrl_cb(const XrlError& error, XrlArgs* args) { if (error != XrlError::OKAY()) XLOG_FATAL("XRL error: %s", error.str().c_str()); _xrl_pending = false; if (!_keys) print(*args); else { do_grab_keys(*args); } } void CliGeneric::do_grab_keys(const XrlArgs& args) { if (args.size() != 1) xorp_throw(CGException, "bad size for keys XRL"); const XrlAtom& atom = args.item(0); if (atom.type() != xrlatom_list) xorp_throw(CGException, "not a list"); const XrlAtomList& list = atom.list(); XLOG_ASSERT(_keys); *_keys = list; } void CliGeneric::print(const XrlArgs& args) { string fmt = set_variables(_fmt); if (!_first && !_separator.empty()) cout << _separator; while (!fmt.empty()) { string::size_type i = fmt.find('%'); if (i == string::npos) { cout << fmt; break; } // print everything upto the argument string bit = fmt.substr(0, i); cout << bit; int numlen = 1; // XXX string num = fmt.substr(i + 1, numlen); unsigned idx = (unsigned) atoi(num.c_str()); fmt = fmt.substr(i + 1 + numlen); // print the argument if (idx >= args.size()) xorp_throw(CGException, "invalid index"); const XrlAtom& a = args.item(idx); cout << atom_val(a); } _first = false; } string CliGeneric::atom_val(const XrlAtom& a) { string s = a.str().c_str(); string::size_type i = s.find('='); if (i == string::npos) xorp_throw(CGException, "bad result"); s = s.substr(i + 1); return s; } void usage(const char* progname) { cout << "Usage: " << progname << " " << endl << "-h\t\thelp" << endl << "-x\t\t" << endl << "-a\t\t" << endl << "-f\t\t" << endl << "-i\t\t" << endl << "-s\t\t" << endl ; exit(1); } void own(int argc, char* argv[]) { EventLoop e; XrlStdRouter router(e, "cli_generic", FinderConstants::FINDER_DEFAULT_HOST().str().c_str()); CliGeneric cg(router); int c; while ((c = getopt(argc, argv, "x:hf:a:i:s:")) != -1) { switch (c) { case 'i': cg.add_iterator(optarg); break; case 'x': cg.set_xrl(optarg); break; case 'f': cg.set_fmt(optarg); break; case 'a': cg.add_arg(optarg); break; case 's': cg.set_separator(optarg); break; default: case 'h': usage(argv[0]); break; } } router.finalize(); while (false == router.failed() && false == router.ready()) e.run(); if (true == router.failed()) xorp_throw(CGException, "Router failed to communicate with finder."); if (false == router.ready()) xorp_throw(CGException, "Connected to finder, but did not become ready."); cg.own(); while (cg.running()) e.run(); } } // anon namespace int main(int argc, char* argv[]) { xlog_init(argv[0], 0); xlog_set_verbose(XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); try { own(argc, argv); } catch (const CGException& e) { UNUSED(e); XLOG_FATAL("CGException: %s", e.str().c_str()); } xlog_stop(); xlog_exit(); exit(0); } xorp/cli/xrl_cli_node.hh0000664000076400007640000001655611540225521015423 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/cli/xrl_cli_node.hh,v 1.25 2008/10/02 21:56:30 bms Exp $ #ifndef __CLI_XRL_CLI_NODE_HH__ #define __CLI_XRL_CLI_NODE_HH__ #include "libxorp/xlog.h" #include "libxipc/xrl_std_router.hh" #include "xrl/targets/cli_base.hh" #include "xrl/interfaces/cli_processor_xif.hh" #include "cli_node.hh" // // TODO: XrlCliProcessorV1p0Client should NOT be a base class. Temp. solution.. // class XrlCliNode : public XrlStdRouter, public XrlCliTargetBase { public: XrlCliNode(EventLoop& eventloop, const string& class_name, const string& finder_hostname, uint16_t finder_port, const string& finder_target, CliNode& cli_node); virtual ~XrlCliNode() {} // // XrlCliNode front-end interface // int enable_cli(); int disable_cli(); int start_cli(); int stop_cli(); /** * Get a reference to the XrlRouter instance. * * @return a reference to the XrlRouter (@ref XrlRouter) instance. */ XrlRouter& xrl_router() { return *this; } /** * Get a const reference to the XrlRouter instance. * * @return a const reference to the XrlRouter (@ref XrlRouter) instance. */ const XrlRouter& xrl_router() const { return *this; } protected: // // Methods to be implemented by derived classes supporting this interface. // /** * Get name of Xrl Target */ virtual XrlCmdError common_0_1_get_target_name( // Output values, string& name); /** * Get version string from Xrl Target */ virtual XrlCmdError common_0_1_get_version( // Output values, string& version); /** * Get status from Xrl Target */ virtual XrlCmdError common_0_1_get_status( // Output values, uint32_t& status, string& reason); /** * Shutdown cleanly */ virtual XrlCmdError common_0_1_shutdown(); virtual XrlCmdError common_0_1_startup() { return XrlCmdError::OKAY(); } /** * Enable/disable/start/stop the CLI. * * @param enable if true, then enable the CLI, otherwise disable it. */ virtual XrlCmdError cli_manager_0_1_enable_cli( // Input values, const bool& enable); virtual XrlCmdError cli_manager_0_1_start_cli(); virtual XrlCmdError cli_manager_0_1_stop_cli(); /** * Add a subnet address to the list of subnet addresses enabled for CLI * access. This method can be called more than once to add a number of * subnet addresses. * * @param subnet_addr the subnet address to add. */ virtual XrlCmdError cli_manager_0_1_add_enable_cli_access_from_subnet4( // Input values, const IPv4Net& subnet_addr); virtual XrlCmdError cli_manager_0_1_add_enable_cli_access_from_subnet6( // Input values, const IPv6Net& subnet_addr); /** * Delete a subnet address from the list of subnet addresses enabled for * CLI access. * * @param subnet_addr the subnet address to delete. */ virtual XrlCmdError cli_manager_0_1_delete_enable_cli_access_from_subnet4( // Input values, const IPv4Net& subnet_addr); virtual XrlCmdError cli_manager_0_1_delete_enable_cli_access_from_subnet6( // Input values, const IPv6Net& subnet_addr); /** * Add a subnet address to the list of subnet addresses disabled for CLI * access. This method can be called more than once to add a number of * subnet addresses. * * @param subnet_addr the subnet address to add. */ virtual XrlCmdError cli_manager_0_1_add_disable_cli_access_from_subnet4( // Input values, const IPv4Net& subnet_addr); virtual XrlCmdError cli_manager_0_1_add_disable_cli_access_from_subnet6( // Input values, const IPv6Net& subnet_addr); /** * Delete a subnet address from the list of subnet addresses disabled for * CLI access. * * @param subnet_addr the subnet address to delete. */ virtual XrlCmdError cli_manager_0_1_delete_disable_cli_access_from_subnet4( // Input values, const IPv4Net& subnet_addr); virtual XrlCmdError cli_manager_0_1_delete_disable_cli_access_from_subnet6( // Input values, const IPv6Net& subnet_addr); /** * Add a CLI command to the CLI manager * * @param processor_name the name of the module that will process that * command. * * @param command_name the name of the command to add. * * @param command_help the help for the command to add. * * @param is_command_cd is true, the string that will replace the CLI * prompt after we "cd" to that level of the CLI command-tree. * * @param command_cd_prompt if * * @param is_command_processor if true, this is a processing command that * would be performed by processor_name. */ virtual XrlCmdError cli_manager_0_1_add_cli_command( // Input values, const string& processor_name, const string& command_name, const string& command_help, const bool& is_command_cd, const string& command_cd_prompt, const bool& is_command_processor); /** * Delete a CLI command from the CLI manager * * @param processor_name the name of the module that sends the request. * * @param command_name the name of the command to delete. */ virtual XrlCmdError cli_manager_0_1_delete_cli_command( // Input values, const string& processor_name, const string& command_name); // // The CLI client-side (i.e., the CLI sending XRLs) // void send_process_command(const string& target, const string& processor_name, const string& cli_term_name, uint32_t cli_session_id, const vector& command_global_name, const vector& command_argv); void recv_process_command_output(const XrlError& xrl_error, const string *processor_name, const string *cli_term_name, const uint32_t *cli_session_id, const string *command_output); private: /** * Called when Finder connection is established. * * Note that this method overwrites an XrlRouter virtual method. */ virtual void finder_connect_event(); /** * Called when Finder disconnect occurs. * * Note that this method overwrites an XrlRouter virtual method. */ virtual void finder_disconnect_event(); CliNode& cli_node() const { return (_cli_node); } EventLoop& _eventloop; CliNode& _cli_node; XrlCliProcessorV0p1Client _xrl_cli_processor_client; bool _is_finder_alive; }; #endif // __CLI_XRL_CLI_NODE_HH__ xorp/cli/cli_command_pipe.cc0000664000076400007640000005311511421137511016221 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // CLI command "pipe" ("|") implementation. // #include "cli_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvx.hh" #include "libxorp/token.hh" #include "libxorp/utils.hh" #include "cli_client.hh" #include "cli_command_pipe.hh" // // Exported variables // // // Local constants definitions // // // Local structures/classes, typedefs and macros // // // Local variables // // // Local functions prototypes // static int cli_pipe_dummy_func(const string& server_name, const string& cli_term_name, uint32_t cli_session_id, const vector& command_global_name, const vector& argv); CliPipe::CliPipe(const string& init_pipe_name) : CliCommand(NULL, init_pipe_name, CliPipe::name2help(init_pipe_name)), _is_running(false), _counter(0), _bool_flag(false), _cli_client(NULL) { _pipe_type = name2pipe_type(init_pipe_name); CLI_PROCESS_CALLBACK _cb = callback(cli_pipe_dummy_func); set_cli_process_callback(_cb); set_can_pipe(true); switch (pipe_type()) { case CLI_PIPE_COMPARE: _start_func_ptr = &CliPipe::pipe_compare_start; _stop_func_ptr = &CliPipe::pipe_compare_stop; _process_func_ptr = &CliPipe::pipe_compare_process; _eof_func_ptr = &CliPipe::pipe_compare_eof; break; case CLI_PIPE_COMPARE_ROLLBACK: _start_func_ptr = &CliPipe::pipe_compare_rollback_start; _stop_func_ptr = &CliPipe::pipe_compare_rollback_stop; _process_func_ptr = &CliPipe::pipe_compare_rollback_process; _eof_func_ptr = &CliPipe::pipe_compare_rollback_eof; break; case CLI_PIPE_COUNT: _start_func_ptr = &CliPipe::pipe_count_start; _stop_func_ptr = &CliPipe::pipe_count_stop; _process_func_ptr = &CliPipe::pipe_count_process; _eof_func_ptr = &CliPipe::pipe_count_eof; break; case CLI_PIPE_DISPLAY: _start_func_ptr = &CliPipe::pipe_display_start; _stop_func_ptr = &CliPipe::pipe_display_stop; _process_func_ptr = &CliPipe::pipe_display_process; _eof_func_ptr = &CliPipe::pipe_display_eof; break; case CLI_PIPE_DISPLAY_DETAIL: _start_func_ptr = &CliPipe::pipe_display_detail_start; _stop_func_ptr = &CliPipe::pipe_display_detail_stop; _process_func_ptr = &CliPipe::pipe_display_detail_process; _eof_func_ptr = &CliPipe::pipe_display_detail_eof; break; case CLI_PIPE_DISPLAY_INHERITANCE: _start_func_ptr = &CliPipe::pipe_display_inheritance_start; _stop_func_ptr = &CliPipe::pipe_display_inheritance_stop; _process_func_ptr = &CliPipe::pipe_display_inheritance_process; _eof_func_ptr = &CliPipe::pipe_display_inheritance_eof; break; case CLI_PIPE_DISPLAY_XML: _start_func_ptr = &CliPipe::pipe_display_xml_start; _stop_func_ptr = &CliPipe::pipe_display_xml_stop; _process_func_ptr = &CliPipe::pipe_display_xml_process; _eof_func_ptr = &CliPipe::pipe_display_xml_eof; break; case CLI_PIPE_EXCEPT: _start_func_ptr = &CliPipe::pipe_except_start; _stop_func_ptr = &CliPipe::pipe_except_stop; _process_func_ptr = &CliPipe::pipe_except_process; _eof_func_ptr = &CliPipe::pipe_except_eof; break; case CLI_PIPE_FIND: _start_func_ptr = &CliPipe::pipe_find_start; _stop_func_ptr = &CliPipe::pipe_find_stop; _process_func_ptr = &CliPipe::pipe_find_process; _eof_func_ptr = &CliPipe::pipe_find_eof; break; case CLI_PIPE_HOLD: _start_func_ptr = &CliPipe::pipe_hold_start; _stop_func_ptr = &CliPipe::pipe_hold_stop; _process_func_ptr = &CliPipe::pipe_hold_process; _eof_func_ptr = &CliPipe::pipe_hold_eof; break; case CLI_PIPE_MATCH: _start_func_ptr = &CliPipe::pipe_match_start; _stop_func_ptr = &CliPipe::pipe_match_stop; _process_func_ptr = &CliPipe::pipe_match_process; _eof_func_ptr = &CliPipe::pipe_match_eof; break; case CLI_PIPE_NOMORE: _start_func_ptr = &CliPipe::pipe_nomore_start; _stop_func_ptr = &CliPipe::pipe_nomore_stop; _process_func_ptr = &CliPipe::pipe_nomore_process; _eof_func_ptr = &CliPipe::pipe_nomore_eof; break; case CLI_PIPE_RESOLVE: _start_func_ptr = &CliPipe::pipe_resolve_start; _stop_func_ptr = &CliPipe::pipe_resolve_stop; _process_func_ptr = &CliPipe::pipe_resolve_process; _eof_func_ptr = &CliPipe::pipe_resolve_eof; break; case CLI_PIPE_SAVE: _start_func_ptr = &CliPipe::pipe_save_start; _stop_func_ptr = &CliPipe::pipe_save_stop; _process_func_ptr = &CliPipe::pipe_save_process; _eof_func_ptr = &CliPipe::pipe_save_eof; break; case CLI_PIPE_TRIM: _start_func_ptr = &CliPipe::pipe_trim_start; _stop_func_ptr = &CliPipe::pipe_trim_stop; _process_func_ptr = &CliPipe::pipe_trim_process; _eof_func_ptr = &CliPipe::pipe_trim_eof; break; case CLI_PIPE_MAX: default: _start_func_ptr = &CliPipe::pipe_unknown_start; _stop_func_ptr = &CliPipe::pipe_unknown_stop; _process_func_ptr = &CliPipe::pipe_unknown_process; _eof_func_ptr = &CliPipe::pipe_unknown_eof; break; } } CliPipe::~CliPipe() { } string CliPipe::name2help(const string& pipe_name) { switch (name2pipe_type(pipe_name)) { case CLI_PIPE_COMPARE: return ("Compare configuration changes with a prior version"); case CLI_PIPE_COMPARE_ROLLBACK: return ("Compare configuration changes with a prior rollback version"); case CLI_PIPE_COUNT: return ("Count occurrences"); case CLI_PIPE_DISPLAY: return ("Display additional configuration information"); case CLI_PIPE_DISPLAY_DETAIL: return ("Display configuration data detail"); case CLI_PIPE_DISPLAY_INHERITANCE: return ("Display inherited configuration data and source group"); case CLI_PIPE_DISPLAY_XML: return ("Display XML content of the command"); case CLI_PIPE_EXCEPT: return ("Show only text that does not match a pattern"); case CLI_PIPE_FIND: return ("Search for the first occurrence of a pattern"); case CLI_PIPE_HOLD: return ("Hold text without exiting the --More-- prompt"); case CLI_PIPE_MATCH: return ("Show only text that matches a pattern"); case CLI_PIPE_NOMORE: return ("Don't paginate output"); case CLI_PIPE_RESOLVE: return ("Resolve IP addresses (NOT IMPLEMENTED YET)"); case CLI_PIPE_SAVE: return ("Save output text to a file (NOT IMPLEMENTED YET)"); case CLI_PIPE_TRIM: return ("Trip specified number of columns from the start line (NOT IMPLEMENTED YET)"); case CLI_PIPE_MAX: // FALLTHROUGH default: return ("Pipe type unknown"); } } CliPipe::cli_pipe_t CliPipe::name2pipe_type(const string& pipe_name) { string token_line = pipe_name; string token; token = pop_token(token_line); if (token.empty()) return (CLI_PIPE_MAX); if (token == "compare") return (CLI_PIPE_COMPARE); if (token == "count") return (CLI_PIPE_COUNT); if (token == "display") { token = pop_token(token_line); if (token.empty()) return (CLI_PIPE_DISPLAY); if (token == "detail") return (CLI_PIPE_DISPLAY_DETAIL); if (token == "inheritance") return (CLI_PIPE_DISPLAY_INHERITANCE); if (token == "xml") return (CLI_PIPE_DISPLAY_XML); return (CLI_PIPE_MAX); } if (token == "except") return (CLI_PIPE_EXCEPT); if (token == "find") return (CLI_PIPE_FIND); if (token == "hold") return (CLI_PIPE_HOLD); if (token == "match") return (CLI_PIPE_MATCH); if (token == "no-more") return (CLI_PIPE_NOMORE); if (token == "resolve") return (CLI_PIPE_RESOLVE); if (token == "save") return (CLI_PIPE_SAVE); if (token == "trim") return (CLI_PIPE_TRIM); return (CLI_PIPE_MAX); } int CliPipe::pipe_compare_start(string& input_line, string& error_msg) { _is_running = true; UNUSED(input_line); UNUSED(error_msg); return (XORP_OK); } int CliPipe::pipe_compare_stop(string& error_msg) { _is_running = false; UNUSED(error_msg); return (XORP_OK); } int CliPipe::pipe_compare_process(string& input_line) { if (! _is_running) return (XORP_ERROR); if (input_line.size()) return (XORP_OK); else return (XORP_ERROR); } int CliPipe::pipe_compare_eof(string& input_line) { if (! _is_running) return (XORP_ERROR); UNUSED(input_line); return (XORP_OK); } int CliPipe::pipe_compare_rollback_start(string& input_line, string& error_msg) { _is_running = true; UNUSED(input_line); UNUSED(error_msg); return (XORP_OK); } int CliPipe::pipe_compare_rollback_stop(string& error_msg) { _is_running = false; UNUSED(error_msg); return (XORP_OK); } int CliPipe::pipe_compare_rollback_process(string& input_line) { if (! _is_running) return (XORP_ERROR); if (input_line.size()) return (XORP_OK); else return (XORP_ERROR); } int CliPipe::pipe_compare_rollback_eof(string& input_line) { if (! _is_running) return (XORP_ERROR); UNUSED(input_line); return (XORP_OK); } int CliPipe::pipe_count_start(string& input_line, string& error_msg) { _counter = 0; _is_running = true; UNUSED(input_line); UNUSED(error_msg); return (XORP_OK); } int CliPipe::pipe_count_stop(string& error_msg) { _is_running = false; UNUSED(error_msg); return (XORP_OK); } int CliPipe::pipe_count_process(string& input_line) { if (! _is_running) return (XORP_ERROR); if (input_line.size()) { input_line = ""; _counter++; return (XORP_OK); } else { return (XORP_ERROR); } } int CliPipe::pipe_count_eof(string& input_line) { if (! _is_running) return (XORP_ERROR); pipe_count_process(input_line); input_line += c_format("Count: %d lines\n", _counter); return (XORP_OK); } int CliPipe::pipe_display_start(string& input_line, string& error_msg) { _is_running = true; UNUSED(input_line); UNUSED(error_msg); return (XORP_OK); } int CliPipe::pipe_display_stop(string& error_msg) { _is_running = false; UNUSED(error_msg); return (XORP_OK); } int CliPipe::pipe_display_process(string& input_line) { if (! _is_running) return (XORP_ERROR); if (input_line.size()) return (XORP_OK); else return (XORP_ERROR); } int CliPipe::pipe_display_eof(string& input_line) { if (! _is_running) return (XORP_ERROR); UNUSED(input_line); return (XORP_OK); } int CliPipe::pipe_display_detail_start(string& input_line, string& error_msg) { _is_running = true; UNUSED(input_line); UNUSED(error_msg); return (XORP_OK); } int CliPipe::pipe_display_detail_stop(string& error_msg) { _is_running = false; UNUSED(error_msg); return (XORP_OK); } int CliPipe::pipe_display_detail_process(string& input_line) { if (! _is_running) return (XORP_ERROR); if (input_line.size()) return (XORP_OK); else return (XORP_ERROR); } int CliPipe::pipe_display_detail_eof(string& input_line) { if (! _is_running) return (XORP_ERROR); UNUSED(input_line); return (XORP_OK); } int CliPipe::pipe_display_inheritance_start(string& input_line, string& error_msg) { _is_running = true; UNUSED(input_line); UNUSED(error_msg); return (XORP_OK); } int CliPipe::pipe_display_inheritance_stop(string& error_msg) { _is_running = false; UNUSED(error_msg); return (XORP_OK); } int CliPipe::pipe_display_inheritance_process(string& input_line) { if (! _is_running) return (XORP_ERROR); if (input_line.size()) return (XORP_OK); else return (XORP_ERROR); } int CliPipe::pipe_display_inheritance_eof(string& input_line) { if (! _is_running) return (XORP_ERROR); UNUSED(input_line); return (XORP_OK); } int CliPipe::pipe_display_xml_start(string& input_line, string& error_msg) { _is_running = true; UNUSED(input_line); UNUSED(error_msg); return (XORP_OK); } int CliPipe::pipe_display_xml_stop(string& error_msg) { _is_running = false; UNUSED(error_msg); return (XORP_OK); } int CliPipe::pipe_display_xml_process(string& input_line) { if (! _is_running) return (XORP_ERROR); if (input_line.size()) return (XORP_OK); else return (XORP_ERROR); } int CliPipe::pipe_display_xml_eof(string& input_line) { if (! _is_running) return (XORP_ERROR); UNUSED(input_line); return (XORP_OK); } int CliPipe::pipe_except_start(string& input_line, string& error_msg) { int error_code; string arg1; if (_pipe_args_list.empty()) { error_msg = c_format("missing argument for \"except\" pipe command."); return (XORP_ERROR); } arg1 = _pipe_args_list[0]; // TODO: check the flags error_code = regcomp(&_preg, arg1.c_str(), REG_EXTENDED | REG_ICASE | REG_NOSUB | REG_NEWLINE); if (error_code != 0) { char buffer[1024]; memset(buffer, 0, sizeof(buffer)); regerror(error_code, &_preg, buffer, sizeof(buffer)); error_msg = c_format("error initializing regular expression state: %s.", buffer); return (XORP_ERROR); } _is_running = true; UNUSED(input_line); return (XORP_OK); } int CliPipe::pipe_except_stop(string& error_msg) { regfree(&_preg); _is_running = false; UNUSED(error_msg); return (XORP_OK); } int CliPipe::pipe_except_process(string& input_line) { int ret_value; if (! _is_running) return (XORP_ERROR); if (! input_line.size()) return (XORP_ERROR); ret_value = regexec(&_preg, input_line.c_str(), 0, NULL, 0); if (ret_value == 0) { // Match input_line = ""; } else { // No-match } return (XORP_OK); } int CliPipe::pipe_except_eof(string& input_line) { if (! _is_running) return (XORP_ERROR); regfree(&_preg); UNUSED(input_line); return (XORP_OK); } int CliPipe::pipe_find_start(string& input_line, string& error_msg) { int error_code; string arg1; if (_pipe_args_list.empty()) { error_msg = c_format("missing argument for \"find\" pipe command."); return (XORP_ERROR); } arg1 = _pipe_args_list[0]; // TODO: check the flags error_code = regcomp(&_preg, arg1.c_str(), REG_EXTENDED | REG_ICASE | REG_NOSUB | REG_NEWLINE); if (error_code != 0) { char buffer[1024]; memset(buffer, 0, sizeof(buffer)); regerror(error_code, &_preg, buffer, sizeof(buffer)); error_msg = c_format("error initializing regular expression state: %s.", buffer); return (XORP_ERROR); } _is_running = true; UNUSED(input_line); return (XORP_OK); } int CliPipe::pipe_find_stop(string& error_msg) { regfree(&_preg); _is_running = false; UNUSED(error_msg); return (XORP_OK); } int CliPipe::pipe_find_process(string& input_line) { int ret_value; if (! _is_running) return (XORP_ERROR); if (! input_line.size()) return (XORP_ERROR); if (! _bool_flag) { // Evaluate the line ret_value = regexec(&_preg, input_line.c_str(), 0, NULL, 0); if (ret_value == 0) { // Match _bool_flag = true; } else { // No-match } } if (! _bool_flag) input_line = ""; // Don't print yet return (XORP_OK); } int CliPipe::pipe_find_eof(string& input_line) { if (! _is_running) return (XORP_ERROR); regfree(&_preg); UNUSED(input_line); return (XORP_OK); } int CliPipe::pipe_hold_start(string& input_line, string& error_msg) { if (_cli_client != NULL) { if (_cli_client->is_interactive()) _cli_client->set_nomore_mode(false); } _is_running = true; UNUSED(input_line); UNUSED(error_msg); return (XORP_OK); } int CliPipe::pipe_hold_stop(string& error_msg) { if (_cli_client != NULL) { if (_cli_client->is_interactive()) _cli_client->set_nomore_mode(false); // XXX: default is to hold } _is_running = false; UNUSED(error_msg); return (XORP_OK); } int CliPipe::pipe_hold_process(string& input_line) { if (! _is_running) return (XORP_ERROR); if (input_line.size()) return (XORP_OK); else return (XORP_ERROR); } int CliPipe::pipe_hold_eof(string& input_line) { if (! _is_running) return (XORP_ERROR); if (_cli_client != NULL) { _cli_client->set_hold_mode(true); } UNUSED(input_line); return (XORP_OK); } int CliPipe::pipe_match_start(string& input_line, string& error_msg) { int error_code; string arg1; if (_pipe_args_list.empty()) { error_msg = c_format("missing argument for \"match\" pipe command."); return (XORP_ERROR); } arg1 = _pipe_args_list[0]; // TODO: check the flags error_code = regcomp(&_preg, arg1.c_str(), REG_EXTENDED | REG_ICASE | REG_NOSUB | REG_NEWLINE); if (error_code != 0) { char buffer[1024]; memset(buffer, 0, sizeof(buffer)); regerror(error_code, &_preg, buffer, sizeof(buffer)); error_msg = c_format("error initializing regular expression state: %s.", buffer); return (XORP_ERROR); } _is_running = true; UNUSED(input_line); return (XORP_OK); } int CliPipe::pipe_match_stop(string& error_msg) { regfree(&_preg); _is_running = false; UNUSED(error_msg); return (XORP_OK); } int CliPipe::pipe_match_process(string& input_line) { int ret_value; if (! _is_running) return (XORP_ERROR); if (! input_line.size()) return (XORP_ERROR); ret_value = regexec(&_preg, input_line.c_str(), 0, NULL, 0); if (ret_value == 0) { // Match } else { // No-match input_line = ""; } return (XORP_OK); } int CliPipe::pipe_match_eof(string& input_line) { if (! _is_running) return (XORP_ERROR); regfree(&_preg); UNUSED(input_line); return (XORP_OK); } int CliPipe::pipe_nomore_start(string& input_line, string& error_msg) { if (_cli_client != NULL) { _cli_client->set_nomore_mode(true); } _is_running = true; UNUSED(input_line); UNUSED(error_msg); return (XORP_OK); } int CliPipe::pipe_nomore_stop(string& error_msg) { if (_cli_client != NULL) { if (_cli_client->is_interactive()) _cli_client->set_nomore_mode(false); } _is_running = false; UNUSED(error_msg); return (XORP_OK); } int CliPipe::pipe_nomore_process(string& input_line) { if (! _is_running) return (XORP_ERROR); if (input_line.size()) return (XORP_OK); else return (XORP_ERROR); } int CliPipe::pipe_nomore_eof(string& input_line) { if (! _is_running) return (XORP_ERROR); if (_cli_client != NULL) { if (_cli_client->is_interactive()) _cli_client->set_nomore_mode(false); } UNUSED(input_line); return (XORP_OK); } int CliPipe::pipe_resolve_start(string& input_line, string& error_msg) { _is_running = true; UNUSED(input_line); UNUSED(error_msg); return (XORP_OK); } int CliPipe::pipe_resolve_stop(string& error_msg) { _is_running = false; UNUSED(error_msg); return (XORP_OK); } int CliPipe::pipe_resolve_process(string& input_line) { if (! _is_running) return (XORP_ERROR); // TODO: implement it if (input_line.size()) return (XORP_OK); else return (XORP_ERROR); } int CliPipe::pipe_resolve_eof(string& input_line) { if (! _is_running) return (XORP_ERROR); UNUSED(input_line); return (XORP_OK); } int CliPipe::pipe_save_start(string& input_line, string& error_msg) { _is_running = true; UNUSED(input_line); UNUSED(error_msg); return (XORP_OK); } int CliPipe::pipe_save_stop(string& error_msg) { _is_running = false; UNUSED(error_msg); return (XORP_OK); } int CliPipe::pipe_save_process(string& input_line) { if (! _is_running) return (XORP_ERROR); // TODO: implement it if (input_line.size()) return (XORP_OK); else return (XORP_ERROR); } int CliPipe::pipe_save_eof(string& input_line) { if (! _is_running) return (XORP_ERROR); UNUSED(input_line); return (XORP_OK); } int CliPipe::pipe_trim_start(string& input_line, string& error_msg) { _is_running = true; UNUSED(input_line); UNUSED(error_msg); return (XORP_OK); } int CliPipe::pipe_trim_stop(string& error_msg) { _is_running = false; UNUSED(error_msg); return (XORP_OK); } int CliPipe::pipe_trim_process(string& input_line) { if (! _is_running) return (XORP_ERROR); // TODO: implement it if (input_line.size()) return (XORP_OK); else return (XORP_ERROR); } int CliPipe::pipe_trim_eof(string& input_line) { if (! _is_running) return (XORP_ERROR); UNUSED(input_line); return (XORP_OK); } int CliPipe::pipe_unknown_start(string& input_line, string& error_msg) { _is_running = true; UNUSED(input_line); UNUSED(error_msg); return (XORP_OK); } int CliPipe::pipe_unknown_stop(string& error_msg) { _is_running = false; UNUSED(error_msg); return (XORP_OK); } int CliPipe::pipe_unknown_process(string& input_line) { if (! _is_running) return (XORP_ERROR); if (input_line.size()) return (XORP_OK); else return (XORP_ERROR); } int CliPipe::pipe_unknown_eof(string& input_line) { if (! _is_running) return (XORP_ERROR); UNUSED(input_line); return (XORP_OK); } // // A dummy function // static int cli_pipe_dummy_func(const string& , // server_name, const string& , // cli_term_name uint32_t , // cli_session_id const vector& , // command_global_name const vector& // argv ) { return (XORP_OK); } xorp/cli/cli_client.cc0000664000076400007640000013446211613325702015055 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // CLI (Command-Line Interface) implementation for XORP. // #include "cli_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvx.hh" #include "libxorp/eventloop.hh" #include "libxorp/token.hh" #include "libxorp/utils.hh" #include "libcomm/comm_api.h" #include "cli_client.hh" #include "cli_command_pipe.hh" #include "cli_private.hh" #ifdef HOST_OS_WINDOWS #define isatty(x) (x).is_console() #endif // // Exported variables // // // Local constants definitions // // // Local structures/classes, typedefs and macros // // // Local variables // // // Local functions prototypes // // TODO: use a parameter to define the buffer size CliClient::CliClient(CliNode& init_cli_node, XorpFd input_fd, XorpFd output_fd, const string& startup_cli_prompt) : _cli_node(init_cli_node), _input_fd(input_fd), _output_fd(output_fd), _input_fd_file(NULL), _output_fd_file(NULL), _client_type(CLIENT_TERMINAL), // XXX: default is terminal _gl(NULL), _telnet_iac(false), _telnet_sb(false), _telnet_dont(false), _telnet_do(false), _telnet_wont(false), _telnet_will(false), _telnet_binary(false), _window_width(80), // TODO: use a parameter instead _window_height(25), // TODO: use a parameter instead _command_buffer(1024), _telnet_sb_buffer(1024), _is_modified_stdio_termios_icanon(false), _is_modified_stdio_termios_echo(false), _is_modified_stdio_termios_isig(false), _saved_stdio_termios_vmin(0), _saved_stdio_termios_vtime(0), _executed_cli_command(NULL), _current_cli_command(NULL), _buff_curpos(0), _is_pipe_mode(false), _is_nomore_mode(false), _is_hold_mode(false), _is_page_mode(false), _is_page_buffer_mode(NULL), _page_buffer(NULL), _page_buffer_last_line_n(NULL), _is_output_buffer_mode(false), _output_buffer_last_line_n(0), _is_help_buffer_mode(false), _help_buffer_last_line_n(0), _is_help_mode(false), _is_prompt_flushed(false), _cli_session_from_address(_cli_node.family()), _is_cli_session_active(false), _cli_session_session_id(0), _is_network(false), _is_log_output(false), _is_waiting_for_data(false) { set_current_cli_command(_cli_node.cli_command_root()); set_current_cli_prompt(startup_cli_prompt); _is_page_buffer_mode = &_is_output_buffer_mode; _page_buffer = &_output_buffer; _page_buffer_last_line_n = &_output_buffer_last_line_n; // // Session info state // set_cli_session_user_name("unknown_user"); set_cli_session_term_name("unknown_terminal"); set_cli_session_session_id(~0U); // XXX: ~0U has no particular meaning set_cli_session_start_time(TimeVal(0, 0)); set_cli_session_stop_time(TimeVal(0, 0)); set_is_cli_session_active(false); // // Set in "no-more" mode if a non-interactive client // if (! is_interactive()) set_nomore_mode(true); } CliClient::~CliClient() { string dummy_error_msg; stop_connection(dummy_error_msg); set_log_output(false); // Remove the input file descriptor from the eventloop if (_input_fd.is_valid()) { cli_node().eventloop().remove_ioevent_cb(_input_fd, IOT_READ); } // Close files and file descriptors if (_input_fd_file != NULL) { fclose(_input_fd_file); _input_fd_file = NULL; _input_fd.clear(); } if (_output_fd_file != NULL) { fclose(_output_fd_file); _output_fd_file = NULL; _output_fd.clear(); } if (_input_fd.is_valid()) { comm_close(_input_fd); _input_fd.clear(); } if (_output_fd.is_valid()) { comm_close(_output_fd); _output_fd.clear(); } if (_gl != NULL) _gl = del_GetLine(_gl); delete_pipe_all(); } bool CliClient::done() const { if (_is_waiting_for_data) return (false); if (! _pending_input_data.empty()) return (false); return (true); } int CliClient::set_log_output(bool v) { if (v) { if (is_log_output()) return (XORP_ERROR); // Already added if (xlog_add_output_func(&CliNode::xlog_output, this) != 0) return (XORP_ERROR); _is_log_output = true; return (XORP_OK); } else { if (! is_log_output()) return (XORP_ERROR); // Was not added if (xlog_remove_output_func(&CliNode::xlog_output, this) != 0) return (XORP_ERROR); _is_log_output = false; return (XORP_OK); } // NOTERACHED return (XORP_ERROR); } bool CliClient::is_input_tty() const { return (isatty(_input_fd) != 0); } bool CliClient::is_output_tty() const { return (isatty(_output_fd) != 0); } bool CliClient::is_network() const { return (_is_network); } void CliClient::set_network_client(bool v) { _is_network = v; } bool CliClient::is_telnet() const { // // TODO: XXX: for the time being we assume that all network connections // are telnet. // return (is_network()); } bool CliClient::is_interactive() const { return (is_input_tty() || is_telnet()); } CliPipe * CliClient::add_pipe(const string& pipe_name) { CliPipe *cli_pipe; cli_pipe = new CliPipe(pipe_name); if (cli_pipe->is_invalid()) { delete cli_pipe; return (NULL); } _pipe_list.push_back(cli_pipe); cli_pipe->set_cli_client(this); set_pipe_mode(true); return (cli_pipe); } CliPipe * CliClient::add_pipe(const string& pipe_name, const list& args_list) { CliPipe *cli_pipe; cli_pipe = add_pipe(pipe_name); if (cli_pipe == NULL) return (NULL); // Add the list of arguments list::const_iterator iter; for (iter = args_list.begin(); iter != args_list.end(); ++iter) { string arg = *iter; cli_pipe->add_pipe_arg(arg); } return (cli_pipe); } void CliClient::delete_pipe_all() { delete_pointers_list(_pipe_list); set_pipe_mode(false); } void CliClient::append_page_buffer_line(const string& buffer_line) { page_buffer().push_back(buffer_line); } void CliClient::concat_page_buffer_line(const string& buffer_line, size_t pos) { XLOG_ASSERT(pos < page_buffer().size()); string& line = page_buffer()[pos]; line += buffer_line; } // Process the line throught the pipes void CliClient::process_line_through_pipes(string& pipe_line) { list::iterator iter; if (! is_pipe_mode()) return; for (iter = _pipe_list.begin(); iter != _pipe_list.end(); ++iter) { CliPipe *cli_pipe = *iter; cli_pipe->process_func(pipe_line); if (pipe_line.empty()) break; } } // Set the page mode void CliClient::set_page_mode(bool v) { const char *s; if (v) { // TRUE if (_is_page_mode) return; _is_page_mode = v; // // Save the key bind commands // _action_name_up_arrow = (s = gl_get_key_binding_action_name(gl(), "up")) ? (s) : ""; _action_name_down_arrow = (s = gl_get_key_binding_action_name(gl(), "down")) ? (s) : ""; _action_name_tab = (s = gl_get_key_binding_action_name(gl(), "\t")) ? (s) : ""; _action_name_newline_n = (s = gl_get_key_binding_action_name(gl(), "\n")) ? (s) : ""; _action_name_newline_r = (s = gl_get_key_binding_action_name(gl(), "\r")) ? (s) : ""; // XXX: no need to save the spacebar binding //_action_name_spacebar //= (s = gl_get_key_binding_action_name(gl(), "\\\\\\040")) ? (s) : ""; _action_name_ctrl_a = (s = gl_get_key_binding_action_name(gl(), "^A")) ? (s) : ""; _action_name_ctrl_b = (s = gl_get_key_binding_action_name(gl(), "^B")) ? (s) : ""; _action_name_ctrl_c = (s = gl_get_key_binding_action_name(gl(), "^C")) ? (s) : ""; _action_name_ctrl_d = (s = gl_get_key_binding_action_name(gl(), "^D")) ? (s) : ""; _action_name_ctrl_e = (s = gl_get_key_binding_action_name(gl(), "^E")) ? (s) : ""; _action_name_ctrl_f = (s = gl_get_key_binding_action_name(gl(), "^F")) ? (s) : ""; _action_name_ctrl_h = (s = gl_get_key_binding_action_name(gl(), "^H")) ? (s) : ""; _action_name_ctrl_k = (s = gl_get_key_binding_action_name(gl(), "^K")) ? (s) : ""; _action_name_ctrl_l = (s = gl_get_key_binding_action_name(gl(), "^L")) ? (s) : ""; _action_name_ctrl_m = (s = gl_get_key_binding_action_name(gl(), "^M")) ? (s) : ""; _action_name_ctrl_n = (s = gl_get_key_binding_action_name(gl(), "^N")) ? (s) : ""; _action_name_ctrl_p = (s = gl_get_key_binding_action_name(gl(), "^P")) ? (s) : ""; _action_name_ctrl_u = (s = gl_get_key_binding_action_name(gl(), "^U")) ? (s) : ""; _action_name_ctrl_x = (s = gl_get_key_binding_action_name(gl(), "^X")) ? (s) : ""; // // Set new binding // string bind_command; if (_action_name_up_arrow.size()) { bind_command = "bind up user-event1"; gl_configure_getline(gl(), bind_command.c_str(), NULL, NULL); } if (_action_name_down_arrow.size()) { bind_command = "bind down user-event2"; gl_configure_getline(gl(), bind_command.c_str(), NULL, NULL); } // XXX: all bindings below are not used, but are needed to // avoid 'beeps' if (_action_name_tab.size()) { bind_command = "bind \\t user-event3"; gl_configure_getline(gl(), bind_command.c_str(), NULL, NULL); } if (_action_name_newline_n.size()) { bind_command = "bind \\n user-event4"; gl_configure_getline(gl(), bind_command.c_str(), NULL, NULL); } if (_action_name_newline_r.size()) { bind_command = "bind \\r user-event4"; gl_configure_getline(gl(), bind_command.c_str(), NULL, NULL); } if (_action_name_spacebar.size() || true) { // XXX: always (re)bind bind_command = "bind \\\\\\040 user-event4"; gl_configure_getline(gl(), bind_command.c_str(), NULL, NULL); } if (_action_name_ctrl_a.size()) { bind_command = "bind ^A user-event4"; gl_configure_getline(gl(), bind_command.c_str(), NULL, NULL); } if (_action_name_ctrl_b.size()) { bind_command = "bind ^B user-event4"; gl_configure_getline(gl(), bind_command.c_str(), NULL, NULL); } if (_action_name_ctrl_c.size()) { bind_command = "bind ^C user-event4"; gl_configure_getline(gl(), bind_command.c_str(), NULL, NULL); } if (_action_name_ctrl_d.size()) { bind_command = "bind ^D user-event4"; gl_configure_getline(gl(), bind_command.c_str(), NULL, NULL); } if (_action_name_ctrl_e.size()) { bind_command = "bind ^E user-event4"; gl_configure_getline(gl(), bind_command.c_str(), NULL, NULL); } if (_action_name_ctrl_f.size()) { bind_command = "bind ^F user-event4"; gl_configure_getline(gl(), bind_command.c_str(), NULL, NULL); } if (_action_name_ctrl_h.size()) { bind_command = "bind ^H user-event4"; gl_configure_getline(gl(), bind_command.c_str(), NULL, NULL); } if (_action_name_ctrl_k.size()) { bind_command = "bind ^K user-event4"; gl_configure_getline(gl(), bind_command.c_str(), NULL, NULL); } if (_action_name_ctrl_l.size()) { bind_command = "bind ^L user-event4"; gl_configure_getline(gl(), bind_command.c_str(), NULL, NULL); } if (_action_name_ctrl_m.size()) { bind_command = "bind ^M user-event4"; gl_configure_getline(gl(), bind_command.c_str(), NULL, NULL); } if (_action_name_ctrl_n.size()) { bind_command = "bind ^N user-event4"; gl_configure_getline(gl(), bind_command.c_str(), NULL, NULL); } if (_action_name_ctrl_p.size()) { bind_command = "bind ^P user-event4"; gl_configure_getline(gl(), bind_command.c_str(), NULL, NULL); } if (_action_name_ctrl_u.size()) { bind_command = "bind ^U user-event4"; gl_configure_getline(gl(), bind_command.c_str(), NULL, NULL); } if (_action_name_ctrl_x.size()) { bind_command = "bind ^X user-event4"; gl_configure_getline(gl(), bind_command.c_str(), NULL, NULL); } return; } else { // FALSE if (! _is_page_mode) return; _is_page_mode = v; // // Restore the key bind commands // string bind_command; if (_action_name_up_arrow.size()) { bind_command = "bind up " + _action_name_up_arrow; gl_configure_getline(gl(), bind_command.c_str(), NULL, NULL); } if (_action_name_down_arrow.size()) { bind_command = "bind down " + _action_name_down_arrow; gl_configure_getline(gl(), bind_command.c_str(), NULL, NULL); } if (_action_name_tab.size()) { bind_command = "bind \\t " + _action_name_tab; gl_configure_getline(gl(), bind_command.c_str(), NULL, NULL); } if (_action_name_newline_n.size()) { bind_command = "bind \\n " + _action_name_newline_n; gl_configure_getline(gl(), bind_command.c_str(), NULL, NULL); } if (_action_name_newline_r.size()) { bind_command = "bind \\r " + _action_name_newline_r; gl_configure_getline(gl(), bind_command.c_str(), NULL, NULL); } if (_action_name_spacebar.size() || true) { // XXX: always (re)bind bind_command = "bind \\\\\\040 " + _action_name_spacebar; gl_configure_getline(gl(), bind_command.c_str(), NULL, NULL); } if (_action_name_ctrl_a.size()) { bind_command = "bind ^A " + _action_name_ctrl_a; gl_configure_getline(gl(), bind_command.c_str(), NULL, NULL); } if (_action_name_ctrl_b.size()) { bind_command = "bind ^B " + _action_name_ctrl_b; gl_configure_getline(gl(), bind_command.c_str(), NULL, NULL); } if (_action_name_ctrl_c.size()) { bind_command = "bind ^C " + _action_name_ctrl_c; gl_configure_getline(gl(), bind_command.c_str(), NULL, NULL); } if (_action_name_ctrl_d.size()) { bind_command = "bind ^D " + _action_name_ctrl_d; gl_configure_getline(gl(), bind_command.c_str(), NULL, NULL); } if (_action_name_ctrl_e.size()) { bind_command = "bind ^E " + _action_name_ctrl_e; gl_configure_getline(gl(), bind_command.c_str(), NULL, NULL); } if (_action_name_ctrl_f.size()) { bind_command = "bind ^F " + _action_name_ctrl_f; gl_configure_getline(gl(), bind_command.c_str(), NULL, NULL); } if (_action_name_ctrl_h.size()) { bind_command = "bind ^H " + _action_name_ctrl_h; gl_configure_getline(gl(), bind_command.c_str(), NULL, NULL); } if (_action_name_ctrl_k.size()) { bind_command = "bind ^K " + _action_name_ctrl_k; gl_configure_getline(gl(), bind_command.c_str(), NULL, NULL); } if (_action_name_ctrl_l.size()) { bind_command = "bind ^L " + _action_name_ctrl_l; gl_configure_getline(gl(), bind_command.c_str(), NULL, NULL); } if (_action_name_ctrl_m.size()) { bind_command = "bind ^M " + _action_name_ctrl_m; gl_configure_getline(gl(), bind_command.c_str(), NULL, NULL); } if (_action_name_ctrl_n.size()) { bind_command = "bind ^N " + _action_name_ctrl_n; gl_configure_getline(gl(), bind_command.c_str(), NULL, NULL); } if (_action_name_ctrl_p.size()) { bind_command = "bind ^P " + _action_name_ctrl_p; gl_configure_getline(gl(), bind_command.c_str(), NULL, NULL); } if (_action_name_ctrl_u.size()) { bind_command = "bind ^U " + _action_name_ctrl_u; gl_configure_getline(gl(), bind_command.c_str(), NULL, NULL); } if (_action_name_ctrl_x.size()) { bind_command = "bind ^X " + _action_name_ctrl_x; gl_configure_getline(gl(), bind_command.c_str(), NULL, NULL); } return; } } const string& CliClient::page_buffer_line(size_t line_n) const { XLOG_ASSERT(line_n < _page_buffer->size()); return ((*_page_buffer)[line_n]); } size_t CliClient::page_buffer_window_lines_n() { if (page_buffer_lines_n() == 0) return (0); return (page_buffer2window_line_n(page_buffer_lines_n() - 1)); } size_t CliClient::page_buffer_last_window_line_n() { if (page_buffer_last_line_n() == 0) return (0); return (page_buffer2window_line_n(page_buffer_last_line_n() - 1)); } size_t CliClient::page_buffer2window_line_n(size_t buffer_line_n) { size_t i; size_t window_line_n = 0; for (i = 0; i <= buffer_line_n; i++) { window_line_n += window_lines_n(i); } return (window_line_n); } size_t CliClient::window_lines_n(size_t buffer_line_n) { bool has_newline = false; size_t window_line_n = 0; XLOG_ASSERT(buffer_line_n < _page_buffer->size()); const string& line = page_buffer_line(buffer_line_n); // Get the line size, but don't count the trailing '\r' and '\n' size_t line_size = line.size(); while (line_size > 0) { if ((line[line_size - 1] == '\r') || (line[line_size - 1] == '\n')) { line_size--; has_newline = true; continue; } break; } window_line_n = (line_size / window_width()) + ((line_size % window_width())? (1) : (0)); if ((line_size == 0) && has_newline) window_line_n++; return (window_line_n); } size_t CliClient::calculate_first_page_buffer_line_by_window_size( size_t last_buffer_line_n, size_t max_window_size) { size_t first_buffer_line_n; size_t window_size = 0; if (last_buffer_line_n == 0) return (0); first_buffer_line_n = last_buffer_line_n - 1; window_size += window_lines_n(first_buffer_line_n); while (window_size < max_window_size) { if (first_buffer_line_n == 0) break; window_size += window_lines_n(first_buffer_line_n - 1); if (window_size > max_window_size) break; first_buffer_line_n--; } return (first_buffer_line_n); } // // Print a message. If a terminal connection, add '\r' before each '\n' // (unless the previous character to print is indeed '\r'). // // XXX: if we cli_print(""), this is EOF, and will "clear-out" // any remaining data in _buffer_line through the pipes int CliClient::cli_print(const string& msg) { int ret_value; string pipe_line, pipe_result; bool is_eof_input = false; bool is_incomplete_last_line = false; if (msg.size() == 0 || (msg[0] == '\0')) { is_eof_input = true; } // Test if the last line added to the page buffer was incomplete do { if (page_buffer().empty()) break; const string& last_line = page_buffer_line(page_buffer().size() - 1); if (last_line.empty()) break; if (last_line[last_line.size() - 1] == '\n') break; is_incomplete_last_line = true; break; } while (false); // Process the data throught the pipe pipe_line += _buffer_line; _buffer_line = ""; size_t i = 0; while (msg[i] != '\0') { pipe_line += msg[i]; if (msg[i] == '\n') { // Process the line throught the pipe process_line_through_pipes(pipe_line); pipe_result += pipe_line; pipe_line = ""; } i++; } if (pipe_line.size()) { if (! _pipe_list.empty()) { if (is_eof_input) { process_line_through_pipes(pipe_line); } else { _buffer_line += pipe_line; pipe_line = ""; } } pipe_result += pipe_line; pipe_line = ""; } // If a terminal connection, add '\r' before each '\n' // (unless the previous character to print is indeed '\r'). pipe_line = ""; string output_string = ""; for (i = 0; i < pipe_result.size(); i++) { if ((_client_type == CLIENT_TERMINAL) && (pipe_result[i] == '\n')) { if ((! telnet_binary()) && (! ((i > 0) && (pipe_result[i-1] == '\r')))) { pipe_line += '\r'; // XXX: Carrige-return } } pipe_line += pipe_result[i]; if (is_page_buffer_mode() && (_client_type == CLIENT_TERMINAL) && (pipe_result[i] == '\n')) { // Add the line to the buffer output if (is_incomplete_last_line) { concat_page_buffer_line(pipe_line, page_buffer().size() - 1); } else { append_page_buffer_line(pipe_line); } if ((page_buffer_window_lines_n() >= window_height()) && (! is_nomore_mode())) { set_page_mode(true); } else { if (! is_incomplete_last_line) incr_page_buffer_last_line_n(); output_string += pipe_line; } pipe_line = ""; is_incomplete_last_line = false; } } if (pipe_line.size()) { // Insert the remaining partial line into the buffer if (is_page_buffer_mode() && (_client_type == CLIENT_TERMINAL)) { // Add the line to the buffer output if (is_incomplete_last_line) { concat_page_buffer_line(pipe_line, page_buffer().size() - 1); } else { append_page_buffer_line(pipe_line); } if ((page_buffer_window_lines_n() >= window_height()) && (! is_nomore_mode())) { set_page_mode(true); } else { if (! is_incomplete_last_line) incr_page_buffer_last_line_n(); } } } if (! (is_page_buffer_mode() && is_page_mode())) { if (pipe_line.size()) output_string += pipe_line; // XXX: the remaining partial line } ret_value = output_string.size(); // if (! (is_page_buffer_mode() && is_page_mode())) if (output_string.size()) ret_value = fprintf(_output_fd_file, "%s", output_string.c_str()); return (ret_value); } // Return: %XORP_OK on success, otherwise %XORP_ERROR. int CliClient::cli_flush() { if ((_output_fd_file != NULL) && (fflush(_output_fd_file) == 0)) return (XORP_OK); return (XORP_ERROR); } void CliClient::set_current_cli_command(CliCommand *cli_command) { _current_cli_command = cli_command; if (cli_command->cd_prompt().size() > 0) set_current_cli_prompt(cli_command->cd_prompt()); } void CliClient::set_current_cli_prompt(const string& cli_prompt) { _current_cli_prompt = cli_prompt; gl_replace_prompt(gl(), _current_cli_prompt.c_str()); } // // Process one input character while in "page mode" // int CliClient::process_char_page_mode(uint8_t val) { string restore_cli_prompt = current_cli_prompt(); // The current prompt bool old_page_buffer_mode = is_page_buffer_mode(); // // Reset the line and clear the current prompt // gl_redisplay_line(gl()); // XXX: must be first gl_reset_line(gl()); set_current_cli_prompt(""); gl_reset_line(gl()); cli_flush(); // // Page commands // // // Print help // if ((val == 'h')) { if (! is_help_mode()) { set_help_mode(true); _is_page_buffer_mode = &_is_help_buffer_mode; _page_buffer = &_help_buffer; _page_buffer_last_line_n = &_help_buffer_last_line_n; set_page_buffer_mode(true); #define CLI_HELP_STRING \ " SUMMARY OF MORE COMMANDS\n" \ "\n" \ " -- Get Help --\n" \ " h * Display this help.\n" \ "\n" \ " -- Scroll Down --\n" \ " Enter Return j * Scroll down one line.\n" \ " ^M ^N DownArrow\n" \ " Tab d ^D ^X * Scroll down one-half screen.\n" \ " Space ^F * Scroll down one whole screen.\n" \ " ^E G * Scroll down to the bottom of the output.\n" \ " N * Display the output all at once instead of one\n"\ " screen at a time. (Same as specifying the\n" \ " | no-more command.)\n" \ "\n" \ " -- Scroll Up --\n" \ " k ^H ^P * Display the previous line of output.\n" \ " UpArrow\n" \ " u ^U * Scroll up one-half screen.\n" \ " b ^B * Scroll up one whole screen.\n" \ " ^A g * Scroll up to the top of the output.\n" \ "\n" \ " -- Misc Commands --\n" \ " ^L * Redraw the output on the screen.\n" \ " q Q ^C ^K * Interrupt the display of output.\n" \ "\n" cli_print(CLI_HELP_STRING); set_page_buffer_mode(false); } goto redisplay_screen_label; } // // Interrupt the display of output // if ((val == 'q') || (val == 'Q') || (val == CHAR_TO_CTRL('c')) || (val == CHAR_TO_CTRL('k'))) { if (is_waiting_for_data()) { interrupt_command(); } goto exit_page_mode_label; } // // Scroll down one line // if ((val == '\n') || (val == '\r') || (val == 'j') || (val == CHAR_TO_CTRL('m')) || (val == CHAR_TO_CTRL('n')) || (gl_get_user_event(gl()) == 2)) { if (page_buffer_last_line_n() < page_buffer_lines_n()) { set_page_buffer_mode(false); cli_print(page_buffer_line(page_buffer_last_line_n())); set_page_buffer_mode(old_page_buffer_mode); incr_page_buffer_last_line_n(); } goto redisplay_line_label; } // // Scroll down one-half screen // if ((val == '\t') || (val == 'd') || (val == CHAR_TO_CTRL('d')) || (val == CHAR_TO_CTRL('x'))) { for (size_t i = 0; i <= window_height() / 2; ) { if (page_buffer_last_line_n() >= page_buffer_lines_n()) break; i += window_lines_n(page_buffer_last_line_n()); if (i > window_height() / 2) break; set_page_buffer_mode(false); cli_print(page_buffer_line(page_buffer_last_line_n())); set_page_buffer_mode(old_page_buffer_mode); incr_page_buffer_last_line_n(); } goto redisplay_line_label; } // // Scroll down one whole screen // if ((val == ' ') || (val == CHAR_TO_CTRL('f'))) { for (size_t i = 0; i <= window_height() - 1; ) { if (page_buffer_last_line_n() >= page_buffer_lines_n()) break; i += window_lines_n(page_buffer_last_line_n()); if (i > window_height() - 1) break; set_page_buffer_mode(false); cli_print(page_buffer_line(page_buffer_last_line_n())); set_page_buffer_mode(old_page_buffer_mode); incr_page_buffer_last_line_n(); } goto redisplay_line_label; } // // Scroll down to the bottom of the output // if ((val == 'G') || (val == CHAR_TO_CTRL('e'))) { set_page_buffer_last_line_n(page_buffer_lines_n()); goto redisplay_screen_label; } // // Display the output all at once instead of oen screen at a time. // (Same as specifying the "| no-more" command.) // if ((val == 'N')) { while (page_buffer_last_line_n() < page_buffer_lines_n()) { set_page_buffer_mode(false); cli_print(page_buffer_line(page_buffer_last_line_n())); set_page_buffer_mode(old_page_buffer_mode); incr_page_buffer_last_line_n(); } // TODO: do we want to exit the page mode at the end? // If "yes", then the line below should be changed to // goto exit_page_mode_label; goto redisplay_line_label; } // // Display the previous line of output // if ((val == 'k') || (val == CHAR_TO_CTRL('h')) || (val == CHAR_TO_CTRL('p')) || (gl_get_user_event(gl()) == 1)) { if (page_buffer_last_line_n() > 0) decr_page_buffer_last_line_n(); goto redisplay_screen_label; } // // Scroll up one-half screen // if ((val == 'u') || (val == CHAR_TO_CTRL('u'))) { if (page_buffer_last_line_n() > 0) { size_t start_window_line = calculate_first_page_buffer_line_by_window_size( page_buffer_last_line_n(), window_height() / 2); set_page_buffer_last_line_n(start_window_line); } goto redisplay_screen_label; } // // Scroll up one whole screen // if ((val == 'b') || (val == CHAR_TO_CTRL('b'))) { if (page_buffer_last_line_n() > 0) { size_t start_window_line = calculate_first_page_buffer_line_by_window_size( page_buffer_last_line_n(), window_height() - 1); set_page_buffer_last_line_n(start_window_line); } goto redisplay_screen_label; } // // Scroll up to the top of the output // if ((val == 'g') || (val == CHAR_TO_CTRL('a'))) { set_page_buffer_last_line_n(0); goto redisplay_screen_label; } // // Redraw the output of the screen // if ((val == CHAR_TO_CTRL('l'))) { redisplay_screen_label: size_t i, start_window_line = 0; set_page_buffer_mode(false); // XXX: clean-up the previous window for (i = 0; i < window_height() - 1; i++) cli_print("\n"); if (page_buffer_last_line_n() > 0) { start_window_line = calculate_first_page_buffer_line_by_window_size( page_buffer_last_line_n(), window_height() - 1); } set_page_buffer_last_line_n(start_window_line); for (i = 0; i <= window_height() - 1; ) { if (page_buffer_last_line_n() >= page_buffer_lines_n()) break; i += window_lines_n(page_buffer_last_line_n()); if (i > window_height() - 1) break; cli_print(page_buffer_line(page_buffer_last_line_n())); incr_page_buffer_last_line_n(); } // XXX: fill-up the rest of the window for ( ; i < window_height() - 1; i++) cli_print("\n"); set_page_buffer_mode(old_page_buffer_mode); goto redisplay_line_label; } goto redisplay_line_label; exit_page_mode_label: reset_page_buffer(); if (is_interactive()) set_nomore_mode(false); if (! is_help_mode()) { // Exit the page mode set_page_mode(false); set_buff_curpos(0); gl_reset_line(gl()); command_buffer().reset(); restore_cli_prompt = current_cli_command()->cd_prompt(); } else { // Exit the help page mode set_help_mode(false); _is_page_buffer_mode = &_is_output_buffer_mode; _page_buffer = &_output_buffer; _page_buffer_last_line_n = &_output_buffer_last_line_n; goto redisplay_screen_label; } // FALLTHROUGH redisplay_line_label: cli_flush(); if (is_page_mode()) { if (page_buffer_last_line_n() < page_buffer_lines_n()) restore_cli_prompt = " --More-- "; else restore_cli_prompt = " --More-- (END) "; } set_current_cli_prompt(restore_cli_prompt); gl_redisplay_line(gl()); cli_flush(); return (XORP_OK); } void CliClient::post_process_command() { // // Test if we are waiting for the result from a processor // if (is_waiting_for_data()) { // We are waiting for the result; silently return. return; } // // Reset the state for the currently executed command // _executed_cli_command = NULL; _executed_cli_command_name.clear(); _executed_cli_command_args.clear(); // // Pipe-process the result // string final_string = ""; cli_print(""); // XXX: EOF: clear-out the pipe list::iterator iter; for (iter = _pipe_list.begin(); iter != _pipe_list.end(); ++iter) { CliPipe *cli_pipe = *iter; cli_pipe->process_func(final_string); cli_pipe->eof_func(final_string); } if (final_string.size()) { bool old_pipe_mode = is_pipe_mode(); set_pipe_mode(false); cli_print(final_string); set_pipe_mode(old_pipe_mode); } if (is_hold_mode()) { set_page_mode(true); set_hold_mode(false); } delete_pipe_all(); if (! is_page_mode()) reset_page_buffer(); // // Page-related state // set_page_buffer_mode(false); if (is_page_mode()) { if (page_buffer_last_line_n() < page_buffer_lines_n()) set_current_cli_prompt(" --More-- "); else set_current_cli_prompt(" --More-- (END) "); } else { reset_page_buffer(); if (is_interactive()) set_nomore_mode(false); } // // Reset buffer, cursor, prompt // command_buffer().reset(); set_buff_curpos(0); if (! is_prompt_flushed()) cli_print(current_cli_prompt()); set_prompt_flushed(false); cli_flush(); // // Process the pending input data (if any) // if (! _pending_input_data.empty()) { schedule_process_input_data(); } } void CliClient::flush_process_command_output() { // // Test if we are waiting for the result from a processor // if (! is_waiting_for_data()) { // We are not waiting for the result; silently return. return; } if (is_help_mode()) { // We don't want the output while in help mode return; } // // Page-related state // if (is_page_mode() && ! is_prompt_flushed()) { string restore_cli_prompt; bool old_page_buffer_mode = is_page_buffer_mode(); set_page_buffer_mode(false); if (page_buffer_last_line_n() < page_buffer_lines_n()) restore_cli_prompt = " --More-- "; else restore_cli_prompt = " --More-- (END) "; set_current_cli_prompt(restore_cli_prompt); cli_print(current_cli_prompt()); cli_flush(); set_page_buffer_mode(old_page_buffer_mode); set_prompt_flushed(true); } } // // Process one input character // Return: %XORP_OK on success, otherwise %XORP_ERROR. // XXX: returning %XORP_ERROR is also an indication that the connection // has been closed. int CliClient::process_char(const string& line, uint8_t val, bool& stop_processing) { int gl_buff_curpos = gl_get_buff_curpos(gl()); int ret_value = XORP_OK; stop_processing = false; if ((val == '\n') || (val == '\r')) { // New command XLOG_ASSERT(is_waiting_for_data() == false); set_page_buffer_mode(true); process_command(line); post_process_command(); // // Set the flag to stop processing pending input data while // processing previous commands. // if (is_waiting_for_data()) { stop_processing = true; } return (XORP_OK); } if (val == '?') { // Command-line help //set_page_buffer_mode(true); // TODO: add "page" support command_line_help(line, gl_buff_curpos, true); //set_page_buffer_mode(false); //if (is_page_mode()) { //if (page_buffer_last_line_n() < page_buffer_lines_n()) //set_current_cli_prompt(" --More-- "); //else //set_current_cli_prompt(" --More-- (END) "); //} else { //reset_page_buffer(); //} //cli_print(current_cli_prompt()); //command_buffer().reset(); //set_buff_curpos(0); return (XORP_OK); } // // XXX: The (val == ' ') and 'Ctrl-C' cases are handled by the // parent function. // // All other characters which we need to print // Store the line in the command buffer command_buffer().reset(); ret_value = XORP_OK; for (int i = 0; line[i] != '\0'; i++) { ret_value = command_buffer().add_data(line[i]); if (ret_value != XORP_OK) break; } if (ret_value == XORP_OK) ret_value = command_buffer().add_data('\0'); if (ret_value != XORP_OK) { // This client is sending too much data. Kick it out! // TODO: print more informative message about the // client: E.g. where it came from, etc. XLOG_WARNING("Removing client (input fd = %s output fd = %s " "family = %d): " "data buffer full", input_fd().str().c_str(), output_fd().str().c_str(), cli_node().family()); return (XORP_ERROR); } set_buff_curpos(gl_buff_curpos); return (XORP_OK); } /** * CliClient::command_line_help: * @line: The current command line. * @word_end: The cursor position. * @remove_last_input_char: If true, then remove the last input character. * * Print the help for the same-line command. **/ void CliClient::command_line_help(const string& line, int word_end, bool remove_last_input_char) { CliCommand *curr_cli_command = _current_cli_command; set command_help_strings; bool is_found = false; if (remove_last_input_char) word_end--; // XXX: exclude the '?' character list::iterator iter; for (iter = curr_cli_command->child_command_list().begin(); iter != curr_cli_command->child_command_list().end(); ++iter) { CliCommand *tmp_cli_command = *iter; if (tmp_cli_command->find_command_help(line.c_str(), word_end, command_help_strings)) is_found = true; } if (is_found) { cli_print("\nPossible completions:\n"); set::const_iterator iter; for (iter = command_help_strings.begin(); iter != command_help_strings.end(); ++iter) { cli_print(*iter); } } else { string token_line = string(line, 0, word_end); token_line = strip_empty_spaces(token_line); cli_print(c_format("\nsyntax error, command \"%s\" is not recognized.\n", token_line.c_str())); } gl_redisplay_line(gl()); if (remove_last_input_char) { // XXX: Move the cursor over the '?' gl_place_cursor(gl(), gl_get_buff_curpos(gl()) - 1); cli_print(" \b"); // XXX: A hack to delete the '?' } } bool CliClient::is_multi_command_prefix(const string& command_line) { return (_current_cli_command->is_multi_command_prefix(command_line)); } int CliClient::process_command(const string& command_line) { string token, token_line; CliCommand *parent_cli_command = current_cli_command(); CliCommand *child_cli_command = NULL; int syntax_error_offset_next = current_cli_prompt().size(); int i, old_len, new_len; vector command_global_name; bool found_type_match_cb = false; token_line = command_line; new_len = token_line.size(); old_len = new_len; if (parent_cli_command != NULL) command_global_name = parent_cli_command->global_name(); for (token = pop_token(token_line); ! token.empty(); token = pop_token(token_line)) { if (token != "|") { child_cli_command = parent_cli_command->command_find(token); new_len = token_line.size(); syntax_error_offset_next += old_len - new_len; old_len = new_len; if (child_cli_command != NULL && (! child_cli_command->is_command_argument())) { parent_cli_command = child_cli_command; // Add the token to the command found_type_match_cb |= child_cli_command->has_type_match_cb(); if (! found_type_match_cb) command_global_name = child_cli_command->global_name(); else command_global_name.push_back(copy_token(token)); continue; } if (parent_cli_command->has_cli_process_callback()) { // The parent command has processing function, so the rest // of the tokens could be arguments for that function child_cli_command = parent_cli_command; } } // Put-back the token and process the arguments after the loop token_line = copy_token(token) + token_line; break; } if (parent_cli_command->has_cli_process_callback()) { // Process the rest of the tokens as arguments for this function vector args_vector; bool is_process_func_arguments = true; bool is_pipe_command_arguments = false; bool pipe_command_empty = false; // true if empty pipe command string pipe_command_name = ""; list pipe_command_args_list; for (token = pop_token(token_line); ! token.empty(); token = pop_token(token_line)) { if (token == "|") { if ((! parent_cli_command->can_pipe()) || (parent_cli_command->cli_command_pipe() == NULL)) { // We cannot use pipe with this command goto print_syntax_error_label; } // Start of a pipe command is_process_func_arguments = false; is_pipe_command_arguments = false; pipe_command_empty = true; if (pipe_command_name.size()) { // Add the previous pipe command add_pipe(pipe_command_name, pipe_command_args_list); pipe_command_name = ""; pipe_command_args_list.clear(); } continue; } if (is_process_func_arguments) { // Arguments for the processing command args_vector.push_back(token); continue; } if (! is_pipe_command_arguments) { // The pipe command name is_pipe_command_arguments = true; pipe_command_empty = false; pipe_command_name = token; continue; } // The pipe command arguments pipe_command_args_list.push_back(token); } if (pipe_command_name.size()) { // Add the last pipe command add_pipe(pipe_command_name, pipe_command_args_list); pipe_command_name = ""; pipe_command_args_list.clear(); } if (pipe_command_empty) { // Empty pipe command parent_cli_command = parent_cli_command->cli_command_pipe(); goto print_syntax_error_label; } // Run the command function { int ret_value; string final_string = ""; bool is_error = false; string error_msg; if (parent_cli_command->default_nomore_mode()) set_nomore_mode(true); list::iterator iter; for (iter = _pipe_list.begin(); iter != _pipe_list.end(); ++iter) { CliPipe *cli_pipe = *iter; if (cli_pipe->start_func(final_string, error_msg) != XORP_OK) { is_error = true; break; } } if (is_error) { // Stop the started pipes string error_msg2; while (iter != _pipe_list.begin()) { --iter; CliPipe *cli_pipe = *iter; cli_pipe->stop_func(error_msg2); } if (is_interactive()) set_nomore_mode(false); cli_print(c_format("ERROR: %s\n", error_msg.c_str())); return (XORP_ERROR); } if (final_string.size()) { bool old_pipe_mode = is_pipe_mode(); set_pipe_mode(false); cli_print(final_string); set_pipe_mode(old_pipe_mode); } final_string = ""; _executed_cli_command = parent_cli_command; _executed_cli_command_name = command_global_name; _executed_cli_command_args = args_vector; ret_value = parent_cli_command->_cli_process_callback->dispatch( parent_cli_command->server_name(), cli_session_term_name(), cli_session_session_id(), _executed_cli_command_name, _executed_cli_command_args); return (ret_value); } } // The rest of the tokens (if any) cannot be processed as arguments // Test if we can "cd" to this function. token = pop_token(token_line); if (token.empty()) { if (parent_cli_command->allow_cd()) { // Set the current command level set_current_cli_command(parent_cli_command); return (XORP_OK); } // Error. Will print the list of child commands (done below). } print_syntax_error_label: // // If there are more tokens, the first one has to be a sub-command. // However, there wasn't a match in our search, hence it has // to be an error. // Further, there was no processing function, hence those cannot // be command arguments. // syntax_error_offset_next -= token.size(); // Unknown command if (parent_cli_command == current_cli_command()) { cli_print(c_format("%*s%s\n", syntax_error_offset_next, " ", "^")); cli_print("unknown command.\n"); return (XORP_ERROR); } if (token.empty()) syntax_error_offset_next++; // Command that cannot be executed if (parent_cli_command->child_command_list().empty()) { string cmd_name = token_vector2line(parent_cli_command->global_name()); if (token.empty()) { cli_print(c_format("syntax error, command \"%s\" is not executable.\n", cmd_name.c_str())); } else { cli_print(c_format("syntax error, command \"%s\" cannot be executed with argument \"%s\".\n", cmd_name.c_str(), token.c_str())); } return (XORP_ERROR); } // Command with invalid sub-parts cli_print(c_format("%*s%s\n", syntax_error_offset_next, " ", "^")); cli_print("syntax error, expecting"); if (parent_cli_command->child_command_list().size() > 4) { // TODO: replace the hard-coded "4" with a #define or a parameter. cli_print(" .\n"); return (XORP_ERROR); } list::iterator iter; i = 0; for (iter = parent_cli_command->child_command_list().begin(); iter != parent_cli_command->child_command_list().end(); ++iter) { child_cli_command = *iter; if (i > 0) { cli_print(","); if ((size_t)(i + 1) == parent_cli_command->child_command_list().size()) cli_print(" or"); } i++; cli_print(c_format(" `%s'", child_cli_command->name().c_str())); } cli_print(".\n"); return (XORP_ERROR); } void CliClient::interrupt_command() { if (! is_waiting_for_data()) goto cleanup_label; if ((_executed_cli_command == NULL) || (! _executed_cli_command->has_cli_interrupt_callback())) { goto cleanup_label; } _executed_cli_command->_cli_interrupt_callback->dispatch( _executed_cli_command->server_name(), cli_session_term_name(), cli_session_session_id(), _executed_cli_command_name, _executed_cli_command_args); cleanup_label: // Reset everything about the command _executed_cli_command = NULL; _executed_cli_command_name.clear(); _executed_cli_command_args.clear(); delete_pipe_all(); set_pipe_mode(false); set_hold_mode(false); set_page_mode(false); reset_page_buffer(); set_page_buffer_mode(false); if (is_interactive()) set_nomore_mode(false); if (is_waiting_for_data()) { cli_print("\n"); // XXX: new line cli_print("Command interrupted!\n"); } // // Ignore current line, reset buffer, line, cursor, prompt // if (current_cli_command() != NULL) { set_current_cli_prompt(current_cli_command()->cd_prompt()); } cli_print("\n"); // XXX: new line gl_redisplay_line(gl()); gl_reset_line(gl()); set_buff_curpos(0); command_buffer().reset(); cli_flush(); set_prompt_flushed(false); set_is_waiting_for_data(false); } int CliClient::command_completion_func(WordCompletion *cpl, void *data, const char *line, int word_end) { int ret_value = 1; CliClient *cli_client = reinterpret_cast(data); CliCommand *curr_cli_command = cli_client->_current_cli_command; list cli_command_match_list; set type_names, no_type_names; if (cpl == NULL) return (1); list::iterator iter; for (iter = curr_cli_command->child_command_list().begin(); iter != curr_cli_command->child_command_list().end(); ++iter) { CliCommand *tmp_cli_command = *iter; if (! tmp_cli_command->has_cli_completion_func()) continue; if (tmp_cli_command->_cli_completion_func(tmp_cli_command, cpl, NULL, line, word_end, cli_command_match_list)) { ret_value = 0; // XXX: there was a completion } } if (curr_cli_command->can_pipe() && (curr_cli_command->cli_command_pipe() != NULL)) { // Add the pipe completions if (curr_cli_command->_cli_completion_func( curr_cli_command->cli_command_pipe(), cpl, NULL, line, word_end, cli_command_match_list)) { ret_value = 0; } } // // Separate the type-match commands from the rest // for (iter = cli_command_match_list.begin(); iter != cli_command_match_list.end(); ++iter) { CliCommand *tmp_cli_command = *iter; if (tmp_cli_command->has_type_match_cb()) type_names.insert(tmp_cli_command->name()); else no_type_names.insert(tmp_cli_command->name()); } if (no_type_names.size() > 1) { // Prepare and print the initial message(s) string token_line = string(line, word_end); string token; // Get the lastest token do { string next_token = pop_token(token_line); if (next_token.empty()) break; token = next_token; } while (true); cli_client->cli_print(c_format("\n`%s' is ambiguous.", token.c_str())); cli_client->cli_print("\nPossible completions:"); } else { if (type_names.size() > 0) { cli_client->command_line_help(line, word_end, false); } } if (ret_value != 0) { cpl_record_error(cpl, "Not a XORP command!"); } return (ret_value); } xorp/cli/libtecla/0000775000076400007640000000000011540224220014176 5ustar greearbgreearbxorp/cli/libtecla/CHANGES0000664000076400007640000021224111421137511015177 0ustar greearbgreearbIn the following log, modification dates are listed using the European convention in which the day comes before the month (ie. DD/MM/YYYY). The most recent modifications are listed first. 10/12/2001 mcs@astro.caltech.edu getline.c If the TIOCGWINSZ ioctl doesn't work, as is the case when running in an emacs shell, leave the size unchanged, rather than returning a fatal error. 07/12/2001 mcs@astro.caltech.edu configure.in configure Now that the configure version of CFLAGS is included in the makefile, I noticed that the optimization flags -g and -O2 had been added. It turns out that if CFLAGS isn't already set, the autoconf AC_PROG_CC macro initializes it with these two optimization flags. Since this would break backwards compatibility in embedded distributions that already use the OPT= makefile argument, and because turning debugging on needlessly bloats the library, I now make sure that CFLAGS is set before calling this macro. 07/12/2001 mcs@astro.caltech.edu enhance.c Use argv[0] in error reports instead of using a hardcoded macro. 07/12/2001 mcs@astro.caltech.edu getline.c The cut buffer wasn't being cleared after being used as a work buffer by gl_load_history(). 06/12/2001 mcs@astro.caltech.edu configure.in configure I removed my now redundant definition of SUN_TPUTS from CFLAGS. I also added "-I/usr/include" to CFLAGS under Solaris to prevent gcc from seeing conflicting versions of system header files in /usr/local/include. 06/12/2001 Markus Gyger (logged here by mcs) Lots of files. Lots of corrections to misspellings and typos in the comments. getline.c Markus reverted a supposed fix that I added a day or two ago. I had incorrectly thought that in Solaris 8, Sun had finally brought their declaration of the callback function of tputs() into line with other systems, but it turned out that gcc was pulling in a GNU version of term.h from /usr/local/include, and this was what confused me. 05/12/2001 mcs@astro.caltech.edu Makefile.in I added @CFLAGS@ to the CFLAGS assignment, so that if CFLAGS is set as an environment variable when configure is run, the corresponding make variable includes its values in the output makefile. 05/12/2001 mcs@astro.caltech.edu getline.c libtecla.h libtecla.map man3/gl_get_line.3 man3/gl_last_signal.3 I added a function that programs can use to find out which signal caused gl_get_line() to return EINTR. 05/12/2001 mcs@astro.caltech.edu getline.c When the newline action was triggered by a printable character, it failed to display that character. It now does. Also, extra control codes that I had added, to clear to the end of the display after the carriage return, but before displaying the prompt, were confusing expect scripts, so I have removed them. This step is now done instead in gl_redisplay() after displaying the full input line. 05/12/2001 mcs@astro.caltech.edu getline.c man3/gl_get_line.3 A user convinced me that continuing to invoke meta keybindings for meta characters that are printable is a bad idea, as is allowing users to ask to have setlocale() called behind the application's back. I have thus changed this. The setlocale configuration option has gone, and gl_get_line() is now completely 8-bit clean, by default. This means that if a meta character is printable, it is treated as a literal character, rather than a potential M-c binding. Meta bindings can still be invoked via their Esc-c equivalents, and indeed most terminal emulators either output such escape pairs by default when the meta character is pressed, or can be configured to do so. I have documented how to configure xterm to do this, in the man page. 03/12/2001 mcs@astro.caltech.edu getline.c man3/gl_get_line.3 gl_get_line() by default now prints any 8-bit printable characters that don't match keybindings. Previously characters > 127 were only printed if preceded by the literal-next action. Alternatively, by placing the command literal_if_printable in the tecla configuration file, all printable characters are treated as literal characters, even if they are bound to action functions. For international users of programs written by programmers that weren't aware of the need to call setlocale() to support alternate character sets, the configuration file can now also contain the single-word command "setlocale", which tells gl_get_line() to remedy this. 27/11/2001 mcs@astro.caltech.edu demo.c demo2.c enhance man3/gl_get_line.3 All demos and programs now call setlocale(LC_CTYPE,""). This makes them support character sets of different locales, where specified with the LC_CTYPE, LC_ALL, or LANG environment variables. I also added this to the demo in the man page, and documented its effect. 27/11/2001 mcs@astro.caltech.edu getline.c When displaying unsigned characters with values over 127 literally, previously it was assumed that they would all be displayable. Now isprint() is consulted, and if it says that a character isn't printable, the character code is displayed in octal like \307. In non-C locales, some characters with values > 127 are displayable, and isprint() tells gl_get_line() which are and which aren't. 27/11/2001 mcs@astro.caltech.edu getline.c pathutil.c history.c enhance.c demo2.c All arguments of the ctype.h character class functions are now cast to (int)(unsigned char). Previously they were cast to (int), which doesn't correctly conform to the requirements of the C standard, and could cause problems for characters with values > 127 on systems with signed char's. 26/11/2001 mcs@astro.caltech.edu man3/enhance.3 man3/libtecla.3 I started writing a man page for the enhance program. 26/11/2001 mcs@astro.caltech.edu Makefile.in Makefile.rules INSTALL It is now possible to specify whether the demos and other programs are to be built, by overriding the default values of the DEMOS, PROGRAMS and PROGRAMS_R variables. I have also documented the BINDIR variable and the install_bin makefile target. 22/11/2001 mcs@astro.caltech.edu getline.c libtecla.h libtecla.map man3/gl_get_line.3 man3/gl_ignore_signal.3 man3/gl_trap_signal.3 Signal handling has now been modified to be customizable. Signals that are trapped by default can be removed from the list of trapped signals, and signals that aren't currently trapped, can be added to the list. Applications can also specify the signal and terminal environments in which an application's signal handler is invoked, and what gl_get_line() does after the signal handler returns. 13/11/2001 mcs@astro.caltech.edu getline.c man3/gl_get_line.3 Added half-bright, reverse-video and blinking text to the available prompt formatting options. getline.c Removed ^O from the default VT100 sgr0 capability string. Apparently it can cause problems with some terminal emulators, and we don't need it, since it turns off the alternative character set mode, which we don't use. getline.c gl_tigetstr() and gl_tgetstr() didn't guard against the error returns of tigetstr() and tgetstr() respectively. They now do. 11/11/2001 mcs@astro.caltech.edu getline.c libtecla.h libtecla.map man3/gl_get_line.3 man3/gl_prompt_style.3 Although the default remains to display the prompt string literally, the new gl_prompt_style() function can be used to enable text attribute formatting directives in prompt strings, such as underlining, bold font, and highlighting directives. 09/11/2001 mcs@astro.caltech.edu enhance.c Makefile.rules configure.in configure I added a new program to the distribution that allows one to run most third party programs with the tecla library providing command-line editing. 08/11/2001 mcs@astro.caltech.edu libtecla.h getline.c man3/gl_get_line.3 history.c history.h I added a max_lines argument to gl_show_history() and _glh_show_history(). This can optionally be used to set a limit on the number of history lines displayed. libtecla.h getline.c man3/gl_get_line.3 I added a new function called gl_replace_prompt(). This can be used by gl_get_line() callback functions to request that a new prompt be use when they return. 06/11/2001 mcs@astro.caltech.edu getline.c man3/gl_get_line.3 I implemented, bound and documented the list-history action, used for listing historical lines of the current history group. getline.c man3/gl_get_line.3 man3/gl_echo_mode.3 I wrote functions to specify and query whether subsequent lines will be visible as they are being typed. 28/10/2001 mcs@astro.caltech.edu getline.c man3/gl_get_line.3 For those cases where a terminal provides its own high-level terminal editing facilities, you can now specify an edit-mode argument of 'none'. This disables all tecla key bindings, and by using canonical terminal input mode instead of raw input mode, editing is left up to the terminal driver. 21/10/2001 mcs@astro.caltech.edu libtecla.h getline.c history.c history.h man3/gl_get_line.3 man3/gl_history_info.3 I added the new gl_state_of_history(), gl_range_of_history() and gl_size_of_history() functions for querying information about the history list. history.c While testing the new gl_size_of_history() function, I noticed that when the history buffer wrapped, any location nodes of old lines between the most recent line and the end of the buffer weren't being removed. This could result in bogus entries appearing at the start of the history list. Now fixed. 20/10/2001 mcs@astro.caltech.edu libtecla.h getline.c history.c history.h man3/gl_get_line.3 man3/gl_lookup_history.3 I added a function called gl_lookup_history(), that the application can use to lookup lines in the history list. libtecla.h getline.c history.c history.h man3/gl_get_line.3 gl_show_history() now takes a format string argument to control how the line is displayed, and with what information. It also now provides the option of either displaying all history lines or just those of the current history group. getline.c man3/gl_get_line.3 gl_get_line() only archives lines in the history buffer if the newline action was invoked by a newline or carriage return character. 16/10/2001 mcs@astro.caltech.edu history.c history.h getline.c libtecla.h libtecla.map man3/gl_get_line.3 man3/gl_resize_history.3 man3/gl_limit_history.3 man3/gl_clear_history.3 man3/gl_toggle_history.3 I added a number of miscellaneous history configuration functions. You can now resize or delete the history buffer, limit the number of lines that are allowed in the buffer, clear either all history or just the history of the current history group, and temporarily enable and disable the history mechanism. 13/10/2001 mcs@astro.caltech.edu getline.c tputs_fp is now only declared if using termcap or terminfo. getline.c libtecla.map man3/gl_get_line.3 man3/gl_terminal_size.3 I added a public gl_terminal_size() function for updating and querying the current size of the terminal. update_version configure.in libtecla.h A user noted that on systems where the configure script couldn't be used, it was inconvenient to have the version number macros set by the configure script, so they are now specified in libtecla.h. To reduce the likelihood that the various files where the version number now appears might get out of sync, I have written the update_version script, which changes the version number in all of these files to a given value. 01/10/2001 mcs@astro.caltech.edu getline.c history.c history.h man3/gl_get_line.3 I added a max_lines argument to gl_save_history(), to allow people to optionally place a ceiling on the number of history lines saved. Specifying this as -1 sets the ceiling to infinity. 01/10/2001 mcs@astro.caltech.edu configure.in configure Under digital unix, getline wouldn't compile with _POSIX_C_SOURCE set, due to type definitions needed by select being excluded by this flag. Defining the _OSF_SOURCE macro as well on this system, resolved this. 30/09/2001 mcs@astro.caltech.edu getline.c libtecla.h history.c history.h man3/gl_get_line.3 man3/gl_group_history.3 I implemented history streams. History streams effectively allow multiple history lists to be stored in a single history buffer. Lines in the buffer are tagged with the current stream identification number, and lookups only consider lines that are marked with the current stream identifier. getline.c libtecla.h history.c history.h man3/gl_get_line.3 man3/gl_show_history.3 The new gl_show_history function displays the current history to a given stdio output stream. 29/09/2001 mcs@astro.caltech.edu getline.c Previously new_GetLine() installed a persistent signal handler to be sure to catch the SIGWINCH (terminal size change) signal between calls to gl_get_line(). This had the drawback that if multiple GetLine objects were created, only the first GetLine object used after the signal was received, would see the signal and adapt to the new terminal size. Instead of this, a signal handler for sigwinch is only installed while gl_get_line() is running, and just after installing this handler, gl_get_line() checks for terminal size changes that might have occurred while the signal handler wasn't installed. getline.c Dynamically allocated copies of capability strings looked up in the terminfo or termcap databases are now made, so that calls to setupterm() etc for one GetLine object don't get trashed when another GetLine object calls setupterm() etc. It is now safe to allocate and use multiple GetLine objects, albeit only within a single thread. 28/09/2001 mcs@astro.caltech.edu version.c Makefile.rules I added a function for querying the version number of the library. 26/09/2001 mcs@astro.caltech.edu getline.c man3/gl_get_line.3 I added the new gl_watch_fd() function, which allows applications to register callback functions to be invoked when activity is seen on arbitrary file descriptors while gl_get_line() is awaiting keyboard input from the user. keytab.c If a request is received to delete a non-existent binding, which happens to be an ambiguous prefix of other bindings no complaint is now generated about it being ambiguous. 23/09/2001 mcs@astro.caltech.edu getline.c history.c history.h man3/gl_get_line.3 libtecla.map demo.c I added new public functions for saving and restoring the contents of the history list. The demo program now uses these functions to load and save history in ~/.demo_history. 23/09/2001 mcs@astro.caltech.edu getline.c On trying the demo for the first time on a KDE konsole terminal, I discovered that the default M-O binding to repeat history was hiding the arrow keys, which are M-OA etc. I have removed this binding. The M-o (ie the lower case version of this), is still bound. 18/09/2001 mcs@astro.caltech.edu getline.c man3/gl_get_line.3 libtecla.map Automatic reading of ~/.teclarc is now postponed until the first call to gl_get_line(), to give the application the chance to specify alternative configuration sources with the new function gl_configure_getline(). The latter function allows configuration to be done with a string, a specified application-specific file, and/or a specified user-specific file. I also added a read-init-files action function, for re-reading the configuration files, if any. This is by default bound to ^X^R. This is all documented in gl_get_line.3. 08/09/2001 mcs@astro.caltech.edu getline.c man3/gl_get_line.3 It is now possible to bind actions to key-sequences that start with printable characters. Previously keysequences were required to start with meta or control characters. This is documented in gl_get_line.3. getline.c man3/gl_get_line.3 A customized completion function can now arrange for gl_get_line() to return the current input line whenever a successful completion has been made. This is signalled by setting the last character of the optional continuation suffix to a newline character. This is documented in gl_get_line.3. 05/07/2001 Bug reported by Mike MacFaden, fixed by mcs configure.in There was a bug in the configure script that only revealed itself on systems without termcap but not terminfo (eg. NetBSD). I traced the bug back to a lack of sufficient quoting of multi-line m4 macro arguments in configure.in, and have now fixed this and recreated the configure script. 05/07/2001 Bug reported and patched by Mike MacFaden (patch modified by mcs to match original intentions). getline.c getline.c wouldn't compile when termcap was selected as the terminal information database. setupterm() was being passed a non-existent variable, in place of the term[] argument of gl_control_strings(). Also if gl_change_terminal() is called with term==NULL, "ansi" is now substituted. 02/07/2001 Version 1.3.3 released. 27/06/2001 mcs@astro.caltech.edu getline.c expand.c cplmatch.c Added checks to fprintf() statements that write to the terminal. getline.c Move the cursor to the end of the line before suspending, so that the cursor doesn't get left in the middle of the input line. Makefile.in On systems that don't support shared libraries, the distclean target of make deleted libtecla.h. This has now been fixed. getline.c gl_change_terminal() was being called by gl_change_editor(), with the unwanted side effect that raw terminal modes were stored as those to be restored later, if called by an action function. gl_change_terminal() was being called in this case to re-establish terminal-specific key bindings, so I have just split this part of the function out into a separate function for both gl_change_editor() and gl_change_terminal() to call. 12/06/2001 mcs@astro.caltech.edu getline.c Signal handling has been improved. Many more signals are now trapped, and instead of using a simple flag set by a signal handler, race conditions are avoided by blocking signals during most of the gl_get_line() code, and unblocking them via calls to sigsetjmp(), just before attempting to read each new character from the user. The matching use of siglongjmp() in the signal handlers ensures that signals are reblocked correctly before they are handled. In most cases, signals cause gl_get_line() to restore the terminal modes and signal handlers of the calling application, then resend the signal to the application. In the case of SIGINT, SIGHUP, SIGPIPE, and SIGQUIT, if the process still exists after the signals are resent, gl_get_line() immediately returns with appropriate values assigned to errno. If SIGTSTP, SIGTTIN or SIGTTOU signals are received, the process is suspended. If any other signal is received, and the process continues to exist after the signal is resent to the calling application, line input is resumed after the terminal is put back into raw mode, the gl_get_line() signal handling is restored, and the input line redrawn. man/gl_get_line(3) I added a SIGNAL HANDLING section to the gl_get_line() man page, describing the new signal handling features. 21/05/2001 Version 1.3.2 released. 21/05/2001 mcs@astro.caltech.edu getline.c When vi-replace-char was used to replace the character at the end of the line, it left the cursor one character to its right instead of on top of it. Now rememdied. getline.c When undoing, to properly emulate vi, the cursor is now left at the leftmost of the saved and current cursor positions. getline.c man3/gl_get_line.3 Implemented find-parenthesis (%), delete-to-paren (M-d%), vi-change-to-paren (M-c%), copy-to-paren (M-y%). cplfile.c pcache.c In three places I was comparing the last argument of strncmp() to zero instead of the return value of strncmp(). 20/05/2001 mcs@astro.caltech.edu getline.c man3/gl_get_line.3 Implemented and documented the vi-repeat-change action, bound to the period key. This repeats the last action that modified the input line. 19/05/2001 mcs@astro.caltech.edu man3/gl_get_line.3 I documented the new action functions and bindings provided by Tim Eliseo, plus the ring-bell action and the new "nobeep" configuration option. getline.c I modified gl_change_editor() to remove and reinstate the terminal settings as well as the default bindings, since these have editor-specific differences. I also modified it to not abort if a key-sequence can't be bound for some reason. This allows the new vi-mode and emacs-mode bindings to be used safely. getline.c When the line was re-displayed on receipt of a SIGWINCH signal, the result wasn't visible until the next character was typed, since a call to fflush() was needed. gl_redisplay_line() now calls gl_flush_output() to remedy this. 17/05/2001 mcs@astro.catlech.edu getline.c Under Linux, calling fflush(gl->output_fd) hangs if terminal output has been suspended with ^S. With the tecla library taking responsability for reading the stop and start characters this was a problem, because once hung in fflush(), the keyboard input loop wasn't entered, so the user couldn't type the start character to resume output. To remedy this, I now have the terminal process these characters, rather than the library. 12/05/2001 mcs@astro.caltech.edu getline.c The literal-next action is now implemented as a single function which reads the next character itself. Previously it just set a flag which effected the interpretation of the next character read by the input loop. getline.c Added a ring-bell action function. This is currently unbound to any key by default, but it is used internally, and can be used by users that want to disable any of the default key-bindings. 12/05/2001 Tim Eliseo (logged here by mcs) getline.c Don't reset gl->number until after calling an action function. By looking at whether gl->number is <0 or not, action functions can then tell whether the count that they were passed was explicitly specified by the user, as opposed to being defaulted to 1. getline.c In vi, the position at which input mode is entered acts as a barrier to backward motion for the few backward moving actions that are enabled in input mode. Tim added this barrier to getline. getline.c In gl_get_line() after reading an input line, or having the read aborted by a signal, the sig_atomic_t gl_pending_signal was being compared to zero instead of -1 to see if no signals had been received. gl_get_line() will thus have been calling raise(-1), which luckily didn't seem to do anything. Tim also arranged for errno to be set to EINTR when a signal aborts gl_get_line(). getline.c The test in gl_add_char_to_line() for detecting when overwriting a character with a wider character, had a < where it needed a >. Overwriting with a wider character thus overwrote trailing characters. Tim also removed a redundant copy of the character into the line buffer. getline.c gl_cursor_left() and gl->cursor_right() were executing a lot of redundant code, when the existing call to the recently added gl_place_cursor() function, does all that is necessary. getline.c Remove redundant code from backward_kill_line() by re-implimenting in terms of gl_place_cursor() and gl_delete_chars(). getline.c gl_forward_delete_char() now records characters in cut buffer when in vi command mode. getline.c In vi mode gl_backward_delete_char() now only deletes up to the point at which input mode was entered. Also gl_delete_chars() restores from the undo buffer when deleting in vi insert mode. getline.c Added action functions, vi-delete-goto-column, vi-change-to-bol, vi-change-line, emacs-mode, vi-mode, vi-forward-change-find, vi-backward-change-find, vi-forward-change-to, vi-backward-change-to, vi-change-goto-col, forward-delete-find, backward-delete-find, forward-delete-to, backward-delete-to, delete-refind, delete-invert-refind, forward-copy-find, backward-copy-find, forward-copy-to, backward-copy-to copy-goto-column, copy-rest-of-line, copy-to-bol, copy-line, history-re-search-forward, history-re-search-backward. 06/05/2001 Version 1.3.1 released. 03/05/2001 mcs@astro.caltech.edu configure.in Old versions of GNU ld don't accept version scripts. Under Linux I thus added a test to try out ld with the --version-script argument to see if it works. If not, version scripts aren't used. configure.in My test for versions of Solaris earlier than 7 failed when confronted by a three figure version number (2.5.1). Fixed. 30/04/2001 mcs@astro.caltech.edu getline.c In vi mode, history-search-backward and history-search-forward weren't doing anything when invoked at the start of an empty line, whereas they should have acted like up-history and down-history. Makefile.in Makefile.rules When shared libraries are being created, the build procedure now arranges for any alternate library links to be created as well, before linking the demos. Without this the demos always linked to the static libraries (which was perfectly ok, but wasn't a good example). Makefile.in Makefile.rules On systems on which shared libraries were being created, if there were no alternate list of names, make would abort due to a Bourne shell 'for' statement that didn't have any arguments. Currently there are no systems who's shared library configurations would trigger this problem. Makefile.rules The demos now relink to take account of changes to the library. configure.in configure When determining whether the reentrant version of the library should be compiled by default, the configure script now attempts to compile a dummy program that includes all of the appropriate system headers and defines _POSIX_C_SOURCE. This should now be a robust test on systems which use C macros to alias these function names to other internal functions. configure.in Under Solaris 2.6 and earlier, the curses library is in /usr/ccs/lib. Gcc wasn't finding this. In addition to remedying this, I had to remove "-z text" from LINK_SHARED under Solaris to get it to successfully compile the shared library against the static curses library. configure.in Under Linux the -soname directive was being used incorrectly, citing the fully qualified name of the library instead of its major version alias. This will unfortunately mean that binaries linked with the 1.2.3 and 1.2.4 versions of the shared library won't use later versions of the library unless relinked. 30/04/2001 mcs@astro.caltech.edu getline.c In gl_get_input_line(), don't redundantly copy the start_line if start_line == gl->line. 30/04/2001 Version 1.3.0 released. 28/04/2001 mcs@astro.caltech.edu configure.in I removed the --no-undefined directive from the Linux LINK_SHARED command. After recent patches to our RedHat 7.0 systems ld started reporting some internal symbols of libc as being undefined. Using nm on libc indicated that the offending symbols are indeed defined, albeit as "common" symbols, so there appears to be a bug in RedHat's ld. Removing this flag allows the tecla shared library to compile, and programs appear to function fine. man3/gl_get_line.3 The default key-sequence used to invoke the read-from-file action was incorrectly cited as ^Xi instead of ^X^F. 26/04/2001 mcs@astro.caltech.edu getline.c man3/gl_get_line.3 A new vi-style editing mode was added. This involved adding many new action functions, adding support for specifying editing modes in users' ~/.teclarc files, writing a higher level cursor motion function to support the different line-end bounds required in vi command mode, and a few small changes to support the fact that vi has two modes, input mode and command mode with different bindings. When vi editing mode is enabled, any binding that starts with an escape or a meta character, is interpreted as a command-mode binding, and switches the library to vi command mode if not already in that mode. Once in command mode the first character of all keysequences entered until input mode is re-enabled, are quietly coerced to meta characters before being looked up in the key-binding table. So, for example, in the key-binding table, the standard vi command-mode 'w' key, which moves the cursor one word to the right, is represented by M-w. This emulates vi's dual sets of bindings in a natural way without needing large changes to the library, or new binding syntaxes. Since cursor keys normally emit keysequences which start with escape, it also does something sensible when a cursor key is pressed during input mode (unlike true vi, which gets upset). I also added a ^Xg binding for the new list-glob action to both the emacs and vi key-binding tables. This lists the files that match the wild-card expression that precedes it on the command line. The function that reads in ~/.teclarc used to tell new_GetLine() to abort if it encountered anything that it didn't understand in this file. It now just reports an error and continues onto the next line. Makefile.in: When passing LIBS=$(LIBS) to recursive invokations of make, quotes weren't included around the $(LIBS) part. This would cause problems if LIBS ever contained more than one word (with the supplied configure script this doesn't happen currently). I added these quotes. expand.c man3/ef_expand_file.3: I wrote a new public function called ef_list_expansions(), to list the matching filenames returned by ef_expand_file(). I also fixed the example in the man page, which cited exp->file instead of exp->files, and changed the dangerous name 'exp' with 'expn'. keytab.c: Key-binding tables start with 100 elements, and are supposedly incremented in size by 100 elements whenever the a table runs out of space. The realloc arguments to do this were wrong. This would have caused problems if anybody added a lot of personal bindings in their ~/.teclarc file. I only noticed it because the number of key bindings needed by the new vi mode exceeded this number. libtecla.map ef_expand_file() is now reported as having been added in the upcoming 1.3.0 release. 25/03/2001 Markus Gyger (logged here by mcs) Makefile.in: Make symbolic links to alternative shared library names relative instead of absolute. Makefile.rules: The HP-UX libtecla.map.opt file should be made in the compilation directory, to allow the source code directory to be on a readonly filesystem. cplmatch.c demo2.c history.c pcache.c To allow the library to be compiled with a C++ compiler, without generating warnings, a few casts were added where void* return values were being assigned directly to none void* pointer variables. 25/03/2001 mcs@astro.caltech.edu libtecla.map: Added comment header to explain the purpose of the file. Also added cpl_init_FileArgs to the list of exported symbols. This symbol is deprecated, and no longer documented, but for backwards compatibility, it should still be exported. configure: I had forgotten to run autoconf before releasing version 1.2.4, so I have just belatedly done so. This enables Markus' changes to "configure.in" documented previously, (see 17/03/2001). 20/03/2001 John Levon (logged here by mcs) libtecla.h A couple of the function prototypes in libtecla.h have (FILE *) argument declarations, which means that stdio.h needs to be included. The header file should be self contained, so libtecla.h now includes stdio.h. 18/03/2001 Version 1.2.4 released. README html/index.html configure.in Incremented minor version from 3 to 4. 18/03/2001 mcs@astro.caltech.edu getline.c The fix for the end-of-line problem that I released a couple of weeks ago, only worked for the first line, because I was handling this case when the cursor position was equal to the last column, rather than when the cursor position modulo ncolumn was zero. Makefile.in Makefile.rules The demos are now made by default, their rules now being int Makefile.rules instead of Makefile.in. INSTALL I documented how to compile the library in a different directory than the distribution directory. I also documented features designed to facilitate configuring and building the library as part of another package. 17/03/2001 Markus Gyger (logged here by mcs) getline.c Until now cursor motions were done one at a time. Markus has added code to make use the of the terminfo capability that moves the cursor by more than one position at a time. This greatly improves performance when editing near the start of long lines. getline.c To further improve performance, Markus switched from writing one character at a time to the terminal, using the write() system call, to using C buffered output streams. The output buffer is only flushed when necessary. Makefile.rules Makefile.in configure.in Added support for compiling for different architectures in different directories. Simply create another directory and run the configure script located in the original directory. Makefile.in configure.in libtecla.map Under Solaris, Linux and HP-UX, symbols that are to be exported by tecla shared libraries are explicitly specified via symbol map files. Only publicly documented functions are thus visible to applications. configure.in When linking shared libraries under Solaris SPARC, registers that are reserved for applications are marked as off limits to the library, using -xregs=no%appl when compiling with Sun cc, or -mno-app-regs when compiling with gcc. Also removed -z redlocsym for Solaris, which caused problems under some releases of ld. homedir.c (after minor changes by mcs) Under ksh, ~+ expands to the current value of the ksh PWD environment variable, which contains the path of the current working directory, including any symbolic links that were traversed to get there. The special username "+" is now treated equally by tecla, except that it substitutes the return value of getcwd() if PWD either isn't set, or if it points at a different directory than that reported by getcwd(). 08/03/2001 Version 1.2.3 released. 08/03/2001 mcs@astro.caltech.edu getline.c On compiling the library under HP-UX for the first time I encountered and fixed a couple of bugs: 1. On all systems except Solaris, the callback function required by tputs() takes an int argument for the character that is to be printed. Under Solaris it takes a char argument. The callback function was passing this argument, regardless of type, to write(), which wrote the first byte of the argument. This was fine under Solaris and under little-endian systems, because the first byte contained the character to be written, but on big-endian systems, it always wrote the zero byte at the other end of the word. As a result, no control characters were being written to the terminal. 2. While attempting to start a newline after the user hit enter, the library was outputting the control sequence for moving the cursor down, instead of the newline character. On many systems the control sequence for moving the cursor down happends to be a newline character, but under HP-UX it isn't. The result was that no new line was being started under HP-UX. 04/03/2001 mcs@astro.caltech.edu configure.in Makefile.in Makefile.stub configure config.guess config.sub Makefile.rules install-sh PORTING README INSTALL Configuration and compilation of the library is now performed with the help of an autoconf configure script. In addition to relieving the user of the need to edit the Makefile, this also allows automatic compilation of the reentrant version of the library on platforms that can handle it, along with the creation of shared libraries where configured. On systems that aren't known to the configure script, just the static tecla library is compiled. This is currently the case on all systems except Linux, Solaris and HP-UX. In the hope that installers will provide specific conigurations for other systems, the configure.in script is heavily commented, and instructions on how to use are included in a new PORTING file. 24/02/2001 Version 1.2b released. 22/02/2001 mcs@astro.caltech.edu getline.c It turns out that most terminals, but not all, on writing a character in the rightmost column, don't wrap the cursor onto the next line until the next character is output. This library wasn't aware of this and thus if one tried to reposition the cursor from the last column, gl_get_line() thought that it was moving relative to a point on the next line, and thus moved the cursor up a line. The fix was to write one extra character when in the last column to force the cursor onto the next line, then backup the cursor to the start of the new line. getline.c On terminal initialization, the dynamic LINES and COLUMNS environment variables were ignored unless terminfo/termcap didn't return sensible dimensions. In practice, when present they should override the static versions in the terminfo/termcap databases. This is the new behavior. In reality this probably won't have caused many problems, because a SIGWINCH signal which informs of terminal size changes is sent when the terminal is opened, so the dimensions established during initialization quickly get updated on most systems. 18/02/2001 Version 1.2a released. 18/02/2001 mcs@astro.caltech.edu getline.c Three months ago I moved the point at which termios.h was included in getline.c. Unfortunately, I didn't notice that this moved it to after the test for TIOCGWINSZ being defined. This resulted in SIGWINCH signals not being trapped for, and thus terminal size changes went unnoticed. I have now moved the test to after the inclusion of termios.h. 12/02/2001 Markus Gyger (described here by mcs) man3/pca_lookup_file.3 man3/gl_get_line.3 man3/ef_expand_file.3 man3/cpl_complete_word.3 In the 1.2 release of the library, all functions in the library were given man pages. Most of these simply include one of the above 4 man pages, which describe the functions while describing the modules that they are in. Markus added all of these function names to the lists in the "NAME" headers of the respective man pages. Previously only the primary function of each module was named there. 11/02/2001 mcs@astro.caltech.edu getline.c On entering a line that wrapped over two or more terminal, if the user pressed enter when the cursor wasn't on the last of the wrapped lines, the text of the wrapped lines that followed it got mixed up with the next line written by the application, or the next input line. Somehow this slipped through the cracks and wasn't noticed until now. Anyway, it is fixed now. 09/02/2001 Version 1.2 released. 04/02/2001 mcs@astro.caltech.edu pcache.c libtecla.h With all filesystems local, demo2 was very fast to start up, but on a Sun system with one of the target directories being on a remote nfs mounted filesystem, the startup time was many seconds. This was due to the executable selection callback being applied to all files in the path at startup. To avoid this, all files are now included in the cache, and the application specified file-selection callback is only called on files as they are matched. Whether the callback rejected or accepted them is then cached so that the next time an already checked file is looked at, the callback doesn't have to be called. As a result, startup is now fast on all systems, and since usually there are only a few matching file completions at a time, the delay during completion is also usually small. The only exception is if the user tries to complete an empty string, at which point all files have to be checked. Having done this once, however, doing it again is fast. man3/pca_lookup_file.3 I added a man page documenting the new PathCache module. man3/.3 I have added man pages for all of the functions in each of the modules. These 1-line pages use the .so directive to redirect nroff to the man page of the parent module. man Makefile update_html I renamed man to man3 to make it easier to test man page rediction, and updated Makefile and update_html accordingly. I also instructed update_html to ignore 1-line man pages when making html equivalents of the man pages. cplmatch.c In cpl_list_completions() the size_t return value of strlen() was being used as the length argument of a "%*s" printf directive. This ought to be an int, so the return value of strlen() is now cast to int. This would have caused problems on architectures where the size of a size_t is not equal to the size of an int. 02/02/2001 mcs@astro.caltech.edu getline.c Under UNIX, certain terminal bindings are set using the stty command. This, for example, specifies which control key generates a user-interrupt (usually ^C or ^Y). What I hadn't realized was that ASCII NUL is used as the way to specify that one of these bindings is unset. I have now modified the code to skip unset bindings, leaving the corresponding action bound to the built-in default, or a user provided binding. 28/01/2001 mcs@astro.caltech.edu pcache.c libtecla.h A new module was added which supports searching for files in any colon separated list of directories, such as the unix execution PATH environment variable. Files in these directories, after being individually okayed for inclusion via an application provided callback, are cached in a PathCache object. You can then look up the full pathname of a given filename, or you can use the provided completion callback to list possible completions in the path-list. The contents of relative directories, such as ".", obviously can't be cached, so these directories are read on the fly during lookups and completions. The obvious application of this facility is to provide Tab-completion of commands, and thus a callback to place executable files in the cache, is provided. demo2.c This new program demonstrates the new PathCache module. It reads and processes lines of input until the word 'exit' is entered, or C-d is pressed. The default tab-completion callback is replaced with one which at the start of a line, looks up completions of commands in the user's execution path, and when invoked in other parts of the line, reverts to normal filename completion. Whenever a new line is entered, it extracts the first word on the line, looks it up in the user's execution path to see if it corresponds to a known command file, and if so, displays the full pathname of the file, along with the remaining arguments. cplfile.c I added an optional pair of callback function/data members to the new cpl_file_completions() configuration structure. Where provided, this callback is asked on a file-by-file basis, which files should be included in the list of file completions. For example, a callback is provided for listing only completions of executable files. cplmatch.c When listing completions, the length of the type suffix of each completion wasn't being taken into account correctly when computing the column widths. Thus the listing appeared ragged sometimes. This is now fixed. pathutil.c I added a function for prepending a string to a path, and another for testing whether a pathname referred to an executable file. 28/01/2001 mcs@astro.caltech.edu libtecla.h cplmatch.c man/cpl_complete_word.3 The use of a publically defined structure to configure the cpl_file_completions() callback was flawed, so a new approach has been designed, and the old method, albeit still supported, is no longer documented in the man pages. The definition of the CplFileArgs structure in libtecla.h is now accompanied by comments warning people not to modify it, since modifications could break applications linked to shared versions of the tecla library. The new method involves an opaque CplFileConf object, instances of which are returned by a provided constructor function, configured with provided accessor functions, and when no longer needed, deleted with a provided destructor function. This is documented in the cpl_complete_word man page. The cpl_file_completions() callback distinguishes what type of configuration structure it has been sent by virtue of a code placed at the beginning of the CplFileConf argument by its constructor. 04/01/2001 mcs@astro.caltech.edu (Release of version 1.1j) getline.c I added upper-case bindings for the default meta-letter keysequences such as M-b. They thus continue to work when the user has caps-lock on. Makefile I re-implemented the "install" target in terms of new install_lib, install_inc and install_man targets. When distributing the library with other packages, these new targets allows for finer grained control of the installation process. 30/12/2000 mcs@astro.caltech.edu getline.c man/gl_get_line.3 I realized that the recall-history action that I implemented wasn't what Markus had asked me for. What he actually wanted was for down-history to continue going forwards through a previous history recall session if no history recall session had been started while entering the current line. I have thus removed the recall-history action and modified the down-history action function accordingly. 24/12/2000 mcs@astro.caltech.edu getline.c I modified gl_get_line() to allow the previously returned line to be passed in the start_line argument. getline.c man/gl_get_line.3 I added a recall-history action function, bound to M^P. This recalls the last recalled history line, regardless of whether it was from the current or previous line. 13/12/2000 mcs@astro.caltech.edu (Release of version 1.1i) getline.c history.h history.c man/gl_get_line.3 I implemented the equivalent of the ksh Operate action. I have named the tecla equivalent "repeat-history". This causes the line that is to be edited to returned, and arranges for the next most recent history line to be preloaded on the next call to gl_get_line(). Repeated invocations of this action thus result in successive history lines being repeated - hence the name. Implementing the ksh Operate action was suggested by Markus Gyger. In ksh it is bound to ^O, but since ^O is traditionally bound by the default terminal settings, to stop-output, I have bound the tecla equivalent to M-o. 01/12/2000 mcs@astro.caltech.edu (Release of version 1.1h) getline.c keytab.c keytab.h man/gl_get_line.3 I added a digit-argument action, to allow repeat counts for actions to be entered. As in both tcsh and readline, this is bound by default to each of M-0, M-1 through to M-9, the number being appended to the current repeat count. Once one of these has been pressed, the subsequent digits of the repeat count can be typed with or without the meta key pressed. It is also possible to bind digit-argument to other keys, with or without a numeric final keystroke. See man page for details. getline.c man/gl_get_line.3 Markus noted that my choice of M-< for the default binding of read-from-file, could be confusing, since readline binds this to beginning-of-history. I have thus rebound it to ^X^F (ie. like find-file in emacs). getline.c history.c history.h man/gl_get_line.3 I have now implemented equivalents of the readline beginning-of-history and end-of-history actions. These are bound to M-< and M-> respectively. history.c history.h I Moved the definition of the GlHistory type, and its subordinate types from history.h to history.c. There is no good reason for any other module to have access to the innards of this structure. 27/11/2000 mcs@astro.caltech.edu (Release of version 1.1g) getline.c man/gl_get_line.3 I added a "read-from-file" action function and bound it by default to M-<. This causes gl_get_line() to temporarily return input from the file who's name precedes the cursor. 26/11/2000 mcs@astro.caltech.edu getline.c keytab.c keytab.h man/gl_get_line.3 I have reworked some of the keybinding code again. Now, within key binding strings, in addition to the previously existing notation, you can now use M-a to denote meta-a, and C-a to denote control-a. For example, a key binding which triggers when the user presses the meta key, the control key and the letter [ simultaneously, can now be denoted by M-C-[, or M-^[ or \EC-[ or \E^[. I also updated the man page to use M- instead of \E in the list of default bindings, since this looks cleaner. getline.c man/gl_get_line.3 I added a copy-region-as-kill action function and gave it a default binding to M-w. 22/11/2000 mcs@astro.caltech.edu *.c Markus Gyger sent me a copy of a previous version of the library, with const qualifiers added in appropriate places. I have done the same for the latest version. Among other things, this gets rid of the warnings that are generated if one tells the compiler to const qualify literal strings. getline.c getline.h glconf.c I have moved the contents of glconf.c and the declaration of the GetLine structure into getline.c. This is cleaner, since now only functions in getline.c can mess with the innards of GetLine objects. It also clears up some problems with system header inclusion order under Solaris, and also the possibility that this might result in inconsistent system macro definitions, which in turn could cause different declarations of the structure to be seen in different files. hash.c I wrote a wrapper function to go around strcmp(), such that when hash.c is compiled with a C++ compiler, the pointer to the wrapper function is a C++ function pointer. This makes it compatible with comparison function pointer recorded in the hash table. cplmatch.c getline.c libtecla.h Markus noted that the Sun C++ compiler wasn't able to match up the declaration of cpl_complete_word() in libtecla.h, where it is surrounded by a extern "C" {} wrapper, with the definition of this function in cplmatch.c. My suspicion is that the compiler looks not only at the function name, but also at the function arguments to see if two functions match, and that the match_fn() argument, being a fully blown function pointer declaration, got interpetted as that of a C function in one case, and a C++ function in the other, thus preventing a match. To fix this I now define a CplMatchFn typedef in libtecla.h, and use this to declare the match_fn callback. 20/11/2000 (Changes suggested by Markus Gyger to support C++ compilers): expand.c Renamed a variable called "explicit" to "xplicit", to avoid conflicts when compiling with C++ compilers. *.c Added explicit casts when converting from (void *) to other pointer types. This isn't needed in C but it is in C++. getline.c tputs() has a strange declaration under Solaris. I was enabling this declaration when the SPARC feature-test macro was set. Markus changed the test to hinge on the __sun and __SVR4 macros. direader.c glconf.c stringrp.c I had omitted to include string.h in these two files. Markus also suggested some other changes, which are still under discussion. With the just above changes however, the library compiles without complaint using g++. 19/11/2000 mcs@astro.caltech.edu getline.h getline.c keytab.c keytab.h glconf.c man/gl_get_line.3 I added support for backslash escapes (include \e for the keyboard escape key) and literal binary characters to the characters allowed within key sequences of key bindings. getline.h getline.c keytab.c keytab.h glconf.c man/gl_get_line.3 I introduced symbolic names for the arrow keys, and modified the library to use the cursor key sequences reported by terminfo/termcap in addition to the default ANSI ones. Anything bound to the symbolically named arrow keys also gets bound to the default and terminfo/termcap cursor key sequences. Note that under Solaris terminfo/termcap report the properties of hardware X terminals when TERM is xterm instead of the terminal emulator properties, and the cursor keys on these two systems generate different key sequences. This is an example of why extra default sequences are needed. getline.h getline.c keytab.c For some reason I was using \e to represent the escape character. This is supported by gcc, which thus doesn't emit a warning except with the -pedantic flag, but isn't part of standard C. I now use a macro to define escape as \033 in getline.h, and this is now used wherever the escape character is needed. 17/11/2000 mcs@astro.caltech.edu (Release of version 1.1d) getline.c, man/gl_get_line(3), html/gl_get_line.html In tcsh ^D is bound to a function which does different things depending on where the cursor is within the input line. I have implemented its equivalent in the tecla library. When invoked at the end of the line this action function displays possible completions. When invoked on an empty line it causes gl_get_line() to return NULL, thus signalling end of input. When invoked within a line it invokes forward-delete-char, as before. The new action function is called del-char-or-list-or-eof. getline.c, man/gl_get_line(3), html/gl_get_line.html I found that the complete-word and expand-file actions had underscores in their names instead of hyphens. This made them different from all other action functions, so I have changed the underscores to hyphens. homedir.c On SCO UnixWare while getpwuid_r() is available, the associated _SC_GETPW_R_SIZE_MAX macro used by sysconf() to find out how big to make the buffer to pass to this function to cater for any password entry, doesn't exist. I also hadn't catered for the case where sysconf() reports that this limit is indeterminate. I have thus change the code to substitute a default limit of 1024 if either the above macro isn't defined or if sysconf() says that the associated limit is indeterminate. 17/11/2000 mcs@astro.caltech.edu (Release of version 1.1c) getline.c, getline.h, history.c, history.h I have modified the way that the history recall functions operate, to make them better emulate the behavior of tcsh. Previously the history search bindings always searched for the prefix that preceded the cursor, then left the cursor at the same point in the line, so that a following search would search using the same prefix. This isn't how tcsh operates. On finding a matching line, tcsh puts the cursor at the end of the line, but arranges for the followup search to continue with the same prefix, unless the user does any cursor motion or character insertion operations in between, in which case it changes the search prefix to the new set of characters that are before the cursor. There are other complications as well, which I have attempted to emulate. As far as I can tell, the tecla history recall facilities now fully emulate those of tcsh. 16/11/2000 mcs@astro.caltech.edu (Release of version 1.1b) demo.c: One can now quit from the demo by typing exit. keytab.c: The first entry of the table was getting deleted by _kt_clear_bindings() regardless of the source of the binding. This deleted the up-arrow binding. Symptoms noted by gazelle@yin.interaccess.com. getline.h: Depending on which system include files were include before the inclusion of getline.h, SIGWINCH and TIOCGWINSZ might or might not be defined. This resulted in different definitions of the GetLine object in different files, and thus some very strange bugs! I have now added #includes for the necessary system header files in getline.h itself. The symptom was that on creating a ~/.teclarc file, the demo program complained of a NULL argument to kt_set_keybinding() for the first line of the file. 15/11/2000 mcs@astro.caltech.edu (Release of version 1.1a) demo.c: I had neglected to check the return value of new_GetLine() in the demo program. Oops. getline.c libtecla.h: I wrote gl_change_terminal(). This allows one to change to a different terminal or I/O stream, by specifying the stdio streams to use for input and output, along with the type of terminal that they are connected to. getline.c libtecla.h: Renamed GetLine::isterm to GetLine::is_term. Standard C reserves names that start with "is" followed by alphanumeric characters, so this avoids potential clashes in the future. keytab.c keytab.h Each key-sequence can now have different binding functions from different sources, with the user provided binding having the highest precedence, followed by the default binding, followed by any terminal specific binding. This allows gl_change_terminal() to redefine the terminal-specific bindings each time that gl_change_terminal() is called, without overwriting the user specified or default bindings. In the future, it will also allow for reconfiguration of user specified bindings after the call to new_GetLine(). Ie. deleting a user specified binding should reinstate any default or terminal specific binding. man/cpl_complete_word.3 html/cpl_complete_word.html man/ef_expand_file.3 html/ef_expand_file.html man/gl_get_line.3 html/gl_get_line.html I added sections on thread safety to the man pages of the individual modules. man/gl_get_line.3 html/gl_get_line.html I documented the new gl_change_terminal() function. man/gl_get_line.3 html/gl_get_line.html In the description of the ~/.teclarc configuration file, I had omitted the 'bind' command word in the example entry. I have now remedied this. xorp/cli/libtecla/update_version0000775000076400007640000000412111421137511017155 0ustar greearbgreearb#!/bin/sh #----------------------------------------------------------------------- # Change the version number of the library. This changes the number in # every file that it is known to appear in. # # Usage: # update_version major minor micro #----------------------------------------------------------------------- usage="$0 major minor micro" if [ $# -ne 3 ]; then echo $usage exit 1 fi # Get the three components of the version number. major="$1" minor="$2" micro="$3" # Everything will need to be reconfigured after this change, so # discard any existing configuration. make distclean 2>/dev/null # Check that the version components are all positive integers. for c in $major $minor $micro; do if echo "$c" | awk '{exit $1 ~ /^[0-9]+$/}'; then echo 'Version number components must all be positive integers.' exit 1 fi done # # Update the version number in the configure.in script. # ed -s configure.in << EOF /^MAJOR_VER=\"[0-9][0-9]*\"/ s/^.*$/MAJOR_VER=\"$major\"/ /^MINOR_VER=\"[0-9][0-9]*\"/ s/^.*$/MINOR_VER=\"$minor\"/ /^MICRO_VER=\"[0-9][0-9]*\"/ s/^.*$/MICRO_VER=\"$micro\"/ w q EOF if which autoconf 1>/dev/null 2>&1; then autoconf else echo 'Note that autoconf needs to be run.' fi # # Update the version number in the libtecla header file script. # ed -s libtecla.h << EOF /^#define TECLA_MAJOR_VER [0-9][0-9]*/ s/^.*$/#define TECLA_MAJOR_VER $major/ /^#define TECLA_MINOR_VER [0-9][0-9]*/ s/^.*$/#define TECLA_MINOR_VER $minor/ /^#define TECLA_MICRO_VER [0-9][0-9]*/ s/^.*$/#define TECLA_MICRO_VER $micro/ w q EOF # # Update the version number in the README file. # ed -s README << EOF /version [0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]* / s/version [0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*/version $major.$minor.$micro/ w q EOF # # Update the version number in the html index file. # ed -s html/index.html << EOF /version [0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\./ s/version [0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*/version $major.$minor.$micro/g /libtecla-[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\./ s/libtecla-[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\./libtecla-$major.$minor.$micro./g w q EOF xorp/cli/libtecla/stringrp.c0000664000076400007640000002113611421137511016221 0ustar greearbgreearb/* * Copyright (c) 2000, 2001 by Martin C. Shepherd. * * All rights reserved. * * 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, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, and/or sell copies of the Software, and to permit persons * to whom the Software is furnished to do so, provided that the above * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Except as contained in this notice, the name of a copyright holder * shall not be used in advertising or otherwise to promote the sale, use * or other dealings in this Software without prior written authorization * of the copyright holder. */ #include #include #include #include "freelist.h" #include "stringrp.h" /* * StringSegment objects store lots of small strings in larger * character arrays. Since the total length of all of the strings can't * be known in advance, an extensible list of large character arrays, * called string-segments are used. */ typedef struct StringSegment StringSegment; struct StringSegment { StringSegment *next; /* A pointer to the next segment in the list */ char *block; /* An array of characters to be shared between strings */ int unused; /* The amount of unused space at the end of block[] */ }; /* * StringGroup is typedef'd in stringrp.h. */ struct StringGroup { FreeList *node_mem; /* The StringSegment free-list */ int block_size; /* The dimension of each character array block */ StringSegment *head; /* The list of character arrays */ }; /* * Specify how many StringSegment's to allocate at a time. */ #define STR_SEG_BLK 20 /*....................................................................... * Create a new StringGroup object. * * Input: * segment_size int The length of each of the large character * arrays in which multiple strings will be * stored. This sets the length of longest * string that can be stored, and for efficiency * should be at least 10 times as large as * the average string that will be stored. * Output: * return StringGroup * The new object, or NULL on error. */ StringGroup *_new_StringGroup(int segment_size) { StringGroup *sg; /* The object to be returned */ /* * Check the arguments. */ if(segment_size < 1) { fprintf(stderr, "_new_StringGroup: Invalid segment_size argument.\n"); return NULL; }; /* * Allocate the container. */ sg = (StringGroup *) malloc(sizeof(StringGroup)); if(!sg) { fprintf(stderr, "_new_StringGroup: Insufficient memory.\n"); return NULL; }; /* * Before attempting any operation that might fail, initialize the * container at least up to the point at which it can safely be passed * to _del_StringGroup(). */ sg->node_mem = NULL; sg->head = NULL; sg->block_size = segment_size; /* * Allocate the free list that is used to allocate list nodes. */ sg->node_mem = _new_FreeList("_new_StringGroup", sizeof(StringSegment), STR_SEG_BLK); if(!sg->node_mem) return _del_StringGroup(sg); return sg; } /*....................................................................... * Delete a StringGroup object. * * Input: * sg StringGroup * The object to be deleted. * Output: * return StringGroup * The deleted object (always NULL). */ StringGroup *_del_StringGroup(StringGroup *sg) { if(sg) { StringSegment *node; /* * Delete the character arrays. */ for(node=sg->head; node; node=node->next) { if(node->block) free(node->block); node->block = NULL; }; /* * Delete the list nodes that contained the string segments. */ sg->node_mem = _del_FreeList("_del_StringGroup", sg->node_mem, 1); sg->head = NULL; /* Already deleted by deleting sg->node_mem */ /* * Delete the container. */ free(sg); }; return NULL; } /*....................................................................... * Make a copy of a string in the specified string group, and return * a pointer to the copy. * * Input: * sg StringGroup * The group to store the string in. * string const char * The string to be recorded. * remove_escapes int If true, omit backslashes which escape * other characters when making the copy. * Output: * return char * The pointer to the copy of the string, * or NULL if there was insufficient memory. */ char *_sg_store_string(StringGroup *sg, const char *string, int remove_escapes) { char *copy; /* The recorded copy of string[] */ /* * Check the arguments. */ if(!sg || !string) return NULL; /* * Get memory for the string. */ copy = _sg_alloc_string(sg, strlen(string)); if(copy) { /* * If needed, remove backslash escapes while copying the input string * into the cache string. */ if(remove_escapes) { int escaped = 0; /* True if the next character should be */ /* escaped. */ const char *src = string; /* A pointer into the input string */ char *dst = copy; /* A pointer into the cached copy of the */ /* string. */ while(*src) { if(!escaped && *src == '\\') { escaped = 1; src++; } else { escaped = 0; *dst++ = *src++; }; }; *dst = '\0'; /* * If escapes have already been removed, copy the input string directly * into the cache. */ } else { strncpy(copy, string, strlen(string)); }; }; /* * Return a pointer to the copy of the string (or NULL if the allocation * failed). */ return copy; } /*....................................................................... * Allocate memory for a string of a given length. * * Input: * sg StringGroup * The group to store the string in. * length int The required length of the string. * Output: * return char * The pointer to the copy of the string, * or NULL if there was insufficient memory. */ char *_sg_alloc_string(StringGroup *sg, int length) { StringSegment *node; /* A node of the list of string segments */ char *copy; /* The allocated string */ /* * If the string is longer than block_size, then we can't record it. */ if(length > sg->block_size || length < 0) return NULL; /* * See if there is room to record the string in one of the existing * string segments. */ for(node=sg->head; node && node->unused <= length; node=node->next) ; /* * If there wasn't room, allocate a new string segment. */ if(!node) { node = (StringSegment *) _new_FreeListNode(sg->node_mem); if(!node) return NULL; /* * Initialize the segment. */ node->next = NULL; node->block = NULL; node->unused = sg->block_size; /* * Attempt to allocate the string segment character array. */ node->block = (char *) malloc(sg->block_size); if(!node->block) return NULL; /* * Prepend the node to the list. */ node->next = sg->head; sg->head = node; }; /* * Get memory for the string. */ copy = node->block + sg->block_size - node->unused; node->unused -= length + 1; /* * Return a pointer to the string memory. */ return copy; } /*....................................................................... * Delete all of the strings that are currently stored by a specified * StringGroup object. * * Input: * sg StringGroup * The group of strings to clear. */ void _clr_StringGroup(StringGroup *sg) { StringSegment *node; /* A node in the list of string segments */ /* * Mark all of the string segments as unoccupied. */ for(node=sg->head; node; node=node->next) node->unused = sg->block_size; return; } xorp/cli/libtecla/cplfile.c0000664000076400007640000007135511421137511015777 0ustar greearbgreearb/* * Copyright (c) 2000, 2001 by Martin C. Shepherd. * * All rights reserved. * * 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, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, and/or sell copies of the Software, and to permit persons * to whom the Software is furnished to do so, provided that the above * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Except as contained in this notice, the name of a copyright holder * shall not be used in advertising or otherwise to promote the sale, use * or other dealings in this Software without prior written authorization * of the copyright holder. */ /* * Standard includes. */ #include #include #include #include #include #include /* * Local includes. */ #include "libtecla.h" #include "direader.h" #include "homedir.h" #include "pathutil.h" #include "cplfile.h" /* * Set the maximum length allowed for usernames. * names. */ #define USR_LEN 100 /* * Set the maximum length allowed for environment variable names. */ #define ENV_LEN 100 /* * Set the max length of the error-reporting string. There is no point * in this being longer than the width of a typical terminal window. * In composing error messages, I have assumed that this number is * at least 80, so you don't decrease it below this number. */ #define ERRLEN 200 /* * The resources needed to complete a filename are maintained in objects * of the following type. */ struct CompleteFile { DirReader *dr; /* A directory reader */ HomeDir *home; /* A home directory expander */ PathName *path; /* The buffer in which to accumulate the path */ PathName *buff; /* A pathname work buffer */ char usrnam[USR_LEN+1]; /* The buffer used when reading the names of */ /* users. */ char envnam[ENV_LEN+1]; /* The buffer used when reading the names of */ /* environment variables. */ char errmsg[ERRLEN+1]; /* The error-report buffer */ }; static int cf_expand_home_dir(CompleteFile *cf, const char *user); static int cf_complete_username(CompleteFile *cf, WordCompletion *cpl, const char *prefix, const char *line, int word_start, int word_end, int escaped); static HOME_DIR_FN(cf_homedir_callback); static int cf_complete_entry(CompleteFile *cf, WordCompletion *cpl, const char *line, int word_start, int word_end, int escaped, CplCheckFn *check_fn, void *check_data); static char *cf_read_name(CompleteFile *cf, const char *type, const char *string, int slen, char *nambuf, int nammax); static int cf_prepare_suffix(CompleteFile *cf, const char *suffix, int add_escapes); /* * A stack based object of the following type is used to pass data to the * cf_homedir_callback() function. */ typedef struct { CompleteFile *cf; /* The file-completion resource object */ WordCompletion *cpl; /* The string-completion rsource object */ const char *prefix; /* The username prefix to be completed */ const char *line; /* The line from which the prefix was extracted */ int word_start; /* The index in line[] of the start of the username */ int word_end; /* The index in line[] following the end of the prefix */ int escaped; /* If true, add escapes to the completion suffixes */ } CfHomeArgs; /*....................................................................... * Create a new file-completion object. * * Output: * return CompleteFile * The new object, or NULL on error. */ CompleteFile *_new_CompleteFile(void) { CompleteFile *cf; /* The object to be returned */ /* * Allocate the container. */ cf = (CompleteFile *) malloc(sizeof(CompleteFile)); if(!cf) { fprintf(stderr, "_new_CompleteFile: Insufficient memory.\n"); return NULL; }; /* * Before attempting any operation that might fail, initialize the * container at least up to the point at which it can safely be passed * to _del_CompleteFile(). */ cf->dr = NULL; cf->home = NULL; cf->path = NULL; cf->buff = NULL; cf->usrnam[0] = '\0'; cf->envnam[0] = '\0'; cf->errmsg[0] = '\0'; /* * Create the object that is used for reading directories. */ cf->dr = _new_DirReader(); if(!cf->dr) return _del_CompleteFile(cf); /* * Create the object that is used to lookup home directories. */ cf->home = _new_HomeDir(); if(!cf->home) return _del_CompleteFile(cf); /* * Create the buffer in which the completed pathname is accumulated. */ cf->path = _new_PathName(); if(!cf->path) return _del_CompleteFile(cf); /* * Create a pathname work buffer. */ cf->buff = _new_PathName(); if(!cf->buff) return _del_CompleteFile(cf); return cf; } /*....................................................................... * Delete a file-completion object. * * Input: * cf CompleteFile * The object to be deleted. * Output: * return CompleteFile * The deleted object (always NULL). */ CompleteFile *_del_CompleteFile(CompleteFile *cf) { if(cf) { cf->dr = _del_DirReader(cf->dr); cf->home = _del_HomeDir(cf->home); cf->path = _del_PathName(cf->path); cf->buff = _del_PathName(cf->buff); free(cf); }; return NULL; } /*....................................................................... * Look up the possible completions of the incomplete filename that * lies between specified indexes of a given command-line string. * * Input: * cpl WordCompletion * The object in which to record the completions. * cf CompleteFile * The filename-completion resource object. * line const char * The string containing the incomplete filename. * word_start int The index of the first character in line[] * of the incomplete filename. * word_end int The index of the character in line[] that * follows the last character of the incomplete * filename. * escaped int If true, backslashes in line[] are * interpreted as escaping the characters * that follow them, and any spaces, tabs, * backslashes, or wildcard characters in the * returned suffixes will be similarly escaped. * If false, backslashes will be interpreted as * literal parts of the file name, and no * backslashes will be added to the returned * suffixes. * check_fn CplCheckFn * If not zero, this argument specifies a * function to call to ask whether a given * file should be included in the list * of completions. * check_data void * Anonymous data to be passed to check_fn(). * Output: * return int 0 - OK. * 1 - Error. A description of the error can be * acquired by calling _cf_last_error(cf). */ int _cf_complete_file(WordCompletion *cpl, CompleteFile *cf, const char *line, int word_start, int word_end, int escaped, CplCheckFn *check_fn, void *check_data) { const char *lptr; /* A pointer into line[] */ int nleft; /* The number of characters still to be processed */ /* in line[]. */ /* * Check the arguments. */ if(!cpl || !cf || !line || word_end < word_start) { if(cf) strncpy(cf->errmsg, "_cf_complete_file: Invalid arguments", sizeof(cf->errmsg)); return 1; }; /* * Clear the buffer in which the filename will be constructed. */ _pn_clear_path(cf->path); /* * How many characters are to be processed? */ nleft = word_end - word_start; /* * Get a pointer to the start of the incomplete filename. */ lptr = line + word_start; /* * If the first character is a tilde, then perform home-directory * interpolation. */ if(nleft > 0 && *lptr == '~') { int slen; if(!cf_read_name(cf, "User", ++lptr, --nleft, cf->usrnam, USR_LEN)) return 1; /* * Advance over the username in the input line. */ slen = strlen(cf->usrnam); lptr += slen; nleft -= slen; /* * If we haven't hit the end of the input string then we have a complete * username to translate to the corresponding home directory. */ if(nleft > 0) { if(cf_expand_home_dir(cf, cf->usrnam)) return 1; /* * ~user and ~ are usually followed by a directory separator to * separate them from the file contained in the home directory. * If the home directory is the root directory, then we don't want * to follow the home directory by a directory separator, so we should * skip over it so that it doesn't get copied into the filename. */ if(strcmp(cf->path->name, FS_ROOT_DIR) == 0 && strncmp(lptr, FS_DIR_SEP, FS_DIR_SEP_LEN) == 0) { lptr += FS_DIR_SEP_LEN; nleft -= FS_DIR_SEP_LEN; }; /* * If we have reached the end of the input string, then the username * may be incomplete, and we should attempt to complete it. */ } else { /* * Look up the possible completions of the username. */ return cf_complete_username(cf, cpl, cf->usrnam, line, word_start+1, word_end, escaped); }; }; /* * Copy the rest of the path, stopping to expand $envvar expressions * where encountered. */ while(nleft > 0) { int seglen; /* The length of the next segment to be copied */ /* * Find the length of the next segment to be copied, stopping if an * unescaped '$' is seen, or the end of the path is reached. */ for(seglen=0; seglen < nleft; seglen++) { int c = lptr[seglen]; if(escaped && c == '\\') seglen++; else if(c == '$') break; /* * We will be completing the last component of the file name, * so whenever a directory separator is seen, assume that it * might be the start of the last component, and mark the character * that follows it as the start of the name that is to be completed. */ if(nleft >= FS_DIR_SEP_LEN && strncmp(lptr + seglen, FS_DIR_SEP, FS_DIR_SEP_LEN)==0) { word_start = (lptr + seglen) - line + FS_DIR_SEP_LEN; }; }; /* * We have reached either the end of the filename or the start of * $environment_variable expression. Record the newly checked * segment of the filename in the output filename, removing * backslash-escapes where needed. */ if(_pn_append_to_path(cf->path, lptr, seglen, escaped) == NULL) { strncpy(cf->errmsg, "Insufficient memory to complete filename", sizeof(cf->errmsg)); return 1; }; lptr += seglen; nleft -= seglen; /* * If the above loop finished before we hit the end of the filename, * then this was because an unescaped $ was seen. In this case, interpolate * the value of the environment variable that follows it into the output * filename. */ if(nleft > 0) { char *value; /* The value of the environment variable */ int vlen; /* The length of the value string */ int nlen; /* The length of the environment variable name */ /* * Read the name of the environment variable. */ if(!cf_read_name(cf, "Environment", ++lptr, --nleft, cf->envnam, ENV_LEN)) return 1; /* * Advance over the environment variable name in the input line. */ nlen = strlen(cf->envnam); lptr += nlen; nleft -= nlen; /* * Get the value of the environment variable. */ value = getenv(cf->envnam); if(!value) { const char *fmt = "Unknown environment variable: %.*s"; snprintf(cf->errmsg, sizeof(cf->errmsg), fmt, ERRLEN - strlen(fmt), cf->envnam); return 1; }; vlen = strlen(value); /* * If we are at the start of the filename and the first character of the * environment variable value is a '~', attempt home-directory * interpolation. */ if(cf->path->name[0] == '\0' && value[0] == '~') { if(!cf_read_name(cf, "User", value+1, vlen-1, cf->usrnam, USR_LEN) || cf_expand_home_dir(cf, cf->usrnam)) return 1; /* * If the home directory is the root directory, and the ~usrname expression * was followed by a directory separator, prevent the directory separator * from being appended to the root directory by skipping it in the * input line. */ if(strcmp(cf->path->name, FS_ROOT_DIR) == 0 && strncmp(lptr, FS_DIR_SEP, FS_DIR_SEP_LEN) == 0) { lptr += FS_DIR_SEP_LEN; nleft -= FS_DIR_SEP_LEN; }; } else { /* * Append the value of the environment variable to the output path. */ if(_pn_append_to_path(cf->path, value, strlen(value), escaped)==NULL) { strncpy(cf->errmsg, "Insufficient memory to complete filename", sizeof(cf->errmsg)); return 1; }; /* * Prevent extra directory separators from being added. */ if(nleft >= FS_DIR_SEP_LEN && strcmp(cf->path->name, FS_ROOT_DIR) == 0 && strncmp(lptr, FS_DIR_SEP, FS_DIR_SEP_LEN) == 0) { lptr += FS_DIR_SEP_LEN; nleft -= FS_DIR_SEP_LEN; } else if(vlen > FS_DIR_SEP_LEN && strcmp(value + vlen - FS_DIR_SEP_LEN, FS_DIR_SEP)==0) { cf->path->name[vlen-FS_DIR_SEP_LEN] = '\0'; }; }; /* * If adding the environment variable didn't form a valid directory, * we can't complete the line, since there is no way to separate append * a partial filename to an environment variable reference without * that appended part of the name being seen later as part of the * environment variable name. Thus if the currently constructed path * isn't a directory, quite now with no completions having been * registered. */ if(!_pu_path_is_dir(cf->path->name)) return 0; /* * For the reasons given above, if we have reached the end of the filename * with the expansion of an environment variable, the only allowed * completion involves the addition of a directory separator. */ if(nleft == 0) { if(cpl_add_completion(cpl, line, lptr-line, word_end, FS_DIR_SEP, "", "")) { strncpy(cf->errmsg, cpl_last_error(cpl), ERRLEN); cf->errmsg[ERRLEN] = '\0'; return 1; }; return 0; }; }; }; /* * Complete the filename if possible. */ return cf_complete_entry(cf, cpl, line, word_start, word_end, escaped, check_fn, check_data); } /*....................................................................... * Return a description of the last path-completion error that occurred. * * Input: * cf CompleteFile * The path-completion resource object. * Output: * return const char * The description of the last error. */ const char *_cf_last_error(CompleteFile *cf) { return cf ? cf->errmsg : "NULL CompleteFile argument"; } /*....................................................................... * Lookup the home directory of the specified user, or the current user * if no name is specified, appending it to output pathname. * * Input: * cf CompleteFile * The pathname completion resource object. * user const char * The username to lookup, or "" to lookup the * current user. * Output: * return int 0 - OK. * 1 - Error. */ static int cf_expand_home_dir(CompleteFile *cf, const char *user) { /* * Attempt to lookup the home directory. */ const char *home_dir = _hd_lookup_home_dir(cf->home, user); /* * Failed? */ if(!home_dir) { strncpy(cf->errmsg, _hd_last_home_dir_error(cf->home), ERRLEN); cf->errmsg[ERRLEN] = '\0'; return 1; }; /* * Append the home directory to the pathname string. */ if(_pn_append_to_path(cf->path, home_dir, -1, 0) == NULL) { strncpy(cf->errmsg, "Insufficient memory for home directory expansion", sizeof(cf->errmsg)); return 1; }; return 0; } /*....................................................................... * Lookup and report all completions of a given username prefix. * * Input: * cf CompleteFile * The filename-completion resource object. * cpl WordCompletion * The object in which to record the completions. * prefix const char * The prefix of the usernames to lookup. * line const char * The command-line in which the username appears. * word_start int The index within line[] of the start of the * username that is being completed. * word_end int The index within line[] of the character which * follows the incomplete username. * escaped int True if the completions need to have special * characters escaped. * Output: * return int 0 - OK. * 1 - Error. */ static int cf_complete_username(CompleteFile *cf, WordCompletion *cpl, const char *prefix, const char *line, int word_start, int word_end, int escaped) { /* * Set up a container of anonymous arguments to be sent to the * username-lookup iterator. */ CfHomeArgs args; args.cf = cf; args.cpl = cpl; args.prefix = prefix; args.line = line; args.word_start = word_start; args.word_end = word_end; args.escaped = escaped; /* * Iterate through the list of users, recording those which start * with the specified prefix. */ if(_hd_scan_user_home_dirs(cf->home, &args, cf_homedir_callback)) { strncpy(cf->errmsg, _hd_last_home_dir_error(cf->home), ERRLEN); cf->errmsg[ERRLEN] = '\0'; return 1; }; return 0; } /*....................................................................... * The user/home-directory scanner callback function (see homedir.h) * used by cf_complete_username(). */ static HOME_DIR_FN(cf_homedir_callback) { /* * Get the file-completion resources from the anonymous data argument. */ CfHomeArgs *args = (CfHomeArgs *) data; WordCompletion *cpl = args->cpl; CompleteFile *cf = args->cf; /* * Get the length of the username prefix. */ int prefix_len = strlen(args->prefix); /* * Get the length of the latest user name that is to be compared to * the prefix. */ int name_len = strlen(usrnam); /* * See if the latest username starts with the prefix that we are * searching for, and record its suffix in the array of matches if so. */ if(name_len >= prefix_len && strncmp(args->prefix, usrnam, prefix_len)==0) { /* * Copy the username into the pathname work buffer, adding backslash * escapes where needed. */ if(cf_prepare_suffix(cf, usrnam+prefix_len, args->escaped)) { strncpy(errmsg, cf->errmsg, maxerr); errmsg[maxerr] = '\0'; return 1; }; /* * Report the completion suffix that was copied above. */ if(cpl_add_completion(cpl, args->line, args->word_start, args->word_end, cf->buff->name, FS_DIR_SEP, FS_DIR_SEP)) { strncpy(errmsg, cpl_last_error(cpl), maxerr); errmsg[maxerr] = '\0'; return 1; }; }; return 0; } /*....................................................................... * Report possible completions of the filename in cf->path->name[]. * * Input: * cf CompleteFile * The file-completion resource object. * cpl WordCompletion * The object in which to record the completions. * line const char * The input line, as received by the callback * function. * word_start int The index within line[] of the start of the * last component of the filename that is being * completed. * word_end int The index within line[] of the character which * follows the incomplete filename. * escaped int If true, escape special characters in the * completion suffixes. * check_fn CplCheckFn * If not zero, this argument specifies a * function to call to ask whether a given * file should be included in the list * of completions. * check_data void * Anonymous data to be passed to check_fn(). * Output: * return int 0 - OK. * 1 - Error. */ static int cf_complete_entry(CompleteFile *cf, WordCompletion *cpl, const char *line, int word_start, int word_end, int escaped, CplCheckFn *check_fn, void *check_data) { const char *dirpath; /* The name of the parent directory */ int start; /* The index of the start of the last filename */ /* component in the transcribed filename. */ const char *prefix; /* The filename prefix to be completed */ int prefix_len; /* The length of the filename prefix */ const char *file_name; /* The lastest filename being compared */ int waserr = 0; /* True after errors */ int terminated=0; /* True if the directory part had to be terminated */ /* * Get the pathname string and its current length. */ char *pathname = cf->path->name; int pathlen = strlen(pathname); /* * Locate the start of the final component of the pathname. */ for(start=pathlen - 1; start >= 0 && strncmp(pathname + start, FS_DIR_SEP, FS_DIR_SEP_LEN) != 0; start--) ; /* * Is the parent directory the root directory? */ if(start==0 || (start < 0 && strncmp(pathname, FS_ROOT_DIR, FS_ROOT_DIR_LEN) == 0)) { dirpath = FS_ROOT_DIR; start += FS_ROOT_DIR_LEN; /* * If we found a directory separator then the part which precedes the * last component is the name of the directory to be opened. */ } else if(start > 0) { /* * The _dr_open_dir() function requires the directory name to be '\0' * terminated, so temporarily do this by overwriting the first character * of the directory separator. */ pathname[start] = '\0'; dirpath = pathname; terminated = 1; /* * We reached the start of the pathname before finding a directory * separator, so arrange to open the current working directory. */ } else { start = 0; dirpath = FS_PWD; }; /* * Attempt to open the directory. */ if(_dr_open_dir(cf->dr, dirpath, NULL)) { const char *fmt = "Can't open directory: %.*s"; snprintf(cf->errmsg, sizeof(cf->errmsg), fmt, ERRLEN - strlen(fmt), dirpath); return 1; }; /* * If removed above, restore the directory separator and skip over it * to the start of the filename. */ if(terminated) { memcpy(pathname + start, FS_DIR_SEP, FS_DIR_SEP_LEN); start += FS_DIR_SEP_LEN; }; /* * Get the filename prefix and its length. */ prefix = pathname + start; prefix_len = strlen(prefix); /* * Traverse the directory, looking for files who's prefixes match the * last component of the pathname. */ while((file_name = _dr_next_file(cf->dr)) != NULL && !waserr) { int name_len = strlen(file_name); /* * Is the latest filename a possible completion of the filename prefix? */ if(name_len >= prefix_len && strncmp(prefix, file_name, prefix_len)==0) { /* * When listing all files in a directory, don't list files that start * with '.'. This is how hidden files are denoted in UNIX. */ if(prefix_len > 0 || file_name[0] != '.') { /* * Copy the completion suffix into the work pathname cf->buff->name, * adding backslash escapes if needed. */ if(cf_prepare_suffix(cf, file_name + prefix_len, escaped)) { waserr = 1; } else { /* * We want directories to be displayed with directory suffixes, * and other fully completed filenames to be followed by spaces. * To check the type of the file, append the current suffix * to the path being completed, check the filetype, then restore * the path to its original form. */ const char *cont_suffix = ""; /* The suffix to add if fully */ /* completed. */ const char *type_suffix = ""; /* The suffix to add when listing */ if(_pn_append_to_path(cf->path, file_name + prefix_len, -1, escaped) == NULL) { strncpy(cf->errmsg, "Insufficient memory to complete filename.", sizeof(cf->errmsg)); return 1; }; /* * Specify suffixes according to the file type. */ if(_pu_path_is_dir(cf->path->name)) { cont_suffix = FS_DIR_SEP; type_suffix = FS_DIR_SEP; } else if(!check_fn || check_fn(check_data, cf->path->name)) { cont_suffix = " "; } else { cf->path->name[pathlen] = '\0'; continue; }; /* * Remove the temporarily added suffix. */ cf->path->name[pathlen] = '\0'; /* * Record the latest completion. */ if(cpl_add_completion(cpl, line, word_start, word_end, cf->buff->name, type_suffix, cont_suffix)) waserr = 1; }; }; }; }; /* * Close the directory. */ _dr_close_dir(cf->dr); return waserr; } /*....................................................................... * Read a username or environment variable name, stopping when a directory * separator is seen, when the end of the string is reached, or the * output buffer overflows. * * Input: * cf CompleteFile * The file-completion resource object. * type char * The capitalized name of the type of name being read. * string char * The string who's prefix contains the name. * slen int The number of characters in string[]. * nambuf char * The output name buffer. * nammax int The longest string that will fit in nambuf[], excluding * the '\0' terminator. * Output: * return char * A pointer to nambuf on success. On error NULL is * returned and a description of the error is recorded * in cf->errmsg[]. */ static char *cf_read_name(CompleteFile *cf, const char *type, const char *string, int slen, char *nambuf, int nammax) { int namlen; /* The number of characters in nambuf[] */ const char *sptr; /* A pointer into string[] */ /* * Work out the max number of characters that should be copied. */ int nmax = nammax < slen ? nammax : slen; /* * Get the environment variable name that follows the dollar. */ for(sptr=string,namlen=0; namlen < nmax && (slen-namlen < FS_DIR_SEP_LEN || strncmp(sptr, FS_DIR_SEP, FS_DIR_SEP_LEN) != 0); namlen++) { nambuf[namlen] = *sptr++; }; /* * Did the name overflow the buffer? */ if(namlen >= nammax) { const char *fmt = "%.*s name too long"; snprintf(cf->errmsg, sizeof(cf->errmsg), fmt, ERRLEN - strlen(fmt), type); return NULL; }; /* * Terminate the string. */ nambuf[namlen] = '\0'; return nambuf; } /*....................................................................... * Using the work buffer cf->buff, make a suitably escaped copy of a * given completion suffix, ready to be passed to cpl_add_completion(). * * Input: * cf CompleteFile * The file-completion resource object. * suffix char * The suffix to be copied. * add_escapes int If true, escape special characters. * Output: * return int 0 - OK. * 1 - Error. */ static int cf_prepare_suffix(CompleteFile *cf, const char *suffix, int add_escapes) { const char *sptr; /* A pointer into suffix[] */ int nbsl; /* The number of backslashes to add to the suffix */ int i; /* * How long is the suffix? */ int suffix_len = strlen(suffix); /* * Clear the work buffer. */ _pn_clear_path(cf->buff); /* * Count the number of backslashes that will have to be added to * escape spaces, tabs, backslashes and wildcard characters. */ nbsl = 0; if(add_escapes) { for(sptr = suffix; *sptr; sptr++) { switch(*sptr) { case ' ': case '\t': case '\\': case '*': case '?': case '[': nbsl++; break; }; }; }; /* * Arrange for the output path buffer to have sufficient room for the * both the suffix and any backslashes that have to be inserted. */ if(_pn_resize_path(cf->buff, suffix_len + nbsl) == NULL) { strncpy(cf->errmsg, "Insufficient memory to complete filename", sizeof(cf->errmsg)); return 1; }; /* * If the suffix doesn't need any escapes, copy it directly into the * work buffer. */ if(nbsl==0) { strncpy(cf->buff->name, suffix, cf->buff->dim); } else { /* * Make a copy with special characters escaped? */ if(nbsl > 0) { const char *src = suffix; char *dst = cf->buff->name; for(i=0; i #include #include #include #include "hash.h" #include "strngmem.h" #include "freelist.h" /* * The following container object contains free-lists to be used * for allocation of HashTable containers and nodes. */ struct HashMemory { FreeList *hash_memory; /* HashTable free-list */ FreeList *node_memory; /* HashNode free-list */ StringMem *string_memory; /* Memory used to allocate hash strings */ }; /* * Define a hash symbol-table entry. * See symbol.h for the definition of the Symbol container type. */ typedef struct HashNode HashNode; struct HashNode { Symbol symbol; /* The symbol stored in the hash-entry */ HashNode *next; /* The next hash-table entry in a bucket list */ }; /* * Each hash-table bucket contains a linked list of entries that * hash to the same bucket. */ typedef struct { HashNode *head; /* The head of the bucket hash-node list */ int count; /* The number of entries in the list */ } HashBucket; /* * Set the max length of the error-reporting string. There is no point * in this being longer than the width of a typical terminal window. * In composing error messages, I have assumed that this number is * at least 80, so you don't decrease it below this number. */ #define ERRLEN 200 /* * A hash-table consists of 'size' hash buckets. * Note that the HashTable typedef for this struct is contained in hash.h. */ struct HashTable { char errmsg[ERRLEN+1];/* Error-report buffer */ HashMemory *mem; /* HashTable free-list */ int internal_mem; /* True if 'mem' was allocated by _new_HashTable() */ int case_sensitive; /* True if case is significant in lookup keys */ int size; /* The number of hash buckets */ HashBucket *bucket; /* An array of 'size' hash buckets */ int (*keycmp)(const char *, const char *); /* Key comparison function */ void *app_data; /* Application-provided data */ HASH_DEL_FN(*del_fn); /* Application-provided 'app_data' destructor */ }; static HashNode *_del_HashNode(HashTable *hash, HashNode *node); static HashNode *_new_HashNode(HashTable *hash, const char *name, int code, void (*fn)(void), void *data, SYM_DEL_FN(*del_fn)); static HashNode *_find_HashNode(HashTable *hash, HashBucket *bucket, const char *name, HashNode **prev); static HashBucket *_find_HashBucket(HashTable *hash, const char *name); static int _ht_lower_strcmp(const char *node_key, const char *look_key); static int _ht_strcmp(const char *node_key, const char *look_key); /*....................................................................... * Allocate a free-list for use in allocating hash tables and their nodes. * * Input: * list_count int The number of HashTable containers per free-list * block. * node_count int The number of HashTable nodes per free-list block. * Output: * return HashMemory * The new free-list for use in allocating hash tables * and their nodes. */ HashMemory *_new_HashMemory(int hash_count, int node_count) { HashMemory *mem; /* * Allocate the free-list container. */ mem = (HashMemory *) malloc(sizeof(HashMemory)); if(!mem) { fprintf(stderr, "_new_HashMemory: Insufficient memory.\n"); return NULL; }; /* * Initialize the container at least up to the point at which it can * safely be passed to _del_HashMemory(). */ mem->hash_memory = NULL; mem->node_memory = NULL; mem->string_memory = NULL; /* * Allocate the two free-lists. */ mem->hash_memory = _new_FreeList("_new_HashMemory", sizeof(HashTable), hash_count); if(!mem->hash_memory) return _del_HashMemory(mem, 1); mem->node_memory = _new_FreeList("_new_HashMemory", sizeof(HashNode), node_count); if(!mem->node_memory) return _del_HashMemory(mem, 1); mem->string_memory = _new_StringMem("_new_HashMemory", 64); if(!mem->string_memory) return _del_HashMemory(mem, 1); /* * Return the free-list container. */ return mem; } /*....................................................................... * Delete a HashTable free-list. An error will be displayed if the list is * still in use and the deletion will be aborted. * * Input: * mem HashMemory * The free-list container to be deleted. * force int If force==0 then _del_HashMemory() will complain * and refuse to delete the free-list if any * of nodes have not been returned to the free-list. * If force!=0 then _del_HashMemory() will not check * whether any nodes are still in use and will * always delete the list. * Output: * return HashMemory * Always NULL (even if the memory could not be * deleted). */ HashMemory *_del_HashMemory(HashMemory *mem, int force) { const char *caller = "_del_HashMemory"; if(mem) { if(!force && (_busy_FreeListNodes(mem->hash_memory) > 0 || _busy_FreeListNodes(mem->node_memory) > 0)) { fprintf(stderr, "%s: Free-list in use.\n", caller); return NULL; }; mem->hash_memory = _del_FreeList(caller, mem->hash_memory, force); mem->node_memory = _del_FreeList(caller, mem->node_memory, force); mem->string_memory = _del_StringMem(caller, mem->string_memory, force); free(mem); }; return NULL; } /*....................................................................... * Create a new hash table. * * Input: * mem HashMemory * An optional free-list for use in allocating * HashTable containers and nodes. See explanation * in hash.h. If you are going to allocate more * than one hash table, then it will be more * efficient to allocate a single free-list for * all of them than to force each hash table * to allocate its own private free-list. * size int The size of the hash table. Best performance * will be acheived if this is a prime number. * hcase HashCase Specify how symbol case is considered when * looking up symbols, from: * IGNORE_CASE - Upper and lower case versions * of a letter are treated as * being identical. * HONOUR_CASE - Upper and lower case versions * of a letter are treated as * being distinct. * characters in a lookup name is significant. * app_data void * Optional application data to be registered * to the table. This is presented to user * provided SYM_DEL_FN() symbol destructors along * with the symbol data. * del_fn() HASH_DEL_FN(*) If you want app_data to be free'd when the * hash-table is destroyed, register a suitable * destructor function here. * Output: * return HashTable * The new hash table, or NULL on error. */ HashTable *_new_HashTable(HashMemory *mem, int size, HashCase hcase, void *app_data, HASH_DEL_FN(*del_fn)) { HashTable *hash; /* The table to be returned */ int allocate_mem = !mem; /* True if mem should be internally allocated */ int i; /* * Check arguments. */ if(size <= 0) { fprintf(stderr, "_new_HashTable: Illegal table size (%d).\n", size); return NULL; }; /* * Allocate an internal free-list? */ if(allocate_mem) { mem = _new_HashMemory(1, 100); if(!mem) return NULL; }; /* * Allocate the container. */ hash = (HashTable *) _new_FreeListNode(mem->hash_memory); if(!hash) { fprintf(stderr, "_new_HashTable: Insufficient memory.\n"); if(allocate_mem) mem = _del_HashMemory(mem, 1); return NULL; }; /* * Before attempting any operation that might fail, initialize * the container at least up to the point at which it can safely * be passed to _del_HashTable(). */ hash->errmsg[0] = '\0'; hash->mem = mem; hash->internal_mem = allocate_mem; hash->case_sensitive = hcase==HONOUR_CASE; hash->size = size; hash->bucket = NULL; hash->keycmp = hash->case_sensitive ? _ht_strcmp : _ht_lower_strcmp; hash->app_data = app_data; hash->del_fn = del_fn; /* * Allocate the array of 'size' hash buckets. */ hash->bucket = (HashBucket *) malloc(sizeof(HashBucket) * size); if(!hash->bucket) { fprintf(stderr, "_new_HashTable: Insufficient memory for %d buckets.\n", size); return _del_HashTable(hash); }; /* * Initialize the bucket array. */ for(i=0; ibucket + i; b->head = NULL; b->count = 0; }; /* * The table is ready for use - albeit currently empty. */ return hash; } /*....................................................................... * Delete a hash-table. * * Input: * hash HashTable * The hash table to be deleted. * Output: * return HashTable * The deleted hash table (always NULL). */ HashTable *_del_HashTable(HashTable *hash) { if(hash) { /* * Clear and delete the bucket array. */ if(hash->bucket) { _clear_HashTable(hash); free(hash->bucket); hash->bucket = NULL; }; /* * Delete application data. */ if(hash->del_fn) hash->del_fn(hash->app_data); /* * If the hash table was allocated from an internal free-list, delete * it and the hash table by deleting the free-list. Otherwise just * return the hash-table to the external free-list. */ if(hash->internal_mem) _del_HashMemory(hash->mem, 1); else hash = (HashTable *) _del_FreeListNode(hash->mem->hash_memory, hash); }; return NULL; } /*....................................................................... * Create and install a new entry in a hash table. If an entry with the * same name already exists, replace its contents with the new data. * * Input: * hash HashTable * The hash table to insert the symbol into. * name const char * The name to tag the entry with. * code int An application-specific code to be stored in * the entry. * fn void (*)(void) An application-specific function to be stored * in the entry. * data void * An application-specific pointer to data to be * associated with the entry, or NULL if not * relevant. * del_fn SYM_DEL_FN(*) An optional destructor function. When the * symbol is deleted this function will be called * with the 'code' and 'data' arguments given * above. Any application data that was registered * to the table via the app_data argument of * _new_HashTable() will also be passed. * Output: * return HashNode * The new entry, or NULL if there was insufficient * memory or the arguments were invalid. */ Symbol *_new_HashSymbol(HashTable *hash, const char *name, int code, void (*fn)(void), void *data, SYM_DEL_FN(*del_fn)) { HashBucket *bucket; /* The hash-bucket associated with the name */ HashNode *node; /* The new node */ /* * Check arguments. */ if(!hash || !name) return NULL; /* * Get the hash bucket of the specified name. */ bucket = _find_HashBucket(hash, name); /* * See if a node with the same name already exists. */ node = _find_HashNode(hash, bucket, name, NULL); /* * If found, delete its contents by calling the user-supplied * destructor function, if provided. */ if(node) { if(node->symbol.data && node->symbol.del_fn) { node->symbol.data = node->symbol.del_fn(hash->app_data, node->symbol.code, node->symbol.data); }; /* * Allocate a new node if necessary. */ } else { node = _new_HashNode(hash, name, code, fn, data, del_fn); if(!node) return NULL; }; /* * Install the node at the head of the hash-bucket list. */ node->next = bucket->head; bucket->head = node; bucket->count++; return &node->symbol; } /*....................................................................... * Remove and delete a given hash-table entry. * * Input: * hash HashTable * The hash table to find the symbol in. * name const char * The name of the entry. * Output: * return HashNode * The deleted hash node (always NULL). */ Symbol *_del_HashSymbol(HashTable *hash, const char *name) { if(hash && name) { HashBucket *bucket = _find_HashBucket(hash, name); HashNode *prev; /* The node preceding the located node */ HashNode *node = _find_HashNode(hash, bucket, name, &prev); /* * Node found? */ if(node) { /* * Remove the node from the bucket list. */ if(prev) { prev->next = node->next; } else { bucket->head = node->next; }; /* * Record the loss of a node. */ bucket->count--; /* * Delete the node. */ (void) _del_HashNode(hash, node); }; }; return NULL; } /*....................................................................... * Look up a symbol in the hash table. * * Input: * hash HashTable * The table to look up the string in. * name const char * The name of the symbol to look up. * Output: * return Symbol * The located hash-table symbol, or NULL if not * found. */ Symbol *_find_HashSymbol(HashTable *hash, const char *name) { HashBucket *bucket; /* The hash-table bucket associated with name[] */ HashNode *node; /* The hash-table node of the requested symbol */ /* * Check arguments. */ if(!hash) return NULL; /* * Nothing to lookup? */ if(!name) return NULL; /* * Hash the name to a hash-table bucket. */ bucket = _find_HashBucket(hash, name); /* * Find the bucket entry that exactly matches the name. */ node = _find_HashNode(hash, bucket, name, NULL); if(!node) return NULL; return &node->symbol; } /*....................................................................... * Private function used to allocate a hash-table node. * The caller is responsible for checking that the specified symbol * is unique and for installing the returned entry in the table. * * Input: * hash HashTable * The table to allocate the node for. * name const char * The name of the new entry. * code int A user-supplied context code. * fn void (*)(void) A user-supplied function pointer. * data void * A user-supplied data pointer. * del_fn SYM_DEL_FN(*) An optional 'data' destructor function. * Output: * return HashNode * The new node, or NULL on error. */ static HashNode *_new_HashNode(HashTable *hash, const char *name, int code, void (*fn)(void), void *data, SYM_DEL_FN(*del_fn)) { HashNode *node; /* The new node */ /* * Allocate the new node from the free list. */ node = (HashNode *) _new_FreeListNode(hash->mem->node_memory); if(!node) return NULL; /* * Before attempting any operation that might fail, initialize the * contents of 'node' at least up to the point at which it can be * safely passed to _del_HashNode(). */ node->symbol.name = NULL; node->symbol.code = code; node->symbol.fn = fn; node->symbol.data = data; node->symbol.del_fn = del_fn; node->next = NULL; /* * Allocate a copy of 'name'. */ node->symbol.name = _new_StringMemString(hash->mem->string_memory, strlen(name) + 1); if(!node->symbol.name) return _del_HashNode(hash, node); /* * If character-case is insignificant in the current table, convert the * name to lower case while copying it. */ if(hash->case_sensitive) { strncpy(node->symbol.name, name, strlen(name) + 1); } else { const char *src = name; char *dst = node->symbol.name; for( ; *src; src++,dst++) *dst = tolower((int)(unsigned char) *src); *dst = '\0'; }; return node; } /*....................................................................... * Private function used to delete a hash-table node. * The node must have been removed from its list before calling this * function. * * Input: * hash HashTable * The table for which the node was originally * allocated. * node HashNode * The node to be deleted. * Output: * return HashNode * The deleted node (always NULL). */ static HashNode *_del_HashNode(HashTable *hash, HashNode *node) { if(node) { node->symbol.name = _del_StringMemString(hash->mem->string_memory, node->symbol.name); /* * Call the user-supplied data-destructor if provided. */ if(node->symbol.data && node->symbol.del_fn) node->symbol.data = node->symbol.del_fn(hash->app_data, node->symbol.code, node->symbol.data); /* * Return the node to the free-list. */ node->next = NULL; node = (HashNode *) _del_FreeListNode(hash->mem->node_memory, node); }; return NULL; } /*....................................................................... * Private function to locate the hash bucket associated with a given * name. * * This uses a hash-function described in the dragon-book * ("Compilers - Principles, Techniques and Tools", by Aho, Sethi and * Ullman; pub. Adison Wesley) page 435. * * Input: * hash HashTable * The table to look up the string in. * name const char * The name of the symbol to look up. * Output: * return HashBucket * The located hash-bucket. */ static HashBucket *_find_HashBucket(HashTable *hash, const char *name) { unsigned const char *kp; unsigned long h = 0L; if(hash->case_sensitive) { for(kp=(unsigned const char *) name; *kp; kp++) h = 65599UL * h + *kp; /* 65599 is a prime close to 2^16 */ } else { for(kp=(unsigned const char *) name; *kp; kp++) h = 65599UL * h + tolower((int)*kp); /* 65599 is a prime close to 2^16 */ }; return hash->bucket + (h % hash->size); } /*....................................................................... * Search for a given name in the entries of a given bucket. * * Input: * hash HashTable * The hash-table being searched. * bucket HashBucket * The bucket to search (use _find_HashBucket()). * name const char * The name to search for. * Output: * prev HashNode ** If prev!=NULL then the pointer to the node * preceding the located node in the list will * be recorded in *prev. This will be NULL either * if the name is not found or the located node is * at the head of the list of entries. * return HashNode * The located hash-table node, or NULL if not * found. */ static HashNode *_find_HashNode(HashTable *hash, HashBucket *bucket, const char *name, HashNode **prev) { HashNode *last; /* The previously searched node */ HashNode *node; /* The node that is being searched */ /* * Search the list for a node containing the specified name. */ for(last=NULL, node=bucket->head; node && hash->keycmp(node->symbol.name, name)!=0; last = node, node=node->next) ; if(prev) *prev = node ? last : NULL; return node; } /*....................................................................... * When hash->case_sensitive is zero this function is called * in place of strcmp(). In such cases the hash-table names are stored * as lower-case versions of the original strings so this function * performs the comparison against lower-case copies of the characters * of the string being compared. * * Input: * node_key const char * The lower-case hash-node key being compared * against. * look_key const char * The lookup key. * Output: * return int <0 if node_key < look_key. * 0 if node_key == look_key. * >0 if node_key > look_key. */ static int _ht_lower_strcmp(const char *node_key, const char *look_key) { int cn; /* The latest character from node_key[] */ int cl; /* The latest character from look_key[] */ do { cn = *node_key++; cl = *look_key++; } while(cn && cn==tolower((int)(unsigned char) cl)); return cn - tolower((int)(unsigned char) cl); } /*....................................................................... * This is a wrapper around strcmp for comparing hash-keys in a case * sensitive manner. The reason for having this wrapper, instead of using * strcmp() directly, is to make some C++ compilers happy. The problem * is that when the library is compiled with a C++ compiler, the * declaration of the comparison function is a C++ declaration, whereas * strcmp() is a pure C function and thus although it appears to have the * same declaration, the compiler disagrees. * * Input: * node_key char * The lower-case hash-node key being compared against. * look_key char * The lookup key. * Output: * return int <0 if node_key < look_key. * 0 if node_key == look_key. * >0 if node_key > look_key. */ static int _ht_strcmp(const char *node_key, const char *look_key) { return strcmp(node_key, look_key); } /*....................................................................... * Empty a hash-table by deleting all of its entries. * * Input: * hash HashTable * The hash table to clear. * Output: * return int 0 - OK. * 1 - Invalid arguments. */ int _clear_HashTable(HashTable *hash) { int i; /* * Check the arguments. */ if(!hash) return 1; /* * Clear the contents of the bucket array. */ for(i=0; isize; i++) { HashBucket *bucket = hash->bucket + i; /* * Delete the list of active hash nodes from the bucket. */ HashNode *node = bucket->head; while(node) { HashNode *next = node->next; (void) _del_HashNode(hash, node); node = next; }; /* * Mark the bucket as empty. */ bucket->head = NULL; bucket->count = 0; }; return 0; } /*....................................................................... * Execute a given function on each entry of a hash table, returning * before completion if the the specified function returns non-zero. * * Input: * hash HashTable * The table to traverse. * scan_fn HASH_SCAN_FN(*) The function to call. * context void * Optional caller-specific context data * to be passed to scan_fn(). * Output: * return int 0 - OK. * 1 - Either the arguments were invalid, or * scan_fn() returned non-zero at some * point. */ int _scan_HashTable(HashTable *hash, HASH_SCAN_FN(*scan_fn), void *context) { int i; /* * Check the arguments. */ if(!hash || !scan_fn) return 1; /* * Iterate through the buckets of the table. */ for(i=0; isize; i++) { HashBucket *bucket = hash->bucket + i; HashNode *node; /* * Iterate through the list of symbols that fall into bucket i, * passing each one to the caller-specified function. */ for(node=bucket->head; node; node=node->next) { if(scan_fn(&node->symbol, context)) return 1; }; }; return 0; } xorp/cli/libtecla/homedir.h0000664000076400007640000000671611421137511016014 0ustar greearbgreearb#ifndef homedir_h #define homedir_h /* * Copyright (c) 2000, 2001 by Martin C. Shepherd. * * All rights reserved. * * 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, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, and/or sell copies of the Software, and to permit persons * to whom the Software is furnished to do so, provided that the above * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Except as contained in this notice, the name of a copyright holder * shall not be used in advertising or otherwise to promote the sale, use * or other dealings in this Software without prior written authorization * of the copyright holder. */ typedef struct HomeDir HomeDir; /* * The following constructor and destructor functions create and * delete the resources needed to look up home directories. */ HomeDir *_new_HomeDir(void); HomeDir *_del_HomeDir(HomeDir *home); /* * Return the home directory of a specified user, or NULL if unknown. */ const char *_hd_lookup_home_dir(HomeDir *home, const char *user); /* * Get the description of the that occured when _hd_lookup_home_dir() was * last called. */ const char *_hd_last_home_dir_error(HomeDir *home); /* * The _hd_scan_user_home_dirs() function calls a user-provided function * for each username known by the system, passing the function both * the name and the home directory of the user. * * The following macro can be used to declare both pointers and * prototypes for the callback functions. The 'data' argument is * a copy of the 'data' argument passed to _hd_scan_user_home_dirs() * and is intended for the user of _hd_scan_user_home_dirs() to use * to pass anonymous context data to the callback function. * The username and home directories are passed to the callback function * in the *usrnam and *homedir arguments respectively. * To abort the scan, and have _hd_scan_user_home_dirs() return 1, the * callback function can return 1. A description of up to maxerr * characters before the terminating '\0', can be written to errmsg[]. * This can then be examined by calling _hd_last_home_dir_error(). * To indicate success and continue the scan, the callback function * should return 0. _hd_scan_user_home_dirs() returns 0 on successful * completion of the scan, or 1 if an error occurred or a call to the * callback function returned 1. */ #define HOME_DIR_FN(fn) int (fn)(void *data, const char *usrnam, const char *homedir, char *errmsg, int maxerr) int _hd_scan_user_home_dirs(HomeDir *home, void *data, HOME_DIR_FN(*callback_fn)); #endif xorp/cli/libtecla/hash.h0000664000076400007640000001344111421137511015301 0ustar greearbgreearb#ifndef hash_h #define hash_h /* * Copyright (c) 2000, 2001 by Martin C. Shepherd. * * All rights reserved. * * 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, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, and/or sell copies of the Software, and to permit persons * to whom the Software is furnished to do so, provided that the above * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Except as contained in this notice, the name of a copyright holder * shall not be used in advertising or otherwise to promote the sale, use * or other dealings in this Software without prior written authorization * of the copyright holder. */ /* * The following macro can be used to prototype or define a * function that deletes the data of a symbol-table entry. * * Input: * app_data void * The _new_HashTable() app_data argument. * code int The Symbol::code argument. * sym_data void * The Symbol::data argument to be deleted. * Output: * return void * The deleted data (always return NULL). */ #define SYM_DEL_FN(fn) void *(fn)(void *app_data, int code, void *sym_data) /* * The following macro can be used to prototype or define a * function that deletes the application-data of a hash-table. * * Input: * data void * The _new_HashTable() 'app_data' argument to be * deleted. * Output: * return void * The deleted data (always return NULL). */ #define HASH_DEL_FN(fn) void *(fn)(void *app_data) /* * The following is a container for recording the context * of a symbol in a manner that is independant of the particular * symbol-table implementation. Each hash-table entry contains * the following user supplied parameters: * * 1. An optional integral parameter 'code'. This is useful for * enumerating a symbol or for describing what type of data * or function is stored in the symbol. * * 2. An optional generic function pointer. This is useful for * associating functions with names. The user is responsible * for casting between the generic function type and the * actual function type. The code field could be used to * enumerate what type of function to cast to. * * 3. An optional generic pointer to a static or heap-allocated * object. It is up to the user to cast this back to the * appropriate object type. Again, the code field could be used * to describe what type of object is stored there. * If the object is dynamically allocated and should be discarded * when the symbol is deleted from the symbol table, send a * destructor function to have it deleted automatically. */ typedef struct { char *name; /* The name of the symbol */ int code; /* Application supplied integral code */ void (*fn)(void); /* Application supplied generic function */ void *data; /* Application supplied context data */ SYM_DEL_FN(*del_fn); /* Data destructor function */ } Symbol; /* * HashNode's and HashTable's are small objects. Separately allocating * many such objects would normally cause memory fragmentation. To * counter this, HashMemory objects are used. These contain * dedicated free-lists formed from large dynamically allocated arrays * of objects. One HashMemory object can be shared between multiple hash * tables (within a single thread). */ typedef struct HashMemory HashMemory; /* Create a free-list for allocation of hash tables and their nodes */ HashMemory *_new_HashMemory(int hash_count, int node_count); /* Delete a redundant free-list if not being used */ HashMemory *_del_HashMemory(HashMemory *mem, int force); /* * Declare an alias for the private HashTable structure defined in * hash.c. */ typedef struct HashTable HashTable; /* * Enumerate case-sensitivity options. */ typedef enum { IGNORE_CASE, /* Ignore case when looking up symbols */ HONOUR_CASE /* Honor case when looking up symbols */ } HashCase; /* Create a new hash-table */ HashTable *_new_HashTable(HashMemory *mem, int size, HashCase hcase, void *app_data, HASH_DEL_FN(*del_fn)); /* Delete a reference to a hash-table */ HashTable *_del_HashTable(HashTable *hash); /* Add an entry to a hash table */ Symbol *_new_HashSymbol(HashTable *hash, const char *key, int code, void (*fn)(void), void *data, SYM_DEL_FN(*del_fn)); /* Remove and delete all the entries in a given hash table */ int _clear_HashTable(HashTable *hash); /* Remove and delete a given hash-table entry */ Symbol *_del_HashSymbol(HashTable *hash, const char *key); /* Lookup a given hash-table entry */ Symbol *_find_HashSymbol(HashTable *hash, const char *key); /* Execute a given function on each entry of a hash table, returning */ /* before completion if the specified function returns non-zero. */ #define HASH_SCAN_FN(fn) int (fn)(Symbol *sym, void *context) int _scan_HashTable(HashTable *hash, HASH_SCAN_FN(*scan_fn), void *context); #endif xorp/cli/libtecla/cplmatch.c0000664000076400007640000007510011421137511016144 0ustar greearbgreearb/* * Copyright (c) 2000, 2001 by Martin C. Shepherd. * * All rights reserved. * * 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, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, and/or sell copies of the Software, and to permit persons * to whom the Software is furnished to do so, provided that the above * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Except as contained in this notice, the name of a copyright holder * shall not be used in advertising or otherwise to promote the sale, use * or other dealings in this Software without prior written authorization * of the copyright holder. */ /* * Standard includes. */ #include #include #include /* * Local includes. */ #include "libtecla.h" #include "stringrp.h" #include "pathutil.h" #include "cplfile.h" /* * Specify the number of strings to allocate when the string free-list * is exhausted. This also sets the number of elements to expand the * matches[] array by whenever it is found to be too small. */ #define STR_BLK_FACT 100 /* * Set the max length of the error-reporting string. There is no point * in this being longer than the width of a typical terminal window. * In composing error messages, I have assumed that this number is * at least 80, so don't decrease it below 80. */ #define ERRLEN 200 /* * Completion matches are recorded in containers of the following * type. */ struct WordCompletion { StringGroup *sg; /* Memory for a group of strings */ int matches_dim; /* The allocated size of result.matches[] */ char errmsg[ERRLEN+1]; /* The error-reporting buffer */ CplMatches result; /* Completions to be returned to the caller */ CompleteFile *cf; /* The resources used for filename completion */ }; static void cpl_sort_matches(WordCompletion *cpl); static void cpl_zap_duplicates(WordCompletion *cpl); static void cpl_clear_completions(WordCompletion *cpl); static int cpl_cmp_matches(const void *v1, const void *v2); static int cpl_cmp_suffixes(const void *v1, const void *v2); /* * The new_CplFileConf() constructor sets the integer first member of * the returned object to the following magic number. On seeing this, * cpl_file_completions() knows when it is passed a valid CplFileConf * object. */ #define CFC_ID_CODE 4568 /* * A pointer to a structure of the following type can be passed to * the builtin file-completion callback function to modify its behavior. */ struct CplFileConf { int id; /* new_CplFileConf() sets this to CFC_ID_CODE */ int escaped; /* If none-zero, backslashes in the input line are */ /* interpreted as escaping special characters and */ /* spaces, and any special characters and spaces in */ /* the listed completions will also be escaped with */ /* added backslashes. This is the default behaviour. */ /* If zero, backslashes are interpreted as being */ /* literal parts of the filename, and none are added */ /* to the completion suffixes. */ int file_start; /* The index in the input line of the first character */ /* of the filename. If you specify -1 here, */ /* cpl_file_completions() identifies the */ /* the start of the filename by looking backwards for */ /* an unescaped space, or the beginning of the line. */ CplCheckFn *chk_fn; /* If not zero, this argument specifies a */ /* function to call to ask whether a given */ /* file should be included in the list */ /* of completions. */ void *chk_data; /* Anonymous data to be passed to check_fn(). */ }; static void cpl_init_FileConf(CplFileConf *cfc); /*....................................................................... * Create a new string-completion object. * * Output: * return WordCompletion * The new object, or NULL on error. */ WordCompletion *new_WordCompletion(void) { WordCompletion *cpl; /* The object to be returned */ /* * Allocate the container. */ cpl = (WordCompletion *) malloc(sizeof(WordCompletion)); if(!cpl) { fprintf(stderr, "new_WordCompletion: Insufficient memory.\n"); return NULL; }; /* * Before attempting any operation that might fail, initialize the * container at least up to the point at which it can safely be passed * to del_WordCompletion(). */ cpl->sg = NULL; cpl->matches_dim = 0; cpl->result.suffix = NULL; cpl->result.cont_suffix = NULL; cpl->result.matches = NULL; cpl->result.nmatch = 0; cpl->cf = NULL; /* * Allocate an object that allows a group of strings to be allocated * efficiently by placing many of them in contiguous string segments. */ cpl->sg = _new_StringGroup(_pu_pathname_dim()); if(!cpl->sg) return del_WordCompletion(cpl); /* * Allocate an array for matching completions. This will be extended later * if needed. */ cpl->matches_dim = STR_BLK_FACT; cpl->result.matches = (CplMatch *) malloc(sizeof(cpl->result.matches[0]) * cpl->matches_dim); if(!cpl->result.matches) { fprintf(stderr, "new_WordCompletion: Insufficient memory to allocate array of matches.\n"); return del_WordCompletion(cpl); }; /* * Allocate a filename-completion resource object. */ cpl->cf = _new_CompleteFile(); if(!cpl->cf) return del_WordCompletion(cpl); return cpl; } /*....................................................................... * Delete a string-completion object. * * Input: * cpl WordCompletion * The object to be deleted. * Output: * return WordCompletion * The deleted object (always NULL). */ WordCompletion *del_WordCompletion(WordCompletion *cpl) { if(cpl) { cpl->sg = _del_StringGroup(cpl->sg); if(cpl->result.matches) { free(cpl->result.matches); cpl->result.matches = NULL; cpl->cf = _del_CompleteFile(cpl->cf); }; free(cpl); }; return NULL; } /*....................................................................... * This function is designed to be called by CplMatchFn callback * functions. It adds one possible completion of the token that is being * completed to an array of completions. If the completion needs any * special quoting to be valid when displayed in the input line, this * quoting must be included in the string. * * Input: * cpl WordCompletion * The argument of the same name that was passed * to the calling CplMatchFn callback function. * line const char * The input line, as received by the callback * function. * word_start int The index within line[] of the start of the * word that is being completed. * word_end int The index within line[] of the character which * follows the incomplete word, as received by the * calling callback function. * suffix const char * The appropriately quoted string that could * be appended to the incomplete token to complete * it. A copy of this string will be allocated * internally. * type_suffix const char * When listing multiple completions, gl_get_line() * appends this string to the completion to indicate * its type to the user. If not pertinent pass "". * Otherwise pass a literal or static string. * cont_suffix const char * If this turns out to be the only completion, * gl_get_line() will append this string as * a continuation. For example, the builtin * file-completion callback registers a directory * separator here for directory matches, and a * space otherwise. If the match were a function * name you might want to append an open * parenthesis, etc.. If not relevant pass "". * Otherwise pass a literal or static string. * Output: * return int 0 - OK. * 1 - Error. */ int cpl_add_completion(WordCompletion *cpl, const char *line, int word_start, int word_end, const char *suffix, const char *type_suffix, const char *cont_suffix) { CplMatch *match; /* The container of the new match */ char *string; /* A newly allocated copy of the completion string */ /* * Check the arguments. */ if(!cpl) return 1; if(!suffix) return 0; if(!type_suffix) type_suffix = ""; if(!cont_suffix) cont_suffix = ""; /* * Do we need to extend the array of matches[]? */ if(cpl->result.nmatch+1 > cpl->matches_dim) { int needed = cpl->matches_dim + STR_BLK_FACT; CplMatch *matches = (CplMatch *) realloc(cpl->result.matches, sizeof(cpl->result.matches[0]) * needed); if(!matches) { strncpy(cpl->errmsg, "Insufficient memory to extend array of matches.", sizeof(cpl->errmsg)); return 1; }; cpl->result.matches = matches; cpl->matches_dim = needed; }; /* * Allocate memory to store the combined completion prefix and the * new suffix. */ string = _sg_alloc_string(cpl->sg, word_end-word_start + strlen(suffix)); if(!string) { strncpy(cpl->errmsg, "Insufficient memory to extend array of matches.", sizeof(cpl->errmsg)); return 1; }; /* * Compose the string. */ strncpy(string, line + word_start, word_end - word_start); strncpy(string + word_end - word_start, suffix, strlen(suffix)+1); /* * Record the new match. */ match = cpl->result.matches + cpl->result.nmatch++; match->completion = string; match->suffix = string + word_end - word_start; match->type_suffix = type_suffix; /* * Record the continuation suffix. */ cpl->result.cont_suffix = cont_suffix; return 0; } /*....................................................................... * Sort the array of matches. * * Input: * cpl WordCompletion * The completion resource object. */ static void cpl_sort_matches(WordCompletion *cpl) { qsort(cpl->result.matches, cpl->result.nmatch, sizeof(cpl->result.matches[0]), cpl_cmp_matches); } /*....................................................................... * This is a qsort() comparison function used to sort matches. * * Input: * v1, v2 void * Pointers to the two matches to be compared. * Output: * return int -1 -> v1 < v2. * 0 -> v1 == v2 * 1 -> v1 > v2 */ static int cpl_cmp_matches(const void *v1, const void *v2) { const CplMatch *m1 = (const CplMatch *) v1; const CplMatch *m2 = (const CplMatch *) v2; return strcmp(m1->completion, m2->completion); } /*....................................................................... * Sort the array of matches in order of their suffixes. * * Input: * cpl WordCompletion * The completion resource object. */ static void cpl_sort_suffixes(WordCompletion *cpl) { qsort(cpl->result.matches, cpl->result.nmatch, sizeof(cpl->result.matches[0]), cpl_cmp_suffixes); } /*....................................................................... * This is a qsort() comparison function used to sort matches in order of * their suffixes. * * Input: * v1, v2 void * Pointers to the two matches to be compared. * Output: * return int -1 -> v1 < v2. * 0 -> v1 == v2 * 1 -> v1 > v2 */ static int cpl_cmp_suffixes(const void *v1, const void *v2) { const CplMatch *m1 = (const CplMatch *) v1; const CplMatch *m2 = (const CplMatch *) v2; return strcmp(m1->suffix, m2->suffix); } /*....................................................................... * Find the common prefix of all of the matching completion matches, * and record a pointer to it in cpl->result.suffix. Note that this has * the side effect of sorting the matches into suffix order. * * Input: * cpl WordCompletion * The completion resource object. * Output: * return int 0 - OK. * 1 - Error. */ static int cpl_common_suffix(WordCompletion *cpl) { CplMatches *result; /* The result container */ const char *first, *last; /* The first and last matching suffixes */ int length; /* The length of the common suffix */ /* * Get the container of the array of matching files. */ result = &cpl->result; /* * No matching completions? */ if(result->nmatch < 1) return 0; /* * Sort th matches into suffix order. */ cpl_sort_suffixes(cpl); /* * Given that the array of matches is sorted, the first and last * suffixes are those that differ most in their prefixes, so the common * prefix of these strings is the longest common prefix of all of the * suffixes. */ first = result->matches[0].suffix; last = result->matches[result->nmatch - 1].suffix; /* * Find the point at which the first and last matching strings * first difffer. */ while(*first && *first == *last) { first++; last++; }; /* * How long is the common suffix? */ length = first - result->matches[0].suffix; /* * Allocate memory to record the common suffix. */ result->suffix = _sg_alloc_string(cpl->sg, length); if(!result->suffix) { strncpy(cpl->errmsg, "Insufficient memory to record common completion suffix.", sizeof(cpl->errmsg)); return 1; }; /* * Record the common suffix. */ strncpy(result->suffix, result->matches[0].suffix, length); result->suffix[length] = '\0'; return 0; } /*....................................................................... * Discard the contents of the array of possible completion matches. * * Input: * cpl WordCompletion * The word-completion resource object. */ static void cpl_clear_completions(WordCompletion *cpl) { /* * Discard all of the strings. */ _clr_StringGroup(cpl->sg); /* * Record the fact that the array is now empty. */ cpl->result.nmatch = 0; cpl->result.suffix = NULL; cpl->result.cont_suffix = ""; /* * Also clear the error message. */ cpl->errmsg[0] = '\0'; return; } /*....................................................................... * Given an input line and the point at which it completion is to be * attempted, return an array of possible completions. * * Input: * cpl WordCompletion * The completion resource object. * line char * The current input line. * word_end int The index of the character in line[] which * follows the end of the token that is being * completed. * data void * Anonymous 'data' to be passed to match_fn(). * match_fn CplMatchFn * The function that will identify the prefix * to be completed from the input line, and * record completion matches. * Output: * return CplMatches * The container of the array of possible * completions. The returned pointer refers * to a container owned by the parent WordCompletion * object, and its contents thus potentially * change on every call to cpl_matches(). * On error, NULL is returned, and a description * of the error can be acquired by calling * cpl_last_error(cpl). */ CplMatches *cpl_complete_word(WordCompletion *cpl, const char *line, int word_end, void *data, CplMatchFn *match_fn) { int line_len = 0; /* The total length of the input line */ /* * How long is the input line? */ if (line != NULL) line_len = strlen(line); /* * Check the arguments. */ if(!cpl || !line || !match_fn || word_end < 0 || word_end > line_len) { if(cpl) strncpy(cpl->errmsg, "cpl_complete_word: Invalid arguments.", sizeof(cpl->errmsg)); return NULL; }; /* * Clear the return container. */ cpl_clear_completions(cpl); /* * Have the matching function record possible completion matches in * cpl->result.matches. */ if(match_fn(cpl, data, line, word_end)) { if(cpl->errmsg[0] == '\0') strncpy(cpl->errmsg, "Error completing word.", sizeof(cpl->errmsg)); return NULL; }; /* * Record a copy of the common initial part of all of the prefixes * in cpl->result.common. */ if(cpl_common_suffix(cpl)) return NULL; /* * Sort the matches into lexicographic order. */ cpl_sort_matches(cpl); /* * Discard any duplicate matches. */ cpl_zap_duplicates(cpl); /* * If there is more than one match, discard the continuation suffix. */ if(cpl->result.nmatch > 1) cpl->result.cont_suffix = ""; /* * Return the array of matches. */ return &cpl->result; } /*....................................................................... * Print out an array of matching completions. * * Input: * result CplMatches * The container of the sorted array of * completions. * fp FILE * The output stream to write to. * term_width int The width of the terminal. * Output: * return int 0 - OK. * 1 - Error. */ int cpl_list_completions(CplMatches *result, FILE *fp, int term_width) { int maxlen; /* The length of the longest matching string */ int width; /* The width of a column */ int ncol; /* The number of columns to list */ int nrow; /* The number of rows needed to list all of the matches */ int row,col; /* The row and column being written to */ int i; int newline, onewline; /* Flags if the prev/curr string ends on '\n' */ /* * Check the arguments. */ if(!result || !fp) { fprintf(stderr, "cpl_list_completions: NULL argument(s).\n"); return 1; }; /* * Not enough space to list anything? */ if(term_width < 1) return 0; /* * Work out the maximum length of the matching strings. */ maxlen = 0; for(i=0; inmatch; i++) { CplMatch *match = result->matches + i; int len = strlen(match->completion) + strlen(match->type_suffix); if(len > maxlen) maxlen = len; }; /* * Nothing to list? */ if(maxlen == 0) return 0; /* * Split the available terminal width into columns of maxlen + 2 characters. */ width = maxlen + 2; ncol = term_width / width; /* * If the column width is greater than the terminal width, the matches will * just have to overlap onto the next line. */ if(ncol < 1) ncol = 1; /* * How many rows will be needed? */ nrow = (result->nmatch + ncol - 1) / ncol; /* * Print the matches out in ncol columns, sorted in row order within each * column. */ onewline = 1; newline = 1; for(row=0; row < nrow; row++) { for(col=0; col < ncol; col++) { int m = col*nrow + row; onewline = newline; newline = 0; if(m < result->nmatch) { CplMatch *match = result->matches + m; /* Compute if the result string ends on '\n' */ do { int len = strlen(match->type_suffix); if (len) { if (match->type_suffix[len-1] == '\n') newline = 1; break; } len = strlen(match->completion); if (len) { if (match->type_suffix[len-1] == '\n') newline = 1; break; } } while (0); if(fprintf(fp, "%s%-*s%s", match->completion, (int) (((!onewline) && ncol > 1) ? maxlen - strlen(match->completion):0), match->type_suffix, newline ? "" : colerrmsg : "NULL WordCompletion argument"; } /*....................................................................... * When an error occurs while performing a completion, you registerf a * terse description of the error by calling cpl_record_error(). This * message will then be returned on the next call to cpl_last_error(). * * Input: * cpl WordCompletion * The string-completion resource object that was * originally passed to the callback. * errmsg const char * The description of the error. */ void cpl_record_error(WordCompletion *cpl, const char *errmsg) { if(cpl && errmsg) { strncpy(cpl->errmsg, errmsg, ERRLEN); cpl->errmsg[ERRLEN] = '\0'; }; } /*....................................................................... * This is the builtin completion callback function which performs file * completion. * * Input: * cpl WordCompletion * An opaque pointer to the object that will * contain the matches. This should be filled * via zero or more calls to cpl_add_completion(). * data void * Either NULL to request the default * file-completion behavior, or a pointer to a * CplFileConf structure, whose members specify * a different behavior. * line char * The current input line. * word_end int The index of the character in line[] which * follows the end of the token that is being * completed. * Output * return int 0 - OK. * 1 - Error. */ CPL_MATCH_FN(cpl_file_completions) { const char *start_path; /* The pointer to the start of the pathname */ /* in line[]. */ CplFileConf *conf; /* The new-style configuration object. */ /* * The following configuration object will be used if the caller didn't * provide one. */ CplFileConf default_conf; /* * This function can be called externally, so check its arguments. */ if(!cpl) return 1; if(!line || word_end < 0) { strncpy(cpl->errmsg, "cpl_file_completions: Invalid arguments.", sizeof(cpl->errmsg)); return 1; }; /* * The 'data' argument is either a CplFileConf pointer, identifiable * by having an integer id code as its first member, or the deprecated * CplFileArgs pointer, or can be NULL to request the default * configuration. */ if(data && *(int *)data == CFC_ID_CODE) { conf = (CplFileConf *) data; } else { /* * Select the defaults. */ conf = &default_conf; cpl_init_FileConf(&default_conf); /* * If we have been passed an instance of the deprecated CplFileArgs * structure, copy its configuration parameters over the defaults. */ if(data) { CplFileArgs *args = (CplFileArgs *) data; conf->escaped = args->escaped; conf->file_start = args->file_start; }; }; /* * Get the start of the filename. If not specified by the caller * identify it by searching backwards in the input line for an * unescaped space or the start of the line. */ if(conf->file_start < 0) { start_path = _pu_start_of_path(line, word_end); if(!start_path) { strncpy(cpl->errmsg, "Unable to find the start of the filename.", sizeof(cpl->errmsg)); return 1; }; } else { start_path = line + conf->file_start; }; /* * Perform the completion. */ if(_cf_complete_file(cpl, cpl->cf, line, start_path - line, word_end, conf->escaped, conf->chk_fn, conf->chk_data)) { cpl_record_error(cpl, _cf_last_error(cpl->cf)); return 1; }; return 0; } /*....................................................................... * Initialize a CplFileArgs structure with default configuration * parameters. Note that the CplFileArgs configuration type is * deprecated. The opaque CplFileConf object should be used in future * applications. * * Input: * cfa CplFileArgs * The configuration object of the * cpl_file_completions() callback. */ void cpl_init_FileArgs(CplFileArgs *cfa) { if(cfa) { cfa->escaped = 1; cfa->file_start = -1; }; } /*....................................................................... * Initialize a CplFileConf structure with default configuration * parameters. * * Input: * cfc CplFileConf * The configuration object of the * cpl_file_completions() callback. */ static void cpl_init_FileConf(CplFileConf *cfc) { if(cfc) { cfc->id = CFC_ID_CODE; cfc->escaped = 1; cfc->file_start = -1; cfc->chk_fn = 0; cfc->chk_data = NULL; }; } /*....................................................................... * Create a new CplFileConf object and initialize it with defaults. * * Output: * return CplFileConf * The new object, or NULL on error. */ CplFileConf *new_CplFileConf(void) { CplFileConf *cfc; /* The object to be returned */ /* * Allocate the container. */ cfc = (CplFileConf *)malloc(sizeof(CplFileConf)); if(!cfc) return NULL; /* * Before attempting any operation that might fail, initialize the * container at least up to the point at which it can safely be passed * to del_CplFileConf(). */ cpl_init_FileConf(cfc); return cfc; } /*....................................................................... * Delete a CplFileConf object. * * Input: * cfc CplFileConf * The object to be deleted. * Output: * return CplFileConf * The deleted object (always NULL). */ CplFileConf *del_CplFileConf(CplFileConf *cfc) { if(cfc) { /* * Delete the container. */ free(cfc); }; return NULL; } /*....................................................................... * If backslashes in the filename should be treated as literal * characters, call the following function with literal=1. Otherwise * the default is to treat them as escape characters, used for escaping * spaces etc.. * * Input: * cfc CplFileConf * The cpl_file_completions() configuration object * to be configured. * literal int Pass non-zero here to enable literal interpretation * of backslashes. Pass 0 to turn off literal * interpretation. */ void cfc_literal_escapes(CplFileConf *cfc, int literal) { if(cfc) cfc->escaped = !literal; } /*....................................................................... * Call this function if you know where the index at which the * filename prefix starts in the input line. Otherwise by default, * or if you specify start_index to be -1, the filename is taken * to start after the first unescaped space preceding the cursor, * or the start of the line, which ever comes first. * * Input: * cfc CplFileConf * The cpl_file_completions() configuration object * to be configured. * start_index int The index of the start of the filename in * the input line, or -1 to select the default. */ void cfc_file_start(CplFileConf *cfc, int start_index) { if(cfc) cfc->file_start = start_index; } /*....................................................................... * If you only want certain types of files to be included in the * list of completions, you use the following function to specify a * callback function which will be called to ask whether a given file * should be included. * * Input: * cfc CplFileConf * The cpl_file_completions() configuration object * to be configured. * chk_fn CplCheckFn * Zero to disable filtering, or a pointer to a * function that returns 1 if a given file should * be included in the list of completions. * chk_data void * Anonymous data to be passed to chk_fn() * every time that it is called. */ void cfc_set_check_fn(CplFileConf *cfc, CplCheckFn *chk_fn, void *chk_data) { if(cfc) { cfc->chk_fn = chk_fn; cfc->chk_data = chk_data; }; } /*....................................................................... * The following CplCheckFn callback returns non-zero if the specified * filename is that of an executable. */ CPL_CHECK_FN(cpl_check_exe) { return _pu_path_is_exe(pathname); } /*....................................................................... * Remove duplicates from a sorted array of matches. * * Input: * cpl WordCompletion * The completion resource object. */ static void cpl_zap_duplicates(WordCompletion *cpl) { CplMatch *matches; /* The array of matches */ int nmatch; /* The number of elements in matches[] */ const char *completion; /* The completion string of the last unique match */ const char *type_suffix; /* The type of the last unique match */ int src; /* The index of the match being considered */ int dst; /* The index at which to record the next */ /* unique match. */ /* * Get the array of matches and the number of matches that it * contains. */ matches = cpl->result.matches; nmatch = cpl->result.nmatch; /* * No matches? */ if(nmatch < 1) return; /* * Initialize the comparison strings with the first match. */ completion = matches[0].completion; type_suffix = matches[0].type_suffix; /* * Go through the array of matches, copying each new unrecorded * match at the head of the array, while discarding duplicates. */ for(src=dst=1; srccompletion) != 0 || strcmp(type_suffix, match->type_suffix) != 0) { if(src != dst) matches[dst] = *match; dst++; completion = match->completion; type_suffix = match->type_suffix; }; }; /* * Record the number of unique matches that remain. */ cpl->result.nmatch = dst; return; } xorp/cli/libtecla/demo2.c0000664000076400007640000002560011421137511015357 0ustar greearbgreearb/* * Copyright (c) 2000, 2001 by Martin C. Shepherd. * * All rights reserved. * * 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, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, and/or sell copies of the Software, and to permit persons * to whom the Software is furnished to do so, provided that the above * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Except as contained in this notice, the name of a copyright holder * shall not be used in advertising or otherwise to promote the sale, use * or other dealings in this Software without prior written authorization * of the copyright holder. */ #include #include #include #include #include #include #include #include #include "libtecla.h" /* * Encapsulate the resources needed by this demo. */ typedef struct { GetLine *gl; /* The line editor */ PathCache *pc; /* A cache of executables in the user's path */ PcaPathConf *ppc; /* The configuration argument of pca_path_completions() */ } DemoRes; /* * The following functions allocate and free instances of the above * structure. */ static DemoRes *new_DemoRes(void); static DemoRes *del_DemoRes(DemoRes *res); /* * Search backwards for the start of a pathname. */ static char *start_of_path(const char *string, int back_from); /* * Find the array indexes of the first character of the first * space-delimited word in the specified string, and of the character * that follows it. */ static int get_word_limits(const char *string, int *wa, int *wb); /* * This is the demonstration completion callback function (defined below). */ static CPL_MATCH_FN(demo_cpl_fn); /*....................................................................... * This demo takes no arguments. It reads lines of input until the * word 'exit' is entered, or C-d is pressed. It replaces the default * tab-completion callback with one which when invoked at the start of * a line, looks up completions of commands in the user's execution * path, and when invoked in other parts of the line, reverts to * normal filename completion. Whenever a new line is entered, it * extracts the first word on the line, looks it up in the user's * execution path to see if it corresponds to a known executable file, * and if so, displays the full pathname of the file, along with the * remaining arguments. */ int main(int argc, char *argv[]) { char *line; /* A line of input */ DemoRes *res; /* The resources of the demo */ int wa,wb; /* The delimiting indexes of a word in line[] */ int major,minor,micro; /* The version number of the library */ /* * Allocate the resources needed by this demo. */ res = new_DemoRes(); if(!res) return 1; /* * If the user has the LC_CTYPE or LC_ALL environment variables set, * enable display of characters corresponding to the specified locale. */ (void) setlocale(LC_CTYPE, ""); /* * Lookup and display the version number of the library. */ libtecla_version(&major, &minor, µ); printf("Welcome to the demo2 program of libtecla version %d.%d.%d\n", major, minor, micro); /* * Read lines of input from the user and print them to stdout. */ do { /* * Get a new line from the user. */ line = gl_get_line(res->gl, "$ ", NULL, 0); if(!line) break; /* * Work out the extent of the first word in the input line, and * try to identify this as a command in the path, displaying the * full pathname of the match if found. */ if(get_word_limits(line, &wa, &wb) == 0) { char *cmd = pca_lookup_file(res->pc, line + wa, wb-wa, 0); if(cmd) { printf("Command=%s\n", cmd); printf("Arguments=%s", line+wb); } else { printf("Command not found\n"); }; }; /* * If the user types "exit", quit the program. */ if(strcmp(line, "exit\n")==0) break; } while(1); /* * Clean up. */ res = del_DemoRes(res); return 0; } /*....................................................................... * This completion callback searches for completions of executables in * the user's path when invoked on a word at the start of the path, and * performs normal filename completion elsewhere. */ static CPL_MATCH_FN(demo_cpl_fn) { /* * Get the resource object that was passed to gl_customize_completion(). */ DemoRes *res = (DemoRes *) data; /* * Find the start of the filename prefix to be completed, searching * backwards for the first unescaped space, or the start of the line. */ char *start = start_of_path(line, word_end); /* * Skip spaces preceding the start of the prefix. */ while(start > line && isspace((int)(unsigned char) start[-1])) start--; /* * If the filename prefix is at the start of the line, attempt * to complete the filename as a command in the path. Otherwise * perform normal filename completion. */ return (start == line) ? pca_path_completions(cpl, res->ppc, line, word_end) : cpl_file_completions(cpl, NULL, line, word_end); } /*....................................................................... * Search backwards for the potential start of a filename. This * looks backwards from the specified index in a given string, * stopping at the first unescaped space or the start of the line. * * Input: * string const char * The string to search backwards in. * back_from int The index of the first character in string[] * that follows the pathname. * Output: * return char * The pointer to the first character of * the potential pathname, or NULL on error. */ static char *start_of_path(const char *string, int back_from) { int i, j; /* * Search backwards from the specified index. */ for(i=back_from-1; i>=0; i--) { int c = string[i]; /* * Stop on unescaped spaces. */ if(isspace((int)(unsigned char)c)) { /* * The space can't be escaped if we are at the start of the line. */ if(i==0) break; /* * Find the extent of the escape characters which precedes the space. */ for(j=i-1; j>=0 && string[j]=='\\'; j--) ; /* * If there isn't an odd number of escape characters before the space, * then the space isn't escaped. */ if((i - 1 - j) % 2 == 0) break; }; }; return (char *)string + i + 1; } /*....................................................................... * Create a new DemoRes object containing the resources needed by the * demo. * * Output: * return DemoRes * The new object, or NULL on error. */ static DemoRes *new_DemoRes(void) { DemoRes *res; /* The object to be returned */ /* * Allocate the container. */ res = (DemoRes *)malloc(sizeof(DemoRes)); if(!res) { fprintf(stderr, "new_DemoRes: Insufficient memory.\n"); return NULL; }; /* * Before attempting any operation that might fail, initialize the * container at least up to the point at which it can safely be passed * to del_DemoRes(). */ res->gl = NULL; res->pc = NULL; res->ppc = NULL; /* * Create the line editor, specifying a max line length of 500 bytes, * and 10000 bytes to allocate to storage of historical input lines. */ res->gl = new_GetLine(500, 10000); if(!res->gl) return del_DemoRes(res); /* * Enable text attribute formatting directives in prompt strings. */ gl_prompt_style(res->gl, GL_FORMAT_PROMPT); /* * Allocate a cache of the executable files found in the user's path. */ res->pc = new_PathCache(); if(!res->pc) return del_DemoRes(res); /* * Populate the cache with the contents of the user's path. */ if(pca_scan_path(res->pc, getenv("PATH"))) return del_DemoRes(res); /* * Arrange for susequent calls to pca_lookup_file() and pca_path_completions() * to only report files that are executable by the user. */ pca_set_check_fn(res->pc, cpl_check_exe, NULL); /* * Allocate a configuration object for use with pca_path_completions(). */ res->ppc = new_PcaPathConf(res->pc); if(!res->ppc) return del_DemoRes(res); /* * Replace the builtin filename completion callback with one which * searches for completions of executables in the user's path when * invoked on a word at the start of the path, and completes files * elsewhere. */ if(gl_customize_completion(res->gl, res, demo_cpl_fn)) return del_DemoRes(res); return res; } /*....................................................................... * Delete a DemoRes object. * * Input: * res DemoRes * The object to be deleted. * Output: * return DemoRes * The deleted object (always NULL). */ static DemoRes *del_DemoRes(DemoRes *res) { if(res) { res->gl = del_GetLine(res->gl); res->pc = del_PathCache(res->pc); res->ppc = del_PcaPathConf(res->ppc); free(res); }; return NULL; } /*....................................................................... * Return the limits of the word at the start of a given string, ignoring * leading white-space, and interpretting the first unescaped space, tab or * the end of the line, as the end of the word. * * Input: * string const char * The string to tokenize. * Input/Output: * wa,wb int * The indexes of the first character of the word, * and the character which follows the last * character of the word, will be assigned to * *wa and *wb, respectively. * Output: * return int 0 - A word was found. * 1 - No word was found before the end of the * string. */ static int get_word_limits(const char *string, int *wa, int *wb) { int escaped = 0; /* True if the next character is escaped */ /* * Skip leading white-space. */ for(*wa=0; isspace((int)(unsigned char)string[*wa]); (*wa)++) ; /* * Find the first unescaped space, stopping early if the end of the * string is reached. */ for(*wb = *wa; ; (*wb)++) { int c = string[*wb]; if(c=='\\') escaped = !escaped; else if((!escaped && isspace((int)(unsigned char)c)) || c=='\0') break; }; return *wa == *wb; } xorp/cli/libtecla/direader.h0000664000076400007640000000354611421137511016142 0ustar greearbgreearb#ifndef dirreader_h #define dirreader_h /* * Copyright (c) 2000, 2001 by Martin C. Shepherd. * * All rights reserved. * * 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, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, and/or sell copies of the Software, and to permit persons * to whom the Software is furnished to do so, provided that the above * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Except as contained in this notice, the name of a copyright holder * shall not be used in advertising or otherwise to promote the sale, use * or other dealings in this Software without prior written authorization * of the copyright holder. */ typedef struct DirReader DirReader; DirReader *_new_DirReader(void); DirReader *_del_DirReader(DirReader *dr); int _dr_open_dir(DirReader *dr, const char *pathname, char **errmsg); char *_dr_next_file(DirReader *dr); void _dr_close_dir(DirReader *dr); #endif xorp/cli/libtecla/getline.h0000664000076400007640000000603311421137511016004 0ustar greearbgreearb#ifndef getline_h #define getline_h /* * Copyright (c) 2000, 2001 by Martin C. Shepherd. * * All rights reserved. * * 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, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, and/or sell copies of the Software, and to permit persons * to whom the Software is furnished to do so, provided that the above * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Except as contained in this notice, the name of a copyright holder * shall not be used in advertising or otherwise to promote the sale, use * or other dealings in this Software without prior written authorization * of the copyright holder. */ /* * Set the name of the getline configuration file. */ #define TECLA_CONFIG_FILE "~/.teclarc" /* * The following macro returns non-zero if a character is * a control character. */ #define IS_CTRL_CHAR(c) ((unsigned char)(c) < ' ' || (unsigned char)(c)=='\177') /* * The following macro returns non-zero if a character is * a meta character. */ #define IS_META_CHAR(c) (((unsigned char)(c) & 0x80) && !isprint((int)(unsigned char)(c))) /* * Return the character that would be produced by pressing the * specified key plus the control key. */ #define MAKE_CTRL(c) ((c)=='?' ? '\177' : ((unsigned char)toupper((int)(unsigned char)(c)) & ~0x40)) /* * Return the character that would be produced by pressing the * specified key plus the meta key. */ #define MAKE_META(c) ((unsigned char)(c) | 0x80) /* * Given a binary control character, return the character that * had to be pressed at the same time as the control key. */ #define CTRL_TO_CHAR(c) (toupper((int)(unsigned char)(c) | 0x40)) /* * Given a meta character, return the character that was pressed * at the same time as the meta key. */ #define META_TO_CHAR(c) ((unsigned char)(c) & ~0x80) /* * Specify the string of characters other than the alphanumeric characters, * that are to be considered parts of words. */ #define GL_WORD_CHARS "_*\?\\[]" /* * Define the escape character, both as a string and as a character. */ #define GL_ESC_STR "\033" #define GL_ESC_CHAR '\033' #endif xorp/cli/libtecla/strngmem.c0000664000076400007640000001630111421137511016203 0ustar greearbgreearb/* * Copyright (c) 2000, 2001 by Martin C. Shepherd. * * All rights reserved. * * 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, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, and/or sell copies of the Software, and to permit persons * to whom the Software is furnished to do so, provided that the above * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Except as contained in this notice, the name of a copyright holder * shall not be used in advertising or otherwise to promote the sale, use * or other dealings in this Software without prior written authorization * of the copyright holder. */ #include #include #include "strngmem.h" #include "freelist.h" struct StringMem { unsigned long nmalloc; /* The number of strings allocated with malloc */ FreeList *fl; /* The free-list */ }; /*....................................................................... * Create a string free-list container and the first block of its free-list. * * Input: * caller const char * The name of the calling function, or NULL * to not report errors to stderr. * blocking_factor int The blocking_factor argument specifies how * many strings of length SM_STRLEN * bytes (see stringmem.h) are allocated in each * free-list block. * For example if blocking_factor=64 and * SM_STRLEN=16, then each new * free-list block will take 1K of memory. * Output: * return StringMem * The new free-list container, or NULL on * error. */ StringMem *_new_StringMem(const char *caller, unsigned blocking_factor) { StringMem *sm; /* The container to be returned. */ /* * Check arguments. */ if(blocking_factor < 1) { if(caller) { fprintf(stderr, "_new_StringMem (%s): Bad blocking factor (%d).\n", caller, blocking_factor); }; return NULL; }; /* * Allocate the container. */ sm = (StringMem *) malloc(sizeof(StringMem)); if(!sm) { if(caller) fprintf(stderr, "_new_StringMem (%s): Insufficient memory.\n", caller); return NULL; }; /* * Before attempting any operation that might fail, initialize * the container at least up to the point at which it can safely * be passed to _del_StringMem(). */ sm->nmalloc = 0; sm->fl = NULL; /* * Allocate the free-list. */ sm->fl = _new_FreeList(caller, SM_STRLEN, blocking_factor); if(!sm->fl) return _del_StringMem(caller, sm, 1); /* * Return the free-list container. */ return sm; } /*....................................................................... * Delete a string free-list. * * Input: * caller const char * The name of the calling function, or NULL to * not report errors to stderr. * sm StringMem * The string free-list to be deleted, or NULL. * force int If force==0 then _del_StringMem() will complain * and refuse to delete the free-list if any * of nodes have not been returned to the free-list. * If force!=0 then _del_StringMem() will not check * whether any nodes are still in use and will * always delete the list. * Output: * return StringMem * Always NULL (even if the list couldn't be * deleted). */ StringMem *_del_StringMem(const char *caller, StringMem *sm, int force) { if(sm) { /* * Check whether any strings have not been returned to the free-list. */ if(!force && (sm->nmalloc > 0 || _busy_FreeListNodes(sm->fl) > 0)) { if(caller) fprintf(stderr, "_del_StringMem (%s): Free-list in use.\n", caller); return NULL; }; /* * Delete the free-list. */ sm->fl = _del_FreeList(caller, sm->fl, force); /* * Delete the container. */ free(sm); }; return NULL; } /*....................................................................... * Allocate an array of 'length' chars. * * Input: * sm StringMem * The string free-list to allocate from. * length size_t The length of the new string (including '\0'). * Output: * return char * The new string or NULL on error. */ char *_new_StringMemString(StringMem *sm, size_t length) { char *string; /* The string to be returned */ int was_malloc; /* True if malloc was used to allocate the string */ /* * Check arguments. */ if(!sm) return NULL; if(length < 1) length = 1; /* * Allocate the new node from the free list if possible. */ if(length < SM_STRLEN) { string = (char *)_new_FreeListNode(sm->fl); if(!string) return NULL; was_malloc = 0; } else { string = (char *) malloc(length+1); /* Leave room for the flag byte */ if(!string) return NULL; /* * Count malloc allocations. */ was_malloc = 1; sm->nmalloc++; }; /* * Use the first byte of the string to record whether the string was * allocated with malloc or from the free-list. Then return the rest * of the string for use by the user. */ string[0] = (char) was_malloc; return string + 1; } /*....................................................................... * Free a string that was previously returned by _new_StringMemString(). * * Input: * sm StringMem * The free-list from which the string was originally * allocated. * s char * The string to be returned to the free-list, or NULL. * Output: * return char * Always NULL. */ char *_del_StringMemString(StringMem *sm, char *s) { int was_malloc; /* True if the string originally came from malloc() */ /* * Is there anything to be deleted? */ if(s && sm) { /* * Retrieve the true string pointer. This is one less than the one * returned by _new_StringMemString() because the first byte of the * allocated memory is reserved by _new_StringMemString as a flag byte * to say whether the memory was allocated from the free-list or directly * from malloc(). */ s--; /* * Get the origination flag. */ was_malloc = s[0]; if(was_malloc) { free(s); s = NULL; sm->nmalloc--; } else { s = (char *) _del_FreeListNode(sm->fl, s); }; }; return NULL; } xorp/cli/libtecla/keytab.c0000664000076400007640000005714711421137511015643 0ustar greearbgreearb/* * Copyright (c) 2000, 2001 by Martin C. Shepherd. * * All rights reserved. * * 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, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, and/or sell copies of the Software, and to permit persons * to whom the Software is furnished to do so, provided that the above * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Except as contained in this notice, the name of a copyright holder * shall not be used in advertising or otherwise to promote the sale, use * or other dealings in this Software without prior written authorization * of the copyright holder. */ #include #include #include #include #include "keytab.h" #include "getline.h" #include "strngmem.h" static int _kt_extend_table(KeyTab *kt); #if 0 static int _kt_parse_keybinding_string(const char *keyseq, char *binary, int *nc); #endif static int _kt_compare_strings(const char *s1, int n1, const char *s2, int n2); static void _kt_assign_action(KeySym *sym, KtBinder binder, KtKeyFn *keyfn); static char _kt_backslash_escape(const char *string, const char **endp); static int _kt_is_emacs_meta(const char *string); static int _kt_is_emacs_ctrl(const char *string); /*....................................................................... * Create a new key-binding symbol table. * * Output: * return KeyTab * The new object, or NULL on error. */ KeyTab *_new_KeyTab(void) { KeyTab *kt; /* The object to be returned */ /* * Allocate the container. */ kt = (KeyTab *) malloc(sizeof(KeyTab)); if(!kt) { fprintf(stderr, "new_KeyTab: Insufficient memory.\n"); return NULL; }; /* * Before attempting any operation that might fail, initialize the * container at least up to the point at which it can safely be passed * to del_KeyTab(). */ kt->size = KT_TABLE_INC; kt->nkey = 0; kt->table = NULL; kt->actions = NULL; kt->smem = NULL; /* * Allocate the table. */ kt->table = (KeySym *) malloc(sizeof(kt->table[0]) * kt->size); if(!kt->table) { fprintf(stderr, "new_KeyTab: Insufficient memory for table of size %d.\n", kt->size); return _del_KeyTab(kt); }; /* * Allocate a hash table of actions. */ kt->actions = _new_HashTable(NULL, KT_HASH_SIZE, IGNORE_CASE, NULL, 0); if(!kt->actions) return _del_KeyTab(kt); /* * Allocate a string allocation object. This allows allocation of * small strings without fragmenting the heap. */ kt->smem = _new_StringMem("new_KeyTab", KT_TABLE_INC); if(!kt->smem) return _del_KeyTab(kt); return kt; } /*....................................................................... * Delete a KeyTab object. * * Input: * kt KeyTab * The object to be deleted. * Output: * return KeyTab * The deleted object (always NULL). */ KeyTab *_del_KeyTab(KeyTab *kt) { if(kt) { if(kt->table) free(kt->table); kt->actions = _del_HashTable(kt->actions); kt->smem = _del_StringMem("del_KeyTab", kt->smem, 1); free(kt); }; return NULL; } /*....................................................................... * Increase the size of the table to accomodate more keys. * * Input: * kt KeyTab * The table to be extended. * Output: * return int 0 - OK. * 1 - Error. */ static int _kt_extend_table(KeyTab *kt) { /* * Attempt to increase the size of the table. */ KeySym *newtab = (KeySym *) realloc(kt->table, sizeof(kt->table[0]) * (kt->size + KT_TABLE_INC)); /* * Failed? */ if(!newtab) { fprintf(stderr, "getline(): Insufficient memory to extend keybinding table.\n"); return 1; }; /* * Install the resized table. */ kt->table = newtab; kt->size += KT_TABLE_INC; return 0; } /*....................................................................... * Add, update or remove a keybinding to the table. * * Input: * kt KeyTab * The table to add the binding to. * binder KtBinder The source of the binding. * keyseq const char * The key-sequence to bind. * action char * The action to associate with the key sequence, or * NULL to remove the action associated with the * key sequence. * Output: * return int 0 - OK. * 1 - Error. */ int _kt_set_keybinding(KeyTab *kt, KtBinder binder, const char *keyseq, const char *action) { KtKeyFn *keyfn; /* The action function */ /* * Check arguments. */ if(kt==NULL || !keyseq) { fprintf(stderr, "kt_set_keybinding: NULL argument(s).\n"); return 1; }; /* * Lookup the function that implements the specified action. */ if(!action) { keyfn = 0; } else { Symbol *sym = _find_HashSymbol(kt->actions, action); if(!sym) { fprintf(stderr, "getline: Unknown key-binding action: %s\n", action); return 1; }; keyfn = (KtKeyFn *) sym->fn; }; /* * Record the action in the table. */ return _kt_set_keyfn(kt, binder, keyseq, keyfn); } /*....................................................................... * Add, update or remove a keybinding to the table, specifying an action * function directly. * * Input: * kt KeyTab * The table to add the binding to. * binder KtBinder The source of the binding. * keyseq char * The key-sequence to bind. * keyfn KtKeyFn * The action function, or NULL to remove any existing * action function. * Output: * return int 0 - OK. * 1 - Error. */ int _kt_set_keyfn(KeyTab *kt, KtBinder binder, const char *keyseq, KtKeyFn *keyfn) { const char *kptr; /* A pointer into keyseq[] */ char *binary; /* The binary version of keyseq[] */ int nc; /* The number of characters in binary[] */ int first,last; /* The first and last entries in the table which */ /* minimally match. */ int size; /* The size to allocate for the binary string */ /* * Check arguments. */ if(kt==NULL || !keyseq) { fprintf(stderr, "kt_set_keybinding: NULL argument(s).\n"); return 1; }; /* * Work out a pessimistic estimate of how much space will be needed * for the binary copy of the string, noting that binary meta characters * embedded in the input string get split into two characters. */ for(size=0,kptr = keyseq; *kptr; kptr++) size += IS_META_CHAR(*kptr) ? 2 : 1; /* * Allocate a string that has the length of keyseq[]. */ binary = _new_StringMemString(kt->smem, size + 1); if(!binary) { fprintf(stderr, "gl_get_line: Insufficient memory to record key sequence.\n"); return 1; }; /* * Convert control and octal character specifications to binary characters. */ if(_kt_parse_keybinding_string(keyseq, binary, &nc)) { binary = _del_StringMemString(kt->smem, binary); return 1; }; /* * Lookup the position in the table at which to insert the binding. */ switch(_kt_lookup_keybinding(kt, binary, nc, &first, &last)) { /* * If an exact match for the key-sequence is already in the table, * simply replace its binding function (or delete the entry if * the new binding is 0). */ case KT_EXACT_MATCH: if(keyfn) { _kt_assign_action(kt->table + first, binder, keyfn); } else { _del_StringMemString(kt->smem, kt->table[first].keyseq); memmove(kt->table + first, kt->table + first + 1, (kt->nkey - first - 1) * sizeof(kt->table[0])); kt->nkey--; }; binary = _del_StringMemString(kt->smem, binary); break; /* * If an ambiguous match has been found and we are installing a * callback, then our new key-sequence would hide all of the ambiguous * matches, so we shouldn't allow it. */ case KT_AMBIG_MATCH: if(keyfn) { fprintf(stderr, "getline: Can't bind \"%s\", because it's a prefix of another binding.\n", keyseq); binary = _del_StringMemString(kt->smem, binary); return 1; }; break; /* * If the entry doesn't exist, create it. */ case KT_NO_MATCH: /* * Add a new binding? */ if(keyfn) { KeySym *sym; /* * We will need a new entry, extend the table if needed. */ if(kt->nkey + 1 > kt->size) { if(_kt_extend_table(kt)) { binary = _del_StringMemString(kt->smem, binary); return 1; }; }; /* * Make space to insert the new key-sequence before 'last'. */ if(last < kt->nkey) { memmove(kt->table + last + 1, kt->table + last, (kt->nkey - last) * sizeof(kt->table[0])); }; /* * Insert the new binding in the vacated position. */ sym = kt->table + last; sym->keyseq = binary; sym->nc = nc; sym->user_fn = sym->term_fn = sym->norm_fn = sym->keyfn = 0; _kt_assign_action(sym, binder, keyfn); kt->nkey++; }; break; case KT_BAD_MATCH: binary = _del_StringMemString(kt->smem, binary); return 1; break; }; return 0; } /*....................................................................... * Perform a min-match lookup of a key-binding. * * Input: * kt KeyTab * The keybinding table to lookup in. * binary_keyseq char * The binary key-sequence to lookup. * nc int the number of characters in keyseq[]. * Input/Output: * first,last int * If there is an ambiguous or exact match, the indexes * of the first and last symbols that minimally match * will be assigned to *first and *last respectively. * If there is no match, then first and last will * bracket the location where the symbol should be * inserted. * * Output: * return KtKeyMatch One of the following enumerators: * KT_EXACT_MATCH - An exact match was found. * KT_AMBIG_MATCH - An ambiguous match was found. * KT_NO_MATCH - No match was found. * KT_BAD_MATCH - An error occurred while searching. */ KtKeyMatch _kt_lookup_keybinding(KeyTab *kt, const char *binary_keyseq, int nc, int *first, int *last) { int mid; /* The index at which to bisect the table */ int bot; /* The lowest index of the table not searched yet */ int top; /* The highest index of the table not searched yet */ int test; /* The return value of strcmp() */ /* * Check the arguments. */ if(!kt || !binary_keyseq || !first || !last || nc < 0) { fprintf(stderr, "kt_lookup_keybinding: NULL argument(s).\n"); return KT_BAD_MATCH; }; /* * Perform a binary search for the key-sequence. */ bot = 0; top = kt->nkey - 1; while(top >= bot) { mid = (top + bot)/2; test = _kt_compare_strings(kt->table[mid].keyseq, kt->table[mid].nc, binary_keyseq, nc); if(test > 0) top = mid - 1; else if(test < 0) bot = mid + 1; else { *first = *last = mid; return KT_EXACT_MATCH; }; }; /* * An exact match wasn't found, but top is the index just below the * index where a match would be found, and bot is the index just above * where the match ought to be found. */ *first = top; *last = bot; /* * See if any ambiguous matches exist, and if so make *first and *last * refer to the first and last matches. */ if(*last < kt->nkey && kt->table[*last].nc > nc && _kt_compare_strings(kt->table[*last].keyseq, nc, binary_keyseq, nc)==0) { *first = *last; while(*last+1 < kt->nkey && kt->table[*last+1].nc > nc && _kt_compare_strings(kt->table[*last+1].keyseq, nc, binary_keyseq, nc)==0) (*last)++; return KT_AMBIG_MATCH; }; /* * No match. */ return KT_NO_MATCH; } /*....................................................................... * Convert a keybinding string into a uniq binary representation. * * Control characters can be given directly in their binary form, * expressed as either ^ or C-, followed by the character, expressed in * octal, like \129 or via C-style backslash escapes, with the addition * of '\E' to denote the escape key. Similarly, meta characters can be * given directly in binary or expressed as M- followed by the character. * Meta characters are recorded as two characters in the binary output * string, the first being the escape key, and the second being the key * that was modified by the meta key. This means that binding to * \EA or ^[A or M-A are all equivalent. * * Input: * keyseq char * The key sequence being added. * Input/Output: * binary char * The binary version of the key sequence will be * assigned to binary[], which must have at least * as many characters as keyseq[] plus the number * of embedded binary meta characters. * nc int * The number of characters assigned to binary[] * will be recorded in *nc. * Output: * return int 0 - OK. * 1 - Error. */ int _kt_parse_keybinding_string(const char *keyseq, char *binary, int *nc) { const char *iptr = keyseq; /* Pointer into keyseq[] */ char *optr = binary; /* Pointer into binary[] */ /* * Parse the input characters until they are exhausted or the * output string becomes full. */ while(*iptr) { /* * Check for special characters. */ switch(*iptr) { case '^': /* A control character specification */ /* * Convert the caret expression into the corresponding control * character unless no character follows the caret, in which case * record a literal caret. */ if(iptr[1]) { *optr++ = MAKE_CTRL(iptr[1]); iptr += 2; } else { *optr++ = *iptr++; }; break; /* * A backslash-escaped character? */ case '\\': /* * Convert the escape sequence to a binary character. */ *optr++ = _kt_backslash_escape(iptr+1, &iptr); break; /* * Possibly an emacs-style meta character? */ case 'M': if(_kt_is_emacs_meta(iptr)) { *optr++ = GL_ESC_CHAR; iptr += 2; } else { *optr++ = *iptr++; }; break; /* * Possibly an emacs-style control character specification? */ case 'C': if(_kt_is_emacs_ctrl(iptr)) { *optr++ = MAKE_CTRL(iptr[2]); iptr += 3; } else { *optr++ = *iptr++; }; break; default: /* * Convert embedded meta characters into an escape character followed * by the meta-unmodified character. */ if(IS_META_CHAR(*iptr)) { *optr++ = GL_ESC_CHAR; *optr++ = META_TO_CHAR(*iptr); iptr++; /* * To allow keysequences that start with printable characters to * be distinguished from the cursor-key keywords, prepend a backslash * to the former. This same operation is performed in gl_interpret_char() * before looking up a keysequence that starts with a printable character. */ } else if(iptr==keyseq && !IS_CTRL_CHAR(*iptr) && strcmp(keyseq, "up") != 0 && strcmp(keyseq, "down") != 0 && strcmp(keyseq, "left") != 0 && strcmp(keyseq, "right") != 0) { *optr++ = '\\'; *optr++ = *iptr++; } else { *optr++ = *iptr++; }; }; }; /* * How many characters were placed in the output array? */ *nc = optr - binary; return 0; } /*....................................................................... * Add, remove or modify an action. * * Input: * kt KeyTab * The key-binding table. * action char * The name of the action. * fn KtKeyFn * The function that implements the action, or NULL * to remove an existing action. * Output: * return int 0 - OK. * 1 - Error. */ int _kt_set_action(KeyTab *kt, const char *action, KtKeyFn *fn) { Symbol *sym; /* The symbol table entry of the action */ /* * Check the arguments. */ if(!kt || !action) { fprintf(stderr, "kt_set_action: NULL argument(s).\n"); return 1; }; /* * If no function was provided, delete an existing action. */ if(!fn) { sym = _del_HashSymbol(kt->actions, action); return 0; }; /* * If the action already exists, replace its action function. */ sym = _find_HashSymbol(kt->actions, action); if(sym) { sym->fn = (void (*)(void))fn; return 0; }; /* * Add a new action. */ if(!_new_HashSymbol(kt->actions, action, 0, (void (*)(void))fn, NULL, 0)) { fprintf(stderr, "Insufficient memory to record new key-binding action.\n"); return 1; }; return 0; } /*....................................................................... * Compare two strings of specified length which may contain embedded * ascii NUL's. * * Input: * s1 char * The first of the strings to be compared. * n1 int The length of the string in s1. * s2 char * The second of the strings to be compared. * n2 int The length of the string in s2. * Output: * return int < 0 if(s1 < s2) * 0 if(s1 == s2) * > 0 if(s1 > s2) */ static int _kt_compare_strings(const char *s1, int n1, const char *s2, int n2) { int i; /* * Find the first character where the two strings differ. */ for(i=0; iuser_fn = keyfn; break; case KTB_TERM: sym->term_fn = keyfn; break; case KTB_NORM: default: sym->norm_fn = keyfn; break; }; /* * Which of the current set of bindings should be used? */ if(sym->user_fn) sym->keyfn = sym->user_fn; else if(sym->norm_fn) sym->keyfn = sym->norm_fn; else sym->keyfn = sym->term_fn; return; } /*....................................................................... * Remove all key bindings that came from a specified source. * * Input: * kt KeyTab * The table of key bindings. * binder KtBinder The source of the bindings to be cleared. */ void _kt_clear_bindings(KeyTab *kt, KtBinder binder) { int oldkey; /* The index of a key in the original binding table */ int newkey; /* The index of a key in the updated binding table */ /* * If there is no table, then no bindings exist to be deleted. */ if(!kt) return; /* * Clear bindings of the given source. */ for(oldkey=0; oldkeynkey; oldkey++) _kt_assign_action(kt->table + oldkey, binder, 0); /* * Delete entries that now don't have a binding from any source. */ newkey = 0; for(oldkey=0; oldkeynkey; oldkey++) { KeySym *sym = kt->table + oldkey; if(!sym->keyfn) { _del_StringMemString(kt->smem, sym->keyseq); } else { if(oldkey != newkey) kt->table[newkey] = *sym; newkey++; }; }; /* * Record the number of keys that were kept. */ kt->nkey = newkey; return; } /*....................................................................... * Translate a backslash escape sequence to a binary character. * * Input: * string const char * The characters that follow the backslash. * Input/Output: * endp const char ** If endp!=NULL, on return *endp will be made to * point to the character in string[] which follows * the escape sequence. * Output: * return char The binary character. */ static char _kt_backslash_escape(const char *string, const char **endp) { char c; /* The output character */ /* * Is the backslash followed by one or more octal digits? */ switch(*string) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': c = strtol(string, (char **)&string, 8); break; case 'a': c = '\a'; string++; break; case 'b': c = '\b'; string++; break; case 'e': case 'E': /* Escape */ c = GL_ESC_CHAR; string++; break; case 'f': c = '\f'; string++; break; case 'n': c = '\n'; string++; break; case 'r': c = '\r'; string++; break; case 't': c = '\t'; string++; break; case 'v': c = '\v'; string++; break; case '\0': c = '\\'; break; default: c = *string++; break; }; /* * Report the character which follows the escape sequence. */ if(endp) *endp = string; return c; } /*....................................................................... * Return non-zero if the next two characters are M- and a third character * follows. Otherwise return 0. * * Input: * string const char * The sub-string to scan. * Output: * return int 1 - The next two characters are M- and these * are followed by at least one character. * 0 - The next two characters aren't M- or no * character follows a M- pair. */ static int _kt_is_emacs_meta(const char *string) { return *string++ == 'M' && *string++ == '-' && *string; } /*....................................................................... * Return non-zero if the next two characters are C- and a third character * follows. Otherwise return 0. * * Input: * string const char * The sub-string to scan. * Output: * return int 1 - The next two characters are C- and these * are followed by at least one character. * 0 - The next two characters aren't C- or no * character follows a C- pair. */ static int _kt_is_emacs_ctrl(const char *string) { return *string++ == 'C' && *string++ == '-' && *string; } /*....................................................................... * Merge an array of bindings with existing bindings. * * Input: * kt KeyTab * The table of key bindings. * binder KtBinder The source of the bindings. * bindings const KtKeyBinding * The array of bindings. * n int The number of bindings in bindings[]. * Output: * return int 0 - OK. * 1 - Error. */ int _kt_add_bindings(KeyTab *kt, KtBinder binder, const KtKeyBinding *bindings, unsigned n) { int i; /* * Check the arguments. */ if(!kt || !bindings) { fprintf(stderr, "_kt_add_bindings: NULL argument(s).\n"); return 1; }; /* * Install the array of bindings. */ for(i=0; i #include #include #include /* * Operating system includes. */ #ifndef __MINGW32__ #include #endif #include #include #include #include "direader.h" /* * Use the reentrant POSIX threads version of readdir()? */ #if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199506L #define USE_READDIR_R 1 #endif /* * Set the max length of the error-reporting string. There is no point * in this being longer than the width of a typical terminal window. * In composing error messages, I have assumed that this number is * at least 80, so you don't decrease it below this number. */ #define ERRLEN 200 /* * Objects of the following type are used to maintain the resources * needed to read directories. */ struct DirReader { DIR *dir; /* The directory stream (if open, NULL otherwise) */ struct dirent *file; /* The latest directory entry */ char errmsg[ERRLEN+1]; /* Error-report buffer */ #ifdef USE_READDIR_R struct dirent *buffer; /* A buffer used by the threaded version of readdir */ int buffer_dim; /* The allocated size of buffer[] */ #endif }; static int _dr_path_is_dir(const char *pathname); /*....................................................................... * Create a new DirReader object. * * Output: * return DirReader * The new object, or NULL on error. */ DirReader *_new_DirReader(void) { DirReader *dr; /* The object to be returned */ /* * Allocate the container. */ dr = (DirReader *) malloc(sizeof(DirReader)); if(!dr) { fprintf(stderr, "_new_DirReader: Insufficient memory.\n"); return NULL; }; /* * Before attempting any operation that might fail, initialize the * container at least up to the point at which it can safely be passed * to _del_DirReader(). */ dr->dir = NULL; dr->file = NULL; dr->errmsg[0] = '\0'; #ifdef USE_READDIR_R dr->buffer = NULL; dr->buffer_dim = 0; #endif return dr; } /*....................................................................... * Delete a DirReader object. * * Input: * dr DirReader * The object to be deleted. * Output: * return DirReader * The deleted object (always NULL). */ DirReader *_del_DirReader(DirReader *dr) { if(dr) { _dr_close_dir(dr); #ifdef USE_READDIR_R free(dr->buffer); #endif free(dr); }; return NULL; } /*....................................................................... * Open a new directory. * * Input: * dr DirReader * The directory reader resource object. * path const char * The directory to be opened. * Input/Output: * errmsg char ** If an error occurs and errmsg isn't NULL, a * pointer to an error description will be assigned * to *errmsg. * Output: * return int 0 - OK. * 1 - Error (see *errmsg for a description). */ int _dr_open_dir(DirReader *dr, const char *path, char **errmsg) { DIR *dir = NULL; /* The directory stream */ /* * If a directory is already open, close it first. */ (void) _dr_close_dir(dr); /* * Is the path a directory? */ if(!_dr_path_is_dir(path)) { if(errmsg) { const char *fmt = "Can't open directory: %.*s\n"; snprintf(dr->errmsg, sizeof(dr->errmsg), fmt, ERRLEN - strlen(fmt), path); *errmsg = dr->errmsg; }; return 1; }; /* * Attempt to open the directory. */ dir = opendir(path); if(!dir) { if(errmsg) { const char *fmt = "Can't open directory: %.*s\n"; snprintf(dr->errmsg, sizeof(dr->errmsg), fmt, ERRLEN - strlen(fmt), path); *errmsg = dr->errmsg; }; return 1; }; /* * If using POSIX threads, allocate a buffer for readdir_r(). */ #ifdef USE_READDIR_R { size_t size; int name_max = pathconf(path, _PC_NAME_MAX); #ifdef NAME_MAX if(name_max < 0) name_max = NAME_MAX; #endif if(name_max < 0) { if(errmsg) { strncpy(dr->errmsg, "Unable to deduce readdir() buffer size.", sizeof(dr->errmsg)); *errmsg = dr->errmsg; }; closedir(dir); return 1; }; /* * How big a buffer do we need to allocate? */ size = sizeof(struct dirent) + name_max; /* * Extend the buffer? */ if(size > dr->buffer_dim || !dr->buffer) { struct dirent *buffer = (struct dirent *) (dr->buffer ? realloc(dr->buffer, size) : malloc(size)); if(!buffer) { if(errmsg) { strncpy(dr->errmsg, "Insufficient memory for readdir() buffer.", sizeof(dr->errmsg)); *errmsg = dr->errmsg; }; closedir(dir); return 1; }; dr->buffer = buffer; dr->buffer_dim = size; }; }; #endif /* * Record the successfully opened directory. */ dr->dir = dir; return 0; } /*....................................................................... * If the DirReader object is currently contains an open directory, * close it. * * Input: * dr DirReader * The directory reader resource object. */ void _dr_close_dir(DirReader *dr) { if(dr && dr->dir) { closedir(dr->dir); dr->dir = NULL; dr->file = NULL; dr->errmsg[0] = '\0'; }; } /*....................................................................... * Read the next file from the directory opened with _dr_open_dir(). * * Input: * dr DirReader * The directory reader resource object. * Output: * return char * The name of the new file, or NULL if we reached * the end of the directory. */ char *_dr_next_file(DirReader *dr) { /* * Are we currently reading a directory? */ if(dr->dir) { /* * Read the next directory entry. */ #ifdef USE_READDIR_R if(readdir_r(dr->dir, dr->buffer, &dr->file) == 0 && dr->file) return dr->file->d_name; #else dr->file = readdir(dr->dir); if(dr->file) return dr->file->d_name; #endif }; /* * When the end of a directory is reached, close it. */ _dr_close_dir(dr); return NULL; } /*....................................................................... * Return 1 if the specified pathname refers to a directory. * * Input: * pathname const char * The path to test. * Output: * return int 0 - Not a directory. * 1 - pathname[] refers to a directory. */ static int _dr_path_is_dir(const char *pathname) { struct stat statbuf; /* The file-statistics return buffer */ /* * Look up the file attributes. */ if(stat(pathname, &statbuf) < 0) return 0; /* * Is the file a directory? */ return S_ISDIR(statbuf.st_mode) != 0; } xorp/cli/libtecla/getline.c0000664000076400007640000103315611421137511016006 0ustar greearbgreearb/* * Copyright (c) 2000, 2001 by Martin C. Shepherd. * * All rights reserved. * * 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, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, and/or sell copies of the Software, and to permit persons * to whom the Software is furnished to do so, provided that the above * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Except as contained in this notice, the name of a copyright holder * shall not be used in advertising or otherwise to promote the sale, use * or other dealings in this Software without prior written authorization * of the copyright holder. */ /* * Standard headers. */ #include #include #include #include #include #include #include /* * UNIX headers. */ #ifdef HAVE_SYS_IOCTL_H #include #endif #ifdef HAVE_SELECT #include #include #include #endif #ifdef __MINGW32__ #define sigsetjmp setjmp #define siglongjmp longjmp #define sigjmp_buf jmp_buf #endif /* * The source of terminal control string and size information. * Note that if no terminal information database is available, * ANSI VT100 control sequences are used. */ #if defined(USE_TERMINFO) #include #include #elif defined(USE_TERMCAP) #if defined(HAVE_TERMCAP_H) #include #else extern int tgetent(char *, char *); extern int tputs(char *, int, int (*)(char)); extern int tgetnum(char *); extern char *tgetstr(char *, char **); #endif #endif /* * Under Solaris default Curses the output function that tputs takes is * declared to have a char argument. On all other systems and on Solaris * X/Open Curses (Issue 4, Version 2) it expects an int argument (using * c89 or options -I /usr/xpg4/include -L /usr/xpg4/lib -R /usr/xpg4/lib * selects XPG4v2 Curses on Solaris 2.6 and later). */ #if defined USE_TERMINFO || defined USE_TERMCAP #if defined __sun && defined __SVR4 && !defined _XOPEN_CURSES typedef char TputsType; #else typedef int TputsType; #endif static int gl_tputs_putchar(TputsType c); #endif /* * POSIX headers. */ #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_TERMIOS_H #include #endif /* * Does the system provide the signal and ioctl query facility used * to inform the process of terminal window size changes? */ #if defined(SIGWINCH) && defined(TIOCGWINSZ) #define USE_SIGWINCH 1 #endif /* * Provide typedefs for standard POSIX structures. */ #ifndef __MINGW32__ typedef struct sigaction SigAction; typedef struct termios Termios; #endif /* * Local headers. */ #include "pathutil.h" #include "libtecla.h" #include "keytab.h" #include "history.h" #include "freelist.h" #include "stringrp.h" #include "getline.h" /* * Enumerate the available editing styles. */ typedef enum { GL_EMACS_MODE, /* Emacs style editing */ GL_VI_MODE, /* Vi style editing */ GL_NO_EDITOR /* Fall back to the basic OS-provided editing */ } GlEditor; /* * In vi mode, the following datatype is used to implement the * undo command. It records a copy of the input line from before * the command-mode action which edited the input line. */ typedef struct { char *line; /* A historical copy of the input line */ int buff_curpos; /* The historical location of the cursor in */ /* line[] when the line was modified. */ int ntotal; /* The number of characters in line[] */ int saved; /* True once a line has been saved after the */ /* last call to gl_interpret_char(). */ } ViUndo; /* * In vi mode, the following datatype is used to record information * needed by the vi-repeat-change command. */ typedef struct { KtKeyFn *fn; /* The last action function that made a */ /* change to the line. */ int count; /* The repeat count that was passed to the */ /* above command. */ int input_curpos; /* Whenever vi command mode is entered, the */ /* the position at which it was first left */ /* is recorded here. */ int command_curpos; /* Whenever vi command mode is entered, the */ /* the location of the cursor is recorded */ /* here. */ char input_char; /* Commands that call gl_read_character() */ /* record the character here, so that it can */ /* used on repeating the function. */ int saved; /* True if a function has been saved since the */ /* last call to gl_interpret_char(). */ int active; /* True while a function is being repeated. */ } ViRepeat; /* * The following datatype is used to encapsulate information specific * to vi mode. */ typedef struct { ViUndo undo; /* Information needed to implement the vi */ /* undo command. */ ViRepeat repeat; /* Information needed to implement the vi */ /* repeat command. */ int command; /* True in vi command-mode */ int find_forward; /* True if the last character search was in the */ /* forward direction. */ int find_onto; /* True if the last character search left the */ /* on top of the located character, as opposed */ /* to just before or after it. */ char find_char; /* The last character sought, or '\0' if no */ /* searches have been performed yet. */ } ViMode; #ifdef HAVE_SELECT /* * Define a type for recording a file-descriptor callback and its associated * data. */ typedef struct { GlFdEventFn *fn; /* The callback function */ void *data; /* Anonymous data to pass to the callback function */ } GlFdHandler; /* * A list of nodes of the following type is used to record file-activity * event handlers, but only on systems that have the select() system call. */ typedef struct GlFdNode GlFdNode; struct GlFdNode { GlFdNode *next; /* The next in the list of nodes */ int fd; /* The file descriptor being watched */ GlFdHandler rd; /* The callback to call when fd is readable */ GlFdHandler wr; /* The callback to call when fd is writable */ GlFdHandler ur; /* The callback to call when fd has urgent data */ }; /* * Set the number of the above structures to allocate every time that * the freelist of GlFdNode's becomes exhausted. */ #define GLFD_FREELIST_BLOCKING 10 /* * Listen for and handle file-descriptor events. */ static int gl_event_handler(GetLine *gl); static int gl_call_fd_handler(GetLine *gl, GlFdHandler *gfh, int fd, GlFdEvent event); #endif /* * Each signal that gl_get_line() traps is described by a list node * of the following type. */ typedef struct GlSignalNode GlSignalNode; struct GlSignalNode { GlSignalNode *next; /* The next signal in the list */ int signo; /* The number of the signal */ sigset_t proc_mask; /* A process mask which only includes signo */ #ifndef __MINGW32__ SigAction original; /* The signal disposition of the calling program */ /* for this signal. */ #endif unsigned flags; /* A bitwise union of GlSignalFlags enumerators */ GlAfterSignal after; /* What to do after the signal has been handled */ int errno_value; /* What to set errno to */ }; /* * Set the number of the above structures to allocate every time that * the freelist of GlSignalNode's becomes exhausted. */ #define GLS_FREELIST_BLOCKING 30 /* * Set the largest key-sequence that can be handled. */ #define GL_KEY_MAX 64 /* * Define the contents of the GetLine object. * Note that the typedef for this object can be found in libtecla.h. */ struct GetLine { GlHistory *glh; /* The line-history buffer */ WordCompletion *cpl; /* String completion resource object */ CplMatchFn(*cpl_fn); /* The tab completion callback function */ void *cpl_data; /* Callback data to pass to cpl_fn() */ ExpandFile *ef; /* ~user/, $envvar and wildcard expansion */ /* resource object. */ StringGroup *capmem; /* Memory for recording terminal capability */ /* strings. */ int input_fd; /* The file descriptor to read on */ int output_fd; /* The file descriptor to write to */ FILE *input_fp; /* A stream wrapper around input_fd */ FILE *output_fp; /* A stream wrapper around output_fd */ FILE *file_fp; /* When input is being temporarily taken from */ /* a file, this is its file-pointer. Otherwise */ /* it is NULL. */ char *term; /* The terminal type specified on the last call */ /* to gl_change_terminal(). */ int is_term; /* True if stdin is a terminal */ int is_net; /* True if the in/out is a network connection */ int net_may_block; /* True if we may block if reading from the net */ int net_read_attempt; /* True if attempt to read from the net */ char keyseq[GL_KEY_MAX+1]; /* A special key sequence being read */ int nkey; /* The number of characters in the key sequence */ int user_event_value; /* The user event value code */ size_t linelen; /* The max number of characters per line */ char *line; /* A line-input buffer of allocated size */ /* linelen+2. The extra 2 characters are */ /* reserved for "\n\0". */ char *cutbuf; /* A cut-buffer of the same size as line[] */ const char *prompt; /* The current prompt string */ int prompt_len; /* The length of the prompt string */ int prompt_changed; /* True after a callback changes the prompt */ int prompt_style; /* How the prompt string is displayed */ FreeList *sig_mem; /* Memory for nodes of the signal list */ GlSignalNode *sigs; /* The head of the list of signals */ sigset_t old_signal_set; /* The signal set on entry to gl_get_line() */ sigset_t new_signal_set; /* The set of signals that we are trapping */ #ifndef __MINGW32__ Termios oldattr; /* Saved terminal attributes. */ #endif KeyTab *bindings; /* A table of key-bindings */ int ntotal; /* The number of characters in gl->line[] */ int buff_curpos; /* The cursor position within gl->line[] */ int term_curpos; /* The cursor position on the terminal */ int buff_mark; /* A marker location in the buffer */ int insert_curpos; /* The cursor position at start of insert */ int insert; /* True in insert mode */ int number; /* If >= 0, a numeric argument is being read */ int endline; /* True to tell gl_get_input_line() to return */ /* the current contents of gl->line[] */ KtKeyFn *current_fn; /* The action function that is currently being */ /* invoked. */ int current_count; /* The repeat count passed to current_fn() */ GlhLineID preload_id; /* When not zero, this should be the ID of a */ /* line in the history buffer for potential */ /* recall. */ int preload_history; /* If true, preload the above history line when */ /* gl_get_input_line() is next called. */ long keyseq_count; /* The number of key sequences entered by the */ /* the user since new_GetLine() was called. */ long last_search; /* The value of oper_count during the last */ /* history search operation. */ GlEditor editor; /* The style of editing, (eg. vi or emacs) */ int silence_bell; /* True if gl_ring_bell() should do nothing. */ ViMode vi; /* Parameters used when editing in vi mode */ const char *left; /* The string that moves the cursor 1 character */ /* left. */ const char *right; /* The string that moves the cursor 1 character */ /* right. */ const char *up; /* The string that moves the cursor 1 character */ /* up. */ const char *down; /* The string that moves the cursor 1 character */ /* down. */ const char *home; /* The string that moves the cursor home */ const char *bol; /* Move cursor to beginning of line */ const char *clear_eol; /* The string that clears from the cursor to */ /* the end of the line. */ const char *clear_eod; /* The string that clears from the cursor to */ /* the end of the display. */ const char *u_arrow; /* The string returned by the up-arrow key */ const char *d_arrow; /* The string returned by the down-arrow key */ const char *l_arrow; /* The string returned by the left-arrow key */ const char *r_arrow; /* The string returned by the right-arrow key */ const char *sound_bell; /* The string needed to ring the terminal bell */ const char *bold; /* Switch to the bold font */ const char *underline; /* Underline subsequent characters */ const char *standout; /* Turn on standout mode */ const char *dim; /* Switch to a dim font */ const char *reverse; /* Turn on reverse video */ const char *blink; /* Switch to a blinking font */ const char *text_attr_off; /* Turn off all text attributes */ int nline; /* The height of the terminal in lines */ int ncolumn; /* The width of the terminal in columns */ #ifdef USE_TERMCAP char *tgetent_buf; /* The buffer that is used by tgetent() to */ /* store a terminal description. */ char *tgetstr_buf; /* The buffer that is used by tgetstr() to */ /* store terminal capabilities. */ #endif #ifdef USE_TERMINFO const char *left_n; /* The parameter string that moves the cursor */ /* n characters left. */ const char *right_n; /* The parameter string that moves the cursor */ /* n characters right. */ #endif char *app_file; /* The pathname of the application-specific */ /* .teclarc configuration file, or NULL. */ char *user_file; /* The pathname of the user-specific */ /* .teclarc configuration file, or NULL. */ int configured; /* True as soon as any teclarc configuration */ /* file has been read. */ int echo; /* True to display the line as it is being */ /* entered. If 0, only the prompt will be */ /* displayed, and the line will not be */ /* archived in the history list. */ int last_signal; /* The last signal that was caught by */ /* the last call to gl_get_line(), or -1 */ /* if no signal has been caught yet. */ #ifdef HAVE_SELECT FreeList *fd_node_mem; /* A freelist of GlFdNode structures */ GlFdNode *fd_nodes; /* The list of fd event descriptions */ fd_set rfds; /* The set of fds to watch for readability */ fd_set wfds; /* The set of fds to watch for writability */ fd_set ufds; /* The set of fds to watch for urgent data */ int max_fd; /* The maximum file-descriptor being watched */ #endif }; /* * Define the max amount of space needed to store a termcap terminal * description. Unfortunately this has to be done by guesswork, so * there is the potential for buffer overflows if we guess too small. * Fortunately termcap has been replaced by terminfo on most * platforms, and with terminfo this isn't an issue. The value that I * am using here is the conventional value, as recommended by certain * web references. */ #ifdef USE_TERMCAP #define TERMCAP_BUF_SIZE 2048 #endif /* * Set the size of the string segments used to store terminal capability * strings. */ #define CAPMEM_SEGMENT_SIZE 512 /* * If no terminal size information is available, substitute the * following vt100 default sizes. */ #define GL_DEF_NLINE 24 #define GL_DEF_NCOLUMN 80 /* * List the signals that we need to catch. In general these are * those that by default terminate or suspend the process, since * in such cases we need to restore terminal settings. */ static const struct GlDefSignal { int signo; /* The number of the signal */ unsigned flags; /* A bitwise union of GlSignalFlags enumerators */ GlAfterSignal after; /* What to do after the signal has been delivered */ int errno_value; /* What to set errno to */ } gl_signal_list[] = { {SIGABRT, GLS_SUSPEND_INPUT, GLS_ABORT, EINTR}, {SIGINT, GLS_SUSPEND_INPUT, GLS_ABORT, EINTR}, {SIGTERM, GLS_SUSPEND_INPUT, GLS_ABORT, EINTR}, #if defined (SIGALRM) {SIGALRM, GLS_RESTORE_ENV, GLS_CONTINUE, 0}, #endif #if defined (SIGCONT) {SIGCONT, GLS_RESTORE_ENV, GLS_CONTINUE, 0}, #endif #if defined(SIGHUP) #ifdef ENOTTY {SIGHUP, GLS_SUSPEND_INPUT, GLS_ABORT, ENOTTY}, #else {SIGHUP, GLS_SUSPEND_INPUT, GLS_ABORT, EINTR}, #endif #endif #if defined(SIGPIPE) #ifdef EPIPE {SIGPIPE, GLS_SUSPEND_INPUT, GLS_ABORT, EPIPE}, #else {SIGPIPE, GLS_SUSPEND_INPUT, GLS_ABORT, EINTR}, #endif #endif #ifdef SIGPWR {SIGPWR, GLS_RESTORE_ENV, GLS_CONTINUE, 0}, #endif #ifdef SIGQUIT {SIGQUIT, GLS_SUSPEND_INPUT, GLS_ABORT, EINTR}, #endif #ifdef SIGTSTP {SIGTSTP, GLS_SUSPEND_INPUT, GLS_CONTINUE, 0}, #endif #ifdef SIGTTIN {SIGTTIN, GLS_SUSPEND_INPUT, GLS_CONTINUE, 0}, #endif #ifdef SIGTTOU {SIGTTOU, GLS_SUSPEND_INPUT, GLS_CONTINUE, 0}, #endif #ifdef SIGUSR1 {SIGUSR1, GLS_RESTORE_ENV, GLS_CONTINUE, 0}, #endif #ifdef SIGUSR2 {SIGUSR2, GLS_RESTORE_ENV, GLS_CONTINUE, 0}, #endif #ifdef SIGVTALRM {SIGVTALRM, GLS_RESTORE_ENV, GLS_CONTINUE, 0}, #endif #ifdef SIGWINCH {SIGWINCH, GLS_RESTORE_ENV, GLS_CONTINUE, 0}, #endif #ifdef SIGXCPU {SIGXCPU, GLS_RESTORE_ENV, GLS_CONTINUE, 0}, #endif }; /* * Define file-scope variables for use in signal handlers. */ static volatile sig_atomic_t gl_pending_signal = -1; static sigjmp_buf gl_setjmp_buffer; static void gl_signal_handler(int signo); static int gl_check_caught_signal(GetLine *gl); /* * Unfortunately both terminfo and termcap require one to use the tputs() * function to output terminal control characters, and this function * doesn't allow one to specify a file stream. As a result, the following * file-scope variable is used to pass the current output file stream. * This is bad, but there doesn't seem to be any alternative. */ #if defined(USE_TERMINFO) || defined(USE_TERMCAP) static FILE *tputs_fp = NULL; #endif /* * Define a tab to be a string of 8 spaces. */ #define TAB_WIDTH 8 /* * Does the system send us SIGWINCH signals when the terminal size * changes? */ #ifdef USE_SIGWINCH static int gl_resize_terminal(GetLine *gl, int redisplay); #endif /* * Getline calls this to temporarily override certain signal handlers * of the calling program. */ static int gl_override_signal_handlers(GetLine *gl); /* * Getline calls this to restore the signal handlers of the calling * program. */ static int gl_restore_signal_handlers(GetLine *gl); /* * Put the terminal into raw input mode, after saving the original * terminal attributes in gl->oldattr. */ static int gl_raw_terminal_mode(GetLine *gl); /* * Restore the terminal attributes from gl->oldattr. */ static int gl_restore_terminal_attributes(GetLine *gl); /* * Read a line from the user in raw mode. */ static int gl_get_input_line(GetLine *gl, const char *start_line, int start_pos, int val); /* * Handle the receipt of the potential start of a new key-sequence from * the user. */ static int gl_interpret_char(GetLine *gl, char c); /* * Bind a single control or meta character to an action. */ static int gl_bind_control_char(GetLine *gl, KtBinder binder, char c, const char *action); /* * Set up terminal-specific key bindings. */ static int gl_bind_terminal_keys(GetLine *gl); /* * Lookup terminal control string and size information. */ static int gl_control_strings(GetLine *gl, const char *term); /* * Wrappers around the terminfo and termcap functions that lookup * strings in the terminal information databases. */ #ifdef USE_TERMINFO static const char *gl_tigetstr(GetLine *gl, const char *name); #elif defined(USE_TERMCAP) static const char *gl_tgetstr(GetLine *gl, const char *name, char **bufptr); #endif /* * Output a binary string directly to the terminal. */ static int gl_output_raw_string(GetLine *gl, const char *string); /* * Output a terminal control sequence. */ static int gl_output_control_sequence(GetLine *gl, int nline, const char *string); /* * Output a character or string to the terminal after converting tabs * to spaces and control characters to a caret followed by the modified * character. */ static int gl_output_char(GetLine *gl, char c, char pad); static int gl_output_string(GetLine *gl, const char *string, char pad); /* * Delete nc characters starting from the one under the cursor. * Optionally copy the deleted characters to the cut buffer. */ static int gl_delete_chars(GetLine *gl, int nc, int cut); /* * Add a character to the line buffer at the current cursor position, * inserting or overwriting according the current mode. */ static int gl_add_char_to_line(GetLine *gl, char c); /* * Insert/append a string to the line buffer and terminal at the current * cursor position. */ static int gl_add_string_to_line(GetLine *gl, const char *s); /* * Read a single character from the terminal. */ static int gl_read_character(GetLine *gl, char *c, int val); /* * Move the terminal cursor n positions to the left or right. */ static int gl_terminal_move_cursor(GetLine *gl, int n); /* * Move the terminal cursor to a given position. */ static int gl_set_term_curpos(GetLine *gl, int term_curpos); /* * Set the position of the cursor both in the line input buffer and on the * terminal. */ /* static int gl_place_cursor(GetLine *gl, int buff_curpos); */ /* * Return the terminal cursor position that corresponds to a given * line buffer cursor position. */ static int gl_buff_curpos_to_term_curpos(GetLine *gl, int buff_curpos); /* * Return the number of terminal characters needed to display a * given raw character. */ static int gl_displayed_char_width(GetLine *gl, char c, int term_curpos); /* * Return the number of terminal characters needed to display a * given substring. */ static int gl_displayed_string_width(GetLine *gl, const char *string, int nc, int term_curpos); /* * Return non-zero if 'c' is to be considered part of a word. */ static int gl_is_word_char(int c); /* * Read a tecla configuration file. */ static int _gl_read_config_file(GetLine *gl, const char *filename, KtBinder who); /* * Read a tecla configuration string. */ static int _gl_read_config_string(GetLine *gl, const char *buffer, KtBinder who); /* * Define the callback function used by _gl_parse_config_line() to * read the next character of a configuration stream. */ #define GLC_GETC_FN(fn) int (fn)(void *stream) typedef GLC_GETC_FN(GlcGetcFn); static GLC_GETC_FN(glc_file_getc); /* Read from a file */ static GLC_GETC_FN(glc_buff_getc); /* Read from a string */ /* * Parse a single configuration command line. */ static int _gl_parse_config_line(GetLine *gl, void *stream, GlcGetcFn *getc_fn, const char *origin, KtBinder who, int *lineno); /* * Bind the actual arrow key bindings to match those of the symbolic * arrow-key bindings. */ static int _gl_bind_arrow_keys(GetLine *gl); /* * Copy the binding of the specified symbolic arrow-key binding to * the terminal specific, and default arrow-key key-sequences. */ static int _gl_rebind_arrow_key(KeyTab *bindings, const char *name, const char *term_seq, const char *def_seq1, const char *def_seq2); /* * After the gl_read_from_file() action has been used to tell gl_get_line() * to temporarily read input from a file, gl_revert_input() arranges * for input to be reverted to the input stream last registered with * gl_change_terminal(). */ static void gl_revert_input(GetLine *gl); /* * Flush unwritten characters to the terminal. */ static int gl_flush_output(GetLine *gl); /* * Change the editor style being emulated. */ static int gl_change_editor(GetLine *gl, GlEditor editor); /* * Searching in a given direction, return the index of a given (or * read) character in the input line, or the character that precedes * it in the specified search direction. Return -1 if not found. */ static int gl_find_char(GetLine *gl, int count, int forward, int onto, char c); /* * Return the buffer index of the nth word ending after the cursor. */ static int gl_nth_word_end_forward(GetLine *gl, int n); /* * Return the buffer index of the nth word start after the cursor. */ static int gl_nth_word_start_forward(GetLine *gl, int n); /* * Return the buffer index of the nth word start before the cursor. */ static int gl_nth_word_start_backward(GetLine *gl, int n); /* * When called when vi command mode is enabled, this function saves the * current line and cursor position for potential restoration later * by the vi undo command. */ static void gl_save_for_undo(GetLine *gl); /* * If in vi mode, switch to vi command mode. */ static void gl_vi_command_mode(GetLine *gl); /* * In vi mode this is used to delete up to or onto a given or read * character in the input line. Also switch to insert mode if requested * after the deletion. */ static int gl_delete_find(GetLine *gl, int count, char c, int forward, int onto, int change); /* * Copy the characters between the cursor and the count'th instance of * a specified (or read) character in the input line, into the cut buffer. */ static int gl_copy_find(GetLine *gl, int count, char c, int forward, int onto); /* * Return the line index of the parenthesis that either matches the one under * the cursor, or not over a parenthesis character, the index of the next * close parenthesis. Return -1 if not found. */ static int gl_index_of_matching_paren(GetLine *gl); /* * Replace a malloc'd string (or NULL), with another malloc'd copy of * a string (or NULL). */ static int gl_record_string(char **sptr, const char *string); /* * Enumerate text display attributes as powers of two, suitable for * use in a bit-mask. */ typedef enum { GL_TXT_STANDOUT=1, /* Display text highlighted */ GL_TXT_UNDERLINE=2, /* Display text underlined */ GL_TXT_REVERSE=4, /* Display text with reverse video */ GL_TXT_BLINK=8, /* Display blinking text */ GL_TXT_DIM=16, /* Display text in a dim font */ GL_TXT_BOLD=32 /* Display text using a bold font */ } GlTextAttr; /* * Display the prompt regardless of the current visibility mode. */ static int gl_display_prompt(GetLine *gl); /* * Return the number of characters used by the prompt on the terminal. */ static int gl_displayed_prompt_width(GetLine *gl); /* * Prepare the return the current input line to the caller of gl_get_line(). */ static int gl_line_ended(GetLine *gl, int newline_char, int archive); /* * Set the maximum length of a line in a user's tecla configuration * file (not counting comments). */ #define GL_CONF_BUFLEN 100 /* * Set the maximum number of arguments supported by individual commands * in tecla configuration files. */ #define GL_CONF_MAXARG 10 /* * Prototype the available action functions. */ static KT_KEY_FN(gl_user_interrupt); static KT_KEY_FN(gl_abort); static KT_KEY_FN(gl_suspend); static KT_KEY_FN(gl_stop_output); static KT_KEY_FN(gl_start_output); static KT_KEY_FN(gl_literal_next); static KT_KEY_FN(gl_cursor_left); static KT_KEY_FN(gl_cursor_right); static KT_KEY_FN(gl_insert_mode); static KT_KEY_FN(gl_beginning_of_line); static KT_KEY_FN(gl_end_of_line); static KT_KEY_FN(gl_delete_line); static KT_KEY_FN(gl_kill_line); static KT_KEY_FN(gl_forward_word); static KT_KEY_FN(gl_backward_word); static KT_KEY_FN(gl_forward_delete_char); static KT_KEY_FN(gl_backward_delete_char); static KT_KEY_FN(gl_forward_delete_word); static KT_KEY_FN(gl_backward_delete_word); static KT_KEY_FN(gl_delete_refind); static KT_KEY_FN(gl_delete_invert_refind); static KT_KEY_FN(gl_delete_to_column); static KT_KEY_FN(gl_delete_to_parenthesis); static KT_KEY_FN(gl_forward_delete_find); static KT_KEY_FN(gl_backward_delete_find); static KT_KEY_FN(gl_forward_delete_to); static KT_KEY_FN(gl_backward_delete_to); static KT_KEY_FN(gl_upcase_word); static KT_KEY_FN(gl_downcase_word); static KT_KEY_FN(gl_capitalize_word); static KT_KEY_FN(gl_redisplay); static KT_KEY_FN(gl_clear_screen); static KT_KEY_FN(gl_transpose_chars); static KT_KEY_FN(gl_set_mark); static KT_KEY_FN(gl_exchange_point_and_mark); static KT_KEY_FN(gl_kill_region); static KT_KEY_FN(gl_copy_region_as_kill); static KT_KEY_FN(gl_yank); static KT_KEY_FN(gl_up_history); static KT_KEY_FN(gl_down_history); static KT_KEY_FN(gl_history_search_backward); static KT_KEY_FN(gl_history_re_search_backward); static KT_KEY_FN(gl_history_search_forward); static KT_KEY_FN(gl_history_re_search_forward); static KT_KEY_FN(gl_complete_word); static KT_KEY_FN(gl_expand_filename); static KT_KEY_FN(gl_del_char_or_list_or_eof); static KT_KEY_FN(gl_list_or_eof); static KT_KEY_FN(gl_read_from_file); static KT_KEY_FN(gl_beginning_of_history); static KT_KEY_FN(gl_end_of_history); static KT_KEY_FN(gl_digit_argument); static KT_KEY_FN(gl_newline); static KT_KEY_FN(gl_repeat_history); static KT_KEY_FN(gl_vi_insert); static KT_KEY_FN(gl_vi_overwrite); static KT_KEY_FN(gl_change_case); static KT_KEY_FN(gl_vi_insert_at_bol); static KT_KEY_FN(gl_vi_append_at_eol); static KT_KEY_FN(gl_vi_append); static KT_KEY_FN(gl_list_glob); static KT_KEY_FN(gl_backward_kill_line); static KT_KEY_FN(gl_goto_column); static KT_KEY_FN(gl_forward_to_word); static KT_KEY_FN(gl_vi_replace_char); static KT_KEY_FN(gl_vi_change_rest_of_line); static KT_KEY_FN(gl_vi_change_line); static KT_KEY_FN(gl_vi_change_to_bol); static KT_KEY_FN(gl_vi_change_refind); static KT_KEY_FN(gl_vi_change_invert_refind); static KT_KEY_FN(gl_vi_change_to_column); static KT_KEY_FN(gl_vi_change_to_parenthesis); static KT_KEY_FN(gl_vi_forward_change_word); static KT_KEY_FN(gl_vi_backward_change_word); static KT_KEY_FN(gl_vi_forward_change_find); static KT_KEY_FN(gl_vi_backward_change_find); static KT_KEY_FN(gl_vi_forward_change_to); static KT_KEY_FN(gl_vi_backward_change_to); static KT_KEY_FN(gl_vi_forward_change_char); static KT_KEY_FN(gl_vi_backward_change_char); static KT_KEY_FN(gl_forward_copy_char); static KT_KEY_FN(gl_backward_copy_char); static KT_KEY_FN(gl_forward_find_char); static KT_KEY_FN(gl_backward_find_char); static KT_KEY_FN(gl_forward_to_char); static KT_KEY_FN(gl_backward_to_char); static KT_KEY_FN(gl_repeat_find_char); static KT_KEY_FN(gl_invert_refind_char); static KT_KEY_FN(gl_append_yank); static KT_KEY_FN(gl_backward_copy_word); static KT_KEY_FN(gl_forward_copy_word); static KT_KEY_FN(gl_copy_to_bol); static KT_KEY_FN(gl_copy_refind); static KT_KEY_FN(gl_copy_invert_refind); static KT_KEY_FN(gl_copy_to_column); static KT_KEY_FN(gl_copy_to_parenthesis); static KT_KEY_FN(gl_copy_rest_of_line); static KT_KEY_FN(gl_copy_line); static KT_KEY_FN(gl_backward_copy_find); static KT_KEY_FN(gl_forward_copy_find); static KT_KEY_FN(gl_backward_copy_to); static KT_KEY_FN(gl_forward_copy_to); static KT_KEY_FN(gl_vi_undo); static KT_KEY_FN(gl_emacs_editing_mode); static KT_KEY_FN(gl_vi_editing_mode); static KT_KEY_FN(gl_ring_bell); static KT_KEY_FN(gl_vi_repeat_change); static KT_KEY_FN(gl_find_parenthesis); static KT_KEY_FN(gl_read_init_files); static KT_KEY_FN(gl_list_history); static KT_KEY_FN(gl_user_event1); static KT_KEY_FN(gl_user_event2); static KT_KEY_FN(gl_user_event3); static KT_KEY_FN(gl_user_event4); /* * Name the available action functions. */ static const struct {const char *name; KT_KEY_FN(*fn);} gl_actions[] = { {"user-interrupt", gl_user_interrupt}, {"abort", gl_abort}, {"suspend", gl_suspend}, {"stop-output", gl_stop_output}, {"start-output", gl_start_output}, {"literal-next", gl_literal_next}, {"cursor-right", gl_cursor_right}, {"cursor-left", gl_cursor_left}, {"insert-mode", gl_insert_mode}, {"beginning-of-line", gl_beginning_of_line}, {"end-of-line", gl_end_of_line}, {"delete-line", gl_delete_line}, {"kill-line", gl_kill_line}, {"forward-word", gl_forward_word}, {"backward-word", gl_backward_word}, {"forward-delete-char", gl_forward_delete_char}, {"backward-delete-char", gl_backward_delete_char}, {"forward-delete-word", gl_forward_delete_word}, {"backward-delete-word", gl_backward_delete_word}, {"delete-refind", gl_delete_refind}, {"delete-invert-refind", gl_delete_invert_refind}, {"delete-to-column", gl_delete_to_column}, {"delete-to-parenthesis", gl_delete_to_parenthesis}, {"forward-delete-find", gl_forward_delete_find}, {"backward-delete-find", gl_backward_delete_find}, {"forward-delete-to", gl_forward_delete_to}, {"backward-delete-to", gl_backward_delete_to}, {"upcase-word", gl_upcase_word}, {"downcase-word", gl_downcase_word}, {"capitalize-word", gl_capitalize_word}, {"redisplay", gl_redisplay}, {"clear-screen", gl_clear_screen}, {"transpose-chars", gl_transpose_chars}, {"set-mark", gl_set_mark}, {"exchange-point-and-mark", gl_exchange_point_and_mark}, {"kill-region", gl_kill_region}, {"copy-region-as-kill", gl_copy_region_as_kill}, {"yank", gl_yank}, {"up-history", gl_up_history}, {"down-history", gl_down_history}, {"history-search-backward", gl_history_search_backward}, {"history-re-search-backward", gl_history_re_search_backward}, {"history-search-forward", gl_history_search_forward}, {"history-re-search-forward", gl_history_re_search_forward}, {"complete-word", gl_complete_word}, {"expand-filename", gl_expand_filename}, {"del-char-or-list-or-eof", gl_del_char_or_list_or_eof}, {"read-from-file", gl_read_from_file}, {"beginning-of-history", gl_beginning_of_history}, {"end-of-history", gl_end_of_history}, {"digit-argument", gl_digit_argument}, {"newline", gl_newline}, {"repeat-history", gl_repeat_history}, {"vi-insert", gl_vi_insert}, {"vi-overwrite", gl_vi_overwrite}, {"vi-insert-at-bol", gl_vi_insert_at_bol}, {"vi-append-at-eol", gl_vi_append_at_eol}, {"vi-append", gl_vi_append}, {"change-case", gl_change_case}, {"list-glob", gl_list_glob}, {"backward-kill-line", gl_backward_kill_line}, {"goto-column", gl_goto_column}, {"forward-to-word", gl_forward_to_word}, {"vi-replace-char", gl_vi_replace_char}, {"vi-change-rest-of-line", gl_vi_change_rest_of_line}, {"vi-change-line", gl_vi_change_line}, {"vi-change-to-bol", gl_vi_change_to_bol}, {"vi-change-refind", gl_vi_change_refind}, {"vi-change-invert-refind", gl_vi_change_invert_refind}, {"vi-change-to-column", gl_vi_change_to_column}, {"vi-change-to-parenthesis", gl_vi_change_to_parenthesis}, {"forward-copy-char", gl_forward_copy_char}, {"backward-copy-char", gl_backward_copy_char}, {"forward-find-char", gl_forward_find_char}, {"backward-find-char", gl_backward_find_char}, {"forward-to-char", gl_forward_to_char}, {"backward-to-char", gl_backward_to_char}, {"repeat-find-char", gl_repeat_find_char}, {"invert-refind-char", gl_invert_refind_char}, {"append-yank", gl_append_yank}, {"backward-copy-word", gl_backward_copy_word}, {"forward-copy-word", gl_forward_copy_word}, {"copy-to-bol", gl_copy_to_bol}, {"copy-refind", gl_copy_refind}, {"copy-invert-refind", gl_copy_invert_refind}, {"copy-to-column", gl_copy_to_column}, {"copy-to-parenthesis", gl_copy_to_parenthesis}, {"copy-rest-of-line", gl_copy_rest_of_line}, {"copy-line", gl_copy_line}, {"backward-copy-find", gl_backward_copy_find}, {"forward-copy-find", gl_forward_copy_find}, {"backward-copy-to", gl_backward_copy_to}, {"forward-copy-to", gl_forward_copy_to}, {"list-or-eof", gl_list_or_eof}, {"vi-undo", gl_vi_undo}, {"vi-backward-change-word", gl_vi_backward_change_word}, {"vi-forward-change-word", gl_vi_forward_change_word}, {"vi-backward-change-find", gl_vi_backward_change_find}, {"vi-forward-change-find", gl_vi_forward_change_find}, {"vi-backward-change-to", gl_vi_backward_change_to}, {"vi-forward-change-to", gl_vi_forward_change_to}, {"vi-backward-change-char", gl_vi_backward_change_char}, {"vi-forward-change-char", gl_vi_forward_change_char}, {"emacs-mode", gl_emacs_editing_mode}, {"vi-mode", gl_vi_editing_mode}, {"ring-bell", gl_ring_bell}, {"vi-repeat-change", gl_vi_repeat_change}, {"find-parenthesis", gl_find_parenthesis}, {"read-init-files", gl_read_init_files}, {"list-history", gl_list_history}, {"user-event1", gl_user_event1}, {"user-event2", gl_user_event2}, {"user-event3", gl_user_event3}, {"user-event4", gl_user_event4}, }; /* * Define the default key-bindings in emacs mode. */ static const KtKeyBinding gl_emacs_bindings[] = { {"right", "cursor-right"}, {"^F", "cursor-right"}, {"left", "cursor-left"}, {"^B", "cursor-left"}, {"M-i", "insert-mode"}, {"M-I", "insert-mode"}, {"^A", "beginning-of-line"}, {"^E", "end-of-line"}, {"^U", "delete-line"}, {"^K", "kill-line"}, {"M-f", "forward-word"}, {"M-F", "forward-word"}, {"M-b", "backward-word"}, {"M-B", "backward-word"}, {"^D", "del-char-or-list-or-eof"}, {"^H", "backward-delete-char"}, {"^?", "backward-delete-char"}, {"M-d", "forward-delete-word"}, {"M-D", "forward-delete-word"}, {"M-^H", "backward-delete-word"}, {"M-^?", "backward-delete-word"}, {"M-u", "upcase-word"}, {"M-U", "upcase-word"}, {"M-l", "downcase-word"}, {"M-L", "downcase-word"}, {"M-c", "capitalize-word"}, {"M-C", "capitalize-word"}, {"^R", "redisplay"}, {"^L", "clear-screen"}, {"^T", "transpose-chars"}, {"^@", "set-mark"}, {"^X^X", "exchange-point-and-mark"}, {"^W", "kill-region"}, {"M-w", "copy-region-as-kill"}, {"M-W", "copy-region-as-kill"}, {"^Y", "yank"}, {"^P", "up-history"}, {"up", "up-history"}, {"^N", "down-history"}, {"down", "down-history"}, {"M-p", "history-search-backward"}, {"M-P", "history-search-backward"}, {"M-n", "history-search-forward"}, {"M-N", "history-search-forward"}, {"\t", "complete-word"}, {"^X*", "expand-filename"}, {"^X^F", "read-from-file"}, {"^X^R", "read-init-files"}, {"^Xg", "list-glob"}, {"^XG", "list-glob"}, {"^Xh", "list-history"}, {"^XH", "list-history"}, {"M-<", "beginning-of-history"}, {"M->", "end-of-history"}, {"M-0", "digit-argument"}, {"M-1", "digit-argument"}, {"M-2", "digit-argument"}, {"M-3", "digit-argument"}, {"M-4", "digit-argument"}, {"M-5", "digit-argument"}, {"M-6", "digit-argument"}, {"M-7", "digit-argument"}, {"M-8", "digit-argument"}, {"M-9", "digit-argument"}, {"\r", "newline"}, {"\n", "newline"}, {"M-o", "repeat-history"}, {"M-C-v", "vi-mode"}, }; /* * Define the default key-bindings in vi mode. Note that in vi-mode * meta-key bindings are command-mode bindings. For example M-i first * switches to command mode if not already in that mode, then moves * the cursor one position right, as in vi. */ static const KtKeyBinding gl_vi_bindings[] = { {"^D", "list-or-eof"}, {"^G", "list-glob"}, {"^H", "backward-delete-char"}, {"\t", "complete-word"}, {"\r", "newline"}, {"\n", "newline"}, {"^L", "clear-screen"}, {"^N", "down-history"}, {"^P", "up-history"}, {"^R", "redisplay"}, {"^U", "backward-kill-line"}, {"^W", "backward-delete-word"}, {"^X^F", "read-from-file"}, {"^X^R", "read-init-files"}, {"^X*", "expand-filename"}, {"^?", "backward-delete-char"}, {"M- ", "cursor-right"}, {"M-$", "end-of-line"}, {"M-*", "expand-filename"}, {"M-+", "down-history"}, {"M--", "up-history"}, {"M-<", "beginning-of-history"}, {"M->", "end-of-history"}, {"M-^", "beginning-of-line"}, {"M-;", "repeat-find-char"}, {"M-,", "invert-refind-char"}, {"M-|", "goto-column"}, {"M-~", "change-case"}, {"M-.", "vi-repeat-change"}, {"M-%", "find-parenthesis"}, {"M-0", "digit-argument"}, {"M-1", "digit-argument"}, {"M-2", "digit-argument"}, {"M-3", "digit-argument"}, {"M-4", "digit-argument"}, {"M-5", "digit-argument"}, {"M-6", "digit-argument"}, {"M-7", "digit-argument"}, {"M-8", "digit-argument"}, {"M-9", "digit-argument"}, {"M-a", "vi-append"}, {"M-A", "vi-append-at-eol"}, {"M-b", "backward-word"}, {"M-B", "backward-word"}, {"M-C", "vi-change-rest-of-line"}, {"M-cb", "vi-backward-change-word"}, {"M-cB", "vi-backward-change-word"}, {"M-cc", "vi-change-line"}, {"M-ce", "vi-forward-change-word"}, {"M-cE", "vi-forward-change-word"}, {"M-cw", "vi-forward-change-word"}, {"M-cW", "vi-forward-change-word"}, {"M-cF", "vi-backward-change-find"}, {"M-cf", "vi-forward-change-find"}, {"M-cT", "vi-backward-change-to"}, {"M-ct", "vi-forward-change-to"}, {"M-c;", "vi-change-refind"}, {"M-c,", "vi-change-invert-refind"}, {"M-ch", "vi-backward-change-char"}, {"M-c^H", "vi-backward-change-char"}, {"M-c^?", "vi-backward-change-char"}, {"M-cl", "vi-forward-change-char"}, {"M-c ", "vi-forward-change-char"}, {"M-c^", "vi-change-to-bol"}, {"M-c0", "vi-change-to-bol"}, {"M-c$", "vi-change-rest-of-line"}, {"M-c|", "vi-change-to-column"}, {"M-c%", "vi-change-to-parenthesis"}, {"M-dh", "backward-delete-char"}, {"M-d^H", "backward-delete-char"}, {"M-d^?", "backward-delete-char"}, {"M-dl", "forward-delete-char"}, {"M-d ", "forward-delete-char"}, {"M-dd", "delete-line"}, {"M-db", "backward-delete-word"}, {"M-dB", "backward-delete-word"}, {"M-de", "forward-delete-word"}, {"M-dE", "forward-delete-word"}, {"M-dw", "forward-delete-word"}, {"M-dW", "forward-delete-word"}, {"M-dF", "backward-delete-find"}, {"M-df", "forward-delete-find"}, {"M-dT", "backward-delete-to"}, {"M-dt", "forward-delete-to"}, {"M-d;", "delete-refind"}, {"M-d,", "delete-invert-refind"}, {"M-d^", "backward-kill-line"}, {"M-d0", "backward-kill-line"}, {"M-d$", "kill-line"}, {"M-D", "kill-line"}, {"M-d|", "delete-to-column"}, {"M-d%", "delete-to-parenthesis"}, {"M-e", "forward-word"}, {"M-E", "forward-word"}, {"M-f", "forward-find-char"}, {"M-F", "backward-find-char"}, {"M--", "up-history"}, {"M-h", "cursor-left"}, {"M-H", "beginning-of-history"}, {"M-i", "vi-insert"}, {"M-I", "vi-insert-at-bol"}, {"M-j", "down-history"}, {"M-J", "history-search-forward"}, {"M-k", "up-history"}, {"M-K", "history-search-backward"}, {"M-l", "cursor-right"}, {"M-L", "end-of-history"}, {"M-n", "history-re-search-forward"}, {"M-N", "history-re-search-backward"}, {"M-p", "append-yank"}, {"M-P", "yank"}, {"M-r", "vi-replace-char"}, {"M-R", "vi-overwrite"}, {"M-s", "vi-forward-change-char"}, {"M-S", "vi-change-line"}, {"M-t", "forward-to-char"}, {"M-T", "backward-to-char"}, {"M-u", "vi-undo"}, {"M-w", "forward-to-word"}, {"M-W", "forward-to-word"}, {"M-x", "forward-delete-char"}, {"M-X", "backward-delete-char"}, {"M-yh", "backward-copy-char"}, {"M-y^H", "backward-copy-char"}, {"M-y^?", "backward-copy-char"}, {"M-yl", "forward-copy-char"}, {"M-y ", "forward-copy-char"}, {"M-ye", "forward-copy-word"}, {"M-yE", "forward-copy-word"}, {"M-yw", "forward-copy-word"}, {"M-yW", "forward-copy-word"}, {"M-yb", "backward-copy-word"}, {"M-yB", "backward-copy-word"}, {"M-yf", "forward-copy-find"}, {"M-yF", "backward-copy-find"}, {"M-yt", "forward-copy-to"}, {"M-yT", "backward-copy-to"}, {"M-y;", "copy-refind"}, {"M-y,", "copy-invert-refind"}, {"M-y^", "copy-to-bol"}, {"M-y0", "copy-to-bol"}, {"M-y$", "copy-rest-of-line"}, {"M-yy", "copy-line"}, {"M-Y", "copy-line"}, {"M-y|", "copy-to-column"}, {"M-y%", "copy-to-parenthesis"}, {"M-^E", "emacs-mode"}, {"M-^H", "cursor-left"}, {"M-^?", "cursor-left"}, {"M-^L", "clear-screen"}, {"M-^N", "down-history"}, {"M-^P", "up-history"}, {"M-^R", "redisplay"}, {"M-^D", "list-or-eof"}, {"M-\r", "newline"}, {"M-\t", "complete-word"}, {"M-\n", "newline"}, {"M-^X^R", "read-init-files"}, {"M-^Xh", "list-history"}, {"M-^XH", "list-history"}, {"down", "down-history"}, {"up", "up-history"}, {"left", "cursor-left"}, {"right", "cursor-right"}, }; /*....................................................................... * Create a new GetLine object. * * Input: * linelen size_t The maximum line length to allow for. * histlen size_t The number of bytes to allocate for recording * a circular buffer of history lines. * Output: * return GetLine * The new object, or NULL on error. */ GetLine *new_GetLine(size_t linelen, size_t histlen) { GetLine *gl; /* The object to be returned */ int i; /* * Check the arguments. */ if(linelen < 10) { fprintf(stderr, "new_GetLine: Line length too small.\n"); return NULL; }; /* * Allocate the container. */ gl = (GetLine *) malloc(sizeof(GetLine)); if(!gl) { fprintf(stderr, "new_GetLine: Insufficient memory.\n"); return NULL; }; /* * Before attempting any operation that might fail, initialize the * container at least up to the point at which it can safely be passed * to del_GetLine(). */ gl->glh = NULL; gl->cpl = NULL; gl->cpl_fn = cpl_file_completions; gl->cpl_data = NULL; gl->ef = NULL; gl->capmem = NULL; gl->term = NULL; gl->is_term = 0; gl->is_net = 0; gl->net_may_block = 0; gl->net_read_attempt = 0; gl->nkey = 0; gl->user_event_value = 0; gl->input_fd = -1; gl->output_fd = -1; gl->input_fp = NULL; gl->output_fp = NULL; gl->file_fp = NULL; gl->linelen = linelen; gl->line = NULL; gl->cutbuf = NULL; gl->linelen = linelen; gl->prompt = ""; gl->prompt_len = 0; gl->prompt_changed = 0; gl->prompt_style = GL_LITERAL_PROMPT; gl->vi.undo.line = NULL; gl->vi.undo.buff_curpos = 0; gl->vi.undo.ntotal = 0; gl->vi.undo.saved = 0; gl->vi.repeat.fn = 0; gl->vi.repeat.count = 0; gl->vi.repeat.input_curpos = 0; gl->vi.repeat.command_curpos = 0; gl->vi.repeat.input_char = '\0'; gl->vi.repeat.saved = 0; gl->vi.repeat.active = 0; gl->sig_mem = NULL; gl->sigs = NULL; #ifndef __MINGW32__ sigemptyset(&gl->old_signal_set); sigemptyset(&gl->new_signal_set); #endif gl->bindings = NULL; gl->ntotal = 0; gl->buff_curpos = 0; gl->term_curpos = 0; gl->buff_mark = 0; gl->insert_curpos = 0; gl->insert = 1; gl->number = -1; gl->endline = 0; gl->current_fn = 0; gl->current_count = 0; gl->preload_id = 0; gl->preload_history = 0; gl->keyseq_count = 0; gl->last_search = -1; gl->editor = GL_EMACS_MODE; gl->silence_bell = 0; gl->vi.command = 0; gl->vi.find_forward = 0; gl->vi.find_onto = 0; gl->vi.find_char = '\0'; gl->left = NULL; gl->right = NULL; gl->up = NULL; gl->down = NULL; gl->home = NULL; gl->bol = 0; gl->clear_eol = NULL; gl->clear_eod = NULL; gl->u_arrow = NULL; gl->d_arrow = NULL; gl->l_arrow = NULL; gl->r_arrow = NULL; gl->sound_bell = NULL; gl->bold = NULL; gl->underline = NULL; gl->standout = NULL; gl->dim = NULL; gl->reverse = NULL; gl->blink = NULL; gl->text_attr_off = NULL; gl->nline = 0; gl->ncolumn = 0; #ifdef USE_TERMINFO gl->left_n = NULL; gl->right_n = NULL; #elif defined(USE_TERMCAP) gl->tgetent_buf = NULL; gl->tgetstr_buf = NULL; #endif gl->app_file = NULL; gl->user_file = NULL; gl->configured = 0; gl->echo = 1; gl->last_signal = -1; #ifdef HAVE_SELECT gl->fd_node_mem = NULL; gl->fd_nodes = NULL; FD_ZERO(&gl->rfds); FD_ZERO(&gl->wfds); FD_ZERO(&gl->ufds); gl->max_fd = 0; #endif /* * Allocate the history buffer. */ gl->glh = _new_GlHistory(histlen); if(!gl->glh) return del_GetLine(gl); /* * Allocate the resource object for file-completion. */ gl->cpl = new_WordCompletion(); if(!gl->cpl) return del_GetLine(gl); /* * Allocate the resource object for file-completion. */ gl->ef = new_ExpandFile(); if(!gl->ef) return del_GetLine(gl); /* * Allocate a string-segment memory allocator for use in storing terminal * capablity strings. */ gl->capmem = _new_StringGroup(CAPMEM_SEGMENT_SIZE); if(!gl->capmem) return del_GetLine(gl); /* * Allocate a line buffer, leaving 2 extra characters for the terminating * '\n' and '\0' characters */ gl->line = (char *) malloc(linelen + 2); if(!gl->line) { fprintf(stderr, "new_GetLine: Insufficient memory to allocate line buffer.\n"); return del_GetLine(gl); }; gl->line[0] = '\0'; /* * Allocate a cut buffer. */ gl->cutbuf = (char *) malloc(linelen + 2); if(!gl->cutbuf) { fprintf(stderr, "new_GetLine: Insufficient memory to allocate cut buffer.\n"); return del_GetLine(gl); }; gl->cutbuf[0] = '\0'; /* * Allocate a vi undo buffer. */ gl->vi.undo.line = (char *) malloc(linelen + 2); if(!gl->vi.undo.line) { fprintf(stderr, "new_GetLine: Insufficient memory to allocate undo buffer.\n"); return del_GetLine(gl); }; gl->vi.undo.line[0] = '\0'; /* * Allocate a freelist from which to allocate nodes for the list * of signals. */ gl->sig_mem = _new_FreeList("new_GetLine", sizeof(GlSignalNode), GLS_FREELIST_BLOCKING); if(!gl->sig_mem) return del_GetLine(gl); /* * Install dispositions for the default list of signals that gl_get_line() * traps. */ for(i=0; isigno, sig->flags, sig->after, sig->errno_value)) return del_GetLine(gl); }; /* * Allocate an empty table of key bindings. */ gl->bindings = _new_KeyTab(); if(!gl->bindings) return del_GetLine(gl); /* * Define the available actions that can be bound to key sequences. */ for(i=0; ibindings, gl_actions[i].name, gl_actions[i].fn)) return del_GetLine(gl); }; /* * Set up the default bindings. */ if(gl_change_editor(gl, gl->editor)) return del_GetLine(gl); /* * Allocate termcap buffers. */ #ifdef USE_TERMCAP gl->tgetent_buf = (char *) malloc(TERMCAP_BUF_SIZE); gl->tgetstr_buf = (char *) malloc(TERMCAP_BUF_SIZE); if(!gl->tgetent_buf || !gl->tgetstr_buf) { fprintf(stderr, "new_GetLine: Insufficient memory for termcap buffers.\n"); return del_GetLine(gl); }; #endif /* * Set up for I/O assuming stdin and stdout. */ if(gl_change_terminal(gl, stdin, stdout, getenv("TERM"))) return del_GetLine(gl); /* * Create a freelist for use in allocating GlFdNode list nodes. */ #ifdef HAVE_SELECT gl->fd_node_mem = _new_FreeList("new_GetLine", sizeof(GlFdNode), GLFD_FREELIST_BLOCKING); if(!gl->fd_node_mem) return del_GetLine(gl); #endif /* * We are done for now. */ return gl; } /*....................................................................... * Delete a GetLine object. * * Input: * gl GetLine * The object to be deleted. * Output: * return GetLine * The deleted object (always NULL). */ GetLine *del_GetLine(GetLine *gl) { if(gl) { gl->glh = _del_GlHistory(gl->glh); gl->cpl = del_WordCompletion(gl->cpl); gl->ef = del_ExpandFile(gl->ef); gl->capmem = _del_StringGroup(gl->capmem); if(gl->line) free(gl->line); if(gl->cutbuf) free(gl->cutbuf); if(gl->vi.undo.line) free(gl->vi.undo.line); gl->sig_mem = _del_FreeList(NULL, gl->sig_mem, 1); gl->sigs = NULL; /* Already freed by freeing sig_mem */ gl->bindings = _del_KeyTab(gl->bindings); #ifdef USE_TERMCAP if(gl->tgetent_buf) free(gl->tgetent_buf); if(gl->tgetstr_buf) free(gl->tgetstr_buf); #endif if(gl->file_fp) fclose(gl->file_fp); if(gl->term) free(gl->term); #ifdef HAVE_SELECT gl->fd_node_mem = _del_FreeList(NULL, gl->fd_node_mem, 1); #endif free(gl); }; return NULL; } /*....................................................................... * Bind a control or meta character to an action. * * Input: * gl GetLine * The resource object of this program. * binder KtBinder The source of the binding. * c char The control or meta character. * If this is '\0', the call is ignored. * action const char * The action name to bind the key to. * Output: * return int 0 - OK. * 1 - Error. */ static int gl_bind_control_char(GetLine *gl, KtBinder binder, char c, const char *action) { char keyseq[2]; /* * Quietly reject binding to the NUL control character, since this * is an ambiguous prefix of all bindings. */ if(c == '\0') return 0; /* * Making sure not to bind characters which aren't either control or * meta characters. */ if(IS_CTRL_CHAR(c) || IS_META_CHAR(c)) { keyseq[0] = c; keyseq[1] = '\0'; } else { return 0; }; /* * Install the binding. */ return _kt_set_keybinding(gl->bindings, binder, keyseq, action); } /*....................................................................... * Read a line from the user. * * Input: * gl GetLine * A resource object returned by new_GetLine(). * prompt char * The prompt to prefix the line with. * start_line char * The initial contents of the input line, or NULL * if it should start out empty. * start_pos int If start_line isn't NULL, this specifies the * index of the character over which the cursor * should initially be positioned within the line. * If you just want it to follow the last character * of the line, send -1. * Output: * return char * An internal buffer containing the input line, or * NULL at the end of input. If the line fitted in * the buffer there will be a '\n' newline character * before the terminating '\0'. If it was truncated * there will be no newline character, and the remains * of the line should be retrieved via further calls * to this function. */ char *gl_get_line(GetLine *gl, const char *prompt, const char *start_line, int start_pos) { int waserr = 0; /* True if an error occurs */ gl->is_net = 0; /* Reset the 'is_net' flag */ gl->net_may_block = 0; gl->net_read_attempt = 0; gl->user_event_value = 0; /* * Check the arguments. */ if(!gl || !prompt) { fprintf(stderr, "gl_get_line: NULL argument(s).\n"); return NULL; }; /* * If this is the first call to this function since new_GetLine(), * complete any postponed configuration. */ if(!gl->configured) { (void) gl_configure_getline(gl, NULL, NULL, TECLA_CONFIG_FILE); gl->configured = 1; }; /* * If input is temporarily being taken from a file, return lines * from the file until the file is exhausted, then revert to * the normal input stream. */ if(gl->file_fp) { if(fgets(gl->line, gl->linelen, gl->file_fp)) return gl->line; gl_revert_input(gl); }; /* * Is input coming from a non-interactive source? */ if(!gl->is_term) return fgets(gl->line, gl->linelen, gl->input_fp); /* * Record the new prompt and its displayed width. */ gl_replace_prompt(gl, prompt); /* * Before installing our signal handler functions, record the fact * that there are no pending signals. */ gl_pending_signal = -1; /* * Temporarily override the signal handlers of the calling program, * so that we can intercept signals that would leave the terminal * in a bad state. */ waserr = gl_override_signal_handlers(gl); /* * After recording the current terminal settings, switch the terminal * into raw input mode. */ waserr = waserr || gl_raw_terminal_mode(gl); /* * Attempt to read the line. */ waserr = waserr || gl_get_input_line(gl, start_line, start_pos, -1); /* * Restore terminal settings. */ gl_restore_terminal_attributes(gl); /* * Restore the signal handlers. */ gl_restore_signal_handlers(gl); /* * Having restored the program terminal and signal environment, * re-submit any signals that were received. */ if(gl_pending_signal != -1) { raise(gl_pending_signal); waserr = 1; }; /* * If gl_get_input_line() aborted input due to the user asking to * temporarily read lines from a file, read the first line from * this file. */ if(!waserr && gl->file_fp) return gl_get_line(gl, prompt, NULL, 0); /* * Return the new input line. */ return waserr ? NULL : gl->line; } /*....................................................................... * Record of the signal handlers of the calling program, so that they * can be restored later. * * Input: * gl GetLine * The resource object of this library. * Output: * return int 0 - OK. * 1 - Error. */ static int gl_override_signal_handlers(GetLine *gl) { #ifndef __MINGW32__ GlSignalNode *sig; /* A node in the list of signals to be caught */ /* * Set up our signal handler. */ SigAction act; act.sa_handler = gl_signal_handler; sigemptyset(&act.sa_mask); act.sa_flags = 0; /* * Get the process signal mask so that we can see which signals the * calling program currently has blocked, and so that we can restore this * mask before returning to the calling program. */ if(sigprocmask(SIG_SETMASK, NULL, &gl->old_signal_set) == -1) { fprintf(stderr, "gl_get_line(): sigprocmask error: %s\n", strerror(errno)); return 1; }; /* * Form a new process signal mask from the list of signals that we have * been asked to trap. */ sigemptyset(&gl->new_signal_set); for(sig=gl->sigs; sig; sig=sig->next) { /* * Trap this signal? If it is blocked by the calling program and we * haven't been told to unblock it, don't arrange to trap this signal. */ if(sig->flags & GLS_UNBLOCK_SIG || !sigismember(&gl->old_signal_set, sig->signo)) { if(sigaddset(&gl->new_signal_set, sig->signo) == -1) { fprintf(stderr, "gl_get_line(): sigaddset error: %s\n", strerror(errno)); return 1; }; }; }; /* * Before installing our signal handlers, block all of the signals * that we are going to be trapping. */ if(sigprocmask(SIG_BLOCK, &gl->new_signal_set, NULL) == -1) { fprintf(stderr, "gl_get_line(): sigprocmask error: %s\n", strerror(errno)); return 1; }; /* * Override the actions of the signals that we are trapping. */ for(sig=gl->sigs; sig; sig=sig->next) { if(sigismember(&gl->new_signal_set, sig->signo) && sigaction(sig->signo, &act, &sig->original)) { fprintf(stderr, "gl_get_line(): sigaction error: %s\n", strerror(errno)); return 1; }; }; /* * Just in case a SIGWINCH signal was sent to the process while our * SIGWINCH signal handler wasn't in place, check to see if the terminal * size needs updating. */ #ifdef USE_SIGWINCH if (gl->is_term) { if(gl_resize_terminal(gl, 0)) return 1; } #endif #endif /* __MINGW32__ */ return 0; } /*....................................................................... * Restore the signal handlers of the calling program. * * Input: * gl GetLine * The resource object of this library. * Output: * return int 0 - OK. * 1 - Error. */ static int gl_restore_signal_handlers(GetLine *gl) { #ifndef __MINGW32__ GlSignalNode *sig; /* A node in the list of signals to be caught */ /* * Restore application signal handlers that were overriden * by gl_override_signal_handlers(). */ for(sig=gl->sigs; sig; sig=sig->next) { if(sigismember(&gl->new_signal_set, sig->signo) && sigaction(sig->signo, &sig->original, NULL)) { fprintf(stderr, "gl_get_line(): sigaction error: %s\n", strerror(errno)); return 1; }; }; /* * Restore the original signal mask. */ if(sigprocmask(SIG_SETMASK, &gl->old_signal_set, NULL) == -1) { fprintf(stderr, "gl_get_line(): sigprocmask error: %s\n", strerror(errno)); return 1; }; #endif /* __MINGW32__ */ return 0; } /*....................................................................... * This signal handler simply records the fact that a given signal was * caught in the file-scope gl_pending_signal variable. */ static void gl_signal_handler(int signo) { gl_pending_signal = signo; siglongjmp(gl_setjmp_buffer, 1); } /*....................................................................... * Switch the terminal into raw mode after storing the previous terminal * settings in gl->attributes. * * Input: * gl GetLine * The resource object of this program. * Output: * return int 0 - OK. * 1 - Error. */ static int gl_raw_terminal_mode(GetLine *gl) { #ifdef __MINGW32__ return 0; #else Termios newattr; /* The new terminal attributes */ /* * Record the current terminal attributes. */ if(tcgetattr(gl->input_fd, &gl->oldattr)) { fprintf(stderr, "getline(): tcgetattr error: %s\n", strerror(errno)); return 1; }; /* * This function shouldn't do anything but record the current terminal * attritubes if editing has been disabled. */ if(gl->editor == GL_NO_EDITOR) return 0; /* * Modify the existing attributes. */ newattr = gl->oldattr; /* * Turn off local echo, canonical input mode and extended input processing. */ newattr.c_lflag &= ~(ECHO | ICANON | IEXTEN); /* * Don't translate carriage return to newline, turn off input parity * checking, don't strip off 8th bit, turn off output flow control. */ newattr.c_iflag &= ~(ICRNL | INPCK | ISTRIP); /* * Clear size bits, turn off parity checking, and allow 8-bit characters. */ newattr.c_cflag &= ~(CSIZE | PARENB); newattr.c_cflag |= CS8; /* * Turn off output processing. */ newattr.c_oflag &= ~(OPOST); /* * Request one byte at a time, without waiting. */ newattr.c_cc[VMIN] = 1; newattr.c_cc[VTIME] = 0; /* * Install the new terminal modes. */ while(tcsetattr(gl->input_fd, TCSADRAIN, &newattr)) { if (errno != EINTR) { fprintf(stderr, "getline(): tcsetattr error: %s\n", strerror(errno)); return 1; }; }; return 0; #endif /* __MINGW32__ */ } /*....................................................................... * Restore the terminal attributes recorded in gl->oldattr. * * Input: * gl GetLine * The resource object of this library. * Output: * return int 0 - OK. * 1 - Error. */ static int gl_restore_terminal_attributes(GetLine *gl) { int waserr = 0; /* * Before changing the terminal attributes, make sure that all output * has been passed to the terminal. */ if(gl_flush_output(gl)) waserr = 1; #ifndef __MINGW32__ /* * Reset the terminal attributes to the values that they had on * entry to gl_get_line(). */ while(tcsetattr(gl->input_fd, TCSADRAIN, &gl->oldattr)) { if(errno != EINTR) { fprintf(stderr, "gl_get_line(): tcsetattr error: %s\n", strerror(errno)); waserr = 1; break; }; }; #endif return waserr; } /*....................................................................... * Read a new input line from the user. * * Input: * gl GetLine * The resource object of this library. * start_line char * The initial contents of the input line, or NULL * if it should start out empty. * start_pos int If start_line isn't NULL, this specifies the * index of the character over which the cursor * should initially be positioned within the line. * If you just want it to follow the last character * of the line, send -1. * val int If non-negative, the single-character value already * read from the network. * Output: * return int 0 - OK. * 1 - Error. */ static int gl_get_input_line(GetLine *gl, const char *start_line, int start_pos, int val) { char c; /* The character being read */ int is_endline = 0; int is_redraw = 0; /* * Reset the properties of the line. */ if (gl->endline) { gl->ntotal = 0; gl->buff_curpos = 0; gl->term_curpos = 0; gl->insert_curpos = 0; gl->number = -1; gl->endline = 0; gl->vi.command = 0; gl->vi.undo.line[0] = '\0'; gl->vi.undo.ntotal = 0; gl->vi.undo.buff_curpos = 0; gl->vi.repeat.fn = 0; gl->last_signal = -1; is_endline = 1; } else { if (! gl->is_net) gl->term_curpos = 0; } /* * Reset the history search pointers. */ if (is_endline) { if(_glh_cancel_search(gl->glh)) return 1; } /* * Draw the prompt at the start of the line. */ if (is_endline || ! gl->term_curpos) { if(gl_display_prompt(gl)) return 1; is_redraw = 1; } /* * Present an initial line? */ if(start_line) { char *cptr; /* A pointer into gl->line[] */ /* * Load the line into the buffer, and display it. */ if(start_line != gl->line) strncpy(gl->line, start_line, gl->linelen); gl->line[gl->linelen] = '\0'; gl->ntotal = strlen(gl->line); /* * Strip off any trailing newline and carriage return characters. */ for(cptr=gl->line + gl->ntotal - 1; cptr >= gl->line && (*cptr=='\n' || *cptr=='\r'); cptr--,gl->ntotal--) ; if(gl->ntotal < 0) gl->ntotal = 0; gl->line[gl->ntotal] = '\0'; /* * Display the string that remains. */ if (is_redraw) { if(gl_output_string(gl, gl->line, '\0')) return 1; } /* * Where should the cursor be placed within the line? */ if(start_pos < 0 || start_pos > gl->ntotal) { if(gl_place_cursor(gl, gl->ntotal)) return 1; } else { if(gl_place_cursor(gl, start_pos)) return 1; }; } else { gl->line[0] = '\0'; }; /* * Preload a history line? */ if(gl->preload_history) { gl->preload_history = 0; if(gl->preload_id) { if(_glh_recall_line(gl->glh, gl->preload_id, gl->line, gl->linelen)) { gl->ntotal = strlen(gl->line); gl->buff_curpos = strlen(gl->line); }; gl->preload_id = 0; }; gl_redisplay(gl, 1); }; /* * Read one character at a time. */ while(gl_read_character(gl, &c, val) == 0) { /* * Increment the count of the number of key sequences entered. */ gl->keyseq_count++; /* * Interpret the character either as the start of a new key-sequence, * as a continuation of a repeat count, or as a printable character * to be added to the line. */ if(gl_interpret_char(gl, c)) break; /* * If we just ran an action function which temporarily asked for * input to be taken from a file, abort this call. */ if(gl->file_fp) return 0; /* * Has the line been completed? */ if(gl->endline) return gl_line_ended(gl, isprint((int)(unsigned char) c) ? c : '\n', gl->echo && (c=='\n' || c=='\r')); if (gl->is_net) return 0; /* For network connection we read a character at a time */ }; /* * To get here, gl_read_character() must have returned non-zero. See * if this was because a signal was caught that requested that the * current line be returned. */ if(gl->endline) return gl_line_ended(gl, '\n', gl->echo); return 1; } /*....................................................................... * Add a character to the line buffer at the current cursor position, * inserting or overwriting according the current mode. * * Input: * gl GetLine * The resource object of this library. * c char The character to be added. * Output: * return int 0 - OK. * 1 - Insufficient room. */ static int gl_add_char_to_line(GetLine *gl, char c) { /* * Keep a record of the current cursor position. */ int buff_curpos = gl->buff_curpos; int term_curpos = gl->term_curpos; /* * Work out the displayed width of the new character. */ int width = gl_displayed_char_width(gl, c, term_curpos); /* * If we are in insert mode, or at the end of the line, * check that we can accomodate a new character in the buffer. * If not, simply return, leaving it up to the calling program * to check for the absence of a newline character. */ if((gl->insert || buff_curpos >= gl->ntotal) && gl->ntotal >= gl->linelen) return 0; /* * Are we adding characters to the line (ie. inserting or appending)? */ if(gl->insert || buff_curpos >= gl->ntotal) { /* * If inserting, make room for the new character. */ if(buff_curpos < gl->ntotal) { memmove(gl->line + buff_curpos + 1, gl->line + buff_curpos, gl->ntotal - buff_curpos); }; /* * Copy the character into the buffer. */ gl->line[buff_curpos] = c; gl->buff_curpos++; /* * If the line was extended, update the record of the string length * and terminate the extended string. */ gl->ntotal++; gl->line[gl->ntotal] = '\0'; /* * Redraw the line from the cursor position to the end of the line, * and move the cursor to just after the added character. */ if(gl_output_string(gl, gl->line + buff_curpos, '\0') || gl_set_term_curpos(gl, term_curpos + width)) return 1; /* * Are we overwriting an existing character? */ } else { /* * Get the widths of the character to be overwritten and the character * that is going to replace it. */ int old_width = gl_displayed_char_width(gl, gl->line[buff_curpos], term_curpos); /* * Overwrite the character in the buffer. */ gl->line[buff_curpos] = c; /* * If we are replacing with a narrower character, we need to * redraw the terminal string to the end of the line, then * overwrite the trailing old_width - width characters * with spaces. */ if(old_width > width) { if(gl_output_string(gl, gl->line + buff_curpos, '\0')) return 1; /* * Clear to the end of the terminal. */ if(gl_output_control_sequence(gl, gl->nline, gl->clear_eod)) return 1; /* * Move the cursor to the end of the new character. */ if(gl_set_term_curpos(gl, term_curpos + width)) return 1; gl->buff_curpos++; /* * If we are replacing with a wider character, then we will be * inserting new characters, and thus extending the line. */ } else if(width > old_width) { /* * Redraw the line from the cursor position to the end of the line, * and move the cursor to just after the added character. */ if(gl_output_string(gl, gl->line + buff_curpos, '\0') || gl_set_term_curpos(gl, term_curpos + width)) return 1; gl->buff_curpos++; /* * The original and replacement characters have the same width, * so simply overwrite. */ } else { /* * Copy the character into the buffer. */ gl->line[buff_curpos] = c; gl->buff_curpos++; /* * Overwrite the original character. */ if(gl_output_char(gl, c, gl->line[gl->buff_curpos])) return 1; }; }; return 0; } /*....................................................................... * Insert/append a string to the line buffer and terminal at the current * cursor position. * * Input: * gl GetLine * The resource object of this library. * s char * The string to be added. * Output: * return int 0 - OK. * 1 - Insufficient room. */ static int gl_add_string_to_line(GetLine *gl, const char *s) { int buff_slen; /* The length of the string being added to line[] */ int term_slen; /* The length of the string being written to the terminal */ int buff_curpos; /* The original value of gl->buff_curpos */ int term_curpos; /* The original value of gl->term_curpos */ /* * Keep a record of the current cursor position. */ buff_curpos = gl->buff_curpos; term_curpos = gl->term_curpos; /* * How long is the string to be added? */ buff_slen = strlen(s); term_slen = gl_displayed_string_width(gl, s, buff_slen, term_curpos); /* * Check that we can accomodate the string in the buffer. * If not, simply return, leaving it up to the calling program * to check for the absence of a newline character. */ if(gl->ntotal + buff_slen > gl->linelen) return 0; /* * Move the characters that follow the cursor in the buffer by * buff_slen characters to the right. */ if(gl->ntotal > gl->buff_curpos) { memmove(gl->line + gl->buff_curpos + buff_slen, gl->line + gl->buff_curpos, gl->ntotal - gl->buff_curpos); }; /* * Copy the string into the buffer. */ memcpy(gl->line + gl->buff_curpos, s, buff_slen); gl->ntotal += buff_slen; gl->buff_curpos += buff_slen; /* * Maintain the buffer properly terminated. */ gl->line[gl->ntotal] = '\0'; /* * Write the modified part of the line to the terminal, then move * the terminal cursor to the end of the displayed input string. */ if(gl_output_string(gl, gl->line + buff_curpos, '\0') || gl_set_term_curpos(gl, term_curpos + term_slen)) return 1; return 0; } /*....................................................................... * Read a single character from the terminal. * * Input: * gl GetLine * The resource object of this library. * val int If non-negative, the single-character value already * read from the network. * Output: * return int 0 - OK. * 1 - Either an I/O error occurred, or a signal was * caught who's disposition is to abort gl_get_line() * or to have gl_get_line() return the current line * as though the user had pressed return. In the * latter case gl->endline will be non-zero. */ static int gl_read_character(GetLine *gl, char *c, int val) { /* * We must take special care between blocking calls. */ if (gl->is_net) { if (gl->net_read_attempt + 1 > 0) gl->net_read_attempt++; if (gl->net_read_attempt > 1) return 1; /* XXX: don't read from the net more than once in a row */ } if (gl->net_may_block) return 1; /* * Before waiting for a new character to be input, flush unwritten * characters to the terminal. */ if(gl_flush_output(gl)) return 1; /* * We may have to repeat the read if window change signals are received. */ for(;;) { /* * If the endline flag becomes set, don't wait for another character. */ if(gl->endline) return 1; /* * If we have given the pre-read value, just set it and return. */ if (val >= 0) { *c = (char)val; return 0; } #ifndef __MINGW32__ /* * Since the code in this function can block, trap signals. */ if(sigsetjmp(gl_setjmp_buffer, 1)==0) { /* * Unblock the signals that we are trapping. */ if(sigprocmask(SIG_UNBLOCK, &gl->new_signal_set, NULL) == -1) { fprintf(stderr, "getline(): sigprocmask error: %s\n", strerror(errno)); return 1; }; #endif /* * If select() is available, watch for activity on any file descriptors * that the user has registered, and for data available on the terminal * file descriptor. */ #ifdef HAVE_SELECT if(gl_event_handler(gl)) return 1; #endif /* * Read one character from the terminal. This could take more * than one call if an interrupt that we aren't trapping is * received. */ #ifdef __MINGW32__ /* XXX: need terminal read implementation here */ #else while(read(gl->input_fd, (void *)c, 1) != 1) { if(errno != EINTR) { #ifdef EAGAIN if(!errno) /* This can happen with SysV O_NDELAY */ errno = EAGAIN; if ((errno == EAGAIN) && gl->is_net) { gl->net_may_block = 1; return 1; /* No more characters to read from the net */ } #elif defined(EWOULDBLOCK) if ((errno == EWOULDBLOCK) && gl->is_net) { gl->net_may_block = 1; return 1; /* No more characters to read from the net */ } #endif return 1; }; }; /* * Block all of the signals that we are trapping. */ if(sigprocmask(SIG_BLOCK, &gl->new_signal_set, NULL) == -1) { fprintf(stderr, "getline(): sigprocmask error: %s\n", strerror(errno)); return 1; }; return 0; }; /* * To get here, one of the signals that we are trapping must have * been received. Note that by using sigsetjmp() instead of setjmp() * the signal mask that was blocking these signals will have been * reinstated, so we can be sure that no more of these signals will * be received until we explicitly unblock them again. */ if(gl_check_caught_signal(gl)) return 1; #endif /* __MINGW32__ */ }; /* for (;;) */ } /*....................................................................... * This function is called to handle signals caught between calls to * sigsetjmp() and siglongjmp(). * * Input: * gl GetLine * The resource object of this library. * Output: * return int 0 - Signal handled internally. * 1 - Signal requires gl_get_line() to abort. */ static int gl_check_caught_signal(GetLine *gl) { #ifdef __MINGW32__ return 0; #else GlSignalNode *sig; /* The signal disposition */ SigAction keep_action; /* The signal disposition of tecla signal handlers */ /* * Was no signal caught? */ if(gl_pending_signal == -1) return 0; /* * Record the signal that was caught, so that the user can query it later. */ gl->last_signal = gl_pending_signal; /* * Did we receive a terminal size signal? */ #ifdef USE_SIGWINCH if(gl_pending_signal == SIGWINCH && gl_resize_terminal(gl, 1)) return 1; #endif /* * Lookup the requested disposition of this signal. */ for(sig=gl->sigs; sig && sig->signo != gl_pending_signal; sig=sig->next) ; if(!sig) return 0; /* * Start a fresh line? */ if(sig->flags & GLS_RESTORE_LINE) { if(gl_set_term_curpos(gl, gl_buff_curpos_to_term_curpos(gl, gl->ntotal)) || gl_output_raw_string(gl, "\r\n")) return 1; }; /* * Restore terminal settings to how they were before gl_get_line() was * called? */ if(sig->flags & GLS_RESTORE_TTY) gl_restore_terminal_attributes(gl); /* * Restore signal handlers to how they were before gl_get_line() was * called? If this hasn't been requested, only reinstate the signal * handler of the signal that we are handling. */ if(sig->flags & GLS_RESTORE_SIG) { gl_restore_signal_handlers(gl); } else { (void) sigaction(sig->signo, &sig->original, &keep_action); (void) sigprocmask(SIG_UNBLOCK, &sig->proc_mask, NULL); }; /* * Forward the signal to the application's signal handler. */ if(!(sig->flags & GLS_DONT_FORWARD)) raise(gl_pending_signal); gl_pending_signal = -1; /* * Reinstate our signal handlers. */ if(sig->flags & GLS_RESTORE_SIG) { gl_override_signal_handlers(gl); } else { (void) sigaction(sig->signo, &keep_action, NULL); (void) sigprocmask(SIG_BLOCK, &sig->proc_mask, NULL); }; /* * Do we need to reinstate our terminal settings? */ if(sig->flags & GLS_RESTORE_TTY) gl_raw_terminal_mode(gl); /* * Redraw the line? */ if(sig->flags & GLS_REDRAW_LINE && gl_redisplay(gl, 1)) return 1; /* * Set errno. */ errno = sig->errno_value; /* * What next? */ switch(sig->after) { case GLS_RETURN: return gl_newline(gl, 1); break; case GLS_ABORT: return 1; break; case GLS_CONTINUE: return 0; break; }; return 0; #endif /* __MINGW32__ */ } /*....................................................................... * Get pertinent terminal control strings and the initial terminal size. * * Input: * gl GetLine * The resource object of this library. * term char * The type of the terminal. * Output: * return int 0 - OK. * 1 - Error. */ static int gl_control_strings(GetLine *gl, const char *term) { int bad_term = 0; /* True if term is unusable */ /* * Discard any existing control strings from a previous terminal. */ gl->left = NULL; gl->right = NULL; gl->up = NULL; gl->down = NULL; gl->home = NULL; gl->bol = 0; gl->clear_eol = NULL; gl->clear_eod = NULL; gl->u_arrow = NULL; gl->d_arrow = NULL; gl->l_arrow = NULL; gl->r_arrow = NULL; gl->sound_bell = NULL; gl->bold = NULL; gl->underline = NULL; gl->standout = NULL; gl->dim = NULL; gl->reverse = NULL; gl->blink = NULL; gl->text_attr_off = NULL; gl->nline = 0; gl->ncolumn = 0; #ifdef USE_TERMINFO gl->left_n = NULL; gl->right_n = NULL; #endif /* * If possible lookup the information in a terminal information * database. */ #ifdef USE_TERMINFO if(!term || setupterm((char *)term, gl->input_fd, NULL) == ERR) { bad_term = 1; } else { _clr_StringGroup(gl->capmem); gl->left = gl_tigetstr(gl, "cub1"); gl->right = gl_tigetstr(gl, "cuf1"); gl->up = gl_tigetstr(gl, "cuu1"); gl->down = gl_tigetstr(gl, "cud1"); gl->home = gl_tigetstr(gl, "home"); gl->clear_eol = gl_tigetstr(gl, "el"); gl->clear_eod = gl_tigetstr(gl, "ed"); gl->u_arrow = gl_tigetstr(gl, "kcuu1"); gl->d_arrow = gl_tigetstr(gl, "kcud1"); gl->l_arrow = gl_tigetstr(gl, "kcub1"); gl->r_arrow = gl_tigetstr(gl, "kcuf1"); gl->left_n = gl_tigetstr(gl, "cub"); gl->right_n = gl_tigetstr(gl, "cuf"); gl->sound_bell = gl_tigetstr(gl, "bel"); gl->bold = gl_tigetstr(gl, "bold"); gl->underline = gl_tigetstr(gl, "smul"); gl->standout = gl_tigetstr(gl, "smso"); gl->dim = gl_tigetstr(gl, "dim"); gl->reverse = gl_tigetstr(gl, "rev"); gl->blink = gl_tigetstr(gl, "blink"); gl->text_attr_off = gl_tigetstr(gl, "sgr0"); }; #elif defined(USE_TERMCAP) if(!term || tgetent(gl->tgetent_buf, (char *)term) < 0) { bad_term = 1; } else { char *tgetstr_buf_ptr = gl->tgetstr_buf; _clr_StringGroup(gl->capmem); gl->left = gl_tgetstr(gl, "le", &tgetstr_buf_ptr); gl->right = gl_tgetstr(gl, "nd", &tgetstr_buf_ptr); gl->up = gl_tgetstr(gl, "up", &tgetstr_buf_ptr); gl->down = gl_tgetstr(gl, "do", &tgetstr_buf_ptr); gl->home = gl_tgetstr(gl, "ho", &tgetstr_buf_ptr); gl->clear_eol = gl_tgetstr(gl, "ce", &tgetstr_buf_ptr); gl->clear_eod = gl_tgetstr(gl, "cd", &tgetstr_buf_ptr); gl->u_arrow = gl_tgetstr(gl, "ku", &tgetstr_buf_ptr); gl->d_arrow = gl_tgetstr(gl, "kd", &tgetstr_buf_ptr); gl->l_arrow = gl_tgetstr(gl, "kl", &tgetstr_buf_ptr); gl->r_arrow = gl_tgetstr(gl, "kr", &tgetstr_buf_ptr); gl->sound_bell = gl_tgetstr(gl, "bl", &tgetstr_buf_ptr); gl->bold = gl_tgetstr(gl, "md", &tgetstr_buf_ptr); gl->underline = gl_tgetstr(gl, "us", &tgetstr_buf_ptr); gl->standout = gl_tgetstr(gl, "so", &tgetstr_buf_ptr); gl->dim = gl_tgetstr(gl, "mh", &tgetstr_buf_ptr); gl->reverse = gl_tgetstr(gl, "mr", &tgetstr_buf_ptr); gl->blink = gl_tgetstr(gl, "mb", &tgetstr_buf_ptr); gl->text_attr_off = gl_tgetstr(gl, "me", &tgetstr_buf_ptr); }; #endif #ifdef __MINGW32__ /* * Check to see if it's an NT command prompt window. * * If there were a nice way of loading ANSI.SYS in 32-bit mode, * then this hack wouldn't be necessary. -bms */ if(!strcasecmp(term, "ansi-nt")) { #define GL_NOT_FOR_WIN32_CMD "" gl->left = "\b \b"; /* ^H, but clear existing character */ gl->right = GL_NOT_FOR_WIN32_CMD; gl->up = GL_NOT_FOR_WIN32_CMD; gl->down = "\n"; /* ^J */ gl->home = GL_NOT_FOR_WIN32_CMD; gl->bol = "\r"; /* ^M */ gl->clear_eol = GL_NOT_FOR_WIN32_CMD; gl->clear_eod = GL_NOT_FOR_WIN32_CMD; /* * These next 4 are used to allow command line history to work; * they are used for input, not output. */ gl->u_arrow = GL_ESC_STR "[A"; gl->d_arrow = GL_ESC_STR "[B"; gl->l_arrow = GL_ESC_STR "[D"; gl->r_arrow = GL_ESC_STR "[C"; gl->sound_bell = "\a"; /* ^G */ gl->bold = GL_NOT_FOR_WIN32_CMD; gl->underline = GL_NOT_FOR_WIN32_CMD; gl->standout = GL_NOT_FOR_WIN32_CMD; gl->dim = GL_NOT_FOR_WIN32_CMD; gl->reverse = GL_NOT_FOR_WIN32_CMD; gl->blink = GL_NOT_FOR_WIN32_CMD; gl->text_attr_off = GL_NOT_FOR_WIN32_CMD; #undef GL_NOT_FOR_WIN32_CMD } #endif /* * Report term being unusable. */ if(bad_term) { fprintf(stderr, "Bad terminal type: \"%s\". Will assume vt100.\n", term ? term : "(null)"); }; /* * Fill in missing information with ANSI VT100 strings. */ if(!gl->left) gl->left = "\b"; /* ^H */ if(!gl->right) gl->right = GL_ESC_STR "[C"; if(!gl->up) gl->up = GL_ESC_STR "[A"; if(!gl->down) gl->down = "\n"; if(!gl->home) gl->home = GL_ESC_STR "[H"; if(!gl->bol) gl->bol = "\r"; if(!gl->clear_eol) gl->clear_eol = GL_ESC_STR "[K"; if(!gl->clear_eod) gl->clear_eod = GL_ESC_STR "[J"; if(!gl->u_arrow) gl->u_arrow = GL_ESC_STR "[A"; if(!gl->d_arrow) gl->d_arrow = GL_ESC_STR "[B"; if(!gl->l_arrow) gl->l_arrow = GL_ESC_STR "[D"; if(!gl->r_arrow) gl->r_arrow = GL_ESC_STR "[C"; if(!gl->sound_bell) gl->sound_bell = "\a"; if(!gl->bold) gl->bold = GL_ESC_STR "[1m"; if(!gl->underline) gl->underline = GL_ESC_STR "[4m"; if(!gl->standout) gl->standout = GL_ESC_STR "[1;7m"; if(!gl->dim) gl->dim = ""; /* Not available */ if(!gl->reverse) gl->reverse = GL_ESC_STR "[7m"; if(!gl->blink) gl->blink = GL_ESC_STR "[5m"; if(!gl->text_attr_off) gl->text_attr_off = GL_ESC_STR "[m"; /* * Find out the current terminal size. */ (void) gl_terminal_size(gl, GL_DEF_NCOLUMN, GL_DEF_NLINE); return 0; } #ifdef USE_TERMINFO /*....................................................................... * This is a private function of gl_control_strings() used to look up * a termninal capability string from the terminfo database and make * a private copy of it. * * Input: * gl GetLine * The resource object of gl_get_line(). * name const char * The name of the terminfo string to look up. * Output: * return const char * The local copy of the capability, or NULL * if not available. */ static const char *gl_tigetstr(GetLine *gl, const char *name) { const char *value = tigetstr((char *)name); if(!value || value == (char *) -1) return NULL; return _sg_store_string(gl->capmem, value, 0); } #elif defined(USE_TERMCAP) /*....................................................................... * This is a private function of gl_control_strings() used to look up * a termninal capability string from the termcap database and make * a private copy of it. Note that some emulations of tgetstr(), such * as that used by Solaris, ignores the buffer pointer that is past to * it, so we can't assume that a private copy has been made that won't * be trashed by another call to gl_control_strings() by another * GetLine object. So we make what may be a redundant private copy * of the string in gl->capmem. * * Input: * gl GetLine * The resource object of gl_get_line(). * name const char * The name of the terminfo string to look up. * Input/Output: * bufptr char ** On input *bufptr points to the location in * gl->tgetstr_buf at which to record the * capability string. On output *bufptr is * incremented over the stored string. * Output: * return const char * The local copy of the capability, or NULL * on error. */ static const char *gl_tgetstr(GetLine *gl, const char *name, char **bufptr) { const char *value = tgetstr((char *)name, bufptr); if(!value || value == (char *) -1) return NULL; return _sg_store_string(gl->capmem, value, 0); } #endif /*....................................................................... * This is an action function that implements a user interrupt (eg. ^C). */ static KT_KEY_FN(gl_user_interrupt) { raise(SIGINT); return 1; } /*....................................................................... * This is an action function that implements the abort signal. */ static KT_KEY_FN(gl_abort) { raise(SIGABRT); return 1; } /*....................................................................... * This is an action function that sends a suspend signal (eg. ^Z) to the * the parent process. */ static KT_KEY_FN(gl_suspend) { #ifdef SIGTSTP raise(SIGTSTP); #endif return 0; } /*....................................................................... * This is an action function that halts output to the terminal. */ static KT_KEY_FN(gl_stop_output) { #ifdef TCOOFF tcflow(gl->output_fd, TCOOFF); #endif return 0; } /*....................................................................... * This is an action function that resumes halted terminal output. */ static KT_KEY_FN(gl_start_output) { #ifdef TCOON tcflow(gl->output_fd, TCOON); #endif return 0; } /*....................................................................... * This is an action function that allows the next character to be accepted * without any interpretation as a special character. */ static KT_KEY_FN(gl_literal_next) { char c; /* The character to be added to the line */ int i; /* * Get the character to be inserted literally. */ if(gl_read_character(gl, &c, -1)) return 1; /* * Add the character to the line 'count' times. */ for(i=0; incolumn) % TAB_WIDTH); if(IS_CTRL_CHAR(c)) return 2; if(!isprint((int)(unsigned char) c)) { char string[TAB_WIDTH + 4]; snprintf(string, sizeof(string), "\\%o", (int)(unsigned char)c); return strlen(string); }; return 1; } /*....................................................................... * Work out the length of given string of characters on the terminal. * * Input: * gl GetLine * The resource object of this library. * string char * The string to be measured. * nc int The number of characters to be measured, or -1 * to measure the whole string. * term_curpos int The destination terminal location of the character. * This is needed because the width of tab characters * depends on where they are, relative to the * preceding tab stops. * Output: * return int The number of displayed characters. */ static int gl_displayed_string_width(GetLine *gl, const char *string, int nc, int term_curpos) { int slen=0; /* The displayed number of characters */ int i; /* * How many characters are to be measured? */ if(nc < 0) nc = strlen(string); /* * Add up the length of the displayed string. */ for(i=0; iecho) { int ndone = 0; /* The number of characters written so far */ /* * How long is the string to be written? */ int slen = strlen(string); /* * Attempt to write the string to the terminal, restarting the * write if a signal is caught. */ while(ndone < slen) { int nnew = fwrite(string + ndone, sizeof(char), slen-ndone, gl->output_fp); if(nnew > 0) ndone += nnew; else if(errno != EINTR) return 1; }; }; return 0; } /*....................................................................... * Output a terminal control sequence. When using terminfo, * this must be a sequence returned by tgetstr() or tigetstr() * respectively. * * Input: * gl GetLine * The resource object of this library. * nline int The number of lines affected by the operation, * or 1 if not relevant. * string char * The control sequence to be sent. * Output: * return int 0 - OK. * 1 - Error. */ static int gl_output_control_sequence(GetLine *gl, int nline, const char *string) { if(gl->echo) { #if defined(USE_TERMINFO) || defined(USE_TERMCAP) tputs_fp = gl->output_fp; errno = 0; tputs((char *)string, nline, gl_tputs_putchar); return errno != 0; #else return gl_output_raw_string(gl, string); #endif }; return 0; } #if defined(USE_TERMINFO) || defined(USE_TERMCAP) /*....................................................................... * The following function is used as the output function of tputs when * terminfo is being used. * * Input: * c TputsType The character to be output. * Output: * return int The character that was written or EOF on error. */ static int gl_tputs_putchar(TputsType c) { return putc(c, tputs_fp); } #endif /*....................................................................... * Move the terminal cursor n characters to the left or right. * * Input: * gl GetLine * The resource object of this program. * n int number of positions to the right (> 0) or left (< 0). * Output: * return int 0 - OK. * 1 - Error. */ static int gl_terminal_move_cursor(GetLine *gl, int n) { int cur_row, cur_col; /* The current terminal row and column index of */ /* the cursor wrt the start of the input line. */ int new_row, new_col; /* The target terminal row and column index of */ /* the cursor wrt the start of the input line. */ /* * How far can we move left? */ if(gl->term_curpos + n < 0) n = gl->term_curpos; /* * Break down the current and target cursor locations into rows and columns. */ cur_row = gl->term_curpos / gl->ncolumn; cur_col = gl->term_curpos % gl->ncolumn; new_row = (gl->term_curpos + n) / gl->ncolumn; new_col = (gl->term_curpos + n) % gl->ncolumn; /* * Move down to the next line. */ for(; cur_row < new_row; cur_row++) { if(gl_output_control_sequence(gl, 1, gl->down)) return 1; }; /* * Move up to the previous line. */ for(; cur_row > new_row; cur_row--) { if(gl_output_control_sequence(gl, 1, gl->up)) return 1; }; /* * Move to the right within the target line? */ if(cur_col < new_col) { #ifdef USE_TERMINFO /* * Use a parameterized control sequence if it generates less control * characters (guess based on ANSI terminal termcap entry). */ if(gl->right_n != NULL && new_col - cur_col > 1) { if(gl_output_control_sequence(gl, 1, tparm((char *)gl->right_n, (long)(new_col - cur_col), 0l, 0l, 0l, 0l, 0l, 0l, 0l, 0l))) return 1; } else #endif { for(; cur_col < new_col; cur_col++) { if(gl_output_control_sequence(gl, 1, gl->right)) return 1; }; }; /* * Move to the left within the target line? */ } else if(cur_col > new_col) { #ifdef USE_TERMINFO /* * Use a parameterized control sequence if it generates less control * characters (guess based on ANSI terminal termcap entry). */ if(gl->left_n != NULL && cur_col - new_col > 3) { if(gl_output_control_sequence(gl, 1, tparm((char *)gl->left_n, (long)(cur_col - new_col), 0l, 0l, 0l, 0l, 0l, 0l, 0l, 0l))) return 1; } else #endif { for(; cur_col > new_col; cur_col--) { if(gl_output_control_sequence(gl, 1, gl->left)) return 1; }; }; } /* * Update the recorded position of the terminal cursor. */ gl->term_curpos += n; return 0; } /*....................................................................... * Write a character to the terminal after expanding tabs and control * characters to their multi-character representations. * * Input: * gl GetLine * The resource object of this program. * c char The character to be output. * pad char Many terminals have the irritating feature that * when one writes a character in the last column of * of the terminal, the cursor isn't wrapped to the * start of the next line until one more character * is written. Some terminals don't do this, so * after such a write, we don't know where the * terminal is unless we output an extra character. * This argument specifies the character to write. * If at the end of the input line send '\0' or a * space, and a space will be written. Otherwise, * pass the next character in the input line * following the one being written. * Output: * return int 0 - OK. */ static int gl_output_char(GetLine *gl, char c, char pad) { char string[TAB_WIDTH + 4]; /* A work area for composing compound strings */ int nchar; /* The number of terminal characters */ int i; /* * Check for special characters. */ if(c == '\t') { /* * How many spaces do we need to represent a tab at the current terminal * column? */ nchar = gl_displayed_char_width(gl, '\t', gl->term_curpos); /* * Compose the tab string. */ for(i=0; iterm_curpos += nchar; /* * If the new character ended exactly at the end of a line, * most terminals won't move the cursor onto the next line until we * have written a character on the next line, so append an extra * space then move the cursor back. */ if(gl->term_curpos % gl->ncolumn == 0) { int term_curpos = gl->term_curpos; if(gl_output_char(gl, pad ? pad : ' ', ' ') || gl_set_term_curpos(gl, term_curpos)) return 1; }; return 0; } /*....................................................................... * Write a string to the terminal after expanding tabs and control * characters to their multi-character representations. * * Input: * gl GetLine * The resource object of this program. * string char * The string to be output. * pad char Many terminals have the irritating feature that * when one writes a character in the last column of * of the terminal, the cursor isn't wrapped to the * start of the next line until one more character * is written. Some terminals don't do this, so * after such a write, we don't know where the * terminal is unless we output an extra character. * This argument specifies the character to write. * If at the end of the input line send '\0' or a * space, and a space will be written. Otherwise, * pass the next character in the input line * following the one being written. * Output: * return int 0 - OK. */ static int gl_output_string(GetLine *gl, const char *string, char pad) { const char *cptr; /* A pointer into string[] */ for(cptr=string; *cptr; cptr++) { char nextc = cptr[1]; if(gl_output_char(gl, *cptr, nextc ? nextc : pad)) return 1; }; return 0; } /*....................................................................... * Given a character position within gl->line[], work out the * corresponding gl->term_curpos position on the terminal. * * Input: * gl GetLine * The resource object of this library. * buff_curpos int The position within gl->line[]. * * Output: * return int The gl->term_curpos position on the terminal. */ static int gl_buff_curpos_to_term_curpos(GetLine *gl, int buff_curpos) { return gl->prompt_len + gl_displayed_string_width(gl, gl->line, buff_curpos, gl->prompt_len); } /*....................................................................... * Move the terminal cursor position. * * Input: * gl GetLine * The resource object of this library. * term_curpos int The destination terminal cursor position. * Output: * return int 0 - OK. * 1 - Error. */ static int gl_set_term_curpos(GetLine *gl, int term_curpos) { return gl_terminal_move_cursor(gl, term_curpos - gl->term_curpos); } /*....................................................................... * This is an action function that moves the buffer cursor one character * left, and updates the terminal cursor to match. */ static KT_KEY_FN(gl_cursor_left) { return gl_place_cursor(gl, gl->buff_curpos - count); } /*....................................................................... * This is an action function that moves the buffer cursor one character * right, and updates the terminal cursor to match. */ static KT_KEY_FN(gl_cursor_right) { return gl_place_cursor(gl, gl->buff_curpos + count); } /*....................................................................... * This is an action function that toggles between overwrite and insert * mode. */ static KT_KEY_FN(gl_insert_mode) { gl->insert = !gl->insert; return 0; } /*....................................................................... * This is an action function which moves the cursor to the beginning of * the line. */ static KT_KEY_FN(gl_beginning_of_line) { return gl_place_cursor(gl, 0); } /*....................................................................... * This is an action function which moves the cursor to the end of * the line. */ static KT_KEY_FN(gl_end_of_line) { return gl_place_cursor(gl, gl->ntotal); } /*....................................................................... * This is an action function which deletes the entire contents of the * current line. */ static KT_KEY_FN(gl_delete_line) { /* * If in vi command mode, preserve the current line for potential * use by vi-undo. */ gl_save_for_undo(gl); /* * Copy the contents of the line to the cut buffer. */ strncpy(gl->cutbuf, gl->line, gl->linelen+2); /* * Clear the buffer. */ gl->ntotal = 0; gl->line[0] = '\0'; /* * Move the terminal cursor to just after the prompt. */ if(gl_place_cursor(gl, 0)) return 1; /* * Clear from the end of the prompt to the end of the terminal. */ if(gl_output_control_sequence(gl, gl->nline, gl->clear_eod)) return 1; return 0; } /*....................................................................... * This is an action function which deletes all characters between the * current cursor position and the end of the line. */ static KT_KEY_FN(gl_kill_line) { /* * If in vi command mode, preserve the current line for potential * use by vi-undo. */ gl_save_for_undo(gl); /* * Copy the part of the line that is about to be deleted to the cut buffer. */ strncpy(gl->cutbuf, gl->line + gl->buff_curpos, gl->linelen+2); /* * Terminate the buffered line at the current cursor position. */ gl->ntotal = gl->buff_curpos; gl->line[gl->ntotal] = '\0'; /* * Clear the part of the line that follows the cursor. */ if(gl_output_control_sequence(gl, gl->nline, gl->clear_eod)) return 1; /* * Explicitly reset the cursor position to allow vi command mode * constraints on its position to be set. */ return gl_place_cursor(gl, gl->buff_curpos); } /*....................................................................... * This is an action function which deletes all characters between the * start of the line and the current cursor position. */ static KT_KEY_FN(gl_backward_kill_line) { /* * How many characters are to be deleted from before the cursor? */ int nc = gl->buff_curpos - gl->insert_curpos; if (!nc) return 0; /* * Move the cursor to the start of the line, or in vi input mode, * the start of the sub-line at which insertion started, and delete * up to the old cursor position. */ return gl_place_cursor(gl, gl->insert_curpos) || gl_delete_chars(gl, nc, gl->editor == GL_EMACS_MODE || gl->vi.command); } /*....................................................................... * This is an action function which moves the cursor forward by a word. */ static KT_KEY_FN(gl_forward_word) { return gl_place_cursor(gl, gl_nth_word_end_forward(gl, count) + (gl->editor==GL_EMACS_MODE)); } /*....................................................................... * This is an action function which moves the cursor forward to the start * of the next word. */ static KT_KEY_FN(gl_forward_to_word) { return gl_place_cursor(gl, gl_nth_word_start_forward(gl, count)); } /*....................................................................... * This is an action function which moves the cursor backward by a word. */ static KT_KEY_FN(gl_backward_word) { return gl_place_cursor(gl, gl_nth_word_start_backward(gl, count)); } /*....................................................................... * Delete one or more characters, starting with the one under the cursor. * * Input: * gl GetLine * The resource object of this library. * nc int The number of characters to delete. * cut int If true, copy the characters to the cut buffer. * Output: * return int 0 - OK. * 1 - Error. */ static int gl_delete_chars(GetLine *gl, int nc, int cut) { /* * If in vi command mode, preserve the current line for potential * use by vi-undo. */ gl_save_for_undo(gl); /* * If there are fewer than nc characters following the cursor, limit * nc to the number available. */ if(gl->buff_curpos + nc > gl->ntotal) nc = gl->ntotal - gl->buff_curpos; /* * Copy the about to be deleted region to the cut buffer. */ if(cut) { memcpy(gl->cutbuf, gl->line + gl->buff_curpos, nc); gl->cutbuf[nc] = '\0'; } /* * Nothing to delete? */ if(nc <= 0) return 0; /* * In vi overwrite mode, restore any previously overwritten characters * from the undo buffer. */ if(gl->editor == GL_VI_MODE && !gl->vi.command && !gl->insert) { /* * How many of the characters being deleted can be restored from the * undo buffer? */ int nrestore = gl->buff_curpos + nc <= gl->vi.undo.ntotal ? nc : gl->vi.undo.ntotal - gl->buff_curpos; /* * Restore any available characters. */ if(nrestore > 0) memcpy(gl->line + gl->buff_curpos, gl->vi.undo.line + gl->buff_curpos, nrestore); /* * If their were insufficient characters in the undo buffer, then this * implies that we are deleting from the end of the line, so we need * to terminate the line either where the undo buffer ran out, or if * we are deleting from beyond the end of the undo buffer, at the current * cursor position. */ if(nc != nrestore) { gl->ntotal = gl->vi.undo.ntotal > gl->buff_curpos ? gl->vi.undo.ntotal : gl->buff_curpos; gl->line[gl->ntotal] = '\0'; }; } else { /* * Copy the remaining part of the line back over the deleted characters. */ memmove(gl->line + gl->buff_curpos, gl->line + gl->buff_curpos + nc, gl->ntotal - gl->buff_curpos - nc + 1); gl->ntotal -= nc; }; /* * Redraw the remaining characters following the cursor. */ if(gl_output_string(gl, gl->line + gl->buff_curpos, '\0')) return 1; /* * Clear to the end of the terminal. */ if(gl_output_control_sequence(gl, gl->nline, gl->clear_eod)) return 1; /* * Place the cursor at the start of where the deletion was performed. */ return gl_place_cursor(gl, gl->buff_curpos); } /*....................................................................... * This is an action function which deletes character(s) under the * cursor without moving the cursor. */ static KT_KEY_FN(gl_forward_delete_char) { /* * Delete 'count' characters. */ return gl_delete_chars(gl, count, gl->vi.command); } /*....................................................................... * This is an action function which deletes character(s) under the * cursor and moves the cursor back one character. */ static KT_KEY_FN(gl_backward_delete_char) { /* * Restrict the deletion count to the number of characters that * precede the insertion point. */ if(count > gl->buff_curpos - gl->insert_curpos) count = gl->buff_curpos - gl->insert_curpos; /* * If in vi command mode, preserve the current line for potential * use by vi-undo. */ gl_save_for_undo(gl); return gl_cursor_left(gl, count) || gl_delete_chars(gl, count, gl->vi.command); } /*....................................................................... * Starting from the cursor position delete to the specified column. */ static KT_KEY_FN(gl_delete_to_column) { if (--count >= gl->buff_curpos) return gl_forward_delete_char(gl, count - gl->buff_curpos); else return gl_backward_delete_char(gl, gl->buff_curpos - count); } /*....................................................................... * Starting from the cursor position delete characters to a matching * parenthesis. */ static KT_KEY_FN(gl_delete_to_parenthesis) { int curpos = gl_index_of_matching_paren(gl); if(curpos >= 0) { gl_save_for_undo(gl); if(curpos >= gl->buff_curpos) return gl_forward_delete_char(gl, curpos - gl->buff_curpos + 1); else return gl_backward_delete_char(gl, ++gl->buff_curpos - curpos + 1); }; return 0; } /*....................................................................... * This is an action function which deletes from the cursor to the end * of the word that the cursor is either in or precedes. */ static KT_KEY_FN(gl_forward_delete_word) { /* * If in vi command mode, preserve the current line for potential * use by vi-undo. */ gl_save_for_undo(gl); /* * In emacs mode delete to the end of the word. In vi mode delete to the * start of the net word. */ if(gl->editor == GL_EMACS_MODE) { return gl_delete_chars(gl, gl_nth_word_end_forward(gl,count) - gl->buff_curpos + 1, 1); } else { return gl_delete_chars(gl, gl_nth_word_start_forward(gl,count) - gl->buff_curpos, gl->vi.command); }; } /*....................................................................... * This is an action function which deletes the word that precedes the * cursor. */ static KT_KEY_FN(gl_backward_delete_word) { /* * Keep a record of the current cursor position. */ int buff_curpos = gl->buff_curpos; /* * If in vi command mode, preserve the current line for potential * use by vi-undo. */ gl_save_for_undo(gl); /* * Move back 'count' words. */ if(gl_backward_word(gl, count)) return 1; /* * Delete from the new cursor position to the original one. */ return gl_delete_chars(gl, buff_curpos - gl->buff_curpos, gl->editor == GL_EMACS_MODE || gl->vi.command); } /*....................................................................... * Searching in a given direction, delete to the count'th * instance of a specified or queried character, in the input line. * * Input: * gl GetLine * The getline resource object. * count int The number of times to search. * c char The character to be searched for, or '\0' if * the character should be read from the user. * forward int True if searching forward. * onto int True if the search should end on top of the * character, false if the search should stop * one character before the character in the * specified search direction. * change int If true, this function is being called upon * to do a vi change command, in which case the * user will be left in insert mode after the * deletion. * Output: * return int 0 - OK. * 1 - Error. */ static int gl_delete_find(GetLine *gl, int count, char c, int forward, int onto, int change) { /* * Search for the character, and abort the deletion if not found. */ int pos = gl_find_char(gl, count, forward, onto, c); if(pos < 0) return 0; /* * If in vi command mode, preserve the current line for potential * use by vi-undo. */ gl_save_for_undo(gl); /* * Allow the cursor to be at the end of the line if this is a change * command. */ if(change) gl->vi.command = 0; /* * Delete the appropriate span of characters. */ if(forward) { if(gl_delete_chars(gl, pos - gl->buff_curpos + 1, 1)) return 1; } else { int buff_curpos = gl->buff_curpos; if(gl_place_cursor(gl, pos) || gl_delete_chars(gl, buff_curpos - gl->buff_curpos, 1)) return 1; }; /* * If this is a change operation, switch the insert mode. */ if(change && gl_vi_insert(gl, 0)) return 1; return 0; } /*....................................................................... * This is an action function which deletes forward from the cursor up to and * including a specified character. */ static KT_KEY_FN(gl_forward_delete_find) { return gl_delete_find(gl, count, '\0', 1, 1, 0); } /*....................................................................... * This is an action function which deletes backward from the cursor back to * and including a specified character. */ static KT_KEY_FN(gl_backward_delete_find) { return gl_delete_find(gl, count, '\0', 0, 1, 0); } /*....................................................................... * This is an action function which deletes forward from the cursor up to but * not including a specified character. */ static KT_KEY_FN(gl_forward_delete_to) { return gl_delete_find(gl, count, '\0', 1, 0, 0); } /*....................................................................... * This is an action function which deletes backward from the cursor back to * but not including a specified character. */ static KT_KEY_FN(gl_backward_delete_to) { return gl_delete_find(gl, count, '\0', 0, 0, 0); } /*....................................................................... * This is an action function which deletes to a character specified by a * previous search. */ static KT_KEY_FN(gl_delete_refind) { return gl_delete_find(gl, count, gl->vi.find_char, gl->vi.find_forward, gl->vi.find_onto, 0); } /*....................................................................... * This is an action function which deletes to a character specified by a * previous search, but in the opposite direction. */ static KT_KEY_FN(gl_delete_invert_refind) { return gl_delete_find(gl, count, gl->vi.find_char, !gl->vi.find_forward, gl->vi.find_onto, 0); } /*....................................................................... * This is an action function which converts the characters in the word * following the cursor to upper case. */ static KT_KEY_FN(gl_upcase_word) { /* * Locate the count'th word ending after the cursor. */ int last = gl_nth_word_end_forward(gl, count); /* * If in vi command mode, preserve the current line for potential * use by vi-undo. */ gl_save_for_undo(gl); /* * Upcase characters from the current cursor position to 'last'. */ while(gl->buff_curpos <= last) { char *cptr = gl->line + gl->buff_curpos++; /* * Convert the character to upper case? */ if(islower((int)(unsigned char) *cptr)) *cptr = toupper((int) *cptr); /* * Write the possibly modified character back. Note that for non-modified * characters we want to do this as well, so as to advance the cursor. */ if(gl_output_char(gl, *cptr, cptr[1])) return 1; }; return gl_place_cursor(gl, gl->buff_curpos); /* bounds check */ } /*....................................................................... * This is an action function which converts the characters in the word * following the cursor to lower case. */ static KT_KEY_FN(gl_downcase_word) { /* * Locate the count'th word ending after the cursor. */ int last = gl_nth_word_end_forward(gl, count); /* * If in vi command mode, preserve the current line for potential * use by vi-undo. */ gl_save_for_undo(gl); /* * Upcase characters from the current cursor position to 'last'. */ while(gl->buff_curpos <= last) { char *cptr = gl->line + gl->buff_curpos++; /* * Convert the character to upper case? */ if(isupper((int)(unsigned char) *cptr)) *cptr = tolower((int) *cptr); /* * Write the possibly modified character back. Note that for non-modified * characters we want to do this as well, so as to advance the cursor. */ if(gl_output_char(gl, *cptr, cptr[1])) return 1; }; return gl_place_cursor(gl, gl->buff_curpos); /* bounds check */ } /*....................................................................... * This is an action function which converts the first character of the * following word to upper case, in order to capitalize the word, and * leaves the cursor at the end of the word. */ static KT_KEY_FN(gl_capitalize_word) { char *cptr; /* &gl->line[gl->buff_curpos] */ int first; /* True for the first letter of the word */ int i; /* * Keep a record of the current insert mode and the cursor position. */ int insert = gl->insert; /* * If in vi command mode, preserve the current line for potential * use by vi-undo. */ gl_save_for_undo(gl); /* * We want to overwrite the modified word. */ gl->insert = 0; /* * Capitalize 'count' words. */ for(i=0; ibuff_curpos < gl->ntotal; i++) { int pos = gl->buff_curpos; /* * If we are not already within a word, skip to the start of the word. */ for(cptr = gl->line + pos ; posntotal && !gl_is_word_char((int) *cptr); pos++, cptr++) ; /* * Move the cursor to the new position. */ if(gl_place_cursor(gl, pos)) return 1; /* * While searching for the end of the word, change lower case letters * to upper case. */ for(first=1; gl->buff_curposntotal && gl_is_word_char((int) *cptr); gl->buff_curpos++, cptr++) { /* * Convert the character to upper case? */ if(first) { if(islower((int)(unsigned char) *cptr)) *cptr = toupper((int) *cptr); } else { if(isupper((int)(unsigned char) *cptr)) *cptr = tolower((int) *cptr); }; first = 0; /* * Write the possibly modified character back. Note that for non-modified * characters we want to do this as well, so as to advance the cursor. */ if(gl_output_char(gl, *cptr, cptr[1])) return 1; }; }; /* * Restore the insertion mode. */ gl->insert = insert; return gl_place_cursor(gl, gl->buff_curpos); /* bounds check */ } /*....................................................................... * This is an action function which redraws the current line. */ static KT_KEY_FN(gl_redisplay) { /* * Keep a record of the current cursor position. */ int buff_curpos = gl->buff_curpos; /* * Move the cursor to the start of the terminal line, and clear from there * to the end of the display. */ if(gl_set_term_curpos(gl, 0) || gl_output_control_sequence(gl, gl->nline, gl->clear_eod)) return 1; /* * Display the current prompt. */ if(gl_display_prompt(gl)) return 1; /* * Render the part of the line that the user has typed in so far. */ if(gl_output_string(gl, gl->line, '\0')) return 1; /* * Restore the cursor position. */ if(gl_place_cursor(gl, buff_curpos)) return 1; /* * Flush the redisplayed line to the terminal. */ return gl_flush_output(gl); } /*....................................................................... * This is an action function which clears the display and redraws the * input line from the home position. */ static KT_KEY_FN(gl_clear_screen) { /* * Record the current cursor position. */ int buff_curpos = gl->buff_curpos; /* * Home the cursor and clear from there to the end of the display. */ if(gl_output_control_sequence(gl, gl->nline, gl->home) || gl_output_control_sequence(gl, gl->nline, gl->clear_eod)) return 1; /* * Redisplay the line. */ gl->term_curpos = 0; gl->buff_curpos = 0; if(gl_redisplay(gl,1)) return 1; /* * Restore the cursor position. */ return gl_place_cursor(gl, buff_curpos); } /*....................................................................... * This is an action function which swaps the character under the cursor * with the character to the left of the cursor. */ static KT_KEY_FN(gl_transpose_chars) { char from[3]; /* The original string of 2 characters */ char swap[3]; /* The swapped string of two characters */ /* * If we are at the beginning or end of the line, there aren't two * characters to swap. */ if(gl->buff_curpos < 1 || gl->buff_curpos >= gl->ntotal) return 0; /* * If in vi command mode, preserve the current line for potential * use by vi-undo. */ gl_save_for_undo(gl); /* * Get the original and swapped strings of the two characters. */ from[0] = gl->line[gl->buff_curpos - 1]; from[1] = gl->line[gl->buff_curpos]; from[2] = '\0'; swap[0] = gl->line[gl->buff_curpos]; swap[1] = gl->line[gl->buff_curpos - 1]; swap[2] = '\0'; /* * Move the cursor to the start of the two characters. */ if(gl_place_cursor(gl, gl->buff_curpos-1)) return 1; /* * Swap the two characters in the buffer. */ gl->line[gl->buff_curpos] = swap[0]; gl->line[gl->buff_curpos+1] = swap[1]; /* * If the sum of the displayed width of the two characters * in their current and final positions is the same, swapping can * be done by just overwriting with the two swapped characters. */ if(gl_displayed_string_width(gl, from, -1, gl->term_curpos) == gl_displayed_string_width(gl, swap, -1, gl->term_curpos)) { int insert = gl->insert; gl->insert = 0; if(gl_output_char(gl, swap[0], swap[1]) || gl_output_char(gl, swap[1], gl->line[gl->buff_curpos+2])) return 1; gl->insert = insert; /* * If the swapped substring has a different displayed size, we need to * redraw everything after the first of the characters. */ } else { if(gl_output_string(gl, gl->line + gl->buff_curpos, '\0') || gl_output_control_sequence(gl, gl->nline, gl->clear_eod)) return 1; }; /* * Advance the cursor to the character after the swapped pair. */ return gl_place_cursor(gl, gl->buff_curpos + 2); } /*....................................................................... * This is an action function which sets a mark at the current cursor * location. */ static KT_KEY_FN(gl_set_mark) { gl->buff_mark = gl->buff_curpos; return 0; } /*....................................................................... * This is an action function which swaps the mark location for the * cursor location. */ static KT_KEY_FN(gl_exchange_point_and_mark) { /* * Get the old mark position, and limit to the extent of the input * line. */ int old_mark = gl->buff_mark <= gl->ntotal ? gl->buff_mark : gl->ntotal; /* * Make the current cursor position the new mark. */ gl->buff_mark = gl->buff_curpos; /* * Move the cursor to the old mark position. */ return gl_place_cursor(gl, old_mark); } /*....................................................................... * This is an action function which deletes the characters between the * mark and the cursor, recording them in gl->cutbuf for later pasting. */ static KT_KEY_FN(gl_kill_region) { /* * If in vi command mode, preserve the current line for potential * use by vi-undo. */ gl_save_for_undo(gl); /* * Limit the mark to be within the line. */ if(gl->buff_mark > gl->ntotal) gl->buff_mark = gl->ntotal; /* * If there are no characters between the cursor and the mark, simply clear * the cut buffer. */ if(gl->buff_mark == gl->buff_curpos) { gl->cutbuf[0] = '\0'; return 0; }; /* * If the mark is before the cursor, swap the cursor and the mark. */ if(gl->buff_mark < gl->buff_curpos && gl_exchange_point_and_mark(gl,1)) return 1; /* * Delete the characters. */ if(gl_delete_chars(gl, gl->buff_mark - gl->buff_curpos, 1)) return 1; /* * Make the mark the same as the cursor position. */ gl->buff_mark = gl->buff_curpos; return 0; } /*....................................................................... * This is an action function which records the characters between the * mark and the cursor, in gl->cutbuf for later pasting. */ static KT_KEY_FN(gl_copy_region_as_kill) { int ca, cb; /* The indexes of the first and last characters in the region */ int mark; /* The position of the mark */ /* * Get the position of the mark, limiting it to lie within the line. */ mark = gl->buff_mark > gl->ntotal ? gl->ntotal : gl->buff_mark; /* * If there are no characters between the cursor and the mark, clear * the cut buffer. */ if(mark == gl->buff_curpos) { gl->cutbuf[0] = '\0'; return 0; }; /* * Get the line indexes of the first and last characters in the region. */ if(mark < gl->buff_curpos) { ca = mark; cb = gl->buff_curpos - 1; } else { ca = gl->buff_curpos; cb = mark - 1; }; /* * Copy the region to the cut buffer. */ memcpy(gl->cutbuf, gl->line + ca, cb + 1 - ca); gl->cutbuf[cb + 1 - ca] = '\0'; return 0; } /*....................................................................... * This is an action function which inserts the contents of the cut * buffer at the current cursor location. */ static KT_KEY_FN(gl_yank) { int i; /* * Set the mark at the current location. */ gl->buff_mark = gl->buff_curpos; /* * Do nothing else if the cut buffer is empty. */ if(gl->cutbuf[0] == '\0') return gl_ring_bell(gl, 1); /* * If in vi command mode, preserve the current line for potential * use by vi-undo. */ gl_save_for_undo(gl); /* * Insert the string count times. */ for(i=0; icutbuf)) return 1; }; /* * gl_add_string_to_line() leaves the cursor after the last character that * was pasted, whereas vi leaves the cursor over the last character pasted. */ if(gl->editor == GL_VI_MODE && gl_cursor_left(gl, 1)) return 1; return 0; } /*....................................................................... * This is an action function which inserts the contents of the cut * buffer one character beyond the current cursor location. */ static KT_KEY_FN(gl_append_yank) { int was_command = gl->vi.command; int i; /* * If the cut buffer is empty, ring the terminal bell. */ if(gl->cutbuf[0] == '\0') return gl_ring_bell(gl, 1); /* * Set the mark at the current location + 1. */ gl->buff_mark = gl->buff_curpos + 1; /* * If in vi command mode, preserve the current line for potential * use by vi-undo. */ gl_save_for_undo(gl); /* * Arrange to paste the text in insert mode after the current character. */ if(gl_vi_append(gl, 0)) return 1; /* * Insert the string count times. */ for(i=0; icutbuf)) return 1; }; /* * Switch back to command mode if necessary. */ if(was_command) gl_vi_command_mode(gl); return 0; } #ifdef USE_SIGWINCH /*....................................................................... * Respond to the receipt of a window change signal. * * Input: * gl GetLine * The resource object of this library. * redisplay int If true redisplay the current line after * getting the new window size. * Output: * return int 0 - OK. * 1 - Error. */ static int gl_resize_terminal(GetLine *gl, int redisplay) { int lines_used; /* The number of lines currently in use */ struct winsize size; /* The new size information */ int i; /* * Record the fact that the sigwinch signal has been noted. */ if(gl_pending_signal == SIGWINCH) gl_pending_signal = -1; /* * Query the new terminal window size. Ignore invalid responses. */ if(ioctl(gl->output_fd, TIOCGWINSZ, &size) == 0 && size.ws_row > 0 && size.ws_col > 0) { /* * Redisplay the input line? */ if(redisplay) { /* * How many lines are currently displayed. */ lines_used = (gl_displayed_string_width(gl,gl->line,-1,gl->prompt_len) + gl->prompt_len + gl->ncolumn - 1) / gl->ncolumn; /* * Move to the cursor to the start of the line. */ for(i=1; iup)) return 1; }; if(gl_output_control_sequence(gl, 1, gl->bol)) return 1; /* * Clear to the end of the terminal. */ if(gl_output_control_sequence(gl, size.ws_row, gl->clear_eod)) return 1; /* * Record the fact that the cursor is now at the beginning of the line. */ gl->term_curpos = 0; }; /* * Update the recorded window size. */ gl->nline = size.ws_row; gl->ncolumn = size.ws_col; }; /* * Redisplay the line? */ return redisplay ? gl_redisplay(gl,1) : 0; } #endif /*....................................................................... * This is the action function that recalls the previous line in the * history buffer. */ static KT_KEY_FN(gl_up_history) { /* * In vi mode, switch to command mode, since the user is very * likely to want to move around newly recalled lines. */ gl_vi_command_mode(gl); /* * Forget any previous recall session. */ gl->preload_id = 0; /* * We don't want a search prefix for this function. */ if(_glh_search_prefix(gl->glh, gl->line, 0)) return 1; /* * Recall the count'th next older line in the history list. If the first one * fails we can return since nothing has changed otherwise we must continue * and update the line state. */ if(_glh_find_backwards(gl->glh, gl->line, gl->linelen) == NULL) return 0; while(--count && _glh_find_backwards(gl->glh, gl->line, gl->linelen)) ; /* * Record the number of characters in the new string. */ gl->ntotal = strlen(gl->line); /* * Arrange to have the cursor placed at the end of the new line. */ gl->buff_curpos = strlen(gl->line); /* * Erase and display the new line. */ return gl_redisplay(gl,1); } /*....................................................................... * This is the action function that recalls the next line in the * history buffer. */ static KT_KEY_FN(gl_down_history) { /* * In vi mode, switch to command mode, since the user is very * likely to want to move around newly recalled lines. */ gl_vi_command_mode(gl); /* * If no search is currently in progress continue a previous recall * session from a previous entered line if possible. */ if(_glh_line_id(gl->glh, 0) == 0 && gl->preload_id) { _glh_recall_line(gl->glh, gl->preload_id, gl->line, gl->linelen); gl->preload_id = 0; } else { /* * We don't want a search prefix for this function. */ if(_glh_search_prefix(gl->glh, gl->line, 0)) return 1; /* * Recall the count'th next newer line in the history list. If the first one * fails we can return since nothing has changed otherwise we must continue * and update the line state. */ if(_glh_find_forwards(gl->glh, gl->line, gl->linelen) == NULL) return 0; while(--count && _glh_find_forwards(gl->glh, gl->line, gl->linelen)) ; }; /* * Record the number of characters in the new string. */ gl->ntotal = strlen(gl->line); /* * Arrange to have the cursor placed at the end of the new line. */ gl->buff_curpos = strlen(gl->line); /* * Erase and display the new line. */ return gl_redisplay(gl,1); } /*....................................................................... * This is the action function that recalls the previous line in the * history buffer whos prefix matches the characters that currently * precede the cursor. By setting count=-1, this can be used internally * to force searching for the prefix used in the last search. */ static KT_KEY_FN(gl_history_search_backward) { /* * In vi mode, switch to command mode, since the user is very * likely to want to move around newly recalled lines. */ gl_vi_command_mode(gl); /* * Forget any previous recall session. */ gl->preload_id = 0; /* * If the previous thing that the user did wasn't to execute a history * search function, set the search prefix equal to the string that * precedes the cursor. In vi command mode include the character that * is under the cursor in the string. If count<0 force a repeat search * even if the last command wasn't a history command. */ if(gl->last_search != gl->keyseq_count - 1 && count>=0 && _glh_search_prefix(gl->glh, gl->line, gl->buff_curpos + (gl->editor==GL_VI_MODE && gl->ntotal>0))) return 1; /* * Record the key sequence number in which this search function is * being executed, so that the next call to this function or * gl_history_search_forward() knows if any other operations * were performed in between. */ gl->last_search = gl->keyseq_count; /* * Search backwards for a match to the part of the line which precedes the * cursor. */ if(_glh_find_backwards(gl->glh, gl->line, gl->linelen) == NULL) return 0; /* * Record the number of characters in the new string. */ gl->ntotal = strlen(gl->line); /* * Arrange to have the cursor placed at the end of the new line. */ gl->buff_curpos = strlen(gl->line); /* * Erase and display the new line. */ return gl_redisplay(gl,1); } /*....................................................................... * This is the action function that recalls the previous line in the * history buffer who's prefix matches that specified in an earlier call * to gl_history_search_backward() or gl_history_search_forward(). */ static KT_KEY_FN(gl_history_re_search_backward) { return gl_history_search_backward(gl, -1); } /*....................................................................... * This is the action function that recalls the next line in the * history buffer who's prefix matches that specified in the earlier call * to gl_history_search_backward) which started the history search. * By setting count=-1, this can be used internally to force searching * for the prefix used in the last search. */ static KT_KEY_FN(gl_history_search_forward) { /* * In vi mode, switch to command mode, since the user is very * likely to want to move around newly recalled lines. */ gl_vi_command_mode(gl); /* * If the previous thing that the user did wasn't to execute a history * search function, set the search prefix equal to the string that * precedes the cursor. In vi command mode include the character that * is under the cursor in the string. If count<0 force a repeat search * even if the last command wasn't a history command. */ if(gl->last_search != gl->keyseq_count - 1 && count>=0 && _glh_search_prefix(gl->glh, gl->line, gl->buff_curpos + (gl->editor==GL_VI_MODE && gl->ntotal>0))) return 1; /* * Record the key sequence number in which this search function is * being executed, so that the next call to this function or * gl_history_search_backward() knows if any other operations * were performed in between. */ gl->last_search = gl->keyseq_count; /* * Search forwards for the next matching line. */ if(_glh_find_forwards(gl->glh, gl->line, gl->linelen) == NULL) return 0; /* * Record the number of characters in the new string. */ gl->ntotal = strlen(gl->line); /* * Arrange for the cursor to be placed at the end of the new line. */ gl->buff_curpos = strlen(gl->line); /* * Erase and display the new line. */ return gl_redisplay(gl,1); } /*....................................................................... * This is the action function that recalls the next line in the * history buffer who's prefix matches that specified in an earlier call * to gl_history_search_backward() or gl_history_search_forward(). */ static KT_KEY_FN(gl_history_re_search_forward) { return gl_history_search_forward(gl, -1); } /*....................................................................... * This is the tab completion function that completes the filename that * precedes the cursor position. */ static KT_KEY_FN(gl_complete_word) { CplMatches *matches; /* The possible completions */ int redisplay=0; /* True if the whole line needs to be redrawn */ int suffix_len; /* The length of the completion extension */ int cont_len; /* The length of any continuation suffix */ int nextra; /* The number of characters being added to the */ /* total length of the line. */ int buff_pos; /* The buffer index at which the completion is */ /* to be inserted. */ /* * In vi command mode, switch to append mode so that the character below * the character is included in the completion (otherwise people can't * complete at the end of the line). */ if(gl->vi.command && gl_vi_append(gl, 0)) return 1; /* * Get the cursor position at which the completion is to be inserted. */ buff_pos = gl->buff_curpos; /* * Perform the completion. */ matches = cpl_complete_word(gl->cpl, gl->line, gl->buff_curpos, gl->cpl_data, gl->cpl_fn); if(!matches) { if(gl->echo && fprintf(gl->output_fp, "\r\n%s\n", cpl_last_error(gl->cpl)) < 0) return 1; gl->term_curpos = 0; redisplay = 1; /* * Are there any completions? */ } else if(matches->nmatch >= 1) { /* * If there any ambiguous matches, report them, starting on a new line. */ if(matches->nmatch > 1 && gl->echo) { if(fprintf(gl->output_fp, "\r\n") < 0) return 1; cpl_list_completions(matches, gl->output_fp, gl->ncolumn); redisplay = 1; }; /* * If the callback called gl_change_prompt(), we will need to redisplay * the whole line. */ if(gl->prompt_changed) redisplay = 1; /* * Get the length of the suffix and any continuation suffix to add to it. */ suffix_len = strlen(matches->suffix); cont_len = strlen(matches->cont_suffix); /* * If there is an unambiguous match, and the continuation suffix ends in * a newline, strip that newline and arrange to have getline return * after this action function returns. */ if(matches->nmatch==1 && cont_len > 0 && matches->cont_suffix[cont_len - 1] == '\n') { cont_len--; if(gl_newline(gl, 1)) return 1; }; /* * Work out the number of characters that are to be added. */ nextra = suffix_len + cont_len; /* * Is there anything to be added? */ if(nextra) { /* * Will there be space for the expansion in the line buffer? */ if(gl->ntotal + nextra < gl->linelen) { /* * Make room to insert the filename extension. */ memmove(gl->line + gl->buff_curpos + nextra, gl->line + gl->buff_curpos, gl->ntotal - gl->buff_curpos); /* * Insert the filename extension. */ memcpy(gl->line + gl->buff_curpos, matches->suffix, suffix_len); /* * Add the terminating characters. */ memcpy(gl->line + gl->buff_curpos + suffix_len, matches->cont_suffix, cont_len); /* * Record the increased length of the line. */ gl->ntotal += nextra; /* * Place the cursor position at the end of the completion. */ gl->buff_curpos += nextra; /* * Terminate the extended line. */ gl->line[gl->ntotal] = '\0'; /* * If we don't have to redisplay the whole line, redisplay the part * of the line which follows the original cursor position, and place * the cursor at the end of the completion. */ if(!redisplay) { if(gl_output_control_sequence(gl, gl->nline, gl->clear_eod) || gl_output_string(gl, gl->line + buff_pos, '\0') || gl_place_cursor(gl, gl->buff_curpos)) return 1; }; } else { fprintf(stderr, "\r\nInsufficient room in line for file completion.\r\n"); redisplay = 1; }; }; }; /* * Redisplay the whole line? */ if(redisplay) { gl->term_curpos = 0; if(gl_redisplay(gl,1)) return 1; }; return 0; } /*....................................................................... * This is the function that expands the filename that precedes the * cursor position. It expands ~user/ expressions, $envvar expressions, * and wildcards. */ static KT_KEY_FN(gl_expand_filename) { char *start_path; /* The pointer to the start of the pathname in */ /* gl->line[]. */ FileExpansion *result; /* The results of the filename expansion */ int pathlen; /* The length of the pathname being expanded */ int redisplay=0; /* True if the whole line needs to be redrawn */ int length; /* The number of characters needed to display the */ /* expanded files. */ int nextra; /* The number of characters to be added */ int i,j; /* * In vi command mode, switch to append mode so that the character below * the character is included in the completion (otherwise people can't * complete at the end of the line). */ if(gl->vi.command && gl_vi_append(gl, 0)) return 1; /* * Locate the start of the filename that precedes the cursor position. */ start_path = _pu_start_of_path(gl->line, gl->buff_curpos > 0 ? gl->buff_curpos : 0); if(!start_path) return 1; /* * Get the length of the string that is to be expanded. */ pathlen = gl->buff_curpos - (start_path - gl->line); /* * Attempt to expand it. */ result = ef_expand_file(gl->ef, start_path, pathlen); /* * If there was an error, report the error on a new line, then redraw * the original line. */ if(!result) { if(gl->echo && fprintf(gl->output_fp, "\r\n%s\n", ef_last_error(gl->ef)) < 0) return 1; gl->term_curpos = 0; return gl_redisplay(gl,1); }; /* * If no files matched, report this as well. */ if(result->nfile == 0 || !result->exists) { if(gl->echo && fprintf(gl->output_fp, "\r\nNo files match.\n") < 0) return 1; gl->term_curpos = 0; return gl_redisplay(gl,1); }; /* * If in vi command mode, preserve the current line for potential use by * vi-undo. */ gl_save_for_undo(gl); /* * Work out how much space we will need to display all of the matching * filenames, taking account of the space that we need to place between * them, and the number of additional '\' characters needed to escape * spaces, tabs and backslash characters in the individual filenames. */ length = 0; for(i=0; infile; i++) { char *file = result->files[i]; while(*file) { int c = *file++; switch(c) { case ' ': case '\t': case '\\': case '*': case '?': case '[': length++; /* Count extra backslash characters */ }; length++; /* Count the character itself */ }; length++; /* Count the space that follows each filename */ }; /* * Work out the number of characters that are to be added. */ nextra = length - pathlen; /* * Will there be space for the expansion in the line buffer? */ if(gl->ntotal + nextra >= gl->linelen) { fprintf(stderr, "\r\nInsufficient room in line for file expansion.\r\n"); redisplay = 1; } else { /* * Do we need to move the part of the line that followed the unexpanded * filename? */ if(nextra != 0) { memmove(gl->line + gl->buff_curpos + nextra, gl->line + gl->buff_curpos, gl->ntotal - gl->buff_curpos); }; /* * Insert the filenames, separated by spaces, and with internal spaces, * tabs and backslashes escaped with backslashes. */ for(i=0,j=start_path - gl->line; infile; i++) { char *file = result->files[i]; while(*file) { int c = *file++; switch(c) { case ' ': case '\t': case '\\': case '*': case '?': case '[': gl->line[j++] = '\\'; }; gl->line[j++] = c; }; gl->line[j++] = ' '; }; /* * Record the increased length of the line. */ gl->ntotal += nextra; /* * Place the cursor position at the end of the expansion. */ gl->buff_curpos += nextra; /* * Terminate the extended line. */ gl->line[gl->ntotal] = '\0'; }; /* * Display the whole line on a new line? */ if(redisplay) { gl->term_curpos = 0; return gl_redisplay(gl,1); }; /* * Otherwise redisplay the part of the line which follows the start of * the original filename. */ if(gl_set_term_curpos(gl, gl_buff_curpos_to_term_curpos(gl, start_path - gl->line)) || gl_output_control_sequence(gl, gl->nline, gl->clear_eod) || gl_output_string(gl, start_path, gl->line[gl->buff_curpos])) return 1; /* * Restore the cursor position to the end of the expansion. */ return gl_place_cursor(gl, gl->buff_curpos); } /*....................................................................... * This is the action function that lists glob expansions of the * filename that precedes the cursor position. It expands ~user/ * expressions, $envvar expressions, and wildcards. */ static KT_KEY_FN(gl_list_glob) { char *start_path; /* The pointer to the start of the pathname in */ /* gl->line[]. */ FileExpansion *result; /* The results of the filename expansion */ int pathlen; /* The length of the pathname being expanded */ /* * Locate the start of the filename that precedes the cursor position. */ start_path = _pu_start_of_path(gl->line, gl->buff_curpos > 0 ? gl->buff_curpos : 0); if(!start_path) return 1; /* * Get the length of the string that is to be expanded. */ pathlen = gl->buff_curpos - (start_path - gl->line); /* * Attempt to expand it. */ result = ef_expand_file(gl->ef, start_path, pathlen); /* * If there was an error, report the error. */ if(!result) { if(gl->echo && fprintf(gl->output_fp, "\r\n%s\n", ef_last_error(gl->ef)) < 0) return 1; /* * If no files matched, report this as well. */ } else if(result->nfile == 0 || !result->exists) { if(gl->echo && fprintf(gl->output_fp, "\r\nNo files match.\n") < 0) return 1; /* * List the matching expansions. */ } else if(gl->echo) { if(fprintf(gl->output_fp, "\r\n") < 0) return 1; ef_list_expansions(result, gl->output_fp, gl->ncolumn); }; /* * Redisplay the line being edited. */ gl->term_curpos = 0; return gl_redisplay(gl,1); } /*....................................................................... * Return non-zero if a character should be considered a part of a word. * * Input: * c int The character to be tested. * Output: * return int True if the character should be considered part of a word. */ static int gl_is_word_char(int c) { return isalnum((int)(unsigned char)c) || strchr(GL_WORD_CHARS, c) != NULL; } /*....................................................................... * Override the builtin file-completion callback that is bound to the * "complete_word" action function. * * Input: * gl GetLine * The resource object of the command-line input * module. * data void * This is passed to match_fn() whenever it is * called. It could, for example, point to a * symbol table where match_fn() could look * for possible completions. * match_fn CplMatchFn * The function that will identify the prefix * to be completed from the input line, and * report matching symbols. * Output: * return int 0 - OK. * 1 - Error. */ int gl_customize_completion(GetLine *gl, void *data, CplMatchFn *match_fn) { /* * Check the arguments. */ if(!gl || !match_fn) { fprintf(stderr, "gl_customize_completion: NULL argument(s).\n"); return 1; }; /* * Record the new completion function and its callback data. */ gl->cpl_fn = match_fn; gl->cpl_data = data; return 0; } /*....................................................................... * Change the terminal (or stream) that getline interacts with. * * Input: * gl GetLine * The resource object of the command-line input * module. * input_fp FILE * The stdio stream to read from. * output_fp FILE * The stdio stream to write to. * term char * The terminal type. This can be NULL if * either or both of input_fp and output_fp don't * refer to a terminal. Otherwise it should refer * to an entry in the terminal information database. * Output: * return int 0 - OK. * 1 - Error. */ int gl_change_terminal(GetLine *gl, FILE *input_fp, FILE *output_fp, const char *term) { int is_term = 0; /* True if both input_fd and output_fd are associated */ /* with a terminal. */ /* * Require that input_fp and output_fp both be valid. */ if(!input_fp || !output_fp) { fprintf(stderr, "\r\ngl_change_terminal: Bad input/output stream(s).\n"); return 1; }; /* * If we are displacing a previous terminal, remove it from the list * of fds being watched. */ #ifdef HAVE_SELECT if(gl->input_fd >= 0) FD_CLR(gl->input_fd, &gl->rfds); #endif /* * Record the file descriptors and streams. */ gl->input_fp = input_fp; gl->input_fd = fileno(input_fp); gl->output_fp = output_fp; gl->output_fd = fileno(output_fp); /* * Make sure that the file descriptor will be visible in the set to * be watched. */ #ifdef HAVE_SELECT FD_SET(gl->input_fd, &gl->rfds); if(gl->input_fd > gl->max_fd) gl->max_fd = gl->input_fd; #endif /* * Disable terminal interaction until we have enough info to interact * with the terminal. */ gl->is_term = 0; /* * For terminal editing, we need both output_fd and input_fd to refer to * a terminal. While we can't verify that they both point to the same * terminal, we can verify that they point to terminals. */ if (! gl->is_net) is_term = isatty(gl->input_fd) && isatty(gl->output_fd); /* * If we are interacting with a terminal and no terminal type has been * specified, treat it as a generic ANSI terminal. */ if(is_term && !term) term = "ansi"; /* * Make a copy of the terminal type string. */ if(term != gl->term) { /* * Delete any old terminal type string. */ if(gl->term) { free(gl->term); gl->term = NULL; }; /* * Make a copy of the new terminal-type string, if any. */ if(term) { gl->term = (char *) malloc(strlen(term)+1); if(gl->term) strncpy(gl->term, term, strlen(term)+1); }; }; /* * Clear any terminal-specific key bindings that were taken from the * settings of the last terminal. */ _kt_clear_bindings(gl->bindings, KTB_TERM); /* * If we have a terminal install new bindings for it. */ if(is_term) { #ifndef __MINGW32__ /* * Get the current settings of the terminal. */ if(tcgetattr(gl->input_fd, &gl->oldattr)) { fprintf(stderr, "\r\ngl_change_terminal: tcgetattr error: %s\n", strerror(errno)); return 1; }; #endif /* __MINGW32__ */ /* * Lookup the terminal control string and size information. */ if(gl_control_strings(gl, term)) return 1; /* * We now have enough info to interact with the terminal. */ gl->is_term = 1; /* * Bind terminal-specific keys. */ if(gl_bind_terminal_keys(gl)) return 1; }; if (gl->is_net) { /* * Lookup the terminal control string and size information. */ if(gl_control_strings(gl, term)) return 1; /* * Bind terminal-specific keys. */ if(gl_bind_terminal_keys(gl)) return 1; #ifndef __MINGW32__ /* * Unbind the dangerous keys that affect the remote program. */ if(gl_bind_control_char(gl, KTB_TERM, gl->oldattr.c_cc[VINTR], NULL) || gl_bind_control_char(gl, KTB_TERM, gl->oldattr.c_cc[VQUIT], NULL) || gl_bind_control_char(gl, KTB_TERM, gl->oldattr.c_cc[VSUSP], NULL)) return 1; if(gl->editor == GL_VI_MODE) { if(gl_bind_control_char(gl, KTB_TERM, MAKE_META(gl->oldattr.c_cc[VINTR]), NULL) || gl_bind_control_char(gl, KTB_TERM, MAKE_META(gl->oldattr.c_cc[VQUIT]), NULL) || gl_bind_control_char(gl, KTB_TERM, MAKE_META(gl->oldattr.c_cc[VSUSP]), NULL)) return 1; }; #endif }; return 0; } /*....................................................................... * Set up terminal-specific key bindings. * * Input: * gl GetLine * The resource object of the command-line input * module. * Output: * return int 0 - OK. * 1 - Error. */ static int gl_bind_terminal_keys(GetLine *gl) { #ifndef __MINGW32__ /* * Install key-bindings for the special terminal characters. */ if(gl_bind_control_char(gl, KTB_TERM, gl->oldattr.c_cc[VINTR], "user-interrupt") || gl_bind_control_char(gl, KTB_TERM, gl->oldattr.c_cc[VQUIT], "abort") || gl_bind_control_char(gl, KTB_TERM, gl->oldattr.c_cc[VSUSP], "suspend")) return 1; /* * In vi-mode, arrange for the above characters to be seen in command * mode. */ if(gl->editor == GL_VI_MODE) { if(gl_bind_control_char(gl, KTB_TERM, MAKE_META(gl->oldattr.c_cc[VINTR]), "user-interrupt") || gl_bind_control_char(gl, KTB_TERM, MAKE_META(gl->oldattr.c_cc[VQUIT]), "abort") || gl_bind_control_char(gl, KTB_TERM, MAKE_META(gl->oldattr.c_cc[VSUSP]), "suspend")) return 1; }; /* * Non-universal special keys. */ #ifdef VLNEXT if(gl_bind_control_char(gl, KTB_TERM, gl->oldattr.c_cc[VLNEXT], "literal-next")) return 1; #else if(_kt_set_keybinding(gl->bindings, KTB_TERM, "^V", "literal-next")) return 1; #endif #endif /* __MINGW32__ */ /* * Bind action functions to the terminal-specific arrow keys * looked up by gl_control_strings(). */ if(_gl_bind_arrow_keys(gl)) return 1; return 0; } /*....................................................................... * This function is normally bound to control-D. When it is invoked within * a line it deletes the character which follows the cursor. When invoked * at the end of the line it lists possible file completions, and when * invoked on an empty line it causes gl_get_line() to return EOF. This * function emulates the one that is normally bound to control-D by tcsh. */ static KT_KEY_FN(gl_del_char_or_list_or_eof) { /* * If we have an empty line arrange to return EOF. */ if(gl->ntotal < 1) { return 1; /* * If we are at the end of the line list possible completions. */ } else if(gl->buff_curpos >= gl->ntotal) { /* * Get the list of possible completions. */ CplMatches *matches = cpl_complete_word(gl->cpl, gl->line, gl->buff_curpos, gl->cpl_data, gl->cpl_fn); if(!matches) { if(gl->echo && fprintf(gl->output_fp, "\r\n%s\n", cpl_last_error(gl->cpl)) < 0) return 1; gl->term_curpos = 0; /* * List the matches. */ } else if(matches->nmatch > 0 && gl->echo) { if(fprintf(gl->output_fp, "\r\n") < 0) return 1; cpl_list_completions(matches, gl->output_fp, gl->ncolumn); }; /* * Redisplay the line unchanged. */ return gl_redisplay(gl,1); /* * Within the line delete the character that follows the cursor. */ } else { /* * If in vi command mode, first preserve the current line for potential use * by vi-undo. */ gl_save_for_undo(gl); /* * Delete 'count' characters. */ return gl_forward_delete_char(gl, count); }; } /*....................................................................... * This function is normally bound to control-D in vi mode. When it is * invoked within a line it lists possible file completions, and when * invoked on an empty line it causes gl_get_line() to return EOF. This * function emulates the one that is normally bound to control-D by tcsh. */ static KT_KEY_FN(gl_list_or_eof) { /* * If we have an empty line arrange to return EOF. */ if(gl->ntotal < 1) { return 1; /* * Otherwise list possible completions. */ } else { /* * Get the list of possible completions. */ CplMatches *matches = cpl_complete_word(gl->cpl, gl->line, gl->buff_curpos, gl->cpl_data, gl->cpl_fn); if(!matches) { if(gl->echo && fprintf(gl->output_fp, "\r\n%s\n", cpl_last_error(gl->cpl)) < 0) return 1; gl->term_curpos = 0; /* * List the matches. */ } else if(matches->nmatch > 0 && gl->echo) { if(fprintf(gl->output_fp, "\r\n") < 0) return 1; cpl_list_completions(matches, gl->output_fp, gl->ncolumn); }; /* * Redisplay the line unchanged. */ return gl_redisplay(gl,1); }; } /*....................................................................... * Where the user has used the symbolic arrow-key names to specify * arrow key bindings, bind the specified action functions to the default * and terminal specific arrow key sequences. * * Input: * gl GetLine * The getline resource object. * Output: * return int 0 - OK. * 1 - Error. */ static int _gl_bind_arrow_keys(GetLine *gl) { /* * Process each of the arrow keys. */ if(_gl_rebind_arrow_key(gl->bindings, "up", gl->u_arrow, "^[[A", "^[OA") || _gl_rebind_arrow_key(gl->bindings, "down", gl->d_arrow, "^[[B", "^[OB") || _gl_rebind_arrow_key(gl->bindings, "left", gl->l_arrow, "^[[D", "^[OD") || _gl_rebind_arrow_key(gl->bindings, "right", gl->r_arrow, "^[[C", "^[OC")) return 1; return 0; } /*....................................................................... * Lookup the action function of a symbolic arrow-key binding, and bind * it to the terminal-specific and default arrow-key sequences. Note that * we don't trust the terminal-specified key sequences to be correct. * The main reason for this is that on some machines the xterm terminfo * entry is for hardware X-terminals, rather than xterm terminal emulators * and the two terminal types emit different character sequences when the * their cursor keys are pressed. As a result we also supply a couple * of default key sequences. * * Input: * bindings KeyTab * The table of key bindings. * name char * The symbolic name of the arrow key. * term_seq char * The terminal-specific arrow-key sequence. * def_seq1 char * The first default arrow-key sequence. * def_seq2 char * The second arrow-key sequence. * Output: * return int 0 - OK. * 1 - Error. */ static int _gl_rebind_arrow_key(KeyTab *bindings, const char *name, const char *term_seq, const char *def_seq1, const char *def_seq2) { int first,last; /* The indexes of the first and last matching entries */ /* * Lookup the key binding for the symbolic name of the arrow key. This * will either be the default action, or a user provided one. */ if(_kt_lookup_keybinding(bindings, name, strlen(name), &first, &last) == KT_EXACT_MATCH) { /* * Get the action function. */ KtKeyFn *key_fn = bindings->table[first].keyfn; /* * Bind this to each of the specified key sequences. */ if((term_seq && _kt_set_keyfn(bindings, KTB_TERM, term_seq, key_fn)) || (def_seq1 && _kt_set_keyfn(bindings, KTB_NORM, def_seq1, key_fn)) || (def_seq2 && _kt_set_keyfn(bindings, KTB_NORM, def_seq2, key_fn))) return 1; }; return 0; } /*....................................................................... * Read getline configuration information from a given file. * * Input: * gl GetLine * The getline resource object. * filename const char * The name of the file to read configuration * information from. The contents of this file * are as described in the gl_get_line(3) man * page for the default ~/.teclarc configuration * file. * who KtBinder Who bindings are to be installed for. * Output: * return int 0 - OK. * 1 - Irrecoverable error. */ static int _gl_read_config_file(GetLine *gl, const char *filename, KtBinder who) { FileExpansion *expansion; /* The expansion of the filename */ FILE *fp; /* The opened file */ int waserr = 0; /* True if an error occurred while reading */ int lineno = 1; /* The line number being processed */ /* * Check the arguments. */ if(!gl || !filename) { fprintf(stderr, "_gl_read_config_file: Invalid arguments.\n"); return 1; }; /* * Expand the filename. */ expansion = ef_expand_file(gl->ef, filename, -1); if(!expansion) { fprintf(stderr, "Unable to expand %s (%s).\n", filename, ef_last_error(gl->ef)); return 1; }; /* * Attempt to open the file. */ fp = fopen(expansion->files[0], "r"); /* * It isn't an error for there to be no configuration file. */ if(!fp) return 0; /* * Parse the contents of the file. */ while(!waserr && !feof(fp)) waserr = _gl_parse_config_line(gl, fp, glc_file_getc, filename, who, &lineno); /* * Bind action functions to the terminal-specific arrow keys. */ if(_gl_bind_arrow_keys(gl)) return 1; /* * Clean up. */ (void) fclose(fp); return waserr; } /*....................................................................... * Read GetLine configuration information from a string. The contents of * the string are the same as those described in the gl_get_line(3) * man page for the contents of the ~/.teclarc configuration file. */ static int _gl_read_config_string(GetLine *gl, const char *buffer, KtBinder who) { const char *bptr; /* A pointer into buffer[] */ int waserr = 0; /* True if an error occurred while reading */ int lineno = 1; /* The line number being processed */ /* * Check the arguments. */ if(!gl || !buffer) { fprintf(stderr, "_gl_read_config_string: Invalid arguments.\n"); return 1; }; /* * Get a pointer to the start of the buffer. */ bptr = buffer; /* * Parse the contents of the buffer. */ while(!waserr && *bptr) waserr = _gl_parse_config_line(gl, &bptr, glc_buff_getc, "", who, &lineno); /* * Bind action functions to the terminal-specific arrow keys. */ if(_gl_bind_arrow_keys(gl)) return 1; return waserr; } /*....................................................................... * Parse the next line of a getline configuration file. * * Input: * gl GetLine * The getline resource object. * stream void * The pointer representing the stream to be read * by getc_fn(). * getc_fn GlcGetcFn * A callback function which when called with * 'stream' as its argument, returns the next * unread character from the stream. * origin const char * The name of the entity being read (eg. a * file name). * who KtBinder Who bindings are to be installed for. * Input/Output: * lineno int * The line number being processed is to be * maintained in *lineno. * Output: * return int 0 - OK. * 1 - Irrecoverable error. */ static int _gl_parse_config_line(GetLine *gl, void *stream, GlcGetcFn *getc_fn, const char *origin, KtBinder who, int *lineno) { char buffer[GL_CONF_BUFLEN+1]; /* The input line buffer */ char *argv[GL_CONF_MAXARG]; /* The argument list */ int argc = 0; /* The number of arguments in argv[] */ int c; /* A character from the file */ int escaped = 0; /* True if the next character is escaped */ int i; /* * Skip spaces and tabs. */ do c = getc_fn(stream); while(c==' ' || c=='\t'); /* * Comments extend to the end of the line. */ if(c=='#') do c = getc_fn(stream); while(c != '\n' && c != EOF); /* * Ignore empty lines. */ if(c=='\n' || c==EOF) { (*lineno)++; return 0; }; /* * Record the buffer location of the start of the first argument. */ argv[argc] = buffer; /* * Read the rest of the line, stopping early if a comment is seen, or * the buffer overflows, and replacing sequences of spaces with a * '\0', and recording the thus terminated string as an argument. */ i = 0; while(i= GL_CONF_MAXARG) { fprintf(stderr, "%s:%d: Too many arguments.\n", origin, *lineno); do c = getc_fn(stream); while(c != '\n' && c != EOF); /* Skip past eol */ return 0; }; argv[argc] = buffer + i; /* * The next character was preceded by spaces, so it isn't escaped. */ escaped = 0; } else { /* * If we hit an unescaped backslash, this means that we should arrange * to treat the next character like a simple alphabetical character. */ if(c=='\\' && !escaped) { escaped = 1; /* * Splice lines where the newline is escaped. */ } else if(c=='\n' && escaped) { (*lineno)++; /* * Record a normal character, preserving any preceding backslash. */ } else { if(escaped) buffer[i++] = '\\'; if(i>=GL_CONF_BUFLEN) break; escaped = 0; buffer[i++] = c; }; /* * Get the next character. */ c = getc_fn(stream); }; }; /* * Did the buffer overflow? */ if(i>=GL_CONF_BUFLEN) { fprintf(stderr, "%s:%d: Line too long.\n", origin, *lineno); return 0; }; /* * The first argument should be a command name. */ if(strcmp(argv[0], "bind") == 0) { const char *action = NULL; /* A NULL action removes a keybinding */ const char *keyseq = NULL; switch(argc) { case 3: action = argv[2]; case 2: /* Note the intentional fallthrough */ keyseq = argv[1]; /* * Attempt to record the new keybinding. */ if(_kt_set_keybinding(gl->bindings, who, keyseq, action)) { fprintf(stderr, "The error occurred at line %d of %s.\n", *lineno, origin); }; break; default: fprintf(stderr, "%s:%d: Wrong number of arguments.\n", origin, *lineno); }; } else if(strcmp(argv[0], "edit-mode") == 0) { if(argc == 2 && strcmp(argv[1], "emacs") == 0) { gl_change_editor(gl, GL_EMACS_MODE); } else if(argc == 2 && strcmp(argv[1], "vi") == 0) { gl_change_editor(gl, GL_VI_MODE); } else if(argc == 2 && strcmp(argv[1], "none") == 0) { gl_change_editor(gl, GL_NO_EDITOR); } else { fprintf(stderr, "%s:%d: The argument of editor should be vi or emacs.\n", origin, *lineno); }; } else if(strcmp(argv[0], "nobeep") == 0) { gl->silence_bell = 1; } else { fprintf(stderr, "%s:%d: Unknown command name '%s'.\n", origin, *lineno, argv[0]); }; /* * Skip any trailing comment. */ while(c != '\n' && c != EOF) c = getc_fn(stream); (*lineno)++; return 0; } /*....................................................................... * This is the _gl_parse_config_line() callback function which reads the * next character from a configuration file. */ static GLC_GETC_FN(glc_file_getc) { return fgetc((FILE *) stream); } /*....................................................................... * This is the _gl_parse_config_line() callback function which reads the * next character from a buffer. Its stream argument is a pointer to a * variable which is, in turn, a pointer into the buffer being read from. */ static GLC_GETC_FN(glc_buff_getc) { const char **lptr = (char const **) stream; return **lptr ? *(*lptr)++ : EOF; } /*....................................................................... * When this action is triggered, it arranges to temporarily read command * lines from the regular file whos name precedes the cursor. * The current line is first discarded. */ static KT_KEY_FN(gl_read_from_file) { char *start_path; /* The pointer to the start of the pathname in */ /* gl->line[]. */ FileExpansion *result; /* The results of the filename expansion */ int pathlen; /* The length of the pathname being expanded */ int error_reported = 0; /* True after an error has been reported */ /* * Locate the start of the filename that precedes the cursor position. */ start_path = _pu_start_of_path(gl->line, gl->buff_curpos > 0 ? gl->buff_curpos : 0); if(!start_path) return 1; /* * Get the length of the pathname string. */ pathlen = gl->buff_curpos - (start_path - gl->line); /* * Attempt to expand the pathname. */ result = ef_expand_file(gl->ef, start_path, pathlen); /* * If there was an error, report the error on a new line, then redraw * the original line. */ if(!result) { if(gl->echo && fprintf(gl->output_fp, "\r\n%s\n", ef_last_error(gl->ef)) < 0) return 1; error_reported = 1; /* * If no files matched, report this as well. */ } else if(result->nfile == 0 || !result->exists) { if(gl->echo && fprintf(gl->output_fp, "\r\nNo files match.\n") < 0) return 1; error_reported = 1; /* * Complain if more than one file matches. */ } else if(result->nfile > 1) { if(gl->echo && fprintf(gl->output_fp, "\r\nMore than one file matches.\n") < 0) return 1; error_reported = 1; /* * Disallow input from anything but normal files. In principle we could * also support input from named pipes. Terminal files would be a problem * since we wouldn't know the terminal type, and other types of files * might cause the library to lock up. */ } else if(!_pu_path_is_file(result->files[0])) { if(gl->echo && fprintf(gl->output_fp, "\r\nNot a normal file.\n") < 0) return 1; error_reported = 1; } else { /* * Attempt to open and install the specified file for reading. */ gl->file_fp = fopen(result->files[0], "r"); if(!gl->file_fp) { if(gl->echo && fprintf(gl->output_fp, "\r\nUnable to open: %s\n", result->files[0]) < 0) return 1; error_reported = 1; }; /* * Inform the user what is happening. */ if(gl->echo && fprintf(gl->output_fp, "\r\n\n", result->files[0]) < 0) return 1; }; /* * If an error was reported, redisplay the current line. */ if(error_reported) { gl->term_curpos = 0; return gl_redisplay(gl,1); }; return 0; } /*....................................................................... * Close any temporary file that is being used for input. * * Input: * gl GetLine * The getline resource object. */ static void gl_revert_input(GetLine *gl) { if(gl->file_fp) fclose(gl->file_fp); gl->file_fp = NULL; } /*....................................................................... * This is the action function that recalls the oldest line in the * history buffer. */ static KT_KEY_FN(gl_beginning_of_history) { /* * In vi mode, switch to command mode, since the user is very * likely to want to move around newly recalled lines. */ gl_vi_command_mode(gl); /* * Forget any previous recall session. */ gl->preload_id = 0; /* * Recall the next oldest line in the history list. */ if(_glh_oldest_line(gl->glh, gl->line, gl->linelen) == NULL) return 0; /* * Record the number of characters in the new string. */ gl->ntotal = strlen(gl->line); /* * Arrange to have the cursor placed at the end of the new line. */ gl->buff_curpos = strlen(gl->line); /* * Erase and display the new line. */ return gl_redisplay(gl,1); } /*....................................................................... * If a history session is currently in progress, this action function * recalls the line that was being edited when the session started. If * no history session is in progress, it does nothing. */ static KT_KEY_FN(gl_end_of_history) { /* * In vi mode, switch to command mode, since the user is very * likely to want to move around newly recalled lines. */ gl_vi_command_mode(gl); /* * Forget any previous recall session. */ gl->preload_id = 0; /* * Recall the next oldest line in the history list. */ if(_glh_current_line(gl->glh, gl->line, gl->linelen) == NULL) return 0; /* * Record the number of characters in the new string. */ gl->ntotal = strlen(gl->line); /* * Arrange to have the cursor placed at the end of the new line. */ gl->buff_curpos = strlen(gl->line); /* * Erase and display the new line. */ return gl_redisplay(gl,1); } /*....................................................................... * This action function is treated specially, in that its count argument * is set to the end keystroke of the keysequence that activated it. * It accumulates a numeric argument, adding one digit on each call in * which the last keystroke was a numeric digit. */ static KT_KEY_FN(gl_digit_argument) { /* * Was the last keystroke a digit? */ int is_digit = isdigit((int)(unsigned char) count); /* * In vi command mode, a lone '0' means goto-start-of-line. */ if(gl->vi.command && gl->number < 0 && count == '0') return gl_beginning_of_line(gl, count); /* * Are we starting to accumulate a new number? */ if(gl->number < 0 || !is_digit) gl->number = 0; /* * Was the last keystroke a digit? */ if(is_digit) { /* * Read the numeric value of the digit, without assuming ASCII. */ int n; char s[2]; s[0] = count; s[1] = '\0'; n = atoi(s); /* * Append the new digit. */ gl->number = gl->number * 10 + n; }; return 0; } /*....................................................................... * The newline action function sets gl->endline to tell * gl_get_input_line() that the line is now complete. */ static KT_KEY_FN(gl_newline) { GlhLineID id; /* The last history line recalled while entering this line */ /* * Flag the line as ended. */ gl->endline = 1; /* * Record the next position in the history buffer, for potential * recall by an action function on the next call to gl_get_line(). */ id = _glh_line_id(gl->glh, 1); if(id) gl->preload_id = id; return 0; } /*....................................................................... * The 'repeat' action function sets gl->endline to tell * gl_get_input_line() that the line is now complete, and records the * ID of the next history line in gl->preload_id so that the next call * to gl_get_input_line() will preload the line with that history line. */ static KT_KEY_FN(gl_repeat_history) { gl->endline = 1; gl->preload_id = _glh_line_id(gl->glh, 1); gl->preload_history = 1; return 0; } /*....................................................................... * Flush unwritten characters to the terminal. * * Input: * gl GetLine * The getline resource object. * Output: * return int 0 - OK. * 1 - Error. */ static int gl_flush_output(GetLine *gl) { /* * Attempt to flush output to the terminal, restarting the output * if a signal is caught. */ while(fflush(gl->output_fp) != 0) { if(errno!=EINTR) return 1; }; return 0; } /*....................................................................... * Change the style of editing to emulate a given editor. * * Input: * gl GetLine * The getline resource object. * editor GlEditor The type of editor to emulate. * Output: * return int 0 - OK. * 1 - Error. */ static int gl_change_editor(GetLine *gl, GlEditor editor) { /* * Install the default key-bindings of the requested editor. */ switch(editor) { case GL_EMACS_MODE: _kt_clear_bindings(gl->bindings, KTB_NORM); _kt_clear_bindings(gl->bindings, KTB_TERM); (void) _kt_add_bindings(gl->bindings, KTB_NORM, gl_emacs_bindings, sizeof(gl_emacs_bindings)/sizeof(gl_emacs_bindings[0])); break; case GL_VI_MODE: _kt_clear_bindings(gl->bindings, KTB_NORM); _kt_clear_bindings(gl->bindings, KTB_TERM); (void) _kt_add_bindings(gl->bindings, KTB_NORM, gl_vi_bindings, sizeof(gl_vi_bindings)/sizeof(gl_vi_bindings[0])); break; case GL_NO_EDITOR: break; default: fprintf(stderr, "gl_change_editor: Unknown editor.\n"); return 1; }; /* * Record the new editing mode. */ gl->editor = editor; gl->vi.command = 0; /* Start in input mode */ gl->insert_curpos = 0; /* * Reinstate terminal-specific bindings. */ if(gl->editor != GL_NO_EDITOR && gl->input_fp) (void) gl_bind_terminal_keys(gl); return 0; } /*....................................................................... * This is an action function that switches to editing using emacs bindings */ static KT_KEY_FN(gl_emacs_editing_mode) { return gl_change_editor(gl, GL_EMACS_MODE); } /*....................................................................... * This is an action function that switches to editing using vi bindings */ static KT_KEY_FN(gl_vi_editing_mode) { return gl_change_editor(gl, GL_VI_MODE); } /*....................................................................... * This is the action function that switches to insert mode. */ static KT_KEY_FN(gl_vi_insert) { /* * If in vi command mode, preserve the current line for potential * use by vi-undo. */ gl_save_for_undo(gl); /* * Switch to vi insert mode. */ gl->insert = 1; gl->vi.command = 0; gl->insert_curpos = gl->buff_curpos; return 0; } /*....................................................................... * This is an action function that switches to overwrite mode. */ static KT_KEY_FN(gl_vi_overwrite) { /* * If in vi command mode, preserve the current line for potential * use by vi-undo. */ gl_save_for_undo(gl); /* * Switch to vi overwrite mode. */ gl->insert = 0; gl->vi.command = 0; gl->insert_curpos = gl->buff_curpos; return 0; } /*....................................................................... * This action function toggles the case of the character under the * cursor. */ static KT_KEY_FN(gl_change_case) { int i; /* * Keep a record of the current insert mode and the cursor position. */ int insert = gl->insert; /* * If in vi command mode, preserve the current line for potential * use by vi-undo. */ gl_save_for_undo(gl); /* * We want to overwrite the modified word. */ gl->insert = 0; /* * Toggle the case of 'count' characters. */ for(i=0; ibuff_curpos < gl->ntotal; i++) { char *cptr = gl->line + gl->buff_curpos++; /* * Convert the character to upper case? */ if(islower((int)(unsigned char) *cptr)) *cptr = toupper((int) *cptr); else if(isupper((int)(unsigned char) *cptr)) *cptr = tolower((int) *cptr); /* * Write the possibly modified character back. Note that for non-modified * characters we want to do this as well, so as to advance the cursor. */ if(gl_output_char(gl, *cptr, cptr[1])) return 1; }; /* * Restore the insertion mode. */ gl->insert = insert; return gl_place_cursor(gl, gl->buff_curpos); /* bounds check */ } /*....................................................................... * This is the action function which implements the vi-style action which * moves the cursor to the start of the line, then switches to insert mode. */ static KT_KEY_FN(gl_vi_insert_at_bol) { gl_save_for_undo(gl); return gl_beginning_of_line(gl, 0) || gl_vi_insert(gl, 0); } /*....................................................................... * This is the action function which implements the vi-style action which * moves the cursor to the end of the line, then switches to insert mode * to allow text to be appended to the line. */ static KT_KEY_FN(gl_vi_append_at_eol) { gl_save_for_undo(gl); gl->vi.command = 0; /* Allow cursor at EOL */ return gl_end_of_line(gl, 0) || gl_vi_insert(gl, 0); } /*....................................................................... * This is the action function which implements the vi-style action which * moves the cursor to right one then switches to insert mode, thus * allowing text to be appended after the next character. */ static KT_KEY_FN(gl_vi_append) { gl_save_for_undo(gl); gl->vi.command = 0; /* Allow cursor at EOL */ return gl_cursor_right(gl, 1) || gl_vi_insert(gl, 0); } /*....................................................................... * This action function moves the cursor to the column specified by the * numeric argument. Column indexes start at 1. */ static KT_KEY_FN(gl_goto_column) { return gl_place_cursor(gl, count - 1); } /*....................................................................... * Starting with the character under the cursor, replace 'count' * characters with the next character that the user types. */ static KT_KEY_FN(gl_vi_replace_char) { char c; /* The replacement character */ int i; /* * Keep a record of the current insert mode. */ int insert = gl->insert; /* * Get the replacement character. */ if(gl->vi.repeat.active) { c = gl->vi.repeat.input_char; } else { if(gl_read_character(gl, &c, -1)) return 1; gl->vi.repeat.input_char = c; }; /* * Are there 'count' characters to be replaced? */ if(gl->ntotal - gl->buff_curpos >= count) { /* * If in vi command mode, preserve the current line for potential * use by vi-undo. */ gl_save_for_undo(gl); /* * Temporarily switch to overwrite mode. */ gl->insert = 0; /* * Overwrite the current character plus count-1 subsequent characters * with the replacement character. */ for(i=0; iinsert = insert; }; return gl_place_cursor(gl, gl->buff_curpos); /* bounds check */ } /*....................................................................... * This is an action function which changes all characters between the * current cursor position and the end of the line. */ static KT_KEY_FN(gl_vi_change_rest_of_line) { gl_save_for_undo(gl); gl->vi.command = 0; /* Allow cursor at EOL */ return gl_kill_line(gl, count) || gl_vi_insert(gl, 0); } /*....................................................................... * This is an action function which changes all characters between the * start of the line and the current cursor position. */ static KT_KEY_FN(gl_vi_change_to_bol) { return gl_backward_kill_line(gl, count) || gl_vi_insert(gl, 0); } /*....................................................................... * This is an action function which deletes the entire contents of the * current line and switches to insert mode. */ static KT_KEY_FN(gl_vi_change_line) { return gl_delete_line(gl, count) || gl_vi_insert(gl, 0); } /*....................................................................... * Starting from the cursor position and looking towards the end of the * line, copy 'count' characters to the cut buffer. */ static KT_KEY_FN(gl_forward_copy_char) { /* * Limit the count to the number of characters available. */ if(gl->buff_curpos + count >= gl->ntotal) count = gl->ntotal - gl->buff_curpos; if(count < 0) count = 0; /* * Copy the characters to the cut buffer. */ memcpy(gl->cutbuf, gl->line + gl->buff_curpos, count); gl->cutbuf[count] = '\0'; return 0; } /*....................................................................... * Starting from the character before the cursor position and looking * backwards towards the start of the line, copy 'count' characters to * the cut buffer. */ static KT_KEY_FN(gl_backward_copy_char) { /* * Limit the count to the number of characters available. */ if(count > gl->buff_curpos) count = gl->buff_curpos; if(count < 0) count = 0; gl_place_cursor(gl, gl->buff_curpos - count); /* * Copy the characters to the cut buffer. */ memcpy(gl->cutbuf, gl->line + gl->buff_curpos, count); gl->cutbuf[count] = '\0'; return 0; } /*....................................................................... * Starting from the cursor position copy to the specified column into the * cut buffer. */ static KT_KEY_FN(gl_copy_to_column) { if (--count >= gl->buff_curpos) return gl_forward_copy_char(gl, count - gl->buff_curpos); else return gl_backward_copy_char(gl, gl->buff_curpos - count); } /*....................................................................... * Starting from the cursor position copy characters up to a matching * parenthesis into the cut buffer. */ static KT_KEY_FN(gl_copy_to_parenthesis) { int curpos = gl_index_of_matching_paren(gl); if(curpos >= 0) { gl_save_for_undo(gl); if(curpos >= gl->buff_curpos) return gl_forward_copy_char(gl, curpos - gl->buff_curpos + 1); else return gl_backward_copy_char(gl, ++gl->buff_curpos - curpos + 1); }; return 0; } /*....................................................................... * Starting from the cursor position copy the rest of the line into the * cut buffer. */ static KT_KEY_FN(gl_copy_rest_of_line) { /* * Copy the characters to the cut buffer. */ memcpy(gl->cutbuf, gl->line + gl->buff_curpos, gl->ntotal - gl->buff_curpos); gl->cutbuf[gl->ntotal - gl->buff_curpos] = '\0'; return 0; } /*....................................................................... * Copy from the beginning of the line to the cursor position into the * cut buffer. */ static KT_KEY_FN(gl_copy_to_bol) { /* * Copy the characters to the cut buffer. */ memcpy(gl->cutbuf, gl->line, gl->buff_curpos); gl->cutbuf[gl->buff_curpos] = '\0'; gl_place_cursor(gl, 0); return 0; } /*....................................................................... * Copy the entire line into the cut buffer. */ static KT_KEY_FN(gl_copy_line) { /* * Copy the characters to the cut buffer. */ memcpy(gl->cutbuf, gl->line, gl->ntotal); gl->cutbuf[gl->ntotal] = '\0'; return 0; } /*....................................................................... * Search forwards for the next character that the user enters. */ static KT_KEY_FN(gl_forward_find_char) { int pos = gl_find_char(gl, count, 1, 1, '\0'); return pos >= 0 && gl_place_cursor(gl, pos); } /*....................................................................... * Search backwards for the next character that the user enters. */ static KT_KEY_FN(gl_backward_find_char) { int pos = gl_find_char(gl, count, 0, 1, '\0'); return pos >= 0 && gl_place_cursor(gl, pos); } /*....................................................................... * Search forwards for the next character that the user enters. Move up to, * but not onto, the found character. */ static KT_KEY_FN(gl_forward_to_char) { int pos = gl_find_char(gl, count, 1, 0, '\0'); return pos >= 0 && gl_place_cursor(gl, pos); } /*....................................................................... * Search backwards for the next character that the user enters. Move back to, * but not onto, the found character. */ static KT_KEY_FN(gl_backward_to_char) { int pos = gl_find_char(gl, count, 0, 0, '\0'); return pos >= 0 && gl_place_cursor(gl, pos); } /*....................................................................... * Searching in a given direction, return the index of a given (or * read) character in the input line, or the character that precedes * it in the specified search direction. Return -1 if not found. * * Input: * gl GetLine * The getline resource object. * count int The number of times to search. * forward int True if searching forward. * onto int True if the search should end on top of the * character, false if the search should stop * one character before the character in the * specified search direction. * c char The character to be sought, or '\0' if the * character should be read from the user. * Output: * return int The index of the character in gl->line[], or * -1 if not found. */ static int gl_find_char(GetLine *gl, int count, int forward, int onto, char c) { int pos; /* The index reached in searching the input line */ int i; /* * Get a character from the user? */ if(!c) { /* * If we are in the process of repeating a previous change command, substitute * the last find character. */ if(gl->vi.repeat.active) { c = gl->vi.find_char; } else { if(gl_read_character(gl, &c, -1)) return -1; /* * Record the details of the new search, for use by repeat finds. */ gl->vi.find_forward = forward; gl->vi.find_onto = onto; gl->vi.find_char = c; }; }; /* * Which direction should we search? */ if(forward) { /* * Search forwards 'count' times for the character, starting with the * character that follows the cursor. */ for(i=0, pos=gl->buff_curpos; intotal; i++) { /* * Advance past the last match (or past the current cursor position * on the first search). */ pos++; /* * Search for the next instance of c. */ for( ; posntotal && c!=gl->line[pos]; pos++) ; }; /* * If the character was found and we have been requested to return the * position of the character that precedes the desired character, then * we have gone one character too far. */ if(!onto && posntotal) pos--; } else { /* * Search backwards 'count' times for the character, starting with the * character that precedes the cursor. */ for(i=0, pos=gl->buff_curpos; i= gl->insert_curpos; i++) { /* * Step back one from the last match (or from the current cursor * position on the first search). */ pos--; /* * Search for the next instance of c. */ for( ; pos>=gl->insert_curpos && c!=gl->line[pos]; pos--) ; }; /* * If the character was found and we have been requested to return the * position of the character that precedes the desired character, then * we have gone one character too far. */ if(!onto && pos>=gl->insert_curpos) pos++; }; /* * If found, return the cursor position of the count'th match. * Otherwise ring the terminal bell. */ if(pos >= gl->insert_curpos && pos < gl->ntotal) { return pos; } else { (void) gl_ring_bell(gl, 1); return -1; } } /*....................................................................... * Repeat the last character search in the same direction as the last * search. */ static KT_KEY_FN(gl_repeat_find_char) { int pos = gl->vi.find_char ? gl_find_char(gl, count, gl->vi.find_forward, gl->vi.find_onto, gl->vi.find_char) : -1; return pos >= 0 && gl_place_cursor(gl, pos); } /*....................................................................... * Repeat the last character search in the opposite direction as the last * search. */ static KT_KEY_FN(gl_invert_refind_char) { int pos = gl->vi.find_char ? gl_find_char(gl, count, !gl->vi.find_forward, gl->vi.find_onto, gl->vi.find_char) : -1; return pos >= 0 && gl_place_cursor(gl, pos); } /*....................................................................... * Search forward from the current position of the cursor for 'count' * word endings, returning the index of the last one found, or the end of * the line if there were less than 'count' words. * * Input: * gl GetLine * The getline resource object. * n int The number of word boundaries to search for. * Output: * return int The buffer index of the located position. */ static int gl_nth_word_end_forward(GetLine *gl, int n) { int bufpos; /* The buffer index being checked. */ int i; /* * In order to guarantee forward motion to the next word ending, * we need to start from one position to the right of the cursor * position, since this may already be at the end of a word. */ bufpos = gl->buff_curpos + 1; /* * If we are at the end of the line, return the index of the last * real character on the line. Note that this will be -1 if the line * is empty. */ if(bufpos >= gl->ntotal) return gl->ntotal - 1; /* * Search 'n' times, unless the end of the input line is reached first. */ for(i=0; intotal; i++) { /* * If we are not already within a word, skip to the start of the next word. */ for( ; bufposntotal && !gl_is_word_char((int)gl->line[bufpos]); bufpos++) ; /* * Find the end of the next word. */ for( ; bufposntotal && gl_is_word_char((int)gl->line[bufpos]); bufpos++) ; }; /* * We will have overshot. */ return bufpos > 0 ? bufpos-1 : bufpos; } /*....................................................................... * Search forward from the current position of the cursor for 'count' * word starts, returning the index of the last one found, or the end of * the line if there were less than 'count' words. * * Input: * gl GetLine * The getline resource object. * n int The number of word boundaries to search for. * Output: * return int The buffer index of the located position. */ static int gl_nth_word_start_forward(GetLine *gl, int n) { int bufpos; /* The buffer index being checked. */ int i; /* * Get the current cursor position. */ bufpos = gl->buff_curpos; /* * Search 'n' times, unless the end of the input line is reached first. */ for(i=0; intotal; i++) { /* * Find the end of the current word. */ for( ; bufposntotal && gl_is_word_char((int)gl->line[bufpos]); bufpos++) ; /* * Skip to the start of the next word. */ for( ; bufposntotal && !gl_is_word_char((int)gl->line[bufpos]); bufpos++) ; }; return bufpos; } /*....................................................................... * Search backward from the current position of the cursor for 'count' * word starts, returning the index of the last one found, or the start * of the line if there were less than 'count' words. * * Input: * gl GetLine * The getline resource object. * n int The number of word boundaries to search for. * Output: * return int The buffer index of the located position. */ static int gl_nth_word_start_backward(GetLine *gl, int n) { int bufpos; /* The buffer index being checked. */ int i; /* * Get the current cursor position. */ bufpos = gl->buff_curpos; /* * Search 'n' times, unless the beginning of the input line (or vi insertion * point) is reached first. */ for(i=0; i gl->insert_curpos; i++) { /* * Starting one character back from the last search, so as not to keep * settling on the same word-start, search backwards until finding a * word character. */ while(--bufpos >= gl->insert_curpos && !gl_is_word_char((int)gl->line[bufpos])) ; /* * Find the start of the word. */ while(--bufpos >= gl->insert_curpos && gl_is_word_char((int)gl->line[bufpos])) ; /* * We will have gone one character too far. */ bufpos++; }; return bufpos >= gl->insert_curpos ? bufpos : gl->insert_curpos; } /*....................................................................... * Copy one or more words into the cut buffer without moving the cursor * or deleting text. */ static KT_KEY_FN(gl_forward_copy_word) { /* * Find the location of the count'th start or end of a word * after the cursor, depending on whether in emacs or vi mode. */ int next = gl->editor == GL_EMACS_MODE ? gl_nth_word_end_forward(gl, count) : gl_nth_word_start_forward(gl, count); /* * How many characters are to be copied into the cut buffer? */ int n = next - gl->buff_curpos; /* * Copy the specified segment and terminate the string. */ memcpy(gl->cutbuf, gl->line + gl->buff_curpos, n); gl->cutbuf[n] = '\0'; return 0; } /*....................................................................... * Copy one or more words preceding the cursor into the cut buffer, * without moving the cursor or deleting text. */ static KT_KEY_FN(gl_backward_copy_word) { /* * Find the location of the count'th start of word before the cursor. */ int next = gl_nth_word_start_backward(gl, count); /* * How many characters are to be copied into the cut buffer? */ int n = gl->buff_curpos - next; gl_place_cursor(gl, next); /* * Copy the specified segment and terminate the string. */ memcpy(gl->cutbuf, gl->line + next, n); gl->cutbuf[n] = '\0'; return 0; } /*....................................................................... * Copy the characters between the cursor and the count'th instance of * a specified character in the input line, into the cut buffer. * * Input: * gl GetLine * The getline resource object. * count int The number of times to search. * c char The character to be searched for, or '\0' if * the character should be read from the user. * forward int True if searching forward. * onto int True if the search should end on top of the * character, false if the search should stop * one character before the character in the * specified search direction. * Output: * return int 0 - OK. * 1 - Error. * */ static int gl_copy_find(GetLine *gl, int count, char c, int forward, int onto) { int n; /* The number of characters in the cut buffer */ /* * Search for the character, and abort the operation if not found. */ int pos = gl_find_char(gl, count, forward, onto, c); if(pos < 0) return 0; /* * Copy the specified segment. */ if(forward) { n = pos + 1 - gl->buff_curpos; memcpy(gl->cutbuf, gl->line + gl->buff_curpos, n); } else { n = gl->buff_curpos - pos; memcpy(gl->cutbuf, gl->line + pos, n); if(gl->editor == GL_VI_MODE) gl_place_cursor(gl, pos); } /* * Terminate the copy. */ gl->cutbuf[n] = '\0'; return 0; } /*....................................................................... * Copy a section up to and including a specified character into the cut * buffer without moving the cursor or deleting text. */ static KT_KEY_FN(gl_forward_copy_find) { return gl_copy_find(gl, count, '\0', 1, 1); } /*....................................................................... * Copy a section back to and including a specified character into the cut * buffer without moving the cursor or deleting text. */ static KT_KEY_FN(gl_backward_copy_find) { return gl_copy_find(gl, count, '\0', 0, 1); } /*....................................................................... * Copy a section up to and not including a specified character into the cut * buffer without moving the cursor or deleting text. */ static KT_KEY_FN(gl_forward_copy_to) { return gl_copy_find(gl, count, '\0', 1, 0); } /*....................................................................... * Copy a section back to and not including a specified character into the cut * buffer without moving the cursor or deleting text. */ static KT_KEY_FN(gl_backward_copy_to) { return gl_copy_find(gl, count, '\0', 0, 0); } /*....................................................................... * Copy to a character specified in a previous search into the cut * buffer without moving the cursor or deleting text. */ static KT_KEY_FN(gl_copy_refind) { return gl_copy_find(gl, count, gl->vi.find_char, gl->vi.find_forward, gl->vi.find_onto); } /*....................................................................... * Copy to a character specified in a previous search, but in the opposite * direction, into the cut buffer without moving the cursor or deleting text. */ static KT_KEY_FN(gl_copy_invert_refind) { return gl_copy_find(gl, count, gl->vi.find_char, !gl->vi.find_forward, gl->vi.find_onto); } /*....................................................................... * Set the position of the cursor in the line input buffer and the * terminal. * * Input: * gl GetLine * The getline resource object. * buff_curpos int The new buffer cursor position. * Output: * return int 0 - OK. * 1 - Error. */ int gl_place_cursor(GetLine *gl, int buff_curpos) { /* * Don't allow the cursor position to go out of the bounds of the input * line. */ if(buff_curpos >= gl->ntotal) buff_curpos = gl->vi.command ? gl->ntotal-1 : gl->ntotal; if(buff_curpos < 0) buff_curpos = 0; /* * Record the new buffer position. */ gl->buff_curpos = buff_curpos; /* * Move the terminal cursor to the corresponding character. */ return gl_set_term_curpos(gl, gl_buff_curpos_to_term_curpos(gl, buff_curpos)); } /*....................................................................... * In vi command mode, this function saves the current line to the * historical buffer needed by the undo command. In emacs mode it does * nothing. In order to allow action functions to call other action * functions, gl_interpret_char() sets gl->vi.undo.saved to 0 before * invoking an action, and thereafter once any call to this function * has set it to 1, further calls are ignored. * * Input: * gl GetLine * The getline resource object. */ static void gl_save_for_undo(GetLine *gl) { if(gl->vi.command && !gl->vi.undo.saved) { strncpy(gl->vi.undo.line, gl->line, gl->linelen + 2); gl->vi.undo.buff_curpos = gl->buff_curpos; gl->vi.undo.ntotal = gl->ntotal; gl->vi.undo.saved = 1; }; if(gl->vi.command && !gl->vi.repeat.saved && gl->current_fn != gl_vi_repeat_change) { gl->vi.repeat.fn = gl->current_fn; gl->vi.repeat.count = gl->current_count; gl->vi.repeat.saved = 1; }; return; } /*....................................................................... * In vi mode, restore the line to the way it was before the last command * mode operation, storing the current line in the buffer so that the * undo operation itself can subsequently be undone. */ static KT_KEY_FN(gl_vi_undo) { /* * Get pointers into the two lines. */ char *undo_ptr = gl->vi.undo.line; char *line_ptr = gl->line; /* * Swap the characters of the two buffers up to the length of the shortest * line. */ while(*undo_ptr && *line_ptr) { char c = *undo_ptr; *undo_ptr++ = *line_ptr; *line_ptr++ = c; }; /* * Copy the rest directly. */ if(gl->ntotal > gl->vi.undo.ntotal) { strncpy(undo_ptr, line_ptr, gl->linelen + 2); *line_ptr = '\0'; } else { strncpy(line_ptr, undo_ptr, gl->linelen + 2); *undo_ptr = '\0'; }; /* * Swap the length information. */ { int ntotal = gl->ntotal; gl->ntotal = gl->vi.undo.ntotal; gl->vi.undo.ntotal = ntotal; }; /* * Set both cursor positions to the leftmost of the saved and current * cursor positions to emulate what vi does. */ if(gl->buff_curpos < gl->vi.undo.buff_curpos) gl->vi.undo.buff_curpos = gl->buff_curpos; else gl->buff_curpos = gl->vi.undo.buff_curpos; /* * Since we have bipassed calling gl_save_for_undo(), record repeat * information inline. */ gl->vi.repeat.fn = gl_vi_undo; gl->vi.repeat.count = 1; /* * Display the restored line. */ return gl_redisplay(gl,1); } /*....................................................................... * Delete the following word and leave the user in vi insert mode. */ static KT_KEY_FN(gl_vi_forward_change_word) { gl_save_for_undo(gl); gl->vi.command = 0; /* Allow cursor at EOL */ return gl_forward_delete_word(gl, count) || gl_vi_insert(gl, 0); } /*....................................................................... * Delete the preceding word and leave the user in vi insert mode. */ static KT_KEY_FN(gl_vi_backward_change_word) { return gl_backward_delete_word(gl, count) || gl_vi_insert(gl, 0); } /*....................................................................... * Delete the following section and leave the user in vi insert mode. */ static KT_KEY_FN(gl_vi_forward_change_find) { return gl_delete_find(gl, count, '\0', 1, 1, 1); } /*....................................................................... * Delete the preceding section and leave the user in vi insert mode. */ static KT_KEY_FN(gl_vi_backward_change_find) { return gl_delete_find(gl, count, '\0', 0, 1, 1); } /*....................................................................... * Delete the following section and leave the user in vi insert mode. */ static KT_KEY_FN(gl_vi_forward_change_to) { return gl_delete_find(gl, count, '\0', 1, 0, 1); } /*....................................................................... * Delete the preceding section and leave the user in vi insert mode. */ static KT_KEY_FN(gl_vi_backward_change_to) { return gl_delete_find(gl, count, '\0', 0, 0, 1); } /*....................................................................... * Delete to a character specified by a previous search and leave the user * in vi insert mode. */ static KT_KEY_FN(gl_vi_change_refind) { return gl_delete_find(gl, count, gl->vi.find_char, gl->vi.find_forward, gl->vi.find_onto, 1); } /*....................................................................... * Delete to a character specified by a previous search, but in the opposite * direction, and leave the user in vi insert mode. */ static KT_KEY_FN(gl_vi_change_invert_refind) { return gl_delete_find(gl, count, gl->vi.find_char, !gl->vi.find_forward, gl->vi.find_onto, 1); } /*....................................................................... * Delete the following character and leave the user in vi insert mode. */ static KT_KEY_FN(gl_vi_forward_change_char) { gl_save_for_undo(gl); gl->vi.command = 0; /* Allow cursor at EOL */ return gl_delete_chars(gl, count, 1) || gl_vi_insert(gl, 0); } /*....................................................................... * Delete the preceding character and leave the user in vi insert mode. */ static KT_KEY_FN(gl_vi_backward_change_char) { return gl_backward_delete_char(gl, count) || gl_vi_insert(gl, 0); } /*....................................................................... * Starting from the cursor position change characters to the specified column. */ static KT_KEY_FN(gl_vi_change_to_column) { if (--count >= gl->buff_curpos) return gl_vi_forward_change_char(gl, count - gl->buff_curpos); else return gl_vi_backward_change_char(gl, gl->buff_curpos - count); } /*....................................................................... * Starting from the cursor position change characters to a matching * parenthesis. */ static KT_KEY_FN(gl_vi_change_to_parenthesis) { int curpos = gl_index_of_matching_paren(gl); if(curpos >= 0) { gl_save_for_undo(gl); if(curpos >= gl->buff_curpos) return gl_vi_forward_change_char(gl, curpos - gl->buff_curpos + 1); else return gl_vi_backward_change_char(gl, ++gl->buff_curpos - curpos + 1); }; return 0; } /*....................................................................... * If in vi mode, switch to vi command mode. * * Input: * gl GetLine * The getline resource object. */ static void gl_vi_command_mode(GetLine *gl) { if(gl->editor == GL_VI_MODE && !gl->vi.command) { gl->insert = 1; gl->vi.command = 1; gl->vi.repeat.input_curpos = gl->insert_curpos; gl->vi.repeat.command_curpos = gl->buff_curpos; gl->insert_curpos = 0; /* unrestrict left motion boundary */ gl_cursor_left(gl, 1); /* Vi moves left one on entering command mode */ }; } /*....................................................................... * This is an action function which rings the terminal bell. */ static KT_KEY_FN(gl_ring_bell) { return gl->silence_bell ? 0 : gl_output_control_sequence(gl, 1, gl->sound_bell); } /*....................................................................... * This is the action function which implements the vi-repeat-change * action. */ static KT_KEY_FN(gl_vi_repeat_change) { int status; /* The return status of the repeated action function */ int i; /* * Nothing to repeat? */ if(!gl->vi.repeat.fn) return gl_ring_bell(gl, 1); /* * Provide a way for action functions to know whether they are being * called by us. */ gl->vi.repeat.active = 1; /* * Re-run the recorded function. */ status = gl->vi.repeat.fn(gl, gl->vi.repeat.count); /* * Mark the repeat as completed. */ gl->vi.repeat.active = 0; /* * Is we are repeating a function that has just switched to input * mode to allow the user to type, re-enter the text that the user * previously entered. */ if(status==0 && !gl->vi.command) { /* * Make sure that the current line has been saved. */ gl_save_for_undo(gl); /* * Repeat a previous insertion or overwrite? */ if(gl->vi.repeat.input_curpos >= 0 && gl->vi.repeat.input_curpos <= gl->vi.repeat.command_curpos && gl->vi.repeat.command_curpos <= gl->vi.undo.ntotal) { /* * Using the current line which is saved in the undo buffer, plus * the range of characters therein, as recorded by gl_vi_command_mode(), * add the characters that the user previously entered, to the input * line. */ for(i=gl->vi.repeat.input_curpos; ivi.repeat.command_curpos; i++) { if(gl_add_char_to_line(gl, gl->vi.undo.line[i])) return 1; }; }; /* * Switch back to command mode, now that the insertion has been repeated. */ gl_vi_command_mode(gl); }; return status; } /*....................................................................... * If the cursor is currently over a parenthesis character, return the * index of its matching parenthesis. If not currently over a parenthesis * character, return the next close parenthesis character to the right of * the cursor. If the respective parenthesis character isn't found, * ring the terminal bell and return -1. * * Input: * gl GetLine * The getline resource object. * Output: * return int Either the index of the matching parenthesis, * or -1 if not found. */ static int gl_index_of_matching_paren(GetLine *gl) { int i; /* * List the recognized parentheses, and their matches. */ const char *o_paren = "([{"; const char *c_paren = ")]}"; const char *cptr; /* * Get the character that is currently under the cursor. */ char c = gl->line[gl->buff_curpos]; /* * If the character under the cursor is an open parenthesis, look forward * for the matching close parenthesis. */ if((cptr=strchr(o_paren, c))) { char match = c_paren[cptr - o_paren]; int matches_needed = 1; for(i=gl->buff_curpos+1; intotal; i++) { if(gl->line[i] == c) matches_needed++; else if(gl->line[i] == match && --matches_needed==0) return i; }; /* * If the character under the cursor is an close parenthesis, look forward * for the matching open parenthesis. */ } else if((cptr=strchr(c_paren, c))) { char match = o_paren[cptr - c_paren]; int matches_needed = 1; for(i=gl->buff_curpos-1; i>=0; i--) { if(gl->line[i] == c) matches_needed++; else if(gl->line[i] == match && --matches_needed==0) return i; }; /* * If not currently over a parenthesis character, search forwards for * the first close parenthesis (this is what the vi % binding does). */ } else { for(i=gl->buff_curpos+1; intotal; i++) if(strchr(c_paren, gl->line[i]) != NULL) return i; }; /* * Not found. */ (void) gl_ring_bell(gl, 1); return -1; } /*....................................................................... * If the cursor is currently over a parenthesis character, this action * function moves the cursor to its matching parenthesis. */ static KT_KEY_FN(gl_find_parenthesis) { int curpos = gl_index_of_matching_paren(gl); if(curpos >= 0) return gl_place_cursor(gl, curpos); return 0; } /*....................................................................... * Handle the receipt of the potential start of a new key-sequence from * the user. * * Input: * gl GetLine * The resource object of this library. * first_char char The first character of the sequence. * Output: * return int 0 - OK. * 1 - Error. */ static int gl_interpret_char(GetLine *gl, char first_char) { KtKeyFn *keyfn; /* An action function */ int count; /* The repeat count of an action function */ int ret = 0; /* The return value of an action function */ int i; /* * Get the first character. */ char c = first_char; /* * If editting is disabled, just add newly entered characters to the * input line buffer, and watch for the end of the line. */ if(gl->editor == GL_NO_EDITOR) { if(gl->ntotal >= gl->linelen) { ret = 0; goto ret_label; } if(c == '\n' || c == '\r') { ret = gl_newline(gl, 1); if (ret) ret = 1; goto ret_label; } gl->line[gl->ntotal++] = c; ret = 0; goto ret_label; }; /* * If the user is in the process of specifying a repeat count and the * new character is a digit, increment the repeat count accordingly. */ if(gl->number >= 0 && isdigit((int)(unsigned char) c)) { ret = gl_digit_argument(gl, c); if (ret) ret = 1; goto ret_label; } /* * In vi command mode, all key-sequences entered need to be * either implicitly or explicitly prefixed with an escape character. */ else if(gl->vi.command && c != GL_ESC_CHAR) { if (gl->nkey == 0) gl->keyseq[gl->nkey++] = GL_ESC_CHAR; } /* * If the first character of the sequence is a printable character, * then to avoid confusion with the special "up", "down", "left" * or "right" cursor key bindings, we need to prefix the * printable character with a backslash escape before looking it up. */ else if(!IS_META_CHAR(c) && !IS_CTRL_CHAR(c)) { if (gl->nkey == 0) gl->keyseq[gl->nkey++] = '\\'; } /* * Compose a potentially multiple key-sequence in gl->keyseq. */ while(gl->nkey < GL_KEY_MAX) { int first, last; /* The matching entries in gl->keys */ /* * If the character is an unprintable meta character, split it * into two characters, an escape character and the character * that was modified by the meta key. */ if(IS_META_CHAR(c)) { if (gl->nkey == 0) gl->keyseq[gl->nkey++] = GL_ESC_CHAR; c = META_TO_CHAR(c); continue; }; /* * Append the latest character to the key sequence. */ gl->keyseq[gl->nkey++] = c; /* * When doing vi-style editing, an escape at the beginning of any binding * switches to command mode. */ if(gl->keyseq[0] == GL_ESC_CHAR && !gl->vi.command) gl_vi_command_mode(gl); /* * Lookup the key sequence. */ switch(_kt_lookup_keybinding(gl->bindings, gl->keyseq, gl->nkey, &first, &last)) { case KT_EXACT_MATCH: /* * Get the matching action function. */ keyfn = gl->bindings->table[first].keyfn; /* * Get the repeat count, passing the last keystroke if executing the * digit-argument action. */ if(keyfn == gl_digit_argument) { count = c; } else { count = gl->number >= 0 ? gl->number : 1; }; /* * Record the function that is being invoked. */ gl->current_fn = keyfn; gl->current_count = count; /* * Mark the current line as not yet preserved for use by the vi undo command. */ gl->vi.undo.saved = 0; gl->vi.repeat.saved = 0; /* * Execute the action function. Note the action function can tell * whether the provided repeat count was defaulted or specified * explicitly by looking at whether gl->number is -1 or not. If * it is negative, then no repeat count was specified by the user. */ ret = keyfn(gl, count); /* * Reset the repeat count after running action functions (other * than digit-argument). */ if(keyfn != gl_digit_argument) gl->number = -1; if(ret) ret = 1; goto ret_label; break; case KT_AMBIG_MATCH: /* Ambiguous match - so look ahead */ if(gl_read_character(gl, &c, -1)) {/* Get the next character */ ret = 1; goto ret_label; } break; case KT_NO_MATCH: /* * If the first character looked like it might be a prefix of a key-sequence * but it turned out not to be, ring the bell to tell the user that it * wasn't recognised. */ if(gl->keyseq[0] != '\\') { gl_ring_bell(gl, 0); } else { /* * The user typed a single printable character that doesn't match * the start of any keysequence, so add it to the line in accordance * with the current repeat count. */ count = gl->number >= 0 ? gl->number : 1; for(i=0; inumber = -1; }; ret = 0; goto ret_label; break; case KT_BAD_MATCH: ret = 1; goto ret_label; break; }; }; /* * Key sequence too long to match. */ ret = 0; ret_label: if (ret && (gl->net_may_block || (gl->net_read_attempt > 1))) { return 0; /* Input incomplete */ } else { gl->nkey = 0; } return ret; } /*....................................................................... * Configure the application and/or user-specific behavior of * gl_get_line(). * * Note that calling this function between calling new_GetLine() and * the first call to new_GetLine(), disables the otherwise automatic * reading of ~/.teclarc on the first call to gl_get_line(). * * Input: * gl GetLine * The resource object of this library. * app_string const char * Either NULL, or a string containing one * or more .teclarc command lines, separated * by newline characters. This can be used to * establish an application-specific * configuration, without the need for an external * file. This is particularly useful in embedded * environments where there is no filesystem. * app_file const char * Either NULL, or the pathname of an * application-specific .teclarc file. The * contents of this file, if provided, are * read after the contents of app_string[]. * user_file const char * Either NULL, or the pathname of a * user-specific .teclarc file. Except in * embedded applications, this should * usually be "~/.teclarc". * Output: * return int 0 - OK. * 1 - Bad argument(s). */ int gl_configure_getline(GetLine *gl, const char *app_string, const char *app_file, const char *user_file) { /* * Check the arguments. */ if(!gl) { fprintf(stderr, "gl_configure_getline: NULL gl argument.\n"); return 1; }; /* * Mark getline as having been explicitly configured. */ gl->configured = 1; /* * Start by parsing the configuration string, if provided. */ if(app_string) (void) _gl_read_config_string(gl, app_string, KTB_NORM); /* * Now parse the application-specific configuration file, if provided. */ if(app_file) (void) _gl_read_config_file(gl, app_file, KTB_NORM); /* * Finally, parse the user-specific configuration file, if provided. */ if(user_file) (void) _gl_read_config_file(gl, user_file, KTB_USER); /* * Record the names of the configuration files to allow them to * be re-read if requested at a later time. */ if(gl_record_string(&gl->app_file, app_file) || gl_record_string(&gl->user_file, user_file)) { fprintf(stderr, "Insufficient memory to record tecla configuration file names.\n"); return 1; }; return 0; } /*....................................................................... * Replace a malloc'd string (or NULL), with another malloc'd copy of * a string (or NULL). * * Input: * sptr char ** On input if *sptr!=NULL, *sptr will be * free'd and *sptr will be set to NULL. Then, * on output, if string!=NULL a malloc'd copy * of this string will be assigned to *sptr. * string const char * The string to be copied, or NULL to simply * discard any existing string. * Output: * return int 0 - OK. * 1 - Malloc failure (no error message is generated). */ static int gl_record_string(char **sptr, const char *string) { /* * If the original string is the same string, don't do anything. */ if(*sptr == string || (*sptr && string && strcmp(*sptr, string)==0)) return 0; /* * Discard any existing cached string. */ if(*sptr) { free(*sptr); *sptr = NULL; }; /* * Allocate memory for a copy of the specified string. */ if(string) { *sptr = (char *) malloc(strlen(string) + 1); if(!*sptr) return 1; /* * Copy the string. */ strncpy(*sptr, string, strlen(string) + 1); }; return 0; } /*....................................................................... * Re-read any application-specific and user-specific files previously * specified via the gl_configure_getline() function. */ static KT_KEY_FN(gl_read_init_files) { return gl_configure_getline(gl, NULL, gl->app_file, gl->user_file); } /*....................................................................... * Save the contents of the history buffer to a given new file. * * Input: * gl GetLine * The resource object of this library. * filename const char * The name of the new file to write to. * comment const char * Extra information such as timestamps will * be recorded on a line started with this * string, the idea being that the file can * double as a command file. Specify "" if * you don't care. * max_lines int The maximum number of lines to save, or -1 * to save all of the lines in the history * list. * Output: * return int 0 - OK. * 1 - Error. */ int gl_save_history(GetLine *gl, const char *filename, const char *comment, int max_lines) { FileExpansion *expansion; /* The expansion of the filename */ /* * Check the arguments. */ if(!gl || !filename || !comment) { fprintf(stderr, "gl_save_history: NULL argument(s).\n"); return 1; }; /* * Expand the filename. */ expansion = ef_expand_file(gl->ef, filename, -1); if(!expansion) { fprintf(stderr, "Unable to expand %s (%s).\n", filename, ef_last_error(gl->ef)); return 1; }; /* * Attempt to save to the specified file. */ return _glh_save_history(gl->glh, expansion->files[0], comment, max_lines); } /*....................................................................... * Restore the contents of the history buffer from a given new file. * * Input: * gl GetLine * The resource object of this library. * filename const char * The name of the new file to write to. * comment const char * This must be the same string that was * passed to gl_save_history() when the file * was written. * Output: * return int 0 - OK. * 1 - Error. */ int gl_load_history(GetLine *gl, const char *filename, const char *comment) { FileExpansion *expansion; /* The expansion of the filename */ /* * Check the arguments. */ if(!gl || !filename || !comment) { fprintf(stderr, "gl_load_history: NULL argument(s).\n"); return 1; }; /* * Expand the filename. */ expansion = ef_expand_file(gl->ef, filename, -1); if(!expansion) { fprintf(stderr, "Unable to expand %s (%s).\n", filename, ef_last_error(gl->ef)); return 1; }; /* * Attempt to load from the specified file. */ if(_glh_load_history(gl->glh, expansion->files[0], comment, gl->cutbuf, gl->linelen)) { gl->cutbuf[0] = '\0'; return 1; }; gl->cutbuf[0] = '\0'; return 0; } /*....................................................................... * Where possible, register a function and associated data to be called * whenever a specified event is seen on a file descriptor. * * Input: * gl GetLine * The resource object of the command-line input * module. * fd int The file descriptor to watch. * event GlFdEvent The type of activity to watch for. * callback GlFdEventFn * The function to call when the specified * event occurs. Setting this to 0 removes * any existing callback. * data void * A pointer to arbitrary data to pass to the * callback function. * Output: * return int 0 - OK. * 1 - Either gl==NULL, or this facility isn't * available on the the host system * (ie. select() isn't available). No * error message is generated in the latter * case. */ int gl_watch_fd(GetLine *gl, int fd, GlFdEvent event, GlFdEventFn *callback, void *data) #if !defined(HAVE_SELECT) {return 1;} /* The facility isn't supported on this system */ #else { GlFdNode *prev; /* The node that precedes 'node' in gl->fd_nodes */ GlFdNode *node; /* The file-descriptor node being checked */ /* * Check the arguments. */ if(!gl) { fprintf(stderr, "gl_watch_fd: NULL gl argument.\n"); return 1; }; if(fd < 0) { fprintf(stderr, "gl_watch_fd: Error fd < 0.\n"); return 1; }; /* * Search the list of already registered fd activity nodes for the specified * file descriptor. */ for(prev=NULL,node=gl->fd_nodes; node && node->fd != fd; prev=node, node=node->next) ; /* * Hasn't a node been allocated for this fd yet? */ if(!node) { /* * If there is no callback to record, just ignore the call. */ if(!callback) return 0; /* * Allocate the new node. */ node = (GlFdNode *) _new_FreeListNode(gl->fd_node_mem); if(!node) { fprintf(stderr, "gl_watch_fd: Insufficient memory.\n"); return 1; }; /* * Prepend the node to the list. */ node->next = gl->fd_nodes; gl->fd_nodes = node; /* * Initialize the node. */ node->fd = fd; node->rd.fn = 0; node->rd.data = NULL; node->ur = node->wr = node->rd; }; /* * Record the new callback. */ switch(event) { case GLFD_READ: node->rd.fn = callback; node->rd.data = data; if(callback) FD_SET(fd, &gl->rfds); else FD_CLR(fd, &gl->rfds); break; case GLFD_WRITE: node->wr.fn = callback; node->wr.data = data; if(callback) FD_SET(fd, &gl->wfds); else FD_CLR(fd, &gl->wfds); break; case GLFD_URGENT: node->ur.fn = callback; node->ur.data = data; if(callback) FD_SET(fd, &gl->ufds); else FD_CLR(fd, &gl->ufds); break; }; /* * Keep a record of the largest file descriptor being watched. */ if(fd > gl->max_fd) gl->max_fd = fd; /* * If we are deleting an existing callback, also delete the parent * activity node if no callbacks are registered to the fd anymore. */ if(!callback) { if(!node->rd.fn && !node->wr.fn && !node->ur.fn) { if(prev) prev->next = node->next; else gl->fd_nodes = node->next; node = (GlFdNode *) _del_FreeListNode(gl->fd_node_mem, node); }; }; return 0; } /*....................................................................... * When select() is available, this function is called by * gl_read_character() to respond to file-descriptor events registered by * the caller. * * Input: * gl GetLine * The resource object of this module. * Output: * return int 0 - A character is waiting to be read from the * terminal. * 1 - An error occurred. */ static int gl_event_handler(GetLine *gl) { /* * If at any time no external callbacks remain, quit the loop return, * so that we can simply wait in read(). This is designed as an * optimization for when no callbacks have been registered on entry to * this function, but since callbacks can delete themselves, it can * also help later. */ while(gl->fd_nodes) { /* * Get the set of descriptors to be watched. */ fd_set rfds = gl->rfds; fd_set wfds = gl->wfds; fd_set ufds = gl->ufds; /* * Wait for activity on any of the file descriptors. */ int nready = select(gl->max_fd+1, &rfds, &wfds, &ufds, NULL); /* * If select() returns but none of the file descriptors are reported * to have activity, then select() timed out. */ if(nready == 0) { fprintf(stdout, "\r\nUnexpected select() timeout\r\n"); return 1; /* * If nready < 0, this means an error occurred. */ } else if(nready < 0) { if(errno != EINTR) { #ifdef EAGAIN if(!errno) /* This can happen with SysV O_NDELAY */ errno = EAGAIN; #endif return 1; }; /* * If the terminal input file descriptor has data available, return. */ } else if(FD_ISSET(gl->input_fd, &rfds)) { return 0; /* * Check for activity on any of the file descriptors registered by the * calling application, and call the associated callback functions. */ } else { GlFdNode *node; /* The fd event node being checked */ /* * Search the list for the file descriptor that caused select() to return. */ for(node=gl->fd_nodes; node; node=node->next) { /* * Is there urgent out of band data waiting to be read on fd? */ if(node->ur.fn && FD_ISSET(node->fd, &ufds)) { if(gl_call_fd_handler(gl, &node->ur, node->fd, GLFD_URGENT)) return 1; break; /* The callback may have changed the list of nodes */ /* * Is the fd readable? */ } else if(node->rd.fn && FD_ISSET(node->fd, &rfds)) { if(gl_call_fd_handler(gl, &node->rd, node->fd, GLFD_READ)) return 1; break; /* The callback may have changed the list of nodes */ /* * Is the fd writable? */ } else if(node->wr.fn && FD_ISSET(node->fd, &rfds)) { if(gl_call_fd_handler(gl, &node->wr, node->fd, GLFD_WRITE)) return 1; break; /* The callback may have changed the list of nodes */ }; }; }; }; return 0; } /*....................................................................... * This is a private function of gl_event_handler(), used to call a * file-descriptor callback. * * Input: * gl GetLine * The resource object of gl_get_line(). * gfh GlFdHandler * The I/O handler. * fd int The file-descriptor being reported. * event GlFdEvent The I/O event being reported. * Output: * return int 0 - OK. * 1 - Error. */ static int gl_call_fd_handler(GetLine *gl, GlFdHandler *gfh, int fd, GlFdEvent event) { Termios attr; /* The terminal attributes */ int redisplay = 0; /* True to have the input line redisplayed */ int waserr = 0; /* True after any error */ /* * We don't want to do a longjmp in the middle of a callback that * might be modifying global or heap data, so block all the signals * that we are trapping. */ if(sigprocmask(SIG_BLOCK, &gl->new_signal_set, NULL) == -1) { fprintf(stderr, "getline(): sigprocmask error: %s\n", strerror(errno)); return 1; }; /* * Re-enable conversion of newline characters to carriage-return/linefeed, * so that the callback can write to the terminal without having to do * anything special. */ if(tcgetattr(gl->input_fd, &attr)) { fprintf(stderr, "\r\ngetline(): tcgetattr error: %s\r\n", strerror(errno)); return 1; }; attr.c_oflag |= OPOST; while(tcsetattr(gl->input_fd, TCSADRAIN, &attr)) { if (errno != EINTR) { fprintf(stderr, "\r\ngetline(): tcsetattr error: %s\r\n", strerror(errno)); return 1; }; }; /* * Invoke the application's callback function. */ switch(gfh->fn(gl, gfh->data, fd, event)) { default: case GLFD_ABORT: waserr = 1; break; case GLFD_REFRESH: redisplay = 1; break; case GLFD_CONTINUE: redisplay = gl->prompt_changed; break; }; /* * Disable conversion of newline characters to carriage-return/linefeed. */ attr.c_oflag &= ~(OPOST); while(tcsetattr(gl->input_fd, TCSADRAIN, &attr)) { if(errno != EINTR) { fprintf(stderr, "\ngetline(): tcsetattr error: %s\n", strerror(errno)); return 1; }; }; /* * If requested, redisplay the input line. */ if(redisplay && gl_redisplay(gl, 1)) return 1; /* * Unblock the signals that we were trapping before this function * was called. */ if(sigprocmask(SIG_UNBLOCK, &gl->new_signal_set, NULL) == -1) { fprintf(stderr, "getline(): sigprocmask error: %s\n", strerror(errno)); return 1; }; return waserr; } #endif /* HAVE_SELECT */ /*....................................................................... * Switch history groups. History groups represent separate history * lists recorded within a single history buffer. Different groups * are distinguished by integer identifiers chosen by the calling * appplicaton. Initially new_GetLine() sets the group identifier to * 0. Whenever a new line is appended to the history list, the current * group identifier is recorded with it, and history lookups only * consider lines marked with the current group identifier. * * Input: * gl GetLine * The resource object of gl_get_line(). * id unsigned The new history group identifier. * Output: * return int 0 - OK. * 1 - Error. */ int gl_group_history(GetLine *gl, unsigned id) { /* * Check the arguments. */ if(!gl) { fprintf(stderr, "gl_group_history: NULL argument(s).\n"); return 1; }; /* * If the group isn't being changed, do nothing. */ if(_glh_get_group(gl->glh) == id) return 0; /* * Establish the new group. */ if(_glh_set_group(gl->glh, id)) return 1; /* * Prevent history information from the previous group being * inappropriately used by the next call to gl_get_line(). */ gl->preload_history = 0; gl->last_search = -1; return 0; } /*....................................................................... * Display the contents of the history list. * * Input: * gl GetLine * The resource object of gl_get_line(). * fp FILE * The stdio output stream to write to. * fmt const char * A format string. This containing characters to be * written verbatim, plus any of the following * format directives: * %D - The date, formatted like 2001-11-20 * %T - The time of day, formatted like 23:59:59 * %N - The sequential entry number of the * line in the history buffer. * %G - The number of the history group that * the line belongs to. * %% - A literal % character. * %H - The history line itself. * Note that a '\n' newline character is not * appended by default. * all_groups int If true, display history lines from all * history groups. Otherwise only display * those of the current history group. * max_lines int If max_lines is < 0, all available lines * are displayed. Otherwise only the most * recent max_lines lines will be displayed. * Output: * return int 0 - OK. * 1 - Error. */ int gl_show_history(GetLine *gl, FILE *fp, const char *fmt, int all_groups, int max_lines) { /* * Check the arguments. */ if(!gl || !fp || !fmt) { fprintf(stderr, "gl_show_history: NULL argument(s).\n"); return 1; }; return _glh_show_history(gl->glh, fp, fmt, all_groups, max_lines); } /*....................................................................... * Update if necessary, and return the current size of the terminal. * * Input: * gl GetLine * The resource object of gl_get_line(). * def_ncolumn int If the number of columns in the terminal * can't be determined, substitute this number. * def_nline int If the number of lines in the terminal can't * be determined, substitute this number. * Output: * return GlTerminalSize The current terminal size. */ GlTerminalSize gl_terminal_size(GetLine *gl, int def_ncolumn, int def_nline) { GlTerminalSize size; /* The return value */ const char *env; /* The value of an environment variable */ int n; /* A number read from env[] */ /* * Set the number of lines and columns to non-sensical values so that * we know later if they have been set. */ gl->nline = 0; gl->ncolumn = 0; /* * Are we reading from a terminal? */ if(gl->is_term) { /* * Ask the terminal directly if possible. */ #ifdef USE_SIGWINCH (void) gl_resize_terminal(gl, 0); #endif /* * If gl_resize_terminal() couldn't be used, or it returned non-sensical * values for the number of lines, see if the LINES environment variable * exists and specifies a believable number. If this doesn't work, * look up the default size in the terminal information database, * where available. */ if(gl->nline < 1) { if((env = getenv("LINES")) && (n=atoi(env)) > 0) gl->nline = n; #ifdef USE_TERMINFO else gl->nline = tigetnum((char *)"lines"); #elif defined(USE_TERMCAP) else gl->nline = tgetnum("li"); #endif }; /* * If gl_resize_terminal() couldn't be used, or it returned non-sensical * values for the number of columns, see if the COLUMNS environment variable * exists and specifies a believable number. If this doesn't work, fall * lookup the default size in the terminal information database, * where available. */ if(gl->ncolumn < 1) { if((env = getenv("COLUMNS")) && (n=atoi(env)) > 0) gl->ncolumn = n; #ifdef USE_TERMINFO else gl->ncolumn = tigetnum((char *)"cols"); #elif defined(USE_TERMCAP) else gl->ncolumn = tgetnum("co"); #endif }; }; /* * If we still haven't been able to acquire reasonable values, substitute * the default values specified by the caller. */ if(gl->nline <= 0) gl->nline = def_nline; if(gl->ncolumn <= 0) gl->ncolumn = def_ncolumn; /* * Copy the new size into the return value. */ size.nline = gl->nline; size.ncolumn = gl->ncolumn; return size; } /*....................................................................... * Resize or delete the history buffer. * * Input: * gl GetLine * The resource object of gl_get_line(). * bufsize size_t The number of bytes in the history buffer, or 0 * to delete the buffer completely. * Output: * return int 0 - OK. * 1 - Insufficient memory (the previous buffer * will have been retained). No error message * will be displayed. */ int gl_resize_history(GetLine *gl, size_t bufsize) { return gl ? _glh_resize_history(gl->glh, bufsize) : 1; } /*....................................................................... * Set an upper limit to the number of lines that can be recorded in the * history list, or remove a previously specified limit. * * Input: * gl GetLine * The resource object of gl_get_line(). * max_lines int The maximum number of lines to allow, or -1 to * cancel a previous limit and allow as many lines * as will fit in the current history buffer size. */ void gl_limit_history(GetLine *gl, int max_lines) { if(gl) _glh_limit_history(gl->glh, max_lines); } /*....................................................................... * Discard either all historical lines, or just those associated with the * current history group. * * Input: * gl GetLine * The resource object of gl_get_line(). * all_groups int If true, clear all of the history. If false, * clear only the stored lines associated with the * currently selected history group. */ void gl_clear_history(GetLine *gl, int all_groups) { if(gl) _glh_clear_history(gl->glh, all_groups); } /*....................................................................... * Temporarily enable or disable the gl_get_line() history mechanism. * * Input: * gl GetLine * The resource object of gl_get_line(). * enable int If true, turn on the history mechanism. If * false, disable it. */ void gl_toggle_history(GetLine *gl, int enable) { if(gl) _glh_toggle_history(gl->glh, enable); } /*....................................................................... * Lookup a history line by its sequential number of entry in the * history buffer. * * Input: * gl GetLine * The resource object of gl_get_line(). * id unsigned long The identification number of the line to * be returned, where 0 denotes the first line * that was entered in the history list, and * each subsequently added line has a number * one greater than the previous one. For * the range of lines currently in the list, * see the gl_range_of_history() function. * Input/Output: * line GlHistoryLine * A pointer to the variable in which to * return the details of the line. * Output: * return int 0 - The line is no longer in the history * list, and *line has not been changed. * 1 - The requested line can be found in * *line. Note that line->line is part * of the history buffer, so a * private copy should be made if you * wish to use it after subsequent calls * to any functions that take *gl as an * argument. */ int gl_lookup_history(GetLine *gl, unsigned long id, GlHistoryLine *line) { return gl ? _glh_lookup_history(gl->glh, (GlhLineID) id, &line->line, &line->group, &line->timestamp) : 0; } /*....................................................................... * Query the state of the history list. Note that any of the input/output * pointers can be specified as NULL. * * Input: * gl GetLine * The resource object of gl_get_line(). * Input/Output: * state GlHistoryState * A pointer to the variable in which to record * the return values. */ void gl_state_of_history(GetLine *gl, GlHistoryState *state) { if(gl && state) _glh_state_of_history(gl->glh, &state->enabled, &state->group, &state->max_lines); } /*....................................................................... * Query the number and range of lines in the history buffer. * * Input: * gl GetLine * The resource object of gl_get_line(). * range GlHistoryRange * A pointer to the variable in which to record * the return values. If range->nline=0, the * range of lines will be given as 0-0. */ void gl_range_of_history(GetLine *gl, GlHistoryRange *range) { if(gl && range) _glh_range_of_history(gl->glh, &range->oldest, &range->newest, &range->nlines); } /*....................................................................... * Return the size of the history buffer and the amount of the * buffer that is currently in use. * * Input: * gl GetLine * The gl_get_line() resource object. * Input/Output: * GlHistorySize size * A pointer to the variable in which to return * the results. */ void gl_size_of_history(GetLine *gl, GlHistorySize *size) { if(gl && size) _glh_size_of_history(gl->glh, &size->size, &size->used); } /*....................................................................... * This is the action function that lists the contents of the history * list. */ static KT_KEY_FN(gl_list_history) { /* * Start a new line. */ if(fprintf(gl->output_fp, "\r\n") < 0) return 1; /* * List history lines that belong to the current group. */ _glh_show_history(gl->glh, gl->output_fp, "%N %T %H\r\n", 0, count<=1 ? -1 : count); /* * Redisplay the line being edited. */ gl->term_curpos = 0; return gl_redisplay(gl,1); } /*....................................................................... * Specify whether text that users type should be displayed or hidden. * In the latter case, only the prompt is displayed, and the final * input line is not archived in the history list. * * Input: * gl GetLine * The gl_get_line() resource object. * enable int 0 - Disable echoing. * 1 - Enable echoing. * -1 - Just query the mode without changing it. * Output: * return int The echoing disposition that was in effect * before this function was called: * 0 - Echoing was disabled. * 1 - Echoing was enabled. */ int gl_echo_mode(GetLine *gl, int enable) { if(gl) { int was_echoing = gl->echo; if(enable >= 0) gl->echo = enable; return was_echoing; }; return 1; } /*....................................................................... * Display the prompt. * * Input: * gl GetLine * The resource object of gl_get_line(). * Output: * return int 0 - OK. * 1 - Error. */ static int gl_display_prompt(GetLine *gl) { const char *pptr; /* A pointer into gl->prompt[] */ unsigned old_attr=0; /* The current text display attributes */ unsigned new_attr=0; /* The requested text display attributes */ /* * Temporarily switch to echoing output characters. */ int kept_echo = gl->echo; gl->echo = 1; /* * In case the screen got messed up, send a carriage return to * put the cursor at the beginning of the current terminal line. */ if(gl_output_control_sequence(gl, 1, gl->bol)) return 1; /* * Write the prompt, using the currently selected prompt style. */ switch(gl->prompt_style) { case GL_LITERAL_PROMPT: if(gl_output_string(gl, gl->prompt, '\0')) return 1; break; case GL_FORMAT_PROMPT: for(pptr=gl->prompt; *pptr; pptr++) { /* * Does the latest character appear to be the start of a directive? */ if(*pptr == '%') { /* * Check for and act on attribute changing directives. */ switch(pptr[1]) { /* * Add or remove a text attribute from the new set of attributes. */ case 'B': case 'U': case 'S': case 'P': case 'F': case 'V': case 'b': case 'u': case 's': case 'p': case 'f': case 'v': switch(*++pptr) { case 'B': /* Switch to a bold font */ new_attr |= GL_TXT_BOLD; break; case 'b': /* Switch to a non-bold font */ new_attr &= ~GL_TXT_BOLD; break; case 'U': /* Start underlining */ new_attr |= GL_TXT_UNDERLINE; break; case 'u': /* Stop underlining */ new_attr &= ~GL_TXT_UNDERLINE; break; case 'S': /* Start highlighting */ new_attr |= GL_TXT_STANDOUT; break; case 's': /* Stop highlighting */ new_attr &= ~GL_TXT_STANDOUT; break; case 'P': /* Switch to a pale font */ new_attr |= GL_TXT_DIM; break; case 'p': /* Switch to a non-pale font */ new_attr &= ~GL_TXT_DIM; break; case 'F': /* Switch to a flashing font */ new_attr |= GL_TXT_BLINK; break; case 'f': /* Switch to a steady font */ new_attr &= ~GL_TXT_BLINK; break; case 'V': /* Switch to reverse video */ new_attr |= GL_TXT_REVERSE; break; case 'v': /* Switch out of reverse video */ new_attr &= ~GL_TXT_REVERSE; break; }; continue; /* * A literal % is represented by %%. Skip the leading %. */ case '%': pptr++; break; }; }; /* * Many terminals, when asked to turn off a single text attribute, turn * them all off, so the portable way to turn one off individually is to * explicitly turn them all off, then specify those that we want from * scratch. */ if(old_attr & ~new_attr) { if(gl_output_control_sequence(gl, 1, gl->text_attr_off)) return 1; old_attr = 0; }; /* * Install new text attributes? */ if(new_attr != old_attr) { if(new_attr & GL_TXT_BOLD && !(old_attr & GL_TXT_BOLD) && gl_output_control_sequence(gl, 1, gl->bold)) return 1; if(new_attr & GL_TXT_UNDERLINE && !(old_attr & GL_TXT_UNDERLINE) && gl_output_control_sequence(gl, 1, gl->underline)) return 1; if(new_attr & GL_TXT_STANDOUT && !(old_attr & GL_TXT_STANDOUT) && gl_output_control_sequence(gl, 1, gl->standout)) return 1; if(new_attr & GL_TXT_DIM && !(old_attr & GL_TXT_DIM) && gl_output_control_sequence(gl, 1, gl->dim)) return 1; if(new_attr & GL_TXT_REVERSE && !(old_attr & GL_TXT_REVERSE) && gl_output_control_sequence(gl, 1, gl->reverse)) return 1; if(new_attr & GL_TXT_BLINK && !(old_attr & GL_TXT_BLINK) && gl_output_control_sequence(gl, 1, gl->blink)) return 1; old_attr = new_attr; }; /* * Display the latest character. */ if(gl_output_char(gl, *pptr, pptr[1])) return 1; }; /* * Turn off all text attributes now that we have finished drawing * the prompt. */ if(gl_output_control_sequence(gl, 1, gl->text_attr_off)) return 1; break; }; /* * Restore the original echo mode. */ gl->echo = kept_echo; /* * The prompt has now been displayed at least once. */ gl->prompt_changed = 0; return 0; } /*....................................................................... * This function can be called from gl_get_line() callbacks to have * the prompt changed when they return. It has no effect if gl_get_line() * is not currently being invoked. * * Input: * gl GetLine * The resource object of gl_get_line(). * prompt const char * The new prompt. */ void gl_replace_prompt(GetLine *gl, const char *prompt) { if(gl) { gl->prompt = prompt ? prompt : ""; gl->prompt_len = gl_displayed_prompt_width(gl); gl->prompt_changed = 1; }; } /*....................................................................... * Work out the length of the current prompt on the terminal, according * to the current prompt formatting style. * * Input: * gl GetLine * The resource object of this library. * Output: * return int The number of displayed characters. */ static int gl_displayed_prompt_width(GetLine *gl) { int slen=0; /* The displayed number of characters */ const char *pptr; /* A pointer into prompt[] */ /* * The length differs according to the prompt display style. */ switch(gl->prompt_style) { case GL_LITERAL_PROMPT: return gl_displayed_string_width(gl, gl->prompt, -1, 0); break; case GL_FORMAT_PROMPT: /* * Add up the length of the displayed string, while filtering out * attribute directives. */ for(pptr=gl->prompt; *pptr; pptr++) { /* * Does the latest character appear to be the start of a directive? */ if(*pptr == '%') { /* * Check for and skip attribute changing directives. */ switch(pptr[1]) { case 'B': case 'b': case 'U': case 'u': case 'S': case 's': pptr++; continue; /* * A literal % is represented by %%. Skip the leading %. */ case '%': pptr++; break; }; }; slen += gl_displayed_char_width(gl, *pptr, slen); }; break; }; return slen; } /*....................................................................... * Specify whether to heed text attribute directives within prompt * strings. * * Input: * gl GetLine * The resource object of gl_get_line(). * style GlPromptStyle The style of prompt (see the definition of * GlPromptStyle in libtecla.h for details). */ void gl_prompt_style(GetLine *gl, GlPromptStyle style) { if(gl) { if(style != gl->prompt_style) { gl->prompt_style = style; gl->prompt_len = gl_displayed_prompt_width(gl); gl->prompt_changed = 1; }; }; } /*....................................................................... * Tell gl_get_line() how to respond to a given signal. This can be used * both to override the default responses to signals that gl_get_line() * normally catches and to add new signals to the list that are to be * caught. * * Input: * gl GetLine * The resource object of gl_get_line(). * signo int The number of the signal to be caught. * flags unsigned A bitwise union of GlSignalFlags enumerators. * after GlAfterSignal What to do after the application's signal * handler has been called. * errno_value int The value to set errno to. * Output: * return int 0 - OK. * 1 - Error. */ int gl_trap_signal(GetLine *gl, int signo, unsigned flags, GlAfterSignal after, int errno_value) { #ifdef __MINGW32__ return 0; #else GlSignalNode *sig; /* * Check the arguments. */ if(!gl) { fprintf(stderr, "gl_trap_signal: NULL argument(s).\n"); return 1; }; /* * See if the signal has already been registered. */ for(sig=gl->sigs; sig && sig->signo != signo; sig = sig->next) ; /* * If the signal hasn't already been registered, allocate a node for * it. */ if(!sig) { sig = (GlSignalNode *) _new_FreeListNode(gl->sig_mem); if(!sig) return 1; /* * Add the new node to the head of the list. */ sig->next = gl->sigs; gl->sigs = sig; /* * Record the signal number. */ sig->signo = signo; /* * Create a signal set that includes just this signal. */ sigemptyset(&sig->proc_mask); if(sigaddset(&sig->proc_mask, signo) == -1) { fprintf(stderr, "gl_trap_signal: sigaddset error: %s\n", strerror(errno)); sig = (GlSignalNode *) _del_FreeListNode(gl->sig_mem, sig); return 1; }; }; /* * Record the new signal attributes. */ sig->flags = flags; sig->after = after; sig->errno_value = errno_value; return 0; #endif /* __MINGW32__ */ } /*....................................................................... * Remove a signal from the list of signals that gl_get_line() traps. * * Input: * gl GetLine * The resource object of gl_get_line(). * signo int The number of the signal to be ignored. * Output: * return int 0 - OK. * 1 - Error. */ int gl_ignore_signal(GetLine *gl, int signo) { GlSignalNode *sig; /* The gl->sigs list node of the specified signal */ GlSignalNode *prev; /* The node that precedes sig in the list */ /* * Check the arguments. */ if(!gl) { fprintf(stderr, "gl_ignore_signal: NULL argument(s).\n"); return 1; }; /* * Find the node of the gl->sigs list which records the disposition * of the specified signal. */ for(prev=NULL,sig=gl->sigs; sig && sig->signo != signo; prev=sig,sig=sig->next) ; if(sig) { /* * Remove the node from the list. */ if(prev) prev->next = sig->next; else gl->sigs = sig->next; /* * Return the node to the freelist. */ sig = (GlSignalNode *) _del_FreeListNode(gl->sig_mem, sig); }; return 0; } /*....................................................................... * This function is called when an input line has been completed. It * appends the specified newline character, terminates the line, * records the line in the history buffer if appropriate, and positions * the terminal cursor at the start of the next line. * * Input: * gl GetLine * The resource object of gl_get_line(). * newline_char int The newline character to add to the end * of the line. * archive int True to have the line archived in the * history buffer. * Output: * return int 0 - OK. * 1 - Error. */ static int gl_line_ended(GetLine *gl, int newline_char, int archive) { /* * If the newline character is printable, display it. */ if(isprint((int)(unsigned char) newline_char)) { if(gl_end_of_line(gl, 1) || gl_add_char_to_line(gl, newline_char)) return 1; } else { /* * Otherwise just append it to the input line buffer. */ gl->line[gl->ntotal++] = newline_char; gl->line[gl->ntotal] = '\0'; }; /* * Add the line to the history buffer if it was entered with a * newline or carriage return character. */ if(archive) (void) _glh_add_history(gl->glh, gl->line, 0); /* * Unless depending on the system-provided line editing, start a new * line after the end of the line that has just been entered. */ if(gl->editor != GL_NO_EDITOR) { if(gl_end_of_line(gl, 1) || gl_output_raw_string(gl, "\r\n")) return 1; }; return 0; } /*....................................................................... * Return the last signal that was caught by the most recent call to * gl_get_line(), or -1 if no signals were caught. This is useful if * gl_get_line() returns errno=EINTR and you need to find out what signal * caused it to abort. * * Input: * gl GetLine * The resource object of gl_get_line(). * Output: * return int The last signal caught by the most recent * call to gl_get_line(), or -1 if no signals * were caught. */ int gl_last_signal(const GetLine *gl) { return gl ? gl->last_signal : -1; } /*....................................................................... * Read a line from the user via a network connection (e.g., telnet). * * Input: * gl GetLine * A resource object returned by new_GetLine(). * prompt char * The prompt to prefix the line with. * start_line char * The initial contents of the input line, or NULL * if it should start out empty. * start_pos int If start_line isn't NULL, this specifies the * index of the character over which the cursor * should initially be positioned within the line. * If you just want it to follow the last character * of the line, send -1. * val int If non-negative, the single-character value already * read from the network. * Output: * return char * An internal buffer containing the input line, or * NULL at the end of input. If the line fitted in * the buffer there will be a '\n' newline character * before the terminating '\0'. If it was truncated * there will be no newline character, and the remains * of the line should be retrieved via further calls * to this function. */ char *gl_get_line_net(GetLine *gl, const char *prompt, const char *start_line, int start_pos, int val) { int waserr = 0; /* True if an error occurs */ /* * Check the arguments. */ if(!gl || !prompt) { fprintf(stderr, "gl_get_line: NULL argument(s).\n"); return NULL; }; gl->is_net = 1; /* TODO: assume it is indeed a network connection */ gl->net_may_block = 0; /* reset every time we call gl_get_line_net() */ gl->net_read_attempt = 0; gl->user_event_value = 0; /* * If this is the first call to this function since new_GetLine(), * complete any postponed configuration. */ if(!gl->configured) { (void) gl_configure_getline(gl, NULL, NULL, TECLA_CONFIG_FILE); gl->configured = 1; }; /* * If input is temporarily being taken from a file, return lines * from the file until the file is exhausted, then revert to * the normal input stream. */ if(gl->file_fp) { if(fgets(gl->line, gl->linelen, gl->file_fp)) return gl->line; gl_revert_input(gl); }; /* * Is input coming from a non-interactive source? */ #if 0 /* XXX: the input is suppose to come from the network */ if(!gl->is_term) return fgets(gl->line, gl->linelen, gl->input_fp); #endif /* * Record the new prompt and its displayed width. */ gl_replace_prompt(gl, prompt); /* * Before installing our signal handler functions, record the fact * that there are no pending signals. */ gl_pending_signal = -1; /* * Temporarily override the signal handlers of the calling program, * so that we can intercept signals that would leave the terminal * in a bad state. */ waserr = gl_override_signal_handlers(gl); /* * After recording the current terminal settings, switch the terminal * into raw input mode. */ #if 0 waserr = waserr || gl_raw_terminal_mode(gl); #endif /* * Attempt to read the line. */ waserr = waserr || gl_get_input_line(gl, start_line, start_pos, val); /* * Restore terminal settings. */ #if 0 gl_restore_terminal_attributes(gl); #endif /* * Restore the signal handlers. */ gl_restore_signal_handlers(gl); /* * Having restored the program terminal and signal environment, * re-submit any signals that were received. */ if(gl_pending_signal != -1) { raise(gl_pending_signal); waserr = 1; }; /* * If gl_get_input_line() aborted input due to the user asking to * temporarily read lines from a file, read the first line from * this file. */ if(!waserr && gl->file_fp) return gl_get_line(gl, prompt, NULL, 0); /* * Return the new input line. */ return waserr ? NULL : gl->line; } /*....................................................................... * Get whether the type of the GetLine object is network. * * Input: * gl GetLine * A resource object returned by new_GetLine(). * Output: * return int If non-zero, the GetLine object type is network. * buffer. */ int gl_is_net(GetLine *gl) { return gl->is_net; } /*....................................................................... * Set whether the type of the GetLine object is network. * * Input: * gl GetLine * A resource object returned by new_GetLine(). * is_net int If non-zero, the GetLine object type is network. */ void gl_set_is_net(GetLine *gl, int is_net) { gl->is_net = is_net; } /*....................................................................... * Get the current position of the cursor within the internal buffer. * * Input: * gl GetLine * A resource object returned by new_GetLine(). * Output: * return int The current position of the cursor within the internal * buffer. */ int gl_get_buff_curpos(const GetLine *gl) { return gl->buff_curpos; } /*....................................................................... * Redraw the current line. * * Input: * gl GetLine * A resource object returned by new_GetLine(). * Output: * return int 0 - OK. * 1 - Error. */ int gl_redisplay_line(GetLine *gl) { if (gl != NULL) gl->term_curpos = 0; return (gl_redisplay(gl, 1)); } /*....................................................................... * Reset the current line. * * Input: * gl GetLine * A resource object returned by new_GetLine(). * Output: * return int 0 - OK. * 1 - Error. */ int gl_reset_line(GetLine *gl) { /* * Clear the buffer. */ gl->ntotal = 0; gl->line[0] = '\0'; #if 0 /* TODO: do we need those? */ gl->number = -1; gl->endline = 0; gl->vi.command = 0; gl->vi.undo.line[0] = '\0'; gl->vi.undo.ntotal = 0; gl->vi.undo.buff_curpos = 0; gl->vi.repeat.fn = 0; gl->last_signal = -1; #endif /* 0 */ /* * Forget any previous recall session. */ gl->preload_id = 0; /* * Recall the next oldest line in the history list. */ _glh_current_line(gl->glh, gl->line, gl->linelen); /* * Move the terminal cursor to just after the prompt. */ if(gl_place_cursor(gl, 0)) return 1; /* * Clear from the end of the prompt to the end of the terminal. */ if(gl_output_control_sequence(gl, gl->nline, gl->clear_eod)) return 1; return 0; } /*....................................................................... * This is the user-defined action function 1. */ static KT_KEY_FN(gl_user_event1) { gl->user_event_value = 1; return 0; } /*....................................................................... * This is the user-defined action function 2. */ static KT_KEY_FN(gl_user_event2) { gl->user_event_value = 2; return 0; } /*....................................................................... * This is the user-defined action function 3. */ static KT_KEY_FN(gl_user_event3) { gl->user_event_value = 3; return 0; } /*....................................................................... * This is the user-defined action function 4. */ static KT_KEY_FN(gl_user_event4) { gl->user_event_value = 4; return 0; } /*....................................................................... * Get the current "user-event" value. * * Input: * gl GetLine * A resource object returned by new_GetLine(). * Output: * return int The current "user-event" value. */ int gl_get_user_event(GetLine *gl) { return gl->user_event_value; } /*....................................................................... * Reset the "user-event" value. * * Input: * gl GetLine * A resource object returned by new_GetLine(). */ void gl_reset_user_event(GetLine *gl) { gl->user_event_value = 0; } /*....................................................................... * Get the action name of the function a key (sequence) has been binded to. * * Input: * gl GetLine * A resource object returned by new_GetLine(). * keyseq char * The key-sequence to find the action name of the * binded function of. * Output: * return const char * The name of the binded function, or NULL if no binding. */ const char * gl_get_key_binding_action_name(GetLine *gl, const char *keyseq) { const char *kptr; /* A pointer into keyseq[] */ char *binary; /* The binary version of keyseq[] */ int nc; /* The number of characters in binary[] */ int first,last; /* The first and last entries in the table which */ /* minimally match. */ int size; /* The size to allocate for the binary string */ KeyTab *kt; /* The table of key-bindings */ const char *action_name = NULL; /* The action name to return */ Symbol *sym; /* The symbol table entry of the action */ /* * Check arguments. */ if(gl==NULL || (kt = gl->bindings)==NULL || !keyseq) { return NULL; }; /* * Work out a pessimistic estimate of how much space will be needed * for the binary copy of the string, noting that binary meta characters * embedded in the input string get split into two characters. */ for(size=0,kptr = keyseq; *kptr; kptr++) size += IS_META_CHAR(*kptr) ? 2 : 1; /* * Allocate a string that has the length of keyseq[]. */ binary = _new_StringMemString(kt->smem, size + 1); if(!binary) { fprintf(stderr, "gl_get_key_binding_action_name: Insufficient memory to record key sequence.\n"); return NULL; }; /* * Convert control and octal character specifications to binary characters. */ if(_kt_parse_keybinding_string(keyseq, binary, &nc)) { binary = _del_StringMemString(kt->smem, binary); return NULL; }; /* * Lookup the position in the table at which there may be binding already. * If an exact match for the key-sequence is already in the table, * lookup its name and return it. */ if (_kt_lookup_keybinding(kt, binary, nc, &first, &last) == KT_EXACT_MATCH) { int i; for (i = 0; i < sizeof(gl_actions)/sizeof(gl_actions[0]); i++) { if (gl_actions[i].fn == kt->table[first].keyfn) { action_name = gl_actions[i].name; break; } } }; binary = _del_StringMemString(kt->smem, binary); return action_name; } xorp/cli/libtecla/SConscript0000664000076400007640000000441111540224220016210 0ustar greearbgreearb# Copyright (c) 2009-2011 XORP, Inc and Others # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # $XORP$ import os Import('env') env = env.Clone() is_shared = env.has_key('SHAREDLIBS') env.AppendUnique(CPPPATH = [ '#' ]) try: env['CFLAGS'].remove('-Werror') except ValueError: pass env['CPPDEFINES'] = { 'STDC_HEADERS': 1, 'HAVE_SYS_TYPES_H': 1, 'HAVE_SYS_STAT_H': 1, 'HAVE_STDLIB_H': 1, 'HAVE_STRING_H': 1, 'HAVE_MEMORY_H': 1, 'HAVE_STRINGS_H': 1, 'HAVE_INTTYPES_H': 1, 'HAVE_STDINT_H': 1, 'HAVE_UNISTD_H': 1, 'HAVE_SELECT_H': 1, 'HAVE_TERMINFO_H': 1, } if not (env.has_key('mingw') and env['mingw']): env.Append(CPPDEFINES={ 'HAVE_SYS_IOCTL_H': 1, 'HAVE_TERMIOS_H': 1, }) libtecla_srcs = [ 'cplfile.c', 'cplmatch.c', 'direader.c', 'expand.c', 'freelist.c', 'getline.c', 'hash.c', 'history.c', 'homedir.c', 'keytab.c', 'pathutil.c', 'pcache.c', 'stringrp.c', 'strngmem.c', 'version.c' ] # External libraries # Some sort of curses library is required. if env.has_key('has_libncurses') and env['has_libncurses']: env.AppendUnique(LIBS = [ 'ncurses' ]) else: # Assume regular curses. env.AppendUnique(LIBS = [ 'curses' ]) if is_shared: libxorp_tecla = env.SharedLibrary('libxorp_tecla', libtecla_srcs) env.Alias('install', env.InstallLibrary(env['xorp_libdir'], libxorp_tecla)) else: libxorp_tecla = env.StaticLibrary('libxorp_tecla', libtecla_srcs) Default(libxorp_tecla) xorp/cli/libtecla/update_html0000775000076400007640000000173411421137511016443 0ustar greearbgreearb#!/bin/sh # Convert man pages to html files. cd man3 for page in *.3;do if [ "`wc -l $page | awk '{print $1}'`" -gt 1 ]; then html="../html/`echo $page | sed -e 's/.3$/.html/'`" man2html $page > $html for ref in "libtecla(3)" "cpl_complete_word(3)" "ef_expand_file(3)" "gl_get_line(3)" "pca_lookup_file(3)" "enhance(3)"; do link="`echo $ref | sed 's/(3)/.html/'`" ed -s $html << EOF 1,\$s|$ref|&|g w q EOF done fi done # Convert the change log into a web page. cd ../html echo 'The tecla library change log' > changes.html echo '
' >> changes.html
cat ../CHANGES >> changes.html
echo '
' >> changes.html # Do the same to the release-notes file. cd ../html echo 'The tecla library release notes' > release.html echo '
' >> release.html
cat ../RELEASE.NOTES >> release.html
echo '
' >> release.html xorp/cli/libtecla/html/0000775000076400007640000000000011421137511015146 5ustar greearbgreearbxorp/cli/libtecla/html/libtecla.html0000664000076400007640000001276511421137511017626 0ustar greearbgreearb Manual Page

NAME

     libtecla - An interactive command-line input library.

SYNOPSIS

     gcc ... -ltecla -lcurses


DESCRIPTION

     The tecla library provides programs with interactive command
     line  editing  facilities, similar to those of the unix tcsh
     shell. In addition to simple command-line editing,  it  sup-
     ports  recall  of previously entered command lines, TAB com-
     pletion of file names or other tokens, and in-line wild-card
     expansion of filenames. The internal functions which perform
     file-name completion and wild-card expansion are also avail-
     able externally for optional use by the calling program.

     The various parts of the library are documented in the  fol-
     lowing man pages:

       gl_get_line(3)        -  The interactive line-input module.
       cpl_complete_word(3)  -  The word completion module.
       ef_expand_file(3)     -  The filename expansion module.
       pca_lookup_file(3)    -  A directory-list based filename
                                lookup and completion module.

     In addition there is one  optional  application  distributed
     with the library:

       enhance(3)            -  Add command-line editing to third
                                party applications.


THREAD SAFETY

     If the library is compiled  with  -D_POSIX_C_SOURCE=199506L,
     reentrant  versions  of  as  many  functions as possible are
     used. This  includes  using  getpwuid_r()  and  getpwnam_r()
     instead  of  getpwuid()  and  getpwnam() when looking up the
     home directories of specific users in the password file (for
     ~user/  expansion), and readdir_r() instead of readdir() for
     reading directory entries when  doing  filename  completion.
     The  reentrant  version  of  the  library  is usually called
     libtecla_r.a instead of libtecla.a, so if only the latter is
     available,  it  probably  isn't  the correct version to link
     with threaded programs.

     Reentrant functions for iterating through the password  file
     aren't  available,  so  when  the  library is compiled to be
     reentrant, TAB completion of incomplete usernames in  ~user-
     name/  expressions  is disabled. This doesn't disable expan-
     sion of complete ~username expressions, which  can  be  done
     reentrantly,  or  expansion  of  the parts of filenames that
     follow them, so this doesn't remove much functionality.

     The terminfo functions setupterm(),  tigetstr(),  tigetnum()
     and  tputs()  also  aren't  reentrant, but very few programs
     will want to  interact  with  multiple  terminals,  so  this
     shouldn't  prevent  this library from being used in threaded
     programs.


LIBRARY VERSION NUMBER

     The version number of the library can be queried  using  the
     following function.

      void libtecla_version(int *major, int *minor, int *micro);


     On return, this function records the three components of the
     libtecla  version number in *major, *minor, *micro. The for-
     mal meaning of the three components is as follows.


      major - Incrementing this number implies that a change has
              been made to the library's public interface, which
              makes it binary incompatible  with programs that
              were linked with previous shared versions of the
              tecla library.

      minor - This number is incremented by one whenever
              additional functionality, such as new functions or
              modules, are added to the library.

      micro - This is incremented whenever modifications to the
              library are made which make no changes to the
              public interface, but which fix bugs and/or improve
              the behind-the-scenes implementation.



TRIVIA

     In Spanish, a "tecla" is the key of a keyboard.  Since  this
     library  centers  on  keyboard input, and given that I wrote
     much of the library while working in Chile, this seemed like
     a suitable name.


FILES

     libtecla.a    -   The tecla library.
     libtecla.h    -   The tecla header file.
     ~/.teclarc    -   The tecla personal customization file.



SEE ALSO

     gl_get_line(3),   ef_expand_file(3),   cpl_complete_word(3),
     pca_lookup_file(3), enhance(3)


AUTHOR

     Martin Shepherd  (mcs@astro.caltech.edu)


ACKNOWLEDGMENTS

     Markus Gyger  - Lots of assistance, including help with
                     shared libraries, configuration information,
                     particularly for Solaris; modifications to
                     support C++ compilers, improvements for ksh
                     users, faster cursor motion, output
                     buffering, and changes to make gl_get_line()
                     8-bit clean.
     Mike MacFaden - Suggestions, feedback and testing that led
                     to many of the major new functions that were
                     added in version 1.4.0.
     Tim Eliseo    - Many vi-mode bindings and fixes.































xorp/cli/libtecla/html/gl_get_line.html0000664000076400007640000032130311421137511020306 0ustar greearbgreearb Manual Page

NAME

     gl_get_line,            new_GetLine,            del_GetLine,
     gl_customize_completion,                 gl_change_terminal,
     gl_configure_getline,   gl_load_history,    gl_save_history,
     gl_group_history,        gl_show_history,       gl_watch_fd,
     gl_terminal_size,    gl_resize_history,    gl_limit_history,
     gl_clear_history,    gl_toggle_history,   gl_lookup_history,
     gl_state_of_history,                    gl_range_of_history,
     gl_size_of_history,     gl_echo_mode,     gl_replace_prompt,
     gl_prompt_style,      gl_ignore_signal,      gl_trap_signal,
     gl_last_signal - allow the user to compose an input line

SYNOPSIS

     #include <stdio.h>
     #include <libtecla.h>

     GetLine *new_GetLine(size_t linelen, size_t histlen);

     GetLine *del_GetLine(GetLine *gl);

     char *gl_get_line(GetLine *gl, const char *prompt,
                      const char *start_line, int start_pos);

     int gl_customize_completion(GetLine *gl, void *data,
                                 CplMatchFn *match_fn);

     int gl_change_terminal(GetLine *gl, FILE *input_fp,
                            FILE *output_fp, const char *term);

     int gl_configure_getline(GetLine *gl,
                              const char *app_string,
                              const char *app_file,
                              const char *user_file);

     int gl_save_history(GetLine *gl, const char *filename,
                         const char *comment, int max_lines);

     int gl_load_history(GetLine *gl, const char *filename,
                         const char *comment);

     int gl_watch_fd(GetLine *gl, int fd, GlFdEvent event,
                     GlFdEventFn *callback, void *data);

     int gl_group_history(GetLine *gl, unsigned stream);

     int gl_show_history(GetLine *gl, FILE *fp,
                         const char *fmt, int all_groups,
                         int max_lines);

     int gl_resize_history(GetLine *gl, size_t bufsize);

     void gl_limit_history(GetLine *gl, int max_lines);
     void gl_clear_history(GetLine *gl, int all_groups);

     void gl_toggle_history(GetLine *gl, int enable);

     GlTerminalSize gl_terminal_size(GetLine *gl,
                                     int def_ncolumn,
                                     int def_nline);

     int gl_lookup_history(GetLine *gl, unsigned long id,
                           GlHistoryLine *hline);

     void gl_state_of_history(GetLine *gl,
                              GlHistoryState *state);

     void gl_range_of_history(GetLine *gl,
                              GlHistoryRange *range);

     void gl_size_of_history(GetLine *gl, GlHistorySize *size);

     void gl_echo_mode(GetLine *gl, int enable);

     void gl_replace_prompt(GetLine *gl, const char *prompt);

     void gl_prompt_style(GetLine *gl, GlPromptStyle style);

     int gl_ignore_signal(GetLine *gl, int signo);

     int gl_trap_signal(GetLine *gl, int signo, unsigned flags,
                        GlAfterSignal after, int errno_value);

     int gl_last_signal(const GetLine *gl);



DESCRIPTION

     The gl_get_line() function is part of the tecla library (see
     the libtecla(3) man page). If the user is typing at a termi-
     nal, it prompts them for an line  of  input,  then  provides
     interactive editing facilities, similar to those of the unix
     tcsh shell. In addition to simple command-line  editing,  it
     supports  recall  of  previously  entered command lines, TAB
     completion of file names, and in-line wild-card expansion of
     filenames.


AN EXAMPLE

     The following shows a complete example of  how  to  use  the
     gl_get_line() function to get input from the user:

       #include <stdio.h>
       #include <locale.h>
       #include <libtecla.h>
       int main(int argc, char *argv[])
       {
         char *line;    /* The line that the user typed */
         GetLine *gl;   /* The gl_get_line() resource object */

         setlocale(LC_CTYPE, ""); /* Adopt the user's choice */
                                  /* of character set. */

         gl = new_GetLine(1024, 2048);
         if(!gl)
           return 1;

         while((line=gl_get_line(gl, "$ ", NULL, -1)) != NULL &&
                strcmp(line, "exit\n") != 0)
           printf("You typed: %s\n", line);

         gl = del_GetLine(gl);
         return 0;
       }

     In  the  example,  first  the  resources   needed   by   the
     gl_get_line() function are created by calling new_GetLine().
     This allocates the memory used in subsequent  calls  to  the
     gl_get_line()  function,  including  the  history buffer for
     recording previously entered lines. Then one or  more  lines
     are read from the user, until either an error occurs, or the
     user types exit. Then finally the resources that were  allo-
     cated  by  new_GetLine(), are returned to the system by cal-
     ling del_GetLine(). Note the use of the NULL return value of
     del_GetLine()  to make gl NULL. This is a safety precaution.
     If  the  program  subsequently  attempts  to  pass   gl   to
     gl_get_line(),  said  function  will complain, and return an
     error, instead of attempting to  use  the  deleted  resource
     object.



THE FUNCTIONS USED IN THE EXAMPLE

     The descriptions of the functions used in the example are as
     follows:

       GetLine *new_GetLine(size_t linelen, size_t histlen)

     This  function   creates   the   resources   used   by   the
     gl_get_line()  function and returns an opaque pointer to the
     object that contains them.  The maximum length of  an  input
     line  is  specified via the linelen argument, and the number
     of bytes to allocate for storing history lines is set by the
     histlen argument. History lines are stored back-to-back in a
     single buffer of this size. Note that this  means  that  the
     number  of  history  lines  that  can be stored at any given
     time, depends on the lengths of the  individual  lines.   If
     you want to place an upper limit on the number of lines that
     can be stored, see the gl_limit_history() function described
     later.  If you don't want history at all, specify histlen as
     zero, and no history buffer will be allocated.

     On error, a  message  is  printed  to  stderr  and  NULL  is
     returned.

       GetLine *del_GetLine(GetLine *gl)

     This function deletes the resources that were returned by  a
     previous call to new_GetLine(). It always returns NULL (ie a
     deleted object). It does nothing if the gl argument is NULL.

       char *gl_get_line(GetLine *gl, const char *prompt,
                        const char *start_line, int start_pos);

     The gl_get_line() function can be called any number of times
     to  read input from the user. The gl argument must have been
     previously returned by a call to new_GetLine().  The  prompt
     argument  should be a normal NUL terminated string, specify-
     ing the prompt to present the user with. By default  prompts
     are   displayed   literally,   but   if   enabled  with  the
     gl_prompt_style() function (see later), prompts can  contain
     directives to do underlining, switch to and from bold fonts,
     or turn highlighting on and off.

     If you want to specify the initial contents of the line, for
     the user to edit, pass the desired string via the start_line
     argument. You can then specify which character of this  line
     the cursor is initially positioned over, using the start_pos
     argument. This should be -1 if you want the cursor to follow
     the  last  character of the start line. If you don't want to
     preload the line in this manner, send  start_line  as  NULL,
     and set start_pos to -1.

     The gl_get_line() function returns a  pointer  to  the  line
     entered  by  the user, or NULL on error or at the end of the
     input. The returned pointer is  part  of  the  specified  gl
     resource  object,  and  thus  should  not  be  free'd by the
     caller, or assumed to be unchanging from  one  call  to  the
     next.  When  reading  from  a user at a terminal, there will
     always be a newline character at the  end  of  the  returned
     line.   When  standard input is being taken from a pipe or a
     file, there will similarly be a  newline  unless  the  input
     line  was  too  long to store in the internal buffer. In the
     latter case you should call gl_get_line() again to read  the
     rest   of   the   line.   Note   that  this  behavior  makes
     gl_get_line() similar to fgets().  In fact when stdin  isn't
     connected to a terminal,gl_get_line() just calls fgets().


OPTIONAL PROMPT FORMATTING

     Whereas by default the prompt string  that  you  specify  is
     displayed  literally,  without any special interpretation of
     the characters within it, the gl_prompt_style() function can
     be  used to enable optional formatting directives within the
     prompt.

       void gl_prompt_style(GetLine *gl, GlPromptStyle style);

     The style argument, which specifies  the  formatting  style,
     can take any of the following values:

       GL_FORMAT_PROMPT   -  In this style, the formatting
                             directives described below, when
                             included in prompt strings, are
                             interpreted as follows:

                               %B  -  Display subsequent
                                      characters with a bold
                                      font.
                               %b  -  Stop displaying characters
                                      with the bold font.
                               %F  -  Make subsequent characters
                                      flash.
                               %f  -  Turn off flashing
                                      characters.
                               %U  -  Underline subsequent
                                      characters.
                               %u  -  Stop underlining
                                      characters.
                               %P  -  Switch to a pale (half
                                      brightness) font.
                               %p  -  Stop using the pale font.
                               %S  -  Highlight subsequent
                                      characters (also known as
                                      standout mode).
                               %s  -  Stop highlighting
                                      characters.
                               %V  -  Turn on reverse video.
                               %v  -  Turn off reverse video.
                               %%  -  Display a single %
                                      character.

                             For example, in this mode, a prompt
                             string like "%UOK%u$ " would
                             display the prompt "OK$ ",
                             but with the OK part
                             underlined.

                             Note that although a pair of
                             characters that starts with a %
                             character, but doesn't match any of
                             the above directives is displayed
                             literally, if a new directive is
                             subsequently introduced which does
                             match, the displayed prompt will
                             change, so it is better to always
                             use %% to display a literal %.

                             Also note that not all terminals
                             support all of these text
                             attributes, and that some substitute
                             a different attribute for missing
                             ones.

       GL_LITERAL_PROMPT  -  In this style, the prompt string is
                             printed literally. This is the
                             default style.



THE AVAILABLE KEY BINDING FUNCTIONS

     The gl_get_line() function provides a  number  of  functions
     which  can  be  bound  to  key sequences. The names of these
     functions, and what they do, are given below.

       user-interrupt           -  Send a SIGINT signal to the
                                   parent process.
       abort                    -  Send a SIGABRT signal to the
                                   parent process.
       suspend                  -  Suspend the parent process.
       stop-output              -  Pause terminal output.
       start-output             -  Resume paused terminal output.
       literal-next             -  Arrange for the next character
                                   to be treated as a normal
                                   character. This allows control
                                   characters to be entered.
       cursor-right             -  Move the cursor one character
                                   right.
       cursor-left              -  Move the cursor one character
                                   left.
       insert-mode              -  Toggle between insert mode and
                                   overwrite mode.
       beginning-of-line        -  Move the cursor to the
                                   beginning of the line.
       end-of-line              -  Move the cursor to the end of
                                   the line.
       delete-line              -  Delete the contents of the
                                   current line.
       kill-line                -  Delete everything that follows
                                   the cursor.
       backward-kill-line       -  Delete all characters between
                                   the cursor and the start of the
                                   line.
       forward-word             -  Move to the end of the word
                                   which follows the cursor.
       forward-to-word          -  Move the cursor to the start of
                                   the word that follows the
                                   cursor.
       backward-word            -  Move to the start of the word
                                   which precedes the cursor.
       goto-column              -  Move the cursor to the
                                   1-relative column in the line
                                   specified by any preceding
                                   digit-argument sequences (see
                                   ENTERING REPEAT COUNTS below).
       find-parenthesis         -  If the cursor is currently
                                   over a parenthesis character,
                                   move it to the matching
                                   parenthesis character. If not
                                   over a parenthesis character
                                   move right to the next close
                                   parenthesis.
       forward-delete-char      -  Delete the character under the
                                   cursor.
       backward-delete-char     -  Delete the character which
                                   precedes the cursor.
       list-or-eof              -  This is intended for binding
                                   to ^D. When invoked when the
                                   cursor is within the line it
                                   displays all possible
                                   completions then redisplays
                                   the line unchanged. When
                                   invoked on an empty line, it
                                   signals end-of-input (EOF) to
                                   the caller of gl_get_line().
       del-char-or-list-or-eof  -  This is intended for binding
                                   to ^D. When invoked when the
                                   cursor is within the line it
                                   invokes forward-delete-char.
                                   When invoked at the end of the
                                   line it displays all possible
                                   completions then redisplays
                                   the line unchanged. When
                                   invoked on an empty line, it
                                   signals end-of-input (EOF) to
                                   the caller of gl_get_line().
       forward-delete-word      -  Delete the word which follows
                                   the cursor.
       backward-delete-word     -  Delete the word which precedes
                                   the cursor.
       upcase-word              -  Convert all of the characters
                                   of the word which follows the
                                   cursor, to upper case.
       downcase-word            -  Convert all of the characters
                                   of the word which follows the
                                   cursor, to lower case.
       capitalize-word          -  Capitalize the word which
                                   follows the cursor.
       change-case              -  If the next character is upper
                                   case, toggle it to lower case
                                   and vice versa.
       redisplay                -  Redisplay the line.
       clear-screen             -  Clear the terminal, then
                                   redisplay the current line.
       transpose-chars          -  Swap the character under the
                                   cursor with the character just
                                   before the cursor.
       set-mark                 -  Set a mark at the position of
                                   the cursor.
       exchange-point-and-mark  -  Move the cursor to the last
                                   mark that was set, and move
                                   the mark to where the cursor
                                   used to be.
       kill-region              -  Delete the characters that lie
                                   between the last mark that was
                                   set, and the cursor.
       copy-region-as-kill      -  Copy the text between the mark
                                   and the cursor to the cut
                                   buffer, without deleting the
                                   original text.
       yank                     -  Insert the text that was last
                                   deleted, just before the
                                   current position of the cursor.
       append-yank              -  Paste the current contents of
                                   the cut buffer, after the
                                   cursor.
       up-history               -  Recall the next oldest line
                                   that was entered. Note that
                                   in vi mode you are left in
                                   command mode.
       down-history             -  Recall the next most recent
                                   line that was entered. If no
                                   history recall session is
                                   currently active, the next
                                   line from a previous recall
                                   session is recalled. Note that
                                   in vi mode you are left in
                                   command mode.
       history-search-backward  -  Recall the next oldest line
                                   who's prefix matches the string
                                   which currently precedes the
                                   cursor (in vi command-mode the
                                   character under the cursor is
                                   also included in the search
                                   string).  Note that in vi mode
                                   you are left in command mode.
       history-search-forward   -  Recall the next newest line
                                   who's prefix matches the string
                                   which currently precedes the
                                   cursor (in vi command-mode the
                                   character under the cursor is
                                   also included in the search
                                   string).  Note that in vi mode
                                   you are left in command mode.
       history-re-search-backward -Recall the next oldest line
                                   who's prefix matches that
                                   established by the last
                                   invocation of either
                                   history-search-forward or
                                   history-search-backward.
       history-re-search-forward - Recall the next newest line
                                   who's prefix matches that
                                   established by the last
                                   invocation of either
                                   history-search-forward or
                                   history-search-backward.
       complete-word            -  Attempt to complete the
                                   incomplete word which
                                   precedes the cursor. Unless
                                   the host program has customized
                                   word completion, filename
                                   completion is attempted. In vi
                                   commmand mode the character
                                   under the cursor is also
                                   included in the word being
                                   completed, and you are left in
                                   vi insert mode.
       expand-filename          -  Within the command line, expand
                                   wild cards, tilde expressions
                                   and dollar expressions in the
                                   filename which immediately
                                   precedes the cursor. In vi
                                   commmand mode the character
                                   under the cursor is also
                                   included in the filename being
                                   expanded, and you are left in
                                   vi insert mode.
       list-glob                -  List any filenames which match
                                   the wild-card, tilde and dollar
                                   expressions in the filename
                                   which immediately precedes the
                                   cursor, then redraw the input
                                   line unchanged.
       list-history             -  Display the contents of the
                                   history list for the current
                                   history group. If a repeat
                                   count of > 1 is specified,
                                   only that many of the most
                                   recent lines are displayed.
                                   See the "ENTERING REPEAT
                                   COUNTS" section.
       read-from-file           -  Temporarily switch to reading
                                   input from the file who's
                                   name precedes the cursor.
       read-init-files          -  Re-read teclarc configuration
                                   files.
       beginning-of-history     -  Move to the oldest line in the
                                   history list. Note that in vi
                                   mode you are left in command
                                   mode.
       end-of-history           -  Move to the newest line in the
                                   history list (ie. the current
                                   line). Note that in vi mode
                                   this leaves you in command
                                   mode.
       digit-argument           -  Enter a repeat count for the
                                   next key-binding function.
                                   For details, see the ENTERING
                                   REPEAT COUNTS section.
       newline                  -  Terminate and return the
                                   current contents of the
                                   line, after appending a
                                   newline character. The newline
                                   character is normally '\n',
                                   but will be the first
                                   character of the key-sequence
                                   that invoked the newline
                                   action, if this happens to be
                                   a printable character. If the
                                   action was invoked by the
                                   '\n' newline character or the
                                   '\r' carriage return
                                   character, the line is
                                   appended to the history
                                   buffer.
       repeat-history           -  Return the line that is being
                                   edited, then arrange for the
                                   next most recent entry in the
                                   history buffer to be recalled
                                   when gl_get_line() is
                                   next called. Repeatedly
                                   invoking this action causes
                                   successive historical input
                                   lines to be re-executed. Note
                                   that this action is equivalent
                                   to the 'Operate' action in
                                   ksh.
       ring-bell                -  Ring the terminal bell, unless
                                   the bell has been silenced via
                                   the nobeep configuration
                                   option (see the THE TECLA
                                   CONFIGURATION FILE section).
       forward-copy-char        -  Copy the next character into
                                   the cut buffer (NB. use repeat
                                   counts to copy more than one).
       backward-copy-char       -  Copy the previous character
                                   into the cut buffer.
       forward-copy-word        -  Copy the next word into the cut
                                   buffer.
       backward-copy-word       -  Copy the previous word into the
                                   cut buffer.
       forward-find-char        -  Move the cursor to the next
                                   occurrence of the next
                                   character that you type.
       backward-find-char       -  Move the cursor to the last
                                   occurrence of the next
                                   character that you type.
       forward-to-char          -  Move the cursor to the
                                   character just before the next
                                   occurrence of the next
                                   character that the user types.
       backward-to-char         -  Move the cursor to the
                                   character just after the last
                                   occurrence before the cursor
                                   of the next character that the
                                   user types.
       repeat-find-char         -  Repeat the last
                                   backward-find-char,
                                   forward-find-char,
                                   backward-to-char or
                                   forward-to-char.
       invert-refind-char       -  Repeat the last
                                   backward-find-char,
                                   forward-find-char,
                                   backward-to-char, or
                                   forward-to-char in the
                                   opposite direction.
       delete-to-column         -  Delete the characters from the
                                   cursor up to the column that
                                   is specified by the repeat
                                   count.
       delete-to-parenthesis    -  Delete the characters from the
                                   cursor up to and including
                                   the matching parenthesis, or
                                   next close parenthesis.
       forward-delete-find      -  Delete the characters from the
                                   cursor up to and including the
                                   following occurence of the
                                   next character typed.
       backward-delete-find     -  Delete the characters from the
                                   cursor up to and including the
                                   preceding occurence of the
                                   next character typed.
       forward-delete-to        -  Delete the characters from the
                                   cursor up to, but not
                                   including, the following
                                   occurence of the next
                                   character typed.
       backward-delete-to       -  Delete the characters from the
                                   cursor up to, but not
                                   including, the preceding
                                   occurence of the next
                                   character typed.
       delete-refind            -  Repeat the last *-delete-find
                                   or *-delete-to action.
       delete-invert-refind     -  Repeat the last *-delete-find
                                   or *-delete-to action, in the
                                   opposite direction.
       copy-to-column           -  Copy the characters from the
                                   cursor up to the column that
                                   is specified by the repeat
                                   count, into the cut buffer.
       copy-to-parenthesis      -  Copy the characters from the
                                   cursor up to and including
                                   the matching parenthesis, or
                                   next close parenthesis, into
                                   the cut buffer.
       forward-copy-find        -  Copy the characters from the
                                   cursor up to and including the
                                   following occurence of the
                                   next character typed, into the
                                   cut buffer.
       backward-copy-find       -  Copy the characters from the
                                   cursor up to and including the
                                   preceding occurence of the
                                   next character typed, into the
                                   cut buffer.
       forward-copy-to          -  Copy the characters from the
                                   cursor up to, but not
                                   including, the following
                                   occurence of the next
                                   character typed, into the cut
                                   buffer.
       backward-copy-to         -  Copy the characters from the
                                   cursor up to, but not
                                   including, the preceding
                                   occurence of the next
                                   character typed, into the cut
                                   buffer.
       copy-refind              -  Repeat the last *-copy-find
                                   or *-copy-to action.
       copy-invert-refind       -  Repeat the last *-copy-find
                                   or *-copy-to action, in the
                                   opposite direction.
       vi-mode                  -  Switch to vi mode from emacs
                                   mode.
       emacs-mode               -  Switch to emacs mode from vi
                                   mode.
       vi-insert                -  From vi command mode, switch to
                                   insert mode.
       vi-overwrite             -  From vi command mode, switch to
                                   overwrite mode.
       vi-insert-at-bol         -  From vi command mode, move the
                                   cursor to the start of the line
                                   and switch to insert mode.
       vi-append-at-eol         -  From vi command mode, move the
                                   cursor to the end of the line
                                   and switch to append mode.
       vi-append                -  From vi command mode, move the
                                   cursor one position right, and
                                   switch to insert mode.
       vi-replace-char          -  From vi command mode, replace
                                   the character under the cursor
                                   with the the next character
                                   entered.
       vi-forward-change-char   -  From vi command mode, delete
                                   the next character then enter
                                   insert mode.
       vi-backward-change-char  -  From vi command mode, delete
                                   the preceding character then
                                   enter insert mode.
       vi-forward-change-word   -  From vi command mode, delete
                                   the next word then enter
                                   insert mode.
       vi-backward-change-word  -  From vi command mode, delete
                                   the preceding word then
                                   enter insert mode.
       vi-change-rest-of-line   -  From vi command mode, delete
                                   from the cursor to the end of
                                   the line, then enter insert
                                   mode.
       vi-change-line           -  From vi command mode, delete
                                   the current line, then enter
                                   insert mode.
       vi-change-to-bol         -  From vi command mode, delete
                                   all characters between the
                                   cursor and the beginning of
                                   the line, then enter insert
                                   mode.
       vi-change-to-column      -  From vi command mode, delete
                                   the characters from the cursor
                                   up to the column that is
                                   specified by the repeat count,
                                   then enter insert mode.
       vi-change-to-parenthesis -  Delete the characters from the
                                   cursor up to and including
                                   the matching parenthesis, or
                                   next close parenthesis, then
                                   enter vi insert mode.
       vi-forward-change-find   -  From vi command mode, delete
                                   the characters from the
                                   cursor up to and including the
                                   following occurence of the
                                   next character typed, then
                                   enter insert mode.
       vi-backward-change-find  -  From vi command mode, delete
                                   the characters from the
                                   cursor up to and including the
                                   preceding occurence of the
                                   next character typed, then
                                   enter insert mode.
       vi-forward-change-to     -  From vi command mode, delete
                                   the characters from the
                                   cursor up to, but not
                                   including, the following
                                   occurence of the next
                                   character typed, then enter
                                   insert mode.
       vi-backward-change-to    -  From vi command mode, delete
                                   the characters from the
                                   cursor up to, but not
                                   including, the preceding
                                   occurence of the next
                                   character typed, then enter
                                   insert mode.
       vi-change-refind         -  Repeat the last
                                   vi-*-change-find or
                                   vi-*-change-to action.
       vi-change-invert-refind  -  Repeat the last
                                   vi-*-change-find or
                                   vi-*-change-to action, in the
                                   opposite direction.
       vi-undo                  -  In vi mode, undo the last
                                   editing operation.
       vi-repeat-change         -  In vi command mode, repeat the
                                   last command that modified the
                                   line.


DEFAULT KEY BINDINGS IN EMACS MODE

     The following default key bindings, which can  be  overriden
     by  the tecla configuration file, are designed to mimic most
     of the bindings of the unix tcsh shell, when it is in  emacs
     editing mode.

     This is the default editing mode of the tecla library.

     Note that a key sequence like  ^A  or  C-a  means  hold  the
     control-key down while pressing the letter A, and that where
     you see \E or M- in a binding, this  represents  the  escape
     key   or   the   Meta   modifier  key.  Also  note  that  to
     gl_get_line(), pressing the  escape  key  before  a  key  is
     equivalent to pressing the meta key at the same time as that
     key. Thus the key sequence M-p can be typed in two ways,  by
     pressing  the  escape  key,  followed  by  pressing p, or by
     pressing the Meta key at the same time as p.

     Under UNIX the terminal driver sets a number of special keys
     for certain functions. The tecla library attempts to use the
     same keybindings to maintain consistency. The key  sequences
     shown for the following 6 bindings are thus just examples of
     what they will probably be set to. If you have used the stty
     command  to  change  these  keys,  then the default bindings
     should match.

       ^C     ->   user-interrupt
       ^\     ->   abort
       ^Z     ->   suspend
       ^Q     ->   start-output
       ^S     ->   stop-output
       ^V     ->   literal-next

     The cursor keys are refered to by name, as follows. This  is
     necessary because different types of terminals generate dif-
     ferent key sequences when their cursor keys are pressed.

       right  ->   cursor-right
       left   ->   cursor-left
       up     ->   up-history
       down   ->   down-history

     The remaining bindings don't depend on  the  terminal  sett-
     tings.

       ^F     ->   cursor-right
       ^B     ->   cursor-left
       M-i    ->   insert-mode
       ^A     ->   beginning-of-line
       ^E     ->   end-of-line
       ^U     ->   delete-line
       ^K     ->   kill-line
       M-f    ->   forward-word
       M-b    ->   backward-word
       ^D     ->   del-char-or-list-or-eof
       ^H     ->   backward-delete-char
       ^?     ->   backward-delete-char
       M-d    ->   forward-delete-word
       M-^H   ->   backward-delete-word
       M-^?   ->   backward-delete-word
       M-u    ->   upcase-word
       M-l    ->   downcase-word
       M-c    ->   capitalize-word
       ^R     ->   redisplay
       ^L     ->   clear-screen
       ^T     ->   transpose-chars
       ^@     ->   set-mark
       ^X^X   ->   exchange-point-and-mark
       ^W     ->   kill-region
       M-w    ->   copy-region-as-kill
       ^Y     ->   yank
       ^P     ->   up-history
       ^N     ->   down-history
       M-p    ->   history-search-backward
       M-n    ->   history-search-forward
       ^I     ->   complete-word
       ^X*    ->   expand-filename
       ^X^F   ->   read-from-file
       ^X^R   ->   read-init-files
       ^Xg    ->   list-glob
       ^Xh    ->   list-history
       M-<    ->   beginning-of-history
       M->    ->   end-of-history
       \n     ->   newline
       \r     ->   newline
       M-o    ->   repeat-history
       M-^V   ->   vi-mode

       M-0, M-1, ... M-9  ->  digit-argument  (see below)

     Note that ^I is what the TAB key generates, and that ^@  can
     be  generated not only by pressing the control key and the @
     key simultaneously, but also by pressing the control key and
     the space bar at the same time.


DEFAULT KEY BINDINGS IN VI MODE

     The following default key bindings are designed to mimic the
     vi  style of editing as closely as possible. This means that
     very few editing functions are provided in the initial char-
     acter  input  mode, editing functions instead being provided
     by the vi command mode. Vi command mode is entered  whenever
     the  escape character is pressed, or whenever a key-sequence
     that starts with a meta character is entered. In addition to
     mimicing  vi, libtecla provides bindings for tab completion,
     wild-card expansion  of  file  names,  and  historical  line
     recall.

     To learn how to tell  the  tecla  library  to  use  vi  mode
     instead  of  the default emacs editing mode, see the section
     entitled THE TECLA CONFIGURATION FILE.

     As already mentioned above in the emacs section, Note that a
     key  sequence like ^A or C-a means hold the control-key down
     while pressing the letter A, and that where you see \E or M-
     in  a  binding,  this  represents the escape key or the Meta
     modifier key. Also note that to gl_get_line(), pressing  the
     escape  key  before a key is equivalent to pressing the meta
     key at the same time as that key. Thus the key sequence  M-p
     can  be  typed in two ways, by pressing the escape key, fol-
     lowed by pressing p, or by pressing the Meta key at the same
     time as p.

     Under UNIX the terminal driver sets a number of special keys
     for certain functions. The tecla library attempts to use the
     same keybindings to maintain consistency, binding them  both
     in  input  mode and in command mode. The key sequences shown
     for the following 6 bindings are thus just examples of  what
     they will probably be set to. If you have used the stty com-
     mand to change these keys, then the default bindings  should
     match.

       ^C     ->   user-interrupt
       ^\     ->   abort
       ^Z     ->   suspend
       ^Q     ->   start-output
       ^S     ->   stop-output
       ^V     ->   literal-next
       M-^C   ->   user-interrupt
       M-^\   ->   abort
       M-^Z   ->   suspend
       M-^Q   ->   start-output
       M-^S   ->   stop-output

     Note that above, most of the  bindings  are  defined  twice,
     once as a raw control code like ^C and then a second time as
     a meta character like M-^C. The former is the binding for vi
     input mode, whereas the latter is the binding for vi command
     mode. Once in command mode all key-sequences that  the  user
     types  that  they don't explicitly start with an escape or a
     meta key, have their first key secretly converted to a  meta
     character  before  the  key sequence is looked up in the key
     binding table. Thus, once in command mode, when you type the
     letter  i,  for example, the tecla library actually looks up
     the binding for M-i.

     The cursor keys are refered to by name, as follows. This  is
     necessary because different types of terminals generate dif-
     ferent key sequences when their cursor keys are pressed.

       right  ->   cursor-right
       left   ->   cursor-left
       up     ->   up-history
       down   ->   down-history

     The cursor keys normally generate a keysequence  that  start
     with  an  escape  character,  so beware that using the arrow
     keys will put you into command mode (if you  aren't  already
     in command mode).

     The following are the terminal-independent key bindings  for
     vi input mode.

       ^D     ->   list-or-eof
       ^G     ->   list-glob
       ^H     ->   backward-delete-char
       ^I     ->   complete-word
       \r     ->   newline
       \n     ->   newline
       ^L     ->   clear-screen
       ^N     ->   down-history
       ^P     ->   up-history
       ^R     ->   redisplay
       ^U     ->   backward-kill-line
       ^W     ->   backward-delete-word
       ^X*    ->   expand-filename
       ^X^F   ->   read-from-file
       ^X^R   ->   read-init-files
       ^?     ->   backward-delete-char

     The following are the key bindings that are  defined  in  vi
     command mode, this being specified by them all starting with
     a meta character. As mentioned above, once in  command  mode
     the  initial  meta  character  is optional. For example, you
     might enter command mode by typing Esc,  and  then  press  h
     twice  to  move the cursor two positions to the left. Both h
     characters get quietly converted to M-h  before  being  com-
     pared to the key-binding table, the first one because Escape
     followed  by  a  character  is  always  converted   to   the
     equivalent  meta  character,  and the second because command
     mode was already active.

       M-\     ->   cursor-right     (Meta-space)
       M-$     ->   end-of-line
       M-*     ->   expand-filename
       M-+     ->   down-history
       M--     ->   up-history
       M-<     ->   beginning-of-history
       M->     ->   end-of-history
       M-^     ->   beginning-of-line
       M-;     ->   repeat-find-char
       M-,     ->   invert-refind-char
       M-|     ->   goto-column
       M-~     ->   change-case
       M-.     ->   vi-repeat-change
       M-%     ->   find-parenthesis
       M-a     ->   vi-append
       M-A     ->   vi-append-at-eol
       M-b     ->   backward-word
       M-B     ->   backward-word
       M-C     ->   vi-change-rest-of-line
       M-cb    ->   vi-backward-change-word
       M-cB    ->   vi-backward-change-word
       M-cc    ->   vi-change-line
       M-ce    ->   vi-forward-change-word
       M-cE    ->   vi-forward-change-word
       M-cw    ->   vi-forward-change-word
       M-cW    ->   vi-forward-change-word
       M-cF    ->   vi-backward-change-find
       M-cf    ->   vi-forward-change-find
       M-cT    ->   vi-backward-change-to
       M-ct    ->   vi-forward-change-to
       M-c;    ->   vi-change-refind
       M-c,    ->   vi-change-invert-refind
       M-ch    ->   vi-backward-change-char
       M-c^H   ->   vi-backward-change-char
       M-c^?   ->   vi-backward-change-char
       M-cl    ->   vi-forward-change-char
       M-c\    ->   vi-forward-change-char  (Meta-c-space)
       M-c^    ->   vi-change-to-bol
       M-c0    ->   vi-change-to-bol
       M-c$    ->   vi-change-rest-of-line
       M-c|    ->   vi-change-to-column
       M-c%    ->   vi-change-to-parenthesis
       M-dh    ->   backward-delete-char
       M-d^H   ->   backward-delete-char
       M-d^?   ->   backward-delete-char
       M-dl    ->   forward-delete-char
       M-d     ->   forward-delete-char    (Meta-d-space)
       M-dd    ->   delete-line
       M-db    ->   backward-delete-word
       M-dB    ->   backward-delete-word
       M-de    ->   forward-delete-word
       M-dE    ->   forward-delete-word
       M-dw    ->   forward-delete-word
       M-dW    ->   forward-delete-word
       M-dF    ->   backward-delete-find
       M-df    ->   forward-delete-find
       M-dT    ->   backward-delete-to
       M-dt    ->   forward-delete-to
       M-d;    ->   delete-refind
       M-d,    ->   delete-invert-refind
       M-d^    ->   backward-kill-line
       M-d0    ->   backward-kill-line
       M-d$    ->   kill-line
       M-D     ->   kill-line
       M-d|    ->   delete-to-column
       M-d%    ->   delete-to-parenthesis
       M-e     ->   forward-word
       M-E     ->   forward-word
       M-f     ->   forward-find-char
       M-F     ->   backward-find-char
       M--     ->   up-history
       M-h     ->   cursor-left
       M-H     ->   beginning-of-history
       M-i     ->   vi-insert
       M-I     ->   vi-insert-at-bol
       M-j     ->   down-history
       M-J     ->   history-search-forward
       M-k     ->   up-history
       M-K     ->   history-search-backward
       M-l     ->   cursor-right
       M-L     ->   end-of-history
       M-n     ->   history-re-search-forward
       M-N     ->   history-re-search-backward
       M-p     ->   append-yank
       M-P     ->   yank
       M-r     ->   vi-replace-char
       M-R     ->   vi-overwrite
       M-s     ->   vi-forward-change-char
       M-S     ->   vi-change-line
       M-t     ->   forward-to-char
       M-T     ->   backward-to-char
       M-u     ->   vi-undo
       M-w     ->   forward-to-word
       M-W     ->   forward-to-word
       M-x     ->   forward-delete-char
       M-X     ->   backward-delete-char
       M-yh    ->   backward-copy-char
       M-y^H   ->   backward-copy-char
       M-y^?   ->   backward-copy-char
       M-yl    ->   forward-copy-char
       M-y\    ->   forward-copy-char  (Meta-y-space)
       M-ye    ->   forward-copy-word
       M-yE    ->   forward-copy-word
       M-yw    ->   forward-copy-word
       M-yW    ->   forward-copy-word
       M-yb    ->   backward-copy-word
       M-yB    ->   backward-copy-word
       M-yf    ->   forward-copy-find
       M-yF    ->   backward-copy-find
       M-yt    ->   forward-copy-to
       M-yT    ->   backward-copy-to
       M-y;    ->   copy-refind
       M-y,    ->   copy-invert-refind
       M-y^    ->   copy-to-bol
       M-y0    ->   copy-to-bol
       M-y$    ->   copy-rest-of-line
       M-yy    ->   copy-line
       M-Y     ->   copy-line
       M-y|    ->   copy-to-column
       M-y%    ->   copy-to-parenthesis
       M-^E    ->   emacs-mode
       M-^H    ->   cursor-left
       M-^?    ->   cursor-left
       M-^L    ->   clear-screen
       M-^N    ->   down-history
       M-^P    ->   up-history
       M-^R    ->   redisplay
       M-^D    ->   list-or-eof
       M-^I    ->   complete-word
       M-\r    ->   newline
       M-\n    ->   newline
       M-^X^R  ->   read-init-files
       M-^Xh   ->   list-history

       M-0, M-1, ... M-9  ->  digit-argument  (see below)

     Note that ^I is what the TAB key generates.


ENTERING REPEAT COUNTS

     Many of the key binding functions described previously, take
     an  optional  count, typed in before the target keysequence.
     This is interpreted as a repeat count by  most  bindings.  A
     notable  exception  is the goto-column binding, which inter-
     prets the count as a column number.

     By default you can specify this count argument  by  pressing
     the  meta key while typing in the numeric count. This relies
     on the digit-argument action being bound to  Meta-0,  Meta-1
     etc.  Once any one of these bindings has been activated, you
     can optionally take your finger off the meta key to type  in
     the rest of the number, since every numeric digit thereafter
     is treated as part of the number, unless it is  preceded  by
     the literal-next binding. As soon as a non-digit, or literal
     digit key is pressed the  repeat  count  is  terminated  and
     either  causes  the  just typed character to be added to the
     line that many times, or causes the next  key-binding  func-
     tion to be given that argument.

     For example, in emacs mode, typing:

       M-12a

     causes the letter 'a' to be added  to  the  line  12  times,
     whereas

       M-4M-c

     Capitalizes the next 4 words.

     In vi command mode the Meta modifier is automatically  added
     to  all  characters  typed  in,  so  to  enter a count in vi
     command-mode, just involves typing in the number, just as at
     it  does in the vi editor itself. So for example, in vi com-
     mand mode, typing:

       4w2x

     moves the cursor four words to the right, then  deletes  two
     characters.

     You can also bind digit-argument to other key sequences.  If
     these  end  in  a numeric digit, that digit gets appended to
     the current repeat count. If it doesn't  end  in  a  numeric
     digit,  a  new repeat count is started with a value of zero,
     and can be completed by typing in the number, after  letting
     go of the key which triggered the digit-argument action.


THE TECLA CONFIGURATION FILE

     By default, the first call to gl_get_line() looks for a file
     called .teclarc in your home directory (ie. ~/.teclarc).  If
     it finds this file, it reads it, interpreting each  line  as
     defining  a  new  key  binding  or  an editing configuration
     option.  Since  the  emacs  keybindings  are  installed   by
     default, if you want to use the non-default vi editing mode,
     the most important item to go in this file is the  following
     line:

       edit-mode vi

     This will re-configure the default bindings for vi-mode. The
     complete set of arguments that this command accepts are:

       vi     -  Install key-bindings like those of the vi
                 editor.
       emacs  -  Install key-bindings like those of the emacs
                 editor. This is the default.
       none   -  Use just the native line editing facilities
                 provided by the terminal driver.

     To prevent the terminal bell from being rung, such  as  when
     an unrecognized control-sequence is typed, place the follow-
     ing line in the configuration file:

       nobeep

     An example of a key binding line in the  configuration  file
     is the following.

       bind M-[2~ insert-mode

     On many keyboards, the above key sequence is generated  when
     one presses the insert key, so with this keybinding, one can
     toggle between the emacs-mode insert and overwrite modes  by
     hitting  one  key.  One  could  also do it by typing out the
     above sequence of characters one by one. As explained above,
     the M- part of this sequence can be typed either by pressing
     the escape key before the following key, or by pressing  the
     Meta  key at the same time as the following key. Thus if you
     had set the above key binding, and the insert  key  on  your
     keyboard  didn't  generate the above key sequence, you could
     still type it in either of the following 2 ways.

       1. Hit the escape key momentarily, then press '[', then '2', then
          finally '~'.

       2. Press the meta key at the same time as pressing the '[' key,
          then press '2', then '~'.

     If you set a keybinding for a key-sequence that  is  already
     bound  to a function, the new binding overrides the old one.
     If in the new binding you omit the name of the new  function
     to  bind  to  the key-sequence, the original binding becomes
     undefined.

     Starting with versions of libtecla later than  1.3.3  it  is
     now  possible  to bind keysequences that begin with a print-
     able character. Previously key-sequences  were  required  to
     start with a control or meta character.

     Note that the special  keywords  "up",  "down",  "left"  and
     "right" refer to the arrow keys, and are thus not treated as
     keysequences. So, for example, to rebind  the  up  and  down
     arrow  keys  to  use the history search mechanism instead of
     the simple history recall method, you could place  the  fol-
     lowing in your configuration file:

       bind up history-search-backwards
       bind down history-search-backwards

     To unbind an existing binding, you can do this with the bind
     command  by  omitting  to  name any action to rebind the key
     sequence to.  For example, by not specifying an action func-
     tion,  the  following command unbinds the default beginning-
     of-line action from the ^A key sequence:

       bind ^A


ALTERNATE CONFIGURATION SOURCES

     As mentioned above, by default users have the option of con-
     figuring  the  behavior of gl_get_line() via a configuration
     file called .teclarc in their  home  directories.  The  fact
     that  all applications share this same configuration file is
     both an advantage and a disadvantage.  In most cases  it  is
     an  advantage, since it encourages uniformity, and frees the
     user from having to configure each  application  separately.
     In  some  applications, however, this single means of confi-
     guration is a problem. This is particularly true of embedded
     software,  where  there's no filesystem to read a configura-
     tion file from, and also in applications where  a  radically
     different  choice  of  keybindings  is  needed  to emulate a
     legacy keyboard interface.  To cater  for  such  cases,  the
     following  function  allows the application to control where
     configuration information is read from.


       int gl_configure_getline(GetLine *gl,
                                const char *app_string,
                                const char *app_file,
                                const char *user_file);


     It allows the configuration commands that would normally  be
     read  from  a user's ~/.teclarc file, to be read from any or
     none of, a string,  an  application  specific  configuration
     file,  and/or  a  user-specific  configuration file. If this
     function is called before the first call  to  gl_get_line(),
     the default behavior of reading ~/.teclarc on the first call
     to gl_get_line() is disabled, so all configuration  must  be
     achieved using the configuration sources specified with this
     function.

     If app_string != NULL, then it is interpreted  as  a  string
     containing  one  or  more  configuration commands, separated
     from each other in the string by  embedded  newline  charac-
     ters. If app_file != NULL then it is interpreted as the full
     pathname of an application-specific configuration  file.  If
     user_file  !=  NULL then it is interpreted as the full path-
     name  of  a  user-specific  configuration  file,   such   as
     ~/.teclarc. For example, in the following call,

       gl_configure_getline(gl, "edit-mode vi \n nobeep",
                                "/usr/share/myapp/teclarc",
                                "~/.teclarc");

     the app_string argument causes the  calling  application  to
     start  in  vi  edit-mode, instead of the default emacs mode,
     and turns off the use of the terminal bell by  the  library.
     It  then attempts to read system-wide configuration commands
     from an optional file called /usr/share/myapp/teclarc,  then
     finally  reads  user-specific configuration commands from an
     optional .teclarc file in the user's  home  directory.  Note
     that  the  arguments are listed in ascending order of prior-
     ity, with the contents of app_string being potentially over-
     riden  by  commands  in  app_file,  and commands in app_file
     potentially being overriden by commands in user_file.
     You can call this function as  many  times  as  needed,  the
     results  being  cumulative,  but  note  that  copies  of any
     filenames specified via the app_file and user_file arguments
     are  recorded  internally  for  subsequent  use by the read-
     init-files key-binding function, so if you plan to call this
     function  multiple  times, be sure that the last call speci-
     fies the filenames that  you  want  re-read  when  the  user
     requests that the configuration files be re-read.


FILENAME AND TILDE COMPLETION

     With the default key bindings, pressing the  TAB  key  (aka.
     ^I)  results  in  gl_get_line()  attempting  to complete the
     incomplete filename that precedes the cursor.  gl_get_line()
     searches backwards from the cursor, looking for the start of
     the filename, stopping when it hits either a  space  or  the
     start  of  the line. If more than one file has the specified
     prefix, gl_get_line() completes the filename up to the point
     at  which  the ambiguous matches start to differ, then lists
     the possible matches.

     In addition to literally  written  filenames,  gl_get_line()
     can complete files that start with ~/ and ~user/ expressions
     and that contain $envvar expressions. In particular, if  you
     hit    TAB   within   an   incomplete   ~user,   expression,
     gl_get_line() will attempt to complete the username, listing
     any ambiguous matches.

     The   completion   binding   is   implemented   using    the
     cpl_word_completions()  function,  which  is  also available
     separately   to   users   of   this   library.    See    the
     cpl_word_completions(3) man page for more details.


CUSTOMIZED WORD COMPLETION

     If in your application, you would like to have  TAB  comple-
     tion  complete  other  things  in  addition to or instead of
     filenames, you can arrange this by registering an  alternate
     completion   callback   function,   via   a   call   to  the
     gl_customize_completion() function.

       int gl_customize_completion(GetLine *gl, void *data,
                                   CplMatchFn *match_fn);

     The data argument provides a way  for  your  application  to
     pass  arbitrary,  application-specific  information  to  the
     callback function. This is passed to the callback every time
     that it is called. It might for example, point to the symbol
     table from which possible completions are to be sought.  The
     match_fn  argument  specifies  the  callback  function to be
     called.  The  CplMatchFn  function  type   is   defined   in
     libtecla.h, as is a CPL_MATCH_FN() macro that you can use to
     declare and prototype callback  functions.  The  declaration
     and  responsibilities of callback functions are described in
     depth in the cpl_complete_word(3) man page.

     In brief, the callback function is responsible  for  looking
     backwards  in  the  input line, back from the point at which
     the user pressed TAB, to find the start of  the  word  being
     completed.  It then must lookup possible completions of this
     word, and record them  one  by  one  in  the  WordCompletion
     object  that  is passed to it as an argument, by calling the
     cpl_add_completion()  function.  If  the  callback  function
     wishes to provide filename completion in addition to its own
     specific completions, it has the option  of  itself  calling
     the  builtin  file-name  completion  callback. This also, is
     documented in the cpl_complete_word(3) man page.

     Note that if you would  like  gl_get_line()  to  return  the
     current  input  line  when  a  successful completion is been
     made,   you    can    arrange    this    when    you    call
     cpl_add_completion(),  by  making  the last character of the
     continuation suffix a newline character. If you do this, the
     input  line  will  be  updated  to  display  the completion,
     together with any contiuation suffix up to the newline char-
     acter, then gl_get_line() will return this input line.


FILENAME EXPANSION

     With  the  default  key  bindings,   pressing   ^X*   causes
     gl_get_line()  to expand the filename that precedes the cur-
     sor,  replacing  ~/  and   ~user/   expressions   with   the
     corresponding   home   directories,  and  replacing  $envvar
     expressions with the  value  of  the  specified  environment
     variable,  then if there are any wildcards, replacing the so
     far expanded filename with a  space-separated  list  of  the
     files which match the wild cards.

     The   expansion   binding   is   implemented    using    the
     ef_expand_file()  function.   See  the ef_expand_file(3) man
     page for more details.


RECALLING PREVIOUSLY TYPED LINES

     Every time that a new line is entered by  the  user,  it  is
     appended  to  a  list  of  historical input lines maintained
     within the GetLine resource object. You can traverse up  and
     down  this  list  using the up and down arrow keys. Alterna-
     tively, you can do the same with the ^P, and ^N keys, and in
     vi  command mode you can alternatively use the k and j char-
     acters. Thus pressing up-arrow once,  replaces  the  current
     input  line  with  the previously entered line. Pressing up-
     arrow again, replaces this with the line  that  was  entered
     before it, etc.. Having gone back one or more lines into the
     history list, one can return  to  newer  lines  by  pressing
     down-arrow  one  or  more  times.  If you do this sufficient
     times, you will return to the original line  that  you  were
     entering when you first hit up-arrow.

     Note that in vi mode, all of the  history  recall  functions
     switch the library into command mode.

     In emacs mode the M-p and M-n keys work just like the ^P and
     ^N  keys,  except  that  they  skip all but those historical
     lines which share the prefix that precedes the cursor. In vi
     command  mode  the upper case K and J characters do the same
     thing, except that the string that they search for  includes
     the character under the cursor as well as what precedes it.

     Thus for example, suppose that you were in emacs  mode,  and
     you  had  just entered the following list of commands in the
     order shown:

       ls ~/tecla/
       cd ~/tecla
       ls -l getline.c
       emacs ~/tecla/getline.c

     If you next typed:

       ls

     and then hit M-p, then rather than returning the  previously
     typed   emacs   line,   which   doesn't   start  with  "ls",
     gl_get_line() would  recall  the  "ls  -l  getline.c"  line.
     Pressing M-p again would recall the "ls ~/tecla/" line.


HISTORY FILES

     To save the contents of the history buffer  before  quitting
     your  application,  and  subsequently  restore them when you
     next start the application, the following functions are pro-
     vided.


      int gl_save_history(GetLine *gl, const char *filename,
                          const char *comment, int max_lines);
      int gl_load_history(GetLine *gl, const char *filename,
                          const char *comment);


     The filename argument specifies the name to give the history
     file  when  saving, or the name of an existing history file,
     when loading. This may contain home-directory  and  environ-
     ment  variable  expressions,  such  as "~/.myapp_history" or
     "$HOME/.myapp_history".
     Along with each history line, extra  information  about  it,
     such  as when it was entered by the user, and what its nest-
     ing level is, is recorded as a comment preceding the line in
     the  history file. Writing this as a comment allows the his-
     tory file to double as a command file, just in case you wish
     to  replay  a whole session using it. Since comment prefixes
     differ in different languages, the comment argument is  pro-
     vided  for  specifying  the  comment prefix. For example, if
     your application were a  unix  shell,  such  as  the  bourne
     shell,  you  would specify "#" here. Whatever you choose for
     the comment character, you must specify the same  prefix  to
     gl_load_history()    that   you   used   when   you   called
     gl_save_history() to write the history file.

     The max_lines must be either -1 to specify that all lines in
     the history list be saved, or a positive number specifying a
     ceiling on how many of  the  most  recent  lines  should  be
     saved.

     Both fuctions return non-zero on  error,  after  writing  an
     error  message  to  stderr. Note that gl_load_history() does
     not consider the non-existence of a file to be an error.


MULTIPLE HISTORY LISTS

     If your application uses a single GetLine object for  enter-
     ing  many  different  types  of  input  lines,  you may wish
     gl_get_line() to distinguish the different types of lines in
     the  history  list,  and  only  recall  lines that match the
     current  type  of  line.  To   support   this   requirement,
     gl_get_line() marks lines being recorded in the history list
     with an integer identifier chosen by the application.   Ini-
     tially  this identifier is set to 0 by new_GetLine(), but it
     can be changed subsequently by calling gl_group_history().


       int gl_group_history(GetLine *gl, unsigned id);


     The integer identifier id can be any number  chosen  by  the
     application,    but    note   that   gl_save_history()   and
     gl_load_history() preserve the association between  identif-
     iers and historical input lines between program invokations,
     so you should choose fixed  identifiers  for  the  different
     types of input line used by your application.

     Whenever gl_get_line() appends a new input line to the  his-
     tory  list,  the current history identifier is recorded with
     it, and when it is asked to recall a historical input  line,
     it only recalls lines that are marked with the current iden-
     tifier.

DISPLAYING HISTORY

     The   history   list   can   be   displayed    by    calling
     gl_show_history().


       int gl_show_history(GetLine *gl, FILE *fp,
                           const char *fmt,
                           int all_groups,
                           int max_lines);


     This displays the current contents of the  history  list  to
     the  stdio  output  stream  fp. If the max_lines argument is
     greater than or equal to zero, then no more than this number
     of   the  most  recent  lines  will  be  displayed.  If  the
     all_groups argument is  non-zero,  lines  from  all  history
     groups  are displayed. Otherwise just those of the currently
     selected history group  are  displayed.  The  format  string
     argument,  fmt,  determines  how the line is displayed. This
     can contain arbitrary characters which are written verbatim,
     interleaved with any of the following format directives:

       %D  -  The date on which the line was originally
              entered, formatted like 2001-11-20.
       %T  -  The time of day when the line was entered,
              formatted like 23:59:59.
       %N  -  The sequential entry number of the line in
              the history buffer.
       %G  -  The number of the history group which the
              line belongs to.
       %%  -  A literal % character.
       %H  -  The history line itself.

     Thus a format string like "%D %T  %H0 would output something
     like:

       2001-11-20 10:23:34  Hello world

     Note the inclusion of an explicit newline character  in  the
     format string.


LOOKING UP HISTORY

     The gl_lookup_history() function allows the calling applica-
     tion to look up lines in the history list.


       typedef struct {
         const char *line;    /* The requested historical */
                              /*  line. */
         unsigned group;      /* The history group to which */
                              /*  the line belongs. */
         time_t timestamp;    /* The date and time at which */
                              /*  the line was originally */
                              /*  entered. */
       } GlHistoryLine;

       int gl_lookup_history(GetLine *gl, unsigned long id,
                             GlHistoryLine *hline);


     The id argument indicates which line to look up,  where  the
     first  line  that  was  entered  in  the  history list after
     new_GetLine() was called, is denoted by 0, and  subsequently
     entered  lines are denoted with successively higher numbers.
     Note that the range of lines currently preserved in the his-
     tory    list    can    be    queried    by    calling    the
     gl_range_of_history()  function,  described  later.  If  the
     requested  line  is  in the history list, the details of the
     line are recorded in the variable pointed to  by  the  hline
     argument,  and  1  is returned. Otherwise 0 is returned, and
     the variable pointed to by hline is left unchanged.

     Beware that the string returned in hline->line  is  part  of
     the  history  buffer,  so  it  must  not  be modified by the
     caller, and will be recycled on the next call to  any  func-
     tion  that  takes  gl  as its argument. Therefore you should
     make a private copy of this string if you need  to  keep  it
     around.


MISCELLANEOUS HISTORY CONFIGURATION

     If you wish to change the size of the  history  buffer  that
     was  originally  specified in the call to new_GetLine(), you
     can do so with the gl_resize_history() function.


       int gl_resize_history(GetLine *gl, size_t histlen);


     The histlen argument specifies the new size in bytes, and if
     you specify this as 0, the buffer will be deleted.

     As mentioned in the discussion of new_GetLine(), the  number
     of  lines  that can be stored in the history buffer, depends
     on the lengths of the individual lines. For example, a  1000
     byte  buffer  could equally store 10 lines of average length
     100 bytes, or 2 lines of average length 50  bytes.  Although
     the  buffer  is  never  expanded when new lines are added, a
     list of pointers into the  buffer  does  get  expanded  when
     needed to accomodate the number of lines currently stored in
     the buffer. To place an upper limit on the number  of  lines
     in  the  buffer,  and thus a ceiling on the amount of memory
     used in this  list,  you  can  call  the  gl_limit_history()
     function.


       void gl_limit_history(GetLine *gl, int max_lines);


     The max_lines should either  be  a  positive  number  >=  0,
     specifying  an  upper  limit  on  the number of lines in the
     buffer, or be -1 to cancel any previously  specified  limit.
     When  a limit is in effect, only the max_lines most recently
     appended lines are kept in the buffer. Older lines are  dis-
     carded.

     To  discard  lines  from  the  history   buffer,   use   the
     gl_clear_history() function.

       void gl_clear_history(GetLine *gl, int all_groups);

     The all_groups argument tells the function whether to delete
     just  the  lines  associated  with the current history group
     (see gl_group_history()), or all  historical  lines  in  the
     buffer.

     The gl_toggle_history() function allows you to  toggle  his-
     tory  on  and off without losing the current contents of the
     history list.


       void gl_toggle_history(GetLine *gl, int enable);


     Setting the enable argument  to  0  turns  off  the  history
     mechanism,  and  setting it to 1 turns it back on. When his-
     tory is turned off, no new lines will be added to  the  his-
     tory  list,  and  history  lookup  key-bindings  will act as
     though there is nothing in the history buffer.


QUERYING HISTORY INFORMATION

     The configured state of the history list can be queried with
     the gl_history_state() function.


       typedef struct {
         int enabled;     /* True if history is enabled */
         unsigned group;  /* The current history group */
         int max_lines;   /* The current upper limit on the */
                          /*  number of lines in the history */
                          /*  list, or -1 if unlimited. */
       } GlHistoryState;

       void gl_state_of_history(GetLine *gl,
                                GlHistoryState *state);

     On return, the status information is recorded in  the  vari-
     able pointed to by the state argument.

     The gl_range_of_history() function returns  the  number  and
     range of lines in the history list.


     typedef struct {
       unsigned long oldest;  /* The sequential entry number */
                              /*  of the oldest line in the */
                              /*  history list. */
       unsigned long newest;  /* The sequential entry number */
                              /*  of the newest line in the */
                              /*  history list. */
       int nlines;            /* The number of lines in the */
                              /*  history list. */
     } GlHistoryRange;

     void gl_range_of_history(GetLine *gl, GlHistoryRange *range);

     The return values are recorded in the variable pointed to by
     the  range  argument. If the nlines member of this structure
     is greater than zero, then the  oldest  and  newest  members
     report    the    range   of   lines   in   the   list,   and
     newest=oldest+nlines-1.  Otherwise they are both zero.

     The gl_size_of_history() function returns the total size  of
     the  history  buffer  and  the  amount of the buffer that is
     currently occupied.

       typedef struct {
         size_t size;      /* The size of the history buffer */
                           /*  (bytes). */
         size_t used;      /* The number of bytes of the */
                           /*  history buffer that are */
                           /*  currently occupied. */
       } GlHistorySize;

       void gl_size_of_history(GetLine *gl, GlHistorySize *size);

     On return, the size information is recorded in the  variable
     pointed to by the size argument.


CHANGING TERMINALS

     The new_GetLine() constructor function assumes that input is
     to  be  read  from  stdin, and output written to stdout. The
     following function allows you to switch to  different  input
     and output streams.

       int gl_change_terminal(GetLine *gl, FILE *input_fp,
                              FILE *output_fp, const char *term);

     The  gl  argument  is  the  object  that  was  returned   by
     new_GetLine().   The  input_fp argument specifies the stream
     to read from, and output_fp specifies the stream to be writ-
     ten  to.  Only  if  both  of these refer to a terminal, will
     interactive   terminal   input   be   enabled.     Otherwise
     gl_get_line()  will  simply  call  fgets()  to  read command
     input. If both streams refer to a terminal, then  they  must
     refer  to  the  same terminal, and the type of this terminal
     must be specified via the term argument. The  value  of  the
     term argument is looked up in the terminal information data-
     base (terminfo or termcap), in order to determine which spe-
     cial control sequences are needed to control various aspects
     of the  terminal.  new_GetLine()  for  example,  passes  the
     return  value  of getenv("TERM") in this argument. Note that
     if one or both of input_fp and output_fp don't  refer  to  a
     terminal,  then it is legal to pass NULL instead of a termi-
     nal type.

     Note  that  if  you  want  to  pass  file   descriptors   to
     gl_change_terminal(),  you  can  do  this  by creating stdio
     stream wrappers using the POSIX fdopen() function.


EXTERNAL EVENT HANDLING

     While gl_get_line() is waiting for keyboard input  from  the
     user, you can ask it to also watch for activity on arbitrary
     file descriptors, such as network sockets,  pipes  etc,  and
     have  it  call  functions  of your choosing when activity is
     seen. This works on any system that has the select()  system
     call, which is most, if not all flavors of unix. Registering
     a file descriptor to be watched  by  gl_get_line()  involves
     calling the gl_watch_fd() function.


       int gl_watch_fd(GetLine *gl, int fd, GlFdEvent event,
                       GlFdEventFn *callback, void *data);


     If this returns non-zero, then it  means  that  either  your
     arguments are invalid, or that this facility isn't supported
     on the host system.

     The fd argument is the file descriptor to  be  watched.  The
     event  argument  specifies  what  type  of  activity  is  of
     interest, chosen from the following enumerated values:


       GLFD_READ   -  Watch for the arrival of data to be read.
       GLFD_WRITE  -  Watch for the ability to write to the file
                      descriptor without blocking.
       GLFD_URGENT -  Watch for the arrival of urgent
                      out-of-band data on the file descriptor.


     The callback argument is  the  function  to  call  when  the
     selected  activity  is  seen.  It should be defined with the
     following macro, which is defined in libtecla.h.


       #define GL_FD_EVENT_FN(fn) GlFdStatus (fn)(GetLine *gl, \
                                           void *data, int fd, \
                                           GlFdEvent event)

     The data argument of the gl_watch_fd() function is passed to
     the callback function for its own use, and can point to any-
     thing you like, including NULL. The file descriptor and  the
     event argument are also passed to the callback function, and
     this potentially allows the same  callback  function  to  be
     registered  to  more than one type of event and/or more than
     one file descriptor. The return value of the callback  func-
     tion should be one of the following values.


       GLFD_ABORT    -  Tell gl_get_line() to abort with an
                        error (errno won't be set, so set it
                        appropriately yourself if you need it).
       GLFD_REFRESH  -  Redraw the input line then continue
                        waiting for input. Return this if
                        your callback wrote to the terminal.
       GLFD_CONTINUE -  Continue to wait for input, without
                        redrawing the line.

     Note that before calling the callback, gl_get_line()  blocks
     most  signals, and leaves its own signal handlers installed,
     so if you need to catch a particular signal you will need to
     both  temporarily  install  your  own  signal  handler,  and
     unblock the signal. Be sure to re-block the  signal  (if  it
     was  originally  blocked)  and reinstate the original signal
     handler, if any, before returning.

     Your callback shouldn't try to read from the terminal, which
     is  left  in  raw mode as far as input is concerned. You can
     however write to the terminal as usual, since features  like
     conversion  of  newline  to carriage-return/linefeed are re-
     enabled while the callback  is  running.  If  your  callback
     function  does  write  to  the terminal, be sure to output a
     newline  first,  and  when  your  callback   returns,   tell
     gl_get_line()  that  the  input line needs to be redrawn, by
     returning the GLFD_REFRESH status code.

     To remove a callback function that you previously registered
     for   a   given  file  descriptor  and  event,  simply  call
     gl_watch_fd() with the same file descriptor and event  argu-
     ments,  but with a callback argument of 0. The data argument
     is ignored in this case.


SIGNAL HANDLING DEFAULTS

     By default, the gl_get_line() function intercepts  a  number
     of signals. This is particularly important for signals which
     would by default terminate the process, since  the  terminal
     needs  to be restored to a usable state before this happens.
     In this section, the signals that are  trapped  by  default,
     and how gl_get_line() responds to them, is described. Chang-
     ing these defaults is the topic of the following section.

     When  the  following   subset   of   signals   are   caught,
     gl_get_line()  first restores the terminal settings and sig-
     nal handling to  how  they  were  before  gl_get_line()  was
     called,   resends   the   signal,   to   allow  the  calling
     application's signal handlers to handle it, then if the pro-
     cess still exists, gl_get_line() returns NULL and sets errno
     as specified below.


      SIGINT  -  This signal is generated both by the keyboard
                 interrupt key (usually ^C), and the keyboard
                 break key.

                 errno=EINTR

      SIGHUP  -  This signal is generated when the controlling
                 terminal exits.

                 errno=ENOTTY

      SIGPIPE -  This signal is generated when a program attempts
                 to write to a pipe who's remote end isn't being
                 read by any process. This can happen for example
                 if you have called gl_change_terminal() to
                 redirect output to a pipe hidden under a pseudo
                 terminal.

                 errno=EPIPE

      SIGQUIT -  This signal is generated by the keyboard quit
                 key (usually ^\).

                 errno=EINTR

      SIGABRT -  This signal is generated by the standard C,
                 abort() function. By default it both
                 terminates the process and generates a core
                 dump.

                 errno=EINTR

      SIGTERM -  This is the default signal that the UN*X
                 kill command sends to processes.

                 errno=EINTR

     Note that in the case of all of  the  above  signals,  POSIX
     mandates that by default the process is terminated, with the
     addition of a core dump in the case of the  SIGQUIT  signal.
     In  other words, if the calling application doesn't override
     the default handler by supplying  its  own  signal  handler,
     receipt  of  the  corresponding  signal  will  terminate the
     application before gl_get_line() returns.

     If gl_get_line() aborts with errno set  to  EINTR,  you  can
     find out what signal caused it to abort, by calling the fol-
     lowing function.

       int gl_last_signal(const GetLine *gl);

     This returns the numeric code (eg. SIGINT) of the last  sig-
     nal  that  was  received  during  the  most  recent  call to
     gl_get_line(), or -1 if no signals were received.

     On systems that support it, when a SIGWINCH (window  change)
     signal  is  received,  gl_get_line() queries the terminal to
     find out its new size, redraws the  current  input  line  to
     accomodate  the  new  size, then returns to waiting for key-
     board input from the user. Unlike other signals, this signal
     isn't resent to the application.

     Finally, the following signals cause gl_get_line() to  first
     restore  the  terminal  and signal environment to that which
     prevailed before gl_get_line() was called, then  resend  the
     signal to the application. If the process still exists after
     the signal has been delivered, then gl_get_line()  then  re-
     establishes  its  own signal handlers, switches the terminal
     back to raw mode, redisplays the input line, and  goes  back
     to awaiting terminal input from the user.

      SIGCONT    -  This signal is generated when a suspended
                    process is resumed.

      SIGPWR     -  This signal is generated when a power failure
                    occurs (presumably when the system is on a
                    UPS).

      SIGALRM    -  This signal is generated when a timer
                    expires.
      SIGUSR1    -  An application specific signal.

      SIGUSR2    -  Another application specific signal.

      SIGVTALRM  -  This signal is generated when a virtual
                    timer expires (see man setitimer(2)).

      SIGXCPU    -  This signal is generated when a process
                    exceeds its soft CPU time limit.

      SIGTSTP    -  This signal is generated by the terminal
                    suspend key, which is usually ^Z, or the
                    delayed terminal suspend key, which is
                    usually ^Y.

      SIGTTIN    -  This signal is generated if the program
                    attempts to read from the terminal while the
                    program is running in the background.

      SIGTTOU    -  This signal is generated if the program
                    attempts to write to the terminal while the
                    program is running in the background.


     Obviously not all of the above signals are supported on  all
     systems,  so  code to support them is conditionally compiled
     into the tecla library.

     Note that if SIGKILL, which by definition can't  be  caught,
     or  any of the hardware generated exception signals, such as
     SIGSEGV, SIGBUS and SIGFPE, are received and unhandled while
     gl_get_line() has the terminal in raw mode, the program will
     be terminated without the terminal having been restored to a
     usable  state. In practice, job-control shells usually reset
     the terminal settings when a process relinquishes  the  con-
     trolling  terminal,  so  this  is  only a problem with older
     shells.


CUSTOMIZED SIGNAL HANDLING

     The previous section listed the signals  that  gl_get_line()
     traps  by  default,  and  described how it responds to them.
     This section describes how to both add  and  remove  signals
     from  the  list  of  trapped signals, and how to specify how
     gl_get_line() should respond to a given signal.

     If you don't need gl_get_line() to do anything  in  response
     to  a  signal  that  it  normally  traps,  you  can  tell to
     gl_get_line()   to   ignore   that   signal    by    calling
     gl_ignore_signal().

       int gl_ignore_signal(GetLine *gl, int signo);
     The signo argument is the number of the signal (eg.  SIGINT)
     that you want to have ignored. If the specified signal isn't
     currently one of those being  trapped,  this  function  does
     nothing.

     The gl_trap_signal() function allows you to either add a new
     signal  to  the list that gl_get_line() traps, or modify how
     it responds to a signal that it already traps.

       int gl_trap_signal(GetLine *gl, int signo, unsigned flags,
                          GlAfterSignal after, int errno_value);

     The signo argument is the number of the signal that you wish
     to  have trapped. The flags argument is a set of flags which
     determine the environment in which the application's  signal
     handler  is  invoked, the after argument tells gl_get_line()
     what to do after the application's signal  handler  returns,
     and  errno_value tells gl_get_line() what to set errno to if
     told to abort.

     The flags argument is a bitwise OR of zero or  more  of  the
     following enumerators:

       GLS_RESTORE_SIG  -  Restore the caller's signal
                           environment while handling the
                           signal.

       GLS_RESTORE_TTY  -  Restore the caller's terminal settings
                           while handling the signal.

       GLS_RESTORE_LINE -  Move the cursor to the start of the
                           line following the input line before
                           invoking the application's signal
                           handler.

       GLS_REDRAW_LINE  -  Redraw the input line when the
                           application's signal handler returns.

       GLS_UNBLOCK_SIG  -  Normally, if the calling program has
                           a signal blocked (man sigprocmask),
                           gl_get_line() does not trap that
                           signal. This flag tells gl_get_line()
                           to trap the signal and unblock it for
                           the duration of the call to
                           gl_get_line().

       GLS_DONT_FORWARD -  If this flag is included, the signal
                           will not be forwarded to the signal
                           handler of the calling program.

     Two commonly useful flag combinations are also enumerated as
     follows:
       GLS_RESTORE_ENV   = GLS_RESTORE_SIG | GLS_RESTORE_TTY |
                           GLS_REDRAW_LINE

       GLS_SUSPEND_INPUT = GLS_RESTORE_ENV | GLS_RESTORE_LINE


     If your signal handler, or the default system signal handler
     for  this  signal, if you haven't overriden it, never either
     writes to the terminal, nor suspends or terminates the  cal-
     ling  program, then you can safely set the flags argument to
     0.

     If your signal handler always writes to the terminal,  reads
     from  it,  or suspends or terminates the program, you should
     specify the flags argument as GL_SUSPEND_INPUT, so that:

     1. The cursor doesn't get left in the middle of the input
        line.
     2. So that the user can type in input and have it echoed.
     3. So that you don't need to end each output line with
        \r\n, instead of just \n.

     The   GL_RESTORE_ENV   combination   is    the    same    as
     GL_SUSPEND_INPUT,  except  that  it doesn't move the cursor,
     and if your signal handler doesn't read or write anything to
     the terminal, the user won't see any visible indication that
     a signal was caught. This can be useful if you have a signal
     handler that only occasionally writes to the terminal, where
     using GL_SUSPEND_LINE would  cause  the  input  line  to  be
     unnecessarily  duplicated  when  nothing had been written to
     the terminal.  Such a signal handler, when it does write  to
     the  terminal,  should  be  sure  to start a new line at the
     start of its first write,  by  writing  a  new  line  before
     returning.  If the signal arrives while the user is entering
     a line that only occupies a signal terminal line, or if  the
     cursor  is on the last terminal line of a longer input line,
     this will have the same effect as  GL_SUSPEND_INPUT.  Other-
     wise  it  will start writing on a line that already contains
     part of the displayed input line.  This doesn't do any harm,
     but  it  looks a bit ugly, which is why the GL_SUSPEND_INPUT
     combination is better if you know that you are always  going
     to be writting to the terminal.

     The after argument, which determines what gl_get_line() does
     after  the  application's  signal  handler  returns  (if  it
     returns), can take any one of the following values:

       GLS_RETURN   - Return the completed input line, just as
                      though the user had pressed the return
                      key.

       GLS_ABORT    - Cause gl_get_line() to return NULL.
       GLS_CONTINUE - Resume command line editing.

     The errno_value argument is intended to be combined with the
     GLS_ABORT  option,  telling  gl_get_line()  what  to set the
     standard errno variable to before returning NULL to the cal-
     ling  program.  It  can  also,  however,  be  used  with the
     GL_RETURN option, in case you wish to have a way to  distin-
     guish  between  an  input  line  that  was entered using the
     return key, and one that was entered by  the  receipt  of  a
     signal.


THE TERMINAL SIZE

     On most systems the combination of the TIOCGWINSZ ioctl  and
     the  SIGWINCH signal is used to maintain an accurate idea of
     the terminal size. The terminal size is newly queried  every
     time  that  gl_get_line()  is called and whenever a SIGWINCH
     signal is received.

     On the few systems where this mechanism isn't available,  at
     startup  new_GetLine() first looks for the LINES and COLUMNS
     environment variables.  If these aren't found, or they  con-
     tain  unusable  values, then if a terminal information data-
     base like terminfo or termcap is available, the default size
     of  the  terminal is looked up in this database. If this too
     fails to provide the terminal size, a  default  size  of  80
     columns by 24 lines is used. If this default isn't appropri-
     ate for your system, gl_terminal_size() can be used to  sup-
     ply a different fallback.

     The gl_terminal_size() function  allows  you  to  query  the
     current size of the terminal, and install an alternate fall-
     back size for cases where the size isn't available.   Beware
     that  the terminal size won't be available if reading from a
     pipe or a file, so the default values can be important  even
     on  systems that do support ways of finding out the terminal
     size.

       typedef struct {
         int nline;        /* The terminal has nline lines */
         int ncolumn;      /* The terminal has ncolumn columns */
       } GlTerminalSize;

       GlTerminalSize gl_terminal_size(GetLine *gl,
                                       int def_ncolumn,
                                       int def_nline);

     This function first updates gl_get_line()'s idea of the ter-
     minal size, then records its findings in the return value.

     The def_ncolumn and def_nline specify the default number  of
     terminal columns and lines to use if the terminal size can't
     be determined.


HIDING WHAT YOU TYPE

     When entering sensitive information, such as  passwords,  it
     is best not to have the text that you are entering echoed on
     the terminal.  Furthermore, such text should not be recorded
     in  the  history  list, since somebody finding your terminal
     unattended  could  then  recall  it,  or  somebody  snooping
     through  your directories could see it in your history file.
     With this in mind, the gl_echo_mode() function allows you to
     toggle  on and off the display and archival of any text that
     is subsequently entered in calls to gl_get_line().


       int gl_echo_mode(GetLine *gl, int enable);


     The enable argument specifies whether entered text should be
     visible  or not. If it is 0, then subsequently entered lines
     will not be  visible  on  the  terminal,  and  will  not  be
     recorded  in  the  history list. If it is 1, then subsequent
     input lines will be displayed as they are entered, and  pro-
     vided  that  history  hasn't  been  turned off via a call to
     gl_toggle_history(), then they will also be archived in  the
     history  list.  Finally,  if the enable argument is -1, then
     the echoing mode is left  unchanged,  which  allows  you  to
     non-destructively  query  the current setting via the return
     value. In all cases, the return value of the function  is  0
     if  echoing was disabled before the function was called, and
     1 if it was enabled.

     When echoing is turned off, note that although  tab  comple-
     tion will invisibly complete your prefix as far as possible,
     ambiguous completions will not be displayed.


CALLBACK FUNCTION FACILITIES

     Unless otherwise stated, callback  functions,  such  as  tab
     completion callbacks and event callbacks should not call any
     functions in this module. The following functions,  however,
     are designed specifically to be used by callback functions.

     Calling the gl_replace_prompt()  function  from  a  callback
     tells  gl_get_line()  to display a different prompt when the
     callback  returns.  It  has  no  effect   if   called   when
     gl_get_line() is not being called.

       void gl_replace_prompt(GetLine *gl, const char *prompt);



INTERNATIONAL CHARACTER SETS

     Since libtecla version 1.4.0, gl_get_line() has  been  8-bit
     clean.  This means that all 8-bit characters that are print-
     able in the user's current locale are now displayed verbatim
     and  included in the returned input line.  Assuming that the
     calling program correctly contains a call like  the  follow-
     ing,

       setlocale(LC_CTYPE, "");

     then the current locale is determined by the  first  of  the
     environment  variables  LC_CTYPE,  LC_ALL, and LANG, that is
     found to contain a valid locale name. If none of these vari-
     ables  are  defined,  or the program neglects to call setlo-
     cale, then the default C locale is used, which is  US  7-bit
     ASCII.  On  most  unix-like platforms, you can get a list of
     valid locales by typing the command:

       locale -a

     at the shell prompt.


Meta keys and locales

     Beware that in most locales other than the default C locale,
     meta  characters  become  printable,  and  they  are then no
     longer considered to match  M-c  style  key  bindings.  This
     allows  international characters to be entered with the com-
     pose key without unexpectedly triggering meta key  bindings.
     You can still invoke meta bindings, since there are actually
     two ways to do this. For example the binding M-c can also be
     invoked  by pressing the escape key momentarily, then press-
     ing the c key, and this  will  work  regardless  of  locale.
     Moreover,  many  modern  terminal emulators, such as gnome's
     gnome-terminal's and KDE's konsole terminals,  already  gen-
     erate  escape  pairs  like  this  when you use the meta key,
     rather than a real meta character, and other emulators  usu-
     ally  have  a  way to request this behavior, so you can con-
     tinue to use the meta key on most systems.

     For example, although xterm terminal emulators generate real
     8-bit  meta characters by default when you use the meta key,
     they can be configured to output the equivalent escape  pair
     by  setting their EightBitInput X resource to False. You can
     either do this by placing a line like the following in  your
     ~/.Xdefaults file,

       XTerm*EightBitInput: False

     or by starting an xterm with an -xrm '*EightBitInput: False'
     command-line  argument.  In recent versions of xterm you can
     toggle this feature on and off with the "Meta Sends  Escape"
     option in the menu that is displayed when you press the left
     mouse button and the control key within an xterm window.  In
     CDE,  dtterms  can  be  similarly coerced to generate escape
     pairs  in  place  of  meta  characters,   by   setting   the
     Dtterm*KshMode resource to True.


Entering international characters

     If you don't have a  keyboard  that  generates  all  of  the
     international  characters  that you need, there is usually a
     compose key that will allow you to enter special characters,
     or  a  way  to  create  one. For example, under X windows on
     unix-like systems, if your keyboard doesn't have  a  compose
     key, you can designate a redundant key to serve this purpose
     with the xmodmap command. For example, on many PC  keyboards
     there is a microsoft-windows key, which is otherwise useless
     under Linux. On my PC the xev program reports that  pressing
     this  key  generates keycode 115, so to turn this key into a
     compose key, I do the following:

       xmodmap -e 'keycode 115 = Multi_key'

     I can then enter an i with a umlaut over it by  typing  this
     key, followed by ", followed by i.


THREAD SAFETY

     In a multi-threaded program, you should use the libtecla_r.a
     version of the library. This uses reentrant versions of sys-
     tem functions, where available. Unfortunately  neither  ter-
     minfo  nor  termcap  were  designed  to be reentrant, so you
     can't safely use the functions of the getline module in mul-
     tiple  threads  (you can use the separate file-expansion and
     word-completion  modules  in  multiple  threads,   see   the
     corresponding man pages for details). However due to the use
     of POSIX reentrant functions for looking up home directories
     etc, it is safe to use this module from a single thread of a
     multi-threaded program, provided  that  your  other  threads
     don't use any termcap or terminfo functions.


FILES

     libtecla.a      -    The tecla library
     libtecla.h      -    The tecla header file.
     ~/.teclarc      -    The personal tecla customization file.


SEE ALSO

     libtecla(3),    ef_expand_file(3),     cpl_complete_word(3),
     pca_lookup_file(3)


AUTHOR

     Martin Shepherd  (mcs@astro.caltech.edu)


















































xorp/cli/libtecla/html/index.html0000664000076400007640000001057011421137511017146 0ustar greearbgreearbThe tecla command-line editing library.

The Tecla command-line editing library.

The tecla library provides UNIX and LINUX programs with interactive command line editing facilities, similar to those of the UNIX tcsh shell. In addition to simple command-line editing, it supports recall of previously entered command lines, TAB completion of file names or other tokens, and in-line wild-card expansion of filenames. The internal functions which perform file-name completion and wild-card expansion are also available externally for optional use by programs.

In addition, the library includes a path-searching module. This allows an application to provide completion and lookup of files located in UNIX style paths. Although not built into the line editor by default, it can easily be called from custom tab-completion callback functions. This was originally conceived for completing the names of executables and providing a way to look up their locations in the user's PATH environment variable, but it can easily be asked to look up and complete other types of files in any list of directories.

Note that special care has been taken to allow the use of this library in threaded programs. The option to enable this is discussed in the Makefile, and specific discussions of thread safety are presented in the included man pages.

The current version is version 1.4.0. This may be obtained from:

http://www.astro.caltech.edu/~mcs/tecla/libtecla-1.4.0.tar.gz

For the sake of automated scripts, the following URL always points to the latest version. Note that the version number can be found in the README file.

http://www.astro.caltech.edu/~mcs/tecla/libtecla.tar.gz

The library is distributed under a permissive non-copyleft free software license (the X11 license with the name of the copyright holder changed). This is compatible with, but not as restrictive as the GNU GPL.

Release notes

The list of major changes that accompany each new release can be found here.

Modifications

The gory details of changes in the latest and previous versions of the library can be found here.

Library documentation

The following are html versions of the libtecla man pages:

Portability

In principle, the standard version of the library should compile without any problems on any UNIX or UNIX like system. So far it has been reported to work on the following systems:
  Sun Solaris 2.5,2.6,7,8, with any of gcc, Sun C, or g++.
  Mandrake Linux 7.1, gcc
  Red Hat Linux 7,7.1, gcc
  Suse Linux 6.4, gcc
  IBM AIX 4.3.3, gcc
  HP-UX 10.20, HP-UX 11, gcc, c89
  SCO UnixWare 7.1.1
  FreeBSD, gcc
  Alpha OSF1, cc, gcc
There haven't been many reports concerning the POSIX reentrant version, so the absence of any of the above from the following list of systems on which the reentrant version is known to work, shouldn't be taken as an indication that the reentrant version doesn't work.
  Sun Solaris 2.5,2.6,7,8, with any of gcc, Sun C, or g++.
  Mandrake Linux 7.1, gcc
  RedHat Linux 7.0,7.1, gcc
  SuSe Linux 6.4, gcc
  HP-UX 11, gcc
  IBM AIX 4.3.3, gcc
  Alpha OSF1, cc
The only system that is known to have issues with the reentrant version of the library is SCO UnixWare 7.1.1. The problem is in the system provided signal.h, which breaks when POSIX_C_SOURCE is defined. It has been reported that this can be fixed by editing signal.h.

If you compile the library on a system that isn't mentioned above, please send E-mail to mcs@astro.caltech.edu.


Martin Shepherd (5-Jul-2001) xorp/cli/libtecla/html/pca_lookup_file.html0000664000076400007640000003335311421137511021176 0ustar greearbgreearb Manual Page

NAME

     pca_lookup_file,       del_PathCache,       del_PcaPathConf,
     new_PathCache,        new_PcaPathConf,       pca_last_error,
     pca_path_completions,    pca_scan_path,    pca_set_check_fn,
     ppc_file_start,  ppc_literal_escapes  -  lookup  a file in a
     list of directories

SYNOPSIS

     #include <libtecla.h>

     PathCache *new_PathCache(void);

     PathCache *del_PathCache(PathCache *pc);

     int pca_scan_path(PathCache *pc, const char *path);

     void pca_set_check_fn(PathCache *pc, CplCheckFn *check_fn,
                           void *data);

     char *pca_lookup_file(PathCache *pc, const char *name,
                           int name_len, int literal);

     const char *pca_last_error(PathCache *pc);

     CPL_MATCH_FN(pca_path_completions);



DESCRIPTION

     The PathCache object is part of the tecla library  (see  the
     libtecla(3) man page).

     PathCache objects allow an application to search  for  files
     in any colon separated list of directories, such as the unix
     execution  PATH  environment  variable.  Files  in  absolute
     directories  are cached in a PathCache object, whereas rela-
     tive directories are scanned as needed.  Using  a  PathCache
     object,  you  can  look  up  the  full  pathname of a simple
     filename, or you can obtain a list of the  possible  comple-
     tions  of  a  given filename prefix. By default all files in
     the list of directories are targets for lookup  and  comple-
     tion, but a versatile mechanism is provided for only select-
     ing specific types of files. The obvious application of this
     facility  is to provide Tab-completion and lookup of execut-
     able commands in the unix  PATH,  so  an  optional  callback
     which rejects all but executable files, is provided.


AN EXAMPLE

     Under UNIX, the  following  example  program  looks  up  and
     displays  the full pathnames of each of the command names on
     the command line.
       #include <stdio.h>
       #include <stdlib.h>
       #include <libtecla.h>

       int main(int argc, char *argv[])
       {
         int i;
       /*
        * Create a cache for executable files.
        */
         PathCache *pc = new_PathCache();
         if(!pc)
           exit(1);
       /*
        * Scan the user's PATH for executables.
        */
         if(pca_scan_path(pc, getenv("PATH"))) {
           fprintf(stderr, "%s\n", pca_last_error(pc));
           exit(1);
         }
       /*
        * Arrange to only report executable files.
        */
        pca_set_check_fn(pc, cpl_check_exe, NULL);
       /*
        * Lookup and display the full pathname of each of the
        * commands listed on the command line.
        */
         for(i=1; i<argc; i++) {
           char *cmd = pca_lookup_file(pc, argv[i], -1, 0);
           printf("The full pathname of '%s' is %s\n", argv[i],
                  cmd ? cmd : "unknown");
         }
         pc = del_PathCache(pc);  /* Clean up */
         return 0;
       }

     The following is an example of what this does on  my  laptop
     under linux:

       $ ./example less more blob
       The full pathname of 'less' is /usr/bin/less
       The full pathname of 'more' is /bin/more
       The full pathname of 'blob' is unknown
       $


FUNCTION DESCRIPTIONS

     In order to use the facilities  of  this  module,  you  must
     first   allocate   a   PathCache   object   by  calling  the
     new_PathCache() constructor function.

       PathCache *new_PathCache(void)

     This function creates the  resources  needed  to  cache  and
     lookup  files  in  a list of directories. It returns NULL on
     error.


POPULATING THE CACHE

     Once you have created a cache, it needs to be populated with
     files.  To do this, call the pca_scan_path() function.

       int pca_scan_path(PathCache *pc, const char *path);

     Whenever this function is called, it  discards  the  current
     contents  of  the  cache, then scans the list of directories
     specified in its path argument for files. The path  argument
     must be a string containing a colon-separated list of direc-
     tories, such as "/usr/bin:/home/mcs/bin:.". This can include
     directories   specified   by   absolute  pathnames  such  as
     "/usr/bin", as well as sub-directories specified by relative
     pathnames such as "." or "bin". Files in the absolute direc-
     tories are immediately cached  in  the  specified  PathCache
     object,  whereas sub-directories, whose identities obviously
     change whenever the current working  directory  is  changed,
     are  marked  to  be  scanned  on  the fly whenever a file is
     looked up.

     On success this function return 0. On error  it  returns  1,
     and  a  description  of the error can be obtained by calling
     pca_last_error(pc).


LOOKING UP FILES

     Once the cache has been populated with files, you  can  look
     up  the  full  pathname  of a file, simply by specifying its
     filename to pca_lookup_file().

       char *pca_lookup_file(PathCache *pc, const char *name,
                             int name_len, int literal);

     To make it possible to pass this function a  filename  which
     is  actually  part of a longer string, the name_len argument
     can be used to specify the length of  the  filename  at  the
     start  of  the  name[]  argument.  If  you  pass -1 for this
     length, the length of the string  will  be  determined  with
     strlen().  If  the  name[]  string might contain backslashes
     that escape the special meanings of spaces and  tabs  within
     the filename, give the literal argument, the value 0. Other-
     wise, if backslashes should be treated as normal characters,
     pass 1 for the value of the literal argument.


FILENAME COMPLETION

     Looking up the potential completions of a filename-prefix in
     the  filename  cache,  is  achieved  by passing the provided
     pca_path_completions()    callback    function    to     the
     cpl_complete_word()  function  (see the cpl_complete_word(3)
     man page).

       CPL_MATCH_FN(pca_path_completions);

     This callback requires that its data argument be  a  pointer
     to  a PcaPathConf object. Configuration objects of this type
     are allocated by calling new_PcaPathConf().

       PcaPathConf *new_PcaPathConf(PathCache *pc);

     This function returns an  object  initialized  with  default
     configuration    parameters,   which   determine   how   the
     cpl_path_completions() callback function behaves. The  func-
     tions  which  allow you to individually change these parame-
     ters are discussed below.

     By default,  the  pca_path_completions()  callback  function
     searches  backwards for the start of the filename being com-
     pleted, looking for the first un-escaped space or the  start
     of  the input line. If you wish to specify a different loca-
     tion, call ppc_file_start() with  the  index  at  which  the
     filename  starts  in  the input line. Passing start_index=-1
     re-enables the default behavior.

       void ppc_file_start(PcaPathConf *ppc, int start_index);

     By default, when pca_path_completions() looks at a  filename
     in  the input line, each lone backslash in the input line is
     interpreted as being a special character which  removes  any
     special significance of the character which follows it, such
     as a space which should be taken as  part  of  the  filename
     rather  than  delimiting  the  start  of the filename. These
     backslashes are thus ignored while looking for  completions,
     and  subsequently  added  before  spaces,  tabs  and literal
     backslashes in the list of completions.  To  have  unescaped
     backslashes    treated    as    normal    characters,   call
     ppc_literal_escapes() with a non-zero value in  its  literal
     argument.

       void ppc_literal_escapes(PcaPathConf *ppc, int literal);

     When you have finished with a PcaPathConf variable, you  can
     pass  it  to  the  del_PcaPathConf()  destructor function to
     reclaim its memory.

       PcaPathConf *del_PcaPathConf(PcaPathConf *ppc);

BEING SELECTIVE

     If you are only interested in certain types or  files,  such
     as,  for example, executable files, or files whose names end
     in a particular suffix, you can arrange for the file comple-
     tion  and  lookup functions to be selective in the filenames
     that they return.  This is done by  registering  a  callback
     function  with your PathCache object. Thereafter, whenever a
     filename is found which  either  matches  a  filename  being
     looked  up,  or  matches  a prefix which is being completed,
     your callback function will be called with the full pathname
     of  the  file,  plus  any application-specific data that you
     provide, and if the callback returns 1 the filename will  be
     reported  as  a  match,  and  if  it  returns  0, it will be
     ignored.  Suitable callback functions and  their  prototypes
     should  be declared with the following macro. The CplCheckFn
     typedef is  also  provided  in  case  you  wish  to  declare
     pointers to such functions.

       #define CPL_CHECK_FN(fn) int (fn)(void *data, \
                                         const char *pathname)
       typedef CPL_CHECK_FN(CplCheckFn);

     Registering one of  these  functions  involves  calling  the
     pca_set_check_fn()  function.  In  addition  to the callback
     function, passed via the check_fn argument, you can  pass  a
     pointer to anything via the data argument. This pointer will
     be passed on to your callback function,  via  its  own  data
     argument,  whenever  it is called, so this provides a way to
     pass appplication specific data to your callback.

       void pca_set_check_fn(PathCache *pc, CplCheckFn *check_fn,
                             void *data);

     Note that these callbacks are passed the  full  pathname  of
     each  matching file, so the decision about whether a file is
     of interest can be based on any property of  the  file,  not
     just   its   filename.   As   an   example,   the   provided
     cpl_check_exe() callback function looks  at  the  executable
     permissions  of  the  file and the permissions of its parent
     directories, and only returns 1 if the user has execute per-
     mission to the file. This callback function can thus be used
     to lookup or complete command names found in the directories
     listed  in the user's PATH environment variable. The example
     program given earlier in this man page provides a demonstra-
     tion of this.

     Beware that if somebody tries to complete an  empty  string,
     your  callback  will  get  called once for every file in the
     cache, which could number in the thousands. If your callback
     does  anything time consuming, this could result in an unac-
     ceptable delay for the user, so  callbacks  should  be  kept
     short.
     To improve performance, whenever one of these  callbacks  is
     called,  the  choice  that  it makes is cached, and the next
     time the corresponding file is looked up, instead of calling
     the  callback  again,  the  cached  record of whether it was
     accepted or rejected is used. Thus if somebody tries to com-
     plete an empty string, and hits tab a second time when noth-
     ing appears to happen, there will only be  one  long  delay,
     since  the second pass will operate entirely from the cached
     dispositions of the files. These cached dipositions are dis-
     carded  whenever  pca_scan_path()  is  called,  and whenever
     pca_set_check_fn() is called with changed callback  function
     or data arguments.


ERROR HANDLING

     If pca_scan_path() reports that an error occurred by return-
     ing  1,  you  can obtain a terse description of the error by
     calling pca_last_error(pc). This returns an internal  string
     containing an error message.

       const char *pca_last_error(PathCache *pc);



CLEANING UP

     Once you have finished using a  PathCache  object,  you  can
     reclaim  its  resources by passing it to the del_PathCache()
     destructor function. This takes a pointer to  one  of  these
     objects, and always returns NULL.

       PathCache *del_PathCache(PathCache *pc);


THREAD SAFETY

     In multi-threaded programs, you should use the  libtecla_r.a
     version  of the library. This uses POSIX reentrant functions
     where available (hence the _r suffix), and disables features
     that  rely on non-reentrant system functions. In the case of
     this module, the only disabled feature is  username  comple-
     tion in ~username/ expressions, in cpl_path_completions().

     Using the libtecla_r.a version of the library, it is safe to
     use  the facilities of this module in multiple threads, pro-
     vided that each thread uses a separately allocated PathCache
     object.  In  other  words,  if  two  threads want to do path
     searching, they should each call new_PathCache() to allocate
     their own caches.


FILES

     libtecla.a    -    The tecla library
     libtecla.h    -    The tecla header file.

SEE ALSO

     libtecla(3),       gl_get_line(3),        ef_expand_file(3),
     cpl_complete_word(3)


AUTHOR

     Martin Shepherd  (mcs@astro.caltech.edu)













































xorp/cli/libtecla/html/release.html0000664000076400007640000004065311421137511017464 0ustar greearbgreearbThe tecla library release notes
This file lists major changes which accompany each new release.

Version 1.4.0:

  The contents of the history list can now be saved and restored with
  the new gl_save_history() and gl_load_history() functions.

  Event handlers can now be registered to watch for and respond to I/O
  on arbitrary file descriptors while gl_get_line() is waiting for
  terminal input from the user. See the gl_get_line(3) man page
  for details on gl_watch_fd().

  As an optional alternative to getting configuration information only
  from ~/.teclarc, the new gl_configure_getline() function allows
  configuration commands to be taken from any of, a string, a
  specified application-specific file, and/or a specified
  user-specific file. See the gl_get_line(3) man page for details.

  The version number of the library can now be queried using the
  libtecla_version() function. See the libtecla(3) man page.

  The new gl_group_history() function allows applications to group
  different types of input line in the history buffer, and arrange for
  only members of the appropriate group to be recalled on a given call
  to gl_get_line(). See the gl_get_line(3) man page.

  The new gl_show_history() function displays the current history list
  to a given stdio output stream. See the gl_get_line(3) man page.

  new_GetLine() now allows you to specify a history buffer size of
  zero, thus requesting that no history buffer be allocated. You can
  subsequently resize or delete the history buffer at any time, by
  calling gl_resize_history(), limit the number of lines that are
  allowed in the buffer by calling gl_limit_history(), clear either
  all history lines from the history list, or just the history lines
  that are associated with the current history group, by calling
  gl_clear_history, and toggle the history mechanism on and off by
  calling gl_toggle_history().

  The new gl_terminal_size() function can be used to query the
  current terminal size. It can also be used to supply a default
  terminal size on systems where no mechanism is available for
  looking up the size.

  The contents and configuration of the history list can now be
  obtained by the calling application, by calling the new
  gl_lookup_history(), gl_state_of_history(), gl_range_of_history()
  and gl_size_of_history() functions. See the gl_get_line(3) man page.

  Echoing of the input line as it is typed, can now be turned on and
  off via the new gl_echo_mode() function. While echoing is disabled,
  newly entered input lines are omitted from the history list.  See
  the gl_get_line(3) man page.

  While the default remains to display the prompt string literally,
  the new gl_prompt_style() function can be used to enable text
  attribute formatting directives in prompt strings, such as
  underlining, bold font, and highlighting directives.

  Signal handling in gl_get_line() is now customizable. The default
  signal handling behavior remains essentially the same, except that
  the SIGTSTP, SIGTTIN and SIGTTOU are now forwarded to the
  corresponding signal handler of the calling program, instead of
  causing a SIGSTOP to be sent to the application.  It is now possible
  to remove signals from the list that are trapped by gl_get_line(),
  as well as add new signals to this list. The signal and terminal
  environments in which the signal handler of the calling program is
  invoked, and what gl_get_line() does after the signal handler
  returns, is now customizable on a per signal basis. You can now also
  query the last signal that was caught by gl_get_line(). This is
  useful when gl_get_line() aborts with errno=EINTR, and you need to
  know which signal caused it to abort.

  Key-sequences bound to action functions can now start with printable
  characters. Previously only keysequences starting with control or
  meta characters were permitted.

  gl_get_line() is now 8-bit clean. If the calling program has
  correctly called setlocale(LC_CTYPE,""), then the user can select an
  alternate locale by setting the standard LC_CTYPE, LC_ALL, or LANG
  environment variables, and international characters can then be
  entered directly, either by using a non-US keyboard, or by using a
  compose key on a standard US keyboard. Note that in locales in which
  meta characters become printable, meta characters no longer match
  M-c bindings, which then have to be entered using their escape-c
  equivalents.  Fortunately most modern terminal emulators either
  output the escape-c version by default when the meta key is used, or
  can be configured to do so (see the gl_get_line(3) man page), so in
  most cases you can continue to use the meta key.

  Completion callback functions can now tell gl_get_line() to return
  the input line immediately after a successful tab completion, simply
  by setting the last character of the optional continuation suffix to
  a newline character (ie. in the call to cpl_add_completion()).

  It is now safe to create and use multiple GetLine objects, albeit
  still only from a single thread. In conjunction with the new
  gl_configure_getline() function, this optionally allows multiple
  GetLine objects with different bindings to be used to implement
  different input modes.

  The edit-mode configuration command now accepts the argument,
  none. This tells gl_get_line() to revert to using just the native
  line editing facilities provided by the terminal driver. This could
  be used if the termcap or terminfo entry of the host terminal were
  badly corrupted.

  Application callback functions invoked by gl_get_line() can now
  change the displayed prompt using the gl_replace_prompt() function.

  Their is now an optional program distributed with the library. This
  is a beta release of a program which adds tecla command-line editing
  to virtually any third party application without the application
  needing to be linked to the library. See the enhance(3) man page for
  further details. Although built and installed by default, the
  INSTALL document explains how to prevent this.

  The INSTALL document now explains how you can stop the demo programs
  from being built and installed.

  NetBSD/termcap fixes. Mike MacFaden reported two problems that he
  saw when compiling libtecla under NetBSD. Both cases were related to
  the use of termcap.  Most systems use terminfo, so this problem has
  gone unnoticed until now, and won't have affected the grand majority
  of users.  The configure script had a bug which prevented the check
  for CPP working properly, and getline.c wouldn't compile due to an
  undeclared variable when USE_TERMCAP was defined. Both problems have
  now been fixed. Note that if you successfully compiled version
  1.3.3, this problem didn't affect you.

  An unfortunate and undocumented binding of the key-sequence M-O was
  shadowing the arrow-key bindings on systems that use ^[OA etc. I
  have removed this binding (the documented lower case M-o binding
  remains bound). Under the KDE konsole terminal this was causing the
  arrow keys to do something other than expected.

  There was a bug in the history list code which could result in
  strange entries appearing at the start of the history list once
  enough history lines had been added to the list to cause the
  circular history buffer to wrap. This is now fixed.
 
Version 1.3.3:

  Signal handling has been re-written, and documentation of its
  behaviour has been added to the gl_get_line(3) man page. In addition
  to eliminating race conditions, and appropriately setting errno for
  those signals that abort gl_get_line(), many more signals are now
  intercepted, making it less likely that the terminal will be left in
  raw mode by a signal that isn't trapped by gl_get_line().

  A bug was also fixed that was leaving the terminal in raw mode if
  the editing mode was changed interactively between vi and emacs.
  This was only noticeable when running programs from old shells that
  don't reset terminal modes.

Version 1.3.2:

  Tim Eliseo contributed a number of improvements to vi mode,
  including a fuller set of vi key-bindings, implementation of the vi
  constraint that the cursor can't backup past the point at which
  input mode was entered, and restoration of overwritten characters
  when backspacing in overwrite mode. There are also now new bindings
  to allow users to toggle between vi and emacs modes interactively.
  The terminal bell is now used in some circumstances, such as when an
  unrecognized key sequence is entered. This can be turned off by the
  new nobeep option in the tecla configuration file.

  Unrelated to the above, a problem under Linux which prevented ^Q
  from being used to resume terminal output after the user had pressed
  ^S, has been fixed.

Version 1.3.1:

  In vi mode a bug was preventing the history-search-backward and
  history-search-forward actions from doing anything when invoked on
  empty lines. On empty lines they now act like up-history and
  down-history respectively, as in emacs mode.

  When creating shared libraries under Linux, the -soname directive
  was being used incorrectly. The result is that Linux binaries linked
  with the 1.2.3, 1.2.4 and 1.3.0 versions of the tecla shared
  libraries, will refuse to see other versions of the shared library
  until relinked with version 1.3.1 or higher.

  The configure script can now handle the fact that under Solaris-2.6
  and earlier, the only curses library is a static one that hides in
  /usr/ccs/lib. Under Linux it now also caters for old versions of GNU
  ld which don't accept version scripts.

  The demos are now linked against the shared version of the library
  if possible. Previously they were always linked with the static
  version.

Version 1.3.0:

  The major change in this release is the addition of an optional vi
  command-line editing mode in gl_get_line(), along with lots of new
  action functions to support its bindings. To enable this, first
  create a ~/.teclarc file if you don't already have one, then add the
  following line to it.

   edit-mode vi

  The default vi bindings, which are designed to mimic those of the vi
  editor as closely as possible, are described in the gl_get_line(3)
  man page.

  A new convenience function called ef_list_expansions() has been
  added for listing filename expansions. See the ef_list_expansions(3)
  man page for details. This is used in a new list-glob binding, bound
  to ^Xg in emacs mode, and ^G in vi input mode.

  A bug has been fixed in the key-binding table expansion code. This
  bug would have caused problems to anybody who defined more than
  about 18 personalized key-bindings in their ~/.teclarc file.

Version 1.2.4:

  Buffered I/O is now used for writing to terminals, and where
  supported, cursor motion is done with move-n-positions terminfo
  capabilities instead of doing lots of move-1-position requests. This
  greatly improves how the library feels over slow links.

  You can now optionally compile different architectures in different
  directories, without having to make multiple copies of the
  distribution. This is documented in the INSTALL file.

  The ksh ~+ directive is now supported.

  Thanks to Markus Gyger for the above improvements.

  Documentation has been added to the INSTALL file describing features
  designed to facilitate configuration and installation of the library
  as part of larger packages. These features are intended to remove
  the need to modify the tecla distribution's configuration and build
  procedures when embedding the libtecla distribution in other package
  distributions.

  A previous fix to stop the cursor from warping when the last
  character of the input line was in the last column of the terminal,
  was only being used for the first terminal line of the input line.
  It is now used for all subsequent lines as well, as originally
  intended.
  
Version 1.2.3:

  The installation procedure has been better automated with the
  addition of an autoconf configure script. This means that installers
  can now compile and install the library by typing:

    ./configure
    make
    make install

  On all systems this makes at least the normal static version of the
  tecla library. It also makes the reentrant version if reentrant
  POSIX functions are detected.  Under Solaris, Linux and HP-UX the
  configuration script arranges for shared libraries to be compiled in
  addition to the static libraries.  It is hoped that installers will
  return information about how to compile shared libraries on other
  systems, for inclusion in future releases, and to this end, a new
  PORTING guide has been provided.

  The versioning number scheme has been changed. This release would
  have been 1.2c, but instead will be refered to as 1.2.3. The
  versioning scheme, based on conventions used by Sun Microsystems, is
  described in configure.in.

  The library was also tested under HP-UX, and this revealed two
  serious bugs, both of which have now been fixed.
  
  The first bug prevented the library from writing control codes to
  terminals on big-endian machines, with the exception of those
  running under Solaris. This was due to an int variable being used
  where a char was needed.

  The second bug had the symptom that on systems that don't use the
  newline character as the control code for moving the cursor down a
  line, a newline wasn't started when the user hit enter.

Version 1.2b:

  Two more minor bug fixes:

  Many terminals don't wrap the cursor to the next line when a
  character is written to the rightmost terminal column. Instead, they
  delay starting a new line until one more character is written, at
  which point they move the cursor two positions.  gl_get_line()
  wasn't aware of this, so cursor repositionings just after writing
  the last character of a column, caused it to erroneously go up a
  line. This has now been remedied, using a method that should work
  regardless of whether a terminal exhibits this behavior or not.

  Some systems dynamically record the current terminal dimensions in
  environment variables called LINES and COLUMNS. On such systems,
  during the initial terminal setup, these values should override the
  static values read from the terminal information databases, and now
  do.  Previously they were only used if the dimensions returned by
  terminfo/termcap looked bogus.

Version 1.2a:

  This minor release fixes the following two bugs:

  The initial terminal size and subsequent changes thereto, weren't
  being noticed by gl_get_line(). This was because the test for the
  existence of TIOCWINSZ was erroneously placed before the inclusion
  of termios.h. One of the results was that on input lines that
  spanned more than one terminal line, the cursor occasionally jumped
  unexpectedly to the previous terminal line.

  On entering a line that wrapped over multiple terminal lines,
  gl_get_line() simply output a carriage-return line-feed at the point
  at which the user pressed return. Thus if one typed in such a line,
  then moved back onto one of the earlier terminal lines before
  hitting return, the cursor was left on a line containing part of the
  line that had just been entered. This didn't do any harm, but it
  looked a mess.

Version 1.2:

  A new facility for looking up and completing filenames in UNIX-style
  paths has now been added (eg. you can search for, or complete
  commands using the UNIX PATH environment variable). See the
  pca_lookup_file(3) man page.

  The already existing filename completion callback can now be made
  selective in what types of files it lists. See the
  cpl_complete_word(3) man page.

  Due to its potential to break applications when changed, the use of
  the publically defined CplFileArgs structure to configure the
  cpl_file_completions() callback is now deprecated.  The definition
  of this structure has been frozen, and its documentation has been
  removed from the man pages.  It will remain supported, but if you
  have used it, you are recommended to switch to the new method, which
  involves a new opaque configuration object, allocated via a provided
  constructor function, configured via accessor functions, and
  eventually deleted with a provided destructor function. The
  cpl_file_completions() callback distinguishes which structure type
  it has been sent by virtue of a code placed at the start of the new
  structure by the constructor.  It is assumed that no existing
  applications set the boolean 'escaped' member of the CplFileArgs
  structure to 4568.  The new method is documented in the
  cpl_complete_word(3) man page.

Version 1.1j

  This was the initial public release on freshmeat.org.
xorp/cli/libtecla/html/enhance.html0000664000076400007640000000551711421137511017445 0ustar greearbgreearb Manual Page

NAME

     enhance - A program that adds command-line editing to  third
     party programs.

SYNOPSIS

     enhance command [ argument ... ]


DESCRIPTION

     The enhance program provides enhanced  command-line  editing
     facilities  to  users  of third party applications, to which
     one doesn't have any source code. It does this by placing  a
     pseudo-terminal  between the application and the real termi-
     nal. It uses the tecla command-line editing library to  read
     input  from  the real terminal, then forwards each just com-
     pleted  input  line  to  the  application  via  the  pseudo-
     terminal.  All output from the application is forwarded back
     unchanged to the real terminal.

     Whenever the application stops generating  output  for  more
     than  a  tenth  of  a second, the enhance program treats the
     latest incomplete output line as the prompt, and  redisplays
     any incompleted input line that the user has typed after it.
     Note that the small delay, which  is  imperceptible  to  the
     user,  isn't necessary for correct operation of the program.
     It is just an optimization, designed to stop the input  line
     from being redisplayed so often that it slows down output.


DEFICIENCIES

     The one major problem that hasn't been solved yet, is how to
     deal  with  applications  that change whether typed input is
     echo'd by their controlling terminal. For example,  programs
     that ask for a password, such as ftp and telnet, temporarily
     tell their controlling terminal not to echo  what  the  user
     types.  Since  this  request goes to the application side of
     the psuedo terminal, the enhance program has no way of know-
     ing  that  this  has  happened,  and continues to echo typed
     input to its controlling  terminal,  while  the  user  types
     their password.

     Furthermore, before  executing  the  host  application,  the
     enhance program initially sets the pseudo terminal to noecho
     mode, so that  everything  that  it  sends  to  the  program
     doesn't  get  redundantly echoed. If a program that switches
     to  noecho  mode  explicitly  restores  echoing  afterwards,
     rather  than  restoring  the terminal modes that were previ-
     ously in force, then subsequently, every time that you enter
     a  new input line, a duplicate copy will be displayed on the
     next line.


FILES

     libtecla.a    -   The tecla library.
     ~/.teclarc    -   The tecla personal customization file.


SEE ALSO

     libtecla(3)


AUTHOR

     Martin Shepherd  (mcs@astro.caltech.edu)









































xorp/cli/libtecla/html/changes.html0000664000076400007640000021240311421137511017446 0ustar greearbgreearbThe tecla library change log
In the following log, modification dates are listed using the European
convention in which the day comes before the month (ie. DD/MM/YYYY).
The most recent modifications are listed first.

10/12/2001 mcs@astro.caltech.edu
           getline.c
             If the TIOCGWINSZ ioctl doesn't work, as is the case when
             running in an emacs shell, leave the size unchanged, rather
             than returning a fatal error.

07/12/2001 mcs@astro.caltech.edu
           configure.in configure
             Now that the configure version of CFLAGS is included in
             the makefile, I noticed that the optimization flags -g
             and -O2 had been added. It turns out that if CFLAGS isn't
             already set, the autoconf AC_PROG_CC macro initializes it
             with these two optimization flags. Since this would break
             backwards compatibility in embedded distributions that
             already use the OPT= makefile argument, and because
             turning debugging on needlessly bloats the library, I now
             make sure that CFLAGS is set before calling this macro.

07/12/2001 mcs@astro.caltech.edu
           enhance.c
             Use argv[0] in error reports instead of using a
             hardcoded macro.

07/12/2001 mcs@astro.caltech.edu
           getline.c
             The cut buffer wasn't being cleared after being
             used as a work buffer by gl_load_history().

06/12/2001 mcs@astro.caltech.edu
           configure.in configure
             I removed my now redundant definition of SUN_TPUTS from
             CFLAGS. I also added "-I/usr/include" to CFLAGS under
             Solaris to prevent gcc from seeing conflicting versions
             of system header files in /usr/local/include.

06/12/2001 Markus Gyger (logged here by mcs)
           Lots of files.
             Lots of corrections to misspellings and typos in the
             comments.
           getline.c
             Markus reverted a supposed fix that I added a day or two
             ago. I had incorrectly thought that in Solaris 8, Sun had
             finally brought their declaration of the callback
             function of tputs() into line with other systems, but it
             turned out that gcc was pulling in a GNU version of
             term.h from /usr/local/include, and this was what
             confused me.

05/12/2001 mcs@astro.caltech.edu
           Makefile.in
             I added @CFLAGS@ to the CFLAGS assignment, so that
             if CFLAGS is set as an environment variable when
             configure is run, the corresponding make variable
             includes its values in the output makefile.

05/12/2001 mcs@astro.caltech.edu
           getline.c libtecla.h libtecla.map man3/gl_get_line.3
           man3/gl_last_signal.3
             I added a function that programs can use to find out
             which signal caused gl_get_line() to return EINTR.

05/12/2001 mcs@astro.caltech.edu
           getline.c
             When the newline action was triggered by a printable
             character, it failed to display that character. It now
             does. Also, extra control codes that I had added, to
             clear to the end of the display after the carriage return,
             but before displaying the prompt, were confusing expect
             scripts, so I have removed them. This step is now done
             instead in gl_redisplay() after displaying the full input
             line.

05/12/2001 mcs@astro.caltech.edu
           getline.c man3/gl_get_line.3
             A user convinced me that continuing to invoke meta
             keybindings for meta characters that are printable is a
             bad idea, as is allowing users to ask to have setlocale()
             called behind the application's back. I have thus changed
             this. The setlocale configuration option has gone, and
             gl_get_line() is now completely 8-bit clean, by default.
             This means that if a meta character is printable, it is
             treated as a literal character, rather than a potential
             M-c binding.  Meta bindings can still be invoked via
             their Esc-c equivalents, and indeed most terminal
             emulators either output such escape pairs by default when
             the meta character is pressed, or can be configured to do
             so. I have documented how to configure xterm to do this,
             in the man page.

03/12/2001 mcs@astro.caltech.edu
           getline.c man3/gl_get_line.3
             gl_get_line() by default now prints any 8-bit printable
             characters that don't match keybindings. Previously
             characters > 127 were only printed if preceded by the
             literal-next action.  Alternatively, by placing the
             command literal_if_printable in the tecla configuration
             file, all printable characters are treated as literal
             characters, even if they are bound to action functions.

             For international users of programs written by
             programmers that weren't aware of the need to call
             setlocale() to support alternate character sets, the
             configuration file can now also contain the single-word
             command "setlocale", which tells gl_get_line() to remedy
             this.

27/11/2001 mcs@astro.caltech.edu
           demo.c demo2.c enhance man3/gl_get_line.3
             All demos and programs now call setlocale(LC_CTYPE,"").
             This makes them support character sets of different
             locales, where specified with the LC_CTYPE, LC_ALL, or
             LANG environment variables. I also added this to the demo
             in the man page, and documented its effect.

27/11/2001 mcs@astro.caltech.edu
           getline.c
             When displaying unsigned characters with values over
             127 literally, previously it was assumed that they would
             all be displayable. Now isprint() is consulted, and if it
             says that a character isn't printable, the character code
             is displayed in octal like \307. In non-C locales, some
             characters with values > 127 are displayable, and
             isprint() tells gl_get_line() which are and which aren't.

27/11/2001 mcs@astro.caltech.edu
           getline.c pathutil.c history.c enhance.c demo2.c
             All arguments of the ctype.h character class functions
             are now cast to (int)(unsigned char). Previously they
             were cast to (int), which doesn't correctly conform to
             the requirements of the C standard, and could cause
             problems for characters with values > 127 on systems
             with signed char's.

26/11/2001 mcs@astro.caltech.edu
           man3/enhance.3 man3/libtecla.3
             I started writing a man page for the enhance program.

26/11/2001 mcs@astro.caltech.edu
           Makefile.in Makefile.rules INSTALL
             It is now possible to specify whether the demos and other
             programs are to be built, by overriding the default
             values of the DEMOS, PROGRAMS and PROGRAMS_R variables.
             I have also documented the BINDIR variable and the
             install_bin makefile target.

22/11/2001 mcs@astro.caltech.edu
           getline.c libtecla.h libtecla.map man3/gl_get_line.3
           man3/gl_ignore_signal.3 man3/gl_trap_signal.3
             Signal handling has now been modified to be customizable.
             Signals that are trapped by default can be removed from
             the list of trapped signals, and signals that aren't
             currently trapped, can be added to the list. Applications
             can also specify the signal and terminal environments in
             which an application's signal handler is invoked, and
             what gl_get_line() does after the signal handler returns.

13/11/2001 mcs@astro.caltech.edu
           getline.c man3/gl_get_line.3
             Added half-bright, reverse-video and blinking text to the
             available prompt formatting options.
           getline.c
             Removed ^O from the default VT100 sgr0 capability
             string.  Apparently it can cause problems with some
             terminal emulators, and we don't need it, since it turns
             off the alternative character set mode, which we don't
             use.
           getline.c
             gl_tigetstr() and gl_tgetstr() didn't guard against the
             error returns of tigetstr() and tgetstr() respectively.
             They now do.

11/11/2001 mcs@astro.caltech.edu
           getline.c libtecla.h libtecla.map man3/gl_get_line.3
           man3/gl_prompt_style.3
             Although the default remains to display the prompt string
             literally, the new gl_prompt_style() function can be used
             to enable text attribute formatting directives in prompt
             strings, such as underlining, bold font, and highlighting
             directives.

09/11/2001 mcs@astro.caltech.edu
           enhance.c Makefile.rules configure.in configure
             I added a new program to the distribution that allows one
             to run most third party programs with the tecla library
             providing command-line editing.

08/11/2001 mcs@astro.caltech.edu
           libtecla.h getline.c man3/gl_get_line.3 history.c history.h
             I added a max_lines argument to gl_show_history() and
             _glh_show_history(). This can optionally be used to
             set a limit on the number of history lines displayed.
           libtecla.h getline.c man3/gl_get_line.3
             I added a new function called gl_replace_prompt(). This
             can be used by gl_get_line() callback functions to
             request that a new prompt be use when they return.

06/11/2001 mcs@astro.caltech.edu
           getline.c man3/gl_get_line.3
             I implemented, bound and documented the list-history
             action, used for listing historical lines of the current
             history group.
           getline.c man3/gl_get_line.3 man3/gl_echo_mode.3
             I wrote functions to specify and query whether subsequent
             lines will be visible as they are being typed.

28/10/2001 mcs@astro.caltech.edu
           getline.c man3/gl_get_line.3
             For those cases where a terminal provides its own
             high-level terminal editing facilities, you can now
             specify an edit-mode argument of 'none'. This disables
             all tecla key bindings, and by using canonical terminal
             input mode instead of raw input mode, editing is left up
             to the terminal driver.

21/10/2001 mcs@astro.caltech.edu
           libtecla.h getline.c history.c history.h
           man3/gl_get_line.3 man3/gl_history_info.3
             I added the new gl_state_of_history(),
             gl_range_of_history() and gl_size_of_history()
             functions for querying information about the
             history list.
           history.c
             While testing the new gl_size_of_history()
             function, I noticed that when the history buffer
             wrapped, any location nodes of old lines between
             the most recent line and the end of the buffer
             weren't being removed. This could result in bogus
             entries appearing at the start of the history list.
             Now fixed.

20/10/2001 mcs@astro.caltech.edu

           libtecla.h getline.c history.c history.h
           man3/gl_get_line.3 man3/gl_lookup_history.3
             I added a function called gl_lookup_history(), that
             the application can use to lookup lines in the history
             list.
           libtecla.h getline.c history.c history.h man3/gl_get_line.3
             gl_show_history() now takes a format string argument
             to control how the line is displayed, and with what
             information. It also now provides the option of either
             displaying all history lines or just those of the
             current history group.
           getline.c man3/gl_get_line.3
             gl_get_line() only archives lines in the history buffer
             if the newline action was invoked by a newline or
             carriage return character.

16/10/2001 mcs@astro.caltech.edu

           history.c history.h getline.c libtecla.h libtecla.map
           man3/gl_get_line.3 man3/gl_resize_history.3
           man3/gl_limit_history.3 man3/gl_clear_history.3
           man3/gl_toggle_history.3
	     I added a number of miscellaneous history configuration
	     functions. You can now resize or delete the history
	     buffer, limit the number of lines that are allowed in the
	     buffer, clear either all history or just the history of
	     the current history group, and temporarily enable and
	     disable the history mechanism.

13/10/2001 mcs@astro.caltech.edu

           getline.c
             tputs_fp is now only declared if using termcap or
             terminfo.
           getline.c libtecla.map man3/gl_get_line.3
           man3/gl_terminal_size.3
             I added a public gl_terminal_size() function for
             updating and querying the current size of the terminal.
           update_version configure.in libtecla.h
             A user noted that on systems where the configure script
             couldn't be used, it was inconvenient to have the version
             number macros set by the configure script, so they are
             now specified in libtecla.h. To reduce the likelihood
             that the various files where the version number now
             appears might get out of sync, I have written the
             update_version script, which changes the version number
             in all of these files to a given value.

01/10/2001 mcs@astro.caltech.edu

           getline.c history.c history.h man3/gl_get_line.3
             I added a max_lines argument to gl_save_history(), to
             allow people to optionally place a ceiling on the number
             of history lines saved. Specifying this as -1 sets the
             ceiling to infinity.

01/10/2001 mcs@astro.caltech.edu

           configure.in configure
             Under digital unix, getline wouldn't compile with
             _POSIX_C_SOURCE set, due to type definitions needed by
             select being excluded by this flag. Defining the
             _OSF_SOURCE macro as well on this system, resolved this.

30/09/2001 mcs@astro.caltech.edu

           getline.c libtecla.h history.c history.h man3/gl_get_line.3
           man3/gl_group_history.3
             I implemented history streams. History streams
             effectively allow multiple history lists to be stored in
             a single history buffer. Lines in the buffer are tagged
             with the current stream identification number, and
             lookups only consider lines that are marked with the
             current stream identifier.
           getline.c libtecla.h history.c history.h man3/gl_get_line.3
           man3/gl_show_history.3
             The new gl_show_history function displays the current
             history to a given stdio output stream.

29/09/2001 mcs@astro.caltech.edu

           getline.c
             Previously new_GetLine() installed a persistent signal
             handler to be sure to catch the SIGWINCH (terminal size
             change) signal between calls to gl_get_line(). This had
             the drawback that if multiple GetLine objects were
             created, only the first GetLine object used after the
             signal was received, would see the signal and adapt to
             the new terminal size. Instead of this, a signal handler
             for sigwinch is only installed while gl_get_line() is
             running, and just after installing this handler,
             gl_get_line() checks for terminal size changes that
             might have occurred while the signal handler wasn't
             installed.
           getline.c
             Dynamically allocated copies of capability strings looked
             up in the terminfo or termcap databases are now made, so
             that calls to setupterm() etc for one GetLine object
             don't get trashed when another GetLine object calls
             setupterm() etc. It is now safe to allocate and use
             multiple GetLine objects, albeit only within a single
             thread.
           
28/09/2001 mcs@astro.caltech.edu

           version.c Makefile.rules
             I added a function for querying the version number of
             the library.

26/09/2001 mcs@astro.caltech.edu

           getline.c man3/gl_get_line.3
             I added the new gl_watch_fd() function, which allows
             applications to register callback functions to be invoked
             when activity is seen on arbitrary file descriptors while
             gl_get_line() is awaiting keyboard input from the user.

           keytab.c
             If a request is received to delete a non-existent
             binding, which happens to be an ambiguous prefix of other
             bindings no complaint is now generated about it being
             ambiguous.

23/09/2001 mcs@astro.caltech.edu

           getline.c history.c history.h man3/gl_get_line.3
           libtecla.map demo.c
             I added new public functions for saving and restoring the
             contents of the history list. The demo program now uses
             these functions to load and save history in ~/.demo_history.

23/09/2001 mcs@astro.caltech.edu

           getline.c
             On trying the demo for the first time on a KDE konsole
             terminal, I discovered that the default M-O binding
             to repeat history was hiding the arrow keys, which are
             M-OA etc. I have removed this binding. The M-o (ie the
             lower case version of this), is still bound.

18/09/2001 mcs@astro.caltech.edu

           getline.c man3/gl_get_line.3 libtecla.map
             Automatic reading of ~/.teclarc is now postponed until
             the first call to gl_get_line(), to give the application
             the chance to specify alternative configuration sources
             with the new function gl_configure_getline(). The latter
             function allows configuration to be done with a string, a
             specified application-specific file, and/or a specified
             user-specific file. I also added a read-init-files action
             function, for re-reading the configuration files, if any.
             This is by default bound to ^X^R. This is all documented
             in gl_get_line.3.

08/09/2001 mcs@astro.caltech.edu

           getline.c man3/gl_get_line.3
             It is now possible to bind actions to key-sequences
             that start with printable characters. Previously
             keysequences were required to start with meta or control
             characters. This is documented in gl_get_line.3.

           getline.c man3/gl_get_line.3
             A customized completion function can now arrange for
             gl_get_line() to return the current input line whenever a
             successful completion has been made. This is signalled by
             setting the last character of the optional continuation
             suffix to a newline character. This is documented in
             gl_get_line.3.

05/07/2001 Bug reported by Mike MacFaden, fixed by mcs

           configure.in
             There was a bug in the configure script that only
             revealed itself on systems without termcap but not
             terminfo (eg. NetBSD). I traced the bug back to a lack of
             sufficient quoting of multi-line m4 macro arguments in
             configure.in, and have now fixed this and recreated the
             configure script.

05/07/2001 Bug reported and patched by Mike MacFaden (patch modified
           by mcs to match original intentions).

           getline.c
             getline.c wouldn't compile when termcap was selected as
             the terminal information database. setupterm() was being
             passed a non-existent variable, in place of the term[]
             argument of gl_control_strings(). Also if
             gl_change_terminal() is called with term==NULL, "ansi"
             is now substituted.

02/07/2001 Version 1.3.3 released.

27/06/2001 mcs@astro.caltech.edu

           getline.c expand.c cplmatch.c
             Added checks to fprintf() statements that write to the
             terminal.
           getline.c
             Move the cursor to the end of the line before suspending,
             so that the cursor doesn't get left in the middle of the
             input line.
           Makefile.in
             On systems that don't support shared libraries, the
             distclean target of make deleted libtecla.h. This has
             now been fixed.
           getline.c
             gl_change_terminal() was being called by gl_change_editor(),
             with the unwanted side effect that raw terminal modes were
             stored as those to be restored later, if called by an
             action function. gl_change_terminal() was being called in
             this case to re-establish terminal-specific key bindings,
             so I have just split this part of the function out into
             a separate function for both gl_change_editor() and
             gl_change_terminal() to call.

12/06/2001 mcs@astro.caltech.edu

           getline.c
             Signal handling has been improved. Many more signals are
             now trapped, and instead of using a simple flag set by a
             signal handler, race conditions are avoided by blocking
             signals during most of the gl_get_line() code, and
             unblocking them via calls to sigsetjmp(), just before
             attempting to read each new character from the user.
             The matching use of siglongjmp() in the signal
             handlers ensures that signals are reblocked correctly
             before they are handled. In most cases, signals cause
             gl_get_line() to restore the terminal modes and signal
             handlers of the calling application, then resend the
             signal to the application. In the case of SIGINT, SIGHUP,
             SIGPIPE, and SIGQUIT, if the process still exists after
             the signals are resent, gl_get_line() immediately returns
             with appropriate values assigned to errno. If SIGTSTP,
             SIGTTIN or SIGTTOU signals are received, the process is
             suspended. If any other signal is received, and the
             process continues to exist after the signal is resent to
             the calling application, line input is resumed after the
             terminal is put back into raw mode, the gl_get_line()
             signal handling is restored, and the input line redrawn.
           man/gl_get_line(3)
             I added a SIGNAL HANDLING section to the gl_get_line()
             man page, describing the new signal handling features.

21/05/2001 Version 1.3.2 released.

21/05/2001 mcs@astro.caltech.edu

           getline.c
             When vi-replace-char was used to replace the character at
             the end of the line, it left the cursor one character to
             its right instead of on top of it. Now rememdied.
           getline.c
             When undoing, to properly emulate vi, the cursor is now
             left at the leftmost of the saved and current cursor
             positions.
           getline.c man3/gl_get_line.3
             Implemented find-parenthesis (%), delete-to-paren (M-d%),
             vi-change-to-paren (M-c%), copy-to-paren (M-y%).
           cplfile.c pcache.c
             In three places I was comparing the last argument of
             strncmp() to zero instead of the return value of
             strncmp().

20/05/2001 mcs@astro.caltech.edu

           getline.c man3/gl_get_line.3
             Implemented and documented the vi-repeat-change action,
             bound to the period key. This repeats the last action
             that modified the input line.

19/05/2001 mcs@astro.caltech.edu

           man3/gl_get_line.3
             I documented the new action functions and bindings
             provided by Tim Eliseo, plus the ring-bell action and
             the new "nobeep" configuration option.
           getline.c
             I modified gl_change_editor() to remove and reinstate the
             terminal settings as well as the default bindings, since
             these have editor-specific differences. I also modified
             it to not abort if a key-sequence can't be bound for some
             reason. This allows the new vi-mode and emacs-mode
             bindings to be used safely.
           getline.c
             When the line was re-displayed on receipt of a SIGWINCH
             signal, the result wasn't visible until the next
             character was typed, since a call to fflush() was needed.
             gl_redisplay_line() now calls gl_flush_output() to remedy
             this.

17/05/2001 mcs@astro.catlech.edu

           getline.c
             Under Linux, calling fflush(gl->output_fd) hangs if
             terminal output has been suspended with ^S. With the
             tecla library taking responsability for reading the stop
             and start characters this was a problem, because once
             hung in fflush(), the keyboard input loop wasn't entered,
             so the user couldn't type the start character to resume
             output.  To remedy this, I now have the terminal process
             these characters, rather than the library.

12/05/2001 mcs@astro.caltech.edu

           getline.c
             The literal-next action is now implemented as a single
             function which reads the next character itself.
             Previously it just set a flag which effected the
             interpretation of the next character read by the input
             loop.
           getline.c
             Added a ring-bell action function. This is currently
             unbound to any key by default, but it is used internally,
             and can be used by users that want to disable any of the
             default key-bindings.

12/05/2001 Tim Eliseo    (logged here by mcs)

           getline.c
             Don't reset gl->number until after calling an action
             function. By looking at whether gl->number is <0 or
             not, action functions can then tell whether the count
             that they were passed was explicitly specified by the
             user, as opposed to being defaulted to 1.
           getline.c
             In vi, the position at which input mode is entered
             acts as a barrier to backward motion for the few
             backward moving actions that are enabled in input mode.
             Tim added this barrier to getline.
           getline.c
             In gl_get_line() after reading an input line, or
             having the read aborted by a signal, the sig_atomic_t
             gl_pending_signal was being compared to zero instead
             of -1 to see if no signals had been received.
             gl_get_line() will thus have been calling raise(-1),
             which luckily didn't seem to do anything. Tim also
             arranged for errno to be set to EINTR when a signal
             aborts gl_get_line().
           getline.c
             The test in gl_add_char_to_line() for detecting
             when overwriting a character with a wider character,
             had a < where it needed a >. Overwriting with a wider
             character thus overwrote trailing characters. Tim also
             removed a redundant copy of the character into the
             line buffer.
           getline.c
             gl_cursor_left() and gl->cursor_right() were executing
             a lot of redundant code, when the existing call to the
             recently added gl_place_cursor() function, does all that
             is necessary.
           getline.c
             Remove redundant code from backward_kill_line() by
             re-implimenting in terms of gl_place_cursor() and
             gl_delete_chars().
           getline.c
             gl_forward_delete_char() now records characters in cut
             buffer when in vi command mode.
           getline.c
             In vi mode gl_backward_delete_char() now only deletes
             up to the point at which input mode was entered. Also
             gl_delete_chars() restores from the undo buffer when
             deleting in vi insert mode.
           getline.c
             Added action functions, vi-delete-goto-column,
             vi-change-to-bol, vi-change-line, emacs-mode, vi-mode,
             vi-forward-change-find, vi-backward-change-find,
             vi-forward-change-to, vi-backward-change-to,
             vi-change-goto-col, forward-delete-find, backward-delete-find,
             forward-delete-to, backward-delete-to,
             delete-refind, delete-invert-refind, forward-copy-find,
             backward-copy-find, forward-copy-to, backward-copy-to
             copy-goto-column, copy-rest-of-line, copy-to-bol, copy-line,
             history-re-search-forward, history-re-search-backward.

06/05/2001 Version 1.3.1 released.

03/05/2001 mcs@astro.caltech.edu

           configure.in
             Old versions of GNU ld don't accept version scripts.
             Under Linux I thus added a test to try out ld with
             the --version-script argument to see if it works.
             If not, version scripts aren't used.
           configure.in
             My test for versions of Solaris earlier than 7
             failed when confronted by a three figure version
             number (2.5.1). Fixed.

30/04/2001 mcs@astro.caltech.edu

           getline.c
             In vi mode, history-search-backward and
             history-search-forward weren't doing anything when
             invoked at the start of an empty line, whereas
             they should have acted like up-history and down-history.
           Makefile.in Makefile.rules
             When shared libraries are being created, the build
             procedure now arranges for any alternate library
             links to be created as well, before linking the
             demos. Without this the demos always linked to the
             static libraries (which was perfectly ok, but wasn't a
             good example).
           Makefile.in Makefile.rules
             On systems on which shared libraries were being created,
             if there were no alternate list of names, make would
             abort due to a Bourne shell 'for' statement that didn't
             have any arguments. Currently there are no systems who's
             shared library configurations would trigger this
             problem.
           Makefile.rules
             The demos now relink to take account of changes to the
             library.
           configure.in configure
             When determining whether the reentrant version of the
             library should be compiled by default, the configure
             script now attempts to compile a dummy program that
             includes all of the appropriate system headers and
             defines _POSIX_C_SOURCE. This should now be a robust test
             on systems which use C macros to alias these function
             names to other internal functions.
           configure.in
             Under Solaris 2.6 and earlier, the curses library is in
             /usr/ccs/lib. Gcc wasn't finding this. In addition to
             remedying this, I had to remove "-z text" from
             LINK_SHARED under Solaris to get it to successfully
             compile the shared library against the static curses
             library.
           configure.in
             Under Linux the -soname directive was being used
             incorrectly, citing the fully qualified name of the
             library instead of its major version alias. This will
             unfortunately mean that binaries linked with the 1.2.3
             and 1.2.4 versions of the shared library won't use
             later versions of the library unless relinked.

30/04/2001 mcs@astro.caltech.edu

           getline.c
             In gl_get_input_line(), don't redundantly copy the
             start_line if start_line == gl->line.

30/04/2001 Version 1.3.0 released.

28/04/2001 mcs@astro.caltech.edu

           configure.in
             I removed the --no-undefined directive from the Linux
             LINK_SHARED command. After recent patches to our RedHat
             7.0 systems ld started reporting some internal symbols of
             libc as being undefined.  Using nm on libc indicated that
             the offending symbols are indeed defined, albeit as
             "common" symbols, so there appears to be a bug in
             RedHat's ld. Removing this flag allows the tecla shared
             library to compile, and programs appear to function fine.
           man3/gl_get_line.3
             The default key-sequence used to invoke the
             read-from-file action was incorrectly cited as ^Xi
             instead of ^X^F.

26/04/2001 mcs@astro.caltech.edu

           getline.c man3/gl_get_line.3
             A new vi-style editing mode was added. This involved
             adding many new action functions, adding support for
             specifying editing modes in users' ~/.teclarc files,
             writing a higher level cursor motion function to support
             the different line-end bounds required in vi command
             mode, and a few small changes to support the fact that vi
             has two modes, input mode and command mode with different
             bindings.

             When vi editing mode is enabled, any binding that starts
             with an escape or a meta character, is interpreted as a
             command-mode binding, and switches the library to vi
             command mode if not already in that mode. Once in command
             mode the first character of all keysequences entered
             until input mode is re-enabled, are quietly coerced to
             meta characters before being looked up in the key-binding
             table. So, for example, in the key-binding table, the
             standard vi command-mode 'w' key, which moves the cursor
             one word to the right, is represented by M-w. This
             emulates vi's dual sets of bindings in a natural way
             without needing large changes to the library, or new
             binding syntaxes. Since cursor keys normally emit
             keysequences which start with escape, it also does
             something sensible when a cursor key is pressed during
             input mode (unlike true vi, which gets upset).

             I also added a ^Xg binding for the new list-glob action
             to both the emacs and vi key-binding tables. This lists
             the files that match the wild-card expression that
             precedes it on the command line.

             The function that reads in ~/.teclarc used to tell
             new_GetLine() to abort if it encountered anything that it
             didn't understand in this file. It now just reports an
             error and continues onto the next line.
           Makefile.in:
             When passing LIBS=$(LIBS) to recursive invokations of
             make, quotes weren't included around the $(LIBS) part.
             This would cause problems if LIBS ever contained more
             than one word (with the supplied configure script this
             doesn't happen currently). I added these quotes.
           expand.c man3/ef_expand_file.3:
             I wrote a new public function called ef_list_expansions(),
             to list the matching filenames returned by
             ef_expand_file().

             I also fixed the example in the man page, which cited
             exp->file instead of exp->files, and changed the
             dangerous name 'exp' with 'expn'.
           keytab.c:
             Key-binding tables start with 100 elements, and are
             supposedly incremented in size by 100 elements whenever
             the a table runs out of space. The realloc arguments to
             do this were wrong. This would have caused problems if
             anybody added a lot of personal bindings in their
             ~/.teclarc file. I only noticed it because the number of
             key bindings needed by the new vi mode exceeded this
             number.
           libtecla.map
             ef_expand_file() is now reported as having been added in
             the upcoming 1.3.0 release.

25/03/2001 Markus Gyger  (logged here by mcs)

           Makefile.in:
             Make symbolic links to alternative shared library names
             relative instead of absolute.
           Makefile.rules:
             The HP-UX libtecla.map.opt file should be made in the
             compilation directory, to allow the source code directory
             to be on a readonly filesystem.
           cplmatch.c demo2.c history.c pcache.c
             To allow the library to be compiled with a C++ compiler,
             without generating warnings, a few casts were added where
             void* return values were being assigned directly to
             none void* pointer variables.

25/03/2001 mcs@astro.caltech.edu

           libtecla.map:
             Added comment header to explain the purpose of the file.
             Also added cpl_init_FileArgs to the list of exported
             symbols. This symbol is deprecated, and no longer
             documented, but for backwards compatibility, it should
             still be exported.
           configure:
             I had forgotten to run autoconf before releasing version
             1.2.4, so I have just belatedly done so.  This enables
             Markus' changes to "configure.in" documented previously,
             (see 17/03/2001).

20/03/2001 John Levon   (logged here by mcs)

           libtecla.h
             A couple of the function prototypes in libtecla.h have
             (FILE *) argument declarations, which means that stdio.h
             needs to be included. The header file should be self
             contained, so libtecla.h now includes stdio.h.

18/03/2001 Version 1.2.4 released.

           README html/index.html configure.in
             Incremented minor version from 3 to 4.

18/03/2001 mcs@astro.caltech.edu

           getline.c
             The fix for the end-of-line problem that I released a
             couple of weeks ago, only worked for the first line,
             because I was handling this case when the cursor position
             was equal to the last column, rather than when the cursor
             position modulo ncolumn was zero.
           Makefile.in Makefile.rules
             The demos are now made by default, their rules now being
             int Makefile.rules instead of Makefile.in.
           INSTALL
             I documented how to compile the library in a different
             directory than the distribution directory.
             I also documented features designed to facilitate
             configuring and building the library as part of another
             package.

17/03/2001 Markus Gyger (logged here by mcs)

           getline.c
             Until now cursor motions were done one at a time. Markus
             has added code to make use the of the terminfo capability
             that moves the cursor by more than one position at a
             time. This greatly improves performance when editing near
             the start of long lines.
           getline.c
             To further improve performance, Markus switched from
             writing one character at a time to the terminal, using
             the write() system call, to using C buffered output
             streams. The output buffer is only flushed when
             necessary.
           Makefile.rules Makefile.in configure.in
             Added support for compiling for different architectures
             in different directories. Simply create another directory
             and run the configure script located in the original
             directory.
           Makefile.in configure.in libtecla.map
             Under Solaris, Linux and HP-UX, symbols that are to be
             exported by tecla shared libraries are explicitly specified
             via symbol map files. Only publicly documented functions
             are thus visible to applications.
           configure.in
             When linking shared libraries under Solaris SPARC,
             registers that are reserved for applications are marked
             as off limits to the library, using -xregs=no%appl when
             compiling with Sun cc, or -mno-app-regs when compiling
             with gcc. Also removed -z redlocsym for Solaris, which
             caused problems under some releases of ld.
           homedir.c  (after minor changes by mcs)
             Under ksh, ~+ expands to the current value of the ksh
             PWD environment variable, which contains the path of
             the current working directory, including any symbolic
             links that were traversed to get there. The special
             username "+" is now treated equally by tecla, except
             that it substitutes the return value of getcwd() if PWD
             either isn't set, or if it points at a different
             directory than that reported by getcwd().

08/03/2001 Version 1.2.3 released.

08/03/2001 mcs@astro.caltech.edu

           getline.c
             On compiling the library under HP-UX for the first time
             I encountered and fixed a couple of bugs:

             1. On all systems except Solaris, the callback function
                required by tputs() takes an int argument for the
                character that is to be printed. Under Solaris it
                takes a char argument. The callback function was
                passing this argument, regardless of type, to write(),
                which wrote the first byte of the argument.  This was
                fine under Solaris and under little-endian systems,
                because the first byte contained the character to be
                written, but on big-endian systems, it always wrote
                the zero byte at the other end of the word. As a
                result, no control characters were being written to
                the terminal.
             2. While attempting to start a newline after the user hit
                enter, the library was outputting the control sequence
                for moving the cursor down, instead of the newline
                character. On many systems the control sequence for
                moving the cursor down happends to be a newline
                character, but under HP-UX it isn't. The result was
                that no new line was being started under HP-UX.

04/03/2001 mcs@astro.caltech.edu

           configure.in Makefile.in Makefile.stub configure config.guess
           config.sub Makefile.rules install-sh PORTING README INSTALL
             Configuration and compilation of the library is now
             performed with the help of an autoconf configure
             script. In addition to relieving the user of the need to
             edit the Makefile, this also allows automatic compilation
             of the reentrant version of the library on platforms that
             can handle it, along with the creation of shared
             libraries where configured. On systems that aren't known
             to the configure script, just the static tecla library is
             compiled. This is currently the case on all systems
             except Linux, Solaris and HP-UX. In the hope that
             installers will provide specific conigurations for other
             systems, the configure.in script is heavily commented,
             and instructions on how to use are included in a new
             PORTING file.

24/02/2001 Version 1.2b released.

22/02/2001 mcs@astro.caltech.edu

           getline.c
             It turns out that most terminals, but not all, on writing
             a character in the rightmost column, don't wrap the
             cursor onto the next line until the next character is
             output. This library wasn't aware of this and thus if one
             tried to reposition the cursor from the last column,
             gl_get_line() thought that it was moving relative to a
             point on the next line, and thus moved the cursor up a
             line. The fix was to write one extra character when in
             the last column to force the cursor onto the next line,
             then backup the cursor to the start of the new line.
           getline.c
             On terminal initialization, the dynamic LINES and COLUMNS
             environment variables were ignored unless
             terminfo/termcap didn't return sensible dimensions. In
             practice, when present they should override the static
             versions in the terminfo/termcap databases. This is the
             new behavior. In reality this probably won't have caused
             many problems, because a SIGWINCH signal which informs of
             terminal size changes is sent when the terminal is
             opened, so the dimensions established during
             initialization quickly get updated on most systems.

18/02/2001 Version 1.2a released.

18/02/2001 mcs@astro.caltech.edu

           getline.c
             Three months ago I moved the point at which termios.h
             was included in getline.c. Unfortunately, I didn't notice
             that this moved it to after the test for TIOCGWINSZ being
             defined. This resulted in SIGWINCH signals not being
             trapped for, and thus terminal size changes went
             unnoticed. I have now moved the test to after the 
             inclusion of termios.h.

12/02/2001 Markus Gyger     (described here by mcs)

           man3/pca_lookup_file.3 man3/gl_get_line.3
           man3/ef_expand_file.3 man3/cpl_complete_word.3
             In the 1.2 release of the library, all functions in the
             library were given man pages. Most of these simply
             include one of the above 4 man pages, which describe the
             functions while describing the modules that they are in.
             Markus added all of these function names to the lists in
             the "NAME" headers of the respective man pages.
             Previously only the primary function of each module was
             named there.

11/02/2001 mcs@astro.caltech.edu

           getline.c
             On entering a line that wrapped over two or more
             terminal, if the user pressed enter when the cursor
             wasn't on the last of the wrapped lines, the text of the
             wrapped lines that followed it got mixed up with the next
             line written by the application, or the next input
             line. Somehow this slipped through the cracks and wasn't
             noticed until now. Anyway, it is fixed now.

09/02/2001 Version 1.2 released.

04/02/2001 mcs@astro.caltech.edu

           pcache.c libtecla.h
             With all filesystems local, demo2 was very fast to start
             up, but on a Sun system with one of the target
             directories being on a remote nfs mounted filesystem, the
             startup time was many seconds. This was due to the
             executable selection callback being applied to all files
             in the path at startup. To avoid this, all files are now
             included in the cache, and the application specified
             file-selection callback is only called on files as they
             are matched. Whether the callback rejected or accepted
             them is then cached so that the next time an already
             checked file is looked at, the callback doesn't have to
             be called. As a result, startup is now fast on all
             systems, and since usually there are only a few matching
             file completions at a time, the delay during completion
             is also usually small. The only exception is if the user
             tries to complete an empty string, at which point all
             files have to be checked. Having done this once, however,
             doing it again is fast.
           man3/pca_lookup_file.3
             I added a man page documenting the new PathCache module.
           man3/.3
             I have added man pages for all of the functions in each
             of the modules. These 1-line pages use the .so directive
             to redirect nroff to the man page of the parent module.
           man Makefile update_html
             I renamed man to man3 to make it easier to test man page
             rediction, and updated Makefile and update_html
             accordingly. I also instructed update_html to ignore
             1-line man pages when making html equivalents of the man
             pages.
           cplmatch.c
             In cpl_list_completions() the size_t return value of
             strlen() was being used as the length argument of a "%*s"
             printf directive. This ought to be an int, so the return
             value of strlen() is now cast to int. This would have
             caused problems on architectures where the size of a
             size_t is not equal to the size of an int.

02/02/2001 mcs@astro.caltech.edu

           getline.c
             Under UNIX, certain terminal bindings are set using the
             stty command. This, for example, specifies which control
             key generates a user-interrupt (usually ^C or ^Y). What I
             hadn't realized was that ASCII NUL is used as the way to
             specify that one of these bindings is unset. I have now
             modified the code to skip unset bindings, leaving the
             corresponding action bound to the built-in default, or a
             user provided binding.

28/01/2001 mcs@astro.caltech.edu

           pcache.c libtecla.h
             A new module was added which supports searching for files
             in any colon separated list of directories, such as the
             unix execution PATH environment variable. Files in these
             directories, after being individually okayed for
             inclusion via an application provided callback, are
             cached in a PathCache object. You can then look up the
             full pathname of a given filename, or you can use the
             provided completion callback to list possible completions
             in the path-list. The contents of relative directories,
             such as ".", obviously can't be cached, so these
             directories are read on the fly during lookups and
             completions. The obvious application of this facility is
             to provide Tab-completion of commands, and thus a
             callback to place executable files in the cache, is
             provided.
           demo2.c
             This new program demonstrates the new PathCache
             module. It reads and processes lines of input until the
             word 'exit' is entered, or C-d is pressed. The default
             tab-completion callback is replaced with one which at the
             start of a line, looks up completions of commands in the
             user's execution path, and when invoked in other parts of
             the line, reverts to normal filename completion. Whenever
             a new line is entered, it extracts the first word on the
             line, looks it up in the user's execution path to see if
             it corresponds to a known command file, and if so,
             displays the full pathname of the file, along with the
             remaining arguments.
           cplfile.c
             I added an optional pair of callback function/data
             members to the new cpl_file_completions() configuration
             structure. Where provided, this callback is asked
             on a file-by-file basis, which files should be included
             in the list of file completions. For example, a callback
             is provided for listing only completions of executable
             files.
           cplmatch.c
             When listing completions, the length of the type suffix
             of each completion wasn't being taken into account
             correctly when computing the column widths. Thus the
             listing appeared ragged sometimes. This is now fixed.
           pathutil.c
             I added a function for prepending a string to a path,
             and another for testing whether a pathname referred to
             an executable file.

28/01/2001 mcs@astro.caltech.edu

           libtecla.h cplmatch.c man/cpl_complete_word.3
             The use of a publically defined structure to configure
             the cpl_file_completions() callback was flawed, so a new
             approach has been designed, and the old method, albeit
             still supported, is no longer documented in the man
             pages. The definition of the CplFileArgs structure in
             libtecla.h is now accompanied by comments warning people
             not to modify it, since modifications could break
             applications linked to shared versions of the tecla
             library. The new method involves an opaque CplFileConf
             object, instances of which are returned by a provided
             constructor function, configured with provided accessor
             functions, and when no longer needed, deleted with a
             provided destructor function. This is documented in the
             cpl_complete_word man page. The cpl_file_completions()
             callback distinguishes what type of configuration
             structure it has been sent by virtue of a code placed at
             the beginning of the CplFileConf argument by its
             constructor.

04/01/2001 mcs@astro.caltech.edu (Release of version 1.1j)

           getline.c
             I added upper-case bindings for the default meta-letter
             keysequences such as M-b. They thus continue to work
             when the user has caps-lock on.
           Makefile
             I re-implemented the "install" target in terms of new
             install_lib, install_inc and install_man targets. When
             distributing the library with other packages, these new
             targets allows for finer grained control of the
             installation process.

30/12/2000 mcs@astro.caltech.edu

           getline.c man/gl_get_line.3
             I realized that the recall-history action that I
             implemented wasn't what Markus had asked me for. What he
             actually wanted was for down-history to continue going
             forwards through a previous history recall session if no
             history recall session had been started while entering
             the current line. I have thus removed the recall-history
             action and modified the down-history action function
             accordingly.

24/12/2000 mcs@astro.caltech.edu

           getline.c
             I modified gl_get_line() to allow the previously returned
             line to be passed in the start_line argument.
           getline.c man/gl_get_line.3
             I added a recall-history action function, bound to M^P.
             This recalls the last recalled history line, regardless
             of whether it was from the current or previous line.

13/12/2000 mcs@astro.caltech.edu (Release of version 1.1i)

           getline.c history.h history.c man/gl_get_line.3
             I implemented the equivalent of the ksh Operate action. I
             have named the tecla equivalent "repeat-history". This
             causes the line that is to be edited to returned, and
             arranges for the next most recent history line to be
             preloaded on the next call to gl_get_line(). Repeated
             invocations of this action thus result in successive
             history lines being repeated - hence the
             name. Implementing the ksh Operate action was suggested
             by Markus Gyger. In ksh it is bound to ^O, but since ^O
             is traditionally bound by the default terminal settings,
             to stop-output, I have bound the tecla equivalent to M-o.

01/12/2000 mcs@astro.caltech.edu (Release of version 1.1h)

           getline.c keytab.c keytab.h man/gl_get_line.3
             I added a digit-argument action, to allow repeat
             counts for actions to be entered. As in both tcsh
             and readline, this is bound by default to each of
             M-0, M-1 through to M-9, the number being appended
             to the current repeat count. Once one of these has been
             pressed, the subsequent digits of the repeat count can be
             typed with or without the meta key pressed. It is also
             possible to bind digit-argument to other keys, with or
             without a numeric final keystroke. See man page for
             details.

           getline.c man/gl_get_line.3
             Markus noted that my choice of M-< for the default
             binding of read-from-file, could be confusing, since
             readline binds this to beginning-of-history. I have
             thus rebound it to ^X^F (ie. like find-file in emacs).

           getline.c history.c history.h man/gl_get_line.3
             I have now implemented equivalents of the readline
             beginning-of-history and end-of-history actions.
             These are bound to M-< and M-> respectively.

           history.c history.h
             I Moved the definition of the GlHistory type, and
             its subordinate types from history.h to history.c.
             There is no good reason for any other module to
             have access to the innards of this structure.

27/11/2000 mcs@astro.caltech.edu (Release of version 1.1g)

           getline.c man/gl_get_line.3
             I added a "read-from-file" action function and bound it
             by default to M-<. This causes gl_get_line() to
             temporarily return input from the file who's name
             precedes the cursor.
             
26/11/2000 mcs@astro.caltech.edu

           getline.c keytab.c keytab.h man/gl_get_line.3
             I have reworked some of the keybinding code again.

             Now, within key binding strings, in addition to the
             previously existing notation, you can now use M-a to
             denote meta-a, and C-a to denote control-a. For example,
             a key binding which triggers when the user presses the
             meta key, the control key and the letter [
             simultaneously, can now be denoted by M-C-[, or M-^[ or
             \EC-[ or \E^[.

             I also updated the man page to use M- instead of \E in
             the list of default bindings, since this looks cleaner.

           getline.c man/gl_get_line.3
             I added a copy-region-as-kill action function and
             gave it a default binding to M-w.

22/11/2000 mcs@astro.caltech.edu

           *.c
             Markus Gyger sent me a copy of a previous version of
             the library, with const qualifiers added in appropriate
             places. I have done the same for the latest version.
             Among other things, this gets rid of the warnings
             that are generated if one tells the compiler to
             const qualify literal strings.

           getline.c getline.h glconf.c
             I have moved the contents of glconf.c and the declaration
             of the GetLine structure into getline.c. This is cleaner,
             since now only functions in getline.c can mess with the
             innards of GetLine objects. It also clears up some problems
             with system header inclusion order under Solaris, and also
             the possibility that this might result in inconsistent
             system macro definitions, which in turn could cause different
             declarations of the structure to be seen in different files.

           hash.c
             I wrote a wrapper function to go around strcmp(), such that
             when hash.c is compiled with a C++ compiler, the pointer
             to the wrapper function is a C++ function pointer.
             This makes it compatible with comparison function pointer
             recorded in the hash table.

           cplmatch.c getline.c libtecla.h
             Markus noted that the Sun C++ compiler wasn't able to
             match up the declaration of cpl_complete_word() in
             libtecla.h, where it is surrounded by a extern "C" {}
             wrapper, with the definition of this function in
             cplmatch.c. My suspicion is that the compiler looks not
             only at the function name, but also at the function
             arguments to see if two functions match, and that the
             match_fn() argument, being a fully blown function pointer
             declaration, got interpetted as that of a C function in
             one case, and a C++ function in the other, thus
             preventing a match.

             To fix this I now define a CplMatchFn typedef in libtecla.h,
             and use this to declare the match_fn callback.

20/11/2000 (Changes suggested by Markus Gyger to support C++ compilers):
           expand.c
             Renamed a variable called "explicit" to "xplicit", to
             avoid conflicts when compiling with C++ compilers.
           *.c
             Added explicit casts when converting from (void *) to
             other pointer types. This isn't needed in C but it is
             in C++.
           getline.c
             tputs() has a strange declaration under Solaris. I was
             enabling this declaration when the SPARC feature-test
             macro was set. Markus changed the test to hinge on the
             __sun and __SVR4 macros.
           direader.c glconf.c stringrp.c
             I had omitted to include string.h in these two files.

           Markus also suggested some other changes, which are still
           under discussion. With the just above changes however, the
           library compiles without complaint using g++.

19/11/2000 mcs@astro.caltech.edu
           getline.h getline.c keytab.c keytab.h glconf.c
           man/gl_get_line.3
             I added support for backslash escapes (include \e
             for the keyboard escape key) and literal binary
             characters to the characters allowed within key sequences
             of key bindings.

           getline.h getline.c keytab.c keytab.h glconf.c
           man/gl_get_line.3
             I introduced symbolic names for the arrow keys, and
             modified the library to use the cursor key sequences
             reported by terminfo/termcap in addition to the default
             ANSI ones. Anything bound to the symbolically named arrow
             keys also gets bound to the default and terminfo/termcap
             cursor key sequences. Note that under Solaris
             terminfo/termcap report the properties of hardware X
             terminals when TERM is xterm instead of the terminal
             emulator properties, and the cursor keys on these two
             systems generate different key sequences. This is an
             example of why extra default sequences are needed.

           getline.h getline.c keytab.c
             For some reason I was using \e to represent the escape
             character. This is supported by gcc, which thus doesn't
             emit a warning except with the -pedantic flag, but isn't
             part of standard C. I now use a macro to define escape
             as \033 in getline.h, and this is now used wherever the
             escape character is needed.

17/11/2000 mcs@astro.caltech.edu (Release of version 1.1d)

           getline.c, man/gl_get_line(3), html/gl_get_line.html
             In tcsh ^D is bound to a function which does different
             things depending on where the cursor is within the input
             line. I have implemented its equivalent in the tecla
             library. When invoked at the end of the line this action
             function displays possible completions. When invoked on
             an empty line it causes gl_get_line() to return NULL,
             thus signalling end of input. When invoked within a line
             it invokes forward-delete-char, as before. The new action
             function is called del-char-or-list-or-eof.

           getline.c, man/gl_get_line(3), html/gl_get_line.html
             I found that the complete-word and expand-file actions
             had underscores in their names instead of hyphens. This
             made them different from all other action functions, so I
             have changed the underscores to hyphens.

           homedir.c
             On SCO UnixWare while getpwuid_r() is available, the
             associated _SC_GETPW_R_SIZE_MAX macro used by sysconf()
             to find out how big to make the buffer to pass to this
             function to cater for any password entry, doesn't
             exist. I also hadn't catered for the case where sysconf()
             reports that this limit is indeterminate. I have thus
             change the code to substitute a default limit of 1024 if
             either the above macro isn't defined or if sysconf() says
             that the associated limit is indeterminate.
           
17/11/2000 mcs@astro.caltech.edu (Release of version 1.1c)

           getline.c, getline.h, history.c, history.h
             I have modified the way that the history recall functions
             operate, to make them better emulate the behavior of
             tcsh. Previously the history search bindings always
             searched for the prefix that preceded the cursor, then
             left the cursor at the same point in the line, so that a
             following search would search using the same prefix. This
             isn't how tcsh operates. On finding a matching line, tcsh
             puts the cursor at the end of the line, but arranges for
             the followup search to continue with the same prefix,
             unless the user does any cursor motion or character
             insertion operations in between, in which case it changes
             the search prefix to the new set of characters that are
             before the cursor. There are other complications as well,
             which I have attempted to emulate. As far as I can
             tell, the tecla history recall facilities now fully
             emulate those of tcsh.

16/11/2000 mcs@astro.caltech.edu (Release of version 1.1b)

           demo.c:
             One can now quit from the demo by typing exit.

           keytab.c:
             The first entry of the table was getting deleted
             by _kt_clear_bindings() regardless of the source
             of the binding. This deleted the up-arrow binding.
             Symptoms noted by gazelle@yin.interaccess.com.

           getline.h:
             Depending on which system include files were include
             before the inclusion of getline.h, SIGWINCH and
             TIOCGWINSZ might or might not be defined. This resulted
             in different definitions of the GetLine object in
             different files, and thus some very strange bugs! I have
             now added #includes for the necessary system header files
             in getline.h itself. The symptom was that on creating a
             ~/.teclarc file, the demo program complained of a NULL
             argument to kt_set_keybinding() for the first line of the
             file.

15/11/2000 mcs@astro.caltech.edu (Release of version 1.1a)

           demo.c:
             I had neglected to check the return value of
             new_GetLine() in the demo program. Oops.

           getline.c libtecla.h:
             I wrote gl_change_terminal(). This allows one to change to
             a different terminal or I/O stream, by specifying the
             stdio streams to use for input and output, along with the
             type of terminal that they are connected to.

           getline.c libtecla.h:
             Renamed GetLine::isterm to GetLine::is_term. Standard
             C reserves names that start with "is" followed by
             alphanumeric characters, so this avoids potential
             clashes in the future.

           keytab.c keytab.h
             Each key-sequence can now have different binding
             functions from different sources, with the user provided
             binding having the highest precedence, followed by the
             default binding, followed by any terminal specific
             binding. This allows gl_change_terminal() to redefine the
             terminal-specific bindings each time that
             gl_change_terminal() is called, without overwriting the
             user specified or default bindings. In the future, it will
             also allow for reconfiguration of user specified
             bindings after the call to new_GetLine(). Ie. deleting a
             user specified binding should reinstate any default or
             terminal specific binding.

           man/cpl_complete_word.3 html/cpl_complete_word.html
           man/ef_expand_file.3    html/ef_expand_file.html
           man/gl_get_line.3       html/gl_get_line.html
             I added sections on thread safety to the man pages of the
             individual modules.

           man/gl_get_line.3       html/gl_get_line.html
             I documented the new gl_change_terminal() function.

           man/gl_get_line.3       html/gl_get_line.html
             In the description of the ~/.teclarc configuration file,
             I had omitted the 'bind' command word in the example
             entry. I have now remedied this.
xorp/cli/libtecla/html/ef_expand_file.html0000664000076400007640000002255011421137511020770 0ustar greearbgreearb Manual Page

NAME

     ef_expand_file,        del_ExpandFile,        ef_last_error,
     ef_list_expansions,  new_ExpandFile  - expand filenames con-
     taining ~user/$envvar and wildcard expressions

SYNOPSIS

     #include <libtecla.h>

     ExpandFile *new_ExpandFile(void);

     ExpandFile *del_ExpandFile(ExpandFile *ef);

     FileExpansion *ef_expand_file(ExpandFile *ef,
                                   const char *path,
                                   int pathlen);

     int ef_list_expansions(FileExpansion *result, FILE *fp,
                            int term_width);

     const char *ef_last_error(ExpandFile *ef);


DESCRIPTION

     The ef_expand_file() function is part of the  tecla  library
     (see  the  libtecla(3)  man  page).  It  expands a specified
     filename, converting ~user/ and ~/ expressions at the  start
     of  the  filename  to  the  corresponding  home directories,
     replacing  $envvar  with  the  value  of  the  corresponding
     environment  variable, and then, if there are any wildcards,
     matching these against existing  filenames.  Backslashes  in
     the  input  filename are interpreted as escaping any special
     meanings  of  the  characters  that   follow   them.    Only
     backslahes  that  are themselves preceded by backslashes are
     preserved in the expanded filename.

     In the presence of wildcards, the returned list of filenames
     only  includes  the  names of existing files which match the
     wildcards. Otherwise,  the  original  filename  is  returned
     after  expansion  of  tilde  and dollar expressions, and the
     result is not checked against existing  files.  This  mimics
     the file-globbing behavior of the unix tcsh shell.

     The supported wildcards and their meanings are:
       *        -  Match any sequence of zero or more characters.
       ?        -  Match any single character.
       [chars]  -  Match any single character that appears in
                   'chars'.  If 'chars' contains an expression of
                   the form a-b, then any character between a and
                   b, including a and b, matches. The '-'
                   character looses its special meaning as a
                   range specifier when it appears at the start
                   of the sequence of characters. The ']'
                   character also looses its significance as the
                   terminator of the range expression if it
                   appears immediately after the opening '[', at
                   which point it is treated one of the
                   characters of the range. If you want both '-'
                   and ']' to be part of the range, the '-'
                   should come first and the ']' second.

       [^chars] -  The same as [chars] except that it matches any
                   single character that doesn't appear in
                   'chars'.

     Note that wildcards never match the initial dot in filenames
     that  start  with  '.'.  The  initial '.' must be explicitly
     specified in the filename. This again  mimics  the  globbing
     behavior  of  most unix shells, and its rational is based in
     the fact that in unix, files with names that start with  '.'
     are usually hidden configuration files, which are not listed
     by default by the ls command.

     The following is a complete example of how to use  the  file
     expansion function.

       #include <stdio.h>
       #include <libtecla.h>

       int main(int argc, char *argv[])
       {
         ExpandFile *ef;      /* The expansion resource object */
         char *filename;      /* The filename being expanded */
         FileExpansion *expn; /* The results of the expansion */
         int i;

         ef = new_ExpandFile();
         if(!ef)
           return 1;

         for(arg = *(argv++); arg; arg = *(argv++)) {
           if((expn = ef_expand_file(ef, arg, -1)) == NULL) {
             fprintf(stderr, "Error expanding %s (%s).\n", arg,
                              ef_last_error(ef));
           } else {
             printf("%s matches the following files:\n", arg);
             for(i=0; i<expn->nfile; i++)
               printf(" %s\n", expn->files[i]);
           }
         }

         ef = del_ExpandFile(ef);
         return 0;
       }

     Descriptions of the functions used above are as follows:

       ExpandFile *new_ExpandFile(void)

     This  function   creates   the   resources   used   by   the
     ef_expand_file()  function.  In particular, it maintains the
     memory  that  is  used  to  record  the  array  of  matching
     filenames  that  is returned by ef_expand_file(). This array
     is expanded as needed, so there is no built in limit to  the
     number of files that can be matched.

       ExpandFile *del_ExpandFile(ExpandFile *ef)

     This function deletes the resources that were returned by  a
     previous  call  to  new_ExpandFile(). It always returns NULL
     (ie a deleted object). It does nothing if the ef argument is
     NULL.

     A  container  of  the  following   type   is   returned   by
     ef_expand_file().

       typedef struct {
         int exists;   /* True if the files in files[] exist */
         int nfile;    /* The number of files in files[] */
         char **files; /* An array of 'nfile' filenames. */
       } FileExpansion;

       FileExpansion *ef_expand_file(ExpandFile *ef,
                                     const char *path,
                                     int pathlen)

     The ef_expand_file() function performs  filename  expansion,
     as  documented at the start of this section. Its first argu-
     ment is a resource object returned  by  new_ExpandFile().  A
     pointer to the start of the filename to be matched is passed
     via the path argument. This must be a normal NUL  terminated
     string, but unless a length of -1 is passed in pathlen, only
     the first pathlen characters will be used  in  the  filename
     expansion.   If  the length is specified as -1, the whole of
     the string will be expanded.

     The function returns a pointer to a container who's contents
     are the results of the expansion. If there were no wildcards
     in the filename, the nfile member will be 1, and the  exists
     member  should  be queried if it is important to know if the
     expanded file currently exists or not. If there  were  wild-
     cards,  then  the  contained  files[] array will contain the
     names of the nfile existing files  that  matched  the  wild-
     carded  filename,  and the exists member will have the value
     1. Note that the returned container belongs to the specified
     ef  object, and its contents will change on each call, so if
     you need to retain the results of  more  than  one  call  to
     ef_expand_file(),  you  should either make a private copy of
     the returned  results,  or  create  multiple  file-expansion
     resource objects via multiple calls to new_ExpandFile().

     On error, NULL is returned, and an explanation of the  error
     can be determined by calling ef_last_error(ef).

       const char *ef_last_error(ExpandFile *ef)

     This function returns the message which describes the  error
     that  occurred on the last call to ef_expand_file(), for the
     given (ExpandFile *ef) resource object.

       int ef_list_expansions(FileExpansion *result, FILE *fp,
                              int terminal_width);

     The ef_list_expansions() function provides a convenient  way
     to    list    the    filename    expansions    returned   by
     ef_expand_file(). Like the unix ls command, it arranges  the
     filenames  into  equal width columns, each column having the
     width of the largest file. The number  of  columns  used  is
     thus  determined  by the length of the longest filename, and
     the specified terminal width. Beware that filenames that are
     longer than the specified terminal width are printed without
     being truncated, so output longer than the specified  termi-
     nal width can occur. The list is written to the stdio stream
     specified by the fp argument.


THREAD SAFETY

     In multi-threaded programs, you should use the  libtecla_r.a
     version  of the library. This uses POSIX reentrant functions
     where available (hence the _r suffix), and disables features
     that rely on non-reentrant system functions. Currently there
     are no features disabled in this module.

     Using the libtecla_r.a version of the library, it is safe to
     use  the facilities of this module in multiple threads, pro-
     vided that each thread uses a separately  allocated  Expand-
     File  object. In other words, if two threads want to do file
     expansion, they should each call new_ExpandFile()  to  allo-
     cate their own file-expansion objects.


FILES

     libtecla.a    -    The tecla library
     libtecla.h    -    The tecla header file.


SEE ALSO

     libtecla(3),      gl_get_line(3),      cpl_complete_word(3),
     pca_lookup_file(3)

AUTHOR

     Martin Shepherd  (mcs@astro.caltech.edu)


















































xorp/cli/libtecla/html/cpl_complete_word.html0000664000076400007640000004312211421137511021537 0ustar greearbgreearb Manual Page

NAME

     cpl_complete_word,   cfc_file_start,    cfc_literal_escapes,
     cfc_set_check_fn,  cpl_add_completion, cpl_file_completions,
     cpl_last_error,   cpl_list_completions,    cpl_record_error,
     del_CplFileConf,     del_WordCompletion,    new_CplFileConf,
     new_WordCompletion - lookup possible completions for a word

SYNOPSIS

     #include <stdio.h>
     #include <libtecla.h>

     WordCompletion *new_WordCompletion(void);

     WordCompletion *del_WordCompletion(WordCompletion *cpl);

     #define CPL_MATCH_FN(fn) int (fn)(WordCompletion *cpl, \
                                       void *data, \
                                       const char *line, \
                                       int word_end)
     typedef CPL_MATCH_FN(CplMatchFn);

     CPL_MATCH_FN(cpl_file_completions);

     CplMatches *cpl_complete_word(WordCompletion *cpl,
                                   const char *line,
                                   int word_end, void *data,
                                   CplMatchFn *match_fn);

     int cpl_list_completions(CplMatches *result, FILE *fp,
                              int term_width);

     int cpl_add_completion(WordCompletion *cpl,
                            const char *line, int word_start,
                            int word_end, const char *suffix,
                            const char *type_suffix,
                            const char *cont_suffix);

     void cpl_record_error(WordCompletion *cpl,
                           const char *errmsg);

     const char *cpl_last_error(WordCompletion *cpl);



DESCRIPTION

     The  cpl_complete_word()  function  is  part  of  the  tecla
     library (see the libtecla(3) man page). It is usually called
     behind the scenes by gl_get_line(3), but can also be  called
     separately.

     Given an input line containing an incomplete word to be com-
     pleted,  it  calls a user-provided callback function (or the
     provided file-completion callback function) to look  up  all
     possible  completion  suffixes  for  that word. The callback
     function is expected to look backward in the line,  starting
     from the specified cursor position, to find the start of the
     word to be completed, then to look up all  possible  comple-
     tions of that word and record them, one at a time by calling
     cpl_add_completion().


     Descriptions of the functions of this module are as follows:

       CompleteWord *new_CompleteWord(void)

     This  function   creates   the   resources   used   by   the
     cpl_complete_word()  function.  In  particular, it maintains
     the memory that is used to return  the  results  of  calling
     cpl_complete_word().

       CompleteWord *del_CompleteWord(CompleteWord *cpl)

     This function deletes the resources that were returned by  a
     previous  call to new_CompleteWord(). It always returns NULL
     (ie. a deleted object). It does nothing if the cpl  argument
     is NULL.

     The callback functions  which  lookup  possible  completions
     should be defined with the following macro (which is defined
     in libtecla.h).

       #define CPL_MATCH_FN(fn) int (fn)(WordCompletion *cpl, \
                                         void *data, \
                                         const char *line, \
                                         int word_end)

     Functions of this type are  called  by  cpl_complete_word(),
     and all of the arguments of the callback are those that were
     passed to said function. In particular,  the  line  argument
     contains the input line containing the word to be completed,
     and word_end is the index of the character that follows  the
     last  character  of  the incomplete word within this string.
     The callback is expected to look backwards from word_end for
     the start of the incomplete word. What constitutes the start
     of a word clearly depends on the application,  so  it  makes
     sense  for  the callback to take on this responsibility. For
     example, the  builtin  filename  completion  function  looks
     backwards  until it hits an unescaped space, or the start of
     the line.  Having found the start of the word, the  callback
     should  then  lookup  all possible completions of this word,
     and  record  each   completion   via   separate   calls   to
     cpl_add_completion().  If  the  callback  needs access to an
     application-specific symbol table, it can pass  it  and  any
     other  data  that  it  needs,  via  the  data argument. This
     removes any need for globals.

     The callback function should return 0 if no errors occur. On
     failure it should return 1, and register a terse description
     of the error by calling cpl_record_error().

       void cpl_record_error(WordCompletion *cpl,
                             const char *errmsg);

     The    last    error    message    recorded    by    calling
     cpl_record_error(),  can  subsequently be queried by calling
     cpl_last_error(), as described later.

       int cpl_add_completion(WordCompletion *cpl,
                              const char *line, int word_start,
                              int word_end, const char *suffix,
                              const char *type_suffix,
                              const char *cont_suffix);

     The cpl_add_completion() function is  called  zero  or  more
     times  by  the  completion  callback function to record each
     possible completion in the specified WordCompletion  object.
     These    completions    are    subsequently    returned   by
     cpl_complete_word(), as described later. The cpl, line,  and
     word_end  arguments  should be those that were passed to the
     callback function. The word_start  argument  should  be  the
     index  within the input line string of the start of the word
     that is being completed. This should  equal  word_end  if  a
     zero-length  string  is being completed. The suffix argument
     is the string that would have to be appended to  the  incom-
     plete  word  to complete it.  If this needs any quoting (eg.
     the addition of backslashes before special charaters) to  be
     valid  within  the  displayed  input  line,  this  should be
     included. A copy of the suffix string  is  allocated  inter-
     nally,  so  there  is  no  need to maintain your copy of the
     string after cpl_add_completion() returns.

     Note that in the array of  possible  completions  which  the
     cpl_complete_word() function returns, the suffix recorded by
     cpl_add_completion() is listed along with the concatentation
     of  this  suffix  with the word that lies between word_start
     and word_end in the input line.

     The type_suffix argument specifies an optional string to  be
     appended  to  the completion if it is displayed as part of a
     list of completions by cpl_list_completions(). The intention
     is  that  this indicate to the user the type of each comple-
     tion. For example, the file  completion  function  places  a
     directory  separator after completions that are directories,
     to indicate their nature to the user. Similary, if the  com-
     pletion were a function, you could indicate this to the user
     by setting type_suffix to "()". Note  that  the  type_suffix
     string  isn't  copied,  so  if  the argument isn't a literal
     string between speech marks, be sure that the string remains
     valid   for   at   least   as   long   as   the  results  of
     cpl_complete_word() are needed.

     The cont_suffix is a continuation suffix to  append  to  the
     completed word in the input line if this is the only comple-
     tion. This is something that isn't part  of  the  completion
     itself, but that gives the user an indication about how they
     might continue to extend the token.  For example, the  file-
     completion  callback  function adds a directory separator if
     the completed word is a directory.  If  the  completed  word
     were  a  function  name, you could similarly aid the user by
     arranging for an open parenthesis to be appended.

       CplMatches *cpl_complete_word(WordCompletion *cpl,
                                     const char *line,
                                     int word_end, void *data,
                                     CplMatchFn *match_fn);

     The cpl_complete_word() is normally called behind the scenes
     by  gl_get_line(3), but can also be called separately if you
     separately allocate a  WordCompletion  object.  It  performs
     word  completion, as described at the beginning of this sec-
     tion. Its first argument is  a  resource  object  previously
     returned  by  new_CompleteWord().   The line argument is the
     input line string, containing the word to be completed.  The
     word_end argument contains the index of the character in the
     input line, that just follows the last character of the word
     to  be  completed. When called by gl_get_line(), this is the
     character over which the  user  pressed  TAB.  The  match_fn
     argument  is  the  function pointer of the callback function
     which will lookup  possible  completions  of  the  word,  as
     described  above,  and  the data argument provides a way for
     the application to pass arbitrary data to the callback func-
     tion.

     If no errors occur, the cpl_complete_word() function returns
     a  pointer to a CplMatches container, as defined below. This
     container is allocated as part of the cpl  object  that  was
     passed  to cpl_complete_word(), and will thus change on each
     call which uses the same cpl argument.

       typedef struct {
         char *completion;        /* A matching completion */
                                  /*  string */
         char *suffix;            /* The part of the */
                                  /*  completion string which */
                                  /*  would have to be */
                                  /*  appended to complete the */
                                  /*  original word. */
         const char *type_suffix; /* A suffix to be added when */
                                  /*  listing completions, to */
                                  /*  indicate the type of the */
                                  /*  completion. */
       } CplMatch;

       typedef struct {
         char *suffix;            /* The common initial part */
                                  /*  of all of the completion */
                                  /*  suffixes. */
         const char *cont_suffix; /* Optional continuation */
                                  /*  string to be appended to */
                                  /*  the sole completion when */
                                  /*  nmatch==1. */
         CplMatch *matches;       /* The array of possible */
                                  /*  completion strings, */
                                  /*  sorted into lexical */
                                  /*  order. */
         int nmatch;              /* The number of elements in */
                                  /*  the above matches[] */
                                  /*  array. */
       } CplMatches;

     If an error occurs  during  completion,  cpl_complete_word()
     returns  NULL. A description of the error can be acquired by
     calling the cpl_last_error() function.

       const char *cpl_last_error(WordCompletion *cpl);

     The cpl_last_error() function returns a terse description of
     the   error   which   occurred   on   the   last   call   to
     cpl_complete_word() or cpl_add_completion().

       int cpl_list_completions(CplMatches *result, FILE *fp,
                                int terminal_width);

     When the cpl_complete_word() function returns multiple  pos-
     sible  completions,  the cpl_list_completions() function can
     be called upon to list them, suitably  arranged  across  the
     available  width  of  the  terminal.  It  arranges  for  the
     displayed columns of completions to all have the same width,
     set   by   the  longest  completion.  It  also  appends  the
     type_suffix strings that were recorded with each completion,
     thus indicating their types to the user.


THE BUILT-IN FILENAME-COMPLETION CALLBACK

     By default the gl_get_line(3) function, passes the following
     completion  callback  function  to cpl_complete_word(). This
     function can also be used separately, either by  sending  it
     to  cpl_complete_word(), or by calling it directly from your
     own completion callback function.

       CPL_MATCH_FN(cpl_file_completions);

     Certain aspects of the behavior  of  this  callback  can  be
     changed  via  its  data  argument. If you are happy with its
     default behavior you can pass NULL in this argument.  Other-
     wise  it should be a pointer to a CplFileConf object, previ-
     ously allocated by calling new_CplFileConf().

       CplFileConf *new_CplFileConf(void);

     CplFileConf objects encapsulate the configuration parameters
     of cpl_file_completions(). These parameters, which start out
     with default values, can be changed by calling the  accessor
     functions described below.

     By default,  the  cpl_file_completions()  callback  function
     searches  backwards for the start of the filename being com-
     pleted, looking for the first un-escaped space or the  start
     of  the input line. If you wish to specify a different loca-
     tion, call cfc_file_start() with  the  index  at  which  the
     filename  starts  in  the input line. Passing start_index=-1
     re-enables the default behavior.

       void cfc_file_start(CplFileConf *cfc, int start_index);

     By default, when cpl_file_completions() looks at a  filename
     in  the input line, each lone backslash in the input line is
     interpreted as being a special character which  removes  any
     special significance of the character which follows it, such
     as a space which should be taken as  part  of  the  filename
     rather  than  delimiting  the  start  of the filename. These
     backslashes are thus ignored while looking for  completions,
     and  subsequently  added  before  spaces,  tabs  and literal
     backslashes in the list of completions.  To  have  unescaped
     backslashes    treated    as    normal    characters,   call
     cfc_literal_escapes() with a non-zero value in  its  literal
     argument.

       void cfc_literal_escapes(CplFileConf *cfc, int literal);

     By default, cpl_file_completions() reports all  files  who's
     names  start with the prefix that is being completed. If you
     only want a selected subset of these files to be reported in
     the list of completions, you can arrange this by providing a
     callback function which takes the full pathname of  a  file,
     and  returns  0  if  the file should be ignored, or 1 if the
     file should be included  in  the  list  of  completions.  To
     register  such a function for use by cpl_file_completions(),
     call cfc_set_check_fn(), and pass it a pointer to the  func-
     tion,  together  with  a  pointer to any data that you would
     like passed to this callback whenever  it  is  called.  Your
     callback can make its decisions based on any property of the
     file, such as the filename itself, whether the file is read-
     able, writable or executable, or even based on what the file
     contains.

       #define CPL_CHECK_FN(fn) int (fn)(void *data, \
                                         const char *pathname)
       typedef CPL_CHECK_FN(CplCheckFn);

       void cfc_set_check_fn(CplFileConf *cfc,
                             CplCheckFn *chk_fn, void *chk_data);

     The cpl_check_exe() function is a provided callback  of  the
     above  type, for use with cpl_file_completions(). It returns
     non-zero if the filename that it is given represents a  nor-
     mal  file that the user has execute permission to. You could
     use this to have cpl_file_completions()  only  list  comple-
     tions of executable files.

     When you have finished with a CplFileConf variable, you  can
     pass  it  to  the  del_CplFileConf()  destructor function to
     reclaim its memory.

       CplFileConf *del_CplFileConf(CplFileConf *cfc);



THREAD SAFETY

     In multi-threaded programs, you should use the  libtecla_r.a
     version  of the library. This uses POSIX reentrant functions
     where available (hence the _r suffix), and disables features
     that  rely on non-reentrant system functions. In the case of
     this module, the only disabled feature is  username  comple-
     tion in ~username/ expressions, in cpl_file_completions().

     Using the libtecla_r.a version of the library, it is safe to
     use  the facilities of this module in multiple threads, pro-
     vided that each thread uses a separately allocated  WordCom-
     pletion  object.  In  other words, if two threads want to do
     word completion, they should each call  new_WordCompletion()
     to allocate their own completion objects.


FILES

     libtecla.a    -    The tecla library
     libtecla.h    -    The tecla header file.


SEE ALSO

     libtecla(3),       gl_get_line(3),        ef_expand_file(3),
     pca_lookup_file(3)


AUTHOR

     Martin Shepherd  (mcs@astro.caltech.edu)


















































xorp/cli/libtecla/pcache.c0000664000076400007640000015223311421137511015577 0ustar greearbgreearb/* * Copyright (c) 2000, 2001 by Martin C. Shepherd. * * All rights reserved. * * 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, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, and/or sell copies of the Software, and to permit persons * to whom the Software is furnished to do so, provided that the above * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Except as contained in this notice, the name of a copyright holder * shall not be used in advertising or otherwise to promote the sale, use * or other dealings in this Software without prior written authorization * of the copyright holder. */ #include #include #include #include "libtecla.h" #include "pathutil.h" #include "homedir.h" #include "freelist.h" #include "direader.h" #include "stringrp.h" /* * The new_PcaPathConf() constructor sets the integer first member of * the returned object to the following magic number. This is then * checked for by pca_path_completions() as a sanity check. */ #define PPC_ID_CODE 4567 /* * A pointer to a structure of the following type can be passed to * the builtin path-completion callback function to modify its behavior. */ struct PcaPathConf { int id; /* This is set to PPC_ID_CODE by new_PcaPathConf() */ PathCache *pc; /* The path-list cache in which to look up the executables */ int escaped; /* If non-zero, backslashes in the input line are */ /* interpreted as escaping special characters and */ /* spaces, and any special characters and spaces in */ /* the listed completions will also be escaped with */ /* added backslashes. This is the default behaviour. */ /* If zero, backslashes are interpreted as being */ /* literal parts of the file name, and none are added */ /* to the completion suffixes. */ int file_start; /* The index in the input line of the first character */ /* of the file name. If you specify -1 here, */ /* pca_path_completions() identifies the */ /* the start of the file by looking backwards for */ /* an unescaped space, or the beginning of the line. */ }; /* * Prepended to each chached filename is a character which contains * one of the following status codes. When a given filename (minus * this byte) is passed to the application's check_fn(), the result * is recorded in this byte, such that the next time it is looked * up, we don't have to call check_fn() again. These codes are cleared * whenever the path is scanned and whenever the check_fn() callback * is changed. */ typedef enum { PCA_F_ENIGMA='?', /* The file remains to be checked */ PCA_F_WANTED='+', /* The file has been selected by the caller's callback */ PCA_F_IGNORE='-' /* The file has been rejected by the caller's callback */ } PcaFileStatus; /* * Encapsulate the memory management objects which supply memoy for * the arrays of filenames. */ typedef struct { StringGroup *sg; /* The memory used to record the names of files */ size_t files_dim; /* The allocated size of files[] */ char **files; /* Memory for 'files_dim' pointers to files */ size_t nfiles; /* The number of filenames currently in files[] */ } CacheMem; static CacheMem *new_CacheMem(void); static CacheMem *del_CacheMem(CacheMem *cm); static void rst_CacheMem(CacheMem *cm); /* * Lists of nodes of the following type are used to record the * names and contents of individual directories. */ typedef struct PathNode PathNode; struct PathNode { PathNode *next; /* The next directory in the path */ int relative; /* True if the directory is a relative pathname */ CacheMem *mem; /* The memory used to store dir[] and files[] */ char *dir; /* The directory pathname (stored in pc->sg) */ int nfile; /* The number of filenames stored in 'files' */ char **files; /* Files of interest in the current directory, */ /* or NULL if dir[] is a relative pathname */ /* who's contents can't be cached. This array */ /* and its contents are taken from pc->abs_mem */ /* or pc->rel_mem */ }; /* * Append a new node to the list of directories in the path. */ static int add_PathNode(PathCache *pc, const char *dirname); /* * Set the max length of the error-reporting string. There is no point * in this being longer than the width of a typical terminal window. * In composing error messages, I have assumed that this number is * at least 80, so you don't decrease it below this number. */ #define ERRLEN 200 /* * Set the maximum length allowed for usernames. * names. */ #define USR_LEN 100 /* * PathCache objects encapsulate the resources needed to record * files of interest from comma-separated lists of directories. */ struct PathCache { FreeList *node_mem; /* A free-list of PathNode objects */ CacheMem *abs_mem; /* Memory for the filenames of absolute paths */ CacheMem *rel_mem; /* Memory for the filenames of relative paths */ PathNode *head; /* The head of the list of directories in the */ /* path, or NULL if no path has been scanned yet. */ PathNode *tail; /* The tail of the list of directories in the */ /* path, or NULL if no path has been scanned yet. */ PathName *path; /* The fully qualified name of a file */ HomeDir *home; /* Home-directory lookup object */ DirReader *dr; /* A portable directory reader */ CplFileConf *cfc; /* Configuration parameters to pass to */ /* cpl_file_completions() */ CplCheckFn *check_fn; /* The callback used to determine if a given */ /* filename should be recorded in the cache. */ void *data; /* Annonymous data to be passed to pc->check_fn() */ char usrnam[USR_LEN+1];/* The buffer used when reading the names of */ /* users. */ char errmsg[ERRLEN+1]; /* Error-report buffer */ }; /* * Empty the cache. */ static void pca_clear_cache(PathCache *pc); /* * Read a username from string[] and record it in pc->usrnam[]. */ static int pca_read_username(PathCache *pc, const char *string, int slen, int literal, const char **nextp); /* * Extract the next component of a colon separated list of directory * paths. */ static int pca_extract_dir(PathCache *pc, const char *path, const char **nextp); /* * Scan absolute directories for files of interest, recording their names * in mem->sg and recording pointers to these names in mem->files[]. */ static int pca_scan_dir(PathCache *pc, const char *dirname, CacheMem *mem); /* * A qsort() comparison function for comparing the cached filename * strings pointed to by two (char **) array elements. Note that * this ignores the initial cache-status byte of each filename. */ static int pca_cmp_matches(const void *v1, const void *v2); /* * A qsort() comparison function for comparing a filename * against an element of an array of pointers to filename cache * entries. */ static int pca_cmp_file(const void *v1, const void *v2); /* * Initialize a PcaPathConf configuration objects with the default * options. */ static int pca_init_PcaPathConf(PcaPathConf *ppc, PathCache *pc); /* * Make a copy of a completion suffix, suitable for passing to * cpl_add_completion(). */ static int pca_prepare_suffix(PathCache *pc, const char *suffix, int add_escapes); /* * Return non-zero if the specified string appears to start with a pathname. */ static int cpa_cmd_contains_path(const char *prefix, int prefix_len); /* * Return a given prefix with escapes optionally removed. */ static const char *pca_prepare_prefix(PathCache *pc, const char *prefix, size_t prefix_len, int escaped); /* * If there is a tilde expression at the beginning of the specified path, * place the corresponding home directory into pc->path. Otherwise * just clear pc->path. */ static int pca_expand_tilde(PathCache *pc, const char *path, int pathlen, int literal, const char **endp); /* * Clear the filename status codes that are recorded before each filename * in the cache. */ static void pca_remove_marks(PathCache *pc); /* * Specify how many PathNode's to allocate at a time. */ #define PATH_NODE_BLK 30 /* * Specify the amount by which the files[] arrays are to be extended * whenever they are found to be too small. */ #define FILES_BLK_FACT 256 /*....................................................................... * Create a new object who's function is to maintain a cache of * filenames found within a list of directories, and provide quick * lookup and completion of selected files in this cache. * * Output: * return PathCache * The new, initially empty cache, or NULL * on error. */ PathCache *new_PathCache(void) { PathCache *pc; /* The object to be returned */ /* * Allocate the container. */ pc = (PathCache *)malloc(sizeof(PathCache)); if(!pc) { fprintf(stderr, "new_PathCache: Insufficient memory.\n"); return NULL; }; /* * Before attempting any operation that might fail, initialize the * container at least up to the point at which it can safely be passed * to del_PathCache(). */ pc->node_mem = NULL; pc->abs_mem = NULL; pc->rel_mem = NULL; pc->head = NULL; pc->tail = NULL; pc->path = NULL; pc->home = NULL; pc->dr = NULL; pc->cfc = NULL; pc->check_fn = 0; pc->data = NULL; pc->usrnam[0] = '\0'; pc->errmsg[0] = '\0'; /* * Allocate the freelist of directory list nodes. */ pc->node_mem = _new_FreeList("new_PathCache", sizeof(PathNode), PATH_NODE_BLK); if(!pc->node_mem) return del_PathCache(pc); /* * Allocate memory for recording names of files in absolute paths. */ pc->abs_mem = new_CacheMem(); if(!pc->abs_mem) return del_PathCache(pc); /* * Allocate memory for recording names of files in relative paths. */ pc->rel_mem = new_CacheMem(); if(!pc->rel_mem) return del_PathCache(pc); /* * Allocate a pathname buffer. */ pc->path = _new_PathName(); if(!pc->path) return del_PathCache(pc); /* * Allocate an object for looking up home-directories. */ pc->home = _new_HomeDir(); if(!pc->home) return del_PathCache(pc); /* * Allocate an object for reading directories. */ pc->dr = _new_DirReader(); if(!pc->dr) return del_PathCache(pc); /* * Allocate a cpl_file_completions() configuration object. */ pc->cfc = new_CplFileConf(); if(!pc->cfc) return del_PathCache(pc); /* * Configure cpl_file_completions() to use check_fn() to select * files of interest. */ cfc_set_check_fn(pc->cfc, pc->check_fn, pc->data); /* * Return the cache, ready for use. */ return pc; } /*....................................................................... * Delete a given cache of files, returning the resources that it * was using to the system. * * Input: * pc PathCache * The cache to be deleted (can be NULL). * Output: * return PathCache * The deleted object (ie. allways NULL). */ PathCache *del_PathCache(PathCache *pc) { if(pc) { /* * Delete the memory of the list of path nodes. */ pc->node_mem = _del_FreeList(NULL, pc->node_mem, 1); /* * Delete the memory used to record filenames. */ pc->abs_mem = del_CacheMem(pc->abs_mem); pc->rel_mem = del_CacheMem(pc->rel_mem); /* * The list of PathNode's was already deleted when node_mem was * deleted. */ pc->head = NULL; pc->tail = NULL; /* * Delete the pathname buffer. */ pc->path = _del_PathName(pc->path); /* * Delete the home-directory lookup object. */ pc->home = _del_HomeDir(pc->home); /* * Delete the directory reader. */ pc->dr = _del_DirReader(pc->dr); /* * Delete the cpl_file_completions() config object. */ pc->cfc = del_CplFileConf(pc->cfc); /* * Delete the container. */ free(pc); }; return NULL; } /*....................................................................... * If you want subsequent calls to pca_lookup_file() and * pca_path_completions() to only return the filenames of certain * types of files, for example executables, or filenames ending in * ".ps", call this function to register a file-selection callback * function. This callback function takes the full pathname of a file, * plus application-specific data, and returns 1 if the file is of * interest, and zero otherwise. * * Input: * pc PathCache * The filename cache. * check_fn CplCheckFn * The function to call to see if the name of * a given file should be included in the * cache. This determines what type of files * will reside in the cache. To revert to * selecting all files, regardless of type, * pass 0 here. * data void * You can pass a pointer to anything you * like here, including NULL. It will be * passed to your check_fn() callback * function, for its private use. */ void pca_set_check_fn(PathCache *pc, CplCheckFn *check_fn, void *data) { if(pc) { /* * If the callback or its data pointer have changed, clear the cached * statuses of files that were accepted or rejected by the previous * calback. */ if(check_fn != pc->check_fn || data != pc->data) pca_remove_marks(pc); /* * Record the new callback locally. */ pc->check_fn = check_fn; pc->data = data; /* * Configure cpl_file_completions() to use the same callback to * select files of interest. */ cfc_set_check_fn(pc->cfc, check_fn, data); }; return; } /*....................................................................... * Return a description of the last path-caching error that occurred. * * Input: * pc PathCache * The filename cache that suffered the error. * Output: * return char * The description of the last error. */ const char *pca_last_error(PathCache *pc) { return pc ? pc->errmsg : "NULL PathCache argument"; } /*....................................................................... * Discard all cached filenames. * * Input: * pc PathCache * The cache to be cleared. */ static void pca_clear_cache(PathCache *pc) { if(pc) { /* * Return all path-nodes to the freelist. */ _rst_FreeList(pc->node_mem); pc->head = pc->tail = NULL; /* * Delete all filename strings. */ rst_CacheMem(pc->abs_mem); rst_CacheMem(pc->rel_mem); }; return; } /*....................................................................... * Build the list of files of interest contained in a given * colon-separated list of directories. * * Input: * pc PathCache * The cache in which to store the names of * the files that are found in the list of * directories. * path const char * A colon-separated list of directory * paths. Under UNIX, when searching for * executables, this should be the return * value of getenv("PATH"). * Output: * return int 0 - OK. * 1 - An error occurred. A description of * the error can be acquired by calling * pca_last_error(pc). */ int pca_scan_path(PathCache *pc, const char *path) { const char *pptr; /* A pointer to the next unprocessed character in path[] */ PathNode *node; /* A node in the list of directory paths */ char **fptr; /* A pointer into pc->abs_mem->files[] */ /* * Check the arguments. */ if(!pc) return 1; /* * Clear the outdated contents of the cache. */ pca_clear_cache(pc); /* * If no path list was provided, there is nothing to be added to the * cache. */ if(!path) return 0; /* * Extract directories from the path list, expanding tilde expressions * on the fly into pc->pathname, then add them to the list of path * nodes, along with a sorted list of the filenames of interest that * the directories hold. */ pptr = path; while(*pptr) { /* * Extract the next pathname component into pc->path->name. */ if(pca_extract_dir(pc, pptr, &pptr)) return 1; /* * Add a new node to the list of paths, containing both the * directory name and, if not a relative pathname, the list of * files of interest in the directory. */ if(add_PathNode(pc, pc->path->name)) return 1; }; /* * The file arrays in each absolute directory node are sections of * pc->abs_mem->files[]. Record pointers to the starts of each * of these sections in each directory node. Note that this couldn't * be done in add_PathNode(), because pc->abs_mem->files[] may * get reallocated in subsequent calls to add_PathNode(), thus * invalidating any pointers to it. */ fptr = pc->abs_mem->files; for(node=pc->head; node; node=node->next) { node->files = fptr; fptr += node->nfile; }; return 0; } /*....................................................................... * Extract the next directory path from a colon-separated list of * directories, expanding tilde home-directory expressions where needed. * * Input: * pc PathCache * The cache of filenames. * path const char * A pointer to the start of the next component * in the path list. * Input/Output: * nextp const char ** A pointer to the next unprocessed character * in path[] will be assigned to *nextp. * Output: * return int 0 - OK. The extracted path is in pc->path->name. * 1 - Error. A description of the error will * have been left in pc->errmsg. */ static int pca_extract_dir(PathCache *pc, const char *path, const char **nextp) { const char *pptr; /* A pointer into path[] */ const char *sptr; /* The path following tilde expansion */ int escaped = 0; /* True if the last character was a backslash */ /* * If there is a tilde expression at the beginning of the specified path, * place the corresponding home directory into pc->path. Otherwise * just clear pc->path. */ if(pca_expand_tilde(pc, path, strlen(path), 0, &pptr)) return 1; /* * Keep a record of the current location in the path. */ sptr = pptr; /* * Locate the end of the directory name in the pathname string, stopping * when either the end of the string is reached, or an un-escaped colon * separator is seen. */ while(*pptr && (escaped || *pptr != ':')) escaped = !escaped && *pptr++ == '\\'; /* * Append the rest of the directory path to the pathname buffer. */ if(_pn_append_to_path(pc->path, sptr, pptr - sptr, 1) == NULL) { strncpy(pc->errmsg, "Insufficient memory to record directory name", sizeof(pc->errmsg)); return 1; }; /* * To facilitate subsequently appending filenames to the directory * path name, make sure that the recorded directory name ends in a * directory separator. */ { int dirlen = strlen(pc->path->name); if(dirlen < FS_DIR_SEP_LEN || strncmp(pc->path->name + dirlen - FS_DIR_SEP_LEN, FS_DIR_SEP, FS_DIR_SEP_LEN) != 0) { if(_pn_append_to_path(pc->path, FS_DIR_SEP, FS_DIR_SEP_LEN, 0) == NULL) { strncpy(pc->errmsg, "Insufficient memory to record directory name", sizeof(pc->errmsg)); return 1; }; }; }; /* * Skip the separator unless we have reached the end of the path. */ if(*pptr==':') pptr++; /* * Return the unprocessed tail of the path-list string. */ *nextp = pptr; return 0; } /*....................................................................... * Read a username, stopping when a directory separator is seen, a colon * separator is seen, the end of the string is reached, or the username * buffer overflows. * * Input: * pc PathCache * The cache of filenames. * string char * The string who's prefix contains the name. * slen int The max number of characters to read from string[]. * literal int If true, treat backslashes as literal characters * instead of escapes. * Input/Output: * nextp char ** A pointer to the next unprocessed character * in string[] will be assigned to *nextp. * Output: * return int 0 - OK. The username can be found in pc->usrnam. * 1 - Error. A description of the error message * can be found in pc->errmsg. */ static int pca_read_username(PathCache *pc, const char *string, int slen, int literal, const char **nextp) { int usrlen; /* The number of characters in pc->usrnam[] */ const char *sptr; /* A pointer into string[] */ int escaped = 0; /* True if the last character was a backslash */ /* * Extract the username. */ for(sptr=string,usrlen=0; usrlen < USR_LEN && (sptr-string) < slen; sptr++) { /* * Stop if the end of the string is reached, or a directory separator * or un-escaped colon separator is seen. */ if(!*sptr || strncmp(sptr, FS_DIR_SEP, FS_DIR_SEP_LEN)==0 || (!escaped && *sptr == ':')) break; /* * Escape the next character? */ if(!literal && !escaped && *sptr == '\\') { escaped = 1; } else { escaped = 0; pc->usrnam[usrlen++] = *sptr; }; }; /* * Did the username overflow the buffer? */ if(usrlen >= USR_LEN) { strncpy(pc->errmsg, "Username too long", sizeof(pc->errmsg)); return 1; }; /* * Terminate the string. */ pc->usrnam[usrlen] = '\0'; /* * Indicate where processing of the input string should continue. */ *nextp = sptr; return 0; } /*....................................................................... * Create a new CacheMem object. * * Output: * return CacheMem * The new object, or NULL on error. */ static CacheMem *new_CacheMem(void) { CacheMem *cm; /* The object to be returned */ /* * Allocate the container. */ cm = (CacheMem *)malloc(sizeof(CacheMem)); if(!cm) { fprintf(stderr, "new_CacheMem: Insufficient memory.\n"); return NULL; }; /* * Before attempting any operation that might fail, initialize the * container at least up to the point at which it can safely be passed * to del_CacheMem(). */ cm->sg = NULL; cm->files_dim = 0; cm->files = NULL; cm->nfiles = 0; /* * Allocate a list of string segments for storing filenames. */ cm->sg = _new_StringGroup(_pu_pathname_dim()); if(!cm->sg) return del_CacheMem(cm); /* * Allocate an array of pointers to filenames. * This will be extended later if needed. */ cm->files_dim = FILES_BLK_FACT; cm->files = (char **) malloc(sizeof(*cm->files) * cm->files_dim); if(!cm->files) { fprintf(stderr, "new_CacheMem: Insufficient memory to allocate array of files.\n"); return del_CacheMem(cm); }; return cm; } /*....................................................................... * Delete a CacheMem object. * * Input: * cm CacheMem * The object to be deleted. * Output: * return CacheMem * The deleted object (always NULL). */ static CacheMem *del_CacheMem(CacheMem *cm) { if(cm) { /* * Delete the memory that was used to record filename strings. */ cm->sg = _del_StringGroup(cm->sg); /* * Delete the array of pointers to filenames. */ cm->files_dim = 0; if(cm->files) { free(cm->files); cm->files = NULL; }; /* * Delete the container. */ free(cm); }; return NULL; } /*....................................................................... * Re-initialize the memory used to allocate filename strings. * * Input: * cm CacheMem * The memory cache to be cleared. */ static void rst_CacheMem(CacheMem *cm) { _clr_StringGroup(cm->sg); cm->nfiles = 0; return; } /*....................................................................... * Append a new directory node to the list of directories read from the * path. * * Input: * pc PathCache * The filename cache. * dirname const char * The name of the new directory. * Output: * return int 0 - OK. * 1 - Error. */ static int add_PathNode(PathCache *pc, const char *dirname) { PathNode *node; /* The new directory list node */ int relative; /* True if dirname[] is a relative pathname */ /* * Have we been passed a relative pathname or an absolute pathname? */ relative = strncmp(dirname, FS_ROOT_DIR, FS_ROOT_DIR_LEN) != 0; /* * If it's an absolute pathname, ignore it if the corresponding * directory doesn't exist. */ if(!relative && !_pu_path_is_dir(dirname)) return 0; /* * Allocate a new list node to record the specifics of the new directory. */ node = (PathNode *) _new_FreeListNode(pc->node_mem); if(!node) { snprintf(pc->errmsg, sizeof(pc->errmsg), "Insufficient memory to cache new directory."); return 1; }; /* * Initialize the node. */ node->next = NULL; node->relative = relative; node->mem = relative ? pc->rel_mem : pc->abs_mem; node->dir = NULL; node->nfile = 0; node->files = NULL; /* * Make a copy of the directory pathname. */ node->dir = _sg_store_string(pc->abs_mem->sg, dirname, 0); if(!node->dir) { strncpy(pc->errmsg, "Insufficient memory to store directory name.", sizeof(pc->errmsg)); return 1; }; /* * Scan absolute directories for files of interest, recording their names * in node->mem->sg and appending pointers to these names to the * node->mem->files[] array. */ if(!node->relative) { int nfile = node->nfile = pca_scan_dir(pc, node->dir, node->mem); if(nfile < 1) { /* No files matched or an error occurred */ node = (PathNode *) _del_FreeListNode(pc->node_mem, node); return nfile < 0; }; }; /* * Append the new node to the list. */ if(pc->head) { pc->tail->next = node; pc->tail = node; } else { pc->head = pc->tail = node; }; return 0; } /*....................................................................... * Scan a given directory for files of interest, record their names * in mem->sg and append pointers to them to the mem->files[] array. * * Input: * pc PathCache * The filename cache. * dirname const char * The pathname of the directory to be scanned. * mem CacheMem * The memory in which to store filenames of * interest. * Output: * return int The number of files recorded, or -1 if a * memory error occurs. Note that the * inability to read the contents of the * directory is not counted as an error. */ static int pca_scan_dir(PathCache *pc, const char *dirname, CacheMem *mem) { int nfile = 0; /* The number of filenames recorded */ const char *filename; /* The name of the file being looked at */ /* * Attempt to open the directory. If the directory can't be read then * there are no accessible files of interest in the directory. */ if(_dr_open_dir(pc->dr, dirname, NULL)) return 0; /* * Record the names of all files in the directory in the cache. */ while((filename = _dr_next_file(pc->dr))) { char *copy; /* A copy of the filename */ /* * Make a temporary copy of the filename with an extra byte prepended. */ _pn_clear_path(pc->path); if(_pn_append_to_path(pc->path, " ", 1, 0) == NULL || _pn_append_to_path(pc->path, filename, -1, 1) == NULL) { strncpy(pc->errmsg, "Insufficient memory to record filename", sizeof(pc->errmsg)); return -1; }; /* * Store the filename. */ copy = _sg_store_string(mem->sg, pc->path->name, 0); if(!copy) { strncpy(pc->errmsg, "Insufficient memory to cache file name.", sizeof(pc->errmsg)); return -1; }; /* * Mark the filename as unchecked. */ copy[0] = PCA_F_ENIGMA; /* * Make room to store a pointer to the copy in mem->files[]. */ if(mem->nfiles + 1 > mem->files_dim) { int needed = mem->files_dim + FILES_BLK_FACT; char **files = (char **) realloc(mem->files, sizeof(*mem->files)*needed); if(!files) { strncpy(pc->errmsg, "Insufficient memory to extend filename cache.", sizeof(pc->errmsg)); return 1; }; mem->files = files; mem->files_dim = needed; }; /* * Record a pointer to the copy of the filename at the end of the files[] * array. */ mem->files[mem->nfiles++] = copy; /* * Keep a record of the number of files matched so far. */ nfile++; }; /* * Sort the list of files into lexical order. */ qsort(mem->files + mem->nfiles - nfile, nfile, sizeof(*mem->files), pca_cmp_matches); /* * Return the number of files recorded in mem->files[]. */ return nfile; } /*....................................................................... * A qsort() comparison function for comparing the cached filename * strings pointed to by two (char **) array elements. Note that * this ignores the initial cache-status byte of each filename. * * Input: * v1, v2 void * Pointers to the pointers of two strings to be compared. * Output: * return int -1 -> v1 < v2. * 0 -> v1 == v2 * 1 -> v1 > v2 */ static int pca_cmp_matches(const void *v1, const void *v2) { const char **s1 = (const char **) v1; const char **s2 = (const char **) v2; return strcmp(*s1+1, *s2+1); } /*....................................................................... * Given the simple name of a file, search the cached list of files * in the order in which they where found in the list of directories * previously presented to pca_scan_path(), and return the pathname * of the first file which has this name. If a pathname to a file is * given instead of a simple filename, this is returned without being * looked up in the cache, but with any initial ~username expression * expanded, and optionally, unescaped backslashes removed. * * Input: * pc PathCache * The cached list of files. * name const char * The name of the file to lookup. * name_len int The length of the filename string at the * beginning of name[], or -1 to indicate that * the filename occupies the whole of the * string. * literal int If this argument is zero, lone backslashes * in name[] are ignored during comparison * with filenames in the cache, under the * assumption that they were in the input line * soley to escape the special significance of * characters like spaces. To have them treated * as normal characters, give this argument a * non-zero value, such as 1. * Output: * return char * The pathname of the first matching file, * or NULL if not found. Note that the returned * pointer points to memory owned by *pc, and * will become invalid on the next call to any * function in the PathCache module. */ char *pca_lookup_file(PathCache *pc, const char *name, int name_len, int literal) { PathNode *node; /* A node in the list of directories in the path */ char **match; /* A pointer to a matching filename string in the cache */ /* * Check the arguments. */ if(!pc || !name || name_len==0) return NULL; /* * If no length was specified, determine the length of the string to * be looked up. */ if(name_len < 0) name_len = strlen(name); /* * If the word starts with a ~username expression, the root directory, * of it contains any directory separators, then treat it isn't a simple * filename that can be looked up in the cache, but rather appears to * be the pathname of a file. If so, return a copy of this pathname with * escapes removed, if requested, and any initial ~username expression * expanded. */ if(cpa_cmd_contains_path(name, name_len)) { const char *nptr; if(pca_expand_tilde(pc, name, name_len, literal, &nptr) || _pn_append_to_path(pc->path, nptr, name_len - (nptr-name), !literal) == NULL) return NULL; return pc->path->name; }; /* * Look up the specified filename in each of the directories of the path, * in the same order that they were listed in the path, and stop as soon * as an instance of the file is found. */ for(node=pc->head; node; node=node->next) { /* * If the directory of the latest node is a relative pathname, * scan it for files of interest. */ if(node->relative) { rst_CacheMem(node->mem); if(pca_scan_dir(pc, node->dir, node->mem) < 1) continue; node->files = node->mem->files; node->nfile = node->mem->nfiles; }; /* * Copy the filename into a temporary buffer, while interpretting * escape characters if needed. */ _pn_clear_path(pc->path); if(_pn_append_to_path(pc->path, name, name_len, !literal) == NULL) return NULL; /* * Perform a binary search for the requested filename. */ match = (char **)bsearch(pc->path->name, node->files, node->nfile, sizeof(*node->files), pca_cmp_file); if(match) { /* * Prepend the pathname in which the directory was found, which we have * guaranteed to end in a directory separator, to the located filename. */ if(_pn_prepend_to_path(pc->path, node->dir, -1, 0) == NULL) return NULL; /* * Return the matching pathname unless it is rejected by the application. */ if(!pc->check_fn || (*match)[0] == PCA_F_WANTED || ((*match)[0]==PCA_F_ENIGMA && pc->check_fn(pc->data, pc->path->name))){ (*match)[0] = PCA_F_WANTED; return pc->path->name; } else { *(match)[0] = PCA_F_IGNORE; }; }; }; /* * File not found. */ return NULL; } /*....................................................................... * A qsort() comparison function for comparing a filename string to * a cached filename string pointed to by a (char **) array element. * This ignores the initial code byte at the start of the cached filename * string. * * Input: * v1, v2 void * Pointers to the pointers of two strings to be compared. * Output: * return int -1 -> v1 < v2. * 0 -> v1 == v2 * 1 -> v1 > v2 */ static int pca_cmp_file(const void *v1, const void *v2) { const char *file_name = (const char *) v1; const char **cache_name = (const char **) v2; return strcmp(file_name, *cache_name + 1); } /*....................................................................... * The PcaPathConf structure may have options added to it in the future. * To allow your application to be linked against a shared version of the * tecla library, without these additions causing your application to * crash, you should use new_PcaPathConf() to allocate such structures. * This will set all of the configuration options to their default values, * which you can then change before passing the structure to * pca_path_completions(). * * Input: * pc PathCache * The filename cache in which to look for * file name completions. * Output: * return PcaPathConf * The new configuration structure, or NULL * on error. A descripition of the error * can be found by calling pca_last_error(pc). */ PcaPathConf *new_PcaPathConf(PathCache *pc) { PcaPathConf *ppc; /* The object to be returned */ /* * Check the arguments. */ if(!pc) return NULL; /* * Allocate the container. */ ppc = (PcaPathConf *)malloc(sizeof(PcaPathConf)); if(!ppc) { strncpy(pc->errmsg, "Insufficient memory.", sizeof(pc->errmsg)); return NULL; }; /* * Before attempting any operation that might fail, initialize the * container at least up to the point at which it can safely be passed * to del_PcaPathConf(). */ if(pca_init_PcaPathConf(ppc, pc)) return del_PcaPathConf(ppc); return ppc; } /*....................................................................... * Initialize a PcaPathConf configuration structure with defaults. * * Input: * ppc PcaPathConf * The structre to be initialized. * pc PathCache * The cache in which completions will be looked up. * Output: * return int 0 - OK. * 1 - Error. A description of the error can be * obtained by calling pca_last_error(pc). */ static int pca_init_PcaPathConf(PcaPathConf *ppc, PathCache *pc) { /* * Check the arguments. */ if(!pc) return 1; /* * Set the default options. */ ppc->id = PPC_ID_CODE; ppc->pc = pc; ppc->escaped = 1; ppc->file_start = -1; return 0; } /*....................................................................... * Delete a PcaPathConf object. * * Input: * ppc PcaPathConf * The object to be deleted. * Output: * return PcaPathConf * The deleted object (always NULL). */ PcaPathConf *del_PcaPathConf(PcaPathConf *ppc) { if(ppc) { ppc->pc = NULL; /* It is up to the caller to delete the cache */ /* * Delete the container. */ free(ppc); }; return NULL; } /*....................................................................... * pca_path_completions() is a completion callback function for use * directly with cpl_complete_word() or gl_customize_completions(), or * indirectly from your own completion callback function. It requires * that a CpaPathArgs object be passed via its 'void *data' argument. */ CPL_MATCH_FN(pca_path_completions) { PcaPathConf *ppc; /* The configuration arguments */ PathCache *pc; /* The cache in which to look for completions */ PathNode *node; /* A node in the list of directories in the path */ const char *filename; /* The name of the file being looked at */ const char *start_path; /* The pointer to the start of the pathname */ /* in line[]. */ int word_start; /* The index in line[] corresponding to start_path */ const char *prefix; /* The file-name prefix being searched for */ size_t prefix_len; /* The length of the prefix being completed */ int bot; /* The lowest index of the array not searched yet */ int top; /* The highest index of the array not searched yet */ /* * Check the arguments. */ if(!cpl) return 1; if(!line || word_end < 0 || !data) { cpl_record_error(cpl, "pca_path_completions: Invalid arguments."); return 1; }; /* * Get the configuration arguments. */ ppc = (PcaPathConf *) data; /* * Check that the callback data is a PcaPathConf structure returned * by new_PcaPathConf(). */ if(ppc->id != PPC_ID_CODE) { cpl_record_error(cpl, "Invalid callback data passed to pca_path_completions()"); return 1; }; /* * Get the filename cache. */ pc = ppc->pc; /* * Get the start of the file name. If not specified by the caller, * identify it by searching backwards in the input line for an * unescaped space or the start of the line. */ if(ppc->file_start < 0) { start_path = _pu_start_of_path(line, word_end); if(!start_path) { cpl_record_error(cpl, "Unable to find the start of the file name."); return 1; }; } else { start_path = line + ppc->file_start; }; /* * Get the index of the start of the word being completed. */ word_start = start_path - line; /* * Work out the length of the prefix that is bein completed. */ prefix_len = word_end - word_start; /* * If the word starts with a ~username expression or the root directory, * of it contains any directory separators, then completion must be * delegated to cpl_file_completions(). */ if(cpa_cmd_contains_path(start_path, prefix_len)) { cfc_file_start(pc->cfc, word_start); return cpl_file_completions(cpl, pc->cfc, line, word_end); }; /* * Look up the specified file name in each of the directories of the path, * in the same order that they were listed in the path, and stop as soon * as an instance of the file is found. */ for(node=pc->head; node; node=node->next) { /* * If the directory of the latest node is a relative pathname, * scan it for files of interest. */ if(node->relative) { rst_CacheMem(node->mem); if(pca_scan_dir(pc, node->dir, node->mem) < 1) continue; node->files = node->mem->files; node->nfile = node->mem->nfiles; }; /* * If needed, make a copy of the file-name being matched, with * escapes removed. Note that we need to do this anew every loop * iteration, because the above call to pca_scan_dir() uses * pc->path. */ prefix = pca_prepare_prefix(pc, start_path, prefix_len, ppc->escaped); if(!prefix) return 1; /* * The directory entries are sorted, so we can perform a binary * search for an instance of the prefix being searched for. */ bot = 0; top = node->nfile - 1; while(top >= bot) { int mid = (top + bot)/2; int test = strncmp(node->files[mid]+1, prefix, prefix_len); if(test > 0) top = mid - 1; else if(test < 0) bot = mid + 1; else { top = bot = mid; break; }; }; /* * If we found a match, look to see if any of its neigbors also match. */ if(top == bot) { while(--bot >= 0 && strncmp(node->files[bot]+1, prefix, prefix_len) == 0) ; while(++top < node->nfile && strncmp(node->files[top]+1, prefix, prefix_len) == 0) ; /* * We will have gone one too far in each direction. */ bot++; top--; /* * Add the completions to the list after checking them against the * callers requirements. */ for( ; bot<=top; bot++) { char *match = node->files[bot]; /* * Form the full pathname of the file. */ _pn_clear_path(pc->path); if(_pn_append_to_path(pc->path, node->dir, -1, 0) == NULL || _pn_append_to_path(pc->path, match+1, -1, 0) == NULL) { strncpy(pc->errmsg, "Insufficient memory to complete file name", sizeof(pc->errmsg)); return 1; }; /* * Should the file be included in the list of completions? */ if(!pc->check_fn || match[0] == PCA_F_WANTED || (match[0]==PCA_F_ENIGMA && pc->check_fn(pc->data, pc->path->name))) { match[0] = PCA_F_WANTED; /* * Copy the completion suffix into the work pathname pc->path->name, * adding backslash escapes if needed. */ if(pca_prepare_suffix(pc, match + 1 + prefix_len, ppc->escaped)) return 1; /* * Record the completion. */ if(cpl_add_completion(cpl, line, word_start, word_end, pc->path->name, "", " ")) return 1; /* * The file was rejected by the application. */ } else { match[0] = PCA_F_IGNORE; }; }; }; }; /* * We now need to search for subdirectories of the current directory which * have matching prefixes. First, if needed, make a copy of the word being * matched, with escapes removed. */ prefix = pca_prepare_prefix(pc, start_path, prefix_len, ppc->escaped); if(!prefix) return 1; /* * Now open the current directory. */ if(_dr_open_dir(pc->dr, FS_PWD, NULL)) return 0; /* * Scan the current directory for sub-directories whos names start with * the prefix that we are completing. */ while((filename = _dr_next_file(pc->dr))) { /* * Does the latest filename match the prefix, and is it a directory? */ if(strncmp(filename, prefix, prefix_len) == 0 && _pu_path_is_dir(filename)){ /* * Record the completion. */ if(pca_prepare_suffix(pc, filename + prefix_len, ppc->escaped) || cpl_add_completion(cpl, line, word_start, word_end, pc->path->name, FS_DIR_SEP, FS_DIR_SEP)) return 1; /* * The prefix in pc->path->name will have been overwritten by * pca_prepare_suffix(). Restore it here. */ prefix = pca_prepare_prefix(pc, start_path, prefix_len, ppc->escaped); if(!prefix) return 1; }; }; _dr_close_dir(pc->dr); return 0; } /*....................................................................... * Using the work buffer pc->path, make a suitably escaped copy of a * given completion suffix, ready to be passed to cpl_add_completion(). * * Input: * pc PathCache * The filename cache resource object. * suffix char * The suffix to be copied. * add_escapes int If true, escape special characters. * Output: * return int 0 - OK. * 1 - Error. */ static int pca_prepare_suffix(PathCache *pc, const char *suffix, int add_escapes) { const char *sptr; /* A pointer into suffix[] */ int nbsl; /* The number of backslashes to add to the suffix */ int i; /* * How long is the suffix? */ int suffix_len = strlen(suffix); /* * Clear the work buffer. */ _pn_clear_path(pc->path); /* * Count the number of backslashes that will have to be added to * escape spaces, tabs, backslashes and wildcard characters. */ nbsl = 0; if(add_escapes) { for(sptr = suffix; *sptr; sptr++) { switch(*sptr) { case ' ': case '\t': case '\\': case '*': case '?': case '[': nbsl++; break; }; }; }; /* * Arrange for the output path buffer to have sufficient room for the * both the suffix and any backslashes that have to be inserted. */ if(_pn_resize_path(pc->path, suffix_len + nbsl) == NULL) { strncpy(pc->errmsg, "Insufficient memory to complete file name", sizeof(pc->errmsg)); return 1; }; /* * If the suffix doesn't need any escapes, copy it directly into the * work buffer. */ if(nbsl==0) { strncpy(pc->path->name, suffix, pc->path->dim); } else { /* * Make a copy with special characters escaped? */ if(nbsl > 0) { const char *src = suffix; char *dst = pc->path->name; for(i=0; i= FS_ROOT_DIR_LEN && strncmp(prefix, FS_ROOT_DIR, FS_ROOT_DIR_LEN) == 0) return 1; /* * Search the prefix for directory separators, returning as soon as * any are found, since their presence indicates that the filename * starts with a pathname specification (valid or otherwise). */ for(i=0; i= FS_DIR_SEP_LEN && strncmp(prefix + i, FS_DIR_SEP, FS_DIR_SEP_LEN) == 0) return 1; }; /* * The file name doesn't appear to start with a pathname specification. */ return 0; } /*....................................................................... * If needed make a new copy of the prefix being matched, in pc->path->name, * but with escapes removed. If no escapes are to be removed, simply return * the original prefix string. * * Input: * pc PathCache * The cache being searched. * prefix const char * The prefix to be processed. * prefix_len size_t The length of the prefix. * escaped int If true, return a copy with escapes removed. * Output: * return const char * The prepared prefix, or NULL on error, in * which case an error message will have been * left in pc->errmsg. */ static const char *pca_prepare_prefix(PathCache *pc, const char *prefix, size_t prefix_len, int escaped) { /* * Make a copy with escapes removed? */ if(escaped) { _pn_clear_path(pc->path); if(_pn_append_to_path(pc->path, prefix, prefix_len, 1) == NULL) { strncpy(pc->errmsg, "Insufficient memory to complete filename", sizeof(pc->errmsg)); return NULL; }; return pc->path->name; }; return prefix; } /*....................................................................... * If backslashes in the filename should be treated as literal * characters, call the following function with literal=1. Otherwise * the default is to treat them as escape characters, used for escaping * spaces etc.. * * Input: * ppc PcaPathConf * The pca_path_completions() configuration object * to be configured. * literal int Pass non-zero here to enable literal interpretation * of backslashes. Pass 0 to turn off literal * interpretation. */ void ppc_literal_escapes(PcaPathConf *ppc, int literal) { if(ppc) ppc->escaped = !literal; } /*....................................................................... * Call this function if you know where the index at which the * filename prefix starts in the input line. Otherwise by default, * or if you specify start_index to be -1, the filename is taken * to start after the first unescaped space preceding the cursor, * or the start of the line, which ever comes first. * * Input: * ppc PcaPathConf * The pca_path_completions() configuration object * to be configured. * start_index int The index of the start of the filename in * the input line, or -1 to select the default. */ void ppc_file_start(PcaPathConf *ppc, int start_index) { if(ppc) ppc->file_start = start_index; } /*....................................................................... * Expand any ~user expression found at the start of a path, leaving * either an empty string in pc->path if there is no ~user expression, * or the corresponding home directory. * * Input: * pc PathCache * The filename cache. * path const char * The path to expand. * pathlen int The max number of characters to look at in path[]. * literal int If true, treat backslashes as literal characters * instead of escapes. * Input/Output: * endp const char * A pointer to the next unprocessed character in * path[] will be assigned to *endp. * Output: * return int 0 - OK * 1 - Error (a description will have been placed * in pc->errmsg[]). */ static int pca_expand_tilde(PathCache *pc, const char *path, int pathlen, int literal, const char **endp) { const char *pptr = path; /* A pointer into path[] */ const char *homedir=NULL; /* A home directory */ /* * Clear the pathname buffer. */ _pn_clear_path(pc->path); /* * If the first character is a tilde, then perform home-directory * interpolation. */ if(*pptr == '~') { /* * Skip the tilde character and attempt to read the username that follows * it, into pc->usrnam[]. */ if(pca_read_username(pc, ++pptr, pathlen-1, literal, &pptr)) return 1; /* * Attempt to lookup the home directory of the user. */ homedir = _hd_lookup_home_dir(pc->home, pc->usrnam); if(!homedir) { strncpy(pc->errmsg, _hd_last_home_dir_error(pc->home), ERRLEN); pc->errmsg[ERRLEN] = '\0'; return 1; }; /* * Append the home directory to the pathname string. */ if(_pn_append_to_path(pc->path, homedir, -1, 0) == NULL) { strncpy(pc->errmsg, "Insufficient memory for home directory expansion", sizeof(pc->errmsg)); return 1; }; }; /* * ~user and ~ are usually followed by a directory separator to * separate them from the file contained in the home directory. * If the home directory is the root directory, then we don't want * to follow the home directory by a directory separator, so we should * skip over it so that it doesn't get copied into the output pathname */ if(homedir && strcmp(homedir, FS_ROOT_DIR) == 0 && (pptr-path) + FS_DIR_SEP_LEN < pathlen && strncmp(pptr, FS_DIR_SEP, FS_DIR_SEP_LEN) == 0) { pptr += FS_DIR_SEP_LEN; }; /* * Return a pointer to the next unprocessed character. */ *endp = pptr; return 0; } /*....................................................................... * Clear the filename status codes that are recorded before each filename * in the cache. * * Input: * pc PathCache * The filename cache. */ static void pca_remove_marks(PathCache *pc) { PathNode *node; /* A node in the list of directories in the path */ int i; /* * Traverse the absolute directories of the path, clearing the * filename status marks that precede each filename. */ for(node=pc->head; node; node=node->next) { if(!node->relative) { for(i=0; infile; i++) *node->files[i] = PCA_F_ENIGMA; }; }; return; } xorp/cli/libtecla/README0000664000076400007640000000514511421137511015067 0ustar greearbgreearbThis is version 1.4.0 of the tecla command-line editing library. For the current official release, please direct your browser to: http://www.astro.caltech.edu/~mcs/tecla/index.html The tecla library provides UNIX and LINUX programs with interactive command line editing facilities, similar to those of the unix tcsh shell. In addition to simple command-line editing, it supports recall of previously entered command lines, TAB completion of file names, and in-line wild-card expansion of filenames. The internal functions which perform file-name completion and wild-card expansion are also available externally for optional use by programs, along with a module for tab-completion and lookup of filenames in a list of directories. Note that special care has been taken to allow the use of this library in threaded programs. The option to enable this is discussed in the Makefile, and specific discussions of thread safety are presented in the included man pages. For instructions on how to compile and install the library, please see the INSTALL file, which should be in the same directory as this file. Copyright and Disclaimer ------------------------ Copyright (c) 2000, 2001 by Martin C. Shepherd. All rights reserved. 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, including without limitation the rights to use, copy, modify, merge, publish, distribute, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, provided that the above copyright notice(s) and this permission notice appear in all copies of the Software and that both the above copyright notice(s) and this permission notice appear in supporting documentation. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. Except as contained in this notice, the name of a copyright holder shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization of the copyright holder. xorp/cli/libtecla/expand.c0000664000076400007640000011601611421137511015632 0ustar greearbgreearb/* * Copyright (c) 2000, 2001 by Martin C. Shepherd. * * All rights reserved. * * 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, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, and/or sell copies of the Software, and to permit persons * to whom the Software is furnished to do so, provided that the above * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Except as contained in this notice, the name of a copyright holder * shall not be used in advertising or otherwise to promote the sale, use * or other dealings in this Software without prior written authorization * of the copyright holder. */ #include #include #include #include #include "freelist.h" #include "direader.h" #include "pathutil.h" #include "homedir.h" #include "stringrp.h" #include "libtecla.h" /* * Specify the number of elements to extend the files[] array by * when it proves to be too small. This also sets the initial size * of the array. */ #define MATCH_BLK_FACT 256 /* * A list of directory iterators is maintained using nodes of the * following form. */ typedef struct DirNode DirNode; struct DirNode { DirNode *next; /* The next directory in the list */ DirNode *prev; /* The node that precedes this node in the list */ DirReader *dr; /* The directory reader object */ }; typedef struct { FreeList *mem; /* Memory for DirNode list nodes */ DirNode *head; /* The head of the list of used and unused cache nodes */ DirNode *next; /* The next unused node between head and tail */ DirNode *tail; /* The tail of the list of unused cache nodes */ } DirCache; /* * Specify how many directory cache nodes to allocate at a time. */ #define DIR_CACHE_BLK 20 /* * Set the maximum length allowed for usernames. */ #define USR_LEN 100 /* * Set the maximum length allowed for environment variable names. */ #define ENV_LEN 100 /* * Set the max length of the error-reporting string. There is no point * in this being longer than the width of a typical terminal window. * In composing error messages, I have assumed that this number is * at least 80, so you don't decrease it below this number. */ #define ERRLEN 200 struct ExpandFile { StringGroup *sg; /* A list of string segments in which */ /* matching filenames are stored. */ DirCache cache; /* The cache of directory reader objects */ PathName *path; /* The pathname being matched */ HomeDir *home; /* Home-directory lookup object */ int files_dim; /* The allocated dimension of result.files[] */ char usrnam[USR_LEN+1]; /* A user name */ char envnam[ENV_LEN+1]; /* An environment variable name */ char errmsg[ERRLEN+1]; /* Error-report buffer */ FileExpansion result; /* The container used to return the results of */ /* expanding a path. */ }; static int ef_record_pathname(ExpandFile *ef, const char *pathname, int remove_escapes); static char *ef_cache_pathname(ExpandFile *ef, const char *pathname, int remove_escapes); static void ef_clear_files(ExpandFile *ef); static DirNode *ef_open_dir(ExpandFile *ef, const char *pathname); static DirNode *ef_close_dir(ExpandFile *ef, DirNode *node); static char *ef_expand_special(ExpandFile *ef, const char *path, int pathlen); static int ef_match_relative_pathname(ExpandFile *ef, DirReader *dr, const char *pattern, int separate); static int ef_matches_range(int c, const char *pattern, const char **endp); static int ef_string_matches_pattern(const char *file, const char *pattern, int xplicit, const char *nextp); static int ef_cmp_strings(const void *v1, const void *v2); /*....................................................................... * Create the resources needed to expand filenames. * * Output: * return ExpandFile * The new object, or NULL on error. */ ExpandFile *new_ExpandFile(void) { ExpandFile *ef; /* The object to be returned */ /* * Allocate the container. */ ef = (ExpandFile *) malloc(sizeof(ExpandFile)); if(!ef) { fprintf(stderr, "new_ExpandFile: Insufficient memory.\n"); return NULL; }; /* * Before attempting any operation that might fail, initialize the * container at least up to the point at which it can safely be passed * to del_ExpandFile(). */ ef->sg = NULL; ef->cache.mem = NULL; ef->cache.head = NULL; ef->cache.next = NULL; ef->cache.tail = NULL; ef->path = NULL; ef->home = NULL; ef->result.files = NULL; ef->result.nfile = 0; ef->usrnam[0] = '\0'; ef->envnam[0] = '\0'; ef->errmsg[0] = '\0'; /* * Allocate a list of string segments for storing filenames. */ ef->sg = _new_StringGroup(_pu_pathname_dim()); if(!ef->sg) return del_ExpandFile(ef); /* * Allocate a freelist for allocating directory cache nodes. */ ef->cache.mem = _new_FreeList("new_ExpandFile", sizeof(DirNode), DIR_CACHE_BLK); if(!ef->cache.mem) return del_ExpandFile(ef); /* * Allocate a pathname buffer. */ ef->path = _new_PathName(); if(!ef->path) return del_ExpandFile(ef); /* * Allocate an object for looking up home-directories. */ ef->home = _new_HomeDir(); if(!ef->home) return del_ExpandFile(ef); /* * Allocate an array for files. This will be extended later if needed. */ ef->files_dim = MATCH_BLK_FACT; ef->result.files = (char **) malloc(sizeof(ef->result.files[0]) * ef->files_dim); if(!ef->result.files) { fprintf(stderr, "new_ExpandFile: Insufficient memory to allocate array of files.\n"); return del_ExpandFile(ef); }; return ef; } /*....................................................................... * Delete a ExpandFile object. * * Input: * ef ExpandFile * The object to be deleted. * Output: * return ExpandFile * The deleted object (always NULL). */ ExpandFile *del_ExpandFile(ExpandFile *ef) { if(ef) { DirNode *dnode; /* * Delete the string segments. */ ef->sg = _del_StringGroup(ef->sg); /* * Delete the cached directory readers. */ for(dnode=ef->cache.head; dnode; dnode=dnode->next) dnode->dr = _del_DirReader(dnode->dr); /* * Delete the memory from which the DirNode list was allocated, thus * deleting the list at the same time. */ ef->cache.mem = _del_FreeList("del_ExpandFile", ef->cache.mem, 1); ef->cache.head = ef->cache.tail = ef->cache.next = NULL; /* * Delete the pathname buffer. */ ef->path = _del_PathName(ef->path); /* * Delete the home-directory lookup object. */ ef->home = _del_HomeDir(ef->home); /* * Delete the array of pointers to files. */ if(ef->result.files) { free(ef->result.files); ef->result.files = NULL; }; /* * Delete the container. */ free(ef); }; return NULL; } /*....................................................................... * Expand a pathname, converting ~user/ and ~/ patterns at the start * of the pathname to the corresponding home directories, replacing * $envvar with the value of the corresponding environment variable, * and then, if there are any wildcards, matching these against existing * filenames. * * If no errors occur, a container is returned containing the array of * files that resulted from the expansion. If there were no wildcards * in the input pathname, this will contain just the original pathname * after expansion of ~ and $ expressions. If there were any wildcards, * then the array will contain the files that matched them. Note that * if there were any wildcards but no existing files match them, this * is counted as an error and NULL is returned. * * The supported wildcards and their meanings are: * * - Match any sequence of zero or more characters. * ? - Match any single character. * [chars] - Match any single character that appears in 'chars'. * If 'chars' contains an expression of the form a-b, * then any character between a and b, including a and b, * matches. The '-' character looses its special meaning * as a range specifier when it appears at the start * of the sequence of characters. * [^chars] - The same as [chars] except that it matches any single * character that doesn't appear in 'chars'. * * Wildcard expressions are applied to individual filename components. * They don't match across directory separators. A '.' character at * the beginning of a filename component must also be matched * explicitly by a '.' character in the input pathname, since these * are UNIX's hidden files. * * Input: * ef ExpandFile * The pathname expansion resource object. * path char * The path name to be expanded. * pathlen int The length of the suffix of path[] that * constitutes the filename to be expanded, * or -1 to specify that the whole of the * path string should be used. Note that * regardless of the value of this argument, * path[] must contain a '\0' terminated * string, since this function checks that * pathlen isn't mistakenly too long. * Output: * return FileExpansion * A pointer to a container within the given * ExpandFile object. This contains an array * of the pathnames that resulted from expanding * ~ and $ expressions and from matching any * wildcards, sorted into lexical order. * This container and its contents will be * recycled on subsequent calls, so if you need * to keep the results of two successive runs, * you will either have to allocate a private * copy of the array, or use two ExpandFile * objects. * * On error NULL is returned. A description * of the error can be acquired by calling the * ef_last_error() function. */ FileExpansion *ef_expand_file(ExpandFile *ef, const char *path, int pathlen) { DirNode *dnode; /* A directory-reader cache node */ const char *dirname; /* The name of the top level directory of the search */ const char *pptr; /* A pointer into path[] */ int wild; /* True if the path contains any wildcards */ /* * Check the arguments. */ if(!ef || !path) { if(ef) strncpy(ef->errmsg, "ef_expand_file: NULL path argument", sizeof(ef->errmsg)); else fprintf(stderr, "ef_expand_file: NULL argument(s).\n"); return NULL; }; /* * If the caller specified that the whole of path[] be matched, * work out the corresponding length. */ if(pathlen < 0 || pathlen > strlen(path)) pathlen = strlen(path); /* * Discard previous expansion results. */ ef_clear_files(ef); /* * Preprocess the path, expanding ~/, ~user/ and $envvar references, * using ef->path as a work directory and returning a pointer to * a copy of the resulting pattern in the cache. */ path = ef_expand_special(ef, path, pathlen); if(!path) return NULL; /* * Clear the pathname buffer. */ _pn_clear_path(ef->path); /* * Does the pathname contain any wildcards? */ for(wild=0,pptr=path; !wild && *pptr; pptr++) { switch(*pptr) { case '\\': /* Skip escaped characters */ if(pptr[1]) pptr++; break; case '*': case '?': case '[': /* A wildcard character? */ wild = 1; break; }; }; /* * If there are no wildcards to match, copy the current expanded * path into the output array, removing backslash escapes while doing so. */ if(!wild) { if(ef_record_pathname(ef, path, 1)) return NULL; /* * Does the filename exist? */ ef->result.exists = _pu_file_exists(ef->result.files[0]); /* * Match wildcards against existing files. */ } else { /* * Only existing files that match the pattern will be returned in the * cache. */ ef->result.exists = 1; /* * Treat matching of the root-directory as a special case since it * isn't contained in a directory. */ if(strcmp(path, FS_ROOT_DIR) == 0) { if(ef_record_pathname(ef, FS_ROOT_DIR, 0)) return NULL; } else { /* * What should the top level directory of the search be? */ if(strncmp(path, FS_ROOT_DIR, FS_ROOT_DIR_LEN) == 0) { dirname = FS_ROOT_DIR; if(!_pn_append_to_path(ef->path, FS_ROOT_DIR, -1, 0)) { strncpy(ef->errmsg, "Insufficient memory to record path", sizeof(ef->errmsg)); return NULL; }; path += FS_ROOT_DIR_LEN; } else { dirname = FS_PWD; }; /* * Open the top-level directory of the search. */ dnode = ef_open_dir(ef, dirname); if(!dnode) return NULL; /* * Recursively match successive directory components of the path. */ if(ef_match_relative_pathname(ef, dnode->dr, path, 0)) { dnode = ef_close_dir(ef, dnode); return NULL; }; /* * Cleanup. */ dnode = ef_close_dir(ef, dnode); }; /* * No files matched? */ if(ef->result.nfile < 1) { strncpy(ef->errmsg, "No files match", sizeof(ef->errmsg)); return NULL; }; /* * Sort the pathnames that matched. */ qsort(ef->result.files, ef->result.nfile, sizeof(ef->result.files[0]), ef_cmp_strings); }; /* * Return the result container. */ return &ef->result; } /*....................................................................... * Attempt to recursively match the given pattern with the contents of * the current directory, descending sub-directories as needed. * * Input: * ef ExpandFile * The pathname expansion resource object. * dr DirReader * The directory reader object of the directory * to be searched. * pattern const char * The pattern to match with files in the current * directory. * separate int When appending a filename from the specified * directory to ef->pathname, insert a directory * separator between the existing pathname and * the filename, unless separate is zero. * Output: * return int 0 - OK. * 1 - Error. */ static int ef_match_relative_pathname(ExpandFile *ef, DirReader *dr, const char *pattern, int separate) { const char *nextp; /* The pointer to the character that follows the part */ /* of the pattern that is to be matched with files */ /* in the current directory. */ char *file; /* The name of the file being matched */ int pathlen; /* The length of ef->pathname[] on entry to this */ /* function */ /* * Record the current length of the pathname string recorded in * ef->pathname[]. */ pathlen = strlen(ef->path->name); /* * Get a pointer to the character that follows the end of the part of * the pattern that should be matched to files within the current directory. * This will either point to a directory separator, or to the '\0' terminator * of the pattern string. */ for(nextp=pattern; *nextp && strncmp(nextp, FS_DIR_SEP, FS_DIR_SEP_LEN) != 0; nextp++) ; /* * Read each file from the directory, attempting to match it to the * current pattern. */ while((file=_dr_next_file(dr)) != NULL) { /* * Does the latest file match the pattern up to nextp? */ if(ef_string_matches_pattern(file, pattern, file[0]=='.', nextp)) { /* * Append the new directory entry to the current matching pathname. */ if((separate && _pn_append_to_path(ef->path, FS_DIR_SEP, -1, 0)==NULL) || _pn_append_to_path(ef->path, file, -1, 0)==NULL) { strncpy(ef->errmsg, "Insufficient memory to record path", sizeof(ef->errmsg)); return 1; }; /* * If we have reached the end of the pattern, record the accumulated * pathname in the list of matching files. */ if(*nextp == '\0') { if(ef_record_pathname(ef, ef->path->name, 0)) return 1; /* * If the matching directory entry is a subdirectory, and the * next character of the pattern is a directory separator, * recursively call the current function to scan the sub-directory * for matches. */ } else if(_pu_path_is_dir(ef->path->name) && strncmp(nextp, FS_DIR_SEP, FS_DIR_SEP_LEN) == 0) { /* * If the pattern finishes with the directory separator, then * record the pathame as matching. */ if(nextp[FS_DIR_SEP_LEN] == '\0') { if(ef_record_pathname(ef, ef->path->name, 0)) return 1; /* * Match files within the directory. */ } else { DirNode *subdnode = ef_open_dir(ef, ef->path->name); if(subdnode) { if(ef_match_relative_pathname(ef, subdnode->dr, nextp+FS_DIR_SEP_LEN, 1)) { subdnode = ef_close_dir(ef, subdnode); return 1; }; subdnode = ef_close_dir(ef, subdnode); }; }; }; /* * Remove the latest filename from the pathname string, so that * another matching file can be appended. */ ef->path->name[pathlen] = '\0'; }; }; return 0; } /*....................................................................... * Record a new matching filename. * * Input: * ef ExpandFile * The filename-match resource object. * pathname const char * The pathname to record. * remove_escapes int If true, remove backslash escapes in the * recorded copy of the pathname. * Output: * return int 0 - OK. * 1 - Error (ef->errmsg will contain a * description of the error). */ static int ef_record_pathname(ExpandFile *ef, const char *pathname, int remove_escapes) { char *copy; /* The recorded copy of pathname[] */ /* * Attempt to make a copy of the pathname in the cache. */ copy = ef_cache_pathname(ef, pathname, remove_escapes); if(!copy) return 1; /* * If there isn't room to record a pointer to the recorded pathname in the * array of files, attempt to extend the array. */ if(ef->result.nfile + 1 > ef->files_dim) { int files_dim = ef->files_dim + MATCH_BLK_FACT; char **files = (char **) realloc(ef->result.files, files_dim * sizeof(files[0])); if(!files) { snprintf(ef->errmsg, sizeof(ef->errmsg), "Insufficient memory to record all of the matching filenames"); return 1; }; ef->result.files = files; ef->files_dim = files_dim; }; /* * Record a pointer to the new match. */ ef->result.files[ef->result.nfile++] = copy; return 0; } /*....................................................................... * Record a pathname in the cache. * * Input: * ef ExpandFile * The filename-match resource object. * pathname char * The pathname to record. * remove_escapes int If true, remove backslash escapes in the * copy of the pathname. * Output: * return char * The pointer to the copy of the pathname. * On error NULL is returned and a description * of the error is left in ef->errmsg[]. */ static char *ef_cache_pathname(ExpandFile *ef, const char *pathname, int remove_escapes) { char *copy = _sg_store_string(ef->sg, pathname, remove_escapes); if(!copy) strncpy(ef->errmsg, "Insufficient memory to store pathname", sizeof(ef->errmsg)); return copy; } /*....................................................................... * Clear the results of the previous expansion operation, ready for the * next. * * Input: * ef ExpandFile * The pathname expansion resource object. */ static void ef_clear_files(ExpandFile *ef) { _clr_StringGroup(ef->sg); _pn_clear_path(ef->path); ef->result.exists = 0; ef->result.nfile = 0; ef->errmsg[0] = '\0'; return; } /*....................................................................... * Get a new directory reader object from the cache. * * Input: * ef ExpandFile * The pathname expansion resource object. * pathname const char * The pathname of the directory. * Output: * return DirNode * The cache entry of the new directory reader, * or NULL on error. On error, ef->errmsg will * contain a description of the error. */ static DirNode *ef_open_dir(ExpandFile *ef, const char *pathname) { char *errmsg = NULL; /* An error message from a called function */ DirNode *node; /* The cache node used */ /* * Get the directory reader cache. */ DirCache *cache = &ef->cache; /* * Extend the cache if there are no free cache nodes. */ if(!cache->next) { node = (DirNode *) _new_FreeListNode(cache->mem); if(!node) { snprintf(ef->errmsg, sizeof(ef->errmsg), "Insufficient memory to open a new directory"); return NULL; }; /* * Initialize the cache node. */ node->next = NULL; node->prev = NULL; node->dr = NULL; /* * Allocate a directory reader object. */ node->dr = _new_DirReader(); if(!node->dr) { snprintf(ef->errmsg, sizeof(ef->errmsg), "Insufficient memory to open a new directory"); node = (DirNode *) _del_FreeListNode(cache->mem, node); return NULL; }; /* * Append the node to the cache list. */ node->prev = cache->tail; if(cache->tail) cache->tail->next = node; else cache->head = node; cache->next = cache->tail = node; }; /* * Get the first unused node, but don't remove it from the list yet. */ node = cache->next; /* * Attempt to open the specified directory. */ if(_dr_open_dir(node->dr, pathname, &errmsg)) { strncpy(ef->errmsg, errmsg, ERRLEN); ef->errmsg[ERRLEN] = '\0'; return NULL; }; /* * Now that we have successfully opened the specified directory, * remove the cache node from the list, and relink the list around it. */ cache->next = node->next; if(node->prev) node->prev->next = node->next; else cache->head = node->next; if(node->next) node->next->prev = node->prev; else cache->tail = node->prev; node->next = node->prev = NULL; /* * Return the successfully initialized cache node to the caller. */ return node; } /*....................................................................... * Return a directory reader object to the cache, after first closing * the directory that it was managing. * * Input: * ef ExpandFile * The pathname expansion resource object. * node DirNode * The cache entry of the directory reader, as returned * by ef_open_dir(). * Output: * return DirNode * The deleted DirNode (ie. allways NULL). */ static DirNode *ef_close_dir(ExpandFile *ef, DirNode *node) { /* * Get the directory reader cache. */ DirCache *cache = &ef->cache; /* * Close the directory. */ _dr_close_dir(node->dr); /* * Return the node to the tail of the cache list. */ node->next = NULL; node->prev = cache->tail; if(cache->tail) cache->tail->next = node; else cache->head = cache->tail = node; if(!cache->next) cache->next = node; return NULL; } /*....................................................................... * Return non-zero if the specified file name matches a given glob * pattern. * * Input: * file const char * The file-name component to be matched to the pattern. * pattern const char * The start of the pattern to match against file[]. * xplicit int If non-zero, the first character must be matched * explicitly (ie. not with a wildcard). * nextp const char * The pointer to the the character following the * end of the pattern in pattern[]. * Output: * return int 0 - Doesn't match. * 1 - The file-name string matches the pattern. */ static int ef_string_matches_pattern(const char *file, const char *pattern, int xplicit, const char *nextp) { const char *pptr = pattern; /* The pointer used to scan the pattern */ const char *fptr = file; /* The pointer used to scan the filename string */ /* * Match each character of the pattern in turn. */ while(pptr < nextp) { /* * Handle the next character of the pattern. */ switch(*pptr) { /* * A match zero-or-more characters wildcard operator. */ case '*': /* * Skip the '*' character in the pattern. */ pptr++; /* * If wildcards aren't allowed, the pattern doesn't match. */ if(xplicit) return 0; /* * If the pattern ends with a the '*' wildcard, then the * rest of the filename matches this. */ if(pptr >= nextp) return 1; /* * Using the wildcard to match successively longer sections of * the remaining characters of the filename, attempt to match * the tail of the filename against the tail of the pattern. */ for( ; *fptr; fptr++) { if(ef_string_matches_pattern(fptr, pptr, 0, nextp)) return 1; }; return 0; /* The pattern following the '*' didn't match */ break; /* * A match-one-character wildcard operator. */ case '?': /* * If there is a character to be matched, skip it and advance the * pattern pointer. */ if(!xplicit && *fptr) { fptr++; pptr++; /* * If we hit the end of the filename string, there is no character * matching the operator, so the string doesn't match. */ } else { return 0; }; break; /* * A character range operator, with the character ranges enclosed * in matching square brackets. */ case '[': if(xplicit || !ef_matches_range(*fptr++, ++pptr, &pptr)) return 0; break; /* * A backslash in the pattern prevents the following character as * being seen as a special character. */ case '\\': pptr++; /* Note fallthrough to default */ /* * A normal character to be matched explicitly. */ default: if(*fptr == *pptr) { fptr++; pptr++; } else { return 0; }; break; }; /* * After passing the first character, turn off the explicit match * requirement. */ xplicit = 0; }; /* * To get here the pattern must have been exhausted. If the filename * string matched, then the filename string must also have been * exhausted. */ return *fptr == '\0'; } /*....................................................................... * Match a character range expression terminated by an unescaped close * square bracket. * * Input: * c int The character to be matched with the range * pattern. * pattern const char * The range pattern to be matched (ie. after the * initiating '[' character). * endp const char ** On output a pointer to the character following the * range expression will be assigned to *endp. * Output: * return int 0 - Doesn't match. * 1 - The character matched. */ static int ef_matches_range(int c, const char *pattern, const char **endp) { const char *pptr = pattern; /* The pointer used to scan the pattern */ int invert = 0; /* True to invert the sense of the match */ int matched = 0; /* True if the character matched the pattern */ /* * If the first character is a caret, the sense of the match is * inverted and only if the character isn't one of those in the * range, do we say that it matches. */ if(*pptr == '^') { pptr++; invert = 1; }; /* * The hyphen is only a special character when it follows the first * character of the range (not including the caret). */ if(*pptr == '-') { pptr++; if(c == '-') { *endp = pptr; matched = 1; }; /* * Skip other leading '-' characters since they make no sense. */ while(*pptr == '-') pptr++; }; /* * The hyphen is only a special character when it follows the first * character of the range (not including the caret or a hyphen). */ if(*pptr == ']') { pptr++; if(c == ']') { *endp = pptr; matched = 1; }; }; /* * Having dealt with the characters that have special meanings at * the beginning of a character range expression, see if the * character matches any of the remaining characters of the range, * up until a terminating ']' character is seen. */ while(!matched && *pptr && *pptr != ']') { /* * Is this a range of characters signaled by the two end characters * separated by a hyphen? */ if(*pptr == '-') { if(pptr[1] != ']') { if(c >= pptr[-1] && c <= pptr[1]) matched = 1; pptr += 2; }; /* * A normal character to be compared directly. */ } else if(*pptr++ == c) { matched = 1; }; }; /* * Find the terminating ']'. */ while(*pptr && *pptr != ']') pptr++; /* * Did we find a terminating ']'? */ if(*pptr == ']') { *endp = pptr + 1; return matched ? !invert : invert; }; /* * If the pattern didn't end with a ']' then it doesn't match, regardless * of the value of the required sense of the match. */ *endp = pptr; return 0; } /*....................................................................... * This is a qsort() comparison function used to sort strings. * * Input: * v1, v2 void * Pointers to the two strings to be compared. * Output: * return int -1 -> v1 < v2. * 0 -> v1 == v2 * 1 -> v1 > v2 */ static int ef_cmp_strings(const void *v1, const void *v2) { char * const *s1 = (char * const *) v1; char * const *s2 = (char * const *) v2; return strcmp(*s1, *s2); } /*....................................................................... * Preprocess a path, expanding ~/, ~user/ and $envvar references, using * ef->path as a work buffer, then copy the result into a cache entry, * and return a pointer to this copy. * * Input: * ef ExpandFile * The resource object of the file matcher. * pathlen int The length of the prefix of path[] to be expanded. * Output: * return char * A pointer to a copy of the output path in the * cache. On error NULL is returned, and a description * of the error is left in ef->errmsg[]. */ static char *ef_expand_special(ExpandFile *ef, const char *path, int pathlen) { int spos; /* The index of the start of the path segment that needs */ /* to be copied from path[] to the output pathname. */ int ppos; /* The index of a character in path[] */ char *pptr; /* A pointer into the output path */ int escaped; /* True if the previous character was a '\' */ int i; /* * Clear the pathname buffer. */ _pn_clear_path(ef->path); /* * We need to perform two passes, one to expand environment variables * and a second to do tilde expansion. This caters for the case * where an initial dollar expansion yields a tilde expression. */ escaped = 0; for(spos=ppos=0; ppos < pathlen; ppos++) { int c = path[ppos]; if(escaped) { escaped = 0; } else if(c == '\\') { escaped = 1; } else if(c == '$') { int envlen; /* The length of the environment variable */ char *value; /* The value of the environment variable */ /* * Record the preceding unrecorded part of the pathname. */ if(spos < ppos && _pn_append_to_path(ef->path, path + spos, ppos-spos, 0) == NULL) { strncpy(ef->errmsg, "Insufficient memory to expand path", sizeof(ef->errmsg)); return NULL; }; /* * Skip the dollar. */ ppos++; /* * Copy the environment variable name that follows the dollar into * ef->envnam[], stopping if a directory separator or end of string * is seen. */ for(envlen=0; envlenenvnam[envlen] = path[ppos++]; /* * If the username overflowed the buffer, treat it as invalid (note that * on most unix systems only 8 characters are allowed in a username, * whereas our ENV_LEN is much bigger than that. */ if(envlen >= ENV_LEN) { strncpy(ef->errmsg, "Environment variable name too long", sizeof(ef->errmsg)); return NULL; }; /* * Terminate the environment variable name. */ ef->envnam[envlen] = '\0'; /* * Lookup the value of the environment variable. */ value = getenv(ef->envnam); if(!value) { const char *fmt = "No expansion found for: $%.*s"; snprintf(ef->errmsg, sizeof(ef->errmsg), fmt, ERRLEN - strlen(fmt), ef->envnam); return NULL; }; /* * Copy the value of the environment variable into the output pathname. */ if(_pn_append_to_path(ef->path, value, -1, 0) == NULL) { strncpy(ef->errmsg, "Insufficient memory to expand path", sizeof(ef->errmsg)); return NULL; }; /* * Record the start of the uncopied tail of the input pathname. */ spos = ppos; }; }; /* * Record the uncopied tail of the pathname. */ if(spos < ppos && _pn_append_to_path(ef->path, path + spos, ppos-spos, 0) == NULL) { strncpy(ef->errmsg, "Insufficient memory to expand path", sizeof(ef->errmsg)); return NULL; }; /* * If the first character of the resulting pathname is a tilde, * then attempt to substitute the home directory of the specified user. */ pptr = ef->path->name; if(*pptr == '~' && path[0] != '\\') { int usrlen; /* The length of the username following the tilde */ const char *homedir; /* The home directory of the user */ int homelen; /* The length of the home directory string */ int plen; /* The current length of the path */ int skip=0; /* The number of characters to skip after the ~user */ /* * Get the current length of the output path. */ plen = strlen(ef->path->name); /* * Skip the tilde. */ pptr++; /* * Copy the optional username that follows the tilde into ef->usrnam[]. */ for(usrlen=0; usrlenusrnam[usrlen] = *pptr++; /* * If the username overflowed the buffer, treat it as invalid (note that * on most unix systems only 8 characters are allowed in a username, * whereas our USR_LEN is much bigger than that. */ if(usrlen >= USR_LEN) { strncpy(ef->errmsg, "Username too long", sizeof(ef->errmsg)); return NULL; }; /* * Terminate the username string. */ ef->usrnam[usrlen] = '\0'; /* * Lookup the home directory of the user. */ homedir = _hd_lookup_home_dir(ef->home, ef->usrnam); if(!homedir) { strncpy(ef->errmsg, _hd_last_home_dir_error(ef->home), ERRLEN); ef->errmsg[ERRLEN] = '\0'; return NULL; }; homelen = strlen(homedir); /* * ~user and ~ are usually followed by a directory separator to * separate them from the file contained in the home directory. * If the home directory is the root directory, then we don't want * to follow the home directory by a directory separator, so we must * erase it. */ if(strcmp(homedir, FS_ROOT_DIR) == 0 && strncmp(pptr, FS_DIR_SEP, FS_DIR_SEP_LEN) == 0) { skip = FS_DIR_SEP_LEN; }; /* * If needed, increase the size of the pathname buffer to allow it * to accomodate the home directory instead of the tilde expression. * Note that pptr may not be valid after this call. */ if(_pn_resize_path(ef->path, plen - usrlen - 1 - skip + homelen)==NULL) { strncpy(ef->errmsg, "Insufficient memory to expand filename", sizeof(ef->errmsg)); return NULL; }; /* * Move the part of the pathname that follows the tilde expression to * the end of where the home directory will need to be inserted. */ memmove(ef->path->name + homelen, ef->path->name + 1 + usrlen + skip, plen - usrlen - 1 - skip+1); /* * Write the home directory at the beginning of the string. */ for(i=0; ipath->name[i] = homedir[i]; }; /* * Copy the result into the cache, and return a pointer to the copy. */ return ef_cache_pathname(ef, ef->path->name, 0); } /*....................................................................... * Return a description of the last path-expansion error that occurred. * * Input: * ef ExpandFile * The path-expansion resource object. * Output: * return char * The description of the last error. */ const char *ef_last_error(ExpandFile *ef) { return ef ? ef->errmsg : "NULL ExpandFile argument"; } /*....................................................................... * Print out an array of matching files. * * Input: * result FileExpansion * The container of the sorted array of * expansions. * fp FILE * The output stream to write to. * term_width int The width of the terminal. * Output: * return int 0 - OK. * 1 - Error. */ int ef_list_expansions(FileExpansion *result, FILE *fp, int term_width) { int maxlen; /* The length of the longest matching string */ int width; /* The width of a column */ int ncol; /* The number of columns to list */ int nrow; /* The number of rows needed to list all of the expansions */ int row,col; /* The row and column being written to */ int i; /* * Check the arguments. */ if(!result || !fp) { fprintf(stderr, "ef_list_expansions: NULL argument(s).\n"); return 1; }; /* * Not enough space to list anything? */ if(term_width < 1) return 0; /* * Work out the maximum length of the matching filenames. */ maxlen = 0; for(i=0; infile; i++) { int len = strlen(result->files[i]); if(len > maxlen) maxlen = len; }; /* * Nothing to list? */ if(maxlen == 0) return 0; /* * Split the available terminal width into columns of maxlen + 2 characters. */ width = maxlen + 2; ncol = term_width / width; /* * If the column width is greater than the terminal width, the matches will * just have to overlap onto the next line. */ if(ncol < 1) ncol = 1; /* * How many rows will be needed? */ nrow = (result->nfile + ncol - 1) / ncol; /* * Print the expansions out in ncol columns, sorted in row order within each * column. */ for(row=0; row < nrow; row++) { for(col=0; col < ncol; col++) { int m = col*nrow + row; if(m < result->nfile) { const char *filename = result->files[m]; if(fprintf(fp, "%s%-*s%s", filename, (int) (ncol > 1 ? maxlen - strlen(filename):0), "", col #include #include #include #include #include #include #include #include #include #include #include #include #include #if HAVE_SYSV_PTY #include /* System-V stream I/O */ char *ptsname(int fd); int grantpt(int fd); int unlockpt(int fd); #endif #include "libtecla.h" /* * Pseudo-terminal devices are found in the following directory. */ #define PTY_DEV_DIR "/dev/" /* * Pseudo-terminal controller device file names start with the following * prefix. */ #define PTY_CNTRL "pty" /* * Pseudo-terminal slave device file names start with the following * prefix. */ #define PTY_SLAVE "tty" /* * Specify the maximum suffix length for the control and slave device * names. */ #define PTY_MAX_SUFFIX 10 /* * Set the maximum length of the master and slave terminal device filenames, * including space for a terminating '\0'. */ #define PTY_MAX_NAME (sizeof(PTY_DEV_DIR)-1 + \ (sizeof(PTY_SLAVE) > sizeof(PTY_CNTRL) ? \ sizeof(PTY_SLAVE) : sizeof(PTY_CNTRL))-1 \ + PTY_MAX_SUFFIX + 1) /* * Set the maximum length of an input line. */ #define PTY_MAX_LINE 4096 /* * Set the size of the buffer used for accumulating bytes written by the * user's terminal to its stdout. */ #define PTY_MAX_READ 1000 /* * Set the amount of memory used to record history. */ #define PTY_HIST_SIZE 10000 /* * Set the timeout delay used to check for quickly arriving * sequential output from the application. */ #define PTY_READ_TIMEOUT 100000 /* micro-seconds */ static int pty_open_master(const char *prog, int *cntrl, char *slave_name); static int pty_open_slave(const char *prog, char *slave_name); static int pty_child(const char *prog, int slave, char *argv[]); static int pty_parent(const char *prog, int cntrl); static int pty_stop_parent(int waserr, int cntrl, GetLine *gl, char *rbuff); static GL_FD_EVENT_FN(pty_read_from_program); static int pty_write_to_fd(int fd, const char *string, int n); static void pty_child_exited(int sig); static int pty_master_readable(int fd, long usec); /*....................................................................... * Run a program with enhanced terminal editing facilities. * * Usage: * enhance program [args...] */ int main(int argc, char *argv[]) { int cntrl = -1; /* The fd of the pseudo-terminal controller device */ int slave = -1; /* The fd of the pseudo-terminal slave device */ pid_t pid; /* The return value of fork() */ int status; /* The return statuses of the parent and child functions */ char slave_name[PTY_MAX_NAME]; /* The filename of the slave end of the */ /* pseudo-terminal. */ char *prog; /* The name of the program (ie. argv[0]) */ /* * Check the arguments. */ if(argc < 2) { fprintf(stderr, "Usage: %s [arguments...]\n", argv[0]); return 1; }; /* * Get the name of the program. */ prog = argv[0]; /* * If the user has the LC_CTYPE or LC_ALL environment variables set, * enable display of characters corresponding to the specified locale. */ (void) setlocale(LC_CTYPE, ""); /* * If the program is taking its input from a pipe or a file, or * sending its output to something other than a terminal, run the * program without tecla. */ if(!isatty(STDIN_FILENO) || !isatty(STDOUT_FILENO)) { if(execvp(argv[1], argv + 1) < 0) { fprintf(stderr, "%s: Unable to execute %s (%s).\n", prog, argv[1], strerror(errno)); fflush(stderr); _exit(1); }; }; /* * Open the master side of a pseudo-terminal pair, and return * the corresponding file descriptor and the filename of the * slave end of the pseudo-terminal. */ if(pty_open_master(prog, &cntrl, slave_name)) return 1; /* * Set up a signal handler to watch for the child process exiting. */ signal(SIGCHLD, pty_child_exited); /* * The above signal handler sends the parent process a SIGINT signal. * This signal is caught by gl_get_line(), which resets the terminal * settings, and if the application signal handler for this signal * doesn't abort the process, gl_get_line() returns NULL with errno * set to EINTR. Arrange to ignore the signal, so that gl_get_line() * returns and we have a chance to cleanup. */ signal(SIGINT, SIG_IGN); /* * We will read user input in one process, and run the user's program * in a child process. */ pid = fork(); if(pid < 0) { fprintf(stderr, "%s: Unable to fork child process (%s).\n", prog, strerror(errno)); return 1; }; /* * Are we the parent? */ if(pid!=0) { status = pty_parent(prog, cntrl); close(cntrl); } else { close(cntrl); /* The child doesn't use the slave device */ signal(SIGCHLD, pty_child_exited); if((slave = pty_open_slave(prog, slave_name)) >= 0) { status = pty_child(prog, slave, argv + 1); close(slave); } else { status = 1; }; }; return status; } /*....................................................................... * Open the master side of a pseudo-terminal pair, and return * the corresponding file descriptor and the filename of the * slave end of the pseudo-terminal. * * Input/Output: * prog const char * The name of this program. * cntrl int * The file descriptor of the pseudo-terminal * controller device will be assigned tp *cntrl. * slave_name char * The file-name of the pseudo-terminal slave device * will be recorded in slave_name[], which must have * at least PTY_MAX_NAME elements. * Output: * return int 0 - OK. * 1 - Error. */ static int pty_open_master(const char *prog, int *cntrl, char *slave_name) { char master_name[PTY_MAX_NAME]; /* The filename of the master device */ DIR *dir; /* The directory iterator */ struct dirent *file; /* A file in "/dev" */ /* * Mark the controller device as not opened yet. */ *cntrl = -1; /* * On systems with the Sys-V pseudo-terminal interface, we don't * have to search for a free master terminal. We just open /dev/ptmx, * and if there is a free master terminal device, we are given a file * descriptor connected to it. */ #if HAVE_SYSV_PTY *cntrl = open("/dev/ptmx", O_RDWR); if(*cntrl >= 0) { /* * Get the filename of the slave side of the pseudo-terminal. */ char *name = ptsname(*cntrl); if(name) { if(strlen(name)+1 > PTY_MAX_NAME) { fprintf(stderr, "%s: Slave pty filename too long.\n", prog); return 1; }; strncpy(slave_name, name, PTY_MAX_NAME); /* * If unable to get the slave name, discard the controller file descriptor, * ready to try a search instead. */ } else { close(*cntrl); *cntrl = -1; }; } else { #endif /* * On systems without /dev/ptmx, or if opening /dev/ptmx failed, * we open one master terminal after another, until one that isn't * in use by another program is found. * * Open the devices directory. */ dir = opendir(PTY_DEV_DIR); if(!dir) { fprintf(stderr, "%s: Couldn't open %s (%s)\n", prog, PTY_DEV_DIR, strerror(errno)); return 1; }; /* * Look for pseudo-terminal controller device files in the devices * directory. */ while(*cntrl < 0 && (file = readdir(dir))) { if(strncmp(file->d_name, PTY_CNTRL, sizeof(PTY_CNTRL)-1) == 0) { /* * Get the common extension of the control and slave filenames. */ const char *ext = file->d_name + sizeof(PTY_CNTRL)-1; if(strlen(ext) > PTY_MAX_SUFFIX) continue; /* * Attempt to open the control file. */ snprintf(master_name, sizeof(master_name), "%s%s%s", PTY_DEV_DIR, PTY_CNTRL, ext); *cntrl = open(master_name, O_RDWR); if(*cntrl < 0) continue; /* * Attempt to open the matching slave file. */ snprintf(slave_name, PTY_MAX_NAME, "%s%s%s", PTY_DEV_DIR, PTY_SLAVE, ext); }; }; closedir(dir); #if HAVE_SYSV_PTY }; #endif /* * Did we fail to find a pseudo-terminal pair that we could open? */ if(*cntrl < 0) { fprintf(stderr, "%s: Unable to find a free pseudo-terminal.\n", prog); return 1; }; /* * System V systems require the program that opens the master to * grant access to the slave side of the pseudo-terminal. */ #ifdef HAVE_SYSV_PTY if(grantpt(*cntrl) < 0 || unlockpt(*cntrl) < 0) { fprintf(stderr, "%s: Unable to unlock terminal (%s).\n", prog, strerror(errno)); return 1; }; #endif /* * Success. */ return 0; } /*....................................................................... * Open the slave end of a pseudo-terminal. * * Input: * prog const char * The name of this program. * slave_name char * The filename of the slave device. * Output: * return int The file descriptor of the successfully opened * slave device, or < 0 on error. */ static int pty_open_slave(const char *prog, char *slave_name) { int fd; /* The file descriptor of the slave device */ /* * Place the process in its own process group. In system-V based * OS's, this ensures that when the pseudo-terminal is opened, it * becomes the controlling terminal of the process. */ if(setsid() < 0) { fprintf(stderr, "%s: Unable to form new process group (%s).\n", prog, strerror(errno)); return -1; }; /* * Attempt to open the specified device. */ fd = open(slave_name, O_RDWR); if(fd < 0) { fprintf(stderr, "%s: Unable to open pseudo-terminal slave device (%s).\n", prog, strerror(errno)); return -1; }; /* * On system-V streams based systems, we need to push the stream modules * that implement pseudo-terminal and termio interfaces. At least on * Solaris, which pushes these automatically when a slave is opened, * this is redundant, so ignore errors when pushing the modules. */ #if HAVE_SYSV_PTY (void) ioctl(fd, I_PUSH, "ptem"); (void) ioctl(fd, I_PUSH, "ldterm"); /* * On BSD based systems other than SunOS 4.x, the following makes the * pseudo-terminal the controlling terminal of the child process. * According to the pseudo-terminal example code in Steven's * Advanced programming in the unix environment, the !defined(CIBAUD) * part of the clause prevents this from being used under SunOS. Since * I only have his code with me, and won't have access to the book, * I don't know why this is necessary. */ #elif defined(TIOCSCTTY) && !defined(CIBAUD) if(ioctl(fd, TIOCSCTTY, (char *) 0) < 0) { fprintf(stderr, "%s: Unable to establish controlling terminal (%s).\n", prog, strerror(errno)); close(fd); return -1; }; #endif return fd; } /*....................................................................... * Read input from the controlling terminal of the program, using * gl_get_line(), and feed it to the user's program running in a child * process, via the controller side of the pseudo-terminal. Also pass * data received from the user's program via the conroller end of * the pseudo-terminal, to stdout. * * Input: * prog const char * The name of this program. * cntrl int The file descriptor of the controller end of the * pseudo-terminal. * Output: * return int 0 - OK. * 1 - Error. */ static int pty_parent(const char *prog, int cntrl) { GetLine *gl = NULL; /* The gl_get_line() resource object */ char *line; /* An input line read from the user */ char *rbuff=NULL; /* A buffer for reading from the pseudo terminal */ /* * Allocate the gl_get_line() resource object. */ gl = new_GetLine(PTY_MAX_LINE, PTY_HIST_SIZE); if(!gl) return pty_stop_parent(1, cntrl, gl, rbuff); /* * Allocate a buffer to use to accumulate bytes read from the * pseudo-terminal. */ rbuff = (char *) malloc(PTY_MAX_READ+1); if(!rbuff) return pty_stop_parent(1, cntrl, gl, rbuff); rbuff[0] = '\0'; /* * Register an event handler to watch for data appearing from the * user's program on the controller end of the pseudo terminal. */ if(gl_watch_fd(gl, cntrl, GLFD_READ, pty_read_from_program, rbuff)) return pty_stop_parent(1, cntrl, gl, rbuff); /* * Read input lines from the user and pass them on to the user's program, * by writing to the controller end of the pseudo-terminal. */ while((line=gl_get_line(gl, rbuff, NULL, 0))) { if(pty_write_to_fd(cntrl, line, strlen(line))) return pty_stop_parent(1, cntrl, gl, rbuff); rbuff[0] = '\0'; }; return pty_stop_parent(0, cntrl, gl, rbuff); } /*....................................................................... * This is a private return function of pty_parent(), used to release * dynamically allocated resources, close the controller end of the * pseudo-terminal, and wait for the child to exit. It returns the * exit status of the child process, unless the caller reports an * error itself, in which case the caller's error status is returned. * * Input: * waserr int True if the caller is calling this function because * an error occured. * cntrl int The file descriptor of the controller end of the * pseudo-terminal. * gl GetLine * The resource object of gl_get_line(). * rbuff char * The buffer used to accumulate bytes read from * the pseudo-terminal. * Output: * return int The desired exit status of the program. */ static int pty_stop_parent(int waserr, int cntrl, GetLine *gl, char *rbuff) { int status; /* The return status of the child process */ /* * Close the controller end of the terminal. */ close(cntrl); /* * Delete the resource object. */ gl = del_GetLine(gl); /* * Delete the read buffer. */ if(rbuff) free(rbuff); /* * Wait for the user's program to end. */ (void) wait(&status); /* * Return either our error status, or the return status of the child * program. */ return waserr ? 1 : status; } /*....................................................................... * Run the user's program, with its stdin and stdout connected to the * slave end of the psuedo-terminal. * * Input: * prog const char * The name of this program. * slave int The file descriptor of the slave end of the * pseudo terminal. * argv char *[] The argument vector to pass to the user's program, * where argv[0] is the name of the user's program, * and the last argument is followed by a pointer * to NULL. * Output: * return int If this function returns at all, an error must * have occured when trying to overlay the process * with the user's program. In this case 1 is * returned. */ static int pty_child(const char *prog, int slave, char *argv[]) { struct termios attr; /* The terminal attributes */ /* * We need to stop the pseudo-terminal from echoing everything that we send it. */ if(tcgetattr(slave, &attr)) { fprintf(stderr, "%s: Can't get pseudo-terminal attributes (%s).\n", prog, strerror(errno)); return 1; }; attr.c_lflag &= ~(ECHO); while(tcsetattr(slave, TCSADRAIN, &attr)) { if(errno != EINTR) { fprintf(stderr, "%s: tcsetattr error: %s\n", prog, strerror(errno)); return 1; }; }; /* * Arrange for stdin, stdout and stderr to be connected to the slave device, * ignoring errors that imply that either stdin or stdout is closed. */ while(dup2(slave, STDIN_FILENO) < 0 && errno==EINTR) ; while(dup2(slave, STDOUT_FILENO) < 0 && errno==EINTR) ; while(dup2(slave, STDERR_FILENO) < 0 && errno==EINTR) ; /* * Run the user's program. */ if(execvp(argv[0], argv) < 0) { fprintf(stderr, "%s: Unable to execute %s (%s).\n", prog, argv[0], strerror(errno)); fflush(stderr); _exit(1); }; return 0; /* This should never be reached */ } /*....................................................................... * This is the event-handler that is called by gl_get_line() whenever * there is tet waiting to be read from the user's program, via the * controller end of the pseudo-terminal. See libtecla.h for details * about its arguments. */ static GL_FD_EVENT_FN(pty_read_from_program) { char *nlptr; /* A pointer to the last newline in the accumulated string */ char *crptr; /* A pointer to the last '\r' in the accumulated string */ char *nextp; /* A pointer to the next unprocessed character */ /* * Get the read buffer in which we are accumulating a line to be * forwarded to stdout. */ char *rbuff = (char *) data; /* * New data may arrive while we are processing the current read, and * it is more efficient to display this here than to keep returning to * gl_get_line() and have it display the latest prefix as a prompt, * followed by the current input line, so we loop, delaying a bit at * the end of each iteration to check for more data arriving from * the application, before finally returning to gl_get_line() when * no more input is available. */ do { /* * Get the current length of the output string. */ int len = strlen(rbuff); /* * Read the text from the program. */ int nnew = read(fd, rbuff + len, PTY_MAX_READ - len); if(nnew < 0) return GLFD_ABORT; len += nnew; /* * Nul terminate the accumulated string. */ rbuff[len] = '\0'; /* * Find the last newline and last carriage return in the buffer, if any. */ nlptr = strrchr(rbuff, '\n'); crptr = strrchr(rbuff, '\r'); /* * We want to output up to just before the last newline or carriage * return. If there are no newlines of carriage returns in the line, * and the buffer is full, then we should output the whole line. In * all cases a new output line will be started after the latest text * has been output. The intention is to leave any incomplete line * in the buffer, for (perhaps temporary) use as the current prompt. */ if(nlptr) { nextp = crptr && crptr < nlptr ? crptr : nlptr; } else if(crptr) { nextp = crptr; } else if(len >= PTY_MAX_READ) { nextp = rbuff + len; } else { nextp = NULL; }; /* * Do we have any text to output yet? */ if(nextp) { /* * If there was already some text in rbuff before this function * was called, then it will have been used as a prompt. Arrange * to rewrite this prefix, plus the new suffix, by moving back to * the start of the line. */ if(len > 0) (void) pty_write_to_fd(STDOUT_FILENO, "\r", 1); /* * Write everything up to the last newline to stdout. */ (void) pty_write_to_fd(STDOUT_FILENO, rbuff, nextp - rbuff); /* * Start a new line. */ (void) pty_write_to_fd(STDOUT_FILENO, "\r\n", 2); /* * Skip trailing carriage returns and newlines. */ while(*nextp=='\n' || *nextp=='\r') nextp++; /* * Move any unwritten text following the newline, to the start of the * buffer. */ memmove(rbuff, nextp, len - (nextp - rbuff) + 1); }; } while(pty_master_readable(fd, PTY_READ_TIMEOUT)); /* * Make the incomplete line in the output buffer the current prompt. */ gl_replace_prompt(gl, rbuff); return GLFD_REFRESH; } /*....................................................................... * Write a given string to a specified file descriptor. * * Input: * fd int The file descriptor to write to. * string const char * The string to write (of at least 'n' characters). * n int The number of characters to write. * Output: * return int 0 - OK. * 1 - Error. */ static int pty_write_to_fd(int fd, const char *string, int n) { int ndone = 0; /* The number of characters written so far */ /* * Do as many writes as are needed to write the whole string. */ while(ndone < n) { int nnew = write(fd, string + ndone, n - ndone); if(nnew > 0) ndone += nnew; else if(errno != EINTR) return 1; }; return 0; } /*....................................................................... * This is the signal handler that is called when the child process * that is running the user's program exits for any reason. It closes * the slave end of the terminal, so that gl_get_line() in the parent * process sees an end of file. */ static void pty_child_exited(int sig) { raise(SIGINT); } /*....................................................................... * Return non-zero after a given amount of time if there is data waiting * to be read from a given file descriptor. * * Input: * fd int The descriptor to watch. * usec long The number of micro-seconds to wait for input to * arrive before giving up. * Output: * return int 0 - No data is waiting to be read (or select isn't * available). * 1 - Data is waiting to be read. */ static int pty_master_readable(int fd, long usec) { #if HAVE_SELECT fd_set rfds; /* The set of file descriptors to check */ struct timeval timeout; /* The timeout */ FD_ZERO(&rfds); FD_SET(fd, &rfds); timeout.tv_sec = 0; timeout.tv_usec = usec; return select(fd+1, &rfds, NULL, NULL, &timeout) == 1; #else return 0; #endif } xorp/cli/libtecla/man3/0000775000076400007640000000000011421137511015040 5ustar greearbgreearbxorp/cli/libtecla/man3/new_CplFileConf.30000664000076400007640000000003511421137511020117 0ustar greearbgreearb.so man3/cpl_complete_word.3 xorp/cli/libtecla/man3/gl_watch_fd.30000664000076400007640000000002711421137511017364 0ustar greearbgreearb.so man3/gl_get_line.3 xorp/cli/libtecla/man3/gl_echo_mode.30000664000076400007640000000002711421137511017527 0ustar greearbgreearb.so man3/gl_get_line.3 xorp/cli/libtecla/man3/cpl_last_error.30000664000076400007640000000003511421137511020134 0ustar greearbgreearb.so man3/cpl_complete_word.3 xorp/cli/libtecla/man3/gl_size_of_history.30000664000076400007640000000002711421137511021024 0ustar greearbgreearb.so man3/gl_get_line.3 xorp/cli/libtecla/man3/gl_ignore_signal.30000664000076400007640000000002711421137511020425 0ustar greearbgreearb.so man3/gl_get_line.3 xorp/cli/libtecla/man3/ef_list_expansions.30000664000076400007640000000003211421137511021013 0ustar greearbgreearb.so man3/ef_expand_file.3 xorp/cli/libtecla/man3/pca_scan_path.30000664000076400007640000000003311421137511017703 0ustar greearbgreearb.so man3/pca_lookup_file.3 xorp/cli/libtecla/man3/gl_state_of_history.30000664000076400007640000000002711421137511021172 0ustar greearbgreearb.so man3/gl_get_line.3 xorp/cli/libtecla/man3/cpl_add_completion.30000664000076400007640000000003511421137511020741 0ustar greearbgreearb.so man3/cpl_complete_word.3 xorp/cli/libtecla/man3/pca_set_check_fn.30000664000076400007640000000003311421137511020356 0ustar greearbgreearb.so man3/pca_lookup_file.3 xorp/cli/libtecla/man3/new_PcaPathConf.30000664000076400007640000000003311421137511020117 0ustar greearbgreearb.so man3/pca_lookup_file.3 xorp/cli/libtecla/man3/gl_get_line.30000664000076400007640000030073011421137511017377 0ustar greearbgreearb.\" Copyright (C) 2000, 2001 by Martin C. Shepherd .\" .\" All rights reserved. .\" .\" 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, including .\" without limitation the rights to use, copy, modify, merge, publish, .\" distribute, and/or sell copies of the Software, and to permit persons .\" to whom the Software is furnished to do so, provided that the above .\" copyright notice(s) and this permission notice appear in all copies of .\" the Software and that both the above copyright notice(s) and this .\" permission notice appear in supporting documentation. .\" .\" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS .\" OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF .\" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT .\" OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR .\" HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL .\" INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING .\" FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, .\" NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION .\" WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .\" Except as contained in this notice, the name of a copyright holder .\" shall not be used in advertising or otherwise to promote the sale, use .\" or other dealings in this Software without prior written authorization .\" of the copyright holder. .TH gl_get_line 3 .SH NAME gl_get_line, new_GetLine, del_GetLine, gl_customize_completion, gl_change_terminal, gl_configure_getline, gl_load_history, gl_save_history, gl_group_history, gl_show_history, gl_watch_fd, gl_terminal_size, gl_resize_history, gl_limit_history, gl_clear_history, gl_toggle_history, gl_lookup_history, gl_state_of_history, gl_range_of_history, gl_size_of_history, gl_echo_mode, gl_replace_prompt, gl_prompt_style, gl_ignore_signal, gl_trap_signal, gl_last_signal \- allow the user to compose an input line .SH SYNOPSIS .nf #include #include GetLine *new_GetLine(size_t linelen, size_t histlen); GetLine *del_GetLine(GetLine *gl); char *gl_get_line(GetLine *gl, const char *prompt, const char *start_line, int start_pos); int gl_customize_completion(GetLine *gl, void *data, CplMatchFn *match_fn); int gl_change_terminal(GetLine *gl, FILE *input_fp, FILE *output_fp, const char *term); int gl_configure_getline(GetLine *gl, const char *app_string, const char *app_file, const char *user_file); int gl_save_history(GetLine *gl, const char *filename, const char *comment, int max_lines); int gl_load_history(GetLine *gl, const char *filename, const char *comment); int gl_watch_fd(GetLine *gl, int fd, GlFdEvent event, GlFdEventFn *callback, void *data); int gl_group_history(GetLine *gl, unsigned stream); int gl_show_history(GetLine *gl, FILE *fp, const char *fmt, int all_groups, int max_lines); int gl_resize_history(GetLine *gl, size_t bufsize); void gl_limit_history(GetLine *gl, int max_lines); void gl_clear_history(GetLine *gl, int all_groups); void gl_toggle_history(GetLine *gl, int enable); GlTerminalSize gl_terminal_size(GetLine *gl, int def_ncolumn, int def_nline); int gl_lookup_history(GetLine *gl, unsigned long id, GlHistoryLine *hline); void gl_state_of_history(GetLine *gl, GlHistoryState *state); void gl_range_of_history(GetLine *gl, GlHistoryRange *range); void gl_size_of_history(GetLine *gl, GlHistorySize *size); void gl_echo_mode(GetLine *gl, int enable); void gl_replace_prompt(GetLine *gl, const char *prompt); void gl_prompt_style(GetLine *gl, GlPromptStyle style); int gl_ignore_signal(GetLine *gl, int signo); int gl_trap_signal(GetLine *gl, int signo, unsigned flags, GlAfterSignal after, int errno_value); int gl_last_signal(const GetLine *gl); .fi .SH DESCRIPTION The \f3gl_get_line()\f1 function is part of the tecla library (see the libtecla(3) man page). If the user is typing at a terminal, it prompts them for an line of input, then provides interactive editing facilities, similar to those of the unix \f3tcsh\f1 shell. In addition to simple command-line editing, it supports recall of previously entered command lines, TAB completion of file names, and in-line wild-card expansion of filenames. .sp .SH AN EXAMPLE The following shows a complete example of how to use the \f3gl_get_line()\f1 function to get input from the user: .nf #include #include #include int main(int argc, char *argv[]) { char *line; /* The line that the user typed */ GetLine *gl; /* The gl_get_line() resource object */ setlocale(LC_CTYPE, ""); /* Adopt the user's choice */ /* of character set. */ gl = new_GetLine(1024, 2048); if(!gl) return 1; while((line=gl_get_line(gl, "$ ", NULL, -1)) != NULL && strcmp(line, "exit\\n") != 0) printf("You typed: %s\\n", line); gl = del_GetLine(gl); return 0; } .fi .sp In the example, first the resources needed by the \f3gl_get_line()\f1 function are created by calling \f3new_GetLine()\f1. This allocates the memory used in subsequent calls to the \f3gl_get_line()\f1 function, including the history buffer for recording previously entered lines. Then one or more lines are read from the user, until either an error occurs, or the user types \f3exit\f1. Then finally the resources that were allocated by \f3new_GetLine()\f1, are returned to the system by calling \f3del_GetLine()\f1. Note the use of the \f3NULL\f1 return value of \f3del_GetLine()\f1 to make \f3gl\f1 \f3NULL\f1. This is a safety precaution. If the program subsequently attempts to pass \f3gl\f1 to \f3gl_get_line()\f1, said function will complain, and return an error, instead of attempting to use the deleted resource object. .sp .SH THE FUNCTIONS USED IN THE EXAMPLE The descriptions of the functions used in the example are as follows: .sp .nf GetLine *new_GetLine(size_t linelen, size_t histlen) .fi .sp This function creates the resources used by the \f3gl_get_line()\f1 function and returns an opaque pointer to the object that contains them. The maximum length of an input line is specified via the \f3linelen\f1 argument, and the number of bytes to allocate for storing history lines is set by the \f3histlen\f1 argument. History lines are stored back-to-back in a single buffer of this size. Note that this means that the number of history lines that can be stored at any given time, depends on the lengths of the individual lines. If you want to place an upper limit on the number of lines that can be stored, see the \f3gl_limit_history()\f1 function described later. If you don't want history at all, specify \f3histlen\f1 as zero, and no history buffer will be allocated. .sp On error, a message is printed to \f3stderr\f1 and \f3NULL\f1 is returned. .sp .nf GetLine *del_GetLine(GetLine *gl) .fi .sp This function deletes the resources that were returned by a previous call to \f3new_GetLine()\f1. It always returns \f3NULL\f1 (ie a deleted object). It does nothing if the \f3gl\f1 argument is \f3NULL\f1. .sp .nf char *gl_get_line(GetLine *gl, const char *prompt, const char *start_line, int start_pos); .fi .sp The \f3gl_get_line()\f1 function can be called any number of times to read input from the user. The \f3gl\f1 argument must have been previously returned by a call to \f3new_GetLine()\f1. The \f3prompt\f1 argument should be a normal \f3NUL\f1 terminated string, specifying the prompt to present the user with. By default prompts are displayed literally, but if enabled with the \f3gl_prompt_style()\f1 function (see later), prompts can contain directives to do underlining, switch to and from bold fonts, or turn highlighting on and off. If you want to specify the initial contents of the line, for the user to edit, pass the desired string via the \f3start_line\f1 argument. You can then specify which character of this line the cursor is initially positioned over, using the \f3start_pos\f1 argument. This should be -1 if you want the cursor to follow the last character of the start line. If you don't want to preload the line in this manner, send \f3start_line\f1 as \f3NULL\f1, and set \f3start_pos\f1 to -1. The \f3gl_get_line()\f1 function returns a pointer to the line entered by the user, or \f3NULL\f1 on error or at the end of the input. The returned pointer is part of the specified \f3gl\f1 resource object, and thus should not be free'd by the caller, or assumed to be unchanging from one call to the next. When reading from a user at a terminal, there will always be a newline character at the end of the returned line. When standard input is being taken from a pipe or a file, there will similarly be a newline unless the input line was too long to store in the internal buffer. In the latter case you should call \f3gl_get_line()\f1 again to read the rest of the line. Note that this behavior makes \f3gl_get_line()\f1 similar to \f3fgets()\f1. In fact when \f3stdin\f1 isn't connected to a terminal,\f3gl_get_line()\f1 just calls \f3fgets()\f1. .SH OPTIONAL PROMPT FORMATTING Whereas by default the prompt string that you specify is displayed literally, without any special interpretation of the characters within it, the \f3gl_prompt_style()\f1 function can be used to enable optional formatting directives within the prompt. .sp .nf void gl_prompt_style(GetLine *gl, GlPromptStyle style); .fi .sp The \f3style\f1 argument, which specifies the formatting style, can take any of the following values: .sp .nf GL_FORMAT_PROMPT - In this style, the formatting directives described below, when included in prompt strings, are interpreted as follows: %B - Display subsequent characters with a bold font. %b - Stop displaying characters with the bold font. %F - Make subsequent characters flash. %f - Turn off flashing characters. %U - Underline subsequent characters. %u - Stop underlining characters. %P - Switch to a pale (half brightness) font. %p - Stop using the pale font. %S - Highlight subsequent characters (also known as standout mode). %s - Stop highlighting characters. %V - Turn on reverse video. %v - Turn off reverse video. %% - Display a single % character. For example, in this mode, a prompt string like \f3"%UOK%u$ "\f1 would display the prompt \f3"OK$ "\f1, but with the \f3OK\f1 part underlined. Note that although a pair of characters that starts with a % character, but doesn't match any of the above directives is displayed literally, if a new directive is subsequently introduced which does match, the displayed prompt will change, so it is better to always use %% to display a literal %. Also note that not all terminals support all of these text attributes, and that some substitute a different attribute for missing ones. GL_LITERAL_PROMPT - In this style, the prompt string is printed literally. This is the default style. .fi .sp .SH THE AVAILABLE KEY BINDING FUNCTIONS The \f3gl_get_line()\f1 function provides a number of functions which can be bound to key sequences. The names of these functions, and what they do, are given below. .nf user-interrupt - Send a SIGINT signal to the parent process. abort - Send a SIGABRT signal to the parent process. suspend - Suspend the parent process. stop-output - Pause terminal output. start-output - Resume paused terminal output. literal-next - Arrange for the next character to be treated as a normal character. This allows control characters to be entered. cursor-right - Move the cursor one character right. cursor-left - Move the cursor one character left. insert-mode - Toggle between insert mode and overwrite mode. beginning-of-line - Move the cursor to the beginning of the line. end-of-line - Move the cursor to the end of the line. delete-line - Delete the contents of the current line. kill-line - Delete everything that follows the cursor. backward-kill-line - Delete all characters between the cursor and the start of the line. forward-word - Move to the end of the word which follows the cursor. forward-to-word - Move the cursor to the start of the word that follows the cursor. backward-word - Move to the start of the word which precedes the cursor. goto-column - Move the cursor to the 1-relative column in the line specified by any preceding digit-argument sequences (see ENTERING REPEAT COUNTS below). find-parenthesis - If the cursor is currently over a parenthesis character, move it to the matching parenthesis character. If not over a parenthesis character move right to the next close parenthesis. forward-delete-char - Delete the character under the cursor. backward-delete-char - Delete the character which precedes the cursor. list-or-eof - This is intended for binding to ^D. When invoked when the cursor is within the line it displays all possible completions then redisplays the line unchanged. When invoked on an empty line, it signals end-of-input (EOF) to the caller of gl_get_line(). del-char-or-list-or-eof - This is intended for binding to ^D. When invoked when the cursor is within the line it invokes forward-delete-char. When invoked at the end of the line it displays all possible completions then redisplays the line unchanged. When invoked on an empty line, it signals end-of-input (EOF) to the caller of gl_get_line(). forward-delete-word - Delete the word which follows the cursor. backward-delete-word - Delete the word which precedes the cursor. upcase-word - Convert all of the characters of the word which follows the cursor, to upper case. downcase-word - Convert all of the characters of the word which follows the cursor, to lower case. capitalize-word - Capitalize the word which follows the cursor. change-case - If the next character is upper case, toggle it to lower case and vice versa. redisplay - Redisplay the line. clear-screen - Clear the terminal, then redisplay the current line. transpose-chars - Swap the character under the cursor with the character just before the cursor. set-mark - Set a mark at the position of the cursor. exchange-point-and-mark - Move the cursor to the last mark that was set, and move the mark to where the cursor used to be. kill-region - Delete the characters that lie between the last mark that was set, and the cursor. copy-region-as-kill - Copy the text between the mark and the cursor to the cut buffer, without deleting the original text. yank - Insert the text that was last deleted, just before the current position of the cursor. append-yank - Paste the current contents of the cut buffer, after the cursor. up-history - Recall the next oldest line that was entered. Note that in vi mode you are left in command mode. down-history - Recall the next most recent line that was entered. If no history recall session is currently active, the next line from a previous recall session is recalled. Note that in vi mode you are left in command mode. history-search-backward - Recall the next oldest line who's prefix matches the string which currently precedes the cursor (in vi command-mode the character under the cursor is also included in the search string). Note that in vi mode you are left in command mode. history-search-forward - Recall the next newest line who's prefix matches the string which currently precedes the cursor (in vi command-mode the character under the cursor is also included in the search string). Note that in vi mode you are left in command mode. history-re-search-backward -Recall the next oldest line who's prefix matches that established by the last invocation of either history-search-forward or history-search-backward. history-re-search-forward - Recall the next newest line who's prefix matches that established by the last invocation of either history-search-forward or history-search-backward. complete-word - Attempt to complete the incomplete word which precedes the cursor. Unless the host program has customized word completion, filename completion is attempted. In vi commmand mode the character under the cursor is also included in the word being completed, and you are left in vi insert mode. expand-filename - Within the command line, expand wild cards, tilde expressions and dollar expressions in the filename which immediately precedes the cursor. In vi commmand mode the character under the cursor is also included in the filename being expanded, and you are left in vi insert mode. list-glob - List any filenames which match the wild-card, tilde and dollar expressions in the filename which immediately precedes the cursor, then redraw the input line unchanged. list-history - Display the contents of the history list for the current history group. If a repeat count of > 1 is specified, only that many of the most recent lines are displayed. See the "ENTERING REPEAT COUNTS" section. read-from-file - Temporarily switch to reading input from the file who's name precedes the cursor. read-init-files - Re-read teclarc configuration files. beginning-of-history - Move to the oldest line in the history list. Note that in vi mode you are left in command mode. end-of-history - Move to the newest line in the history list (ie. the current line). Note that in vi mode this leaves you in command mode. digit-argument - Enter a repeat count for the next key-binding function. For details, see the ENTERING REPEAT COUNTS section. newline - Terminate and return the current contents of the line, after appending a newline character. The newline character is normally '\\n', but will be the first character of the key-sequence that invoked the newline action, if this happens to be a printable character. If the action was invoked by the '\\n' newline character or the '\\r' carriage return character, the line is appended to the history buffer. repeat-history - Return the line that is being edited, then arrange for the next most recent entry in the history buffer to be recalled when \f3gl_get_line()\f1 is next called. Repeatedly invoking this action causes successive historical input lines to be re-executed. Note that this action is equivalent to the 'Operate' action in ksh. ring-bell - Ring the terminal bell, unless the bell has been silenced via the \f3nobeep\f1 configuration option (see the THE TECLA CONFIGURATION FILE section). forward-copy-char - Copy the next character into the cut buffer (NB. use repeat counts to copy more than one). backward-copy-char - Copy the previous character into the cut buffer. forward-copy-word - Copy the next word into the cut buffer. backward-copy-word - Copy the previous word into the cut buffer. forward-find-char - Move the cursor to the next occurrence of the next character that you type. backward-find-char - Move the cursor to the last occurrence of the next character that you type. forward-to-char - Move the cursor to the character just before the next occurrence of the next character that the user types. backward-to-char - Move the cursor to the character just after the last occurrence before the cursor of the next character that the user types. repeat-find-char - Repeat the last backward-find-char, forward-find-char, backward-to-char or forward-to-char. invert-refind-char - Repeat the last backward-find-char, forward-find-char, backward-to-char, or forward-to-char in the opposite direction. delete-to-column - Delete the characters from the cursor up to the column that is specified by the repeat count. delete-to-parenthesis - Delete the characters from the cursor up to and including the matching parenthesis, or next close parenthesis. forward-delete-find - Delete the characters from the cursor up to and including the following occurence of the next character typed. backward-delete-find - Delete the characters from the cursor up to and including the preceding occurence of the next character typed. forward-delete-to - Delete the characters from the cursor up to, but not including, the following occurence of the next character typed. backward-delete-to - Delete the characters from the cursor up to, but not including, the preceding occurence of the next character typed. delete-refind - Repeat the last *-delete-find or *-delete-to action. delete-invert-refind - Repeat the last *-delete-find or *-delete-to action, in the opposite direction. copy-to-column - Copy the characters from the cursor up to the column that is specified by the repeat count, into the cut buffer. copy-to-parenthesis - Copy the characters from the cursor up to and including the matching parenthesis, or next close parenthesis, into the cut buffer. forward-copy-find - Copy the characters from the cursor up to and including the following occurence of the next character typed, into the cut buffer. backward-copy-find - Copy the characters from the cursor up to and including the preceding occurence of the next character typed, into the cut buffer. forward-copy-to - Copy the characters from the cursor up to, but not including, the following occurence of the next character typed, into the cut buffer. backward-copy-to - Copy the characters from the cursor up to, but not including, the preceding occurence of the next character typed, into the cut buffer. copy-refind - Repeat the last *-copy-find or *-copy-to action. copy-invert-refind - Repeat the last *-copy-find or *-copy-to action, in the opposite direction. vi-mode - Switch to vi mode from emacs mode. emacs-mode - Switch to emacs mode from vi mode. vi-insert - From vi command mode, switch to insert mode. vi-overwrite - From vi command mode, switch to overwrite mode. vi-insert-at-bol - From vi command mode, move the cursor to the start of the line and switch to insert mode. vi-append-at-eol - From vi command mode, move the cursor to the end of the line and switch to append mode. vi-append - From vi command mode, move the cursor one position right, and switch to insert mode. vi-replace-char - From vi command mode, replace the character under the cursor with the the next character entered. vi-forward-change-char - From vi command mode, delete the next character then enter insert mode. vi-backward-change-char - From vi command mode, delete the preceding character then enter insert mode. vi-forward-change-word - From vi command mode, delete the next word then enter insert mode. vi-backward-change-word - From vi command mode, delete the preceding word then enter insert mode. vi-change-rest-of-line - From vi command mode, delete from the cursor to the end of the line, then enter insert mode. vi-change-line - From vi command mode, delete the current line, then enter insert mode. vi-change-to-bol - From vi command mode, delete all characters between the cursor and the beginning of the line, then enter insert mode. vi-change-to-column - From vi command mode, delete the characters from the cursor up to the column that is specified by the repeat count, then enter insert mode. vi-change-to-parenthesis - Delete the characters from the cursor up to and including the matching parenthesis, or next close parenthesis, then enter vi insert mode. vi-forward-change-find - From vi command mode, delete the characters from the cursor up to and including the following occurence of the next character typed, then enter insert mode. vi-backward-change-find - From vi command mode, delete the characters from the cursor up to and including the preceding occurence of the next character typed, then enter insert mode. vi-forward-change-to - From vi command mode, delete the characters from the cursor up to, but not including, the following occurence of the next character typed, then enter insert mode. vi-backward-change-to - From vi command mode, delete the characters from the cursor up to, but not including, the preceding occurence of the next character typed, then enter insert mode. vi-change-refind - Repeat the last vi-*-change-find or vi-*-change-to action. vi-change-invert-refind - Repeat the last vi-*-change-find or vi-*-change-to action, in the opposite direction. vi-undo - In vi mode, undo the last editing operation. vi-repeat-change - In vi command mode, repeat the last command that modified the line. .fi .SH DEFAULT KEY BINDINGS IN EMACS MODE The following default key bindings, which can be overriden by the tecla configuration file, are designed to mimic most of the bindings of the unix \f3tcsh\f1 shell, when it is in emacs editing mode. .sp This is the default editing mode of the tecla library. .sp Note that a key sequence like \f3^A\f1 or \f3C-a\f1 means hold the control-key down while pressing the letter \f3A\f1, and that where you see \f3\\E\f1 or \f3M-\f1 in a binding, this represents the escape key or the Meta modifier key. Also note that to \f3gl_get_line()\f1, pressing the escape key before a key is equivalent to pressing the meta key at the same time as that key. Thus the key sequence \f3M-p\f1 can be typed in two ways, by pressing the escape key, followed by pressing \f3p\f1, or by pressing the Meta key at the same time as \f3p\f1. .sp Under UNIX the terminal driver sets a number of special keys for certain functions. The tecla library attempts to use the same keybindings to maintain consistency. The key sequences shown for the following 6 bindings are thus just examples of what they will probably be set to. If you have used the \f3stty\f1 command to change these keys, then the default bindings should match. .nf ^C -> user-interrupt ^\\ -> abort ^Z -> suspend ^Q -> start-output ^S -> stop-output ^V -> literal-next .fi The cursor keys are refered to by name, as follows. This is necessary because different types of terminals generate different key sequences when their cursor keys are pressed. right -> cursor-right left -> cursor-left up -> up-history down -> down-history The remaining bindings don't depend on the terminal setttings. .nf ^F -> cursor-right ^B -> cursor-left M-i -> insert-mode ^A -> beginning-of-line ^E -> end-of-line ^U -> delete-line ^K -> kill-line M-f -> forward-word M-b -> backward-word ^D -> del-char-or-list-or-eof ^H -> backward-delete-char ^? -> backward-delete-char M-d -> forward-delete-word M-^H -> backward-delete-word M-^? -> backward-delete-word M-u -> upcase-word M-l -> downcase-word M-c -> capitalize-word ^R -> redisplay ^L -> clear-screen ^T -> transpose-chars ^@ -> set-mark ^X^X -> exchange-point-and-mark ^W -> kill-region M-w -> copy-region-as-kill ^Y -> yank ^P -> up-history ^N -> down-history M-p -> history-search-backward M-n -> history-search-forward ^I -> complete-word ^X* -> expand-filename ^X^F -> read-from-file ^X^R -> read-init-files ^Xg -> list-glob ^Xh -> list-history M-< -> beginning-of-history M-> -> end-of-history \\n -> newline \\r -> newline M-o -> repeat-history M-^V -> vi-mode M-0, M-1, ... M-9 -> digit-argument (see below) .fi Note that ^I is what the TAB key generates, and that ^@ can be generated not only by pressing the control key and the @ key simultaneously, but also by pressing the control key and the space bar at the same time. .SH DEFAULT KEY BINDINGS IN VI MODE The following default key bindings are designed to mimic the vi style of editing as closely as possible. This means that very few editing functions are provided in the initial character input mode, editing functions instead being provided by the vi command mode. Vi command mode is entered whenever the escape character is pressed, or whenever a key-sequence that starts with a meta character is entered. In addition to mimicing vi, libtecla provides bindings for tab completion, wild-card expansion of file names, and historical line recall. .sp To learn how to tell the tecla library to use vi mode instead of the default emacs editing mode, see the section entitled THE TECLA CONFIGURATION FILE. .sp As already mentioned above in the emacs section, Note that a key sequence like \f3^A\f1 or \f3C-a\f1 means hold the control-key down while pressing the letter \f3A\f1, and that where you see \f3\\E\f1 or \f3M-\f1 in a binding, this represents the escape key or the Meta modifier key. Also note that to \f3gl_get_line()\f1, pressing the escape key before a key is equivalent to pressing the meta key at the same time as that key. Thus the key sequence \f3M-p\f1 can be typed in two ways, by pressing the escape key, followed by pressing \f3p\f1, or by pressing the Meta key at the same time as \f3p\f1. .sp Under UNIX the terminal driver sets a number of special keys for certain functions. The tecla library attempts to use the same keybindings to maintain consistency, binding them both in input mode and in command mode. The key sequences shown for the following 6 bindings are thus just examples of what they will probably be set to. If you have used the \f3stty\f1 command to change these keys, then the default bindings should match. .nf ^C -> user-interrupt ^\\ -> abort ^Z -> suspend ^Q -> start-output ^S -> stop-output ^V -> literal-next M-^C -> user-interrupt M-^\\ -> abort M-^Z -> suspend M-^Q -> start-output M-^S -> stop-output .fi Note that above, most of the bindings are defined twice, once as a raw control code like \f3^C\f1 and then a second time as a meta character like \f3M-^C\f1. The former is the binding for vi input mode, whereas the latter is the binding for vi command mode. Once in command mode all key-sequences that the user types that they don't explicitly start with an escape or a meta key, have their first key secretly converted to a meta character before the key sequence is looked up in the key binding table. Thus, once in command mode, when you type the letter \f3i\f1, for example, the tecla library actually looks up the binding for \f3M-i\f1. The cursor keys are refered to by name, as follows. This is necessary because different types of terminals generate different key sequences when their cursor keys are pressed. right -> cursor-right left -> cursor-left up -> up-history down -> down-history The cursor keys normally generate a keysequence that start with an escape character, so beware that using the arrow keys will put you into command mode (if you aren't already in command mode). .sp The following are the terminal-independent key bindings for vi input mode. .nf ^D -> list-or-eof ^G -> list-glob ^H -> backward-delete-char ^I -> complete-word \\r -> newline \\n -> newline ^L -> clear-screen ^N -> down-history ^P -> up-history ^R -> redisplay ^U -> backward-kill-line ^W -> backward-delete-word ^X* -> expand-filename ^X^F -> read-from-file ^X^R -> read-init-files ^? -> backward-delete-char .fi The following are the key bindings that are defined in vi command mode, this being specified by them all starting with a meta character. As mentioned above, once in command mode the initial meta character is optional. For example, you might enter command mode by typing Esc, and then press h twice to move the cursor two positions to the left. Both h characters get quietly converted to M-h before being compared to the key-binding table, the first one because Escape followed by a character is always converted to the equivalent meta character, and the second because command mode was already active. .nf M-\\ -> cursor-right (Meta-space) M-$ -> end-of-line M-* -> expand-filename M-+ -> down-history M-- -> up-history M-< -> beginning-of-history M-> -> end-of-history M-^ -> beginning-of-line M-; -> repeat-find-char M-, -> invert-refind-char M-| -> goto-column M-~ -> change-case M-. -> vi-repeat-change M-% -> find-parenthesis M-a -> vi-append M-A -> vi-append-at-eol M-b -> backward-word M-B -> backward-word M-C -> vi-change-rest-of-line M-cb -> vi-backward-change-word M-cB -> vi-backward-change-word M-cc -> vi-change-line M-ce -> vi-forward-change-word M-cE -> vi-forward-change-word M-cw -> vi-forward-change-word M-cW -> vi-forward-change-word M-cF -> vi-backward-change-find M-cf -> vi-forward-change-find M-cT -> vi-backward-change-to M-ct -> vi-forward-change-to M-c; -> vi-change-refind M-c, -> vi-change-invert-refind M-ch -> vi-backward-change-char M-c^H -> vi-backward-change-char M-c^? -> vi-backward-change-char M-cl -> vi-forward-change-char M-c\\ -> vi-forward-change-char (Meta-c-space) M-c^ -> vi-change-to-bol M-c0 -> vi-change-to-bol M-c$ -> vi-change-rest-of-line M-c| -> vi-change-to-column M-c% -> vi-change-to-parenthesis M-dh -> backward-delete-char M-d^H -> backward-delete-char M-d^? -> backward-delete-char M-dl -> forward-delete-char M-d -> forward-delete-char (Meta-d-space) M-dd -> delete-line M-db -> backward-delete-word M-dB -> backward-delete-word M-de -> forward-delete-word M-dE -> forward-delete-word M-dw -> forward-delete-word M-dW -> forward-delete-word M-dF -> backward-delete-find M-df -> forward-delete-find M-dT -> backward-delete-to M-dt -> forward-delete-to M-d; -> delete-refind M-d, -> delete-invert-refind M-d^ -> backward-kill-line M-d0 -> backward-kill-line M-d$ -> kill-line M-D -> kill-line M-d| -> delete-to-column M-d% -> delete-to-parenthesis M-e -> forward-word M-E -> forward-word M-f -> forward-find-char M-F -> backward-find-char M-- -> up-history M-h -> cursor-left M-H -> beginning-of-history M-i -> vi-insert M-I -> vi-insert-at-bol M-j -> down-history M-J -> history-search-forward M-k -> up-history M-K -> history-search-backward M-l -> cursor-right M-L -> end-of-history M-n -> history-re-search-forward M-N -> history-re-search-backward M-p -> append-yank M-P -> yank M-r -> vi-replace-char M-R -> vi-overwrite M-s -> vi-forward-change-char M-S -> vi-change-line M-t -> forward-to-char M-T -> backward-to-char M-u -> vi-undo M-w -> forward-to-word M-W -> forward-to-word M-x -> forward-delete-char M-X -> backward-delete-char M-yh -> backward-copy-char M-y^H -> backward-copy-char M-y^? -> backward-copy-char M-yl -> forward-copy-char M-y\\ -> forward-copy-char (Meta-y-space) M-ye -> forward-copy-word M-yE -> forward-copy-word M-yw -> forward-copy-word M-yW -> forward-copy-word M-yb -> backward-copy-word M-yB -> backward-copy-word M-yf -> forward-copy-find M-yF -> backward-copy-find M-yt -> forward-copy-to M-yT -> backward-copy-to M-y; -> copy-refind M-y, -> copy-invert-refind M-y^ -> copy-to-bol M-y0 -> copy-to-bol M-y$ -> copy-rest-of-line M-yy -> copy-line M-Y -> copy-line M-y| -> copy-to-column M-y% -> copy-to-parenthesis M-^E -> emacs-mode M-^H -> cursor-left M-^? -> cursor-left M-^L -> clear-screen M-^N -> down-history M-^P -> up-history M-^R -> redisplay M-^D -> list-or-eof M-^I -> complete-word M-\\r -> newline M-\\n -> newline M-^X^R -> read-init-files M-^Xh -> list-history M-0, M-1, ... M-9 -> digit-argument (see below) .fi Note that ^I is what the TAB key generates. .SH ENTERING REPEAT COUNTS Many of the key binding functions described previously, take an optional count, typed in before the target keysequence. This is interpreted as a repeat count by most bindings. A notable exception is the goto-column binding, which interprets the count as a column number. .sp By default you can specify this count argument by pressing the meta key while typing in the numeric count. This relies on the \f3digit-argument\f1 action being bound to Meta-0, Meta-1 etc. Once any one of these bindings has been activated, you can optionally take your finger off the meta key to type in the rest of the number, since every numeric digit thereafter is treated as part of the number, unless it is preceded by the \f3literal-next\f1 binding. As soon as a non-digit, or literal digit key is pressed the repeat count is terminated and either causes the just typed character to be added to the line that many times, or causes the next key-binding function to be given that argument. .sp For example, in emacs mode, typing: .sp .nf M-12a .fi .sp causes the letter 'a' to be added to the line 12 times, whereas .sp .nf M-4M-c .fi .sp Capitalizes the next 4 words. .sp In vi command mode the Meta modifier is automatically added to all characters typed in, so to enter a count in vi command-mode, just involves typing in the number, just as at it does in the vi editor itself. So for example, in vi command mode, typing: .sp .nf 4w2x .fi .sp moves the cursor four words to the right, then deletes two characters. .sp You can also bind \f3digit-argument\f1 to other key sequences. If these end in a numeric digit, that digit gets appended to the current repeat count. If it doesn't end in a numeric digit, a new repeat count is started with a value of zero, and can be completed by typing in the number, after letting go of the key which triggered the digit-argument action. .SH THE TECLA CONFIGURATION FILE By default, the first call to \f3gl_get_line()\f1 looks for a file called \f3\&.teclarc\f1 in your home directory (ie. \f3~/.teclarc\f1). If it finds this file, it reads it, interpreting each line as defining a new key binding or an editing configuration option. Since the emacs keybindings are installed by default, if you want to use the non-default vi editing mode, the most important item to go in this file is the following line: .nf edit-mode vi .fi This will re-configure the default bindings for vi-mode. The complete set of arguments that this command accepts are: .sp .nf vi - Install key-bindings like those of the vi editor. emacs - Install key-bindings like those of the emacs editor. This is the default. none - Use just the native line editing facilities provided by the terminal driver. .fi .sp To prevent the terminal bell from being rung, such as when an unrecognized control-sequence is typed, place the following line in the configuration file: .nf nobeep .fi An example of a key binding line in the configuration file is the following. .nf bind M-[2~ insert-mode .fi On many keyboards, the above key sequence is generated when one presses the \f3insert\f1 key, so with this keybinding, one can toggle between the emacs-mode insert and overwrite modes by hitting one key. One could also do it by typing out the above sequence of characters one by one. As explained above, the \f3M-\f1 part of this sequence can be typed either by pressing the escape key before the following key, or by pressing the Meta key at the same time as the following key. Thus if you had set the above key binding, and the insert key on your keyboard didn't generate the above key sequence, you could still type it in either of the following 2 ways. .nf 1. Hit the escape key momentarily, then press '[', then '2', then finally '~'. 2. Press the meta key at the same time as pressing the '[' key, then press '2', then '~'. .fi If you set a keybinding for a key-sequence that is already bound to a function, the new binding overrides the old one. If in the new binding you omit the name of the new function to bind to the key-sequence, the original binding becomes undefined. .sp Starting with versions of libtecla later than 1.3.3 it is now possible to bind keysequences that begin with a printable character. Previously key-sequences were required to start with a control or meta character. .sp Note that the special keywords "up", "down", "left" and "right" refer to the arrow keys, and are thus not treated as keysequences. So, for example, to rebind the up and down arrow keys to use the history search mechanism instead of the simple history recall method, you could place the following in your configuration file: .nf bind up history-search-backwards bind down history-search-backwards .fi .sp To unbind an existing binding, you can do this with the bind command by omitting to name any action to rebind the key sequence to. For example, by not specifying an action function, the following command unbinds the default beginning-of-line action from the ^A key sequence: .nf bind ^A .fi .SH ALTERNATE CONFIGURATION SOURCES As mentioned above, by default users have the option of configuring the behavior of \f3gl_get_line()\f1 via a configuration file called \f3\&.teclarc\f1 in their home directories. The fact that all applications share this same configuration file is both an advantage and a disadvantage. In most cases it is an advantage, since it encourages uniformity, and frees the user from having to configure each application separately. In some applications, however, this single means of configuration is a problem. This is particularly true of embedded software, where there's no filesystem to read a configuration file from, and also in applications where a radically different choice of keybindings is needed to emulate a legacy keyboard interface. To cater for such cases, the following function allows the application to control where configuration information is read from. .sp .nf int gl_configure_getline(GetLine *gl, const char *app_string, const char *app_file, const char *user_file); .fi .sp It allows the configuration commands that would normally be read from a user's \f3~/.teclarc\f1 file, to be read from any or none of, a string, an application specific configuration file, and/or a user-specific configuration file. If this function is called before the first call to \f3gl_get_line()\f1, the default behavior of reading \f3~/.teclarc\f1 on the first call to \f3gl_get_line()\f1 is disabled, so all configuration must be achieved using the configuration sources specified with this function. If \f3app_string != NULL\f1, then it is interpreted as a string containing one or more configuration commands, separated from each other in the string by embedded newline characters. If \f3app_file != NULL\f1 then it is interpreted as the full pathname of an application-specific configuration file. If \f3user_file != NULL\f1 then it is interpreted as the full pathname of a user-specific configuration file, such as \f3~/.teclarc\f1. For example, in the following call, gl_configure_getline(gl, "edit-mode vi \\n nobeep", "/usr/share/myapp/teclarc", "~/.teclarc"); the \f3app_string\f1 argument causes the calling application to start in vi edit-mode, instead of the default emacs mode, and turns off the use of the terminal bell by the library. It then attempts to read system-wide configuration commands from an optional file called \f3/usr/share/myapp/teclarc\f1, then finally reads user-specific configuration commands from an optional \f3\&.teclarc\f1 file in the user's home directory. Note that the arguments are listed in ascending order of priority, with the contents of \f3app_string\f1 being potentially overriden by commands in \f3app_file\f1, and commands in \f3app_file\f1 potentially being overriden by commands in \f3user_file\f1. .sp You can call this function as many times as needed, the results being cumulative, but note that copies of any filenames specified via the \f3app_file\f1 and \f3user_file\f1 arguments are recorded internally for subsequent use by the \f3read-init-files\f1 key-binding function, so if you plan to call this function multiple times, be sure that the last call specifies the filenames that you want re-read when the user requests that the configuration files be re-read. .SH FILENAME AND TILDE COMPLETION With the default key bindings, pressing the TAB key (aka. ^I) results in \f3gl_get_line()\f1 attempting to complete the incomplete filename that precedes the cursor. \f3gl_get_line()\f1 searches backwards from the cursor, looking for the start of the filename, stopping when it hits either a space or the start of the line. If more than one file has the specified prefix, \f3gl_get_line()\f1 completes the filename up to the point at which the ambiguous matches start to differ, then lists the possible matches. .sp In addition to literally written filenames, \f3gl_get_line()\f1 can complete files that start with \f3~/\f1 and \f3~user/\f1 expressions and that contain \f3$envvar\f1 expressions. In particular, if you hit TAB within an incomplete \f3~user\f1, expression, \f3gl_get_line()\f1 will attempt to complete the username, listing any ambiguous matches. .sp The completion binding is implemented using the \f3cpl_word_completions()\f1 function, which is also available separately to users of this library. See the \f3cpl_word_completions(3)\f1 man page for more details. .SH CUSTOMIZED WORD COMPLETION If in your application, you would like to have TAB completion complete other things in addition to or instead of filenames, you can arrange this by registering an alternate completion callback function, via a call to the \f3gl_customize_completion()\f1 function. .sp .nf int gl_customize_completion(GetLine *gl, void *data, CplMatchFn *match_fn); .fi .sp The \f3data\f1 argument provides a way for your application to pass arbitrary, application-specific information to the callback function. This is passed to the callback every time that it is called. It might for example, point to the symbol table from which possible completions are to be sought. The \f3match_fn\f1 argument specifies the callback function to be called. The \f3CplMatchFn\f1 function type is defined in \f3libtecla.h\f1, as is a \f3CPL_MATCH_FN()\f1 macro that you can use to declare and prototype callback functions. The declaration and responsibilities of callback functions are described in depth in the \f1cpl_complete_word(3)\f1 man page. .sp In brief, the callback function is responsible for looking backwards in the input line, back from the point at which the user pressed TAB, to find the start of the word being completed. It then must lookup possible completions of this word, and record them one by one in the \f3WordCompletion\f1 object that is passed to it as an argument, by calling the \f3cpl_add_completion()\f1 function. If the callback function wishes to provide filename completion in addition to its own specific completions, it has the option of itself calling the builtin file-name completion callback. This also, is documented in the \f3cpl_complete_word(3)\f1 man page. .sp Note that if you would like \f3gl_get_line()\f1 to return the current input line when a successful completion is been made, you can arrange this when you call \f3cpl_add_completion()\f1, by making the last character of the continuation suffix a newline character. If you do this, the input line will be updated to display the completion, together with any contiuation suffix up to the newline character, then \f3gl_get_line()\f1 will return this input line. .SH FILENAME EXPANSION With the default key bindings, pressing \f3^X*\f1 causes \f3gl_get_line()\f1 to expand the filename that precedes the cursor, replacing \f3~/\f1 and \f3~user/\f1 expressions with the corresponding home directories, and replacing \f3$envvar\f1 expressions with the value of the specified environment variable, then if there are any wildcards, replacing the so far expanded filename with a space-separated list of the files which match the wild cards. .sp The expansion binding is implemented using the \f3ef_expand_file()\f1 function. See the \f3ef_expand_file(3)\f1 man page for more details. .SH RECALLING PREVIOUSLY TYPED LINES Every time that a new line is entered by the user, it is appended to a list of historical input lines maintained within the GetLine resource object. You can traverse up and down this list using the up and down arrow keys. Alternatively, you can do the same with the \f3^P\f1, and \f3^N\f1 keys, and in vi command mode you can alternatively use the k and j characters. Thus pressing up-arrow once, replaces the current input line with the previously entered line. Pressing up-arrow again, replaces this with the line that was entered before it, etc.. Having gone back one or more lines into the history list, one can return to newer lines by pressing down-arrow one or more times. If you do this sufficient times, you will return to the original line that you were entering when you first hit up-arrow. .sp Note that in vi mode, all of the history recall functions switch the library into command mode. .sp In emacs mode the \f3M-p\f1 and \f3M-n\f1 keys work just like the \f3^P\f1 and \f3^N\f1 keys, except that they skip all but those historical lines which share the prefix that precedes the cursor. In vi command mode the upper case \f3K\f1 and \f3J\f1 characters do the same thing, except that the string that they search for includes the character under the cursor as well as what precedes it. .sp Thus for example, suppose that you were in emacs mode, and you had just entered the following list of commands in the order shown: .nf ls ~/tecla/ cd ~/tecla ls -l getline.c emacs ~/tecla/getline.c .fi If you next typed: .nf ls .fi and then hit \f3M-p\f1, then rather than returning the previously typed emacs line, which doesn't start with "ls", \f3gl_get_line()\f1 would recall the "ls -l getline.c" line. Pressing \f3M-p\f1 again would recall the "ls ~/tecla/" line. .SH HISTORY FILES To save the contents of the history buffer before quitting your application, and subsequently restore them when you next start the application, the following functions are provided. .sp .nf int gl_save_history(GetLine *gl, const char *filename, const char *comment, int max_lines); int gl_load_history(GetLine *gl, const char *filename, const char *comment); .fi .sp The \f3filename\f1 argument specifies the name to give the history file when saving, or the name of an existing history file, when loading. This may contain home-directory and environment variable expressions, such as "~/.myapp_history" or "$HOME/.myapp_history". .sp Along with each history line, extra information about it, such as when it was entered by the user, and what its nesting level is, is recorded as a comment preceding the line in the history file. Writing this as a comment allows the history file to double as a command file, just in case you wish to replay a whole session using it. Since comment prefixes differ in different languages, the \f3comment\f1 argument is provided for specifying the comment prefix. For example, if your application were a unix shell, such as the bourne shell, you would specify "#" here. Whatever you choose for the comment character, you must specify the same prefix to \f3gl_load_history()\f1 that you used when you called \f3gl_save_history()\f1 to write the history file. .sp The \f3max_lines\f1 must be either -1 to specify that all lines in the history list be saved, or a positive number specifying a ceiling on how many of the most recent lines should be saved. .sp Both fuctions return non-zero on error, after writing an error message to stderr. Note that \f3gl_load_history()\f1 does not consider the non-existence of a file to be an error. .SH MULTIPLE HISTORY LISTS If your application uses a single \f3GetLine\f1 object for entering many different types of input lines, you may wish \f3gl_get_line()\f1 to distinguish the different types of lines in the history list, and only recall lines that match the current type of line. To support this requirement, \f3gl_get_line()\f1 marks lines being recorded in the history list with an integer identifier chosen by the application. Initially this identifier is set to \f10\f3 by \f3new_GetLine()\f1, but it can be changed subsequently by calling \f3gl_group_history()\f1. .sp .nf int gl_group_history(GetLine *gl, unsigned id); .fi .sp The integer identifier \f3id\f1 can be any number chosen by the application, but note that \f3gl_save_history()\f1 and \f3gl_load_history()\f1 preserve the association between identifiers and historical input lines between program invokations, so you should choose fixed identifiers for the different types of input line used by your application. .sp Whenever \f3gl_get_line()\f1 appends a new input line to the history list, the current history identifier is recorded with it, and when it is asked to recall a historical input line, it only recalls lines that are marked with the current identifier. .SH DISPLAYING HISTORY The history list can be displayed by calling \f3gl_show_history()\f1. .sp .nf int gl_show_history(GetLine *gl, FILE *fp, const char *fmt, int all_groups, int max_lines); .fi .sp This displays the current contents of the history list to the stdio output stream \f3fp\f1. If the \f3max_lines\f1 argument is greater than or equal to zero, then no more than this number of the most recent lines will be displayed. If the \f3all_groups\f1 argument is non-zero, lines from all history groups are displayed. Otherwise just those of the currently selected history group are displayed. The format string argument, \f3fmt\f1, determines how the line is displayed. This can contain arbitrary characters which are written verbatim, interleaved with any of the following format directives: .nf %D - The date on which the line was originally entered, formatted like 2001-11-20. %T - The time of day when the line was entered, formatted like 23:59:59. %N - The sequential entry number of the line in the history buffer. %G - The number of the history group which the line belongs to. %% - A literal % character. %H - The history line itself. .fi Thus a format string like \f3"%D %T %H\n"\f1 would output something like: .nf 2001-11-20 10:23:34 Hello world .fi Note the inclusion of an explicit newline character in the format string. .SH LOOKING UP HISTORY The \f3gl_lookup_history()\f1 function allows the calling application to look up lines in the history list. .sp .nf typedef struct { const char *line; /* The requested historical */ /* line. */ unsigned group; /* The history group to which */ /* the line belongs. */ time_t timestamp; /* The date and time at which */ /* the line was originally */ /* entered. */ } GlHistoryLine; int gl_lookup_history(GetLine *gl, unsigned long id, GlHistoryLine *hline); .fi .sp The \f3id\f1 argument indicates which line to look up, where the first line that was entered in the history list after \f3new_GetLine()\f1 was called, is denoted by 0, and subsequently entered lines are denoted with successively higher numbers. Note that the range of lines currently preserved in the history list can be queried by calling the \f3gl_range_of_history()\f1 function, described later. If the requested line is in the history list, the details of the line are recorded in the variable pointed to by the \f3hline\f1 argument, and \f31\f1 is returned. Otherwise \f30\f1 is returned, and the variable pointed to by \f3hline\f1 is left unchanged. .sp Beware that the string returned in \f3hline->line\f1 is part of the history buffer, so it must not be modified by the caller, and will be recycled on the next call to any function that takes \f3gl\f1 as its argument. Therefore you should make a private copy of this string if you need to keep it around. .SH MISCELLANEOUS HISTORY CONFIGURATION If you wish to change the size of the history buffer that was originally specified in the call to \f3new_GetLine()\f1, you can do so with the \f3gl_resize_history()\f1 function. .sp .nf int gl_resize_history(GetLine *gl, size_t histlen); .fi .sp The \f3histlen\f1 argument specifies the new size in bytes, and if you specify this as 0, the buffer will be deleted. .sp As mentioned in the discussion of \f3new_GetLine()\f1, the number of lines that can be stored in the history buffer, depends on the lengths of the individual lines. For example, a 1000 byte buffer could equally store 10 lines of average length 100 bytes, or 2 lines of average length 50 bytes. Although the buffer is never expanded when new lines are added, a list of pointers into the buffer does get expanded when needed to accomodate the number of lines currently stored in the buffer. To place an upper limit on the number of lines in the buffer, and thus a ceiling on the amount of memory used in this list, you can call the \f3gl_limit_history()\f1 function. .sp .nf void gl_limit_history(GetLine *gl, int max_lines); .fi .sp The \f3max_lines\f1 should either be a positive number \f3>= 0\f1, specifying an upper limit on the number of lines in the buffer, or be \f3-1\f1 to cancel any previously specified limit. When a limit is in effect, only the \f3max_lines\f1 most recently appended lines are kept in the buffer. Older lines are discarded. .sp To discard lines from the history buffer, use the \f3gl_clear_history()\f1 function. .sp .nf void gl_clear_history(GetLine *gl, int all_groups); .fi .sp The \f3all_groups\f1 argument tells the function whether to delete just the lines associated with the current history group (see \f3gl_group_history()\f1), or all historical lines in the buffer. .sp The \f3gl_toggle_history()\f1 function allows you to toggle history on and off without losing the current contents of the history list. .sp .nf void gl_toggle_history(GetLine *gl, int enable); .fi .sp Setting the \f3enable\f1 argument to 0 turns off the history mechanism, and setting it to 1 turns it back on. When history is turned off, no new lines will be added to the history list, and history lookup key-bindings will act as though there is nothing in the history buffer. .SH QUERYING HISTORY INFORMATION The configured state of the history list can be queried with the \f3gl_history_state()\f1 function. .sp .nf typedef struct { int enabled; /* True if history is enabled */ unsigned group; /* The current history group */ int max_lines; /* The current upper limit on the */ /* number of lines in the history */ /* list, or -1 if unlimited. */ } GlHistoryState; void gl_state_of_history(GetLine *gl, GlHistoryState *state); .fi .sp On return, the status information is recorded in the variable pointed to by the \f3state\f1 argument. .sp The \f3gl_range_of_history()\f1 function returns the number and range of lines in the history list. .sp .nf typedef struct { unsigned long oldest; /* The sequential entry number */ /* of the oldest line in the */ /* history list. */ unsigned long newest; /* The sequential entry number */ /* of the newest line in the */ /* history list. */ int nlines; /* The number of lines in the */ /* history list. */ } GlHistoryRange; void gl_range_of_history(GetLine *gl, GlHistoryRange *range); .fi .sp The return values are recorded in the variable pointed to by the \f3range\f1 argument. If the \f3nlines\f1 member of this structure is greater than zero, then the \f3oldest\f1 and \f3newest\f1 members report the range of lines in the list, and \f3newest=oldest+nlines-1\f1. Otherwise they are both zero. .sp The \f3gl_size_of_history()\f1 function returns the total size of the history buffer and the amount of the buffer that is currently occupied. .sp .nf typedef struct { size_t size; /* The size of the history buffer */ /* (bytes). */ size_t used; /* The number of bytes of the */ /* history buffer that are */ /* currently occupied. */ } GlHistorySize; void gl_size_of_history(GetLine *gl, GlHistorySize *size); .fi .sp On return, the size information is recorded in the variable pointed to by the \f3size\f1 argument. .SH CHANGING TERMINALS The \f3new_GetLine()\f1 constructor function assumes that input is to be read from \f3stdin\f1, and output written to \f3stdout\f1. The following function allows you to switch to different input and output streams. .sp .nf int gl_change_terminal(GetLine *gl, FILE *input_fp, FILE *output_fp, const char *term); .fi .sp The \f3gl\f1 argument is the object that was returned by \f3new_GetLine()\f1. The \f3input_fp\f1 argument specifies the stream to read from, and \f3output_fp\f1 specifies the stream to be written to. Only if both of these refer to a terminal, will interactive terminal input be enabled. Otherwise \f3gl_get_line()\f1 will simply call \f3fgets()\f1 to read command input. If both streams refer to a terminal, then they must refer to the same terminal, and the type of this terminal must be specified via the \f3term\f1 argument. The value of the \f3term\f1 argument is looked up in the terminal information database (terminfo or termcap), in order to determine which special control sequences are needed to control various aspects of the terminal. \f3new_GetLine()\f1 for example, passes the return value of \f3getenv("TERM")\f1 in this argument. Note that if one or both of \f3input_fp\f1 and \f3output_fp\f1 don't refer to a terminal, then it is legal to pass \f3NULL\f1 instead of a terminal type. .sp Note that if you want to pass file descriptors to \f3gl_change_terminal()\f1, you can do this by creating stdio stream wrappers using the POSIX \f3fdopen()\f1 function. .SH EXTERNAL EVENT HANDLING While \f3gl_get_line()\f1 is waiting for keyboard input from the user, you can ask it to also watch for activity on arbitrary file descriptors, such as network sockets, pipes etc, and have it call functions of your choosing when activity is seen. This works on any system that has the \f3select()\f1 system call, which is most, if not all flavors of unix. Registering a file descriptor to be watched by \f3gl_get_line()\f1 involves calling the \f3gl_watch_fd()\f1 function. .sp .nf int gl_watch_fd(GetLine *gl, int fd, GlFdEvent event, GlFdEventFn *callback, void *data); .fi .sp If this returns non-zero, then it means that either your arguments are invalid, or that this facility isn't supported on the host system. .sp The \f3fd\f1 argument is the file descriptor to be watched. The \f3event\f1 argument specifies what type of activity is of interest, chosen from the following enumerated values: .sp .nf GLFD_READ - Watch for the arrival of data to be read. GLFD_WRITE - Watch for the ability to write to the file descriptor without blocking. GLFD_URGENT - Watch for the arrival of urgent out-of-band data on the file descriptor. .fi .sp The \f3callback\f1 argument is the function to call when the selected activity is seen. It should be defined with the following macro, which is defined in libtecla.h. .sp .nf #define GL_FD_EVENT_FN(fn) GlFdStatus (fn)(GetLine *gl, \\ void *data, int fd, \\ GlFdEvent event) .fi .sp The \f3data\f1 argument of the \f3gl_watch_fd()\f1 function is passed to the callback function for its own use, and can point to anything you like, including \f3NULL\f1. The file descriptor and the event argument are also passed to the callback function, and this potentially allows the same callback function to be registered to more than one type of event and/or more than one file descriptor. The return value of the callback function should be one of the following values. .sp .nf GLFD_ABORT - Tell gl_get_line() to abort with an error (errno won't be set, so set it appropriately yourself if you need it). GLFD_REFRESH - Redraw the input line then continue waiting for input. Return this if your callback wrote to the terminal. GLFD_CONTINUE - Continue to wait for input, without redrawing the line. .fi .sp Note that before calling the callback, \f3gl_get_line()\f1 blocks most signals, and leaves its own signal handlers installed, so if you need to catch a particular signal you will need to both temporarily install your own signal handler, and unblock the signal. Be sure to re-block the signal (if it was originally blocked) and reinstate the original signal handler, if any, before returning. .sp Your callback shouldn't try to read from the terminal, which is left in raw mode as far as input is concerned. You can however write to the terminal as usual, since features like conversion of newline to carriage-return/linefeed are re-enabled while the callback is running. If your callback function does write to the terminal, be sure to output a newline first, and when your callback returns, tell \f3gl_get_line()\f1 that the input line needs to be redrawn, by returning the \f3GLFD_REFRESH\f1 status code. .sp To remove a callback function that you previously registered for a given file descriptor and event, simply call \f3gl_watch_fd()\f1 with the same file descriptor and \f3event\f1 arguments, but with a \f3callback\f1 argument of \f30\f1. The \f3data\f1 argument is ignored in this case. .SH SIGNAL HANDLING DEFAULTS By default, the \f3gl_get_line()\f1 function intercepts a number of signals. This is particularly important for signals which would by default terminate the process, since the terminal needs to be restored to a usable state before this happens. In this section, the signals that are trapped by default, and how gl_get_line() responds to them, is described. Changing these defaults is the topic of the following section. .sp When the following subset of signals are caught, \f3gl_get_line()\f1 first restores the terminal settings and signal handling to how they were before \f3gl_get_line()\f1 was called, resends the signal, to allow the calling application's signal handlers to handle it, then if the process still exists, \f3gl_get_line()\f1 returns \f3NULL\f1 and sets \f3errno\f1 as specified below. .sp .nf SIGINT - This signal is generated both by the keyboard interrupt key (usually ^C), and the keyboard break key. errno=EINTR SIGHUP - This signal is generated when the controlling terminal exits. errno=ENOTTY SIGPIPE - This signal is generated when a program attempts to write to a pipe who's remote end isn't being read by any process. This can happen for example if you have called \f3gl_change_terminal()\f1 to redirect output to a pipe hidden under a pseudo terminal. errno=EPIPE SIGQUIT - This signal is generated by the keyboard quit key (usually ^\\). errno=EINTR SIGABRT - This signal is generated by the standard C, abort() function. By default it both terminates the process and generates a core dump. errno=EINTR SIGTERM - This is the default signal that the UN*X kill command sends to processes. errno=EINTR .fi .sp Note that in the case of all of the above signals, POSIX mandates that by default the process is terminated, with the addition of a core dump in the case of the \f3SIGQUIT\f1 signal. In other words, if the calling application doesn't override the default handler by supplying its own signal handler, receipt of the corresponding signal will terminate the application before \f3gl_get_line()\f1 returns. .sp If gl_get_line() aborts with errno set to EINTR, you can find out what signal caused it to abort, by calling the following function. .sp .nf int gl_last_signal(const GetLine *gl); .fi .sp This returns the numeric code (eg. \f3SIGINT\f1) of the last signal that was received during the most recent call to \f3gl_get_line()\f1, or \f3-1\f1 if no signals were received. .sp On systems that support it, when a SIGWINCH (window change) signal is received, \f3gl_get_line()\f1 queries the terminal to find out its new size, redraws the current input line to accomodate the new size, then returns to waiting for keyboard input from the user. Unlike other signals, this signal isn't resent to the application. .sp Finally, the following signals cause \f3gl_get_line()\f1 to first restore the terminal and signal environment to that which prevailed before \f3gl_get_line()\f1 was called, then resend the signal to the application. If the process still exists after the signal has been delivered, then \f3gl_get_line()\f1 then re-establishes its own signal handlers, switches the terminal back to raw mode, redisplays the input line, and goes back to awaiting terminal input from the user. .sp .nf SIGCONT - This signal is generated when a suspended process is resumed. SIGPWR - This signal is generated when a power failure occurs (presumably when the system is on a UPS). SIGALRM - This signal is generated when a timer expires. SIGUSR1 - An application specific signal. SIGUSR2 - Another application specific signal. SIGVTALRM - This signal is generated when a virtual timer expires (see man setitimer(2)). SIGXCPU - This signal is generated when a process exceeds its soft CPU time limit. SIGTSTP - This signal is generated by the terminal suspend key, which is usually ^Z, or the delayed terminal suspend key, which is usually ^Y. SIGTTIN - This signal is generated if the program attempts to read from the terminal while the program is running in the background. SIGTTOU - This signal is generated if the program attempts to write to the terminal while the program is running in the background. .fi .sp Obviously not all of the above signals are supported on all systems, so code to support them is conditionally compiled into the tecla library. .sp Note that if \f3SIGKILL\f1, which by definition can't be caught, or any of the hardware generated exception signals, such as \f3SIGSEGV\f1, \f3SIGBUS\f1 and \f3SIGFPE\f1, are received and unhandled while \f3gl_get_line()\f1 has the terminal in raw mode, the program will be terminated without the terminal having been restored to a usable state. In practice, job-control shells usually reset the terminal settings when a process relinquishes the controlling terminal, so this is only a problem with older shells. .SH CUSTOMIZED SIGNAL HANDLING The previous section listed the signals that \f3gl_get_line()\f1 traps by default, and described how it responds to them. This section describes how to both add and remove signals from the list of trapped signals, and how to specify how \f3gl_get_line()\f1 should respond to a given signal. .sp If you don't need \f3gl_get_line()\f1 to do anything in response to a signal that it normally traps, you can tell to \f3gl_get_line()\f1 to ignore that signal by calling \f3gl_ignore_signal()\f1. .sp .nf int gl_ignore_signal(GetLine *gl, int signo); .fi .sp The \f3signo\f1 argument is the number of the signal (eg. \f3SIGINT\f1) that you want to have ignored. If the specified signal isn't currently one of those being trapped, this function does nothing. .sp The \f3gl_trap_signal()\f1 function allows you to either add a new signal to the list that \f3gl_get_line()\f1 traps, or modify how it responds to a signal that it already traps. .sp .nf int gl_trap_signal(GetLine *gl, int signo, unsigned flags, GlAfterSignal after, int errno_value); .fi .sp The \f3signo\f1 argument is the number of the signal that you wish to have trapped. The \f3flags\f1 argument is a set of flags which determine the environment in which the application's signal handler is invoked, the \f3after\f1 argument tells \f3gl_get_line()\f1 what to do after the application's signal handler returns, and \f3errno_value\f1 tells \f3gl_get_line()\f1 what to set \f3errno\f1 to if told to abort. .sp The \f3flags\f1 argument is a bitwise OR of zero or more of the following enumerators: .sp .nf GLS_RESTORE_SIG - Restore the caller's signal environment while handling the signal. GLS_RESTORE_TTY - Restore the caller's terminal settings while handling the signal. GLS_RESTORE_LINE - Move the cursor to the start of the line following the input line before invoking the application's signal handler. GLS_REDRAW_LINE - Redraw the input line when the application's signal handler returns. GLS_UNBLOCK_SIG - Normally, if the calling program has a signal blocked (man sigprocmask), gl_get_line() does not trap that signal. This flag tells gl_get_line() to trap the signal and unblock it for the duration of the call to gl_get_line(). GLS_DONT_FORWARD - If this flag is included, the signal will not be forwarded to the signal handler of the calling program. .fi .sp Two commonly useful flag combinations are also enumerated as follows: .sp .nf GLS_RESTORE_ENV = GLS_RESTORE_SIG | GLS_RESTORE_TTY | GLS_REDRAW_LINE GLS_SUSPEND_INPUT = GLS_RESTORE_ENV | GLS_RESTORE_LINE .fi .sp If your signal handler, or the default system signal handler for this signal, if you haven't overriden it, never either writes to the terminal, nor suspends or terminates the calling program, then you can safely set the \f3flags\f1 argument to \f30\f1. .sp If your signal handler always writes to the terminal, reads from it, or suspends or terminates the program, you should specify the \f3flags\f1 argument as \f3GL_SUSPEND_INPUT\f1, so that: .sp .nf 1. The cursor doesn't get left in the middle of the input line. 2. So that the user can type in input and have it echoed. 3. So that you don't need to end each output line with \f3\\r\\n\f1, instead of just \f3\\n\f1. .fi .sp The \f3GL_RESTORE_ENV\f1 combination is the same as \f3GL_SUSPEND_INPUT\f1, except that it doesn't move the cursor, and if your signal handler doesn't read or write anything to the terminal, the user won't see any visible indication that a signal was caught. This can be useful if you have a signal handler that only occasionally writes to the terminal, where using \f3GL_SUSPEND_LINE\f1 would cause the input line to be unnecessarily duplicated when nothing had been written to the terminal. Such a signal handler, when it does write to the terminal, should be sure to start a new line at the start of its first write, by writing a '\\n' character, and should be sure to leave the cursor on a new line before returning. If the signal arrives while the user is entering a line that only occupies a signal terminal line, or if the cursor is on the last terminal line of a longer input line, this will have the same effect as \f3GL_SUSPEND_INPUT\f1. Otherwise it will start writing on a line that already contains part of the displayed input line. This doesn't do any harm, but it looks a bit ugly, which is why the \f3GL_SUSPEND_INPUT\f1 combination is better if you know that you are always going to be writting to the terminal. .sp The \f3after\f1 argument, which determines what \f3gl_get_line()\f1 does after the application's signal handler returns (if it returns), can take any one of the following values: .sp .nf GLS_RETURN - Return the completed input line, just as though the user had pressed the return key. GLS_ABORT - Cause gl_get_line() to return \f3NULL\f1. GLS_CONTINUE - Resume command line editing. .fi .sp The \f3errno_value\f1 argument is intended to be combined with the \f3GLS_ABORT\f1 option, telling \f3gl_get_line()\f1 what to set the standard \f3errno\f1 variable to before returning \f3NULL\f1 to the calling program. It can also, however, be used with the \f3GL_RETURN\f1 option, in case you wish to have a way to distinguish between an input line that was entered using the return key, and one that was entered by the receipt of a signal. .SH THE TERMINAL SIZE On most systems the combination of the \f3TIOCGWINSZ\f1 ioctl and the \f3SIGWINCH\f1 signal is used to maintain an accurate idea of the terminal size. The terminal size is newly queried every time that \f3gl_get_line()\f1 is called and whenever a \f3SIGWINCH\f1 signal is received. .sp On the few systems where this mechanism isn't available, at startup \f3new_GetLine()\f1 first looks for the \f3LINES\f1 and \f3COLUMNS\f1 environment variables. If these aren't found, or they contain unusable values, then if a terminal information database like terminfo or termcap is available, the default size of the terminal is looked up in this database. If this too fails to provide the terminal size, a default size of 80 columns by 24 lines is used. If this default isn't appropriate for your system, \f3gl_terminal_size()\f1 can be used to supply a different fallback. .sp The \f3gl_terminal_size()\f1 function allows you to query the current size of the terminal, and install an alternate fallback size for cases where the size isn't available. Beware that the terminal size won't be available if reading from a pipe or a file, so the default values can be important even on systems that do support ways of finding out the terminal size. .sp .nf typedef struct { int nline; /* The terminal has nline lines */ int ncolumn; /* The terminal has ncolumn columns */ } GlTerminalSize; GlTerminalSize gl_terminal_size(GetLine *gl, int def_ncolumn, int def_nline); .fi .sp This function first updates \f3gl_get_line()\f1's idea of the terminal size, then records its findings in the return value. .sp The \f3def_ncolumn\f1 and \f3def_nline\f1 specify the default number of terminal columns and lines to use if the terminal size can't be determined. .SH HIDING WHAT YOU TYPE When entering sensitive information, such as passwords, it is best not to have the text that you are entering echoed on the terminal. Furthermore, such text should not be recorded in the history list, since somebody finding your terminal unattended could then recall it, or somebody snooping through your directories could see it in your history file. With this in mind, the \f3gl_echo_mode()\f1 function allows you to toggle on and off the display and archival of any text that is subsequently entered in calls to \f3gl_get_line()\f1. .sp .nf int gl_echo_mode(GetLine *gl, int enable); .fi .sp The \f3enable\f1 argument specifies whether entered text should be visible or not. If it is \f30\f1, then subsequently entered lines will not be visible on the terminal, and will not be recorded in the history list. If it is \f31\f1, then subsequent input lines will be displayed as they are entered, and provided that history hasn't been turned off via a call to \f3gl_toggle_history()\f1, then they will also be archived in the history list. Finally, if the \f3enable\f1 argument is \f3-1\f1, then the echoing mode is left unchanged, which allows you to non-destructively query the current setting via the return value. In all cases, the return value of the function is \f30\f1 if echoing was disabled before the function was called, and \f31\f1 if it was enabled. .sp When echoing is turned off, note that although tab completion will invisibly complete your prefix as far as possible, ambiguous completions will not be displayed. .SH CALLBACK FUNCTION FACILITIES Unless otherwise stated, callback functions, such as tab completion callbacks and event callbacks should not call any functions in this module. The following functions, however, are designed specifically to be used by callback functions. .sp Calling the \f3gl_replace_prompt()\f1 function from a callback tells \f3gl_get_line() to display a different prompt when the callback returns. It has no effect if called when \f3gl_get_line()\f1 is not being called. .sp .nf void gl_replace_prompt(GetLine *gl, const char *prompt); .fi .sp .SH INTERNATIONAL CHARACTER SETS Since libtecla version 1.4.0, \f3gl_get_line()\f1 has been 8-bit clean. This means that all 8-bit characters that are printable in the user's current locale are now displayed verbatim and included in the returned input line. Assuming that the calling program correctly contains a call like the following, .sp .nf setlocale(LC_CTYPE, ""); .fi .sp then the current locale is determined by the first of the environment variables \f3LC_CTYPE\f1, \f3LC_ALL\f1, and \f3LANG\f1, that is found to contain a valid locale name. If none of these variables are defined, or the program neglects to call setlocale, then the default \f3C\f1 locale is used, which is US 7-bit ASCII. On most unix-like platforms, you can get a list of valid locales by typing the command: .sp .nf locale -a .fi .sp at the shell prompt. .sp .SS "Meta keys and locales" Beware that in most locales other than the default C locale, meta characters become printable, and they are then no longer considered to match \f3M-c\f1 style key bindings. This allows international characters to be entered with the compose key without unexpectedly triggering meta key bindings. You can still invoke meta bindings, since there are actually two ways to do this. For example the binding \f3M-c\f1 can also be invoked by pressing the escape key momentarily, then pressing the \f3c\f1 key, and this will work regardless of locale. Moreover, many modern terminal emulators, such as gnome's gnome-terminal's and KDE's konsole terminals, already generate escape pairs like this when you use the meta key, rather than a real meta character, and other emulators usually have a way to request this behavior, so you can continue to use the meta key on most systems. .sp For example, although xterm terminal emulators generate real 8-bit meta characters by default when you use the meta key, they can be configured to output the equivalent escape pair by setting their \f3EightBitInput\f1 X resource to \f3False\f1. You can either do this by placing a line like the following in your \f3~/.Xdefaults\f1 file, .sp .nf XTerm*EightBitInput: False .sp .fi or by starting an xterm with an \f3-xrm '*EightBitInput: False'\f1 command-line argument. In recent versions of xterm you can toggle this feature on and off with the \f3"Meta Sends Escape"\f1 option in the menu that is displayed when you press the left mouse button and the control key within an xterm window. In CDE, dtterms can be similarly coerced to generate escape pairs in place of meta characters, by setting the \f3Dtterm*KshMode\f1 resource to \f3True\f1. .sp .SS "Entering international characters" If you don't have a keyboard that generates all of the international characters that you need, there is usually a compose key that will allow you to enter special characters, or a way to create one. For example, under X windows on unix-like systems, if your keyboard doesn't have a compose key, you can designate a redundant key to serve this purpose with the xmodmap command. For example, on many PC keyboards there is a microsoft-windows key, which is otherwise useless under Linux. On my PC the \f3xev\f1 program reports that pressing this key generates keycode 115, so to turn this key into a compose key, I do the following: .sp .nf xmodmap -e 'keycode 115 = Multi_key' .fi .sp I can then enter an i with a umlaut over it by typing this key, followed by \f3"\f1, followed by i. .SH THREAD SAFETY In a multi-threaded program, you should use the libtecla_r.a version of the library. This uses reentrant versions of system functions, where available. Unfortunately neither terminfo nor termcap were designed to be reentrant, so you can't safely use the functions of the getline module in multiple threads (you can use the separate file-expansion and word-completion modules in multiple threads, see the corresponding man pages for details). However due to the use of POSIX reentrant functions for looking up home directories etc, it is safe to use this module from a single thread of a multi-threaded program, provided that your other threads don't use any termcap or terminfo functions. .SH FILES .nf libtecla.a - The tecla library libtecla.h - The tecla header file. ~/.teclarc - The personal tecla customization file. .fi .SH SEE ALSO libtecla(3), ef_expand_file(3), cpl_complete_word(3), pca_lookup_file(3) .SH AUTHOR Martin Shepherd (mcs@astro.caltech.edu) xorp/cli/libtecla/man3/gl_toggle_history.30000664000076400007640000000002711421137511020647 0ustar greearbgreearb.so man3/gl_get_line.3 xorp/cli/libtecla/man3/gl_range_of_history.30000664000076400007640000000002711421137511021146 0ustar greearbgreearb.so man3/gl_get_line.3 xorp/cli/libtecla/man3/libtecla.30000664000076400007640000001416111421137511016706 0ustar greearbgreearb.\" Copyright (C) 2000, 2001 by Martin C. Shepherd .\" .\" All rights reserved. .\" .\" 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, including .\" without limitation the rights to use, copy, modify, merge, publish, .\" distribute, and/or sell copies of the Software, and to permit persons .\" to whom the Software is furnished to do so, provided that the above .\" copyright notice(s) and this permission notice appear in all copies of .\" the Software and that both the above copyright notice(s) and this .\" permission notice appear in supporting documentation. .\" .\" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS .\" OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF .\" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT .\" OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR .\" HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL .\" INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING .\" FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, .\" NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION .\" WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .\" Except as contained in this notice, the name of a copyright holder .\" shall not be used in advertising or otherwise to promote the sale, use .\" or other dealings in this Software without prior written authorization .\" of the copyright holder. .TH libtecla 3 .SH NAME libtecla - An interactive command-line input library. .SH SYNOPSIS .nf gcc ... -ltecla -lcurses .fi .SH DESCRIPTION The \f3tecla\f1 library provides programs with interactive command line editing facilities, similar to those of the unix \f3tcsh\f1 shell. In addition to simple command-line editing, it supports recall of previously entered command lines, TAB completion of file names or other tokens, and in-line wild-card expansion of filenames. The internal functions which perform file-name completion and wild-card expansion are also available externally for optional use by the calling program. .sp The various parts of the library are documented in the following man pages: .nf gl_get_line(3) - The interactive line-input module. cpl_complete_word(3) - The word completion module. ef_expand_file(3) - The filename expansion module. pca_lookup_file(3) - A directory-list based filename lookup and completion module. .fi In addition there is one optional application distributed with the library: .nf enhance(3) - Add command-line editing to third party applications. .fi .SH THREAD SAFETY If the library is compiled with -D_POSIX_C_SOURCE=199506L, reentrant versions of as many functions as possible are used. This includes using getpwuid_r() and getpwnam_r() instead of getpwuid() and getpwnam() when looking up the home directories of specific users in the password file (for ~user/ expansion), and readdir_r() instead of readdir() for reading directory entries when doing filename completion. The reentrant version of the library is usually called libtecla_r.a instead of libtecla.a, so if only the latter is available, it probably isn't the correct version to link with threaded programs. Reentrant functions for iterating through the password file aren't available, so when the library is compiled to be reentrant, TAB completion of incomplete usernames in \f3~username/\f1 expressions is disabled. This doesn't disable expansion of complete \f3~username\f1 expressions, which can be done reentrantly, or expansion of the parts of filenames that follow them, so this doesn't remove much functionality. The terminfo functions setupterm(), tigetstr(), tigetnum() and tputs() also aren't reentrant, but very few programs will want to interact with multiple terminals, so this shouldn't prevent this library from being used in threaded programs. .SH LIBRARY VERSION NUMBER The version number of the library can be queried using the following function. .sp .nf void libtecla_version(int *major, int *minor, int *micro); .fi .sp On return, this function records the three components of the libtecla version number in \f3*major\f1, \f3*minor\f1, \f3*micro\f1. The formal meaning of the three components is as follows. .sp .nf major - Incrementing this number implies that a change has been made to the library's public interface, which makes it binary incompatible with programs that were linked with previous shared versions of the tecla library. minor - This number is incremented by one whenever additional functionality, such as new functions or modules, are added to the library. micro - This is incremented whenever modifications to the library are made which make no changes to the public interface, but which fix bugs and/or improve the behind-the-scenes implementation. .fi .sp .SH TRIVIA In Spanish, a "tecla" is the key of a keyboard. Since this library centers on keyboard input, and given that I wrote much of the library while working in Chile, this seemed like a suitable name. .SH FILES .nf libtecla.a - The tecla library. libtecla.h - The tecla header file. ~/.teclarc - The tecla personal customization file. .fi .SH SEE ALSO gl_get_line(3), ef_expand_file(3), cpl_complete_word(3), pca_lookup_file(3), enhance(3) .SH AUTHOR Martin Shepherd (mcs@astro.caltech.edu) .SH ACKNOWLEDGMENTS .nf Markus Gyger - Lots of assistance, including help with shared libraries, configuration information, particularly for Solaris; modifications to support C++ compilers, improvements for ksh users, faster cursor motion, output buffering, and changes to make gl_get_line() 8-bit clean. Mike MacFaden - Suggestions, feedback and testing that led to many of the major new functions that were added in version 1.4.0. Tim Eliseo - Many vi-mode bindings and fixes. .fi xorp/cli/libtecla/man3/gl_show_history.30000664000076400007640000000002711421137511020346 0ustar greearbgreearb.so man3/gl_get_line.3 xorp/cli/libtecla/man3/del_WordCompletion.30000664000076400007640000000003511421137511020713 0ustar greearbgreearb.so man3/cpl_complete_word.3 xorp/cli/libtecla/man3/gl_save_history.30000664000076400007640000000002711421137511020324 0ustar greearbgreearb.so man3/gl_get_line.3 xorp/cli/libtecla/man3/ef_expand_file.30000664000076400007640000002361711421137511020065 0ustar greearbgreearb.\" Copyright (C) 2000, 2001 by Martin C. Shepherd .\" .\" All rights reserved. .\" .\" 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, including .\" without limitation the rights to use, copy, modify, merge, publish, .\" distribute, and/or sell copies of the Software, and to permit persons .\" to whom the Software is furnished to do so, provided that the above .\" copyright notice(s) and this permission notice appear in all copies of .\" the Software and that both the above copyright notice(s) and this .\" permission notice appear in supporting documentation. .\" .\" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS .\" OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF .\" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT .\" OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR .\" HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL .\" INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING .\" FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, .\" NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION .\" WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .\" Except as contained in this notice, the name of a copyright holder .\" shall not be used in advertising or otherwise to promote the sale, use .\" or other dealings in this Software without prior written authorization .\" of the copyright holder. .TH ef_expand_file 3 .SH NAME ef_expand_file, del_ExpandFile, ef_last_error, ef_list_expansions, new_ExpandFile \- expand filenames containing ~user/$envvar and wildcard expressions .SH SYNOPSIS .nf #include ExpandFile *new_ExpandFile(void); ExpandFile *del_ExpandFile(ExpandFile *ef); FileExpansion *ef_expand_file(ExpandFile *ef, const char *path, int pathlen); int ef_list_expansions(FileExpansion *result, FILE *fp, int term_width); const char *ef_last_error(ExpandFile *ef); .fi .SH DESCRIPTION The \f3ef_expand_file()\f1 function is part of the tecla library (see the libtecla(3) man page). It expands a specified filename, converting \f3~user/\f1 and \f3~/\f1 expressions at the start of the filename to the corresponding home directories, replacing \f3$envvar\f1 with the value of the corresponding environment variable, and then, if there are any wildcards, matching these against existing filenames. Backslashes in the input filename are interpreted as escaping any special meanings of the characters that follow them. Only backslahes that are themselves preceded by backslashes are preserved in the expanded filename. .sp In the presence of wildcards, the returned list of filenames only includes the names of existing files which match the wildcards. Otherwise, the original filename is returned after expansion of tilde and dollar expressions, and the result is not checked against existing files. This mimics the file-globbing behavior of the unix \f3tcsh\f1 shell. .sp The supported wildcards and their meanings are: .nf * - Match any sequence of zero or more characters. ? - Match any single character. [chars] - Match any single character that appears in 'chars'. If 'chars' contains an expression of the form a-b, then any character between a and b, including a and b, matches. The '-' character looses its special meaning as a range specifier when it appears at the start of the sequence of characters. The ']' character also looses its significance as the terminator of the range expression if it appears immediately after the opening '[', at which point it is treated one of the characters of the range. If you want both '-' and ']' to be part of the range, the '-' should come first and the ']' second. [^chars] - The same as [chars] except that it matches any single character that doesn't appear in 'chars'. .fi Note that wildcards never match the initial dot in filenames that start with '.'. The initial '.' must be explicitly specified in the filename. This again mimics the globbing behavior of most unix shells, and its rational is based in the fact that in unix, files with names that start with '.' are usually hidden configuration files, which are not listed by default by the ls command. .sp The following is a complete example of how to use the file expansion function. .nf #include #include int main(int argc, char *argv[]) { ExpandFile *ef; /* The expansion resource object */ char *filename; /* The filename being expanded */ FileExpansion *expn; /* The results of the expansion */ int i; ef = new_ExpandFile(); if(!ef) return 1; for(arg = *(argv++); arg; arg = *(argv++)) { if((expn = ef_expand_file(ef, arg, -1)) == NULL) { fprintf(stderr, "Error expanding %s (%s).\\n", arg, ef_last_error(ef)); } else { printf("%s matches the following files:\\n", arg); for(i=0; infile; i++) printf(" %s\\n", expn->files[i]); } } ef = del_ExpandFile(ef); return 0; } .fi .sp Descriptions of the functions used above are as follows: .sp .nf ExpandFile *new_ExpandFile(void) .fi .sp This function creates the resources used by the \f3ef_expand_file()\f1 function. In particular, it maintains the memory that is used to record the array of matching filenames that is returned by \f3ef_expand_file()\f1. This array is expanded as needed, so there is no built in limit to the number of files that can be matched. .sp .nf ExpandFile *del_ExpandFile(ExpandFile *ef) .fi .sp This function deletes the resources that were returned by a previous call to \f3new_ExpandFile()\f1. It always returns \f3NULL\f1 (ie a deleted object). It does nothing if the \f3ef\f1 argument is \f3NULL\f1. .sp A container of the following type is returned by \f3ef_expand_file()\f1. .sp .nf typedef struct { int exists; /* True if the files in files[] exist */ int nfile; /* The number of files in files[] */ char **files; /* An array of 'nfile' filenames. */ } FileExpansion; .fi .sp .nf FileExpansion *ef_expand_file(ExpandFile *ef, const char *path, int pathlen) .fi .sp The \f3ef_expand_file()\f1 function performs filename expansion, as documented at the start of this section. Its first argument is a resource object returned by \f3new_ExpandFile()\f1. A pointer to the start of the filename to be matched is passed via the \f3path\f1 argument. This must be a normal \f3NUL\f1 terminated string, but unless a length of -1 is passed in \f3pathlen\f1, only the first \f3pathlen\f1 characters will be used in the filename expansion. If the length is specified as -1, the whole of the string will be expanded. .sp The function returns a pointer to a container who's contents are the results of the expansion. If there were no wildcards in the filename, the \f3nfile\f1 member will be 1, and the \f3exists\f1 member should be queried if it is important to know if the expanded file currently exists or not. If there were wildcards, then the contained \f3files[]\f1 array will contain the names of the \f3nfile\f1 existing files that matched the wildcarded filename, and the \f3exists\f1 member will have the value 1. Note that the returned container belongs to the specified \f3ef\f1 object, and its contents will change on each call, so if you need to retain the results of more than one call to \f3ef_expand_file()\f1, you should either make a private copy of the returned results, or create multiple file-expansion resource objects via multiple calls to \f3new_ExpandFile()\f1. .sp On error, \f3NULL\f1 is returned, and an explanation of the error can be determined by calling \f3ef_last_error(ef)\f1. .sp .nf const char *ef_last_error(ExpandFile *ef) .fi .sp This function returns the message which describes the error that occurred on the last call to \f3ef_expand_file()\f1, for the given \f3(ExpandFile *ef)\f1 resource object. .sp .nf int ef_list_expansions(FileExpansion *result, FILE *fp, int terminal_width); .fi .sp The \f3ef_list_expansions()\f1 function provides a convenient way to list the filename expansions returned by \f3ef_expand_file()\f1. Like the unix \f3ls\f1 command, it arranges the filenames into equal width columns, each column having the width of the largest file. The number of columns used is thus determined by the length of the longest filename, and the specified terminal width. Beware that filenames that are longer than the specified terminal width are printed without being truncated, so output longer than the specified terminal width can occur. The list is written to the stdio stream specified by the \f3fp\f1 argument. .SH THREAD SAFETY In multi-threaded programs, you should use the \f3libtecla_r.a\f1 version of the library. This uses POSIX reentrant functions where available (hence the \f3_r\f1 suffix), and disables features that rely on non-reentrant system functions. Currently there are no features disabled in this module. Using the \f3libtecla_r.a\f1 version of the library, it is safe to use the facilities of this module in multiple threads, provided that each thread uses a separately allocated \f3ExpandFile\f1 object. In other words, if two threads want to do file expansion, they should each call \f3new_ExpandFile()\f1 to allocate their own file-expansion objects. .SH FILES .nf libtecla.a - The tecla library libtecla.h - The tecla header file. .fi .SH SEE ALSO libtecla(3), gl_get_line(3), cpl_complete_word(3), pca_lookup_file(3) .SH AUTHOR Martin Shepherd (mcs@astro.caltech.edu) xorp/cli/libtecla/man3/new_GetLine.30000664000076400007640000000002711421137511017323 0ustar greearbgreearb.so man3/gl_get_line.3 xorp/cli/libtecla/man3/libtecla_version.30000664000076400007640000000002411421137511020444 0ustar greearbgreearb.so man3/libtecla.3 xorp/cli/libtecla/man3/gl_customize_completion.30000664000076400007640000000002711421137511022060 0ustar greearbgreearb.so man3/gl_get_line.3 xorp/cli/libtecla/man3/gl_clear_history.30000664000076400007640000000002711421137511020454 0ustar greearbgreearb.so man3/gl_get_line.3 xorp/cli/libtecla/man3/gl_prompt_style.30000664000076400007640000000002711421137511020346 0ustar greearbgreearb.so man3/gl_get_line.3 xorp/cli/libtecla/man3/pca_lookup_file.30000664000076400007640000003340711421137511020266 0ustar greearbgreearb.\" Copyright (C) 2001 by Martin C. Shepherd .\" .\" All rights reserved. .\" .\" 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, including .\" without limitation the rights to use, copy, modify, merge, publish, .\" distribute, and/or sell copies of the Software, and to permit persons .\" to whom the Software is furnished to do so, provided that the above .\" copyright notice(s) and this permission notice appear in all copies of .\" the Software and that both the above copyright notice(s) and this .\" permission notice appear in supporting documentation. .\" .\" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS .\" OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF .\" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT .\" OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR .\" HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL .\" INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING .\" FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, .\" NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION .\" WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .\" Except as contained in this notice, the name of a copyright holder .\" shall not be used in advertising or otherwise to promote the sale, use .\" or other dealings in this Software without prior written authorization .\" of the copyright holder. .TH pca_lookup_file 3 .SH NAME pca_lookup_file, del_PathCache, del_PcaPathConf, new_PathCache, new_PcaPathConf, pca_last_error, pca_path_completions, pca_scan_path, pca_set_check_fn, ppc_file_start, ppc_literal_escapes \- lookup a file in a list of directories .SH SYNOPSIS .nf #include PathCache *new_PathCache(void); PathCache *del_PathCache(PathCache *pc); int pca_scan_path(PathCache *pc, const char *path); void pca_set_check_fn(PathCache *pc, CplCheckFn *check_fn, void *data); char *pca_lookup_file(PathCache *pc, const char *name, int name_len, int literal); const char *pca_last_error(PathCache *pc); CPL_MATCH_FN(pca_path_completions); .fi .SH DESCRIPTION The \f3PathCache\f1 object is part of the tecla library (see the libtecla(3) man page). .sp \f3PathCache\f1 objects allow an application to search for files in any colon separated list of directories, such as the unix execution PATH environment variable. Files in absolute directories are cached in a \f3PathCache\f1 object, whereas relative directories are scanned as needed. Using a \f3PathCache\f1 object, you can look up the full pathname of a simple filename, or you can obtain a list of the possible completions of a given filename prefix. By default all files in the list of directories are targets for lookup and completion, but a versatile mechanism is provided for only selecting specific types of files. The obvious application of this facility is to provide Tab-completion and lookup of executable commands in the unix PATH, so an optional callback which rejects all but executable files, is provided. .sp .SH AN EXAMPLE Under UNIX, the following example program looks up and displays the full pathnames of each of the command names on the command line. .sp .nf #include #include #include int main(int argc, char *argv[]) { int i; /* * Create a cache for executable files. */ PathCache *pc = new_PathCache(); if(!pc) exit(1); /* * Scan the user's PATH for executables. */ if(pca_scan_path(pc, getenv("PATH"))) { fprintf(stderr, "%s\\n", pca_last_error(pc)); exit(1); } /* * Arrange to only report executable files. */ pca_set_check_fn(pc, cpl_check_exe, NULL); /* * Lookup and display the full pathname of each of the * commands listed on the command line. */ for(i=1; i #include WordCompletion *new_WordCompletion(void); WordCompletion *del_WordCompletion(WordCompletion *cpl); #define CPL_MATCH_FN(fn) int (fn)(WordCompletion *cpl, \\ void *data, \\ const char *line, \\ int word_end) typedef CPL_MATCH_FN(CplMatchFn); CPL_MATCH_FN(cpl_file_completions); CplMatches *cpl_complete_word(WordCompletion *cpl, const char *line, int word_end, void *data, CplMatchFn *match_fn); int cpl_list_completions(CplMatches *result, FILE *fp, int term_width); int cpl_add_completion(WordCompletion *cpl, const char *line, int word_start, int word_end, const char *suffix, const char *type_suffix, const char *cont_suffix); void cpl_record_error(WordCompletion *cpl, const char *errmsg); const char *cpl_last_error(WordCompletion *cpl); .fi .SH DESCRIPTION The \f3cpl_complete_word()\f1 function is part of the tecla library (see the libtecla(3) man page). It is usually called behind the scenes by \f3gl_get_line(3)\f1, but can also be called separately. Given an input line containing an incomplete word to be completed, it calls a user-provided callback function (or the provided file-completion callback function) to look up all possible completion suffixes for that word. The callback function is expected to look backward in the line, starting from the specified cursor position, to find the start of the word to be completed, then to look up all possible completions of that word and record them, one at a time by calling \f3cpl_add_completion()\f1. .sp Descriptions of the functions of this module are as follows: .sp .nf CompleteWord *new_CompleteWord(void) .fi .sp This function creates the resources used by the \f3cpl_complete_word()\f1 function. In particular, it maintains the memory that is used to return the results of calling \f3cpl_complete_word()\f1. .sp .nf CompleteWord *del_CompleteWord(CompleteWord *cpl) .fi .sp This function deletes the resources that were returned by a previous call to \f3new_CompleteWord()\f1. It always returns \f3NULL\f1 (ie. a deleted object). It does nothing if the \f3cpl\f1 argument is \f3NULL\f1. .sp The callback functions which lookup possible completions should be defined with the following macro (which is defined in libtecla.h). .sp .nf #define CPL_MATCH_FN(fn) int (fn)(WordCompletion *cpl, \\ void *data, \\ const char *line, \\ int word_end) .fi .sp Functions of this type are called by \f3cpl_complete_word()\f1, and all of the arguments of the callback are those that were passed to said function. In particular, the \f3line\f1 argument contains the input line containing the word to be completed, and \f3word_end\f1 is the index of the character that follows the last character of the incomplete word within this string. The callback is expected to look backwards from \f3word_end\f1 for the start of the incomplete word. What constitutes the start of a word clearly depends on the application, so it makes sense for the callback to take on this responsibility. For example, the builtin filename completion function looks backwards until it hits an unescaped space, or the start of the line. Having found the start of the word, the callback should then lookup all possible completions of this word, and record each completion via separate calls to \f3cpl_add_completion()\f1. If the callback needs access to an application-specific symbol table, it can pass it and any other data that it needs, via the \f3data\f1 argument. This removes any need for globals. .sp The callback function should return 0 if no errors occur. On failure it should return 1, and register a terse description of the error by calling \f3cpl_record_error()\f1. .sp .nf void cpl_record_error(WordCompletion *cpl, const char *errmsg); .fi .sp The last error message recorded by calling \f3cpl_record_error()\f1, can subsequently be queried by calling \f3cpl_last_error()\f1, as described later. .sp .nf int cpl_add_completion(WordCompletion *cpl, const char *line, int word_start, int word_end, const char *suffix, const char *type_suffix, const char *cont_suffix); .fi .sp The \f3cpl_add_completion()\f1 function is called zero or more times by the completion callback function to record each possible completion in the specified \f3WordCompletion\f1 object. These completions are subsequently returned by \f3cpl_complete_word()\f1, as described later. The \f3cpl\f1, \f3line\f1, and \f3word_end\f1 arguments should be those that were passed to the callback function. The \f3word_start\f1 argument should be the index within the input line string of the start of the word that is being completed. This should equal \f3word_end\f1 if a zero-length string is being completed. The \f3suffix\f1 argument is the string that would have to be appended to the incomplete word to complete it. If this needs any quoting (eg. the addition of backslashes before special charaters) to be valid within the displayed input line, this should be included. A copy of the suffix string is allocated internally, so there is no need to maintain your copy of the string after \f3cpl_add_completion()\f1 returns. .sp Note that in the array of possible completions which the \f3cpl_complete_word()\f1 function returns, the suffix recorded by \f3cpl_add_completion()\f1 is listed along with the concatentation of this suffix with the word that lies between \f3word_start\f1 and \f3word_end\f1 in the input line. .sp The \f3type_suffix\f1 argument specifies an optional string to be appended to the completion if it is displayed as part of a list of completions by \f3cpl_list_completions()\f1. The intention is that this indicate to the user the type of each completion. For example, the file completion function places a directory separator after completions that are directories, to indicate their nature to the user. Similary, if the completion were a function, you could indicate this to the user by setting \f3type_suffix\f1 to "()". Note that the \f3type_suffix\f1 string isn't copied, so if the argument isn't a literal string between speech marks, be sure that the string remains valid for at least as long as the results of \f3cpl_complete_word()\f1 are needed. .sp The \f3cont_suffix\f1 is a continuation suffix to append to the completed word in the input line if this is the only completion. This is something that isn't part of the completion itself, but that gives the user an indication about how they might continue to extend the token. For example, the file-completion callback function adds a directory separator if the completed word is a directory. If the completed word were a function name, you could similarly aid the user by arranging for an open parenthesis to be appended. .sp .nf CplMatches *cpl_complete_word(WordCompletion *cpl, const char *line, int word_end, void *data, CplMatchFn *match_fn); .fi .sp The \f3cpl_complete_word()\f1 is normally called behind the scenes by \f3gl_get_line(3)\f1, but can also be called separately if you separately allocate a \f3WordCompletion\f1 object. It performs word completion, as described at the beginning of this section. Its first argument is a resource object previously returned by \f3new_CompleteWord()\f1. The \f3line\f1 argument is the input line string, containing the word to be completed. The \f3word_end\f1 argument contains the index of the character in the input line, that just follows the last character of the word to be completed. When called by \f3gl_get_line()\f1, this is the character over which the user pressed \f3TAB\f1. The \f3match_fn\f3 argument is the function pointer of the callback function which will lookup possible completions of the word, as described above, and the \f3data\f1 argument provides a way for the application to pass arbitrary data to the callback function. .sp If no errors occur, the \f3cpl_complete_word()\f1 function returns a pointer to a \f3CplMatches\f1 container, as defined below. This container is allocated as part of the \f3cpl\f1 object that was passed to \f3cpl_complete_word()\f1, and will thus change on each call which uses the same \f3cpl\f1 argument. .sp .nf typedef struct { char *completion; /* A matching completion */ /* string */ char *suffix; /* The part of the */ /* completion string which */ /* would have to be */ /* appended to complete the */ /* original word. */ const char *type_suffix; /* A suffix to be added when */ /* listing completions, to */ /* indicate the type of the */ /* completion. */ } CplMatch; typedef struct { char *suffix; /* The common initial part */ /* of all of the completion */ /* suffixes. */ const char *cont_suffix; /* Optional continuation */ /* string to be appended to */ /* the sole completion when */ /* nmatch==1. */ CplMatch *matches; /* The array of possible */ /* completion strings, */ /* sorted into lexical */ /* order. */ int nmatch; /* The number of elements in */ /* the above matches[] */ /* array. */ } CplMatches; .fi .sp If an error occurs during completion, \f3cpl_complete_word()\f1 returns NULL. A description of the error can be acquired by calling the \f3cpl_last_error()\f3 function. .sp .nf const char *cpl_last_error(WordCompletion *cpl); .fi .sp The \f3cpl_last_error()\f3 function returns a terse description of the error which occurred on the last call to \f3cpl_complete_word()\f1 or \f3cpl_add_completion()\f1. .sp .nf int cpl_list_completions(CplMatches *result, FILE *fp, int terminal_width); .fi .sp When the \f3cpl_complete_word()\f1 function returns multiple possible completions, the \f3cpl_list_completions()\f1 function can be called upon to list them, suitably arranged across the available width of the terminal. It arranges for the displayed columns of completions to all have the same width, set by the longest completion. It also appends the \f3type_suffix\f1 strings that were recorded with each completion, thus indicating their types to the user. .SH THE BUILT-IN FILENAME-COMPLETION CALLBACK By default the \f3gl_get_line(3)\f1 function, passes the following completion callback function to \f3cpl_complete_word()\f1. This function can also be used separately, either by sending it to \f3cpl_complete_word()\f1, or by calling it directly from your own completion callback function. .sp .nf CPL_MATCH_FN(cpl_file_completions); .fi .sp Certain aspects of the behavior of this callback can be changed via its \f3data\f1 argument. If you are happy with its default behavior you can pass \f3NULL\f1 in this argument. Otherwise it should be a pointer to a \f3CplFileConf\f1 object, previously allocated by calling \f3new_CplFileConf()\f1. .sp .nf CplFileConf *new_CplFileConf(void); .fi .sp \f3CplFileConf\f1 objects encapsulate the configuration parameters of \f3cpl_file_completions()\f1. These parameters, which start out with default values, can be changed by calling the accessor functions described below. .sp By default, the \f3cpl_file_completions()\f3 callback function searches backwards for the start of the filename being completed, looking for the first un-escaped space or the start of the input line. If you wish to specify a different location, call \f3cfc_file_start()\f1 with the index at which the filename starts in the input line. Passing start_index=-1 re-enables the default behavior. .sp .nf void cfc_file_start(CplFileConf *cfc, int start_index); .fi .sp By default, when \f3cpl_file_completions()\f1 looks at a filename in the input line, each lone backslash in the input line is interpreted as being a special character which removes any special significance of the character which follows it, such as a space which should be taken as part of the filename rather than delimiting the start of the filename. These backslashes are thus ignored while looking for completions, and subsequently added before spaces, tabs and literal backslashes in the list of completions. To have unescaped backslashes treated as normal characters, call \f3cfc_literal_escapes()\f1 with a non-zero value in its \f3literal\f1 argument. .sp .nf void cfc_literal_escapes(CplFileConf *cfc, int literal); .fi .sp By default, \f3cpl_file_completions()\f1 reports all files who's names start with the prefix that is being completed. If you only want a selected subset of these files to be reported in the list of completions, you can arrange this by providing a callback function which takes the full pathname of a file, and returns \f30\f1 if the file should be ignored, or \f31\f1 if the file should be included in the list of completions. To register such a function for use by \f3cpl_file_completions()\f1, call \f3cfc_set_check_fn()\f1, and pass it a pointer to the function, together with a pointer to any data that you would like passed to this callback whenever it is called. Your callback can make its decisions based on any property of the file, such as the filename itself, whether the file is readable, writable or executable, or even based on what the file contains. .sp .nf #define CPL_CHECK_FN(fn) int (fn)(void *data, \\ const char *pathname) typedef CPL_CHECK_FN(CplCheckFn); void cfc_set_check_fn(CplFileConf *cfc, CplCheckFn *chk_fn, void *chk_data); .fi .sp The \f3cpl_check_exe()\f1 function is a provided callback of the above type, for use with \f3cpl_file_completions()\f1. It returns non-zero if the filename that it is given represents a normal file that the user has execute permission to. You could use this to have \f3cpl_file_completions()\f1 only list completions of executable files. .sp When you have finished with a \f3CplFileConf\f1 variable, you can pass it to the \f3del_CplFileConf()\f1 destructor function to reclaim its memory. .sp .nf CplFileConf *del_CplFileConf(CplFileConf *cfc); .fi .sp .SH THREAD SAFETY In multi-threaded programs, you should use the \f3libtecla_r.a\f1 version of the library. This uses POSIX reentrant functions where available (hence the \f3_r\f1 suffix), and disables features that rely on non-reentrant system functions. In the case of this module, the only disabled feature is username completion in \f3~username/\f1 expressions, in \f3cpl_file_completions()\f1. Using the \f3libtecla_r.a\f1 version of the library, it is safe to use the facilities of this module in multiple threads, provided that each thread uses a separately allocated \f3WordCompletion\f1 object. In other words, if two threads want to do word completion, they should each call \f3new_WordCompletion()\f1 to allocate their own completion objects. .SH FILES .nf libtecla.a - The tecla library libtecla.h - The tecla header file. .fi .SH SEE ALSO libtecla(3), gl_get_line(3), ef_expand_file(3), pca_lookup_file(3) .SH AUTHOR Martin Shepherd (mcs@astro.caltech.edu) xorp/cli/libtecla/man3/cfc_file_start.30000664000076400007640000000003511421137511020071 0ustar greearbgreearb.so man3/cpl_complete_word.3 xorp/cli/libtecla/INSTALL0000664000076400007640000001736111421137511015243 0ustar greearbgreearbTo compile and optionally install the library, it is first necessary to create a makefile for your system, by typing: ./configure The Makefile that this generates is designed to install the files of the library in subdirectories of /usr/local/. If you would prefer to install them under a different directory, you can type: ./configure --prefix /wherever Where you would replace /wherever with your chosen directory. Other command-line options are available, and can be listed by typing: ./configure --help Having run the configure script, you are then ready to make the library. To do this, just type: make What 'make' does depends on whether the configure script knows about your system. If the configure script doesn't know anything specific about your system, it will arrange for 'make' to produce the static tecla library, called libtecla.a, and if possible, the reentrant version of this called libtecla_r.a. If it does know about your system, it will also create shared libraries if possible. If you are on a system that isn't known, and you would like shared libraries to be compiled, please read the file called PORTING to see how this can be achieved. To install the library, its include file and it manual pages, type: make install Note that this will also compile the library if you haven't already done so. Having compiled the library, if you wish, you can test it by running the demo programs. After building the library, you should find two programs, called demo and demo2, in the current directory. The first of the demos programs reads input lines from the user, and writes what was typed back to the screen. While typing a line of input, you can experiment with line editing, tab completion, history recall etc.. For details about these line editing features, see the man page gl_get_line(3). If you haven't installed this yet, you can see it anyway by typing: nroff -man man3/gl_get_line.3 | more The second demo program, called demo2, demonstrates command-completion with the UNIX PATH. If you type in a partial command name, and press TAB, the command name will be completed if possible, and possible completions will be listed if it is ambiguous. When you then enter the line, the demo program then prints out the full pathname of the command that you typed. If you type anything after the command name, filename completion with the tab key reverts to its default behavior of completing filenames in the current directory. COMPILING IN A DIFFERENT DIRECTORY ---------------------------------- If you unpack the distribution in a directory which is visible from multiple hosts which have different architectures, you have the option of compiling the library for the different architectures in different directories. You might for example create a sub-directory for each architecture, under the top level directory of the distribution. You would then log in to a host of one of these architectures, cd to the sub-directory that you created for it, and type: ../configure The configure script then creates a makefile in the current directory which is designed to build the library, object files, demos etc for the architecture of the current host, in the current directory, using the original source code in ../. You then repeat this procedure on hosts of other architectures. The compilation directories don't have to be sub-directories of the top level directory of the distribution. That was just described as an example. They can be anywhere you like. Every rule in the makefiles that are generated by the configure script, cites the paths of the target and source files explicitly, so this procedure should work on any system, without the need for vpath makefile support. EMBEDDING IN OTHER PACKAGE DISTRIBUTIONS ---------------------------------------- If you distribute the library with another package, which has its own heirarchy and configuration procedures, the following installation options may be of interest to you. At first glance, the use of a GNU configure script by the tecla library, may appear to reduce your options for controlling what gets made, and where it gets installed, but this isn't the case, because many of the parameters configured by the configure script are assigned to make variables which can be overriden when you run make. For example, lets say that you have your own configuration script in the parent directory of the libtecla top-level directory. In your configuration script, you would first need to have the following line: (cd libtecla; ./configure) Now, from your makefile or whatever script you use to build your application, you would need to make the library. Assuming that your makefile or build script is in the parent directory of the libtecla distribution, the following line tells make to just make the non-reentrant, static version of the tecla library, and to install it and the tecla include file in sub-directories called lib and include in your current directory. (cd libtecla; make LIBDIR=../lib INCDIR=../include TARGETS=normal TARGET_LIBS="static" install_lib install_inc) First, the LIBDIR=../lib means that on installing the library, it should be placed in the directory libtecla/../lib. Similarly INCDIR tells make where to place the include files. The install_lib and install_inc targets tell make to install the libraries and the include file. Because the install_man and install_bin targets have been omitted in this example, the man pages and programs aren't installed. If you were to include these additional targets then you could use the MANDIR and BINDIR variables, respectively to control where they were installed. The TARGETS variable is used to specify which of the normal and reentrant versions of the library are compiled. This can contain one or both of the words "normal" and "reentrant". If you don't specify this when you invoke make, the default value generated by the configure script will be used. Depending on whether reentrant POSIX functions are available for compilation of the reentrant version, this will be either "normal" or "normal reentrant". The TARGET_LIBS variable is used to specify which of the static and shared libraries are to be built. This can contain one or both of the words "static" and "shared". If you don't specify this when you invoke make, the default value generated by the configure script will be used. Depending on whether the configure script knows how to create shared libraries on the target system, this will be either "static" or "static shared". Beware that shared libraries aren't supported on many systems, so requiring "shared" will limit which systems you can compile your package on. Also note that unless your package installs the tecla library in a directory which all users of your program will have access to, you should only compile the static version. Instructions for adding shared-library creation rules for new systems are included in the PORTING file. The OPT variable can be used to change the default optimization from the default of "-O" to something else. The DEMOS variable controls whether the demo programs are built. Normally this has the value "demos", which tells the makefile to build the demo programs. Setting it to an empty string stops the demos from being built. The PROGRAMS variable is used to specify which programs are to be built and subsequently installed. All available programs are built by default. Currently there is only one such program, selected by specifying the word "enhance". This program uses tecla-enhanced pseudo-terminals to layer command line editing on top of third party programs. The PROGRAMS_R variable serves the same purpose as the PROGRAMS variable, except that programs listed here are linked with the reentrant version of the library, and should be specified with a _r suffix. Currently this variable is empty by default. Martin Shepherd (mcs@astro.caltech.edu) xorp/cli/libtecla/RELEASE.NOTES0000664000076400007640000004050611421137511016101 0ustar greearbgreearbThis file lists major changes which accompany each new release. Version 1.4.0: The contents of the history list can now be saved and restored with the new gl_save_history() and gl_load_history() functions. Event handlers can now be registered to watch for and respond to I/O on arbitrary file descriptors while gl_get_line() is waiting for terminal input from the user. See the gl_get_line(3) man page for details on gl_watch_fd(). As an optional alternative to getting configuration information only from ~/.teclarc, the new gl_configure_getline() function allows configuration commands to be taken from any of, a string, a specified application-specific file, and/or a specified user-specific file. See the gl_get_line(3) man page for details. The version number of the library can now be queried using the libtecla_version() function. See the libtecla(3) man page. The new gl_group_history() function allows applications to group different types of input line in the history buffer, and arrange for only members of the appropriate group to be recalled on a given call to gl_get_line(). See the gl_get_line(3) man page. The new gl_show_history() function displays the current history list to a given stdio output stream. See the gl_get_line(3) man page. new_GetLine() now allows you to specify a history buffer size of zero, thus requesting that no history buffer be allocated. You can subsequently resize or delete the history buffer at any time, by calling gl_resize_history(), limit the number of lines that are allowed in the buffer by calling gl_limit_history(), clear either all history lines from the history list, or just the history lines that are associated with the current history group, by calling gl_clear_history, and toggle the history mechanism on and off by calling gl_toggle_history(). The new gl_terminal_size() function can be used to query the current terminal size. It can also be used to supply a default terminal size on systems where no mechanism is available for looking up the size. The contents and configuration of the history list can now be obtained by the calling application, by calling the new gl_lookup_history(), gl_state_of_history(), gl_range_of_history() and gl_size_of_history() functions. See the gl_get_line(3) man page. Echoing of the input line as it is typed, can now be turned on and off via the new gl_echo_mode() function. While echoing is disabled, newly entered input lines are omitted from the history list. See the gl_get_line(3) man page. While the default remains to display the prompt string literally, the new gl_prompt_style() function can be used to enable text attribute formatting directives in prompt strings, such as underlining, bold font, and highlighting directives. Signal handling in gl_get_line() is now customizable. The default signal handling behavior remains essentially the same, except that the SIGTSTP, SIGTTIN and SIGTTOU are now forwarded to the corresponding signal handler of the calling program, instead of causing a SIGSTOP to be sent to the application. It is now possible to remove signals from the list that are trapped by gl_get_line(), as well as add new signals to this list. The signal and terminal environments in which the signal handler of the calling program is invoked, and what gl_get_line() does after the signal handler returns, is now customizable on a per signal basis. You can now also query the last signal that was caught by gl_get_line(). This is useful when gl_get_line() aborts with errno=EINTR, and you need to know which signal caused it to abort. Key-sequences bound to action functions can now start with printable characters. Previously only keysequences starting with control or meta characters were permitted. gl_get_line() is now 8-bit clean. If the calling program has correctly called setlocale(LC_CTYPE,""), then the user can select an alternate locale by setting the standard LC_CTYPE, LC_ALL, or LANG environment variables, and international characters can then be entered directly, either by using a non-US keyboard, or by using a compose key on a standard US keyboard. Note that in locales in which meta characters become printable, meta characters no longer match M-c bindings, which then have to be entered using their escape-c equivalents. Fortunately most modern terminal emulators either output the escape-c version by default when the meta key is used, or can be configured to do so (see the gl_get_line(3) man page), so in most cases you can continue to use the meta key. Completion callback functions can now tell gl_get_line() to return the input line immediately after a successful tab completion, simply by setting the last character of the optional continuation suffix to a newline character (ie. in the call to cpl_add_completion()). It is now safe to create and use multiple GetLine objects, albeit still only from a single thread. In conjunction with the new gl_configure_getline() function, this optionally allows multiple GetLine objects with different bindings to be used to implement different input modes. The edit-mode configuration command now accepts the argument, none. This tells gl_get_line() to revert to using just the native line editing facilities provided by the terminal driver. This could be used if the termcap or terminfo entry of the host terminal were badly corrupted. Application callback functions invoked by gl_get_line() can now change the displayed prompt using the gl_replace_prompt() function. Their is now an optional program distributed with the library. This is a beta release of a program which adds tecla command-line editing to virtually any third party application without the application needing to be linked to the library. See the enhance(3) man page for further details. Although built and installed by default, the INSTALL document explains how to prevent this. The INSTALL document now explains how you can stop the demo programs from being built and installed. NetBSD/termcap fixes. Mike MacFaden reported two problems that he saw when compiling libtecla under NetBSD. Both cases were related to the use of termcap. Most systems use terminfo, so this problem has gone unnoticed until now, and won't have affected the grand majority of users. The configure script had a bug which prevented the check for CPP working properly, and getline.c wouldn't compile due to an undeclared variable when USE_TERMCAP was defined. Both problems have now been fixed. Note that if you successfully compiled version 1.3.3, this problem didn't affect you. An unfortunate and undocumented binding of the key-sequence M-O was shadowing the arrow-key bindings on systems that use ^[OA etc. I have removed this binding (the documented lower case M-o binding remains bound). Under the KDE konsole terminal this was causing the arrow keys to do something other than expected. There was a bug in the history list code which could result in strange entries appearing at the start of the history list once enough history lines had been added to the list to cause the circular history buffer to wrap. This is now fixed. Version 1.3.3: Signal handling has been re-written, and documentation of its behaviour has been added to the gl_get_line(3) man page. In addition to eliminating race conditions, and appropriately setting errno for those signals that abort gl_get_line(), many more signals are now intercepted, making it less likely that the terminal will be left in raw mode by a signal that isn't trapped by gl_get_line(). A bug was also fixed that was leaving the terminal in raw mode if the editing mode was changed interactively between vi and emacs. This was only noticeable when running programs from old shells that don't reset terminal modes. Version 1.3.2: Tim Eliseo contributed a number of improvements to vi mode, including a fuller set of vi key-bindings, implementation of the vi constraint that the cursor can't backup past the point at which input mode was entered, and restoration of overwritten characters when backspacing in overwrite mode. There are also now new bindings to allow users to toggle between vi and emacs modes interactively. The terminal bell is now used in some circumstances, such as when an unrecognized key sequence is entered. This can be turned off by the new nobeep option in the tecla configuration file. Unrelated to the above, a problem under Linux which prevented ^Q from being used to resume terminal output after the user had pressed ^S, has been fixed. Version 1.3.1: In vi mode a bug was preventing the history-search-backward and history-search-forward actions from doing anything when invoked on empty lines. On empty lines they now act like up-history and down-history respectively, as in emacs mode. When creating shared libraries under Linux, the -soname directive was being used incorrectly. The result is that Linux binaries linked with the 1.2.3, 1.2.4 and 1.3.0 versions of the tecla shared libraries, will refuse to see other versions of the shared library until relinked with version 1.3.1 or higher. The configure script can now handle the fact that under Solaris-2.6 and earlier, the only curses library is a static one that hides in /usr/ccs/lib. Under Linux it now also caters for old versions of GNU ld which don't accept version scripts. The demos are now linked against the shared version of the library if possible. Previously they were always linked with the static version. Version 1.3.0: The major change in this release is the addition of an optional vi command-line editing mode in gl_get_line(), along with lots of new action functions to support its bindings. To enable this, first create a ~/.teclarc file if you don't already have one, then add the following line to it. edit-mode vi The default vi bindings, which are designed to mimic those of the vi editor as closely as possible, are described in the gl_get_line(3) man page. A new convenience function called ef_list_expansions() has been added for listing filename expansions. See the ef_list_expansions(3) man page for details. This is used in a new list-glob binding, bound to ^Xg in emacs mode, and ^G in vi input mode. A bug has been fixed in the key-binding table expansion code. This bug would have caused problems to anybody who defined more than about 18 personalized key-bindings in their ~/.teclarc file. Version 1.2.4: Buffered I/O is now used for writing to terminals, and where supported, cursor motion is done with move-n-positions terminfo capabilities instead of doing lots of move-1-position requests. This greatly improves how the library feels over slow links. You can now optionally compile different architectures in different directories, without having to make multiple copies of the distribution. This is documented in the INSTALL file. The ksh ~+ directive is now supported. Thanks to Markus Gyger for the above improvements. Documentation has been added to the INSTALL file describing features designed to facilitate configuration and installation of the library as part of larger packages. These features are intended to remove the need to modify the tecla distribution's configuration and build procedures when embedding the libtecla distribution in other package distributions. A previous fix to stop the cursor from warping when the last character of the input line was in the last column of the terminal, was only being used for the first terminal line of the input line. It is now used for all subsequent lines as well, as originally intended. Version 1.2.3: The installation procedure has been better automated with the addition of an autoconf configure script. This means that installers can now compile and install the library by typing: ./configure make make install On all systems this makes at least the normal static version of the tecla library. It also makes the reentrant version if reentrant POSIX functions are detected. Under Solaris, Linux and HP-UX the configuration script arranges for shared libraries to be compiled in addition to the static libraries. It is hoped that installers will return information about how to compile shared libraries on other systems, for inclusion in future releases, and to this end, a new PORTING guide has been provided. The versioning number scheme has been changed. This release would have been 1.2c, but instead will be refered to as 1.2.3. The versioning scheme, based on conventions used by Sun Microsystems, is described in configure.in. The library was also tested under HP-UX, and this revealed two serious bugs, both of which have now been fixed. The first bug prevented the library from writing control codes to terminals on big-endian machines, with the exception of those running under Solaris. This was due to an int variable being used where a char was needed. The second bug had the symptom that on systems that don't use the newline character as the control code for moving the cursor down a line, a newline wasn't started when the user hit enter. Version 1.2b: Two more minor bug fixes: Many terminals don't wrap the cursor to the next line when a character is written to the rightmost terminal column. Instead, they delay starting a new line until one more character is written, at which point they move the cursor two positions. gl_get_line() wasn't aware of this, so cursor repositionings just after writing the last character of a column, caused it to erroneously go up a line. This has now been remedied, using a method that should work regardless of whether a terminal exhibits this behavior or not. Some systems dynamically record the current terminal dimensions in environment variables called LINES and COLUMNS. On such systems, during the initial terminal setup, these values should override the static values read from the terminal information databases, and now do. Previously they were only used if the dimensions returned by terminfo/termcap looked bogus. Version 1.2a: This minor release fixes the following two bugs: The initial terminal size and subsequent changes thereto, weren't being noticed by gl_get_line(). This was because the test for the existence of TIOCWINSZ was erroneously placed before the inclusion of termios.h. One of the results was that on input lines that spanned more than one terminal line, the cursor occasionally jumped unexpectedly to the previous terminal line. On entering a line that wrapped over multiple terminal lines, gl_get_line() simply output a carriage-return line-feed at the point at which the user pressed return. Thus if one typed in such a line, then moved back onto one of the earlier terminal lines before hitting return, the cursor was left on a line containing part of the line that had just been entered. This didn't do any harm, but it looked a mess. Version 1.2: A new facility for looking up and completing filenames in UNIX-style paths has now been added (eg. you can search for, or complete commands using the UNIX PATH environment variable). See the pca_lookup_file(3) man page. The already existing filename completion callback can now be made selective in what types of files it lists. See the cpl_complete_word(3) man page. Due to its potential to break applications when changed, the use of the publically defined CplFileArgs structure to configure the cpl_file_completions() callback is now deprecated. The definition of this structure has been frozen, and its documentation has been removed from the man pages. It will remain supported, but if you have used it, you are recommended to switch to the new method, which involves a new opaque configuration object, allocated via a provided constructor function, configured via accessor functions, and eventually deleted with a provided destructor function. The cpl_file_completions() callback distinguishes which structure type it has been sent by virtue of a code placed at the start of the new structure by the constructor. It is assumed that no existing applications set the boolean 'escaped' member of the CplFileArgs structure to 4568. The new method is documented in the cpl_complete_word(3) man page. Version 1.1j This was the initial public release on freshmeat.org. xorp/cli/libtecla/version.c0000664000076400007640000000230711421137511016035 0ustar greearbgreearb#include "libtecla.h" /*....................................................................... * Return the version number of the tecla library. * * Input: * major int * The major version number of the library * will be assigned to *major. This number is * only incremented when a change to the library is * made that breaks binary (shared library) and/or * compilation backwards compatibility. * minor int * The minor version number of the library * will be assigned to *minor. This number is * incremented whenever new functions are added to * the public API. * micro int * The micro version number of the library will be * assigned to *micro. This number is incremented * whenever internal changes are made that don't * change the public API, such as bug fixes and * performance enhancements. */ void libtecla_version(int *major, int *minor, int *micro) { if(major) *major = TECLA_MAJOR_VER; if(minor) *minor = TECLA_MINOR_VER; if(micro) *micro = TECLA_MICRO_VER; } xorp/cli/libtecla/pathutil.c0000664000076400007640000003750111421137511016206 0ustar greearbgreearb/* * Copyright (c) 2000, 2001 by Martin C. Shepherd. * * All rights reserved. * * 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, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, and/or sell copies of the Software, and to permit persons * to whom the Software is furnished to do so, provided that the above * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Except as contained in this notice, the name of a copyright holder * shall not be used in advertising or otherwise to promote the sale, use * or other dealings in this Software without prior written authorization * of the copyright holder. */ #include #include #include #include #include #include #ifndef __MINGW32__ #include #endif #include #include #include "pathutil.h" #ifdef __MINGW32__ #define stat _stat #define statbuf _statbuf #endif /*....................................................................... * Create a new PathName object. * * Output: * return PathName * The new object, or NULL on error. */ PathName *_new_PathName(void) { PathName *path; /* The object to be returned */ /* * Allocate the container. */ path = (PathName *)malloc(sizeof(PathName)); if(!path) { fprintf(stderr, "_new_PathName: Insufficient memory.\n"); return NULL; }; /* * Before attempting any operation that might fail, initialize the * container at least up to the point at which it can safely be passed * to _del_PathName(). */ path->name = NULL; path->dim = 0; /* * Figure out the maximum length of an expanded pathname. */ path->dim = _pu_pathname_dim(); if(path->dim == 0) return _del_PathName(path); /* * Allocate the pathname buffer. */ path->name = (char *)malloc(path->dim * sizeof(char)); if(!path->name) { fprintf(stderr, "_new_PathName: Insufficient memory to allocate pathname buffer.\n"); return _del_PathName(path); }; return path; } /*....................................................................... * Delete a PathName object. * * Input: * path PathName * The object to be deleted. * Output: * return PathName * The deleted object (always NULL). */ PathName *_del_PathName(PathName *path) { if(path) { if(path->name) free(path->name); free(path); }; return NULL; } /*....................................................................... * Return the pathname to a zero-length string. * * Input: * path PathName * The pathname container. * Output: * return char * The cleared pathname buffer, or NULL on error. */ char *_pn_clear_path(PathName *path) { /* * Check the arguments. */ if(!path) { fprintf(stderr, "_pn_clear_path: NULL argument.\n"); return NULL; }; path->name[0] = '\0'; return path->name; } /*....................................................................... * Append a string to a pathname, increasing the size of the pathname * buffer if needed. * * Input: * path PathName * The pathname container. * string const char * The string to be appended to the pathname. * Note that regardless of the slen argument, * this should be a '\0' terminated string. * slen int The maximum number of characters to append * from string[], or -1 to append the whole * string. * remove_escapes int If true, remove the backslashes that escape * spaces, tabs, backslashes etc.. * Output: * return char * The pathname string path->name[], which may * have been reallocated, or NULL if there was * insufficient memory to extend the pathname. */ char *_pn_append_to_path(PathName *path, const char *string, int slen, int remove_escapes) { int pathlen; /* The length of the pathname */ int i; /* * Check the arguments. */ if(!path || !string) { fprintf(stderr, "_pn_append_to_path: NULL argument(s).\n"); return NULL; }; /* * Get the current length of the pathname. */ pathlen = strlen(path->name); /* * How many characters should be appended? */ if(slen < 0 || slen > strlen(string)) slen = strlen(string); /* * Resize the pathname if needed. */ if(!_pn_resize_path(path, pathlen + slen)) return NULL; /* * Append the string to the output pathname, removing any escape * characters found therein. */ if(remove_escapes) { int is_escape = 0; for(i=0; iname[pathlen++] = string[i]; }; /* * Terminate the string. */ path->name[pathlen] = '\0'; } else { /* * Append the string directly to the pathname. */ memcpy(path->name + pathlen, string, slen); path->name[pathlen + slen] = '\0'; }; return path->name; } /*....................................................................... * Prepend a string to a pathname, increasing the size of the pathname * buffer if needed. * * Input: * path PathName * The pathname container. * string const char * The string to be prepended to the pathname. * Note that regardless of the slen argument, * this should be a '\0' terminated string. * slen int The maximum number of characters to prepend * from string[], or -1 to append the whole * string. * remove_escapes int If true, remove the backslashes that escape * spaces, tabs, backslashes etc.. * Output: * return char * The pathname string path->name[], which may * have been reallocated, or NULL if there was * insufficient memory to extend the pathname. */ char *_pn_prepend_to_path(PathName *path, const char *string, int slen, int remove_escapes) { int pathlen; /* The length of the pathname */ int shift; /* The number of characters to shift the suffix by */ int i,j; /* * Check the arguments. */ if(!path || !string) { fprintf(stderr, "_pn_prepend_to_path: NULL argument(s).\n"); return NULL; }; /* * Get the current length of the pathname. */ pathlen = strlen(path->name); /* * How many characters should be appended? */ if(slen < 0 || slen > strlen(string)) slen = strlen(string); /* * Work out how far we need to shift the original path string to make * way for the new prefix. When removing escape characters, we need * final length of the new prefix, after unescaped backslashes have * been removed. */ if(remove_escapes) { int is_escape = 0; for(shift=0,i=0; iname + shift, path->name, pathlen+1); /* * Copy the new prefix into the vacated space at the beginning of the * output pathname, removing any escape characters if needed. */ if(remove_escapes) { int is_escape = 0; for(i=j=0; iname[j++] = string[i]; }; } else { memcpy(path->name, string, slen); }; return path->name; } /*....................................................................... * If needed reallocate a given pathname buffer to allow a string of * a given length to be stored in it. * * Input: * path PathName * The pathname container object. * length size_t The required length of the pathname buffer, * not including the terminating '\0'. * Output: * return char * The pathname buffer, or NULL if there was * insufficient memory (this isn't reported * to stderr). */ char *_pn_resize_path(PathName *path, size_t length) { /* * Check the arguments. */ if(!path) { fprintf(stderr, "_pn_resize_path: NULL argument(s).\n"); return NULL; }; /* * If the pathname buffer isn't large enough to accomodate a string * of the specified length, attempt to reallocate it with the new * size, plus space for a terminating '\0'. Also add a bit of * head room to prevent too many reallocations if the initial length * turned out to be very optimistic. */ if(length + 1 > path->dim) { size_t dim = length + 1 + PN_PATHNAME_INC; char *name = (char *) realloc(path->name, dim); if(!name) return NULL; path->name = name; path->dim = dim; }; return path->name; } /*....................................................................... * Estimate the largest amount of space needed to store a pathname. * * Output: * return size_t The number of bytes needed, including space for the * terminating '\0'. */ size_t _pu_pathname_dim(void) { int maxlen; /* The return value excluding space for the '\0' */ /* * If the POSIX PATH_MAX macro is defined in limits.h, use it. */ #ifdef PATH_MAX maxlen = PATH_MAX; /* * If we have pathconf, use it. */ #elif defined(_PC_PATH_MAX) maxlen = pathconf(FS_ROOT_DIR, _PC_PATH_MAX); if(maxlen < 0) { if(errno) { fprintf(stderr, "pathconf error: %s\n", strerror(errno)); return 0; }; /* * If errno wasn't set then pathconf is telling us that the * pathlength is effectively infinite. We will thus have to * substitute a reasonable guess. */ maxlen = MAX_PATHLEN_FALLBACK; }; /* * None of the above approaches worked, so substitute our fallback * guess. */ #else maxlen = MAX_PATHLEN_FALLBACK; #endif /* * Return the amount of space needed to accomodate a pathname plus * a terminating '\0'. */ return maxlen + 1; } /*....................................................................... * Return non-zero if the specified path name refers to a directory. * * Input: * pathname const char * The path to test. * Output: * return int 0 - Not a directory. * 1 - pathname[] refers to a directory. */ int _pu_path_is_dir(const char *pathname) { struct stat statbuf; /* The file-statistics return buffer */ /* * Look up the file attributes. */ if(stat(pathname, &statbuf) < 0) return 0; /* * Is the file a directory? */ return S_ISDIR(statbuf.st_mode) != 0; } /*....................................................................... * Return non-zero if the specified path name refers to a regular file. * * Input: * pathname const char * The path to test. * Output: * return int 0 - Not a regular file. * 1 - pathname[] refers to a regular file. */ int _pu_path_is_file(const char *pathname) { struct stat statbuf; /* The file-statistics return buffer */ /* * Look up the file attributes. */ if(stat(pathname, &statbuf) < 0) return 0; /* * Is the file a regular file? */ return S_ISREG(statbuf.st_mode) != 0; } /*....................................................................... * Return non-zero if the specified path name refers to an executable. * * Input: * pathname const char * The path to test. * Output: * return int 0 - Not an executable file. * 1 - pathname[] refers to an executable file. */ int _pu_path_is_exe(const char *pathname) { struct stat statbuf; /* The file-statistics return buffer */ /* * Look up the file attributes. */ if(stat(pathname, &statbuf) < 0) return 0; /* * Is the file a regular file which is executable by the current user. */ return S_ISREG(statbuf.st_mode) != 0 && #ifdef __MINGW32__ 1; #else (statbuf.st_mode & (S_IXOTH | S_IXGRP | S_IXUSR)) && access(pathname, X_OK) == 0; #endif } /*....................................................................... * Search backwards for the potential start of a filename. This * looks backwards from the specified index in a given string, * stopping at the first unescaped space or the start of the line. * * Input: * string const char * The string to search backwards in. * back_from int The index of the first character in string[] * that follows the pathname. * Output: * return char * The pointer to the first character of * the potential pathname, or NULL on error. */ char *_pu_start_of_path(const char *string, int back_from) { int i, j; /* * Check the arguments. */ if(!string || back_from < 0) { fprintf(stderr, "_pu_start_path: Invalid argument(s).\n"); return NULL; }; /* * Search backwards from the specified index. */ for(i=back_from-1; i>=0; i--) { int c = string[i]; /* * Stop on unescaped spaces. */ if(isspace((int)(unsigned char)c)) { /* * The space can't be escaped if we are at the start of the line. */ if(i==0) break; /* * Find the extent of the escape characters which precedes the space. */ for(j=i-1; j>=0 && string[j]=='\\'; j--) ; /* * If there isn't an odd number of escape characters before the space, * then the space isn't escaped. */ if((i - 1 - j) % 2 == 0) break; }; }; return (char *)string + i + 1; } /*....................................................................... * Find the length of a potential filename starting from a given * point. This looks forwards from the specified index in a given string, * stopping at the first unescaped space or the end of the line. * * Input: * string const char * The string to search backwards in. * start_from int The index of the first character of the pathname * in string[]. * Output: * return char * The pointer to the character that follows * the potential pathname, or NULL on error. */ char *_pu_end_of_path(const char *string, int start_from) { int c; /* The character being examined */ int escaped = 0; /* True when the next character is escaped */ int i; /* * Check the arguments. */ if(!string || start_from < 0) { fprintf(stderr, "_pu_end_path: Invalid argument(s).\n"); return NULL; }; /* * Search forwards from the specified index. */ for(i=start_from; (c=string[i]) != '\0'; i++) { if(escaped) { escaped = 0; } else if(isspace((int)(unsigned char) c)) { break; } else if(c == '\\') { escaped = 1; }; }; return (char *)string + i; } /*....................................................................... * Return non-zero if the specified path name refers to an existing file. * * Input: * pathname const char * The path to test. * Output: * return int 0 - The file doesn't exist. * 1 - The file does exist. */ int _pu_file_exists(const char *pathname) { struct stat statbuf; return stat(pathname, &statbuf) == 0; } xorp/cli/libtecla/libtecla.map0000664000076400007640000000712311421137511016463 0ustar greearbgreearb# This mapfile (or version script) lists the public symbols that are # publically exported by each version of the tecla library. This file # has the format required by the Sun and Linux linkers, and also acts # as a template from which map files for other systems can be derived # with awk or sed. # # Under Solaris and Linux, this map file is used by ld during shared # library creation. It has two purposes: # # 1. It specifies which symbols in the library are to be made visible # to applications. This has the dual benefits of reducing namespace # polution, and of preventing applications from using private # internal library functions that might change or disappear in # future releases. # # 2. The information listed in this file is recorded in the shared # library, such that when an application is linked against it, the # linker can record a dependency in the application which says # which is the earliest library version which included all of the # symbols that the application needs. This means that if the # application is copied to another system that has an earlier # version of the library, the linker can quickly determine whether # the earlier version contains all of the symbols that it needs. # # Under Linux, mapfiles can also be used to allow multiple # incompatible versions of a given function to exist in a library, # thus supporting applications that were compiled against different # incompatible versions of the library. Since this feature (and the # inclusion of .symver directives) isn't supported by Solaris, it # can't be included in this file. Non backwards compatibility in the # ABI must instead be handled in the more traditional way, by # incrementing the major version number. # # When a new minor release is made, a new tecla_1.x specification # should be added which inherits the symbols of the previous release # and lists newly added functions. For example, below you will find # the following clause: # # tecla_1.3 { # global: # ef_list_expansions; # } tecla_1.2; # # This says that ef_list_expansions is the name of a public function # that was added in the 1.3 release, and that the symbols defined in # the previous tecla_1.2 clause have been inherited by tecla_1.3. # # For more details see the following URL: # # http://www.usenix.org/publications/library/proceedings/als2000/browndavid.html #------------------------------------------------------------------------------- tecla_1.2 { global: cfc_file_start; cfc_literal_escapes; cfc_set_check_fn; cpl_add_completion; cpl_check_exe; cpl_complete_word; cpl_file_completions; cpl_init_FileArgs; cpl_last_error; cpl_list_completions; cpl_record_error; del_CplFileConf; del_ExpandFile; del_GetLine; del_PathCache; del_PcaPathConf; del_WordCompletion; ef_expand_file; ef_last_error; gl_change_terminal; gl_customize_completion; gl_get_line; new_CplFileConf; new_ExpandFile; new_GetLine; new_PathCache; new_PcaPathConf; new_WordCompletion; pca_last_error; pca_lookup_file; pca_path_completions; pca_scan_path; pca_set_check_fn; ppc_file_start; ppc_literal_escapes; local: *; }; tecla_1.3 { global: ef_list_expansions; } tecla_1.2; tecla_1.4 { global: gl_configure_getline; gl_save_history; gl_load_history; gl_group_history; gl_show_history; gl_resize_history; gl_limit_history; gl_clear_history; gl_toggle_history; gl_watch_fd; libtecla_version; gl_terminal_size; gl_state_of_history; gl_range_of_history; gl_size_of_history; gl_lookup_history; gl_echo_mode; gl_replace_prompt; gl_prompt_style; gl_ignore_signal; gl_trap_signal; gl_last_signal; } tecla_1.3; xorp/cli/libtecla/strngmem.h0000664000076400007640000000631511421137511016214 0ustar greearbgreearb#ifndef stringmem_h #define stringmem_h /* * Copyright (c) 2000, 2001 by Martin C. Shepherd. * * All rights reserved. * * 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, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, and/or sell copies of the Software, and to permit persons * to whom the Software is furnished to do so, provided that the above * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Except as contained in this notice, the name of a copyright holder * shall not be used in advertising or otherwise to promote the sale, use * or other dealings in this Software without prior written authorization * of the copyright holder. */ typedef struct StringMem StringMem; /* * Applications that dynamically allocate lots of small strings * run the risk of significantly fragmenting the heap. This module * aims to reduce this risk by allocating large arrays of small fixed * length strings, arranging them as a free-list and allowing * callers to allocate from the list. Strings that are too long * to be allocated from the free-list are allocated from the heap. * Since typical implementations of malloc() eat up a minimum of * 16 bytes per call to malloc() [because of alignment and space * management constraints] it makes sense to set the free-list * string size to 16 bytes. Note that unlike malloc() which typically * keeps 8 bytes per allocation for its own use, our allocator will * return all but one of the 16 bytes for use. One hidden byte of overhead * is reserved for flagging whether the string was allocated directly * from malloc or from the free-list. */ /* * Set the length of each free-list string. The longest string that * will be returned without calling malloc() will be one less than * this number. */ #define SM_STRLEN 16 /* * Create a string free-list container and the first block of its free-list. */ StringMem *_new_StringMem(const char *caller, unsigned blocking_factor); /* * Delete a string free-list. */ StringMem *_del_StringMem(const char *caller, StringMem *sm, int force); /* * Allocate an array of 'length' chars. */ char *_new_StringMemString(StringMem *sm, size_t size); /* * Free a string that was previously returned by _new_StringMemString(). */ char *_del_StringMemString(StringMem *sm, char *s); #endif xorp/cli/libtecla/freelist.c0000664000076400007640000003010011421137511016155 0ustar greearbgreearb/* * Copyright (c) 2000, 2001 by Martin C. Shepherd. * * All rights reserved. * * 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, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, and/or sell copies of the Software, and to permit persons * to whom the Software is furnished to do so, provided that the above * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Except as contained in this notice, the name of a copyright holder * shall not be used in advertising or otherwise to promote the sale, use * or other dealings in this Software without prior written authorization * of the copyright holder. */ #include #include #include "freelist.h" typedef struct FreeListBlock FreeListBlock; struct FreeListBlock { FreeListBlock *next; /* The next block in the list */ char *nodes; /* The array of free-list nodes */ }; struct FreeList { size_t node_size; /* The size of a free-list node */ unsigned blocking_factor; /* The number of nodes per block */ long nbusy; /* The number of nodes that are in use */ FreeListBlock *block; /* The head of the list of free-list blocks */ void *free_list; /* The free-list of nodes */ }; static FreeListBlock *_new_FreeListBlock(FreeList *fl); static FreeListBlock *_del_FreeListBlock(FreeListBlock *fl); static void _thread_FreeListBlock(FreeList *fl, FreeListBlock *block); /*....................................................................... * Allocate a new free-list from blocks of 'blocking_factor' objects of size * node_size. * * Input: * caller const char * The name of the calling function, for use in * error messages, or NULL to not report errors * to stderr. * node_size size_t The size of the free-list nodes to be returned * by _new_FreeListNode(). Use sizeof() to * determine this. * blocking_factor unsigned The number of objects of size 'object_size' * to allocate per block. * Output: * return FreeList * The new freelist, or NULL on error. */ FreeList *_new_FreeList(const char *caller, size_t node_size, unsigned blocking_factor) { FreeList *fl; /* The new free-list container */ /* * When a free-list node is on the free-list, it is used as a (void *) * link field. Roundup node_size to a mulitple of the size of a void * pointer. This, plus the fact that the array of nodes is obtained via * malloc, which returns memory suitably aligned for any object, will * ensure that the first sizeof(void *) bytes of each node will be * suitably aligned to use as a (void *) link pointer. */ node_size = sizeof(void *) * ((node_size + sizeof(void *) - 1) / sizeof(void *)); /* * Enfore a minimum block size. */ if(blocking_factor < 1) blocking_factor = 1; /* * Allocate the container of the free list. */ fl = (FreeList *) malloc(sizeof(FreeList)); if(!fl) { if(caller) fprintf(stderr, "_new_FreeList (%s): Insufficient memory.\n", caller); return NULL; }; /* * Before attempting any operation that might fail, initialize the * container at least up to the point at which it can safely be passed * to _del_FreeList(). */ fl->node_size = node_size; fl->blocking_factor = blocking_factor; fl->nbusy = 0; fl->block = NULL; fl->free_list = NULL; /* * Allocate the first block of memory. */ fl->block = _new_FreeListBlock(fl); if(!fl->block) { if(caller) fprintf(stderr, "_new_FreeList (%s): Insufficient memory.\n", caller); return _del_FreeList(caller, fl, 1); }; /* * Add the new list of nodes to the free-list. */ fl->free_list = fl->block->nodes; /* * Return the free-list for use. */ return fl; } /*....................................................................... * Re-thread a freelist to reclaim all allocated nodes. * This function should not be called unless if it is known that none * of the currently allocated nodes are still being used. * * Input: * fl FreeList * The free-list to be reset, or NULL. */ void _rst_FreeList(FreeList *fl) { if(fl) { FreeListBlock *block; /* * Re-thread the nodes of each block into individual free-lists. */ for(block=fl->block; block; block=block->next) _thread_FreeListBlock(fl, block); /* * Link all of the block freelists into one large freelist. */ fl->free_list = NULL; for(block=fl->block; block; block=block->next) { /* * Locate the last node of the current block. */ char *last_node = block->nodes + fl->node_size * (fl->blocking_factor - 1); /* * Make the link-field of the last node point to the first * node of the current freelist, then make the first node of the * new block the start of the freelist. */ *(void **)last_node = fl->free_list; fl->free_list = block->nodes; }; /* * All allocated nodes have now been returned to the freelist. */ fl->nbusy = 0; }; } /*....................................................................... * Delete a free-list. * * Input: * caller const char * The name of the calling function, for use in * error messages, or NULL if error messages * shouldn't be reported to stderr. * fl FreeList * The free-list to be deleted, or NULL. * force int If force==0 then _del_FreeList() will complain * and refuse to delete the free-list if any * of nodes have not been returned to the free-list. * If force!=0 then _del_FreeList() will not check * whether any nodes are still in use and will * always delete the list. * Output: * return FreeList * Always NULL (even if the list couldn't be * deleted). */ FreeList *_del_FreeList(const char *caller, FreeList *fl, int force) { if(fl) { /* * Check whether any nodes are in use. */ if(!force && _busy_FreeListNodes(fl) != 0) { if(caller) fprintf(stderr, "_del_FreeList (%s): %ld nodes are still in use.\n", caller, _busy_FreeListNodes(fl)); return NULL; }; /* * Delete the list blocks. */ { FreeListBlock *next = fl->block; while(next) { FreeListBlock *block = next; next = block->next; block = _del_FreeListBlock(block); }; }; fl->block = NULL; fl->free_list = NULL; /* * Discard the container. */ free(fl); }; return NULL; } /*....................................................................... * Allocate a new object from a free-list. * * Input: * fl FreeList * The free-list to return an object from. * Output: * return void * A new object of the size that was specified via * the node_size argument of _new_FreeList() when * the free-list was created, or NULL if there * is insufficient memory, or 'fl' is NULL. */ void *_new_FreeListNode(FreeList *fl) { void *node; /* The node to be returned */ /* * Check arguments. */ if(!fl) return NULL; /* * If the free-list has been exhausted extend it by allocating * another block of nodes. */ if(!fl->free_list) { FreeListBlock *block = _new_FreeListBlock(fl); if(!block) return NULL; /* * Prepend the new block to the list of free-list blocks. */ block->next = fl->block; fl->block = block; /* * Add the new list of nodes to the free-list. */ fl->free_list = fl->block->nodes; }; /* * Remove and return a node from the front of the free list. */ node = fl->free_list; fl->free_list = *(void **)node; /* * Record the loss of a node from the free-list. */ fl->nbusy++; /* * Return the node. */ return node; } /*....................................................................... * Return an object to the free-list that it was allocated from. * * Input: * caller const char * The name of the calling function, for use in * error messages, or NULL to not report errors * to stderr. * fl FreeList * The free-list from which the object was taken. * object void * The node to be returned. * Output: * return void * Always NULL. */ void *_del_FreeListNode(FreeList *fl, void *object) { /* * Check arguments. */ if(!fl) return NULL; /* * Return the node to the head of the free list. */ if(object) { *(void **)object = fl->free_list; fl->free_list = object; /* * Record the return of the node to the free-list. */ fl->nbusy--; }; return NULL; } /*....................................................................... * Return a count of the number of nodes that are currently allocated. * * Input: * fl FreeList * The list to count wrt, or NULL. * Output: * return long The number of nodes (or 0 if fl==NULL). */ long _busy_FreeListNodes(FreeList *fl) { return fl ? fl->nbusy : 0; } /*....................................................................... * Allocate a new list of free-list nodes. On return the nodes will * be linked together as a list starting with the node at the lowest * address and ending with a NULL next pointer. * * Input: * fl FreeList * The free-list to allocate the list for. * Output: * return FreeListBlock * The new linked block of free-list nodes, * or NULL on error. */ static FreeListBlock *_new_FreeListBlock(FreeList *fl) { FreeListBlock *block; /* The new block to be returned */ /* * Allocate the container. */ block = (FreeListBlock *) malloc(sizeof(FreeListBlock)); if(!block) return NULL; /* * Before attempting any operation that might fail, initialize the * container at least up to the point at which it can safely be passed * to _del_FreeListBlock(). */ block->next = NULL; block->nodes = NULL; /* * Allocate the block of nodes. */ block->nodes = (char *) malloc(fl->node_size * fl->blocking_factor); if(!block->nodes) return _del_FreeListBlock(block); /* * Initialize the block as a linked list of FreeListNode's. */ _thread_FreeListBlock(fl, block); return block; } /*....................................................................... * Link each node of a freelist block to the node that follows it. * * Input: * fl FreeList * The freelist that contains the block. * block FreeListBlock * The block to be threaded. */ static void _thread_FreeListBlock(FreeList *fl, FreeListBlock *block) { char *mem = block->nodes; int i; for(i=0; iblocking_factor - 1; i++, mem += fl->node_size) *(void **)mem = mem + fl->node_size; /* Link to the next node */ *(void **)mem = NULL; /* Terminate the list */ } /*....................................................................... * Delete a free-list block. * * Input: * fl FreeListBlock * The block to be deleted, or NULL. * Output: * return FreeListBlock * Always NULL. */ static FreeListBlock *_del_FreeListBlock(FreeListBlock *fl) { if(fl) { fl->next = NULL; if(fl->nodes) free(fl->nodes); fl->nodes = NULL; free(fl); }; return NULL; } xorp/cli/libtecla/pathutil.h0000664000076400007640000001054111421137511016206 0ustar greearbgreearb#ifndef pathutil_h #define pathutil_h /* * Copyright (c) 2000, 2001 by Martin C. Shepherd. * * All rights reserved. * * 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, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, and/or sell copies of the Software, and to permit persons * to whom the Software is furnished to do so, provided that the above * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Except as contained in this notice, the name of a copyright holder * shall not be used in advertising or otherwise to promote the sale, use * or other dealings in this Software without prior written authorization * of the copyright holder. */ /* * The following object encapsulates a buffer designed to be used to * store pathnames. The pathname member of the object is initially * allocated with the size that _pu_pathname_dim() returns, and then * if this turns out to be pessimistic, the pathname can be reallocated * via calls to pb_append_to_path() and/or pb_resize_path(). */ typedef struct { char *name; /* The path buffer */ size_t dim; /* The current allocated size of buffer[] */ } PathName; PathName *_new_PathName(void); PathName *_del_PathName(PathName *path); char *_pn_clear_path(PathName *path); char *_pn_append_to_path(PathName *path, const char *string, int slen, int remove_escapes); char *_pn_prepend_to_path(PathName *path, const char *string, int slen, int remove_escapes); char *_pn_resize_path(PathName *path, size_t length); /* * Search backwards for the potential start of a filename. This * looks backwards from the specified index in a given string, * stopping at the first unescaped space or the start of the line. */ char *_pu_start_of_path(const char *string, int back_from); /* * Find the end of a potential filename, starting from a given index * in the string. This looks forwards from the specified index in a * given string, stopping at the first unescaped space or the end * of the line. */ char *_pu_end_of_path(const char *string, int start_from); /* * Return an estimate of the the length of the longest pathname * on the local system. */ size_t _pu_pathname_dim(void); /* * Return non-zero if the specified path name refers to a directory. */ int _pu_path_is_dir(const char *pathname); /* * Return non-zero if the specified path name refers to a regular file. */ int _pu_path_is_file(const char *pathname); /* * Return non-zero if the specified path name refers to an executable. */ int _pu_path_is_exe(const char *pathname); /* * Return non-zero if a file exists with the specified pathname. */ int _pu_file_exists(const char *pathname); /* * If neither the POSIX PATH_MAX macro nor the pathconf() function * can be used to find out the maximum pathlength on the target * system, the following fallback maximum length is used. */ #define MAX_PATHLEN_FALLBACK 1024 /* * If the pathname buffer turns out to be too small, it will be extended * in chunks of the following amount (plus whatever is needed at the time). */ #define PN_PATHNAME_INC 100 /* * Define the special character-sequences of the filesystem. */ #define FS_ROOT_DIR "/" /* The root directory */ #define FS_ROOT_DIR_LEN (sizeof(FS_ROOT_DIR) - 1) #define FS_PWD "." /* The current working directory */ #define FS_PWD_LEN (sizeof(FS_PWD_LEN) - 1) #define FS_DIR_SEP "/" /* The directory separator string */ #define FS_DIR_SEP_LEN (sizeof(FS_DIR_SEP) - 1) #endif xorp/cli/libtecla/homedir.c0000664000076400007640000003220111421137511015773 0ustar greearbgreearb/* * Copyright (c) 2000, 2001 by Martin C. Shepherd. * * All rights reserved. * * 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, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, and/or sell copies of the Software, and to permit persons * to whom the Software is furnished to do so, provided that the above * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Except as contained in this notice, the name of a copyright holder * shall not be used in advertising or otherwise to promote the sale, use * or other dealings in this Software without prior written authorization * of the copyright holder. */ #include #include #include #include #include #include #ifndef __MINGW32__ #include #include #endif #include "pathutil.h" #include "homedir.h" /* * Set the max length of the error-reporting string. There is no point * in this being longer than the width of a typical terminal window. * In composing error messages, I have assumed that this number is * at least 80, so you don't decrease it below this number. */ #define ERRLEN 200 /* * Use the reentrant POSIX threads versions of the password lookup functions? */ #if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199506L #define THREAD_COMPATIBLE 1 #endif /* * Provide a password buffer size fallback in case the max size reported * by sysconf() is said to be indeterminate. */ #define DEF_GETPW_R_SIZE_MAX 1024 /* * The resources needed to lookup and record a home directory are * maintained in objects of the following type. */ struct HomeDir { char errmsg[ERRLEN+1]; /* Error-report buffer */ char *buffer; /* A buffer for reading password entries and */ /* directory paths. */ int buflen; /* The allocated size of buffer[] */ #ifdef THREAD_COMPATIBLE struct passwd pwd; /* The password entry of a user */ #endif }; static const char *hd_getpwd(HomeDir *home); /*....................................................................... * Create a new HomeDir object. * * Output: * return HomeDir * The new object, or NULL on error. */ HomeDir *_new_HomeDir(void) { HomeDir *home; /* The object to be returned */ size_t pathlen; /* The estimated maximum size of a pathname */ /* * Allocate the container. */ home = (HomeDir *) malloc(sizeof(HomeDir)); if(!home) { fprintf(stderr, "_new_HomeDir: Insufficient memory.\n"); return NULL; }; /* * Before attempting any operation that might fail, initialize the * container at least up to the point at which it can safely be passed * to _del_HomeDir(). */ home->errmsg[0] = '\0'; home->buffer = NULL; home->buflen = 0; /* * Allocate the buffer that is used by the reentrant POSIX password-entry * lookup functions. */ #ifdef THREAD_COMPATIBLE /* * Get the length of the buffer needed by the reentrant version * of getpwnam(). */ #ifndef _SC_GETPW_R_SIZE_MAX home->buflen = DEF_GETPW_R_SIZE_MAX; #else errno = 0; home->buflen = sysconf(_SC_GETPW_R_SIZE_MAX); /* * Limit not available? */ if(home->buflen < 0) { /* * Was the limit unavailable because of an error? */ if(errno) { fprintf(stderr, "syconf(_SC_GETPW_R_SIZE_MAX) -> %s\n", strerror(errno)); return _del_HomeDir(home); }; /* * If errno isn't set, it means that the limit is indeterminate. In this * case substitute a suitably large fallback value. */ home->buflen = DEF_GETPW_R_SIZE_MAX; }; #endif #endif /* * If the existing buffer length requirement is too restrictive to record * a pathname, increase its length. */ pathlen = _pu_pathname_dim(); if(pathlen > home->buflen) home->buflen = pathlen; /* * Allocate a work buffer. */ home->buffer = (char *) malloc(home->buflen); if(!home->buffer) { fprintf(stderr, "_new_HomeDir: Insufficient memory."); return _del_HomeDir(home); }; return home; } /*....................................................................... * Delete a HomeDir object. * * Input: * home HomeDir * The object to be deleted. * Output: * return HomeDir * The deleted object (always NULL). */ HomeDir *_del_HomeDir(HomeDir *home) { if(home) { if(home->buffer) free(home->buffer); free(home); }; return NULL; } /*....................................................................... * Lookup the home directory of a given user in the password file. * * Input: * home HomeDir * The resources needed to lookup the home directory. * user const char * The name of the user to lookup, or "" to lookup * the home directory of the person running the * program. * Output: * return const char * The home directory. If the library was compiled * with threads, this string is part of the HomeDir * object and will change on subsequent calls. If * the library wasn't compiled to be reentrant, * then the string is a pointer into a static string * in the C library and will change not only on * subsequent calls to this function, but also if * any calls are made to the C library password * file lookup functions. Thus to be safe, you should * make a copy of this string before calling any * other function that might do a password file * lookup. * * On error, NULL is returned and a description * of the error can be acquired by calling * _hd_last_home_dir_error(). */ const char *_hd_lookup_home_dir(HomeDir *home, const char *user) { const char *home_dir; /* A pointer to the home directory of the user */ /* * If no username has been specified, arrange to lookup the current * user. */ int login_user = !user || *user=='\0'; /* * Check the arguments. */ if(!home) { fprintf(stderr, "_hd_lookup_home_dir: NULL argument(s).\n"); return NULL; }; #ifdef __MINGW32__ return NULL; #else /* * Handle the ksh "~+". This expands to the absolute path of the * current working directory. */ if (!login_user && strcmp(user, "+") == 0) { home_dir = hd_getpwd(home); if(!home_dir) { strncpy(home->errmsg, "Cannot determine current directory.", ERRLEN); home->errmsg[ERRLEN] = '\0'; return NULL; } return home_dir; }; /* * Look up the password entry of the user. * First the POSIX threads version - this is painful! */ #ifdef THREAD_COMPATIBLE { struct passwd *ret; /* The returned pointer to pwd */ int status; /* The return value of getpwnam_r() */ /* * Look up the password entry of the specified user. */ if(login_user) status = getpwuid_r(geteuid(), &home->pwd, home->buffer, home->buflen, &ret); else status = getpwnam_r(user, &home->pwd, home->buffer, home->buflen, &ret); if(status || !ret) { const char *fmt = "User '%.*s' doesn't exist."; snprintf(home->errmsg, sizeof(home->errmsg), fmt, ERRLEN - strlen(fmt), user); return NULL; }; /* * Get a pointer to the string that holds the home directory. */ home_dir = home->pwd.pw_dir; }; /* * Now the classic unix version. */ #else { struct passwd *pwd = login_user ? getpwuid(geteuid()) : getpwnam(user); if(!pwd) { const char *fmt = "User '%.*s' doesn't exist."; snprintf(home->errmsg, sizeof(home->errmsg), fmt, ERRLEN - strlen(fmt), user); return NULL; }; /* * Get a pointer to the home directory. */ home_dir = pwd->pw_dir; }; #endif return home_dir; #endif /* __MINGW32__*/ } /*....................................................................... * Return a description of the last error that caused _hd_lookup_home_dir() * to return NULL. * * Input: * home HomeDir * The resources needed to record the home directory. * Output: * return char * The description of the last error. */ const char *_hd_last_home_dir_error(HomeDir *home) { return home ? home->errmsg : "NULL HomeDir argument"; } /*....................................................................... * The _hd_scan_user_home_dirs() function calls a user-provided function * for each username known by the system, passing the function both * the name and the home directory of the user. * * Input: * home HomeDir * The resource object for reading home * directories. * data void * Anonymous data to be passed to the * callback function. * callback_fn HOME_DIR_FN(*) The function to call for each user. * Output: * return int 0 - Successful completion. * 1 - An error occurred. A description * of the error can be obtained by * calling _hd_last_home_dir_error(). */ int _hd_scan_user_home_dirs(HomeDir *home, void *data, HOME_DIR_FN(*callback_fn)) { #ifdef __MINGW32__ return 1; #else int waserr = 0; /* True after errors */ /* * Check the arguments. */ if(!home || !callback_fn) { if(home) strncpy(home->errmsg, "_hd_scan_user_home_dirs: Missing callback function", sizeof(home->errmsg)); return 1; }; /* * There are no reentrant versions of getpwent() etc for scanning * the password file, so disable username completion when the * library is compiled to be reentrant. */ #if !defined(_POSIX_C_SOURCE) || _POSIX_C_SOURCE < 199506L { struct passwd *pwd; /* The pointer to the latest password entry */ /* * Open the password file. */ setpwent(); /* * Read the contents of the password file, looking for usernames * that start with the specified prefix, and adding them to the * list of matches. */ while((pwd = getpwent()) != NULL && !waserr) waserr = callback_fn(data, pwd->pw_name, pwd->pw_dir, home->errmsg, ERRLEN); /* * Close the password file. */ endpwent(); }; #endif /* * Under ksh ~+ stands for the absolute pathname of the current working * directory. */ if (!waserr) { const char *pwd = hd_getpwd(home); if(pwd) { waserr = callback_fn(data, "+", pwd, home->errmsg, ERRLEN); } else { waserr = 1; strncpy(home->errmsg, "Cannot determine current directory.", ERRLEN); home->errmsg[ERRLEN] = '\0'; }; }; return waserr; #endif /* __MINGW32__ */ } /*....................................................................... * Return the value of getenv("PWD") if this points to the current * directory, or the return value of getcwd() otherwise. The reason for * prefering PWD over getcwd() is that the former preserves the history * of symbolic links that have been traversed to reach the current * directory. This function is designed to provide the equivalent * expansion of the ksh ~+ directive, which normally returns its value * of PWD. * * Input: * home HomeDir * The resource object for reading home directories. * Output: * return const char * A pointer to either home->buffer, where the * pathname is recorded, the string returned by * getenv("PWD"), or NULL on error. */ static const char *hd_getpwd(HomeDir *home) { /* * Get the absolute path of the current working directory. */ char *cwd = getcwd(home->buffer, home->buflen); /* * Some shells set PWD with the path of the current working directory. * This will differ from cwd in that it won't have had symbolic links * expanded. */ const char *pwd = getenv("PWD"); /* * If PWD was set, and it points to the same directory as cwd, return * its value. Note that it won't be the same if the current shell or * the current program has changed directories, after inheriting PWD * from a parent shell. */ struct stat cwdstat, pwdstat; if(pwd && cwd && stat(cwd, &cwdstat)==0 && stat(pwd, &pwdstat)==0 && cwdstat.st_dev == pwdstat.st_dev && cwdstat.st_ino == pwdstat.st_ino) return pwd; /* * Also return pwd if getcwd() failed, since it represents the best * information that we have access to. */ if(!cwd) return pwd; /* * In the absence of a valid PWD, return cwd. */ return cwd; } xorp/cli/libtecla/nettest_libtecla.c0000664000076400007640000002746011421137511017704 0ustar greearbgreearb/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- */ /* * Copyright (c) 2001-2009 XORP, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 2, June * 1991 as published by the Free Software Foundation. Redistribution * and/or modification of this program under the terms of any other * version of the GNU General Public License is not permitted. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, * see the GNU General Public License, Version 2, a copy of which can be * found in the XORP LICENSE.gpl file. * * XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; * http://xorp.net */ /* * Sample CLI (Command-Line Interface) implementation. * * Author: Pavlin Radoslavov (pavlin@icsi.berkeley.edu) * Date: Wed Dec 12 00:11:34 PST 2001 * * XXX: note that this example is very simplified: * it doesn't handle more than one connection, the error checks are * not adequate, etc. */ /* * To complile, you need a modified version of libtecla that supports * network connection. If the directory to that library is ``./libtecla/'', * then: * gcc -g nettest_libtecla.c -L./libtecla -ltecla -ltermcap -o nettest_libtecla * * Usage: * 1. Start this program: ./nettest_libtecla * 2. From a different window you can telnet to this process on port 12000: * telnet localhost 12000 */ #include #include #include #include #include #include #include #include #include #include "libtecla/libtecla.h" /* * Exported variables */ /* * Local constants definitions */ #define CLIENT_TERMINAL 1 #define CLI_WELCOME "Welcome!" #define CLI_PROMPT "CLI> " /* * Local structures, typedefs and macros */ /* * Local variables */ static int _serv_socket = -1; static unsigned short _serv_port = 0; /* XXX: not defined yet */ static FILE *_fd_file_read = NULL; static FILE *_fd_file_write = NULL; static int _client_type = CLIENT_TERMINAL; static GetLine *_gl = NULL; static int _telnet_iac = 0; static int _telnet_sb = 0; static int _window_width = 0; static int _window_height = 0; static int _telnet_sb_buffer_size = 0; static char _telnet_sb_buffer[1024]; static int _telnet_dont = 0; static int _telnet_do = 0; static int _telnet_wont = 0; static int _telnet_will = 0; static char _command_buffer[1024]; static int _command_buffer_size = 0; static int _buff_curpos = 0; /* * Local functions prototypes */ void cli_setup(); void start_connection(int fd); void client_read(int fd); int command_line_character(const char *line, int word_end, uint8_t val); int main(int argc, char *argv[]) { struct sockaddr_in sin_addr; struct sockaddr_in cli_addr; int addrlen; fd_set read_fds, write_fds; int sock = -1; int max_sock; int reuse = 1; _serv_socket = socket(AF_INET, SOCK_STREAM, 0); if (setsockopt(_serv_socket, SOL_SOCKET, SO_REUSEADDR, (void *)&reuse, sizeof(reuse)) < 0) { perror("setsockopt(SO_REUSEADDR)"); exit (1); } /* TODO: comment-out sin_len setup if the OS doesn't have it */ memset(&sin_addr, 0, sizeof(sin_addr)); sin_addr.sin_len = sizeof(sin_addr); sin_addr.sin_family = AF_INET; sin_addr.sin_port = htons(12000); /* XXX: fixed port 12000 */ sin_addr.sin_addr.s_addr = INADDR_ANY; if (bind(_serv_socket, (struct sockaddr *)&sin_addr, sizeof(sin_addr)) < 0) { perror("Error binding"); exit (1); } if (listen(_serv_socket, 5) < 0) { perror("Error listen"); exit (1); } cli_setup(); for ( ;; ) { struct timeval my_timeval; max_sock = _serv_socket; FD_ZERO(&read_fds); FD_ZERO(&write_fds); FD_SET(_serv_socket, &read_fds); /* FD_SET(_serv_socket, &write_fds); */ if (sock >= 0) { FD_SET(sock, &read_fds); /* FD_SET(sock, &write_fds); */ if (max_sock < sock) max_sock = sock; } max_sock++; my_timeval.tv_sec = 1; my_timeval.tv_usec = 0; select(max_sock, &read_fds, &write_fds, NULL, &my_timeval); if (FD_ISSET(_serv_socket, &read_fds)) { memset(&cli_addr, 0, sizeof(cli_addr)); cli_addr.sin_len = sizeof(cli_addr); cli_addr.sin_family = AF_INET; sock = accept(_serv_socket, (struct sockaddr *)&cli_addr, &addrlen); if (sock < 0) { perror("Error accepting connection"); exit (1); } start_connection(sock); continue; } if ((sock >= 0) && FD_ISSET(sock, &read_fds)) { client_read(sock); } printf("Tick...\n"); } } void cli_setup() { _fd_file_read = NULL; _fd_file_write = NULL; _client_type = CLIENT_TERMINAL; /* XXX: default is terminal */ _gl = NULL; _telnet_iac = 0; _telnet_sb = 0; /* TODO: those should be read in the beginning */ _window_width = 80; _window_height = 25; } const char * newline() { if (_client_type == CLIENT_TERMINAL) return ("\r\n"); return ("\n"); } void start_connection(int fd) { char cli_welcome[] = CLI_WELCOME; char cli_prompt[] = CLI_PROMPT; char buf[128]; /* Setup the telnet options */ { char will_echo_cmd[] = { IAC, WILL, TELOPT_ECHO, '\0' }; char will_sga_cmd[] = { IAC, WILL, TELOPT_SGA, '\0' }; char dont_linemode_cmd[] = { IAC, DONT, TELOPT_LINEMODE, '\0' }; char do_window_size_cmd[] = { IAC, DO, TELOPT_NAWS, '\0' }; char do_transmit_binary_cmd[] = { IAC, DO, TELOPT_BINARY, '\0' }; write(fd, will_echo_cmd, sizeof(will_echo_cmd)); write(fd, will_sga_cmd, sizeof(will_sga_cmd)); write(fd, dont_linemode_cmd, sizeof(dont_linemode_cmd)); write(fd, do_window_size_cmd, sizeof(do_window_size_cmd)); write(fd, do_transmit_binary_cmd, sizeof(do_transmit_binary_cmd)); } snprintf(buf, sizeof(buf), "%s%s", cli_welcome, newline()); write(fd, buf, strlen(buf)); snprintf(buf, sizeof(buf), "%s", cli_prompt); write(fd, buf, strlen(buf)); _fd_file_read = fdopen(fd, "r"); _fd_file_write = fdopen(fd, "w"); _gl = new_GetLine(1024, 2048); if (_gl == NULL) exit (1); /* Change the input and output streams for libtecla */ if (gl_change_terminal(_gl, _fd_file_read, _fd_file_write, NULL) != 0) { _gl = del_GetLine(_gl); exit (1); } } void client_read(int fd) { char *line = NULL; char buf[1024]; int i, n, ret_value; /* * XXX: Peek at the data, and later it will be read * by the command-line processing library */ n = recv(fd, buf, sizeof(buf), MSG_PEEK); for (i = 0; i < n; i++) printf("VAL = %d\n", buf[i]); printf("client_read %d octets\n", n); if (n <= 0) { exit (0); } /* Scan the input data and filter-out the Telnet commands */ for (i = 0; i < n; i++) { uint8_t val = buf[i]; if (val == IAC) { /* Probably a telnet command */ if (! _telnet_iac) { _telnet_iac = 1; goto post_process_telnet_label; } _telnet_iac = 0; } if (_telnet_iac) { switch (val) { case SB: /* Begin subnegotiation of the indicated option. */ _telnet_sb_buffer_size = 0; _telnet_sb = 1; break; case SE: /* End subnegotiation of the indicated option. */ if (! _telnet_sb) break; switch(_telnet_sb_buffer[0]) { case TELOPT_NAWS: /* Telnet Window Size Option */ if (_telnet_sb_buffer_size < 5) break; { _window_width = 256 * _telnet_sb_buffer[1]; _window_width += _telnet_sb_buffer[2]; _window_height = 256 * _telnet_sb_buffer[3]; _window_height += _telnet_sb_buffer[4]; printf("Client window size changed to width = %d " "height = %d\n", _window_width, _window_height); } break; default: break; } _telnet_sb_buffer_size = 0; _telnet_sb = 0; break; case DONT: /* you are not to use option */ _telnet_dont = 1; break; case DO: /* please, you use option */ _telnet_do = 1; break; case WONT: /* I won't use option */ _telnet_wont = 1; break; case WILL: /* I will use option */ _telnet_will = 1; break; case GA: /* you may reverse the line */ break; case EL: /* erase the current line */ break; case EC: /* erase the current character */ break; case AYT: /* are you there */ break; case AO: /* abort output--but let prog finish */ break; case IP: /* interrupt process--permanently */ break; case BREAK: /* break */ break; case DM: /* data mark--for connect. cleaning */ break; case NOP: /* nop */ break; case EOR: /* end of record (transparent mode) */ break; case ABORT: /* Abort process */ break; case SUSP: /* Suspend process */ break; case xEOF: /* End of file: EOF is already used... */ break; default: break; } _telnet_iac = 0; goto post_process_telnet_label; } if (_telnet_sb) { /* A negotiated option value */ _telnet_sb_buffer[_telnet_sb_buffer_size] = val; if ( ++_telnet_sb_buffer_size >= sizeof(_telnet_sb_buffer)) { /* This client is sending too much options. Kick it out! */ printf("Removing client!\n"); exit (1); } goto post_process_telnet_label; } if (_telnet_dont) { /* Telnet DONT option code */ _telnet_dont = 0; goto post_process_telnet_label; } if (_telnet_do) { /* Telnet DO option code */ _telnet_do = 0; goto post_process_telnet_label; } if (_telnet_wont) { /* Telnet WONT option code */ _telnet_wont = 0; goto post_process_telnet_label; } if (_telnet_will) { /* Telnet WILL option code */ _telnet_will = 0; goto post_process_telnet_label; } line = gl_get_line_net(_gl, CLI_PROMPT, _command_buffer, _buff_curpos); fflush(_fd_file_write); /* Store the line in the command buffer */ if (line != NULL) { if ((val == '\n') || (val == '\r')) { /* New command */ fprintf(_fd_file_write, "Your command is: %s%s", line, newline()); fflush(_fd_file_write); fprintf(_fd_file_write, "%s", CLI_PROMPT); fflush(_fd_file_write); _command_buffer_size = 0; _command_buffer[0] = '\0'; } else { /* Same-line character */ int gl_buff_curpos = gl_get_buff_curpos(_gl); if (command_line_character(line, gl_buff_curpos, val)) { int i; /* We need to print this character */ _command_buffer_size = 0; _command_buffer[0] = '\0'; ret_value = 0; for (i = 0; line[i] != '\0'; i++) { _command_buffer[_command_buffer_size] = line[i]; if (++_command_buffer_size >= sizeof(_command_buffer)) ret_value = -1; if (ret_value < 0) break; } if (ret_value == 0) _command_buffer[_command_buffer_size] = '\0'; if (++_command_buffer_size >= sizeof(_command_buffer)) ret_value = -1; if (ret_value != 0) { /* This client is sending too much data. Kick it out!*/ printf("Removing client!\n"); exit (1); } _buff_curpos = gl_buff_curpos; } } } fflush(_fd_file_write); continue; post_process_telnet_label: recv(fd, buf, 1, 0); /* Read-out this octet */ continue; } } /** * command_line_character: * @line: The current command line. * @word_end: The cursor position. * @val: The last typed character. * * Have a peek in the last type character and perform any special actions * if needed. * * Return value: 1 to output this character, otherwise 0 **/ int command_line_character(const char *line, int word_end, uint8_t val) { if (val == '?') { fprintf(_fd_file_write, "%sThis is a help!%s", newline(), newline()); fflush(_fd_file_write); fprintf(_fd_file_write, "%s", CLI_PROMPT); fflush(_fd_file_write); fprintf(_fd_file_write, "%s", _command_buffer); fflush(_fd_file_write); return (0); } return (1); } xorp/cli/libtecla/libtecla.h0000664000076400007640000015120311421137511016134 0ustar greearbgreearb#ifndef libtecla_h #define libtecla_h /* * Copyright (c) 2000, 2001 by Martin C. Shepherd. * * All rights reserved. * * 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, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, and/or sell copies of the Software, and to permit persons * to whom the Software is furnished to do so, provided that the above * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Except as contained in this notice, the name of a copyright holder * shall not be used in advertising or otherwise to promote the sale, use * or other dealings in this Software without prior written authorization * of the copyright holder. */ #ifdef __cplusplus extern "C" { #endif #include /* FILE * */ #include /* size_t */ #include /* time_t */ /* * The following are the three components of the libtecla version number. * Note that it is better to use the libtecla_version() function than these * macros since the macros only tell you which version of the library your * code was compiled against, whereas the libtecla_version() function * tells you which version of the shared tecla library your program is * actually linked to. */ #define TECLA_MAJOR_VER 1 #define TECLA_MINOR_VER 4 #define TECLA_MICRO_VER 0 /*....................................................................... * Query the version number of the tecla library. * * Input: * major int * The major version number of the library * will be assigned to *major. This number is * only incremented when a change to the library is * made that breaks binary (shared library) and/or * compilation backwards compatibility. * minor int * The minor version number of the library * will be assigned to *minor. This number is * incremented whenever new functions are added to * the public API. * micro int * The micro version number of the library will be * assigned to *micro. This number is incremented * whenever internal changes are made that don't * change the public API, such as bug fixes and * performance enhancements. */ void libtecla_version(int *major, int *minor, int *micro); /*----------------------------------------------------------------------- * The getline module provides interactive command-line input, recall * and editing by users at terminals. See the gl_getline(3) man page for * more details. *-----------------------------------------------------------------------*/ /* * Provide an opaque handle for the resource object that is defined in * getline.h. */ typedef struct GetLine GetLine; /* * The following two functions are used to create and delete the * resource objects that are used by the gl_getline() function. */ GetLine *new_GetLine(size_t linelen, size_t histlen); GetLine *del_GetLine(GetLine *gl); /* * Read a line into an internal buffer of gl. */ char *gl_get_line(GetLine *gl, const char *prompt, const char *start_line, int start_pos); /* * Configure the application specific and/or user-specific behavior of * gl_get_line(). */ int gl_configure_getline(GetLine *gl, const char *app_string, const char *app_file, const char *user_file); /* * Read a line from the network into an internal buffer of gl. */ char *gl_get_line_net(GetLine *gl, const char *prompt, const char *start_line, int start_pos, int val); /* * Return the current position of the cursor within the internal buffer, * and the cursor position on the terminal. */ int gl_is_net(GetLine *gl); void gl_set_is_net(GetLine *gl, int is_net); int gl_get_buff_curpos(const GetLine *gl); int gl_place_cursor(GetLine *gl, int buff_curpos); int gl_redisplay_line(GetLine *gl); int gl_reset_line(GetLine *gl); int gl_get_user_event(GetLine *gl); void gl_reset_user_event(GetLine *gl); const char *gl_get_key_binding_action_name(GetLine *gl, const char *keyseq); /*----------------------------------------------------------------------- * The file-expansion module provides facilities for expanding ~user/ and * $envvar expressions, and for expanding glob-style wildcards. * See the ef_expand_file(3) man page for more details. *-----------------------------------------------------------------------*/ /* * ExpandFile objects contain the resources needed to expand pathnames. */ typedef struct ExpandFile ExpandFile; /* * The following functions are used to create and delete the resource * objects that are used by the ef_expand_file() function. */ ExpandFile *new_ExpandFile(void); ExpandFile *del_ExpandFile(ExpandFile *ef); /* * A container of the following type is returned by ef_expand_file(). */ typedef struct { int exists; /* True if the files in files[] currently exist. */ /* This only time that this may not be true is if */ /* the input filename didn't contain any wildcards */ /* and thus wasn't matched against existing files. */ /* In this case the single entry in 'nfile' may not */ /* refer to an existing file. */ int nfile; /* The number of files in files[] */ char **files; /* An array of 'nfile' filenames. */ } FileExpansion; /* * The ef_expand_file() function expands a specified pathname, converting * ~user/ and ~/ patterns at the start of the pathname to the * corresponding home directories, replacing $envvar with the value of * the corresponding environment variable, and then, if there are any * wildcards, matching these against existing filenames. * * If no errors occur, a container is returned containing the array of * files that resulted from the expansion. If there were no wildcards * in the input pathname, this will contain just the original pathname * after expansion of ~ and $ expressions. If there were any wildcards, * then the array will contain the files that matched them. Note that * if there were any wildcards but no existing files match them, this * is counted as an error and NULL is returned. * * The supported wildcards and their meanings are: * * - Match any sequence of zero or more characters. * ? - Match any single character. * [chars] - Match any single character that appears in 'chars'. * If 'chars' contains an expression of the form a-b, * then any character between a and b, including a and b, * matches. The '-' character looses its special meaning * as a range specifier when it appears at the start * of the sequence of characters. * [^chars] - The same as [chars] except that it matches any single * character that doesn't appear in 'chars'. * * Wildcard expressions are applied to individual filename components. * They don't match across directory separators. A '.' character at * the beginning of a filename component must also be matched * explicitly by a '.' character in the input pathname, since these * are UNIX's hidden files. * * Input: * fe ExpandFile * The pathname expansion resource object. * path const char * The path name to be expanded. * pathlen int The length of the suffix of path[] that * constitutes the filename to be expanded, * or -1 to specify that the whole of the * path string should be used. * Output: * return FileExpansion * A pointer to a results container within the * given ExpandFile object. This contains an * array of the pathnames that resulted from * expanding ~ and $ expressions and from * matching any wildcards, sorted into lexical * order. * * This container and its contents will be * recycled on subsequent calls, so if you need * to keep the results of two successive runs, * you will either have to allocate a private * copy of the array, or use two ExpandFile * objects. * * On error, NULL is returned. A description * of the error can be acquired by calling the * ef_last_error() function. */ FileExpansion *ef_expand_file(ExpandFile *ef, const char *path, int pathlen); /*....................................................................... * Print out an array of matching files. * * Input: * result FileExpansion * The container of the sorted array of * expansions. * fp FILE * The output stream to write to. * term_width int The width of the terminal. * Output: * return int 0 - OK. * 1 - Error. */ int ef_list_expansions(FileExpansion *result, FILE *fp, int term_width); /* * The ef_last_error() function returns a description of the last error * that occurred in a call ef_expand_file(). Note that this message is * contained in an array which is allocated as part of *ef, and its * contents thus potentially change on every call to ef_expand_file(). */ const char *ef_last_error(ExpandFile *ef); /*----------------------------------------------------------------------- * The WordCompletion module is used for completing incomplete words, such * as filenames. Programs can use functions within this module to register * their own customized completion functions. *-----------------------------------------------------------------------*/ /* * Ambiguous completion matches are recorded in objects of the * following type. */ typedef struct WordCompletion WordCompletion; /* * Create a new completion object. */ WordCompletion *new_WordCompletion(void); /* * Delete a redundant completion object. */ WordCompletion *del_WordCompletion(WordCompletion *cpl); /*....................................................................... * Callback functions declared and prototyped using the following macro * are called upon to return an array of possible completion suffixes * for the token that precedes a specified location in the given * input line. It is up to this function to figure out where the token * starts, and to call cpl_add_completion() to register each possible * completion before returning. * * Input: * cpl WordCompletion * An opaque pointer to the object that will * contain the matches. This should be filled * via zero or more calls to cpl_add_completion(). * data void * The anonymous 'data' argument that was * passed to cpl_complete_word() or * gl_customize_completion()). * line const char * The current input line. * word_end int The index of the character in line[] which * follows the end of the token that is being * completed. * Output * return int 0 - OK. * 1 - Error. */ #define CPL_MATCH_FN(fn) int (fn)(WordCompletion *cpl, void *data, \ const char *line, int word_end) typedef CPL_MATCH_FN(CplMatchFn); /*....................................................................... * Optional callback functions declared and prototyped using the * following macro are called upon to return non-zero if a given * file, specified by its pathname, is to be included in a list of * completions. * * Input: * data void * The application specified pointer which * was specified when this callback function * was registered. This can be used to have * anything you like passed to your callback. * pathname const char * The pathname of the file to be checked to * see if it should be included in the list * of completions. * Output * return int 0 - Ignore this file. * 1 - Do include this file in the list * of completions. */ #define CPL_CHECK_FN(fn) int (fn)(void *data, const char *pathname) typedef CPL_CHECK_FN(CplCheckFn); /* * You can use the following CplCheckFn callback function to only * have executables included in a list of completions. */ CPL_CHECK_FN(cpl_check_exe); /* * cpl_file_completions() is the builtin filename completion callback * function. This can also be called by your own custom CPL_MATCH_FN() * callback functions. To do this pass on all of the arguments of your * custom callback function to cpl_file_completions(), with the exception * of the (void *data) argument. The data argument should either be passed * NULL to request the default behaviour of the file-completion function, * or be passed a pointer to a CplFileConf structure (see below). In the * latter case the contents of the structure modify the behavior of the * file-completer. */ CPL_MATCH_FN(cpl_file_completions); /* * Objects of the following type can be used to change the default * behavior of the cpl_file_completions() callback function. */ typedef struct CplFileConf CplFileConf; /* * If you want to change the behavior of the cpl_file_completions() * callback function, call the following function to allocate a * configuration object, then call one or more of the subsequent * functions to change any of the default configuration parameters * that you don't want. This function returns NULL when there is * insufficient memory. */ CplFileConf *new_CplFileConf(void); /* * If backslashes in the prefix being passed to cpl_file_completions() * should be treated as literal characters, call the following function * with literal=1. Otherwise the default is to treat them as escape * characters which remove the special meanings of spaces etc.. */ void cfc_literal_escapes(CplFileConf *cfc, int literal); /* * Before calling cpl_file_completions(), call this function if you * know the index at which the filename prefix starts in the input line. * Otherwise by default, or if you specify start_index to be -1, the * filename is taken to start after the first unescaped space preceding * the cursor, or the start of the line, which ever comes first. */ void cfc_file_start(CplFileConf *cfc, int start_index); /* * If you only want certain types of files to be included in the * list of completions, use the following function to specify a * callback function which will be called to ask whether a given file * should be included. The chk_data argument is will be passed to the * callback function whenever it is called and can be anything you want. */ void cfc_set_check_fn(CplFileConf *cfc, CplCheckFn *chk_fn, void *chk_data); /* * The following function deletes a CplFileConf objects previously * returned by new_CplFileConf(). It always returns NULL. */ CplFileConf *del_CplFileConf(CplFileConf *cfc); /* * The following configuration structure is deprecated. Do not change * its contents, since this will break any programs that still use it, * and don't use it in new programs. Instead use opaque CplFileConf * objects as described above. cpl_file_completions() figures out * what type of structure you pass it, by virtue of a magic int code * placed at the start of CplFileConf object by new_CplFileConf(). */ typedef struct { int escaped; /* Opposite to the argument of cfc_literal_escapes() */ int file_start; /* Equivalent to the argument of cfc_file_start() */ } CplFileArgs; /* * This initializes the deprecated CplFileArgs structures. */ void cpl_init_FileArgs(CplFileArgs *cfa); /*....................................................................... * When an error occurs while performing a completion, custom completion * callback functions should register a terse description of the error * by calling cpl_record_error(). This message will then be returned on * the next call to cpl_last_error() and used by getline to display an * error message to the user. * * Input: * cpl WordCompletion * The string-completion resource object that was * originally passed to the callback. * errmsg const char * The description of the error. */ void cpl_record_error(WordCompletion *cpl, const char *errmsg); /*....................................................................... * This function can be used to replace the builtin filename-completion * function with one of the user's choice. The user's completion function * has the option of calling the builtin filename-completion function * if it believes that the token that it has been presented with is a * filename (see cpl_file_completions() above). * * Input: * gl GetLine * The resource object of the command-line input * module. * data void * This is passed to match_fn() whenever it is * called. It could, for example, point to a * symbol table that match_fn() would look up * matches in. * match_fn CplMatchFn * The function that will identify the prefix * to be completed from the input line, and * report matching symbols. * Output: * return int 0 - OK. * 1 - Error. */ int gl_customize_completion(GetLine *gl, void *data, CplMatchFn *match_fn); /*....................................................................... * Change the terminal (or stream) that getline interacts with. * * Input: * gl GetLine * The resource object of the command-line input * module. * input_fp FILE * The stdio stream to read from. * output_fp FILE * The stdio stream to write to. * term const char * The terminal type. This can be NULL if * either or both of input_fp and output_fp don't * refer to a terminal. Otherwise it should refer * to an entry in the terminal information database. * Output: * return int 0 - OK. * 1 - Error. */ int gl_change_terminal(GetLine *gl, FILE *input_fp, FILE *output_fp, const char *term); /*....................................................................... * The following functions can be used to save and restore the contents * of the history buffer. * * Input: * gl GetLine * The resource object of the command-line input * module. * filename const char * The name of the new file to write to. * comment const char * Extra information such as timestamps will * be recorded on a line started with this * string, the idea being that the file can * double as a command file. Specify "" if * you don't care. Be sure to specify the * same string to both functions. * max_lines int The maximum number of lines to save, or -1 * to save all of the lines in the history * list. * Output: * return int 0 - OK. * 1 - Error. */ int gl_save_history(GetLine *gl, const char *filename, const char *comment, int max_lines); int gl_load_history(GetLine *gl, const char *filename, const char *comment); /* * Enumerate file-descriptor events that can be waited for. */ typedef enum { GLFD_READ, /* Watch for data waiting to be read from a file descriptor */ GLFD_WRITE, /* Watch for ability to write to a file descriptor */ GLFD_URGENT /* Watch for urgent out-of-band data on the file descriptor */ } GlFdEvent; /* * The following enumeration is used for the return status of file * descriptor event callbacks. */ typedef enum { GLFD_ABORT, /* Cause gl_get_line() to abort with an error */ GLFD_REFRESH, /* Redraw the input line and continue waiting for input */ GLFD_CONTINUE /* Continue to wait for input, without redrawing the line */ } GlFdStatus; /*....................................................................... * While gl_get_line() is waiting for terminal input, it can also be * asked to listen for activity on arbitrary file descriptors. * Callback functions of the following type can be registered to be * called when activity is seen. If your callback needs to write to * the terminal or use signals, please see the gl_get_line(3) man * page. * * Input: * gl GetLine * The gl_get_line() resource object. You can use * this safely to call gl_watch_fd() or * gl_watch_time(). The effect of calling other * functions that take a gl argument is undefined, * and must be avoided. * data void * A pointer to arbitrary callback data, as originally * registered with gl_watch_fd(). * fd int The file descriptor that has activity. * event GlFdEvent The activity seen on the file descriptor. The * inclusion of this argument allows the same * callback to be registered for multiple events. * Output: * return GlFdStatus GLFD_ABORT - Cause gl_get_line() to abort with * an error (set errno if you need it). * GLFD_REFRESH - Redraw the input line and continue * waiting for input. Use this if you * wrote something to the terminal. * GLFD_CONTINUE - Continue to wait for input, without * redrawing the line. */ #define GL_FD_EVENT_FN(fn) GlFdStatus (fn)(GetLine *gl, void *data, int fd, \ GlFdEvent event) typedef GL_FD_EVENT_FN(GlFdEventFn); /*....................................................................... * Where possible, register a function and associated data to be called * whenever a specified event is seen on a file descriptor. * * Input: * gl GetLine * The resource object of the command-line input * module. * fd int The file descriptor to watch. * event GlFdEvent The type of activity to watch for. * callback GlFdEventFn * The function to call when the specified * event occurs. Setting this to 0 removes * any existing callback. * data void * A pointer to arbitrary data to pass to the * callback function. * Output: * return int 0 - OK. * 1 - Either gl==NULL, or this facility isn't * available on the the host system * (ie. select() isn't available). No * error message is generated in the latter * case. */ int gl_watch_fd(GetLine *gl, int fd, GlFdEvent event, GlFdEventFn *callback, void *data); /*....................................................................... * Switch history streams. History streams represent separate history * lists recorded within a single history buffer. Different streams * are distinguished by integer identifiers chosen by the calling * appplicaton. Initially new_GetLine() sets the stream identifier to * 0. Whenever a new line is appended to the history list, the current * stream identifier is recorded with it, and history lookups only * consider lines marked with the current stream identifier. * * Input: * gl GetLine * The resource object of gl_get_line(). * id unsigned The new history stream identifier. * Output: * return int 0 - OK. * 1 - Error. */ int gl_group_history(GetLine *gl, unsigned id); /*....................................................................... * Display the contents of the history list. * * Input: * gl GetLine * The resource object of gl_get_line(). * fp FILE * The stdio output stream to write to. * fmt const char * A format string. This containing characters to be * written verbatim, plus any of the following * format directives: * %D - The date, formatted like 2001-11-20 * %T - The time of day, formatted like 23:59:59 * %N - The sequential entry number of the * line in the history buffer. * %G - The number of the history group that * the line belongs to. * %% - A literal % character. * %H - The history line itself. * Note that a '\n' newline character is not * appended by default. * all_groups int If true, display history lines from all * history groups. Otherwise only display * those of the current history group. * max_lines int If max_lines is < 0, all available lines * are displayed. Otherwise only the most * recent max_lines lines will be displayed. * Output: * return int 0 - OK. * 1 - Error. */ int gl_show_history(GetLine *gl, FILE *fp, const char *fmt, int all_groups, int max_lines); /*....................................................................... * Resize or delete the history buffer. * * Input: * gl GetLine * The resource object of gl_get_line(). * bufsize size_t The number of bytes in the history buffer, or 0 * to delete the buffer completely. * Output: * return int 0 - OK. * 1 - Insufficient memory (the previous buffer * will have been retained). No error message * will be displayed. */ int gl_resize_history(GetLine *gl, size_t bufsize); /*....................................................................... * Set an upper limit to the number of lines that can be recorded in the * history list, or remove a previously specified limit. * * Input: * gl GetLine * The resource object of gl_get_line(). * max_lines int The maximum number of lines to allow, or -1 to * cancel a previous limit and allow as many lines * as will fit in the current history buffer size. */ void gl_limit_history(GetLine *gl, int max_lines); /*....................................................................... * Discard either all historical lines, or just those associated with the * current history group. * * Input: * gl GetLine * The resource object of gl_get_line(). * all_groups int If true, clear all of the history. If false, * clear only the stored lines associated with the * currently selected history group. */ void gl_clear_history(GetLine *gl, int all_groups); /*....................................................................... * Temporarily enable or disable the gl_get_line() history mechanism. * * Input: * gl GetLine * The resource object of gl_get_line(). * enable int If true, turn on the history mechanism. If * false, disable it. */ void gl_toggle_history(GetLine *gl, int enable); /* * Objects of the following type are returned by gl_terminal_size(). */ typedef struct { int nline; /* The terminal has nline lines */ int ncolumn; /* The terminal has ncolumn columns */ } GlTerminalSize; /*....................................................................... * Update if necessary, and return the current size of the terminal. * * Input: * gl GetLine * The resource object of gl_get_line(). * def_ncolumn int If the number of columns in the terminal * can't be determined, substitute this number. * def_nline int If the number of lines in the terminal can't * be determined, substitute this number. * Output: * return GlTerminalSize The current terminal size. */ GlTerminalSize gl_terminal_size(GetLine *gl, int def_ncolumn, int def_nline); /* * The gl_lookup_history() function returns information in an * argument of the following type. */ typedef struct { const char *line; /* The requested history line */ unsigned group; /* The history group to which the */ /* line belongs. */ time_t timestamp; /* The date and time at which the */ /* line was originally entered. */ } GlHistoryLine; /*....................................................................... * Lookup a history line by its sequential number of entry in the * history buffer. * * Input: * gl GetLine * The resource object of gl_get_line(). * id unsigned long The identification number of the line to * be returned, where 0 denotes the first line * that was entered in the history list, and * each subsequently added line has a number * one greater than the previous one. For * the range of lines currently in the list, * see the gl_range_of_history() function. * Input/Output: * line GlHistoryLine * A pointer to the variable in which to * return the details of the line. * Output: * return int 0 - The line is no longer in the history * list, and *line has not been changed. * 1 - The requested line can be found in * *line. Note that the string in * line->line is part of the history * buffer and will change, so a private * copy should be made if you wish to * use it after subsequent calls to any * functions that take gl as an argument. */ int gl_lookup_history(GetLine *gl, unsigned long id, GlHistoryLine *line); /* * The gl_state_of_history() function returns information in an argument * of the following type. */ typedef struct { int enabled; /* True if history is enabled */ unsigned group; /* The current history group */ int max_lines; /* The current upper limit on the number of lines */ /* in the history list, or -1 if unlimited. */ } GlHistoryState; /*....................................................................... * Query the state of the history list. Note that any of the input/output * pointers can be specified as NULL. * * Input: * gl GetLine * The resource object of gl_get_line(). * Input/Output: * state GlHistoryState * A pointer to the variable in which to record * the return values. */ void gl_state_of_history(GetLine *gl, GlHistoryState *state); /* * The gl_range_of_history() function returns information in an argument * of the following type. */ typedef struct { unsigned long oldest; /* The sequential entry number of the oldest */ /* line in the history list. */ unsigned long newest; /* The sequential entry number of the newest */ /* line in the history list. */ int nlines; /* The number of lines in the history list */ } GlHistoryRange; /*....................................................................... * Query the number and range of lines in the history buffer. * * Input: * gl GetLine * The resource object of gl_get_line(). * range GlHistoryRange * A pointer to the variable in which to record * the return values. If range->nline=0, the * range of lines will be given as 0-0. */ void gl_range_of_history(GetLine *gl, GlHistoryRange *range); /* * The gl_size_of_history() function returns information in an argument * of the following type. */ typedef struct { size_t size; /* The size of the history buffer (bytes) */ size_t used; /* The number of bytes of the history buffer */ /* that are currently occupied. */ } GlHistorySize; /*....................................................................... * Return the size of the history buffer and the amount of the * buffer that is currently in use. * * Input: * gl GetLine * The resource object of gl_get_line(). * Input/Output: * GlHistorySize size * A pointer to the variable in which to return * the results. */ void gl_size_of_history(GetLine *gl, GlHistorySize *size); /*....................................................................... * Specify whether text that users type should be displayed or hidden. * In the latter case, only the prompt is displayed, and the final * input line is not archived in the history list. * * Input: * gl GetLine * The input-line history maintenance object. * enable int 0 - Disable echoing. * 1 - Enable echoing. * -1 - Just query the mode without changing it. * Output: * return int The echoing disposition that was in effect * before this function was called: * 0 - Echoing was disabled. * 1 - Echoing was enabled. */ int gl_echo_mode(GetLine *gl, int enable); /*....................................................................... * This function can be called from gl_get_line() callbacks to have * the prompt changed when they return. It has no effect if gl_get_line() * is not currently being invoked. * * Input: * gl GetLine * The resource object of gl_get_line(). * prompt const char * The new prompt. */ void gl_replace_prompt(GetLine *gl, const char *prompt); /* * Enumerate the available prompt formatting styles. */ typedef enum { GL_LITERAL_PROMPT, /* Display the prompt string literally */ GL_FORMAT_PROMPT /* The prompt string can contain any of the */ /* following formatting directives: */ /* %B - Display subsequent characters */ /* with a bold font. */ /* %b - Stop displaying characters */ /* with the bold font. */ /* %U - Underline subsequent characters. */ /* %u - Stop underlining characters. */ /* %S - Highlight subsequent characters */ /* (also known as standout mode). */ /* %s - Stop highlighting characters */ /* %% - Display a single % character. */ } GlPromptStyle; /*....................................................................... * Specify whether to heed text attribute directives within prompt * strings. * * Input: * gl GetLine * The resource object of gl_get_line(). * style GlPromptStyle The style of prompt (see the definition of * GlPromptStyle in libtecla.h for details). */ void gl_prompt_style(GetLine *gl, GlPromptStyle style); /*....................................................................... * Remove a signal from the list of signals that gl_get_line() traps. * * Input: * gl GetLine * The resource object of gl_get_line(). * signo int The number of the signal to be ignored. * Output: * return int 0 - OK. * 1 - Error. */ int gl_ignore_signal(GetLine *gl, int signo); /* * A bitwise union of the following enumerators is passed to * gl_trap_signal() to specify the environment in which the * application's signal handler is to be called. */ typedef enum { GLS_RESTORE_SIG=1, /* Restore the caller's signal environment */ /* while handling the signal. */ GLS_RESTORE_TTY=2, /* Restore the caller's terminal settings */ /* while handling the signal. */ GLS_RESTORE_LINE=4, /* Move the cursor to the start of the next line */ GLS_REDRAW_LINE=8, /* Redraw the input line when the signal handler */ /* returns. */ GLS_UNBLOCK_SIG=16, /* Normally a signal who's delivery is found to */ /* be blocked by the calling application is not */ /* trapped by gl_get_line(). Including this flag */ /* causes it to be temporarily unblocked and */ /* trapped while gl_get_line() is executing. */ GLS_DONT_FORWARD=32,/* Don't forward the signal to the signal handler */ /* of the calling program. */ GLS_RESTORE_ENV = GLS_RESTORE_SIG | GLS_RESTORE_TTY | GLS_REDRAW_LINE, GLS_SUSPEND_INPUT = GLS_RESTORE_ENV | GLS_RESTORE_LINE } GlSignalFlags; /* * The following enumerators are passed to gl_trap_signal() to tell * it what to do after the application's signal handler has been called. */ typedef enum { GLS_RETURN, /* Return the line as though the user had pressed the */ /* return key. */ GLS_ABORT, /* Cause gl_get_line() to return NULL */ GLS_CONTINUE /* After handling the signal, resume command line editing */ } GlAfterSignal; /*....................................................................... * Tell gl_get_line() how to respond to a given signal. This can be used * both to override the default responses to signals that gl_get_line() * normally catches and to add new signals to the list that are to be * caught. * * Input: * gl GetLine * The resource object of gl_get_line(). * signo int The number of the signal to be caught. * flags unsigned A bitwise union of GlSignalFlags enumerators. * after GlAfterSignal What to do after the application's signal * handler has been called. * errno_value int The value to set errno to. * Output: * return int 0 - OK. * 1 - Insufficient memory to record the * new signal disposition. */ int gl_trap_signal(GetLine *gl, int signo, unsigned flags, GlAfterSignal after, int errno_value); /*....................................................................... * Return the last signal that was caught by the most recent call to * gl_get_line(), or -1 if no signals were caught. This is useful if * gl_get_line() returns errno=EINTR and you need to find out what signal * caused it to abort. * * Input: * gl GetLine * The resource object of gl_get_line(). * Output: * return int The last signal caught by the most recent * call to gl_get_line(), or -1 if no signals * were caught. */ int gl_last_signal(const GetLine *gl); /*....................................................................... * This function is designed to be called by CPL_MATCH_FN() callback * functions. It adds one possible completion of the token that is being * completed to an array of completions. If the completion needs any * special quoting to be valid when displayed in the input line, this * quoting must be included in the string. * * Input: * cpl WordCompletion * The argument of the same name that was passed * to the calling CPL_MATCH_FN() callback function. * line const char * The input line, as received by the callback * function. * word_start int The index within line[] of the start of the * word that is being completed. If an empty * string is being completed, set this to be * the same as word_end. * word_end int The index within line[] of the character which * follows the incomplete word, as received by the * callback function. * suffix const char * The appropriately quoted string that could * be appended to the incomplete token to complete * it. A copy of this string will be allocated * internally. * type_suffix const char * When listing multiple completions, gl_get_line() * appends this string to the completion to indicate * its type to the user. If not pertinent pass "". * Otherwise pass a literal or static string. * cont_suffix const char * If this turns out to be the only completion, * gl_get_line() will append this string as * a continuation. For example, the builtin * file-completion callback registers a directory * separator here for directory matches, and a * space otherwise. If the match were a function * name you might want to append an open * parenthesis, etc.. If not relevant pass "". * Otherwise pass a literal or static string. * Output: * return int 0 - OK. * 1 - Error. */ int cpl_add_completion(WordCompletion *cpl, const char *line, int word_start, int word_end, const char *suffix, const char *type_suffix, const char *cont_suffix); /* * Each possible completion string is recorded in an array element of * the following type. */ typedef struct { char *completion; /* The matching completion string */ char *suffix; /* The pointer into completion[] at which the */ /* string was extended. */ const char *type_suffix; /* A suffix to be added when listing completions */ /* to indicate the type of the completion. */ } CplMatch; /* * Completions are returned in a container of the following form. */ typedef struct { char *suffix; /* The common initial part of all of the */ /* completion suffixes. */ const char *cont_suffix; /* Optional continuation string to be appended to */ /* the sole completion when nmatch==1. */ CplMatch *matches; /* The array of possible completion strings, */ /* sorted into lexical order. */ int nmatch; /* The number of elements in matches[] */ } CplMatches; /*....................................................................... * Given an input line and the point at which completion is to be * attempted, return an array of possible completions. * * Input: * cpl WordCompletion * The word-completion resource object. * line const char * The current input line. * word_end int The index of the character in line[] which * follows the end of the token that is being * completed. * data void * Anonymous 'data' to be passed to match_fn(). * match_fn CplMatchFn * The function that will identify the prefix * to be completed from the input line, and * record completion suffixes. * Output: * return CplMatches * The container of the array of possible * completions. The returned pointer refers * to a container owned by the parent Completion * object, and its contents thus potentially * change on every call to cpl_matches(). */ CplMatches *cpl_complete_word(WordCompletion *cpl, const char *line, int word_end, void *data, CplMatchFn *match_fn); /*....................................................................... * Print out an array of matching completions. * * Input: * result CplMatches * The container of the sorted array of * completions. * fp FILE * The output stream to write to. * term_width int The width of the terminal. * Output: * return int 0 - OK. * 1 - Error. */ int cpl_list_completions(CplMatches *result, FILE *fp, int term_width); /*....................................................................... * Return a description of the error that occurred on the last call to * cpl_complete_word() or cpl_add_completion(). * * Input: * cpl WordCompletion * The string-completion resource object. * Output: * return const char * The description of the last error. */ const char *cpl_last_error(WordCompletion *cpl); /* * PathCache objects encapsulate the resources needed to record * files of interest from comma-separated lists of directories. */ typedef struct PathCache PathCache; /*....................................................................... * Create an object who's function is to maintain a cache of filenames * found within a list of directories, and provide quick lookup and * completion of selected files in this cache. * * Output: * return PathCache * The new, initially empty cache, or NULL * on error. */ PathCache *new_PathCache(void); /*....................................................................... * Delete a given cache of files, returning the resources that it * was using to the system. * * Input: * pc PathCache * The cache to be deleted (can be NULL). * Output: * return PathCache * The deleted object (ie. allways NULL). */ PathCache *del_PathCache(PathCache *pc); /*....................................................................... * Return a description of the last path-caching error that occurred. * * Input: * pc PathCache * The filename cache that suffered the error. * Output: * return char * The description of the last error. */ const char *pca_last_error(PathCache *pc); /*....................................................................... * Build the list of files of interest contained in a given * colon-separated list of directories. * * Input: * pc PathCache * The cache in which to store the names of * the files that are found in the list of * directories. * path const char * A colon-separated list of directory * paths. Under UNIX, when searching for * executables, this should be the return * value of getenv("PATH"). * Output: * return int 0 - OK. * 1 - An error occurred. */ int pca_scan_path(PathCache *pc, const char *path); /*....................................................................... * If you want subsequent calls to pca_lookup_file() and * pca_path_completions() to only return the filenames of certain * types of files, for example executables, or filenames ending in * ".ps", call this function to register a file-selection callback * function. This callback function takes the full pathname of a file, * plus application-specific data, and returns 1 if the file is of * interest, and zero otherwise. * * Input: * pc PathCache * The filename cache. * check_fn CplCheckFn * The function to call to see if the name of * a given file should be included in the * cache. This determines what type of files * will reside in the cache. To revert to * selecting all files, regardless of type, * pass 0 here. * data void * You can pass a pointer to anything you * like here, including NULL. It will be * passed to your check_fn() callback * function, for its private use. */ void pca_set_check_fn(PathCache *pc, CplCheckFn *check_fn, void *data); /*....................................................................... * Given the simple name of a file, search the cached list of files * in the order in which they where found in the list of directories * previously presented to pca_scan_path(), and return the pathname * of the first file which has this name. * * Input: * pc PathCache * The cached list of files. * name const char * The name of the file to lookup. * name_len int The length of the filename substring at the * beginning of name[], or -1 to assume that the * filename occupies the whole of the string. * literal int If this argument is zero, lone backslashes * in name[] are ignored during comparison * with filenames in the cache, under the * assumption that they were in the input line * soley to escape the special significance of * characters like spaces. To have them treated * as normal characters, give this argument a * non-zero value, such as 1. * Output: * return char * The pathname of the first matching file, * or NULL if not found. Note that the returned * pointer points to memory owned by *pc, and * will become invalid on the next call. */ char *pca_lookup_file(PathCache *pc, const char *name, int name_len, int literal); /* * Objects of the following type can be used to change the default * behavior of the pca_path_completions() callback function. */ typedef struct PcaPathConf PcaPathConf; /* * pca_path_completions() is a completion callback function for use directly * with cpl_complete_word() or gl_customize_completions(), or indirectly * from your own completion callback function. It requires that a PcaPathConf * object be passed via its 'void *data' argument (see below). */ CPL_MATCH_FN(pca_path_completions); /*....................................................................... * Allocate and initialize a pca_path_completions() configuration object. * * Input: * pc PathCache * The filename cache in which to look for * file name completions. * Output: * return PcaPathConf * The new configuration structure, or NULL * on error. */ PcaPathConf *new_PcaPathConf(PathCache *pc); /*....................................................................... * Deallocate memory, previously allocated by new_PcaPathConf(). * * Input: * ppc PcaPathConf * Any pointer previously returned by * new_PcaPathConf() [NULL is allowed]. * Output: * return PcaPathConf * The deleted structure (always NULL). */ PcaPathConf *del_PcaPathConf(PcaPathConf *ppc); /* * If backslashes in the prefix being passed to pca_path_completions() * should be treated as literal characters, call the following function * with literal=1. Otherwise the default is to treat them as escape * characters which remove the special meanings of spaces etc.. */ void ppc_literal_escapes(PcaPathConf *ppc, int literal); /* * Before calling pca_path_completions, call this function if you know * the index at which the filename prefix starts in the input line. * Otherwise by default, or if you specify start_index to be -1, the * filename is taken to start after the first unescaped space preceding * the cursor, or the start of the line, whichever comes first. */ void ppc_file_start(PcaPathConf *ppc, int start_index); #ifdef __cplusplus } #endif #endif xorp/cli/libtecla/freelist.h0000664000076400007640000000602711421137511016175 0ustar greearbgreearb#ifndef freelist_h #define freelist_h /* * Copyright (c) 2000, 2001 by Martin C. Shepherd. * * All rights reserved. * * 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, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, and/or sell copies of the Software, and to permit persons * to whom the Software is furnished to do so, provided that the above * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Except as contained in this notice, the name of a copyright holder * shall not be used in advertising or otherwise to promote the sale, use * or other dealings in this Software without prior written authorization * of the copyright holder. */ /* * This module provides a memory allocation scheme that helps to * prevent memory fragmentation by allocating large blocks of * fixed sized objects and forming them into a free-list for * subsequent allocations. The free-list is expanded as needed. */ typedef struct FreeList FreeList; /* * Allocate a new free-list from blocks of 'blocking_factor' objects of size * node_size. The node_size argument should be determined by applying * the sizeof() operator to the object type that you intend to allocate from * the freelist. */ FreeList *_new_FreeList(const char *caller, size_t node_size, unsigned blocking_factor); /* * If it is known that none of the nodes currently allocated from * a freelist are still in use, the following function can be called * to return all nodes to the freelist without the overhead of * having to call del_FreeListNode() for every allocated node. The * nodes of the freelist can then be reused by future callers to * new_FreeListNode(). */ void _rst_FreeList(FreeList *fl); /* * Delete a free-list. */ FreeList *_del_FreeList(const char *caller, FreeList *fl, int force); /* * Determine the number of nodes that are currently allocated. */ long _busy_FreeListNodes(FreeList *fl); /* * Allocate a new object from a free-list. */ void *_new_FreeListNode(FreeList *fl); /* * Return an object to the free-list that it was allocated from. */ void *_del_FreeListNode(FreeList *fl, void *object); #endif xorp/cli/libtecla/history.c0000664000076400007640000016403211421137511016055 0ustar greearbgreearb/* * Copyright (c) 2000, 2001 by Martin C. Shepherd. * * All rights reserved. * * 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, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, and/or sell copies of the Software, and to permit persons * to whom the Software is furnished to do so, provided that the above * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Except as contained in this notice, the name of a copyright holder * shall not be used in advertising or otherwise to promote the sale, use * or other dealings in this Software without prior written authorization * of the copyright holder. */ #include #include #include #include #include #include #include "history.h" #include "freelist.h" /* * GlLineNode's record the location and length of historical lines in * a buffer array. */ typedef struct GlLineNode GlLineNode; struct GlLineNode { long id; /* The unique identifier of this history line */ time_t timestamp; /* The time at which the line was archived */ unsigned group; /* The identifier of the history group to which the */ /* the line belongs. */ GlLineNode *next; /* The next youngest line in the list */ GlLineNode *prev; /* The next oldest line in the list */ int start; /* The start index of the line in the buffer */ int nchar; /* The total length of the line, including the '\0' */ }; /* * The number of GlLineNode elements per freelist block. */ #define LINE_NODE_BLK 100 /* * Lines are organised in the buffer from oldest to newest. The * positions of the lines are recorded in a doubly linked list * of GlLineNode objects. */ typedef struct { FreeList *node_mem; /* A freelist of GlLineNode objects */ GlLineNode *head; /* The head of the list of lines */ GlLineNode *tail; /* The tail of the list of lines */ } GlLineList; /* * All elements of the history mechanism are recorded in an object of * the following type. */ struct GlHistory { char *buffer; /* A circular buffer used to record historical input */ /* lines. */ size_t buflen; /* The length of the buffer array */ GlLineList list; /* A list of the start of lines in buffer[] */ GlLineNode *recall; /* The last line recalled, or NULL if no recall */ /* session is currently active. */ GlLineNode *id_node;/* The node at which the last ID search terminated */ const char *prefix; /* A pointer to the line containing the prefix that */ /* is being searched for. */ int prefix_len; /* The length of the prefix */ unsigned long seq; /* The next ID to assign to a line node */ unsigned group; /* The identifier of the current history group */ int nline; /* The number of lines currently in the history list */ int max_lines; /* Either -1 or a ceiling on the number of lines */ int enable; /* If false, ignore history additions and lookups */ }; static char *_glh_restore_line(GlHistory *glh, char *line, size_t dim); static int _glh_cant_load_history(GlHistory *glh, const char *filename, int lineno, const char *message, FILE *fp); static int _glh_write_timestamp(FILE *fp, time_t timestamp); static int _glh_decode_timestamp(char *string, char **endp, time_t *t); static void _glh_discard_node(GlHistory *glh, GlLineNode *node); static GlLineNode *_glh_find_id(GlHistory *glh, GlhLineID id); /*....................................................................... * Create a line history maintenance object. * * Input: * buflen size_t The number of bytes to allocate to the circular * buffer that is used to record all of the * most recent lines of user input that will fit. * If buflen==0, no buffer will be allocated. * Output: * return GlHistory * The new object, or NULL on error. */ GlHistory *_new_GlHistory(size_t buflen) { GlHistory *glh; /* The object to be returned */ /* * Allocate the container. */ glh = (GlHistory *) malloc(sizeof(GlHistory)); if(!glh) { fprintf(stderr, "_new_GlHistory: Insufficient memory.\n"); return NULL; }; /* * Before attempting any operation that might fail, initialize the * container at least up to the point at which it can safely be passed * to _del_GlHistory(). */ glh->buffer = NULL; glh->buflen = buflen; glh->list.node_mem = NULL; glh->list.head = NULL; glh->list.tail = NULL; glh->recall = NULL; glh->id_node = NULL; glh->prefix = NULL; glh->prefix_len = 0; glh->seq = 0; glh->group = 0; glh->nline = 0; glh->max_lines = -1; glh->enable = 1; /* * Allocate the buffer, if required. */ if(buflen > 0) { glh->buffer = (char *) malloc(sizeof(char) * buflen); if(!glh->buffer) { fprintf(stderr, "_new_GlHistory: Insufficient memory.\n"); return _del_GlHistory(glh); }; }; /* * Allocate the GlLineNode freelist. */ glh->list.node_mem = _new_FreeList("_new_GlHistory", sizeof(GlLineNode), LINE_NODE_BLK); if(!glh->list.node_mem) return _del_GlHistory(glh); return glh; } /*....................................................................... * Delete a GlHistory object. * * Input: * glh GlHistory * The object to be deleted. * Output: * return GlHistory * The deleted object (always NULL). */ GlHistory *_del_GlHistory(GlHistory *glh) { if(glh) { /* * Delete the buffer. */ if(glh->buffer) { free(glh->buffer); glh->buffer = NULL; }; /* * Delete the freelist of GlLineNode's. */ glh->list.node_mem = _del_FreeList("_del_GlHistory", glh->list.node_mem, 1); /* * The contents of the list were deleted by deleting the freelist. */ glh->list.head = NULL; glh->list.tail = NULL; /* * Delete the container. */ free(glh); }; return NULL; } /*....................................................................... * Add a new line to the end of the history buffer, wrapping round to the * start of the buffer if needed. * * Input: * glh GlHistory * The input-line history maintenance object. * line char * The line to be archived. * force int Unless this flag is non-zero, empty lines and * lines which match the previous line in the history * buffer, aren't archived. This flag requests that * the line be archived regardless. * Output: * return int 0 - OK. * 1 - Error. */ int _glh_add_history(GlHistory *glh, const char *line, int force) { GlLineList *list; /* The line location list */ int nchar; /* The number of characters needed to record the line */ GlLineNode *node; /* The new line location list node */ int empty; /* True if the string is empty */ const char *nlptr;/* A pointer to a newline character in line[] */ int i; /* * Check the arguments. */ if(!glh || !line) return 1; /* * Is history enabled? */ if(!glh->enable || !glh->buffer || glh->max_lines == 0) return 0; /* * Get the line location list. */ list = &glh->list; /* * Cancel any ongoing search. */ if(_glh_cancel_search(glh)) return 1; /* * See how much buffer space will be needed to record the line? * * If the string contains a terminating newline character, arrange to * have the archived line NUL terminated at this point. */ nlptr = strchr(line, '\n'); if(nlptr) nchar = (nlptr - line) + 1; else nchar = strlen(line) + 1; /* * If the line is too big to fit in the buffer, truncate it. */ if(nchar > glh->buflen) nchar = glh->buflen; /* * Is the line empty? */ empty = 1; for(i=0; itail && strlen(glh->buffer + list->tail->start) == nchar-1 && strncmp(line, glh->buffer + list->tail->start, nchar-1)==0) return 0; /* * Allocate the list node that will record the line location. */ node = (GlLineNode *) _new_FreeListNode(list->node_mem); if(!node) return 1; /* * Is the buffer empty? */ if(!list->head) { /* * Place the line at the beginning of the buffer. */ strncpy(glh->buffer, line, nchar); glh->buffer[nchar-1] = '\0'; /* * Record the location of the line. */ node->start = 0; /* * The buffer has one or more lines in it. */ } else { /* * Place the start of the new line just after the most recently * added line. */ int start = list->tail->start + list->tail->nchar; /* * If there is insufficient room between the end of the most * recently added line and the end of the buffer, we place the * line at the beginning of the buffer. To make as much space * as possible for this line, we first delete any old lines * at the end of the buffer, then shift the remaining contents * of the buffer to the end of the buffer. */ if(start + nchar >= glh->buflen) { GlLineNode *last; /* The last line in the buffer */ GlLineNode *ln; /* A member of the list of line locations */ int shift; /* The shift needed to move the contents of the */ /* buffer to its end. */ /* * Delete any old lines between the most recent line and the end of the * buffer. */ while(list->head && list->head->start > list->tail->start) _glh_discard_node(glh, list->head); /* * Find the line that is nearest the end of the buffer. */ last = NULL; for(ln=list->head; ln; ln=ln->next) { if(!last || ln->start > last->start) last = ln; }; /* * How big a shift is needed to move the existing contents of the * buffer to the end of the buffer? */ shift = last ? (glh->buflen - (last->start + last->nchar)) : 0; /* * Is any shift needed? */ if(shift > 0) { /* * Move the buffer contents to the end of the buffer. */ memmove(glh->buffer + shift, glh->buffer, glh->buflen - shift); /* * Update the listed locations to reflect the shift. */ for(ln=list->head; ln; ln=ln->next) ln->start += shift; }; /* * The new line should now be located at the start of the buffer. */ start = 0; }; /* * Make space for the new line at the beginning of the buffer by * deleting the oldest lines. This just involves removing them * from the list of used locations. Also enforce the current * maximum number of lines. */ while(list->head && ((list->head->start >= start && list->head->start - start < nchar) || (glh->max_lines >= 0 && glh->nline>=glh->max_lines))) { _glh_discard_node(glh, list->head); }; /* * Copy the new line into the buffer. */ memcpy(glh->buffer + start, line, nchar); glh->buffer[start + nchar - 1] = '\0'; /* * Record its location. */ node->start = start; }; /* * Append the line location node to the end of the list. */ node->id = glh->seq++; node->timestamp = time(NULL); node->group = glh->group; node->nchar = nchar; node->next = NULL; node->prev = list->tail; if(list->tail) list->tail->next = node; else list->head = node; list->tail = node; glh->nline++; return 0; } /*....................................................................... * Recall the next oldest line that has the search prefix last recorded * by _glh_search_prefix(). * * Input: * glh GlHistory * The input-line history maintenance object. * line char * The input line buffer. On input this should contain * the current input line, and on output, if anything * was found, its contents will have been replaced * with the matching line. * dim size_t The allocated dimensions of the line buffer. * Output: * return char * A pointer to line[0], or NULL if not found. */ char *_glh_find_backwards(GlHistory *glh, char *line, size_t dim) { GlLineNode *node; /* The line location node being checked */ int first; /* True if this is the start of a new search */ /* * Check the arguments. */ if(!glh || !line) { fprintf(stderr, "_glh_find_backwards: NULL argument(s).\n"); return NULL; }; /* * Is history enabled? */ if(!glh->enable || !glh->buffer || glh->max_lines == 0) return NULL; /* * Check the line dimensions. */ if(dim < strlen(line) + 1) { fprintf(stderr, "_glh_find_backwards: 'dim' inconsistent with strlen(line) contents.\n"); return NULL; }; /* * Is this the start of a new search? */ first = glh->recall==NULL; /* * If this is the first search backwards, save the current line * for potential recall later, and mark it as the last line * recalled. */ if(first) { if(_glh_add_history(glh, line, 1)) return NULL; glh->recall = glh->list.tail; }; /* * If there is no search prefix, the prefix last set by glh_search_prefix() * doesn't exist in the history buffer. */ if(!glh->prefix) return NULL; /* * From where should we start the search? */ if(glh->recall) node = glh->recall->prev; else node = glh->list.tail; /* * Search backwards through the list for the first match with the * prefix string. */ for( ; node && (node->group != glh->group || strncmp(glh->buffer + node->start, glh->prefix, glh->prefix_len) != 0); node = node->prev) ; /* * Was a matching line found? */ if(node) { /* * Recall the found node as the starting point for subsequent * searches. */ glh->recall = node; /* * Copy the matching line into the provided line buffer. */ strncpy(line, glh->buffer + node->start, dim); line[dim-1] = '\0'; return line; }; /* * No match was found. */ return NULL; } /*....................................................................... * Recall the next newest line that has the search prefix last recorded * by _glh_search_prefix(). * * Input: * glh GlHistory * The input-line history maintenance object. * line char * The input line buffer. On input this should contain * the current input line, and on output, if anything * was found, its contents will have been replaced * with the matching line. * dim size_t The allocated dimensions of the line buffer. * Output: * return char * The line requested, or NULL if no matching line * was found. */ char *_glh_find_forwards(GlHistory *glh, char *line, size_t dim) { GlLineNode *node; /* The line location node being checked */ /* * Check the arguments. */ if(!glh || !line) { fprintf(stderr, "_glh_find_forwards: NULL argument(s).\n"); return NULL; }; /* * Is history enabled? */ if(!glh->enable || !glh->buffer || glh->max_lines == 0) return NULL; /* * Check the line dimensions. */ if(dim < strlen(line) + 1) { fprintf(stderr, "_glh_find_forwards: 'dim' inconsistent with strlen(line) contents.\n"); return NULL; }; /* * From where should we start the search? */ if(glh->recall) node = glh->recall->next; else return NULL; /* * If there is no search prefix, the prefix last set by glh_search_prefix() * doesn't exist in the history buffer. */ if(!glh->prefix) return NULL; /* * Search forwards through the list for the first match with the * prefix string. */ for( ; node && (node->group != glh->group || strncmp(glh->buffer + node->start, glh->prefix, glh->prefix_len) != 0); node = node->next) ; /* * Was a matching line found? */ if(node) { /* * Did we hit the line that was originally being edited when the * current history traversal started? */ if(node == glh->list.tail) return _glh_restore_line(glh, line, dim); /* * Copy the matching line into the provided line buffer. */ strncpy(line, glh->buffer + node->start, dim); line[dim-1] = '\0'; /* * Record the starting point of the next search. */ glh->recall = node; /* * Return the matching line to the user. */ return line; }; /* * No match was found. */ return NULL; } /*....................................................................... * If a search is in progress, cancel it. * * This involves discarding the line that was temporarily saved by * _glh_find_backwards() when the search was originally started, * and reseting the search iteration pointer to NULL. * * Input: * glh GlHistory * The input-line history maintenance object. * Output: * return int 0 - OK. * 1 - Error. */ int _glh_cancel_search(GlHistory *glh) { /* * Check the arguments. */ if(!glh) { fprintf(stderr, "_glh_cancel_search: NULL argument(s).\n"); return 1; }; /* * If there wasn't a search in progress, do nothing. */ if(!glh->recall) return 0; /* * Delete the node of the preserved line. */ _glh_discard_node(glh, glh->list.tail); /* * Reset the search pointers. */ glh->recall = NULL; glh->prefix = ""; glh->prefix_len = 0; return 0; } /*....................................................................... * Set the prefix of subsequent history searches. * * Input: * glh GlHistory * The input-line history maintenance object. * line char * The command line who's prefix is to be used. * prefix_len int The length of the prefix. * Output: * return int 0 - OK. * 1 - Error. */ int _glh_search_prefix(GlHistory *glh, const char *line, int prefix_len) { GlLineNode *node; /* The line location node being checked */ /* * Check the arguments. */ if(!glh) { fprintf(stderr, "_glh_search_prefix: NULL argument(s).\n"); return 1; }; /* * Is history enabled? */ if(!glh->enable || !glh->buffer || glh->max_lines == 0) return 0; /* * Record a zero length search prefix? */ if(prefix_len <= 0) { glh->prefix_len = 0; glh->prefix = ""; return 0; }; /* * Record the length of the new search prefix. */ glh->prefix_len = prefix_len; /* * If any history line starts with the specified prefix, record a * pointer to it for comparison in subsequent searches. If the prefix * doesn't match any of the lines, then simply record NULL to indicate * that there is no point in searching. Note that _glh_add_history() * clears this pointer by calling _glh_cancel_search(), so there is * no danger of it being used after the buffer has been modified. */ for(node = glh->list.tail ; node && (node->group != glh->group || strncmp(glh->buffer + node->start, line, prefix_len) != 0); node = node->prev) ; /* * If a matching line was found record it for use as the search * prefix. */ glh->prefix = node ? glh->buffer + node->start : NULL; return 0; } /*....................................................................... * Recall the oldest recorded line. * * Input: * glh GlHistory * The input-line history maintenance object. * line char * The input line buffer. On input this should contain * the current input line, and on output, its contents * will have been replaced with the oldest line. * dim size_t The allocated dimensions of the line buffer. * Output: * return char * A pointer to line[0], or NULL if not found. */ char *_glh_oldest_line(GlHistory *glh, char *line, size_t dim) { GlLineNode *node; /* The line location node being checked */ int first; /* True if this is the start of a new search */ /* * Check the arguments. */ if(!glh || !line) { fprintf(stderr, "_glh_oldest_line: NULL argument(s).\n"); return NULL; }; /* * Is history enabled? */ if(!glh->enable || !glh->buffer || glh->max_lines == 0) return NULL; /* * Check the line dimensions. */ if(dim < strlen(line) + 1) { fprintf(stderr, "_glh_oldest_line: 'dim' inconsistent with strlen(line) contents.\n"); return NULL; }; /* * Is this the start of a new search? */ first = glh->recall==NULL; /* * If this is the first search backwards, save the current line * for potential recall later, and mark it as the last line * recalled. */ if(first) { if(_glh_add_history(glh, line, 1)) return NULL; glh->recall = glh->list.tail; }; /* * Locate the oldest line that belongs to the current group. */ for(node=glh->list.head; node && node->group != glh->group; node = node->next) ; /* * No line found? */ if(!node) return NULL; /* * Record the above node as the starting point for subsequent * searches. */ glh->recall = node; /* * Copy the recalled line into the provided line buffer. */ strncpy(line, glh->buffer + node->start, dim); line[dim-1] = '\0'; return line; } /*....................................................................... * Recall the line that was being entered when the search started. * * Input: * glh GlHistory * The input-line history maintenance object. * line char * The input line buffer. On input this should contain * the current input line, and on output, its contents * will have been replaced with the line that was * being entered when the search was started. * dim size_t The allocated dimensions of the line buffer. * Output: * return char * A pointer to line[0], or NULL if not found. */ char *_glh_current_line(GlHistory *glh, char *line, size_t dim) { /* * Check the arguments. */ if(!glh || !line) { fprintf(stderr, "_glh_current_line: NULL argument(s).\n"); return NULL; }; /* * Is history enabled? */ if(!glh->enable || !glh->buffer || glh->max_lines == 0) return NULL; /* * Check the line dimensions. */ if(dim < strlen(line) + 1) { fprintf(stderr, "_glh_current_line: 'dim' inconsistent with strlen(line) contents.\n"); return NULL; }; /* * Restore the original line. */ return _glh_restore_line(glh, line, dim); } /*....................................................................... * Remove the line that was originally being edited when the history * traversal was started, from its saved position in the history list, * and place it in the provided line buffer. * * Input: * glh GlHistory * The input-line history maintenance object. * line char * The input line buffer. On input this should contain * the current input line, and on output, its contents * will have been replaced with the saved line. * dim size_t The allocated dimensions of the line buffer. * Output: * return char * A pointer to line[0], or NULL if not found. */ static char *_glh_restore_line(GlHistory *glh, char *line, size_t dim) { GlLineNode *tail; /* The tail node to be discarded */ /* * If there wasn't a search in progress, do nothing. */ if(!glh->recall) return NULL; /* * Get the list node that is to be removed. */ tail = glh->list.tail; /* * If a pointer to the saved line is being used to record the * current search prefix, reestablish the search prefix, to * have it recorded by another history line if possible. */ if(glh->prefix == glh->buffer + tail->start) (void) _glh_search_prefix(glh, glh->buffer + tail->start, glh->prefix_len); /* * Copy the recalled line into the input-line buffer. */ strncpy(line, glh->buffer + tail->start, dim); line[dim-1] = '\0'; /* * Discard the line-location node. */ _glh_discard_node(glh, tail); /* * Mark the search as ended. */ glh->recall = NULL; return line; } /*....................................................................... * Query the id of a history line offset by a given number of lines from * the one that is currently being recalled. If a recall session isn't * in progress, or the offset points outside the history list, 0 is * returned. * * Input: * glh GlHistory * The input-line history maintenance object. * offset int The line offset (0 for the current line, < 0 * for an older line, > 0 for a newer line. * Output: * return GlhLineID The identifier of the line that is currently * being recalled, or 0 if no recall session is * currently in progress. */ GlhLineID _glh_line_id(GlHistory *glh, int offset) { GlLineNode *node; /* The line location node being checked */ /* * Is history enabled? */ if(!glh->enable || !glh->buffer || glh->max_lines == 0) return 0; /* * Search forward 'offset' lines to find the required line. */ if(offset >= 0) { for(node=glh->recall; node && offset != 0; node=node->next) { if(node->group == glh->group) offset--; }; } else { for(node=glh->recall; node && offset != 0; node=node->prev) { if(node->group == glh->group) offset++; }; }; return node ? node->id : 0; } /*....................................................................... * Recall a line by its history buffer ID. If the line is no longer * in the buffer, or the id is zero, NULL is returned. * * Input: * glh GlHistory * The input-line history maintenance object. * id GlhLineID The ID of the line to be returned. * line char * The input line buffer. On input this should contain * the current input line, and on output, its contents * will have been replaced with the saved line. * dim size_t The allocated dimensions of the line buffer. * Output: * return char * A pointer to line[0], or NULL if not found. */ char *_glh_recall_line(GlHistory *glh, GlhLineID id, char *line, size_t dim) { GlLineNode *node; /* The line location node being checked */ /* * Is history enabled? */ if(!glh->enable || !glh->buffer || glh->max_lines == 0) return NULL; /* * If we are starting a new recall session, save the current line * for potential recall later. */ if(!glh->recall && _glh_add_history(glh, line, 1)) return NULL; /* * Search for the specified line. */ node = _glh_find_id(glh, id); /* * Not found? */ if(!node || node->group != glh->group) return NULL; /* * Record the node of the matching line as the starting point * for subsequent searches. */ glh->recall = node; /* * Copy the recalled line into the provided line buffer. */ strncpy(line, glh->buffer + node->start, dim); line[dim-1] = '\0'; return line; } /*....................................................................... * Save the current history in a specified file. * * Input: * glh GlHistory * The input-line history maintenance object. * filename const char * The name of the new file to record the * history in. * comment const char * Extra information such as timestamps will * be recorded on a line started with this * string, the idea being that the file can * double as a command file. Specify "" if * you don't care. * max_lines int The maximum number of lines to save, or -1 * to save all of the lines in the history * list. * Output: * return int 0 - OK. * 1 - Error. */ int _glh_save_history(GlHistory *glh, const char *filename, const char *comment, int max_lines) { FILE *fp; /* The output file */ GlLineNode *node; /* The line being saved */ GlLineNode *head; /* The head of the list of lines to be saved */ /* * Check the arguments. */ if(!glh || !filename || !comment) { fprintf(stderr, "_glh_save_history: NULL argument(s).\n"); return 1; }; /* * Attempt to open the specified file. */ fp = fopen(filename, "w"); if(!fp) { fprintf(stderr, "_glh_save_history: Can't open %s (%s).\n", filename, strerror(errno)); return 1; }; /* * If a ceiling on the number of lines to save was specified, count * that number of lines backwards, to find the first line to be saved. */ head = NULL; if(max_lines >= 0) { for(head=glh->list.tail; head && --max_lines > 0; head=head->prev) ; }; if(!head) head = glh->list.head; /* * Write the contents of the history buffer to the history file, writing * associated data such as timestamps, to a line starting with the * specified comment string. */ for(node=head; node; node=node->next) { /* * Write peripheral information associated with the line, as a comment. */ if(fprintf(fp, "%s ", comment) < 0 || _glh_write_timestamp(fp, node->timestamp) || fprintf(fp, " %u\n", node->group) < 0) { fprintf(stderr, "Error writing %s (%s).\n", filename, strerror(errno)); (void) fclose(fp); return 1; }; /* * Write the history line. */ if(fprintf(fp, "%s\n", glh->buffer + node->start) < 0) { fprintf(stderr, "Error writing %s (%s).\n", filename, strerror(errno)); (void) fclose(fp); return 1; }; }; /* * Close the history file. */ if(fclose(fp) == EOF) { fprintf(stderr, "Error writing %s (%s).\n", filename, strerror(errno)); return 1; }; return 0; } /*....................................................................... * Restore previous history lines from a given file. * * Input: * glh GlHistory * The input-line history maintenance object. * filename const char * The name of the file to read from. * comment const char * The same comment string that was passed to * _glh_save_history() when this file was * written. * line char * A buffer into which lines can be read. * dim size_t The allocated dimension of line[]. * Output: * return int 0 - OK. * 1 - Error. */ int _glh_load_history(GlHistory *glh, const char *filename, const char *comment, char *line, size_t dim) { FILE *fp; /* The output file */ size_t comment_len; /* The length of the comment string */ time_t timestamp; /* The timestamp of the history line */ unsigned group; /* The identifier of the history group to which */ /* the line belongs. */ int lineno; /* The line number being read */ /* * Check the arguments. */ if(!glh || !filename || !comment || !line) { fprintf(stderr, "_glh_load_history: NULL argument(s).\n"); return 1; }; /* * Measure the length of the comment string. */ comment_len = strlen(comment); /* * Clear the history list. */ _glh_clear_history(glh, 1); /* * Attempt to open the specified file. Don't treat it as an error * if the file doesn't exist. */ fp = fopen(filename, "r"); if(!fp) return 0; /* * Attempt to read each line and preceding peripheral info, and add these * to the history list. */ for(lineno=1; fgets(line, dim, fp) != NULL; lineno++) { char *lptr; /* A pointer into the input line */ /* * Check that the line starts with the comment string. */ if(strncmp(line, comment, comment_len) != 0) { return _glh_cant_load_history(glh, filename, lineno, "Corrupt history parameter line", fp); }; /* * Skip spaces and tabs after the comment. */ for(lptr=line+comment_len; *lptr && (*lptr==' ' || *lptr=='\t'); lptr++) ; /* * The next word must be a timestamp. */ if(_glh_decode_timestamp(lptr, &lptr, ×tamp)) { return _glh_cant_load_history(glh, filename, lineno, "Corrupt timestamp", fp); }; /* * Skip spaces and tabs. */ while(*lptr==' ' || *lptr=='\t') lptr++; /* * The next word must be an unsigned integer group number. */ group = (int) strtoul(lptr, &lptr, 10); if(*lptr != ' ' && *lptr != '\n') { return _glh_cant_load_history(glh, filename, lineno, "Corrupt group id", fp); }; /* * Skip spaces and tabs. */ while(*lptr==' ' || *lptr=='\t') lptr++; /* * There shouldn't be anything left on the line. */ if(*lptr != '\n') { return _glh_cant_load_history(glh, filename, lineno, "Corrupt parameter line", fp); }; /* * Now read the history line itself. */ lineno++; if(fgets(line, dim, fp) == NULL) return _glh_cant_load_history(glh, filename, lineno, "Read error", fp); /* * Append the line to the history buffer. */ if(_glh_add_history(glh, line, 1)) { return _glh_cant_load_history(glh, filename, lineno, "Insufficient memory to record line", fp); }; /* * Record the group and timestamp information along with the line. */ if(glh->list.tail) { glh->list.tail->timestamp = timestamp; glh->list.tail->group = group; }; }; /* * Close the file. */ (void) fclose(fp); return 0; } /*....................................................................... * This is a private error return function of _glh_load_history(). */ static int _glh_cant_load_history(GlHistory *glh, const char *filename, int lineno, const char *message, FILE *fp) { fprintf(stderr, "%s:%d: %s.\n", filename, lineno, message); (void) fclose(fp); return 1; } /*....................................................................... * Switch history groups. * * Input: * glh GlHistory * The input-line history maintenance object. * group unsigned The new group identifier. This will be recorded * with subsequent history lines, and subsequent * history searches will only return lines with * this group identifier. This allows multiple * separate history lists to exist within * a single GlHistory object. Note that the * default group identifier is 0. * Output: * return int 0 - OK. * 1 - Error. */ int _glh_set_group(GlHistory *glh, unsigned group) { /* * Check the arguments. */ if(!glh) { fprintf(stderr, "_glh_set_group: NULL argument(s).\n"); return 1; }; /* * Is the group being changed? */ if(group != glh->group) { /* * Cancel any ongoing search. */ if(_glh_cancel_search(glh)) return 1; /* * Record the new group. */ glh->group = group; }; return 0; } /*....................................................................... * Query the current history group. * * Input: * glh GlHistory * The input-line history maintenance object. * Output: * return unsigned The group identifier. */ int _glh_get_group(GlHistory *glh) { return glh ? glh->group : 0; } /*....................................................................... * Write a timestamp to a given stdio stream, in the format * yyyymmddhhmmss * * Input: * fp FILE * The stream to write to. * timestamp time_t The timestamp to be written. * Output: * return int 0 - OK. * 1 - Error. */ static int _glh_write_timestamp(FILE *fp, time_t timestamp) { struct tm *t; /* THe broken-down calendar time */ /* * Get the calendar components corresponding to the given timestamp. */ if(timestamp < 0 || (t = localtime(×tamp)) == NULL) { if(fprintf(fp, "?") < 0) return 1; return 0; }; /* * Write the calendar time as yyyymmddhhmmss. */ if(fprintf(fp, "%04d%02d%02d%02d%02d%02d", t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec) < 0) return 1; return 0; } /*....................................................................... * Read a timestamp from a string. * * Input: * string char * The string to read from. * Input/Output: * endp char ** On output *endp will point to the next unprocessed * character in string[]. * timestamp time_t * The timestamp will be assigned to *t. * Output: * return int 0 - OK. * 1 - Error. */ static int _glh_decode_timestamp(char *string, char **endp, time_t *timestamp) { unsigned year,month,day,hour,min,sec; /* Calendar time components */ struct tm t; /* * There are 14 characters in the date format yyyymmddhhmmss. */ enum {TSLEN=14}; char timestr[TSLEN+1]; /* The timestamp part of the string */ /* * If the time wasn't available at the time that the line was recorded * it will have been written as "?". Check for this before trying * to read the timestamp. */ if(string[0] == '\?') { *endp = string+1; *timestamp = -1; return 0; }; /* * The timestamp is expected to be written in the form yyyymmddhhmmss. */ if(strlen(string) < TSLEN) { *endp = string; return 1; }; /* * Copy the timestamp out of the string. */ strncpy(timestr, string, TSLEN); timestr[TSLEN] = '\0'; /* * Decode the timestamp. */ if(sscanf(timestr, "%4u%2u%2u%2u%2u%2u", &year, &month, &day, &hour, &min, &sec) != 6) { *endp = string; return 1; }; /* * Advance the string pointer over the successfully read timestamp. */ *endp = string + TSLEN; /* * Copy the read values into a struct tm. */ t.tm_sec = sec; t.tm_min = min; t.tm_hour = hour; t.tm_mday = day; t.tm_wday = 0; t.tm_yday = 0; t.tm_mon = month - 1; t.tm_year = year - 1900; t.tm_isdst = -1; /* * Convert the contents of the struct tm to a time_t. */ *timestamp = mktime(&t); return 0; } /*....................................................................... * Display the contents of the history list. * * Input: * glh GlHistory * The input-line history maintenance object. * fp FILE * The stdio stream to write to. * fmt const char * A format string. This can contain arbitrary * characters, which are written verbatim, plus * any of the following format directives: * %D - The date, like 2001-11-20 * %T - The time of day, like 23:59:59 * %N - The sequential entry number of the * line in the history buffer. * %G - The history group number of the line. * %% - A literal % character. * %H - The history line. * all_groups int If true, display history lines from all * history groups. Otherwise only display * those of the current history group. * max_lines int If max_lines is < 0, all available lines * are displayed. Otherwise only the most * recent max_lines lines will be displayed. * Output: * return int 0 - OK. * 1 - Error. */ int _glh_show_history(GlHistory *glh, FILE *fp, const char *fmt, int all_groups, int max_lines) { GlLineNode *node; /* The line being displayed */ GlLineNode *oldest; /* The oldest line to display */ enum {TSMAX=32}; /* The maximum length of the date and time string */ char buffer[TSMAX+1]; /* The buffer in which to write the date and time */ int idlen; /* The length of displayed ID strings */ unsigned grpmax; /* The maximum group number in the buffer */ int grplen; /* The number of characters needed to print grpmax */ /* * Check the arguments. */ if(!glh || !fp || !fmt) { fprintf(stderr, "_glh_show_history: NULL argument(s).\n"); return 1; }; /* * Is history enabled? */ if(!glh->enable || !glh->list.head) return 0; /* * Work out the length to display ID numbers, choosing the length of * the biggest number in the buffer. Smaller numbers will be padded * with leading zeroes if needed. */ snprintf(buffer, sizeof(buffer), "%lu", (unsigned long) glh->list.tail->id); idlen = strlen(buffer); /* * Find the largest group number. */ grpmax = 0; for(node=glh->list.head; node; node=node->next) { if(node->group > grpmax) grpmax = node->group; }; /* * Find out how many characters are needed to display the group number. */ snprintf(buffer, sizeof(buffer), "%u", (unsigned) grpmax); grplen = strlen(buffer); /* * Find the node that follows the oldest line to be displayed. */ if(max_lines < 0) { oldest = glh->list.head; } else if(max_lines==0) { return 0; } else { for(oldest=glh->list.tail; oldest; oldest=oldest->prev) { if((all_groups || oldest->group == glh->group) && --max_lines <= 0) break; }; /* * If the number of lines in the buffer doesn't exceed the specified * maximum, start from the oldest line in the buffer. */ if(!oldest) oldest = glh->list.head; }; /* * List the history lines in increasing time order. */ for(node=oldest; node; node=node->next) { /* * Only display lines from the current history group, unless * told otherwise. */ if(all_groups || node->group == glh->group) { const char *fptr; /* A pointer into the format string */ struct tm *t = NULL; /* The broken time version of the timestamp */ /* * Work out the calendar representation of the node timestamp. */ if(node->timestamp != (time_t) -1) t = localtime(&node->timestamp); /* * Parse the format string. */ fptr = fmt; while(*fptr) { /* * Search for the start of the next format directive or the end of the string. */ const char *start = fptr; while(*fptr && *fptr != '%') fptr++; /* * Display any literal characters that precede the located directive. */ if(fptr > start && fprintf(fp, "%.*s", (int) (fptr - start), start) < 0) return 1; /* * Did we hit a new directive before the end of the line? */ if(*fptr) { /* * Obey the directive. Ignore unknown directives. */ switch(*++fptr) { case 'D': /* Display the date */ if(t && strftime(buffer, TSMAX, "%Y-%m-%d", t) != 0 && fprintf(fp, "%s", buffer) < 0) return 1; break; case 'T': /* Display the time of day */ if(t && strftime(buffer, TSMAX, "%H:%M:%S", t) != 0 && fprintf(fp, "%s", buffer) < 0) return 1; break; case 'N': /* Display the sequential entry number */ if(fprintf(fp, "%*lu", idlen, (unsigned long) node->id) < 0) return 1; break; case 'G': if(fprintf(fp, "%*u", grplen, (unsigned) node->group) < 0) return 1; break; case 'H': /* Display the history line */ if(fprintf(fp, "%s", glh->buffer + node->start) < 0) return 1; break; case '%': /* A literal % symbol */ if(fputc('%', fp) == EOF) return 1; break; }; /* * Skip the directive. */ if(*fptr) fptr++; }; }; }; }; return 0; } /*....................................................................... * Change the size of the history buffer. * * Input: * glh GlHistory * The input-line history maintenance object. * bufsize size_t The number of bytes in the history buffer, or 0 * to delete the buffer completely. * Output: * return int 0 - OK. * 1 - Insufficient memory (the previous buffer * will have been retained). No error message * will be displayed. */ int _glh_resize_history(GlHistory *glh, size_t bufsize) { GlLineNode *node; /* A line location node in the list of lines */ GlLineNode *prev; /* The line location node preceding 'node' */ /* * Check the arguments. */ if(!glh) return bufsize > 0; /* * If the new size doesn't differ from the existing size, do nothing. */ if(glh->buflen == bufsize) return 0; /* * Cancel any ongoing search. */ (void) _glh_cancel_search(glh); /* * Create a wholly new buffer? */ if(glh->buflen == 0) { glh->buffer = (char *) malloc(bufsize); if(!glh->buffer) return 1; glh->buflen = bufsize; /* * Delete an existing buffer? */ } else if(bufsize == 0) { _glh_clear_history(glh, 1); free(glh->buffer); glh->buffer = NULL; glh->buflen = 0; /* * To get here, we must be shrinking or expanding from one * finite size to another. */ } else { /* * If we are shrinking the size of the buffer, then we first need * to discard the oldest lines that won't fit in the new buffer. */ if(bufsize < glh->buflen) { size_t nbytes = 0; /* The number of bytes used in the new buffer */ GlLineNode *oldest; /* The oldest node to be kept */ /* * Searching backwards from the youngest line, find the oldest * line for which there will be sufficient room in the new buffer. */ for(oldest = glh->list.tail; oldest && (nbytes += oldest->nchar) <= bufsize; oldest = oldest->prev) ; /* * We will have gone one node too far, unless we reached the oldest line * without exceeding the target length. */ if(oldest) { nbytes -= oldest->nchar; oldest = oldest->next; }; /* * Discard the nodes that can't be retained. */ while(glh->list.head && glh->list.head != oldest) _glh_discard_node(glh, glh->list.head); /* * If we are increasing the size of the buffer, we need to reallocate * the buffer before shifting the lines into their new positions. */ } else { char *new_buffer = (char *) realloc(glh->buffer, bufsize); if(!new_buffer) return 1; glh->buffer = new_buffer; glh->buflen = bufsize; }; /* * If there are any lines to be preserved, copy the block of lines * that precedes the end of the existing buffer to what will be * the end of the new buffer. */ if(glh->list.head) { int shift; /* The number of bytes to shift lines in the buffer */ /* * Get the oldest line to be kept. */ GlLineNode *oldest = glh->list.head; /* * Count the number of characters that are used in the lines that * precede the end of the current buffer (ie. not including those * lines that have been wrapped to the start of the buffer). */ int n = 0; for(node=oldest,prev=oldest->prev; node && node->start >= oldest->start; prev=node, node=node->next) n += node->nchar; /* * Move these bytes to the end of the resized buffer. */ memmove(glh->buffer + bufsize - n, glh->buffer + oldest->start, n); /* * Adjust the buffer pointers to reflect the new locations of the moved * lines. */ shift = bufsize - n - oldest->start; for(node=prev; node && node->start >= oldest->start; node=node->prev) node->start += shift; }; /* * Shrink the buffer? */ if(bufsize < glh->buflen) { char *new_buffer = (char *) realloc(glh->buffer, bufsize); if(new_buffer) glh->buffer = new_buffer; glh->buflen = bufsize; /* Mark it as shrunk, regardless of success */ }; }; return 0; } /*....................................................................... * Set an upper limit to the number of lines that can be recorded in the * history list, or remove a previously specified limit. * * Input: * glh GlHistory * The input-line history maintenance object. * max_lines int The maximum number of lines to allow, or -1 to * cancel a previous limit and allow as many lines * as will fit in the current history buffer size. */ void _glh_limit_history(GlHistory *glh, int max_lines) { if(!glh) return; /* * Apply a new limit? */ if(max_lines >= 0 && max_lines != glh->max_lines) { /* * Count successively older lines until we reach the start of the * list, or until we have seen max_lines lines (at which point 'node' * will be line number max_lines+1). */ int nline = 0; GlLineNode *node; for(node=glh->list.tail; node && ++nline <= max_lines; node=node->prev) ; /* * Discard any lines that exceed the limit. */ if(node) { GlLineNode *oldest = node->next; /* The oldest line to be kept */ /* * Delete nodes from the head of the list until we reach the node that * is to be kept. */ while(glh->list.head && glh->list.head != oldest) _glh_discard_node(glh, glh->list.head); }; }; /* * Record the new limit. */ glh->max_lines = max_lines; return; } /*....................................................................... * Discard either all history, or the history associated with the current * history group. * * Input: * glh GlHistory * The input-line history maintenance object. * all_groups int If true, clear all of the history. If false, * clear only the stored lines associated with the * currently selected history group. */ void _glh_clear_history(GlHistory *glh, int all_groups) { /* * Check the arguments. */ if(!glh) return; /* * Cancel any ongoing search. */ (void) _glh_cancel_search(glh); /* * Delete all history lines regardless of group? */ if(all_groups) { _rst_FreeList(glh->list.node_mem); glh->list.head = glh->list.tail = NULL; glh->nline = 0; glh->id_node = NULL; /* * Just delete lines of the current group? */ } else { GlLineNode *node; /* The line node being checked */ GlLineNode *prev; /* The line node that precedes 'node' */ GlLineNode *next; /* The line node that follows 'node' */ /* * Search out and delete the line nodes of the current group. */ for(node=glh->list.head; node; node=next) { /* * Keep a record of the following node before we delete the current * node. */ next = node->next; /* * Discard this node? */ if(node->group == glh->group) _glh_discard_node(glh, node); }; /* * If there are any lines left, and we deleted any lines, there will * be gaps in the buffer. These need to be removed. */ if(glh->list.head) { int epos; /* The index of the last used element in the buffer */ /* * Find the line nearest the end of the buffer. */ GlLineNode *enode; for(node=glh->list.head, prev=NULL; node && node->start >= glh->list.head->start; prev=node, node = node->next) ; enode = prev; /* * Move the end line to abutt the end of the buffer, and remove gaps * between the lines that precede it. */ epos = glh->buflen; for(node=enode; node; node=node->prev) { int shift = epos - (node->start + node->nchar); if(shift) { memmove(glh->buffer + node->start + shift, glh->buffer + node->start, node->nchar); node->start += shift; }; epos = node->start; }; /* * Move the first line in the buffer to the start of the buffer, and * remove gaps between the lines that follow it. */ epos = 0; for(node=enode ? enode->next : NULL; node; node=node->next) { int shift = epos - node->start; if(shift) { memmove(glh->buffer + node->start + shift, glh->buffer + node->start, node->nchar); node->start += shift; }; epos = node->start + node->nchar; }; }; }; return; } /*....................................................................... * Temporarily enable or disable the history list. * * Input: * glh GlHistory * The input-line history maintenance object. * enable int If true, turn on the history mechanism. If * false, disable it. */ void _glh_toggle_history(GlHistory *glh, int enable) { if(glh) glh->enable = enable; } /*....................................................................... * Remove a given line location node from the history list, and return * it to the freelist. * * Input: * glh GlHistory * The input-line history maintenance object. * node GlLineNode * The node to be removed. This must be currently * in the list who's head is glh->list.head, or * be NULL. */ static void _glh_discard_node(GlHistory *glh, GlLineNode *node) { if(node) { /* * Make the node that precedes the node being removed point * to the one that follows it. */ if(node->prev) node->prev->next = node->next; else glh->list.head = node->next; /* * Make the node that follows the node being removed point * to the one that precedes it. */ if(node->next) node->next->prev = node->prev; else glh->list.tail = node->prev; /* * If we are deleting the node that is marked as the start point of the * last ID search, remove the cached starting point. */ if(node == glh->id_node) glh->id_node = NULL; /* * Return the node to the free list. */ node = (GlLineNode *) _del_FreeListNode(glh->list.node_mem, node); /* * Decrement the count of the number of lines in the buffer. */ glh->nline--; }; } /*....................................................................... * Lookup the details of a given history line, given its id. * * Input: * glh GlHistory * The input-line history maintenance object. * id GlLineID The sequential number of the line. * Input/Output: * line const char ** A pointer to the history line will be assigned * to *line. * group unsigned * The group membership of the line will be assigned * to *group. * timestamp time_t * The timestamp of the line will be assigned to * *timestamp. * Output: * return int 0 - The requested line wasn't found. * 1 - The line was found. */ int _glh_lookup_history(GlHistory *glh, GlhLineID id, const char **line, unsigned *group, time_t *timestamp) { GlLineNode *node; /* The located line location node */ /* * Check the arguments. */ if(!glh) return 0; /* * Search for the line that has the specified ID. */ node = _glh_find_id(glh, (GlhLineID) id); /* * Not found? */ if(!node) return 0; /* * Return the details of the line. */ if(line) *line = glh->buffer + node->start; if(group) *group = node->group; if(timestamp) *timestamp = node->timestamp; return 1; } /*....................................................................... * Lookup a node in the history list by its ID. * * Input: * glh GlHistory * The input-line history maintenance object. * id GlhLineID The ID of the line to be returned. * Output: * return GlLIneNode * The located node, or NULL if not found. */ static GlLineNode *_glh_find_id(GlHistory *glh, GlhLineID id) { GlLineNode *node; /* The node being checked */ /* * Is history enabled? */ if(!glh->enable || !glh->list.head) return NULL; /* * If possible, start at the end point of the last ID search. * Otherwise start from the head of the list. */ node = glh->id_node; if(!node) node = glh->list.head; /* * Search forwards from 'node'? */ if(node->id < id) { while(node && node->id != id) node = node->next; glh->id_node = node ? node : glh->list.tail; /* * Search backwards from 'node'? */ } else { while(node && node->id != id) node = node->prev; glh->id_node = node ? node : glh->list.head; }; /* * Return the located node (this will be NULL if the ID wasn't found). */ return node; } /*....................................................................... * Query the state of the history list. Note that any of the input/output * pointers can be specified as NULL. * * Input: * glh GlHistory * The input-line history maintenance object. * Input/Output: * enabled int * If history is enabled, *enabled will be * set to 1. Otherwise it will be assigned 0. * group unsigned * The current history group ID will be assigned * to *group. * max_lines int * The currently requested limit on the number * of history lines in the list, or -1 if * unlimited. */ void _glh_state_of_history(GlHistory *glh, int *enabled, unsigned *group, int *max_lines) { if(glh) { if(enabled) *enabled = glh->enable; if(group) *group = glh->group; if(max_lines) *max_lines = glh->max_lines; }; } /*....................................................................... * Get the range of lines in the history buffer. * * Input: * glh GlHistory * The input-line history maintenance object. * Input/Output: * oldest unsigned long * The sequential entry number of the oldest * line in the history list will be assigned * to *oldest, unless there are no lines, in * which case 0 will be assigned. * newest unsigned long * The sequential entry number of the newest * line in the history list will be assigned * to *newest, unless there are no lines, in * which case 0 will be assigned. * nlines int * The number of lines currently in the history * list. */ void _glh_range_of_history(GlHistory *glh, unsigned long *oldest, unsigned long *newest, int *nlines) { if(glh) { if(oldest) *oldest = glh->list.head ? glh->list.head->id : 0; if(newest) *newest = glh->list.tail ? glh->list.tail->id : 0; if(nlines) *nlines = glh->nline; }; } /*....................................................................... * Return the size of the history buffer and the amount of the * buffer that is currently in use. * * Input: * glh GlHistory * The input-line history maintenance object. * Input/Output: * buff_size size_t * The size of the history buffer (bytes). * buff_used size_t * The amount of the history buffer that * is currently occupied (bytes). */ void _glh_size_of_history(GlHistory *glh, size_t *buff_size, size_t *buff_used) { if(glh) { if(buff_size) *buff_size = glh->buflen; /* * Determine the amount of buffer space that is currently occupied. */ if(buff_used) { size_t used = 0; GlLineNode *node; for(node=glh->list.head; node; node=node->next) used += node->nchar; *buff_used = used; }; }; } xorp/cli/libtecla/demo.c0000664000076400007640000000670311421137511015300 0ustar greearbgreearb/* * Copyright (c) 2000, 2001 by the California Institute of Technology. * * All rights reserved. * * 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, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, and/or sell copies of the Software, and to permit persons * to whom the Software is furnished to do so, provided that the above * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Except as contained in this notice, the name of a copyright holder * shall not be used in advertising or otherwise to promote the sale, use * or other dealings in this Software without prior written authorization * of the copyright holder. */ #include #include #include #include #include #include #include #include #include "libtecla.h" int main(int argc, char *argv[]) { char *line; /* A line of input */ GetLine *gl; /* The line editor */ int major,minor,micro; /* The version number of the library */ /* * Create the line editor, specifying a max line length of 500 bytes, * and 10000 bytes to allocate to storage of historical input lines. */ gl = new_GetLine(500, 5000); if(!gl) return 1; /* * If the user has the LC_CTYPE or LC_ALL environment variables set, * enable display of characters corresponding to the specified locale. */ (void) setlocale(LC_CTYPE, ""); /* * Lookup and display the version number of the library. */ libtecla_version(&major, &minor, µ); printf("Welcome to the demo program of libtecla version %d.%d.%d\n", major, minor, micro); /* * Load history. */ (void) gl_load_history(gl, "~/.demo_history", "#"); /* * Read lines of input from the user and print them to stdout. */ do { /* * Get a new line from the user. */ line = gl_get_line(gl, "$ ", NULL, 0); if(!line) break; /* * Display what was entered. */ if(printf("You entered: %s", line) < 0 || fflush(stdout)) break; /* * If the user types "exit", quit the program. */ if(strcmp(line, "exit\n")==0) break; else if(strcmp(line, "history\n")==0) gl_show_history(gl, stdout, "%N %T %H\n", 0, -1); else if(strcmp(line, "size\n")==0) { GlTerminalSize size = gl_terminal_size(gl, 80, 24); printf("Terminal size = %d columns x %d lines.\n", size.ncolumn, size.nline); }; } while(1); /* * Save historical command lines. */ (void) gl_save_history(gl, "~/.demo_history", "#", -1); /* * Clean up. */ gl = del_GetLine(gl); return 0; } xorp/cli/libtecla/cplfile.h0000664000076400007640000001056511421137511016000 0ustar greearbgreearb#ifndef cplfile_h #define cplfile_h /* * Copyright (c) 2000, 2001 by Martin C. Shepherd. * * All rights reserved. * * 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, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, and/or sell copies of the Software, and to permit persons * to whom the Software is furnished to do so, provided that the above * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Except as contained in this notice, the name of a copyright holder * shall not be used in advertising or otherwise to promote the sale, use * or other dealings in this Software without prior written authorization * of the copyright holder. */ typedef struct CompleteFile CompleteFile; /* * Create a file-completion resource object. */ CompleteFile *_new_CompleteFile(void); /* * Delete a file-completion resource object. */ CompleteFile *_del_CompleteFile(CompleteFile *cf); /*....................................................................... * Complete the string between path[0] and path[len-1] as a pathname, * leaving the last component uncompleted if it is potentially ambiguous, * and returning an array of possible completions. Note that the returned * container belongs to the 'cf' object and its contents will change on * subsequent calls to this function. * * Input: * cpl WordCompletion * The object in which to record the completions. * cf CompleteFile * The filename-completion resource object. * line const char * The string containing the incomplete filename. * word_start int The index of the first character in line[] * of the incomplete filename. * word_end int The index of the character in line[] that * follows the last character of the incomplete * filename. * escaped int If true, backslashes in path[] are * interpreted as escaping the characters * that follow them, and any spaces, tabs, * backslashes, or wildcard characters in the * returned suffixes will be similarly be escaped. * If false, backslashes will be interpreted as * literal parts of the file name, and no * backslashes will be added to the returned * suffixes. * check_fn CplCheckFn * If not zero, this argument specifies a * function to call to ask whether a given * file should be included in the list * of completions. * check_data void * Anonymous data to be passed to check_fn(). * Output: * return int 0 - OK. * 1 - Error. A description of the error can be * acquired by calling cf_last_error(cf). */ int _cf_complete_file(WordCompletion *cpl, CompleteFile *cf, const char *line, int word_start, int word_end, int escaped, CplCheckFn *check_fn, void *check_data); /*....................................................................... * Return a description of the error that occurred on the last call to * cf_complete_file(). * * Input: * cf CompleteFile * The path-completion resource object. * Output: * return char * The description of the last error. */ const char *_cf_last_error(CompleteFile *cf); #endif xorp/cli/libtecla/history.h0000664000076400007640000001223711421137511016061 0ustar greearbgreearb#ifndef history_h #define history_h /* * Copyright (c) 2000, 2001 by Martin C. Shepherd. * * All rights reserved. * * 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, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, and/or sell copies of the Software, and to permit persons * to whom the Software is furnished to do so, provided that the above * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Except as contained in this notice, the name of a copyright holder * shall not be used in advertising or otherwise to promote the sale, use * or other dealings in this Software without prior written authorization * of the copyright holder. */ #include /* FILE * */ /*----------------------------------------------------------------------- * This module is used to record and traverse historical lines of user input. */ typedef struct GlHistory GlHistory; /* * Create a new history maintenance object. */ GlHistory *_new_GlHistory(size_t buflen); /* * Delete a history maintenance object. */ GlHistory *_del_GlHistory(GlHistory *glh); int _glh_add_history(GlHistory *glh, const char *line, int force); int _glh_search_prefix(GlHistory *glh, const char *line, int prefix_len); char *_glh_find_backwards(GlHistory *glh, char *line, size_t dim); char *_glh_find_forwards(GlHistory *glh, char *line, size_t dim); int _glh_cancel_search(GlHistory *glh); char *_glh_oldest_line(GlHistory *glh, char *line, size_t dim); char *_glh_current_line(GlHistory *glh, char *line, size_t dim); /* * Whenever a new line is added to the history buffer, it is given * a unique ID, recorded in an object of the following type. */ typedef unsigned long GlhLineID; /* * Query the id of a history line offset by a given number of lines from * the one that is currently being recalled. If a recall session isn't * in progress, or the offset points outside the history list, 0 is * returned. */ GlhLineID _glh_line_id(GlHistory *glh, int offset); /* * Recall a line by its history buffer ID. If the line is no longer * in the buffer, or the specified id is zero, NULL is returned. */ char *_glh_recall_line(GlHistory *glh, GlhLineID id, char *line, size_t dim); /* * Write the contents of the history buffer to a given file. Note that * ~ and $ expansion are not performed on the filename. */ int _glh_save_history(GlHistory *glh, const char *filename, const char *comment, int max_lines); /* * Restore the contents of the history buffer from a given file. * Note that ~ and $ expansion are not performed on the filename. */ int _glh_load_history(GlHistory *glh, const char *filename, const char *comment, char *line, size_t dim); /* * Set and query the current history group. */ int _glh_set_group(GlHistory *glh, unsigned group); int _glh_get_group(GlHistory *glh); /* * Display the contents of the history list to the specified stdio * output group. */ int _glh_show_history(GlHistory *glh, FILE *fp, const char *fmt, int all_groups, int max_lines); /* * Change the size of the history buffer. */ int _glh_resize_history(GlHistory *glh, size_t bufsize); /* * Set an upper limit to the number of lines that can be recorded in the * history list, or remove a previously specified limit. */ void _glh_limit_history(GlHistory *glh, int max_lines); /* * Discard either all history, or the history associated with the current * history group. */ void _glh_clear_history(GlHistory *glh, int all_groups); /* * Temporarily enable or disable the history facility. */ void _glh_toggle_history(GlHistory *glh, int enable); /* * Lookup a history line by its sequential number of entry in the * history buffer. */ int _glh_lookup_history(GlHistory *glh, GlhLineID id, const char **line, unsigned *group, time_t *timestamp); /* * Query the state of the history list. */ void _glh_state_of_history(GlHistory *glh, int *enabled, unsigned *group, int *max_lines); /* * Get the range of lines in the history buffer. */ void _glh_range_of_history(GlHistory *glh, unsigned long *oldest, unsigned long *newest, int *nlines); /* * Return the size of the history buffer and the amount of the * buffer that is currently in use. */ void _glh_size_of_history(GlHistory *glh, size_t *buff_size, size_t *buff_used); #endif xorp/cli/cli_private.hh0000664000076400007640000000252311703345405015255 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2012 XORP, Inc and Others-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __CLI_CLI_PRIVATE_HH__ #define __CLI_CLI_PRIVATE_HH__ // // CLI implementation-specific definitions. // // // Constants definitions // #define CLI_MAX_CONNECTIONS 129 // XXX: intentionally not 2^n number #define XORP_CLI_PROMPT "Xorp> " #define XORP_CLI_PROMPT_ENABLE "XORP# " #ifndef CHAR_TO_CTRL #define CHAR_TO_CTRL(c) ((c) & 0x1f) #endif #ifndef CHAR_TO_META #define CHAR_TO_META(c) ((c) | 0x080) #endif #endif // __CLI_CLI_PRIVATE_HH__ xorp/xorp_install.bash0000664000076400007640000000553111643352302015240 0ustar greearbgreearb#!/bin/bash # Usage: cd /usr/local/xorp; ./xorp_install.bash # # This will create the xorp user, attempt to fix up the # group configure properly, and also attempt to soft-link # some libraries to help binaries compiled on other OS versions # to work on the target systems. # Xorp should be un-tarred in /usr/local if [ `pwd` != "/usr/local/xorp" ] then echo "" echo "ERROR: You must un-tar xorp.tgz in /usr/local so that the files" echo " are placed in /usr/local/xorp, and this script must be run from" echo " the /usr/local/xorp directory." exit 1 fi # Add xorp user and group #Check for adduser commands adduserArgs="" if adduser -h 2>&1 | grep ".--system" > /dev/null 2>&1 then adduserArgs="$adduserArgs --system" fi if adduser -h 2>&1 | grep ".--no-create-home" > /dev/null 2>&1 then adduserArgs="$adduserArgs --no-create-home" fi echo "Creating xorp user and adding xorp to xorp and root groups..." adduser $adduserArgs xorp usermod -a -G xorp xorp usermod -a -G xorp root usermod -a -G xorp lanforge usermod -a -G root xorp # Xorp may have been compiled on older systems and thus have dependencies # on older libraries. Often, we can just link to newer ones and things # work fine. for i in `ldd ./sbin/* ./lib/xorp/sbin/*|grep "not found"|cut -f1 -d" "|sort|uniq` do echo "Missing library: $i" extension=(`expr match "$i" '.*\([0-9]\)'`); filename=${i%$extension} #echo "base: $filename extension: $extension" while [ $extension -lt 20 ] do #echo "extension: $extension" let extension++ if uname -a|grep x86_64 > /dev/null 2>&1 then if [ -f /lib64/$filename$extension ] then echo "Linking: /lib64/$filename$extension to /lib64/$i" ln -s /lib64/$filename$extension /lib64/$i break fi if [ -f /usr/lib64/$filename$extension ] then echo "Linking: /usr/lib64/$filename$extension to /usr/lib64/$i" ln -s /usr/lib64/$filename$extension /usr/lib64/$i break fi else if [ -f /lib/$filename$extension ] then echo "Linking: /lib/$filename$extension to /lib/$i" ln -s /lib/$filename$extension /lib/$i break fi if [ -f /usr/lib/$filename$extension ] then echo "Linking: /usr/lib/$filename$extension to /usr/lib/$i" ln -s /usr/lib/$filename$extension /usr/lib/$i break fi fi done done echo "Completed installation setup for Xorp, testing library linkage...." if /usr/local/xorp/sbin/xorpsh -h > /dev/null 2>&1 then echo "" echo "SUCCESS: Xorpsh seems to work fine..." else echo "" echo "ERROR: xorpsh returned error code. Please contact support." echo " You are probably missing some libraries, perhaps older versions" echo " of ones existing on your system. The command:" echo " ldd /usr/local/xorp/sbin/xorpsh" echo " might help you figure out which ones are missing." fi xorp/etc/0000775000076400007640000000000011421137511012427 5ustar greearbgreearbxorp/etc/templates/0000775000076400007640000000000011703343670014435 5ustar greearbgreearbxorp/etc/templates/ospfv2.cmds0000664000076400007640000002605611421137511016525 0ustar greearbgreearb/* $XORP: xorp/etc/templates/ospfv2.cmds,v 1.10 2006/04/27 20:01:32 pavlin Exp $ */ clear ospf4 database { %command: "ospf_clear_database -2" %help: HELP; %module: ospf4; %tag: HELP "Clear LSA database"; } show ospf4 { %command: "" %help: "Display information about OSPFv2"; %module: ospf4; } show ospf4 database { %command: "ospf_print_lsas -b" %help: HELP; %module: ospf4; %tag: HELP "Show LSA database"; } show ospf4 database brief { %command: "ospf_print_lsas -b" %help: HELP; %module: ospf4; %tag: HELP "Display brief output (default)"; } show ospf4 database detail { %command: "ospf_print_lsas -d" %help: HELP; %module: ospf4; %tag: HELP "Display detailed output"; } /* Router-LSA */ show ospf4 database router { %command: "ospf_print_lsas -b -f 1" %help: HELP; %module: ospf4; %tag: HELP "Show Router-LSA database"; } show ospf4 database router brief { %command: "ospf_print_lsas -b -f 1" %help: HELP; %module: ospf4; %tag: HELP "Display brief output (default)"; } show ospf4 database router detail { %command: "ospf_print_lsas -d -f 1" %help: HELP; %module: ospf4; %tag: HELP "Display detailed output"; } /* Network-LSA */ show ospf4 database network { %command: "ospf_print_lsas -b -f 2" %help: HELP; %module: ospf4; %tag: HELP "Show Network-LSA database"; } show ospf4 database network brief { %command: "ospf_print_lsas -b -f 2" %help: HELP; %module: ospf4; %tag: HELP "Display brief output (default)"; } show ospf4 database network detail { %command: "ospf_print_lsas -d -f 2" %help: HELP; %module: ospf4; %tag: HELP "Display detailed output"; } /* Summary-LSA (network) */ show ospf4 database netsummary { %command: "ospf_print_lsas -b -f 3" %help: HELP; %module: ospf4; %tag: HELP "Show Summary-LSA (network) database"; } show ospf4 database netsummary brief { %command: "ospf_print_lsas -b -f 3" %help: HELP; %module: ospf4; %tag: HELP "Display brief output (default)"; } show ospf4 database netsummary detail { %command: "ospf_print_lsas -d -f 3" %help: HELP; %module: ospf4; %tag: HELP "Display detailed output"; } /* Summary-LSA (AS boundary router) */ show ospf4 database asbrsummary { %command: "ospf_print_lsas -b -f 4" %help: HELP; %module: ospf4; %tag: HELP "Show Summary-LSA (AS boundary router) database"; } show ospf4 database asbrsummary brief { %command: "ospf_print_lsas -b -f 4" %help: HELP; %module: ospf4; %tag: HELP "Display brief output (default)"; } show ospf4 database asbrsummary detail { %command: "ospf_print_lsas -d -f 4" %help: HELP; %module: ospf4; %tag: HELP "Display detailed output"; } /* AS-External-LSA */ show ospf4 database external { %command: "ospf_print_lsas -b -f 5" %help: HELP; %module: ospf4; %tag: HELP "Show External-LSA database"; } show ospf4 database external brief { %command: "ospf_print_lsas -b -f 5" %help: HELP; %module: ospf4; %tag: HELP "Display brief output (default)"; } show ospf4 database external detail { %command: "ospf_print_lsas -d -f 5" %help: HELP; %module: ospf4; %tag: HELP "Display detailed output"; } /* NSSA-LSA */ show ospf4 database nssa { %command: "ospf_print_lsas -b -f 7" %help: HELP; %module: ospf4; %tag: HELP "Show NSSA-LSA database"; } show ospf4 database nssa brief { %command: "ospf_print_lsas -b -f 7" %help: HELP; %module: ospf4; %tag: HELP "Display brief output (default)"; } show ospf4 database nssa detail { %command: "ospf_print_lsas -d -f 7" %help: HELP; %module: ospf4; %tag: HELP "Display detailed output"; } /* AREA */ show ospf4 database area $(protocols.ospf4.area.*) { %command: "ospf_print_lsas -a $5 -b" %help: HELP; %module: ospf4; %tag: HELP "Show LSA database"; } show ospf4 database area $(protocols.ospf4.area.*) brief { %command: "ospf_print_lsas -a $5 -b" %help: HELP; %module: ospf4; %tag: HELP "Display brief output (default)"; } show ospf4 database area $(protocols.ospf4.area.*) detail { %command: "ospf_print_lsas -a $5 -d" %help: HELP; %module: ospf4; %tag: HELP "Display detailed output"; } /* Router-LSA */ show ospf4 database area $(protocols.ospf4.area.*) router { %command: "ospf_print_lsas -a $5 -b -f 1" %help: HELP; %module: ospf4; %tag: HELP "Show Router-LSA database"; } show ospf4 database area $(protocols.ospf4.area.*) router brief { %command: "ospf_print_lsas -a $5 -b -f 1" %help: HELP; %module: ospf4; %tag: HELP "Display brief output (default)"; } show ospf4 database area $(protocols.ospf4.area.*) router detail { %command: "ospf_print_lsas -a $5 -d -f 1" %help: HELP; %module: ospf4; %tag: HELP "Display detailed output"; } /* Network-LSA */ show ospf4 database area $(protocols.ospf4.area.*) network { %command: "ospf_print_lsas -a $5 -b -f 2" %help: HELP; %module: ospf4; %tag: HELP "Show Network-LSA database"; } show ospf4 database area $(protocols.ospf4.area.*) network brief { %command: "ospf_print_lsas -a $5 -b -f 2" %help: HELP; %module: ospf4; %tag: HELP "Display brief output (default)"; } show ospf4 database area $(protocols.ospf4.area.*) network detail { %command: "ospf_print_lsas -a $5 -d -f 2" %help: HELP; %module: ospf4; %tag: HELP "Display detailed output"; } /* Summary-LSA (network) */ show ospf4 database area $(protocols.ospf4.area.*) netsummary { %command: "ospf_print_lsas -a $5 -b -f 3" %help: HELP; %module: ospf4; %tag: HELP "Show Summary-LSA (network) database"; } show ospf4 database area $(protocols.ospf4.area.*) netsummary brief { %command: "ospf_print_lsas -a $5 -b -f 3" %help: HELP; %module: ospf4; %tag: HELP "Display brief output (default)"; } show ospf4 database area $(protocols.ospf4.area.*) netsummary detail { %command: "ospf_print_lsas -a $5 -d -f 3" %help: HELP; %module: ospf4; %tag: HELP "Display detailed output"; } /* Summary-LSA (AS boundary router) */ show ospf4 database area $(protocols.ospf4.area.*) asbrsummary { %command: "ospf_print_lsas -a $5 -b -f 4" %help: HELP; %module: ospf4; %tag: HELP "Show Summary-LSA (AS boundary router) database"; } show ospf4 database area $(protocols.ospf4.area.*) asbrsummary brief { %command: "ospf_print_lsas -a $5 -b -f 4" %help: HELP; %module: ospf4; %tag: HELP "Display brief output (default)"; } show ospf4 database area $(protocols.ospf4.area.*) asbrsummary detail { %command: "ospf_print_lsas -a $5 -d -f 4" %help: HELP; %module: ospf4; %tag: HELP "Display detailed output"; } /* AS-External-LSA */ show ospf4 database area $(protocols.ospf4.area.*) external { %command: "ospf_print_lsas -a $5 -b -f 5" %help: HELP; %module: ospf4; %tag: HELP "Show External-LSA database"; } show ospf4 database area $(protocols.ospf4.area.*) external brief { %command: "ospf_print_lsas -a $5 -b -f 5" %help: HELP; %module: ospf4; %tag: HELP "Display brief output (default)"; } show ospf4 database area $(protocols.ospf4.area.*) external detail { %command: "ospf_print_lsas -a $5 -d -f 5" %help: HELP; %module: ospf4; %tag: HELP "Display detailed output"; } /* NSSA-LSA */ show ospf4 database area $(protocols.ospf4.area.*) nssa { %command: "ospf_print_lsas -a $5 -b -f 7" %help: HELP; %module: ospf4; %tag: HELP "Show NSSA-LSA database"; } show ospf4 database area $(protocols.ospf4.area.*) nssa brief { %command: "ospf_print_lsas -a $5 -b -f 7" %help: HELP; %module: ospf4; %tag: HELP "Display brief output (default)"; } show ospf4 database area $(protocols.ospf4.area.*) nssa detail { %command: "ospf_print_lsas -a $5 -d -f 7" %help: HELP; %module: ospf4; %tag: HELP "Display detailed output"; } /* SUMMARY */ show ospf4 database summary { %command: "ospf_print_lsas -s" %help: HELP; %module: ospf4; %tag: HELP "Display summary output"; } show ospf4 database summary router { %command: "ospf_print_lsas -s -f 1" %help: HELP; %module: ospf4; %tag: HELP "Show Router-LSA database"; } show ospf4 database summary network { %command: "ospf_print_lsas -s -f 2" %help: HELP; %module: ospf4; %tag: HELP "Show Network-LSA database"; } show ospf4 database summary netsummary { %command: "ospf_print_lsas -s -f 3" %help: HELP; %module: ospf4; %tag: HELP "Show Summary-LSA (network) database"; } show ospf4 database summary asbrsummary { %command: "ospf_print_lsas -s -f 4" %help: HELP; %module: ospf4; %tag: HELP "Show Summary-LSA (AS boundary router) database"; } show ospf4 database summary external { %command: "ospf_print_lsas -s -f 5" %help: HELP; %module: ospf4; %tag: HELP "Show External-LSA database"; } show ospf4 database summary nssa { %command: "ospf_print_lsas -s -f 7" %help: HELP; %module: ospf4; %tag: HELP "Show NSSA-LSA database"; } show ospf4 database summary area $(protocols.ospf4.area.*) { %command: "ospf_print_lsas -a $6 -s" %help: HELP; %module: ospf4; %tag: HELP "Display summary output"; } show ospf4 database summary area $(protocols.ospf4.area.*) router { %command: "ospf_print_lsas -a $6 -s -f 1" %help: HELP; %module: ospf4; %tag: HELP "Show Router-LSA database"; } show ospf4 database summary area $(protocols.ospf4.area.*) network { %command: "ospf_print_lsas -a $6 -s -f 2" %help: HELP; %module: ospf4; %tag: HELP "Show Network-LSA database"; } show ospf4 database summary area $(protocols.ospf4.area.*) netsummary { %command: "ospf_print_lsas -a $6 -s -f 3" %help: HELP; %module: ospf4; %tag: HELP "Show Summary-LSA (network) database"; } show ospf4 database summary area $(protocols.ospf4.area.*) asbrsummary { %command: "ospf_print_lsas -a $6 -s -f 4" %help: HELP; %module: ospf4; %tag: HELP "Show Summary-LSA (AS boundary router) database"; } show ospf4 database summary area $(protocols.ospf4.area.*) external { %command: "ospf_print_lsas -a $6 -s -f 5" %help: HELP; %module: ospf4; %tag: HELP "Show External-LSA database"; } show ospf4 database summary area $(protocols.ospf4.area.*) nssa { %command: "ospf_print_lsas -a $6 -s -f 7" %help: HELP; %module: ospf4; %tag: HELP "Show NSSA-LSA database"; } /* Neighbor commands */ show ospf4 neighbor { %command: "ospf_print_neighbours" %help: HELP; %module: ospf4; %tag: HELP "Show Neighbors"; } show ospf4 neighbor brief { %command: "ospf_print_neighbours -b" %help: HELP; %module: ospf4; %tag: HELP "Show Neighbors"; } show ospf4 neighbor detail { %command: "ospf_print_neighbours -d" %help: HELP; %module: ospf4; %tag: HELP "Show Neighbors"; } show ospf4 neighbor { %command: "ospf_print_neighbours -f $4" %help: HELP; %module: ospf4; %tag: HELP "Show Neighbors"; } show ospf4 neighbor brief { %command: "ospf_print_neighbours -b -f $4" %help: HELP; %module: ospf4; %tag: HELP "Show Neighbors"; } show ospf4 neighbor detail { %command: "ospf_print_neighbours -d -f $4" %help: HELP; %module: ospf4; %tag: HELP "Show Neighbors"; } xorp/etc/templates/ripng.cmds0000664000076400007640000000267711421137511016430 0ustar greearbgreearb/* $XORP$ */ show ripng { %command: "" %help: HELP; %module: ripng; %tag: HELP "Display information about RIPng"; } show ripng peer statistics $(protocols.ripng.interface.*) $(protocols.ripng.interface.*.vif.*) $(protocols.ripng.interface.*.vif.*.address.*) { %command: "rip_show_peer_stats $2 $5 $6 $7" %help: HELP; %module: ripng; %tag: HELP "Show RIPng statistics for peers on specified address"; } show ripng peer statistics all { %command: "rip_show_peer_stats $2" %help: HELP; %module: ripng; %tag: HELP "Show RIPng statistics for all peers"; } show ripng statistics $(protocols.ripng.interface.*) $(protocols.ripng.interface.*.vif.*) $(protocols.ripng.interface.*.vif.*.address.*) { %command: "rip_show_stats $2 $4 $5 $6" %help: HELP; %module: ripng; %tag: HELP "Show RIPng statistics on specified address"; } show ripng statistics all { %command: "rip_show_stats $2" %help: HELP; %module: ripng; %tag: HELP "Show RIPng statistics for all addresses"; } show ripng status $(protocols.ripng.interface.*) $(protocols.ripng.interface.*.vif.*) $(protocols.ripng.interface.*.vif.*.address.*) { %command: "rip_show_stats -b $2 $4 $5 $6" %help: HELP; %module: ripng; %tag: HELP "Show RIPng status on specified address"; } show ripng status all { %command: "rip_show_stats -b $2" %help: HELP; %module: ripng; %tag: HELP "Show RIPng status for all addresses"; } xorp/etc/templates/vrrp.cmds0000664000076400007640000000243111421137511016266 0ustar greearbgreearb/* $XORP$ */ show vrrp { %command: "cli_generic -i %i=finder://vrrp/vrrp/0.1/get_ifs -i %v=finder://vrrp/vrrp/0.1/get_vifs?ifname:txt=%i -i %k=finder://vrrp/vrrp/0.1/get_vrids?ifname:txt=%i&vifname:txt=%v -x finder://vrrp/vrrp/0.1/get_vrid_info?ifname:txt=%i&vifname:txt=%v&vrid:u32=%k -f Interface\\t%i\\nVif\\t\\t%v\\nVRID\\t\\t%k\\nState\\t\\t%0\\nMaster_IP\\t%1\\n -s \\n" %help: HELP; %module: vrrp; %tag: HELP "Show configured vrids"; } show vrrp $(protocols.vrrp.interface.*) $(protocols.vrrp.interface.*.vif.*) { %command: "cli_generic -i %k=finder://vrrp/vrrp/0.1/get_vrids?ifname:txt=$3&vifname:txt=$4 -a $3 -a $4 -x finder://vrrp/vrrp/0.1/get_vrid_info?ifname:txt=$3&vifname:txt=$4&vrid:u32=%k -a $3 -a $4 -f Interface\\t$3\\nVif\\t\\t$4\\nVRID\\t\\t%k\\nState\\t\\t%0\\nMaster_IP\\t%1\\n -a $3 -a $4 -s \\n" %help: HELP; %module: vrrp; %tag: HELP "Show configured vrids on an interface"; } show vrrp $(protocols.vrrp.interface.*) $(protocols.vrrp.interface.*.vif.*) $(protocols.vrrp.interface.*.vif.*.vrid.*) { %command: "cli_generic -x finder://vrrp/vrrp/0.1/get_vrid_info?ifname:txt=$3&vifname:txt=$4&vrid:u32=$5 -a $3 -a $4 -a $5 -f VRID\\t\\t$5\\nState\\t\\t%0\\nMaster_IP\\t%1\\n -a $5" %help: HELP; %module: vrrp; %tag: HELP "Show vrid details"; } xorp/etc/templates/pimsm6.tp0000664000076400007640000003433711421137511016217 0ustar greearbgreearb/* $XORP: xorp/etc/templates/pimsm6.tp,v 1.34 2008/05/01 22:49:50 bms Exp $ */ protocols { pimsm6 { targetname: txt = "PIMSM_6"; disable: toggle = false; enabled: bool; /* %deprecated */ interface @: txt { vif @: txt { disable: toggle = false; enabled: bool; /* %deprecated */ enable-ip-router-alert-option-check: bool; /* %deprecated */ dr-priority: u32 = 1; hello-period: u32 = 30; hello-triggered-delay: u32 = 5; alternative-subnet @: ipv6net { } } } static-rps { rp @: ipv6 { group-prefix @: ipv6net { rp-priority: u32 = 192; hash-mask-len: u32 = 126; } } } bootstrap { disable: toggle = false; enabled: bool; /* %deprecated */ cand-bsr { scope-zone @: ipv6net { is-scope-zone: bool = false; cand-bsr-by-vif-name: txt; cand-bsr-by-vif-addr: ipv6 = ::; bsr-priority: u32 = 1; hash-mask-len: u32 = 126; } } cand-rp { group-prefix @: ipv6net { is-scope-zone: bool = false; cand-rp-by-vif-name: txt; cand-rp-by-vif-addr: ipv6 = ::; rp-priority: u32 = 192; rp-holdtime: u32 = 150; } } } switch-to-spt-threshold { disable: toggle = false; enabled: bool; /* %deprecated */ interval: u32 = 100; bytes: u32 = 0; } traceoptions { flag { all { disable: toggle = false; enabled: bool; /* %deprecated */ } } } } } protocols { pimsm6 { %help: short "Configure the IPv6 PIM-SM protocol"; %modinfo: provides pimsm6; %modinfo: depends mfea6; %modinfo: depends mld; %modinfo: depends rib; %modinfo: path "xorp_pimsm6"; %modinfo: default_targetname "pim"; %modinfo: status_method xrl "$(pimsm6.targetname)/common/0.1/get_status->status:u32&reason:txt"; %modinfo: startup_method xrl "$(pimsm6.targetname)/common/0.1/startup"; %modinfo: shutdown_method xrl "$(pimsm6.targetname)/common/0.1/shutdown"; %mandatory: $(@.targetname); targetname { %user-hidden: "XRL target name"; %help: short "XRL target name"; %set:; } disable { %help: short "Disable the IPv6 PIM-SM protocol"; %create:; %set: xrl "$(pimsm6.targetname)/pim/0.1/enable_pim?enable:bool=`~$(@)`"; %delete: xrl "$(pimsm6.targetname)/pim/0.1/enable_pim?enable:bool=`~$(DEFAULT)`"; } enabled { %deprecated: "Statement 'enabled: true/false' is replaced with 'disable: false/true'"; %help: short "Enable the IPv6 PIM-SM protocol"; %create:; %set: xrl "$(pimsm6.targetname)/pim/0.1/enable_pim?enable:bool=$(@)"; } interface @ { %help: short "Configure IPv6 PIM-SM on a network interface"; %delete: xrl "$(pimsm6.targetname)/pim/0.1/stop_vif?vif_name:txt=$(interface.@)"; vif @ { %help: short "Configure IPv6 PIM-SM on a virtual interface"; %activate: xrl "$(pimsm6.targetname)/pim/0.1/start_vif?vif_name:txt=$(vif.@)"; %update: xrl "$(pimsm6.targetname)/pim/0.1/start_vif?vif_name:txt=$(vif.@)"; %delete: xrl "$(pimsm6.targetname)/pim/0.1/stop_vif?vif_name:txt=$(vif.@)"; disable { %help: short "Disable IPv6 PIM-SM on an interface"; %set: xrl "$(pimsm6.targetname)/pim/0.1/enable_vif?vif_name:txt=$(vif.@)&enable:bool=`~$(@)`"; %delete: xrl "$(pimsm6.targetname)/pim/0.1/enable_vif?vif_name:txt=$(vif.@)&enable:bool=`~$(DEFAULT)`"; } enabled { %deprecated: "Statement 'enabled: true/false' is replaced with 'disable: false/true'"; %help: short "Enable IPv6 PIM-SM on an interface"; %set: xrl "$(pimsm6.targetname)/pim/0.1/enable_vif?vif_name:txt=$(vif.@)&enable:bool=$(@)"; %delete: xrl "$(pimsm6.targetname)/pim/0.1/enable_vif?vif_name:txt=$(vif.@)&enable:bool=`~$(DEFAULT)`"; } enable-ip-router-alert-option-check { %deprecated: "IP Router Alert is no longer required for PIM"; %help: short "Enable the IP Router Alert option check"; %create:; %set:; %delete:; } dr-priority { %help: short "Set the Designated Router election priority"; %create: xrl "$(pimsm6.targetname)/pim/0.1/set_vif_dr_priority?vif_name:txt=$(vif.@)&dr_priority:u32=$(@)"; %set: xrl "$(pimsm6.targetname)/pim/0.1/set_vif_dr_priority?vif_name:txt=$(vif.@)&dr_priority:u32=$(@)"; %delete: xrl "$(pimsm6.targetname)/pim/0.1/set_vif_dr_priority?vif_name:txt=$(vif.@)&dr_priority:u32=$(DEFAULT)"; } hello-period { %help: short "Set the Hello messages period (in seconds)"; %allow-range: $(@) "1" "18724" %help: "The Hello messages period (in seconds)"; %create: xrl "$(pimsm6.targetname)/pim/0.1/set_vif_hello_period?vif_name:txt=$(vif.@)&hello_period:u32=$(@)"; %set: xrl "$(pimsm6.targetname)/pim/0.1/set_vif_hello_period?vif_name:txt=$(vif.@)&hello_period:u32=$(@)"; %delete: xrl "$(pimsm6.targetname)/pim/0.1/set_vif_hello_period?vif_name:txt=$(vif.@)&hello_period:u32=$(DEFAULT)"; } hello-triggered-delay { %help: short "Set the randomized triggered delay of the Hello messages (in seconds)"; %allow-range: $(@) "1" "255" %help: "The randomized triggered delay of the Hello messages (in seconds)"; %create: xrl "$(pimsm6.targetname)/pim/0.1/set_vif_hello_triggered_delay?vif_name:txt=$(vif.@)&hello_triggered_delay:u32=$(@)"; %set: xrl "$(pimsm6.targetname)/pim/0.1/set_vif_hello_triggered_delay?vif_name:txt=$(vif.@)&hello_triggered_delay:u32=$(@)"; %delete: xrl "$(pimsm6.targetname)/pim/0.1/set_vif_hello_triggered_delay?vif_name:txt=$(vif.@)&hello_triggered_delay:u32=$(DEFAULT)"; } alternative-subnet @ { %help: short "Fake a subnet to appear directly-connected"; %create: xrl "$(pimsm6.targetname)/pim/0.1/add_alternative_subnet6?vif_name:txt=$(vif.@)&subnet:ipv6net=$(alternative-subnet.@)"; %delete: xrl "$(pimsm6.targetname)/pim/0.1/delete_alternative_subnet6?vif_name:txt=$(vif.@)&subnet:ipv6net=$(alternative-subnet.@)"; } } } static-rps { %help: short "Configure the set of static RPs"; %activate: xrl "$(pimsm6.targetname)/pim/0.1/config_static_rp_done"; %update: xrl "$(pimsm6.targetname)/pim/0.1/config_static_rp_done"; %delete: xrl "$(pimsm6.targetname)/pim/0.1/delete_config_all_static_rps"; %delete: xrl "$(pimsm6.targetname)/pim/0.1/config_static_rp_done"; rp @ { %help: short "Configure a static RP"; %delete: xrl "$(pimsm6.targetname)/pim/0.1/delete_config_all_static_group_prefixes_rp6?rp_addr:ipv6=$(rp.@)"; %delete: xrl "$(pimsm6.targetname)/pim/0.1/config_static_rp_done"; group-prefix @ { %help: short "Configure the group prefix address covered by the RP"; %create: xrl "$(pimsm6.targetname)/pim/0.1/add_config_static_rp6?group_prefix:ipv6net=$(group-prefix.@)&rp_addr:ipv6=$(rp.@)&rp_priority:u32=$(@.rp-priority)&hash_mask_len:u32=$(@.hash-mask-len)"; %delete: xrl "$(pimsm6.targetname)/pim/0.1/delete_config_static_rp6?group_prefix:ipv6net=$(group-prefix.@)&rp_addr:ipv6=$(rp.@)"; %delete: xrl "$(pimsm6.targetname)/pim/0.1/config_static_rp_done"; rp-priority { %help: short "Configure the RP priority"; %allow-range: $(@) "0" "255" %help: "The RP priority"; %create:; %set: xrl "$(pimsm6.targetname)/pim/0.1/add_config_static_rp6?group_prefix:ipv6net=$(group-prefix.@)&rp_addr:ipv6=$(rp.@)&rp_priority:u32=$(group-prefix.@.rp-priority)&hash_mask_len:u32=$(group-prefix.@.hash-mask-len)"; %delete: xrl "$(pimsm6.targetname)/pim/0.1/add_config_static_rp6?group_prefix:ipv6net=$(group-prefix.@)&rp_addr:ipv6=$(rp.@)&rp_priority:u32=$(DEFAULT)&hash_mask_len:u32=$(group-prefix.@.hash-mask-len)"; %delete: xrl "$(pimsm6.targetname)/pim/0.1/config_static_rp_done"; } hash-mask-len { %help: short "Configure the hash mask length for the hash function"; %allow-range: $(@) "8" "128" %help: "The hash mask length for the hash function"; %create:; %set: xrl "$(pimsm6.targetname)/pim/0.1/add_config_static_rp6?group_prefix:ipv6net=$(group-prefix.@)&rp_addr:ipv6=$(rp.@)&rp_priority:u32=$(group-prefix.@.rp-priority)&hash_mask_len:u32=$(group-prefix.@.hash-mask-len)"; %delete: xrl "$(pimsm6.targetname)/pim/0.1/add_config_static_rp6?group_prefix:ipv6net=$(group-prefix.@)&rp_addr:ipv6=$(rp.@)&rp_priority:u32=$(group-prefix.@.rp-priority)&hash_mask_len:u32=$(DEFAULT)"; %delete: xrl "$(pimsm6.targetname)/pim/0.1/config_static_rp_done"; } } } } bootstrap { %help: short "Configure the IPv6 Bootstrap mechanism"; %activate: xrl "$(pimsm6.targetname)/pim/0.1/stop_bsr"; %activate: xrl "$(pimsm6.targetname)/pim/0.1/start_bsr"; %update: xrl "$(pimsm6.targetname)/pim/0.1/apply_bsr_changes"; %delete: xrl "$(pimsm6.targetname)/pim/0.1/stop_bsr"; disable { %help: short "Disable the IPv6 Bootstrap mechanism"; %create: xrl "$(pimsm6.targetname)/pim/0.1/enable_bsr?enable:bool=`~$(@)`"; %set: xrl "$(pimsm6.targetname)/pim/0.1/enable_bsr?enable:bool=`~$(@)`"; %delete: xrl "$(pimsm6.targetname)/pim/0.1/enable_bsr?enable:bool=`~$(DEFAULT)`"; } enabled { %deprecated: "Statement 'enabled: true/false' is replaced with 'disable: false/true'"; %help: short "Enable the IPv6 Bootstrap mechanism"; %create: xrl "$(pimsm6.targetname)/pim/0.1/enable_bsr?enable:bool=$(@)"; %set: xrl "$(pimsm6.targetname)/pim/0.1/enable_bsr?enable:bool=$(@)"; } cand-bsr { %help: short "Configure this router as a Candidate-BSR"; scope-zone @ { %help: short "Configure a scope zone in this Candidate-BSR"; %mandatory: $(@.cand-bsr-by-vif-name); %create: xrl "$(pimsm6.targetname)/pim/0.1/add_config_cand_bsr6?scope_zone_id:ipv6net=$(scope-zone.@)&is_scope_zone:bool=$(@.is-scope-zone)&vif_name:txt=$(@.cand-bsr-by-vif-name)&vif_addr:ipv6=$(@.cand-bsr-by-vif-addr)&bsr_priority:u32=$(@.bsr-priority)&hash_mask_len:u32=$(@.hash-mask-len)"; %update: xrl "$(pimsm6.targetname)/pim/0.1/add_config_cand_bsr6?scope_zone_id:ipv6net=$(scope-zone.@)&is_scope_zone:bool=$(@.is-scope-zone)&vif_name:txt=$(@.cand-bsr-by-vif-name)&vif_addr:ipv6=$(@.cand-bsr-by-vif-addr)&bsr_priority:u32=$(@.bsr-priority)&hash_mask_len:u32=$(@.hash-mask-len)"; %delete: xrl "$(pimsm6.targetname)/pim/0.1/delete_config_cand_bsr6?scope_zone_id:ipv6net=$(scope-zone.@)&is_scope_zone:bool=$(@.is-scope-zone)"; is-scope-zone { %help: short "Flag to indicate a scoped or global zone"; %set:; } cand-bsr-by-vif-name { %help: short "Specify the virtual interface with the address of the Candidate-BSR"; %set:; } cand-bsr-by-vif-addr { %help: short "Specify the virtual interface's address to use as the Candidate-BSR"; %set:; } bsr-priority { %help: short "Configure the Candidate-BSR priority"; %allow-range: $(@) "0" "255" %help: "The Candidate-BSR priority"; %set:; } hash-mask-len { %help: short "Configure the hash mask length for the hash function"; %allow-range: $(@) "8" "128" %help: "The hash mask length for the hash function"; %set:; } } } cand-rp { %help: short "Configure this router as a Candidate-RP"; group-prefix @ { %help: short "Configure a scope zone in this Candidate-RP"; %mandatory: $(@.cand-rp-by-vif-name); %create: xrl "$(pimsm6.targetname)/pim/0.1/add_config_cand_rp6?group_prefix:ipv6net=$(group-prefix.@)&is_scope_zone:bool=$(@.is-scope-zone)&vif_name:txt=$(@.cand-rp-by-vif-name)&vif_addr:ipv6=$(@.cand-rp-by-vif-addr)&rp_priority:u32=$(@.rp-priority)&rp_holdtime:u32=$(@.rp-holdtime)"; %update: xrl "$(pimsm6.targetname)/pim/0.1/add_config_cand_rp6?group_prefix:ipv6net=$(group-prefix.@)&is_scope_zone:bool=$(@.is-scope-zone)&vif_name:txt=$(@.cand-rp-by-vif-name)&vif_addr:ipv6=$(@.cand-rp-by-vif-addr)&rp_priority:u32=$(@.rp-priority)&rp_holdtime:u32=$(@.rp-holdtime)"; %delete: xrl "$(pimsm6.targetname)/pim/0.1/delete_config_cand_rp6?group_prefix:ipv6net=$(group-prefix.@)&is_scope_zone:bool=$(@.is-scope-zone)&vif_name:txt=$(@.cand-rp-by-vif-name)&vif_addr:ipv6=$(@.cand-rp-by-vif-addr)"; is-scope-zone { %help: short "Flag to indicate a scoped or global zone"; %set:; } cand-rp-by-vif-name { %help: short "Specify the virtual interface with the address of the Candidate-RP"; %set:; } cand-rp-by-vif-addr { %help: short "Specify the virtual interface's address to use as the Candidate-RP"; %set:; } rp-priority { %help: short "Configure the Candidate-RP priority"; %allow-range: $(@) "0" "255" %help: "The Candidate-RP priority"; %set:; } rp-holdtime { %help: short "Configure the Candidate-RP holdtime"; %allow-range: $(@) "0" "65535" %help: "The Candidate-RP holdtime (in seconds)"; %set:; } } } } switch-to-spt-threshold { %help: short "Configure the shortest-path switch threshold"; %activate: xrl "$(pimsm6.targetname)/pim/0.1/set_switch_to_spt_threshold?is_enabled:bool=`~$(@.disable)`&interval_sec:u32=$(@.interval)&bytes:u32=$(@.bytes)"; %update: xrl "$(pimsm6.targetname)/pim/0.1/set_switch_to_spt_threshold?is_enabled:bool=`~$(@.disable)`&interval_sec:u32=$(@.interval)&bytes:u32=$(@.bytes)"; %delete: xrl "$(pimsm6.targetname)/pim/0.1/reset_switch_to_spt_threshold"; disable { %help: short "Disable the shortest-path switching"; %set:; } enabled { %deprecated: "Statement 'enabled: true/false' is replaced with 'disable: false/true'"; %help: short "Enable the shortest-path switching"; %set:; } interval { %help: short "The frequency of measuring the bandwidth threshold (sec)"; %allow-range: $(@) "3" "2147483647" %help: "The frequency of measuring the bandwidth threshold (sec)"; %set:; } bytes { %help: short "The bandwidth threshold (in bytes) per interval"; %set:; } } traceoptions { %help: short "Configure the tracing options"; flag { %help: short "Configure the tracing operation to perform"; all { %help: short "Configure all tracing operations"; disable { %help: short "Disable all tracing operations"; %set: xrl "$(pimsm6.targetname)/pim/0.1/log_trace_all?enable:bool=`~$(@)`"; %delete: xrl "$(pimsm6.targetname)/pim/0.1/log_trace_all?enable:bool=$(DEFAULT)"; } enabled { %deprecated: "Statement 'enabled: true/false' is replaced with 'disable: false/true'"; %help: short "Enable all tracing operations"; %set: xrl "$(pimsm6.targetname)/pim/0.1/log_trace_all?enable:bool=$(@)"; } } } } } } xorp/etc/templates/mfea6.tp0000664000076400007640000000667711421137511016010 0ustar greearbgreearb/* $XORP: xorp/etc/templates/mfea6.tp,v 1.15 2006/04/19 19:59:26 pavlin Exp $ */ plumbing { mfea6 { targetname: txt = "MFEA_6"; disable: toggle = false; enabled: bool; /* %deprecated */ interface @: txt { vif @: txt { disable: toggle = false; enabled: bool; /* %deprecated */ } } traceoptions { flag { all { disable: toggle = false; enabled: bool; /* %deprecated */ } } } } } plumbing { mfea6 { %help: short "Configure the IPv6 Multicast Forwarding Engine Abstraction"; %modinfo: provides mfea6; %modinfo: depends fea; %modinfo: path "xorp_fea"; %modinfo: default_targetname "mfea"; %modinfo: status_method xrl "$(mfea6.targetname)/common/0.1/get_status->status:u32&reason:txt"; %modinfo: startup_method xrl "$(mfea6.targetname)/common/0.1/startup"; %modinfo: shutdown_method xrl "$(mfea6.targetname)/common/0.1/shutdown"; %mandatory: $(@.targetname); /* TODO: remove this? */ /* %activate: xrl "$(mfea6.targetname)/mfea/0.1/start_mfea"; %update: xrl "$(mfea6.targetname)/mfea/0.1/start_mfea"; */ targetname { %user-hidden: "XRL target name"; %help: short "XRL target name"; %set:; } disable { %help: short "Disable the IPv6 MFEA"; %create:; %set: xrl "$(mfea6.targetname)/mfea/0.1/enable_mfea?enable:bool=`~$(@)`"; %delete: xrl "$(mfea6.targetname)/mfea/0.1/enable_mfea?enable:bool=`~$(DEFAULT)`"; } enabled { %deprecated: "Statement 'enabled: true/false' is replaced with 'disable: false/true'"; %help: short "Enable the IPv6 MFEA"; %create:; %set: xrl "$(mfea6.targetname)/mfea/0.1/enable_mfea?enable:bool=$(@)"; } interface @ { %help: short "Configure IPv6 MFEA on a network interface"; %delete: xrl "$(mfea6.targetname)/mfea/0.1/stop_vif?vif_name:txt=$(interface.@)"; vif @ { %help: short "Configure IPv6 MFEA on a virtual interface"; %activate: xrl "$(mfea6.targetname)/mfea/0.1/start_vif?vif_name:txt=$(vif.@)"; %update: xrl "$(mfea6.targetname)/mfea/0.1/start_vif?vif_name:txt=$(vif.@)"; %delete: xrl "$(mfea6.targetname)/mfea/0.1/stop_vif?vif_name:txt=$(vif.@)"; disable { %help: short "Disable IPv6 MFEA on an interface"; %set: xrl "$(mfea6.targetname)/mfea/0.1/enable_vif?vif_name:txt=$(vif.@)&enable:bool=`~$(@)`"; %delete: xrl "$(mfea6.targetname)/mfea/0.1/enable_vif?vif_name:txt=$(vif.@)&enable:bool=`~$(DEFAULT)`"; } enabled { %deprecated: "Statement 'enabled: true/false' is replaced with 'disable: false/true'"; %help: short "Enable IPv6 MFEA on an interface"; %set: xrl "$(mfea6.targetname)/mfea/0.1/enable_vif?vif_name:txt=$(vif.@)&enable:bool=$(@)"; %delete: xrl "$(mfea6.targetname)/mfea/0.1/enable_vif?vif_name:txt=$(vif.@)&enable:bool=`~$(DEFAULT)`"; } } } traceoptions { %help: short "Configure the tracing options"; flag { %help: short "Configure the tracing operation to perform"; all { %help: short "Configure all tracing operations"; disable { %help: short "Disable all tracing operations"; %set: xrl "$(mfea6.targetname)/mfea/0.1/log_trace_all?enable:bool=`~$(@)`"; %delete: xrl "$(mfea6.targetname)/mfea/0.1/log_trace_all?enable:bool=$(DEFAULT)"; } enabled { %deprecated: "Statement 'enabled: true/false' is replaced with 'disable: false/true'"; %help: short "Enable all tracing operations"; %set: xrl "$(mfea6.targetname)/mfea/0.1/log_trace_all?enable:bool=$(@)"; } } } } } } xorp/etc/templates/policy.cmds0000664000076400007640000000562211421137511016601 0ustar greearbgreearb/* $XORP */ test { %command: "" %help: HELP; /* * TODO: XXX: Remove the "policy" dependency if the "test" node is * needed by some other module. */ %module: policy; %tag: HELP "Test operation"; } test policy { %command: "" %help: HELP; %module: policy; %tag: HELP "Test routing policies"; } test policy $(policy.policy-statement.*) { %command: "" %help: HELP; %module: policy; %tag: HELP "Test a routing policy"; } test policy $(policy.policy-statement.*) { %command: "cli_send_processor_xrl -t policy -- test $3 $4" %help: HELP; %module: policy; %tag: HELP "Test a routing policy on a prefix"; } test policy $(policy.policy-statement.*) { %command: "cli_send_processor_xrl -t policy -- test $3 $4 $5" %help: HELP; %module: policy; %tag: HELP "Test a routing policy on a prefix and route attribute"; } show policy { %command: "" %help: HELP; %module: policy; %tag: HELP "Show policy information"; } show policy network4-list { %command: "cli_send_processor_xrl -t policy -- show set_ipv4net" %help: HELP; %module: policy; %tag: HELP "Show policy network4 lists"; } show policy network4-list $(policy.network4-list.*) { %command: "cli_send_processor_xrl -t policy -- show set_ipv4net $4" %help: HELP; %module: policy; %tag: HELP "Show policy network4 list"; } show policy network6-list { %command: "cli_send_processor_xrl -t policy -- show set_ipv6net" %help: HELP; %module: policy; %tag: HELP "Show policy network6 lists"; } show policy network6-list $(policy.network6-list.*) { %command: "cli_send_processor_xrl -t policy -- show set_ipv6net $4" %help: HELP; %module: policy; %tag: HELP "Show policy network6 list"; } show policy community-list { %command: "cli_send_processor_xrl -t policy -- show set_com32" %help: HELP; %module: policy; %tag: HELP "Show policy community lists"; } show policy community-list $(policy.community-list.*) { %command: "cli_send_processor_xrl -t policy -- show set_com32 $4" %help: HELP; %module: policy; %tag: HELP "Show policy community list"; } show policy as-path-list { %command: "cli_send_processor_xrl -t policy -- show set_str" %help: HELP; %module: policy; %tag: HELP "Show policy AS path lists"; } show policy as-path-list $(policy.as-path-list.*) { %command: "cli_send_processor_xrl -t policy -- show set_str $4" %help: HELP; %module: policy; %tag: HELP "Show policy AS path list"; } show policy policy-statement { %command: "cli_send_processor_xrl -t policy -- show policy-statement" %help: HELP; %module: policy; %tag: HELP "Show policy statements"; } show policy policy-statement $(policy.policy-statement.*) { %command: "cli_send_processor_xrl -t policy -- show policy-statement $4" %help: HELP; %module: policy; %tag: HELP "Show policy statement"; } xorp/etc/templates/xorpsh.cmds0000664000076400007640000000031211703343670016624 0ustar greearbgreearb show { %command: "" %help: HELP; %tag: HELP "Display information about the system"; } show version { %command: "echo Version 1.8.5" %help: HELP; %tag: HELP "Display system version"; } xorp/etc/templates/mfea4.tp0000664000076400007640000000667711421137511016006 0ustar greearbgreearb/* $XORP: xorp/etc/templates/mfea4.tp,v 1.15 2006/04/19 19:59:26 pavlin Exp $ */ plumbing { mfea4 { targetname: txt = "MFEA_4"; disable: toggle = false; enabled: bool; /* %deprecated */ interface @: txt { vif @: txt { disable: toggle = false; enabled: bool; /* %deprecated */ } } traceoptions { flag { all { disable: toggle = false; enabled: bool; /* %deprecated */ } } } } } plumbing { mfea4 { %help: short "Configure the IPv4 Multicast Forwarding Engine Abstraction"; %modinfo: provides mfea4; %modinfo: depends fea; %modinfo: path "xorp_fea"; %modinfo: default_targetname "mfea"; %modinfo: status_method xrl "$(mfea4.targetname)/common/0.1/get_status->status:u32&reason:txt"; %modinfo: startup_method xrl "$(mfea4.targetname)/common/0.1/startup"; %modinfo: shutdown_method xrl "$(mfea4.targetname)/common/0.1/shutdown"; %mandatory: $(@.targetname); /* TODO: remove this? */ /* %activate: xrl "$(mfea4.targetname)/mfea/0.1/start_mfea"; %update: xrl "$(mfea4.targetname)/mfea/0.1/start_mfea"; */ targetname { %user-hidden: "XRL target name"; %help: short "XRL target name"; %set:; } disable { %help: short "Disable the IPv4 MFEA"; %create:; %set: xrl "$(mfea4.targetname)/mfea/0.1/enable_mfea?enable:bool=`~$(@)`"; %delete: xrl "$(mfea4.targetname)/mfea/0.1/enable_mfea?enable:bool=`~$(DEFAULT)`"; } enabled { %deprecated: "Statement 'enabled: true/false' is replaced with 'disable: false/true'"; %help: short "Enable the IPv4 MFEA"; %create:; %set: xrl "$(mfea4.targetname)/mfea/0.1/enable_mfea?enable:bool=$(@)"; } interface @ { %help: short "Configure IPv4 MFEA on a network interface"; %delete: xrl "$(mfea4.targetname)/mfea/0.1/stop_vif?vif_name:txt=$(interface.@)"; vif @ { %help: short "Configure IPv4 MFEA on a virtual interface"; %activate: xrl "$(mfea4.targetname)/mfea/0.1/start_vif?vif_name:txt=$(vif.@)"; %update: xrl "$(mfea4.targetname)/mfea/0.1/start_vif?vif_name:txt=$(vif.@)"; %delete: xrl "$(mfea4.targetname)/mfea/0.1/stop_vif?vif_name:txt=$(vif.@)"; disable { %help: short "Disable IPv4 MFEA on an interface"; %set: xrl "$(mfea4.targetname)/mfea/0.1/enable_vif?vif_name:txt=$(vif.@)&enable:bool=`~$(@)`"; %delete: xrl "$(mfea4.targetname)/mfea/0.1/enable_vif?vif_name:txt=$(vif.@)&enable:bool=`~$(DEFAULT)`"; } enabled { %deprecated: "Statement 'enabled: true/false' is replaced with 'disable: false/true'"; %help: short "Enable IPv4 MFEA on an interface"; %set: xrl "$(mfea4.targetname)/mfea/0.1/enable_vif?vif_name:txt=$(vif.@)&enable:bool=$(@)"; %delete: xrl "$(mfea4.targetname)/mfea/0.1/enable_vif?vif_name:txt=$(vif.@)&enable:bool=`~$(DEFAULT)`"; } } } traceoptions { %help: short "Configure the tracing options"; flag { %help: short "Configure the tracing operation to perform"; all { %help: short "Configure all tracing operations"; disable { %help: short "Disable all tracing operations"; %set: xrl "$(mfea4.targetname)/mfea/0.1/log_trace_all?enable:bool=`~$(@)`"; %delete: xrl "$(mfea4.targetname)/mfea/0.1/log_trace_all?enable:bool=$(DEFAULT)"; } enabled { %deprecated: "Statement 'enabled: true/false' is replaced with 'disable: false/true'"; %help: short "Enable all tracing operations"; %set: xrl "$(mfea4.targetname)/mfea/0.1/log_trace_all?enable:bool=$(@)"; } } } } } } xorp/etc/templates/fib2mrib.tp0000664000076400007640000000460511421137511016473 0ustar greearbgreearb/* $XORP: xorp/etc/templates/fib2mrib.tp,v 1.10 2006/02/22 02:26:10 pavlin Exp $ */ protocols { fib2mrib { targetname: txt = "fib2mrib"; disable: toggle = false; enabled: bool; /* %deprecated */ } } policy { policy-statement @: txt { term @: txt { from { metric: u32range; } } } } protocols { fib2mrib { %help: short "Configure the FIB2MRIB module"; %modinfo: provides fib2mrib; %modinfo: depends rib; %modinfo: depends policy; %modinfo: path "xorp_fib2mrib"; %modinfo: default_targetname "fib2mrib"; %modinfo: status_method xrl "$(fib2mrib.targetname)/common/0.1/get_status->status:u32&reason:txt"; %modinfo: startup_method xrl "$(fib2mrib.targetname)/common/0.1/startup"; %modinfo: shutdown_method xrl "$(fib2mrib.targetname)/common/0.1/shutdown"; %mandatory: $(@.targetname); %activate: xrl "$(fib2mrib.targetname)/fib2mrib/0.1/start_fib2mrib"; targetname { %user-hidden: "XRL target name"; %help: short "XRL target name"; %set:; } disable { %help: short "Disable the FIB2MRIB module"; %create:; %set: xrl "$(fib2mrib.targetname)/fib2mrib/0.1/enable_fib2mrib?enable:bool=`~$(@)`"; %delete: xrl "$(fib2mrib.targetname)/fib2mrib/0.1/enable_fib2mrib?enable:bool=`~$(DEFAULT)`"; } enabled { %deprecated: "Statement 'enabled: true/false' is replaced with 'disable: false/true'"; %help: short "Enable the FIB2MRIB module"; %create:; %set: xrl "$(fib2mrib.targetname)/fib2mrib/0.1/enable_fib2mrib?enable:bool=$(@)"; } } } policy { %create: xrl "$(policy.targetname)/policy/0.1/set_proto_target?protocol:txt=fib2mrib&target:txt=fib2mrib"; %create: xrl "$(policy.targetname)/policy/0.1/add_varmap?protocol:txt=fib2mrib&variable:txt=network4&type:txt=ipv4net&access:txt=r&id:u32=10"; %create: xrl "$(policy.targetname)/policy/0.1/add_varmap?protocol:txt=fib2mrib&variable:txt=metric&type:txt=u32&access:txt=rw&id:u32=14"; policy-statement @: txt { term @: txt { from { metric { %help: short "Set the metric value"; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt=metric $(<>) $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt="; } } } } } xorp/etc/templates/protocols.tp0000664000076400007640000000052011421137511017013 0ustar greearbgreearb/* $XORP$ */ protocols { %help: short "Configure routing protocols"; %help: long "The protocols grouping in the router configuration groups together all the \n configuration information for routing protocols.\nTyping protocols { as a command will start direct entry mode, whereby the \nrouter configuration can be entered."; } xorp/etc/templates/ospfv2.tp0000664000076400007640000005703311421137511016221 0ustar greearbgreearb/* $XORP: xorp/etc/templates/ospfv2.tp,v 1.58 2008/08/06 08:24:08 abittau Exp $ */ protocols { ospf4 { targetname: txt = "ospfv2"; router-id: ipv4; rfc1583-compatibility: bool = false; ip-router-alert: bool = false; traceoptions { flag { all { disable: toggle = false; } } } area @: ipv4 { area-type: txt = "normal"; default-lsa { disable: toggle = false; metric: u32 = 0; } summaries { disable: toggle = false; } area-range @: ipv4net { advertise: bool = true; } virtual-link @: ipv4 { transit-area: ipv4; hello-interval: u32 = 10; router-dead-interval: u32 = 40; retransmit-interval: u32 = 5; transit-delay: u32 = 1; authentication { type: txt; /* %deprecated */ password: txt; /* %deprecated */ simple-password: txt = ""; md5 @: u32 { password: txt = ""; start-time: txt = ""; end-time: txt = ""; max-time-drift: u32 = 3600; } } } interface @: txt { link-type: txt = "broadcast"; vif @: txt { address @: ipv4 { priority: u32 = 128; hello-interval: u32 = 10; router-dead-interval: u32 = 40; interface-cost: u32 = 1; retransmit-interval: u32 = 5; transit-delay: u32 = 1; authentication { type: txt; /* %deprecated */ password: txt; /* %deprecated */ simple-password: txt = ""; md5 @: u32 { password: txt = ""; start-time: txt = ""; end-time: txt = ""; max-time-drift: u32 = 3600; } } passive { disable: toggle = false; host: bool = false; } neighbor @: ipv4 { router-id: ipv4; } disable: toggle = false; } } } } import: txt; export: txt; } } policy { policy-statement @: txt { term @: txt { from { metric: u32range; external-type: u32; } to { metric: u32range; external-type: u32; } then { metric: u32; external-type: u32; } } } } protocols { ospf4 { %help: short "Configure the OSPF protocol"; %modinfo: provides ospf4; %modinfo: depends rib; %modinfo: depends policy; %modinfo: path "xorp_ospfv2"; %modinfo: default_targetname "ospfv2"; %modinfo: status_method xrl "$(ospf4.targetname)/common/0.1/get_status->status:u32&reason:txt"; %modinfo: shutdown_method xrl "$(ospf4.targetname)/common/0.1/shutdown"; %modinfo: startup_method xrl "$(ospf4.targetname)/common/0.1/startup"; %mandatory: $(@.router-id); %mandatory: $(@.targetname); targetname { %user-hidden: "XRL target name"; %help: short "XRL target name"; %set:; } router-id { %help: short "A unique 32-bit identifier within this AS"; %help: long "A 32-bit number assigned to each router running the OSPF protocol. This number uniquely identifies the router within an Autonomous System"; %set: xrl "$(ospf4.targetname)/ospfv2/0.1/set_router_id?id:ipv4=$(@)"; } rfc1583-compatibility { %help: short "Criteria for handling AS external routes"; %set: xrl "$(ospf4.targetname)/ospfv2/0.1/set_rfc1583_compatibility?compatibility:bool=$(@)"; %delete: xrl "$(ospf4.targetname)/ospfv2/0.1/set_rfc1583_compatibility?compatibility:bool=$(DEFAULT)"; } ip-router-alert { %help: short "Send the IP router alert option in packets"; %help: long "If this option is true the IP router alert option will be set in all transmitted packets"; %set: xrl "$(ospf4.targetname)/ospfv2/0.1/set_ip_router_alert?ip_router_alert:bool=$(@)"; %delete: xrl "$(ospf4.targetname)/ospfv2/0.1/set_ip_router_alert?ip_router_alert:bool=$(DEFAULT)"; } area @: ipv4 { %help: short "The OSPF area to which the attached network belongs"; area-type { %help: short "Type of area"; %allow: $(@) "normal" %help: "OSPF normal area"; %allow: $(@) "stub" %help: "OSPF stubby area"; %allow: $(@) "nssa" %help: "OSPF not-so-stubby area"; %set: xrl "$(ospf4.targetname)/ospfv2/0.1/change_area_router_type?area:ipv4=$(area.@)&type:txt=$(@)"; %delete: xrl "$(ospf4.targetname)/ospfv2/0.1/change_area_router_type?area:ipv4=$(area.@)&type:txt=$(DEFAULT)"; } %create: xrl "$(ospf4.targetname)/ospfv2/0.1/create_area_router?area:ipv4=$(@)&type:txt=$(@.area-type)"; %delete: xrl "$(ospf4.targetname)/ospfv2/0.1/destroy_area_router?area:ipv4=$(@)"; default-lsa { %help: short "Originate default route in stub or not-so-stubby areas"; disable { %help: short "disable the origination of the default route"; %set: xrl "$(ospf4.targetname)/ospfv2/0.1/originate_default_route?area:ipv4=$(area.@)&enable:bool=`~$(@)`"; %delete: xrl "$(ospf4.targetname)/ospfv2/0.1/originate_default_route?area:ipv4=$(area.@)&enable:bool=$(DEFAULT)"; } metric { %help: short "Metric of default route"; %set: xrl "$(ospf4.targetname)/ospfv2/0.1/stub_default_cost?area:ipv4=$(area.@)&cost:u32=$(@)"; %delete: xrl "$(ospf4.targetname)/ospfv2/0.1/stub_default_cost?area:ipv4=$(area.@)&cost:u32=$(DEFAULT)"; } } summaries { %help: short "Generate summaries into stub or not-so-stubby areas"; disable { %help: short "disable summaries into stub or not-so-stubby areas"; %set: xrl "$(ospf4.targetname)/ospfv2/0.1/summaries?area:ipv4=$(area.@)&enable:bool=`~$(@)`"; %delete: xrl "$(ospf4.targetname)/ospfv2/0.1/summaries?area:ipv4=$(area.@)&enable:bool=$(DEFAULT)"; } } area-range @: ipv4net { %help: short "Area range for generating summaries"; %create: xrl "$(ospf4.targetname)/ospfv2/0.1/area_range_add?area:ipv4=$(area.@)&net:ipv4net=$(@)&advertise:bool=$(@.advertise)"; %delete: xrl "$(ospf4.targetname)/ospfv2/0.1/area_range_delete?area:ipv4=$(area.@)&net:ipv4net=$(@)"; advertise { %help: short "Advertise or DoNotAdvertise"; %set: xrl "$(ospf4.targetname)/ospfv2/0.1/area_range_change_state?area:ipv4=$(area.@)&net:ipv4net=$(area-range.@)&advertise:bool=$(@)"; %delete: xrl "$(ospf4.targetname)/ospfv2/0.1/area_range_change_state?area:ipv4=$(area.@)&net:ipv4net=$(area-range.@)&advertise:bool=$(DEFAULT)"; } } virtual-link @: ipv4 { %help: short "Virtual link"; %create: xrl "$(ospf4.targetname)/ospfv2/0.1/create_virtual_link?neighbour_id:ipv4=$(virtual-link.@)&area:ipv4=$(area.@)"; %delete: xrl "$(ospf4.targetname)/ospfv2/0.1/delete_virtual_link?neighbour_id:ipv4=$(virtual-link.@)"; transit-area { %help: short "Area through which to transit"; %set: xrl "$(ospf4.targetname)/ospfv2/0.1/transit_area_virtual_link?neighbour_id:ipv4=$(virtual-link.@)&transit_area:ipv4=$(@)"; %delete: xrl "$(ospf4.targetname)/ospfv2/0.1/transit_area_virtual_link?neighbour_id:ipv4=$(virtual-link.@)&transit_area:ipv4=$(DEFAULT)"; } hello-interval { %help: short "Hello packets sent every interval seconds"; %allow-range: $(@) "1" "65535" %help: "The Hello packets interval (in seconds)"; %set: xrl "$(ospf4.targetname)/ospfv2/0.1/set_hello_interval?ifname:txt=vlink&vifname:txt=$(virtual-link.@)&area:ipv4=$(area.@)&interval:u32=$(@)"; %delete: xrl "$(ospf4.targetname)/ospfv2/0.1/set_hello_interval?ifname:txt=vlink&vifname:txt=$(virtual-link.@)&area:ipv4=$(area.@)&interval:u32=$(DEFAULT)"; } router-dead-interval { %help: short "Seconds to wait before considering a neighbor dead"; %allow-range: $(@) "1" "4294967295" %help: "The neighbor router dead interval (in seconds)"; %set: xrl "$(ospf4.targetname)/ospfv2/0.1/set_router_dead_interval?ifname:txt=vlink&vifname:txt=$(virtual-link.@)&area:ipv4=$(area.@)&interval:u32=$(@)"; %delete: xrl "$(ospf4.targetname)/ospfv2/0.1/set_router_dead_interval?ifname:txt=vlink&vifname:txt=$(virtual-link.@)&area:ipv4=$(area.@)&interval:u32=$(DEFAULT)"; } retransmit-interval { %help: short "The retransmit interval (RxmtInterval)"; %allow-range: $(@) "1" "65535" %help: "The retransmit interval (in seconds)"; %set: xrl "$(ospf4.targetname)/ospfv2/0.1/set_retransmit_interval?ifname:txt=vlink&vifname:txt=$(virtual-link.@)&area:ipv4=$(area.@)&interval:u32=$(@)"; %delete: xrl "$(ospf4.targetname)/ospfv2/0.1/set_retransmit_interval?ifname:txt=vlink&vifname:txt=$(virtual-link.@)&area:ipv4=$(area.@)&interval:u32=$(DEFAULT)"; } transit-delay { %help: short "Add to age field of all transmitted LSAs"; %allow-range: $(@) "0" "3600" %help: "The extra addition to age field of all transmitted LSAs"; %set: xrl "$(ospf4.targetname)/ospfv2/0.1/set_inftransdelay?ifname:txt=vlink&vifname:txt=$(virtual-link.@)&area:ipv4=$(area.@)&delay:u32=$(@)"; %delete: xrl "$(ospf4.targetname)/ospfv2/0.1/set_inftransdelay?ifname:txt=vlink&vifname:txt=$(virtual-link.@)&area:ipv4=$(area.@)&delay:u32=$(DEFAULT)"; } authentication { %help: short "Set the authentication"; type { %deprecated: "Statement 'type' is obsoleted by 'simple-password' and 'md5'"; %help: short "Set the authentication type"; %allow: $(@) "none" %help: "No authentication"; %allow: $(@) "simple" %help: "Simple password authentication"; %allow: $(@) "md5" %help: "MD5 authentication"; } password { %deprecated: "Statement 'password' is replaced with 'simple-password'"; %help: short "Set the authentication password"; } simple-password { %help: short "Set simple password authentication key"; %create: xrl "$(ospf4.targetname)/ospfv2/0.1/set_simple_authentication_key?ifname:txt=vlink&vifname:txt=$(virtual-link.@)&area:ipv4=$(area.@)&password:txt=$(@)"; %set: xrl "$(ospf4.targetname)/ospfv2/0.1/set_simple_authentication_key?ifname:txt=vlink&vifname:txt=$(virtual-link.@)&area:ipv4=$(area.@)&password:txt=$(@)"; %delete: xrl "$(ospf4.targetname)/ospfv2/0.1/delete_simple_authentication_key?ifname:txt=vlink&vifname:txt=$(virtual-link.@)&area:ipv4=$(area.@)"; } md5 @ { %help: short "Set MD5 authentication key"; %allow-range: $(@) "0" "255" %help: "The MD5 authentication key ID"; %activate: xrl "$(ospf4.targetname)/ospfv2/0.1/set_md5_authentication_key?ifname:txt=vlink&vifname:txt=$(virtual-link.@)&area:ipv4=$(area.@)&key_id:u32=$(@)&password:txt=$(@.password)&start_time:txt=$(@.start-time)&end_time:txt=$(@.end-time)&max_time_drift:u32=$(@.max-time-drift)"; %update: xrl "$(ospf4.targetname)/ospfv2/0.1/set_md5_authentication_key?ifname:txt=vlink&vifname:txt=$(virtual-link.@)&area:ipv4=$(area.@)&key_id:u32=$(@)&password:txt=$(@.password)&start_time:txt=$(@.start-time)&end_time:txt=$(@.end-time)&max_time_drift:u32=$(@.max-time-drift)"; %delete: xrl "$(ospf4.targetname)/ospfv2/0.1/delete_md5_authentication_key?ifname:txt=vlink&vifname:txt=$(virtual-link.@)&area:ipv4=$(area.@)&key_id:u32=$(@)"; password { %help: short "Set the authentication password"; } start-time { %help: short "Set the authentication start time (YYYY-MM-DD.HH:MM)"; } end-time { %help: short "Set the authentication end time (YYYY-MM-DD.HH:MM)"; } max-time-drift { %help: short "Set the maximum time drift (in seconds) among all routers"; %allow-range: $(@) "0" "65534" %help: "The maximum time drift (in seconds)"; %allow-range: $(@) "65535" "65535" %help: "Unlimited time drift"; } } } } interface @: txt { %help: short "Include an interface in this area"; link-type { %help: short "broadcast or p2p or p2m"; %allow: $(@) "broadcast" %help: "Broadcast link"; %allow: $(@) "p2p" %help: "Point-to-point link"; %allow: $(@) "p2m" %help: "Point-to-multipoint link"; %set:; } vif @: txt { %help: short "Include an vif in this area"; address @ { %help: short "Address on vif used for OSPF"; %create: xrl "$(ospf4.targetname)/ospfv2/0.1/create_peer?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv4=$(@)&type:txt=$(interface.@.link-type)&area:ipv4=$(area.@)"; %delete: xrl "$(ospf4.targetname)/ospfv2/0.1/delete_peer?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)"; priority { %help: short "Priority used in DR election"; %allow-range: $(@) "0" "255" %help: "The priority used in DR election"; %set: xrl "$(ospf4.targetname)/ospfv2/0.1/set_router_priority?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&area:ipv4=$(area.@)&priority:u32=$(@)"; %delete: xrl "$(ospf4.targetname)/ospfv2/0.1/set_router_priority?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&area:ipv4=$(area.@)&priority:u32=$(DEFAULT)"; } hello-interval { %help: short "Hello packets sent every interval seconds"; %allow-range: $(@) "1" "65535" %help: "The Hello packets interval (in seconds)"; %set: xrl "$(ospf4.targetname)/ospfv2/0.1/set_hello_interval?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&area:ipv4=$(area.@)&interval:u32=$(@)"; %delete: xrl "$(ospf4.targetname)/ospfv2/0.1/set_hello_interval?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&area:ipv4=$(area.@)&interval:u32=$(DEFAULT)"; } router-dead-interval { %help: short "Seconds to wait before considering a neighbor dead"; %allow-range: $(@) "1" "4294967295" %help: "The neighbor router dead interval (in seconds)"; %set: xrl "$(ospf4.targetname)/ospfv2/0.1/set_router_dead_interval?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&area:ipv4=$(area.@)&interval:u32=$(@)"; %delete: xrl "$(ospf4.targetname)/ospfv2/0.1/set_router_dead_interval?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&area:ipv4=$(area.@)&interval:u32=$(DEFAULT)"; } interface-cost { %help: short "Cost of this address"; %allow-range: $(@) "1" "65535" %help: "The cost of this address"; %set: xrl "$(ospf4.targetname)/ospfv2/0.1/set_interface_cost?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&area:ipv4=$(area.@)&cost:u32=$(@)"; %delete: xrl "$(ospf4.targetname)/ospfv2/0.1/set_interface_cost?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&area:ipv4=$(area.@)&cost:u32=$(DEFAULT)"; } retransmit-interval { %help: short "The retransmit interval (RxmtInterval)"; %allow-range: $(@) "1" "65535" %help: "The retransmit interval (in seconds)"; %set: xrl "$(ospf4.targetname)/ospfv2/0.1/set_retransmit_interval?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&area:ipv4=$(area.@)&interval:u32=$(@)"; %delete: xrl "$(ospf4.targetname)/ospfv2/0.1/set_retransmit_interval?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&area:ipv4=$(area.@)&interval:u32=$(DEFAULT)"; } transit-delay { %help: short "Add to age field of all transmitted LSAs"; %allow-range: $(@) "0" "3600" %help: "The extra addition to age field of all transmitted LSAs"; %set: xrl "$(ospf4.targetname)/ospfv2/0.1/set_inftransdelay?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&area:ipv4=$(area.@)&delay:u32=$(@)"; %delete: xrl "$(ospf4.targetname)/ospfv2/0.1/set_inftransdelay?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&area:ipv4=$(area.@)&delay:u32=$(DEFAULT)"; } authentication { %help: short "Set the authentication"; type { %deprecated: "Statement 'type' is obsoleted by 'simple-password' and 'md5'"; %help: short "Set the authentication type"; %allow: $(@) "none" %help: "No authentication"; %allow: $(@) "simple" %help: "Simple password authentication"; %allow: $(@) "md5" %help: "MD5 authentication"; } password { %deprecated: "Statement 'password' is replaced with 'simple-password'"; %help: short "Set the authentication password"; } simple-password { %help: short "Set simple password authentication key"; %create: xrl "$(ospf4.targetname)/ospfv2/0.1/set_simple_authentication_key?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&area:ipv4=$(area.@)&password:txt=$(@)"; %set: xrl "$(ospf4.targetname)/ospfv2/0.1/set_simple_authentication_key?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&area:ipv4=$(area.@)&password:txt=$(@)"; %delete: xrl "$(ospf4.targetname)/ospfv2/0.1/delete_simple_authentication_key?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&area:ipv4=$(area.@)"; } md5 @ { %help: short "Set MD5 authentication key"; %allow-range: $(@) "0" "255" %help: "The MD5 authentication key ID"; %activate: xrl "$(ospf4.targetname)/ospfv2/0.1/set_md5_authentication_key?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&area:ipv4=$(area.@)&key_id:u32=$(@)&password:txt=$(@.password)&start_time:txt=$(@.start-time)&end_time:txt=$(@.end-time)&max_time_drift:u32=$(@.max-time-drift)"; %update: xrl "$(ospf4.targetname)/ospfv2/0.1/set_md5_authentication_key?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&area:ipv4=$(area.@)&key_id:u32=$(@)&password:txt=$(@.password)&start_time:txt=$(@.start-time)&end_time:txt=$(@.end-time)&max_time_drift:u32=$(@.max-time-drift)"; %delete: xrl "$(ospf4.targetname)/ospfv2/0.1/delete_md5_authentication_key?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&area:ipv4=$(area.@)&key_id:u32=$(@)"; password { %help: short "Set the authentication password"; } start-time { %help: short "Set the authentication start time (YYYY-MM-DD.HH:MM)"; } end-time { %help: short "Set the authentication end time (YYYY-MM-DD.HH:MM)"; } max-time-drift { %help: short "Set the maximum time drift (in seconds) among all routers"; %allow-range: $(@) "0" "65534" %help: "The maximum time drift (in seconds)"; %allow-range: $(@) "65535" "65535" %help: "Unlimited time drift"; } } } passive { %help: short "Do not run OSPF, but advertise it"; %activate: xrl "$(ospf4.targetname)/ospfv2/0.1/set_passive?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&area:ipv4=$(area.@)&passive:bool=`~$(@.disable)`&host:bool=$(@.host)"; %update: xrl "$(ospf4.targetname)/ospfv2/0.1/set_passive?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&area:ipv4=$(area.@)&passive:bool=`~$(@.disable)`&host:bool=$(@.host)"; %delete: xrl "$(ospf4.targetname)/ospfv2/0.1/set_passive?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&area:ipv4=$(area.@)&passive:bool=$(@.disable.DEFAULT)&host:bool=$(@.host.DEFAULT)"; disable { %help: short "Disable passive (loopback)"; } host { %help: short "Advertise host route"; } } disable { %help: short "Disable OSPF on address"; %set: xrl "$(ospf4.targetname)/ospfv2/0.1/set_peer_state?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&enable:bool=`~$(@)`"; %delete: xrl "$(ospf4.targetname)/ospfv2/0.1/set_peer_state?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&enable:bool=`~$(DEFAULT)`"; } neighbor @: ipv4 { %help: short "Neighbors address"; %mandatory: $(@.router-id); router-id { %help: short "Neighbors router-id"; %set:; } %create: xrl "$(ospf4.targetname)/ospfv2/0.1/add_neighbour?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&area:ipv4=$(area.@)&neighbour_address:ipv4=$(@)&neighbour_id:ipv4=$(@.router-id)"; %delete: xrl "$(ospf4.targetname)/ospfv2/0.1/remove_neighbour?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&area:ipv4=$(area.@)&neighbour_address:ipv4=$(@)&neighbour_id:ipv4=$(@.router-id)"; } } } } } traceoptions { %help: short "Configure the tracing options"; flag { %help: short "Configure the tracing operation to perform"; all { %help: short "Configure all tracing operations"; disable { %help: short "Disable all tracing operations"; %set: xrl "$(ospf4.targetname)/ospfv2/0.1/trace?tvar:txt=all&enable:bool=`~$(@)`"; %delete: xrl "$(ospf4.targetname)/ospfv2/0.1/trace?tvar:txt=all&enable:bool=$(DEFAULT)"; } } } } import { %help: short "Import policy"; %delete: xrl "$(policy.targetname)/policy/0.1/import?protocol:txt=ospf4&policies:txt=&modifier:txt="; %set: xrl "$(policy.targetname)/policy/0.1/import?protocol:txt=ospf4&policies:txt=$(@)&modifier:txt="; } export { %help: short "Export policy"; %delete: xrl "$(policy.targetname)/policy/0.1/export?protocol:txt=ospf4&policies:txt=&modifier:txt="; %set: xrl "$(policy.targetname)/policy/0.1/export?protocol:txt=ospf4&policies:txt=$(@)&modifier:txt="; } } } policy { %create: xrl "$(policy.targetname)/policy/0.1/set_proto_target?protocol:txt=ospf4&target:txt=ospfv2"; %create: xrl "$(policy.targetname)/policy/0.1/add_varmap?protocol:txt=ospf4&variable:txt=network4&type:txt=ipv4net&access:txt=r&id:u32=10"; %create: xrl "$(policy.targetname)/policy/0.1/add_varmap?protocol:txt=ospf4&variable:txt=nexthop4&type:txt=ipv4nexthop&access:txt=rw&id:u32=11"; %create: xrl "$(policy.targetname)/policy/0.1/add_varmap?protocol:txt=ospf4&variable:txt=metric&type:txt=u32&access:txt=rw&id:u32=12"; %create: xrl "$(policy.targetname)/policy/0.1/add_varmap?protocol:txt=ospf4&variable:txt=etype&type:txt=u32&access:txt=rw&id:u32=13"; policy-statement @: txt { term @: txt { from { metric { %help: short "Metric value"; %allow-operator: ":" "==" "!=" "<" ">" "<=" ">="; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt=metric $(<>) $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt="; } external-type { %help: short "Type of external OSPF route"; %allow-operator: ":" "==" "!="; %allow-range: $(@) "1" "2" %help: "Either type 1 or type 2"; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt=etype $(<>) $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt="; } } to { metric { %help: short "Metric value"; %allow-operator: ":" "==" "!=" "<" ">" "<=" ">="; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=1&order:txt=$(#)&statement:txt=metric $(<>) $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=1&order:txt=$(#)&statement:txt="; } external-type { %help: short "Type of external OSPF route"; %allow-operator: ":" "==" "!="; %allow-range: $(@) "1" "2" %help: "Either type 1 or type 2"; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=1&order:txt=$(#)&statement:txt=etype $(<>) $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=1&order:txt=$(#)&statement:txt="; } } then { metric { %help: short "Set the metric value"; %allow-operator: ":" "=" "sub" "add"; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=2&order:txt=$(#)&statement:txt=metric $(<>) $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=2&order:txt=$(#)&statement:txt="; } external-type { %help: short "Set the type of external OSPF route"; %allow-range: $(@) "1" "2" %help: "Either type 1 or type 2"; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=2&order:txt=$(#)&statement:txt=etype $(<>) $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=2&order:txt=$(#)&statement:txt="; } } } } } xorp/etc/templates/interfaces.tp0000664000076400007640000003233511535503566017140 0ustar greearbgreearb interfaces { targetname: txt = "fea"; restore-original-config-on-shutdown: bool = false; interface @: txt { description: txt = ""; disable: toggle = false; enabled: bool; /* %deprecated */ discard: toggle = false; unreachable: toggle = false; management: toggle = false; mac: macaddr; mtu: u32; /* Virtual-interface settings, leave blank if not a VLAN */ parent-ifname: txt = ""; /* Parent interface */ iface-type: txt = ""; /* VLAN */ vid: txt = ""; /* virtual-ID (VLAN-ID for VLANs) */ vif @: txt { disable: toggle = false; enabled: bool; /* %deprecated */ address @: ipv4 { prefix-length: u32; broadcast: ipv4; destination: ipv4; multicast-capable: bool; point-to-point: bool; loopback: bool; disable: toggle = false; enabled: bool; /* %deprecated */ } address @: ipv6 { prefix-length: u32; destination: ipv6; multicast-capable: bool; point-to-point: bool; loopback: bool; disable: toggle = false; enabled: bool; /* %deprecated */ } } } } interfaces { %help: short "Configure network interfaces"; %modinfo: provides interfaces; %modinfo: path "xorp_fea"; %modinfo: default_targetname "fea"; %modinfo: status_method xrl "$(interfaces.targetname)/common/0.1/get_status->status:u32&reason:txt"; %modinfo: shutdown_method xrl "$(interfaces.targetname)/common/0.1/shutdown"; %modinfo: startup_method xrl "$(interfaces.targetname)/ifmgr/0.1/startup_ifmgr"; %modinfo: start_commit xrl "$(interfaces.targetname)/ifmgr/0.1/start_transaction->tid:u32=$(interfaces.TID)"; %modinfo: end_commit xrl "$(interfaces.targetname)/ifmgr/0.1/commit_transaction?tid:u32=$(interfaces.TID)"; %mandatory: $(@.targetname); targetname { %user-hidden: "XRL target name"; %help: short "XRL target name"; %set:; } TID { %user-hidden: "Transaction ID generated by the FEA"; %create:; } restore-original-config-on-shutdown { %help: short "Restore original configuration on shutdown"; %create: xrl "$(interfaces.targetname)/ifmgr/0.1/set_restore_original_config_on_shutdown?enable:bool=$(@)"; %set: xrl "$(interfaces.targetname)/ifmgr/0.1/set_restore_original_config_on_shutdown?enable:bool=$(@)"; %delete: xrl "$(interfaces.targetname)/ifmgr/0.1/set_restore_original_config_on_shutdown?enable:bool=$(DEFAULT)"; } interface @: txt { %help: short "Configure a network interface"; %create: xrl "$(interfaces.targetname)/ifmgr/0.1/create_interface?tid:u32=$(interfaces.TID)&ifname:txt=$(@)"; %delete: xrl "$(interfaces.targetname)/ifmgr/0.1/delete_interface?tid:u32=$(interfaces.TID)&ifname:txt=$(@)"; %list: xrl "$(interfaces.targetname)/ifmgr/0.1/get_configured_interface_names->ifnames:list"; description { %help: short "Add a human-readable description of an interface"; %set:; %get:; %delete:; } disable { %help: short "Disable a network interface"; %set: xrl "$(interfaces.targetname)/ifmgr/0.1/set_interface_enabled?tid:u32=$(interfaces.TID)&ifname:txt=$(interface.@)&enabled:bool=`~$(@)`"; } enabled { %deprecated: "Statement 'enabled: true/false' is replaced with 'disable: false/true'"; %help: short "Enable a network interface"; %set: xrl "$(interfaces.targetname)/ifmgr/0.1/set_interface_enabled?tid:u32=$(interfaces.TID)&ifname:txt=$(interface.@)&enabled:bool=$(@)"; } discard { %help: short "Configure a discard interface"; %set: xrl "$(interfaces.targetname)/ifmgr/0.1/set_interface_discard?tid:u32=$(interfaces.TID)&ifname:txt=$(interface.@)&discard:bool=$(@)"; } unreachable { %help: short "Configure an unreachable interface"; %set: xrl "$(interfaces.targetname)/ifmgr/0.1/set_interface_unreachable?tid:u32=$(interfaces.TID)&ifname:txt=$(interface.@)&unreachable:bool=$(@)"; } management { %help: short "Configure a management interface"; %set: xrl "$(interfaces.targetname)/ifmgr/0.1/set_interface_management?tid:u32=$(interfaces.TID)&ifname:txt=$(interface.@)&management:bool=$(@)"; } default-system-config { %help: short "Use default values from the system"; %create: xrl "$(interfaces.targetname)/ifmgr/0.1/configure_interface_from_system?tid:u32=$(interfaces.TID)&ifname:txt=$(interface.@)&enable:bool=true"; %delete: xrl "$(interfaces.targetname)/ifmgr/0.1/configure_interface_from_system?tid:u32=$(interfaces.TID)&ifname:txt=$(interface.@)&enable:bool=false"; } mac { %help: short "Set the MAC address"; %set: xrl "$(interfaces.targetname)/ifmgr/0.1/set_mac?tid:u32=$(interfaces.TID)&ifname:txt=$(interface.@)&mac:mac=$(@)"; %delete: xrl "$(interfaces.targetname)/ifmgr/0.1/restore_original_mac?tid:u32=$(interfaces.TID)&ifname:txt=$(interface.@)"; %get: xrl "$(interfaces.targetname)/ifmgr/0.1/get_configured_mac?ifname:txt=$(interface.@)->mac:mac=$(@)"; } mtu { %help: short "Set the MTU"; /* Minimum and maximum MTU as defined in RFC 791 and RFC 1191 */ %allow-range: $(@) "68" "65535" %help: "The MTU (in octets)"; %set: xrl "$(interfaces.targetname)/ifmgr/0.1/set_mtu?tid:u32=$(interfaces.TID)&ifname:txt=$(interface.@)&mtu:u32=$(@)"; %delete: xrl "$(interfaces.targetname)/ifmgr/0.1/restore_original_mtu?tid:u32=$(interfaces.TID)&ifname:txt=$(interface.@)"; %get: xrl "$(interfaces.targetname)/ifmgr/0.1/get_configured_mtu?ifname:txt=$(interface.@)->mtu:u32=$(@)"; } parent-ifname { %help: short "Parent interface this virtual belongs to."; %set: xrl "$(interfaces.targetname)/ifmgr/0.1/set_parent_ifname?tid:u32=$(interfaces.TID)&ifname:txt=$(interface.@)&parent_ifname:txt=$(@)"; } iface-type { %help: short "Interface-type: VLAN."; %set: xrl "$(interfaces.targetname)/ifmgr/0.1/set_iface_type?tid:u32=$(interfaces.TID)&ifname:txt=$(interface.@)&iface_type:txt=$(@)"; } vid { %help: short "Virtual Interface Identifier: VLAN-ID for VLANs."; %set: xrl "$(interfaces.targetname)/ifmgr/0.1/set_vid?tid:u32=$(interfaces.TID)&ifname:txt=$(interface.@)&vid:txt=$(@)"; } vif @: txt { %help: short "Configure a virtual interface"; %create: xrl "$(interfaces.targetname)/ifmgr/0.1/create_vif?tid:u32=$(interfaces.TID)&ifname:txt=$(interface.@)&vif:txt=$(@)"; %delete: xrl "$(interfaces.targetname)/ifmgr/0.1/delete_vif?tid:u32=$(interfaces.TID)&ifname:txt=$(interface.@)&vif:txt=$(@)"; %list: xrl "$(interfaces.targetname)/ifmgr/0.1/get_configured_vif_names?ifname:txt=$(interface.@)->vifs:list"; disable { %help: short "Disable a network interface"; %set: xrl "$(interfaces.targetname)/ifmgr/0.1/set_vif_enabled?tid:u32=$(interfaces.TID)&ifname:txt=$(interface.@)&vif:txt=$(vif.@)&enabled:bool=`~$(@)`"; } enabled { %deprecated: "Statement 'enabled: true/false' is replaced with 'disable: false/true'"; %help: short "Enable a network interface"; %set: xrl "$(interfaces.targetname)/ifmgr/0.1/set_vif_enabled?tid:u32=$(interfaces.TID)&ifname:txt=$(interface.@)&vif:txt=$(vif.@)&enabled:bool=$(@)"; } address @: ipv4 { %help: short "Configure an IPv4 address"; %mandatory: $(@.prefix-length); %create: xrl "$(interfaces.targetname)/ifmgr/0.1/create_address4?tid:u32=$(interfaces.TID)&ifname:txt=$(interface.@)&vif:txt=$(vif.@)&address:ipv4=$(@)"; %delete: xrl "$(interfaces.targetname)/ifmgr/0.1/delete_address4?tid:u32=$(interfaces.TID)&ifname:txt=$(interface.@)&vif:txt=$(vif.@)&address:ipv4=$(@)"; %list: xrl "$(interfaces.targetname)/ifmgr/0.1/get_configured_vif_addresses4?ifname:txt=$(interface.@)&vif:txt->addresses:list"; disable { %help: short "Disable the address"; %set: xrl "$(interfaces.targetname)/ifmgr/0.1/set_address_enabled4?tid:u32=$(interfaces.TID)&ifname:txt=$(interface.@)&vif:txt=$(vif.@)&address:ipv4=$(address.@)&enabled:bool=`~$(@)`"; } enabled { %deprecated: "Statement 'enabled: true/false' is replaced with 'disable: false/true'"; %help: short "Enable the address"; %set: xrl "$(interfaces.targetname)/ifmgr/0.1/set_address_enabled4?tid:u32=$(interfaces.TID)&ifname:txt=$(interface.@)&vif:txt=$(vif.@)&address:ipv4=$(address.@)&enabled:bool=$(@)"; } prefix-length { %help: short "Assign the network prefix length"; %allow-range: $(@) "1" "32" %help: "The network prefix length"; %set: xrl "$(interfaces.targetname)/ifmgr/0.1/set_prefix4?tid:u32=$(interfaces.TID)&ifname:txt=$(interface.@)&vif:txt=$(vif.@)&address:ipv4=$(address.@)&prefix_len:u32=$(@)"; %get: xrl "$(interfaces.targetname)/ifmgr/0.1/get_configured_prefix4?ifname:txt=$(interface.@)&vif:txt=$(vif.@)&address:ipv4=$(address.@)->prefix_len:u32"; } broadcast { %help: short "Assign the network broadcast address"; %set: xrl "$(interfaces.targetname)/ifmgr/0.1/set_broadcast4?tid:u32=$(interfaces.TID)&ifname:txt=$(interface.@)&vif:txt=$(vif.@)&address:ipv4=$(address.@)&broadcast:ipv4=$(@)"; %get: xrl "$(interfaces.targetname)/ifmgr/0.1/get_configured_broadcast4?ifname:txt=$(interface.@)&vif:txt=$(vif.@)&address:ipv4=$(address.@)->broadcast:ipv4=$(@)"; } destination { %help: short "Assign the destination address on point-to-point links"; %set: xrl "$(interfaces.targetname)/ifmgr/0.1/set_endpoint4?tid:u32=$(interfaces.TID)&ifname:txt=$(interface.@)&vif:txt=$(vif.@)&address:ipv4=$(address.@)&endpoint:ipv4=$(@)"; %get: xrl "$(interfaces.targetname)/ifmgr/0.1/get_configured_endpoint4?ifname:txt=$(interface.@)&vif:txt=$(vif.@)&address:ipv4=$(address.@)->endpoint:ipv4=$(@)"; } multicast-capable { %help: short "Show if the address is multicast capable"; %get: xrl "$(interfaces.targetname)/ifmgr/0.1/get_configured_address_flags4?ifname:txt=$(interface.@)&vif:txt=$(vif.@)&address:ipv4=$(address.@)->enabled:bool&broadcast:bool&loopback:bool&point_to_point:bool&multicast:bool=$(@)"; } point-to-point { %help: short "Show if the address is point-to-point"; %get: xrl "$(interfaces.targetname)/ifmgr/0.1/get_configured_address_flags4?ifname:txt=$(interface.@)&vif:txt=$(vif.@)&address:ipv4=$(address.@)->enabled:bool&broadcast:bool&loopback:bool&point_to_point:bool=$(@)&multicast:bool"; } loopback { %help: short "Show if this is a loopback address"; %get: xrl "$(interfaces.targetname)/ifmgr/0.1/get_configured_address_flags4?ifname:txt=$(interface.@)&vif:txt=$(vif.@)&address:ipv4=$(address.@)->enabled:bool&broadcast:bool&loopback:bool=$(@)&point_to_point:bool&multicast:bool"; } } address @: ipv6 { %help: short "Configure an IPv6 address"; %mandatory: $(@.prefix-length); %create: xrl "$(interfaces.targetname)/ifmgr/0.1/create_address6?tid:u32=$(interfaces.TID)&ifname:txt=$(interface.@)&vif:txt=$(vif.@)&address:ipv6=$(@)"; %delete: xrl "$(interfaces.targetname)/ifmgr/0.1/delete_address6?tid:u32=$(interfaces.TID)&ifname:txt=$(interface.@)&vif:txt=$(vif.@)&address:ipv6=$(@)"; %list: xrl "$(interfaces.targetname)/ifmgr/0.1/get_configured_vif_addresses6?ifname:txt=$(interface.@)&vif:txt->addresses:list"; disable { %help: short "Disable the address"; %set: xrl "$(interfaces.targetname)/ifmgr/0.1/set_address_enabled6?tid:u32=$(interfaces.TID)&ifname:txt=$(interface.@)&vif:txt=$(vif.@)&address:ipv6=$(address.@)&enabled:bool=`~$(@)`"; } enabled { %deprecated: "Statement 'enabled: true/false' is replaced with 'disable: false/true'"; %help: short "Enable the address"; %set: xrl "$(interfaces.targetname)/ifmgr/0.1/set_address_enabled6?tid:u32=$(interfaces.TID)&ifname:txt=$(interface.@)&vif:txt=$(vif.@)&address:ipv6=$(address.@)&enabled:bool=$(@)"; } prefix-length { %help: short "Assign the network prefix length"; %allow-range: $(@) "1" "128" %help: "The network prefix length"; %set: xrl "$(interfaces.targetname)/ifmgr/0.1/set_prefix6?tid:u32=$(interfaces.TID)&ifname:txt=$(interface.@)&vif:txt=$(vif.@)&address:ipv6=$(address.@)&prefix_len:u32=$(@)"; %get: xrl "$(interfaces.targetname)/ifmgr/0.1/get_configured_prefix6?ifname:txt=$(interface.@)&vif:txt=$(vif.@)&address:ipv6=$(address.@)->prefix_len:u32"; } destination { %help: short "Assign the destination address on point-to-point links"; %set: xrl "$(interfaces.targetname)/ifmgr/0.1/set_endpoint6?tid:u32=$(interfaces.TID)&ifname:txt=$(interface.@)&vif:txt=$(vif.@)&address:ipv6=$(address.@)&endpoint:ipv6=$(@)"; %get: xrl "$(interfaces.targetname)/ifmgr/0.1/get_configured_endpoint6?ifname:txt=$(interface.@)&vif:txt=$(vif.@)&address:ipv6=$(address.@)->endpoint:ipv6=$(@)"; } multicast-capable { %help: short "Show if the address is multicast capable"; %get: xrl "$(interfaces.targetname)/ifmgr/0.1/get_configured_address_flags6?ifname:txt=$(interface.@)&vif:txt=$(vif.@)&address:ipv6=$(address.@)->enabled:bool&loopback:bool&point_to_point:bool&multicast:bool=$(@)"; } point-to-point { %help: short "Show if the address is point-to-point"; %get: xrl "$(interfaces.targetname)/ifmgr/0.1/get_configured_address_flags6?ifname:txt=$(interface.@)&vif:txt=$(vif.@)&address:ipv6=$(address.@)->enabled:bool&loopback:bool&point_to_point:bool=$(@)&multicast:bool"; } loopback { %help: short "Show if this is a loopback address"; %get: xrl "$(interfaces.targetname)/ifmgr/0.1/get_configured_address_flags6?ifname:txt=$(interface.@)&vif:txt=$(vif.@)&address:ipv6=$(address.@)->enabled:bool&loopback:bool=$(@)&point_to_point:bool&multicast:bool"; } } } } } xorp/etc/templates/pimsm4.tp0000664000076400007640000003436311421137511016214 0ustar greearbgreearb/* $XORP: xorp/etc/templates/pimsm4.tp,v 1.34 2008/05/01 22:49:50 bms Exp $ */ protocols { pimsm4 { targetname: txt = "PIMSM_4"; disable: toggle = false; enabled: bool; /* %deprecated */ interface @: txt { vif @: txt { disable: toggle = false; enabled: bool; /* %deprecated */ enable-ip-router-alert-option-check: bool; /* %deprecated */ dr-priority: u32 = 1; hello-period: u32 = 30; hello-triggered-delay: u32 = 5; alternative-subnet @: ipv4net { } } } static-rps { rp @: ipv4 { group-prefix @: ipv4net { rp-priority: u32 = 192; hash-mask-len: u32 = 30; } } } bootstrap { disable: toggle = false; enabled: bool; /* %deprecated */ cand-bsr { scope-zone @: ipv4net { is-scope-zone: bool = false; cand-bsr-by-vif-name: txt; cand-bsr-by-vif-addr: ipv4 = 0.0.0.0; bsr-priority: u32 = 1; hash-mask-len: u32 = 30; } } cand-rp { group-prefix @: ipv4net { is-scope-zone: bool = false; cand-rp-by-vif-name: txt; cand-rp-by-vif-addr: ipv4 = 0.0.0.0; rp-priority: u32 = 192; rp-holdtime: u32 = 150; } } } switch-to-spt-threshold { disable: toggle = false; enabled: bool; /* %deprecated */ interval: u32 = 100; bytes: u32 = 0; } traceoptions { flag { all { disable: toggle = false; enabled: bool; /* %deprecated */ } } } } } protocols { pimsm4 { %help: short "Configure the IPv4 PIM-SM protocol"; %modinfo: provides pimsm4; %modinfo: depends mfea4; %modinfo: depends igmp; %modinfo: depends rib; %modinfo: path "xorp_pimsm4"; %modinfo: default_targetname "pim"; %modinfo: status_method xrl "$(pimsm4.targetname)/common/0.1/get_status->status:u32&reason:txt"; %modinfo: startup_method xrl "$(pimsm4.targetname)/common/0.1/startup"; %modinfo: shutdown_method xrl "$(pimsm4.targetname)/common/0.1/shutdown"; %mandatory: $(@.targetname); targetname { %user-hidden: "XRL target name"; %help: short "XRL target name"; %set:; } disable { %help: short "Disable the IPv4 PIM-SM protocol"; %create:; %set: xrl "$(pimsm4.targetname)/pim/0.1/enable_pim?enable:bool=`~$(@)`"; %delete: xrl "$(pimsm4.targetname)/pim/0.1/enable_pim?enable:bool=`~$(DEFAULT)`"; } enabled { %deprecated: "Statement 'enabled: true/false' is replaced with 'disable: false/true'"; %help: short "Enable the IPv4 PIM-SM protocol"; %create:; %set: xrl "$(pimsm4.targetname)/pim/0.1/enable_pim?enable:bool=$(@)"; } interface @ { %help: short "Configure IPv4 PIM-SM on a network interface"; %delete: xrl "$(pimsm4.targetname)/pim/0.1/stop_vif?vif_name:txt=$(interface.@)"; vif @ { %help: short "Configure IPv4 PIM-SM on a virtual interface"; %activate: xrl "$(pimsm4.targetname)/pim/0.1/start_vif?vif_name:txt=$(vif.@)"; %update: xrl "$(pimsm4.targetname)/pim/0.1/start_vif?vif_name:txt=$(vif.@)"; %delete: xrl "$(pimsm4.targetname)/pim/0.1/stop_vif?vif_name:txt=$(vif.@)"; disable { %help: short "Disable IPv4 PIM-SM on an interface"; %set: xrl "$(pimsm4.targetname)/pim/0.1/enable_vif?vif_name:txt=$(vif.@)&enable:bool=`~$(@)`"; %delete: xrl "$(pimsm4.targetname)/pim/0.1/enable_vif?vif_name:txt=$(vif.@)&enable:bool=`~$(DEFAULT)`"; } enabled { %deprecated: "Statement 'enabled: true/false' is replaced with 'disable: false/true'"; %help: short "Enable IPv4 PIM-SM on an interface"; %set: xrl "$(pimsm4.targetname)/pim/0.1/enable_vif?vif_name:txt=$(vif.@)&enable:bool=$(@)"; %delete: xrl "$(pimsm4.targetname)/pim/0.1/enable_vif?vif_name:txt=$(vif.@)&enable:bool=`~$(DEFAULT)`"; } enable-ip-router-alert-option-check { %deprecated: "IP Router Alert is no longer required for PIM"; %help: short "Enable the IP Router Alert option check"; %create:; %set:; %delete:; } dr-priority { %help: short "Set the Designated Router election priority"; %create: xrl "$(pimsm4.targetname)/pim/0.1/set_vif_dr_priority?vif_name:txt=$(vif.@)&dr_priority:u32=$(@)"; %set: xrl "$(pimsm4.targetname)/pim/0.1/set_vif_dr_priority?vif_name:txt=$(vif.@)&dr_priority:u32=$(@)"; %delete: xrl "$(pimsm4.targetname)/pim/0.1/set_vif_dr_priority?vif_name:txt=$(vif.@)&dr_priority:u32=$(DEFAULT)"; } hello-period { %help: short "Set the Hello messages period (in seconds)"; %allow-range: $(@) "1" "18724" %help: "The Hello messages period (in seconds)"; %create: xrl "$(pimsm4.targetname)/pim/0.1/set_vif_hello_period?vif_name:txt=$(vif.@)&hello_period:u32=$(@)"; %set: xrl "$(pimsm4.targetname)/pim/0.1/set_vif_hello_period?vif_name:txt=$(vif.@)&hello_period:u32=$(@)"; %delete: xrl "$(pimsm4.targetname)/pim/0.1/set_vif_hello_period?vif_name:txt=$(vif.@)&hello_period:u32=$(DEFAULT)"; } hello-triggered-delay { %help: short "Set the randomized triggered delay of the Hello messages (in seconds)"; %allow-range: $(@) "1" "255" %help: "The randomized triggered delay of the Hello messages (in seconds)"; %create: xrl "$(pimsm4.targetname)/pim/0.1/set_vif_hello_triggered_delay?vif_name:txt=$(vif.@)&hello_triggered_delay:u32=$(@)"; %set: xrl "$(pimsm4.targetname)/pim/0.1/set_vif_hello_triggered_delay?vif_name:txt=$(vif.@)&hello_triggered_delay:u32=$(@)"; %delete: xrl "$(pimsm4.targetname)/pim/0.1/set_vif_hello_triggered_delay?vif_name:txt=$(vif.@)&hello_triggered_delay:u32=$(DEFAULT)"; } alternative-subnet @ { %help: short "Fake a subnet to appear directly-connected"; %create: xrl "$(pimsm4.targetname)/pim/0.1/add_alternative_subnet4?vif_name:txt=$(vif.@)&subnet:ipv4net=$(alternative-subnet.@)"; %delete: xrl "$(pimsm4.targetname)/pim/0.1/delete_alternative_subnet4?vif_name:txt=$(vif.@)&subnet:ipv4net=$(alternative-subnet.@)"; } } } static-rps { %help: short "Configure the set of static RPs"; %activate: xrl "$(pimsm4.targetname)/pim/0.1/config_static_rp_done"; %update: xrl "$(pimsm4.targetname)/pim/0.1/config_static_rp_done"; %delete: xrl "$(pimsm4.targetname)/pim/0.1/delete_config_all_static_rps"; %delete: xrl "$(pimsm4.targetname)/pim/0.1/config_static_rp_done"; rp @ { %help: short "Configure a static RP"; %delete: xrl "$(pimsm4.targetname)/pim/0.1/delete_config_all_static_group_prefixes_rp4?rp_addr:ipv4=$(rp.@)"; %delete: xrl "$(pimsm4.targetname)/pim/0.1/config_static_rp_done"; group-prefix @ { %help: short "Configure the group prefix address covered by the RP"; %create: xrl "$(pimsm4.targetname)/pim/0.1/add_config_static_rp4?group_prefix:ipv4net=$(group-prefix.@)&rp_addr:ipv4=$(rp.@)&rp_priority:u32=$(@.rp-priority)&hash_mask_len:u32=$(@.hash-mask-len)"; %delete: xrl "$(pimsm4.targetname)/pim/0.1/delete_config_static_rp4?group_prefix:ipv4net=$(group-prefix.@)&rp_addr:ipv4=$(rp.@)"; %delete: xrl "$(pimsm4.targetname)/pim/0.1/config_static_rp_done"; rp-priority { %help: short "Configure the RP priority"; %allow-range: $(@) "0" "255" %help: "The RP priority"; %create:; %set: xrl "$(pimsm4.targetname)/pim/0.1/add_config_static_rp4?group_prefix:ipv4net=$(group-prefix.@)&rp_addr:ipv4=$(rp.@)&rp_priority:u32=$(group-prefix.@.rp-priority)&hash_mask_len:u32=$(group-prefix.@.hash-mask-len)"; %delete: xrl "$(pimsm4.targetname)/pim/0.1/add_config_static_rp4?group_prefix:ipv4net=$(group-prefix.@)&rp_addr:ipv4=$(rp.@)&rp_priority:u32=$(DEFAULT)&hash_mask_len:u32=$(group-prefix.@.hash-mask-len)"; %delete: xrl "$(pimsm4.targetname)/pim/0.1/config_static_rp_done"; } hash-mask-len { %help: short "Configure the hash mask length for the hash function"; %allow-range: $(@) "4" "32" %help: "The hash mask length for the hash function"; %create:; %set: xrl "$(pimsm4.targetname)/pim/0.1/add_config_static_rp4?group_prefix:ipv4net=$(group-prefix.@)&rp_addr:ipv4=$(rp.@)&rp_priority:u32=$(group-prefix.@.rp-priority)&hash_mask_len:u32=$(group-prefix.@.hash-mask-len)"; %delete: xrl "$(pimsm4.targetname)/pim/0.1/add_config_static_rp4?group_prefix:ipv4net=$(group-prefix.@)&rp_addr:ipv4=$(rp.@)&rp_priority:u32=$(group-prefix.@.rp-priority)&hash_mask_len:u32=$(DEFAULT)"; %delete: xrl "$(pimsm4.targetname)/pim/0.1/config_static_rp_done"; } } } } bootstrap { %help: short "Configure the IPv4 Bootstrap mechanism"; %activate: xrl "$(pimsm4.targetname)/pim/0.1/stop_bsr"; %activate: xrl "$(pimsm4.targetname)/pim/0.1/start_bsr"; %update: xrl "$(pimsm4.targetname)/pim/0.1/apply_bsr_changes"; %delete: xrl "$(pimsm4.targetname)/pim/0.1/stop_bsr"; disable { %help: short "Disable the IPv4 Bootstrap mechanism"; %create: xrl "$(pimsm4.targetname)/pim/0.1/enable_bsr?enable:bool=`~$(@)`"; %set: xrl "$(pimsm4.targetname)/pim/0.1/enable_bsr?enable:bool=`~$(@)`"; %delete: xrl "$(pimsm4.targetname)/pim/0.1/enable_bsr?enable:bool=`~$(DEFAULT)`"; } enabled { %deprecated: "Statement 'enabled: true/false' is replaced with 'disable: false/true'"; %help: short "Enable the IPv4 Bootstrap mechanism"; %create: xrl "$(pimsm4.targetname)/pim/0.1/enable_bsr?enable:bool=$(@)"; %set: xrl "$(pimsm4.targetname)/pim/0.1/enable_bsr?enable:bool=$(@)"; } cand-bsr { %help: short "Configure this router as a Candidate-BSR"; scope-zone @ { %help: short "Configure a scope zone in this Candidate-BSR"; %mandatory: $(@.cand-bsr-by-vif-name); %create: xrl "$(pimsm4.targetname)/pim/0.1/add_config_cand_bsr4?scope_zone_id:ipv4net=$(scope-zone.@)&is_scope_zone:bool=$(@.is-scope-zone)&vif_name:txt=$(@.cand-bsr-by-vif-name)&vif_addr:ipv4=$(@.cand-bsr-by-vif-addr)&bsr_priority:u32=$(@.bsr-priority)&hash_mask_len:u32=$(@.hash-mask-len)"; %update: xrl "$(pimsm4.targetname)/pim/0.1/add_config_cand_bsr4?scope_zone_id:ipv4net=$(scope-zone.@)&is_scope_zone:bool=$(@.is-scope-zone)&vif_name:txt=$(@.cand-bsr-by-vif-name)&vif_addr:ipv4=$(@.cand-bsr-by-vif-addr)&bsr_priority:u32=$(@.bsr-priority)&hash_mask_len:u32=$(@.hash-mask-len)"; %delete: xrl "$(pimsm4.targetname)/pim/0.1/delete_config_cand_bsr4?scope_zone_id:ipv4net=$(scope-zone.@)&is_scope_zone:bool=$(@.is-scope-zone)"; is-scope-zone { %help: short "Flag to indicate a scoped or global zone"; %set:; } cand-bsr-by-vif-name { %help: short "Specify the virtual interface with the address of the Candidate-BSR"; %set:; } cand-bsr-by-vif-addr { %help: short "Specify the virtual interface's address to use as the Candidate-BSR"; %set:; } bsr-priority { %help: short "Configure the Candidate-BSR priority"; %allow-range: $(@) "0" "255" %help: "The Candidate-BSR priority"; %set:; } hash-mask-len { %help: short "Configure the hash mask length for the hash function"; %allow-range: $(@) "4" "32" %help: "The hash mask length for the hash function"; %set:; } } } cand-rp { %help: short "Configure this router as a Candidate-RP"; group-prefix @ { %help: short "Configure a scope zone in this Candidate-RP"; %mandatory: $(@.cand-rp-by-vif-name); %create: xrl "$(pimsm4.targetname)/pim/0.1/add_config_cand_rp4?group_prefix:ipv4net=$(group-prefix.@)&is_scope_zone:bool=$(@.is-scope-zone)&vif_name:txt=$(@.cand-rp-by-vif-name)&vif_addr:ipv4=$(@.cand-rp-by-vif-addr)&rp_priority:u32=$(@.rp-priority)&rp_holdtime:u32=$(@.rp-holdtime)"; %update: xrl "$(pimsm4.targetname)/pim/0.1/add_config_cand_rp4?group_prefix:ipv4net=$(group-prefix.@)&is_scope_zone:bool=$(@.is-scope-zone)&vif_name:txt=$(@.cand-rp-by-vif-name)&vif_addr:ipv4=$(@.cand-rp-by-vif-addr)&rp_priority:u32=$(@.rp-priority)&rp_holdtime:u32=$(@.rp-holdtime)"; %delete: xrl "$(pimsm4.targetname)/pim/0.1/delete_config_cand_rp4?group_prefix:ipv4net=$(group-prefix.@)&is_scope_zone:bool=$(@.is-scope-zone)&vif_name:txt=$(@.cand-rp-by-vif-name)&vif_addr:ipv4=$(@.cand-rp-by-vif-addr)"; is-scope-zone { %help: short "Flag to indicate a scoped or global zone"; %set:; } cand-rp-by-vif-name { %help: short "Specify the virtual interface with the address of the Candidate-RP"; %set:; } cand-rp-by-vif-addr { %help: short "Specify the virtual interface's address to use as the Candidate-RP"; %set:; } rp-priority { %help: short "Configure the Candidate-RP priority"; %allow-range: $(@) "0" "255" %help: "The Candidate-RP priority"; %set:; } rp-holdtime { %help: short "Configure the Candidate-RP holdtime (in seconds)"; %allow-range: $(@) "0" "65535" %help: "The Candidate-RP holdtime (in seconds)"; %set:; } } } } switch-to-spt-threshold { %help: short "Configure the shortest-path switch threshold"; %activate: xrl "$(pimsm4.targetname)/pim/0.1/set_switch_to_spt_threshold?is_enabled:bool=`~$(@.disable)`&interval_sec:u32=$(@.interval)&bytes:u32=$(@.bytes)"; %update: xrl "$(pimsm4.targetname)/pim/0.1/set_switch_to_spt_threshold?is_enabled:bool=`~$(@.disable)`&interval_sec:u32=$(@.interval)&bytes:u32=$(@.bytes)"; %delete: xrl "$(pimsm4.targetname)/pim/0.1/reset_switch_to_spt_threshold"; disable { %help: short "Disable the shortest-path switching"; %set:; } enabled { %deprecated: "Statement 'enabled: true/false' is replaced with 'disable: false/true'"; %help: short "Enable the shortest-path switching"; %set:; } interval { %help: short "The frequency of measuring the bandwidth threshold (sec)"; %allow-range: $(@) "3" "2147483647" %help: "The frequency of measuring the bandwidth threshold (sec)"; %set:; } bytes { %help: short "The bandwidth threshold (in bytes) per interval"; %set:; } } traceoptions { %help: short "Configure the tracing options"; flag { %help: short "Configure the tracing operation to perform"; all { %help: short "Configure all tracing operations"; disable { %help: short "Disable all tracing operations"; %set: xrl "$(pimsm4.targetname)/pim/0.1/log_trace_all?enable:bool=`~$(@)`"; %delete: xrl "$(pimsm4.targetname)/pim/0.1/log_trace_all?enable:bool=$(DEFAULT)"; } enabled { %deprecated: "Statement 'enabled: true/false' is replaced with 'disable: false/true'"; %help: short "Enable all tracing operations"; %set: xrl "$(pimsm4.targetname)/pim/0.1/log_trace_all?enable:bool=$(@)"; } } } } } } xorp/etc/templates/firewall.tp0000664000076400007640000001665311421137511016612 0ustar greearbgreearb/* $XORP: xorp/etc/templates/firewall.tp,v 1.4 2008/04/26 00:59:41 pavlin Exp $ */ firewall { targetname: txt = "fea"; rule4 @: u32 { action: txt; protocol: u32 = 0; source { interface: txt = ""; vif: txt = ""; network: ipv4net = 0.0.0.0/0; port-begin: u32 = 0; port-end: u32 = 65535; } destination { network: ipv4net = 0.0.0.0/0; port-begin: u32 = 0; port-end: u32 = 65535; } } rule6 @: u32 { action: txt; protocol: u32 = 0; source { interface: txt = ""; vif: txt = ""; network: ipv6net = ::/0; port-begin: u32 = 0; port-end: u32 = 65535; } destination { network: ipv6net = ::/0; port-begin: u32 = 0; port-end: u32 = 65535; } } } firewall { %help: short "Configure the firewall"; %modinfo: provides firewall; %modinfo: depends interfaces; %modinfo: path "xorp_fea"; %modinfo: default_targetname "fea"; %modinfo: start_commit xrl "$(interfaces.targetname)/fea_firewall/0.1/start_transaction->tid:u32=$(firewall.TID)"; %modinfo: end_commit xrl "$(interfaces.targetname)/fea_firewall/0.1/commit_transaction?tid:u32=$(firewall.TID)"; %modinfo: status_method xrl "$(interfaces.targetname)/common/0.1/get_status->status:u32&reason:txt"; %modinfo: startup_method xrl "$(interfaces.targetname)/fea_firewall/0.1/startup_firewall"; %mandatory: $(@.targetname); targetname { %user-hidden: "XRL target name"; %help: short "XRL target name"; %set:; } TID { %user-hidden: "Transaction ID generated by the FEA"; %create:; } rule4 @: u32 { %help: short "Firewall IPv4 rule"; %order: sorted-numeric; %mandatory: $(@.action); %create: xrl "$(interfaces.targetname)/fea_firewall/0.1/add_entry4?tid:u32=$(firewall.TID)&rule_number:u32=$(@)&ifname:txt=$(@.source.interface)&vifname:txt=$(@.source.vif)&src_network:ipv4net=$(@.source.network)&dst_network:ipv4net=$(@.destination.network)&ip_protocol:u32=$(@.protocol)&src_port_begin:u32=$(@.source.port-begin)&src_port_end:u32=$(@.source.port-end)&dst_port_begin:u32=$(@.destination.port-begin)&dst_port_end:u32=$(@.destination.port-end)&action:txt=$(@.action)"; %update: xrl "$(interfaces.targetname)/fea_firewall/0.1/replace_entry4?tid:u32=$(firewall.TID)&rule_number:u32=$(@)&ifname:txt=$(@.source.interface)&vifname:txt=$(@.source.vif)&src_network:ipv4net=$(@.source.network)&dst_network:ipv4net=$(@.destination.network)&ip_protocol:u32=$(@.protocol)&src_port_begin:u32=$(@.source.port-begin)&src_port_end:u32=$(@.source.port-end)&dst_port_begin:u32=$(@.destination.port-begin)&dst_port_end:u32=$(@.destination.port-end)&action:txt=$(@.action)"; %delete: xrl "$(interfaces.targetname)/fea_firewall/0.1/delete_entry4?tid:u32=$(firewall.TID)&rule_number:u32=$(@)&ifname:txt=$(@.source.interface)&vifname:txt=$(@.source.vif)&src_network:ipv4net=$(@.source.network)&dst_network:ipv4net=$(@.destination.network)&ip_protocol:u32=$(@.protocol)&src_port_begin:u32=$(@.source.port-begin)&src_port_end:u32=$(@.source.port-end)&dst_port_begin:u32=$(@.destination.port-begin)&dst_port_end:u32=$(@.destination.port-end)"; action { %help: short "Firewall rule action"; %allow: $(@) "none" %help: "No action"; %allow: $(@) "pass" %help: "Pass matching packets"; %allow: $(@) "drop" %help: "Drop matching packets"; %allow: $(@) "reject" %help: "Reject matching packets"; } protocol { %help: short "The IP protocol number or 0 if wildcard"; %allow-range: $(@) "0" "255" %help: "The IP protocol number or 0 if wildcard"; } source { %help: short "The source information"; interface { %help: short "The incoming interface name"; } vif { %help: short "The incoming vif name"; } network { %help: short "The source network address prefix"; } port-begin { %help: short "The source TCP/UDP begin port"; %allow-range: $(@) "0" "65535" %help: "The source TCP/UDP begin port"; } port-end { %help: short "The source TCP/UDP end port"; %allow-range: $(@) "0" "65535" %help: "The source TCP/UDP end port"; } } destination { %help: short "The destination information"; network { %help: short "The destination network address prefix"; } port-begin { %help: short "The destination TCP/UDP begin port"; %allow-range: $(@) "0" "65535" %help: "The destination TCP/UDP begin port"; } port-end { %help: short "The destination TCP/UDP end port"; %allow-range: $(@) "0" "65535" %help: "The destination TCP/UDP end port"; } } } rule6 @: u32 { %help: short "Firewall IPv6 rule"; %order: sorted-numeric; %mandatory: $(@.action); %create: xrl "$(interfaces.targetname)/fea_firewall/0.1/add_entry6?tid:u32=$(firewall.TID)&rule_number:u32=$(@)&ifname:txt=$(@.source.interface)&vifname:txt=$(@.source.vif)&src_network:ipv6net=$(@.source.network)&dst_network:ipv6net=$(@.destination.network)&ip_protocol:u32=$(@.protocol)&src_port_begin:u32=$(@.source.port-begin)&src_port_end:u32=$(@.source.port-end)&dst_port_begin:u32=$(@.destination.port-begin)&dst_port_end:u32=$(@.destination.port-end)&action:txt=$(@.action)"; %update: xrl "$(interfaces.targetname)/fea_firewall/0.1/replace_entry6?tid:u32=$(firewall.TID)&rule_number:u32=$(@)&ifname:txt=$(@.source.interface)&vifname:txt=$(@.source.vif)&src_network:ipv6net=$(@.source.network)&dst_network:ipv6net=$(@.destination.network)&ip_protocol:u32=$(@.protocol)&src_port_begin:u32=$(@.source.port-begin)&src_port_end:u32=$(@.source.port-end)&dst_port_begin:u32=$(@.destination.port-begin)&dst_port_end:u32=$(@.destination.port-end)&action:txt=$(@.action)"; %delete: xrl "$(interfaces.targetname)/fea_firewall/0.1/delete_entry6?tid:u32=$(firewall.TID)&rule_number:u32=$(@)&ifname:txt=$(@.source.interface)&vifname:txt=$(@.source.vif)&src_network:ipv6net=$(@.source.network)&dst_network:ipv6net=$(@.destination.network)&ip_protocol:u32=$(@.protocol)&src_port_begin:u32=$(@.source.port-begin)&src_port_end:u32=$(@.source.port-end)&dst_port_begin:u32=$(@.destination.port-begin)&dst_port_end:u32=$(@.destination.port-end)"; action { %help: short "Firewall rule action"; %allow: $(@) "none" %help: "No action"; %allow: $(@) "pass" %help: "Pass matching packets"; %allow: $(@) "drop" %help: "Drop matching packets"; %allow: $(@) "reject" %help: "Reject matching packets"; } protocol { %help: short "The IP protocol number or 0 if wildcard"; %allow-range: $(@) "0" "255" %help: "The IP protocol number or 0 if wildcard"; } source { %help: short "The source information"; interface { %help: short "The incoming interface name"; } vif { %help: short "The incoming vif name"; } network { %help: short "The source network address prefix"; } port-begin { %help: short "The source TCP/UDP begin port"; %allow-range: $(@) "0" "65535" %help: "The source TCP/UDP begin port"; } port-end { %help: short "The source TCP/UDP end port"; %allow-range: $(@) "0" "65535" %help: "The source TCP/UDP end port"; } } destination { %help: short "The destination information"; network { %help: short "The destination network address prefix"; } port-begin { %help: short "The destination TCP/UDP begin port"; %allow-range: $(@) "0" "65535" %help: "The destination TCP/UDP begin port"; } port-end { %help: short "The destination TCP/UDP end port"; %allow-range: $(@) "0" "65535" %help: "The destination TCP/UDP end port"; } } } } xorp/etc/templates/igmp.cmds0000664000076400007640000000131611421137511016232 0ustar greearbgreearb/* $XORP: xorp/etc/templates/igmp.cmds,v 1.3 2004/05/26 04:25:08 pavlin Exp $ */ show igmp { %command: "" %help: HELP; %module: igmp; %tag: HELP "Display information about IGMP"; } show igmp group { %command: "cli_send_processor_xrl -t IGMP $0" %help: HELP; %module: igmp; %tag: HELP "Display information about IGMP group membership"; } show igmp interface { %command: "cli_send_processor_xrl -t IGMP $0" %help: HELP; %module: igmp; %tag: HELP "Display information about IGMP interfaces"; } show igmp interface address { %command: "cli_send_processor_xrl -t IGMP $0" %help: HELP; %module: igmp; %tag: HELP "Display information about addresses of IGMP interfaces"; } xorp/etc/templates/mfea.cmds0000664000076400007640000000203211421137511016202 0ustar greearbgreearb/* $XORP: xorp/etc/templates/mfea.cmds,v 1.4 2004/06/02 02:38:41 pavlin Exp $ */ show mfea { %command: "" %help: HELP; %module: mfea4; %tag: HELP "Display information about IPv4 MFEA"; } show mfea dataflow { %command: "cli_send_processor_xrl -t MFEA_4 $0" %help: HELP; %module: mfea4; %tag: HELP "Display information about MFEA IPv4 dataflow filters"; } show mfea interface { %command: "cli_send_processor_xrl -t MFEA_4 $0" %help: HELP; %module: mfea4; %tag: HELP "Display information about MFEA IPv4 interfaces"; } show mfea interface address { %command: "cli_send_processor_xrl -t MFEA_4 $0" %help: HELP; %module: mfea4; %tag: HELP "Display information about addresses of MFEA IPv4 interfaces"; } /* * Commented-out, because when we run everything through the rtrmgr, * the MFEA is not used to get the routes from the kernel. show mfea mrib { %command: "cli_send_processor_xrl -t MFEA_4 $0" %help: HELP; %module: mfea4; %tag: HELP "Display MRIB IPv4 information inside MFEA"; } */ xorp/etc/templates/rib.cmds0000664000076400007640000006307111421137511016060 0ustar greearbgreearb /* $XORP: xorp/etc/templates/rib.cmds,v 1.12 2008/04/24 15:20:18 bms Exp $ */ /* * At the time this file was written, route redistribution from the * rib only occurs from IGP tables and the ribout. Redistribution from * ribout can be filtered to match a particular protocol. * */ show route { %command: "" %help: HELP; %module: rib; %tag: HELP "Show routes"; } show route admin { %command: "" %help: HELP; %module: rib; %tag: HELP "Show RIB administrative information"; } show route table { %command: "" %help: HELP; %module: rib; %tag: HELP "Show routes from a RIB table"; } show route table ipv4 { %command: "" %help: HELP; %module: rib; %tag: HELP "Show IPv4 routes"; } show route table ipv6 { %command: "" %help: HELP; %module: rib; %tag: HELP "Show IPv6 routes"; } show route table ipv4 unicast { %command: "" %help: HELP; %module: rib; %tag: HELP "Show IPv4 unicast routes"; } show route table ipv6 unicast { %command: "" %help: HELP; %module: rib; %tag: HELP "Show IPv6 unicast routes"; } show route table ipv4 multicast { %command: "" %help: HELP; %module: rib; %tag: HELP "Show IPv4 MRIB routes"; } show route table ipv6 multicast { %command: "" %help: HELP; %module: rib; %tag: HELP "Show IPv6 MRIB routes"; } /* * Administrative distance commands */ show route admin distance { %command: "" %help: HELP; %module: rib; %tag: HELP "Show RIB administrative distances"; } show route admin distance ipv4 { %command: "" %help: HELP; %module: rib; %tag: HELP "Show IPv4 administrative distances"; } show route admin distance ipv6 { %command: "" %help: HELP; %module: rib; %tag: HELP "Show IPv6 administrative distances"; } show route admin distance ipv4 unicast { %command: "rib_show_distances ribin $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv4 unicast protocol administrative distances"; } show route admin distance ipv6 unicast { %command: "rib_show_distances ribin $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv6 unicast protocol administrative distances"; } show route admin distance ipv4 multicast { %command: "rib_show_distances ribin $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv4 multicast protocol administrative distances"; } show route admin distance ipv6 multicast { %command: "rib_show_distances ribin $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv6 multicast protocol administrative distances"; } /* * Connected show route table commands */ show route table ipv4 unicast connected { %command: "rib_show_routes ribin $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv4 routes from connected interfaces"; } show route table ipv6 unicast connected { %command: "rib_show_routes ribin $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv6 routes from connected interfaces"; } show route table ipv4 unicast connected brief { %command: "rib_show_routes -b ribin $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv4 routes from connected interfaces (brief format)"; } show route table ipv6 unicast connected brief { %command: "rib_show_routes -b ribin $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv6 routes from connected interfaces (brief format)"; } show route table ipv4 unicast connected detail { %command: "rib_show_routes -d ribin $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv4 routes from connected interfaces (detailed format)"; } show route table ipv6 unicast connected detail { %command: "rib_show_routes -d ribin $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv6 routes from connected interfaces (detailed format)"; } show route table ipv4 unicast connected terse { %command: "rib_show_routes -t ribin $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv4 routes from connected interfaces (terse format)"; } show route table ipv6 unicast connected terse { %command: "rib_show_routes -t ribin $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv6 routes from connected interfaces (terse format)"; } /* * Static show route table commands */ show route table ipv4 unicast static { %command: "rib_show_routes ribin $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv4 static routes"; } show route table ipv6 unicast static { %command: "rib_show_routes ribin $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv6 static routes"; } show route table ipv4 multicast static { %command: "rib_show_routes ribin $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv4 MRIB static routes"; } show route table ipv6 multicast static { %command: "rib_show_routes ribin $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv6 MRIB static routes"; } show route table ipv4 unicast static brief { %command: "rib_show_routes -b ribin $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv4 static routes (brief format)"; } show route table ipv6 unicast static brief { %command: "rib_show_routes -b ribin $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv6 static routes (brief format)"; } show route table ipv4 multicast static brief { %command: "rib_show_routes -b ribin $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv4 MRIB static routes (brief format)"; } show route table ipv6 multicast static brief { %command: "rib_show_routes -b ribin $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv6 MRIB static routes (brief format)"; } show route table ipv4 unicast static detail { %command: "rib_show_routes -d ribin $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv4 static routes (detailed format)"; } show route table ipv6 unicast static detail { %command: "rib_show_routes -d ribin $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv6 static routes (detailed format)"; } show route table ipv4 multicast static detail { %command: "rib_show_routes -d ribin $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv4 MRIB static routes (detailed format)"; } show route table ipv6 multicast static detail { %command: "rib_show_routes -d ribin $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv6 MRIB static routes (detailed format)"; } show route table ipv4 unicast static terse { %command: "rib_show_routes -t ribin $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv4 static routes (terse format)"; } show route table ipv6 unicast static terse { %command: "rib_show_routes -t ribin $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv6 static routes (terse format)"; } show route table ipv4 multicast static terse { %command: "rib_show_routes -t ribin $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv4 MRIB static routes (terse format)"; } show route table ipv6 multicast static terse { %command: "rib_show_routes -t ribin $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv6 MRIB static routes (terse format)"; } /* * fib2mrib */ show route table ipv4 multicast fib2mrib { %command: "rib_show_routes ribin $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv4 unicast routes for multicast extracted from kernel"; } show route table ipv6 multicast fib2mrib { %command: "rib_show_routes ribin $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv6 unicast routes for multicast extracted from kernel"; } show route table ipv4 multicast fib2mrib brief { %command: "rib_show_routes -b ribin $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv4 unicast routes for multicast extracted from kernel (brief format)"; } show route table ipv6 multicast fib2mrib brief { %command: "rib_show_routes -b ribin $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv6 unicast routes for multicast extracted from kernel (brief format)"; } show route table ipv4 multicast fib2mrib detail { %command: "rib_show_routes -d ribin $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv4 unicast routes for multicast extracted from kernel (detailed format)"; } show route table ipv6 multicast fib2mrib detail { %command: "rib_show_routes -d ribin $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv6 unicast routes for multicast extracted from kernel (detailed format)"; } show route table ipv4 multicast fib2mrib terse { %command: "rib_show_routes -t ribin $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv4 unicast routes for multicast extracted from kernel (terse format)"; } show route table ipv6 multicast fib2mrib terse { %command: "rib_show_routes -t ribin $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv6 unicast routes for multicast extracted from kernel (terse format)"; } /* * BGP */ show route table ipv4 unicast ibgp { %command: "rib_show_routes ribout $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv4 iBGP routes"; } show route table ipv4 unicast ebgp { %command: "rib_show_routes ribout $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv4 eBGP routes"; } show route table ipv4 multicast ibgp { %command: "rib_show_routes ribout $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv4 iBGP MBGP routes"; } show route table ipv4 multicast ebgp { %command: "rib_show_routes ribout $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv4 eBGP MBGP routes"; } show route table ipv6 unicast ibgp { %command: "rib_show_routes ribout $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv6 iBGP routes"; } show route table ipv6 unicast ebgp { %command: "rib_show_routes ribout $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv6 eBGP routes"; } show route table ipv6 multicast ibgp { %command: "rib_show_routes ribout $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv6 iBGP MBGP routes"; } show route table ipv6 multicast ebgp { %command: "rib_show_routes ribout $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv6 eBGP MBGP routes"; } show route table ipv4 unicast ibgp brief { %command: "rib_show_routes -b ribout $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv4 iBGP routes (brief format)"; } show route table ipv4 unicast ebgp brief { %command: "rib_show_routes -b ribout $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv4 eBGP routes (brief format)"; } show route table ipv4 multicast ibgp brief { %command: "rib_show_routes -b ribout $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv4 iBGP MBGP routes (brief format)"; } show route table ipv4 multicast ebgp brief { %command: "rib_show_routes -b ribout $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv4 eBGP MBGP routes (brief format)"; } show route table ipv6 unicast ibgp brief { %command: "rib_show_routes -b ribout $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv6 iBGP routes (brief format)"; } show route table ipv6 unicast ebgp brief { %command: "rib_show_routes -b ribout $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv6 eBGP routes (brief format)"; } show route table ipv6 multicast ibgp brief { %command: "rib_show_routes -b ribout $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv6 iBGP MBGP routes (brief format)"; } show route table ipv6 multicast ebgp brief { %command: "rib_show_routes -b ribout $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv6 eBGP MBGP routes (brief format)"; } show route table ipv4 unicast ibgp detail { %command: "rib_show_routes -d ribout $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv4 iBGP routes (detailed format)"; } show route table ipv4 unicast ebgp detail { %command: "rib_show_routes -d ribout $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv4 eBGP routes (detailed format)"; } show route table ipv4 multicast ibgp detail { %command: "rib_show_routes -d ribout $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv4 iBGP MBGP routes (detailed format)"; } show route table ipv4 multicast ebgp detail { %command: "rib_show_routes -d ribout $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv4 eBGP MBGP routes (detailed format)"; } show route table ipv6 unicast ibgp detail { %command: "rib_show_routes -d ribout $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv6 iBGP routes (detailed format)"; } show route table ipv6 unicast ebgp detail { %command: "rib_show_routes -d ribout $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv6 eBGP routes (detailed format)"; } show route table ipv6 multicast ibgp detail { %command: "rib_show_routes -d ribout $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv6 iBGP MBGP routes (detailed format)"; } show route table ipv6 multicast ebgp detail { %command: "rib_show_routes -d ribout $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv6 eBGP MBGP routes (detailed format)"; } show route table ipv4 unicast ibgp terse { %command: "rib_show_routes -t ribout $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv4 iBGP routes (terse format)"; } show route table ipv4 unicast ebgp terse { %command: "rib_show_routes -t ribout $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv4 eBGP routes (terse format)"; } show route table ipv4 multicast ibgp terse { %command: "rib_show_routes -t ribout $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv4 iBGP MBGP routes (terse format)"; } show route table ipv4 multicast ebgp terse { %command: "rib_show_routes -t ribout $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv4 eBGP MBGP routes (terse format)"; } show route table ipv6 unicast ibgp terse { %command: "rib_show_routes -t ribout $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv6 iBGP routes (terse format)"; } show route table ipv6 unicast ebgp terse { %command: "rib_show_routes -t ribout $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv6 eBGP routes (terse format)"; } show route table ipv6 multicast ibgp terse { %command: "rib_show_routes -t ribout $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv6 iBGP MBGP routes (terse format)"; } show route table ipv6 multicast ebgp terse { %command: "rib_show_routes -t ribout $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show IPv6 eBGP MBGP routes (terse format)"; } /* * RIP show route commands */ show route table ipv4 unicast rip { %command: "rib_show_routes ribin $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show routes from RIP"; } show route table ipv4 unicast rip winners { %command: "rib_show_routes ribout $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show RIP that reach forwarding engine"; } show route table ipv4 unicast rip brief { %command: "rib_show_routes -b ribin $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show routes from RIP (brief format)"; } show route table ipv4 unicast rip winners brief { %command: "rib_show_routes -b ribout $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show RIP that reach forwarding engine (brief format)"; } show route table ipv4 unicast rip detail { %command: "rib_show_routes -d ribin $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show routes from RIP (detailed format)"; } show route table ipv4 unicast rip winners detail { %command: "rib_show_routes -d ribout $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show RIP that reach forwarding engine (detailed format)"; } show route table ipv4 unicast rip terse { %command: "rib_show_routes -t ribin $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show routes from RIP (terse format)"; } show route table ipv4 unicast rip winners terse { %command: "rib_show_routes -t ribout $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show RIP that reach forwarding engine (terse format)"; } /* * RIPng show route commands */ show route table ipv6 unicast ripng { %command: "rib_show_routes ribin $4 $5 rip" %help: HELP; %module: rib; %tag: HELP "Show routes from RIPng"; } show route table ipv6 unicast ripng winners { %command: "rib_show_routes ribout $4 $5 rip" %help: HELP; %module: rib; %tag: HELP "Show RIPng that reach forwarding engine"; } show route table ipv6 unicast ripng brief { %command: "rib_show_routes -b ribin $4 $5 rip" %help: HELP; %module: rib; %tag: HELP "Show routes from RIPng (brief format)"; } show route table ipv6 unicast ripng winners brief { %command: "rib_show_routes -b ribout $4 $5 rip" %help: HELP; %module: rib; %tag: HELP "Show RIPng that reach forwarding engine (brief format)"; } show route table ipv6 unicast ripng detail { %command: "rib_show_routes -d ribin $4 $5 rip" %help: HELP; %module: rib; %tag: HELP "Show routes from RIPng (detailed format)"; } show route table ipv6 unicast ripng winners detail { %command: "rib_show_routes -d ribout $4 $5 rip" %help: HELP; %module: rib; %tag: HELP "Show RIPng that reach forwarding engine (detailed format)"; } show route table ipv6 unicast ripng terse { %command: "rib_show_routes -t ribin $4 $5 rip" %help: HELP; %module: rib; %tag: HELP "Show routes from RIPng (terse format)"; } show route table ipv6 unicast ripng winners terse { %command: "rib_show_routes -t ribout $4 $5 rip" %help: HELP; %module: rib; %tag: HELP "Show RIPng that reach forwarding engine (terse format)"; } /* * OSPFv2 show route commands */ show route table ipv4 unicast ospf { %command: "rib_show_routes ribin $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show routes from OSPFv2"; } show route table ipv4 unicast ospf winners { %command: "rib_show_routes ribout $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show OSPFv2 that reach forwarding engine"; } show route table ipv4 unicast ospf brief { %command: "rib_show_routes -b ribin $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show routes from OSPFv2 (brief format)"; } show route table ipv4 unicast ospf winners brief { %command: "rib_show_routes -b ribout $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show OSPFv2 that reach forwarding engine (brief format)"; } show route table ipv4 unicast ospf detail { %command: "rib_show_routes -d ribin $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show routes from OSPFv2 (detailed format)"; } show route table ipv4 unicast ospf winners detail { %command: "rib_show_routes -d ribout $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show OSPFv2 that reach forwarding engine (detailed format)"; } show route table ipv4 unicast ospf terse { %command: "rib_show_routes -t ribin $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show routes from OSPFv2 (terse format)"; } show route table ipv4 unicast ospf winners terse { %command: "rib_show_routes -t ribout $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show OSPFv2 that reach forwarding engine (terse format)"; } /* * OSPFv3 show route commands */ show route table ipv6 unicast ospf { %command: "rib_show_routes ribin $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show routes from OSPFv3"; } show route table ipv6 unicast ospf winners { %command: "rib_show_routes ribout $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show OSPFv3 that reach forwarding engine"; } show route table ipv6 unicast ospf brief { %command: "rib_show_routes -b ribin $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show routes from OSPFv3 (brief format)"; } show route table ipv6 unicast ospf winners brief { %command: "rib_show_routes -b ribout $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show OSPFv3 that reach forwarding engine (brief format)"; } show route table ipv6 unicast ospf detail { %command: "rib_show_routes -d ribin $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show routes from OSPFv3 (detailed format)"; } show route table ipv6 unicast ospf winners detail { %command: "rib_show_routes -d ribout $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show OSPFv3 that reach forwarding engine (detailed format)"; } show route table ipv6 unicast ospf terse { %command: "rib_show_routes -t ribin $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show routes from OSPFv3 (terse format)"; } show route table ipv6 unicast ospf winners terse { %command: "rib_show_routes -t ribout $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show OSPFv3 that reach forwarding engine (terse format)"; } /* * OLSRv1 IPv4 show route commands */ show route table ipv4 unicast olsr { %command: "rib_show_routes ribin $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show routes from OLSRv1"; } show route table ipv4 unicast olsr winners { %command: "rib_show_routes ribout $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show OLSRv1 that reach forwarding engine"; } show route table ipv4 unicast olsr brief { %command: "rib_show_routes -b ribin $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show routes from OLSRv1 (brief format)"; } show route table ipv4 unicast olsr winners brief { %command: "rib_show_routes -b ribout $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show OLSRv1 that reach forwarding engine (brief format)"; } show route table ipv4 unicast olsr detail { %command: "rib_show_routes -d ribin $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show routes from OLSRv1 (detailed format)"; } show route table ipv4 unicast olsr winners detail { %command: "rib_show_routes -d ribout $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show OLSRv1 that reach forwarding engine (detailed format)"; } show route table ipv4 unicast olsr terse { %command: "rib_show_routes -t ribin $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show routes from OLSRv1 (terse format)"; } show route table ipv4 unicast olsr winners terse { %command: "rib_show_routes -t ribout $4 $5 $6" %help: HELP; %module: rib; %tag: HELP "Show OLSRv1 that reach forwarding engine (terse format)"; } /* * Show winning routes (unfiltered output from "all" routing table). */ show route table ipv4 unicast final { %command: "rib_show_routes ribout $4 $5 all" %help: HELP; %module: rib; %tag: HELP "Show IPv4 winning routes"; } show route table ipv6 unicast final { %command: "rib_show_routes ribin $4 $5 all" %help: HELP; %module: rib; %tag: HELP "Show IPv6 winning routes"; } show route table ipv4 multicast final { %command: "rib_show_routes ribin $4 $5 all" %help: HELP; %module: rib; %tag: HELP "Show IPv4 MRIB winning routes"; } show route table ipv6 multicast final { %command: "rib_show_routes ribin $4 $5 all" %help: HELP; %module: rib; %tag: HELP "Show IPv6 MRIB winning routes"; } show route table ipv4 unicast final brief { %command: "rib_show_routes -b ribout $4 $5 all" %help: HELP; %module: rib; %tag: HELP "Show IPv4 winning routes (brief format)"; } show route table ipv6 unicast final brief { %command: "rib_show_routes -b ribout $4 $5 all" %help: HELP; %module: rib; %tag: HELP "Show IPv6 winning routes (brief format)"; } show route table ipv4 multicast final brief { %command: "rib_show_routes -b ribin $4 $5 all" %help: HELP; %module: rib; %tag: HELP "Show IPv4 MRIB winning routes (brief format)"; } show route table ipv6 multicast final brief { %command: "rib_show_routes -b ribin $4 $5 all" %help: HELP; %module: rib; %tag: HELP "Show IPv6 MRIB winning routes (brief format)"; } show route table ipv4 unicast final detail { %command: "rib_show_routes -d ribout $4 $5 all" %help: HELP; %module: rib; %tag: HELP "Show IPv4 winning routes (detailed format)"; } show route table ipv6 unicast final detail { %command: "rib_show_routes -d ribout $4 $5 all" %help: HELP; %module: rib; %tag: HELP "Show IPv6 winning routes (detailed format)"; } show route table ipv4 multicast final detail { %command: "rib_show_routes -d ribin $4 $5 all" %help: HELP; %module: rib; %tag: HELP "Show IPv4 MRIB winning routes (detailed format)"; } show route table ipv6 multicast final detail { %command: "rib_show_routes -d ribin $4 $5 all" %help: HELP; %module: rib; %tag: HELP "Show IPv6 MRIB winning routes (detailed format)"; } show route table ipv4 unicast final terse { %command: "rib_show_routes -t ribout $4 $5 all" %help: HELP; %module: rib; %tag: HELP "Show IPv4 winning routes (terse format)"; } show route table ipv6 unicast final terse { %command: "rib_show_routes -t ribout $4 $5 all" %help: HELP; %module: rib; %tag: HELP "Show IPv6 winning routes (terse format)"; } show route table ipv4 multicast final terse { %command: "rib_show_routes -t ribin $4 $5 all" %help: HELP; %module: rib; %tag: HELP "Show IPv4 MRIB winning routes (terse format)"; } show route table ipv6 multicast final terse { %command: "rib_show_routes -t ribin $4 $5 all" %help: HELP; %module: rib; %tag: HELP "Show IPv6 MRIB winning routes (terse format)"; } xorp/etc/templates/olsr4.tp0000664000076400007640000003405011421137511016037 0ustar greearbgreearb/* $XORP: xorp/etc/templates/olsr4.tp,v 1.3 2008/08/06 08:23:24 abittau Exp $ */ /* vim:set sts=4 ts=8 sw=4 syntax=c: */ /* ** OLSRv1 for IPv4 -- configuration commands. */ protocols { olsr4 { targetname: txt = "olsr4"; main-address: ipv4; willingness: u32 = 3; mpr-coverage: u32 = 1; /* notyet etx: bool = false; */ /* notyet mpr { mode: txt = "rfc3626"; "classic" "default" coverage: u32 = 1; } */ hello-interval: u32 = 2; refresh-interval: u32 = 2; mid-interval: u32 = 5; dup-hold-time: u32 = 30; interface @: txt { vif @: txt { address @: ipv4 { local-port: u32 = 698; all-nodes-address: ipv4 = 255.255.255.255; all-nodes-port: u32 = 698; interface-cost: u32 = 1; /* notyet auto-cost: bool = false; */ disable: toggle = false; } } } topology { interval: u32 = 5; redundancy: txt = "mprs"; /* fisheye: bool = false; */ } external { interval: u32 = 5; /* base-cost: u32 = 0; */ } traceoptions { flag { all { disable: toggle = false; } } } import: txt; export: txt; } } policy { policy-statement @: txt { term @: txt { from { metric: u32range; vtype: u32; originator: ipv4range; dest-main-address: ipv4range; } to { metric: u32range; vtype: u32; originator: ipv4range; dest-main-address: ipv4range; } then { metric: u32; } } } } protocols { olsr4 { %modinfo: provides olsr4; %modinfo: depends rib; %modinfo: depends policy; %modinfo: path "xorp_olsr4"; %modinfo: default_targetname "olsr4"; %modinfo: status_method xrl "$(olsr4.targetname)/common/0.1/get_status->status:u32&reason:txt"; %modinfo: shutdown_method xrl "$(olsr4.targetname)/common/0.1/shutdown"; %help: short "Configure the OLSRv1 IPv4 protocol"; %mandatory: $(@.main-address); %mandatory: $(@.targetname); targetname { %user-hidden: "XRL target name"; %help: short "XRL target name"; %set:; } main-address { %help: short "A unique IPv4 address within the MANET"; %help: long "This address uniquely identifies the router within an OLSRv1 MANET"; %set: xrl "$(olsr4.targetname)/olsr4/0.1/set_main_address?addr:ipv4=$(@)"; %get: xrl "$(olsr4.targetname)/olsr4/0.1/get_main_address->addr:ipv4"; } /* etx etx { %help: short "Use ETX metrics in this OLSRv1 domain"; %help: long "This option enables the use of ETX metrics, which is not compatible with RFC 3626 compliant OLSR."; } */ willingness { %help: short "Willingness-to-forward of this node"; %allow-range: $(@) "0" "7" %help: "The willingness-to-forward of this node (0 means node will never forward)"; %set: xrl "$(olsr4.targetname)/olsr4/0.1/set_willingness?willingness:u32=$(@)"; } mpr-coverage { %help: short "Count of MPRs which must cover two-hop neighbors"; %allow-range: $(@) "1" "32" %help: "The number of one-hop neighbors which must be selected to cover each two-hop neighbor"; %set: xrl "$(olsr4.targetname)/olsr4/0.1/set_mpr_coverage?coverage:u32=$(@)"; } hello-interval { %help: short "HELLO messages sent every interval seconds"; %allow-range: $(@) "1" "128" %help: "The interval between HELLO message (in seconds)"; %set: xrl "$(olsr4.targetname)/olsr4/0.1/set_hello_interval?interval:u32=$(@)"; } refresh-interval { %help: short "HELLO messages expected from peers within interval seconds"; %allow-range: $(@) "1" "128" %help: "The expected interval for the arrival of HELLO messages from peers in order to refresh link status (in seconds)"; %set: xrl "$(olsr4.targetname)/olsr4/0.1/set_refresh_interval?interval:u32=$(@)"; } mid-interval { %help: short "MID messages sent every interval seconds"; %allow-range: $(@) "1" "128" %help: "The interval between Multiple Interface Declaration messages (in seconds)"; %set: xrl "$(olsr4.targetname)/olsr4/0.1/set_mid_interval?interval:u32=$(@)"; } dup-hold-time { %help: short "Duplicate messages ignored for interval seconds"; %allow-range: $(@) "1" "128" %help: "The hold time for messages detected as duplicates (in seconds)"; %set: xrl "$(olsr4.targetname)/olsr4/0.1/set_dup_hold_time?dup_hold_time:u32=$(@)"; } interface @: txt { %help: short "Include an interface in this OLSRv1 MANET"; vif @: txt { %help: short "Include an vif in this OLSRv1 MANET"; address @ { %help: short "Address on vif used for OLSRv1"; %create: xrl "$(olsr4.targetname)/olsr4/0.1/bind_address?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&local_addr:ipv4=$(@)&local_port:u32=$(@.local-port)&all_nodes_addr:ipv4=$(@.all-nodes-address)&all_nodes_port:u32=$(@.all-nodes-port)"; %delete: xrl "$(olsr4.targetname)/olsr4/0.1/unbind_address?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)"; %mandatory: $(@.local-port); %mandatory: $(@.all-nodes-address); %mandatory: $(@.all-nodes-port); local-port { %help: short "Set the UDP port where OLSRv1 will listen on this interface"; %allow-range: $(@) "1" "65535" %help: "The listen port"; %set: xrl "$(olsr4.targetname)/olsr4/0.1/change_local_addr_port?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&local_addr:ipv4=$(address.@)&local_port:u32=$(@)"; } all-nodes-address { %help: short "Set the IPv4 address where OLSRv1 will send control traffic from this interface"; %set: xrl "$(olsr4.targetname)/olsr4/0.1/change_all_nodes_addr_port?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&all_nodes_addr:ipv4=$(@)&all_nodes_port:u32=$(address.@.all-nodes-port)"; } all-nodes-port { %help: short "Set the UDP port where OLSRv1 will send control traffic on this interface"; %allow-range: $(@) "1" "65535" %help: "The transmit port"; %set: xrl "$(olsr4.targetname)/olsr4/0.1/change_all_nodes_addr_port?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&all_nodes_addr:ipv4=$(address.@.all-nodes-address)&all_nodes_port:u32=$(@)"; } disable { %help: short "Disable OLSRv1 on interface"; %set: xrl "$(olsr4.targetname)/olsr4/0.1/set_binding_enabled?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&enabled:bool=`~$(@)`"; %delete: xrl "$(olsr4.targetname)/olsr4/0.1/set_binding_enabled?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&enabled:bool=`~$(DEFAULT)`"; } } } } topology { %help: short "Configure TC sub-protocol options"; interval { %help: short "TC messages sent every interval seconds"; %allow-range: $(@) "1" "128" %help: "The interval between Topology Control advertisement messages (in seconds)"; %set: xrl "$(olsr4.targetname)/olsr4/0.1/set_tc_interval?interval:u32=$(@)"; } redundancy { %help: short "Contents of Advertised Neighbor Set in transmitted TCs"; %allow: $(@) "mprs" %help: "MPRs only"; %allow: $(@) "mprs-and-selectors" %help: "MPRs and MPR selectors only"; %allow: $(@) "all" %help: "All neighbors"; %set: xrl "$(olsr4.targetname)/olsr4/0.1/set_tc_redundancy?redundancy:txt=$(@)"; } /* notyet fisheye { %help: short "Enable fish-eye topology broadcasts"; %set: xrl "$(olsr4.targetname)/olsr4/0.1/set_tc_fisheye?enabled:bool=$(@)"; %help: long "This option causes Topology Control messages to be more gradually diffused throughout the network."; } */ } external { %help: short "Configure HNA sub-protocol options"; interval { %help: short "HNA messages sent every interval seconds"; %allow-range: $(@) "1" "128" %help: "The interval between Host and Network Association messages (in seconds)"; %set: xrl "$(olsr4.targetname)/olsr4/0.1/set_hna_interval?interval:u32=$(@)"; } /* base-cost { %help: short "Base cost of learned HNA routes"; %allow-range: $(@) "0" "65535" %help: "The base cost of learned HNA routes when redistributed to the RIB (and visible to other protocols)"; %set: xrl "$(olsr4.targetname)/olsr4/0.1/set_hna_base_cost?base_cost:u32=$(@)"; } */ } traceoptions { %help: short "Configure the tracing options"; flag { %help: short "Configure the tracing operation to perform"; all { %help: short "Configure all tracing operations"; disable { %help: short "Disable all tracing operations"; %set: xrl "$(olsr4.targetname)/olsr4/0.1/trace?tvar:txt=all&enable:bool=`~$(@)`"; %delete: xrl "$(olsr4.targetname)/olsr4/0.1/trace?tvar:txt=all&enable:bool=$(DEFAULT)"; } } } } import { %help: short "Import policy"; %delete: xrl "$(policy.targetname)/policy/0.1/import?protocol:txt=olsr4&policies:txt=&modifier:txt="; %set: xrl "$(policy.targetname)/policy/0.1/import?protocol:txt=olsr4&policies:txt=$(@)&modifier:txt="; } export { %help: short "Export policy"; %delete: xrl "$(policy.targetname)/policy/0.1/export?protocol:txt=olsr4&policies:txt=&modifier:txt="; %set: xrl "$(policy.targetname)/policy/0.1/export?protocol:txt=olsr4&policies:txt=$(@)&modifier:txt="; } } } policy { %create: xrl "$(policy.targetname)/policy/0.1/set_proto_target?protocol:txt=olsr4&target:txt=olsr4"; %create: xrl "$(policy.targetname)/policy/0.1/add_varmap?protocol:txt=olsr4&variable:txt=network4&type:txt=ipv4net&access:txt=r&id:u32=10"; %create: xrl "$(policy.targetname)/policy/0.1/add_varmap?protocol:txt=olsr4&variable:txt=nexthop4&type:txt=ipv4nexthop&access:txt=rw&id:u32=11"; %create: xrl "$(policy.targetname)/policy/0.1/add_varmap?protocol:txt=olsr4&variable:txt=metric&type:txt=u32&access:txt=rw&id:u32=12"; %create: xrl "$(policy.targetname)/policy/0.1/add_varmap?protocol:txt=olsr4&variable:txt=vtype&type:txt=u32&access:txt=r&id:u32=13"; %create: xrl "$(policy.targetname)/policy/0.1/add_varmap?protocol:txt=olsr4&variable:txt=originator&type:txt=ipv4&access:txt=r&id:u32=14"; %create: xrl "$(policy.targetname)/policy/0.1/add_varmap?protocol:txt=olsr4&variable:txt=dest-main-addr&type:txt=ipv4&access:txt=r&id:u32=15"; policy-statement @: txt { term @: txt { from { metric { %help: short "Metric value"; %allow-operator: ":" "==" "!=" "<" ">" "<=" ">="; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt=metric $(<>) $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt="; } vtype { %help: short "Type of OLSRv1 route"; %allow-operator: ":" "==" "!="; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt=vtype $(<>) $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt="; } originator { %help: short "Originator of OLSRv1 route"; %allow-operator: ":" "==" "!=" "<" ">" "<=" ">="; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt=originator $(<>) $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt="; } dest-main-address { %help: short "Main address of OLSRv1 destination"; %allow-operator: ":" "==" "!=" "<" ">" "<=" ">="; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt=dest-main-address $(<>) $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt="; } } to { metric { %help: short "Metric value"; %allow-operator: ":" "==" "!=" "<" ">" "<=" ">="; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=1&order:txt=$(#)&statement:txt=metric $(<>) $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=1&order:txt=$(#)&statement:txt="; } vtype { %help: short "Type of OLSRv1 route"; %allow-operator: ":" "==" "!="; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt=vtype $(<>) $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt="; } originator { %help: short "Originator of OLSRv1 route"; %allow-operator: ":" "==" "!=" "<" ">" "<=" ">="; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt=originator $(<>) $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt="; } dest-main-address { %help: short "Main address of OLSRv1 destination"; %allow-operator: ":" "==" "!=" "<" ">" "<=" ">="; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt=dest-main-address $(<>) $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt="; } } then { metric { %help: short "Set the metric value"; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=2&order:txt=$(#)&statement:txt=metric $(<>) $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=2&order:txt=$(#)&statement:txt="; } } } } } xorp/etc/templates/mld.cmds0000664000076400007640000000127611421137511016057 0ustar greearbgreearb/* $XORP: xorp/etc/templates/mld.cmds,v 1.3 2004/05/26 04:25:09 pavlin Exp $ */ show mld { %command: "" %help: HELP; %module: mld; %tag: HELP "Display information about MLD"; } show mld group { %command: "cli_send_processor_xrl -t MLD $0" %help: HELP; %module: mld; %tag: HELP "Display information about MLD group membership"; } show mld interface { %command: "cli_send_processor_xrl -t MLD $0" %help: HELP; %module: mld; %tag: HELP "Display information about MLD interfaces"; } show mld interface address { %command: "cli_send_processor_xrl -t MLD $0" %help: HELP; %module: mld; %tag: HELP "Display information about addresses of MLD interfaces"; } xorp/etc/templates/misc.cmds0000664000076400007640000000172011421137511016230 0ustar greearbgreearb/* $XORP: xorp/etc/templates/misc.cmds,v 1.10 2006/01/18 21:58:00 pavlin Exp $ */ ping { %command: "" %help: "Ping a hostname or IP address"; %nomore_mode; } ping { %command: "ping $2" %help: "Give a hostname or IP address to ping."; %nomore_mode; } ping6 { %command: "" %help: "Ping an IPv6 hostname or IPv6 address"; %nomore_mode; } ping6 { %command: "ping6 $2" %help: "Give an IPv6 hostname or IPv6 address to ping."; %nomore_mode; } traceroute { %command: "" %help: "Trace the IP route to a hostname or IP address"; %nomore_mode; } traceroute { %command: "traceroute $2" %help: "Give a hostname or IP address to traceroute to."; %nomore_mode; } traceroute6 { %command: "" %help: "Trace the IPv6 route to a hostname or IPv6 address"; %nomore_mode; } traceroute6 { %command: "traceroute6 $2" %help: "Give an IPv6 hostname or IPv6 address to traceroute to."; %nomore_mode; } xorp/etc/templates/rtrmgr.tp0000664000076400007640000001266011421137511016314 0ustar greearbgreearb/* $XORP: xorp/etc/templates/rtrmgr.tp,v 1.6 2006/02/22 02:07:32 pavlin Exp $ */ rtrmgr { targetname: txt = "rtrmgr"; config-directory: txt = ""; load-file-command: txt = "fetch"; load-file-command-args: txt = "-o"; load-ftp-command: txt = "fetch"; load-ftp-command-args: txt = "-o"; load-http-command: txt = "fetch"; load-http-command-args: txt = "-o"; load-tftp-command: txt = "sh -c 'echo Not implemented 1>&2 && exit 1'"; load-tftp-command-args: txt = ""; save-file-command: txt = "sh -c 'echo Not implemented 1>&2 && exit 1'"; save-file-command-args: txt = ""; save-ftp-command: txt = "sh -c 'echo Not implemented 1>&2 && exit 1'"; save-ftp-command-args: txt = ""; save-http-command: txt = "sh -c 'echo Not implemented 1>&2 && exit 1'"; save-http-command-args: txt = ""; save-tftp-command: txt = "sh -c 'echo Not implemented 1>&2 && exit 1'"; save-tftp-command-args: txt = ""; load @: url_file { } load @: url_ftp { } load @: url_http { } load @: url_tftp { } save @: url_file { } save @: url_ftp { } save @: url_http { } save @: url_tftp { } } rtrmgr { %help: short "Configure the Router Manager"; %modinfo: provides rtrmgr; %modinfo: default_targetname "rtrmgr"; %mandatory: $(@.targetname); targetname { %user-hidden: "XRL target name"; %help: short "XRL target name"; %set:; } CONFIG_FILENAME { %user-hidden: "Internal transient file name"; %create:; } CONFIG_STDERR { %user-hidden: "Internal transient command stderr output"; %create:; } config-directory { %help: short "Set the name of the directory with the configuration files"; %create: xrl "$(rtrmgr.targetname)/rtrmgr/0.1/set_config_directory?config_directory:txt=$(@)"; %set: xrl "$(rtrmgr.targetname)/rtrmgr/0.1/set_config_directory?config_directory:txt=$(@)"; %delete: xrl "$(rtrmgr.targetname)/rtrmgr/0.1/set_config_directory?config_directory:txt=$(DEFAULT)"; } load-file-command { %help: short "Load configuration command (FILE URL)"; %create:; %set:; } load-file-command-args { %help: short "Load configuration command arguments (FILE URL)"; %create:; %set:; } load-ftp-command { %help: short "Load configuration command (FTP URL)"; %create:; %set:; } load-ftp-command-args { %help: short "Load configuration command arguments (FTP URL)"; %create:; %set:; } load-http-command { %help: short "Load configuration command (HTTP URL)"; %create:; %set:; } load-http-command-args { %help: short "Load configuration command arguments (HTTP URL)"; %create:; %set:; } load-tftp-command { %help: short "Load configuration command (TFTP URL)"; %create:; %set:; } load-tftp-command-args { %help: short "Load configuration command arguments (TFTP URL)"; %create:; %set:; } save-file-command { %help: short "Save configuration command (FILE URL)"; %create:; %set:; } save-file-command-args { %help: short "Save configuration command arguments (FILE URL)"; %create:; %set:; } save-ftp-command { %help: short "Save configuration command (FTP URL)"; %create:; %set:; } save-ftp-command-args { %help: short "Save configuration command arguments (FTP URL)"; %create:; %set:; } save-http-command { %help: short "Save configuration command (HTTP URL)"; %create:; %set:; } save-http-command-args { %help: short "Save configuration command arguments (HTTP URL)"; %create:; %set:; } save-tftp-command { %help: short "Save configuration command (TFTP URL)"; %create:; %set:; } save-tftp-command-args { %help: short "Save configuration command arguments (TFTP URL)"; %create:; %set:; } load @: url_file { %help: short "Load configuration file"; %create: program "$(rtrmgr.load-file-command) $(rtrmgr.load-file-command-args) $(rtrmgr.CONFIG_FILENAME) $(@) -> stderr=$(rtrmgr.CONFIG_STDERR)"; } load @: url_ftp { %help: short "Load configuration file"; %create: program "$(rtrmgr.load-ftp-command) $(rtrmgr.load-ftp-command-args) $(rtrmgr.CONFIG_FILENAME) $(@) -> stderr=$(rtrmgr.CONFIG_STDERR)"; } load @: url_http { %help: short "Load configuration file"; %create: program "$(rtrmgr.load-http-command) $(rtrmgr.load-http-command-args) $(rtrmgr.CONFIG_FILENAME) $(@) -> stderr=$(rtrmgr.CONFIG_STDERR)"; } load @: url_tftp { %help: short "Load configuration file"; %create: program "$(rtrmgr.load-tftp-command) $(rtrmgr.load-tftp-command-args) $(rtrmgr.CONFIG_FILENAME) $(@) -> stderr=$(rtrmgr.CONFIG_STDERR)"; } save @: url_file { %help: short "Save configuration file"; %create: program "$(rtrmgr.save-file-command) $(rtrmgr.save-file-command-args) $(rtrmgr.CONFIG_FILENAME) $(@) -> stderr=$(rtrmgr.CONFIG_STDERR)"; } save @: url_ftp { %help: short "Save configuration file"; %create: program "$(rtrmgr.save-ftp-command) $(rtrmgr.save-ftp-command-args) $(rtrmgr.CONFIG_FILENAME) $(@) -> stderr=$(rtrmgr.CONFIG_STDERR)"; } save @: url_http { %help: short "Save configuration file"; %create: program "$(rtrmgr.save-http-command) $(rtrmgr.save-http-command-args) $(rtrmgr.CONFIG_FILENAME) $(@) -> stderr=$(rtrmgr.CONFIG_STDERR)"; } save @: url_tftp { %help: short "Save configuration file"; %create: program "$(rtrmgr.save-tftp-command) $(rtrmgr.save-tftp-command-args) $(rtrmgr.CONFIG_FILENAME) $(@) -> stderr=$(rtrmgr.CONFIG_STDERR)"; } } xorp/etc/templates/pim6.cmds0000664000076400007640000000413311421137511016151 0ustar greearbgreearb/* $XORP: xorp/etc/templates/pim6.cmds,v 1.3 2004/05/26 04:25:09 pavlin Exp $ */ show pim6 { %command: "" %help: HELP; %module: pimsm6; %tag: HELP "Display information about IPv6 PIM"; } show pim6 bootstrap { %command: "cli_send_processor_xrl -t PIMSM_6 $0" %help: HELP; %module: pimsm6; %tag: HELP "Display information about PIM IPv6 bootstrap routers"; } show pim6 bootstrap rps { %command: "cli_send_processor_xrl -t PIMSM_6 $0" %help: HELP; %module: pimsm6; %tag: HELP "Display information about PIM IPv6 bootstrap RPs"; } show pim6 interface { %command: "cli_send_processor_xrl -t PIMSM_6 $0" %help: HELP; %module: pimsm6; %tag: HELP "Display information about PIM IPv6 interfaces"; } show pim6 interface address { %command: "cli_send_processor_xrl -t PIMSM_6 $0" %help: HELP; %module: pimsm6; %tag: HELP "Display information about addresses of PIM IPv6 interfaces"; } show pim6 join { %command: "cli_send_processor_xrl -t PIMSM_6 $0" %help: HELP; %module: pimsm6; %tag: HELP "Display information about PIM IPv6 groups"; } show pim6 join all { %command: "cli_send_processor_xrl -t PIMSM_6 $0" %help: HELP; %module: pimsm6; %tag: HELP "Display information about all PIM IPv6 groups"; } show pim6 mfc { %command: "cli_send_processor_xrl -t PIMSM_6 $0" %help: HELP; %module: pimsm6; %tag: HELP "Display information about PIM Multicast Forwarding Cache"; } show pim6 mrib { %command: "cli_send_processor_xrl -t PIMSM_6 $0" %help: HELP; %module: pimsm6; %tag: HELP "Display MRIB IPv6 information inside PIM"; } show pim6 neighbors { %command: "cli_send_processor_xrl -t PIMSM_6 $0" %help: HELP; %module: pimsm6; %tag: HELP "Display information about PIM IPv6 neighbors"; } show pim6 rps { %command: "cli_send_processor_xrl -t PIMSM_6 $0" %help: HELP; %module: pimsm6; %tag: HELP "Display information about PIM IPv6 RPs"; } show pim6 scope { %command: "cli_send_processor_xrl -t PIMSM_6 $0" %help: HELP; %module: pimsm6; %tag: HELP "Display information about PIM IPv6 scope zones"; } xorp/etc/templates/host.cmds0000664000076400007640000000073211421137511016254 0ustar greearbgreearb/* $XORP: xorp/etc/templates/host.cmds,v 1.3 2006/02/22 02:07:31 pavlin Exp $ */ show host { %command: "" %help: HELP; %tag: HELP "Display information about the host"; } show host date { %command: "date" %help: HELP; %tag: HELP "Show host current date"; } show host name { %command: "hostname" %help: HELP; %tag: HELP "Show host name"; } show host os { %command: "uname -a" %help: HELP; %tag: HELP "Show host operating system details"; } xorp/etc/templates/olsr4.cmds0000664000076400007640000000341111421137511016337 0ustar greearbgreearb/* $XORP: xorp/etc/templates/olsr4.cmds,v 1.1 2008/04/24 15:20:17 bms Exp $ */ /* vim:set sts=4 ts=8 sw=4 syntax=c: */ /* ** OLSRv1 for IPv4 -- operational commands. */ clear olsr4 database { %command: "olsr_clear_database" %help: HELP; %module: olsr4; %tag: HELP "Clear OLSRv1 databases"; } show olsr4 { %command: "" %help: "Display information about OLSRv1 for IPv4"; %module: olsr4; } /* * Interfaces. */ show olsr4 interface { %command: "olsr_print_databases $3" %help: HELP; %module: olsr4; %tag: HELP "Show OLSRv1 interface status"; } /* * Links. */ show olsr4 link { %command: "olsr_print_databases $3" %help: HELP; %module: olsr4; %tag: HELP "Show OLSRv1 link status"; } /* * Neighbors. */ show olsr4 neighbor { %command: "olsr_print_databases $3" %help: HELP; %module: olsr4; %tag: HELP "Show OLSRv1 one-hop neighbor status"; } /* * Two-hop links. */ show olsr4 twohop-link { %command: "olsr_print_databases $3" %help: HELP; %module: olsr4; %tag: HELP "Show OLSRv1 two-hop link status"; } /* * Two-hop neighbors. */ show olsr4 twohop-neighbor { %command: "olsr_print_databases $3" %help: HELP; %module: olsr4; %tag: HELP "Show OLSRv1 two-hop neighbor status"; } /* * MID entries. */ show olsr4 mid { %command: "olsr_print_databases $3" %help: HELP; %module: olsr4; %tag: HELP "Show OLSRv1 Multiple Interface database"; } /* * TC entries. */ show olsr4 topology { %command: "olsr_print_databases $3" %help: HELP; %module: olsr4; %tag: HELP "Show OLSRv1 Topology Control database"; } /* * HNA entries. */ show olsr4 external { %command: "olsr_print_databases $3" %help: HELP; %module: olsr4; %tag: HELP "Show OLSRv1 external routes"; } xorp/etc/templates/SConscript0000664000076400007640000000661311540225522016447 0ustar greearbgreearb# Copyright (c) 2009-2011 XORP, Inc and Others # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # $XORP$ import os, re Import('env') e = env.Clone() cmds = [ 'fea', 'host', 'igmp', 'mfea', 'misc', 'mld', 'pim', 'policy', 'rib', ] tps = [ 'fib2mrib', 'igmp', 'interfaces', 'mfea4', 'mld', 'pimsm4', 'plumbing', 'policy', 'protocols', 'rib', 'rtrmgr', 'static_routes', ] tp_raw = [ 'fea' ] if not (env.has_key('disable_ipv6') and env['disable_ipv6']): cmds.append('mfea6') tps.append('mfea6') cmds.append('pim6') tps.append('pimsm6') if env['enable_ospf']: cmds.append('ospfv3') tps.append('ospfv3') if not (env.has_key('disable_fw') and env['disable_fw']): tps.append('firewall') if env['enable_olsr']: cmds.append('olsr4') tps.append('olsr4') if env['enable_ospf']: cmds.append('ospfv2') tps.append('ospfv2') if env['enable_bgp']: cmds.append('bgp') tps.append('bgp') if env['enable_rip']: cmds.append('rip') tps.append('rip') cmds.append('ripng') tps.append('ripng') if env['enable_vrrp']: cmds.append('vrrp') tps.append('vrrp') if env['enable_xorpsh']: cmds.append('xorpsh') # Total hack for now, might want to consider some sort of lame CPP syntax # instead of hard-coding the pattern matching below. def BuildXrlTemplate(target, source, env): # base = tp[:-len('.raw')] skip_fw = False if (env.has_key('disable_fw') and env['disable_fw']): skip_fw = True sfname = str(source[0]) print "source: ", sfname print "target: ", str(target[0]) ifile = open(sfname, 'r') ofile = open(str(target[0]), 'w') if sfname == 'etc/templates/fea.tp.raw': ignore_ln = re.compile(".*firewall.*") for line in ifile.readlines(): if not (skip_fw and ignore_ln.match(line)): if line[-1] == "\n": # trim trailing newline line = line[0 : -1] print >>ofile, "%s" % line else: return -1 # error return None bld = Builder(action = BuildXrlTemplate, suffix = '.tp', src_suffix = '.tp.raw') e.Append(BUILDERS = {'BuildTP' : bld}) all_tp_raw = [] for tp in tp_raw: all_tp_raw.append(e.BuildTP(tp)) Default(all_tp_raw) e.Alias('install', [ e.InstallData(env['xorp_templatedir'], [c.__add__('.cmds') for c in cmds]), e.InstallData(env['xorp_templatedir'], [t.__add__('.tp') for t in tps]), e.InstallData(env['xorp_templatedir'], [t.__add__('.tp') for t in tp_raw]) ]) xorp/etc/templates/policy.tp0000664000076400007640000005146611421137511016305 0ustar greearbgreearb/* $XORP: xorp/etc/templates/policy.tp,v 1.31 2008/08/06 08:25:26 abittau Exp $ */ policy { targetname: txt = "policy"; policy-statement @: txt { term @: txt { from { protocol: txt; network4: ipv4net; network6: ipv6net; network4-list: txt; network6-list: txt; prefix-length4: u32range; prefix-length6: u32range; policy: txt; tag: u32range; nexthop4: ipv4range; nexthop6: ipv6range; } to { network4: ipv4net; network6: ipv6net; network4-list: txt; network6-list: txt; prefix-length4: u32range; prefix-length6: u32range; policy: txt; tag: u32range; nexthop4: ipv4range; nexthop6: ipv6range; } then { nexthop4: ipv4; nexthop6: ipv6; nexthop4-var: txt; nexthop6-var: txt; trace: u32; tag: u32; next: txt; accept {} reject {} } } then { accept {} reject {} } } network4-list @: txt { elements: txt; /* %deprecated */ network @: ipv4net { modifier: txt; } } network6-list @: txt { elements: txt; /* %deprecated */ network @: ipv6net { modifier: txt; } } } policy { %help: short "Configure routing policies"; %modinfo: provides policy; %modinfo: depends rib; %modinfo: path "xorp_policy"; %modinfo: default_targetname "policy"; %modinfo: status_method xrl "$(policy.targetname)/common/0.1/get_status->status:u32&reason:txt"; %modinfo: startup_method xrl "$(policy.targetname)/common/0.1/startup"; %modinfo: shutdown_method xrl "$(policy.targetname)/common/0.1/shutdown"; %modinfo: end_commit xrl "$(policy.targetname)/policy/0.1/done_global_policy_conf"; %mandatory: $(@.targetname); targetname { %user-hidden: "XRL target name"; %help: short "XRL target name"; %set:; } policy-statement @: txt { %help: short "Routing policy"; %create: xrl "$(policy.targetname)/policy/0.1/create_policy?policy:txt=$(@)"; %delete: xrl "$(policy.targetname)/policy/0.1/delete_policy?policy:txt=$(@)"; term @: txt { %help: short "Term of the routing policy"; %create: xrl "$(policy.targetname)/policy/0.1/create_term?policy:txt=$(policy-statement.@)&order:txt=$(#)&term:txt=$(@)"; %delete: xrl "$(policy.targetname)/policy/0.1/delete_term?policy:txt=$(policy-statement.@)&term:txt=$(@)"; from { %help: short "Conditions to match the source of a route"; protocol { %help: short "Protocol from which route was learned"; %allow: $(@) "bgp" %help: "BGP routes"; %allow: $(@) "connected" %help: "Directly connected sub-network routes"; %allow: $(@) "fib2mrib" %help: "FIB2MRIB routes"; %allow: $(@) "olsr4" %help: "OLSRv1 IPv4 routes"; %allow: $(@) "ospf4" %help: "OSPF IPv4 routes"; %allow: $(@) "ospf6" %help: "OSPF IPv6 routes"; %allow: $(@) "rip" %help: "RIP routes"; %allow: $(@) "ripng" %help: "RIPng routes"; %allow: $(@) "static" %help: "Static routes"; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt=protocol $(<>) $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt="; } nexthop4 { %help: short "IPv4 nexthop"; %allow-operator: ":" "==" "!=" "<" ">" "<=" ">="; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt=nexthop4 $(<>) $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt="; } nexthop6 { %help: short "IPv6 nexthop"; %allow-operator: ":" "==" "!=" "<" ">" "<=" ">="; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt=nexthop6 $(<>) $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt="; } network4 { %help: short "IPv4 network"; %allow-operator: ":" "==" "!=" "<" ">" "<=" ">=" "exact" "not" "shorter" "longer" "orshorter" "orlonger"; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt=network4 $(<>) $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt="; } network6 { %help: short "IPv6 network"; %allow-operator: ":" "==" "!=" "<" ">" "<=" ">=" "exact" "not" "shorter" "longer" "orshorter" "orlonger"; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt=network6 $(<>) $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt="; } network4-list { %help: short "Valid named set of IPv4 networks"; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt=network4 <= SET $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt="; } network6-list { %help: short "Valid named set of IPv6 networks"; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt=network6 <= SET $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt="; } prefix-length4 { %help: short "IPv4 prefix length range"; %allow-operator: "==" "!=" "<" "<=" ">" ">="; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt=network4 $(<>) $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt="; } prefix-length6 { %help: short "IPv6 prefix length range"; %allow-operator: "==" "!=" "<" "<=" ">" ">="; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt=network6 $(<>) $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt="; } policy { %help: short "Policy subroutine"; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt=policy $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt="; } tag { %help: short "Tag value"; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt=tag $(<>) $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt="; } } to { %help: short "Conditions to match the destination of a route"; network4 { %help: short "IPv4 network"; %allow-operator: ":" "==" "!=" "<" ">" "<=" ">=" "exact" "not" "shorter" "longer" "orshorter" "orlonger"; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=1&order:txt=$(#)&statement:txt=network4 $(<>) $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=1&order:txt=$(#)&statement:txt="; } network6 { %help: short "IPv6 network"; %allow-operator: ":" "==" "!=" "<" ">" "<=" ">=" "exact" "not" "shorter" "longer" "orshorter" "orlonger"; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=1&order:txt=$(#)&statement:txt=network6 $(<>) $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=1&order:txt=$(#)&statement:txt="; } nexthop4 { %help: short "IPv4 nexthop"; %allow-operator: ":" "==" "!=" "<" ">" "<=" ">="; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=1&order:txt=$(#)&statement:txt=nexthop4 $(<>) $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=1&order:txt=$(#)&statement:txt="; } nexthop6 { %help: short "IPv6 nexthop"; %allow-operator: ":" "==" "!=" "<" ">" "<=" ">="; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=1&order:txt=$(#)&statement:txt=nexthop6 $(<>) $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=1&order:txt=$(#)&statement:txt="; } network4-list { %help: short "Valid named set of IPv4 networks"; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=1&order:txt=$(#)&statement:txt=network4 <= SET $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=1&order:txt=$(#)&statement:txt="; } network6-list { %help: short "Valid named set of IPv6 networks"; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=1&order:txt=$(#)&statement:txt=network6 <= SET $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=1&order:txt=$(#)&statement:txt="; } prefix-length4 { %help: short "IPv4 prefix length range"; %allow-operator: "==" "!=" "<" "<=" ">" ">="; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=1&order:txt=$(#)&statement:txt=network4 $(<>) $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=1&order:txt=$(#)&statement:txt="; } prefix-length6 { %help: short "IPv6 prefix length range"; %allow-operator: "==" "!=" "<" "<=" ">" ">="; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=1&order:txt=$(#)&statement:txt=network6 $(<>) $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=1&order:txt=$(#)&statement:txt="; } policy { %help: short "Policy subroutine"; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=1&order:txt=$(#)&statement:txt=policy $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=1&order:txt=$(#)&statement:txt="; } tag { %help: short "Tag value"; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=1&order:txt=$(#)&statement:txt=tag $(<>) $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=1&order:txt=$(#)&statement:txt="; } } then { %help: short "Actions to take if conditions match"; trace { %help: short "Set the tracing"; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=2&order:txt=$(#)&statement:txt=trace = $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=2&order:txt=$(#)&statement:txt="; } nexthop4 { %help: short "Set the IPv4 nexthop"; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=2&order:txt=$(#)&statement:txt=nexthop4 $(<>) (ctr 'ipv4nexthop' '$(@)');"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=2&order:txt=$(#)&statement:txt="; } nexthop6 { %help: short "Set the IPv6 nexthop"; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=2&order:txt=$(#)&statement:txt=nexthop6 $(<>) (ctr 'ipv6nexthop' '$(@)');"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=2&order:txt=$(#)&statement:txt="; } nexthop4-var { %help: short "Set the IPv4 nexthop"; %allow: $(@) "peer-address" %help: "Peer address"; %allow: $(@) "self" %help: "Self"; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=2&order:txt=$(#)&statement:txt=nexthop4 $(<>) (ctr 'ipv4nexthop' '$(@)');"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=2&order:txt=$(#)&statement:txt="; } nexthop6-var { %help: short "Set the IPv6 nexthop"; %allow: $(@) "peer-address" %help: "Peer address"; %allow: $(@) "self" %help: "Self"; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=2&order:txt=$(#)&statement:txt=nexthop6 $(<>) (ctr 'ipv6nexthop' '$(@)');"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=2&order:txt=$(#)&statement:txt="; } tag { %help: short "Tag value"; %allow-operator: ":" "=" "add" "sub"; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=2&order:txt=$(#)&statement:txt=tag $(<>) $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=2&order:txt=$(#)&statement:txt="; } next { %help: short "Change control flow to next term or policy"; %allow: $(@) "policy" %help: "Skip to next policy"; %allow: $(@) "term" %help: "Skip to next term"; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=2&order:txt=$(#)&statement:txt=next $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=2&order:txt=$(#)&statement:txt="; } accept { %help: short "Accept a route"; %create: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=2&order:txt=$(#)&statement:txt=accept;"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=2&order:txt=$(#)&statement:txt="; } reject { %help: short "Reject a route"; %create: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=2&order:txt=$(#)&statement:txt=reject;"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=2&order:txt=$(#)&statement:txt="; } } } /* term */ /* unnamed term */ then { %help: short "Final action"; %create: xrl "$(policy.targetname)/policy/0.1/create_term?policy:txt=$(policy-statement.@)&order:txt=$(#)&term:txt=__final"; %delete: xrl "$(policy.targetname)/policy/0.1/delete_term?policy:txt=$(policy-statement.@)&term:txt=__final"; accept { %help: short "Accept a route"; %create: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=__final&block:u32=2&order:txt=$(#)&statement:txt=accept;"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=__final&block:u32=2&order:txt=$(#)&statement:txt="; } reject { %help: short "Reject a route"; %create: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=__final&block:u32=2&order:txt=$(#)&statement:txt=reject;"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=__final&block:u32=2&order:txt=$(#)&statement:txt="; } } } network4-list @: txt { %help: short "Named set of IPv4 networks"; %delete: xrl "$(policy.targetname)/policy/0.1/delete_set?set:txt=$(@)"; %create: xrl "$(policy.targetname)/policy/0.1/create_set?set:txt=$(@)"; elements { %deprecated: "Statement 'elements' is replaced with 'network'"; %help: short "Elements in the named set of IPv4 networks"; %set: xrl "$(policy.targetname)/policy/0.1/update_set?type:txt=set_ipv4net&set:txt=$(network4-list.@)&elements:txt=$(@)"; } network @: ipv4net { %help: short "Element in the named set of IPv4 networks"; %create: xrl "$(policy.targetname)/policy/0.1/add_to_set?type:txt=set_ipv4net&set:txt=$(network4-list.@)&element:txt=$(@)"; %delete: xrl "$(policy.targetname)/policy/0.1/delete_from_set?type:txt=set_ipv4net&set:txt=$(network4-list.@)&element:txt=$(@)"; modifier { %help: short "Set prefix modifier"; %set: xrl "$(policy.targetname)/policy/0.1/delete_from_set?type:txt=set_ipv4net&set:txt=$(network4-list.@)&element:txt=$(network.@)"; %set: xrl "$(policy.targetname)/policy/0.1/add_to_set?type:txt=set_ipv4net&set:txt=$(network4-list.@)&element:txt=$(network.@) $(@)"; %delete: xrl "$(policy.targetname)/policy/0.1/delete_from_set?type:txt=set_ipv4net&set:txt=$(network4-list.@)&element:txt=$(network.@)"; %delete: xrl "$(policy.targetname)/policy/0.1/add_to_set?type:txt=set_ipv4net&set:txt=$(network4-list.@)&element:txt=$(network.@)"; } } } network6-list @: txt { %help: short "Named set of IPv6 networks"; %delete: xrl "$(policy.targetname)/policy/0.1/delete_set?set:txt=$(@)"; %create: xrl "$(policy.targetname)/policy/0.1/create_set?set:txt=$(@)"; elements { %deprecated: "Statement 'elements' is replaced with 'network'"; %help: short "Elements in the named set of IPv6 networks"; %set: xrl "$(policy.targetname)/policy/0.1/update_set?type:txt=set_ipv6net&set:txt=$(network6-list.@)&elements:txt=$(@)"; } network @: ipv6net { %help: short "Element in the named set of IPv6 networks"; %create: xrl "$(policy.targetname)/policy/0.1/add_to_set?type:txt=set_ipv6net&set:txt=$(network6-list.@)&element:txt=$(@)"; %delete: xrl "$(policy.targetname)/policy/0.1/delete_from_set?type:txt=set_ipv6net&set:txt=$(network6-list.@)&element:txt=$(@)"; modifier { %help: short "Set prefix modifier"; %set: xrl "$(policy.targetname)/policy/0.1/delete_from_set?type:txt=set_ipv6net&set:txt=$(network6-list.@)&element:txt=$(network.@)"; %set: xrl "$(policy.targetname)/policy/0.1/add_to_set?type:txt=set_ipv6net&set:txt=$(network6-list.@)&element:txt=$(network.@) $(@)"; %delete: xrl "$(policy.targetname)/policy/0.1/delete_from_set?type:txt=set_ipv6net&set:txt=$(network6-list.@)&element:txt=$(network.@)"; %delete: xrl "$(policy.targetname)/policy/0.1/add_to_set?type:txt=set_ipv6net&set:txt=$(network6-list.@)&element:txt=$(network.@)"; } } } } xorp/etc/templates/ospfv3.tp0000664000076400007640000004504211421137511016217 0ustar greearbgreearb/* $XORP: xorp/etc/templates/ospfv3.tp,v 1.14 2008/08/06 08:24:08 abittau Exp $ */ protocols { ospf6 @: u32 { targetname: txt = "ospfv3"; router-id: ipv4; ip-router-alert: bool = false; traceoptions { flag { all { disable: toggle = false; } } } area @: ipv4 { area-type: txt = "normal"; default-lsa { disable: toggle = false; metric: u32 = 0; } summaries { disable: toggle = false; } area-range @: ipv6net { advertise: bool = true; } virtual-link @: ipv4 { transit-area: ipv4; hello-interval: u32 = 10; router-dead-interval: u32 = 40; retransmit-interval: u32 = 5; transit-delay: u32 = 1; } interface @: txt { link-type: txt = "broadcast"; vif @: txt { address @: ipv6 { disable: toggle = false; } priority: u32 = 128; hello-interval: u32 = 10; router-dead-interval: u32 = 40; interface-cost: u32 = 1; retransmit-interval: u32 = 5; transit-delay: u32 = 1; passive: bool = false; neighbor @: ipv4 { router-id: ipv4; } disable: toggle = false; } } } import: txt; export: txt; } } policy { policy-statement @: txt { term @: txt { from { metric: u32range; external-type: u32; } to { metric: u32range; external-type: u32; } then { metric: u32; external-type: u32; } } } } protocols { ospf6 @: u32 { %help: short "Configure the OSPF protocol"; %modinfo: provides ospf6; %modinfo: depends rib; %modinfo: depends policy; %modinfo: path "xorp_ospfv3"; %modinfo: default_targetname "ospfv3"; %modinfo: status_method xrl "$(ospf6.@.targetname)/common/0.1/get_status->status:u32&reason:txt"; %modinfo: shutdown_method xrl "$(ospf6.@.targetname)/common/0.1/shutdown"; %modinfo: startup_method xrl "$(ospf6.@.targetname)/common/0.1/startup"; %create: xrl "$(ospf6.@.targetname)/ospfv3/0.1/set_instance_id?id:u32=$(ospf6.@)"; %mandatory: $(@.router-id); %mandatory: $(@.targetname); targetname { %user-hidden: "XRL target name"; %help: short "XRL target name"; %set:; } router-id { %help: short "A unique 32-bit identifier within this AS"; %help: long "A 32-bit number assigned to each router running the OSPF protocol. This number uniquely identifies the router within an Autonomous System"; %set: xrl "$(ospf6.@.targetname)/ospfv3/0.1/set_router_id?id:ipv4=$(@)"; } ip-router-alert { %help: short "Send the IP router alert option in packets"; %help: long "If this option is true the IP router alert option will be set in all transmitted packets"; %set: xrl "$(ospf6.@.targetname)/ospfv3/0.1/set_ip_router_alert?ip_router_alert:bool=$(@)"; %delete: xrl "$(ospf6.@.targetname)/ospfv3/0.1/set_ip_router_alert?ip_router_alert:bool=$(DEFAULT)"; } area @: ipv4 { %help: short "The OSPF area to which the attached network belongs"; area-type { %help: short "Type of area"; %allow: $(@) "normal" %help: "OSPF normal area"; %allow: $(@) "stub" %help: "OSPF stubby area"; %allow: $(@) "nssa" %help: "OSPF not-so-stubby area"; %set: xrl "$(ospf6.@.targetname)/ospfv3/0.1/change_area_router_type?area:ipv4=$(area.@)&type:txt=$(@)"; %delete: xrl "$(ospf6.@.targetname)/ospfv3/0.1/change_area_router_type?area:ipv4=$(area.@)&type:txt=$(DEFAULT)"; } %create: xrl "$(ospf6.@.targetname)/ospfv3/0.1/create_area_router?area:ipv4=$(@)&type:txt=$(@.area-type)"; %delete: xrl "$(ospf6.@.targetname)/ospfv3/0.1/destroy_area_router?area:ipv4=$(@)"; default-lsa { %help: short "Originate default route in stub or not-so-stubby areas"; disable { %help: short "disable the origination of the default route"; %set: xrl "$(ospf6.@.targetname)/ospfv3/0.1/originate_default_route?area:ipv4=$(area.@)&enable:bool=`~$(@)`"; %delete: xrl "$(ospf6.@.targetname)/ospfv3/0.1/originate_default_route?area:ipv4=$(area.@)&enable:bool=$(DEFAULT)"; } metric { %help: short "Metric of default route"; %set: xrl "$(ospf6.@.targetname)/ospfv3/0.1/stub_default_cost?area:ipv4=$(area.@)&cost:u32=$(@)"; %delete: xrl "$(ospf6.@.targetname)/ospfv3/0.1/stub_default_cost?area:ipv4=$(area.@)&cost:u32=$(DEFAULT)"; } } summaries { %help: short "Generate summaries into stub or not-so-stubby areas"; disable { %help: short "disable summaries into stub or not-so-stubby areas"; %set: xrl "$(ospf6.@.targetname)/ospfv3/0.1/summaries?area:ipv4=$(area.@)&enable:bool=`~$(@)`"; %delete: xrl "$(ospf6.@.targetname)/ospfv3/0.1/summaries?area:ipv4=$(area.@)&enable:bool=$(DEFAULT)"; } } area-range @: ipv6net { %help: short "Area range for generating summaries"; %create: xrl "$(ospf6.@.targetname)/ospfv3/0.1/area_range_add?area:ipv4=$(area.@)&net:ipv6net=$(@)&advertise:bool=$(@.advertise)"; %delete: xrl "$(ospf6.@.targetname)/ospfv3/0.1/area_range_delete?area:ipv4=$(area.@)&net:ipv6net=$(@)"; advertise { %help: short "Advertise or DoNotAdvertise"; %set: xrl "$(ospf6.@.targetname)/ospfv3/0.1/area_range_change_state?area:ipv4=$(area.@)&net:ipv6net=$(area-range.@)&advertise:bool=$(@)"; %delete: xrl "$(ospf6.@.targetname)/ospfv3/0.1/area_range_change_state?area:ipv4=$(area.@)&net:ipv6net=$(area-range.@)&advertise:bool=$(DEFAULT)"; } } virtual-link @: ipv4 { %help: short "Virtual link"; %create: xrl "$(ospf6.@.targetname)/ospfv3/0.1/create_virtual_link?neighbour_id:ipv4=$(virtual-link.@)&area:ipv4=$(area.@)"; %delete: xrl "$(ospf6.@.targetname)/ospfv3/0.1/delete_virtual_link?neighbour_id:ipv4=$(virtual-link.@)"; transit-area { %help: short "Area through which to transit"; %set: xrl "$(ospf6.@.targetname)/ospfv3/0.1/transit_area_virtual_link?neighbour_id:ipv4=$(virtual-link.@)&transit_area:ipv4=$(@)"; %delete: xrl "$(ospf6.@.targetname)/ospfv3/0.1/transit_area_virtual_link?neighbour_id:ipv4=$(virtual-link.@)&transit_area:ipv4=$(DEFAULT)"; } hello-interval { %help: short "Hello packets sent every interval seconds"; %allow-range: $(@) "1" "65535" %help: "The Hello packets interval (in seconds)"; %set: xrl "$(ospf6.@.targetname)/ospfv3/0.1/set_hello_interval?ifname:txt=vlink&vifname:txt=$(virtual-link.@)&area:ipv4=$(area.@)&interval:u32=$(@)"; %delete: xrl "$(ospf6.@.targetname)/ospfv3/0.1/set_hello_interval?ifname:txt=vlink&vifname:txt=$(virtual-link.@)&area:ipv4=$(area.@)&interval:u32=$(DEFAULT)"; } router-dead-interval { %help: short "Seconds to wait before considering a neighbor dead"; %allow-range: $(@) "1" "4294967295" %help: "The neighbor router dead interval (in seconds)"; %set: xrl "$(ospf6.@.targetname)/ospfv3/0.1/set_router_dead_interval?ifname:txt=vlink&vifname:txt=$(virtual-link.@)&area:ipv4=$(area.@)&interval:u32=$(@)"; %delete: xrl "$(ospf6.@.targetname)/ospfv3/0.1/set_router_dead_interval?ifname:txt=vlink&vifname:txt=$(virtual-link.@)&area:ipv4=$(area.@)&interval:u32=$(DEFAULT)"; } retransmit-interval { %help: short "The retransmit interval (RxmtInterval)"; %allow-range: $(@) "1" "65535" %help: "The retransmit interval (in seconds)"; %set: xrl "$(ospf6.@.targetname)/ospfv3/0.1/set_retransmit_interval?ifname:txt=vlink&vifname:txt=$(virtual-link.@)&area:ipv4=$(area.@)&interval:u32=$(@)"; %delete: xrl "$(ospf6.@.targetname)/ospfv3/0.1/set_retransmit_interval?ifname:txt=vlink&vifname:txt=$(virtual-link.@)&area:ipv4=$(area.@)&interval:u32=$(DEFAULT)"; } transit-delay { %help: short "Add to age field of all transmitted LSAs"; %allow-range: $(@) "0" "3600" %help: "The extra addition to age field of all transmitted LSAs"; %set: xrl "$(ospf6.@.targetname)/ospfv3/0.1/set_inftransdelay?ifname:txt=vlink&vifname:txt=$(virtual-link.@)&area:ipv4=$(area.@)&delay:u32=$(@)"; %delete: xrl "$(ospf6.@.targetname)/ospfv3/0.1/set_inftransdelay?ifname:txt=vlink&vifname:txt=$(virtual-link.@)&area:ipv4=$(area.@)&delay:u32=$(DEFAULT)"; } } interface @: txt { %help: short "Include an interface in this area"; link-type { %help: short "broadcast or p2p or p2m"; %allow: $(@) "broadcast" %help: "Broadcast link"; %allow: $(@) "p2p" %help: "Point-to-point link"; %allow: $(@) "p2m" %help: "Point-to-multipoint link"; %set:; } vif @: txt { %help: short "Include an vif in this area"; %create: xrl "$(ospf6.@.targetname)/ospfv3/0.1/create_peer?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&type:txt=$(interface.@.link-type)&area:ipv4=$(area.@)"; %delete: xrl "$(ospf6.@.targetname)/ospfv3/0.1/delete_peer?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)"; %activate: xrl "$(ospf6.@.targetname)/ospfv3/0.1/activate_peer?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&area:ipv4=$(area.@)"; %update: xrl "$(ospf6.@.targetname)/ospfv3/0.1/update_peer?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&area:ipv4=$(area.@)"; address @ { %help: short "Address on interface/vif"; %create: xrl "$(ospf6.@.targetname)/ospfv3/0.1/add_address_peer?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&area:ipv4=$(area.@)&addr:ipv6=$(@)"; %delete: xrl "$(ospf6.@.targetname)/ospfv3/0.1/remove_address_peer?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&area:ipv4=$(area.@)&addr:ipv6=$(@)"; disable { %help: short "Disable OSPF on interface/vif"; %set: xrl "$(ospf6.@.targetname)/ospfv3/0.1/set_address_state_peer?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&area:ipv4=$(area.@)&addr:ipv6=$(address.@)&enable:bool=`~$(@)`"; %delete: xrl "$(ospf6.@.targetname)/ospfv3/0.1/set_address_state_peer?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&area:ipv4=$(area.@)&addr:ipv6=$(address.@)&enable:bool=`~$(DEFAULT)`"; } } priority { %help: short "Priority used in DR election"; %allow-range: $(@) "0" "255" %help: "The priority used in DR election"; %set: xrl "$(ospf6.@.targetname)/ospfv3/0.1/set_router_priority?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&area:ipv4=$(area.@)&priority:u32=$(@)"; %delete: xrl "$(ospf6.@.targetname)/ospfv3/0.1/set_router_priority?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&area:ipv4=$(area.@)&priority:u32=$(DEFAULT)"; } hello-interval { %help: short "Hello packets sent every interval seconds"; %allow-range: $(@) "1" "65535" %help: "The Hello packets interval (in seconds)"; %set: xrl "$(ospf6.@.targetname)/ospfv3/0.1/set_hello_interval?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&area:ipv4=$(area.@)&interval:u32=$(@)"; %delete: xrl "$(ospf6.@.targetname)/ospfv3/0.1/set_hello_interval?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&area:ipv4=$(area.@)&interval:u32=$(DEFAULT)"; } router-dead-interval { %help: short "Seconds to wait before considering a neighbor dead"; %allow-range: $(@) "1" "4294967295" %help: "The neighbor router dead interval (in seconds)"; %set: xrl "$(ospf6.@.targetname)/ospfv3/0.1/set_router_dead_interval?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&area:ipv4=$(area.@)&interval:u32=$(@)"; %delete: xrl "$(ospf6.@.targetname)/ospfv3/0.1/set_router_dead_interval?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&area:ipv4=$(area.@)&interval:u32=$(DEFAULT)"; } interface-cost { %help: short "Cost of this interface/vif"; %allow-range: $(@) "1" "65535" %help: "The cost of this address"; %set: xrl "$(ospf6.@.targetname)/ospfv3/0.1/set_interface_cost?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&area:ipv4=$(area.@)&cost:u32=$(@)"; %delete: xrl "$(ospf6.@.targetname)/ospfv3/0.1/set_interface_cost?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&area:ipv4=$(area.@)&cost:u32=$(DEFAULT)"; } retransmit-interval { %help: short "The retransmit interval (RxmtInterval)"; %allow-range: $(@) "1" "65535" %help: "The retransmit interval (in seconds)"; %set: xrl "$(ospf6.@.targetname)/ospfv3/0.1/set_retransmit_interval?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&area:ipv4=$(area.@)&interval:u32=$(@)"; %delete: xrl "$(ospf6.@.targetname)/ospfv3/0.1/set_retransmit_interval?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&area:ipv4=$(area.@)&interval:u32=$(DEFAULT)"; } transit-delay { %help: short "Add to age field of all transmitted LSAs"; %allow-range: $(@) "0" "3600" %help: "The extra addition to age field of all transmitted LSAs"; %set: xrl "$(ospf6.@.targetname)/ospfv3/0.1/set_inftransdelay?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&area:ipv4=$(area.@)&delay:u32=$(@)"; %delete: xrl "$(ospf6.@.targetname)/ospfv3/0.1/set_inftransdelay?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&area:ipv4=$(area.@)&delay:u32=$(DEFAULT)"; } passive { %help: short "Do not run OSPF, but advertise it"; %set: xrl "$(ospf6.@.targetname)/ospfv3/0.1/set_passive?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&area:ipv4=$(area.@)&passive:bool=$(@)"; %delete: xrl "$(ospf6.@.targetname)/ospfv3/0.1/set_passive?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&area:ipv4=$(area.@)&passive:bool=$(DEFAULT)"; } disable { %help: short "Disable OSPF on interface/vif"; %set: xrl "$(ospf6.@.targetname)/ospfv3/0.1/set_peer_state?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&enable:bool=`~$(@)`"; %delete: xrl "$(ospf6.@.targetname)/ospfv3/0.1/set_peer_state?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&enable:bool=`~$(DEFAULT)`"; } neighbor @: ipv4 { %help: short "Neighbors address"; %mandatory: $(@.router-id); router-id { %help: short "Neighbors router-id"; %set:; } %create: xrl "$(ospf6.@.targetname)/ospfv3/0.1/add_neighbour?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&area:ipv4=$(area.@)&neighbour_address:ipv6=$(@)&neighbour_id:ipv4=$(@.router-id)"; %delete: xrl "$(ospf6.@.targetname)/ospfv3/0.1/remove_neighbour?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&area:ipv4=$(area.@)&neighbour_address:ipv6=$(@)&neighbour_id:ipv4=$(@.router-id)"; } } } } traceoptions { %help: short "Configure the tracing options"; flag { %help: short "Configure the tracing operation to perform"; all { %help: short "Configure all tracing operations"; disable { %help: short "Disable all tracing operations"; %set: xrl "$(ospf6.@.targetname)/ospfv3/0.1/trace?tvar:txt=all&enable:bool=`~$(@)`"; %delete: xrl "$(ospf6.@.targetname)/ospfv3/0.1/trace?tvar:txt=all&enable:bool=$(DEFAULT)"; } } } } import { %help: short "Import policy"; %delete: xrl "$(policy.targetname)/policy/0.1/import?protocol:txt=ospf6&policies:txt=&modifier:txt="; %set: xrl "$(policy.targetname)/policy/0.1/import?protocol:txt=ospf6&policies:txt=$(@)&modifier:txt="; } export { %help: short "Export policy"; %delete: xrl "$(policy.targetname)/policy/0.1/export?protocol:txt=ospf6&policies:txt=&modifier:txt="; %set: xrl "$(policy.targetname)/policy/0.1/export?protocol:txt=ospf6&policies:txt=$(@)&modifier:txt="; } } } policy { %create: xrl "$(policy.targetname)/policy/0.1/set_proto_target?protocol:txt=ospf6&target:txt=ospfv3"; %create: xrl "$(policy.targetname)/policy/0.1/add_varmap?protocol:txt=ospf6&variable:txt=network6&type:txt=ipv6net&access:txt=r&id:u32=10"; %create: xrl "$(policy.targetname)/policy/0.1/add_varmap?protocol:txt=ospf6&variable:txt=nexthop6&type:txt=ipv6nexthop&access:txt=rw&id:u32=11"; %create: xrl "$(policy.targetname)/policy/0.1/add_varmap?protocol:txt=ospf6&variable:txt=metric&type:txt=u32&access:txt=rw&id:u32=12"; %create: xrl "$(policy.targetname)/policy/0.1/add_varmap?protocol:txt=ospf6&variable:txt=etype&type:txt=u32&access:txt=rw&id:u32=13"; policy-statement @: txt { term @: txt { from { metric { %help: short "Metric value"; %allow-operator: ":" "==" "!=" "<" ">" "<=" ">="; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt=metric $(<>) $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt="; } external-type { %help: short "Type of external OSPF route"; %allow-operator: ":" "==" "!="; %allow-range: $(@) "1" "2" %help: "Either type 1 or type 2"; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt=etype $(<>) $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt="; } } to { metric { %help: short "Metric value"; %allow-operator: ":" "==" "!=" "<" ">" "<=" ">="; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=1&order:txt=$(#)&statement:txt=metric $(<>) $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=1&order:txt=$(#)&statement:txt="; } external-type { %help: short "Type of external OSPF route"; %allow-operator: ":" "==" "!="; %allow-range: $(@) "1" "2" %help: "Either type 1 or type 2"; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=1&order:txt=$(#)&statement:txt=etype $(<>) $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=1&order:txt=$(#)&statement:txt="; } } then { metric { %help: short "Set the metric value"; %allow-operator: ":" "=" "sub" "add"; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=2&order:txt=$(#)&statement:txt=metric $(<>) $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=2&order:txt=$(#)&statement:txt="; } external-type { %help: short "Set the type of external OSPF route"; %allow-range: $(@) "1" "2" %help: "Either type 1 or type 2"; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=2&order:txt=$(#)&statement:txt=etype $(<>) $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=2&order:txt=$(#)&statement:txt="; } } } } } xorp/etc/templates/fea.cmds0000664000076400007640000000064711421137511016037 0ustar greearbgreearb/* $XORP: xorp/etc/templates/fea.cmds,v 1.8 2004/05/26 04:25:08 pavlin Exp $ */ show interfaces { %command: "fea_show_interfaces" %help: HELP; %module: interfaces; %tag: HELP "Show network interface information"; } show interfaces $(interfaces.interface.*) { %command: "fea_show_interfaces -i $3" %help: HELP; %module: interfaces; %tag: HELP "Show information about a single network interface"; } xorp/etc/templates/pim.cmds0000664000076400007640000000411611421137511016064 0ustar greearbgreearb/* $XORP: xorp/etc/templates/pim.cmds,v 1.3 2004/05/26 04:25:09 pavlin Exp $ */ show pim { %command: "" %help: HELP; %module: pimsm4; %tag: HELP "Display information about IPv4 PIM"; } show pim bootstrap { %command: "cli_send_processor_xrl -t PIMSM_4 $0" %help: HELP; %module: pimsm4; %tag: HELP "Display information about PIM IPv4 bootstrap routers"; } show pim bootstrap rps { %command: "cli_send_processor_xrl -t PIMSM_4 $0" %help: HELP; %module: pimsm4; %tag: HELP "Display information about PIM IPv4 bootstrap RPs"; } show pim interface { %command: "cli_send_processor_xrl -t PIMSM_4 $0" %help: HELP; %module: pimsm4; %tag: HELP "Display information about PIM IPv4 interfaces"; } show pim interface address { %command: "cli_send_processor_xrl -t PIMSM_4 $0" %help: HELP; %module: pimsm4; %tag: HELP "Display information about addresses of PIM IPv4 interfaces"; } show pim join { %command: "cli_send_processor_xrl -t PIMSM_4 $0" %help: HELP; %module: pimsm4; %tag: HELP "Display information about PIM IPv4 groups"; } show pim join all { %command: "cli_send_processor_xrl -t PIMSM_4 $0" %help: HELP; %module: pimsm4; %tag: HELP "Display information about all PIM IPv4 groups"; } show pim mfc { %command: "cli_send_processor_xrl -t PIMSM_4 $0" %help: HELP; %module: pimsm4; %tag: HELP "Display information about PIM Multicast Forwarding Cache"; } show pim mrib { %command: "cli_send_processor_xrl -t PIMSM_4 $0" %help: HELP; %module: pimsm4; %tag: HELP "Display MRIB IPv4 information inside PIM"; } show pim neighbors { %command: "cli_send_processor_xrl -t PIMSM_4 $0" %help: HELP; %module: pimsm4; %tag: HELP "Display information about PIM IPv4 neighbors"; } show pim rps { %command: "cli_send_processor_xrl -t PIMSM_4 $0" %help: HELP; %module: pimsm4; %tag: HELP "Display information about PIM IPv4 RPs"; } show pim scope { %command: "cli_send_processor_xrl -t PIMSM_4 $0" %help: HELP; %module: pimsm4; %tag: HELP "Display information about PIM IPv4 scope zones"; } xorp/etc/templates/static_routes.tp0000664000076400007640000010364511421137511017673 0ustar greearbgreearb/* $XORP: xorp/etc/templates/static_routes.tp,v 1.43 2008/08/06 08:23:24 abittau Exp $ */ protocols { static { targetname: txt = "static_routes"; disable: toggle = false; enabled: bool; /* %deprecated */ route @: ipv4net { next-hop: ipv4; nexthop: ipv4; /* %deprecated */ metric: u32 = 1; qualified-next-hop @: ipv4 { metric: u32 = 10; } } route @: ipv6net { next-hop: ipv6; nexthop: ipv4; /* %deprecated */ metric: u32 = 1; qualified-next-hop @: ipv6 { metric: u32 = 10; } } route4 @: ipv4net { /* %deprecated */ next-hop: ipv4; nexthop: ipv4; /* %deprecated */ metric: u32 = 1; } route6 @: ipv6net { /* %deprecated */ next-hop: ipv6; nexthop: ipv4; /* %deprecated */ metric: u32 = 1; } interface-route @: ipv4net { next-hop-interface: txt; next-hop-vif: txt; next-hop-router: ipv4 = 0.0.0.0; metric: u32 = 1; qualified-next-hop-interface @: txt { qualified-next-hop-vif @: txt { next-hop-router: ipv4 = 0.0.0.0; metric: u32 = 10; } } } interface-route @: ipv6net { next-hop-interface: txt; next-hop-vif: txt; next-hop-router: ipv6 = ::; metric: u32 = 1; qualified-next-hop-interface @: txt { qualified-next-hop-vif @: txt { next-hop-router: ipv6 = ::; metric: u32 = 10; } } } interface-route4 @: ipv4net { /* %deprecated */ next-hop-interface: txt; next-hop-vif: txt; next-hop-router: ipv4 = 0.0.0.0; metric: u32 = 1; } interface-route6 @: ipv6net { /* %deprecated */ next-hop-interface: txt; next-hop-vif: txt; next-hop-router: ipv6 = ::; metric: u32 = 1; } mrib-route @: ipv4net { next-hop: ipv4; nexthop: ipv4; /* %deprecated */ metric: u32 = 1; qualified-next-hop @: ipv4 { metric: u32 = 10; } } mrib-route @: ipv6net { next-hop: ipv6; nexthop: ipv4; /* %deprecated */ metric: u32 = 1; qualified-next-hop @: ipv6 { metric: u32 = 10; } } mrib-route4 @: ipv4net { /* %deprecated */ next-hop: ipv4; nexthop: ipv4; /* %deprecated */ metric: u32 = 1; } mrib-route6 @: ipv6net { /* %deprecated */ next-hop: ipv6; nexthop: ipv4; /* %deprecated */ metric: u32 = 1; } mrib-interface-route @: ipv4net { next-hop-interface: txt; next-hop-vif: txt; next-hop-router: ipv4 = 0.0.0.0; metric: u32 = 1; qualified-next-hop-interface @: txt { qualified-next-hop-vif @: txt { next-hop-router: ipv4 = 0.0.0.0; metric: u32 = 10; } } } mrib-interface-route @: ipv6net { next-hop-interface: txt; next-hop-vif: txt; next-hop-router: ipv6 = ::; metric: u32 = 1; qualified-next-hop-interface @: txt { qualified-next-hop-vif @: txt { next-hop-router: ipv6 = ::; metric: u32 = 10; } } } mrib-interface-route4 @: ipv4net { /* %deprecated */ next-hop-interface: txt; next-hop-vif: txt; next-hop-router: ipv4 = 0.0.0.0; metric: u32 = 1; } mrib-interface-route6 @: ipv6net { /* %deprecated */ next-hop-interface: txt; next-hop-vif: txt; next-hop-router: ipv6 = ::; metric: u32 = 1; } import: txt; } } policy { policy-statement @: txt { term @: txt { from { metric: u32range; } } } } protocols { static { %help: short "Configure static routes"; %modinfo: provides static_routes; %modinfo: depends rib; %modinfo: depends policy; %modinfo: path "xorp_static_routes"; %modinfo: default_targetname "static_routes"; %modinfo: status_method xrl "$(static.targetname)/common/0.1/get_status->status:u32&reason:txt"; %modinfo: startup_method xrl "$(static.targetname)/common/0.1/startup"; %modinfo: shutdown_method xrl "$(static.targetname)/common/0.1/shutdown"; %mandatory: $(@.targetname); %activate: xrl "$(static.targetname)/static_routes/0.1/start_static_routes"; targetname { %user-hidden: "XRL target name"; %help: short "XRL target name"; %set:; } disable { %help: short "Disable the static routes"; %create:; %set: xrl "$(static.targetname)/static_routes/0.1/enable_static_routes?enable:bool=`~$(@)`"; %delete: xrl "$(static.targetname)/static_routes/0.1/enable_static_routes?enable:bool=`~$(DEFAULT)`"; } enabled { %deprecated: "Statement 'enabled: true/false' is replaced with 'disable: false/true'"; %help: short "Enable the static routes"; %create:; %set: xrl "$(static.targetname)/static_routes/0.1/enable_static_routes?enable:bool=$(@)"; } route @: ipv4net { %help: short "Configure a static route"; %mandatory: $(@.next-hop); %create: xrl "$(static.targetname)/static_routes/0.1/add_route4?unicast:bool=true&multicast:bool=false&network:ipv4net=$(@)&nexthop:ipv4=$(@.next-hop)&metric:u32=$(@.metric)"; %update: xrl "$(static.targetname)/static_routes/0.1/replace_route4?unicast:bool=true&multicast:bool=false&network:ipv4net=$(@)&nexthop:ipv4=$(@.next-hop)&metric:u32=$(@.metric)"; %delete: xrl "$(static.targetname)/static_routes/0.1/delete_route4?unicast:bool=true&multicast:bool=false&network:ipv4net=$(@)&nexthop:ipv4=$(@.next-hop)"; next-hop { %help: short "Configure the next-hop router"; %set:; } nexthop { %deprecated: "Statement 'nexthop' is replaced with 'next-hop'"; %help: short "Configure the next-hop router"; %set:; } metric { %help: short "Configure the routing metric"; %allow-range: $(@) "1" "65535" %help: "The routing metric"; %set:; } qualified-next-hop @: ipv4 { %help: short "Next hop with qualifiers"; %create: xrl "$(static.targetname)/static_routes/0.1/add_backup_route4?unicast:bool=true&multicast:bool=false&network:ipv4net=$(route.@)&nexthop:ipv4=$(@)&metric:u32=$(@.metric)"; %update: xrl "$(static.targetname)/static_routes/0.1/replace_backup_route4?unicast:bool=true&multicast:bool=false&network:ipv4net=$(route.@)&nexthop:ipv4=$(@)&metric:u32=$(@.metric)"; %delete: xrl "$(static.targetname)/static_routes/0.1/delete_backup_route4?unicast:bool=true&multicast:bool=false&network:ipv4net=$(route.@)&nexthop:ipv4=$(@)"; metric { %help: short "Configure the routing metric"; %allow-range: $(@) "1" "65535" %help: "The routing metric"; %set:; } } } route @: ipv6net { %help: short "Configure a static route"; %mandatory: $(@.next-hop); %create: xrl "$(static.targetname)/static_routes/0.1/add_route6?unicast:bool=true&multicast:bool=false&network:ipv6net=$(@)&nexthop:ipv6=$(@.next-hop)&metric:u32=$(@.metric)"; %update: xrl "$(static.targetname)/static_routes/0.1/replace_route6?unicast:bool=true&multicast:bool=false&network:ipv6net=$(@)&nexthop:ipv6=$(@.next-hop)&metric:u32=$(@.metric)"; %delete: xrl "$(static.targetname)/static_routes/0.1/delete_route6?unicast:bool=true&multicast:bool=false&network:ipv6net=$(@)&nexthop:ipv6=$(@.next-hop)"; next-hop { %help: short "Configure the next-hop router"; %set:; } nexthop { %deprecated: "Statement 'nexthop' is replaced with 'next-hop'"; %help: short "Configure the next-hop router"; %set:; } metric { %help: short "Configure the routing metric"; %allow-range: $(@) "1" "65535" %help: "The routing metric"; %set:; } qualified-next-hop @: ipv6 { %help: short "Next hop with qualifiers"; %create: xrl "$(static.targetname)/static_routes/0.1/add_backup_route6?unicast:bool=true&multicast:bool=false&network:ipv6net=$(route.@)&nexthop:ipv6=$(@)&metric:u32=$(@.metric)"; %update: xrl "$(static.targetname)/static_routes/0.1/replace_backup_route6?unicast:bool=true&multicast:bool=false&network:ipv6net=$(route.@)&nexthop:ipv6=$(@)&metric:u32=$(@.metric)"; %delete: xrl "$(static.targetname)/static_routes/0.1/delete_backup_route6?unicast:bool=true&multicast:bool=false&network:ipv6net=$(route.@)&nexthop:ipv6=$(@)"; metric { %help: short "Configure the routing metric"; %allow-range: $(@) "1" "65535" %help: "The routing metric"; %set:; } } } route4 @: ipv4net { %deprecated: "Statement 'route4' is replaced with 'route'"; %help: short "Configure an IPv4 static route"; %mandatory: $(@.next-hop); %create: xrl "$(static.targetname)/static_routes/0.1/add_route4?unicast:bool=true&multicast:bool=false&network:ipv4net=$(@)&nexthop:ipv4=$(@.next-hop)&metric:u32=$(@.metric)"; %update: xrl "$(static.targetname)/static_routes/0.1/replace_route4?unicast:bool=true&multicast:bool=false&network:ipv4net=$(@)&nexthop:ipv4=$(@.next-hop)&metric:u32=$(@.metric)"; %delete: xrl "$(static.targetname)/static_routes/0.1/delete_route4?unicast:bool=true&multicast:bool=false&network:ipv4net=$(@)&nexthop:ipv4=$(@.next-hop)"; next-hop { %help: short "Configure the next-hop router"; %set:; } nexthop { %deprecated: "Statement 'nexthop' is replaced with 'next-hop'"; %help: short "Configure the next-hop router"; %set:; } metric { %help: short "Configure the routing metric"; %allow-range: $(@) "1" "65535" %help: "The routing metric"; %set:; } } route6 @: ipv6net { %deprecated: "Statement 'route6' is replaced with 'route'"; %help: short "Configure an IPv6 static route"; %mandatory: $(@.next-hop); %create: xrl "$(static.targetname)/static_routes/0.1/add_route6?unicast:bool=true&multicast:bool=false&network:ipv6net=$(@)&nexthop:ipv6=$(@.next-hop)&metric:u32=$(@.metric)"; %update: xrl "$(static.targetname)/static_routes/0.1/replace_route6?unicast:bool=true&multicast:bool=false&network:ipv6net=$(@)&nexthop:ipv6=$(@.next-hop)&metric:u32=$(@.metric)"; %delete: xrl "$(static.targetname)/static_routes/0.1/delete_route6?unicast:bool=true&multicast:bool=false&network:ipv6net=$(@)&nexthop:ipv6=$(@.next-hop)"; next-hop { %help: short "Configure the next-hop router"; %set:; } nexthop { %deprecated: "Statement 'nexthop' is replaced with 'next-hop'"; %help: short "Configure the next-hop router"; %set:; } metric { %help: short "Configure the routing metric"; %allow-range: $(@) "1" "65535" %help: "The routing metric"; %set:; } } interface-route @: ipv4net { %help: short "Configure an interface-based static route"; %mandatory: $(@.next-hop-interface), $(@.next-hop-vif); %create: xrl "$(static.targetname)/static_routes/0.1/add_interface_route4?unicast:bool=true&multicast:bool=false&network:ipv4net=$(@)&nexthop:ipv4=$(@.next-hop-router)&ifname:txt=$(@.next-hop-interface)&vifname:txt=$(@.next-hop-vif)&metric:u32=$(@.metric)"; %update: xrl "$(static.targetname)/static_routes/0.1/replace_interface_route4?unicast:bool=true&multicast:bool=false&network:ipv4net=$(@)&nexthop:ipv4=$(@.next-hop-router)&ifname:txt=$(@.next-hop-interface)&vifname:txt=$(@.next-hop-vif)&metric:u32=$(@.metric)"; %delete: xrl "$(static.targetname)/static_routes/0.1/delete_interface_route4?unicast:bool=true&multicast:bool=false&network:ipv4net=$(@)&nexthop:ipv4=$(@.next-hop-router)&ifname:txt=$(@.next-hop-interface)&vifname:txt=$(@.next-hop-vif)"; next-hop-interface { %help: short "Configure the next-hop network interface name"; %set:; } next-hop-vif { %help: short "Configure the next-hop virtual interface name"; %set:; } next-hop-router { %help: short "Configure the optional next-hop router"; %set:; } metric { %help: short "Configure the routing metric"; %allow-range: $(@) "1" "65535" %help: "The routing metric"; %set:; } qualified-next-hop-interface @: txt { %help: short "Next hop interface name with qualifiers"; qualified-next-hop-vif @: txt { %help: short "Next hop virtual interface name with qualifiers"; %create: xrl "$(static.targetname)/static_routes/0.1/add_backup_interface_route4?unicast:bool=true&multicast:bool=false&network:ipv4net=$(interface-route.@)&nexthop:ipv4=$(@.next-hop-router)&ifname:txt=$(qualified-next-hop-interface.@)&vifname:txt=$(qualified-next-hop-vif.@)&metric:u32=$(@.metric)"; %update: xrl "$(static.targetname)/static_routes/0.1/replace_backup_interface_route4?unicast:bool=true&multicast:bool=false&network:ipv4net=$(interface-route.@)&nexthop:ipv4=$(@.next-hop-router)&ifname:txt=$(qualified-next-hop-interface.@)&vifname:txt=$(qualified-next-hop-vif.@)&metric:u32=$(@.metric)"; %delete: xrl "$(static.targetname)/static_routes/0.1/delete_backup_interface_route4?unicast:bool=true&multicast:bool=false&network:ipv4net=$(interface-route.@)&nexthop:ipv4=$(@.next-hop-router)&ifname:txt=$(qualified-next-hop-interface.@)&vifname:txt=$(qualified-next-hop-vif.@)"; next-hop-router { %help: short "Configure the optional next-hop router"; %set:; } metric { %help: short "Configure the routing metric"; %allow-range: $(@) "1" "65535" %help: "The routing metric"; %set:; } } } } interface-route @: ipv6net { %help: short "Configure an interface-based static route"; %mandatory: $(@.next-hop-interface), $(@.next-hop-vif); %create: xrl "$(static.targetname)/static_routes/0.1/add_interface_route6?unicast:bool=true&multicast:bool=false&network:ipv6net=$(@)&nexthop:ipv6=$(@.next-hop-router)&ifname:txt=$(@.next-hop-interface)&vifname:txt=$(@.next-hop-vif)&metric:u32=$(@.metric)"; %update: xrl "$(static.targetname)/static_routes/0.1/replace_interface_route6?unicast:bool=true&multicast:bool=false&network:ipv6net=$(@)&nexthop:ipv6=$(@.next-hop-router)&ifname:txt=$(@.next-hop-interface)&vifname:txt=$(@.next-hop-vif)&metric:u32=$(@.metric)"; %delete: xrl "$(static.targetname)/static_routes/0.1/delete_interface_route6?unicast:bool=true&multicast:bool=false&network:ipv6net=$(@)&nexthop:ipv6=$(@.next-hop-router)&ifname:txt=$(@.next-hop-interface)&vifname:txt=$(@.next-hop-vif)"; next-hop-interface { %help: short "Configure the next-hop network interface name"; %set:; } next-hop-vif { %help: short "Configure the next-hop virtual interface name"; %set:; } next-hop-router { %help: short "Configure the optional next-hop router"; %set:; } metric { %help: short "Configure the routing metric"; %allow-range: $(@) "1" "65535" %help: "The routing metric"; %set:; } qualified-next-hop-interface @: txt { %help: short "Next hop interface name with qualifiers"; qualified-next-hop-vif @: txt { %help: short "Next hop virtual interface name with qualifiers"; %create: xrl "$(static.targetname)/static_routes/0.1/add_backup_interface_route6?unicast:bool=true&multicast:bool=false&network:ipv6net=$(interface-route.@)&nexthop:ipv6=$(@.next-hop-router)&ifname:txt=$(qualified-next-hop-interface.@)&vifname:txt=$(qualified-next-hop-vif.@)&metric:u32=$(@.metric)"; %update: xrl "$(static.targetname)/static_routes/0.1/replace_backup_interface_route6?unicast:bool=true&multicast:bool=false&network:ipv6net=$(interface-route.@)&nexthop:ipv6=$(@.next-hop-router)&ifname:txt=$(qualified-next-hop-interface.@)&vifname:txt=$(qualified-next-hop-vif.@)&metric:u32=$(@.metric)"; %delete: xrl "$(static.targetname)/static_routes/0.1/delete_backup_interface_route6?unicast:bool=true&multicast:bool=false&network:ipv6net=$(interface-route.@)&nexthop:ipv6=$(@.next-hop-router)&ifname:txt=$(qualified-next-hop-interface.@)&vifname:txt=$(qualified-next-hop-vif.@)"; next-hop-router { %help: short "Configure the optional next-hop router"; %set:; } metric { %help: short "Configure the routing metric"; %allow-range: $(@) "1" "65535" %help: "The routing metric"; %set:; } } } } interface-route4 @: ipv4net { %deprecated: "Statement 'interface-route4' is replaced with 'interface-route'"; %help: short "Configure an interface-based IPv4 static route"; %mandatory: $(@.next-hop-interface), $(@.next-hop-vif); %create: xrl "$(static.targetname)/static_routes/0.1/add_interface_route4?unicast:bool=true&multicast:bool=false&network:ipv4net=$(@)&nexthop:ipv4=$(@.next-hop-router)&ifname:txt=$(@.next-hop-interface)&vifname:txt=$(@.next-hop-vif)&metric:u32=$(@.metric)"; %update: xrl "$(static.targetname)/static_routes/0.1/replace_interface_route4?unicast:bool=true&multicast:bool=false&network:ipv4net=$(@)&nexthop:ipv4=$(@.next-hop-router)&ifname:txt=$(@.next-hop-interface)&vifname:txt=$(@.next-hop-vif)&metric:u32=$(@.metric)"; %delete: xrl "$(static.targetname)/static_routes/0.1/delete_interface_route4?unicast:bool=true&multicast:bool=false&network:ipv4net=$(@)&nexthop:ipv4=$(@.next-hop-router)&ifname:txt=$(@.next-hop-interface)&vifname:txt=$(@.next-hop-vif)"; next-hop-interface { %help: short "Configure the next-hop network interface name"; %set:; } next-hop-vif { %help: short "Configure the next-hop virtual interface name"; %set:; } next-hop-router { %help: short "Configure the optional next-hop router"; %set:; } metric { %help: short "Configure the routing metric"; %allow-range: $(@) "1" "65535" %help: "The routing metric"; %set:; } } interface-route6 @: ipv6net { %deprecated: "Statement 'interface-route6' is replaced with 'interface-route'"; %help: short "Configure an interface-based IPv6 static route"; %mandatory: $(@.next-hop-interface), $(@.next-hop-vif); %create: xrl "$(static.targetname)/static_routes/0.1/add_interface_route6?unicast:bool=true&multicast:bool=false&network:ipv6net=$(@)&nexthop:ipv6=$(@.next-hop-router)&ifname:txt=$(@.next-hop-interface)&vifname:txt=$(@.next-hop-vif)&metric:u32=$(@.metric)"; %update: xrl "$(static.targetname)/static_routes/0.1/replace_interface_route6?unicast:bool=true&multicast:bool=false&network:ipv6net=$(@)&nexthop:ipv6=$(@.next-hop-router)&ifname:txt=$(@.next-hop-interface)&vifname:txt=$(@.next-hop-vif)&metric:u32=$(@.metric)"; %delete: xrl "$(static.targetname)/static_routes/0.1/delete_interface_route6?unicast:bool=true&multicast:bool=false&network:ipv6net=$(@)&nexthop:ipv6=$(@.next-hop-router)&ifname:txt=$(@.next-hop-interface)&vifname:txt=$(@.next-hop-vif)"; next-hop-interface { %help: short "Configure the next-hop network interface name"; %set:; } next-hop-vif { %help: short "Configure the next-hop virtual interface name"; %set:; } next-hop-router { %help: short "Configure the optional next-hop router"; %set:; } metric { %help: short "Configure the routing metric"; %allow-range: $(@) "1" "65535" %help: "The routing metric"; %set:; } } mrib-route @: ipv4net { %help: short "Configure a multicast-specific static route"; %mandatory: $(@.next-hop); %create: xrl "$(static.targetname)/static_routes/0.1/add_route4?unicast:bool=false&multicast:bool=true&network:ipv4net=$(@)&nexthop:ipv4=$(@.next-hop)&metric:u32=$(@.metric)"; %update: xrl "$(static.targetname)/static_routes/0.1/replace_route4?unicast:bool=false&multicast:bool=true&network:ipv4net=$(@)&nexthop:ipv4=$(@.next-hop)&metric:u32=$(@.metric)"; %delete: xrl "$(static.targetname)/static_routes/0.1/delete_route4?unicast:bool=false&multicast:bool=true&network:ipv4net=$(@)&nexthop:ipv4=$(@.next-hop)"; next-hop { %help: short "Configure the next-hop router"; %set:; } nexthop { %deprecated: "Statement 'nexthop' is replaced with 'next-hop'"; %help: short "Configure the next-hop router"; %set:; } metric { %help: short "Configure the routing metric"; %allow-range: $(@) "1" "65535" %help: "The routing metric"; %set:; } qualified-next-hop @: ipv4 { %help: short "Next hop with qualifiers"; %create: xrl "$(static.targetname)/static_routes/0.1/add_backup_route4?unicast:bool=false&multicast:bool=true&network:ipv4net=$(mrib-route.@)&nexthop:ipv4=$(@)&metric:u32=$(@.metric)"; %update: xrl "$(static.targetname)/static_routes/0.1/replace_backup_route4?unicast:bool=false&multicast:bool=true&network:ipv4net=$(mrib-route.@)&nexthop:ipv4=$(@)&metric:u32=$(@.metric)"; %delete: xrl "$(static.targetname)/static_routes/0.1/delete_backup_route4?unicast:bool=false&multicast:bool=true&network:ipv4net=$(mrib-route.@)&nexthop:ipv4=$(@)"; metric { %help: short "Configure the routing metric"; %allow-range: $(@) "1" "65535" %help: "The routing metric"; %set:; } } } mrib-route @: ipv6net { %help: short "Configure a multicast-specific static route"; %mandatory: $(@.next-hop); %create: xrl "$(static.targetname)/static_routes/0.1/add_route6?unicast:bool=false&multicast:bool=true&network:ipv6net=$(@)&nexthop:ipv6=$(@.next-hop)&metric:u32=$(@.metric)"; %update: xrl "$(static.targetname)/static_routes/0.1/replace_route6?unicast:bool=false&multicast:bool=true&network:ipv6net=$(@)&nexthop:ipv6=$(@.next-hop)&metric:u32=$(@.metric)"; %delete: xrl "$(static.targetname)/static_routes/0.1/delete_route6?unicast:bool=false&multicast:bool=true&network:ipv6net=$(@)&nexthop:ipv6=$(@.next-hop)"; next-hop { %help: short "Configure the next-hop router"; %set:; } nexthop { %deprecated: "Statement 'nexthop' is replaced with 'next-hop'"; %help: short "Configure the next-hop router"; %set:; } metric { %help: short "Configure the routing metric"; %allow-range: $(@) "1" "65535" %help: "The routing metric"; %set:; } qualified-next-hop @: ipv6 { %help: short "Next hop with qualifiers"; %create: xrl "$(static.targetname)/static_routes/0.1/add_backup_route6?unicast:bool=false&multicast:bool=true&network:ipv6net=$(mrib-route.@)&nexthop:ipv6=$(@)&metric:u32=$(@.metric)"; %update: xrl "$(static.targetname)/static_routes/0.1/replace_backup_route6?unicast:bool=false&multicast:bool=true&network:ipv6net=$(mrib-route.@)&nexthop:ipv6=$(@)&metric:u32=$(@.metric)"; %delete: xrl "$(static.targetname)/static_routes/0.1/delete_backup_route6?unicast:bool=false&multicast:bool=true&network:ipv6net=$(mrib-route.@)&nexthop:ipv6=$(@)"; metric { %help: short "Configure the routing metric"; %allow-range: $(@) "1" "65535" %help: "The routing metric"; %set:; } } } mrib-route4 @: ipv4net { %deprecated: "Statement 'mrib-route4' is replaced with 'mrib-route'"; %help: short "Configure a multicast-specific IPv4 static route"; %mandatory: $(@.next-hop); %create: xrl "$(static.targetname)/static_routes/0.1/add_route4?unicast:bool=false&multicast:bool=true&network:ipv4net=$(@)&nexthop:ipv4=$(@.next-hop)&metric:u32=$(@.metric)"; %update: xrl "$(static.targetname)/static_routes/0.1/replace_route4?unicast:bool=false&multicast:bool=true&network:ipv4net=$(@)&nexthop:ipv4=$(@.next-hop)&metric:u32=$(@.metric)"; %delete: xrl "$(static.targetname)/static_routes/0.1/delete_route4?unicast:bool=false&multicast:bool=true&network:ipv4net=$(@)&nexthop:ipv4=$(@.next-hop)"; next-hop { %help: short "Configure the next-hop router"; %set:; } nexthop { %deprecated: "Statement 'nexthop' is replaced with 'next-hop'"; %help: short "Configure the next-hop router"; %set:; } metric { %help: short "Configure the routing metric"; %allow-range: $(@) "1" "65535" %help: "The routing metric"; %set:; } } mrib-route6 @: ipv6net { %deprecated: "Statement 'mrib-route6' is replaced with 'mrib-route'"; %help: short "Configure a multicast-specific IPv6 static route"; %mandatory: $(@.next-hop); %create: xrl "$(static.targetname)/static_routes/0.1/add_route6?unicast:bool=false&multicast:bool=true&network:ipv6net=$(@)&nexthop:ipv6=$(@.next-hop)&metric:u32=$(@.metric)"; %update: xrl "$(static.targetname)/static_routes/0.1/replace_route6?unicast:bool=false&multicast:bool=true&network:ipv6net=$(@)&nexthop:ipv6=$(@.next-hop)&metric:u32=$(@.metric)"; %delete: xrl "$(static.targetname)/static_routes/0.1/delete_route6?unicast:bool=false&multicast:bool=true&network:ipv6net=$(@)&nexthop:ipv6=$(@.next-hop)"; next-hop { %help: short "Configure the next-hop router"; %set:; } nexthop { %deprecated: "Statement 'nexthop' is replaced with 'next-hop'"; %help: short "Configure the next-hop router"; %set:; } metric { %help: short "Configure the routing metric"; %allow-range: $(@) "1" "65535" %help: "The routing metric"; %set:; } } mrib-interface-route @: ipv4net { %help: short "Configure a multicast-specific interface-based static route"; %mandatory: $(@.next-hop-interface), $(@.next-hop-vif); %create: xrl "$(static.targetname)/static_routes/0.1/add_interface_route4?unicast:bool=false&multicast:bool=true&network:ipv4net=$(@)&nexthop:ipv4=$(@.next-hop-router)&ifname:txt=$(@.next-hop-interface)&vifname:txt=$(@.next-hop-vif)&metric:u32=$(@.metric)"; %update: xrl "$(static.targetname)/static_routes/0.1/replace_interface_route4?unicast:bool=false&multicast:bool=true&network:ipv4net=$(@)&nexthop:ipv4=$(@.next-hop-router)&ifname:txt=$(@.next-hop-interface)&vifname:txt=$(@.next-hop-vif)&metric:u32=$(@.metric)"; %delete: xrl "$(static.targetname)/static_routes/0.1/delete_interface_route4?unicast:bool=false&multicast:bool=true&network:ipv4net=$(@)&nexthop:ipv4=$(@.next-hop-router)&ifname:txt=$(@.next-hop-interface)&vifname:txt=$(@.next-hop-vif)"; next-hop-interface { %help: short "Configure the next-hop network interface name"; %set:; } next-hop-vif { %help: short "Configure the next-hop virtual interface name"; %set:; } next-hop-router { %help: short "Configure the optional next-hop router"; %set:; } metric { %help: short "Configure the routing metric"; %allow-range: $(@) "1" "65535" %help: "The routing metric"; %set:; } qualified-next-hop-interface @: txt { %help: short "Next hop interface name with qualifiers"; qualified-next-hop-vif @: txt { %help: short "Next hop virtual interface name with qualifiers"; %create: xrl "$(static.targetname)/static_routes/0.1/add_backup_interface_route4?unicast:bool=false&multicast:bool=true&network:ipv4net=$(mrib-interface-route.@)&nexthop:ipv4=$(@.next-hop-router)&ifname:txt=$(qualified-next-hop-interface.@)&vifname:txt=$(qualified-next-hop-vif.@)&metric:u32=$(@.metric)"; %update: xrl "$(static.targetname)/static_routes/0.1/replace_backup_interface_route4?unicast:bool=false&multicast:bool=true&network:ipv4net=$(mrib-interface-route.@)&nexthop:ipv4=$(@.next-hop-router)&ifname:txt=$(qualified-next-hop-interface.@)&vifname:txt=$(qualified-next-hop-vif.@)&metric:u32=$(@.metric)"; %delete: xrl "$(static.targetname)/static_routes/0.1/delete_backup_interface_route4?unicast:bool=false&multicast:bool=true&network:ipv4net=$(mrib-interface-route.@)&nexthop:ipv4=$(@.next-hop-router)&ifname:txt=$(qualified-next-hop-interface.@)&vifname:txt=$(qualified-next-hop-vif.@)"; next-hop-router { %help: short "Configure the optional next-hop router"; %set:; } metric { %help: short "Configure the routing metric"; %allow-range: $(@) "1" "65535" %help: "The routing metric"; %set:; } } } } mrib-interface-route @: ipv6net { %help: short "Configure a multicast-specific interface-based static route"; %mandatory: $(@.next-hop-interface), $(@.next-hop-vif); %create: xrl "$(static.targetname)/static_routes/0.1/add_interface_route6?unicast:bool=false&multicast:bool=true&network:ipv6net=$(@)&nexthop:ipv6=$(@.next-hop-router)&ifname:txt=$(@.next-hop-interface)&vifname:txt=$(@.next-hop-vif)&metric:u32=$(@.metric)"; %update: xrl "$(static.targetname)/static_routes/0.1/replace_interface_route6?unicast:bool=false&multicast:bool=true&network:ipv6net=$(@)&nexthop:ipv6=$(@.next-hop-router)&ifname:txt=$(@.next-hop-interface)&vifname:txt=$(@.next-hop-vif)&metric:u32=$(@.metric)"; %delete: xrl "$(static.targetname)/static_routes/0.1/delete_interface_route6?unicast:bool=false&multicast:bool=true&network:ipv6net=$(@)&nexthop:ipv6=$(@.next-hop-router)&ifname:txt=$(@.next-hop-interface)&vifname:txt=$(@.next-hop-vif)"; next-hop-interface { %help: short "Configure the next-hop network interface name"; %set:; } next-hop-vif { %help: short "Configure the next-hop virtual interface name"; %set:; } next-hop-router { %help: short "Configure the optional next-hop router"; %set:; } metric { %help: short "Configure the routing metric"; %allow-range: $(@) "1" "65535" %help: "The routing metric"; %set:; } qualified-next-hop-interface @: txt { %help: short "Next hop interface name with qualifiers"; qualified-next-hop-vif @: txt { %help: short "Next hop virtual interface name with qualifiers"; %create: xrl "$(static.targetname)/static_routes/0.1/add_backup_interface_route6?unicast:bool=false&multicast:bool=true&network:ipv6net=$(mrib-interface-route.@)&nexthop:ipv6=$(@.next-hop-router)&ifname:txt=$(qualified-next-hop-interface.@)&vifname:txt=$(qualified-next-hop-vif.@)&metric:u32=$(@.metric)"; %update: xrl "$(static.targetname)/static_routes/0.1/replace_backup_interface_route6?unicast:bool=false&multicast:bool=true&network:ipv6net=$(mrib-interface-route.@)&nexthop:ipv6=$(@.next-hop-router)&ifname:txt=$(qualified-next-hop-interface.@)&vifname:txt=$(qualified-next-hop-vif.@)&metric:u32=$(@.metric)"; %delete: xrl "$(static.targetname)/static_routes/0.1/delete_backup_interface_route6?unicast:bool=false&multicast:bool=true&network:ipv6net=$(mrib-interface-route.@)&nexthop:ipv6=$(@.next-hop-router)&ifname:txt=$(qualified-next-hop-interface.@)&vifname:txt=$(qualified-next-hop-vif.@)"; next-hop-router { %help: short "Configure the optional next-hop router"; %set:; } metric { %help: short "Configure the routing metric"; %allow-range: $(@) "1" "65535" %help: "The routing metric"; %set:; } } } } mrib-interface-route4 @: ipv4net { %deprecated: "Statement 'mrib-interface-route4' is replaced with 'mrib-interface-route'"; %help: short "Configure a multicast-specific interface-based IPv4 static route"; %mandatory: $(@.next-hop-interface), $(@.next-hop-vif); %create: xrl "$(static.targetname)/static_routes/0.1/add_interface_route4?unicast:bool=false&multicast:bool=true&network:ipv4net=$(@)&nexthop:ipv4=$(@.next-hop-router)&ifname:txt=$(@.next-hop-interface)&vifname:txt=$(@.next-hop-vif)&metric:u32=$(@.metric)"; %update: xrl "$(static.targetname)/static_routes/0.1/replace_interface_route4?unicast:bool=false&multicast:bool=true&network:ipv4net=$(@)&nexthop:ipv4=$(@.next-hop-router)&ifname:txt=$(@.next-hop-interface)&vifname:txt=$(@.next-hop-vif)&metric:u32=$(@.metric)"; %delete: xrl "$(static.targetname)/static_routes/0.1/delete_interface_route4?unicast:bool=false&multicast:bool=true&network:ipv4net=$(@)&nexthop:ipv4=$(@.next-hop-router)&ifname:txt=$(@.next-hop-interface)&vifname:txt=$(@.next-hop-vif)"; next-hop-interface { %help: short "Configure the next-hop network interface name"; %set:; } next-hop-vif { %help: short "Configure the next-hop virtual interface name"; %set:; } next-hop-router { %help: short "Configure the optional next-hop router"; %set:; } metric { %help: short "Configure the routing metric"; %allow-range: $(@) "1" "65535" %help: "The routing metric"; %set:; } } mrib-interface-route6 @: ipv6net { %deprecated: "Statement 'mrib-interface-route4' is replaced with 'mrib-interface-route'"; %help: short "Configure a multicast-specific interface-based IPv6 static route"; %mandatory: $(@.next-hop-interface), $(@.next-hop-vif); %create: xrl "$(static.targetname)/static_routes/0.1/add_interface_route6?unicast:bool=false&multicast:bool=true&network:ipv6net=$(@)&nexthop:ipv6=$(@.next-hop-router)&ifname:txt=$(@.next-hop-interface)&vifname:txt=$(@.next-hop-vif)&metric:u32=$(@.metric)"; %update: xrl "$(static.targetname)/static_routes/0.1/replace_interface_route6?unicast:bool=false&multicast:bool=true&network:ipv6net=$(@)&nexthop:ipv6=$(@.next-hop-router)&ifname:txt=$(@.next-hop-interface)&vifname:txt=$(@.next-hop-vif)&metric:u32=$(@.metric)"; %delete: xrl "$(static.targetname)/static_routes/0.1/delete_interface_route6?unicast:bool=false&multicast:bool=true&network:ipv6net=$(@)&nexthop:ipv6=$(@.next-hop-router)&ifname:txt=$(@.next-hop-interface)&vifname:txt=$(@.next-hop-vif)"; next-hop-interface { %help: short "Configure the next-hop network interface name"; %set:; } next-hop-vif { %help: short "Configure the next-hop virtual interface name"; %set:; } next-hop-router { %help: short "Configure the optional next-hop router"; %set:; } metric { %help: short "Configure the routing metric"; %allow-range: $(@) "1" "65535" %help: "The routing metric"; %set:; } } import { %help: short "Import policy"; %delete: xrl "policy/policy/0.1/import?protocol:txt=static&policies:txt=&modifier:txt="; %set: xrl "policy/policy/0.1/import?protocol:txt=static&policies:txt=$(@)&modifier:txt="; } } } policy { %create: xrl "$(policy.targetname)/policy/0.1/set_proto_target?protocol:txt=static&target:txt=static_routes"; %create: xrl "$(policy.targetname)/policy/0.1/add_varmap?protocol:txt=static&variable:txt=network4&type:txt=ipv4net&access:txt=r&id:u32=10"; %create: xrl "$(policy.targetname)/policy/0.1/add_varmap?protocol:txt=static&variable:txt=nexthop4&type:txt=ipv4nexthop&access:txt=rw&id:u32=11"; %create: xrl "$(policy.targetname)/policy/0.1/add_varmap?protocol:txt=static&variable:txt=network6&type:txt=ipv6net&access:txt=r&id:u32=12"; %create: xrl "$(policy.targetname)/policy/0.1/add_varmap?protocol:txt=static&variable:txt=nexthop6&type:txt=ipv6nexthop&access:txt=rw&id:u32=13"; %create: xrl "$(policy.targetname)/policy/0.1/add_varmap?protocol:txt=static&variable:txt=metric&type:txt=u32&access:txt=rw&id:u32=14"; policy-statement @: txt { term @: txt { from { metric { %help: short "Metric value"; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt=metric $(<>) $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt="; } } } } } xorp/etc/templates/rip.cmds0000664000076400007640000000314211421137511016067 0ustar greearbgreearb/* $XORP: xorp/etc/templates/rip.cmds,v 1.4 2004/06/06 02:12:25 hodson Exp $ */ show rip { %command: "" %help: HELP; %module: rip; %tag: HELP "Display information about RIP"; } show rip peers { %command: "rip_show_peer_stats -1 $2" %help: HELP; %module: rip; %tag: HELP "Show all RIP peers each on a single line"; } show rip peer statistics $(protocols.rip.interface.*) $(protocols.rip.interface.*.vif.*) $(protocols.rip.interface.*.vif.*.address.*) { %command: "rip_show_peer_stats $2 $5 $6 $7" %help: HELP; %module: rip; %tag: HELP "Show RIP statistics for peers on specified address"; } show rip peer statistics all { %command: "rip_show_peer_stats $2" %help: HELP; %module: rip; %tag: HELP "Show RIP statistics for all peers"; } show rip statistics $(protocols.rip.interface.*) $(protocols.rip.interface.*.vif.*) $(protocols.rip.interface.*.vif.*.address.*) { %command: "rip_show_stats $2 $4 $5 $6" %help: HELP; %module: rip; %tag: HELP "Show RIP statistics on specified address"; } show rip statistics all { %command: "rip_show_stats $2" %help: HELP; %module: rip; %tag: HELP "Show RIP statistics for all addresses"; } show rip status $(protocols.rip.interface.*) $(protocols.rip.interface.*.vif.*) $(protocols.rip.interface.*.vif.*.address.*) { %command: "rip_show_stats -b $2 $4 $5 $6" %help: HELP; %module: rip; %tag: HELP "Show RIP status on specified address"; } show rip status all { %command: "rip_show_stats -b $2" %help: HELP; %module: rip; %tag: HELP "Show RIP status for all addresses"; } xorp/etc/templates/mld.tp0000664000076400007640000001545511510461254015563 0ustar greearbgreearb/* $XORP: xorp/etc/templates/mld.tp,v 1.20 2006/07/03 06:52:37 pavlin Exp $ */ protocols { mld { targetname: txt = "MLD"; disable: toggle = false; enabled: bool; /* %deprecated */ interface @: txt { vif @: txt { disable: toggle = false; enabled: bool; /* %deprecated */ version: u32 = 1; /* Default to MLDv1 */ enable-ip-router-alert-option-check: bool = false; query-interval: u32 = 125; query-last-member-interval: u32 = 1; query-response-interval: u32 = 10; robust-count: u32 = 2; } } traceoptions { flag { all { disable: toggle = false; enabled: bool; /* %deprecated */ } } } } } protocols { mld { %help: short "Configure the MLD protocol"; %modinfo: provides mld; %modinfo: depends mfea6; %modinfo: path "xorp_mld"; %modinfo: default_targetname "mld6igmp"; %modinfo: status_method xrl "$(mld.targetname)/common/0.1/get_status->status:u32&reason:txt"; %modinfo: startup_method xrl "$(mld.targetname)/common/0.1/startup"; %modinfo: shutdown_method xrl "$(mld.targetname)/common/0.1/shutdown"; %mandatory: $(@.targetname); targetname { %user-hidden: "XRL target name"; %help: short "XRL target name"; %set:; } disable { %help: short "Disable the MLD protocol"; %create:; %set: xrl "$(mld.targetname)/mld6igmp/0.1/enable_mld6igmp?enable:bool=`~$(@)`"; %delete: xrl "$(mld.targetname)/mld6igmp/0.1/enable_mld6igmp?enable:bool=`~$(DEFAULT)`"; } enabled { %deprecated: "Statement 'enabled: true/false' is replaced with 'disable: false/true'"; %help: short "Enable the MLD protocol"; %create:; %set: xrl "$(mld.targetname)/mld6igmp/0.1/enable_mld6igmp?enable:bool=$(@)"; } interface @ { %help: short "Configure MLD on a network interface"; %delete: xrl "$(mld.targetname)/mld6igmp/0.1/stop_vif?vif_name:txt=$(interface.@)"; vif @ { %help: short "Configure MLD on a virtual interface"; %activate: xrl "$(mld.targetname)/mld6igmp/0.1/start_vif?vif_name:txt=$(vif.@)"; %update: xrl "$(mld.targetname)/mld6igmp/0.1/start_vif?vif_name:txt=$(vif.@)"; %delete: xrl "$(mld.targetname)/mld6igmp/0.1/stop_vif?vif_name:txt=$(vif.@)"; disable { %help: short "Disable MLD on an interface"; %set: xrl "$(mld.targetname)/mld6igmp/0.1/enable_vif?vif_name:txt=$(vif.@)&enable:bool=`~$(@)`"; %delete: xrl "$(mld.targetname)/mld6igmp/0.1/enable_vif?vif_name:txt=$(vif.@)&enable:bool=`~$(DEFAULT)`"; } enabled { %deprecated: "Statement 'enabled: true/false' is replaced with 'disable: false/true'"; %help: short "Enable MLD on an interface"; %set: xrl "$(mld.targetname)/mld6igmp/0.1/enable_vif?vif_name:txt=$(vif.@)&enable:bool=$(@)"; %delete: xrl "$(mld.targetname)/mld6igmp/0.1/enable_vif?vif_name:txt=$(vif.@)&enable:bool=`~$(DEFAULT)`"; } version { %help: short "Set the MLD protocol version"; %allow-range: $(@) "1" "2" %help: "The MLD protocol version"; %create: xrl "$(mld.targetname)/mld6igmp/0.1/set_vif_proto_version?vif_name:txt=$(vif.@)&proto_version:u32=$(@)"; %set: xrl "$(mld.targetname)/mld6igmp/0.1/set_vif_proto_version?vif_name:txt=$(vif.@)&proto_version:u32=$(@)"; %delete: xrl "$(mld.targetname)/mld6igmp/0.1/set_vif_proto_version?vif_name:txt=$(vif.@)&proto_version:u32=$(DEFAULT)"; } enable-ip-router-alert-option-check { %help: short "Enable the IP Router Alert option check"; %create: xrl "$(mld.targetname)/mld6igmp/0.1/set_vif_ip_router_alert_option_check?vif_name:txt=$(vif.@)&enable:bool=$(@)"; %set: xrl "$(mld.targetname)/mld6igmp/0.1/set_vif_ip_router_alert_option_check?vif_name:txt=$(vif.@)&enable:bool=$(@)"; %delete: xrl "$(mld.targetname)/mld6igmp/0.1/set_vif_ip_router_alert_option_check?vif_name:txt=$(vif.@)&enable:bool=$(DEFAULT)"; } query-interval { %help: short "Set the query interval (in seconds)"; %allow-range: $(@) "1" "1024" %help: "The query interval (in seconds)"; %create: xrl "$(mld.targetname)/mld6igmp/0.1/set_vif_query_interval?vif_name:txt=$(vif.@)&interval_sec:u32=$(@)&interval_usec:u32=0"; %set: xrl "$(mld.targetname)/mld6igmp/0.1/set_vif_query_interval?vif_name:txt=$(vif.@)&interval_sec:u32=$(@)&interval_usec:u32=0"; %delete: xrl "$(mld.targetname)/mld6igmp/0.1/set_vif_query_interval?vif_name:txt=$(vif.@)&interval_sec:u32=$(DEFAULT)&interval_usec:u32=0"; } query-last-member-interval { %help: short "Set the last member query interval (in seconds)"; %allow-range: $(@) "1" "1024" %help: "The last member query interval (in seconds)"; %create: xrl "$(mld.targetname)/mld6igmp/0.1/set_vif_query_last_member_interval?vif_name:txt=$(vif.@)&interval_sec:u32=$(@)&interval_usec:u32=0"; %set: xrl "$(mld.targetname)/mld6igmp/0.1/set_vif_query_last_member_interval?vif_name:txt=$(vif.@)&interval_sec:u32=$(@)&interval_usec:u32=0"; %delete: xrl "$(mld.targetname)/mld6igmp/0.1/set_vif_query_last_member_interval?vif_name:txt=$(vif.@)&interval_sec:u32=$(DEFAULT)&interval_usec:u32=0"; } query-response-interval { %help: short "Set the query response interval (in seconds)"; %allow-range: $(@) "1" "1024" %help: "The query response interval (in seconds)"; %create: xrl "$(mld.targetname)/mld6igmp/0.1/set_vif_query_response_interval?vif_name:txt=$(vif.@)&interval_sec:u32=$(@)&interval_usec:u32=0"; %set: xrl "$(mld.targetname)/mld6igmp/0.1/set_vif_query_response_interval?vif_name:txt=$(vif.@)&interval_sec:u32=$(@)&interval_usec:u32=0"; %delete: xrl "$(mld.targetname)/mld6igmp/0.1/set_vif_query_response_interval?vif_name:txt=$(vif.@)&interval_sec:u32=$(DEFAULT)&interval_usec:u32=0"; } robust-count { %help: short "Set the robustness variable count"; %allow-range: $(@) "2" "10" %help: "The robustness variable count"; %create: xrl "$(mld.targetname)/mld6igmp/0.1/set_vif_robust_count?vif_name:txt=$(vif.@)&robust_count:u32=$(@)"; %set: xrl "$(mld.targetname)/mld6igmp/0.1/set_vif_robust_count?vif_name:txt=$(vif.@)&robust_count:u32=$(@)"; %delete: xrl "$(mld.targetname)/mld6igmp/0.1/set_vif_robust_count?vif_name:txt=$(vif.@)&robust_count:u32=$(DEFAULT)"; } } } traceoptions { %help: short "Configure the tracing options"; flag { %help: short "Configure the tracing operation to perform"; all { %help: short "Configure all tracing operations"; disable { %help: short "Enable all tracing operations"; %set: xrl "$(mld.targetname)/mld6igmp/0.1/log_trace_all?enable:bool=`~$(@)`"; %delete: xrl "$(mld.targetname)/mld6igmp/0.1/log_trace_all?enable:bool=$(DEFAULT)"; } enabled { %deprecated: "Statement 'enabled: true/false' is replaced with 'disable: false/true'"; %help: short "Enable all tracing operations"; %set: xrl "$(mld.targetname)/mld6igmp/0.1/log_trace_all?enable:bool=$(@)"; } } } } } } xorp/etc/templates/ospfv3.cmds0000664000076400007640000003506311421137511016524 0ustar greearbgreearb/* $XORP: xorp/etc/templates/ospfv3.cmds,v 1.6 2007/02/26 11:08:37 atanu Exp $ */ clear ospf6 database { %command: "ospf_clear_database -3" %help: HELP; %module: ospf6; %tag: HELP "Clear LSA database"; } show ospf6 { %command: "" %help: "Display information about OSPFv3"; %module: ospf6; } show ospf6 database { %command: "ospf_print_lsas -3 -b" %help: HELP; %module: ospf6; %tag: HELP "Show LSA database"; } show ospf6 database brief { %command: "ospf_print_lsas -3 -b" %help: HELP; %module: ospf6; %tag: HELP "Display brief output (default)"; } show ospf6 database detail { %command: "ospf_print_lsas -3 -d" %help: HELP; %module: ospf6; %tag: HELP "Display detailed output"; } /* Router-LSA */ show ospf6 database router { %command: "ospf_print_lsas -3 -b -f 0x2001" %help: HELP; %module: ospf6; %tag: HELP "Show Router-LSA database"; } show ospf6 database router brief { %command: "ospf_print_lsas -3 -b -f 0x2001" %help: HELP; %module: ospf6; %tag: HELP "Display brief output (default)"; } show ospf6 database router detail { %command: "ospf_print_lsas -3 -d -f 0x2001" %help: HELP; %module: ospf6; %tag: HELP "Display detailed output"; } /* Network-LSA */ show ospf6 database network { %command: "ospf_print_lsas -3 -b -f 0x2002" %help: HELP; %module: ospf6; %tag: HELP "Show Network-LSA database"; } show ospf6 database network brief { %command: "ospf_print_lsas -3 -b -f 0x2002" %help: HELP; %module: ospf6; %tag: HELP "Display brief output (default)"; } show ospf6 database network detail { %command: "ospf_print_lsas -3 -d -f 0x2002" %help: HELP; %module: ospf6; %tag: HELP "Display detailed output"; } /* Summary-LSA (network) */ show ospf6 database netsummary { %command: "ospf_print_lsas -3 -b -f 0x2003" %help: HELP; %module: ospf6; %tag: HELP "Show Summary-LSA (network) database"; } show ospf6 database netsummary brief { %command: "ospf_print_lsas -3 -b -f 0x2003" %help: HELP; %module: ospf6; %tag: HELP "Display brief output (default)"; } show ospf6 database netsummary detail { %command: "ospf_print_lsas -3 -d -f 0x2003" %help: HELP; %module: ospf6; %tag: HELP "Display detailed output"; } /* Summary-LSA (AS boundary router) */ show ospf6 database asbrsummary { %command: "ospf_print_lsas -3 -b -f 0x2004" %help: HELP; %module: ospf6; %tag: HELP "Show Summary-LSA (AS boundary router) database"; } show ospf6 database asbrsummary brief { %command: "ospf_print_lsas -3 -b -f 0x2004" %help: HELP; %module: ospf6; %tag: HELP "Display brief output (default)"; } show ospf6 database asbrsummary detail { %command: "ospf_print_lsas -3 -d -f 0x2004" %help: HELP; %module: ospf6; %tag: HELP "Display detailed output"; } /* AS-External-LSA */ show ospf6 database external { %command: "ospf_print_lsas -3 -b -f 0x4005" %help: HELP; %module: ospf6; %tag: HELP "Show External-LSA database"; } show ospf6 database external brief { %command: "ospf_print_lsas -3 -b -f 0x4005" %help: HELP; %module: ospf6; %tag: HELP "Display brief output (default)"; } show ospf6 database external detail { %command: "ospf_print_lsas -3 -d -f 0x4005" %help: HELP; %module: ospf6; %tag: HELP "Display detailed output"; } /* NSSA-LSA */ show ospf6 database nssa { %command: "ospf_print_lsas -3 -b -f 0x2007" %help: HELP; %module: ospf6; %tag: HELP "Show NSSA-LSA database"; } show ospf6 database nssa brief { %command: "ospf_print_lsas -3 -b -f 0x2007" %help: HELP; %module: ospf6; %tag: HELP "Display brief output (default)"; } show ospf6 database nssa detail { %command: "ospf_print_lsas -3 -d -f 0x2007" %help: HELP; %module: ospf6; %tag: HELP "Display detailed output"; } /* Link-LSA */ show ospf6 database link { %command: "ospf_print_lsas -3 -b -f 0x0008" %help: HELP; %module: ospf6; %tag: HELP "Show Link-LSA database"; } show ospf6 database link brief { %command: "ospf_print_lsas -3 -b -f 0x0008" %help: HELP; %module: ospf6; %tag: HELP "Display brief output (default)"; } show ospf6 database link detail { %command: "ospf_print_lsas -3 -d -f 0x0008" %help: HELP; %module: ospf6; %tag: HELP "Display detailed output"; } /* Intra-Area-Prefix-LSA */ show ospf6 database intra-area-prefix { %command: "ospf_print_lsas -3 -b -f 0x2009" %help: HELP; %module: ospf6; %tag: HELP "Show Intra-Area-Prefix-LSA database"; } show ospf6 database intra-area-prefix brief { %command: "ospf_print_lsas -3 -b -f 0x2009" %help: HELP; %module: ospf6; %tag: HELP "Display brief output (default)"; } show ospf6 database intra-area-prefix detail { %command: "ospf_print_lsas -3 -d -f 0x2009" %help: HELP; %module: ospf6; %tag: HELP "Display detailed output"; } /* AREA */ show ospf6 database area $(protocols.ospf6.*.area.*) { %command: "ospf_print_lsas -3 -a $5 -b" %help: HELP; %module: ospf6; %tag: HELP "Show LSA database"; } show ospf6 database area $(protocols.ospf6.*.area.*) brief { %command: "ospf_print_lsas -3 -a $5 -b" %help: HELP; %module: ospf6; %tag: HELP "Display brief output (default)"; } show ospf6 database area $(protocols.ospf6.*.area.*) detail { %command: "ospf_print_lsas -3 -a $5 -d" %help: HELP; %module: ospf6; %tag: HELP "Display detailed output"; } /* Router-LSA */ show ospf6 database area $(protocols.ospf6.*.area.*) router { %command: "ospf_print_lsas -3 -a $5 -b -f 0x2001" %help: HELP; %module: ospf6; %tag: HELP "Show Router-LSA database"; } show ospf6 database area $(protocols.ospf6.*.area.*) router brief { %command: "ospf_print_lsas -3 -a $5 -b -f 0x2001" %help: HELP; %module: ospf6; %tag: HELP "Display brief output (default)"; } show ospf6 database area $(protocols.ospf6.*.area.*) router detail { %command: "ospf_print_lsas -3 -a $5 -d -f 0x2001" %help: HELP; %module: ospf6; %tag: HELP "Display detailed output"; } /* Network-LSA */ show ospf6 database area $(protocols.ospf6.*.area.*) network { %command: "ospf_print_lsas -3 -a $5 -b -f 0x2002" %help: HELP; %module: ospf6; %tag: HELP "Show Network-LSA database"; } show ospf6 database area $(protocols.ospf6.*.area.*) network brief { %command: "ospf_print_lsas -3 -a $5 -b -f 0x2002" %help: HELP; %module: ospf6; %tag: HELP "Display brief output (default)"; } show ospf6 database area $(protocols.ospf6.*.area.*) network detail { %command: "ospf_print_lsas -3 -a $5 -d -f 0x2002" %help: HELP; %module: ospf6; %tag: HELP "Display detailed output"; } /* Summary-LSA (network) */ show ospf6 database area $(protocols.ospf6.*.area.*) netsummary { %command: "ospf_print_lsas -3 -a $5 -b -f 0x2003" %help: HELP; %module: ospf6; %tag: HELP "Show Summary-LSA (network) database"; } show ospf6 database area $(protocols.ospf6.*.area.*) netsummary brief { %command: "ospf_print_lsas -3 -a $5 -b -f 0x2003" %help: HELP; %module: ospf6; %tag: HELP "Display brief output (default)"; } show ospf6 database area $(protocols.ospf6.*.area.*) netsummary detail { %command: "ospf_print_lsas -3 -a $5 -d -f 0x2003" %help: HELP; %module: ospf6; %tag: HELP "Display detailed output"; } /* Summary-LSA (AS boundary router) */ show ospf6 database area $(protocols.ospf6.*.area.*) asbrsummary { %command: "ospf_print_lsas -3 -a $5 -b -f 0x2004" %help: HELP; %module: ospf6; %tag: HELP "Show Summary-LSA (AS boundary router) database"; } show ospf6 database area $(protocols.ospf6.*.area.*) asbrsummary brief { %command: "ospf_print_lsas -3 -a $5 -b -f 0x2004" %help: HELP; %module: ospf6; %tag: HELP "Display brief output (default)"; } show ospf6 database area $(protocols.ospf6.*.area.*) asbrsummary detail { %command: "ospf_print_lsas -3 -a $5 -d -f 0x2004" %help: HELP; %module: ospf6; %tag: HELP "Display detailed output"; } /* AS-External-LSA */ show ospf6 database area $(protocols.ospf6.*.area.*) external { %command: "ospf_print_lsas -3 -a $5 -b -f 0x4005" %help: HELP; %module: ospf6; %tag: HELP "Show External-LSA database"; } show ospf6 database area $(protocols.ospf6.*.area.*) external brief { %command: "ospf_print_lsas -3 -a $5 -b -f 0x4005" %help: HELP; %module: ospf6; %tag: HELP "Display brief output (default)"; } show ospf6 database area $(protocols.ospf6.*.area.*) external detail { %command: "ospf_print_lsas -3 -a $5 -d -f 0x4005" %help: HELP; %module: ospf6; %tag: HELP "Display detailed output"; } /* NSSA-LSA */ show ospf6 database area $(protocols.ospf6.*.area.*) nssa { %command: "ospf_print_lsas -3 -a $5 -b -f 0x2007" %help: HELP; %module: ospf6; %tag: HELP "Show NSSA-LSA database"; } show ospf6 database area $(protocols.ospf6.*.area.*) nssa brief { %command: "ospf_print_lsas -3 -a $5 -b -f 0x2007" %help: HELP; %module: ospf6; %tag: HELP "Display brief output (default)"; } show ospf6 database area $(protocols.ospf6.*.area.*) nssa detail { %command: "ospf_print_lsas -3 -a $5 -d -f 0x2007" %help: HELP; %module: ospf6; %tag: HELP "Display detailed output"; } /* Link-LSA */ show ospf6 database area $(protocols.ospf6.*.area.*) link { %command: "ospf_print_lsas -3 -a $5 -b -f 0x0008" %help: HELP; %module: ospf6; %tag: HELP "Show Link-LSA database"; } show ospf6 database area $(protocols.ospf6.*.area.*) link brief { %command: "ospf_print_lsas -3 -a $5 -b -f 0x0008" %help: HELP; %module: ospf6; %tag: HELP "Display brief output (default)"; } show ospf6 database area $(protocols.ospf6.*.area.*) link detail { %command: "ospf_print_lsas -3 -a $5 -d -f 0x0008" %help: HELP; %module: ospf6; %tag: HELP "Display detailed output"; } /* Intra-Area-Prefix-LSA */ show ospf6 database area $(protocols.ospf6.*.area.*) intra-area-prefix { %command: "ospf_print_lsas -3 -a $5 -b -f 0x2009" %help: HELP; %module: ospf6; %tag: HELP "Show Intra-Area-Prefix-LSA database"; } show ospf6 database area $(protocols.ospf6.*.area.*) intra-area-prefix brief { %command: "ospf_print_lsas -3 -a $5 -b -f 0x2009" %help: HELP; %module: ospf6; %tag: HELP "Display brief output (default)"; } show ospf6 database area $(protocols.ospf6.*.area.*) intra-area-prefix detail { %command: "ospf_print_lsas -3 -a $5 -d -f 0x2009" %help: HELP; %module: ospf6; %tag: HELP "Display detailed output"; } /* SUMMARY */ show ospf6 database summary { %command: "ospf_print_lsas -3 -s" %help: HELP; %module: ospf6; %tag: HELP "Display summary output"; } show ospf6 database summary router { %command: "ospf_print_lsas -3 -s -f 0x2001" %help: HELP; %module: ospf6; %tag: HELP "Show Router-LSA database"; } show ospf6 database summary network { %command: "ospf_print_lsas -3 -s -f 0x2002" %help: HELP; %module: ospf6; %tag: HELP "Show Network-LSA database"; } show ospf6 database summary netsummary { %command: "ospf_print_lsas -3 -s -f 0x2003" %help: HELP; %module: ospf6; %tag: HELP "Show Summary-LSA (network) database"; } show ospf6 database summary asbrsummary { %command: "ospf_print_lsas -3 -s -f 0x2004" %help: HELP; %module: ospf6; %tag: HELP "Show Summary-LSA (AS boundary router) database"; } show ospf6 database summary external { %command: "ospf_print_lsas -3 -s -f 0x4005" %help: HELP; %module: ospf6; %tag: HELP "Show External-LSA database"; } show ospf6 database summary nssa { %command: "ospf_print_lsas -3 -s -f 0x2007" %help: HELP; %module: ospf6; %tag: HELP "Show NSSA-LSA database"; } show ospf6 database summary link { %command: "ospf_print_lsas -3 -s -f 0x0008" %help: HELP; %module: ospf6; %tag: HELP "Show Link-LSA database"; } show ospf6 database summary intra-area-prefix { %command: "ospf_print_lsas -3 -s -f 0x2009" %help: HELP; %module: ospf6; %tag: HELP "Show Intra-Area-Prefix-LSA database"; } show ospf6 database summary area $(protocols.ospf6.*.area.*) { %command: "ospf_print_lsas -3 -a $6 -s" %help: HELP; %module: ospf6; %tag: HELP "Display summary output"; } show ospf6 database summary area $(protocols.ospf6.*.area.*) router { %command: "ospf_print_lsas -3 -a $6 -s -f 0x2001" %help: HELP; %module: ospf6; %tag: HELP "Show Router-LSA database"; } show ospf6 database summary area $(protocols.ospf6.*.area.*) network { %command: "ospf_print_lsas -3 -a $6 -s -f 0x2002" %help: HELP; %module: ospf6; %tag: HELP "Show Network-LSA database"; } show ospf6 database summary area $(protocols.ospf6.*.area.*) netsummary { %command: "ospf_print_lsas -3 -a $6 -s -f 0x2003" %help: HELP; %module: ospf6; %tag: HELP "Show Summary-LSA (network) database"; } show ospf6 database summary area $(protocols.ospf6.*.area.*) asbrsummary { %command: "ospf_print_lsas -3 -a $6 -s -f 0x2004" %help: HELP; %module: ospf6; %tag: HELP "Show Summary-LSA (AS boundary router) database"; } show ospf6 database summary area $(protocols.ospf6.*.area.*) external { %command: "ospf_print_lsas -3 -a $6 -s -f 0x4005" %help: HELP; %module: ospf6; %tag: HELP "Show External-LSA database"; } show ospf6 database summary area $(protocols.ospf6.*.area.*) nssa { %command: "ospf_print_lsas -3 -a $6 -s -f 0x2007" %help: HELP; %module: ospf6; %tag: HELP "Show NSSA-LSA database"; } show ospf6 database summary area $(protocols.ospf6.*.area.*) link { %command: "ospf_print_lsas -3 -a $6 -s -f 0x0008" %help: HELP; %module: ospf6; %tag: HELP "Show Link-LSA database"; } show ospf6 database summary area $(protocols.ospf6.*.area.*) intra-area-prefix{ %command: "ospf_print_lsas -3 -a $6 -s -f 0x2009" %help: HELP; %module: ospf6; %tag: HELP "Show Intra-Area-Prefix-LSA database"; } /* Neighbor commands */ show ospf6 neighbor { %command: "ospf_print_neighbours -3" %help: HELP; %module: ospf6; %tag: HELP "Show Neighbors"; } show ospf6 neighbor brief { %command: "ospf_print_neighbours -3 -b" %help: HELP; %module: ospf6; %tag: HELP "Show Neighbors"; } show ospf6 neighbor detail { %command: "ospf_print_neighbours -3 -d" %help: HELP; %module: ospf6; %tag: HELP "Show Neighbors"; } show ospf6 neighbor { %command: "ospf_print_neighbours -3 -f $4" %help: HELP; %module: ospf6; %tag: HELP "Show Neighbors"; } show ospf6 neighbor brief { %command: "ospf_print_neighbours -3 -b -f $4" %help: HELP; %module: ospf6; %tag: HELP "Show Neighbors"; } show ospf6 neighbor detail { %command: "ospf_print_neighbours -3 -d -f $4" %help: HELP; %module: ospf6; %tag: HELP "Show Neighbors"; } xorp/etc/templates/rib.tp0000664000076400007640000000267411421137511015557 0ustar greearbgreearb/* $XORP: xorp/etc/templates/rib.tp,v 1.19 2006/01/27 21:35:34 pavlin Exp $ */ plumbing { rib { targetname: txt = "rib"; } } policy { /* Nothing to declare */ } plumbing { rib { %help: short "Configure the Routing Information Base module"; %modinfo: provides rib; %modinfo: depends fea; %modinfo: path "xorp_rib"; %modinfo: default_targetname "rib"; %modinfo: startup_method xrl "$(rib.targetname)/common/0.1/startup"; %modinfo: status_method xrl "$(rib.targetname)/common/0.1/get_status->status:u32&reason:txt"; %modinfo: shutdown_method xrl "$(rib.targetname)/common/0.1/shutdown"; %mandatory: $(@.targetname); targetname { %user-hidden: "XRL target name"; %help: short "XRL target name"; %set:; } } } policy { %create: xrl "$(policy.targetname)/policy/0.1/set_proto_target?protocol:txt=connected&target:txt=rib"; /* * A temporary hack to allow import of directly connected routes. * This MUST be fixed at some other place (where?). */ %create: xrl "$(policy.targetname)/policy/0.1/add_varmap?protocol:txt=connected&variable:txt=DUMMY_DIRTY_HACK&type:txt=u32&access:txt=r&id:u32=9"; %create: xrl "$(policy.targetname)/policy/0.1/add_varmap?protocol:txt=connected&variable:txt=network4&type:txt=ipv4net&access:txt=r&id:u32=10"; %create: xrl "$(policy.targetname)/policy/0.1/add_varmap?protocol:txt=connected&variable:txt=network6&type:txt=ipv6net&access:txt=r&id:u32=12"; } xorp/etc/templates/ripng.tp0000664000076400007640000003423511421137511016120 0ustar greearbgreearb/* $XORP: xorp/etc/templates/ripng.tp,v 1.47 2008/10/29 21:59:38 andrewma Exp $ */ /* * RIPng attribute descriptions */ protocols { ripng { targetname: txt = "ripng"; traceoptions { flag { all { disable: toggle = false; } } } interface @: txt { vif @: txt { address @: ipv6 { metric: u32 = 1; horizon: txt = "split-horizon-poison-reverse"; disable: toggle = false; enabled: bool; /* %deprecated */ passive: bool = false; accept-non-rip-requests: bool = true; accept-default-route: bool = true; advertise-default-route: bool = true; route-timeout: u32 = 180; route-expiry-secs: u32; /* %deprecated */ deletion-delay: u32 = 120; route-deletion-secs: u32; /* %deprecated */ triggered-delay: u32 = 3; triggered-jitter: u32 = 66; update-interval: u32 = 30; update-jitter: u32 = 16; request-interval: u32 = 30; table-request-secs: u32; /* %deprecated */ interpacket-delay: u32 = 50; interpacket-delay-msecs: u32; /* %deprecated */ } } } import: txt; export: txt; } } policy { policy-statement @: txt { term @: txt { from { metric: u32range; } to { metric: u32range; } then { metric: u32; } } } } /* * RIPNG attribute manipulation. */ protocols { ripng { %modinfo: provides ripng; %modinfo: depends rib; %modinfo: depends policy; %modinfo: path "xorp_ripng"; %modinfo: default_targetname "ripng"; %modinfo: status_method xrl "$(ripng.targetname)/common/0.1/get_status->status:u32&reason:txt"; %modinfo: shutdown_method xrl "$(ripng.targetname)/common/0.1/shutdown"; %help: short "RIPng configuration"; %mandatory: $(@.targetname); targetname { %user-hidden: "XRL target name"; %help: short "XRL target name"; %set:; } traceoptions { %help: short "Configure the tracing options"; flag { %help: short "Configure the tracing operation to perform"; all { %help: short "Configure all tracing operations"; disable { %help: short "Disable all tracing operations"; %set: xrl "$(ripng.targetname)/ripng/0.1/trace?tvar:txt=all&enable:bool=`~$(@)`"; %delete: xrl "$(ripng.targetname)/ripng/0.1/trace?tvar:txt=all&enable:bool=$(DEFAULT)"; } } } } interface @ { %help: short "Interface in RIPng domain"; vif @ { %help: short "Vif in RIPng domain"; address @ { %help: short "Address on vif used for RIPng"; %create: xrl "$(ripng.targetname)/ripng/0.1/add_rip_address?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv6=$(address.@)"; %delete: xrl "$(ripng.targetname)/ripng/0.1/remove_rip_address?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv6=$(address.@)"; %activate: xrl "$(ripng.targetname)/ripng/0.1/set_rip_address_enabled?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv6=$(address.@)&enabled:bool=`~$(@.disable)`"; disable { %help: short "Disable RIPng on address"; %create:; %set: xrl "$(ripng.targetname)/ripng/0.1/set_rip_address_enabled?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv6=$(address.@)&enabled:bool=`~$(@)`"; } enabled { %deprecated: "Statement 'enabled: true/false' is replaced with 'disable: false/true'"; %set: xrl "$(ripng.targetname)/ripng/0.1/set_rip_address_enabled?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv6=$(address.@)&enabled:bool=$(@)"; %get: xrl "$(ripng.targetname)/ripng/0.1/rip_address_enabled?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv6=$(address.@)->enabled:bool"; %help: short "Enable RIPng on address"; } metric { %allow-range: $(@) "0" "16" %help: "Cost metric added to routes received on address"; %set: xrl "$(ripng.targetname)/ripng/0.1/set_cost?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv6=$(address.@)&cost:u32=$(@)"; %get: xrl "$(ripng.targetname)/ripng/0.1/cost?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv6=$(address.@)->cost:u32"; %help: short "Cost metric added to routes received on address"; } horizon { %allow: $(@) "none" %help: "No horizon type"; %allow: $(@) "split-horizon" %help: "Split horizon"; %allow: $(@) "split-horizon-poison-reverse" %help: "Split horizon with poisoned reverse"; %set: xrl "$(ripng.targetname)/ripng/0.1/set_horizon?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv6=$(address.@)&horizon:txt=$(@)"; %get: xrl "$(ripng.targetname)/ripng/0.1/horizon?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv6=$(address.@)->horizon:txt"; %help: short "Horizon type applied to announced routes"; } passive { %set: xrl "$(ripng.targetname)/ripng/0.1/set_passive?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv6=$(address.@)&passive:bool=$(@)"; %get: xrl "$(ripng.targetname)/ripng/0.1/passive?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv6=$(address.@)->passive:bool"; %help: short "Do not transmit RIPng packets"; } accept-non-rip-requests { %set: xrl "$(ripng.targetname)/ripng/0.1/set_accept_non_rip_requests?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv6=$(address.@)&accept:bool=$(@)"; %get: xrl "$(ripng.targetname)/ripng/0.1/accept_non_rip_requests?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv6=$(address.@)->accept:bool"; %help: short "Accept RIPng requests from non-RIPng port"; } accept-default-route { %set: xrl "$(ripng.targetname)/ripng/0.1/set_accept_default_route?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv6=$(address.@)&accept:bool=$(@)"; %get: xrl "$(ripng.targetname)/ripng/0.1/accept_default_route?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv6=$(address.@)->accept:bool"; %help: short "Accept default route from RIPng neighbor"; } advertise-default-route { %set: xrl "$(ripng.targetname)/ripng/0.1/set_advertise_default_route?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv6=$(address.@)&advertise:bool=$(@)"; %get: xrl "$(ripng.targetname)/ripng/0.1/advertise_default_route?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv6=$(address.@)->advertise:bool"; %help: short "Advertise default route to RIPng neighbors"; } route-timeout { %set: xrl "$(ripng.targetname)/ripng/0.1/set_route_timeout?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv6=$(address.@)&t_secs:u32=$(@)"; %get: xrl "$(ripng.targetname)/ripng/0.1/route_timeout?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv6=$(address.@)->t_secs:u32"; %help: short "Route expiration time (sec)"; } route-expiry-secs { %deprecated: "Statement 'route-expiry-secs' is replaced with 'route-timeout'"; %set: xrl "$(ripng.targetname)/ripng/0.1/set_route_timeout?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv6=$(address.@)&t_secs:u32=$(@)"; %get: xrl "$(ripng.targetname)/ripng/0.1/route_timeout?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv6=$(address.@)->t_secs:u32"; %help: short "Route expiration interval in the absence of updates"; } deletion-delay { %set: xrl "$(ripng.targetname)/ripng/0.1/set_deletion_delay?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv6=$(address.@)&t_secs:u32=$(@)"; %get: xrl "$(ripng.targetname)/ripng/0.1/deletion_delay?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv6=$(address.@)->t_secs:u32"; %help: short "Delay before expired routes are deleted (sec)"; } route-deletion-secs { %deprecated: "Statement 'route-deletion-secs' is replaced with 'deletion-delay'"; %set: xrl "$(ripng.targetname)/ripng/0.1/set_deletion_delay?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv6=$(address.@)&t_secs:u32=$(@)"; %get: xrl "$(ripng.targetname)/ripng/0.1/deletion_delay?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv6=$(address.@)->t_secs:u32"; %help: short "Route deletion interval after advertised as unreachable"; } triggered-delay { %set: xrl "$(ripng.targetname)/ripng/0.1/set_triggered_update_delay?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv6=$(address.@)&t_secs:u32=$(@)"; %get: xrl "$(ripng.targetname)/ripng/0.1/triggered_update_delay?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv6=$(address.@)->t_secs:u32"; %help: short "Delay before sending repeated triggered update (sec)"; } triggered-jitter { %allow-range: $(@) "0" "100" %help: "Jitter as percents of 'triggered-delay'"; %set: xrl "$(ripng.targetname)/ripng/0.1/set_triggered_update_jitter?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv6=$(address.@)&t_jitter:u32=$(@)"; %get: xrl "$(ripng.targetname)/ripng/0.1/triggered_update_jitter?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv6=$(address.@)->t_jitter:u32"; %help: short "Jitter of repeated triggered update delay (percents)"; } update-interval { %set: xrl "$(ripng.targetname)/ripng/0.1/set_update_interval?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv6=$(address.@)&t_secs:u32=$(@)"; %get: xrl "$(ripng.targetname)/ripng/0.1/update_interval?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv6=$(address.@)->t_secs:u32"; %help: short "Interval between regular route updates (sec)"; } update-jitter { %allow-range: $(@) "0" "100" %help: "Jitter as percents of 'update-interval'"; %set: xrl "$(ripng.targetname)/ripng/0.1/set_update_jitter?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv6=$(address.@)&t_jitter:u32=$(@)"; %get: xrl "$(ripng.targetname)/ripng/0.1/update_jitter?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv6=$(address.@)->t_jitter:u32"; %help: short "Jitter of regular route update interval (percents)"; } request-interval { %allow-range: $(@) "1" "10000" %help: "RIPng request interval when no known neighbors (sec)"; %allow-range: $(@) "0" "0" %help: "Disable periodic RIPng requests"; %set: xrl "$(ripng.targetname)/ripng/0.1/set_request_interval?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv6=$(address.@)&t_secs:u32=$(@)"; %get: xrl "$(ripng.targetname)/ripng/0.1/request_interval?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv6=$(address.@)->t_secs:u32"; %help: short "RIPng request interval when no known neighbors (sec)"; } table-request-secs { %deprecated: "Statement 'table-request-secs' is replaced with 'request-interval'"; %allow-range: $(@) "1" "10000" %help: "RIPng request interval when no known neighbors"; %allow-range: $(@) "0" "0" %help: "Disable periodic RIPng request packets when no known neighbors"; %set: xrl "$(ripng.targetname)/ripng/0.1/set_request_interval?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv6=$(address.@)&t_secs:u32=$(@)"; %get: xrl "$(ripng.targetname)/ripng/0.1/request_interval?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv6=$(address.@)->t_secs:u32"; %help: short "RIPng request interval when no known neighbors"; } interpacket-delay { %set: xrl "$(ripng.targetname)/ripng/0.1/set_interpacket_delay?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv6=$(address.@)&t_msecs:u32=$(@)"; %get: xrl "$(ripng.targetname)/ripng/0.1/interpacket_delay?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv6=$(address.@)->t_msecs:u32"; %help: short "Minimum delay between outbound RIPng packets (msec)"; } interpacket-delay-msecs { %deprecated: "Statement 'interpacket-delay-msecs' is replaced with 'interpacket-delay'"; %set: xrl "$(ripng.targetname)/ripng/0.1/set_interpacket_delay?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv6=$(address.@)&t_msecs:u32=$(@)"; %get: xrl "$(ripng.targetname)/ripng/0.1/interpacket_delay?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv6=$(address.@)->t_msecs:u32"; %help: short "Minimum delay between outbound RIP packets"; } } } } import { %help: short "Import policy name(s)"; %delete: xrl "policy/policy/0.1/import?protocol:txt=$(ripng.targetname)&policies:txt=&modifier:txt="; %set: xrl "policy/policy/0.1/import?protocol:txt=$(ripng.targetname)&policies:txt=$(@)&modifier:txt="; } export { %help: short "Export policy name(s)"; %delete: xrl "policy/policy/0.1/export?protocol:txt=$(ripng.targetname)&policies:txt=&modifier:txt="; %set: xrl "policy/policy/0.1/export?protocol:txt=$(ripng.targetname)&policies:txt=$(@)&modifier:txt="; } } } policy { %create: xrl "$(policy.targetname)/policy/0.1/add_varmap?protocol:txt=ripng&variable:txt=network6&type:txt=ipv6net&access:txt=r&id:u32=12"; %create: xrl "$(policy.targetname)/policy/0.1/add_varmap?protocol:txt=ripng&variable:txt=nexthop6&type:txt=ipv6nexthop&access:txt=rw&id:u32=13"; %create: xrl "$(policy.targetname)/policy/0.1/add_varmap?protocol:txt=ripng&variable:txt=metric&type:txt=u32&access:txt=rw&id:u32=14"; policy-statement @: txt { term @: txt { from { metric { %help: short "Metric value"; %allow-operator: ":" "==" "!=" "<" ">" "<=" ">="; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt=metric $(<>) $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt="; } } to { metric { %help: short "Metric value"; %allow-operator: ":" "==" "!=" "<" ">" "<=" ">="; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=1&order:txt=$(#)&statement:txt=metric $(<>) $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=1&order:txt=$(#)&statement:txt="; } } then { metric { %help: short "Set the metric value"; %allow-operator: ":" "=" "sub" "add"; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=2&order:txt=$(#)&statement:txt=metric $(<>) $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=2&order:txt=$(#)&statement:txt="; } } } } } xorp/etc/templates/plumbing.tp0000664000076400007640000000011411421137511016603 0ustar greearbgreearb/* $XORP$ */ plumbing { %help: short "Configure plumbing modules"; } xorp/etc/templates/fea.tp.raw0000664000076400007640000002522611421137511016324 0ustar greearbgreearb/* $XORP: xorp/etc/templates/fea.tp,v 1.40 2007/08/09 07:03:23 pavlin Exp $ */ fea { targetname: txt = "fea"; unicast-forwarding4 { disable: toggle = false; table-id: u32; forwarding-entries { retain-on-startup: bool = false; retain-on-shutdown: bool = false; } } enable-unicast-forwarding4: bool; /* %deprecated */ unicast-forwarding6 { disable: toggle = false; table-id: u32; forwarding-entries { retain-on-startup: bool = false; retain-on-shutdown: bool = false; } } enable-unicast-forwarding6: bool; /* %deprecated */ click { disable: toggle = false; enabled: bool; /* %deprecated */ duplicate-routes-to-kernel: bool = false; kernel-click { disable: bool = false; enabled: bool; /* %deprecated */ install-on-startup: bool = false; kernel-click-modules: txt = "/usr/local/click/linuxmodule/proclikefs.o:/usr/local/click/linuxmodule/click.o"; mount-directory: txt = "/click"; kernel-click-config-generator-file: txt = "/usr/local/xorp/fea/xorp_fea_click_config_generator"; } user-click { disable: bool = false; enabled: bool; /* %deprecated */ command-file: txt = "/usr/local/bin/click"; command-extra-arguments: txt = "-R"; command-execute-on-startup: bool = true; control-address: ipv4 = 127.0.0.1; control-socket-port: u32 = 13000; startup-config-file: txt = "/dev/null"; user-click-config-generator-file: txt = "/usr/local/xorp/fea/xorp_fea_click_config_generator"; } } } fea { %help: short "Configure the Forwarding Engine Abstraction"; %modinfo: provides fea; %modinfo: depends firewall; %modinfo: path "xorp_fea"; %modinfo: default_targetname "fea"; %modinfo: status_method xrl "$(fea.targetname)/common/0.1/get_status->status:u32&reason:txt"; %modinfo: startup_method xrl "$(fea.targetname)/fea_firewall/0.1/startup_firewall"; %mandatory: $(@.targetname); targetname { %user-hidden: "XRL target name"; %help: short "XRL target name"; %set:; } unicast-forwarding4 { %help: short "Configure IPv4 unicast forwarding"; disable { %help: short "Disable IPv4 unicast forwarding"; %set: xrl "$(fea.targetname)/fti/0.2/set_unicast_forwarding_enabled4?enabled:bool=`~$(@)`"; %delete: xrl "$(fea.targetname)/fti/0.2/set_unicast_forwarding_enabled4?enabled:bool=`~$(DEFAULT)`"; } table-id { %help: short "Configure IPv4 unicast forwarding table ID"; %set: xrl "$(fea.targetname)/fti/0.2/set_unicast_forwarding_table_id4?is_configured:bool=true&table_id:u32=$(@)"; %delete: xrl "$(fea.targetname)/fti/0.2/set_unicast_forwarding_table_id4?is_configured:bool=false&table_id:u32=0"; } forwarding-entries { %help: short "Configure IPv4 forwarding entries properties"; retain-on-startup { %help: short "Retain IPv4 unicast forwarding entries on startup"; %set: xrl "$(fea.targetname)/fti/0.2/set_unicast_forwarding_entries_retain_on_startup4?retain:bool=$(@)"; %delete: xrl "$(fea.targetname)/fti/0.2/set_unicast_forwarding_entries_retain_on_startup4?retain:bool=$(DEFAULT)"; } retain-on-shutdown { %help: short "Retain IPv4 unicast forwarding entries on shutdown"; %set: xrl "$(fea.targetname)/fti/0.2/set_unicast_forwarding_entries_retain_on_shutdown4?retain:bool=$(@)"; %delete: xrl "$(fea.targetname)/fti/0.2/set_unicast_forwarding_entries_retain_on_shutdown4?retain:bool=$(DEFAULT)"; } } } enable-unicast-forwarding4 { %deprecated: "Statement 'enable-unicast-forwarding4: true/false' is replaced with 'unicast-forwarding4 { disable: false/true }'"; %help: short "Enable IPv4 unicast forwarding"; %set: xrl "$(fea.targetname)/fti/0.2/set_unicast_forwarding_enabled4?enabled:bool=$(@)"; %delete: xrl "$(fea.targetname)/fti/0.2/set_unicast_forwarding_enabled4?enabled:bool=`~$(DEFAULT)`"; } unicast-forwarding6 { %help: short "Configure IPv6 unicast forwarding"; disable { %help: short "Disable IPv6 unicast forwarding"; %set: xrl "$(fea.targetname)/fti/0.2/set_unicast_forwarding_enabled6?enabled:bool=`~$(@)`"; %delete: xrl "$(fea.targetname)/fti/0.2/set_unicast_forwarding_enabled6?enabled:bool=`~$(DEFAULT)`"; } table-id { %help: short "Configure IPv6 unicast forwarding table ID"; %set: xrl "$(fea.targetname)/fti/0.2/set_unicast_forwarding_table_id6?is_configured:bool=true&table_id:u32=$(@)"; %delete: xrl "$(fea.targetname)/fti/0.2/set_unicast_forwarding_table_id6?is_configured:bool=false&table_id:u32=0"; } forwarding-entries { %help: short "Configure IPv6 forwarding entries properties"; retain-on-startup { %help: short "Retain IPv6 unicast forwarding entries on startup"; %set: xrl "$(fea.targetname)/fti/0.2/set_unicast_forwarding_entries_retain_on_startup6?retain:bool=$(@)"; %delete: xrl "$(fea.targetname)/fti/0.2/set_unicast_forwarding_entries_retain_on_startup6?retain:bool=$(DEFAULT)"; } retain-on-shutdown { %help: short "Retain IPv6 unicast forwarding entries on shutdown"; %set: xrl "$(fea.targetname)/fti/0.2/set_unicast_forwarding_entries_retain_on_shutdown6?retain:bool=$(@)"; %delete: xrl "$(fea.targetname)/fti/0.2/set_unicast_forwarding_entries_retain_on_shutdown6?retain:bool=$(DEFAULT)"; } } } enable-unicast-forwarding6 { %deprecated: "Statement 'enable-unicast-forwarding6: true/false' is replaced with 'unicast-forwarding6 { disable: false/true }'"; %help: short "Enable IPv6 unicast forwarding"; %set: xrl "$(fea.targetname)/fti/0.2/set_unicast_forwarding_enabled6?enabled:bool=$(@)"; %delete: xrl "$(fea.targetname)/fti/0.2/set_unicast_forwarding_enabled6?enabled:bool=`~$(DEFAULT)`"; } click { %help: short "Configure Click FEA support"; %create: xrl "$(fea.targetname)/fea_click/0.1/load_click"; %activate: xrl "$(fea.targetname)/fea_click/0.1/start_click"; %delete: xrl "$(fea.targetname)/fea_click/0.1/unload_click"; disable { %help: short "Disable Click FEA support"; %set: xrl "$(fea.targetname)/fea_click/0.1/enable_click?enable:bool=`~$(@)`"; %delete: xrl "$(fea.targetname)/fea_click/0.1/enable_click?enable:bool=`~$(DEFAULT)`"; } enabled { %deprecated: "Statement 'enabled: true/false' is replaced with 'disable: false/true'"; %help: short "Enable Click FEA support"; %set: xrl "$(fea.targetname)/fea_click/0.1/enable_click?enable:bool=$(@)"; } duplicate-routes-to-kernel { %help: short "Enable duplicating the Click routes to the system kernel"; %set: xrl "$(fea.targetname)/fea_click/0.1/enable_duplicate_routes_to_kernel?enable:bool=$(@)"; %delete: xrl "$(fea.targetname)/fea_click/0.1/enable_duplicate_routes_to_kernel?enable:bool=$(DEFAULT)"; } kernel-click { disable { %help: short "Disable kernel-level Click FEA support"; %set: xrl "$(fea.targetname)/fea_click/0.1/enable_kernel_click?enable:bool=`~$(@)`"; %delete: xrl "$(fea.targetname)/fea_click/0.1/enable_kernel_click?enable:bool=`~$(DEFAULT)`"; } enabled { %deprecated: "Statement 'enabled: true/false' is replaced with 'disable: false/true'"; %help: short "Enable kernel-level Click FEA support"; %set: xrl "$(fea.targetname)/fea_click/0.1/enable_kernel_click?enable:bool=$(@)"; } install-on-startup { %help: short "Enable installing kernel-level Click on startup"; %set: xrl "$(fea.targetname)/fea_click/0.1/enable_kernel_click_install_on_startup?enable:bool=$(@)"; %delete: xrl "$(fea.targetname)/fea_click/0.1/enable_kernel_click_install_on_startup?enable:bool=$(DEFAULT)"; } kernel-click-modules { %help: short "Specify the list of kernel Click modules (separated by colon) to load on startup (if installing kernel-level Click on startup is enabled)"; %set: xrl "$(fea.targetname)/fea_click/0.1/set_kernel_click_modules?modules:txt=$(@)"; %delete: xrl "$(fea.targetname)/fea_click/0.1/set_kernel_click_modules?modules:txt=$(DEFAULT)"; } mount-directory { %help: short "Specify the kernel-level Click mount directory"; %set: xrl "$(fea.targetname)/fea_click/0.1/set_kernel_click_mount_directory?directory:txt=$(@)"; %delete: xrl "$(fea.targetname)/fea_click/0.1/set_kernel_click_mount_directory?directory:txt=$(DEFAULT)"; } kernel-click-config-generator-file { %help: short "Specify the external program to generate the kernel-level Click configuration"; %set: xrl "$(fea.targetname)/fea_click/0.1/set_kernel_click_config_generator_file?kernel_click_config_generator_file:txt=$(@)"; } } user-click { disable { %help: short "Disable user-level Click FEA support"; %set: xrl "$(fea.targetname)/fea_click/0.1/enable_user_click?enable:bool=`~$(@)`"; %delete: xrl "$(fea.targetname)/fea_click/0.1/enable_user_click?enable:bool=`~$(DEFAULT)`"; } enabled { %deprecated: "Statement 'enabled: true/false' is replaced with 'disable: false/true'"; %help: short "Enable user-level Click FEA support"; %set: xrl "$(fea.targetname)/fea_click/0.1/enable_user_click?enable:bool=$(@)"; } command-file { %help: short "Specify the user-level Click command file"; %set: xrl "$(fea.targetname)/fea_click/0.1/set_user_click_command_file?user_click_command_file:txt=$(@)"; } command-extra-arguments { %help: short "Specify the extra arguments to the user-level Click command"; %set: xrl "$(fea.targetname)/fea_click/0.1/set_user_click_command_extra_arguments?user_click_command_extra_arguments:txt=$(@)"; } command-execute-on-startup { %help: short "Specify whether to execute on startup the user-level Click command"; %set: xrl "$(fea.targetname)/fea_click/0.1/set_user_click_command_execute_on_startup?user_click_command_execute_on_startup:bool=$(@)"; } control-address { %help: short "Specify the address to use for control access to the user-level Click"; %set: xrl "$(fea.targetname)/fea_click/0.1/set_user_click_control_address?user_click_control_address:ipv4=$(@)"; } control-socket-port { %help: short "Specify the socket port to use for control access to the user-level Click"; %allow-range: $(@) "1" "65535" %help: "The control socket port"; %set: xrl "$(fea.targetname)/fea_click/0.1/set_user_click_control_socket_port?user_click_control_socket_port:u32=$(@)"; } startup-config-file { %help: short "Specify the configuration file to be used by user-level Click on startup"; %set: xrl "$(fea.targetname)/fea_click/0.1/set_user_click_startup_config_file?user_click_startup_config_file:txt=$(@)"; } user-click-config-generator-file { %help: short "Specify the external program to generate the user-level Click configuration"; %set: xrl "$(fea.targetname)/fea_click/0.1/set_user_click_config_generator_file?user_click_config_generator_file:txt=$(@)"; } } } } xorp/etc/templates/vrrp.tp0000664000076400007640000000626111421137511015770 0ustar greearbgreearb/* $XORP: xorp/etc/templates/vrrp.tp,v 1.1 2008/10/09 17:40:58 abittau Exp $ */ protocols { vrrp { targetname: txt = "vrrp"; interface @: txt { vif @: txt { vrid @: u32 { priority: u32 = 100; interval: u32 = 1; preempt: bool = true; ip @: ipv4 { prefix-length: u32 = 24; } disable: bool = false; } } } } } protocols { vrrp { %help: short "Configure VRRP"; %modinfo: provides vrrp; %modinfo: depends rib; %modinfo: depends fea; %modinfo: path "xorp_vrrp"; %modinfo: default_targetname "vrrp"; %modinfo: status_method xrl "$(vrrp.targetname)/common/0.1/get_status->status:u32&reason:txt"; %modinfo: shutdown_method xrl "$(vrrp.targetname)/common/0.1/shutdown"; %mandatory: $(@.targetname); targetname { %user-hidden: "XRL target name"; %set:; } interface @ { %help: short "Physical interface"; vif @ { %help: short "Logical interface"; vrid @ { %help: short "Virtual router ID"; %create: xrl "$(vrrp.targetname)/vrrp/0.1/add_vrid?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&vrid:u32=$(vrid.@)"; %delete: xrl "$(vrrp.targetname)/vrrp/0.1/delete_vrid?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&vrid:u32=$(vrid.@)"; %activate: xrl "$(vrrp.targetname)/vrrp/0.1/set_disable?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&vrid:u32=$(vrid.@)&disable:bool=$(@.disable)"; disable { %help: short "Disable this VRRP instance"; %set: xrl "$(vrrp.targetname)/vrrp/0.1/set_disable?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&vrid:u32=$(vrid.@)&disable:bool=$(@)"; } priority { %help: short "The priority of the router when deciding to become master"; %allow-range: $(@) "1" "254" %help: "Router priority"; %set: xrl "$(vrrp.targetname)/vrrp/0.1/set_priority?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&vrid:u32=$(vrid.@)&priority:u32=$(@)"; } interval { %help: short "Interval in seconds between VRRP advertisements"; %set: xrl "$(vrrp.targetname)/vrrp/0.1/set_interval?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&vrid:u32=$(vrid.@)&interval:u32=$(@)"; } preempt { %help: short "Whether a higher priority backup preempts a lower priority master"; %set: xrl "$(vrrp.targetname)/vrrp/0.1/set_preempt?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&vrid:u32=$(vrid.@)&preempt:bool=$(@)"; } ip @ { %help: short "IP associated with virtual router"; %create: xrl "$(vrrp.targetname)/vrrp/0.1/add_ip?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&vrid:u32=$(vrid.@)&ip:ipv4=$(ip.@)"; %delete: xrl "$(vrrp.targetname)/vrrp/0.1/delete_ip?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&vrid:u32=$(vrid.@)&ip:ipv4=$(ip.@)"; prefix-length { %help: short "Assign the virtual-router network prefix length"; %allow-range: $(@) "1" "32" %help: "The virtual-router network prefix length"; %set: xrl "$(vrrp.targetname)/vrrp/0.1/set_prefix?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&vrid:u32=$(vrid.@)&ip:ipv4=$(ip.@)&prefix_len:u32=$(@)"; } } } } } } } xorp/etc/templates/bgp.cmds0000664000076400007640000000713611421137511016054 0ustar greearbgreearb/* $XORP: xorp/etc/templates/bgp.cmds,v 1.12 2004/06/11 03:48:18 atanu Exp $ */ show bgp { %command: "" %help: HELP; %module: bgp; %tag: HELP "Display information about BGP"; } show bgp peers { %command: "bgp_print_peers $0" %help: HELP; %module: bgp; %tag: HELP "Show BGP peers info"; } show bgp peers detail { %command: "bgp_print_peers $0" %help: HELP; %module: bgp; %tag: HELP "Show detailed BGP peers info"; } show bgp routes { %command: "bgp_print_routes $0" %help: HELP; %module: bgp; %tag: HELP "Print BGP routes"; } show bgp routes detail { %command: "bgp_print_routes $0" %help: HELP; %module: bgp; %tag: HELP "Print detailed BGP routes"; } show bgp routes summary { %command: "bgp_print_routes $0" %help: HELP; %module: bgp; %tag: HELP "Print summary of BGP routes"; } show bgp routes ipv4 { %command: "bgp_print_routes $0" %help: HELP; %module: bgp; %tag: HELP "Print BGP IPv4 routes"; } show bgp routes ipv4 detail { %command: "bgp_print_routes $0" %help: HELP; %module: bgp; %tag: HELP "Print detailed BGP IPv4 routes"; } show bgp routes ipv4 summary { %command: "bgp_print_routes $0" %help: HELP; %module: bgp; %tag: HELP "Print summary of BGP IPv4 routes"; } show bgp routes ipv4 unicast { %command: "bgp_print_routes $0" %help: HELP; %module: bgp; %tag: HELP "Print BGP IPv4 unicast routes"; } show bgp routes ipv4 unicast detail { %command: "bgp_print_routes $0" %help: HELP; %module: bgp; %tag: HELP "Print detailed BGP IPv4 unicast routes"; } show bgp routes ipv4 unicast summary { %command: "bgp_print_routes $0" %help: HELP; %module: bgp; %tag: HELP "Print summary of BGP IPv4 unicast routes"; } show bgp routes ipv4 multicast { %command: "bgp_print_routes $0" %help: HELP; %module: bgp; %tag: HELP "Print BGP IPv4 multicast routes"; } show bgp routes ipv4 multicast detail { %command: "bgp_print_routes $0" %help: HELP; %module: bgp; %tag: HELP "Print detailed BGP IPv4 multicast routes"; } show bgp routes ipv4 multicast summary { %command: "bgp_print_routes $0" %help: HELP; %module: bgp; %tag: HELP "Print summary of BGP IPv4 multicast routes"; } show bgp routes ipv6 { %command: "bgp_print_routes $0" %help: HELP; %module: bgp; %tag: HELP "Print BGP IPv6 routes"; } show bgp routes ipv6 detail { %command: "bgp_print_routes $0" %help: HELP; %module: bgp; %tag: HELP "Print detailed BGP IPv6 routes"; } show bgp routes ipv6 summary { %command: "bgp_print_routes $0" %help: HELP; %module: bgp; %tag: HELP "Print summary of BGP IPv6 routes"; } show bgp routes ipv6 unicast { %command: "bgp_print_routes $0" %help: HELP; %module: bgp; %tag: HELP "Print BGP IPv6 unicast routes"; } show bgp routes ipv6 unicast detail { %command: "bgp_print_routes $0" %help: HELP; %module: bgp; %tag: HELP "Print detailed BGP IPv6 unicast routes"; } show bgp routes ipv6 unicast summary { %command: "bgp_print_routes $0" %help: HELP; %module: bgp; %tag: HELP "Print summary of BGP IPv6 unicast routes"; } show bgp routes ipv6 multicast { %command: "bgp_print_routes $0" %help: HELP; %module: bgp; %tag: HELP "Print BGP IPv6 multicast routes"; } show bgp routes ipv6 multicast detail { %command: "bgp_print_routes $0" %help: HELP; %module: bgp; %tag: HELP "Print detailed BGP IPv6 multicast routes"; } show bgp routes ipv6 multicast summary { %command: "bgp_print_routes $0" %help: HELP; %module: bgp; %tag: HELP "Print summary of BGP IPv6 multicast routes"; } xorp/etc/templates/bgp.tp0000664000076400007640000011003011421137511015535 0ustar greearbgreearb/* $XORP: xorp/etc/templates/bgp.tp,v 1.99 2008/08/06 08:23:24 abittau Exp $ */ protocols { bgp { targetname: txt = "bgp"; bgp-id: ipv4; local-as: txt; enable-4byte-as-numbers: bool = false; route-reflector { cluster-id: ipv4; disable: bool = false; } confederation { identifier: u32; disable: bool = false; } damping { half-life: u32 = 15; max-suppress: u32 = 60; reuse: u32 = 750; suppress: u32 = 3000; disable: bool = false; } peer @: txt { peer-port: u32 = 179; local-port: u32 = 179; local-ip: txt; local-dev: txt = ""; as: txt; next-hop: ipv4; next-hop6: ipv6; holdtime: u32 = 90; delay-open-time: u32 = 0; client: bool = false; confederation-member: bool = false; prefix-limit { maximum: u32 = 250000; disable: bool = false; } disable: bool = false; enabled: bool; /* %deprecated */ ipv4-unicast: bool = true; ipv4-multicast: bool = false; ipv6-unicast: bool = false; ipv6-multicast: bool = false; /* md5-password: txt = "";*/ import: txt = ""; export: txt = ""; } /* network4 @: ipv4net { next-hop:ipv4; unicast:bool = true; multicast:bool = false; } network6 @: ipv6net { next-hop:ipv6; unicast:bool = true; multicast:bool = false; } */ traceoptions { flag { verbose { disable: toggle = false; } all { disable: toggle = false; } message-in { disable: toggle = false; } message-out { disable: toggle = false; } state-change { disable: toggle = false; } policy-configuration { disable: toggle = false; } } } import: txt = ""; export: txt = ""; } } policy { policy-statement @: txt { term @: txt { from { as-path: txt; as-path-list: txt; community: txt; community-list: txt; neighbor: ipv4range; origin: u32; med: u32range; localpref: u32range; } to { as-path: txt; as-path-list: txt; community: txt; neighbor: ipv4range; origin: u32; med: u32range; localpref: u32range; was-aggregated: bool; } then { as-path-prepend: u32; as-path-expand: u32; community: txt; community-add: txt; community-del: txt; origin: u32; med: u32; med-remove: bool; localpref: u32; aggregate-prefix-len: u32; aggregate-brief-mode: bool; } } } community-list @: txt { elements: txt; } as-path-list @: txt { elements: txt; } } protocols { bgp { %modinfo: provides bgp; %modinfo: depends rib; %modinfo: depends policy; %modinfo: path "xorp_bgp"; %modinfo: default_targetname "bgp"; %modinfo: status_method xrl "$(bgp.targetname)/common/0.1/get_status->status:u32&reason:txt"; %modinfo: shutdown_method xrl "$(bgp.targetname)/common/0.1/shutdown"; %mandatory: $(@.bgp-id); %mandatory: $(@.local-as); %mandatory: $(@.targetname); targetname { %user-hidden: "XRL target name"; %help: short "XRL target name"; %set:; } %create: xrl "$(policy.targetname)/policy/0.1/add_varmap?protocol:txt=bgp&variable:txt=network4&type:txt=ipv4net&access:txt=r&id:u32=10"; %create: xrl "$(policy.targetname)/policy/0.1/add_varmap?protocol:txt=bgp&variable:txt=network6&type:txt=ipv6net&access:txt=r&id:u32=12"; %create: xrl "$(policy.targetname)/policy/0.1/add_varmap?protocol:txt=bgp&variable:txt=nexthop4&type:txt=ipv4nexthop&access:txt=rw&id:u32=11"; %create: xrl "$(policy.targetname)/policy/0.1/add_varmap?protocol:txt=bgp&variable:txt=nexthop6&type:txt=ipv6nexthop&access:txt=rw&id:u32=13"; %create: xrl "$(policy.targetname)/policy/0.1/add_varmap?protocol:txt=bgp&variable:txt=aspath&type:txt=aspath&access:txt=rw&id:u32=14"; %create: xrl "$(policy.targetname)/policy/0.1/add_varmap?protocol:txt=bgp&variable:txt=community&type:txt=set_com32&access:txt=rw&id:u32=18"; %create: xrl "$(policy.targetname)/policy/0.1/add_varmap?protocol:txt=bgp&variable:txt=neighbor&type:txt=ipv4&access:txt=r&id:u32=16"; %create: xrl "$(policy.targetname)/policy/0.1/add_varmap?protocol:txt=bgp&variable:txt=origin&type:txt=u32&access:txt=rw&id:u32=15"; %create: xrl "$(policy.targetname)/policy/0.1/add_varmap?protocol:txt=bgp&variable:txt=med&type:txt=u32&access:txt=rw&id:u32=19"; %create: xrl "$(policy.targetname)/policy/0.1/add_varmap?protocol:txt=bgp&variable:txt=med_remove&type:txt=bool&access:txt=rw&id:u32=20"; %create: xrl "$(policy.targetname)/policy/0.1/add_varmap?protocol:txt=bgp&variable:txt=localpref&type:txt=u32&access:txt=rw&id:u32=17"; %create: xrl "$(policy.targetname)/policy/0.1/add_varmap?protocol:txt=bgp&variable:txt=aggregate_prefix_len&type:txt=u32&access:txt=w&id:u32=21"; %create: xrl "$(policy.targetname)/policy/0.1/add_varmap?protocol:txt=bgp&variable:txt=aggregate_brief_mode&type:txt=bool&access:txt=w&id:u32=22"; %create: xrl "$(policy.targetname)/policy/0.1/add_varmap?protocol:txt=bgp&variable:txt=was_aggregated&type:txt=bool&access:txt=r&id:u32=23"; bgp-id { %set: xrl "$(bgp.targetname)/bgp/0.3/set_bgp_id?id:ipv4=$(@)"; %get: xrl "$(bgp.targetname)/bgp/0.3/get_bgp_id->id:ipv4"; } local-as { /* we'd like to specify this as an int, but 4 byte AS numbers are specified as . so this has to be text */ /* %allow-range: $(@) "1" "65535" ; */ %help: short "Local AS number: or ."; %set: xrl "$(bgp.targetname)/bgp/0.3/set_local_as?as:txt=$(@)"; %get: xrl "$(bgp.targetname)/bgp/0.3/get_local_as->as:txt"; } enable-4byte-as-numbers { %help: short "Enable extended AS numbers"; %set: xrl "$(bgp.targetname)/bgp/0.3/set_4byte_as_support?enable:bool=$(@)"; } route-reflector { %mandatory: $(@.cluster-id); %create: xrl "$(bgp.targetname)/bgp/0.3/set_cluster_id?cluster_id:ipv4=$(@.cluster-id)&disable:bool=$(@.disable)"; %activate: xrl "$(bgp.targetname)/bgp/0.3/set_cluster_id?cluster_id:ipv4=$(@.cluster-id)&disable:bool=$(@.disable)"; %delete: xrl "$(bgp.targetname)/bgp/0.3/set_cluster_id?cluster_id:ipv4=$(@.cluster-id)&disable:bool=`~$(@.disable.DEFAULT)`"; cluster-id { %set:; } disable { %set:; } } confederation { %mandatory: $(@.identifier); %create: xrl "$(bgp.targetname)/bgp/0.3/set_confederation_identifier?as:txt=$(@.identifier)&disable:bool=$(@.disable)"; %activate: xrl "$(bgp.targetname)/bgp/0.3/set_confederation_identifier?as:txt=$(@.identifier)&disable:bool=$(@.disable)"; %delete: xrl "$(bgp.targetname)/bgp/0.3/set_confederation_identifier?as:txt=$(@.identifier)&disable:bool=`~$(@.disable.DEFAULT)`"; identifier { %set:; } disable { %set:; } } damping { %create: xrl "$(bgp.targetname)/bgp/0.3/set_damping?half_life:u32=$(@.half-life)&max_suppress:u32=$(@.max-suppress)&reuse:u32=$(@.reuse)&suppress:u32=$(@.suppress)&disable:bool=$(@.disable)"; %activate: xrl "$(bgp.targetname)/bgp/0.3/set_damping?half_life:u32=$(@.half-life)&max_suppress:u32=$(@.max-suppress)&reuse:u32=$(@.reuse)&suppress:u32=$(@.suppress)&disable:bool=$(@.disable)"; %delete: xrl "$(bgp.targetname)/bgp/0.3/set_damping?half_life:u32=$(@.half-life)&max_suppress:u32=$(@.max-suppress)&reuse:u32=$(@.reuse)&suppress:u32=$(@.suppress)&disable:bool=`~$(@.disable.DEFAULT)`"; half-life { %set:; } max-suppress { %set:; } reuse { %set:; } suppress { %set:; } } peer @ { %mandatory: $(@.as); %mandatory: $(@.next-hop); %mandatory: $(@.local-port); %mandatory: $(@.peer-port); %mandatory: $(@.local-ip); %create: xrl "$(bgp.targetname)/bgp/0.3/add_peer?local_dev:txt=$(@.local-dev)&local_ip:txt=$(@.local-ip)&local_port:u32=$(@.local-port)&peer_ip:txt=$(@)&peer_port:u32=$(@.peer-port)&as:txt=$(@.as)&next_hop:ipv4=$(@.next-hop)&holdtime:u32=$(@.holdtime)"; %delete: xrl "$(bgp.targetname)/bgp/0.3/delete_peer?local_ip:txt=$(@.local-ip)&local_port:u32=$(@.local-port)&peer_ip:txt=$(@)&peer_port:u32=$(@.peer-port)"; %activate: xrl "$(bgp.targetname)/bgp/0.3/activate?local_ip:txt=$(@.local-ip)&local_port:u32=$(@.local-port)&peer_ip:txt=$(@)&peer_port:u32=$(@.peer-port)"; /* peer-port { %set: xrl "$(bgp.targetname)/bgp/0.3/change_peer_port?local_ip:txt=$(peer.@.local-ip)&local_port:u32=$(peer.@.local-port)&peer_ip:txt=$(peer.@)&peer_port:u32=$(peer.@.peer-port)&new_peer_port:u32=$(@)"; } */ /* local-port { %set: xrl "$(bgp.targetname)/bgp/0.3/change_local_port?local_ip:txt=$(peer.@.local-ip)&local_port:u32=$(peer.@.local-port)&peer_ip:txt=$(peer.@)&peer_port:u32=$(peer.@.peer-port)&new_local_port:u32=$(@)"; } */ local-dev { %set: xrl "$(bgp.targetname)/bgp/0.3/change_local_ip?local_ip:txt=$(peer.@.local-ip)&local_port:u32=$(peer.@.local-port)&peer_ip:txt=$(peer.@)&peer_port:u32=$(peer.@.peer-port)&new_local_ip:txt=$(peer.@.local-ip)&new_local_dev:txt=$(@)"; } local-ip { %set: xrl "$(bgp.targetname)/bgp/0.3/change_local_ip?local_ip:txt=$(peer.@.local-ip)&local_port:u32=$(peer.@.local-port)&peer_ip:txt=$(peer.@)&peer_port:u32=$(peer.@.peer-port)&new_local_ip:txt=$(@)&new_local_dev:txt=$(peer.@.local-dev)"; } /* md5-password { %set:; } */ as { /*%allow-range: $(@) "1" "65535" %help: "The peer AS number";*/ %set: xrl "$(bgp.targetname)/bgp/0.3/set_peer_as?local_ip:txt=$(peer.@.local-ip)&local_port:u32=$(peer.@.local-port)&peer_ip:txt=$(peer.@)&peer_port:u32=$(peer.@.peer-port)&peer_as:txt=$(@)"; } holdtime { %allow-range: $(@) "0" "0" %help: "Disable holdtime"; %allow-range: $(@) "3" "65535" %help: "Time to wait for peer"; %set: xrl "$(bgp.targetname)/bgp/0.3/set_holdtime?local_ip:txt=$(peer.@.local-ip)&local_port:u32=$(peer.@.local-port)&peer_ip:txt=$(peer.@)&peer_port:u32=$(peer.@.peer-port)&holdtime:u32=$(@)"; } delay-open-time { %set: xrl "$(bgp.targetname)/bgp/0.3/set_delay_open_time?local_ip:txt=$(peer.@.local-ip)&local_port:u32=$(peer.@.local-port)&peer_ip:txt=$(peer.@)&peer_port:u32=$(peer.@.peer-port)&delay_open_time:u32=$(@)"; %delete: xrl "$(bgp.targetname)/bgp/0.3/set_delay_open_time?local_ip:txt=$(peer.@.local-ip)&local_port:u32=$(peer.@.local-port)&peer_ip:txt=$(peer.@)&peer_port:u32=$(peer.@.peer-port)&delay_open_time:u32=$(DEFAULT)"; } client { %set: xrl "$(bgp.targetname)/bgp/0.3/set_route_reflector_client?local_ip:txt=$(peer.@.local-ip)&local_port:u32=$(peer.@.local-port)&peer_ip:txt=$(peer.@)&peer_port:u32=$(peer.@.peer-port)&state:bool=$(@)"; } confederation-member { %set: xrl "$(bgp.targetname)/bgp/0.3/set_confederation_member?local_ip:txt=$(peer.@.local-ip)&local_port:u32=$(peer.@.local-port)&peer_ip:txt=$(peer.@)&peer_port:u32=$(peer.@.peer-port)&state:bool=$(@)"; } prefix-limit { %create: xrl "$(bgp.targetname)/bgp/0.3/set_prefix_limit?local_ip:txt=$(peer.@.local-ip)&local_port:u32=$(peer.@.local-port)&peer_ip:txt=$(peer.@)&peer_port:u32=$(peer.@.peer-port)&maximum:u32=$(@.maximum)&state:bool=`~$(@.disable)`"; %activate: xrl "$(bgp.targetname)/bgp/0.3/set_prefix_limit?local_ip:txt=$(peer.@.local-ip)&local_port:u32=$(peer.@.local-port)&peer_ip:txt=$(peer.@)&peer_port:u32=$(peer.@.peer-port)&maximum:u32=$(@.maximum)&state:bool=`~$(@.disable)`"; %delete: xrl "$(bgp.targetname)/bgp/0.3/set_prefix_limit?local_ip:txt=$(peer.@.local-ip)&local_port:u32=$(peer.@.local-port)&peer_ip:txt=$(peer.@)&peer_port:u32=$(peer.@.peer-port)&maximum:u32=$(@.maximum)&state:bool=$(@.disable.DEFAULT)"; maximum { %set:; } disable { %set:; } } next-hop { %set: xrl "$(bgp.targetname)/bgp/0.3/set_nexthop4?local_ip:txt=$(peer.@.local-ip)&local_port:u32=$(peer.@.local-port)&peer_ip:txt=$(peer.@)&peer_port:u32=$(peer.@.peer-port)&next_hop:ipv4=$(@)"; } next-hop6 { %set: xrl "$(bgp.targetname)/bgp/0.3/set_nexthop6?local_ip:txt=$(peer.@.local-ip)&local_port:u32=$(peer.@.local-port)&peer_ip:txt=$(peer.@)&peer_port:u32=$(peer.@.peer-port)&next_hop:ipv6=$(@)"; } disable { %set: xrl "$(bgp.targetname)/bgp/0.3/set_peer_state?local_ip:txt=$(peer.@.local-ip)&local_port:u32=$(peer.@.local-port)&peer_ip:txt=$(peer.@)&peer_port:u32=$(peer.@.peer-port)&toggle:bool=`~$(@)`"; %delete: xrl "$(bgp.targetname)/bgp/0.3/set_peer_state?local_ip:txt=$(peer.@.local-ip)&local_port:u32=$(peer.@.local-port)&peer_ip:txt=$(peer.@)&peer_port:u32=$(peer.@.peer-port)&toggle:bool=`~$(DEFAULT)`"; } enabled { %deprecated: "Statement 'enabled: true/false' is replaced with 'disable: false/true'"; %set: xrl "$(bgp.targetname)/bgp/0.3/set_peer_state?local_ip:txt=$(peer.@.local-ip)&local_port:u32=$(peer.@.local-port)&peer_ip:txt=$(peer.@)&peer_port:u32=$(peer.@.peer-port)&toggle:bool=$(@)"; } ipv4-unicast { %set: xrl "$(bgp.targetname)/bgp/0.3/set_parameter?local_ip:txt=$(peer.@.local-ip)&local_port:u32=$(peer.@.local-port)&peer_ip:txt=$(peer.@)&peer_port:u32=$(peer.@.peer-port)¶meter:txt=MultiProtocol.IPv4.Unicast&toggle:bool=$(@)"; %delete: xrl "$(bgp.targetname)/bgp/0.3/set_parameter?local_ip:txt=$(peer.@.local-ip)&local_port:u32=$(peer.@.local-port)&peer_ip:txt=$(peer.@)&peer_port:u32=$(peer.@.peer-port)¶meter:txt=MultiProtocol.IPv4.Unicast&toggle:bool=$(DEFAULT)"; } ipv4-multicast { %set: xrl "$(bgp.targetname)/bgp/0.3/set_parameter?local_ip:txt=$(peer.@.local-ip)&local_port:u32=$(peer.@.local-port)&peer_ip:txt=$(peer.@)&peer_port:u32=$(peer.@.peer-port)¶meter:txt=MultiProtocol.IPv4.Multicast&toggle:bool=$(@)"; %delete: xrl "$(bgp.targetname)/bgp/0.3/set_parameter?local_ip:txt=$(peer.@.local-ip)&local_port:u32=$(peer.@.local-port)&peer_ip:txt=$(peer.@)&peer_port:u32=$(peer.@.peer-port)¶meter:txt=MultiProtocol.IPv4.Multicast&toggle:bool=$(DEFAULT)"; } ipv6-unicast { %set: xrl "$(bgp.targetname)/bgp/0.3/set_parameter?local_ip:txt=$(peer.@.local-ip)&local_port:u32=$(peer.@.local-port)&peer_ip:txt=$(peer.@)&peer_port:u32=$(peer.@.peer-port)¶meter:txt=MultiProtocol.IPv6.Unicast&toggle:bool=$(@)"; %delete: xrl "$(bgp.targetname)/bgp/0.3/set_parameter?local_ip:txt=$(peer.@.local-ip)&local_port:u32=$(peer.@.local-port)&peer_ip:txt=$(peer.@)&peer_port:u32=$(peer.@.peer-port)¶meter:txt=MultiProtocol.IPv6.Unicast&toggle:bool=$(DEFAULT)"; } ipv6-multicast { %set: xrl "$(bgp.targetname)/bgp/0.3/set_parameter?local_ip:txt=$(peer.@.local-ip)&local_port:u32=$(peer.@.local-port)&peer_ip:txt=$(peer.@)&peer_port:u32=$(peer.@.peer-port)¶meter:txt=MultiProtocol.IPv6.Multicast&toggle:bool=$(@)"; %delete: xrl "$(bgp.targetname)/bgp/0.3/set_parameter?local_ip:txt=$(peer.@.local-ip)&local_port:u32=$(peer.@.local-port)&peer_ip:txt=$(peer.@)&peer_port:u32=$(peer.@.peer-port)¶meter:txt=MultiProtocol.IPv6.Multicast&toggle:bool=$(DEFAULT)"; } import { %help: short "Import policy"; %delete: xrl "$(policy.targetname)/policy/0.1/import?protocol:txt=$(bgp.targetname)&policies:txt=&modifier:txt=neighbor == $(peer.@);"; %set: xrl "$(policy.targetname)/policy/0.1/import?protocol:txt=$(bgp.targetname)&policies:txt=$(@)&modifier:txt=neighbor == $(peer.@);"; } export { %help: short "Export policy"; %delete: xrl "$(policy.targetname)/policy/0.1/export?protocol:txt=$(bgp.targetname)&policies:txt=&modifier:txt=neighbor == $(peer.@);"; %set: xrl "$(policy.targetname)/policy/0.1/export?protocol:txt=$(bgp.targetname)&policies:txt=$(@)&modifier:txt=neighbor == $(peer.@);"; } } /* peer */ /* network4 @: ipv4net { %mandatory: $(@.next-hop); %activate: xrl "$(bgp.targetname)/bgp/0.3/originate_route4?nlri:ipv4net=$(@)&next_hop:ipv4=$(@.next-hop)&unicast:bool=$(@.unicast)&multicast:bool=$(@.multicast)"; %delete: xrl "$(bgp.targetname)/bgp/0.3/withdraw_route4?nlri:ipv4net=$(@)&unicast:bool=$(@.unicast)&multicast:bool=$(@.multicast)"; next-hop { %set:; } unicast { %set:; } multicast { %set:; } } network6 @: ipv6net { %mandatory: $(@.next-hop); %activate: xrl "$(bgp.targetname)/bgp/0.3/originate_route6?nlri:ipv6net=$(@)&next_hop:ipv6=$(@.next-hop)&unicast:bool=$(@.unicast)&multicast:bool=$(@.multicast)"; %delete: xrl "$(bgp.targetname)/bgp/0.3/withdraw_route6?nlri:ipv6net=$(@)&unicast:bool=$(@.unicast)&multicast:bool=$(@.multicast)"; next-hop { %set:; } unicast { %set:; } multicast { %set:; } } */ traceoptions { flag { /* Deliberately not included in all */ verbose { disable { %set: xrl "$(bgp.targetname)/bgp/0.3/trace?tvar:txt=xlog_verbose&enable:bool=`~$(@)`"; %delete: xrl "$(bgp.targetname)/bgp/0.3/trace?tvar:txt=xlog_verbose&enable:bool=$(DEFAULT)"; } } all { disable { %set: xrl "$(bgp.targetname)/bgp/0.3/trace?tvar:txt=trace_message_in&enable:bool=`~$(@)`"; %set: xrl "$(bgp.targetname)/bgp/0.3/trace?tvar:txt=trace_message_out&enable:bool=`~$(@)`"; %set: xrl "$(bgp.targetname)/bgp/0.3/trace?tvar:txt=trace_state_change&enable:bool=`~$(@)`"; %set: xrl "$(bgp.targetname)/bgp/0.3/trace?tvar:txt=trace_policy_configure&enable:bool=`~$(@)`"; %delete: xrl "$(bgp.targetname)/bgp/0.3/trace?tvar:txt=trace_message_in&enable:bool=$(DEFAULT)"; %delete: xrl "$(bgp.targetname)/bgp/0.3/trace?tvar:txt=trace_message_out&enable:bool=$(DEFAULT)"; %delete: xrl "$(bgp.targetname)/bgp/0.3/trace?tvar:txt=trace_state_change&enable:bool=$(DEFAULT)"; %delete: xrl "$(bgp.targetname)/bgp/0.3/trace?tvar:txt=trace_policy_configure&enable:bool=$(DEFAULT)"; } } message-in { disable { %set: xrl "$(bgp.targetname)/bgp/0.3/trace?tvar:txt=trace_message_in&enable:bool=`~$(@)`"; %delete: xrl "$(bgp.targetname)/bgp/0.3/trace?tvar:txt=trace_message_in&enable:bool=$(DEFAULT)"; } } message-out { disable { %set: xrl "$(bgp.targetname)/bgp/0.3/trace?tvar:txt=trace_message_out&enable:bool=`~$(@)`"; %delete: xrl "$(bgp.targetname)/bgp/0.3/trace?tvar:txt=trace_message_out&enable:bool=$(DEFAULT)"; } } state-change { disable { %set: xrl "$(bgp.targetname)/bgp/0.3/trace?tvar:txt=trace_state_change&enable:bool=`~$(@)`"; %delete: xrl "$(bgp.targetname)/bgp/0.3/trace?tvar:txt=trace_state_change&enable:bool=$(DEFAULT)"; } } policy-configuration { disable { %set: xrl "$(bgp.targetname)/bgp/0.3/trace?tvar:txt=trace_policy_configure&enable:bool=`~$(@)`"; %delete: xrl "$(bgp.targetname)/bgp/0.3/trace?tvar:txt=trace_policy_configure&enable:bool=$(DEFAULT)"; } } } } import { %help: short "Import policy"; %delete: xrl "$(policy.targetname)/policy/0.1/import?protocol:txt=$(bgp.targetname)&policies:txt=&modifier:txt="; %create:; %set: xrl "$(policy.targetname)/policy/0.1/import?protocol:txt=$(bgp.targetname)&policies:txt=$(@)&modifier:txt="; } export { %help: short "Export policy"; %delete: xrl "$(policy.targetname)/policy/0.1/export?protocol:txt=$(bgp.targetname)&policies:txt=&modifier:txt="; %create:; %set: xrl "$(policy.targetname)/policy/0.1/export?protocol:txt=$(bgp.targetname)&policies:txt=$(@)&modifier:txt="; } %activate: xrl "$(policy.targetname)/policy/0.1/import?protocol:txt=$(bgp.targetname)&policies:txt=$(@.import)&modifier:txt="; %activate: xrl "$(policy.targetname)/policy/0.1/export?protocol:txt=$(bgp.targetname)&policies:txt=$(@.export)&modifier:txt="; } } protocols { bgp { %help: short "Configure BGP inter-domain routing"; targetname { } bgp-id { %help: short "Set the BGP identifier (must be an IPv4 address)"; } local-as { %help: short "Set the Autonomous System (AS) number for this domain"; } route-reflector { %help: short "Make this router a route reflector"; cluster-id { %help: short "CLUSTER_ID for this route reflector"; } disable { %help: short "disable this router reflector"; } } confederation { %help: short "Make this router part of a confederation"; identifier { %help: short "AS number used to non confederation peers"; } disable { %help: short "disable confederations"; } } damping { %help: short "Route Flap Damping"; half-life { %help: short "Decay half-life in minutes"; } max-suppress { %help: short "Maximum hold-down time in minutes"; } reuse { %help: short "Reuse threshold"; } suppress { %help: short "Cutoff (suppression) threshold"; } disable { %help: short "disable damping"; } } peer @ { %help: short "Configure a peering session with another router."; peer-port { %help: short "Set the TCP port used by on the remote router."; } local-port { %help: short "Set the TCP port used by BGP on this router."; } local-ip { %help: short "Set the IP address on this router to use for this peering."; } as { %help: short "Set the AS number of this peer."; } holdtime { %help: short "Set the length of time before idle timeout."; } delay-open-time { %help: short "Set the delay open time."; } client { %help: short "Set to true if the peer is a route reflector client."; } confederation-member { %help: short "Set to true if the peer is a confederation member"; } prefix-limit { %help: short "Threshold for the number of prefixes that can be accepted."; maximum { %help: short "Number of prefixes to accept before tearing down session"; } disable { %help: short "Disable the prefix-limit"; } } next-hop { %help: short "IPv4 next-hop"; } next-hop6 { %help: short "IPv6 next-hop"; } disable { %help: short "Disable this peering."; } enabled { %help: short "Enable this peering."; } client { %help: short "Treat peer as a route reflector client."; } /* md5-password { %help: short "Enable and set the password for TCP-MD5 authentication with this peer."; } */ set-parameter { } ipv4-unicast { %help: short "IPv4 Unicast"; } ipv4-multicast { %help: short "IPv4 Multicast"; } ipv6-unicast { %help: short "IPv6 Unicast"; } ipv6-multicast { %help: short "IPv6 Multicast"; } } traceoptions { %help: short "Configure the tracing options"; flag { %help: short "Configure the tracing operation to perform"; verbose { %help: short "Configure verbose traces"; disable { %help: short "Disable verbose traces"; } } all { %help: short "Configure all tracing operations"; disable { %help: short "Disable all tracing operations"; } } message-in { %help: short "Configure receive message tracing"; disable { %help: short "Disable receive message tracing"; } } message-out { %help: short "Configure send message tracing"; disable { %help: short "Disable send message tracing"; } } state-change { %help: short "Configure FSM state change tracing"; disable { %help: short "Disable FSM state change tracing"; } } policy-configuration { %help: short "Configure policy configuration tracing"; disable { %help: short "Disable policy configuration tracing"; } } } } } } policy { policy-statement @: txt { term @: txt { from { as-path { %help: short "BGP AS path"; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt=aspath REGEX '$(@)';"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt="; } as-path-list { %help: short "Named set of BGP AS paths"; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt=aspath REGEX SET $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt="; } community { %help: short "BGP community"; %allow-operator: ":" "==" "!=" "<" ">" "<=" ">="; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt=community $(<>) (ctr 'set_com32' '$(@)');"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt="; } community-list { %help: short "Named set of BGP communities"; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt=community ne_int SET $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt="; } neighbor { %help: short "Neighbor router"; %allow-operator: ":" "==" "!=" "<" ">" "<=" ">="; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt=neighbor $(<>) $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt="; } origin { %help: short "BGP origin attribute"; %allow-range: $(@) "0" "2" %help: "Origin attribute: 0 for IGP, 1 for EGP, 2 for incomplete"; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt=origin $(<>) $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt="; } med { %help: short "BGP MED attribute"; %allow-operator: ":" "==" "!=" "<" ">" "<=" ">="; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt=med $(<>) $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt="; } localpref { %help: short "Local preference associated with a route"; %allow-operator: ":" "==" "!=" "<" ">" "<=" ">="; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt=localpref $(<>) $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt="; } } /* from */ to { as-path { %help: short "BGP AS path"; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=1&order:txt=$(#)&statement:txt=aspath REGEX '$(@)';"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=1&order:txt=$(#)&statement:txt="; } as-path-list { %help: short "Named set of BGP AS paths"; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=1&order:txt=$(#)&statement:txt=aspath REGEX SET $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=1&order:txt=$(#)&statement:txt="; } community { %help: short "BGP community"; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=1&order:txt=$(#)&statement:txt=community ne_int (ctr 'set_com32' '$(@)');"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=1&order:txt=$(#)&statement:txt="; } neighbor { %help: short "Neighbor router"; %allow-operator: ":" "==" "!=" "<" ">" "<=" ">="; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=1&order:txt=$(#)&statement:txt=neighbor $(<>) $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=1&order:txt=$(#)&statement:txt="; } origin { %help: short "BGP origin attribute"; %allow-range: $(@) "0" "2" %help: "Origin attribute: 0 for IGP, 1 for EGP, 2 for incomplete"; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=1&order:txt=$(#)&statement:txt=origin $(<>) $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=1&order:txt=$(#)&statement:txt="; } med { %help: short "BGP MED attribute"; %allow-operator: ":" "==" "!=" "<" ">" "<=" ">="; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=1&order:txt=$(#)&statement:txt=med $(<>) $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=1&order:txt=$(#)&statement:txt="; } localpref { %help: short "Local preference associated with a route"; %allow-operator: ":" "==" "!=" "<" ">" "<=" ">="; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=1&order:txt=$(#)&statement:txt=localpref $(<>) $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=1&order:txt=$(#)&statement:txt="; } was-aggregated { %help: short "True if this route contributed to origination of an aggregate"; %allow-operator: ":" "==" "!="; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=1&order:txt=$(#)&statement:txt=was_aggregated $(<>) $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=1&order:txt=$(#)&statement:txt="; } } /* to */ then { as-path-prepend { %help: short "Prepend AS numbers to an AS path"; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=2&order:txt=$(#)&statement:txt=aspath = $(@) + aspath;"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=2&order:txt=$(#)&statement:txt="; } as-path-expand { %help: short "Prepend AS numbers prior to adding local-as"; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=2&order:txt=$(#)&statement:txt=aspath = $(@) * aspath;"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=2&order:txt=$(#)&statement:txt="; } community { %help: short "Set the BGP communities in the route"; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=2&order:txt=$(#)&statement:txt=community $(<>) (ctr 'set_com32' '$(@)');"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=2&order:txt=$(#)&statement:txt="; } community-add { %help: short "Add BGP communities to the route"; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=2&order:txt=$(#)&statement:txt=community = community + (ctr 'set_com32' '$(@)');"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=2&order:txt=$(#)&statement:txt="; } community-del { %help: short "Remove BGP communities from the route"; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=2&order:txt=$(#)&statement:txt=community = community - (ctr 'set_com32' '$(@)');"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=2&order:txt=$(#)&statement:txt="; } origin { %help: short "Set the BGP origin attribute"; %allow-range: $(@) "0" "2" %help: "Origin attribute: 0 for IGP, 1 for EGP, 2 for incomplete"; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=2&order:txt=$(#)&statement:txt=origin $(<>) $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=2&order:txt=$(#)&statement:txt="; } med { %help: short "Set the BGP MED attribute"; %allow-operator: ":" "=" "sub" "add"; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=2&order:txt=$(#)&statement:txt=med $(<>) $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=2&order:txt=$(#)&statement:txt="; } med-remove { %help: short "Remove BGP MED attribute"; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=2&order:txt=$(#)&statement:txt=med_remove $(<>) $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=2&order:txt=$(#)&statement:txt="; } localpref { %help: short "Set the local preference"; %allow-operator: ":" "=" "sub" "add"; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=2&order:txt=$(#)&statement:txt=localpref $(<>) $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=2&order:txt=$(#)&statement:txt="; } aggregate-prefix-len { %help: short "Originate an aggregate route with this prefix length"; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=2&order:txt=$(#)&statement:txt=aggregate_prefix_len $(<>) $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=2&order:txt=$(#)&statement:txt="; } aggregate-brief-mode { %help: short "Do not generate AS_SETs for aggregate routes"; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=2&order:txt=$(#)&statement:txt=aggregate_brief_mode $(<>) $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=2&order:txt=$(#)&statement:txt="; } } /* then */ } /* term */ } /* policy statement */ community-list @: txt { %help: short "Named set of BGP communities"; %delete: xrl "$(policy.targetname)/policy/0.1/delete_set?set:txt=$(@)"; %create: xrl "$(policy.targetname)/policy/0.1/create_set?set:txt=$(@)"; elements { %help: short "Elements in the named set of BGP communities"; %set: xrl "$(policy.targetname)/policy/0.1/update_set?type:txt=set_com32&set:txt=$(community-list.@)&elements:txt=$(@)"; } } as-path-list @: txt { %help: short "Named set of BGP AS paths"; %delete: xrl "$(policy.targetname)/policy/0.1/delete_set?set:txt=$(@)"; %create: xrl "$(policy.targetname)/policy/0.1/create_set?set:txt=$(@)"; elements { %help: short "Elements in the named set of BGP AS paths"; %set: xrl "$(policy.targetname)/policy/0.1/update_set?type:txt=set_str&set:txt=$(as-path-list.@)&elements:txt=$(@)"; } } } xorp/etc/templates/igmp.tp0000664000076400007640000001554111421137511015734 0ustar greearbgreearb/* $XORP: xorp/etc/templates/igmp.tp,v 1.20 2006/07/03 06:52:37 pavlin Exp $ */ protocols { igmp { targetname: txt = "IGMP"; disable: toggle = false; enabled: bool; /* %deprecated */ interface @: txt { vif @: txt { disable: toggle = false; enabled: bool; /* %deprecated */ version: u32 = 2; /* Default to IGMPv2 */ enable-ip-router-alert-option-check: bool = false; query-interval: u32 = 125; query-last-member-interval: u32 = 1; query-response-interval: u32 = 10; robust-count: u32 = 2; } } traceoptions { flag { all { disable: toggle = false; enabled: bool; /* %deprecated */ } } } } } protocols { igmp { %help: short "Configure the IGMP protocol"; %modinfo: provides igmp; %modinfo: depends mfea4; %modinfo: path "xorp_igmp"; %modinfo: default_targetname "mld6igmp"; %modinfo: status_method xrl "$(igmp.targetname)/common/0.1/get_status->status:u32&reason:txt"; %modinfo: startup_method xrl "$(igmp.targetname)/common/0.1/startup"; %modinfo: shutdown_method xrl "$(igmp.targetname)/common/0.1/shutdown"; %mandatory: $(@.targetname); targetname { %user-hidden: "XRL target name"; %help: short "XRL target name"; %set:; } disable { %help: short "Disable the IGMP protocol"; %create:; %set: xrl "$(igmp.targetname)/mld6igmp/0.1/enable_mld6igmp?enable:bool=`~$(@)`"; %delete: xrl "$(igmp.targetname)/mld6igmp/0.1/enable_mld6igmp?enable:bool=`~$(DEFAULT)`"; } enabled { %deprecated: "Statement 'enabled: true/false' is replaced with 'disable: false/true'"; %help: short "Enable the IGMP protocol"; %create:; %set: xrl "$(igmp.targetname)/mld6igmp/0.1/enable_mld6igmp?enable:bool=$(@)"; } interface @ { %help: short "Configure IGMP on a network interface"; %delete: xrl "$(igmp.targetname)/mld6igmp/0.1/stop_vif?vif_name:txt=$(interface.@)"; vif @ { %help: short "Configure IGMP on a virtual interface"; %activate: xrl "$(igmp.targetname)/mld6igmp/0.1/start_vif?vif_name:txt=$(vif.@)"; %update: xrl "$(igmp.targetname)/mld6igmp/0.1/start_vif?vif_name:txt=$(vif.@)"; %delete: xrl "$(igmp.targetname)/mld6igmp/0.1/stop_vif?vif_name:txt=$(vif.@)"; disable { %help: short "Disable IGMP on an interface"; %set: xrl "$(igmp.targetname)/mld6igmp/0.1/enable_vif?vif_name:txt=$(vif.@)&enable:bool=`~$(@)`"; %delete: xrl "$(igmp.targetname)/mld6igmp/0.1/enable_vif?vif_name:txt=$(vif.@)&enable:bool=`~$(DEFAULT)`"; } enabled { %deprecated: "Statement 'enabled: true/false' is replaced with 'disable: false/true'"; %help: short "Enable IGMP on an interface"; %set: xrl "$(igmp.targetname)/mld6igmp/0.1/enable_vif?vif_name:txt=$(vif.@)&enable:bool=$(@)"; %delete: xrl "$(igmp.targetname)/mld6igmp/0.1/enable_vif?vif_name:txt=$(vif.@)&enable:bool=`~$(DEFAULT)`"; } version { %help: short "Set the IGMP protocol version"; %allow-range: $(@) "1" "3" %help: "The IGMP protocol version"; %create: xrl "$(igmp.targetname)/mld6igmp/0.1/set_vif_proto_version?vif_name:txt=$(vif.@)&proto_version:u32=$(@)"; %set: xrl "$(igmp.targetname)/mld6igmp/0.1/set_vif_proto_version?vif_name:txt=$(vif.@)&proto_version:u32=$(@)"; %delete: xrl "$(igmp.targetname)/mld6igmp/0.1/set_vif_proto_version?vif_name:txt=$(vif.@)&proto_version:u32=$(DEFAULT)"; } enable-ip-router-alert-option-check { %help: short "Enable the IP Router Alert option check"; %create: xrl "$(igmp.targetname)/mld6igmp/0.1/set_vif_ip_router_alert_option_check?vif_name:txt=$(vif.@)&enable:bool=$(@)"; %set: xrl "$(igmp.targetname)/mld6igmp/0.1/set_vif_ip_router_alert_option_check?vif_name:txt=$(vif.@)&enable:bool=$(@)"; %delete: xrl "$(igmp.targetname)/mld6igmp/0.1/set_vif_ip_router_alert_option_check?vif_name:txt=$(vif.@)&enable:bool=$(DEFAULT)"; } query-interval { %help: short "Set the query interval (in seconds)"; %allow-range: $(@) "1" "1024" %help: "The query interval (in seconds)"; %create: xrl "$(igmp.targetname)/mld6igmp/0.1/set_vif_query_interval?vif_name:txt=$(vif.@)&interval_sec:u32=$(@)&interval_usec:u32=0"; %set: xrl "$(igmp.targetname)/mld6igmp/0.1/set_vif_query_interval?vif_name:txt=$(vif.@)&interval_sec:u32=$(@)&interval_usec:u32=0"; %delete: xrl "$(igmp.targetname)/mld6igmp/0.1/set_vif_query_interval?vif_name:txt=$(vif.@)&interval_sec:u32=$(DEFAULT)&interval_usec:u32=0"; } query-last-member-interval { %help: short "Set the last member query interval (in seconds)"; %allow-range: $(@) "1" "1024" %help: "The last member query interval (in seconds)"; %create: xrl "$(igmp.targetname)/mld6igmp/0.1/set_vif_query_last_member_interval?vif_name:txt=$(vif.@)&interval_sec:u32=$(@)&interval_usec:u32=0"; %set: xrl "$(igmp.targetname)/mld6igmp/0.1/set_vif_query_last_member_interval?vif_name:txt=$(vif.@)&interval_sec:u32=$(@)&interval_usec:u32=0"; %delete: xrl "$(igmp.targetname)/mld6igmp/0.1/set_vif_query_last_member_interval?vif_name:txt=$(vif.@)&interval_sec:u32=$(DEFAULT)&interval_usec:u32=0"; } query-response-interval { %help: short "Set the query response interval (in seconds)"; %allow-range: $(@) "1" "1024" %help: "The query response interval (in seconds)"; %create: xrl "$(igmp.targetname)/mld6igmp/0.1/set_vif_query_response_interval?vif_name:txt=$(vif.@)&interval_sec:u32=$(@)&interval_usec:u32=0"; %set: xrl "$(igmp.targetname)/mld6igmp/0.1/set_vif_query_response_interval?vif_name:txt=$(vif.@)&interval_sec:u32=$(@)&interval_usec:u32=0"; %delete: xrl "$(igmp.targetname)/mld6igmp/0.1/set_vif_query_response_interval?vif_name:txt=$(vif.@)&interval_sec:u32=$(DEFAULT)&interval_usec:u32=0"; } robust-count { %help: short "Set the robustness variable count"; %allow-range: $(@) "2" "10" %help: "The robustness variable count"; %create: xrl "$(igmp.targetname)/mld6igmp/0.1/set_vif_robust_count?vif_name:txt=$(vif.@)&robust_count:u32=$(@)"; %set: xrl "$(igmp.targetname)/mld6igmp/0.1/set_vif_robust_count?vif_name:txt=$(vif.@)&robust_count:u32=$(@)"; %delete: xrl "$(igmp.targetname)/mld6igmp/0.1/set_vif_robust_count?vif_name:txt=$(vif.@)&robust_count:u32=$(DEFAULT)"; } } } traceoptions { %help: short "Configure the tracing options"; flag { %help: short "Configure the tracing operation to perform"; all { %help: short "Configure all tracing operations"; disable { %help: short "Disable all tracing operations"; %set: xrl "$(igmp.targetname)/mld6igmp/0.1/log_trace_all?enable:bool=`~$(@)`"; %delete: xrl "$(igmp.targetname)/mld6igmp/0.1/log_trace_all?enable:bool=$(DEFAULT)"; } enabled { %deprecated: "Statement 'enabled: true/false' is replaced with 'disable: false/true'"; %help: short "Enable all tracing operations"; %set: xrl "$(igmp.targetname)/mld6igmp/0.1/log_trace_all?enable:bool=$(@)"; } } } } } } xorp/etc/templates/rip.tp0000664000076400007640000004041111421137511015564 0ustar greearbgreearb/* $XORP: xorp/etc/templates/rip.tp,v 1.52 2008/10/29 21:59:38 andrewma Exp $ */ /* * RIP attribute descriptions */ protocols { rip { targetname: txt = "rip"; traceoptions { flag { all { disable: toggle = false; } } } interface @: txt { vif @: txt { address @: ipv4 { metric: u32 = 1; horizon: txt = "split-horizon-poison-reverse"; disable: toggle = false; enabled: bool; /* %deprecated */ passive: bool = false; accept-non-rip-requests: bool = true; accept-default-route: bool = true; advertise-default-route: bool = true; route-timeout: u32 = 180; route-expiry-secs: u32; /* %deprecated */ deletion-delay: u32 = 120; route-deletion-secs: u32; /* %deprecated */ triggered-delay: u32 = 3; triggered-jitter: u32 = 66; update-interval: u32 = 30; update-jitter: u32 = 16; request-interval: u32 = 30; table-request-secs: u32; /* %deprecated */ interpacket-delay: u32 = 50; interpacket-delay-msecs: u32; /* %deprecated */ authentication { type: txt; /* %deprecated */ password: txt; /* %deprecated */ simple-password: txt = ""; md5 @: u32 { password: txt = ""; start-time: txt = ""; end-time: txt = ""; } } } } } import: txt; export: txt; } } policy { policy-statement @: txt { term @: txt { from { metric: u32range; } to { metric: u32range; } then { metric: u32; } } } } /* * RIP attribute manipulation. */ protocols { rip { %modinfo: provides rip; %modinfo: depends rib; %modinfo: depends policy; %modinfo: path "xorp_rip"; %modinfo: default_targetname "rip"; %modinfo: status_method xrl "$(rip.targetname)/common/0.1/get_status->status:u32&reason:txt"; %modinfo: shutdown_method xrl "$(rip.targetname)/common/0.1/shutdown"; %help: short "RIP configuration"; %mandatory: $(@.targetname); targetname { %user-hidden: "XRL target name"; %help: short "XRL target name"; %set:; } traceoptions { %help: short "Configure the tracing options"; flag { %help: short "Configure the tracing operation to perform"; all { %help: short "Configure all tracing operations"; disable { %help: short "Disable all tracing operations"; %set: xrl "$(rip.targetname)/rip/0.1/trace?tvar:txt=all&enable:bool=`~$(@)`"; %delete: xrl "$(rip.targetname)/rip/0.1/trace?tvar:txt=all&enable:bool=$(DEFAULT)"; } } } } interface @ { %help: short "Interface in RIP domain"; vif @ { %help: short "Vif in RIP domain"; address @ { %help: short "Address on vif used for RIP"; %create: xrl "$(rip.targetname)/rip/0.1/add_rip_address?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv4=$(address.@)"; %delete: xrl "$(rip.targetname)/rip/0.1/remove_rip_address?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv4=$(address.@)"; %activate: xrl "$(rip.targetname)/rip/0.1/set_rip_address_enabled?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv4=$(address.@)&enabled:bool=`~$(@.disable)`"; disable { %help: short "Disable RIP on address"; %create:; %set: xrl "$(rip.targetname)/rip/0.1/set_rip_address_enabled?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv4=$(address.@)&enabled:bool=`~$(@)`"; } enabled { %deprecated: "Statement 'enabled: true/false' is replaced with 'disable: false/true'"; %set: xrl "$(rip.targetname)/rip/0.1/set_rip_address_enabled?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv4=$(address.@)&enabled:bool=$(@)"; %get: xrl "$(rip.targetname)/rip/0.1/rip_address_enabled?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv4=$(address.@)->enabled:bool"; %help: short "Enable RIP on address"; } metric { %allow-range: $(@) "0" "16" %help: "Cost metric added to routes received on address"; %set: xrl "$(rip.targetname)/rip/0.1/set_cost?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv4=$(address.@)&cost:u32=$(@)"; %get: xrl "$(rip.targetname)/rip/0.1/cost?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv4=$(address.@)->cost:u32"; %help: short "Cost metric added to routes received on address"; } horizon { %allow: $(@) "none" %help: "No horizon type"; %allow: $(@) "split-horizon" %help: "Split horizon"; %allow: $(@) "split-horizon-poison-reverse" %help: "Split horizon with poisoned reverse"; %set: xrl "$(rip.targetname)/rip/0.1/set_horizon?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv4=$(address.@)&horizon:txt=$(@)"; %get: xrl "$(rip.targetname)/rip/0.1/horizon?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv4=$(address.@)->horizon:txt"; %help: short "Horizon type applied to announced routes"; } passive { %set: xrl "$(rip.targetname)/rip/0.1/set_passive?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv4=$(address.@)&passive:bool=$(@)"; %get: xrl "$(rip.targetname)/rip/0.1/passive?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv4=$(address.@)->passive:bool"; %help: short "Do not transmit RIP packets"; } accept-non-rip-requests { %set: xrl "$(rip.targetname)/rip/0.1/set_accept_non_rip_requests?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv4=$(address.@)&accept:bool=$(@)"; %get: xrl "$(rip.targetname)/rip/0.1/accept_non_rip_requests?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv4=$(address.@)->accept:bool"; %help: short "Accept RIP requests from non-RIP port"; } accept-default-route { %set: xrl "$(rip.targetname)/rip/0.1/set_accept_default_route?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv4=$(address.@)&accept:bool=$(@)"; %get: xrl "$(rip.targetname)/rip/0.1/accept_default_route?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv4=$(address.@)->accept:bool"; %help: short "Accept default route from RIP neighbor"; } advertise-default-route { %set: xrl "$(rip.targetname)/rip/0.1/set_advertise_default_route?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv4=$(address.@)&advertise:bool=$(@)"; %get: xrl "$(rip.targetname)/rip/0.1/advertise_default_route?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv4=$(address.@)->advertise:bool"; %help: short "Advertise default route to RIP neighbors"; } route-timeout { %set: xrl "$(rip.targetname)/rip/0.1/set_route_timeout?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv4=$(address.@)&t_secs:u32=$(@)"; %get: xrl "$(rip.targetname)/rip/0.1/route_timeout?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv4=$(address.@)->t_secs:u32"; %help: short "Route expiration time (sec)"; } route-expiry-secs { %deprecated: "Statement 'route-expiry-secs' is replaced with 'route-timeout'"; %set: xrl "$(rip.targetname)/rip/0.1/set_route_timeout?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv4=$(address.@)&t_secs:u32=$(@)"; %get: xrl "$(rip.targetname)/rip/0.1/route_timeout?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv4=$(address.@)->t_secs:u32"; %help: short "Route expiration interval in the absence of updates"; } deletion-delay { %set: xrl "$(rip.targetname)/rip/0.1/set_deletion_delay?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv4=$(address.@)&t_secs:u32=$(@)"; %get: xrl "$(rip.targetname)/rip/0.1/deletion_delay?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv4=$(address.@)->t_secs:u32"; %help: short "Delay before expired routes are deleted (sec)"; } route-deletion-secs { %deprecated: "Statement 'route-deletion-secs' is replaced with 'deletion-delay'"; %set: xrl "$(rip.targetname)/rip/0.1/set_deletion_delay?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv4=$(address.@)&t_secs:u32=$(@)"; %get: xrl "$(rip.targetname)/rip/0.1/deletion_delay?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv4=$(address.@)->t_secs:u32"; %help: short "Route deletion interval after advertised as unreachable"; } triggered-delay { %set: xrl "$(rip.targetname)/rip/0.1/set_triggered_update_delay?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv4=$(address.@)&t_secs:u32=$(@)"; %get: xrl "$(rip.targetname)/rip/0.1/triggered_update_delay?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv4=$(address.@)->t_secs:u32"; %help: short "Delay before sending repeated triggered update (sec)"; } triggered-jitter { %allow-range: $(@) "0" "100" %help: "Jitter as percents of 'triggered-delay'"; %set: xrl "$(rip.targetname)/rip/0.1/set_triggered_update_jitter?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv4=$(address.@)&t_jitter:u32=$(@)"; %get: xrl "$(rip.targetname)/rip/0.1/triggered_update_jitter?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv4=$(address.@)->t_jitter:u32"; %help: short "Jitter of repeated triggered update delay (percents)"; } update-interval { %set: xrl "$(rip.targetname)/rip/0.1/set_update_interval?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv4=$(address.@)&t_secs:u32=$(@)"; %get: xrl "$(rip.targetname)/rip/0.1/update_interval?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv4=$(address.@)->t_secs:u32"; %help: short "Interval between regular route updates (sec)"; } update-jitter { %allow-range: $(@) "0" "100" %help: "Jitter as percents of 'update-interval'"; %set: xrl "$(rip.targetname)/rip/0.1/set_update_jitter?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv4=$(address.@)&t_jitter:u32=$(@)"; %get: xrl "$(rip.targetname)/rip/0.1/update_jitter?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv4=$(address.@)->t_jitter:u32"; %help: short "Jitter of regular route update interval (percents)"; } request-interval { %allow-range: $(@) "1" "10000" %help: "RIP request interval when no known neighbors (sec)"; %allow-range: $(@) "0" "0" %help: "Disable periodic RIP requests"; %set: xrl "$(rip.targetname)/rip/0.1/set_request_interval?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv4=$(address.@)&t_secs:u32=$(@)"; %get: xrl "$(rip.targetname)/rip/0.1/request_interval?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv4=$(address.@)->t_secs:u32"; %help: short "RIP request interval when no known neighbors (sec)"; } table-request-secs { %deprecated: "Statement 'table-request-secs' is replaced with 'request-interval'"; %allow-range: $(@) "1" "10000" %help: "RIP request interval when no known neighbors"; %allow-range: $(@) "0" "0" %help: "Disable periodic RIP request packets when no known neighbors"; %set: xrl "$(rip.targetname)/rip/0.1/set_request_interval?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv4=$(address.@)&t_secs:u32=$(@)"; %get: xrl "$(rip.targetname)/rip/0.1/request_interval?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv4=$(address.@)->t_secs:u32"; %help: short "RIP request interval when no known neighbors"; } interpacket-delay { %set: xrl "$(rip.targetname)/rip/0.1/set_interpacket_delay?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv4=$(address.@)&t_msecs:u32=$(@)"; %get: xrl "$(rip.targetname)/rip/0.1/interpacket_delay?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv4=$(address.@)->t_msecs:u32"; %help: short "Minimum delay between outbound RIP packets (msec)"; } interpacket-delay-msecs { %deprecated: "Statement 'interpacket-delay-msecs' is replaced with 'interpacket-delay'"; %set: xrl "$(rip.targetname)/rip/0.1/set_interpacket_delay?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv4=$(address.@)&t_msecs:u32=$(@)"; %get: xrl "$(rip.targetname)/rip/0.1/interpacket_delay?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv4=$(address.@)->t_msecs:u32"; %help: short "Minimum delay between outbound RIP packets"; } authentication { %help: short "RIP packet authentication"; type { %deprecated: "Statement 'type' is obsoleted by 'simple-password' and 'md5'"; %help: short "Authentication type"; %allow: $(@) "none" %help: "No authentication"; %allow: $(@) "simple" %help: "Simple password authentication"; %allow: $(@) "md5" %help: "MD5 authentication"; } password { %deprecated: "Statement 'password' is replaced with 'simple-password'"; %help: short "Authentication password"; } simple-password { %help: short "Simple password authentication key"; %create: xrl "$(rip.targetname)/rip/0.1/set_simple_authentication_key?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv4=$(address.@)&password:txt=$(@)"; %set: xrl "$(rip.targetname)/rip/0.1/set_simple_authentication_key?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv4=$(address.@)&password:txt=$(@)"; %delete: xrl "$(rip.targetname)/rip/0.1/delete_simple_authentication_key?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv4=$(address.@)"; } md5 @ { %help: short "MD5 authentication key"; %allow-range: $(@) "0" "255" %help: "MD5 authentication key ID"; %activate: xrl "$(rip.targetname)/rip/0.1/set_md5_authentication_key?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv4=$(address.@)&key_id:u32=$(@)&password:txt=$(@.password)&start_time:txt=$(@.start-time)&end_time:txt=$(@.end-time)"; %update: xrl "$(rip.targetname)/rip/0.1/set_md5_authentication_key?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv4=$(address.@)&key_id:u32=$(@)&password:txt=$(@.password)&start_time:txt=$(@.start-time)&end_time:txt=$(@.end-time)"; %delete: xrl "$(rip.targetname)/rip/0.1/delete_md5_authentication_key?ifname:txt=$(interface.@)&vifname:txt=$(vif.@)&addr:ipv4=$(address.@)&key_id:u32=$(@)"; password { %help: short "Authentication password"; } start-time { %help: short "Authentication start time (YYYY-MM-DD.HH:MM)"; } end-time { %help: short "Authentication end time (YYYY-MM-DD.HH:MM)"; } } } } } } import { %help: short "Import policy name(s)"; %delete: xrl "policy/policy/0.1/import?protocol:txt=$(rip.targetname)&policies:txt=&modifier:txt="; %set: xrl "policy/policy/0.1/import?protocol:txt=$(rip.targetname)&policies:txt=$(@)&modifier:txt="; } export { %help: short "Export policy name(s)"; %delete: xrl "policy/policy/0.1/export?protocol:txt=$(rip.targetname)&policies:txt=&modifier:txt="; %set: xrl "policy/policy/0.1/export?protocol:txt=$(rip.targetname)&policies:txt=$(@)&modifier:txt="; } } } policy { %create: xrl "$(policy.targetname)/policy/0.1/add_varmap?protocol:txt=rip&variable:txt=network4&type:txt=ipv4net&access:txt=r&id:u32=10"; %create: xrl "$(policy.targetname)/policy/0.1/add_varmap?protocol:txt=rip&variable:txt=nexthop4&type:txt=ipv4nexthop&access:txt=rw&id:u32=11"; %create: xrl "$(policy.targetname)/policy/0.1/add_varmap?protocol:txt=rip&variable:txt=metric&type:txt=u32&access:txt=rw&id:u32=14"; policy-statement @: txt { term @: txt { from { metric { %help: short "Metric value"; %allow-operator: ":" "==" "!=" "<" ">" "<=" ">="; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt=metric $(<>) $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=0&order:txt=$(#)&statement:txt="; } } to { metric { %help: short "Metric value"; %allow-operator: ":" "==" "!=" "<" ">" "<=" ">="; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=1&order:txt=$(#)&statement:txt=metric $(<>) $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=1&order:txt=$(#)&statement:txt="; } } then { metric { %help: short "Set the metric value"; %allow-operator: ":" "=" "sub" "add"; %set: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=2&order:txt=$(#)&statement:txt=metric $(<>) $(@);"; %delete: xrl "$(policy.targetname)/policy/0.1/update_term_block?policy:txt=$(policy-statement.@)&term:txt=$(term.@)&block:u32=2&order:txt=$(#)&statement:txt="; } } } } } xorp/etc/templates/mfea6.cmds0000664000076400007640000000204011421137511016267 0ustar greearbgreearb/* $XORP: xorp/etc/templates/mfea6.cmds,v 1.4 2004/06/02 02:38:41 pavlin Exp $ */ show mfea6 { %command: "" %help: HELP; %module: mfea6; %tag: HELP "Display information about IPv6 MFEA"; } show mfea6 dataflow { %command: "cli_send_processor_xrl -t MFEA_6 $0" %help: HELP; %module: mfea6; %tag: HELP "Display information about MFEA IPv6 dataflow filters"; } show mfea6 interface { %command: "cli_send_processor_xrl -t MFEA_6 $0" %help: HELP; %module: mfea6; %tag: HELP "Display information about MFEA IPv6 interfaces"; } show mfea6 interface address { %command: "cli_send_processor_xrl -t MFEA_6 $0" %help: HELP; %module: mfea6; %tag: HELP "Display information about addresses of MFEA IPv6 interfaces"; } /* * Commented-out, because when we run everything through the rtrmgr, * the MFEA is not used to get the routes from the kernel. show mfea6 mrib { %command: "cli_send_processor_xrl -t MFEA_6 $0" %help: HELP; %module: mfea6; %tag: HELP "Display MRIB IPv6 information inside MFEA"; } */ xorp/mld6igmp/0000775000076400007640000000000011703345405013401 5ustar greearbgreearbxorp/mld6igmp/mld6igmp_node_cli.cc0000664000076400007640000003060211421137511017256 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // MLD6IGMP protocol CLI implementation // #include "mld6igmp_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvx.hh" #include "mld6igmp_group_record.hh" #include "mld6igmp_node.hh" #include "mld6igmp_node_cli.hh" #include "mld6igmp_vif.hh" // // Exported variables // // // Local constants definitions // // // Local structures/classes, typedefs and macros // // // Local variables // // // Local functions prototypes // /** * Mld6igmpNodeCli::Mld6igmpNodeCli: * @mld6igmp_node: The MLD6IGMP node to use. * * Mld6igmpNodeCli constructor. **/ Mld6igmpNodeCli::Mld6igmpNodeCli(Mld6igmpNode& mld6igmp_node) : ProtoNodeCli(mld6igmp_node.family(), mld6igmp_node.module_id()), _mld6igmp_node(mld6igmp_node) { } Mld6igmpNodeCli::~Mld6igmpNodeCli() { stop(); } int Mld6igmpNodeCli::start() { if (! is_enabled()) return (XORP_OK); if (is_up() || is_pending_up()) return (XORP_OK); if (ProtoUnit::start() != XORP_OK) return (XORP_ERROR); if (add_all_cli_commands() != XORP_OK) return (XORP_ERROR); XLOG_INFO("CLI started"); return (XORP_OK); } int Mld6igmpNodeCli::stop() { int ret_code = XORP_OK; if (is_down()) return (XORP_OK); if (delete_all_cli_commands() != XORP_OK) ret_code = XORP_ERROR; XLOG_INFO("CLI stopped"); return (ret_code); } /** * Enable the node operation. * * If an unit is not enabled, it cannot be start, or pending-start. */ void Mld6igmpNodeCli::enable() { ProtoUnit::enable(); XLOG_INFO("CLI 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 Mld6igmpNodeCli::disable() { stop(); ProtoUnit::disable(); XLOG_INFO("CLI disabled"); } int Mld6igmpNodeCli::add_all_cli_commands() { // XXX: command "show" must have been installed by the CLI itself. if (mld6igmp_node().proto_is_igmp()) { add_cli_dir_command("show igmp", "Display information about IGMP"); add_cli_command("show igmp group", "Display information about IGMP group membership", callback(this, &Mld6igmpNodeCli::cli_show_mld6igmp_group)); add_cli_command("show igmp interface", "Display information about IGMP interfaces", callback(this, &Mld6igmpNodeCli::cli_show_mld6igmp_interface)); add_cli_command("show igmp interface address", "Display information about addresses of IGMP interfaces", callback(this, &Mld6igmpNodeCli::cli_show_mld6igmp_interface_address)); } if (mld6igmp_node().proto_is_mld6()) { add_cli_dir_command("show mld", "Display information about MLD"); add_cli_command("show mld group", "Display information about MLD group membership", callback(this, &Mld6igmpNodeCli::cli_show_mld6igmp_group)); add_cli_command("show mld interface", "Display information about MLD interfaces", callback(this, &Mld6igmpNodeCli::cli_show_mld6igmp_interface)); add_cli_command("show mld interface address", "Display information about addresses of MLD interfaces", callback(this, &Mld6igmpNodeCli::cli_show_mld6igmp_interface_address)); } return (XORP_OK); } // // CLI COMMAND: "show mld interface [interface-name]" // CLI COMMAND: "show igmp interface [interface-name]" // // Display information about the interfaces on which MLD/IGMP is configured. // int Mld6igmpNodeCli::cli_show_mld6igmp_interface(const vector& argv) { string interface_name; // Check the optional argument if (argv.size()) { interface_name = argv[0]; if (mld6igmp_node().vif_find_by_name(interface_name.c_str()) == NULL) { cli_print(c_format("ERROR: Invalid interface name: %s\n", interface_name.c_str())); return (XORP_ERROR); } } cli_print(c_format("%-12s %-8s %-15s %7s %7s %6s\n", "Interface", "State", "Querier", "Timeout", "Version", "Groups")); for (uint32_t i = 0; i < mld6igmp_node().maxvifs(); i++) { Mld6igmpVif *mld6igmp_vif = mld6igmp_node().vif_find_by_vif_index(i); if (mld6igmp_vif == NULL) continue; // Test if we should print this entry bool do_print = true; if (interface_name.size()) { do_print = false; if (mld6igmp_vif->name() == interface_name) { do_print = true; } } if (! do_print) continue; string querier_timeout_sec_string; if (mld6igmp_vif->const_other_querier_timer().scheduled()) { TimeVal tv; mld6igmp_vif->const_other_querier_timer().time_remaining(tv); querier_timeout_sec_string = c_format("%d", XORP_INT_CAST(tv.sec())); } else { querier_timeout_sec_string = "None"; } cli_print(c_format("%-12s %-8s %-15s %7s %7d %6u\n", mld6igmp_vif->name().c_str(), mld6igmp_vif->state_str().c_str(), cstring(mld6igmp_vif->querier_addr()), querier_timeout_sec_string.c_str(), mld6igmp_vif->proto_version(), XORP_UINT_CAST(mld6igmp_vif->group_records().size()))); } return (XORP_OK); } // // CLI COMMAND: "show mld interface address [interface-name]" // CLI COMMAND: "show igmp interface address [interface-name]" // // Display information about the addresses of MLD/IGMP interfaces // int Mld6igmpNodeCli::cli_show_mld6igmp_interface_address(const vector& argv) { string interface_name; // Check the optional argument if (argv.size()) { interface_name = argv[0]; if (mld6igmp_node().vif_find_by_name(interface_name) == NULL) { cli_print(c_format("ERROR: Invalid interface name: %s\n", interface_name.c_str())); return (XORP_ERROR); } } cli_print(c_format("%-12s %-15s %-15s\n", "Interface", "PrimaryAddr", "SecondaryAddr")); for (uint32_t i = 0; i < mld6igmp_node().maxvifs(); i++) { Mld6igmpVif *mld6igmp_vif = mld6igmp_node().vif_find_by_vif_index(i); if (mld6igmp_vif == NULL) continue; // Test if we should print this entry bool do_print = true; if (interface_name.size()) { do_print = false; if (mld6igmp_vif->name() == interface_name) { do_print = true; } } if (! do_print) continue; // // Create a list with all secondary addresses // list secondary_addr_list; list::const_iterator vif_addr_iter; for (vif_addr_iter = mld6igmp_vif->addr_list().begin(); vif_addr_iter != mld6igmp_vif->addr_list().end(); ++vif_addr_iter) { const VifAddr& vif_addr = *vif_addr_iter; if (vif_addr.addr() == mld6igmp_vif->primary_addr()) continue; secondary_addr_list.push_back(vif_addr.addr()); } cli_print(c_format("%-12s %-15s %-15s\n", mld6igmp_vif->name().c_str(), cstring(mld6igmp_vif->primary_addr()), (secondary_addr_list.size())? cstring(secondary_addr_list.front()): "")); // Pop the first secondary address if (secondary_addr_list.size()) secondary_addr_list.pop_front(); // // Print the rest of the secondary addresses // list::iterator secondary_addr_iter; for (secondary_addr_iter = secondary_addr_list.begin(); secondary_addr_iter != secondary_addr_list.end(); ++secondary_addr_iter) { IPvX& secondary_addr = *secondary_addr_iter; cli_print(c_format("%-12s %-15s %-15s\n", " ", " ", cstring(secondary_addr))); } } return (XORP_OK); } // // CLI COMMAND: "show mld group [group-name [...]]" // CLI COMMAND: "show igmp group [group-name [...]]" // // Display information about MLD/IGMP group membership. // int Mld6igmpNodeCli::cli_show_mld6igmp_group(const vector& argv) { vector groups; // Check the (optional) arguments, and create an array of groups to test for (size_t i = 0; i < argv.size(); i++) { try { IPvX g(argv[i].c_str()); if (g.af() != family()) { cli_print(c_format("ERROR: Address with invalid address family: %s\n", argv[i].c_str())); return (XORP_ERROR); } if (! g.is_multicast()) { cli_print(c_format("ERROR: Not a multicast address: %s\n", argv[i].c_str())); return (XORP_ERROR); } groups.push_back(g); } catch (InvalidString) { cli_print(c_format("ERROR: Invalid IP address: %s\n", argv[i].c_str())); return (XORP_ERROR); } } cli_print(c_format("%-12s %-15s %-15s %-12s %7s %1s %5s\n", "Interface", "Group", "Source", "LastReported", "Timeout", "V", "State")); for (uint32_t i = 0; i < mld6igmp_node().maxvifs(); i++) { const Mld6igmpVif *mld6igmp_vif = mld6igmp_node().vif_find_by_vif_index(i); if (mld6igmp_vif == NULL) continue; Mld6igmpGroupSet::const_iterator group_iter; for (group_iter = mld6igmp_vif->group_records().begin(); group_iter != mld6igmp_vif->group_records().end(); ++group_iter) { const Mld6igmpGroupRecord *group_record = group_iter->second; Mld6igmpSourceSet::const_iterator source_iter; int version = 0; string state; // Test if we should print this entry bool do_print = true; if (groups.size()) { do_print = false; for (size_t j = 0; j < groups.size(); j++) { if (groups[j] == group_record->group()) { do_print = true; break; } } } if (! do_print) continue; // Calcuate the group entry version do { version = 0; if (mld6igmp_vif->is_igmpv1_mode(group_record)) { version = 1; break; } if (mld6igmp_vif->is_igmpv2_mode(group_record)) { version = 2; break; } if (mld6igmp_vif->is_igmpv3_mode(group_record)) { version = 3; break; } if (mld6igmp_vif->is_mldv1_mode(group_record)) { version = 1; break; } if (mld6igmp_vif->is_mldv2_mode(group_record)) { version = 2; break; } break; } while (false); XLOG_ASSERT(version > 0); // // The state: // - "I" = INCLUDE (for group entry) // - "E" = EXCLUDE (for group entry) // - "F" = Forward (for source entry) // - "D" = Don't forward (for source entry) // // The group state if (group_record->is_include_mode()) state = "I"; if (group_record->is_exclude_mode()) state = "E"; // Print the group-specific output cli_print(c_format("%-12s %-15s %-15s %-12s %7d %1d %5s\n", mld6igmp_vif->name().c_str(), cstring(group_record->group()), cstring(IPvX::ZERO(family())), cstring(group_record->last_reported_host()), XORP_INT_CAST(group_record->timeout_sec()), version, state.c_str())); // Print the sources to forward state = "F"; for (source_iter = group_record->do_forward_sources().begin(); source_iter != group_record->do_forward_sources().end(); ++source_iter) { const Mld6igmpSourceRecord *source_record = source_iter->second; cli_print(c_format("%-12s %-15s %-15s %-12s %7d %1d %5s\n", mld6igmp_vif->name().c_str(), cstring(group_record->group()), cstring(source_record->source()), cstring(group_record->last_reported_host()), XORP_INT_CAST(source_record->timeout_sec()), version, state.c_str())); } // Print the sources not to forward state = "D"; for (source_iter = group_record->dont_forward_sources().begin(); source_iter != group_record->dont_forward_sources().end(); ++source_iter) { const Mld6igmpSourceRecord *source_record = source_iter->second; cli_print(c_format("%-12s %-15s %-15s %-12s %7d %1d %5s\n", mld6igmp_vif->name().c_str(), cstring(group_record->group()), cstring(source_record->source()), cstring(group_record->last_reported_host()), XORP_INT_CAST(source_record->timeout_sec()), version, state.c_str())); } } } return (XORP_OK); } xorp/mld6igmp/mld6igmp_proto.cc0000664000076400007640000007161311540225530016655 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // Internet Group Management Protocol implementation. // IGMPv1, IGMPv2 (RFC 2236), and IGMPv3 (RFC 3376). // // AND // // Multicast Listener Discovery protocol implementation. // MLDv1 (RFC 2710) and MLDv2 (RFC 3810). // #include "mld6igmp_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvx.hh" #include "mld6igmp_vif.hh" /** * Mld6igmpVif::mld6igmp_membership_query_recv: * @src: The message source address. * @dst: The message destination address. * @message_type: The message type. * @max_resp_code: The Maximum Response Code from the MLD/IGMP header. * @group_address: The Group Address from the MLD/IGMP message. * @buffer: The buffer with the rest of the message. * * Receive and process IGMP_MEMBERSHIP_QUERY/MLD_LISTENER_QUERY message * from another router. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int Mld6igmpVif::mld6igmp_membership_query_recv(const IPvX& src, const IPvX& dst, uint8_t message_type, uint16_t max_resp_code, const IPvX& group_address, buffer_t *buffer) { int message_version = 0; // Ignore my own queries if (mld6igmp_node().is_my_addr(src)) return (XORP_ERROR); // // Determine the protocol version of the Query message // if (proto_is_igmp()) { // // The IGMP version of a Membership Query message is: // - IGMPv1 Query: length = 8 AND Max Resp Code field is zero // - IGMPv2 Query: length = 8 AND Max Resp Code field is non-zero // - IGMPv3 Query: length >= 12 // do { // // Note that the Query processing so far has decoded the message // up to the group address included (i.e., 8 octets), hence // we add-back the size of the decoded portion. // size_t data_size = BUFFER_DATA_SIZE(buffer) + IGMP_MINLEN; if ((data_size == IGMP_MINLEN) && (max_resp_code == 0)) { message_version = IGMP_V1; break; } if ((data_size == IGMP_MINLEN) && (max_resp_code != 0)) { message_version = IGMP_V2; break; } if (data_size >= IGMP_V3_QUERY_MINLEN) { message_version = IGMP_V3; break; } // // Silently ignore all other Query messages that don't match // any of the above conditions. // return (XORP_ERROR); } while (false); XLOG_ASSERT(message_version > 0); // // Query version consistency check. // If there is mismatch, then drop the message. // See RFC 3376 Section 7.3.1, and RFC 3810 Section 8.3.1. // if (mld6igmp_query_version_consistency_check(src, dst, message_type, message_version) != XORP_OK) { return (XORP_ERROR); } } if (proto_is_mld6()) { // // The MLD version of a Membership Query message is: // - MLDv1 Query: length = 24 // - MLDv2 Query: length >= 28 // do { // // Note that the Query processing so far has decoded the message // up to the group address included (i.e., 24 octets), hence // we add-back the size of the decoded portion. // size_t data_size = BUFFER_DATA_SIZE(buffer) + MLD_MINLEN; if (data_size == MLD_MINLEN) { message_version = MLD_V1; break; } if (data_size >= MLD_V2_QUERY_MINLEN) { message_version = MLD_V2; break; } // // Silently ignore all other Query messages that don't match // any of the above conditions. // return (XORP_ERROR); } while (false); XLOG_ASSERT(message_version > 0); // // Query version consistency check. // If there is mismatch, then drop the message. // See RFC 3376 Section 7.3.1, and RFC 3810 Section 8.3.1. // if (mld6igmp_query_version_consistency_check(src, dst, message_type, message_version) != XORP_OK) { return (XORP_ERROR); } } XLOG_ASSERT(message_version > 0); // // Compare this querier address with my address. // XXX: Here we should compare the old and new querier // addresses, but we don't really care. // XLOG_ASSERT(primary_addr() != IPvX::ZERO(family())); if (src < primary_addr()) { // Eventually a new querier _query_timer.unschedule(); set_querier_addr(src); set_i_am_querier(false); TimeVal other_querier_present_interval = effective_query_interval() * effective_robustness_variable() + query_response_interval().get() / 2; _other_querier_timer = mld6igmp_node().eventloop().new_oneoff_after( other_querier_present_interval, callback(this, &Mld6igmpVif::other_querier_timer_timeout)); } // // XXX: if this is IGMPv3 or MLDv2 Query, then process separately // the rest the message. // if ((proto_is_igmp() && (message_version >= IGMP_V3)) || (proto_is_mld6() && (message_version >= MLD_V2))) { mld6igmp_ssm_membership_query_recv(src, dst, message_type, max_resp_code, group_address, buffer); return (XORP_OK); } // // From RFC 2236: // "When a non-Querier receives a Group-Specific Query message, if its // existing group membership timer is greater than [Last Member Query // Count] times the Max Response Time specified in the message, it sets // its group membership timer to that value." // // // From RFC 2710: // "When a router in Non-Querier state receives a Multicast-Address- // Specific Query, if its timer value for the identified multicast // address is greater than [Last Listener Query Count] times the Maximum // Response Delay specified in the message, it sets the address's timer // to that latter value." // if ( (! group_address.is_zero()) && (max_resp_code != 0) && (! i_am_querier())) { uint32_t timer_scale = mld6igmp_constant_timer_scale(); TimeVal received_resp_tv; // "Last Member Query Count" / "Last Listener Query Count" received_resp_tv = TimeVal(effective_robustness_variable() * max_resp_code, 0); received_resp_tv = received_resp_tv / timer_scale; _group_records.lower_group_timer(group_address, received_resp_tv); } return (XORP_OK); } /** * Mld6igmpVif::mld6igmp_ssm_membership_query_recv: * @src: The message source address. * @dst: The message destination address. * @message_type: The message type. * @max_resp_code: The Maximum Response Code from the MLD/IGMP header. * @group_address: The Group Address from the MLD/IGMP message. * @buffer: The buffer with the rest of the message. * * Receive and process IGMPv3/MLDv2 IGMP_MEMBERSHIP_QUERY/MLD_LISTENER_QUERY * message from another router. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int Mld6igmpVif::mld6igmp_ssm_membership_query_recv(const IPvX& src, const IPvX& dst, uint8_t message_type, uint16_t max_resp_code, const IPvX& group_address, buffer_t *buffer) { bool s_flag = false; uint8_t qrv = 0; uint8_t qqic = 0; uint16_t sources_n = 0; TimeVal max_resp_time, qqi; set sources; string error_msg; // // Decode the Max Resp Code // if (proto_is_igmp()) { decode_exp_time_code8(max_resp_code, max_resp_time, mld6igmp_constant_timer_scale()); } if (proto_is_mld6()) { decode_exp_time_code16(max_resp_code, max_resp_time, mld6igmp_constant_timer_scale()); } // // Decode the rest of the message header // BUFFER_GET_OCTET(qrv, buffer); BUFFER_GET_OCTET(qqic, buffer); BUFFER_GET_HOST_16(sources_n, buffer); if (proto_is_igmp()) { s_flag = IGMP_SFLAG(qrv); qrv = IGMP_QRV(qrv); } if (proto_is_mld6()) { s_flag = MLD_SFLAG(qrv); qrv = MLD_QRV(qrv); } decode_exp_time_code8(qqic, qqi, 1); // // Check the remaining size of the message // if (BUFFER_DATA_SIZE(buffer) < sources_n * IPvX::addr_bytelen(family())) { error_msg = c_format("RX %s from %s to %s on vif %s: " "source addresses array size too short" "(received %u expected at least %u)", proto_message_type2ascii(message_type), cstring(src), cstring(dst), name().c_str(), XORP_UINT_CAST(BUFFER_DATA_SIZE(buffer)), XORP_UINT_CAST(sources_n * IPvX::addr_bytelen(family()))); XLOG_WARNING("%s", error_msg.c_str()); return (XORP_ERROR); } // // Decode the array of source addresses // while (sources_n != 0) { IPvX ipvx(family()); BUFFER_GET_IPVX(family(), ipvx, buffer); sources.insert(ipvx); sources_n--; } // // Adopt the Querier's Robustness Variable and Query Interval // if (! i_am_querier()) { if (qrv != 0) { set_effective_robustness_variable(qrv); } else { set_effective_robustness_variable(configured_robust_count().get()); } if (qqi != TimeVal::ZERO()) { set_effective_query_interval(qqi); } else { set_effective_query_interval(configured_query_interval().get()); } } // // Lower the group and source timers // if (! s_flag) { if (sources.empty()) { // XXX: Q(G) Query _group_records.lower_group_timer(group_address, last_member_query_time()); } else { // XXX: Q(G, A) Query _group_records.lower_source_timer(group_address, sources, last_member_query_time()); } } return (XORP_OK); rcvlen_error: XLOG_UNREACHABLE(); error_msg = c_format("RX %s from %s to %s on vif %s: " "some fields are too short", proto_message_type2ascii(message_type), cstring(src), cstring(dst), name().c_str()); XLOG_WARNING("%s", error_msg.c_str()); return (XORP_ERROR); } /** * Mld6igmpVif::mld6igmp_membership_report_recv: * @src: The message source address. * @dst: The message destination address. * @message_type: The message type. * @max_resp_code: The Maximum Response Code from the MLD/IGMP header. * @group_address: The Group Address from the MLD/IGMP message. * @buffer: The buffer with the rest of the message. * * Receive and process * (IGMP_V1_MEMBERSHIP_REPORT or IGMP_V2_MEMBERSHIP_REPORT)/MLD_LISTENER_REPORT * message from a host. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int Mld6igmpVif::mld6igmp_membership_report_recv(const IPvX& src, const IPvX& dst, uint8_t message_type, uint16_t max_resp_code, const IPvX& group_address, buffer_t *buffer) { Mld6igmpGroupRecord *group_record = NULL; UNUSED(dst); // The group address must be a valid multicast address if (! group_address.is_multicast()) { XLOG_WARNING("RX %s from %s to %s on vif %s: " "the group address %s is not " "valid multicast address", proto_message_type2ascii(message_type), cstring(src), cstring(dst), name().c_str(), cstring(group_address)); return (XORP_ERROR); } set no_sources; // XXX: empty set group_records().process_mode_is_exclude(group_address, no_sources, src); // // Check whether an older Membership report has been received // int message_version = 0; if (proto_is_igmp()) { switch (message_type) { case IGMP_V1_MEMBERSHIP_REPORT: message_version = IGMP_V1; break; case IGMP_V2_MEMBERSHIP_REPORT: message_version = IGMP_V2; break; case IGMP_V3_MEMBERSHIP_REPORT: message_version = IGMP_V3; break; default: message_version = IGMP_V2; break; } } if (proto_is_mld6()) { switch (message_type) { case MLD_LISTENER_REPORT: message_version = MLD_V1; break; case MLDV2_LISTENER_REPORT: message_version = MLD_V2; break; default: message_version = MLD_V1; break; } } XLOG_ASSERT(message_version > 0); group_record = _group_records.find_group_record(group_address); XLOG_ASSERT(group_record != NULL); group_record->received_older_membership_report(message_version); UNUSED(max_resp_code); UNUSED(buffer); return (XORP_OK); } /** * Mld6igmpVif::mld6igmp_leave_group_recv: * @src: The message source address. * @dst: The message destination address. * @message_type: The message type. * @max_resp_code: The Maximum Response Code from the MLD/IGMP header. * @group_address: The Group Address from the MLD/IGMP message. * @buffer: The buffer with the rest of the message. * * Receive and process IGMP_V2_LEAVE_GROUP/MLD_LISTENER_DONE message * from a host. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int Mld6igmpVif::mld6igmp_leave_group_recv(const IPvX& src, const IPvX& dst, uint8_t message_type, uint16_t max_resp_code, const IPvX& group_address, buffer_t *buffer) { Mld6igmpGroupRecord *group_record = NULL; string dummy_error_msg; UNUSED(dst); UNUSED(message_type); // The group address must be a valid multicast address if (! group_address.is_multicast()) { XLOG_WARNING("RX %s from %s to %s on vif %s: " "the group address %s is not " "valid multicast address", proto_message_type2ascii(message_type), cstring(src), cstring(dst), name().c_str(), cstring(group_address)); return (XORP_ERROR); } // // Find if we already have an entry for this group // group_record = _group_records.find_group_record(group_address); if (group_record == NULL) { // Nothing found. Ignore. return (XORP_OK); } // // Group found // if (is_igmpv1_mode(group_record)) { // // Ignore this 'Leave Group' message because this // group has IGMPv1 hosts members. // return (XORP_OK); } set no_sources; // XXX: empty set group_records().process_change_to_include_mode(group_address, no_sources, src); return (XORP_OK); UNUSED(max_resp_code); UNUSED(buffer); return (XORP_OK); } /** * Mld6igmpVif::mld6igmp_ssm_membership_report_recv: * @src: The message source address. * @dst: The message destination address. * @message_type: The message type. * @buffer: The buffer with the rest of the message. * * Receive and process IGMP_V3_MEMBERSHIP_REPORT/MLDV2_LISTENER_REPORT * message from a host. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int Mld6igmpVif::mld6igmp_ssm_membership_report_recv(const IPvX& src, const IPvX& dst, uint8_t message_type, buffer_t *buffer) { uint16_t group_records_n = 0; string error_msg; typedef pair > gs_record; // XXX: a handy typedef list mode_is_include_groups; list mode_is_exclude_groups; list change_to_include_mode_groups; list change_to_exclude_mode_groups; list allow_new_sources_groups; list block_old_sources_groups; list::iterator gs_iter; // // Decode the rest of the message header // BUFFER_GET_SKIP(2, buffer); // The 'Reserved' field BUFFER_GET_HOST_16(group_records_n, buffer); // // Decode the array of group records // while (group_records_n != 0) { uint8_t record_type; uint8_t aux_data_len; uint16_t sources_n; IPvX group_address(family()); set source_addresses; list* gs_record_ptr = NULL; BUFFER_GET_OCTET(record_type, buffer); BUFFER_GET_OCTET(aux_data_len, buffer); BUFFER_GET_HOST_16(sources_n, buffer); BUFFER_GET_IPVX(family(), group_address, buffer); // Decode the array of source addresses while (sources_n != 0) { IPvX ipvx(family()); BUFFER_GET_IPVX(family(), ipvx, buffer); source_addresses.insert(ipvx); sources_n--; } // XXX: Skip the 'Auxiliary Data', because we don't use it BUFFER_GET_SKIP(aux_data_len, buffer); // // Select the appropriate set, and add the group and the sources to it // if (proto_is_igmp()) { switch (record_type) { case IGMP_MODE_IS_INCLUDE: gs_record_ptr = &mode_is_include_groups; break; case IGMP_MODE_IS_EXCLUDE: gs_record_ptr = &mode_is_exclude_groups; break; case IGMP_CHANGE_TO_INCLUDE_MODE: gs_record_ptr = &change_to_include_mode_groups; break; case IGMP_CHANGE_TO_EXCLUDE_MODE: gs_record_ptr = &change_to_exclude_mode_groups; break; case IGMP_ALLOW_NEW_SOURCES: gs_record_ptr = &allow_new_sources_groups; break; case IGMP_BLOCK_OLD_SOURCES: gs_record_ptr = &block_old_sources_groups; break; default: break; } } if (proto_is_mld6()) { switch (record_type) { case MLD_MODE_IS_INCLUDE: gs_record_ptr = &mode_is_include_groups; break; case MLD_MODE_IS_EXCLUDE: gs_record_ptr = &mode_is_exclude_groups; break; case MLD_CHANGE_TO_INCLUDE_MODE: gs_record_ptr = &change_to_include_mode_groups; break; case MLD_CHANGE_TO_EXCLUDE_MODE: gs_record_ptr = &change_to_exclude_mode_groups; break; case MLD_ALLOW_NEW_SOURCES: gs_record_ptr = &allow_new_sources_groups; break; case MLD_BLOCK_OLD_SOURCES: gs_record_ptr = &block_old_sources_groups; break; default: break; } } if (gs_record_ptr != NULL) { gs_record_ptr->push_back(make_pair(group_address, source_addresses)); } else { error_msg = c_format("RX %s from %s to %s on vif %s: " "unrecognized record type %d (ignored)", proto_message_type2ascii(message_type), cstring(src), cstring(dst), name().c_str(), record_type); XLOG_WARNING("%s", error_msg.c_str()); } group_records_n--; } // // Process the records // for (gs_iter = mode_is_include_groups.begin(); gs_iter != mode_is_include_groups.end(); ++gs_iter) { group_records().process_mode_is_include(gs_iter->first, gs_iter->second, src); } for (gs_iter = mode_is_exclude_groups.begin(); gs_iter != mode_is_exclude_groups.end(); ++gs_iter) { group_records().process_mode_is_exclude(gs_iter->first, gs_iter->second, src); } for (gs_iter = change_to_include_mode_groups.begin(); gs_iter != change_to_include_mode_groups.end(); ++gs_iter) { group_records().process_change_to_include_mode(gs_iter->first, gs_iter->second, src); } for (gs_iter = change_to_exclude_mode_groups.begin(); gs_iter != change_to_exclude_mode_groups.end(); ++gs_iter) { group_records().process_change_to_exclude_mode(gs_iter->first, gs_iter->second, src); } for (gs_iter = allow_new_sources_groups.begin(); gs_iter != allow_new_sources_groups.end(); ++gs_iter) { group_records().process_allow_new_sources(gs_iter->first, gs_iter->second, src); } for (gs_iter = block_old_sources_groups.begin(); gs_iter != block_old_sources_groups.end(); ++gs_iter) { group_records().process_block_old_sources(gs_iter->first, gs_iter->second, src); } return (XORP_OK); rcvlen_error: error_msg = c_format("RX %s from %s to %s on vif %s: " "some fields are too short", proto_message_type2ascii(message_type), cstring(src), cstring(dst), name().c_str()); XLOG_WARNING("%s", error_msg.c_str()); return (XORP_ERROR); } /** * Mld6igmpVif::other_querier_timer_timeout: * * Timeout: the previous querier has expired. I will become the querier. **/ void Mld6igmpVif::other_querier_timer_timeout() { string dummy_error_msg; if (primary_addr() == IPvX::ZERO(family())) { // XXX: the vif address is unknown; this cannot happen if the // vif status is UP. XLOG_ASSERT(! is_up()); return; } set_querier_addr(primary_addr()); set_i_am_querier(true); // // Now I am the querier. Send a general membership query. // TimeVal max_resp_time = query_response_interval().get(); set no_sources; // XXX: empty set mld6igmp_query_send(primary_addr(), IPvX::MULTICAST_ALL_SYSTEMS(family()), max_resp_time, IPvX::ZERO(family()), // XXX: ANY no_sources, false, dummy_error_msg); _startup_query_count = 0; // XXX: not a startup case _query_timer = mld6igmp_node().eventloop().new_oneoff_after( effective_query_interval(), callback(this, &Mld6igmpVif::query_timer_timeout)); } /** * Mld6igmpVif::query_timer_timeout: * * Timeout: time to send a membership query. **/ void Mld6igmpVif::query_timer_timeout() { TimeVal interval; string dummy_error_msg; if (! i_am_querier()) return; // I am not the querier anymore. Ignore. if (primary_addr() == IPvX::ZERO(family())) { XLOG_WARNING("%s: Called query_timer_timeout, but primary_addr" " is ZERO. Not sending any pkt.\n", name().c_str()); return; } // // Send a general membership query // TimeVal max_resp_time = query_response_interval().get(); set no_sources; // XXX: empty set mld6igmp_query_send(primary_addr(), IPvX::MULTICAST_ALL_SYSTEMS(family()), max_resp_time, IPvX::ZERO(family()), // XXX: ANY no_sources, false, dummy_error_msg); if (_startup_query_count > 0) _startup_query_count--; if (_startup_query_count > 0) { // "Startup Query Interval" interval = effective_query_interval() / 4; } else { interval = effective_query_interval(); } _query_timer = mld6igmp_node().eventloop().new_oneoff_after( interval, callback(this, &Mld6igmpVif::query_timer_timeout)); } /** * mld6igmp_query_version_consistency_check: * @src: The message source address. * @dst: The message destination address. * @message_type: The type of the MLD/IGMP message. * @message_version: The protocol version of the received Query message: * (IGMP_V1, IGMP_V2, IGMP_V3 for IGMP) or (MLD_V1, MLD_V2 for MLD). * * Check for MLD/IGMP protocol version interface configuration consistency. * For example, if the received Query message was IGMPv1, a correctly * configured local interface must be operating in IGMPv1 mode. * Similarly, if the local interface is operating in IGMPv1 mode, * all other neighbor routers (for that interface) must be * operating in IGMPv1 as well. * * Return value: %XORP_OK if consistency, otherwise %XORP_ERROR. **/ int Mld6igmpVif::mld6igmp_query_version_consistency_check(const IPvX& src, const IPvX& dst, uint8_t message_type, int message_version) { string proto_name, mode_config, mode_received; UNUSED(src); UNUSED(dst); UNUSED(message_type); if (message_version == proto_version()) return (XORP_OK); if (proto_is_igmp()) proto_name = "IGMP"; if (proto_is_mld6()) proto_name = "MLD"; mode_config = c_format("%sv%u", proto_name.c_str(), proto_version()); mode_received = c_format("%sv%u", proto_name.c_str(), message_version); // TODO: rate-limit the warning XLOG_WARNING("RX %s from %s to %s on vif %s: " "this interface is in %s mode, but received %s message", proto_message_type2ascii(message_type), cstring(src), cstring(dst), name().c_str(), mode_config.c_str(), mode_received.c_str()); XLOG_WARNING("Please configure properly all routers on " "that subnet to use same %s version", proto_name.c_str()); return (XORP_ERROR); } void Mld6igmpVif::set_configured_query_interval_cb(TimeVal v) { set_effective_query_interval(v); } void Mld6igmpVif::set_effective_query_interval(const TimeVal& v) { _effective_query_interval = v; recalculate_effective_query_interval(); } void Mld6igmpVif::recalculate_effective_query_interval() { recalculate_group_membership_interval(); recalculate_older_version_host_present_interval(); } void Mld6igmpVif::set_query_last_member_interval_cb(TimeVal v) { UNUSED(v); recalculate_last_member_query_time(); } void Mld6igmpVif::set_query_response_interval_cb(TimeVal v) { UNUSED(v); recalculate_group_membership_interval(); recalculate_older_version_host_present_interval(); } void Mld6igmpVif::set_configured_robust_count_cb(uint32_t v) { set_effective_robustness_variable(v); } void Mld6igmpVif::set_effective_robustness_variable(uint32_t v) { _effective_robustness_variable = v; recalculate_effective_robustness_variable(); } void Mld6igmpVif::recalculate_effective_robustness_variable() { recalculate_group_membership_interval(); recalculate_last_member_query_count(); recalculate_older_version_host_present_interval(); } void Mld6igmpVif::recalculate_last_member_query_count() { _last_member_query_count = effective_robustness_variable(); recalculate_last_member_query_time(); } void Mld6igmpVif::recalculate_group_membership_interval() { _group_membership_interval = effective_query_interval() * effective_robustness_variable() + query_response_interval().get(); } void Mld6igmpVif::recalculate_last_member_query_time() { _last_member_query_time = query_last_member_interval().get() * last_member_query_count(); } void Mld6igmpVif::recalculate_older_version_host_present_interval() { _older_version_host_present_interval = effective_query_interval() * effective_robustness_variable() + query_response_interval().get(); } void Mld6igmpVif::restore_effective_variables() { // Restore the default Query Interval and Robustness Variable set_effective_robustness_variable(configured_robust_count().get()); set_effective_query_interval(configured_query_interval().get()); } void Mld6igmpVif::decode_exp_time_code8(uint8_t code, TimeVal& timeval, uint32_t timer_scale) { uint32_t decoded_time = 0; // // From RFC 3376 Section 4.1.1, and RFC 3810 Section 5.1.9: // // If Code < 128, Time = Code // // If Code >= 128, Code represents a floating-point value as follows: // // 0 1 2 3 4 5 6 7 // +-+-+-+-+-+-+-+-+ // |1| exp | mant | // +-+-+-+-+-+-+-+-+ // // Time = (mant | 0x10) << (exp + 3) // if (code < 128) { decoded_time = code; } else { uint8_t mant = code & 0xf; uint8_t exp = (code >> 4) & 0x7; decoded_time = (mant | 0x10) << (exp + 3); } timeval = TimeVal(decoded_time, 0); timeval = timeval / timer_scale; } void Mld6igmpVif::decode_exp_time_code16(uint16_t code, TimeVal& timeval, uint32_t timer_scale) { uint32_t decoded_time = 0; // // From RFC 3810 Section 5.1.9: // // If Code < 32768, Time = Code // // If Code >= 32768, Code represents a floating-point value as follows: // // 0 1 2 3 4 5 6 7 8 9 A B C D E F // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // |1| exp | mant | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // // Time = (mant | 0x1000) << (exp + 3) // if (code < 32768) { decoded_time = code; } else { uint8_t mant = code & 0xfff; uint8_t exp = (code >> 12) & 0x7; decoded_time = (mant | 0x1000) << (exp + 3); } timeval = TimeVal(decoded_time, 0); timeval = timeval / timer_scale; } void Mld6igmpVif::encode_exp_time_code8(const TimeVal& timeval, uint8_t& code, uint32_t timer_scale) { TimeVal scaled_max_resp_time = timeval * timer_scale; uint32_t decoded_time = scaled_max_resp_time.sec(); code = 0; // // From RFC 3376 Section 4.1.1, and RFC 3810 Section 5.1.9: // // If Code < 128, Time = Code // // If Code >= 128, Code represents a floating-point value as follows: // // 0 1 2 3 4 5 6 7 // +-+-+-+-+-+-+-+-+ // |1| exp | mant | // +-+-+-+-+-+-+-+-+ // // Time = (mant | 0x10) << (exp + 3) // if (decoded_time < 128) { code = decoded_time; } else { uint8_t mant = 0; uint8_t exp = 0; // Calculate the "mant" and the "exp" while ((decoded_time >> (exp + 3)) > 0x1f) { exp++; } mant = (decoded_time >> (exp + 3)) & 0xf; code = 0x80 | (exp << 4) | mant; } } void Mld6igmpVif::encode_exp_time_code16(const TimeVal& timeval, uint16_t& code, uint32_t timer_scale) { TimeVal scaled_max_resp_time = timeval * timer_scale; uint32_t decoded_time = scaled_max_resp_time.sec(); code = 0; // // From RFC 3810 Section 5.1.9: // // If Code < 32768, Time = Code // // If Code >= 32768, Code represents a floating-point value as follows: // // 0 1 2 3 4 5 6 7 8 9 A B C D E F // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // |1| exp | mant | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // // Time = (mant | 0x1000) << (exp + 3) // if (decoded_time < 32768) { code = decoded_time; } else { uint8_t mant = 0; uint8_t exp = 0; // Calculate the "mant" and the "exp" while ((decoded_time >> (exp + 3)) > 0x1fff) { exp++; } mant = (decoded_time >> (exp + 3)) & 0xfff; code = 0x8000 | (exp << 12) | mant; } } xorp/mld6igmp/TODO0000664000076400007640000000360311421137511014065 0ustar greearbgreearb# # $XORP: xorp/mld6igmp/TODO,v 1.20 2007/03/14 01:52:03 pavlin Exp $ # * Implement RFC 3590 "Source Address Selection for the Multicast Listener Discovery (MLD) Protocol". * Implement graceful stopping of node and/or a vif. I.e., perform protocol-related operations to inform other nodes that this node/vif is stopping operations (if there are such operations). * Add vif manager config similar to PIM. * Add Mld6igmpConfig class to Mld6igmpNode. * Add Mld6igmpNode::pending_down() and Mld6igmpVif::pending_down() similar to PIM. * When a protocol registers with MLD6IGMP, that protocol should try periodically the registration if MLD6IGMP has not been started yet (applies to PIM as well). Similarly, protocols that register with MFEA should retry the registration if the MFEA is not UP yet (applies to MLD6IGMP, PIM) * When adding a protocol (for membership info), the return messages should use vif_name instead of vif_index. Also, check that in add/delete_protocol, the vif_name and vif_index match. Similar match check should be applied for all XRLs in MFEA, MLD6IGMP and PIM * The "State" in "show igmp interface" should be "Up/Down" instead of "UP/DOWN" ?? (for consistency with Juniper??) * Change all (Foo *)casting to static_cast<> * Replace all proto_family with family ? (in all mcast-related directories) * The return XrlCmdError values in mld6igmp_proto_access.hh are probably bogus. * Initialize/setup mld6igmp_vif::vif_index() and vif_name(). * Instead of using Join/Prune in the context of IGMP/MLD6, use Join/Leave instead? * The IGMP spec must say that the Group Address in Membership Report and Leave Group messages must be a valid multicast address. * In mld6igmp_recv(), if the mld6igmp_vif interface is NULL, e.g. the interface is not configured, then print a warning or at least a message?? Currently, the program will crash (XLOG_UNREACHABLE()). xorp/mld6igmp/mld6igmp_group_record.hh0000664000076400007640000003446111540224230020212 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/mld6igmp/mld6igmp_group_record.hh,v 1.23 2008/10/02 21:57:43 bms Exp $ #ifndef __MLD6IGMP_MLD6IGMP_GROUP_RECORD_HH__ #define __MLD6IGMP_MLD6IGMP_GROUP_RECORD_HH__ // // IGMP and MLD group record. // #include "libxorp/ipvx.hh" #include "libxorp/timer.hh" #include "mld6igmp_source_record.hh" // // Constants definitions // // // Structures/classes, typedefs and macros // class Mld6igmpVif; /** * @short A class to store information about multicast group membership. */ class Mld6igmpGroupRecord { public: /** * Constructor for a given vif and group address. * * @param mld6igmp_vif the interface this entry belongs to. * @param group the multicast group address. */ Mld6igmpGroupRecord(Mld6igmpVif& mld6igmp_vif, const IPvX& group); /** * Destructor. */ ~Mld6igmpGroupRecord(); /** * Get the vif this entry belongs to. * * @return a reference to the vif this entry belongs to. */ Mld6igmpVif& mld6igmp_vif() const { return (_mld6igmp_vif); } /** * Get the multicast group address. * * @return the multicast group address. */ const IPvX& group() const { return (_group); } /** * Get the corresponding event loop. * * @return the corresponding event loop. */ EventLoop& eventloop(); /** * Test whether the filter mode is INCLUDE. * * @return true if the filter mode is INCLUDE. */ bool is_include_mode() const { return (_is_include_mode); } /** * Test whether the filter mode is EXCLUDE. * * @return true if the filter mode is EXCLUDE. */ bool is_exclude_mode() const { return (! _is_include_mode); } /** * Set the filter mode to INCLUDE. */ void set_include_mode() { _is_include_mode = true; } /** * Set the filter mode to EXCLUDE. */ void set_exclude_mode() { _is_include_mode = false; } /** * Test whether the entry is unused. * * @return true if the entry is unused, otherwise false. */ bool is_unused() const; /** * Find a source that should be forwarded. * * @param source the source address. * @return the corresponding source record (@ref Mld6igmpSourceRecord) * if found, otherwise NULL. */ Mld6igmpSourceRecord* find_do_forward_source(const IPvX& source); /** * Find a source that should not be forwarded. * * @param source the source address. * @return the corresponding source record (@ref Mld6igmpSourceRecord) * if found, otherwise NULL. */ Mld6igmpSourceRecord* find_dont_forward_source(const IPvX& source); /** * Get a reference to the set of sources to forward. * * @return a reference to the set of sources to forward. */ const Mld6igmpSourceSet& do_forward_sources() const { return (_do_forward_sources); } /** * Get a reference to the set of sources not to forward. * * @return a reference to the set of sources not to forward. */ const Mld6igmpSourceSet& dont_forward_sources() const { return (_dont_forward_sources); } /** * Process MODE_IS_INCLUDE report. * * @param sources the source addresses. * @param last_reported_host the address of the host that last reported * as member. */ void process_mode_is_include(const set& sources, const IPvX& last_reported_host); /** * Process MODE_IS_EXCLUDE report. * * @param sources the source addresses. * @param last_reported_host the address of the host that last reported * as member. */ void process_mode_is_exclude(const set& sources, const IPvX& last_reported_host); /** * Process CHANGE_TO_INCLUDE_MODE report. * * @param sources the source addresses. * @param last_reported_host the address of the host that last reported * as member. */ void process_change_to_include_mode(const set& sources, const IPvX& last_reported_host); /** * Process CHANGE_TO_EXCLUDE_MODE report. * * @param sources the source addresses. * @param last_reported_host the address of the host that last reported * as member. */ void process_change_to_exclude_mode(const set& sources, const IPvX& last_reported_host); /** * Process ALLOW_NEW_SOURCES report. * * @param sources the source addresses. * @param last_reported_host the address of the host that last reported * as member. */ void process_allow_new_sources(const set& sources, const IPvX& last_reported_host); /** * Process BLOCK_OLD_SOURCES report. * * @param sources the source addresses. * @param last_reported_host the address of the host that last reported * as member. */ void process_block_old_sources(const set& sources, const IPvX& last_reported_host); /** * Lower the group timer. * * @param timeval the timeout interval the timer should be lowered to. */ void lower_group_timer(const TimeVal& timeval); /** * Lower the source timer for a set of sources. * * @param sources the source addresses. * @param timeval the timeout interval the timer should be lowered to. */ void lower_source_timer(const set& sources, const TimeVal& timeval); /** * Take the appropriate actions for a source that has expired. * * @param source_record the source record that has expired. */ void source_expired(Mld6igmpSourceRecord* source_record); /** * Get the number of seconds until the group timer expires. * * @return the number of seconds until the group timer expires. */ uint32_t timeout_sec() const; /** * Get the address of the host that last reported as member. * * @return the address of the host that last reported as member. */ const IPvX& last_reported_host() const { return (_last_reported_host); } /** * Get a refererence to the group timer. * * @return a reference to the group timer. */ XorpTimer& group_timer() { return _group_timer; } /** * Schedule periodic Group-Specific and Group-and-Source-Specific Query * retransmission. * * If the sources list is empty, we schedule Group-Specific Query, * otherwise we schedule Group-and-Source-Specific Query. * * @param sources the source addresses. */ void schedule_periodic_group_query(const set& sources); /** * Record that an older Membership report message has been received. * * @param message_version the corresponding protocol version of the * received message. */ void received_older_membership_report(int message_version); /** * Test if the group is running in IGMPv1 mode. * * @return true if the group is running in IGMPv1 mode, otherwise false. */ bool is_igmpv1_mode() const; /** * Test if the group is running in IGMPv2 mode. * * @return true if the group is running in IGMPv2 mode, otherwise false. */ bool is_igmpv2_mode() const; /** * Test if the group is running in IGMPv3 mode. * * @return true if the group is running in IGMPv3 mode, otherwise false. */ bool is_igmpv3_mode() const; /** * Test if the group is running in MLDv1 mode. * * @return true if the group is running in MLDv1 mode, otherwise false. */ bool is_mldv1_mode() const; /** * Test if the group is running in MLDv2 mode. * * @return true if the group is running in MLDv2 mode, otherwise false. */ bool is_mldv2_mode() const; /** * Get the address family. * * @return the address family. */ int family() const { return _group.af(); } private: /** * Calculate the forwarding changes and notify the interested parties. * * @param old_is_include mode if true, the old filter mode was INCLUDE, * otherwise was EXCLUDE. * @param old_do_forward_sources the old set of sources to forward. * @param old_dont_forward_sources the old set of sources not to forward. */ void calculate_forwarding_changes(bool old_is_include_mode, const set& old_do_forward_sources, const set& old_dont_forward_sources) const; /** * Timeout: one of the older version host present timers has expired. */ void older_version_host_present_timer_timeout(); /** * Timeout: the group timer has expired. */ void group_timer_timeout(); /** * Periodic timeout: time to send the next Group-Specific and * Group-and-Source-Specific Queries. * * @return true if the timer should be scheduled again, otherwise false. */ bool group_query_periodic_timeout(); /** * Set the address of the host that last reported as member. * * @param v the address of the host that last reported as member. */ void set_last_reported_host(const IPvX& v) { _last_reported_host = v; } Mld6igmpVif& _mld6igmp_vif; // The interface this entry belongs to IPvX _group; // The multicast group address bool _is_include_mode; // Flag for INCLUDE/EXCLUDE filter mode Mld6igmpSourceSet _do_forward_sources; // Sources to forward Mld6igmpSourceSet _dont_forward_sources; // Sources not to forward IPvX _last_reported_host; // The host that last reported as member // Timers indicating that hosts running older protocol version are present XorpTimer _igmpv1_host_present_timer; XorpTimer _igmpv2_mldv1_host_present_timer; XorpTimer _group_timer; // Group timer for filter mode switch XorpTimer _group_query_timer; // Timer for periodic Queries size_t _query_retransmission_count; // Count for periodic Queries }; /** * @short A class to store information about a set of multicast groups. */ class Mld6igmpGroupSet : public map { public: /** * Constructor for a given vif. * * @param mld6igmp_vif the interface this set belongs to. */ Mld6igmpGroupSet(Mld6igmpVif& mld6igmp_vif); /** * Destructor. */ ~Mld6igmpGroupSet(); /** * Find a group record. * * @param group the group address. * @return the corresponding group record (@ref Mld6igmpGroupRecord) * if found, otherwise NULL. */ Mld6igmpGroupRecord* find_group_record(const IPvX& group); /** * Delete the payload of the set, and clear the set itself. */ void delete_payload_and_clear(); /** * Process MODE_IS_INCLUDE report. * * @param group the group address. * @param sources the source addresses. * @param last_reported_host the address of the host that last reported * as member. */ void process_mode_is_include(const IPvX& group, const set& sources, const IPvX& last_reported_host); /** * Process MODE_IS_EXCLUDE report. * * @param group the group address. * @param sources the source addresses. * @param last_reported_host the address of the host that last reported * as member. */ void process_mode_is_exclude(const IPvX& group, const set& sources, const IPvX& last_reported_host); /** * Process CHANGE_TO_INCLUDE_MODE report. * * @param group the group address. * @param sources the source addresses. * @param last_reported_host the address of the host that last reported * as member. */ void process_change_to_include_mode(const IPvX& group, const set& sources, const IPvX& last_reported_host); /** * Process CHANGE_TO_EXCLUDE_MODE report. * * @param group the group address. * @param sources the source addresses. * @param last_reported_host the address of the host that last reported * as member. */ void process_change_to_exclude_mode(const IPvX& group, const set& sources, const IPvX& last_reported_host); /** * Process ALLOW_NEW_SOURCES report. * * @param group the group address. * @param sources the source addresses. * @param last_reported_host the address of the host that last reported * as member. */ void process_allow_new_sources(const IPvX& group, const set& sources, const IPvX& last_reported_host); /** * Process BLOCK_OLD_SOURCES report. * * @param group the group address. * @param sources the source addresses. * @param last_reported_host the address of the host that last reported * as member. */ void process_block_old_sources(const IPvX& group, const set& sources, const IPvX& last_reported_host); /** * Lower the group timer. * * @param group the group address. * @param timeval the timeout interval the timer should be lowered to. */ void lower_group_timer(const IPvX& group, const TimeVal& timeval); /** * Lower the source timer for a set of sources. * * @param group the group address. * @param sources the source addresses. * @param timeval the timeout interval the timer should be lowered to. */ void lower_source_timer(const IPvX& group, const set& sources, const TimeVal& timeval); private: Mld6igmpVif& _mld6igmp_vif; // The interface this set belongs to }; // // Global variables // // // Global functions prototypes // #endif // __MLD6IGMP_MLD6IGMP_GROUP_RECORD_HH__ xorp/mld6igmp/xrl_mld6igmp_shell_funcs.sh0000664000076400007640000002672211421137511020731 0ustar greearbgreearb#!/bin/sh # # $XORP: xorp/mld6igmp/xrl_mld6igmp_shell_funcs.sh,v 1.15 2005/06/01 09:34:01 pavlin Exp $ # # # Library of functions to sent XRLs to a running MLD6IGMP process. # # Conditionally set ${srcdir} if it wasn't assigned (e.g., by `gmake check`) if [ "X${srcdir}" = "X" ] ; then srcdir=`dirname $0` ; fi . ${srcdir}/../utils/xrl_shell_lib.sh # # Conditionally set the target name # IP_VERSION=${IP_VERSION:?"IP_VERSION undefined. Must be defined to either IPV4 or IPV6"} case "${IP_VERSION}" in IPV4) MLD6IGMP_TARGET=${MLD6IGMP_TARGET:="IGMP"} ;; IPV6) MLD6IGMP_TARGET=${MLD6IGMP_TARGET:="MLD"} ;; *) echo "Error: invalid IP_VERSION = ${IP_VERSION}. Must be either IPV4 or IPV6" exit 1 ;; esac mld6igmp_enable_vif() { if [ $# -lt 2 ] ; then echo "Usage: mld6igmp_enable_vif " exit 1 fi vif_name=$1 enable=$2 echo "mld6igmp_enable_vif" $* XRL="finder://$MLD6IGMP_TARGET/mld6igmp/0.1/enable_vif" XRL_ARGS="?vif_name:txt=$vif_name&enable:bool=$enable" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } mld6igmp_start_vif() { if [ $# -lt 1 ] ; then echo "Usage: mld6igmp_start_vif " exit 1 fi vif_name=$1 echo "mld6igmp_start_vif" $* XRL="finder://$MLD6IGMP_TARGET/mld6igmp/0.1/start_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } mld6igmp_stop_vif() { if [ $# -lt 1 ] ; then echo "Usage: mld6igmp_stop_vif " exit 1 fi vif_name=$1 echo "mld6igmp_stop_vif" $* XRL="finder://$MLD6IGMP_TARGET/mld6igmp/0.1/stop_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } mld6igmp_enable_all_vifs() { if [ $# -lt 1 ] ; then echo "Usage: mld6igmp_enable_all_vifs " exit 1 fi enable=$1 echo "mld6igmp_enable_all_vifs" $* XRL="finder://$MLD6IGMP_TARGET/mld6igmp/0.1/enable_all_vifs" XRL_ARGS="?enable:bool=$enable" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } mld6igmp_start_all_vifs() { echo "mld6igmp_start_all_vifs" $* XRL="finder://$MLD6IGMP_TARGET/mld6igmp/0.1/start_all_vifs" XRL_ARGS="" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } mld6igmp_stop_all_vifs() { echo "mld6igmp_stop_all_vifs" $* XRL="finder://$MLD6IGMP_TARGET/mld6igmp/0.1/stop_all_vifs" XRL_ARGS="" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } mld6igmp_enable_mld6igmp() { if [ $# -lt 1 ] ; then echo "Usage: mld6igmp_enable_mld6igmp " exit 1 fi enable=$1 echo "mld6igmp_enable_mld6igmp" $* XRL="finder://$MLD6IGMP_TARGET/mld6igmp/0.1/enable_mld6igmp" XRL_ARGS="?enable:bool=$enable" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } mld6igmp_start_mld6igmp() { echo "mld6igmp_start_mld6igmp" $* XRL="finder://$MLD6IGMP_TARGET/mld6igmp/0.1/start_mld6igmp" XRL_ARGS="" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } mld6igmp_stop_mld6igmp() { echo "mld6igmp_stop_mld6igmp" $* XRL="finder://$MLD6IGMP_TARGET/mld6igmp/0.1/stop_mld6igmp" XRL_ARGS="" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } mld6igmp_enable_cli() { if [ $# -lt 1 ] ; then echo "Usage: mld6igmp_enable_cli " exit 1 fi enable=$1 echo "mld6igmp_enable_cli" $* XRL="finder://$MLD6IGMP_TARGET/mld6igmp/0.1/enable_cli" XRL_ARGS="?enable:bool=$enable" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } mld6igmp_start_cli() { echo "mld6igmp_start_cli" $* XRL="finder://$MLD6IGMP_TARGET/mld6igmp/0.1/start_cli" XRL_ARGS="" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } mld6igmp_stop_cli() { echo "mld6igmp_stop_cli" $* XRL="finder://$MLD6IGMP_TARGET/mld6igmp/0.1/stop_cli" XRL_ARGS="" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } # # Configure MLD6IGMP interface-related metrics. # mld6igmp_get_vif_proto_version() { if [ $# -lt 1 ] ; then echo "Usage: mld6igmp_get_vif_proto_version " exit 1 fi vif_name=$1 echo "mld6igmp_get_vif_proto_version" $* XRL="finder://$MLD6IGMP_TARGET/mld6igmp/0.1/get_vif_proto_version" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p proto_version:u32 $XRL$XRL_ARGS } mld6igmp_set_vif_proto_version() { if [ $# -lt 2 ] ; then echo "Usage: mld6igmp_set_vif_proto_version " exit 1 fi vif_name=$1 proto_version=$2 echo "mld6igmp_set_vif_proto_version" $* XRL="finder://$MLD6IGMP_TARGET/mld6igmp/0.1/set_vif_proto_version" XRL_ARGS="?vif_name:txt=$vif_name&proto_version:u32=$proto_version" call_xrl_wrapper $XRL$XRL_ARGS } mld6igmp_reset_vif_proto_version() { if [ $# -lt 1 ] ; then echo "Usage: mld6igmp_reset_vif_proto_version " exit 1 fi vif_name=$1 echo "mld6igmp_reset_vif_proto_version" $* XRL="finder://$MLD6IGMP_TARGET/mld6igmp/0.1/reset_vif_proto_version" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper $XRL$XRL_ARGS } mld6igmp_get_vif_ip_router_alert_option_check() { if [ $# -lt 1 ] ; then echo "Usage: mld6igmp_get_vif_ip_router_alert_option_check " exit 1 fi vif_name=$1 echo "mld6igmp_get_vif_ip_router_alert_option_check" $* XRL="finder://$MLD6IGMP_TARGET/mld6igmp/0.1/get_vif_ip_router_alert_option_check" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p enabled:bool $XRL$XRL_ARGS } mld6igmp_set_vif_ip_router_alert_option_check() { if [ $# -lt 2 ] ; then echo "Usage: mld6igmp_set_vif_ip_router_alert_option_check " exit 1 fi vif_name=$1 enable=$2 echo "mld6igmp_set_vif_ip_router_alert_option_check" $* XRL="finder://$MLD6IGMP_TARGET/mld6igmp/0.1/set_vif_ip_router_alert_option_check" XRL_ARGS="?vif_name:txt=$vif_name&enable:bool=$enable" call_xrl_wrapper $XRL$XRL_ARGS } mld6igmp_reset_vif_ip_router_alert_option_check() { if [ $# -lt 1 ] ; then echo "Usage: mld6igmp_reset_vif_ip_router_alert_option_check " exit 1 fi vif_name=$1 echo "mld6igmp_reset_vif_ip_router_alert_option_check" $* XRL="finder://$MLD6IGMP_TARGET/mld6igmp/0.1/reset_vif_ip_router_alert_option_check" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper $XRL$XRL_ARGS } mld6igmp_get_vif_query_interval() { if [ $# -lt 1 ] ; then echo "Usage: mld6igmp_get_vif_query_interval " exit 1 fi vif_name=$1 echo "mld6igmp_get_vif_query_interval" $* XRL="finder://$MLD6IGMP_TARGET/mld6igmp/0.1/get_vif_query_interval" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p interval_sec:u32 -p interval_usec:u32 $XRL$XRL_ARGS } mld6igmp_set_vif_query_interval() { if [ $# -lt 3 ] ; then echo "Usage: mld6igmp_set_vif_query_interval " exit 1 fi vif_name=$1 interval_sec=$2 interval_usec=$3 echo "mld6igmp_set_vif_query_interval" $* XRL="finder://$MLD6IGMP_TARGET/mld6igmp/0.1/set_vif_query_interval" XRL_ARGS="?vif_name:txt=$vif_name&interval_sec:u32=$interval_sec&interval_usec:u32=$interval_usec" call_xrl_wrapper $XRL$XRL_ARGS } mld6igmp_reset_vif_query_interval() { if [ $# -lt 1 ] ; then echo "Usage: mld6igmp_reset_vif_query_interval " exit 1 fi vif_name=$1 echo "mld6igmp_reset_vif_query_interval" $* XRL="finder://$MLD6IGMP_TARGET/mld6igmp/0.1/reset_vif_query_interval" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper $XRL$XRL_ARGS } mld6igmp_get_vif_query_last_member_interval() { if [ $# -lt 1 ] ; then echo "Usage: mld6igmp_get_vif_query_last_member_interval " exit 1 fi vif_name=$1 echo "mld6igmp_get_vif_query_last_member_interval" $* XRL="finder://$MLD6IGMP_TARGET/mld6igmp/0.1/get_vif_query_last_member_interval" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p interval_sec:u32 -p interval_usec:u32 $XRL$XRL_ARGS } mld6igmp_set_vif_query_last_member_interval() { if [ $# -lt 3 ] ; then echo "Usage: mld6igmp_set_vif_query_last_member_interval " exit 1 fi vif_name=$1 interval_sec=$2 interval_usec=$3 echo "mld6igmp_set_vif_query_last_member_interval" $* XRL="finder://$MLD6IGMP_TARGET/mld6igmp/0.1/set_vif_query_last_member_interval" XRL_ARGS="?vif_name:txt=$vif_name&interval_sec:u32=$interval_sec&interval_usec:u32=$interval_usec" call_xrl_wrapper $XRL$XRL_ARGS } mld6igmp_reset_vif_query_last_member_interval() { if [ $# -lt 1 ] ; then echo "Usage: mld6igmp_reset_vif_query_last_member_interval " exit 1 fi vif_name=$1 echo "mld6igmp_reset_vif_query_last_member_interval" $* XRL="finder://$MLD6IGMP_TARGET/mld6igmp/0.1/reset_vif_query_last_member_interval" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper $XRL$XRL_ARGS } mld6igmp_get_vif_query_response_interval() { if [ $# -lt 1 ] ; then echo "Usage: mld6igmp_get_vif_query_response_interval " exit 1 fi vif_name=$1 echo "mld6igmp_get_vif_query_response_interval" $* XRL="finder://$MLD6IGMP_TARGET/mld6igmp/0.1/get_vif_query_response_interval" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p interval_sec:u32 -p interval_usec:u32 $XRL$XRL_ARGS } mld6igmp_set_vif_query_response_interval() { if [ $# -lt 3 ] ; then echo "Usage: mld6igmp_set_vif_query_response_interval " exit 1 fi vif_name=$1 interval_sec=$2 interval_usec=$3 echo "mld6igmp_set_vif_query_response_interval" $* XRL="finder://$MLD6IGMP_TARGET/mld6igmp/0.1/set_vif_query_response_interval" XRL_ARGS="?vif_name:txt=$vif_name&interval_sec:u32=$interval_sec&interval_usec:u32=$interval_usec" call_xrl_wrapper $XRL$XRL_ARGS } mld6igmp_reset_vif_query_response_interval() { if [ $# -lt 1 ] ; then echo "Usage: mld6igmp_reset_vif_query_response_interval " exit 1 fi vif_name=$1 echo "mld6igmp_reset_vif_query_response_interval" $* XRL="finder://$MLD6IGMP_TARGET/mld6igmp/0.1/reset_vif_query_response_interval" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper $XRL$XRL_ARGS } mld6igmp_get_vif_robust_count() { if [ $# -lt 1 ] ; then echo "Usage: mld6igmp_get_vif_robust_count " exit 1 fi vif_name=$1 echo "mld6igmp_get_vif_robust_count" $* XRL="finder://$MLD6IGMP_TARGET/mld6igmp/0.1/get_vif_robust_count" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p robust_count:u32 $XRL$XRL_ARGS } mld6igmp_set_vif_robust_count() { if [ $# -lt 2 ] ; then echo "Usage: mld6igmp_set_vif_robust_count " exit 1 fi vif_name=$1 robust_count=$2 echo "mld6igmp_set_vif_robust_count" $* XRL="finder://$MLD6IGMP_TARGET/mld6igmp/0.1/set_vif_robust_count" XRL_ARGS="?vif_name:txt=$vif_name&robust_count:u32=$robust_count" call_xrl_wrapper $XRL$XRL_ARGS } mld6igmp_reset_vif_robust_count() { if [ $# -lt 1 ] ; then echo "Usage: mld6igmp_reset_vif_robust_count " exit 1 fi vif_name=$1 echo "mld6igmp_reset_vif_robust_count" $* XRL="finder://$MLD6IGMP_TARGET/mld6igmp/0.1/reset_vif_robust_count" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper $XRL$XRL_ARGS } mld6igmp_log_trace_all() { if [ $# -lt 1 ] ; then echo "Usage: mld6igmp_log_trace_all " exit 1 fi enable=$1 echo "mld6igmp_log_trace_all" $* XRL="finder://$MLD6IGMP_TARGET/mld6igmp/0.1/log_trace_all" XRL_ARGS="?enable:bool=$enable" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } xorp/mld6igmp/SConscript0000664000076400007640000000662511631505504015422 0ustar greearbgreearb# Copyright (c) 2009-2011 XORP, Inc and Others # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # $XORP$ import os Import('env') env = env.Clone() is_shared = env.has_key('SHAREDLIBS') env.AppendUnique(CPPPATH = [ '#', '$BUILDDIR', ]) env.PrependUnique(LIBPATH = [ '$BUILDDIR/libxorp', '$BUILDDIR/libcomm', '$BUILDDIR/libxipc', '$BUILDDIR/libproto', '$BUILDDIR/libfeaclient', '$BUILDDIR/policy/backend', '$BUILDDIR/policy/common', '$BUILDDIR/xrl/interfaces', '$BUILDDIR/xrl/targets', '$BUILDDIR/mrt', '.' ]) env.AppendUnique(LIBS = [ 'xorp_mld6igmp', 'xorp_fea_client', 'xif_mld6igmp_client', 'xif_fea_rawpkt4', 'xif_fea_ifmgr_mirror', 'xif_fea_ifmgr_replicator', 'xif_cli_manager', 'xif_finder_event_notifier', 'xst_fea_ifmgr_mirror', 'xst_mld6igmp', 'xorp_mrt', 'xorp_proto', 'xorp_ipc', 'xorp_core', 'xorp_comm' ]) if not is_shared: env.AppendUnique(LIBS = [ "crypto", ]) if not (env.has_key('mingw') and env['mingw']): env.AppendUnique(LIBS = [ "rt", ]) if (env.has_key('mingw') and env['mingw']): env.AppendUnique(LIBS = [ 'ws2_32', 'iphlpapi', 'winmm', # 'mprapi', # 'regex', ]) env.Append(LIBS = ['xorp_core']) libxorp_mld6igmp_srcs = [ 'mld6igmp_config.cc', 'mld6igmp_group_record.cc', 'mld6igmp_node.cc', 'mld6igmp_node_cli.cc', 'mld6igmp_proto.cc', 'mld6igmp_source_record.cc', 'mld6igmp_vif.cc', 'xrl_mld6igmp_node.cc' ] if not (env.has_key('disable_ipv6') and env['disable_ipv6']): env.AppendUnique(LIBS = [ 'xif_fea_rawpkt6', ]) env.Replace(RPATH = [ env.Literal(env['xorp_module_rpath']) ]) if is_shared: libxorp_mld6igmp = env.SharedLibrary(target = 'libxorp_mld6igmp', source = libxorp_mld6igmp_srcs, LIBS = '') if env['rtld_origin']: for obj in libxorp_mld6igmp: env.AddPostAction(libxorp_mld6igmp, env.Symlink(obj.abspath, os.path.join(env['xorp_alias_libdir'], str(obj)))) else: libxorp_mld6igmp = env.StaticLibrary(target = 'libxorp_mld6igmp', source = libxorp_mld6igmp_srcs, LIBS = '') igmpsrcs = [ 'xorp_igmp.cc', ] igmp = env.Program(target = 'xorp_igmp', source = igmpsrcs) mldsrcs = [ 'xorp_mld.cc', ] mld = env.Program(target = 'xorp_mld', source = mldsrcs) env.Alias('install', env.InstallProgram(env['xorp_moduledir'], igmp)) env.Alias('install', env.InstallProgram(env['xorp_moduledir'], mld)) if is_shared: env.Alias('install', env.InstallLibrary(env['xorp_libdir'], libxorp_mld6igmp)) Default(igmp, mld) xorp/mld6igmp/command_mld6igmp0000775000076400007640000000214511421137511016540 0ustar greearbgreearb#!/bin/sh # # $XORP: xorp/pim/command_pim,v 1.8 2003/10/15 19:02:07 pavlin Exp $ # # # Send commands to a running PIM process. # MODULE_NAME="IGMP" HOSTNAME=`hostname` echo "Sending command to $MODULE_NAME on $HOSTNAME..." # Conditionally set ${srcdir} if it wasn't assigned (e.g., by `gmake check`) if [ "X${srcdir}" = "X" ] ; then srcdir=`dirname $0` ; fi # # The optional first argument should be either -4 or -6 to specify # IPv4 or IPv6 command # # The next argument should be the command to call. # The rest of the arguments should be the arguments to the command to call. # usage() { echo "Usage: $0 [-4 | -6 ] [xrl_command_arguments ... ]" } if [ $# -lt 1 ] ; then usage; exit 1 fi case "${1}" in -4) if [ $# -lt 2 ] ; then usage; exit 1 fi IP_VERSION=IPV4 shift ;; -6) if [ $# -lt 2 ] ; then usage; exit 1 fi IP_VERSION=IPV6 shift ;; *) # XXX: IP version defaults to IPv4 IP_VERSION=IPV4 ;; esac . ${srcdir}/../cli/xrl_cli_shell_funcs.sh . ${srcdir}/../fea/xrl_mfea_shell_funcs.sh . ${srcdir}/../mld6igmp/xrl_mld6igmp_shell_funcs.sh $* exit $? xorp/mld6igmp/mld6igmp_source_record.hh0000664000076400007640000002001511540224230020344 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/mld6igmp/mld6igmp_source_record.hh,v 1.14 2008/10/02 21:57:44 bms Exp $ #ifndef __MLD6IGMP_MLD6IGMP_SOURCE_RECORD_HH__ #define __MLD6IGMP_MLD6IGMP_SOURCE_RECORD_HH__ // // IGMP and MLD source record. // #include "libxorp/ipvx.hh" #include "libxorp/timer.hh" // // Constants definitions // // // Structures/classes, typedefs and macros // class EventLoop; class Mld6igmpGroupRecord; /** * @short A class to store information about a source (within a given * multicast group). */ class Mld6igmpSourceRecord { public: /** * Constructor for a given group record and source address. * * @param group_record the group record this entry belongs to. * @param source the source address. */ Mld6igmpSourceRecord(Mld6igmpGroupRecord& group_record, const IPvX& source); /** * Destructor. */ ~Mld6igmpSourceRecord(); /** * Get the group record this entry belongs to. * * @return a reference to the group record this entry belongs to. */ Mld6igmpGroupRecord& group_record() const { return (_group_record); } /** * Get the source address. * * @return the source address. */ const IPvX& source() const { return (_source); } /** * Get the address family. * * @return the address family. */ int family() const { return _source.af(); } /** * Set the source timer. * * @param timeval the timeout interval of the source timer. */ void set_source_timer(const TimeVal& timeval); /** * Cancel the source timer. */ void cancel_source_timer(); /** * Lower the source timer. * * @param timeval the timeout interval the source timer should be * lowered to. */ void lower_source_timer(const TimeVal& timeval); /** * Get a reference to the source timer. * * @return a reference to the source timer. */ XorpTimer& source_timer() { return _source_timer; } /** * Get the number of seconds until the source timer expires. * * @return the number of seconds until the source timer expires. */ uint32_t timeout_sec() const; /** * Get the Query retransmission count. * * @return the Query retransmission count. */ size_t query_retransmission_count() const { return _query_retransmission_count; } /** * Set the Query retransmission count. * * @param v the value to set. */ void set_query_retransmission_count(size_t v) { _query_retransmission_count = v; } private: /** * Timeout: the source timer has expired. */ void source_timer_timeout(); Mld6igmpGroupRecord& _group_record; // The group record we belong to IPvX _source; // The source address XorpTimer _source_timer; // The source timer size_t _query_retransmission_count; // Count for periodic Queries }; /** * @short A class to store information about a set of sources. */ class Mld6igmpSourceSet : public map { public: /** * Constructor for a given group record. * * @param group_record the group record this set belongs to. */ Mld6igmpSourceSet(Mld6igmpGroupRecord& group_record); /** * Destructor. */ ~Mld6igmpSourceSet(); /** * Find a source record. * * @param source the source address. * @return the corresponding source record (@ref Mld6igmpSourceRecord) * if found, otherwise NULL. */ Mld6igmpSourceRecord* find_source_record(const IPvX& source); /** * Delete the payload of the set, and clear the set itself. */ void delete_payload_and_clear(); /** * Assignment operator for sets. * * @param other the right-hand operand. * @return the assigned set. */ Mld6igmpSourceSet& operator=(const Mld6igmpSourceSet& other); /** * UNION operator for sets. * * @param other the right-hand operand. * @return the union of two sets. Note that if an element is in * both sets, we use the value from the first set. */ Mld6igmpSourceSet operator+(const Mld6igmpSourceSet& other); /** * UNION operator for sets when the second operand is a set of IPvX * addresses. * * @param other the right-hand operand. * @return the union of two sets. Note that if an element is not in the * first set, then it is created (see @ref Mld6igmpSourceRecord). */ Mld6igmpSourceSet operator+(const set& other); /** * INTERSECTION operator for sets. * * @param other the right-hand operand. * @return the intersection of two sets. Note that we use the values * from the first set. */ Mld6igmpSourceSet operator*(const Mld6igmpSourceSet& other); /** * INTERSECTION operator for sets when the second operand is a set of IPvX * addresses. * * @param other the right-hand operand. * @return the intersection of two sets. Note that we use the values * from the first set. */ Mld6igmpSourceSet operator*(const set& other); /** * REMOVAL operator for sets. * * @param other the right-hand operand. * @return the elements from the first set (after the elements from * the right-hand set have been removed). */ Mld6igmpSourceSet operator-(const Mld6igmpSourceSet& other); /** * REMOVAL operator for sets when the second operand is a set of IPvX * addresses. * * @param other the right-hand operand. * @return the elements from the first set (after the elements from * the right-hand set have been removed). */ Mld6igmpSourceSet operator-(const set& other); /** * Set the source timer for a set of source addresses. * * @param sources the set of source addresses whose source timer will * be set. * @param timeval the timeout interval of the source timer. */ void set_source_timer(const set& sources, const TimeVal& timeval); /** * Set the source timer for all source addresses. * * @param timeval the timeout interval of the source timer. */ void set_source_timer(const TimeVal& timeval); /** * Cancel the source timer for a set of source addresses. * * @param sources the set of source addresses whose source timer will * be canceled. */ void cancel_source_timer(const set& sources); /** * Cancel the source timer for all source addresses. */ void cancel_source_timer(); /** * Lower the source timer for a set of sources. * * @param sources the source addresses. * @param timeval the timeout interval the source timer should be * lowered to. */ void lower_source_timer(const set& sources, const TimeVal& timeval); /** * Extract the set of source addresses. * * @return the set with the source addresses. */ set extract_source_addresses() const; private: Mld6igmpGroupRecord& _group_record; // The group record this set belongs to }; // // Global variables // // // Global functions prototypes // #endif // __MLD6IGMP_MLD6IGMP_SOURCE_RECORD_HH__ xorp/mld6igmp/README0000664000076400007640000000316411421137511014257 0ustar greearbgreearb# # $XORP: xorp/mld6igmp/README,v 1.18 2006/07/05 03:38:51 pavlin Exp $ # Multicast Listener Discovery/Internet Group Management Protocol (MLD/IGMP) Implementation =============================================================== This directory contains the XORP implementation of the MLD and IGMP protocols. Configuration ============= MLD/IGMP like most XORP processes does not take its configuration parameters from the command line. Its parameters are provided via XRLs. At the very least a MLD/IGMP process must be provided with the set of network interfaces to enable for multicast. Startup ======= In normal operation, MLD/IGMP would be started by the XORP router manager process, not directly from the command line. For information how to configure MLD/IGMP see http://www.xorp.org/getting_started.html or http://www.xorp.org/releases/current/docs/user_manual/user_manual.pdf Documentation ============= The MLD/IGMP design architecture and code structure are described in: ${XORP}/docs/mld6igmp/ The programming documentation is in: ${XORP}/docs/kdoc/html/mld6igmp/ Testing ======= Currently, the MLD/IGMP testing is performed manually, by starting MLD/IGMP on a number of testbed machines. In the future, automated testing will be added, similar to the BGP testing framework. Status ====== The MLD/IGMP implementation is based on the specification in the following documents: * RFC 2236: Internet Group Management Protocol, Version 2 * RFC 3376: Internet Group Management Protocol, Version 3 * RFC 2710: Multicast Listener Discovery for IPv6, Version 1 * RFC 3810: Multicast Listener Discovery Version 2 (MLDv2) for IPv6 xorp/mld6igmp/mld6igmp_source_record.cc0000664000076400007640000003036211421137511020343 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // Multicast source record information used by IGMPv3 (RFC 3376) and // MLDv2 (RFC 3810). // #include "mld6igmp_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvx.hh" #include "mld6igmp_source_record.hh" #include "mld6igmp_group_record.hh" #include "mld6igmp_vif.hh" // // Exported variables // // // Local constants definitions // // // Local structures/classes, typedefs and macros // // // Local variables // // // Local functions prototypes // /** * Mld6igmpSourceRecord::Mld6igmpSourceRecord: * @group_record: The group record this entry belongs to. * @source: The entry source address. * * Return value: **/ Mld6igmpSourceRecord::Mld6igmpSourceRecord(Mld6igmpGroupRecord& group_record, const IPvX& source) : _group_record(group_record), _source(source), _query_retransmission_count(0) { } /** * Mld6igmpSourceRecord::~Mld6igmpSourceRecord: * @: * * Mld6igmpSourceRecord destructor. **/ Mld6igmpSourceRecord::~Mld6igmpSourceRecord() { } /** * Set the source timer. * * @param timeval the timeout interval of the source timer. */ void Mld6igmpSourceRecord::set_source_timer(const TimeVal& timeval) { EventLoop& eventloop = _group_record.eventloop(); _source_timer = eventloop.new_oneoff_after( timeval, callback(this, &Mld6igmpSourceRecord::source_timer_timeout)); } /** * Cancel the source timer. */ void Mld6igmpSourceRecord::cancel_source_timer() { _source_timer.unschedule(); } /** * Lower the source timer. * * @param timeval the timeout interval the source timer should be * lowered to. */ void Mld6igmpSourceRecord::lower_source_timer(const TimeVal& timeval) { EventLoop& eventloop = _group_record.eventloop(); TimeVal timeval_remaining; // // Lower the source timer // _source_timer.time_remaining(timeval_remaining); if (timeval < timeval_remaining) { _source_timer = eventloop.new_oneoff_after( timeval, callback(this, &Mld6igmpSourceRecord::source_timer_timeout)); } } /** * Timeout: the source timer has expired. */ void Mld6igmpSourceRecord::source_timer_timeout() { _group_record.source_expired(this); } /** * Get the number of seconds until the source timer expires. * * @return the number of seconds until the source timer expires. */ uint32_t Mld6igmpSourceRecord::timeout_sec() const { TimeVal tv; _source_timer.time_remaining(tv); return (tv.sec()); } /** * Constructor for a given group record. * * @param group_record the group record this set belongs to. */ Mld6igmpSourceSet::Mld6igmpSourceSet(Mld6igmpGroupRecord& group_record) : _group_record(group_record) { } /** * Destructor. */ Mld6igmpSourceSet::~Mld6igmpSourceSet() { // XXX: don't delete the payload, because it might be used elsewhere } /** * Find a source record. * * @param source the source address. * @return the corresponding source record (@ref Mld6igmpSourceRecord) * if found, otherwise NULL. */ Mld6igmpSourceRecord* Mld6igmpSourceSet::find_source_record(const IPvX& source) { Mld6igmpSourceSet::iterator iter = this->find(source); if (iter != this->end()) return (iter->second); return (NULL); } /** * Delete the payload of the set, and clear the set itself. */ void Mld6igmpSourceSet::delete_payload_and_clear() { Mld6igmpSourceSet::iterator iter; // // Delete the payload of the set // for (iter = this->begin(); iter != this->end(); ++iter) { Mld6igmpSourceRecord* source_record = iter->second; delete source_record; } // // Clear the set itself // this->clear(); } /** * Assignment operator for sets. * * @param other the right-hand operand. * @return the assigned set. */ Mld6igmpSourceSet& Mld6igmpSourceSet::operator=(const Mld6igmpSourceSet& other) { Mld6igmpSourceSet::const_iterator iter; XLOG_ASSERT(&_group_record == &(other._group_record)); this->clear(); // // Copy the payload of the set // for (iter = other.begin(); iter != other.end(); ++iter) { insert(make_pair(iter->first, iter->second)); } return (*this); } /** * UNION operator for sets. * * @param other the right-hand operand. * @return the union of two sets. Note that if an element is in * both sets, we use the value from the first set. */ Mld6igmpSourceSet Mld6igmpSourceSet::operator+(const Mld6igmpSourceSet& other) { Mld6igmpSourceSet result(*this); // XXX: all elements from the first set Mld6igmpSourceSet::const_iterator iter; // // Insert all elements from the second set that are not in the first set // for (iter = other.begin(); iter != other.end(); ++iter) { const IPvX& ipvx = iter->first; if (result.find(ipvx) == result.end()) result.insert(make_pair(iter->first, iter->second)); } return (result); } /** * UNION operator for sets when the second operand is a set of IPvX * addresses. * * @param other the right-hand operand. * @return the union of two sets. Note that if an element is not in the * first set, then it is created (see @ref Mld6igmpSourceRecord). */ Mld6igmpSourceSet Mld6igmpSourceSet::operator+(const set& other) { Mld6igmpSourceSet result(*this); // XXX: all elements from the first set set::const_iterator iter; Mld6igmpSourceRecord* source_record; // // Insert all elements from the second set that are not in the first set // for (iter = other.begin(); iter != other.end(); ++iter) { const IPvX& ipvx = *iter; if (result.find(ipvx) == result.end()) { source_record = new Mld6igmpSourceRecord(_group_record, ipvx); result.insert(make_pair(ipvx, source_record)); } } return (result); } /** * INTERSECTION operator for sets. * * @param other the right-hand operand. * @return the intersection of two sets. Note that we use the values * from the first set. */ Mld6igmpSourceSet Mld6igmpSourceSet::operator*(const Mld6igmpSourceSet& other) { Mld6igmpSourceSet result(_group_record); Mld6igmpSourceSet::const_iterator iter; // // Insert all elements from the first set that are also in the second set // for (iter = this->begin(); iter != this->end(); ++iter) { const IPvX& ipvx = iter->first; if (other.find(ipvx) != other.end()) result.insert(make_pair(iter->first, iter->second)); } return (result); } /** * INTERSECTION operator for sets when the second operand is a set of IPvX * addresses. * * @param other the right-hand operand. * @return the intersection of two sets. Note that we use the values * from the first set. */ Mld6igmpSourceSet Mld6igmpSourceSet::operator*(const set& other) { Mld6igmpSourceSet result(_group_record); Mld6igmpSourceSet::const_iterator iter; // // Insert all elements from the first set that are also in the second set // for (iter = this->begin(); iter != this->end(); ++iter) { const IPvX& ipvx = iter->first; if (other.find(ipvx) != other.end()) result.insert(make_pair(iter->first, iter->second)); } return (result); } /** * REMOVAL operator for sets. * * @param other the right-hand operand. * @return the elements from the first set (after the elements from * the right-hand set have been removed). */ Mld6igmpSourceSet Mld6igmpSourceSet::operator-(const Mld6igmpSourceSet& other) { Mld6igmpSourceSet result(_group_record); Mld6igmpSourceSet::const_iterator iter; // // Insert all elements from the first set that are not in the second set // for (iter = this->begin(); iter != this->end(); ++iter) { const IPvX& ipvx = iter->first; if (other.find(ipvx) == other.end()) result.insert(make_pair(iter->first, iter->second)); } return (result); } /** * REMOVAL operator for sets when the second operand is a set of IPvX * addresses. * * @param other the right-hand operand. * @return the elements from the first set (after the elements from * the right-hand set have been removed). */ Mld6igmpSourceSet Mld6igmpSourceSet::operator-(const set& other) { Mld6igmpSourceSet result(_group_record); Mld6igmpSourceSet::const_iterator iter; // // Insert all elements from the first set that are not in the second set // for (iter = this->begin(); iter != this->end(); ++iter) { const IPvX& ipvx = iter->first; if (other.find(ipvx) == other.end()) result.insert(make_pair(iter->first, iter->second)); } return (result); } /** * Set the source timer for a set of source addresses. * * @param sources the set of source addresses whose source timer will * be set. * @param timeval the timeout interval of the source timer. */ void Mld6igmpSourceSet::set_source_timer(const set& sources, const TimeVal& timeval) { set::const_iterator iter; Mld6igmpSourceSet::iterator record_iter; for (iter = sources.begin(); iter != sources.end(); ++iter) { const IPvX& ipvx = *iter; record_iter = this->find(ipvx); if (record_iter != this->end()) { Mld6igmpSourceRecord* source_record = record_iter->second; source_record->set_source_timer(timeval); } } } /** * Set the source timer for all source addresses. * * @param timeval the timeout interval of the source timer. */ void Mld6igmpSourceSet::set_source_timer(const TimeVal& timeval) { Mld6igmpSourceSet::iterator iter; for (iter = this->begin(); iter != this->end(); ++iter) { Mld6igmpSourceRecord* source_record = iter->second; source_record->set_source_timer(timeval); } } /** * Cancel the source timer for a set of source addresses. * * @param sources the set of source addresses whose source timer will * be canceled. */ void Mld6igmpSourceSet::cancel_source_timer(const set& sources) { set::const_iterator iter; Mld6igmpSourceSet::iterator record_iter; for (iter = sources.begin(); iter != sources.end(); ++iter) { const IPvX& ipvx = *iter; record_iter = this->find(ipvx); if (record_iter != this->end()) { Mld6igmpSourceRecord* source_record = record_iter->second; source_record->cancel_source_timer(); } } } /** * Cancel the source timer for all source addresses. */ void Mld6igmpSourceSet::cancel_source_timer() { Mld6igmpSourceSet::iterator iter; for (iter = this->begin(); iter != this->end(); ++iter) { Mld6igmpSourceRecord* source_record = iter->second; source_record->cancel_source_timer(); } } /** * Lower the source timer for a set of sources. * * @param sources the source addresses. * @param timeval the timeout interval the source timer should be * lowered to. */ void Mld6igmpSourceSet::lower_source_timer(const set& sources, const TimeVal& timeval) { set::const_iterator iter; Mld6igmpSourceSet::iterator record_iter; for (iter = sources.begin(); iter != sources.end(); ++iter) { const IPvX& ipvx = *iter; record_iter = this->find(ipvx); if (record_iter != this->end()) { Mld6igmpSourceRecord* source_record = record_iter->second; source_record->lower_source_timer(timeval); } } } /** * Extract the set of source addresses. * * @return the set with the source addresses. */ set Mld6igmpSourceSet::extract_source_addresses() const { set sources; Mld6igmpSourceSet::const_iterator record_iter; for (record_iter = this->begin(); record_iter != this->end(); ++record_iter) { const Mld6igmpSourceRecord* source_record = record_iter->second; const IPvX& ipvx = source_record->source(); sources.insert(ipvx); } return (sources); } xorp/mld6igmp/xrl_mld6igmp_node.cc0000664000076400007640000017511111540225530017322 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "mld6igmp_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvx.hh" #include "libxorp/status_codes.h" #include "libxorp/utils.hh" #include "mld6igmp_node.hh" #include "mld6igmp_node_cli.hh" #include "mld6igmp_vif.hh" #include "xrl_mld6igmp_node.hh" const TimeVal XrlMld6igmpNode::RETRY_TIMEVAL = TimeVal(1, 0); // // XrlMld6igmpNode front-end interface // XrlMld6igmpNode::XrlMld6igmpNode(int family, xorp_module_id module_id, EventLoop& eventloop, const string& class_name, const string& finder_hostname, uint16_t finder_port, const string& finder_target, const string& fea_target, const string& mfea_target) : Mld6igmpNode(family, module_id, eventloop), XrlStdRouter(eventloop, class_name.c_str(), finder_hostname.c_str(), finder_port), XrlMld6igmpTargetBase(&xrl_router()), Mld6igmpNodeCli(*static_cast(this)), _eventloop(eventloop), _finder_target(finder_target), _fea_target(fea_target), _mfea_target(mfea_target), _ifmgr(eventloop, mfea_target.c_str(), xrl_router().finder_address(), xrl_router().finder_port()), _xrl_fea_client4(&xrl_router()), #ifdef HAVE_IPV6 _xrl_fea_client6(&xrl_router()), #endif _xrl_mld6igmp_client_client(&xrl_router()), _xrl_cli_manager_client(&xrl_router()), _xrl_finder_client(&xrl_router()), _is_finder_alive(false), _is_fea_alive(false), _is_fea_registered(false), _is_mfea_alive(false), _is_mfea_registered(false) { _ifmgr.set_observer(dynamic_cast(this)); _ifmgr.attach_hint_observer(dynamic_cast(this)); } XrlMld6igmpNode::~XrlMld6igmpNode() { shutdown(); _ifmgr.detach_hint_observer(dynamic_cast(this)); _ifmgr.unset_observer(dynamic_cast(this)); delete_pointers_list(_xrl_tasks_queue); } int XrlMld6igmpNode::startup() { if (start_mld6igmp() != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int XrlMld6igmpNode::shutdown() { int ret_value = XORP_OK; if (stop_cli() != XORP_OK) ret_value = XORP_ERROR; if (stop_mld6igmp() != XORP_OK) ret_value = XORP_ERROR; return (ret_value); } int XrlMld6igmpNode::enable_cli() { Mld6igmpNodeCli::enable(); return (XORP_OK); } int XrlMld6igmpNode::disable_cli() { Mld6igmpNodeCli::disable(); return (XORP_OK); } int XrlMld6igmpNode::start_cli() { if (Mld6igmpNodeCli::start() != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int XrlMld6igmpNode::stop_cli() { if (Mld6igmpNodeCli::stop() != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int XrlMld6igmpNode::enable_mld6igmp() { Mld6igmpNode::enable(); return (XORP_OK); } int XrlMld6igmpNode::disable_mld6igmp() { Mld6igmpNode::disable(); return (XORP_OK); } int XrlMld6igmpNode::start_mld6igmp() { if (Mld6igmpNode::start() != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int XrlMld6igmpNode::stop_mld6igmp() { if (Mld6igmpNode::stop() != XORP_OK) return (XORP_ERROR); return (XORP_OK); } // // Finder-related events // /** * Called when Finder connection is established. * * Note that this method overwrites an XrlRouter virtual method. */ void XrlMld6igmpNode::finder_connect_event() { _is_finder_alive = true; } /** * Called when Finder disconnect occurs. * * Note that this method overwrites an XrlRouter virtual method. */ void XrlMld6igmpNode::finder_disconnect_event() { XLOG_ERROR("Finder disconnect event. Exiting immediately..."); _is_finder_alive = false; stop_mld6igmp(); } // // Task-related methods // void XrlMld6igmpNode::add_task(XrlTaskBase* xrl_task) { _xrl_tasks_queue.push_back(xrl_task); // If the queue was empty before, start sending the changes if (_xrl_tasks_queue.size() == 1) send_xrl_task(); } void XrlMld6igmpNode::send_xrl_task() { if (_xrl_tasks_queue.empty()) return; XrlTaskBase* xrl_task_base = _xrl_tasks_queue.front(); XLOG_ASSERT(xrl_task_base != NULL); xrl_task_base->dispatch(); } void XrlMld6igmpNode::pop_xrl_task() { XLOG_ASSERT(! _xrl_tasks_queue.empty()); XrlTaskBase* xrl_task_base = _xrl_tasks_queue.front(); XLOG_ASSERT(xrl_task_base != NULL); delete xrl_task_base; _xrl_tasks_queue.pop_front(); } void XrlMld6igmpNode::retry_xrl_task() { if (_xrl_tasks_queue_timer.scheduled()) return; // XXX: already scheduled _xrl_tasks_queue_timer = _eventloop.new_oneoff_after( RETRY_TIMEVAL, callback(this, &XrlMld6igmpNode::send_xrl_task)); } // // Register with the FEA // void XrlMld6igmpNode::fea_register_startup() { if (! _is_finder_alive) return; // The Finder is dead if (_is_fea_registered) return; // Already registered Mld6igmpNode::incr_startup_requests_n(); // XXX: for FEA registration Mld6igmpNode::incr_startup_requests_n(); // XXX: for FEA birth // // Register interest in the FEA with the Finder // add_task(new RegisterUnregisterInterest(*this, _fea_target, true)); } // // Register with the MFEA // void XrlMld6igmpNode::mfea_register_startup() { if (! _is_finder_alive) return; // The Finder is dead if (_is_mfea_registered) return; // Already registered Mld6igmpNode::incr_startup_requests_n(); // XXX: for MFEA registration Mld6igmpNode::incr_startup_requests_n(); // XXX: for MFEA birth Mld6igmpNode::incr_startup_requests_n(); // XXX: for the ifmgr // // Register interest in the FEA with the Finder // add_task(new RegisterUnregisterInterest(*this, _mfea_target, true)); } // // De-register with the FEA // void XrlMld6igmpNode::fea_register_shutdown() { if (! _is_finder_alive) return; // The Finder is dead if (! _is_fea_alive) return; // The FEA is not there anymore if (! _is_fea_registered) return; // Not registered Mld6igmpNode::incr_shutdown_requests_n(); // XXX: for FEA deregistration // // De-register interest in the FEA with the Finder // add_task(new RegisterUnregisterInterest(*this, _fea_target, false)); } // // De-register with the MFEA // void XrlMld6igmpNode::mfea_register_shutdown() { if (! _is_finder_alive) return; // The Finder is dead if (! _is_mfea_alive) return; // The MFEA is not there anymore if (! _is_mfea_registered) return; // Not registered Mld6igmpNode::incr_shutdown_requests_n(); // XXX: for MFEA deregistration Mld6igmpNode::incr_shutdown_requests_n(); // XXX: for the ifmgr // // De-register interest in the MFEA with the Finder // add_task(new RegisterUnregisterInterest(*this, _mfea_target, false)); // // XXX: when the shutdown is completed, Mld6igmpNode::status_change() // will be called. // _ifmgr.shutdown(); } void XrlMld6igmpNode::send_register_unregister_interest() { bool success = true; if (! _is_finder_alive) return; // The Finder is dead XLOG_ASSERT(! _xrl_tasks_queue.empty()); XrlTaskBase* xrl_task_base = _xrl_tasks_queue.front(); RegisterUnregisterInterest* entry; entry = dynamic_cast(xrl_task_base); XLOG_ASSERT(entry != NULL); if (entry->is_register()) { // Register interest success = _xrl_finder_client.send_register_class_event_interest( _finder_target.c_str(), xrl_router().instance_name(), entry->target_name(), callback(this, &XrlMld6igmpNode::finder_send_register_unregister_interest_cb)); } else { // Unregister interest success = _xrl_finder_client.send_deregister_class_event_interest( _finder_target.c_str(), xrl_router().instance_name(), entry->target_name(), callback(this, &XrlMld6igmpNode::finder_send_register_unregister_interest_cb)); } if (! success) { // // If an error, then try again // XLOG_ERROR("Failed to %s register interest in %s with the Finder. " "Will try again.", entry->operation_name(), entry->target_name().c_str()); retry_xrl_task(); return; } } void XrlMld6igmpNode::finder_send_register_unregister_interest_cb(const XrlError& xrl_error) { XLOG_ASSERT(! _xrl_tasks_queue.empty()); XrlTaskBase* xrl_task_base = _xrl_tasks_queue.front(); RegisterUnregisterInterest* entry; entry = dynamic_cast(xrl_task_base); XLOG_ASSERT(entry != NULL); switch (xrl_error.error_code()) { case OKAY: // // If success, then schedule the next task // if (entry->is_register()) { // // Register interest // if (entry->target_name() == _fea_target) { // // If success, then the FEA birth event will startup the FEA // registration. // _is_fea_registered = true; Mld6igmpNode::decr_startup_requests_n(); // XXX: for FEA registration } if (entry->target_name() == _mfea_target) { // // If success, then the MFEA birth event will startup the MFEA // registration and the ifmgr. // _is_mfea_registered = true; Mld6igmpNode::decr_startup_requests_n(); // XXX: for MFEA registration } } else { // // Unregister interest // if (entry->target_name() == _fea_target) { _is_fea_registered = false; Mld6igmpNode::decr_shutdown_requests_n(); // XXX: for the FEA } if (entry->target_name() == _mfea_target) { _is_mfea_registered = false; Mld6igmpNode::decr_shutdown_requests_n(); // XXX: for the MFEA } } pop_xrl_task(); send_xrl_task(); break; case COMMAND_FAILED: // // If a command failed because the other side rejected it, this is // fatal. // XLOG_FATAL("Cannot %s interest in Finder events: %s", entry->operation_name(), xrl_error.str().c_str()); break; case NO_FINDER: case RESOLVE_FAILED: case SEND_FAILED: // // A communication error that should have been caught elsewhere // (e.g., by tracking the status of the Finder and the other targets). // Probably we caught it here because of event reordering. // In some cases we print an error. In other cases our job is done. // if (entry->is_register()) { XLOG_ERROR("XRL communication error: %s", xrl_error.str().c_str()); } else { if (entry->target_name() == _fea_target) { _is_fea_registered = false; } if (entry->target_name() == _mfea_target) { _is_mfea_registered = false; } pop_xrl_task(); send_xrl_task(); } break; case BAD_ARGS: case NO_SUCH_METHOD: case INTERNAL_ERROR: // // An error that should happen only if there is something unusual: // e.g., there is XRL mismatch, no enough internal resources, etc. // We don't try to recover from such errors, hence this is fatal. // XLOG_FATAL("Fatal XRL error: %s", xrl_error.str().c_str()); break; case REPLY_TIMED_OUT: case SEND_FAILED_TRANSIENT: // // If a transient error, then try again // XLOG_ERROR("Failed to %s interest in Finder envents: %s. " "Will try again.", entry->operation_name(), xrl_error.str().c_str()); retry_xrl_task(); break; } } int XrlMld6igmpNode::register_receiver(const string& if_name, const string& vif_name, uint8_t ip_protocol, bool enable_multicast_loopback) { Mld6igmpNode::incr_startup_requests_n(); // XXX: for FEA-receiver add_task(new RegisterUnregisterReceiver(*this, if_name, vif_name, ip_protocol, enable_multicast_loopback, true)); return (XORP_OK); } int XrlMld6igmpNode::unregister_receiver(const string& if_name, const string& vif_name, uint8_t ip_protocol) { Mld6igmpNode::incr_shutdown_requests_n(); // XXX: for FEA-non-receiver add_task(new RegisterUnregisterReceiver(*this, if_name, vif_name, ip_protocol, false, // XXX: ignored false)); return (XORP_OK); } void XrlMld6igmpNode::send_register_unregister_receiver() { bool success = true; if (! _is_finder_alive) return; // The Finder is dead XLOG_ASSERT(! _xrl_tasks_queue.empty()); XrlTaskBase* xrl_task_base = _xrl_tasks_queue.front(); RegisterUnregisterReceiver* entry; entry = dynamic_cast(xrl_task_base); XLOG_ASSERT(entry != NULL); // // Check whether we have already registered with the FEA // if (! _is_fea_registered) { retry_xrl_task(); return; } if (entry->is_register()) { // Register a receiver with the FEA if (Mld6igmpNode::is_ipv4()) { success = _xrl_fea_client4.send_register_receiver( _fea_target.c_str(), xrl_router().instance_name(), entry->if_name(), entry->vif_name(), entry->ip_protocol(), entry->enable_multicast_loopback(), callback(this, &XrlMld6igmpNode::fea_client_send_register_unregister_receiver_cb)); if (success) return; } #ifdef HAVE_IPV6 if (Mld6igmpNode::is_ipv6()) { success = _xrl_fea_client6.send_register_receiver( _fea_target.c_str(), xrl_router().instance_name(), entry->if_name(), entry->vif_name(), entry->ip_protocol(), entry->enable_multicast_loopback(), callback(this, &XrlMld6igmpNode::fea_client_send_register_unregister_receiver_cb)); if (success) return; } #endif } else { // Unregister a receiver with the FEA if (Mld6igmpNode::is_ipv4()) { success = _xrl_fea_client4.send_unregister_receiver( _fea_target.c_str(), xrl_router().instance_name(), entry->if_name(), entry->vif_name(), entry->ip_protocol(), callback(this, &XrlMld6igmpNode::fea_client_send_register_unregister_receiver_cb)); if (success) return; } #ifdef HAVE_IPV6 if (Mld6igmpNode::is_ipv6()) { success = _xrl_fea_client6.send_unregister_receiver( _fea_target.c_str(), xrl_router().instance_name(), entry->if_name(), entry->vif_name(), entry->ip_protocol(), callback(this, &XrlMld6igmpNode::fea_client_send_register_unregister_receiver_cb)); if (success) return; } #endif } if (! success) { // // If an error, then try again // XLOG_ERROR("Failed to %s register receiver on interface %s vif %s " "IP protocol %u with the FEA. " "Will try again.", entry->operation_name(), entry->if_name().c_str(), entry->vif_name().c_str(), entry->ip_protocol()); retry_xrl_task(); return; } } void XrlMld6igmpNode::fea_client_send_register_unregister_receiver_cb( const XrlError& xrl_error) { XLOG_ASSERT(! _xrl_tasks_queue.empty()); XrlTaskBase* xrl_task_base = _xrl_tasks_queue.front(); RegisterUnregisterReceiver* entry; entry = dynamic_cast(xrl_task_base); XLOG_ASSERT(entry != NULL); switch (xrl_error.error_code()) { case OKAY: // // If success, then schedule the next task // if (entry->is_register()) Mld6igmpNode::decr_startup_requests_n(); // XXX: for FEA-receiver else Mld6igmpNode::decr_shutdown_requests_n(); // XXX: for FEA-non-receiver pop_xrl_task(); send_xrl_task(); break; case COMMAND_FAILED: // // If a command failed because the other side rejected it, this is // fatal. // XLOG_FATAL("Cannot %s receiver with the FEA: %s", entry->operation_name(), xrl_error.str().c_str()); break; case NO_FINDER: case RESOLVE_FAILED: case SEND_FAILED: // // A communication error that should have been caught elsewhere // (e.g., by tracking the status of the Finder and the other targets). // Probably we caught it here because of event reordering. // In some cases we print an error. In other cases our job is done. // if (entry->is_register()) { XLOG_ERROR("XRL communication error: %s", xrl_error.str().c_str()); } else { Mld6igmpNode::decr_shutdown_requests_n(); // XXX: for FEA-non-receiver pop_xrl_task(); send_xrl_task(); } break; case BAD_ARGS: case NO_SUCH_METHOD: case INTERNAL_ERROR: // // An error that should happen only if there is something unusual: // e.g., there is XRL mismatch, no enough internal resources, etc. // We don't try to recover from such errors, hence this is fatal. // XLOG_FATAL("Fatal XRL error: %s", xrl_error.str().c_str()); break; case REPLY_TIMED_OUT: case SEND_FAILED_TRANSIENT: // // If a transient error, then try again // XLOG_ERROR("Failed to %s receiver with the FEA: %s. " "Will try again.", entry->operation_name(), xrl_error.str().c_str()); retry_xrl_task(); break; } } int XrlMld6igmpNode::join_multicast_group(const string& if_name, const string& vif_name, uint8_t ip_protocol, const IPvX& group_address) { Mld6igmpNode::incr_startup_requests_n(); // XXX: for FEA-join add_task(new JoinLeaveMulticastGroup(*this, if_name, vif_name, ip_protocol, group_address, true)); return (XORP_OK); } int XrlMld6igmpNode::leave_multicast_group(const string& if_name, const string& vif_name, uint8_t ip_protocol, const IPvX& group_address) { Mld6igmpNode::incr_shutdown_requests_n(); // XXX: for FEA-leave add_task(new JoinLeaveMulticastGroup(*this, if_name, vif_name, ip_protocol, group_address, false)); return (XORP_OK); } void XrlMld6igmpNode::send_join_leave_multicast_group() { bool success = true; if (! _is_finder_alive) return; // The Finder is dead XLOG_ASSERT(! _xrl_tasks_queue.empty()); XrlTaskBase* xrl_task_base = _xrl_tasks_queue.front(); JoinLeaveMulticastGroup* entry; entry = dynamic_cast(xrl_task_base); XLOG_ASSERT(entry != NULL); // // Check whether we have already registered with the FEA // if (! _is_fea_registered) { retry_xrl_task(); return; } if (entry->is_join()) { // Join a multicast group with the FEA if (Mld6igmpNode::is_ipv4()) { success = _xrl_fea_client4.send_join_multicast_group( _fea_target.c_str(), xrl_router().instance_name(), entry->if_name(), entry->vif_name(), entry->ip_protocol(), entry->group_address().get_ipv4(), callback(this, &XrlMld6igmpNode::fea_client_send_join_leave_multicast_group_cb)); if (success) return; } #ifdef HAVE_IPV6 if (Mld6igmpNode::is_ipv6()) { success = _xrl_fea_client6.send_join_multicast_group( _fea_target.c_str(), xrl_router().instance_name(), entry->if_name(), entry->vif_name(), entry->ip_protocol(), entry->group_address().get_ipv6(), callback(this, &XrlMld6igmpNode::fea_client_send_join_leave_multicast_group_cb)); if (success) return; } #endif } else { // Leave a multicast group with the FEA if (Mld6igmpNode::is_ipv4()) { success = _xrl_fea_client4.send_leave_multicast_group( _fea_target.c_str(), xrl_router().instance_name(), entry->if_name(), entry->vif_name(), entry->ip_protocol(), entry->group_address().get_ipv4(), callback(this, &XrlMld6igmpNode::fea_client_send_join_leave_multicast_group_cb)); if (success) return; } #ifdef HAVE_IPV6 if (Mld6igmpNode::is_ipv6()) { success = _xrl_fea_client6.send_leave_multicast_group( _fea_target.c_str(), xrl_router().instance_name(), entry->if_name(), entry->vif_name(), entry->ip_protocol(), entry->group_address().get_ipv6(), callback(this, &XrlMld6igmpNode::fea_client_send_join_leave_multicast_group_cb)); if (success) return; } #endif } if (! success) { // // If an error, then try again // XLOG_ERROR("Failed to %s group %s on interface/vif %s/%s with the FEA. " "Will try again.", entry->operation_name(), entry->group_address().str().c_str(), entry->if_name().c_str(), entry->vif_name().c_str()); retry_xrl_task(); return; } } void XrlMld6igmpNode::fea_client_send_join_leave_multicast_group_cb( const XrlError& xrl_error) { XLOG_ASSERT(! _xrl_tasks_queue.empty()); XrlTaskBase* xrl_task_base = _xrl_tasks_queue.front(); JoinLeaveMulticastGroup* entry; entry = dynamic_cast(xrl_task_base); XLOG_ASSERT(entry != NULL); switch (xrl_error.error_code()) { case OKAY: // // If success, then schedule the next task // if (entry->is_join()) Mld6igmpNode::decr_startup_requests_n(); // XXX: for FEA-join else Mld6igmpNode::decr_shutdown_requests_n(); // XXX: for FEA-leave pop_xrl_task(); send_xrl_task(); break; case COMMAND_FAILED: XLOG_WARNING("Cannot %s a multicast group with the FEA: %s", entry->operation_name(), xrl_error.str().c_str()); // If it was a join, try to leave it to clean things up as best as possible. // Currently, this doesn't seem to really do anything locally, but might // help FEA clean out it's logic. if (entry->is_join()) { leave_multicast_group(entry->if_name(), entry->vif_name(), entry->ip_protocol(), entry->group_address()); } break; case NO_FINDER: case RESOLVE_FAILED: case SEND_FAILED: // // A communication error that should have been caught elsewhere // (e.g., by tracking the status of the Finder and the other targets). // Probably we caught it here because of event reordering. // In some cases we print an error. In other cases our job is done. // if (entry->is_join()) { XLOG_ERROR("XRL communication error: %s", xrl_error.str().c_str()); } else { Mld6igmpNode::decr_shutdown_requests_n(); // XXX: for FEA-leave pop_xrl_task(); send_xrl_task(); } break; case BAD_ARGS: case NO_SUCH_METHOD: case INTERNAL_ERROR: // // An error that should happen only if there is something unusual: // e.g., there is XRL mismatch, no enough internal resources, etc. // We don't try to recover from such errors, hence this is fatal. // XLOG_FATAL("Fatal XRL error: %s", xrl_error.str().c_str()); break; case REPLY_TIMED_OUT: case SEND_FAILED_TRANSIENT: // // If a transient error, then try again // XLOG_ERROR("Failed to %s group %s on interface/vif %s/%s " "with the FEA: %s. " "Will try again.", entry->operation_name(), entry->group_address().str().c_str(), entry->if_name().c_str(), entry->vif_name().c_str(), xrl_error.str().c_str()); retry_xrl_task(); break; } } int XrlMld6igmpNode::send_add_membership(const string& dst_module_instance_name, xorp_module_id dst_module_id, uint32_t vif_index, const IPvX& source, const IPvX& group) { Mld6igmpVif *mld6igmp_vif = Mld6igmpNode::vif_find_by_vif_index(vif_index); if (mld6igmp_vif == NULL) { XLOG_ERROR("Cannot send add_membership to %s for (%s, %s) on vif " "with vif_index %d: no such vif", dst_module_instance_name.c_str(), cstring(source), cstring(group), vif_index); return (XORP_ERROR); } _send_add_delete_membership_queue.push_back(SendAddDeleteMembership( dst_module_instance_name, dst_module_id, vif_index, source, group, true)); // If the queue was empty before, start sending the changes if (_send_add_delete_membership_queue.size() == 1) { send_add_delete_membership(); } return (XORP_OK); } int XrlMld6igmpNode::send_delete_membership(const string& dst_module_instance_name, xorp_module_id dst_module_id, uint32_t vif_index, const IPvX& source, const IPvX& group) { Mld6igmpVif *mld6igmp_vif = Mld6igmpNode::vif_find_by_vif_index(vif_index); if (mld6igmp_vif == NULL) { XLOG_ERROR("Cannot send delete_membership to %s for (%s, %s) on vif " "with vif_index %d: no such vif", dst_module_instance_name.c_str(), cstring(source), cstring(group), vif_index); return (XORP_ERROR); } _send_add_delete_membership_queue.push_back(SendAddDeleteMembership( dst_module_instance_name, dst_module_id, vif_index, source, group, false)); // If the queue was empty before, start sending the changes if (_send_add_delete_membership_queue.size() == 1) { send_add_delete_membership(); } return (XORP_OK); } void XrlMld6igmpNode::send_add_delete_membership() { bool success = true; Mld6igmpVif *mld6igmp_vif = NULL; if (! _is_finder_alive) return; // The Finder is dead if (_send_add_delete_membership_queue.empty()) return; // No more changes const SendAddDeleteMembership& membership = _send_add_delete_membership_queue.front(); mld6igmp_vif = Mld6igmpNode::vif_find_by_vif_index(membership.vif_index()); if (mld6igmp_vif == NULL) { XLOG_ERROR("Cannot send %s for (%s, %s) on vif " "with vif_index %d to %s: no such vif", membership.operation_name(), cstring(membership.source()), cstring(membership.group()), membership.vif_index(), membership.dst_module_instance_name().c_str()); _send_add_delete_membership_queue.pop_front(); goto start_timer_label; } if (membership.is_add()) { // Send add_membership to the client protocol if (Mld6igmpNode::is_ipv4()) { success = _xrl_mld6igmp_client_client.send_add_membership4( membership.dst_module_instance_name().c_str(), xrl_router().class_name(), mld6igmp_vif->name(), membership.vif_index(), membership.source().get_ipv4(), membership.group().get_ipv4(), callback(this, &XrlMld6igmpNode::mld6igmp_client_send_add_delete_membership_cb)); if (success) return; } if (Mld6igmpNode::is_ipv6()) { success = _xrl_mld6igmp_client_client.send_add_membership6( membership.dst_module_instance_name().c_str(), xrl_router().class_name(), mld6igmp_vif->name(), membership.vif_index(), membership.source().get_ipv6(), membership.group().get_ipv6(), callback(this, &XrlMld6igmpNode::mld6igmp_client_send_add_delete_membership_cb)); if (success) return; } } else { // Send delete_membership to the client protocol if (Mld6igmpNode::is_ipv4()) { success = _xrl_mld6igmp_client_client.send_delete_membership4( membership.dst_module_instance_name().c_str(), xrl_router().class_name(), mld6igmp_vif->name(), membership.vif_index(), membership.source().get_ipv4(), membership.group().get_ipv4(), callback(this, &XrlMld6igmpNode::mld6igmp_client_send_add_delete_membership_cb)); if (success) return; } if (Mld6igmpNode::is_ipv6()) { success = _xrl_mld6igmp_client_client.send_delete_membership6( membership.dst_module_instance_name().c_str(), xrl_router().class_name(), mld6igmp_vif->name(), membership.vif_index(), membership.source().get_ipv6(), membership.group().get_ipv6(), callback(this, &XrlMld6igmpNode::mld6igmp_client_send_add_delete_membership_cb)); if (success) return; } } if (! success) { // // If an error, then try again // XLOG_ERROR("Failed to send %s for (%s, %s) on vif %s to %s. " "Will try again.", membership.operation_name(), cstring(membership.source()), cstring(membership.group()), mld6igmp_vif->name().c_str(), membership.dst_module_instance_name().c_str()); start_timer_label: _send_add_delete_membership_queue_timer = _eventloop.new_oneoff_after( RETRY_TIMEVAL, callback(this, &XrlMld6igmpNode::send_add_delete_membership)); } } void XrlMld6igmpNode::mld6igmp_client_send_add_delete_membership_cb( const XrlError& xrl_error) { #ifdef L_ERROR const SendAddDeleteMembership& membership = _send_add_delete_membership_queue.front(); #endif switch (xrl_error.error_code()) { case OKAY: // // If success, then schedule the next task // _send_add_delete_membership_queue.pop_front(); send_add_delete_membership(); break; case COMMAND_FAILED: // // If a command failed because the other side rejected it, // then print an error and send the next one. // XLOG_ERROR("Cannot %s for a multicast group with a client: %s: Will continue.", membership.operation_name(), xrl_error.str().c_str()); _send_add_delete_membership_queue.pop_front(); send_add_delete_membership(); break; case BAD_ARGS: case NO_SUCH_METHOD: case INTERNAL_ERROR: // // An error that should happen only if there is something unusual: // e.g., there is XRL mismatch, no enough internal resources, etc. // We don't try to recover from such errors, hence this is fatal. // XLOG_FATAL("Fatal XRL error: %s", xrl_error.str().c_str()); break; case NO_FINDER: case RESOLVE_FAILED: case SEND_FAILED: // // A communication error that should have been caught elsewhere // (e.g., by tracking the status of the Finder and the other targets). // Probably we caught it here because of event reordering. // In some cases we print an error. In other cases our job is done. // XLOG_ERROR("XRL communication error: %s", xrl_error.str().c_str()); // TODO: We must make some progress or otherwise deal with this error. // For now, fall through and re-try. // Fall through to the retry logic below. case REPLY_TIMED_OUT: case SEND_FAILED_TRANSIENT: // // If a transient error, then try again // if (! _send_add_delete_membership_queue_timer.scheduled()) { XLOG_ERROR("Failed to %s for a multicast group with a client: %s. " "Will try again.", membership.operation_name(), xrl_error.str().c_str()); _send_add_delete_membership_queue_timer = _eventloop.new_oneoff_after( RETRY_TIMEVAL, callback(this, &XrlMld6igmpNode::send_add_delete_membership)); } break; }/* switch */ } // // Protocol node methods // int XrlMld6igmpNode::proto_send(const string& if_name, const string& vif_name, const IPvX& src_address, const IPvX& dst_address, uint8_t ip_protocol, int32_t ip_ttl, int32_t ip_tos, bool ip_router_alert, bool ip_internet_control, const uint8_t* sndbuf, size_t sndlen, string& error_msg) { add_task(new SendProtocolMessage(*this, if_name, vif_name, src_address, dst_address, ip_protocol, ip_ttl, ip_tos, ip_router_alert, ip_internet_control, sndbuf, sndlen)); error_msg = ""; return (XORP_OK); } /** * Send a protocol message through the FEA. **/ void XrlMld6igmpNode::send_protocol_message() { bool success = true; if (! _is_finder_alive) return; // The Finder is dead XLOG_ASSERT(! _xrl_tasks_queue.empty()); XrlTaskBase* xrl_task_base = _xrl_tasks_queue.front(); SendProtocolMessage* entry; entry = dynamic_cast(xrl_task_base); XLOG_ASSERT(entry != NULL); // // Check whether we have already registered with the FEA // if (! _is_fea_registered) { retry_xrl_task(); return; } // // Send the protocol message // do { if (Mld6igmpNode::is_ipv4()) { success = _xrl_fea_client4.send_send( _fea_target.c_str(), entry->if_name(), entry->vif_name(), entry->src_address().get_ipv4(), entry->dst_address().get_ipv4(), entry->ip_protocol(), entry->ip_ttl(), entry->ip_tos(), entry->ip_router_alert(), entry->ip_internet_control(), entry->payload(), callback(this, &XrlMld6igmpNode::fea_client_send_protocol_message_cb)); if (success) return; break; } #ifdef HAVE_IPV6 if (Mld6igmpNode::is_ipv6()) { // XXX: no Extention headers XrlAtomList ext_headers_type; XrlAtomList ext_headers_payload; success = _xrl_fea_client6.send_send( _fea_target.c_str(), entry->if_name(), entry->vif_name(), entry->src_address().get_ipv6(), entry->dst_address().get_ipv6(), entry->ip_protocol(), entry->ip_ttl(), entry->ip_tos(), entry->ip_router_alert(), entry->ip_internet_control(), ext_headers_type, ext_headers_payload, entry->payload(), callback(this, &XrlMld6igmpNode::fea_client_send_protocol_message_cb)); if (success) return; break; } XLOG_UNREACHABLE(); #endif break; } while (false); if (! success) { // // If an error, then try again // XLOG_ERROR("Failed to send a protocol message on interface/vif %s/%s. " "Will try again.", entry->if_name().c_str(), entry->vif_name().c_str()); retry_xrl_task(); return; } } void XrlMld6igmpNode::fea_client_send_protocol_message_cb(const XrlError& xrl_error) { XLOG_ASSERT(! _xrl_tasks_queue.empty()); XrlTaskBase* xrl_task_base = _xrl_tasks_queue.front(); SendProtocolMessage* entry; entry = dynamic_cast(xrl_task_base); XLOG_ASSERT(entry != NULL); switch (xrl_error.error_code()) { case OKAY: // // If success, then schedule the next task // pop_xrl_task(); send_xrl_task(); break; case COMMAND_FAILED: // // If a command failed because the other side rejected it, // then print an error and send the next one. // // XXX: The FEA may fail to send a protocol message, therefore // we don't call XLOG_FATAL() here. For example, the transimssion // by the FEA it may fail if there is no buffer space or if an // unicast destination is not reachable. // Furthermore, all protocol messages are soft-state (i.e., they are // retransmitted periodically by the protocol), // hence we don't retransmit them here if there was an error. // XLOG_ERROR("Cannot send a protocol message: %s", xrl_error.str().c_str()); pop_xrl_task(); send_xrl_task(); break; case NO_FINDER: case RESOLVE_FAILED: case SEND_FAILED: // // A communication error that should have been caught elsewhere // (e.g., by tracking the status of the Finder and the other targets). // Probably we caught it here because of event reordering. // In some cases we print an error. In other cases our job is done. // XLOG_ERROR("Cannot send a protocol message: %s", xrl_error.str().c_str()); pop_xrl_task(); send_xrl_task(); break; case BAD_ARGS: case NO_SUCH_METHOD: case INTERNAL_ERROR: // // An error that should happen only if there is something unusual: // e.g., there is XRL mismatch, no enough internal resources, etc. // We don't try to recover from such errors, hence this is fatal. // XLOG_FATAL("Fatal XRL error: %s", xrl_error.str().c_str()); break; case REPLY_TIMED_OUT: case SEND_FAILED_TRANSIENT: // // XXX: if a transient error, then don't try again. // All protocol messages are soft-state (i.e., they are // retransmitted periodically by the protocol), // hence we don't retransmit them here if there was an error. // XLOG_ERROR("Failed to send a protocol message: %s", xrl_error.str().c_str()); pop_xrl_task(); send_xrl_task(); break; } } // // Protocol node CLI methods // int XrlMld6igmpNode::add_cli_command_to_cli_manager(const char *command_name, const char *command_help, bool is_command_cd, const char *command_cd_prompt, bool is_command_processor ) { bool success = false; if (! _is_finder_alive) return (XORP_ERROR); // The Finder is dead success = _xrl_cli_manager_client.send_add_cli_command( xorp_module_name(family(), XORP_MODULE_CLI), xrl_router().class_name(), string(command_name), string(command_help), is_command_cd, string(command_cd_prompt), is_command_processor, callback(this, &XrlMld6igmpNode::cli_manager_client_send_add_cli_command_cb)); if (! success) { XLOG_ERROR("Failed to add CLI command '%s' to the CLI manager", command_name); return (XORP_ERROR); } return (XORP_OK); } void XrlMld6igmpNode::cli_manager_client_send_add_cli_command_cb( const XrlError& xrl_error) { switch (xrl_error.error_code()) { case OKAY: // // If success, then we are done // break; case COMMAND_FAILED: // // If a command failed because the other side rejected it, this is // fatal. // XLOG_FATAL("Cannot add a command to CLI manager: %s", xrl_error.str().c_str()); break; case NO_FINDER: case RESOLVE_FAILED: case SEND_FAILED: // // A communication error that should have been caught elsewhere // (e.g., by tracking the status of the Finder and the other targets). // Probably we caught it here because of event reordering. // In some cases we print an error. In other cases our job is done. // XLOG_ERROR("Cannot add a command to CLI manager: %s", xrl_error.str().c_str()); break; case BAD_ARGS: case NO_SUCH_METHOD: case INTERNAL_ERROR: // // An error that should happen only if there is something unusual: // e.g., there is XRL mismatch, no enough internal resources, etc. // We don't try to recover from such errors, hence this is fatal. // XLOG_FATAL("Fatal XRL error: %s", xrl_error.str().c_str()); break; case REPLY_TIMED_OUT: case SEND_FAILED_TRANSIENT: // // If a transient error, then try again // // // TODO: if the command failed, then we should retransmit it // XLOG_ERROR("Failed to add a command to CLI manager: %s", xrl_error.str().c_str()); break; } } int XrlMld6igmpNode::delete_cli_command_from_cli_manager(const char *command_name) { bool success = true; if (! _is_finder_alive) return (XORP_ERROR); // The Finder is dead success = _xrl_cli_manager_client.send_delete_cli_command( xorp_module_name(family(), XORP_MODULE_CLI), xrl_router().class_name(), string(command_name), callback(this, &XrlMld6igmpNode::cli_manager_client_send_delete_cli_command_cb)); if (! success) { XLOG_ERROR("Failed to delete CLI command '%s' with the CLI manager", command_name); return (XORP_ERROR); } return (XORP_OK); } void XrlMld6igmpNode::cli_manager_client_send_delete_cli_command_cb( const XrlError& xrl_error) { switch (xrl_error.error_code()) { case OKAY: // // If success, then we are done // break; case COMMAND_FAILED: // // If a command failed because the other side rejected it, this is // fatal. // XLOG_FATAL("Cannot delete a command from CLI manager: %s", xrl_error.str().c_str()); break; case NO_FINDER: case RESOLVE_FAILED: case SEND_FAILED: // // A communication error that should have been caught elsewhere // (e.g., by tracking the status of the Finder and the other targets). // Probably we caught it here because of event reordering. // In some cases we print an error. In other cases our job is done. // XLOG_ERROR("Cannot delete a command from CLI manager: %s", xrl_error.str().c_str()); break; case BAD_ARGS: case NO_SUCH_METHOD: case INTERNAL_ERROR: // // An error that should happen only if there is something unusual: // e.g., there is XRL mismatch, no enough internal resources, etc. // We don't try to recover from such errors, hence this is fatal. // XLOG_FATAL("Fatal XRL error: %s", xrl_error.str().c_str()); break; case REPLY_TIMED_OUT: case SEND_FAILED_TRANSIENT: // // If a transient error, then try again // // // TODO: if the command failed, then we should retransmit it // XLOG_ERROR("Failed to delete a command from CLI manager: %s", xrl_error.str().c_str()); break; } } // // XRL target methods // XrlCmdError XrlMld6igmpNode::common_0_1_get_target_name( // Output values, string& name) { name = xrl_router().class_name(); return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::common_0_1_get_version( // Output values, string& version) { version = XORP_MODULE_VERSION; return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::common_0_1_get_status(// Output values, uint32_t& status, string& reason) { status = Mld6igmpNode::node_status(reason); return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::common_0_1_shutdown() { if (shutdown() != XORP_OK) { string error_msg = c_format("Failed to shutdown %s", Mld6igmpNode::proto_is_igmp() ? "IGMP" : "MLD"); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::common_0_1_startup() { if (startup() != XORP_OK) { string error_msg = c_format("Failed to startup %s", Mld6igmpNode::proto_is_igmp() ? "IGMP" : "MLD"); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } /** * Announce target birth to observer. * * @param target_class the target class name. * * @param target_instance the target instance name. */ XrlCmdError XrlMld6igmpNode::finder_event_observer_0_1_xrl_target_birth( // Input values, const string& target_class, const string& target_instance) { if (target_class == _fea_target) { _is_fea_alive = true; Mld6igmpNode::decr_startup_requests_n(); // XXX: for FEA birth } if (target_class == _mfea_target) { _is_mfea_alive = true; Mld6igmpNode::decr_startup_requests_n(); // XXX: for MFEA birth // // XXX: when the startup is completed, // IfMgrHintObserver::tree_complete() will be called. // if (_ifmgr.startup() != XORP_OK) { Mld6igmpNode::set_status(SERVICE_FAILED); Mld6igmpNode::update_status(); } } return XrlCmdError::OKAY(); UNUSED(target_instance); } /** * Announce target death to observer. * * @param target_class the target class name. * * @param target_instance the target instance name. */ XrlCmdError XrlMld6igmpNode::finder_event_observer_0_1_xrl_target_death( // Input values, const string& target_class, const string& target_instance) { bool do_shutdown = false; UNUSED(target_instance); if (target_class == _fea_target) { XLOG_ERROR("FEA (instance %s) has died, shutting down.", target_instance.c_str()); _is_fea_alive = false; do_shutdown = true; } if (target_class == _mfea_target) { XLOG_ERROR("MFEA (instance %s) has died, shutting down.", target_instance.c_str()); _is_mfea_alive = false; do_shutdown = true; } if (do_shutdown) stop_mld6igmp(); return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::cli_processor_0_1_process_command( // Input values, const string& processor_name, const string& cli_term_name, const uint32_t& cli_session_id, const string& command_name, const string& command_args, // Output values, string& ret_processor_name, string& ret_cli_term_name, uint32_t& ret_cli_session_id, string& ret_command_output) { Mld6igmpNodeCli::cli_process_command(processor_name, cli_term_name, cli_session_id, command_name, command_args, ret_processor_name, ret_cli_term_name, ret_cli_session_id, ret_command_output); return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::raw_packet4_client_0_1_recv( // Input values, const string& if_name, const string& vif_name, const IPv4& src_address, const IPv4& dst_address, const uint32_t& ip_protocol, const int32_t& ip_ttl, const int32_t& ip_tos, const bool& ip_router_alert, const bool& ip_internet_control, const vector& payload) { string error_msg; // // Verify the address family // if (! Mld6igmpNode::is_ipv4()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv4"); return XrlCmdError::COMMAND_FAILED(error_msg); } // // Receive the message // Mld6igmpNode::proto_recv(if_name, vif_name, IPvX(src_address), IPvX(dst_address), ip_protocol, ip_ttl, ip_tos, ip_router_alert, ip_internet_control, payload, error_msg); // XXX: no error returned, because if there is any, it is at the // protocol level, and the FEA shoudn't care about it. // // Success // return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::raw_packet6_client_0_1_recv( // Input values, const string& if_name, const string& vif_name, const IPv6& src_address, const IPv6& dst_address, const uint32_t& ip_protocol, const int32_t& ip_ttl, const int32_t& ip_tos, const bool& ip_router_alert, const bool& ip_internet_control, const XrlAtomList& ext_headers_type, const XrlAtomList& ext_headers_payload, const vector& payload) { string error_msg; UNUSED(ext_headers_type); UNUSED(ext_headers_payload); // // Verify the address family // if (! Mld6igmpNode::is_ipv6()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv6"); return XrlCmdError::COMMAND_FAILED(error_msg); } // // Receive the message // Mld6igmpNode::proto_recv(if_name, vif_name, IPvX(src_address), IPvX(dst_address), ip_protocol, ip_ttl, ip_tos, ip_router_alert, ip_internet_control, payload, error_msg); // XXX: no error returned, because if there is any, it is at the // protocol level, and the FEA shoudn't care about it. // // Success // return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_enable_vif( // Input values, const string& vif_name, const bool& enable) { string error_msg; int ret_value; if (enable) ret_value = Mld6igmpNode::enable_vif(vif_name, error_msg); else ret_value = Mld6igmpNode::disable_vif(vif_name, error_msg); if (ret_value != XORP_OK) return XrlCmdError::COMMAND_FAILED(error_msg); return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_start_vif( // Input values, const string& vif_name) { string error_msg; if (Mld6igmpNode::start_vif(vif_name, error_msg) != XORP_OK) return XrlCmdError::COMMAND_FAILED(error_msg); return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_stop_vif( // Input values, const string& vif_name) { string error_msg; if (Mld6igmpNode::stop_vif(vif_name, error_msg) != XORP_OK) return XrlCmdError::COMMAND_FAILED(error_msg); return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_enable_all_vifs( // Input values, const bool& enable) { string error_msg; int ret_value; if (enable) ret_value = Mld6igmpNode::enable_all_vifs(); else ret_value = Mld6igmpNode::enable_all_vifs(); if (ret_value != XORP_OK) { if (enable) error_msg = c_format("Failed to enable all vifs"); else error_msg = c_format("Failed to disable all vifs"); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_start_all_vifs() { string error_msg; if (Mld6igmpNode::start_all_vifs() != XORP_OK) { error_msg = c_format("Failed to start all vifs"); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_stop_all_vifs() { string error_msg; if (Mld6igmpNode::stop_all_vifs() != XORP_OK) { error_msg = c_format("Failed to stop all vifs"); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_enable_mld6igmp( // Input values, const bool& enable) { string error_msg; int ret_value; if (enable) ret_value = enable_mld6igmp(); else ret_value = disable_mld6igmp(); if (ret_value != XORP_OK) { if (enable) error_msg = c_format("Failed to enable MLD6IGMP"); else error_msg = c_format("Failed to disable MLD6IGMP"); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_start_mld6igmp() { string error_msg; if (start_mld6igmp() != XORP_OK) { error_msg = c_format("Failed to start MLD6IMGP"); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_stop_mld6igmp() { string error_msg; if (stop_mld6igmp() != XORP_OK) { error_msg = c_format("Failed to stop MLD6IMGP"); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_enable_cli( // Input values, const bool& enable) { string error_msg; int ret_value; if (enable) ret_value = enable_cli(); else ret_value = disable_cli(); if (ret_value != XORP_OK) { if (enable) error_msg = c_format("Failed to enable MLD6IGMP CLI"); else error_msg = c_format("Failed to disable MLD6IGMP CLI"); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_start_cli() { string error_msg; if (start_cli() != XORP_OK) { error_msg = c_format("Failed to start MLD6IMGP CLI"); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_stop_cli() { string error_msg; if (stop_cli() != XORP_OK) { error_msg = c_format("Failed to stop MLD6IMGP CLI"); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_get_vif_proto_version( // Input values, const string& vif_name, // Output values, uint32_t& proto_version) { string error_msg; int v; if (Mld6igmpNode::get_vif_proto_version(vif_name, v, error_msg) != XORP_OK) return XrlCmdError::COMMAND_FAILED(error_msg); proto_version = v; return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_set_vif_proto_version( // Input values, const string& vif_name, const uint32_t& proto_version) { string error_msg; if (Mld6igmpNode::set_vif_proto_version(vif_name, proto_version, error_msg) != XORP_OK) return XrlCmdError::COMMAND_FAILED(error_msg); return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_reset_vif_proto_version( // Input values, const string& vif_name) { string error_msg; if (Mld6igmpNode::reset_vif_proto_version(vif_name, error_msg) != XORP_OK) return XrlCmdError::COMMAND_FAILED(error_msg); return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_get_vif_ip_router_alert_option_check( // Input values, const string& vif_name, // Output values, bool& enabled) { string error_msg; bool v; if (Mld6igmpNode::get_vif_ip_router_alert_option_check(vif_name, v, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } enabled = v; return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_set_vif_ip_router_alert_option_check( // Input values, const string& vif_name, const bool& enable) { string error_msg; if (Mld6igmpNode::set_vif_ip_router_alert_option_check(vif_name, enable, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_reset_vif_ip_router_alert_option_check( // Input values, const string& vif_name) { string error_msg; if (Mld6igmpNode::reset_vif_ip_router_alert_option_check(vif_name, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_get_vif_query_interval( // Input values, const string& vif_name, // Output values, uint32_t& interval_sec, uint32_t& interval_usec) { string error_msg; TimeVal v; if (Mld6igmpNode::get_vif_query_interval(vif_name, v, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } interval_sec = v.sec(); interval_usec = v.usec(); return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_set_vif_query_interval( // Input values, const string& vif_name, const uint32_t& interval_sec, const uint32_t& interval_usec) { string error_msg; TimeVal interval(interval_sec, interval_usec); if (Mld6igmpNode::set_vif_query_interval(vif_name, interval, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_reset_vif_query_interval( // Input values, const string& vif_name) { string error_msg; if (Mld6igmpNode::reset_vif_query_interval(vif_name, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_get_vif_query_last_member_interval( // Input values, const string& vif_name, // Output values, uint32_t& interval_sec, uint32_t& interval_usec) { string error_msg; TimeVal v; if (Mld6igmpNode::get_vif_query_last_member_interval(vif_name, v, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } interval_sec = v.sec(); interval_usec = v.usec(); return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_set_vif_query_last_member_interval( // Input values, const string& vif_name, const uint32_t& interval_sec, const uint32_t& interval_usec) { string error_msg; TimeVal interval(interval_sec, interval_usec); if (Mld6igmpNode::set_vif_query_last_member_interval(vif_name, interval, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_reset_vif_query_last_member_interval( // Input values, const string& vif_name) { string error_msg; if (Mld6igmpNode::reset_vif_query_last_member_interval(vif_name, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_get_vif_query_response_interval( // Input values, const string& vif_name, // Output values, uint32_t& interval_sec, uint32_t& interval_usec) { string error_msg; TimeVal v; if (Mld6igmpNode::get_vif_query_response_interval(vif_name, v, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } interval_sec = v.sec(); interval_usec = v.usec(); return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_set_vif_query_response_interval( // Input values, const string& vif_name, const uint32_t& interval_sec, const uint32_t& interval_usec) { string error_msg; TimeVal interval(interval_sec, interval_usec); if (Mld6igmpNode::set_vif_query_response_interval(vif_name, interval, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_reset_vif_query_response_interval( // Input values, const string& vif_name) { string error_msg; if (Mld6igmpNode::reset_vif_query_response_interval(vif_name, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_get_vif_robust_count( // Input values, const string& vif_name, // Output values, uint32_t& robust_count) { string error_msg; uint32_t v; if (Mld6igmpNode::get_vif_robust_count(vif_name, v, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } robust_count = v; return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_set_vif_robust_count( // Input values, const string& vif_name, const uint32_t& robust_count) { string error_msg; if (Mld6igmpNode::set_vif_robust_count(vif_name, robust_count, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_reset_vif_robust_count( // Input values, const string& vif_name) { string error_msg; if (Mld6igmpNode::reset_vif_robust_count(vif_name, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_log_trace_all( // Input values, const bool& enable) { Mld6igmpNode::set_log_trace(enable); return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_add_protocol4( // Input values, const string& xrl_sender_name, const string& , // protocol_name, const uint32_t& protocol_id, const string& vif_name, const uint32_t& vif_index) { string error_msg; // // Verify the address family // if (! Mld6igmpNode::is_ipv4()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv4"); return XrlCmdError::COMMAND_FAILED(error_msg); } // // Verify the module ID // xorp_module_id src_module_id = static_cast(protocol_id); if (! is_valid_module_id(src_module_id)) { error_msg = c_format("Invalid module ID = %d", XORP_INT_CAST(protocol_id)); return XrlCmdError::COMMAND_FAILED(error_msg); } if (Mld6igmpNode::add_protocol(xrl_sender_name, src_module_id, vif_index) != XORP_OK) { // TODO: must find-out and return the reason for failure error_msg = c_format("Cannot add protocol instance '%s' " "on vif %s with vif_index %d", xrl_sender_name.c_str(), vif_name.c_str(), XORP_INT_CAST(vif_index)); return XrlCmdError::COMMAND_FAILED(error_msg); } // // Send info about all existing membership on the particular vif. // Mld6igmpVif *mld6igmp_vif = Mld6igmpNode::vif_find_by_vif_index(vif_index); if (mld6igmp_vif == NULL) { Mld6igmpNode::delete_protocol(xrl_sender_name, src_module_id, vif_index, error_msg); error_msg = c_format("Cannot add protocol instance '%s' " "on vif %s with vif_index %d: " "no such vif", xrl_sender_name.c_str(), vif_name.c_str(), XORP_INT_CAST(vif_index)); return XrlCmdError::COMMAND_FAILED(error_msg); } Mld6igmpGroupSet::const_iterator iter; for (iter = mld6igmp_vif->group_records().begin(); iter != mld6igmp_vif->group_records().end(); ++iter) { const Mld6igmpGroupRecord *group_record = iter->second; send_add_membership(xrl_sender_name.c_str(), src_module_id, mld6igmp_vif->vif_index(), IPvX::ZERO(family()), group_record->group()); } // // Success // return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_add_protocol6( // Input values, const string& xrl_sender_name, const string& , // protocol_name, const uint32_t& protocol_id, const string& vif_name, const uint32_t& vif_index) { string error_msg; // // Verify the address family // if (! Mld6igmpNode::is_ipv6()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv6"); return XrlCmdError::COMMAND_FAILED(error_msg); } // // Verify the module ID // xorp_module_id src_module_id = static_cast(protocol_id); if (! is_valid_module_id(src_module_id)) { error_msg = c_format("Invalid module ID = %d", XORP_INT_CAST(protocol_id)); return XrlCmdError::COMMAND_FAILED(error_msg); } if (Mld6igmpNode::add_protocol(xrl_sender_name, src_module_id, vif_index) != XORP_OK) { // TODO: must find-out and return the reason for failure error_msg = c_format("Cannot add protocol instance '%s' " "on vif %s with vif_index %d", xrl_sender_name.c_str(), vif_name.c_str(), XORP_INT_CAST(vif_index)); return XrlCmdError::COMMAND_FAILED(error_msg); } // // Send info about all existing membership on the particular vif. // Mld6igmpVif *mld6igmp_vif = Mld6igmpNode::vif_find_by_vif_index(vif_index); if (mld6igmp_vif == NULL) { Mld6igmpNode::delete_protocol(xrl_sender_name, src_module_id, vif_index, error_msg); error_msg = c_format("Cannot add protocol instance '%s' " "on vif %s with vif_index %d: " "no such vif", xrl_sender_name.c_str(), vif_name.c_str(), XORP_INT_CAST(vif_index)); return XrlCmdError::COMMAND_FAILED(error_msg); } Mld6igmpGroupSet::const_iterator iter; for (iter = mld6igmp_vif->group_records().begin(); iter != mld6igmp_vif->group_records().end(); ++iter) { const Mld6igmpGroupRecord *group_record = iter->second; send_add_membership(xrl_sender_name.c_str(), src_module_id, mld6igmp_vif->vif_index(), IPvX::ZERO(family()), group_record->group()); } // // Success // return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_delete_protocol4( // Input values, const string& xrl_sender_name, const string& , // protocol_name, const uint32_t& protocol_id, const string& vif_name, const uint32_t& vif_index) { UNUSED(vif_name); string error_msg; // // Verify the address family // if (! Mld6igmpNode::is_ipv4()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv4"); return XrlCmdError::COMMAND_FAILED(error_msg); } // // Verify the module ID // xorp_module_id src_module_id = static_cast(protocol_id); if (! is_valid_module_id(src_module_id)) { error_msg = c_format("Invalid module ID = %d", XORP_INT_CAST(protocol_id)); return XrlCmdError::COMMAND_FAILED(error_msg); } if (Mld6igmpNode::delete_protocol(xrl_sender_name, src_module_id, vif_index, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } // // Success // return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_delete_protocol6( // Input values, const string& xrl_sender_name, const string& , // protocol_name, const uint32_t& protocol_id, const string& vif_name, const uint32_t& vif_index) { UNUSED(vif_name); string error_msg; // // Verify the address family // if (! Mld6igmpNode::is_ipv6()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv6"); return XrlCmdError::COMMAND_FAILED(error_msg); } // // Verify the module ID // xorp_module_id src_module_id = static_cast(protocol_id); if (! is_valid_module_id(src_module_id)) { error_msg = c_format("Invalid module ID = %d", XORP_INT_CAST(protocol_id)); return XrlCmdError::COMMAND_FAILED(error_msg); } if (Mld6igmpNode::delete_protocol(xrl_sender_name, src_module_id, vif_index, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } // // Success // return XrlCmdError::OKAY(); } xorp/mld6igmp/mld6igmp_node_cli.hh0000664000076400007640000000556511540224230017277 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/mld6igmp/mld6igmp_node_cli.hh,v 1.12 2008/10/02 21:57:43 bms Exp $ #ifndef __MLD6IGMP_MLD6IGMP_NODE_CLI_HH__ #define __MLD6IGMP_MLD6IGMP_NODE_CLI_HH__ // // MLD6IGMP protocol CLI // #include "libproto/proto_node_cli.hh" // // Constants definitions // // // Structures/classes, typedefs and macros // // MLD6IGMP protocol CLI class class Mld6igmpNode; /** * @short The class for @ref Mld6igmpNode CLI access. */ class Mld6igmpNodeCli : public ProtoNodeCli { public: /** * Constructor for a given MLD6IGMP node. * * @param mld6igmp_node the @ref Mld6igmpNode this node belongs to. */ Mld6igmpNodeCli(Mld6igmpNode& mld6igmp_node); /** * Destructor */ virtual ~Mld6igmpNodeCli(); /** * Start the CLI operation. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int start(); /** * Stop the CLI operation. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int stop(); /** * Enable node operation. * * If an unit is not enabled, it cannot be start, or pending-start. */ void enable(); /** * Disable 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 disable(); /** * Install all MLD6IGMP-related CLI commands to the CLI. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_all_cli_commands(); private: Mld6igmpNode& mld6igmp_node() const { return (_mld6igmp_node); } Mld6igmpNode& _mld6igmp_node; // // MLD6IGMP CLI commands // int cli_show_mld6igmp_interface(const vector& argv); int cli_show_mld6igmp_interface_address(const vector& argv); int cli_show_mld6igmp_group(const vector& argv); }; // // Global variables // // // Global functions prototypes // #endif // __MLD6IGMP_MLD6IGMP_NODE_CLI_HH__ xorp/mld6igmp/mld6igmp_config.cc0000664000076400007640000003520711421137511016755 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // TODO: a temporary solution for various MLD6IGMP configuration // #include "mld6igmp_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvx.hh" #include "mld6igmp_node.hh" #include "mld6igmp_vif.hh" int Mld6igmpNode::set_config_all_vifs_done(string& error_msg) { map::iterator vif_iter; map& configured_vifs = ProtoNode::configured_vifs(); string dummy_error_msg; // // Add new vifs and update existing ones // for (vif_iter = configured_vifs.begin(); vif_iter != configured_vifs.end(); ++vif_iter) { Vif* vif = &vif_iter->second; Vif* node_vif = vif_find_by_name(vif->name()); if (vif->is_pim_register()) continue; // XXX: don't add the PIM Register vifs // // Add a new vif // if (node_vif == NULL) { add_vif(*vif, dummy_error_msg); continue; } // // Update the vif flags // set_vif_flags(vif->name(), vif->is_pim_register(), vif->is_p2p(), vif->is_loopback(), vif->is_multicast_capable(), vif->is_broadcast_capable(), vif->is_underlying_vif_up(), vif->mtu(), dummy_error_msg); } // // Add new vif addresses, update existing ones, and remove old addresses // for (vif_iter = configured_vifs.begin(); vif_iter != configured_vifs.end(); ++vif_iter) { Vif* vif = &vif_iter->second; Vif* node_vif = vif_find_by_name(vif->name()); list::const_iterator vif_addr_iter; if (vif->is_pim_register()) continue; // XXX: don't add the PIM Register vifs if (node_vif == NULL) continue; for (vif_addr_iter = vif->addr_list().begin(); vif_addr_iter != vif->addr_list().end(); ++vif_addr_iter) { const VifAddr& vif_addr = *vif_addr_iter; add_vif_addr(vif->name(), vif_addr.addr(), vif_addr.subnet_addr(), vif_addr.broadcast_addr(), vif_addr.peer_addr(), dummy_error_msg); } // // Delete vif addresses that don't exist anymore // { list delete_addresses_list; 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->find_address(vif_addr.addr()) == 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; delete_vif_addr(vif->name(), ipvx, dummy_error_msg); } } } // // Remove vifs that don't exist anymore // for (uint32_t i = 0; i < maxvifs(); i++) { Vif* node_vif = vif_find_by_vif_index(i); if (node_vif == NULL) continue; if (configured_vifs.find(node_vif->name()) == configured_vifs.end()) { // Delete the interface string vif_name = node_vif->name(); delete_vif(vif_name, dummy_error_msg); continue; } } if (end_config(error_msg) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int Mld6igmpNode::get_vif_proto_version(const string& vif_name, int& proto_version, string& error_msg) { Mld6igmpVif *mld6igmp_vif = vif_find_by_name(vif_name); if (mld6igmp_vif == NULL) { error_msg = c_format("Cannot get protocol version for vif %s: " "no such vif", vif_name.c_str()); return (XORP_ERROR); } proto_version = mld6igmp_vif->proto_version(); return (XORP_OK); } int Mld6igmpNode::set_vif_proto_version(const string& vif_name, int proto_version, string& error_msg) { Mld6igmpVif *mld6igmp_vif = vif_find_by_name(vif_name); if (start_config(error_msg) != XORP_OK) return (XORP_ERROR); if (mld6igmp_vif == NULL) { end_config(error_msg); error_msg = c_format("Cannot set protocol version for vif %s: " "no such vif", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } if (mld6igmp_vif->set_proto_version(proto_version) != XORP_OK) { end_config(error_msg); error_msg = c_format("Cannot set protocol version for vif %s: " "invalid protocol version %d", vif_name.c_str(), proto_version); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } if (end_config(error_msg) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int Mld6igmpNode::reset_vif_proto_version(const string& vif_name, string& error_msg) { Mld6igmpVif *mld6igmp_vif = vif_find_by_name(vif_name); if (start_config(error_msg) != XORP_OK) return (XORP_ERROR); if (mld6igmp_vif == NULL) { end_config(error_msg); error_msg = c_format("Cannot reset protocol version for vif %s: " "no such vif", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } mld6igmp_vif->set_proto_version(mld6igmp_vif->proto_version_default()); if (end_config(error_msg) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int Mld6igmpNode::get_vif_ip_router_alert_option_check(const string& vif_name, bool& enabled, string& error_msg) { Mld6igmpVif *mld6igmp_vif = vif_find_by_name(vif_name); if (mld6igmp_vif == NULL) { error_msg = c_format("Cannot get 'IP Router Alert option check' " "flag for vif %s: " "no such vif", vif_name.c_str()); return (XORP_ERROR); } enabled = mld6igmp_vif->ip_router_alert_option_check().get(); return (XORP_OK); } int Mld6igmpNode::set_vif_ip_router_alert_option_check(const string& vif_name, bool enable, string& error_msg) { Mld6igmpVif *mld6igmp_vif = vif_find_by_name(vif_name); if (start_config(error_msg) != XORP_OK) return (XORP_ERROR); if (mld6igmp_vif == NULL) { end_config(error_msg); error_msg = c_format("Cannot set 'IP Router Alert option check' " "flag for vif %s: " "no such vif", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } mld6igmp_vif->ip_router_alert_option_check().set(enable); if (end_config(error_msg) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int Mld6igmpNode::reset_vif_ip_router_alert_option_check(const string& vif_name, string& error_msg) { Mld6igmpVif *mld6igmp_vif = vif_find_by_name(vif_name); if (start_config(error_msg) != XORP_OK) return (XORP_ERROR); if (mld6igmp_vif == NULL) { end_config(error_msg); error_msg = c_format("Cannot reset 'IP Router Alert option check' " "flag for vif %s: " "no such vif", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } mld6igmp_vif->ip_router_alert_option_check().reset(); if (end_config(error_msg) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int Mld6igmpNode::get_vif_query_interval(const string& vif_name, TimeVal& interval, string& error_msg) { Mld6igmpVif *mld6igmp_vif = vif_find_by_name(vif_name); if (mld6igmp_vif == NULL) { error_msg = c_format("Cannot get Query Interval for vif %s: " "no such vif", vif_name.c_str()); return (XORP_ERROR); } interval = mld6igmp_vif->configured_query_interval().get(); return (XORP_OK); } int Mld6igmpNode::set_vif_query_interval(const string& vif_name, const TimeVal& interval, string& error_msg) { Mld6igmpVif *mld6igmp_vif = vif_find_by_name(vif_name); if (start_config(error_msg) != XORP_OK) return (XORP_ERROR); if (mld6igmp_vif == NULL) { end_config(error_msg); error_msg = c_format("Cannot set Query Interval for vif %s: " "no such vif", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } mld6igmp_vif->configured_query_interval().set(interval); if (end_config(error_msg) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int Mld6igmpNode::reset_vif_query_interval(const string& vif_name, string& error_msg) { Mld6igmpVif *mld6igmp_vif = vif_find_by_name(vif_name); if (start_config(error_msg) != XORP_OK) return (XORP_ERROR); if (mld6igmp_vif == NULL) { end_config(error_msg); error_msg = c_format("Cannot reset Query Interval for vif %s: " "no such vif", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } mld6igmp_vif->configured_query_interval().reset(); if (end_config(error_msg) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int Mld6igmpNode::get_vif_query_last_member_interval(const string& vif_name, TimeVal& interval, string& error_msg) { Mld6igmpVif *mld6igmp_vif = vif_find_by_name(vif_name); if (mld6igmp_vif == NULL) { error_msg = c_format("Cannot get Last Member Query Interval for vif %s: " "no such vif", vif_name.c_str()); return (XORP_ERROR); } interval = mld6igmp_vif->query_last_member_interval().get(); return (XORP_OK); } int Mld6igmpNode::set_vif_query_last_member_interval(const string& vif_name, const TimeVal& interval, string& error_msg) { Mld6igmpVif *mld6igmp_vif = vif_find_by_name(vif_name); if (start_config(error_msg) != XORP_OK) return (XORP_ERROR); if (mld6igmp_vif == NULL) { end_config(error_msg); error_msg = c_format("Cannot set Last Member Query Interval for vif %s: " "no such vif", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } mld6igmp_vif->query_last_member_interval().set(interval); if (end_config(error_msg) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int Mld6igmpNode::reset_vif_query_last_member_interval(const string& vif_name, string& error_msg) { Mld6igmpVif *mld6igmp_vif = vif_find_by_name(vif_name); if (start_config(error_msg) != XORP_OK) return (XORP_ERROR); if (mld6igmp_vif == NULL) { end_config(error_msg); error_msg = c_format("Cannot reset Last Member Query Interval for vif %s: " "no such vif", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } mld6igmp_vif->query_last_member_interval().reset(); if (end_config(error_msg) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int Mld6igmpNode::get_vif_query_response_interval(const string& vif_name, TimeVal& interval, string& error_msg) { Mld6igmpVif *mld6igmp_vif = vif_find_by_name(vif_name); if (mld6igmp_vif == NULL) { error_msg = c_format("Cannot get Query Response Interval for vif %s: " "no such vif", vif_name.c_str()); return (XORP_ERROR); } interval = mld6igmp_vif->query_response_interval().get(); return (XORP_OK); } int Mld6igmpNode::set_vif_query_response_interval(const string& vif_name, const TimeVal& interval, string& error_msg) { Mld6igmpVif *mld6igmp_vif = vif_find_by_name(vif_name); if (start_config(error_msg) != XORP_OK) return (XORP_ERROR); if (mld6igmp_vif == NULL) { end_config(error_msg); error_msg = c_format("Cannot set Query Response Interval for vif %s: " "no such vif", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } mld6igmp_vif->query_response_interval().set(interval); if (end_config(error_msg) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int Mld6igmpNode::reset_vif_query_response_interval(const string& vif_name, string& error_msg) { Mld6igmpVif *mld6igmp_vif = vif_find_by_name(vif_name); if (start_config(error_msg) != XORP_OK) return (XORP_ERROR); if (mld6igmp_vif == NULL) { end_config(error_msg); error_msg = c_format("Cannot reset Query Response Interval for vif %s: " "no such vif", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } mld6igmp_vif->query_response_interval().reset(); if (end_config(error_msg) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int Mld6igmpNode::get_vif_robust_count(const string& vif_name, uint32_t& robust_count, string& error_msg) { Mld6igmpVif *mld6igmp_vif = vif_find_by_name(vif_name); if (mld6igmp_vif == NULL) { error_msg = c_format("Cannot get Robustness Variable count for vif %s: " "no such vif", vif_name.c_str()); return (XORP_ERROR); } robust_count = mld6igmp_vif->configured_robust_count().get(); return (XORP_OK); } int Mld6igmpNode::set_vif_robust_count(const string& vif_name, uint32_t robust_count, string& error_msg) { Mld6igmpVif *mld6igmp_vif = vif_find_by_name(vif_name); if (start_config(error_msg) != XORP_OK) return (XORP_ERROR); if (mld6igmp_vif == NULL) { end_config(error_msg); error_msg = c_format("Cannot set Robustness Variable count for vif %s: " "no such vif", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } mld6igmp_vif->configured_robust_count().set(robust_count); if (end_config(error_msg) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int Mld6igmpNode::reset_vif_robust_count(const string& vif_name, string& error_msg) { Mld6igmpVif *mld6igmp_vif = vif_find_by_name(vif_name); if (start_config(error_msg) != XORP_OK) return (XORP_ERROR); if (mld6igmp_vif == NULL) { end_config(error_msg); error_msg = c_format("Cannot reset Robustness Variable count for vif %s: " "no such vif", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } mld6igmp_vif->configured_robust_count().reset(); if (end_config(error_msg) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } xorp/mld6igmp/mld6igmp_node.cc0000664000076400007640000012070411540225530016433 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // Multicast Listener Discovery and Internet Group Management Protocol // node implementation (common part). // IGMPv1 and IGMPv2 (RFC 2236), IGMPv3 (RFC 3376), // MLDv1 (RFC 2710), and MLDv2 (RFC 3810). // #include "mld6igmp_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvx.hh" #include "mld6igmp_node.hh" #include "mld6igmp_vif.hh" /** * Mld6igmpNode::Mld6igmpNode: * @family: The address family (%AF_INET or %AF_INET6 * for IPv4 and IPv6 respectively). * @module_id: The module ID (must be %XORP_MODULE_MLD6IGMP). * @eventloop: The event loop. * * MLD6IGMP node constructor. **/ Mld6igmpNode::Mld6igmpNode(int family, xorp_module_id module_id, EventLoop& eventloop) : ProtoNode(family, module_id, eventloop), _is_log_trace(false) { XLOG_ASSERT(module_id == XORP_MODULE_MLD6IGMP); if (module_id != XORP_MODULE_MLD6IGMP) { XLOG_FATAL("Invalid module ID = %d (must be 'XORP_MODULE_MLD6IGMP' = %d)", module_id, XORP_MODULE_MLD6IGMP); } _buffer_recv = BUFFER_MALLOC(BUF_SIZE_DEFAULT); // // Set the node status // ProtoNode::set_node_status(PROC_STARTUP); // // Set myself as an observer when the node status changes // set_observer(this); } /** * Mld6igmpNode::~Mld6igmpNode: * @: * * MLD6IGMP node destructor. * **/ Mld6igmpNode::~Mld6igmpNode() { // // Unset myself as an observer when the node status changes // unset_observer(this); stop(); ProtoNode::set_node_status(PROC_NULL); delete_all_vifs(); BUFFER_FREE(_buffer_recv); } /** * Mld6igmpNode::start: * @: * * Start the MLD or IGMP protocol. * TODO: This function should not start the protocol operation on the * interfaces. The interfaces must be activated separately. * After the startup operations are completed, * Mld6igmpNode::final_start() is called to complete the job. * * Return value: %XORP_OK on success, otherwize %XORP_ERROR. **/ int Mld6igmpNode::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() != XORP_OK) return (XORP_ERROR); // // Register with the FEA and MFEA // fea_register_startup(); mfea_register_startup(); // // Set the node status // ProtoNode::set_node_status(PROC_STARTUP); // // Update the node status // update_status(); return (XORP_OK); } /** * Mld6igmpNode::final_start: * @: * * Complete the start-up of the MLD/IGMP protocol. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int Mld6igmpNode::final_start() { #if 0 // TODO: XXX: PAVPAVPAV if (! is_pending_up()) return (XORP_ERROR); #endif if (ProtoNode::start() != XORP_OK) { ProtoNode::stop(); return (XORP_ERROR); } // Start the mld6igmp_vifs start_all_vifs(); XLOG_INFO("Protocol started"); return (XORP_OK); } /** * Mld6igmpNode::stop: * @: * * Gracefully stop the MLD or IGMP protocol. * XXX: After the cleanup is completed, * Mld6igmpNode::final_stop() is called to complete the job. * XXX: This function, unlike start(), will stop the protocol * operation on all interfaces. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int Mld6igmpNode::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() != XORP_OK) return (XORP_ERROR); // // Perform misc. MLD6IGMP-specific stop operations // // XXX: nothing to do // Stop the vifs stop_all_vifs(); // // Set the node status // ProtoNode::set_node_status(PROC_SHUTDOWN); // // Update the node status // update_status(); return (XORP_OK); } /** * Mld6igmpNode::final_stop: * @: * * Completely stop the MLD/IGMP protocol. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int Mld6igmpNode::final_stop() { if (! (is_up() || is_pending_up() || is_pending_down())) return (XORP_ERROR); if (ProtoNode::stop() != XORP_OK) return (XORP_ERROR); XLOG_INFO("Protocol stopped"); return (XORP_OK); } /** * Enable the node operation. * * If an unit is not enabled, it cannot be start, or pending-start. */ void Mld6igmpNode::enable() { ProtoUnit::enable(); XLOG_INFO("Protocol 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 Mld6igmpNode::disable() { stop(); ProtoUnit::disable(); XLOG_INFO("Protocol disabled"); } /** * Get the IP protocol number. * * @return the IP protocol number. */ uint8_t Mld6igmpNode::ip_protocol_number() const { if (proto_is_igmp()) return (IPPROTO_IGMP); if (proto_is_mld6()) return (IPPROTO_ICMPV6); XLOG_UNREACHABLE(); return (0); } void Mld6igmpNode::status_change(ServiceBase* service, ServiceStatus old_status, ServiceStatus new_status) { if (service == this) { if ((old_status == SERVICE_STARTING) && (new_status == SERVICE_RUNNING)) { // The startup process has completed if (final_start() != XORP_OK) { 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)) { decr_shutdown_requests_n(); // XXX: for the ifmgr } } } void Mld6igmpNode::tree_complete() { decr_startup_requests_n(); // XXX: for the ifmgr // // XXX: we use same actions when the tree is completed or updates are made // updates_made(); } void Mld6igmpNode::updates_made() { map::iterator mld6igmp_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.interfaces().begin(); ifmgr_iface_iter != _iftree.interfaces().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; mld6igmp_vif_iter = configured_vifs().find(ifmgr_vif_name); if (mld6igmp_vif_iter != configured_vifs().end()) { node_vif = &(mld6igmp_vif_iter->second); } // // Add a new vif // if (node_vif == NULL) { uint32_t vif_index = ifmgr_vif.vif_index(); XLOG_ASSERT(vif_index != Vif::VIF_INDEX_INVALID); if (add_config_vif(ifmgr_vif_name, vif_index, error_msg) != XORP_OK) { XLOG_ERROR("Cannot add vif %s to the set of configured " "vifs: %s", ifmgr_vif_name.c_str(), error_msg.c_str()); continue; } mld6igmp_vif_iter = configured_vifs().find(ifmgr_vif_name); XLOG_ASSERT(mld6igmp_vif_iter != configured_vifs().end()); node_vif = &(mld6igmp_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_vif.enabled(); set_config_vif_flags(ifmgr_vif_name, ifmgr_vif.pim_register(), ifmgr_vif.p2p_capable(), ifmgr_vif.loopback(), ifmgr_vif.multicast_capable(), ifmgr_vif.broadcast_capable(), is_up, ifmgr_iface.mtu(), error_msg); } } // // Add new vif addresses, update existing ones, and remove old addresses // for (ifmgr_iface_iter = _iftree.interfaces().begin(); ifmgr_iface_iter != _iftree.interfaces().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 // mld6igmp_vif_iter = configured_vifs().find(ifmgr_vif_name); if (mld6igmp_vif_iter != configured_vifs().end()) { node_vif = &(mld6igmp_vif_iter->second); } if (is_ipv4()) { IfMgrVifAtom::IPv4Map::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) != XORP_OK) { 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) != XORP_OK) { 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) != XORP_OK) { 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::IPv6Map::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) != XORP_OK) { 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) != XORP_OK) { 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) != XORP_OK) { 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) != XORP_OK) { 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 (mld6igmp_vif_iter = configured_vifs().begin(); mld6igmp_vif_iter != configured_vifs().end(); ++mld6igmp_vif_iter) { Vif* node_vif = &mld6igmp_vif_iter->second; #if 0 if (node_vif->is_pim_register()) continue; // XXX: don't delete the PIM Register vif #endif 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) != XORP_OK) { 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); } /** * Mld6igmpNode::add_vif: * @vif: Information about the new Mld6igmpVif to install. * @error_msg: The error message (if error). * * Install a new MLD/IGMP vif. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int Mld6igmpNode::add_vif(const Vif& vif, string& error_msg) { // // Create a new Mld6igmpVif // Mld6igmpVif *mld6igmp_vif = new Mld6igmpVif(*this, vif); if (ProtoNode::add_vif(mld6igmp_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 mld6igmp_vif; return (XORP_ERROR); } // // Update and check the primary address // do { if (mld6igmp_vif->update_primary_address(error_msg) == XORP_OK) break; if (mld6igmp_vif->addr_ptr() == NULL) { // XXX: don't print an error if the vif has no addresses break; } if (mld6igmp_vif->is_loopback() || mld6igmp_vif->is_pim_register()) { // XXX: don't print an error if this is a loopback or register_vif break; } XLOG_ERROR("Error updating primary address for vif %s: %s", mld6igmp_vif->name().c_str(), error_msg.c_str()); return (XORP_ERROR); } while (false); XLOG_INFO("mld6igmp: Interface added: %s", mld6igmp_vif->str().c_str()); return XORP_OK; } /** * Mld6igmpNode::add_vif: * @vif_name: The name of the new vif. * @vif_index: The vif index of the new vif. * @error_msg: The error message (if error). * * Install a new MLD/IGMP vif. If the vif exists, nothing is installed. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int Mld6igmpNode::add_vif(const string& vif_name, uint32_t vif_index, string& error_msg) { Mld6igmpVif *mld6igmp_vif = vif_find_by_vif_index(vif_index); if ((mld6igmp_vif != NULL) && (mld6igmp_vif->name() == vif_name)) { return XORP_OK; // Already have this vif } // // Create a new Vif // Vif vif(vif_name); vif.set_vif_index(vif_index); return add_vif(vif, error_msg); } /** * Mld6igmpNode::delete_vif: * @vif_name: The name of the vif to delete. * @error_msg: The error message (if error). * * Delete an existing MLD/IGMP vif. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int Mld6igmpNode::delete_vif(const string& vif_name, string& error_msg) { Mld6igmpVif *mld6igmp_vif = vif_find_by_name(vif_name); if (mld6igmp_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(mld6igmp_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 mld6igmp_vif; return (XORP_ERROR); } delete mld6igmp_vif; XLOG_INFO("Interface deleted: %s", vif_name.c_str()); return (XORP_OK); } int Mld6igmpNode::set_vif_flags(const string& vif_name, bool is_pim_register, bool is_p2p, bool is_loopback, bool is_multicast, bool is_broadcast, bool is_up, uint32_t mtu, string& error_msg) { bool is_changed = false; Mld6igmpVif *mld6igmp_vif = vif_find_by_name(vif_name); if (mld6igmp_vif == NULL) { error_msg = c_format("Cannot set flags vif %s: no such vif", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } if (mld6igmp_vif->is_pim_register() != is_pim_register) { mld6igmp_vif->set_pim_register(is_pim_register); is_changed = true; } if (mld6igmp_vif->is_p2p() != is_p2p) { mld6igmp_vif->set_p2p(is_p2p); is_changed = true; } if (mld6igmp_vif->is_loopback() != is_loopback) { mld6igmp_vif->set_loopback(is_loopback); is_changed = true; } if (mld6igmp_vif->is_multicast_capable() != is_multicast) { mld6igmp_vif->set_multicast_capable(is_multicast); is_changed = true; } if (mld6igmp_vif->is_broadcast_capable() != is_broadcast) { mld6igmp_vif->set_broadcast_capable(is_broadcast); is_changed = true; } if (mld6igmp_vif->is_underlying_vif_up() != is_up) { mld6igmp_vif->set_underlying_vif_up(is_up); is_changed = true; } if (mld6igmp_vif->mtu() != mtu) { mld6igmp_vif->set_mtu(mtu); is_changed = true; } if (is_changed) { XLOG_INFO("Interface flags changed: %s", mld6igmp_vif->str().c_str()); mld6igmp_vif->notifyUpdated(); } return (XORP_OK); } int Mld6igmpNode::add_vif_addr(const string& vif_name, const IPvX& addr, const IPvXNet& subnet_addr, const IPvX& broadcast_addr, const IPvX& peer_addr, string& error_msg) { Mld6igmpVif *mld6igmp_vif = vif_find_by_name(vif_name); if (mld6igmp_vif == NULL) { error_msg = c_format("Cannot add address on vif %s: no such vif", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } const VifAddr vif_addr(addr, subnet_addr, broadcast_addr, peer_addr); // // Check the arguments // if (! addr.is_unicast()) { error_msg = c_format("Cannot add address on vif %s: " "invalid unicast address: %s", vif_name.c_str(), addr.str().c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } if ((addr.af() != family()) || (subnet_addr.af() != family()) || (broadcast_addr.af() != family()) || (peer_addr.af() != family())) { error_msg = c_format("Cannot add address on vif %s: " "invalid address family: %s ", vif_name.c_str(), vif_addr.str().c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } VifAddr* node_vif_addr = mld6igmp_vif->find_address(addr); if ((node_vif_addr != NULL) && (*node_vif_addr == vif_addr)) return (XORP_OK); // Already have this address // // TODO: If an interface changes its primary IP address, then // we should do something about it. // // However, by adding or updating an existing address we cannot // change a valid primary address, hence we do nothing here. // if (node_vif_addr != NULL) { // Update the address XLOG_INFO("Updated existing address on vif %s: old is %s new is %s", mld6igmp_vif->name().c_str(), node_vif_addr->str().c_str(), vif_addr.str().c_str()); *node_vif_addr = vif_addr; } else { // Add a new address mld6igmp_vif->add_address(vif_addr); XLOG_INFO("Added new address to vif %s: %s", mld6igmp_vif->name().c_str(), vif_addr.str().c_str()); } // // Update and check the primary address // do { if (mld6igmp_vif->update_primary_address(error_msg) == XORP_OK) break; if (! (mld6igmp_vif->is_up() || mld6igmp_vif->is_pending_up())) { // XXX: print an error only if the interface is UP or PENDING_UP break; } if (mld6igmp_vif->is_loopback() || mld6igmp_vif->is_pim_register()) { // XXX: don't print an error if this is a loopback or register_vif break; } XLOG_ERROR("Error updating primary address for vif %s: %s", mld6igmp_vif->name().c_str(), error_msg.c_str()); return (XORP_ERROR); } while (false); mld6igmp_vif->notifyUpdated(); // might be able to start now if it wants to... return (XORP_OK); } int Mld6igmpNode::delete_vif_addr(const string& vif_name, const IPvX& addr, string& error_msg) { Mld6igmpVif *mld6igmp_vif = vif_find_by_name(vif_name); if (mld6igmp_vif == NULL) { error_msg = c_format("Cannot delete address on vif %s: no such vif", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } const VifAddr *tmp_vif_addr = mld6igmp_vif->find_address(addr); if (tmp_vif_addr == NULL) { error_msg = c_format("Cannot delete address on vif %s: " "invalid address %s", vif_name.c_str(), addr.str().c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } VifAddr vif_addr = *tmp_vif_addr; // Get a copy // // Get the vif's old primary address and whether the vif is UP // bool old_vif_is_up = mld6igmp_vif->is_up() || mld6igmp_vif->is_pending_up(); IPvX old_primary_addr = mld6igmp_vif->primary_addr(); // // If an interface's primary address is deleted, first stop the vif. // if (old_vif_is_up) { if (mld6igmp_vif->primary_addr() == addr) { string dummy_error_msg; mld6igmp_vif->stop(dummy_error_msg); } } if (mld6igmp_vif->delete_address(addr) != XORP_OK) { XLOG_UNREACHABLE(); return (XORP_ERROR); } XLOG_INFO("Deleted address on interface %s: %s", mld6igmp_vif->name().c_str(), vif_addr.str().c_str()); // // Update and check the primary address. // If the vif has no primary address, then stop it. // If the vif's primary address was changed, then restart the vif. // do { string dummy_error_msg; if (mld6igmp_vif->update_primary_address(error_msg) != XORP_OK) { XLOG_ERROR("Error updating primary address for vif %s: %s", mld6igmp_vif->name().c_str(), error_msg.c_str()); } if (mld6igmp_vif->primary_addr().is_zero()) { mld6igmp_vif->stop(dummy_error_msg); break; } if (old_primary_addr == mld6igmp_vif->primary_addr()) break; // Nothing changed // Conditionally restart the interface mld6igmp_vif->stop(dummy_error_msg); if (old_vif_is_up) mld6igmp_vif->start(dummy_error_msg); break; } while (false); return (XORP_OK); } /** * Mld6igmpNode::enable_vif: * @vif_name: The name of the vif to enable. * @error_msg: The error message (if error). * * Enable an existing MLD6IGMP vif. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int Mld6igmpNode::enable_vif(const string& vif_name, string& error_msg) { Mld6igmpVif *mld6igmp_vif = vif_find_by_name(vif_name); if (mld6igmp_vif == NULL) { // Seems we have some sort of race...it's asked to be enabled before it is // created on config-file reload. // For now, create it manually. // TODO: Fix enable-vif on cfg-file load. // NOTE: pim has similar issue and similar fixup code. error_msg = c_format("Mld6igmpNode: Cannot enable vif %s: no such vif (will attempt to create it))", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); int if_index = -1; errno = 0; #ifdef HAVE_IF_NAMETOINDEX if_index = if_nametoindex(vif_name.c_str()); #endif if (if_index < 0) { XLOG_ERROR("Could not convert vif_name to ifindex: %s possible error: %s\n", vif_name.c_str(), strerror(errno)); return XORP_ERROR; } else { add_vif(vif_name, if_index, error_msg); mld6igmp_vif = vif_find_by_name(vif_name); } } mld6igmp_vif->enable(); return (XORP_OK); } /** * Mld6igmpNode::disable_vif: * @vif_name: The name of the vif to disable. * @error_msg: The error message (if error). * * Disable an existing MLD6IGMP vif. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int Mld6igmpNode::disable_vif(const string& vif_name, string& error_msg) { Mld6igmpVif *mld6igmp_vif = vif_find_by_name(vif_name); if (mld6igmp_vif == NULL) { error_msg = c_format("Cannot disable vif %s: no such vif", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); // It's as disabled as it will get, don't fail commit. error_msg = ""; return XORP_OK; } mld6igmp_vif->disable(); return (XORP_OK); } /** * Mld6igmpNode::start_vif: * @vif_name: The name of the vif to start. * @error_msg: The error message (if error). * * Start an existing MLD6IGMP vif. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int Mld6igmpNode::start_vif(const string& vif_name, string& error_msg) { Mld6igmpVif *mld6igmp_vif = vif_find_by_name(vif_name); if (mld6igmp_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 (mld6igmp_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); } return (XORP_OK); } /** * Mld6igmpNode::stop_vif: * @vif_name: The name of the vif to stop. * @error_msg: The error message (if error). * * Stop an existing MLD6IGMP vif. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int Mld6igmpNode::stop_vif(const string& vif_name, string& error_msg) { Mld6igmpVif *mld6igmp_vif = vif_find_by_name(vif_name); if (mld6igmp_vif == NULL) { error_msg = c_format("Cannot stop vif %s: no such vif (will continue)", vif_name.c_str()); XLOG_WARNING("%s", error_msg.c_str()); // VIF is gone, it's as stopped as it will get. Returning error will cause entire // commit to fail. return XORP_OK; } if (mld6igmp_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); } /** * Mld6igmpNode::start_all_vifs: * @: * * Start MLD/IGMP on all enabled interfaces. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int Mld6igmpNode::start_all_vifs() { vector::iterator iter; string error_msg; int ret_value = XORP_OK; for (iter = proto_vifs().begin(); iter != proto_vifs().end(); ++iter) { Mld6igmpVif *mld6igmp_vif = (*iter); if (mld6igmp_vif == NULL) continue; if (start_vif(mld6igmp_vif->name(), error_msg) != XORP_OK) ret_value = XORP_ERROR; } return (ret_value); } /** * Mld6igmpNode::stop_all_vifs: * @: * * Stop MLD/IGMP on all interfaces it was running on. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int Mld6igmpNode::stop_all_vifs() { vector::iterator iter; string error_msg; int ret_value = XORP_OK; for (iter = proto_vifs().begin(); iter != proto_vifs().end(); ++iter) { Mld6igmpVif *mld6igmp_vif = (*iter); if (mld6igmp_vif == NULL) continue; if (stop_vif(mld6igmp_vif->name(), error_msg) != XORP_OK) ret_value = XORP_ERROR; } return (ret_value); } /** * Mld6igmpNode::enable_all_vifs: * @: * * Enable MLD/IGMP on all interfaces. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int Mld6igmpNode::enable_all_vifs() { vector::iterator iter; string error_msg; int ret_value = XORP_OK; for (iter = proto_vifs().begin(); iter != proto_vifs().end(); ++iter) { Mld6igmpVif *mld6igmp_vif = (*iter); if (mld6igmp_vif == NULL) continue; if (enable_vif(mld6igmp_vif->name(), error_msg) != XORP_OK) ret_value = XORP_ERROR; } return (ret_value); } /** * Mld6igmpNode::disable_all_vifs: * @: * * Disable MLD/IGMP on all interfaces. All running interfaces are stopped * first. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int Mld6igmpNode::disable_all_vifs() { vector::iterator iter; string error_msg; int ret_value = XORP_OK; for (iter = proto_vifs().begin(); iter != proto_vifs().end(); ++iter) { Mld6igmpVif *mld6igmp_vif = (*iter); if (mld6igmp_vif == NULL) continue; if (disable_vif(mld6igmp_vif->name(), error_msg) != XORP_OK) ret_value = XORP_ERROR; } return (ret_value); } /** * Mld6igmpNode::delete_all_vifs: * @: * * Delete all MLD/IGMP vifs. **/ void Mld6igmpNode::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) { Mld6igmpVif *mld6igmp_vif = (*iter); if (mld6igmp_vif != NULL) { string vif_name = mld6igmp_vif->name(); vif_names.push_back(mld6igmp_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 Mld6igmpNode::vif_shutdown_completed(const string& vif_name) { vector::iterator iter; // // If all vifs have completed the shutdown, then de-register with // the MFEA. // for (iter = proto_vifs().begin(); iter != proto_vifs().end(); ++iter) { Mld6igmpVif *mld6igmp_vif = *iter; if (mld6igmp_vif == NULL) continue; if (! mld6igmp_vif->is_down()) return; } // // De-register with the FEA and MFEA // if (ServiceBase::status() == SERVICE_SHUTTING_DOWN) { mfea_register_shutdown(); fea_register_shutdown(); } UNUSED(vif_name); } int Mld6igmpNode::proto_recv(const string& if_name, const string& vif_name, const IPvX& src_address, const IPvX& dst_address, uint8_t ip_protocol, int32_t ip_ttl, int32_t ip_tos, bool ip_router_alert, bool ip_internet_control, const vector& payload, string& error_msg) { Mld6igmpVif *mld6igmp_vif = NULL; int ret_value = XORP_ERROR; debug_msg("Received message on %s/%s from %s to %s: " "ip_ttl = %d ip_tos = %#x ip_router_alert = %d " "ip_internet_control = %d rcvlen = %u\n", if_name.c_str(), vif_name.c_str(), cstring(src_address), cstring(dst_address), ip_ttl, ip_tos, ip_router_alert, ip_internet_control, XORP_UINT_CAST(payload.size())); UNUSED(if_name); // // XXX: We registered to receive only one protocol, hence we ignore // the ip_protocol value. // UNUSED(ip_protocol); // // Check whether the node is up. // if (! is_up()) { error_msg = c_format("MLD/IGMP node is not UP"); return (XORP_ERROR); } // // Find the vif for that packet // mld6igmp_vif = vif_find_by_name(vif_name); if (mld6igmp_vif == NULL) { error_msg = c_format("Cannot find vif with vif_name = %s", vif_name.c_str()); return (XORP_ERROR); } // Copy the data to the receiving #buffer_t BUFFER_RESET(_buffer_recv); BUFFER_PUT_DATA(&payload[0], _buffer_recv, payload.size()); // Process the data by the vif ret_value = mld6igmp_vif->mld6igmp_recv(src_address, dst_address, ip_ttl, ip_tos, ip_router_alert, ip_internet_control, _buffer_recv, error_msg); return (ret_value); buflen_error: XLOG_UNREACHABLE(); return (XORP_ERROR); } int Mld6igmpNode::mld6igmp_send(const string& if_name, const string& vif_name, const IPvX& src_address, const IPvX& dst_address, uint8_t ip_protocol, int32_t ip_ttl, int32_t ip_tos, bool ip_router_alert, bool ip_internet_control, buffer_t *buffer, string& error_msg) { if (! is_up()) { error_msg = c_format("MLD/IGMP node is not UP"); return (XORP_ERROR); } if (proto_send(if_name, vif_name, src_address, dst_address, ip_protocol, ip_ttl, ip_tos, ip_router_alert, ip_internet_control, BUFFER_DATA_HEAD(buffer), BUFFER_DATA_SIZE(buffer), error_msg) != XORP_OK) { return (XORP_ERROR); } return (XORP_OK); } /** * Mld6igmpNode::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. * @vif_index: The vif index of the interface to add the protocol to. * * Add a protocol to the list of entries that would be notified if there * is membership change on a particular interface. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int Mld6igmpNode::add_protocol(const string& module_instance_name, xorp_module_id module_id, uint32_t vif_index) { Mld6igmpVif *mld6igmp_vif = vif_find_by_vif_index(vif_index); if (mld6igmp_vif == NULL) { XLOG_ERROR("Cannot add protocol instance %s on vif_index %d: " "no such vif", module_instance_name.c_str(), vif_index); return (XORP_ERROR); } if (mld6igmp_vif->add_protocol(module_id, module_instance_name) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } /** * Mld6igmpNode::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. * @vif_index: The vif index of the interface to delete the protocol from. * * Delete a protocol from the list of entries that would be notified if there * is membership change on a particular interface. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int Mld6igmpNode::delete_protocol(const string& module_instance_name, xorp_module_id module_id, uint32_t vif_index, string& error_msg) { Mld6igmpVif *mld6igmp_vif = vif_find_by_vif_index(vif_index); if (mld6igmp_vif == NULL) { ostringstream oss; oss << "Cannot delete protocol instance: " << module_instance_name << " on vif_index: " << vif_index << ". No such vif."; XLOG_ERROR("%s", oss.str().c_str()); error_msg.append(oss.str()); return (XORP_ERROR); } if (mld6igmp_vif->delete_protocol(module_id, module_instance_name, error_msg) != XORP_OK) { return (XORP_ERROR); } return (XORP_OK); } /** * Mld6igmpNode::join_prune_notify_routing: * @module_instance_name: The module instance name of the protocol to notify. * @module_id: The #xorp_module_id of the protocol to notify. * @vif_index: The vif index of the interface with membership change. * @source: The source address of the (S,G) or (*,G) entry that has changed. * In case of group-specific membership, it is IPvX::ZERO(). * @group: The group address of the (S,G) or (*,G) entry that has changed. * @action_jp: The membership change type #action_jp_t: * either %ACTION_JOIN or %ACTION_PRUNE. * * Notify the protocol instance with name @module_instance_name that there is * multicast membership change on interface with vif index of @vif_index. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int Mld6igmpNode::join_prune_notify_routing(const string& module_instance_name, xorp_module_id module_id, uint32_t vif_index, const IPvX& source, const IPvX& group, action_jp_t action_jp) { // // Send add/delete membership to the registered protocol instance. // switch (action_jp) { case ACTION_JOIN: send_add_membership(module_instance_name, module_id, vif_index, source, group); break; case ACTION_PRUNE: send_delete_membership(module_instance_name, module_id, vif_index, source, group); break; default: XLOG_UNREACHABLE(); break; } return (XORP_OK); } /** * Mld6igmpNode::is_directly_connected: * @mld6igmp_vif: The virtual interface to test against. * @ipaddr_test: The address to test. * * Note that the virtual interface the address is directly connected to * must be UP. * * Return value: True if @ipaddr_test is directly connected to @mld6igmp_vif, * otherwise false. **/ bool Mld6igmpNode::is_directly_connected(const Mld6igmpVif& mld6igmp_vif, const IPvX& ipaddr_test) const { if (! mld6igmp_vif.is_up()) return (false); #if 0 // TODO: not implemented yet // // Test the alternative subnets // list::const_iterator iter; for (iter = mld6igmp_vif.alternative_subnet_list().begin(); iter != mld6igmp_vif.alternative_subnet_list().end(); ++iter) { const IPvXNet& ipvxnet = *iter; if (ipvxnet.contains(ipaddr_test)) return true; } #endif // // Test the same subnet addresses, or the P2P addresses // return (mld6igmp_vif.is_same_subnet(ipaddr_test) || mld6igmp_vif.is_same_p2p(ipaddr_test)); } xorp/mld6igmp/mld6igmp_module.h0000664000076400007640000000235511421137511016635 0ustar greearbgreearb/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- */ /* * Copyright (c) 2001-2009 XORP, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 2, June * 1991 as published by the Free Software Foundation. Redistribution * and/or modification of this program under the terms of any other * version of the GNU General Public License is not permitted. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, * see the GNU General Public License, Version 2, a copy of which can be * found in the XORP LICENSE.gpl file. * * XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; * http://xorp.net */ /* * $XORP: xorp/mld6igmp/mld6igmp_module.h,v 1.11 2008/10/02 21:57:43 bms Exp $ */ /* * Module definitions. */ #ifndef __MLD6IGMP_MLD6IGMP_MODULE_H__ #define __MLD6IGMP_MLD6IGMP_MODULE_H__ #ifndef XORP_MODULE_NAME #define XORP_MODULE_NAME "MLD6IGMP" #endif #ifndef XORP_MODULE_VERSION #define XORP_MODULE_VERSION "0.1" #endif #endif /* __MLD6IGMP_MLD6IGMP_MODULE_H__ */ xorp/mld6igmp/mld6_proto.h0000664000076400007640000001336611421137511015642 0ustar greearbgreearb/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- */ /* * Copyright (c) 2001-2009 XORP, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 2, June * 1991 as published by the Free Software Foundation. Redistribution * and/or modification of this program under the terms of any other * version of the GNU General Public License is not permitted. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, * see the GNU General Public License, Version 2, a copy of which can be * found in the XORP LICENSE.gpl file. * * XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; * http://xorp.net */ /* * $XORP: xorp/mld6igmp/mld6_proto.h,v 1.23 2008/10/02 21:57:43 bms Exp $ */ #ifndef __MLD6IGMP_MLD6_PROTO_H__ #define __MLD6IGMP_MLD6_PROTO_H__ /* * Multicast Listener Discovery protocol-specific definitions. * MLDv1 (RFC 2710), and MLDv2 (RFC 3810). */ #ifdef HAVE_NETINET_IN_H #include #endif #ifdef HAVE_NETINET_ICMP6_H #include #endif /* * Constants definitions */ #ifndef IPPROTO_ICMPV6 #define IPPROTO_ICMPV6 58 #endif /* MLD versions definition */ #define MLD_V1 1 #define MLD_V2 2 #define MLD_VERSION_MIN MLD_V1 #define MLD_VERSION_MAX MLD_V2 #define MLD_VERSION_DEFAULT MLD_V1 /* * Constans for Multicast Listener Discovery protocol for IPv6. * All intervals are in seconds. * XXX: Several of these, especially the robustness variable, should be * variables and not constants. */ #define MLD_ROBUSTNESS_VARIABLE 2 #define MLD_QUERY_INTERVAL 125 #define MLD_QUERY_RESPONSE_INTERVAL 10 #define MLD_MULTICAST_LISTENER_INTERVAL (MLD_ROBUSTNESS_VARIABLE \ * MLD_QUERY_INTERVAL \ + MLD_QUERY_RESPONSE_INTERVAL) #define MLD_OTHER_QUERIER_PRESENT_INTERVAL (MLD_ROBUSTNESS_VARIABLE \ * MLD_QUERY_INTERVAL \ + MLD_QUERY_RESPONSE_INTERVAL / 2) #define MLD_STARTUP_QUERY_INTERVAL (MLD_QUERY_INTERVAL / 4) #define MLD_STARTUP_QUERY_COUNT MLD_ROBUSTNESS_VARIABLE #define MLD_LAST_LISTENER_QUERY_INTERVAL 1 #define MLD_LAST_LISTENER_QUERY_COUNT MLD_ROBUSTNESS_VARIABLE #define MLD_OLDER_VERSION_HOST_PRESENT_INTERVAL (MLD_ROBUSTNESS_VARIABLE \ * MLD_QUERY_INTERVAL \ + MLD_QUERY_RESPONSE_INTERVAL) #ifndef MLD_TIMER_SCALE /* the MLD max. response delay is in 1000th of seconds */ #define MLD_TIMER_SCALE 1000 #endif /* * MLDv1-related missing definitions * * Note that on newer systems all MLD-related definitions use * mld_xxx and MLD_XXX instead of mld6_xxx and MLD6_XXX. */ #ifndef MLD_LISTENER_QUERY # ifdef MLD6_LISTENER_QUERY # define MLD_LISTENER_QUERY MLD6_LISTENER_QUERY # else # define MLD_LISTENER_QUERY 130 # endif #endif #ifndef MLD_LISTENER_REPORT # ifdef MLD6_LISTENER_REPORT # define MLD_LISTENER_REPORT MLD6_LISTENER_REPORT # else # define MLD_LISTENER_REPORT 131 # endif #endif #ifndef MLD_LISTENER_DONE # ifdef MLD6_LISTENER_DONE # define MLD_LISTENER_DONE MLD6_LISTENER_DONE # else # define MLD_LISTENER_DONE 132 # endif #endif #ifndef MLD_MTRACE_RESP # ifdef MLD6_MTRACE_RESP # define MLD_MTRACE_RESP MLD6_MTRACE_RESP # else # define MLD_MTRACE_RESP 200 # endif #endif #ifndef MLD_MTRACE # ifdef MLD6_MTRACE # define MLD_MTRACE MLD6_MTRACE # else # define MLD_MTRACE 201 # endif #endif #ifndef MLD_MINLEN # ifdef HAVE_STRUCT_MLD_HDR # define MLD_MINLEN (sizeof(struct mld_hdr)) # else # define MLD_MINLEN 24 # endif #endif /* * MLDv2-related missing definitions */ #ifndef MLDV2_LISTENER_REPORT # ifdef MLD6V2_LISTENER_REPORT # define MLDV2_LISTENER_REPORT MLD6V2_LISTENER_REPORT # else # define MLDV2_LISTENER_REPORT 143 # endif #endif #ifndef MLD_MODE_IS_INCLUDE # define MLD_MODE_IS_INCLUDE 1 #endif #ifndef MLD_MODE_IS_EXCLUDE # define MLD_MODE_IS_EXCLUDE 2 #endif #ifndef MLD_CHANGE_TO_INCLUDE_MODE # define MLD_CHANGE_TO_INCLUDE_MODE 3 #endif #ifndef MLD_CHANGE_TO_EXCLUDE_MODE # define MLD_CHANGE_TO_EXCLUDE_MODE 4 #endif #ifndef MLD_ALLOW_NEW_SOURCES # define MLD_ALLOW_NEW_SOURCES 5 #endif #ifndef MLD_BLOCK_OLD_SOURCES # define MLD_BLOCK_OLD_SOURCES 6 #endif #ifndef MLD_V2_QUERY_MINLEN # define MLD_V2_QUERY_MINLEN 28 #endif #ifndef MLD_HOST_MRC_EXP # define MLD_HOST_MRC_EXP(x) (((x) >> 12) & 0x0007) #endif #ifndef MLD_HOST_MRC_MANT # define MLD_HOST_MRC_MANT(x) ((x) & 0x0fff) #endif #ifndef MLD_QQIC_EXP # define MLD_QQIC_EXP(x) (((x) >> 4) & 0x07) #endif #ifndef MLD_QQIC_MANT # define MLD_QQIC_MANT(x) ((x) & 0x0f) #endif #ifndef MLD_QRESV # define MLD_QRESV(x) (((x) >> 4) & 0x0f) #endif #ifndef MLD_SFLAG # define MLD_SFLAG(x) (((x) >> 3) & 0x01) #endif #ifndef MLD_QRV # define MLD_QRV(x) ((x) & 0x07) #endif /* * Structures, typedefs and macros */ /* * The ASCII names of the MLD protocol control messages */ #ifdef MLDV2_LISTENER_REPORT #define MLDV2TYPE2ASCII(t) \ (((t) == MLDV2_LISTENER_REPORT) ? \ "MLDV2_LISTENER_REPORT" \ : "MLD_type_unknown") #else #define MLDV2TYPE2ASCII(t) "MLD_type_unknown" #endif #define MLDTYPE2ASCII(t) \ (((t) == MLD_LISTENER_QUERY) ? \ "MLD_LISTENER_QUERY" \ : ((t) == MLD_LISTENER_REPORT) ? \ "MLD_LISTENER_REPORT" \ : ((t) == MLD_LISTENER_DONE) ? \ "MLD_LISTENER_DONE" \ : ((t) == MLD_MTRACE_RESP) ? \ "MLD_MTRACE_RESP" \ : ((t) == MLD_MTRACE) ? \ "MLD_MTRACE" \ : MLDV2TYPE2ASCII(t)) /* * Global variables */ /* * Global functions prototypes */ __BEGIN_DECLS __END_DECLS #endif /* __MPD6IGMP_MLD6_PROTO_H__ */ xorp/mld6igmp/mld6igmp_node.hh0000664000076400007640000007550611540225530016456 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __MLD6IGMP_MLD6IGMP_NODE_HH__ #define __MLD6IGMP_MLD6IGMP_NODE_HH__ // // IGMP and MLD node definition. // #include "libxorp/vif.hh" #include "libproto/proto_node.hh" #include "libfeaclient/ifmgr_xrl_mirror.hh" #include "mrt/buffer.h" #include "mrt/multicast_defs.h" class EventLoop; class IPvX; class IPvXNet; class Mld6igmpVif; /** * @short The MLD/IGMP node class. * * There should be one node per MLD or IGMP instance. There should be * one instance per address family. */ class Mld6igmpNode : public ProtoNode, public IfMgrHintObserver, public ServiceChangeObserverBase { public: /** * Constructor for a given address family, module ID, and event loop. * * @param family the address family (AF_INET or AF_INET6 for * IPv4 and IPv6 respectively). * @param module_id the module ID (@ref xorp_module_id). Should be * equal to XORP_MODULE_MLD6IGMP. * @param eventloop the event loop to use. */ Mld6igmpNode(int family, xorp_module_id module_id, EventLoop& eventloop); /** * Destructor */ virtual ~Mld6igmpNode(); /** * Start the node operation. * * Start the MLD or IGMP protocol. * After the startup operations are completed, * @ref Mld6igmpNode::final_start() is called internally * to complete the job. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int start(); /** * Stop the node operation. * * Gracefully stop the MLD or IGMP protocol. * After the shutdown operations are completed, * @ref Mld6igmpNode::final_stop() is called internally * to complete the job. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int stop(); /** * Completely start the node operation. * * This method should be called internally after @ref Mld6igmpNode::start() * to complete the job. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int final_start(); /** * Completely stop the node operation. * * This method should be called internally after @ref Mld6igmpNode::stop() * to complete the job. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int final_stop(); /** * Enable node operation. * * If an unit is not enabled, it cannot be start, or pending-start. */ void enable(); /** * Disable 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 disable(); /** * Get the IP protocol number. * * @return the IP protocol number. */ uint8_t ip_protocol_number() const; /** * Install a new MLD/IGMP vif. * * @param vif vif information about the new Mld6igmpVif to install. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_vif(const Vif& vif, string& error_msg); /** * Install a new MLD/IGMP vif. * * @param vif_name the name of the new vif. * @param vif_index the vif index of the new vif. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_vif(const string& vif_name, uint32_t vif_index, string& error_msg); /** * Delete an existing MLD/IGMP vif. * * @param vif_name the name of the vif to delete. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int delete_vif(const string& vif_name, string& error_msg); /** * Set flags to a vif. * * @param vif_name the name of the vif. * @param is_pim_register true if this is a PIM Register vif. * @param is_p2p true if this is a point-to-point vif. * @param is_loopback true if this is a loopback interface. * @param is_multicast true if the vif is multicast-capable. * @param is_broadcast true if the vif is broadcast-capable. * @param is_up true if the vif is UP and running. * @param mtu the MTU of the vif. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_vif_flags(const string& vif_name, bool is_pim_register, bool is_p2p, bool is_loopback, bool is_multicast, bool is_broadcast, bool is_up, uint32_t mtu, string& error_msg); /** * Add an address to a vif. * * @param vif_name the name of the vif. * @param addr the unicast address to add. * @param subnet_addr the subnet address to add. * @param broadcast_addr the broadcast address (when applicable). * @param peer_addr the peer address (when applicable). * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_vif_addr(const string& vif_name, const IPvX& addr, const IPvXNet& subnet_addr, const IPvX& broadcast_addr, const IPvX& peer_addr, string& error_msg); /** * Delete an address from a vif. * * @param vif_name the name of the vif. * @param addr the unicast address to delete. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int delete_vif_addr(const string& vif_name, const IPvX& addr, string& error_msg); /** * Enable an existing MLD6IGMP vif. * * @param vif_name the name of the vif to enable. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int enable_vif(const string& vif_name, string& error_msg); /** * Disable an existing MLD6IGMP vif. * * @param vif_name the name of the vif to disable. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int disable_vif(const string& vif_name, string& error_msg); /** * Start an existing MLD6IGMP vif. * * @param vif_name the name of the vif to start. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int start_vif(const string& vif_name, string& error_msg); /** * Stop an existing MLD6IGMP vif. * * @param vif_name the name of the vif to start. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int stop_vif(const string& vif_name, string& error_msg); /** * Start MLD/IGMP on all enabled interfaces. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int start_all_vifs(); /** * Stop MLD/IGMP on all interfaces it was running on. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int stop_all_vifs(); /** * Enable MLD/IGMP on all interfaces. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int enable_all_vifs(); /** * Disable MLD/IGMP on all interfaces. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int disable_all_vifs(); /** * Delete all MLD/IGMP vifs. */ void delete_all_vifs(); /** * A method called when a vif has completed its shutdown. * * @param vif_name the name of the vif that has completed its shutdown. */ void vif_shutdown_completed(const string& vif_name); /** * Receive a protocol packet. * * @param if_name the interface name the packet arrived on. * @param vif_name the vif name the packet arrived on. * @param src_address the IP source address. * @param dst_address the IP destination address. * @param ip_protocol the IP protocol number. * @param ip_ttl the IP TTL (hop-limit). If it has a negative value, then * the received value is unknown. * @param ip_tos the Type of Service (Diffserv/ECN bits for IPv4). If it * has a negative value, then the received value is unknown. * @param ip_router_alert if true, the IP Router Alert option was included * in the IP packet. * @param ip_internet_control if true, then this is IP control traffic. * @param payload the payload, everything after the IP header and options. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int proto_recv(const string& if_name, const string& vif_name, const IPvX& src_address, const IPvX& dst_address, uint8_t ip_protocol, int32_t ip_ttl, int32_t ip_tos, bool ip_router_alert, bool ip_internet_control, const vector& payload, string& error_msg); /** * Send a protocol packet. * * @param if_name the interface to send the packet on. It is essential for * multicast. In the unicast case this field may be empty. * @param vif_name the vif to send the packet on. It is essential for * multicast. In the unicast case this field may be empty. * @param src_address the IP source address. * @param dst_address the IP destination address. * @param ip_protocol the IP protocol number. It must be between 1 and * 255. * @param ip_ttl the IP TTL (hop-limit). If it has a negative value, the * TTL will be set internally before transmission. * @param ip_tos the Type Of Service (Diffserv/ECN bits for IPv4). If it * has a negative value, the TOS will be set internally before * transmission. * @param ip_router_alert if true, then add the IP Router Alert option to * the IP packet. * @param ip_internet_control if true, then this is IP control traffic. * @param buffer the data buffer with the packet to send. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int mld6igmp_send(const string& if_name, const string& vif_name, const IPvX& src_address, const IPvX& dst_address, uint8_t ip_protocol, int32_t ip_ttl, int32_t ip_tos, bool ip_router_alert, bool ip_internet_control, buffer_t *buffer, string& error_msg); /** * Receive signal message: not used by MLD/IGMP. */ int signal_message_recv(const string& , // src_module_instance_name, int , // message_type, uint32_t , // vif_index, const IPvX& , // src, const IPvX& , // dst, const uint8_t * , // rcvbuf, size_t // rcvlen ) { XLOG_UNREACHABLE(); return (XORP_ERROR); } /** * Send signal message: not used by MLD/IGMP. */ int signal_message_send(const string& , // dst_module_instance_name, int , // message_type, uint32_t , // vif_index, const IPvX& , // src, const IPvX& , // dst, const uint8_t * , // sndbuf, size_t // sndlen ) { XLOG_UNREACHABLE(); return (XORP_ERROR); } /** * Register as a receiver to receive packets. * * This is a pure virtual function, and it must be implemented * by the communication-wrapper class that inherits this base class. * * @param if_name the interface through which packets should be accepted. * @param vif_name the vif through which packets should be accepted. * @param ip_protocol the IP protocol number that the receiver is * interested in. It must be between 0 and 255. A protocol number of 0 is * used to specify all protocols. * @param enable_multicast_loopback if true then enable delivering * of multicast datagrams back to this host (assuming the host is * a member of the same multicast group). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int register_receiver(const string& if_name, const string& vif_name, uint8_t ip_protocol, bool enable_multicast_loopback) = 0; /** * Unregister as a receiver to receive packets. * * This is a pure virtual function, and it must be implemented * by the communication-wrapper class that inherits this base class. * * @param if_name the interface through which packets should not be * accepted. * @param vif_name the vif through which packets should not be accepted. * @param ip_protocol the IP Protocol number that the receiver is * not interested in anymore. It must be between 0 and 255. A protocol * number of 0 is used to specify all protocols. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int unregister_receiver(const string& if_name, const string& vif_name, uint8_t ip_protocol) = 0; /** * Join a multicast group on an interface. * * This is a pure virtual function, and it must be implemented * by the communication-wrapper class that inherits this base class. * * TODO: add a source address as well!! * * @param if_name the interface name to join. * @param vif_name the vif name to join. * @param ip_protocol the IP protocol number that the receiver is * interested in. * @param group_address the multicast group address to join. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int join_multicast_group(const string& if_name, const string& vif_name, uint8_t ip_protocol, const IPvX& group_address) = 0; /** * Leave a multicast group on an interface. * * This is a pure virtual function, and it must be implemented * by the communication-wrapper class that inherits this base class. * * TODO: add a source address as well!! * * @param if_name the interface name to leave. * @param vif_name the vif name to leave. * @param ip_protocol the IP protocol number that the receiver is * not interested in anymore. * @param group_address the multicast group address to leave. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int leave_multicast_group(const string& if_name, const string& vif_name, uint8_t ip_protocol, const IPvX& group_address) = 0; /** * Add a protocol that needs to be notified about multicast membership * changes. * * Add a protocol to the list of entries that would be notified * if there is membership change on a particular interface. * * @param module_instance_name the module instance name of the * protocol to add. * @param module_id the module ID (@ref xorp_module_id) of the * protocol to add. * @param vif_index the vif index of the interface to add the protocol to. * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_protocol(const string& module_instance_name, xorp_module_id module_id, uint32_t vif_index); /** * Delete a protocol that needs to be notified about multicast membership * changes. * * Delete a protocol from the list of entries that would be notified * if there is membership change on a particular interface. * * @param module_instance_name the module instance name of the * protocol to delete. * @param module_id the module ID (@ref xorp_module_id) of the * protocol to delete. * @param vif_index the vif index of the interface to delete the * protocol from. * @return XORP_OK on success, otherwise XORP_ERROR. */ int delete_protocol(const string& module_instance_name, xorp_module_id module_id, uint32_t vif_index, string& error_msg); /** * Send "add membership" to a protocol that needs to be notified * about multicast membership changes. * * This is a pure virtual function, and it must be implemented * by the communication-wrapper class that inherits this base class. * * @param dst_module_instance_name the module instance name of the * protocol to notify. * @param dst_module_id the module ID (@ref xorp_module_id) of the * protocol to notify. * @param vif_index the vif index of the interface with membership change. * @param source the source address of the (S,G) or (*,G) entry that has * changed membership. In case of Any-Source Multicast, it is IPvX::ZERO(). * @param group the group address. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int send_add_membership(const string& dst_module_instance_name, xorp_module_id dst_module_id, uint32_t vif_index, const IPvX& source, const IPvX& group) = 0; /** * Send "delete membership" to a protocol that needs to be notified * about multicast membership changes. * * This is a pure virtual function, and it must be implemented * by the communication-wrapper class that inherits this base class. * * @param dst_module_instance_name the module instance name of the * protocol to notify. * @param dst_module_id the module ID (@ref xorp_module_id) of the * protocol to notify. * @param vif_index the vif index of the interface with membership change. * @param source the source address of the (S,G) or (*,G) entry that has * changed membership. In case of Any-Source Multicast, it is IPvX::ZERO(). * @param group the group address of the (S,G) or (*,G) entry that has * changed. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int send_delete_membership(const string& dst_module_instance_name, xorp_module_id dst_module_id, uint32_t vif_index, const IPvX& source, const IPvX& group) = 0; /** * Notify a protocol about multicast membership change. * * @param module_instance_name the module instance name of the * protocol to notify. * @param module_id the module ID (@ref xorp_module_id) of the * protocol to notify. * @param vif_index the vif index of the interface with membership change. * @param source the source address of the (S,G) or (*,G) entry that has * changed. In case of group-specific multicast, it is IPvX::ZERO(). * @param group the group address of the (S,G) or (*,G) entry that has * changed. * @param action_jp the membership change type (@ref action_jp_t): * either ACTION_JOIN or ACTION_PRUNE. * @return XORP_OK on success, otherwise XORP_ERROR. */ int join_prune_notify_routing(const string& module_instance_name, xorp_module_id module_id, uint32_t vif_index, const IPvX& source, const IPvX& group, action_jp_t action_jp); /** * Test if an address is directly connected to a specified virtual * interface. * * Note that the virtual interface the address is directly connected to * must be UP. * * @param mld6igmp_vif the virtual interface to test against. * @param ipaddr_test the address to test. * @return true if @ref ipaddr_test is directly connected to @ref vif, * otherwise false. */ bool is_directly_connected(const Mld6igmpVif& mld6igmp_vif, const IPvX& ipaddr_test) const; // // Configuration methods // /** * Complete the set of vif configuration changes. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_config_all_vifs_done(string& error_msg); /** * Get the protocol version on an interface. * * @param vif_name the name of the vif to get the protocol version of. * @param proto_version the return-by-reference protocol version. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int get_vif_proto_version(const string& vif_name, int& proto_version, string& error_msg); /** * Set the protocol version on an interface. * * @param vif_name the name of the vif to set the protocol version of. * @param proto_version the new protocol version. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_vif_proto_version(const string& vif_name, int proto_version, string& error_msg); /** * Reset the protocol version on an interface to its default value. * * @param vif_name the name of the vif to reset the protocol version of * to its default value. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int reset_vif_proto_version(const string& vif_name, string& error_msg); /** * Get the value of the flag that enables/disables the IP Router Alert * option check per interface for received packets. * * @param vif_name the name of the vif to apply to. * @param enabled the return-by-reference flag value. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int get_vif_ip_router_alert_option_check(const string& vif_name, bool& enabled, string& error_msg); /** * Enable/disable the IP Router Alert option check per interface for * received packets. * * @param vif_name the name of the vif to apply to. * @param enable if true, then enable the IP Router Alert option check, * otherwise disable it. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_vif_ip_router_alert_option_check(const string& vif_name, bool enable, string& error_msg); /** * Reset the value of the flag that enables/disables the IP Router Alert * option check per interface for received packets to its default value. * * @param vif_name the name of the vif to apply to. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int reset_vif_ip_router_alert_option_check(const string& vif_name, string& error_msg); /** * Get the Query Interval per interface. * * @param vif_name the name of the vif to apply to. * @param interval the return-by-reference interval. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int get_vif_query_interval(const string& vif_name, TimeVal& interval, string& error_msg); /** * Set the Query Interval per interface. * * @param vif_name the name of the vif to apply to. * @param interval the interval. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_vif_query_interval(const string& vif_name, const TimeVal& interval, string& error_msg); /** * Reset the Query Interval per interface. * * @param vif_name the name of the vif to apply to. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int reset_vif_query_interval(const string& vif_name, string& error_msg); /** * Get the Last Member Query Interval per interface. * * @param vif_name the name of the vif to apply to. * @param interval the return-by-reference interval. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int get_vif_query_last_member_interval(const string& vif_name, TimeVal& interval, string& error_msg); /** * Set the Last Member Query Interval per interface. * * @param vif_name the name of the vif to apply to. * @param interval the interval. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_vif_query_last_member_interval(const string& vif_name, const TimeVal& interval, string& error_msg); /** * Reset the Last Member Query Interval per interface. * * @param vif_name the name of the vif to apply to. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int reset_vif_query_last_member_interval(const string& vif_name, string& error_msg); /** * Get the Query Response Interval per interface. * * @param vif_name the name of the vif to apply to. * @param interval the return-by-reference interval. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int get_vif_query_response_interval(const string& vif_name, TimeVal& interval, string& error_msg); /** * Set the Query Response Interval per interface. * * @param vif_name the name of the vif to apply to. * @param interval the interval. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_vif_query_response_interval(const string& vif_name, const TimeVal& interval, string& error_msg); /** * Reset the Query Response Interval per interface. * * @param vif_name the name of the vif to apply to. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int reset_vif_query_response_interval(const string& vif_name, string& error_msg); /** * Get the Robustness Variable count per interface. * * @param vif_name the name of the vif to apply to. * @param robust_count the return-by-reference count value. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int get_vif_robust_count(const string& vif_name, uint32_t& robust_count, string& error_msg); /** * Set the Robustness Variable count per interface. * * @param vif_name the name of the vif to apply to. * @param robust_count the count value. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_vif_robust_count(const string& vif_name, uint32_t robust_count, string& error_msg); /** * Reset the Robustness Variable count per interface. * * @param vif_name the name of the vif to apply to. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int reset_vif_robust_count(const string& vif_name, string& error_msg); // // Debug-related methods // /** * Test if trace log is enabled. * * This method is used to test whether to output trace log debug messges. * * @return true if trace log is enabled, otherwise false. */ bool is_log_trace() const { return (_is_log_trace); } /** * Enable/disable trace log. * * This method is used to enable/disable trace log debug messages output. * * @param is_enabled if true, trace log is enabled, otherwise is disabled. */ void set_log_trace(bool is_enabled) { _is_log_trace = is_enabled; } protected: // // IfMgrHintObserver methods // void tree_complete(); void updates_made(); private: /** * A method invoked when the status of a service changes. * * @param service the service whose status has changed. * @param old_status the old status. * @param new_status the new status. */ void status_change(ServiceBase* service, ServiceStatus old_status, ServiceStatus new_status); /** * Get a reference to the service base of the interface manager. * * This is a pure virtual function, and it must be implemented * by the communication-wrapper class that inherits this base class. * * @return a reference to the service base of the interface manager. */ virtual const ServiceBase* ifmgr_mirror_service_base() const = 0; /** * Get a reference to the interface manager tree. * * This is a pure virtual function, and it must be implemented * by the communication-wrapper class that inherits this base class. * * @return a reference to the interface manager tree. */ virtual const IfMgrIfTree& ifmgr_iftree() const = 0; /** * Initiate registration with the FEA. * * This is a pure virtual function, and it must be implemented * by the communication-wrapper class that inherits this base class. */ virtual void fea_register_startup() = 0; /** * Initiate registration with the MFEA. * * This is a pure virtual function, and it must be implemented * by the communication-wrapper class that inherits this base class. */ virtual void mfea_register_startup() = 0; /** * Initiate de-registration with the FEA. * * This is a pure virtual function, and it must be implemented * by the communication-wrapper class that inherits this base class. */ virtual void fea_register_shutdown() = 0; /** * Initiate de-registration with the MFEA. * * This is a pure virtual function, and it must be implemented * by the communication-wrapper class that inherits this base class. */ virtual void mfea_register_shutdown() = 0; buffer_t *_buffer_recv; // Buffer for receiving messages // // Status-related state // size_t _waiting_for_mfea_startup_events; // // A local copy with the interface state information // IfMgrIfTree _iftree; // // Debug and test-related state // bool _is_log_trace; // If true, enable XLOG_TRACE() }; // // Global variables // // // Global functions prototypes // #endif // __MLD6IGMP_MLD6IGMP_NODE_HH__ xorp/mld6igmp/igmp_proto.h0000664000076400007640000001722411421137511015731 0ustar greearbgreearb/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- */ /* * Copyright (c) 2001-2009 XORP, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 2, June * 1991 as published by the Free Software Foundation. Redistribution * and/or modification of this program under the terms of any other * version of the GNU General Public License is not permitted. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, * see the GNU General Public License, Version 2, a copy of which can be * found in the XORP LICENSE.gpl file. * * XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; * http://xorp.net */ /* * $XORP: xorp/mld6igmp/igmp_proto.h,v 1.20 2008/10/02 21:57:43 bms Exp $ */ #ifndef __MLD6IGMP_IGMP_PROTO_H__ #define __MLD6IGMP_IGMP_PROTO_H__ /* * Internet Group Management Protocol protocol-specific definitions: * IGMPv1 and IGMPv2 (RFC 2236), and IGMPv3 (RFC 3376). */ #ifdef HAVE_NETINET_IN_H #include #endif #ifdef HAVE_NETINET_IGMP_H #include #endif /* * Constants definitions */ #ifndef IPPROTO_IGMP #define IPPROTO_IGMP 2 #endif /* IGMP versions definition */ #define IGMP_V1 1 #define IGMP_V2 2 #define IGMP_V3 3 #define IGMP_VERSION_MIN IGMP_V1 #define IGMP_VERSION_MAX IGMP_V3 #define IGMP_VERSION_DEFAULT IGMP_V2 /* * Constants for IGMP Version 2. * All intervals are in seconds. * XXX: several of these, especially the robustness variable, should be * variables and not constants. * XXX: some of the definitions are copied from mrouted code. */ #define IGMP_ROBUSTNESS_VARIABLE 2 #define IGMP_QUERY_INTERVAL 125 #define IGMP_QUERY_RESPONSE_INTERVAL 10 #define IGMP_GROUP_MEMBERSHIP_INTERVAL (IGMP_ROBUSTNESS_VARIABLE \ * IGMP_QUERY_INTERVAL \ + IGMP_QUERY_RESPONSE_INTERVAL) #define IGMP_OTHER_QUERIER_PRESENT_INTERVAL (IGMP_ROBUSTNESS_VARIABLE \ * IGMP_QUERY_INTERVAL \ + IGMP_QUERY_RESPONSE_INTERVAL / 2) #define IGMP_STARTUP_QUERY_INTERVAL (IGMP_QUERY_INTERVAL / 4) #define IGMP_STARTUP_QUERY_COUNT IGMP_ROBUSTNESS_VARIABLE #define IGMP_LAST_MEMBER_QUERY_INTERVAL 1 #define IGMP_LAST_MEMBER_QUERY_COUNT IGMP_ROBUSTNESS_VARIABLE #define IGMP_VERSION1_ROUTER_PRESENT_TIMEOUT 400 #define IGMP_OLDER_VERSION_HOST_PRESENT_INTERVAL (IGMP_ROBUSTNESS_VARIABLE \ * IGMP_QUERY_INTERVAL \ + IGMP_QUERY_RESPONSE_INTERVAL) #ifndef IGMP_TIMER_SCALE /* the igmp code field is in 10th of seconds */ #define IGMP_TIMER_SCALE 10 #endif /* * DVMRP message types from mrouted/mrinfo * (carried in the "code" field of an IGMP header) */ /* TODO: remove the things we don't need */ #define DVMRP_PROBE 1 /* for finding neighbors */ #define DVMRP_REPORT 2 /* for reporting some or all routes */ #define DVMRP_ASK_NEIGHBORS 3 /* sent by mapper, asking for a list */ /* of this router's neighbors. */ #define DVMRP_NEIGHBORS 4 /* response to such a request */ #define DVMRP_ASK_NEIGHBORS2 5 /* as above, want new format reply */ #define DVMRP_NEIGHBORS2 6 #define DVMRP_PRUNE 7 /* prune message */ #define DVMRP_GRAFT 8 /* graft message */ #define DVMRP_GRAFT_ACK 9 /* graft acknowledgement */ #define DVMRP_INFO_REQUEST 10 /* information request */ #define DVMRP_INFO_REPLY 11 /* information reply */ /* * 'flags' byte values in DVMRP_NEIGHBORS2 reply. */ #define DVMRP_NF_TUNNEL 0x01 /* neighbors reached via tunnel */ #define DVMRP_NF_SRCRT 0x02 /* tunnel uses IP source routing */ #define DVMRP_NF_PIM 0x04 /* neighbor is a PIM neighbor */ #define DVMRP_NF_DOWN 0x10 /* kernel state of interface */ #define DVMRP_NF_DISABLED 0x20 /* administratively disabled */ #define DVMRP_NF_QUERIER 0x40 /* I am the subnet's querier */ #define DVMRP_NF_LEAF 0x80 /* Neighbor reports that it is a leaf*/ /* * Request/reply types for info queries/replies */ #define DVMRP_INFO_VERSION 1 /* version string */ #define DVMRP_INFO_NEIGHBORS 2 /* neighbors2 data */ /* * IGMPv1,v2-related missing definitions */ #ifndef IGMP_MEMBERSHIP_QUERY # ifdef IGMP_HOST_MEMBERSHIP_QUERY # define IGMP_MEMBERSHIP_QUERY IGMP_HOST_MEMBERSHIP_QUERY # else # define IGMP_MEMBERSHIP_QUERY 0x11 # endif #endif #ifndef IGMP_V1_MEMBERSHIP_REPORT # ifdef IGMP_v1_HOST_MEMBERSHIP_REPORT # define IGMP_V1_MEMBERSHIP_REPORT IGMP_v1_HOST_MEMBERSHIP_REPORT # else # define IGMP_V1_MEMBERSHIP_REPORT 0x12 # endif #endif #ifndef IGMP_V2_MEMBERSHIP_REPORT # ifdef IGMP_v2_HOST_MEMBERSHIP_REPORT # define IGMP_V2_MEMBERSHIP_REPORT IGMP_v2_HOST_MEMBERSHIP_REPORT # else # define IGMP_V2_MEMBERSHIP_REPORT 0x16 # endif #endif #ifndef IGMP_V2_LEAVE_GROUP # ifdef IGMP_HOST_LEAVE_MESSAGE # define IGMP_V2_LEAVE_GROUP IGMP_HOST_LEAVE_MESSAGE # else # define IGMP_V2_LEAVE_GROUP 0x17 # endif #endif #ifndef IGMP_DVMRP # define IGMP_DVMRP 0x13 #endif #ifndef IGMP_PIM # define IGMP_PIM 0x14 #endif #ifndef IGMP_MTRACE_RESP # ifdef IGMP_MTRACE_REPLY # define IGMP_MTRACE_RESP IGMP_MTRACE_REPLY # else # define IGMP_MTRACE_RESP 0x1e # endif #endif #ifndef IGMP_MTRACE # ifdef IGMP_MTRACE_QUERY # define IGMP_MTRACE IGMP_MTRACE_QUERY # else # define IGMP_MTRACE 0x1f # endif #endif #ifndef IGMP_MINLEN # define IGMP_MINLEN 8 #endif /* * IGMPv3-related missing definitions */ #ifndef IGMP_V3_MEMBERSHIP_REPORT # ifdef IGMP_v3_HOST_MEMBERSHIP_REPORT # define IGMP_V3_MEMBERSHIP_REPORT IGMP_v3_HOST_MEMBERSHIP_REPORT # else # define IGMP_V3_MEMBERSHIP_REPORT 0x22 # endif #endif #ifndef IGMP_MODE_IS_INCLUDE # define IGMP_MODE_IS_INCLUDE 1 #endif #ifndef IGMP_MODE_IS_EXCLUDE # define IGMP_MODE_IS_EXCLUDE 2 #endif #ifndef IGMP_CHANGE_TO_INCLUDE_MODE # define IGMP_CHANGE_TO_INCLUDE_MODE 3 #endif #ifndef IGMP_CHANGE_TO_EXCLUDE_MODE # define IGMP_CHANGE_TO_EXCLUDE_MODE 4 #endif #ifndef IGMP_ALLOW_NEW_SOURCES # define IGMP_ALLOW_NEW_SOURCES 5 #endif #ifndef IGMP_BLOCK_OLD_SOURCES # define IGMP_BLOCK_OLD_SOURCES 6 #endif #ifndef IGMP_V3_QUERY_MINLEN # define IGMP_V3_QUERY_MINLEN 12 #endif #ifndef IGMP_EXP # define IGMP_EXP(x) (((x) >> 4) & 0x07) #endif #ifndef IGMP_MANT # define IGMP_MANT(x) ((x) & 0x0f) #endif #ifndef IGMP_QRESV # define GMP_QRESV(x) (((x) >> 4) & 0x0f) #endif #ifndef IGMP_SFLAG # define IGMP_SFLAG(x) (((x) >> 3) & 0x01) #endif #ifndef IGMP_QRV # define IGMP_QRV(x) ((x) & 0x07) #endif /* * Structures, typedefs and macros */ /* * The ASCII names of the IGMP protocol control messages */ #ifdef IGMP_V3_MEMBERSHIP_REPORT #define IGMPV3TYPE2ASCII(t) \ (((t) == IGMP_V3_MEMBERSHIP_REPORT) ? \ "IGMP_V3_MEMBERSHIP_REPORT" \ : "IGMP_type_unknown") #else #define IGMPV3TYPE2ASCII(t) "IGMP_type_unknown" #endif #define IGMPTYPE2ASCII(t) \ (((t) == IGMP_MEMBERSHIP_QUERY) ? \ "IGMP_MEMBERSHIP_QUERY" \ : ((t) == IGMP_V1_MEMBERSHIP_REPORT) ? \ "IGMP_V1_MEMBERSHIP_REPORT" \ : ((t) == IGMP_V2_MEMBERSHIP_REPORT) ? \ "IGMP_V2_MEMBERSHIP_REPORT" \ : ((t) == IGMP_V2_LEAVE_GROUP) ? \ "IGMP_V2_LEAVE_GROUP" \ : ((t) == IGMP_DVMRP) ? \ "IGMP_DVMRP" \ : ((t) == IGMP_PIM) ? \ "IGMP_PIM" \ : ((t) == IGMP_MTRACE_RESP) ? \ "IGMP_MTRACE_RESP" \ : ((t) == IGMP_MTRACE) ? \ "IGMP_MTRACE" \ : IGMPV3TYPE2ASCII(t)) /* * Global variables */ /* * Global functions prototypes */ __BEGIN_DECLS __END_DECLS #endif /* __MLD6IGMP_IGMP_PROTO_H__ */ xorp/mld6igmp/mld6igmp_vif.hh0000664000076400007640000006163511540225530016313 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/mld6igmp/mld6igmp_vif.hh,v 1.51 2008/10/02 21:57:44 bms Exp $ #ifndef __MLD6IGMP_MLD6IGMP_VIF_HH__ #define __MLD6IGMP_MLD6IGMP_VIF_HH__ // // IGMP and MLD virtual interface definition. // #include "libxorp/config_param.hh" #include "libxorp/timer.hh" #include "libxorp/vif.hh" #include "libproto/proto_unit.hh" #include "mrt/buffer.h" #include "mrt/multicast_defs.h" #include "igmp_proto.h" #include "mld6_proto.h" #include "mld6igmp_node.hh" #include "mld6igmp_group_record.hh" // // Constants definitions // // // Structures/classes, typedefs and macros // /** * @short A class for MLD/IGMP-specific virtual interface. */ class Mld6igmpVif : public ProtoUnit, public Vif { public: /** * Constructor for a given MLD/IGMP node and a generic virtual interface. * * @param mld6igmp_node the @ref Mld6igmpNode this interface belongs to. * @param vif the generic Vif interface that contains various information. */ Mld6igmpVif(Mld6igmpNode& mld6igmp_node, const Vif& vif); /** * Destructor */ virtual ~Mld6igmpVif(); /** * Set the current protocol version. * * The protocol version must be in the interval * [IGMP_VERSION_MIN, IGMP_VERSION_MAX] * or [MLD_VERSION_MIN, MLD_VERSION_MAX] * * @param proto_version the protocol version to set. * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_proto_version(int proto_version); /** * Start MLD/IGMP on a single virtual interface. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int start(string& error_msg); /** Attempt deferred start. */ void notifyUpdated(); /** * Stop MLD/IGMP on a single virtual interface. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int stop(string& error_msg); /** * Enable MLD/IGMP on a single virtual interface. * * If an unit is not enabled, it cannot be start, or pending-start. */ void enable(); /** * Disable MLD/IGMP 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 disable(); /** * Receive a protocol message. * * @param src the source address of the message. * @param dst the destination address of the message. * @param ip_ttl the IP TTL of the message. If it has a negative value * it should be ignored. * @param ip_ttl the IP TOS of the message. If it has a negative value, * it should be ignored. * @param ip_router_alert if true, the IP Router Alert option in the IP * packet was set (when applicable). * @param ip_internet_control if true, then this is IP control traffic. * @param buffer the data buffer with the received message. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int mld6igmp_recv(const IPvX& src, const IPvX& dst, int ip_ttl, int ip_tos, bool ip_router_alert, bool ip_internet_control, buffer_t *buffer, string& error_msg); /** * Get the string with the flags about the vif status. * * TODO: temporary here. Should go to the Vif class after the Vif * class starts using the Proto class. * * @return the C++ style string with the flags about the vif status * (e.g., UP/DOWN/DISABLED, etc). */ string flags_string() const; /** * Get the MLD6IGMP node (@ref Mld6igmpNode). * * @return a reference to the MLD6IGMP node (@ref Mld6igmpNode). */ Mld6igmpNode& mld6igmp_node() const { return (_mld6igmp_node); } /** * Get my primary address on this interface. * * @return my primary address on this interface. */ const IPvX& primary_addr() const { return (_primary_addr); } /** * Set my primary address on this interface. * * @param v the value of the primary address. */ void set_primary_addr(const IPvX& v) { _primary_addr = v; } /** * Update the primary address. * * The primary address should be a link-local unicast address, and * is used for transmitting the multicast control packets on the LAN. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int update_primary_address(string& error_msg); /** * Get the MLD/IGMP querier address. * * @return the MLD/IGMP querier address. */ const IPvX& querier_addr() const { return (_querier_addr); } /** * Set the MLD6/IGMP querier address. * * @param v the value of the MLD/IGMP querier address. */ void set_querier_addr(const IPvX& v) { _querier_addr = v; } /** * Get the set with the multicast group records information * (@ref Mld6igmpGroupSet). * * @return the set with the multicast group records information * (@ref Mld6igmpGroupSet). */ Mld6igmpGroupSet& group_records() { return (_group_records); } /** * Get the const set with the multicast group records information * (@ref Mld6igmpGroupSet). * * @return the const set with the multicast group records information * (@ref Mld6igmpGroupSet). */ const Mld6igmpGroupSet& group_records() const { return (_group_records); } /** * Test if the protocol is Source-Specific Multicast (e.g., IGMPv3 * or MLDv2). * * @return true if the protocol is Source-Specific Multicast (e.g., IGMPv3 * or MLDv2). */ bool proto_is_ssm() const; /** * Get the timer to timeout the (other) MLD/IGMP querier. * * @return a reference to the timer to timeout the (other) * MLD/IGMP querier. * */ const XorpTimer& const_other_querier_timer() const { return (_other_querier_timer); } /** * Optain a reference to the "IP Router Alert option check" flag. * * @return a reference to the "IP Router Alert option check" flag. */ ConfigParam& ip_router_alert_option_check() { return (_ip_router_alert_option_check); } /** * Optain a reference to the configured Query Interval. * * @return a reference to the configured Query Interval. */ ConfigParam& configured_query_interval() { return (_configured_query_interval); } /** * Get the effective Query Interval value. * * Note that this value may be modified by reconfiguring the router, * or by the Query message from the current Querier. * * @return the value of the effective Query Interval. */ const TimeVal& effective_query_interval() const { return (_effective_query_interval); } /** * Set the effective Query Interval. * * Note that this value may be modified by reconfiguring the router, * or by the Query message from the current Querier. * * @param v the value of the effective Query Interval. */ void set_effective_query_interval(const TimeVal& v); /** * Optain a reference to the Last Member Query Interval. * * @return a reference to the Last Member Query Interval. */ ConfigParam& query_last_member_interval() { return (_query_last_member_interval); } /** * Optain a reference to the Query Response Interval. * * @return a reference to the Query Response Interval. */ ConfigParam& query_response_interval() { return (_query_response_interval); } /** * Optain a reference to the configured Robustness Variable count. * * @return a reference to the configured Robustness Variable count. */ ConfigParam& configured_robust_count() { return (_configured_robust_count); } /** * Get the effective Robustness Variable value. * * Note that this value may be modified by reconfiguring the router, * or by the Query messages from the current Querier. * * @return the value of the effective Robustness Variable. */ uint32_t effective_robustness_variable() const { return (_effective_robustness_variable); } /** * Set the effective Robustness Variable. * * Note that this value may be modified by reconfiguring the router, * or by the Query messages from the current Querier. * * @param v the value of the effective Robustness Variable. */ void set_effective_robustness_variable(uint32_t v); /** * Get the Last Member Query Count value. * * Note: According to the IGMP/MLD spec, the default value for the * Last Member Query Count is the Robustness Variable. * Hence, the Last Member Query Count itself should be configurable. * For simplicity (and for consistency with other router vendors), it * is always same as the Robustness Variable. * * @return the value of the Last Member Query Count. */ uint32_t last_member_query_count() const { return (_last_member_query_count); } /** * Obtain a reference to the Group Membership Interval. * * Note that it is not directly configurable, but may be tuned by * changing the values of the parameters it depends on. * * @return a reference to the Group Membership Interval. */ const TimeVal& group_membership_interval() const { return (_group_membership_interval); } /** * Obtain a reference to the Last Member Query Time. * * Note that it is not directly configurable, but may be tuned by * changing the values of the parameters it depends on. * * @return a reference to the Last Member Query Time. */ const TimeVal& last_member_query_time() const { return (_last_member_query_time); } /** * Obtain a reference to the Older Version Host Present Interval. * * Note that it is not directly configurable, but may be tuned by * changing the values of the parameters it depends on. * * @return a reference to the Older Version Host Present Interval. */ const TimeVal& older_version_host_present_interval() const { return (_older_version_host_present_interval); } // // Add/delete routing protocols that need to be notified for membership // changes. // /** * Add a protocol that needs to be notified about multicast membership * changes. * * Add a protocol to the list of entries that would be notified * if there is membership change on a particular interface. * * @param module_instance_name the module instance name of the * protocol to add. * @param module_id the module ID (@ref xorp_module_id) of the * protocol to add. * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_protocol(xorp_module_id module_id, const string& module_instance_name); /** * Delete a protocol that needs to be notified about multicast membership * changes. * * Delete a protocol from the list of entries that would be notified * if there is membership change on a particular interface. * * @param module_instance_name the module instance name of the * protocol to delete. * @param module_id the module ID (@ref xorp_module_id) of the * protocol to delete. * @return XORP_OK on success, otherwise XORP_ERROR. */ int delete_protocol(xorp_module_id module_id, const string& module_instance_name, string& error_msg); /** * Notify the interested parties that there is membership change among * the local members. * * @param source the source address of the (S,G) entry that has changed. * In case of group-specific membership, it could be IPvX::ZERO(). * @param group the group address of the (S,G) entry that has changed. * @param action_jp the membership change: @ref ACTION_JOIN * or @ref ACTION_PRUNE. * @return XORP_OK on success, otherwise XORP_ERROR. */ int join_prune_notify_routing(const IPvX& source, const IPvX& group, action_jp_t action_jp) const; // // Functions for sending protocol messages // /** * Send MLD or IGMP message. * * @param src the message source address. * @param dst the message destination address. * @param message_type the MLD or IGMP type of the message. * @param max_resp_code the "Maximum Response Code" or "Max Resp Code" * field in the MLD or IGMP headers respectively (in the particular * protocol resolution). * @param group_address the "Multicast Address" or "Group Address" field * in the MLD or IGMP headers respectively. * @param buffer the buffer with the rest of the message. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. **/ int mld6igmp_send(const IPvX& src, const IPvX& dst, uint8_t message_type, uint16_t max_resp_code, const IPvX& group_address, buffer_t *buffer, string& error_msg); /** * Send Group-Specific Query message. * * @param group_address the "Multicast Address" or "Group Address" field * in the MLD or IGMP headers respectively. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. **/ int mld6igmp_group_query_send(const IPvX& group_address, string& error_msg); /** * Send MLDv2 or IGMPv3 Group-and-Source-Specific Query message. * * @param group_address the "Multicast Address" or "Group Address" field * in the MLD or IGMP headers respectively. * @param sources the set of source addresses. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. **/ int mld6igmp_group_source_query_send(const IPvX& group_address, const set& sources, string& error_msg); /** * Send MLD or IGMP Query message. * * @param src the message source address. * @param dst the message destination address. * @param max_resp_time the maximum response time. * @param group_address the "Multicast Address" or "Group Address" field * in the MLD or IGMP headers respectively. * @param sources the set of source addresses (for IGMPv3 or MLDv2 only). * @param s_flag the "Suppress Router-Side Processing" bit (for IGMPv3 * or MLDv2 only; in all other cases it should be set to false). * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. **/ int mld6igmp_query_send(const IPvX& src, const IPvX& dst, const TimeVal& max_resp_time, const IPvX& group_address, const set& sources, bool s_flag, string& error_msg); /** * Test if the interface is running in IGMPv1 mode. * * @return true if the interface is running in IGMPv1 mode, * otherwise false. */ bool is_igmpv1_mode() const; /** * Test if the interface is running in IGMPv2 mode. * * @return true if the interface is running in IGMPv2 mode, * otherwise false. */ bool is_igmpv2_mode() const; /** * Test if the interface is running in IGMPv3 mode. * * @return true if the interface is running in IGMPv3 mode, * otherwise false. */ bool is_igmpv3_mode() const; /** * Test if the interface is running in MLDv1 mode. * * @return true if the interface is running in MLDv1 mode, * otherwise false. */ bool is_mldv1_mode() const; /** * Test if the interface is running in MLDv2 mode. * * @return true if the interface is running in MLDv2 mode, * otherwise false. */ bool is_mldv2_mode() const; /** * Test if a group is running in IGMPv1 mode. * * Note that if @ref group_record is NULL, then we test whether the * interface itself is running in IGMPv1 mode. * @param group_record the group record to test. * @return true if the group is running in IGMPv1 mode, * otherwise false. */ bool is_igmpv1_mode(const Mld6igmpGroupRecord* group_record) const; /** * Test if a group is running in IGMPv2 mode. * * Note that if @ref group_record is NULL, then we test whether the * interface itself is running in IGMPv2 mode. * @param group_record the group record to test. * @return true if the group is running in IGMPv2 mode, * otherwise false. */ bool is_igmpv2_mode(const Mld6igmpGroupRecord* group_record) const; /** * Test if a group is running in IGMPv3 mode. * * Note that if @ref group_record is NULL, then we test whether the * interface itself is running in IGMPv3 mode. * @param group_record the group record to test. * @return true if the group is running in IGMPv3 mode, * otherwise false. */ bool is_igmpv3_mode(const Mld6igmpGroupRecord* group_record) const; /** * Test if a group is running in MLDv1 mode. * * Note that if @ref group_record is NULL, then we test whether the * interface itself is running in MLDv1 mode. * @param group_record the group record to test. * @return true if the group is running in MLDv1 mode, * otherwise false. */ bool is_mldv1_mode(const Mld6igmpGroupRecord* group_record) const; /** * Test if a group is running in MLDv2 mode. * * Note that if @ref group_record is NULL, then we test whether the * interface itself is running in MLDv2 mode. * @param group_record the group record to test. * @return true if the group is running in MLDv2 mode, * otherwise false. */ bool is_mldv2_mode(const Mld6igmpGroupRecord* group_record) const; private: // // Private functions // /** * Return the ASCII text description of the protocol message. * * @param message_type the protocol message type. * @return the ASCII text descrpition of the protocol message. */ const char *proto_message_type2ascii(uint8_t message_type) const; /** * Reset and prepare the buffer for sending data. * * @return the prepared buffer. */ buffer_t *buffer_send_prepare(); /** * Calculate the checksum of an IPv6 "pseudo-header" as described * in RFC 2460. * * @param src the source address of the pseudo-header. * @param dst the destination address of the pseudo-header. * @param len the upper-layer packet length of the pseudo-header * (in host-order). * @param protocol the upper-layer protocol number. * @return the checksum of the IPv6 "pseudo-header". */ uint16_t calculate_ipv6_pseudo_header_checksum(const IPvX& src, const IPvX& dst, size_t len, uint8_t protocol); /** * Test whether I am the querier for this vif. * * @return true if I am the querier for this vif, otherwise false. */ bool i_am_querier() const; /** * Set the state whether I am the querier for this vif. * * @param v if true, then I am the querier for this vif. */ void set_i_am_querier(bool v); // // Callbacks for configuration and non-configurable parameters // void set_configured_query_interval_cb(TimeVal v); void set_query_last_member_interval_cb(TimeVal v); void set_query_response_interval_cb(TimeVal v); void set_configured_robust_count_cb(uint32_t v); void recalculate_effective_query_interval(); void recalculate_effective_robustness_variable(); void recalculate_last_member_query_count(); void recalculate_group_membership_interval(); void recalculate_last_member_query_time(); void recalculate_older_version_host_present_interval(); void restore_effective_variables(); // // Private state // Mld6igmpNode& _mld6igmp_node; // The MLD6IGMP node I belong to buffer_t *_buffer_send; // Buffer for sending messages enum { MLD6IGMP_VIF_QUERIER = 1 << 0 // I am the querier }; uint32_t _proto_flags; // Various flags (MLD6IGMP_VIF_*) IPvX _primary_addr; // The primary address on this vif IPvX _querier_addr; // IP address of the current querier XorpTimer _other_querier_timer; // To timeout the (other) 'querier' XorpTimer _query_timer; // Timer to send queries uint8_t _startup_query_count; // Number of queries to send quickly // during startup Mld6igmpGroupSet _group_records; // The group records // // Misc configuration parameters // ConfigParam _ip_router_alert_option_check; // The IP Router Alert option check flag ConfigParam _configured_query_interval; // The configured Query Interval TimeVal _effective_query_interval; // The effective Query Interval ConfigParam _query_last_member_interval; // The Last Member Query Interval ConfigParam _query_response_interval; // The Query Response Interval ConfigParam _configured_robust_count; // The configured Robustness Variable count uint32_t _effective_robustness_variable; // The effective Robustness Variable // // Other parameters that are not directly configurable // uint32_t _last_member_query_count; // The Last Member Query Count TimeVal _group_membership_interval; // The Group Membership Interval TimeVal _last_member_query_time; // The Last Member Query Time TimeVal _older_version_host_present_interval; // The Older Version Host Present Interval // // Misc. other state // // Registered protocols to notify for membership change. vector > _notify_routing_protocols; bool _dummy_flag; // Dummy flag // // Not-so handy private functions that should go somewhere else // // MLD/IGMP control messages recv functions int mld6igmp_membership_query_recv(const IPvX& src, const IPvX& dst, uint8_t message_type, uint16_t max_resp_code, const IPvX& group_address, buffer_t *buffer); int mld6igmp_ssm_membership_query_recv(const IPvX& src, const IPvX& dst, uint8_t message_type, uint16_t max_resp_code, const IPvX& group_address, buffer_t *buffer); int mld6igmp_membership_report_recv(const IPvX& src, const IPvX& dst, uint8_t message_type, uint16_t max_resp_code, const IPvX& group_address, buffer_t *buffer); int mld6igmp_leave_group_recv(const IPvX& src, const IPvX& dst, uint8_t message_type, uint16_t max_resp_code, const IPvX& group_address, buffer_t *buffer); int mld6igmp_ssm_membership_report_recv(const IPvX& src, const IPvX& dst, uint8_t message_type, buffer_t *buffer); int mld6igmp_query_version_consistency_check(const IPvX& src, const IPvX& dst, uint8_t message_type, int message_version); // MLD/IGMP control messages process functions int mld6igmp_process(const IPvX& src, const IPvX& dst, int ip_ttl, int ip_tos, bool ip_router_alert, bool ip_internet_control, buffer_t *buffer, string& error_msg); // MLD/IGMP uniform interface for protocol-related constants size_t mld6igmp_constant_minlen() const; uint32_t mld6igmp_constant_timer_scale() const; uint8_t mld6igmp_constant_membership_query() const; void other_querier_timer_timeout(); void query_timer_timeout(); void decode_exp_time_code8(uint8_t code, TimeVal& timeval, uint32_t timer_scale); void decode_exp_time_code16(uint16_t code, TimeVal& timeval, uint32_t timer_scale); void encode_exp_time_code8(const TimeVal& timeval, uint8_t& code, uint32_t timer_scale); void encode_exp_time_code16(const TimeVal& timeval, uint16_t& code, uint32_t timer_scale); bool wants_to_be_started; // as soon as we can, ie if the interface appears. }; // // Global variables // // // Global functions prototypes // #endif // __MLD6IGMP_MLD6IGMP_VIF_HH__ xorp/mld6igmp/mld6igmp_vif.cc0000664000076400007640000015065011703345405016302 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2012 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // MLD6IGMP virtual interfaces implementation. // #include "mld6igmp_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvx.hh" #include "libproto/checksum.h" #include "mld6igmp_vif.hh" /** * Mld6igmpVif::Mld6igmpVif: * @mld6igmp_node: The MLD6IGMP node this interface belongs to. * @vif: The generic Vif interface that contains various information. * * MLD6IGMP protocol vif constructor. **/ Mld6igmpVif::Mld6igmpVif(Mld6igmpNode& mld6igmp_node, const Vif& vif) : ProtoUnit(mld6igmp_node.family(), mld6igmp_node.module_id()), Vif(vif), _mld6igmp_node(mld6igmp_node), _proto_flags(0), _primary_addr(IPvX::ZERO(mld6igmp_node.family())), _querier_addr(IPvX::ZERO(mld6igmp_node.family())), _startup_query_count(0), _group_records(*this), _ip_router_alert_option_check(false), _configured_query_interval( TimeVal(0, 0), callback(this, &Mld6igmpVif::set_configured_query_interval_cb)), _effective_query_interval(TimeVal(0, 0)), _query_last_member_interval( TimeVal(0, 0), callback(this, &Mld6igmpVif::set_query_last_member_interval_cb)), _query_response_interval( TimeVal(0, 0), callback(this, &Mld6igmpVif::set_query_response_interval_cb)), _configured_robust_count( 0, callback(this, &Mld6igmpVif::set_configured_robust_count_cb)), _effective_robustness_variable(0), _last_member_query_count(0), _group_membership_interval(TimeVal(0, 0)), _last_member_query_time(TimeVal(0, 0)), _older_version_host_present_interval(TimeVal(0, 0)), _dummy_flag(false) { XLOG_ASSERT(proto_is_igmp() || proto_is_mld6()); wants_to_be_started = false; // // TODO: when more things become classes, most of this init should go away // _buffer_send = BUFFER_MALLOC(BUF_SIZE_DEFAULT); // // Set the protocol version // if (proto_is_igmp()) { set_proto_version_default(IGMP_VERSION_DEFAULT); _configured_query_interval.set(TimeVal(IGMP_QUERY_INTERVAL, 0)); _query_last_member_interval.set( TimeVal(IGMP_LAST_MEMBER_QUERY_INTERVAL, 0)); _query_response_interval.set(TimeVal(IGMP_QUERY_RESPONSE_INTERVAL, 0)); _configured_robust_count.set(IGMP_ROBUSTNESS_VARIABLE); } if (proto_is_mld6()) { set_proto_version_default(MLD_VERSION_DEFAULT); _configured_query_interval.set(TimeVal(MLD_QUERY_INTERVAL, 0)); _query_last_member_interval.set( TimeVal(MLD_LAST_LISTENER_QUERY_INTERVAL, 0)); _query_response_interval.set(TimeVal(MLD_QUERY_RESPONSE_INTERVAL, 0)); _configured_robust_count.set(MLD_ROBUSTNESS_VARIABLE); } set_proto_version(proto_version_default()); } /** * Mld6igmpVif::~Mld6igmpVif: * @: * * MLD6IGMP protocol vif destructor. * **/ Mld6igmpVif::~Mld6igmpVif() { string error_msg; stop(error_msg); _group_records.delete_payload_and_clear(); BUFFER_FREE(_buffer_send); } /** * Mld6igmpVif::set_proto_version: * @proto_version: The protocol version to set. * * Set protocol version. * * Return value: %XORP_OK is @proto_version is valid, otherwise %XORP_ERROR. **/ int Mld6igmpVif::set_proto_version(int proto_version) { if (proto_is_igmp()) { if ((proto_version < IGMP_VERSION_MIN) || (proto_version > IGMP_VERSION_MAX)) { return (XORP_ERROR); } if (proto_version < IGMP_V3) { // // XXX: Restore the variables that might have been adopted from // the Querier. // restore_effective_variables(); } } if (proto_is_mld6()) { if ((proto_version < MLD_VERSION_MIN) || (proto_version > MLD_VERSION_MAX)) { return (XORP_ERROR); } if (proto_version < IGMP_V3) { // // XXX: Restore the variables that might have been adopted from // the Querier. // restore_effective_variables(); } } ProtoUnit::set_proto_version(proto_version); return (XORP_OK); } /** * Mld6igmpVif::proto_is_ssm: * @: * * Test if the interface is running a source-specific multicast capable * protocol version (e.g. IGMPv3 or MLDv2). * * Return value: @true if the protocol version is source-specific multicast * capable, otherwise @fa.se **/ bool Mld6igmpVif::proto_is_ssm() const { if (proto_is_igmp()) return (proto_version() >= IGMP_V3); if (proto_is_mld6()) return (proto_version() >= MLD_V2); return (false); } /** System detected some change. */ void Mld6igmpVif::notifyUpdated() { if (wants_to_be_started) { string err_msg; int rv = start(err_msg); if (rv == XORP_OK) { XLOG_WARNING("notifyUpdated, successfully started mld6igmp_vif: %s", name().c_str()); } else { XLOG_WARNING("notifyUpdated, tried to start vif: %s, but failed: %s", name().c_str(), err_msg.c_str()); } } } /** * Mld6igmpVif::start: * @error_msg: The error message (if error). * * Start MLD or IGMP on a single virtual interface. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int Mld6igmpVif::start(string& error_msg) { string dummy_error_msg; if (! is_enabled()) return (XORP_OK); if (is_up() || is_pending_up()) return (XORP_OK); if (! is_underlying_vif_up()) { wants_to_be_started = true; XLOG_WARNING("WARNING: Delaying start of mld6igmp-vif: %s because underlying vif is not up.", name().c_str()); return XORP_OK; } if (! (is_pim_register() || is_multicast_capable())) { wants_to_be_started = true; XLOG_WARNING("WARNING: Delaying start of mld6igmp-vif: %s because underlying vif is not multicast capable.", name().c_str()); return XORP_OK; } // // Start the vif only if it is of the appropriate type: // multicast-capable (loopback excluded). // if (is_loopback()) { error_msg = "mld6igmp: Loopback interfaces cannot be used for multicast."; return (XORP_ERROR); } if (update_primary_address(error_msg) != XORP_OK) { // TODO: Set wants-to-start and try again later // when IP is found?? return (XORP_ERROR); } if (ProtoUnit::start() != XORP_OK) { error_msg = "internal error"; return (XORP_ERROR); } // On startup, assume I am the MLD6IGMP Querier set_querier_addr(primary_addr()); set_i_am_querier(true); // // Register as a receiver with the kernel // if (mld6igmp_node().register_receiver(name(), name(), mld6igmp_node().ip_protocol_number(), true) != XORP_OK) { error_msg = c_format("cannot register as a receiver on vif %s " "with the kernel", name().c_str()); return (XORP_ERROR); } // // Join the appropriate multicast groups: ALL-SYSTEMS, ALL-ROUTERS, // and SSM-ROUTERS. // list groups; list::iterator groups_iter; groups.push_back(IPvX::MULTICAST_ALL_SYSTEMS(family())); groups.push_back(IPvX::MULTICAST_ALL_ROUTERS(family())); groups.push_back(IPvX::SSM_ROUTERS(family())); for (groups_iter = groups.begin(); groups_iter != groups.end(); ++groups_iter) { const IPvX& group = *groups_iter; if (mld6igmp_node().join_multicast_group(name(), name(), mld6igmp_node().ip_protocol_number(), group) != XORP_OK) { error_msg = c_format("cannot join group %s on vif %s", cstring(group), name().c_str()); return (XORP_ERROR); } } // // Query all members on startup // TimeVal max_resp_time = query_response_interval().get(); set no_sources; // XXX: empty set mld6igmp_query_send(primary_addr(), IPvX::MULTICAST_ALL_SYSTEMS(family()), max_resp_time, IPvX::ZERO(family()), no_sources, false, dummy_error_msg); _startup_query_count = effective_robustness_variable(); if (_startup_query_count > 0) _startup_query_count--; TimeVal startup_query_interval = effective_query_interval() / 4; _query_timer = mld6igmp_node().eventloop().new_oneoff_after( startup_query_interval, callback(this, &Mld6igmpVif::query_timer_timeout)); XLOG_INFO("Interface started: %s%s", this->str().c_str(), flags_string().c_str()); wants_to_be_started = false; //it worked return (XORP_OK); } /** * Mld6igmpVif::stop: * @error_msg: The error message (if error). * * Stop MLD or IGMP on a single virtual interface. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int Mld6igmpVif::stop(string& error_msg) { int ret_value = XORP_OK; wants_to_be_started = false; 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() != XORP_OK) { error_msg = "internal error"; ret_value = XORP_ERROR; } // // XXX: we don't have to explicitly leave the multicast groups // we have joined on that interface, because this will happen // automatically when we stop the vif through the MFEA. // if (ProtoUnit::stop() != XORP_OK) { error_msg = "internal error"; ret_value = XORP_ERROR; } set_i_am_querier(false); set_querier_addr(IPvX::ZERO(family())); // XXX: ANY _other_querier_timer.unschedule(); _query_timer.unschedule(); _startup_query_count = 0; // Notify routing and remove all group records Mld6igmpGroupSet::const_iterator group_iter; for (group_iter = _group_records.begin(); group_iter != _group_records.end(); ++group_iter) { const Mld6igmpGroupRecord *group_record = group_iter->second; Mld6igmpSourceSet::const_iterator source_iter; // Clear the state for all included sources for (source_iter = group_record->do_forward_sources().begin(); source_iter != group_record->do_forward_sources().end(); ++source_iter) { const Mld6igmpSourceRecord *source_record = source_iter->second; join_prune_notify_routing(source_record->source(), group_record->group(), ACTION_PRUNE); } // Clear the state for all excluded sources for (source_iter = group_record->dont_forward_sources().begin(); source_iter != group_record->dont_forward_sources().end(); ++source_iter) { const Mld6igmpSourceRecord *source_record = source_iter->second; join_prune_notify_routing(source_record->source(), group_record->group(), ACTION_JOIN); } if (group_record->is_exclude_mode()) { join_prune_notify_routing(IPvX::ZERO(family()), group_record->group(), ACTION_PRUNE); } } _group_records.delete_payload_and_clear(); // // Unregister as a receiver with the kernel // if (mld6igmp_node().unregister_receiver(name(), name(), mld6igmp_node().ip_protocol_number()) != XORP_OK) { XLOG_ERROR("Cannot unregister as a receiver on 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 // mld6igmp_node().vif_shutdown_completed(name()); return (ret_value); } /** * Enable MLD/IGMP on a single virtual interface. * * If an unit is not enabled, it cannot be start, or pending-start. */ void Mld6igmpVif::enable() { ProtoUnit::enable(); XLOG_INFO("Interface enabled: %s%s", this->str().c_str(), flags_string().c_str()); } /** * Disable MLD/IGMP 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 Mld6igmpVif::disable() { string error_msg; stop(error_msg); ProtoUnit::disable(); XLOG_INFO("Interface disabled: %s%s", this->str().c_str(), flags_string().c_str()); } /** * Mld6igmpVif::mld6igmp_send: * @src: The message source address. * @dst: The message destination address. * @message_type: The MLD or IGMP type of the message. * @max_resp_code: The "Maximum Response Code" or "Max Resp Code" * field in the MLD or IGMP headers respectively (in the particular * protocol resolution). * @group_address: The "Multicast Address" or "Group Address" field * in the MLD or IGMP headers respectively. * @buffer: The buffer with the rest of the message. * @error_msg: The error message (if error). * * Send MLD or IGMP message. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int Mld6igmpVif::mld6igmp_send(const IPvX& src, const IPvX& dst, uint8_t message_type, uint16_t max_resp_code, const IPvX& group_address, buffer_t *buffer, string& error_msg) { uint16_t cksum; int ret_value; size_t datalen; bool ip_router_alert = true; // XXX: always use Router Alert option bool ip_internet_control = true; // XXX: always true if (! (is_up() || is_pending_down())) { error_msg = c_format("vif %s is not UP", name().c_str()); return (XORP_ERROR); } XLOG_ASSERT(src != IPvX::ZERO(family())); // // Prepare the MLD or IGMP header. // // Point the buffer to the protocol header datalen = BUFFER_DATA_SIZE(buffer); if (datalen > 0) { BUFFER_RESET_TAIL(buffer); } if (proto_is_igmp()) { BUFFER_PUT_OCTET(message_type, buffer); // The message type BUFFER_PUT_OCTET(max_resp_code, buffer); BUFFER_PUT_HOST_16(0, buffer); // Zero the checksum field BUFFER_PUT_IPVX(group_address, buffer); } if (proto_is_mld6()) { BUFFER_PUT_OCTET(message_type, buffer); // The message type BUFFER_PUT_OCTET(0, buffer); // XXX: Always 0 for MLD BUFFER_PUT_HOST_16(0, buffer); // Zero the checksum field BUFFER_PUT_HOST_16(max_resp_code, buffer); BUFFER_PUT_HOST_16(0, buffer); // Reserved BUFFER_PUT_IPVX(group_address, buffer); } // Restore the buffer to include the data if (datalen > 0) { BUFFER_RESET_TAIL(buffer); BUFFER_PUT_SKIP(datalen, buffer); } // // Compute the checksum // cksum = inet_checksum(BUFFER_DATA_HEAD(buffer), BUFFER_DATA_SIZE(buffer)); #ifdef HAVE_IPV6 // Add the checksum for the IPv6 pseudo-header if (proto_is_mld6()) { uint16_t cksum2; size_t ph_len = BUFFER_DATA_SIZE(buffer); cksum2 = calculate_ipv6_pseudo_header_checksum(src, dst, ph_len, IPPROTO_ICMPV6); cksum = inet_checksum_add(cksum, cksum2); } #endif // HAVE_IPV6 BUFFER_COPYPUT_INET_CKSUM(cksum, buffer, 2); // XXX: the checksum XLOG_TRACE(mld6igmp_node().is_log_trace(), "TX %s from %s to %s", proto_message_type2ascii(message_type), cstring(src), cstring(dst)); // // Send the message // ret_value = mld6igmp_node().mld6igmp_send(name(), name(), src, dst, mld6igmp_node().ip_protocol_number(), MINTTL, -1, ip_router_alert, ip_internet_control, buffer, error_msg); return (ret_value); buflen_error: XLOG_UNREACHABLE(); error_msg = c_format("TX %s from %s to %s: " "packet cannot fit into sending buffer", proto_message_type2ascii(message_type), cstring(src), cstring(dst)); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } /** * Send Group-Specific Query message. * * @param group_address the "Multicast Address" or "Group Address" field * in the MLD or IGMP headers respectively. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. **/ int Mld6igmpVif::mld6igmp_group_query_send(const IPvX& group_address, string& error_msg) { const IPvX& src = primary_addr(); const IPvX& dst = group_address; const TimeVal& max_resp_time = query_last_member_interval().get(); Mld6igmpGroupRecord* group_record = NULL; set no_sources; // XXX: empty set int ret_value; // // Only the Querier should originate Query messages // if (! i_am_querier()) return (XORP_OK); // Find the group record group_record = _group_records.find_group_record(group_address); if (group_record == NULL) return (XORP_ERROR); // No such group // // Lower the group timer // _group_records.lower_group_timer(group_address, last_member_query_time()); // // Send the message // ret_value = mld6igmp_query_send(src, dst, max_resp_time, group_address, no_sources, false, // XXX: reset the s_flag error_msg); // // Schedule the periodic Group-Specific Query // if (ret_value == XORP_OK) group_record->schedule_periodic_group_query(no_sources); // // Print the error message if there was an error // if (ret_value != XORP_OK) { XLOG_ERROR("Error sending Group-Specific query for %s: %s", cstring(group_address), error_msg.c_str()); } return (ret_value); } /** * Send MLDv2 or IGMPv3 Group-and-Source-Specific Query message. * * @param group_address the "Multicast Address" or "Group Address" field * in the MLD or IGMP headers respectively. * @param sources the set of source addresses. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. **/ int Mld6igmpVif::mld6igmp_group_source_query_send(const IPvX& group_address, const set& sources, string& error_msg) { const IPvX& src = primary_addr(); const IPvX& dst = group_address; const TimeVal& max_resp_time = query_last_member_interval().get(); Mld6igmpGroupRecord* group_record = NULL; set selected_sources; set::const_iterator source_iter; int ret_value; // // Only the Querier should originate Query messages // if (! i_am_querier()) return (XORP_OK); if (sources.empty()) return (XORP_OK); // No sources to query // Find the group record group_record = _group_records.find_group_record(group_address); if (group_record == NULL) return (XORP_ERROR); // No such group // // Select only the sources with source timer larger than the // Last Member Query Time. // for (source_iter = sources.begin(); source_iter != sources.end(); ++source_iter) { const IPvX& ipvx = *source_iter; Mld6igmpSourceRecord* source_record; source_record = group_record->find_do_forward_source(ipvx); if (source_record == NULL) continue; TimeVal timeval_remaining; source_record->source_timer().time_remaining(timeval_remaining); if (timeval_remaining <= last_member_query_time()) continue; selected_sources.insert(ipvx); } if (selected_sources.empty()) return (XORP_OK); // No selected sources to query // // Lower the source timer // group_record->lower_source_timer(selected_sources, last_member_query_time()); // // Send the message // ret_value = mld6igmp_query_send(src, dst, max_resp_time, group_address, selected_sources, false, // XXX: reset the s_flag error_msg); // // Schedule the periodic Group-and-Source-Specific Query // if (ret_value == XORP_OK) group_record->schedule_periodic_group_query(selected_sources); // // Print the error message if there was an error // if (ret_value != XORP_OK) { XLOG_ERROR("Error sending Group-and-Source-Specific query for %s: %s", cstring(group_address), error_msg.c_str()); } return (ret_value); } /** * Send MLD or IGMP Query message. * * @param src the message source address. * @param dst the message destination address. * @param max_resp_time the maximum response time. * @param group_address the "Multicast Address" or "Group Address" field * in the MLD or IGMP headers respectively. * @param sources the set of source addresses (for IGMPv3 or MLDv2 only). * @param s_flag the "Suppress Router-Side Processing" bit (for IGMPv3 * or MLDv2 only; in all other cases it should be set to false). * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. **/ int Mld6igmpVif::mld6igmp_query_send(const IPvX& src, const IPvX& dst, const TimeVal& max_resp_time, const IPvX& group_address, const set& sources, bool s_flag, string& error_msg) { buffer_t *buffer; uint32_t timer_scale = mld6igmp_constant_timer_scale(); TimeVal scaled_max_resp_time = max_resp_time * timer_scale; set::const_iterator source_iter; uint8_t qrv, qqic; size_t max_sources_n; size_t max_payload = 0; Mld6igmpGroupRecord* group_record = NULL; // // Only the Querier should originate Query messages // if (! i_am_querier()) return (XORP_OK); // Find the group record group_record = _group_records.find_group_record(group_address); // // Check protocol version and Query message matching // do { if (sources.empty()) break; // IGMPv3/MLDv2 Query(G, A) message if (is_igmpv3_mode(group_record) || is_mldv2_mode(group_record)) break; // XXX: Query(G, A) messages are not allowed in this mode return (XORP_ERROR); } while (false); // // Lower the group and source timers (if necessary) // if (! s_flag) { if (sources.empty()) { // XXX: Q(G) Query _group_records.lower_group_timer(group_address, last_member_query_time()); } else { // XXX: Q(G, A) Query _group_records.lower_source_timer(group_address, sources, last_member_query_time()); } } // // Prepare the data after the Query header // // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | Resv |S| QRV | QQIC | Number of Sources (N) | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // qrv = 0; if (effective_robustness_variable() <= 0x7) qrv = effective_robustness_variable(); if (s_flag) qrv |= 0x8; qqic = 0; encode_exp_time_code8(effective_query_interval(), qqic, 1); // // Calculate the maximum number of sources // max_sources_n = sources.size(); if (proto_is_igmp()) { max_payload = mtu() // The MTU of the vif - (0xf << 2) // IPv4 max header size - 4 // IPv4 Router Alert option - IGMP_V3_QUERY_MINLEN; // IGMPv3 Query pre-source fields } if (proto_is_mld6()) { max_payload = mtu() // The MTU of the vif - 8 // IPv6 Hop-by-hop Ext. Header with Router Alert option - MLD_V2_QUERY_MINLEN; // MLDv2 Query pre-source fields } max_sources_n = min(max_sources_n, max_payload / IPvX::addr_bytelen(family())); // // XXX: According to RFC 3810 (MLDv2), Section 8.3.2, the Querier // continues to send MLDv2 queries, regardless of its Multicast Address // Compatibility Mode. // // Interestingly, RFC 3376 (IGMPv3) does not include this statement. // According to the following email, the need for this statement has been // discovered too late to be included in RFC 3376: // http://www1.ietf.org/mail-archive/web/ssm/current/msg00084.html // // // Prepare the buffer // buffer = buffer_send_prepare(); BUFFER_PUT_SKIP(mld6igmp_constant_minlen(), buffer); // // Insert the data (for IGMPv3 and MLDv2 only) // if (is_igmpv3_mode() || is_mldv2_mode()) { // // XXX: Note that we consider only the interface mode, but ignore // the Multicast Address Compatibility Mode. // BUFFER_PUT_OCTET(qrv, buffer); BUFFER_PUT_OCTET(qqic, buffer); BUFFER_PUT_HOST_16(max_sources_n, buffer); source_iter = sources.begin(); while (max_sources_n > 0) { const IPvX& ipvx = *source_iter; BUFFER_PUT_IPVX(ipvx, buffer); ++source_iter; max_sources_n--; } } else { // // If IGMPv1 Multicast Address Compatibility Mode, then set the // Max Response Time to zero. // if (is_igmpv1_mode(group_record)) scaled_max_resp_time = TimeVal::ZERO(); } // // Send the message // //XLOG_WARNING("%s: Sending igmp query, src: %s dst: %s\n", // name().c_str(), cstring(src), cstring(dst)); return (mld6igmp_send(src, dst, mld6igmp_constant_membership_query(), scaled_max_resp_time.sec(), group_address, buffer, error_msg)); buflen_error: XLOG_UNREACHABLE(); XLOG_ERROR("INTERNAL mld6igmp_query_send() ERROR: " "buffer size too small"); return (XORP_ERROR); } /** * Mld6igmpVif::mld6igmp_recv: * @src: The message source address. * @dst: The message destination address. * @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. * @ip_router_alert: True if the received IP packet had the Router Alert * IP option set. * @ip_internet_control: If true, then this is IP control traffic. * @buffer: The buffer with the received message. * @error_msg: The error message (if error). * * Receive MLD or IGMP message and pass it for processing. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int Mld6igmpVif::mld6igmp_recv(const IPvX& src, const IPvX& dst, int ip_ttl, int ip_tos, bool ip_router_alert, bool ip_internet_control, buffer_t *buffer, string& error_msg) { int ret_value = XORP_ERROR; if (! is_up()) { error_msg = c_format("vif %s is not UP", name().c_str()); return (XORP_ERROR); } ret_value = mld6igmp_process(src, dst, ip_ttl, ip_tos, ip_router_alert, ip_internet_control, buffer, error_msg); return (ret_value); } /** * Mld6igmpVif::mld6igmp_process: * @src: The message source address. * @dst: The message destination address. * @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. * @ip_router_alert: True if the received IP packet had the Router Alert * IP option set. * @ip_internet_control: If true, then this is IP control traffic. * @buffer: The buffer with the message. * @error_msg: The error message (if error). * * Process MLD or IGMP message and pass the control to the type-specific * functions. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int Mld6igmpVif::mld6igmp_process(const IPvX& src, const IPvX& dst, int ip_ttl, int ip_tos, bool ip_router_alert, bool ip_internet_control, buffer_t *buffer, string& error_msg) { uint8_t message_type = 0; uint16_t max_resp_code = 0; IPvX group_address(family()); uint16_t cksum; bool check_router_alert_option = false; bool check_src_linklocal_unicast = false; bool allow_src_zero_address = false; bool check_dst_multicast = false; bool check_group_interfacelocal_multicast = false; bool decode_extra_fields = false; // // Message length check. // if (BUFFER_DATA_SIZE(buffer) < mld6igmp_constant_minlen()) { error_msg = c_format("RX packet from %s to %s on vif %s: " "too short data field (%u octets)", cstring(src), cstring(dst), name().c_str(), XORP_UINT_CAST(BUFFER_DATA_SIZE(buffer))); XLOG_WARNING("%s", error_msg.c_str()); return (XORP_ERROR); } // // Checksum verification. // cksum = inet_checksum(BUFFER_DATA_HEAD(buffer), BUFFER_DATA_SIZE(buffer)); #ifdef HAVE_IPV6 // Add the checksum for the IPv6 pseudo-header if (proto_is_mld6()) { uint16_t cksum2; size_t ph_len = BUFFER_DATA_SIZE(buffer); cksum2 = calculate_ipv6_pseudo_header_checksum(src, dst, ph_len, IPPROTO_ICMPV6); cksum = inet_checksum_add(cksum, cksum2); } #endif // HAVE_IPV6 if (cksum) { error_msg = c_format("RX packet from %s to %s on vif %s: " "checksum error", cstring(src), cstring(dst), name().c_str()); XLOG_WARNING("%s", error_msg.c_str()); return (XORP_ERROR); } // // Protocol version check. // // XXX: MLD and IGMP messages do not have an explicit field for protocol // version. Protocol version check is performed later, per (some) message // type. // // // Get the message type and the max. resp. time (in case of IGMP). // // Note that in case of IGMP the max. resp. time is the `igmp_code' field // in `struct igmp'. // if (proto_is_igmp()) { BUFFER_GET_OCTET(message_type, buffer); BUFFER_GET_OCTET(max_resp_code, buffer); BUFFER_GET_SKIP(2, buffer); // The checksum } if (proto_is_mld6()) { BUFFER_GET_OCTET(message_type, buffer); BUFFER_GET_SKIP(1, buffer); // The `Code' field: unused BUFFER_GET_SKIP(2, buffer); // The `Checksum' field } XLOG_TRACE(mld6igmp_node().is_log_trace(), "mld6igmp_process: RX %s from %s to %s on vif %s", proto_message_type2ascii(message_type), cstring(src), cstring(dst), name().c_str()); // // Ignore messages that are not recognized by older protocol version. // // XXX: Unrecognized message types MUST be silently ignored. // if (proto_is_igmp()) { switch (message_type) { case IGMP_MEMBERSHIP_QUERY: // Recognized by IGMPv1, IGMPv2, IGMPv3 break; case IGMP_V1_MEMBERSHIP_REPORT: // Recognized by IGMPv1, IGMPv2, IGMPv3 break; case IGMP_V2_MEMBERSHIP_REPORT: // Recognized by IGMPv2, IGMPv3 if (is_igmpv1_mode()) return (XORP_ERROR); break; case IGMP_V2_LEAVE_GROUP: // Recognized by IGMPv2, IGMPv3 if (is_igmpv1_mode()) return (XORP_ERROR); break; case IGMP_V3_MEMBERSHIP_REPORT: // Recognized by IGMPv3 if (is_igmpv1_mode() || is_igmpv2_mode()) return (XORP_ERROR); break; case IGMP_DVMRP: case IGMP_MTRACE: break; default: // Unrecognized message return (XORP_ERROR); } } if (proto_is_mld6()) { switch (message_type) { case MLD_LISTENER_QUERY: // Recognized by MLDv1, MLDv2 break; case MLD_LISTENER_REPORT: // Recognized by MLDv1, MLDv2 break; case MLD_LISTENER_DONE: // Recognized by MLDv1, MLDv2 break; case MLDV2_LISTENER_REPORT: // Recognized by MLDv2 if (is_mldv1_mode()) return (XORP_ERROR); break; case MLD_MTRACE: break; default: // Unrecognized message return (XORP_ERROR); } } // // Assign various flags what needs to be checked, based on the // message type: // - check_router_alert_option // - check_src_linklocal_unicast // - allow_src_zero_address // - check_dst_multicast // - check_group_interfacelocal_multicast // - decode_extra_fields // if (proto_is_igmp()) { switch (message_type) { case IGMP_MEMBERSHIP_QUERY: case IGMP_V1_MEMBERSHIP_REPORT: case IGMP_V2_MEMBERSHIP_REPORT: case IGMP_V2_LEAVE_GROUP: case IGMP_V3_MEMBERSHIP_REPORT: if (_ip_router_alert_option_check.get()) check_router_alert_option = true; check_src_linklocal_unicast = false; // Not needed for IPv4 if (is_igmpv3_mode()) { if ((message_type == IGMP_V1_MEMBERSHIP_REPORT) || (message_type == IGMP_V2_MEMBERSHIP_REPORT) || (message_type == IGMP_V3_MEMBERSHIP_REPORT)) { allow_src_zero_address = true; // True only for IGMPv3 } } check_dst_multicast = true; if (is_igmpv3_mode()) check_dst_multicast = false; // XXX: disable check_group_interfacelocal_multicast = false;// Not needed for IPv4 decode_extra_fields = true; if (message_type == IGMP_V3_MEMBERSHIP_REPORT) decode_extra_fields = false; break; case IGMP_DVMRP: case IGMP_MTRACE: // TODO: Assign the flags as appropriate break; default: break; } } if (proto_is_mld6()) { switch (message_type) { case MLD_LISTENER_QUERY: case MLD_LISTENER_REPORT: case MLD_LISTENER_DONE: case MLDV2_LISTENER_REPORT: check_router_alert_option = true; check_src_linklocal_unicast = true; allow_src_zero_address = false; // Always false for MLD check_dst_multicast = true; if (is_mldv2_mode()) check_dst_multicast = false; // XXX: disable check_group_interfacelocal_multicast = true; decode_extra_fields = true; if (message_type == MLDV2_LISTENER_REPORT) decode_extra_fields = false; break; case MLD_MTRACE: // TODO: Assign the flags as appropriate break; default: break; } } // // Decode the extra fields: the max. resp. time (in case of MLD), // and the group address. // if (decode_extra_fields) { if (proto_is_igmp()) { BUFFER_GET_IPVX(family(), group_address, buffer); } if (proto_is_mld6()) { BUFFER_GET_HOST_16(max_resp_code, buffer); BUFFER_GET_SKIP(2, buffer); // The `Reserved' field BUFFER_GET_IPVX(family(), group_address, buffer); } } // // IP Router Alert option check. // if (check_router_alert_option && (! ip_router_alert)) { error_msg = c_format("RX %s from %s to %s on vif %s: " "missing IP Router Alert option", proto_message_type2ascii(message_type), cstring(src), cstring(dst), name().c_str()); XLOG_WARNING("%s", error_msg.c_str()); return (XORP_ERROR); } // // TODO: check the TTL, TOS and ip_internet_control flag if we are // running in secure mode. // UNUSED(ip_ttl); UNUSED(ip_tos); UNUSED(ip_internet_control); #if 0 if (ip_ttl != MINTTL) { error_msg = c_format("RX %s from %s to %s on vif %s: " "ip_ttl = %d instead of %d", proto_message_type2ascii(message_type), cstring(src), cstring(dst), name().c_str(), ip_ttl, MINTTL); XLOG_WARNING("%s", error_msg.c_str()); return (XORP_ERROR); } #endif // 0 // // Source address check. // if (! (src.is_unicast() || (allow_src_zero_address && src.is_zero()))) { // // Source address must always be unicast. // The kernel should have checked that, but just in case... // error_msg = c_format("RX %s from %s to %s on vif %s: " "source must be unicast", proto_message_type2ascii(message_type), cstring(src), cstring(dst), name().c_str()); XLOG_WARNING("%s", error_msg.c_str()); return (XORP_ERROR); } if (src.af() != family()) { // Invalid source address family XLOG_WARNING("RX %s from %s to %s on vif %s: " "invalid source address family " "(received %d expected %d)", proto_message_type2ascii(message_type), cstring(src), cstring(dst), name().c_str(), src.af(), family()); } // Source address must be directly connected if (! (mld6igmp_node().is_directly_connected(*this, src) || (allow_src_zero_address && src.is_zero()))) { error_msg = c_format("RX %s from %s to %s on vif %s: " "source must be directly connected", proto_message_type2ascii(message_type), cstring(src), cstring(dst), name().c_str()); XLOG_WARNING("%s", error_msg.c_str()); return (XORP_ERROR); } if (check_src_linklocal_unicast) { if (src.is_linklocal_unicast() || (allow_src_zero_address && src.is_zero())) { // The source address is link-local or (allowed) zero address } else { // The source address is not link-local error_msg = c_format("RX %s from %s to %s on vif %s: " "source is not a link-local address", proto_message_type2ascii(message_type), cstring(src), cstring(dst), name().c_str()); XLOG_WARNING("%s", error_msg.c_str()); return (XORP_ERROR); } } // // Destination address check. // if (dst.af() != family()) { // Invalid destination address family XLOG_WARNING("RX %s from %s to %s on vif %s: " "invalid destination address family " "(received %d expected %d)", proto_message_type2ascii(message_type), cstring(src), cstring(dst), name().c_str(), dst.af(), family()); } if (check_dst_multicast && (! dst.is_multicast())) { // The destination address is not multicast error_msg = c_format("RX %s from %s to %s on vif %s: " "destination must be multicast. " "Packet ignored.", proto_message_type2ascii(message_type), cstring(src), cstring(dst), name().c_str()); XLOG_WARNING("%s", error_msg.c_str()); return (XORP_ERROR); } // // Inner multicast address scope check. // if (check_group_interfacelocal_multicast && group_address.is_interfacelocal_multicast()) { error_msg = c_format("RX %s from %s to %s on vif %s: " "invalid interface-local scope of inner " "multicast address: %s", proto_message_type2ascii(message_type), cstring(src), cstring(dst), name().c_str(), cstring(group_address)); XLOG_WARNING("%s", error_msg.c_str()); return (XORP_ERROR); } // // Origin router neighbor check. // // XXX: in IGMP and MLD we don't need such check // // Process each message, based on its type. // if (proto_is_igmp()) { switch (message_type) { case IGMP_MEMBERSHIP_QUERY: mld6igmp_membership_query_recv(src, dst, message_type, max_resp_code, group_address, buffer); break; case IGMP_V1_MEMBERSHIP_REPORT: case IGMP_V2_MEMBERSHIP_REPORT: mld6igmp_membership_report_recv(src, dst, message_type, max_resp_code, group_address, buffer); break; case IGMP_V2_LEAVE_GROUP: mld6igmp_leave_group_recv(src, dst, message_type, max_resp_code, group_address, buffer); break; case IGMP_V3_MEMBERSHIP_REPORT: mld6igmp_ssm_membership_report_recv(src, dst, message_type, buffer); break; case IGMP_DVMRP: { // // XXX: We care only about the DVMRP messages that are used // by mrinfo. // // XXX: the older purpose of the 'igmp_code' field uint16_t igmp_code = max_resp_code; switch (igmp_code) { case DVMRP_ASK_NEIGHBORS: // Some old DVMRP messages from mrinfo(?). // TODO: not implemented yet. // TODO: do we really need this message implemented? break; case DVMRP_ASK_NEIGHBORS2: // Used for mrinfo support. // XXX: not implemented yet. break; case DVMRP_INFO_REQUEST: // Information request (TODO: used by mrinfo?) // TODO: not implemented yet. break; default: // XXX: We don't care about the rest of the DVMRP_* messages break; } } case IGMP_MTRACE: // TODO: is this the new message sent by 'mtrace'? // TODO: not implemented yet. break; default: // XXX: Unrecognized message types MUST be silently ignored. break; } } if (proto_is_mld6()) { switch (message_type) { case MLD_LISTENER_QUERY: mld6igmp_membership_query_recv(src, dst, message_type, max_resp_code, group_address, buffer); break; case MLD_LISTENER_REPORT: mld6igmp_membership_report_recv(src, dst, message_type, max_resp_code, group_address, buffer); break; case MLD_LISTENER_DONE: mld6igmp_leave_group_recv(src, dst, message_type, max_resp_code, group_address, buffer); break; case MLDV2_LISTENER_REPORT: mld6igmp_ssm_membership_report_recv(src, dst, message_type, buffer); break; case MLD_MTRACE: // TODO: is this the new message sent by 'mtrace'? // TODO: not implemented yet. break; default: // XXX: Unrecognized message types MUST be silently ignored. break; } } return (XORP_OK); rcvlen_error: XLOG_UNREACHABLE(); error_msg = c_format("RX packet from %s to %s on vif %s: " "some fields are too short", cstring(src), cstring(dst), name().c_str()); XLOG_WARNING("%s", error_msg.c_str()); return (XORP_ERROR); } /** * Mld6igmpVif::update_primary_address: * @error_msg: The error message (if error). * * Update the primary address. * * The primary address should be a link-local unicast address, and * is used for transmitting the multicast control packets on the LAN. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int Mld6igmpVif::update_primary_address(string& error_msg) { bool i_was_querier = false; IPvX primary_a(IPvX::ZERO(family())); IPvX domain_wide_a(IPvX::ZERO(family())); // // Reset the primary address if it is not valid anymore. // if (Vif::find_address(primary_addr()) == NULL) { if (primary_addr() == querier_addr()) { // Reset the querier address set_querier_addr(IPvX::ZERO(family())); set_i_am_querier(false); i_was_querier = true; } set_primary_addr(IPvX::ZERO(family())); } list::const_iterator iter; for (iter = addr_list().begin(); iter != addr_list().end(); ++iter) { const VifAddr& vif_addr = *iter; const IPvX& addr = vif_addr.addr(); if (! addr.is_unicast()) continue; if (addr.is_linklocal_unicast()) { if (primary_a.is_zero()) primary_a = addr; continue; } // // XXX: assume that everything else can be a domain-wide reachable // address. if (domain_wide_a.is_zero()) domain_wide_a = addr; } // // XXX: In case of IPv6 if there is no link-local address we may try // to use the the domain-wide address as a primary address, // but the MLD spec is clear that the MLD messages are to be originated // from a link-local address. // Hence, only in case of IPv4 we assign the domain-wide address // to the primary address. // if (is_ipv4()) { if (primary_a.is_zero()) primary_a = domain_wide_a; } // // Check that the interface has a primary address. // if (primary_addr().is_zero() && primary_a.is_zero()) { error_msg = c_format("invalid primary address: %s primary_a: %s", primary_addr().str().c_str(), primary_a.str().c_str()); return (XORP_ERROR); } if (primary_addr().is_zero()) set_primary_addr(primary_a); if (i_was_querier) { // Assume again that I am the MLD6IGMP Querier set_querier_addr(primary_addr()); set_i_am_querier(true); } return (XORP_OK); } /** * Mld6igmpVif::add_protocol: * @module_id: The #xorp_module_id of the protocol to add. * @module_instance_name: The module instance name of the protocol to add. * * Add a protocol to the list of entries that would be notified if there * is membership change on this interface. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int Mld6igmpVif::add_protocol(xorp_module_id module_id, const string& module_instance_name) { if (find(_notify_routing_protocols.begin(), _notify_routing_protocols.end(), pair(module_id, module_instance_name)) != _notify_routing_protocols.end()) { return (XORP_ERROR); // Already added } _notify_routing_protocols.push_back( pair(module_id, module_instance_name)); return (XORP_OK); } /** * Mld6igmpVif::delete_protocol: * @module_id: The #xorp_module_id of the protocol to delete. * @module_instance_name: The module instance name of the protocol to delete. * * Delete a protocol from the list of entries that would be notified if there * is membership change on this interface. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int Mld6igmpVif::delete_protocol(xorp_module_id module_id, const string& module_instance_name, string& error_msg) { vector >::iterator iter; iter = find(_notify_routing_protocols.begin(), _notify_routing_protocols.end(), pair(module_id, module_instance_name)); if (iter == _notify_routing_protocols.end()) { ostringstream oss; oss << "ERROR: Cannot find routing module matching module_id: " << (int)(module_id) << " instance_name: " << module_instance_name; error_msg.append(oss.str()); return (XORP_ERROR); // Not on the list } _notify_routing_protocols.erase(iter); return (XORP_OK); } /** * Test if the interface is running in IGMPv1 mode. * * @return true if the interface is running in IGMPv1 mode, otherwise false. */ bool Mld6igmpVif::is_igmpv1_mode() const { return (proto_is_igmp() && (proto_version() == IGMP_V1)); } /** * Test if the interface is running in IGMPv2 mode. * * @return true if the interface is running in IGMPv2 mode, otherwise false. */ bool Mld6igmpVif::is_igmpv2_mode() const { return (proto_is_igmp() && (proto_version() == IGMP_V2)); } /** * Test if the interface is running in IGMPv3 mode. * * @return true if the interface is running in IGMPv3 mode, otherwise false. */ bool Mld6igmpVif::is_igmpv3_mode() const { return (proto_is_igmp() && (proto_version() == IGMP_V3)); } /** * Test if the interface is running in MLDv1 mode. * * @return true if the interface is running in MLDv1 mode, otherwise false. */ bool Mld6igmpVif::is_mldv1_mode() const { return (proto_is_mld6() && (proto_version() == MLD_V1)); } /** * Test if the interface is running in MLDv2 mode. * * @return true if the interface is running in MLDv2 mode, otherwise false. */ bool Mld6igmpVif::is_mldv2_mode() const { return (proto_is_mld6() && (proto_version() == MLD_V2)); } /** * Test if a group is running in IGMPv1 mode. * * Note that if @ref group_record is NULL, then we test whether the * interface itself is running in IGMPv1 mode. * @param group_record the group record to test. * @return true if the group is running in IGMPv1 mode, * otherwise false. */ bool Mld6igmpVif::is_igmpv1_mode(const Mld6igmpGroupRecord* group_record) const { if (group_record != NULL) return (group_record->is_igmpv1_mode()); return (is_igmpv1_mode()); } /** * Test if a group is running in IGMPv2 mode. * * Note that if @ref group_record is NULL, then we test whether the * interface itself is running in IGMPv2 mode. * @param group_record the group record to test. * @return true if the group is running in IGMPv2 mode, * otherwise false. */ bool Mld6igmpVif::is_igmpv2_mode(const Mld6igmpGroupRecord* group_record) const { if (group_record != NULL) return (group_record->is_igmpv2_mode()); return (is_igmpv2_mode()); } /** * Test if a group is running in IGMPv3 mode. * * Note that if @ref group_record is NULL, then we test whether the * interface itself is running in IGMPv3 mode. * @param group_record the group record to test. * @return true if the group is running in IGMPv3 mode, * otherwise false. */ bool Mld6igmpVif::is_igmpv3_mode(const Mld6igmpGroupRecord* group_record) const { if (group_record != NULL) return (group_record->is_igmpv3_mode()); return (is_igmpv3_mode()); } /** * Test if a group is running in MLDv1 mode. * * Note that if @ref group_record is NULL, then we test whether the * interface itself is running in MLDv1 mode. * @param group_record the group record to test. * @return true if the group is running in MLDv1 mode, * otherwise false. */ bool Mld6igmpVif::is_mldv1_mode(const Mld6igmpGroupRecord* group_record) const { if (group_record != NULL) return (group_record->is_mldv1_mode()); return (is_mldv1_mode()); } /** * Test if a group is running in MLDv2 mode. * * Note that if @ref group_record is NULL, then we test whether the * interface itself is running in MLDv2 mode. * @param group_record the group record to test. * @return true if the group is running in MLDv2 mode, * otherwise false. */ bool Mld6igmpVif::is_mldv2_mode(const Mld6igmpGroupRecord* group_record) const { if (group_record != NULL) return (group_record->is_mldv2_mode()); return (is_mldv2_mode()); } /** * Return the ASCII text description of the protocol message. * * @param message_type the protocol message type. * @return the ASCII text descrpition of the protocol message. */ const char * Mld6igmpVif::proto_message_type2ascii(uint8_t message_type) const { if (proto_is_igmp()) return (IGMPTYPE2ASCII(message_type)); if (proto_is_mld6()) return (MLDTYPE2ASCII(message_type)); return ("Unknown protocol message"); } /** * Reset and prepare the buffer for sending data. * * @return the prepared buffer. */ buffer_t * Mld6igmpVif::buffer_send_prepare() { BUFFER_RESET(_buffer_send); return (_buffer_send); } /** * Calculate the checksum of an IPv6 "pseudo-header" as described * in RFC 2460. * * @param src the source address of the pseudo-header. * @param dst the destination address of the pseudo-header. * @param len the upper-layer packet length of the pseudo-header * (in host-order). * @param protocol the upper-layer protocol number. * @return the checksum of the IPv6 "pseudo-header". */ uint16_t Mld6igmpVif::calculate_ipv6_pseudo_header_checksum(const IPvX& src, const IPvX& dst, size_t len, uint8_t protocol) { struct ip6_pseudo_hdr { struct in6_addr ip6_src; // Source address struct in6_addr ip6_dst; // Destination address uint32_t ph_len; // Upper-layer packet length uint8_t ph_zero[3]; // Zero uint8_t ph_next; // Upper-layer protocol number } ip6_pseudo_header; // TODO: may need __attribute__((__packed__)) src.copy_out(ip6_pseudo_header.ip6_src); dst.copy_out(ip6_pseudo_header.ip6_dst); ip6_pseudo_header.ph_len = htonl(len); ip6_pseudo_header.ph_zero[0] = 0; ip6_pseudo_header.ph_zero[1] = 0; ip6_pseudo_header.ph_zero[2] = 0; ip6_pseudo_header.ph_next = protocol; uint16_t cksum = inet_checksum( reinterpret_cast(&ip6_pseudo_header), sizeof(ip6_pseudo_header)); return (cksum); } /** * Notify the interested parties that there is membership change among * the local members. * * @param source the source address of the (S,G) entry that has changed. * In case of group-specific membership, it could be IPvX::ZERO(). * @param group the group address of the (S,G) entry that has changed. * @param action_jp the membership change: @ref ACTION_JOIN * or @ref ACTION_PRUNE. * @return XORP_OK on success, otherwise XORP_ERROR. */ int Mld6igmpVif::join_prune_notify_routing(const IPvX& source, const IPvX& group, action_jp_t action_jp) const { XLOG_TRACE(mld6igmp_node().is_log_trace(), "Notify routing %s membership for (%s, %s) on vif %s", (action_jp == ACTION_JOIN)? "add" : "delete", cstring(source), cstring(group), name().c_str()); vector >::const_iterator iter; for (iter = _notify_routing_protocols.begin(); iter != _notify_routing_protocols.end(); ++iter) { pair my_pair = *iter; xorp_module_id module_id = my_pair.first; string module_instance_name = my_pair.second; if (mld6igmp_node().join_prune_notify_routing(module_instance_name, module_id, vif_index(), source, group, action_jp) != XORP_OK) { // // TODO: remove ?? // } } return (XORP_OK); } bool Mld6igmpVif::i_am_querier() const { if (_proto_flags & MLD6IGMP_VIF_QUERIER) return (true); else return (false); } void Mld6igmpVif::set_i_am_querier(bool v) { if (v) { _proto_flags |= MLD6IGMP_VIF_QUERIER; // // XXX: Restore the variables that might have been adopted from // the Querier. // restore_effective_variables(); } else { _proto_flags &= ~MLD6IGMP_VIF_QUERIER; } } size_t Mld6igmpVif::mld6igmp_constant_minlen() const { if (proto_is_igmp()) return (IGMP_MINLEN); if (proto_is_mld6()) return (MLD_MINLEN); XLOG_UNREACHABLE(); return (0); } uint32_t Mld6igmpVif::mld6igmp_constant_timer_scale() const { if (proto_is_igmp()) return (IGMP_TIMER_SCALE); if (proto_is_mld6()) return (MLD_TIMER_SCALE); XLOG_UNREACHABLE(); return (0); } uint8_t Mld6igmpVif::mld6igmp_constant_membership_query() const { if (proto_is_igmp()) return (IGMP_MEMBERSHIP_QUERY); if (proto_is_mld6()) return (MLD_LISTENER_QUERY); XLOG_UNREACHABLE(); return (0); } // TODO: temporary here. Should go to the Vif class after the Vif // class starts using the Proto class string Mld6igmpVif::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); } xorp/mld6igmp/xrl_mld6igmp_node.hh0000664000076400007640000007106211540225530017334 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/mld6igmp/xrl_mld6igmp_node.hh,v 1.49 2008/10/02 21:57:44 bms Exp $ #ifndef __MLD6IGMP_XRL_MLD6IGMP_NODE_HH__ #define __MLD6IGMP_XRL_MLD6IGMP_NODE_HH__ // // MLD6IGMP XRL-aware node definition. // #include "libxipc/xrl_std_router.hh" #include "libfeaclient/ifmgr_xrl_mirror.hh" #include "xrl/interfaces/finder_event_notifier_xif.hh" #include "xrl/interfaces/fea_rawpkt4_xif.hh" #ifdef HAVE_IPV6 #include "xrl/interfaces/fea_rawpkt6_xif.hh" #endif #include "xrl/interfaces/cli_manager_xif.hh" #include "xrl/interfaces/mld6igmp_client_xif.hh" #include "xrl/targets/mld6igmp_base.hh" #include "mld6igmp_node.hh" #include "mld6igmp_node_cli.hh" // // The top-level class that wraps-up everything together under one roof // class XrlMld6igmpNode : public Mld6igmpNode, public XrlStdRouter, public XrlMld6igmpTargetBase, public Mld6igmpNodeCli { public: XrlMld6igmpNode(int family, xorp_module_id module_id, EventLoop& eventloop, const string& class_name, const string& finder_hostname, uint16_t finder_port, const string& finder_target, const string& fea_target, const string& mfea_target); virtual ~XrlMld6igmpNode(); /** * Startup the node operation. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int startup(); /** * Shutdown the node operation. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int shutdown(); /** * Get a reference to the XrlRouter instance. * * @return a reference to the XrlRouter (@ref XrlRouter) instance. */ XrlRouter& xrl_router() { return *this; } /** * Get a const reference to the XrlRouter instance. * * @return a const reference to the XrlRouter (@ref XrlRouter) instance. */ const XrlRouter& xrl_router() const { return *this; } // // XrlMld6igmpNode front-end interface // int enable_cli(); int disable_cli(); int start_cli(); int stop_cli(); int enable_mld6igmp(); int disable_mld6igmp(); int start_mld6igmp(); int stop_mld6igmp(); // XrlTask relatedMethods that need to be public void send_register_unregister_interest(); void send_register_unregister_receiver(); void send_join_leave_multicast_group(); void send_protocol_message(); protected: // // XRL target methods // /** * Get name of Xrl Target */ XrlCmdError common_0_1_get_target_name( // Output values, string& name); /** * Get version string from Xrl Target */ XrlCmdError common_0_1_get_version( // Output values, string& version); /** * Get status from Xrl Target */ XrlCmdError common_0_1_get_status(// Output values, uint32_t& status, string& reason); /** * Shutdown cleanly */ XrlCmdError common_0_1_shutdown(); XrlCmdError common_0_1_startup(); /** * Announce target birth to observer. * * @param target_class the target class name. * * @param target_instance the target instance name. */ XrlCmdError finder_event_observer_0_1_xrl_target_birth( // Input values, const string& target_class, const string& target_instance); /** * Announce target death to observer. * * @param target_class the target class name. * * @param target_instance the target instance name. */ XrlCmdError finder_event_observer_0_1_xrl_target_death( // Input values, const string& target_class, const string& target_instance); /** * Process a CLI command. * * @param processor_name the processor name for this command. * * @param cli_term_name the terminal name the command was entered from. * * @param cli_session_id the CLI session ID the command was entered from. * * @param command_name the command name to process. * * @param command_args the command arguments to process. * * @param ret_processor_name the processor name to return back to the CLI. * * @param ret_cli_term_name the terminal name to return back. * * @param ret_cli_session_id the CLI session ID to return back. * * @param ret_command_output the command output to return back. */ XrlCmdError cli_processor_0_1_process_command( // Input values, const string& processor_name, const string& cli_term_name, const uint32_t& cli_session_id, const string& command_name, const string& command_args, // Output values, string& ret_processor_name, string& ret_cli_term_name, uint32_t& ret_cli_session_id, string& ret_command_output); /** * Receive an IPv4 packet from a raw socket. * * @param if_name the interface name the packet arrived on. * * @param vif_name the vif name the packet arrived on. * * @param src_address the IP source address. * * @param dst_address the IP destination address. * * @param ip_protocol the IP protocol number. * * @param ip_ttl the IP TTL (hop-limit). If it has a negative value, then * the received value is unknown. * * @param ip_tos the Type of Service (Diffserv/ECN bits for IPv4). If it * has a negative value, then the received value is unknown. * * @param ip_router_alert if true, the IP Router Alert option was included * in the IP packet. * * @param ip_internet_control if true, then this is IP control traffic. */ XrlCmdError raw_packet4_client_0_1_recv( // Input values, const string& if_name, const string& vif_name, const IPv4& src_address, const IPv4& dst_address, const uint32_t& ip_protocol, const int32_t& ip_ttl, const int32_t& ip_tos, const bool& ip_router_alert, const bool& ip_internet_control, const vector& payload); /** * Receive an IPv6 packet from a raw socket. * * @param if_name the interface name the packet arrived on. * * @param vif_name the vif name the packet arrived on. * * @param src_address the IP source address. * * @param dst_address the IP destination address. * * @param ip_protocol the IP protocol number. * * @param ip_ttl the IP TTL (hop-limit). If it has a negative value, then * the received value is unknown. * * @param ip_tos the Type Of Service (IP traffic class for IPv4). If it * has a negative value, then the received value is unknown. * * @param ip_router_alert if true, the IP Router Alert option was included * in the IP packet. * * @param ip_internet_control if true, then this is IP control traffic. * * @param ext_headers_type a list of u32 integers with the types of the * optional extention headers. * * @param ext_headers_payload a list of payload data, one for each * optional extention header. The number of entries must match * ext_headers_type. */ XrlCmdError raw_packet6_client_0_1_recv( // Input values, const string& if_name, const string& vif_name, const IPv6& src_address, const IPv6& dst_address, const uint32_t& ip_protocol, const int32_t& ip_ttl, const int32_t& ip_tos, const bool& ip_router_alert, const bool& ip_internet_control, const XrlAtomList& ext_headers_type, const XrlAtomList& ext_headers_payload, const vector& payload); /** * Enable/disable/start/stop a MLD6IGMP vif interface. * * @param vif_name the name of the vif to enable/disable/start/stop. * * @param enable if true, then enable the vif, otherwise disable it. */ XrlCmdError mld6igmp_0_1_enable_vif( // Input values, const string& vif_name, const bool& enable); XrlCmdError mld6igmp_0_1_start_vif( // Input values, const string& vif_name); XrlCmdError mld6igmp_0_1_stop_vif( // Input values, const string& vif_name); /** * Enable/disable/start/stop all MLD6IGMP vif interfaces. * * @param enable if true, then enable the vifs, otherwise disable them. */ XrlCmdError mld6igmp_0_1_enable_all_vifs( // Input values, const bool& enable); XrlCmdError mld6igmp_0_1_start_all_vifs(); XrlCmdError mld6igmp_0_1_stop_all_vifs(); /** * Enable/disable/start/stop the MLD6IGMP protocol. * * @param enable if true, then enable the MLD6IGMP protocol, otherwise * disable it. */ XrlCmdError mld6igmp_0_1_enable_mld6igmp( // Input values, const bool& enable); XrlCmdError mld6igmp_0_1_start_mld6igmp(); XrlCmdError mld6igmp_0_1_stop_mld6igmp(); /** * Enable/disable/start/stop the MLD6IGMP CLI access. * * @param enable if true, then enable the MLD6IGMP CLI access, otherwise * disable it. */ XrlCmdError mld6igmp_0_1_enable_cli( // Input values, const bool& enable); XrlCmdError mld6igmp_0_1_start_cli(); XrlCmdError mld6igmp_0_1_stop_cli(); /** * Get the configured protocol version per interface. * * @param vif_name the name of the vif to apply to. * * @param proto_version the protocol version. */ XrlCmdError mld6igmp_0_1_get_vif_proto_version( // Input values, const string& vif_name, // Output values, uint32_t& proto_version); /** * Set the protocol version per interface. * * @param vif_name the name of the vif to apply to. * * @param proto_version the protocol version. */ XrlCmdError mld6igmp_0_1_set_vif_proto_version( // Input values, const string& vif_name, const uint32_t& proto_version); /** * Reset the protocol version per interface to its default value. * * @param vif_name the name of the vif to apply to. */ XrlCmdError mld6igmp_0_1_reset_vif_proto_version( // Input values, const string& vif_name); /** * Get the IP Router Alert option check per interface for received * packets. * * @param vif_name the name of the vif to apply to. * * @param enabled if true, then the IP Router Alert option check was * enabled, otherwise it was disabled. */ XrlCmdError mld6igmp_0_1_get_vif_ip_router_alert_option_check( // Input values, const string& vif_name, // Output values, bool& enabled); /** * Set the IP Router Alert option check per interface for received * packets. * * @param vif_name the name of the vif to apply to. * * @param enable if true, then enable the IP Router Alert option check, * otherwise disable it. */ XrlCmdError mld6igmp_0_1_set_vif_ip_router_alert_option_check( // Input values, const string& vif_name, const bool& enable); /** * Reset the IP Router Alert option check per interface for received * packets to its default value. * * @param vif_name the name of the vif to apply to. */ XrlCmdError mld6igmp_0_1_reset_vif_ip_router_alert_option_check( // Input values, const string& vif_name); /** * Get the Query Interval per interface. * * @param vif_name the name of the vif to apply to. * * @param interval_sec the number of seconds in the interval. * * @param interval_usec the number of microseconds (in addition to * interval_sec) in the interval. */ XrlCmdError mld6igmp_0_1_get_vif_query_interval( // Input values, const string& vif_name, // Output values, uint32_t& interval_sec, uint32_t& interval_usec); /** * Set the Query Interval per interface. * * @param vif_name the name of the vif to apply to. * * @param interval_sec the number of seconds in the interval. * * @param interval_usec the number of microseconds (in addition to * interval_sec) in the interval. */ XrlCmdError mld6igmp_0_1_set_vif_query_interval( // Input values, const string& vif_name, const uint32_t& interval_sec, const uint32_t& interval_usec); /** * Reset the Query Interval per interface to its default value. * * @param vif_name the name of the vif to apply to. */ XrlCmdError mld6igmp_0_1_reset_vif_query_interval( // Input values, const string& vif_name); /** * Get the Last Member Query Interval per interface. * * @param vif_name the name of the vif to apply to. * * @param interval_sec the number of seconds in the interval. * * @param interval_usec the number of microseconds (in addition to * interval_sec) in the interval. */ XrlCmdError mld6igmp_0_1_get_vif_query_last_member_interval( // Input values, const string& vif_name, // Output values, uint32_t& interval_sec, uint32_t& interval_usec); /** * Set the Last Member Query Interval per interface. * * @param vif_name the name of the vif to apply to. * * @param interval_sec the number of seconds in the interval. * * @param interval_usec the number of microseconds (in addition to * interval_sec) in the interval. */ XrlCmdError mld6igmp_0_1_set_vif_query_last_member_interval( // Input values, const string& vif_name, const uint32_t& interval_sec, const uint32_t& interval_usec); /** * Reset the Last Member Query Interval per interface to its default * value. * * @param vif_name the name of the vif to apply to. */ XrlCmdError mld6igmp_0_1_reset_vif_query_last_member_interval( // Input values, const string& vif_name); /** * Get the Query Response Interval per interface. * * @param vif_name the name of the vif to apply to. * * @param interval_sec the number of seconds in the interval. * * @param interval_usec the number of microseconds (in addition to * interval_sec) in the interval. */ XrlCmdError mld6igmp_0_1_get_vif_query_response_interval( // Input values, const string& vif_name, // Output values, uint32_t& interval_sec, uint32_t& interval_usec); /** * Set the Query Response Interval per interface. * * @param vif_name the name of the vif to apply to. * * @param interval_sec the number of seconds in the interval. * * @param interval_usec the number of microseconds (in addition to * interval_sec) in the interval. */ XrlCmdError mld6igmp_0_1_set_vif_query_response_interval( // Input values, const string& vif_name, const uint32_t& interval_sec, const uint32_t& interval_usec); /** * Reset the Query Response Interval per interface to its default value. * * @param vif_name the name of the vif to apply to. */ XrlCmdError mld6igmp_0_1_reset_vif_query_response_interval( // Input values, const string& vif_name); /** * Get the Robustness Variable count per interface. * * @param vif_name the name of the vif to apply to. * * @param robust_count the count value. */ XrlCmdError mld6igmp_0_1_get_vif_robust_count( // Input values, const string& vif_name, // Output values, uint32_t& robust_count); /** * Set the Robustness Variable count per interface. * * @param vif_name the name of the vif to apply to. * * @param robust_count the count value. */ XrlCmdError mld6igmp_0_1_set_vif_robust_count( // Input values, const string& vif_name, const uint32_t& robust_count); /** * Reset the Robustness Variable count per interface to its default value. * * @param vif_name the name of the vif to apply to. */ XrlCmdError mld6igmp_0_1_reset_vif_robust_count( // Input values, const string& vif_name); /** * Enable/disable the MLD6IGMP trace log for all operations. * * @param enable if true, then enable the trace log, otherwise disable it. */ XrlCmdError mld6igmp_0_1_log_trace_all( // Input values, const bool& enable); /** * Add/delete a client protocol in the MLD/IGMP protocol. * * @param xrl_sender_name the XRL name of the originator of this XRL. * * @param protocol_name the name of the protocol to add/delete. * * @param protocol_id the ID of the protocol to add/delete (both sides * must agree on the particular values). * * @param vif_name the name of the vif the protocol add/delete to apply * to. * * @param vif_index the index of the vif the protocol add/delete to apply * to. The added protocol will receive Join/Leave membership information * about same-LAN members for the particular vif. */ XrlCmdError mld6igmp_0_1_add_protocol4( // Input values, const string& xrl_sender_name, const string& protocol_name, const uint32_t& protocol_id, const string& vif_name, const uint32_t& vif_index); XrlCmdError mld6igmp_0_1_add_protocol6( // Input values, const string& xrl_sender_name, const string& protocol_name, const uint32_t& protocol_id, const string& vif_name, const uint32_t& vif_index); XrlCmdError mld6igmp_0_1_delete_protocol4( // Input values, const string& xrl_sender_name, const string& protocol_name, const uint32_t& protocol_id, const string& vif_name, const uint32_t& vif_index); XrlCmdError mld6igmp_0_1_delete_protocol6( // Input values, const string& xrl_sender_name, const string& protocol_name, const uint32_t& protocol_id, const string& vif_name, const uint32_t& vif_index); private: class XrlTaskBase; const ServiceBase* ifmgr_mirror_service_base() const { return dynamic_cast(&_ifmgr); } const IfMgrIfTree& ifmgr_iftree() const { return _ifmgr.iftree(); } /** * Called when Finder connection is established. * * Note that this method overwrites an XrlRouter virtual method. */ virtual void finder_connect_event(); /** * Called when Finder disconnect occurs. * * Note that this method overwrites an XrlRouter virtual method. */ virtual void finder_disconnect_event(); // // Methods to handle the XRL tasks // void add_task(XrlTaskBase* xrl_task); void send_xrl_task(); void pop_xrl_task(); void retry_xrl_task(); void fea_register_startup(); void mfea_register_startup(); void fea_register_shutdown(); void mfea_register_shutdown(); void finder_send_register_unregister_interest_cb(const XrlError& xrl_error); // // Protocol node methods // int register_receiver(const string& if_name, const string& vif_name, uint8_t ip_protocol, bool enable_multicast_loopback); int unregister_receiver(const string& if_name, const string& vif_name, uint8_t ip_protocol); void fea_client_send_register_unregister_receiver_cb(const XrlError& xrl_error); int join_multicast_group(const string& if_name, const string& vif_name, uint8_t ip_protocol, const IPvX& group_address); int leave_multicast_group(const string& if_name, const string& vif_name, uint8_t ip_protocol, const IPvX& group_address); void fea_client_send_join_leave_multicast_group_cb(const XrlError& xrl_error); int proto_send(const string& if_name, const string& vif_name, const IPvX& src_address, const IPvX& dst_address, uint8_t ip_protocol, int32_t ip_ttl, int32_t ip_tos, bool ip_router_alert, bool ip_internet_control, const uint8_t* sndbuf, size_t sndlen, string& error_msg); void fea_client_send_protocol_message_cb(const XrlError& xrl_error); int send_add_membership(const string& dst_module_instance_name, xorp_module_id dst_module_id, uint32_t vif_index, const IPvX& source, const IPvX& group); int send_delete_membership(const string& dst_module_instance_name, xorp_module_id dst_module_id, uint32_t vif_index, const IPvX& source, const IPvX& group); void send_add_delete_membership(); void mld6igmp_client_send_add_delete_membership_cb(const XrlError& xrl_error); // // Protocol node CLI methods // int add_cli_command_to_cli_manager(const char *command_name, const char *command_help, bool is_command_cd, const char *command_cd_prompt, bool is_command_processor); void cli_manager_client_send_add_cli_command_cb(const XrlError& xrl_error); int delete_cli_command_from_cli_manager(const char *command_name); void cli_manager_client_send_delete_cli_command_cb(const XrlError& xrl_error); int family() const { return (Mld6igmpNode::family()); } /** * A base class for handling tasks for sending XRL requests. */ class XrlTaskBase { public: XrlTaskBase(XrlMld6igmpNode& xrl_mld6igmp_node) : _xrl_mld6igmp_node(xrl_mld6igmp_node) {} virtual ~XrlTaskBase() {} virtual void dispatch() = 0; virtual const char* operation_name() const = 0; protected: XrlMld6igmpNode& _xrl_mld6igmp_node; private: }; /** * Class for handling the task to register/unregister interest * in the FEA or MFEA with the Finder. */ class RegisterUnregisterInterest : public XrlTaskBase { public: RegisterUnregisterInterest(XrlMld6igmpNode& xrl_mld6igmp_node, const string& target_name, bool is_register) : XrlTaskBase(xrl_mld6igmp_node), _target_name(target_name), _is_register(is_register) {} void dispatch() { _xrl_mld6igmp_node.send_register_unregister_interest(); } const char* operation_name() const { return ((_is_register)? "register" : "unregister"); } const string& target_name() const { return _target_name; } bool is_register() const { return _is_register; } private: string _target_name; bool _is_register; }; /** * Class for handling the task to register/unregister with the FEA * as a receiver on an interface. */ class RegisterUnregisterReceiver : public XrlTaskBase { public: RegisterUnregisterReceiver(XrlMld6igmpNode& xrl_mld6igmp_node, const string& if_name, const string& vif_name, uint8_t ip_protocol, bool enable_multicast_loopback, bool is_register) : XrlTaskBase(xrl_mld6igmp_node), _if_name(if_name), _vif_name(vif_name), _ip_protocol(ip_protocol), _enable_multicast_loopback(enable_multicast_loopback), _is_register(is_register) {} void dispatch() { _xrl_mld6igmp_node.send_register_unregister_receiver(); } const char* operation_name() const { return ((_is_register)? "register" : "unregister"); } const string& if_name() const { return _if_name; } const string& vif_name() const { return _vif_name; } uint8_t ip_protocol() const { return _ip_protocol; } bool enable_multicast_loopback() const { return _enable_multicast_loopback; } bool is_register() const { return _is_register; } private: string _if_name; string _vif_name; uint8_t _ip_protocol; bool _enable_multicast_loopback; bool _is_register; }; /** * Class for handling the task of join/leave multicast group requests */ class JoinLeaveMulticastGroup : public XrlTaskBase { public: JoinLeaveMulticastGroup(XrlMld6igmpNode& xrl_mld6igmp_node, const string& if_name, const string& vif_name, uint8_t ip_protocol, const IPvX& group_address, bool is_join) : XrlTaskBase(xrl_mld6igmp_node), _if_name(if_name), _vif_name(vif_name), _ip_protocol(ip_protocol), _group_address(group_address), _is_join(is_join) {} void dispatch() { _xrl_mld6igmp_node.send_join_leave_multicast_group(); } const char* operation_name() const { return ((_is_join)? "join" : "leave"); } const string& if_name() const { return _if_name; } const string& vif_name() const { return _vif_name; } uint8_t ip_protocol() const { return _ip_protocol; } const IPvX& group_address() const { return _group_address; } bool is_join() const { return _is_join; } private: string _if_name; string _vif_name; uint8_t _ip_protocol; IPvX _group_address; bool _is_join; }; /** * Class for handling the task of sending protocol messages */ class SendProtocolMessage : public XrlTaskBase { public: SendProtocolMessage(XrlMld6igmpNode& xrl_mld6igmp_node, const string& if_name, const string& vif_name, const IPvX& src_address, const IPvX& dst_address, uint8_t ip_protocol, int32_t ip_ttl, int32_t ip_tos, bool ip_router_alert, bool ip_internet_control, const uint8_t* sndbuf, size_t sndlen) : XrlTaskBase(xrl_mld6igmp_node), _if_name(if_name), _vif_name(vif_name), _src_address(src_address), _dst_address(dst_address), _ip_protocol(ip_protocol), _ip_ttl(ip_ttl), _ip_tos(ip_tos), _ip_router_alert(ip_router_alert), _ip_internet_control(ip_internet_control) { _payload.resize(sndlen); for (size_t i = 0; i < sndlen; i++) _payload[i] = sndbuf[i]; } void dispatch() { _xrl_mld6igmp_node.send_protocol_message(); } const char* operation_name() const { return ("send"); } const string& if_name() const { return _if_name; } const string& vif_name() const { return _vif_name; } const IPvX& src_address() const { return _src_address; } const IPvX& dst_address() const { return _dst_address; } uint8_t ip_protocol() const { return _ip_protocol; } int32_t ip_ttl() const { return _ip_ttl; } int32_t ip_tos() const { return _ip_tos; } bool ip_router_alert() const { return _ip_router_alert; } bool ip_internet_control() const { return _ip_internet_control; } const vector& payload() const { return _payload; } private: string _if_name; string _vif_name; IPvX _src_address; IPvX _dst_address; uint8_t _ip_protocol; int32_t _ip_ttl; int32_t _ip_tos; bool _ip_router_alert; bool _ip_internet_control; vector _payload; }; /** * Class for handling the queue of sending Add/Delete membership requests */ class SendAddDeleteMembership { public: SendAddDeleteMembership(const string& dst_module_instance_name, xorp_module_id dst_module_id, uint32_t vif_index, const IPvX& source, const IPvX& group, bool is_add) : _dst_module_instance_name(dst_module_instance_name), _dst_module_id(dst_module_id), _vif_index(vif_index), _source(source), _group(group), _is_add(is_add) {} #ifdef XORP_USE_USTL SendAddDeleteMembership() { } #endif const char* operation_name() const { return ((_is_add)? "add membership" : "delete membership"); } const string& dst_module_instance_name() const { return _dst_module_instance_name; } xorp_module_id dst_module_id() const { return _dst_module_id; } uint32_t vif_index() const { return _vif_index; } const IPvX& source() const { return _source; } const IPvX& group() const { return _group; } bool is_add() const { return _is_add; } private: string _dst_module_instance_name; xorp_module_id _dst_module_id; uint32_t _vif_index; IPvX _source; IPvX _group; bool _is_add; }; EventLoop& _eventloop; const string _finder_target; const string _fea_target; const string _mfea_target; IfMgrXrlMirror _ifmgr; XrlRawPacket4V0p1Client _xrl_fea_client4; #ifdef HAVE_IPV6 XrlRawPacket6V0p1Client _xrl_fea_client6; #endif XrlMld6igmpClientV0p1Client _xrl_mld6igmp_client_client; XrlCliManagerV0p1Client _xrl_cli_manager_client; XrlFinderEventNotifierV0p1Client _xrl_finder_client; static const TimeVal RETRY_TIMEVAL; bool _is_finder_alive; bool _is_fea_alive; bool _is_fea_registered; bool _is_mfea_alive; bool _is_mfea_registered; list _xrl_tasks_queue; XorpTimer _xrl_tasks_queue_timer; list _send_add_delete_membership_queue; XorpTimer _send_add_delete_membership_queue_timer; }; #endif // __MLD6IGMP_XRL_MLD6IGMP_NODE_HH__ xorp/mld6igmp/xorp_mld.cc0000664000076400007640000001254211540224231015530 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // XORP MLD/IGMP module implementation. // #include "mld6igmp_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/callback.hh" #include "libxorp/eventloop.hh" #include "libxorp/exceptions.hh" #include "xrl_mld6igmp_node.hh" #ifdef HAVE_GETOPT_H #include #endif // // Local functions prototypes // static void usage(const char *argv0, int exit_value); /** * usage: * @argv0: Argument 0 when the program was called (the program name itself). * @exit_value: The exit value of the program. * * Print the program usage. * If @exit_value is 0, the usage will be printed to the standart output, * otherwise to the standart error. **/ static void usage(const char *argv0, int exit_value) { FILE *output; const char *progname = strrchr(argv0, '/'); if (progname != NULL) progname++; // Skip the last '/' if (progname == NULL) progname = argv0; // // If the usage is printed because of error, output to stderr, otherwise // output to stdout. // if (exit_value == 0) output = stdout; else output = stderr; fprintf(output, "Usage: %s [-F [:]]\n", progname); fprintf(output, " -F [:] : finder hostname and port\n"); fprintf(output, " -h : usage (this message)\n"); fprintf(output, "\n"); fprintf(output, "Program name: %s\n", progname); fprintf(output, "Module name: %s\n", XORP_MODULE_NAME); fprintf(output, "Module version: %s\n", XORP_MODULE_VERSION); exit (exit_value); // NOTREACHED } static void mld6igmp_main(const string& finder_hostname, uint16_t finder_port) { #ifdef HAVE_IPV6 setup_dflt_sighandlers(); // // Init stuff // EventLoop eventloop; // // MLD6IGMP node // XrlMld6igmpNode xrl_mld6igmp_node6(AF_INET6, XORP_MODULE_MLD6IGMP, eventloop, xorp_module_name(AF_INET6, XORP_MODULE_MLD6IGMP), finder_hostname, finder_port, "finder", xorp_module_name(AF_INET6, XORP_MODULE_FEA), xorp_module_name(AF_INET6, XORP_MODULE_MFEA)); wait_until_xrl_router_is_ready(eventloop, xrl_mld6igmp_node6.xrl_router()); // // Startup // #ifdef HAVE_IPV6_MULTICAST xrl_mld6igmp_node6.enable_mld6igmp(); // xrl_mld6igmp_node6.startup(); xrl_mld6igmp_node6.enable_cli(); xrl_mld6igmp_node6.start_cli(); #endif // // Main loop // #ifdef HAVE_IPV6_MULTICAST while (xorp_do_run && !xrl_mld6igmp_node6.is_done()) { eventloop.run(); } while (xrl_mld6igmp_node6.xrl_router().pending()) { eventloop.run(); } #endif // HAVE_IPV6_MULTICAST #endif // HAVE_IPV6 UNUSED(finder_hostname); UNUSED(finder_port); } int main(int argc, char *argv[]) { int ch; string::size_type idx; const char *argv0 = argv[0]; string finder_hostname = FinderConstants::FINDER_DEFAULT_HOST().str(); uint16_t finder_port = FinderConstants::FINDER_DEFAULT_PORT(); // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_level_set_verbose(XLOG_LEVEL_INFO, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); // // Get the program options // while ((ch = getopt(argc, argv, "F:h")) != -1) { switch (ch) { case 'F': // Finder hostname and port finder_hostname = optarg; idx = finder_hostname.find(':'); if (idx != string::npos) { if (idx + 1 >= finder_hostname.length()) { // No port number usage(argv0, 1); // NOTREACHED } char* p = &finder_hostname[idx + 1]; finder_port = static_cast(atoi(p)); finder_hostname = finder_hostname.substr(0, idx); } break; case 'h': case '?': // Help usage(argv0, 0); // NOTREACHED break; default: usage(argv0, 1); // NOTREACHED break; } } argc -= optind; argv += optind; if (argc != 0) { usage(argv0, 1); // NOTREACHED } // // Run everything // try { mld6igmp_main(finder_hostname, finder_port); } catch(...) { xorp_catch_standard_exceptions(); } // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); exit (0); } xorp/mld6igmp/mld6igmp_group_record.cc0000664000076400007640000013213411540224230020174 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // Multicast group record information used by // IGMPv1 and IGMPv2 (RFC 2236), IGMPv3 (RFC 3376), // MLDv1 (RFC 2710), and MLDv2 (RFC 3810). // #include "mld6igmp_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvx.hh" #include "mld6igmp_group_record.hh" #include "mld6igmp_node.hh" #include "mld6igmp_vif.hh" /** * Mld6igmpGroupRecord::Mld6igmpGroupRecord: * @mld6igmp_vif: The vif interface this entry belongs to. * @group: The entry group address. * * Return value: **/ Mld6igmpGroupRecord::Mld6igmpGroupRecord(Mld6igmpVif& mld6igmp_vif, const IPvX& group) : _mld6igmp_vif(mld6igmp_vif), _group(group), _is_include_mode(true), _do_forward_sources(*this), _dont_forward_sources(*this), _last_reported_host(IPvX::ZERO(family())), _query_retransmission_count(0) { } /** * Mld6igmpGroupRecord::~Mld6igmpGroupRecord: * @: * * Mld6igmpGroupRecord destructor. **/ Mld6igmpGroupRecord::~Mld6igmpGroupRecord() { _do_forward_sources.delete_payload_and_clear(); _dont_forward_sources.delete_payload_and_clear(); } /** * Get the corresponding event loop. * * @return the corresponding event loop. */ EventLoop& Mld6igmpGroupRecord::eventloop() { return (_mld6igmp_vif.mld6igmp_node().eventloop()); } /** * Find a source that should be forwarded. * * @param source the source address. * @return the corresponding source record (@ref Mld6igmpSourceRecord) * if found, otherwise NULL. */ Mld6igmpSourceRecord* Mld6igmpGroupRecord::find_do_forward_source(const IPvX& source) { return (_do_forward_sources.find_source_record(source)); } /** * Find a source that should not be forwarded. * * @param source the source address. * @return the corresponding source record (@ref Mld6igmpSourceRecord) * if found, otherwise NULL. */ Mld6igmpSourceRecord* Mld6igmpGroupRecord::find_dont_forward_source(const IPvX& source) { return (_dont_forward_sources.find_source_record(source)); } /** * Test whether the entry is unused. * * @return true if the entry is unused, otherwise false. */ bool Mld6igmpGroupRecord::is_unused() const { if (is_include_mode()) { if (_do_forward_sources.empty()) { XLOG_ASSERT(_dont_forward_sources.empty()); return (true); } return (false); } if (is_exclude_mode()) { // // XXX: the group timer must be running in EXCLUDE mode, // otherwise there must have been transition to INCLUDE mode. // if (_group_timer.scheduled()) return (false); XLOG_ASSERT(_do_forward_sources.empty()); XLOG_ASSERT(_dont_forward_sources.empty()); return (true); } XLOG_UNREACHABLE(); return (true); } /** * Process MODE_IS_INCLUDE report. * * @param sources the source addresses. * @param last_reported_host the address of the host that last reported * as member. */ void Mld6igmpGroupRecord::process_mode_is_include(const set& sources, const IPvX& last_reported_host) { bool old_is_include_mode = is_include_mode(); set old_do_forward_sources = _do_forward_sources.extract_source_addresses(); set old_dont_forward_sources = _dont_forward_sources.extract_source_addresses(); set_last_reported_host(last_reported_host); if (is_include_mode()) { // // Router State: INCLUDE (A) // Report Received: IS_IN (B) // New Router State: INCLUDE (A + B) // Actions: (B) = GMI // Mld6igmpSourceSet& a = _do_forward_sources; const set& b = sources; TimeVal gmi = _mld6igmp_vif.group_membership_interval(); set_include_mode(); _do_forward_sources = a + b; // (A + B) _do_forward_sources.set_source_timer(b, gmi); // (B) = GMI calculate_forwarding_changes(old_is_include_mode, old_do_forward_sources, old_dont_forward_sources); return; } if (is_exclude_mode()) { // // Router State: EXCLUDE (X, Y) // Report Received: IS_IN (A) // New Router State: EXCLUDE (X + A, Y - A) // Actions: (A) = GMI // Mld6igmpSourceSet& x = _do_forward_sources; Mld6igmpSourceSet& y = _dont_forward_sources; const set& a = sources; TimeVal gmi = _mld6igmp_vif.group_membership_interval(); set_exclude_mode(); // XXX: first transfer (Y * A) from (Y) to (X) Mld6igmpSourceSet y_and_a = y * a; _do_forward_sources = x + y_and_a; _do_forward_sources = x + a; // (X + A) _dont_forward_sources = y - a; // (Y - A) _do_forward_sources.set_source_timer(a, gmi); // (A) = GMI calculate_forwarding_changes(old_is_include_mode, old_do_forward_sources, old_dont_forward_sources); return; } } /** * Process MODE_IS_EXCLUDE report. * * @param sources the source addresses. * @param last_reported_host the address of the host that last reported * as member. */ void Mld6igmpGroupRecord::process_mode_is_exclude(const set& sources, const IPvX& last_reported_host) { bool old_is_include_mode = is_include_mode(); set old_do_forward_sources = _do_forward_sources.extract_source_addresses(); set old_dont_forward_sources = _dont_forward_sources.extract_source_addresses(); set_last_reported_host(last_reported_host); if (is_include_mode()) { // // Router State: INCLUDE (A) // Report Received: IS_EX (B) // New Router State: EXCLUDE (A * B, B - A) // Actions: (B - A) = 0 // Delete (A - B) // Group Timer = GMI // Mld6igmpSourceSet& a = _do_forward_sources; const set& b = sources; TimeVal gmi = _mld6igmp_vif.group_membership_interval(); set_exclude_mode(); Mld6igmpSourceSet a_minus_b = a - b; // A - B _dont_forward_sources = _dont_forward_sources + b; // B _dont_forward_sources = _dont_forward_sources - a; // B - A _do_forward_sources = a * b; // A * B _dont_forward_sources.cancel_source_timer(); // (B - A) = 0 a_minus_b.delete_payload_and_clear(); // Delete (A-B) _group_timer = eventloop().new_oneoff_after( gmi, callback(this, &Mld6igmpGroupRecord::group_timer_timeout)); calculate_forwarding_changes(old_is_include_mode, old_do_forward_sources, old_dont_forward_sources); return; } if (is_exclude_mode()) { // // Router State: EXCLUDE (X, Y) // Report Received: IS_EX (A) // New Router State: EXCLUDE (A - Y, Y * A) // Actions: (A - X - Y) = GMI // Delete (X - A) // Delete (Y - A) // Group Timer = GMI // Mld6igmpSourceSet& x = _do_forward_sources; Mld6igmpSourceSet x_copy = x; Mld6igmpSourceSet& y = _dont_forward_sources; const set& a = sources; TimeVal gmi = _mld6igmp_vif.group_membership_interval(); set_exclude_mode(); Mld6igmpSourceSet x_minus_a = x - a; // X - A Mld6igmpSourceSet y_minus_a = y - a; // Y - A // XXX: The (X * A) below is needed to preserve the original timers _do_forward_sources = x * a; // X * A _do_forward_sources = _do_forward_sources + a; // A _do_forward_sources = _do_forward_sources - y; // A - Y _dont_forward_sources = y * a; // Y * A Mld6igmpSourceSet a_minus_x_minus_y(*this); a_minus_x_minus_y = _do_forward_sources - x_copy; // A - X - Y a_minus_x_minus_y.set_source_timer(gmi); // (A - X - Y) = GMI x_minus_a.delete_payload_and_clear(); // Delete (X - A) y_minus_a.delete_payload_and_clear(); // Delete (Y - A) _group_timer = eventloop().new_oneoff_after( gmi, callback(this, &Mld6igmpGroupRecord::group_timer_timeout)); calculate_forwarding_changes(old_is_include_mode, old_do_forward_sources, old_dont_forward_sources); return; } } /** * Process CHANGE_TO_INCLUDE_MODE report. * * @param sources the source addresses. * @param last_reported_host the address of the host that last reported * as member. */ void Mld6igmpGroupRecord::process_change_to_include_mode(const set& sources, const IPvX& last_reported_host) { bool old_is_include_mode = is_include_mode(); set old_do_forward_sources = _do_forward_sources.extract_source_addresses(); set old_dont_forward_sources = _dont_forward_sources.extract_source_addresses(); string dummy_error_msg; set_last_reported_host(last_reported_host); if (is_include_mode()) { // // Router State: INCLUDE (A) // Report Received: TO_IN (B) // New Router State: INCLUDE (A + B) // Actions: (B) = GMI // Send Q(G, A - B) // Mld6igmpSourceSet& a = _do_forward_sources; const set& b = sources; TimeVal gmi = _mld6igmp_vif.group_membership_interval(); set_include_mode(); Mld6igmpSourceSet a_minus_b = a - b; // A - B _do_forward_sources = a + b; // A + B _do_forward_sources.set_source_timer(b, gmi); // (B) = GMI // Send Q(G, A - B) with a_minus_b _mld6igmp_vif.mld6igmp_group_source_query_send( group(), a_minus_b.extract_source_addresses(), dummy_error_msg); calculate_forwarding_changes(old_is_include_mode, old_do_forward_sources, old_dont_forward_sources); return; } if (is_exclude_mode()) { // // Router State: EXCLUDE (X, Y) // Report Received: TO_IN (A) // New Router State: EXCLUDE (X + A, Y - A) // Actions: (A) = GMI // Send Q(G, X - A) // Send Q(G) // Mld6igmpSourceSet& x = _do_forward_sources; Mld6igmpSourceSet& y = _dont_forward_sources; const set& a = sources; TimeVal gmi = _mld6igmp_vif.group_membership_interval(); set_exclude_mode(); Mld6igmpSourceSet x_minus_a = x - a; // X - A // XXX: first transfer (Y * A) from (Y) to (X) Mld6igmpSourceSet y_and_a = y * a; _do_forward_sources = x + y_and_a; _do_forward_sources = x + a; // X + A _dont_forward_sources = y - a; // Y - A _do_forward_sources.set_source_timer(a, gmi); // (A) = GMI // Send Q(G, X - A) with x_minus_a _mld6igmp_vif.mld6igmp_group_source_query_send( group(), x_minus_a.extract_source_addresses(), dummy_error_msg); // Send Q(G) _mld6igmp_vif.mld6igmp_group_query_send(group(), dummy_error_msg); calculate_forwarding_changes(old_is_include_mode, old_do_forward_sources, old_dont_forward_sources); return; } } /** * Process CHANGE_TO_EXCLUDE_MODE report. * * @param sources the source addresses. * @param last_reported_host the address of the host that last reported * as member. */ void Mld6igmpGroupRecord::process_change_to_exclude_mode(const set& sources, const IPvX& last_reported_host) { bool old_is_include_mode = is_include_mode(); set old_do_forward_sources = _do_forward_sources.extract_source_addresses(); set old_dont_forward_sources = _dont_forward_sources.extract_source_addresses(); string dummy_error_msg; set_last_reported_host(last_reported_host); if (is_include_mode()) { // // Router State: INCLUDE (A) // Report Received: TO_EX (B) // New Router State: EXCLUDE (A * B, B - A) // Actions: (B - A) = 0 // Delete (A - B) // Send Q(G, A * B) // Group Timer = GMI // Mld6igmpSourceSet& a = _do_forward_sources; const set& b = sources; TimeVal gmi = _mld6igmp_vif.group_membership_interval(); set_exclude_mode(); Mld6igmpSourceSet a_minus_b = a - b; // A - B _dont_forward_sources = _dont_forward_sources + b; // B _dont_forward_sources = _dont_forward_sources - a; // B - A _do_forward_sources = a * b; // A * B _dont_forward_sources.cancel_source_timer(); // (B - A) = 0 a_minus_b.delete_payload_and_clear(); // Delete (A-B) _group_timer = eventloop().new_oneoff_after( gmi, callback(this, &Mld6igmpGroupRecord::group_timer_timeout)); // Send Q(G, A * B) with _do_forward_sources _mld6igmp_vif.mld6igmp_group_source_query_send( group(), _do_forward_sources.extract_source_addresses(), dummy_error_msg); calculate_forwarding_changes(old_is_include_mode, old_do_forward_sources, old_dont_forward_sources); return; } if (is_exclude_mode()) { // // Router State: EXCLUDE (X, Y) // Report Received: TO_EX (A) // New Router State: EXCLUDE (A - Y, Y * A) // Actions: (A - X - Y) = Group Timer // Delete (X - A) // Delete (Y - A) // Send Q(G, A - Y) // Group Timer = GMI // Mld6igmpSourceSet& x = _do_forward_sources; Mld6igmpSourceSet x_copy = x; Mld6igmpSourceSet& y = _dont_forward_sources; const set& a = sources; TimeVal gmi = _mld6igmp_vif.group_membership_interval(); TimeVal gt; _group_timer.time_remaining(gt); set_exclude_mode(); Mld6igmpSourceSet x_minus_a = x - a; // X - A Mld6igmpSourceSet y_minus_a = y - a; // Y - A // XXX: The (X * A) below is needed to preserve the original timers _do_forward_sources = x * a; // X * A _do_forward_sources = _do_forward_sources + a; // A _do_forward_sources = _do_forward_sources - y; // A - Y _dont_forward_sources = y * a; // Y * A Mld6igmpSourceSet a_minus_x_minus_y(*this); a_minus_x_minus_y = _do_forward_sources - x_copy; // A - X - Y a_minus_x_minus_y.set_source_timer(gt); // (A - X - Y) = Group Timer x_minus_a.delete_payload_and_clear(); // Delete (X - A) y_minus_a.delete_payload_and_clear(); // Delete (Y - A) _group_timer = eventloop().new_oneoff_after( gmi, callback(this, &Mld6igmpGroupRecord::group_timer_timeout)); // Send Q(G, A - Y) with _do_forward_sources _mld6igmp_vif.mld6igmp_group_source_query_send( group(), _do_forward_sources.extract_source_addresses(), dummy_error_msg); calculate_forwarding_changes(old_is_include_mode, old_do_forward_sources, old_dont_forward_sources); return; } } /** * Process ALLOW_NEW_SOURCES report. * * @param sources the source addresses. * @param last_reported_host the address of the host that last reported * as member. */ void Mld6igmpGroupRecord::process_allow_new_sources(const set& sources, const IPvX& last_reported_host) { bool old_is_include_mode = is_include_mode(); set old_do_forward_sources = _do_forward_sources.extract_source_addresses(); set old_dont_forward_sources = _dont_forward_sources.extract_source_addresses(); set_last_reported_host(last_reported_host); if (is_include_mode()) { // // Router State: INCLUDE (A) // Report Received: ALLOW (B) // New Router State: INCLUDE (A + B) // Actions: (B) = GMI // Mld6igmpSourceSet& a = _do_forward_sources; const set& b = sources; TimeVal gmi = _mld6igmp_vif.group_membership_interval(); set_include_mode(); _do_forward_sources = a + b; // A + B _do_forward_sources.set_source_timer(b, gmi); // (B) = GMI calculate_forwarding_changes(old_is_include_mode, old_do_forward_sources, old_dont_forward_sources); return; } if (is_exclude_mode()) { // // Router State: EXCLUDE (X, Y) // Report Received: ALLOW (A) // New Router State: EXCLUDE (X + A, Y - A) // Actions: (A) = GMI // Mld6igmpSourceSet& x = _do_forward_sources; Mld6igmpSourceSet& y = _dont_forward_sources; const set& a = sources; TimeVal gmi = _mld6igmp_vif.group_membership_interval(); set_exclude_mode(); // XXX: first transfer (Y * A) from (Y) to (X) Mld6igmpSourceSet y_and_a = y * a; _do_forward_sources = x + y_and_a; _do_forward_sources = x + a; // X + A _dont_forward_sources = y - a; // Y - A _do_forward_sources.set_source_timer(a, gmi); // (A) = GMI calculate_forwarding_changes(old_is_include_mode, old_do_forward_sources, old_dont_forward_sources); return; } } /** * Process BLOCK_OLD_SOURCES report. * * @param sources the source addresses. * @param last_reported_host the address of the host that last reported * as member. */ void Mld6igmpGroupRecord::process_block_old_sources(const set& sources, const IPvX& last_reported_host) { bool old_is_include_mode = is_include_mode(); set old_do_forward_sources = _do_forward_sources.extract_source_addresses(); set old_dont_forward_sources = _dont_forward_sources.extract_source_addresses(); string dummy_error_msg; set_last_reported_host(last_reported_host); if (is_include_mode()) { // // Router State: INCLUDE (A) // Report Received: BLOCK (B) // New Router State: INCLUDE (A) // Actions: Send Q(G, A * B) // Mld6igmpSourceSet& a = _do_forward_sources; const set& b = sources; set_include_mode(); Mld6igmpSourceSet a_and_b = a * b; // Send Q(G, A * B) with a_and_b _mld6igmp_vif.mld6igmp_group_source_query_send( group(), a_and_b.extract_source_addresses(), dummy_error_msg); calculate_forwarding_changes(old_is_include_mode, old_do_forward_sources, old_dont_forward_sources); return; } if (is_exclude_mode()) { // // Router State: EXCLUDE (X, Y) // Report Received: BLOCK (A) // New Router State: EXCLUDE (X + (A - Y), Y) // Actions: (A - X - Y) = Group Timer // Send Q(G, A - Y) // Mld6igmpSourceSet& x = _do_forward_sources; Mld6igmpSourceSet x_copy = x; Mld6igmpSourceSet& y = _dont_forward_sources; const set& a = sources; TimeVal gt; _group_timer.time_remaining(gt); set_exclude_mode(); Mld6igmpSourceSet a_minus_y(*this); a_minus_y = a_minus_y + a; // A a_minus_y = a_minus_y - y; // A - Y _do_forward_sources = x + a_minus_y; // X + (A - Y) Mld6igmpSourceSet a_minus_x_minus_y = _do_forward_sources; // (X+(A-Y)) a_minus_x_minus_y = a_minus_x_minus_y - x_copy; // A - X - Y a_minus_x_minus_y = a_minus_x_minus_y - y; // A - X - Y Mld6igmpSourceSet y_minus_a = y - a; // Y - A a_minus_x_minus_y.set_source_timer(gt); // (A - X - Y) = Group Timer // Send Q(G, A - Y) with a_minus_y _mld6igmp_vif.mld6igmp_group_source_query_send( group(), a_minus_y.extract_source_addresses(), dummy_error_msg); calculate_forwarding_changes(old_is_include_mode, old_do_forward_sources, old_dont_forward_sources); return; } } /** * Lower the group timer. * * @param timeval the timeout interval the timer should be lowered to. */ void Mld6igmpGroupRecord::lower_group_timer(const TimeVal& timeval) { TimeVal timeval_remaining; // // Lower the group timer // _group_timer.time_remaining(timeval_remaining); if (timeval < timeval_remaining) { _group_timer = eventloop().new_oneoff_after( timeval, callback(this, &Mld6igmpGroupRecord::group_timer_timeout)); } } /** * Lower the source timer for a set of sources. * * @param sources the source addresses. * @param timeval the timeout interval the timer should be lowered to. */ void Mld6igmpGroupRecord::lower_source_timer(const set& sources, const TimeVal& timeval) { // // Lower the source timer // _do_forward_sources.lower_source_timer(sources, timeval); } /** * Take the appropriate actions for a source that has expired. * * @param source_record the source record that has expired. */ void Mld6igmpGroupRecord::source_expired(Mld6igmpSourceRecord* source_record) { Mld6igmpSourceSet::iterator iter; // Erase the source record from the appropriate source set iter = _do_forward_sources.find(source_record->source()); XLOG_ASSERT(iter != _do_forward_sources.end()); _do_forward_sources.erase(iter); if (is_include_mode()) { // notify routing (-) mld6igmp_vif().join_prune_notify_routing(source_record->source(), group(), ACTION_PRUNE); // Delete the source record delete source_record; // If no more source records, then delete the group record if (_do_forward_sources.empty()) { XLOG_ASSERT(_dont_forward_sources.empty()); mld6igmp_vif().group_records().erase(group()); delete this; } return; } if (is_exclude_mode()) { // notify routing (-) // // XXX: Note that we send a PRUNE twice: the first one to remove the // original JOIN for the source, and the second one to create // PRUNE state for the source. // mld6igmp_vif().join_prune_notify_routing(source_record->source(), group(), ACTION_PRUNE); mld6igmp_vif().join_prune_notify_routing(source_record->source(), group(), ACTION_PRUNE); // Do not remove record, but add it to the appropriate set _dont_forward_sources.insert(make_pair(source_record->source(), source_record)); return; } } /** * Get the number of seconds until the group timer expires. * * @return the number of seconds until the group timer expires. */ uint32_t Mld6igmpGroupRecord::timeout_sec() const { TimeVal tv; _group_timer.time_remaining(tv); return (tv.sec()); } /** * Timeout: the group timer has expired. */ void Mld6igmpGroupRecord::group_timer_timeout() { if (is_include_mode()) { // XXX: Nothing to do when in INCLUDE mode. return; } if (is_exclude_mode()) { // Clear the state for all excluded sources Mld6igmpSourceSet::const_iterator source_iter; for (source_iter = this->dont_forward_sources().begin(); source_iter != this->dont_forward_sources().end(); ++source_iter) { const Mld6igmpSourceRecord *source_record = source_iter->second; mld6igmp_vif().join_prune_notify_routing(source_record->source(), this->group(), ACTION_JOIN); } // Delete the source records with zero timers _dont_forward_sources.delete_payload_and_clear(); // notify routing (-) mld6igmp_vif().join_prune_notify_routing(IPvX::ZERO(family()), group(), ACTION_PRUNE); if (! _do_forward_sources.empty()) { // Transition to INCLUDE mode set_include_mode(); return; } // // No sources with running source timers. // Delete the group record and return immediately. // mld6igmp_vif().group_records().erase(group()); delete this; return; } } /** * Schedule periodic Group-Specific and Group-and-Source-Specific Query * retransmission. * * If the sources list is empty, we schedule Group-Specific Query, * otherwise we schedule Group-and-Source-Specific Query. * * @param sources the source addresses. */ void Mld6igmpGroupRecord::schedule_periodic_group_query(const set& sources) { Mld6igmpSourceSet::iterator source_iter; size_t count = _mld6igmp_vif.last_member_query_count() - 1; // // Reset the count for query retransmission for all "don't forward" sources // for (source_iter = _dont_forward_sources.begin(); source_iter != _dont_forward_sources.end(); ++source_iter) { Mld6igmpSourceRecord* source_record = source_iter->second; source_record->set_query_retransmission_count(0); } if (_mld6igmp_vif.last_member_query_count() == 0) return; if (_mld6igmp_vif.query_last_member_interval().get() == TimeVal::ZERO()) return; // // Set the count for query retransmissions // if (sources.empty()) { // // Set the count for Group-Specific Query retransmission // _query_retransmission_count = count; } else { // // Set the count for Group-and-Source-Specific Query retransmission // set::const_iterator ipvx_iter; for (ipvx_iter = sources.begin(); ipvx_iter != sources.end(); ++ipvx_iter) { const IPvX& ipvx = *ipvx_iter; Mld6igmpSourceRecord* source_record = find_do_forward_source(ipvx); if (source_record == NULL) continue; source_record->set_query_retransmission_count(count); } } // // Set the periodic timer for SSM Group-Specific and // Group-and-Source-Specific Queries. // // Note that we set the timer only if it wasn't running already. // if (! _group_query_timer.scheduled()) { _group_query_timer = eventloop().new_periodic( _mld6igmp_vif.query_last_member_interval().get(), callback(this, &Mld6igmpGroupRecord::group_query_periodic_timeout)); } } /** * Periodic timeout: time to send the next Group-Specific and * Group-and-Source-Specific Queries. * * @return true if the timer should be scheduled again, otherwise false. */ bool Mld6igmpGroupRecord::group_query_periodic_timeout() { string dummy_error_msg; bool s_flag = false; set no_sources; // XXX: empty set set sources_with_s_flag; set sources_without_s_flag; Mld6igmpSourceSet::iterator source_iter; TimeVal max_resp_time = mld6igmp_vif().query_last_member_interval().get(); bool do_send_group_query = true; // // XXX: Don't send Group-Specific or Group-and-Source-Specific Queries // for entries that are in IGMPv1 mode. // if (is_igmpv1_mode()) return (false); // // XXX: The IGMPv3/MLDv2 spec doesn't say what to do here if we changed // from a Querier to a non-Querier. // However, the IGMPv2 spec says that Querier to non-Querier transitions // are to be ignored (see the bottom part of Section 3 of RFC 2236). // Hence, for this reason and for robustness purpose we send the Query // messages without taking into account any Querier to non-Querier // transitions. // // // Send the Group-Specific Query message // if (_query_retransmission_count == 0) { do_send_group_query = false; // No more queries to send } else { _query_retransmission_count--; // // Calculate the group-specific "Suppress Router-Side Processing" bit // TimeVal timeval_remaining; group_timer().time_remaining(timeval_remaining); if (timeval_remaining > _mld6igmp_vif.last_member_query_time()) s_flag = true; _mld6igmp_vif.mld6igmp_query_send(mld6igmp_vif().primary_addr(), group(), max_resp_time, group(), no_sources, s_flag, dummy_error_msg); } // // Select all the sources that should be queried, and add them to // the appropriate set. // for (source_iter = _do_forward_sources.begin(); source_iter != _do_forward_sources.end(); ++source_iter) { Mld6igmpSourceRecord* source_record = source_iter->second; size_t count = source_record->query_retransmission_count(); bool s_flag = false; if (count == 0) continue; source_record->set_query_retransmission_count(count - 1); // // Calculate the "Suppress Router-Side Processing" bit // TimeVal timeval_remaining; source_record->source_timer().time_remaining(timeval_remaining); if (timeval_remaining > _mld6igmp_vif.last_member_query_time()) s_flag = true; if (s_flag) sources_with_s_flag.insert(source_record->source()); else sources_without_s_flag.insert(source_record->source()); } // // Send the Group-and-Source-Specific Query messages // if ((! sources_with_s_flag.empty()) && (! do_send_group_query)) { // // According to RFC 3376, Section 6.6.3.2: // "If a group specific query is scheduled to be transmitted at the // same time as a group and source specific query for the same group, // then transmission of the group and source specific message with the // "Suppress Router-Side Processing" bit set may be suppressed." // // The corresponding text from RFC 3810, Section 7.6.3.2 is similar. // _mld6igmp_vif.mld6igmp_query_send(mld6igmp_vif().primary_addr(), group(), max_resp_time, group(), sources_with_s_flag, true, // XXX: set the s_flag dummy_error_msg); } if (! sources_without_s_flag.empty()) { _mld6igmp_vif.mld6igmp_query_send(mld6igmp_vif().primary_addr(), group(), max_resp_time, group(), sources_without_s_flag, false, // XXX: reset the s_flag dummy_error_msg); } if (sources_with_s_flag.empty() && sources_without_s_flag.empty() && (! do_send_group_query)) { return (false); // No more queries to send } return (true); // Schedule the next timeout } /** * Record that an older Membership report message has been received. * * @param message_version the corresponding protocol version of the * received message. */ void Mld6igmpGroupRecord::received_older_membership_report(int message_version) { TimeVal timeval = _mld6igmp_vif.older_version_host_present_interval(); if (_mld6igmp_vif.proto_is_igmp()) { switch (message_version) { case IGMP_V1: if (_mld6igmp_vif.is_igmpv2_mode()) { // // XXX: The value specified in RFC 2236 is different from // the value specified in RFC 3376. // timeval = _mld6igmp_vif.group_membership_interval(); } _igmpv1_host_present_timer = eventloop().new_oneoff_after( timeval, callback(this, &Mld6igmpGroupRecord::older_version_host_present_timer_timeout)); break; case IGMP_V2: _igmpv2_mldv1_host_present_timer = eventloop().new_oneoff_after( timeval, callback(this, &Mld6igmpGroupRecord::older_version_host_present_timer_timeout)); break; default: break; } } if (_mld6igmp_vif.proto_is_mld6()) { switch (message_version) { case MLD_V1: _igmpv2_mldv1_host_present_timer = eventloop().new_oneoff_after( timeval, callback(this, &Mld6igmpGroupRecord::older_version_host_present_timer_timeout)); break; default: break; } } } void Mld6igmpGroupRecord::older_version_host_present_timer_timeout() { // XXX: nothing to do } /** * Test if the group is running in IGMPv1 mode. * * @return true if the group is running in IGMPv1 mode, otherwise false. */ bool Mld6igmpGroupRecord::is_igmpv1_mode() const { if (! _mld6igmp_vif.proto_is_igmp()) return (false); if (_mld6igmp_vif.is_igmpv1_mode()) return (true); // XXX: explicitly configured in IGMPv1 mode return (_igmpv1_host_present_timer.scheduled()); } /** * Test if the group is running in IGMPv2 mode. * * @return true if the group is running in IGMPv2 mode, otherwise false. */ bool Mld6igmpGroupRecord::is_igmpv2_mode() const { if (! _mld6igmp_vif.proto_is_igmp()) return (false); if (is_igmpv1_mode()) return (false); return (_igmpv2_mldv1_host_present_timer.scheduled()); } /** * Test if the group is running in IGMPv3 mode. * * @return true if the group is running in IGMPv3 mode, otherwise false. */ bool Mld6igmpGroupRecord::is_igmpv3_mode() const { if (! _mld6igmp_vif.proto_is_igmp()) return (false); if (is_igmpv1_mode() || is_igmpv2_mode()) return (false); return (true); } /** * Test if the group is running in MLDv1 mode. * * @return true if the group is running in MLDv1 mode, otherwise false. */ bool Mld6igmpGroupRecord::is_mldv1_mode() const { if (! _mld6igmp_vif.proto_is_mld6()) return (false); if (_mld6igmp_vif.is_mldv1_mode()) return (true); // XXX: explicitly configured in MLDv1 mode return (_igmpv2_mldv1_host_present_timer.scheduled()); } /** * Test if the group is running in MLDv2 mode. * * @return true if the group is running in MLDv2 mode, otherwise false. */ bool Mld6igmpGroupRecord::is_mldv2_mode() const { if (! _mld6igmp_vif.proto_is_mld6()) return (false); if (is_mldv1_mode()) return (false); return (true); } /** * Calculate the forwarding changes and notify the interested parties. * * @param old_is_include mode if true, the old filter mode was INCLUDE, * otherwise was EXCLUDE. * @param old_do_forward_sources the old set of sources to forward. * @param old_dont_forward_sources the old set of sources not to forward. */ void Mld6igmpGroupRecord::calculate_forwarding_changes( bool old_is_include_mode, const set& old_do_forward_sources, const set& old_dont_forward_sources) const { bool new_is_include_mode = is_include_mode(); set new_do_forward_sources = _do_forward_sources.extract_source_addresses(); set new_dont_forward_sources = _dont_forward_sources.extract_source_addresses(); set::const_iterator iter; if (old_is_include_mode) { if (new_is_include_mode) { // INCLUDE -> INCLUDE XLOG_ASSERT(old_dont_forward_sources.empty()); XLOG_ASSERT(new_dont_forward_sources.empty()); // Join all new sources that are to be forwarded for (iter = new_do_forward_sources.begin(); iter != new_do_forward_sources.end(); ++iter) { const IPvX& ipvx = *iter; if (old_do_forward_sources.find(ipvx) == old_do_forward_sources.end()) { mld6igmp_vif().join_prune_notify_routing(ipvx, group(), ACTION_JOIN); } } // Prune all old sources that were forwarded for (iter = old_do_forward_sources.begin(); iter != old_do_forward_sources.end(); ++iter) { const IPvX& ipvx = *iter; if (new_do_forward_sources.find(ipvx) == new_do_forward_sources.end()) { mld6igmp_vif().join_prune_notify_routing(ipvx, group(), ACTION_PRUNE); } } } if (! new_is_include_mode) { // INCLUDE -> EXCLUDE XLOG_ASSERT(old_dont_forward_sources.empty()); // Prune the old sources that were forwarded for (iter = old_do_forward_sources.begin(); iter != old_do_forward_sources.end(); ++iter) { const IPvX& ipvx = *iter; if (new_do_forward_sources.find(ipvx) == new_do_forward_sources.end()) { mld6igmp_vif().join_prune_notify_routing(ipvx, group(), ACTION_PRUNE); } } // Join the group itself mld6igmp_vif().join_prune_notify_routing(IPvX::ZERO(family()), group(), ACTION_JOIN); // Join all new sources that are to be forwarded for (iter = new_do_forward_sources.begin(); iter != new_do_forward_sources.end(); ++iter) { const IPvX& ipvx = *iter; if (old_do_forward_sources.find(ipvx) == old_do_forward_sources.end()) { mld6igmp_vif().join_prune_notify_routing(ipvx, group(), ACTION_JOIN); } } // Prune all new sources that are not to be forwarded for (iter = new_dont_forward_sources.begin(); iter != new_dont_forward_sources.end(); ++iter) { const IPvX& ipvx = *iter; if (old_dont_forward_sources.find(ipvx) == old_dont_forward_sources.end()) { mld6igmp_vif().join_prune_notify_routing(ipvx, group(), ACTION_PRUNE); } } } } if (! old_is_include_mode) { if (new_is_include_mode) { // EXCLUDE -> INCLUDE XLOG_ASSERT(new_dont_forward_sources.empty()); // Join all old sources that were not to be forwarded for (iter = old_dont_forward_sources.begin(); iter != old_dont_forward_sources.end(); ++iter) { const IPvX& ipvx = *iter; if (new_dont_forward_sources.find(ipvx) == new_dont_forward_sources.end()) { mld6igmp_vif().join_prune_notify_routing(ipvx, group(), ACTION_JOIN); } } // Prune the group itself mld6igmp_vif().join_prune_notify_routing(IPvX::ZERO(family()), group(), ACTION_PRUNE); // Join all new sources that are to be forwarded for (iter = new_do_forward_sources.begin(); iter != new_do_forward_sources.end(); ++iter) { const IPvX& ipvx = *iter; if (old_do_forward_sources.find(ipvx) == old_do_forward_sources.end()) { mld6igmp_vif().join_prune_notify_routing(ipvx, group(), ACTION_JOIN); } } } if (! new_is_include_mode) { // EXCLUDE -> EXCLUDE // Join all new sources that are to be forwarded for (iter = new_do_forward_sources.begin(); iter != new_do_forward_sources.end(); ++iter) { const IPvX& ipvx = *iter; if (old_do_forward_sources.find(ipvx) == old_do_forward_sources.end()) { mld6igmp_vif().join_prune_notify_routing(ipvx, group(), ACTION_JOIN); } } // Prune all old sources that were forwarded for (iter = old_do_forward_sources.begin(); iter != old_do_forward_sources.end(); ++iter) { const IPvX& ipvx = *iter; if (new_do_forward_sources.find(ipvx) == new_do_forward_sources.end()) { mld6igmp_vif().join_prune_notify_routing(ipvx, group(), ACTION_PRUNE); } } // Join all old sources that were not to be forwarded for (iter = old_dont_forward_sources.begin(); iter != old_dont_forward_sources.end(); ++iter) { const IPvX& ipvx = *iter; if (new_dont_forward_sources.find(ipvx) == new_dont_forward_sources.end()) { mld6igmp_vif().join_prune_notify_routing(ipvx, group(), ACTION_JOIN); } } // Prune all new sources that are not to be forwarded for (iter = new_dont_forward_sources.begin(); iter != new_dont_forward_sources.end(); ++iter) { const IPvX& ipvx = *iter; if (old_dont_forward_sources.find(ipvx) == old_dont_forward_sources.end()) { mld6igmp_vif().join_prune_notify_routing(ipvx, group(), ACTION_PRUNE); } } } } } /** * Constructor for a given vif. * * @param mld6igmp_vif the interface this set belongs to. */ Mld6igmpGroupSet::Mld6igmpGroupSet(Mld6igmpVif& mld6igmp_vif) : _mld6igmp_vif(mld6igmp_vif) { } /** * Destructor. */ Mld6igmpGroupSet::~Mld6igmpGroupSet() { // XXX: don't delete the payload, because it might be used elsewhere } /** * Find a group record. * * @param group the group address. * @return the corresponding group record (@ref Mld6igmpGroupRecord) * if found, otherwise NULL. */ Mld6igmpGroupRecord* Mld6igmpGroupSet::find_group_record(const IPvX& group) { Mld6igmpGroupSet::iterator iter = this->find(group); if (iter != this->end()) return (iter->second); return (NULL); } /** * Delete the payload of the set, and clear the set itself. */ void Mld6igmpGroupSet::delete_payload_and_clear() { Mld6igmpGroupSet::iterator iter; // // Delete the payload of the set // for (iter = this->begin(); iter != this->end(); ++iter) { Mld6igmpGroupRecord* group_record = iter->second; delete group_record; } // // Clear the set itself // this->clear(); } /** * Process MODE_IS_INCLUDE report. * * @param group the group address. * @param sources the source addresses. * @param last_reported_host the address of the host that last reported * as member. */ void Mld6igmpGroupSet::process_mode_is_include(const IPvX& group, const set& sources, const IPvX& last_reported_host) { Mld6igmpGroupSet::iterator iter; Mld6igmpGroupRecord* group_record = NULL; iter = this->find(group); if (iter != this->end()) { group_record = iter->second; } else { group_record = new Mld6igmpGroupRecord(_mld6igmp_vif, group); this->insert(make_pair(group, group_record)); } XLOG_ASSERT(group_record != NULL); group_record->process_mode_is_include(sources, last_reported_host); // // If the group record is not used anymore, then delete it // if (group_record->is_unused()) { this->erase(group); delete group_record; } } /** * Process MODE_IS_EXCLUDE report. * * @param group the group address. * @param sources the source addresses. * @param last_reported_host the address of the host that last reported * as member. */ void Mld6igmpGroupSet::process_mode_is_exclude(const IPvX& group, const set& sources, const IPvX& last_reported_host) { Mld6igmpGroupSet::iterator iter; Mld6igmpGroupRecord* group_record = NULL; iter = this->find(group); if (iter != this->end()) { group_record = iter->second; } else { group_record = new Mld6igmpGroupRecord(_mld6igmp_vif, group); this->insert(make_pair(group, group_record)); } XLOG_ASSERT(group_record != NULL); group_record->process_mode_is_exclude(sources, last_reported_host); // // If the group record is not used anymore, then delete it // if (group_record->is_unused()) { this->erase(group); delete group_record; } } /** * Process CHANGE_TO_INCLUDE_MODE report. * * @param group the group address. * @param sources the source addresses. * @param last_reported_host the address of the host that last reported * as member. */ void Mld6igmpGroupSet::process_change_to_include_mode(const IPvX& group, const set& sources, const IPvX& last_reported_host) { Mld6igmpGroupSet::iterator iter; Mld6igmpGroupRecord* group_record = NULL; iter = this->find(group); if (iter != this->end()) { group_record = iter->second; } else { group_record = new Mld6igmpGroupRecord(_mld6igmp_vif, group); this->insert(make_pair(group, group_record)); } XLOG_ASSERT(group_record != NULL); if (_mld6igmp_vif.is_igmpv1_mode(group_record)) { // // XXX: Ignore CHANGE_TO_INCLUDE_MODE messages when in // IGMPv1 mode. // } else { group_record->process_change_to_include_mode(sources, last_reported_host); } // // If the group record is not used anymore, then delete it // if (group_record->is_unused()) { this->erase(group); delete group_record; } } /** * Process CHANGE_TO_EXCLUDE_MODE report. * * @param group the group address. * @param sources the source addresses. * @param last_reported_host the address of the host that last reported * as member. */ void Mld6igmpGroupSet::process_change_to_exclude_mode(const IPvX& group, const set& sources, const IPvX& last_reported_host) { Mld6igmpGroupSet::iterator iter; Mld6igmpGroupRecord* group_record = NULL; iter = this->find(group); if (iter != this->end()) { group_record = iter->second; } else { group_record = new Mld6igmpGroupRecord(_mld6igmp_vif, group); this->insert(make_pair(group, group_record)); } XLOG_ASSERT(group_record != NULL); if (_mld6igmp_vif.is_igmpv1_mode(group_record) || _mld6igmp_vif.is_igmpv2_mode(group_record) || _mld6igmp_vif.is_mldv1_mode(group_record)) { // // XXX: Ignore the source list in the CHANGE_TO_EXCLUDE_MODE // messages when in IGMPv1, IGMPv2, or MLDv1 mode. // set no_sources; // XXX: empty set group_record->process_change_to_exclude_mode(no_sources, last_reported_host); } else { group_record->process_change_to_exclude_mode(sources, last_reported_host); } // // If the group record is not used anymore, then delete it // if (group_record->is_unused()) { this->erase(group); delete group_record; } } /** * Process ALLOW_NEW_SOURCES report. * * @param group the group address. * @param sources the source addresses. * @param last_reported_host the address of the host that last reported * as member. */ void Mld6igmpGroupSet::process_allow_new_sources(const IPvX& group, const set& sources, const IPvX& last_reported_host) { Mld6igmpGroupSet::iterator iter; Mld6igmpGroupRecord* group_record = NULL; iter = this->find(group); if (iter != this->end()) { group_record = iter->second; } else { group_record = new Mld6igmpGroupRecord(_mld6igmp_vif, group); this->insert(make_pair(group, group_record)); } XLOG_ASSERT(group_record != NULL); group_record->process_allow_new_sources(sources, last_reported_host); // // If the group record is not used anymore, then delete it // if (group_record->is_unused()) { this->erase(group); delete group_record; } } /** * Process BLOCK_OLD_SOURCES report. * * @param group the group address. * @param sources the source addresses. * @param last_reported_host the address of the host that last reported * as member. */ void Mld6igmpGroupSet::process_block_old_sources(const IPvX& group, const set& sources, const IPvX& last_reported_host) { Mld6igmpGroupSet::iterator iter; Mld6igmpGroupRecord* group_record = NULL; iter = this->find(group); if (iter != this->end()) { group_record = iter->second; } else { group_record = new Mld6igmpGroupRecord(_mld6igmp_vif, group); this->insert(make_pair(group, group_record)); } XLOG_ASSERT(group_record != NULL); if (_mld6igmp_vif.is_igmpv1_mode(group_record) || _mld6igmp_vif.is_igmpv2_mode(group_record) || _mld6igmp_vif.is_mldv1_mode(group_record)) { // // XXX: Ignore BLOCK_OLD_SOURCES messages when in // IGMPv1, IGMPv2, or MLDv1 mode. // } else { group_record->process_block_old_sources(sources, last_reported_host); } // // If the group record is not used anymore, then delete it // if (group_record->is_unused()) { this->erase(group); delete group_record; } } /** * Lower the group timer. * * @param group the group address. * @param timeval the timeout interval the timer should be lowered to. */ void Mld6igmpGroupSet::lower_group_timer(const IPvX& group, const TimeVal& timeval) { Mld6igmpGroupSet::iterator iter; iter = this->find(group); if (iter != this->end()) { Mld6igmpGroupRecord* group_record = iter->second; group_record->lower_group_timer(timeval); } } /** * Lower the source timer for a set of sources. * * @param group the group address. * @param sources the source addresses. * @param timeval the timeout interval the timer should be lowered to. */ void Mld6igmpGroupSet::lower_source_timer(const IPvX& group, const set& sources, const TimeVal& timeval) { Mld6igmpGroupSet::iterator iter; iter = this->find(group); if (iter != this->end()) { Mld6igmpGroupRecord* group_record = iter->second; group_record->lower_source_timer(sources, timeval); } } xorp/mld6igmp/xorp_igmp.cc0000664000076400007640000001214211540224230015703 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // XORP MLD/IGMP module implementation. // #include "mld6igmp_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/callback.hh" #include "libxorp/eventloop.hh" #include "libxorp/exceptions.hh" #include "xrl_mld6igmp_node.hh" #ifdef HAVE_GETOPT_H #include #endif // // Local functions prototypes // static void usage(const char *argv0, int exit_value); /** * usage: * @argv0: Argument 0 when the program was called (the program name itself). * @exit_value: The exit value of the program. * * Print the program usage. * If @exit_value is 0, the usage will be printed to the standart output, * otherwise to the standart error. **/ static void usage(const char *argv0, int exit_value) { FILE *output; const char *progname = strrchr(argv0, '/'); if (progname != NULL) progname++; // Skip the last '/' if (progname == NULL) progname = argv0; // // If the usage is printed because of error, output to stderr, otherwise // output to stdout. // if (exit_value == 0) output = stdout; else output = stderr; fprintf(output, "Usage: %s [-F [:]]\n", progname); fprintf(output, " -F [:] : finder hostname and port\n"); fprintf(output, " -h : usage (this message)\n"); fprintf(output, "\n"); fprintf(output, "Program name: %s\n", progname); fprintf(output, "Module name: %s\n", XORP_MODULE_NAME); fprintf(output, "Module version: %s\n", XORP_MODULE_VERSION); exit (exit_value); // NOTREACHED } static void mld6igmp_main(const string& finder_hostname, uint16_t finder_port) { setup_dflt_sighandlers(); // // Init stuff // EventLoop eventloop; // // MLD6IGMP node // XrlMld6igmpNode xrl_mld6igmp_node4(AF_INET, XORP_MODULE_MLD6IGMP, eventloop, xorp_module_name(AF_INET, XORP_MODULE_MLD6IGMP), finder_hostname, finder_port, "finder", xorp_module_name(AF_INET, XORP_MODULE_FEA), xorp_module_name(AF_INET, XORP_MODULE_MFEA)); wait_until_xrl_router_is_ready(eventloop, xrl_mld6igmp_node4.xrl_router()); // // Startup // xrl_mld6igmp_node4.enable_mld6igmp(); // xrl_mld6igmp_node4.startup(); xrl_mld6igmp_node4.enable_cli(); xrl_mld6igmp_node4.start_cli(); // // Main loop // while (xorp_do_run && !xrl_mld6igmp_node4.is_done()) { eventloop.run(); } while (xrl_mld6igmp_node4.xrl_router().pending()) { eventloop.run(); } } int main(int argc, char *argv[]) { int ch; string::size_type idx; const char *argv0 = argv[0]; string finder_hostname = FinderConstants::FINDER_DEFAULT_HOST().str(); uint16_t finder_port = FinderConstants::FINDER_DEFAULT_PORT(); // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); // // Get the program options // while ((ch = getopt(argc, argv, "F:h")) != -1) { switch (ch) { case 'F': // Finder hostname and port finder_hostname = optarg; idx = finder_hostname.find(':'); if (idx != string::npos) { if (idx + 1 >= finder_hostname.length()) { // No port number usage(argv0, 1); // NOTREACHED } char* p = &finder_hostname[idx + 1]; finder_port = static_cast(atoi(p)); finder_hostname = finder_hostname.substr(0, idx); } break; case 'h': case '?': // Help usage(argv0, 0); // NOTREACHED break; default: usage(argv0, 1); // NOTREACHED break; } } argc -= optind; argv += optind; if (argc != 0) { usage(argv0, 1); // NOTREACHED } // // Run everything // try { mld6igmp_main(finder_hostname, finder_port); } catch(...) { xorp_catch_standard_exceptions(); } // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); exit (0); } xorp/description.txt0000644000076400007640000000015111665020734014743 0ustar greearbgreearbXORP modular router with improvements to better support LANforge. More information: http://www.xorp.org xorp/bgp/0000775000076400007640000000000011633743677012451 5ustar greearbgreearbxorp/bgp/process_watch.cc0000664000076400007640000001104511540225521015601 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #include "bgp_module.h" #include "libxorp/exceptions.hh" #include "xrl/interfaces/finder_event_notifier_xif.hh" #include "exceptions.hh" #include "process_watch.hh" ProcessWatch::ProcessWatch(XrlStdRouter *xrl_router, EventLoop& eventloop, const char *bgp_mib_name, TerminateCallback cb) : _eventloop(eventloop), _shutdown(cb), _fea(false), _rib(false) { /* ** Register interest in the fea and rib and snmp trap handler. */ XrlFinderEventNotifierV0p1Client finder(xrl_router); finder.send_register_class_event_interest("finder", xrl_router->instance_name(), "fea", callback(this, &ProcessWatch::interest_callback)); finder.send_register_class_event_interest("finder", xrl_router->instance_name(), "rib", callback(this, &ProcessWatch::interest_callback)); finder.send_register_class_event_interest("finder", xrl_router->instance_name(), bgp_mib_name, callback(this, &ProcessWatch::interest_callback)); } void ProcessWatch::interest_callback(const XrlError& error) { debug_msg("callback %s\n", error.str().c_str()); if(XrlError::OKAY() != error.error_code()) { XLOG_FATAL("callback: %s", error.str().c_str()); } } void ProcessWatch::birth(const string& target_class, const string& target_instance) { debug_msg("birth: %s %s\n", target_class.c_str(), target_instance.c_str()); if(target_class == "fea" && false == _fea) { _fea = true; _fea_instance = target_instance; } else if(target_class == "rib" && false == _rib) { _rib = true; _rib_instance = target_instance; } else add_target(target_class, target_instance); } void ProcessWatch::death(const string& target_class, const string& target_instance) { debug_msg("death: %s %s\n", target_class.c_str(), target_instance.c_str()); if (_fea_instance == target_instance) { XLOG_ERROR("The fea died"); ::exit(-1); } else if (_rib_instance == target_instance) { XLOG_ERROR("The rib died"); start_kill_timer(); _shutdown->dispatch(); } else remove_target(target_class, target_instance); } void ProcessWatch::finder_death(const char *file, const int lineno) { XLOG_ERROR("The finder has died BGP process exiting called from %s:%d", file, lineno); UNUSED(file); UNUSED(lineno); start_kill_timer(); xorp_throw(NoFinder, ""); } void ProcessWatch::start_kill_timer() { _shutdown_timer = _eventloop. new_oneoff_after_ms(1000 * 10, ::callback(::exit, -1)); } bool ProcessWatch::ready() const { return _fea && _rib; } bool ProcessWatch::target_exists(const string& target) const { debug_msg("target_exists: %s\n", target.c_str()); list::const_iterator i; for(i = _processes.begin(); i != _processes.end(); i++) if(target == i->_target_class) { debug_msg("target: %s found\n", target.c_str()); return true; } debug_msg("target %s not found\n", target.c_str()); return false; } void ProcessWatch::add_target(const string& target_class, const string& target_instance) { debug_msg("add_target: %s %s\n", target_class.c_str(), target_instance.c_str()); _processes.push_back(Process(target_class, target_instance)); } void ProcessWatch::remove_target(const string& target_class, const string& target_instance) { debug_msg("remove_target: %s %s\n", target_class.c_str(), target_instance.c_str()); list::iterator i; for(i = _processes.begin(); i != _processes.end(); i++) if(target_class == i->_target_class && target_instance == i->_target_instance) { _processes.erase(i); return; } XLOG_FATAL("unknown target %s %s", target_class.c_str(), target_instance.c_str()); } xorp/bgp/route_table_ribout.cc0000664000076400007640000003502011540225521016625 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #include "route_table_ribout.hh" #include "peer_handler.hh" template RibOutTable::RibOutTable(string table_name, Safi safi, BGPRouteTable *init_parent, PeerHandler *peer) : BGPRouteTable("RibOutTable-" + table_name, safi) { this->_parent = init_parent; _peer = peer; _peer_busy = false; _peer_is_up = false; } template RibOutTable::~RibOutTable() { print_queue(_queue); typename list*>::iterator i; i = _queue.begin(); while (i != _queue.end()) { delete (*i); ++i; } } template void RibOutTable::print_queue(const list*>& queue) const { #ifdef DEBUG_LOGGING debug_msg("Output Queue:\n"); typename list*>::const_iterator i; i = queue.begin(); while (i != queue.end()) { string s; switch ((*i)->op()) { case RTQUEUE_OP_ADD: s = "ADD"; break; case RTQUEUE_OP_DELETE: s = "DEL"; break; case RTQUEUE_OP_REPLACE_OLD: s = "R_O"; break; case RTQUEUE_OP_REPLACE_NEW: s = "R_N"; break; case RTQUEUE_OP_PUSH: break; } debug_msg(" Entry: %s (%s)\n", (*i)->net().str().c_str(), s.c_str()); ++i; } debug_msg("<<<<<< end of queue\n"); #else UNUSED(queue); #endif } template int RibOutTable::add_route(InternalMessage &rtmsg, BGPRouteTable *caller) { debug_msg("\n %s\n caller: %s\n rtmsg: %p route: %p\n%s\n", this->tablename().c_str(), caller->tablename().c_str(), &rtmsg, rtmsg.route(), rtmsg.str().c_str()); print_queue(_queue); XLOG_ASSERT(caller == this->_parent); // check the queue to see if there's a matching delete - if so we // can replace the delete with an add. const RouteQueueEntry* queued_entry = NULL; typename list*>::iterator i; for (i = _queue.begin(); i != _queue.end(); i++) { if ( (*i)->net() == rtmsg.net()) { debug_msg("old entry %s matches new entry %s\n", (*i)->net().str().c_str(), rtmsg.net().str().c_str()); queued_entry = *i; break; } } RouteQueueEntry* entry; if (queued_entry == NULL) { // just add the route to the queue. rtmsg.attributes()->lock(); entry = new RouteQueueEntry(rtmsg.route(), rtmsg.attributes(), RTQUEUE_OP_ADD); entry->set_origin_peer(rtmsg.origin_peer()); _queue.push_back(entry); } else if (queued_entry->op() == RTQUEUE_OP_DELETE) { // There was a delete in the queue. The delete must become a replace. debug_msg("removing delete entry from output queue to become replace\n"); _queue.erase(i); FPAListRef old_fpa_list = queued_entry->attributes(); entry = new RouteQueueEntry(queued_entry->route(), old_fpa_list, (RouteQueueOp)RTQUEUE_OP_REPLACE_OLD); entry->set_origin_peer(queued_entry->origin_peer()); _queue.push_back(entry); rtmsg.attributes()->lock(); entry = new RouteQueueEntry(rtmsg.route(), rtmsg.attributes(), RTQUEUE_OP_REPLACE_NEW); entry->set_origin_peer(rtmsg.origin_peer()); _queue.push_back(entry); delete queued_entry; } else if (queued_entry->op() == RTQUEUE_OP_REPLACE_OLD) { // There was a replace in the queue. The new part of the // replace must be updated. queued_entry->attributes()->unlock(); i++; queued_entry = *i; XLOG_ASSERT(queued_entry->op() == RTQUEUE_OP_REPLACE_NEW); rtmsg.attributes()->lock(); entry = new RouteQueueEntry(rtmsg.route(), rtmsg.attributes(), RTQUEUE_OP_REPLACE_NEW); entry->set_origin_peer(rtmsg.origin_peer()); _queue.insert(i, entry); _queue.erase(i); delete queued_entry; } else if (queued_entry->op() == RTQUEUE_OP_ADD) { XLOG_FATAL("RibOut: add_route called for a subnet already in the output queue\n"); } // handle push if (rtmsg.push()) push(this->_parent); debug_msg("After-->:\n"); print_queue(_queue); debug_msg("<--After\n"); return ADD_USED; } template int RibOutTable::replace_route(InternalMessage &old_rtmsg, InternalMessage &new_rtmsg, BGPRouteTable *caller) { debug_msg("%s::replace_route %p %p\n", this->tablename().c_str(), &old_rtmsg, &new_rtmsg); XLOG_ASSERT(old_rtmsg.push() == false); delete_route(old_rtmsg, caller); return add_route(new_rtmsg, caller); } template int RibOutTable::delete_route(InternalMessage &rtmsg, BGPRouteTable *caller) { debug_msg("\n %s\n caller: %s\n rtmsg: %p route: %p\n%s\n", this->tablename().c_str(), caller->tablename().c_str(), &rtmsg, rtmsg.route(), rtmsg.str().c_str()); print_queue(_queue); XLOG_ASSERT(caller == this->_parent); // check the queue to see if there's a matching entry. const RouteQueueEntry* queued_entry = NULL; typename list*>::iterator i; for (i = _queue.begin(); i != _queue.end(); i++) { if ( (*i)->net() == rtmsg.net()) { queued_entry = *i; break; } } RouteQueueEntry* entry; if (queued_entry == NULL) { // add the route delete operation to the queue. rtmsg.attributes()->lock(); entry = new RouteQueueEntry(rtmsg.route(), rtmsg.attributes(), RTQUEUE_OP_DELETE); entry->set_origin_peer(rtmsg.origin_peer()); _queue.push_back(entry); } else if (queued_entry->op() == RTQUEUE_OP_ADD) { // The happens when a route with the same nexthop has been // introduced on two peers and the resolvability changes. // The RIB-IN sends a delete followed by an add. If this is // the better route then the other peer will send an add then // a delete. // RIB-IN RIB-OUT // Peer A -> delete // add // Peer A -> add // delete _queue.erase(i); queued_entry->attributes()->unlock(); delete queued_entry; } else if (queued_entry->op() == RTQUEUE_OP_DELETE) { // This should not happen. XLOG_UNREACHABLE(); } else if (queued_entry->op() == RTQUEUE_OP_REPLACE_OLD) { // The happens when a route with the same nexthop has been // introduced on two peers and the resolvability changes. i = _queue.erase(i); const RouteQueueEntry* new_queued_entry = *i; XLOG_ASSERT(new_queued_entry->op() == RTQUEUE_OP_REPLACE_NEW); new_queued_entry->attributes()->unlock(); delete new_queued_entry; _queue.erase(i); FPAListRef old_pa_list = queued_entry->attributes(); entry = new RouteQueueEntry(queued_entry->route(), old_pa_list, RTQUEUE_OP_DELETE); entry->set_origin_peer(queued_entry->origin_peer()); _queue.push_back(entry); //these are the same attributes we just enqueued, so don't unlock them. //queued_entry->attributes()->unlock(); delete queued_entry; } // handle push if (rtmsg.push()) push(this->_parent); return 0; } template int RibOutTable::push(BGPRouteTable *caller) { debug_msg("%s PUSH\n", this->tablename().c_str()); XLOG_ASSERT(caller == this->_parent); // In push, we need to collect together all the SubnetRoutes that // have the same Path Attributes, and send them together in an // Update message. We repeatedly do this until the queue is empty. while (_queue.empty() == false) { // go through _queue and move all of the queue elements that // have the same attribute list as the first element to // tmp_queue list *> tmp_queue; FPAListRef attributes = NULL; int ctr = 1; typedef typename list*>::iterator Iter; Iter i = _queue.begin(); while (i != _queue.end()) { if ((*i)->op() == RTQUEUE_OP_REPLACE_OLD) { // we have to handle replace differently from the rest // because replace uses two paired queue entries, and // we must move them together. We only care about the // attributes on the new one of the pair. Iter i2 = i; i2++; XLOG_ASSERT((*i2)->op() == RTQUEUE_OP_REPLACE_NEW); if (attributes == NULL) attributes = (*i2)->attributes(); if ((*i2)->attributes() == attributes) { tmp_queue.push_back(*i); tmp_queue.push_back(*i2); _queue.erase(i); i = _queue.erase(i2); ctr++; } else { ++i; XLOG_ASSERT(i != _queue.end()); ++i; } } else { if (attributes == NULL) attributes = (*i)->attributes(); if (*((*i)->attributes()) == *attributes) { tmp_queue.push_back((*i)); i = _queue.erase(i); ctr++; } else { ++i; } } } print_queue(tmp_queue); // at this point we pass the tmp_queue to the output BGP // session object for output debug_msg("************************************\n"); debug_msg("* Outputting route to BGP peer\n"); debug_msg("* Attributes: %s\n", attributes->str().c_str()); i = tmp_queue.begin(); _peer->start_packet(); while (i != tmp_queue.end()) { debug_msg("* Subnet: %s\n", (*i)->net().str().c_str()); if ((*i)->op() == RTQUEUE_OP_ADD ) { debug_msg("* Announce %s\n", (*i)->route()->net().str().c_str()); // the sanity checking was done in add_route... FPAListRef pa_list = (*i)->attributes(); pa_list->unlock(); _peer->add_route(*((*i)->route()), pa_list, (*i)->origin_peer()->ibgp(), this->safi()); delete (*i); } else if ((*i)->op() == RTQUEUE_OP_DELETE ) { // the sanity checking was done in delete_route... debug_msg("* Withdraw\n"); FPAListRef pa_list = (*i)->attributes(); pa_list->unlock(); _peer->delete_route(*((*i)->route()), pa_list, (*i)->origin_peer()->ibgp(), this->safi()); delete (*i); } else if ((*i)->op() == RTQUEUE_OP_REPLACE_OLD ) { debug_msg("* Replace\n"); const SubnetRoute *old_route = (*i)->route(); bool old_ibgp = (*i)->origin_peer()->ibgp(); const RouteQueueEntry *old_queue_entry = (*i); i++; XLOG_ASSERT(i != tmp_queue.end()); XLOG_ASSERT((*i)->op() == RTQUEUE_OP_REPLACE_NEW); const SubnetRoute *new_route = (*i)->route(); bool new_ibgp = (*i)->origin_peer()->ibgp(); FPAListRef pa_list = (*i)->attributes(); pa_list->unlock(); old_queue_entry->attributes()->unlock(); _peer->replace_route(*old_route, old_ibgp, *new_route, new_ibgp, pa_list, this->safi()); delete old_queue_entry; delete (*i); } else { XLOG_UNREACHABLE(); } ++i; } /* push the packet */ /* if the peer is busy (a queue has built up), there's not much the RibOut can do for the things already in its output queue - we just keep sending those to the peer_handler. But we want to not request any more data for now. */ if (_peer->push_packet() == PEER_OUTPUT_BUSY) _peer_busy = true; debug_msg("************************************\n"); } return 0; } template void RibOutTable::wakeup() { reschedule_self(); } /* value is a tradeoff between not too many calls to timers, and not being away from eventloop for too long - probably needs tuning*/ #define MAX_MSGS_IN_BATCH 10 template bool RibOutTable::pull_next_route() { /* don't do anything if we can't sent to the peer handler yet */ if (_peer_busy == true) return false; /* we're down, so ignore this wakeup */ if (_peer_is_up == false) return false; for (int msgs = 0; msgs < MAX_MSGS_IN_BATCH; msgs++) { /* only request a limited about of messages, so we don't hog the eventloop for too long */ /* get_next_message will cause the upstream queue to send another update (add, delete, replace or push) through the normal RouteTable interfaces*/ bool upstream_queue_exists = this->_parent->get_next_message(this); if (upstream_queue_exists == false) { /*the queue upstream has now drained*/ /*there's nothing left to do here*/ return false; } if (_peer_busy == true) { /*stop requesting messages because the output queue filled up again*/ return false; } } return true; } template void RibOutTable::output_no_longer_busy() { debug_msg("%s: output_no_longer_busy\n", this->tablename().c_str()); debug_msg("%s: _peer_busy true->false\n", this->tablename().c_str()); _peer_busy = false; reschedule_self(); } template void RibOutTable::reschedule_self() { /*call back immediately, but after network events or expired timers */ if (_pull_routes_task.scheduled()) return; _pull_routes_task = _peer->eventloop().new_task( callback(this, &RibOutTable::pull_next_route), XorpTask::PRIORITY_DEFAULT, XorpTask::WEIGHT_DEFAULT); } template const SubnetRoute* RibOutTable::lookup_route(const IPNet &net, uint32_t& genid, FPAListRef& pa_list) const { return this->_parent->lookup_route(net, genid, pa_list); } template void RibOutTable::peering_went_down(const PeerHandler *peer, uint32_t genid, BGPRouteTable *caller) { XLOG_ASSERT(this->_parent == caller); UNUSED(genid); if (peer == _peer) { _peer_is_up = false; } } template void RibOutTable::peering_down_complete(const PeerHandler *peer, uint32_t genid, BGPRouteTable *caller) { XLOG_ASSERT(this->_parent == caller); UNUSED(genid); UNUSED(peer); } template void RibOutTable::peering_came_up(const PeerHandler *peer, uint32_t genid, BGPRouteTable *caller) { XLOG_ASSERT(this->_parent == caller); UNUSED(genid); if (peer == _peer) { _peer_is_up = true; _peer_busy = false; } } template string RibOutTable::str() const { string s = "RibOutTable" + this->tablename(); return s; } template class RibOutTable; /** IPv6 stuff */ #ifdef HAVE_IPV6 template class RibOutTable; #endif // ipv6 xorp/bgp/open_packet.cc0000664000076400007640000001316211421137511015226 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "bgp_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "packet.hh" extern void dump_bytes(const uint8_t *d, size_t l); /* **************** OpenPacket *********************** */ OpenPacket::OpenPacket(const AsNum& as, const IPv4& id, const uint16_t holdtime) : _id(id), _as(as), _HoldTime(holdtime), _Version(BGPVERSION) { debug_msg("OpenPacket(BGPLocalData *d) constructor called\n"); _Type = MESSAGETYPEOPEN; _OptParmLen = 0; } bool OpenPacket::encode(uint8_t *d, size_t& len, const BGPPeerData *peerdata) const { UNUSED(peerdata); XLOG_ASSERT(d != 0); if (len < BGPPacket::MINOPENPACKET + _OptParmLen) return false; len = BGPPacket::MINOPENPACKET + _OptParmLen; d = basic_encode(len, d); d[BGPPacket::COMMON_HEADER_LEN] = _Version; _as.copy_out(d + BGPPacket::COMMON_HEADER_LEN + 1); d[BGPPacket::COMMON_HEADER_LEN + 3] = (_HoldTime >> 8) & 0xff; d[BGPPacket::COMMON_HEADER_LEN + 4] = _HoldTime & 0xff; _id.copy_out(d + BGPPacket::COMMON_HEADER_LEN + 5); d[BGPPacket::COMMON_HEADER_LEN + 9] = _OptParmLen; debug_msg("encoding Open, optparmlen = %u\n", (uint32_t)_OptParmLen); if (!_parameter_list.empty()) { size_t i = BGPPacket::MINOPENPACKET; ParameterList::const_iterator pi = _parameter_list.begin(); while(pi != _parameter_list.end()) { XLOG_ASSERT(i + (*pi)->length() <= len); memcpy(d + i, (*pi)->data(), (*pi)->length()); i += (*pi)->length(); pi++; } } return true; } OpenPacket::OpenPacket(const uint8_t *d, uint16_t l) throw(CorruptMessage) : _as(AsNum::AS_INVALID) { debug_msg("OpenPacket(const uint8_t *, uint16_t %u) constructor called\n", (uint32_t)l); _Type = MESSAGETYPEOPEN; _OptParmLen = 0; size_t i, myOptParmLen, remaining; if (l < BGPPacket::MINOPENPACKET) { debug_msg("Open message too short\n"); xorp_throw(CorruptMessage, "Open message too short", MSGHEADERERR, BADMESSLEN, d + BGPPacket::MARKER_SIZE, 2); } remaining = l; d += BGPPacket::COMMON_HEADER_LEN; // skip common header remaining -= BGPPacket::COMMON_HEADER_LEN; _Version = d[0]; _as = AsNum(d+1); _HoldTime = (d[3]<<8) + d[4]; _id = IPv4(d+5); myOptParmLen = i = d[9]; d += 10; // skip over fixed open header remaining -= 10; // we shouldn't trust myOptParmLen yet - make sure it's not // greater than the amount of data we actually received. if (remaining < myOptParmLen) { debug_msg("Open message too short\n"); xorp_throw(CorruptMessage, "Open message too short", OPENMSGERROR, UNSPECIFIED); } while (i > 0) { size_t len; debug_msg("Length of unread parameters : %u\n", XORP_UINT_CAST(i)); if (remaining < 2) { debug_msg("Open message too short\n"); xorp_throw(CorruptMessage, "Parameter is too short", OPENMSGERROR, UNSPECIFIED); } BGPParameter *p = BGPParameter::create(d, i, len); if (p != NULL) add_parameter(p); // This assert is safe because if len is bad an exception // should already have been thrown. XLOG_ASSERT(len > 0); XLOG_ASSERT(i >= len); i -= len; d += len; } // check to see if the length of the optional parameters defined in the // packet is the same as the total length of the decoded parameters. if (myOptParmLen != _OptParmLen) { xorp_throw(CorruptMessage, "bad parameters length", OPENMSGERROR, UNSPECIFIED); } return; } string OpenPacket::str() const { string s = "Open Packet\n"; s += c_format(" - Version : %d\n" " - Autonomous System Number : %s\n" " - Holdtime : %d\n" " - BGP Identifier : %s\n", _Version, _as.str().c_str(), _HoldTime, _id.str().c_str()); ParameterList::const_iterator pi = parameter_list().begin(); while (pi != parameter_list().end()) { s = s + " - " + (*pi)->str() + "\n"; ++pi; } return s; } bool OpenPacket::operator==(const OpenPacket& him) const { // version // as if (_as != him.as()) return false; // hold time if (_HoldTime != him.HoldTime()) return false; // bgp id if (_id != him.id()) return false; // opt parm len if (_OptParmLen != him.OptParmLen()) return false; // Compare the parameters ParameterList::const_iterator me_pi = parameter_list().begin(); ParameterList::const_iterator him_pi = him.parameter_list().begin(); for (; me_pi != parameter_list().end(); me_pi++) { bool match = false; for (; him_pi != him.parameter_list().end(); him_pi++) { if ((*me_pi)->compare(*(*him_pi))) { match = true; break; } } if (match == false) return false; } return true; } void OpenPacket::add_parameter(const ParameterNode& p) { _parameter_list.push_back(p); _OptParmLen = _OptParmLen + p->length(); } xorp/bgp/peer_list.cc0000664000076400007640000001120411421137511014717 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "bgp_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "peer_list.hh" BGPPeerList::BGPPeerList() : _next_token(0) { } BGPPeerList::~BGPPeerList() { list::iterator i; for(i = _peers.begin(); i != _peers.end(); i++) { (*i)->event_stop(); delete (*i); *i = 0; } _peers.clear(); } void BGPPeerList::all_stop(bool restart) { list::iterator i; for(i = _peers.begin(); i != _peers.end(); i++) { (*i)->event_stop(restart); if (restart) { if ((*i)->get_current_peer_state() && STATEIDLE == (*i)->state()) (*i)->event_start(); } } /* We now need to drop back to the EventLoop - the peers will only move to idle and cleanly tear down their state when the EventLoop calls their transmit complete callbacks */ debug_msg("RETURNING FROM ALL_STOP\n"); } bool BGPPeerList::not_all_idle() { list::iterator i; for(i = _peers.begin(); i != _peers.end(); i++) if(STATEIDLE != (*i)->state()) return true; return false; } void BGPPeerList::add_peer(BGPPeer *p) { list::iterator i; if (_peers.empty() || p->remote_ip_ge_than(*(_peers.back()))) { _peers.push_back(p); return; } for(i = _peers.begin(); i != _peers.end(); i++) { if((*i)->remote_ip_ge_than(*p)) { _peers.insert(i,p); return; } } XLOG_UNREACHABLE(); } void BGPPeerList::remove_peer(BGPPeer *p) { detach_peer(p); delete p; } void BGPPeerList::detach_peer(BGPPeer *p) { //Before we remove a peer from the peer list, we need to check //whether there are any readers that point to this peer. As the //number of readers is unlikely to be large, and this is not a //really frequest operation, we don't mind iterating through the //whole map of readers. map ::iterator>::iterator mi; for (mi = _readers.begin(); mi != _readers.end(); ) { uint32_t token = mi->first; list::iterator pli = mi->second; ++mi; if (*pli == p) { pli++; _readers.erase(token); _readers.insert(make_pair(token, pli)); } } //Now it's safe to do the deletion. list::iterator i; for (i = _peers.begin(); i != _peers.end(); i++) { if (p == *i) { _peers.erase(i); return; } } XLOG_FATAL("Peer %s not found in peerlist", p->str().c_str()); } void BGPPeerList::dump_list() { list::iterator i; for(i = _peers.begin(); i != _peers.end(); i++) debug_msg("%s\n", (*i)->str().c_str()); } bool BGPPeerList::get_peer_list_start(uint32_t& token) { list::iterator i = _peers.begin(); if (i == _peers.end()) return false; _readers.insert(make_pair(_next_token, i)); token = _next_token; _next_token++; return true; } bool BGPPeerList::get_peer_list_next(const uint32_t& token, string& local_ip, uint32_t& local_port, string& peer_ip, uint32_t& peer_port) { map ::iterator>::iterator mi; mi = _readers.find(token); if (mi == _readers.end()) return false; list::iterator i = mi->second; if (i == _peers.end()) { //this can happen only if the iterator pointed to the last //peer, and it was deleted since we were last here. local_ip = "0.0.0.0"; local_port = 0; peer_ip = "0.0.0.0"; peer_port = 0; } else { BGPPeer *peer = *i; local_ip = peer->peerdata()->iptuple().get_local_addr(); local_port = peer->peerdata()->iptuple().get_local_port(); peer_ip = peer->peerdata()->iptuple().get_peer_addr(); peer_port = peer->peerdata()->iptuple().get_peer_port(); i++; } if (i == _peers.end()) { _readers.erase(mi); return false; } _readers.erase(token); _readers.insert(make_pair(token, i)); return true; } xorp/bgp/packet.cc0000664000076400007640000000311511421137511014202 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #include "bgp_module.h" #include "libxorp/xlog.h" #include "packet.hh" /* **************** BGPPacket *********************** */ const uint8_t BGPPacket::Marker[BGPPacket::MARKER_SIZE] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; uint8_t * BGPPacket::basic_encode(size_t len, uint8_t *buf) const { if (buf == 0) buf = new uint8_t[len]; XLOG_ASSERT(len >= BGPPacket::MARKER_SIZE + 3); memcpy(buf, Marker, BGPPacket::MARKER_SIZE); buf[BGPPacket::MARKER_SIZE] = (len >> 8) & 0xff; buf[BGPPacket::MARKER_SIZE + 1] = len & 0xff; buf[BGPPacket::MARKER_SIZE + 2] = _Type; return buf; } xorp/bgp/subnet_route.cc0000664000076400007640000001704611421137511015461 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "bgp_module.h" #include "libxorp/xlog.h" #include "subnet_route.hh" RouteMetaData::RouteMetaData(const RouteMetaData& metadata) { _flags = metadata._flags; // leave the ref count - this will be // fixed by the container class _igp_metric = metadata._igp_metric; _policytags = metadata._policytags; for (int i = 0; i < 3; i++) _pfilter[i] = metadata._pfilter[i]; } RouteMetaData::RouteMetaData() :_flags(0), _igp_metric(0xffffffff) { } template SubnetRoute::SubnetRoute(const SubnetRoute& route_to_clone) : _attributes(route_to_clone._attributes), _metadata(route_to_clone._metadata) { debug_msg("SubnetRoute constructor1 giving %p\n", this); //note that we need an explicit constructor here, rather than //relying in C++ for the default constructor, or we get the //reference counts wrong in the attribute manager _net = route_to_clone._net; _parent_route = route_to_clone._parent_route; //set our reference count to one (our own self-reference) //and clear the deleted flag _metadata.reset_flags(); //update parent refcount if (_parent_route) _parent_route->bump_refcount(1); } template SubnetRoute::SubnetRoute(const IPNet &n, PAListRef atts, const SubnetRoute* parent_route) : _net(n), _attributes(atts), _parent_route(parent_route) { debug_msg("SubnetRoute constructor2 giving %p\n", this); //the attribute manager handles memory management, and ensuring //that only one copy of each attribute list is ever stored _metadata.reset_flags(); //the in_use flag is set to false so reduce the work we have to //re-do when we have to touch all the routes in a route_table. It //should default to true if we don't know for sure that a route is //not used as this is always safe, if somewhat inefficient. _metadata.set_in_use(true); // we must set the aggregate_prefix_len to SR_AGGRLEN_IGNORE to // indicate that the route has not been (yet) marked for aggregation // the statement bellow is equivalent to // this->set_aggr_prefix_len(SR_AGGRLEN_IGNORE) _metadata.dont_aggregate(); if (_parent_route) { _parent_route->bump_refcount(1); } } template SubnetRoute::SubnetRoute(const IPNet &n, PAListRef atts, const SubnetRoute* parent_route, uint32_t igp_metric) : _net(n), _attributes(atts), _parent_route(parent_route) { debug_msg("SubnetRoute constructor3 giving %p\n", this); _metadata.reset_flags(); //the in_use flag is set to false so reduce the work we have to //re-do when we have to touch all the routes in a route_table. It //should default to true if we don't know for sure that a route is //not used as this is always safe, if somewhat inefficient. _metadata.set_in_use(true); // we must set the aggregate_prefix_len to SR_AGGRLEN_IGNORE to // indicate that the route has not been (yet) marked for aggregation // the statement bellow is equivalent to // this->set_aggr_prefix_len(SR_AGGRLEN_IGNORE) _metadata.dont_aggregate(); if (parent_route) { _parent_route->bump_refcount(1); } _metadata.set_igp_metric(igp_metric); } template bool SubnetRoute::operator==(const SubnetRoute& them) const { //only compare net and attributes, not flags if (!(_net == them._net)) return false; if (!(_attributes == (them._attributes))) return false; return true; } template SubnetRoute::~SubnetRoute() { debug_msg("SubnetRoute destructor called for %p\n", this); assert(refcount() == 0); if (_parent_route) _parent_route->bump_refcount(-1); //prevent accidental reuse after deletion. _net = IPNet(); _parent_route = (const SubnetRoute*)0xbad; } template void SubnetRoute::unref() const { if (_metadata.is_deleted()) { XLOG_FATAL("SubnetRoute %p: multiple unref's\n", this); } if (refcount() == 0) delete this; else { _metadata.set_deleted(); } } template void SubnetRoute::set_parent_route(const SubnetRoute *parent) { assert(parent != this); if (_parent_route) _parent_route->bump_refcount(-1); _parent_route = parent; if (_parent_route) _parent_route->bump_refcount(1); } template void SubnetRoute::set_is_winner(uint32_t igp_metric) const { _metadata.set_is_winner(igp_metric); if (_parent_route) { _parent_route->set_is_winner(igp_metric); } } template void SubnetRoute::set_is_not_winner() const { _metadata.set_is_not_winner(); if (_parent_route) { _parent_route->set_is_not_winner(); } } template void SubnetRoute::set_in_use(bool used) const { _metadata.set_in_use(used); if (_parent_route) { _parent_route->set_in_use(used); } #ifdef DEBUG_FLAGS printf("set_in_use: %p = %s", this, bool_c_str(used)); printf("\n%s\n", str().c_str()); #endif } template void SubnetRoute::set_nexthop_resolved(bool resolvable) const { _metadata.set_nexthop_resolved(resolvable); if (_parent_route) { _parent_route->set_nexthop_resolved(resolvable); } #ifdef DEBUG_FLAGS printf("set_nexthop_resolved: %p = %s", this, bool_c_str(resolvable)); printf("\n%s\n", str().c_str()); #endif } template void SubnetRoute::set_filtered(bool filtered) const { _metadata.set_filtered(filtered); #ifdef DEBUG_FLAGS printf("set_filtered: %p = %s", this, bool_c_str(filtered)); printf("\n%s\n", str().c_str()); #endif } template string SubnetRoute::str() const { string s; s = "SubnetRoute:\n"; s += " Net: " + _net.str() + "\n"; s += " PAList: " + _attributes->str(); #ifdef DEBUG_FLAGS s += "\n"; s += " Policytags: " + _policytags.str() + "\n"; if (is_winner()) { s += " route is winner\n"; } else { s += " route is not winner\n"; } if (is_filtered()) { s += " route is filtered"; } else { s += " route is not filtered"; } if (in_use()) { s += " route is in use"; } else { s += " route is not in use"; } if (nexthop_resolved()) { s += " route's nexthop resolved"; } else { s += " route's nexthop did not resolve"; } #endif return s; } template const RefPf& SubnetRoute::policyfilter(uint32_t i) const { if (_parent_route) return _parent_route->policyfilter(i); return _metadata.policyfilter(i); } template void SubnetRoute::set_policyfilter(uint32_t i, const RefPf& f) const { if (_parent_route) { _parent_route->set_policyfilter(i, f); } _metadata.set_policyfilter(i, f); } template class SubnetRoute; template class SubnetRoute; xorp/bgp/socket.hh0000664000076400007640000001733611421137511014247 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/bgp/socket.hh,v 1.24 2008/10/02 21:56:21 bms Exp $ #ifndef __BGP_SOCKET_HH__ #define __BGP_SOCKET_HH__ // #define DEBUG_PEERNAME #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/exceptions.hh" #include "libxorp/xorpfd.hh" #include "libxorp/eventloop.hh" #include "libxorp/asyncio.hh" #include "libxorp/callback.hh" #ifdef HAVE_NETDB_H #include #endif #ifdef HAVE_SYS_SOCKET_H #include #endif #ifdef HAVE_SYS_UN_H #include #endif #ifdef HAVE_NETINET_IN_H #include #endif #include "packet.hh" #include "iptuple.hh" /* **************** BGPSocket *********************** */ class Socket { public: static const int MAX_LISTEN_QUEUE = 5; Socket(const Iptuple& iptuple, EventLoop& e); /** * Given an address (IPv4 or IPv6) symbolic or numeric fill in the * provided structure. * * Note: This method is provided as a service to other code and is * no longer used by this class. Don't remove it as the test code * uses it. */ static void init_sockaddr(string addr, uint16_t local_port, struct sockaddr_storage& ss, size_t& len); // void set_eventloop(EventLoop *evt) {_eventloop = evt;} EventLoop& eventloop() {return _eventloop;} XorpFd get_sock() { return _s; } void create_listener(); /* Intended for debugging use only */ void set_remote_host(const char *s) {_remote_host = s;} const char *get_remote_host() {return _remote_host.c_str();} protected: void set_sock(XorpFd s) { _s = s; } void close_socket(); void create_socket(const struct sockaddr *sin, int is_blocking); const struct sockaddr *get_local_socket(size_t& len) { return _iptuple.get_local_socket(len); } const string& get_local_interface() const { return _iptuple.get_local_interface(); } string get_local_addr() {return _iptuple.get_local_addr();} uint16_t get_local_port() {return _iptuple.get_local_port();} const struct sockaddr *get_bind_socket(size_t& len) { return _iptuple.get_bind_socket(len); } const struct sockaddr *get_remote_socket(size_t& len) { return _iptuple.get_peer_socket(len); } string get_remote_addr() {return _iptuple.get_peer_addr();} uint16_t get_remote_port() {return _iptuple.get_peer_port();} private: XorpFd _s; /* ** All in network byte order. */ const Iptuple _iptuple; EventLoop& _eventloop; /* ** Remote host. For debugging only */ string _remote_host; }; class SocketClient : public Socket { public: /** * @param iptuple specification of the connection endpoints. */ SocketClient(const Iptuple& iptuple, EventLoop& e, bool md5sig = false); ~SocketClient(); /** * Callback for connection attempts. */ typedef XorpCallback1::RefPtr ConnectCallback; /** * Asynchronously connect. * * @param cb is called when connection suceeds or fails. */ void connect(ConnectCallback cb); /** * Break asynchronous connect attempt. * * Each instance of the this class has a single connection * associated with it. If while we are attemping to connect to a * peer (using connect()), the peer connects to us. It is * necessary to stop the outgoing connect. */ void connect_break(); /** * The peer has initiated the connection so form an association. * * @param s incoming socket file descriptor */ void connected(XorpFd s); /** * Throw away all the data that is queued to be sent on this socket. */ void flush_transmit_queue(); /** * Stop reading data on this socket. */ void stop_reader() {async_remove_reader();} /** * Disconnect this socket. */ void disconnect(); /** * @return Are we currrent disconecting. */ bool disconnecting() {return _disconnecting;} /** * Callback for incoming data. * * @param const uint8_t* pointer to data. * @param size_t length of data. */ typedef XorpCallback4::RefPtr MessageCallback; /** * Set the callback for incoming data. */ void set_callback(const MessageCallback& cb) {_callback = cb;} enum Event { DATA = AsyncFileWriter::DATA, FLUSHING = AsyncFileWriter::FLUSHING, ERROR = AsyncFileWriter::OS_ERROR }; /** * Callback for data transmission. * * @param Event status of the send. * @param const uint8_t* pointer to the transmitted data. Allows * caller to free the data. */ typedef XorpCallback2::RefPtr SendCompleteCallback; /** * Asynchronously Send a message. * * @param buf pointer to data buffer. * @param cnt length of data buffer. * @param cb notification of success or failure. * * @return true if the message is accepted. * */ bool send_message(const uint8_t* buf, size_t cnt, SendCompleteCallback cb); /** * Flow control signal. * * Data will be queued for transmission until a resource such as * memory is exceeded. A correctly written client should stop * sending messages if the output queue is not draining. * * @return true if its time to stop sending messages. */ bool output_queue_busy() const; /** * @return number of messages in the output queue. */ int output_queue_size() const; /** * @return true if a session exists. */ bool is_connected(); /** * @return true if we are still reading. */ bool still_reading(); protected: private: void connect_socket(XorpFd sock, string raddr, uint16_t port, string laddr, ConnectCallback cb); void connect_socket_complete(XorpFd fd, IoEventType type, ConnectCallback cb); void connect_socket_break(); void async_add(XorpFd sock); void async_remove(); void async_remove_reader(); void read_from_server(XorpFd sock); void write_to_server(XorpFd sock); void send_message_complete(AsyncFileWriter::Event e, const uint8_t* buf, const size_t buf_bytes, const size_t offset, SendCompleteCallback cb); void async_read_start(size_t cnt = BGPPacket::COMMON_HEADER_LEN, size_t ofset = 0); void async_read_message(AsyncFileWriter::Event ev, const uint8_t *buf, const size_t buf_bytes, const size_t offset); MessageCallback _callback; AsyncFileWriter *_async_writer; AsyncFileReader *_async_reader; bool _disconnecting; bool _connecting; bool _md5sig; uint8_t _read_buf[BGPPacket::MAXPACKETSIZE]; // Maximum allowed BGP message }; class SocketServer : public Socket { public: SocketServer(EventLoop& e); SocketServer(const Iptuple& iptuple, EventLoop& e); }; #endif // __BGP_SOCKET_HH__ xorp/bgp/route_table_policy_ex.cc0000664000076400007640000000376011421137511017321 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #include "bgp_module.h" #include "route_table_policy_ex.hh" #include "bgp_varrw_export.hh" template PolicyTableExport::PolicyTableExport(const string& tablename, const Safi& safi, BGPRouteTable* parent, PolicyFilters& pfs, const string& neighbor, const A& self) : PolicyTable(tablename, safi, parent, pfs, filter::EXPORT), _neighbor(neighbor) { this->_parent = parent; // // XXX: Explicitly call PolicyTableExport::init_varrw(), // because the virtual PolicyTable::init_varrw() won't be overwritten // by PolicyTableExport::init_varrw() when called inside the // PolicyTable constructor. // init_varrw(); this->_varrw->set_self(self); } template void PolicyTableExport::init_varrw() { if (this->_varrw) delete this->_varrw; this->_varrw = new BGPVarRWExport( filter::filter2str(PolicyTable::_filter_type), _neighbor); } template class PolicyTableExport; template class PolicyTableExport; xorp/bgp/path_attribute.cc0000664000076400007640000025506211540224217015766 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net //#define DEBUG_LOGGING //#define DEBUG_PRINT_FUNCTION_NAME #include "bgp_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/ipv4.hh" #include "libxorp/ipv6.hh" #ifdef HAVE_NETINET_IN_H #include #endif #include "peer_data.hh" #include "path_attribute.hh" #include "packet.hh" #include "peer.hh" #include "bgp.hh" template AttributeManager* PAListRef::_att_mgr = 0; #ifdef DEBUG_LOGGING inline static void dump_bytes(const uint8_t* d, uint8_t l) { for (uint16_t i = 0; i < l; i++) { debug_msg("%3u 0x%02x\n", i, d[i]); } } #else inline static void dump_bytes(const uint8_t*, uint8_t) {} #endif /* DEBUG_LOGGING */ /* * Flags values crib: * * Well-known mandatory: optional = false, transitive = true * Well-known discretionary: optional = false, transitive = true * Optional transitive: optional = true, transitive = true, * Optional nontransitive: optional = true, transitive = false * * Yes, you really can't tell the difference between well-known * mandatory and well-known discretionary from the flags. They're * well-known, so you should know :-) */ /******************** DERIVED CLASSES ***************************/ /** * OriginAttribute */ OriginAttribute::OriginAttribute(OriginType t) : PathAttribute(Transitive, ORIGIN), _origin(t) { } PathAttribute * OriginAttribute::clone() const { return new OriginAttribute(origin()); } OriginAttribute::OriginAttribute(const uint8_t* d) throw(CorruptMessage) : PathAttribute(d) { if (length(d) != 1) { xorp_throw(CorruptMessage, c_format("OriginAttribute bad length %u", XORP_UINT_CAST(length(d))), UPDATEMSGERR, ATTRLEN); } if (!well_known() || !transitive()) { xorp_throw(CorruptMessage, c_format("Bad Flags in Origin attribute %#x",flags()), UPDATEMSGERR, ATTRFLAGS, d, total_tlv_length(d)); } const uint8_t* data = payload(d); // skip header. switch (data[0]) { case IGP: case EGP: case INCOMPLETE: _origin = (OriginType)data[0]; break; default: xorp_throw(CorruptMessage, c_format("Unknown Origin Type %d", data[0]), UPDATEMSGERR, INVALORGATTR, d, total_tlv_length(d)); } } bool OriginAttribute::encode(uint8_t *buf, size_t &wire_size, const BGPPeerData* peerdata) const { UNUSED(peerdata); debug_msg("OriginAttribute::encode\n"); // ensure there's enough space if (wire_size < 4) return false; uint8_t *d = set_header(buf, 1, wire_size); d[0] = origin(); return true; } string OriginAttribute::str() const { string s = "Origin Path Attribute - "; switch (origin()) { case IGP: s += "IGP"; break; case EGP: s += "EGP"; break; case INCOMPLETE: s += "INCOMPLETE"; break; default: s += "UNKNOWN"; } return s; } /** * ASPathAttribute */ ASPathAttribute::ASPathAttribute(const ASPath& p) : PathAttribute(Transitive, AS_PATH) { _as_path = new ASPath(p); } PathAttribute * ASPathAttribute::clone() const { return new ASPathAttribute(as_path()); } ASPathAttribute::ASPathAttribute(const uint8_t* d, bool use_4byte_asnums) throw(CorruptMessage) : PathAttribute(d) { if (!well_known() || !transitive()) xorp_throw(CorruptMessage, c_format("Bad Flags in AS Path attribute %#x", flags()), UPDATEMSGERR, ATTRFLAGS, d, total_tlv_length(d)); if (use_4byte_asnums) _as_path = new AS4Path(payload(d), length(d)); else _as_path = new ASPath(payload(d), length(d)); } /** * see note in aspath.hh on using 4-byte AS numbers */ bool ASPathAttribute::encode(uint8_t *buf, size_t &wire_size, const BGPPeerData* peerdata) const { debug_msg("ASPathAttribute encode(peerdata=%p)\n", peerdata); bool enc_4byte_asnums = false; if (peerdata == NULL) { /* when we're hashing the attributes, we don't know the peer. Use the most general form, which is 4-byte ASnums */ enc_4byte_asnums = true; } else if (peerdata->use_4byte_asnums() && peerdata->we_use_4byte_asnums()) { // both us and the peer use 4-byte AS numbers, so encode using // 4-byte ASnums. enc_4byte_asnums = true; } if (enc_4byte_asnums) { size_t l = as4_path().wire_size(); if (l + 4 >= wire_size) { // There's not enough space to encode this. return false; } uint8_t *d = set_header(buf, l, wire_size); // set and skip header as4_path().encode(l, d); // encode the payload in the buffer } else { // either we're not using 4-byte AS numbers, or the peer isn't // so encode as two-byte AS nums. If we've got any 4-byte AS // numbers in there, they'll be mapped to AS_TRAN. size_t l = as_path().wire_size(); if (l + 4 >= wire_size) { // There's not enough space to encode this. return false; } uint8_t *d = set_header(buf, l, wire_size); // set and skip header as_path().encode(l, d); // encode the payload in the buffer } return true; } /* * AS4 Path Attribute - see note in aspath.hh for usage details */ AS4PathAttribute::AS4PathAttribute(const AS4Path& p) : PathAttribute((Flags)(Optional|Transitive), AS4_PATH) { _as_path = new AS4Path(p); } AS4PathAttribute::AS4PathAttribute(const uint8_t* d) throw(CorruptMessage) : PathAttribute(d) { if (!optional() || !transitive()) xorp_throw(CorruptMessage, c_format("Bad Flags in AS4 Path attribute %#x", flags()), UPDATEMSGERR, ATTRFLAGS, d, total_tlv_length(d)); _as_path = new AS4Path(payload(d), length(d)); } PathAttribute * AS4PathAttribute::clone() const { return new AS4PathAttribute(as4_path()); } bool AS4PathAttribute::encode(uint8_t *buf, size_t &wire_size, const BGPPeerData* peerdata) const { // If we're encoding this, it's because we're not configured to do // 4-byte AS numbers, but we received a 4-byte AS Path from a peer // and now we're sending it on to another peer unchanged. So this // should alwasy be encoded as a 4-byte AS Path, no matter who // we're sending it to. UNUSED(peerdata); debug_msg("AS4PathAttribute encode()\n"); size_t l = as4_path().wire_size(); if (l + 4 >= wire_size) { // There's not enough space to encode this. return false; } uint8_t *d = set_header(buf, l, wire_size); // set and skip header as4_path().encode(l, d); // encode the payload in the buffer return true; } /** * NextHopAttribute */ template NextHopAttribute::NextHopAttribute(const A& n) throw(CorruptMessage) : PathAttribute(Transitive, NEXT_HOP), _next_hop(n) { verify(); } template PathAttribute * NextHopAttribute::clone() const { return new NextHopAttribute(_next_hop); } /* Throw exception if there are problems...do nothing * otherwise. */ template void NextHopAttribute::verify() throw(CorruptMessage) { if (!_next_hop.is_unicast()) { //XLOG_ASSERT(0); xorp_throw(CorruptMessage, c_format("NextHop %s is not a unicast address", _next_hop.str().c_str()), UPDATEMSGERR, INVALNHATTR); } } template NextHopAttribute::NextHopAttribute(const uint8_t* d) throw(CorruptMessage) : PathAttribute(d) { if (!well_known() || !transitive()) xorp_throw(CorruptMessage, c_format("Bad Flags in NextHop attribute %#x", flags()), UPDATEMSGERR, ATTRFLAGS, d, total_tlv_length(d)); if (length(d) != A::addr_bytelen()) { xorp_throw(CorruptMessage, c_format("Bad size in NextHop address, was %u, should be %u", (uint32_t)length(d), (uint32_t)A::addr_bytelen()), UPDATEMSGERR, ATTRLEN); } _next_hop = A(payload(d)); verify(); } template bool NextHopAttribute::encode(uint8_t *buf, size_t &wire_size, const BGPPeerData* peerdata) const { UNUSED(peerdata); debug_msg("NextHopAttribute::encode\n"); if (wire_size <= 3 + A::addr_bytelen()) { return false; } uint8_t *d = set_header(buf, A::addr_bytelen(), wire_size); _next_hop.copy_out(d); return true; } template class NextHopAttribute; template class NextHopAttribute; /** * MEDAttribute */ MEDAttribute::MEDAttribute(const uint32_t med) : PathAttribute(Optional, MED), _med(med) { } PathAttribute * MEDAttribute::clone() const { return new MEDAttribute(med()); } MEDAttribute::MEDAttribute(const uint8_t* d) throw(CorruptMessage) : PathAttribute(d) { if (!optional() || transitive()) xorp_throw(CorruptMessage, c_format("Bad Flags in MEDAttribute %#x", flags()), UPDATEMSGERR, ATTRFLAGS, d, total_tlv_length(d)); if (length(d) != 4) xorp_throw(CorruptMessage, "Bad size in MEDAttribute", UPDATEMSGERR, ATTRLEN); memcpy(&_med, payload(d), 4); _med = ntohl(_med); } bool MEDAttribute::encode(uint8_t *buf, size_t &wire_size, const BGPPeerData* peerdata) const { debug_msg("MEDAttribute::encode\n"); UNUSED(peerdata); if (wire_size <= 7) { return false; } uint8_t *d = set_header(buf, 4, wire_size); uint32_t x = htonl(_med); memcpy(d, &x, 4); return true; } string MEDAttribute::str() const { return c_format("Multiple Exit Descriminator Attribute: MED=%u", XORP_UINT_CAST(med())); } /** * LocalPrefAttribute */ LocalPrefAttribute::LocalPrefAttribute(const uint32_t localpref) : PathAttribute(Transitive, LOCAL_PREF), _localpref(localpref) { } PathAttribute * LocalPrefAttribute::clone() const { return new LocalPrefAttribute(localpref()); } LocalPrefAttribute::LocalPrefAttribute(const uint8_t* d) throw(CorruptMessage) : PathAttribute(d) { if (!well_known() || !transitive()) xorp_throw(CorruptMessage, c_format("Bad Flags in LocalPrefAttribute %#x", flags()), UPDATEMSGERR, ATTRFLAGS, d, total_tlv_length(d)); if (length(d) != 4) xorp_throw(CorruptMessage, "Bad size in LocalPrefAttribute", UPDATEMSGERR, ATTRLEN); memcpy(&_localpref, payload(d), 4); _localpref = ntohl(_localpref); } bool LocalPrefAttribute::encode(uint8_t *buf, size_t &wire_size, const BGPPeerData* peerdata) const { debug_msg("LocalPrefAttribute::encode\n"); UNUSED(peerdata); if (wire_size < 7) { return false; } uint8_t *d = set_header(buf, 4, wire_size); uint32_t x = htonl(_localpref); memcpy(d, &x, 4); return true; } string LocalPrefAttribute::str() const { return c_format("Local Preference Attribute - %u", XORP_UINT_CAST(_localpref)); } /** * AtomicAggAttribute has length 0 */ AtomicAggAttribute::AtomicAggAttribute() : PathAttribute(Transitive, ATOMIC_AGGREGATE) { } bool AtomicAggAttribute::encode(uint8_t *buf, size_t &wire_size, const BGPPeerData* peerdata) const { debug_msg("AAggAttribute::encode\n"); UNUSED(peerdata); if (wire_size < 3) { return false; } set_header(buf, 0, wire_size); return true; } PathAttribute * AtomicAggAttribute::clone() const { return new AtomicAggAttribute(); } AtomicAggAttribute::AtomicAggAttribute(const uint8_t* d) throw(CorruptMessage) : PathAttribute(d) { if (length(d) != 0) xorp_throw(CorruptMessage, c_format("AtomicAggregate bad length %u", XORP_UINT_CAST(length(d))), UPDATEMSGERR, ATTRLEN); if (!well_known() || !transitive()) xorp_throw(CorruptMessage, c_format("Bad Flags in AtomicAggregate attribute %#x", flags()), UPDATEMSGERR, ATTRFLAGS, d, total_tlv_length(d)); } /** * AggregatorAttribute */ AggregatorAttribute::AggregatorAttribute(const IPv4& speaker, const AsNum& as) : PathAttribute((Flags)(Optional|Transitive), AGGREGATOR), _speaker(speaker), _as(as) { } PathAttribute * AggregatorAttribute::clone() const { return new AggregatorAttribute(route_aggregator(), aggregator_as()); } AggregatorAttribute::AggregatorAttribute(const uint8_t* d, bool use_4byte_asnums) throw(CorruptMessage) : PathAttribute(d), _as(AsNum::AS_INVALID) { if (!use_4byte_asnums && length(d) != 6) xorp_throw(CorruptMessage, c_format("Aggregator bad length %u", XORP_UINT_CAST(length(d))), UPDATEMSGERR, ATTRLEN); if (use_4byte_asnums && length(d) != 8) xorp_throw(CorruptMessage, c_format("Aggregator bad length %u", XORP_UINT_CAST(length(d))), UPDATEMSGERR, ATTRLEN); if (!optional() || !transitive()) xorp_throw(CorruptMessage, c_format("Bad Flags in AtomicAggregate attribute %#x", flags()), UPDATEMSGERR, ATTRFLAGS, d, total_tlv_length(d)); d = payload(d); _as = AsNum(d, use_4byte_asnums); if (use_4byte_asnums) _speaker = IPv4(d+4); else { _speaker = IPv4(d+2); } } bool AggregatorAttribute::encode(uint8_t *buf, size_t &wire_size, const BGPPeerData* peerdata) const { debug_msg("AggAttribute::encode\n"); bool enc_4byte_asnums = false; if (peerdata == NULL) { /* when we're hashing the attributes, we don't know the peer. Use the most general form, which is 4-byte ASnums */ enc_4byte_asnums = true; } else if (peerdata->use_4byte_asnums() && peerdata->we_use_4byte_asnums()) { // both us and the peer use 4-byte AS numbers, so encode using // 4-byte ASnums. enc_4byte_asnums = true; } if (enc_4byte_asnums) { if (wire_size < 11) return false; uint8_t *d = set_header(buf, 8, wire_size); _as.copy_out4(d); _speaker.copy_out(d + 4); // defined to be an IPv4 address in RFC 2858 } else { if (wire_size < 9) return false; uint8_t *d = set_header(buf, 6, wire_size); _as.copy_out(d); _speaker.copy_out(d + 2); // defined to be an IPv4 address in RFC 2858 } return true; } string AggregatorAttribute::str() const { return "Aggregator Attribute " + _as.str() + " " + _speaker.str(); } /** * AS4AggregatorAttribute */ AS4AggregatorAttribute::AS4AggregatorAttribute(const IPv4& speaker, const AsNum& as) : PathAttribute((Flags)(Optional|Transitive), AS4_AGGREGATOR), _speaker(speaker), _as(as) { } PathAttribute * AS4AggregatorAttribute::clone() const { return new AS4AggregatorAttribute(route_aggregator(), aggregator_as()); } AS4AggregatorAttribute::AS4AggregatorAttribute(const uint8_t* d) throw(CorruptMessage) : PathAttribute(d), _as(AsNum::AS_INVALID) { if (length(d) != 8) xorp_throw(CorruptMessage, c_format("AS4Aggregator bad length %u", XORP_UINT_CAST(length(d))), UPDATEMSGERR, ATTRLEN); if (!optional() || !transitive()) xorp_throw(CorruptMessage, c_format("Bad Flags in AtomicAggregate attribute %#x", flags()), UPDATEMSGERR, ATTRFLAGS, d, total_tlv_length(d)); d = payload(d); _as = AsNum(d, true); //force interpretation as a 4-byte quantity _speaker = IPv4(d+4); } bool AS4AggregatorAttribute::encode(uint8_t *buf, size_t &wire_size, const BGPPeerData* peerdata) const { debug_msg("AS4AggAttribute::encode\n"); if (wire_size < 11) return false; if (peerdata && peerdata->use_4byte_asnums() && peerdata->we_use_4byte_asnums()) { // we're configured to do 4-byte AS numbers and our peer is // also configured to do them. We should not be encoding 4 // byte AS numbers in an AS4AggregatorAttribute, but rather // doing native 4 byte AS numbers in a regular ASaggregator. XLOG_UNREACHABLE(); } uint8_t *d = set_header(buf, 8, wire_size); _as.copy_out4(d); _speaker.copy_out(d + 4); // defined to be an IPv4 address in RFC 2858 return true; } string AS4AggregatorAttribute::str() const { return "AS4Aggregator Attribute " + _as.str() + " " + _speaker.str(); } /** * CommunityAttribute */ CommunityAttribute::CommunityAttribute() : PathAttribute((Flags)(Optional | Transitive), COMMUNITY) { } PathAttribute * CommunityAttribute::clone() const { CommunityAttribute *ca = new CommunityAttribute(); for(const_iterator i = community_set().begin(); i != community_set().end(); i++) ca->add_community(*i); return ca; } CommunityAttribute::CommunityAttribute(const uint8_t* d) throw(CorruptMessage) : PathAttribute(d) { if (!optional() || !transitive()) xorp_throw(CorruptMessage, "Bad Flags in Community attribute", UPDATEMSGERR, ATTRFLAGS); size_t len = length(d); d = payload(d); for (size_t l = len; l >= 4; d += 4, l -= 4) { uint32_t value; memcpy(&value, d, 4); _communities.insert(ntohl(value)); } } bool CommunityAttribute::encode(uint8_t *buf, size_t &wire_size, const BGPPeerData* peerdata) const { debug_msg("CommAttribute::encode\n"); UNUSED(peerdata); size_t size = 4 * _communities.size(); if (wire_size < size + 4) { return false; } uint8_t *d = set_header(buf, size, wire_size); const_iterator i = _communities.begin(); for (; i != _communities.end(); d += 4, i++) { uint32_t value = htonl(*i); memcpy(d, &value, 4); } return true; } string CommunityAttribute::str() const { string s = "Community Attribute "; const_iterator i = _communities.begin(); for ( ; i != _communities.end(); ++i) { switch (*i) { case NO_EXPORT: s += "NO_EXPORT "; break; case NO_ADVERTISE: s += "NO_ADVERTISE "; break; case NO_EXPORT_SUBCONFED: s += "NO_EXPORT_SUBCONFED "; break; } s += c_format("%d:%d %#x ", XORP_UINT_CAST(((*i >> 16) & 0xffff)), XORP_UINT_CAST((*i & 0xffff)), XORP_UINT_CAST(*i)); } return s; } void CommunityAttribute::add_community(uint32_t community) { _communities.insert(community); } bool CommunityAttribute::contains(uint32_t community) const { const_iterator i = _communities.find(community); if (i == _communities.end()) return false; return true; } /** * OriginatorIDAttribute */ OriginatorIDAttribute::OriginatorIDAttribute(const IPv4 originator_id) : PathAttribute(Optional, ORIGINATOR_ID), _originator_id(originator_id) { } PathAttribute * OriginatorIDAttribute::clone() const { return new OriginatorIDAttribute(originator_id()); } OriginatorIDAttribute::OriginatorIDAttribute(const uint8_t* d) throw(CorruptMessage) : PathAttribute(d) { if (!optional() || transitive()) xorp_throw(CorruptMessage, "Bad Flags in OriginatorIDAttribute", UPDATEMSGERR, ATTRFLAGS); if (length(d) != 4) xorp_throw(CorruptMessage, "Bad size in OriginatorIDAttribute", UPDATEMSGERR, INVALNHATTR); _originator_id.copy_in(payload(d)); } bool OriginatorIDAttribute::encode(uint8_t *buf, size_t &wire_size, const BGPPeerData* peerdata) const { debug_msg("OriginatorIDAttribute::encode\n"); UNUSED(peerdata); if (wire_size < 7) { return false; } uint8_t *d = set_header(buf, 4, wire_size); _originator_id.copy_out(d); return true; } string OriginatorIDAttribute::str() const { return c_format("ORIGINATOR ID Attribute: %s", cstring(originator_id())); } /** * ClusterListAttribute */ ClusterListAttribute::ClusterListAttribute() : PathAttribute(Optional, CLUSTER_LIST) { } ClusterListAttribute::ClusterListAttribute(const uint8_t* d) throw(CorruptMessage) : PathAttribute(d) { if (!optional() || transitive()) xorp_throw(CorruptMessage, "Bad Flags in CLUSTER_LIST attribute", UPDATEMSGERR, ATTRFLAGS); size_t size = length(d); d = payload(d); for (size_t l = size; l >= 4; d += 4, l -= 4) { IPv4 i; i.copy_in(d); _cluster_list.push_back(i); } } PathAttribute * ClusterListAttribute::clone() const { ClusterListAttribute *ca = new ClusterListAttribute(); list::const_reverse_iterator i = cluster_list().rbegin(); for(; i != cluster_list().rend(); i++) ca->prepend_cluster_id(*i); return ca; } bool ClusterListAttribute::encode(uint8_t *buf, size_t &wire_size, const BGPPeerData* peerdata) const { debug_msg("CLAttribute::encode\n"); UNUSED(peerdata); size_t size = 4 * _cluster_list.size(); XLOG_ASSERT(size < 256); if (wire_size < size + 4) { return false; } uint8_t *d = set_header(buf, size, wire_size); const_iterator i = _cluster_list.begin(); for (; i != _cluster_list.end(); d += 4, i++) { i->copy_out(d); } return true; } string ClusterListAttribute::str() const { string s = "Cluster List Attribute "; const_iterator i = _cluster_list.begin(); for ( ; i != _cluster_list.end(); ++i) s += c_format("%s ", cstring(*i)); return s; } void ClusterListAttribute::prepend_cluster_id(IPv4 cluster_id) { _cluster_list.push_front(cluster_id); } bool ClusterListAttribute::contains(IPv4 cluster_id) const { const_iterator i = find(_cluster_list.begin(), _cluster_list.end(), cluster_id); if (i == _cluster_list.end()) return false; return true; } /** * Multiprotocol Reachable NLRI - MP_REACH_NLRI (Type Code 14): * * This is an optional non-transitive attribute that can be used for the * following purposes: * * (a) to advertise a feasible route to a peer * * (b) to permit a router to advertise the Network Layer address of * the router that should be used as the next hop to the destinations * listed in the Network Layer Reachability Information field of the * MP_NLRI attribute. * * (c) to allow a given router to report some or all of the * Subnetwork Points of Attachment (SNPAs) that exist within the * local system */ template <> bool MPReachNLRIAttribute::encode(uint8_t *buf, size_t &wire_size, const BGPPeerData* peerdata) const { debug_msg("MPReachAttribute::encode\n"); UNUSED(peerdata); XLOG_ASSERT(AFI_IPV6 == _afi); XLOG_ASSERT((SAFI_UNICAST == _safi) || (SAFI_MULTICAST == _safi)); XLOG_ASSERT(16 == IPv6::addr_bytelen()); /* ** Figure out how many bytes we need to allocate. */ size_t len; len = 2; // AFI len += 1; // SAFI len += 1; // Length of Next Hop Address len += IPv6::addr_bytelen(); // Next Hop if (!_link_local_next_hop.is_zero()) len += IPv6::addr_bytelen(); len += 1; // Number of SNPAs const_iterator i; for (i = _nlri.begin(); i != _nlri.end(); i++) { len += 1; len += (i->prefix_len() + 7) / 8; // check it will fit if (wire_size < len + 4) { // not enough space to encode! return false; } } uint8_t *d = set_header(buf, len, wire_size); /* ** Fill in the buffer. */ *d++ = (_afi << 8) & 0xff; // AFI *d++ = _afi & 0xff; // AFI *d++ = _safi; // SAFIs if (_link_local_next_hop.is_zero()) { *d++ = IPv6::addr_bytelen(); nexthop().copy_out(d); d += IPv6::addr_bytelen(); } else { *d++ = IPv6::addr_bytelen() * 2; nexthop().copy_out(d); d += IPv6::addr_bytelen(); _link_local_next_hop.copy_out(d); d += IPv6::addr_bytelen(); } *d++ = 0; // Number of SNPAs for (i = _nlri.begin(); i != _nlri.end(); i++) { int bytes = (i->prefix_len() + 7)/ 8; // if there's no space, truncate len -= 1 + bytes; if (len <= 0) break; debug_msg("encode %s bytes = %d\n", i->str().c_str(), bytes); uint8_t buf[IPv6::addr_bytelen()]; i->masked_addr().copy_out(buf); *d++ = i->prefix_len(); memcpy(d, buf, bytes); d += bytes; } return true; } template <> bool MPReachNLRIAttribute::encode(uint8_t *buf, size_t &wire_size, const BGPPeerData* peerdata) const { debug_msg("MPReachAttribute::encode\n"); UNUSED(peerdata); XLOG_ASSERT(AFI_IPV4 == _afi && SAFI_MULTICAST == _safi); XLOG_ASSERT(4 == IPv4::addr_bytelen()); /* ** Figure out how many bytes we need to allocate. */ size_t len; len = 2; // AFI len += 1; // SAFI len += 1; // Length of Next Hop Address len += IPv4::addr_bytelen(); // Next Hop len += 1; // Number of SNPAs const_iterator i; for (i = _nlri.begin(); i != _nlri.end(); i++) { len += 1; len += (i->prefix_len() + 7) / 8; // check it will fit if (wire_size < len + 4) { // not enough space to encode! return false; } } uint8_t *d = set_header(buf, len, wire_size); /* ** Fill in the buffer. */ *d++ = (_afi << 8) & 0xff; // AFI *d++ = _afi & 0xff; // AFI *d++ = _safi; // SAFIs *d++ = IPv4::addr_bytelen(); nexthop().copy_out(d); d += IPv4::addr_bytelen(); *d++ = 0; // Number of SNPAs for (i = _nlri.begin(); i != _nlri.end(); i++) { int bytes = (i->prefix_len() + 7) / 8; // if there's no space, truncate len -= 1 + bytes; if (len <= 0) break; debug_msg("encode %s bytes = %d\n", i->str().c_str(), bytes); uint8_t buf_local[IPv4::addr_bytelen()]; i->masked_addr().copy_out(buf_local); *d++ = i->prefix_len(); memcpy(d, buf_local, bytes); d += bytes; } return true; } template <> MPReachNLRIAttribute::MPReachNLRIAttribute(Safi safi) : PathAttribute((Flags)(Optional), MP_REACH_NLRI), _afi(AFI_IPV6), _safi(safi) { } template <> MPReachNLRIAttribute::MPReachNLRIAttribute(Safi safi) : PathAttribute((Flags)(Optional), MP_REACH_NLRI), _afi(AFI_IPV4), _safi(safi) { } template PathAttribute * MPReachNLRIAttribute::clone() const { MPReachNLRIAttribute *mp = new MPReachNLRIAttribute(_safi); mp->_afi = _afi; mp->_nexthop = _nexthop; for(const_iterator i = _nlri.begin(); i != _nlri.end(); i++) mp->_nlri.push_back(*i); return mp; } template <> MPReachNLRIAttribute::MPReachNLRIAttribute(const uint8_t* d) throw(CorruptMessage) : PathAttribute(d) { if (!optional() || transitive()) xorp_throw(CorruptMessage, "Bad Flags in Multiprotocol Reachable NLRI attribute", UPDATEMSGERR, ATTRFLAGS); const uint8_t *data = payload(d); const uint8_t *end = payload(d) + length(d); uint16_t afi; afi = *data++; afi <<= 8; afi |= *data++; /* ** This is method is specialized for dealing with IPv6. */ if (AFI_IPV6_VAL != afi) xorp_throw(CorruptMessage, c_format("Expected AFI to be %d not %d", AFI_IPV6, afi), UPDATEMSGERR, OPTATTR); _afi = AFI_IPV6; uint8_t safi = *data++; switch(safi) { case SAFI_UNICAST_VAL: _safi = SAFI_UNICAST; break; case SAFI_MULTICAST_VAL: _safi = SAFI_MULTICAST; break; default: xorp_throw(CorruptMessage, c_format("Expected SAFI to %d or %d not %d", SAFI_UNICAST, SAFI_MULTICAST, _safi), UPDATEMSGERR, OPTATTR); } /* ** Next Hop */ uint8_t len = *data++; XLOG_ASSERT(16 == IPv6::addr_bytelen()); IPv6 temp; switch(len) { case 16: temp.copy_in(data); set_nexthop(temp); data += IPv6::addr_bytelen(); break; case 32: temp.copy_in(data); set_nexthop(temp); data += IPv6::addr_bytelen(); _link_local_next_hop.copy_in(data); data += IPv6::addr_bytelen(); break; default: xorp_throw(CorruptMessage, c_format("BAD Next Hop size in " "IPv6 Multiprotocol Reachable NLRI attribute " "16 and 32 allowed not %u", len), UPDATEMSGERR, OPTATTR); } if (data > end) xorp_throw(CorruptMessage, "Premature end of Multiprotocol Reachable NLRI attribute", UPDATEMSGERR, ATTRLEN); /* ** SNPA - I have no idea how these are supposed to be used for IPv6 ** so lets just step over them. */ uint8_t snpa_cnt = *data++; for (;snpa_cnt > 0; snpa_cnt--) { uint8_t snpa_length = *data++; data += snpa_length; } if (data > end) { xorp_throw(CorruptMessage, "Premature end of Multiprotocol Reachable NLRI attribute", UPDATEMSGERR, ATTRLEN); } /* ** NLRI */ while(data < end) { uint8_t prefix_length = *data++; size_t bytes = (prefix_length + 7)/ 8; if (bytes > IPv6::addr_bytelen()) xorp_throw(CorruptMessage, c_format("prefix length too long %d", prefix_length), UPDATEMSGERR, OPTATTR); uint8_t buf[IPv6::addr_bytelen()]; memset(buf, 0, sizeof(buf)); memcpy(buf, data, bytes); IPv6 nlri; nlri.set_addr(buf); _nlri.push_back(IPNet(nlri, prefix_length)); data += bytes; debug_msg("decode %s/%d bytes = %u\n", nlri.str().c_str(), prefix_length, XORP_UINT_CAST(bytes)); } } template <> MPReachNLRIAttribute::MPReachNLRIAttribute(const uint8_t* d) throw(CorruptMessage) : PathAttribute(d) { if (!optional() || transitive()) xorp_throw(CorruptMessage, "Bad Flags in Multiprotocol Reachable NLRI attribute", UPDATEMSGERR, ATTRFLAGS); const uint8_t *data = payload(d); const uint8_t *end = payload(d) + length(d); uint16_t afi; afi = *data++; afi <<= 8; afi |= *data++; /* ** This is method is specialized for dealing with IPv4. */ if (AFI_IPV4_VAL != afi) xorp_throw(CorruptMessage, c_format("Expected AFI to be %d not %d", AFI_IPV4, afi), UPDATEMSGERR, OPTATTR); _afi = AFI_IPV4; uint8_t safi = *data++; switch(safi) { case SAFI_UNICAST_VAL: _safi = SAFI_UNICAST; break; case SAFI_MULTICAST_VAL: _safi = SAFI_MULTICAST; break; default: xorp_throw(CorruptMessage, c_format("Expected SAFI to %d or %d not %d", SAFI_UNICAST, SAFI_MULTICAST, _safi), UPDATEMSGERR, OPTATTR); } // XXX - Temporary hack as SAFI_UNICAST causes problems. if (SAFI_UNICAST == _safi) xorp_throw(CorruptMessage, c_format("Can't handle AFI_IPv4 and SAFI_UNICAST"), UPDATEMSGERR, OPTATTR); /* ** Next Hop */ uint8_t len = *data++; XLOG_ASSERT(4 == IPv4::addr_bytelen()); IPv4 temp; switch(len) { case 4: temp.copy_in(data); set_nexthop(temp); data += IPv4::addr_bytelen(); break; default: xorp_throw(CorruptMessage, c_format("BAD Next Hop size in " "IPv4 Multiprotocol Reachable NLRI attribute " "4 allowed not %u", len), UPDATEMSGERR, OPTATTR); } if (data > end) xorp_throw(CorruptMessage, "Premature end of Multiprotocol Reachable NLRI attribute", UPDATEMSGERR, ATTRLEN); /* ** SNPA - I have no idea how these are supposed to be used for IPv4 ** so lets just step over them. */ uint8_t snpa_cnt = *data++; for (;snpa_cnt > 0; snpa_cnt--) { uint8_t snpa_length = *data++; data += snpa_length; } if (data > end) { xorp_throw(CorruptMessage, "Premature end of Multiprotocol Reachable NLRI attribute", UPDATEMSGERR, ATTRLEN); } /* ** NLRI */ while(data < end) { uint8_t prefix_length = *data++; size_t bytes = (prefix_length + 7) / 8; if (bytes > IPv4::addr_bytelen()) xorp_throw(CorruptMessage, c_format("prefix length too long %d", prefix_length), UPDATEMSGERR, OPTATTR); uint8_t buf[IPv4::addr_bytelen()]; memset(buf, 0, sizeof(buf)); memcpy(buf, data, bytes); IPv4 nlri; nlri.copy_in(buf); _nlri.push_back(IPNet(nlri, prefix_length)); data += bytes; debug_msg("decode %s/%d bytes = %u\n", nlri.str().c_str(), prefix_length, XORP_UINT_CAST(bytes)); } } template string MPReachNLRIAttribute::str() const { string s = c_format("Multiprotocol Reachable NLRI AFI = %d SAFI = %d\n", _afi, _safi); s += c_format(" - Next Hop Attribute %s\n", nexthop().str().c_str()); s += c_format(" - Link Local Next Hop Attribute %s", link_local_nexthop().str().c_str()); typename list >::const_iterator i = nlri_list().begin(); for(; i != nlri_list().end(); i++) s += c_format("\n - Nlri %s", i->str().c_str()); return s; } template <> bool MPUNReachNLRIAttribute::encode(uint8_t *buf, size_t &wire_size, const BGPPeerData* peerdata) const { debug_msg("MPUnreachAttribute::encode\n"); UNUSED(peerdata); XLOG_ASSERT(AFI_IPV6 == _afi); XLOG_ASSERT((SAFI_UNICAST == _safi) || (SAFI_MULTICAST == _safi)); XLOG_ASSERT(16 == IPv6::addr_bytelen()); /* ** Figure out how many bytes we need to allocate. */ size_t len; len = 2; // AFI len += 1; // SAFI const_iterator i; for (i = _withdrawn.begin(); i != _withdrawn.end(); i++) { len += 1; len += (i->prefix_len() + 7)/ 8; // check it will fit if (wire_size < len + 4) { // not enough space to encode! return false; } } uint8_t *d = set_header(buf, len, wire_size); /* ** Fill in the buffer. */ *d++ = (_afi << 8) & 0xff; // AFI *d++ = _afi & 0xff; // AFI *d++ = _safi; // SAFIs for (i = _withdrawn.begin(); i != _withdrawn.end(); i++) { int bytes = (i->prefix_len() + 7)/ 8; // if there's no space, truncate len -= 1 + bytes; if (len <= 0) break; debug_msg("encode %s bytes = %d\n", i->str().c_str(), bytes); uint8_t buf[IPv6::addr_bytelen()]; i->masked_addr().copy_out(buf); *d++ = i->prefix_len(); memcpy(d, buf, bytes); d += bytes; } return true; } template <> bool MPUNReachNLRIAttribute::encode(uint8_t *buf, size_t &wire_size, const BGPPeerData* peerdata) const { debug_msg("MPUnreachNLRIAttribute::encode\n"); UNUSED(peerdata); XLOG_ASSERT(AFI_IPV4 == _afi && SAFI_MULTICAST == _safi); XLOG_ASSERT(4 == IPv4::addr_bytelen()); /* ** Figure out how many bytes we need to allocate. */ size_t len; len = 2; // AFI len += 1; // SAFI const_iterator i; for (i = _withdrawn.begin(); i != _withdrawn.end(); i++) { len += 1; len += (i->prefix_len() + 7) / 8; // check it will fit if (wire_size < len + 4) { // not enough space to encode! return false; } } uint8_t *d = set_header(buf, len, wire_size); /* ** Fill in the buffer. */ *d++ = (_afi << 8) & 0xff; // AFI *d++ = _afi & 0xff; // AFI *d++ = _safi; // SAFIs for (i = _withdrawn.begin(); i != _withdrawn.end(); i++) { int bytes = (i->prefix_len() + 7) / 8; // if there's no space, truncate len -= 1 + bytes; if (len <= 0) break; debug_msg("encode %s bytes = %d\n", i->str().c_str(), bytes); uint8_t buf[IPv4::addr_bytelen()]; i->masked_addr().copy_out(buf); *d++ = i->prefix_len(); memcpy(d, buf, bytes); d += bytes; } return true; } template <> MPUNReachNLRIAttribute::MPUNReachNLRIAttribute(Safi safi) : PathAttribute((Flags)(Optional), MP_UNREACH_NLRI), _afi(AFI_IPV6), _safi(safi) { } template <> MPUNReachNLRIAttribute::MPUNReachNLRIAttribute(Safi safi) : PathAttribute((Flags)(Optional), MP_UNREACH_NLRI), _afi(AFI_IPV4), _safi(safi) { } template PathAttribute * MPUNReachNLRIAttribute::clone() const { MPUNReachNLRIAttribute *mp = new MPUNReachNLRIAttribute(_safi); mp->_afi = _afi; for(const_iterator i = _withdrawn.begin(); i != _withdrawn.end(); i++) mp->_withdrawn.push_back(*i); return mp; } template <> MPUNReachNLRIAttribute::MPUNReachNLRIAttribute(const uint8_t* d) throw(CorruptMessage) : PathAttribute(d) { if (!optional() || transitive()) xorp_throw(CorruptMessage, "Bad Flags in Multiprotocol UNReachable NLRI attribute", UPDATEMSGERR, ATTRFLAGS); const uint8_t *data = payload(d); const uint8_t *end = payload(d) + length(d); uint16_t afi; afi = *data++; afi <<= 8; afi |= *data++; /* ** This is method is specialized for dealing with IPv6. */ if (AFI_IPV6_VAL != afi) xorp_throw(CorruptMessage, c_format("Expected AFI to be %d not %d", AFI_IPV6, afi), UPDATEMSGERR, OPTATTR); _afi = AFI_IPV6; uint8_t safi = *data++; switch(safi) { case SAFI_UNICAST_VAL: _safi = SAFI_UNICAST; break; case SAFI_MULTICAST_VAL: _safi = SAFI_MULTICAST; break; default: xorp_throw(CorruptMessage, c_format("Expected SAFI to %d or %d not %d", SAFI_UNICAST, SAFI_MULTICAST, _safi), UPDATEMSGERR, OPTATTR); } /* ** NLRI */ while(data < end) { uint8_t prefix_length = *data++; debug_msg("decode prefix length = %d\n", prefix_length); size_t bytes = (prefix_length + 7)/ 8; if (bytes > IPv6::addr_bytelen()) xorp_throw(CorruptMessage, c_format("prefix length too long %d", prefix_length), UPDATEMSGERR, OPTATTR); debug_msg("decode bytes = %u\n", XORP_UINT_CAST(bytes)); uint8_t buf[IPv6::addr_bytelen()]; memset(buf, 0, sizeof(buf)); memcpy(buf, data, bytes); IPv6 nlri; nlri.set_addr(buf); _withdrawn.push_back(IPNet(nlri, prefix_length)); data += bytes; } } template <> MPUNReachNLRIAttribute::MPUNReachNLRIAttribute(const uint8_t* d) throw(CorruptMessage) : PathAttribute(d) { if (!optional() || transitive()) xorp_throw(CorruptMessage, "Bad Flags in Multiprotocol UNReachable NLRI attribute", UPDATEMSGERR, ATTRFLAGS); const uint8_t *data = payload(d); const uint8_t *end = payload(d) + length(d); uint16_t afi; afi = *data++; afi <<= 8; afi |= *data++; /* ** This is method is specialized for dealing with IPv4. */ if (AFI_IPV4_VAL != afi) xorp_throw(CorruptMessage, c_format("Expected AFI to be %d not %d", AFI_IPV4, afi), UPDATEMSGERR, OPTATTR); _afi = AFI_IPV4; uint8_t safi = *data++; switch(safi) { case SAFI_UNICAST_VAL: _safi = SAFI_UNICAST; break; case SAFI_MULTICAST_VAL: _safi = SAFI_MULTICAST; break; default: xorp_throw(CorruptMessage, c_format("Expected SAFI to %d or %d not %d", SAFI_UNICAST, SAFI_MULTICAST, _safi), UPDATEMSGERR, OPTATTR); } // XXX - Temporary hack as SAFI_UNICAST causes problems. if (SAFI_UNICAST == _safi) xorp_throw(CorruptMessage, c_format("Can't handle AFI_IPv4 and SAFI_UNICAST"), UPDATEMSGERR, OPTATTR); /* ** NLRI */ while(data < end) { uint8_t prefix_length = *data++; debug_msg("decode prefix length = %d\n", prefix_length); size_t bytes = (prefix_length + 7)/ 8; if (bytes > IPv4::addr_bytelen()) xorp_throw(CorruptMessage, c_format("prefix length too long %d", prefix_length), UPDATEMSGERR, OPTATTR); debug_msg("decode bytes = %u\n", XORP_UINT_CAST(bytes)); uint8_t buf[IPv4::addr_bytelen()]; memset(buf, 0, sizeof(buf)); memcpy(buf, data, bytes); IPv4 nlri; nlri.copy_in(buf); _withdrawn.push_back(IPNet(nlri, prefix_length)); data += bytes; } } template string MPUNReachNLRIAttribute::str() const { string s = c_format("Multiprotocol UNReachable NLRI AFI = %d SAFI = %d", _afi, _safi); typename list >::const_iterator i = wr_list().begin(); for(; i != wr_list().end(); i++) s += c_format("\n - Withdrawn %s", i->str().c_str()); return s; } /************************************************************************** **** **** PathAttribute **** **************************************************************************/ UnknownAttribute::UnknownAttribute(const uint8_t* d) throw(CorruptMessage) : PathAttribute(d) { // It shouldn't be possible to receive an unknown attribute that // is well known. if (well_known()) xorp_throw(CorruptMessage, "Bad Flags in Unknown attribute", UPDATEMSGERR, UNRECOGWATTR, d, total_tlv_length(d)); _size = total_tlv_length(d); _data = new uint8_t[_size]; memcpy(_data, d, _size); } UnknownAttribute::UnknownAttribute(uint8_t *data, size_t size, uint8_t flags) : PathAttribute(data) { /* override the flags from the data because they may have been modified */ _flags = flags; _size = size; _data = new uint8_t[_size]; memcpy(_data, data, _size); } PathAttribute * UnknownAttribute::clone() const { return new UnknownAttribute(_data, _size, _flags); } string UnknownAttribute::str() const { string s = "Unknown Attribute "; for (size_t i=0; i < _size; i++) s += c_format("%x ", _data[i]); s += c_format(" flags: %x", _flags); return s; } bool UnknownAttribute::encode(uint8_t *buf, size_t &wire_size, const BGPPeerData* peerdata) const { debug_msg("UnknownAttribute::encode\n"); UNUSED(peerdata); // check it will fit in the buffer if (wire_size < _size) return false; memcpy(buf, _data, _size); /* reassert the flags because we may have changed them (typically to set Partial before propagating to a peer) */ buf[0]=_flags; wire_size = _size; return true; } /************************************************************************** **** **** PathAttribute **** **************************************************************************/ PathAttribute * PathAttribute::create(const uint8_t* d, uint16_t max_len, size_t& l /* actual length */, const BGPPeerData* peerdata, uint32_t ip_version) throw(CorruptMessage) { PathAttribute *pa; if (max_len < 3) { // must be at least 3 bytes! xorp_throw(CorruptMessage, c_format("PathAttribute too short %d bytes", max_len), UPDATEMSGERR, ATTRLEN, d, max_len); } // compute length, which is 1 or 2 bytes depending on flags d[0] if ( (d[0] & Extended) && max_len < 4) { xorp_throw(CorruptMessage, c_format("PathAttribute (extended) too short %d bytes", max_len), UPDATEMSGERR, ATTRLEN, d, max_len); } l = length(d) + (d[0] & Extended ? 4 : 3); if (max_len < l) { xorp_throw(CorruptMessage, c_format("PathAttribute too short %d bytes need %u", max_len, XORP_UINT_CAST(l)), UPDATEMSGERR, ATTRLEN, d, max_len); } // now we are sure that the data block is large enough. debug_msg("++ create type %d max_len %d actual_len %u\n", d[1], max_len, XORP_UINT_CAST(l)); bool use_4byte_asnums = true; if (peerdata) use_4byte_asnums = peerdata->use_4byte_asnums(); switch (d[1]) { // depending on type, do the right thing. case ORIGIN: pa = new OriginAttribute(d); break; case AS_PATH: pa = new ASPathAttribute(d, use_4byte_asnums); break; case AS4_PATH: pa = new AS4PathAttribute(d); break; case NEXT_HOP: switch (ip_version) { case 4: pa = new IPv4NextHopAttribute(d); break; case 6: pa = new IPv6NextHopAttribute(d); break; default: XLOG_UNREACHABLE(); } break; case MED: pa = new MEDAttribute(d); break; case LOCAL_PREF: pa = new LocalPrefAttribute(d); break; case ATOMIC_AGGREGATE: pa = new AtomicAggAttribute(d); break; case AGGREGATOR: pa = new AggregatorAttribute(d, use_4byte_asnums); break; case AS4_AGGREGATOR: pa = new AS4AggregatorAttribute(d); break; case COMMUNITY: pa = new CommunityAttribute(d); break; case ORIGINATOR_ID: pa = new OriginatorIDAttribute(d); break; case CLUSTER_LIST: pa = new ClusterListAttribute(d); break; case MP_REACH_NLRI: try { pa = new MPReachNLRIAttribute(d); } catch(...) { pa = new MPReachNLRIAttribute(d); } break; case MP_UNREACH_NLRI: try { pa = new MPUNReachNLRIAttribute(d); } catch(...) { pa = new MPUNReachNLRIAttribute(d); } break; default: pa = new UnknownAttribute(d); break; } return pa; } /** * We need to encode an attribute to send to a peer. However we only * have the canonically encoded byte stream data for it. Sometimes * that is fine, and we should just send that; sometimes we need to * decode and re-encode for this specific peer. */ string PathAttribute::str() const { string s = "Path attribute of type "; switch (type()) { case ORIGIN: s += "ORIGIN"; break; case AS_PATH: s += "AS_PATH"; break; case AS4_PATH: s += "AS4_PATH"; break; case NEXT_HOP: s += "NEXT_HOP"; break; case MED: s += "MED"; break; case LOCAL_PREF: s += "LOCAL_PREF"; break; case ATOMIC_AGGREGATE: s += "ATOMIC_AGGREGATOR"; break; case AGGREGATOR: s += "AGGREGATOR"; break; case AS4_AGGREGATOR: s += "AS4_AGGREGATOR"; break; case COMMUNITY: s += "COMMUNITY"; break; case ORIGINATOR_ID: s += "ORIGINATOR_ID"; break; case CLUSTER_LIST: s += "CLUSTER_LIST"; break; case MP_REACH_NLRI: s += "MP_REACH_NLRI"; break; case MP_UNREACH_NLRI: s += "MP_UNREACH_NLRI"; break; default: s += c_format("UNKNOWN(type: %d flags: %x): ", type(), _flags); } return s; } /** * encode a path attribute. In a sane world, we'd use a virtual * function for this. But we store so many path attributes that we * can't afford the overhead of a virtual function table for them, so * we have to do this the hard way * * XXX THE ABOVE IS NO LONGER THE CASE. WE SHOULD REWRITE THIS TO USE * VIRTUAL FUNCTIONS */ bool PathAttribute::encode(uint8_t* buf, size_t &wire_size, const BGPPeerData* peerdata) const { string s = "Path attribute of type "; switch (type()) { case ORIGIN: return ((const OriginAttribute*)this)->encode(buf, wire_size, peerdata); case AS_PATH: return ((const ASPathAttribute*)this)->encode(buf, wire_size, peerdata); case AS4_PATH: return ((const ASPathAttribute*)this)->encode(buf, wire_size, peerdata); case NEXT_HOP: if (dynamic_cast*>(this)) { return ((const NextHopAttribute*)this)->encode(buf, wire_size, peerdata); } else { return ((const NextHopAttribute*)this)->encode(buf, wire_size, peerdata); } case MED: return ((const MEDAttribute*)this)->encode(buf, wire_size, peerdata); case LOCAL_PREF: return ((const LocalPrefAttribute*)this)->encode(buf, wire_size, peerdata); case ATOMIC_AGGREGATE: return ((const AtomicAggAttribute*)this)->encode(buf, wire_size, peerdata); case AGGREGATOR: return ((const AggregatorAttribute*)this)->encode(buf, wire_size, peerdata); case AS4_AGGREGATOR: return ((const AS4AggregatorAttribute*)this)->encode(buf, wire_size, peerdata); case COMMUNITY: return ((const CommunityAttribute*)this)->encode(buf, wire_size, peerdata); case ORIGINATOR_ID: return ((const OriginatorIDAttribute*)this)->encode(buf, wire_size, peerdata); case CLUSTER_LIST: return ((const ClusterListAttribute*)this)->encode(buf, wire_size, peerdata); case MP_REACH_NLRI: if (dynamic_cast*>(this)) { return ((const MPReachNLRIAttribute*)this) ->encode(buf, wire_size, peerdata); } else { return ((const MPReachNLRIAttribute*)this) ->encode(buf, wire_size, peerdata); } case MP_UNREACH_NLRI: if (dynamic_cast*>(this)) { return ((const MPUNReachNLRIAttribute*)this) ->encode(buf, wire_size, peerdata); } else { return ((const MPUNReachNLRIAttribute*)this) ->encode(buf, wire_size, peerdata); } case UNKNOWN_TESTING1: case UNKNOWN_TESTING2: // This is only for the test harness return true; } return true; } bool PathAttribute::operator<(const PathAttribute& him) const { uint8_t mybuf[BGPPacket::MAXPACKETSIZE], hisbuf[BGPPacket::MAXPACKETSIZE]; size_t mybuflen, hisbuflen; if (sorttype() < him.sorttype()) return true; if (sorttype() > him.sorttype()) return false; // equal sorttypes imply equal types switch (type()) { case ORIGIN: return ( ((OriginAttribute &)*this).origin() < ((OriginAttribute &)him).origin() ); case AS_PATH: return ( ((ASPathAttribute &)*this).as_path() < ((ASPathAttribute &)him).as_path() ); case AS4_PATH: return ( ((AS4PathAttribute &)*this).as_path() < ((AS4PathAttribute &)him).as_path() ); case NEXT_HOP: return ( ((NextHopAttribute &)*this).nexthop() < ((NextHopAttribute &)him).nexthop() ); case MED: return ( ((MEDAttribute &)*this).med() < ((MEDAttribute &)him).med() ); case LOCAL_PREF: return ( ((LocalPrefAttribute &)*this).localpref() < ((LocalPrefAttribute &)him).localpref() ); case ATOMIC_AGGREGATE: return false; case AGGREGATOR: if ( ((AggregatorAttribute &)*this).aggregator_as() == ((AggregatorAttribute &)him).aggregator_as() ) { return ( ((AggregatorAttribute &)*this).route_aggregator() < ((AggregatorAttribute &)him).route_aggregator() ); } else { return ( ((AggregatorAttribute &)*this).aggregator_as() < ((AggregatorAttribute &)him).aggregator_as() ); } case AS4_AGGREGATOR: if ( ((AS4AggregatorAttribute &)*this).aggregator_as() == ((AS4AggregatorAttribute &)him).aggregator_as() ) { return ( ((AS4AggregatorAttribute &)*this).route_aggregator() < ((AS4AggregatorAttribute &)him).route_aggregator() ); } else { return ( ((AS4AggregatorAttribute &)*this).aggregator_as() < ((AS4AggregatorAttribute &)him).aggregator_as() ); } case COMMUNITY: // XXX using encode for this is a little inefficient mybuflen = hisbuflen = BGPPacket::MAXPACKETSIZE; ((const CommunityAttribute*)this)->encode(mybuf, mybuflen, NULL); ((const CommunityAttribute&)him).encode(hisbuf, hisbuflen, NULL); if (mybuflen < hisbuflen) return true; if (mybuflen > hisbuflen) return false; return (memcmp(mybuf, hisbuf, mybuflen) < 0); case ORIGINATOR_ID: return ( ((const OriginatorIDAttribute &)*this).originator_id() < ((const OriginatorIDAttribute &)him).originator_id() ); case CLUSTER_LIST: // XXX using encode for this is a little inefficient mybuflen = hisbuflen = BGPPacket::MAXPACKETSIZE; ((const ClusterListAttribute*)this)->encode(mybuf, mybuflen, NULL); ((const ClusterListAttribute&)him).encode(hisbuf, hisbuflen, NULL); if (mybuflen < hisbuflen) return true; if (mybuflen > hisbuflen) return false; return (memcmp(mybuf, hisbuf, mybuflen) < 0); case MP_REACH_NLRI: case MP_UNREACH_NLRI: // at places in the code where we'll use this operator, these // shouldn't be present in the PathAttributes. XLOG_UNREACHABLE(); default: /* this must be an unknown attribute */ #if 0 XLOG_ASSERT(dynamic_cast(this) != 0); mybuflen = hisbuflen = BGPPacket::MAXPACKETSIZE; ((const UnknownAttribute*)this)->encode(mybuf, mybuflen, NULL); ((const UnknownAttribute&)him).encode(hisbuf, hisbuflen, NULL); if (mybuflen < hisbuflen) return true; if (mybuflen > hisbuflen) return false; return (memcmp(mybuf, hisbuf, mybuflen) < 0); #endif mybuflen = hisbuflen = BGPPacket::MAXPACKETSIZE; this->encode(mybuf, mybuflen, NULL); him.encode(hisbuf, hisbuflen, NULL); if (mybuflen < hisbuflen) return true; if (mybuflen > hisbuflen) return false; return (memcmp(mybuf, hisbuf, mybuflen) < 0); } XLOG_UNREACHABLE(); } bool PathAttribute::operator==(const PathAttribute& him) const { uint8_t mybuf[BGPPacket::MAXPACKETSIZE], hisbuf[BGPPacket::MAXPACKETSIZE]; size_t mybuflen, hisbuflen; if (sorttype() != him.sorttype()) return false; switch (type()) { case ORIGIN: return ( ((OriginAttribute &)*this).origin() == ((OriginAttribute &)him).origin() ); case AS_PATH: return ( ((ASPathAttribute &)*this).as_path() == ((ASPathAttribute &)him).as_path() ); case AS4_PATH: return ( ((AS4PathAttribute &)*this).as_path() == ((AS4PathAttribute &)him).as_path() ); case NEXT_HOP: return ( ((NextHopAttribute &)*this).nexthop() == ((NextHopAttribute &)him).nexthop() ); case MED: return ( ((MEDAttribute &)*this).med() == ((MEDAttribute &)him).med() ); case LOCAL_PREF: return ( ((LocalPrefAttribute &)*this).localpref() == ((LocalPrefAttribute &)him).localpref() ); case ATOMIC_AGGREGATE: return true; case AGGREGATOR: return ( ((AggregatorAttribute &)*this).aggregator_as() == ((AggregatorAttribute &)him).aggregator_as() && ((AggregatorAttribute &)*this).route_aggregator() == ((AggregatorAttribute &)him).route_aggregator() ); case AS4_AGGREGATOR: return ( ((AS4AggregatorAttribute &)*this).aggregator_as() == ((AS4AggregatorAttribute &)him).aggregator_as() && ((AS4AggregatorAttribute &)*this).route_aggregator() == ((AS4AggregatorAttribute &)him).route_aggregator() ); case COMMUNITY: // XXX using encode for this is a little inefficient mybuflen = hisbuflen = 4096; ((const CommunityAttribute*)this)->encode(mybuf, mybuflen, NULL); ((const CommunityAttribute&)him).encode(hisbuf, hisbuflen, NULL); if (mybuflen != hisbuflen) return false; return memcmp(mybuf, hisbuf, mybuflen) == 0; case ORIGINATOR_ID: return ( ((OriginatorIDAttribute &)*this).originator_id() == ((OriginatorIDAttribute &)him).originator_id() ); case CLUSTER_LIST: // XXX using encode for this is a little inefficient mybuflen = hisbuflen = 4096; ((const ClusterListAttribute*)this)->encode(mybuf, mybuflen, NULL); ((const ClusterListAttribute&)him).encode(hisbuf, hisbuflen, NULL); if (mybuflen != hisbuflen) return false; return memcmp(mybuf, hisbuf, mybuflen) == 0; case MP_REACH_NLRI: // XXX using encode for this is a little inefficient mybuflen = hisbuflen = 4096; if (dynamic_cast*>(this)) { ((const MPReachNLRIAttribute*)this) ->encode(mybuf, mybuflen, NULL); ((const MPReachNLRIAttribute&)him) .encode(hisbuf, hisbuflen, NULL); } else { ((const MPReachNLRIAttribute*)this) ->encode(mybuf, mybuflen, NULL); ((const MPReachNLRIAttribute&)him) .encode(hisbuf, hisbuflen, NULL); } if (mybuflen != hisbuflen) return false; return memcmp(mybuf, hisbuf, mybuflen) == 0; case MP_UNREACH_NLRI: // XXX using encode for this is a little inefficient mybuflen = hisbuflen = 4096; if (dynamic_cast*>(this)) { ((const MPUNReachNLRIAttribute*)this) ->encode(mybuf, mybuflen, NULL); ((const MPUNReachNLRIAttribute&)him) .encode(hisbuf, hisbuflen, NULL); } else { ((const MPUNReachNLRIAttribute*)this) ->encode(mybuf, mybuflen, NULL); ((const MPUNReachNLRIAttribute&)him) .encode(hisbuf, hisbuflen, NULL); } if (mybuflen != hisbuflen) return false; return memcmp(mybuf, hisbuf, mybuflen) == 0; default: /* this must be an unknown attribute */ XLOG_ASSERT(dynamic_cast(this) != 0); mybuflen = hisbuflen = BGPPacket::MAXPACKETSIZE; ((const UnknownAttribute*)this)->encode(mybuf, mybuflen, NULL); ((const UnknownAttribute&)him).encode(hisbuf, hisbuflen, NULL); if (mybuflen != hisbuflen) { return false; } return memcmp(mybuf, hisbuf, mybuflen) == 0; } XLOG_UNREACHABLE(); } uint8_t * PathAttribute::set_header(uint8_t *data, size_t payload_size, size_t &wire_size) const { uint8_t final_flags = _flags; if (payload_size > 255) final_flags |= Extended; else final_flags &= ~Extended; data[0] = final_flags & ValidFlags; data[1] = type(); if (final_flags & Extended) { data[2] = (payload_size>>8) & 0xff; data[3] = payload_size & 0xff; wire_size = payload_size + 4; return data + 4; } else { data[2] = payload_size & 0xff; wire_size = payload_size + 3; return data + 3; } } /************************************************************************** **** **** PathAttributeList **** **************************************************************************/ //#define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #include "bgp_module.h" #include "libxorp/xlog.h" #include "libxorp/ipv4.hh" #include "libxorp/ipv6.hh" #define PARANOID template PathAttributeList::PathAttributeList() : _refcount(0), _managed_refcount(0) { debug_msg("%p\n", this); _canonical_data = 0; _canonical_length = 0; } template PathAttributeList::PathAttributeList(const PathAttributeList& palist) : _refcount(0), _managed_refcount(0) { debug_msg("%p\n", this); _canonical_length = palist._canonical_length; _canonical_data = new uint8_t[_canonical_length]; memcpy(_canonical_data, palist._canonical_data, _canonical_length); } template PathAttributeList::PathAttributeList(FPAListRef& fpa_list) : _refcount(0), _managed_refcount(0) { fpa_list->canonicalize(); _canonical_length = fpa_list->canonical_length(); _canonical_data = new uint8_t[_canonical_length]; memcpy(_canonical_data, fpa_list->canonical_data(), _canonical_length); } template PathAttributeList::~PathAttributeList() { //debug_msg("%p %s\n", this, str().c_str()); XLOG_ASSERT(_refcount == 0); if (_canonical_data) delete[] _canonical_data; } template string PathAttributeList::str() const { // this isn't very efficient - better not to call this unless you really need it. PAListRef pal(this); FastPathAttributeList fast_pa_list(pal); return fast_pa_list.str(); } template<> bool PathAttributeList:: operator< (const PathAttributeList &him) const { // first we compare on nexthop int result; result = memcmp(_canonical_data, him.canonical_data(), 7); if (result < 0) return true; else if (result > 0) return false; if (_canonical_length < him.canonical_length()) return true; if (_canonical_length > him.canonical_length()) return false; return (memcmp(_canonical_data+7, him.canonical_data()+7, _canonical_length-7) < 0); } template<> bool PathAttributeList:: operator< (const PathAttributeList &him) const { // first we compare on nexthop int result; result = memcmp(_canonical_data, him.canonical_data(), 19); if (result < 0) return true; else if (result > 0) return false; if (_canonical_length < him.canonical_length()) return true; if (_canonical_length > him.canonical_length()) return false; XLOG_ASSERT(_canonical_length >= 19); return (memcmp(_canonical_data+19, him.canonical_data()+19, _canonical_length-19) < 0); } template bool PathAttributeList:: operator== (const PathAttributeList &him) const { if (_canonical_length != him.canonical_length()) return false; return (memcmp(_canonical_data, him.canonical_data(), _canonical_length) == 0); } #if 0 template void PathAttributeList::replace_attribute(PathAttribute* new_att) { debug_msg("%p\n", this); PathAttType type = new_att->type(); switch (new_att->type()) { default: break; case ORIGIN: _origin_att = (OriginAttribute *)new_att; break; case AS_PATH: _aspath_att = (ASPathAttribute *)new_att; break; case NEXT_HOP: _nexthop_att = (NextHopAttribute *)new_att; break; } debug_msg("Replace attribute\n"); debug_msg("Before: \n%s\n", str().c_str()); debug_msg("New Att: %s\n", new_att->str().c_str()); for (iterator i = begin(); i != end() ; ++i) if ((*i)->type() == type) { insert(i, new_att); delete (*i); erase(i); debug_msg("After: \n%s\n", str().c_str()); memset(_hash, 0, sizeof(_hash)); return; } XLOG_UNREACHABLE(); } #endif #if 0 /* * Are all the mandatory attributes there? */ template bool PathAttributeList::complete() const { FastPathAttributeList fast_pa_list(const_cast>(*this)); return fast_pa_list.complete(); } #endif /************************************************************************** **** **** PAListRef **** **************************************************************************/ template PAListRef::PAListRef(const PathAttributeList* palist) { _palist = palist; if (palist) { palist->incr_refcount(1); } } template PAListRef::PAListRef(const PAListRef& palistref) { _palist = palistref.attributes(); if (_palist) { _palist->incr_refcount(1); } } template PAListRef::~PAListRef() { if (_palist) { _palist->decr_refcount(1); _palist = 0; } } template PAListRef& PAListRef::operator=(const PAListRef& palistref) { if (_palist) { // we're not empty if (_palist != palistref.attributes()) { // it's not a no-op _palist->decr_refcount(1); _palist = palistref.attributes(); _palist->incr_refcount(1); } } else { // we were previously empty _palist = palistref.attributes(); if (_palist) { _palist->incr_refcount(1); } } return *this; } template bool PAListRef::operator==(const PAListRef& palistref) const { if (_palist == palistref.attributes()) { // identical pointers return true; } if (_palist && palistref.attributes()) { // compare the content. return *_palist == *(palistref.attributes()); } // if we got here, one or the other pointer is null, but not both. return false; } template bool PAListRef::operator<(const PAListRef& palistref) const { debug_msg("PAListRef::operator< %p vs %p\n", _palist, palistref.attributes()); if (_palist == palistref.attributes()) { // identical pointers debug_msg("identical ptrs -> false\n"); return false; } if (_palist && palistref.attributes()) { // compare the content. debug_msg("comparing content\n"); bool result = (*_palist) < (*(palistref.attributes())); #if 0 printf("first: %s\n", _palist->str().c_str()); printf("second: %s\n", palistref.attributes()->str().c_str()); if (result) printf("true\n"); else printf("false\n"); #endif return result; } // if we got here, one or the other pointer is null, but not both. // This is an odd case - the order is arbitrary debug_msg("one is null\n"); if (_palist) { return true; } else { return false; } } template void PAListRef::register_with_attmgr() { XLOG_ASSERT(_palist); PAListRef found_palist = _att_mgr->add_attribute_list(*this); if (_palist == found_palist.attributes()) { // we already held a copy of the stored version return; } else { // there's already a copy stored and it's not our one _palist->decr_refcount(1); _palist = found_palist.attributes(); _palist->incr_refcount(1); } } template void PAListRef::deregister_with_attmgr() { XLOG_ASSERT(_palist); _att_mgr->delete_attribute_list(*this); } template void PAListRef::release() { if (_palist) { _palist->decr_refcount(1); _palist = 0; } } /************************************************************************** **** **** FastPathAttributeList **** **************************************************************************/ template FastPathAttributeList::FastPathAttributeList() : _slave_pa_list(0), _attribute_count(0), _locked(false), _canonical_data(0), _canonical_length(0), _canonicalized(false) { _att.resize(MAX_ATTRIBUTE+1); for (int i = 0; i <= MAX_ATTRIBUTE; i++) { _att_bytes[i] = 0; _att_lengths[i] = 0; _att[i] = 0; } } template FastPathAttributeList::FastPathAttributeList(PAListRef& palistref) : _slave_pa_list(palistref), _attribute_count(0), _locked(false), _canonical_data(0), _canonical_length(0), _canonicalized(false) { _att.resize(MAX_ATTRIBUTE+1); for (int i = 0; i <= MAX_ATTRIBUTE; i++) { _att_bytes[i] = 0; _att_lengths[i] = 0; _att[i] = 0; } quick_decode(_slave_pa_list->canonical_data(), _slave_pa_list->canonical_length()); count_attributes(); } template FastPathAttributeList::FastPathAttributeList(FastPathAttributeList& him) : _slave_pa_list(him._slave_pa_list), _locked(false), _canonical_data(0), _canonical_length(0), _canonicalized(false) { _att.resize(MAX_ATTRIBUTE+1); for (int i = 0; i <= MAX_ATTRIBUTE; i++) { _att_bytes[i] = him._att_bytes[i]; _att_lengths[i] = him._att_lengths[i]; if (him._att[i]) { _att[i] = him._att[i]->clone(); } } count_attributes(); } template FastPathAttributeList:: FastPathAttributeList(const NextHopAttribute &nexthop_att, const ASPathAttribute &aspath_att, const OriginAttribute &origin_att) : _slave_pa_list(), _attribute_count(0), _locked(false), _canonical_data(0), _canonical_length(0), _canonicalized(false) { debug_msg("%p\n", this); _att.resize(MAX_ATTRIBUTE+1); for (int i = 0; i <= MAX_ATTRIBUTE; i++) { _att_bytes[i] = 0; _att_lengths[i] = 0; _att[i] = 0; } add_path_attribute(origin_att); add_path_attribute(nexthop_att); add_path_attribute(aspath_att); } template FastPathAttributeList::~FastPathAttributeList() { XLOG_ASSERT(!_locked); if (_canonical_data) delete[] _canonical_data; for (int i = 0; i <= MAX_ATTRIBUTE; i++) { if (_att[i]) delete _att[i]; } } template bool FastPathAttributeList::operator==(const FastPathAttributeList& him) const { canonicalize(); him.canonicalize(); if (_canonical_length != him.canonical_length()) return false; if (memcmp(_canonical_data, him.canonical_data(), _canonical_length)==0) return true; return false; } template void FastPathAttributeList::quick_decode(const uint8_t* data, uint16_t length) { XLOG_ASSERT(!_locked); _canonicalized = false; size_t remaining_length = length; size_t att_length; size_t tlv_att_length; while (remaining_length > 0) { size_t hdr_len; // compute length, which is 1 or 2 bytes depending on flags d[0] if ( (data[0] & PathAttribute::Extended) && remaining_length < 4) { xorp_throw(CorruptMessage, c_format("PathAttribute (extended) too short %u bytes", XORP_UINT_CAST(remaining_length)), UPDATEMSGERR, ATTRLEN, data, remaining_length); } if (data[0] & PathAttribute::Extended) { att_length = (data[2]<<8) + data[3]; hdr_len = 4; } else { att_length = data[2]; hdr_len = 3; } tlv_att_length = att_length + hdr_len; if (remaining_length < tlv_att_length) { xorp_throw(CorruptMessage, c_format("PathAttribute too short %u bytes need %u", XORP_UINT_CAST(remaining_length), XORP_UINT_CAST(tlv_att_length)), UPDATEMSGERR, ATTRLEN, data, remaining_length); } uint8_t att_type = data[1]; if (att_type <= MAX_ATTRIBUTE) { _att_bytes[att_type] = data; _att_lengths[att_type] = tlv_att_length; } data += tlv_att_length; remaining_length -= tlv_att_length; } } template NextHopAttribute* FastPathAttributeList::nexthop_att() { return (NextHopAttribute*)find_attribute_by_type(NEXT_HOP); } template A& FastPathAttributeList::nexthop() { NextHopAttribute* nha = nexthop_att(); return nha->nexthop(); } template OriginAttribute* FastPathAttributeList::origin_att() { return (OriginAttribute*)find_attribute_by_type(ORIGIN); } template OriginType FastPathAttributeList::origin() { const OriginAttribute* oa = origin_att(); return oa->origin(); } template ASPathAttribute* FastPathAttributeList::aspath_att() { return (ASPathAttribute*)find_attribute_by_type(AS_PATH); } template AS4PathAttribute* FastPathAttributeList::as4path_att() { return (AS4PathAttribute*)find_attribute_by_type(AS4_PATH); } template ASPath& FastPathAttributeList::aspath() { ASPathAttribute* aspa = aspath_att(); return aspa->as_path(); } template void FastPathAttributeList::load_raw_data(const uint8_t *data, size_t size, const BGPPeerData* peerdata, bool have_nlri, BGPMain *mainprocess, bool do_checks) { debug_msg("FastPathAttributeList::load_raw_data\n"); XLOG_ASSERT(!_locked); _canonicalized = false; bool have_ipv4_nlri = have_nlri; // We need to decode the data into a set of path attributes, then // store the canonical form. int pa_count = 0; size_t pa_len = size; while (pa_len > 0) { size_t used = 0; PathAttribute *pa = PathAttribute::create(data, pa_len, used, peerdata, A::ip_version()); debug_msg("attribute size %u\n", XORP_UINT_CAST(used)); if (used == 0) { xorp_throw(CorruptMessage,"Attribute Size", UPDATEMSGERR, ATTRLEN); } debug_msg("Decoded Attribute: %s\n", pa->str().c_str()); pa_count++; uint32_t type = pa->type(); XLOG_ASSERT(type <= 255); if (type > _att.size()) { // hopefully this shouldn't happen often, and only when // new Path Attributes are standardized _att.resize(type+1, 0); } // check it's not a duplicate if (_att[type] != 0) { // we've got a duplicate! debug_msg("duplicate PA list entry, type: %d\n", (int)type); xorp_throw(CorruptMessage,"Duplicate PA list entry", UPDATEMSGERR, MALATTRLIST); } // seeing as we've decoded to a PathAttribute, store it for now. _att[type] = pa; // Check it's not an unknown well-known attribute if ((dynamic_cast(_att[type]) != 0) && _att[type]->well_known()) { uint8_t buf[8192]; size_t wire_size = 8192; pa->encode(buf, wire_size, peerdata); xorp_throw(CorruptMessage,"Unknown well-known attribute", UPDATEMSGERR, UNRECOGWATTR, buf, wire_size); } if (type <= MAX_ATTRIBUTE) { // we don't yet have a canonical form stored, so these // accessor shortcuts are not yet usable _att_bytes[type] = 0; _att_lengths[type] = 0; } data += used; pa_len -= used; } /* ** Check for multiprotocol parameters that haven't been "negotiated". ** ** There are two questions that seem to ambiguous in the BGP specs. ** ** 1) How do we deal with a multiprotocol attribute that hasn't ** been "negotiated". ** a) Strip out the offending attribute. ** b) Send a notify and drop the peering. ** 2) In order to accept a multiprotol attribute. I.E to consider ** it to have been "negotiated", what is the criteria. ** a) Is it sufficient for this speaker to have announced the ** capability. ** b) Do both speakers in a session need to announce a capability. ** ** For the time being we are going with 1) (a) and 2 (a). */ // BGPPeerData::Direction dir = BGPPeerData::NEGOTIATED; BGPPeerData::Direction dir = BGPPeerData::SENT; // Check the multiprotocol NLRI info. if (_att[MP_REACH_NLRI]) { // XXXX this duplicates the behaviour of older code, but I'm // not convinced it's correct. It assumes there's only one // AFI/SAFI in the attribute. MPReachNLRIAttribute* mp4_reach_att = dynamic_cast*>(_att[MP_REACH_NLRI]); if (mp4_reach_att) { Safi safi = mp4_reach_att->safi(); // if we didn't negotiate the SAFI, remove the attribute if (!peerdata->template multiprotocol(safi, dir)) { delete _att[MP_REACH_NLRI]; _att[MP_REACH_NLRI] = 0; } else { have_nlri = true; // if there's an NLRI, there must be a non-zero nexthop if (do_checks && mp4_reach_att->nexthop() == IPv4::ZERO()) { uint8_t data = NEXT_HOP; xorp_throw(CorruptMessage,"Illegal nexthop", UPDATEMSGERR, MISSWATTR, &data, 1); } if (mainprocess && mainprocess->interface_address4(mp4_reach_att->nexthop())) { XLOG_ERROR("Nexthop in update belongs to this router:\n %s", cstring(*this)); xorp_throw(UnusableMessage, "Nexthop belongs to this router"); } } } #ifdef HAVE_IPV6 MPReachNLRIAttribute* mp6_reach_att = dynamic_cast*>(_att[MP_REACH_NLRI]); if (mp6_reach_att) { Safi safi = mp6_reach_att->safi(); // if we didn't negotiate the SAFI, remove the attribute if (!peerdata->template multiprotocol(safi, dir)) { delete _att[MP_REACH_NLRI]; _att[MP_REACH_NLRI] = 0; } else { have_nlri = true; if (do_checks && mp6_reach_att->nexthop() == IPv6::ZERO()) { uint8_t data = NEXT_HOP; // if there's an NLRI, there must be a non-zero nexthop xorp_throw(CorruptMessage,"Illegal nexthop", UPDATEMSGERR, MISSWATTR, &data, 1); } if (do_checks && mainprocess && mainprocess->interface_address6(mp6_reach_att->nexthop())) { XLOG_ERROR("Nexthop in update belongs to this router:\n %s", cstring(*this)); xorp_throw(UnusableMessage, "Nexthop6 belongs to this router"); } } } #endif MPUNReachNLRIAttribute* mp4_unreach_att = dynamic_cast*>(_att[MP_UNREACH_NLRI]); if (mp4_unreach_att) { Safi safi = mp4_unreach_att->safi(); // if we didn't negotiate the SAFI, remove the attribute if (!peerdata->template multiprotocol(safi, dir)) { delete _att[MP_UNREACH_NLRI]; _att[MP_UNREACH_NLRI] = 0; } } MPUNReachNLRIAttribute* mp6_unreach_att = dynamic_cast*>(_att[MP_UNREACH_NLRI]); if (mp6_unreach_att) { Safi safi = mp6_unreach_att->safi(); // if we didn't negotiate the SAFI, remove the attribute if (!peerdata->template multiprotocol(safi, dir)) { delete _att[MP_UNREACH_NLRI]; _att[MP_UNREACH_NLRI] = 0; } } } // If we have NLRI entries, the PA list must be non-empty. If we // don't have NLRI entries, we don't need PA list entries (in // which case we won't keep this instance) unless we have // Multiprotocol Attribute entries which serve the role of NLRI // entries. if (pa_count == 0 && have_nlri) { debug_msg("Empty path attribute list and " "non-empty NLRI list\n"); xorp_throw(CorruptMessage,"Illegal nexthop", UPDATEMSGERR, MALATTRLIST); } /* ** If a NLRI attribute is present check for the following ** mandatory fields: ** ORIGIN ** AS_PATH ** NEXT_HOP */ if (do_checks && have_nlri) { if (_att[ORIGIN] == NULL) { debug_msg("Missing ORIGIN\n"); uint8_t data = ORIGIN; xorp_throw(CorruptMessage,"Missing Origin", UPDATEMSGERR, MISSWATTR, &data, 1); } // The AS Path attribute is mandatory if (_att[AS_PATH] == NULL) { debug_msg("Missing AS_PATH\n"); uint8_t data = AS_PATH; xorp_throw(CorruptMessage,"Missing AS Path", UPDATEMSGERR, MISSWATTR, &data, 1); } // The NEXT_HOP attribute is mandatory for IPv4 unicast. For // multiprotocol NLRI its in the path attribute - we checked // this above. if (have_ipv4_nlri && _att[NEXT_HOP] == NULL) { debug_msg("Missing NEXT_HOP\n"); uint8_t data = NEXT_HOP; xorp_throw(CorruptMessage,"Missing Next Hop", UPDATEMSGERR, MISSWATTR, &data, 1); } } // If we got this far and as_path_attr is not set this is a // multiprotocol withdraw. if (_att[AS_PATH] != NULL) { if (!peerdata->ibgp()) { // If this is an EBGP peering, the AS Path MUST NOT be empty if (((ASPathAttribute*)_att[AS_PATH])->as_path().path_length() == 0) xorp_throw(CorruptMessage,"Empty AS Path", UPDATEMSGERR, MALASPATH); // If this is an EBGP peering, the AS Path MUST start // with the AS number of the peer. AsNum my_asnum(peerdata->as()); if (((ASPathAttribute*)_att[AS_PATH])->as_path().first_asnum() != my_asnum) xorp_throw(CorruptMessage,"AS path must list peer", UPDATEMSGERR, MALASPATH); // If this is an EBGP peering and a route reflector // attribute has been received then generate an error. if (_att[CLUSTER_LIST] || _att[ORIGINATOR_ID]) xorp_throw(CorruptMessage,"RR on EBGP peering", UPDATEMSGERR, MALATTRLIST); } // Receiving confederation path segments when the router // is not configured for confederations is an error. if (!peerdata->confederation() && ((ASPathAttribute*)_att[AS_PATH])->as_path().contains_confed_segments()) xorp_throw(CorruptMessage,"Unexpected confederation", UPDATEMSGERR, MALASPATH); } // If an update message is received that contains a nexthop // that belongs to this router then discard the update, don't // send a notification. if (_att[NEXT_HOP] != NULL) { if (mainprocess && mainprocess->interface_address4(((NextHopAttribute*)_att[NEXT_HOP])->nexthop())) { XLOG_ERROR("Nexthop in update belongs to this router:\n %s", cstring(((NextHopAttribute*)_att[NEXT_HOP])->nexthop())); xorp_throw(UnusableMessage, "Nexthop belongs to this router"); } } count_attributes(); } template void FastPathAttributeList::canonicalize() const { if (_canonicalized) return; // allocate some extra space because canonical form can be a little longer than wire form. size_t remaining_space = 2 * BGPPacket::MAXPACKETSIZE; size_t size_so_far = 0; uint8_t buf[remaining_space]; uint8_t *p = buf; for (uint32_t i = 0; i < _att.size(); i++) { // reorder so nexthop is first, so we can find nexthops when // IGP distance changes uint32_t type = att_order(i); // make sure we really have decoded this before canonicalizing it if (_att_bytes[type] && !_att[type]) { // we're got data for an attribute, but not decoded it yet // this must have come from a canonicalize PA list in the // first place, because if we received it from a peer, // we'd have decoded everything size_t length = _att_lengths[type]; memcpy(p, _att_bytes[type], length); p += length; XLOG_ASSERT(remaining_space >= length); remaining_space -= length; size_so_far += length; } else { // encode each attribute if (_att[type]) { size_t length = remaining_space; // encode the most general peer-independent version if (!(_att[type]->encode(p, length, NULL))) { // we ran out of space to encode; this can't happen XLOG_UNREACHABLE(); } p += length; XLOG_ASSERT(remaining_space >= length); remaining_space -= length; size_so_far += length; } } } bool alloc_space = true; if (_canonical_data) { if (_canonical_length >= size_so_far) { // we've got enough space already alloc_space = false; } else { delete[] _canonical_data; } } if (alloc_space) this->_canonical_data = new uint8_t[size_so_far]; memcpy(this->_canonical_data, buf, size_so_far); this->_canonical_length = size_so_far; _canonicalized = true; } template void FastPathAttributeList::add_path_attribute(const PathAttribute &att) { debug_msg("%p %s\n", this, att.str().c_str()); PathAttribute *a = att.clone(); add_path_attribute(a); } template void FastPathAttributeList::add_path_attribute(PathAttribute *a) { uint8_t type = a->type(); _canonicalized = false; debug_msg("%p %s\n", this, a->str().c_str()); XLOG_ASSERT(!_locked); #if 0 // we shouldn't need this code if (_att.size() <= type) { size_t old_size = _att.size(); _att.resize(type + 1); printf("adding att of type %u, old size was %u, new size is %u\n", (uint32_t)type, (uint32_t)old_size, (uint32_t)_att.size()); for (size_t i = old_size; i < _att.size(); i++) { _att[i] = 0; } } #endif if (_att[type]) { XLOG_ERROR("ERROR: Attribute type: %d already exists. Currently, only a single" " attribute for each type is supported. Deleting old one and adding" " this new one.", (int)(type)); delete _att[type]; _att[type] = a; } else { _att[type] = a; _attribute_count++; } } template PathAttribute* FastPathAttributeList::find_attribute_by_type(PathAttType type) { if (_att[type]) { return _att[type]; } if (_att_bytes[type] == 0) return 0; // we're got data for an attribute, but not decoded it yet size_t used = _att_lengths[type]; PathAttribute *pa = PathAttribute::create(_att_bytes[type], _att_lengths[type], used, NULL, A::ip_version()); _att[type] = pa; return pa; } template void FastPathAttributeList::replace_attribute(PathAttribute* new_att) { debug_msg("%p\n", this); XLOG_ASSERT(!_locked); XLOG_ASSERT(new_att); _canonicalized = false; XLOG_ASSERT(_att[new_att->type()] != 0 || _att_bytes[new_att->type()] != 0); if (_att[new_att->type()]) { // remove the old decoded version delete _att[new_att->type()]; } else { // we hadn't yet decoded it; now we never will _att_bytes[new_att->type()] = 0; _att_lengths[new_att->type()] = 0; } _att[new_att->type()] = new_att; } template void FastPathAttributeList::replace_AS_path(const ASPath& new_as_path) { debug_msg("%p\n", this); replace_attribute(new ASPathAttribute(new_as_path)); } template void FastPathAttributeList::replace_nexthop(const A& new_nexthop) { debug_msg("%p\n", this); replace_attribute(new NextHopAttribute(new_nexthop)); } template void FastPathAttributeList::replace_origin(const OriginType& new_origin) { debug_msg("%p\n", this); replace_attribute(new OriginAttribute(new_origin)); } template void FastPathAttributeList::remove_attribute_by_type(PathAttType type) { debug_msg("%p\n", this); XLOG_ASSERT(!_locked); _canonicalized = false; bool old_existed = false; if (_att[type]) { delete _att[type]; _att[type] = 0; old_existed = true; } if (_att_bytes[type]) { _att_bytes[type] = 0; _att_lengths[type] = 0; old_existed = true; } if (old_existed) _attribute_count--; } template void FastPathAttributeList::remove_attribute_by_pointer(PathAttribute *p) { debug_msg("%p\n", this); XLOG_ASSERT(!_locked); remove_attribute_by_type(p->type()); } template MEDAttribute* FastPathAttributeList::med_att() { debug_msg("%p\n", this); return (MEDAttribute*)find_attribute_by_type(MED); } template LocalPrefAttribute* FastPathAttributeList::local_pref_att() { debug_msg("%p\n", this); return (LocalPrefAttribute*)find_attribute_by_type(LOCAL_PREF); } template AtomicAggAttribute* FastPathAttributeList::atomic_aggregate_att() { debug_msg("%p\n", this); return (AtomicAggAttribute*)find_attribute_by_type(ATOMIC_AGGREGATE); } template AggregatorAttribute* FastPathAttributeList::aggregator_att() { debug_msg("%p\n", this); return (AggregatorAttribute*)find_attribute_by_type(AGGREGATOR); } template CommunityAttribute* FastPathAttributeList::community_att() { debug_msg("%p\n", this); return (CommunityAttribute*)find_attribute_by_type(COMMUNITY); } template OriginatorIDAttribute* FastPathAttributeList::originator_id() { debug_msg("%p\n", this); return (OriginatorIDAttribute*)find_attribute_by_type(ORIGINATOR_ID); } template ClusterListAttribute* FastPathAttributeList::cluster_list() { debug_msg("%p\n", this); return (ClusterListAttribute*)find_attribute_by_type(CLUSTER_LIST); } template void FastPathAttributeList::process_unknown_attributes() { debug_msg("%p\n", this); debug_msg("process_unknown_attributes\n"); for (uint32_t i = 0; i < _att.size(); i++) { if (_att[i] && dynamic_cast(_att[i])) { if (_att[i]->transitive()) { _att[i]->set_partial(); } else { delete _att[i]; _att[i] = 0; } } } } /* * encode the PA list for transmission to this specific peer. */ template bool FastPathAttributeList::encode(uint8_t* buf, size_t &wire_size, const BGPPeerData* peerdata) const { size_t len_so_far = 0; size_t attr_len; // fill path attribute list for (uint32_t i = 0; i < _att.size(); i++) { uint32_t type = i; attr_len = wire_size - len_so_far; if (_att[type]) { if (_att[type]->encode(buf + len_so_far, attr_len, peerdata) ) { len_so_far += attr_len; XLOG_ASSERT(len_so_far <= wire_size); } else { // not enough space to encode the PA list. return false; } } else if (_att_bytes[type]) { // We've got an attribute that hasn't yet been decoded, // which means it can't have changed. Often we can // shortcut the encoding process and just use the // previously encoded version. if (encode_and_decode_attribute(_att_bytes[type], _att_lengths[type], buf + len_so_far, attr_len, peerdata)) { len_so_far += attr_len; XLOG_ASSERT(len_so_far <= wire_size); } else { // not enough space to encode the PA list. return false; } } } if (peerdata->we_use_4byte_asnums() && !peerdata->use_4byte_asnums()) { // we're using 4byte AS nums, but our peer isn't so we need to // add an AS4Path attribute XLOG_ASSERT(_att[AS_PATH]); // surely we've decoded this by now? if (!((ASPathAttribute*)_att[AS_PATH])->as_path().two_byte_compatible()) { // only add the AS4Path if we can't code the ASPath without losing information attr_len = wire_size - len_so_far; AS4PathAttribute as4path_att(((ASPathAttribute*)_att[AS_PATH])->as4_path()); if (as4path_att.encode(buf + len_so_far, attr_len, peerdata) ) { len_so_far += attr_len; XLOG_ASSERT(len_so_far <= wire_size); } else { // not enough space to encode the PA list. return false; } } // XXXXX we should do something here for AS4Aggregator if // we're aggregating } wire_size = len_so_far; return true; } template bool FastPathAttributeList::encode_and_decode_attribute(const uint8_t* att_data, const size_t& att_len, uint8_t *buf, size_t& wire_size , const BGPPeerData* peerdata) const { PathAttribute *pa; bool use_4byte_asnums = peerdata->use_4byte_asnums(); switch (att_data[1]) { // depending on type, do the right thing. case AS_PATH: case AGGREGATOR: // AS Path and Aggregator can be encoded differently for each // peer, so we need to decode and re-encode if we are not // using 4-byte AS nums. if (use_4byte_asnums) { if (wire_size < att_len) return false; memcpy(buf, att_data, att_len); wire_size = att_len; return true; } else { if (att_data[1] == AS_PATH) { ASPathAttribute as_path_att(att_data, use_4byte_asnums); return as_path_att.encode(buf, wire_size, peerdata); } else { AggregatorAttribute agg_att(att_data, use_4byte_asnums); return agg_att.encode(buf, wire_size, peerdata); } } case AS4_PATH: case AS4_AGGREGATOR: case MP_REACH_NLRI: case MP_UNREACH_NLRI: // these can't exist in the canonical version, because we use // 4-byte AS nums internally and speak MP XLOG_UNREACHABLE(); default: // all the remainder encode identically for all peers if (wire_size < att_len) return false; memcpy(buf, att_data, att_len); wire_size = att_len; return true; } return pa; } template string FastPathAttributeList::str() const { // Don't change this formatting or the isolation tests will fail. string s; for (uint32_t i = 0; i < _att.size(); i++) { uint32_t type = att_order(i); if (_att[type]) { s += "\n\t" + _att[type]->str(); } else if(_att_lengths[type]>0) { try { // we've got data for an attribute, but not decoded it yet size_t used = _att_lengths[type]; PathAttribute *pa = PathAttribute::create(_att_bytes[type], _att_lengths[type], used, NULL, A::ip_version()); _att[type] = pa; s += "\n\t" + _att[type]->str(); } catch (const XorpException& e) { s += "\n\tException: " + e.str(); } } } return s; } template class FastPathAttributeList; template class FastPathAttributeList; template class PathAttributeList; template class PathAttributeList; template class PAListRef; template class PAListRef; xorp/bgp/run_tests0000775000076400007640000000413711421137511014405 0ustar greearbgreearb#!/bin/sh # # $XORP: xorp/bgp/run_tests,v 1.1.1.1 2002/12/11 23:55:50 hodson Exp $ # case `hostname` in x20) MESS="Running tests on x20" CONFIG_FILE=configs/config-x20.internal.hiddennet.net.bgp 2>error ;; x20.internal.hiddennet.net) MESS="Running tests on x20" CONFIG_FILE=configs/config-x20.internal.hiddennet.net.bgp 2>error ;; xorp0) MESS="Running tests on xorp0" CONFIG_FILE=configs/config-xorp0.icir.org.bgp ;; xorp1) MESS="Running tests on xorp1" CONFIG_FILE=configs/config-xorp1.icir.org.bgp ;; xorp2) MESS="Running tests on xorp2" CONFIG_FILE=configs/config-xorp2.icir.org.bgp ;; xorp3) MESS="Running tests on xorp3" CONFIG_FILE=configs/config-xorp3.icir.org.bgp ;; xorp4) MESS="Running tests on xorp4" CONFIG_FILE=configs/config-xorp4.icir.org.bgp ;; xorp6) MESS="Running tests on xorp6" CONFIG_FILE=configs/config-xorp6.icir.org.bgp ;; xorp7) MESS="Running tests on xorp7" CONFIG_FILE=configs/config-xorp7.icir.org.bgp ;; tigger.icir.org) MESS="Running tests on tigger" CONFIG_FILE=configs/config-tigger.icir.org.bgp ;; aardvark.icir.org) MESS="Running tests on aardvark" CONFIG_FILE=configs/config-aardvark.icir.org.bgp ;; www.icir.org) MESS="Running tests on www" CONFIG_FILE=configs/config-www.icir.org.bgp ;; lemming.icir.org) MESS="Running tests on lemming" CONFIG_FILE=configs/config-lemming.icir.org.bgp ;; thinmint.icir.org) MESS="Running tests on thinmint" ;; *) echo "Unknown host :" `hostname` exit 1 ;; esac echo $MESS MALLOC_OPTIONS="AJX" export MALLOC_OPTIONS # # Any argument to this script will cause the XRL code to be used to # configure the bgp process. # if [ $# != 1 ] then ./BGPTest $CONFIG_FILE else . ./bgp_xrl_shell_funcs.sh # Start a finder in the background. $FINDER & # Start a bgp process and then send it the XRL config commands. # We don't start the bgp process in the background as we want to # be able to kill it from the keyboard. So schedule the xrl commands # to be sent in five seconds and then start the bgp process in # the foreground. (sleep 5; bgp_xrl $CONFIG_FILE) & ./xorp_bgp fi xorp/bgp/aspath_test.cc0000664000076400007640000002406411421137511015260 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "bgp_module.h" #include "libxorp/xorp.h" #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_GETOPT_H #include #endif #include "aspath.hh" void test_string(bool verbose) { /*********************************************************************/ /**** testing construction from a string ****/ /*********************************************************************/ if (verbose) printf("Test constructing an As Path from a string\n"); ASPath aspath_str("65008,1,2,{3,4,5},6,{7,8},9"); // From string ASPath aspath_con; // Constructed { ASSegment seq = ASSegment(AS_SEQUENCE); seq.add_as(AsNum(65008)); seq.add_as(AsNum(1)); seq.add_as(AsNum(2)); aspath_con.add_segment(seq); } { ASSegment set = ASSegment(AS_SET); set.add_as(AsNum(3)); set.add_as(AsNum(4)); set.add_as(AsNum(5)); aspath_con.add_segment(set); } { ASSegment seq = ASSegment(AS_SEQUENCE); seq.set_type(AS_SEQUENCE); seq.add_as(AsNum(6)); aspath_con.add_segment(seq); } { ASSegment set = ASSegment(AS_SET); set.add_as(AsNum(7)); set.add_as(AsNum(8)); aspath_con.add_segment(set); } { ASSegment seq; seq.set_type(AS_SEQUENCE); seq.add_as(AsNum(9)); aspath_con.add_segment(seq); } assert(aspath_str == aspath_con); } void test_string_as4_compat(bool verbose) { /*********************************************************************/ /**** testing construction from a string using 4-byte syntax ****/ /*********************************************************************/ if (verbose) printf("Test2 constructing an As Path from a string using 4-byte syntax\n"); /* use 4-byte syntax for ASnums that are really 16-bit */ ASPath aspath_str("0.65008,1,2,{3,0.4,5},6,{7,8},9"); // From string ASPath aspath_con; // Constructed { ASSegment seq = ASSegment(AS_SEQUENCE); seq.add_as(AsNum(65008)); seq.add_as(AsNum(1)); seq.add_as(AsNum(2)); aspath_con.add_segment(seq); } { ASSegment set = ASSegment(AS_SET); set.add_as(AsNum(3)); set.add_as(AsNum(4)); set.add_as(AsNum(5)); aspath_con.add_segment(set); } { ASSegment seq = ASSegment(AS_SEQUENCE); seq.set_type(AS_SEQUENCE); seq.add_as(AsNum(6)); aspath_con.add_segment(seq); } { ASSegment set = ASSegment(AS_SET); set.add_as(AsNum(7)); set.add_as(AsNum(8)); aspath_con.add_segment(set); } { ASSegment seq; seq.set_type(AS_SEQUENCE); seq.add_as(AsNum(9)); aspath_con.add_segment(seq); } if (verbose) printf("Comparing\n %s with\n %s\n", aspath_str.str().c_str(), aspath_con.str().c_str()); assert(aspath_str == aspath_con); } void test_string_as4(bool verbose) { /*********************************************************************/ /**** testing construction from a string using 4-byte syntax ****/ /*********************************************************************/ if (verbose) printf("Test3 constructing an As Path from a string using 4-byte syntax\n"); /* use 4-byte syntax for ASnums that are really 16-bit */ ASPath aspath_str("2.65008,1,2,{3,2.4,5},6,{7,8},2.9"); // From string ASPath aspath_con; // Constructed { ASSegment seq = ASSegment(AS_SEQUENCE); seq.add_as(AsNum((uint32_t)196080)); seq.add_as(AsNum(1)); seq.add_as(AsNum(2)); aspath_con.add_segment(seq); } { ASSegment set = ASSegment(AS_SET); set.add_as(AsNum(3)); set.add_as(AsNum((uint32_t)131076)); set.add_as(AsNum(5)); aspath_con.add_segment(set); } { ASSegment seq = ASSegment(AS_SEQUENCE); seq.set_type(AS_SEQUENCE); seq.add_as(AsNum(6)); aspath_con.add_segment(seq); } { ASSegment set = ASSegment(AS_SET); set.add_as(AsNum(7)); set.add_as(AsNum(8)); aspath_con.add_segment(set); } { ASSegment seq; seq.set_type(AS_SEQUENCE); seq.add_as(AsNum((uint32_t)131081)); aspath_con.add_segment(seq); } if (verbose) printf("Comparing\n %s with\n %s\n", aspath_str.str().c_str(), aspath_con.str().c_str()); assert(aspath_str == aspath_con); } void test_as4_coding(bool verbose) { /*********************************************************************/ /**** testing encoding and decoding of 4-byte AS numbers ****/ /*********************************************************************/ if (verbose) printf("Test4 coding and decoding an AS4 path\n"); /* use 4-byte syntax for ASnums that are really 16-bit */ ASPath aspath("2.65008,1,2,{3,2.4,5},6,{7,8},2.9"); // From string size_t len = ((AS4Path*)(&aspath))->wire_size(); uint8_t *buf = new uint8_t[len]; ((AS4Path*)&aspath)->encode(len, buf); AS4Path dec_aspath(buf, len); if (verbose) printf("Comparing\n %s with\n %s\n", aspath.str().c_str(), dec_aspath.str().c_str()); delete []buf; assert(aspath == dec_aspath); } void test_as4_coding_compat(bool verbose) { /*********************************************************************/ /**** testing encoding and decoding of compat 4-byte AS numbers ****/ /*********************************************************************/ if (verbose) printf("Test5 coding and decoding a compat AS4 path\n"); /* construct a 4-byte AS path */ ASPath aspath("2.65008,1,2,{3,2.4,5},6,{7,8},2.9"); // 23456 is AS_TRAN ASPath compat_aspath("23456,1,2,{3,23456,5},6,{7,8},23456"); size_t len = aspath.wire_size(); uint8_t *buf = new uint8_t[len]; /* 2-byte encoding function, which should convert 4-byte-only ASnums to AS_TRAN */ aspath.encode(len, buf); ASPath dec_aspath(buf, len); if (verbose) printf("Comparing\n %s with\n %s\n", compat_aspath.str().c_str(), dec_aspath.str().c_str()); delete []buf; assert(compat_aspath == dec_aspath); } int main(int argc, char* argv[]) { int c; bool verbose = false; while ((c = getopt(argc, argv, "v")) != EOF) { switch (c) { case 'v': verbose = true; break; } } AsNum *as[13]; int i; for (i=0;i<=9;i++) { as[i] = new AsNum(i); } ASSegment seq1 = ASSegment(AS_SEQUENCE); seq1.add_as(*(as[1])); seq1.add_as(*(as[2])); seq1.add_as(*(as[3])); ASSegment seq2 = ASSegment(AS_SEQUENCE); seq2.add_as(*(as[7])); seq2.add_as(*(as[8])); seq2.add_as(*(as[9])); ASSegment set1 = ASSegment(AS_SET); set1.add_as(*(as[4])); set1.add_as(*(as[5])); set1.add_as(*(as[6])); for (i=0;i<=9;i++) { delete as[i]; } ASPath *aspath= new ASPath; aspath->add_segment(seq1); aspath->add_segment(set1); aspath->add_segment(seq2); assert(aspath->num_segments() == 3); if (verbose) printf("Original: %s\n", aspath->str().c_str()); ASPath *aspathcopy= new ASPath(*aspath); if (verbose) printf("Copy: %s\n", aspathcopy->str().c_str()); if (verbose) printf("Deleting orginal\n"); delete aspath; if (verbose) printf("Copy: %s\n", aspathcopy->str().c_str()); AsNum *asn; for (i=1;i<=9;i++) { asn = new AsNum(i); assert(aspathcopy->contains(*asn) == true); delete asn; } asn = new AsNum(AsNum::AS_INVALID); // XXX should never do this assert(aspathcopy->contains(*asn) == false); delete asn; if (verbose) printf("Testing add_As_in_sequence - adding to existing sequence\n"); asn = new AsNum(65000); aspathcopy->prepend_as(*asn); if (verbose) printf("Extended: %s\n", aspathcopy->str().c_str()); assert(aspathcopy->contains(*asn) == true); delete asn; for (i=10;i<=12;i++) { as[i] = new AsNum(i); } ASSegment set2 = ASSegment(AS_SET); set2.add_as(*(as[10])); set2.add_as(*(as[11])); set2.add_as(*(as[12])); aspath= new ASPath; aspath->add_segment(set2); aspath->add_segment(seq2); aspath->add_segment(set1); aspath->add_segment(seq1); if (verbose) printf("Testing add_As_in_sequence - adding to existing set\n"); asn = new AsNum(65001); if (verbose) printf("Before: %s\n", aspath->str().c_str()); aspath->prepend_as(*asn); if (verbose) printf("Extended: %s\n", aspath->str().c_str()); assert(aspath->contains(*as[10]) == true); assert(aspath->contains(*as[11]) == true); assert(aspath->contains(*as[12]) == true); assert(aspath->contains(*asn) == true); delete asn; for (i=10;i<=12;i++) delete as[i]; test_string(verbose); test_string_as4_compat(verbose); test_string_as4(verbose); test_as4_coding(verbose); test_as4_coding_compat(verbose); if (verbose) printf("All tests passed\n"); #if 0 printf("Check for space leak: check memory usage now....\n"); TimerList::system_sleep(TimeVal(5, 0)); printf("Continuing...\n"); aspath = new ASPath(*aspathcopy); delete aspathcopy; for(i=0; i< 10000; i++) { aspathcopy= new ASPath(*aspath); delete aspath; aspath = new ASPath(*aspathcopy); delete aspathcopy; } printf("Done...\n"); for (i=1;i<=9;i++) { asn = new AsNum(i); assert(aspath->contains(*asn) == true); delete asn; } asn = new AsNum; assert(aspath->contains(*asn) == false); delete asn; TimerList::system_sleep(TimeVal(10, 0)); #else delete aspath; delete aspathcopy; #endif exit(0); } xorp/bgp/attribute_manager.hh0000664000076400007640000000661011540224217016447 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/bgp/attribute_manager.hh,v 1.12 2008/11/08 06:14:36 mjh Exp $ #ifndef __BGP_ATTRIBUTE_MANAGER_HH__ #define __BGP_ATTRIBUTE_MANAGER_HH__ #include "path_attribute.hh" template class PathAttributeList; #if 0 template class StoredAttributeList { public: StoredAttributeList(const PathAttributeList *att) { _attribute_list = att; _references = 1; } ~StoredAttributeList() { if (_references == 0) { delete _attribute_list; } } int references() const { return _references; } void increase() { _references++; } void decrease() { _references--; } void clone_data() { _attribute_list = new PathAttributeList(*_attribute_list); } const PathAttributeList *attribute() const { return _attribute_list; } const uint8_t* hash() const { return _attribute_list->hash(); } bool operator<(const StoredAttributeList& them) const; private: const PathAttributeList *_attribute_list; int _references; }; #endif /** * Att_Ptr_Cmp is needed because we want BGPAttributeManager to use a * set of pointers, but when we check to see if something's in the set, * we want to compare the data and not the pointers themselves. This * forces the right comparison. */ template class Att_Ptr_Cmp { public: bool operator() (const PAListRef& a, const PAListRef& b) const { /* printf("Att_Ptr_Cmp [%s] < [%s] ?\n", a->attribute()->str().c_str(), b->attribute()->str().c_str());*/ #ifdef OLDWAY if (*(a->attribute()) < *(b->attribute())) { // printf("true\n"); return true; } else { // printf("false\n"); return false; } #else return a < b; #endif } }; /** * AttributeManager manages the storage of PathAttributeLists, so * that we don't store the same attribute list more than once. The * interface is simple: to store something, you give it a pointer, and * it gives you back a pointer to where it stored it. To unstore * something, you just tell it to delete it, and the undeletion is * handled for you if no-one else is still referencing a copy. */ template class AttributeManager { public: AttributeManager(); PAListRef add_attribute_list(PAListRef& attribute_list); void delete_attribute_list(PAListRef& attribute_list); int number_of_managed_atts() const { return _attribute_lists.size(); } private: // set above for explanation of Att_Ptr_Cmp set , Att_Ptr_Cmp > _attribute_lists; int _total_references; }; #endif // __BGP_ATTRIBUTE_MANAGER_HH__ xorp/bgp/TODO0000664000076400007640000000524311421137511013120 0ustar greearbgreearb# # $XORP: xorp/bgp/TODO,v 1.20 2007/03/20 21:58:36 atanu Exp $ # ------------------------------ TODO ----------------------------- 4) Make test packet code check more things, for example go through and check the results of the decoding against what was encoded. 18) Check the validity of the withdrawn routes field in check_update_packet (peer.cc) and the validity of network reachability field. ---added 2002-09-24 by mjh--- 22) Why is the BGPPeer stopped timer 10 seconds? 26) Encode method in BGPAuthParameter --added 2002-10-21 by APG--- 28) In FanoutTable's queue, we skip entries if they have the same origin peer as the output branch calling get_next_message. If the queued origin peer is NULL, we use this to indicate it should go to all peers. However, the RIB peerhandle is also NULL, so routes that come from the rib would then be sent back to the RIB, which is bad. Rewrite this code so it doesn't overload the NULL value. 30) When a next hop changes for a particular address family both the unicast and multicast decision tables will be notified. This is not a big deal. -- added 2003-10-13 by AG 35) BGP makes its own TCP connections it should be changed to make its connections through the FEA. It should be noted that BGP is using the asyncio library not the buffered asyncio library this will be adversely affecting performance. -- added 2005-03-10 by AG 36) BGP has a state of our own invention STOPPED that is used to wait for a notify message to be sent. When it is necessary to send a notify the socket could be switched to AcceptSession and the STOPPED state will no longer be required. -- added 2005-09-23 by AG 38) The way we create ChainedSubnetRoutes is inefficient - we create a subnet route, then clone it into a chained subnet route. Should add a new constructor for chained subnet route to avoid this extra copy. -- added 2008-07-04 by mjh 39) The changed flag on an InternalMessage seems to be superfluous, except for the isolation tests. Once everything is stable again, it should be removed. The same may be true of the copied flag, though this is more important for memory management. -- added 2008-07-16 by mjh 40) OriginateRouteFilter is broken (always passes). Figure out what it is for and fix it or remove it. -- added 2008-08-13 by mjh ------------------------------ TESTING -------------------------- 1) Memory exhaustion. 2) IGP Metrics. 3) Filters. 4) Route flat damping. 5) Bad updates. 6) Capability mismatches. 7) Peer up/down. 8) SAFI. 9) Synchronisation. 10) Loop detection. 11) EBGP vs IBGP. 12) Route reflector. 13) Confederations. 14) IPv6 15) Test FSM with inappropriate packets. 16) IGP i.e. RIB interactions. 17) Comparison against ideal. xorp/bgp/bgp_trie.cc0000664000076400007640000001016711421137511014533 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING #include "bgp_module.h" #include "bgp_trie.hh" template ChainedSubnetRoute:: ChainedSubnetRoute(const SubnetRoute& route, const ChainedSubnetRoute* prev) : SubnetRoute(route) { if (prev != NULL) { set_prev(prev); set_next(prev->next()); _prev->set_next(this); _next->set_prev(this); } else { _prev = this; _next = this; } } template ChainedSubnetRoute:: ChainedSubnetRoute(const ChainedSubnetRoute& original) : SubnetRoute(original) { _prev = &original; _next = original.next(); original.set_next(this); _next->set_prev(this); } template bool ChainedSubnetRoute::unchain() const { _prev->set_next(_next); _next->set_prev(_prev); return _next != this; } /*************************************************************************/ template BgpTrie::BgpTrie() { } template BgpTrie::~BgpTrie() { if (this->route_count() > 0) { XLOG_FATAL("BgpTrie being deleted while still containing data\n"); } } template typename BgpTrie::iterator BgpTrie::insert(const IPNet& net, const SubnetRoute& route) { typename PathmapType::iterator pmi = _pathmap.find(route.attributes()); const ChainedSubnetRoute* found = (pmi == _pathmap.end()) ? NULL : pmi->second; ChainedSubnetRoute* chained_rt = new ChainedSubnetRoute(route, found); // The trie will copy chained_rt. The copy constructor will insert // the copy into the chain after chained_rt. iterator iter = ((RouteTrie*)this)->insert(net, *chained_rt); if (found == NULL) { debug_msg(" on new chain"); _pathmap[route.attributes()] = &(iter.payload()); } debug_msg("\n"); chained_rt->unchain(); chained_rt->unref(); return iter; } template void BgpTrie::erase(const IPNet& net) { // unlink the node from the _pathmap chain iterator iter = this->lookup_node(net); XLOG_ASSERT(iter != this->end()); const ChainedSubnetRoute *found = &(iter.payload()); XLOG_ASSERT(iter.key() == net); XLOG_ASSERT(found->net() == net); typename PathmapType::iterator pmi = _pathmap.find(found->attributes()); if (pmi == _pathmap.end()) { XLOG_ERROR("Error deleting route for %s with attributes %s", net.str().c_str(), found->attributes()->str().c_str()); XLOG_INFO("Pathmap dump follows: \n"); for (pmi == _pathmap.begin(); pmi != _pathmap.end(); pmi++) { XLOG_INFO("%s\n\n", pmi->second->str().c_str()); } XLOG_FATAL("Exiting\n"); } if (pmi->second == found) { // this was the head node if (found->next() == found) { // it's the only node in the chain _pathmap.erase(pmi); debug_msg(" and erasing chain\n"); } else { // there are other nodes _pathmap[found->attributes()] = found->next(); found->unchain(); debug_msg(" chain remains but head moved\n"); } } else { found->unchain(); debug_msg(" chain remains\n"); } debug_msg("\n"); // now delete it from the Actual Trie ((RouteTrie*)this)->erase(iter); } template void BgpTrie::delete_all_nodes() { while (_pathmap.empty() == false) _pathmap.erase(_pathmap.begin()); ((RouteTrie*)this)->delete_all_nodes(); } template class BgpTrie; template class BgpTrie; xorp/bgp/route_table_policy_sm.cc0000664000076400007640000001036311421137511017321 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #include "bgp_module.h" #include "route_table_policy_sm.hh" #include "route_table_decision.hh" template PolicyTableSourceMatch::PolicyTableSourceMatch(const string& tablename, const Safi& safi, BGPRouteTable* parent, PolicyFilters& pfs, EventLoop& ev) : PolicyTable(tablename, safi, parent, pfs, filter::EXPORT_SOURCEMATCH), _pushing_routes(false), _dump_iter(NULL), _ev(ev) { this->_parent = parent; } template PolicyTableSourceMatch::~PolicyTableSourceMatch() { if (_dump_iter) delete _dump_iter; } template void PolicyTableSourceMatch::push_routes(list*>& peer_list) { _pushing_routes = true; _dump_iter = new DumpIterator(NULL, peer_list); debug_msg("[BGP] Push routes\n"); _dump_task = eventloop().new_task( callback(this, &PolicyTableSourceMatch::do_background_dump), XorpTask::PRIORITY_BACKGROUND, XorpTask::WEIGHT_DEFAULT); } template void PolicyTableSourceMatch::do_next_route_dump() { if (!_dump_iter->is_valid()) { end_route_dump(); return; } BGPRouteTable* parent = this->_parent; XLOG_ASSERT(parent); DecisionTable* dt = dynamic_cast*>(parent); XLOG_ASSERT(dt != NULL); if (!dt->dump_next_route(*_dump_iter)) { if (!_dump_iter->next_peer()) { end_route_dump(); return; } } } template void PolicyTableSourceMatch::end_route_dump() { debug_msg("[BGP] End of push routes\n"); delete _dump_iter; _dump_iter = NULL; _pushing_routes = false; _dump_task.unschedule(); } template EventLoop& PolicyTableSourceMatch::eventloop() { return _ev; } template bool PolicyTableSourceMatch::do_background_dump() { debug_msg("[BGP] doing a background dump step\n"); // we are done... if (!_pushing_routes) return false; // do a dump do_next_route_dump(); // continue in background... return true; } template void PolicyTableSourceMatch::peering_is_down(const PeerHandler *peer, uint32_t genid) { if (pushing_routes()) _dump_iter->peering_is_down(peer, genid); } template void PolicyTableSourceMatch::peering_went_down(const PeerHandler *peer, uint32_t genid, BGPRouteTable *caller) { if (pushing_routes()) _dump_iter->peering_went_down(peer, genid); BGPRouteTable::peering_went_down(peer, genid, caller); } template void PolicyTableSourceMatch::peering_down_complete(const PeerHandler *peer, uint32_t genid, BGPRouteTable *caller) { if (pushing_routes()) _dump_iter->peering_down_complete(peer, genid); BGPRouteTable::peering_down_complete(peer, genid, caller); } template void PolicyTableSourceMatch::peering_came_up(const PeerHandler *peer, uint32_t genid, BGPRouteTable *caller) { if (pushing_routes()) _dump_iter->peering_came_up(peer, genid); BGPRouteTable::peering_came_up(peer, genid, caller); } template bool PolicyTableSourceMatch::pushing_routes() { return _pushing_routes; } template class PolicyTableSourceMatch; template class PolicyTableSourceMatch; xorp/bgp/profile_vars.cc0000664000076400007640000000337111421137511015432 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "bgp_module.h" #include "libxorp/xorp.h" #include "libxorp/profile.hh" #include "profile_vars.hh" struct profile_vars { string var; string comment; } profile_vars[] = { {profile_message_in, "Messages entering BGP"}, {profile_route_ribin, "Routes entering BGP"}, {profile_route_rpc_in, "Routes being queued for the RIB"}, {profile_route_rpc_out, "Routes being sent to the RIB"}, {trace_message_in, "Trace Message entering BGP"}, {trace_message_out, "Trace Message leaving BGP"}, {trace_nexthop_resolution, "Trace nexthop resolution with RIB"}, {trace_policy_configure, "Trace policy introduction"}, {trace_state_change, "Trace FSM state change"}, }; void initialize_profiling_variables(Profile& p) { for (size_t i = 0; i < sizeof(profile_vars) / sizeof(struct profile_vars); i++) p.create(profile_vars[i].var, profile_vars[i].comment); } xorp/bgp/profile_vars.hh0000664000076400007640000000327411540225521015447 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/bgp/profile_vars.hh,v 1.15 2008/10/02 21:56:18 bms Exp $ #ifndef __BGP_PROFILE_VARS_HH__ #define __BGP_PROFILE_VARS_HH__ #ifndef XORP_DISABLE_PROFILE /** * Profile variables * See: profile_vars.cc for definitions. */ const string profile_message_in = "message_in"; const string profile_route_ribin = "route_ribin"; const string profile_route_rpc_in = "route_rpc_in"; const string profile_route_rpc_out = "route_rpc_out"; const string trace_message_in = "trace_message_in"; const string trace_message_out = "trace_message_out"; const string trace_nexthop_resolution = "trace_nexthop_resolution"; const string trace_policy_configure = "trace_policy_configure"; const string trace_state_change = "trace_state_change"; void initialize_profiling_variables(Profile& p); #endif // profile #endif // __BGP_PROFILE_VARS_HH__ xorp/bgp/update_test.cc0000664000076400007640000000630711421137511015262 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "bgp_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "packet.hh" #include "peer.hh" #include "bgp.hh" /* * XXX NOTE THAT THIS TEST IS CURRENTLY UNUSED. * * we try to decode a handcrafted update packet. */ uint8_t buffer[] = { // marker 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x13, // length (to be filled later) 0x02, // type 2 = update #if 0 0x00, 0x00, 0x00, 0x32, 0x40, 0x01, 0x01, 0x00, 0x40, 0x02, 0x24, 0x02, 0x11, 0xfd, 0xe8, 0xfd, 0xec, 0x41, 0x36, 0x00, 0x19, 0x2c, 0x9f, 0x01, 0x25, 0x01, 0x79, 0x01, 0x79, 0x01, 0x79, 0x01, 0x79, 0x01, 0x79, 0x01, 0x79, 0x01, 0x79, 0x01, 0x79, 0x01, 0x79, 0x01, 0x79, 0x01, 0x79, 0x40, 0x03, 0x04, 0xc0, 0x96, 0xbb, 0x02, 0x18, 0xc0, 0x96, 0xfd, 0x18, 0xc6, 0x33, 0xee, 0x18, 0xc6, 0x33, 0xef, 0x18, 0xc0, 0xbe, 0xc9, 0x18, 0xc0, 0xbe, 0xca, 0x18, 0xc0, 0xc3, 0x69, 0x18, 0xc1, 0x20, 0x10, 0x18, 0xc1, 0x20 #endif }; void test1(unsigned int as_size) { ASSegment seq1 = ASSegment(AS_SEQUENCE); for (unsigned int i = 0; i < as_size; i++) seq1.add_as(AsNum(10)); size_t len; fprintf(stderr, "trying size %u wire_size %u\n", as_size, XORP_UINT_CAST(seq1.wire_size())); const uint8_t *d = seq1.encode(len, NULL); ASSegment *seq2 = new ASSegment(d); delete[] d; ASSegment seq3(*seq2); delete seq2; } int main(int, char **argv) { XorpUnexpectedHandler x(xorp_unexpected_handler); // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); EventLoop eventloop; LocalData localdata(eventloop); Iptuple iptuple; BGPPeerData* pd = new BGPPeerData(localdata, iptuple, AsNum(0), IPv4(),0); pd->compute_peer_type(); BGPMain main(eventloop); BGPPeer peer(&localdata, pd, NULL, &main); try { UpdatePacket pac(&buffer[0], sizeof(buffer), pd, &main, true); for(int i = 0; i < 2048; i++) test1(i); } catch(...) { xorp_catch_standard_exceptions(); } // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); return 0; } xorp/bgp/route_table_debug.hh0000664000076400007640000000623411421137511016425 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/bgp/route_table_debug.hh,v 1.16 2008/11/08 06:14:38 mjh Exp $ #ifndef __BGP_ROUTE_TABLE_DEBUG_HH__ #define __BGP_ROUTE_TABLE_DEBUG_HH__ #include "route_table_base.hh" #include "libxorp/ref_trie.hh" template class DebugTable : public BGPRouteTable { public: DebugTable(string tablename, BGPRouteTable *parent); ~DebugTable(); int add_route(InternalMessage &rtmsg, BGPRouteTable *caller); int replace_route(InternalMessage &old_rtmsg, InternalMessage &new_rtmsg, BGPRouteTable *caller); int delete_route(InternalMessage &rtmsg, BGPRouteTable *caller); int push(BGPRouteTable *caller); int route_dump(InternalMessage &rtmsg, BGPRouteTable *caller, const PeerHandler *peer); const SubnetRoute *lookup_route(const IPNet &net, uint32_t& genid, FPAListRef& pa_list) const; void wakeup(); void route_used(const SubnetRoute* route, bool in_use); RouteTableType type() const {return DEBUG_TABLE;} string str() const; /* mechanisms to implement flow control in the output plumbing */ void output_state(bool busy, BGPRouteTable *next_table); bool get_next_message(BGPRouteTable *next_table); void peering_went_down(const PeerHandler *peer, uint32_t genid, BGPRouteTable *caller); void peering_down_complete(const PeerHandler *peer, uint32_t genid, BGPRouteTable *caller); void set_canned_response(int response) { _canned_response = response; } void set_next_messages(int msgs) { _msgs = msgs; } void set_get_on_wakeup(bool get_on_wakeup) { _get_on_wakeup = get_on_wakeup; } bool set_output_file(const string& filename); void set_output_file(FILE *file); FILE *output_file() const {return _ofile;} void write_separator() const; void write_comment(const string& s) const; void enable_tablename_printing() {_print_tablename = true;} void set_is_winner(bool value) {_set_is_winner = value;} private: void print_route(const SubnetRoute& route, FPAListRef palist) const; int _canned_response; int _msgs; FILE *_ofile; bool _close_on_delete; bool _print_tablename; bool _get_on_wakeup; bool _set_is_winner; }; #endif // __BGP_ROUTE_TABLE_DEBUG_HH__ xorp/bgp/route_table_ribin.cc0000664000076400007640000004457011633743677016462 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #include "bgp_module.h" #include "libxorp/xlog.h" #include "route_table_ribin.hh" #include "route_table_deletion.hh" #include "rib_ipc_handler.hh" #include "bgp.hh" template RibInTable::RibInTable(string table_name, Safi safi, const PeerHandler *peer) : BGPRouteTable("RibInTable-" + table_name, safi), _peer(peer) { _route_table = new BgpTrie; _peer_is_up = true; _genid = 1; /*zero is not a valid genid*/ _table_version = 1; this->_parent = NULL; _nexthop_push_active = false; } template RibInTable::~RibInTable() { delete _route_table; } template void RibInTable::flush() { debug_msg("%s\n", this->tablename().c_str()); _route_table->delete_all_nodes(); } template void RibInTable::ribin_peering_went_down() { log("Peering went down"); _peer_is_up = false; // Stop pushing changed nexthops. stop_nexthop_push(); /* When the peering goes down we unhook our entire RibIn routing table and give it to a new deletion table. We plumb the deletion table in after the RibIn, and it can then delete the routes as a background task while filtering the routes we give it so that the later tables get the correct deletes at the correct time. This means that we don't have to concern ourselves with holding deleted and new routes if the peer comes back up before we've finished the background deletion process - credits to Atanu Ghosh for this neat idea */ if (_route_table->route_count() > 0) { string tablename = "Deleted" + this->tablename(); DeletionTable* deletion_table = new DeletionTable(tablename, this->safi(), _route_table, _peer, _genid, this); _route_table = new BgpTrie; deletion_table->set_next_table(this->_next_table); this->_next_table->set_parent(deletion_table); this->_next_table = deletion_table; this->_next_table->peering_went_down(_peer, _genid, this); deletion_table->initiate_background_deletion(); } else { //nothing to delete - just notify everyone this->_next_table->peering_went_down(_peer, _genid, this); this->_next_table->push(this); this->_next_table->peering_down_complete(_peer, _genid, this); } } template void RibInTable::ribin_peering_came_up() { log("Peering came up"); _peer_is_up = true; _genid++; // cope with wrapping genid without using zero which is reserved if (_genid == 0) { _genid = 1; } _table_version = 1; this->_next_table->peering_came_up(_peer, _genid, this); } template int RibInTable::add_route(const IPNet& net, FPAListRef& fpa_list, const PolicyTags& policy_tags) { const ChainedSubnetRoute *new_route; const SubnetRoute *existing_route; XLOG_ASSERT(_peer_is_up); XLOG_ASSERT(this->_next_table != NULL); XLOG_ASSERT(!fpa_list->is_locked()); log("add route: " + net.str()); int response; typename BgpTrie::iterator iter = _route_table->lookup_node(net); if (iter != _route_table->end()) { existing_route = &(iter.payload()); XLOG_ASSERT(existing_route->net() == net); #if 0 if (rtmsg.route()->attributes() == existing_route->attributes()) { // this route is the same as before. // no need to do anything. return ADD_UNUSED; } #endif // Preserve the route. Taking a reference will prevent the // route being deleted when it's erased from the Trie. // Deletion will occur when the reference goes out of scope. SubnetRouteConstRef route_reference(existing_route); deletion_nexthop_check(existing_route); PAListRef old_pa_list = existing_route->attributes(); FPAListRef old_fpa_list = new FastPathAttributeList(old_pa_list); // delete from the Trie _route_table->erase(net); _table_version++; old_pa_list.deregister_with_attmgr(); InternalMessage old_rt_msg(existing_route, old_fpa_list, _peer, _genid); // Create the concise format PA list. fpa_list->canonicalize(); PAListRef pa_list = new PathAttributeList(fpa_list); pa_list.register_with_attmgr(); // Store it locally. The BgpTrie will copy it into a ChainedSubnetRoute SubnetRoute* tmp_route = new SubnetRoute(net, pa_list, NULL); tmp_route->set_policytags(policy_tags); typename BgpTrie::iterator iter = _route_table->insert(net, *tmp_route); tmp_route->unref(); new_route = &(iter.payload()); // propagate downstream InternalMessage new_rt_msg(new_route, fpa_list, _peer, _genid); response = this->_next_table->replace_route(old_rt_msg, new_rt_msg, (BGPRouteTable*)this); } else { // Create the concise format PA list. fpa_list->canonicalize(); PAListRef pa_list = new PathAttributeList(fpa_list); pa_list.register_with_attmgr(); SubnetRoute* tmp_route = new SubnetRoute(net, pa_list, NULL); tmp_route->set_policytags(policy_tags); typename BgpTrie::iterator iter = _route_table->insert(net, *tmp_route); tmp_route->unref(); new_route = &(iter.payload()); // progogate downstream InternalMessage new_rt_msg(new_route, fpa_list, _peer, _genid); response = this->_next_table->add_route(new_rt_msg, (BGPRouteTable*)this); } switch (response) { case ADD_UNUSED: new_route->set_in_use(false); new_route->set_filtered(false); break; case ADD_FILTERED: new_route->set_in_use(false); new_route->set_filtered(true); break; case ADD_USED: case ADD_FAILURE: // the default if we don't know for sure that a route is unused // should be that it is used. new_route->set_in_use(true); new_route->set_filtered(false); break; } return response; } template int RibInTable::delete_route(const IPNet &net) { XLOG_ASSERT(_peer_is_up); log("delete route: " + net.str()); typename BgpTrie::iterator iter = _route_table->lookup_node(net); if (iter != _route_table->end()) { const SubnetRoute *existing_route = &(iter.payload()); // Preserve the route. Taking a reference will prevent the // route being deleted when it's erased from the Trie. // Deletion will occur when the reference goes out of scope. SubnetRouteConstRef route_reference(existing_route); deletion_nexthop_check(existing_route); PAListRef old_pa_list = iter.payload().attributes(); FPAListRef old_fpa_list = new FastPathAttributeList(old_pa_list); // remove from the Trie _route_table->erase(net); _table_version++; old_pa_list.deregister_with_attmgr(); // propogate downstream InternalMessage old_rt_msg(existing_route, old_fpa_list, _peer, _genid); if (this->_next_table != NULL) this->_next_table->delete_route(old_rt_msg, (BGPRouteTable*)this); } else { // we received a delete, but didn't have anything to delete. // It's debatable whether we should silently ignore this, or // drop the peering. If we don't hold input-filtered routes in // the RIB-In, then this would be commonplace, so we'd have to // silently ignore it. Currently (Sept 2002) we do still hold // filtered routes in the RIB-In, so this should be an error. // But we'll just ignore this error, and log a warning. string s = "Attempt to delete route for net " + net.str() + " that wasn't in RIB-In\n"; XLOG_WARNING("%s", s.c_str()); return -1; } return 0; } template int RibInTable::push(BGPRouteTable *caller) { debug_msg("RibInTable::push\n"); XLOG_ASSERT(caller == NULL); XLOG_ASSERT(_peer_is_up); XLOG_ASSERT(this->_next_table != NULL); return this->_next_table->push((BGPRouteTable*)this); } template const SubnetRoute* RibInTable::lookup_route(const IPNet &net, uint32_t& genid, FPAListRef& fpa_list_ref) const { if (_peer_is_up == false) return NULL; typename BgpTrie::iterator iter = _route_table->lookup_node(net); if (iter != _route_table->end()) { // assert(iter.payload().net() == net); genid = _genid; PAListRef pa_list = iter.payload().attributes(); FastPathAttributeList* fpa_list = new FastPathAttributeList(pa_list); fpa_list_ref = fpa_list; return &(iter.payload()); } else fpa_list_ref = NULL; return NULL; } template void RibInTable::route_used(const SubnetRoute* used_route, bool in_use) { // we look this up rather than modify used_route itself because // it's possible that used_route originates the other side of a // cache, and modifying it might not modify the version we have // here in the RibIn if (_peer_is_up == false) return; typename BgpTrie::iterator iter = _route_table->lookup_node(used_route->net()); XLOG_ASSERT(iter != _route_table->end()); iter.payload().set_in_use(in_use); } template string RibInTable::str() const { string s = "RibInTable" + this->tablename(); return s; } template bool RibInTable::dump_next_route(DumpIterator& dump_iter) { typename BgpTrie::iterator route_iterator; debug_msg("dump iter: %s\n", dump_iter.str().c_str()); if (dump_iter.route_iterator_is_valid()) { debug_msg("route_iterator is valid\n"); route_iterator = dump_iter.route_iterator(); // Make sure the iterator is valid. If it is pointing at a // deleted node this comparison will move it forward. if (route_iterator == _route_table->end()) { return false; } //we need to move on to the next node, except if the iterator //was pointing at a deleted node, because then it will have //just been moved to the next node to dump, so we need to dump //the node that the iterator is currently pointing at. if (dump_iter.iterator_got_moved(route_iterator.key()) == false) route_iterator++; } else { debug_msg("route_iterator is not valid\n"); route_iterator = _route_table->begin(); } if (route_iterator == _route_table->end()) { return false; } const ChainedSubnetRoute* chained_rt; for ( ; route_iterator != _route_table->end(); route_iterator++) { chained_rt = &(route_iterator.payload()); debug_msg("chained_rt: %s\n", chained_rt->str().c_str()); // propagate downstream // only dump routes that actually won // XXX: or if its a policy route dump if (chained_rt->is_winner() || dump_iter.peer_to_dump_to() == NULL) { InternalMessage rt_msg(chained_rt, _peer, _genid); //XLOG_WARNING("dump route: %s", rt_msg.str().c_str()); try { int res = this->_next_table->route_dump(rt_msg, (BGPRouteTable*)this, dump_iter.peer_to_dump_to()); if(res == ADD_FILTERED) chained_rt->set_filtered(true); else chained_rt->set_filtered(false); } catch (const XorpException& e) { //TODO: Make sure bad routes never get into the table in the first place // (was an IPv6 zero default route that triggered this bug initially) // See test 28-ipv6 in harness/test_peering1.sh --Ben XLOG_WARNING("Exception in dump_next_route: %s\n", e.str().c_str()); XLOG_WARNING(" rt_msg: %s\n", rt_msg.str().c_str()); } break; } } if (route_iterator == _route_table->end()) return false; // Store the new iterator value as its valid. dump_iter.set_route_iterator(route_iterator); return true; } template void RibInTable::igp_nexthop_changed(const A& bgp_nexthop) { debug_msg("igp_nexthop_changed for bgp_nexthop %s on table %s\n", bgp_nexthop.str().c_str(), this->tablename().c_str()); log("igp nexthop changed: " + bgp_nexthop.str()); typename set ::const_iterator i; i = _changed_nexthops.find(bgp_nexthop); if (i != _changed_nexthops.end()) { debug_msg("nexthop already queued\n"); // this nexthop is already queued to be pushed again return; } if (_nexthop_push_active) { debug_msg("push active, adding to queue\n"); _changed_nexthops.insert(bgp_nexthop); } else { debug_msg("push was inactive, activating\n"); // this is more work than it should be - we need to create a // canonicalized path attribute list containing only the // nexthop. When we call lower bound, this will find the // first pathmap chain containing this nexthop. FPAListRef dummy_fpa_list = new FastPathAttributeList(); NextHopAttribute nh_att(bgp_nexthop); dummy_fpa_list->add_path_attribute(nh_att); dummy_fpa_list->canonicalize(); PAListRef dummy_pa_list = new PathAttributeList(dummy_fpa_list); typename BgpTrie::PathmapType::const_iterator pmi; pmi = _route_table->pathmap().lower_bound(dummy_pa_list); if (pmi == _route_table->pathmap().end()) { // no route in this trie has this Nexthop debug_msg("no matching routes - do nothing\n"); return; } PAListRef pa_list = pmi->first; FPAListRef fpa_list = new FastPathAttributeList(pa_list); if (fpa_list->nexthop() != bgp_nexthop) { debug_msg("no matching routes (2)- do nothing\n"); return; } _current_changed_nexthop = bgp_nexthop; _nexthop_push_active = true; _current_chain = pmi; const SubnetRoute* next_route_to_push = _current_chain->second; UNUSED(next_route_to_push); debug_msg("Found route with nexthop %s:\n%s\n", bgp_nexthop.str().c_str(), next_route_to_push->str().c_str()); // _next_table->push((BGPRouteTable*)this); // call back immediately, but after network events or expired timers _push_task = eventloop().new_task( callback(this, &RibInTable::push_next_changed_nexthop), XorpTask::PRIORITY_DEFAULT, XorpTask::WEIGHT_DEFAULT); } } template bool RibInTable::push_next_changed_nexthop() { if (_nexthop_push_active == false) { // // XXX: No more nexthops to push, probably because routes have // been just deleted. // return false; } XLOG_ASSERT(_peer_is_up); const ChainedSubnetRoute* chained_rt, *first_rt; first_rt = chained_rt = _current_chain->second; while (1) { // Replace the route with itself. This will cause filters to // be re-applied, and decision to re-evaluate the route. InternalMessage old_rt_msg(chained_rt, _peer, _genid); InternalMessage new_rt_msg(chained_rt, _peer, _genid); //we used to send this as a replace route, but replacing a //route with itself isn't safe in terms of preserving the //flags. log("push next changed nexthop: " + old_rt_msg.net().str()); this->_next_table->delete_route(old_rt_msg, (BGPRouteTable*)this); this->_next_table->add_route(new_rt_msg, (BGPRouteTable*)this); if (chained_rt->next() == first_rt) { debug_msg("end of chain\n"); break; } else { debug_msg("chain continues\n"); } chained_rt = chained_rt->next(); } debug_msg("****RibIn Sending push***\n"); this->_next_table->push((BGPRouteTable*)this); next_chain(); if (_nexthop_push_active == false) return false; return true; } template void RibInTable::deletion_nexthop_check(const SubnetRoute* route) { // checks to make sure that the deletion doesn't make // _next_route_to_push invalid if (!_nexthop_push_active) return; const ChainedSubnetRoute* next_route_to_push = _current_chain->second; if (*route == *next_route_to_push) { if (next_route_to_push == next_route_to_push->next()) { // this is the last route in the chain, so we need to bump // the iterator before we delete it. next_chain(); } } } template void RibInTable::next_chain() { _current_chain++; if (_current_chain != _route_table->pathmap().end()) { PAListRef pa_list =_current_chain->first; FPAListRef fpa_list = new FastPathAttributeList(pa_list); XLOG_ASSERT(fpa_list->nexthop_att() ); if (fpa_list->nexthop() == _current_changed_nexthop) { // there's another chain with the same nexthop return; } } while (1) { // that's it for this nexthop - try the next if (_changed_nexthops.empty()) { // no more nexthops to push _nexthop_push_active = false; return; } typename set ::iterator i = _changed_nexthops.begin(); _current_changed_nexthop = *i; _changed_nexthops.erase(i); FPAListRef dummy_fpa_list = new FastPathAttributeList(); NextHopAttribute nh_att(_current_changed_nexthop); dummy_fpa_list->add_path_attribute(nh_att); dummy_fpa_list->canonicalize(); PAListRef dummy_pa_list = new PathAttributeList(dummy_fpa_list); typename BgpTrie::PathmapType::const_iterator pmi; pmi = _route_table->pathmap().lower_bound(dummy_pa_list); if (pmi == _route_table->pathmap().end()) { // no route in this trie has this Nexthop, try the next nexthop continue; } PAListRef pa_list = pmi->first; FPAListRef fpa_list = new FastPathAttributeList(pa_list); if (fpa_list->nexthop() != _current_changed_nexthop) { // no route in this trie has this Nexthop, try the next nexthop continue; } _current_chain = pmi; return; } } template void RibInTable::stop_nexthop_push() { // When a peering goes down tear out all the nexthop change state _changed_nexthops.clear(); _nexthop_push_active = false; _current_changed_nexthop = A::ZERO(); // _current_chain = _route_table->pathmap().end(); _push_task.unschedule(); } template string RibInTable::dump_state() const { string s; s = "=================================================================\n"; s += "RibInTable\n"; s += str() + "\n"; s += "=================================================================\n"; if (_peer_is_up) s += "Peer is UP\n"; else s += "Peer is DOWN\n"; s += _route_table->str(); s += CrashDumper::dump_state(); return s; } template EventLoop& RibInTable::eventloop() const { return _peer->eventloop(); } template class RibInTable; template class RibInTable; xorp/bgp/main.cc0000664000076400007640000000477011540224217013671 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "bgp_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/eventloop.hh" #include "libcomm/comm_api.h" #include "libxipc/xrl_std_router.hh" #include "bgp.hh" #include "xrl_target.hh" int main(int /*argc*/, char **argv) { XorpUnexpectedHandler x(xorp_unexpected_handler); // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_level_set_verbose(XLOG_LEVEL_WARNING, XLOG_VERBOSE_HIGH); // Enable verbose tracing via configuration to increase the tracing level // xlog_level_set_verbose(XLOG_LEVEL_INFO, XLOG_VERBOSE_HIGH); // xlog_level_set_verbose(XLOG_LEVEL_TRACE, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); comm_init(); setup_dflt_sighandlers(); try { EventLoop eventloop; // signal(SIGINT, terminate_main_loop); BGPMain bgp(eventloop); /* ** By default assume there is a rib running. */ bgp.register_ribname("rib"); /* ** Wait for our local configuration information and for the ** FEA and RIB to start. */ while (xorp_do_run && bgp.get_xrl_target()->waiting()) { eventloop.run(); } /* ** Check we shouldn't be exiting. */ if (!bgp.get_xrl_target()->done()) bgp.main_loop(); } catch(...) { xorp_catch_standard_exceptions(); } // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); comm_exit(); debug_msg("Bye!\n"); return 0; } xorp/bgp/peer.hh0000664000076400007640000003577011421137511013714 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/bgp/peer.hh,v 1.53 2008/11/08 06:14:37 mjh Exp $ #ifndef __BGP_PEER_HH__ #define __BGP_PEER_HH__ #include #include "socket.hh" #include "local_data.hh" #include "peer_data.hh" enum FSMState { STATEIDLE = 1, STATECONNECT = 2, STATEACTIVE = 3, STATEOPENSENT = 4, STATEOPENCONFIRM = 5, STATEESTABLISHED = 6, STATESTOPPED = 7 // This state is not in the protocol specification. // After sending a notify it is necessary to // close the connection. Data transmission/reception // is asynchronous, but the close is currently // synchronous. Thus the stopped state allows // us to wait for the notify to be sent to TCP, // before closing the connection. }; enum FSMEvent { EVENTBGPSTART = 1, EVENTBGPSTOP = 2, EVENTBGPTRANOPEN = 3, EVENTBGPTRANCLOSED = 4, EVENTBGPCONNOPENFAIL = 5, EVENTBGPTRANFATALERR = 6, EVENTCONNTIMEEXP = 7, EVENTHOLDTIMEEXP = 8, EVENTKEEPALIVEEXP = 9, EVENTRECOPENMESS = 10, EVENTRECKEEPALIVEMESS = 11, EVENTRECUPDATEMESS = 12, EVENTRECNOTMESS = 13 }; enum PeerOutputState { PEER_OUTPUT_OK = 1, PEER_OUTPUT_BUSY = 2, PEER_OUTPUT_FAIL = 3 }; #define OPENMSGOK 0 #define UPDATEMSGOK 0 const uint32_t RIB_IPC_HANDLER_UNIQUE_ID = 0; const uint32_t AGGR_HANDLER_UNIQUE_ID = 1; const uint32_t UNIQUE_ID_START = AGGR_HANDLER_UNIQUE_ID + 1; class BGPMain; class PeerHandler; class AcceptSession; /** * Manage the damping of peer oscillations. */ class DampPeerOscillations { public: DampPeerOscillations(EventLoop& eventloop, uint32_t restart_threshold, uint32_t time_period, uint32_t idle_holdtime); /** * The session has been restarted. */ void restart(); /** * @return the idle holdtime. */ uint32_t idle_holdtime() const; /** * Reset the state, possibly due to a manual restart. */ void reset(); private: EventLoop& _eventloop; // Reference to the eventloop. const uint32_t _restart_threshold; // Number of restart after // which the idle holdtime // will increase. const uint32_t _time_period; // Period in seconds over // which to sample errors. const uint32_t _idle_holdtime; // Holdtime in seconds to use once the // error threshold is passed. uint32_t _restart_counter; // Count number of restarts in // last time quantum. XorpTimer _zero_restart; // Zero the restart counter. /** * Used by the timer to zero the error count. */ void zero_restart_count(); }; class BGPPeer { public: BGPPeer(LocalData *ld, BGPPeerData *pd, SocketClient *sock, BGPMain *m); virtual ~BGPPeer(); /** * Get this peers unique ID. */ uint32_t get_unique_id() const { return _unique_id; } /** * Zero all the stats counters. */ void zero_stats(); /** * Clear the last error. */ void clear_last_error(); /** * Replace the old peerdata with a new copy. It is the * responsiblity of the caller to free the memory. */ BGPPeerData *swap_peerdata(BGPPeerData *pd) { BGPPeerData *tmp = _peerdata; _peerdata = pd; return tmp; } void connected(XorpFd s); void remove_accept_attempt(AcceptSession *conn); SocketClient *swap_sockets(SocketClient *new_sock); XorpFd get_sock(); /** * state machine handlers for the various BGP events */ void event_start(); // EVENTBGPSTART void event_stop(bool restart=false, bool automatic = false);// EVENTBGPSTOP void event_open(); // EVENTBGPTRANOPEN void event_open(const XorpFd sock); // EVENTBGPTRANOPEN void event_closed(); // EVENTBGPTRANCLOSED void event_openfail(); // EVENTBGPCONNOPENFAIL void event_tranfatal(); // EVENTBGPTRANFATALERR void event_connexp(); // EVENTCONNTIMEEXP void event_holdexp(); // EVENTHOLDTIMEEXP void event_keepexp(); // EVENTKEEPALIVEEXP void event_delay_open_exp(); // Event 12: DelayOpenTimer_Expires void event_idle_hold_exp(); // Event 13: IdleHoldTimer_Expires void event_openmess(const OpenPacket& p); // EVENTRECOPENMESS void event_keepmess(); // EVENTRECKEEPALIVEMESS void event_recvupdate(UpdatePacket& p); // EVENTRECUPDATEMESS void event_recvnotify(const NotificationPacket& p); // EVENTRECNOTMESS void generate_open_message(OpenPacket& open); void notify_peer_of_error(const int error, const int subcode = UNSPECIFIED, const uint8_t*data = 0, const size_t len = 0); FSMState state() { return _state; } static const char *pretty_print_state(FSMState s); /** * If jitter is globally enabled apply it to the time provided otherwise * just return the input time. */ TimeVal jitter(const TimeVal& t); void clear_all_timers(); void start_connect_retry_timer(); void clear_connect_retry_timer(); void restart_connect_retry_timer(); void start_keepalive_timer(); void clear_keepalive_timer(); void start_hold_timer(); void clear_hold_timer(); void restart_hold_timer(); void start_stopped_timer(); void clear_stopped_timer(); void start_idle_hold_timer(); void clear_idle_hold_timer(); /** * @return true if the idle hold timer is running. */ bool running_idle_hold_timer() const; void start_delay_open_timer(); void clear_delay_open_timer(); bool get_message(BGPPacket::Status status, const uint8_t *buf, size_t len, SocketClient *socket_client); PeerOutputState send_message(const BGPPacket& p); void send_message_complete(SocketClient::Event, const uint8_t *buf); string str() const { return _peername; } bool is_connected() const { return _SocketClient->is_connected(); } bool still_reading() const { return _SocketClient->still_reading(); } LocalData* localdata() { return _localdata; } IPv4 id() const { return _localdata->get_id(); } BGPMain* main() const { return _mainprocess; } const BGPPeerData* peerdata() const { return _peerdata; } bool ibgp() const { return peerdata()->ibgp(); } bool use_4byte_asnums() const { return _peerdata->use_4byte_asnums(); } bool we_use_4byte_asnums() const { return _localdata->use_4byte_asnums(); } /** * send the netreachability message, return send result. */ bool send_netreachability(const BGPUpdateAttrib &n); /* ** Virtual so that it can be subclassed in the plumbing test code. */ virtual PeerOutputState send_update_message(const UpdatePacket& p); uint32_t get_established_transitions() const { return _established_transitions; } uint32_t get_established_time() const; void get_msg_stats(uint32_t& in_updates, uint32_t& out_updates, uint32_t& in_msgs, uint32_t& out_msgs, uint16_t& last_error, uint32_t& in_update_elapsed) const; protected: private: LocalData* _localdata; /** * For the processing in decision every peer requires a unique ID. */ static uint32_t _unique_id_allocator; const uint32_t _unique_id; friend class BGPPeerList; void connect_to_peer(SocketClient::ConnectCallback cb) { XLOG_ASSERT(_SocketClient); _SocketClient->connect(cb); } void connect_to_peer_complete(bool success) { if (success) event_open(); // Event = EVENTBGPTRANOPEN else event_openfail(); // Event = EVENTBGPCONNOPENFAIL } void send_notification(const NotificationPacket& p, bool restart = true, bool automatic = true); void send_notification_complete(SocketClient::Event, const uint8_t *buf, bool restart, bool automatic); void flush_transmit_queue() { _SocketClient->flush_transmit_queue(); } void stop_reader() { _SocketClient->stop_reader(); } SocketClient *_SocketClient; bool _output_queue_was_busy; FSMState _state; BGPPeerData* _peerdata; BGPMain* _mainprocess; PeerHandler *_handler; list _accept_attempt; string _peername; XorpTimer _timer_connect_retry; XorpTimer _timer_hold_time; XorpTimer _timer_keep_alive; XorpTimer _idle_hold; XorpTimer _delay_open; // counters needed for the BGP MIB uint32_t _in_updates; uint32_t _out_updates; uint32_t _in_total_messages; uint32_t _out_total_messages; uint8_t _last_error[2]; uint32_t _established_transitions; TimeVal _established_time; TimeVal _in_update_time; /** * This timer is to break us out of the stopped state. */ XorpTimer _timer_stopped; void hook_stopped(); void check_open_packet(const OpenPacket *p) throw (CorruptMessage); NotificationPacket* check_update_packet(const UpdatePacket *p, bool& good_nexthop); /** * Called the first time that we go to the established state. */ bool established(); bool release_resources(); /** * move to the desired state, plus does some additional * work to clean up existing state and possibly retrying to * open/connect if restart = true * * @param restart if true and this is a transition to idle restart * the connection. * @param automatic if the transition is to idle and automatic restart has * been request. This is not a manual restart. */ void set_state(FSMState s, bool restart = true, bool automatic = true); bool remote_ip_ge_than(const BGPPeer& peer); bool _damping_peer_oscillations; // True if Damp Peer // Oscillations is enabled. DampPeerOscillations _damp_peer_oscillations; /** * Called every time there is an automatic restart. Used for * tracking peer damp oscillations. */ void automatic_restart(); private: friend class BGPMain; bool _current_state; void set_current_peer_state(bool state) {_current_state = state;} bool get_current_peer_state() {return _current_state;} bool _next_state; void set_next_peer_state(bool state) {_next_state = state;} bool get_next_peer_state() {return _next_state;} bool _activated; void set_activate_state(bool state) {_activated = state;} bool get_activate_state() {return _activated;} }; /* * All incoming TCP connection attempts are handled through this class. * The BGPPeer class handles outgoing connection attempts. * * Under normal circumstances only one connection attempt will be * taking place. When both BGP processes at either end of a session * attempt to make a connection at the same time there may be a * connection collision in this case it is necessary to hold two TCP * connections until an open message is seen by the peer to decide * which session should be selected. If a connection collision is * detected this class does *not* send an open message, it waits for * the peers open message. It should be noted that the BGPPeer class * is not aware that a connection collision condition exists, hence it * does not check the IDs. The assumptions are that: * 1) The IDs will be identical in both open messages so it's only * necessary to check one of the two open messages. * 2) A BGP process will actually send a open message after making a * connection. * * This class could be used to get rid of the XORP invented STOPPED * state in state machine. */ class AcceptSession { public: AcceptSession(BGPPeer& peer, XorpFd sock); ~AcceptSession(); /** * Start the FSM. */ void start(); /** * Timeout routine that is called if no messages are seen from the * peer. Ideally an open message should be seen from the peer. */ void no_open_received(); /** * This FSM has done its job signal to the peer to remove this * class. This should be the last method to be called in any methods. */ void remove(); /** * Send a notification. */ void send_notification_accept(const NotificationPacket& np); /** * Notification callback. */ void send_notification_cb(SocketClient::Event ev, const uint8_t* buf); /** * Send a cease. */ void cease(); /** * The main FSM is in state OPENCONFIRM so both IDs are available * to resolve the collision. */ void collision(); /** * An open message has just been received on the accept socket * decide and keep the winner. */ void event_openmess_accept(const OpenPacket& p); /** * Swap the socket in this class with the one in the main FSM. */ void swap_sockets(); /** * Replace this socket with the one in the main FSM and feed in * an open packet. */ void swap_sockets(const OpenPacket& p); void notify_peer_of_error_accept(const int error, const int subcode = UNSPECIFIED, const uint8_t*data = 0, const size_t len = 0); void event_tranfatal_accept(); /** * Called if the TCP connection is closed. */ void event_closed_accept(); /** * Called if a keepalive message is seen. */ void event_keepmess_accept(); /** * Called if a update message is seen. */ void event_recvupdate_accept(const UpdatePacket& p); /** * Called if a notify message is seen. */ void event_recvnotify_accept(const NotificationPacket& p); /** * Handle incoming messages. */ bool get_message_accept(BGPPacket::Status status, const uint8_t *buf, size_t len, SocketClient *socket_client); bool is_connected() { return _socket_client->is_connected(); } bool still_reading() { return _socket_client->still_reading(); } void ignore_message() { _accept_messages = false; } bool accept_message() const { return _accept_messages; } string str() { return _peer.str(); } private: BGPPeer& _peer; XorpFd _sock; SocketClient *_socket_client; bool _accept_messages; XorpTimer _open_wait; // Wait for an open message from the peer. BGPMain *main() { return _peer.main(); } FSMState state() { return _peer.state(); } const BGPPeerData* peerdata() const{ return _peer.peerdata(); } bool running_idle_hold_timer() const { return _peer.running_idle_hold_timer(); } }; #endif // __BGP_PEER_HH__ xorp/bgp/peer_handler_debug.hh0000664000076400007640000000551311540224220016543 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/bgp/peer_handler_debug.hh,v 1.12 2008/11/08 06:14:37 mjh Exp $ #ifndef __BGP_PEER_HANDLER_DEBUG_HH__ #define __BGP_PEER_HANDLER_DEBUG_HH__ #include "peer_handler.hh" class DebugPeerHandler : public PeerHandler { public: DebugPeerHandler(BGPPeer *peer); ~DebugPeerHandler(); int start_packet(); /* add_route and delete_route are called to propagate a route *to* the RIB. */ int add_route(const SubnetRoute &rt, FPAList4Ref& pa_list, bool ibgp, Safi safi); int add_route(const SubnetRoute &rt, FPAList6Ref& pa_list, bool ibgp, Safi safi); int replace_route(const SubnetRoute &old_rt, bool old_ibgp, const SubnetRoute &new_rt, bool new_ibgp, FPAList4Ref& pa_list, Safi safi); int replace_route(const SubnetRoute &old_rt, bool old_ibgp, const SubnetRoute &new_rt, bool new_ibgp, FPAList6Ref& pa_list, Safi safi); int delete_route(const SubnetRoute &rt, FPAList4Ref& pa_list, bool new_ibgp, Safi safi); int delete_route(const SubnetRoute &rt, FPAList6Ref& pa_list, bool new_ibgp, Safi safi); PeerOutputState push_packet(); void set_output_file(FILE *file) {_ofile = file;} void set_canned_response(PeerOutputState state) { _canned_response = state; } private: void print_route(const SubnetRoute& route, FPAList4Ref palist) const; void print_route(const SubnetRoute& route, FPAList6Ref palist) const; FILE *_ofile; PeerOutputState _canned_response; }; #endif // __BGP_PEER_HANDLER_DEBUG_HH__ xorp/bgp/route_table_reader.cc0000664000076400007640000001025011421137511016560 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "bgp_module.h" #include "libxorp/xlog.h" #include "route_table_reader.hh" #include "route_table_ribin.hh" #include "peer_handler.hh" template ReaderIxTuple::ReaderIxTuple(const IPv4& peer_id, trie_iterator route_iter, const RibInTable* ribin) : _peer_id(peer_id), _route_iter(route_iter), _ribin(ribin) { _net = _route_iter.key(); } template bool ReaderIxTuple::operator<(const ReaderIxTuple& them) const { if (masked_addr() < them.masked_addr()) { return true; } if (them.masked_addr() < masked_addr()) { return false; } #if 0 //The MIB requires it to be least-specific first if (prefix_len() < them.prefix_len()) { return true; } if (them.prefix_len() < _prefix_len()) { return false; } #else //XXX But we don't have a Trie iterator for that, so we do most //specific first for now. if (prefix_len() < them.prefix_len()) { return false; } if (them.prefix_len() < prefix_len()) { return true; } #endif if (_peer_id < them.peer_id()) { return true; } if (them.peer_id() < _peer_id) { return false; } return false; } template bool ReaderIxTuple::is_consistent() const { if (_route_iter == _ribin->trie().end()) { // the route we were pointing at has been deleted, and there // are no routes after this in the trie. return false; } if (_route_iter.key() != _net) { // the route we were pointing at has been deleted, and the // iterator now points to a later subnet in the trie. return false; } return true; } template RouteTableReader::RouteTableReader(const list *>& ribins, const IPNet& /*prefix*/) { typename list *>::const_iterator i; for(i = ribins.begin(); i != ribins.end(); i++) { trie_iterator ti = (*i)->trie().begin(); if (ti != (*i)->trie().end()) { IPv4 peer_id = (*i)->peer_handler()->id(); _peer_readers.insert(new ReaderIxTuple(peer_id, ti, (*i))); } } } template bool RouteTableReader::get_next(const SubnetRoute*& route, IPv4& peer_id) { typename set *>::iterator i; while (1) { i = _peer_readers.begin(); if (i == _peer_readers.end()) { return false; } ReaderIxTuple* reader = *i; if (reader->is_consistent()) { //return the route and the peer_id from the reader route = &(reader->route_iterator().payload()); peer_id = reader->peer_id(); //if necessary, prepare this peer's reader for next time reader->route_iterator()++; if (reader->route_iterator() != reader->ribin()->trie().end()) { _peer_readers.insert(new ReaderIxTuple(reader->peer_id(), reader-> route_iterator(), reader->ribin())); } //remove the obsolete reader _peer_readers.erase(i); delete reader; return true; } //the iterator was inconsistent, so we need to re-insert a //consistent version into the set and try again. if (reader->route_iterator() != reader->ribin()->trie().end()) { _peer_readers.insert(new ReaderIxTuple(reader->peer_id(), reader->route_iterator(), reader->ribin())); } //remove the obsolete reader _peer_readers.erase(i); delete reader; } } template class RouteTableReader; template class RouteTableReader; xorp/bgp/route_table_deletion.cc0000664000076400007640000002313211421137511017124 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #include "bgp_module.h" #include "libxorp/xlog.h" #include "libxorp/callback.hh" #include "route_table_deletion.hh" template DeletionTable::DeletionTable(string table_name, Safi safi, BgpTrie* route_table, const PeerHandler *peer, uint32_t genid, BGPRouteTable *parent_table) : BGPRouteTable("DeletionTable-" + table_name, safi) { this->_parent = parent_table; _genid = genid; _route_table = route_table; _peer = peer; this->_next_table = 0; } template DeletionTable::~DeletionTable() { _route_table->delete_self(); } template int DeletionTable::add_route(InternalMessage &rtmsg, BGPRouteTable *caller) { debug_msg("DeletionTable::add_route %p on %s\n", &rtmsg, this->tablename().c_str()); XLOG_ASSERT(caller == this->_parent); XLOG_ASSERT(this->_next_table != NULL); IPNet net = rtmsg.net(); // check if we have this route in our deletion cache typename BgpTrie::iterator iter; iter = _route_table->lookup_node(net); if (iter == _route_table->end()) { return this->_next_table->add_route(rtmsg, (BGPRouteTable*)this); } else { const SubnetRoute *existing_route = &(iter.payload()); // We have a copy of this route in our deletion cache. XLOG_ASSERT(existing_route->net() == rtmsg.net()); // Preserve the route. Taking a reference will prevent the // route being deleted when it's erased from the Trie. // Deletion will occur when the reference goes out of scope. SubnetRouteConstRef route_reference(existing_route); // delete from the Trie if ((_del_sweep->second->net() == rtmsg.net()) && (_del_sweep->second->prev() == _del_sweep->second)) { // we're about to delete the chain that's pointed to by our // iterator, so move the iterator on now. _del_sweep++; } _route_table->erase(rtmsg.net()); // propogate downstream PAListRef pa_list= existing_route->attributes(); FPAListRef fpa_list = new FastPathAttributeList(pa_list); pa_list.deregister_with_attmgr(); InternalMessage old_rt_msg(existing_route, fpa_list, _peer, _genid); old_rt_msg.set_from_previous_peering(); return this->_next_table->replace_route(old_rt_msg, rtmsg, (BGPRouteTable*)this); } XLOG_UNREACHABLE(); } template int DeletionTable::replace_route(InternalMessage &old_rtmsg, InternalMessage &new_rtmsg, BGPRouteTable *caller) { debug_msg("DeletionTable::replace_route %p -> %p on %s\n", &old_rtmsg, &new_rtmsg, this->tablename().c_str()); XLOG_ASSERT(caller == this->_parent); XLOG_ASSERT(this->_next_table != NULL); XLOG_ASSERT(old_rtmsg.net() == new_rtmsg.net()); // we should never see a replace for a net that's in the deletion cache XLOG_ASSERT(_route_table->lookup_node(old_rtmsg.net()) == _route_table->end()); return this->_next_table->replace_route(old_rtmsg, new_rtmsg, (BGPRouteTable*)this); } template int DeletionTable::route_dump(InternalMessage &rtmsg, BGPRouteTable *caller, const PeerHandler *dump_peer) { XLOG_ASSERT(caller == this->_parent); XLOG_ASSERT(this->_next_table != NULL); /* A route dump must have been initiated after this table was created (because the creation of this table would terminate any previous route dump). So the contents of this dump MUST NOT be in our table */ XLOG_ASSERT(_route_table->lookup_node(rtmsg.net()) == _route_table->end()); return this->_next_table->route_dump(rtmsg, (BGPRouteTable*)this, dump_peer); } template int DeletionTable::delete_route(InternalMessage &rtmsg, BGPRouteTable *caller) { debug_msg("DeletionTable::delete_route %p on %s\n", &rtmsg, this->tablename().c_str()); XLOG_ASSERT(caller == this->_parent); XLOG_ASSERT(this->_next_table != NULL); // we should never see a delete for a net that's in the deletion cache XLOG_ASSERT(_route_table->lookup_node(rtmsg.net()) == _route_table->end()); return this->_next_table->delete_route(rtmsg, (BGPRouteTable*)this); } template int DeletionTable::push(BGPRouteTable *caller) { XLOG_ASSERT(caller == this->_parent); return this->_next_table->push((BGPRouteTable*)this); } template const SubnetRoute* DeletionTable::lookup_route(const IPNet &net, uint32_t& genid, FPAListRef& fpa_list) const { // Even though the peering has gone down, we still need to answer // lookup requests. This is because we need to be internally // consistent - the route is treated as still being active until we // explicitly tell the downstream tables that it has been deleted. typename BgpTrie::iterator iter = _route_table->lookup_node(net); if (iter != _route_table->end()) { genid = _genid; const SubnetRoute *route = &(iter.payload()); PAListRef pa_list = route->attributes(); fpa_list = new FastPathAttributeList(pa_list); return route; } else return this->_parent->lookup_route(net, genid, fpa_list); } template void DeletionTable::route_used(const SubnetRoute* rt, bool in_use) { //either we have this route, in which case we process this //locally, or we don't, in which case we pass it upstream. Not //both. typename BgpTrie::iterator iter = _route_table->lookup_node(rt->net()); if (iter != _route_table->end()) { iter.payload().set_in_use(in_use); } else { this->_parent->route_used(rt, in_use); } } template string DeletionTable::str() const { string s = "DeletionTable" + this->tablename(); return s; } template void DeletionTable::initiate_background_deletion() { XLOG_ASSERT(this->_next_table != NULL); _del_sweep = _route_table->pathmap().begin(); _deleted = 0; _chains = 0; // Make sure that anything previously sent by this peer has been // pushed from the output queue in the RibOut tables. this->_next_table->push(this); _deletion_task = eventloop().new_task( callback(this, &DeletionTable::delete_next_chain), XorpTask::PRIORITY_BACKGROUND, XorpTask::WEIGHT_DEFAULT); } template bool DeletionTable::delete_next_chain() { debug_msg("deleted %d routes in %d chains\n", _deleted, _chains); if (_del_sweep == _route_table->pathmap().end()) { unplumb_self(); delete this; return false; } const ChainedSubnetRoute* chained_rt, *first_rt, *next_rt; first_rt = chained_rt = _del_sweep->second; // increment the iterator here, before we delete the node, as // deletion may invalidate the iterator _del_sweep++; // erase the first_rt last chained_rt = chained_rt->next(); while (1) { // preserve the information next_rt = chained_rt->next(); // Preserve the route. Taking a reference will prevent the // route being deleted when it's erased from the Trie. // Deletion will occur when the reference goes out of scope. SubnetRouteConstRef route_reference(chained_rt); // delete from the Trie _route_table->erase(chained_rt->net()); // propagate downstream InternalMessage rt_msg(chained_rt, _peer, _genid); rt_msg.set_from_previous_peering(); if (this->_next_table != NULL) this->_next_table->delete_route(rt_msg, (BGPRouteTable*)this); chained_rt->attributes().deregister_with_attmgr(); _deleted++; if (chained_rt == first_rt) { debug_msg("end of chain\n"); break; } else { debug_msg("chain continues\n"); } chained_rt = next_rt; } if (this->_next_table != NULL) this->_next_table->push((BGPRouteTable*)this); _chains++; return true; } template void DeletionTable::unplumb_self() { debug_msg("unplumbing self\n"); XLOG_ASSERT(this->_next_table != NULL); XLOG_ASSERT(this->_parent != NULL); XLOG_ASSERT(0 == _route_table->route_count()); // signal downstream that we finished deleting routes from this // version of this RibIn. this->_next_table->peering_down_complete(_peer, _genid, this); this->_parent->set_next_table(this->_next_table); this->_next_table->set_parent(this->_parent); // ensure we can't continue to operate this->_next_table = (BGPRouteTable*)0xd0d0; this->_parent = (BGPRouteTable*)0xd0d0; } template string DeletionTable::dump_state() const { string s; s = "=================================================================\n"; s += "DeletionTable\n"; s += str() + "\n"; s += "=================================================================\n"; s += c_format("GenID: %d\n", _genid); s += _route_table->str(); return s; } template class DeletionTable; template class DeletionTable; xorp/bgp/route_table_filter.cc0000664000076400007640000007372011421137511016616 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net //#define DEBUG_LOGGING //#define DEBUG_PRINT_FUNCTION_NAME #include "bgp_module.h" #include "libxorp/xlog.h" #include "route_table_filter.hh" #include "peer_handler.hh" #include "bgp.hh" /*************************************************************************/ #if 0 template void BGPRouteFilter::propagate_flags(const InternalMessage *rtmsg, InternalMessage *new_rtmsg) const { if (rtmsg->push()) new_rtmsg->set_push(); if (rtmsg->from_previous_peering()) new_rtmsg->set_from_previous_peering(); } template void BGPRouteFilter::propagate_flags(const SubnetRoute& route, SubnetRoute& new_route) const { new_route.set_filtered(route.is_filtered()); new_route.set_policytags(route.policytags()); new_route.set_aggr_prefix_len(route.aggr_prefix_len()); for (int i = 0; i < 3; i++) new_route.set_policyfilter(i, route.policyfilter(i)); if(route.is_winner()) new_route.set_is_winner(route.igp_metric()); new_route.set_nexthop_resolved(route.nexthop_resolved()); } #endif /*************************************************************************/ template AggregationFilter::AggregationFilter(bool is_ibgp) : _is_ibgp(is_ibgp) { } template bool AggregationFilter::filter(InternalMessage& rtmsg) const { uint8_t aggr_tag = rtmsg.route()->aggr_prefix_len(); if (aggr_tag == SR_AGGR_IGNORE) { // Route was not even marked for aggregation return true; } // Has our AggregationTable properly marked the route? XLOG_ASSERT(aggr_tag >= SR_AGGR_EBGP_AGGREGATE); if (_is_ibgp) { // Peering is IBGP if (aggr_tag == SR_AGGR_IBGP_ONLY) { return true; } else { return false; } } else { // Peering is EBGP if (aggr_tag != SR_AGGR_IBGP_ONLY) { // EBGP_AGGREGATE | EBGP_NOT_AGGREGATED | EBGP_WAS_AGGREGATED return true; } else { return false; } } } /*************************************************************************/ template SimpleASFilter::SimpleASFilter(const AsNum &as_num) : _as_num(as_num) { } template bool SimpleASFilter::filter(InternalMessage& rtmsg) const { FPAListRef& attributes = rtmsg.attributes(); const ASPath& as_path = attributes->aspath(); debug_msg("Filter: AS_Path filter for >%s< checking >%s<\n", _as_num.str().c_str(), as_path.str().c_str()); if (as_path.contains(_as_num)) { return false; } return true; } /*************************************************************************/ template RRInputFilter::RRInputFilter(IPv4 bgp_id, IPv4 cluster_id) : _bgp_id(bgp_id), _cluster_id(cluster_id) { } template bool RRInputFilter::filter(InternalMessage& rtmsg) const { FPAListRef attributes = rtmsg.attributes(); const OriginatorIDAttribute *oid = attributes->originator_id(); if (0 != oid && oid->originator_id() == _bgp_id) { return false; } const ClusterListAttribute *cl = attributes->cluster_list(); if (0 != cl && cl->contains(_cluster_id)) { return false; } return true; } /*************************************************************************/ template ASPrependFilter::ASPrependFilter(const AsNum &as_num, bool is_confederation_peer) : _as_num(as_num), _is_confederation_peer(is_confederation_peer) { } template bool ASPrependFilter::filter(InternalMessage& rtmsg) const { //Create a new AS path with our AS number prepended to it. ASPath new_as_path(rtmsg.attributes()->aspath()); if (_is_confederation_peer) { new_as_path.prepend_confed_as(_as_num); } else { new_as_path.remove_confed_segments(); new_as_path.prepend_as(_as_num); } //Form a new path attribute list containing the new AS path FPAListRef& palist = rtmsg.attributes(); palist->replace_AS_path(new_as_path); rtmsg.set_changed(); return true; } /*************************************************************************/ template NexthopRewriteFilter::NexthopRewriteFilter(const A& local_nexthop, bool directly_connected, const IPNet& subnet) : _local_nexthop(local_nexthop), _directly_connected(directly_connected), _subnet(subnet) { } template bool NexthopRewriteFilter::filter(InternalMessage& rtmsg) const { // If the peer and the router are directly connected and the // NEXT_HOP is in the same network don't rewrite the // NEXT_HOP. This is known as a third party NEXT_HOP. if (_directly_connected && _subnet.contains(rtmsg.attributes()->nexthop())) { return true; } // This check is needed to fix crash in test2-ipv6 of test_routing1.sh // TODO: This needs review. --Ben if (_local_nexthop.is_unicast()) { //Form a new path attribute list containing the new nexthop FPAListRef& palist = rtmsg.attributes(); palist->replace_nexthop(_local_nexthop); //note that we changed the route rtmsg.set_changed(); } return true; } /*************************************************************************/ template NexthopPeerCheckFilter::NexthopPeerCheckFilter(const A& local_nexthop, const A& peer_address) : _local_nexthop(local_nexthop), _peer_address(peer_address) { } template bool NexthopPeerCheckFilter::filter(InternalMessage& rtmsg) const { // Only consider rewritting if this is a self originated route. if (! rtmsg.origin_peer()->originate_route_handler()) { return true; } // If the nexthop does not match the peer's address all if fine. if (rtmsg.attributes()->nexthop() != _peer_address) { return true; } // Make sure we don't throw an exception due to :: for nexthop. // See similar change in NexthopRewriteFilter::filter. // TODO: This needs review. --Ben if (_local_nexthop.is_unicast()) { // The nexthop matches the peer's address so rewrite it. FPAListRef& palist = rtmsg.attributes(); palist->replace_nexthop(_local_nexthop); //note that we changed the route rtmsg.set_changed(); } return true; } /*************************************************************************/ template IBGPLoopFilter::IBGPLoopFilter() { } template bool IBGPLoopFilter::filter(InternalMessage& rtmsg) const { //If the route originated from a vanilla IBGP, then this filter //will drop the route. This filter should only be plumbed on the //output branch to a vanilla IBGP peer. if (rtmsg.origin_peer()->get_peer_type() == PEER_TYPE_IBGP) { return false; } return true; } /*************************************************************************/ template RRIBGPLoopFilter::RRIBGPLoopFilter(bool rr_client, IPv4 bgp_id, IPv4 cluster_id) : _rr_client(rr_client), _bgp_id(bgp_id), _cluster_id(cluster_id) { } template bool RRIBGPLoopFilter::filter(InternalMessage& rtmsg) const { // Only if this is *not* a route reflector client should the // packet be filtered. Note PEER_TYPE_IBGP_CLIENT is just passed // through. if (rtmsg.origin_peer()->get_peer_type() == PEER_TYPE_IBGP && !_rr_client) { return false; } // If as ORIGINATOR_ID is not present add one. //Form a new path attribute list containing the new AS path FPAListRef& palist = rtmsg.attributes(); if (0 == palist->originator_id()) { if (rtmsg.origin_peer()->get_peer_type() == PEER_TYPE_INTERNAL) { OriginatorIDAttribute originator_id_att(_bgp_id); palist->add_path_attribute(originator_id_att); } else { OriginatorIDAttribute originator_id_att(rtmsg.origin_peer()->id()); palist->add_path_attribute(originator_id_att); } } // Prepend the CLUSTER_ID to the CLUSTER_LIST, if the CLUSTER_LIST // does not exist add one. const ClusterListAttribute *cla = palist->cluster_list(); ClusterListAttribute *ncla = 0; if (0 == cla) { ncla = new ClusterListAttribute; } else { ncla = dynamic_cast(cla->clone()); palist->remove_attribute_by_type(CLUSTER_LIST); } ncla->prepend_cluster_id(_cluster_id); palist->add_path_attribute(ncla); rtmsg.set_changed(); debug_msg("Route with originator id and cluster list %s\n", rtmsg.str().c_str()); return true; } /*************************************************************************/ template RRPurgeFilter::RRPurgeFilter() { } template bool RRPurgeFilter::filter(InternalMessage& rtmsg) const { if (!rtmsg.attributes()->originator_id() && !rtmsg.attributes()->cluster_list()) return true; FPAListRef& palist = rtmsg.attributes(); // If an ORIGINATOR_ID is present remove it. if (0 != palist->originator_id()) palist->remove_attribute_by_type(ORIGINATOR_ID); // If a CLUSTER_LIST is present remove it. if (0 != palist->cluster_list()) palist->remove_attribute_by_type(CLUSTER_LIST); rtmsg.set_changed(); debug_msg("Route with originator id and cluster list %s\n", rtmsg.str().c_str()); return true; } /*************************************************************************/ template LocalPrefInsertionFilter:: LocalPrefInsertionFilter(uint32_t default_local_pref) : _default_local_pref(default_local_pref) { } template bool LocalPrefInsertionFilter::filter(InternalMessage& rtmsg) const { debug_msg("local preference insertion filter\n"); //Form a new path attribute list containing the new AS path FPAListRef& palist = rtmsg.attributes(); LocalPrefAttribute local_pref_att(_default_local_pref); //Either a bad peer or running this filter multiple times mean //that local preference may already be present so remove it. palist->remove_attribute_by_type(LOCAL_PREF); palist->add_path_attribute(local_pref_att); rtmsg.set_changed(); debug_msg("Route with local preference %s\n", rtmsg.str().c_str()); return true; } /*************************************************************************/ template LocalPrefRemovalFilter::LocalPrefRemovalFilter() { } template bool LocalPrefRemovalFilter::filter(InternalMessage& rtmsg) const { debug_msg("local preference removal filter\n"); FPAListRef& palist = rtmsg.attributes(); palist->remove_attribute_by_type(LOCAL_PREF); rtmsg.set_changed(); return true; } /*************************************************************************/ template MEDInsertionFilter:: MEDInsertionFilter(NextHopResolver& next_hop_resolver) : _next_hop_resolver(next_hop_resolver) { } template bool MEDInsertionFilter::filter(InternalMessage& rtmsg) const { debug_msg("MED insertion filter\n"); debug_msg("Route: %s\n", rtmsg.route()->str().c_str()); //XXX theoretically unsafe test for debugging purposes XLOG_ASSERT(rtmsg.route()->igp_metric() != 0xffffffff); FPAListRef& palist = rtmsg.attributes(); MEDAttribute med_att(rtmsg.route()->igp_metric()); palist->add_path_attribute(med_att); //note that we changed the route rtmsg.set_changed(); debug_msg("Route with local preference %s\n", rtmsg.str().c_str()); return true; } /*************************************************************************/ template MEDRemovalFilter::MEDRemovalFilter() { } template bool MEDRemovalFilter::filter(InternalMessage& rtmsg) const { debug_msg("MED removal filter\n"); //Form a new path attribute list containing the new AS path FPAListRef& palist = rtmsg.attributes(); palist->remove_attribute_by_type(MED); //note that we changed the route rtmsg.set_changed(); return true; } /*************************************************************************/ template KnownCommunityFilter::KnownCommunityFilter(PeerType peer_type) : _peer_type(peer_type) { } template bool KnownCommunityFilter::filter(InternalMessage& rtmsg) const { const CommunityAttribute* ca = rtmsg.attributes()->community_att(); if (ca == NULL) return true; // Routes with NO_ADVERTISE don't get sent to anyone else if (ca->contains(CommunityAttribute::NO_ADVERTISE)) { return false; } if (_peer_type == PEER_TYPE_EBGP) { // Routes with NO_EXPORT don't get sent to EBGP peers if (ca->contains(CommunityAttribute::NO_EXPORT)) { return false; } } if (_peer_type == PEER_TYPE_EBGP || _peer_type == PEER_TYPE_EBGP_CONFED) { // Routes with NO_EXPORT_SUBCONFED don't get sent to EBGP // peers or to other members ASes inside a confed if (ca->contains(CommunityAttribute::NO_EXPORT_SUBCONFED)) { return false; } } return true; } /*************************************************************************/ template UnknownFilter::UnknownFilter() { } template bool UnknownFilter::filter(InternalMessage& rtmsg) const { debug_msg("Unknown filter\n"); FPAListRef palist = rtmsg.attributes(); palist->process_unknown_attributes(); rtmsg.set_changed(); return true; } /*************************************************************************/ template OriginateRouteFilter::OriginateRouteFilter(const AsNum &as_num, PeerType peer_type) : _as_num(as_num), _peer_type(peer_type) { } template bool OriginateRouteFilter::filter(InternalMessage& rtmsg) const { debug_msg("Originate Route Filter\n"); // If we didn't originate this route forget it. if (!rtmsg.origin_peer()->originate_route_handler()) { return true; } return true; } /*************************************************************************/ template FilterVersion::FilterVersion(NextHopResolver& next_hop_resolver) : _genid(0), _used(false), _ref_count(0), _next_hop_resolver(next_hop_resolver) { } template FilterVersion::~FilterVersion() { typename list *>::iterator iter; iter = _filters.begin(); while (iter != _filters.end()) { delete (*iter); ++iter; } } template int FilterVersion::add_aggregation_filter(bool is_ibgp) { AggregationFilter* aggregation_filter; aggregation_filter = new AggregationFilter(is_ibgp); _filters.push_back(aggregation_filter); return 0; } template int FilterVersion::add_simple_AS_filter(const AsNum& as_num) { SimpleASFilter* AS_filter; AS_filter = new SimpleASFilter(as_num); _filters.push_back(AS_filter); return 0; } template int FilterVersion::add_route_reflector_input_filter(IPv4 bgp_id, IPv4 cluster_id) { RRInputFilter* RR_input_filter; RR_input_filter = new RRInputFilter(bgp_id, cluster_id); _filters.push_back(RR_input_filter); return 0; } template int FilterVersion::add_AS_prepend_filter(const AsNum& as_num, bool is_confederation_peer) { ASPrependFilter* AS_prepender; AS_prepender = new ASPrependFilter(as_num, is_confederation_peer); _filters.push_back(AS_prepender); return 0; } template int FilterVersion::add_nexthop_rewrite_filter(const A& nexthop, bool directly_connected, const IPNet &subnet) { NexthopRewriteFilter* nh_rewriter; nh_rewriter = new NexthopRewriteFilter(nexthop, directly_connected, subnet); _filters.push_back(nh_rewriter); return 0; } template int FilterVersion::add_nexthop_peer_check_filter(const A& nexthop, const A& peer_address) { NexthopPeerCheckFilter* nh_peer_check; nh_peer_check = new NexthopPeerCheckFilter(nexthop, peer_address); _filters.push_back(nh_peer_check); return 0; } template int FilterVersion::add_ibgp_loop_filter() { IBGPLoopFilter* ibgp_filter; ibgp_filter = new IBGPLoopFilter; _filters.push_back(ibgp_filter); return 0; } template int FilterVersion::add_route_reflector_ibgp_loop_filter(bool client, IPv4 bgp_id, IPv4 cluster_id) { RRIBGPLoopFilter* rr_ibgp_filter; rr_ibgp_filter = new RRIBGPLoopFilter(client, bgp_id, cluster_id); _filters.push_back(rr_ibgp_filter); return 0; } template int FilterVersion::add_route_reflector_purge_filter() { RRPurgeFilter* rr_purge_filter; rr_purge_filter = new RRPurgeFilter; _filters.push_back(rr_purge_filter); return 0; } template int FilterVersion::add_localpref_insertion_filter(uint32_t default_local_pref) { LocalPrefInsertionFilter *localpref_filter; localpref_filter = new LocalPrefInsertionFilter(default_local_pref); _filters.push_back(localpref_filter); return 0; } template int FilterVersion::add_localpref_removal_filter() { LocalPrefRemovalFilter *localpref_filter; localpref_filter = new LocalPrefRemovalFilter(); _filters.push_back(localpref_filter); return 0; } template int FilterVersion::add_med_insertion_filter() { MEDInsertionFilter *med_filter; med_filter = new MEDInsertionFilter(_next_hop_resolver); _filters.push_back(med_filter); return 0; } template int FilterVersion::add_med_removal_filter() { MEDRemovalFilter *med_filter; med_filter = new MEDRemovalFilter(); _filters.push_back(med_filter); return 0; } template int FilterVersion::add_known_community_filter(PeerType peer_type) { KnownCommunityFilter *wkc_filter; wkc_filter = new KnownCommunityFilter(peer_type); _filters.push_back(wkc_filter); return 0; } template int FilterVersion::add_unknown_filter() { UnknownFilter *unknown_filter; unknown_filter = new UnknownFilter(); _filters.push_back(unknown_filter); return 0; } template int FilterVersion::add_originate_route_filter(const AsNum &asn, PeerType peer_type) { OriginateRouteFilter *originate_route_filter; originate_route_filter = new OriginateRouteFilter(asn, peer_type); _filters.push_back(originate_route_filter); return 0; } template bool FilterVersion::apply_filters(InternalMessage& rtmsg, int ref_change) { bool filter_passed = true; _used = true; typename list *>::const_iterator iter; iter = _filters.begin(); while (iter != _filters.end()) { filter_passed = (*iter)->filter(rtmsg); if (filter_passed == false) break; ++iter; } _ref_count += ref_change; return filter_passed; } /*************************************************************************/ template FilterTable::FilterTable(string table_name, Safi safi, BGPRouteTable *parent_table, NextHopResolver& next_hop_resolver) : BGPRouteTable("FilterTable-" + table_name, safi), _next_hop_resolver(next_hop_resolver), _do_versioning(false) { this->_parent = parent_table; _current_filter = new FilterVersion(_next_hop_resolver); } template FilterTable::~FilterTable() { set *> filters; typename map * >::iterator i; for (i = _filter_versions.begin(); i != _filter_versions.end(); i++) { filters.insert(i->second); } typename set * >::iterator j; for (j = filters.begin(); j != filters.end(); j++) { if ((*j) == _current_filter) _current_filter = 0; delete (*j); } if (_current_filter) delete _current_filter; } template void FilterTable::reconfigure_filter() { // if the current filter has never been used, we can delete it now if (_current_filter->ref_count() == 0) { if (_current_filter->used()) { _deleted_filters.insert(_current_filter->genid()); _filter_versions.erase(_current_filter->genid()); } delete _current_filter; } _current_filter = new FilterVersion(_next_hop_resolver); } template int FilterTable::add_route(InternalMessage &rtmsg, BGPRouteTable *caller) { debug_msg("\n %s\n caller: %s\n rtmsg: %p genid: %d route: %p\n%s\n", this->tablename().c_str(), caller->tablename().c_str(), &rtmsg, rtmsg.genid(), rtmsg.route(), rtmsg.str().c_str()); XLOG_ASSERT(caller == this->_parent); XLOG_ASSERT(this->_next_table != NULL); XLOG_ASSERT(!rtmsg.copied()); bool filtered_passed = apply_filters(rtmsg, 1); if (filtered_passed == false) { return ADD_FILTERED; } return this->_next_table->add_route(rtmsg, (BGPRouteTable*)this); } template int FilterTable::replace_route(InternalMessage &old_rtmsg, InternalMessage &new_rtmsg, BGPRouteTable *caller) { debug_msg("\n %s\n" "caller: %s\n" "old rtmsg: %p new rtmsg: %p " "old route: %p" "new route: %p" "old: %s\n new: %s\n", this->tablename().c_str(), caller->tablename().c_str(), &old_rtmsg, &new_rtmsg, old_rtmsg.route(), new_rtmsg.route(), old_rtmsg.str().c_str(), new_rtmsg.str().c_str()); XLOG_ASSERT(caller == this->_parent); XLOG_ASSERT(this->_next_table != NULL); /* increment ref_count before decrementing it */ bool new_passed_filter = apply_filters(new_rtmsg, 1); bool old_passed_filter = apply_filters(old_rtmsg, -1); int result; if (old_passed_filter == false && new_passed_filter == false) { //neither old nor new routes passed the filter result = ADD_FILTERED; } else if (old_passed_filter == true && new_passed_filter == false) { //the new route failed to pass the filter //the replace becomes a delete this->_next_table->delete_route(old_rtmsg, (BGPRouteTable*)this); result = ADD_FILTERED; } else if (old_passed_filter == false && new_passed_filter == true) { //the replace becomes an add result = this->_next_table->add_route(new_rtmsg, (BGPRouteTable*)this); } else { result = this->_next_table->replace_route(old_rtmsg, new_rtmsg, (BGPRouteTable*)this); } return result; } template int FilterTable::route_dump(InternalMessage &rtmsg, BGPRouteTable *caller, const PeerHandler *dump_peer) { XLOG_ASSERT(caller == this->_parent); XLOG_ASSERT(this->_next_table != NULL); bool filter_passed = apply_filters(rtmsg, 0); if (filter_passed == false) return ADD_FILTERED; int result; result = this->_next_table->route_dump(rtmsg, (BGPRouteTable*)this, dump_peer); return result; } template int FilterTable::delete_route(InternalMessage &rtmsg, BGPRouteTable *caller) { debug_msg("\n %s\n caller: %s\n rtmsg: %p route: %p\n%s\n", this->tablename().c_str(), caller->tablename().c_str(), &rtmsg, rtmsg.route(), rtmsg.str().c_str()); XLOG_ASSERT(caller == this->_parent); XLOG_ASSERT(this->_next_table != NULL); bool filter_passed = apply_filters(rtmsg, -1); if (filter_passed == false) return 0; return this->_next_table->delete_route(rtmsg, (BGPRouteTable*)this); } template int FilterTable::push(BGPRouteTable *caller) { XLOG_ASSERT(caller == this->_parent); XLOG_ASSERT(this->_next_table != NULL); return this->_next_table->push((BGPRouteTable*)this); } template const SubnetRoute* FilterTable::lookup_route(const IPNet &net, uint32_t& genid, FPAListRef& pa_list) const { //We should never get called with a route that gets modified by //our filters, because there's no persistent storage to return as //the result. But we can get called with a route that gets //dropped by our filters. const SubnetRoute *found_route; uint32_t found_genid; found_route = this->_parent->lookup_route(net, found_genid, pa_list); if (found_route == NULL) return NULL; InternalMessage msg(found_route, pa_list, NULL, found_genid); bool filter_passed = apply_filters(msg); if (filter_passed == false) return NULL; genid = found_genid; return found_route; } template void FilterTable::route_used(const SubnetRoute* rt, bool in_use) { this->_parent->route_used(rt, in_use); } template string FilterTable::str() const { string s = "FilterTable" + this->tablename(); return s; } template int FilterTable::add_aggregation_filter(bool is_ibgp) { _current_filter->add_aggregation_filter(is_ibgp); return 0; } template int FilterTable::add_simple_AS_filter(const AsNum& as_num) { _current_filter->add_simple_AS_filter(as_num); return 0; } template int FilterTable::add_route_reflector_input_filter(IPv4 bgp_id, IPv4 cluster_id) { _current_filter->add_route_reflector_input_filter(bgp_id, cluster_id); return 0; } template int FilterTable::add_AS_prepend_filter(const AsNum& as_num, bool is_confederation_peer) { _current_filter->add_AS_prepend_filter(as_num, is_confederation_peer); return 0; } template int FilterTable::add_nexthop_rewrite_filter(const A& nexthop, bool directly_connected, const IPNet &subnet) { _current_filter->add_nexthop_rewrite_filter(nexthop, directly_connected, subnet); return 0; } template int FilterTable::add_nexthop_peer_check_filter(const A& nexthop, const A& peer_address) { _current_filter->add_nexthop_peer_check_filter(nexthop, peer_address); return 0; } template int FilterTable::add_ibgp_loop_filter() { _current_filter->add_ibgp_loop_filter(); return 0; } template int FilterTable::add_route_reflector_ibgp_loop_filter(bool client, IPv4 bgp_id, IPv4 cluster_id) { _current_filter->add_route_reflector_ibgp_loop_filter(client, bgp_id, cluster_id); return 0; } template int FilterTable::add_route_reflector_purge_filter() { _current_filter->add_route_reflector_purge_filter(); return 0; } template int FilterTable::add_localpref_insertion_filter(uint32_t default_local_pref) { _current_filter->add_localpref_insertion_filter(default_local_pref); return 0; } template int FilterTable::add_localpref_removal_filter() { _current_filter->add_localpref_removal_filter(); return 0; } template int FilterTable::add_med_insertion_filter() { _current_filter->add_med_insertion_filter(); return 0; } template int FilterTable::add_known_community_filter(PeerType peer_type) { _current_filter->add_known_community_filter(peer_type); return 0; } template int FilterTable::add_med_removal_filter() { _current_filter->add_med_removal_filter(); return 0; } template int FilterTable::add_unknown_filter() { _current_filter->add_unknown_filter(); return 0; } template int FilterTable::add_originate_route_filter(const AsNum &asn, PeerType peer_type) { _current_filter->add_originate_route_filter(asn, peer_type); return 0; } template bool FilterTable::apply_filters(InternalMessage& rtmsg) const { // in this case, this is actually const, so this hack is safe return const_cast*>(this)->apply_filters(rtmsg, 0); } template bool FilterTable::apply_filters(InternalMessage& rtmsg, int ref_change) { bool filter_passed = true; FilterVersion *filter; if (_do_versioning) { typename map* >::iterator i; uint32_t genid = rtmsg.genid(); i = _filter_versions.find(genid); if (i == _filter_versions.end()) { // check we're not trying to use a GenID that has been retired. XLOG_ASSERT(_deleted_filters.find(genid) == _deleted_filters.end()); _filter_versions[genid] = _current_filter; _current_filter->set_genid(genid); filter = _current_filter; } else { filter = i->second; XLOG_ASSERT(filter->genid() == genid); } filter_passed = filter->apply_filters(rtmsg, ref_change); // if there are no more routes that used an old filter, delete it now if (filter->ref_count() == 0) { if (filter != _current_filter) { if (filter->used()) _deleted_filters.insert(filter->genid()); delete filter; _filter_versions.erase(i); } } } else { filter_passed = _current_filter->apply_filters(rtmsg, ref_change); } if (filter_passed == false) drop_message(&rtmsg); return filter_passed; } template void FilterTable::drop_message(const InternalMessage *rtmsg) const { if (rtmsg->copied()) { //It's the responsibility of the final recipient of a //copied route to store it or free it. rtmsg->route()->unref(); } #if 0 if (modified) { //This filterbank created this message. We need to delete //it because no-one else can. delete rtmsg; } #endif } template bool FilterTable::get_next_message(BGPRouteTable *next_table) { BGPRouteTable* parent = this->_parent; XLOG_ASSERT(parent); XLOG_ASSERT(this->_next_table == next_table); return parent->get_next_message(this); } template class FilterTable; template class FilterTable; xorp/bgp/rib_ipc_handler.hh0000664000076400007640000001662411540225521016063 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/bgp/rib_ipc_handler.hh,v 1.51 2008/11/08 06:14:37 mjh Exp $ #ifndef __BGP_RIB_IPC_HANDLER_HH__ #define __BGP_RIB_IPC_HANDLER_HH__ #include #include "peer_handler.hh" #include "plumbing.hh" #include "libxorp/eventloop.hh" #include "libxorp/timer.hh" #include "libxipc/xrl_std_router.hh" #include "policy/backend/policytags.hh" class RibIpcHandler; class EventLoop; template class XrlQueue { public: XrlQueue(RibIpcHandler &rib_ipc_handler, XrlStdRouter &xrl_router, BGPMain &_bgp); void queue_add_route(string ribname, bool ibgp, Safi, const IPNet& net, const A& nexthop, const PolicyTags& policytags); void queue_delete_route(string ribname, bool ibgp, Safi, const IPNet& net); bool busy(); private: static const size_t XRL_HIWAT = 100; // Maximum number of XRLs // allowed in flight. static const size_t XRL_LOWAT = 10; // Low watermark for XRL // in-flight flow control // hysteresis. RibIpcHandler &_rib_ipc_handler; XrlStdRouter &_xrl_router; BGPMain &_bgp; struct Queued { bool add; string ribname; bool ibgp; Safi safi; IPNet net; A nexthop; string comment; PolicyTags policytags; }; deque _xrl_queue; size_t _flying; //XRLs currently in flight bool _flow_controlled; /** * Flow control hysteresis */ bool flow_controlled() { if (_flying >= XRL_HIWAT) _flow_controlled = true; else if (_flying <= XRL_LOWAT) _flow_controlled = false; return _flow_controlled; } /** * Start the transmission of XRLs to tbe RIB. */ void start(); /** * The specialised method called by sendit to deal with IPv4/IPv6. * * @param q the queued command. * @param bgp "ibg"p or "ebgp". * @return True if the add/delete was queued. */ bool sendit_spec(Queued& q, const char *bgp); EventLoop& eventloop() const; void route_command_done(const XrlError& error, const string comment); }; /* * RibIpcHandler's job is to convert to and from XRLs, and to handle the * XRL state machine for talking to the RIB process */ class RibIpcHandler : public PeerHandler { public: RibIpcHandler(XrlStdRouter& xrl_router, BGPMain& bgp); ~RibIpcHandler(); /** * Set the rib's name, allows for having a dummy rib or not having * a RIB at all. */ bool register_ribname(const string& r); int start_packet(); /* add_route and delete_route are called to propagate a route *to* the RIB. */ int add_route(const SubnetRoute &rt, FPAList4Ref& pa_list, bool ibgp, Safi safi); int replace_route(const SubnetRoute &old_rt, bool old_ibgp, const SubnetRoute &new_rt, bool new_ibgp, FPAList4Ref& pa_list, Safi safi); int delete_route(const SubnetRoute &rt, FPAList4Ref& pa_list, bool ibgp, Safi safi); void rib_command_done(const XrlError& error, const char *comment); PeerOutputState push_packet(); void set_plumbing(BGPPlumbing *plumbing_unicast, BGPPlumbing *plumbing_multicast) { _plumbing_unicast = plumbing_unicast; _plumbing_multicast = plumbing_multicast; } /** * @return true if routes are being sent to the RIB. */ bool busy() { return (_v4_queue.busy() #ifdef HAVE_IPV6 || _v6_queue.busy() #endif ); } /** * Originate an IPv4 route * * @param origin of the path information * @param aspath * @param nlri subnet to announce * @param next_hop to forward to * @param unicast if true install in unicast routing table * @param multicast if true install in multicast routing table * @param policytags policy-tags associated with the route * * @return true on success */ bool originate_route(const OriginType origin, const ASPath& aspath, const IPv4Net& nlri, const IPv4& next_hop, const bool& unicast, const bool& multicast, const PolicyTags& policytags); /** * Withdraw an IPv4 route * * @param nlri subnet to withdraw * @param unicast if true withdraw from unicast routing table * @param multicast if true withdraw from multicast routing table * * @return true on success */ bool withdraw_route(const IPv4Net& nlri, const bool& unicast, const bool& multicast); virtual uint32_t get_unique_id() const { return _fake_unique_id; } //fake a zero IP address so the RIB IPC handler gets listed first //in the Fanout Table. const IPv4& id() const { return _fake_id; } virtual PeerType get_peer_type() const { return PEER_TYPE_INTERNAL; } /** * This is the handler that deals with originating routes. * * @return true */ virtual bool originate_route_handler() const {return true;} /** * @return the main eventloop */ virtual EventLoop& eventloop() const { return _xrl_router.eventloop();} /** IPv6 stuff */ #ifdef HAVE_IPV6 int add_route(const SubnetRoute &rt, FPAList6Ref& pa_list, bool ibgp, Safi safi); int replace_route(const SubnetRoute &old_rt, bool old_ibgp, const SubnetRoute &new_rt, bool new_ibgp, FPAList6Ref& pa_list, Safi safi); int delete_route(const SubnetRoute &rt, FPAList6Ref& pa_list, bool ibgp, Safi safi); /** * Originate an IPv6 route * * @param origin of the path information * @param aspath * @param nlri subnet to announce * @param next_hop to forward to * @param unicast if true install in unicast routing table * @param multicast if true install in multicast routing table * @param policytags policy-tags associated with the route * * @return true on success */ bool originate_route(const OriginType origin, const ASPath& aspath, const IPv6Net& nlri, const IPv6& next_hop, const bool& unicast, const bool& multicast, const PolicyTags& policytags); /** * Withdraw an IPv6 route * * @return true on success */ bool withdraw_route(const IPv6Net& nlri, const bool& unicast, const bool& multicast); #endif // ipv6 private: bool unregister_rib(string ribname); string _ribname; XrlStdRouter& _xrl_router; XrlQueue _v4_queue; #ifdef HAVE_IPV6 XrlQueue _v6_queue; #endif const uint32_t _fake_unique_id; IPv4 _fake_id; }; #endif // __BGP_RIB_IPC_HANDLER_HH__ xorp/bgp/xrl_target.hh0000664000076400007640000003405711540225521015132 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/bgp/xrl_target.hh,v 1.46 2008/10/02 21:56:23 bms Exp $ #ifndef __BGP_XRL_TARGET_HH__ #define __BGP_XRL_TARGET_HH__ #include "xrl/targets/bgp_base.hh" class BGPMain; class XrlBgpTarget : XrlBgpTargetBase { public: XrlBgpTarget(XrlRouter *r, BGPMain& bgp); XrlCmdError common_0_1_get_target_name(string& name); XrlCmdError common_0_1_get_version(string& version); /** * Get status from Xrl Target */ XrlCmdError common_0_1_get_status( // Output values, uint32_t& status, string& reason); /** * Request target shut down cleanly */ XrlCmdError common_0_1_shutdown(); XrlCmdError common_0_1_startup() { return XrlCmdError::OKAY(); } XrlCmdError bgp_0_3_get_bgp_version( // Output values, uint32_t& version); XrlCmdError bgp_0_3_local_config( // Input values, const string& as_num, const IPv4& id, const bool& use_4byte_asnums); XrlCmdError bgp_0_3_set_local_as( // Input values, const string& as); XrlCmdError bgp_0_3_get_local_as( // Output values, string& as); XrlCmdError bgp_0_3_set_4byte_as_support( // Input values, const bool& enabled); XrlCmdError bgp_0_3_set_bgp_id( // Input values, const IPv4& id); XrlCmdError bgp_0_3_get_bgp_id( // Output values, IPv4& id); XrlCmdError bgp_0_3_set_confederation_identifier( // Input values, const string& as, const bool& disable); XrlCmdError bgp_0_3_set_cluster_id( // Input values, const IPv4& cluster_id, const bool& disable); XrlCmdError bgp_0_3_set_damping( // Input values, const uint32_t& half_life, const uint32_t& max_suppress, const uint32_t& reuse, const uint32_t& suppress, const bool& disable); XrlCmdError bgp_0_3_add_peer( // Input values, const string& local_dev, const string& local_ip, const uint32_t& local_port, const string& peer_ip, const uint32_t& peer_port, const string& as, const IPv4& next_hop, const uint32_t& holdtime); XrlCmdError bgp_0_3_delete_peer( // Input values, const string& local_ip, const uint32_t& local_port, const string& peer_ip, const uint32_t& peer_port); XrlCmdError bgp_0_3_enable_peer( // Input values, const string& local_ip, const uint32_t& local_port, const string& peer_ip, const uint32_t& peer_port); XrlCmdError bgp_0_3_disable_peer( // Input values, const string& local_ip, const uint32_t& local_port, const string& peer_ip, const uint32_t& peer_port); XrlCmdError bgp_0_3_change_local_ip( // Input values, const string& local_ip, const uint32_t& local_port, const string& peer_ip, const uint32_t& peer_port, const string& new_local_ip, const string& new_local_dev); XrlCmdError bgp_0_3_change_local_port( // Input values, const string& local_ip, const uint32_t& local_port, const string& peer_ip, const uint32_t& peer_port, const uint32_t& new_local_port); XrlCmdError bgp_0_3_change_peer_port( // Input values, const string& local_ip, const uint32_t& local_port, const string& peer_ip, const uint32_t& peer_port, const uint32_t& new_peer_port); XrlCmdError bgp_0_3_set_peer_as( // Input values, const string& local_ip, const uint32_t& local_port, const string& peer_ip, const uint32_t& peer_port, const string& peer_as); XrlCmdError bgp_0_3_set_holdtime( // Input values, const string& local_ip, const uint32_t& local_port, const string& peer_ip, const uint32_t& peer_port, const uint32_t& holdtime); XrlCmdError bgp_0_3_set_delay_open_time( // Input values, const string& local_ip, const uint32_t& local_port, const string& peer_ip, const uint32_t& peer_port, const uint32_t& delay_open_time); XrlCmdError bgp_0_3_set_route_reflector_client( // Input values, const string& local_ip, const uint32_t& local_port, const string& peer_ip, const uint32_t& peer_port, const bool& state); XrlCmdError bgp_0_3_set_confederation_member( // Input values, const string& local_ip, const uint32_t& local_port, const string& peer_ip, const uint32_t& peer_port, const bool& state); XrlCmdError bgp_0_3_set_prefix_limit( // Input values, const string& local_ip, const uint32_t& local_port, const string& peer_ip, const uint32_t& peer_port, const uint32_t& maximum, const bool& state); XrlCmdError bgp_0_3_set_nexthop4( // Input values, const string& local_ip, const uint32_t& local_port, const string& peer_ip, const uint32_t& peer_port, const IPv4& next_hop); XrlCmdError bgp_0_3_set_peer_state( // Input values, const string& local_ip, const uint32_t& local_port, const string& peer_ip, const uint32_t& peer_port, const bool& state); XrlCmdError bgp_0_3_set_peer_md5_password( // Input values, const string& local_ip, const uint32_t& local_port, const string& peer_ip, const uint32_t& peer_port, const string& password); XrlCmdError bgp_0_3_activate( // Input values, const string& local_ip, const uint32_t& local_port, const string& peer_ip, const uint32_t& peer_port); XrlCmdError bgp_0_3_next_hop_rewrite_filter( // Input values, const string& local_ip, const uint32_t& local_port, const string& peer_ip, const uint32_t& peer_port, const IPv4& next_hop); XrlCmdError bgp_0_3_originate_route4( // Input values, const IPv4Net& nlri, const IPv4& next_hop, const bool& unicast, const bool& multicast); XrlCmdError bgp_0_3_withdraw_route4( // Input values, const IPv4Net& nlri, const bool& unicast, const bool& multicast); XrlCmdError bgp_0_3_trace( // Input values, const string& tvar, const bool& enable); XrlCmdError bgp_0_3_get_peer_list_start( // Output values, uint32_t& token, bool& more); XrlCmdError bgp_0_3_get_peer_list_next( // Input values, const uint32_t& token, // Output values, string& local_ip, uint32_t& local_port, string& peer_ip, uint32_t& peer_port, bool& more); XrlCmdError bgp_0_3_get_peer_id( // Input values, const string& local_ip, const uint32_t& local_port, const string& peer_ip, const uint32_t& peer_port, // Output values, IPv4& peer_id); XrlCmdError bgp_0_3_get_peer_status( // Input values, const string& local_ip, const uint32_t& local_port, const string& peer_ip, const uint32_t& peer_port, // Output values, uint32_t& peer_state, uint32_t& admin_status); XrlCmdError bgp_0_3_get_peer_negotiated_version( // Input values, const string& local_ip, const uint32_t& local_port, const string& peer_ip, const uint32_t& peer_port, // Output values, int32_t& neg_version); XrlCmdError bgp_0_3_get_peer_as( // Input values, const string& local_ip, const uint32_t& local_port, const string& peer_ip, const uint32_t& peer_port, // Output values, string& peer_as); XrlCmdError bgp_0_3_get_peer_msg_stats( // Input values, const string& local_ip, const uint32_t& local_port, const string& peer_ip, const uint32_t& peer_port, // Output values, uint32_t& in_updates, uint32_t& out_updates, uint32_t& in_msgs, uint32_t& out_msgs, uint32_t& last_error, uint32_t& in_update_elapsed); XrlCmdError bgp_0_3_get_peer_established_stats( // Input values, const string& local_ip, const uint32_t& local_port, const string& peer_ip, const uint32_t& peer_port, // Output values, uint32_t& transitions, uint32_t& established_time); XrlCmdError bgp_0_3_get_peer_timer_config( // Input values, const string& local_ip, const uint32_t& local_port, const string& peer_ip, const uint32_t& peer_port, // Output values, uint32_t& retry_interval, uint32_t& hold_time, uint32_t& keep_alive, uint32_t& hold_time_conf, uint32_t& keep_alive_conf, uint32_t& min_as_origin_interval, uint32_t& min_route_adv_interval); XrlCmdError bgp_0_3_register_rib( // Input values, const string& name); XrlCmdError bgp_0_3_get_v4_route_list_start( // Input values, const IPv4Net& net, const bool& unicast, const bool& multicast, // Output values, uint32_t& token); XrlCmdError bgp_0_3_get_v4_route_list_next( // Input values, const uint32_t& token, // Output values, IPv4& peer_id, IPv4Net& net, uint32_t& best_and_origin, vector& aspath, IPv4& nexthop, int32_t& med, int32_t& localpref, int32_t& atomic_agg, vector& aggregator, int32_t& calc_localpref, vector& attr_unknown, bool& valid, bool& unicast, bool& multicast); XrlCmdError rib_client_0_1_route_info_changed4( // Input values, const IPv4& addr, const uint32_t& prefix_len, const IPv4& nexthop, const uint32_t& metric, const uint32_t& admin_distance, const string& protocol_origin); XrlCmdError rib_client_0_1_route_info_invalid4( // Input values, const IPv4& addr, const uint32_t& prefix_len); XrlCmdError bgp_0_3_set_parameter( // Input values, const string& local_ip, const uint32_t& local_port, const string& peer_ip, const uint32_t& peer_port, const string& parameter, const bool& toggle); XrlCmdError finder_event_observer_0_1_xrl_target_birth( // Input values, const string& target_class, const string& target_instance); XrlCmdError finder_event_observer_0_1_xrl_target_death( // Input values, const string& target_class, const string& target_instance); XrlCmdError policy_backend_0_1_configure( // Input values, const uint32_t& filter, const string& conf); XrlCmdError policy_backend_0_1_reset( // Input values, const uint32_t& filter); XrlCmdError policy_backend_0_1_push_routes(); XrlCmdError policy_redist4_0_1_add_route4( // Input values, const IPv4Net& network, const bool& unicast, const bool& multicast, const IPv4& nexthop, const uint32_t& metric, const XrlAtomList& policytags); XrlCmdError policy_redist4_0_1_delete_route4( // Input values, const IPv4Net& network, const bool& unicast, const bool& multicast); #ifdef HAVE_IPV6 XrlCmdError bgp_0_3_set_nexthop6( // Input values, const string& local_ip, const uint32_t& local_port, const string& peer_ip, const uint32_t& peer_port, const IPv6& next_hop); XrlCmdError bgp_0_3_get_nexthop6( // Input values, const string& local_ip, const uint32_t& local_port, const string& peer_ip, const uint32_t& peer_port, // Output values, IPv6& next_hop); XrlCmdError bgp_0_3_originate_route6( // Input values, const IPv6Net& nlri, const IPv6& next_hop, const bool& unicast, const bool& multicast); XrlCmdError bgp_0_3_withdraw_route6( // Input values, const IPv6Net& nlri, const bool& unicast, const bool& multicast); XrlCmdError bgp_0_3_get_v6_route_list_start( // Input values, const IPv6Net& net, const bool& unicast, const bool& multicast, // Output values, uint32_t& token); XrlCmdError bgp_0_3_get_v6_route_list_next( // Input values, const uint32_t& token, // Output values, IPv4& peer_id, IPv6Net& net, uint32_t& best_and_origin, vector& aspath, IPv6& nexthop, int32_t& med, int32_t& localpref, int32_t& atomic_agg, vector& aggregator, int32_t& calc_localpref, vector& attr_unknown, bool& valid, bool& unicast, bool& multicast); XrlCmdError rib_client_0_1_route_info_changed6( // Input values, const IPv6& addr, const uint32_t& prefix_len, const IPv6& nexthop, const uint32_t& metric, const uint32_t& admin_distance, const string& protocol_origin); XrlCmdError rib_client_0_1_route_info_invalid6( // Input values, const IPv6& addr, const uint32_t& prefix_len); XrlCmdError policy_redist6_0_1_add_route6( // Input values, const IPv6Net& network, const bool& unicast, const bool& multicast, const IPv6& nexthop, const uint32_t& metric, const XrlAtomList& policytags); XrlCmdError policy_redist6_0_1_delete_route6( // Input values, const IPv6Net& network, const bool& unicast, const bool& multicast); #endif //ipv6 #ifndef XORP_DISABLE_PROFILE XrlCmdError profile_0_1_enable( // Input values, const string& pname); XrlCmdError profile_0_1_disable( // Input values, const string& pname); XrlCmdError profile_0_1_get_entries( // Input values, const string& pname, const string& instance_name); XrlCmdError profile_0_1_clear( // Input values, const string& pname); XrlCmdError profile_0_1_list( // Output values, string& info); #endif bool waiting(); bool done(); private: /** * The main object that all requests go to. */ BGPMain& _bgp; /** * Waiting for configuration. Such as our own AS number. */ bool _awaiting_config; /** * Waiting for AS number. */ bool _awaiting_as; /** * Local AS number. */ AsNum _as; /** * Waiting for BGP id */ bool _awaiting_bgp_id; /** * BGP id. */ IPv4 _id; /** * Waiting for info on using 4-byte AS numbers. */ bool _awaiting_4byte_asnums; /** * Do we use 4byte AS numbers? */ bool _use_4byte_asnums; /** * Set to true if we should be exiting. */ bool _done; }; #endif // __BGP_XRL_TARGET_HH__ xorp/bgp/route_table_fanout.cc0000664000076400007640000007613511421137511016630 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net //#define DEBUG_LOGGING //#define DEBUG_PRINT_FUNCTION_NAME // #define DEBUG_QUEUE #include "bgp_module.h" #include "libxorp/xlog.h" #include "route_table_fanout.hh" #include "route_table_dump.hh" template NextTableMap::~NextTableMap() { typename map *, PeerTableInfo* >::iterator i; i = _next_tables.begin(); while (i != _next_tables.end()) { delete i->second; _next_tables.erase(i); i = _next_tables.begin(); } } template void NextTableMap::insert(BGPRouteTable *next_table, const PeerHandler *ph, uint32_t genid) { PeerTableInfo* prpair = new PeerTableInfo(next_table, ph, genid); _next_tables[next_table] = prpair; //we have to choose a sort order so our results are repeatable //across different platforms. we arbitrarily choose to sort on //BGP ID because it should be unique. //check we don't have two peers with the same address if (_next_table_order.find(ph->id().addr()) != _next_table_order.end()) XLOG_WARNING("BGP: Two peers have same BGP ID: %s\n", ph->id().str().c_str()); _next_table_order.insert(make_pair(ph->id().addr(),prpair)); } template void NextTableMap::erase(iterator& iter) { PeerTableInfo* prpair = &(iter.second()); typename map *, PeerTableInfo* >::iterator i; i = _next_tables.find(prpair->route_table()); XLOG_ASSERT(i != _next_tables.end()); uint32_t id = i->second->peer_handler()->id().addr(); _next_tables.erase(i); typename multimap* >::iterator j; j = _next_table_order.find(id); while (j->first == id && j->second != prpair) { //find the right one. j++; } //if it's in _next_table, it must be in _next_table_order too. XLOG_ASSERT(j != _next_table_order.end()); XLOG_ASSERT(j->second == prpair); _next_table_order.erase(j); delete prpair; } template typename NextTableMap::iterator NextTableMap::find(BGPRouteTable *next_table) { typename map *, PeerTableInfo* >::iterator i; i = _next_tables.find(next_table); if (i == _next_tables.end()) return end(); PeerTableInfo* prpair = i->second; typename multimap* >::iterator j; uint32_t id = i->second->peer_handler()->id().addr(); j = _next_table_order.find(id); while (j->first == id && j->second != prpair) { //find the right one. j++; } //if it's in _next_table, it must be in _next_table_order too. XLOG_ASSERT(j != _next_table_order.end()); XLOG_ASSERT(j->second == prpair); return iterator(j); } template typename NextTableMap::iterator NextTableMap::begin() { return iterator(_next_table_order.begin()); } template typename NextTableMap::iterator NextTableMap::end() { return iterator(_next_table_order.end()); }; template FanoutTable::FanoutTable(string table_name, Safi safi, BGPRouteTable *init_parent, PeerHandler *aggr_handler, BGPRouteTable *aggr_table) : BGPRouteTable("FanoutTable-" + table_name, safi) { this->_parent = init_parent; if (aggr_table != NULL && aggr_table != NULL) _aggr_peerinfo = new PeerTableInfo(aggr_table, aggr_handler, GENID_UNKNOWN); else _aggr_peerinfo = NULL; } template FanoutTable::~FanoutTable() { if (_aggr_peerinfo != NULL) delete _aggr_peerinfo; } template int FanoutTable::add_next_table(BGPRouteTable *new_next_table, const PeerHandler *ph, uint32_t genid) { debug_msg("FanoutTable::add_next_table %p %s\n", XORP_UINT_CAST(A::ip_version()), pretty_string_safi(this->safi()), new_next_table, new_next_table->tablename().c_str()); if (_next_tables.find(new_next_table) != _next_tables.end()) { // the next_table is already in the set return -1; } _next_tables.insert(new_next_table, ph, genid); new_next_table->peering_came_up(ph, genid, this); return 0; } template int FanoutTable::remove_next_table(BGPRouteTable *ex_next_table) { debug_msg("removing: %s\n", ex_next_table->tablename().c_str()); typename NextTableMap::iterator iter; iter = _next_tables.find(ex_next_table); if (iter == _next_tables.end()) { // the next_table is not already in the set XLOG_FATAL("Attempt to remove table that is not in list: %s", ex_next_table->tablename().c_str()); } skip_entire_queue(ex_next_table); DumpTable *dtp = dynamic_cast*>(ex_next_table); if (dtp) { remove_dump_table(dtp); dtp->suspend_dump(); } _next_tables.erase(iter); return 0; } template int FanoutTable::replace_next_table(BGPRouteTable *old_next_table, BGPRouteTable *new_next_table) { typename NextTableMap::iterator iter; iter = _next_tables.find(old_next_table); if (iter == _next_tables.end()) { // the next_table is not already in the set XLOG_FATAL("Attempt to remove table that is not in list: %s", old_next_table->tablename().c_str()); } const PeerHandler *peer = iter.second().peer_handler(); uint32_t genid = iter.second().genid(); _next_tables.erase(iter); _next_tables.insert(new_next_table, peer, genid); return 0; } template int FanoutTable::add_route(InternalMessage &rtmsg, BGPRouteTable *caller) { debug_msg("\n %s\n caller: %s\n rtmsg: %p route: %p\n%s\n", this->tablename().c_str(), caller ? caller->tablename().c_str() : "NULL", &rtmsg, rtmsg.route(), rtmsg.str().c_str()); XLOG_ASSERT(caller == this->_parent); XLOG_ASSERT(rtmsg.route()->nexthop_resolved()); XLOG_ASSERT(!rtmsg.attributes()->is_locked()); const PeerHandler *origin_peer = rtmsg.origin_peer(); log("add_route rcvd, net: " + rtmsg.route()->net().str() + " peer: " + origin_peer->peername() + c_format(" filters: %p,%p,%p", rtmsg.route()->policyfilter(0).get(), rtmsg.route()->policyfilter(1).get(), rtmsg.route()->policyfilter(2).get())); typename NextTableMap::iterator i = _next_tables.begin(); list *> queued_peers; while (i != _next_tables.end()) { const PeerHandler *next_peer = i.second().peer_handler(); if (origin_peer == next_peer) { // don't send the route back to the peer it came from debug_msg("FanoutTable::add_route %p.\n Don't send back to %s\n", XORP_UINT_CAST(A::ip_version()), pretty_string_safi(this->safi()), &rtmsg, (i.first())->tablename().c_str()); } else { debug_msg("FanoutTable::add_route %p to %s\n", XORP_UINT_CAST(A::ip_version()), pretty_string_safi(this->safi()), &rtmsg, (i.first())->tablename().c_str()); //we now always add to the queue, and require a //get_next_message from downstream to liberate a queued //entry. debug_msg("Fanout: queuing route, queue len is %u\n", XORP_UINT_CAST(queued_peers.size())); queued_peers.push_back(&(i.second())); // we don't bother to return ADD_UNUSED or ADD_FILTERED, // but if there's a failure, we'll return the last failure // code. Not clear how useful this is, but best not to // hide the failure completely. } i++; } //queue it if anyone needs it. if (queued_peers.empty() == false) { add_to_queue(RTQUEUE_OP_ADD, rtmsg, queued_peers); wakeup_downstream(queued_peers); } return ADD_USED; } template int FanoutTable::replace_route(InternalMessage &old_rtmsg, InternalMessage &new_rtmsg, BGPRouteTable *caller) { debug_msg("\n %s\n" "caller: %s\n" "old rtmsg: %p new rtmsg: %p " "old route: %p" "new route: %p" "old: %s\n new: %s\n", this->tablename().c_str(), caller->tablename().c_str(), &old_rtmsg, &new_rtmsg, old_rtmsg.route(), new_rtmsg.route(), old_rtmsg.str().c_str(), new_rtmsg.str().c_str()); XLOG_ASSERT(caller == this->_parent); XLOG_ASSERT(old_rtmsg.route()->nexthop_resolved()); XLOG_ASSERT(new_rtmsg.route()->nexthop_resolved()); const PeerHandler *origin_peer = old_rtmsg.origin_peer(); XLOG_ASSERT(origin_peer == new_rtmsg.origin_peer()); log("replace_route rcvd, net: " + old_rtmsg.route()->net().str() + " peer: " + origin_peer->peername()); list *> queued_peers; typename NextTableMap::iterator i; for (i = _next_tables.begin(); i != _next_tables.end(); i++) { const PeerHandler *next_peer = i.second().peer_handler(); if (origin_peer == next_peer) { // don't send the route back to the peer it came from } else { debug_msg("FanoutTable::replace_route %p -> %p to %s\n", XORP_UINT_CAST(A::ip_version()), pretty_string_safi(this->safi()), &old_rtmsg, &new_rtmsg, (i.first())->tablename().c_str()); debug_msg("queueing replace_route\n"); queued_peers.push_back(&(i.second())); } } if (queued_peers.empty() == false) { add_replace_to_queue(old_rtmsg, new_rtmsg, queued_peers); wakeup_downstream(queued_peers); } return ADD_USED; } template int FanoutTable::delete_route(InternalMessage &rtmsg, BGPRouteTable *caller) { debug_msg("\n %s\n caller: %s\n rtmsg: %p route: %p\n%s\n", this->tablename().c_str(), caller ? caller->tablename().c_str() : "NULL", &rtmsg, rtmsg.route(), rtmsg.str().c_str()); XLOG_ASSERT(caller == this->_parent); XLOG_ASSERT(rtmsg.route()->nexthop_resolved()); XLOG_ASSERT(!rtmsg.attributes()->is_locked()); const PeerHandler *origin_peer = rtmsg.origin_peer(); log("delete_route rcvd, net: " + rtmsg.route()->net().str() + " peer: " + origin_peer->peername() + c_format(" filters: %p,%p,%p", rtmsg.route()->policyfilter(0).get(), rtmsg.route()->policyfilter(1).get(), rtmsg.route()->policyfilter(2).get())); list *> queued_peers; typename NextTableMap::iterator i; for (i = _next_tables.begin(); i != _next_tables.end(); i++) { const PeerHandler *next_peer = i.second().peer_handler(); if (origin_peer == next_peer) { debug_msg("FanoutTable::delete_route %p.\n Don't send back to %s\n", XORP_UINT_CAST(A::ip_version()), pretty_string_safi(this->safi()), &rtmsg, (i.first())->tablename().c_str()); } else { debug_msg("FanoutTable::delete_route %p to %s\n", XORP_UINT_CAST(A::ip_version()), pretty_string_safi(this->safi()), &rtmsg, (i.first())->tablename().c_str()); queued_peers.push_back(&(i.second())); } } if (queued_peers.empty() == false) { add_to_queue(RTQUEUE_OP_DELETE, rtmsg, queued_peers); wakeup_downstream(queued_peers); } return 0; } template int FanoutTable::route_dump(InternalMessage &rtmsg, BGPRouteTable *caller, const PeerHandler *dump_peer) { XLOG_ASSERT(caller == this->_parent); XLOG_ASSERT(rtmsg.route()->nexthop_resolved()); log("route_dump, net: " + rtmsg.route()->net().str() + " dump peer: " + dump_peer->peername()); BGPRouteTable *dump_child = 0; typename NextTableMap::iterator i; for (i = _next_tables.begin(); i != _next_tables.end(); i++) { if (i.second().peer_handler() == dump_peer) { dump_child = i.first(); break; } } XLOG_ASSERT(i != _next_tables.end()); int result; result = dump_child->route_dump(rtmsg, (BGPRouteTable*)this, dump_peer); if (result == ADD_USED || result == ADD_UNUSED || result == ADD_FILTERED) return 0; return result; } template int FanoutTable::push(BGPRouteTable *caller) { debug_msg("Push\n"); log("received push"); XLOG_ASSERT(caller == this->_parent); list *> queued_peers; typename NextTableMap::iterator i; for (i = _next_tables.begin(); i != _next_tables.end(); i++) { // a push needs to go to all peers because an add may cause a // delete (or vice versa) of a route that originated from a // different peer. queued_peers.push_back(&(i.second())); } if (queued_peers.empty() == false) { // if the origin peer we send to add_push_to_queue is NULL, the // push will go to all peers add_push_to_queue(queued_peers, NULL); wakeup_downstream(queued_peers); } return 0; } template void FanoutTable::wakeup_downstream(list *>& queued_peers) { //send a wakeup downstream if they're currently waiting typename list *>::iterator i; for (i = queued_peers.begin(); i != queued_peers.end(); i++) { if ((*i)->is_ready()) { (*i)->wakeup_sent(); (*i)->route_table()->wakeup(); } } } template const SubnetRoute* FanoutTable::lookup_route(const IPNet &net, uint32_t& genid, FPAListRef& pa_list) const { return this->_parent->lookup_route(net, genid, pa_list); } template string FanoutTable::str() const { string s = "FanoutTable" + this->tablename(); return s; } template void FanoutTable::add_dump_table(DumpTable *dump_table) { _dump_tables.insert(dump_table); } template void FanoutTable::remove_dump_table(DumpTable *dump_table) { debug_msg("remove: %s\n", dump_table->str().c_str()); typename set *>::iterator i; i = _dump_tables.find(dump_table); XLOG_ASSERT(i != _dump_tables.end()); _dump_tables.erase(i); } template void FanoutTable::peer_table_info(list*>& peer_list) { typename NextTableMap::iterator i; for (i = _next_tables.begin(); i != _next_tables.end(); i++) { if (i.second().peer_handler() != NULL) peer_list.push_back(&(i.second())); } } template int FanoutTable::dump_entire_table(BGPRouteTable *child_to_dump_to, Safi safi, string ribname) { XLOG_ASSERT(child_to_dump_to->type() != DUMP_TABLE); typename NextTableMap::iterator i; PeerTableInfo *peer_info = NULL; list *> peer_list; for (i = _next_tables.begin(); i != _next_tables.end(); i++) { if (i.second().peer_handler() != NULL) peer_list.push_back(&(i.second())); if (i.first() == child_to_dump_to) peer_info = &(i.second()); } // add the aggregation table / handler at the end of the list if (_aggr_peerinfo != NULL) peer_list.push_back(_aggr_peerinfo); XLOG_ASSERT(peer_info != NULL); const PeerHandler *peer_handler = peer_info->peer_handler(); string tablename = string(ribname + "DumpTable" + peer_handler->peername()); DumpTable* dump_table = new DumpTable(tablename, peer_handler, peer_list, (BGPRouteTable*)this, safi); dump_table->set_next_table(child_to_dump_to); child_to_dump_to->set_parent(dump_table); replace_next_table(child_to_dump_to, dump_table); //find the new peer_info (we just deleted the old one) peer_info = NULL; for (i = _next_tables.begin(); i != _next_tables.end(); i++) { if (i.first() == dump_table) peer_info = &(i.second()); } XLOG_ASSERT(peer_info != NULL); add_dump_table(dump_table); dump_table->initiate_background_dump(); return 0; } /* mechanisms to implement flow control in the output plumbing */ template void FanoutTable::add_to_queue(RouteQueueOp operation, InternalMessage &rtmsg, const list*>& queued_peers) { debug_msg("FanoutTable::add_to_queue, op=%d, net=%s\n", operation, rtmsg.net().str().c_str()); RouteQueueEntry *queue_entry; rtmsg.attributes()->lock(); queue_entry = new RouteQueueEntry(rtmsg.route(), rtmsg.attributes(), operation); queue_entry->set_origin_peer(rtmsg.origin_peer()); queue_entry->set_genid(rtmsg.genid()); _output_queue.push_back(queue_entry); set_queue_positions(queued_peers); if (rtmsg.push()) queue_entry->set_push(true); // we are the end stop for this rtmsg so unref the route if needed if (rtmsg.copied()) rtmsg.inactivate(); } template void FanoutTable::add_replace_to_queue(InternalMessage &old_rtmsg, InternalMessage &new_rtmsg, const list*>& queued_peers) { debug_msg("FanoutTable::add_replace_to_queue\n"); // replace entails two queue entries, but they're always paired up // in the order OLD then NEW RouteQueueEntry *queue_entry; old_rtmsg.attributes()->lock(); queue_entry = new RouteQueueEntry(old_rtmsg.route(), old_rtmsg.attributes(), RTQUEUE_OP_REPLACE_OLD); queue_entry->set_origin_peer(old_rtmsg.origin_peer()); queue_entry->set_genid(old_rtmsg.genid()); _output_queue.push_back(queue_entry); // set queue positions now, before we add the second queue entry set_queue_positions(queued_peers); new_rtmsg.attributes()->lock(); queue_entry = new RouteQueueEntry(new_rtmsg.route(), new_rtmsg.attributes(), RTQUEUE_OP_REPLACE_NEW); queue_entry->set_origin_peer(new_rtmsg.origin_peer()); queue_entry->set_genid(new_rtmsg.genid()); _output_queue.push_back(queue_entry); if (new_rtmsg.push()) { if (new_rtmsg.origin_peer() == old_rtmsg.origin_peer()) //add_push_to_queue(queued_peers, new_rtmsg.origin_peer()); queue_entry->set_push(true); else // if the origin peer we send to add_push_to_queue, the push will // go to all peers add_push_to_queue(queued_peers, NULL); } } template void FanoutTable::add_push_to_queue(const list*>& queued_peers, const PeerHandler *origin_peer) { debug_msg("FanoutTable::add_push_to_queue\n"); RouteQueueEntry *queue_entry; queue_entry = new RouteQueueEntry(RTQUEUE_OP_PUSH, origin_peer); _output_queue.push_back(queue_entry); set_queue_positions(queued_peers); } template void FanoutTable::set_queue_positions(const list*>& queued_peers) { typename list*>::const_iterator i; for (i = queued_peers.begin(); i != queued_peers.end(); i++) { if ((*i)->has_queued_data() == false) { /* set the queue position to the current last element */ (*i)->set_queue_position( --(_output_queue.end()) ); (*i)->set_has_queued_data(true); } } } template bool FanoutTable::get_next_message(BGPRouteTable *next_table) { debug_msg("next table: %s\n", next_table->tablename().c_str()); print_queue(); typename NextTableMap::iterator i; i = _next_tables.find(next_table); XLOG_ASSERT(i != _next_tables.end()); PeerTableInfo *peer_info = &(i.second()); peer_info->received_get(); if (peer_info->has_queued_data() == false) { debug_msg("no data queued\n"); peer_info->set_is_ready(); log("get next message (non available): " + peer_info->peer_handler()->peername()); return false; } log("get next message: " + peer_info->peer_handler()->peername()); typename list*>::iterator queue_ptr; queue_ptr = peer_info->queue_position(); bool discard_possible = false; switch ((*queue_ptr)->op()) { case RTQUEUE_OP_ADD: { debug_msg("OP_ADD, net=%s\n", (*queue_ptr)->route()->net().str().c_str()); // Need to clone the PA list or we'll modify the same version // multiple times in different ways downstream. FPAListRef fpa_list = new FastPathAttributeList(*(*queue_ptr)->attributes()); assert(!fpa_list->is_locked()); InternalMessage rtmsg((*queue_ptr)->route(), fpa_list, (*queue_ptr)->origin_peer(), (*queue_ptr)->genid()); if ((*queue_ptr)->push()) rtmsg.set_push(); log("sending add_route: " + (*queue_ptr)->route()->net().str()); assert(!fpa_list->is_locked()); next_table->add_route(rtmsg, (BGPRouteTable*)this); break; } case RTQUEUE_OP_DELETE: { debug_msg("OP_DELETE\n"); // Need to clone the PA list or we'll modify the same version // multiple times in different ways downstream. FPAListRef fpa_list = new FastPathAttributeList(*(*queue_ptr)->attributes()); InternalMessage rtmsg((*queue_ptr)->route(), fpa_list, (*queue_ptr)->origin_peer(), (*queue_ptr)->genid()); if ((*queue_ptr)->push()) rtmsg.set_push(); log("sending delete_route: " + (*queue_ptr)->route()->net().str()); next_table->delete_route(rtmsg, (BGPRouteTable*)this); break; } case RTQUEUE_OP_REPLACE_OLD: { debug_msg("OP_REPLACE_OLD\n"); // Need to clone the PA list or we'll modify the same version // multiple times in different ways downstream. FPAListRef old_fpa_list = new FastPathAttributeList(*(*queue_ptr)->attributes()); InternalMessage old_rtmsg((*queue_ptr)->route(), old_fpa_list, (*queue_ptr)->origin_peer(), (*queue_ptr)->genid()); if (queue_ptr == _output_queue.begin()) discard_possible = true; queue_ptr++; XLOG_ASSERT(queue_ptr != _output_queue.end()); // Need to clone the PA list or we'll modify the same version // multiple times in different ways downstream. FPAListRef new_fpa_list = new FastPathAttributeList(*(*queue_ptr)->attributes()); InternalMessage new_rtmsg((*queue_ptr)->route(), new_fpa_list, (*queue_ptr)->origin_peer(), (*queue_ptr)->genid()); if ((*queue_ptr)->push()) new_rtmsg.set_push(); log("sending replace_route: " + (*queue_ptr)->route()->net().str()); next_table->replace_route(old_rtmsg, new_rtmsg, (BGPRouteTable*)this); break; } case RTQUEUE_OP_REPLACE_NEW: { debug_msg("OP_REPLACE_NEW\n"); // this can't happen because the replace queue entries are // always old, then new, and the RTQUEUE_OP_REPLACE_OLD steps // over both entries XLOG_FATAL("illegal route queue state"); break; } case RTQUEUE_OP_PUSH: { debug_msg("OP_PUSH\n"); log("sending push"); next_table->push(this); break; } } if (queue_ptr == _output_queue.begin()) discard_possible = true; queue_ptr++; /* skip past anything that came from the peer that called us */ while ((queue_ptr != _output_queue.end()) && ((*queue_ptr)->origin_peer() != NULL) && ((*queue_ptr)->origin_peer() == peer_info->peer_handler())) { debug_msg("queued_peer: %p\n", (*queue_ptr)->origin_peer()); debug_msg("our peer: %p\n", peer_info->peer_handler()); switch ((*queue_ptr)->op()) { case RTQUEUE_OP_ADD: debug_msg("skipping OP_ADD\n"); break; case RTQUEUE_OP_DELETE: debug_msg("skipping OP_DELETE\n"); break; case RTQUEUE_OP_REPLACE_OLD: debug_msg("skipping OP_REPLACE_OLD\n"); break; case RTQUEUE_OP_REPLACE_NEW: debug_msg("skipping OP_REPLACE_NEW\n"); break; case RTQUEUE_OP_PUSH: debug_msg("skipping OP_PUSH\n"); break; } if ((*queue_ptr)->op() == RTQUEUE_OP_REPLACE_OLD) queue_ptr++; // sanity check if(queue_ptr == _output_queue.end()) { // this shouldn't ever happen, as a REPLACE_NEW should // follow a REPLACE_OLD crash_dump(); XLOG_UNREACHABLE(); } queue_ptr++; } if (queue_ptr == _output_queue.end()) { debug_msg("no more data queued for this peer\n"); peer_info->set_has_queued_data(false); } if (peer_info->has_queued_data()) { peer_info->set_queue_position(queue_ptr); } /* now we have to deal with freeing up the head of the queue if no-one else needs it now*/ while(discard_possible) { typename NextTableMap::iterator nti; // iterating on a map isn't very efficient, but the map // shouldn't be all that large bool discard = true; for (nti = _next_tables.begin(); nti != _next_tables.end(); nti++) { if (nti.second().has_queued_data()) { // if someone still references the queue head, we can't // discard. if (nti.second().queue_position() == _output_queue.begin()) discard = false; } } if (discard) { // check if the item to delete is a replace, in which case // we need to delete both replace items bool delete_two = false; if (_output_queue.front()->op() == RTQUEUE_OP_REPLACE_OLD) delete_two = true; if (_output_queue.front()->op() != RTQUEUE_OP_PUSH) _output_queue.front()->attributes()->unlock(); delete _output_queue.front(); _output_queue.pop_front(); if (delete_two) { XLOG_ASSERT(_output_queue.front()->op() == RTQUEUE_OP_REPLACE_NEW); XLOG_ASSERT(!_output_queue.empty()); _output_queue.front()->attributes()->unlock(); delete _output_queue.front(); _output_queue.pop_front(); } if (_output_queue.empty()) discard_possible = false; } else { discard_possible = false; } } return peer_info->has_queued_data(); } template void FanoutTable::skip_entire_queue(BGPRouteTable *next_table) { typename NextTableMap::iterator i; i = _next_tables.find(next_table); XLOG_ASSERT(i != _next_tables.end()); PeerTableInfo *peer_info = &(i.second()); peer_info->peer_reset(); if (peer_info->has_queued_data() == false) return; typename list*>::iterator queue_ptr; queue_ptr = peer_info->queue_position(); bool more_queued_data = true; while (more_queued_data) { bool discard_possible = false; switch ((*queue_ptr)->op()) { case RTQUEUE_OP_ADD: case RTQUEUE_OP_DELETE: case RTQUEUE_OP_PUSH: break; case RTQUEUE_OP_REPLACE_OLD: if (queue_ptr == _output_queue.begin()) discard_possible = true; queue_ptr++; break; case RTQUEUE_OP_REPLACE_NEW: XLOG_FATAL("illegal route queue state"); break; } if (queue_ptr == _output_queue.begin()) discard_possible = true; queue_ptr++; // skip over stuff that's not for this peer while ((queue_ptr != _output_queue.end()) && ((*queue_ptr)->origin_peer() == peer_info->peer_handler())) { queue_ptr++; if (queue_ptr == _output_queue.end()) { break; } else { if ((*queue_ptr)->op() == RTQUEUE_OP_REPLACE_NEW) queue_ptr++; } } if (queue_ptr == _output_queue.end()) { more_queued_data = false; peer_info->set_has_queued_data(false); } else { peer_info->set_queue_position(queue_ptr); } /* now we have to deal with freeing up the head of the queue if no-one else needs it now*/ while (discard_possible) { typename NextTableMap::iterator nti; // iterating on a map isn't very efficient, but the map // shouldn't be all that large bool discard = true; for (nti = _next_tables.begin(); nti != _next_tables.end(); nti++) { if (nti.second().has_queued_data()) { // if someone still references the queue head, we can't // discard. if (nti.second().queue_position() == _output_queue.begin()) discard = false; } } if (discard) { // check if the item to delete is a replace, in which case // we need to delete both replace items bool delete_two = false; if (_output_queue.front()->op() == RTQUEUE_OP_REPLACE_OLD) delete_two = true; if (_output_queue.front()->op() != RTQUEUE_OP_PUSH) _output_queue.front()->attributes()->unlock(); delete _output_queue.front(); _output_queue.pop_front(); if (delete_two) { XLOG_ASSERT(_output_queue.front()->op() == RTQUEUE_OP_REPLACE_NEW); XLOG_ASSERT(!_output_queue.empty()); _output_queue.front()->attributes()->unlock(); delete _output_queue.front(); _output_queue.pop_front(); } if (_output_queue.empty()) discard_possible = false; } else { discard_possible = false; } } } return; } template void FanoutTable::peering_went_down(const PeerHandler *peer, uint32_t genid, BGPRouteTable *caller) { XLOG_ASSERT(this->_parent == caller); log("Peering went down: " + peer->peername()); typename NextTableMap::iterator i; for (i = _next_tables.begin(); i != _next_tables.end(); i++) { i.first()->peering_went_down(peer, genid, (BGPRouteTable*)this); } } template void FanoutTable::peering_down_complete(const PeerHandler *peer, uint32_t genid, BGPRouteTable *caller) { XLOG_ASSERT(this->_parent == caller); log("Peering down complete: " + peer->peername()); debug_msg("peer: %s genid: %u caller: %s\n", peer->peername().c_str(), XORP_UINT_CAST(genid), caller->tablename().c_str()); print_queue(); typename NextTableMap::iterator i; for (i = _next_tables.begin(); i != _next_tables.end(); ) { BGPRouteTable* next_table = i.first(); //move the iterator on now, as peering_down_complete may cause //a dump table to unplumb itself. i++; next_table->peering_down_complete(peer, genid, (BGPRouteTable*)this); } } template void FanoutTable::peering_came_up(const PeerHandler *peer, uint32_t genid, BGPRouteTable *caller) { XLOG_ASSERT(this->_parent == caller); log("Peering came up: " + peer->peername()); typename NextTableMap::iterator i; for (i = _next_tables.begin(); i != _next_tables.end(); i++) { i.first()->peering_came_up(peer, genid, (BGPRouteTable*)this); } } template void FanoutTable::print_queue() { #ifdef DEBUG_QUEUE debug_msg("Rate control queue:\n"); typename list *>::iterator i; int ctr = 0; for (i = _output_queue.begin(); i != _output_queue.end(); i++) { ctr++; debug_msg("%-5d %s\n", ctr, (*i)->str().c_str()); } #endif } template string FanoutTable::dump_state() const { string s; s = "=================================================================\n"; s += "FanoutTable\n"; s += "=================================================================\n"; s += "Rate control queue:\n"; typename list *>::const_iterator i; int ctr = 0; for (i = _output_queue.begin(); i != _output_queue.end(); i++) { ctr++; s += c_format("%-5d %s\n", ctr, (*i)->str().c_str()); s += c_format("Parent now: %p\n", (*i)->route()->parent_route()); s += c_format("Filters now: %p,%p,%p\n", (*i)->route()->policyfilter(0).get(), (*i)->route()->policyfilter(1).get(), (*i)->route()->policyfilter(2).get()); } s += CrashDumper::dump_state(); return s; } template class PeerTableInfo; template class PeerTableInfo; template class FanoutTable; template class FanoutTable; xorp/bgp/route_table_policy_im.hh0000664000076400007640000000446011421137511017322 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/bgp/route_table_policy_im.hh,v 1.10 2008/11/08 06:14:39 mjh Exp $ #ifndef __BGP_ROUTE_TABLE_POLICY_IM_HH__ #define __BGP_ROUTE_TABLE_POLICY_IM_HH__ #include "route_table_policy.hh" /** * @short Import policy tables also deal with propagating policy route dumps. * * Import tables will detect policy route dumps [NULL in dump_peer]. It will * then transport the dump to an add/delete/request based on the outcome of the * filter. */ template class PolicyTableImport : public PolicyTable { public: /** * @param tablename name of the table. * @param safi the safe. * @param parent parent table. * @param pfs a reference to the global policy filters. */ PolicyTableImport(const string& tablename, const Safi& safi, BGPRouteTable* parent, PolicyFilters& pfs, const A& peer, const A& self); /** * If dump_peer is null, then it is a policy route dump and we need to deal * with it. * * @param rtmsg route being dumped. * @param caller table that called this method. * @param dump_peer peer we are dumping to. If policy dump, * it will be NULL. * @return ADD_FILTERED if route is rejected. XORP_OK otherwise. */ int route_dump(InternalMessage &rtmsg, BGPRouteTable *caller, const PeerHandler *dump_peer); }; #endif // __BGP_ROUTE_TABLE_POLICY_IM_HH__ xorp/bgp/route_queue.cc0000664000076400007640000000333111421137511015275 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "bgp_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "route_queue.hh" #include "peer_handler.hh" template string RouteQueueEntry::str() const { string s; switch(_op) { case RTQUEUE_OP_ADD: s = "RTQUEUE_OP_ADD"; break; case RTQUEUE_OP_DELETE: s = "RTQUEUE_OP_DELETE"; break; case RTQUEUE_OP_REPLACE_OLD: s = "RTQUEUE_OP_REPLACE_OLD"; break; case RTQUEUE_OP_REPLACE_NEW: s = "RTQUEUE_OP_REPLACE_NEW"; break; case RTQUEUE_OP_PUSH: s = "RTQUEUE_OP_PUSH"; break; } if (_route_ref.route() != NULL) s += "\n" + _route_ref.route()->str(); else s += "\n_route is NULL"; if (_origin_peer != NULL) s += "\nOrigin Peer: " + _origin_peer->peername(); else s += "\n_origin_peer is NULL"; return s; } template class RouteQueueEntry; template class RouteQueueEntry; xorp/bgp/path_attribute.hh0000664000076400007640000007464311540225521016003 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/bgp/path_attribute.hh,v 1.54 2008/12/11 21:05:59 mjh Exp $ #ifndef __BGP_PATH_ATTRIBUTE_HH__ #define __BGP_PATH_ATTRIBUTE_HH__ #include "libxorp/xorp.h" #include "libxorp/ipv4.hh" #include "libxorp/ipv6.hh" #include "libxorp/ipnet.hh" #include "libxorp/ref_ptr.hh" #include #include "exceptions.hh" // for CorruptMessage exception #include "aspath.hh" #include "parameter.hh" class BGPPeerData; class BGPPeer; class BGPMain; template class AttributeManager; /** * PathAttribute * * components of the path attribute. They have variable sizes * The actual layout on the wire is the following: * [ flags ] 1 byte * [ type ] 1 byte * [ len ][....] 1 or 2 bytes * [ data ] len bytes * * PathAttribute is the base class for a set of derived class which * represent the various attributes. * A PathAttribute object of a given type can be created explicitly, * using one of the constructors, and then adding components to it; * or it can be created by calling the create() method on a block * of data received from the wire. * * In addition to the parsed components (next hops, AS numbers and paths, * and various other attributes), the objects always contain the wire * representation of the object, a pointer to which is accessible with * the data() method, and whose size is size(). * Whenever the object is altered, the wire representation needs to be * recomputed. */ enum PathAttType { ORIGIN = 1, AS_PATH = 2, NEXT_HOP = 3, MED = 4, LOCAL_PREF = 5, ATOMIC_AGGREGATE = 6, AGGREGATOR = 7, COMMUNITY = 8, ORIGINATOR_ID = 9, CLUSTER_LIST = 10, MP_REACH_NLRI = 14, MP_UNREACH_NLRI = 15, AS4_PATH = 17, AS4_AGGREGATOR = 18, UNKNOWN_TESTING1 = 19, // reserved for testing only - should be one // more than the highest known attribute. UNKNOWN_TESTING2 = 20 // reserved for testing only - should be two // more than the highest known attribute. }; // MAX_ATTRIBUTE must contain the largest attribute number from the enum above #define MAX_ATTRIBUTE 20 class PathAttribute : public NONCOPYABLE { public: enum Flags { Optional = 0x80, Transitive = 0x40, Partial = 0x20, Extended = 0x10, ValidFlags = 0xf0, NoFlags = 0 }; /** * main routine to create a PathAttribute from incoming data. * Takes a chunk of memory of size l, returns an object of the * appropriate type and actual_length is the number of bytes used * from the packet. * Throws an exception on error. */ static PathAttribute *create(const uint8_t* d, uint16_t max_len, size_t& actual_length, const BGPPeerData* peerdata, uint32_t ip_version) throw(CorruptMessage); /** * Make a copy of the current attribute. * The derived class should use new to generate a copy of * itself. The wire format representation will not be used by the * caller. */ virtual PathAttribute *clone() const = 0; /* * The destructor, invoked after the derived class' destructors, * frees the internal representation of the object. */ virtual ~PathAttribute() { } virtual bool encode(uint8_t* buf, size_t &length, const BGPPeerData* peerdata) const = 0; /** * @return the size of the header. */ size_t header_size() const { return extended() ? 4 : 3; } /** * @return the type of the attribute */ PathAttType type() const { return (PathAttType)_type; } /** * @return the flags for the attribute */ Flags flags() const { return (Flags)_flags; } /** * Set the partial flag */ void set_partial() { _flags |= Partial; } /** * comparison operators are used to sort attributes. * Right now the sort order is based on the type, * size() and payload representation. */ bool operator<(const PathAttribute& him) const; bool operator==(const PathAttribute& him) const; /** * compute the hash for this object. */ void add_hash(MD5_CTX *context) const { size_t length = 4096; uint8_t buf[4096]; // XXX should probably do something more efficient here if (!encode(buf, length, NULL)) { XLOG_WARNING("Insufficient space to encode PA list for MD5 hash\n"); } MD5_Update(context, buf, length); } virtual string str() const; void pretty_print(); bool optional() const { return _flags & Optional; } bool transitive() const { return _flags & Transitive; } bool partial() const { return _flags & Partial; } bool extended() const { return _flags & Extended; } bool well_known() const { return !optional(); } protected: /** * sorttype() is only used in sorting a path attribute list. * It is different from PathAttType because we want to sort the path * attribute list on NextHop for less expensive processing when the IGP * information for a nexthop changes. * So we give priority to NEXT_HOP and keep other values unchanged. */ int sorttype() const { return type() == NEXT_HOP ? -1 : type(); } /** * helper constructor used when creating an object from a derived class. */ PathAttribute(Flags f, PathAttType t) : _flags(f & ValidFlags), _type(t) {} /** * basic constructor from data, assumes that the block has at least the * required size. */ PathAttribute(const uint8_t *d) : _flags(d[0] & ValidFlags), _type(d[1]) {} /** * helper function to fill the header. Needs _flags and _type * properly initialized. Writes into data buffer, and returns * pointer to first byte of buffer after the header. */ uint8_t *set_header(uint8_t *data, size_t payload_size, size_t &wire_size) const; /** * fetch the length from the header. Assume the header is there. */ static size_t length(const uint8_t* d) { return (d[0] & Extended) ? ( (d[2]<<8) + d[3] ) : d[2] ; } /** * Total length including the header. Used to send the whole TLV * back when an error has been detected. */ static size_t total_tlv_length(const uint8_t* d) { return length(d) + ((d[0] & Extended) ? 4 : 3); } // helper function returning a pointer to the payload const uint8_t *payload(const uint8_t *d) { return d + ((d[0] & Extended) ? 4 : 3); } #if 0 // storage for information in the attribute. size_t _size; // this is only the size of the payload. uint8_t * _data; // wire representation #endif uint8_t _flags; uint8_t _type; private: PathAttribute(); // Not directly constructible. }; // Origin values enum OriginType { IGP = 0, EGP = 1, INCOMPLETE = 2 }; /** * OriginAttribute has a payload of size 1 containing the origin type. */ class OriginAttribute : public PathAttribute { public: OriginAttribute(OriginType t); OriginAttribute(const uint8_t* d) throw(CorruptMessage); PathAttribute *clone() const; string str() const; OriginType origin() const { return _origin; } bool encode(uint8_t* buf, size_t &wire_size, const BGPPeerData* peerdata) const; private: OriginType _origin; }; /** * ASPathAttribute contain an ASPath, whose structure is documented * in aspath.hh */ class ASPathAttribute : public PathAttribute { public: ~ASPathAttribute() { delete _as_path; } ASPathAttribute(const ASPath& p); ASPathAttribute(const uint8_t* d, bool use_4byte_asnums) throw(CorruptMessage); PathAttribute *clone() const; string str() const { return "AS Path Attribute " + as_path().str(); } ASPath &as_path() const { return (ASPath &)*_as_path; } AS4Path &as4_path() const { return (AS4Path &)*_as_path;} bool encode(uint8_t* buf, size_t &wire_size, const BGPPeerData* peerdata) const; private: ASPath *_as_path; }; /** * AS4PathAttribute contain an AS4Path, whose structure is documented * in aspath.hh. See comment there for usage. */ class AS4PathAttribute : public PathAttribute { public: ~AS4PathAttribute() { delete _as_path; } AS4PathAttribute(const AS4Path& p); AS4PathAttribute(const uint8_t* d) throw(CorruptMessage); PathAttribute *clone() const; string str() const { return "AS4 Path Attribute " + as_path().str(); } ASPath &as_path() const { return (ASPath &)*_as_path; } AS4Path &as4_path() const { return (AS4Path &)*_as_path;} bool encode(uint8_t* buf, size_t &wire_size, const BGPPeerData* peerdata) const; private: AS4Path *_as_path; }; /** * NextHopAttribute contains the IP address of the next hop. */ template class NextHopAttribute : public PathAttribute { public: NextHopAttribute(const A& n) throw(CorruptMessage); NextHopAttribute(const uint8_t* d) throw(CorruptMessage); PathAttribute *clone() const; /* Throw exception if there are problems...do nothing * otherwise. */ void verify() throw(CorruptMessage); string str() const { return "Next Hop Attribute " + _next_hop.str(); } A& nexthop() { return _next_hop; } // This method is for use in MPReachNLRIAttribute only. void set_nexthop(const A& n) { _next_hop = n; } bool encode(uint8_t* buf, size_t &wire_size, const BGPPeerData* peerdata) const; private: A _next_hop; }; typedef NextHopAttribute IPv4NextHopAttribute; typedef NextHopAttribute IPv6NextHopAttribute; /** * MEDAttribute is an optional non-transitive uint32 */ class MEDAttribute : public PathAttribute { public: MEDAttribute(const uint32_t med); MEDAttribute(const uint8_t* d) throw(CorruptMessage); PathAttribute *clone() const; string str() const; uint32_t med() const { return _med; } bool encode(uint8_t* buf, size_t &wire_size, const BGPPeerData* peerdata) const; private: uint32_t _med; // XXX stored in host format! }; /** * LocalPrefAttribute is a well-known uint32 */ class LocalPrefAttribute : public PathAttribute { public: LocalPrefAttribute(const uint32_t localpref); LocalPrefAttribute(const uint8_t* d) throw(CorruptMessage); PathAttribute *clone() const; string str() const; uint32_t localpref() const { return _localpref; } static uint32_t default_value() { // The default Local Preference value is 100 according to Halabi. // This should probably be a configuration option. return 100; } bool encode(uint8_t* buf, size_t &wire_size, const BGPPeerData* peerdata) const; private: uint32_t _localpref; }; class AtomicAggAttribute : public PathAttribute { public: AtomicAggAttribute(); AtomicAggAttribute(const uint8_t* d) throw(CorruptMessage); PathAttribute *clone() const; string str() const { return "Atomic Aggregate Attribute"; } bool encode(uint8_t* buf, size_t &wire_size, const BGPPeerData* peerdata) const; private: }; class AggregatorAttribute : public PathAttribute { public: AggregatorAttribute(const IPv4& speaker, const AsNum& as); AggregatorAttribute(const uint8_t* d, bool use_4byte_asnums) throw(CorruptMessage); PathAttribute *clone() const; string str() const; const IPv4& route_aggregator() const { return _speaker; } const AsNum& aggregator_as() const { return _as; } bool encode(uint8_t* buf, size_t &wire_size, const BGPPeerData* peerdata) const; private: IPv4 _speaker; AsNum _as; }; class AS4AggregatorAttribute : public PathAttribute { public: AS4AggregatorAttribute(const IPv4& speaker, const AsNum& as); AS4AggregatorAttribute(const uint8_t* d) throw(CorruptMessage); PathAttribute *clone() const; string str() const; const IPv4& route_aggregator() const { return _speaker; } const AsNum& aggregator_as() const { return _as; } bool encode(uint8_t* buf, size_t &wire_size, const BGPPeerData* peerdata) const; private: IPv4 _speaker; AsNum _as; }; class CommunityAttribute : public PathAttribute { public: static const uint32_t NO_EXPORT = 0xFFFFFF01; // RFC 1997 static const uint32_t NO_ADVERTISE = 0xFFFFFF02; // RFC 1997 static const uint32_t NO_EXPORT_SUBCONFED = 0xFFFFFF03; // RFC 1997 typedef set ::const_iterator const_iterator; CommunityAttribute(); CommunityAttribute(const uint8_t* d) throw(CorruptMessage); PathAttribute *clone() const; string str() const; const set & community_set() const { return _communities; } void add_community(uint32_t community); bool contains(uint32_t community) const; bool encode(uint8_t* buf, size_t &wire_size, const BGPPeerData* peerdata) const; private: set _communities; }; /** * OriginatorIDAttribute is an optional non-transitive uint32 */ class OriginatorIDAttribute : public PathAttribute { public: OriginatorIDAttribute(const IPv4 originator_id); OriginatorIDAttribute(const uint8_t* d) throw(CorruptMessage); PathAttribute *clone() const; string str() const; IPv4 originator_id() const { return _originator_id; } bool encode(uint8_t* buf, size_t &wire_size, const BGPPeerData* peerdata) const; private: IPv4 _originator_id; }; class ClusterListAttribute : public PathAttribute { public: typedef list ::const_iterator const_iterator; ClusterListAttribute(); ClusterListAttribute(const uint8_t* d) throw(CorruptMessage); PathAttribute *clone() const; string str() const; const list & cluster_list() const { return _cluster_list; } void prepend_cluster_id(IPv4 cluster_id); bool contains(IPv4 cluster_id) const; bool encode(uint8_t* buf, size_t &wire_size, const BGPPeerData* peerdata) const; private: list _cluster_list; }; template class MPReachNLRIAttribute : public PathAttribute { public: typedef typename list >::const_iterator const_iterator; /** * Specialise these constructors for each AFI. */ MPReachNLRIAttribute(Safi safi); MPReachNLRIAttribute(const uint8_t* d) throw(CorruptMessage); PathAttribute *clone() const; string str() const; const A& nexthop() const { return _nexthop; } void set_nexthop(const A& nexthop) { _nexthop = nexthop; } void add_nlri(const IPNet& nlri) {_nlri.push_back(nlri);} const list >& nlri_list() const { return _nlri;} // IPv6 specific const A& link_local_nexthop() const { return _link_local_next_hop; } void set_link_local_nexthop(const A& n) { _link_local_next_hop = n;} // SNPA - Don't deal. (ATM, FRAME RELAY, SMDS) Safi safi() const { return _safi; } bool encode(uint8_t* buf, size_t &wire_size, const BGPPeerData* peerdata) const; private: Afi _afi; // Address Family Identifier. Safi _safi; // Subsequent Address Family Identifier. A _nexthop; // Next Hop. // list _snpa; // Subnetwork point of attachment. list > _nlri; // Network level reachability information. A _link_local_next_hop; // Link local next hop IPv6 specific. }; template class MPUNReachNLRIAttribute : public PathAttribute { public: typedef typename list >::const_iterator const_iterator; /** * Specialise these constructors for each AFI. */ MPUNReachNLRIAttribute(Safi safi); MPUNReachNLRIAttribute(const uint8_t* d) throw(CorruptMessage); PathAttribute *clone() const; string str() const; void add_withdrawn(const IPNet& nlri) {_withdrawn.push_back(nlri);} const list >& wr_list() const { return _withdrawn;} Safi safi() const { return _safi; } bool encode(uint8_t* buf, size_t &wire_size, const BGPPeerData* peerdata) const; private: Afi _afi; // Address Family Identifier. Safi _safi; // Subsequent Address Family Identifier. list > _withdrawn; // Withdrawn routes. }; class UnknownAttribute : public PathAttribute { public: UnknownAttribute(const uint8_t* d) throw(CorruptMessage); UnknownAttribute(uint8_t *data, size_t size, uint8_t flags); PathAttribute *clone() const; string str() const; bool encode(uint8_t* buf, size_t &wire_size, const BGPPeerData* peerdata) const; private: // storage for information in the attribute. size_t _size; // this is only the size of the payload. uint8_t * _data; // wire representation }; /* it ought to be possible to typedef this, but I don't know how */ #define FPAListRef ref_ptr > #define FPAList4Ref ref_ptr > #define FPAList6Ref ref_ptr > template class FastPathAttributeList; /** * PathAttributeList is used to handle efficiently path attribute lists. * * An object in the class is initialized from explicit PathAttribute * objects passed in by reference. The initialization creates a copy * of the attribute, links it into a list, and for mandatory attributes * it also stores a pointer to the newly created attribute into a * class member (e.g. _aspath_att ...) for ease of use. */ template class PathAttributeList { public: typedef list::const_iterator const_iterator; typedef list::iterator iterator; PathAttributeList(); PathAttributeList(const PathAttributeList& palist); PathAttributeList(FPAListRef& fpa_list); virtual ~PathAttributeList(); // complete() is true when all the mandatory attributes are present // normally call this on a FastPathAttributeList //virtual bool complete() const; string str() const; /* operator< is used to store and search for PathAttributeLists in STL containers. In principle, it doesn't matter what the order is, so long as there is a strict monotonicity to the ordering */ /* In practice, the ordering is important - we want PathAttributesLists to be ordered first in order of NextHop, as this makes the RIB-In's task much easier when a nexthop changes */ bool operator< (const PathAttributeList &them) const; bool operator== (const PathAttributeList &them) const; const uint8_t* canonical_data() const {return _canonical_data;} size_t canonical_length() const {return _canonical_length;} void incr_refcount(uint32_t change) const { XLOG_ASSERT(0xffffffff - change > _refcount); _refcount += change; // printf("incr_refcount for %p: now %u\n", this, _refcount); } void decr_refcount(uint32_t change) const { XLOG_ASSERT(_refcount >= change); _refcount -= change; // printf("decr_refcount for %p: now %u\n", this, _refcount); if (_refcount == 0 && _managed_refcount == 0) { delete this; } } uint32_t references() const { return _refcount; } void incr_managed_refcount(uint32_t change) const { XLOG_ASSERT(0xffffffff - change > _managed_refcount); _managed_refcount += change; // printf("incr_managed_refcount for %p: now %u\n", this, _managed_refcount); } void decr_managed_refcount(uint32_t change) const { XLOG_ASSERT(_refcount >= change); _managed_refcount -= change; // printf("decr_managed_refcount for %p: now %u\n", this, _managed_refcount); if (_refcount == 0 && _managed_refcount == 0) { delete this; } } uint32_t managed_references() const { return _managed_refcount; } protected: // Canonical data is the path attribute list stored in BGP wire // format in the most canonical form we know. For example, AS // numbers are always 4-byte. Note that as we speak IPv6, there // should be no IPv6 NLRI path attributes in here. They're in the // prefix instead. When we send to a particular peer, we will // typically re-encode a path attribute for that peer, so this // should not be sent directly - it's only for internal storage. uint8_t* _canonical_data; uint16_t _canonical_length; private: // void assert_rehash() const; // const PathAttribute* find_attribute_by_type(PathAttType type) const; // ref count is the number of FastPathAttributeList objects // currently referencing this PathAttributeList. It's used to // avoid premature deletion, and to keep track if we've asked for // this to be deleted. mutable uint32_t _refcount; // managed refcount is the number of routes referencing this PA // list when this PA list is stored in the attribute manager. mutable uint32_t _managed_refcount; // uint8_t _hash[16]; // used for fast comparisons }; template class PAListRef { public: PAListRef(const PathAttributeList* _palist); PAListRef(const PAListRef& palistref); PAListRef() : _palist(0) {} ~PAListRef(); PAListRef& operator=(const PAListRef& palistref); PAListRef& operator=(FPAListRef& fpalistref); bool operator==(const PAListRef& palistref) const; bool operator<(const PAListRef& palistref) const; const PathAttributeList* operator->() const { return _palist; } const PathAttributeList* operator*() const { return _palist; } void register_with_attmgr(); void deregister_with_attmgr(); inline bool is_empty() const {return _palist == 0;} void release(); const PathAttributeList* attributes() const {return _palist;} void create_attribute_manager() { _att_mgr = new AttributeManager(); }; /** * DEBUGGING ONLY */ int number_of_managed_atts() const { return _att_mgr->number_of_managed_atts(); } private: // this ought not be a pointer, but the the two classes would // mutually self-reference, and you can't do that. static AttributeManager *_att_mgr; const PathAttributeList* _palist; }; /* FastPathAttributeList is an subclass of a PathAttributeList that is used to quickly access the path attributes. It takes a slave PathAttributeList if it is constructed from one, rather than using its own storage, because it is intended to be transient and the slave is intended to be more permanent, so we typically want the slave to persist when this is deleted */ template class FastPathAttributeList /*: public PathAttributeList*/ { public: FastPathAttributeList(PAListRef& palist); FastPathAttributeList(FastPathAttributeList& fpalist); FastPathAttributeList(const NextHopAttribute &nexthop, const ASPathAttribute &aspath, const OriginAttribute &origin); FastPathAttributeList(); virtual ~FastPathAttributeList(); /** * Load the raw path attribute data from an update message. This * data will not yet be in canonical form. Call canonicalize() to * put the data in canonical form. */ void load_raw_data(const uint8_t *data, size_t size, const BGPPeerData* peer, bool have_nlri, BGPMain *mainprocess, bool do_checks); /* see commemt on _locked variable */ void lock() const { XLOG_ASSERT(_locked == false); _locked = true; } void unlock() const { XLOG_ASSERT(_locked == true); _locked = false; } bool is_locked() const {return _locked;} // All known attributes need accessor methods here NextHopAttribute* nexthop_att(); ASPathAttribute* aspath_att(); AS4PathAttribute* as4path_att(); OriginAttribute* origin_att(); MEDAttribute* med_att(); LocalPrefAttribute* local_pref_att(); AtomicAggAttribute* atomic_aggregate_att(); AggregatorAttribute* aggregator_att(); CommunityAttribute* community_att(); OriginatorIDAttribute* originator_id(); ClusterListAttribute* cluster_list(); template MPReachNLRIAttribute *mpreach(Safi) ; template MPUNReachNLRIAttribute *mpunreach(Safi); // short cuts A& nexthop(); ASPath& aspath(); OriginType origin(); // complete() is true when all the mandatory attributes are present virtual bool complete() const { return ((_att_bytes[NEXT_HOP] || _att[NEXT_HOP]) && (_att_bytes[AS_PATH] || _att[AS_PATH]) && (_att_bytes[ORIGIN] || _att[ORIGIN])); } /** * Add this path attribute to the list after making a local copy. */ void add_path_attribute(const PathAttribute &att); /** * Add this path attribute to the list don't make a local copy. */ void add_path_attribute(PathAttribute *att); /** * return the relevant path attribute, given the PA type. */ PathAttribute* find_attribute_by_type(PathAttType type); /** * return the highest attribute type. */ int max_att() const {return _att.size();} /** * For unknown attributes: * 1) If transitive set the partial bit. * 2) If not transitive remove. */ void process_unknown_attributes(); void replace_nexthop(const A& nexthop); void replace_AS_path(const ASPath& as_path); void replace_origin(const OriginType& origin); void remove_attribute_by_type(PathAttType type); void remove_attribute_by_pointer(PathAttribute*); /** * Encode the PA List for transmission to the specified peer. * Note that as Some peers speak 4-byte AS numbers and some don't, * the encoding is peer-specific. * * @return true if the data was successfully encoded; false if * there wasn't enough space in the buffer for the data. * * @param buf is the buffer to encode into. * * @param wire_size is given the size of the buffer to encode * into, and returns the amount of data placed in the buffer. * * @param peer is the peer to encode this for. Some peers want * 4-byte AS numbers and some don't. * */ bool encode(uint8_t* buf, size_t &wire_size, const BGPPeerData* peerdata) const; string str() const; void canonicalize() const; const uint8_t* canonical_data() const {return _canonical_data;} size_t canonical_length() const {return _canonical_length;} bool canonicalized() const {return _canonicalized;} bool operator==(const FastPathAttributeList& him) const; bool is_empty() const { return _attribute_count == 0; } int attribute_count() const {return _attribute_count;} private: void quick_decode(const uint8_t *canonical_data, uint16_t canonical_length); void replace_attribute(PathAttribute *att); uint32_t att_order(uint32_t index) const { switch(index) { case 1: return (uint32_t)NEXT_HOP; case 2: return (uint32_t)ORIGIN; case 3: return (uint32_t)AS_PATH; default: return index; } } void count_attributes() { _attribute_count = 0; for (uint32_t i = 0; i < _att.size(); i++) { if (_att[i]) { _attribute_count++; continue; } if (i <= MAX_ATTRIBUTE && _att_bytes[i]) _attribute_count++; } } /** * We need to encode an attribute to send to a peer. However we only * have the canonically encoded byte stream data for it. Sometimes * that is fine, and we should just send that; sometimes we need to * decode and re-encode for this specific peer. */ bool encode_and_decode_attribute(const uint8_t* att_data, const size_t& att_len, uint8_t *buf, size_t& wire_size , const BGPPeerData* peerdata) const; const PAListRef _slave_pa_list; // Break out of the path attribute list by type for quick access. // bytes and lengths include the header so we preserve the flags. // _att is only filled out on demand. These can be arrays because // we don't fill them out unless they're attributes we know about, // so the maximum size is bounded. const uint8_t* _att_bytes[MAX_ATTRIBUTE+1]; size_t _att_lengths[MAX_ATTRIBUTE+1]; // This is mutable because the nominal content of the class // doesn't change, but we cache the result we calculate in const // methods. // We use a vector here because we want to be able to // resize it if we see an unknown path attribute that is greater // than MAX_ATTRIBUTE. mutable vector _att; /** * A count of the number of attributes in this attribute list. */ int _attribute_count; /** * We pass around ref_ptrs to FastPathAttributeLists for * efficiency reasons, and this greatly simplifies memory * management. However, it makes it easy to store one temporarily * in one place and the while it's stored, modify it via a * reference somewhere else. This would be a bug, but it's hard to * catch. We add the ability to lock the contents to detect such * a condition and aid debugging. */ mutable bool _locked; // Canonical data is the path attribute list stored in BGP wire // format in the most canonical form we know. For example, AS // numbers are always 4-byte. Note that as we speak IPv6, there // should be no IPv6 NLRI path attributes in here. They're in the // prefix instead. When we send to a particular peer, we will // typically re-encode a path attribute for that peer, so this // should not be sent directly - it's only for internal storage. mutable uint8_t* _canonical_data; mutable uint16_t _canonical_length; mutable bool _canonicalized; // is the canonical data up-to-date? }; template template MPReachNLRIAttribute* FastPathAttributeList::mpreach(Safi safi) { debug_msg("%p\n", this); PathAttribute* att = find_attribute_by_type(MP_REACH_NLRI); MPReachNLRIAttribute* mp_att = dynamic_cast*>(att); if (mp_att && safi == mp_att->safi()) return mp_att; return 0; } template template MPUNReachNLRIAttribute* FastPathAttributeList::mpunreach(Safi safi) { debug_msg("%p\n", this); PathAttribute* att = find_attribute_by_type(MP_UNREACH_NLRI); MPUNReachNLRIAttribute* mp_att = dynamic_cast*>(att); if (mp_att && safi == mp_att->safi()) return mp_att; return 0; } #endif // __BGP_PATH_ATTRIBUTE_HH__ xorp/bgp/route_table_reader.hh0000664000076400007640000000433211540224220016572 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/bgp/route_table_reader.hh,v 1.15 2008/10/02 21:56:21 bms Exp $ #ifndef __BGP_ROUTE_TABLE_READER_HH__ #define __BGP_ROUTE_TABLE_READER_HH__ #include "libxorp/xorp.h" #include "libxorp/ipnet.hh" #include "bgp_trie.hh" template class RibInTable; template class ReaderIxTuple { public: typedef typename BgpTrie::iterator trie_iterator; ReaderIxTuple(const IPv4& peer_id, trie_iterator route_iter, const RibInTable* _ribin); const A& masked_addr() const {return _net.masked_addr();} uint32_t prefix_len() const {return _net.prefix_len();} IPNet net() const {return _net;} bool is_consistent() const; const IPv4& peer_id() const {return _peer_id;} const RibInTable* ribin() const {return _ribin;} bool operator<(const ReaderIxTuple& them) const; trie_iterator& route_iterator() { return _route_iter; } private: IPNet _net; IPv4 _peer_id; trie_iterator _route_iter; const RibInTable* _ribin; }; template class RouteTableReader { public: typedef typename BgpTrie::iterator trie_iterator; RouteTableReader(const list *>& ribins, const IPNet& prefix); bool get_next(const SubnetRoute*& route, IPv4& peer_id); private: set *> _peer_readers; }; #endif // __BGP_ROUTE_TABLE_READER_HH__ xorp/bgp/peer_list.hh0000664000076400007640000000502311540224220014727 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/bgp/peer_list.hh,v 1.14 2008/10/02 21:56:17 bms Exp $ #ifndef __BGP_PEER_LIST_HH__ #define __BGP_PEER_LIST_HH__ #include "peer.hh" class BGPPeer; class BGPPeerList { public: BGPPeerList(); ~BGPPeerList(); /** * Stop all the peerings. * * @param restart if true will bounce the peerings, if false the * peerings will be taken down and kept down. * */ void all_stop(bool restart = false); /** * Are the peers idle? Used to poll the peers when BGP is being * gracefully taken down. * * @return true while peers are still active. */ bool not_all_idle(); /** * Add this peer to the peer list. */ void add_peer(BGPPeer *p); /** * Remove the peer from the peer list and delete it. */ void remove_peer(BGPPeer *p); /** * Detach this peer from the peer list (DO NOT DELETE IT). */ void detach_peer(BGPPeer *p); /** * Get the list of attached peers. */ list& get_list() {return _peers;} /** * Debugging entry point that prints all the peers. */ void dump_list(); /** * Aquire a token that can be used to scan through the peers. */ bool get_peer_list_start(uint32_t& token); /** * Using the token get information about the peers. */ bool get_peer_list_next(const uint32_t& token, string& local_ip, uint32_t& local_port, string& peer_ip, uint32_t& peer_port); protected: private: list _peers; map ::iterator> _readers; uint32_t _next_token; }; #endif // __BGP_PEER_LIST_HH__ xorp/bgp/timer_const.hh0000664000076400007640000000334211421137511015275 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/bgp/timer_const.hh,v 1.10 2008/10/02 21:56:23 bms Exp $ #ifndef __BGP_TIMER_CONST_HH__ #define __BGP_TIMER_CONST_HH__ #include #include "libxorp/debug.h" class BGPTimerConst { public: BGPTimerConst() { _hold_duration = 90; _retry_duration = 120; _keepalive_duration = 30; } uint32_t get_hold_duration() { return _hold_duration; } void set_hold_duration(uint32_t d) { _hold_duration = d; } uint32_t get_retry_duration() { return _retry_duration; } void set_retry_duration(uint32_t d) { _retry_duration = d; } uint32_t get_keepalive_duration() { return _keepalive_duration; } void set_keepalive_duration(uint32_t d) { _keepalive_duration = d; } private: // In seconds. uint32_t _hold_duration; uint32_t _retry_duration; uint32_t _keepalive_duration; }; #endif // __BGP_TIMER_CONST_HH__ xorp/bgp/iptuple.cc0000664000076400007640000001600011540224217014414 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #include "bgp_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/exceptions.hh" #include "libxorp/callback.hh" #include "socket.hh" #include "iptuple.hh" Iptuple::Iptuple() {} Iptuple::Iptuple(const char* local_dev, const char *local_addr, uint16_t local_port, const char *peer_addr, uint16_t peer_port) throw(UnresolvableHost,AddressFamilyMismatch) : _local_dev(local_dev), _local_addr(local_addr), _peer_addr(peer_addr), _local_port(local_port), _peer_port(peer_port) { _local_sock_len = sizeof(_local_sock); _bind_sock_len = sizeof(_bind_sock); _peer_sock_len = sizeof(_peer_sock); debug_msg("Iptuple creation, local_dev: %s local_addr: %s local_port: %i peer_addr: %s peer_port: %i\n", local_dev, local_addr, (int)(local_port), peer_addr, (int)(peer_port)); fill_address(local_addr, local_port, _local_sock, _local_sock_len, _local_address); string bind_address; // We don't care about this address fill_address(local_addr, 0, _bind_sock, _bind_sock_len, bind_address); fill_address(peer_addr, peer_port, _peer_sock, _peer_sock_len, _peer_address); // Test for an address family mismatch if (_local_sock.ss_family != _peer_sock.ss_family) xorp_throw(AddressFamilyMismatch, c_format("mismatch %s (%u) %s (%u)", local_addr, _local_sock.ss_family, peer_addr, _peer_sock.ss_family)); _local_address_ipvx = IPvX(_local_address.c_str()); _peer_address_ipvx = IPvX(_peer_address.c_str()); } Iptuple::Iptuple(const Iptuple& rhs) { copy(rhs); } Iptuple& Iptuple::operator=(const Iptuple& rhs) { if (&rhs == this) return *this; copy(rhs); return *this; } void Iptuple::copy(const Iptuple& rhs) { _local_addr = rhs._local_addr; _local_dev = rhs._local_dev; _peer_addr = rhs._peer_addr; memcpy(&_local_sock, &rhs._local_sock, sizeof(_local_sock)); _local_sock_len = rhs._local_sock_len; memcpy(&_bind_sock, &rhs._bind_sock, sizeof(_bind_sock)); _bind_sock_len = rhs._bind_sock_len; memcpy(&_peer_sock, &rhs._peer_sock, sizeof(_peer_sock)); _peer_sock_len = rhs._peer_sock_len; _local_address = rhs._local_address; _local_address_ipvx = rhs._local_address_ipvx; _peer_address = rhs._peer_address; _peer_address_ipvx = rhs._peer_address_ipvx; _local_port = rhs._local_port; _peer_port = rhs._peer_port; } bool Iptuple::operator==(const Iptuple& rhs) const { /* ** Note: we don't include the peer port in the comparison. ** This peer port is not useful when deciding if an iptuple is ** unique as it is not necessarily fixed. */ return _local_address == rhs._local_address && _local_port == rhs._local_port && _peer_address == rhs._peer_address; } /* ** Take an IP[46] address and return a completed sockaddr as well as ** the numeric representation of the address. */ void Iptuple::fill_address(const char *addr, uint16_t local_port, struct sockaddr_storage& ss, size_t& len, string& numeric_addr) throw(UnresolvableHost) { string port = c_format("%d", local_port); const char *servname; if (local_port == 0) { servname = 0; } else { servname = port.c_str(); } int error; struct addrinfo hints, *res0; // Need to provide a hint because we are providing a numeric port number. memset(&hints, 0, sizeof(hints)); #ifdef HOST_OS_WINDOWS hints.ai_family = PF_INET; #else hints.ai_family = PF_UNSPEC; #endif hints.ai_socktype = SOCK_STREAM; if ((error = getaddrinfo(addr, servname, &hints, &res0))) { const char *error_string = gai_strerror(error); xorp_throw(UnresolvableHost, c_format("getaddrinfo(%s,%s,...) failed: %s", addr, port.c_str(), error_string)); } debug_msg("addrlen %u len %u\n", XORP_UINT_CAST(res0->ai_addrlen), XORP_UINT_CAST(len)); XLOG_ASSERT(res0->ai_addrlen <= len); memcpy(&ss, res0->ai_addr, res0->ai_addrlen); debug_msg("family %d\n", ss.ss_family); len = res0->ai_addrlen; // Recover the numeric form of the address char hostname[1024]; if ((error = getnameinfo(res0->ai_addr, res0->ai_addrlen, hostname, sizeof(hostname), 0, 0, NI_NUMERICHOST))) { const char *error_string = gai_strerror(error); xorp_throw(UnresolvableHost, c_format("getnameinfo() failed: %s", error_string)); } numeric_addr = hostname; debug_msg("Original %s Hostname: %s\n", addr, numeric_addr.c_str()); freeaddrinfo(res0); } const struct sockaddr * Iptuple::get_local_socket(size_t& len) const { len = _local_sock_len; return reinterpret_cast(&_local_sock); } string Iptuple::get_local_addr() const { return _local_address; } bool Iptuple::get_local_addr(IPv4& addr) const { if (!_local_address_ipvx.is_ipv4()) return false; addr = _local_address_ipvx.get_ipv4(); return true; } bool Iptuple::get_local_addr(IPv6& addr) const { if (!_local_address_ipvx.is_ipv6()) return false; addr = _local_address_ipvx.get_ipv6(); return true; } uint16_t Iptuple::get_local_port() const { return _local_port; } const struct sockaddr * Iptuple::get_bind_socket(size_t& len) const { len = _bind_sock_len; return reinterpret_cast(&_bind_sock); } const struct sockaddr * Iptuple::get_peer_socket(size_t& len) const { len = _peer_sock_len; return reinterpret_cast(&_peer_sock); } string Iptuple::get_peer_addr() const { return _peer_address; } bool Iptuple::get_peer_addr(IPv4& addr) const { if (!_peer_address_ipvx.is_ipv4()) return false; addr = _peer_address_ipvx.get_ipv4(); return true; } bool Iptuple::get_peer_addr(IPv6& addr) const { if (!_peer_address_ipvx.is_ipv6()) return false; addr = _peer_address_ipvx.get_ipv6(); return true; } uint16_t Iptuple::get_peer_port() const { return _peer_port; } string Iptuple::str() const { return c_format("{%s%s(%d) %s(%d)}", _local_dev.c_str(), _local_addr.c_str(), _local_port, _peer_addr.c_str(), _peer_port); } xorp/bgp/route_table_decision.cc0000664000076400007640000006603411633743677017153 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME // All routes hold some internal state for example: is this route // resolvable? This internal state is required because the state of // a route may change. For example a route may go from being // resolvable to unresolvable. To maintain a consistent internal state // in the plumbing it is important when considering routes on other // branches to look at the cached state in the route itself, rather // than the actual state. Never change the state of a route that is // not on the current peering. #include "bgp_module.h" #include "libxorp/xlog.h" #include "dump_iterators.hh" #include "route_table_decision.hh" #ifdef PARANOID #define PARANOID_ASSERT(x) assert(x) #else #define PARANOID_ASSERT(x) {} #endif template DecisionTable::DecisionTable(string table_name, Safi safi, NextHopResolver& next_hop_resolver) : BGPRouteTable("DecisionTable" + table_name, safi), _next_hop_resolver(next_hop_resolver) { } template DecisionTable::~DecisionTable() { //clean up - this should really be done by removing the parent //when the peering goes down, but we don't do that currently - we //reuse the existing branch because that makes deleting old routes //much easier. typename map*, PeerTableInfo* >::iterator i; for (i = _parents.begin(); i != _parents.end(); i++) { delete i->second; } } template int DecisionTable::add_parent(BGPRouteTable *new_parent, PeerHandler *peer_handler, uint32_t genid) { debug_msg("DecisionTable::add_parent: %p ID %s\n", new_parent, cstring(peer_handler->id())); if (_parents.find(new_parent)!=_parents.end()) { //the parent is already in the set return -1; } PeerTableInfo *pti = new PeerTableInfo(new_parent, peer_handler, genid); _parents[new_parent] = pti; XLOG_ASSERT(_sorted_parents.find(peer_handler->get_unique_id()) == _sorted_parents.end()); _sorted_parents[peer_handler->get_unique_id()] = pti; return 0; } template int DecisionTable::remove_parent(BGPRouteTable *ex_parent) { debug_msg("DecisionTable::remove_parent: %p\n", ex_parent); typename map*, PeerTableInfo* >::iterator i; i = _parents.find(ex_parent); PeerTableInfo *pti = i->second; const PeerHandler* peer = pti->peer_handler(); _parents.erase(i); _sorted_parents.erase(_sorted_parents.find(peer->get_unique_id())); delete pti; return 0; } template int DecisionTable::add_route(InternalMessage &rtmsg, BGPRouteTable *caller) { PARANOID_ASSERT(_parents.find(caller) != _parents.end()); debug_msg("DT:add_route %s\n", rtmsg.route()->str().c_str()); //if the nexthop isn't resolvable, don't even consider the route debug_msg("testing resolvability\n"); XLOG_ASSERT(rtmsg.route()->nexthop_resolved() == resolvable(rtmsg.nexthop())); if (!resolvable(rtmsg.nexthop())) { debug_msg("route not resolvable\n"); return ADD_UNUSED; } debug_msg("route resolvable\n"); //find the alternative routes, and the old winner if there was one. RouteData *old_winner = NULL, *old_winner_clone = NULL; list > alternatives; old_winner = find_alternative_routes(caller, rtmsg.net(), alternatives); debug_msg("there are %d alternatives\n", (int)alternatives.size()); //preserve old_winner because the original may be deleted by the //decision process if (old_winner != NULL) { old_winner_clone = new RouteData(*old_winner); } RouteData *new_winner = NULL; RouteData new_route(rtmsg.route(), rtmsg.attributes(), caller, rtmsg.origin_peer(), rtmsg.genid()); if (!alternatives.empty()) { //add the new route to the pool of possible winners. alternatives.push_back(new_route); new_winner = find_winner(alternatives); } else { //the new route wins by default new_winner = &new_route; } XLOG_ASSERT(new_winner != NULL); if (old_winner_clone != NULL) { if (old_winner_clone->route() == new_winner->route()) { //the winner didn't change. XLOG_ASSERT(old_winner_clone != NULL); delete old_winner_clone; return ADD_UNUSED; } //the winner did change, so send a delete for the old winner InternalMessage old_rt_msg(old_winner_clone->route(), old_winner_clone->attributes(), old_winner_clone->peer_handler(), old_winner_clone->genid()); this->_next_table->delete_route(old_rt_msg, (BGPRouteTable*)this); //the old winner is no longer the winner old_winner_clone->set_is_not_winner(); //clean up temporary state delete old_winner_clone; } //send an add for the new winner new_winner->route()->set_is_winner( igp_distance(new_winner->attributes()->nexthop())); int result; if (new_winner->route() != rtmsg.route()) { //we have a new winner, but it isn't the route that was just added. //this can happen due to MED wierdness. InternalMessage new_rt_msg(new_winner->route(), new_winner->attributes(), new_winner->peer_handler(), new_winner->genid()); if (rtmsg.push()) new_rt_msg.set_push(); result = this->_next_table->add_route(new_rt_msg, (BGPRouteTable*)this); } else { result = this->_next_table->add_route(rtmsg, (BGPRouteTable*)this); } if (result == ADD_UNUSED) { //if it got as far as the decision table, we declare it //used, even if it gets filtered downstream. result = ADD_USED; } return result; } template int DecisionTable::replace_route(InternalMessage &old_rtmsg, InternalMessage &new_rtmsg, BGPRouteTable *caller) { PARANOID_ASSERT(_parents.find(caller)!=_parents.end()); XLOG_ASSERT(old_rtmsg.net()==new_rtmsg.net()); debug_msg("DT:replace_route.\nOld route: %s\nNew Route: %s\n", old_rtmsg.route()->str().c_str(), new_rtmsg.route()->str().c_str()); list > alternatives; RouteData *old_winner, *old_winner_clone = NULL; old_winner = find_alternative_routes(caller, old_rtmsg.net(),alternatives); if (old_winner) { //preserve this, because the original old_winner's data will //be clobbered by route_wins. old_winner_clone = new RouteData(*old_winner); } else if (old_rtmsg.route()->is_winner()) { //the route being deleted was the old winner old_winner_clone = new RouteData(old_rtmsg.route(), old_rtmsg.attributes(), caller, old_rtmsg.origin_peer(), old_rtmsg.genid()); } if (old_winner_clone == NULL) { //no route was the old winner, presumably because no route was //resolvable. return add_route(new_rtmsg, caller); } RouteData *new_winner = NULL; RouteData new_route(new_rtmsg.route(), new_rtmsg.attributes(), caller, new_rtmsg.origin_peer(), new_rtmsg.genid()); if (!alternatives.empty()) { //add the new route to the pool of possible winners. alternatives.push_back(new_route); new_winner = find_winner(alternatives); } else if (resolvable(new_rtmsg.nexthop())) { //the new route wins by default if it's resolvable. new_winner = &new_route; } //if there's no new winner, just delete the old route. if (new_winner == NULL) { delete_route(old_rtmsg, caller); if (new_rtmsg.push() && !old_rtmsg.push()) this->_next_table->push(this); delete old_winner_clone; return ADD_UNUSED; } if (new_winner->route() == old_winner_clone->route()) { //No change. //I don't think this can happen. delete old_winner_clone; return ADD_USED; } //create the deletion part of the message InternalMessage *old_rtmsg_p, *new_rtmsg_p; if (old_winner_clone->route() == old_rtmsg.route()) { old_rtmsg.clear_push(); // FIXME: hack to enable policy route pushing. // old_rtmsg.route()->set_is_not_winner(); old_rtmsg_p = &old_rtmsg; } else { old_rtmsg_p = new InternalMessage(old_winner_clone->route(), old_winner_clone->attributes(), old_winner_clone->peer_handler(), old_winner_clone->genid()); old_winner_clone->set_is_not_winner(); } //create the addition part of the message new_winner->route()->set_is_winner( igp_distance(new_winner->attributes()->nexthop())); int result; if (new_winner->route() == new_rtmsg.route()) { new_rtmsg_p = &new_rtmsg; } else { new_rtmsg_p = new InternalMessage(new_winner->route(), new_winner->attributes(), new_winner->peer_handler(), new_winner->genid()); if (new_rtmsg.push()) const_cast*>(new_rtmsg_p)->set_push(); } //send the replace message if (old_rtmsg_p->origin_peer() == new_rtmsg_p->origin_peer()) { //we can send this as a replace without confusing the fanout table result = this->_next_table->replace_route(*old_rtmsg_p, *new_rtmsg_p, (BGPRouteTable*)this); } else { //we need to send this as a delete and an add, because the //fanout table will send them to different sets of peers. this->_next_table->delete_route(*old_rtmsg_p, (BGPRouteTable*)this); result = this->_next_table->add_route(*new_rtmsg_p, (BGPRouteTable*)this); } //clean up temporary state delete old_winner_clone; if (old_rtmsg_p != &old_rtmsg) delete old_rtmsg_p; if (new_rtmsg_p != &new_rtmsg) delete new_rtmsg_p; return result; } template int DecisionTable::delete_route(InternalMessage &rtmsg, BGPRouteTable *caller) { debug_msg("delete route: %s\n", rtmsg.route()->str().c_str()); PARANOID_ASSERT(_parents.find(caller) != _parents.end()); XLOG_ASSERT(this->_next_table != NULL); //find the alternative routes, and the old winner if there was one. RouteData *old_winner = NULL, *old_winner_clone = NULL; list > alternatives; old_winner = find_alternative_routes(caller, rtmsg.net(), alternatives); //preserve old_winner because the original may be deleted by the //decision process if (old_winner != NULL) { old_winner_clone = new RouteData(*old_winner); debug_msg("The Old winner was %s\n", old_winner->route()->str().c_str()); } else if (rtmsg.route()->is_winner()) { //the route being deleted was the old winner old_winner_clone = new RouteData(rtmsg.route(), rtmsg.attributes(), caller, rtmsg.origin_peer(), rtmsg.genid()); } RouteData *new_winner = NULL; if (!alternatives.empty()) { new_winner = find_winner(alternatives); } if (old_winner_clone == NULL && new_winner == NULL) { //there are no resolvable routes, and there weren't before either/ //nothing to do. return -1; } bool delayed_push = rtmsg.push(); if (old_winner_clone != NULL) { if (new_winner != NULL && old_winner_clone->route() == new_winner->route()) { //the winner didn't change. XLOG_ASSERT(old_winner_clone != NULL); delete old_winner_clone; return -1; } //the winner did change, or there's no new winner, so send a //delete for the old winner if (old_winner_clone->route() != rtmsg.route()) { InternalMessage old_rt_msg(old_winner_clone->route(), old_winner_clone->attributes(), old_winner_clone->peer_handler(), old_winner_clone->genid()); if (rtmsg.push() && new_winner == NULL) old_rt_msg.set_push(); this->_next_table->delete_route(old_rt_msg, (BGPRouteTable*)this); old_winner_clone->set_is_not_winner(); } else { if (new_winner != NULL) rtmsg.clear_push(); this->_next_table->delete_route(rtmsg, (BGPRouteTable*)this); rtmsg.route()->set_is_not_winner(); } //clean up temporary state delete old_winner_clone; } if (new_winner != NULL) { //send an add for the new winner new_winner->route()->set_is_winner( igp_distance(new_winner->attributes()->nexthop())); InternalMessage new_rt_msg(new_winner->route(), new_winner->attributes(), new_winner->peer_handler(), new_winner->genid()); // if (rtmsg.push()) // new_rt_msg.set_push(); this->_next_table->add_route(new_rt_msg, (BGPRouteTable*)this); if (delayed_push) this->_next_table->push((BGPRouteTable*)this); } return 0; } template int DecisionTable::push(BGPRouteTable *caller) { PARANOID_ASSERT(_parents.find(caller) != _parents.end()); UNUSED(caller); if (this->_next_table != NULL) return this->_next_table->push((BGPRouteTable*)this); return 0; } /** * This version of lookup_route finds the previous winner if there was * one, else it finds the best alternative. Note that the previous * winner might not actually be the best current winner, but in this * context we need to be consistent - if it won before, then it still * wins until a delete_route or replace_route arrives to update our * idea of the winner */ template const SubnetRoute* DecisionTable::lookup_route(const BGPRouteTable* ignore_parent, const IPNet &net, const PeerHandler*& best_routes_peer, BGPRouteTable*& best_routes_parent) const { list > alternatives; RouteData* winner = find_alternative_routes(ignore_parent, net, alternatives); if (winner == NULL && !alternatives.empty()) { winner = find_winner(alternatives); } if (winner != NULL) { best_routes_peer = winner->peer_handler(); best_routes_parent = winner->parent_table(); return winner->route(); } return NULL; } template const SubnetRoute* DecisionTable::lookup_route(const IPNet &net, uint32_t& genid, FPAListRef& pa_list) const { list > alternatives; RouteData* winner = find_alternative_routes(NULL, net, alternatives); if (winner == NULL) return NULL; else { genid = winner->genid(); pa_list = winner->attributes(); return winner->route(); } XLOG_UNREACHABLE(); return NULL; } template RouteData* DecisionTable::find_alternative_routes( const BGPRouteTable *caller, const IPNet& net, list >& alternatives) const { RouteData* previous_winner = NULL; typename map*, PeerTableInfo* >::const_iterator i; const SubnetRoute* found_route; for (i = _parents.begin(); i != _parents.end(); i++) { //We don't need to lookup the route in the parent that the new //route came from - if this route replaced an earlier route //from the same parent we'd see it as a replace, not an add if (i->first != caller) { uint32_t found_genid; FPAListRef found_attributes; found_route = i->first->lookup_route(net, found_genid, found_attributes); if (found_route != NULL) { PeerTableInfo *pti = i->second; alternatives.push_back(RouteData(found_route, found_attributes, pti->route_table(), pti->peer_handler(), found_genid)); if (found_route->is_winner()) { XLOG_ASSERT(previous_winner == NULL); previous_winner = &(alternatives.back()); } } } } return previous_winner; } template uint32_t DecisionTable::local_pref(const FPAListRef& pa_list) const { /* * Local Pref should be present on all routes. If the route comes * from EBGP, the incoming FilterTable should have added it. If * the route comes from IBGP, it should have been present on the * incoming route. */ const LocalPrefAttribute* localpref_att = pa_list->local_pref_att(); if (localpref_att) { return localpref_att->localpref(); } return 0; } template uint32_t DecisionTable::med(const FPAListRef& pa_list) const { const MEDAttribute* med_attribute = pa_list->med_att(); if (med_attribute) { return med_attribute->med(); } return 0; } /* ** Is this next hop resolvable. Ask the RIB via the next hop resolver. ** NOTE: If unsynchronised operation is required then this is the ** place to always return true. */ template bool DecisionTable::resolvable(const A nexthop) const { bool resolvable; uint32_t metric; if(!_next_hop_resolver.lookup(nexthop, resolvable, metric)) XLOG_FATAL("This next hop must be known %s", nexthop.str().c_str()); return resolvable; } /* ** Get the igp distance from the RIB via the next hop resolver. */ template uint32_t DecisionTable::igp_distance(const A nexthop) const { bool resolvable; uint32_t metric; if(!_next_hop_resolver.lookup(nexthop, resolvable, metric)) XLOG_FATAL("This next hop must be known %s", nexthop.str().c_str()); if (resolvable) debug_msg("Decision: IGP distance for %s is %u\n", nexthop.str().c_str(), XORP_UINT_CAST(metric)); else debug_msg("Decision: IGP distance for %s is unknown\n", nexthop.str().c_str()); return metric; } /* ** The main decision process. ** ** return true if test_route is better than current_route. ** resolved is set to true if the winning route resolves. */ template RouteData* DecisionTable::find_winner(list >& alternatives) const { debug_msg("find_winner: there are %d alternatices\n", (int)alternatives.size()); typename list >::iterator i; /* The spec seems pretty odd. In our architecture, it seems reasonable to do phase 2 before phase 1, because if a route isn't resolvable we don't want to consider it */ /* ** Phase 2: Route Selection. */ /* ** Check if routes resolve. */ for (i=alternatives.begin(); i!=alternatives.end();) { if (!i->route()->nexthop_resolved()) { i = alternatives.erase(i); } else { i++; } } /* If there are no resolvable alternatives, no-one wins */ if (alternatives.empty()) { debug_msg("no resolvable routes\n"); return NULL; } if (alternatives.size()==1) { debug_msg("one one resolvable route\n"); return &(alternatives.front()); } /* By this point, all remaining routes are resolvable */ /* ** Phase 1: Calculation of degree of preference. */ int test_pref = local_pref(alternatives.front().attributes()); i = alternatives.begin(); i++; while(i!=alternatives.end()) { int lp = local_pref(i->attributes()); XLOG_ASSERT(lp >= 0); //prefer higher preference if (lp < test_pref) { i = alternatives.erase(i); } else if (lp > test_pref) { test_pref = lp; alternatives.erase(alternatives.begin(), i); i++; } else { i++; } } if (alternatives.size()==1) { debug_msg("decided on localpref\n"); return &(alternatives.front()); } /* ** If we got here the preferences for both routes are the same. */ /* ** Here we are crappy tie breaking. */ debug_msg("tie breaking\n"); debug_msg("AS path test\n"); /* ** Shortest AS path length. */ int test_aspath_length = alternatives.front().attributes()-> aspath().path_length(); i = alternatives.begin(); i++; while(i!=alternatives.end()) { int len = i->attributes()->aspath().path_length(); XLOG_ASSERT(len >= 0); //prefer shortest path if (len > test_aspath_length) { i = alternatives.erase(i); } else if (len < test_aspath_length) { test_aspath_length = len; alternatives.erase(alternatives.begin(), i); } else { i++; } } if (alternatives.size()==1) { return &(alternatives.front()); } debug_msg("origin test\n"); /* ** Lowest origin value. */ int test_origin = alternatives.front().attributes()->origin(); i = alternatives.begin(); i++; while(i!=alternatives.end()) { int origin = i->attributes()->origin(); //prefer lower origin if (origin > test_origin) { i = alternatives.erase(i); } else if (origin < test_origin) { test_origin = origin; alternatives.erase(alternatives.begin(), i); i++; } else { i++; } } if (alternatives.size()==1) { return &(alternatives.front()); } debug_msg("MED test\n"); /* ** Compare meds if both routes came from the same neighbour AS. */ typename list >::iterator j; for (i=alternatives.begin(); i!=alternatives.end();) { ASPath aspath1 = i->attributes()->aspath(); AsNum asnum1 = (0 == aspath1.path_length()) ? AsNum(AsNum::AS_INVALID) : aspath1.first_asnum(); int med1 = med(i->attributes()); bool del_i = false; for (j=alternatives.begin(); j!=alternatives.end();) { bool del_j = false; if (i != j) { ASPath aspath2 = j->attributes()->aspath(); AsNum asnum2 = (0 == aspath2.path_length()) ? AsNum(AsNum::AS_INVALID) : aspath2.first_asnum(); int med2 = med(j->attributes()); if (asnum1 == asnum2) { if (med1 > med2) { i = alternatives.erase(i); del_i = true; break; } else if (med1 < med2) { j = alternatives.erase(j); del_j = true; } } } //move to the next j if we didn't already do it using erase if (del_j == false) { j++; } } //move to the next i if we didn't already do it using erase if (del_i == false) { i++; } } if (alternatives.size()==1) { return &(alternatives.front()); } debug_msg("EBGP vs IBGP test\n"); /* ** Prefer routes from external peers over internal peers. */ bool test_ibgp = alternatives.front().peer_handler()->ibgp(); i = alternatives.begin(); i++; while(i!=alternatives.end()) { bool ibgp = i->peer_handler()->ibgp(); if ((!test_ibgp) && ibgp) { //test route is external, alternative is internal i = alternatives.erase(i); } else if (test_ibgp && !ibgp) { //test route is internal, alternative is external alternatives.erase(alternatives.begin(), i); test_ibgp = ibgp; i++; } else { i++; } } if (alternatives.size()==1) { return &(alternatives.front()); } debug_msg("IGP distance test\n"); /* ** Compare IGP distances. */ int test_igp_distance = igp_distance(alternatives.front().attributes()->nexthop()); i = alternatives.begin(); i++; while(i!=alternatives.end()) { int igp_dist = igp_distance(i->attributes()->nexthop()); //prefer lower IGP distance if (test_igp_distance < igp_dist) { i = alternatives.erase(i); } else if (test_igp_distance > igp_dist) { alternatives.erase(alternatives.begin(), i); test_igp_distance = igp_dist; i++; } else { i++; } } if (alternatives.size()==1) { return &(alternatives.front()); } debug_msg("BGP ID test\n"); /* ** Choose the route from the neighbour with the lowest BGP ID. */ IPv4 test_id = alternatives.front().peer_handler()->id(); i = alternatives.begin(); i++; while(i!=alternatives.end()) { IPv4 id = i->peer_handler()->id(); //prefer lower ID distance if (test_id < id) { i = alternatives.erase(i); } else if (test_id > id) { alternatives.erase(alternatives.begin(), i); test_id = id; i++; } else { i++; } } if (alternatives.size()==1) { return &(alternatives.front()); } debug_msg("neighbour addr test\n"); /* ** Choose the route from the neighbour with the lowest neighbour ** address. */ int test_address = alternatives.front().peer_handler() ->neighbour_address(); i = alternatives.begin(); i++; while(i!=alternatives.end()) { int address = i->peer_handler()->neighbour_address(); //prefer lower address if (test_address < address) { i = alternatives.erase(i); } else if (test_address > address) { alternatives.erase(alternatives.begin(), i); test_address = address; i++; } else { i++; } } if (alternatives.empty()) { XLOG_UNREACHABLE(); } //We can get here with more than one route if we compare two //identically rated routes. Just choose one. return &(alternatives.front()); } template bool DecisionTable::dump_next_route(DumpIterator& dump_iter) { const PeerHandler* peer = dump_iter.current_peer(); typename map* >::const_iterator i; i = _sorted_parents.find(peer->get_unique_id()); XLOG_ASSERT(i != _sorted_parents.end()); return i->second->route_table()->dump_next_route(dump_iter); } template int DecisionTable::route_dump(InternalMessage &rtmsg, BGPRouteTable */*caller*/, const PeerHandler *peer) { XLOG_ASSERT(this->_next_table != NULL); return this->_next_table->route_dump(rtmsg, (BGPRouteTable*)this, peer); } template void DecisionTable::igp_nexthop_changed(const A& bgp_nexthop) { typename map* >::const_iterator i; for (i = _sorted_parents.begin(); i != _sorted_parents.end(); i++) { i->second->route_table()->igp_nexthop_changed(bgp_nexthop); } } template void DecisionTable::peering_went_down(const PeerHandler *peer, uint32_t genid, BGPRouteTable *caller) { XLOG_ASSERT(this->_next_table != NULL); typename map *, PeerTableInfo*>::const_iterator i; i = _parents.find(caller); XLOG_ASSERT(i !=_parents.end()); XLOG_ASSERT(i->second->peer_handler() == peer); XLOG_ASSERT(i->second->genid() == genid); this->_next_table->peering_went_down(peer, genid, this); } template void DecisionTable::peering_down_complete(const PeerHandler *peer, uint32_t genid, BGPRouteTable *caller) { XLOG_ASSERT(this->_next_table != NULL); typename map *, PeerTableInfo*>::const_iterator i; i = _parents.find(caller); XLOG_ASSERT(i !=_parents.end()); XLOG_ASSERT(i->second->peer_handler() == peer); this->_next_table->peering_down_complete(peer, genid, this); } template void DecisionTable::peering_came_up(const PeerHandler *peer, uint32_t genid, BGPRouteTable *caller) { XLOG_ASSERT(this->_next_table != NULL); typename map *, PeerTableInfo*>::iterator i; i = _parents.find(caller); XLOG_ASSERT(i !=_parents.end()); XLOG_ASSERT(i->second->peer_handler() == peer); i->second->set_genid(genid); this->_next_table->peering_came_up(peer, genid, this); } template string DecisionTable::str() const { string s = "DecisionTable" + this->tablename(); return s; } template class DecisionTable; template class DecisionTable; xorp/bgp/crash_dump.cc0000664000076400007640000000650411540224217015067 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2006-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "bgp_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/c_format.hh" #include "libxorp/timer.hh" #include "crash_dump.hh" #ifndef HOST_OS_WINDOWS #include #endif #define CRASHLOG_SIZE 100 //make it real CrashDumpManager CrashDumper::_mgr; CrashDumpManager::CrashDumpManager() { } void CrashDumpManager::register_dumper(CrashDumper *dumper) { _dumpers.push_back(dumper); } void CrashDumpManager::unregister_dumper(CrashDumper *dumper) { list ::iterator i; for (i = _dumpers.begin(); i != _dumpers.end(); ++i) { if (*i == dumper) { _dumpers.erase(i); return; } } XLOG_UNREACHABLE(); } void CrashDumpManager::crash_dump() { FILE *dumpfile; #ifndef HOST_OS_WINDOWS struct passwd *pwd = getpwuid(getuid()); string filename = "/tmp/bgp_dump."; filename += pwd->pw_name; #else char *tmppath = (char *)malloc(256); GetTempPathA(256, tmppath); string filename = string(tmppath) + "bgp_dump"; free(tmppath); #endif dumpfile = fopen(filename.c_str(), "w"); if (dumpfile == NULL) { XLOG_WARNING("Failed to open dump file: %s", filename.c_str()); return; } list ::iterator i; for (i = _dumpers.begin(); i != _dumpers.end(); i++) { string s = (*i)->dump_state(); fwrite(s.c_str(), 1, s.size(), dumpfile); } fclose(dumpfile); } CrashDumper::CrashDumper() { _mgr.register_dumper(this); _logfirst = 0; _loglast = 0; } CrashDumper::~CrashDumper() { _mgr.unregister_dumper(this); } void CrashDumper::crash_dump() const { _mgr.crash_dump(); } void CrashDumper::log(const string& msg) { if (_logfirst == _loglast) { // first time we're called, allocate the storage _log.resize(CRASHLOG_SIZE); _logtimes.resize(CRASHLOG_SIZE); } if ( ((_loglast + 1) % CRASHLOG_SIZE) == _logfirst) { // need to overwrite an old entry _loglast = _logfirst; _logfirst = ((_logfirst + 1) % CRASHLOG_SIZE); } else { // there's still space _loglast = ((_loglast + 1) % CRASHLOG_SIZE); } _log[_loglast] = msg; TimeVal tv; TimerList::system_gettimeofday(&tv); _logtimes[_loglast] = tv; } string CrashDumper::dump_state() const { string s; if (_logfirst != _loglast) { s = "Audit Log:\n"; int i = _logfirst; while (1) { s += _logtimes[i].str() + " " + _log[i] + "\n"; if (i == _loglast) break; i = ((i + 1) % CRASHLOG_SIZE); } } return s; } xorp/bgp/route_table_nhlookup.cc0000664000076400007640000003670411540225521017172 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #include "bgp_module.h" #include "route_table_nhlookup.hh" template MessageQueueEntry::MessageQueueEntry(InternalMessage* add_msg, InternalMessage* delete_msg) : _added_route_ref(add_msg->route()), _deleted_route_ref(delete_msg ? delete_msg->route() : NULL) { copy_in(add_msg, delete_msg); } template MessageQueueEntry::MessageQueueEntry(const MessageQueueEntry& them) : _added_route_ref(them.add_msg()->route()), _deleted_route_ref(them.delete_msg() ? them.delete_msg()->route() : NULL) { copy_in(them.add_msg(), them.delete_msg()); } template void MessageQueueEntry::copy_in(InternalMessage* add_msg, InternalMessage* delete_msg) { /* Note: this all depends on _added_route_ref and _deleted_route_ref, whose pupose is to maintain the reference count on the SubnetRoutes from the add and delete message, so that the original won't go away before we've finished using it */ XLOG_ASSERT(add_msg != NULL); debug_msg("MessageQueueEntry: add_msg: %p\n%s\n", add_msg, add_msg->str().c_str()); // Copy the add_msg. We can't assume it will still be around. _add_msg = new InternalMessage(add_msg->route(), add_msg->attributes(), add_msg->origin_peer(), add_msg->genid()); // changed must be false - we don't store new routes here, so the // plumbing has to ensure that there's a cache upstream. XLOG_ASSERT(add_msg->copied() == false); if (delete_msg == NULL) { _delete_msg = NULL; } else { _delete_msg = new InternalMessage(delete_msg->route(), delete_msg->attributes(), delete_msg->origin_peer(), delete_msg->genid()); } } template MessageQueueEntry::~MessageQueueEntry() { delete _add_msg; if (_delete_msg != NULL) { delete _delete_msg; } } template string MessageQueueEntry::str() const { string s; s += c_format("add_msg: %p\n", _add_msg); s += c_format("delete_msg: %p\n", _delete_msg); return s; } template NhLookupTable::NhLookupTable(string tablename, Safi safi, NextHopResolver* next_hop_resolver, BGPRouteTable *parent) : BGPRouteTable(tablename, safi) { this->_parent = parent; _next_hop_resolver = next_hop_resolver; } template int NhLookupTable::add_route(InternalMessage &rtmsg, BGPRouteTable *caller) { debug_msg("\n %s\n caller: %s\n rtmsg: %p route: %p\n%s\n", this->tablename().c_str(), caller ? caller->tablename().c_str() : "NULL", &rtmsg, rtmsg.route(), rtmsg.str().c_str()); XLOG_ASSERT(caller == this->_parent); XLOG_ASSERT(0 == lookup_in_queue(rtmsg.nexthop(), rtmsg.net())); debug_msg("register_nexthop %s %s\n", cstring(rtmsg.nexthop()), cstring(rtmsg.net())); if (_next_hop_resolver->register_nexthop(rtmsg.nexthop(), rtmsg.net(), this)) { bool resolvable; uint32_t metric; _next_hop_resolver->lookup(rtmsg.nexthop(), resolvable, metric); rtmsg.route()->set_nexthop_resolved(resolvable); return this->_next_table->add_route(rtmsg, this); } add_to_queue(rtmsg.nexthop(), rtmsg.net(), &rtmsg, NULL); // we don't know if it will ultimately be used, so err on the safe // side return ADD_USED; } template int NhLookupTable::replace_route(InternalMessage &old_rtmsg, InternalMessage &new_rtmsg, BGPRouteTable *caller) { debug_msg("\n %s\n" "caller: %s\n" "old rtmsg: %p new rtmsg: %p " "old route: %p" "new route: %p" "old: %s\n new: %s\n", this->tablename().c_str(), caller == 0 ? "NULL" : caller->tablename().c_str(), &old_rtmsg, &new_rtmsg, old_rtmsg.route(), new_rtmsg.route(), old_rtmsg.str().c_str(), new_rtmsg.str().c_str()); XLOG_ASSERT(caller == this->_parent); debug_msg("NhLookupTable::replace_route\n"); IPNet net = new_rtmsg.net(); // Are we still waiting for the old_rtmsg to resolve? bool old_msg_is_queued; MessageQueueEntry* mqe = lookup_in_queue(old_rtmsg.nexthop(), net); old_msg_is_queued = (mqe != NULL); // The correct behaviour is to deregister interest in this // nexthop. If the nexthop for the old route is the same as in the // new route then we may have done a horrible thing. We have told // the resolver that we are no longer interested in this nexthop, // if this is the last reference we will remove all state related // to this nexthop. The next thing we do is register interest in // this nexthop which will not resolve, thus requiring us to // queue the request. Not deregistering saves the extra queing. // // If the nexthops of the old and new route are different we need // to keep the old nexthop resolution around so that lookups can // be satisfied. // // _next_hop_resolver->deregister_nexthop(old_rtmsg.route()->nexthop(), // old_rtmsg.net(), this); bool new_msg_needs_queuing; debug_msg("register_nexthop %s %s\n", cstring(new_rtmsg.nexthop()), cstring(new_rtmsg.net())); if (_next_hop_resolver->register_nexthop(new_rtmsg.nexthop(), new_rtmsg.net(), this)) { new_msg_needs_queuing = false; bool resolvable = false; uint32_t metric; _next_hop_resolver->lookup(new_rtmsg.nexthop(), resolvable, metric); new_rtmsg.route()->set_nexthop_resolved(resolvable); } else { new_msg_needs_queuing = true; } debug_msg("need queuing %s\n", bool_c_str(new_msg_needs_queuing)); InternalMessage* real_old_msg = &old_rtmsg; SubnetRoute* preserve_route = NULL; bool propagate_as_add = false; if (old_msg_is_queued) { // there was an entry for this net in our queue awaiting // resolution of it's nexthop if (mqe->type() == MessageQueueEntry::REPLACE) { // preserve the old delete message and route preserve_route = new SubnetRoute(*(mqe->deleted_route())); InternalMessage* preserve_msg = new InternalMessage(preserve_route, mqe->delete_msg()->attributes(), mqe->delete_msg()->origin_peer(), mqe->delete_msg()->genid()); #if 0 // re-enable me! if (mqe->delete_msg()->changed()) preserve_msg->set_changed(); #endif if (mqe->delete_msg()->copied()) preserve_msg->set_copied(); real_old_msg = preserve_msg; } else if (mqe->type() == MessageQueueEntry::ADD) { // there was an ADD queued. No-one downstream heard this // add, so our REPLACE has now become an ADD propagate_as_add = true; } // we can now remove the old queue entry, because it's no longer // needed remove_from_queue(mqe->add_msg()->nexthop(), net); } bool deregister = true; int retval; if (new_msg_needs_queuing) { if (propagate_as_add) { add_to_queue(new_rtmsg.nexthop(), net, &new_rtmsg, NULL); } else { add_to_queue(new_rtmsg.nexthop(), net, &new_rtmsg, real_old_msg); deregister = false; } if (real_old_msg != &old_rtmsg) { delete real_old_msg; preserve_route->unref(); } retval = ADD_USED; } else { bool success; if (propagate_as_add) { success = this->_next_table->add_route(new_rtmsg, this); } else { success = this->_next_table->replace_route(*real_old_msg, new_rtmsg, this); } if (real_old_msg != &old_rtmsg) { delete real_old_msg; preserve_route->unref(); } retval = success; } if (deregister) { debug_msg("deregister_nexthop %s %s\n", cstring(old_rtmsg.nexthop()), cstring(old_rtmsg.net())); _next_hop_resolver->deregister_nexthop(old_rtmsg.nexthop(), old_rtmsg.net(), this); } else { debug_msg("Deferring deregistration\n"); } return retval; } template int NhLookupTable::delete_route(InternalMessage &rtmsg, BGPRouteTable *caller) { debug_msg("\n %s\n caller: %s\n rtmsg: %p route: %p\n%s\n", this->tablename().c_str(), caller ? caller->tablename().c_str() : "NULL", &rtmsg, rtmsg.route(), rtmsg.str().c_str()); XLOG_ASSERT(caller == this->_parent); IPNet net = rtmsg.net(); // Are we still waiting for the old_rtmsg to resolve? bool msg_is_queued; MessageQueueEntry* mqe = lookup_in_queue(rtmsg.nexthop(), net); msg_is_queued = 0 != mqe; debug_msg("deregister_nexthop %s %s\n", cstring(rtmsg.nexthop()), cstring(rtmsg.net())); _next_hop_resolver->deregister_nexthop(rtmsg.nexthop(), rtmsg.net(), this); InternalMessage* real_msg = &rtmsg; if (msg_is_queued == true) { // there was an entry for this net in our queue awaiting // resolution of it's nexthop bool dont_send_delete = true; switch (mqe->type()) { case MessageQueueEntry::REPLACE: { // preserve the old delete message InternalMessage* preserve_msg = new InternalMessage(mqe->delete_msg()->route(), mqe->delete_msg()->attributes(), mqe->delete_msg()->origin_peer(), mqe->delete_msg()->genid()); #if 0 if (mqe->delete_msg()->changed()) preserve_msg->set_changed(); #endif if (mqe->delete_msg()->copied()) preserve_msg->set_copied(); real_msg = preserve_msg; dont_send_delete = false; break; } case MessageQueueEntry::ADD: dont_send_delete = true; break; } if (dont_send_delete) { // we can now remove the old queue entry, because it's no longer // needed remove_from_queue(mqe->add_msg()->nexthop(), net); // there was an ADD in the queue - we just dequeued it, and // don't need to propagate the delete further return 0; } } bool success = this->_next_table->delete_route(*real_msg, this); if (real_msg != &rtmsg) { delete real_msg; // we can now remove the old queue entry, because it's no longer // needed remove_from_queue(mqe->add_msg()->nexthop(), net); } return success; } template int NhLookupTable::push(BGPRouteTable *caller) { XLOG_ASSERT(caller == this->_parent); // Always propagate a push - we'll add new pushes each time a // nexthop resolves. return this->_next_table->push(this); } template const SubnetRoute * NhLookupTable::lookup_route(const IPNet &net, uint32_t& genid, FPAListRef& pa_list) const { debug_msg("net: %s\n", cstring(net)); // Are we still waiting for the old_rtmsg to resolve? const MessageQueueEntry* mqe = lookup_in_queue(A::ZERO(), net); if (0 == mqe) return this->_parent->lookup_route(net, genid, pa_list); switch (mqe->type()) { case MessageQueueEntry::ADD: debug_msg("ADD\n"); // although there is a route, we don't know the true nexthop // yet, so we act as though we don't know the answer return NULL; case MessageQueueEntry::REPLACE: debug_msg("********** JACKPOT **********\n"); debug_msg("REPLACE\n"); // although there is a route, we don't know the true nexthop // for it yet, so we act as though we only know the old answer. genid = mqe->delete_msg()->genid(); pa_list = mqe->delete_msg()->attributes(); return mqe->deleted_route(); } XLOG_UNREACHABLE(); } template void NhLookupTable::route_used(const SubnetRoute* route, bool in_use) { this->_parent->route_used(route, in_use); } template void NhLookupTable::RIB_lookup_done(const A& nexthop, const set >& nets, bool lookup_succeeded) { typename set >::const_iterator net_iter; for (net_iter = nets.begin(); net_iter != nets.end(); net_iter++) { const MessageQueueEntry* mqe = lookup_in_queue(nexthop, *net_iter); XLOG_ASSERT(0 != mqe); switch (mqe->type()) { case MessageQueueEntry::ADD: { mqe->add_msg()->route()->set_nexthop_resolved(lookup_succeeded); this->_next_table->add_route(*(mqe->add_msg()), this); break; } case MessageQueueEntry::REPLACE: mqe->add_msg()->route()->set_nexthop_resolved(lookup_succeeded); this->_next_table->replace_route(*(mqe->delete_msg()), *(mqe->add_msg()), this); // Perform the deferred deregistration. debug_msg("Performing deferred deregistration\n"); debug_msg("deregister_nexthop %s %s\n", cstring(mqe->deleted_attributes()->nexthop()), cstring(mqe->delete_msg()->net())); _next_hop_resolver-> deregister_nexthop(mqe->deleted_attributes()->nexthop(), mqe->delete_msg()->net(), this); break; } } for (net_iter = nets.begin(); net_iter != nets.end(); net_iter++) { remove_from_queue(nexthop, *net_iter); } // force a push because the original push may be long gone this->_next_table->push(this); } template void NhLookupTable::add_to_queue(const A& nexthop, const IPNet& net, InternalMessage* new_msg, InternalMessage* old_msg) { typename RefTrie >::iterator inserted; inserted = _queue_by_net.insert(net, MessageQueueEntry(new_msg, old_msg)); MessageQueueEntry* mqep = &(inserted.payload()); _queue_by_nexthop.insert(make_pair(nexthop, mqep)); } template MessageQueueEntry * NhLookupTable::lookup_in_queue(const A& nexthop, const IPNet& net) const { MessageQueueEntry* mqe = NULL; typename RefTrie >::iterator i; i = _queue_by_net.lookup_node(net); if (i != _queue_by_net.end()) { mqe = &(i.payload()); if (A::ZERO() != nexthop) XLOG_ASSERT(mqe->added_attributes()->nexthop() == nexthop); } return mqe; } template void NhLookupTable::remove_from_queue(const A& nexthop, const IPNet& net) { // Find the queue entry in the queue by net MessageQueueEntry* mqe; typename RefTrie >::iterator net_iter; net_iter = _queue_by_net.lookup_node(net); XLOG_ASSERT(net_iter != _queue_by_net.end()); mqe = &(net_iter.payload()); // Find the queue entry in the queue by nexthop typename multimap *>::iterator nh_iter = _queue_by_nexthop.find(nexthop); for (; nh_iter != _queue_by_nexthop.end(); nh_iter++) if (nh_iter->second->net() == net) break; XLOG_ASSERT(nh_iter != _queue_by_nexthop.end()); XLOG_ASSERT(nh_iter->first == nexthop); // Verify that both queues point at the same entry. XLOG_ASSERT(mqe == nh_iter->second); _queue_by_nexthop.erase(nh_iter); _queue_by_net.erase(net_iter); } template string NhLookupTable::str() const { string s = "NhLookupTable" + this->tablename(); return s; } template class NhLookupTable; template class NhLookupTable; xorp/bgp/aspath.cc0000664000076400007640000006673311540224217014234 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net //#define DEBUG_LOGGING //#define DEBUG_PRINT_FUNCTION_NAME #include "bgp_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_NETINET_IN_H #include #endif #ifdef HAVE_ARPA_INET_H #include #endif #include "aspath.hh" #include "path_attribute.hh" #include "packet.hh" extern void dump_bytes(const uint8_t *d, uint8_t l); /* *************** ASSegment ********************** */ /** * Convert the external representation into the internal one. * _type is d[0], _entries is d[1], entries follow. */ void ASSegment::decode(const uint8_t *d) throw(CorruptMessage) { size_t n = d[1]; clear(); _type = (ASPathSegType)d[0]; switch(_type) { case AS_NONE: case AS_SET: case AS_SEQUENCE: case AS_CONFED_SET: case AS_CONFED_SEQUENCE: break; default: xorp_throw(CorruptMessage, c_format("Bad AS Segment type: %u\n", _type), UPDATEMSGERR, MALASPATH); } d += 2; // skip header, d points to the raw data now. for (size_t i = 0; i < n; d += 2, i++) add_as(AsNum(d)); } /** * Convert from internal to external representation. */ const uint8_t * ASSegment::encode(size_t &len, uint8_t *data) const { debug_msg("AsSegment encode\n"); XLOG_ASSERT(_aslist.size() <= 255); size_t i = wire_size(); const_iterator as; if (data == 0) data = new uint8_t[i]; else XLOG_ASSERT(len >= i); len = i; data[0] = _type; data[1] = _aslist.size(); for (i = 2, as = _aslist.begin(); as != _aslist.end(); i += 2, ++as) { debug_msg("Encoding 16-bit As %d\n", as->as()); as->copy_out(data + i); } return data; } const AsNum& ASSegment::first_asnum() const { if (_type == AS_SET || _type == AS_CONFED_SET) { // This shouldn't be possible. The spec doesn't explicitly // prohibit passing someone an AS_PATH starting with an AS_SET, // but it doesn't make sense, and doesn't seem to be allowed by // the aggregation rules. XLOG_ERROR("Attempting to extract first AS Number " "from an AS Path that starts with an AS_SET " "not an AS_SEQUENCE\n"); } XLOG_ASSERT(!_aslist.empty()); return _aslist.front(); } /** * prints ASSegments as * * AS_SEQUENCE: [comma-separated-asn-list] * AS_SET: {comma-separated-asn-list} * AS_CONFED_SEQUENCE: (comma-separated-asn-list) * AS_CONFED_SET: */ string ASSegment::str() const { string s; string sep; switch(_type) { case AS_NONE: break; case AS_SET: sep = "{"; break; case AS_SEQUENCE: sep = "["; break; case AS_CONFED_SEQUENCE: sep = "("; break; case AS_CONFED_SET: sep = "<"; break; } const_iterator iter = _aslist.begin(); for (u_int i = 0; i<_aslist.size(); i++, ++iter) { s += sep ; s += iter->str(); sep = ", "; } switch(_type) { case AS_NONE: break; case AS_SET: sep = "}"; break; case AS_SEQUENCE: sep = "]"; break; case AS_CONFED_SEQUENCE: sep = ")"; break; case AS_CONFED_SET: sep = ">"; break; } s += sep; return s; } /** * prints ASSegments as * * AS_SEQUENCE: comma-separated-asn-list * AS_SET: {comma-separated-asn-list} * AS_CONFED_SEQUENCE: (comma-separated-asn-list) * AS_CONFED_SET: */ string ASSegment::short_str() const { string s; string sep; switch(_type) { case AS_NONE: break; case AS_SET: sep = "{"; break; case AS_SEQUENCE: sep = ""; break; case AS_CONFED_SEQUENCE: sep = "("; break; case AS_CONFED_SET: sep = "<"; break; } const_iterator iter = _aslist.begin(); for (u_int i = 0; i<_aslist.size(); i++, ++iter) { s += sep ; s += iter->short_str(); sep = " "; } switch(_type) { case AS_NONE: break; case AS_SET: sep = "}"; break; case AS_SEQUENCE: sep = ""; break; case AS_CONFED_SEQUENCE: sep = ")"; break; case AS_CONFED_SET: sep = ">"; break; } s += sep; return s; } /** * compares internal representations for equality. */ bool ASSegment::operator==(const ASSegment& him) const { if (_aslist.size() != him._aslist.size()) { return false; } const_iterator i; const_iterator b = him._aslist.begin(); for (i = _aslist.begin(); i!= _aslist.end(); i++, b++) { if (*i == *b) { continue; } return false; } return true; } /** * Compares internal representations for <. */ bool ASSegment::operator<(const ASSegment& him) const { int mysize = _aslist.size(); int hissize = him._aslist.size(); if (mysize < hissize) return true; if (mysize > hissize) return false; const_iterator i; const_iterator b = him._aslist.begin(); for (i = _aslist.begin(); i!= _aslist.end(); i++, b++) { if (*i == *b) { continue; } if (*i < *b) return true; return false; } return false; //must be equal } // XXX isn't this the same format as on the wire ??? size_t ASSegment::encode_for_mib(uint8_t* buf, size_t buf_size) const { //See RFC 1657, Page 15 for the encoding XLOG_ASSERT(buf_size >= (2 + _aslist.size() * 2)); uint8_t *p = buf; *p = (uint8_t)_type; p++; *p = (uint8_t)_aslist.size(); p++; const_iterator i; for (i = _aslist.begin(); i!= _aslist.end(); i++, p += 2) i->copy_out(p); return (2 + _aslist.size() * 2); } bool ASSegment::two_byte_compatible() const { const_iterator i; for (i = _aslist.begin(); i != _aslist.end(); i++) { if (i->extended()) return false; } return true; } /* *************** AS4Segment ***************** */ /** * Convert the external representation into the internal one. This is * used when decoding a AS4_PATH attribute. * * _type is d[0], _entries is d[1], entries follow. */ void AS4Segment::decode(const uint8_t *d) throw(CorruptMessage) { size_t n = d[1]; clear(); _type = (ASPathSegType)d[0]; switch(_type) { case AS_NONE: case AS_SET: case AS_SEQUENCE: case AS_CONFED_SET: case AS_CONFED_SEQUENCE: break; default: xorp_throw(CorruptMessage, c_format("Bad AS Segment type: %u\n", _type), UPDATEMSGERR, MALASPATH); } d += 2; // skip header, d points to the raw data now. for (size_t i = 0; i < n; d += 4, i++) { /* copy to avoid alignment issues, and force use of correct constructor */ uint32_t as_num; memcpy(&as_num, d, 4); as_num = htonl(as_num); add_as(AsNum(as_num)); } } /** * Convert from internal to external AS4_PATH representation. */ const uint8_t * AS4Segment::encode(size_t &len, uint8_t *data) const { debug_msg("AS4Segment encode\n"); XLOG_ASSERT(_aslist.size() <= 255); size_t i = wire_size(); const_iterator as; if (data == 0) data = new uint8_t[i]; else XLOG_ASSERT(len >= i); len = i; data[0] = _type; data[1] = _aslist.size(); for (i = 2, as = _aslist.begin(); as != _aslist.end(); i += 4, ++as) { debug_msg("Encoding 4-byte As %d\n", as->as()); uint32_t as_num = htonl(as->as4()); memcpy(data + i, &as_num, 4); } return data; } /* *************** ASPath *********************** */ /** * ASPath constructor by parsing strings. Input strings * should have the form * * "segment, segment, segment, ... ,segment" * * where segments are parsed as * * AS_SEQUENCE: [comma-separated-asn-list] or comma-separated-asn-list * AS_SET: {comma-separated-asn-list} * AS_CONFED_SEQUENCE: (comma-separated-asn-list) * AS_CONFED_SET: * * blank spaces " " can appear at any point in the string. */ ASPath::ASPath(const char *as_path) throw(InvalidString) { debug_msg("ASPath(%s) constructor called\n", as_path); _num_segments = 0; _path_len = 0; // make a copy removing all spaces from the string. string path = as_path; size_t pos; while(string::npos != (pos = path.find(" "))) { path = path.substr(0, pos) + path.substr(pos + 1); } ASSegment seg; for (size_t i = 0; i < path.length(); i++) { char c = path[i]; debug_msg("check <%c> at pos %u\n", c, XORP_UINT_CAST(i)); if (xorp_isdigit(c)) { size_t start = i; if (seg.type() == AS_NONE) // possibly start new segment seg.set_type(AS_SEQUENCE); while (i < path.length() && (xorp_isdigit(path[i]) || path[i]=='.')) { i++; } string asnum = path.substr(start, i-start); i--; // back to last valid character debug_msg("asnum = %s\n", asnum.c_str()); seg.add_as(AsNum(asnum)); } else if (c == '[') { if (seg.type() == AS_SEQUENCE) { // push previous thing and start a new one add_segment(seg); seg.clear(); } else if (seg.type() != AS_NONE) // nested, invalid xorp_throw(InvalidString, c_format("Illegal character: <%c> %s", c, path.c_str())); seg.set_type(AS_SEQUENCE); } else if (c == ']') { if (seg.type() == AS_SEQUENCE) { // push previous thing and start a new one add_segment(seg); seg.clear(); } else xorp_throw(InvalidString, c_format("Illegal character: <%c> %s", c, path.c_str())); } else if (c == '{') { if (seg.type() == AS_SEQUENCE) { // push previous thing and start a new one add_segment(seg); seg.clear(); } else if (seg.type() != AS_NONE) // nested, invalid xorp_throw(InvalidString, c_format("Illegal character: <%c> %s", c, path.c_str())); seg.set_type(AS_SET); } else if (c == '}') { if (seg.type() == AS_SET) { // push previous thing and start a new one add_segment(seg); seg.clear(); } else xorp_throw(InvalidString, c_format("Illegal character: <%c> %s", c, path.c_str())); } else if (c == '(') { if (seg.type() == AS_SEQUENCE) { // push previous thing and start a new one add_segment(seg); seg.clear(); } else if (seg.type() != AS_NONE) // nested, invalid xorp_throw(InvalidString, c_format("Illegal character: <%c> %s", c, path.c_str())); seg.set_type(AS_CONFED_SEQUENCE); } else if (c == ')') { if (seg.type() == AS_CONFED_SEQUENCE) { // push previous thing and start a new one add_segment(seg); seg.clear(); } else xorp_throw(InvalidString, c_format("Illegal character: <%c> %s", c, path.c_str())); } else if (c == '<') { if (seg.type() == AS_SEQUENCE) { // push previous thing and start a new one add_segment(seg); seg.clear(); } else if (seg.type() != AS_NONE) // nested, invalid xorp_throw(InvalidString, c_format("Illegal character: <%c> %s", c, path.c_str())); seg.set_type(AS_CONFED_SET); } else if (c == '>') { if (seg.type() == AS_CONFED_SET) { // push previous thing and start a new one add_segment(seg); seg.clear(); } else xorp_throw(InvalidString, c_format("Illegal character: <%c> %s", c, path.c_str())); } else if (c == ',') { } else if (c == ' ' || c == '\t') { } else xorp_throw(InvalidString, c_format("Illegal character: <%c> %s", c, path.c_str())); } if (seg.type() == AS_SEQUENCE) // close existing seg. add_segment(seg); else if (seg.type() == AS_SET) xorp_throw(InvalidString, c_format("Unterminated ASSet: %s", path.c_str())); debug_msg("end of ASPath()\n"); } /** * populate an ASPath from the received data representation */ void ASPath::decode(const uint8_t *d, size_t l) throw(CorruptMessage) { _num_segments = 0; _path_len = 0; while (l > 0) { // grab segments size_t len = 2 + d[1]*2; // XXX length in bytes for 16bit AS's if (len > l) xorp_throw(CorruptMessage, c_format("Bad ASpath (len) %u > (l) %u\n", XORP_UINT_CAST(len), XORP_UINT_CAST(l)), UPDATEMSGERR, MALASPATH); ASSegment s(d); add_segment(s); d += len; l -= len; } } /** * construct a new aggregate ASPath from two ASPaths */ ASPath::ASPath(const ASPath &asp1, const ASPath &asp2) { _num_segments = 0; _path_len = 0; size_t curseg; size_t matchelem = 0; bool fullmatch = true; for (curseg = 0; curseg < asp1.num_segments() && curseg < asp2.num_segments(); curseg++) { if (asp1.segment(curseg).type() != asp2.segment(curseg).type()) break; size_t minseglen = min(asp1.segment(curseg).path_length(), asp2.segment(curseg).path_length()); for (matchelem = 0; matchelem < minseglen; matchelem++) if (asp1.segment(curseg).as_num(matchelem) != asp2.segment(curseg).as_num(matchelem)) break; if (matchelem) { ASSegment newseg(asp1.segment(curseg).type()); for (size_t elem = 0; elem < matchelem; elem++) newseg.add_as(asp1.segment(curseg).as_num(elem)); this->add_segment(newseg); } if (matchelem < asp1.segment(curseg).path_length() || matchelem < asp2.segment(curseg).path_length()) { fullmatch = false; break; } } if (!fullmatch) { ASSegment new_asset(AS_SET); size_t startelem = matchelem; for (size_t curseg1 = curseg; curseg1 < asp1.num_segments(); curseg1++) { for (size_t elem = startelem; elem < asp1.segment(curseg1).path_length(); elem++) { const class AsNum asn = asp1.segment(curseg).as_num(elem); if (!new_asset.contains(asn)) new_asset.add_as(asn); } startelem = 0; } startelem = matchelem; for (size_t curseg2 = curseg; curseg2 < asp2.num_segments(); curseg2++) { for (size_t elem = startelem; elem < asp2.segment(curseg2).path_length(); elem++) { const class AsNum asn = asp2.segment(curseg).as_num(elem); if (!new_asset.contains(asn)) new_asset.add_as(asn); } startelem = 0; } this->add_segment(new_asset); } } void ASPath::add_segment(const ASSegment& s) { debug_msg("Adding As Segment\n"); _segments.push_back(s); _num_segments++; size_t n = s.path_length(); _path_len += n; debug_msg("End of add_segment, As Path length by %u to be %u\n", XORP_UINT_CAST(n), XORP_UINT_CAST(_path_len)); } void ASPath::prepend_segment(const ASSegment& s) { debug_msg("Prepending As Segment\n"); _segments.push_front(s); _num_segments++; size_t n = s.path_length(); _path_len += n; debug_msg("End of add_segment, As Path length by %u to be %u\n", XORP_UINT_CAST(n), XORP_UINT_CAST(_path_len)); } string ASPath::str() const { string s = "ASPath:"; const_iterator iter = _segments.begin(); while(iter != _segments.end()) { s.append(" "); s.append((*iter).str()); ++iter; } return s; } string ASPath::short_str() const { string s; const_iterator iter = _segments.begin(); while(iter != _segments.end()) { if (iter != _segments.begin()) s.append(" "); s.append((*iter).short_str()); ++iter; } return s; } size_t ASPath::wire_size() const { size_t l = 0; const_iterator iter = _segments.begin(); for (; iter != _segments.end(); ++iter) l += iter->wire_size(); return l; } const uint8_t * ASPath::encode(size_t &len, uint8_t *buf) const { XLOG_ASSERT(_num_segments == _segments.size()); // XXX expensive const_iterator i; size_t pos, l = wire_size(); // allocate or check the memory. if (buf == 0) // no buffer, allocate one buf = new uint8_t[l]; else // we got a buffer, make sure is large enough. XLOG_ASSERT(len >= l); // in fact, just abort if not so. len = l; // set the correct value. // encode into the buffer for (pos = 0, i = _segments.begin(); i != _segments.end(); ++i) { l = i->wire_size(); i->encode(l, buf + pos); pos += l; } return buf; } void ASPath::prepend_as(const AsNum &asn) { if (_segments.empty() || _segments.front().type() == AS_SET) { ASSegment seg = ASSegment(AS_SEQUENCE); seg.add_as(asn); _segments.push_front(seg); _num_segments++; } else { XLOG_ASSERT(_segments.front().type() == AS_SEQUENCE); _segments.front().prepend_as(asn); } _path_len++; // in both cases the length increases by one. } void ASPath::prepend_confed_as(const AsNum &asn) { if (_segments.empty() || _segments.front().type() == AS_SET || _segments.front().type() == AS_SEQUENCE) { ASSegment seg = ASSegment(AS_CONFED_SEQUENCE); seg.add_as(asn); _segments.push_front(seg); _num_segments++; } else { XLOG_ASSERT(_segments.front().type() == AS_CONFED_SEQUENCE); _segments.front().prepend_as(asn); } _path_len++; // in both cases the length increases by one. } void ASPath::remove_confed_segments() { debug_msg("Deleting all CONFED Segments\n"); const_iterator iter = _segments.begin(); const_iterator next_iter; while (iter != _segments.end()) { next_iter = iter; ++next_iter; if ((*iter).type() == AS_CONFED_SEQUENCE || (*iter).type() == AS_CONFED_SET) { _path_len--; _num_segments--; _segments.remove((*iter)); } iter = next_iter; } } bool ASPath::contains_confed_segments() const { for (const_iterator i = _segments.begin(); i != _segments.end(); i++) { ASPathSegType type = (*i).type(); if (AS_CONFED_SEQUENCE == type || AS_CONFED_SET == type) return true; } return false; } ASPath& ASPath::operator=(const ASPath& him) { // clear out my list while (!_segments.empty()) { _segments.pop_front(); } // copy in from his const_iterator i; for (i = him._segments.begin() ;i != him._segments.end(); i++) { _segments.push_back(*i); } return *this; } bool ASPath::operator==(const ASPath& him) const { if (_num_segments != him._num_segments) return false; const_iterator my_i = _segments.begin(); const_iterator his_i = him._segments.begin(); for (;my_i != _segments.end(); my_i++, his_i++) if (*my_i != *his_i) return false; return true; } bool ASPath::operator<(const ASPath& him) const { if (_num_segments < him._num_segments) return true; if (_num_segments > him._num_segments) return false; const_iterator my_i = _segments.begin(); const_iterator his_i = him._segments.begin(); for (;my_i != _segments.end(); my_i++, his_i++) { if (*my_i < *his_i) return true; if (*his_i < *my_i) return false; } return false; } void ASPath::encode_for_mib(vector& encode_buf) const { //See RFC 1657, Page 15 for the encoding. size_t buf_size = wire_size(); if (buf_size > 2) encode_buf.resize(buf_size); else { //The AS path was empty - this can be the case if the route //originated in our AS. //XXX The MIB doesn't say how to encode this, but it says the //vector must be at least two bytes in size. encode_buf.resize(2); encode_buf[0]=0; encode_buf[1]=0; return; } int ctr = 0; const_iterator i = _segments.begin(); for(i = _segments.begin(); i != _segments.end(); i++) { ctr += i->encode_for_mib(&encode_buf[ctr], buf_size - ctr); } } bool ASPath::two_byte_compatible() const { const_iterator i = _segments.begin(); for(i = _segments.begin(); i != _segments.end(); i++) { if ( !(i->two_byte_compatible()) ) return false; } return true; } void ASPath::merge_as4_path(AS4Path& as4_path) { as4_path.cross_validate(*this); (*this) = as4_path; } #if 0 AS4Path::AS4Path(const uint8_t* d, size_t len, const ASPath& as_path) throw(CorruptMessage) { decode(d, len); cross_validate(as_path); } #endif AS4Path::AS4Path(const uint8_t* d, size_t len) throw(CorruptMessage) { decode(d, len); } /** * populate a ASPath from the received data representation from a * AS4_PATH attribute. */ void AS4Path::decode(const uint8_t *d, size_t l) throw(CorruptMessage) { _num_segments = 0; _path_len = 0; while (l > 0) { // grab segments size_t len = 2 + d[1]*4; // XXX length in bytes for 32bit AS's XLOG_ASSERT(len <= l); AS4Segment s(d); add_segment(s); d += len; l -= len; } } /** * the AS_PATH attribute may have had new ASes added since the * AS4_PATH was last updated. We need to copy across anything that * is missing */ void AS4Path::cross_validate(const ASPath& as_path) { debug_msg("cross validate\n%s\n%s\n", str().c_str(), as_path.str().c_str()); if (as_path.path_length() < path_length() ) { debug_msg("as_path.path_length() < path_length()\n"); // This is illegal. The spec says to ignore the AS4_PATH // attribute and use the data from the AS_PATH attribute throw // away the data we had. while (!_segments.empty()) { _segments.pop_front(); } // copy in from the AS_PATH version for (uint32_t i = 0; i < as_path.num_segments(); i++) { debug_msg("adding %u %s\n", i, as_path.segment(i).str().c_str()); add_segment(as_path.segment(i)); } return; } // The AS_PATH attribute may contain ASes that were added by an // old BGP speaker that are missing from the AS4_PATH attribute. if (as_path.path_length() > path_length()) { if (as_path.num_segments() < num_segments()) { // Now we're in trouble! The AS_PATH has more ASes but // fewer segments than the AS4_PATH. do_patchup(as_path); return; } // The AS_PATH has at least as many segments as the // AS4_PATH find where they differ, and copy across. for (uint32_t i = 1; i <= num_segments(); i++) { const ASSegment *old_seg; ASSegment *new_seg; old_seg = &(as_path.segment(as_path.num_segments() - i)); new_seg = const_cast(&(segment(num_segments() - i))); printf("old seg: %s\n", old_seg->str().c_str()); printf("new seg: %s\n", new_seg->str().c_str()); if (old_seg->path_length() == new_seg->path_length()) continue; if (old_seg->path_length() < new_seg->path_length()) { // ooops - WTF happened here debug_msg("do patchup\n"); do_patchup(as_path); } if (old_seg->path_length() > new_seg->path_length()) { // found a segment that needs data copying over debug_msg("pad segment\n"); printf("new_seg type: %u\n", new_seg->type()); pad_segment(*old_seg, *new_seg); } } debug_msg("after patching: \n"); debug_msg("old as_path (len %u): %s\n", (uint32_t)as_path.path_length(), as_path.str().c_str()); debug_msg("new as_path (len %u): %s\n", (uint32_t)path_length(), str().c_str()); if (as_path.path_length() == path_length()) { return; } XLOG_ASSERT(as_path.num_segments() > num_segments()); // There are more segments, so copy across whole segments for (int i = as_path.num_segments() - num_segments() - 1; i >= 0; i--) { prepend_segment(as_path.segment(i)); } XLOG_ASSERT(as_path.path_length() == path_length()); return; } } void AS4Path::pad_segment(const ASSegment& old_seg, ASSegment& new_seg) { debug_msg("pad: new type: %u\n", new_seg.type()); if (new_seg.type() == AS_SET) { debug_msg("new == AS_SET\n"); // find everything in the old seg that's not in the new seg, // and add it. for (int i = old_seg.as_size()-1; i >= 0; i--) { const AsNum *asn = &(old_seg.as_num(i)); if (asn->as() != AsNum::AS_TRAN) { if (!new_seg.contains(*asn)) { new_seg.prepend_as(*asn); } } } // If that wasn't enough, do arbitrary padding to match size while (old_seg.as_size() < new_seg.as_size()) { new_seg.prepend_as(new_seg.first_asnum()); } return; } if (old_seg.type() == AS_SET) { debug_msg("old == AS_SET\n"); // The old segment was an AS_SET, but the new isn't. Looks // like some old BGP speaker did some form of aggregation. // Convert the new one to an AS_SET too. new_seg.set_type(AS_SET); pad_segment(old_seg, new_seg); return; } debug_msg("both are sequences\n"); // They're both AS_SEQUENCES, so we need to preserve sequence. // First validate that new_seg is a sub-sequence of old_seg for (uint32_t i = 1; i <= new_seg.as_size(); i++) { const AsNum *old_asn = &(old_seg.as_num(old_seg.as_size()-i)); const AsNum *new_asn = &(new_seg.as_num(new_seg.as_size()-i)); debug_msg("old_asn: %s, new_asn: %s\n", old_asn->str().c_str(), new_asn->str().c_str()); // do a compare on the 16-bit compat versions of the numbers if (old_asn->as() != new_asn->as()) { debug_msg("Mismatch\n"); // We've got a mismatch - make it into an AS_SET. This is // a pretty arbitrary decision, but this shouldn't happen // in reality. At least it should be safe. new_seg.set_type(AS_SET); pad_segment(old_seg, new_seg); return; } } // now we can simply copy across the missing AS numbers for (int i = old_seg.as_size() - new_seg.as_size() - 1; i >= 0; i--) { new_seg.prepend_as(old_seg.as_num(i)); _path_len++; } return; } void AS4Path::do_patchup(const ASPath& as_path) { // we get here when the cross validation fails, and we need to do // something that isn't actually illegal. // There's no good way to deal with this, but simplest is to pad // the AS4_PATH with an AS_SET containing anything that wasn't // previously in the AS4_PATH. This at least should prevent // loops forming, but it's really ugly. ASSegment new_set(AS_SET); for (uint32_t i = 0; i < as_path.path_length(); i++) { const ASSegment *s = &(as_path.segment(i)); for (uint32_t j = 0; j < s->path_length(); j++) { const AsNum *asn = &(s->as_num(j)); if (asn->as() == AsNum::AS_TRAN) continue; if (!contains(*asn)) { new_set.add_as(*asn); if (new_set.path_length() + path_length() == as_path.path_length()) { // we've got enough now i = as_path.path_length(); break; } } } } // All this work, and we've still not got enough ASes in the AS // path. Artificially inflate it. if (_segments.front().type() == AS_SET) { // need to merge two sets for (uint32_t i = 0; i < new_set.path_length(); i++) { _segments.front().add_as(new_set.as_num(i)); } } else { prepend_segment(new_set); } // if we still haven't enough, then just pad while (as_path.path_length() > path_length()) { prepend_as(first_asnum()); } return; } const uint8_t * AS4Path::encode(size_t &len, uint8_t *buf) const { debug_msg("AS4Path::encode\n"); XLOG_ASSERT(_num_segments == _segments.size()); // XXX expensive const_iterator i; size_t pos, l = wire_size(); // allocate or check the memory. if (buf == 0) // no buffer, allocate one buf = new uint8_t[l]; else // we got a buffer, make sure is large enough. XLOG_ASSERT(len >= l); // in fact, just abort if not so. len = l; // set the correct value. // encode into the buffer for (pos = 0, i = _segments.begin(); i != _segments.end(); ++i) { const AS4Segment *new_seg = (const AS4Segment*)(&(*i)); l = new_seg->wire_size(); new_seg->encode(l, buf + pos); pos += l; } return buf; } size_t AS4Path::wire_size() const { size_t l = 0; const_iterator i; for (i = _segments.begin(); i != _segments.end(); ++i) { const AS4Segment *new_seg = (const AS4Segment*)(&(*i)); l += new_seg->wire_size(); } return l; } xorp/bgp/aspath.hh0000664000076400007640000003721311540224217014235 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/bgp/aspath.hh,v 1.34 2008/10/02 21:56:14 bms Exp $ #ifndef __BGP_ASPATH_HH__ #define __BGP_ASPATH_HH__ #include #include #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/asnum.hh" #include "libxorp/exceptions.hh" #include "exceptions.hh" class AS4Path; /** * This file contains the classes to manipulate AS segments/lists/paths * * An AS_PATH is made of a list of segments, each segment being * an AS_SET or an AS_SEQUENCE. * Logically, you can think an AS_SEQUENCE as a list of AsNum, * and an AS_SET as an unordered set of AsNum; the path would then be * made of alternate AS_SET and AS_SEQUENCEs. However, the max number * of elements in an AS_SEQUENCE is 255, so you might need to split a * sequence into multiple ones. * * The external representation of AS_SET or AS_SEQUENCE is: * 1 byte: segment type * 1 byte: number of element in the segment * n entries: the ASnumbers in the segment, 2 or 4 bytes each * * In terms of internal representation, it might be more efficient * useful to store segments as either an AS_SET, or a sequence of size 1, * and then do the necessary grouping on output. * * The current implementation allows both forms. * * Note that the external representation (provided by encode()) returns * a malloc'ed chunk of memory which must be freed by the caller. * * RFC 3065 (Autonomous System Confederations for BGP) introduced two * additional segment types: * * AS_CONFED_SEQUENCE: ordered set of Member AS Numbers * in the local confederation that the UPDATE message has traversed * * AS_CONFED_SET: unordered set of Member AS Numbers in * the local confederation that the UPDATE message has traversed * * * 4-byte AS numbers need to be handled carefully. There are two main * cases to consider: when we are configured to do 4-byte AS numbers, * and when we are not. * * When we are not configured to do 4-byte AS numbers all AS path * information received from peers will be 2-byte AS numbers. If * they're configured to do 4-byte AS numbers, they will send us an * AS_PATH and an AS4_PATH, but if we're not configured to do 4-byte * AS numbers, we shouldn't care. We don't merge the two; we * propagate AS4_PATH unchanged, and do the normal stuff with the * regular AS_PATH. The AS4_PATH is actually stored internally in an * UnknownAttribute, as we don't need to know anything about it. * * When we are configured to do 4-byte AS numbers, we can tell from * the negotiated capabilities whether a peer is sending 4-byte AS * numbers or 2-byte AS numbers. If a peer is sending 4-byte AS * numbers, these arrive in the AS_PATH attribute, and we store them * internally in an AS4Path, but stored in the ASPathAttribute, NOT in * the AS4PathAttribute. If a peer is sending 2-byte AS numbers in * AS_PATH, he may also propagate an AS4_PATH. We merge these on * input, cross-validate the two, and store the results just like we * would if we'd received a 4-byte AS_PATH. So, whatever our peer * does, the only place we have AS path information that we use for * the decision process is in the ASPathAttribute. * * If we're doing 4-byte AS numbers and our peer is doing 4-byte AS * numbers, then all routes we send him will contain 4-byte AS_PATH * attributes. We just need to make sure we use the right encode() * method. If our peer is not doing 4-byte AS numbers, then we send a * 2-byte AS_PATH, substituting AS_TRANS for any AS numbers that can't * be represented in 2 bytes. We also construct an AS4_PATH from our * ASPathAttribute, and send that too. */ // AS Path Values enum ASPathSegType { AS_NONE = 0, // not initialized AS_SET = 1, AS_SEQUENCE = 2, AS_CONFED_SEQUENCE = 3, AS_CONFED_SET = 4 }; /** * Parent class for ASPath elements, which can be either ASSet or ASSequence. */ class ASSegment { public: typedef list ASLIST; typedef ASLIST::iterator iterator; typedef ASLIST::const_iterator const_iterator; typedef ASLIST::const_reverse_iterator const_reverse_iterator; /** * Constructor of an empty ASSegment */ ASSegment(ASPathSegType t = AS_NONE) : _type(t) { } /** * constructor from external representation will just decode * the chunk of data received, and convert to internal representation. * In fact, the external representation is quite simple and effective. * * _type is d[0], l is d[1], entries follow. */ ASSegment(const uint8_t* d) throw(CorruptMessage) { decode(d); } /** * Copy constructor */ ASSegment(const ASSegment& a) : _type(a._type), _aslist(a._aslist) {} /** * The destructor has nothing to do, the underlying container will * take care of the thing. */ ~ASSegment() {} /** * reset whatever is currently contained in the object. */ void clear() { _type = AS_NONE; _aslist.clear(); } /** * @return the path length, which is 1 for an AS_SET, * and the length of the sequence for an AS_SEQUENCE */ size_t path_length() const { if (_type == AS_SET || _type == AS_CONFED_SET) return 1; else if (_type == AS_SEQUENCE || _type == AS_CONFED_SEQUENCE) return _aslist.size(); else return 0; // XXX should not be called! } size_t as_size() const { return _aslist.size(); } /** * Add AsNum at the end of the segment (order is irrelevant * for AS_SET but important for AS_SEQUENCE) * This is used when initializing from a string. */ void add_as(const AsNum& n) { debug_msg("Number of As entries %u\n", XORP_UINT_CAST(_aslist.size())); _aslist.push_back(n); } /** * Add AsNum at the beginning of the segment (order is irrelevant * for AS_SET but important for AS_SEQUENCE). * This is used e.g. when a node is adding its AsNum to the sequence. */ void prepend_as(const AsNum& n) { debug_msg("Number of As entries %u\n", XORP_UINT_CAST(_aslist.size())); _aslist.push_front(n); } /** * Check if a given AsNum is contained in the segment. */ bool contains(const AsNum& as_num) const { const_iterator iter; for (iter = _aslist.begin(); iter != _aslist.end(); ++iter) if (*iter == as_num) return true; return false; } const AsNum& first_asnum() const; /** * find the n'th AS number in the segment */ const AsNum& as_num(int n) const { const_iterator i = _aslist.begin(); while (n--) i++; return *i; } /** * Convert the external representation into the internal one. * _type is d[0], _entries is d[1], entries follow. */ void decode(const uint8_t *d) throw(CorruptMessage); /** * Convert from internal to external representation. * If we do not pass a buffer (buf = 0), then the routine will * allocate a new one; otherwise, len indicates the size of the * input buffer, which must be large enough to store the encoding. * @return the pointer to the buffer, len is the actual size. */ const uint8_t *encode(size_t &len, uint8_t *buf) const; /** * @return the size of the list on the wire. */ size_t wire_size() const { return 2 + 2 * _aslist.size(); } /** * @return fancy string representation of the segment */ string str() const; /** * @return compact string representation of the segment */ string short_str() const; /** * compares internal representations for equality. */ bool operator==(const ASSegment& him) const; /** * Compares internal representations for <. */ bool operator<(const ASSegment& him) const; ASPathSegType type() const { return _type; } void set_type(ASPathSegType t) { _type = t; } size_t encode_for_mib(uint8_t* buf, size_t buf_size) const; /** * returns true if the AS segment does not lose information when * represented entirely as two-byte AS numbers */ bool two_byte_compatible() const; protected: ASPathSegType _type; ASLIST _aslist; }; /* subsclass of ASSegment to handle encoding and decoding of 4-byte AS numbers from a AS4_PATH attribute */ class AS4Segment : public ASSegment { public: AS4Segment(const uint8_t* d) throw(CorruptMessage) { decode(d); } /** * Convert the external representation into the internal one. * _type is d[0], _entries is d[1], entries follow. */ void decode(const uint8_t *d) throw(CorruptMessage); /** * Convert from internal to external representation. * If we do not pass a buffer (buf = 0), then the routine will * allocate a new one; otherwise, len indicates the size of the * input buffer, which must be large enough to store the encoding. * @return the pointer to the buffer, len is the actual size. */ const uint8_t *encode(size_t &len, uint8_t *buf) const; /** * @return the size of the list on the wire. */ size_t wire_size() const { return 2 + 4 * _aslist.size(); } private: /* no storage, as this is handled by the underlying ASSegment */ }; /** * An ASPath is a list of ASSegments, each of which can be an AS_SET, * AS_CONFED_SET, AS_SEQUENCE, or an AS_CONFED_SEQUENCE. */ class ASPath { public: typedef list ::const_iterator const_iterator; typedef list ::iterator iterator; ASPath() : _num_segments(0), _path_len(0) {} /** * Initialize from a string in the format * 1,2,(3,4,5),6,7,8,(9,10,11),12,13 */ ASPath(const char *as_path) throw(InvalidString); /** * construct from received data */ ASPath(const uint8_t* d, size_t len) throw(CorruptMessage) { decode(d, len); } /** * construct an aggregate from two ASPaths */ ASPath(const ASPath &asp1, const ASPath &asp2); /** * Copy constructor */ ASPath(const ASPath &a) : _segments(a._segments), _num_segments(a._num_segments), _path_len(a._path_len) {} ~ASPath() {} void add_segment(const ASSegment& s); void prepend_segment(const ASSegment& s); size_t path_length() const { return _path_len; } bool contains(const AsNum& as_num) const { const_iterator i = _segments.begin(); for (; i != _segments.end(); ++i) if ((*i).contains(as_num)) return true; return false; } const AsNum& first_asnum() const { XLOG_ASSERT(!_segments.empty()); return _segments.front().first_asnum(); } string str() const; string short_str() const; const ASSegment& segment(size_t n) const { if (n < _num_segments) { const_iterator iter = _segments.begin(); for (u_int i = 0; i& aspath) const; /** * returns true if the AS path does not lose information when * represented entirely as two-byte AS numbers */ bool two_byte_compatible() const; /** * Merge an AS4Path into a 2-byte AS Path. Both paths will end up * containing the same data * * param as4path the AS4_PATH to be merged. */ void merge_as4_path(AS4Path& as4_path); protected: /** * internal representation */ list _segments; size_t _num_segments; size_t _path_len; private: /** * populate an ASPath from received data. Only used in the constructor. */ void decode(const uint8_t *d, size_t len) throw(CorruptMessage); }; /* subclass to handle 4-byte AS encoding and decoding */ class AS4Path : public ASPath { public: /** * Construct from received data from 4-byte peer. */ AS4Path(const uint8_t* d, size_t len) throw(CorruptMessage); /** * Initialize from a string in the format * 3.1,2,(3,10.4,5),6,7,8,(9,10,11),12,13 */ AS4Path(const char *as_path) throw(InvalidString) : ASPath(as_path) {}; #if 0 /** * Construct from received data from 2-byte peer. This needs to * take the regular ASPath in addition to the AS4_PATH data, * because it needs to cross-validate the two. */ AS4Path(const uint8_t* d, size_t len, const ASPath& as_path) throw(CorruptMessage); #endif /** * Convert from internal to external representation, with the * correct representation for the original AS4_PATH attribute. * * If we do not pass a buffer (buf = 0), then the routine will * allocate a new one; otherwise, len indicates the size of the * input buffer, which must be large enough to store the encoding. * @return the pointer to the buffer, len is the actual size. */ const uint8_t *encode(size_t &len, uint8_t *buf) const; size_t wire_size() const; void cross_validate(const ASPath& as_path); private: /** * populate an ASPath from received data. Only used in the constructor. */ void decode(const uint8_t *d, size_t len) throw(CorruptMessage); void pad_segment(const ASSegment& old_seg, ASSegment& new_seg); void do_patchup(const ASPath& as_path); }; #endif // __BGP_ASPATH_HH__ xorp/bgp/internal_message.hh0000664000076400007640000001137511421137511016274 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/bgp/internal_message.hh,v 1.14 2008/11/08 06:14:36 mjh Exp $ #ifndef __BGP_INTERNAL_MESSAGES_HH__ #define __BGP_INTERNAL_MESSAGES_HH__ #include "libxorp/xorp.h" #include "libxorp/ref_ptr.hh" #include "subnet_route.hh" #include "path_attribute.hh" class PeerHandler; #define GENID_UNKNOWN 0 /** * @short InternalMessage is used to pass route changes between BGP * route table classes. * * XORP BGP is implemented as a pipelined series of route_tables, * starting with a RibInTable for each peering, converging on a * DecisionTable to decide which competing route is prefered, and then * fanning out again to a RibOutTable for each peer. Routing changes * such as add_route, delete_route, and replace_route propagate * through this pipeline. The "payload" of these changes is an * InternalMessage, which contains the route itself, the peering from * which this route originated, and the generation ID of the RibIn at * that peering. */ template class InternalMessage { public: InternalMessage(const SubnetRoute *route, const PeerHandler *origin_peer, uint32_t genid); InternalMessage(const SubnetRoute *route, FPAListRef pa_list, const PeerHandler *origin_peer, uint32_t genid); ~InternalMessage(); const IPNet& net() const; const SubnetRoute *route() const { return _subnet_route; } const PeerHandler* origin_peer() const { return _origin_peer; } const A& nexthop() const { return _attributes->nexthop(); } FPAListRef& attributes() {return _attributes;} const FPAListRef& const_attributes() const {return _attributes;} bool changed() const { return _changed; } void set_changed() { _changed = true; } void clear_changed() const { _changed = false; } bool copied() const { return _copied; } void set_copied() { _copied = true; } void clear_copied() const { _copied = false; } bool push() const { return _push; } void set_push() { _push = true; } void clear_push() { _push = false; } bool from_previous_peering() const { return _from_previous_peering; } void set_from_previous_peering() { _from_previous_peering = true; } uint32_t genid() const { return _genid; } #if 0 // This is a hack to override const in DecisionTable. void force_clear_push() const { _push = false; } #endif // this should only be called if the msg had been copied void inactivate() const { if (_copied) { _subnet_route->unref(); _subnet_route = NULL; } } string str() const; protected: private: /** * the actual route data. */ mutable const SubnetRoute *_subnet_route; /** * the path attribute list we use for fast accesses. This is * reference counted so we don't need to worry about freeing it * when we copy it from message to message. */ FPAListRef _attributes; /** * we need origin_peer to make sure we don't send a route back to * the peer it came from, or send an IBGP route to an IBGP peer. */ const PeerHandler *_origin_peer; /** * changed indicates that the route data has been modified since * the route was last stored (and so needs storing by a * CacheTable). */ mutable bool _changed; /** * copied indicates that the subnet route data has been copied since * the route was last stored (and so needs freeing by the final recipient) */ mutable bool _copied; /** * genid is the generation ID from the RibIn, if known, or zero if * it's not known. */ uint32_t _genid; /** * push indicates that this is the last route in a batch, so the * push to peers is implicit. */ mutable bool _push; /** * from_previous_peering is set on messages where the deleted route * originates from a previous peering that has now gone down. */ bool _from_previous_peering; }; #endif // __BGP_INTERNAL_MESSAGES_HH__ xorp/bgp/configure_bgp.sh0000775000076400007640000000357511421137511015606 0ustar greearbgreearb#!/bin/sh # # $XORP: xorp/bgp/configure_bgp.sh,v 1.15 2002/12/09 10:59:36 pavlin Exp $ # # # Send configuration commands to a running bgp process. # . ./bgp_xrl_shell_funcs.sh case `hostname` in aardvark) LOCALHOST=aardvark.icir.org PORT=9000 AS=65009 ID=192.150.187.20 HOLDTIME=0 local_config $AS $ID # register_rib rib PEER=xorp-c4000.icir.org PEER_AS=65000 PEER_PORT=179 NEXT_HOP=192.150.187.78 add_peer $LOCALHOST $PORT $PEER $PEER_PORT $PEER_AS $NEXT_HOP $HOLDTIME enable_peer $LOCALHOST $PORT $PEER $PEER_PORT $PEER_AS ;; dhcp111) LOCALHOST=dhcp111.icir.org PORT=9000 AS=65001 ID=192.150.187.111 HOLDTIME=0 local_config $AS $ID # register_rib rib PEER=xorp-c4000.icir.org PEER_AS=65000 PEER_PORT=179 NEXT_HOP=192.150.187.78 add_peer $LOCALHOST $PORT $PEER $PEER_PORT $PEER_AS $NEXT_HOP $HOLDTIME enable_peer $LOCALHOST $PORT $PEER $PEER_PORT $PEER_AS ;; tigger.icir.org) LOCALHOST=tigger.icir.org PORT=9000 AS=65008 ID=192.150.187.78 HOLDTIME=0 local_config $AS $ID # register_rib rib PEER=xorp-c4000.icir.org PEER_AS=65000 PEER_PORT=179 NEXT_HOP=192.150.187.78 add_peer $LOCALHOST $PORT $PEER $PEER_PORT $PEER_AS $NEXT_HOP $HOLDTIME enable_peer $LOCALHOST $PORT $PEER $PEER_PORT $PEER_AS PEER=tigger.icir.org PEER_AS=65008 PEER_PORT=9001 NEXT_HOP=192.150.187.78 add_peer $LOCALHOST $PORT $PEER $PEER_PORT $PEER_AS $NEXT_HOP $HOLDTIME enable_peer $LOCALHOST $PORT $PEER $PEER_PORT $PEER_AS exit ORIGIN=0 AS_PATH=$AS NEXT_HOP=192.150.187.78 NLRI=128.16.0.0/16 #add_route $ORIGIN $AS_PATH $NEXT_HOP $NLRI #sleep 10 #disable_peer $PEER $PEER_AS #enable_peer $PEER $PEER_AS #add_route $ORIGIN $AS_PATH $NEXT_HOP $NLRI #terminate # set -e # Bomb if a call fails while : do sleep 20 date disable_peer $PEER $PEER_AS sleep 20 date enable_peer $PEER $PEER_AS done ;; *) echo "Unknown host :" `hostname` exit 1 ;; esac xorp/bgp/xrl_target.cc0000664000076400007640000012265111540225521015116 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #define PROFILE_UTILS_REQUIRED #include "bgp_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/status_codes.h" #include "libxorp/eventloop.hh" #include "libxorp/asnum.hh" #include "libxipc/xrl_std_router.hh" #include "libxipc/xrl_error.hh" #ifndef XORP_DISABLE_PROFILE #include "xrl/interfaces/profile_client_xif.hh" #endif #include "bgp.hh" #include "iptuple.hh" #include "xrl_target.hh" #include "profile_vars.hh" XrlBgpTarget::XrlBgpTarget(XrlRouter *r, BGPMain& bgp) : XrlBgpTargetBase(r), _bgp(bgp), _awaiting_config(true), _awaiting_as(true), _as(AsNum::AS_INVALID), _awaiting_bgp_id(true), _awaiting_4byte_asnums(true), _use_4byte_asnums(false), _done(false) { } XrlCmdError XrlBgpTarget::common_0_1_get_target_name(string& name) { name = "bgp"; return XrlCmdError::OKAY(); } XrlCmdError XrlBgpTarget::common_0_1_get_version(string& version) { version = "0.1"; return XrlCmdError::OKAY(); } XrlCmdError XrlBgpTarget::common_0_1_get_status( // Output values, uint32_t& status, string& reason) { status = _bgp.status(reason); return XrlCmdError::OKAY(); } XrlCmdError XrlBgpTarget::common_0_1_shutdown() { if(!_awaiting_config) { _bgp.terminate(); } else { _awaiting_config = false; _done = true; } return XrlCmdError::OKAY(); } XrlCmdError XrlBgpTarget::bgp_0_3_get_bgp_version( // Output values, uint32_t& version) { version = 4; return XrlCmdError::OKAY(); } XrlCmdError XrlBgpTarget::bgp_0_3_local_config( // Input values, const string& as, const IPv4& id, const bool& use_4byte_asnums) { debug_msg("as %s id %s\n", as.c_str(), id.str().c_str()); /* ** We may already be configured so don't allow a reconfiguration. */ if(!_awaiting_config) return XrlCmdError::COMMAND_FAILED("Attempt to reconfigure BGP denied."); _use_4byte_asnums = use_4byte_asnums; try { _as = AsNum(as); } catch(InvalidString &e) { return XrlCmdError::COMMAND_FAILED(e.str()); } _bgp.local_config(_as.as4(), id, use_4byte_asnums); _awaiting_config = false; return XrlCmdError::OKAY(); } XrlCmdError XrlBgpTarget::bgp_0_3_set_local_as( // Input values, const string& as) { debug_msg("as %s\n", as.c_str()); /* ** We may already be configured so don't allow a reconfiguration. */ // if(!_awaiting_as) // return XrlCmdError::COMMAND_FAILED("Attempt to reconfigure BGP AS"); try { _as = AsNum(as); } catch(InvalidString &e) { return XrlCmdError::COMMAND_FAILED(e.str()); } _awaiting_as = false; if(!_awaiting_as && !_awaiting_bgp_id && !_awaiting_4byte_asnums) { _bgp.local_config(_as.as4(), _id, _use_4byte_asnums); _awaiting_config = false; } return XrlCmdError::OKAY(); } XrlCmdError XrlBgpTarget::bgp_0_3_get_local_as( // Output values, string& as) { if(_awaiting_as) return XrlCmdError::COMMAND_FAILED("BGP AS not yet configured"); as = _as.short_str(); return XrlCmdError::OKAY(); } XrlCmdError XrlBgpTarget::bgp_0_3_set_4byte_as_support( // Input values, const bool& enabled) { _use_4byte_asnums = enabled; _awaiting_4byte_asnums = false; if(!_awaiting_as && !_awaiting_bgp_id && !_awaiting_4byte_asnums) { _bgp.local_config(_as.as4(), _id, _use_4byte_asnums); _awaiting_config = false; } return XrlCmdError::OKAY(); } XrlCmdError XrlBgpTarget::bgp_0_3_set_bgp_id( // Input values, const IPv4& id) { debug_msg("id %s\n", id.str().c_str()); /* ** We may already be configured so don't allow a reconfiguration. */ // if(!_awaiting_bgp_id) // return XrlCmdError::COMMAND_FAILED("Attempt to reconfigure BGP ID"); _id = id; _awaiting_bgp_id = false; if(!_awaiting_as && !_awaiting_bgp_id && !_awaiting_4byte_asnums) { _bgp.local_config(_as.as4(), _id, _use_4byte_asnums); _awaiting_config = false; } return XrlCmdError::OKAY(); } XrlCmdError XrlBgpTarget::bgp_0_3_get_bgp_id( // Output values, IPv4& id) { if(_awaiting_bgp_id) return XrlCmdError::COMMAND_FAILED("BGP ID not yet configured"); id = _id; return XrlCmdError::OKAY(); } XrlCmdError XrlBgpTarget::bgp_0_3_set_confederation_identifier(const string& as, const bool& disable) { debug_msg("as %s disable %s\n", as.c_str(), bool_c_str(disable)); try { _bgp.set_confederation_identifier(AsNum(as).as4(), disable); } catch(InvalidString &e) { return XrlCmdError::COMMAND_FAILED(e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlBgpTarget::bgp_0_3_set_cluster_id(const IPv4& cluster_id, const bool& disable) { debug_msg("Cluster ID %s disable %s\n", cstring(cluster_id), bool_c_str(disable)); _bgp.set_cluster_id(cluster_id, disable); return XrlCmdError::OKAY(); } XrlCmdError XrlBgpTarget::bgp_0_3_set_damping(const uint32_t& half_life, const uint32_t& max_suppress, const uint32_t& reuse, const uint32_t& suppress, const bool& disable) { debug_msg("Damping half-life %u max-supress %u reuse %u suppress %u" " disable %s\n", half_life, max_suppress, reuse, suppress, bool_c_str(disable)); if (half_life < 1 || half_life > 45) return XrlCmdError:: COMMAND_FAILED(c_format("half-life %u not 1..45", half_life)); if (max_suppress < 1 || max_suppress > 720) return XrlCmdError:: COMMAND_FAILED(c_format("max-suppress %u not 1..720", max_suppress)); if (reuse < 1 || reuse > 20000) return XrlCmdError:: COMMAND_FAILED(c_format("reuse %u not 1..20000", reuse)); if (suppress < 1 || suppress > 20000) return XrlCmdError:: COMMAND_FAILED(c_format("suppress %u not 1..20000", suppress)); _bgp.set_damping(half_life, max_suppress, reuse, suppress, disable); return XrlCmdError::OKAY(); } XrlCmdError XrlBgpTarget::bgp_0_3_add_peer( // Input values, const string& local_dev, const string& local_ip, const uint32_t& local_port, const string& peer_ip, const uint32_t& peer_port, const string& as, const IPv4& next_hop, const uint32_t& holdtime) { debug_msg("local ip %s local port %u peer ip %s peer port %u as %s" " next_hop %s holdtime %u\n", local_ip.c_str(), XORP_UINT_CAST(local_port), peer_ip.c_str(), XORP_UINT_CAST(peer_port), as.c_str(), next_hop.str().c_str(), XORP_UINT_CAST(holdtime)); if(_awaiting_config) return XrlCmdError::COMMAND_FAILED("BGP Not configured!!!"); if(!_bgp.processes_ready()) return XrlCmdError::COMMAND_FAILED("FEA or RIB not running"); BGPPeerData *pd = 0; try { Iptuple iptuple(local_dev.c_str(), local_ip.c_str(), local_port, peer_ip.c_str(), peer_port); AsNum asn(as); pd = new BGPPeerData(*_bgp.get_local_data(), iptuple, asn, next_hop, holdtime); } catch(XorpException& e) { delete pd; return XrlCmdError::COMMAND_FAILED(e.str()); } if(!_bgp.create_peer(pd)) { delete pd; return XrlCmdError::COMMAND_FAILED(); } return XrlCmdError::OKAY(); } XrlCmdError XrlBgpTarget::bgp_0_3_delete_peer( // Input values, const string& local_ip, const uint32_t& local_port, const string& peer_ip, const uint32_t& peer_port) { debug_msg("local ip %s local port %u peer ip %s peer port %u\n", local_ip.c_str(), XORP_UINT_CAST(local_port), peer_ip.c_str(), XORP_UINT_CAST(peer_port)); try { Iptuple iptuple("", local_ip.c_str(), local_port, peer_ip.c_str(), peer_port); if(!_bgp.delete_peer(iptuple)) return XrlCmdError::COMMAND_FAILED(); } catch(XorpException& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlBgpTarget::bgp_0_3_enable_peer( // Input values, const string& local_ip, const uint32_t& local_port, const string& peer_ip, const uint32_t& peer_port) { debug_msg("local ip %s local port %u peer ip %s peer port %u\n", local_ip.c_str(), XORP_UINT_CAST(local_port), peer_ip.c_str(), XORP_UINT_CAST(peer_port)); try { Iptuple iptuple("", local_ip.c_str(), local_port, peer_ip.c_str(), peer_port); if(!_bgp.enable_peer(iptuple)) return XrlCmdError::COMMAND_FAILED(); } catch(XorpException& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlBgpTarget::bgp_0_3_disable_peer( // Input values, const string& local_ip, const uint32_t& local_port, const string& peer_ip, const uint32_t& peer_port) { debug_msg("local ip %s local port %u peer ip %s peer port %u\n", local_ip.c_str(), XORP_UINT_CAST(local_port), peer_ip.c_str(), XORP_UINT_CAST(peer_port)); try { Iptuple iptuple("", local_ip.c_str(), local_port, peer_ip.c_str(), peer_port); if(!_bgp.disable_peer(iptuple)) return XrlCmdError::COMMAND_FAILED(); } catch(XorpException& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlBgpTarget::bgp_0_3_change_local_ip( // Input values, const string& local_ip, const uint32_t& local_port, const string& peer_ip, const uint32_t& peer_port, const string& new_local_ip, const string& new_local_dev) { XLOG_ERROR("local ip %s local port %u peer ip %s peer port %u" " new_local_ip %s new_local_dev: %s\n", local_ip.c_str(), XORP_UINT_CAST(local_port), peer_ip.c_str(), XORP_UINT_CAST(peer_port), new_local_ip.c_str(), new_local_dev.c_str()); try { Iptuple iptuple("", local_ip.c_str(), local_port, peer_ip.c_str(), peer_port); if(!_bgp.change_local_ip(iptuple, new_local_ip, new_local_dev)) return XrlCmdError::COMMAND_FAILED(); } catch(XorpException& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlBgpTarget::bgp_0_3_change_local_port( // Input values, const string& local_ip, const uint32_t& local_port, const string& peer_ip, const uint32_t& peer_port, const uint32_t& new_local_port) { debug_msg("local ip %s local port %u peer ip %s peer port %u" " new local port %u\n", local_ip.c_str(), XORP_UINT_CAST(local_port), peer_ip.c_str(), XORP_UINT_CAST(peer_port), new_local_port); try { Iptuple iptuple("", local_ip.c_str(), local_port, peer_ip.c_str(), peer_port); if(!_bgp.change_local_port(iptuple, new_local_port)) return XrlCmdError::COMMAND_FAILED(); } catch(XorpException& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlBgpTarget::bgp_0_3_change_peer_port( // Input values, const string& local_ip, const uint32_t& local_port, const string& peer_ip, const uint32_t& peer_port, const uint32_t& new_peer_port) { debug_msg("local ip %s local port %u peer ip %s peer port %u" " new peer port %u\n", local_ip.c_str(), XORP_UINT_CAST(local_port), peer_ip.c_str(), XORP_UINT_CAST(peer_port), new_peer_port); try { Iptuple iptuple("", local_ip.c_str(), local_port, peer_ip.c_str(), peer_port); if(!_bgp.change_peer_port(iptuple, new_peer_port)) return XrlCmdError::COMMAND_FAILED(); } catch(XorpException& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlBgpTarget::bgp_0_3_set_peer_as( // Input values, const string& local_ip, const uint32_t& local_port, const string& peer_ip, const uint32_t& peer_port, const string& peer_as) { debug_msg("local ip %s local port %u peer ip %s peer port %u" " peer as %s\n", local_ip.c_str(), XORP_UINT_CAST(local_port), peer_ip.c_str(), XORP_UINT_CAST(peer_port), peer_as.c_str()); try { Iptuple iptuple("", local_ip.c_str(), local_port, peer_ip.c_str(), peer_port); AsNum peer_asn(peer_as); if(!_bgp.set_peer_as(iptuple, peer_asn.as4())) return XrlCmdError::COMMAND_FAILED(); } catch(XorpException& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlBgpTarget::bgp_0_3_set_holdtime( // Input values, const string& local_ip, const uint32_t& local_port, const string& peer_ip, const uint32_t& peer_port, const uint32_t& holdtime) { debug_msg("local ip %s local port %u peer ip %s peer port %u" " new holdtime %u\n", local_ip.c_str(), XORP_UINT_CAST(local_port), peer_ip.c_str(), XORP_UINT_CAST(peer_port), holdtime); try { Iptuple iptuple("", local_ip.c_str(), local_port, peer_ip.c_str(), peer_port); if(!_bgp.set_holdtime(iptuple, holdtime)) return XrlCmdError::COMMAND_FAILED(); } catch(XorpException& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlBgpTarget::bgp_0_3_set_delay_open_time( // Input values, const string& local_ip, const uint32_t& local_port, const string& peer_ip, const uint32_t& peer_port, const uint32_t& delay_open_time) { debug_msg("local ip %s local port %u peer ip %s peer port %u" " new delay open time %u\n", local_ip.c_str(), XORP_UINT_CAST(local_port), peer_ip.c_str(), XORP_UINT_CAST(peer_port), delay_open_time); try { Iptuple iptuple("", local_ip.c_str(), local_port, peer_ip.c_str(), peer_port); if(!_bgp.set_delay_open_time(iptuple, delay_open_time)) return XrlCmdError::COMMAND_FAILED(); } catch(XorpException& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlBgpTarget::bgp_0_3_set_route_reflector_client( // Input values, const string& local_ip, const uint32_t& local_port, const string& peer_ip, const uint32_t& peer_port, const bool& state) { debug_msg("local ip %s local port %u peer ip %s peer port %u" " state %s\n", local_ip.c_str(), XORP_UINT_CAST(local_port), peer_ip.c_str(), XORP_UINT_CAST(peer_port), bool_c_str(state)); try { Iptuple iptuple("", local_ip.c_str(), local_port, peer_ip.c_str(), peer_port); if(!_bgp.set_route_reflector_client(iptuple, state)) return XrlCmdError::COMMAND_FAILED(); } catch(XorpException& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlBgpTarget::bgp_0_3_set_confederation_member( // Input values, const string& local_ip, const uint32_t& local_port, const string& peer_ip, const uint32_t& peer_port, const bool& state) { debug_msg("local ip %s local port %u peer ip %s peer port %u" " state %s\n", local_ip.c_str(), XORP_UINT_CAST(local_port), peer_ip.c_str(), XORP_UINT_CAST(peer_port), bool_c_str(state)); try { Iptuple iptuple("", local_ip.c_str(), local_port, peer_ip.c_str(), peer_port); if(!_bgp.set_confederation_member(iptuple, state)) return XrlCmdError::COMMAND_FAILED(); } catch(XorpException& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlBgpTarget::bgp_0_3_set_prefix_limit( // Input values, const string& local_ip, const uint32_t& local_port, const string& peer_ip, const uint32_t& peer_port, const uint32_t& maximum, const bool& state) { debug_msg("local ip %s local port %u peer ip %s peer port %u" " maximum %u state %s\n", local_ip.c_str(), XORP_UINT_CAST(local_port), peer_ip.c_str(), XORP_UINT_CAST(peer_port), XORP_UINT_CAST(maximum), bool_c_str(state)); try { Iptuple iptuple("", local_ip.c_str(), local_port, peer_ip.c_str(), peer_port); if(!_bgp.set_prefix_limit(iptuple, maximum, state)) return XrlCmdError::COMMAND_FAILED(); } catch(XorpException& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlBgpTarget::bgp_0_3_set_nexthop4( // Input values, const string& local_ip, const uint32_t& local_port, const string& peer_ip, const uint32_t& peer_port, const IPv4& next_hop) { debug_msg("local ip %s local port %u peer ip %s peer port %u" " nexthop %s\n", local_ip.c_str(), XORP_UINT_CAST(local_port), peer_ip.c_str(), XORP_UINT_CAST(peer_port), cstring(next_hop)); try { Iptuple iptuple("", local_ip.c_str(), local_port, peer_ip.c_str(), peer_port); if(!_bgp.set_nexthop4(iptuple, next_hop)) return XrlCmdError::COMMAND_FAILED(); } catch(XorpException& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlBgpTarget::bgp_0_3_set_peer_state( // Input values, const string& local_ip, const uint32_t& local_port, const string& peer_ip, const uint32_t& peer_port, const bool& toggle) { debug_msg("local ip %s local port %u peer ip %s peer port %u toggle %d\n", local_ip.c_str(), XORP_UINT_CAST(local_port), peer_ip.c_str(), XORP_UINT_CAST(peer_port), toggle); try { Iptuple iptuple("", local_ip.c_str(), local_port, peer_ip.c_str(), peer_port); if(!_bgp.set_peer_state(iptuple, toggle)) return XrlCmdError::COMMAND_FAILED(); } catch(XorpException& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlBgpTarget::bgp_0_3_set_peer_md5_password( // Input values, const string& local_ip, const uint32_t& local_port, const string& peer_ip, const uint32_t& peer_port, const string& password) { debug_msg("local ip %s local port %u peer ip %s peer port %u password %s\n", local_ip.c_str(), XORP_UINT_CAST(local_port), peer_ip.c_str(), XORP_UINT_CAST(peer_port), password.c_str()); try { Iptuple iptuple("", local_ip.c_str(), local_port, peer_ip.c_str(), peer_port); if(!_bgp.set_peer_md5_password(iptuple, password)) return XrlCmdError::COMMAND_FAILED(); } catch(XorpException& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlBgpTarget::bgp_0_3_activate( // Input values, const string& local_ip, const uint32_t& local_port, const string& peer_ip, const uint32_t& peer_port) { debug_msg("local ip %s local port %u peer ip %s peer port %u\n", local_ip.c_str(), XORP_UINT_CAST(local_port), peer_ip.c_str(), XORP_UINT_CAST(peer_port)); try { Iptuple iptuple("", local_ip.c_str(), local_port, peer_ip.c_str(), peer_port); if(!_bgp.activate(iptuple)) return XrlCmdError::COMMAND_FAILED(); } catch(XorpException& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlBgpTarget::bgp_0_3_next_hop_rewrite_filter( // Input values, const string& local_ip, const uint32_t& local_port, const string& peer_ip, const uint32_t& peer_port, const IPv4& next_hop) { debug_msg("local ip %s local port %u peer ip %s peer port %u " "next hop %s\n", local_ip.c_str(), XORP_UINT_CAST(local_port), peer_ip.c_str(), XORP_UINT_CAST(peer_port), next_hop.str().c_str()); try { Iptuple iptuple("", local_ip.c_str(), local_port, peer_ip.c_str(), peer_port); if(!_bgp.next_hop_rewrite_filter(iptuple, next_hop)) return XrlCmdError::COMMAND_FAILED(); } catch(XorpException& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlBgpTarget::bgp_0_3_originate_route4( // Input values, const IPv4Net& nlri, const IPv4& next_hop, const bool& unicast, const bool& multicast) { debug_msg("nlri %s next hop %s unicast %d multicast %d\n", nlri.str().c_str(), next_hop.str().c_str(), unicast, multicast); if (!_bgp.originate_route(nlri, next_hop, unicast, multicast,PolicyTags())) return XrlCmdError::COMMAND_FAILED(); return XrlCmdError::OKAY(); } XrlCmdError XrlBgpTarget::bgp_0_3_withdraw_route4( // Input values, const IPv4Net& nlri, const bool& unicast, const bool& multicast) { debug_msg("nlri %s unicast %d multicast %d\n", nlri.str().c_str(), unicast, multicast); if (!_bgp.withdraw_route(nlri, unicast, multicast)) return XrlCmdError::COMMAND_FAILED(); return XrlCmdError::OKAY(); } XrlCmdError XrlBgpTarget::bgp_0_3_trace(const string& tvar, const bool& enable) { debug_msg("trace variable %s %s\n", tvar.c_str(), enable ? "enable" : "disable"); // A little hack to change the verbosity level of the trace messages. if ("xlog_verbose" == tvar) { xlog_verbose_t verbose = enable ? XLOG_VERBOSE_HIGH : XLOG_VERBOSE_LOW; xlog_level_set_verbose(XLOG_LEVEL_INFO, verbose); xlog_level_set_verbose(XLOG_LEVEL_TRACE, verbose); return XrlCmdError::OKAY(); } #ifndef XORP_DISABLE_PROFILE try { if (enable) _bgp.profile().enable(tvar); else _bgp.profile().disable(tvar); } catch(PVariableUnknown& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } catch(PVariableLocked& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } #endif return XrlCmdError::OKAY(); } XrlCmdError XrlBgpTarget::bgp_0_3_get_peer_list_start( // Output values, uint32_t& token, bool& more) { more = _bgp.get_peer_list_start(token); return XrlCmdError::OKAY(); } XrlCmdError XrlBgpTarget::bgp_0_3_get_peer_list_next( // Input values, const uint32_t& token, // Output values, string& local_ip, uint32_t& local_port, string& peer_ip, uint32_t& peer_port, bool& more) { more = _bgp.get_peer_list_next(token, local_ip, local_port, peer_ip, peer_port); debug_msg("local ip %s local port %u peer ip %s peer port %u " "token %#x %s\n", local_ip.c_str(), XORP_UINT_CAST(local_port), peer_ip.c_str(), XORP_UINT_CAST(peer_port), XORP_UINT_CAST(token), more ? "more" : ""); return XrlCmdError::OKAY(); } XrlCmdError XrlBgpTarget::bgp_0_3_get_peer_id( // Input values, const string& local_ip, const uint32_t& local_port, const string& peer_ip, const uint32_t& peer_port, // Output values, IPv4& peer_id) { debug_msg("local ip %s local port %u peer ip %s peer port %u\n", local_ip.c_str(), XORP_UINT_CAST(local_port), peer_ip.c_str(), XORP_UINT_CAST(peer_port)); try { Iptuple iptuple("", local_ip.c_str(), local_port, peer_ip.c_str(), peer_port); if (!_bgp.get_peer_id(iptuple, peer_id)) { return XrlCmdError::COMMAND_FAILED(); } } catch(XorpException& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlBgpTarget::bgp_0_3_get_peer_status( // Input values, const string& local_ip, const uint32_t& local_port, const string& peer_ip, const uint32_t& peer_port, // Output values, uint32_t& peer_state, uint32_t& admin_status) { try { Iptuple iptuple("", local_ip.c_str(), local_port, peer_ip.c_str(), peer_port); if (!_bgp.get_peer_status(iptuple, peer_state, admin_status)) { return XrlCmdError::COMMAND_FAILED(); } } catch(XorpException& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlBgpTarget::bgp_0_3_get_peer_negotiated_version( // Input values, const string& local_ip, const uint32_t& local_port, const string& peer_ip, const uint32_t& peer_port, // Output values, int32_t& neg_version) { try { Iptuple iptuple("", local_ip.c_str(), local_port, peer_ip.c_str(), peer_port); if (!_bgp.get_peer_negotiated_version(iptuple, neg_version)) { return XrlCmdError::COMMAND_FAILED(); } } catch(XorpException& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlBgpTarget::bgp_0_3_get_peer_as( // Input values, const string& local_ip, const uint32_t& local_port, const string& peer_ip, const uint32_t& peer_port, // Output values, string& peer_as) { try { Iptuple iptuple("", local_ip.c_str(), local_port, peer_ip.c_str(), peer_port); uint32_t peer_asn; if (!_bgp.get_peer_as(iptuple, peer_asn)) { return XrlCmdError::COMMAND_FAILED(); } peer_as = AsNum(peer_asn).short_str(); } catch(XorpException& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlBgpTarget::bgp_0_3_get_peer_msg_stats( // Input values, const string& local_ip, const uint32_t& local_port, const string& peer_ip, const uint32_t& peer_port, // Output values, uint32_t& in_updates, uint32_t& out_updates, uint32_t& in_msgs, uint32_t& out_msgs, uint32_t& last_error, uint32_t& in_update_elapsed) { try { Iptuple iptuple("", local_ip.c_str(), local_port, peer_ip.c_str(), peer_port); uint16_t last_error_short; if (!_bgp.get_peer_msg_stats(iptuple, in_updates, out_updates, in_msgs, out_msgs, last_error_short, in_update_elapsed)) { return XrlCmdError::COMMAND_FAILED(); } last_error = last_error_short; } catch(XorpException& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlBgpTarget::bgp_0_3_get_peer_established_stats( // Input values, const string& local_ip, const uint32_t& local_port, const string& peer_ip, const uint32_t& peer_port, // Output values, uint32_t& transitions, uint32_t& established_time) { try { Iptuple iptuple("", local_ip.c_str(), local_port, peer_ip.c_str(), peer_port); if (!_bgp.get_peer_established_stats(iptuple, transitions, established_time)) { return XrlCmdError::COMMAND_FAILED(); } } catch(XorpException& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlBgpTarget::bgp_0_3_get_peer_timer_config( // Input values, const string& local_ip, const uint32_t& local_port, const string& peer_ip, const uint32_t& peer_port, // Output values, uint32_t& retry_interval, uint32_t& hold_time, uint32_t& keep_alive, uint32_t& hold_time_conf, uint32_t& keep_alive_conf, uint32_t& min_as_origin_interval, uint32_t& min_route_adv_interval) { try { Iptuple iptuple("", local_ip.c_str(), local_port, peer_ip.c_str(), peer_port); if (!_bgp.get_peer_timer_config(iptuple, retry_interval, hold_time, keep_alive, hold_time_conf, keep_alive_conf, min_as_origin_interval, min_route_adv_interval)) { return XrlCmdError::COMMAND_FAILED(); } } catch(XorpException& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlBgpTarget::bgp_0_3_register_rib( // Input values, const string& name) { debug_msg("%s\n", name.c_str()); if(!_bgp.register_ribname(name)) { return XrlCmdError::COMMAND_FAILED(c_format( "Couldn't register rib name %s", name.c_str())); } return XrlCmdError::OKAY(); } XrlCmdError XrlBgpTarget::bgp_0_3_get_v4_route_list_start( // Input values, const IPv4Net& prefix, const bool& unicast, const bool& multicast, // Output values, uint32_t& token) { debug_msg("\n"); if (_bgp.get_route_list_start(token, prefix, unicast, multicast)) { return XrlCmdError::OKAY(); } else { return XrlCmdError::COMMAND_FAILED(); } } XrlCmdError XrlBgpTarget::bgp_0_3_get_v4_route_list_next( // Input values, const uint32_t& token, // Output values, IPv4& peer_id, IPv4Net& net, uint32_t& best_and_origin, vector& aspath, IPv4& nexthop, int32_t& med, int32_t& localpref, int32_t& atomic_agg, vector& aggregator, int32_t& calc_localpref, vector& attr_unknown, bool& valid, bool& unicast, bool& multicast) { debug_msg("\n"); uint32_t origin; bool best = false; if (_bgp.get_route_list_next(token, peer_id, net, origin, aspath, nexthop, med, localpref, atomic_agg, aggregator, calc_localpref, attr_unknown, best, unicast, multicast)) { //trivial encoding to keep XRL arg count small enough if (best) { best_and_origin = (2 << 16) | origin; } else { best_and_origin = (1 << 16) | origin; } valid = true; } else { valid = false; } return XrlCmdError::OKAY(); } XrlCmdError XrlBgpTarget::rib_client_0_1_route_info_changed4( // Input values, const IPv4& addr, const uint32_t& prefix_len, const IPv4& nexthop, const uint32_t& metric, const uint32_t& admin_distance, const string& protocol_origin) { IPNet net(addr, prefix_len); debug_msg("IGP route into changed for net %s\n", net.str().c_str()); debug_msg("Nexthop: %s Metric: %u AdminDistance: %u ProtocolOrigin: %s\n", nexthop.str().c_str(), XORP_UINT_CAST(metric), XORP_UINT_CAST(admin_distance), protocol_origin.c_str()); // TODO: admin_distance and protocol_origin are not used if(!_bgp.rib_client_route_info_changed4(addr, prefix_len, nexthop, metric)) return XrlCmdError::COMMAND_FAILED(); return XrlCmdError::OKAY(); } XrlCmdError XrlBgpTarget::rib_client_0_1_route_info_invalid4( // Input values, const IPv4& addr, const uint32_t& prefix_len) { IPNet net(addr, prefix_len); debug_msg("IGP route into changed for net %s\n", net.str().c_str()); if(!_bgp.rib_client_route_info_invalid4(addr, prefix_len)) return XrlCmdError::COMMAND_FAILED(); return XrlCmdError::OKAY(); } XrlCmdError XrlBgpTarget::bgp_0_3_set_parameter( // Input values, const string& local_ip, const uint32_t& local_port, const string& peer_ip, const uint32_t& peer_port, const string& parameter, const bool& toggle) { debug_msg("local ip %s local port %u peer ip %s peer port %u" " parameter %s state %s\n", local_ip.c_str(), XORP_UINT_CAST(local_port), peer_ip.c_str(), XORP_UINT_CAST(peer_port), parameter.c_str(), toggle ? "set" : "unset"); try { Iptuple iptuple("", local_ip.c_str(), local_port, peer_ip.c_str(), peer_port); if(!_bgp.set_parameter(iptuple,parameter, toggle)) return XrlCmdError::COMMAND_FAILED(); } catch(XorpException& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlBgpTarget::finder_event_observer_0_1_xrl_target_birth( // Input values, const string& target_class, const string& target_instance) { debug_msg("birth class %s instance %s\n", target_class.c_str(), target_instance.c_str()); _bgp.notify_birth(target_class, target_instance); return XrlCmdError::OKAY(); } XrlCmdError XrlBgpTarget::finder_event_observer_0_1_xrl_target_death( // Input values, const string& target_class, const string& target_instance) { debug_msg("death class %s instance %s\n", target_class.c_str(), target_instance.c_str()); _bgp.notify_death(target_class, target_instance); return XrlCmdError::OKAY(); } XrlCmdError XrlBgpTarget::policy_backend_0_1_configure(const uint32_t& filter, const string& conf) { try { debug_msg("[BGP] policy filter: %d conf: %s\n", filter, conf.c_str()); PROFILE(XLOG_TRACE(_bgp.profile().enabled(trace_policy_configure), "policy filter: %d conf: %s\n", filter, conf.c_str())); _bgp.configure_filter(filter,conf); } catch(const PolicyException& e) { return XrlCmdError::COMMAND_FAILED("Filter configure failed: " + e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlBgpTarget::policy_backend_0_1_reset(const uint32_t& filter) { try { debug_msg("[BGP] policy reset: %d\n", filter); PROFILE(XLOG_TRACE(_bgp.profile().enabled(trace_policy_configure), "policy filter: %d\n", filter)); _bgp.reset_filter(filter); } catch(const PolicyException& e){ return XrlCmdError::COMMAND_FAILED("Filter reset failed: " + e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlBgpTarget::policy_backend_0_1_push_routes() { debug_msg("[BGP] Route Push\n"); _bgp.push_routes(); return XrlCmdError::OKAY(); } XrlCmdError XrlBgpTarget::policy_redist4_0_1_add_route4( const IPv4Net& network, const bool& unicast, const bool& multicast, const IPv4& nexthop, const uint32_t& metric, const XrlAtomList& policytags) { UNUSED(metric); // // XXX: Accept the multicast routes, otherwise we cannot originate // NLRI for multicast purpose. // _bgp.originate_route(network,nexthop,unicast,multicast,policytags); return XrlCmdError::OKAY(); } XrlCmdError XrlBgpTarget::policy_redist4_0_1_delete_route4( const IPv4Net& network, const bool& unicast, const bool& multicast) { // // XXX: Accept the multicast routes, otherwise we cannot originate // NLRI for multicast purpose. // _bgp.withdraw_route(network,unicast,multicast); return XrlCmdError::OKAY(); } #ifndef XORP_DISABLE_PROFILE XrlCmdError XrlBgpTarget::profile_0_1_enable(const string& pname) { debug_msg("profile variable %s\n", pname.c_str()); try { _bgp.profile().enable(pname); } catch(PVariableUnknown& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } catch(PVariableLocked& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlBgpTarget::profile_0_1_disable(const string& pname) { debug_msg("profile variable %s\n", pname.c_str()); try { _bgp.profile().disable(pname); } catch(PVariableUnknown& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlBgpTarget::profile_0_1_get_entries(const string& pname, const string& instance_name) { debug_msg("profile variable %s instance %s\n", pname.c_str(), instance_name.c_str()); // Lock and initialize. try { _bgp.profile().lock_log(pname); } catch(PVariableUnknown& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } catch(PVariableLocked& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } ProfileUtils::transmit_log(pname, _bgp.get_router(), instance_name, &_bgp.profile()); return XrlCmdError::OKAY(); } XrlCmdError XrlBgpTarget::profile_0_1_clear(const string& pname) { debug_msg("profile variable %s\n", pname.c_str()); try { _bgp.profile().clear(pname); } catch(PVariableUnknown& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } catch(PVariableLocked& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlBgpTarget::profile_0_1_list(string& info) { debug_msg("\n"); info = _bgp.profile().get_list(); return XrlCmdError::OKAY(); } #endif bool XrlBgpTarget::waiting() { return _awaiting_config; } bool XrlBgpTarget::done() { return _done; } #ifdef HAVE_IPV6 /** IPv6 stuff */ XrlCmdError XrlBgpTarget::bgp_0_3_set_nexthop6( // Input values, const string& local_ip, const uint32_t& local_port, const string& peer_ip, const uint32_t& peer_port, const IPv6& next_hop) { debug_msg("local ip %s local port %u peer ip %s peer port %u " " next-hop %s\n", local_ip.c_str(), XORP_UINT_CAST(local_port), peer_ip.c_str(), XORP_UINT_CAST(peer_port), cstring(next_hop)); try { Iptuple iptuple("", local_ip.c_str(), local_port, peer_ip.c_str(), peer_port); if(!_bgp.set_nexthop6(iptuple, next_hop)) return XrlCmdError::COMMAND_FAILED(); } catch(XorpException& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlBgpTarget::bgp_0_3_get_nexthop6( // Input values, const string& local_ip, const uint32_t& local_port, const string& peer_ip, const uint32_t& peer_port, IPv6& next_hop) { debug_msg("local ip %s local port %u peer ip %s peer port %u\n", local_ip.c_str(), XORP_UINT_CAST(local_port), peer_ip.c_str(), XORP_UINT_CAST(peer_port)); try { Iptuple iptuple("", local_ip.c_str(), local_port, peer_ip.c_str(), peer_port); if(!_bgp.get_nexthop6(iptuple, next_hop)) return XrlCmdError::COMMAND_FAILED(); } catch(XorpException& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlBgpTarget::bgp_0_3_originate_route6( // Input values, const IPv6Net& nlri, const IPv6& next_hop, const bool& unicast, const bool& multicast) { debug_msg("nlri %s next hop %s unicast %d multicast %d\n", nlri.str().c_str(), next_hop.str().c_str(), unicast, multicast); if (!_bgp.originate_route(nlri, next_hop, unicast, multicast,PolicyTags())) return XrlCmdError::COMMAND_FAILED(); return XrlCmdError::OKAY(); } XrlCmdError XrlBgpTarget::bgp_0_3_withdraw_route6( // Input values, const IPv6Net& nlri, const bool& unicast, const bool& multicast) { debug_msg("nlri %s unicast %d multicast %d\n", nlri.str().c_str(), unicast, multicast); if (!_bgp.withdraw_route(nlri, unicast, multicast)) return XrlCmdError::COMMAND_FAILED(); return XrlCmdError::OKAY(); } XrlCmdError XrlBgpTarget::bgp_0_3_get_v6_route_list_start( // Input values, const IPv6Net& prefix, const bool& unicast, const bool& multicast, // Output values, uint32_t& token) { if (_bgp.get_route_list_start(token, prefix, unicast, multicast)) { return XrlCmdError::OKAY(); } else { return XrlCmdError::COMMAND_FAILED(); } } XrlCmdError XrlBgpTarget::bgp_0_3_get_v6_route_list_next( // Input values, const uint32_t& token, // Output values, IPv4& peer_id, IPv6Net& net, uint32_t& best_and_origin, vector& aspath, IPv6& nexthop, int32_t& med, int32_t& localpref, int32_t& atomic_agg, vector& aggregator, int32_t& calc_localpref, vector& attr_unknown, bool& valid, bool& unicast, bool& multicast) { debug_msg("\n"); uint32_t origin; bool best; if (_bgp.get_route_list_next(token, peer_id, net, origin, aspath, nexthop, med, localpref, atomic_agg, aggregator, calc_localpref, attr_unknown, best, unicast, multicast)) { //trivial encoding to keep XRL arg count small enough if (best) { best_and_origin = (2 << 16) | origin; } else { best_and_origin = (1 << 16) | origin; } valid = true; } else { valid = false; } return XrlCmdError::OKAY(); } XrlCmdError XrlBgpTarget::rib_client_0_1_route_info_changed6( // Input values, const IPv6& addr, const uint32_t& prefix_len, const IPv6& nexthop, const uint32_t& metric, const uint32_t& admin_distance, const string& protocol_origin) { IPNet net(addr, prefix_len); debug_msg("IGP route into changed for net %s\n", net.str().c_str()); debug_msg("Nexthop: %s Metric: %u AdminDistance: %u ProtocolOrigin: %s\n", nexthop.str().c_str(), XORP_UINT_CAST(metric), XORP_UINT_CAST(admin_distance), protocol_origin.c_str()); // TODO: admin_distance and protocol_origin are not used if(!_bgp.rib_client_route_info_changed6(addr, prefix_len, nexthop, metric)) return XrlCmdError::COMMAND_FAILED(); return XrlCmdError::OKAY(); } XrlCmdError XrlBgpTarget::rib_client_0_1_route_info_invalid6( // Input values, const IPv6& addr, const uint32_t& prefix_len) { IPNet net(addr, prefix_len); debug_msg("IGP route into changed for net %s\n", net.str().c_str()); if(!_bgp.rib_client_route_info_invalid6(addr, prefix_len)) return XrlCmdError::COMMAND_FAILED(); return XrlCmdError::OKAY(); } XrlCmdError XrlBgpTarget::policy_redist6_0_1_add_route6( const IPv6Net& network, const bool& unicast, const bool& multicast, const IPv6& nexthop, const uint32_t& metric, const XrlAtomList& policytags) { UNUSED(metric); // // XXX: Accept the multicast routes, otherwise we cannot originate // NLRI for multicast purpose. // _bgp.originate_route(network,nexthop,unicast,multicast,policytags); return XrlCmdError::OKAY(); } XrlCmdError XrlBgpTarget::policy_redist6_0_1_delete_route6( const IPv6Net& network, const bool& unicast, const bool& multicast) { // // XXX: Accept the multicast routes, otherwise we cannot originate // NLRI for multicast purpose. // _bgp.withdraw_route(network,unicast,multicast); return XrlCmdError::OKAY(); } #endif //ipv6 xorp/bgp/route_table_deletion.hh0000664000076400007640000001032011421137511017131 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/bgp/route_table_deletion.hh,v 1.25 2008/11/08 06:14:39 mjh Exp $ #ifndef __BGP_ROUTE_TABLE_DELETION_HH__ #define __BGP_ROUTE_TABLE_DELETION_HH__ #include "route_table_base.hh" #include "peer_handler.hh" #include "bgp_trie.hh" #include "crash_dump.hh" class EventLoop; /** * @short DeletionTable is a temporary BGPRouteTable used to delete * routes when a peer goes down * * When a peer goes down, all the routes stored in a RibIn need to be * deleted. However, this can take some time, so it cannot occur in * one atomic operation, so it must be done route-by-route as a * background task. This is complicated by the fact that the peering * may come back up while this background deletion is occuring, and * new routes may appear. To handle the background deletion while * keeping the RibIn simple, we simply create a new DeletionTable * route table, plumb it in directly after the RibIn, and pass the * RibIn's entire route trie to the DeletionTable. RibIn can now * forget these routes ever existed, and DeletionTable can get on with * the background deletion task, unplumbing and deleting itself when * no routes remain. * * Care must be taken to ensure that the downstream routing tables see * consistent information. For example, if there is a route for * subnet X in the DeletionTable that has not yet been deleted, and an * add_route for X comes downstream from rthe RibIn, then this would * need to be propagated downstream as a replace_route. * * Note that if a peering flaps multiple times, multiple * DeletionTables may be plumbed in, one after another, behind a * RibInTable. */ template class DeletionTable : public BGPRouteTable, CrashDumper { public: DeletionTable(string tablename, Safi safi, BgpTrie* route_table, const PeerHandler *peer, uint32_t genid, BGPRouteTable *parent); ~DeletionTable(); int add_route(InternalMessage &rtmsg, BGPRouteTable *caller); int replace_route(InternalMessage &old_rtmsg, InternalMessage &new_rtmsg, BGPRouteTable *caller); int delete_route(InternalMessage &rtmsg, BGPRouteTable *caller); int route_dump(InternalMessage &rtmsg, BGPRouteTable *caller, const PeerHandler *dump_peer); int push(BGPRouteTable *caller); const SubnetRoute *lookup_route(const IPNet &net, uint32_t& genid, FPAListRef& pa_list) const; void route_used(const SubnetRoute* route, bool in_use); RouteTableType type() const { return DELETION_TABLE; } string str() const; /* mechanisms to implement flow control in the output plumbing */ void output_state(bool /*busy*/, BGPRouteTable */*next_table*/) { abort(); } bool get_next_message(BGPRouteTable */*next_table*/) { abort(); return false; } void initiate_background_deletion(); /** * @return the generation id. */ uint32_t genid() const {return _genid;} string dump_state() const; private: void unplumb_self(); bool delete_next_chain(); EventLoop& eventloop() const { return _peer->eventloop(); } const PeerHandler *_peer; uint32_t _genid; BgpTrie* _route_table; typename BgpTrie::PathmapType::const_iterator _del_sweep; int _deleted, _chains; XorpTask _deletion_task; }; #endif // __BGP_ROUTE_TABLE_DELETION_HH__ xorp/bgp/process_watch.hh0000664000076400007640000000546411421137511015622 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/bgp/process_watch.hh,v 1.16 2008/10/02 21:56:18 bms Exp $ #ifndef __BGP_PROCESS_WATCH_HH__ #define __BGP_PROCESS_WATCH_HH__ #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/eventloop.hh" #include "libxorp/timer.hh" #include "libxipc/xrl_std_router.hh" class ProcessWatch { public: typedef XorpCallback0::RefPtr TerminateCallback; ProcessWatch(XrlStdRouter *xrl_router, EventLoop& eventloop, const char *bgp_mib_name, TerminateCallback cb); /** * Method to call when the birth of a process has been detected. */ void birth(const string& target_class, const string& target_instance); /** * Method to call when the death of a process has been detected. */ void death(const string& target_class, const string& target_instance); /** * Method to call if the finder dies. */ void finder_death(const char *file, const int lineno); /** * Start a timer to kill this process if for some reason we get * hung up. */ void start_kill_timer(); /** * @return Return true when all the processes that BGP requires * for correct operation are running. */ bool ready() const; /** * @return true if the target process exists. */ bool target_exists(const string& target) const; protected: void interest_callback(const XrlError& error); void add_target(const string& target_class, const string& target_instance); void remove_target(const string& target_class, const string& target_instance); private: EventLoop& _eventloop; TerminateCallback _shutdown; bool _fea; bool _rib; string _fea_instance; string _rib_instance; XorpTimer _shutdown_timer; class Process { public: Process(string c, string i) : _target_class(c), _target_instance(i) {} string _target_class; string _target_instance; }; list _processes; }; #endif // __BGP_PROCESS_WATCH_HH__ xorp/bgp/peer_handler.hh0000664000076400007640000001556211540225521015407 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/bgp/peer_handler.hh,v 1.31 2008/11/08 06:14:37 mjh Exp $ #ifndef __BGP_PEER_HANDLER_HH__ #define __BGP_PEER_HANDLER_HH__ class BGPMain; class BGPPeer; class BGPPlumbing; #include "libxorp/debug.h" #include "internal_message.hh" #include "packet.hh" #include "peer.hh" #include "parameter.hh" /** * PeerHandler's job is primarily format conversion. But it also * servers as a handle to tie together the input and output sides of a * RIB. */ class PeerHandler { public: PeerHandler(const string &peername, BGPPeer *peer, BGPPlumbing *plumbing_unicast, BGPPlumbing *plumbing_multicast); virtual ~PeerHandler(); void stop(); void peering_went_down(); void peering_came_up(); bool peering_is_up() const { return _peering_is_up; } /** * process_update_packet is called when an update packet has been * received by this peer */ int process_update_packet(UpdatePacket *p); /** * Given an update packet find all the NLRIs with * specified and inject one by one into the plumbing. * * @param p - packet to tease apart * @param safi - Subsequent address family identifier * * @return true if an was found. */ template bool add(const UpdatePacket *p, ref_ptr >& original_pa_list, ref_ptr >& pa_list, Safi safi); /** * Given an update packet find all the WITHDRAWs with * specified and inject one by on into the plumbing. * * @param p - packet to tease apart * @param safi - Subsequent address family identifier * * @return true if an was found. */ template bool withdraw(const UpdatePacket *p, ref_ptr >& original_pa_list, Safi safi); template bool multiprotocol(Safi safi, BGPPeerData::Direction d) const; /** * add_route and delete_route are called by the plumbing to * propagate a route *to* the peer. */ virtual int start_packet(); virtual int add_route(const SubnetRoute &rt, FPAList4Ref& pa_list, bool ibgp, Safi safi); virtual int replace_route(const SubnetRoute &old_rt, bool old_ibgp, const SubnetRoute &new_rt, bool new_ibgp, FPAList4Ref& pa_list, Safi safi); virtual int delete_route(const SubnetRoute &rt, FPAList4Ref& pa_list, bool new_ibgp, Safi safi); virtual PeerOutputState push_packet(); virtual void output_no_longer_busy(); /** * The AS number of this router. */ AsNum my_AS_number() const { return _peer->peerdata()->my_AS_number(); } /** * The AS number of the peer router. */ AsNum AS_number() const { return _peer->peerdata()->as(); } /** * Do we use 4-byte AS numbers with this peer? */ bool use_4byte_asnums() const { return _peer->peerdata()->use_4byte_asnums(); } virtual PeerType get_peer_type() const { return _peer->peerdata()->get_peer_type(); } const string& peername() const { return _peername; } bool ibgp() const { if (0 == _peer) { XLOG_ASSERT(originate_route_handler()); return false; } return _peer->peerdata()->ibgp(); } /** * @return true if this is the originate route handler. */ virtual bool originate_route_handler() const {return false;} /** * @return an ID that is unique per peer for use in decision. */ virtual uint32_t get_unique_id() const { return _peer->get_unique_id(); } /** * @return the neighbours BGP ID as an integer for use by decision. */ virtual const IPv4& id() const { return _peer->peerdata()->id(); } /** * @return the neighbours IP address an as integer for use by * decision. */ uint32_t neighbour_address() const { //return ntohl(_peer->peerdata()->iptuple().get_peer_addr().s_addr); return id().addr(); } const IPv4& my_v4_nexthop() const { return _peer->peerdata()->get_v4_local_addr(); } /** * Get the local address as a string in numeric form. */ string get_local_addr() const { return _peer->peerdata()->iptuple().get_local_addr(); } /** * Get the peer address as a string in numeric form. */ string get_peer_addr() const { return _peer->peerdata()->iptuple().get_peer_addr(); } /** * @param addr fill in the address if this is IPv4. * @return true if the peer address is IPv4. */ bool get_peer_addr(IPv4& addr) const { return _peer->peerdata()->iptuple().get_peer_addr(addr); } /** * @return the number of prefixes in the RIB-IN. */ uint32_t get_prefix_count() const; virtual EventLoop& eventloop() const; #ifdef HAVE_IPV6 virtual int add_route(const SubnetRoute &rt, FPAList6Ref& pa_list, bool ibgp, Safi safi); virtual int replace_route(const SubnetRoute &old_rt, bool old_ibgp, const SubnetRoute &new_rt, bool new_ibgp, FPAList6Ref& pa_list, Safi safi); virtual int delete_route(const SubnetRoute &rt, FPAList6Ref& pa_list, bool new_ibgp, Safi safi); const IPv6& my_v6_nexthop() const { return _peer->peerdata()->get_v6_local_addr(); } /** * @param addr fill in the address if this is IPv6. * @return true if the peer address is IPv6. */ bool get_peer_addr(IPv6& addr) const { return _peer->peerdata()->iptuple().get_peer_addr(addr); } #endif //ipv6 protected: BGPPlumbing *_plumbing_unicast; BGPPlumbing *_plumbing_multicast; private: string _peername; BGPPeer *_peer; bool _peering_is_up; /*whether we still think it's up (it may be down, but the FSM hasn't told us yet) */ UpdatePacket *_packet; /* this is a packet we construct to send */ /*stats*/ uint32_t _nlri_total; uint32_t _packets; }; #endif // __BGP_PEER_HANDLER_HH__ xorp/bgp/peer_handler.cc0000664000076400007640000005606211540225521015375 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #include "bgp_module.h" #include "libxorp/xlog.h" #include "peer_handler.hh" #include "plumbing.hh" #include "packet.hh" #include "bgp.hh" PeerHandler::PeerHandler(const string &init_peername, BGPPeer *peer, BGPPlumbing *plumbing_unicast, BGPPlumbing *plumbing_multicast) : _plumbing_unicast(plumbing_unicast), _plumbing_multicast(plumbing_multicast), _peername(init_peername), _peer(peer), _packet(NULL) { debug_msg("peername: %s peer %p unicast %p multicast %p\n", _peername.c_str(), peer, _plumbing_unicast, _plumbing_multicast); if (_plumbing_unicast != NULL) _plumbing_unicast->add_peering(this); if (_plumbing_multicast != NULL) _plumbing_multicast->add_peering(this); _peering_is_up = true; // stats for debugging only _nlri_total = 0; _packets = 0; } /* The typical action is only to call peering_went_down when a peering goes down. In the case of BGP itself going down, we typically call stop on all the peerhandlers, then call peering_went_down on all the peerhandlers, then wait for all the peerhandlers to indicate idle, then call the destructors on the peerhandlers. In the case of de-configuring a peering (as opposed to it going down temporarily, we would call peering_went_down, then wait for the peerhandler to indicate idle, and finally call the destructor on the peerhandler. */ PeerHandler::~PeerHandler() { debug_msg("PeerHandler destructor called\n"); if (_plumbing_unicast != NULL) _plumbing_unicast->delete_peering(this); if (_plumbing_multicast != NULL) _plumbing_multicast->delete_peering(this); if (_packet != NULL) delete _packet; } void PeerHandler::stop() { debug_msg("PeerHandler STOP!\n"); _plumbing_unicast->stop_peering(this); _plumbing_multicast->stop_peering(this); } void PeerHandler::peering_went_down() { _peering_is_up = false; _plumbing_unicast->peering_went_down(this); _plumbing_multicast->peering_went_down(this); } void PeerHandler::peering_came_up() { _peering_is_up = true; _plumbing_unicast->peering_came_up(this); _plumbing_multicast->peering_came_up(this); } template <> bool PeerHandler::add(const UpdatePacket *p, ref_ptr >& original_pa_list, ref_ptr >& pa_list, Safi safi) { UNUSED(original_pa_list); XLOG_ASSERT(!pa_list->is_locked()); switch(safi) { case SAFI_UNICAST: { if (p->nlri_list().empty()) return false; XLOG_ASSERT(pa_list->complete()); BGPUpdateAttribList::const_iterator ni4; ni4 = p->nlri_list().begin(); int count = p->nlri_list().size(); while (ni4 != p->nlri_list().end()) { if (!ni4->net().is_unicast()) { XLOG_ERROR("NLRI <%s> is not semantically correct ignoring.%s", cstring(ni4->net()), cstring(*p)); ++ni4; continue; } PolicyTags policy_tags; FPAList4Ref fpalist; if (count == 1) { // no need to copy if there's only one fpalist = pa_list; } else { // need to copy the others, or each one's changes will affect the rest fpalist = new FastPathAttributeList(*pa_list); } XLOG_ASSERT(!fpalist->is_locked()); _plumbing_unicast->add_route(ni4->net(), fpalist, policy_tags, this); ++ni4; } } break; case SAFI_MULTICAST: { const MPReachNLRIAttribute *mpreach = pa_list->mpreach(safi); if(!mpreach) return false; XLOG_ASSERT(pa_list->complete()); list >::const_iterator ni; int count = mpreach->nlri_list().size(); ni = mpreach->nlri_list().begin(); while (ni != mpreach->nlri_list().end()) { if (!ni->is_unicast()) { XLOG_ERROR("NLRI <%s> is not semantically correct ignoring.%s", cstring(*ni), cstring(*p)); ++ni; continue; } PolicyTags policy_tags; FPAList4Ref fpalist; if (count == 1) { // no need to copy fpalist = pa_list; } else { // need to copy the others, or each one's changes will // affect the rest fpalist = new FastPathAttributeList(*pa_list); } // we're done with this attribute now. fpalist->remove_attribute_by_type(MP_REACH_NLRI); _plumbing_multicast->add_route(*ni, fpalist, policy_tags, this); ++ni; } } break; } return true; } #ifdef HAVE_IPV6 template <> bool PeerHandler::add(const UpdatePacket *p, ref_ptr >& original_pa_list, ref_ptr >& pa_list, Safi safi) { UNUSED(original_pa_list); UNUSED(p); const MPReachNLRIAttribute *mpreach = pa_list->mpreach(safi); if(!mpreach) return false; XLOG_ASSERT(pa_list->complete()); list >::const_iterator ni; int nlris_remaining = mpreach->nlri_list().size(); ni = mpreach->nlri_list().begin(); while (nlris_remaining > 0) { IPNet n = *ni; if (!n.is_unicast()) { XLOG_ERROR("NLRI <%s> is not semantically correct ignoring.%s", cstring(*ni), cstring(*p)); ++ni; continue; } PolicyTags policy_tags; FPAList6Ref fpalist; if (mpreach->nlri_list().size() == 1) { // no need to copy fpalist = pa_list; } else { // need to copy the others, or each one's changes will affect the rest fpalist = new FastPathAttributeList(*pa_list); } // we're done with this attribute now. fpalist->remove_attribute_by_type(MP_REACH_NLRI); switch(safi) { case SAFI_UNICAST: _plumbing_unicast->add_route(n, fpalist, policy_tags, this); break; case SAFI_MULTICAST: _plumbing_multicast->add_route(n, fpalist, policy_tags, this); break; } nlris_remaining--; if (nlris_remaining > 0) { // only do this if there are more nlris, because otherwise // the iterator will be invalid ++ni; } } return true; } template <> bool PeerHandler::withdraw(const UpdatePacket *p, ref_ptr >& original_pa_list, Safi safi) { UNUSED(p); const MPUNReachNLRIAttribute *mpunreach = original_pa_list->mpunreach(safi); if (!mpunreach) return false; list >::const_iterator wi; wi = mpunreach->wr_list().begin(); while (wi != mpunreach->wr_list().end()) { switch(safi) { case SAFI_UNICAST: _plumbing_unicast->delete_route(*wi, this); break; case SAFI_MULTICAST: _plumbing_multicast->delete_route(*wi, this); break; } ++wi; } return true; } #endif template <> bool PeerHandler::withdraw(const UpdatePacket *p, ref_ptr >& original_pa_list, Safi safi) { switch(safi) { case SAFI_UNICAST: { if (p->wr_list().empty()) return false; BGPUpdateAttribList::const_iterator wi; wi = p->wr_list().begin(); while (wi != p->wr_list().end()) { _plumbing_unicast->delete_route(wi->net(), this); ++wi; } } break; case SAFI_MULTICAST: { const MPUNReachNLRIAttribute *mpunreach = original_pa_list->mpunreach(safi); if (!mpunreach) return false; list >::const_iterator wi; wi = mpunreach->wr_list().begin(); while (wi != mpunreach->wr_list().end()) { _plumbing_multicast->delete_route(*wi, this); ++wi; } } break; } return true; } inline void set_if_true(bool& orig, bool now) { if (now) orig = true; } /* * process_update_packet is called when we've received an Update * message from the peer. We break down the update into its pieces, * and pass the information into the plumbing to be stored in the * RibIn and distributed as required. */ int PeerHandler::process_update_packet(UpdatePacket *p) { debug_msg("Processing packet\n %s\n", p->str().c_str()); FPAList4Ref pa_list = p->pa_list(); FPAList4Ref pa_ipv4_unicast = new FastPathAttributeList(); FPAList4Ref pa_ipv4_multicast = new FastPathAttributeList(); #ifdef HAVE_IPV6 FPAList6Ref pa_ipv6_unicast = new FastPathAttributeList(); FPAList6Ref pa_ipv6_multicast = new FastPathAttributeList(); bool ipv6_unicast = false; bool ipv6_multicast = false; #endif XLOG_ASSERT(!pa_ipv4_unicast->is_locked()); bool ipv4_unicast = false; bool ipv4_multicast = false; const PathAttribute* pa; // Store a reference to the ASPath here temporarily, as we may // need to mess with it before passing it to the final PA lists. // It's safe to mess with the ASPath in place, as we won't need // the original after this. ASPath* as_path = 0; if (!pa_list->is_empty()) { if (pa_list->aspath_att()) as_path = const_cast(&(pa_list->aspath())); for (int i = 0; i < pa_list->max_att(); i++) { pa = pa_list->find_attribute_by_type((PathAttType)i); if (pa) { switch((PathAttType)i) { case AS_PATH: // add this later. continue; case AS4_PATH: if (_peer->use_4byte_asnums()) { // we got an AS4_PATH from a peer that shouldn't send // us one because it speaks native 4-byte AS numbers. // The spec says to silently discard the attribute. continue; } else if (_peer->localdata()->use_4byte_asnums()) { // We got an AS4_PATH, but we're configured to use // 4-byte AS numbers. We need to merge this into the // AS_PATH we've already decoded, then discard the // AS4_PATH as we don't need both. const AS4PathAttribute* as4attr = (const AS4PathAttribute*)(pa_list->as4path_att()); XLOG_ASSERT(as_path); as_path->merge_as4_path(as4attr->as4_path()); /* don't store the AS4path in the PA list */ continue; } else { // We got an AS4_PATH, but we're not configured to use // 4-byte AS numbers. We just propagate this // unchanged, so fall through and add it to the // pa_list in the normal way. } break; case MP_REACH_NLRI: #ifdef HAVE_IPV6 // we've got a multiprotocol NLRI to split out if (dynamic_cast*>(pa)) { // it's IPv6 const MPReachNLRIAttribute* mpreach = dynamic_cast*>(pa); switch(mpreach->safi()) { case SAFI_UNICAST: { IPv6NextHopAttribute nha(mpreach->nexthop()); pa_ipv6_unicast->add_path_attribute(nha); pa_ipv6_unicast->add_path_attribute(*pa); break; } case SAFI_MULTICAST: { IPv6NextHopAttribute nha(mpreach->nexthop()); pa_ipv6_multicast->add_path_attribute(nha); pa_ipv6_multicast->add_path_attribute(*pa); break; } } } #endif if (dynamic_cast*>(pa)) { // it's IPv4 const MPReachNLRIAttribute* mpreach = dynamic_cast*>(pa); switch(mpreach->safi()) { case SAFI_UNICAST: // XXXX what should we do here? XLOG_WARNING("AFI == IPv4 && SAFI == UNICAST???"); //pa_ipv4_unicast. //add_path_attribute(IPv4NextHopAttribute(mpreach->nexthop())); break; case SAFI_MULTICAST: { IPv4NextHopAttribute nha(mpreach->nexthop()); pa_ipv4_multicast->add_path_attribute(nha); pa_ipv4_multicast->add_path_attribute(*pa); break; } } } continue; case MP_UNREACH_NLRI: #ifdef HAVE_IPV6 if (dynamic_cast*>(pa)) { /* don't store this in the PA list */ continue; } #endif if (dynamic_cast*>(pa)) { /* don't store this in the PA list */ continue; } default: /* the remaining attributes are handled normally below */ {} } /* end of switch */ pa_ipv4_unicast->add_path_attribute(*pa); /* ** The nexthop path attribute applies only to IPv4 Unicast case. */ if (NEXT_HOP != pa->type()) { pa_ipv4_multicast->add_path_attribute(*pa); #ifdef HAVE_IPV6 pa_ipv6_unicast->add_path_attribute(*pa); pa_ipv6_multicast->add_path_attribute(*pa); #endif } } /* end of if */ } /* end of for loop */ } /* end of if pa_list */ /* finally store the ASPath attribute, now we know we're done messing with it */ if (as_path) { ASPathAttribute as_path_attr(*as_path); pa_ipv4_unicast->add_path_attribute(as_path_attr); pa_ipv4_multicast->add_path_attribute(as_path_attr); #ifdef HAVE_IPV6 pa_ipv6_unicast->add_path_attribute(as_path_attr); pa_ipv6_multicast->add_path_attribute(as_path_attr); #endif } // IPv4 Unicast Withdraws set_if_true(ipv4_unicast, withdraw(p, pa_list, SAFI_UNICAST)); // IPv4 Multicast Withdraws set_if_true(ipv4_multicast, withdraw(p, pa_list, SAFI_MULTICAST)); #ifdef HAVE_IPV6 // IPv6 Unicast Withdraws set_if_true(ipv6_unicast, withdraw(p, pa_list, SAFI_UNICAST)); // IPv6 Multicast Withdraws set_if_true(ipv6_multicast, withdraw(p, pa_list, SAFI_MULTICAST)); #endif // IPv4 Unicast Route add. XLOG_ASSERT(!pa_ipv4_unicast->is_locked()); set_if_true(ipv4_unicast, add(p, pa_list, pa_ipv4_unicast, SAFI_UNICAST)); // IPv4 Multicast Route add. set_if_true(ipv4_multicast, add(p, pa_list, pa_ipv4_multicast,SAFI_MULTICAST)); #ifdef HAVE_IPV6 // IPv6 Unicast Route add. set_if_true(ipv6_unicast, add(p, pa_list, pa_ipv6_unicast, SAFI_UNICAST)); // IPv6 Multicast Route add. set_if_true(ipv6_multicast, add(p, pa_list, pa_ipv6_multicast,SAFI_MULTICAST)); #endif if (ipv4_unicast) _plumbing_unicast->push(this); if (ipv4_multicast) _plumbing_multicast->push(this); #ifdef HAVE_IPV6 if (ipv6_unicast) _plumbing_unicast->push(this); if (ipv6_multicast) _plumbing_multicast->push(this); #endif return 0; } template <> bool PeerHandler::multiprotocol(Safi safi, BGPPeerData::Direction d) const { return _peer->peerdata()->multiprotocol(safi, d); } int PeerHandler::start_packet() { XLOG_ASSERT(_packet == NULL); _packet = new UpdatePacket(); return 0; } int PeerHandler::add_route(const SubnetRoute &rt, ref_ptr >& pa_list, bool /*ibgp*/, Safi safi) { debug_msg("PeerHandler::add_route(IPv4) %p\n", &rt); XLOG_ASSERT(_packet != NULL); // if a route came from IBGP, it shouldn't go to IBGP (unless // we're a route reflector) // if (ibgp) // XLOG_ASSERT(!_peer->ibgp()); // Check this peer wants this NLRI if (!multiprotocol(safi, BGPPeerData::NEGOTIATED)) return 0; if (_packet->big_enough()) { push_packet(); start_packet(); } // did we already add the packet attribute list? if (_packet->pa_list()->is_empty()) { debug_msg("First add on this packet\n"); debug_msg("SubnetRoute is %s\n", rt.str().c_str()); // no, so add all the path attributes _packet->replace_pathattribute_list(pa_list); #if 0 PathAttributeList::const_iterator pai; pai = rt.attributes()->begin(); while (pai != rt.attributes()->end()) { debug_msg("Adding attribute %s\n", (*pai)->str().c_str()); /* ** Don't put a multicast nexthop in the parameter list. */ if (!(NEXT_HOP == (*pai)->type() && safi == SAFI_MULTICAST)) _packet.add_pathatt(**pai); ++pai; } #endif if (SAFI_MULTICAST == safi) { // Multicast SAFI packets don't have a regular nexthop, // but have on in the MultiProtocol attribute instead. _packet->pa_list()->remove_attribute_by_type(NEXT_HOP); MPReachNLRIAttribute mp(safi); mp.set_nexthop(pa_list->nexthop()); _packet->add_pathatt(mp); } } // add the NLRI information. switch(safi) { case SAFI_UNICAST: { BGPUpdateAttrib nlri(rt.net()); XLOG_ASSERT(_packet->pa_list()->nexthop() == pa_list->nexthop()); _packet->add_nlri(nlri); } break; case SAFI_MULTICAST: { XLOG_ASSERT(pa_list->mpreach(SAFI_MULTICAST)); XLOG_ASSERT(pa_list->mpreach(SAFI_MULTICAST)->nexthop() == pa_list->nexthop()); MPReachNLRIAttribute* mpreach_att = _packet->pa_list()->mpreach(SAFI_MULTICAST); XLOG_ASSERT(mpreach_att); mpreach_att->add_nlri(rt.net()); } break; } return 0; } int PeerHandler::replace_route(const SubnetRoute &old_rt, bool /*old_ibgp*/, const SubnetRoute &new_rt, bool new_ibgp, FPAList4Ref& pa_list, Safi safi) { // in the basic PeerHandler, we can ignore the old route, because // to change a route, the BGP protocol just sends the new // replacement route UNUSED(old_rt); return add_route(new_rt, pa_list, new_ibgp, safi); } int PeerHandler::delete_route(const SubnetRoute &rt, FPAList4Ref& /*pa_list*/, bool /*ibgp*/, Safi safi) { debug_msg("PeerHandler::delete_route(IPv4) %p\n", &rt); XLOG_ASSERT(_packet != NULL); // Check this peer wants this NLRI if (!multiprotocol(safi, BGPPeerData::NEGOTIATED)) return 0; if (_packet->big_enough()) { push_packet(); start_packet(); } if (SAFI_MULTICAST == safi && 0 == _packet->pa_list()->mpunreach(safi)) { MPUNReachNLRIAttribute* mp = new MPUNReachNLRIAttribute(safi); _packet->pa_list()->add_path_attribute(mp); } switch(safi) { case SAFI_UNICAST: { BGPUpdateAttrib wdr(rt.net()); _packet->add_withdrawn(wdr); } break; case SAFI_MULTICAST: { XLOG_ASSERT(_packet->pa_list()->mpunreach(SAFI_MULTICAST)); _packet->pa_list()->mpunreach(SAFI_MULTICAST)->add_withdrawn(rt.net()); } break; } return 0; } PeerOutputState PeerHandler::push_packet() { debug_msg("PeerHandler::push_packet - sending packet:\n %s\n", _packet->str().c_str()); // do some sanity checking XLOG_ASSERT(_packet); int wdr = _packet->wr_list().size(); int nlri = _packet->nlri_list().size(); if(_packet->pa_list()->mpreach(SAFI_MULTICAST)) nlri += _packet->pa_list()->mpreach(SAFI_MULTICAST)->nlri_list().size(); if(_packet->pa_list()->mpunreach(SAFI_MULTICAST)) wdr += _packet->pa_list()->mpunreach(SAFI_MULTICAST)->wr_list().size(); #ifdef HAVE_IPV6 // Account for IPv6 if(_packet->pa_list()->mpreach(SAFI_UNICAST)) nlri += _packet->pa_list()->mpreach(SAFI_UNICAST)->nlri_list().size(); if(_packet->pa_list()->mpunreach(SAFI_UNICAST)) wdr += _packet->pa_list()->mpunreach(SAFI_UNICAST)->wr_list().size(); if(_packet->pa_list()->mpreach(SAFI_MULTICAST)) nlri += _packet->pa_list()->mpreach(SAFI_MULTICAST)->nlri_list().size(); if(_packet->pa_list()->mpunreach(SAFI_MULTICAST)) wdr += _packet->pa_list()->mpunreach(SAFI_MULTICAST)->wr_list().size(); #endif // XLOG_ASSERT( (wdr+nlri) > 0); // If we get here and wdr+nlri equals zero then we were trying to // push an AFI/SAFI that our peer did not request so just drop // the packet and return. if (0 == (wdr+nlri)) { delete _packet; _packet = NULL; return PEER_OUTPUT_OK; } if (nlri > 0) XLOG_ASSERT(!_packet->pa_list()->is_empty()); _nlri_total += nlri; _packets++; debug_msg("Mean packet has %f nlri's\n", ((float)_nlri_total)/_packets); PeerOutputState result; result = _peer->send_update_message(*_packet); delete _packet; _packet = NULL; return result; } void PeerHandler::output_no_longer_busy() { if (_peering_is_up) { _plumbing_unicast->output_no_longer_busy(this); _plumbing_multicast->output_no_longer_busy(this); } } uint32_t PeerHandler::get_prefix_count() const { return _plumbing_unicast->get_prefix_count(this) + _plumbing_multicast->get_prefix_count(this); } EventLoop& PeerHandler::eventloop() const { return _peer->main()->eventloop(); } /** IPv6 stuff */ #ifdef HAVE_IPV6 template <> bool PeerHandler::multiprotocol(Safi safi, BGPPeerData::Direction d) const { return _peer->peerdata()->multiprotocol(safi, d); } int PeerHandler::add_route(const SubnetRoute &rt, ref_ptr >& pa_list, bool /*ibgp*/, Safi safi) { debug_msg("PeerHandler::add_route(IPv6) %p\n", &rt); XLOG_ASSERT(_packet != NULL); // if a route came from IBGP, it shouldn't go to IBGP (unless // we're a route reflector) // if (ibgp) // XLOG_ASSERT(!_peer->ibgp()); // Check this peer wants this NLRI if (!multiprotocol(safi, BGPPeerData::NEGOTIATED)) return 0; if (_packet->big_enough()) { push_packet(); start_packet(); } // did we already add the packet attribute list? if (_packet->pa_list()->is_empty() && !pa_list->is_empty()) { // no, so add all the path attributes for (int i = 0; i < MAX_ATTRIBUTE; i++) { const PathAttribute* pa; pa = pa_list->find_attribute_by_type((PathAttType)i); if (pa && i != NEXT_HOP) { /* ** Don't put an IPv6 next hop in the IPv4 path attribute list. */ _packet->add_pathatt(*pa); } } MPReachNLRIAttribute mp(safi); mp.set_nexthop(pa_list->nexthop()); _packet->add_pathatt(mp); } MPReachNLRIAttribute* mpreach_att = _packet->pa_list()->mpreach(safi); XLOG_ASSERT(mpreach_att); XLOG_ASSERT(mpreach_att->nexthop() == pa_list->nexthop()); mpreach_att->add_nlri(rt.net()); return 0; } int PeerHandler::replace_route(const SubnetRoute &old_rt, bool /*old_ibgp*/, const SubnetRoute &new_rt, bool new_ibgp, FPAList6Ref& pa_list, Safi safi) { // in the basic PeerHandler, we can ignore the old route, because // to change a route, the BGP protocol just sends the new // replacement route UNUSED(old_rt); return add_route(new_rt, pa_list, new_ibgp, safi); } int PeerHandler::delete_route(const SubnetRoute& rt, FPAList6Ref& /*pa_list*/, bool /*ibgp*/, Safi safi) { debug_msg("PeerHandler::delete_route(IPv6) %p\n", &rt); XLOG_ASSERT(_packet != NULL); // Check this peer wants this NLRI if (!multiprotocol(safi, BGPPeerData::NEGOTIATED)) return 0; if (_packet->big_enough()) { push_packet(); start_packet(); } if (0 == _packet->pa_list()->mpunreach(safi)) { MPUNReachNLRIAttribute* mp = new MPUNReachNLRIAttribute(safi); _packet->pa_list()->add_path_attribute(mp); } XLOG_ASSERT(_packet->pa_list()->mpunreach(safi)); _packet->pa_list()->mpunreach(safi)->add_withdrawn(rt.net()); return 0; } #endif // ipv6 xorp/bgp/route_queue.hh0000664000076400007640000000536411421137511015317 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/bgp/route_queue.hh,v 1.18 2008/11/08 06:14:37 mjh Exp $ #ifndef __BGP_ROUTE_QUEUE_HH__ #define __BGP_ROUTE_QUEUE_HH__ #include "subnet_route.hh" class PeerHandler; typedef enum ribout_queue_op { RTQUEUE_OP_ADD = 1, RTQUEUE_OP_DELETE = 2, RTQUEUE_OP_REPLACE_OLD = 3, RTQUEUE_OP_REPLACE_NEW = 4, RTQUEUE_OP_PUSH = 5 } RouteQueueOp; template class RouteQueueEntry { public: RouteQueueEntry(const SubnetRoute* rt, FPAListRef& pa_list, RouteQueueOp op) : _route_ref(rt), _pa_list(pa_list) { // mandate that we lock the pa_list before storing it here. XLOG_ASSERT(pa_list->is_locked()); _op = op; _origin_peer = 0; _push = false; } //for push only RouteQueueEntry(RouteQueueOp op, const PeerHandler *origin_peer) : _route_ref(NULL) { assert(op == RTQUEUE_OP_PUSH); _op = op; _origin_peer = origin_peer; // NULL is valid. _push = false; } ~RouteQueueEntry() { } const SubnetRoute* route() const { return _route_ref.route(); } const IPNet& net() const { return _route_ref.route()->net(); } FPAListRef attributes() const { // mandate that the attributes are still locked. if (!_pa_list.is_empty()) XLOG_ASSERT(_pa_list->is_locked()); return _pa_list; } RouteQueueOp op() const { return _op; } void set_origin_peer(const PeerHandler *peer) {_origin_peer = peer; } const PeerHandler *origin_peer() const { return _origin_peer; } void set_genid(uint32_t genid) { _genid = genid; } uint32_t genid() const { return _genid; } void set_push(bool push) { _push = push; } bool push() const { return _push;} string str() const; private: RouteQueueOp _op; SubnetRouteConstRef _route_ref; FPAListRef _pa_list; const PeerHandler *_origin_peer; uint32_t _genid; bool _push; }; #endif // __BGP_ROUTE_QUEUE_HH__ xorp/bgp/route_table_ribout.hh0000664000076400007640000000631111540224220016633 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/bgp/route_table_ribout.hh,v 1.21 2008/11/08 06:14:40 mjh Exp $ #ifndef __BGP_ROUTE_TABLE_RIBOUT_HH__ #define __BGP_ROUTE_TABLE_RIBOUT_HH__ #include "bgp_module.h" #include "libxorp/xlog.h" #include "libxorp/eventloop.hh" #include "route_table_base.hh" #include "route_queue.hh" #include "parameter.hh" template class RibOutTable : public BGPRouteTable { public: RibOutTable(string tablename, Safi safi, BGPRouteTable *parent, PeerHandler *peer); ~RibOutTable(); void print_queue(const list *>& queue) const; int add_route(InternalMessage &rtmsg, BGPRouteTable *caller); int replace_route(InternalMessage &old_rtmsg, InternalMessage &new_rtmsg, BGPRouteTable *caller); int delete_route(InternalMessage &rtmsg, BGPRouteTable *caller); int push(BGPRouteTable *caller); const SubnetRoute *lookup_route(const IPNet &net, uint32_t& genid, FPAListRef& pa_list) const; RouteTableType type() const {return RIB_OUT_TABLE;} string str() const; int dump_entire_table() { abort(); return 0; } /* mechanisms to implement flow control in the output plumbing */ void wakeup(); bool pull_next_route(); /* output_no_longer_busy is called asynchronously by the peer handler when the state of the output queue goes from busy to non-busy. When the output queue has been busy we will have signalled back upstream to stop further routes arriving, so we need to go and retrieve the queued routes */ void output_no_longer_busy(); bool get_next_message(BGPRouteTable */*next_table*/) { abort(); return false; } void reschedule_self(); void peering_went_down(const PeerHandler *peer, uint32_t genid, BGPRouteTable *caller); void peering_down_complete(const PeerHandler *peer, uint32_t genid, BGPRouteTable *caller); void peering_came_up(const PeerHandler *peer, uint32_t genid, BGPRouteTable *caller); private: //the queue that builds, prior to receiving a push, so we can //send updates to our peers atomically list *> _queue; PeerHandler *_peer; bool _peer_busy; bool _peer_is_up; XorpTask _pull_routes_task; }; #endif // __BGP_ROUTE_TABLE_RIBOUT_HH__ xorp/bgp/bgp_varrw.hh0000664000076400007640000001442511421137511014744 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/bgp/bgp_varrw.hh,v 1.26 2008/11/08 06:14:36 mjh Exp $ #ifndef __BGP_BGP_VARRW_HH__ #define __BGP_BGP_VARRW_HH__ #include "policy/backend/single_varrw.hh" #include "policy/common/element_factory.hh" #include "internal_message.hh" template class BGPVarRWCallbacks; /** * @short Allows reading an modifying a BGP route. * * If the route is modified, the user is responsible for retrieving the * filtered message and deleting it. * * Non-copyable due to inheritance from VarRW. */ template class BGPVarRW : public SingleVarRW { public: enum { VAR_NETWORK4 = VAR_PROTOCOL, VAR_NEXTHOP4, VAR_NETWORK6, VAR_NEXTHOP6, VAR_ASPATH, VAR_ORIGIN, // 15 VAR_NEIGHBOR, VAR_LOCALPREF, VAR_COMMUNITY, VAR_MED, VAR_MED_REMOVE, // 20 VAR_AGGREGATE_PREFIX_LEN, VAR_AGGREGATE_BRIEF_MODE, VAR_WAS_AGGREGATED, VAR_BGPMAX // must be last }; typedef Element* (BGPVarRW::*ReadCallback)(); typedef void (BGPVarRW::*WriteCallback)(const Element& e); /** * This varrw allows for routes to remain untouched even though they are * filtered. This is useful in order to check if a route will be accepted * or rejected, without caring about its modifications. * * @param name the name of the filter to print in case of tracing. */ BGPVarRW(const string& name); virtual ~BGPVarRW(); void set_peer(const A& peer); void set_self(const A& self); /** * Attach a route to the varrw. * * @param rtmsg the message to filter and possibly modify. * @param no_modify if true, the route will not be modified. */ void attach_route(InternalMessage& rtmsg, bool no_modify); void detach_route(InternalMessage& rtmsg); /** * Caller owns the message [responsible for delete]. * Calling multiple times will always return the same message, not a copy. * * @return the modified message. Null if no changes were made. */ InternalMessage* filtered_message(); // SingleVarRW interface Element* single_read(const Id& id); void single_write(const Id& id, const Element& e); void end_write(); /** * If a route is modified, the caller may obtain it via the filtered_message * call. * * @return true if route was modified. False otherwise. */ bool modified(); /** * Output basic BGP specific information. * * @return BGP trace based on verbosity level returned from trace(). */ virtual string more_tracelog(); /** * Reads the neighbor variable. This is different on input/output branch. * * @return the neighbor variable. */ virtual Element* read_neighbor(); /** * Callback wrapper used to call the virtual @ref read_neighbor() method. * * @return the neighbor variable. */ Element* read_neighbor_base_cb() { return read_neighbor(); } Element* read_policytags(); Element* read_filter_im(); Element* read_filter_sm(); Element* read_filter_ex(); Element* read_network4(); Element* read_network6(); Element* read_nexthop4(); Element* read_nexthop6(); Element* read_aspath(); Element* read_origin(); Element* read_localpref(); Element* read_community(); Element* read_med(); Element* read_med_remove(); Element* read_aggregate_prefix_len(); Element* read_aggregate_brief_mode(); Element* read_was_aggregated(); Element* read_tag(); void write_filter_im(const Element& e); void write_filter_sm(const Element& e); void write_filter_ex(const Element& e); void write_policytags(const Element& e); void write_nexthop4(const Element& e); void write_nexthop6(const Element& e); void write_aspath(const Element& e); void write_origin(const Element& e); void write_aggregate_prefix_len(const Element& e); void write_aggregate_brief_mode(const Element& e); void write_was_aggregated(const Element& e); void write_localpref(const Element& e); void write_community(const Element& e); void write_med(const Element& e); void write_med_remove(const Element& e); void write_tag(const Element& e); protected: ElementFactory _ef; string _name; private: void cleanup(); void write_nexthop(const Element& e); InternalMessage* _rtmsg; bool _got_fmsg; PolicyTags* _ptags; bool _wrote_ptags; FPAListRef _palist; bool _no_modify; bool _modified; RefPf _pfilter[3]; bool _wrote_pfilter[3]; bool _route_modify; A _self; A _peer; // Aggregation -> we cannot write those directly into the subnet // route so must provide local volatile copies to be operated on uint32_t _aggr_prefix_len; bool _aggr_brief_mode; // not impl BGPVarRW(const BGPVarRW&); BGPVarRW& operator=(const BGPVarRW&); static BGPVarRWCallbacks _callbacks; }; template class BGPVarRWCallbacks { public: // XXX don't know how to refer to BGPVarRW::ReadCallback in gcc 2.95 typedef Element* (BGPVarRW::*RCB)(); typedef void (BGPVarRW::*WCB)(const Element&); void init_rw(const VarRW::Id&, RCB, WCB); BGPVarRWCallbacks(); RCB _read_map[BGPVarRW::VAR_BGPMAX]; WCB _write_map[BGPVarRW::VAR_BGPMAX]; }; #endif // __BGP_BGP_VARRW_HH__ xorp/bgp/route_table_damping.hh0000664000076400007640000001020611421137511016750 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/bgp/route_table_damping.hh,v 1.8 2008/11/08 06:14:38 mjh Exp $ #ifndef __BGP_ROUTE_TABLE_DAMPING_HH__ #define __BGP_ROUTE_TABLE_DAMPING_HH__ #include "libxorp/trie.hh" #include "route_table_base.hh" #include "damping.hh" #include "peer_handler.hh" /** * BGP Route Flap Damping RFC 2439 */ /** * An entry of this form exists for all networks. */ class Damp { public: Damp(uint32_t time, uint32_t merit) : _time(time), _merit(merit), _damped(false) {} uint32_t _time; // Last time the route was announced or withdrawn. uint32_t _merit; // Figure of merit. bool _damped; // True if this route is currently damped. }; /** * An entry of this form exists only if the route has been damped. */ template class DampRoute { public: DampRoute(const SubnetRoute* route, uint32_t genid) : _routeref(route), _genid(genid) {} const SubnetRoute* route() const { return _routeref.route(); } uint32_t genid() const { return _genid; } XorpTimer& timer() { return _timer; } private: SubnetRouteConstRef _routeref; uint32_t _genid; XorpTimer _timer; // If this route is damped time when it should // be released. }; /** * Manage the damping of routes. * * NOTE: If damping was enabled and is then disabled it is possible * that some routes may be damped. While damped routes exist the * damping code is entered, no more routes are damped but routes are * only released as the timers fire. */ template class DampingTable : public BGPRouteTable { public: DampingTable(string tablename, Safi safi, BGPRouteTable* parent, const PeerHandler *peer, Damping& damping); ~DampingTable(); int add_route(InternalMessage &rtmsg, BGPRouteTable *caller); int replace_route(InternalMessage &old_rtmsg, InternalMessage &new_rtmsg, BGPRouteTable *caller); int delete_route(InternalMessage &rtmsg, BGPRouteTable *caller); int push(BGPRouteTable *caller); int route_dump(InternalMessage &rtmsg, BGPRouteTable *caller, const PeerHandler *dump_peer); const SubnetRoute *lookup_route(const IPNet &net, uint32_t& genid, FPAListRef& pa_list) const; void route_used(const SubnetRoute* route, bool in_use); RouteTableType type() const {return DAMPING_TABLE;} string str() const; private: bool damping() const { if (_peer->ibgp()) return false; if (0 != _damp_count) return true; return _damping.get_damping(); } bool damping_global() const { return _damping.get_damping(); } /** * Update the figure of merit for this route. * * @return true if the route should be damped. */ bool update_figure_of_merit(Damp&damp, const InternalMessage &rtmsg); /** * Is this route being damped. */ bool is_this_route_damped(const IPNet &net) const; /** * Callback method called to release damped route. */ void undamp(IPNet net); EventLoop& eventloop() const; private: const PeerHandler *_peer; Damping& _damping; Trie _damp; RefTrie > _damped; uint32_t _damp_count; // Number of damped routes. }; #endif // __BGP_ROUTE_TABLE_DAMPING_HH__ xorp/bgp/SConscript0000664000076400007640000001151511631507236014451 0ustar greearbgreearb# Copyright (c) 2009-2011 XORP, Inc and Others # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # $XORP$ import os Import('env') subdirs = [ 'tools', 'tests', 'harness', ] SConscript(dirs = subdirs, exports='env') is_shared = env.has_key('SHAREDLIBS') if not is_shared: env.AppendUnique(LIBS = [ "crypto", ]) if not (env.has_key('mingw') and env['mingw']): env.AppendUnique(LIBS = [ "rt", ]) if (env.has_key('mingw') and env['mingw']): env.AppendUnique(LIBS = [ 'ws2_32', 'iphlpapi', # 'mprapi', 'regex', 'winmm', ]) env.Append(LIBS = ['xorp_core']) bgp_env = env.Clone() bgp_env.AppendUnique(CPPPATH = [ '#', '$BUILDDIR', ]) bgp_env.PrependUnique(LIBPATH = [ '.', '$BUILDDIR/policy/backend', '$BUILDDIR/policy/common', '$BUILDDIR/libfeaclient', '$BUILDDIR/xrl/interfaces', '$BUILDDIR/xrl/targets', '$BUILDDIR/libxipc', '$BUILDDIR/libxorp', '$BUILDDIR/libcomm', ]) bgp_env.AppendUnique(LIBS = [ 'xorp_policy_backend', 'xorp_policy_common', 'xorp_fea_client', 'xst_bgp', 'xst_fea_ifmgr_mirror', 'xif_rib', 'xif_finder_event_notifier', 'xif_fea_ifmgr_mirror', 'xif_fea_ifmgr_replicator', 'xorp_ipc', 'xorp_core', 'xorp_comm', ]) if not (bgp_env.has_key('disable_profile') and bgp_env['disable_profile']): bgp_env.AppendUnique(LIBS = [ 'xif_profile_client' ]) bgp_env.Replace(RPATH = [ bgp_env.Literal(bgp_env['xorp_module_rpath']) ]) ### libxorp_bgp libxorp_bgp_env = bgp_env.Clone() libxorp_bgp_srcs = [ 'aspath.cc', 'attribute_manager.cc', 'bgp.cc', 'bgp_trie.cc', 'bgp_varrw.cc', 'bgp_varrw_export.cc', 'crash_dump.cc', 'damping.cc', 'dump_iterators.cc', 'internal_message.cc', 'iptuple.cc', 'local_data.cc', 'next_hop_resolver.cc', 'notification_packet.cc', 'open_packet.cc', 'packet.cc', 'parameter.cc', 'path_attribute.cc', 'peer.cc', 'peer_data.cc', 'peer_handler.cc', 'peer_list.cc', 'plumbing.cc', 'process_watch.cc', 'rib_ipc_handler.cc', 'route_queue.cc', 'route_table_aggregation.cc', 'route_table_base.cc', 'route_table_cache.cc', 'route_table_damping.cc', 'route_table_decision.cc', 'route_table_deletion.cc', 'route_table_dump.cc', 'route_table_fanout.cc', 'route_table_filter.cc', 'route_table_nhlookup.cc', 'route_table_policy.cc', 'route_table_policy_ex.cc', 'route_table_policy_im.cc', 'route_table_policy_sm.cc', 'route_table_reader.cc', 'route_table_ribin.cc', 'route_table_ribout.cc', 'socket.cc', 'subnet_route.cc', 'update_attrib.cc', 'update_packet.cc', 'xrl_target.cc', ] if not (bgp_env.has_key('disable_profile') and bgp_env['disable_profile']): libxorp_bgp_srcs.append('profile_vars.cc') if (bgp_env.has_key('mingw') and bgp_env['mingw']): bgp_env.Append(LIBS = [ 'ws2_32', 'iphlpapi', # 'mprapi', 'regex', 'winmm', ]) bgp_env.Append(LIBS = ['xorp_core', 'crypto', 'ws2_32']) if is_shared: libxorp_bgp = bgp_env.SharedLibrary(target = 'libxorp_bgp', source = libxorp_bgp_srcs, LIBS = '') if bgp_env['rtld_origin']: for obj in libxorp_bgp: bgp_env.AddPostAction(libxorp_bgp, bgp_env.Symlink(obj.abspath, os.path.join(bgp_env['xorp_alias_libdir'], str(obj)))) else: libxorp_ospf = bgp_env.StaticLibrary(target = 'libxorp_bgp', source = libxorp_bgp_srcs, LIBS = '') ### xorp_bgp #bgp_env2 = bgp_env.Clone() bgp_env.PrependUnique(LIBS = [ 'xorp_bgp', ]) bgpsrcs = [ 'main.cc', ] bgp = bgp_env.Program(target = 'xorp_bgp', source = bgpsrcs) bgp_env.Alias('install', bgp_env.InstallProgram(bgp_env['xorp_moduledir'], bgp)) if is_shared: bgp_env.Alias('install', env.InstallLibrary(bgp_env['xorp_libdir'], libxorp_bgp)) # Install scripts env.Alias('install', env.InstallScript('$exec_prefix/sbin/', env.Entry('bgp_xrl_shell_funcs.sh'))) Default(bgp) xorp/bgp/bgp_varrw_export.cc0000664000076400007640000000250311421137511016325 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "bgp_module.h" #include "libxorp/xorp.h" #include "bgp_varrw_export.hh" template BGPVarRWExport::BGPVarRWExport(const string& name, const string& neighbor) : BGPVarRW(name), _neighbor(neighbor) { } template Element* BGPVarRWExport::read_neighbor() { return BGPVarRW::_ef.create(ElemIPv4::id, _neighbor.c_str()); } template class BGPVarRWExport; template class BGPVarRWExport; xorp/bgp/packet.hh0000664000076400007640000002600111421137511014213 0ustar greearbgreearb // -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/bgp/packet.hh,v 1.50 2008/12/05 02:02:07 atanu Exp $ #ifndef __BGP_PACKET_HH__ #define __BGP_PACKET_HH__ #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/ipv4net.hh" #include "libxorp/ipv4.hh" #include "path_attribute.hh" #ifdef HAVE_SYS_UIO_H #include #endif #ifdef HAVE_NETINET_IN_H #include #endif #include "libproto/packet.hh" #include "exceptions.hh" #include "parameter.hh" #include "local_data.hh" #include "peer_data.hh" #include "path_attribute.hh" #include "update_attrib.hh" class BGPPeer; enum BgpPacketType { MESSAGETYPEOPEN = 1, MESSAGETYPEUPDATE = 2, MESSAGETYPENOTIFICATION = 3, MESSAGETYPEKEEPALIVE = 4 }; /** * Notification message error codes with associated subcodes. */ enum Notify { #define UNSPECIFIED 0 // Unspecified subcode error MSGHEADERERR = 1, // Message Header Error #define CONNNOTSYNC 1 // Connection Not Synchronized #define BADMESSLEN 2 // Bad Message Length #define BADMESSTYPE 3 // Bad Message Type OPENMSGERROR = 2, // OPEN Message Error #define UNSUPVERNUM 1 // Unsupported Version Number #define BADASPEER 2 // Bad BGPPeer AS #define BADBGPIDENT 3 // Bad BGP Identifier #define UNSUPOPTPAR 4 // Unsupported Optional Parameter #define AUTHFAIL 5 // Authentication Failure #define UNACCEPTHOLDTIME 6 // Unacceptable Hold Time #define UNSUPCAPABILITY 7 // Unsupported Capability (RFC 3392) UPDATEMSGERR = 3, // UPDATE Message Error #define MALATTRLIST 1 // Malformed Attribute List #define UNRECOGWATTR 2 // Unrecognized Well-known Attribute #define MISSWATTR 3 // Missing Well-known Attribute #define ATTRFLAGS 4 // Attribute Flags Error #define ATTRLEN 5 // Attribute Length Error #define INVALORGATTR 6 // Invalid ORIGIN Attribute //#define #define INVALNHATTR 8 // Invalid NEXT_HOP Attribute #define OPTATTR 9 // Optional Attribute Error #define INVALNETFIELD 10 // Invalid Network Field #define MALASPATH 11 // Malformed AS_PATH HOLDTIMEEXP = 4, // Hold Timer Expired FSMERROR = 5, // Finite State Machine Error CEASE = 6 // Cease }; /** * The main container for BGP messages (packets) which are sent * back and forth. * * This base class only contains the standard fields (length, type) * leaving other information to be stored in the derived objects. */ class BGPPacket { public: /** * Status returned by message reader. */ enum Status { GOOD_MESSAGE, ILLEGAL_MESSAGE_LENGTH, CONNECTION_CLOSED, }; // // The BGP common header (on the wire) // // struct fixed_header { // uint8_t marker[MARKER_SIZE]; // normally all bits are ones. // uint16_t length; // this is in network format // uint8_t type; // enum BgpPacketType // }; // // Various header-related constants static const size_t MARKER_SIZE = 16; static const size_t COMMON_HEADER_LEN = MARKER_SIZE + sizeof(uint16_t) + sizeof(uint8_t); static const size_t MARKER_OFFSET = 0; static const size_t LENGTH_OFFSET = MARKER_SIZE; static const size_t TYPE_OFFSET = LENGTH_OFFSET + sizeof(uint16_t); // // Various min and max packet size, accounting for the basic // information in the packet itself. // static const size_t MINPACKETSIZE = COMMON_HEADER_LEN; static const size_t MAXPACKETSIZE = 4096; static const size_t MINOPENPACKET = COMMON_HEADER_LEN + 10; static const size_t MINUPDATEPACKET = COMMON_HEADER_LEN + 2 + 2; static const size_t MINKEEPALIVEPACKET = COMMON_HEADER_LEN; static const size_t MINNOTIFICATIONPACKET = COMMON_HEADER_LEN + 2; // The default marker. static const uint8_t Marker[MARKER_SIZE]; BGPPacket() {} virtual ~BGPPacket() {} uint8_t type() const { return _Type; } virtual string str() const = 0; virtual bool encode(uint8_t *buf, size_t &len, const BGPPeerData *peerdata) const = 0; protected: /* * Return the external representation of the packet into a buffer. * If the caller supplies the buffer it is its responsibility to make * sure that it has the correct size, otherwise the routine will * allocate it with new uint8_t[len]. * It is responsibility of the caller to dispose of the buffer. * Note that this routine will only copy the BGP common header part. * The derived-class methods are in charge of filling up any * additional data past it. */ uint8_t *basic_encode(size_t len, uint8_t *buf) const; // don't allow the use of the default copy constructor BGPPacket(const BGPPacket& BGPPacket); uint8_t _Type; private: }; /* **************** OpenPacket *********************** */ class OpenPacket : public BGPPacket { public: OpenPacket(const uint8_t *d, uint16_t l) throw(CorruptMessage); OpenPacket(const AsNum& as, const IPv4& bgpid, const uint16_t holdtime); ~OpenPacket() {} bool encode(uint8_t *buf, size_t& len, const BGPPeerData *peerdata) const; string str() const; uint8_t Version() const { return _Version; } const AsNum as() const { return _as; } uint16_t HoldTime() const { return _HoldTime; } const IPv4 id() const { return _id; } uint8_t OptParmLen() const { return _OptParmLen; } bool operator==(const OpenPacket& him) const; void add_parameter(const ParameterNode& p); const ParameterList& parameter_list() const { return _parameter_list; } protected: private: OpenPacket(); // don't allow the use of the default copy constructor OpenPacket(const OpenPacket& OpenPacket); IPv4 _id; AsNum _as; uint16_t _HoldTime; uint8_t _OptParmLen; uint8_t _Version; ParameterList _parameter_list; }; /* **************** UpdatePacket *********************** */ class UpdatePacket : public BGPPacket { public: UpdatePacket(); UpdatePacket(const uint8_t *d, uint16_t l, const BGPPeerData *peerdata, BGPMain* mainprocess, bool do_checks) throw(CorruptMessage,UnusableMessage); ~UpdatePacket(); void add_withdrawn(const BGPUpdateAttrib& wdr); void replace_pathattribute_list(FPAList4Ref& pa_list); void add_pathatt(const PathAttribute& pa); void add_pathatt(PathAttribute *pa); void add_nlri(const BGPUpdateAttrib& nlri); const BGPUpdateAttribList& wr_list() const { return _wr_list; } FPAList4Ref& pa_list() { return _pa_list; } const BGPUpdateAttribList& nlri_list() const { return _nlri_list; } template const MPReachNLRIAttribute *mpreach(Safi) const; template const MPUNReachNLRIAttribute *mpunreach(Safi) const; bool encode(uint8_t *buf, size_t& len, const BGPPeerData *peerdata) const; bool big_enough() const; string str() const; bool operator==(const UpdatePacket& him) const; protected: private: // don't allow the use of the default copy constructor UpdatePacket(const UpdatePacket& UpdatePacket); BGPUpdateAttribList _wr_list; FPAList4Ref _pa_list; BGPUpdateAttribList _nlri_list; }; template const MPReachNLRIAttribute * UpdatePacket::mpreach(Safi safi) const { XLOG_ASSERT(!(A::ip_version() == 4 && SAFI_UNICAST == safi)); FastPathAttributeList& fpalist = *_pa_list; MPReachNLRIAttribute* mpreach = fpalist.template mpreach(safi); return mpreach; } template const MPUNReachNLRIAttribute * UpdatePacket::mpunreach(Safi safi) const { XLOG_ASSERT(!(A::ip_version() == 4 && SAFI_UNICAST == safi)); FastPathAttributeList& fpalist = *_pa_list; MPUNReachNLRIAttribute* mpunreach = fpalist.template mpunreach(safi); return mpunreach; } /* **************** BGPNotificationPacket *********************** */ class NotificationPacket : public BGPPacket { public: NotificationPacket(const uint8_t *d, uint16_t l) throw(CorruptMessage); NotificationPacket(uint8_t ec, uint8_t esc = 0, const uint8_t *d = 0, size_t l=0); NotificationPacket(); ~NotificationPacket() { delete[] _error_data; } uint8_t error_code() const { return _error_code; } uint8_t error_subcode() const { return _error_subcode; } /** * Verify that the supplied error code and subcode are legal. */ static bool validate_error_code(const int error, const int subcode); /** * Generate a human readable error string. */ static string pretty_print_error_code(const int error, const int subcode, const uint8_t* error_data = 0, const size_t len = 0); const uint8_t* error_data() const { return _error_data; } bool encode(uint8_t *buf, size_t &len, const BGPPeerData *peerdata) const; string str() const; bool operator==(const NotificationPacket& him) const; protected: private: // don't allow the use of the default copy constructor NotificationPacket(const NotificationPacket& Notificationpacket); size_t _Length; // total size incl. header const uint8_t* _error_data; uint8_t _error_code; uint8_t _error_subcode; }; /** * KeepAlivePacket are extremely simple, being made only of a header. * with the appropriate type and length. */ class KeepAlivePacket : public BGPPacket { public: /** * need nothing to parse incoming data */ KeepAlivePacket(const uint8_t *buf, uint16_t l) throw(CorruptMessage) { if (l != BGPPacket::MINKEEPALIVEPACKET) xorp_throw(CorruptMessage, c_format("KeepAlivePacket length %d instead of %u", l, XORP_UINT_CAST(BGPPacket::MINKEEPALIVEPACKET)), MSGHEADERERR, BADMESSLEN, buf + BGPPacket::MARKER_SIZE, 2); _Type = MESSAGETYPEKEEPALIVE; } KeepAlivePacket() { _Type = MESSAGETYPEKEEPALIVE; } ~KeepAlivePacket() {} bool encode(uint8_t *buf, size_t &len, const BGPPeerData *peerdata) const { UNUSED(peerdata); len = BGPPacket::MINKEEPALIVEPACKET; return basic_encode(len, buf); } virtual string str() const { return "Keepalive Packet\n"; } bool operator==(const KeepAlivePacket&) const { return true; } protected: private: // don't allow the use of the default copy constructor KeepAlivePacket(const KeepAlivePacket& KeepAlivePacket); }; #endif // __BGP_PACKET_HH__ xorp/bgp/bgp_xrl_shell_funcs.sh0000775000076400007640000001072111421137511017006 0ustar greearbgreearb#!/bin/sh if [ "_$CALLXRL" == "_" ] then if which call_xrl > /dev/null 2>&1 then CALLXRL=call_xrl else CALLXRL=/usr/local/xorp/sbin/call_xrl fi fi if ! which $CALLXRL > /dev/null 2>&1 then echo "ERROR: Cannot find call_xrl, tried: $CALLXRL" exit 1 fi local_config() { echo "local_config" $* # $CALLXRL "finder://bgp/bgp/0.1/local_config?localhost:txt=$1&port:i32=$2&as_num:i32=$3&id:ipv4=$4&version:i32=$5&holdtime:i32=$6" $CALLXRL "finder://bgp/bgp/0.3/local_config?as:txt=$1&id:ipv4=$2&use_4byte_asnums:bool=$3" } route_reflector() { echo "route_reflector" $* $CALLXRL "finder://bgp/bgp/0.3/set_cluster_id?cluster_id:ipv4=$1&disable:bool=$2" } set_damping() { echo "route_flap_damping" $* $CALLXRL "finder://bgp/bgp/0.3/set_damping?half_life:u32=$1&max_suppress:u32=$2&reuse:u32=$3&suppress:u32=$4&disable:bool=$5" } add_peer() { echo "add_peer" $* # $CALLXRL "finder://bgp/bgp/0.1/add_peer?peer:txt=$1&as:i32=$2&port:i32=$3&next_hop:ipv4=$4" $CALLXRL "finder://bgp/bgp/0.3/add_peer?local_dev:txt=$1&local_ip:txt=$2&local_port:u32=$3&peer_ip:txt=$4&peer_port:u32=$5&as:txt=$6&next_hop:ipv4=$7&holdtime:u32=$8" } delete_peer() { echo "delete_peer" $* # $CALLXRL "finder://bgp/bgp/0.1/delete_peer?peer:txt=$1&as:i32=$2" $CALLXRL "finder://bgp/bgp/0.3/delete_peer?local_ip:txt=$1&local_port:u32=$2&peer_ip:txt=$3&peer_port:u32=$4&as:txt=$5" } enable_peer() { echo "enable_peer" $* # $CALLXRL "finder://bgp/bgp/0.1/enable_peer?peer:txt=$1&as:i32=$2" $CALLXRL "finder://bgp/bgp/0.3/enable_peer?local_ip:txt=$1&local_port:u32=$2&peer_ip:txt=$3&peer_port:u32=$4" } set_parameter() { echo "set_parameter" $* # $CALLXRL "finder://bgp/bgp/0.3/set_parameter?local_ip:txt=$1&local_port:u32=$2&peer_ip:txt=$3&peer_port:u32=$4¶meter:txt=$5" $CALLXRL "finder://bgp/bgp/0.3/set_parameter?local_ip:txt=$1&local_port:u32=$2&peer_ip:txt=$3&peer_port:u32=$4¶meter:txt=$5&toggle:bool=$6" } disable_peer() { echo "disable_peer" $* # $CALLXRL "finder://bgp/bgp/0.1/disable_peer?peer:txt=$1&as:i32=$2" $CALLXRL "finder://bgp/bgp/0.3/disable_peer?local_ip:txt=$1&local_port:u32=$2&peer_ip:txt=$3&peer_port:u32=$4" } route_reflector_client() { echo "route_reflector_client" $* $CALLXRL "finder://bgp/bgp/0.3/set_route_reflector_client?local_ip:txt=$1&local_port:u32=$2&peer_ip:txt=$3&peer_port:u32=$3&state:bool=$5" } set_peer_md5_password() { echo "set_peer_md5_password" $* $CALLXRL "finder://bgp/bgp/0.3/set_peer_md5_password?local_ip:txt=$1&local_port:u32=$2&peer_ip:txt=$3&peer_port:u32=$4&password:txt=$5" } next_hop_rewrite_filter() { echo "next_hop_rewrite_filter" $* $CALLXRL "finder://bgp/bgp/0.3/next_hop_rewrite_filter?local_ip:txt=$1&local_port:u32=$2&peer_ip:txt=$3&peer_port:u32=$4&next_hop:ipv4=$5" } register_rib() { echo "register_rib" $* $CALLXRL "finder://bgp/bgp/0.3/register_rib?name:txt=$1" } originate_route4() { echo "originate_route4" $* $CALLXRL "finder://bgp/bgp/0.3/originate_route4?nlri:ipv4net=$1&next_hop:ipv4=$2&unicast:bool=$3&multicast:bool=$4" } originate_route6() { echo "originate_route6" $* $CALLXRL "finder://bgp/bgp/0.3/originate_route6?nlri:ipv6net=$1&next_hop:ipv6=$2&unicast:bool=$3&multicast:bool=$4" } withdraw_route4() { echo "withdraw_route4" $* $CALLXRL "finder://bgp/bgp/0.3/withdraw_route4?nlri:ipv4net=$1&unicast:bool&=$2multicast:bool=$3" } withdraw_route6() { echo "withdraw_route6" $* $CALLXRL "finder://bgp/bgp/0.3/withdraw_route6?nlri:ipv6net=$1&unicast:bool&=$2multicast:bool=$3" } shutdown() { echo "shutdown" $* $CALLXRL "finder://bgp/common/0.1/shutdown" } time_wait_seconds() { # If there are less than 200 PCB's in TIMEWAIT then return 0. local twc twc=`netstat -n | grep TIME_WAIT | grep 19999 | wc -l` if [ $twc -lt 200 ] then echo "0" return fi local tw os os=`uname -s` case $os in Linux) tw=`sysctl -n net.ipv4.tcp_fin_timeout 2>/dev/null` if [ $? -eq 0 ] ; then echo $tw return fi ;; FreeBSD) local msl msl=`sysctl -n net.inet.tcp.msl 2>/dev/null` if [ $? -eq 0 ] ; then # Timewait is 2 * msl and msl is in milliseconds tw=`expr $msl + $msl + 1` tw=`expr $tw / 1000` echo $tw return fi ;; *) # All other OS: use the default value below ;; esac # Defailt to 60 seconds echo "60" } # We have arguments. if [ $# != 0 ] then $* fi # Local Variables: # mode: shell-script # sh-indentation: 4 # End: xorp/bgp/configs/0000775000076400007640000000000011421137511014054 5ustar greearbgreearbxorp/bgp/configs/config-xorp6.icir.org.bgp0000664000076400007640000000051011421137511020576 0ustar greearbgreearbSERVER xorp6.icir.org 9000 #LOCAL AS_NUM BGP_ID VERISON HOLDTIME LOCAL 65011 1000 4 300000 #ROUTE NET PREFIX LEN ORIGIN LOCAL_PREF NEXT_HOP AS_PATH #ROUTE 192.168.2.4 8 EGP 5 192.168.1.4 1234 5678 9012 END #PEER NAME PORT BGP_ID #PEER xorp-c4000.icir.org 179 65000 192.150.187.106 PEER xorp7.icir.org 9000 65012 192.150.187.106 xorp/bgp/configs/config-xorp1.icir.org.bgp0000664000076400007640000000036511421137511020601 0ustar greearbgreearb#SERVER #LOCAL AS_NUM BGP_ID VERISON HOLDTIME LOCAL 65005 1000 4 300000 #ROUTE NET PREFIX LEN ORIGIN LOCAL_PREF NEXT_HOP AS_PATH #ROUTE 192.168.2.4 8 EGP 5 192.168.1.4 1234 5678 9012 END #PEER NAME PORT BGP_ID PEER xorp-c4000.icir.org 179 65000 xorp/bgp/configs/config-xorp7.icir.org.bgp0000664000076400007640000000057011421137511020605 0ustar greearbgreearbSERVER xorp7.icir.org 9000 #LOCAL AS_NUM BGP_ID VERISON HOLDTIME LOCAL 65012 1000 4 300000 #ROUTE NET PREFIX LEN ORIGIN LOCAL_PREF NEXT_HOP AS_PATH #ROUTE 192.168.2.4 8 EGP 5 192.168.1.4 1234 5678 9012 END #PEER NAME PORT BGP_ID #PEER xorp-c4000.icir.org 179 65000 192.150.187.107 PEER tigger.icir.org 9000 65008 192.150.187.107 PEER xorp6.icir.org 9000 65011 192.150.187.107 xorp/bgp/configs/config.bgp0000664000076400007640000000050511421137511016013 0ustar greearbgreearb#SERVER #LOCAL AS_NUM BGP_ID VERISON HOLDTIME LOCAL 65002 1000 4 300000 #ROUTE NET PREFIX LEN ORIGIN LOCAL_PREF NEXT_HOP AS_PATH #ROUTE 192.168.2.4 8 EGP 5 192.168.1.4 1234 5678 9012 END #PEER NAME PORT BGP_ID NEXT_HOP #PEER xorp-c4000.icir.org 179 65000 192.150.187.109 PATHATTRIBUTE AUTHENTICATION CAPABILITY ROUTE-REFRESH xorp/bgp/configs/server.bgp0000664000076400007640000000005011421137511016047 0ustar greearbgreearbSERVER x20 6969 LOCAL 123 1000 4 300000 xorp/bgp/configs/config-xorp2.icir.org.bgp0000664000076400007640000000036511421137511020602 0ustar greearbgreearb#SERVER #LOCAL AS_NUM BGP_ID VERISON HOLDTIME LOCAL 65003 1000 4 300000 #ROUTE NET PREFIX LEN ORIGIN LOCAL_PREF NEXT_HOP AS_PATH #ROUTE 192.168.2.4 8 EGP 5 192.168.1.4 1234 5678 9012 END #PEER NAME PORT BGP_ID PEER xorp-c4000.icir.org 179 65000 xorp/bgp/configs/config-x20.internal.hiddennet.net.bgp0000664000076400007640000000050211421137511022760 0ustar greearbgreearb#SERVER #LOCAL AS_NUM BGP_ID VERISON HOLDTIME LOCAL 65101 1000 4 300000 #ROUTE NET PREFIX LEN ORIGIN LOCAL_PREF NEXT_HOP AS_PATH #ROUTE 192.168.2.4 8 EGP 5 192.168.1.4 1234 5678 9012 END #PEER NAME PORT BGP_ID PEER merry.internal.hiddennet.net 179 65100 192.168.1.31 PEER x20.internal.hiddennet.net 179 65101 192.168.1.18 xorp/bgp/configs/plumbing_test.bgp0000664000076400007640000000036711421137511017430 0ustar greearbgreearb#SERVER #LOCAL AS_NUM BGP_ID VERISON HOLDTIME LOCAL 65001 1000 4 300000 #ROUTE NET PREFIX LEN ORIGIN LOCAL_PREF NEXT_HOP AS_PATH #ROUTE 192.168.2.4 8 EGP 5 192.168.1.4 1234 5678 9012 END #PEER NAME PORT BGP_ID #PEER xorp-c4000.icir.org 179 65000 xorp/bgp/configs/config-aardvark.icir.org.bgp0000664000076400007640000000052311421137511021317 0ustar greearbgreearbSERVER aardvark.icir.org 9000 #LOCAL AS_NUM BGP_ID VERISON HOLDTIME LOCAL 65009 1000 4 300000 #ROUTE NET PREFIX LEN ORIGIN LOCAL_PREF NEXT_HOP AS_PATH #ROUTE 192.168.2.4 8 EGP 5 192.168.1.4 1234 5678 9012 END #PEER NAME PORT THEIR_AS MY_NEXTHOP PEER xorp-c4000.icir.org 179 65000 192.150.187.20 PEER www.icir.org 9000 65013 192.150.187.20 xorp/bgp/configs/config-www.icir.org.bgp0000664000076400007640000000047011421137511020351 0ustar greearbgreearbSERVER www.icir.org 9000 #LOCAL AS_NUM BGP_ID VERISON HOLDTIME LOCAL 65013 1000 4 300000 #ROUTE NET PREFIX LEN ORIGIN LOCAL_PREF NEXT_HOP AS_PATH #ROUTE 192.168.2.4 8 EGP 5 192.168.1.4 1234 5678 9012 END #PEER NAME PORT BGP_ID #PEER xorp-c4000.icir.org 179 65000 PEER aardvark.icir.org 9000 65009 192.150.187.11 xorp/bgp/configs/config-xorp3.icir.org.bgp0000664000076400007640000000036511421137511020603 0ustar greearbgreearb#SERVER #LOCAL AS_NUM BGP_ID VERISON HOLDTIME LOCAL 65006 1000 4 300000 #ROUTE NET PREFIX LEN ORIGIN LOCAL_PREF NEXT_HOP AS_PATH #ROUTE 192.168.2.4 8 EGP 5 192.168.1.4 1234 5678 9012 END #PEER NAME PORT BGP_ID PEER xorp-c4000.icir.org 179 65000 xorp/bgp/configs/config-thinmint.icir.org.bgp0000664000076400007640000000042411421137511021356 0ustar greearbgreearbSERVER thinmint.icir.org 9000 #LOCAL AS_NUM BGP_ID VERISON HOLDTIME LOCAL 65015 1000 4 300000 #ROUTE NET PREFIX LEN ORIGIN LOCAL_PREF NEXT_HOP AS_PATH #ROUTE 192.168.2.4 8 EGP 5 192.168.1.4 1234 5678 9012 END #PEER NAME PORT BGP_ID PEER 64.170.142.98 9000 65014 64.170.142.99 xorp/bgp/configs/config-xorp0.icir.org.bgp0000664000076400007640000000036511421137511020600 0ustar greearbgreearb#SERVER #LOCAL AS_NUM BGP_ID VERISON HOLDTIME LOCAL 65002 1000 4 300000 #ROUTE NET PREFIX LEN ORIGIN LOCAL_PREF NEXT_HOP AS_PATH #ROUTE 192.168.2.4 8 EGP 5 192.168.1.4 1234 5678 9012 END #PEER NAME PORT BGP_ID PEER xorp-c4000.icir.org 179 65000 xorp/bgp/configs/config-lemming.icir.org.bgp0000664000076400007640000000042311421137511021153 0ustar greearbgreearbSERVER lemming.icir.org 9000 #LOCAL AS_NUM BGP_ID VERISON HOLDTIME LOCAL 65014 1000 4 300000 #ROUTE NET PREFIX LEN ORIGIN LOCAL_PREF NEXT_HOP AS_PATH #ROUTE 192.168.2.4 8 EGP 5 192.168.1.4 1234 5678 9012 END #PEER NAME PORT BGP_ID PEER 64.170.142.99 9000 65015 64.170.142.98 xorp/bgp/configs/config-tigger.icir.org.bgp0000664000076400007640000000050611421137511021006 0ustar greearbgreearbSERVER tigger.icir.org 9000 #LOCAL AS_NUM BGP_ID VERISON HOLDTIME LOCAL 65008 1000 4 300000 #ROUTE NET PREFIX LEN ORIGIN LOCAL_PREF NEXT_HOP AS_PATH #ROUTE 192.168.2.4 8 EGP 5 192.168.1.4 1234 5678 9012 END #PEER NAME PORT BGP_ID PEER xorp-c4000.icir.org 179 65000 192.150.187.78 PEER xorp7.icir.org 9000 65012 192.150.187.78 xorp/bgp/configs/config-xorp4.icir.org.bgp0000664000076400007640000000036511421137511020604 0ustar greearbgreearb#SERVER #LOCAL AS_NUM BGP_ID VERISON HOLDTIME LOCAL 65007 1000 4 300000 #ROUTE NET PREFIX LEN ORIGIN LOCAL_PREF NEXT_HOP AS_PATH #ROUTE 192.168.2.4 8 EGP 5 192.168.1.4 1234 5678 9012 END #PEER NAME PORT BGP_ID PEER xorp-c4000.icir.org 179 65000 xorp/bgp/exceptions.hh0000664000076400007640000000623511421137511015134 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/bgp/exceptions.hh,v 1.16 2008/11/08 06:14:36 mjh Exp $ #ifndef __BGP_EXCEPTIONS_HH__ #define __BGP_EXCEPTIONS_HH__ #include "libxorp/xorp.h" #include "libxorp/exceptions.hh" #ifdef HAVE_SYS_UIO_H #include #endif #ifdef HAVE_NETINET_IN_H #include #endif /** * This exception is thrown when a bad input message is received. */ class CorruptMessage : public XorpReasonedException { public: static const size_t MAXPACKETSIZE = 4096; CorruptMessage(const char* file, size_t line, const string init_why = "") : XorpReasonedException("CorruptMessage", file, line, init_why), _error(0), _subcode(0), _len(0) {} CorruptMessage(const char* file, size_t line, const string init_why, const int error, const int subcode) : XorpReasonedException("CorruptMessage", file, line, init_why), _error(error), _subcode(subcode), _len(0) {} CorruptMessage(const char* file, size_t line, const string init_why, const int error, const int subcode, const uint8_t *data, const size_t len) : XorpReasonedException("CorruptMessage", file, line, init_why), _error(error), _subcode(subcode), _len(len) {assert(_len < MAXPACKETSIZE); memcpy(_data, data, _len);} int error() const { return _error; } int subcode() const { return _subcode; } const uint8_t *data() const { return _data; } size_t len() const { return _len; } private: const int _error; const int _subcode; // this can't reference external data, as that may go out of // scope, so have a large enough buffer here to store anything we // need to return uint8_t _data[MAXPACKETSIZE]; const size_t _len; }; /** * This exception is thrown when an update message is received, and we * find it unusable, but not so bad that we want to send a * Notification and terminate the connection. */ class UnusableMessage : public XorpReasonedException { public: UnusableMessage(const char* file, size_t line, const string init_why) : XorpReasonedException("CorruptMessage", file, line, init_why) {} }; class NoFinder : public XorpReasonedException { public: NoFinder(const char* file, size_t line, const string init_why = "") : XorpReasonedException("NoFinder", file, line, init_why) {} }; #endif // __BGP_EXCEPTIONS_HH__ xorp/bgp/dump_iterators.cc0000664000076400007640000004234711421137511016006 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net //#define DEBUG_LOGGING //#define DEBUG_PRINT_FUNCTION_NAME #include "bgp_module.h" #include "libxorp/xlog.h" #include "dump_iterators.hh" #include "internal_message.hh" //#define DEBUG_CODEPATH #ifdef DEBUG_CODEPATH #define cp(x) printf("DumpItCodePath: %2d\n", x) #else #define cp(x) {} #endif template PeerDumpState::PeerDumpState(const PeerHandler* peer, PeerDumpStatus status, uint32_t genid) { _peer = peer; _genid = genid; _status = status; _routes_dumped = false; debug_msg("%s", str().c_str()); } #ifdef NOTDEF template PeerDumpState::PeerDumpState(const PeerHandler* peer, uint32_t genid) { _peer = peer; _routes_dumped = false; _genid = genid; } #endif template string PeerDumpState::str() const { return c_format("peer: %p routes_dumped: %s last_net: %s, genid: %d\n", _peer, bool_c_str(_routes_dumped), _last_net_before_down.str().c_str(), XORP_INT_CAST(_genid)); } template PeerDumpState::~PeerDumpState() { } template void PeerDumpState::set_delete_complete(uint32_t genid) { debug_msg("set_delete_complete: Peer: %p genid: %d\n", _peer, XORP_INT_CAST(genid)); typename set ::iterator i; i = _deleting_genids.find(genid); if (i != _deleting_genids.end()) { _deleting_genids.erase(i); return; } else { switch(_status) { case STILL_TO_DUMP: case CURRENTLY_DUMPING: // this should never happen because we'd have recorded the // genid when the peering went down, or at DumpTable // startup. XLOG_UNREACHABLE(); break; case DOWN_DURING_DUMP: case DOWN_BEFORE_DUMP: case COMPLETELY_DUMPED: case NEW_PEER: case FIRST_SEEN_DURING_DUMP: // this can happen, because in these states we don't // bother to record when the peering goes down again. return; } } } template DumpIterator::DumpIterator(const PeerHandler* peer, const list *>& peers_to_dump) { debug_msg("peer: %p\n", peer); _peer = peer; typename list *>::const_iterator i; int ctr = 0; for (i = peers_to_dump.begin(); i != peers_to_dump.end(); i++) { if ((*i)->peer_handler() != peer) { // Store it in the list of peers to dump. // This determines the dump order. debug_msg("adding peer %p to dump list\n", *i); _peers_to_dump.push_back(**i); // Also store it in the general peer state data _peers[(*i)->peer_handler()] = new PeerDumpState((*i)->peer_handler(), STILL_TO_DUMP, (*i)->genid()); ctr++; } } debug_msg("Peers to dump has %d members\n", ctr); _current_peer = _peers_to_dump.begin(); if (_current_peer != _peers_to_dump.end()) { _current_peer_debug = &(*_current_peer); typename map * >::iterator state_i; state_i = _peers.find(_current_peer->peer_handler()); XLOG_ASSERT(state_i != _peers.end()); state_i->second->start_dump(); } else { _current_peer_debug = NULL; } _route_iterator_is_valid = false; _routes_dumped_on_current_peer = false; } template DumpIterator::~DumpIterator() { if (_route_iterator.cur() != NULL) debug_msg("refcnt: %d\n", XORP_INT_CAST(_route_iterator.cur()->references())); else if (_aggr_iterator.cur() != NULL) debug_msg("aggr refcnt: %d\n", XORP_INT_CAST(_route_iterator.cur()->references())); else debug_msg("iterator not valid\n"); //delete the payloads of the map typename map * >::iterator i; for (i = _peers.begin(); i!= _peers.end(); i++) { delete i->second; } } template string DumpIterator::str() const { return c_format("peer: %p last dumped net %s", _peer, _last_dumped_net.str().c_str()); } template void DumpIterator::route_dump(const InternalMessage &rtmsg) { // XXX inefficient sanity checks XLOG_ASSERT(rtmsg.origin_peer() == _current_peer->peer_handler()); typename map * >::iterator state_i; state_i = _peers.find(_current_peer->peer_handler()); XLOG_ASSERT(state_i != _peers.end()); debug_msg("route_dump: rtmsg.genid():%d state_i->second->genid():%d\n", XORP_INT_CAST(rtmsg.genid()), XORP_INT_CAST(state_i->second->genid())); switch (state_i->second->status()) { case STILL_TO_DUMP: debug_msg("STILL_TO_DUMP\n"); break; case CURRENTLY_DUMPING: debug_msg("CURRENTLY_DUMPING\n"); break; case DOWN_BEFORE_DUMP: debug_msg("DOWN_BEFORE_DUMP\n"); break; case DOWN_DURING_DUMP: case COMPLETELY_DUMPED: case NEW_PEER: case FIRST_SEEN_DURING_DUMP: debug_msg("OTHER\n"); break; } XLOG_ASSERT(rtmsg.genid() == state_i->second->genid()); // end sanity checks _routes_dumped_on_current_peer = true; _last_dumped_net = rtmsg.net(); } template bool DumpIterator::is_valid() const { // stupid hack to get around inability to compare const and // non-const iterators list > *peer_lst = const_cast > *>(&_peers_to_dump); if (_current_peer == peer_lst->end()) { debug_msg("Iterator is not valid\n"); return false; } debug_msg("Iterator is valid\n"); return true; } template bool DumpIterator::next_peer() { typename map * >::iterator state_i; state_i = _peers.find(_current_peer->peer_handler()); XLOG_ASSERT(state_i != _peers.end()); //we've finished with the old peer if (state_i->second->status() == CURRENTLY_DUMPING) state_i->second->set_dump_complete(); //move to next undumped peer, if any remain while (state_i->second->status() != STILL_TO_DUMP) { _current_peer++; if (_current_peer == _peers_to_dump.end()) { _current_peer_debug = NULL; break; } _current_peer_debug = &(*_current_peer); state_i = _peers.find(_current_peer->peer_handler()); } //record that we've started if (_current_peer != _peers_to_dump.end()) { state_i->second->start_dump(); } // Make sure the iterator no longer points at a trie that may go away. typename BgpTrie::iterator empty; _route_iterator = empty; typename RefTrie >::iterator aggr_empty; _aggr_iterator = aggr_empty; _route_iterator_is_valid = false; _routes_dumped_on_current_peer = false; if (_current_peer == _peers_to_dump.end()) return false; return true; } /** * peering_is_down is called on DumpTable startup to indicate peerings * that already had DeletionTables in progress. */ template void DumpIterator::peering_is_down(const PeerHandler *peer, uint32_t genid) { XLOG_ASSERT(peer != _peer); debug_msg("peering_is_down %p genid %d\n", peer, XORP_INT_CAST(genid)); /* * first we must locate the appropriate state for this peer */ typename map * >::iterator state_i; state_i = _peers.find(peer); if (state_i == _peers.end()) { _peers[peer] = new PeerDumpState(peer, DOWN_BEFORE_DUMP, genid); _peers[peer]->set_delete_occurring(genid); return; } /* * what we do depends on the peer state */ switch (state_i->second->status()) { case STILL_TO_DUMP: case CURRENTLY_DUMPING: case DOWN_BEFORE_DUMP: state_i->second->set_delete_occurring(genid); return; case DOWN_DURING_DUMP: case COMPLETELY_DUMPED: case NEW_PEER: case FIRST_SEEN_DURING_DUMP: XLOG_UNREACHABLE(); //only called at startup return; } XLOG_UNREACHABLE(); } template void DumpIterator::peering_went_down(const PeerHandler *peer, uint32_t genid) { XLOG_ASSERT(peer != _peer); /* * first we must locate the appropriate state for this peer */ typename map * >::iterator state_i; state_i = _peers.find(peer); XLOG_ASSERT(state_i != _peers.end()); /* * what we do depends on the peer state */ switch (state_i->second->status()) { case STILL_TO_DUMP: state_i->second->set_down(genid); return; case CURRENTLY_DUMPING: if (_routes_dumped_on_current_peer) { state_i->second->set_down_during_dump(_last_dumped_net, genid); } else { state_i->second->set_down(genid); } next_peer(); return; case DOWN_DURING_DUMP: case DOWN_BEFORE_DUMP: // it went down before - we don't care about it going down again. return; case COMPLETELY_DUMPED: // we've finished with it - we don't care about it going down now. return; case NEW_PEER: case FIRST_SEEN_DURING_DUMP: // nothing to do here. return; } XLOG_UNREACHABLE(); } template void DumpIterator::peering_down_complete(const PeerHandler *peer, uint32_t genid) { XLOG_ASSERT(peer != _peer); /* * first we must locate the appropriate state for this peer */ typename map * >::iterator state_i; state_i = _peers.find(peer); XLOG_ASSERT(state_i != _peers.end()); state_i->second->set_delete_complete(genid); return; } template bool DumpIterator::waiting_for_deletion_completion() const { typename map * >::const_iterator i; bool wait = false; for (i = _peers.begin(); i != _peers.end() && wait == false; i++) { if (i->second->delete_complete() == false) { wait = true; break; } switch (i->second->status()) { case STILL_TO_DUMP: case CURRENTLY_DUMPING: wait = true; break; case DOWN_DURING_DUMP: case DOWN_BEFORE_DUMP: case COMPLETELY_DUMPED: case NEW_PEER: case FIRST_SEEN_DURING_DUMP: //don't care break; } } return wait; } template void DumpIterator::peering_came_up(const PeerHandler *peer, uint32_t genid) { XLOG_ASSERT(peer != _peer); /* * first we must locate the appropriate state for this peer */ typename map * >::iterator state_i; state_i = _peers.find(peer); if (state_i == _peers.end()) { // we've not heard about this one. _peers[peer] = new PeerDumpState(peer, NEW_PEER, genid); return; } switch (state_i->second->status()) { case STILL_TO_DUMP: case CURRENTLY_DUMPING: XLOG_UNREACHABLE(); case DOWN_DURING_DUMP: case DOWN_BEFORE_DUMP: case COMPLETELY_DUMPED: case NEW_PEER: // We don't care about these. return; case FIRST_SEEN_DURING_DUMP: // Anything prior to this must be obsolete data, but now the // peer has actually come up properly. We need to record it as // a new peer now. _peers.erase(state_i); _peers[peer] = new PeerDumpState(peer, NEW_PEER, genid); return; } } template bool DumpIterator::route_change_is_valid(const PeerHandler* origin_peer, const IPNet& net, uint32_t genid, RouteQueueOp op) { /* route_change_is_valid is called to determine whether or not a route add, delete, or replace should be passed through to the peer. The answer depends on how far we've got in the route dump process. true indicates that the change should be propagated downstream; false indicates it shouldn't because the dump will get to this eventually */ /* If we've not yet dumped this peer, but it is in our list of peers to dump, then we return false, because we'll get to it later. If this route change is on the peer we're currently dumping, then the change is valid if we've passed that point in the route space, otherwise we'll get to it in due course. If we've already dumped this peer, then the change is if it's genid is at least as new as the genid when we dumped the peer. Otherwise the change may be an old background deletion finally getting round to deleting the route. */ /* This is complicated by peers that come up and down during the dump process. If the peer is one that came up after we started dumping, then normally the route change will be valid, but it could be invalid if it has an older GenID that the GenID of the peer when it came up. This might happen because there's a DeletionTable doing a background deletion for routes from before we started the dump. If the peer is one we've never heard of, then the change must be from a DeletionTable doing a background deletion for routes from before we started the dump. */ debug_msg("peer:%p, pa:%s , gi:%d, op:%d\n", origin_peer, net.str().c_str(), XORP_INT_CAST(genid), XORP_INT_CAST(op)); switch (op) { case RTQUEUE_OP_DELETE: debug_msg("DELETE\n"); cp(1); break; case RTQUEUE_OP_REPLACE_OLD: debug_msg("REPLACE_OLD\n"); cp(2); break; case RTQUEUE_OP_REPLACE_NEW: debug_msg("REPLACE_NEW\n"); cp(3); break; case RTQUEUE_OP_ADD: debug_msg("ADD\n"); cp(4); break; default: XLOG_UNREACHABLE(); } /* * first we must locate the appropriate state for this peer */ typename map * >::iterator state_i; state_i = _peers.find(origin_peer); if (state_i == _peers.end()) { // We have never seen this peer before. this can only happen // for peers that were down when we started the dump, but had // stuff on background tasks in DeletionTable or NextHopLookup. // We never pass these routes downstream. _peers[origin_peer] = new PeerDumpState(origin_peer, FIRST_SEEN_DURING_DUMP, genid); // Don't pass downstream. return false; } if (genid < state_i->second->genid()) { // The route predates anything we know about. We definitely // don't want to pass this downstream - it's an obsolete // route. return false; } switch (state_i->second->status()) { case STILL_TO_DUMP: debug_msg("STILL_TO_DUMP\n"); XLOG_ASSERT(genid == state_i->second->genid()); // Don't pass downstream - we'll dump it later return false; case CURRENTLY_DUMPING: debug_msg("CURRENTLY_DUMPING\n"); XLOG_ASSERT(genid == state_i->second->genid()); // Depends on whether we've dumped this route already. if (_routes_dumped_on_current_peer) { if (net == _last_dumped_net || net < _last_dumped_net) { //We have dumped this route already, so pass downstream. return true; } } // Don't pass downstream - we'll dump it later return false; case DOWN_DURING_DUMP: debug_msg("DOWN_DURING_DUMP\n"); if (genid == state_i->second->genid()) { // The change is from the version of the rib we'd half dumped. debug_msg("last_net: %s, net: %s\n", state_i->second->last_net().str().c_str(), net.str().c_str()); if (net ==state_i->second->last_net() || net < state_i->second->last_net()) { //We have dumped this route already, so pass downstream. return true; } // Don't pass downstream - we hadn't dumped this before it // went down. return false; } // The change is from a later version of the rib, so we must // pass it on downstream. return true; case DOWN_BEFORE_DUMP: debug_msg("DOWN_BEFORE_DUMP\n"); // This peer went down before we'd had a chance to dump it. if (genid == state_i->second->genid()) { // We hadn't dumped these - don't pass downstream any changes. return false; } // The change is from a later version of the rib, so we must // pass it on downstream. return true; case COMPLETELY_DUMPED: debug_msg("COMPLETELY_DUMPED\n"); // We have already dumped this peer, so all changes are valid. return true; case NEW_PEER: debug_msg("NEW_PEER\n"); // This peer came up while we were dumping, and we'd not // previously heard of it. All changes are valid. return true; case FIRST_SEEN_DURING_DUMP: debug_msg("FIRST_SEEN_DURING_DUMP\n"); XLOG_ASSERT(genid == state_i->second->genid()); return false; } XLOG_UNREACHABLE(); } template bool DumpIterator::iterator_got_moved(IPNet new_net) const { /* need to be careful where we call this - the iterator MUST be valid, as we don't sanity check here */ if (_routes_dumped_on_current_peer == false) return false; if (new_net == _last_dumped_net) { return false; } else { XLOG_INFO("iterator has moved; was %s now %s", _last_dumped_net.str().c_str(), new_net.str().c_str()); return true; } } template class DumpIterator; template class DumpIterator; xorp/bgp/rib_ipc_handler.cc0000664000076400007640000004620111540225521016043 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #include "bgp_module.h" #include "libxorp/xlog.h" #include "xrl/interfaces/rib_xif.hh" #include "bgp.hh" #include "rib_ipc_handler.hh" #include "profile_vars.hh" RibIpcHandler::RibIpcHandler(XrlStdRouter& xrl_router, BGPMain& bgp) : PeerHandler("RIBIpcHandler", NULL, NULL, NULL), _ribname(""), _xrl_router(xrl_router), _v4_queue(*this, xrl_router, bgp), #ifdef HAVE_IPV6 _v6_queue(*this, xrl_router, bgp), #endif _fake_unique_id(RIB_IPC_HANDLER_UNIQUE_ID), _fake_id(IPv4::ZERO()) { } RibIpcHandler::~RibIpcHandler() { if(_v4_queue.busy() #ifdef HAVE_IPV6 || _v6_queue.busy() #endif ) XLOG_WARNING("Deleting RibIpcHandler with callbacks pending"); /* ** Flush static routes. */ _plumbing_unicast->flush(this); _plumbing_multicast->flush(this); set_plumbing(NULL, NULL); if (!_ribname.empty()) XLOG_WARNING("Deleting RibIpcHandler while still registered with RIB"); /* ** If would be great to de-register from the RIB here. The problem ** is that if we start a de-register the callback will return to a ** freed data structure. */ } bool RibIpcHandler::register_ribname(const string& r) { if (_ribname == r) return true; string previous_ribname = _ribname; _ribname = r; if (r.empty()) { return unregister_rib(previous_ribname); } XrlRibV0p1Client rib(&_xrl_router); //create our tables //ebgp - v4 //name - "ebgp" //unicast - true //multicast - true rib.send_add_egp_table4(_ribname.c_str(), "ebgp", _xrl_router.class_name(), _xrl_router.instance_name(), true, true, callback(this, &RibIpcHandler::rib_command_done,"add_table")); //ibgp - v4 //name - "ibgp" //unicast - true //multicast - true rib.send_add_egp_table4(_ribname.c_str(), "ibgp", _xrl_router.class_name(), _xrl_router.instance_name(), true, true, callback(this, &RibIpcHandler::rib_command_done,"add_table")); #ifdef HAVE_IPV6 //create our tables //ebgp - v6 //name - "ebgp" //unicast - true //multicast - true rib.send_add_egp_table6(_ribname.c_str(), "ebgp", _xrl_router.class_name(), _xrl_router.instance_name(), true, true, callback(this, &RibIpcHandler::rib_command_done,"add_table")); //ibgp - v6 //name - "ibgp" //unicast - true //multicast - true rib.send_add_egp_table6(_ribname.c_str(), "ibgp", _xrl_router.class_name(), _xrl_router.instance_name(), true, true, callback(this, &RibIpcHandler::rib_command_done,"add_table")); #endif return true; } bool RibIpcHandler::unregister_rib(string ribname) { XrlRibV0p1Client rib(&_xrl_router); //delete our tables //ebgp - v4 //name - "ebgp" //unicast - true //multicast - true rib.send_delete_egp_table4(ribname.c_str(), "ebgp", _xrl_router.class_name(), _xrl_router.instance_name(), true, true, callback(this, &RibIpcHandler::rib_command_done, "delete_table")); //ibgp - v4 //name - "ibgp" //unicast - true //multicast - true rib.send_delete_egp_table4(ribname.c_str(), "ibgp", _xrl_router.class_name(), _xrl_router.instance_name(), true, true, callback(this, &RibIpcHandler::rib_command_done, "delete_table")); #ifdef HAVE_IPV6 //delete our tables //ebgp - v6 //name - "ebgp" //unicast - true //multicast - true rib.send_delete_egp_table6(ribname.c_str(), "ebgp", _xrl_router.class_name(), _xrl_router.instance_name(), true, true, callback(this, &RibIpcHandler::rib_command_done, "delete_table")); //ibgp - v6 //name - "ibgp" //unicast - true //multicast - true rib.send_delete_egp_table6(ribname.c_str(), "ibgp", _xrl_router.class_name(), _xrl_router.instance_name(), true, true, callback(this, &RibIpcHandler::rib_command_done, "delete_table")); #endif return true; } int RibIpcHandler::start_packet() { debug_msg("RibIpcHandler::start packet\n"); return 0; } int RibIpcHandler::add_route(const SubnetRoute &rt, FPAList4Ref& pa_list, bool ibgp, Safi safi) { debug_msg("RibIpcHandler::add_route(IPv4) %p\n", &rt); if (_ribname.empty()) return 0; _v4_queue.queue_add_route(_ribname, ibgp, safi, rt.net(), pa_list->nexthop(), rt.policytags()); return 0; } int RibIpcHandler::replace_route(const SubnetRoute &old_rt, bool old_ibgp, const SubnetRoute &new_rt, bool new_ibgp, FPAList4Ref& pa_list, Safi safi) { debug_msg("RibIpcHandler::replace_route(IPv4) %p %p\n", &old_rt, &new_rt); delete_route(old_rt, pa_list, old_ibgp, safi); add_route(new_rt, pa_list, new_ibgp, safi); return 0; } int RibIpcHandler::delete_route(const SubnetRoute &rt, FPAList4Ref& /*pa_list*/, bool ibgp, Safi safi) { debug_msg("RibIpcHandler::delete_route(IPv4) %p\n", &rt); if (_ribname.empty()) return 0; _v4_queue.queue_delete_route(_ribname, ibgp, safi, rt.net()); return 0; } void RibIpcHandler::rib_command_done(const XrlError& error, const char *comment) { debug_msg("callback %s %s\n", comment, error.str().c_str()); if(XrlError::OKAY() != error) { XLOG_WARNING("callback: %s %s", comment, error.str().c_str()); } } PeerOutputState RibIpcHandler::push_packet() { debug_msg("RibIpcHandler::push packet\n"); #if 0 if(_v4_queue.busy() || _v6_queue.busy()) { debug_msg("busy\n"); return PEER_OUTPUT_BUSY; } debug_msg("not busy\n"); #endif return PEER_OUTPUT_OK; } bool RibIpcHandler::originate_route(const OriginType origin, const ASPath& aspath, const IPv4Net& nlri, const IPv4& next_hop, const bool& unicast, const bool& multicast, const PolicyTags& policy_tags) { debug_msg("origin %d aspath %s nlri %s next hop %s unicast %d" " multicast %d\n", origin, aspath.str().c_str(), nlri.str().c_str(), next_hop.str().c_str(), unicast, multicast); FPAList4Ref pa_list; try { /* ** Construct the path attribute list. */ pa_list = new FastPathAttributeList(next_hop, aspath, origin); } catch (const XorpException& e) { XLOG_WARNING("WARNING: Exception in originate_route: %s\n", e.str().c_str()); // Returning false may cause more trouble than it's worth.. return true; } /* ** Add a local pref for I-BGP peers. */ LocalPrefAttribute local_pref_att(LocalPrefAttribute::default_value()); pa_list->add_path_attribute(local_pref_att); /* ** Inject the message into the plumbing. */ if (unicast) { _plumbing_unicast->add_route(nlri, pa_list, policy_tags, this); _plumbing_unicast->push(this); } if (multicast) { _plumbing_multicast->add_route(nlri, pa_list, policy_tags, this); _plumbing_multicast->push(this); } return true; } bool RibIpcHandler::withdraw_route(const IPv4Net& nlri, const bool& unicast, const bool& multicast) { debug_msg("nlri %s unicast %d multicast %d\n", nlri.str().c_str(), unicast, multicast); // XXX: bug... wrong function called #if 0 /* ** Create a subnet route */ SubnetRoute* msg_route = new SubnetRoute(nlri, 0, NULL); /* ** Make an internal message. */ InternalMessage msg(msg_route, this, GENID_UNKNOWN); /* ** Inject the message into the plumbing. */ #endif if (unicast) { _plumbing_unicast->delete_route(nlri, this); _plumbing_unicast->push(this); } if (multicast) { _plumbing_multicast->delete_route(nlri, this); _plumbing_multicast->push(this); } // msg_route->unref(); return true; } template XrlQueue::XrlQueue(RibIpcHandler& rib_ipc_handler, XrlStdRouter& xrl_router, BGPMain& bgp) : _rib_ipc_handler(rib_ipc_handler), _xrl_router(xrl_router), _bgp(bgp), _flying(0), _flow_controlled(false) { } template EventLoop& XrlQueue::eventloop() const { return _rib_ipc_handler.eventloop(); } template void XrlQueue::queue_add_route(string ribname, bool ibgp, Safi safi, const IPNet& net, const A& nexthop, const PolicyTags& policytags) { Queued q; PROFILE(if (_bgp.profile().enabled(profile_route_rpc_in)) _bgp.profile().log(profile_route_rpc_in, c_format("add %s", net.str().c_str()))); q.add = true; q.ribname = ribname; q.ibgp = ibgp; q.safi = safi; q.net = net; q.nexthop = nexthop; q.comment = c_format("add_route: ribname %s %s safi %d net %s nexthop %s", ribname.c_str(), ibgp ? "ibgp" : "ebgp", safi, net.str().c_str(), nexthop.str().c_str()); q.policytags = policytags; _xrl_queue.push_back(q); start(); } template void XrlQueue::queue_delete_route(string ribname, bool ibgp, Safi safi, const IPNet& net) { Queued q; PROFILE(if (_bgp.profile().enabled(profile_route_rpc_in)) _bgp.profile().log(profile_route_rpc_in, c_format("delete %s", net.str().c_str()))); q.add = false; q.ribname = ribname; q.ibgp = ibgp; q.safi = safi; q.net = net; q.comment = c_format("delete_route: ribname %s %s safi %d net %s", ribname.c_str(), ibgp ? "ibgp" : "ebgp", safi, net.str().c_str()); _xrl_queue.push_back(q); start(); } template bool XrlQueue::busy() { return 0 != _flying; } template void XrlQueue::start() { // If we are currently busy don't attempt to send any more XRLs. #if 0 if (busy()) return; #else if (flow_controlled()) return; #endif // Now there are no outstanding XRLs try and send as many of the queued // route commands as possible as possible. for(;;) { debug_msg("queue length %u\n", XORP_UINT_CAST(_xrl_queue.size())); if(_xrl_queue.empty()) { debug_msg("Output no longer busy\n"); #if 0 _rib_ipc_handler.output_no_longer_busy(); #endif return; } typename deque::Queued>::const_iterator qi; qi = _xrl_queue.begin(); XLOG_ASSERT(qi != _xrl_queue.end()); Queued q = *qi; const char *bgp = q.ibgp ? "ibgp" : "ebgp"; bool sent = sendit_spec(q, bgp); if (sent) { _flying++; _xrl_queue.pop_front(); if (flow_controlled()) return; continue; } // We expect that the send may fail if the socket buffer is full. // It should therefore be the case that we have some route // adds/deletes in flight. If _flying is zero then something // unexpected has happended. We have no outstanding sends and // still its gone to poo. if (0 == _flying) XLOG_WARNING("No XRLs in flight, however send could not be scheduled"); // We failed to send the last XRL. Don't attempt to send any more. return; } } template<> bool XrlQueue::sendit_spec(Queued& q, const char *bgp) { bool sent; bool unicast = false; bool multicast = false; switch(q.safi) { case SAFI_UNICAST: unicast = true; break; case SAFI_MULTICAST: multicast = true; break; } XrlRibV0p1Client rib(&_xrl_router); if(q.add) { debug_msg("adding route from %s peer to rib\n", bgp); PROFILE(if (_bgp.profile().enabled(profile_route_rpc_out)) _bgp.profile().log(profile_route_rpc_out, c_format("add %s", q.net.str().c_str()))); sent = rib.send_add_route4(q.ribname.c_str(), bgp, unicast, multicast, q.net, q.nexthop, /*metric*/0, q.policytags.xrl_atomlist(), callback(this, &XrlQueue::route_command_done, q.comment)); // if (!sent) // XLOG_WARNING("scheduling add route %s failed", // q.net.str().c_str()); } else { debug_msg("deleting route from %s peer to rib\n", bgp); PROFILE(if (_bgp.profile().enabled(profile_route_rpc_out)) _bgp.profile().log(profile_route_rpc_out, c_format("delete %s", q.net.str().c_str()))); sent = rib.send_delete_route4(q.ribname.c_str(), bgp, unicast, multicast, q.net, ::callback(this, &XrlQueue::route_command_done, q.comment)); // if (!sent) // XLOG_WARNING("scheduling delete route %s failed", // q.net.str().c_str()); } return sent; } template void XrlQueue::route_command_done(const XrlError& error, const string comment) { _flying--; debug_msg("callback %s %s\n", comment.c_str(), error.str().c_str()); switch (error.error_code()) { case OKAY: break; case REPLY_TIMED_OUT: // We should really be using a reliable transport where // this error cannot happen. But it has so lets retry if we can. XLOG_WARNING("callback: %s %s", comment.c_str(), error.str().c_str()); break; case RESOLVE_FAILED: case SEND_FAILED: case SEND_FAILED_TRANSIENT: case NO_SUCH_METHOD: XLOG_ERROR("callback: %s %s", comment.c_str(), error.str().c_str()); break; case NO_FINDER: // XXX - Temporarily code dump if this condition occurs. XLOG_FATAL("NO FINDER"); _bgp.finder_death(__FILE__, __LINE__); break; case BAD_ARGS: XLOG_FATAL("callback: %s %s", comment.c_str(), error.str().c_str()); break; case COMMAND_FAILED: XLOG_ERROR("callback: %s %s", comment.c_str(), error.str().c_str()); break; case INTERNAL_ERROR: XLOG_FATAL("callback: %s %s", comment.c_str(), error.str().c_str()); break; } // Fire of more requests. start(); } template class XrlQueue; /** IPv6 stuff */ #ifdef HAVE_IPV6 int RibIpcHandler::add_route(const SubnetRoute& rt, FPAList6Ref& pa_list, bool ibgp, Safi safi) { debug_msg("RibIpcHandler::add_route(IPv6) %p\n", &rt); if (_ribname.empty()) return 0; _v6_queue.queue_add_route(_ribname, ibgp, safi, rt.net(), pa_list->nexthop(), rt.policytags()); return 0; } int RibIpcHandler::replace_route(const SubnetRoute &old_rt, bool old_ibgp, const SubnetRoute &new_rt, bool new_ibgp, FPAList6Ref& pa_list, Safi safi) { debug_msg("RibIpcHandler::replace_route(IPv6) %p %p\n", &old_rt, &new_rt); delete_route(old_rt, pa_list, old_ibgp, safi); add_route(new_rt, pa_list, new_ibgp, safi); return 0; } int RibIpcHandler::delete_route(const SubnetRoute& rt, FPAList6Ref& /*pa_list*/, bool ibgp, Safi safi) { debug_msg("RibIpcHandler::delete_route(IPv6) %p\n", &rt); UNUSED(rt); if (_ribname.empty()) return 0; _v6_queue.queue_delete_route(_ribname, ibgp, safi, rt.net()); return 0; } bool RibIpcHandler::originate_route(const OriginType origin, const ASPath& aspath, const IPv6Net& nlri, const IPv6& next_hop, const bool& unicast, const bool& multicast, const PolicyTags& policy_tags) { XLOG_WARNING("origin %d aspath %s nlri %s next hop %s unicast %d" " multicast %d\n", origin, aspath.str().c_str(), nlri.str().c_str(), next_hop.str().c_str(), unicast, multicast); /* ** Construct the path attribute list. */ FPAList6Ref pa_list; try { pa_list = new FastPathAttributeList(next_hop, aspath, origin); } catch (const XorpException& e) { XLOG_WARNING("WARNING: Exception in originate_route(v6): %s\n", e.str().c_str()); // Returning false may cause more trouble than it's worth.. return true; } /* ** Add a local pref for I-BGP peers. */ LocalPrefAttribute local_pref_att(LocalPrefAttribute::default_value()); pa_list->add_path_attribute(local_pref_att); /* ** Inject the message into the plumbing. */ if (unicast) { _plumbing_unicast->add_route(nlri, pa_list, policy_tags, this); _plumbing_unicast->push(this); } if (multicast) { _plumbing_multicast->add_route(nlri, pa_list, policy_tags, this); _plumbing_multicast->push(this); } return true; } bool RibIpcHandler::withdraw_route(const IPv6Net& nlri, const bool& unicast, const bool& multicast) { debug_msg("nlri %s unicast %d multicast %d\n", nlri.str().c_str(), unicast, multicast); // XXX: bug... wrong function called #if 0 /* ** Create a subnet route */ SubnetRoute* msg_route = new SubnetRoute(nlri, 0, NULL); /* ** Make an internal message. */ InternalMessage msg(msg_route, this, GENID_UNKNOWN); /* ** Inject the message into the plumbing. */ #endif if (unicast) { _plumbing_unicast->delete_route(nlri, this); _plumbing_unicast->push(this); } if (multicast) { _plumbing_multicast->delete_route(nlri, this); _plumbing_multicast->push(this); } // msg_route->unref(); return true; } template<> bool XrlQueue::sendit_spec(Queued& q, const char *bgp) { bool sent; bool unicast = false; bool multicast = false; switch(q.safi) { case SAFI_UNICAST: unicast = true; break; case SAFI_MULTICAST: multicast = true; break; } XrlRibV0p1Client rib(&_xrl_router); if(q.add) { debug_msg("adding route from %s peer to rib\n", bgp); PROFILE(if (_bgp.profile().enabled(profile_route_rpc_out)) _bgp.profile().log(profile_route_rpc_out, c_format("add %s", q.net.str().c_str()))); sent = rib.send_add_route6(q.ribname.c_str(), bgp, unicast, multicast, q.net, q.nexthop, /*metric*/0, q.policytags.xrl_atomlist(), callback(this, &XrlQueue::route_command_done, q.comment)); // if (!sent) // XLOG_WARNING("scheduling add route %s failed", // q.net.str().c_str()); } else { debug_msg("deleting route from %s peer to rib\n", bgp); PROFILE(if (_bgp.profile().enabled(profile_route_rpc_out)) _bgp.profile().log(profile_route_rpc_out, c_format("delete %s", q.net.str().c_str()))); sent = rib.send_delete_route6(q.ribname.c_str(), bgp, unicast, multicast, q.net, callback(this, &XrlQueue::route_command_done, q.comment)); // if (!sent) // XLOG_WARNING("scheduling delete route %s failed", // q.net.str().c_str()); } return sent; } template class XrlQueue; #endif //ipv6 xorp/bgp/README0000664000076400007640000000337411421137511013313 0ustar greearbgreearb# # $XORP: xorp/bgp/README,v 1.14 2007/03/20 21:54:37 atanu Exp $ # Border Gateway Protocol (BGP) Implementation ============================================ This directory contains the XORP implementation of the BGP protocol. Configuration ============= BGP like most Xorp processes does not take its configuration parameters from the command line. Its parameters are provided via XRLs. At the very least a BGP process must be provided with its AS number and its ID. Startup ======= In normal operation, bgp would be started by the XORP router manager process, not directly from the command line. For testing and debugging purposes, BGP can be manually started. The Xorp BGP process assumes that a number of programs will be running before it is started: ../libxipc/xorp_finder (or xorp_rtrmgr which includes a xorp_finder) ../rib/xorp_rib ../fea/xorp_fea Documentation ============= The BGP design architecture and code structure are described in: ${XORP}/docs/bgp/ The programming documentation is in: ${XORP}/docs/kdoc/html/bgp/ Testing ======= As with most XORP processes, running "gmake check" in the bgp directory will run a set of validation checks. Any new functionality committed to bgp must come with it's own set of validation checks. Status ====== July 2008: The core functionality of BGP is basically complete. We have a reasonable number of test suites available, but nowhere near enough to be sure of correct operation in all cases. - BGP is totally integrated with the policy code. - Confederations are supported. - Route Reflection is supported. - Communities are supported via policy. - IPv6 is supported. - Support for four-octet AS number space. Known problems: - Once BGP selects a route it is taking too long to install it in the kernel. xorp/bgp/route_table_filter.hh0000664000076400007640000004017311540224220016620 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/bgp/route_table_filter.hh,v 1.33 2008/11/08 06:14:39 mjh Exp $ #ifndef __BGP_ROUTE_TABLE_FILTER_HH__ #define __BGP_ROUTE_TABLE_FILTER_HH__ #include "route_table_base.hh" #include "next_hop_resolver.hh" #include "peer_data.hh" /** * @short Base class for a single filter within FilterTable's filter bank. * * FilterTable implements a bank of filters for modifying or dropping * routes. Each filter within such a filter bank is a BGPRouteFilter. * BGPRouteFilter is a generic filter, and so needs specializing in a * subclass to actually implement a filter. */ template class BGPRouteFilter { public: BGPRouteFilter() {}; virtual ~BGPRouteFilter() {} /* "modified" is needed because we need to know whether we should free the rtmsg or not if we modify the route. If this filterbank has modified it, and then modifies it again, then we should free the rtmsg. Otherwise it's the responsibility of whoever created the rtmsg*/ virtual bool filter(InternalMessage& rtmsg) const = 0; protected: #if 0 void propagate_flags(const InternalMessage *rtmsg, InternalMessage *new_rtmsg) const; void propagate_flags(const SubnetRoute& route, SubnetRoute& new_route) const; virtual void drop_message(const InternalMessage *rtmsg, bool &modified) const ; #endif private: }; /** * @short filters out aggregate routes depending whether on IBGP or EBGP * outbound branch. */ template class AggregationFilter : public BGPRouteFilter { public: AggregationFilter(bool is_ibgp); bool filter(InternalMessage& rtmsg) const ; private: bool _is_ibgp; }; /** * @short BGPRouteFilter that drops routes that have a particular AS * in their AS path. * * SimpleASFilter is a BGPRouteFilter that drops routes that have a * particular AS in their AS path. This is typically used in loop * prevention, where an inbound filter from an EBGP peer drops routes * that contain our own AS number * * This works on both regular EBGP and CONFEDERATION EBGP sessions */ template class SimpleASFilter : public BGPRouteFilter { public: SimpleASFilter(const AsNum &as_num); bool filter(InternalMessage& rtmsg) const ; private: AsNum _as_num; }; /** * @short BGPRouteFilter that drops routes with this routers * ORIGINATOR_ID or CLUSTER_ID. * * RRInputFilter is a BGPRouteFilter that drops with this routers * ORIGINATOR_ID or CLUSTER_ID. An inbound filter that is configured * on IBGP peerings when this router is a route reflector. * */ template class RRInputFilter : public BGPRouteFilter { public: RRInputFilter(IPv4 bgp_id, IPv4 cluster_id); bool filter(InternalMessage& rtmsg) const ; private: IPv4 _bgp_id; IPv4 _cluster_id; }; /** * @short BGPRouteFilter that prepends an AS to the AS path. * * ASPrependFilter is a BGPRouteFilter that prepends an AS to the AS * path. This is typically used when sending a route to an EBGP peer * to add our own AS number to the path. * * This works on both regular EBGP and CONFEDERATION EBGP sessions */ template class ASPrependFilter : public BGPRouteFilter { public: ASPrependFilter(const AsNum &as_num, bool is_confederation_peer); bool filter(InternalMessage& rtmsg) const; private: AsNum _as_num; bool _is_confederation_peer; }; /** * @short BGPRouteFilter that changes the nexthop attribute in a route. * * NexthopRewriteFilter is a BGPRouteFilter that changes the nexthop * attribute in a route passing though the filter. A typicaly use is * when passing a route to an EBGP peer, we change the nexthop * attribute to be our own IP address on the appropriate interface to * that peer. If the EBGP peer is on the the same subnet as the router * and the NEXT_HOP is on the same subnet the NEXT_HOP should not be * rewritten. */ template class NexthopRewriteFilter : public BGPRouteFilter { public: NexthopRewriteFilter(const A &local_nexthop, bool directly_connected, const IPNet &subnet); bool filter(InternalMessage& rtmsg) const ; private: A _local_nexthop; bool _directly_connected; IPNet _subnet; }; /** * @short BGPRouteFilter rewrites the nexthop if it matches the peers * address. * * NexthopPeerCheckFilter is a BGPRouteFilter that checks that the * nexthop is not equal to the peers address. If the nexthop is equal * to the peers address then the nexthop is rewritten to the routers * nexthop address. */ template class NexthopPeerCheckFilter : public BGPRouteFilter { public: NexthopPeerCheckFilter(const A &local_nexthop, const A &peer_address); bool filter(InternalMessage& rtmsg) const ; private: A _local_nexthop; A _peer_address; }; /** * @short BGPRouteFilter that drops routes that came to us from an IBGP peer. * * IBGPLoopFilter is a BGPRouteFilter that drops routes that * came to us from an IBGP peer. Typically it is used in a outgoing * filter on a branch to another IBGP peer, and prevents routes coming * from one IBGP peer from being forwarded to another IBGP peer. */ template class IBGPLoopFilter : public BGPRouteFilter { public: IBGPLoopFilter(); bool filter(InternalMessage& rtmsg) const ; private: }; /** * @short BGPRouteFilter that drops or reflects routes from an IBGP * peer. Add the originator ID and the cluster ID. * * RRIBGPLoopFilter is a BGPRouteFilter that is a replacement for the * IBGLoopFilter when the router is a route reflector. * Incoming route came from: * E-BGP peer, route is passed. * I-BGP peer, route is passed if this is a route reflector client. * I-BGP client peer, route is passed to all types of client. */ template class RRIBGPLoopFilter : public BGPRouteFilter { public: RRIBGPLoopFilter(bool rr_client, IPv4 bgp_id, IPv4 cluster_id); bool filter(InternalMessage& rtmsg) const ; private: bool _rr_client; IPv4 _bgp_id; IPv4 _cluster_id; }; /** * @short RRPurge Remove ORIGINATOR_ID and CLUSTER_LIST attributes. * * Outbound filter on E-BGP peers to remove ORIGINATOR_ID and * CLUSTER_LIST attributes (Route Reflection attributes that should * never go to an E-BGP peer). */ template class RRPurgeFilter : public BGPRouteFilter { public: RRPurgeFilter(); bool filter(InternalMessage& rtmsg) const ; private: }; /** * @short BGPRouteFilter that inserts a LocalPref attribute. * * LocalPrefInsertionFilter is a BGPRouteFilter that inserts a LocalPref * attribute into routes passing though the filter. This is typically * used in an incoming filter bank from an EBGP peer to add LocalPref. */ template class LocalPrefInsertionFilter : public BGPRouteFilter { public: LocalPrefInsertionFilter(uint32_t default_local_pref); bool filter(InternalMessage& rtmsg) const ; private: uint32_t _default_local_pref; }; /** * @short BGPRouteFilter that deletes a LocalPref attribute. * * LocalPrefRemovalFilter is a BGPRouteFilter that deletes the * LocalPref attribute (if present) from routes passing though the * filter. This is typically used in an outgoing filter bank to an * EBGP peer. */ template class LocalPrefRemovalFilter : public BGPRouteFilter { public: LocalPrefRemovalFilter(); bool filter(InternalMessage& rtmsg) const ; private: }; /** * @short BGPRouteFilter that inserts a MED attribute. * * MEDInsertionFilter is a BGPRouteFilter that inserts a MED attribute * into routes passing though the filter. This is typically used in * an outgoing filter bank to an EBGP peer. */ template class MEDInsertionFilter : public BGPRouteFilter { public: MEDInsertionFilter(NextHopResolver& next_hop_resolver); bool filter(InternalMessage& rtmsg) const ; private: NextHopResolver& _next_hop_resolver; }; /** * @short BGPRouteFilter that removes a MED attribute. * * MEDInsertionFilter is a BGPRouteFilter that removes a MED attribute * from routes passing though the filter. This is typically used in * an outgoing filter bank to an EBGP peer, before we add our own MED. */ template class MEDRemovalFilter : public BGPRouteFilter { public: MEDRemovalFilter(); bool filter(InternalMessage& rtmsg) const ; private: }; /** * @short BGPRouteFilter that processes well-known communities. * * KnownCommunityFilter is a BGPRouteFilter that drops routes with * certain well-known communities. See RFC 1997 and the IANA registry. */ template class KnownCommunityFilter : public BGPRouteFilter { public: KnownCommunityFilter(PeerType peer_type); bool filter(InternalMessage& rtmsg) const ; private: PeerType _peer_type; }; /** * @short BGPRouteFilter that processes unknown attributes. * * UnknownFilter is a BGPRouteFilter that processes unknown attributes * according to their flags. It is typically used in an outgoing * filter bank to avoid incorrectly passing unknown non-transitive * attributes on to a peer, and to set the partial but on unknown * transitive attributes. */ template class UnknownFilter : public BGPRouteFilter { public: UnknownFilter(); bool filter(InternalMessage& rtmsg) const ; private: }; /** * Perform any filter operations that are required for routes that we * originate. * * Currently this only involves prepending our AS number on IBGP * peers. We assume that on EBGP peers the AS number has already been * prepended. * */ template class OriginateRouteFilter : public BGPRouteFilter { public: OriginateRouteFilter(const AsNum &as_num, PeerType peer_type); bool filter(InternalMessage& rtmsg) const ; private: AsNum _as_num; PeerType _peer_type; }; /** * @short specific version of a static route filter * * When a peering is reconfigured, we need to change the filter, but * still maintain consistency for downstream tables. We keep the old * filter around until there are no more routes that need it, and only * then delete it. FilterVersion holds a specific version of a static * route filter bank. */ template class FilterVersion { public: FilterVersion(NextHopResolver& next_hop_resolver); ~FilterVersion(); void set_genid(uint32_t genid) {_genid = genid;} int add_aggregation_filter(bool is_ibgp); int add_simple_AS_filter(const AsNum &asn); int add_route_reflector_input_filter(IPv4 bgp_id, IPv4 cluster_id); int add_AS_prepend_filter(const AsNum &asn, bool is_confederation_peer); int add_nexthop_rewrite_filter(const A& nexthop, bool directly_connected, const IPNet &subnet); int add_nexthop_peer_check_filter(const A& nexthop, const A& peer_address); int add_ibgp_loop_filter(); int add_route_reflector_ibgp_loop_filter(bool client, IPv4 bgp_id, IPv4 cluster_id); int add_route_reflector_purge_filter(); int add_localpref_insertion_filter(uint32_t default_local_pref); int add_localpref_removal_filter(); int add_med_insertion_filter(); int add_med_removal_filter(); int add_known_community_filter(PeerType peer_type); int add_unknown_filter(); int add_originate_route_filter(const AsNum &asn, PeerType peer_type); bool apply_filters(InternalMessage& rtmsg, int ref_change); int ref_count() const {return _ref_count;} uint32_t genid() const {return _genid;} bool used() const {return _used;} private: uint32_t _genid; bool _used; list *> _filters; int _ref_count; NextHopResolver& _next_hop_resolver; }; /** * @short specialized BGPRouteTable implementing a filter bank to * modify or drop routes. * * The XORP BGP is internally implemented as a set of pipelines * consisting of a series of BGPRouteTables. Each pipeline receives * routes from a BGP peer, stores them, and applies filters to them to * modify the routes. Then the pipelines converge on a single * decision process, which decides which route wins amongst possible * alternative routes. After decision, the winning routes fanout * again along a set of pipelines, again being filtered, before being * transmitted to peers. * * FilterTable is a BGPRouteTable that can hold banks of route * filters. Normally FilterTable propagates add_route, * delete_route and the response to lookup_route directly through from * the parent to the child. A route filter can cause these to not be * be propagated, or can modify the attributes of the route in the * message. * * Typically there are two FilterTables for each peer, one in the * input branch from that peer, and one in the output branch to that * peer. A FilterTable does not store the routes it modifies. Thus * is is normally followed by a CacheTable which stores those routes * for later use. */ template class FilterTable : public BGPRouteTable { public: FilterTable(string tablename, Safi safi, BGPRouteTable *parent, NextHopResolver& next_hop_resolver); ~FilterTable(); void reconfigure_filter(); int add_route(InternalMessage &rtmsg, BGPRouteTable *caller); int replace_route(InternalMessage &old_rtmsg, InternalMessage &new_rtmsg, BGPRouteTable *caller); int delete_route(InternalMessage &rtmsg, BGPRouteTable *caller); int route_dump(InternalMessage &rtmsg, BGPRouteTable *caller, const PeerHandler *dump_peer); int push(BGPRouteTable *caller); const SubnetRoute *lookup_route(const IPNet &net, uint32_t& genid, FPAListRef& pa_list) const; void route_used(const SubnetRoute* route, bool in_use); RouteTableType type() const {return FILTER_TABLE;} string str() const; /* mechanisms to implement flow control in the output plumbing */ bool get_next_message(BGPRouteTable *next_table); int add_aggregation_filter(bool is_ibgp); int add_simple_AS_filter(const AsNum &asn); int add_route_reflector_input_filter(IPv4 bgp_id, IPv4 cluster_id); int add_AS_prepend_filter(const AsNum &asn, bool is_confederation_peer); int add_nexthop_rewrite_filter(const A& nexthop, bool directly_connected, const IPNet &subnet); int add_nexthop_peer_check_filter(const A& nexthop, const A& peer_address); int add_ibgp_loop_filter(); int add_route_reflector_ibgp_loop_filter(bool client, IPv4 bgp_id, IPv4 cluster_id); int add_route_reflector_purge_filter(); int add_localpref_insertion_filter(uint32_t default_local_pref); int add_localpref_removal_filter(); int add_med_insertion_filter(); int add_med_removal_filter(); int add_known_community_filter(PeerType peer_type); int add_unknown_filter(); int add_originate_route_filter(const AsNum &asn, PeerType peer_type); void do_versioning() {_do_versioning = true;} private: bool apply_filters(InternalMessage& rtmsg, int ref_change); bool apply_filters(InternalMessage& rtmsg) const; void drop_message(const InternalMessage *rtmsg) const ; map * > _filter_versions; set _deleted_filters; /* kept as a sanity check */ FilterVersion* _current_filter; NextHopResolver& _next_hop_resolver; bool _do_versioning; }; #endif // __BGP_ROUTE_TABLE_FILTER_HH__ xorp/bgp/iptuple.hh0000664000076400007640000001133711421137511014434 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/bgp/iptuple.hh,v 1.16 2008/10/02 21:56:16 bms Exp $ #ifndef __BGP_IPTUPLE_HH__ #define __BGP_IPTUPLE_HH__ #include "bgp_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/ipvx.hh" class UnresolvableHost : public XorpReasonedException { public: UnresolvableHost(const char* file, size_t line, const string init_why = "") : XorpReasonedException("UnresolvableHost", file, line, init_why) {} }; class AddressFamilyMismatch : public XorpReasonedException { public: AddressFamilyMismatch(const char* file, size_t line, const string init_why = "") : XorpReasonedException("AddressFamilyMismatch", file, line, init_why) {} }; /** * Store the Local Interface, IP, Local Server Port, Peer IP and * Peer Server Port tuple. * * Also create the socket buffers to be used by the socket code. All the IP * protocol family differences can therefore be hidden in here. * * The endpoint addresses can be presented as either numeric addresses * or symbolic addresses. Symbolic addresses are converted to numeric * addresses and held as such. The symbolic address are kept to aid * debugging but are never accessed after the initial conversion to * the numeric form. Only in the constructor is there a possibility of * a DNS / Yellow pages interaction taking place. After this as we are * dealing with IP addresses there should be no danger. */ class Iptuple { public: Iptuple(); Iptuple(const char* local_dev, const char *local_addr, uint16_t local_port, const char *peer_addr, uint16_t peer_port) throw(UnresolvableHost,AddressFamilyMismatch); Iptuple(const Iptuple&); Iptuple& operator=(const Iptuple&); void copy(const Iptuple&); bool operator==(const Iptuple&) const; const struct sockaddr *get_local_socket(size_t& len) const; string get_local_addr() const; bool get_local_addr(IPv4& addr) const; bool get_local_addr(IPv6& addr) const; uint16_t get_local_port() const; const struct sockaddr *get_bind_socket(size_t& len) const; const struct sockaddr *get_peer_socket(size_t& len) const; string get_peer_addr() const; const string& get_local_interface() const { return _local_dev; } bool get_peer_addr(IPv4& addr) const; bool get_peer_addr(IPv6& addr) const; uint16_t get_peer_port() const; string str() const; private: void fill_address(const char *addr, uint16_t local_port, struct sockaddr_storage& ss, size_t& len, string& addr_numeric) throw(UnresolvableHost); string _local_dev; // The interface (device) name. NOT IP. string _local_addr; // String representation only for debugging. IP Address. string _peer_addr; // String representation only for debugging. IP Address // For listen(). struct sockaddr_storage _local_sock; // Local socket size_t _local_sock_len; // Length of local socket // For bind() before connect. struct sockaddr_storage _bind_sock; // Bind socket size_t _bind_sock_len; // Length of bind socket // For connect(). struct sockaddr_storage _peer_sock; // Peer socket size_t _peer_sock_len; // Length of peer socket /* ** The local address and the peer address are stored twice once in ** string format and one in IPvX format. Originally the addresses ** were stored only as strings as they are rarely used, however ** the policy code may repeatedly request the peer address in IPv4 ** or IPv6 format so keep it in the internal format for ** performance reasons. */ string _local_address; // Local address in numeric form IPvX _local_address_ipvx; // Local address in IPvX form string _peer_address; // Peer address in numeric form IPvX _peer_address_ipvx; // Peer address in IPvX form /* ** Held in host byte order */ uint16_t _local_port; // Local port uint16_t _peer_port; // Peer port }; #endif // __BGP_IPTUPLE_HH__ xorp/bgp/route_table_policy_ex.hh0000664000076400007640000000370511421137511017332 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/bgp/route_table_policy_ex.hh,v 1.8 2008/10/02 21:56:20 bms Exp $ #ifndef __BGP_ROUTE_TABLE_POLICY_EX_HH__ #define __BGP_ROUTE_TABLE_POLICY_EX_HH__ #include "route_table_policy.hh" /** * @short Export policy tables match neighbor in a different way. * * Export tables will supply a specialized varrw which will match neighbor * against the peer which the advertisement is about to be sent out, rather than * the originating peer. */ template class PolicyTableExport : public PolicyTable { public: /** * @param tablename name of the table. * @param safi the safe. * @param parent parent table. * @param pfs a reference to the global policy filters. * @param neighbor the id of the peer on this output branch. */ PolicyTableExport(const string& tablename, const Safi& safi, BGPRouteTable* parent, PolicyFilters& pfs, const string& neighbor, const A& self); protected: void init_varrw(); private: const string _neighbor; }; #endif // __BGP_ROUTE_TABLE_POLICY_EX_HH__ xorp/bgp/parameter.cc0000664000076400007640000003655311421137511014727 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #include "bgp_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libproto/packet.hh" #include "packet.hh" extern void dump_bytes(const uint8_t *d, size_t l); /* **************** BGPParameter *********************** */ BGPParameter::BGPParameter(uint8_t l, const uint8_t* d) { // Allocate the Data buffer uint8_t *data = new uint8_t[l]; memcpy(data, d, l); _data = data; _length = l; // length is the buffer length including parameter header } BGPParameter::BGPParameter(const BGPParameter& param) { _type = param._type; if (_data != NULL) { _length = param._length; uint8_t *data = new uint8_t[_length]; memcpy(data, param._data, _length); _data = data; } else { _data = NULL; _length = 0; } } void BGPParameter::set_length(int l) { if (_data != NULL) delete[] _data; XLOG_ASSERT(l >= 2 && l < 256); debug_msg("_length set %d\n", l); _length = l; _data = new uint8_t[_length]; _data[1] = _length-2; } /* **************** BGPCapParameter *********************** */ BGPCapParameter::BGPCapParameter() : BGPParameter() { _type = PARAMTYPECAP; _cap_length = 0; } BGPCapParameter::BGPCapParameter(uint8_t l, const uint8_t* d) : BGPParameter(l, d) { _type = PARAMTYPECAP; } BGPCapParameter::BGPCapParameter(const BGPCapParameter& param) : BGPParameter(param) { _cap_code = param._cap_code; _cap_length = param._cap_length; } /* ************** BGPRefreshCapability - ****************** */ BGPRefreshCapability::BGPRefreshCapability() { debug_msg("BGPRefreshCapability() constructor\n"); _cap_code = CAPABILITYREFRESH; _length = 4; _data = new uint8_t[_length]; _old_type_code = false; } BGPRefreshCapability::BGPRefreshCapability(uint8_t l, const uint8_t* d) : BGPCapParameter(l, d) { debug_msg("BGPRefreshCapability(uint8_t, uint8_t*) constructor called\n"); debug_msg("_type %d _length %d (total length %d)\n", _type, _length, _length+2); decode(); } BGPRefreshCapability:: BGPRefreshCapability(const BGPRefreshCapability& param) : BGPCapParameter(param) { _old_type_code = param._old_type_code; if (param._data != NULL) { _length = param._length; uint8_t *p = new uint8_t[_length]; memcpy(p, param._data, _length); _data = p; } else { _length = 0; _data = NULL; } } void BGPRefreshCapability::decode() { /* ** Note: In the normal case this method is called by ** BGPParameter::create, which has already checked the type, ** length and capability fields. It is therefore safe to have ** asserts in here as a sanity check. */ _type = static_cast(*_data); XLOG_ASSERT(_type == PARAMTYPECAP); _length = *(_data+1) + 2; // includes 2 byte header XLOG_ASSERT(_length == 4); _cap_code = static_cast(*(_data+2)); if (_cap_code == CAPABILITYREFRESHOLD) { _old_type_code = true; _cap_code = CAPABILITYREFRESH; } else { _old_type_code = false; } XLOG_ASSERT(_cap_code == CAPABILITYREFRESH); _cap_length = *(_data+3); if (_cap_length > 0) { debug_msg("Throw exception\n"); xorp_throw(CorruptMessage, c_format("Refresh Capability length %d is greater than zero.", _cap_length), OPENMSGERROR, UNSPECIFIED); } } void BGPRefreshCapability::encode() const { debug_msg("Encoding a BGP refresh capability parameter\n"); _data[0] = PARAMTYPECAP; _data[1] = 2; if (_old_type_code) _data[2] = CAPABILITYREFRESHOLD; else _data[2] = CAPABILITYREFRESH; _data[3] = 0; debug_msg("length set as %d\n", _length); } string BGPRefreshCapability::str() const { string s; s = "BGP Refresh Capability"; return s; } /* ************** BGPMultiProtocolCapability - ****************** */ BGPMultiProtocolCapability:: BGPMultiProtocolCapability(Afi afi, Safi safi) : BGPCapParameter() { _cap_code = CAPABILITYMULTIPROTOCOL; _length = 8; _data = new uint8_t[_length]; _address_family = afi; _subsequent_address_family = safi; } BGPMultiProtocolCapability:: BGPMultiProtocolCapability(uint8_t l, const uint8_t* d) : BGPCapParameter(l, d) { debug_msg("BGPMultiProtocolCapability(uint8_t, uint8_t*)\n"); decode(); debug_msg("_type %d _length %d (total length %d) \n", _type, _length, _length+2); } BGPMultiProtocolCapability:: BGPMultiProtocolCapability(const BGPMultiProtocolCapability& param) : BGPCapParameter(param) { _address_family = param._address_family; _subsequent_address_family = param._subsequent_address_family; if (param._data != NULL) { _length = param._length; uint8_t *p = new uint8_t[_length]; memcpy(p, param._data, _length); _data = p; } else { _length = 0; _data = NULL; } } void BGPMultiProtocolCapability::decode() { _type = static_cast(*_data); XLOG_ASSERT(_type == PARAMTYPECAP); // See comment in: // BGPRefreshCapability::decode() _length = *(_data+1) + 2; // includes 2 byte header _cap_code = static_cast(*(_data+2)); XLOG_ASSERT(_cap_code == CAPABILITYMULTIPROTOCOL); _cap_length = *(_data+3); uint16_t afi; afi = *(_data+4); afi <<= 8; afi = *(_data+5); switch(afi) { case AFI_IPV4_VAL: _address_family = AFI_IPV4; break; case AFI_IPV6_VAL: _address_family = AFI_IPV6; break; default: xorp_throw(CorruptMessage, c_format("MultiProtocol Capability unrecognised afi %u", afi), OPENMSGERROR, UNSUPOPTPAR); } debug_msg("address family %d\n", _address_family); uint8_t safi = *(_data+7); switch(safi) { case SAFI_UNICAST_VAL: _subsequent_address_family = SAFI_UNICAST; break; case SAFI_MULTICAST_VAL: _subsequent_address_family = SAFI_MULTICAST; break; default: xorp_throw(CorruptMessage, c_format("MultiProtocol Capability unrecognised safi %u", safi), OPENMSGERROR, UNSUPOPTPAR); } } void BGPMultiProtocolCapability::encode() const { debug_msg("Encoding a BGP multi protocol capability parameter\n"); _data[0] = PARAMTYPECAP; _data[1] = 6; // parameter length _data[2] = CAPABILITYMULTIPROTOCOL; _data[3] = 4; // capability length uint16_t k = htons(static_cast(_address_family)); // AFI memcpy(&_data[4],&k, 2); _data[6] = 0 ; // Reserved _data[7] = _subsequent_address_family ; // SAFI } // bool BGPMultiProtocolCapability::operator==(const BGPParameter& rhs) const { bool BGPMultiProtocolCapability::compare(const BGPParameter& rhs) const { const BGPMultiProtocolCapability *ptr = dynamic_cast(&rhs); if(!ptr) return false; if (_address_family != ptr->get_address_family()) return false; if (_subsequent_address_family != ptr->get_subsequent_address_family_id()) return false; return true; } string BGPMultiProtocolCapability::str() const { return c_format("BGP Multiple Protocol Capability AFI = %d SAFI = %d", _address_family, _subsequent_address_family); } /* ************** BGPMultiRouteCapability - ****************** */ // Unsure of the exact packet format of this capability BGPMultiRouteCapability:: BGPMultiRouteCapability() { _cap_code = CAPABILITYMULTIROUTE; _length = 8; _data = new uint8_t[_length]; } BGPMultiRouteCapability:: BGPMultiRouteCapability(uint8_t l, const uint8_t* d) : BGPCapParameter(l, d) { debug_msg("BGPMultiRouteCapability(uint8_t, uint8_t*)\n"); decode(); debug_msg("_type %d _length %d (total length %d) \n", _type, _length, _length+2); } BGPMultiRouteCapability:: BGPMultiRouteCapability(const BGPMultiRouteCapability& param) : BGPCapParameter(param) { // _address_family = param._address_family; // _subsequent_address_family = param._subsequent_address_family; if (param._data != NULL) { _length = param._length; uint8_t *p = new uint8_t[_length]; memcpy(p, param._data, _length); _data = p; } else { _length = 0; _data = NULL; } } void BGPMultiRouteCapability::decode() { _type = static_cast(*_data); XLOG_ASSERT(_type == PARAMTYPECAP); // See comment in: // BGPRefreshCapability::decode() _length = *(_data+1) + 2; // includes 2 byte header _cap_code = static_cast(*(_data+2)); XLOG_ASSERT(_cap_code == CAPABILITYMULTIROUTE); _cap_length = *(_data+3); // _address_family = ntohs((uint16_t &)*(_data+4)); // _subsequent_address_family = (uint8_t &)*(_data+7); } void BGPMultiRouteCapability::encode() const { debug_msg("Encoding a BGP multi route to host capability parameter\n"); _data[0] = PARAMTYPECAP; _data[1] = 6; _data[2] = CAPABILITYMULTIROUTE; // _data[3] = 4; // _data[4] = htons(_address_family) ; // address family // _data[6] = 0 ; // Reserved // _data[7] = _subsequent_address_family ; // SAFI } /* ************** BGP4ByteASCapability - ****************** */ BGP4ByteASCapability:: BGP4ByteASCapability() { _cap_code = CAPABILITY4BYTEAS; _length = 8; _data = new uint8_t[_length]; } BGP4ByteASCapability:: BGP4ByteASCapability(uint8_t l, const uint8_t* d) : BGPCapParameter(l, d) { debug_msg("BGP4ByteASCapability(uint8_t, uint8_t*)\n"); decode(); debug_msg("_type %d _length %d (total length %d) \n", _type, _length, _length+2); } BGP4ByteASCapability:: BGP4ByteASCapability(const BGP4ByteASCapability& param) : BGPCapParameter(param) { _as4 = param._as4; if (param._data != NULL) { _length = param._length; uint8_t *p = new uint8_t[_length]; memcpy(p, param._data, _length); _data = p; } else { _length = 0; _data = NULL; } } BGP4ByteASCapability:: BGP4ByteASCapability(const AsNum& as) { _as4 = as.as4(); _length = 8; _data = new uint8_t[_length]; encode(); } void BGP4ByteASCapability::decode() { _type = static_cast(*_data); XLOG_ASSERT(_type == PARAMTYPECAP); // See comment in: // BGPRefreshCapability::decode() _length = extract_8(_data + 1) + 2; // includes 2 byte header _cap_code = static_cast(*(_data + 2)); XLOG_ASSERT(_cap_code == CAPABILITY4BYTEAS); _cap_length = extract_8(_data + 3); _as4 = extract_32(_data + 4); } void BGP4ByteASCapability::encode() const { debug_msg("Encoding a BGP 4 byte AS capability parameter\n"); _data[0] = PARAMTYPECAP; _data[1] = 6; _data[2] = CAPABILITY4BYTEAS; _data[3] = 4; uint32_t as = htonl(_as4); memcpy(&_data[4], &as, 4); } string BGP4ByteASCapability::str() const { return c_format("BGP 4 Byte AS Capability AS = %u", _as4); } /************** BGPUnknownCapability - ****************** */ // This is used when we don't understand / support a capability // we are sent. BGPUnknownCapability:: BGPUnknownCapability() { // we shouldn't ever create one of these. // abort() ? _cap_code = CAPABILITYUNKNOWN; _length = 0; _data = NULL; } BGPUnknownCapability:: BGPUnknownCapability(uint8_t l, const uint8_t* d) : BGPCapParameter(l, d) { debug_msg("BGPUnkownCapability(uint8_t, uint8_t*)\n"); decode(); debug_msg("_type %d _length %d (total length %d) \n", _type, _length, _length+2); } BGPUnknownCapability:: BGPUnknownCapability(const BGPUnknownCapability& param) : BGPCapParameter(param) { if (param._data != NULL) { _length = param._length; uint8_t *p = new uint8_t[_length]; memcpy(p, param._data, _length); _data = p; } else { _length = 0; _data = NULL; } } void BGPUnknownCapability::decode() { debug_msg("decoding unknown capability\n"); _type = static_cast(*_data); XLOG_ASSERT(_type == PARAMTYPECAP); // See comment in: // BGPRefreshCapability::decode() _length = (uint8_t)*(_data+1) + 2; // includes 2 byte header // the original capability code _unknown_cap_code = static_cast(*(_data+2)); _cap_code = CAPABILITYUNKNOWN; // hard code capability to be UNKNOWN. // don't check the capability type, since we already know we // don't know what it is. // assert(_cap_code == CAPABILITYUNKNOWN); _cap_length = *(_data+3); // _data pointer holds pointer to the rest of the information // can't decode it further since we don't know what it means. } void BGPUnknownCapability::encode() const { /* debug_msg("Encoding an Unknown capability parameter\n"); _data[0] = PARAMTYPECAP; _data[1] = 8; _data[2] = CAPABILITYUNKNOWN; */ // should never do this XLOG_FATAL("we shouldn't ever send an unknown capability\n"); } BGPParameter * BGPParameter::create(const uint8_t* d, uint16_t max_len, size_t& len) throw(CorruptMessage) { XLOG_ASSERT(d != 0); // this is a programming error if (max_len < 2) xorp_throw(CorruptMessage, "Short block to BGPParameter::create\n", OPENMSGERROR, 0); ParamType param_type = static_cast(d[0]); len = d[1] + 2; // count the header too if (len == 2 || len > max_len ) { XLOG_WARNING("Create: max_len %u len %u type: %u\n", max_len, (uint32_t)len, (uint32_t)param_type); debug_msg("Badly constructed Parameters\n"); debug_msg("Throw exception\n"); debug_msg("Send bad packet\n"); // XXX there doesn't seem to be a good error code for this. xorp_throw(CorruptMessage, "Badly constructed Parameters\n", OPENMSGERROR, 0); } debug_msg("param type %d len+header %u\n", param_type, XORP_UINT_CAST(len)); BGPParameter *p = NULL; switch (param_type) { case PARAMTYPEAUTH: xorp_throw(CorruptMessage, "Deprecated BGP Authentication Parameter received", OPENMSGERROR, UNSUPOPTPAR); case PARAMTYPECAP: { CapType cap_type = static_cast(d[2]); switch (cap_type) { // This is the capability type case CAPABILITYMULTIPROTOCOL: p = new BGPMultiProtocolCapability(len, d); break; case CAPABILITYREFRESH: case CAPABILITYREFRESHOLD: p = new BGPRefreshCapability(len, d); break; case CAPABILITYMULTIROUTE: p = new BGPMultiRouteCapability(len, d); break; case CAPABILITY4BYTEAS: p = new BGP4ByteASCapability(len, d); break; default: p = new BGPUnknownCapability(len, d); } break; } default : xorp_throw(CorruptMessage, c_format("Unrecognised optional parameter %d max_len %u len %u", param_type, max_len, XORP_UINT_CAST(len)), OPENMSGERROR, UNSUPOPTPAR); } return p; } xorp/bgp/update_attrib.cc0000664000076400007640000000750411540224220015564 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "bgp_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/ipv4net.hh" #include "update_attrib.hh" #include "packet.hh" BGPUpdateAttrib::BGPUpdateAttrib(const uint8_t *d) { uint8_t plen = d[0]; union { uint8_t a8[4]; uint32_t a32; } a; a.a32 = 0; // Reset the original value; // Calculate the number of bytes to copy uint8_t bytes = plen / 8; if (plen % 8) bytes++; // Copy the address if (bytes > 0) memcpy(a.a8, d + 1, bytes); // Set the address IPv4Net& net = *this; net = IPv4Net(IPv4(a.a32), plen); } void BGPUpdateAttrib::copy_out(uint8_t *d) const { uint8_t plen = prefix_len(); union { uint8_t a8[4]; uint32_t a32; } a; // Get the address a.a32 = masked_addr().addr(); // Calculate the number of bytes to copy uint8_t bytes = plen / 8; if (plen % 8) bytes++; // Copy the address d[0] = plen; if (bytes > 0) memcpy(d + 1, a.a8, bytes); } size_t BGPUpdateAttrib::size(const uint8_t *d) throw(CorruptMessage) { if (d[0] > 32) xorp_throw(CorruptMessage, c_format("inconsistent length %d", d[0]), UPDATEMSGERR, INVALNETFIELD); return (d[0] + 7)/8 + 1; } size_t BGPUpdateAttribList::wire_size() const { size_t len = 0; for (const_iterator uai = begin() ; uai != end(); ++uai) len += uai->wire_size(); debug_msg("BGPUpdateAttribList::wire_size %p is %u (list size %u)\n", this, XORP_UINT_CAST(len), XORP_UINT_CAST(size())); return len; } uint8_t * BGPUpdateAttribList::encode(size_t &l, uint8_t *d) const { size_t want = wire_size(); if (d != 0) { // have a buffer, check length // XXX this should become an exception assert (l >= want); } else d = new uint8_t[want]; l = want; size_t i=0; for (const_iterator uai = begin() ; uai != end(); ++uai) { uai->copy_out(d+i); i += uai->wire_size(); } return d; } #if 0 void BGPUpdateAttribList::add(const BGPUpdateAttrib &x) { // XXX fill it up } #endif void BGPUpdateAttribList::decode(const uint8_t *d, size_t len) throw(CorruptMessage) { clear(); set x_set; while (len >0 && len >= BGPUpdateAttrib::size(d)) { BGPUpdateAttrib wr(d); len -= BGPUpdateAttrib::size(d); d += BGPUpdateAttrib::size(d); if (x_set.find(wr.net()) == x_set.end()) { push_back(wr); x_set.insert(wr.net()); } else XLOG_WARNING("Received duplicate %s in update message", wr.str("nlri or withdraw").c_str()); } if (len != 0) xorp_throw(CorruptMessage, c_format("leftover bytes %u", XORP_UINT_CAST(len)), UPDATEMSGERR, ATTRLEN); } string BGPUpdateAttribList::str(string nlri_or_withdraw) const { string s = ""; for (const_iterator i = begin(); i != end(); ++i) s += " - " + i->str(nlri_or_withdraw) + "\n"; return s; } xorp/bgp/peer_handler_debug.cc0000664000076400007640000001207311421137511016534 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net //#define DEBUG_LOGGING #include "bgp_module.h" #include "libxorp/xlog.h" #include "peer_handler_debug.hh" DebugPeerHandler::DebugPeerHandler(BGPPeer *peer) : PeerHandler("DebugPeerHandler", peer, NULL, NULL) { } DebugPeerHandler::~DebugPeerHandler() { } void DebugPeerHandler::print_route(const SubnetRoute& route, FPAList4Ref palist) const { palist->canonicalize(); string s; s = "SubnetRoute:\n"; s += " Net: " + route.net().str() + "\n"; s += " PAList: " + palist->str() + "\n"; fprintf(_ofile, "%s", s.c_str()); } void DebugPeerHandler::print_route(const SubnetRoute& route, FPAList6Ref palist) const { palist->canonicalize(); string s; s = "SubnetRoute:\n"; s += " Net: " + route.net().str() + "\n"; s += " PAList: " + palist->str() + "\n"; fprintf(_ofile, "%s", s.c_str()); } int DebugPeerHandler::start_packet() { debug_msg("DebugPeerHandler::start packet\n"); fprintf(_ofile, "[PEER: START_PACKET]\n"); return 0; } int DebugPeerHandler::add_route(const SubnetRoute &rt, FPAList4Ref& pa_list, bool ibgp, Safi) { debug_msg("DebugPeerHandler::add_route(IPv4) %p\n", &rt); fprintf(_ofile, "[PEER: ADD_ROUTE, "); if (ibgp) fprintf(_ofile, "IBGP]\n"); else fprintf(_ofile, "EBGP]\n"); // fprintf(_ofile, "%s\n", rt.str().c_str()); print_route(rt, pa_list); return 0; } int DebugPeerHandler::add_route(const SubnetRoute& rt, FPAList6Ref& pa_list, bool ibgp, Safi) { debug_msg("DebugPeerHandler::add_route(IPv6) %p\n", &rt); fprintf(_ofile, "[PEER: ADD_ROUTE, "); if (ibgp) fprintf(_ofile, "IBGP]\n"); else fprintf(_ofile, "EBGP]\n"); //fprintf(_ofile, "%s\n", rt.str().c_str()); print_route(rt, pa_list); return 0; } int DebugPeerHandler::replace_route(const SubnetRoute &old_rt, bool old_ibgp, const SubnetRoute &new_rt, bool new_ibgp, FPAList4Ref& pa_list, Safi) { debug_msg("DebugPeerHandler::replace_route(IPv4) %p %p\n", &old_rt, &new_rt); UNUSED(new_rt); fprintf(_ofile, "[PEER: REPLACE_ROUTE]\n"); fprintf(_ofile, "[PEER: OLD, "); if (old_ibgp) fprintf(_ofile, "IBGP]\n"); else fprintf(_ofile, "EBGP]\n"); fprintf(_ofile, "%s\n", old_rt.str().c_str()); fprintf(_ofile, "[PEER: NEW, "); if (new_ibgp) fprintf(_ofile, "IBGP]\n"); else fprintf(_ofile, "EBGP]\n"); // fprintf(_ofile, "%s\n", new_rt.str().c_str()); print_route(new_rt, pa_list); return 0; } int DebugPeerHandler::replace_route(const SubnetRoute &old_rt, bool old_ibgp, const SubnetRoute &new_rt, bool new_ibgp, FPAList6Ref& pa_list, Safi) { debug_msg("DebugPeerHandler::replace_route(IPv6) %p %p\n", &old_rt, &new_rt); UNUSED(new_rt); fprintf(_ofile, "[PEER: REPLACE_ROUTE]\n"); fprintf(_ofile, "[PEER: OLD, "); if (old_ibgp) fprintf(_ofile, "IBGP]\n"); else fprintf(_ofile, "EBGP]\n"); fprintf(_ofile, "%s\n", old_rt.str().c_str()); fprintf(_ofile, "[PEER: NEW, "); if (new_ibgp) fprintf(_ofile, "IBGP]\n"); else fprintf(_ofile, "EBGP]\n"); //fprintf(_ofile, "%s\n", new_rt.str().c_str()); print_route(new_rt, pa_list); return 0; } int DebugPeerHandler::delete_route(const SubnetRoute &rt, FPAList4Ref& pa_list, bool ibgp, Safi) { debug_msg("DebugPeerHandler::delete_route(IPv4) %p\n", &rt); fprintf(_ofile, "[PEER: DELETE_ROUTE, "); if (ibgp) fprintf(_ofile, "IBGP]\n"); else fprintf(_ofile, "EBGP]\n"); //fprintf(_ofile, "%s\n", rt.str().c_str()); print_route(rt, pa_list); return 0; } int DebugPeerHandler::delete_route(const SubnetRoute& rt, FPAList6Ref& pa_list, bool ibgp, Safi) { debug_msg("DebugPeerHandler::delete_route(IPv6) %p\n", &rt); fprintf(_ofile, "[PEER: DELETE_ROUTE, "); if (ibgp) fprintf(_ofile, "IBGP]\n"); else fprintf(_ofile, "EBGP]\n"); //fprintf(_ofile, "%s\n", rt.str().c_str()); print_route(rt, pa_list); return 0; } PeerOutputState DebugPeerHandler::push_packet() { debug_msg("DebugPeerHandler::push packet\n"); fprintf(_ofile, "[PEER: PUSH_PACKET]\n"); return _canned_response; } xorp/bgp/subnet_route.hh0000664000076400007640000005302711421137511015472 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/bgp/subnet_route.hh,v 1.28 2008/11/08 06:14:40 mjh Exp $ #ifndef __BGP_SUBNET_ROUTE_HH__ #define __BGP_SUBNET_ROUTE_HH__ #include "path_attribute.hh" #include "attribute_manager.hh" #include "libxorp/xorp.h" #include "libxorp/ipv4net.hh" #include "libxorp/ipv6net.hh" #include "policy/backend/policytags.hh" #include "policy/backend/policy_filter.hh" #define SRF_IN_USE 0x00000001 #define SRF_WINNER 0x00000002 #define SRF_FILTERED 0x00000004 #define SRF_DELETED 0x00000008 #define SRF_NH_RESOLVED 0x00000010 #define SRF_AGGR_BRIEF_MODE 0x00000080 #define SRF_AGGR_PREFLEN_MASK 0x0000ff00 #define SRF_REFCOUNT 0xffff0000 // Aggregation flags // XXX Marko any better ideas where to put those? #define SR_AGGR_IGNORE 0xff #define SR_AGGR_IBGP_ONLY 0xe0 #define SR_AGGR_EBGP_AGGREGATE 0xd0 #define SR_AGGR_EBGP_NOT_AGGREGATED 0xd1 #define SR_AGGR_EBGP_WAS_AGGREGATED 0xd2 //Defining paranoid emables some additional checks to ensure we don't //try to reuse deleted data, or follow an obsolete parent_route //pointer. template class SubnetRouteRef; template class SubnetRouteConstRef; class RouteMetaData { public: RouteMetaData(const RouteMetaData& metadata); RouteMetaData(); ~RouteMetaData() { // prevent accidental reuse after deletion _flags = 0xffffffff; } /** * @return whether or not this route is in use. "in use" here * does not mean the route won the decision process, but rather * that it was at least a contender for decision, and was not * filtered in the incoming filter bank. */ inline bool in_use() const {return (_flags & SRF_IN_USE) != 0;} /** * Record whether or not this route is in use. "in use" here * does not mean the route won the decision process, but rather * that it was at least a contender for decision, and was not * filtered in the incoming filter bank. * * @param used true if the route is "in use". */ void set_in_use(bool used) { if (used) { _flags |= SRF_IN_USE; } else { _flags &= ~SRF_IN_USE; } } /** * returns true if the route was chosen by the routing decision * process as the winning route for this subnet. * * is_winner should NOT be called by anything other than the * DecisionTable because caching means that it may not return the * right answer anywhere else */ inline bool is_winner() const {return (_flags & SRF_WINNER) != 0;} /** * when a route is chosen by the routing decision process as the * winning route for this subnet, set_is_winner should be called * to record this fact and to record the igp_metric at the time * the route was chosen. * * set_is_winner should NOT be called by anything other than the * DecisionTable because caching means that it may not return the * right answer anywhere else */ void set_is_winner(uint32_t igp_metric) { _flags |= SRF_WINNER; _igp_metric = igp_metric; } /** * when a route fails to be chosen by the routing decision process as the * winning route for this subnet, set_is_not_winner should be called * to record this fact. */ inline void set_is_not_winner() { _flags &= ~SRF_WINNER; } /** * record whether or not a route's nexthop resolved */ void set_nexthop_resolved(bool resolvable) { if (resolvable) { _flags |= SRF_NH_RESOLVED; } else { _flags &= ~SRF_NH_RESOLVED; } } /** * did the route's nexthop resolve when it was passed through the * NextHop resolver table. */ inline bool nexthop_resolved() const { return (_flags & SRF_NH_RESOLVED) != 0; } /** * is_filtered returns true if the route was filtered out by the * incoming filter bank, false otherwise. As such it only makes * sense calling this on routes that are stored in the RibIn. */ inline bool is_filtered() const {return (_flags & SRF_FILTERED) != 0;} /** * set_filtered record whether or not the route was filtered out * by the incoming filter bank. As such it only * makes sense calling this on routes that are stored in the * RibIn. * * @param filtered true if the route was filtered, false otherwise. */ void set_filtered(bool filtered) { if (filtered) { _flags |= SRF_FILTERED; } else { _flags &= ~SRF_FILTERED; } } /** * is_deleted returns true if the route has already been deleted * (but the class instance representing it has not been because * it's reference count is non-zero) */ inline bool is_deleted() const {return (_flags & SRF_DELETED) != 0;} inline void set_deleted() {_flags |= SRF_DELETED;} /** * @returns the IGP routing protocol metric that applied when the * route won the decision process. If the route has not won, this * value is undefined. */ inline uint32_t igp_metric() const {return _igp_metric;} inline void set_igp_metric(uint32_t igp_metric) { _igp_metric = igp_metric; } inline void dont_aggregate() { _flags |= SRF_AGGR_PREFLEN_MASK; } inline uint16_t refcount() const { return (_flags & SRF_REFCOUNT) >> 16; } //set our reference count to one (our own self-reference) //and clear the deleted flag inline void reset_flags() { _flags ^= (_flags & (SRF_REFCOUNT | SRF_DELETED)); } /** * @return policy tags associated with route. */ inline const PolicyTags& policytags() const { return _policytags; } /** * Replaced policy tags of route. * * @param tags new policy tags for route. */ inline void set_policytags(const PolicyTags& tags) { _policytags = tags; } inline const RefPf& policyfilter(uint32_t i) const { return _pfilter[i]; } inline void set_policyfilter(uint32_t i, const RefPf& pf) { _pfilter[i] = pf; } /** * Set the "brief" mode flag on an candidate for aggregation. */ void set_aggr_brief_mode() { _flags |= SRF_AGGR_BRIEF_MODE; } /** * Clear the "brief" mode flag on an candidate for aggregation. */ void clear_aggr_brief_mode() { _flags &= ~SRF_AGGR_BRIEF_MODE; } /** * Read the "brief" aggregation mode flag. */ bool aggr_brief_mode() const { return (_flags & SRF_AGGR_BRIEF_MODE); } /** * Set the target prefix length on an candidate for aggregation. * The field is also used for storing aggregation markers. * * @param preflen prefix length of the requested aggregate route. */ void set_aggr_prefix_len(uint32_t preflen) { _flags = (_flags & ~SRF_AGGR_PREFLEN_MASK) | ((preflen << 8) & SRF_AGGR_PREFLEN_MASK); } /** * Read the aggregation prefix length marker. * The field is also used for storing aggregation markers. */ uint32_t aggr_prefix_len() const { return (_flags & SRF_AGGR_PREFLEN_MASK) >> 8; } bool bump_refcount(int delta) { XLOG_ASSERT(delta == 1 || delta == -1); uint16_t refs = refcount(); if (delta == 1) { XLOG_ASSERT(refs < 0xffff); } else { XLOG_ASSERT(refs > 0); } refs += delta; //re-insert the new ref count _flags = (_flags ^ (_flags&SRF_REFCOUNT)) | (refs << 16); //handle delayed deletion if ((refs==0) && ((_flags & SRF_DELETED) != 0)) { return true; } return false; } private: /** * Flag definitions: * * SRF_IN_USE indicates whether this route is currently * used for anything. The route might not be used if it wasn't * chosen by the BGP decision mechanism, or if a downstream filter * caused a modified version of this route to be installed in a * cache table * * SRF_WINNER indicates that the route won the decision process. * * SRF_FILTERED indicates that the route was filtered downstream. * Currently this is only used for RIB-IN routes that are filtered * in the inbound filter bank * * SRF_REFCOUNT (16 bits) maintains a reference count of the number * of objects depending on this SubnetRoute instance. Deletion * will be delayed until the reference count reaches zero */ uint32_t _flags; /** * If the route is a winner (SRF_WINNER is set), then * DecisionTable will fill in the IGP metric that was used in * deciding the route was a winner */ uint32_t _igp_metric; PolicyTags _policytags; RefPf _pfilter[3]; }; /** * @short SubnetRoute holds a BGP routing table entry. * * SubnetRoute is the basic class used to hold a BGP routing table * entry in BGP's internal representation. It's templated so the same * code can be used to hold IPv4 or IPv6 routes - their representation * is essentially the same internally, even though they're encoded * differently on the wire by the BGP protocol. A route essentially * consists of the subnet (address and prefix) referred to by the * route, a BGP path attribute list, and some metadata for our own * use. When a route update from BGP comes it, it is split into * multiple subnet routes, one for each NLRI (IPv4) or MP_REACH (IPv6) * attribute. SubnetRoute is also the principle way routing * information is passed around internally in our BGP implementation. * * SubnetRoute is reference-counted - delete should NOT normally be * called directly on a SubnetRoute; instead unref should be called, * which will decrement the reference count, and delete the instance * if the reference count has reached zero. */ template class SubnetRoute { friend class SubnetRouteRef; friend class SubnetRouteConstRef; public: /** * @short SubnetRoute copy constructor */ SubnetRoute(const SubnetRoute& route_to_clone); /** * @short SubnetRoute constructor * * @param net the subnet (address and prefix) this route refers to. * @param attributes pointer to the path attribute list associated with * this route. * @param parent_route the SubnetRoute that this route was derived * from. For example, if a filter takes one SubnetRoute and * generates a modified version, the parent_route of the new one * should point to the original. If this is set to non-NULL, care * must be taken to ensure the original route is never deleted * before the derived route. */ SubnetRoute(const IPNet &net, PAListRef attributes, const SubnetRoute* parent_route); /** * @short SubnetRoute constructor * * @param net the subnet (address and prefix) this route refers to. * @param attributes pointer to the path attribute list associated with * this route. * @param parent_route the SubnetRoute that this route was derived * from. For example, if a filter takes one SubnetRoute and * generates a modified version, the parent_route of the new one * should point to the original. If this is set to non-NULL, care * must be taken to ensure the original route is never deleted * before the derived route. * @param igp_metric the IGP routing protocol metric to reach the * nexthop obtained from the RIB. */ SubnetRoute(const IPNet &net, PAListRef attributes, const SubnetRoute* parent_route, uint32_t igp_metric); /** * @short Equality comparison for SubnetRoutes * * Equality comparison for SubnetRoutes. Only the subnet and * attributes are compared, not the metadata such as flags, * igp_metric, etc * * @param them Route to be compared against. */ bool operator==(const SubnetRoute& them) const; /** * @return the subnet this route refers to. */ const IPNet& net() const {return _net;} #if 0 /** * @return the address of the nexthop for this route. */ // remove this because we shouldn't be pulling attributes directly // out of the stored version - use a FastPathAttributeList // instead. const A& nexthop() const {return _attributes.nexthop();} #endif /** * @return a pointer to the path attribute list for this route. */ PAListRef attributes() const {return _attributes;} /** * @return whether or not this route is in use. "in use" here * does not mean the route won the decision process, but rather * that it was at least a contender for decision, and was not * filtered in the incoming filter bank. */ bool in_use() const {return _metadata.in_use();} /** * Record whether or not this route is in use. "in use" here * does not mean the route won the decision process, but rather * that it was at least a contender for decision, and was not * filtered in the incoming filter bank. * * @param used true if the route is "in use". */ void set_in_use(bool used) const; /** * returns true if the route was chosen by the routing decision * process as the winning route for this subnet. * * is_winner should NOT be called by anything other than the * DecisionTable because caching means that it may not return the * right answer anywhere else */ bool is_winner() const {return _metadata.is_winner();} /** * when a route is chosen by the routing decision process as the * winning route for this subnet, set_is_winner should be called * to record this fact and to record the igp_metric at the time * the route was chosen. * * set_is_winner should NOT be called by anything other than the * DecisionTable because caching means that it may not return the * right answer anywhere else */ void set_is_winner(uint32_t igp_metric) const; /** * when a route fails to be chosen by the routing decision process as the * winning route for this subnet, set_is_not_winner should be called * to record this fact. */ void set_is_not_winner() const; /** * record whether or not a route's nexthop resolved */ void set_nexthop_resolved(bool resolved) const; /** * did the route's nexthop resolve when it was passed through the * NextHop resolver table. */ bool nexthop_resolved() const {return _metadata.nexthop_resolved();} /** * is_filtered returns true if the route was filtered out by the * incoming filter bank, false otherwise. As such it only makes * sense calling this on routes that are stored in the RibIn. */ bool is_filtered() const {return _metadata.is_filtered();} /** * set_filtered record whether or not the route was filtered out * by the incoming filter bank. As such it only * makes sense calling this on routes that are stored in the * RibIn. * * @param filtered true if the route was filtered, false otherwise. */ void set_filtered(bool filtered) const; /** * is_deleted returns true if the route has already been deleted * (but the class instance representing it has not been because * it's reference count is non-zero) */ bool is_deleted() const {return _metadata.is_deleted();} /** * @returns a string representation of the route for debugging purposes */ string str() const; /** * @returns the IGP routing protocol metric that applied when the * route won the decision process. If the route has not won, this * value is undefined. */ uint32_t igp_metric() const {return _metadata.igp_metric();} /** * @returns the original version of this route, before any filters * were applied to it to modify it. If no filters have been * applied, this may be this route itself. */ const SubnetRoute *original_route() const { if (_parent_route) return _parent_route; else return this; } /** * @returns the original version of this route, before any filters * were applied to it to modify it. If no filters have been * applied, NULL will be returned. */ const SubnetRoute* parent_route() const { return _parent_route; } /** * @short record the original version of this route, before any * filters were applied. * * @param parent the original version of this route. */ void set_parent_route(const SubnetRoute *parent); /** * @short delete this route * * unref is called to delete a SubnetRoute, and should be called * instead of calling delete on the route. SubnetRoutes are * reference-counted, so unref decrements the reference count and * deletes the storage only if the reference count reaches zero. */ void unref() const; uint16_t refcount() const { return _metadata.refcount(); } /** * @return policy tags associated with route. */ const PolicyTags& policytags() const { return _metadata.policytags(); } /** * Replaced policy tags of route. * * @param tags new policy tags for route. */ void set_policytags(const PolicyTags& tags) const { _metadata.set_policytags(tags); } const RefPf& policyfilter(uint32_t i) const; void set_policyfilter(uint32_t i, const RefPf& pf) const; /** * Set the "brief" mode flag on an candidate for aggregation. */ void set_aggr_brief_mode() const { _metadata.set_aggr_brief_mode(); } /** * Clear the "brief" mode flag on an candidate for aggregation. */ void clear_aggr_brief_mode() const { _metadata.clear_aggr_brief_mode(); } /** * Read the "brief" aggregation mode flag. */ bool aggr_brief_mode() const { return _metadata.aggr_brief_mode(); } /** * Set the target prefix length on an candidate for aggregation. * The field is also used for storing aggregation markers. * * @param preflen prefix length of the requested aggregate route. */ void set_aggr_prefix_len(uint32_t preflen) { _metadata.set_aggr_prefix_len(preflen); } /** * Read the aggregation prefix length marker. * The field is also used for storing aggregation markers. */ uint32_t aggr_prefix_len() const { return _metadata.aggr_prefix_len(); } protected: /** * @short protected SubnetRoute destructor. * * The destructor is protected because you are not supposed to * directly delete a SubnetRoute. Instead you should call unref() * and the SubnetRoute will be deleted when it's reference count * reaches zero. */ ~SubnetRoute(); private: void bump_refcount(int delta) const { if (_metadata.bump_refcount(delta)) delete this; } // Copyable, but not assignable. const SubnetRoute& operator=(const SubnetRoute&); /** * _net is the subnet (address and prefix) for this route. */ IPNet _net; /** * _attributes is a pointer to the path attribute list for this * route. The actual data storage for the path attribute list is * handled inside the attribute manager */ PAListRef _attributes; /** * _parent_route is a pointer to the original version of this * route, before any filters modified it. It may be NULL, * indicating this is the original version. */ const SubnetRoute *_parent_route; mutable RouteMetaData _metadata; }; /** * @short a class to hold a reference to a SubnetRoute * * SubnetRouteRef holds a reference to a SubnetRoute. When it is * deleted or goes out of scope, the SubnetRoute's reference count * will be decreased, and the SubnetRoute may be deleted if the * reference count reaches zero. */ template class SubnetRouteRef { public: SubnetRouteRef(SubnetRoute* route) : _route(route) { if (_route) _route->bump_refcount(1); } SubnetRouteRef(const SubnetRouteRef& ref) { _route = ref._route; if (_route) _route->bump_refcount(1); } ~SubnetRouteRef() { if (_route) _route->bump_refcount(-1); } SubnetRoute* route() const {return _route;} private: //_route can be NULL SubnetRoute* _route; }; /** * @short a class to hold a const reference to a SubnetRoute * * SubnetRouteRef holds a const reference to a SubnetRoute. When it * is deleted or goes out of scope, the SubnetRoute's reference count * will be decreased, and the SubnetRoute may be deleted if the * reference count reaches zero. */ template class SubnetRouteConstRef { public: SubnetRouteConstRef(const SubnetRoute* route) : _route(route) { if (_route) _route->bump_refcount(1); } SubnetRouteConstRef(const SubnetRouteConstRef& ref) { _route = ref._route; if (_route) _route->bump_refcount(1); } ~SubnetRouteConstRef() { if (_route) _route->bump_refcount(-1); } const SubnetRoute* route() const {return _route;} private: //_route can be NULL const SubnetRoute* _route; }; #endif // __BGP_SUBNET_ROUTE_HH__ xorp/bgp/dump_iterators.hh0000664000076400007640000001344111540224217016013 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/bgp/dump_iterators.hh,v 1.21 2008/10/02 21:56:15 bms Exp $ #ifndef __BGP_DUMP_ITERATORS_HH__ #define __BGP_DUMP_ITERATORS_HH__ #include "path_attribute.hh" #include "bgp_trie.hh" #include "route_queue.hh" #include "peer_route_pair.hh" #include "route_table_aggregation.hh" class BGPPlumbing; class PeerHandler; template class InternalMessage; typedef enum { STILL_TO_DUMP, CURRENTLY_DUMPING, DOWN_DURING_DUMP, DOWN_BEFORE_DUMP, COMPLETELY_DUMPED, NEW_PEER, FIRST_SEEN_DURING_DUMP } PeerDumpStatus; template class PeerDumpState { public: PeerDumpState(const PeerHandler* peer, PeerDumpStatus status, uint32_t genid); ~PeerDumpState(); string str() const; const PeerHandler* peer_handler() const { return _peer; } const IPNet& last_net() const { return _last_net_before_down; } void set_down_during_dump(IPNet& last_net, uint32_t genid) { XLOG_ASSERT(genid == _genid); _status = DOWN_DURING_DUMP; _last_net_before_down = last_net; set_delete_occurring(genid); } void set_down(uint32_t genid) { XLOG_ASSERT(_status == STILL_TO_DUMP || _status == CURRENTLY_DUMPING); _status = DOWN_BEFORE_DUMP; set_delete_occurring(genid); } void start_dump() { XLOG_ASSERT(_status == STILL_TO_DUMP); _status = CURRENTLY_DUMPING; } void set_dump_complete() { XLOG_ASSERT(_status == CURRENTLY_DUMPING); _status = COMPLETELY_DUMPED; } uint32_t genid() const { return _genid; } void set_genid(uint32_t genid) { _genid = genid; } PeerDumpStatus status() const { return _status; } void set_delete_complete(uint32_t genid); void set_delete_occurring(uint32_t genid) { _deleting_genids.insert(genid); } bool delete_complete() const { return _deleting_genids.empty(); } private: const PeerHandler* _peer; bool _routes_dumped; IPNet _last_net_before_down; uint32_t _genid; set _deleting_genids; PeerDumpStatus _status; }; template class DumpIterator { public: DumpIterator(const PeerHandler* peer, const list *>& peers_to_dump); ~DumpIterator(); string str() const; void route_dump(const InternalMessage &rtmsg); const PeerHandler* current_peer() const { return _current_peer->peer_handler(); } const PeerHandler* peer_to_dump_to() const { return _peer; } bool is_valid() const; bool route_iterator_is_valid() const { return _route_iterator_is_valid; } bool next_peer(); const typename BgpTrie::iterator& route_iterator() const { return _route_iterator; } const typename RefTrie >::iterator& aggr_iterator() const { return _aggr_iterator; } // const IPNet& net() const { return _route_iterator_net; } void set_route_iterator(typename BgpTrie::iterator& new_iter) { _route_iterator = new_iter; _route_iterator_is_valid = true; } void set_aggr_iterator(typename RefTrie >::iterator& new_iter) { _aggr_iterator = new_iter; _route_iterator_is_valid = true; } //void set_route_iterator_net(const IPNet& net) { // _route_iterator_net = net; // } // uint32_t rib_version() const { return _rib_version; } // void set_rib_version(uint32_t version) { _rib_version = version; } bool route_change_is_valid(const PeerHandler* origin_peer, const IPNet& net, uint32_t genid, RouteQueueOp op); /** * A peer which is down but still deleting routes when this peer * is brought up. */ void peering_is_down(const PeerHandler *peer, uint32_t genid); /** * A peer which does down while dumping is taking place. */ void peering_went_down(const PeerHandler *peer, uint32_t genid); /** * A peer that is down and has now completed deleting all its routes. */ void peering_down_complete(const PeerHandler *peer, uint32_t genid); /** * A peer that was down came up. */ void peering_came_up(const PeerHandler *peer, uint32_t genid); /** * @return true while peers we deleting routes. */ bool waiting_for_deletion_completion() const; /** * @return true if the iterator got moved since the last delete */ bool iterator_got_moved(IPNet new_net) const; private: const PeerHandler *_peer; /** * The list of peers that remain to be dumped */ list > _peers_to_dump; typename list >::iterator _current_peer; PeerTableInfo* _current_peer_debug; //XXX just to aid debugging in gdb bool _route_iterator_is_valid; typename BgpTrie::iterator _route_iterator; typename RefTrie >::iterator _aggr_iterator; bool _routes_dumped_on_current_peer; IPNet _last_dumped_net; map * > _peers; }; #endif // __BGP_DUMP_ITERATORS_HH__ xorp/bgp/route_table_dump.hh0000664000076400007640000001106211421137511016277 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/bgp/route_table_dump.hh,v 1.27 2008/11/08 06:14:39 mjh Exp $ #ifndef __BGP_ROUTE_TABLE_DUMP_HH__ #define __BGP_ROUTE_TABLE_DUMP_HH__ #include "route_table_base.hh" #include "peer_handler.hh" #include "bgp_trie.hh" #include "dump_iterators.hh" #include "peer_route_pair.hh" #define AUDIT_ENABLE #define AUDIT_LEN 1000 class EventLoop; template class DumpTable : public BGPRouteTable { public: DumpTable(string tablename, const PeerHandler *peer, const list *>& peer_list, BGPRouteTable *parent, Safi safi); int add_route(InternalMessage &rtmsg, BGPRouteTable *caller); int replace_route(InternalMessage &old_rtmsg, InternalMessage &new_rtmsg, BGPRouteTable *caller); int delete_route(InternalMessage &rtmsg, BGPRouteTable *caller); int route_dump(InternalMessage &rtmsg, BGPRouteTable *caller, const PeerHandler *dump_peer); int push(BGPRouteTable *caller); const SubnetRoute *lookup_route(const IPNet &net, uint32_t& genid, FPAListRef& pa_list) const; void route_used(const SubnetRoute* route, bool in_use); RouteTableType type() const {return DUMP_TABLE;} string str() const; /* mechanisms to implement flow control in the output plumbing */ bool get_next_message(BGPRouteTable *next_table); void initiate_background_dump(); void suspend_dump(); /** * A peer which is down but still deleting routes when this peer * is brought up. */ void peering_is_down(const PeerHandler *peer, uint32_t genid); /** * A peer which does down while dumping is taking place. */ void peering_went_down(const PeerHandler *peer, uint32_t genid, BGPRouteTable *caller); /** * A peer that is down and has now completed deleting all its routes. */ void peering_down_complete(const PeerHandler *peer, uint32_t genid, BGPRouteTable *caller); /** * A peer which was previously down came up. */ void peering_came_up(const PeerHandler *peer, uint32_t genid, BGPRouteTable *caller); /** * Signal from upstream that triggered data is ready */ void wakeup(); #ifdef AUDIT_ENABLE void print_and_clear_audit(); #endif private: /** * Called when the dump table has completed its tasks. */ void completed(); void schedule_unplumb_self(); void unplumb_self(); void wakeup_downstream(); bool do_next_route_dump(); EventLoop& eventloop() const {return _peer->eventloop();} const PeerHandler *_peer; DumpIterator _dump_iter; bool _output_busy; int _dumped; bool _dump_active; // true if the dump is in progress bool _triggered_event; // true if DumpTable has been woken up by // a triggered event (in which case we // shouldn't do any dumping or we may // surprise the upstream by dumping in // the middle of an event ) XorpTimer _dump_timer; //if we get to the end of the route dump, and some peers that went //down during our dump have not yet finished their deletion //process, we need to delay unplumbing ourself until they finish //to avoid propagating unnecessary deletes downstream. bool _waiting_for_deletion_completion; //The dump table has done its job and can be removed. bool _completed; #ifdef AUDIT_ENABLE //audit trail for debugging - keep a log of last few events, use //this as a circular buffer string _audit_entry[AUDIT_LEN]; int _first_audit, _last_audit, _audit_entries; void add_audit(const string& log_entry); #endif }; #endif // __BGP_ROUTE_TABLE_DUMP_HH__ xorp/bgp/local_data.cc0000664000076400007640000000173411421137511015023 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net //#define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #include "bgp_module.h" #include "libxorp/xorp.h" xorp/bgp/bgp.hh0000664000076400007640000010202311540225520013513 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __BGP_MAIN_HH__ #define __BGP_MAIN_HH__ #include "libxorp/status_codes.h" #include "libxipc/xrl_std_router.hh" #include "libxorp/profile.hh" #include "socket.hh" #include "packet.hh" #include "peer.hh" #include "peer_list.hh" #include "plumbing.hh" #include "iptuple.hh" #include "path_attribute.hh" #include "peer_handler.hh" #include "process_watch.hh" #include "libfeaclient/ifmgr_xrl_mirror.hh" #include "policy/backend/version_filters.hh" class EventLoop; class XrlBgpTarget; class BGPMain : public ServiceBase, public IfMgrHintObserver, public ServiceChangeObserverBase { public: BGPMain(EventLoop& eventloop); ~BGPMain(); /** * Get the process status */ ProcessStatus status(string& reason); /** * Startup operation. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int startup(); /** * Shutdown operation. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int shutdown(); /** * A method that should be called when an internal subsystem comes up. * * @param component_name the name of the component. */ void component_up(const string& component_name); /** * A method that should be called when an internal subsystem goes down. * * @param component_name the name of the component. */ void component_down(const string& component_name); /** * Test whether an interface is enabled. * * @param interface the name of the interface to test. * @return true if it exists and is enabled, otherwise false. */ bool is_interface_enabled(const string& interface) const; /** * Test whether an interface/vif is enabled. * * @param interface the name of the interface to test. * @param vif the name of the vif to test. * @return true if it exists and is enabled, otherwise false. */ bool is_vif_enabled(const string& interface, const string& vif) const; /** * Test whether an IPv4 interface/vif/address is enabled. * * @param interface the name of the interface to test. * @param vif the name of the vif to test. * @param address the address to test. * @return true if it exists and is enabled, otherwise false. */ bool is_address_enabled(const string& interface, const string& vif, const IPv4& address) const; typedef XorpCallback2::RefPtr InterfaceStatusCb; typedef XorpCallback3::RefPtr VifStatusCb; typedef XorpCallback5::RefPtr AddressStatus4Cb; /** * Add a callback for tracking the interface status. * * The callback will be invoked whenever the status of the interface * is changed from disabled to enabled or vice-versa. * * @param cb the callback to register. */ void register_interface_status(InterfaceStatusCb cb) { _interface_status_cb = cb; } /** * Add a callback for tracking the interface/vif status. * * The callback will be invoked whenever the status of the interface/vif * is changed from disabled to enabled or vice-versa. * * @param cb the callback to register. */ void register_vif_status(VifStatusCb cb) { _vif_status_cb = cb; } /** * Add a callback for tracking the IPv4 interface/vif/address status. * * The callback will be invoked whenever the status of the tuple * (interface, vif, address) is changed from disabled to enabled * or vice-versa. * * @param cb the callback to register. */ void register_address_status(AddressStatus4Cb cb) { _address_status4_cb = cb; } /** * Obtain the subnet prefix length for an IPv4 interface/vif/address. * * @param interface the name of the interface. * @param vif the name of the vif. * @param address the address. * @return the subnet prefix length for the address. */ uint32_t get_prefix_length(const string& interface, const string& vif, const IPv4& address); /** * Obtain the MTU for an interface. * * @param the name of the interface. * @return the mtu for the interface. */ uint32_t get_mtu(const string& interface); /** * Is the address one of this routers interface addresses? */ bool interface_address4(const IPv4& address) const; /** * Obtain the prefix length for a particular IPv4 address. * * @param address the address to search for. * @param prefix_len the return-by-reference prefix length * for @ref address. * @return true if the address belongs to this router, otherwise false. */ bool interface_address_prefix_len4(const IPv4& address, uint32_t& prefix_len) const; /** * Set the local configuration. * * @param as as number. * * @param id router id. * * @param use_4byte_asnums indicates we should send 4 byte AS * numbers to all peers that are 4byte capable. */ void local_config(const uint32_t& as, const IPv4& id, bool use_4byte_asnums); /** * Set or disable the confederation identifier. */ void set_confederation_identifier(const uint32_t& as, bool disable); /** * Set the cluster ID and enable or disable route reflection. */ void set_cluster_id(const IPv4& cluster_id, bool disable); /** * Set the route flap damping parameters. */ void set_damping(uint32_t half_life,uint32_t max_suppress,uint32_t reuse, uint32_t suppress, bool disable); /** * attach peer to peerlist * * @param p BGP peer. */ void attach_peer(BGPPeer *p); /** * detach peer from the peerlist. * * @param p BGP peer. */ void detach_peer(BGPPeer *p); /** * attach peer to deleted peerlist * * @param p BGP peer. */ void attach_deleted_peer(BGPPeer *p); /** * detach peer from the deleted peerlist. * * @param p BGP peer. */ void detach_deleted_peer(BGPPeer *p); /** * Find peer with this iptuple from the list provided * * @param search iptuple. * @param peers list to search. * * @return A pointer to a peer if one is found NULL otherwise. */ BGPPeer *find_peer(const Iptuple& search, list& peers); /** * Find peer with this iptuple * * @param search iptuple. * * @return A pointer to a peer if one is found NULL otherwise. */ BGPPeer *find_peer(const Iptuple& search); /** * Find peer with this iptuple on the deleted peer list. * * @param search iptuple. * * @return A pointer to a peer if one is found NULL otherwise. */ BGPPeer *find_deleted_peer(const Iptuple& search); /** * create a new peer and attach it to the peerlist. * * @param pd BGP peer data. * * @return true on success */ bool create_peer(BGPPeerData *pd); /** * delete peer tear down connection and remove for peerlist. XrlBgpTarget xbt(bgp.get_router(), bgp); * * @param iptuple iptuple. * * @return true on success */ bool delete_peer(const Iptuple& iptuple); /** * enable peer * * @param iptuple iptuple. * * @return true on success */ bool enable_peer(const Iptuple& iptuple); /** * disable peer * * @param iptuple iptuple. * * @return true on success */ bool disable_peer(const Iptuple& iptuple); /** * Drop this peering and if it was configured up allow it attempt * a new peering. * * @param iptuple iptuple. * * @return true on success */ bool bounce_peer(const Iptuple& iptuple); /** * One of the local IP addresses of this router has changed. Where * a change can be an addition or removal or a change in the link * status. If the provided address matches any of the local ip * addresses of any of the peerings unconditionally bounce the * peering. Unconditional bouncing of the peering is all that is * required if a link has gone down the old session will be * dropped and the new one will fail. If the link has just come up * then a session will be made. */ void local_ip_changed(string local_address); /** * Change one of the tuple settings of this peering. * * @param iptuple original tuple. * @param iptuple new tuple. * * @return true on success */ bool change_tuple(const Iptuple& iptuple, const Iptuple& nptuple); /** * Find the tuple that has this peer address and both ports are * 179. This is a hack as at the moment of writing the rtrmgr * can't send both the old and new state of a variable. * * @param peer_addr of tuple. * @param otuple the tuple if one is found. * * @return true if a tuple is matched. */ bool find_tuple_179(string peer_addr, Iptuple& otuple); /** * Change the local IP address of this peering. * * @param iptuple iptuple. * @param local_ip new IP value * @param local_dev new Interface value. * * @return true on success */ bool change_local_ip(const Iptuple& iptuple, const string& local_ip, const string& local_dev); /** * Change the local IP port of this peering. * * @param iptuple iptuple. * @param local_port new value. * * @return true on success */ bool change_local_port(const Iptuple& iptuple, uint32_t local_port); /** * Change the peer IP port of this peering. * * @param iptuple iptuple. * @param peer_port new value. * * @return true on success */ bool change_peer_port(const Iptuple& iptuple, uint32_t peer_port); /** * set peer as * * @param iptuple iptuple. * @param peer_as new value. * * @return true on success */ bool set_peer_as(const Iptuple& iptuple, uint32_t peer_as); /** * set holdtime * * @param iptuple iptuple. * @param holdtime new value. * * @return true on success */ bool set_holdtime(const Iptuple& iptuple, uint32_t holdtime); /** * set delay open time * * @param iptuple iptuple. * @param delay_open_time new value. * * @return true on success */ bool set_delay_open_time(const Iptuple& iptuple, uint32_t delay_open_time); /** * set route reflector client * * @param iptuple iptuple. * @param rr true if this peer is a route reflector client. * * @return true on success */ bool set_route_reflector_client(const Iptuple& iptuple, bool rr); /** * set route confederation member * * @param iptuple iptuple. * @param conf true if this peer is a confederation member. * * @return true on success */ bool set_confederation_member(const Iptuple& iptuple, bool conf); /** * set prefix limit * * @param maximum number of prefixes * @param state true if the prefix limit is being enforced * * @return true on success */ bool set_prefix_limit(const Iptuple& iptuple, uint32_t maximum, bool state); /** * set IPv4 next-hop. * * @param iptuple iptuple. * @param next-hop * * @return true on success */ bool set_nexthop4(const Iptuple& iptuple, const IPv4& next_hop); /** * Set peer state. * * @param iptuple iptuple. * @param state should the peering be enable or disabled. * * @ return true on success. */ bool set_peer_state(const Iptuple& iptuple, bool state); /** * Activate peer. * * Enable the peering based on the peer state. * * @param iptuple iptuple. * * @ return true on success. */ bool activate(const Iptuple& iptuple); /** * Activate all peers. * * @return true on success */ bool activate_all_peers(); /** * Set peer TCP-MD5 password. * * @param iptuple iptuple. * @param password The password to use for TCP-MD5 authentication; * if this is the empty string, then authentication will be disabled. * * @return true on success. */ bool set_peer_md5_password(const Iptuple& iptuple, const string& password); /* * Set next hop rewrite filter. * * @param iptuple iptuple. * @param next_hop next hop value zero to clear filter. * * @return true on success */ bool next_hop_rewrite_filter(const Iptuple& iptuple, const IPv4& next_hop); bool get_peer_list_start(uint32_t& token); bool get_peer_list_next(const uint32_t& token, string& local_ip, uint32_t& local_port, string& peer_ip, uint32_t& peer_port); bool get_peer_id(const Iptuple& iptuple, IPv4& peer_id); bool get_peer_status(const Iptuple& iptuple, uint32_t& peer_state, uint32_t& admin_status); bool get_peer_negotiated_version(const Iptuple& iptuple, int32_t& neg_version); bool get_peer_as(const Iptuple& iptuple, uint32_t& peer_as); bool get_peer_msg_stats(const Iptuple& iptuple, uint32_t& in_updates, uint32_t& out_updates, uint32_t& in_msgs, uint32_t& out_msgs, uint16_t& last_error, uint32_t& in_update_elapsed); bool get_peer_established_stats(const Iptuple& iptuple, uint32_t& transitions, uint32_t& established_time); bool get_peer_timer_config(const Iptuple& iptuple, uint32_t& retry_interval, uint32_t& hold_time, uint32_t& keep_alive, uint32_t& hold_time_configured, uint32_t& keep_alive_configured, uint32_t& min_as_origination_interval, uint32_t& min_route_adv_interval); bool register_ribname(const string& name); void main_loop(); void terminate() { xorp_do_run = 0; } XorpFd create_listener(const Iptuple& iptuple); LocalData *get_local_data(); void start_server(const Iptuple& iptuple); void stop_server(const Iptuple& iptuple); /** * Stop listening for incoming connections. */ void stop_all_servers(); /** * Originate an IPv4 route * * @param nlri subnet to announce * @param next_hop to forward to * @param unicast if true install in unicast routing table * @param multicast if true install in multicast routing table * @param policytags policy-tags associated with route. * * @return true on success */ bool originate_route(const IPv4Net& nlri, const IPv4& next_hop, const bool& unicast, const bool& multicast, const PolicyTags& policytags); /** * Withdraw an IPv4 route * * @param nlri subnet to withdraw * @param unicast if true withdraw from unicast routing table * @param multicast if true withdraw from multicast routing table * * @return true on success */ bool withdraw_route(const IPv4Net& nlri, const bool& unicast, const bool& multicast) const; template bool get_route_list_start(uint32_t& token, const IPNet& prefix, const bool& unicast, const bool& multicast); template bool get_route_list_next( // Input values, const uint32_t& token, // Output values, IPv4& peer_id, IPNet& net, uint32_t& origin, vector& aspath, A& nexthop, int32_t& med, int32_t& localpref, int32_t& atomic_agg, vector& aggregator, int32_t& calc_localpref, vector& attr_unknown, bool& best, bool& unicast, bool& multicast); bool rib_client_route_info_changed4( // Input values, const IPv4& addr, const uint32_t& prefix_len, const IPv4& nexthop, const uint32_t& metric); bool rib_client_route_info_invalid4( // Input values, const IPv4& addr, const uint32_t& prefix_len); /** * set parameter * * Typically called via XRL's to set which parameters we support * per peer. * * @param iptuple iptuple * @param parameter * @param toggle enable or disable parameter */ bool set_parameter( // Input values, const Iptuple& iptuple, const string& parameter, const bool toggle); /** * Originally inserted for testing. However, now used by all the * "rib_client_route_info_*" methods. */ BGPPlumbing *plumbing_unicast() const { return _plumbing_unicast; } BGPPlumbing *plumbing_multicast() const { return _plumbing_multicast; } XrlStdRouter *get_router() { return _xrl_router; } EventLoop& eventloop() { return _eventloop; } XrlBgpTarget *get_xrl_target() { return _xrl_target; } /** * Call via XrlBgpTarget when the finder reports that a process * has started. * * @param target_class Class of process that has started. * @param target_instance Instance name of process that has started. */ void notify_birth(const string& target_class, const string& target_instance) { _process_watch->birth(target_class, target_instance); } /** * Call via XrlBgpTarget when the finder reports that a process * has terminated. * * @param target_class Class of process that has terminated. * @param target_instance Instance name of process that has terminated. */ void notify_death(const string& target_class, const string& target_instance) { _process_watch->death(target_class, target_instance); } /** * @return Return true when all the processes that BGP is * dependent on are ready. */ bool processes_ready() { return _process_watch->ready(); } /** * @return Return the bgp mib name. */ string bgp_mib_name() const { return "bgp4_mib"; } /** * Check to see if the bgp snmp entity is running. */ bool do_snmp_trap() const { return _process_watch->target_exists(bgp_mib_name()); } /** * To be called when the finder dies. */ void finder_death(const char *file, const int lineno) { _process_watch->finder_death(file, lineno); } /** * Configure a policy filter * * @param filter Id of filter to configure. * @param conf Configuration of filter. */ void configure_filter(const uint32_t& filter, const string& conf); /** * Reset a policy filter. * * @param filter Id of filter to reset. */ void reset_filter(const uint32_t& filter); /** * Push routes through policy filters for re-filtering. */ void push_routes(); /** IPv6 stuff */ #ifdef HAVE_IPV6 /** * Test whether an IPv6 interface/vif/address is enabled. * * @param interface the name of the interface to test. * @param vif the name of the vif to test. * @param address the address to test. * @return true if it exists and is enabled, otherwise false. */ bool is_address_enabled(const string& interface, const string& vif, const IPv6& address) const; typedef XorpCallback5::RefPtr AddressStatus6Cb; /** * Add a callback for tracking the IPv6 interface/vif/address status. * * The callback will be invoked whenever the status of the tuple * (interface, vif, address) is changed from disabled to enabled * or vice-versa. * * @param cb the callback to register. */ void register_address_status(AddressStatus6Cb cb) { _address_status6_cb = cb; } /** * Obtain the subnet prefix length for an IPv6 interface/vif/address. * * @param interface the name of the interface. * @param vif the name of the vif. * @param address the address. * @return the subnet prefix length for the address. */ uint32_t get_prefix_length(const string& interface, const string& vif, const IPv6& address); /** * Is the address one of this routers interface addresses? */ bool interface_address6(const IPv6& address) const; /** * Obtain the prefix length for a particular IPv6 address. * * @param address the address to search for. * @param prefix_len the return-by-reference prefix length * for @ref address. * @return true if the address belongs to this router, otherwise false. */ bool interface_address_prefix_len6(const IPv6& address, uint32_t& prefix_len) const; /** * set IPv6 next-hop. * * @param iptuple iptuple. * @param next-hop * * @return true on success */ bool set_nexthop6(const Iptuple& iptuple, const IPv6& next_hop); /** * get IPv6 next-hop. * * @param iptuple iptuple. * @param next-hop * * @return true on success */ bool get_nexthop6(const Iptuple& iptuple, IPv6& next_hop); /** * Originate an IPv6 route * * @param nlri subnet to announce * @param next_hop to forward to * @param unicast if true install in unicast routing table * @param multicast if true install in multicast routing table * @param policytags policy-tags associated with route. * * @return true on success */ bool originate_route(const IPv6Net& nlri, const IPv6& next_hop, const bool& unicast, const bool& multicast, const PolicyTags& policytags); /** * Withdraw an IPv6 route * * @return true on success */ bool withdraw_route(const IPv6Net& nlri, const bool& unicast, const bool& multicast) const; bool rib_client_route_info_changed6( // Input values, const IPv6& addr, const uint32_t& prefix_len, const IPv6& nexthop, const uint32_t& metric); bool rib_client_route_info_invalid6( // Input values, const IPv6& addr, const uint32_t& prefix_len); #endif //ipv6 #ifndef XORP_DISABLE_PROFILE /** * @return a reference to the profiler. */ Profile& profile() {return _profile;} #endif protected: private: /** * A method invoked when the status of a service changes. * * @param service the service whose status has changed. * @param old_status the old status. * @param new_status the new status. */ void status_change(ServiceBase* service, ServiceStatus old_status, ServiceStatus new_status); /** * Obtain a pointer to the interface manager service base. * * @return a pointer to the interface manager service base. */ const ServiceBase* ifmgr_mirror_service_base() const { return dynamic_cast(_ifmgr); } /** * Obtain a reference to the interface manager's interface tree. * * @return a reference to the interface manager's interface tree. */ const IfMgrIfTree& ifmgr_iftree() const { return _ifmgr->iftree(); } /** * An IfMgrHintObserver method invoked when the initial interface tree * information has been received. */ void tree_complete(); /** * An IfMgrHintObserver method invoked whenever the interface tree * information has been changed. */ void updates_made(); /** * Callback method that is invoked when the status of an address changes. */ void address_status_change4(const string& interface, const string& vif, const IPv4& source, uint32_t prefix_len, bool state); /** * Store the socket descriptor and iptuple together. */ struct Server { Server(XorpFd fd, const Iptuple& iptuple) : _serverfd(fd) { _tuples.push_back(iptuple); } Server(const Server& rhs) { _serverfd = rhs._serverfd; _tuples = rhs._tuples; } Server& operator=(const Server& rhs) { if (&rhs == this) return *this; _serverfd = rhs._serverfd; _tuples = rhs._tuples; return *this; } string str() { return c_format("fd(%s)", _serverfd.str().c_str()); } XorpFd _serverfd; list _tuples; }; list _serverfds; /** * Callback method called when a connection attempt is made. */ void connect_attempt(XorpFd fd, IoEventType type, string laddr, uint16_t lport); template void extract_attributes(// Input values, PAListRef attributes, // Output values, uint32_t& origin, vector& aspath, A& nexthop, int32_t& med, int32_t& localpref, int32_t& atomic_agg, vector& aggregator, int32_t& calc_localpref, vector& attr_unknown); EventLoop& _eventloop; bool _exit_loop; BGPPeerList *_peerlist; // List of current BGP peers. BGPPeerList *_deleted_peerlist; // List of deleted BGP peers. /** * Unicast Routing Table. SAFI = 1. */ BGPPlumbing *_plumbing_unicast; NextHopResolver *_next_hop_resolver_ipv4; /** * Multicast Routing Table. SAFI = 2. */ BGPPlumbing *_plumbing_multicast; /** * Token generator to map between unicast and multicast. */ template class RoutingTableToken { public: RoutingTableToken() : _last(0) {} uint32_t create(uint32_t& internal_token, const IPNet& prefix, const bool& unicast, const bool& multicast) { while(_tokens.find(_last) != _tokens.end()) _last++; _tokens.insert(make_pair(_last, WhichTable(internal_token, prefix, unicast, multicast))); return _last; } bool lookup(uint32_t& token, IPNet& prefix, bool& unicast, bool& multicast) { typename map ::iterator i; i = _tokens.find(token); if (i == _tokens.end()) return false; WhichTable t = i->second; token = t._token; prefix = t._prefix; unicast = t._unicast; multicast = t._multicast; return true; } void erase(uint32_t& token) { _tokens.erase(token); } private: struct WhichTable { WhichTable() {} WhichTable(uint32_t token, IPNet prefix, bool unicast, bool multicast) : _token(token), _prefix(prefix), _unicast(unicast), _multicast(multicast) {} uint32_t _token; IPNet _prefix; bool _unicast; bool _multicast; }; map _tokens; uint32_t _last; }; template RoutingTableToken& get_token_table(); RoutingTableToken _table_ipv4; XrlBgpTarget *_xrl_target; RibIpcHandler *_rib_ipc_handler; AggregationHandler *_aggregation_handler; LocalData *_local_data; XrlStdRouter *_xrl_router; ProcessWatch *_process_watch; VersionFilters _policy_filters; #ifndef XORP_DISABLE_PROFILE Profile _profile; #endif size_t _component_count; IfMgrXrlMirror* _ifmgr; bool _is_ifmgr_ready; IfMgrIfTree _iftree; // The interface state information InterfaceStatusCb _interface_status_cb; VifStatusCb _vif_status_cb; AddressStatus4Cb _address_status4_cb; map _interfaces_ipv4; // IPv4 interface addresses bool _first_policy_push; // Don't form peerings until the // first policy push is seen. #ifdef HAVE_IPV6 /** * Callback method that is invoked when the status of an address changes. */ void address_status_change6(const string& interface, const string& vif, const IPv6& source, uint32_t prefix_len, bool state); NextHopResolver *_next_hop_resolver_ipv6; RoutingTableToken _table_ipv6; AddressStatus6Cb _address_status6_cb; map _interfaces_ipv6; // IPv6 interface addresses #endif //ipv6 }; template bool BGPMain::get_route_list_start(uint32_t& token, const IPNet& prefix, const bool& unicast, const bool& multicast) { if (unicast) { token = _plumbing_unicast->create_route_table_reader(prefix); } else if (multicast) { token = _plumbing_multicast->create_route_table_reader(prefix); } else { XLOG_ERROR("Must specify at least one of unicast or multicast"); return false; } token = get_token_table().create(token, prefix, unicast, multicast); return true; } template void BGPMain::extract_attributes(// Input values, PAListRef attributes, // Output values, uint32_t& origin, vector& aspath, A& nexthop, int32_t& med, int32_t& localpref, int32_t& atomic_agg, vector& aggregator, int32_t& calc_localpref, vector& attr_unknown) { FastPathAttributeList fpa_list(attributes); origin = fpa_list.origin(); fpa_list.aspath().encode_for_mib(aspath); nexthop = fpa_list.nexthop(); const MEDAttribute* med_att = fpa_list.med_att(); if (med_att) { med = (int32_t)med_att->med(); if (med < 0) { med = 0x7ffffff; XLOG_WARNING("MED truncated in MIB from %u to %u\n", XORP_UINT_CAST(med_att->med()), XORP_UINT_CAST(med)); } } else { med = -1; } const LocalPrefAttribute* local_pref_att = fpa_list.local_pref_att(); if (local_pref_att) { localpref = (int32_t)local_pref_att->localpref(); if (localpref < 0) { localpref = 0x7ffffff; XLOG_WARNING("LOCAL_PREF truncated in MIB from %u to %u\n", XORP_UINT_CAST(local_pref_att->localpref()), XORP_UINT_CAST(localpref)); } } else { localpref = -1; } if (fpa_list.atomic_aggregate_att()) atomic_agg = 2; else atomic_agg = 1; const AggregatorAttribute* agg_att = fpa_list.aggregator_att(); if (agg_att) { aggregator.resize(6); agg_att->route_aggregator().copy_out(&aggregator[0]); agg_att->aggregator_as().copy_out(&aggregator[4]); } else { assert(aggregator.size()==0); } calc_localpref = 0; attr_unknown.resize(0); } template bool BGPMain::get_route_list_next( // Input values, const uint32_t& token, // Output values, IPv4& peer_id, IPNet& net, uint32_t& origin, vector& aspath, A& nexthop, int32_t& med, int32_t& localpref, int32_t& atomic_agg, vector& aggregator, int32_t& calc_localpref, vector& attr_unknown, bool& best, bool& unicast_global, bool& multicast_global) { IPNet prefix; bool unicast = false, multicast = false; uint32_t internal_token, global_token; internal_token = global_token = token; if (!get_token_table().lookup(internal_token, prefix, unicast, multicast)) return false; const SubnetRoute* route; if (unicast) { if (_plumbing_unicast->read_next_route(internal_token, route, peer_id)) { net = route->net(); extract_attributes(route->attributes(), origin, aspath, nexthop, med, localpref, atomic_agg, aggregator, calc_localpref, attr_unknown); best = route->is_winner(); unicast_global = true; multicast_global = false; return true; } // We may have been asked for the unicast and multicast // routing tables. In which case once we have completed the // unicast routing table move onto providing the multicast // table. get_token_table().erase(global_token); if (multicast) { internal_token = _plumbing_multicast->create_route_table_reader(prefix); global_token = get_token_table(). create(internal_token, prefix, false, true); } } if (multicast) { if (_plumbing_multicast->read_next_route(internal_token, route, peer_id)) { net = route->net(); extract_attributes(route->attributes(), origin, aspath, nexthop, med, localpref, atomic_agg, aggregator, calc_localpref, attr_unknown); best = route->is_winner(); unicast_global = false; multicast_global = true; return true; } get_token_table().erase(global_token); } return false; } // template // struct NameOf { // static const char* get() { return "Unknown"; } // }; // template <> const char* NameOf::get() { return "IPv4"; } // template <> const char* NameOf::get() { return "IPv6"; } #endif // __BGP_MAIN_HH__ xorp/bgp/peer_route_pair.hh0000664000076400007640000001114211540224220016124 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/bgp/peer_route_pair.hh,v 1.21 2008/10/02 21:56:17 bms Exp $ #ifndef __BGP_PEER_ROUTE_PAIR_HH__ #define __BGP_PEER_ROUTE_PAIR_HH__ #include "libxorp/xlog.h" #include "libxorp/timer.hh" #include "libxorp/timeval.hh" #include "libxorp/timer.hh" template class RouteQueueEntry; template class BGPRouteTable; template class PeerTableInfo { public: PeerTableInfo(BGPRouteTable *init_route_table, const PeerHandler *ph, uint32_t genid) { _route_table = init_route_table; _peer_handler = ph; _genid = genid; _is_ready = true; _has_queued_data = false; _waiting_for_get = false; TimerList::system_gettimeofday(&_wakeup_sent); } PeerTableInfo(const PeerTableInfo& other) { _route_table = other._route_table; _peer_handler = other._peer_handler; _genid = other._genid; _is_ready = other._is_ready; _has_queued_data = other._has_queued_data; _peer_number = other._peer_number; if (_has_queued_data) { _posn = other._posn; } _waiting_for_get = other._waiting_for_get; _wakeup_sent = other._wakeup_sent; } ~PeerTableInfo() { _wakeup_sent = TimeVal::ZERO(); } BGPRouteTable *route_table() const { return _route_table; } const PeerHandler* peer_handler() const { return _peer_handler; } void set_genid(uint32_t genid) { _genid = genid; } uint32_t genid() const { return _genid; } bool is_ready() const {return _is_ready;} void set_is_ready() {_is_ready = true;} void set_is_not_ready() {_is_ready = false;} bool has_queued_data() const {return _has_queued_data;} void set_has_queued_data(bool has_data) {_has_queued_data = has_data;} void set_queue_position(typename list*>::iterator posn) { _posn = posn; } typename list*>::iterator queue_position() const { return _posn; } // The following two functions are here to do a sanity check. If // we sent a wakeup to a peer more than 20 minutes ago, and still // haven't received a get_next_message, then something has failed // badly, and we need to dump a core and diagnose what it is that // happened. void received_get() { if (_waiting_for_get) _waiting_for_get = false; } void wakeup_sent() { TimeVal now; TimerList::system_gettimeofday(&now); if (_waiting_for_get) { if ((now.sec() - _wakeup_sent.sec()) > 1200) { /* we sent a wakeup 20 minutes ago, and the peer still hasn't requested the data - something must have gone badly wrong */ string s = "Peer seems to have permanently locked up\n"; s += "Time now: " + now.str() + ", time then: " + _wakeup_sent.str() + "\n"; XLOG_FATAL("%s", s.c_str()); } } else { XLOG_ASSERT(_wakeup_sent != TimeVal::ZERO()); _wakeup_sent = now; _waiting_for_get = true; } } void peer_reset() { _waiting_for_get = false; } private: BGPRouteTable *_route_table; //the next table after //the fanout table const PeerHandler *_peer_handler; bool _has_queued_data; //there is data queued for this peer in the //fanout table int _peer_number; //used to ensure consistency of ordering uint32_t _genid; // this is set when downstream has requested a message be sent, // and we didn't have anything to send. It indicates that a // message may be immediately sent downstream. bool _is_ready; typename list*>::iterator _posn; /*the next item of data to send to this peer in the fanout table queue. This only has meaning if _has_queued_data is true */ bool _waiting_for_get; TimeVal _wakeup_sent; }; #endif // __BGP_PEER_ROUTE_PAIR_HH__ xorp/bgp/route_table_aggregation.cc0000664000076400007640000006422411421137511017617 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net //#define DEBUG_LOGGING //#define DEBUG_PRINT_FUNCTION_NAME #include "bgp_module.h" #include "libxorp/xlog.h" #include "path_attribute.hh" #include "bgp.hh" #include "dump_iterators.hh" #include "route_table_aggregation.hh" template AggregationTable::AggregationTable(string table_name, BGPPlumbing& master, BGPRouteTable *parent_table) : BGPRouteTable("AggregationTable-" + table_name, master.safi()), _master_plumbing(master) { this->_parent = parent_table; } template AggregationTable::~AggregationTable() { if (_aggregates_table.begin() != _aggregates_table.end()) { XLOG_WARNING("AggregatesTable trie was not empty on deletion\n"); } } template int AggregationTable::add_route(InternalMessage &rtmsg, BGPRouteTable *caller) { debug_msg("\n %s\n caller: %s\n rtmsg: %p route: %p\n%s\n", this->tablename().c_str(), caller ? caller->tablename().c_str() : "NULL", &rtmsg, rtmsg.route(), rtmsg.str().c_str()); const SubnetRoute *orig_route = rtmsg.route(); XLOG_ASSERT(caller == this->_parent); XLOG_ASSERT(this->_next_table != NULL); XLOG_ASSERT(orig_route->nexthop_resolved()); XLOG_ASSERT(!rtmsg.attributes()->is_locked()); bool must_push = false; /* * If not marked as aggregation candidate, pass the request * unmodified downstream. DONE. */ uint32_t aggr_prefix_len = rtmsg.route()->aggr_prefix_len(); debug_msg("aggr_prefix_len=%d\n", aggr_prefix_len); if (aggr_prefix_len == SR_AGGR_IGNORE) return this->_next_table->add_route(rtmsg, (BGPRouteTable*)this); /* * If the route has less a specific prefix length then the requested * aggregate, pass the request downstream without considering * to create an aggregate. Since we have to modify the * aggr_prefix_len field of the route, we must operate on a copy * of the original route. */ const IPNet orig_net = rtmsg.net(); const IPNet aggr_net = IPNet(orig_net.masked_addr(), aggr_prefix_len); SubnetRoute *ibgp_r = new SubnetRoute(*orig_route); InternalMessage ibgp_msg(ibgp_r, rtmsg.attributes(), rtmsg.origin_peer(), rtmsg.genid()); // propagate internal message flags if (rtmsg.push()) must_push = true; if (rtmsg.from_previous_peering()) ibgp_msg.set_from_previous_peering(); if (orig_net.prefix_len() < aggr_prefix_len) { // Send the "original" version downstream, and we are DONE. debug_msg("Bogus marking\n"); if (must_push) ibgp_msg.set_push(); ibgp_r->set_aggr_prefix_len(SR_AGGR_IGNORE); int res = this->_next_table-> add_route(ibgp_msg, (BGPRouteTable*)this); ibgp_r->unref(); return res; } // Find existing or create a new aggregate route typename RefTrie >::iterator ai; ai = _aggregates_table.lookup_node(aggr_net); if (ai == _aggregates_table.end()) { LocalData *local_data = _master_plumbing.main().get_local_data(); const AggregateRoute *new_aggr_route = new AggregateRoute(aggr_net, orig_route->aggr_brief_mode(), local_data->get_id(), local_data->get_as()); ai = _aggregates_table.insert(aggr_net, *new_aggr_route); } AggregateRoute *aggr_route = const_cast *> (&ai.payload()); // Check we don't already have the original route stored XLOG_ASSERT(aggr_route->components_table()->lookup_node(orig_net) == aggr_route->components_table()->end()); // We make the assumption here that nothing has modified the path // attribute list since the last time it was canonicalized, or // this will fail to store the chances. aggr_route->components_table()->insert(orig_net, ComponentRoute( rtmsg.route(), rtmsg.origin_peer(), rtmsg.genid(), rtmsg.from_previous_peering())); /* * If our component route holds a more specific prefix than the * aggregate, announce it to EBGP peering branches. */ if (aggr_route->net() != orig_net || aggr_route->is_suppressed()) { SubnetRoute *ebgp_r = new SubnetRoute(*orig_route); InternalMessage ebgp_msg(ebgp_r, rtmsg.attributes(), rtmsg.origin_peer(), rtmsg.genid()); // propagate internal message flags if (rtmsg.from_previous_peering()) ebgp_msg.set_from_previous_peering(); if (aggr_route->is_suppressed()) ebgp_r->set_aggr_prefix_len(SR_AGGR_EBGP_NOT_AGGREGATED); else ebgp_r->set_aggr_prefix_len(SR_AGGR_EBGP_WAS_AGGREGATED); this->_next_table->add_route(ebgp_msg, (BGPRouteTable*)this); ebgp_r->unref(); } /* * Recompute the aggregate. If pa_list different from the old one, * check whether we have to withdraw the old and / or announce the * new aggregate and / or all the component routes. */ aggr_route->reevaluate(this); /* * Send the "original" version downstream. SR_AGGR_IBGP_ONLY marker * will instruct the post-fanout static filters to propagate this * route only to IBGP peerings and localRIB. */ ibgp_r->set_aggr_prefix_len(SR_AGGR_IBGP_ONLY); int res = this->_next_table->add_route(ibgp_msg, (BGPRouteTable*)this); ibgp_r->unref(); if (must_push) this->_next_table->push(this); return res; } template int AggregationTable::delete_route(InternalMessage &rtmsg, BGPRouteTable *caller) { debug_msg("\n %s\n caller: %s\n rtmsg: %p route: %p\n%s\n", this->tablename().c_str(), caller ? caller->tablename().c_str() : "NULL", &rtmsg, rtmsg.route(), rtmsg.str().c_str()); const SubnetRoute *orig_route = rtmsg.route(); XLOG_ASSERT(caller == this->_parent); XLOG_ASSERT(this->_next_table != NULL); XLOG_ASSERT(orig_route->nexthop_resolved()); bool must_push = false; /* * If not marked as aggregation candidate, pass the request * unmodified downstream. DONE. */ uint32_t aggr_prefix_len = orig_route->aggr_prefix_len(); debug_msg("aggr_prefix_len=%d\n", aggr_prefix_len); if (aggr_prefix_len == SR_AGGR_IGNORE) return this->_next_table->delete_route(rtmsg, (BGPRouteTable*)this); /* * If the route has less a specific prefix length then the requested * aggregate, pass the request downstream without considering * to create an aggregate. Since we have to modify the * aggr_prefix_len field of the route, we must operate on a copy * of the original route. */ const IPNet orig_net = rtmsg.net(); const IPNet aggr_net = IPNet(orig_net.masked_addr(), aggr_prefix_len); SubnetRoute *ibgp_r = new SubnetRoute(*orig_route); InternalMessage ibgp_msg(ibgp_r, rtmsg.origin_peer(), rtmsg.genid()); // propagate internal message flags if (rtmsg.push()) must_push = true; if (rtmsg.from_previous_peering()) ibgp_msg.set_from_previous_peering(); if (orig_net.prefix_len() < aggr_prefix_len) { // Send the "original" version downstream, and we are DONE. debug_msg("Bogus marking\n"); if (must_push) ibgp_msg.set_push(); ibgp_r->set_aggr_prefix_len(SR_AGGR_IGNORE); int res = this->_next_table-> delete_route(ibgp_msg, (BGPRouteTable*)this); ibgp_r->unref(); return res; } // Find the appropriate aggregate route typename RefTrie >::iterator ai; ai = _aggregates_table.lookup_node(aggr_net); // Check that the aggregate exists, otherwise something's gone wrong. XLOG_ASSERT(ai != _aggregates_table.end()); AggregateRoute *aggr_route = const_cast *> (&ai.payload()); /* * If our component route holds a more specific prefix than the * aggregate, send a delete rquest for it to EBGP branches. */ if (aggr_route->net() != orig_net || aggr_route->is_suppressed()) { SubnetRoute *ebgp_r = new SubnetRoute(*orig_route); InternalMessage ebgp_msg(ebgp_r, rtmsg.origin_peer(), rtmsg.genid()); // propagate internal message flags if (rtmsg.from_previous_peering()) ebgp_msg.set_from_previous_peering(); if (aggr_route->is_suppressed()) ebgp_r->set_aggr_prefix_len(SR_AGGR_EBGP_NOT_AGGREGATED); else ebgp_r->set_aggr_prefix_len(SR_AGGR_EBGP_WAS_AGGREGATED); this->_next_table->delete_route(ebgp_msg, (BGPRouteTable*)this); ebgp_r->unref(); } aggr_route->components_table()->erase(orig_net); /* * Recompute the aggregate. If pa_list different from the old one, * check whether we have to withdraw the old and / or announce the * new aggregate. */ aggr_route->reevaluate(this); if (aggr_route->components_table()->route_count() == 0) _aggregates_table.erase(aggr_net); /* * Send the "original" version downstream. SR_AGGR_IBGP_ONLY marker * will instruct the post-fanout static filters to propagate this * route only to IBGP peerings and localRIB. */ ibgp_r->set_aggr_prefix_len(SR_AGGR_IBGP_ONLY); int res = this->_next_table-> delete_route(ibgp_msg, (BGPRouteTable*)this); ibgp_r->unref(); if (must_push) this->_next_table->push(this); return res; } template void AggregateRoute::reevaluate(AggregationTable *parent) { typename RefTrie >::PostOrderIterator comp_iter; uint32_t med = 0; bool must_set_atomic_aggr = false; bool old_was_suppressed = _is_suppressed; bool old_was_announced = _was_announced; _is_suppressed = false; PAListRef old_pa_list = _pa_list; // create an FPAList so we can access the elements, and perhaps // pass it downstream for deletion FPAListRef old_fpa_list = new FastPathAttributeList(old_pa_list); NextHopAttribute nhatt(A::ZERO()); ASPath aspath; OriginAttribute igp_origin_att(IGP); FPAListRef fpa_list = new FastPathAttributeList(nhatt, aspath, igp_origin_att); /* * PHASE 1: * * Iterate through all component routes in order to compute the new * path attributes and determine whether we are allowed to announce * the aggregate or not. */ debug_msg("PHASE 1\n"); for (comp_iter = _components_table.begin(); comp_iter != _components_table.end(); comp_iter++) { const SubnetRoute* comp_route = comp_iter.payload().route(); PAListRef comp_pa_list = comp_route->attributes(); FastPathAttributeList comp_fpa_list(comp_pa_list); debug_msg("comp_route: %s\n %s\n", comp_route->net().str().c_str(), comp_fpa_list.aspath().str().c_str()); if (comp_iter == _components_table.begin()) { if (comp_fpa_list.med_att()) { med = comp_fpa_list.med_att()->med(); } fpa_list->replace_AS_path(comp_fpa_list.aspath()); fpa_list->replace_origin((OriginType)comp_fpa_list.origin()); } else { if (comp_fpa_list.med_att() && med != comp_fpa_list.med_att()->med()) { _is_suppressed = true; break; } // Origin attr: INCOMPLETE overrides EGP which overrides IGP if (comp_fpa_list.origin() > old_fpa_list->origin()) fpa_list->replace_origin((OriginType)comp_fpa_list.origin()); // Update the aggregate AS path using this component route. if (this->brief_mode()) { /* * The agggregate will have an empty AS path, in which * case we also must set the ATOMIC AGGREGATE attribute. */ if (fpa_list->aspath() != comp_fpa_list.aspath()) { fpa_list->replace_AS_path(ASPath()); must_set_atomic_aggr = true; } } else { /* * Merge the current AS path with the component route's one * by creating an AS SET for non-matching ASNs. */ fpa_list->replace_AS_path(ASPath(old_fpa_list->aspath(), comp_fpa_list.aspath())); } } // Propagate the ATOMIC AGGREGATE attribute if (comp_fpa_list.atomic_aggregate_att()) must_set_atomic_aggr = true; } // Add a MED attr if needed and allowed to if (med && !(fpa_list->aspath().num_segments() && fpa_list->aspath().segment(0).type() == AS_SET)) { MEDAttribute med_attr(med); fpa_list->add_path_attribute(med_attr); } if (must_set_atomic_aggr) { AtomicAggAttribute aa_attr; fpa_list->add_path_attribute(aa_attr); } fpa_list->add_path_attribute(*_aggregator_attribute); fpa_list->canonicalize(); _pa_list = new PathAttributeList(fpa_list); debug_msg("OLD ATTRIBUTES:%s\n", old_fpa_list->str().c_str()); debug_msg("NEW ATTRIBUTES:%s\n", fpa_list->str().c_str()); /* * Phase 2: * * If the aggregate was announced, and either it should be suppressed * from now on, or its path attributes have changed, delete the old * version. */ debug_msg("PHASE 2\n"); if (_was_announced && (_is_suppressed || !(*old_fpa_list == *fpa_list))) { /* * We need to send a delete request for the old aggregate * before proceeding any further. Construct a temporary * subnet route / message and send downstream. */ SubnetRoute* tmp_route = new SubnetRoute(_net, old_pa_list, NULL, 0); tmp_route->set_nexthop_resolved(true); // Cheating tmp_route->set_aggr_prefix_len(SR_AGGR_EBGP_AGGREGATE); InternalMessage tmp_rtmsg(tmp_route, old_fpa_list, parent->_master_plumbing.rib_handler(), GENID_UNKNOWN); parent->_next_table->delete_route(tmp_rtmsg, parent); tmp_route->unref(); _was_announced = false; } /* * Phase 3: * * If the aggregate must be suppressed now, but was active before, so * we must reannounce all the component routes with proper marking, i.e. * SR_AGGR_EBGP_NOT_AGGREGATED instead of SR_AGGR_EBGP_WAS_AGGREGATED. * Similarly, if previously the aggregate was suppressed, but now it * should not be any more, we must reannounce all the component routes * and mark them as SR_AGGR_EBGP_WAS_AGGREGATED. * * NOTE: If the aggregate contains a large number of component routes, * this naive approach of atomic replacing of all the component routes * until completion can lead to severe lockouts. We might be much * better off with a timer-based replacement routine, but this would * significantly complicate the implementation. */ debug_msg("PHASE 3\n"); if (old_was_suppressed != _is_suppressed) { for (comp_iter = _components_table.begin(); comp_iter != _components_table.end(); comp_iter++) { const SubnetRoute *comp_route = comp_iter.payload().route(); debug_msg("%s\n", comp_route->str().c_str()); SubnetRoute *old_r = new SubnetRoute(*comp_route); SubnetRoute *new_r = new SubnetRoute(*comp_route); InternalMessage old_msg(old_r, comp_iter.payload().origin_peer(), comp_iter.payload().genid()); InternalMessage new_msg(new_r, comp_iter.payload().origin_peer(), comp_iter.payload().genid()); if (old_was_suppressed) { old_r->set_aggr_prefix_len(SR_AGGR_EBGP_NOT_AGGREGATED); new_r->set_aggr_prefix_len(SR_AGGR_EBGP_WAS_AGGREGATED); } else { old_r->set_aggr_prefix_len(SR_AGGR_EBGP_WAS_AGGREGATED); new_r->set_aggr_prefix_len(SR_AGGR_EBGP_NOT_AGGREGATED); } /* * Do not add / remove component routes if they would * conflict with the aggregate. */ if (!(old_was_announced && comp_route->net() == _net)) { debug_msg("deleting\n"); parent->_next_table->delete_route(old_msg, parent); } if (!(!_is_suppressed && comp_route->net() == _net)) { debug_msg("announcing\n"); parent->_next_table->add_route(new_msg, parent); } old_r->unref(); new_r->unref(); } } /* * Phase 4: * * If the new path attributes are different from the old ones, delete * the old version of the aggregate route and announce a fresh one, * unless _is_suppressed flag was set. */ debug_msg("PHASE 4\n"); if (!_was_announced && !_is_suppressed && _components_table.route_count() != 0 && (old_was_suppressed || !(*old_fpa_list == *fpa_list))) { SubnetRoute* tmp_route = new SubnetRoute(_net, _pa_list, NULL, 0); tmp_route->set_nexthop_resolved(true); // Cheating tmp_route->set_aggr_prefix_len(SR_AGGR_EBGP_AGGREGATE); InternalMessage tmp_rtmsg(tmp_route, fpa_list, parent->_master_plumbing.rib_handler(), GENID_UNKNOWN); parent->_next_table->add_route(tmp_rtmsg, parent); tmp_route->unref(); _was_announced = true; } } template int AggregationTable::replace_route(InternalMessage &old_rtmsg, InternalMessage &new_rtmsg, BGPRouteTable *caller) { debug_msg("\n %s\n" "caller: %s\n" "old rtmsg: %p new rtmsg: %p " "old route: %p" "new route: %p" "old: %s\n new: %s\n", this->tablename().c_str(), caller->tablename().c_str(), &old_rtmsg, &new_rtmsg, old_rtmsg.route(), new_rtmsg.route(), old_rtmsg.str().c_str(), new_rtmsg.str().c_str()); XLOG_ASSERT(caller == this->_parent); XLOG_ASSERT(old_rtmsg.route()->nexthop_resolved()); XLOG_ASSERT(new_rtmsg.route()->nexthop_resolved()); if (old_rtmsg.route()->aggr_prefix_len() == SR_AGGR_IGNORE && new_rtmsg.route()->aggr_prefix_len() == SR_AGGR_IGNORE) { return this->_next_table->replace_route(old_rtmsg, new_rtmsg, (BGPRouteTable*)this); } this->delete_route(old_rtmsg, (BGPRouteTable*)caller); return this->add_route(new_rtmsg, (BGPRouteTable*)caller); } template int AggregationTable::push(BGPRouteTable *caller) { debug_msg("Push\n"); XLOG_ASSERT(caller == this->_parent); return this->_next_table->push((BGPRouteTable*)this); } template bool AggregationTable::dump_next_route(DumpIterator& dump_iter) { const PeerHandler* peer = dump_iter.current_peer(); // Propagate the request upstream if not for us. if (peer->get_unique_id() != AGGR_HANDLER_UNIQUE_ID) return this->_parent->dump_next_route(dump_iter); typename RefTrie >::iterator route_iterator; debug_msg("dump iter: %s\n", dump_iter.str().c_str()); if (dump_iter.route_iterator_is_valid()) { debug_msg("route_iterator is valid\n"); route_iterator = dump_iter.aggr_iterator(); // Make sure the iterator is valid. If it is pointing at a // deleted node this comparison will move it forward. if (route_iterator == _aggregates_table.end()) { return false; } //we need to move on to the next node, except if the iterator //was pointing at a deleted node, because then it will have //just been moved to the next node to dump, so we need to dump //the node that the iterator is currently pointing at. if (dump_iter.iterator_got_moved(route_iterator.key()) == false) route_iterator++; } else { debug_msg("route_iterator is not valid\n"); route_iterator = _aggregates_table.begin(); } if (route_iterator == _aggregates_table.end()) { return false; } const AggregateRoute* aggr_rt; for ( ; route_iterator != _aggregates_table.end(); route_iterator++) { aggr_rt = &(route_iterator.payload()); debug_msg("aggr_rt: %s\n", aggr_rt->net().str().c_str()); // propagate downstream if (dump_iter.peer_to_dump_to() != NULL && aggr_rt->was_announced()) { SubnetRoute *tmp_route = new SubnetRoute(aggr_rt->net(), aggr_rt->pa_list(), NULL, 0); tmp_route->set_nexthop_resolved(true); // Cheating tmp_route->set_aggr_prefix_len(SR_AGGR_EBGP_AGGREGATE); PAListRef pa_list = aggr_rt->pa_list(); FPAListRef fpa_list = new FastPathAttributeList(pa_list); InternalMessage rt_msg(tmp_route, fpa_list, peer, GENID_UNKNOWN); this->_next_table->route_dump(rt_msg, (BGPRouteTable*)this, dump_iter.peer_to_dump_to()); break; } } if (route_iterator == _aggregates_table.end()) return false; // Store the new iterator value as its valid. dump_iter.set_aggr_iterator(route_iterator); return true; } template int AggregationTable::route_dump(InternalMessage &rtmsg, BGPRouteTable *caller, const PeerHandler *dump_peer) { debug_msg("\n %s\n caller: %s\n rtmsg: %p route: %p\n%s\n", this->tablename().c_str(), caller ? caller->tablename().c_str() : "NULL", &rtmsg, rtmsg.route(), rtmsg.str().c_str()); const SubnetRoute *orig_route = rtmsg.route(); XLOG_ASSERT(caller == this->_parent); XLOG_ASSERT(this->_next_table != NULL); XLOG_ASSERT(orig_route->nexthop_resolved()); /* * If not marked as aggregation candidate, pass the request * unmodified downstream. DONE. */ uint32_t aggr_prefix_len = rtmsg.route()->aggr_prefix_len(); debug_msg("aggr_prefix_len=%d\n", aggr_prefix_len); if (aggr_prefix_len == SR_AGGR_IGNORE) return this->_next_table->route_dump(rtmsg, (BGPRouteTable*)this, dump_peer); /* * If the route has less a specific prefix length then the requested * aggregate, pass the request downstream without considering * to create an aggregate. Since we have to modify the * aggr_prefix_len field of the route, we must operate on a copy * of the original route. */ const IPNet orig_net = rtmsg.net(); const IPNet aggr_net = IPNet(orig_net.masked_addr(), aggr_prefix_len); SubnetRoute *ibgp_r = new SubnetRoute(*orig_route); InternalMessage ibgp_msg(ibgp_r, rtmsg.origin_peer(), rtmsg.genid()); // propagate internal message flags if (rtmsg.from_previous_peering()) ibgp_msg.set_from_previous_peering(); if (orig_net.prefix_len() < aggr_prefix_len || dump_peer->ibgp()) { // Send the "original" version downstream, and we are DONE. ibgp_r->set_aggr_prefix_len(SR_AGGR_IGNORE); int res = this->_next_table->route_dump(ibgp_msg, (BGPRouteTable*)this, dump_peer); ibgp_r->unref(); return res; } // Find the appropriate aggregate route typename RefTrie >::iterator ai; ai = _aggregates_table.lookup_node(aggr_net); // Check that the aggregate exists, otherwise something's gone wrong. XLOG_ASSERT(ai != _aggregates_table.end()); AggregateRoute *aggr_route = const_cast *> (&ai.payload()); /* * If our component route holds a more specific prefix than the * aggregate, send it downstream. */ if (aggr_route->net() != orig_net || aggr_route->is_suppressed()) { SubnetRoute *ebgp_r = new SubnetRoute(*orig_route); InternalMessage ebgp_msg(ebgp_r, rtmsg.origin_peer(), rtmsg.genid()); // propagate internal message flags if (rtmsg.from_previous_peering()) ebgp_msg.set_from_previous_peering(); if (aggr_route->is_suppressed()) ebgp_r->set_aggr_prefix_len(SR_AGGR_EBGP_NOT_AGGREGATED); else ebgp_r->set_aggr_prefix_len(SR_AGGR_EBGP_WAS_AGGREGATED); int res = this->_next_table->route_dump(ebgp_msg, (BGPRouteTable*)this, dump_peer); ebgp_r->unref(); return res; } debug_msg("XXX Shouldn't get here unless EBGP & aggregate == component\n"); return 0; // XXX REVISIT! } template const SubnetRoute* AggregationTable::lookup_route(const IPNet &net, uint32_t& genid, FPAListRef& pa_list) const { debug_msg("Lookup_route\n"); // XXX Can this ever be called? REVISIT!!! return this->_parent->lookup_route(net, genid, pa_list); } template void AggregationTable::route_used(const SubnetRoute* rt, bool in_use) { debug_msg("route_used\n"); // XXX Can this ever be called? REVISIT!!! this->_parent->route_used(rt, in_use); } template string AggregationTable::str() const { string s = "AggregationTable" + this->tablename(); return s; } template bool AggregationTable::get_next_message(BGPRouteTable *next_table) { debug_msg("next table: %s\n", next_table->tablename().c_str()); // XXX Can this ever be called? REVISIT!!! return 0; // XXX } template void AggregationTable::peering_went_down(const PeerHandler *peer, uint32_t genid, BGPRouteTable *caller) { debug_msg("%s %d %s\n", peer->peername().c_str(), genid, caller->str().c_str()); XLOG_ASSERT(this->_parent == caller); XLOG_ASSERT(this->_next_table != NULL); this->_next_table->peering_went_down(peer, genid, this); } template void AggregationTable::peering_down_complete(const PeerHandler *peer, uint32_t genid, BGPRouteTable *caller) { debug_msg("%s %d %s\n", peer->peername().c_str(), genid, caller->str().c_str()); XLOG_ASSERT(this->_parent == caller); XLOG_ASSERT(this->_next_table != NULL); this->_next_table->peering_down_complete(peer, genid, this); } template void AggregationTable::peering_came_up(const PeerHandler *peer, uint32_t genid, BGPRouteTable *caller) { debug_msg("%s %d %s\n", peer->peername().c_str(), genid, caller->str().c_str()); XLOG_ASSERT(this->_parent == caller); XLOG_ASSERT(this->_next_table != NULL); this->_next_table->peering_came_up(peer, genid, this); } AggregationHandler::AggregationHandler() : PeerHandler("AggregationHandler", NULL, NULL, NULL), _fake_unique_id(AGGR_HANDLER_UNIQUE_ID) { } template class AggregationTable; template class AggregationTable; xorp/bgp/tests/0000775000076400007640000000000011540224220013562 5ustar greearbgreearbxorp/bgp/tests/test_filter.cc0000664000076400007640000004547211540224220016431 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "bgp_module.h" #include "libxorp/xorp.h" #include "libxorp/xorpfd.hh" #include "libxorp/eventloop.hh" #include "libxorp/xlog.h" #include "libxorp/test_main.hh" #ifdef HAVE_PWD_H #include #endif #include "bgp.hh" #include "route_table_base.hh" #include "route_table_filter.hh" #include "route_table_ribin.hh" #include "route_table_debug.hh" #include "path_attribute.hh" #include "local_data.hh" #include "dump_iterators.hh" #include "dummy_next_hop_resolver.hh" bool test_filter(TestInfo& /*info*/) { #ifndef HOST_OS_WINDOWS struct passwd *pwd = getpwuid(getuid()); string filename = "/tmp/test_filter."; filename += pwd->pw_name; #else char *tmppath = (char *)malloc(256); GetTempPathA(256, tmppath); string filename = string(tmppath) + "test_filter"; free(tmppath); #endif EventLoop eventloop; BGPMain bgpmain(eventloop); LocalData localdata(bgpmain.eventloop()); Iptuple iptuple; BGPPeerData *pd1 = new BGPPeerData(localdata, iptuple, AsNum(0), IPv4(),0); BGPPeer peer1(&localdata, pd1, NULL, &bgpmain); PeerHandler handler1("test1", &peer1, NULL, NULL); BGPPeerData *pd2 = new BGPPeerData(localdata, iptuple, AsNum(0), IPv4(),0); BGPPeer peer2(&localdata, pd2, NULL, &bgpmain); PeerHandler handler2("test2", &peer2, NULL, NULL); DummyNextHopResolver next_hop_resolver(bgpmain.eventloop(), bgpmain); // Trivial plumbing. We're not testing the RibInTable here, so // mostly we'll just inject directly into the FilterTable, but we // need an RibInTable here to test lookup_route. RibInTable *ribin_table = new RibInTable("RIB-in", SAFI_UNICAST, &handler1); FilterTable *filter_table = new FilterTable("FILTER", SAFI_UNICAST, ribin_table, next_hop_resolver); filter_table->do_versioning(); ribin_table->set_next_table(filter_table); DebugTable* debug_table = new DebugTable("D1", (BGPRouteTable*)filter_table); filter_table->set_next_table(debug_table); debug_table->set_output_file(filename); debug_table->set_canned_response(ADD_USED); // Add a filter that drops routes with AS 5 in their path filter_table->add_simple_AS_filter(AsNum((5))); // create a load of attributes IPNet net1("1.0.1.0/24"); // IPNet net2("1.0.2.0/24"); IPv4 nexthop1("2.0.0.1"); NextHopAttribute nhatt1(nexthop1); IPv4 nexthop2("2.0.0.2"); NextHopAttribute nhatt2(nexthop2); IPv4 nexthop3("2.0.0.3"); NextHopAttribute nhatt3(nexthop3); OriginAttribute igp_origin_att(IGP); ASPath aspath1; aspath1.prepend_as(AsNum(1)); aspath1.prepend_as(AsNum(2)); aspath1.prepend_as(AsNum(3)); ASPathAttribute aspathatt1(aspath1); ASPath aspath2; aspath2.prepend_as(AsNum(4)); aspath2.prepend_as(AsNum(5)); aspath2.prepend_as(AsNum(6)); ASPathAttribute aspathatt2(aspath2); ASPath aspath3; aspath3.prepend_as(AsNum(7)); aspath3.prepend_as(AsNum(8)); aspath3.prepend_as(AsNum(9)); ASPathAttribute aspathatt3(aspath3); FPAList4Ref fpalist1 = new FastPathAttributeList(nhatt1, aspathatt1, igp_origin_att); PAListRef palist1 = new PathAttributeList(fpalist1); FPAList4Ref fpalist2 = new FastPathAttributeList(nhatt2, aspathatt2, igp_origin_att); PAListRef palist2 = new PathAttributeList(fpalist2); FPAList4Ref fpalist3 = new FastPathAttributeList(nhatt3, aspathatt3, igp_origin_att); PAListRef palist3 = new PathAttributeList(fpalist3); // create a subnet route SubnetRoute *sr1, *sr2; InternalMessage* msg; PolicyTags pt; // ================================================================ // Test1: trivial add and delete // ================================================================ // add a route debug_table->write_comment("TEST 1"); debug_table->write_comment("ADD AND DELETE UNFILTERED"); sr1 = new SubnetRoute(net1, palist1, NULL); msg = new InternalMessage(sr1, &handler1, 1); filter_table->add_route(*msg, ribin_table); debug_table->write_separator(); // delete the route filter_table->delete_route(*msg, ribin_table); debug_table->write_separator(); sr1->unref(); delete msg; // ================================================================ // Test1b: trivial add and delete, AS filtered // ================================================================ // add a route // create a subnet route debug_table->write_comment("TEST 1a"); debug_table->write_comment("ADD AND DELETE FILTERING"); debug_table->write_comment("ADD, FILTERED"); sr1 = new SubnetRoute(net1, palist2, NULL); msg = new InternalMessage(sr1, &handler1, 1); filter_table->add_route(*msg, ribin_table); debug_table->write_separator(); debug_table->write_comment("DELETE, FILTERED"); // delete the route filter_table->delete_route(*msg, ribin_table); debug_table->write_separator(); sr1->unref(); delete msg; // ================================================================ // Test2: trivial replace // ================================================================ // add a route debug_table->write_comment("TEST 2"); sr1 = new SubnetRoute(net1, palist1, NULL); msg = new InternalMessage(sr1, &handler1, 1); sr2 = new SubnetRoute(net1, palist3, NULL); InternalMessage* msg2 = new InternalMessage(sr2, &handler1, 1); debug_table->write_comment("REPLACE ROUTE"); filter_table->replace_route(*msg, *msg2, ribin_table); debug_table->write_separator(); delete msg; delete msg2; sr1->unref(); sr2->unref(); // ================================================================ // Test2a: trivial replace, original route filtered // ================================================================ // add a route debug_table->write_comment("TEST 2a"); sr1 = new SubnetRoute(net1, palist2, NULL); msg = new InternalMessage(sr1, &handler1, 1); sr2 = new SubnetRoute(net1, palist1, NULL); msg2 = new InternalMessage(sr2, &handler1, 1); debug_table->write_comment("REPLACE ROUTE, OLD ROUTE FILTERED"); filter_table->replace_route(*msg, *msg2, ribin_table); debug_table->write_separator(); delete msg; delete msg2; sr1->unref(); sr2->unref(); // ================================================================ // Test2b: trivial replace, new route filtered // ================================================================ // add a route debug_table->write_comment("TEST 2b"); sr1 = new SubnetRoute(net1, palist1, NULL); msg = new InternalMessage(sr1, &handler1, 1); sr2 = new SubnetRoute(net1, palist2, NULL); msg2 = new InternalMessage(sr2, &handler1, 1); debug_table->write_comment("REPLACE ROUTE, NEW ROUTE FILTERED"); filter_table->replace_route(*msg, *msg2, ribin_table); debug_table->write_separator(); delete msg; delete msg2; sr1->unref(); sr2->unref(); // ================================================================ // Test2c: trivial replace, both routes filtered // ================================================================ // add a route debug_table->write_comment("TEST 2c"); sr1 = new SubnetRoute(net1, palist2, NULL); msg = new InternalMessage(sr1, &handler1, 1); sr2 = new SubnetRoute(net1, palist2, NULL); msg2 = new InternalMessage(sr2, &handler1, 1); debug_table->write_comment("REPLACE ROUTE, BOTH ROUTES FILTERED"); filter_table->replace_route(*msg, *msg2, ribin_table); delete msg; delete msg2; sr1->unref(); sr2->unref(); debug_table->write_separator(); // ================================================================ // Test3: route dump // ================================================================ // add a route debug_table->write_comment("TEST 3"); sr1 = new SubnetRoute(net1, palist1, NULL); msg = new InternalMessage(sr1, &handler1, 1); debug_table->write_comment("ROUTE DUMP"); filter_table->route_dump(*msg, ribin_table, &handler2); sr1->unref(); delete msg; debug_table->write_separator(); // ================================================================ // Test3a: route dump, filtered // ================================================================ // add a route debug_table->write_comment("TEST 3a"); sr1 = new SubnetRoute(net1, palist2, NULL); msg = new InternalMessage(sr1, &handler1, 1); debug_table->write_comment("ROUTE_DUMP, FILTERED"); filter_table->route_dump(*msg, ribin_table, &handler2); sr1->unref(); delete msg; debug_table->write_separator(); // ================================================================ // Test4: trivial route lookup // ================================================================ // add a route debug_table->write_comment("TEST 4"); debug_table->write_comment("ADD, LOOKUP AND DELETE UNFILTERED"); ribin_table->add_route(net1, fpalist1, pt); debug_table->write_separator(); debug_table->write_comment("LOOKUP ROUTE"); const SubnetRoute *found_route; uint32_t genid = 0xffff; FPAList4Ref found_fpalist; found_route = filter_table->lookup_route(net1, genid, found_fpalist); assert(found_route != NULL); assert(found_route->net() == net1); #if 0 printf("orig attributes: \n%s\n", palist1->str().c_str()); printf("found attributes1: \n%s\n", found_route->attributes()->str().c_str()); printf("found attributes2: \n%s\n", found_fpalist->str().c_str()); #endif assert(found_route->attributes() == palist1); debug_table->write_comment("TEST SUCCESSFUL"); debug_table->write_separator(); // delete the route ribin_table->delete_route(net1); debug_table->write_separator(); // ================================================================ // Test4a: trivial route lookup, filtered // ================================================================ // add a route debug_table->write_comment("TEST 4a"); debug_table->write_comment("ADD, LOOKUP AND DELETE FILTERED"); debug_table->write_comment("ADD, SHOULD BE FILTERED"); ribin_table->add_route(net1, fpalist2, pt); debug_table->write_separator(); debug_table->write_comment("LOOKUP ROUTE"); found_route = filter_table->lookup_route(net1, genid, found_fpalist); // route should not be found because it was filtered assert(found_route == NULL); debug_table->write_comment("TEST SUCCESSFUL"); debug_table->write_separator(); // delete the route debug_table->write_comment("DELETE, SHOULD BE FILTERED"); ribin_table->delete_route(net1); debug_table->write_separator(); // ================================================================ // Test5: add and delete with a local_pref_insertion filter // ================================================================ filter_table->add_localpref_insertion_filter(100); // add a route debug_table->write_comment("TEST 5"); debug_table->write_comment("ADD AND DELETE"); sr1 = new SubnetRoute(net1, palist1, NULL); msg = new InternalMessage(sr1, &handler1, 1); filter_table->add_route(*msg, ribin_table); sr1->unref(); delete msg; debug_table->write_separator(); // delete the route sr1 = new SubnetRoute(net1, palist1, NULL); msg = new InternalMessage(sr1, &handler1, 1); filter_table->delete_route(*msg, ribin_table); debug_table->write_separator(); sr1->unref(); delete msg; // ================================================================ // Test6: add and delete with a local_pref_insertion filter and a // next_hop_rewrite_filter - both these modify a route // ================================================================ filter_table->add_nexthop_rewrite_filter(IPv4("128.16.64.4"), false, IPNet()); // add a route debug_table->write_comment("TEST 6"); debug_table->write_comment("ADD AND DELETE"); sr1 = new SubnetRoute(net1, palist1, NULL); msg = new InternalMessage(sr1, &handler1, 1); filter_table->add_route(*msg, ribin_table); debug_table->write_separator(); // delete the route filter_table->delete_route(*msg, ribin_table); debug_table->write_separator(); sr1->unref(); delete msg; // ================================================================ // Test 7a: add and delete with a well known attributes filter // This should be passed through, as it has no well-known attributes // ================================================================ filter_table->add_known_community_filter(PEER_TYPE_EBGP); // add a route debug_table->write_comment("TEST 7"); debug_table->write_comment("ADD AND DELETE"); debug_table->write_comment("EXPECT CHANGE TO PROPAGATE"); sr1 = new SubnetRoute(net1, palist1, NULL); msg = new InternalMessage(sr1, &handler1, 1); filter_table->add_route(*msg, ribin_table); debug_table->write_separator(); // delete the route filter_table->delete_route(*msg, ribin_table); debug_table->write_separator(); sr1->unref(); delete msg; // ================================================================ // Test 7b: add and delete with a well known attributes filter // This should not be passed through, as it has NO_EXPORT // ================================================================ // add a route debug_table->write_comment("TEST 7b"); debug_table->write_comment("ADD AND DELETE"); debug_table->write_comment("EXPECT CHANGE NOT TO PROPAGATE"); FPAList4Ref fpalist4 = new FastPathAttributeList(nhatt1, aspathatt1, igp_origin_att); CommunityAttribute comm_att; comm_att.add_community(CommunityAttribute::NO_EXPORT); assert(comm_att.contains(CommunityAttribute::NO_EXPORT)); fpalist4->add_path_attribute(comm_att); PAListRef palist4 = new PathAttributeList(fpalist4); sr1 = new SubnetRoute(net1, palist4, NULL); msg = new InternalMessage(sr1, &handler1, 1); filter_table->add_route(*msg, ribin_table); debug_table->write_separator(); // delete the route filter_table->delete_route(*msg, ribin_table); debug_table->write_separator(); sr1->unref(); delete msg; // ================================================================ // Test 8: test filter versions // old filter should give same results as test 6. // new filter should not modify route. // ================================================================ // add a route debug_table->write_comment("TEST 8a"); debug_table->write_comment("ADD"); debug_table->write_comment("EXPECT AS TEST 6"); sr1 = new SubnetRoute(net1, palist1, NULL); msg = new InternalMessage(sr1, &handler1, 1); filter_table->add_route(*msg, ribin_table); debug_table->write_separator(); // reset filters debug_table->write_comment("RECONFIGURE FILTER"); filter_table->reconfigure_filter(); // add a route debug_table->write_comment("TEST 8b"); debug_table->write_comment("ADD AND DELETE"); debug_table->write_comment("EXPECT ROUTE UNMODIFIED"); sr2 = new SubnetRoute(net1, palist1, NULL); msg2 = new InternalMessage(sr2, &handler1, 2/*GenID*/); filter_table->add_route(*msg2, ribin_table); debug_table->write_separator(); // delete the route filter_table->delete_route(*msg2, ribin_table); debug_table->write_separator(); sr2->unref(); delete msg2; debug_table->write_comment("TEST 8c"); debug_table->write_comment("DELETE"); debug_table->write_comment("EXPECT AS TEST 6"); // delete the route filter_table->delete_route(*msg, ribin_table); debug_table->write_separator(); sr1->unref(); delete msg; // ================================================================ debug_table->write_comment("SHUTDOWN AND CLEAN UP"); delete ribin_table; delete filter_table; delete debug_table; palist1.release(); palist2.release(); palist3.release(); palist4.release(); fpalist1 = 0; fpalist2 = 0; fpalist3 = 0; fpalist4 = 0; FILE *file = fopen(filename.c_str(), "r"); if (file == NULL) { fprintf(stderr, "Failed to read %s\n", filename.c_str()); fprintf(stderr, "TEST FILTER FAILED\n"); fclose(file); return false; } #define BUFSIZE 8192 char testout[BUFSIZE]; memset(testout, 0, BUFSIZE); int bytes1 = fread(testout, 1, BUFSIZE, file); if (bytes1 == BUFSIZE) { fprintf(stderr, "Output too long for buffer\n"); fprintf(stderr, "TEST FILTER FAILED\n"); fclose(file); return false; } fclose(file); string ref_filename; const char* srcdir = getenv("srcdir"); if (srcdir) { ref_filename = string(srcdir); } else { ref_filename = "."; } ref_filename += "/test_filter.reference"; file = fopen(ref_filename.c_str(), "r"); if (file == NULL) { fprintf(stderr, "Failed to read %s\n", ref_filename.c_str()); fprintf(stderr, "TEST FILTER FAILED\n"); fclose(file); return false; } char refout[BUFSIZE]; memset(refout, 0, BUFSIZE); int bytes2 = fread(refout, 1, BUFSIZE, file); if (bytes2 == BUFSIZE) { fprintf(stderr, "Output too long for buffer\n"); fprintf(stderr, "TEST FILTER FAILED\n"); fclose(file); return false; } fclose(file); if ((bytes1 != bytes2) || (memcmp(testout, refout, bytes1) != 0)) { fprintf(stderr, "Output in %s doesn't match reference output\n", filename.c_str()); fprintf(stderr, "TEST FILTER FAILED\n"); return false; } #ifndef HOST_OS_WINDOWS unlink(filename.c_str()); #else DeleteFileA(filename.c_str()); #endif return true; } xorp/bgp/tests/test_packet.cc0000664000076400007640000003141411421137511016406 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net //#define DEBUG_LOGGING #include "bgp_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #ifdef HAVE_FCNTL_H #include #endif #ifdef HAVE_SYS_STAT_H #include #endif #include "bgp.hh" #include "test_packet.hh" #include "socket.hh" /* **************** main *********************** */ int main(int /* argc */, char *argv[]) { BGPTestPacket tp; // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); int retval = tp.run_tests(); // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); return retval; } BGPTestPacket::BGPTestPacket() { } int BGPTestPacket::run_tests() { // Run tests struct test { string test_name; bool (BGPTestPacket::*func)(); } tests[] = { {"Keep alive", &BGPTestPacket::test_keepalive}, {"Open", &BGPTestPacket::test_open}, {"Update IPv4", &BGPTestPacket::test_update}, {"Update IPv6", &BGPTestPacket::test_update_ipv6}, {"Notification", &BGPTestPacket::test_notification}, {"AS path", &BGPTestPacket::test_aspath}, }; bool failed = false; for(unsigned int i = 0; i < sizeof(tests) / sizeof(struct test); i++) { printf("%-15s", tests[i].test_name.c_str()); if(!((*this).*tests[i].func)()) { printf("FAILED\n"); failed = true; } else { printf("passed\n"); } } return !failed ? 0 : -1; } bool BGPTestPacket::test_keepalive() { // test packet writing. // create a KeepAlive packet and then read it back. bool result; // dummy stuff, because encode needs a peer EventLoop eventloop; LocalData localdata(eventloop); Iptuple iptuple; BGPPeerData pd(localdata, iptuple, AsNum(0), IPv4(),0); debug_msg("Starting test of KeepAlivePacket\n\n"); debug_msg("Encoding KeepAlivePacket\n\n"); KeepAlivePacket *ka = create_keepalive(); size_t len = BGPPacket::MAXPACKETSIZE; uint8_t buf[BGPPacket::MAXPACKETSIZE]; assert(ka->encode(buf, len, &pd)); debug_msg("Decoding KeepAlivePacket\n\n"); KeepAlivePacket* keepalivepacket = new KeepAlivePacket(buf,len); debug_msg("Ending test of KeepAlivePacket\n\n"); result = (*ka == *keepalivepacket); delete keepalivepacket; delete ka; return result; } bool BGPTestPacket::test_open() { // test packet writing. // create a Open packet and then read it back. bool result; // dummy stuff, because encode needs a peer EventLoop eventloop; LocalData localdata(eventloop); Iptuple iptuple; BGPPeerData pd(localdata, iptuple, AsNum(0), IPv4(),0); debug_msg("Starting test of OpenPacket\n\n"); debug_msg("Encoding OpenPacket\n\n"); OpenPacket *op = create_open(); size_t len = BGPPacket::MAXPACKETSIZE; uint8_t buf[BGPPacket::MAXPACKETSIZE]; assert(op->encode(buf, len, &pd)); debug_msg("Decoding OpenPacket\n\n"); OpenPacket *openpacket; try { openpacket = new OpenPacket(buf,len); } catch (CorruptMessage &err) { debug_msg("Construction of UpdatePacket from buffer failed\n"); delete op; return false; } debug_msg("Ending test of OpenPacket\n\n"); result = (*openpacket == *op); delete openpacket; delete op; return result; } bool BGPTestPacket::test_update() { // test packet writing. // create a Update packet and then read it back. bool result; // dummy stuff, because encode needs a peer EventLoop eventloop; LocalData localdata(eventloop); localdata.set_as(AsNum(0)); // IBGP Iptuple iptuple; BGPPeerData* pd = new BGPPeerData(localdata, iptuple, AsNum(0), IPv4(),0); pd->compute_peer_type(); BGPMain main(eventloop); BGPPeer peer(&localdata, pd, NULL, &main); debug_msg("Starting test of UpdatePacket\n\n"); debug_msg("Encoding UpdatePacket\n\n"); UpdatePacket *up = create_update(); size_t len = BGPPacket::MAXPACKETSIZE; uint8_t buf[BGPPacket::MAXPACKETSIZE]; assert(up->encode(buf, len, pd)); debug_msg("Decoding UpdatePacket\n\n"); UpdatePacket *updatepacket; try { updatepacket = new UpdatePacket(buf,len, pd, &main, true); } catch (CorruptMessage &err) { debug_msg("Construction of UpdatePacket from buffer failed: %s\n", err.why().c_str()); delete up; return false; } debug_msg("Ending test of UpdatePacket\n\n"); result = (*updatepacket == *up); delete updatepacket; delete up; return result; } bool BGPTestPacket::test_update_ipv6() { // test packet writing. // create a Update packet and then read it back. bool result; // dummy stuff, because encode needs a peer EventLoop eventloop; LocalData localdata(eventloop); localdata.set_as(AsNum(0)); // IBGP Iptuple iptuple; BGPPeerData *pd = new BGPPeerData(localdata, iptuple, AsNum(0), IPv4(),0); pd->compute_peer_type(); BGPMain main(eventloop); BGPPeer peer(&localdata, pd, NULL, &main); debug_msg("Starting test of UpdatePacket IPv6\n\n"); debug_msg("Encoding UpdatePacket IPv6\n\n"); UpdatePacket *up = create_update_ipv6(); size_t len = BGPPacket::MAXPACKETSIZE; uint8_t buf[BGPPacket::MAXPACKETSIZE]; assert(up->encode(buf, len, pd)); debug_msg("Decoding UpdatePacket IPv6\n\n"); UpdatePacket *updatepacket; try { updatepacket = new UpdatePacket(buf,len, pd, &main, true); } catch (CorruptMessage &err) { debug_msg("Construction of UpdatePacket IPv6 from buffer failed\n"); delete up; return false; } debug_msg("Ending test of UpdatePacket IPv6\n\n"); result = (*updatepacket == *up); delete updatepacket; delete up; return result; } bool BGPTestPacket::test_notification() { // test packet writing. // create a Notification packet and then read it back. bool result; // dummy stuff, because encode needs a peer EventLoop eventloop; LocalData localdata(eventloop); Iptuple iptuple; BGPPeerData pd(localdata, iptuple, AsNum(0), IPv4(),0); debug_msg("Starting test of NotificationPacket\n\n"); debug_msg("Encoding NotificationPacket\n\n"); NotificationPacket *np = create_notification(); size_t len = BGPPacket::MAXPACKETSIZE; uint8_t buf[BGPPacket::MAXPACKETSIZE]; assert(np->encode(buf, len, &pd)); debug_msg("Decoding NotificationPacket\n\n"); NotificationPacket *notificationpacket; try { notificationpacket = new NotificationPacket(buf,len); } catch (InvalidPacket& err) { debug_msg("Construction of NotificationPacket from buffer failed\n"); delete np; return false; } debug_msg("Ending test of NotificationPacket\n\n"); result = (*notificationpacket == *np); delete notificationpacket; delete np; return result; } KeepAlivePacket* BGPTestPacket::create_keepalive() { KeepAlivePacket* p = new KeepAlivePacket(); //uint8_t m[16] = {255,1,255,2,255,3,255,4,255,5,255,6,255,7,255,8}; // p->set_marker(m); // XXX no support for random markers right now. return p; } UpdatePacket* BGPTestPacket::create_update() { IPv4Net net[3]; net[0] = IPv4Net("1.2.3.4/32"); net[1] = IPv4Net("5.6.7.8/32"); net[2] = IPv4Net("1.2.3.4/32"); BGPUpdateAttrib wdr(net[2]); ASSegment as_seq; as_seq.set_type(AS_SEQUENCE); debug_msg("sequence length : %u\n", XORP_UINT_CAST(as_seq.as_size())); as_seq.add_as(AsNum(12)); debug_msg("sequence length : %u\n", XORP_UINT_CAST(as_seq.as_size())); as_seq.add_as(AsNum(13)); debug_msg("sequence length : %u\n", XORP_UINT_CAST(as_seq.as_size())); as_seq.add_as(AsNum(14)); ASPath p; p.add_segment(as_seq); debug_msg("sequence length : %u\n", XORP_UINT_CAST(as_seq.as_size())); BGPUpdateAttrib nlr_0(net[0]); BGPUpdateAttrib nlr_1(net[1]); // nlr_0.dump(); // nlr_1.dump(); // wdr.dump(); OriginAttribute origin_att(IGP); ASPathAttribute path_att(p); NextHopAttribute nh_att(IPv4("1.2.3.4")); OriginatorIDAttribute originator_id(IPv4("1.2.3.4")); ClusterListAttribute cluster_list; cluster_list.prepend_cluster_id(IPv4("4.3.2.1")); cluster_list.prepend_cluster_id(IPv4("5.6.7.8")); UpdatePacket *bup = new UpdatePacket(); bup->add_withdrawn(wdr); bup->add_pathatt(origin_att); bup->add_pathatt(path_att); bup->add_pathatt(nh_att); bup->add_pathatt(originator_id); bup->add_nlri(nlr_0); bup->add_nlri(nlr_1); return bup; } UpdatePacket* BGPTestPacket::create_update_ipv6() { IPv4Net net[3]; net[0] = IPv4Net("1.2.3.4/32"); net[1] = IPv4Net("5.6.7.8/32"); net[2] = IPv4Net("1.2.3.4/32"); BGPUpdateAttrib wdr(net[2]); OriginAttribute origin_att(IGP); ASSegment as_seq; as_seq.set_type(AS_SEQUENCE); debug_msg("sequence length : %u\n", XORP_UINT_CAST(as_seq.as_size())); as_seq.add_as(AsNum(12)); debug_msg("sequence length : %u\n", XORP_UINT_CAST(as_seq.as_size())); as_seq.add_as(AsNum(13)); debug_msg("sequence length : %u\n", XORP_UINT_CAST(as_seq.as_size())); as_seq.add_as(AsNum(14)); ASPath p; p.add_segment(as_seq); debug_msg("sequence length : %u\n", XORP_UINT_CAST(as_seq.as_size())); BGPUpdateAttrib nlr_0(net[0]); BGPUpdateAttrib nlr_1(net[1]); // nlr_0.dump(); // nlr_1.dump(); // wdr.dump(); ASPathAttribute path_att(p); NextHopAttribute nh_att(IPv4("8.7.6.5")); UpdatePacket *bup = new UpdatePacket(); bup->add_withdrawn(wdr); bup->add_pathatt(origin_att); bup->add_pathatt(path_att); bup->add_pathatt(nh_att); bup->add_nlri(nlr_0); bup->add_nlri(nlr_1); MPReachNLRIAttribute mpreach(SAFI_UNICAST); mpreach.set_nexthop("20:20:20:20:20:20:20:20"); mpreach.add_nlri("2000::/3"); // mpreach.encode(); debug_msg("%s\n", mpreach.str().c_str()); bup->add_pathatt(mpreach); debug_msg("%s\n", bup->str().c_str()); MPUNReachNLRIAttribute mpunreach(SAFI_UNICAST); mpunreach.add_withdrawn("2000::/3"); // mpunreach.encode(); bup->add_pathatt(mpunreach); return bup; } OpenPacket* BGPTestPacket::create_open() { /* commented out since not sure if authentication data is correctly being set here */ //uint8_t data[16] = {8,6,3,4,5,6,7,8,9,0,1,2,3,4,5,6}; //BGPAuthParameter* p = new BGPAuthParameter(); //p->set_length(6); //p->set_authcode(1); //p->set_authdata(data); //uint8_t data2[16] = {9,7,3,4,5,6,7,8,9,0,1,2,3,4,5,6}; //BGPAuthParameter* p2 = new BGPAuthParameter(); //p2->set_length(6); //p2->set_authcode(2); //p2->set_authdata(data2); //ld->add_parameter(p); //ld->add_parameter(p2); OpenPacket* op = new OpenPacket(AsNum(1234), IPv4("192.168.1.1"), 20 /* holdtime */); //delete ld; return op; } NotificationPacket* BGPTestPacket::create_notification() { return new NotificationPacket(1,1); } bool BGPTestPacket::test_aspath() { AsNum* asnum1 = new AsNum(16); AsNum* asnum2 = new AsNum(16); AsNum* asnum3 = new AsNum(17); AsNum* asnum4 = new AsNum((uint32_t)16); AsNum* asnum5 = new AsNum((uint32_t)16); if (*asnum1 == *asnum2) debug_msg("AS 16 == AS16\n"); else debug_msg("AS 16 != AS16\n"); if (*asnum1 == *asnum3) debug_msg("AS 16 == AS17\n"); else debug_msg("AS 16 != AS17\n"); if (*asnum1 == *asnum4) debug_msg("AS 16 == AS16 (extended)\n"); else debug_msg("AS 16 != AS16 (extended)\n"); if (*asnum4 == *asnum5) debug_msg("AS 16 (extended) == AS16 (extended)\n"); else debug_msg("AS 16 (extended) != AS16 (extended)\n"); delete asnum1; delete asnum2; delete asnum3; delete asnum4; delete asnum5; // dummy return value; return true; } xorp/bgp/tests/test_next_hop_resolver.cc0000664000076400007640000007160411421137511020711 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #include "bgp_module.h" #include "libxorp/xorp.h" #include "libxorp/test_main.hh" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/ipv4.hh" #include "test_next_hop_resolver.hh" #include "next_hop_resolver.hh" #include "route_table_nhlookup.hh" #include "route_table_decision.hh" #include "bgp.hh" template class DummyNhLookupTable : public NhLookupTable { public: DummyNhLookupTable(TestInfo& info, NextHopResolver *nexthop_resolver) : NhLookupTable("tablename", SAFI_UNICAST, nexthop_resolver, 0), _done(false), _info(info) { } void RIB_lookup_done(const A& /*nexthop*/, const set >& /*nets*/, bool /*lookup_succeeded*/) { DOUT(_info) << "Rib lookup done\n"; _done = true; } bool done() { return _done; } private: bool _done; TestInfo& _info; }; template class DummyDecisionTable : public DecisionTable { public: DummyDecisionTable(TestInfo& info, NextHopResolver& nexthop_resolver) : DecisionTable("tablename", SAFI_UNICAST, nexthop_resolver), _done(false), _info(info) { } void igp_nexthop_changed(const A& nexthop) { DOUT(_info) << "next hop changed " << nexthop.str() << endl; _done = true; } bool done() { return _done; } void clear() { _done = false; } private: bool _done; TestInfo& _info; }; template class DummyNextHopResolver2 : public NextHopResolver { public: DummyNextHopResolver2(EventLoop& eventloop, BGPMain& bgp) : NextHopResolver(0, eventloop, bgp) { // Must set a ribname to force RIB interactions. this->register_ribname("bogus"); } }; /** * Register interest in a nexthop */ template bool nhr_test1(TestInfo& info, A nexthop, A real_nexthop, IPNet subnet) { DOUT(info) << "nexthop: " << nexthop.str() << endl; EventLoop eventloop; BGPMain bgp(eventloop); DummyNextHopResolver2 nhr = DummyNextHopResolver2(eventloop, bgp); DummyNhLookupTable nht(info, &nhr); /* ** Register interest in this nexthop. */ nhr.register_nexthop(nexthop, subnet, &nht); /* ** Get a handle to the rib request class so the response method ** can be called. */ NextHopRibRequest *next_hop_rib_request = nhr.get_next_hop_rib_request(); /* ** This is the response that would typically come from the RIB. */ bool resolves = true; uint32_t prefix_len = 16; uint32_t real_prefix_len = 16; A addr = nexthop.mask_by_prefix_len(prefix_len); uint32_t metric = 1; string comment = "testing"; next_hop_rib_request->register_interest_response(XrlError::OKAY(), &resolves, &addr, &prefix_len, &real_prefix_len, &real_nexthop, &metric, nexthop, comment); /* ** Verify that the callback went all the way to the next hop ** table. This must be true before a lookup will succeed. */ if(!nht.done()) { DOUT(info) << "Callback to next hop table failed\n"; return false; } /* ** The nexthop should now be resolvable. */ bool res; uint32_t met; if(!nhr.lookup(nexthop, res, met)) { DOUT(info) << "Nexthop not in table?\n"; return false; } /* ** Make sure that the metrics match the ones that we returned to ** the response function. */ if(resolves != res || metric != met) { DOUT(info) << "Metrics did not match\n"; return false; } /* ** Deregister interest. */ nhr.deregister_nexthop(nexthop, subnet, &nht); /* ** A lookup should fail now. */ if(nhr.lookup(nexthop, res, met)) { DOUT(info) << "Nexthop in table?\n"; return false; } return true; } /** * Register interest in a nexthop multiple times. */ template bool nhr_test2(TestInfo& info, A nexthop, A real_nexthop, IPNet subnet, int reg) { DOUT(info) << "nexthop: " << nexthop.str() << endl; EventLoop eventloop; BGPMain bgp(eventloop); DummyNextHopResolver2 nhr = DummyNextHopResolver2(eventloop, bgp); DummyNhLookupTable nht(info, &nhr); /* ** Register interest in this nexthop. */ for(int i = 0; i < reg; i++) nhr.register_nexthop(nexthop, subnet, &nht); /* ** Get a handle to the rib request class so the response method ** can be called. */ NextHopRibRequest *next_hop_rib_request = nhr.get_next_hop_rib_request(); /* ** This is the response that would typically come from the RIB. */ bool resolves = true; uint32_t prefix_len = 16; uint32_t real_prefix_len = 16; A addr = nexthop.mask_by_prefix_len(prefix_len); uint32_t metric = 1; string comment = "testing"; next_hop_rib_request->register_interest_response(XrlError::OKAY(), &resolves, &addr, &prefix_len, &real_prefix_len, &real_nexthop, &metric, nexthop, comment); /* ** Verify that the callback went all the way to the next hop ** table. This must be true before a lookup will succeed. */ if(!nht.done()) { DOUT(info) << "Callback to next hop table failed\n"; return false; } /* ** The nexthop should now be resolvable. */ bool res; uint32_t met; if(!nhr.lookup(nexthop, res, met)) { DOUT(info) << "Address not in table?\n"; return false; } /* ** Make sure that the metrics match the ones that we returned to ** the response function. */ if(resolves != res || metric != met) { DOUT(info) << "Metrics did not match\n"; return false; } /* ** Deregister interest. */ for(int i = 0; i < reg; i++) nhr.deregister_nexthop(nexthop, subnet, &nht); /* ** A lookup should fail now. */ if(nhr.lookup(nexthop, res, met)) { DOUT(info) << "Nexthop in table?\n"; return false; } return true; } /** * Register interest in a nexthop multiple times but wait for the * first request to resolve before making subsequent calls. */ template bool nhr_test3(TestInfo& info, A nexthop, A real_nexthop, IPNet subnet, int reg) { DOUT(info) << "nexthop: " << nexthop.str() << endl; EventLoop eventloop; BGPMain bgp(eventloop); DummyNextHopResolver2 nhr = DummyNextHopResolver2(eventloop, bgp); DummyNhLookupTable nht(info, &nhr); /* ** Register interest in this nexthop. */ nhr.register_nexthop(nexthop, subnet, &nht); /* ** Get a handle to the rib request class so the response method ** can be called. */ NextHopRibRequest *next_hop_rib_request = nhr.get_next_hop_rib_request(); /* ** This is the response that would typically come from the RIB. */ bool resolves = true; uint32_t prefix_len = 16; uint32_t real_prefix_len = 16; A addr = nexthop.mask_by_prefix_len(prefix_len); uint32_t metric = 1; string comment = "testing"; next_hop_rib_request->register_interest_response(XrlError::OKAY(), &resolves, &addr, &prefix_len, &real_prefix_len, &real_nexthop, &metric, nexthop, comment); /* ** Verify that the callback went all the way to the next hop ** table. This must be true before a lookup will succeed. */ if(!nht.done()) { DOUT(info) << "Callback to next hop table failed\n"; return false; } /* ** The nexthop should now be resolvable. */ bool res; uint32_t met; if(!nhr.lookup(nexthop, res, met)) { DOUT(info) << "Address not in table?\n"; return false; } /* ** Make sure that the metrics match the ones that we returned to ** the response function. */ if(resolves != res || metric != met) { DOUT(info) << "Metrics did not match\n"; return false; } /* ** Register interest in this nexthop a bunch more times */ for(int i = 0; i < reg; i++) nhr.register_nexthop(nexthop, subnet, &nht); /* ** Deregister interest. */ for(int i = 0; i < reg + 1; i++) nhr.deregister_nexthop(nexthop, subnet, &nht); /* ** A lookup should fail now. */ if(nhr.lookup(nexthop, res, met)) { DOUT(info) << "Nexthop in table?\n"; return false; } return true; } /** * Register interest in a nexthop then deregister before the response * from the RIB (pseudo). */ template bool nhr_test4(TestInfo& info, A nexthop, A real_nexthop, IPNet subnet) { DOUT(info) << "nexthop: " << nexthop.str() << endl; EventLoop eventloop; BGPMain bgp(eventloop); DummyNextHopResolver2 nhr = DummyNextHopResolver2(eventloop, bgp); DummyNhLookupTable nht(info, &nhr); /* ** Register interest in this nexthop. */ nhr.register_nexthop(nexthop, subnet, &nht); /* ** Deregister interest before the response function is called. */ nhr.deregister_nexthop(nexthop, subnet, &nht); /* ** Get a handle to the rib request class so the response method ** can be called. */ NextHopRibRequest *next_hop_rib_request = nhr.get_next_hop_rib_request(); /* ** This is the response that would typically come from the RIB. */ bool resolves = true; uint32_t prefix_len = 16; uint32_t real_prefix_len = 16; A addr = nexthop.mask_by_prefix_len(prefix_len); uint32_t metric = 1; string comment = "testing"; next_hop_rib_request->register_interest_response(XrlError::OKAY(), &resolves, &addr, &prefix_len, &real_prefix_len, &real_nexthop, &metric, nexthop, comment); /* ** We have deregistered interest in this nexthop we should not get ** this callback. */ if(nht.done()) { DOUT(info) << "Call to next hop table should not have occured\n"; return false; } /* ** A lookup should fail now. */ bool res; uint32_t met; if(nhr.lookup(nexthop, res, met)) { DOUT(info) << "Nexthop in table?\n"; return false; } return true; } /** * The normal sequence of events is: * 1) Next hop table registers interest in a nexthop. * 2) A query is sent to RIB. * 3) The RIB sends back a response. * 4) The next hop resolver notifies the next hop table that a * response is available. * 5) The decision process performs a lookup. * * It is possible between 4 and 5 that the RIB notifies us that the * answer given in 3 is now invalid. * * The next hop resolver handles this case by returing the old value * to the decision process and requesting the new value from the * RIB. When the new value arrives the decision process is notified. */ template bool nhr_test5(TestInfo& info, A nexthop, A real_nexthop, IPNet subnet) { DOUT(info) << "nexthop: " << nexthop.str() << endl; EventLoop eventloop; BGPMain bgp(eventloop); DummyNextHopResolver2 nhr = DummyNextHopResolver2(eventloop, bgp); DummyDecisionTable dt(info, nhr); DummyNhLookupTable nht(info, &nhr); /* ** Give the next hop resolver a pointer to the decision table. */ nhr.add_decision(&dt); /* ** Register interest in this nexthop. */ nhr.register_nexthop(nexthop, subnet, &nht); /* ** Get a handle to the rib request class so the response method ** can be called. */ NextHopRibRequest *next_hop_rib_request = nhr.get_next_hop_rib_request(); /* ** This is the response that would typically come from the RIB. */ bool resolves = true; uint32_t prefix_len = 16; uint32_t real_prefix_len = 16; A addr = nexthop.mask_by_prefix_len(prefix_len); uint32_t metric = 1; string comment = "testing"; next_hop_rib_request->register_interest_response(XrlError::OKAY(), &resolves, &addr, &prefix_len, &real_prefix_len, &real_nexthop, &metric, nexthop, comment); /* ** Verify that the callback went all the way to the next hop ** table. This must be true before a lookup will succeed. */ if(!nht.done()) { DOUT(info) << "Callback to next hop table failed\n"; return false; } /* ** Simulate the RIB calling us back and telling us this sucker is ** invalid. */ if(!nhr.rib_client_route_info_invalid(addr, prefix_len)) { DOUT(info) << "Marking address as invalid failed\n"; return false; } /* ** The nexthop should still be available. */ bool res; uint32_t met; if(!nhr.lookup(nexthop, res, met)) { DOUT(info) << "Nexthop not in table?\n"; return false; } /* ** Make sure that the metrics match the ones that we returned to ** the response function. */ if(resolves != res || metric != met) { DOUT(info) << "Metrics did not match\n"; return false; } /* ** Respond to the second request that should have been made. */ metric++; // Change metric to defeat no change optimisation. next_hop_rib_request->register_interest_response(XrlError::OKAY(), &resolves, &addr, &prefix_len, &real_prefix_len, &real_nexthop, &metric, nexthop, comment); /* ** Verify that we called the decision process with the new ** metrics. */ if(!dt.done()) { DOUT(info) << "Callback to decision table failed\n"; return false; } /* ** The decision process would perform the lookup again. */ if(!nhr.lookup(nexthop, res, met)) { DOUT(info) << "Nexthop not in table?\n"; return false; } /* ** Make sure that the metrics match the ones that we returned to ** the response function. */ if(resolves != res || metric != met) { DOUT(info) << "Metrics did not match\n"; return false; } /* ** Deregister interest. */ nhr.deregister_nexthop(nexthop, subnet, &nht); /* ** A lookup should fail now. */ if(nhr.lookup(nexthop, res, met)) { DOUT(info) << "Nexthop in table?\n"; return false; } return true; } /** * Register interest in a nexthop then deregister before the response * from the RIB. */ template bool nhr_test6(TestInfo& info, A nexthop, A real_nexthop, IPNet subnet) { DOUT(info) << "nexthop: " << nexthop.str() << endl; EventLoop eventloop; BGPMain bgp(eventloop); DummyNextHopResolver2 nhr = DummyNextHopResolver2(eventloop, bgp); DummyNhLookupTable nht(info, &nhr); /* ** Register interest in this nexthop. */ nhr.register_nexthop(nexthop, subnet, &nht); /* ** Deregister interest immediately. */ nhr.deregister_nexthop(nexthop, subnet, &nht); /* ** Get a handle to the rib request class so the response method ** can be called. */ NextHopRibRequest *next_hop_rib_request = nhr.get_next_hop_rib_request(); /* ** This is the response that would typically come from the RIB. */ bool resolves = true; uint32_t prefix_len = 16; uint32_t real_prefix_len = 16; A addr = nexthop.mask_by_prefix_len(prefix_len); uint32_t metric = 1; string comment = "testing"; next_hop_rib_request->register_interest_response(XrlError::OKAY(), &resolves, &addr, &prefix_len, &real_prefix_len, &real_nexthop, &metric, nexthop, comment); /* ** Verify that the callback did not get back to the next hop table. */ if(nht.done()) { DOUT(info) << "Callback to next hop table?\n"; return false; } /* ** A lookup should fail. */ bool res; uint32_t met; if(nhr.lookup(nexthop, res, met)) { DOUT(info) << "Nexthop in table?\n"; return false; } return true; } /** * 1) Register interest in a nexthop. * 2) Get response from RIB * 3) Get the RIB to tell us that the nexthop is invalid. * 4) The next hop resolver automatically makes another request of the * RIB. * 5) Before the response gets back from the RIB deregister. */ template bool nhr_test7(TestInfo& info, A nexthop, A real_nexthop, IPNet subnet) { DOUT(info) << "nexthop: " << nexthop.str() << endl; EventLoop eventloop; BGPMain bgp(eventloop); DummyNextHopResolver2 nhr = DummyNextHopResolver2(eventloop, bgp); DummyDecisionTable dt(info, nhr); DummyNhLookupTable nht(info, &nhr); /* ** Give the next hop resolver a pointer to the decision table. */ nhr.add_decision(&dt); /* ** Register interest in this nexthop. */ nhr.register_nexthop(nexthop, subnet, &nht); /* ** Get a handle to the rib request class so the response method ** can be called. */ NextHopRibRequest *next_hop_rib_request = nhr.get_next_hop_rib_request(); /* ** This is the response that would typically come from the RIB. */ bool resolves = true; uint32_t prefix_len = 16; uint32_t real_prefix_len = 16; A addr = nexthop.mask_by_prefix_len(prefix_len); uint32_t metric = 1; string comment = "testing"; next_hop_rib_request->register_interest_response(XrlError::OKAY(), &resolves, &addr, &prefix_len, &real_prefix_len, &real_nexthop, &metric, nexthop, comment); /* ** Verify that the callback went all the way to the next hop ** table. This must be true before a lookup will succeed. */ if(!nht.done()) { DOUT(info) << "Callback to next hop table failed\n"; return false; } /* ** The nexthop should now be resolvable. */ bool res; uint32_t met; if(!nhr.lookup(nexthop, res, met)) { DOUT(info) << "Nexthop not in table?\n"; return false; } /* ** Make sure that the metrics match the ones that we returned to ** the response function. */ if(resolves != res || metric != met) { DOUT(info) << "Metrics did not match\n"; return false; } /* ** Simulate the RIB calling us back and telling us this sucker is ** invalid. */ if(!nhr.rib_client_route_info_invalid(addr, prefix_len)) { DOUT(info) << "Marking address as invalid failed\n"; return false; } /* ** Deregister interest. */ nhr.deregister_nexthop(nexthop, subnet, &nht); /* ** Respond to the second request that should have been made. */ metric++; // Change metric to defeat no change optimisation. next_hop_rib_request->register_interest_response(XrlError::OKAY(), &resolves, &addr, &prefix_len, &real_prefix_len, &real_nexthop, &metric, nexthop, comment); /* ** Verify that we don't call back to the decision table. */ if(dt.done()) { DOUT(info) << "Callback to decision table?\n"; return false; } /* ** A lookup should fail now. */ if(nhr.lookup(nexthop, res, met)) { DOUT(info) << "Nexthop in table?\n"; return false; } return true; } /** * 1) Register interest in a nexthop. * 2) Get response from RIB. * 3) Send a bunch of metrics have changed calls from the RIB. * 4) Verify that decision gets called with the new metrics. */ template bool nhr_test8(TestInfo& info, A nexthop, A real_nexthop, IPNet subnet) { DOUT(info) << "nexthop: " << nexthop.str() << endl; EventLoop eventloop; BGPMain bgp(eventloop); DummyNextHopResolver2 nhr = DummyNextHopResolver2(eventloop, bgp); DummyDecisionTable dt(info, nhr); DummyNhLookupTable nht(info, &nhr); /* ** Give the next hop resolver a pointer to the decision table. */ nhr.add_decision(&dt); /* ** Register interest in this nexthop. */ nhr.register_nexthop(nexthop, subnet, &nht); /* ** Get a handle to the rib request class so the response method ** can be called. */ NextHopRibRequest *next_hop_rib_request = nhr.get_next_hop_rib_request(); /* ** This is the response that would typically come from the RIB. */ bool resolves = false; uint32_t prefix_len = 16; uint32_t real_prefix_len = 16; A addr = nexthop.mask_by_prefix_len(prefix_len); uint32_t metric = 1; string comment = "testing"; next_hop_rib_request->register_interest_response(XrlError::OKAY(), &resolves, &addr, &prefix_len, &real_prefix_len, &real_nexthop, &metric, nexthop, comment); /* ** Verify that the callback went all the way to the next hop ** table. This must be true before a lookup will succeed. */ if(!nht.done()) { DOUT(info) << "Callback to next hop table failed\n"; return false; } /* ** Case 1: A nexthop has been marked invalid we re-register ** interest but the metrics have not changed so we don't get a ** callback to the decision table. */ /* ** Simulate the RIB calling us back and telling us this sucker is ** invalid. */ if(!nhr.rib_client_route_info_invalid(addr, prefix_len)) { DOUT(info) << "Marking address as invalid failed\n"; return false; } /* ** Respond to the second request that should have been made. */ next_hop_rib_request->register_interest_response(XrlError::OKAY(), &resolves, &addr, &prefix_len, &real_prefix_len, &real_nexthop, &metric, nexthop, comment); /* ** The metrics haven't changed we shouldn't call back to the ** decision process. */ if(dt.done()) { DOUT(info) << "Callback to decision table?\n"; return false; } /* ** Make sure the lookup still works. */ bool res; uint32_t met; if(!nhr.lookup(nexthop, res, met)) { DOUT(info) << "Nexthop not in table?\n"; return false; } /* ** Make sure that the metrics match the ones that we returned to ** the response function. */ if(resolves != res || metric != met) { DOUT(info) << "Metrics did not match\n"; return false; } /* ** Case 2: A nexthop has been marked invalid we re-register ** interest. We will change the metric value but the route does ** not resolve so the metric value is irrelevant. We still should ** not get the callback. */ /* ** Simulate the RIB calling us back and telling us this sucker is ** invalid. */ if(!nhr.rib_client_route_info_invalid(addr, prefix_len)) { DOUT(info) << "Marking address as invalid failed\n"; return false; } /* ** Respond to the second request that should have been made. */ metric++; // Change the metric next_hop_rib_request->register_interest_response(XrlError::OKAY(), &resolves, &addr, &prefix_len, &real_prefix_len, &real_nexthop, &metric, nexthop, comment); /* ** The metrics haven't changed we shouldn't call back to the ** decision process. */ if(dt.done()) { DOUT(info) << "Callback to decision table?\n"; return false; } /* ** Make sure the lookup still works. */ if(!nhr.lookup(nexthop, res, met)) { DOUT(info) << "Nexthop not in table?\n"; return false; } /* ** Make sure that the metrics match the ones that we returned to ** the response function. */ if(resolves != res || metric != met) { DOUT(info) << "Metrics did not match\n"; return false; } /* ** Case 3: A nexthop has been marked invalid we re-register ** interest. This time make the next hop resolvable we should get ** the callback. */ /* ** Simulate the RIB calling us back and telling us this sucker is ** invalid. */ if(!nhr.rib_client_route_info_invalid(addr, prefix_len)) { DOUT(info) << "Marking address as invalid failed\n"; return false; } /* ** Respond to the second request that should have been made. */ resolves = true; next_hop_rib_request->register_interest_response(XrlError::OKAY(), &resolves, &addr, &prefix_len, &real_prefix_len, &real_nexthop, &metric, nexthop, comment); /* ** The next hop now resolves we expect a callback to the decison process. */ if(!dt.done()) { DOUT(info) << "Callback to decision table failed\n"; return false; } /* ** Make sure the lookup still works. */ if(!nhr.lookup(nexthop, res, met)) { DOUT(info) << "Nexthop not in table?\n"; return false; } /* ** Make sure that the metrics match the ones that we returned to ** the response function. */ if(resolves != res || metric != met) { DOUT(info) << "Metrics did not match\n"; return false; } /* ** Case 4: A nexthop has been marked invalid we re-register ** interest. The next hop is already resolvable just change the ** metric. */ /* ** Simulate the RIB calling us back and telling us this sucker is ** invalid. */ if(!nhr.rib_client_route_info_invalid(addr, prefix_len)) { DOUT(info) << "Marking address as invalid failed\n"; return false; } /* ** Respond to the second request that should have been made. */ dt.clear(); metric++; next_hop_rib_request->register_interest_response(XrlError::OKAY(), &resolves, &addr, &prefix_len, &real_prefix_len, &real_nexthop, &metric, nexthop, comment); /* ** The next hop now resolves we expect a callback to the decison process. */ if(!dt.done()) { DOUT(info) << "Callback to decision table failed\n"; return false; } /* ** Make sure the lookup still works. */ if(!nhr.lookup(nexthop, res, met)) { DOUT(info) << "Nexthop not in table?\n"; return false; } /* ** Make sure that the metrics match the ones that we returned to ** the response function. */ if(resolves != res || metric != met) { DOUT(info) << "Metrics did not match\n"; return false; } /* ** Deregister interest. */ nhr.deregister_nexthop(nexthop, subnet, &nht); /* ** A lookup should fail now. */ if(nhr.lookup(nexthop, res, met)) { DOUT(info) << "Nexthop in table?\n"; return false; } return true; } /** * On a loaded system many registrations and de-registrations may be * queued while waiting for the RIB to respond. * * In this test the RIB will never respond. These used to be a bug in * the next_hop_resolver that if a registration was made twice the * first deregistration would throw away all the state. The second * deregistration would then fail. */ template bool nhr_test9(TestInfo& info, A nexthop, A /*real_nexthop*/, IPNet subnet, int reg) { DOUT(info) << "nexthop: " << nexthop.str() << endl; EventLoop eventloop; BGPMain bgp(eventloop); DummyNextHopResolver2 nhr = DummyNextHopResolver2(eventloop, bgp); DummyNhLookupTable nht(info, &nhr); nhr.register_nexthop(nexthop, subnet, &nht); nhr.register_nexthop(nexthop, subnet, &nht); nhr.deregister_nexthop(nexthop, subnet, &nht); nhr.deregister_nexthop(nexthop, subnet, &nht); /* ** Register interest in this nexthop. */ for(int i = 0; i < reg; i++) nhr.register_nexthop(nexthop, subnet, &nht); for(int i = 0; i < reg; i++) nhr.deregister_nexthop(nexthop, subnet, &nht); return true; } /* ** This function is never called it exists to instantiate the ** templatised functions. */ void dummy() { XLOG_FATAL("Not to be called"); callback(nhr_test1); callback(nhr_test1); callback(nhr_test2); callback(nhr_test2); callback(nhr_test3); callback(nhr_test3); callback(nhr_test4); callback(nhr_test4); callback(nhr_test5); callback(nhr_test5); callback(nhr_test6); callback(nhr_test6); callback(nhr_test7); callback(nhr_test7); callback(nhr_test8); callback(nhr_test8); callback(nhr_test9); callback(nhr_test9); } xorp/bgp/tests/test_ribout.reference0000664000076400007640000000516011421137511020013 0ustar greearbgreearb[comment] TEST 1 [comment] ADD AND DELETE [PEER: START_PACKET] [PEER: ADD_ROUTE, IBGP] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [PEER: PUSH_PACKET] [separator]------------------------------------- [PEER: START_PACKET] [PEER: DELETE_ROUTE, IBGP] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [PEER: PUSH_PACKET] [separator]------------------------------------- [comment] TEST 2 [comment] THREE ADDS ON TWO PA_LISTS [PEER: START_PACKET] [PEER: ADD_ROUTE, IBGP] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [PEER: ADD_ROUTE, IBGP] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [PEER: PUSH_PACKET] [PEER: START_PACKET] [PEER: ADD_ROUTE, IBGP] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/5, AS/4] [PEER: PUSH_PACKET] [separator]------------------------------------- [comment] TEST 3 [comment] DELETE THEN ADD [PEER: START_PACKET] [PEER: REPLACE_ROUTE] [PEER: OLD, IBGP] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [PEER: NEW, IBGP] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [PEER: PUSH_PACKET] [separator]------------------------------------- [comment] TEST 4 [comment] ADD WHEN PEER BUSY [PEER: START_PACKET] [PEER: ADD_ROUTE, IBGP] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [PEER: PUSH_PACKET] [separator]------------------------------------- [comment] PEER_GOES IDLE [comment] TEST 5 [comment] SWITCH FROM IBGP PEER TO EBGP PEER [PEER: START_PACKET] [PEER: REPLACE_ROUTE] [PEER: OLD, IBGP] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [PEER: NEW, EBGP] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/5, AS/4] [PEER: PUSH_PACKET] [separator]------------------------------------- [comment] PEER_GOES IDLE [separator]------------------------------------- [comment] SHUTDOWN AND CLEAN UP xorp/bgp/tests/test_ribout.cc0000664000076400007640000002440511540224220016441 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "bgp_module.h" #include "libxorp/xorp.h" #include "libxorp/xorpfd.hh" #include "libxorp/eventloop.hh" #include "libxorp/xlog.h" #include "libxorp/asnum.hh" #include "libxorp/test_main.hh" #ifdef HAVE_PWD_H #include #endif #include "bgp.hh" #include "route_table_base.hh" #include "route_table_ribout.hh" #include "route_table_debug.hh" #include "peer_handler_debug.hh" #include "path_attribute.hh" #include "local_data.hh" #include "dump_iterators.hh" bool test_ribout(TestInfo& /*info*/) { #ifndef HOST_OS_WINDOWS struct passwd *pwd = getpwuid(getuid()); string filename = "/tmp/test_ribout."; filename += pwd->pw_name; #else char *tmppath = (char *)malloc(256); GetTempPathA(256, tmppath); string filename = string(tmppath) + "test_ribout"; free(tmppath); #endif EventLoop eventloop; BGPMain bgpmain(eventloop); LocalData localdata(bgpmain.eventloop()); localdata.set_as(AsNum(1)); Iptuple iptuple; BGPPeerData *peer_data1 = new BGPPeerData(localdata, iptuple, AsNum(1), IPv4("2.0.0.1"), 30); peer_data1->compute_peer_type(); BGPPeer peer1(&localdata, peer_data1, NULL, &bgpmain); DebugPeerHandler handler(&peer1); BGPPeerData *peer_data2 = new BGPPeerData(localdata, iptuple, AsNum(2), IPv4("2.0.0.2"), 30); peer_data2->compute_peer_type(); BGPPeer peer2(&localdata, peer_data2, NULL, &bgpmain); DebugPeerHandler ebgp_handler(&peer2); //trivial plumbing DebugTable* debug_table = new DebugTable("D1", NULL); RibOutTable *ribout_table = new RibOutTable("RibOut", SAFI_UNICAST, debug_table, &handler); ribout_table->peering_came_up(&handler, 0, debug_table); debug_table->set_output_file(filename); debug_table->set_canned_response(ADD_USED); handler.set_output_file(debug_table->output_file()); //create a load of attributes IPNet net1("1.0.1.0/24"); IPNet net2("1.0.2.0/24"); IPNet net3("1.0.3.0/24"); IPv4 nexthop1("2.0.0.1"); NextHopAttribute nhatt1(nexthop1); IPv4 nexthop2("2.0.0.2"); NextHopAttribute nhatt2(nexthop2); IPv4 nexthop3("2.0.0.3"); NextHopAttribute nhatt3(nexthop3); OriginAttribute igp_origin_att(IGP); ASPath aspath1; aspath1.prepend_as(AsNum(1)); aspath1.prepend_as(AsNum(2)); aspath1.prepend_as(AsNum(3)); ASPathAttribute aspathatt1(aspath1); ASPath aspath2; aspath2.prepend_as(AsNum(4)); aspath2.prepend_as(AsNum(5)); aspath2.prepend_as(AsNum(6)); ASPathAttribute aspathatt2(aspath2); ASPath aspath3; aspath3.prepend_as(AsNum(7)); aspath3.prepend_as(AsNum(8)); aspath3.prepend_as(AsNum(9)); ASPathAttribute aspathatt3(aspath3); FPAList4Ref fpalist1 = new FastPathAttributeList(nhatt1, aspathatt1, igp_origin_att); PAListRef palist1 = new PathAttributeList(fpalist1); FPAList4Ref fpalist2 = new FastPathAttributeList(nhatt2, aspathatt2, igp_origin_att); PAListRef palist2 = new PathAttributeList(fpalist2); FPAList4Ref fpalist3 = new FastPathAttributeList(nhatt3, aspathatt3, igp_origin_att); PAListRef palist3 = new PathAttributeList(fpalist3); //create a subnet route SubnetRoute *sr1; InternalMessage* msg; UNUSED(palist3); //================================================================ //Test1: trivial add and delete //================================================================ //add a route debug_table->write_comment("TEST 1"); debug_table->write_comment("ADD AND DELETE"); sr1 = new SubnetRoute(net1, palist1, NULL); msg = new InternalMessage(sr1, &handler, 0); msg->set_push(); ribout_table->add_route(*msg, debug_table); debug_table->write_separator(); //delete the route ribout_table->delete_route(*msg, debug_table); msg->set_push(); debug_table->write_separator(); sr1->unref(); delete msg; //================================================================ //Test2: three adds, two with the same PAlist. //================================================================ //add a route debug_table->write_comment("TEST 2"); debug_table->write_comment("THREE ADDS ON TWO PA_LISTS"); sr1 = new SubnetRoute(net1, palist1, NULL); msg = new InternalMessage(sr1, &handler, 0); ribout_table->add_route(*msg, debug_table); sr1->unref(); delete msg; sr1 = new SubnetRoute(net2, palist2, NULL); msg = new InternalMessage(sr1, &handler, 0); ribout_table->add_route(*msg, debug_table); sr1->unref(); delete msg; sr1 = new SubnetRoute(net3, palist1, NULL); msg = new InternalMessage(sr1, &handler, 0); ribout_table->add_route(*msg, debug_table); sr1->unref(); delete msg; ribout_table->push(debug_table); debug_table->write_separator(); //================================================================ //Test3: delete then add //================================================================ //add a route debug_table->write_comment("TEST 3"); debug_table->write_comment("DELETE THEN ADD"); sr1 = new SubnetRoute(net1, palist1, NULL); msg = new InternalMessage(sr1, &handler, 0); ribout_table->delete_route(*msg, debug_table); sr1->unref(); delete msg; sr1 = new SubnetRoute(net1, palist1, NULL); msg = new InternalMessage(sr1, &handler, 0); ribout_table->add_route(*msg, debug_table); sr1->unref(); delete msg; ribout_table->push(debug_table); debug_table->write_separator(); //================================================================ //Test4: add when peer is busy //================================================================ //add a route debug_table->write_comment("TEST 4"); debug_table->write_comment("ADD WHEN PEER BUSY"); handler.set_canned_response(PEER_OUTPUT_BUSY); sr1 = new SubnetRoute(net1, palist1, NULL); msg = new InternalMessage(sr1, &handler, 0); ribout_table->add_route(*msg, debug_table); sr1->unref(); delete msg; ribout_table->push(debug_table); debug_table->write_separator(); debug_table->write_comment("PEER_GOES IDLE"); handler.set_canned_response(PEER_OUTPUT_OK); debug_table->set_next_messages(2); ribout_table->output_no_longer_busy(); //================================================================ //Test5: replace from IBGP peer to EBGP peer //================================================================ //add a route debug_table->write_comment("TEST 5"); debug_table->write_comment("SWITCH FROM IBGP PEER TO EBGP PEER"); handler.set_canned_response(PEER_OUTPUT_BUSY); sr1 = new SubnetRoute(net1, palist1, NULL); msg = new InternalMessage(sr1, &handler, 0); SubnetRoute *sr2; InternalMessage* msg2; sr2 = new SubnetRoute(net1, palist2, NULL); msg2 = new InternalMessage(sr2, &ebgp_handler, 0); ribout_table->replace_route(*msg, *msg2, debug_table); sr1->unref(); delete msg; sr2->unref(); delete msg2; ribout_table->push(debug_table); debug_table->write_separator(); debug_table->write_comment("PEER_GOES IDLE"); handler.set_canned_response(PEER_OUTPUT_OK); debug_table->set_next_messages(2); ribout_table->output_no_longer_busy(); //================================================================ //Check debug output against reference //================================================================ debug_table->write_separator(); debug_table->write_comment("SHUTDOWN AND CLEAN UP"); delete ribout_table; delete debug_table; palist1.release(); palist2.release(); palist3.release(); fpalist1 = 0; fpalist2 = 0; fpalist3 = 0; FILE *file = fopen(filename.c_str(), "r"); if (file == NULL) { fprintf(stderr, "Failed to read %s\n", filename.c_str()); fprintf(stderr, "TEST RIBOUT FAILED\n"); fclose(file); return false; } #define BUFSIZE 8192 char testout[BUFSIZE]; memset(testout, 0, BUFSIZE); int bytes1 = fread(testout, 1, BUFSIZE, file); if (bytes1 == BUFSIZE) { fprintf(stderr, "Output too long for buffer\n"); fprintf(stderr, "TEST RIBOUT FAILED\n"); fclose(file); return false; } fclose(file); string ref_filename; const char* srcdir = getenv("srcdir"); if (srcdir) { ref_filename = string(srcdir); } else { ref_filename = "."; } ref_filename += "/test_ribout.reference"; file = fopen(ref_filename.c_str(), "r"); if (file == NULL) { fprintf(stderr, "Failed to read %s\n", ref_filename.c_str()); fprintf(stderr, "TEST RIBOUT FAILED\n"); fclose(file); return false; } char refout[BUFSIZE]; memset(refout, 0, BUFSIZE); int bytes2 = fread(refout, 1, BUFSIZE, file); if (bytes2 == BUFSIZE) { fprintf(stderr, "Output too long for buffer\n"); fprintf(stderr, "TEST RIBOUT FAILED\n"); fclose(file); return false; } fclose(file); if ((bytes1 != bytes2) || (memcmp(testout, refout, bytes1)!= 0)) { fprintf(stderr, "Output in %s doesn't match reference output\n", filename.c_str()); fprintf(stderr, "TEST RIBOUT FAILED\n"); return false; } #ifndef HOST_OS_WINDOWS unlink(filename.c_str()); #else DeleteFileA(filename.c_str()); #endif return true; } xorp/bgp/tests/test_ribin.cc0000664000076400007640000003664011540224220016244 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "bgp_module.h" #include "libxorp/xorp.h" #include "libxorp/xorpfd.hh" #include "libxorp/eventloop.hh" #include "libxorp/xlog.h" #include "libxorp/test_main.hh" #ifdef HAVE_PWD_H #include #endif #include "bgp.hh" #include "route_table_base.hh" #include "route_table_ribin.hh" #include "route_table_debug.hh" #include "path_attribute.hh" #include "local_data.hh" #include "dump_iterators.hh" bool validate_reference_file(string reference_file, string output_file, string testname); bool test_ribin_dump(TestInfo& /*info*/) { #ifndef HOST_OS_WINDOWS struct passwd *pwd = getpwuid(getuid()); string filename = "/tmp/test_ribin_dump."; filename += pwd->pw_name; #else char *tmppath = (char *)malloc(256); GetTempPathA(256, tmppath); string filename = string(tmppath) + "test_ribin_dump"; free(tmppath); #endif EventLoop eventloop; BGPMain bgpmain(eventloop); LocalData localdata(bgpmain.eventloop()); Iptuple iptuple; BGPPeerData *pd1 = new BGPPeerData(localdata, iptuple, AsNum(0), IPv4(),0); BGPPeer peer1(&localdata, pd1, NULL, &bgpmain); BGPPeerData *pd2 = new BGPPeerData(localdata, iptuple, AsNum(0), IPv4(),0); BGPPeer peer2(&localdata, pd2, NULL, &bgpmain); PeerHandler handler1("test1", &peer1, NULL, NULL); PeerHandler handler2("test1", &peer2, NULL, NULL); RibInTable *ribin = new RibInTable("RIB-IN", SAFI_UNICAST, &handler1); DebugTable* debug_table = new DebugTable("D1", (BGPRouteTable*)ribin); ribin->set_next_table(debug_table); debug_table->set_output_file(filename); debug_table->set_is_winner(true); IPNet net1("10.0.0.0/8"); IPNet net2("30.0.0.0/8"); IPNet net3("20.0.0.0/8"); IPv4 nexthop1("2.0.0.1"); NextHopAttribute nhatt1(nexthop1); OriginAttribute igp_origin_att(IGP); ASPath aspath1("3,2,1"); ASPathAttribute aspathatt1(aspath1); FPAList4Ref palist1 = new FastPathAttributeList(nhatt1, aspathatt1, igp_origin_att); PolicyTags policytags; debug_table->write_comment("TEST 1"); ribin->add_route(net1, palist1, policytags); ribin->add_route(net2, palist1, policytags); list *> peers_to_dump; PeerTableInfo* pti = new PeerTableInfo(NULL, &handler1, ribin->genid()); peers_to_dump.push_back(pti); DumpIterator* dump_iter = new DumpIterator(&handler2, peers_to_dump); ribin->dump_next_route(*dump_iter); debug_table->write_comment("The dump has started now add a new route" " it should appear once we start " "dumping again"); ribin->add_route(net3, palist1, policytags); while (ribin->dump_next_route(*dump_iter)) { while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } } delete dump_iter; debug_table-> write_comment(c_format("USE A NEW ITERATOR to prove %s is present", net3.str().c_str()).c_str()); dump_iter = new DumpIterator(&handler2, peers_to_dump); ribin->dump_next_route(*dump_iter); ribin->dump_next_route(*dump_iter); ribin->dump_next_route(*dump_iter); // Notify the dump iterator that the peering went down. Otherwise // it will still have an iterator pointing at the RIB-IN trie. dump_iter->peering_went_down(&handler1, 1); // Delete all the allocated memory // Delete the routes from the ribin. debug_table->write_comment("Delete all the routes from the RIB-IN " "so the memory can be free'd"); ribin->delete_route(net1); ribin->delete_route(net2); ribin->delete_route(net3); delete ribin; delete debug_table; // Delete the dump iterator last to verify it no longer has any // references to the RIB-IN. delete dump_iter; delete pti; return validate_reference_file("/test_ribin_dump.reference", filename, "RIBIN DUMP"); } bool test_ribin(TestInfo& /*info*/) { #ifndef HOST_OS_WINDOWS struct passwd *pwd = getpwuid(getuid()); string filename = "/tmp/test_ribin."; filename += pwd->pw_name; #else char *tmppath = (char *)malloc(256); GetTempPathA(256, tmppath); string filename = string(tmppath) + "test_ribin"; free(tmppath); #endif EventLoop eventloop; BGPMain bgpmain(eventloop); LocalData localdata(bgpmain.eventloop()); Iptuple iptuple; BGPPeerData *pd1 = new BGPPeerData(localdata, iptuple, AsNum(0), IPv4(),0); BGPPeer peer1(&localdata, pd1, NULL, &bgpmain); PeerHandler handler1("test1", &peer1, NULL, NULL); BGPPeerData *pd2 = new BGPPeerData(localdata, iptuple, AsNum(0), IPv4(),0); BGPPeer peer2(&localdata, pd2, NULL, &bgpmain); PeerHandler handler2("test2", &peer2, NULL, NULL); //trivial plumbing RibInTable *ribin = new RibInTable("RIB-IN", SAFI_UNICAST, &handler1); DebugTable* debug_table = new DebugTable("D1", (BGPRouteTable*)ribin); ribin->set_next_table(debug_table); debug_table->set_output_file(filename); debug_table->set_canned_response(ADD_USED); debug_table->set_is_winner(true); //create a load of attributes IPNet net1("1.0.1.0/24"); IPNet net2("1.0.2.0/24"); IPv4 nexthop1("2.0.0.1"); NextHopAttribute nhatt1(nexthop1); IPv4 nexthop2("2.0.0.2"); NextHopAttribute nhatt2(nexthop2); OriginAttribute igp_origin_att(IGP); ASPath aspath1; aspath1.prepend_as(AsNum(1)); aspath1.prepend_as(AsNum(2)); aspath1.prepend_as(AsNum(3)); ASPath foo("3,2,1"); assert (foo == aspath1); ASPathAttribute aspathatt1(aspath1); ASPath aspath2; aspath2.prepend_as(AsNum(3)); aspath2.prepend_as(AsNum(4)); aspath2.prepend_as(AsNum(5)); ASPathAttribute aspathatt2(aspath2); FPAList4Ref palist1 = new FastPathAttributeList(nhatt1, aspathatt1, igp_origin_att); FPAList4Ref palist2 = new FastPathAttributeList(nhatt2, aspathatt1, igp_origin_att); FPAList4Ref palist3, palist4; PAListRef dummy_palist; dummy_palist.create_attribute_manager(); PolicyTags policytags; //================================================================ //Test1: trivial add and delete //================================================================ //add a route debug_table->write_comment("TEST 1"); ribin->add_route(net1, palist1, policytags); //check we only ended up with one copy of the PA list assert(dummy_palist.number_of_managed_atts() == 1); //check there's one route in the RIB-IN assert(ribin->route_count() == 1); debug_table->write_separator(); //delete the route ribin->delete_route(net1); //check there's still one copy of the PA list //XXX now there is no copy list - think this is better assert(dummy_palist.number_of_managed_atts() == 0); //check there are no routes in the RIB-IN assert(ribin->route_count() == 0); debug_table->write_separator(); //================================================================ //Test2: trivial add , replace and delete //================================================================ //add a route debug_table->write_comment("TEST 2"); debug_table->write_comment("ADD FIRST ROUTE"); ribin->add_route(net1, palist1, policytags); palist1 = 0; //check there's still one route in the RIB-IN assert(ribin->route_count() == 1); //check we only ended up with one copy of the PA list assert(dummy_palist.number_of_managed_atts() == 1); debug_table->write_separator(); //add the same route again - should be treated as an update, and //trigger a replace operation debug_table->write_comment("ADD MODIFIED FIRST ROUTE"); ribin->add_route(net1, palist2, policytags); //check we only ended up with one copy of the PA list assert(dummy_palist.number_of_managed_atts() == 1); //check there's still one route in the RIB-IN assert(ribin->route_count() == 1); debug_table->write_separator(); //delete the route debug_table->write_comment("DELETE ROUTE"); ribin->delete_route(net1); //check there's still one copy of the PA list //XXX now zero assert(dummy_palist.number_of_managed_atts() == 0); //check there are no routes in the RIB-IN assert(ribin->route_count() == 0); debug_table->write_separator(); palist2 = 0; //================================================================ //Test3: trivial add two routes, then drop peering //================================================================ //add a route debug_table->write_comment("TEST 3"); palist1 = new FastPathAttributeList(nhatt1, aspathatt1, igp_origin_att); palist2 = new FastPathAttributeList(nhatt2, aspathatt1, igp_origin_att); debug_table->write_comment("ADD FIRST ROUTE"); ribin->add_route(net1, palist1, policytags); palist1 = 0; debug_table->write_comment("ADD SECOND ROUTE"); ribin->add_route(net2, palist2, policytags); palist2 = 0; //check there are two routes in the RIB-IN assert(ribin->route_count() == 2); //check we only ended up with two PA lists assert(dummy_palist.number_of_managed_atts() == 2); debug_table->write_separator(); debug_table->write_comment("NOW DROP THE PEERING"); ribin->ribin_peering_went_down(); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } debug_table->write_separator(); //================================================================ //Test4: trivial add two routes, then do a route dump //================================================================ ribin->ribin_peering_came_up(); //add a route debug_table->write_comment("TEST 4"); palist1 = new FastPathAttributeList(nhatt1, aspathatt1, igp_origin_att); palist2 = new FastPathAttributeList(nhatt2, aspathatt1, igp_origin_att); //routes will only be dumped if they won at decision debug_table->write_comment("ADD FIRST ROUTE"); ribin->add_route(net1, palist1, policytags); debug_table->write_comment("ADD SECOND ROUTE"); ribin->add_route(net2, palist2, policytags); palist1 = 0; //check there are two routes in the RIB-IN assert(ribin->route_count() == 2); //check we only ended up with two PA lists assert(dummy_palist.number_of_managed_atts() == 2); palist2 = 0; debug_table->write_separator(); debug_table->write_comment("NOW DO THE ROUTE DUMP"); list *> peers_to_dump; PeerTableInfo* pti = new PeerTableInfo(NULL, &handler1, ribin->genid()); peers_to_dump.push_back(pti); DumpIterator* dump_iter = new DumpIterator(&handler2, peers_to_dump); while (ribin->dump_next_route(*dump_iter)) { while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } } delete dump_iter; delete pti; debug_table->write_separator(); debug_table->write_comment("DELETE THE ROUTES AND CLEAN UP"); ribin->delete_route(net1); ribin->delete_route(net2); debug_table->write_separator(); //================================================================ //Test5: IGP nexthop changes //================================================================ debug_table->write_comment("TEST 5"); debug_table->write_comment("IGP NEXTHOP CHANGES"); palist1 = new FastPathAttributeList(nhatt1, aspathatt1, igp_origin_att); palist2 = new FastPathAttributeList(nhatt2, aspathatt1, igp_origin_att); debug_table->write_comment("ADD FIRST ROUTE"); ribin->add_route(net1, palist1, policytags); palist1 = 0; debug_table->write_comment("ADD SECOND ROUTE"); ribin->add_route(net2, palist2, policytags); palist2 = 0; //check there are two routes in the RIB-IN assert(ribin->route_count() == 2); debug_table->write_separator(); debug_table->write_comment("NEXTHOP 2.0.0.2 CHANGES"); //this should trigger a replace ribin->igp_nexthop_changed(nexthop2); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } debug_table->write_separator(); debug_table->write_comment("NEXTHOP 2.0.0.1 CHANGES"); //this should trigger a replace ribin->igp_nexthop_changed(nexthop1); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } IPv4 nexthop3("1.0.0.1"); debug_table->write_separator(); debug_table->write_comment("NEXTHOP 1.0.0.1 CHANGES"); //this should have no effect ribin->igp_nexthop_changed(nexthop3); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } IPv4 nexthop4("3.0.0.1"); debug_table->write_separator(); //this should have no effect debug_table->write_comment("NEXTHOP 3.0.0.1 CHANGES"); ribin->igp_nexthop_changed(nexthop4); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } debug_table->write_separator(); //Add two more routes with same nexthop as net1 IPNet net3("1.0.3.0/24"); IPNet net4("1.0.4.0/24"); //palist1 has the same palist as the route for net1 palist3 = new FastPathAttributeList(nhatt1, aspathatt1, igp_origin_att); //palist1 has the same nexthop, but different palist as the route for net1 palist4 = new FastPathAttributeList(nhatt1, aspathatt2, igp_origin_att); debug_table->write_comment("ADD THIRD ROUTE"); ribin->add_route(net3, palist3, policytags); palist3 = 0; //check there are 3 routes in the RIB-IN assert(ribin->route_count() == 3); debug_table->write_comment("ADD FOURTH ROUTE"); ribin->add_route(net4, palist4, policytags); palist4 = 0; //check there are 4 routes in the RIB-IN assert(ribin->route_count() == 4); debug_table->write_separator(); debug_table->write_separator(); debug_table->write_comment("NEXTHOP 2.0.0.1 CHANGES"); //this should trigger three a replaces ribin->igp_nexthop_changed(nexthop1); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } debug_table->write_separator(); debug_table->write_comment("DELETE THE ROUTES AND CLEAN UP"); ribin->delete_route(net1); ribin->delete_route(net2); ribin->delete_route(net3); ribin->delete_route(net4); debug_table->write_separator(); //================================================================ debug_table->write_comment("SHUTDOWN AND CLEAN UP"); delete ribin; delete debug_table; return validate_reference_file("/test_ribin.reference", filename, "RIBIN"); } xorp/bgp/tests/test_plumbing.hh0000664000076400007640000000335711421137511016773 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/bgp/test_plumbing.hh,v 1.13 2008/11/08 06:14:41 mjh Exp $ #ifndef __BGP_TEST_PLUMBING_HH__ #define __BGP_TEST_PLUMBING_HH__ #include "plumbing.hh" #include "peer.hh" class DummyPeer : public BGPPeer { public: DummyPeer(LocalData *ld, BGPPeerData *pd, SocketClient *sock, BGPMain *m); PeerOutputState send_update_message(const UpdatePacket& p); }; class DummyPeerHandler : public PeerHandler { public: DummyPeerHandler(const string &peername, BGPPeer *peer, BGPPlumbing *plumbing_unicast, BGPPlumbing *plumbing_multicast); }; class PlumbingTest : public BGPPlumbing { public: PlumbingTest(NextHopResolver& nhr_ipv4, NextHopResolver& nhr_ipv6, PolicyFilters& pfs, BGPMain& bgp); bool run_tests(BGPMain& bgpmain); bool test1(BGPMain& bgpmain); bool test2(BGPMain& bgpmain); }; #endif // __BGP_TEST_PLUMBING_HH__ xorp/bgp/tests/test_policy.reference0000664000076400007640000000372311421137511020011 0ustar greearbgreearb[comment] TEST 1 [comment] ADD AND DELETE UNFILTERED [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [separator]------------------------------------- [comment] TEST 2 [comment] ADD, CONFIG, DELETE [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [comment] CONFIGURE EXPORT FILTER [comment] DO DUMP [comment] EXPECT DELETE TO *NOT* HAVE LOCALPREF [comment] EXPECT ADD TO HAVE LOCALPREF OF 200 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [ADD] CHANGED flag is set SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 200 [comment] EXPECT DELETE TO HAVE LOCALPREF OF 200 [DELETE] CHANGED flag is set SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 200 [separator]------------------------------------- [comment] TEST 3 [comment] ADD AND DELETE FILTERED [comment] EXPECT ROUTES TO HAVE LOCALPREF [ADD] CHANGED flag is set SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 200 [DELETE] CHANGED flag is set SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 200 [separator]------------------------------------- [comment] SHUTDOWN AND CLEAN UP xorp/bgp/tests/test_deletion.cc0000664000076400007640000005322011540224220016735 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "bgp_module.h" #include "libxorp/xorp.h" #include "libxorp/xorpfd.hh" #include "libxorp/eventloop.hh" #include "libxorp/xlog.h" #include "libxorp/test_main.hh" #ifdef HAVE_PWD_H #include #endif #include "bgp.hh" #include "route_table_base.hh" #include "route_table_ribin.hh" #include "route_table_debug.hh" #include "path_attribute.hh" #include "local_data.hh" #include "dump_iterators.hh" bool test_deletion(TestInfo& /*info*/) { #ifndef HOST_OS_WINDOWS struct passwd *pwd = getpwuid(getuid()); string filename = "/tmp/test_deletion."; filename += pwd->pw_name; #else char *tmppath = (char *)malloc(256); GetTempPathA(256, tmppath); string filename = string(tmppath) + "test_deletion"; free(tmppath); #endif EventLoop eventloop; BGPMain bgpmain(eventloop); LocalData localdata(bgpmain.eventloop()); Iptuple iptuple; BGPPeerData *pd1 = new BGPPeerData(localdata, iptuple, AsNum(0), IPv4(),0); BGPPeer peer1(&localdata, pd1, NULL, &bgpmain); PeerHandler handler1("test1", &peer1, NULL, NULL); BGPPeerData *pd2 = new BGPPeerData(localdata, iptuple, AsNum(0), IPv4(),0); BGPPeer peer2(&localdata, pd2, NULL, &bgpmain); PeerHandler handler2("test2", &peer2, NULL, NULL); //trivial plumbing RibInTable *ribin = new RibInTable("RIB-IN", SAFI_UNICAST, &handler1); DebugTable* debug_table = new DebugTable("D1", (BGPRouteTable*)ribin); ribin->set_next_table(debug_table); debug_table->set_output_file(filename); debug_table->set_canned_response(ADD_USED); //create a load of attributes IPNet net1("1.0.1.0/24"); IPNet net2("1.0.2.0/24"); IPNet net3("1.0.3.0/24"); IPNet net4("1.0.4.0/24"); IPv4 nexthop1("2.0.0.1"); NextHopAttribute nhatt1(nexthop1); IPv4 nexthop2("2.0.0.2"); NextHopAttribute nhatt2(nexthop2); IPv4 nexthop3("2.0.0.3"); NextHopAttribute nhatt3(nexthop3); OriginAttribute igp_origin_att(IGP); ASPath aspath1; aspath1.prepend_as(AsNum(1)); aspath1.prepend_as(AsNum(2)); aspath1.prepend_as(AsNum(3)); ASPathAttribute aspathatt1(aspath1); ASPath aspath2; aspath2.prepend_as(AsNum(3)); aspath2.prepend_as(AsNum(4)); aspath2.prepend_as(AsNum(5)); ASPathAttribute aspathatt2(aspath2); ASPath aspath3; aspath3.prepend_as(AsNum(6)); aspath3.prepend_as(AsNum(6)); aspath3.prepend_as(AsNum(7)); aspath3.prepend_as(AsNum(8)); ASPathAttribute aspathatt3(aspath3); FPAList4Ref fpalist1 = new FastPathAttributeList(nhatt1, aspathatt1, igp_origin_att); PAListRef palist1 = new PathAttributeList(fpalist1); FPAList4Ref fpalist2 = new FastPathAttributeList(nhatt2, aspathatt2, igp_origin_att); PAListRef palist2 = new PathAttributeList(fpalist2); FPAList4Ref fpalist3 = new FastPathAttributeList(nhatt3, aspathatt3, igp_origin_att); PAListRef palist3 = new PathAttributeList(fpalist3); PolicyTags pt; //================================================================ //Test1: trivial add and peering goes down //================================================================ //add a route debug_table->write_comment("****************************************************************"); debug_table->write_comment("TEST 1"); debug_table->write_comment("TRIVIAL ADD AND PEERING GOES DOWN"); debug_table->write_separator(); debug_table->write_comment("ADD A ROUTE"); ribin->add_route(net1, fpalist1, pt); debug_table->write_separator(); debug_table->write_comment("PEERING GOES DOWN"); ribin->ribin_peering_went_down(); debug_table->write_separator(); debug_table->write_comment("LET EVENT QUEUE DRAIN"); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } debug_table->write_separator(); debug_table->write_comment("PEERING COMES UP"); ribin->ribin_peering_came_up(); debug_table->write_separator(); //================================================================ //Test2: trivial add and peering goes down //================================================================ //add a route debug_table->write_comment("****************************************************************"); debug_table->write_comment("TEST 2"); debug_table->write_comment("3 ADDS, SAME PALIST, PEERING GOES DOWN"); debug_table->write_separator(); debug_table->write_comment("ADD A ROUTE"); ribin->add_route(net1, fpalist1, pt); debug_table->write_separator(); debug_table->write_comment("ADD A ROUTE"); ribin->add_route(net2, fpalist1, pt); debug_table->write_separator(); debug_table->write_comment("ADD A ROUTE"); ribin->add_route(net3, fpalist1, pt); debug_table->write_separator(); debug_table->write_comment("PEERING GOES DOWN"); ribin->ribin_peering_went_down(); debug_table->write_separator(); debug_table->write_comment("LET EVENT QUEUE DRAIN"); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } debug_table->write_separator(); debug_table->write_comment("PEERING COMES UP"); ribin->ribin_peering_came_up(); debug_table->write_separator(); //================================================================ //Test3: trivial add and peering goes down //================================================================ //add a route debug_table->write_comment("****************************************************************"); debug_table->write_comment("TEST 3"); debug_table->write_comment("3 ADDS, 3 PALISTS, PEERING GOES DOWN"); debug_table->write_separator(); debug_table->write_comment("ADD A ROUTE"); ribin->add_route(net1, fpalist1, pt); debug_table->write_separator(); debug_table->write_comment("ADD A ROUTE"); ribin->add_route(net2, fpalist2, pt); debug_table->write_separator(); debug_table->write_comment("ADD A ROUTE"); ribin->add_route(net3, fpalist3, pt); debug_table->write_separator(); debug_table->write_comment("PEERING GOES DOWN"); ribin->ribin_peering_went_down(); debug_table->write_separator(); debug_table->write_comment("LET EVENT QUEUE DRAIN"); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } debug_table->write_separator(); debug_table->write_comment("PEERING COMES UP"); ribin->ribin_peering_came_up(); debug_table->write_separator(); //================================================================ //Test4: trivial add and peering goes down //================================================================ //add a route debug_table->write_comment("****************************************************************"); debug_table->write_comment("TEST 4"); debug_table->write_comment("4 ADDS, 2 PALISTS, PEERING GOES DOWN"); debug_table->write_separator(); debug_table->write_comment("ADD A ROUTE"); ribin->add_route(net1, fpalist1, pt); debug_table->write_separator(); debug_table->write_comment("ADD A ROUTE"); ribin->add_route(net2, fpalist2, pt); debug_table->write_separator(); debug_table->write_comment("ADD A ROUTE"); ribin->add_route(net3, fpalist1, pt); debug_table->write_separator(); debug_table->write_comment("ADD A ROUTE"); ribin->add_route(net4, fpalist2, pt); debug_table->write_separator(); debug_table->write_comment("PEERING GOES DOWN"); ribin->ribin_peering_went_down(); debug_table->write_separator(); debug_table->write_comment("LET EVENT QUEUE DRAIN"); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } debug_table->write_separator(); debug_table->write_comment("PEERING COMES UP"); ribin->ribin_peering_came_up(); debug_table->write_separator(); //================================================================ //Test5: trivial add and peering goes down //================================================================ //add a route debug_table->write_comment("****************************************************************"); debug_table->write_comment("TEST 5"); debug_table->write_comment("BOUNCE THE PEERING AGAIN"); debug_table->write_separator(); debug_table->write_comment("PEERING GOES DOWN"); ribin->ribin_peering_went_down(); debug_table->write_separator(); debug_table->write_comment("LET EVENT QUEUE DRAIN"); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } debug_table->write_separator(); debug_table->write_comment("PEERING COMES UP"); ribin->ribin_peering_came_up(); debug_table->write_separator(); debug_table->write_comment("LET EVENT QUEUE DRAIN"); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } debug_table->write_separator(); //================================================================ //Test6: trivial add and peering goes down //================================================================ //add a route debug_table->write_comment("****************************************************************"); debug_table->write_comment("TEST 6"); debug_table->write_comment("4 ADDS, 2 PALISTS, PEERING GOES DOWN"); debug_table->write_comment("2 MORE ADDS BEFORE PROCESSING TIMERS"); debug_table->write_separator(); debug_table->write_comment("ADD A ROUTE"); ribin->add_route(net1, fpalist1, pt); debug_table->write_separator(); debug_table->write_comment("ADD A ROUTE"); ribin->add_route(net2, fpalist2, pt); debug_table->write_separator(); debug_table->write_comment("ADD A ROUTE"); ribin->add_route(net3, fpalist1, pt); debug_table->write_separator(); debug_table->write_comment("ADD A ROUTE"); ribin->add_route(net4, fpalist2, pt); debug_table->write_separator(); debug_table->write_comment("PEERING GOES DOWN"); ribin->ribin_peering_went_down(); debug_table->write_separator(); debug_table->write_comment("PEERING COMES UP"); ribin->ribin_peering_came_up(); debug_table->write_separator(); debug_table->write_comment("ADD A ROUTE"); ribin->add_route(net1, fpalist1, pt); debug_table->write_separator(); debug_table->write_comment("ADD A ROUTE"); ribin->add_route(net4, fpalist2, pt); debug_table->write_separator(); debug_table->write_comment("LET EVENT QUEUE DRAIN"); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } debug_table->write_separator(); debug_table->write_comment("PEERING COMES UP"); ribin->ribin_peering_came_up(); debug_table->write_separator(); debug_table->write_comment("PEERING GOES DOWN"); ribin->ribin_peering_went_down(); debug_table->write_separator(); debug_table->write_comment("LET EVENT QUEUE DRAIN"); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } debug_table->write_separator(); debug_table->write_comment("PEERING COMES UP"); ribin->ribin_peering_came_up(); //================================================================ //Test7: trivial add and peering goes down //================================================================ //add a route debug_table->write_comment("****************************************************************"); debug_table->write_comment("TEST 7"); debug_table->write_comment("4 ADDS, 2 PALISTS, PEERING GOES DOWN"); debug_table->write_comment("2 MORE ADDS BEFORE PROCESSING TIMERS"); debug_table->write_comment("AS TEST 6, BUT NEW PALISTS ARE DIFFERENT"); debug_table->write_separator(); debug_table->write_comment("ADD A ROUTE"); ribin->add_route(net1, fpalist1, pt); debug_table->write_separator(); debug_table->write_comment("ADD A ROUTE"); ribin->add_route(net2, fpalist2, pt); debug_table->write_separator(); debug_table->write_comment("ADD A ROUTE"); ribin->add_route(net3, fpalist1, pt); debug_table->write_separator(); debug_table->write_comment("ADD A ROUTE"); ribin->add_route(net4, fpalist2, pt); debug_table->write_separator(); debug_table->write_comment("PEERING GOES DOWN"); ribin->ribin_peering_went_down(); debug_table->write_separator(); debug_table->write_comment("PEERING COMES UP"); ribin->ribin_peering_came_up(); debug_table->write_separator(); debug_table->write_comment("ADD A ROUTE"); ribin->add_route(net1, fpalist2, pt); debug_table->write_separator(); debug_table->write_comment("ADD A ROUTE"); ribin->add_route(net4, fpalist3, pt); debug_table->write_separator(); debug_table->write_comment("LET EVENT QUEUE DRAIN"); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } debug_table->write_separator(); debug_table->write_comment("PEERING COMES UP"); ribin->ribin_peering_came_up(); debug_table->write_separator(); debug_table->write_comment("PEERING GOES DOWN"); ribin->ribin_peering_went_down(); debug_table->write_separator(); debug_table->write_comment("LET EVENT QUEUE DRAIN"); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } debug_table->write_separator(); debug_table->write_comment("PEERING COMES UP"); ribin->ribin_peering_came_up(); //================================================================ //Test8: //================================================================ //add a route debug_table->write_comment("****************************************************************"); debug_table->write_comment("TEST 8"); debug_table->write_comment("4 ADDS, 2 PALISTS, PEERING GOES DOWN"); debug_table->write_comment("2 MORE ADDS BEFORE PROCESSING TIMERS"); debug_table->write_comment("AS TEST 6, NEW ROUTES SHOULD DELETE CHAIN"); debug_table->write_separator(); debug_table->write_comment("ADD A ROUTE"); ribin->add_route(net1, fpalist1, pt); debug_table->write_separator(); debug_table->write_comment("ADD A ROUTE"); ribin->add_route(net2, fpalist2, pt); debug_table->write_separator(); debug_table->write_comment("ADD A ROUTE"); ribin->add_route(net3, fpalist1, pt); debug_table->write_separator(); debug_table->write_comment("ADD A ROUTE"); ribin->add_route(net4, fpalist2, pt); debug_table->write_separator(); debug_table->write_comment("PEERING GOES DOWN"); ribin->ribin_peering_went_down(); debug_table->write_separator(); debug_table->write_comment("PEERING COMES UP"); ribin->ribin_peering_came_up(); debug_table->write_separator(); debug_table->write_comment("ADD A ROUTE"); ribin->add_route(net1, fpalist3, pt); debug_table->write_separator(); debug_table->write_comment("ADD A ROUTE"); ribin->add_route(net3, fpalist3, pt); debug_table->write_separator(); debug_table->write_comment("LET EVENT QUEUE DRAIN"); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } debug_table->write_separator(); debug_table->write_comment("PEERING COMES UP"); ribin->ribin_peering_came_up(); debug_table->write_separator(); debug_table->write_comment("PEERING GOES DOWN"); ribin->ribin_peering_went_down(); debug_table->write_separator(); debug_table->write_comment("LET EVENT QUEUE DRAIN"); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } debug_table->write_separator(); debug_table->write_comment("PEERING COMES UP"); ribin->ribin_peering_came_up(); //================================================================ //Test9: //================================================================ //add a route debug_table->write_comment("****************************************************************"); debug_table->write_comment("TEST 9"); debug_table->write_comment("2 ADDS, 2 PALISTS, PEERING GOES DOWN"); debug_table->write_comment("SAME 2 ADDS BEFORE PROCESSING TIMERS"); debug_table->write_comment("ADDS SHOULD REMOVE ALL ROUTE FROM DEL TAB"); debug_table->write_separator(); debug_table->write_comment("ADD A ROUTE"); ribin->add_route(net1, fpalist1, pt); debug_table->write_separator(); debug_table->write_comment("ADD A ROUTE"); ribin->add_route(net2, fpalist2, pt); debug_table->write_separator(); debug_table->write_comment("PEERING GOES DOWN"); ribin->ribin_peering_went_down(); debug_table->write_separator(); debug_table->write_comment("PEERING COMES UP"); ribin->ribin_peering_came_up(); debug_table->write_separator(); debug_table->write_comment("ADD A ROUTE"); ribin->add_route(net1, fpalist1, pt); debug_table->write_separator(); debug_table->write_comment("ADD A ROUTE"); ribin->add_route(net2, fpalist2, pt); debug_table->write_separator(); debug_table->write_comment("LET EVENT QUEUE DRAIN"); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } debug_table->write_separator(); debug_table->write_comment("PEERING COMES UP"); ribin->ribin_peering_came_up(); debug_table->write_separator(); debug_table->write_comment("PEERING GOES DOWN"); ribin->ribin_peering_went_down(); debug_table->write_separator(); debug_table->write_comment("LET EVENT QUEUE DRAIN"); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } debug_table->write_separator(); debug_table->write_comment("PEERING COMES UP"); ribin->ribin_peering_came_up(); //================================================================ //Test10: //================================================================ //add a route debug_table->write_comment("****************************************************************"); debug_table->write_comment("TEST 10"); debug_table->write_comment("TWO DELETION TABLES"); debug_table->write_separator(); debug_table->write_comment("ADD A ROUTE"); ribin->add_route(net1, fpalist1, pt); debug_table->write_separator(); debug_table->write_comment("PEERING GOES DOWN"); ribin->ribin_peering_went_down(); debug_table->write_separator(); debug_table->write_comment("PEERING COMES UP"); ribin->ribin_peering_came_up(); debug_table->write_separator(); debug_table->write_comment("ADD A ROUTE"); ribin->add_route(net2, fpalist2, pt); debug_table->write_separator(); debug_table->write_comment("PEERING GOES DOWN"); ribin->ribin_peering_went_down(); debug_table->write_separator(); debug_table->write_comment("PEERING COMES UP"); ribin->ribin_peering_came_up(); debug_table->write_separator(); debug_table->write_comment("LET EVENT QUEUE DRAIN"); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } debug_table->write_separator(); debug_table->write_comment("PEERING COMES UP"); ribin->ribin_peering_came_up(); //================================================================ debug_table->write_comment("SHUTDOWN AND CLEAN UP"); delete ribin; delete debug_table; palist1.release(); palist2.release(); palist3.release(); fpalist1 = 0; fpalist2 = 0; fpalist3 = 0; FILE *file = fopen(filename.c_str(), "r"); if (file == NULL) { fprintf(stderr, "Failed to read %s\n", filename.c_str()); fprintf(stderr, "TEST DELETION FAILED\n"); fclose(file); return false; } #define BUFSIZE 20000 char testout[BUFSIZE]; memset(testout, 0, BUFSIZE); int bytes1 = fread(testout, 1, BUFSIZE, file); if (bytes1 == BUFSIZE) { fprintf(stderr, "Output too long for buffer\n"); fprintf(stderr, "TEST DELETION FAILED\n"); fclose(file); return false; } fclose(file); string ref_filename; const char* srcdir = getenv("srcdir"); if (srcdir) { ref_filename = string(srcdir); } else { ref_filename = "."; } ref_filename += "/test_deletion.reference"; file = fopen(ref_filename.c_str(), "r"); if (file == NULL) { fprintf(stderr, "Failed to read %s\n", ref_filename.c_str()); fprintf(stderr, "TEST DELETION FAILED\n"); fclose(file); return false; } char refout[BUFSIZE]; memset(refout, 0, BUFSIZE); int bytes2 = fread(refout, 1, BUFSIZE, file); if (bytes2 == BUFSIZE) { fprintf(stderr, "Output too long for buffer\n"); fprintf(stderr, "TEST DELETION FAILED\n"); fclose(file); return false; } fclose(file); if ((bytes1 != bytes2) || (memcmp(testout, refout, bytes1)!= 0)) { fprintf(stderr, "Output in %s doesn't match reference output\n", filename.c_str()); fprintf(stderr, "TEST DELETION FAILED\n"); return false; } #ifndef HOST_OS_WINDOWS unlink(filename.c_str()); #else DeleteFileA(filename.c_str()); #endif return true; } xorp/bgp/tests/test_ribin.reference0000664000076400007640000001540511421137511017615 0ustar greearbgreearb[comment] TEST 1 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [separator]------------------------------------- [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [separator]------------------------------------- [comment] TEST 2 [comment] ADD FIRST ROUTE [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [separator]------------------------------------- [comment] ADD MODIFIED FIRST ROUTE [REPLACE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [separator]------------------------------------- [comment] DELETE ROUTE [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [separator]------------------------------------- [comment] TEST 3 [comment] ADD FIRST ROUTE [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [comment] ADD SECOND ROUTE [ADD] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [separator]------------------------------------- [comment] NOW DROP THE PEERING [PUSH] [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [PUSH] [DELETE] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [PUSH] [separator]------------------------------------- [comment] TEST 4 [comment] ADD FIRST ROUTE [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [comment] ADD SECOND ROUTE [ADD] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [separator]------------------------------------- [comment] NOW DO THE ROUTE DUMP [DUMP] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [DUMP] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [separator]------------------------------------- [comment] DELETE THE ROUTES AND CLEAN UP [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [DELETE] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [separator]------------------------------------- [comment] TEST 5 [comment] IGP NEXTHOP CHANGES [comment] ADD FIRST ROUTE [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [comment] ADD SECOND ROUTE [ADD] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [separator]------------------------------------- [comment] NEXTHOP 2.0.0.2 CHANGES [DELETE] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [ADD] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [PUSH] [separator]------------------------------------- [comment] NEXTHOP 2.0.0.1 CHANGES [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [PUSH] [separator]------------------------------------- [comment] NEXTHOP 1.0.0.1 CHANGES [separator]------------------------------------- [comment] NEXTHOP 3.0.0.1 CHANGES [separator]------------------------------------- [comment] ADD THIRD ROUTE [ADD] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [comment] ADD FOURTH ROUTE [ADD] SubnetRoute: Net: 1.0.4.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/5, AS/4, AS/3] [separator]------------------------------------- [separator]------------------------------------- [comment] NEXTHOP 2.0.0.1 CHANGES [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [DELETE] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [ADD] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [PUSH] [DELETE] SubnetRoute: Net: 1.0.4.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/5, AS/4, AS/3] [ADD] SubnetRoute: Net: 1.0.4.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/5, AS/4, AS/3] [PUSH] [separator]------------------------------------- [comment] DELETE THE ROUTES AND CLEAN UP [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [DELETE] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [DELETE] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [DELETE] SubnetRoute: Net: 1.0.4.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/5, AS/4, AS/3] [separator]------------------------------------- [comment] SHUTDOWN AND CLEAN UP xorp/bgp/tests/test_decision.reference0000664000076400007640000020526411421137511020313 0ustar greearbgreearb[comment] ****************************************** [comment] TEST 1 [comment] ADD AND DELETE [comment] SENDING FROM PEER 1 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [PUSH] [separator]------------------------------------- [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [PUSH] [separator]------------------------------------- [comment] ****************************************** [comment] TEST 1b [comment] ADD AND DELETE, NEXTHOP UNRESOLVABLE [comment] SENDING ADD FROM PEER 1 [separator]------------------------------------- [comment] SENDING PUSH FROM PEER 1 [PUSH] [separator]------------------------------------- [comment] SENDING DELETE FROM PEER 1 [PUSH] [separator]------------------------------------- [comment] ****************************************** [comment] TEST 2 [comment] IDENTICAL ADD FROM TWO PEERS [comment] PEER1 HAS LOWEST NEIGHBOR ADDRESS [comment] SENDING FROM PEER 1 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [separator]------------------------------------- [comment] DELETION FROM PEER 1 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] DELETION FROM PEER 2 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] ****************************************** [comment] TEST 2B [comment] IDENTICAL ADD FROM TWO PEERS [comment] PEER1 HAS LOWEST NEIGHBOR ADDRESS [comment] SENDING FROM PEER 2 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 1 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] DELETION FROM PEER 2 [separator]------------------------------------- [comment] DELETION FROM PEER 1 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] ****************************************** [comment] TEST 2C [comment] IDENTICAL ADD FROM TWO PEERS [comment] PEER1 HAS LOWEST NEIGHBOR ADDRESS [comment] PEER2 HAS LOWEST BGP ID [comment] SENDING FROM PEER 1 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] DELETION FROM PEER 1 [separator]------------------------------------- [comment] DELETION FROM PEER 2 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] ****************************************** [comment] TEST 2D [comment] IDENTICAL ADD FROM TWO PEERS [comment] PEER1 HAS LOWEST NEIGHBOR ADDRESS [comment] PEER2 HAS LOWEST BGP ID [comment] SENDING FROM PEER 2 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 1 [separator]------------------------------------- [comment] DELETION FROM PEER 2 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] DELETION FROM PEER 1 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] ****************************************** [comment] TEST 3 [comment] TEST OF DIFFERENT AS PATH LENGTHS [comment] SENDING FROM PEER 1 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [separator]------------------------------------- [comment] DELETION FROM PEER 1 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [separator]------------------------------------- [comment] DELETION FROM PEER 2 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [separator]------------------------------------- [comment] ****************************************** [comment] TEST 3B [comment] TEST OF DIFFERENT AS PATH LENGTHS [comment] SENDING FROM PEER 1 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] DELETION FROM PEER 1 [separator]------------------------------------- [comment] DELETION FROM PEER 2 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] ****************************************** [comment] TEST 4 [comment] TEST OF LOCALPREF [comment] HIGHEST LOCALPREF WINS [comment] SENDING FROM PEER 1 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 200 [separator]------------------------------------- [comment] DELETION FROM PEER 1 [separator]------------------------------------- [comment] DELETION FROM PEER 2 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 200 [separator]------------------------------------- [comment] ****************************************** [comment] TEST 4B [comment] TEST OF LOCALPREF [comment] HIGHEST LOCALPREF WINS [comment] SENDING FROM PEER 1 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 200 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [separator]------------------------------------- [comment] DELETION FROM PEER 1 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 200 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] DELETION FROM PEER 2 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] ****************************************** [comment] TEST 4C [comment] TEST OF LOCALPREF [comment] HIGHEST LOCALPREF WINS [comment] SENDING FROM PEER 1 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 3 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 200 [separator]------------------------------------- [comment] DELETION FROM PEER 1 [separator]------------------------------------- [comment] DELETION FROM PEER 2 [separator]------------------------------------- [comment] DELETION FROM PEER 3 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 200 [separator]------------------------------------- [comment] ****************************************** [comment] TEST 5 [comment] TEST OF MED [comment] LOWEST MED WINS [comment] SENDING FROM PEER 1 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Multiple Exit Descriminator Attribute: MED=100 Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [separator]------------------------------------- [comment] DELETION FROM PEER 1 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Multiple Exit Descriminator Attribute: MED=100 Local Preference Attribute - 100 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Multiple Exit Descriminator Attribute: MED=200 Local Preference Attribute - 100 [separator]------------------------------------- [comment] DELETION FROM PEER 2 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Multiple Exit Descriminator Attribute: MED=200 Local Preference Attribute - 100 [separator]------------------------------------- [comment] ****************************************** [comment] TEST 5B [comment] TEST OF MED [comment] LOWEST MED WINS [comment] SENDING FROM PEER 1 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Multiple Exit Descriminator Attribute: MED=200 Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Multiple Exit Descriminator Attribute: MED=200 Local Preference Attribute - 100 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Multiple Exit Descriminator Attribute: MED=100 Local Preference Attribute - 100 [separator]------------------------------------- [comment] DELETION FROM PEER 1 [separator]------------------------------------- [comment] DELETION FROM PEER 2 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Multiple Exit Descriminator Attribute: MED=100 Local Preference Attribute - 100 [separator]------------------------------------- [comment] ****************************************** [comment] TEST 5C [comment] TEST OF MED - ONLY ONE HAS MED [comment] ROUTE WITHOUT MED WINS [comment] SENDING FROM PEER 1 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Multiple Exit Descriminator Attribute: MED=100 Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Multiple Exit Descriminator Attribute: MED=100 Local Preference Attribute - 100 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] DELETION FROM PEER 1 [separator]------------------------------------- [comment] DELETION FROM PEER 2 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] ****************************************** [comment] TEST 5D [comment] TEST OF MED - ONLY ONE HAS MED [comment] ROUTE WITHOUT MED WINS [comment] SENDING FROM PEER 1 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [separator]------------------------------------- [comment] DELETION FROM PEER 1 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Multiple Exit Descriminator Attribute: MED=100 Local Preference Attribute - 100 [separator]------------------------------------- [comment] DELETION FROM PEER 2 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Multiple Exit Descriminator Attribute: MED=100 Local Preference Attribute - 100 [separator]------------------------------------- [comment] ****************************************** [comment] TEST 5E [comment] TEST OF MED - DIFFERENT MEDS FROM DIFFERENCE AS'S [comment] [comment] SENDING FROM PEER 1 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Multiple Exit Descriminator Attribute: MED=100 Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Multiple Exit Descriminator Attribute: MED=100 Local Preference Attribute - 100 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Multiple Exit Descriminator Attribute: MED=200 Local Preference Attribute - 100 [separator]------------------------------------- [comment] DELETION FROM PEER 1 [separator]------------------------------------- [comment] DELETION FROM PEER 2 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Multiple Exit Descriminator Attribute: MED=200 Local Preference Attribute - 100 [separator]------------------------------------- [comment] ****************************************** [comment] TEST 5F1 [comment] TEST OF MED [comment] [comment] SENDING FROM PEER 1 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Multiple Exit Descriminator Attribute: MED=100 Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [comment] NEW ROUTE WINS ON BGP ID [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Multiple Exit Descriminator Attribute: MED=100 Local Preference Attribute - 100 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Multiple Exit Descriminator Attribute: MED=200 Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 3 [comment] ROUTE 1 RE-INSTATED [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Multiple Exit Descriminator Attribute: MED=200 Local Preference Attribute - 100 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Multiple Exit Descriminator Attribute: MED=100 Local Preference Attribute - 100 [separator]------------------------------------- [comment] DELETION FROM PEER 3 [comment] ROUTE 2 TAKES OVER [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Multiple Exit Descriminator Attribute: MED=100 Local Preference Attribute - 100 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Multiple Exit Descriminator Attribute: MED=200 Local Preference Attribute - 100 [separator]------------------------------------- [comment] DELETION FROM PEER 2 [comment] ROUTE 1 TAKES OVER [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Multiple Exit Descriminator Attribute: MED=200 Local Preference Attribute - 100 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Multiple Exit Descriminator Attribute: MED=100 Local Preference Attribute - 100 [separator]------------------------------------- [comment] DELETION FROM PEER 1 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Multiple Exit Descriminator Attribute: MED=100 Local Preference Attribute - 100 [separator]------------------------------------- [comment] ****************************************** [comment] TEST 5F2 [comment] TEST OF MED [comment] [separator]------------------------------------- [comment] SENDING FROM PEER 3 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Multiple Exit Descriminator Attribute: MED=50 Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 1 [comment] WINS ON BGP ID [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Multiple Exit Descriminator Attribute: MED=50 Local Preference Attribute - 100 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Multiple Exit Descriminator Attribute: MED=100 Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [comment] NO CHANGE [separator]------------------------------------- [comment] DELETION FROM PEER 2 [comment] NO CHANGE [separator]------------------------------------- [comment] DELETION FROM PEER 1 [comment] ROUTE 3 TAKES OVER [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Multiple Exit Descriminator Attribute: MED=100 Local Preference Attribute - 100 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Multiple Exit Descriminator Attribute: MED=50 Local Preference Attribute - 100 [separator]------------------------------------- [comment] DELETION FROM PEER 3 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Multiple Exit Descriminator Attribute: MED=50 Local Preference Attribute - 100 [separator]------------------------------------- [comment] ****************************************** [comment] TEST 5F3 [comment] TEST OF MED [comment] [separator]------------------------------------- [comment] SENDING FROM PEER 2 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Multiple Exit Descriminator Attribute: MED=200 Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 1 [comment] NO CHANGE [separator]------------------------------------- [comment] SENDING FROM PEER 3 [comment] ROUTE 1 NOW WINS [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Multiple Exit Descriminator Attribute: MED=200 Local Preference Attribute - 100 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Multiple Exit Descriminator Attribute: MED=100 Local Preference Attribute - 100 [separator]------------------------------------- [comment] DELETION FROM PEER 3 [comment] ROUTE 2 TAKES OVER [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Multiple Exit Descriminator Attribute: MED=100 Local Preference Attribute - 100 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Multiple Exit Descriminator Attribute: MED=200 Local Preference Attribute - 100 [separator]------------------------------------- [comment] DELETION FROM PEER 1 [comment] NO CHANGE [separator]------------------------------------- [comment] DELETION FROM PEER 2 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Multiple Exit Descriminator Attribute: MED=200 Local Preference Attribute - 100 [separator]------------------------------------- [comment] ****************************************** [comment] TEST 5F4 [comment] TEST OF MED [comment] [separator]------------------------------------- [comment] SENDING FROM PEER 1 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Multiple Exit Descriminator Attribute: MED=100 Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 3 [comment] NO CHANGE [separator]------------------------------------- [comment] SENDING FROM PEER 2 [comment] NO CHANGE [separator]------------------------------------- [comment] DELETION FROM PEER 2 [comment] NO CHANGE [separator]------------------------------------- [comment] DELETION FROM PEER 3 [comment] NO CHANGE [separator]------------------------------------- [comment] DELETION FROM PEER 1 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Multiple Exit Descriminator Attribute: MED=100 Local Preference Attribute - 100 [separator]------------------------------------- [comment] ****************************************** [comment] TEST 5F5 [comment] TEST OF MED [comment] [comment] SENDING FROM PEER 2 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Multiple Exit Descriminator Attribute: MED=200 Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 3 [comment] ROUTE 3 NOW WINS [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Multiple Exit Descriminator Attribute: MED=200 Local Preference Attribute - 100 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Multiple Exit Descriminator Attribute: MED=50 Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 1 [comment] ROUTE 1 NOW WINS [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Multiple Exit Descriminator Attribute: MED=50 Local Preference Attribute - 100 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Multiple Exit Descriminator Attribute: MED=100 Local Preference Attribute - 100 [separator]------------------------------------- [comment] DELETION FROM PEER 1 [comment] ROUTE 3 TAKES OVER [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Multiple Exit Descriminator Attribute: MED=100 Local Preference Attribute - 100 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Multiple Exit Descriminator Attribute: MED=50 Local Preference Attribute - 100 [separator]------------------------------------- [comment] DELETION FROM PEER 3 [comment] ROUTE 2 TAKES OVER [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Multiple Exit Descriminator Attribute: MED=50 Local Preference Attribute - 100 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Multiple Exit Descriminator Attribute: MED=200 Local Preference Attribute - 100 [separator]------------------------------------- [comment] DELETION FROM PEER 2 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Multiple Exit Descriminator Attribute: MED=200 Local Preference Attribute - 100 [separator]------------------------------------- [comment] ****************************************** [comment] TEST 5F6 [comment] TEST OF MED [comment] [separator]------------------------------------- [comment] SENDING FROM PEER 3 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Multiple Exit Descriminator Attribute: MED=50 Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [comment] NO CHANGE [separator]------------------------------------- [comment] SENDING FROM PEER 1 [comment] ROUTE 1 WINS [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Multiple Exit Descriminator Attribute: MED=50 Local Preference Attribute - 100 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Multiple Exit Descriminator Attribute: MED=100 Local Preference Attribute - 100 [separator]------------------------------------- [comment] DELETION FROM PEER 1 [comment] ROUTE 3 TAKES OVER [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Multiple Exit Descriminator Attribute: MED=100 Local Preference Attribute - 100 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Multiple Exit Descriminator Attribute: MED=50 Local Preference Attribute - 100 [separator]------------------------------------- [comment] DELETION FROM PEER 2 [comment] NO CHANGE [separator]------------------------------------- [comment] DELETION FROM PEER 3 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Multiple Exit Descriminator Attribute: MED=50 Local Preference Attribute - 100 [separator]------------------------------------- [comment] ****************************************** [comment] TEST 6 [comment] TEST OF ORIGIN [comment] IGP beats EGP beats INCOMPLETE [comment] SENDING FROM PEER 1 [comment] EXPECT: WINS BY DEFAULT [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [comment] EXPECT: LOSES [separator]------------------------------------- [comment] DELETION FROM PEER 1 [comment] EXPECT: ALLOWS EGP ROUTE TO WIN [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - EGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 1 [comment] EXPECT: LOSES [separator]------------------------------------- [comment] DELETION FROM PEER 2 [comment] EXPECT: ALLOWS INCOMPLETE ROUTE TO WIN [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - EGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - INCOMPLETE AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] DELETION FROM PEER 1 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - INCOMPLETE AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] ****************************************** [comment] TEST 6B [comment] TEST OF ORIGIN [comment] IGP beats EGP beats INCOMPLETE [separator]------------------------------------- [comment] SENDING FROM PEER 2 [comment] EXPECT: WINS BY DEFAULT [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - INCOMPLETE AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 1 [comment] EXPECT: WINS [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - INCOMPLETE AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - EGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] DELETION FROM PEER 2 [comment] EXPECT: NO CHANGE [separator]------------------------------------- [comment] SENDING FROM PEER 2 [comment] EXPECT: WINS [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - EGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] DELETION FROM PEER 2 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - EGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] DELETION FROM PEER 1 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - EGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] ****************************************** [comment] TEST 7 [comment] TEST OF IGP DISTANCE [comment] SENDING FROM PEER 1 [comment] PEER 1 HAS BETTER IGP DISTANCE [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [separator]------------------------------------- [comment] DELETION FROM PEER 1 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [separator]------------------------------------- [comment] DELETION FROM PEER 2 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [separator]------------------------------------- [comment] ****************************************** [comment] TEST 7B [comment] TEST OF IGP DISTANCE [comment] SENDING FROM PEER 1 [comment] PEER 2 HAS BETTER IGP DISTANCE [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [separator]------------------------------------- [comment] DELETION FROM PEER 1 [separator]------------------------------------- [comment] DELETION FROM PEER 2 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [separator]------------------------------------- [comment] ****************************************** [comment] TEST 7C [comment] TEST OF IGP DISTANCE [comment] PEER 2 NOT RESOLVABLE [separator]------------------------------------- [comment] SENDING FROM PEER 1 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [separator]------------------------------------- [comment] DELETION FROM PEER 1 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] DELETION FROM PEER 2 [separator]------------------------------------- [comment] ****************************************** [comment] TEST 7D [comment] TEST OF IGP DISTANCE [comment] PEER 1 NOT RESOLVABLE [separator]------------------------------------- [comment] SENDING FROM PEER 1 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [separator]------------------------------------- [comment] DELETION FROM PEER 1 [separator]------------------------------------- [comment] DELETION FROM PEER 2 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [separator]------------------------------------- [comment] ****************************************** [comment] TEST 7E [comment] TEST OF IGP DISTANCE [comment] NEITHER PEER IS RESOLVABLE [separator]------------------------------------- [comment] SENDING FROM PEER 1 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [separator]------------------------------------- [comment] DELETION FROM PEER 1 [separator]------------------------------------- [comment] DELETION FROM PEER 2 [separator]------------------------------------- [comment] ****************************************** [comment] TEST 8A [comment] TEST OF REPLACE_ROUTE [comment] REPLACING FROM A SINGLE PEER [separator]------------------------------------- [comment] SENDING FROM PEER 1 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 1 [REPLACE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] DELETION FROM PEER 1 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] ****************************************** [comment] TEST 8B [comment] TEST OF REPLACE_ROUTE [comment] REPLACING FROM A SINGLE PEER, FIRST NOT RESOLVABLE [separator]------------------------------------- [comment] SENDING FROM PEER 1 [separator]------------------------------------- [comment] SENDING FROM PEER 1 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [separator]------------------------------------- [comment] DELETION FROM PEER 1 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [separator]------------------------------------- [comment] ****************************************** [comment] TEST 8C [comment] TEST OF REPLACE_ROUTE [comment] REPLACING FROM A SINGLE PEER, SECOND NOT RESOLVABLE [separator]------------------------------------- [comment] SENDING FROM PEER 1 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 1 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [separator]------------------------------------- [comment] DELETION FROM PEER 1 [separator]------------------------------------- [comment] ****************************************** [comment] TEST 8D [comment] TEST OF REPLACE_ROUTE [comment] REPLACING FROM A SINGLE PEER, NEITHER RESOLVABLE [separator]------------------------------------- [comment] SENDING FROM PEER 1 [separator]------------------------------------- [comment] SENDING FROM PEER 1 [separator]------------------------------------- [comment] DELETION FROM PEER 1 [separator]------------------------------------- [comment] ****************************************** [comment] TEST 9A [comment] TEST OF REPLACE_ROUTE [comment] PEER 1 WINS, PEER 2 LOSES, PEER2 WINS [separator]------------------------------------- [comment] SENDING FROM PEER 1 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 200 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 200 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 300 [separator]------------------------------------- [comment] DELETION FROM PEER 2 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 300 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 200 [separator]------------------------------------- [comment] DELETION FROM PEER 1 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 200 [separator]------------------------------------- [comment] ****************************************** [comment] TEST 9B [comment] TEST OF REPLACE_ROUTE [comment] PEER 2 WINS, PEER 1 WINS, PEER2 WINS [separator]------------------------------------- [comment] SENDING FROM PEER 2 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 1 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 200 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 200 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 300 [separator]------------------------------------- [comment] DELETION FROM PEER 2 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 300 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 200 [separator]------------------------------------- [comment] DELETION FROM PEER 1 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 200 [separator]------------------------------------- [comment] ****************************************** [comment] TEST 9C [comment] TEST OF REPLACE_ROUTE [comment] PEER 1 WINS, PEER 2 LOSES, PEER2 LOSES [separator]------------------------------------- [comment] SENDING FROM PEER 1 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 300 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [separator]------------------------------------- [comment] DELETION FROM PEER 2 [separator]------------------------------------- [comment] DELETION FROM PEER 1 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 300 [separator]------------------------------------- [comment] ****************************************** [comment] TEST 9D [comment] TEST OF REPLACE_ROUTE [comment] PEER 1 WINS, PEER 2 WINS, PEER2 WINS [separator]------------------------------------- [comment] SENDING FROM PEER 1 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 300 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [REPLACE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 300 SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 200 [separator]------------------------------------- [comment] DELETION FROM PEER 2 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 200 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] DELETION FROM PEER 1 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] ****************************************** [comment] TEST 9E [comment] TEST OF REPLACE_ROUTE [comment] PEER 1 WINS, PEER 2 WINS, PEER2 LOSES [separator]------------------------------------- [comment] SENDING FROM PEER 1 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 200 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 200 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 300 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 300 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 200 [separator]------------------------------------- [comment] DELETION FROM PEER 1 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 200 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] DELETION FROM PEER 2 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] ****************************************** [comment] TEST 10A [comment] TEST OF REPLACE_ROUTE [comment] PEER 1 WINS, PEER 2 WINS, PEER2 BECOMES UNRESOLVABLE [separator]------------------------------------- [comment] SENDING FROM PEER 1 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 200 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 200 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 300 [separator]------------------------------------- [comment] NEXTHOP 2.0.0.3 BECOMES UNRESOLVABLE [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 300 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 200 [PUSH] [separator]------------------------------------- [comment] DELETION FROM PEER 1 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 200 [separator]------------------------------------- [comment] DELETION FROM PEER 2 [separator]------------------------------------- [comment] ****************************************** [comment] TEST 10B [comment] TEST OF REPLACE_ROUTE [comment] PEER 1 WINS, PEER 2 WINS, PEER1 BECOMES UNRESOLVABLE [separator]------------------------------------- [comment] SENDING FROM PEER 1 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 200 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 200 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 300 [separator]------------------------------------- [comment] NEXTHOP 2.0.0.2 BECOMES UNRESOLVABLE [PUSH] [separator]------------------------------------- [comment] DELETION FROM PEER 2 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 300 [separator]------------------------------------- [comment] DELETION FROM PEER 1 [separator]------------------------------------- [comment] ****************************************** [comment] TEST 10C [comment] TEST OF REPLACE_ROUTE [comment] PEER 1 WINS, PEER 2 NOT RESOLVABLE, PEER2 BECOMES RESOLVABLE AND WINS [separator]------------------------------------- [comment] SENDING FROM PEER 1 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 200 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [separator]------------------------------------- [comment] NEXTHOP 2.0.0.3 BECOMES RESOLVABLE [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 200 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 300 [PUSH] [separator]------------------------------------- [comment] DELETION FROM PEER 2 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 300 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 200 [separator]------------------------------------- [comment] DELETION FROM PEER 1 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 200 [separator]------------------------------------- [comment] ****************************************** [comment] TEST 11A [comment] TEST OF ROUTES BECOMING RESOLVABLE [comment] PEER 1, 2 NOT RESOLVABLE, PEER1 BECOMES RESOLVABLE [separator]------------------------------------- [comment] SENDING FROM PEER 1 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [separator]------------------------------------- [comment] NEXTHOP 2.0.0.2 BECOMES RESOLVABLE [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 200 [PUSH] [separator]------------------------------------- [comment] DELETION FROM PEER 2 [separator]------------------------------------- [comment] DELETION FROM PEER 1 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 200 [separator]------------------------------------- [comment] ****************************************** [comment] TEST 11B [comment] TEST OF ROUTES BECOMING RESOLVABLE [comment] PEER 1, 2 NOT RESOLVABLE, BOTH BECOME RESOLVABLE SIMULTANEOUSLY [separator]------------------------------------- [comment] SENDING FROM PEER 1 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [separator]------------------------------------- [comment] NEXTHOP 2.0.0.1 BECOMES RESOLVABLE [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 200 [PUSH] [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 200 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 300 [PUSH] [separator]------------------------------------- [comment] DELETION FROM PEER 2 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 300 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 200 [separator]------------------------------------- [comment] DELETION FROM PEER 1 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 200 [separator]------------------------------------- [comment] ****************************************** [comment] TEST 11C [comment] TEST OF ROUTES BECOMING UNRESOLVABLE [comment] PEER 1, 2 RESOLVABLE, BOTH BECOME UNRESOLVABLE SIMULTANEOUSLY [separator]------------------------------------- [comment] SENDING FROM PEER 1 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 200 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 200 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 300 [separator]------------------------------------- [comment] NEXTHOP 2.0.0.1 BECOMES UNRESOLVABLE [PUSH] [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 300 [PUSH] [separator]------------------------------------- [comment] DELETION FROM PEER 2 [separator]------------------------------------- [comment] DELETION FROM PEER 1 [separator]------------------------------------- [comment] ****************************************** [comment] TEST 12 [comment] TEST WITH THREE PEERS [separator]------------------------------------- [comment] SENDING FROM PEER 1 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 200 [separator]------------------------------------- [comment] SENDING FROM PEER 3 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 200 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 300 [separator]------------------------------------- [comment] DELETION FROM PEER 3 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 300 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 200 [separator]------------------------------------- [comment] DELETION FROM PEER 2 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 200 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] DELETION FROM PEER 1 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] ****************************************** [comment] TEST 13 [comment] TEST OF IBGP vs EBGP [comment] SENDING FROM PEER 1 [comment] PEER 1 HAS BETTER IGP DISTANCE [comment] PEER 2 IS EBGP [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [comment] EXPECT: WINS [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [separator]------------------------------------- [comment] DELETION FROM PEER 1 [separator]------------------------------------- [comment] DELETION FROM PEER 2 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [separator]------------------------------------- [comment] ****************************************** [comment] TEST 13B [comment] TEST OF IBGP vs EBGP [comment] SENDING FROM PEER 1 [comment] PEER 1 HAS BETTER IGP DISTANCE [comment] PEER 2 IS EBGP [separator]------------------------------------- [comment] SENDING FROM PEER 2 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 1 [comment] EXPECT: LOSES [separator]------------------------------------- [comment] DELETION FROM PEER 2 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] DELETION FROM PEER 1 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] SHUTDOWN AND CLEAN UP xorp/bgp/tests/test_dump.cc0000664000076400007640000022652211540224220016106 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "bgp_module.h" #include "libxorp/xorp.h" #include "libxorp/xorpfd.hh" #include "libxorp/eventloop.hh" #include "libxorp/xlog.h" #include "libxorp/test_main.hh" #ifdef HAVE_PWD_H #include #endif #include "bgp.hh" #include "route_table_base.hh" #include "route_table_filter.hh" #include "route_table_ribin.hh" #include "route_table_debug.hh" #include "path_attribute.hh" #include "local_data.hh" #include "dummy_next_hop_resolver.hh" bool test_dump_create(TestInfo& /*info*/) { const list *> l; DumpTable *dt = new DumpTable("dumptable", 0, l, 0, SAFI_UNICAST); dt->set_next_table(dt); dt->suspend_dump(); return true; } bool test_dump(TestInfo& /*info*/) { //stuff needed to create an eventloop #ifndef HOST_OS_WINDOWS struct passwd *pwd = getpwuid(getuid()); string filename = "/tmp/test_dump."; filename += pwd->pw_name; #else char *tmppath = (char *)malloc(256); GetTempPathA(256, tmppath); string filename = string(tmppath) + "test_dump"; free(tmppath); #endif EventLoop eventloop; BGPMain bgpmain(eventloop); LocalData localdata(bgpmain.eventloop()); Iptuple iptuple1("3.0.0.127", 179, "2.0.0.1", 179); BGPPeerData *peer_data1 = new BGPPeerData(localdata, iptuple1, AsNum(1), IPv4("2.0.0.1"), 30); //start off with both being IBGP peer_data1->set_id("2.0.0.1"); BGPPeer peer1(&localdata, peer_data1, NULL, &bgpmain); PeerHandler handler1("test1", &peer1, NULL, NULL); Iptuple iptuple2("3.0.0.127", 179, "2.0.0.2", 179); BGPPeerData *peer_data2 = new BGPPeerData(localdata, iptuple2, AsNum(1), IPv4("2.0.0.2"), 30); //start off with both being IBGP peer_data2->set_id("2.0.0.2"); BGPPeer peer2(&localdata, peer_data2, NULL, &bgpmain); PeerHandler handler2("test2", &peer2, NULL, NULL); Iptuple iptuple3("3.0.0.127", 179, "2.0.0.3", 179); BGPPeerData *peer_data3 = new BGPPeerData(localdata, iptuple3, AsNum(1), IPv4("2.0.0.3"), 30); //start off with both being IBGP peer_data3->set_id("2.0.0.3"); BGPPeer peer3(&localdata, peer_data3, NULL, &bgpmain); PeerHandler handler3("test3", &peer3, NULL, NULL); DummyNextHopResolver next_hop_resolver(bgpmain.eventloop(), bgpmain); DecisionTable *decision_table = new DecisionTable("DECISION", SAFI_UNICAST, next_hop_resolver); RibInTable* ribin_table1 = new RibInTable("RIB-IN1", SAFI_UNICAST, &handler1); //In principle the cache shouldn't be needed, but ribin can't be //directly followed by decision because the dump_table calls //set_parent on the next table, and decision table doesn't support //set_parent because it is the only table with multiple parents. CacheTable* cache_table1 = new CacheTable("Cache1", SAFI_UNICAST, ribin_table1, &handler1); NhLookupTable* nhlookup_table1 = new NhLookupTable("NHLOOKUP", SAFI_UNICAST, &next_hop_resolver, cache_table1); ribin_table1->set_next_table(cache_table1); cache_table1->set_next_table(nhlookup_table1); nhlookup_table1->set_next_table(decision_table); RibInTable* ribin_table2 = new RibInTable("RIB-IN2", SAFI_UNICAST, &handler2); CacheTable* cache_table2 = new CacheTable("Cache2", SAFI_UNICAST, ribin_table2, &handler2); NhLookupTable* nhlookup_table2 = new NhLookupTable("NHLOOKUP", SAFI_UNICAST, &next_hop_resolver, cache_table2); ribin_table2->set_next_table(cache_table2); cache_table2->set_next_table(nhlookup_table2); nhlookup_table2->set_next_table(decision_table); RibInTable* ribin_table3 = new RibInTable("RIB-IN3", SAFI_UNICAST, &handler3); CacheTable* cache_table3 = new CacheTable("Cache3", SAFI_UNICAST, ribin_table3, &handler3); NhLookupTable* nhlookup_table3 = new NhLookupTable("NHLOOKUP", SAFI_UNICAST, &next_hop_resolver, cache_table3); ribin_table3->set_next_table(cache_table3); cache_table3->set_next_table(nhlookup_table3); nhlookup_table3->set_next_table(decision_table); decision_table->add_parent(nhlookup_table1, &handler1, ribin_table1->genid()); decision_table->add_parent(nhlookup_table2, &handler2, ribin_table2->genid()); decision_table->add_parent(nhlookup_table3, &handler3, ribin_table3->genid()); FanoutTable *fanout_table = new FanoutTable("FANOUT", SAFI_UNICAST, decision_table, NULL, NULL); decision_table->set_next_table(fanout_table); DebugTable* debug_table1 = new DebugTable("D1", (BGPRouteTable*)fanout_table); fanout_table->add_next_table(debug_table1, &handler1, ribin_table1->genid()); DebugTable* debug_table2 = new DebugTable("D2", (BGPRouteTable*)fanout_table); fanout_table->add_next_table(debug_table2, &handler2, ribin_table2->genid()); DebugTable* debug_table3 = new DebugTable("D3", (BGPRouteTable*)fanout_table); fanout_table->add_next_table(debug_table3, &handler3, ribin_table3->genid()); debug_table1->set_output_file(filename); debug_table1->set_canned_response(ADD_USED); debug_table1->enable_tablename_printing(); debug_table1->set_get_on_wakeup(true); debug_table2->set_output_file(debug_table1->output_file()); debug_table2->set_canned_response(ADD_USED); debug_table2->enable_tablename_printing(); debug_table2->set_get_on_wakeup(true); debug_table3->set_output_file(debug_table1->output_file()); debug_table3->set_canned_response(ADD_USED); debug_table3->enable_tablename_printing(); debug_table3->set_get_on_wakeup(true); //create a load of attributes IPNet net0("1.0.0.0/24"); IPNet net1("1.0.1.0/24"); IPNet net2("1.0.2.0/24"); IPNet net3("1.0.3.0/24"); IPNet net4("1.0.4.0/24"); IPNet net5("1.0.5.0/24"); IPv4 nexthop1("2.0.0.1"); NextHopAttribute nhatt1(nexthop1); IPv4 nexthop2("2.0.0.2"); NextHopAttribute nhatt2(nexthop2); IPv4 nexthop3("2.0.0.3"); NextHopAttribute nhatt3(nexthop3); IPv4 nexthop4("2.0.0.4"); NextHopAttribute nhatt4(nexthop4); IPv4 nexthop5("2.0.0.5"); NextHopAttribute nhatt5(nexthop5); next_hop_resolver.set_nexthop_metric(nexthop1, 27); next_hop_resolver.set_nexthop_metric(nexthop2, 27); next_hop_resolver.set_nexthop_metric(nexthop3, 27); next_hop_resolver.set_nexthop_metric(nexthop4, 27); next_hop_resolver.set_nexthop_metric(nexthop5, 27); OriginAttribute igp_origin_att(IGP); ASPath aspath1; aspath1.prepend_as(AsNum(1)); aspath1.prepend_as(AsNum(2)); aspath1.prepend_as(AsNum(3)); ASPathAttribute aspathatt1(aspath1); ASPath aspath2; aspath2.prepend_as(AsNum(4)); aspath2.prepend_as(AsNum(5)); aspath2.prepend_as(AsNum(6)); aspath2.prepend_as(AsNum(6)); ASPathAttribute aspathatt2(aspath2); ASPath aspath3; aspath3.prepend_as(AsNum(7)); aspath3.prepend_as(AsNum(8)); aspath3.prepend_as(AsNum(9)); ASPathAttribute aspathatt3(aspath3); LocalPrefAttribute lpa1(100); FPAList4Ref fpalist1 = new FastPathAttributeList(nhatt1, aspathatt1, igp_origin_att); fpalist1->add_path_attribute(lpa1); PAListRef palist1 = new PathAttributeList(fpalist1); palist1.create_attribute_manager(); FPAList4Ref fpalist2 = new FastPathAttributeList(nhatt2, aspathatt2, igp_origin_att); fpalist2->add_path_attribute(lpa1); PAListRef palist2 = new PathAttributeList(fpalist2); FPAList4Ref fpalist3 = new FastPathAttributeList(nhatt3, aspathatt3, igp_origin_att); fpalist3->add_path_attribute(lpa1); PAListRef palist3 = new PathAttributeList(fpalist3); FPAList4Ref fpalist4 = new FastPathAttributeList(nhatt4, aspathatt3, igp_origin_att); fpalist4->add_path_attribute(lpa1); PAListRef palist4 = new PathAttributeList(fpalist4); FPAList4Ref fpalist5 = new FastPathAttributeList(nhatt5, aspathatt3, igp_origin_att); fpalist5->add_path_attribute(lpa1); PAListRef palist5 = new PathAttributeList(fpalist5); //create a subnet route PolicyTags pt; //================================================================ //Test1: trivial add and delete to test plumbing //================================================================ //add a route debug_table1->write_comment("******************************************"); debug_table1->write_comment("TEST 1"); debug_table1->write_comment("ADD AND DELETE"); debug_table1->write_comment("SENDING FROM PEER 1"); ribin_table1->add_route(net1, fpalist1, pt); debug_table1->write_separator(); //delete the route ribin_table1->delete_route(net1); debug_table1->write_separator(); //================================================================ //Test2: simple dump //================================================================ //take peer3 down fanout_table->remove_next_table(debug_table3); ribin_table3->ribin_peering_went_down(); debug_table1->write_comment("******************************************"); debug_table1->write_comment("TEST 2"); debug_table1->write_comment("SIMPLE DUMP TO PEER 3"); debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 1"); ribin_table1->add_route(net1, fpalist1, pt); debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 2"); ribin_table2->add_route(net2, fpalist2, pt); debug_table1->write_separator(); debug_table1->write_comment("BRING PEER 3 UP"); ribin_table3->ribin_peering_came_up(); fanout_table->add_next_table(debug_table3, &handler3, ribin_table3->genid()); debug_table3->set_parent(fanout_table); fanout_table->dump_entire_table(debug_table3, SAFI_UNICAST, "ribname"); debug_table1->write_separator(); debug_table1->write_comment("LET EVENT QUEUE DRAIN"); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } while (debug_table3->parent()->get_next_message(debug_table3)) {} while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } //delete the routes debug_table1->write_separator(); debug_table1->write_comment("DELETING FROM PEER 1"); ribin_table1->delete_route(net1); debug_table1->write_separator(); debug_table1->write_comment("DELETING FROM PEER 2"); ribin_table2->delete_route(net2); debug_table1->write_separator(); //================================================================ //Test3: simple dump //================================================================ //take peer3 down fanout_table->remove_next_table(debug_table3); ribin_table3->ribin_peering_went_down(); debug_table1->write_comment("******************************************"); debug_table1->write_comment("TEST 3"); debug_table1->write_comment("SIMPLE DUMP TO PEER 3"); debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 1"); ribin_table1->add_route(net1, fpalist1, pt); debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 1"); ribin_table1->add_route(net3, fpalist3, pt); debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 2"); ribin_table2->add_route(net2, fpalist2, pt); debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 2"); ribin_table2->add_route(net4, fpalist2, pt); debug_table1->write_separator(); debug_table1->write_comment("BRING PEER 3 UP"); ribin_table3->ribin_peering_came_up(); fanout_table->add_next_table(debug_table3, &handler3, ribin_table3->genid()); debug_table3->set_parent(fanout_table); fanout_table->dump_entire_table(debug_table3, SAFI_UNICAST, "ribname"); debug_table1->write_separator(); debug_table1->write_comment("LET EVENT QUEUE DRAIN"); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } while (debug_table3->parent()->get_next_message(debug_table3)) {} while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } //delete the routes debug_table1->write_separator(); debug_table1->write_comment("DELETING FROM PEER 1"); ribin_table1->delete_route(net1); debug_table1->write_separator(); debug_table1->write_comment("DELETING FROM PEER 1"); ribin_table1->delete_route(net3); debug_table1->write_separator(); debug_table1->write_comment("DELETING FROM PEER 2"); ribin_table2->delete_route(net2); debug_table1->write_separator(); debug_table1->write_comment("DELETING FROM PEER 2"); ribin_table2->delete_route(net4); debug_table1->write_separator(); //================================================================ //Test4: dump, peer 2 goes down during dump, before anything has // been dumped //================================================================ //take peer3 down fanout_table->remove_next_table(debug_table3); ribin_table3->ribin_peering_went_down(); debug_table1->write_comment("******************************************"); debug_table1->write_comment("TEST 4"); debug_table1->write_comment("SIMPLE DUMP TO PEER 3, PEER 2 GOES DOWN"); debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 1"); ribin_table1->add_route(net1, fpalist1, pt); debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 1"); ribin_table1->add_route(net3, fpalist3, pt); debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 2"); ribin_table2->add_route(net2, fpalist2, pt); debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 2"); ribin_table2->add_route(net4, fpalist2, pt); debug_table1->write_separator(); debug_table1->write_comment("BRING PEER 3 UP"); ribin_table3->ribin_peering_came_up(); fanout_table->add_next_table(debug_table3, &handler3, ribin_table3->genid()); debug_table3->set_parent(fanout_table); fanout_table->dump_entire_table(debug_table3, SAFI_UNICAST, "ribname"); debug_table1->write_separator(); debug_table1->write_comment("PEER 2 GOES DOWN"); fanout_table->remove_next_table(debug_table2); debug_table1->write_comment("XXXX"); ribin_table2->ribin_peering_went_down(); debug_table1->write_separator(); debug_table1->write_comment("LET EVENT QUEUE DRAIN"); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } //delete the routes debug_table1->write_separator(); debug_table1->write_comment("DELETING FROM PEER 1"); ribin_table1->delete_route(net1); debug_table1->write_separator(); debug_table1->write_comment("DELETING FROM PEER 1"); ribin_table1->delete_route(net3); debug_table1->write_separator(); debug_table1->write_comment("BRING PEER 2 UP"); ribin_table2->ribin_peering_came_up(); fanout_table->add_next_table(debug_table2, &handler2, ribin_table2->genid()); debug_table2->set_parent(fanout_table); fanout_table->dump_entire_table(debug_table2, SAFI_UNICAST, "ribname"); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } debug_table1->write_separator(); //================================================================ //Test5: dump, peer 2 goes down during dump, half way through peer 2 dump //================================================================ //take peer3 down fanout_table->remove_next_table(debug_table3); ribin_table3->ribin_peering_went_down(); debug_table1->write_comment("******************************************"); debug_table1->write_comment("TEST 5"); debug_table1->write_comment("DUMP TO PEER 3, PEER 2 GOES DOWN DURING P2 DUMP"); debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 1"); debug_table1->write_comment("EXPECT RECEIVED BY PEER 2"); ribin_table1->add_route(net1, fpalist1, pt); debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 2"); debug_table1->write_comment("EXPECT RECEIVED BY PEER 1"); ribin_table2->add_route(net2, fpalist2, pt); debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 2"); debug_table1->write_comment("EXPECT RECEIVED BY PEER 1"); ribin_table2->add_route(net3, fpalist3, pt); debug_table1->write_separator(); debug_table1->write_comment("BRING PEER 3 UP"); ribin_table3->ribin_peering_came_up(); fanout_table->add_next_table(debug_table3, &handler3, ribin_table3->genid()); debug_table3->set_parent(fanout_table); fanout_table->dump_entire_table(debug_table3, SAFI_UNICAST, "ribname"); debug_table1->write_separator(); debug_table1->write_comment("LET EVENT QUEUE DRAIN"); debug_table1->write_comment("EXPECT ADD 1.0.1.0/24 RECEIVED BY PEER 3"); debug_table1->write_comment("EXPECT ADD 1.0.2.0/24 RECEIVED BY PEER 3"); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } debug_table1->write_separator(); debug_table1->write_comment("PEER 2 GOES DOWN"); fanout_table->remove_next_table(debug_table2); ribin_table2->ribin_peering_went_down(); debug_table1->write_separator(); debug_table1->write_comment("LET EVENT QUEUE DRAIN"); debug_table1->write_comment("EXPECT DEL 1.0.2.0/24 RECEIVED BY PEER 1"); debug_table1->write_comment("EXPECT DEL 1.0.2.0/24 RECEIVED BY PEER 3"); debug_table1->write_comment("EXPECT DEL 1.0.3.0/24 RECEIVED BY PEER 1"); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } //delete the routes debug_table1->write_separator(); debug_table1->write_comment("DELETING FROM PEER 1"); debug_table1->write_comment("EXPECT DEL 1.0.1.0/24 RECEIVED BY PEER 3"); ribin_table1->delete_route(net1); debug_table1->write_separator(); debug_table1->write_comment("BRING PEER 2 UP"); debug_table1->write_comment("EXPECT NO CHANGE"); ribin_table2->ribin_peering_came_up(); fanout_table->add_next_table(debug_table2, &handler2, ribin_table2->genid()); debug_table2->set_parent(fanout_table); fanout_table->dump_entire_table(debug_table2, SAFI_UNICAST, "ribname"); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } debug_table1->write_separator(); //================================================================ //Test6: dump, peer 1 goes down before any dump processing can happen //================================================================ //take peer3 down fanout_table->remove_next_table(debug_table3); ribin_table3->ribin_peering_went_down(); debug_table1->write_comment("******************************************"); debug_table1->write_comment("TEST 6"); debug_table1->write_comment("DUMP TO PEER 3, PEER 1 GOES DOWN"); debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 1"); debug_table1->write_comment("EXPECT RECEIVED BY PEER 2"); ribin_table1->add_route(net1, fpalist1, pt); debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 2"); debug_table1->write_comment("EXPECT RECEIVED BY PEER 1"); ribin_table2->add_route(net2, fpalist2, pt); debug_table1->write_separator(); debug_table1->write_comment("BRING PEER 3 UP"); debug_table1->write_comment("EXPECT NO IMMEDIATE CHANGE"); ribin_table3->ribin_peering_came_up(); fanout_table->add_next_table(debug_table3, &handler3, ribin_table3->genid()); debug_table3->set_parent(fanout_table); fanout_table->dump_entire_table(debug_table3, SAFI_UNICAST, "ribname"); debug_table1->write_separator(); debug_table1->write_comment("PEER 1 GOES DOWN"); debug_table1->write_comment("EXPECT NO IMMEDIATE CHANGE"); fanout_table->remove_next_table(debug_table1); ribin_table1->ribin_peering_went_down(); debug_table1->write_separator(); debug_table1->write_comment("LET EVENT QUEUE DRAIN"); debug_table1->write_comment("EXPECT ADD 1.0.2.0/24 RECEIVED BY PEER 3"); debug_table1->write_comment("EXPECT DEL 1.0.1.0/24 RECEIVED BY PEER 2"); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } //delete the routes debug_table1->write_separator(); debug_table1->write_comment("DELETING FROM PEER 2"); debug_table1->write_comment("EXPECT DEL 1.0.2.0/24 RECEIVED BY PEER 3"); ribin_table2->delete_route(net2); debug_table1->write_separator(); debug_table1->write_comment("BRING PEER 1 UP"); debug_table1->write_comment("EXPECT NO CHANGE"); ribin_table1->ribin_peering_came_up(); fanout_table->add_next_table(debug_table1, &handler1, ribin_table1->genid()); debug_table1->set_parent(fanout_table); fanout_table->dump_entire_table(debug_table1, SAFI_UNICAST, "ribname"); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } debug_table1->write_separator(); //================================================================ //Test7: dump, peer 2 goes down during dump, // between peer 1 and peer 2 dumps //================================================================ //take peer3 down fanout_table->remove_next_table(debug_table3); ribin_table3->ribin_peering_went_down(); debug_table1->write_comment("******************************************"); debug_table1->write_comment("TEST 7"); debug_table1->write_comment("DUMP TO PEER 3, PEER 2 GOES DOWN BETWEEN P1 AND P2 DUMPS"); debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 1"); debug_table1->write_comment("EXPECT RECEIVED BY PEER 2"); ribin_table1->add_route(net1, fpalist1, pt); debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 2"); debug_table1->write_comment("EXPECT RECEIVED BY PEER 1"); ribin_table2->add_route(net2, fpalist2, pt); debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 2"); debug_table1->write_comment("EXPECT RECEIVED BY PEER 1"); ribin_table2->add_route(net3, fpalist3, pt); debug_table1->write_separator(); debug_table1->write_comment("BRING PEER 3 UP"); ribin_table3->ribin_peering_came_up(); fanout_table->add_next_table(debug_table3, &handler3, ribin_table3->genid()); debug_table3->set_parent(fanout_table); fanout_table->dump_entire_table(debug_table3, SAFI_UNICAST, "ribname"); debug_table1->write_separator(); debug_table1->write_comment("LET EVENT QUEUE DRAIN"); debug_table1->write_comment("EXPECT ADD 1.0.1.0/24 RECEIVED BY PEER 3"); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } debug_table1->write_separator(); debug_table1->write_comment("PEER 2 GOES DOWN"); fanout_table->remove_next_table(debug_table2); ribin_table2->ribin_peering_went_down(); debug_table1->write_separator(); debug_table1->write_comment("LET EVENT QUEUE DRAIN"); debug_table1->write_comment("EXPECT DEL 1.0.2.0/24 RECEIVED BY PEER 1"); debug_table1->write_comment("EXPECT DEL 1.0.3.0/24 RECEIVED BY PEER 1"); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } //delete the routes debug_table1->write_separator(); debug_table1->write_comment("DELETING FROM PEER 1"); debug_table1->write_comment("EXPECT DEL 1.0.1.0/24 RECEIVED BY PEER 3"); ribin_table1->delete_route(net1); debug_table1->write_separator(); debug_table1->write_comment("BRING PEER 2 UP"); debug_table1->write_comment("EXPECT NO CHANGE"); ribin_table2->ribin_peering_came_up(); fanout_table->add_next_table(debug_table2, &handler2, ribin_table2->genid()); debug_table2->set_parent(fanout_table); fanout_table->dump_entire_table(debug_table2, SAFI_UNICAST, "ribname"); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } debug_table1->write_separator(); //================================================================ //Test8: dump, peer 2 goes down during dump, // before peer 1 dump //================================================================ //take peer3 down fanout_table->remove_next_table(debug_table3); ribin_table3->ribin_peering_went_down(); debug_table1->write_comment("******************************************"); debug_table1->write_comment("TEST 8"); debug_table1->write_comment("DUMP TO PEER 3, PEER 2 GOES DOWN BEFORE P1 DUMP"); debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 1"); debug_table1->write_comment("EXPECT RECEIVED BY PEER 2"); ribin_table1->add_route(net1, fpalist1, pt); debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 2"); debug_table1->write_comment("EXPECT RECEIVED BY PEER 1"); ribin_table2->add_route(net2, fpalist2, pt); debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 2"); debug_table1->write_comment("EXPECT RECEIVED BY PEER 1"); ribin_table2->add_route(net3, fpalist3, pt); debug_table1->write_separator(); debug_table1->write_comment("BRING PEER 3 UP"); ribin_table3->ribin_peering_came_up(); fanout_table->add_next_table(debug_table3, &handler3, ribin_table3->genid()); debug_table3->set_parent(fanout_table); fanout_table->dump_entire_table(debug_table3, SAFI_UNICAST, "ribname"); debug_table1->write_separator(); debug_table1->write_comment("PEER 2 GOES DOWN"); fanout_table->remove_next_table(debug_table2); ribin_table2->ribin_peering_went_down(); debug_table1->write_separator(); debug_table1->write_comment("LET EVENT QUEUE DRAIN"); debug_table1->write_comment("EXPECT ADD 1.0.1.0/24 RECEIVED BY PEER 3"); debug_table1->write_comment("EXPECT DEL 1.0.2.0/24 RECEIVED BY PEER 1"); debug_table1->write_comment("EXPECT DEL 1.0.3.0/24 RECEIVED BY PEER 1"); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } //delete the routes debug_table1->write_separator(); debug_table1->write_comment("DELETING FROM PEER 1"); debug_table1->write_comment("EXPECT DEL 1.0.1.0/24 RECEIVED BY PEER 3"); ribin_table1->delete_route(net1); debug_table1->write_separator(); debug_table1->write_comment("BRING PEER 2 UP"); debug_table1->write_comment("EXPECT NO CHANGE"); ribin_table2->ribin_peering_came_up(); fanout_table->add_next_table(debug_table2, &handler2, ribin_table2->genid()); debug_table2->set_parent(fanout_table); fanout_table->dump_entire_table(debug_table2, SAFI_UNICAST, "ribname"); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } debug_table1->write_separator(); //================================================================ //Test9: dump, both peers go down before first dump //================================================================ //take peer3 down fanout_table->remove_next_table(debug_table3); ribin_table3->ribin_peering_went_down(); debug_table1->write_comment("******************************************"); debug_table1->write_comment("TEST 9"); debug_table1->write_comment("DUMP TO PEER 3, P1 AND P2 GO DOWN BEFORE FIRST DUMP"); debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 1"); debug_table1->write_comment("EXPECT RECEIVED BY PEER 2"); ribin_table1->add_route(net1, fpalist1, pt); debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 2"); debug_table1->write_comment("EXPECT RECEIVED BY PEER 1"); ribin_table2->add_route(net2, fpalist2, pt); debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 2"); debug_table1->write_comment("EXPECT RECEIVED BY PEER 1"); ribin_table2->add_route(net3, fpalist3, pt); debug_table1->write_separator(); debug_table1->write_comment("BRING PEER 3 UP"); ribin_table3->ribin_peering_came_up(); fanout_table->add_next_table(debug_table3, &handler3, ribin_table3->genid()); debug_table3->set_parent(fanout_table); fanout_table->dump_entire_table(debug_table3, SAFI_UNICAST, "ribname"); debug_table1->write_separator(); debug_table1->write_comment("PEER 2 GOES DOWN"); fanout_table->remove_next_table(debug_table2); ribin_table2->ribin_peering_went_down(); debug_table1->write_separator(); debug_table1->write_comment("PEER 1 GOES DOWN"); fanout_table->remove_next_table(debug_table1); ribin_table1->ribin_peering_went_down(); debug_table1->write_separator(); debug_table1->write_comment("LET EVENT QUEUE DRAIN"); debug_table1->write_comment("EXPECT NO CHANGE"); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } debug_table1->write_separator(); debug_table1->write_comment("BRING PEER 2 UP"); debug_table1->write_comment("EXPECT NO CHANGE"); ribin_table2->ribin_peering_came_up(); fanout_table->add_next_table(debug_table2, &handler2, ribin_table2->genid()); debug_table2->set_parent(fanout_table); fanout_table->dump_entire_table(debug_table2, SAFI_UNICAST, "ribname"); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } debug_table1->write_separator(); debug_table1->write_comment("BRING PEER 1 UP"); debug_table1->write_comment("EXPECT NO CHANGE"); ribin_table1->ribin_peering_came_up(); fanout_table->add_next_table(debug_table1, &handler1, ribin_table1->genid()); debug_table1->set_parent(fanout_table); fanout_table->dump_entire_table(debug_table1, SAFI_UNICAST, "ribname"); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } debug_table1->write_separator(); //================================================================ //Test10: dump. Send new routes during dump //================================================================ //take peer3 down fanout_table->remove_next_table(debug_table3); ribin_table3->ribin_peering_went_down(); debug_table1->write_comment("******************************************"); debug_table1->write_comment("TEST 10"); debug_table1->write_comment("DUMP TO PEER 3, SEND NEW ROUTES"); debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 1"); debug_table1->write_comment("EXPECT RECEIVED BY PEER 2"); ribin_table1->add_route(net1, fpalist1, pt); debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 2"); debug_table1->write_comment("EXPECT RECEIVED BY PEER 1"); ribin_table2->add_route(net3, fpalist3, pt); debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 2"); debug_table1->write_comment("EXPECT RECEIVED BY PEER 1"); ribin_table2->add_route(net4, fpalist4, pt); debug_table1->write_separator(); debug_table1->write_comment("BRING PEER 3 UP"); ribin_table3->ribin_peering_came_up(); fanout_table->add_next_table(debug_table3, &handler3, ribin_table3->genid()); debug_table3->set_parent(fanout_table); fanout_table->dump_entire_table(debug_table3, SAFI_UNICAST, "ribname"); debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 1"); debug_table1->write_comment("EXPECT RECEIVED BY PEER 2"); ribin_table1->add_route(net2, fpalist2, pt); debug_table1->write_separator(); debug_table1->write_comment("LET EVENT QUEUE DRAIN"); debug_table1->write_comment("EXPECT ADD 1.0.1.0/24 RECEIVED BY PEER 3"); debug_table1->write_comment("EXPECT ADD 1.0.2.0/24 RECEIVED BY PEER 3"); debug_table1->write_comment("EXPECT ADD 1.0.3.0/24 RECEIVED BY PEER 3"); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 2"); debug_table1->write_comment("EXPECT RECEIVED BY PEER 1"); debug_table1->write_comment("EXPECT RECEIVED BY PEER 3 WHEN EVENTLOOP RUNS"); ribin_table2->add_route(net0, fpalist5, pt); debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 2"); debug_table1->write_comment("EXPECT RECEIVED BY PEER 1"); ribin_table2->add_route(net5, fpalist5, pt); debug_table1->write_separator(); debug_table1->write_comment("LET EVENT QUEUE DRAIN"); debug_table1->write_comment("EXPECT ADD 1.0.4.0/24 RECEIVED BY PEER 3"); debug_table1->write_comment("EXPECT ADD 1.0.5.0/24 RECEIVED BY PEER 3"); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } //delete the routes debug_table1->write_separator(); debug_table1->write_comment("DELETING FROM PEER 1"); debug_table1->write_comment("EXPECT DEL 1.0.1.0/24 RECEIVED BY PEER 2"); debug_table1->write_comment("EXPECT DEL 1.0.1.0/24 RECEIVED BY PEER 3"); ribin_table1->delete_route(net1); //delete the routes debug_table1->write_separator(); debug_table1->write_comment("DELETING FROM PEER 1"); debug_table1->write_comment("EXPECT DEL 1.0.2.0/24 RECEIVED BY PEER 2"); debug_table1->write_comment("EXPECT DEL 1.0.2.0/24 RECEIVED BY PEER 3"); ribin_table1->delete_route(net2); //delete the routes debug_table1->write_separator(); debug_table1->write_comment("DELETING FROM PEER 2"); debug_table1->write_comment("EXPECT DEL 1.0.3.0/24 RECEIVED BY PEER 1"); debug_table1->write_comment("EXPECT DEL 1.0.3.0/24 RECEIVED BY PEER 3"); ribin_table2->delete_route(net3); //delete the routes debug_table1->write_separator(); debug_table1->write_comment("DELETING FROM PEER 2"); debug_table1->write_comment("EXPECT DEL 1.0.4.0/24 RECEIVED BY PEER 1"); debug_table1->write_comment("EXPECT DEL 1.0.4.0/24 RECEIVED BY PEER 3"); ribin_table2->delete_route(net4); //delete the routes debug_table1->write_separator(); debug_table1->write_comment("DELETING FROM PEER 2"); debug_table1->write_comment("EXPECT DEL 1.0.0.0/24 RECEIVED BY PEER 1"); debug_table1->write_comment("EXPECT DEL 1.0.0.0/24 RECEIVED BY PEER 3"); ribin_table2->delete_route(net0); //delete the routes debug_table1->write_separator(); debug_table1->write_comment("DELETING FROM PEER 2"); debug_table1->write_comment("EXPECT DEL 1.0.5.0/24 RECEIVED BY PEER 1"); debug_table1->write_comment("EXPECT DEL 1.0.5.0/24 RECEIVED BY PEER 3"); ribin_table2->delete_route(net5); debug_table1->write_separator(); //================================================================ //Test11: dump. Replace routes during dump //================================================================ //take peer3 down fanout_table->remove_next_table(debug_table3); ribin_table3->ribin_peering_went_down(); debug_table1->write_comment("******************************************"); debug_table1->write_comment("TEST 11"); debug_table1->write_comment("DUMP TO PEER 3, REPLACE ROUTES"); debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 1"); debug_table1->write_comment("EXPECT RECEIVED BY PEER 2"); ribin_table1->add_route(net1, fpalist1, pt); debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 2"); debug_table1->write_comment("EXPECT RECEIVED BY PEER 1"); ribin_table2->add_route(net3, fpalist3, pt); debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 2"); debug_table1->write_comment("EXPECT RECEIVED BY PEER 1"); ribin_table2->add_route(net4, fpalist4, pt); debug_table1->write_separator(); debug_table1->write_comment("BRING PEER 3 UP"); ribin_table3->ribin_peering_came_up(); fanout_table->add_next_table(debug_table3, &handler3, ribin_table3->genid()); debug_table3->set_parent(fanout_table); fanout_table->dump_entire_table(debug_table3, SAFI_UNICAST, "ribname"); debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 1"); debug_table1->write_comment("EXPECT REPLACE AT PEER 2"); ribin_table1->add_route(net1, fpalist5, pt); debug_table1->write_separator(); debug_table1->write_comment("LET EVENT QUEUE DRAIN"); debug_table1->write_comment("EXPECT ADD 1.0.1.0/24 RECEIVED BY PEER 3"); debug_table1->write_comment("EXPECT ADD 1.0.3.0/24 RECEIVED BY PEER 3"); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 2"); debug_table1->write_comment("EXPECT REPLACE AT PEER 1"); debug_table1->write_comment("EXPECT REPLACE AT PEER 3 WHEN EVENTLOOP RUNS"); ribin_table2->add_route(net3, fpalist5, pt); debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 2"); debug_table1->write_comment("EXPECT REPLACE AT PEER 1"); ribin_table2->add_route(net4, fpalist5, pt); debug_table1->write_separator(); debug_table1->write_comment("LET EVENT QUEUE DRAIN"); debug_table1->write_comment("EXPECT REPLACE 1.0.3.0/24 RECEIVED BY PEER 3"); debug_table1->write_comment("EXPECT ADD 1.0.4.0/24 RECEIVED BY PEER 3"); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } //delete the routes debug_table1->write_separator(); debug_table1->write_comment("DELETING FROM PEER 1"); debug_table1->write_comment("EXPECT DEL 1.0.1.0/24 RECEIVED BY PEER 2"); debug_table1->write_comment("EXPECT DEL 1.0.1.0/24 RECEIVED BY PEER 3"); ribin_table1->delete_route(net1); //delete the routes debug_table1->write_separator(); debug_table1->write_comment("DELETING FROM PEER 2"); debug_table1->write_comment("EXPECT DEL 1.0.3.0/24 RECEIVED BY PEER 1"); debug_table1->write_comment("EXPECT DEL 1.0.3.0/24 RECEIVED BY PEER 3"); ribin_table2->delete_route(net3); //delete the routes debug_table1->write_separator(); debug_table1->write_comment("DELETING FROM PEER 2"); debug_table1->write_comment("EXPECT DEL 1.0.4.0/24 RECEIVED BY PEER 1"); debug_table1->write_comment("EXPECT DEL 1.0.4.0/24 RECEIVED BY PEER 3"); ribin_table2->delete_route(net4); debug_table1->write_separator(); //================================================================ // Test12: peer 1 goes down just before peer 3 comes up, peer 1 // deletion table only runs after peer 3 comes up. //================================================================ //take peer3 down fanout_table->remove_next_table(debug_table3); ribin_table3->ribin_peering_went_down(); debug_table1->write_comment("******************************************"); debug_table1->write_comment("TEST 12"); debug_table1->write_comment("PEER 1 GOES DOWN JUST BEFORE P3 COMES UP"); debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 1"); debug_table1->write_comment("EXPECT RECEIVED BY PEER 2"); ribin_table1->add_route(net1, fpalist1, pt); debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 1"); debug_table1->write_comment("EXPECT RECEIVED BY PEER 2"); ribin_table1->add_route(net2, fpalist2, pt); debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 2"); debug_table1->write_comment("EXPECT RECEIVED BY PEER 1"); ribin_table2->add_route(net3, fpalist3, pt); debug_table1->write_separator(); debug_table1->write_comment("TAKE PEER 1 DOWN"); int genid = ribin_table1->genid(); fanout_table->remove_next_table(debug_table1); ribin_table1->ribin_peering_went_down(); debug_table1->write_separator(); debug_table1->write_comment("BRING PEER 3 UP"); ribin_table3->ribin_peering_came_up(); fanout_table->add_next_table(debug_table3, &handler3, ribin_table3->genid()); debug_table3->set_parent(fanout_table); fanout_table->dump_entire_table(debug_table3, SAFI_UNICAST, "ribname"); ((DumpTable*)debug_table3->parent()) ->peering_is_down(&handler1, genid); debug_table1->write_separator(); debug_table1->write_comment("LET EVENT QUEUE DRAIN"); debug_table1->write_comment("EXPECT DEL 1.0.1.0/24 RECEIVED BY PEER 2"); debug_table1->write_comment("EXPECT DEL 1.0.2.0/24 RECEIVED BY PEER 2"); debug_table1->write_comment("EXPECT ADD 1.0.3.0/24 RECEIVED BY PEER 3"); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } debug_table1->write_separator(); //delete the routes debug_table1->write_separator(); debug_table1->write_comment("DELETING FROM PEER 2"); debug_table1->write_comment("EXPECT DEL 1.0.3.0/24 RECEIVED BY PEER 3"); ribin_table2->delete_route(net3); debug_table1->write_separator(); debug_table1->write_comment("BRING PEER 1 UP"); debug_table1->write_comment("EXPECT NO CHANGE"); ribin_table1->ribin_peering_came_up(); fanout_table->add_next_table(debug_table1, &handler1, ribin_table1->genid()); debug_table1->set_parent(fanout_table); fanout_table->dump_entire_table(debug_table1, SAFI_UNICAST, "ribname"); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } debug_table1->write_separator(); //================================================================ // Test13: adds coming through before we dump the peer //================================================================ //take peer3 down fanout_table->remove_next_table(debug_table3); ribin_table3->ribin_peering_went_down(); debug_table1->write_comment("******************************************"); debug_table1->write_comment("TEST 13"); debug_table1->write_comment("NEW ADD ARRIVES BEFORE PEER IS DUMPED"); debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 1"); debug_table1->write_comment("EXPECT RECEIVED BY PEER 2"); ribin_table1->add_route(net1, fpalist1, pt); debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 2"); debug_table1->write_comment("EXPECT RECEIVED BY PEER 1"); ribin_table2->add_route(net2, fpalist2, pt); debug_table1->write_separator(); debug_table1->write_comment("BRING PEER 3 UP"); debug_table1->write_comment("EXPECT NO CHANGE UNTIL EVENTLOOP RUNS"); ribin_table3->ribin_peering_came_up(); fanout_table->add_next_table(debug_table3, &handler3, ribin_table3->genid()); debug_table3->set_parent(fanout_table); fanout_table->dump_entire_table(debug_table3, SAFI_UNICAST, "ribname"); debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 2"); debug_table1->write_comment("EXPECT RECEIVED BY PEER 1 BUT NOT PEER 3"); ribin_table2->add_route(net3, fpalist3, pt); debug_table1->write_separator(); debug_table1->write_comment("RUN EVENT LOOP TO COMPLETION"); debug_table1->write_comment("EXPECT ADD 1.0.1.0/24 RECEIVED BY PEER 3"); debug_table1->write_comment("EXPECT ADD 1.0.2.0/24 RECEIVED BY PEER 3"); debug_table1->write_comment("EXPECT ADD 1.0.3.0/24 RECEIVED BY PEER 3"); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } debug_table1->write_separator(); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } debug_table1->write_separator(); //delete the routes debug_table1->write_separator(); debug_table1->write_comment("DELETING FROM PEER 2"); debug_table1->write_comment("EXPECT DEL 1.0.2.0/24 RECEIVED BY PEER 1 & 3"); ribin_table2->delete_route(net2); //delete the routes debug_table1->write_separator(); debug_table1->write_comment("DELETING FROM PEER 2"); debug_table1->write_comment("EXPECT DEL 1.0.3.0/24 RECEIVED BY PEER 2 & 3"); ribin_table2->delete_route(net3); //delete the routes debug_table1->write_separator(); debug_table1->write_comment("DELETING FROM PEER 1"); debug_table1->write_comment("EXPECT DEL 1.0.1.0/24 RECEIVED BY PEER 2 & 3"); ribin_table1->delete_route(net1); debug_table1->write_separator(); debug_table1->write_separator(); //================================================================ // Test14: old delete coming through after we dump the peer //================================================================ //take peer3 down fanout_table->remove_next_table(debug_table3); ribin_table3->ribin_peering_went_down(); debug_table1->write_comment("******************************************"); debug_table1->write_comment("TEST 14"); debug_table1->write_comment("OLD DEL AFTER PEER HAS BEEN DUMPED"); debug_table1->write_comment("TEST OLD GENID DELETE ON CURRENT DUMP PEER"); debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 1"); debug_table1->write_comment("EXPECT RECEIVED BY PEER 2"); ribin_table1->add_route(net1, fpalist1, pt); debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 1"); debug_table1->write_comment("EXPECT RECEIVED BY PEER 2"); ribin_table1->add_route(net3, fpalist3, pt); debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 2"); debug_table1->write_comment("EXPECT RECEIVED BY PEER 1"); ribin_table2->add_route(net2, fpalist2, pt); debug_table1->write_separator(); debug_table1->write_comment("TAKE PEER 1 DOWN"); debug_table1->write_comment("DONT RUN EVENTLOOP, SO DELETION IS DELAYED"); debug_table1->write_comment("EXPECT NO CHANGE"); fanout_table->remove_next_table(debug_table1); ribin_table1->ribin_peering_went_down(); uint32_t del_genid = ribin_table1->genid(); debug_table1->write_separator(); debug_table1->write_comment("BRING PEER 1 UP AGAIN, BUT SKIP DUMPING TO IT"); debug_table1->write_comment("EXPECT NO CHANGE"); ribin_table1->ribin_peering_came_up(); fanout_table->add_next_table(debug_table1, &handler1, ribin_table1->genid()); debug_table1->set_parent(fanout_table); debug_table1->write_separator(); debug_table1->write_comment("BRING PEER 3 UP"); debug_table1->write_comment("EXPECT NO CHANGE UNTIL EVENTLOOP RUNS"); ribin_table3->ribin_peering_came_up(); fanout_table->add_next_table(debug_table3, &handler3, ribin_table3->genid()); debug_table3->set_parent(fanout_table); fanout_table->dump_entire_table(debug_table3, SAFI_UNICAST, "ribname"); ((DumpTable*)(debug_table3->parent()))->peering_is_down(&handler1, del_genid); debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 1"); debug_table1->write_comment("EXPECT RECEIVED BY PEER 2"); debug_table1->write_comment("EXPECT NOT RECEIVED BY PEER 2 - PEER NOT YET DUMPED"); ribin_table1->add_route(net4, fpalist4, pt); debug_table1->write_separator(); debug_table1->write_comment("RUN EVENT LOOP TO COMPLETION"); debug_table1->write_comment("EXPECT DEL 1.0.1.0/24 RECEIVED BY PEER 2"); debug_table1->write_comment("EXPECT DEL 1.0.3.0/24 RECEIVED BY PEER 2"); debug_table1->write_comment("EXPECT DEL 1.0.3.0 **NOT** RECEIVED BY PEER 3"); debug_table1->write_comment("EXPECT ADD 1.0.2.0/24 RECEIVED BY PEER 3"); debug_table1->write_comment("EXPECT ADD 1.0.4.0/24 RECEIVED BY PEER 3"); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } debug_table1->write_separator(); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } //delete the routes debug_table1->write_separator(); debug_table1->write_comment("DELETING FROM PEER 2"); debug_table1->write_comment("EXPECT DEL 1.0.2.0/24 RECEIVED BY PEER 1 & 3"); ribin_table2->delete_route(net2); //delete the routes debug_table1->write_separator(); debug_table1->write_comment("DELETING FROM PEER 1"); debug_table1->write_comment("EXPECT DEL 1.0.4.0/24 RECEIVED BY PEER 2 & 3"); ribin_table1->delete_route(net4); debug_table1->write_separator(); //================================================================ // Test15: new route during dump, after peer has been dumped //================================================================ //take peer3 down // printf("TEST 15\n"); fanout_table->remove_next_table(debug_table3); ribin_table3->ribin_peering_went_down(); debug_table1->write_comment("******************************************"); debug_table1->write_comment("TEST 15"); debug_table1->write_comment("ADD AFTER PEER HAS BEEN DUMPED"); debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 1"); debug_table1->write_comment("EXPECT RECEIVED BY PEER 2"); ribin_table1->add_route(net1, fpalist1, pt); debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 2"); debug_table1->write_comment("EXPECT RECEIVED BY PEER 1"); ribin_table2->add_route(net2, fpalist2, pt); debug_table1->write_separator(); debug_table1->write_comment("BRING PEER 3 UP"); debug_table1->write_comment("EXPECT NO CHANGE UNTIL EVENTLOOP RUNS"); ribin_table3->ribin_peering_came_up(); fanout_table->add_next_table(debug_table3, &handler3, ribin_table3->genid()); debug_table3->set_parent(fanout_table); fanout_table->dump_entire_table(debug_table3, SAFI_UNICAST, "ribname"); debug_table1->write_separator(); debug_table1->write_comment("LET EVENT QUEUE DRAIN"); debug_table1->write_comment("EXPECT ADD 1.0.1.0/24 RECEIVED BY PEER 3"); debug_table1->write_comment("EXPECT (MOVES TO DUMPING NEXT PEER)"); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 1"); debug_table1->write_comment("EXPECT RECEIVED BY PEER 2"); debug_table1->write_comment("EXPECT NOT RECEIVED BY PEER 3 YET"); ribin_table1->add_route(net3, fpalist3, pt); debug_table1->write_separator(); debug_table1->write_comment("RUN EVENT LOOP TO COMPLETION"); debug_table1->write_comment("EXPECT ADD 1.0.3.0/24 RECEIVED BY PEER 3"); debug_table1->write_comment("EXPECT ADD 1.0.2.0/24 RECEIVED BY PEER 3"); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } //delete the routes debug_table1->write_separator(); debug_table1->write_comment("DELETING FROM PEER 1"); debug_table1->write_comment("EXPECT DEL 1.0.1.0/24 RECEIVED BY PEER 2 & 3"); ribin_table1->delete_route(net1); //delete the routes debug_table1->write_separator(); debug_table1->write_comment("DELETING FROM PEER 1"); debug_table1->write_comment("EXPECT DEL 1.0.3.0/24 RECEIVED BY PEER 2 & 3"); ribin_table1->delete_route(net3); //delete the routes debug_table1->write_separator(); debug_table1->write_comment("DELETING FROM PEER 2"); debug_table1->write_comment("EXPECT DEL 1.0.2.0/24 RECEIVED BY PEER 1 & 3"); ribin_table2->delete_route(net2); debug_table1->write_separator(); // printf("TEST 15 END\n"); //================================================================ // Test16: new route during dump, after peer with no routes has been dumped //================================================================ //take peer3 down // printf("TEST 16\n"); fanout_table->remove_next_table(debug_table3); ribin_table3->ribin_peering_went_down(); debug_table1->write_comment("******************************************"); debug_table1->write_comment("TEST 16"); debug_table1->write_comment("ADD AFTER PEER WITH NO ROUTES HAS BEEN DUMPED"); debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 2"); debug_table1->write_comment("EXPECT RECEIVED BY PEER 1"); ribin_table2->add_route(net2, fpalist2, pt); debug_table1->write_separator(); debug_table1->write_comment("BRING PEER 3 UP"); debug_table1->write_comment("EXPECT NO CHANGE UNTIL EVENTLOOP RUNS"); ribin_table3->ribin_peering_came_up(); fanout_table->add_next_table(debug_table3, &handler3, ribin_table3->genid()); debug_table3->set_parent(fanout_table); fanout_table->dump_entire_table(debug_table3, SAFI_UNICAST, "ribname"); debug_table1->write_separator(); debug_table1->write_comment("LET EVENT QUEUE DRAIN"); debug_table1->write_comment("NO ROUTES IN 1st PEER (MOVES TO DUMPING NEXT PEER)"); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 1"); debug_table1->write_comment("EXPECT RECEIVED BY PEER 2"); debug_table1->write_comment("EXPECT NOT RECEIVED BY PEER 3 YET"); ribin_table1->add_route(net3, fpalist3, pt); debug_table1->write_separator(); debug_table1->write_comment("RUN EVENT LOOP TO COMPLETION"); debug_table1->write_comment("EXPECT ADD 1.0.3.0/24 RECEIVED BY PEER 3"); debug_table1->write_comment("EXPECT ADD 1.0.2.0/24 RECEIVED BY PEER 3"); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } //delete the routes debug_table1->write_separator(); debug_table1->write_comment("DELETING FROM PEER 1"); debug_table1->write_comment("EXPECT DEL 1.0.3.0/24 RECEIVED BY PEER 2 & 3"); ribin_table1->delete_route(net3); //delete the routes debug_table1->write_separator(); debug_table1->write_comment("DELETING FROM PEER 2"); debug_table1->write_comment("EXPECT DEL 1.0.2.0/24 RECEIVED BY PEER 1 & 3"); ribin_table2->delete_route(net2); debug_table1->write_separator(); // printf("TEST 16 END\n"); //================================================================ // Test17: new route during dump on a new peer //================================================================ // printf("TEST 17\n"); //take peer3 down fanout_table->remove_next_table(debug_table3); ribin_table3->ribin_peering_went_down(); //take peer1 down fanout_table->remove_next_table(debug_table1); ribin_table1->ribin_peering_went_down(); debug_table1->write_comment("******************************************"); debug_table1->write_comment("TEST 17"); debug_table1->write_comment("ADD ON NEW PEER DURING DUMP"); debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 2"); debug_table1->write_comment("EXPECT RECEIVED BY NOONE"); ribin_table2->add_route(net2, fpalist2, pt); debug_table1->write_separator(); debug_table1->write_comment("BRING PEER 3 UP"); debug_table1->write_comment("EXPECT NO CHANGE UNTIL EVENTLOOP RUNS"); ribin_table3->ribin_peering_came_up(); fanout_table->add_next_table(debug_table3, &handler3, ribin_table3->genid()); debug_table3->set_parent(fanout_table); fanout_table->dump_entire_table(debug_table3, SAFI_UNICAST, "ribname"); debug_table1->write_separator(); debug_table1->write_comment("BRING PEER 1 UP"); debug_table1->write_comment("NO NEED TO DUMP TO THIS PEER FOR THIS TEST"); ribin_table1->ribin_peering_came_up(); fanout_table->add_next_table(debug_table1, &handler1, ribin_table1->genid()); debug_table1->set_parent(fanout_table); debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 1"); debug_table1->write_comment("EXPECT RECEIVED BY PEER 2"); debug_table1->write_comment("EXPECT NOT RECEIVED BY PEER 3 YET"); ribin_table1->add_route(net1, fpalist1, pt); debug_table1->write_separator(); debug_table1->write_comment("RUN EVENT LOOP TO COMPLETION"); debug_table1->write_comment("EXPECT ADD 1.0.1.0/24 RECEIVED BY PEER 3"); debug_table1->write_comment("EXPECT ADD 1.0.2.0/24 RECEIVED BY PEER 3"); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } //delete the routes debug_table1->write_separator(); debug_table1->write_comment("DELETING FROM PEER 1"); debug_table1->write_comment("EXPECT DEL 1.0.1.0/24 RECEIVED BY PEER 2 & 3"); ribin_table1->delete_route(net1); //delete the routes debug_table1->write_separator(); debug_table1->write_comment("DELETING FROM PEER 2"); debug_table1->write_comment("EXPECT DEL 1.0.2.0/24 RECEIVED BY PEER 1 & 3"); ribin_table2->delete_route(net2); debug_table1->write_separator(); // printf("TEST 17 END\n"); //================================================================ // Test18: new route during dump on a new peer //================================================================ // printf("TEST 18\n"); //take peer3 down fanout_table->remove_next_table(debug_table3); ribin_table3->ribin_peering_went_down(); debug_table1->write_comment("******************************************"); debug_table1->write_comment("TEST 18"); debug_table1->write_comment("ADD ON NEW PEER DURING DUMP"); debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 1"); debug_table1->write_comment("EXPECT RECEIVED BY PEER 2"); debug_table1->write_comment("EXPECT NOT RECEIVED BY PEER 3 YET"); ribin_table1->add_route(net1, fpalist1, pt); debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 2"); debug_table1->write_comment("EXPECT RECEIVED BY NOONE"); ribin_table2->add_route(net2, fpalist2, pt); debug_table1->write_separator(); //take peer1 down debug_table1->write_comment("TAKE PEER 1 DOWN"); debug_table1->write_comment("EXPECT NO CHANGE UNTIL EVENTLOOP RUNS"); fanout_table->remove_next_table(debug_table1); ribin_table1->ribin_peering_went_down(); del_genid = ribin_table1->genid(); debug_table1->write_separator(); debug_table1->write_comment("BRING PEER 3 UP"); debug_table1->write_comment("EXPECT NO CHANGE UNTIL EVENTLOOP RUNS"); ribin_table3->ribin_peering_came_up(); fanout_table->add_next_table(debug_table3, &handler3, ribin_table3->genid()); debug_table3->set_parent(fanout_table); fanout_table->dump_entire_table(debug_table3, SAFI_UNICAST, "ribname"); ((DumpTable*)(debug_table3->parent()))->peering_is_down(&handler1, del_genid); debug_table1->write_separator(); debug_table1->write_comment("BRING PEER 1 UP"); debug_table1->write_comment("NO NEED TO DUMP TO THIS PEER FOR THIS TEST"); ribin_table1->ribin_peering_came_up(); fanout_table->add_next_table(debug_table1, &handler1, ribin_table1->genid()); debug_table1->set_parent(fanout_table); debug_table1->write_separator(); debug_table1->write_comment("RUN EVENT LOOP TO COMPLETION"); debug_table1->write_comment("EXPECT DEL 1.0.1.0/24 RECEIVED BY PEER 2"); debug_table1->write_comment("EXPECT DEL 1.0.1.0/24 NOT RECEIVED BY PEER 3"); debug_table1->write_comment("EXPECT ADD 1.0.2.0/24 RECEIVED BY PEER 3"); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } //delete the routes debug_table1->write_separator(); debug_table1->write_comment("DELETING FROM PEER 2"); debug_table1->write_comment("EXPECT DEL 1.0.2.0/24 RECEIVED BY PEER 1 & 3"); ribin_table2->delete_route(net2); debug_table1->write_separator(); // printf("TEST 18 END\n"); //================================================================ // Test19: delete during dump on a peer dump table doesn't know about //================================================================ // printf("TEST 19\n"); //take peer3 down fanout_table->remove_next_table(debug_table3); ribin_table3->ribin_peering_went_down(); debug_table1->write_comment("******************************************"); debug_table1->write_comment("TEST 19"); debug_table1->write_comment("DELETE ON PEER THAT DUMP TABLE DOESNT KNOW ABOUT"); debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 1"); debug_table1->write_comment("EXPECT RECEIVED BY PEER 2"); ribin_table1->add_route(net1, fpalist1, pt); debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 1"); debug_table1->write_comment("EXPECT RECEIVED BY PEER 2"); ribin_table1->add_route(net3, fpalist3, pt); debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 2"); debug_table1->write_comment("EXPECT RECEIVED BY PEER 1"); ribin_table2->add_route(net2, fpalist2, pt); debug_table1->write_separator(); //take peer1 down debug_table1->write_comment("TAKE PEER 1 DOWN"); debug_table1->write_comment("EXPECT NO CHANGE UNTIL EVENTLOOP RUNS"); fanout_table->remove_next_table(debug_table1); ribin_table1->ribin_peering_went_down(); del_genid = ribin_table1->genid(); debug_table1->write_separator(); debug_table1->write_comment("BRING PEER 1 UP"); debug_table1->write_comment("NO NEED TO DUMP TO THIS PEER FOR THIS TEST"); ribin_table1->ribin_peering_came_up(); fanout_table->add_next_table(debug_table1, &handler1, ribin_table1->genid()); debug_table1->set_parent(fanout_table); debug_table1->write_separator(); debug_table1->write_comment("BRING PEER 3 UP"); debug_table1->write_comment("EXPECT NO CHANGE UNTIL EVENTLOOP RUNS"); ribin_table3->ribin_peering_came_up(); fanout_table->add_next_table(debug_table3, &handler3, ribin_table3->genid()); debug_table3->set_parent(fanout_table); fanout_table->dump_entire_table(debug_table3, SAFI_UNICAST, "ribname"); ((DumpTable*)(debug_table3->parent()))->peering_is_down(&handler1, del_genid); debug_table1->write_separator(); debug_table1->write_comment("RUN EVENT LOOP TO COMPLETION"); debug_table1->write_comment("EXPECT DEL 1.0.1.0/24 RECEIVED BY PEER 2"); debug_table1->write_comment("EXPECT DEL 1.0.1.0/24 NOT RECEIVED BY PEER 3"); debug_table1->write_comment("EXPECT DEL 1.0.3.0/24 RECEIVED BY PEER 2"); debug_table1->write_comment("EXPECT DEL 1.0.3.0/24 NOT RECEIVED BY PEER 3"); debug_table1->write_comment("EXPECT ADD 1.0.2.0/24 RECEIVED BY PEER 3"); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } //delete the routes debug_table1->write_separator(); debug_table1->write_comment("DELETING FROM PEER 2"); debug_table1->write_comment("EXPECT DEL 1.0.2.0/24 RECEIVED BY PEER 1 & 3"); ribin_table2->delete_route(net2); debug_table1->write_separator(); // printf("TEST 19 END\n"); //================================================================ // Test20: delete during dump on a peer dump table doesn't know about //================================================================ // printf("TEST 20\n"); //take peer3 down fanout_table->remove_next_table(debug_table3); ribin_table3->ribin_peering_went_down(); debug_table1->write_comment("******************************************"); debug_table1->write_comment("TEST 20"); debug_table1->write_comment("DELETE ON PEER THAT DUMP TABLE DOESNT KNOW ABOUT"); debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 1"); debug_table1->write_comment("EXPECT RECEIVED BY PEER 2"); ribin_table1->add_route(net1, fpalist1, pt); debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 1"); debug_table1->write_comment("EXPECT RECEIVED BY PEER 2"); ribin_table1->add_route(net3, fpalist3, pt); debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 1"); debug_table1->write_comment("EXPECT RECEIVED BY PEER 2"); ribin_table1->add_route(net4, fpalist4, pt); debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 2"); debug_table1->write_comment("EXPECT RECEIVED BY PEER 1"); ribin_table2->add_route(net2, fpalist2, pt); debug_table1->write_separator(); //take peer1 down debug_table1->write_comment("TAKE PEER 1 DOWN"); debug_table1->write_comment("EXPECT NO CHANGE UNTIL EVENTLOOP RUNS"); fanout_table->remove_next_table(debug_table1); ribin_table1->ribin_peering_went_down(); del_genid = ribin_table1->genid(); debug_table1->write_separator(); debug_table1->write_comment("BRING PEER 1 UP"); debug_table1->write_comment("NO NEED TO DUMP TO THIS PEER FOR THIS TEST"); ribin_table1->ribin_peering_came_up(); fanout_table->add_next_table(debug_table1, &handler1, ribin_table1->genid()); debug_table1->set_parent(fanout_table); debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 1"); debug_table1->write_comment("EXPECT RECEIVED BY PEER 2"); ribin_table1->add_route(net5, fpalist5, pt); debug_table1->write_separator(); debug_table1->write_comment("BRING PEER 3 UP"); debug_table1->write_comment("EXPECT NO CHANGE UNTIL EVENTLOOP RUNS"); ribin_table3->ribin_peering_came_up(); fanout_table->add_next_table(debug_table3, &handler3, ribin_table3->genid()); debug_table3->set_parent(fanout_table); fanout_table->dump_entire_table(debug_table3, SAFI_UNICAST, "ribname"); ((DumpTable*)(debug_table3->parent()))->peering_is_down(&handler1, del_genid); debug_table1->write_separator(); debug_table1->write_comment("RUN EVENT LOOP TO COMPLETION"); debug_table1->write_comment("EXPECT DEL 1.0.1.0/24 RECEIVED BY PEER 2"); debug_table1->write_comment("EXPECT DEL 1.0.1.0/24 NOT RECEIVED BY PEER 3"); debug_table1->write_comment("EXPECT DEL 1.0.3.0/24 RECEIVED BY PEER 2"); debug_table1->write_comment("EXPECT DEL 1.0.3.0/24 NOT RECEIVED BY PEER 3"); debug_table1->write_comment("EXPECT DEL 1.0.4.0/24 RECEIVED BY PEER 2"); debug_table1->write_comment("EXPECT DEL 1.0.4.0/24 NOT RECEIVED BY PEER 3"); debug_table1->write_comment("EXPECT ADD 1.0.2.0/24 RECEIVED BY PEER 3"); debug_table1->write_comment("EXPECT ADD 1.0.5.0/24 RECEIVED BY PEER 3"); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } //delete the routes debug_table1->write_separator(); debug_table1->write_comment("DELETING FROM PEER 2"); debug_table1->write_comment("EXPECT DEL 1.0.2.0/24 RECEIVED BY PEER 1 & 3"); ribin_table2->delete_route(net2); debug_table1->write_separator(); debug_table1->write_comment("DELETING FROM PEER 1"); debug_table1->write_comment("EXPECT DEL 1.0.5.0/24 RECEIVED BY PEER 2 & 3"); ribin_table1->delete_route(net5); debug_table1->write_separator(); // printf("TEST 20 END\n"); //================================================================ // Test21: add on new rib, after dump went down halfway through // dumping old rib //================================================================ // printf("TEST 21\n"); //take peer3 down fanout_table->remove_next_table(debug_table3); ribin_table3->ribin_peering_went_down(); debug_table1->write_comment("******************************************"); debug_table1->write_comment("TEST 21"); debug_table1->write_comment("ADD ON NEW VERSION OF HALF-DUMPED PEER"); debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 1"); debug_table1->write_comment("EXPECT RECEIVED BY PEER 2"); ribin_table1->add_route(net1, fpalist1, pt); debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 1"); debug_table1->write_comment("EXPECT RECEIVED BY PEER 2"); ribin_table1->add_route(net3, fpalist3, pt); debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 2"); debug_table1->write_comment("EXPECT RECEIVED BY PEER 1"); ribin_table2->add_route(net2, fpalist2, pt); debug_table1->write_separator(); debug_table1->write_comment("BRING PEER 3 UP"); debug_table1->write_comment("EXPECT NO CHANGE UNTIL EVENTLOOP RUNS"); ribin_table3->ribin_peering_came_up(); fanout_table->add_next_table(debug_table3, &handler3, ribin_table3->genid()); debug_table3->set_parent(fanout_table); fanout_table->dump_entire_table(debug_table3, SAFI_UNICAST, "ribname"); debug_table1->write_separator(); debug_table1->write_comment("STEP EVENT LOOP ONCE TO DUMP ONE ROUTE"); debug_table1->write_comment("EXPECT ADD 1.0.1.0/24 RECEIVED BY PEER 3"); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } debug_table1->write_separator(); //take peer1 down debug_table1->write_comment("TAKE PEER 1 DOWN"); debug_table1->write_comment("EXPECT NO CHANGE UNTIL EVENTLOOP RUNS"); fanout_table->remove_next_table(debug_table1); ribin_table1->ribin_peering_went_down(); debug_table1->write_separator(); debug_table1->write_comment("BRING PEER 1 UP"); debug_table1->write_comment("NO NEED TO DUMP TO THIS PEER FOR THIS TEST"); ribin_table1->ribin_peering_came_up(); fanout_table->add_next_table(debug_table1, &handler1, ribin_table1->genid()); debug_table1->set_parent(fanout_table); debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 1"); debug_table1->write_comment("EXPECT ADD 1.0.5.0/24 RECEIVED BY PEER 2"); ribin_table1->add_route(net5, fpalist5, pt); debug_table1->write_separator(); debug_table1->write_comment("RUN EVENT LOOP TO COMPLETION"); debug_table1->write_comment("EXPECT DEL 1.0.1.0/24 RECEIVED BY PEER 2 & 3"); debug_table1->write_comment("EXPECT DEL 1.0.2.0/24 RECEIVED BY PEER 2"); debug_table1->write_comment("EXPECT ADD 1.0.2.0/24 RECEIVED BY PEER 3"); debug_table1->write_comment("EXPECT ADD 1.0.5.0/24 RECEIVED BY PEER 3"); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } //delete the routes debug_table1->write_separator(); debug_table1->write_comment("DELETING FROM PEER 2"); debug_table1->write_comment("EXPECT DEL 1.0.2.0/24 RECEIVED BY PEER 1 & 3"); ribin_table2->delete_route(net2); debug_table1->write_separator(); debug_table1->write_comment("DELETING FROM PEER 1"); debug_table1->write_comment("EXPECT DEL 1.0.5.0/24 RECEIVED BY PEER 2 & 3"); ribin_table1->delete_route(net5); debug_table1->write_separator(); // printf("TEST 21 END\n"); //================================================================ // Test22: two copies of same route - only one should be dumped // dumping old rib //================================================================ //take peer3 down fanout_table->remove_next_table(debug_table3); ribin_table3->ribin_peering_went_down(); debug_table1->write_comment("******************************************"); debug_table1->write_comment("TEST 22"); debug_table1->write_comment("TWO COPIES OF SAME ROUTE"); debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 1"); debug_table1->write_comment("EXPECT RECEIVED BY PEER 2"); ribin_table1->add_route(net1, fpalist1, pt); debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 2"); debug_table1->write_comment("EXPECT NO CHANGE"); ribin_table2->add_route(net1, fpalist2, pt); debug_table1->write_separator(); debug_table1->write_comment("BRING PEER 3 UP"); ribin_table3->ribin_peering_came_up(); fanout_table->add_next_table(debug_table3, &handler3, ribin_table3->genid()); debug_table3->set_parent(fanout_table); fanout_table->dump_entire_table(debug_table3, SAFI_UNICAST, "ribname"); debug_table1->write_comment("EXPECT ADD 1.0.1.0/24 RECEIVED BY PEER 3"); debug_table1->write_comment("**ONLY ONE COPY**"); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } debug_table1->write_separator(); debug_table1->write_comment("DELETING FROM PEER 1"); debug_table1->write_comment("EXPECT DEL 1.0.1.0/24 RECEIVED BY PEER 2"); debug_table1->write_comment("EXPECT REP 1.0.1.0/24 RECEIVED BY PEER 3"); debug_table1->write_comment("EXPECT ADD 1.0.1.0/24 RECEIVED BY PEER 1"); ribin_table1->delete_route(net1); ribin_table1->push(NULL); //delete the routes debug_table1->write_separator(); debug_table1->write_comment("DELETING FROM PEER 2"); debug_table1->write_comment("EXPECT DEL 1.0.1.0/24 RECEIVED BY P1 & 3"); ribin_table2->delete_route(net1); debug_table1->write_separator(); //================================================================ debug_table1->write_comment("SHUTDOWN AND CLEAN UP"); delete decision_table; delete debug_table1; delete debug_table2; delete debug_table3; delete ribin_table1; delete ribin_table2; delete ribin_table3; delete fanout_table; delete cache_table1; delete cache_table2; delete cache_table3; delete nhlookup_table1; delete nhlookup_table2; delete nhlookup_table3; palist1.release(); palist2.release(); palist3.release(); palist4.release(); palist5.release(); fpalist1 = 0; fpalist2 = 0; fpalist3 = 0; fpalist4 = 0; fpalist5 = 0; FILE *file = fopen(filename.c_str(), "r"); if (file == NULL) { fprintf(stderr, "Failed to read %s\n", filename.c_str()); fprintf(stderr, "TEST DUMP FAILED\n"); fclose(file); return false; } #define BUFSIZE 100000 char testout[BUFSIZE]; memset(testout, 0, BUFSIZE); int bytes1 = fread(testout, 1, BUFSIZE, file); if (bytes1 == BUFSIZE) { fprintf(stderr, "Output too long for buffer\n"); fprintf(stderr, "TEST DUMP FAILED\n"); fclose(file); return false; } fclose(file); string ref_filename; const char* srcdir = getenv("srcdir"); if (srcdir) { ref_filename = string(srcdir); } else { ref_filename = "."; } ref_filename += "/test_dump.reference"; file = fopen(ref_filename.c_str(), "r"); if (file == NULL) { fprintf(stderr, "Failed to read %s\n", ref_filename.c_str()); fprintf(stderr, "TEST DUMP FAILED\n"); fclose(file); return false; } char refout[BUFSIZE]; memset(refout, 0, BUFSIZE); int bytes2 = fread(refout, 1, BUFSIZE, file); if (bytes2 == BUFSIZE) { fprintf(stderr, "Output too long for buffer\n"); fprintf(stderr, "TEST DUMP FAILED\n"); fclose(file); return false; } fclose(file); if ((bytes1 != bytes2) || (memcmp(testout, refout, bytes1)!= 0)) { fprintf(stderr, "Output in %s doesn't match reference output\n", filename.c_str()); fprintf(stderr, "TEST DUMP FAILED\n"); return false; } #ifndef HOST_OS_WINDOWS unlink(filename.c_str()); #else DeleteFileA(filename.c_str()); #endif return true; } xorp/bgp/tests/SConscript0000664000076400007640000000424611421137511015606 0ustar greearbgreearb# Copyright (c) 2009 XORP, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # # $XORP$ import os Import("env") env = env.Clone() env.AppendUnique(CPPPATH = [ '#', '$BUILDDIR', '$BUILDDIR/ospf', '$BUILDDIR/ospf/tests', ]) env.AppendUnique(LIBPATH = [ '$BUILDDIR/ospf', '$BUILDDIR/libfeaclient', '$BUILDDIR/policy/backend', '$BUILDDIR/policy/common', '$BUILDDIR/xrl/interfaces', '$BUILDDIR/xrl/targets', '$BUILDDIR/libxipc', '$BUILDDIR/libproto', '$BUILDDIR/libxorp', '$BUILDDIR/libcomm', ]) env.AppendUnique(LIBS = [ 'xorp_ospf', #'xst_ospfv2', #'xst_ospfv3', 'xif_rip', 'xif_finder_event_notifier', 'xorp_fea_client', 'xif_fea_ifmgr_mirror', 'xif_fea_ifmgr_replicator', 'xst_fea_ifmgr_mirror', 'xif_fea_rawpkt4', 'xif_fea_rawpkt6', 'xorp_policy_backend', 'xorp_policy_common', 'xorp_ipc', 'xorp_proto', 'xorp_core', 'xorp_comm', ]) # XXX BGP regression tests are currently stubbed out in # shipping XORP community releases. # XXX test_leaks.sh is a shell script (compound test); # should probably be eliminated in favour of valgrind. simple_cpp_tests = [ 'cache', 'decision', 'deletion', 'dump', 'fanout', 'filter', 'main', 'next_hop_resolver', 'nhlookup', 'packet', 'packet_coding', 'peer_data', 'plumbing', 'policy', 'ribin', 'ribout', 'subnet_route', ] cpp_test_targets = [] #for ct in simple_cpp_tests: # cpp_test_targets.append(env.AutoTest(target = 'test_%s' % ct, # source = 'test_%s.cc' % ct)) xorp/bgp/tests/test_packet_coding.cc0000664000076400007640000006707311421137511017743 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "bgp_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/exceptions.hh" #include "libxorp/test_main.hh" #include "libproto/packet.hh" #include "bgp.hh" #include "local_data.hh" #include "packet.hh" #include "path_attribute.hh" bool test_multprotocol(TestInfo& /*info*/, BGPPeerData* /*peerdata*/) { BGPMultiProtocolCapability multi(AFI_IPV6, SAFI_UNICAST); multi.encode(); assert(8 == multi.length()); BGPMultiProtocolCapability recv(multi.length(), multi.data()); assert(multi.length() == recv.length()); assert(memcmp(multi.data(), recv.data(), recv.length()) == 0); return true; } bool test_multiprotocol_reach_ipv4(TestInfo& /*info*/, BGPPeerData *peerdata) { uint8_t buf[BGPPacket::MAXPACKETSIZE], buf2[BGPPacket::MAXPACKETSIZE]; size_t enc_len = BGPPacket::MAXPACKETSIZE, recv_len = BGPPacket::MAXPACKETSIZE; MPReachNLRIAttribute mpreach(SAFI_MULTICAST); assert(mpreach.optional()); assert(!mpreach.transitive()); assert(mpreach.encode(buf, enc_len, peerdata)); assert(12 == enc_len); mpreach.set_nexthop(IPv4("128.16.0.0")); mpreach.add_nlri(IPNet("128.16.0.0/1")); mpreach.add_nlri(IPNet("128.16.0.0/2")); mpreach.add_nlri(IPNet("128.16.0.0/3")); mpreach.add_nlri(IPNet("128.16.0.0/4")); mpreach.add_nlri(IPNet("128.16.0.0/5")); mpreach.add_nlri(IPNet("128.16.0.0/6")); mpreach.add_nlri(IPNet("128.16.0.0/7")); mpreach.add_nlri(IPNet("128.16.0.0/8")); mpreach.add_nlri(IPNet("128.16.0.0/9")); mpreach.add_nlri(IPNet("128.16.0.0/10")); mpreach.add_nlri(IPNet("128.16.0.0/11")); mpreach.add_nlri(IPNet("128.16.0.0/12")); mpreach.add_nlri(IPNet("128.16.0.0/13")); mpreach.add_nlri(IPNet("128.16.0.0/14")); mpreach.add_nlri(IPNet("128.16.0.0/15")); mpreach.add_nlri(IPNet("128.16.0.0/16")); enc_len = BGPPacket::MAXPACKETSIZE; assert(mpreach.encode(buf, enc_len, peerdata)); assert(52 == enc_len); MPReachNLRIAttribute recv(buf); // we need to re-encode to get the received data as a buffer recv.encode(buf2, recv_len, peerdata); assert(enc_len == recv_len); assert(memcmp(buf, buf2, recv_len) == 0); return true; } bool test_multiprotocol_unreach(TestInfo& /*info*/, BGPPeerData* peerdata) { MPUNReachNLRIAttribute mpunreach(SAFI_UNICAST); uint8_t buf[BGPPacket::MAXPACKETSIZE], buf2[BGPPacket::MAXPACKETSIZE]; size_t enc_len = BGPPacket::MAXPACKETSIZE, recv_len = BGPPacket::MAXPACKETSIZE; assert(mpunreach.optional()); assert(!mpunreach.transitive()); assert(mpunreach.encode(buf, enc_len, peerdata)); assert(6 == enc_len); mpunreach.add_withdrawn(IPNet("2000::/3")); enc_len = BGPPacket::MAXPACKETSIZE; mpunreach.encode(buf, enc_len, peerdata); assert(8 == enc_len); MPUNReachNLRIAttribute recv(buf); // we need to re-encode to get the received data as a buffer assert(recv.encode(buf2, recv_len, peerdata)); assert(enc_len == recv_len); assert(memcmp(buf, buf2, recv_len) == 0); return true; } bool test_refresh(TestInfo& /*info*/, BGPPeerData* /*peerdata*/) { BGPRefreshCapability refresh; refresh.encode(); assert(4 == refresh.length()); BGPRefreshCapability recv(refresh.length(), refresh.data()); assert(refresh.length() == recv.length()); assert(memcmp(refresh.data(), recv.data(), recv.length()) == 0); return true; } bool test_simple_open_packet(TestInfo& /*info*/, BGPPeerData* peerdata) { /* In this test we create an Open Packet, pretend to send it, pretend to receive it, and check that what we sent is what we received */ OpenPacket openpacket(AsNum(666), IPv4("1.2.3.4"), 1234); uint8_t buf[BGPPacket::MAXPACKETSIZE]; size_t len = BGPPacket::MAXPACKETSIZE; assert(openpacket.encode(buf, len, peerdata)); //open packets with no parameters have a fixed length of 29 bytes assert(len == BGPPacket::MINOPENPACKET); //check the common header const uint8_t *skip = buf + BGPPacket::MARKER_SIZE; // skip marker uint16_t plen = extract_16(skip); assert(plen == BGPPacket::MINOPENPACKET); skip += 2; uint8_t type = *skip; assert(type == MESSAGETYPEOPEN); skip++; OpenPacket receivedpacket(buf, plen); assert(receivedpacket.type()==MESSAGETYPEOPEN); //check the information we put in came out again OK. assert(receivedpacket.HoldTime() == 1234); assert(receivedpacket.as() == AsNum(666)); assert(receivedpacket.id() == IPv4("1.2.3.4")); assert(receivedpacket.Version() == 4); #ifdef OPEN_PACKET_OPTIONS //check there are no parameters const list& pl = pd->parameter_list(); assert(pl.begin() == pl.end()); assert(pd->unsupported_parameters() == false); #endif //try encoding the received packet, and check we get the same //encoded packet as when we encoded the constructed packet uint8_t buf2[BGPPacket::MAXPACKETSIZE]; size_t len2 = BGPPacket::MAXPACKETSIZE; assert(receivedpacket.encode(buf2, len2, peerdata)); assert(len == len2); assert(memcmp(buf, buf2, len2) == 0); return true; } bool test_open_packet_with_capabilities(TestInfo& /*info*/, BGPPeerData* peerdata) { /* In this test we create an Open Packet, pretend to send it, pretend to receive it, and check that what we sent is what we received */ OpenPacket openpacket(AsNum(666), IPv4("1.2.3.4"), 1234); // Add a multiprotocol parameter. openpacket.add_parameter( new BGPMultiProtocolCapability(AFI_IPV6, SAFI_UNICAST)); // Add a refresh parameter openpacket.add_parameter( new BGPRefreshCapability()); uint8_t buf[BGPPacket::MAXPACKETSIZE]; size_t len = BGPPacket::MAXPACKETSIZE; assert(openpacket.encode(buf, len, peerdata)); //open packets with no parameters have a fixed length of 29 bytes // +8 for the multiprotocol parameter. // +4 for the refresh parameter. assert(len == BGPPacket::MINOPENPACKET + 8 + 4); //check the common header const uint8_t *skip = buf + BGPPacket::MARKER_SIZE; // skip marker uint16_t plen = extract_16(skip); // +8 for the multiprotocol parameter. // +4 for the refresh parameter. assert(plen == BGPPacket::MINOPENPACKET + 8 + 4); skip += 2; uint8_t type = *skip; assert(type == MESSAGETYPEOPEN); skip++; OpenPacket receivedpacket(buf, plen); assert(receivedpacket.type()==MESSAGETYPEOPEN); //check the information we put in came out again OK. assert(receivedpacket.HoldTime() == 1234); assert(receivedpacket.as() == AsNum(666)); assert(receivedpacket.id() == IPv4("1.2.3.4")); assert(receivedpacket.Version() == 4); //try encoding the received packet, and check we get the same //encoded packet as when we encoded the constructed packet uint8_t buf2[BGPPacket::MAXPACKETSIZE]; size_t len2 = BGPPacket::MAXPACKETSIZE; assert(receivedpacket.encode(buf2, len2, peerdata)); assert(len == len2); assert(memcmp(buf, buf2, len2) == 0); return true; } bool test_keepalive_packet(TestInfo& /*info*/, BGPPeerData* peerdata) { /* In this test we create an Keepalive Packet, pretend to send it, pretend to receive it, and check that what we sent is what we received */ KeepAlivePacket keepalivepacket; uint8_t buf[BGPPacket::MAXPACKETSIZE]; size_t len = BGPPacket::MAXPACKETSIZE; assert(keepalivepacket.encode(buf, len, peerdata)); //keepalive packets with no parameters have a fixed length of 19 bytes assert(len == BGPPacket::COMMON_HEADER_LEN); //check the common header const uint8_t *skip = buf + BGPPacket::MARKER_SIZE; // skip marker uint16_t plen = extract_16(skip); assert(plen == BGPPacket::COMMON_HEADER_LEN); skip+=2; uint8_t type = *skip; assert(type == MESSAGETYPEKEEPALIVE); skip++; KeepAlivePacket receivedpacket(buf, plen); assert(receivedpacket.type()==MESSAGETYPEKEEPALIVE); //try encoding the received packet, and check we get the same //encoded packet as when we encoded the constructed packet uint8_t buf2[BGPPacket::MAXPACKETSIZE]; size_t len2 = BGPPacket::MAXPACKETSIZE; assert(receivedpacket.encode(buf2, len2, peerdata)); assert(len == len2); assert(memcmp(buf, buf2, len2) == 0); return true; } bool test_notification_packets(TestInfo& info, const uint8_t *d, uint8_t ec, uint8_t esc, uint16_t l, BGPPeerData *peerdata) { DOUT(info) << "test_notification_packets\n"; /* In this test we create a Notification Packet, pretend to send it, pretend to receive it, and check that what we sent is what we received */ // We want to test all the constructors for NotificationPacket NotificationPacket *notificationpacket; notificationpacket = new NotificationPacket(ec, esc, d, l); uint8_t buf[BGPPacket::MAXPACKETSIZE]; size_t len = BGPPacket::MAXPACKETSIZE; assert(notificationpacket->encode(buf, len, peerdata)); //notification packets have a length of 21 bytes plus the length //of the error data if (d==NULL) assert(len == BGPPacket::MINNOTIFICATIONPACKET); else assert(len == BGPPacket::MINNOTIFICATIONPACKET + l); //check the common header const uint8_t *skip = buf + BGPPacket::MARKER_SIZE; uint16_t plen = extract_16(skip); if (d == NULL) assert(plen == BGPPacket::MINNOTIFICATIONPACKET); else assert(plen == BGPPacket::MINNOTIFICATIONPACKET + l); skip+=2; uint8_t type = *skip; assert(type == MESSAGETYPENOTIFICATION); skip++; NotificationPacket receivedpacket(buf, plen); assert(receivedpacket.type()==MESSAGETYPENOTIFICATION); assert(receivedpacket.error_code() == ec); assert(receivedpacket.error_subcode() == esc); if (d!=NULL) assert(memcmp(d, receivedpacket.error_data(), l)==0); //try encoding the received packet, and check we get the same //encoded packet as when we encoded the constructed packet uint8_t buf2[BGPPacket::MAXPACKETSIZE]; size_t len2 = BGPPacket::MAXPACKETSIZE; assert(receivedpacket.encode(buf2, len2, peerdata)); assert(len == len2); assert(memcmp(buf, buf2, len2) == 0); delete notificationpacket; return true; } bool test_withdraw_packet(TestInfo& info, BGPPeer* peer) { /* In this test we create an Update Packet, pretend to send it, pretend to receive it, and check that what we sent is what we received */ /* Here we'll create an update packet that only contains withdrawn routes */ UpdatePacket updatepacket; IPv4Net n1("1.2.3.0/24"); IPv4Net n2("1.2.4.0/24"); BGPUpdateAttrib r1(n1); BGPUpdateAttrib r2(n2); updatepacket.add_withdrawn(r1); updatepacket.add_withdrawn(r2); uint8_t buf[BGPPacket::MAXPACKETSIZE]; size_t len = BGPPacket::MAXPACKETSIZE; assert(updatepacket.encode(buf, len, peer->peerdata())); //update packets with have a minumum length of 23 bytes, plus all //the routing information assert(len == 31); //check the common header const uint8_t *skip = buf + BGPPacket::MARKER_SIZE; uint16_t plen = extract_16(skip); assert(plen == 31); skip+=2; uint8_t type = *skip; assert(type == MESSAGETYPEUPDATE); skip++; UpdatePacket receivedpacket(buf, plen, peer->peerdata(), peer->main(), true); assert(receivedpacket.type()==MESSAGETYPEUPDATE); BGPUpdateAttribList::const_iterator iter; iter = receivedpacket.wr_list().begin(); DOUT(info) << "Withdrawn route: " << iter->net().str().c_str() << " n1" << n1.str().c_str() << endl; assert(iter->net() == n1); iter++; DOUT(info) << "Withdrawn route: " << iter->net().str().c_str() << endl; assert(iter->net() == n2); iter++; assert(iter == receivedpacket.wr_list().end()); //try encoding the received packet, and check we get the same //encoded packet as when we encoded the constructed packet uint8_t buf2[BGPPacket::MAXPACKETSIZE]; size_t len2 = BGPPacket::MAXPACKETSIZE; assert(receivedpacket.encode(buf2, len2, peer->peerdata())); assert(len == len2); assert(memcmp(buf, buf2, len2) == 0); return true; } bool test_announce_packet1(TestInfo& info, BGPPeer* peer) { /* In this test we create an Update Packet, pretend to send it, pretend to receive it, and check that what we sent is what we received */ /* Here we'll create an update packet that only contains announce routes */ UpdatePacket updatepacket; FPAList4Ref fpa_list = updatepacket.pa_list(); IPv4Net n1("1.2.3.0/24"); IPv4Net n2("1.2.4.0/24"); BGPUpdateAttrib r1(n1); BGPUpdateAttrib r2(n2); updatepacket.add_nlri(r1); updatepacket.add_nlri(r2); NextHopAttribute nexthop_att(IPv4("10.0.0.1")); fpa_list->add_path_attribute(nexthop_att); AsNum *as[13]; int i; for (i=0;i<=9;i++) { as[i] = new AsNum(i); } ASSegment seq1 = ASSegment(AS_SEQUENCE); seq1.add_as(*(as[1])); seq1.add_as(*(as[2])); seq1.add_as(*(as[3])); ASSegment seq2 = ASSegment(AS_SEQUENCE); seq2.add_as(*(as[7])); seq2.add_as(*(as[8])); seq2.add_as(*(as[9])); ASSegment set1 = ASSegment(AS_SET); set1.add_as(*(as[4])); set1.add_as(*(as[5])); set1.add_as(*(as[6])); ASPath aspath; aspath.add_segment(seq1); aspath.add_segment(set1); aspath.add_segment(seq2); ASPathAttribute aspath_att(aspath); fpa_list->add_path_attribute(aspath_att); OriginAttribute origin_att(IGP); fpa_list->add_path_attribute(origin_att); LocalPrefAttribute local_pref_att(237); fpa_list->add_path_attribute(local_pref_att); MEDAttribute med_att(515); fpa_list->add_path_attribute(med_att); AtomicAggAttribute atomic_agg_att; fpa_list->add_path_attribute(atomic_agg_att); AggregatorAttribute agg_att(IPv4("20.20.20.2"), AsNum(701)); fpa_list->add_path_attribute(agg_att); CommunityAttribute com_att; com_att.add_community(57); com_att.add_community(58); com_att.add_community(59); fpa_list->add_path_attribute(com_att); #if 0 const PathAttribute *pa; PathAttributeList::const_iterator iter; iter = palist.begin(); while(iter != palist.end()) { pa = *iter; updatepacket.add_pathatt(*pa); ++iter; } #endif uint8_t buf[BGPPacket::MAXPACKETSIZE]; size_t len = BGPPacket::MAXPACKETSIZE; assert(updatepacket.encode(buf, len, peer->peerdata())); //update packets with have a minumum length of 23 bytes, plus all //the routing information //23 bytes, plus 8 bytes of NLRI, //+ 4 bytes origin, //+ 7 bytes nexthop //+ (3+3*(2+(2*3))) = 27 bytes Aspath //+ 7 bytes local pref //+ 7 bytes MED //+ 3 bytes Atomic Aggregate //+ 9 bytes Aggregator //+ 15 bytes Community DOUT(info) << "len == " << len << endl; assert(len == 110); //check the common header const uint8_t *skip = buf + BGPPacket::MARKER_SIZE; uint16_t plen = extract_16(skip); assert(plen == 110); skip+=2; uint8_t type = *skip; assert(type == MESSAGETYPEUPDATE); skip++; UpdatePacket receivedpacket(buf, plen, peer->peerdata(), peer->main(), true); assert(receivedpacket.type()==MESSAGETYPEUPDATE); //check there are no withdrawn routes assert(receivedpacket.wr_list().empty()); //check the NLRI BGPUpdateAttribList::const_iterator ni; ni = receivedpacket.nlri_list().begin(); DOUT(info) << "NLRI: " << ni->net().str().c_str() << endl; assert(ni->net() == n1); ni++; DOUT(info) << "NLRI: " << ni->net().str().c_str() << endl; assert(ni->net() == n2); ni++; assert(ni == receivedpacket.nlri_list().end()); //check the path attributes for (int i = 0; i < MAX_ATTRIBUTE; i++) { PathAttribute* pa = receivedpacket.pa_list()->find_attribute_by_type((PathAttType)i); if (pa == 0) continue; switch (pa->type()) { case ORIGIN: { OriginAttribute *oa = (OriginAttribute *)pa; assert(oa->origin() == IGP); DOUT(info) << pa->str().c_str() << endl; break; } case AS_PATH: { ASPathAttribute *asa = (ASPathAttribute *)pa; for (int i=1; i<=9; i++) assert(asa->as_path().contains(*as[i])); DOUT(info) << pa->str().c_str() << endl; break; } case NEXT_HOP: { NextHopAttribute *nha = (NextHopAttribute *)pa; assert(nha->nexthop() == IPv4("10.0.0.1")); DOUT(info) << pa->str().c_str() << endl; break; } case LOCAL_PREF: { LocalPrefAttribute *lpa = (LocalPrefAttribute *)pa; assert(lpa->localpref() == 237); DOUT(info) << pa->str().c_str() << endl; break; } case MED: { MEDAttribute *meda = (MEDAttribute *)pa; assert(meda->med() == 515); DOUT(info) << pa->str().c_str() << endl; break; } case ATOMIC_AGGREGATE: { DOUT(info) << pa->str().c_str() << endl; break; } case AGGREGATOR: { AggregatorAttribute *aa = (AggregatorAttribute *)pa; assert(aa->route_aggregator() == IPv4("20.20.20.2")); assert(aa->aggregator_as() == AsNum(701)); DOUT(info) << pa->str().c_str() << endl; break; } case COMMUNITY: { CommunityAttribute *ca = (CommunityAttribute*)pa; set ::const_iterator iter; iter = ca->community_set().begin(); assert(*iter == 57); ++iter; assert(*iter == 58); ++iter; assert(*iter == 59); ++iter; assert(iter == ca->community_set().end()); DOUT(info) << pa->str().c_str() << endl; break; } default: XLOG_FATAL("Received PathAttribute type: %d\n", pa->type()); } } //try encoding the received packet, and check we get the same //encoded packet as when we encoded the constructed packet uint8_t buf2[BGPPacket::MAXPACKETSIZE]; size_t len2 = BGPPacket::MAXPACKETSIZE; assert(receivedpacket.pa_list()->attribute_count() == 8); assert(receivedpacket.encode(buf2, len2, peer->peerdata())); assert(len == len2); assert(memcmp(buf, buf2, len2) == 0); // clean up for (i=0;i<=9;i++) { delete as[i]; } return true; } bool test_announce_packet2(TestInfo& info, BGPPeer* peer) { /* In this test we create an Update Packet, pretend to send it, pretend to receive it, and check that what we sent is what we received */ /* Here we'll create an update packet that only contains announce routes */ UpdatePacket updatepacket; FPAList4Ref fpa_list = updatepacket.pa_list(); IPv4Net n1("1.2.3.0/24"); IPv4Net n2("0.0.0.0/0"); /* This used to trigger a bug in the decoder */ BGPUpdateAttrib r1(n1); BGPUpdateAttrib r2(n2); updatepacket.add_nlri(r1); updatepacket.add_nlri(r2); NextHopAttribute nexthop_att(IPv4("10.0.0.1")); fpa_list->add_path_attribute(nexthop_att); AsNum *as[13]; int i; for (i=0;i<=9;i++) { as[i] = new AsNum(i); } ASSegment seq1 = ASSegment(AS_SEQUENCE); seq1.add_as(*(as[1])); seq1.add_as(*(as[2])); seq1.add_as(*(as[3])); ASSegment seq2 = ASSegment(AS_SEQUENCE); seq2.add_as(*(as[7])); seq2.add_as(*(as[8])); seq2.add_as(*(as[9])); ASSegment set1 = ASSegment(AS_SET); set1.add_as(*(as[4])); set1.add_as(*(as[5])); set1.add_as(*(as[6])); ASPath aspath; aspath.add_segment(seq1); aspath.add_segment(set1); aspath.add_segment(seq2); ASPathAttribute aspath_att(aspath); fpa_list->add_path_attribute(aspath_att); OriginAttribute origin_att(IGP); fpa_list->add_path_attribute(origin_att); LocalPrefAttribute local_pref_att(237); fpa_list->add_path_attribute(local_pref_att); MEDAttribute med_att(515); fpa_list->add_path_attribute(med_att); AtomicAggAttribute atomic_agg_att; fpa_list->add_path_attribute(atomic_agg_att); AggregatorAttribute agg_att(IPv4("20.20.20.2"), AsNum(701)); fpa_list->add_path_attribute(agg_att); CommunityAttribute com_att; com_att.add_community(57); com_att.add_community(58); com_att.add_community(59); fpa_list->add_path_attribute(com_att); #if 0 const PathAttribute *pa; PathAttributeList::const_iterator iter; iter = palist.begin(); while(iter != palist.end()) { pa = *iter; updatepacket.add_pathatt(*pa); ++iter; } #endif uint8_t buf[BGPPacket::MAXPACKETSIZE]; size_t len = BGPPacket::MAXPACKETSIZE; assert(updatepacket.encode(buf, len, peer->peerdata())); //update packets with have a minumum length of 23 bytes, plus all //the routing information //23 bytes, plus 5 bytes of NLRI, //+ 4 bytes origin, //+ 7 bytes nexthop //+ (3+3*(2+(2*3))) = 27 bytes Aspath //+ 7 bytes local pref //+ 7 bytes MED //+ 3 bytes Atomic Aggregate //+ 9 bytes Aggregator //+ 15 bytes Community DOUT(info) << "len == " << len << endl; assert(len == 107); //check the common header const uint8_t *skip = buf + BGPPacket::MARKER_SIZE; uint16_t plen = extract_16(skip); assert(plen == 107); skip+=2; uint8_t type = *skip; assert(type == MESSAGETYPEUPDATE); skip++; UpdatePacket receivedpacket(buf, plen, peer->peerdata(), peer->main(), true); assert(receivedpacket.type()==MESSAGETYPEUPDATE); //check there are no withdrawn routes assert(receivedpacket.wr_list().empty()); //check the NLRI BGPUpdateAttribList::const_iterator ni; ni = receivedpacket.nlri_list().begin(); DOUT(info) << "NLRI: " << ni->net().str().c_str() << endl; assert(ni->net() == n1); ni++; DOUT(info) << "NLRI: " << ni->net().str().c_str() << endl; assert(ni->net() == n2); ni++; assert(ni == receivedpacket.nlri_list().end()); //check the path attributes //check the path attributes for (int i = 0; i < MAX_ATTRIBUTE; i++) { PathAttribute* pa = receivedpacket.pa_list()->find_attribute_by_type((PathAttType)i); if (pa == 0) continue; switch (pa->type()) { case ORIGIN: { OriginAttribute *oa = (OriginAttribute *)pa; assert(oa->origin() == IGP); DOUT(info) << pa->str().c_str() << endl; break; } case AS_PATH: { ASPathAttribute *asa = (ASPathAttribute *)pa; for (int i=1; i<=9; i++) assert(asa->as_path().contains(*as[i])); DOUT(info) << pa->str().c_str() << endl; break; } case NEXT_HOP: { NextHopAttribute *nha = (NextHopAttribute *)pa; assert(nha->nexthop() == IPv4("10.0.0.1")); DOUT(info) << pa->str().c_str() << endl; break; } case LOCAL_PREF: { LocalPrefAttribute *lpa = (LocalPrefAttribute *)pa; assert(lpa->localpref() == 237); DOUT(info) << pa->str().c_str() << endl; break; } case MED: { MEDAttribute *meda = (MEDAttribute *)pa; assert(meda->med() == 515); DOUT(info) << pa->str().c_str() << endl; break; } case ATOMIC_AGGREGATE: { DOUT(info) << pa->str().c_str() << endl; break; } case AGGREGATOR: { AggregatorAttribute *aa = (AggregatorAttribute *)pa; assert(aa->route_aggregator() == IPv4("20.20.20.2")); assert(aa->aggregator_as() == AsNum(701)); DOUT(info) << pa->str().c_str() << endl; break; } case COMMUNITY: { CommunityAttribute *ca = (CommunityAttribute*)pa; set ::const_iterator iter; iter = ca->community_set().begin(); assert(*iter == 57); ++iter; assert(*iter == 58); ++iter; assert(*iter == 59); ++iter; assert(iter == ca->community_set().end()); DOUT(info) << pa->str().c_str() << endl; break; } default: XLOG_FATAL("Received PathAttribute type: %d\n", pa->type()); } } //try encoding the received packet, and check we get the same //encoded packet as when we encoded the constructed packet uint8_t buf2[BGPPacket::MAXPACKETSIZE]; size_t len2 = BGPPacket::MAXPACKETSIZE; assert(receivedpacket.pa_list()->attribute_count() == 8); assert(receivedpacket.encode(buf2, len2, peer->peerdata())); assert(len == len2); assert(memcmp(buf, buf2, len2) == 0); // clean up for (i=0;i<=9;i++) { delete as[i]; } return true; } int main(int argc, char** argv) { XorpUnexpectedHandler x(xorp_unexpected_handler); xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); TestMain t(argc, argv); EventLoop eventloop; LocalData localdata(eventloop); localdata.set_as(AsNum(0)); // IBGP Iptuple iptuple; BGPPeerData *pd = new BGPPeerData(localdata, iptuple, AsNum(0), IPv4(),0); pd->compute_peer_type(); BGPMain main(eventloop); BGPPeer peer(&localdata, pd, NULL, &main); string test_name = t.get_optional_args("-t", "--test", "run only the specified test"); t.complete_args_parsing(); try { uint8_t edata[2]; edata[0]=1; edata[1]=2; struct test { string test_name; XorpCallback1::RefPtr cb; } tests[] = { {"multiprotocol", callback(test_multprotocol, pd)}, {"multiprotocol_reach_ipv4", callback(test_multiprotocol_reach_ipv4, pd)}, {"multiprotocol_unreach", callback(test_multiprotocol_unreach, pd)}, {"refresh", callback(test_refresh, pd)}, {"simple_open_packet", callback(test_simple_open_packet, pd)}, {"open_packet", callback(test_open_packet_with_capabilities, pd)}, {"keepalive_packet", callback(test_keepalive_packet, pd)}, {"notification_packets1", callback(test_notification_packets, reinterpret_cast(0), static_cast(CEASE), static_cast(0), static_cast(0), pd)}, {"notification_packets2", callback(test_notification_packets, reinterpret_cast(0), static_cast(CEASE), static_cast(1), static_cast(0), pd)}, {"notification_packets3", callback(test_notification_packets, reinterpret_cast(edata), static_cast(MSGHEADERERR), static_cast(2), static_cast(2), pd)}, {"withdraw_packet", callback(test_withdraw_packet, &peer)}, {"announce_packet1", callback(test_announce_packet1, &peer)}, {"announce_packet2", callback(test_announce_packet2, &peer)}, }; if("" == test_name) { for(unsigned int i = 0; i < sizeof(tests) / sizeof(struct test); i++) t.run(tests[i].test_name, tests[i].cb); } else { for(unsigned int i = 0; i < sizeof(tests) / sizeof(struct test); i++) if(test_name == tests[i].test_name) { t.run(tests[i].test_name, tests[i].cb); return t.exit(); } t.failed("No test with name " + test_name + " found\n"); } } catch(...) { xorp_catch_standard_exceptions(); } xlog_stop(); xlog_exit(); return t.exit(); } xorp/bgp/tests/test_plumbing.cc0000664000076400007640000003076411540224220016757 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "bgp_module.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "bgp.hh" #include "test_plumbing.hh" PlumbingTest::PlumbingTest(NextHopResolver& nhr_ipv4, NextHopResolver& nhr_ipv6, PolicyFilters& pfs, BGPMain& bgp) : BGPPlumbing(SAFI_UNICAST, NULL, NULL, nhr_ipv4, nhr_ipv6, pfs, bgp) { } bool PlumbingTest::run_tests(BGPMain& bgpmain) { typedef bool (PlumbingTest::*test)(BGPMain&); test tests[] = {&PlumbingTest::test1, &PlumbingTest::test2}; for(unsigned int i = 0; i < sizeof(tests)/sizeof(test); i++) if(!(this->*tests[i])(bgpmain)) return false; return true; } bool PlumbingTest::test1(BGPMain& bgpmain) { Iptuple iptuple1("127.0.0.1", 179, "1.0.0.1", 179); Iptuple iptuple2("127.0.0.1", 179, "1.0.0.2", 179); IPv4 nh; // EventLoop eventloop; LocalData local_data(bgpmain.eventloop()); local_data.set_as(AsNum(1)); BGPPeerData *peer_data1 = new BGPPeerData(local_data, iptuple1, AsNum(666), nh, 0); // peer_data1->set_as(AsNum(666)); peer_data1->set_id("1.0.0.1"); DummyPeer dummy_peer1(&local_data, peer_data1, 0, (BGPMain *)NULL); printf("Adding Peering 1\n"); //note: creating the peerhandler adds the peering. DummyPeerHandler dummy_peerhandler1("peer1", &dummy_peer1, this, 0); //add_peering(&dummy_peerhandler1); printf("Peering Added.\n"); BGPPeerData *peer_data2 = new BGPPeerData(local_data, iptuple2, AsNum(667), nh, 0); // peer_data2->set_as(AsNum(667)); peer_data2->set_id("1.0.0.2"); DummyPeer dummy_peer2(&local_data, peer_data2, 0, (BGPMain *)NULL); printf("Adding Peering 2\n"); //note: creating the peerhandler adds the peering. DummyPeerHandler dummy_peerhandler2("peer2", &dummy_peer2, this, 0); //add_peering(&dummy_peerhandler2); printf("Peering Added.\n"); PolicyTags pt; printf("------------------------------------------------------\n"); printf("ADD A ROUTE\n"); IPv4 nhaddr("20.20.20.1"); NextHopAttribute nexthop(nhaddr); ASPath as_path; ASSegment as_seq; as_seq.set_type(AS_SEQUENCE); as_seq.add_as(AsNum(666)); as_path.add_segment(as_seq); ASPathAttribute aspathatt(as_path); printf("****>%s<****\n", aspathatt.str().c_str()); OriginAttribute origin(EGP); FPAList4Ref fpalist1 = new FastPathAttributeList(nexthop, aspathatt, origin); PAListRef pathattr1 = new PathAttributeList(fpalist1); IPv4 rtaddr1("10.10.10.0"); IPv4Net net1(rtaddr1, 24); SubnetRoute *route1; InternalMessage *rtm1; printf("Adding Route 1 from peerhandler %p\n", &dummy_peerhandler1); add_route(net1, fpalist1, pt, &dummy_peerhandler1); printf("4 ****>%s<****\n", aspathatt.str().c_str()); printf("Add done\n"); printf("------------------------------------------------------\n"); printf("PUSH A ROUTE\n"); printf("Pushing Route 1\n"); push(&dummy_peerhandler1); printf("Push done\n"); printf("------------------------------------------------------\n"); printf("DELETE THE ROUTE\n"); printf("Deleting Route 1\n"); route1 = new SubnetRoute(net1, pathattr1, NULL); rtm1 = new InternalMessage(route1, &dummy_peerhandler1, GENID_UNKNOWN); rtm1->set_push(); delete_route(*rtm1, &dummy_peerhandler1); printf("Delete done\n"); delete rtm1; route1->unref(); printf("------------------------------------------------------\n"); IPv4 rtaddr2("10.10.20.0"); IPv4Net net2(rtaddr2, 24); SubnetRoute *route2; printf("Adding Route 1\n"); add_route(net1, fpalist1, pt, &dummy_peerhandler1); printf("Add done\n"); InternalMessage *rtm2; printf("Adding Route 2\n"); add_route(net2, fpalist1, pt, &dummy_peerhandler1); printf("Add done\n"); printf("Pushing Routes\n"); push(&dummy_peerhandler1); printf("Push done\n"); printf("------------------------------------------------------\n"); printf("Deleting Route 2\n"); route2 = new SubnetRoute(net2, pathattr1, NULL); rtm2 = new InternalMessage(route2, &dummy_peerhandler1, GENID_UNKNOWN); rtm2->set_push(); delete_route(*rtm2, &dummy_peerhandler1); delete rtm2; route2->unref(); printf("Delete done\n"); printf("------------------------------------------------------\n"); printf("Deleting Route 1\n"); route1 = new SubnetRoute(net1, pathattr1, NULL); rtm1 = new InternalMessage(route1, &dummy_peerhandler1, GENID_UNKNOWN); rtm1->set_push(); delete_route(*rtm1, &dummy_peerhandler1); printf("Delete done\n"); delete rtm1; route1->unref(); printf("------------------------------------------------------\n"); printf("Test1 of decision\n"); printf("Adding Route 1\n"); add_route(net1, fpalist1, pt, &dummy_peerhandler1); printf("Add done\n"); printf("Pushing Routes\n"); push(&dummy_peerhandler1); printf("Push done\n"); IPv4 nhaddr2("20.20.20.2"); NextHopAttribute nexthop2(nhaddr2); FPAList4Ref fpalist2 = new FastPathAttributeList(nexthop2, aspathatt, origin); PAListRef pathattr2 = new PathAttributeList(fpalist2); printf("\n\nAdding Route 2 - this should lose to route 1\n"); add_route(net1, fpalist2, pt, &dummy_peerhandler2); printf("Add done\n"); printf("Pushing Routes\n"); push(&dummy_peerhandler2); printf("Push done\n"); printf("\n\nDeleting Route 2 - shouldn't affect RibOut\n"); route2 = new SubnetRoute(IPv4Net(rtaddr1, 24), pathattr2, NULL); rtm2 = new InternalMessage(route2, &dummy_peerhandler2, GENID_UNKNOWN); delete_route(*rtm2, &dummy_peerhandler2); printf("Delete done\n"); delete rtm2; route2->unref(); printf("Pushing Routes\n"); push(&dummy_peerhandler2); printf("Push done\n"); IPv4 nhaddr3("20.20.19.1"); NextHopAttribute nexthop3(nhaddr3); FPAList4Ref fpalist3 = new FastPathAttributeList(nexthop3, aspathatt, origin); PAListRef pathattr3 = new PathAttributeList(fpalist3); printf("\n\nAdding Route 3 - this should beat route 1\n"); add_route(net1, fpalist3, pt, &dummy_peerhandler2); printf("Add done\n"); printf("Pushing Routes\n"); push(&dummy_peerhandler2); printf("Push done\n"); printf("\n\nDeleting Route 3 - this should cause route 1 to win again\n"); rtm2 = new InternalMessage(route2, &dummy_peerhandler2, GENID_UNKNOWN); delete_route(*rtm2, &dummy_peerhandler2); printf("Delete done\n"); delete rtm2; route2->unref(); printf("Pushing Routes\n"); push(&dummy_peerhandler2); printf("Push done\n"); printf("------------------------------------------------------\n"); printf("ALL TESTS DONE\n\n"); printf("****>%s<****\n", aspathatt.str().c_str()); return true; } bool PlumbingTest::test2(BGPMain& bgpmain) { /* ** 1. Create a single peer (peer1). ** 2. Inject one route. ** 3. Add another peer (peer2). ** 4. Verify that the injected route emerges by inspection. */ /* ** 1. Create a single peer (peer1). */ Iptuple iptuple1("127.0.0.1", 179, "1.0.0.1", 179); Iptuple iptuple2("127.0.0.1", 179, "1.0.0.2", 179); IPv4 nh; // EventLoop eventloop; LocalData local_data(bgpmain.eventloop()); local_data.set_as(AsNum(1)); BGPPeerData *peer_data1 = new BGPPeerData(local_data, iptuple1, AsNum(666), nh, 0); // peer_data1->set_as(AsNum(666)); peer_data1->set_id("1.0.0.1"); DummyPeer dummy_peer1(&local_data, peer_data1, 0, (BGPMain *)NULL); printf("Adding Peering 1\n"); //note: creating the peerhandler adds the peering. DummyPeerHandler dummy_peerhandler1("peer1", &dummy_peer1, this, 0); //add_peering(&dummy_peerhandler1); printf("Peering Added.\n"); /* ** 2. Inject one route. */ printf("------------------------------------------------------\n"); printf("ADD A ROUTE\n"); IPv4 nhaddr("20.20.20.1"); NextHopAttribute nexthop(nhaddr); ASPath as_path; ASSegment as_seq; as_seq.set_type(AS_SEQUENCE); as_seq.add_as(AsNum(666)); as_path.add_segment(as_seq); ASPathAttribute aspathatt(as_path); printf("****>%s<****\n", aspathatt.str().c_str()); OriginAttribute origin(EGP); FPAList4Ref fpalist1 = new FastPathAttributeList(nexthop, aspathatt, origin); // PAListRef pathattr1 = new PathAttributeList(fpalist1); IPv4 rtaddr1("10.10.10.0"); IPv4Net net1(rtaddr1, 24); PolicyTags pt; printf("Adding Route 1 from peerhandler %p\n", &dummy_peerhandler1); add_route(net1, fpalist1, pt, &dummy_peerhandler1); printf("4 ****>%s<****\n", aspathatt.str().c_str()); printf("Add done\n"); printf("------------------------------------------------------\n"); printf("PUSH A ROUTE\n"); printf("Pushing Route 1\n"); push(&dummy_peerhandler1); printf("Push done\n"); /* ** 3. Add another peer (peer2). */ BGPPeerData *peer_data2 = new BGPPeerData(local_data, iptuple2, AsNum(667), nh, 0); peer_data2->set_id("1.0.0.2"); // peer_data2->set_as(AsNum(667)); DummyPeer dummy_peer2(&local_data, peer_data2, 0, (BGPMain *)NULL); printf("Adding Peering 2\n"); //note: creating the peerhandler adds the peering. DummyPeerHandler dummy_peerhandler2("peer2", &dummy_peer2, this, 0); //add_peering(&dummy_peerhandler2); printf("Peering Added.\n"); return true; } DummyPeer::DummyPeer(LocalData *ld, BGPPeerData *pd, SocketClient *sock, BGPMain *m) : BGPPeer(ld, pd, sock, m) { } PeerOutputState DummyPeer::send_update_message(const UpdatePacket& p) { printf("send_update_message %s\n", p.str().c_str()); return PEER_OUTPUT_OK; } DummyPeerHandler::DummyPeerHandler(const string &init_peername, BGPPeer *peer, BGPPlumbing *plumbing_unicast, BGPPlumbing *plumbing_multicast) : PeerHandler(init_peername, peer, plumbing_unicast, plumbing_multicast) { } /* **************** main *********************** */ int main(int /* argc */, char *argv[]) { XorpUnexpectedHandler x(xorp_unexpected_handler); // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); try { // The BGP constructor expects to use the finder, so start one. #ifndef HOST_OS_WINDOWS pid_t pid; switch(pid = fork()) { case 0: execlp("../libxipc/xorp_finder", "xorp_finder", static_cast(NULL)); exit(0); case -1: XLOG_FATAL("unable to exec xorp_finder"); default: break; } #else // HOST_OS_WINDOWS STARTUPINFOA si; PROCESS_INFORMATION pi; GetStartupInfoA(&si); if (CreateProcessA("..\\libxipc\\xorp_finder.exe", NULL, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi) == 0) { XLOG_FATAL("unable to exec xorp_finder"); } #endif // !HOST_OS_WINDOWS EventLoop eventloop; BGPMain bgpm(eventloop); PlumbingTest *tester; tester = (PlumbingTest*)(bgpm.plumbing_unicast()); if (!tester->run_tests(bgpm)) { fprintf(stderr, "Test failed\n"); exit(1); } // Remember to kill the finder. #ifndef HOST_OS_WINDOWS kill(pid, SIGTERM); #else TerminateProcess(pi.hProcess, 1); #endif printf("Tests successful\n"); } catch(...) { xorp_catch_standard_exceptions(); } xlog_stop(); xlog_exit(); return 0; } xorp/bgp/tests/test_fanout.cc0000664000076400007640000006344711540224220016442 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "bgp_module.h" #include "libxorp/xorp.h" #include "libxorp/xorpfd.hh" #include "libxorp/eventloop.hh" #include "libxorp/xlog.h" #include "libxorp/test_main.hh" #ifdef HAVE_PWD_H #include #endif #include "bgp.hh" #include "route_table_base.hh" #include "route_table_filter.hh" #include "route_table_ribin.hh" #include "route_table_debug.hh" #include "path_attribute.hh" #include "local_data.hh" #include "dump_iterators.hh" bool test_fanout(TestInfo& /*info*/) { #ifndef HOST_OS_WINDOWS struct passwd *pwd = getpwuid(getuid()); string filename = "/tmp/test_fanout."; filename += pwd->pw_name; #else char *tmppath = (char *)malloc(256); GetTempPathA(256, tmppath); string filename = string(tmppath) + "test_fanout"; free(tmppath); #endif EventLoop eventloop; BGPMain bgpmain(eventloop); LocalData localdata(bgpmain.eventloop()); Iptuple tuple1("127.0.0.1", 179, "10.0.0.1", 179); Iptuple tuple2("127.0.0.1", 179, "10.0.0.2", 179); Iptuple tuple3("127.0.0.1", 179, "10.0.0.3", 179); BGPPeerData* pd1 = new BGPPeerData(localdata, tuple1, AsNum(1), IPv4("10.0.0.1"), 30); pd1->set_id(IPv4("10.0.0.1")); BGPPeerData* pd2 = new BGPPeerData(localdata, tuple2, AsNum(1), IPv4("10.0.0.2"), 30); pd2->set_id(IPv4("10.0.0.2")); BGPPeerData* pd3 = new BGPPeerData(localdata, tuple3, AsNum(1), IPv4("10.0.0.3"), 30); pd3->set_id(IPv4("10.0.0.3")); BGPPeer peer1(&localdata, pd1, NULL, &bgpmain); PeerHandler handler1("test1", &peer1, NULL, NULL); BGPPeer peer2(&localdata, pd2, NULL, &bgpmain); PeerHandler handler2("test2", &peer2, NULL, NULL); FanoutTable *fanout_table = new FanoutTable("FANOUT", SAFI_UNICAST, NULL, NULL, NULL); DebugTable* debug_table1 = new DebugTable("D1", (BGPRouteTable*)fanout_table); fanout_table->add_next_table(debug_table1, &handler1, 1); DebugTable* debug_table2 = new DebugTable("D2", (BGPRouteTable*)fanout_table); fanout_table->add_next_table(debug_table2, &handler2, 1); debug_table1->set_output_file(filename); debug_table1->set_canned_response(ADD_USED); debug_table1->enable_tablename_printing(); debug_table2->set_output_file(debug_table1->output_file()); debug_table2->set_canned_response(ADD_USED); debug_table2->enable_tablename_printing(); //create a load of attributes IPNet net1("1.0.1.0/24"); IPNet net2("1.0.2.0/24"); // IPNet net2("1.0.2.0/24"); IPv4 nexthop1("2.0.0.1"); NextHopAttribute nhatt1(nexthop1); IPv4 nexthop2("2.0.0.2"); NextHopAttribute nhatt2(nexthop2); IPv4 nexthop3("2.0.0.3"); NextHopAttribute nhatt3(nexthop3); OriginAttribute igp_origin_att(IGP); ASPath aspath1; aspath1.prepend_as(AsNum(1)); aspath1.prepend_as(AsNum(2)); aspath1.prepend_as(AsNum(3)); ASPathAttribute aspathatt1(aspath1); ASPath aspath2; aspath2.prepend_as(AsNum(4)); aspath2.prepend_as(AsNum(5)); aspath2.prepend_as(AsNum(6)); ASPathAttribute aspathatt2(aspath2); ASPath aspath3; aspath3.prepend_as(AsNum(7)); aspath3.prepend_as(AsNum(8)); aspath3.prepend_as(AsNum(9)); ASPathAttribute aspathatt3(aspath3); FPAList4Ref fpalist1 = new FastPathAttributeList(nhatt1, aspathatt1, igp_origin_att); PAListRef palist1 = new PathAttributeList(fpalist1); FPAList4Ref fpalist2 = new FastPathAttributeList(nhatt2, aspathatt2, igp_origin_att); PAListRef palist2 = new PathAttributeList(fpalist2); FPAList4Ref fpalist3 = new FastPathAttributeList(nhatt3, aspathatt3, igp_origin_att); PAListRef palist3 = new PathAttributeList(fpalist3); //create a subnet route SubnetRoute *sr1, *sr2; InternalMessage* msg; //================================================================ //Test1: trivial add and delete //================================================================ //add a route debug_table1->write_comment("******************************************"); debug_table1->write_comment("TEST 1"); debug_table1->write_comment("ADD AND DELETE"); debug_table1->write_comment("SENDING FROM PEER 1"); debug_table1->set_get_on_wakeup(true); debug_table2->set_get_on_wakeup(true); sr1 = new SubnetRoute(net1, palist1, NULL); sr1->set_nexthop_resolved(true); msg = new InternalMessage(sr1, &handler1, 0); fanout_table->add_route(*msg, NULL); fanout_table->push(NULL); debug_table1->write_separator(); //delete the route fanout_table->delete_route(*msg, NULL); fanout_table->push(NULL); delete msg; debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 2"); msg = new InternalMessage(sr1, &handler2, 0); fanout_table->add_route(*msg, NULL); fanout_table->push(NULL); debug_table1->write_separator(); //delete the route fanout_table->delete_route(*msg, NULL); fanout_table->push(NULL); debug_table1->write_separator(); sr1->unref(); delete msg; //================================================================ //Test2: trivial flow control //================================================================ //add a route debug_table1->write_comment("******************************************"); debug_table1->write_comment("TEST 2"); debug_table1->write_comment("SIMPLE FLOW CONTROL - PEER 2 BUSY"); debug_table1->write_comment("SENDING FROM PEER 1"); //set output state on peer 2 to be busy // fanout_table->output_state(true, debug_table2); debug_table2->set_get_on_wakeup(false); sr1 = new SubnetRoute(net1, palist1, NULL); sr1->set_nexthop_resolved(true); msg = new InternalMessage(sr1, &handler1, 0); fanout_table->add_route(*msg, NULL); fanout_table->push(NULL); delete msg; debug_table1->write_separator(); debug_table1->write_comment("PEER 2 GET_NEXT_MESSAGE"); fanout_table->get_next_message(debug_table2); debug_table1->write_separator(); debug_table1->write_comment("PEER 2 GET_NEXT_MESSAGE"); fanout_table->get_next_message(debug_table2); debug_table1->write_separator(); debug_table1->write_comment("PEER 2 GET_NEXT_MESSAGE"); fanout_table->get_next_message(debug_table2); debug_table1->write_separator(); sr1->unref(); //================================================================ //Test2b: trivial flow control //================================================================ //add a route debug_table1->write_comment("******************************************"); debug_table1->write_comment("TEST 2B"); debug_table1->write_comment("SIMPLE FLOW CONTROL - PEER 2 BUSY"); debug_table1->write_comment("SENDING FROM PEER 1"); //set output state on peer 2 to be busy debug_table2->set_get_on_wakeup(false); sr1 = new SubnetRoute(net1, palist1, NULL); sr1->set_nexthop_resolved(true); msg = new InternalMessage(sr1, &handler1, 0); //use the implicit push bit rather than an explicit push msg->set_push(); fanout_table->add_route(*msg, NULL); delete msg; debug_table1->write_separator(); debug_table1->write_comment("PEER 2 GET_NEXT_MESSAGE"); fanout_table->get_next_message(debug_table2); debug_table1->write_separator(); debug_table1->write_comment("PEER 2 GET_NEXT_MESSAGE"); fanout_table->get_next_message(debug_table2); debug_table1->write_separator(); debug_table1->write_comment("PEER 2 GET_NEXT_MESSAGE"); fanout_table->get_next_message(debug_table2); debug_table1->write_separator(); sr1->unref(); //================================================================ //Test3: more complex flow control //================================================================ //add a route debug_table1->write_comment("******************************************"); debug_table1->write_comment("TEST 3"); debug_table1->write_comment("INTERLEAVED FLOW CONTROL - BOTH PEERS BUSY"); debug_table1->write_comment("SENDING FROM PEER 1"); //set output state on both peers to be busy //fanout_table->output_state(true, debug_table1); //fanout_table->output_state(true, debug_table2); debug_table1->set_get_on_wakeup(false); debug_table2->set_get_on_wakeup(false); sr1 = new SubnetRoute(net1, palist1, NULL); sr1->set_nexthop_resolved(true); msg = new InternalMessage(sr1, &handler1, 0); fanout_table->add_route(*msg, NULL); delete msg; msg = new InternalMessage(sr1, &handler1, 0); fanout_table->delete_route(*msg, NULL); delete msg; debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 2"); sr2 = new SubnetRoute(net2, palist2, NULL); sr2->set_nexthop_resolved(true); msg = new InternalMessage(sr2, &handler2, 0); fanout_table->add_route(*msg, NULL); delete msg; msg = new InternalMessage(sr2, &handler2, 0); fanout_table->delete_route(*msg, NULL); delete msg; debug_table1->write_separator(); debug_table1->write_comment("PEER 1 GET_NEXT_MESSAGE"); fanout_table->get_next_message(debug_table1); debug_table1->write_separator(); debug_table1->write_comment("PEER 1 GET_NEXT_MESSAGE"); fanout_table->get_next_message(debug_table1); debug_table1->write_separator(); debug_table1->write_comment("PEER 1 GET_NEXT_MESSAGE"); fanout_table->get_next_message(debug_table1); debug_table1->write_separator(); debug_table1->write_comment("PEER 2 GET_NEXT_MESSAGE"); fanout_table->get_next_message(debug_table2); debug_table1->write_separator(); debug_table1->write_comment("PEER 2 GET_NEXT_MESSAGE"); fanout_table->get_next_message(debug_table2); debug_table1->write_separator(); debug_table1->write_comment("PEER 2 GET_NEXT_MESSAGE"); fanout_table->get_next_message(debug_table2); debug_table1->write_separator(); sr1->unref(); sr2->unref(); //================================================================ //Test4: more complex flow control //================================================================ //add a route debug_table1->write_comment("******************************************"); debug_table1->write_comment("TEST 4"); debug_table1->write_comment("INTERLEAVED FLOW CONTROL - BOTH PEERS BUSY"); debug_table1->write_comment("SENDING FROM PEER 1"); //set output state on both peers to be busy //fanout_table->output_state(true, debug_table1); //fanout_table->output_state(true, debug_table2); debug_table1->set_get_on_wakeup(false); debug_table2->set_get_on_wakeup(false); sr1 = new SubnetRoute(net1, palist1, NULL); sr1->set_nexthop_resolved(true); msg = new InternalMessage(sr1, &handler1, 0); msg->set_push(); fanout_table->add_route(*msg, NULL); delete msg; debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 2"); sr2 = new SubnetRoute(net2, palist2, NULL); sr2->set_nexthop_resolved(true); msg = new InternalMessage(sr2, &handler2, 0); msg->set_push(); fanout_table->add_route(*msg, NULL); delete msg; fanout_table->print_queue(); debug_table1->write_separator(); debug_table1->write_comment("PEER 1 GET_NEXT_MESSAGE"); fanout_table->get_next_message(debug_table1); debug_table1->write_separator(); debug_table1->write_comment("PEER 1 GET_NEXT_MESSAGE"); fanout_table->get_next_message(debug_table1); debug_table1->write_separator(); debug_table1->write_comment("PEER 1 GET_NEXT_MESSAGE"); fanout_table->get_next_message(debug_table1); debug_table1->write_separator(); debug_table1->write_comment("PEER 2 GET_NEXT_MESSAGE"); fanout_table->get_next_message(debug_table2); debug_table1->write_separator(); debug_table1->write_comment("PEER 2 GET_NEXT_MESSAGE"); fanout_table->get_next_message(debug_table2); debug_table1->write_separator(); debug_table1->write_comment("PEER 2 GET_NEXT_MESSAGE"); fanout_table->get_next_message(debug_table2); debug_table1->write_separator(); sr1->unref(); sr2->unref(); //================================================================ //Test5: more complex flow control, three peers //================================================================ //add a route //set output state on both peers to be busy BGPPeer peer3(&localdata, pd3, NULL, &bgpmain); PeerHandler handler3("test3", &peer3, NULL, NULL); DebugTable* debug_table3 = new DebugTable("D3", (BGPRouteTable*)fanout_table); fanout_table->add_next_table(debug_table3, &handler3, 1); //debug_table3->set_output_file(debug_table1->output_file()); debug_table3->set_canned_response(ADD_USED); debug_table3->enable_tablename_printing(); debug_table3->set_output_file(debug_table1->output_file()); debug_table1->write_comment("******************************************"); debug_table1->write_comment("TEST 5"); debug_table1->write_comment("INTERLEAVED FLOW CONTROL - THREE PEERS BUSY"); debug_table1->write_comment("SENDING FROM PEER 1"); //fanout_table->output_state(true, debug_table1); //fanout_table->output_state(true, debug_table2); //fanout_table->output_state(true, debug_table3); sr1 = new SubnetRoute(net1, palist1, NULL); sr1->set_nexthop_resolved(true); msg = new InternalMessage(sr1, &handler1, 0); msg->set_push(); fanout_table->add_route(*msg, NULL); delete msg; debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 2"); sr2 = new SubnetRoute(net2, palist2, NULL); sr2->set_nexthop_resolved(true); msg = new InternalMessage(sr2, &handler2, 0); msg->set_push(); fanout_table->add_route(*msg, NULL); delete msg; fanout_table->print_queue(); debug_table1->write_separator(); debug_table1->write_comment("PEER 1 GET_NEXT_MESSAGE"); fanout_table->get_next_message(debug_table1); debug_table1->write_separator(); debug_table1->write_comment("PEER 1 GET_NEXT_MESSAGE"); fanout_table->get_next_message(debug_table1); debug_table1->write_separator(); debug_table1->write_comment("PEER 1 GET_NEXT_MESSAGE"); fanout_table->get_next_message(debug_table1); debug_table1->write_separator(); debug_table1->write_comment("PEER 2 GET_NEXT_MESSAGE"); fanout_table->get_next_message(debug_table2); debug_table1->write_separator(); debug_table1->write_comment("PEER 2 GET_NEXT_MESSAGE"); fanout_table->get_next_message(debug_table2); debug_table1->write_separator(); debug_table1->write_comment("PEER 2 GET_NEXT_MESSAGE"); fanout_table->get_next_message(debug_table2); debug_table1->write_separator(); debug_table1->write_comment("PEER 3 GET_NEXT_MESSAGE"); fanout_table->get_next_message(debug_table3); debug_table1->write_separator(); debug_table1->write_comment("PEER 3 GET_NEXT_MESSAGE"); fanout_table->get_next_message(debug_table3); debug_table1->write_separator(); debug_table1->write_comment("PEER 3 GET_NEXT_MESSAGE"); fanout_table->get_next_message(debug_table3); debug_table1->write_separator(); debug_table1->write_comment("PEER 3 GET_NEXT_MESSAGE"); fanout_table->get_next_message(debug_table3); debug_table1->write_separator(); debug_table1->write_comment("PEER 3 GET_NEXT_MESSAGE"); fanout_table->get_next_message(debug_table3); debug_table1->write_separator(); sr1->unref(); sr2->unref(); //================================================================ //Test6: test of queuing //================================================================ //add a route debug_table1->write_comment("******************************************"); debug_table1->write_comment("TEST 6"); debug_table1->write_comment("TEST QUEUING"); debug_table1->set_get_on_wakeup(false); debug_table2->set_get_on_wakeup(false); debug_table3->set_get_on_wakeup(false); debug_table1->write_comment("SENDING FROM PEER 1"); sr1 = new SubnetRoute(net1, palist1, NULL); sr1->set_nexthop_resolved(true); msg = new InternalMessage(sr1, &handler1, 0); msg->set_push(); fanout_table->add_route(*msg, NULL); debug_table1->write_separator(); delete msg; debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 2"); sr2 = new SubnetRoute(net2, palist2, NULL); sr2->set_nexthop_resolved(true); msg = new InternalMessage(sr2, &handler2, 0); msg->set_push(); fanout_table->add_route(*msg, NULL); debug_table1->write_separator(); delete msg; debug_table1->write_separator(); debug_table1->write_comment("PEER 3 GET_NEXT_MESSAGE"); debug_table1->write_comment("EXPECT TO GET A ROUTE"); fanout_table->get_next_message(debug_table3); debug_table1->write_separator(); debug_table1->write_comment("PEER 3 GET_NEXT_MESSAGE"); debug_table1->write_comment("EXPECT TO GET A ROUTE"); fanout_table->get_next_message(debug_table3); debug_table1->write_separator(); debug_table1->write_comment("PEER 3 GET_NEXT_MESSAGE"); debug_table1->write_comment("EXPECT TO GET NO ROUTE"); fanout_table->get_next_message(debug_table3); debug_table1->write_separator(); debug_table1->write_comment("PEER 2 GET_NEXT_MESSAGE"); debug_table1->write_comment("EXPECT TO GET A ROUTE"); fanout_table->get_next_message(debug_table2); debug_table1->write_separator(); debug_table1->write_comment("PEER 2 GET_NEXT_MESSAGE"); debug_table1->write_comment("EXPECT TO GET NO ROUTE"); fanout_table->get_next_message(debug_table2); debug_table1->write_separator(); debug_table1->write_comment("PEER 1 SKIP ENTIRE QUEUE"); debug_table1->write_comment("EXPECT TO GET NO ROUTE"); fanout_table->remove_next_table(debug_table1); fanout_table->add_next_table(debug_table1, &handler1, 1); debug_table1->write_separator(); sr1->unref(); sr2->unref(); //================================================================ //Test7: test of queuing //================================================================ //add a route debug_table1->write_comment("******************************************"); debug_table1->write_comment("TEST 7"); debug_table1->write_comment("TEST QUEUING"); debug_table1->set_get_on_wakeup(false); debug_table2->set_get_on_wakeup(false); debug_table3->set_get_on_wakeup(false); debug_table1->write_comment("SENDING FROM PEER 1"); sr1 = new SubnetRoute(net1, palist1, NULL); sr1->set_nexthop_resolved(true); msg = new InternalMessage(sr1, &handler1, 0); msg->set_push(); fanout_table->add_route(*msg, NULL); debug_table1->write_separator(); delete msg; debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 2"); sr2 = new SubnetRoute(net2, palist2, NULL); sr2->set_nexthop_resolved(true); msg = new InternalMessage(sr2, &handler2, 0); msg->set_push(); fanout_table->add_route(*msg, NULL); debug_table1->write_separator(); delete msg; debug_table1->write_separator(); debug_table1->write_comment("PEER 3 GET_NEXT_MESSAGE"); debug_table1->write_comment("EXPECT TO GET A ROUTE"); fanout_table->get_next_message(debug_table3); debug_table1->write_separator(); debug_table1->write_comment("PEER 1 SKIP ENTIRE QUEUE"); debug_table1->write_comment("EXPECT TO GET NO ROUTE"); fanout_table->remove_next_table(debug_table1); fanout_table->add_next_table(debug_table1, &handler1, 1); debug_table1->write_separator(); debug_table1->write_separator(); debug_table1->write_comment("PEER 3 GET_NEXT_MESSAGE"); debug_table1->write_comment("EXPECT TO GET A ROUTE"); fanout_table->get_next_message(debug_table3); debug_table1->write_separator(); debug_table1->write_comment("PEER 3 GET_NEXT_MESSAGE"); debug_table1->write_comment("EXPECT TO GET NO ROUTE"); fanout_table->get_next_message(debug_table3); debug_table1->write_separator(); debug_table1->write_comment("PEER 2 GET_NEXT_MESSAGE"); debug_table1->write_comment("EXPECT TO GET A ROUTE"); fanout_table->get_next_message(debug_table2); debug_table1->write_separator(); debug_table1->write_comment("PEER 2 GET_NEXT_MESSAGE"); debug_table1->write_comment("EXPECT TO GET NO ROUTE"); fanout_table->get_next_message(debug_table2); sr1->unref(); sr2->unref(); //================================================================ //Test8: test of queuing //================================================================ //add a route debug_table1->write_comment("******************************************"); debug_table1->write_comment("TEST 8"); debug_table1->write_comment("TEST QUEUING"); debug_table1->set_get_on_wakeup(false); debug_table2->set_get_on_wakeup(false); debug_table3->set_get_on_wakeup(false); debug_table1->write_separator(); debug_table1->write_comment("SENDING FROM PEER 2"); sr2 = new SubnetRoute(net2, palist2, NULL); sr2->set_nexthop_resolved(true); msg = new InternalMessage(sr2, &handler2, 0); msg->set_push(); fanout_table->add_route(*msg, NULL); debug_table1->write_separator(); delete msg; debug_table1->write_comment("SENDING FROM PEER 1"); sr1 = new SubnetRoute(net1, palist1, NULL); sr1->set_nexthop_resolved(true); msg = new InternalMessage(sr1, &handler1, 0); msg->set_push(); fanout_table->add_route(*msg, NULL); debug_table1->write_separator(); delete msg; debug_table1->write_separator(); debug_table1->write_comment("PEER 3 GET_NEXT_MESSAGE"); debug_table1->write_comment("EXPECT TO GET A ROUTE"); fanout_table->get_next_message(debug_table3); debug_table1->write_separator(); debug_table1->write_comment("PEER 1 SKIP ENTIRE QUEUE"); debug_table1->write_comment("EXPECT TO GET NO ROUTE"); fanout_table->remove_next_table(debug_table1); fanout_table->add_next_table(debug_table1, &handler1, 1); debug_table1->write_separator(); debug_table1->write_separator(); debug_table1->write_comment("PEER 3 GET_NEXT_MESSAGE"); debug_table1->write_comment("EXPECT TO GET A ROUTE"); fanout_table->get_next_message(debug_table3); debug_table1->write_separator(); debug_table1->write_comment("PEER 3 GET_NEXT_MESSAGE"); debug_table1->write_comment("EXPECT TO GET NO ROUTE"); fanout_table->get_next_message(debug_table3); debug_table1->write_separator(); debug_table1->write_comment("PEER 2 GET_NEXT_MESSAGE"); debug_table1->write_comment("EXPECT TO GET A ROUTE"); fanout_table->get_next_message(debug_table2); debug_table1->write_separator(); debug_table1->write_comment("PEER 2 GET_NEXT_MESSAGE"); debug_table1->write_comment("EXPECT TO GET NO ROUTE"); fanout_table->get_next_message(debug_table2); sr1->unref(); sr2->unref(); //================================================================ debug_table1->write_comment("SHUTDOWN AND CLEAN UP"); delete fanout_table; delete debug_table1; delete debug_table2; delete debug_table3; palist1.release(); palist2.release(); palist3.release(); fpalist1 = 0; fpalist2 = 0; fpalist3 = 0; FILE *file = fopen(filename.c_str(), "r"); if (file == NULL) { fprintf(stderr, "Failed to read %s\n", filename.c_str()); fprintf(stderr, "TEST FANOUT FAILED\n"); fclose(file); return false; } #define BUFSIZE 16384 char testout[BUFSIZE]; memset(testout, 0, BUFSIZE); int bytes1 = fread(testout, 1, BUFSIZE, file); if (bytes1 == BUFSIZE) { fprintf(stderr, "Output too long for buffer\n"); fprintf(stderr, "TEST FANOUT FAILED\n"); fclose(file); return false; } fclose(file); string ref_filename; const char* srcdir = getenv("srcdir"); if (srcdir) { ref_filename = string(srcdir); } else { ref_filename = "."; } ref_filename += "/test_fanout.reference"; file = fopen(ref_filename.c_str(), "r"); if (file == NULL) { fprintf(stderr, "Failed to read %s\n", ref_filename.c_str()); fprintf(stderr, "TEST FANOUT FAILED\n"); fclose(file); return false; } char refout[BUFSIZE]; memset(refout, 0, BUFSIZE); int bytes2 = fread(refout, 1, BUFSIZE, file); if (bytes2 == BUFSIZE) { fprintf(stderr, "Output too long for buffer\n"); fprintf(stderr, "TEST FANOUT FAILED\n"); fclose(file); return false; } fclose(file); if ((bytes1 != bytes2) || (memcmp(testout, refout, bytes1)!= 0)) { fprintf(stderr, "Output in %s doesn't match reference output\n", filename.c_str()); fprintf(stderr, "TEST FANOUT FAILED\n"); return false; } #ifndef HOST_OS_WINDOWS unlink(filename.c_str()); #else DeleteFileA(filename.c_str()); #endif return true; } xorp/bgp/tests/test_cache.cc0000664000076400007640000003373311540224220016204 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "bgp_module.h" #include "libxorp/xorp.h" #include "libxorp/xorpfd.hh" #include "libxorp/eventloop.hh" #include "libxorp/xlog.h" #include "libxorp/test_main.hh" #ifdef HAVE_PWD_H #include #endif #include "bgp.hh" #include "route_table_base.hh" #include "route_table_cache.hh" #include "route_table_debug.hh" #include "path_attribute.hh" #include "local_data.hh" #include "dump_iterators.hh" bool test_cache(TestInfo& /*info*/) { #ifndef HOST_OS_WINDOWS struct passwd *pwd = getpwuid(getuid()); string filename = "/tmp/test_cache."; filename += pwd->pw_name; #else char *tmppath = (char *)malloc(256); GetTempPathA(256, tmppath); string filename = string(tmppath) + "test_cache"; free(tmppath); #endif EventLoop eventloop; BGPMain bgpmain(eventloop); LocalData localdata(bgpmain.eventloop()); Iptuple iptuple; BGPPeerData *pd1 = new BGPPeerData(localdata, iptuple, AsNum(0), IPv4(),0); BGPPeer peer1(&localdata, pd1, NULL, &bgpmain); PeerHandler handler1("test1", &peer1, NULL, NULL); BGPPeerData *pd2 = new BGPPeerData(localdata, iptuple, AsNum(0), IPv4(),0); BGPPeer peer2(&localdata, pd2, NULL, &bgpmain); PeerHandler handler2("test2", &peer2, NULL, NULL); #if 0 PAListRef dummy_palist; dummy_palist.create_attribute_manager(); #endif // trivial plumbing CacheTable *cache_table = new CacheTable("CACHE", SAFI_UNICAST, NULL, &handler1); DebugTable* debug_table = new DebugTable("D1", (BGPRouteTable*)cache_table); cache_table->set_next_table(debug_table); assert(cache_table->route_count() == 0); debug_table->set_output_file(filename); debug_table->set_canned_response(ADD_USED); // create a load of attributes IPNet net1("1.0.1.0/24"); IPNet net2("1.0.2.0/24"); IPv4 nexthop1("2.0.0.1"); NextHopAttribute nhatt1(nexthop1); IPv4 nexthop2("2.0.0.2"); NextHopAttribute nhatt2(nexthop2); IPv4 nexthop3("2.0.0.3"); NextHopAttribute nhatt3(nexthop3); OriginAttribute igp_origin_att(IGP); ASPath aspath1; aspath1.prepend_as(AsNum(1)); aspath1.prepend_as(AsNum(2)); aspath1.prepend_as(AsNum(3)); ASPathAttribute aspathatt1(aspath1); ASPath aspath2; aspath2.prepend_as(AsNum(4)); aspath2.prepend_as(AsNum(5)); aspath2.prepend_as(AsNum(6)); ASPathAttribute aspathatt2(aspath2); ASPath aspath3; aspath3.prepend_as(AsNum(7)); aspath3.prepend_as(AsNum(8)); aspath3.prepend_as(AsNum(9)); ASPathAttribute aspathatt3(aspath3); FPAList4Ref fpalist1 = new FastPathAttributeList(nhatt1, aspathatt1, igp_origin_att); PAListRef palist1 = new PathAttributeList(fpalist1); FPAList4Ref fpalist2 = new FastPathAttributeList(nhatt2, aspathatt2, igp_origin_att); PAListRef palist2 = new PathAttributeList(fpalist2); FPAList4Ref fpalist3 = new FastPathAttributeList(nhatt3, aspathatt3, igp_origin_att); PAListRef palist3 = new PathAttributeList(fpalist3); // create a subnet route SubnetRoute *sr1, *sr2; InternalMessage* msg, *msg2; // ================================================================ // Test1: trivial add and delete // ================================================================ // add a route debug_table->write_comment("TEST 1"); debug_table->write_comment("ADD AND DELETE, UNCACHED"); sr1 = new SubnetRoute(net1, palist1, NULL); msg = new InternalMessage(sr1, &handler1, 0); msg->set_push(); cache_table->add_route(*msg, NULL); // we even cache unchanged routes now assert(cache_table->route_count() == 1); debug_table->write_separator(); // delete the route cache_table->delete_route(*msg, NULL); assert(cache_table->route_count() == 0); debug_table->write_separator(); sr1->unref(); delete msg; // ================================================================ // Test1b: trivial add and delete, route cached // ================================================================ // add a route // create a subnet route debug_table->write_comment("TEST 1a"); debug_table->write_comment("ADD AND DELETE WITH CACHING."); debug_table->write_comment("ADD, CACHED"); sr1 = new SubnetRoute(net1, palist1, NULL); msg = new InternalMessage(sr1, &handler1, 0); msg->set_changed(); msg->set_push(); cache_table->add_route(*msg, NULL); // note that as the route has changed, the cache table is // responsible for deleting the route in the message delete msg; sr1->unref(); // verify that this route was now cached assert(cache_table->route_count() == 1); debug_table->write_separator(); debug_table->write_comment("DELETE, CACHED"); // delete the route sr1 = new SubnetRoute(net1, palist1, NULL); msg = new InternalMessage(sr1, &handler1, 0); msg->set_push(); cache_table->delete_route(*msg, NULL); // note that as the route has changed, the cache table is // responsible for deleting the route in the message delete msg; sr1->unref(); // verify that this route was now deleted from the cache assert(cache_table->route_count() == 0); debug_table->write_separator(); #if 0 Test no longer makes sense now we cache all data // ================================================================ // Test2: trivial replace // ================================================================ // add a route debug_table->write_comment("TEST 2"); debug_table->write_comment("REPLACE ROUTE, NOT CACHED"); sr1 = new SubnetRoute(net1, palist1, NULL); msg = new InternalMessage(sr1, &handler1, 0); sr2 = new SubnetRoute(net1, palist3, NULL); msg2 = new InternalMessage(sr2, &handler1, 0); msg2->set_push(); cache_table->replace_route(*msg, *msg2, NULL); assert(cache_table->route_count() == 0); debug_table->write_separator(); delete msg; delete msg2; sr1->unref(); sr2->unref(); // ================================================================ // Test2a: trivial replace, original route cacheed // ================================================================ // add a route debug_table->write_comment("TEST 2a"); debug_table->write_comment("REPLACE ROUTE, OLD ROUTE CACHEED"); sr1 = new SubnetRoute(net1, palist2, NULL); msg = new InternalMessage(sr1, &handler1, 0); msg->set_changed(); cache_table->add_route(*msg, NULL); // note that as the route has changed, the cache table is // responsible for deleting the route in the message delete msg; assert(cache_table->route_count() == 1); sr1 = new SubnetRoute(net1, palist2, NULL); msg = new InternalMessage(sr1, &handler1, 0); msg->set_changed(); sr2 = new SubnetRoute(net1, palist1, NULL); msg2 = new InternalMessage(sr2, &handler1, 0); msg2->set_push(); cache_table->replace_route(*msg, *msg2, NULL); assert(cache_table->route_count() == 0); debug_table->write_separator(); delete msg; delete msg2; sr2->unref(); // ================================================================ // Test2b: trivial replace, new route to be cached // ================================================================ assert(cache_table->route_count() == 0); debug_table->write_comment("TEST 2b"); debug_table->write_comment("REPLACE ROUTE, NEW ROUTE TO BE CACHED"); sr1 = new SubnetRoute(net1, palist1, NULL); msg = new InternalMessage(sr1, &handler1, 0); sr2 = new SubnetRoute(net1, palist2, NULL); msg2 = new InternalMessage(sr2, &handler1, 0); msg2->set_changed(); msg2->set_push(); cache_table->replace_route(*msg, *msg2, NULL); // verify that the new route was cached assert(cache_table->route_count() == 1); debug_table->write_separator(); delete msg; delete msg2; sr1->unref(); // delete the route debug_table->write_comment("CLEANUP"); sr2 = new SubnetRoute(net1, palist2, NULL); msg = new InternalMessage(sr2, &handler1, 0); msg->set_changed(); cache_table->delete_route(*msg, NULL); delete msg; assert(cache_table->route_count() == 0); debug_table->write_separator(); #endif // ================================================================ // Test2c: trivial replace, both routes cacheed // ================================================================ debug_table->write_comment("TEST 2c"); debug_table->write_comment("REPLACE ROUTE, BOTH ROUTES CACHED"); assert(cache_table->route_count() == 0); sr1 = new SubnetRoute(net1, palist1, NULL); msg = new InternalMessage(sr1, &handler1, 0); msg->set_changed(); debug_table->write_comment("ADD ROUTE TO CACHE"); cache_table->add_route(*msg, NULL); // assert(msg->route() == NULL); sr1->unref(); delete msg; assert(cache_table->route_count() == 1); sr1 = new SubnetRoute(net1, palist1, NULL); msg = new InternalMessage(sr1, &handler1, 0); msg->set_changed(); sr2 = new SubnetRoute(net1, palist2, NULL); msg2 = new InternalMessage(sr2, &handler1, 0); msg2->set_changed(); msg2->set_push(); debug_table->write_comment("REPLACE ROUTE"); cache_table->replace_route(*msg, *msg2, NULL); // verify that the new route was cached assert(cache_table->route_count() == 1); debug_table->write_separator(); // assert(msg->route() == NULL); // assert(msg2->route() == NULL); sr1->unref(); sr2->unref(); delete msg; delete msg2; // delete the route sr2 = new SubnetRoute(net1, palist2, NULL); msg = new InternalMessage(sr2, &handler1, 0); msg->set_changed(); debug_table->write_comment("CLEANUP"); debug_table->write_comment("DELETE ROUTE FROM CACHE"); cache_table->delete_route(*msg, NULL); delete msg; sr2->unref(); assert(cache_table->route_count() == 0); debug_table->write_separator(); // ================================================================ // Test3: flush cache // ================================================================ // add a route debug_table->write_comment("TEST 3"); debug_table->write_comment("FLUSH_CACHE"); assert(cache_table->route_count() == 0); sr1 = new SubnetRoute(net1, palist1, NULL); msg = new InternalMessage(sr1, &handler1, 0); msg->set_changed(); cache_table->add_route(*msg, NULL); // assert(msg->route() == NULL); sr1->unref(); delete msg; sr1 = new SubnetRoute(net2, palist2, NULL); msg = new InternalMessage(sr1, &handler1, 0); msg->set_changed(); cache_table->add_route(*msg, NULL); // assert(msg->route() == NULL); sr1->unref(); delete msg; assert(cache_table->route_count() == 2); cache_table->flush_cache(); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } assert(cache_table->route_count() == 0); // ================================================================ // Check debug output against reference // ================================================================ debug_table->write_separator(); debug_table->write_comment("SHUTDOWN AND CLEAN UP"); delete cache_table; delete debug_table; palist1.release(); palist2.release(); palist3.release(); fpalist1 = 0; fpalist2 = 0; fpalist3 = 0; FILE *file = fopen(filename.c_str(), "r"); if (file == NULL) { fprintf(stderr, "Failed to read %s\n", filename.c_str()); fprintf(stderr, "TEST CACHE FAILED\n"); fclose(file); return false; } #define BUFSIZE 8192 char testout[BUFSIZE]; memset(testout, 0, BUFSIZE); int bytes1 = fread(testout, 1, BUFSIZE, file); if (bytes1 == BUFSIZE) { fprintf(stderr, "Output too long for buffer\n"); fprintf(stderr, "TEST CACHE FAILED\n"); fclose(file); return false; } fclose(file); string ref_filename; const char* srcdir = getenv("srcdir"); if (srcdir) { ref_filename = string(srcdir); } else { ref_filename = "."; } ref_filename += "/test_cache.reference"; file = fopen(ref_filename.c_str(), "r"); if (file == NULL) { fprintf(stderr, "Failed to read %s\n", ref_filename.c_str()); fprintf(stderr, "TEST CACHE FAILED\n"); fclose(file); return false; } char refout[BUFSIZE]; memset(refout, 0, BUFSIZE); int bytes2 = fread(refout, 1, BUFSIZE, file); if (bytes2 == BUFSIZE) { fprintf(stderr, "Output too long for buffer\n"); fprintf(stderr, "TEST CACHE FAILED\n"); fclose(file); return false; } fclose(file); if ((bytes1 != bytes2) || (memcmp(testout, refout, bytes1) != 0)) { fprintf(stderr, "Output in %s doesn't match reference output\n", filename.c_str()); fprintf(stderr, "TEST CACHE FAILED\n"); return false; } #ifndef HOST_OS_WINDOWS unlink(filename.c_str()); #else DeleteFileA(filename.c_str()); #endif return true; } xorp/bgp/tests/test_next_hop_resolver.hh0000664000076400007640000000411311421137511020712 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/bgp/test_next_hop_resolver.hh,v 1.10 2008/10/02 21:56:22 bms Exp $ #ifndef __BGP_TEST_NEXT_HOP_RESOLVER_HH__ #define __BGP_TEST_NEXT_HOP_RESOLVER_HH__ #include "libxorp/test_main.hh" #include "libxorp/ipv4.hh" #include "libxorp/ipv4net.hh" #include "libxorp/ipv6.hh" #include "libxorp/ipv6net.hh" template bool nhr_test1(TestInfo& info, A nexthop, A real_nexthop, IPNet subnet); template bool nhr_test2(TestInfo& info, A nexthop, A real_nexthop, IPNet subnet, int reg); template bool nhr_test3(TestInfo& info, A nexthop, A real_nexthop, IPNet subnet, int reg); template bool nhr_test4(TestInfo& info, A nexthop, A real_nexthop, IPNet subnet); template bool nhr_test5(TestInfo& info, A nexthop, A real_nexthop, IPNet subnet); template bool nhr_test6(TestInfo& info, A nexthop, A real_nexthop, IPNet subnet); template bool nhr_test7(TestInfo& info, A nexthop, A real_nexthop, IPNet subnet); template bool nhr_test8(TestInfo& info, A nexthop, A real_nexthop, IPNet subnet); template bool nhr_test9(TestInfo& info, A nexthop, A real_nexthop, IPNet subnet, int reg); #endif // __BGP_TEST_NEXT_HOP_RESOLVER_HH__ xorp/bgp/tests/test_ribin_dump.reference0000664000076400007640000000403411421137511020636 0ustar greearbgreearb[comment] TEST 1 [ADD] SubnetRoute: Net: 10.0.0.0/8 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [ADD] SubnetRoute: Net: 30.0.0.0/8 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [DUMP] SubnetRoute: Net: 10.0.0.0/8 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [comment] The dump has started now add a new route it should appear once we start dumping again [ADD] SubnetRoute: Net: 20.0.0.0/8 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [DUMP] SubnetRoute: Net: 20.0.0.0/8 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [DUMP] SubnetRoute: Net: 30.0.0.0/8 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [comment] USE A NEW ITERATOR to prove 20.0.0.0/8 is present [DUMP] SubnetRoute: Net: 10.0.0.0/8 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [DUMP] SubnetRoute: Net: 20.0.0.0/8 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [DUMP] SubnetRoute: Net: 30.0.0.0/8 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [comment] Delete all the routes from the RIB-IN so the memory can be free'd [DELETE] SubnetRoute: Net: 10.0.0.0/8 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [DELETE] SubnetRoute: Net: 30.0.0.0/8 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [DELETE] SubnetRoute: Net: 20.0.0.0/8 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] xorp/bgp/tests/test_policy_export.reference0000664000076400007640000000267411421137511021416 0ustar greearbgreearb[comment] TEST 1 [comment] ADD AND DELETE UNFILTERED [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [separator]------------------------------------- [comment] TEST 2 [comment] ADD, CONFIG, DELETE [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [comment] CONFIGURE FILTER [comment] EXPECT DELETE TO NOT HAVE LOCALPREF [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [separator]------------------------------------- [comment] TEST 3 [comment] ADD AND DELETE FILTERED [comment] EXPECT ROUTES TO HAVE LOCALPREF [ADD] CHANGED flag is set SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 200 [DELETE] CHANGED flag is set SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 200 [separator]------------------------------------- [comment] SHUTDOWN AND CLEAN UP xorp/bgp/tests/test_main.cc0000664000076400007640000001525311540224220016062 0ustar greearbgreearb // -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "bgp_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "test_next_hop_resolver.hh" bool test_ribin(TestInfo& info); bool test_ribin_dump(TestInfo& info); bool test_deletion(TestInfo& info); bool test_filter(TestInfo& info); bool test_policy(TestInfo& info); bool test_policy_export(TestInfo& info); bool test_policy_dump(TestInfo& info); bool test_cache(TestInfo& info); bool test_nhlookup(TestInfo& info); bool test_decision(TestInfo& info); bool test_fanout(TestInfo& info); bool test_dump_create(TestInfo& info); bool test_dump(TestInfo& info); bool test_ribout(TestInfo& info); template bool test_subnet_route1(TestInfo& info, IPNet net); template bool test_subnet_route2(TestInfo& info, IPNet net); bool validate_reference_file(string reference_file, string output_file, string testname) { FILE *file = fopen(output_file.c_str(), "r"); if (file == NULL) { fprintf(stderr, "Failed to read %s\n", output_file.c_str()); fprintf(stderr, "TEST %s FAILED\n", testname.c_str()); fclose(file); return false; } #define BUFSIZE 20000 char testout[BUFSIZE]; memset(testout, 0, BUFSIZE); int bytes1 = fread(testout, 1, BUFSIZE, file); if (bytes1 == BUFSIZE) { fprintf(stderr, "Output too long for buffer\n"); fprintf(stderr, "TEST %s FAILED\n", testname.c_str()); fclose(file); return false; } fclose(file); string ref_filename; const char* srcdir = getenv("srcdir"); if (srcdir) { ref_filename = string(srcdir); } else { ref_filename = "."; } ref_filename += reference_file; file = fopen(ref_filename.c_str(), "r"); if (file == NULL) { fprintf(stderr, "Failed to read %s\n", ref_filename.c_str()); fprintf(stderr, "TEST %s FAILED\n", testname.c_str()); return false; } char refout[BUFSIZE]; memset(refout, 0, BUFSIZE); int bytes2 = fread(refout, 1, BUFSIZE, file); if (bytes2 == BUFSIZE) { fprintf(stderr, "Output too long for buffer\n"); fprintf(stderr, "TEST %s FAILED\n", testname.c_str()); fclose(file); return false; } fclose(file); if ((bytes1 != bytes2) || (memcmp(testout, refout, bytes1)!= 0)) { fprintf(stderr, "Output in %s doesn't match reference output\n", output_file.c_str()); fprintf(stderr, "TEST %s FAILED\n", testname.c_str()); return false; } #ifdef HOST_OS_WINDOWS DeleteFileA(output_file.c_str()); #else unlink(output_file.c_str()); #endif return true; } int main(int argc, char** argv) { XorpUnexpectedHandler x(xorp_unexpected_handler); xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); TestMain t(argc, argv); string test_name = t.get_optional_args("-t", "--test", "run only the specified test"); t.complete_args_parsing(); try { /* ** For next hop resolver tests. */ IPv4 nh4("128.16.64.1"); IPv4 rnh4("1.1.1.1"); IPv4Net nlri4("22.0.0.0/8"); IPv6 nh6("::128.16.64.1"); IPv6 rnh6("::1.1.1.1"); IPv6Net nlri6("::22.0.0.0/8"); IPv4Net route4("128.16.0.0/24"); IPv6Net route6("1::0/24"); const int iter = 1000; struct test { string test_name; XorpCallback1::RefPtr cb; } tests[] = { {"RibIn", callback(test_ribin)}, {"RibInDump", callback(test_ribin_dump)}, {"Deletion", callback(test_deletion)}, {"Filter", callback(test_filter)}, {"PolicyExport", callback(test_policy_export)}, {"Policy", callback(test_policy)}, {"PolicyDump", callback(test_policy_dump)}, {"Cache", callback(test_cache)}, {"NhLookup", callback(test_nhlookup)}, {"Decision", callback(test_decision)}, {"Fanout", callback(test_fanout)}, {"DumpCreate", callback(test_dump_create)}, {"Dump", callback(test_dump)}, {"Ribout", callback(test_ribout)}, {"SubnetRoute1", callback(test_subnet_route1, route4)}, {"SubnetRoute1.ipv6", callback(test_subnet_route1, route6)}, {"SubnetRoute2", callback(test_subnet_route2, route4)}, {"SubnetRoute2.ipv6", callback(test_subnet_route2, route6)}, {"nhr.test1", callback(nhr_test1, nh4, rnh4, nlri4)}, {"nhr.test1.ipv6", callback(nhr_test1, nh6, rnh6, nlri6)}, {"nhr.test2", callback(nhr_test2, nh4, rnh4, nlri4, iter)}, {"nhr.test2.ipv6", callback(nhr_test2, nh6, rnh6, nlri6, iter)}, {"nhr.test3", callback(nhr_test3, nh4, rnh4, nlri4, iter)}, {"nhr.test3.ipv6", callback(nhr_test3, nh6, rnh6, nlri6, iter)}, {"nhr.test4", callback(nhr_test4, nh4, rnh4, nlri4)}, {"nhr.test4.ipv6", callback(nhr_test4, nh6, rnh6, nlri6)}, {"nhr.test5", callback(nhr_test5, nh4, rnh4, nlri4)}, {"nhr.test5.ipv6", callback(nhr_test5, nh6, rnh6, nlri6)}, {"nhr.test6", callback(nhr_test6, nh4, rnh4, nlri4)}, {"nhr.test6.ipv6", callback(nhr_test6, nh6, rnh6, nlri6)}, {"nhr.test7", callback(nhr_test7, nh4, rnh4, nlri4)}, {"nhr.test7.ipv6", callback(nhr_test7, nh6, rnh6, nlri6)}, {"nhr.test8", callback(nhr_test8, nh4, rnh4, nlri4)}, {"nhr.test8.ipv6", callback(nhr_test8, nh6, rnh6, nlri6)}, {"nhr.test9", callback(nhr_test9, nh4, rnh4, nlri4, iter)}, {"nhr.test9.ipv6", callback(nhr_test9, nh6, rnh6, nlri6, iter)}, }; if("" == test_name) { for(unsigned int i = 0; i < sizeof(tests) / sizeof(struct test); i++) t.run(tests[i].test_name, tests[i].cb); } else { for(unsigned int i = 0; i < sizeof(tests) / sizeof(struct test); i++) if(test_name == tests[i].test_name) { t.run(tests[i].test_name, tests[i].cb); return t.exit(); } t.failed("No test with name " + test_name + " found\n"); } } catch(...) { xorp_catch_standard_exceptions(); } xlog_stop(); xlog_exit(); return t.exit(); } xorp/bgp/tests/test_filter.reference0000664000076400007640000001354511421137511020002 0ustar greearbgreearb[comment] TEST 1 [comment] ADD AND DELETE UNFILTERED [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [separator]------------------------------------- [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [separator]------------------------------------- [comment] TEST 1a [comment] ADD AND DELETE FILTERING [comment] ADD, FILTERED [separator]------------------------------------- [comment] DELETE, FILTERED [separator]------------------------------------- [comment] TEST 2 [comment] REPLACE ROUTE [REPLACE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] [separator]------------------------------------- [comment] TEST 2a [comment] REPLACE ROUTE, OLD ROUTE FILTERED [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [separator]------------------------------------- [comment] TEST 2b [comment] REPLACE ROUTE, NEW ROUTE FILTERED [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [separator]------------------------------------- [comment] TEST 2c [comment] REPLACE ROUTE, BOTH ROUTES FILTERED [separator]------------------------------------- [comment] TEST 3 [comment] ROUTE DUMP [DUMP] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [separator]------------------------------------- [comment] TEST 3a [comment] ROUTE_DUMP, FILTERED [separator]------------------------------------- [comment] TEST 4 [comment] ADD, LOOKUP AND DELETE UNFILTERED [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [separator]------------------------------------- [comment] LOOKUP ROUTE [comment] TEST SUCCESSFUL [separator]------------------------------------- [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [separator]------------------------------------- [comment] TEST 4a [comment] ADD, LOOKUP AND DELETE FILTERED [comment] ADD, SHOULD BE FILTERED [separator]------------------------------------- [comment] LOOKUP ROUTE [comment] TEST SUCCESSFUL [separator]------------------------------------- [comment] DELETE, SHOULD BE FILTERED [separator]------------------------------------- [comment] TEST 5 [comment] ADD AND DELETE [ADD] CHANGED flag is set SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [DELETE] CHANGED flag is set SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] TEST 6 [comment] ADD AND DELETE [ADD] CHANGED flag is set SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 128.16.64.4 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [DELETE] CHANGED flag is set SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 128.16.64.4 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] TEST 7 [comment] ADD AND DELETE [comment] EXPECT CHANGE TO PROPAGATE [ADD] CHANGED flag is set SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 128.16.64.4 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [DELETE] CHANGED flag is set SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 128.16.64.4 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] TEST 7b [comment] ADD AND DELETE [comment] EXPECT CHANGE NOT TO PROPAGATE [separator]------------------------------------- [separator]------------------------------------- [comment] TEST 8a [comment] ADD [comment] EXPECT AS TEST 6 [ADD] CHANGED flag is set SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 128.16.64.4 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] RECONFIGURE FILTER [comment] TEST 8b [comment] ADD AND DELETE [comment] EXPECT ROUTE UNMODIFIED [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [separator]------------------------------------- [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [separator]------------------------------------- [comment] TEST 8c [comment] DELETE [comment] EXPECT AS TEST 6 [DELETE] CHANGED flag is set SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 128.16.64.4 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] SHUTDOWN AND CLEAN UP xorp/bgp/tests/test_cache.reference0000664000076400007640000000444311421137511017555 0ustar greearbgreearb[comment] TEST 1 [comment] ADD AND DELETE, UNCACHED [ADD] PUSH flag is set SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [separator]------------------------------------- [DELETE] PUSH flag is set SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [separator]------------------------------------- [comment] TEST 1a [comment] ADD AND DELETE WITH CACHING. [comment] ADD, CACHED [ADD] PUSH flag is set SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [separator]------------------------------------- [comment] DELETE, CACHED [DELETE] PUSH flag is set SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [separator]------------------------------------- [comment] TEST 2c [comment] REPLACE ROUTE, BOTH ROUTES CACHED [comment] ADD ROUTE TO CACHE [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [comment] REPLACE ROUTE [REPLACE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] PUSH flag is set SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/5, AS/4] [separator]------------------------------------- [comment] CLEANUP [comment] DELETE ROUTE FROM CACHE [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/5, AS/4] [separator]------------------------------------- [comment] TEST 3 [comment] FLUSH_CACHE [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [ADD] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/5, AS/4] [separator]------------------------------------- [comment] SHUTDOWN AND CLEAN UP xorp/bgp/tests/test_dump.reference0000664000076400007640000024336611421137511017470 0ustar greearbgreearb[comment] ****************************************** [comment] TEST 1 [comment] ADD AND DELETE [comment] SENDING FROM PEER 1 [** TABLE DebugTable-D2 **] [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [** TABLE DebugTable-D2 **] [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [** TABLE DebugTable-D1 **] [PUSH] [** TABLE DebugTable-D2 **] [PUSH] [comment] ****************************************** [comment] TEST 2 [comment] SIMPLE DUMP TO PEER 3 [separator]------------------------------------- [comment] SENDING FROM PEER 1 [** TABLE DebugTable-D2 **] [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [** TABLE DebugTable-D1 **] [ADD] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [separator]------------------------------------- [comment] BRING PEER 3 UP [separator]------------------------------------- [comment] LET EVENT QUEUE DRAIN [** TABLE DebugTable-D3 **] [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [PUSH] [** TABLE DebugTable-D3 **] [ADD] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [PUSH] [separator]------------------------------------- [comment] DELETING FROM PEER 1 [** TABLE DebugTable-D2 **] [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] DELETING FROM PEER 2 [** TABLE DebugTable-D1 **] [DELETE] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [DELETE] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [separator]------------------------------------- [** TABLE DebugTable-D1 **] [PUSH] [** TABLE DebugTable-D2 **] [PUSH] [comment] ****************************************** [comment] TEST 3 [comment] SIMPLE DUMP TO PEER 3 [separator]------------------------------------- [comment] SENDING FROM PEER 1 [** TABLE DebugTable-D2 **] [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 1 [** TABLE DebugTable-D2 **] [ADD] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [** TABLE DebugTable-D1 **] [ADD] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [** TABLE DebugTable-D1 **] [ADD] SubnetRoute: Net: 1.0.4.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [separator]------------------------------------- [comment] BRING PEER 3 UP [separator]------------------------------------- [comment] LET EVENT QUEUE DRAIN [** TABLE DebugTable-D3 **] [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [PUSH] [** TABLE DebugTable-D3 **] [ADD] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [PUSH] [** TABLE DebugTable-D3 **] [ADD] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [PUSH] [** TABLE DebugTable-D3 **] [ADD] SubnetRoute: Net: 1.0.4.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [PUSH] [separator]------------------------------------- [comment] DELETING FROM PEER 1 [** TABLE DebugTable-D2 **] [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] DELETING FROM PEER 1 [** TABLE DebugTable-D2 **] [DELETE] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [DELETE] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [separator]------------------------------------- [comment] DELETING FROM PEER 2 [** TABLE DebugTable-D1 **] [DELETE] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [DELETE] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [separator]------------------------------------- [comment] DELETING FROM PEER 2 [** TABLE DebugTable-D1 **] [DELETE] SubnetRoute: Net: 1.0.4.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [DELETE] SubnetRoute: Net: 1.0.4.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [separator]------------------------------------- [** TABLE DebugTable-D1 **] [PUSH] [** TABLE DebugTable-D2 **] [PUSH] [comment] ****************************************** [comment] TEST 4 [comment] SIMPLE DUMP TO PEER 3, PEER 2 GOES DOWN [separator]------------------------------------- [comment] SENDING FROM PEER 1 [** TABLE DebugTable-D2 **] [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 1 [** TABLE DebugTable-D2 **] [ADD] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [** TABLE DebugTable-D1 **] [ADD] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [** TABLE DebugTable-D1 **] [ADD] SubnetRoute: Net: 1.0.4.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [separator]------------------------------------- [comment] BRING PEER 3 UP [separator]------------------------------------- [comment] PEER 2 GOES DOWN [comment] XXXX [** TABLE DebugTable-D1 **] [PUSH] [** TABLE DebugTable-D3 **] [PUSH] [separator]------------------------------------- [comment] LET EVENT QUEUE DRAIN [** TABLE DebugTable-D3 **] [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [PUSH] [** TABLE DebugTable-D3 **] [ADD] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [PUSH] [** TABLE DebugTable-D1 **] [DELETE] SubnetRoute: Net: 1.0.4.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [** TABLE DebugTable-D1 **] [DELETE] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [** TABLE DebugTable-D1 **] [PUSH] [** TABLE DebugTable-D3 **] [PUSH] [separator]------------------------------------- [comment] DELETING FROM PEER 1 [** TABLE DebugTable-D3 **] [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] DELETING FROM PEER 1 [** TABLE DebugTable-D3 **] [DELETE] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [separator]------------------------------------- [comment] BRING PEER 2 UP [separator]------------------------------------- [** TABLE DebugTable-D1 **] [PUSH] [** TABLE DebugTable-D2 **] [PUSH] [comment] ****************************************** [comment] TEST 5 [comment] DUMP TO PEER 3, PEER 2 GOES DOWN DURING P2 DUMP [separator]------------------------------------- [comment] SENDING FROM PEER 1 [comment] EXPECT RECEIVED BY PEER 2 [** TABLE DebugTable-D2 **] [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [comment] EXPECT RECEIVED BY PEER 1 [** TABLE DebugTable-D1 **] [ADD] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [comment] EXPECT RECEIVED BY PEER 1 [** TABLE DebugTable-D1 **] [ADD] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [separator]------------------------------------- [comment] BRING PEER 3 UP [separator]------------------------------------- [comment] LET EVENT QUEUE DRAIN [comment] EXPECT ADD 1.0.1.0/24 RECEIVED BY PEER 3 [comment] EXPECT ADD 1.0.2.0/24 RECEIVED BY PEER 3 [** TABLE DebugTable-D3 **] [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [PUSH] [** TABLE DebugTable-D3 **] [ADD] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [PUSH] [** TABLE DebugTable-D3 **] [ADD] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [PUSH] [separator]------------------------------------- [comment] PEER 2 GOES DOWN [** TABLE DebugTable-D1 **] [PUSH] [** TABLE DebugTable-D3 **] [PUSH] [separator]------------------------------------- [comment] LET EVENT QUEUE DRAIN [comment] EXPECT DEL 1.0.2.0/24 RECEIVED BY PEER 1 [comment] EXPECT DEL 1.0.2.0/24 RECEIVED BY PEER 3 [comment] EXPECT DEL 1.0.3.0/24 RECEIVED BY PEER 1 [** TABLE DebugTable-D1 **] [DELETE] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [DELETE] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [** TABLE DebugTable-D1 **] [PUSH] [** TABLE DebugTable-D3 **] [PUSH] [** TABLE DebugTable-D1 **] [DELETE] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [DELETE] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [** TABLE DebugTable-D1 **] [PUSH] [** TABLE DebugTable-D3 **] [PUSH] [separator]------------------------------------- [comment] DELETING FROM PEER 1 [comment] EXPECT DEL 1.0.1.0/24 RECEIVED BY PEER 3 [** TABLE DebugTable-D3 **] [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] BRING PEER 2 UP [comment] EXPECT NO CHANGE [separator]------------------------------------- [** TABLE DebugTable-D1 **] [PUSH] [** TABLE DebugTable-D2 **] [PUSH] [comment] ****************************************** [comment] TEST 6 [comment] DUMP TO PEER 3, PEER 1 GOES DOWN [separator]------------------------------------- [comment] SENDING FROM PEER 1 [comment] EXPECT RECEIVED BY PEER 2 [** TABLE DebugTable-D2 **] [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [comment] EXPECT RECEIVED BY PEER 1 [** TABLE DebugTable-D1 **] [ADD] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [separator]------------------------------------- [comment] BRING PEER 3 UP [comment] EXPECT NO IMMEDIATE CHANGE [separator]------------------------------------- [comment] PEER 1 GOES DOWN [comment] EXPECT NO IMMEDIATE CHANGE [** TABLE DebugTable-D2 **] [PUSH] [** TABLE DebugTable-D3 **] [PUSH] [separator]------------------------------------- [comment] LET EVENT QUEUE DRAIN [comment] EXPECT ADD 1.0.2.0/24 RECEIVED BY PEER 3 [comment] EXPECT DEL 1.0.1.0/24 RECEIVED BY PEER 2 [** TABLE DebugTable-D3 **] [ADD] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [PUSH] [** TABLE DebugTable-D2 **] [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [** TABLE DebugTable-D2 **] [PUSH] [** TABLE DebugTable-D3 **] [PUSH] [separator]------------------------------------- [comment] DELETING FROM PEER 2 [comment] EXPECT DEL 1.0.2.0/24 RECEIVED BY PEER 3 [** TABLE DebugTable-D3 **] [DELETE] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [separator]------------------------------------- [comment] BRING PEER 1 UP [comment] EXPECT NO CHANGE [separator]------------------------------------- [** TABLE DebugTable-D1 **] [PUSH] [** TABLE DebugTable-D2 **] [PUSH] [comment] ****************************************** [comment] TEST 7 [comment] DUMP TO PEER 3, PEER 2 GOES DOWN BETWEEN P1 AND P2 DUMPS [separator]------------------------------------- [comment] SENDING FROM PEER 1 [comment] EXPECT RECEIVED BY PEER 2 [** TABLE DebugTable-D2 **] [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [comment] EXPECT RECEIVED BY PEER 1 [** TABLE DebugTable-D1 **] [ADD] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [comment] EXPECT RECEIVED BY PEER 1 [** TABLE DebugTable-D1 **] [ADD] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [separator]------------------------------------- [comment] BRING PEER 3 UP [separator]------------------------------------- [comment] LET EVENT QUEUE DRAIN [comment] EXPECT ADD 1.0.1.0/24 RECEIVED BY PEER 3 [** TABLE DebugTable-D3 **] [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [PUSH] [** TABLE DebugTable-D3 **] [ADD] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [PUSH] [** TABLE DebugTable-D3 **] [ADD] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [PUSH] [separator]------------------------------------- [comment] PEER 2 GOES DOWN [** TABLE DebugTable-D1 **] [PUSH] [** TABLE DebugTable-D3 **] [PUSH] [separator]------------------------------------- [comment] LET EVENT QUEUE DRAIN [comment] EXPECT DEL 1.0.2.0/24 RECEIVED BY PEER 1 [comment] EXPECT DEL 1.0.3.0/24 RECEIVED BY PEER 1 [** TABLE DebugTable-D1 **] [DELETE] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [DELETE] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [** TABLE DebugTable-D1 **] [PUSH] [** TABLE DebugTable-D3 **] [PUSH] [** TABLE DebugTable-D1 **] [DELETE] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [DELETE] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [** TABLE DebugTable-D1 **] [PUSH] [** TABLE DebugTable-D3 **] [PUSH] [separator]------------------------------------- [comment] DELETING FROM PEER 1 [comment] EXPECT DEL 1.0.1.0/24 RECEIVED BY PEER 3 [** TABLE DebugTable-D3 **] [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] BRING PEER 2 UP [comment] EXPECT NO CHANGE [separator]------------------------------------- [** TABLE DebugTable-D1 **] [PUSH] [** TABLE DebugTable-D2 **] [PUSH] [comment] ****************************************** [comment] TEST 8 [comment] DUMP TO PEER 3, PEER 2 GOES DOWN BEFORE P1 DUMP [separator]------------------------------------- [comment] SENDING FROM PEER 1 [comment] EXPECT RECEIVED BY PEER 2 [** TABLE DebugTable-D2 **] [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [comment] EXPECT RECEIVED BY PEER 1 [** TABLE DebugTable-D1 **] [ADD] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [comment] EXPECT RECEIVED BY PEER 1 [** TABLE DebugTable-D1 **] [ADD] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [separator]------------------------------------- [comment] BRING PEER 3 UP [separator]------------------------------------- [comment] PEER 2 GOES DOWN [** TABLE DebugTable-D1 **] [PUSH] [** TABLE DebugTable-D3 **] [PUSH] [separator]------------------------------------- [comment] LET EVENT QUEUE DRAIN [comment] EXPECT ADD 1.0.1.0/24 RECEIVED BY PEER 3 [comment] EXPECT DEL 1.0.2.0/24 RECEIVED BY PEER 1 [comment] EXPECT DEL 1.0.3.0/24 RECEIVED BY PEER 1 [** TABLE DebugTable-D3 **] [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [PUSH] [** TABLE DebugTable-D1 **] [DELETE] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [** TABLE DebugTable-D1 **] [PUSH] [** TABLE DebugTable-D3 **] [PUSH] [** TABLE DebugTable-D1 **] [DELETE] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [** TABLE DebugTable-D1 **] [PUSH] [** TABLE DebugTable-D3 **] [PUSH] [separator]------------------------------------- [comment] DELETING FROM PEER 1 [comment] EXPECT DEL 1.0.1.0/24 RECEIVED BY PEER 3 [** TABLE DebugTable-D3 **] [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] BRING PEER 2 UP [comment] EXPECT NO CHANGE [separator]------------------------------------- [** TABLE DebugTable-D1 **] [PUSH] [** TABLE DebugTable-D2 **] [PUSH] [comment] ****************************************** [comment] TEST 9 [comment] DUMP TO PEER 3, P1 AND P2 GO DOWN BEFORE FIRST DUMP [separator]------------------------------------- [comment] SENDING FROM PEER 1 [comment] EXPECT RECEIVED BY PEER 2 [** TABLE DebugTable-D2 **] [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [comment] EXPECT RECEIVED BY PEER 1 [** TABLE DebugTable-D1 **] [ADD] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [comment] EXPECT RECEIVED BY PEER 1 [** TABLE DebugTable-D1 **] [ADD] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [separator]------------------------------------- [comment] BRING PEER 3 UP [separator]------------------------------------- [comment] PEER 2 GOES DOWN [** TABLE DebugTable-D1 **] [PUSH] [** TABLE DebugTable-D3 **] [PUSH] [separator]------------------------------------- [comment] PEER 1 GOES DOWN [** TABLE DebugTable-D3 **] [PUSH] [separator]------------------------------------- [comment] LET EVENT QUEUE DRAIN [comment] EXPECT NO CHANGE [** TABLE DebugTable-D3 **] [PUSH] [** TABLE DebugTable-D3 **] [PUSH] [** TABLE DebugTable-D3 **] [PUSH] [separator]------------------------------------- [comment] BRING PEER 2 UP [comment] EXPECT NO CHANGE [separator]------------------------------------- [comment] BRING PEER 1 UP [comment] EXPECT NO CHANGE [separator]------------------------------------- [** TABLE DebugTable-D1 **] [PUSH] [** TABLE DebugTable-D2 **] [PUSH] [comment] ****************************************** [comment] TEST 10 [comment] DUMP TO PEER 3, SEND NEW ROUTES [separator]------------------------------------- [comment] SENDING FROM PEER 1 [comment] EXPECT RECEIVED BY PEER 2 [** TABLE DebugTable-D2 **] [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [comment] EXPECT RECEIVED BY PEER 1 [** TABLE DebugTable-D1 **] [ADD] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [comment] EXPECT RECEIVED BY PEER 1 [** TABLE DebugTable-D1 **] [ADD] SubnetRoute: Net: 1.0.4.0/24 PAList: Next Hop Attribute 2.0.0.4 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [separator]------------------------------------- [comment] BRING PEER 3 UP [separator]------------------------------------- [comment] SENDING FROM PEER 1 [comment] EXPECT RECEIVED BY PEER 2 [** TABLE DebugTable-D2 **] [ADD] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [separator]------------------------------------- [comment] LET EVENT QUEUE DRAIN [comment] EXPECT ADD 1.0.1.0/24 RECEIVED BY PEER 3 [comment] EXPECT ADD 1.0.2.0/24 RECEIVED BY PEER 3 [comment] EXPECT ADD 1.0.3.0/24 RECEIVED BY PEER 3 [** TABLE DebugTable-D3 **] [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [PUSH] [** TABLE DebugTable-D3 **] [ADD] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [PUSH] [** TABLE DebugTable-D3 **] [ADD] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [PUSH] [** TABLE DebugTable-D3 **] [ADD] SubnetRoute: Net: 1.0.4.0/24 PAList: Next Hop Attribute 2.0.0.4 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [PUSH] [separator]------------------------------------- [comment] SENDING FROM PEER 2 [comment] EXPECT RECEIVED BY PEER 1 [comment] EXPECT RECEIVED BY PEER 3 WHEN EVENTLOOP RUNS [** TABLE DebugTable-D1 **] [ADD] SubnetRoute: Net: 1.0.0.0/24 PAList: Next Hop Attribute 2.0.0.5 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [ADD] SubnetRoute: Net: 1.0.0.0/24 PAList: Next Hop Attribute 2.0.0.5 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [comment] EXPECT RECEIVED BY PEER 1 [** TABLE DebugTable-D1 **] [ADD] SubnetRoute: Net: 1.0.5.0/24 PAList: Next Hop Attribute 2.0.0.5 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [ADD] SubnetRoute: Net: 1.0.5.0/24 PAList: Next Hop Attribute 2.0.0.5 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [separator]------------------------------------- [comment] LET EVENT QUEUE DRAIN [comment] EXPECT ADD 1.0.4.0/24 RECEIVED BY PEER 3 [comment] EXPECT ADD 1.0.5.0/24 RECEIVED BY PEER 3 [separator]------------------------------------- [comment] DELETING FROM PEER 1 [comment] EXPECT DEL 1.0.1.0/24 RECEIVED BY PEER 2 [comment] EXPECT DEL 1.0.1.0/24 RECEIVED BY PEER 3 [** TABLE DebugTable-D2 **] [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] DELETING FROM PEER 1 [comment] EXPECT DEL 1.0.2.0/24 RECEIVED BY PEER 2 [comment] EXPECT DEL 1.0.2.0/24 RECEIVED BY PEER 3 [** TABLE DebugTable-D2 **] [DELETE] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [DELETE] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [separator]------------------------------------- [comment] DELETING FROM PEER 2 [comment] EXPECT DEL 1.0.3.0/24 RECEIVED BY PEER 1 [comment] EXPECT DEL 1.0.3.0/24 RECEIVED BY PEER 3 [** TABLE DebugTable-D1 **] [DELETE] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [DELETE] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [separator]------------------------------------- [comment] DELETING FROM PEER 2 [comment] EXPECT DEL 1.0.4.0/24 RECEIVED BY PEER 1 [comment] EXPECT DEL 1.0.4.0/24 RECEIVED BY PEER 3 [** TABLE DebugTable-D1 **] [DELETE] SubnetRoute: Net: 1.0.4.0/24 PAList: Next Hop Attribute 2.0.0.4 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [DELETE] SubnetRoute: Net: 1.0.4.0/24 PAList: Next Hop Attribute 2.0.0.4 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [separator]------------------------------------- [comment] DELETING FROM PEER 2 [comment] EXPECT DEL 1.0.0.0/24 RECEIVED BY PEER 1 [comment] EXPECT DEL 1.0.0.0/24 RECEIVED BY PEER 3 [** TABLE DebugTable-D1 **] [DELETE] SubnetRoute: Net: 1.0.0.0/24 PAList: Next Hop Attribute 2.0.0.5 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [DELETE] SubnetRoute: Net: 1.0.0.0/24 PAList: Next Hop Attribute 2.0.0.5 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [separator]------------------------------------- [comment] DELETING FROM PEER 2 [comment] EXPECT DEL 1.0.5.0/24 RECEIVED BY PEER 1 [comment] EXPECT DEL 1.0.5.0/24 RECEIVED BY PEER 3 [** TABLE DebugTable-D1 **] [DELETE] SubnetRoute: Net: 1.0.5.0/24 PAList: Next Hop Attribute 2.0.0.5 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [DELETE] SubnetRoute: Net: 1.0.5.0/24 PAList: Next Hop Attribute 2.0.0.5 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [separator]------------------------------------- [** TABLE DebugTable-D1 **] [PUSH] [** TABLE DebugTable-D2 **] [PUSH] [comment] ****************************************** [comment] TEST 11 [comment] DUMP TO PEER 3, REPLACE ROUTES [separator]------------------------------------- [comment] SENDING FROM PEER 1 [comment] EXPECT RECEIVED BY PEER 2 [** TABLE DebugTable-D2 **] [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [comment] EXPECT RECEIVED BY PEER 1 [** TABLE DebugTable-D1 **] [ADD] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [comment] EXPECT RECEIVED BY PEER 1 [** TABLE DebugTable-D1 **] [ADD] SubnetRoute: Net: 1.0.4.0/24 PAList: Next Hop Attribute 2.0.0.4 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [separator]------------------------------------- [comment] BRING PEER 3 UP [separator]------------------------------------- [comment] SENDING FROM PEER 1 [comment] EXPECT REPLACE AT PEER 2 [** TABLE DebugTable-D2 **] [REPLACE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.5 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [separator]------------------------------------- [comment] LET EVENT QUEUE DRAIN [comment] EXPECT ADD 1.0.1.0/24 RECEIVED BY PEER 3 [comment] EXPECT ADD 1.0.3.0/24 RECEIVED BY PEER 3 [** TABLE DebugTable-D3 **] [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.5 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [PUSH] [** TABLE DebugTable-D3 **] [ADD] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [PUSH] [** TABLE DebugTable-D3 **] [ADD] SubnetRoute: Net: 1.0.4.0/24 PAList: Next Hop Attribute 2.0.0.4 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [PUSH] [separator]------------------------------------- [comment] SENDING FROM PEER 2 [comment] EXPECT REPLACE AT PEER 1 [comment] EXPECT REPLACE AT PEER 3 WHEN EVENTLOOP RUNS [** TABLE DebugTable-D1 **] [REPLACE] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.5 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [REPLACE] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.5 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [comment] EXPECT REPLACE AT PEER 1 [** TABLE DebugTable-D1 **] [REPLACE] SubnetRoute: Net: 1.0.4.0/24 PAList: Next Hop Attribute 2.0.0.4 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 SubnetRoute: Net: 1.0.4.0/24 PAList: Next Hop Attribute 2.0.0.5 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [REPLACE] SubnetRoute: Net: 1.0.4.0/24 PAList: Next Hop Attribute 2.0.0.4 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 SubnetRoute: Net: 1.0.4.0/24 PAList: Next Hop Attribute 2.0.0.5 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [separator]------------------------------------- [comment] LET EVENT QUEUE DRAIN [comment] EXPECT REPLACE 1.0.3.0/24 RECEIVED BY PEER 3 [comment] EXPECT ADD 1.0.4.0/24 RECEIVED BY PEER 3 [separator]------------------------------------- [comment] DELETING FROM PEER 1 [comment] EXPECT DEL 1.0.1.0/24 RECEIVED BY PEER 2 [comment] EXPECT DEL 1.0.1.0/24 RECEIVED BY PEER 3 [** TABLE DebugTable-D2 **] [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.5 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.5 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [separator]------------------------------------- [comment] DELETING FROM PEER 2 [comment] EXPECT DEL 1.0.3.0/24 RECEIVED BY PEER 1 [comment] EXPECT DEL 1.0.3.0/24 RECEIVED BY PEER 3 [** TABLE DebugTable-D1 **] [DELETE] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.5 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [DELETE] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.5 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [separator]------------------------------------- [comment] DELETING FROM PEER 2 [comment] EXPECT DEL 1.0.4.0/24 RECEIVED BY PEER 1 [comment] EXPECT DEL 1.0.4.0/24 RECEIVED BY PEER 3 [** TABLE DebugTable-D1 **] [DELETE] SubnetRoute: Net: 1.0.4.0/24 PAList: Next Hop Attribute 2.0.0.5 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [DELETE] SubnetRoute: Net: 1.0.4.0/24 PAList: Next Hop Attribute 2.0.0.5 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [separator]------------------------------------- [** TABLE DebugTable-D1 **] [PUSH] [** TABLE DebugTable-D2 **] [PUSH] [comment] ****************************************** [comment] TEST 12 [comment] PEER 1 GOES DOWN JUST BEFORE P3 COMES UP [separator]------------------------------------- [comment] SENDING FROM PEER 1 [comment] EXPECT RECEIVED BY PEER 2 [** TABLE DebugTable-D2 **] [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 1 [comment] EXPECT RECEIVED BY PEER 2 [** TABLE DebugTable-D2 **] [ADD] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [comment] EXPECT RECEIVED BY PEER 1 [** TABLE DebugTable-D1 **] [ADD] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [separator]------------------------------------- [comment] TAKE PEER 1 DOWN [** TABLE DebugTable-D2 **] [PUSH] [separator]------------------------------------- [comment] BRING PEER 3 UP [separator]------------------------------------- [comment] LET EVENT QUEUE DRAIN [comment] EXPECT DEL 1.0.1.0/24 RECEIVED BY PEER 2 [comment] EXPECT DEL 1.0.2.0/24 RECEIVED BY PEER 2 [comment] EXPECT ADD 1.0.3.0/24 RECEIVED BY PEER 3 [** TABLE DebugTable-D3 **] [ADD] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [PUSH] [** TABLE DebugTable-D2 **] [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [** TABLE DebugTable-D2 **] [PUSH] [** TABLE DebugTable-D3 **] [PUSH] [** TABLE DebugTable-D2 **] [DELETE] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [** TABLE DebugTable-D2 **] [PUSH] [** TABLE DebugTable-D3 **] [PUSH] [separator]------------------------------------- [separator]------------------------------------- [comment] DELETING FROM PEER 2 [comment] EXPECT DEL 1.0.3.0/24 RECEIVED BY PEER 3 [** TABLE DebugTable-D3 **] [DELETE] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [separator]------------------------------------- [comment] BRING PEER 1 UP [comment] EXPECT NO CHANGE [separator]------------------------------------- [** TABLE DebugTable-D1 **] [PUSH] [** TABLE DebugTable-D2 **] [PUSH] [comment] ****************************************** [comment] TEST 13 [comment] NEW ADD ARRIVES BEFORE PEER IS DUMPED [separator]------------------------------------- [comment] SENDING FROM PEER 1 [comment] EXPECT RECEIVED BY PEER 2 [** TABLE DebugTable-D2 **] [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [comment] EXPECT RECEIVED BY PEER 1 [** TABLE DebugTable-D1 **] [ADD] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [separator]------------------------------------- [comment] BRING PEER 3 UP [comment] EXPECT NO CHANGE UNTIL EVENTLOOP RUNS [separator]------------------------------------- [comment] SENDING FROM PEER 2 [comment] EXPECT RECEIVED BY PEER 1 BUT NOT PEER 3 [** TABLE DebugTable-D1 **] [ADD] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [separator]------------------------------------- [comment] RUN EVENT LOOP TO COMPLETION [comment] EXPECT ADD 1.0.1.0/24 RECEIVED BY PEER 3 [comment] EXPECT ADD 1.0.2.0/24 RECEIVED BY PEER 3 [comment] EXPECT ADD 1.0.3.0/24 RECEIVED BY PEER 3 [** TABLE DebugTable-D3 **] [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [PUSH] [** TABLE DebugTable-D3 **] [ADD] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [PUSH] [** TABLE DebugTable-D3 **] [ADD] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [PUSH] [separator]------------------------------------- [separator]------------------------------------- [separator]------------------------------------- [comment] DELETING FROM PEER 2 [comment] EXPECT DEL 1.0.2.0/24 RECEIVED BY PEER 1 & 3 [** TABLE DebugTable-D1 **] [DELETE] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [DELETE] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [separator]------------------------------------- [comment] DELETING FROM PEER 2 [comment] EXPECT DEL 1.0.3.0/24 RECEIVED BY PEER 2 & 3 [** TABLE DebugTable-D1 **] [DELETE] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [DELETE] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [separator]------------------------------------- [comment] DELETING FROM PEER 1 [comment] EXPECT DEL 1.0.1.0/24 RECEIVED BY PEER 2 & 3 [** TABLE DebugTable-D2 **] [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [separator]------------------------------------- [** TABLE DebugTable-D1 **] [PUSH] [** TABLE DebugTable-D2 **] [PUSH] [comment] ****************************************** [comment] TEST 14 [comment] OLD DEL AFTER PEER HAS BEEN DUMPED [comment] TEST OLD GENID DELETE ON CURRENT DUMP PEER [separator]------------------------------------- [comment] SENDING FROM PEER 1 [comment] EXPECT RECEIVED BY PEER 2 [** TABLE DebugTable-D2 **] [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 1 [comment] EXPECT RECEIVED BY PEER 2 [** TABLE DebugTable-D2 **] [ADD] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [comment] EXPECT RECEIVED BY PEER 1 [** TABLE DebugTable-D1 **] [ADD] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [separator]------------------------------------- [comment] TAKE PEER 1 DOWN [comment] DONT RUN EVENTLOOP, SO DELETION IS DELAYED [comment] EXPECT NO CHANGE [** TABLE DebugTable-D2 **] [PUSH] [separator]------------------------------------- [comment] BRING PEER 1 UP AGAIN, BUT SKIP DUMPING TO IT [comment] EXPECT NO CHANGE [separator]------------------------------------- [comment] BRING PEER 3 UP [comment] EXPECT NO CHANGE UNTIL EVENTLOOP RUNS [separator]------------------------------------- [comment] SENDING FROM PEER 1 [comment] EXPECT RECEIVED BY PEER 2 [comment] EXPECT NOT RECEIVED BY PEER 2 - PEER NOT YET DUMPED [** TABLE DebugTable-D2 **] [ADD] SubnetRoute: Net: 1.0.4.0/24 PAList: Next Hop Attribute 2.0.0.4 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [separator]------------------------------------- [comment] RUN EVENT LOOP TO COMPLETION [comment] EXPECT DEL 1.0.1.0/24 RECEIVED BY PEER 2 [comment] EXPECT DEL 1.0.3.0/24 RECEIVED BY PEER 2 [comment] EXPECT DEL 1.0.3.0 **NOT** RECEIVED BY PEER 3 [comment] EXPECT ADD 1.0.2.0/24 RECEIVED BY PEER 3 [comment] EXPECT ADD 1.0.4.0/24 RECEIVED BY PEER 3 [** TABLE DebugTable-D3 **] [ADD] SubnetRoute: Net: 1.0.4.0/24 PAList: Next Hop Attribute 2.0.0.4 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [PUSH] [** TABLE DebugTable-D3 **] [ADD] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [PUSH] [** TABLE DebugTable-D2 **] [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [** TABLE DebugTable-D1 **] [PUSH] [** TABLE DebugTable-D2 **] [PUSH] [** TABLE DebugTable-D3 **] [PUSH] [** TABLE DebugTable-D2 **] [DELETE] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [** TABLE DebugTable-D1 **] [PUSH] [** TABLE DebugTable-D2 **] [PUSH] [** TABLE DebugTable-D3 **] [PUSH] [separator]------------------------------------- [separator]------------------------------------- [comment] DELETING FROM PEER 2 [comment] EXPECT DEL 1.0.2.0/24 RECEIVED BY PEER 1 & 3 [** TABLE DebugTable-D1 **] [DELETE] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [DELETE] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [separator]------------------------------------- [comment] DELETING FROM PEER 1 [comment] EXPECT DEL 1.0.4.0/24 RECEIVED BY PEER 2 & 3 [** TABLE DebugTable-D2 **] [DELETE] SubnetRoute: Net: 1.0.4.0/24 PAList: Next Hop Attribute 2.0.0.4 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [DELETE] SubnetRoute: Net: 1.0.4.0/24 PAList: Next Hop Attribute 2.0.0.4 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [separator]------------------------------------- [** TABLE DebugTable-D1 **] [PUSH] [** TABLE DebugTable-D2 **] [PUSH] [comment] ****************************************** [comment] TEST 15 [comment] ADD AFTER PEER HAS BEEN DUMPED [separator]------------------------------------- [comment] SENDING FROM PEER 1 [comment] EXPECT RECEIVED BY PEER 2 [** TABLE DebugTable-D2 **] [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [comment] EXPECT RECEIVED BY PEER 1 [** TABLE DebugTable-D1 **] [ADD] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [separator]------------------------------------- [comment] BRING PEER 3 UP [comment] EXPECT NO CHANGE UNTIL EVENTLOOP RUNS [separator]------------------------------------- [comment] LET EVENT QUEUE DRAIN [comment] EXPECT ADD 1.0.1.0/24 RECEIVED BY PEER 3 [comment] EXPECT (MOVES TO DUMPING NEXT PEER) [** TABLE DebugTable-D3 **] [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [PUSH] [** TABLE DebugTable-D3 **] [ADD] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [PUSH] [separator]------------------------------------- [comment] SENDING FROM PEER 1 [comment] EXPECT RECEIVED BY PEER 2 [comment] EXPECT NOT RECEIVED BY PEER 3 YET [** TABLE DebugTable-D2 **] [ADD] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [ADD] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [separator]------------------------------------- [comment] RUN EVENT LOOP TO COMPLETION [comment] EXPECT ADD 1.0.3.0/24 RECEIVED BY PEER 3 [comment] EXPECT ADD 1.0.2.0/24 RECEIVED BY PEER 3 [separator]------------------------------------- [comment] DELETING FROM PEER 1 [comment] EXPECT DEL 1.0.1.0/24 RECEIVED BY PEER 2 & 3 [** TABLE DebugTable-D2 **] [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] DELETING FROM PEER 1 [comment] EXPECT DEL 1.0.3.0/24 RECEIVED BY PEER 2 & 3 [** TABLE DebugTable-D2 **] [DELETE] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [DELETE] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [separator]------------------------------------- [comment] DELETING FROM PEER 2 [comment] EXPECT DEL 1.0.2.0/24 RECEIVED BY PEER 1 & 3 [** TABLE DebugTable-D1 **] [DELETE] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [DELETE] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [separator]------------------------------------- [** TABLE DebugTable-D1 **] [PUSH] [** TABLE DebugTable-D2 **] [PUSH] [comment] ****************************************** [comment] TEST 16 [comment] ADD AFTER PEER WITH NO ROUTES HAS BEEN DUMPED [separator]------------------------------------- [comment] SENDING FROM PEER 2 [comment] EXPECT RECEIVED BY PEER 1 [** TABLE DebugTable-D1 **] [ADD] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [separator]------------------------------------- [comment] BRING PEER 3 UP [comment] EXPECT NO CHANGE UNTIL EVENTLOOP RUNS [separator]------------------------------------- [comment] LET EVENT QUEUE DRAIN [comment] NO ROUTES IN 1st PEER (MOVES TO DUMPING NEXT PEER) [** TABLE DebugTable-D3 **] [ADD] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [PUSH] [separator]------------------------------------- [comment] SENDING FROM PEER 1 [comment] EXPECT RECEIVED BY PEER 2 [comment] EXPECT NOT RECEIVED BY PEER 3 YET [** TABLE DebugTable-D2 **] [ADD] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [ADD] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [separator]------------------------------------- [comment] RUN EVENT LOOP TO COMPLETION [comment] EXPECT ADD 1.0.3.0/24 RECEIVED BY PEER 3 [comment] EXPECT ADD 1.0.2.0/24 RECEIVED BY PEER 3 [separator]------------------------------------- [comment] DELETING FROM PEER 1 [comment] EXPECT DEL 1.0.3.0/24 RECEIVED BY PEER 2 & 3 [** TABLE DebugTable-D2 **] [DELETE] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [DELETE] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [separator]------------------------------------- [comment] DELETING FROM PEER 2 [comment] EXPECT DEL 1.0.2.0/24 RECEIVED BY PEER 1 & 3 [** TABLE DebugTable-D1 **] [DELETE] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [DELETE] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [separator]------------------------------------- [** TABLE DebugTable-D1 **] [PUSH] [** TABLE DebugTable-D2 **] [PUSH] [** TABLE DebugTable-D2 **] [PUSH] [comment] ****************************************** [comment] TEST 17 [comment] ADD ON NEW PEER DURING DUMP [separator]------------------------------------- [comment] SENDING FROM PEER 2 [comment] EXPECT RECEIVED BY NOONE [separator]------------------------------------- [comment] BRING PEER 3 UP [comment] EXPECT NO CHANGE UNTIL EVENTLOOP RUNS [separator]------------------------------------- [comment] BRING PEER 1 UP [comment] NO NEED TO DUMP TO THIS PEER FOR THIS TEST [separator]------------------------------------- [comment] SENDING FROM PEER 1 [comment] EXPECT RECEIVED BY PEER 2 [comment] EXPECT NOT RECEIVED BY PEER 3 YET [** TABLE DebugTable-D2 **] [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] RUN EVENT LOOP TO COMPLETION [comment] EXPECT ADD 1.0.1.0/24 RECEIVED BY PEER 3 [comment] EXPECT ADD 1.0.2.0/24 RECEIVED BY PEER 3 [** TABLE DebugTable-D3 **] [ADD] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [PUSH] [separator]------------------------------------- [comment] DELETING FROM PEER 1 [comment] EXPECT DEL 1.0.1.0/24 RECEIVED BY PEER 2 & 3 [** TABLE DebugTable-D2 **] [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] DELETING FROM PEER 2 [comment] EXPECT DEL 1.0.2.0/24 RECEIVED BY PEER 1 & 3 [** TABLE DebugTable-D1 **] [DELETE] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [DELETE] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [separator]------------------------------------- [** TABLE DebugTable-D1 **] [PUSH] [** TABLE DebugTable-D2 **] [PUSH] [comment] ****************************************** [comment] TEST 18 [comment] ADD ON NEW PEER DURING DUMP [separator]------------------------------------- [comment] SENDING FROM PEER 1 [comment] EXPECT RECEIVED BY PEER 2 [comment] EXPECT NOT RECEIVED BY PEER 3 YET [** TABLE DebugTable-D2 **] [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [comment] EXPECT RECEIVED BY NOONE [** TABLE DebugTable-D1 **] [ADD] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [separator]------------------------------------- [comment] TAKE PEER 1 DOWN [comment] EXPECT NO CHANGE UNTIL EVENTLOOP RUNS [** TABLE DebugTable-D2 **] [PUSH] [separator]------------------------------------- [comment] BRING PEER 3 UP [comment] EXPECT NO CHANGE UNTIL EVENTLOOP RUNS [separator]------------------------------------- [comment] BRING PEER 1 UP [comment] NO NEED TO DUMP TO THIS PEER FOR THIS TEST [separator]------------------------------------- [comment] RUN EVENT LOOP TO COMPLETION [comment] EXPECT DEL 1.0.1.0/24 RECEIVED BY PEER 2 [comment] EXPECT DEL 1.0.1.0/24 NOT RECEIVED BY PEER 3 [comment] EXPECT ADD 1.0.2.0/24 RECEIVED BY PEER 3 [** TABLE DebugTable-D3 **] [ADD] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [PUSH] [** TABLE DebugTable-D2 **] [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [** TABLE DebugTable-D1 **] [PUSH] [** TABLE DebugTable-D2 **] [PUSH] [** TABLE DebugTable-D3 **] [PUSH] [separator]------------------------------------- [comment] DELETING FROM PEER 2 [comment] EXPECT DEL 1.0.2.0/24 RECEIVED BY PEER 1 & 3 [** TABLE DebugTable-D1 **] [DELETE] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [DELETE] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [separator]------------------------------------- [** TABLE DebugTable-D1 **] [PUSH] [** TABLE DebugTable-D2 **] [PUSH] [comment] ****************************************** [comment] TEST 19 [comment] DELETE ON PEER THAT DUMP TABLE DOESNT KNOW ABOUT [separator]------------------------------------- [comment] SENDING FROM PEER 1 [comment] EXPECT RECEIVED BY PEER 2 [** TABLE DebugTable-D2 **] [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 1 [comment] EXPECT RECEIVED BY PEER 2 [** TABLE DebugTable-D2 **] [ADD] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [comment] EXPECT RECEIVED BY PEER 1 [** TABLE DebugTable-D1 **] [ADD] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [separator]------------------------------------- [comment] TAKE PEER 1 DOWN [comment] EXPECT NO CHANGE UNTIL EVENTLOOP RUNS [** TABLE DebugTable-D2 **] [PUSH] [separator]------------------------------------- [comment] BRING PEER 1 UP [comment] NO NEED TO DUMP TO THIS PEER FOR THIS TEST [separator]------------------------------------- [comment] BRING PEER 3 UP [comment] EXPECT NO CHANGE UNTIL EVENTLOOP RUNS [separator]------------------------------------- [comment] RUN EVENT LOOP TO COMPLETION [comment] EXPECT DEL 1.0.1.0/24 RECEIVED BY PEER 2 [comment] EXPECT DEL 1.0.1.0/24 NOT RECEIVED BY PEER 3 [comment] EXPECT DEL 1.0.3.0/24 RECEIVED BY PEER 2 [comment] EXPECT DEL 1.0.3.0/24 NOT RECEIVED BY PEER 3 [comment] EXPECT ADD 1.0.2.0/24 RECEIVED BY PEER 3 [** TABLE DebugTable-D3 **] [ADD] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [PUSH] [** TABLE DebugTable-D2 **] [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [** TABLE DebugTable-D1 **] [PUSH] [** TABLE DebugTable-D2 **] [PUSH] [** TABLE DebugTable-D3 **] [PUSH] [** TABLE DebugTable-D2 **] [DELETE] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [** TABLE DebugTable-D1 **] [PUSH] [** TABLE DebugTable-D2 **] [PUSH] [** TABLE DebugTable-D3 **] [PUSH] [separator]------------------------------------- [comment] DELETING FROM PEER 2 [comment] EXPECT DEL 1.0.2.0/24 RECEIVED BY PEER 1 & 3 [** TABLE DebugTable-D1 **] [DELETE] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [DELETE] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [separator]------------------------------------- [** TABLE DebugTable-D1 **] [PUSH] [** TABLE DebugTable-D2 **] [PUSH] [comment] ****************************************** [comment] TEST 20 [comment] DELETE ON PEER THAT DUMP TABLE DOESNT KNOW ABOUT [separator]------------------------------------- [comment] SENDING FROM PEER 1 [comment] EXPECT RECEIVED BY PEER 2 [** TABLE DebugTable-D2 **] [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 1 [comment] EXPECT RECEIVED BY PEER 2 [** TABLE DebugTable-D2 **] [ADD] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 1 [comment] EXPECT RECEIVED BY PEER 2 [** TABLE DebugTable-D2 **] [ADD] SubnetRoute: Net: 1.0.4.0/24 PAList: Next Hop Attribute 2.0.0.4 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [comment] EXPECT RECEIVED BY PEER 1 [** TABLE DebugTable-D1 **] [ADD] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [separator]------------------------------------- [comment] TAKE PEER 1 DOWN [comment] EXPECT NO CHANGE UNTIL EVENTLOOP RUNS [** TABLE DebugTable-D2 **] [PUSH] [separator]------------------------------------- [comment] BRING PEER 1 UP [comment] NO NEED TO DUMP TO THIS PEER FOR THIS TEST [separator]------------------------------------- [comment] SENDING FROM PEER 1 [comment] EXPECT RECEIVED BY PEER 2 [** TABLE DebugTable-D2 **] [ADD] SubnetRoute: Net: 1.0.5.0/24 PAList: Next Hop Attribute 2.0.0.5 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [separator]------------------------------------- [comment] BRING PEER 3 UP [comment] EXPECT NO CHANGE UNTIL EVENTLOOP RUNS [separator]------------------------------------- [comment] RUN EVENT LOOP TO COMPLETION [comment] EXPECT DEL 1.0.1.0/24 RECEIVED BY PEER 2 [comment] EXPECT DEL 1.0.1.0/24 NOT RECEIVED BY PEER 3 [comment] EXPECT DEL 1.0.3.0/24 RECEIVED BY PEER 2 [comment] EXPECT DEL 1.0.3.0/24 NOT RECEIVED BY PEER 3 [comment] EXPECT DEL 1.0.4.0/24 RECEIVED BY PEER 2 [comment] EXPECT DEL 1.0.4.0/24 NOT RECEIVED BY PEER 3 [comment] EXPECT ADD 1.0.2.0/24 RECEIVED BY PEER 3 [comment] EXPECT ADD 1.0.5.0/24 RECEIVED BY PEER 3 [** TABLE DebugTable-D3 **] [ADD] SubnetRoute: Net: 1.0.5.0/24 PAList: Next Hop Attribute 2.0.0.5 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [PUSH] [** TABLE DebugTable-D3 **] [ADD] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [PUSH] [** TABLE DebugTable-D2 **] [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [** TABLE DebugTable-D1 **] [PUSH] [** TABLE DebugTable-D2 **] [PUSH] [** TABLE DebugTable-D3 **] [PUSH] [** TABLE DebugTable-D2 **] [DELETE] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [** TABLE DebugTable-D1 **] [PUSH] [** TABLE DebugTable-D2 **] [PUSH] [** TABLE DebugTable-D3 **] [PUSH] [** TABLE DebugTable-D2 **] [DELETE] SubnetRoute: Net: 1.0.4.0/24 PAList: Next Hop Attribute 2.0.0.4 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [** TABLE DebugTable-D1 **] [PUSH] [** TABLE DebugTable-D2 **] [PUSH] [** TABLE DebugTable-D3 **] [PUSH] [separator]------------------------------------- [comment] DELETING FROM PEER 2 [comment] EXPECT DEL 1.0.2.0/24 RECEIVED BY PEER 1 & 3 [** TABLE DebugTable-D1 **] [DELETE] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [DELETE] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [separator]------------------------------------- [comment] DELETING FROM PEER 1 [comment] EXPECT DEL 1.0.5.0/24 RECEIVED BY PEER 2 & 3 [** TABLE DebugTable-D2 **] [DELETE] SubnetRoute: Net: 1.0.5.0/24 PAList: Next Hop Attribute 2.0.0.5 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [DELETE] SubnetRoute: Net: 1.0.5.0/24 PAList: Next Hop Attribute 2.0.0.5 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [separator]------------------------------------- [** TABLE DebugTable-D1 **] [PUSH] [** TABLE DebugTable-D2 **] [PUSH] [comment] ****************************************** [comment] TEST 21 [comment] ADD ON NEW VERSION OF HALF-DUMPED PEER [separator]------------------------------------- [comment] SENDING FROM PEER 1 [comment] EXPECT RECEIVED BY PEER 2 [** TABLE DebugTable-D2 **] [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 1 [comment] EXPECT RECEIVED BY PEER 2 [** TABLE DebugTable-D2 **] [ADD] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [comment] EXPECT RECEIVED BY PEER 1 [** TABLE DebugTable-D1 **] [ADD] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [separator]------------------------------------- [comment] BRING PEER 3 UP [comment] EXPECT NO CHANGE UNTIL EVENTLOOP RUNS [separator]------------------------------------- [comment] STEP EVENT LOOP ONCE TO DUMP ONE ROUTE [comment] EXPECT ADD 1.0.1.0/24 RECEIVED BY PEER 3 [** TABLE DebugTable-D3 **] [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [PUSH] [** TABLE DebugTable-D3 **] [ADD] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [PUSH] [** TABLE DebugTable-D3 **] [ADD] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [PUSH] [separator]------------------------------------- [comment] TAKE PEER 1 DOWN [comment] EXPECT NO CHANGE UNTIL EVENTLOOP RUNS [** TABLE DebugTable-D2 **] [PUSH] [** TABLE DebugTable-D3 **] [PUSH] [separator]------------------------------------- [comment] BRING PEER 1 UP [comment] NO NEED TO DUMP TO THIS PEER FOR THIS TEST [separator]------------------------------------- [comment] SENDING FROM PEER 1 [comment] EXPECT ADD 1.0.5.0/24 RECEIVED BY PEER 2 [** TABLE DebugTable-D2 **] [ADD] SubnetRoute: Net: 1.0.5.0/24 PAList: Next Hop Attribute 2.0.0.5 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [ADD] SubnetRoute: Net: 1.0.5.0/24 PAList: Next Hop Attribute 2.0.0.5 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [separator]------------------------------------- [comment] RUN EVENT LOOP TO COMPLETION [comment] EXPECT DEL 1.0.1.0/24 RECEIVED BY PEER 2 & 3 [comment] EXPECT DEL 1.0.2.0/24 RECEIVED BY PEER 2 [comment] EXPECT ADD 1.0.2.0/24 RECEIVED BY PEER 3 [comment] EXPECT ADD 1.0.5.0/24 RECEIVED BY PEER 3 [** TABLE DebugTable-D2 **] [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [** TABLE DebugTable-D1 **] [PUSH] [** TABLE DebugTable-D2 **] [PUSH] [** TABLE DebugTable-D3 **] [PUSH] [** TABLE DebugTable-D2 **] [DELETE] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [DELETE] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [** TABLE DebugTable-D1 **] [PUSH] [** TABLE DebugTable-D2 **] [PUSH] [** TABLE DebugTable-D3 **] [PUSH] [separator]------------------------------------- [comment] DELETING FROM PEER 2 [comment] EXPECT DEL 1.0.2.0/24 RECEIVED BY PEER 1 & 3 [** TABLE DebugTable-D1 **] [DELETE] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [DELETE] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [separator]------------------------------------- [comment] DELETING FROM PEER 1 [comment] EXPECT DEL 1.0.5.0/24 RECEIVED BY PEER 2 & 3 [** TABLE DebugTable-D2 **] [DELETE] SubnetRoute: Net: 1.0.5.0/24 PAList: Next Hop Attribute 2.0.0.5 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [DELETE] SubnetRoute: Net: 1.0.5.0/24 PAList: Next Hop Attribute 2.0.0.5 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] Local Preference Attribute - 100 [separator]------------------------------------- [** TABLE DebugTable-D1 **] [PUSH] [** TABLE DebugTable-D2 **] [PUSH] [comment] ****************************************** [comment] TEST 22 [comment] TWO COPIES OF SAME ROUTE [separator]------------------------------------- [comment] SENDING FROM PEER 1 [comment] EXPECT RECEIVED BY PEER 2 [** TABLE DebugTable-D2 **] [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [comment] EXPECT NO CHANGE [separator]------------------------------------- [comment] BRING PEER 3 UP [comment] EXPECT ADD 1.0.1.0/24 RECEIVED BY PEER 3 [comment] **ONLY ONE COPY** [** TABLE DebugTable-D3 **] [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [PUSH] [separator]------------------------------------- [comment] DELETING FROM PEER 1 [comment] EXPECT DEL 1.0.1.0/24 RECEIVED BY PEER 2 [comment] EXPECT REP 1.0.1.0/24 RECEIVED BY PEER 3 [comment] EXPECT ADD 1.0.1.0/24 RECEIVED BY PEER 1 [** TABLE DebugTable-D2 **] [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] Local Preference Attribute - 100 [** TABLE DebugTable-D1 **] [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [** TABLE DebugTable-D1 **] [PUSH] [** TABLE DebugTable-D2 **] [PUSH] [** TABLE DebugTable-D3 **] [PUSH] [separator]------------------------------------- [comment] DELETING FROM PEER 2 [comment] EXPECT DEL 1.0.1.0/24 RECEIVED BY P1 & 3 [** TABLE DebugTable-D1 **] [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [** TABLE DebugTable-D3 **] [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/6, AS/5, AS/4] Local Preference Attribute - 100 [separator]------------------------------------- [comment] SHUTDOWN AND CLEAN UP xorp/bgp/tests/test_subnet_route.cc0000664000076400007640000000764011421137511017661 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "bgp_module.h" #include "libxorp/xorp.h" #include "libxorp/test_main.hh" #include "libxorp/exceptions.hh" #include "libxorp/ipv4.hh" #include "libxorp/ipv4net.hh" #include "libxorp/ipv6.hh" #include "libxorp/ipv6net.hh" #include "libxorp/ref_trie.hh" #include "libxorp/timeval.hh" #include "libxorp/timer.hh" #include "path_attribute.hh" #include "subnet_route.hh" template<> inline void RefTrieNode > ::delete_payload(const SubnetRoute* p) { debug_msg("delete_payload %p\n", p); p->unref(); } template<> inline void RefTrieNode > ::delete_payload(const SubnetRoute* p) { p->unref(); } const int routes = 200000; // Number of routes template bool test_subnet_route1(TestInfo& info, IPNet net) { DOUT(info) << info.test_name() << endl; FPAListRef fpa = new FastPathAttributeList(); A nexthop; NextHopAttribute nha(nexthop); fpa->add_path_attribute(nha); ASPathAttribute aspa(ASPath("1,2,3")); fpa->add_path_attribute(aspa); PAListRef pa = new PathAttributeList(fpa); RefTrie > route_table; for(int i = 0; i < routes; i++) { SubnetRoute *route = new SubnetRoute(net, pa, 0); route_table.insert(net, *route); route->unref(); ++net; } TimeVal now; TimeVal start; TimeVal used; TimerList::system_gettimeofday(&now); start = now; route_table.delete_all_nodes(); TimerList::system_gettimeofday(&now); used = now - start; DOUT(info) << " To delete " << routes << " routes with the same path attribute list took " << used.str() << " seconds" << endl; return true; } template bool test_subnet_route2(TestInfo& info, IPNet net) { DOUT(info) << info.test_name() << endl; FPAListRef fpa = new FastPathAttributeList(); A nexthop; NextHopAttribute nha(nexthop); fpa->add_path_attribute(nha); ASPathAttribute aspa(ASPath("1,2,3")); fpa->add_path_attribute(aspa); PAListRef pa = new PathAttributeList(fpa); RefTrie > route_table; for(int i = 0; i < routes; i++) { ++nexthop; fpa->replace_nexthop(nexthop); pa.release(); pa = new PathAttributeList(fpa); SubnetRoute *route = new SubnetRoute(net, pa, 0); route_table.insert(net, *route); route->unref(); ++net; } TimeVal now; TimeVal start; TimeVal used; TimerList::system_gettimeofday(&now); start = now; route_table.delete_all_nodes(); TimerList::system_gettimeofday(&now); used = now - start; DOUT(info) << " To delete " << routes << " routes with the a different path attribute list per route took " << used.str() << "seconds" << endl; return true; } /* ** This function is never called it exists to instantiate the ** templatised functions. */ void dummy_subnet_route() { XLOG_FATAL("Not to be called"); callback(test_subnet_route1); callback(test_subnet_route1); callback(test_subnet_route2); callback(test_subnet_route2); } xorp/bgp/tests/test_nhlookup.reference0000664000076400007640000001623511421137511020353 0ustar greearbgreearb[comment] TEST 1 [comment] ADD AND DELETE, NEXTHOP RESOLVES [NH REGISTER] NextHop: 2.0.0.1 Net: 1.0.1.0/24 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [separator]------------------------------------- [NH DEREGISTER] NextHop: 2.0.0.1 Net: 1.0.1.0/24 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [separator]------------------------------------- [comment] TEST 1a [comment] ADD AND DELETE, NEXTHOP NOT YET RESOLVED [NH REGISTER] NextHop: 2.0.0.1 Net: 1.0.1.0/24 [separator]------------------------------------- [NH DEREGISTER] NextHop: 2.0.0.1 Net: 1.0.1.0/24 [separator]------------------------------------- [comment] TEST 1b [comment] ADD AND DELETE, NEXTHOP NOT YET RESOLVED [NH REGISTER] NextHop: 2.0.0.1 Net: 1.0.1.0/24 [separator]------------------------------------- [comment] THEN NEXTHOP RESOLVES [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [PUSH] [separator]------------------------------------- [comment] NOW DELETE THE ROUTE [NH DEREGISTER] NextHop: 2.0.0.1 Net: 1.0.1.0/24 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [separator]------------------------------------- [comment] TEST 1c [comment] ADDx2 AND DELETE, NEXTHOP NOT YET RESOLVED [NH REGISTER] NextHop: 2.0.0.1 Net: 1.0.1.0/24 [NH REGISTER] NextHop: 2.0.0.1 Net: 1.0.2.0/24 [separator]------------------------------------- [comment] THEN NEXTHOP RESOLVES [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [ADD] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [PUSH] [separator]------------------------------------- [comment] NOW DELETE THE ROUTES [NH DEREGISTER] NextHop: 2.0.0.1 Net: 1.0.1.0/24 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [NH DEREGISTER] NextHop: 2.0.0.1 Net: 1.0.2.0/24 [DELETE] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [separator]------------------------------------- [comment] TEST 2 [comment] ADD, REPLACE, RESOLVE, DELETE [comment] ADD [NH REGISTER] NextHop: 2.0.0.1 Net: 1.0.1.0/24 [separator]------------------------------------- [comment] REPLACE [NH REGISTER] NextHop: 2.0.0.2 Net: 1.0.1.0/24 [NH DEREGISTER] NextHop: 2.0.0.1 Net: 1.0.1.0/24 [separator]------------------------------------- [comment] THEN NEXTHOP RESOLVES [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/5, AS/4] [PUSH] [separator]------------------------------------- [comment] NOW DELETE THE ROUTE [NH DEREGISTER] NextHop: 2.0.0.2 Net: 1.0.1.0/24 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/5, AS/4] [separator]------------------------------------- [comment] TEST 2b [comment] ADD, REPLACE (OLD IS DELETED), RESOLVE, DELETE [comment] ADD [NH REGISTER] NextHop: 2.0.0.1 Net: 1.0.1.0/24 [separator]------------------------------------- [comment] REPLACE [NH REGISTER] NextHop: 2.0.0.2 Net: 1.0.1.0/24 [NH DEREGISTER] NextHop: 2.0.0.1 Net: 1.0.1.0/24 [separator]------------------------------------- [comment] THEN NEXTHOP RESOLVES [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/5, AS/4] [PUSH] [separator]------------------------------------- [comment] NOW DELETE THE ROUTE [NH DEREGISTER] NextHop: 2.0.0.2 Net: 1.0.1.0/24 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/5, AS/4] [separator]------------------------------------- [comment] TEST 3 [comment] REPLACE, REPLACE, RESOLVE, DELETE [comment] FIRST REPLACE [NH REGISTER] NextHop: 2.0.0.2 Net: 1.0.1.0/24 [separator]------------------------------------- [comment] SECOND REPLACE [NH REGISTER] NextHop: 2.0.0.3 Net: 1.0.1.0/24 [separator]------------------------------------- [comment] THEN NEXTHOP RESOLVES [REPLACE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] [NH DEREGISTER] NextHop: 2.0.0.1 Net: 1.0.1.0/24 [PUSH] [separator]------------------------------------- [comment] NOW DELETE THE ROUTE [NH DEREGISTER] NextHop: 2.0.0.3 Net: 1.0.1.0/24 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/9, AS/8, AS/7] [separator]------------------------------------- [comment] TEST 4 [comment] TESTING PUSH [comment] ADDx2 AND DELETE, NEXTHOP NOT YET RESOLVED [NH REGISTER] NextHop: 2.0.0.1 Net: 1.0.1.0/24 [NH REGISTER] NextHop: 2.0.0.1 Net: 1.0.2.0/24 [comment] SEND PUSH [PUSH] [separator]------------------------------------- [comment] THEN NEXTHOP RESOLVES [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [ADD] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [PUSH] [separator]------------------------------------- [comment] NOW DELETE THE ROUTES [NH DEREGISTER] NextHop: 2.0.0.1 Net: 1.0.1.0/24 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [NH DEREGISTER] NextHop: 2.0.0.1 Net: 1.0.2.0/24 [DELETE] PUSH flag is set SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [comment] SEND PUSH AGAIN [PUSH] [separator]------------------------------------- [comment] TEST 5 [comment] ADD, REPLACE WITH UNRESOLVABLE, DELETE [NH REGISTER] NextHop: 2.0.0.1 Net: 1.0.1.0/24 [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [separator]------------------------------------- [comment] REPLACE [NH REGISTER] NextHop: 2.0.0.2 Net: 1.0.1.0/24 [separator]------------------------------------- [comment] NOW DELETE THE ROUTE [NH DEREGISTER] NextHop: 2.0.0.2 Net: 1.0.1.0/24 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [separator]------------------------------------- [separator]------------------------------------- [comment] SHUTDOWN AND CLEAN UP xorp/bgp/tests/test_packet.hh0000664000076400007640000000312611421137511016417 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/bgp/test_packet.hh,v 1.11 2008/10/02 21:56:22 bms Exp $ #ifndef __BGP_TEST_PACKET_HH__ #define __BGP_TEST_PACKET_HH__ #include "packet.hh" #include "socket.hh" #include "libxorp/debug.h" #include "libxorp/exceptions.hh" #include "libxorp/eventloop.hh" class BGPTestPacket { public: BGPTestPacket(); int run_tests(); protected: private: KeepAlivePacket* create_keepalive(); bool test_keepalive(); UpdatePacket* create_update(); bool test_update(); UpdatePacket* create_update_ipv6(); bool test_update_ipv6(); OpenPacket* create_open(); bool test_open(); NotificationPacket* create_notification(); bool test_notification(); bool test_aspath(); }; #endif // __BGP_TEST_PACKET_HH__ xorp/bgp/tests/test_policy.cc0000664000076400007640000006651511540224220016444 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2006-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "bgp_module.h" #include "libxorp/xorp.h" #include "libxorp/xorpfd.hh" #include "libxorp/eventloop.hh" #include "libxorp/xlog.h" #include "libxorp/test_main.hh" #ifdef HAVE_PWD_H #include #endif #include "policy/backend/version_filters.hh" #include "bgp.hh" #include "route_table_base.hh" #include "route_table_policy.hh" #include "route_table_ribin.hh" #include "route_table_debug.hh" #include "path_attribute.hh" #include "local_data.hh" #include "dummy_next_hop_resolver.hh" bool test_policy_export(TestInfo& /*info*/) { #ifndef HOST_OS_WINDOWS struct passwd *pwd = getpwuid(getuid()); string filename = "/tmp/test_policy_export."; filename += pwd->pw_name; #else char *tmppath = (char *)malloc(256); GetTempPathA(256, tmppath); string filename = string(tmppath) + "test_policy_export"; free(tmppath); #endif EventLoop eventloop; BGPMain bgpmain(eventloop); LocalData localdata(bgpmain.eventloop()); Iptuple iptuple; BGPPeerData *pd1 = new BGPPeerData(localdata, iptuple, AsNum(0), IPv4(),0); BGPPeer peer1(&localdata, pd1, NULL, &bgpmain); PeerHandler handler1("test1", &peer1, NULL, NULL); BGPPeerData *pd2 = new BGPPeerData(localdata, iptuple, AsNum(0), IPv4(),0); BGPPeer peer2(&localdata, pd2, NULL, &bgpmain); PeerHandler handler2("test2", &peer2, NULL, NULL); VersionFilters policy_filters; // Trivial plumbing. We're not testing the RibInTable here, so // mostly we'll just inject directly into the PolicyTable, but we // need an RibInTable here to test lookup_route. RibInTable *ribin_table = new RibInTable("RIB-in", SAFI_UNICAST, &handler1); PolicyTableExport *policy_table = new PolicyTableExport("POLICY", SAFI_UNICAST, ribin_table, policy_filters, "test_neighbour", IPv4("1.2.3.4")); ribin_table->set_next_table(policy_table); DebugTable* debug_table = new DebugTable("D1", (BGPRouteTable*)policy_table); policy_table->set_next_table(debug_table); debug_table->set_output_file(filename); debug_table->set_canned_response(ADD_USED); // create a load of attributes IPNet net1("1.0.1.0/24"); // IPNet net2("1.0.2.0/24"); IPv4 nexthop1("2.0.0.1"); NextHopAttribute nhatt1(nexthop1); IPv4 nexthop2("2.0.0.2"); NextHopAttribute nhatt2(nexthop2); IPv4 nexthop3("2.0.0.3"); NextHopAttribute nhatt3(nexthop3); OriginAttribute igp_origin_att(IGP); ASPath aspath1; aspath1.prepend_as(AsNum(1)); aspath1.prepend_as(AsNum(2)); aspath1.prepend_as(AsNum(3)); ASPathAttribute aspathatt1(aspath1); ASPath aspath2; aspath2.prepend_as(AsNum(4)); aspath2.prepend_as(AsNum(5)); aspath2.prepend_as(AsNum(6)); ASPathAttribute aspathatt2(aspath2); ASPath aspath3; aspath3.prepend_as(AsNum(7)); aspath3.prepend_as(AsNum(8)); aspath3.prepend_as(AsNum(9)); ASPathAttribute aspathatt3(aspath3); FPAList4Ref fpalist1 = new FastPathAttributeList(nhatt1, aspathatt1, igp_origin_att); PAListRef palist1 = new PathAttributeList(fpalist1); FPAList4Ref fpalist2 = new FastPathAttributeList(nhatt2, aspathatt2, igp_origin_att); PAListRef palist2 = new PathAttributeList(fpalist2); FPAList4Ref fpalist3 = new FastPathAttributeList(nhatt3, aspathatt3, igp_origin_att); PAListRef palist3 = new PathAttributeList(fpalist3); // create a subnet route SubnetRoute *sr1, *sr2; UNUSED(sr2); InternalMessage* msg; // ================================================================ // Test1: trivial add and delete before config // ================================================================ // add a route debug_table->write_comment("TEST 1"); debug_table->write_comment("ADD AND DELETE UNFILTERED"); sr1 = new SubnetRoute(net1, palist1, NULL); msg = new InternalMessage(sr1, &handler1, 1); policy_table->add_route(*msg, ribin_table); // delete the route policy_table->delete_route(*msg, ribin_table); debug_table->write_separator(); sr1->unref(); delete msg; // ================================================================ // Test2: trivial add then configure then delete // ================================================================ // add a route debug_table->write_comment("TEST 2"); debug_table->write_comment("ADD, CONFIG, DELETE"); sr1 = new SubnetRoute(net1, palist1, NULL); msg = new InternalMessage(sr1, &handler1, 1); policy_table->add_route(*msg, ribin_table); debug_table->write_comment("CONFIGURE FILTER"); // Configure filter string filter_conf = "POLICY_START foo\n\ TERM_START foo2\n\ PUSH u32 200\n\ STORE 17\n\ ACCEPT\n\ TERM_END\n\ POLICY_END\n"; policy_filters.configure(filter::EXPORT, filter_conf); debug_table->write_comment("EXPECT DELETE TO NOT HAVE LOCALPREF"); // delete the route policy_table->delete_route(*msg, ribin_table); debug_table->write_separator(); sr1->unref(); delete msg; // ================================================================ // Test3: add and delete with configured policy // ================================================================ // add a route debug_table->write_comment("TEST 3"); debug_table->write_comment("ADD AND DELETE FILTERED"); debug_table->write_comment("EXPECT ROUTES TO HAVE LOCALPREF"); sr1 = new SubnetRoute(net1, palist1, NULL); msg = new InternalMessage(sr1, &handler1, 1); policy_table->add_route(*msg, ribin_table); // delete the route policy_table->delete_route(*msg, ribin_table); debug_table->write_separator(); sr1->unref(); delete msg; // ================================================================ debug_table->write_comment("SHUTDOWN AND CLEAN UP"); delete ribin_table; delete policy_table; delete debug_table; palist1.release(); palist2.release(); palist3.release(); fpalist1 = 0; fpalist2 = 0; fpalist3 = 0; FILE *file = fopen(filename.c_str(), "r"); if (file == NULL) { fprintf(stderr, "Failed to read %s\n", filename.c_str()); fprintf(stderr, "TEST POLICY EXPORT FAILED\n"); fclose(file); return false; } #define BUFSIZE 8192 char testout[BUFSIZE]; memset(testout, 0, BUFSIZE); int bytes1 = fread(testout, 1, BUFSIZE, file); if (bytes1 == BUFSIZE) { fprintf(stderr, "Output too long for buffer\n"); fprintf(stderr, "TEST POLICY EXPORT FAILED\n"); fclose(file); return false; } fclose(file); string ref_filename; const char* srcdir = getenv("srcdir"); if (srcdir) { ref_filename = string(srcdir); } else { ref_filename = "."; } ref_filename += "/test_policy_export.reference"; file = fopen(ref_filename.c_str(), "r"); if (file == NULL) { fprintf(stderr, "Failed to read %s\n", ref_filename.c_str()); fprintf(stderr, "TEST POLICY EXPORT FAILED\n"); fclose(file); return false; } char refout[BUFSIZE]; memset(refout, 0, BUFSIZE); int bytes2 = fread(refout, 1, BUFSIZE, file); if (bytes2 == BUFSIZE) { fprintf(stderr, "Output too long for buffer\n"); fprintf(stderr, "TEST POLICY EXPORT FAILED\n"); fclose(file); return false; } fclose(file); if ((bytes1 != bytes2) || (memcmp(testout, refout, bytes1) != 0)) { fprintf(stderr, "Output in %s doesn't match reference output\n", filename.c_str()); fprintf(stderr, "TEST POLICY EXPORT FAILED\n"); return false; } #ifndef HOST_OS_WINDOWS unlink(filename.c_str()); #else DeleteFileA(filename.c_str()); #endif return true; } bool test_policy(TestInfo& /*info*/) { #ifndef HOST_OS_WINDOWS struct passwd *pwd = getpwuid(getuid()); string filename = "/tmp/test_policy."; filename += pwd->pw_name; #else char *tmppath = (char *)malloc(256); GetTempPathA(256, tmppath); string filename = string(tmppath) + "test_policy"; free(tmppath); #endif EventLoop eventloop; BGPMain bgpmain(eventloop); LocalData localdata(bgpmain.eventloop()); Iptuple iptuple; BGPPeerData *pd1 = new BGPPeerData(localdata, iptuple, AsNum(0), IPv4(),0); BGPPeer peer1(&localdata, pd1, NULL, &bgpmain); PeerHandler handler1("test1", &peer1, NULL, NULL); BGPPeerData *pd2 = new BGPPeerData(localdata, iptuple, AsNum(0), IPv4(),0); BGPPeer peer2(&localdata, pd2, NULL, &bgpmain); PeerHandler handler2("test2", &peer2, NULL, NULL); VersionFilters policy_filters; // Trivial plumbing. We're not testing the RibInTable here, so // mostly we'll just inject directly into the PolicyTable, but we // need an RibInTable here to test lookup_route. RibInTable *ribin_table = new RibInTable("RIB-in", SAFI_UNICAST, &handler1); PolicyTableImport *policy_table_import = new PolicyTableImport("POLICY", SAFI_UNICAST, ribin_table, policy_filters, IPv4("1.2.3.4"), // peer IPv4("1.2.3.5")); // self ribin_table->set_next_table(policy_table_import); FanoutTable *fanout_table = new FanoutTable("FANOUT", SAFI_UNICAST, policy_table_import, NULL, NULL); policy_table_import->set_next_table(fanout_table); PolicyTableExport *policy_table_export = new PolicyTableExport("POLICY", SAFI_UNICAST, fanout_table, policy_filters, "test_neighbour", IPv4("1.2.3.4")); DebugTable* debug_table = new DebugTable("D1", (BGPRouteTable*)policy_table_export); policy_table_export->set_next_table(debug_table); fanout_table->add_next_table(policy_table_export, &handler2, 1); debug_table->set_output_file(filename); debug_table->set_canned_response(ADD_USED); // create a load of attributes IPNet net1("1.0.1.0/24"); // IPNet net2("1.0.2.0/24"); IPv4 nexthop1("2.0.0.1"); NextHopAttribute nhatt1(nexthop1); IPv4 nexthop2("2.0.0.2"); NextHopAttribute nhatt2(nexthop2); IPv4 nexthop3("2.0.0.3"); NextHopAttribute nhatt3(nexthop3); OriginAttribute igp_origin_att(IGP); ASPath aspath1; aspath1.prepend_as(AsNum(1)); aspath1.prepend_as(AsNum(2)); aspath1.prepend_as(AsNum(3)); ASPathAttribute aspathatt1(aspath1); ASPath aspath2; aspath2.prepend_as(AsNum(4)); aspath2.prepend_as(AsNum(5)); aspath2.prepend_as(AsNum(6)); ASPathAttribute aspathatt2(aspath2); ASPath aspath3; aspath3.prepend_as(AsNum(7)); aspath3.prepend_as(AsNum(8)); aspath3.prepend_as(AsNum(9)); ASPathAttribute aspathatt3(aspath3); FPAList4Ref fpalist1 = new FastPathAttributeList(nhatt1, aspathatt1, igp_origin_att); PAListRef palist1 = new PathAttributeList(fpalist1); FPAList4Ref fpalist2 = new FastPathAttributeList(nhatt2, aspathatt2, igp_origin_att); PAListRef palist2 = new PathAttributeList(fpalist2); FPAList4Ref fpalist3 = new FastPathAttributeList(nhatt3, aspathatt3, igp_origin_att); PAListRef palist3 = new PathAttributeList(fpalist3); // create a subnet route SubnetRoute *sr1, *sr2; UNUSED(sr2); InternalMessage* msg; // ================================================================ // Test1: trivial add and delete before config // ================================================================ // add a route debug_table->write_comment("TEST 1"); debug_table->write_comment("ADD AND DELETE UNFILTERED"); sr1 = new SubnetRoute(net1, palist1, NULL); sr1->set_nexthop_resolved(true); //fpalist1->set_nexthop_resolved(true); msg = new InternalMessage(sr1, fpalist1, &handler1, 1); policy_table_import->add_route(*msg, ribin_table); fanout_table->get_next_message(policy_table_export); fpalist1 = 0; delete msg; // delete the route fpalist1 = new FastPathAttributeList(nhatt1, aspathatt1, igp_origin_att); msg = new InternalMessage(sr1, fpalist1, &handler1, 1); policy_table_import->delete_route(*msg, ribin_table); fanout_table->get_next_message(policy_table_export); debug_table->write_separator(); fpalist1 = 0; sr1->unref(); delete msg; // ================================================================ // Test2: trivial add then configure then dump // ================================================================ // add a route debug_table->write_comment("TEST 2"); debug_table->write_comment("ADD, CONFIG, DELETE"); fpalist1 = new FastPathAttributeList(nhatt1, aspathatt1, igp_origin_att); sr1 = new SubnetRoute(net1, palist1, NULL); sr1->set_nexthop_resolved(true); msg = new InternalMessage(sr1, fpalist1, &handler1, 1); policy_table_import->add_route(*msg, ribin_table); fanout_table->get_next_message(policy_table_export); fpalist1 = 0; delete msg; debug_table->write_comment("CONFIGURE EXPORT FILTER"); // Configure export filter string filter_export_conf = "POLICY_START foo\n\ TERM_START foo2\n\ PUSH u32 200\n\ STORE 17\n\ ACCEPT\n\ TERM_END\n\ POLICY_END\n"; policy_filters.configure(filter::EXPORT, filter_export_conf); // Configure import filter string filter_import_conf = "POLICY_START foo\n\ TERM_START foo2\n\ PUSH u32 100\n\ STORE 17\n\ ACCEPT\n\ TERM_END\n\ POLICY_END\n"; policy_filters.configure(filter::IMPORT, filter_import_conf); // dump the route debug_table->write_comment("DO DUMP"); debug_table->write_comment("EXPECT DELETE TO *NOT* HAVE LOCALPREF"); debug_table->write_comment("EXPECT ADD TO HAVE LOCALPREF OF 200"); fpalist1 = new FastPathAttributeList(nhatt1, aspathatt1, igp_origin_att); msg = new InternalMessage(sr1, fpalist1, &handler1, 1); policy_table_import->route_dump(*msg, ribin_table, NULL); fanout_table->get_next_message(policy_table_export); fanout_table->get_next_message(policy_table_export); fpalist1 = 0; delete msg; // delete the route debug_table->write_comment("EXPECT DELETE TO HAVE LOCALPREF OF 200"); fpalist1 = new FastPathAttributeList(nhatt1, aspathatt1, igp_origin_att); msg = new InternalMessage(sr1, fpalist1, &handler1, 1); policy_table_import->delete_route(*msg, ribin_table); fanout_table->get_next_message(policy_table_export); debug_table->write_separator(); fpalist1 = 0; sr1->unref(); delete msg; // ================================================================ // Test3: add and delete with configured policy // ================================================================ // add a route debug_table->write_comment("TEST 3"); debug_table->write_comment("ADD AND DELETE FILTERED"); debug_table->write_comment("EXPECT ROUTES TO HAVE LOCALPREF"); sr1 = new SubnetRoute(net1, palist1, NULL); sr1->set_nexthop_resolved(true); fpalist1 = new FastPathAttributeList(nhatt1, aspathatt1, igp_origin_att); msg = new InternalMessage(sr1, fpalist1, &handler1, 1); policy_table_import->add_route(*msg, ribin_table); fanout_table->get_next_message(policy_table_export); // delete the route policy_table_import->delete_route(*msg, ribin_table); fanout_table->get_next_message(policy_table_export); debug_table->write_separator(); sr1->unref(); delete msg; // ================================================================ debug_table->write_comment("SHUTDOWN AND CLEAN UP"); delete ribin_table; delete policy_table_import; delete fanout_table; delete policy_table_export; delete debug_table; palist1.release(); palist2.release(); palist3.release(); fpalist1 = 0; fpalist2 = 0; fpalist3 = 0; FILE *file = fopen(filename.c_str(), "r"); if (file == NULL) { fprintf(stderr, "Failed to read %s\n", filename.c_str()); fprintf(stderr, "TEST POLICY FAILED\n"); fclose(file); return false; } #define BUFSIZE 8192 char testout[BUFSIZE]; memset(testout, 0, BUFSIZE); int bytes1 = fread(testout, 1, BUFSIZE, file); if (bytes1 == BUFSIZE) { fprintf(stderr, "Output too long for buffer\n"); fprintf(stderr, "TEST POLICY FAILED\n"); fclose(file); return false; } fclose(file); string ref_filename; const char* srcdir = getenv("srcdir"); if (srcdir) { ref_filename = string(srcdir); } else { ref_filename = "."; } ref_filename += "/test_policy.reference"; file = fopen(ref_filename.c_str(), "r"); if (file == NULL) { fprintf(stderr, "Failed to read %s\n", ref_filename.c_str()); fprintf(stderr, "TEST POLICY FAILED\n"); fclose(file); return false; } char refout[BUFSIZE]; memset(refout, 0, BUFSIZE); int bytes2 = fread(refout, 1, BUFSIZE, file); if (bytes2 == BUFSIZE) { fprintf(stderr, "Output too long for buffer\n"); fprintf(stderr, "TEST POLICY FAILED\n"); fclose(file); return false; } fclose(file); if ((bytes1 != bytes2) || (memcmp(testout, refout, bytes1) != 0)) { fprintf(stderr, "Output in %s doesn't match reference output\n", filename.c_str()); fprintf(stderr, "TEST POLICY FAILED\n"); return false; } #ifndef HOST_OS_WINDOWS unlink(filename.c_str()); #else DeleteFileA(filename.c_str()); #endif return true; } bool test_policy_dump(TestInfo& /*info*/) { #ifndef HOST_OS_WINDOWS struct passwd *pwd = getpwuid(getuid()); string filename = "/tmp/test_policy_dump."; filename += pwd->pw_name; #else char *tmppath = (char *)malloc(256); GetTempPathA(256, tmppath); string filename = string(tmppath) + "test_policy_dump"; free(tmppath); #endif EventLoop eventloop; BGPMain bgpmain(eventloop); LocalData localdata(bgpmain.eventloop()); Iptuple iptuple; BGPPeerData *pd1 = new BGPPeerData(localdata, iptuple, AsNum(0), IPv4(),0); BGPPeer peer1(&localdata, pd1, NULL, &bgpmain); PeerHandler handler1("test1", &peer1, NULL, NULL); BGPPeerData *pd2 = new BGPPeerData(localdata, iptuple, AsNum(0), IPv4(),0); BGPPeer peer2(&localdata, pd2, NULL, &bgpmain); PeerHandler handler2("test2", &peer2, NULL, NULL); VersionFilters policy_filters; DummyNextHopResolver next_hop_resolver(bgpmain.eventloop(), bgpmain); RibInTable *ribin_table = new RibInTable("RIB-in", SAFI_UNICAST, &handler1); FilterTable *filter_table = new FilterTable("FILTER", SAFI_UNICAST, ribin_table, next_hop_resolver); filter_table->add_AS_prepend_filter(AsNum(1000), false); ribin_table->set_next_table(filter_table); PolicyTableImport *policy_table_import = new PolicyTableImport("POLICY", SAFI_UNICAST, filter_table, policy_filters, IPv4("1.2.3.4"), // peer IPv4("1.2.3.5")); // self filter_table->set_next_table(policy_table_import); CacheTable *cache_table = new CacheTable("CACHE", SAFI_UNICAST, policy_table_import, &handler1); policy_table_import->set_next_table(cache_table); NhLookupTable* nhlookup_table = new NhLookupTable("NHLOOKUP", SAFI_UNICAST, &next_hop_resolver, cache_table); cache_table->set_next_table(nhlookup_table); DecisionTable *decision_table = new DecisionTable("DECISION", SAFI_UNICAST, next_hop_resolver); nhlookup_table->set_next_table(decision_table); decision_table->add_parent(nhlookup_table, &handler1, ribin_table->genid()); PolicyTableSourceMatch *policy_table_sm = new PolicyTableSourceMatch("POLICY_SM", SAFI_UNICAST, decision_table, policy_filters, bgpmain.eventloop()); decision_table->set_next_table(policy_table_sm); FanoutTable *fanout_table = new FanoutTable("FANOUT", SAFI_UNICAST, policy_table_sm, NULL, NULL); policy_table_sm->set_next_table(fanout_table); PolicyTableExport *policy_table_export = new PolicyTableExport("POLICY", SAFI_UNICAST, fanout_table, policy_filters, "test_neighbour", IPv4("1.2.3.4")); DebugTable* debug_table = new DebugTable("D1", (BGPRouteTable*)policy_table_export); policy_table_export->set_next_table(debug_table); fanout_table->add_next_table(policy_table_export, &handler2, 1); debug_table->set_output_file(filename); debug_table->set_canned_response(ADD_USED); // create a load of attributes IPNet net1("1.0.1.0/24"); // IPNet net2("1.0.2.0/24"); IPv4 nexthop1("2.0.0.1"); NextHopAttribute nhatt1(nexthop1); IPv4 nexthop2("2.0.0.2"); NextHopAttribute nhatt2(nexthop2); IPv4 nexthop3("2.0.0.3"); NextHopAttribute nhatt3(nexthop3); next_hop_resolver.set_nexthop_metric(nexthop1, 27); next_hop_resolver.set_nexthop_metric(nexthop2, 27); next_hop_resolver.set_nexthop_metric(nexthop3, 27); OriginAttribute igp_origin_att(IGP); ASPath aspath1; aspath1.prepend_as(AsNum(1)); aspath1.prepend_as(AsNum(2)); aspath1.prepend_as(AsNum(3)); ASPathAttribute aspathatt1(aspath1); ASPath aspath2; aspath2.prepend_as(AsNum(4)); aspath2.prepend_as(AsNum(5)); aspath2.prepend_as(AsNum(6)); ASPathAttribute aspathatt2(aspath2); ASPath aspath3; aspath3.prepend_as(AsNum(7)); aspath3.prepend_as(AsNum(8)); aspath3.prepend_as(AsNum(9)); ASPathAttribute aspathatt3(aspath3); FPAList4Ref fpalist1 = new FastPathAttributeList(nhatt1, aspathatt1, igp_origin_att); PAListRef palist1 = new PathAttributeList(fpalist1); FPAList4Ref fpalist2 = new FastPathAttributeList(nhatt2, aspathatt2, igp_origin_att); PAListRef palist2 = new PathAttributeList(fpalist2); FPAList4Ref fpalist3 = new FastPathAttributeList(nhatt3, aspathatt3, igp_origin_att); PAListRef palist3 = new PathAttributeList(fpalist3); PolicyTags pt; // ================================================================ // Test: trivial add then configure then dump // ================================================================ // add a route debug_table->write_comment("TEST 1"); debug_table->write_comment("ADD, CONFIG, DELETE"); //sr1->set_nexthop_resolved(true); ribin_table->add_route(net1, fpalist1, pt); fanout_table->get_next_message(policy_table_export); debug_table->write_comment("CONFIGURE EXPORT FILTER"); string filter_export_conf = ""; string filter_import_conf = ""; string filter_sm_conf = ""; #if 1 // Configure export filter filter_export_conf = "POLICY_START foo\n\ TERM_START foo2\n\ PUSH u32 200\n\ STORE 17\n\ ACCEPT\n\ TERM_END\n\ POLICY_END\n"; #endif policy_filters.configure(filter::EXPORT, filter_export_conf); #if 0 // Configure import filter filter_import_conf = "POLICY_START foo\n\ TERM_START foo2\n\ PUSH u32 100\n\ STORE 17\n\ ACCEPT\n\ TERM_END\n\ POLICY_END\n"; #endif policy_filters.configure(filter::IMPORT, filter_import_conf); #if 0 // Configure source match filter filter_sm_conf = "POLICY_START foo\n\ TERM_START foo2\n\ PUSH u32 150\n\ STORE 17\n\ ACCEPT\n\ TERM_END\n\ POLICY_END\n"; #endif policy_filters.configure(filter::EXPORT_SOURCEMATCH, filter_sm_conf); debug_table->write_comment("PUSH_ROUTES"); list*> peer_list; peer_list.push_back(new PeerTableInfo(NULL, &handler1, ribin_table->genid())); policy_table_sm->push_routes(peer_list); delete peer_list.front(); // dump the route debug_table->write_comment("RUN EVENT LOOP TO COMPLETION"); debug_table->write_comment("EXPECT DELETE TO *NOT* HAVE LOCALPREF"); debug_table->write_comment("EXPECT ADD TO HAVE LOCALPREF OF 200"); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } //fanout_table->crash_dump(); fanout_table->get_next_message(policy_table_export); fanout_table->get_next_message(policy_table_export); fanout_table->get_next_message(policy_table_export); // delete the route debug_table->write_comment("EXPECT DELETE TO HAVE LOCALPREF OF 200"); ribin_table->delete_route(net1); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } fanout_table->get_next_message(policy_table_export); fanout_table->get_next_message(policy_table_export); debug_table->write_separator(); // ================================================================ debug_table->write_comment("SHUTDOWN AND CLEAN UP"); delete ribin_table; delete filter_table; delete policy_table_import; delete cache_table; delete nhlookup_table; delete decision_table; delete policy_table_sm; delete fanout_table; delete policy_table_export; delete debug_table; palist1.release(); palist2.release(); palist3.release(); fpalist1 = 0; fpalist2 = 0; fpalist3 = 0; FILE *file = fopen(filename.c_str(), "r"); if (file == NULL) { fprintf(stderr, "Failed to read %s\n", filename.c_str()); fprintf(stderr, "TEST POLICY FAILED\n"); fclose(file); return false; } #define BUFSIZE 8192 char testout[BUFSIZE]; memset(testout, 0, BUFSIZE); int bytes1 = fread(testout, 1, BUFSIZE, file); if (bytes1 == BUFSIZE) { fprintf(stderr, "Output too long for buffer\n"); fprintf(stderr, "TEST POLICY FAILED\n"); fclose(file); return false; } fclose(file); string ref_filename; const char* srcdir = getenv("srcdir"); if (srcdir) { ref_filename = string(srcdir); } else { ref_filename = "."; } ref_filename += "/test_policy_dump.reference"; file = fopen(ref_filename.c_str(), "r"); if (file == NULL) { fprintf(stderr, "Failed to read %s\n", ref_filename.c_str()); fprintf(stderr, "TEST POLICY FAILED\n"); fclose(file); return false; } char refout[BUFSIZE]; memset(refout, 0, BUFSIZE); int bytes2 = fread(refout, 1, BUFSIZE, file); if (bytes2 == BUFSIZE) { fprintf(stderr, "Output too long for buffer\n"); fprintf(stderr, "TEST POLICY FAILED\n"); fclose(file); return false; } fclose(file); if ((bytes1 != bytes2) || (memcmp(testout, refout, bytes1) != 0)) { fprintf(stderr, "Output in %s doesn't match reference output\n", filename.c_str()); fprintf(stderr, "TEST POLICY FAILED\n"); return false; } #ifndef HOST_OS_WINDOWS unlink(filename.c_str()); #else DeleteFileA(filename.c_str()); #endif return true; } xorp/bgp/tests/test_leaks.sh0000775000076400007640000000442411421137511016267 0ustar greearbgreearb#!/bin/sh # This script runs some of the libxipc tests through Erwin Andreason # and Henner Zeller's LeakTracer in an attempt to spot memory leaks. # It is little more than glue to automate both elements of the leak # detection method - ie generating leak output and correlating it with # the source code. # # Synopsis: # spot_leaks # # Description: # Attempt to run command and look for leaks. The supplied command # must be a dynamically linked C++ program since LeakTracer uses dynamic # link loading to override the new and delete operators in libstdc++.XX.so. # spot_leaks() { local cxxlibs if [ $# -eq 0 ] ; then echo "No command supplied to spots leak." return 1; fi if [ ! -x ${1} ] ; then echo "\"$1\" either does not exist or is not executable" return 1 fi cxxlibs=`ldd $1 2>/dev/null | grep c++` if [ $? -ne 0 ] ; then echo "$1 does not appear to be dynamically linked." return 0 fi if [ -z "${cxxlibs}" ] ; then echo "$1 does not appear to be a C++ generated executable (ignoring)." return 0 fi status=">>> Running LeakTracer on \"$*\"" echo "$status" LeakCheck $* 1>/dev/null 2>&1 leak-analyze $1 1>leak-log 2>warn-log grep -q 'Gathered' warn-log if [ $? -ne 0 ] ; then echo "Did not appear to generate data points" cat warn-log leak-log return 1 fi # Get number of unique points, ie leaks leaks=`cat warn-log | sed -n '1s/[^(]*(\([0-9][0-9]*\).*/\1/p'` ret=0 if [ "${leaks}" -eq 0 ] ; then echo " ==> No leaks detected." else echo " ==> Leaks detected." cat warn-log leak-log ret=1 fi return ${ret} } tidy_up() { rm -rf warn-log leak-log } # # Check LeakTracer is installed. # have_leak_check="no" for p in `echo ${PATH} | tr ':' ' '` ; do if [ -x $p/LeakCheck ] ; then have_leak_check=yes break fi done if [ "$have_leak_check" = "no" ] ; then echo "LeakCheck binary not found skipping test." exit 0 fi # # These are the "simple" libxipc test programs, i.e. they take no # arguments. # SIMPLE_TESTS="./test_packet \ ./test_plumbing \ ./aspath_test \ ./test_packet_coding ./isolation_tests \ ./test_peer_data" failures=0 for t in ${SIMPLE_TESTS} ; do spot_leaks $t [ $? -eq 0 ] || failures=1 done tidy_up exit ${failures} xorp/bgp/tests/test_peer_data.cc0000664000076400007640000001231011421137511017055 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #include "bgp_module.h" #include "libxorp/xorp.h" #include "libxorp/test_main.hh" #include "libxorp/debug.h" #include "libxorp/ipv4.hh" #include "libxorp/ipv6.hh" #include "libxorp/asnum.hh" #include "iptuple.hh" #include "parameter.hh" #include "peer_data.hh" bool test1(TestInfo& info) { DOUT(info) << "test1: " << endl; assert(BGPPeerData::SENT < BGPPeerData::ARRAY_SIZE); assert(BGPPeerData::RECEIVED < BGPPeerData::ARRAY_SIZE); assert(BGPPeerData::NEGOTIATED < BGPPeerData::ARRAY_SIZE); return true; } /* ** Fill in a parameter list structure with the parameters a peer might ** send. */ void open_packet_parameters(ParameterList& parameters) { parameters.push_back(new BGPRefreshCapability()); parameters.push_back(new BGPMultiProtocolCapability(AFI_IPV4, SAFI_UNICAST)); parameters.push_back(new BGPMultiProtocolCapability(AFI_IPV6, SAFI_UNICAST)); parameters.push_back(new BGPMultiProtocolCapability(AFI_IPV4, SAFI_MULTICAST)); parameters.push_back(new BGPMultiProtocolCapability(AFI_IPV6, SAFI_MULTICAST)); } bool test2(TestInfo& info) { DOUT(info) << "test2: " << endl; /* ** Create a PeerData structure. */ EventLoop eventloop; LocalData localdata(eventloop); Iptuple iptuple; BGPPeerData pd(localdata, iptuple, AsNum(12), IPv4("10.10.10.10"), 0); if(pd.multiprotocol(SAFI_UNICAST)) { DOUT(info) << "We should not offer unicast IPv4 by default\n"; return false; } if(pd.multiprotocol(SAFI_UNICAST)) { DOUT(info) << "We should not offer unicast IPv6 by default\n"; return false; } /* ** We support Unicast IPv4. */ pd.add_sent_parameter(new BGPMultiProtocolCapability(AFI_IPV4, SAFI_UNICAST)); /* ** We support Unicast IPv6. */ pd.add_sent_parameter(new BGPMultiProtocolCapability(AFI_IPV6, SAFI_UNICAST)); /* ** Create a parameter list analogous to the list we get from an ** open packet (our peer). */ ParameterList parameters; open_packet_parameters(parameters); /* ** Pass the paremeters into the PeerData as we would do from an ** open packet. */ pd.save_parameters(parameters); /* ** Do the negotiation. */ pd.open_negotiation(); if(!pd.multiprotocol(SAFI_UNICAST)) { DOUT(info) << "We should still be offering unicast IPv4\n"; return false; } /* ** We should have decided that unicast IPv6 is a go. */ if(!pd.multiprotocol(SAFI_UNICAST)) { DOUT(info) << "We did not manage to negotiate IPv6\n"; return false; } /* ** Create a parameter list analogous to the list we get from an ** open packet (our peer). */ ParameterList np; open_packet_parameters(np); /* ** Remove the parameters. */ ParameterList::iterator i; for(i = np.begin(); i != np.end(); i++) pd.remove_recv_parameter(*i); /* ** Verify that the list is empty. */ if(!pd.parameter_recv_list().empty()) { DOUT(info) << "The receive list should be empty\n"; ParameterList pl = pd.parameter_recv_list(); for(ParameterList::iterator i = pl.begin(); i != pl.end(); i++) DOUT(info) << "Parameter = " << (*i)->str() << endl; return false; } return true; } int main(int argc, char** argv) { XorpUnexpectedHandler x(xorp_unexpected_handler); xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); TestMain t(argc, argv); string test_name = t.get_optional_args("-t", "--test", "run only the specified test"); t.complete_args_parsing(); try { struct test { string test_name; XorpCallback1::RefPtr cb; } tests[] = { {"test1", callback(test1)}, {"test2", callback(test2)}, }; if("" == test_name) { for(unsigned int i = 0; i < sizeof(tests) / sizeof(struct test); i++) t.run(tests[i].test_name, tests[i].cb); } else { for(unsigned int i = 0; i < sizeof(tests) / sizeof(struct test); i++) if(test_name == tests[i].test_name) { t.run(tests[i].test_name, tests[i].cb); return t.exit(); } t.failed("No test with name " + test_name + " found\n"); } } catch(...) { xorp_catch_standard_exceptions(); } xlog_stop(); xlog_exit(); return t.exit(); } xorp/bgp/tests/test_deletion.reference0000664000076400007640000004553311421137511020322 0ustar greearbgreearb[comment] **************************************************************** [comment] TEST 1 [comment] TRIVIAL ADD AND PEERING GOES DOWN [separator]------------------------------------- [comment] ADD A ROUTE [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [separator]------------------------------------- [comment] PEERING GOES DOWN [PUSH] [separator]------------------------------------- [comment] LET EVENT QUEUE DRAIN [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [PUSH] [separator]------------------------------------- [comment] PEERING COMES UP [separator]------------------------------------- [comment] **************************************************************** [comment] TEST 2 [comment] 3 ADDS, SAME PALIST, PEERING GOES DOWN [separator]------------------------------------- [comment] ADD A ROUTE [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [separator]------------------------------------- [comment] ADD A ROUTE [ADD] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [separator]------------------------------------- [comment] ADD A ROUTE [ADD] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [separator]------------------------------------- [comment] PEERING GOES DOWN [PUSH] [separator]------------------------------------- [comment] LET EVENT QUEUE DRAIN [DELETE] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [DELETE] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [PUSH] [separator]------------------------------------- [comment] PEERING COMES UP [separator]------------------------------------- [comment] **************************************************************** [comment] TEST 3 [comment] 3 ADDS, 3 PALISTS, PEERING GOES DOWN [separator]------------------------------------- [comment] ADD A ROUTE [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [separator]------------------------------------- [comment] ADD A ROUTE [ADD] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/5, AS/4, AS/3] [separator]------------------------------------- [comment] ADD A ROUTE [ADD] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/8, AS/7, AS/6, AS/6] [separator]------------------------------------- [comment] PEERING GOES DOWN [PUSH] [separator]------------------------------------- [comment] LET EVENT QUEUE DRAIN [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [PUSH] [DELETE] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/5, AS/4, AS/3] [PUSH] [DELETE] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/8, AS/7, AS/6, AS/6] [PUSH] [separator]------------------------------------- [comment] PEERING COMES UP [separator]------------------------------------- [comment] **************************************************************** [comment] TEST 4 [comment] 4 ADDS, 2 PALISTS, PEERING GOES DOWN [separator]------------------------------------- [comment] ADD A ROUTE [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [separator]------------------------------------- [comment] ADD A ROUTE [ADD] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/5, AS/4, AS/3] [separator]------------------------------------- [comment] ADD A ROUTE [ADD] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [separator]------------------------------------- [comment] ADD A ROUTE [ADD] SubnetRoute: Net: 1.0.4.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/5, AS/4, AS/3] [separator]------------------------------------- [comment] PEERING GOES DOWN [PUSH] [separator]------------------------------------- [comment] LET EVENT QUEUE DRAIN [DELETE] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [PUSH] [DELETE] SubnetRoute: Net: 1.0.4.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/5, AS/4, AS/3] [DELETE] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/5, AS/4, AS/3] [PUSH] [separator]------------------------------------- [comment] PEERING COMES UP [separator]------------------------------------- [comment] **************************************************************** [comment] TEST 5 [comment] BOUNCE THE PEERING AGAIN [separator]------------------------------------- [comment] PEERING GOES DOWN [PUSH] [separator]------------------------------------- [comment] LET EVENT QUEUE DRAIN [separator]------------------------------------- [comment] PEERING COMES UP [separator]------------------------------------- [comment] LET EVENT QUEUE DRAIN [separator]------------------------------------- [comment] **************************************************************** [comment] TEST 6 [comment] 4 ADDS, 2 PALISTS, PEERING GOES DOWN [comment] 2 MORE ADDS BEFORE PROCESSING TIMERS [separator]------------------------------------- [comment] ADD A ROUTE [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [separator]------------------------------------- [comment] ADD A ROUTE [ADD] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/5, AS/4, AS/3] [separator]------------------------------------- [comment] ADD A ROUTE [ADD] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [separator]------------------------------------- [comment] ADD A ROUTE [ADD] SubnetRoute: Net: 1.0.4.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/5, AS/4, AS/3] [separator]------------------------------------- [comment] PEERING GOES DOWN [PUSH] [separator]------------------------------------- [comment] PEERING COMES UP [separator]------------------------------------- [comment] ADD A ROUTE [REPLACE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [separator]------------------------------------- [comment] ADD A ROUTE [REPLACE] SubnetRoute: Net: 1.0.4.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/5, AS/4, AS/3] SubnetRoute: Net: 1.0.4.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/5, AS/4, AS/3] [separator]------------------------------------- [comment] LET EVENT QUEUE DRAIN [DELETE] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [PUSH] [DELETE] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/5, AS/4, AS/3] [PUSH] [separator]------------------------------------- [comment] PEERING COMES UP [separator]------------------------------------- [comment] PEERING GOES DOWN [PUSH] [separator]------------------------------------- [comment] LET EVENT QUEUE DRAIN [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [PUSH] [DELETE] SubnetRoute: Net: 1.0.4.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/5, AS/4, AS/3] [PUSH] [separator]------------------------------------- [comment] PEERING COMES UP [comment] **************************************************************** [comment] TEST 7 [comment] 4 ADDS, 2 PALISTS, PEERING GOES DOWN [comment] 2 MORE ADDS BEFORE PROCESSING TIMERS [comment] AS TEST 6, BUT NEW PALISTS ARE DIFFERENT [separator]------------------------------------- [comment] ADD A ROUTE [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [separator]------------------------------------- [comment] ADD A ROUTE [ADD] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/5, AS/4, AS/3] [separator]------------------------------------- [comment] ADD A ROUTE [ADD] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [separator]------------------------------------- [comment] ADD A ROUTE [ADD] SubnetRoute: Net: 1.0.4.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/5, AS/4, AS/3] [separator]------------------------------------- [comment] PEERING GOES DOWN [PUSH] [separator]------------------------------------- [comment] PEERING COMES UP [separator]------------------------------------- [comment] ADD A ROUTE [REPLACE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/5, AS/4, AS/3] [separator]------------------------------------- [comment] ADD A ROUTE [REPLACE] SubnetRoute: Net: 1.0.4.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/5, AS/4, AS/3] SubnetRoute: Net: 1.0.4.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/8, AS/7, AS/6, AS/6] [separator]------------------------------------- [comment] LET EVENT QUEUE DRAIN [DELETE] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [PUSH] [DELETE] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/5, AS/4, AS/3] [PUSH] [separator]------------------------------------- [comment] PEERING COMES UP [separator]------------------------------------- [comment] PEERING GOES DOWN [PUSH] [separator]------------------------------------- [comment] LET EVENT QUEUE DRAIN [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/5, AS/4, AS/3] [PUSH] [DELETE] SubnetRoute: Net: 1.0.4.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/8, AS/7, AS/6, AS/6] [PUSH] [separator]------------------------------------- [comment] PEERING COMES UP [comment] **************************************************************** [comment] TEST 8 [comment] 4 ADDS, 2 PALISTS, PEERING GOES DOWN [comment] 2 MORE ADDS BEFORE PROCESSING TIMERS [comment] AS TEST 6, NEW ROUTES SHOULD DELETE CHAIN [separator]------------------------------------- [comment] ADD A ROUTE [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [separator]------------------------------------- [comment] ADD A ROUTE [ADD] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/5, AS/4, AS/3] [separator]------------------------------------- [comment] ADD A ROUTE [ADD] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [separator]------------------------------------- [comment] ADD A ROUTE [ADD] SubnetRoute: Net: 1.0.4.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/5, AS/4, AS/3] [separator]------------------------------------- [comment] PEERING GOES DOWN [PUSH] [separator]------------------------------------- [comment] PEERING COMES UP [separator]------------------------------------- [comment] ADD A ROUTE [REPLACE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/8, AS/7, AS/6, AS/6] [separator]------------------------------------- [comment] ADD A ROUTE [REPLACE] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/8, AS/7, AS/6, AS/6] [separator]------------------------------------- [comment] LET EVENT QUEUE DRAIN [DELETE] SubnetRoute: Net: 1.0.4.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/5, AS/4, AS/3] [DELETE] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/5, AS/4, AS/3] [PUSH] [separator]------------------------------------- [comment] PEERING COMES UP [separator]------------------------------------- [comment] PEERING GOES DOWN [PUSH] [separator]------------------------------------- [comment] LET EVENT QUEUE DRAIN [DELETE] SubnetRoute: Net: 1.0.3.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/8, AS/7, AS/6, AS/6] [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.3 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/8, AS/7, AS/6, AS/6] [PUSH] [separator]------------------------------------- [comment] PEERING COMES UP [comment] **************************************************************** [comment] TEST 9 [comment] 2 ADDS, 2 PALISTS, PEERING GOES DOWN [comment] SAME 2 ADDS BEFORE PROCESSING TIMERS [comment] ADDS SHOULD REMOVE ALL ROUTE FROM DEL TAB [separator]------------------------------------- [comment] ADD A ROUTE [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [separator]------------------------------------- [comment] ADD A ROUTE [ADD] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/5, AS/4, AS/3] [separator]------------------------------------- [comment] PEERING GOES DOWN [PUSH] [separator]------------------------------------- [comment] PEERING COMES UP [separator]------------------------------------- [comment] ADD A ROUTE [REPLACE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [separator]------------------------------------- [comment] ADD A ROUTE [REPLACE] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/5, AS/4, AS/3] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/5, AS/4, AS/3] [separator]------------------------------------- [comment] LET EVENT QUEUE DRAIN [separator]------------------------------------- [comment] PEERING COMES UP [separator]------------------------------------- [comment] PEERING GOES DOWN [PUSH] [separator]------------------------------------- [comment] LET EVENT QUEUE DRAIN [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [PUSH] [DELETE] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/5, AS/4, AS/3] [PUSH] [separator]------------------------------------- [comment] PEERING COMES UP [comment] **************************************************************** [comment] TEST 10 [comment] TWO DELETION TABLES [separator]------------------------------------- [comment] ADD A ROUTE [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [separator]------------------------------------- [comment] PEERING GOES DOWN [PUSH] [separator]------------------------------------- [comment] PEERING COMES UP [separator]------------------------------------- [comment] ADD A ROUTE [ADD] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/5, AS/4, AS/3] [separator]------------------------------------- [comment] PEERING GOES DOWN [PUSH] [separator]------------------------------------- [comment] PEERING COMES UP [separator]------------------------------------- [comment] LET EVENT QUEUE DRAIN [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [PUSH] [DELETE] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/5, AS/4, AS/3] [PUSH] [separator]------------------------------------- [comment] PEERING COMES UP [comment] SHUTDOWN AND CLEAN UP xorp/bgp/tests/test_decision.cc0000664000076400007640000022734211540224220016737 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "bgp_module.h" #include "libxorp/xorp.h" #include "libxorp/xorpfd.hh" #include "libxorp/eventloop.hh" #include "libxorp/xlog.h" #include "libxorp/test_main.hh" #ifdef HAVE_PWD_H #include #endif #include "libcomm/comm_api.h" #include "bgp.hh" #include "route_table_base.hh" #include "route_table_filter.hh" #include "route_table_ribin.hh" #include "route_table_debug.hh" #include "path_attribute.hh" #include "local_data.hh" #include "dummy_next_hop_resolver.hh" bool test_decision(TestInfo& /*info*/) { #ifndef HOST_OS_WINDOWS struct passwd *pwd = getpwuid(getuid()); string filename = "/tmp/test_decision."; filename += pwd->pw_name; #else char *tmppath = (char *)malloc(256); GetTempPathA(256, tmppath); string filename = string(tmppath) + "test_decision"; free(tmppath); #endif EventLoop eventloop; BGPMain bgpmain(eventloop); LocalData localdata(bgpmain.eventloop()); localdata.set_as(AsNum(1)); comm_init(); Iptuple iptuple1("3.0.0.127", 179, "2.0.0.1", 179); BGPPeerData *peer_data1 = new BGPPeerData(localdata, iptuple1, AsNum(1), IPv4("2.0.0.1"), 30); peer_data1->compute_peer_type(); // start off with both being IBGP peer_data1->set_id("2.0.0.0"); BGPPeer peer1(&localdata, peer_data1, NULL, &bgpmain); PeerHandler handler1("test1", &peer1, NULL, NULL); Iptuple iptuple2("3.0.0.127", 179, "2.0.0.2", 179); BGPPeerData *peer_data2 = new BGPPeerData(localdata, iptuple2, AsNum(1), IPv4("2.0.0.2"), 30); peer_data2->compute_peer_type(); // start off with both being IBGP peer_data2->set_id("2.0.0.1"); BGPPeer peer2(&localdata, peer_data2, NULL, &bgpmain); PeerHandler handler2("test2", &peer2, NULL, NULL); Iptuple iptuple3("3.0.0.127", 179, "2.0.0.3", 179); BGPPeerData *peer_data3 = new BGPPeerData(localdata, iptuple2, AsNum(1), IPv4("2.0.0.3"), 30); peer_data3->compute_peer_type(); // start off with both being IBGP peer_data3->set_id("2.0.0.3"); BGPPeer peer3(&localdata, peer_data3, NULL, &bgpmain); PeerHandler handler3("test3", &peer3, NULL, NULL); DummyNextHopResolver next_hop_resolver(bgpmain.eventloop(), bgpmain); DecisionTable *decision_table = new DecisionTable("DECISION", SAFI_UNICAST, next_hop_resolver); DebugTable* debug_table = new DebugTable("D1", (BGPRouteTable*)decision_table); decision_table->set_next_table(debug_table); RibInTable* ribin_table1 = new RibInTable("RIB-IN1", SAFI_UNICAST, &handler1); NhLookupTable* nhlookup_table1 = new NhLookupTable("NHL-IN1", SAFI_UNICAST, &next_hop_resolver, ribin_table1); ribin_table1->set_next_table(nhlookup_table1); nhlookup_table1->set_next_table(decision_table); decision_table->add_parent(nhlookup_table1, &handler1, ribin_table1->genid()); RibInTable* ribin_table2 = new RibInTable("RIB-IN2", SAFI_UNICAST, &handler2); NhLookupTable* nhlookup_table2 = new NhLookupTable("NHL-IN2", SAFI_UNICAST, &next_hop_resolver, ribin_table2); ribin_table2->set_next_table(nhlookup_table2); nhlookup_table2->set_next_table(decision_table); decision_table->add_parent(nhlookup_table2, &handler2, ribin_table2->genid()); RibInTable* ribin_table3 = new RibInTable("RIB-IN3", SAFI_UNICAST, &handler3); NhLookupTable* nhlookup_table3 = new NhLookupTable("NHL-IN3", SAFI_UNICAST, &next_hop_resolver, ribin_table3); ribin_table3->set_next_table(nhlookup_table3); nhlookup_table3->set_next_table(decision_table); decision_table->add_parent(nhlookup_table3, &handler3, ribin_table3->genid()); debug_table->set_output_file(filename); debug_table->set_canned_response(ADD_USED); // create a load of attributes IPNet net1("1.0.1.0/24"); IPNet net2("1.0.2.0/24"); // IPNet net2("1.0.2.0/24"); IPv4 nexthop1("2.0.0.1"); NextHopAttribute nhatt1(nexthop1); IPv4 nexthop2("2.0.0.2"); NextHopAttribute nhatt2(nexthop2); IPv4 nexthop3("2.0.0.3"); NextHopAttribute nhatt3(nexthop3); next_hop_resolver.set_nexthop_metric(nexthop1, 27); next_hop_resolver.set_nexthop_metric(nexthop2, 27); next_hop_resolver.set_nexthop_metric(nexthop3, 27); OriginAttribute igp_origin_att(IGP); OriginAttribute egp_origin_att(EGP); OriginAttribute incomplete_origin_att(INCOMPLETE); ASPath aspath1; aspath1.prepend_as(AsNum(1)); aspath1.prepend_as(AsNum(2)); aspath1.prepend_as(AsNum(3)); ASPathAttribute aspathatt1(aspath1); ASPath aspath2; aspath2.prepend_as(AsNum(4)); aspath2.prepend_as(AsNum(5)); aspath2.prepend_as(AsNum(6)); aspath2.prepend_as(AsNum(6)); ASPathAttribute aspathatt2(aspath2); ASPath aspath3; aspath3.prepend_as(AsNum(7)); aspath3.prepend_as(AsNum(8)); aspath3.prepend_as(AsNum(9)); ASPathAttribute aspathatt3(aspath3); LocalPrefAttribute lpa1(100); LocalPrefAttribute lpa2(200); LocalPrefAttribute lpa3(300); FPAList4Ref fpalist1 = new FastPathAttributeList(nhatt1, aspathatt1, igp_origin_att); fpalist1->add_path_attribute(lpa1); PAListRef palist1 = new PathAttributeList(fpalist1); FPAList4Ref fpalist2 = new FastPathAttributeList(nhatt2, aspathatt2, igp_origin_att); fpalist2->add_path_attribute(lpa1); PAListRef palist2 = new PathAttributeList(fpalist2); FPAList4Ref fpalist3 = new FastPathAttributeList(nhatt3, aspathatt3, igp_origin_att); fpalist3->add_path_attribute(lpa1); PAListRef palist3 = new PathAttributeList(fpalist3); FPAList4Ref fpalist4; FPAList4Ref fpalist5; FPAList4Ref fpalist6; PAListRef palist4; PAListRef palist5; PAListRef palist6; // create a subnet route SubnetRoute *sr1; InternalMessage* msg; PolicyTags pt; UNUSED(net2); // ================================================================ // Test1: trivial add and delete // ================================================================ // add a route debug_table->write_comment("******************************************"); debug_table->write_comment("TEST 1"); debug_table->write_comment("ADD AND DELETE"); debug_table->write_comment("SENDING FROM PEER 1"); ribin_table1->add_route(net1, fpalist1, pt); debug_table->write_separator(); ribin_table1->push(NULL); debug_table->write_separator(); // delete the route ribin_table1->delete_route(net1); ribin_table1->push(NULL); debug_table->write_separator(); // ================================================================ // Test1b: trivial add and delete // ================================================================ // add a route - nexthop is unresolvable debug_table->write_comment("******************************************"); debug_table->write_comment("TEST 1b"); debug_table->write_comment("ADD AND DELETE, NEXTHOP UNRESOLVABLE"); debug_table->write_comment("SENDING ADD FROM PEER 1"); next_hop_resolver.unset_nexthop_metric(nexthop1); ribin_table1->add_route(net1, fpalist1, pt); debug_table->write_separator(); debug_table->write_comment("SENDING PUSH FROM PEER 1"); ribin_table1->push(NULL); debug_table->write_separator(); // delete the route debug_table->write_comment("SENDING DELETE FROM PEER 1"); ribin_table1->delete_route(net1); ribin_table1->push(NULL); next_hop_resolver.set_nexthop_metric(nexthop1, 27); debug_table->write_separator(); // ================================================================ // Test2: identical add from two peers // ================================================================ // add a route debug_table->write_comment("******************************************"); debug_table->write_comment("TEST 2"); debug_table->write_comment("IDENTICAL ADD FROM TWO PEERS"); debug_table->write_comment("PEER1 HAS LOWEST NEIGHBOR ADDRESS"); debug_table->write_comment("SENDING FROM PEER 1"); fpalist1 = new FastPathAttributeList(nhatt1, aspathatt1, igp_origin_att); fpalist1->add_path_attribute(lpa1); ribin_table1->add_route(net1, fpalist1, pt); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 2"); ribin_table2->add_route(net1, fpalist1, pt); debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 1"); ribin_table1->delete_route(net1); debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 2"); ribin_table2->delete_route(net1); debug_table->write_separator(); // ================================================================ // Test2B: identical add from two peers // ================================================================ // add a route debug_table->write_comment("******************************************"); debug_table->write_comment("TEST 2B"); debug_table->write_comment("IDENTICAL ADD FROM TWO PEERS"); debug_table->write_comment("PEER1 HAS LOWEST NEIGHBOR ADDRESS"); debug_table->write_comment("SENDING FROM PEER 2"); ribin_table2->add_route(net1, fpalist1, pt); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 1"); ribin_table1->add_route(net1, fpalist1, pt); debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 2"); ribin_table2->delete_route(net1); debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 1"); ribin_table1->delete_route(net1); debug_table->write_separator(); // ================================================================ // Test2C: identical add from two peers // ================================================================ // add a route peer_data1->set_id("101.0.0.0"); peer_data2->set_id("100.0.0.0"); debug_table->write_comment("******************************************"); debug_table->write_comment("TEST 2C"); debug_table->write_comment("IDENTICAL ADD FROM TWO PEERS"); debug_table->write_comment("PEER1 HAS LOWEST NEIGHBOR ADDRESS"); debug_table->write_comment("PEER2 HAS LOWEST BGP ID"); debug_table->write_comment("SENDING FROM PEER 1"); ribin_table1->add_route(net1, fpalist1, pt); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 2"); ribin_table2->add_route(net1, fpalist1, pt); debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 1"); ribin_table1->delete_route(net1); debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 2"); ribin_table2->delete_route(net1); debug_table->write_separator(); // ================================================================ // Test2D: identical add from two peers // ================================================================ // add a route debug_table->write_comment("******************************************"); debug_table->write_comment("TEST 2D"); debug_table->write_comment("IDENTICAL ADD FROM TWO PEERS"); debug_table->write_comment("PEER1 HAS LOWEST NEIGHBOR ADDRESS"); debug_table->write_comment("PEER2 HAS LOWEST BGP ID"); debug_table->write_comment("SENDING FROM PEER 2"); ribin_table2->add_route(net1, fpalist1, pt); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 1"); ribin_table1->add_route(net1, fpalist1, pt); debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 2"); ribin_table2->delete_route(net1); debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 1"); ribin_table1->delete_route(net1); debug_table->write_separator(); // ================================================================ // Test3: decision by AS path length // ================================================================ // add a route debug_table->write_comment("******************************************"); debug_table->write_comment("TEST 3"); debug_table->write_comment("TEST OF DIFFERENT AS PATH LENGTHS"); debug_table->write_comment("SENDING FROM PEER 1"); ribin_table1->add_route(net1, fpalist1, pt); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 2"); ribin_table2->add_route(net1, fpalist2, pt); debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 1"); ribin_table1->delete_route(net1); debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 2"); ribin_table2->delete_route(net1); debug_table->write_separator(); // ================================================================ // Test3b: decision by AS path length // ================================================================ // add a route debug_table->write_comment("******************************************"); debug_table->write_comment("TEST 3B"); debug_table->write_comment("TEST OF DIFFERENT AS PATH LENGTHS"); debug_table->write_comment("SENDING FROM PEER 1"); ribin_table1->add_route(net1, fpalist2, pt); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 2"); ribin_table2->add_route(net1, fpalist1, pt); debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 1"); ribin_table1->delete_route(net1); debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 2"); ribin_table2->delete_route(net1); debug_table->write_separator(); // ================================================================ // Test4: decision by LocalPref // ================================================================ fpalist4 = new FastPathAttributeList(nhatt1, aspathatt1, igp_origin_att); fpalist4->add_path_attribute(lpa2); palist4 = new PathAttributeList(fpalist4); debug_table->write_comment("******************************************"); debug_table->write_comment("TEST 4"); debug_table->write_comment("TEST OF LOCALPREF"); debug_table->write_comment("HIGHEST LOCALPREF WINS"); debug_table->write_comment("SENDING FROM PEER 1"); ribin_table1->add_route(net1, fpalist1, pt); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 2"); ribin_table2->add_route(net1, fpalist4, pt); debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 1"); ribin_table1->delete_route(net1); debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 2"); ribin_table2->delete_route(net1); debug_table->write_separator(); // ================================================================ // Test4b: decision by LocalPref // ================================================================ debug_table->write_comment("******************************************"); debug_table->write_comment("TEST 4B"); debug_table->write_comment("TEST OF LOCALPREF"); debug_table->write_comment("HIGHEST LOCALPREF WINS"); debug_table->write_comment("SENDING FROM PEER 1"); ribin_table1->add_route(net1, fpalist4, pt); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 2"); ribin_table2->add_route(net1, fpalist1, pt); debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 1"); ribin_table1->delete_route(net1); debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 2"); ribin_table2->delete_route(net1); palist4.release(); debug_table->write_separator(); // ================================================================ // Test4c: Test for incorrectly allowing routes with second-highest // local pref to pass through to ASPATH length selection // ================================================================ fpalist4 = new FastPathAttributeList(nhatt1, aspathatt2, igp_origin_att); fpalist4->add_path_attribute(lpa1); palist4 = new PathAttributeList(fpalist4); fpalist5 = new FastPathAttributeList(nhatt1, aspathatt1, igp_origin_att); fpalist5->add_path_attribute(lpa1); palist5 = new PathAttributeList(fpalist5); fpalist6 = new FastPathAttributeList(nhatt1, aspathatt2, igp_origin_att); fpalist6->add_path_attribute(lpa2); palist6 = new PathAttributeList(fpalist6); debug_table->write_comment("******************************************"); debug_table->write_comment("TEST 4C"); debug_table->write_comment("TEST OF LOCALPREF"); debug_table->write_comment("HIGHEST LOCALPREF WINS"); debug_table->write_comment("SENDING FROM PEER 1"); ribin_table1->add_route(net1, fpalist4, pt); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 2"); ribin_table2->add_route(net1, fpalist5, pt); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 3"); ribin_table3->add_route(net1, fpalist6, pt); debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 1"); ribin_table1->delete_route(net1); debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 2"); ribin_table2->delete_route(net1); debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 3"); ribin_table3->delete_route(net1); palist4.release(); palist5.release(); palist6.release(); debug_table->write_separator(); // ================================================================ // Test5: decision by MED // ================================================================ fpalist4 = new FastPathAttributeList(nhatt1, aspathatt1, igp_origin_att); fpalist4->add_path_attribute(lpa1); MEDAttribute med1(100); fpalist4->add_path_attribute(med1); fpalist5 = new FastPathAttributeList(nhatt1, aspathatt1, igp_origin_att); fpalist5->add_path_attribute(lpa1); MEDAttribute med2(200); fpalist5->add_path_attribute(med2); debug_table->write_comment("******************************************"); debug_table->write_comment("TEST 5"); debug_table->write_comment("TEST OF MED"); debug_table->write_comment("LOWEST MED WINS"); debug_table->write_comment("SENDING FROM PEER 1"); ribin_table1->add_route(net1, fpalist4, pt); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 2"); ribin_table2->add_route(net1, fpalist5, pt); debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 1"); ribin_table1->delete_route(net1); debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 2"); ribin_table2->delete_route(net1); debug_table->write_separator(); // ================================================================ // Test5B: decision by MED // ================================================================ debug_table->write_comment("******************************************"); debug_table->write_comment("TEST 5B"); debug_table->write_comment("TEST OF MED"); debug_table->write_comment("LOWEST MED WINS"); debug_table->write_comment("SENDING FROM PEER 1"); ribin_table1->add_route(net1, fpalist5, pt); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 2"); ribin_table2->add_route(net1, fpalist4, pt); debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 1"); ribin_table1->delete_route(net1); debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 2"); ribin_table2->delete_route(net1); debug_table->write_separator(); // ================================================================ // Test5C: decision by MED // ================================================================ fpalist5->remove_attribute_by_type(MED); palist5 = new PathAttributeList(fpalist5); debug_table->write_comment("******************************************"); debug_table->write_comment("TEST 5C"); debug_table->write_comment("TEST OF MED - ONLY ONE HAS MED"); debug_table->write_comment("ROUTE WITHOUT MED WINS"); debug_table->write_comment("SENDING FROM PEER 1"); ribin_table1->add_route(net1, fpalist4, pt); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 2"); ribin_table2->add_route(net1, fpalist5, pt); debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 1"); ribin_table1->delete_route(net1); debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 2"); ribin_table2->delete_route(net1); debug_table->write_separator(); // ================================================================ // Test5D: decision by MED // ================================================================ fpalist5->remove_attribute_by_type(MED); palist5 = new PathAttributeList(fpalist5); debug_table->write_comment("******************************************"); debug_table->write_comment("TEST 5D"); debug_table->write_comment("TEST OF MED - ONLY ONE HAS MED"); debug_table->write_comment("ROUTE WITHOUT MED WINS"); debug_table->write_comment("SENDING FROM PEER 1"); ribin_table1->add_route(net1, fpalist5, pt); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 2"); ribin_table2->add_route(net1, fpalist4, pt); debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 1"); ribin_table1->delete_route(net1); debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 2"); ribin_table2->delete_route(net1); palist4.release(); palist5.release(); debug_table->write_separator(); // ================================================================ // Test5E: decision by MED // ================================================================ // AS paths are the same length, but different paths. // MEDs are different (peer 1 has better MED), but shouldn't be compared. // peer 2 should win on BGP ID test. peer_data1->set_id("101.0.0.0"); peer_data2->set_id("100.0.0.0"); fpalist4 = new FastPathAttributeList(nhatt1, aspathatt1, igp_origin_att); fpalist4->add_path_attribute(lpa1); fpalist4->add_path_attribute(med1); palist4 = new PathAttributeList(fpalist4); fpalist5 = new FastPathAttributeList(nhatt1, aspathatt3, igp_origin_att); fpalist5->add_path_attribute(lpa1); fpalist5->add_path_attribute(med2); palist5 = new PathAttributeList(fpalist5); debug_table->write_comment("******************************************"); debug_table->write_comment("TEST 5E"); debug_table->write_comment("TEST OF MED - DIFFERENT MEDS FROM DIFFERENCE AS'S"); debug_table->write_comment(""); debug_table->write_comment("SENDING FROM PEER 1"); ribin_table1->add_route(net1, fpalist4, pt); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 2"); sr1 = new SubnetRoute(net1, palist5, NULL); msg = new InternalMessage(sr1, &handler2, 0); msg->set_push(); ribin_table2->add_route(net1, fpalist5, pt); sr1->unref(); delete msg; debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 1"); sr1 = new SubnetRoute(net1, palist4, NULL); msg = new InternalMessage(sr1, &handler1, 0); msg->set_push(); ribin_table1->delete_route(net1); sr1->unref(); delete msg; debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 2"); sr1 = new SubnetRoute(net1, palist5, NULL); msg = new InternalMessage(sr1, &handler2, 0); msg->set_push(); ribin_table2->delete_route(net1); sr1->unref(); delete msg; palist4.release(); palist5.release(); debug_table->write_separator(); // ================================================================ // Test5F: decision by MED // ================================================================ peer_data1->set_id("101.0.0.0"); peer_data2->set_id("100.0.0.0"); peer_data3->set_id("102.0.0.0"); fpalist4 = new FastPathAttributeList(nhatt1, aspathatt1, igp_origin_att); fpalist4->add_path_attribute(lpa1); fpalist4->add_path_attribute(med1); palist4 = new PathAttributeList(fpalist4); fpalist5 = new FastPathAttributeList(nhatt1, aspathatt3, igp_origin_att); fpalist5->add_path_attribute(lpa1); fpalist5->add_path_attribute(med2); palist5 = new PathAttributeList(fpalist5); fpalist6 = new FastPathAttributeList(nhatt1, aspathatt3, igp_origin_att); fpalist6->add_path_attribute(lpa1); MEDAttribute med50(50); fpalist6->add_path_attribute(med50); palist6 = new PathAttributeList(fpalist6); debug_table->write_comment("******************************************"); debug_table->write_comment("TEST 5F1"); debug_table->write_comment("TEST OF MED"); debug_table->write_comment(""); debug_table->write_comment("SENDING FROM PEER 1"); sr1 = new SubnetRoute(net1, palist4, NULL); msg = new InternalMessage(sr1, &handler1, 0); msg->set_push(); ribin_table1->add_route(net1, fpalist4, pt); sr1->unref(); delete msg; debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 2"); debug_table->write_comment("NEW ROUTE WINS ON BGP ID"); sr1 = new SubnetRoute(net1, palist5, NULL); msg = new InternalMessage(sr1, &handler2, 0); msg->set_push(); ribin_table2->add_route(net1, fpalist5, pt); sr1->unref(); delete msg; debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 3"); debug_table->write_comment("ROUTE 1 RE-INSTATED"); sr1 = new SubnetRoute(net1, palist6, NULL); msg = new InternalMessage(sr1, &handler3, 0); msg->set_push(); ribin_table3->add_route(net1, fpalist6, pt); sr1->unref(); delete msg; debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 3"); debug_table->write_comment("ROUTE 2 TAKES OVER"); sr1 = new SubnetRoute(net1, palist6, NULL); msg = new InternalMessage(sr1, &handler3, 0); msg->set_push(); ribin_table3->delete_route(net1); sr1->unref(); delete msg; debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 2"); debug_table->write_comment("ROUTE 1 TAKES OVER"); sr1 = new SubnetRoute(net1, palist5, NULL); msg = new InternalMessage(sr1, &handler2, 0); msg->set_push(); ribin_table2->delete_route(net1); sr1->unref(); delete msg; debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 1"); sr1 = new SubnetRoute(net1, palist4, NULL); msg = new InternalMessage(sr1, &handler1, 0); msg->set_push(); ribin_table1->delete_route(net1); sr1->unref(); delete msg; debug_table->write_separator(); debug_table->write_comment("******************************************"); debug_table->write_comment("TEST 5F2"); debug_table->write_comment("TEST OF MED"); debug_table->write_comment(""); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 3"); sr1 = new SubnetRoute(net1, palist6, NULL); msg = new InternalMessage(sr1, &handler3, 0); msg->set_push(); ribin_table3->add_route(net1, fpalist6, pt); sr1->unref(); delete msg; debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 1"); debug_table->write_comment("WINS ON BGP ID"); sr1 = new SubnetRoute(net1, palist4, NULL); msg = new InternalMessage(sr1, &handler1, 0); msg->set_push(); ribin_table1->add_route(net1, fpalist4, pt); sr1->unref(); delete msg; debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 2"); debug_table->write_comment("NO CHANGE"); sr1 = new SubnetRoute(net1, palist5, NULL); msg = new InternalMessage(sr1, &handler2, 0); msg->set_push(); ribin_table2->add_route(net1, fpalist5, pt); sr1->unref(); delete msg; debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 2"); debug_table->write_comment("NO CHANGE"); sr1 = new SubnetRoute(net1, palist5, NULL); msg = new InternalMessage(sr1, &handler2, 0); msg->set_push(); ribin_table2->delete_route(net1); sr1->unref(); delete msg; debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 1"); debug_table->write_comment("ROUTE 3 TAKES OVER"); sr1 = new SubnetRoute(net1, palist4, NULL); msg = new InternalMessage(sr1, &handler1, 0); msg->set_push(); ribin_table1->delete_route(net1); sr1->unref(); delete msg; debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 3"); sr1 = new SubnetRoute(net1, palist6, NULL); msg = new InternalMessage(sr1, &handler3, 0); msg->set_push(); ribin_table3->delete_route(net1); sr1->unref(); delete msg; debug_table->write_separator(); debug_table->write_comment("******************************************"); debug_table->write_comment("TEST 5F3"); debug_table->write_comment("TEST OF MED"); debug_table->write_comment(""); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 2"); sr1 = new SubnetRoute(net1, palist5, NULL); msg = new InternalMessage(sr1, &handler2, 0); msg->set_push(); ribin_table2->add_route(net1, fpalist5, pt); sr1->unref(); delete msg; debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 1"); debug_table->write_comment("NO CHANGE"); sr1 = new SubnetRoute(net1, palist4, NULL); msg = new InternalMessage(sr1, &handler1, 0); msg->set_push(); ribin_table1->add_route(net1, fpalist4, pt); sr1->unref(); delete msg; debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 3"); debug_table->write_comment("ROUTE 1 NOW WINS"); sr1 = new SubnetRoute(net1, palist6, NULL); msg = new InternalMessage(sr1, &handler3, 0); msg->set_push(); ribin_table3->add_route(net1, fpalist6, pt); sr1->unref(); delete msg; debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 3"); debug_table->write_comment("ROUTE 2 TAKES OVER"); sr1 = new SubnetRoute(net1, palist6, NULL); msg = new InternalMessage(sr1, &handler3, 0); msg->set_push(); ribin_table3->delete_route(net1); sr1->unref(); delete msg; debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 1"); debug_table->write_comment("NO CHANGE"); sr1 = new SubnetRoute(net1, palist4, NULL); msg = new InternalMessage(sr1, &handler1, 0); msg->set_push(); ribin_table1->delete_route(net1); sr1->unref(); delete msg; debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 2"); sr1 = new SubnetRoute(net1, palist5, NULL); msg = new InternalMessage(sr1, &handler2, 0); msg->set_push(); ribin_table2->delete_route(net1); sr1->unref(); delete msg; debug_table->write_separator(); debug_table->write_comment("******************************************"); debug_table->write_comment("TEST 5F4"); debug_table->write_comment("TEST OF MED"); debug_table->write_comment(""); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 1"); sr1 = new SubnetRoute(net1, palist4, NULL); msg = new InternalMessage(sr1, &handler1, 0); msg->set_push(); ribin_table1->add_route(net1, fpalist4, pt); sr1->unref(); delete msg; debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 3"); debug_table->write_comment("NO CHANGE"); sr1 = new SubnetRoute(net1, palist6, NULL); msg = new InternalMessage(sr1, &handler3, 0); msg->set_push(); ribin_table3->add_route(net1, fpalist6, pt); sr1->unref(); delete msg; debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 2"); debug_table->write_comment("NO CHANGE"); sr1 = new SubnetRoute(net1, palist5, NULL); msg = new InternalMessage(sr1, &handler2, 0); msg->set_push(); ribin_table2->add_route(net1, fpalist5, pt); sr1->unref(); delete msg; debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 2"); debug_table->write_comment("NO CHANGE"); sr1 = new SubnetRoute(net1, palist5, NULL); msg = new InternalMessage(sr1, &handler2, 0); msg->set_push(); ribin_table2->delete_route(net1); sr1->unref(); delete msg; debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 3"); debug_table->write_comment("NO CHANGE"); sr1 = new SubnetRoute(net1, palist6, NULL); msg = new InternalMessage(sr1, &handler3, 0); msg->set_push(); ribin_table3->delete_route(net1); sr1->unref(); delete msg; debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 1"); sr1 = new SubnetRoute(net1, palist4, NULL); msg = new InternalMessage(sr1, &handler1, 0); msg->set_push(); ribin_table1->delete_route(net1); sr1->unref(); delete msg; debug_table->write_separator(); debug_table->write_comment("******************************************"); debug_table->write_comment("TEST 5F5"); debug_table->write_comment("TEST OF MED"); debug_table->write_comment(""); debug_table->write_comment("SENDING FROM PEER 2"); sr1 = new SubnetRoute(net1, palist5, NULL); msg = new InternalMessage(sr1, &handler2, 0); msg->set_push(); ribin_table2->add_route(net1, fpalist5, pt); sr1->unref(); delete msg; debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 3"); debug_table->write_comment("ROUTE 3 NOW WINS"); sr1 = new SubnetRoute(net1, palist6, NULL); msg = new InternalMessage(sr1, &handler3, 0); msg->set_push(); ribin_table3->add_route(net1, fpalist6, pt); sr1->unref(); delete msg; debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 1"); debug_table->write_comment("ROUTE 1 NOW WINS"); sr1 = new SubnetRoute(net1, palist4, NULL); msg = new InternalMessage(sr1, &handler1, 0); msg->set_push(); ribin_table1->add_route(net1, fpalist4, pt); sr1->unref(); delete msg; debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 1"); debug_table->write_comment("ROUTE 3 TAKES OVER"); sr1 = new SubnetRoute(net1, palist4, NULL); msg = new InternalMessage(sr1, &handler1, 0); msg->set_push(); ribin_table1->delete_route(net1); sr1->unref(); delete msg; debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 3"); debug_table->write_comment("ROUTE 2 TAKES OVER"); sr1 = new SubnetRoute(net1, palist6, NULL); msg = new InternalMessage(sr1, &handler3, 0); msg->set_push(); ribin_table3->delete_route(net1); sr1->unref(); delete msg; debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 2"); sr1 = new SubnetRoute(net1, palist5, NULL); msg = new InternalMessage(sr1, &handler2, 0); msg->set_push(); ribin_table2->delete_route(net1); sr1->unref(); delete msg; debug_table->write_separator(); debug_table->write_comment("******************************************"); debug_table->write_comment("TEST 5F6"); debug_table->write_comment("TEST OF MED"); debug_table->write_comment(""); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 3"); sr1 = new SubnetRoute(net1, palist6, NULL); msg = new InternalMessage(sr1, &handler3, 0); msg->set_push(); ribin_table3->add_route(net1, fpalist6, pt); sr1->unref(); delete msg; debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 2"); debug_table->write_comment("NO CHANGE"); sr1 = new SubnetRoute(net1, palist5, NULL); msg = new InternalMessage(sr1, &handler2, 0); msg->set_push(); ribin_table2->add_route(net1, fpalist5, pt); sr1->unref(); delete msg; debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 1"); debug_table->write_comment("ROUTE 1 WINS"); sr1 = new SubnetRoute(net1, palist4, NULL); msg = new InternalMessage(sr1, &handler1, 0); msg->set_push(); ribin_table1->add_route(net1, fpalist4, pt); sr1->unref(); delete msg; debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 1"); debug_table->write_comment("ROUTE 3 TAKES OVER"); sr1 = new SubnetRoute(net1, palist4, NULL); msg = new InternalMessage(sr1, &handler1, 0); msg->set_push(); ribin_table1->delete_route(net1); sr1->unref(); delete msg; debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 2"); debug_table->write_comment("NO CHANGE"); sr1 = new SubnetRoute(net1, palist5, NULL); msg = new InternalMessage(sr1, &handler2, 0); msg->set_push(); ribin_table2->delete_route(net1); sr1->unref(); delete msg; debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 3"); sr1 = new SubnetRoute(net1, palist6, NULL); msg = new InternalMessage(sr1, &handler3, 0); msg->set_push(); ribin_table3->delete_route(net1); sr1->unref(); delete msg; palist4.release(); palist5.release(); palist6.release(); debug_table->write_separator(); // ================================================================ // Test6: decision by Origin // ================================================================ fpalist4 = new FastPathAttributeList(nhatt1, aspathatt1, egp_origin_att); fpalist4->add_path_attribute(lpa1); palist4 = new PathAttributeList(fpalist4); fpalist5 = new FastPathAttributeList(nhatt1, aspathatt1, incomplete_origin_att); fpalist5->add_path_attribute(lpa1); palist5 = new PathAttributeList(fpalist5); debug_table->write_comment("******************************************"); debug_table->write_comment("TEST 6"); debug_table->write_comment("TEST OF ORIGIN"); debug_table->write_comment("IGP beats EGP beats INCOMPLETE"); debug_table->write_comment("SENDING FROM PEER 1"); debug_table->write_comment("EXPECT: WINS BY DEFAULT"); sr1 = new SubnetRoute(net1, palist1, NULL); msg = new InternalMessage(sr1, &handler1, 0); msg->set_push(); ribin_table1->add_route(net1, fpalist1, pt); sr1->unref(); delete msg; debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 2"); debug_table->write_comment("EXPECT: LOSES"); sr1 = new SubnetRoute(net1, palist4, NULL); msg = new InternalMessage(sr1, &handler2, 0); msg->set_push(); ribin_table2->add_route(net1, fpalist4, pt); sr1->unref(); delete msg; debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 1"); debug_table->write_comment("EXPECT: ALLOWS EGP ROUTE TO WIN"); sr1 = new SubnetRoute(net1, palist1, NULL); msg = new InternalMessage(sr1, &handler1, 0); msg->set_push(); ribin_table1->delete_route(net1); sr1->unref(); delete msg; debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 1"); debug_table->write_comment("EXPECT: LOSES"); sr1 = new SubnetRoute(net1, palist5, NULL); msg = new InternalMessage(sr1, &handler1, 0); msg->set_push(); ribin_table1->add_route(net1, fpalist5, pt); sr1->unref(); delete msg; debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 2"); debug_table->write_comment("EXPECT: ALLOWS INCOMPLETE ROUTE TO WIN"); sr1 = new SubnetRoute(net1, palist4, NULL); msg = new InternalMessage(sr1, &handler2, 0); msg->set_push(); ribin_table2->delete_route(net1); sr1->unref(); delete msg; debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 1"); sr1 = new SubnetRoute(net1, palist5, NULL); msg = new InternalMessage(sr1, &handler1, 0); msg->set_push(); ribin_table1->delete_route(net1); sr1->unref(); delete msg; palist4.release(); palist5.release(); debug_table->write_separator(); // ================================================================ // Test6B: decision by Origin // ================================================================ fpalist4 = new FastPathAttributeList(nhatt1, aspathatt1, egp_origin_att); fpalist4->add_path_attribute(lpa1); palist4 = new PathAttributeList(fpalist4); fpalist5 = new FastPathAttributeList(nhatt1, aspathatt1, incomplete_origin_att); fpalist5->add_path_attribute(lpa1); palist5 = new PathAttributeList(fpalist5); debug_table->write_comment("******************************************"); debug_table->write_comment("TEST 6B"); debug_table->write_comment("TEST OF ORIGIN"); debug_table->write_comment("IGP beats EGP beats INCOMPLETE"); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 2"); debug_table->write_comment("EXPECT: WINS BY DEFAULT"); sr1 = new SubnetRoute(net1, palist5, NULL); msg = new InternalMessage(sr1, &handler2, 0); msg->set_push(); ribin_table2->add_route(net1, fpalist5, pt); sr1->unref(); delete msg; debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 1"); debug_table->write_comment("EXPECT: WINS"); sr1 = new SubnetRoute(net1, palist4, NULL); msg = new InternalMessage(sr1, &handler1, 0); msg->set_push(); ribin_table1->add_route(net1, fpalist4, pt); sr1->unref(); delete msg; debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 2"); debug_table->write_comment("EXPECT: NO CHANGE"); sr1 = new SubnetRoute(net1, palist5, NULL); msg = new InternalMessage(sr1, &handler2, 0); msg->set_push(); ribin_table2->delete_route(net1); sr1->unref(); delete msg; debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 2"); debug_table->write_comment("EXPECT: WINS"); sr1 = new SubnetRoute(net1, palist1, NULL); msg = new InternalMessage(sr1, &handler2, 0); msg->set_push(); ribin_table2->add_route(net1, fpalist1, pt); sr1->unref(); delete msg; debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 2"); sr1 = new SubnetRoute(net1, palist1, NULL); msg = new InternalMessage(sr1, &handler2, 0); msg->set_push(); ribin_table2->delete_route(net1); sr1->unref(); delete msg; debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 1"); sr1 = new SubnetRoute(net1, palist5, NULL); msg = new InternalMessage(sr1, &handler1, 0); msg->set_push(); ribin_table1->delete_route(net1); sr1->unref(); delete msg; palist4.release(); palist5.release(); debug_table->write_separator(); // ================================================================ // Test7: decision by IGP distance // ================================================================ next_hop_resolver.unset_nexthop_metric(nexthop1); next_hop_resolver.unset_nexthop_metric(nexthop3); next_hop_resolver.set_nexthop_metric(nexthop1, 100); next_hop_resolver.set_nexthop_metric(nexthop3, 200); debug_table->write_comment("******************************************"); debug_table->write_comment("TEST 7"); debug_table->write_comment("TEST OF IGP DISTANCE"); debug_table->write_comment("SENDING FROM PEER 1"); debug_table->write_comment("PEER 1 HAS BETTER IGP DISTANCE"); ribin_table1->add_route(net1, fpalist1, pt); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 2"); ribin_table2->add_route(net1, fpalist3, pt); debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 1"); ribin_table1->delete_route(net1); debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 2"); ribin_table2->delete_route(net1); debug_table->write_separator(); // ================================================================ // Test7B: decision by IGP distance // ================================================================ next_hop_resolver.unset_nexthop_metric(nexthop1); next_hop_resolver.unset_nexthop_metric(nexthop3); next_hop_resolver.set_nexthop_metric(nexthop1, 200); next_hop_resolver.set_nexthop_metric(nexthop3, 100); debug_table->write_comment("******************************************"); debug_table->write_comment("TEST 7B"); debug_table->write_comment("TEST OF IGP DISTANCE"); debug_table->write_comment("SENDING FROM PEER 1"); debug_table->write_comment("PEER 2 HAS BETTER IGP DISTANCE"); ribin_table1->add_route(net1, fpalist1, pt); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 2"); ribin_table2->add_route(net1, fpalist3, pt); debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 1"); ribin_table1->delete_route(net1); debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 2"); ribin_table2->delete_route(net1); debug_table->write_separator(); // ================================================================ // Test7C: decision by IGP distance // ================================================================ next_hop_resolver.unset_nexthop_metric(nexthop1); next_hop_resolver.unset_nexthop_metric(nexthop3); next_hop_resolver.set_nexthop_metric(nexthop1, 100); debug_table->write_comment("******************************************"); debug_table->write_comment("TEST 7C"); debug_table->write_comment("TEST OF IGP DISTANCE"); debug_table->write_comment("PEER 2 NOT RESOLVABLE"); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 1"); ribin_table1->add_route(net1, fpalist1, pt); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 2"); ribin_table2->add_route(net1, fpalist3, pt); debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 1"); ribin_table1->delete_route(net1); debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 2"); ribin_table2->delete_route(net1); debug_table->write_separator(); // ================================================================ // Test7D: decision by IGP distance // ================================================================ next_hop_resolver.unset_nexthop_metric(nexthop1); next_hop_resolver.set_nexthop_metric(nexthop3, 100); debug_table->write_comment("******************************************"); debug_table->write_comment("TEST 7D"); debug_table->write_comment("TEST OF IGP DISTANCE"); debug_table->write_comment("PEER 1 NOT RESOLVABLE"); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 1"); ribin_table1->add_route(net1, fpalist1, pt); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 2"); ribin_table2->add_route(net1, fpalist3, pt); debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 1"); ribin_table1->delete_route(net1); debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 2"); ribin_table2->delete_route(net1); debug_table->write_separator(); // ================================================================ // Test7E: decision by IGP distance // ================================================================ next_hop_resolver.unset_nexthop_metric(nexthop3); debug_table->write_comment("******************************************"); debug_table->write_comment("TEST 7E"); debug_table->write_comment("TEST OF IGP DISTANCE"); debug_table->write_comment("NEITHER PEER IS RESOLVABLE"); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 1"); ribin_table1->add_route(net1, fpalist1, pt); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 2"); ribin_table2->add_route(net1, fpalist3, pt); debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 1"); ribin_table1->delete_route(net1); debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 2"); ribin_table2->delete_route(net1); debug_table->write_separator(); // ================================================================ // Test8A: test of replace_route // ================================================================ next_hop_resolver.set_nexthop_metric(nexthop1, 27); debug_table->write_comment("******************************************"); debug_table->write_comment("TEST 8A"); debug_table->write_comment("TEST OF REPLACE_ROUTE"); debug_table->write_comment("REPLACING FROM A SINGLE PEER"); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 1"); ribin_table1->add_route(net1, fpalist2, pt); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 1"); ribin_table1->add_route(net1, fpalist1, pt); debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 1"); ribin_table1->delete_route(net1); debug_table->write_separator(); // ================================================================ // Test8B: test of replace_route // ================================================================ debug_table->write_comment("******************************************"); debug_table->write_comment("TEST 8B"); debug_table->write_comment("TEST OF REPLACE_ROUTE"); debug_table->write_comment("REPLACING FROM A SINGLE PEER, FIRST NOT RESOLVABLE"); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 1"); ribin_table1->add_route(net1, fpalist3, pt); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 1"); ribin_table1->add_route(net1, fpalist2, pt); debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 1"); ribin_table1->delete_route(net1); debug_table->write_separator(); // ================================================================ // Test8C: test of replace_route // ================================================================ debug_table->write_comment("******************************************"); debug_table->write_comment("TEST 8C"); debug_table->write_comment("TEST OF REPLACE_ROUTE"); debug_table->write_comment("REPLACING FROM A SINGLE PEER, SECOND NOT RESOLVABLE"); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 1"); ribin_table1->add_route(net1, fpalist2, pt); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 1"); ribin_table1->add_route(net1, fpalist3, pt); debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 1"); ribin_table1->delete_route(net1); debug_table->write_separator(); // ================================================================ // Test8D: test of replace_route // ================================================================ next_hop_resolver.unset_nexthop_metric(nexthop2); debug_table->write_comment("******************************************"); debug_table->write_comment("TEST 8D"); debug_table->write_comment("TEST OF REPLACE_ROUTE"); debug_table->write_comment("REPLACING FROM A SINGLE PEER, NEITHER RESOLVABLE"); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 1"); ribin_table1->add_route(net1, fpalist2, pt); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 1"); ribin_table1->add_route(net1, fpalist3, pt); debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 1"); ribin_table1->delete_route(net1); debug_table->write_separator(); // ================================================================ // Test9A: test of replace_route // ================================================================ next_hop_resolver.set_nexthop_metric(nexthop2, 27); next_hop_resolver.set_nexthop_metric(nexthop3, 27); fpalist4 = new FastPathAttributeList(nhatt1, aspathatt1, igp_origin_att); fpalist4->add_path_attribute(lpa1); palist4 = new PathAttributeList(fpalist4); fpalist5 = new FastPathAttributeList(nhatt2, aspathatt1, igp_origin_att); fpalist5->add_path_attribute(lpa2); palist5 = new PathAttributeList(fpalist5); fpalist6 = new FastPathAttributeList(nhatt3, aspathatt1, igp_origin_att); fpalist6->add_path_attribute(lpa3); palist6 = new PathAttributeList(fpalist6); debug_table->write_comment("******************************************"); debug_table->write_comment("TEST 9A"); debug_table->write_comment("TEST OF REPLACE_ROUTE"); debug_table->write_comment("PEER 1 WINS, PEER 2 LOSES, PEER2 WINS"); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 1"); ribin_table1->add_route(net1, fpalist5, pt); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 2"); ribin_table2->add_route(net1, fpalist4, pt); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 2"); ribin_table2->add_route(net1, fpalist6, pt); debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 2"); ribin_table2->delete_route(net1); debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 1"); ribin_table1->delete_route(net1); debug_table->write_separator(); // ================================================================ // Test9B: test of replace_route // ================================================================ debug_table->write_comment("******************************************"); debug_table->write_comment("TEST 9B"); debug_table->write_comment("TEST OF REPLACE_ROUTE"); debug_table->write_comment("PEER 2 WINS, PEER 1 WINS, PEER2 WINS"); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 2"); ribin_table2->add_route(net1, fpalist4, pt); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 1"); ribin_table1->add_route(net1, fpalist5, pt); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 2"); ribin_table2->add_route(net1, fpalist6, pt); debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 2"); ribin_table2->delete_route(net1); debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 1"); ribin_table1->delete_route(net1); debug_table->write_separator(); // ================================================================ // Test9C: test of replace_route // ================================================================ debug_table->write_comment("******************************************"); debug_table->write_comment("TEST 9C"); debug_table->write_comment("TEST OF REPLACE_ROUTE"); debug_table->write_comment("PEER 1 WINS, PEER 2 LOSES, PEER2 LOSES"); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 1"); ribin_table1->add_route(net1, fpalist6, pt); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 2"); ribin_table2->add_route(net1, fpalist4, pt); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 2"); ribin_table2->add_route(net1, fpalist5, pt); debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 2"); ribin_table2->delete_route(net1); debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 1"); ribin_table1->delete_route(net1); debug_table->write_separator(); // ================================================================ // Test9D: test of replace_route // ================================================================ debug_table->write_comment("******************************************"); debug_table->write_comment("TEST 9D"); debug_table->write_comment("TEST OF REPLACE_ROUTE"); debug_table->write_comment("PEER 1 WINS, PEER 2 WINS, PEER2 WINS"); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 1"); ribin_table1->add_route(net1, fpalist4, pt); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 2"); ribin_table2->add_route(net1, fpalist6, pt); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 2"); ribin_table2->add_route(net1, fpalist5, pt); debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 2"); ribin_table2->delete_route(net1); debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 1"); ribin_table1->delete_route(net1); debug_table->write_separator(); // ================================================================ // Test9E: test of replace_route // ================================================================ debug_table->write_comment("******************************************"); debug_table->write_comment("TEST 9E"); debug_table->write_comment("TEST OF REPLACE_ROUTE"); debug_table->write_comment("PEER 1 WINS, PEER 2 WINS, PEER2 LOSES"); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 1"); ribin_table1->add_route(net1, fpalist5, pt); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 2"); ribin_table2->add_route(net1, fpalist6, pt); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 2"); ribin_table2->add_route(net1, fpalist4, pt); debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 1"); ribin_table1->delete_route(net1); debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 2"); ribin_table2->delete_route(net1); debug_table->write_separator(); // ================================================================ // Test 10A: test of replace_route // ================================================================ debug_table->write_comment("******************************************"); debug_table->write_comment("TEST 10A"); debug_table->write_comment("TEST OF REPLACE_ROUTE"); debug_table->write_comment("PEER 1 WINS, PEER 2 WINS, PEER2 BECOMES UNRESOLVABLE"); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 1"); ribin_table1->add_route(net1, fpalist5, pt); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 2"); ribin_table2->add_route(net1, fpalist6, pt); debug_table->write_separator(); debug_table->write_comment(("NEXTHOP " + nexthop3.str() + " BECOMES UNRESOLVABLE").c_str()); next_hop_resolver.unset_nexthop_metric(nexthop3); decision_table->igp_nexthop_changed(nexthop3); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 1"); ribin_table1->delete_route(net1); debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 2"); ribin_table2->delete_route(net1); debug_table->write_separator(); next_hop_resolver.set_nexthop_metric(nexthop3, 27); // ================================================================ // Test 10B: test of replace_route // ================================================================ debug_table->write_comment("******************************************"); debug_table->write_comment("TEST 10B"); debug_table->write_comment("TEST OF REPLACE_ROUTE"); debug_table->write_comment("PEER 1 WINS, PEER 2 WINS, PEER1 BECOMES UNRESOLVABLE"); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 1"); ribin_table1->add_route(net1, fpalist5, pt); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 2"); ribin_table2->add_route(net1, fpalist6, pt); debug_table->write_separator(); debug_table->write_comment(("NEXTHOP " + nexthop2.str() + " BECOMES UNRESOLVABLE").c_str()); next_hop_resolver.unset_nexthop_metric(nexthop2); decision_table->igp_nexthop_changed(nexthop2); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 2"); ribin_table2->delete_route(net1); debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 1"); ribin_table1->delete_route(net1); debug_table->write_separator(); // ================================================================ // Test 10C: test of replace_route // ================================================================ next_hop_resolver.set_nexthop_metric(nexthop2, 27); next_hop_resolver.unset_nexthop_metric(nexthop3); debug_table->write_comment("******************************************"); debug_table->write_comment("TEST 10C"); debug_table->write_comment("TEST OF REPLACE_ROUTE"); debug_table->write_comment("PEER 1 WINS, PEER 2 NOT RESOLVABLE, PEER2 BECOMES RESOLVABLE AND WINS"); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 1"); ribin_table1->add_route(net1, fpalist5, pt); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 2"); ribin_table2->add_route(net1, fpalist6, pt); debug_table->write_separator(); debug_table->write_comment(("NEXTHOP " + nexthop3.str() + " BECOMES RESOLVABLE").c_str()); next_hop_resolver.set_nexthop_metric(nexthop3, 27); decision_table->igp_nexthop_changed(nexthop3); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 2"); ribin_table2->delete_route(net1); debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 1"); ribin_table1->delete_route(net1); debug_table->write_separator(); palist4.release(); palist5.release(); palist6.release(); // ================================================================ // Test 11A: test of routes becoming resolvable // ================================================================ fpalist4 = new FastPathAttributeList(nhatt1, aspathatt1, igp_origin_att); fpalist4->add_path_attribute(lpa1); palist4 = new PathAttributeList(fpalist4); fpalist5 = new FastPathAttributeList(nhatt2, aspathatt1, igp_origin_att); fpalist5->add_path_attribute(lpa2); palist5 = new PathAttributeList(fpalist5); fpalist6 = new FastPathAttributeList(nhatt3, aspathatt1, igp_origin_att); fpalist6->add_path_attribute(lpa3); palist6 = new PathAttributeList(fpalist6); next_hop_resolver.unset_nexthop_metric(nexthop2); next_hop_resolver.unset_nexthop_metric(nexthop3); debug_table->write_comment("******************************************"); debug_table->write_comment("TEST 11A"); debug_table->write_comment("TEST OF ROUTES BECOMING RESOLVABLE"); debug_table->write_comment("PEER 1, 2 NOT RESOLVABLE, PEER1 BECOMES RESOLVABLE"); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 1"); ribin_table1->add_route(net1, fpalist5, pt); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 2"); ribin_table2->add_route(net1, fpalist6, pt); debug_table->write_separator(); debug_table->write_comment(("NEXTHOP " + nexthop2.str() + " BECOMES RESOLVABLE").c_str()); next_hop_resolver.set_nexthop_metric(nexthop2, 27); decision_table->igp_nexthop_changed(nexthop2); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 2"); ribin_table2->delete_route(net1); debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 1"); ribin_table1->delete_route(net1); debug_table->write_separator(); palist4.release(); palist5.release(); palist6.release(); // ================================================================ // Test 11B: test of routes becoming resolvable // ================================================================ fpalist4 = new FastPathAttributeList(nhatt1, aspathatt1, igp_origin_att); fpalist4->add_path_attribute(lpa1); palist4 = new PathAttributeList(fpalist4); fpalist5 = new FastPathAttributeList(nhatt1, aspathatt1, igp_origin_att); fpalist5->add_path_attribute(lpa2); palist5 = new PathAttributeList(fpalist5); fpalist6 = new FastPathAttributeList(nhatt1, aspathatt1, igp_origin_att); fpalist6->add_path_attribute(lpa3); palist6 = new PathAttributeList(fpalist6); next_hop_resolver.unset_nexthop_metric(nexthop1); next_hop_resolver.unset_nexthop_metric(nexthop2); debug_table->write_comment("******************************************"); debug_table->write_comment("TEST 11B"); debug_table->write_comment("TEST OF ROUTES BECOMING RESOLVABLE"); debug_table->write_comment("PEER 1, 2 NOT RESOLVABLE, BOTH BECOME RESOLVABLE SIMULTANEOUSLY"); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 1"); ribin_table1->add_route(net1, fpalist5, pt); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 2"); ribin_table2->add_route(net1, fpalist6, pt); debug_table->write_separator(); debug_table->write_comment(("NEXTHOP " + nexthop1.str() + " BECOMES RESOLVABLE").c_str()); next_hop_resolver.set_nexthop_metric(nexthop1, 27); decision_table->igp_nexthop_changed(nexthop1); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 2"); ribin_table2->delete_route(net1); debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 1"); ribin_table1->delete_route(net1); debug_table->write_separator(); // ================================================================ // Test 11C: test of routes becoming unresolvable // ================================================================ debug_table->write_comment("******************************************"); debug_table->write_comment("TEST 11C"); debug_table->write_comment("TEST OF ROUTES BECOMING UNRESOLVABLE"); debug_table->write_comment("PEER 1, 2 RESOLVABLE, BOTH BECOME UNRESOLVABLE SIMULTANEOUSLY"); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 1"); ribin_table1->add_route(net1, fpalist5, pt); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 2"); ribin_table2->add_route(net1, fpalist6, pt); debug_table->write_separator(); debug_table->write_comment(("NEXTHOP " + nexthop1.str() + " BECOMES UNRESOLVABLE").c_str()); next_hop_resolver.unset_nexthop_metric(nexthop1); decision_table->igp_nexthop_changed(nexthop1); while (bgpmain.eventloop().events_pending()) { bgpmain.eventloop().run(); } debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 2"); ribin_table2->delete_route(net1); debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 1"); ribin_table1->delete_route(net1); debug_table->write_separator(); // ================================================================ // Test 12: test with three peers // ================================================================ next_hop_resolver.set_nexthop_metric(nexthop1, 27); next_hop_resolver.set_nexthop_metric(nexthop2, 27); next_hop_resolver.set_nexthop_metric(nexthop3, 27); debug_table->write_comment("******************************************"); debug_table->write_comment("TEST 12"); debug_table->write_comment("TEST WITH THREE PEERS"); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 1"); ribin_table1->add_route(net1, fpalist4, pt); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 2"); ribin_table2->add_route(net1, fpalist5, pt); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 3"); ribin_table3->add_route(net1, fpalist6, pt); debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 3"); ribin_table3->delete_route(net1); debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 2"); ribin_table2->delete_route(net1); debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 1"); ribin_table1->delete_route(net1); debug_table->write_separator(); // ================================================================ // Test 13: decision of IBGP vs EBGP // ================================================================ next_hop_resolver.unset_nexthop_metric(nexthop1); next_hop_resolver.unset_nexthop_metric(nexthop3); next_hop_resolver.set_nexthop_metric(nexthop1, 200); next_hop_resolver.set_nexthop_metric(nexthop3, 100); peer_data2->set_as(AsNum(9)); peer_data2->compute_peer_type(); debug_table->write_comment("******************************************"); debug_table->write_comment("TEST 13"); debug_table->write_comment("TEST OF IBGP vs EBGP"); debug_table->write_comment("SENDING FROM PEER 1"); debug_table->write_comment("PEER 1 HAS BETTER IGP DISTANCE"); debug_table->write_comment("PEER 2 IS EBGP"); ribin_table1->add_route(net1, fpalist1, pt); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 2"); debug_table->write_comment("EXPECT: WINS"); ribin_table2->add_route(net1, fpalist3, pt); debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 1"); ribin_table1->delete_route(net1); debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 2"); ribin_table2->delete_route(net1); debug_table->write_separator(); // ================================================================ // Test 13B: decision of IBGP vs EBGP // ================================================================ debug_table->write_comment("******************************************"); debug_table->write_comment("TEST 13B"); debug_table->write_comment("TEST OF IBGP vs EBGP"); debug_table->write_comment("SENDING FROM PEER 1"); debug_table->write_comment("PEER 1 HAS BETTER IGP DISTANCE"); debug_table->write_comment("PEER 2 IS EBGP"); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 2"); ribin_table2->add_route(net1, fpalist3, pt); debug_table->write_separator(); debug_table->write_comment("SENDING FROM PEER 1"); debug_table->write_comment("EXPECT: LOSES"); ribin_table1->add_route(net1, fpalist1, pt); debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 2"); ribin_table2->delete_route(net1); debug_table->write_separator(); debug_table->write_comment("DELETION FROM PEER 1"); ribin_table1->delete_route(net1); debug_table->write_separator(); // ================================================================ debug_table->write_comment("SHUTDOWN AND CLEAN UP"); delete decision_table; delete debug_table; delete ribin_table1; delete ribin_table2; delete ribin_table3; delete nhlookup_table1; delete nhlookup_table2; delete nhlookup_table3; palist1.release(); palist2.release(); palist3.release(); palist4.release(); palist5.release(); palist6.release(); FILE *file = fopen(filename.c_str(), "r"); if (file == NULL) { fprintf(stderr, "Failed to read %s\n", filename.c_str()); fprintf(stderr, "TEST DECISION FAILED\n"); fclose(file); return false; } #define BUFSIZE 80000 char testout[BUFSIZE]; memset(testout, 0, BUFSIZE); int bytes1 = fread(testout, 1, BUFSIZE, file); if (bytes1 == BUFSIZE) { fprintf(stderr, "Output too long for buffer\n"); fprintf(stderr, "TEST DECISION FAILED\n"); fclose(file); return false; } fclose(file); string ref_filename; const char* srcdir = getenv("srcdir"); if (srcdir) { ref_filename = string(srcdir); } else { ref_filename = "."; } ref_filename += "/test_decision.reference"; file = fopen(ref_filename.c_str(), "r"); if (file == NULL) { fprintf(stderr, "Failed to read %s\n", ref_filename.c_str()); fprintf(stderr, "TEST DECISION FAILED\n"); fclose(file); return false; } char refout[BUFSIZE]; memset(refout, 0, BUFSIZE); int bytes2 = fread(refout, 1, BUFSIZE, file); if (bytes2 == BUFSIZE) { fprintf(stderr, "Output too long for buffer\n"); fprintf(stderr, "TEST DECISION FAILED\n"); fclose(file); return false; } fclose(file); if ((bytes1 != bytes2) || (memcmp(testout, refout, bytes1) != 0)) { fprintf(stderr, "Output in %s doesn't match reference output\n", filename.c_str()); fprintf(stderr, "TEST DECISION FAILED\n"); return false; } #ifndef HOST_OS_WINDOWS unlink(filename.c_str()); #else DeleteFileA(filename.c_str()); #endif return true; } xorp/bgp/tests/test_policy_dump.reference0000664000076400007640000000214011421137511021026 0ustar greearbgreearb[comment] TEST 1 [comment] ADD, CONFIG, DELETE [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/1000, AS/3, AS/2, AS/1] [comment] CONFIGURE EXPORT FILTER [comment] PUSH_ROUTES [comment] RUN EVENT LOOP TO COMPLETION [comment] EXPECT DELETE TO *NOT* HAVE LOCALPREF [comment] EXPECT ADD TO HAVE LOCALPREF OF 200 [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/1000, AS/3, AS/2, AS/1] [ADD] CHANGED flag is set SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/1000, AS/3, AS/2, AS/1] Local Preference Attribute - 200 [comment] EXPECT DELETE TO HAVE LOCALPREF OF 200 [DELETE] CHANGED flag is set SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/1000, AS/3, AS/2, AS/1] Local Preference Attribute - 200 [separator]------------------------------------- [comment] SHUTDOWN AND CLEAN UP xorp/bgp/tests/test_nhlookup.cc0000664000076400007640000005151111540224220016772 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "bgp_module.h" #include "libxorp/xorp.h" #include "libxorp/xorpfd.hh" #include "libxorp/eventloop.hh" #include "libxorp/xlog.h" #include "libxorp/test_main.hh" #ifdef HAVE_PWD_H #include #endif #include "bgp.hh" #include "route_table_nhlookup.hh" #include "route_table_debug.hh" #include "path_attribute.hh" #include "local_data.hh" template class DummyResolver : public NextHopResolver { public: DummyResolver(EventLoop& eventloop, BGPMain& bgp) : NextHopResolver(0, eventloop, bgp) { _ofile = NULL; _response = false; } void set_output_file(FILE *file) { _ofile = file;} void set_response(bool response) {_response = response;} bool register_nexthop(A nexthop, IPNet net, NhLookupTable */*requester*/) { fprintf(_ofile, "[NH REGISTER]\n"); fprintf(_ofile, "NextHop: %s\n", nexthop.str().c_str()); fprintf(_ofile, "Net: %s\n", net.str().c_str()); return _response; } void deregister_nexthop(A nexthop, IPNet net, NhLookupTable */*requester*/) { fprintf(_ofile, "[NH DEREGISTER]\n"); fprintf(_ofile, "NextHop: %s\n", nexthop.str().c_str()); fprintf(_ofile, "Net: %s\n", net.str().c_str()); } private: FILE *_ofile; bool _response; }; bool test_nhlookup(TestInfo& /*info*/) { #ifndef HOST_OS_WINDOWS struct passwd *pwd = getpwuid(getuid()); string filename = "/tmp/test_nhlookup."; filename += pwd->pw_name; #else char *tmppath = (char *)malloc(256); GetTempPathA(256, tmppath); string filename = string(tmppath) + "test_nhlookup"; free(tmppath); #endif EventLoop eventloop; BGPMain bgpmain(eventloop); LocalData localdata(bgpmain.eventloop()); Iptuple iptuple; BGPPeerData *pd1 = new BGPPeerData(localdata, iptuple, AsNum(0), IPv4(),0); BGPPeer peer1(&localdata, pd1, NULL, &bgpmain); PeerHandler handler1("test1", &peer1, NULL, NULL); BGPPeerData *pd2 = new BGPPeerData(localdata, iptuple, AsNum(0), IPv4(),0); BGPPeer peer2(&localdata, pd2, NULL, &bgpmain); PeerHandler handler2("test2", &peer2, NULL, NULL); DummyResolver nh_resolver(bgpmain.eventloop(), bgpmain); //trivial plumbing NhLookupTable *nhlookup_table = new NhLookupTable("NHLOOKUP", SAFI_UNICAST, &nh_resolver, NULL); DebugTable* debug_table = new DebugTable("D1", (BGPRouteTable*)nhlookup_table); nhlookup_table->set_next_table(debug_table); debug_table->set_output_file(filename); debug_table->set_canned_response(ADD_USED); //use the same output file as debug table nh_resolver.set_output_file(debug_table->output_file()); //create a load of attributes IPNet net1("1.0.1.0/24"); IPNet net2("1.0.2.0/24"); IPv4 nexthop1("2.0.0.1"); NextHopAttribute nhatt1(nexthop1); IPv4 nexthop2("2.0.0.2"); NextHopAttribute nhatt2(nexthop2); IPv4 nexthop3("2.0.0.3"); NextHopAttribute nhatt3(nexthop3); OriginAttribute igp_origin_att(IGP); ASPath aspath1; aspath1.prepend_as(AsNum(1)); aspath1.prepend_as(AsNum(2)); aspath1.prepend_as(AsNum(3)); ASPathAttribute aspathatt1(aspath1); ASPath aspath2; aspath2.prepend_as(AsNum(4)); aspath2.prepend_as(AsNum(5)); aspath2.prepend_as(AsNum(6)); ASPathAttribute aspathatt2(aspath2); ASPath aspath3; aspath3.prepend_as(AsNum(7)); aspath3.prepend_as(AsNum(8)); aspath3.prepend_as(AsNum(9)); ASPathAttribute aspathatt3(aspath3); FPAList4Ref fpalist1 = new FastPathAttributeList(nhatt1, aspathatt1, igp_origin_att); PAListRef palist1 = new PathAttributeList(fpalist1); FPAList4Ref fpalist2 = new FastPathAttributeList(nhatt2, aspathatt2, igp_origin_att); PAListRef palist2 = new PathAttributeList(fpalist2); FPAList4Ref fpalist3 = new FastPathAttributeList(nhatt3, aspathatt3, igp_origin_att); PAListRef palist3 = new PathAttributeList(fpalist3); SubnetRoute *sr1, *sr2, *sr3; InternalMessage *msg, *msg2; //================================================================ //Test1: trivial add and delete, nexthop resolves //================================================================ //add a route debug_table->write_comment("TEST 1"); debug_table->write_comment("ADD AND DELETE, NEXTHOP RESOLVES"); //pretend we have already resolved the nexthop nh_resolver.set_response(true); sr1 = new SubnetRoute(net1, palist1, NULL); msg = new InternalMessage(sr1, &handler1, 0); nhlookup_table->add_route(*msg, NULL); sr1->unref(); delete msg; debug_table->write_separator(); //delete the route sr1 = new SubnetRoute(net1, palist1, NULL); msg = new InternalMessage(sr1, &handler1, 0); nhlookup_table->delete_route(*msg, NULL); debug_table->write_separator(); sr1->unref(); delete msg; //================================================================ //Test1a: trivial add and delete, nexthop doesn't yet resolve //================================================================ //add a route debug_table->write_comment("TEST 1a"); debug_table->write_comment("ADD AND DELETE, NEXTHOP NOT YET RESOLVED"); //pretend we have already resolved the nexthop nh_resolver.set_response(false); sr1 = new SubnetRoute(net1, palist1, NULL); msg = new InternalMessage(sr1, &handler1, 0); nhlookup_table->add_route(*msg, NULL); delete msg; sr1->unref(); debug_table->write_separator(); //delete the route sr1 = new SubnetRoute(net1, palist1, NULL); msg = new InternalMessage(sr1, &handler1, 0); nhlookup_table->delete_route(*msg, NULL); debug_table->write_separator(); delete msg; sr1->unref(); //================================================================ //Test1b: trivial add and delete, nexthop doesn't yet resolve //================================================================ //add a route debug_table->write_comment("TEST 1b"); debug_table->write_comment("ADD AND DELETE, NEXTHOP NOT YET RESOLVED"); //pretend we have not yet resolved the nexthop nh_resolver.set_response(false); sr1 = new SubnetRoute(net1, palist1, NULL); msg = new InternalMessage(sr1, &handler1, 0); nhlookup_table->add_route(*msg, NULL); //Note: don't sr1->unref() here - the NhLookupTable assumes the //subnet route is still held in the RIB-In. delete msg; debug_table->write_separator(); debug_table->write_comment("THEN NEXTHOP RESOLVES"); set > nets; nets.insert(net1); nhlookup_table->RIB_lookup_done(nexthop1, nets, true); nets.erase(nets.begin()); assert(nets.empty()); debug_table->write_separator(); debug_table->write_comment("NOW DELETE THE ROUTE"); //delete the route msg = new InternalMessage(sr1, &handler1, 0); nhlookup_table->delete_route(*msg, NULL); debug_table->write_separator(); sr1->unref(); delete msg; //================================================================ //Test1c: trivial add and delete, nexthop doesn't yet resolve. // Resolve two routes that use the same nexthop //================================================================ debug_table->write_comment("TEST 1c"); debug_table->write_comment("ADDx2 AND DELETE, NEXTHOP NOT YET RESOLVED"); //pretend we have not yet resolved the nexthop nh_resolver.set_response(false); sr1 = new SubnetRoute(net1, palist1, NULL); msg = new InternalMessage(sr1, &handler1, 0); nhlookup_table->add_route(*msg, NULL); //Note: don't delete sr1 here - the NhLookupTable assumes the //subnet route is still held in the RIB-In. delete msg; sr2 = new SubnetRoute(net2, palist1, NULL); msg = new InternalMessage(sr2, &handler1, 0); nhlookup_table->add_route(*msg, NULL); //Note: don't delete sr2 here - the NhLookupTable assumes the //subnet route is still held in the RIB-In. delete msg; debug_table->write_separator(); debug_table->write_comment("THEN NEXTHOP RESOLVES"); //tell NhLookupTable the nexthop now resolves nets.insert(net1); nets.insert(net2); nhlookup_table->RIB_lookup_done(nexthop1, nets, true); while (!nets.empty()) nets.erase(nets.begin()); debug_table->write_separator(); debug_table->write_comment("NOW DELETE THE ROUTES"); //delete the routes msg = new InternalMessage(sr1, &handler1, 0); nhlookup_table->delete_route(*msg, NULL); sr1->unref(); delete msg; msg = new InternalMessage(sr2, &handler1, 0); nhlookup_table->delete_route(*msg, NULL); sr2->unref(); delete msg; debug_table->write_separator(); //================================================================ //Test2: add, nexthop doesn't yet resolve, replace, resolve, delete //================================================================ debug_table->write_comment("TEST 2"); debug_table->write_comment("ADD, REPLACE, RESOLVE, DELETE"); //pretend we have not yet resolved the nexthop nh_resolver.set_response(false); sr1 = new SubnetRoute(net1, palist1, NULL); msg = new InternalMessage(sr1, &handler1, 0); debug_table->write_comment("ADD"); nhlookup_table->add_route(*msg, NULL); //Note: don't delete sr1 here - the NhLookupTable assumes the //subnet route is still held in the RIB-In. delete msg; debug_table->write_separator(); msg = new InternalMessage(sr1, &handler1, 0); sr2 = new SubnetRoute(net1, palist2, NULL); msg2 = new InternalMessage(sr2, &handler1, 0); debug_table->write_comment("REPLACE"); nhlookup_table->replace_route(*msg, *msg2, NULL); //Note: don't delete sr2 here - the NhLookupTable assumes the //subnet route is still held in the RIB-In. sr1->unref(); delete msg; delete msg2; debug_table->write_separator(); debug_table->write_comment("THEN NEXTHOP RESOLVES"); nets.insert(net1); nhlookup_table->RIB_lookup_done(nexthop2, nets, true); nets.erase(nets.begin()); assert(nets.empty()); debug_table->write_separator(); debug_table->write_comment("NOW DELETE THE ROUTE"); //delete the route msg = new InternalMessage(sr2, &handler1, 0); nhlookup_table->delete_route(*msg, NULL); debug_table->write_separator(); sr2->unref(); delete msg; //================================================================ //Test2b: add, nexthop doesn't yet resolve, replace, resolve, delete //================================================================ debug_table->write_comment("TEST 2b"); debug_table->write_comment("ADD, REPLACE (OLD IS DELETED), RESOLVE, DELETE"); //pretend we have not yet resolved the nexthop nh_resolver.set_response(false); sr1 = new SubnetRoute(net1, palist1, NULL); msg = new InternalMessage(sr1, &handler1, 0); debug_table->write_comment("ADD"); nhlookup_table->add_route(*msg, NULL); //Note: don't sr1->unref() here - the NhLookupTable assumes the //subnet route is still held in the RIB-In. delete msg; debug_table->write_separator(); sr3 = new SubnetRoute(net1, palist1, NULL); msg = new InternalMessage(sr3, &handler1, 0); //delete the old route, as might happen with a replace route on RibIn sr1->unref(); sr2 = new SubnetRoute(net1, palist2, NULL); msg2 = new InternalMessage(sr2, &handler1, 0); debug_table->write_comment("REPLACE"); nhlookup_table->replace_route(*msg, *msg2, NULL); //Note: don't sr2->unref() here - the NhLookupTable assumes the //subnet route is still held in the RIB-In. sr3->unref(); delete msg; delete msg2; debug_table->write_separator(); debug_table->write_comment("THEN NEXTHOP RESOLVES"); nets.insert(net1); nhlookup_table->RIB_lookup_done(nexthop2, nets, true); nets.erase(nets.begin()); assert(nets.empty()); debug_table->write_separator(); debug_table->write_comment("NOW DELETE THE ROUTE"); //delete the route msg = new InternalMessage(sr2, &handler1, 0); nhlookup_table->delete_route(*msg, NULL); debug_table->write_separator(); sr2->unref(); delete msg; //================================================================ //Test3: replace, nexthop doesn't yet resolve, replace, resolve, delete //================================================================ //add a route debug_table->write_comment("TEST 3"); debug_table->write_comment("REPLACE, REPLACE, RESOLVE, DELETE"); //We'll start off by sending a replace. This is so we can get the //replace into the queue, and then send a replace to replace the //replace. But it also tests the case where the previous nexthop //did resolve and now the new one doesn't. nh_resolver.set_response(false); sr1 = new SubnetRoute(net1, palist1, NULL); sr2 = new SubnetRoute(net1, palist2, NULL); msg = new InternalMessage(sr1, &handler1, 0); msg2 = new InternalMessage(sr2, &handler1, 0); debug_table->write_comment("FIRST REPLACE"); nhlookup_table->replace_route(*msg, *msg2, NULL); //Note: don't sr2->unref() here - the NhLookupTable assumes the //subnet route is still held in the RIB-In. delete msg; delete msg2; sr1->unref(); sr2->unref(); debug_table->write_separator(); sr1 = new SubnetRoute(net1, palist2, NULL); sr3 = new SubnetRoute(net1, palist3, NULL); msg = new InternalMessage(sr1, &handler1, 0); msg2 = new InternalMessage(sr3, &handler1, 0); debug_table->write_comment("SECOND REPLACE"); nhlookup_table->replace_route(*msg, *msg2, NULL); //Note: don't sr2->unref() here - the NhLookupTable assumes the //subnet route is still held in the RIB-In. sr1->unref(); delete msg; delete msg2; debug_table->write_separator(); debug_table->write_comment("THEN NEXTHOP RESOLVES"); nets.insert(net1); nhlookup_table->RIB_lookup_done(nexthop3, nets, true); nets.erase(nets.begin()); assert(nets.empty()); debug_table->write_separator(); debug_table->write_comment("NOW DELETE THE ROUTE"); //delete the route msg = new InternalMessage(sr3, &handler1, 0); nhlookup_table->delete_route(*msg, NULL); debug_table->write_separator(); sr3->unref(); delete msg; //================================================================ //Test4: checking push is propagated properly. // trivial add and delete, nexthop doesn't yet resolve. // Resolve two routes that use the same nexthop //================================================================ debug_table->write_comment("TEST 4"); debug_table->write_comment("TESTING PUSH"); debug_table->write_comment("ADDx2 AND DELETE, NEXTHOP NOT YET RESOLVED"); //pretend we have not yet resolved the nexthop nh_resolver.set_response(false); sr1 = new SubnetRoute(net1, palist1, NULL); msg = new InternalMessage(sr1, &handler1, 0); msg->set_push(); nhlookup_table->add_route(*msg, NULL); //Note: don't delete sr1 here - the NhLookupTable assumes the //subnet route is still held in the RIB-In. delete msg; sr2 = new SubnetRoute(net2, palist1, NULL); msg = new InternalMessage(sr2, &handler1, 0); nhlookup_table->add_route(*msg, NULL); //Note: don't delete sr2 here - the NhLookupTable assumes the //subnet route is still held in the RIB-In. delete msg; debug_table->write_comment("SEND PUSH"); nhlookup_table->push(NULL); debug_table->write_separator(); debug_table->write_comment("THEN NEXTHOP RESOLVES"); //tell NhLookupTable the nexthop now resolves nets.insert(net1); nets.insert(net2); nhlookup_table->RIB_lookup_done(nexthop1, nets, true); while (!nets.empty()) nets.erase(nets.begin()); debug_table->write_separator(); debug_table->write_comment("NOW DELETE THE ROUTES"); //delete the routes msg = new InternalMessage(sr1, &handler1, 0); nhlookup_table->delete_route(*msg, NULL); sr1->unref(); delete msg; msg = new InternalMessage(sr2, &handler1, 0); msg->set_push(); nhlookup_table->delete_route(*msg, NULL); sr2->unref(); delete msg; debug_table->write_comment("SEND PUSH AGAIN"); nhlookup_table->push(NULL); debug_table->write_separator(); //================================================================ //Test5: trivial add when resolves, replace with one that doesn't //resolve, delete //================================================================ //add a route debug_table->write_comment("TEST 5"); debug_table->write_comment("ADD, REPLACE WITH UNRESOLVABLE, DELETE"); //pretend we have resolved the nexthop nh_resolver.set_response(true); sr1 = new SubnetRoute(net1, palist1, NULL); msg = new InternalMessage(sr1, &handler1, 0); nhlookup_table->add_route(*msg, NULL); delete msg; debug_table->write_separator(); //pretend it now doesn't resolve nh_resolver.set_response(false); msg = new InternalMessage(sr1, &handler1, 0); sr2 = new SubnetRoute(net1, palist2, NULL); msg2 = new InternalMessage(sr2, &handler1, 0); debug_table->write_comment("REPLACE"); nhlookup_table->replace_route(*msg, *msg2, NULL); delete msg; delete msg2; //delete sr1 now, as that would have now been deleted from the //RibIn. sr2 must remain, as the nexthop code assumes the route //is still stored upstream. sr1->unref(); debug_table->write_separator(); debug_table->write_comment("NOW DELETE THE ROUTE"); //delete the route msg = new InternalMessage(sr2, &handler1, 0); nhlookup_table->delete_route(*msg, NULL); debug_table->write_separator(); sr2->unref(); delete msg; //================================================================ //Check debug output against reference //================================================================ debug_table->write_separator(); debug_table->write_comment("SHUTDOWN AND CLEAN UP"); delete nhlookup_table; delete debug_table; palist1.release(); palist2.release(); palist3.release(); fpalist1 = 0; fpalist2 = 0; fpalist3 = 0; FILE *file = fopen(filename.c_str(), "r"); if (file == NULL) { fprintf(stderr, "Failed to read %s\n", filename.c_str()); fprintf(stderr, "TEST NHLOOKUP FAILED\n"); fclose(file); return false; } #define BUFSIZE 8192 char testout[BUFSIZE]; memset(testout, 0, BUFSIZE); int bytes1 = fread(testout, 1, BUFSIZE, file); if (bytes1 == BUFSIZE) { fprintf(stderr, "Output too long for buffer\n"); fprintf(stderr, "TEST NHLOOKUP FAILED\n"); fclose(file); return false; } fclose(file); string ref_filename; const char* srcdir = getenv("srcdir"); if (srcdir) { ref_filename = string(srcdir); } else { ref_filename = "."; } ref_filename += "/test_nhlookup.reference"; file = fopen(ref_filename.c_str(), "r"); if (file == NULL) { fprintf(stderr, "Failed to read %s\n", ref_filename.c_str()); fprintf(stderr, "TEST NHLOOKUP FAILED\n"); fclose(file); return false; } char refout[BUFSIZE]; memset(refout, 0, BUFSIZE); int bytes2 = fread(refout, 1, BUFSIZE, file); if (bytes2 == BUFSIZE) { fprintf(stderr, "Output too long for buffer\n"); fprintf(stderr, "TEST NHLOOKUP FAILED\n"); fclose(file); return false; } fclose(file); if ((bytes1 != bytes2) || (memcmp(testout, refout, bytes1)!= 0)) { fprintf(stderr, "Output in %s doesn't match reference output\n", filename.c_str()); fprintf(stderr, "TEST NHLOOKUP FAILED\n"); return false; } #ifndef HOST_OS_WINDOWS unlink(filename.c_str()); #else DeleteFileA(filename.c_str()); #endif return true; } xorp/bgp/tests/test_fanout.reference0000664000076400007640000002774311421137511020016 0ustar greearbgreearb[comment] ****************************************** [comment] TEST 1 [comment] ADD AND DELETE [comment] SENDING FROM PEER 1 [** TABLE DebugTable-D2 **] [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [** TABLE DebugTable-D1 **] [PUSH] [** TABLE DebugTable-D2 **] [PUSH] [separator]------------------------------------- [** TABLE DebugTable-D2 **] [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [** TABLE DebugTable-D1 **] [PUSH] [** TABLE DebugTable-D2 **] [PUSH] [separator]------------------------------------- [comment] SENDING FROM PEER 2 [** TABLE DebugTable-D1 **] [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [** TABLE DebugTable-D1 **] [PUSH] [** TABLE DebugTable-D2 **] [PUSH] [separator]------------------------------------- [** TABLE DebugTable-D1 **] [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [** TABLE DebugTable-D1 **] [PUSH] [** TABLE DebugTable-D2 **] [PUSH] [separator]------------------------------------- [comment] ****************************************** [comment] TEST 2 [comment] SIMPLE FLOW CONTROL - PEER 2 BUSY [comment] SENDING FROM PEER 1 [** TABLE DebugTable-D1 **] [PUSH] [separator]------------------------------------- [comment] PEER 2 GET_NEXT_MESSAGE [** TABLE DebugTable-D2 **] [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [separator]------------------------------------- [comment] PEER 2 GET_NEXT_MESSAGE [** TABLE DebugTable-D2 **] [PUSH] [separator]------------------------------------- [comment] PEER 2 GET_NEXT_MESSAGE [separator]------------------------------------- [comment] ****************************************** [comment] TEST 2B [comment] SIMPLE FLOW CONTROL - PEER 2 BUSY [comment] SENDING FROM PEER 1 [separator]------------------------------------- [comment] PEER 2 GET_NEXT_MESSAGE [** TABLE DebugTable-D2 **] [ADD] PUSH flag is set SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [separator]------------------------------------- [comment] PEER 2 GET_NEXT_MESSAGE [separator]------------------------------------- [comment] PEER 2 GET_NEXT_MESSAGE [separator]------------------------------------- [comment] ****************************************** [comment] TEST 3 [comment] INTERLEAVED FLOW CONTROL - BOTH PEERS BUSY [comment] SENDING FROM PEER 1 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [separator]------------------------------------- [comment] PEER 1 GET_NEXT_MESSAGE [** TABLE DebugTable-D1 **] [ADD] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/5, AS/4] [separator]------------------------------------- [comment] PEER 1 GET_NEXT_MESSAGE [** TABLE DebugTable-D1 **] [DELETE] SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/5, AS/4] [separator]------------------------------------- [comment] PEER 1 GET_NEXT_MESSAGE [separator]------------------------------------- [comment] PEER 2 GET_NEXT_MESSAGE [** TABLE DebugTable-D2 **] [ADD] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [separator]------------------------------------- [comment] PEER 2 GET_NEXT_MESSAGE [** TABLE DebugTable-D2 **] [DELETE] SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [separator]------------------------------------- [comment] PEER 2 GET_NEXT_MESSAGE [separator]------------------------------------- [comment] ****************************************** [comment] TEST 4 [comment] INTERLEAVED FLOW CONTROL - BOTH PEERS BUSY [comment] SENDING FROM PEER 1 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [separator]------------------------------------- [comment] PEER 1 GET_NEXT_MESSAGE [** TABLE DebugTable-D1 **] [ADD] PUSH flag is set SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/5, AS/4] [separator]------------------------------------- [comment] PEER 1 GET_NEXT_MESSAGE [separator]------------------------------------- [comment] PEER 1 GET_NEXT_MESSAGE [separator]------------------------------------- [comment] PEER 2 GET_NEXT_MESSAGE [** TABLE DebugTable-D2 **] [ADD] PUSH flag is set SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [separator]------------------------------------- [comment] PEER 2 GET_NEXT_MESSAGE [separator]------------------------------------- [comment] PEER 2 GET_NEXT_MESSAGE [separator]------------------------------------- [comment] ****************************************** [comment] TEST 5 [comment] INTERLEAVED FLOW CONTROL - THREE PEERS BUSY [comment] SENDING FROM PEER 1 [separator]------------------------------------- [comment] SENDING FROM PEER 2 [separator]------------------------------------- [comment] PEER 1 GET_NEXT_MESSAGE [** TABLE DebugTable-D1 **] [ADD] PUSH flag is set SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/5, AS/4] [separator]------------------------------------- [comment] PEER 1 GET_NEXT_MESSAGE [separator]------------------------------------- [comment] PEER 1 GET_NEXT_MESSAGE [separator]------------------------------------- [comment] PEER 2 GET_NEXT_MESSAGE [** TABLE DebugTable-D2 **] [ADD] PUSH flag is set SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [separator]------------------------------------- [comment] PEER 2 GET_NEXT_MESSAGE [separator]------------------------------------- [comment] PEER 2 GET_NEXT_MESSAGE [separator]------------------------------------- [comment] PEER 3 GET_NEXT_MESSAGE [** TABLE DebugTable-D3 **] [ADD] PUSH flag is set SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [separator]------------------------------------- [comment] PEER 3 GET_NEXT_MESSAGE [** TABLE DebugTable-D3 **] [ADD] PUSH flag is set SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/5, AS/4] [separator]------------------------------------- [comment] PEER 3 GET_NEXT_MESSAGE [separator]------------------------------------- [comment] PEER 3 GET_NEXT_MESSAGE [separator]------------------------------------- [comment] PEER 3 GET_NEXT_MESSAGE [separator]------------------------------------- [comment] ****************************************** [comment] TEST 6 [comment] TEST QUEUING [comment] SENDING FROM PEER 1 [separator]------------------------------------- [separator]------------------------------------- [comment] SENDING FROM PEER 2 [separator]------------------------------------- [separator]------------------------------------- [comment] PEER 3 GET_NEXT_MESSAGE [comment] EXPECT TO GET A ROUTE [** TABLE DebugTable-D3 **] [ADD] PUSH flag is set SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [separator]------------------------------------- [comment] PEER 3 GET_NEXT_MESSAGE [comment] EXPECT TO GET A ROUTE [** TABLE DebugTable-D3 **] [ADD] PUSH flag is set SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/5, AS/4] [separator]------------------------------------- [comment] PEER 3 GET_NEXT_MESSAGE [comment] EXPECT TO GET NO ROUTE [separator]------------------------------------- [comment] PEER 2 GET_NEXT_MESSAGE [comment] EXPECT TO GET A ROUTE [** TABLE DebugTable-D2 **] [ADD] PUSH flag is set SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [separator]------------------------------------- [comment] PEER 2 GET_NEXT_MESSAGE [comment] EXPECT TO GET NO ROUTE [separator]------------------------------------- [comment] PEER 1 SKIP ENTIRE QUEUE [comment] EXPECT TO GET NO ROUTE [separator]------------------------------------- [comment] ****************************************** [comment] TEST 7 [comment] TEST QUEUING [comment] SENDING FROM PEER 1 [separator]------------------------------------- [separator]------------------------------------- [comment] SENDING FROM PEER 2 [separator]------------------------------------- [separator]------------------------------------- [comment] PEER 3 GET_NEXT_MESSAGE [comment] EXPECT TO GET A ROUTE [** TABLE DebugTable-D3 **] [ADD] PUSH flag is set SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [separator]------------------------------------- [comment] PEER 1 SKIP ENTIRE QUEUE [comment] EXPECT TO GET NO ROUTE [separator]------------------------------------- [separator]------------------------------------- [comment] PEER 3 GET_NEXT_MESSAGE [comment] EXPECT TO GET A ROUTE [** TABLE DebugTable-D3 **] [ADD] PUSH flag is set SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/5, AS/4] [separator]------------------------------------- [comment] PEER 3 GET_NEXT_MESSAGE [comment] EXPECT TO GET NO ROUTE [separator]------------------------------------- [comment] PEER 2 GET_NEXT_MESSAGE [comment] EXPECT TO GET A ROUTE [** TABLE DebugTable-D2 **] [ADD] PUSH flag is set SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [separator]------------------------------------- [comment] PEER 2 GET_NEXT_MESSAGE [comment] EXPECT TO GET NO ROUTE [comment] ****************************************** [comment] TEST 8 [comment] TEST QUEUING [separator]------------------------------------- [comment] SENDING FROM PEER 2 [separator]------------------------------------- [comment] SENDING FROM PEER 1 [separator]------------------------------------- [separator]------------------------------------- [comment] PEER 3 GET_NEXT_MESSAGE [comment] EXPECT TO GET A ROUTE [** TABLE DebugTable-D3 **] [ADD] PUSH flag is set SubnetRoute: Net: 1.0.2.0/24 PAList: Next Hop Attribute 2.0.0.2 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/6, AS/5, AS/4] [separator]------------------------------------- [comment] PEER 1 SKIP ENTIRE QUEUE [comment] EXPECT TO GET NO ROUTE [separator]------------------------------------- [separator]------------------------------------- [comment] PEER 3 GET_NEXT_MESSAGE [comment] EXPECT TO GET A ROUTE [** TABLE DebugTable-D3 **] [ADD] PUSH flag is set SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [separator]------------------------------------- [comment] PEER 3 GET_NEXT_MESSAGE [comment] EXPECT TO GET NO ROUTE [separator]------------------------------------- [comment] PEER 2 GET_NEXT_MESSAGE [comment] EXPECT TO GET A ROUTE [** TABLE DebugTable-D2 **] [ADD] PUSH flag is set SubnetRoute: Net: 1.0.1.0/24 PAList: Next Hop Attribute 2.0.0.1 Origin Path Attribute - IGP AS Path Attribute ASPath: [AS/3, AS/2, AS/1] [separator]------------------------------------- [comment] PEER 2 GET_NEXT_MESSAGE [comment] EXPECT TO GET NO ROUTE [comment] SHUTDOWN AND CLEAN UP xorp/bgp/tools/0000775000076400007640000000000011631506761013576 5ustar greearbgreearbxorp/bgp/tools/bgptools_module.h0000664000076400007640000000236211421137511017136 0ustar greearbgreearb/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- */ /* * Copyright (c) 2001-2009 XORP, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 2, June * 1991 as published by the Free Software Foundation. Redistribution * and/or modification of this program under the terms of any other * version of the GNU General Public License is not permitted. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, * see the GNU General Public License, Version 2, a copy of which can be * found in the XORP LICENSE.gpl file. * * XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; * http://xorp.net */ /* * $XORP: xorp/bgp/tools/bgptools_module.h,v 1.11 2008/10/02 21:56:27 bms Exp $ */ /* * Module definitions. */ #ifndef __BGP_TOOLS_BGPTOOLS_MODULE_H__ #define __BGP_TOOLS_BGPTOOLS_MODULE_H__ #ifndef XORP_MODULE_NAME #define XORP_MODULE_NAME "BGPTOOLS" #endif #ifndef XORP_MODULE_VERSION #define XORP_MODULE_VERSION "0.1" #endif #endif /* __BGP_TOOLS_BGPTOOLS_MODULE_H__ */ xorp/bgp/tools/print_peer.cc0000664000076400007640000003014711421137511016247 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "print_peer.hh" #include "libxorp/asnum.hh" PrintPeers::PrintPeers(bool verbose, int interval) : XrlBgpV0p3Client(&_xrl_rtr), _xrl_rtr(_eventloop, "print_peers"), _verbose(verbose) { _prev_no_bgp = false; _prev_no_peers = false; // Wait for the finder to become ready. { bool timed_out = false; XorpTimer t = _eventloop.set_flag_after_ms(10000, &timed_out); while (_xrl_rtr.connected() == false && timed_out == false) { _eventloop.run(); } if (_xrl_rtr.connected() == false) { XLOG_WARNING("XrlRouter did not become ready. No Finder?"); } } for (;;) { _done = false; _token = 0; _count = 0; get_peer_list_start(); while (_done == false) { _eventloop.run(); } // Infinite loop by design. // The command will be repeated every interval seconds until // interrupted by the user. if (interval <= 0) break; TimerList::system_sleep(TimeVal(interval, 0)); } } void PrintPeers::get_peer_list_start() { XorpCallback3::RefPtr cb; cb = callback(this, &PrintPeers::get_peer_list_start_done); send_get_peer_list_start("bgp", cb); } void PrintPeers::get_peer_list_start_done(const XrlError& e, const uint32_t* token, const bool* more) { if (e != XrlError::OKAY()) { //fprintf(stderr, "Failed to get peer list start\n"); if (_prev_no_bgp == false) printf("\n\nBGP is not running or may not be configured\n"); _prev_no_bgp = true; _done = true; return; } _prev_no_bgp = false; if (*more == false) { if (_prev_no_peers == false) printf("\n\nNo BGP peers are configured\n"); _prev_no_peers = true; _done = true; return; } _prev_no_peers = false; _token = *token; get_peer_list_next(); } void PrintPeers::get_peer_list_next() { XorpCallback6::RefPtr cb; cb = callback(this, &PrintPeers::get_peer_list_next_done); send_get_peer_list_next("bgp", _token, cb); } void PrintPeers::get_peer_list_next_done(const XrlError& e, const string* local_ip, const uint32_t* local_port, const string* peer_ip, const uint32_t* peer_port, const bool* more) { if (e != XrlError::OKAY()) { fprintf(stderr, "Failed to get peer list next\n"); _done = true; return; } if (local_port == 0 && peer_port == 0) { //this is an error condition where the last peer was deleted //before we could list it. _done = true; return; } _count++; printf("Peer %u: local %s/%u remote %s/%u\n", XORP_UINT_CAST(_count), local_ip->c_str(), XORP_UINT_CAST(*local_port), peer_ip->c_str(), XORP_UINT_CAST(*peer_port)); if (_verbose) { _more = *more; print_peer_verbose(*local_ip, *local_port, *peer_ip, *peer_port); return; } else { if (*more == false) { _done = true; return; } get_peer_list_next(); } } void PrintPeers::print_peer_verbose(const string& local_ip, uint32_t local_port, const string& peer_ip, uint32_t peer_port) { //node: ports are still in network byte order //pipeline all the requests so we get as close to possible to an //instantaneous snapshot of the peer config _received = 0; XorpCallback2::RefPtr cb1; cb1 = callback(this, &PrintPeers::get_peer_id_done); send_get_peer_id("bgp", local_ip, local_port, peer_ip, peer_port, cb1); XorpCallback3::RefPtr cb2; cb2 = callback(this, &PrintPeers::get_peer_status_done); send_get_peer_status("bgp", local_ip, local_port, peer_ip, peer_port, cb2); XorpCallback2::RefPtr cb3; cb3 = callback(this, &PrintPeers::get_peer_negotiated_version_done); send_get_peer_negotiated_version("bgp", local_ip, local_port, peer_ip, peer_port, cb3); XorpCallback2::RefPtr cb4; cb4 = callback(this, &PrintPeers::get_peer_as_done); send_get_peer_as("bgp", local_ip, local_port, peer_ip, peer_port, cb4); XorpCallback7::RefPtr cb5; cb5 = callback(this, &PrintPeers::get_peer_msg_stats_done); send_get_peer_msg_stats("bgp", local_ip, local_port, peer_ip, peer_port, cb5); XorpCallback3::RefPtr cb6; cb6 = callback(this, &PrintPeers::get_peer_established_stats_done); send_get_peer_established_stats("bgp", local_ip, local_port, peer_ip, peer_port, cb6); XorpCallback8::RefPtr cb7; cb7 = callback(this, &PrintPeers::get_peer_timer_config_done); send_get_peer_timer_config("bgp", local_ip, local_port, peer_ip, peer_port, cb7); } void PrintPeers::get_peer_id_done(const XrlError& e, const IPv4* peer_id) { if (e != XrlError::OKAY()) { //printf("Failed to retrieve verbose data (%s)\n", e.str().c_str()); if (_more) get_peer_list_next(); return; } _peer_id = *peer_id; _received++; if (_received == 7) do_verbose_peer_print(); } void PrintPeers::get_peer_status_done(const XrlError& e, const uint32_t* peer_state, const uint32_t* admin_status) { if (e != XrlError::OKAY()) { //printf("Failed to retrieve verbose data\n"); if (_more) get_peer_list_next(); return; } _peer_state = *peer_state; _admin_state = *admin_status; _received++; if (_received == 7) do_verbose_peer_print(); } void PrintPeers::get_peer_negotiated_version_done(const XrlError& e, const int32_t* neg_version) { if (e != XrlError::OKAY()) { //printf("Failed to retrieve verbose data\n"); if (_more) get_peer_list_next(); return; } _negotiated_version = *neg_version; _received++; if (_received == 7) do_verbose_peer_print(); } void PrintPeers::get_peer_as_done(const XrlError& e, const string* peer_as) { if (e != XrlError::OKAY()) { //printf("Failed to retrieve verbose data\n"); if (_more) get_peer_list_next(); return; } AsNum asn(*peer_as); _peer_as = asn.as4(); _received++; if (_received == 7) do_verbose_peer_print(); } void PrintPeers::get_peer_msg_stats_done(const XrlError& e, const uint32_t* in_updates, const uint32_t* out_updates, const uint32_t* in_msgs, const uint32_t* out_msgs, const uint32_t* last_error, const uint32_t* in_update_elapsed) { if (e != XrlError::OKAY()) { //printf("Failed to retrieve verbose data\n"); if (_more) get_peer_list_next(); return; } _in_updates = *in_updates; _out_updates = *out_updates; _in_msgs = *in_msgs; _out_msgs = *out_msgs; _last_error = *last_error; _in_update_elapsed = *in_update_elapsed; _received++; if (_received == 7) do_verbose_peer_print(); } void PrintPeers::get_peer_established_stats_done(const XrlError& e, const uint32_t* transitions, const uint32_t* established_time) { if (e != XrlError::OKAY()) { //printf("Failed to retrieve verbose data\n"); if (_more) get_peer_list_next(); return; } _transitions = *transitions; _established_time = *established_time; _received++; if (_received == 7) do_verbose_peer_print(); } void PrintPeers::get_peer_timer_config_done(const XrlError& e, const uint32_t* retry_interval, const uint32_t* hold_time, const uint32_t* keep_alive, const uint32_t* hold_time_conf, const uint32_t* keep_alive_conf, const uint32_t* min_as_origination_interval, const uint32_t* min_route_adv_interval) { if (e != XrlError::OKAY()) { printf("Failed to retrieve verbose data\n"); if (_more) get_peer_list_next(); else _done = true; return; } _retry_interval = *retry_interval; _hold_time = *hold_time; _keep_alive = *keep_alive; _hold_time_conf = *hold_time_conf; _keep_alive_conf = *keep_alive_conf; _min_as_origination_interval = *min_as_origination_interval; _min_route_adv_interval = *min_route_adv_interval; _received++; if (_received == 7) do_verbose_peer_print(); } string PrintPeers::time_units(uint32_t secs) const { string s; if (secs == 1) s = "1 second"; else s = c_format("%u seconds", XORP_UINT_CAST(secs)); return s; } void PrintPeers::do_verbose_peer_print() { if (_peer_state == 6) { printf(" Peer ID: %s\n", _peer_id.str().c_str()); } else { printf(" Peer ID: none\n"); } printf(" Peer State: "); switch (_peer_state) { case 1: printf("IDLE\n"); break; case 2: printf("CONNECT\n"); break; case 3: printf("ACTIVE\n"); break; case 4: printf("OPENSENT\n"); break; case 5: printf("OPENCONFIRM\n"); break; case 6: printf("ESTABLISHED\n"); break; case 7: printf("IDLE(*)\n"); break; default: printf("UNKNOWN (THIS SHOULDN'T HAPPEN)\n"); } printf(" Admin State: "); switch (_admin_state) { case 1: printf("STOPPED\n"); break; case 2: printf("START\n"); break; default: printf("UNKNOWN (THIS SHOULDN'T HAPPEN)\n"); } if (_peer_state == 6) printf(" Negotiated BGP Version: %d\n", XORP_INT_CAST(_negotiated_version)); else printf(" Negotiated BGP Version: n/a\n"); printf(" Peer AS Number: %u\n", XORP_UINT_CAST(_peer_as)); printf(" Updates Received: %u, Updates Sent: %u\n", XORP_UINT_CAST(_in_updates), XORP_UINT_CAST(_out_updates)); printf(" Messages Received: %u, Messages Sent: %u\n", XORP_UINT_CAST(_in_msgs), XORP_UINT_CAST(_out_msgs)); if (_in_updates > 0) { printf(" Time since last received update: %s\n", time_units(_in_update_elapsed).c_str()); } else { printf(" Time since last received update: n/a\n"); } printf(" Number of transitions to ESTABLISHED: %u\n", XORP_UINT_CAST(_transitions)); if (_peer_state == 6) { printf(" Time since last entering ESTABLISHED state: %s\n", time_units(_established_time).c_str()); } else if (_transitions > 0) { printf(" Time since last in ESTABLISHED state: %s\n", time_units(_established_time).c_str()); } else { printf(" Time since last in ESTABLISHED state: n/a\n"); } printf(" Retry Interval: %s\n", time_units(_retry_interval).c_str()); if (_peer_state == 6) { printf(" Hold Time: %s, Keep Alive Time: %s\n", time_units(_hold_time).c_str(), time_units(_keep_alive).c_str()); } else { printf(" Hold Time: n/a, Keep Alive Time: n/a\n"); } printf(" Configured Hold Time: %s, Configured Keep Alive Time: %s\n", time_units(_hold_time_conf).c_str(), time_units(_keep_alive_conf).c_str()); printf(" Minimum AS Origination Interval: %s\n", time_units(_min_as_origination_interval).c_str()); printf(" Minimum Route Advertisement Interval: %s\n", time_units(_min_route_adv_interval).c_str()); if (_more) { printf("\n"); get_peer_list_next(); } else { _done = true; } } xorp/bgp/tools/print_peer.hh0000664000076400007640000000760511421137511016264 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/bgp/tools/print_peer.hh,v 1.19 2008/10/02 21:56:28 bms Exp $ #ifndef __BGP_TOOLS_PRINT_PEER_HH__ #define __BGP_TOOLS_PRINT_PEER_HH__ #include "bgptools_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxipc/xrl_std_router.hh" #include "libxipc/xrl_args.hh" #include "xrl/interfaces/bgp_xif.hh" class PrintPeers : public XrlBgpV0p3Client { public: PrintPeers(bool verbose, int interval); void get_peer_list_start(); void get_peer_list_start_done(const XrlError& e, const uint32_t* token, const bool* more); void get_peer_list_next(); void get_peer_list_next_done(const XrlError& e, const string* local_ip, const uint32_t* local_port, const string* peer_ip, const uint32_t* peer_port, const bool* more); void print_peer_verbose(const string& local_ip, uint32_t local_port, const string& peer_ip, uint32_t peer_port); void get_peer_id_done(const XrlError& e, const IPv4* peer_id); void get_peer_status_done(const XrlError& e, const uint32_t* peer_state, const uint32_t* admin_status); void get_peer_negotiated_version_done(const XrlError&, const int32_t* neg_version); void get_peer_as_done(const XrlError& e, const string* peer_as); void get_peer_msg_stats_done(const XrlError& e, const uint32_t* in_updates, const uint32_t* out_updates, const uint32_t* in_msgs, const uint32_t* out_msgs, const uint32_t* last_error, const uint32_t* in_update_elapsed); void get_peer_established_stats_done(const XrlError&, const uint32_t* transitions, const uint32_t* established_time); void get_peer_timer_config_done(const XrlError& e, const uint32_t* retry_interval, const uint32_t* hold_time, const uint32_t* keep_alive, const uint32_t* hold_time_conf, const uint32_t* keep_alive_conf, const uint32_t* min_as_origination_interval, const uint32_t* min_route_adv_interval); void do_verbose_peer_print(); string time_units(uint32_t s) const; private: EventLoop _eventloop; XrlStdRouter _xrl_rtr; bool _verbose; uint32_t _token; bool _done; uint32_t _count; bool _prev_no_bgp; bool _prev_no_peers; //temporary storage while we retrieve peer data. bool _more; //after this one, are there more peers to retrieve uint32_t _received; IPv4 _peer_id; uint32_t _peer_state; uint32_t _admin_state; int32_t _negotiated_version; uint32_t _peer_as; uint32_t _in_updates; uint32_t _out_updates; uint32_t _in_msgs; uint32_t _out_msgs; uint32_t _last_error; uint32_t _in_update_elapsed; uint32_t _transitions; uint32_t _established_time; uint32_t _retry_interval; uint32_t _hold_time; uint32_t _keep_alive; uint32_t _hold_time_conf; uint32_t _keep_alive_conf; uint32_t _min_as_origination_interval; uint32_t _min_route_adv_interval; }; #endif // __BGP_TOOLS_PRINT_PEER_HH__ xorp/bgp/tools/print_routes.cc0000664000076400007640000001674311540225521016644 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "print_routes.hh" // ---------------------------------------------------------------------------- // Specialized PrintRoutes implementation template <> void PrintRoutes::get_route_list_start(IPNet net, bool unicast, bool multicast) { _active_requests = 0; send_get_v4_route_list_start("bgp", net, unicast, multicast, callback(this, &PrintRoutes::get_route_list_start_done)); } template <> void PrintRoutes::get_route_list_next() { send_get_v4_route_list_next("bgp", _token, callback(this, &PrintRoutes::get_route_list_next_done)); } // ---------------------------------------------------------------------------- // Common PrintRoutes implementation template PrintRoutes::PrintRoutes(detail_t verbose, int interval, IPNet net, bool unicast, bool multicast, int lines) : XrlBgpV0p3Client(&_xrl_rtr), _xrl_rtr(_eventloop, "print_routes"), _verbose(verbose), _unicast(unicast), _multicast(multicast), _lines(lines) { _prev_no_bgp = false; _prev_no_routes = false; // Wait for the finder to become ready. { bool timed_out = false; XorpTimer t = _eventloop.set_flag_after_ms(10000, &timed_out); while (_xrl_rtr.connected() == false && timed_out == false) { _eventloop.run(); } if (_xrl_rtr.connected() == false) { XLOG_WARNING("XrlRouter did not become ready. No Finder?"); } } for (;;) { _done = false; _token = 0; _count = 0; get_route_list_start(net, _unicast, _multicast); while (_done == false || _active_requests > 0) { _eventloop.run(); if (_lines == static_cast(_count)) { printf("Output truncated at %u lines\n", XORP_UINT_CAST(_count)); break; } } // Infinite loop by design. // The command will be repeated every interval seconds until // interrupted by the user. if (interval <= 0) break; //delay before next call XorpCallback0::RefPtr cb = callback(this, &PrintRoutes::timer_expired); _done = false; _timer = _eventloop.new_oneoff_after_ms(interval*1000, cb); while (_done == false) { _eventloop.run(); } } } template void PrintRoutes::get_route_list_start_done(const XrlError& e, const uint32_t* token) { if (e != XrlError::OKAY()) { //fprintf(stderr, "Failed to get peer list start\n"); if (_prev_no_bgp == false) printf("\n\nBGP is not running or may not be configured\n"); _prev_no_bgp = true; _done = true; return; } _prev_no_bgp = false; switch(_verbose) { case SUMMARY: case NORMAL: printf("Status Codes: * valid route, > best route\n"); printf("Origin Codes: i IGP, e EGP, ? incomplete\n\n"); printf(" Prefix Nexthop " "Peer AS Path\n"); printf(" ------ ------- " "---- -------\n"); break; case DETAIL: break; } _token = *token; for (uint32_t i = 0; i < MAX_REQUESTS; i++) { _active_requests++; get_route_list_next(); } } // See RFC 1657 (BGP MIB) for full definitions of return values. template void PrintRoutes::get_route_list_next_done(const XrlError& e, const IPv4* peer_id, const IPNet* net, const uint32_t *best_and_origin, const vector* aspath, const A* nexthop, const int32_t* med, const int32_t* localpref, const int32_t* atomic_agg, const vector* aggregator, const int32_t* calc_localpref, const vector* attr_unknown, const bool* valid, const bool* /*unicast*/, const bool* /*multicast*/) { UNUSED(calc_localpref); UNUSED(attr_unknown); UNUSED(aspath); if (e != XrlError::OKAY() || false == *valid) { _active_requests--; _done = true; return; } _count++; uint8_t best = (*best_and_origin)>>16; uint8_t origin = (*best_and_origin)&255; ASPath asp((const uint8_t*)(&((*aspath)[0])), aspath->size()); switch(_verbose) { case SUMMARY: case NORMAL: //XXX this should be used to indicate a route is valid printf("*"); switch (best) { case 1: printf(" "); break; case 2: printf(">"); break; default: printf("?"); } printf(" %-20s %-25s %-12s %s ", net->str().c_str(), nexthop->str().c_str(), peer_id->str().c_str(), asp.short_str().c_str()); switch (origin) { case IGP: printf("i\n"); break; case EGP: printf("e\n"); break; case INCOMPLETE: printf("?\n"); break; default: printf ("BAD ORIGIN\n"); break; } break; case DETAIL: printf("%s\n", cstring(*net)); printf("\tFrom peer: %s\n", cstring(*peer_id)); printf("\tRoute: "); switch (best) { case 1: printf("Not Used\n"); break; case 2: printf("Winner\n"); break; default: printf("UNKNOWN\n"); } printf("\tOrigin: "); switch (origin) { case IGP: printf("IGP\n"); break; case EGP: printf("EGP\n"); break; case INCOMPLETE: printf("INCOMPLETE\n"); break; default: printf("BAD ORIGIN\n"); break; } printf("\tAS Path: %s\n", asp.short_str().c_str()); printf("\tNexthop: %s\n", cstring(*nexthop)); if (INVALID != *med) printf("\tMultiple Exit Discriminator: %d\n", *med); if (INVALID != *localpref) printf("\tLocal Preference: %d\n", *localpref); if (2 == *atomic_agg) printf("\tAtomic Aggregate: Less Specific Route Selected\n"); #if 0 printf("\tAtomic Aggregate: "); switch (*atomic_agg) { case 1: printf("Less Specific Route Not Selected\n"); break; case 2: printf("Less Specific Route Selected\n"); break; default: printf("UNKNOWN\n"); break; } #endif if (!aggregator->empty()) { XLOG_ASSERT(6 == aggregator->size()); A agg(&((*aggregator)[0])); AsNum asnum(&((*aggregator)[4])); printf("\tAggregator: %s %s\n", cstring(agg), cstring(asnum)); } break; } get_route_list_next(); } template void PrintRoutes::timer_expired() { _done = true; } // ---------------------------------------------------------------------------- // Template Instantiations template class PrintRoutes; #ifdef HAVE_IPV6 template <> void PrintRoutes::get_route_list_start(IPNet net, bool unicast, bool multicast) { _active_requests = 0; send_get_v6_route_list_start("bgp", net, unicast, multicast, callback(this, &PrintRoutes::get_route_list_start_done)); } template <> void PrintRoutes::get_route_list_next() { send_get_v6_route_list_next("bgp", _token, callback(this, &PrintRoutes::get_route_list_next_done)); } template class PrintRoutes; #endif // ipv6 xorp/bgp/tools/print_routes.hh0000664000076400007640000000522211421137511016643 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/bgp/tools/print_routes.hh,v 1.25 2008/10/02 21:56:28 bms Exp $ #ifndef __BGP_TOOLS_PRINT_PEER_HH__ #define __BGP_TOOLS_PRINT_PEER_HH__ #include "bgptools_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxipc/xrl_std_router.hh" #include "libxipc/xrl_args.hh" #include "xrl/interfaces/bgp_xif.hh" #include "bgp/aspath.hh" #include "bgp/path_attribute.hh" template class PrintRoutes : public XrlBgpV0p3Client { public: static const uint32_t MAX_REQUESTS = 100; static const int32_t INVALID = -1; enum detail_t {SUMMARY, NORMAL, DETAIL}; PrintRoutes(detail_t verbose, int interval, IPNet net, bool unicast, bool multicast, int lines = -1); void get_route_list_start(IPNet net, bool unicast, bool multicast); void get_route_list_start_done(const XrlError& e, const uint32_t* token); void get_route_list_next(); void get_route_list_next_done(const XrlError& e, const IPv4* peer_id, const IPNet* net, const uint32_t *best_and_origin, const vector* aspath, const A* nexthop, const int32_t* med, const int32_t* localpref, const int32_t* atomic_agg, const vector* aggregator, const int32_t* calc_localpref, const vector* attr_unknown, const bool* valid, const bool* unicast, const bool* multicast); private: void timer_expired(); EventLoop _eventloop; XrlStdRouter _xrl_rtr; detail_t _verbose; uint32_t _token; bool _done; uint32_t _count; bool _prev_no_bgp; bool _prev_no_routes; XorpTimer _timer; int _active_requests; bool _unicast; bool _multicast; int _lines; }; #endif // __BGP_TOOLS_PRINT_PEER_HH__ xorp/bgp/tools/SConscript0000664000076400007640000000626611631506761015622 0ustar greearbgreearb# Copyright (c) 2009-2011 XORP, Inc and Others # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # $XORP$ import os Import('env') env = env.Clone() is_shared = env.has_key('SHAREDLIBS') env.AppendUnique(CPPPATH = [ '#', '$BUILDDIR', ]) env.AppendUnique(LIBPATH = [ '$BUILDDIR/libxorp', '$BUILDDIR/libcomm', '$BUILDDIR/libxipc', '$BUILDDIR/libfeaclient', '$BUILDDIR/policy/backend', '$BUILDDIR/policy/common', '$BUILDDIR/xrl/interfaces', '$BUILDDIR/xrl/targets', '$BUILDDIR/bgp', '.', ]) env.AppendUnique(LIBS = [ 'xorp_bgp', # for ASPath 'xif_bgp', 'xorp_policy_backend', 'xorp_policy_common', 'xorp_ipc', 'xorp_core', 'xorp_comm', 'xif_rib', 'xorp_fea_client', 'xif_fea_ifmgr_mirror', 'xst_fea_ifmgr_mirror', 'xif_fea_ifmgr_replicator', 'xst_bgp', 'xif_finder_event_notifier', ]) if not is_shared: env.AppendUnique(LIBS = [ "crypto", ]) if not (env.has_key('mingw') and env['mingw']): env.AppendUnique(LIBS = [ "rt", ]) if (env.has_key('mingw') and env['mingw']): env.AppendUnique(LIBS = [ 'ws2_32', 'iphlpapi', # 'mprapi', 'regex', 'winmm', ]) env.Append(LIBS = ['xorp_core']) if not (env.has_key('disable_profile') and env['disable_profile']): env.AppendUnique(LIBS = [ 'xif_profile_client', ]) env.Replace(RPATH = [ env.Literal(env['xorp_tool_rpath']) ]) ### print_peers ppeers_srcs = [ 'print_peer.cc', 'print_peers.cc', ] ppeers = env.Program(target = 'print_peers', source = ppeers_srcs) # don't install -for debug only #env.Alias('install', # env.InstallProgram(env['xorp_tooldir'], ppeers)) ### print_routes proutes_srcs = [ 'print_routes.cc', 'print_routes_main.cc', ] proutes = env.Program(target = 'print_routes', source = proutes_srcs) # don't install -for debug only #env.Alias('install', # env.InstallProgram(env['xorp_tooldir'], proutes)) ### xorpsh_print_peers xppeers_srcs = [ 'print_peer.cc', 'xorpsh_print_peers.cc', ] xppeers = env.Program(target = 'bgp_print_peers', source = xppeers_srcs) env.Alias('install', env.InstallProgram(env['xorp_tooldir'], xppeers)) ### xorpsh_print_routes xproutes_srcs = [ 'print_routes.cc', 'xorpsh_print_routes.cc', ] xproutes = env.Program(target = 'bgp_print_routes', source = xproutes_srcs) env.Alias('install', env.InstallProgram(env['xorp_tooldir'], xproutes)) Default(xppeers, xproutes) xorp/bgp/tools/print_routes_main.cc0000664000076400007640000000663511421137511017646 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "print_routes.hh" #include "bgp/aspath.hh" #include "bgp/path_attribute.hh" #ifdef HAVE_GETOPT_H #include #endif void usage() { fprintf(stderr, "Usage: print_routes [-4 -6 -u -m -s -v -p ]" " [-l ]" " [-i ]\n" "-4 IPv4\n" "-6 IPv6\n" "-p \n" "-u Unicast\n" "-m Multicast\n" "-s summary output\n" "-v enables verbose output\n" ); } int main(int argc, char **argv) { XorpUnexpectedHandler x(xorp_unexpected_handler); // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); string prefix; bool ipv4, ipv6, unicast, multicast; ipv4 = ipv6 = unicast = multicast = false; int lines = -1; PrintRoutes::detail_t verbose_ipv4 = PrintRoutes::NORMAL; PrintRoutes::detail_t verbose_ipv6 = PrintRoutes::NORMAL; int c; int interval = -1; while ((c = getopt(argc, argv, "46p:umvi:l:")) != -1) { switch (c) { case '4': ipv4 = true; break; case '6': ipv6 = true; break; case 'p': prefix = optarg; break; case 'u': unicast = true; break; case 'm': multicast = true; break; case 'l': lines = atoi(optarg); break; case 's': verbose_ipv4 = PrintRoutes::SUMMARY; verbose_ipv6 = PrintRoutes::SUMMARY; break; case 'v': verbose_ipv4 = PrintRoutes::DETAIL; verbose_ipv6 = PrintRoutes::DETAIL; break; case 'i': interval = atoi(optarg); break; default: usage(); return -1; } } if (ipv4 == false && ipv6 == false) ipv4 = true; if (unicast == false && multicast == false) unicast = true; try { if (ipv4) { IPNet net; if ("" != prefix) { IPNet subnet(prefix.c_str()); net = subnet; } PrintRoutes route_printer(verbose_ipv4, interval, net, unicast, multicast, lines); } if (ipv6) { IPNet net; if ("" != prefix) { IPNet subnet(prefix.c_str()); net = subnet; } PrintRoutes route_printer(verbose_ipv6, interval, net, unicast, multicast, lines); } } catch(...) { xorp_catch_standard_exceptions(); } // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); return 0; } xorp/bgp/tools/xorpsh_print_peers.cc0000664000076400007640000000415211421137511020032 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "print_peer.hh" void usage() { fprintf(stderr, "Usage: xorpsh_print_peers show bgp peers [detail]\n" "where detail enables verbose output.\n"); } int main(int argc, char **argv) { XorpUnexpectedHandler x(xorp_unexpected_handler); // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); bool verbose = false; int interval = -1; if (argc > 5) { usage(); return -1; } if (argc < 4) { usage(); return -1; } if (strcmp(argv[1], "show") != 0) { usage(); return -1; } if (strcmp(argv[2], "bgp") != 0) { usage(); return -1; } if (strcmp(argv[3], "peers") != 0) { usage(); return -1; } if (argc == 5) { if (strcmp(argv[4], "detail")==0) { verbose = true; } else { usage(); return -1; } } try { PrintPeers peer_printer(verbose, interval); } catch(...) { xorp_catch_standard_exceptions(); } // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); return 0; } xorp/bgp/tools/print_peers.cc0000664000076400007640000000370711421137511016434 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "print_peer.hh" #ifdef HAVE_GETOPT_H #include #endif void usage() { fprintf(stderr, "Usage: print_peer [-v] [-i ]\n" "where -v enables verbose output.\n"); } int main(int argc, char **argv) { XorpUnexpectedHandler x(xorp_unexpected_handler); // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); bool verbose = false; int c; int interval = -1; while ((c = getopt(argc, argv, "i:v")) != -1) { switch (c) { case 'v': verbose = true; break; case 'i': interval = atoi(optarg); break; default: usage(); return -1; } } try { PrintPeers peer_printer(verbose, interval); } catch(...) { xorp_catch_standard_exceptions(); } // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); return 0; } xorp/bgp/tools/xorpsh_print_routes.cc0000664000076400007640000001026611540225521020241 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "print_routes.hh" #include "bgp/aspath.hh" #include "bgp/path_attribute.hh" #ifdef HAVE_GETOPT_H #include #endif void usage() { fprintf(stderr, "Usage: [ -l ] xorpsh_print_routes show bgp routes " "[ipv4|ipv6] [unicast|multicast] [summary|detail] [x.x.x.x/n]\n" "where summary enables brief output.\n"); } int main(int argc, char **argv) { XorpUnexpectedHandler x(xorp_unexpected_handler); // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); IPNet net_ipv4; IPNet net_ipv6; bool ipv4, ipv6, unicast, multicast; ipv4 = ipv6 = unicast = multicast = false; PrintRoutes::detail_t verbose_ipv4 = PrintRoutes::NORMAL; PrintRoutes::detail_t verbose_ipv6 = PrintRoutes::NORMAL; int interval = -1; int lines = -1; int c; while ((c = getopt(argc, argv, "46uml:")) != -1) { switch (c) { case '4': ipv4 = true; break; case '6': ipv6 = true; break; case 'u': unicast = true; break; case 'm': multicast = true; break; case 'l': lines = atoi(optarg); break; default: printf("hello\n"); usage(); return -1; } } argc -= optind; argv += optind; if ((argc < 3) || (argc > 7)) { usage(); return -1; } if (strcmp(argv[0], "show") != 0) { usage(); return -1; } if (strcmp(argv[1], "bgp") != 0) { usage(); return -1; } if (strcmp(argv[2], "routes") != 0) { usage(); return -1; } int counter = 4; if (argc > 3) { if (strcmp(argv[3], "ipv4") == 0) { ipv4 = true; counter++; } if (strcmp(argv[3], "ipv6") == 0) { ipv6 = true; counter++; } } if (argc > 4) { if (strcmp(argv[4], "unicast") == 0) { unicast = true; counter++; } if (strcmp(argv[4], "multicast") == 0) { multicast = true; counter++; } } if (argc >= counter) { if (strcmp(argv[counter - 1], "summary")==0) { verbose_ipv4 = PrintRoutes::SUMMARY; verbose_ipv6 = PrintRoutes::SUMMARY; counter++; } else if (strcmp(argv[counter - 1], "detail")==0) { verbose_ipv4 = PrintRoutes::DETAIL; verbose_ipv6 = PrintRoutes::DETAIL; counter++; } } if (argc == counter) { try { IPNet subnet(argv[counter - 1]); net_ipv4 = subnet; } catch(...) { try { IPNet subnet(argv[counter - 1]); net_ipv6 = subnet; } catch(...) { // xorp_catch_standard_exceptions(); usage(); return -1; } } } if ((ipv4 == false) && (ipv6 == false)) ipv4 = true; if ((unicast == false) && (multicast == false)) unicast = true; try { if (ipv4) { PrintRoutes route_printer(verbose_ipv4, interval, net_ipv4, unicast, multicast, lines); } #ifdef HAVE_IPV6 if (ipv6) { PrintRoutes route_printer(verbose_ipv6, interval, net_ipv6, unicast, multicast, lines); } #endif } catch(...) { xorp_catch_standard_exceptions(); } // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); return 0; } xorp/bgp/update_packet.cc0000664000076400007640000002155111421137511015550 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net //#define DEBUG_LOGGING //#define DEBUG_PRINT_FUNCTION_NAME #include "bgp_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "packet.hh" #include "peer.hh" #if 1 void dump_bytes(const uint8_t *d, size_t l) { printf("DEBUG_BYTES FN : %p %u\n", d, XORP_UINT_CAST(l)); for (u_int i=0;i()) { _Type = MESSAGETYPEUPDATE; } UpdatePacket::~UpdatePacket() { } void UpdatePacket::add_nlri(const BGPUpdateAttrib& nlri) { _nlri_list.push_back(nlri); } void UpdatePacket::add_pathatt(const PathAttribute& pa) { _pa_list->add_path_attribute(pa); } void UpdatePacket::add_pathatt(PathAttribute *pa) { _pa_list->add_path_attribute(pa); } void UpdatePacket::replace_pathattribute_list(FPAList4Ref& pa_list) { _pa_list = pa_list; } void UpdatePacket::add_withdrawn(const BGPUpdateAttrib& wdr) { _wr_list.push_back(wdr); } bool UpdatePacket::big_enough() const { /* is the packet now large enough that we'd be best to send the part we already have before it exceeds the 4K size limit? */ //XXXX needs additional tests for v6 //quick and dirty check if (((_wr_list.size() + _nlri_list.size())* 4) > 2048) { debug_msg("withdrawn size = %u\n", XORP_UINT_CAST(_wr_list.size())); debug_msg("nlri size = %u\n", XORP_UINT_CAST(_wr_list.size())); return true; } return false; } bool UpdatePacket::encode(uint8_t *d, size_t &len, const BGPPeerData *peerdata) const { XLOG_ASSERT( (_nlri_list.empty()) || !(_pa_list->is_empty()) ); XLOG_ASSERT(d != 0); XLOG_ASSERT(len != 0); debug_msg("UpdatePacket::encode: len=%u\n", (uint32_t)len); #if 0 list ::const_iterator pai; #endif size_t i, pa_len = 0; size_t wr_len = wr_list().wire_size(); size_t nlri_len = nlri_list().wire_size(); // compute packet length pa_len = BGPPacket::MAXPACKETSIZE; uint8_t pa_list_buf[pa_len]; if (_pa_list->is_empty() ) { pa_len = 0; } else { if (_pa_list->encode(pa_list_buf, pa_len, peerdata) == false) { XLOG_WARNING("failed to encode update - no space for pa list\n"); return false; } } #if 0 for (pai = pa_list().begin() ; pai != pa_list().end(); ++pai) pa_len += (*pai)->wire_size(); #endif size_t desired_len = BGPPacket::MINUPDATEPACKET + wr_len + pa_len + nlri_len; if (len < desired_len) { abort(); ///XXXX XLOG_WARNING("failed to encode update, desired_len=%u, len=%u, wr=%u, pa=%u, nlri=%u\n", (uint32_t)desired_len, (uint32_t)len, (uint32_t)wr_len, (uint32_t)pa_len, (uint32_t)nlri_len); return false; } len = desired_len; if (len > BGPPacket::MAXPACKETSIZE) // XXX XLOG_FATAL("Attempt to encode a packet that is too big"); debug_msg("Withdrawn Routes: %u Net Reach: %u length: %u\n", XORP_UINT_CAST(_wr_list.size()), XORP_UINT_CAST(_nlri_list.size()), XORP_UINT_CAST(len)); d = basic_encode(len, d); // allocate buffer and fill header // fill withdraw list length (bytes) d[BGPPacket::COMMON_HEADER_LEN] = (wr_len >> 8) & 0xff; d[BGPPacket::COMMON_HEADER_LEN + 1] = wr_len & 0xff; // fill withdraw list i = BGPPacket::COMMON_HEADER_LEN + 2; wr_list().encode(wr_len, d + i); i += wr_len; // fill pathattribute length (bytes) d[i++] = (pa_len >> 8) & 0xff; d[i++] = pa_len & 0xff; #if 0 // fill path attribute list for (pai = pa_list().begin() ; pai != pa_list().end(); ++pai) { memcpy(d+i, (*pai)->data(), (*pai)->wire_size()); i += (*pai)->wire_size(); } #endif memcpy(d+i, pa_list_buf, pa_len); i += pa_len; // fill NLRI list nlri_list().encode(nlri_len, d+i); i += nlri_len; return true; } UpdatePacket::UpdatePacket(const uint8_t *d, uint16_t l, const BGPPeerData* peerdata, BGPMain *mainprocess, bool do_checks) throw(CorruptMessage,UnusableMessage) { debug_msg("UpdatePacket constructor called\n"); _Type = MESSAGETYPEUPDATE; if (l < BGPPacket::MINUPDATEPACKET) xorp_throw(CorruptMessage, c_format("Update Message too short %d", l), MSGHEADERERR, BADMESSLEN, d + BGPPacket::MARKER_SIZE, 2); d += BGPPacket::COMMON_HEADER_LEN; // move past header size_t wr_len = (d[0] << 8) + d[1]; // withdrawn length if (BGPPacket::MINUPDATEPACKET + wr_len > l) xorp_throw(CorruptMessage, c_format("Unreachable routes length is bogus %u > %u", XORP_UINT_CAST(wr_len), XORP_UINT_CAST(l - BGPPacket::MINUPDATEPACKET)), UPDATEMSGERR, MALATTRLIST); size_t pa_len = (d[wr_len+2] << 8) + d[wr_len+3]; // pathatt length if (BGPPacket::MINUPDATEPACKET + pa_len + wr_len > l) xorp_throw(CorruptMessage, c_format("Pathattr length is bogus %u > %u", XORP_UINT_CAST(pa_len), XORP_UINT_CAST(l - wr_len - BGPPacket::MINUPDATEPACKET)), UPDATEMSGERR, MALATTRLIST); size_t nlri_len = l - BGPPacket::MINUPDATEPACKET - pa_len - wr_len; // Start of decoding of withdrawn routes. d += 2; // point to the routes. _wr_list.decode(d, wr_len); d += wr_len; // Start of decoding of Path Attributes d += 2; // move past Total Path Attributes Length field size_t used = pa_len; _pa_list = new FastPathAttributeList(); _pa_list->load_raw_data(d, used, peerdata, (nlri_len > 0), mainprocess, do_checks); d += used; // Start of decoding of Network Reachability _nlri_list.decode(d, nlri_len); /* End of decoding of Network Reachability */ debug_msg("No of withdrawn routes %u. " "No of networks %u.\n", XORP_UINT_CAST(_wr_list.size()), XORP_UINT_CAST(_nlri_list.size())); } string UpdatePacket::str() const { string s = "Update Packet\n"; debug_msg("No of withdrawn routes %u. " "No of networks %u.\n", XORP_UINT_CAST(_wr_list.size()), XORP_UINT_CAST(_nlri_list.size())); if (!_wr_list.empty()) s += _wr_list.str("Withdrawn"); if (!_pa_list->is_empty()) { s += _pa_list->str(); s += "\n"; } #if 0 list ::const_iterator pai = pa_list().begin(); while (pai != pa_list().end()) { s = s + " - " + (*pai)->str() + "\n"; ++pai; } #endif s += _nlri_list.str("Nlri"); return s; } /* * Helper function used when sorting Path Attribute lists. */ inline bool compare_path_attributes(PathAttribute *a, PathAttribute *b) { return *a < *b; } bool UpdatePacket::operator==(const UpdatePacket& him) const { debug_msg("compare %s and %s", this->str().c_str(), him.str().c_str()); if (_wr_list != him.wr_list()) return false; #if 0 //path attribute equals list temp_att_list(pa_list()); temp_att_list.sort(compare_path_attributes); list temp_att_list_him(him.pa_list()); temp_att_list_him.sort(compare_path_attributes); if (temp_att_list.size() != temp_att_list_him.size()) return false; list ::const_iterator pai = temp_att_list.begin(); list ::const_iterator pai_him = temp_att_list_him.begin(); while (pai != temp_att_list.end() && pai_him != temp_att_list_him.end()) { if ( (**pai) == (**pai_him) ) { ++pai; ++pai_him; } else { debug_msg("%s did not match %s\n", (*pai)->str().c_str(), (*pai_him)->str().c_str()); return false; } } #endif bool him_empty = him._pa_list->is_empty(); bool me_empty = _pa_list->is_empty(); if (me_empty) { if (!him_empty) { return false; } } else { int count = 0; for (int i=0; i < MAX_ATTRIBUTE; i++) if (_pa_list->find_attribute_by_type((PathAttType)i) != 0) count++; if (him_empty) { return false; } if ( !(*_pa_list == *(him._pa_list)) ) { return false; } } //net layer reachability equals if (_nlri_list != him.nlri_list()) return false; return true; } xorp/bgp/peer_data.cc0000664000076400007640000002560111540224217014665 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #include "bgp_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "peer_data.hh" BGPPeerData::BGPPeerData(const LocalData& local_data, const Iptuple& iptuple, AsNum as, const IPv4& next_hop, const uint16_t holdtime) : _local_data(local_data), _iptuple(iptuple), _as(as), _use_4byte_asnums(false), _route_reflector(false), _confederation(false), _prefix_limit(0, false), _delay_open_time(0), _hold_duration(0), _retry_duration(0), _keepalive_duration(0), _peer_type(PEER_TYPE_UNCONFIGURED) { set_v4_local_addr(next_hop); set_configured_hold_time(holdtime); set_retry_duration(2 * 60); // Connect retry time. // if we're configured to use 4byte AS numbers, make sure we send // the relevant capability. if (_local_data.use_4byte_asnums()) { add_sent_parameter( new BGP4ByteASCapability(as) ); } // we support route refresh // add_sent_parameter( new BGPRefreshCapability() ); // add_sent_parameter( new BGPMultiRouteCapability() ); // Call this here to initialize all the state. open_negotiation(); } BGPPeerData::~BGPPeerData() { } const AsNum BGPPeerData::my_AS_number() const { AsNum confid = _local_data.get_confed_id(); if (confid.as() == AsNum::AS_INVALID) { return _local_data.get_as(); } else { if (confederation()) { return _local_data.get_as(); } else { return confid; } } XLOG_UNREACHABLE(); return AsNum(AsNum::AS_INVALID); } void BGPPeerData::compute_peer_type() { if (_local_data.get_as() == as()) { _peer_type = _local_data.get_route_reflector() && route_reflector() ? PEER_TYPE_IBGP_CLIENT : PEER_TYPE_IBGP; } else { _peer_type = _local_data.get_confed_id().as() != AsNum::AS_INVALID && confederation() ? PEER_TYPE_EBGP_CONFED : PEER_TYPE_EBGP; } } string BGPPeerData::get_peer_type_str() const { string s; switch (get_peer_type()) { case PEER_TYPE_EBGP: s += "EBGP"; break; case PEER_TYPE_IBGP: s += "IBGP"; break; case PEER_TYPE_EBGP_CONFED: s += "Confederation EBGP"; break; case PEER_TYPE_IBGP_CLIENT: s += "IBGP CLIENT"; break; case PEER_TYPE_INTERNAL: XLOG_UNREACHABLE(); // this should never happen break; default: s += c_format("UNKNOWN(%d)", get_peer_type()); } return s; } PeerType BGPPeerData::get_peer_type() const { debug_msg("Peer type retrieved as %s\n", get_peer_type_str().c_str()); return _peer_type; } bool BGPPeerData::ibgp() const { XLOG_ASSERT(PEER_TYPE_UNCONFIGURED != _peer_type); return _peer_type == PEER_TYPE_IBGP || _peer_type == PEER_TYPE_IBGP_CLIENT; } bool BGPPeerData::ibgp_vanilla() const { XLOG_ASSERT(PEER_TYPE_UNCONFIGURED != _peer_type); return _peer_type == PEER_TYPE_IBGP; } bool BGPPeerData::ibgp_client() const { XLOG_ASSERT(PEER_TYPE_UNCONFIGURED != _peer_type); return _peer_type == PEER_TYPE_IBGP_CLIENT; } bool BGPPeerData::ebgp_vanilla() const { XLOG_ASSERT(PEER_TYPE_UNCONFIGURED != _peer_type); return _peer_type == PEER_TYPE_EBGP; } bool BGPPeerData::ebgp_confed() const { XLOG_ASSERT(PEER_TYPE_UNCONFIGURED != _peer_type); return _peer_type == PEER_TYPE_EBGP_CONFED; } uint32_t BGPPeerData::get_hold_duration() const { debug_msg("BGP hold duration retrieved as %u s\n", XORP_UINT_CAST(_hold_duration)); return _hold_duration; } void BGPPeerData::set_hold_duration(uint32_t d) { _hold_duration = d; debug_msg("BGP hold duration set as %u s\n", XORP_UINT_CAST(_hold_duration)); } uint32_t BGPPeerData::get_retry_duration() const { return _retry_duration; } void BGPPeerData::set_retry_duration(uint32_t d) { _retry_duration = d; } uint32_t BGPPeerData::get_keepalive_duration() const { return _keepalive_duration; } void BGPPeerData::set_keepalive_duration(uint32_t d) { _keepalive_duration = d; } void BGPPeerData::add_parameter(ParameterList& p_list, const ParameterNode& p) { debug_msg("add_parameter %s\n", p->str().c_str()); // Its possible that a parameter is added more than once, so // remove any old instances. remove_parameter(p_list, p); p_list.push_back(p); } void BGPPeerData::remove_parameter(ParameterList& p_list, const ParameterNode& p) { debug_msg("remove_parameter %s\n", p->str().c_str()); const BGPParameter *par = p.get(); ParameterList::iterator iter; for(iter = p_list.begin(); iter != p_list.end(); iter++) { const ParameterNode& pnode = *iter; if (par->compare(*(pnode.get()))) { debug_msg("removing %s\n", pnode.get()->str().c_str()); p_list.erase(iter); return; } } // XLOG_WARNING("Could not find %s", p->str().c_str()); } void BGPPeerData::save_parameters(const ParameterList& plist) { bool multiprotocol = false; ParameterList::const_iterator i; for (i = plist.begin(); i != plist.end(); i++) { add_recv_parameter(*i); if (dynamic_cast((*i).get())) { multiprotocol = true; } } // If there wasn't any MP capability parameters in the open packet, we must // make fallback and assume that peer supports IPv4 unicast only. if (!multiprotocol) add_recv_parameter(new BGPMultiProtocolCapability(AFI_IPV4, SAFI_UNICAST)); } void BGPPeerData::open_negotiation() { // Set everything to false and use the parameters to fill in the values. _ipv4_unicast[SENT] = _ipv6_unicast[SENT] = _ipv4_multicast[SENT] = _ipv6_multicast[SENT] = false; _ipv4_unicast[RECEIVED] = _ipv6_unicast[RECEIVED] = _ipv4_multicast[RECEIVED] = _ipv6_multicast[RECEIVED] = false; _ipv4_unicast[NEGOTIATED] = _ipv6_unicast[NEGOTIATED] = _ipv4_multicast[NEGOTIATED] = _ipv6_multicast[NEGOTIATED] = false; /* ** Compare the parameters that we have sent against the ones we ** have received and place the common ones in negotiated. */ _negotiated_parameters.clear(); ParameterList::iterator iter_sent; ParameterList::iterator iter_recv; for(iter_sent = _sent_parameters.begin(); iter_sent != _sent_parameters.end(); iter_sent++) { for(iter_recv = _recv_parameters.begin(); iter_recv != _recv_parameters.end(); iter_recv++) { ParameterNode& pn_sent = *iter_sent; ParameterNode& pn_recv = *iter_recv; debug_msg("sent: %s recv: %s\n", pn_sent.get()->str().c_str(), pn_recv.get()->str().c_str()); const BGPParameter *sent = pn_sent.get(); const BGPParameter *recv = pn_recv.get(); if(recv->compare(*sent)) { debug_msg("match\n"); _negotiated_parameters.push_back(pn_sent); } } } /* ** To save lookup time cache the multiprotocol parameters that we ** have sent, received and negotiated (the union of sent and ** received). */ XLOG_ASSERT(SENT < ARRAY_SIZE); for(iter_sent = _sent_parameters.begin(); iter_sent != _sent_parameters.end(); iter_sent++) { ParameterNode& pn = *iter_sent; if(const BGPMultiProtocolCapability *multi = dynamic_cast(pn.get())) { Afi afi = multi->get_address_family(); Safi safi = multi->get_subsequent_address_family_id(); debug_msg("sent AFI = %d SAFI = %d\n", afi, safi); switch(afi) { case AFI_IPV4: switch(safi) { case SAFI_UNICAST: _ipv4_unicast[SENT] = true; break; case SAFI_MULTICAST: _ipv4_multicast[SENT] = true; break; } break; case AFI_IPV6: switch(safi) { case SAFI_UNICAST: _ipv6_unicast[SENT] = true; break; case SAFI_MULTICAST: _ipv6_multicast[SENT] = true; break; } break; } } } XLOG_ASSERT(RECEIVED < ARRAY_SIZE); for(iter_recv = _recv_parameters.begin(); iter_recv != _recv_parameters.end(); iter_recv++) { ParameterNode& pn = *iter_recv; if(const BGPMultiProtocolCapability *multi = dynamic_cast(pn.get())) { Afi afi = multi->get_address_family(); Safi safi = multi->get_subsequent_address_family_id(); debug_msg("recv AFI = %d SAFI = %d\n", afi, safi); switch(afi) { case AFI_IPV4: switch(safi) { case SAFI_UNICAST: _ipv4_unicast[RECEIVED] = true; break; case SAFI_MULTICAST: _ipv4_multicast[RECEIVED] = true; break; } break; case AFI_IPV6: switch(safi) { case SAFI_UNICAST: _ipv6_unicast[RECEIVED] = true; break; case SAFI_MULTICAST: _ipv6_multicast[RECEIVED] = true; break; } break; } } } XLOG_ASSERT(NEGOTIATED < ARRAY_SIZE); ParameterList::iterator iter_negotiated; for(iter_negotiated = _negotiated_parameters.begin(); iter_negotiated != _negotiated_parameters.end(); iter_negotiated++) { ParameterNode& pn = *iter_negotiated; if(const BGPMultiProtocolCapability *multi = dynamic_cast(pn.get())) { Afi afi = multi->get_address_family(); Safi safi = multi->get_subsequent_address_family_id(); debug_msg("negotiated AFI = %d SAFI = %d\n", afi, safi); switch(afi) { case AFI_IPV4: switch(safi) { case SAFI_UNICAST: _ipv4_unicast[NEGOTIATED] = true; break; case SAFI_MULTICAST: _ipv4_multicast[NEGOTIATED] = true; break; } break; case AFI_IPV6: switch(safi) { case SAFI_UNICAST: _ipv6_unicast[NEGOTIATED] = true; break; case SAFI_MULTICAST: _ipv6_multicast[NEGOTIATED] = true; break; } break; } } } // if we are configured for 4 byte AS numbers, and the other side // sent this capability too, then extract their AS number from it. if (_local_data.use_4byte_asnums()) { for(iter_recv = _recv_parameters.begin(); iter_recv != _recv_parameters.end(); iter_recv++) { ParameterNode& pn = *iter_recv; if(const BGP4ByteASCapability *cap4byte = dynamic_cast(pn.get())) { _use_4byte_asnums = true; _as = AsNum(cap4byte->as()); } } } } void BGPPeerData::dump_peer_data() const { debug_msg("Iptuple: %s AS: %s\n", iptuple().str().c_str(), as().str().c_str()); } xorp/bgp/route_table_damping.cc0000664000076400007640000002402611421137511016743 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #include "bgp_module.h" #include "libxorp/xlog.h" #include "bgp.hh" #include "route_table_damping.hh" template DampingTable::DampingTable(string tablename, Safi safi, BGPRouteTable* parent, const PeerHandler *peer, Damping& damping) : BGPRouteTable(tablename, safi), _peer(peer), _damping(damping), _damp_count(0) { this->_parent = parent; } template DampingTable::~DampingTable() { } template int DampingTable::add_route(InternalMessage &rtmsg, BGPRouteTable *caller) { debug_msg("\n %s\n caller: %s\n rtmsg: %p route: %p\n%s\n", this->tablename().c_str(), caller ? caller->tablename().c_str() : "NULL", &rtmsg, rtmsg.route(), rtmsg.str().c_str()); XLOG_ASSERT(caller == this->_parent); XLOG_ASSERT(this->_next_table != NULL); XLOG_ASSERT(!rtmsg.attributes()->is_locked()); if (!damping()) return this->_next_table-> add_route(rtmsg, static_cast*>(this)); if (!damping_global()) return this->_next_table-> add_route(rtmsg, static_cast*>(this)); // The first route for this network. typename Trie::iterator i = _damp.lookup_node(rtmsg.net()); if (i == _damp.end()) { Damp damp(_damping.get_tick(), _damping.get_merit()); _damp.insert(rtmsg.net(), damp); return this->_next_table-> add_route(rtmsg, static_cast*>(this)); } // Update the figure of merit and damp if necessary. Damp& damp = i.payload(); if (update_figure_of_merit(damp, rtmsg)) return ADD_UNUSED; return this->_next_table-> add_route(rtmsg, static_cast*>(this)); } template int DampingTable::replace_route(InternalMessage &old_rtmsg, InternalMessage &new_rtmsg, BGPRouteTable *caller) { debug_msg("\n %s\n" "caller: %s\n" "old rtmsg: %p new rtmsg: %p " "old route: %p" "new route: %p" "old: %s\n new: %s\n", this->tablename().c_str(), caller ? caller->tablename().c_str() : "NULL", &old_rtmsg, &new_rtmsg, old_rtmsg.route(), new_rtmsg.route(), old_rtmsg.str().c_str(), new_rtmsg.str().c_str()); XLOG_ASSERT(caller == this->_parent); XLOG_ASSERT(this->_next_table != NULL); IPNet net = old_rtmsg.net(); XLOG_ASSERT(net == new_rtmsg.net()); if (!damping()) return this->_next_table-> replace_route(old_rtmsg, new_rtmsg, static_cast*>(this)); // Find the record for this route which must exist. If the route // was being damped continue to damp. typename Trie::iterator i = _damp.lookup_node(old_rtmsg.net()); // An entry should be found, but if damping was enabled after the // original route passed through here it won't be found. if (i == _damp.end()) { return this->_next_table-> replace_route(old_rtmsg, new_rtmsg, static_cast*>(this)); } Damp& damp = i.payload(); if (damp._damped) { typename RefTrie >::iterator r; r = _damped.lookup_node(old_rtmsg.net()); XLOG_ASSERT(r != _damped.end()); TimeVal exp; if (!r.payload().timer().time_remaining(exp)) XLOG_FATAL("Route is being damped but no timer is scheduled"); r.payload().timer().unschedule(); _damped.erase(r); if (damping_global()) { DampRoute damproute(new_rtmsg.route(), new_rtmsg.genid()); damproute.timer() = eventloop(). new_oneoff_after(exp, callback(this, &DampingTable::undamp, new_rtmsg.net())); _damped.insert(new_rtmsg.net(), damproute); return ADD_UNUSED; } // Routes are no longer being damped, but they were previously // send the new route through as an add. damp._damped = false; _damp_count--; return this->_next_table-> add_route(new_rtmsg, static_cast*>(this)); } if (update_figure_of_merit(damp, new_rtmsg)) { this->_next_table-> delete_route(old_rtmsg, static_cast*>(this)); return ADD_UNUSED; } return this->_next_table-> replace_route(old_rtmsg, new_rtmsg, static_cast*>(this)); } template int DampingTable::delete_route(InternalMessage &rtmsg, BGPRouteTable *caller) { debug_msg("\n %s\n caller: %s\n rtmsg: %p route: %p\n%s\n", this->tablename().c_str(), caller ? caller->tablename().c_str() : "NULL", &rtmsg, rtmsg.route(), rtmsg.str().c_str()); XLOG_ASSERT(caller == this->_parent); XLOG_ASSERT(this->_next_table != NULL); if (!damping()) return this->_next_table-> delete_route(rtmsg, static_cast*>(this)); // Don't update the figure of merit just remove the route if it // was being damped. typename Trie::iterator i = _damp.lookup_node(rtmsg.net()); // An entry should be found, but if damping was enabled after the // original route passed through here it won't be found. if (i == _damp.end()) { return this->_next_table-> delete_route(rtmsg, static_cast*>(this)); } Damp& damp = i.payload(); if (damp._damped) { typename RefTrie >::iterator r; r = _damped.lookup_node(rtmsg.net()); XLOG_ASSERT(r != _damped.end()); r.payload().timer().unschedule(); // Not strictly necessary. _damped.erase(r); damp._damped = false; _damp_count--; return 0; } return this->_next_table-> delete_route(rtmsg, static_cast*>(this)); } template int DampingTable::push(BGPRouteTable *caller) { XLOG_ASSERT(caller == this->_parent); return this->_next_table->push((BGPRouteTable*)this); } template int DampingTable::route_dump(InternalMessage &rtmsg, BGPRouteTable *caller, const PeerHandler *dump_peer) { XLOG_ASSERT(caller == this->_parent); if (!damping()) return this->_next_table-> route_dump(rtmsg, static_cast*>(this), dump_peer); if (is_this_route_damped(rtmsg.net())) return ADD_UNUSED; return this->_next_table-> route_dump(rtmsg, static_cast*>(this), dump_peer); } template const SubnetRoute* DampingTable::lookup_route(const IPNet &net, uint32_t& genid, FPAListRef& pa_list) const { if (!damping()) return this->_parent->lookup_route(net, genid, pa_list); if (is_this_route_damped(net)) return 0; return this->_parent->lookup_route(net, genid, pa_list); } template void DampingTable::route_used(const SubnetRoute* rt, bool in_use) { if (!damping()) this->_parent->route_used(rt, in_use); if (is_this_route_damped(rt->net())) XLOG_FATAL("A damped route can't be used"); this->_parent->route_used(rt, in_use); } template bool DampingTable::update_figure_of_merit(Damp& damp, const InternalMessage &rtmsg) { // If damping has been disabled but some routes are still being // damped just return false. if (!_damping.get_damping()) return false; damp._merit = _damping.compute_merit(damp._time, damp._merit); damp._time = _damping.get_tick(); debug_msg("\n %s\n rtmsg: %p route: %p\n%s\n", this->tablename().c_str(), &rtmsg, rtmsg.route(), rtmsg.str().c_str()); debug_msg("Merit %d\n", damp._merit); // The figure of merit is above the cutoff threshold damp the route. if (_damping.cutoff(damp._merit)) { debug_msg("Damped\n"); damp._damped = true; _damp_count++; DampRoute damproute(rtmsg.route(), rtmsg.genid()); damproute.timer() = eventloop(). new_oneoff_after(TimeVal(_damping.get_reuse_time(damp._merit), 0), callback(this, &DampingTable::undamp, rtmsg.net())); _damped.insert(rtmsg.net(), damproute); return true; } return false; } template bool DampingTable::is_this_route_damped(const IPNet &net) const { typename Trie::iterator i = _damp.lookup_node(net); if (i == _damp.end()) return false; if (i.payload()._damped) return true; return false; } template void DampingTable::undamp(IPNet net) { debug_msg("Released net %s\n", cstring(net)); typename Trie::iterator i = _damp.lookup_node(net); XLOG_ASSERT(i != _damp.end()); Damp& damp = i.payload(); XLOG_ASSERT(damp._damped); typename RefTrie >::iterator r; r = _damped.lookup_node(net); XLOG_ASSERT(r != _damped.end()); InternalMessage rtmsg(r.payload().route(), _peer, r.payload().genid()); _damped.erase(r); damp._damped = false; _damp_count--; this->_next_table->add_route(rtmsg, static_cast*>(this)); this->_next_table->push(static_cast*>(this)); } template EventLoop& DampingTable::eventloop() const { return _peer->eventloop(); } template string DampingTable::str() const { string s = "DampingTable" + this->tablename(); return s; } template class DampingTable; template class DampingTable; xorp/bgp/dummy_main.cc0000664000076400007640000000637711421137511015107 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "bgp_module.h" #include "libxorp/xorp.h" #include "libxorp/xorpfd.hh" #include "libxorp/eventloop.hh" #include "libxorp/xlog.h" #include "bgp.hh" #include "path_attribute.hh" #include "iptuple.hh" BGPMain::BGPMain(EventLoop& eventloop) : _eventloop(eventloop) { _local_data = new LocalData(_eventloop); _xrl_router = NULL; } BGPMain::~BGPMain() { /* ** Any xrl events added by the destruction of the rib_ipc_hander ** are soaked up before destroying the xrl_router. */ if (_xrl_router != NULL) while(_xrl_router->pending()) eventloop().run(); delete _local_data; } int BGPMain::startup() { return (XORP_OK); } int BGPMain::shutdown() { return (XORP_OK); } void BGPMain::status_change(ServiceBase* service, ServiceStatus old_status, ServiceStatus new_status) { UNUSED(service); UNUSED(old_status); UNUSED(new_status); } void BGPMain::tree_complete() { } void BGPMain::updates_made() { } bool BGPMain::interface_address4(const IPv4& /*address*/) const { return false; } bool BGPMain::interface_address6(const IPv6& /*address*/) const { return false; } bool BGPMain::interface_address_prefix_len4(const IPv4& /*address*/, uint32_t& /*prefix_len*/) const { return false; } bool BGPMain::interface_address_prefix_len6(const IPv6& /*address*/, uint32_t& /*prefix_len*/) const { return false; } void BGPMain::main_loop() { } void BGPMain::local_config(const uint32_t&, const IPv4&, bool) { } /* ** Callback registered with the asyncio code. */ void BGPMain::connect_attempt(XorpFd, IoEventType, string, uint16_t) { } void BGPMain::attach_peer(BGPPeer*) { } void BGPMain::detach_peer(BGPPeer*) { } /* ** Find this peer if it exists. */ BGPPeer * BGPMain::find_peer(const Iptuple&) { return 0; } bool BGPMain::create_peer(BGPPeerData *) { return false; } bool BGPMain::delete_peer(const Iptuple&) { return false; } bool BGPMain::enable_peer(const Iptuple&) { return false; } bool BGPMain::disable_peer(const Iptuple&) { return false; } bool BGPMain::register_ribname(const string&) { return false; } XorpFd BGPMain::create_listener(const Iptuple&) { XorpFd tmpfd; return (tmpfd); } LocalData* BGPMain::get_local_data() { return 0; } void BGPMain::start_server(const Iptuple&) { } void BGPMain::stop_server(const Iptuple&) { } void BGPMain::stop_all_servers() { } xorp/bgp/bgp_trie.hh0000664000076400007640000001075511540224217014552 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/bgp/bgp_trie.hh,v 1.22 2008/11/08 06:14:36 mjh Exp $ #ifndef __BGP_BGP_TRIE_HH__ #define __BGP_BGP_TRIE_HH__ #include "subnet_route.hh" #include "libxorp/ref_trie.hh" template class Path_Att_Ptr_Cmp { public: bool operator() (const PAListRef a, const PAListRef b) const { return a < b; } }; template class ChainedSubnetRoute : public SubnetRoute { public: ChainedSubnetRoute(const IPNet &net, const PAListRef attributes) : SubnetRoute(net, attributes), _prev(0), _next(0) {} ChainedSubnetRoute(const SubnetRoute& route, const ChainedSubnetRoute* prev); ChainedSubnetRoute(const ChainedSubnetRoute& csr); const ChainedSubnetRoute *prev() const { return _prev; } const ChainedSubnetRoute *next() const { return _next; } bool unchain() const; protected: void set_next(const ChainedSubnetRoute *next) const { _next = next; } void set_prev(const ChainedSubnetRoute *prev) const { _prev = prev; } ChainedSubnetRoute& operator=(const ChainedSubnetRoute& csr); // Not impl. private: //The destructor is private because it's not supposed to be called //directly. Instead, unref() should be used which will only delete //the SubnetRoute when the reference count reaches zero. friend class SubnetRoute; //shut the compiler up. ~ChainedSubnetRoute() {} // it looks odd to have these be mutable and the methods to set // them be const, but that's because the chaining is really // conceptually part of the container, not the payload. It these // aren't mutable we can't modify the chaining and have the payload // be const. mutable const ChainedSubnetRoute *_prev; mutable const ChainedSubnetRoute *_next; }; /** * Template specialization of the RefTrieNode, so that the payload is * not immediately deleted unless the SubnetRoute reference count is * zero. */ template<> inline void RefTrieNode >::delete_payload(const ChainedSubnetRoute* p) { p->unref(); } template<> inline void RefTrieNode >::delete_payload(const ChainedSubnetRoute* p) { p->unref(); } /** * The BgpTrie is an augmented, specialized trie that allows us to * lookup by network address or by path attribute list. We need this * because we can't efficiently extract entries with the same path * attribute list from a regular trie. Each set of nodes with the same * path attribute pointer are linked together into a chain (a circular * doubly-linked list). The BgpTrie holds a pointer to any one of * those nodes. * * XXX mjh: I've changed the pathmap to be a nexthop map. This will still * allow the code to find the next route when the igp distance failed, * but is much faster. The downside is we may not dump routes with * the same PA list together. Hopefully this can be remedied when we * change to a pull-based model. */ template class BgpTrie : public RefTrie > { public: typedef ::IPNet IPNet; typedef ::ChainedSubnetRoute ChainedSubnetRoute; typedef map, const ChainedSubnetRoute*, Path_Att_Ptr_Cmp > PathmapType; typedef RefTrie RouteTrie; typedef typename RouteTrie::iterator iterator; BgpTrie(); ~BgpTrie(); iterator insert(const IPNet& net, const SubnetRoute& route); void erase(const IPNet& net); void delete_all_nodes(); const PathmapType& pathmap() const { return _pathmap; } private: PathmapType _pathmap; }; #endif // __BGP_BGP_TRIE_HH__ xorp/bgp/peer.cc0000664000076400007640000021503711540225521013677 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME // #define SAVE_PACKETS #define CHECK_TIME #include "bgp_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/timer.hh" #include "libxorp/timespent.hh" #include "libcomm/comm_api.h" #include "libproto/packet.hh" #include "peer.hh" #include "bgp.hh" #include "profile_vars.hh" #define DEBUG_BGPPeer uint32_t BGPPeer::_unique_id_allocator = UNIQUE_ID_START; #if 0 inline void trap_callback(const XrlError& error, const char *comment); #endif BGPPeer::BGPPeer(LocalData *ld, BGPPeerData *pd, SocketClient *sock, BGPMain *m) : _unique_id(_unique_id_allocator++), _damping_peer_oscillations(true), _damp_peer_oscillations(m->eventloop(), 10, /* restart threshold */ 5 * 60, /* time period */ 2 * 60 /* idle holdtime */) { debug_msg("BGPPeer constructor called (1)\n"); _localdata = ld; _peerdata = pd; _mainprocess = m; _state = STATEIDLE; _SocketClient = sock; _output_queue_was_busy = false; _handler = NULL; _peername = c_format("Peer-%s", peerdata()->iptuple().str().c_str()); zero_stats(); _current_state = _next_state = _activated = false; } BGPPeer::~BGPPeer() { delete _SocketClient; delete _peerdata; list::iterator i; for (i = _accept_attempt.begin(); i != _accept_attempt.end(); i++) delete (*i); _accept_attempt.clear(); } void BGPPeer::zero_stats() { _in_updates = 0; _out_updates = 0; _in_total_messages = 0; _out_total_messages = 0; _last_error[0] = 0; _last_error[1] = 0; _established_transitions = 0; _mainprocess->eventloop().current_time(_established_time); _mainprocess->eventloop().current_time(_in_update_time); } void BGPPeer::clear_last_error() { _last_error[0] = 0; _last_error[1] = 0; } /* * This call is dispatched by the reader after making sure that * we have a packet with at least a BGP common header and whose * length matches the one in that header. * Our job now is to decode the message and dispatch it to the * state machine. */ bool BGPPeer::get_message(BGPPacket::Status status, const uint8_t *buf, size_t length, SocketClient *socket_client) { XLOG_ASSERT(0 == socket_client || _SocketClient == socket_client); PROFILE(if (main()->profile().enabled(profile_message_in)) main()->profile().log(profile_message_in, c_format("message on %s len %u", str().c_str(), XORP_UINT_CAST(length)))); TIMESPENT(); switch (status) { case BGPPacket::GOOD_MESSAGE: break; case BGPPacket::ILLEGAL_MESSAGE_LENGTH: notify_peer_of_error(MSGHEADERERR, BADMESSLEN, buf + BGPPacket::MARKER_SIZE, 2); // event_tranfatal(); TIMESPENT_CHECK(); return false; case BGPPacket::CONNECTION_CLOSED: // _SocketClient->disconnect(); event_closed(); // XLOG_ASSERT(!is_connected()); TIMESPENT_CHECK(); return false; } #ifdef SAVE_PACKETS // XXX // This is a horrible hack to try and find a problem before the // 1.1 Release. // All packets will be stored in fname, in MRTD format, if they // arrived on an IPv4 peering. // The route_btoa program can be used to print the packets. // TODO after the release: // 1) Move the structures into a header file in libxorp. // 2) Add an XRL to enable the saving of incoming packets on a // per peer basis. // 3) Handle IPv6 peerings. // 4) Save all the values that are required by mrt_update. // 5) Move all this code into a separate method. // 6) Keep the file open. // 7) Don't call gettimeofday directly, get the time from the eventloop. #ifndef HOST_OS_WINDOWS string fname = "/tmp/bgpin.mrtd"; #else string fname = "C:\\BGPIN.MRTD"; #endif FILE *fp = fopen(fname.c_str(), "a"); if(0 == fp) XLOG_FATAL("fopen of %s failed: %s", fname.c_str(), strerror(errno)); struct mrt_header { uint32_t time; uint16_t type; uint16_t subtype; uint32_t length; }; struct mrt_update { uint16_t source_as; uint16_t dest_as; uint16_t ifindex; uint16_t af; uint32_t source_ip; uint32_t dest_ip; }; TimeVal tv; TimerList::system_gettimeofday(&tv); mrt_header mrt_header; mrt_header.time = htonl(tv.sec()); mrt_header.type = htons(16); mrt_header.subtype = htons(1); mrt_header.length = htonl(length + sizeof(mrt_update)); if(fwrite(&mrt_header, sizeof(mrt_header), 1, fp) != 1) XLOG_FATAL("fwrite of %s failed: %s", fname.c_str(), strerror(errno)); mrt_update update; memset(&update, 0, sizeof(update)); update.af = htons(1); /* IPv4 */ string peer_addr = peerdata()->iptuple().get_peer_addr(); try { update.source_as = htons(peerdata()->as().as()); update.source_ip = IPv4(peer_addr.c_str()).addr(); if(fwrite(&update, sizeof(update), 1, fp) != 1) XLOG_FATAL("fwrite of %s failed: %s", fname.c_str(), strerror(errno)); if(fwrite(buf, length, 1, fp) != 1) XLOG_FATAL("fwrite of %s failed: %s", fname.c_str(), strerror(errno)); } catch(InvalidFamily &e) { XLOG_ERROR("%s might not be an IPv4 address %s", peer_addr.c_str(), e.str().c_str()); } fclose(fp); #endif _in_total_messages++; /* ** We should only have a good packet at this point. ** The buffer pointer must be non 0. */ XLOG_ASSERT(0 != buf); const uint8_t* marker = buf + BGPPacket::MARKER_OFFSET; uint8_t type = extract_8(buf + BGPPacket::TYPE_OFFSET); try { /* ** Check the Marker, total waste of time as it never contains ** anything of interest. */ if (0 != memcmp(const_cast(&BGPPacket::Marker[0]), marker, BGPPacket::MARKER_SIZE)) { xorp_throw(CorruptMessage,"Bad Marker", MSGHEADERERR, CONNNOTSYNC); } switch (type) { case MESSAGETYPEOPEN: { debug_msg("OPEN Packet RECEIVED\n"); OpenPacket pac(buf, length); PROFILE(XLOG_TRACE(main()->profile().enabled(trace_message_in), "Peer %s: Receive: %s", peerdata()->iptuple().str().c_str(), cstring(pac))); // All decode errors should throw a CorruptMessage. debug_msg("%s", pac.str().c_str()); // want unified decode call. now need to get peerdata out. _peerdata->dump_peer_data(); event_openmess(pac); TIMESPENT_CHECK(); break; } case MESSAGETYPEKEEPALIVE: { debug_msg("KEEPALIVE Packet RECEIVED %u\n", XORP_UINT_CAST(length)); // Check that the length is correct or throw an exception KeepAlivePacket pac(buf, length); PROFILE(XLOG_TRACE(main()->profile().enabled(trace_message_in), "Peer %s: Receive: %s", peerdata()->iptuple().str().c_str(), cstring(pac))); // debug_msg("%s", pac.str().c_str()); event_keepmess(); TIMESPENT_CHECK(); break; } case MESSAGETYPEUPDATE: { debug_msg("UPDATE Packet RECEIVED\n"); _in_updates++; _mainprocess->eventloop().current_time(_in_update_time); UpdatePacket pac(buf, length, _peerdata, _mainprocess, /*do checks*/true); PROFILE(XLOG_TRACE(main()->profile().enabled(trace_message_in), "Peer %s: Receive: %s", peerdata()->iptuple().str().c_str(), cstring(pac))); // All decode errors should throw a CorruptMessage. debug_msg("%s", pac.str().c_str()); event_recvupdate(pac); TIMESPENT_CHECK(); if (TIMESPENT_OVERLIMIT()) { XLOG_WARNING("Processing packet took longer than %u second %s", XORP_UINT_CAST(TIMESPENT_LIMIT), pac.str().c_str()); } break; } case MESSAGETYPENOTIFICATION: { debug_msg("NOTIFICATION Packet RECEIVED\n"); NotificationPacket pac(buf, length); PROFILE(XLOG_TRACE(main()->profile().enabled(trace_message_in), "Peer %s: Receive: %s", peerdata()->iptuple().str().c_str(), cstring(pac))); // All decode errors should throw a CorruptMessage. debug_msg("%s", pac.str().c_str()); event_recvnotify(pac); TIMESPENT_CHECK(); break; } default: /* ** Send a notification to the peer. This is a bad message type. */ XLOG_ERROR("%s Unknown packet type %d", this->str().c_str(), type); notify_peer_of_error(MSGHEADERERR, BADMESSTYPE, buf + BGPPacket::TYPE_OFFSET, 1); // event_tranfatal(); TIMESPENT_CHECK(); return false; } } catch(CorruptMessage& c) { /* ** This peer has sent us a bad message. Send a notification ** and drop the the peering. */ XLOG_WARNING("%s %s %s", this->str().c_str(), c.where().c_str(), c.why().c_str()); notify_peer_of_error(c.error(), c.subcode(), c.data(), c.len()); // event_tranfatal(); TIMESPENT_CHECK(); return false; } catch (UnusableMessage& um) { // the packet wasn't usable for some reason, but also // wasn't so corrupt we need to send a notification - // this is a "silent" error. XLOG_WARNING("%s %s %s", this->str().c_str(), um.where().c_str(), um.why().c_str()); } TIMESPENT_CHECK(); /* ** If we are still connected and supposed to be reading. */ if (!is_connected() || !still_reading()) { TIMESPENT_CHECK(); return false; } return true; } PeerOutputState BGPPeer::send_message(const BGPPacket& p) { debug_msg("%s", p.str().c_str()); PROFILE(XLOG_TRACE(main()->profile().enabled(trace_message_out), "Peer %s: Send: %s", peerdata()->iptuple().str().c_str(), cstring(p))); uint8_t packet_type = p.type(); if ( packet_type != MESSAGETYPEOPEN && packet_type != MESSAGETYPEUPDATE && packet_type != MESSAGETYPENOTIFICATION && packet_type != MESSAGETYPEKEEPALIVE) { xorp_throw(InvalidPacket, c_format("Unknown packet type %d\n", packet_type)); } _out_total_messages++; if (packet_type == MESSAGETYPEUPDATE) _out_updates++; uint8_t *buf = new uint8_t[BGPPacket::MAXPACKETSIZE]; size_t ccnt = BGPPacket::MAXPACKETSIZE; /* ** This buffer is dynamically allocated and should be freed. */ XLOG_ASSERT(p.encode(buf, ccnt, _peerdata)); debug_msg("Buffer for sent packet is %p\n", buf); /* ** This write is async. So we can't free the data now, ** we will deal with it in the complete routine. */ bool ret = _SocketClient->send_message(buf, ccnt, callback(this,&BGPPeer::send_message_complete)); if (ret == false) delete[] buf; if (ret) { int size = _SocketClient->output_queue_size(); UNUSED(size); debug_msg("Output queue size is %d\n", size); if (_SocketClient->output_queue_busy()) { _output_queue_was_busy = true; return PEER_OUTPUT_BUSY; } else return PEER_OUTPUT_OK; } else return PEER_OUTPUT_FAIL; } void BGPPeer::send_message_complete(SocketClient::Event ev, const uint8_t *buf) { TIMESPENT(); debug_msg("Packet sent, queue size now %d\n", _SocketClient->output_queue_size()); switch (ev) { case SocketClient::DATA: debug_msg("event: data\n"); if (_output_queue_was_busy && (_SocketClient->output_queue_busy() == false)) { debug_msg("Peer: output no longer busy\n"); _output_queue_was_busy = false; if (_handler != NULL) _handler->output_no_longer_busy(); } TIMESPENT_CHECK(); /*drop through to next case*/ case SocketClient::FLUSHING: debug_msg("event: flushing\n"); debug_msg("Freeing Buffer for sent packet: %p\n", buf); delete[] buf; TIMESPENT_CHECK(); break; case SocketClient::ERROR: // The most likely cause of an error is that the peer closed // the connection. debug_msg("event: error\n"); /* Don't free the message here we'll get it in the flush */ // XLOG_ERROR("Writing buffer failed: %s", strerror(errno)); // _SocketClient->disconnect(); event_closed(); // XLOG_ASSERT(!is_connected()); TIMESPENT_CHECK(); } } void BGPPeer::send_notification(const NotificationPacket& p, bool restart, bool automatic) { debug_msg("%s", p.str().c_str()); XLOG_INFO("Sending: %s", cstring(p)); PROFILE(XLOG_TRACE(main()->profile().enabled(trace_message_out), "Peer %s: Send: %s", peerdata()->iptuple().str().c_str(), cstring(p))); /* ** We need to deal with NOTIFICATION differently from other packets - ** NOTIFICATION is the last packet we send on a connection, and because ** we're using async sends, we need to chain the rest of the ** cleanup on the send complete callback. */ /* * First we need to clear the transmit queue - we no longer care * about anything that's in it, and we want to make sure that the * transmit complete callbacks can no longer happen. */ flush_transmit_queue(); /* ** Don't read anything more on this connection. */ stop_reader(); /* * This buffer is dynamically allocated and should be freed. */ size_t ccnt = BGPPacket::MAXPACKETSIZE; uint8_t *buf = new uint8_t[BGPPacket::MAXPACKETSIZE]; XLOG_ASSERT(p.encode(buf, ccnt, _peerdata)); debug_msg("Buffer for sent packet is %p\n", buf); /* ** This write is async. So we can't free the data now, ** we will deal with it in the complete routine. */ bool ret =_SocketClient->send_message(buf, ccnt, callback(this, &BGPPeer::send_notification_complete, restart, automatic)); if (!ret) { delete[] buf; return; } } void BGPPeer::send_notification_complete(SocketClient::Event ev, const uint8_t* buf, bool restart, bool automatic) { TIMESPENT(); switch (ev) { case SocketClient::DATA: debug_msg("Notification sent\n"); XLOG_ASSERT(STATESTOPPED == _state); delete[] buf; set_state(STATEIDLE, restart); break; case SocketClient::FLUSHING: delete[] buf; break; case SocketClient::ERROR: debug_msg("Notification not sent\n"); XLOG_ASSERT(STATESTOPPED == _state); set_state(STATEIDLE, restart, automatic); /* Don't free the message here we'll get it in the flush */ break; } /* there doesn't appear to be anything we can do at this point if status indicates something went wrong */ } /* ** Timer callback */ void BGPPeer::hook_stopped() { XLOG_ASSERT(STATESTOPPED == _state); XLOG_WARNING("%s Unable to send Notification so taking peer to idle", this->str().c_str()); /* ** If the original notification was not an error such as sending a ** CEASE. If we arrived here due to a timeout, something has gone ** wrong so unconditionally set the restart to true. */ set_state(STATEIDLE); } /* * Here begins the BGP state machine. * It is split into multiple functions, each one dealing with a * specific event. */ /* * STATESTOPPED is the state we're in after we've queued a * CEASE notification, but before it's actually been sent. We * ignore packet events, but a further STOP event or error * will cause is to stop waiting for the CEASE to be sent, and * a START event will cause us to transition to idle, and then * process the START event as normal */ /* * start event. No data associated with the event. */ void BGPPeer::event_start() // EVENTBGPSTART { TIMESPENT(); // Compute the type of this peering. const_cast(peerdata())->compute_peer_type(); switch(_state) { case STATESTOPPED: flush_transmit_queue(); // ensure callback can't happen set_state(STATEIDLE, false);// go through STATEIDLE to clear resources // fallthrough now to process the start event case STATEIDLE: // Initalise resources start_connect_retry_timer(); set_state(STATECONNECT); connect_to_peer(callback(this, &BGPPeer::connect_to_peer_complete)); break; // in all other cases, remain in the same state case STATECONNECT: case STATEACTIVE: case STATEOPENSENT: case STATEOPENCONFIRM: case STATEESTABLISHED: break; } } /* * stop event. No data associated with the event. */ void BGPPeer::event_stop(bool restart, bool automatic) // EVENTBGPSTOP { TIMESPENT(); switch(_state) { case STATEIDLE: break; case STATECONNECT: _SocketClient->connect_break(); clear_connect_retry_timer(); /*FALLTHROUGH*/ case STATEACTIVE: set_state(STATEIDLE, restart, automatic); break; case STATEOPENSENT: case STATEOPENCONFIRM: case STATEESTABLISHED: { // Send Notification Message with error code of CEASE. NotificationPacket np(CEASE); send_notification(np, restart, automatic); set_state(STATESTOPPED, restart, automatic); break; } case STATESTOPPED: // a second stop will cause us to give up on sending the CEASE flush_transmit_queue(); // ensure callback can't happen set_state(STATEIDLE, restart, automatic); break; } } /* * EVENTBGPTRANOPEN event. No data associated with the event. * We only call this when a connect() or an accept() succeeded, * and we have a valid socket. * The only states from which we enter here are STATECONNECT and STATEACTIVE */ void BGPPeer::event_open() // EVENTBGPTRANOPEN { TIMESPENT(); switch(_state) { case STATEOPENSENT: case STATEOPENCONFIRM: case STATEESTABLISHED: case STATESTOPPED: case STATEIDLE: XLOG_FATAL("%s can't get EVENTBGPTRANOPEN in state %s", this->str().c_str(), pretty_print_state(_state)); break; case STATECONNECT: case STATEACTIVE: { if (0 != _peerdata->get_delay_open_time()) { start_delay_open_timer(); clear_connect_retry_timer(); return; } OpenPacket open_packet(_peerdata->my_AS_number(), _localdata->get_id(), _peerdata->get_configured_hold_time()); #if 0 ParameterList::const_iterator pi = _peerdata->parameter_sent_list().begin(); while(pi != _peerdata->parameter_sent_list().end()) { if ((*pi)->send()) open_packet.add_parameter(*pi); pi++; } #endif generate_open_message(open_packet); send_message(open_packet); clear_connect_retry_timer(); if ((_state == STATEACTIVE) || (_state == STATECONNECT)) { // Start Holdtimer - four minutes recommended in spec. _peerdata->set_hold_duration(4 * 60); start_hold_timer(); } // Change state to OpenSent set_state(STATEOPENSENT); break; } } } /* * EVENTBGPTRANCLOSED event. No data associated with the event. */ void BGPPeer::event_closed() // EVENTBGPTRANCLOSED { TIMESPENT(); switch(_state) { case STATEIDLE: break; case STATECONNECT: if (_SocketClient->is_connected()) _SocketClient->connect_break(); clear_connect_retry_timer(); set_state(STATEIDLE); break; case STATEACTIVE: set_state(STATEIDLE); break; case STATEOPENSENT: // Close Connection _SocketClient->disconnect(); // Restart ConnectRetry Timer restart_connect_retry_timer(); set_state(STATEACTIVE); break; case STATEOPENCONFIRM: case STATEESTABLISHED: set_state(STATEIDLE); break; case STATESTOPPED: flush_transmit_queue(); // ensure callback can't happen set_state(STATEIDLE); break; } } /* * EVENTBGPCONNOPENFAIL event. No data associated with the event. * We can only be in STATECONNECT */ void BGPPeer::event_openfail() // EVENTBGPCONNOPENFAIL { TIMESPENT(); switch(_state) { case STATEIDLE: case STATEACTIVE: case STATEOPENSENT: case STATEOPENCONFIRM: case STATEESTABLISHED: case STATESTOPPED: XLOG_FATAL("%s can't get EVENTBGPCONNOPENFAIL in state %s", this->str().c_str(), pretty_print_state(_state)); break; case STATECONNECT: if (_peerdata->get_delay_open_time() == 0) { // // If the DelayOpenTimer is not running, we have to go first // through STATEIDLE. // XXX: Instead of checking the configured DelayOpenTime value, // we should check whether the corresponding timer is indeed // not running. However, there is no dedicated DelayOpenTimer // (the _idle_hold timer is reused instead), hence we check // the corresponding DelayOpenTime configured value. // set_state(STATEIDLE, false); } restart_connect_retry_timer(); set_state(STATEACTIVE); // Continue to listen for a connection break; } } /* * EVENTBGPTRANFATALERR event. No data associated with the event. */ void BGPPeer::event_tranfatal() // EVENTBGPTRANFATALERR { TIMESPENT(); switch(_state) { case STATEIDLE: break; case STATECONNECT: case STATEACTIVE: set_state(STATEIDLE); break; case STATEOPENSENT: case STATEOPENCONFIRM: case STATEESTABLISHED: set_state(STATEIDLE); break; case STATESTOPPED: // ensure callback can't happen flush_transmit_queue(); set_state(STATEIDLE); break; } } /* * EVENTCONNTIMEEXP event. No data associated with the event. * This is a timer hook called from a connection_retry_timer expiring * (it is a oneoff timer) */ void BGPPeer::event_connexp() // EVENTCONNTIMEEXP { TIMESPENT(); switch(_state) { case STATEIDLE: case STATESTOPPED: break; case STATECONNECT: restart_connect_retry_timer(); _SocketClient->connect_break(); connect_to_peer(callback(this, &BGPPeer::connect_to_peer_complete)); break; case STATEACTIVE: restart_connect_retry_timer(); set_state(STATECONNECT); connect_to_peer(callback(this, &BGPPeer::connect_to_peer_complete)); break; /* * if these happen, we failed to properly cancel a timer (?) */ case STATEOPENSENT: case STATEOPENCONFIRM: case STATEESTABLISHED: { // Send Notification Message with error code of FSM error. // XXX this needs to be revised. XLOG_WARNING("%s FSM received EVENTCONNTIMEEXP in state %s", this->str().c_str(), pretty_print_state(_state)); NotificationPacket np(FSMERROR); send_notification(np); set_state(STATESTOPPED, true); break; } } } /* * EVENTHOLDTIMEEXP event. No data associated with the event. * Only called by a hold_timer (oneoff) expiring. * If it goes off, the connection is over so we do not start the timer again. */ void BGPPeer::event_holdexp() // EVENTHOLDTIMEEXP { TIMESPENT(); switch(_state) { case STATEIDLE: case STATESTOPPED: break; case STATECONNECT: case STATEACTIVE: set_state(STATEIDLE); break; case STATEOPENSENT: case STATEOPENCONFIRM: case STATEESTABLISHED: { // Send Notification Message with error code of Hold Timer expired. NotificationPacket np(HOLDTIMEEXP); send_notification(np); set_state(STATESTOPPED); break; } } } /* * EVENTKEEPALIVEEXP event. * A keepalive timer expired. */ void BGPPeer::event_keepexp() // EVENTKEEPALIVEEXP { TIMESPENT(); switch(_state) { case STATEIDLE: case STATESTOPPED: case STATECONNECT: case STATEACTIVE: case STATEOPENSENT: XLOG_FATAL("%s FSM received EVENTKEEPALIVEEXP in state %s", this->str().c_str(), pretty_print_state(_state)); break; case STATEOPENCONFIRM: case STATEESTABLISHED: start_keepalive_timer(); KeepAlivePacket kp; send_message(kp); break; } } void BGPPeer::event_delay_open_exp() { TIMESPENT(); switch(_state) { case STATEIDLE: case STATESTOPPED: case STATEOPENSENT: case STATEESTABLISHED: { XLOG_WARNING("%s FSM received EVENTRECOPENMESS in state %s", this->str().c_str(), pretty_print_state(_state)); NotificationPacket np(FSMERROR); send_notification(np); set_state(STATESTOPPED); } break; case STATECONNECT: case STATEACTIVE: case STATEOPENCONFIRM: { OpenPacket open_packet(_peerdata->my_AS_number(), _localdata->get_id(), _peerdata->get_configured_hold_time()); generate_open_message(open_packet); send_message(open_packet); if ((_state == STATEACTIVE) || (_state == STATECONNECT)) { // Start Holdtimer - four minutes recommended in spec. _peerdata->set_hold_duration(4 * 60); start_hold_timer(); } // Change state to OpenSent set_state(STATEOPENSENT); } break; } } void BGPPeer::event_idle_hold_exp() { TIMESPENT(); XLOG_ASSERT(state() == STATEIDLE); event_start(); } /* * EVENTRECOPENMESS event. * We receive an open packet. */ void BGPPeer::event_openmess(const OpenPacket& p) // EVENTRECOPENMESS { TIMESPENT(); switch(_state) { case STATECONNECT: case STATEACTIVE: { // The only way to get here is due to a delayed open. // Kill the delay open timer and send the open packet. clear_delay_open_timer(); OpenPacket open_packet(_peerdata->my_AS_number(), _localdata->get_id(), _peerdata->get_configured_hold_time()); generate_open_message(open_packet); send_message(open_packet); } /* FALLTHROUGH */ case STATEOPENSENT: // Process OPEN MESSAGE try { check_open_packet(&p); // We liked the open packet continue, trying to setup session. KeepAlivePacket kp; send_message(kp); // start timers debug_msg("Starting timers\n"); clear_all_timers(); start_keepalive_timer(); start_hold_timer(); // Save the parameters from the open packet. _peerdata->save_parameters(p.parameter_list()); // Compare _peerdata->open_negotiation(); set_state(STATEOPENCONFIRM); } catch(CorruptMessage& c) { XLOG_WARNING("%s %s", this->str().c_str(), c.why().c_str()); notify_peer_of_error(c.error(), c.subcode(), c.data(), c.len()); } break; case STATEIDLE: case STATEOPENCONFIRM: case STATEESTABLISHED: { // Send Notification - FSM error XLOG_WARNING("%s FSM received EVENTRECOPENMESS in state %s", this->str().c_str(), pretty_print_state(_state)); notify_peer_of_error(FSMERROR); break; } case STATESTOPPED: break; } } /* * EVENTRECKEEPALIVEMESS event. * We received a keepalive message. */ void BGPPeer::event_keepmess() // EVENTRECKEEPALIVEMESS { TIMESPENT(); switch(_state) { case STATEIDLE: case STATECONNECT: case STATEACTIVE: XLOG_FATAL("%s FSM received EVENTRECKEEPALIVEMESS in state %s", this->str().c_str(), pretty_print_state(_state)); break; case STATESTOPPED: break; case STATEOPENSENT: { // Send Notification Message with error code of FSM error. XLOG_WARNING("%s FSM received EVENTRECKEEPALIVEMESS in state %s", this->str().c_str(), pretty_print_state(_state)); NotificationPacket np(FSMERROR); send_notification(np); set_state(STATESTOPPED); break; } case STATEOPENCONFIRM: // this is what we were waiting for. set_state(STATEESTABLISHED); /* FALLTHROUGH */ case STATEESTABLISHED: // this is a legitimate message. restart_hold_timer(); break; } } /* * EVENTRECUPDATEMESS event. * We received an update message. */ void BGPPeer::event_recvupdate(UpdatePacket& p) // EVENTRECUPDATEMESS { TIMESPENT(); switch(_state) { case STATEIDLE: case STATECONNECT: case STATEACTIVE: { XLOG_WARNING("%s FSM received EVENTRECUPDATEMESS in state %s", this->str().c_str(), pretty_print_state(_state)); NotificationPacket np(FSMERROR); send_notification(np); set_state(STATESTOPPED); } break; case STATEOPENSENT: case STATEOPENCONFIRM: { // Send Notification Message with error code of FSM error. XLOG_WARNING("%s FSM received EVENTRECUPDATEMESS in state %s", this->str().c_str(), pretty_print_state(_state)); NotificationPacket np(FSMERROR); send_notification(np); set_state(STATESTOPPED); break; } case STATEESTABLISHED: { restart_hold_timer(); // Check that the prefix limit if set will not be exceeded. ConfigVar &prefix_limit = const_cast(peerdata())->get_prefix_limit(); if (prefix_limit.get_enabled()) { if ((_handler->get_prefix_count() + p.nlri_list().size()) > prefix_limit.get_var()) { NotificationPacket np(CEASE); send_notification(np); set_state(STATESTOPPED); break; } } // process the packet... debug_msg("Process the packet!!!\n"); XLOG_ASSERT(_handler); // XXX - Temporary hack until we get programmable filters. const IPv4 next_hop = peerdata()->get_next_hop_rewrite(); if (!next_hop.is_zero()) { FPAList4Ref l = p.pa_list(); if (l->nexthop_att()) { l->replace_nexthop(next_hop); } } _handler->process_update_packet(&p); break; } case STATESTOPPED: break; } } /* * EVENTRECNOTMESS event. * We received a notify message. Currently we ignore the payload. */ void BGPPeer::event_recvnotify(const NotificationPacket& p) // EVENTRECNOTMESS { TIMESPENT(); XLOG_INFO("%s in state %s received %s", this->str().c_str(), pretty_print_state(_state), p.str().c_str()); _last_error[0] = p.error_code(); _last_error[1] = p.error_subcode(); switch(_state) { case STATEIDLE: XLOG_FATAL("%s FSM received EVENTRECNOTMESS in state %s", this->str().c_str(), pretty_print_state(_state)); break; case STATECONNECT: case STATEACTIVE: case STATEOPENSENT: case STATEOPENCONFIRM: case STATEESTABLISHED: set_state(STATEIDLE); break; case STATESTOPPED: break; } } void BGPPeer::generate_open_message(OpenPacket& open) { uint8_t last_error_code = _last_error[0]; uint8_t last_error_subcode = _last_error[1]; bool ignore_cap_optional_parameters = false; ParameterList::const_iterator pi; if ((last_error_code == OPENMSGERROR) && (last_error_subcode == UNSUPOPTPAR)) { // // XXX: If the last error was Unsupported Optional Parameter, then // don't include the Capabilities Optional Parameter. // ignore_cap_optional_parameters = true; } for (pi = _peerdata->parameter_sent_list().begin(); pi != _peerdata->parameter_sent_list().end(); ++pi) { if (ignore_cap_optional_parameters) { // XXX: ignore the Capabilities Optional Parameters. const ref_ptr& ref_par = *pi; const BGPParameter& par = *ref_par; const BGPCapParameter* cap_par; cap_par = dynamic_cast(&par); if (cap_par != NULL) continue; } open.add_parameter(*pi); } } /* ** A BGP Fatal error has ocurred. Try and send a notification. */ void BGPPeer::notify_peer_of_error(const int error, const int subcode, const uint8_t *data, const size_t len) { if (!NotificationPacket::validate_error_code(error, subcode)) { XLOG_WARNING("%s Attempt to send invalid error code %d subcode %d", this->str().c_str(), error, subcode); // XXX make it fatal ? } /* * An error has occurred. If we still have a connection to the * peer send a notification of the error. */ if (is_connected()) { NotificationPacket np(error, subcode, data, len); send_notification(np); set_state(STATESTOPPED); return; } // The peer is no longer connected make sure we get to idle. event_tranfatal(); } /* * Handle incoming connection events. * * The argment is the incoming socket descriptor, which * must be used or closed by this method. */ void BGPPeer::event_open(const XorpFd sock) { debug_msg("Connection attempt: State %d ", _state); if (_state == STATECONNECT || _state == STATEACTIVE) { debug_msg("accepted\n"); if (_state == STATECONNECT) _SocketClient->connect_break(); _SocketClient->connected(sock); event_open(); } else { debug_msg("rejected\n"); XLOG_INFO("%s rejecting connection: current state %s", this->str().c_str(), pretty_print_state(_state)); comm_sock_close(sock); } } void BGPPeer::check_open_packet(const OpenPacket *p) throw(CorruptMessage) { if (p->Version() != BGPVERSION) { static uint8_t data[2]; embed_16(data, BGPVERSION); xorp_throw(CorruptMessage, c_format("Unsupported BGPVERSION %d", p->Version()), OPENMSGERROR, UNSUPVERNUM, &data[0], sizeof(data)); } if (p->as() != _peerdata->as()) { debug_msg("**** Peer has %s, should have %s ****\n", p->as().str().c_str(), _peerdata->as().str().c_str()); xorp_throw(CorruptMessage, c_format("Wrong AS %s expecting %s", p->as().str().c_str(), _peerdata->as().str().c_str()), OPENMSGERROR, BADASPEER); } // Must be a valid unicast IP host address. if (!p->id().is_unicast() || p->id().is_zero()) { xorp_throw(CorruptMessage, c_format("Not a valid unicast IP host address %s", p->id().str().c_str()), OPENMSGERROR, BADBGPIDENT); } // This has to be a valid IPv4 address. _peerdata->set_id(p->id()); // put received parameters into the peer data. // _peerdata->clone_parameters( p->parameter_list() ); // check the received parameters #if 0 if (_peerdata->unsupported_parameters() == true) xorp_throw(CorruptMessage, c_format("Unsupported parameters"), OPENMSGERROR, UNSUPOPTPAR); #endif /* * Set the holdtime and keepalive times. * * Internally we store the values in milliseconds. The value sent * on the wire is in seconds. First check that we have been * offered a legal value. If the value is legal convert it to * milliseconds. Compare the holdtime in the packet against our * configured value and choose the lowest value. * * The spec suggests using a keepalive time of a third the hold * duration. */ uint16_t hold_secs = p->HoldTime(); if (hold_secs == 1 || hold_secs == 2) xorp_throw(CorruptMessage, c_format("Illegal holdtime value %d secs", hold_secs), OPENMSGERROR, UNACCEPTHOLDTIME); if (_peerdata->get_configured_hold_time() < hold_secs) hold_secs = _peerdata->get_configured_hold_time(); _peerdata->set_hold_duration(hold_secs); _peerdata->set_keepalive_duration(hold_secs / 3); /* ** Any unrecognised optional parameters would already have caused ** any exception to be thrown in the open packet decoder. */ _peerdata->dump_peer_data(); debug_msg("check_open_packet says it's OK with us\n"); } #define REMOVE_UNNEGOTIATED_NLRI inline bool check_multiprotocol_nlri(const UpdatePacket *, const BGPUpdateAttribList& pa, bool neg) { if (!pa.empty() && !neg) { #ifdef REMOVE_UNNEGOTIATED_NLRI // We didn't advertise this capability, so strip this sucker out. const_cast(&pa)->clear(); #else // We didn't advertise this capability, drop peering. return false; #endif } return true; } #if 0 inline bool check_multiprotocol_nlri(const UpdatePacket *p, PathAttribute *pa, bool neg) { if (pa && !neg) { #ifdef REMOVE_UNNEGOTIATED_NLRI // We didn't advertise this capability, so strip this sucker out. PathAttributeList& tmp = const_cast&>(p->pa_list()); tmp.remove_attribute_by_pointer(pa); #else // We didn't advertise this capability, drop peering. return false; #endif } return true; } #endif NotificationPacket * BGPPeer::check_update_packet(const UpdatePacket *p, bool& good_nexthop) { UNUSED(p); UNUSED(good_nexthop); // return codes // 1 - Malformed Attribute List // 2 - Unrecognized Well-known Attribute // 3 - Missing Well-known Attribute // 4 - Attribute Flags Error // 5 - Attribute Length Error // 6 - Invalid ORIGIN Attribute // 8 - Invalid NEXT-HOP Attribute // 9 - Optional Attribute Error // 10 - Invalid Network Field // 11 - Malformed AS_PATH /* ** The maximum message size is 4096 bytes. We don't need an ** explicit check here as the packet construction routines will ** already have generated an error if the maximum size had been ** exceeded. */ #if 0 /* ** Check for multiprotocol parameters that haven't been "negotiated". ** ** There are two questions that seem to ambiguous in the BGP specs. ** ** 1) How do we deal with a multiprotocol attribute that hasn't ** been "negotiated". ** a) Strip out the offending attribute. ** b) Send a notify and drop the peering. ** 2) In order to accept a multiprotol attribute. I.E to consider ** it to have been "negotiated", what is the criteria. ** a) Is it sufficient for this speaker to have announced the ** capability. ** b) Do both speakers in a session need to announce a capability. ** ** For the time being we are going with 1) (a) and 2 (a). */ good_nexthop = true; // BGPPeerData::Direction dir = BGPPeerData::NEGOTIATED; BGPPeerData::Direction dir = BGPPeerData::SENT; bool bad_nlri = false; #define STOP_IPV4_UNICAST #ifdef STOP_IPV4_UNICAST if (!check_multiprotocol_nlri(p, p->nlri_list(), peerdata()-> multiprotocol(SAFI_UNICAST, dir))) bad_nlri = true; if (!check_multiprotocol_nlri(p, p->wr_list(), peerdata()-> multiprotocol(SAFI_UNICAST, dir))) bad_nlri = true; #endif if (!check_multiprotocol_nlri(p, p->mpreach(SAFI_MULTICAST), peerdata()-> multiprotocol(SAFI_MULTICAST, dir))) bad_nlri = true; if (!check_multiprotocol_nlri(p, p->mpunreach(SAFI_MULTICAST), peerdata()-> multiprotocol(SAFI_MULTICAST, dir))) bad_nlri = true; if (!check_multiprotocol_nlri(p, p->mpreach(SAFI_UNICAST), peerdata()-> multiprotocol(SAFI_UNICAST, dir))) bad_nlri = true; if (!check_multiprotocol_nlri(p, p->mpunreach(SAFI_UNICAST), peerdata()-> multiprotocol(SAFI_UNICAST, dir))) bad_nlri = true; if (!check_multiprotocol_nlri(p, p->mpreach(SAFI_MULTICAST), peerdata()-> multiprotocol(SAFI_MULTICAST, dir))) bad_nlri = true; if (!check_multiprotocol_nlri(p, p->mpunreach(SAFI_MULTICAST), peerdata()-> multiprotocol(SAFI_MULTICAST, dir))) bad_nlri = true; #ifndef REMOVE_UNNEGOTIATED_NLRI if (bad_nlri) return new NotificationPacket(UPDATEMSGERR, OPTATTR); #endif #endif // if the path attributes list is empty then no network // layer reachability information should be present. #if 0 if ( p->pa_list().empty() ) { if ( p->nlri_list().empty() == false ) { debug_msg("Empty path attribute list and " "non-empty NLRI list\n"); return new NotificationPacket(UPDATEMSGERR, MALATTRLIST); } } else { // if we have path attributes, check that they are valid. bool local_pref = false; bool route_reflector = false; list l = p->pa_list(); list::const_iterator i; ASPathAttribute* as_path_attr = NULL; OriginAttribute* origin_attr = NULL; NextHopAttribute* next_hop_attr = NULL; set prev_attrs; for (i = l.begin(); i != l.end(); i++) { // check there are no duplicate attributes if (prev_attrs.find((*i)->type()) != prev_attrs.end()) return new NotificationPacket(UPDATEMSGERR, MALATTRLIST); prev_attrs.insert((*i)->type()); switch ((*i)->type()) { case ORIGIN: origin_attr = (OriginAttribute*)(*i); break; case AS_PATH: as_path_attr = (ASPathAttribute*)(*i); break; case NEXT_HOP: next_hop_attr = (NextHopAttribute*)(*i); break; case LOCAL_PREF: local_pref = true; break; case MED: break; case ATOMIC_AGGREGATE: break; case AGGREGATOR: break; case COMMUNITY: break; case ORIGINATOR_ID: /* FALLTHROUGH */ case CLUSTER_LIST: route_reflector = true; break; default: if ((*i)->well_known()) { // unrecognized well_known attribute. uint8_t buf[8192]; size_t wire_size = 8192; (*i)->encode(buf, wire_size, _peerdata); return new NotificationPacket(UPDATEMSGERR, UNRECOGWATTR, buf, wire_size); } } } /* ** If a NLRI attribute is present check for the following ** mandatory fields: ** ORIGIN ** AS_PATH ** NEXT_HOP */ if ( p->nlri_list().empty() == false || p->mpreach(SAFI_MULTICAST) || p->mpreach(SAFI_UNICAST) || p->mpreach(SAFI_MULTICAST)) { // The ORIGIN Path attribute is mandatory if (origin_attr == NULL) { debug_msg("Missing ORIGIN\n"); uint8_t data = ORIGIN; return new NotificationPacket(UPDATEMSGERR, MISSWATTR, &data, 1); } // The AS Path attribute is mandatory if (as_path_attr == NULL) { debug_msg("Missing AS_PATH\n"); uint8_t data = AS_PATH; return new NotificationPacket(UPDATEMSGERR, MISSWATTR, &data, 1); } // The NEXT_HOP attribute is mandatory for IPv4 unicast. // For multiprotocol NLRI its in the path attribute. if (p->nlri_list().empty() == false && next_hop_attr == NULL) { debug_msg("Missing NEXT_HOP\n"); uint8_t data = NEXT_HOP; return new NotificationPacket(UPDATEMSGERR, MISSWATTR, &data, 1); } // In a multiprotocol NLRI message there is always a nexthop, // just check that its not zero. if (p->mpreach(SAFI_MULTICAST) && p->mpreach(SAFI_MULTICAST)->nexthop() == IPv4::ZERO()) { uint8_t data = NEXT_HOP; return new NotificationPacket(UPDATEMSGERR, MISSWATTR, &data, 1); } if (p->mpreach(SAFI_UNICAST) && p->mpreach(SAFI_UNICAST)->nexthop() == IPv6::ZERO()) { uint8_t data = NEXT_HOP; return new NotificationPacket(UPDATEMSGERR, MISSWATTR, &data, 1); } if (p->mpreach(SAFI_MULTICAST) && p->mpreach(SAFI_MULTICAST)->nexthop() == IPv6::ZERO()) { uint8_t data = NEXT_HOP; return new NotificationPacket(UPDATEMSGERR, MISSWATTR, &data, 1); } } // If we got this far and as_path_attr is not set this is a // multiprotocol withdraw. if (as_path_attr != NULL) { if (!ibgp()) { // If this is an EBGP peering, the AS Path MUST NOT be empty if (as_path_attr->as_path().path_length() == 0) return new NotificationPacket(UPDATEMSGERR, MALASPATH); // If this is an EBGP peering, the AS Path MUST start // with the AS number of the peer. AsNum my_asnum(peerdata()->as()); if (as_path_attr->as_path().first_asnum() != my_asnum) return new NotificationPacket(UPDATEMSGERR, MALASPATH); // If this is an EBGP peering and a route reflector // attribute has been received then generate an error. if (route_reflector) return new NotificationPacket(UPDATEMSGERR, MALATTRLIST); } // Receiving confederation path segments when the router // is not configured for confederations is an error. if (!_peerdata->confederation() && as_path_attr->as_path().contains_confed_segments()) return new NotificationPacket(UPDATEMSGERR, MALASPATH); } // If an update message is received that contains a nexthop // that belongs to this router then discard the update, don't // send a notification. if (next_hop_attr != NULL) { if (_mainprocess->interface_address4(next_hop_attr->nexthop())) { XLOG_ERROR("Nexthop in update belongs to this router:\n %s", cstring(*p)); good_nexthop = false; return 0; } } if (p->mpreach(SAFI_MULTICAST)) { if (_mainprocess-> interface_address4(p->mpreach(SAFI_MULTICAST)-> nexthop())) { XLOG_ERROR("Nexthop in update belongs to this router:\n %s", cstring(*p)); good_nexthop = false; return 0; } } if (p->mpreach(SAFI_UNICAST)) { if (_mainprocess-> interface_address6(p->mpreach(SAFI_UNICAST)-> nexthop())) { XLOG_ERROR("Nexthop in update belongs to this router:\n %s", cstring(*p)); good_nexthop = false; return 0; } } if (p->mpreach(SAFI_MULTICAST)) { if (_mainprocess-> interface_address6(p->mpreach(SAFI_MULTICAST)-> nexthop())) { XLOG_ERROR("Nexthop in update belongs to this router:\n %s", cstring(*p)); good_nexthop = false; return 0; } } // XXX // In our implementation BGP is multihop by default. If we // ever switch to single hop (default in other // implementations) then we need to make sure that the // provided nexthop falls into a common subnet. if (ibgp() && !local_pref) XLOG_WARNING("%s Update packet from ibgp with no LOCAL_PREF", this->str().c_str()); if (!ibgp() && local_pref) XLOG_WARNING("%s Update packet from ebgp with LOCAL_PREF", this->str().c_str()); // A semantically incorrect NLRI generates an error message // for the log and is ignored, it does *not* generate a // notification message and drop the peering. The semantic // check is therefore performed in the peer handler where the // update packet is being turned into individual messages // where the offending NLRIs can be dropped. } #endif // XXX Check withdrawn routes are correct. return 0; // all correct. } bool BGPPeer::established() { debug_msg("%s\n", this->str().c_str()); if (_localdata == NULL) { XLOG_ERROR("No _localdata"); return false; } if (_handler == NULL) { // plumb peer into plumbing string peername = "Peer-" + peerdata()->iptuple().str(); debug_msg("Peer is called >%s<\n", peername.c_str()); _handler = new PeerHandler(peername, this, _mainprocess->plumbing_unicast(), _mainprocess->plumbing_multicast()); } else { _handler->peering_came_up(); } // _in_updates = 0; // _out_updates = 0; // _in_total_messages = 0; // _out_total_messages = 0; _established_transitions++; _mainprocess->eventloop().current_time(_established_time); _mainprocess->eventloop().current_time(_in_update_time); return true; } void BGPPeer::connected(XorpFd sock) { if (!_SocketClient) XLOG_FATAL("%s No socket structure", this->str().c_str()); /* ** simultaneous open */ if (_SocketClient->get_sock() == sock) { debug_msg("Simultaneous open\n"); return; } /* ** A connection attempt comes in. We already have a valid socket ** so just close the connection attempt and continue. Don't bother ** the state machine. ** ** XXX ** Need to check the spec to see if the incoming connection should ** override the existing connection. If we do make this change ** then it may be necessary to deal with the STATESTOPPED ** explictly. */ // if (is_connected()) { // debug_msg("Already connected dropping this connection\n"); // ::close(sock); // return; // } // event_open(sock); AcceptSession *connect_attempt = new AcceptSession(*this, sock); _accept_attempt.push_back(connect_attempt); connect_attempt->start(); } void BGPPeer::remove_accept_attempt(AcceptSession *conn) { list::iterator i; for (i = _accept_attempt.begin(); i != _accept_attempt.end(); i++) if (conn == (*i)) { delete (*i); _accept_attempt.erase(i); return; } XLOG_UNREACHABLE(); } SocketClient * BGPPeer::swap_sockets(SocketClient *new_sock) { debug_msg("%s Wiring up the accept socket %s\n", str().c_str(), pretty_print_state(_state)); XLOG_ASSERT(_state == STATEACTIVE || _state == STATECONNECT || _state == STATEOPENSENT || _state == STATEOPENCONFIRM); SocketClient *old_sock = _SocketClient; _SocketClient = new_sock; _SocketClient->set_callback(callback(this, &BGPPeer::get_message)); set_state(STATEACTIVE); event_open(); return old_sock; } XorpFd BGPPeer::get_sock() { if (_SocketClient != NULL) return _SocketClient->get_sock(); else { XorpFd invalidfd; return invalidfd; } } TimeVal BGPPeer::jitter(const TimeVal& t) { if (!_localdata->get_jitter()) return t; // Uniformly distributed between 0.75 and 1.0 return random_uniform(TimeVal(t.get_double() * 0.75), t); } void BGPPeer::clear_all_timers() { clear_connect_retry_timer(); clear_hold_timer(); clear_keepalive_timer(); clear_stopped_timer(); clear_delay_open_timer(); clear_idle_hold_timer(); } void BGPPeer::start_connect_retry_timer() { debug_msg("Start Connect Retry timer after %u s\n", XORP_UINT_CAST(_peerdata->get_retry_duration())); _timer_connect_retry = _mainprocess->eventloop(). new_oneoff_after(jitter(TimeVal(_peerdata->get_retry_duration(), 0)), callback(this, &BGPPeer::event_connexp)); } void BGPPeer::clear_connect_retry_timer() { debug_msg("Unschedule Connect Retry timer\n"); _timer_connect_retry.unschedule(); } void BGPPeer::restart_connect_retry_timer() { debug_msg("restart Connect Retry timer after %u s\n", XORP_UINT_CAST(_peerdata->get_retry_duration())); clear_connect_retry_timer(); start_connect_retry_timer(); } /* * For some reason we need to restart the hold_timer, but we only do so * if we have negotiated the use of keepalives with the other side * (i.e. a non-zero value). */ void BGPPeer::start_hold_timer() { uint32_t duration = _peerdata->get_hold_duration(); if (duration != 0) { /* Add another second to give the remote keepalive a chance */ duration += 1; debug_msg("Holdtimer started %u s\n", XORP_UINT_CAST(duration)); _timer_hold_time = _mainprocess->eventloop(). new_oneoff_after(TimeVal(duration, 0), callback(this, &BGPPeer::event_holdexp)); } } void BGPPeer::clear_hold_timer() { debug_msg("Holdtimer cleared\n"); _timer_hold_time.unschedule(); } void BGPPeer::restart_hold_timer() { debug_msg("Holdtimer restarted\n"); clear_hold_timer(); start_hold_timer(); } void BGPPeer::start_keepalive_timer() { uint32_t duration = _peerdata->get_keepalive_duration(); debug_msg("KeepAlive timer started with duration %u s\n", XORP_UINT_CAST(duration)); if (duration > 0) { TimeVal delay = jitter(TimeVal(duration, 0)); // A keepalive must not be sent more frequently that once a second. delay = delay < TimeVal(1, 0) ? TimeVal(1, 0) : delay; _timer_keep_alive = _mainprocess->eventloop(). new_oneoff_after(delay, callback(this, &BGPPeer::event_keepexp)); } } void BGPPeer::clear_keepalive_timer() { debug_msg("KeepAlive timer cleared\n"); _timer_keep_alive.unschedule(); } void BGPPeer::start_stopped_timer() { /* XXX - Only allow 10 seconds in the stopped state */ const int delay = 10; debug_msg("Stopped timer started with duration %d s\n", delay); _timer_stopped = _mainprocess->eventloop(). new_oneoff_after(TimeVal(delay, 0), callback(this, &BGPPeer::hook_stopped)); } void BGPPeer::clear_stopped_timer() { debug_msg("Stopped timer cleared\n"); _timer_stopped.unschedule(); } void BGPPeer::start_idle_hold_timer() { if (!_damping_peer_oscillations) return; _idle_hold = _mainprocess->eventloop(). new_oneoff_after(TimeVal(_damp_peer_oscillations.idle_holdtime(), 0), callback(this, &BGPPeer::event_idle_hold_exp)); } void BGPPeer::clear_idle_hold_timer() { if (!_damping_peer_oscillations) return; _idle_hold.unschedule(); } bool BGPPeer::running_idle_hold_timer() const { if (!_damping_peer_oscillations) return false; return _idle_hold.scheduled(); } void BGPPeer::start_delay_open_timer() { _idle_hold = _mainprocess->eventloop(). new_oneoff_after(TimeVal(_peerdata->get_delay_open_time(), 0), callback(this, &BGPPeer::event_delay_open_exp)); } void BGPPeer::clear_delay_open_timer() { _delay_open.unschedule(); } bool BGPPeer::release_resources() { TIMESPENT(); debug_msg("BGPPeer::release_resources()\n"); if (_handler != NULL && _handler->peering_is_up()) _handler->peering_went_down(); TIMESPENT_CHECK(); /* ** Only if we are connected call the disconnect. */ if (is_connected()) _SocketClient->disconnect(); // clear the counters. _in_updates = 0; _out_updates = 0; _in_total_messages = 0; _out_total_messages = 0; _mainprocess->eventloop().current_time(_established_time); return true; } #if 0 string BGPPeer::str() const { return c_format("Peer(%d)-%s", get_sock(), peerdata()->iptuple().str().c_str()); } #endif const char * BGPPeer::pretty_print_state(FSMState s) { switch (s) { case STATEIDLE: return "IDLE(1)"; case STATECONNECT: return "CONNECT(2)"; case STATEACTIVE: return "ACTIVE(3)"; case STATEOPENSENT: return "OPENSENT(4)"; case STATEOPENCONFIRM: return "OPENCONFIRM(5)"; case STATEESTABLISHED: return "ESTABLISHED(6)"; case STATESTOPPED: return "STOPPED(7)"; } return "ERROR"; } void BGPPeer::set_state(FSMState s, bool restart, bool automatic) { TIMESPENT(); //XLOG_INFO("Peer %s: Previous state: %s Current state: %s\n", // peerdata()->iptuple().str().c_str(), // pretty_print_state(_state), // pretty_print_state(s)); PROFILE(XLOG_TRACE(main()->profile().enabled(trace_state_change), "Peer %s: Previous state: %s Current state: %s\n", peerdata()->iptuple().str().c_str(), pretty_print_state(_state), pretty_print_state(s))); FSMState previous_state = _state; _state = s; if (previous_state == STATESTOPPED && _state != STATESTOPPED) clear_stopped_timer(); switch (_state) { case STATEIDLE: if (previous_state != STATEIDLE) { // default actions clear_all_timers(); // Release resources - which includes a disconnect release_resources(); if (restart) { if (automatic) { automatic_restart(); start_idle_hold_timer(); } else { /* XXX ** Connection has been blown away try to ** reconnect. */ event_start(); // XXX ouch, recursive call into // state machine } } } break; case STATESTOPPED: if (previous_state != STATESTOPPED) { clear_all_timers(); start_stopped_timer(); } if (previous_state == STATEESTABLISHED) { // We'll have an active peerhandler, so we need to inactivate it. XLOG_ASSERT(0 != _handler); _handler->stop(); } break; case STATECONNECT: case STATEACTIVE: case STATEOPENSENT: case STATEOPENCONFIRM: break; case STATEESTABLISHED: if (STATEESTABLISHED != previous_state) established(); break; } #if 0 /* ** If there is a BGP MIB target running send it traps when a state ** transition takes place. */ BGPMain *m = _mainprocess; if (m->do_snmp_trap()) { XrlBgpMibTrapsV0p1Client snmp(m->get_router()); string last_error = NotificationPacket::pretty_print_error_code( _last_error[0], _last_error[1]); if (STATEESTABLISHED == _state && STATEESTABLISHED != previous_state){ snmp.send_send_bgp_established_trap(m->bgp_mib_name().c_str(), last_error,_state, callback(trap_callback, "established")); } else if (_state < previous_state) { snmp.send_send_bgp_backward_transition_trap( m->bgp_mib_name().c_str(), last_error, _state, callback(trap_callback, "backward")); } } #endif } #if 0 inline void trap_callback(const XrlError& error, const char *comment) { debug_msg("trap_callback %s %s\n", comment, error.str().c_str()); if (XrlError::OKAY() != error) { XLOG_WARNING("trap_callback: %s %s", comment, error.str().c_str()); } } #endif PeerOutputState BGPPeer::send_update_message(const UpdatePacket& p) { PeerOutputState queue_state; debug_msg("send_update_message called\n"); assert(STATEESTABLISHED == _state); queue_state = send_message(p); debug_msg("send_update_message: queue is state %d\n", queue_state); return queue_state; } bool BGPPeer::send_netreachability(const BGPUpdateAttrib &n) { debug_msg("send_netreachability called\n"); UpdatePacket bup; bup.add_nlri(n); return send_message(bup); } uint32_t BGPPeer::get_established_time() const { TimeVal now; _mainprocess->eventloop().current_time(now); return now.sec() - _established_time.sec(); } void BGPPeer::get_msg_stats(uint32_t& in_updates, uint32_t& out_updates, uint32_t& in_msgs, uint32_t& out_msgs, uint16_t& last_error, uint32_t& in_update_elapsed) const { in_updates = _in_updates; out_updates = _out_updates; in_msgs = _in_total_messages; out_msgs = _out_total_messages; memcpy(&last_error, _last_error, 2); TimeVal now; _mainprocess->eventloop().current_time(now); in_update_elapsed = now.sec() - _in_update_time.sec(); } bool BGPPeer::remote_ip_ge_than(const BGPPeer& peer) { IPvX this_remote_ip(peerdata()->iptuple().get_peer_addr().c_str()); IPvX other_remote_ip(peer.peerdata()->iptuple().get_peer_addr().c_str()); return (this_remote_ip >= other_remote_ip); } void BGPPeer::automatic_restart() { if (!_damping_peer_oscillations) return; _damp_peer_oscillations.restart(); } // Second FSM. AcceptSession::AcceptSession(BGPPeer& peer, XorpFd sock) : _peer(peer), _sock(sock), _accept_messages(true) { const BGPPeerData *pd = peer.peerdata(); bool md5sig = !pd->get_md5_password().empty(); _socket_client = new SocketClient(pd->iptuple(), peer.main()->eventloop(), md5sig); _socket_client->set_callback(callback(this, &AcceptSession::get_message_accept)); } AcceptSession::~AcceptSession() { debug_msg("Socket %p\n", _socket_client); XLOG_ASSERT(BAD_XORPFD == _sock); XLOG_ASSERT(!is_connected()); XLOG_ASSERT(!_open_wait.scheduled()); delete _socket_client; _socket_client = 0; } void AcceptSession::start() { debug_msg("%s %s\n", str().c_str(), _peer.pretty_print_state(state())); uint32_t hold_duration; // Note this is the state of the main FSM. switch(state()) { case STATEIDLE: // Drop this connection, we are in idle. debug_msg("rejected\n"); XLOG_INFO("%s rejecting connection: current state %s %s", str().c_str(), _peer.pretty_print_state(state()), running_idle_hold_timer() ? "holdtimer running" : ""); comm_sock_close(_sock); _sock = BAD_XORPFD; remove(); break; case STATEOPENSENT: // Note we are not going to send anything. // Wait for an open message from the peer so that the ID's can // be compared to determine which connection to accept. // Start a timer in case the open never arrives. hold_duration = _peer.peerdata()->get_hold_duration(); if (0 == hold_duration) { hold_duration = 4 * 60; XLOG_WARNING("Connection collision hold duration is 0 " "setting to %d seconds", hold_duration); } _open_wait = main()-> eventloop(). new_oneoff_after(TimeVal(hold_duration, 0), callback(this, &AcceptSession::no_open_received)); _socket_client->connected(_sock); _sock = BAD_XORPFD; break; case STATEOPENCONFIRM: // Check the IDs to see what should be done. collision(); break; case STATEESTABLISHED: // Send a cease and shutdown this attempt. cease(); break; case STATECONNECT: case STATEACTIVE: case STATESTOPPED: // Accept this connection attempt. _socket_client->set_callback(callback(_peer, &BGPPeer::get_message)); _peer.event_open(_sock); _sock = BAD_XORPFD; remove(); break; } } void AcceptSession::no_open_received() { debug_msg("\n"); cease(); } void AcceptSession::remove() { _peer.remove_accept_attempt(this); } void AcceptSession::send_notification_accept(const NotificationPacket& np) { // Don't process any more incoming messages ignore_message(); if (BAD_XORPFD != _sock) { _socket_client->connected(_sock); _sock = BAD_XORPFD; } _socket_client->stop_reader(); // This buffer is dynamically allocated and needs to be freed. size_t ccnt = BGPPacket::MAXPACKETSIZE; uint8_t *buf = new uint8_t[BGPPacket::MAXPACKETSIZE]; XLOG_ASSERT(np.encode(buf, ccnt, _peer.peerdata())); debug_msg("Buffer for sent packet is %p\n", buf); XLOG_INFO("Sending: %s", cstring(np)); PROFILE(XLOG_TRACE(main()->profile().enabled(trace_message_out), "Peer %s: Send: %s", peerdata()->iptuple().str().c_str(), cstring(np))); // Free the buffer in the completion routine. bool ret =_socket_client->send_message(buf, ccnt, callback(this, &AcceptSession::send_notification_cb)); if (!ret) { delete[] buf; remove(); return; } } void AcceptSession::send_notification_cb(SocketClient::Event ev, const uint8_t* buf) { switch (ev) { case SocketClient::DATA: debug_msg("Notification sent\n"); delete[] buf; break; case SocketClient::FLUSHING: delete[] buf; break; case SocketClient::ERROR: debug_msg("Notification not sent\n"); /* Don't free the message here we'll get it in the flush */ break; } _socket_client->disconnect(); remove(); } void AcceptSession::cease() { NotificationPacket np(CEASE); send_notification_accept(np); } void AcceptSession::collision() { IPv4 id = _peer.id(); IPv4 peerid = _peer.peerdata()->id(); debug_msg("%s router id %s peerid %s\n", str().c_str(), cstring(id), cstring(peerid)); // This is a comparison of the IDs in the main state machine not // this one. The BGP that has the largest ID gets to make the // connection. // Dump the other connection and take this one. if (peerid > id) { debug_msg("%s Accepting connect attempt\n", str().c_str()); swap_sockets(); } else { debug_msg("%s Dropping connect attempt\n", str().c_str()); } cease(); } void AcceptSession::event_openmess_accept(const OpenPacket& p) { debug_msg("%s %s\n", str().c_str(), cstring(p)); // While waiting for the open message the state of the main FSM // may have changed. bool compare = false; switch(state()) { case STATEIDLE: // The peer was taken to idle while we waited for the open message. debug_msg("rejected\n"); XLOG_INFO("%s rejecting connection: current state %s", str().c_str(), _peer.pretty_print_state(state())); _socket_client->disconnect(); remove(); break; case STATEACTIVE: // The main FSM is now waiting for a connection so provide // this one, it doesn't have a connection so there is no need // to send a cease(). swap_sockets(p); remove(); break; case STATECONNECT: case STATEOPENSENT: // We have an open packet on this session that should sort // things out. compare = true; break; case STATEOPENCONFIRM: // If the IDs used in both sessions are the same we don't need // to check the other open packet. compare = true; break; case STATEESTABLISHED: // Send a cease and shutdown this attempt. cease(); break; case STATESTOPPED: // The socket in the main FSM is now shut. So swap in this socket. swap_sockets(p); XLOG_ASSERT(BAD_XORPFD == _socket_client->get_sock()); remove();// remove() not cease() break; } if (!compare) return; IPv4 id = _peer.id(); IPv4 peerid = p.id(); if (peerid > id) { debug_msg("%s Accepting connect attempt\n", str().c_str()); swap_sockets(p); } else { debug_msg("%s Dropping connect attempt\n", str().c_str()); } XLOG_ASSERT(BAD_XORPFD == _sock); cease(); } void AcceptSession::swap_sockets() { debug_msg("%s\n", str().c_str()); if (BAD_XORPFD != _sock) { _socket_client->connected(_sock); _sock = BAD_XORPFD; } _socket_client = _peer.swap_sockets(_socket_client); _socket_client-> set_callback(callback(this, &AcceptSession::get_message_accept)); } void AcceptSession::swap_sockets(const OpenPacket& p) { swap_sockets(); // Either call open message in the main FSM or go through // get_message. Calling get_message ensures all the counters // and profiling information is correct. // If profiling is enabled the open message will be seen twice. #ifdef NO_STATS _peer.event_openmess(p); #else // This buffer is dynamically allocated and needs to be freed. size_t ccnt = BGPPacket::MAXPACKETSIZE; uint8_t *buf = new uint8_t[BGPPacket::MAXPACKETSIZE]; XLOG_ASSERT(p.encode(buf, ccnt, NULL)); _peer.get_message(BGPPacket::GOOD_MESSAGE, buf, ccnt, 0); delete[] buf; #endif } void AcceptSession::notify_peer_of_error_accept(const int error, const int subcode, const uint8_t *data, const size_t len) { if (!NotificationPacket::validate_error_code(error, subcode)) { XLOG_WARNING("%s Attempt to send invalid error code %d subcode %d", this->str().c_str(), error, subcode); } if (is_connected()) { NotificationPacket np(error, subcode, data, len); send_notification_accept(np); return; } } void AcceptSession::event_tranfatal_accept() { } void AcceptSession::event_closed_accept() { // The socket is closed so out of here. _socket_client->disconnect(); remove(); } void AcceptSession::event_keepmess_accept() { // A keepalive is unexpected send a cease and out of here. cease(); } void AcceptSession::event_recvupdate_accept(const UpdatePacket& /*p*/) { // An update is unexpected send a cease and out of here. cease(); } void AcceptSession::event_recvnotify_accept(const NotificationPacket& /*p*/) { // Can't send a notify in response to a notify just shut the // connection and out of here. _socket_client->disconnect(); remove(); } bool AcceptSession::get_message_accept(BGPPacket::Status status, const uint8_t *buf, size_t length, SocketClient *socket_client) { XLOG_ASSERT(socket_client == _socket_client); // An open is expected but any packet will break us out of this state. _open_wait.clear(); if (!accept_message()) { debug_msg("Disregarding messages %#x %u", status, XORP_UINT_CAST(length)); return true; } TIMESPENT(); switch (status) { case BGPPacket::GOOD_MESSAGE: break; case BGPPacket::ILLEGAL_MESSAGE_LENGTH: notify_peer_of_error_accept(MSGHEADERERR, BADMESSLEN, buf + BGPPacket::MARKER_SIZE, 2); // event_tranfatal_accept(); TIMESPENT_CHECK(); debug_msg("Returning false\n"); return false; case BGPPacket::CONNECTION_CLOSED: event_closed_accept(); TIMESPENT_CHECK(); debug_msg("Returning false\n"); return false; } /* ** We should only have a good packet at this point. ** The buffer pointer must be non 0. */ XLOG_ASSERT(0 != buf); const uint8_t* marker = buf + BGPPacket::MARKER_OFFSET; uint8_t type = extract_8(buf + BGPPacket::TYPE_OFFSET); try { /* ** Check the Marker, total waste of time as it never contains ** anything of interest. */ if (0 != memcmp(const_cast(&BGPPacket::Marker[0]), marker, BGPPacket::MARKER_SIZE)) { xorp_throw(CorruptMessage,"Bad Marker", MSGHEADERERR, CONNNOTSYNC); } switch (type) { case MESSAGETYPEOPEN: { debug_msg("OPEN Packet RECEIVED\n"); OpenPacket pac(buf, length); PROFILE(XLOG_TRACE(main()->profile().enabled(trace_message_in), "Peer %s: Receive: %s", peerdata()->iptuple().str().c_str(), cstring(pac))); // All decode errors should throw a CorruptMessage. debug_msg("%s", pac.str().c_str()); // want unified decode call. now need to get peerdata out. // _peerdata->dump_peer_data(); event_openmess_accept(pac); TIMESPENT_CHECK(); break; } case MESSAGETYPEKEEPALIVE: { debug_msg("KEEPALIVE Packet RECEIVED %u\n", XORP_UINT_CAST(length)); // Check that the length is correct or throw an exception KeepAlivePacket pac(buf, length); PROFILE(XLOG_TRACE(main()->profile().enabled(trace_message_in), "Peer %s: Receive: %s", peerdata()->iptuple().str().c_str(), cstring(pac))); // debug_msg(pac.str().c_str()); event_keepmess_accept(); TIMESPENT_CHECK(); break; } case MESSAGETYPEUPDATE: { debug_msg("UPDATE Packet RECEIVED\n"); // _in_updates++; // main()->eventloop().current_time(_in_update_time); UpdatePacket pac(buf, length, _peer.peerdata(), _peer.main(), /*do checks*/true); PROFILE(XLOG_TRACE(main()->profile().enabled(trace_message_in), "Peer %s: Receive: %s", peerdata()->iptuple().str().c_str(), cstring(pac))); // All decode errors should throw a CorruptMessage. debug_msg("%s", pac.str().c_str()); event_recvupdate_accept(pac); TIMESPENT_CHECK(); if (TIMESPENT_OVERLIMIT()) { XLOG_WARNING("Processing packet took longer than %u second %s", XORP_UINT_CAST(TIMESPENT_LIMIT), pac.str().c_str()); } break; } case MESSAGETYPENOTIFICATION: { debug_msg("NOTIFICATION Packet RECEIVED\n"); NotificationPacket pac(buf, length); PROFILE(XLOG_TRACE(main()->profile().enabled(trace_message_in), "Peer %s: Receive: %s", peerdata()->iptuple().str().c_str(), cstring(pac))); // All decode errors should throw a CorruptMessage. debug_msg("%s", pac.str().c_str()); event_recvnotify_accept(pac); TIMESPENT_CHECK(); break; } default: /* ** Send a notification to the peer. This is a bad message type. */ XLOG_ERROR("%s Unknown packet type %d", this->str().c_str(), type); notify_peer_of_error_accept(MSGHEADERERR, BADMESSTYPE, buf + BGPPacket::TYPE_OFFSET, 1); // event_tranfatal_accept(); TIMESPENT_CHECK(); debug_msg("Returning false\n"); return false; } } catch(CorruptMessage& c) { /* ** This peer has sent us a bad message. Send a notification ** and drop the the peering. */ XLOG_WARNING("%s %s %s", this->str().c_str(), c.where().c_str(), c.why().c_str()); notify_peer_of_error_accept(c.error(), c.subcode(), c.data(), c.len()); // event_tranfatal_accept(); TIMESPENT_CHECK(); debug_msg("Returning false\n"); return false; } catch (UnusableMessage& um) { // the packet wasn't usable for some reason, but also // wasn't so corrupt we need to send a notification - // this is a "silent" error. XLOG_WARNING("%s %s %s", this->str().c_str(), um.where().c_str(), um.why().c_str()); } TIMESPENT_CHECK(); /* ** If we are still connected and supposed to be reading. */ if (!socket_client->is_connected() || !socket_client->still_reading()) { TIMESPENT_CHECK(); debug_msg("Returning false %s %s socket %p\n", is_connected() ? "connected" : "not connected", still_reading() ? "reading" : "not reading", _socket_client); return false; } return true; } DampPeerOscillations::DampPeerOscillations(EventLoop& eventloop, uint32_t restart_threshold, uint32_t time_period, uint32_t idle_holdtime) : _eventloop(eventloop), _restart_threshold(restart_threshold), _time_period(time_period), _idle_holdtime(idle_holdtime), _restart_counter(0) { } void DampPeerOscillations::restart() { if (0 == _restart_counter++) { _zero_restart = _eventloop. new_oneoff_after(TimeVal(_time_period, 0), callback(this, &DampPeerOscillations:: zero_restart_count)); } } void DampPeerOscillations::reset() { _zero_restart.unschedule(); zero_restart_count(); } void DampPeerOscillations::zero_restart_count() { _restart_counter = 0; } uint32_t DampPeerOscillations::idle_holdtime() const { return _restart_counter < _restart_threshold ? 0 : _idle_holdtime; } xorp/bgp/route_table_dump.cc0000664000076400007640000004123011421137511016265 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net //#define DEBUG_LOGGING //#define DEBUG_PRINT_FUNCTION_NAME #include "bgp_module.h" #include "libxorp/xlog.h" #include "libxorp/callback.hh" #include "route_table_dump.hh" #include "route_table_fanout.hh" //#define DEBUG_CODEPATH #ifdef DEBUG_CODEPATH #define cp(x) debug_msg("DumpCodePath: %2d\n", x) #else #define cp(x) {} #endif template DumpTable::DumpTable(string table_name, const PeerHandler *peer, const list *>& peer_list, BGPRouteTable *parent_table, Safi safi) : BGPRouteTable("DumpTable-" + table_name, safi), _dump_iter(peer, peer_list) { debug_msg("%s peer: %p\n", this->tablename().c_str(), peer); //printf("New dumbtable %s peer: %p\n", this->tablename().c_str(), peer); cp(1); this->_parent = parent_table; _peer = peer; this->_next_table = 0; _output_busy = false; _waiting_for_deletion_completion = false; _completed = false; _triggered_event = false; #ifdef AUDIT_ENABLE _audit_entries = 0; _first_audit = 0; _last_audit = 0; #endif } template int DumpTable::add_route(InternalMessage &rtmsg, BGPRouteTable *caller) { debug_msg("\n %s\n caller: %s\n" "rtmsg: %p route: %p\n%s\n", this->tablename().c_str(), caller->tablename().c_str(), &rtmsg, rtmsg.route(), rtmsg.str().c_str()); XLOG_ASSERT(caller == this->_parent); XLOG_ASSERT(this->_next_table != NULL); cp(1); if (_dump_iter.route_change_is_valid(rtmsg.origin_peer(), rtmsg.net(), rtmsg.genid(), RTQUEUE_OP_ADD)) { cp(2); #ifdef AUDIT_ENABLE add_audit(c_format("%s::add_route peer:%p/%u net:%s valid", this->tablename().c_str(), rtmsg.origin_peer(), XORP_UINT_CAST(rtmsg.genid()), rtmsg.net().str().c_str())); #endif return this->_next_table->add_route(rtmsg, static_cast*>(this)); } else { cp(3); #ifdef AUDIT_ENABLE add_audit(c_format("%s::add_route peer:%p/%u net:%s not valid", this->tablename().c_str(), rtmsg.origin_peer(), XORP_UINT_CAST(rtmsg.genid()), rtmsg.net().str().c_str())); #endif return ADD_UNUSED; } } /* * We can see a replace_route during a route dump because routes we've * already dumped may then change before the dump has completed */ template int DumpTable::replace_route(InternalMessage &old_rtmsg, InternalMessage &new_rtmsg, BGPRouteTable *caller) { debug_msg("%s::replace_route %p -> %p\n", this->tablename().c_str(), &old_rtmsg, &new_rtmsg); XLOG_ASSERT(caller == this->_parent); XLOG_ASSERT(this->_next_table != NULL); XLOG_ASSERT(old_rtmsg.net() == new_rtmsg.net()); cp(4); bool old_is_valid = _dump_iter.route_change_is_valid(old_rtmsg.origin_peer(), old_rtmsg.net(), old_rtmsg.genid(), RTQUEUE_OP_REPLACE_OLD); bool new_is_valid = _dump_iter.route_change_is_valid(new_rtmsg.origin_peer(), new_rtmsg.net(), new_rtmsg.genid(), RTQUEUE_OP_REPLACE_NEW); #ifdef AUDIT_ENABLE add_audit(c_format("%s::replace_route old_peer:%p/%u new_peer:%p/%u net:%s ov:%d nv:%d", this->tablename().c_str(), old_rtmsg.origin_peer(), XORP_UINT_CAST(old_rtmsg.genid()), new_rtmsg.origin_peer(), XORP_UINT_CAST(new_rtmsg.genid()), new_rtmsg.net().str().c_str(), old_is_valid, new_is_valid)); #endif /* I'm not sure that all these combinations can happen */ if (old_is_valid && new_is_valid) { cp(5); return this->_next_table->replace_route(old_rtmsg, new_rtmsg, (BGPRouteTable*)this); } else if (new_is_valid) { cp(6); return this->_next_table->add_route(new_rtmsg, (BGPRouteTable*)this); } else if (old_is_valid) { cp(7); return this->_next_table->delete_route(new_rtmsg, (BGPRouteTable*)this); } else { cp(8); return ADD_UNUSED; } } template int DumpTable::route_dump(InternalMessage &rtmsg, BGPRouteTable *caller, const PeerHandler* dump_peer) { cp(9); XLOG_ASSERT(caller == this->_parent); XLOG_ASSERT(this->_next_table != NULL); XLOG_ASSERT(dump_peer == _peer); XLOG_ASSERT(!_completed); debug_msg("Route_dump: %s %s\n", this->tablename().c_str(), rtmsg.net().str().c_str()); #ifdef AUDIT_ENABLE add_audit(c_format("%s:route_dump peer:%p/%u net:%s valid", this->tablename().c_str(), rtmsg.origin_peer(), XORP_UINT_CAST(rtmsg.genid()), rtmsg.net().str().c_str())); #endif /* turn the route_dump into a route_add */ _dump_iter.route_dump(rtmsg); _dumped++; int result = this->_next_table->add_route(rtmsg, (BGPRouteTable*)this); this->_next_table->push((BGPRouteTable*)this); return result; } /* * We can see a delete_route during a route dump because routes we've * already dumped may then be deleted before the dump has completed */ template int DumpTable::delete_route(InternalMessage &rtmsg, BGPRouteTable *caller) { debug_msg("%s::delete_route %p\n", this->tablename().c_str(), &rtmsg); XLOG_ASSERT(caller == this->_parent); XLOG_ASSERT(this->_next_table != NULL); if (_dump_iter.route_change_is_valid(rtmsg.origin_peer(), rtmsg.net(), rtmsg.genid(), RTQUEUE_OP_DELETE)) { cp(10); #ifdef AUDIT_ENABLE add_audit(c_format("%s::delete_route peer:%p/%u net:%s valid", this->tablename().c_str(), rtmsg.origin_peer(), XORP_UINT_CAST(rtmsg.genid()), rtmsg.net().str().c_str())); #endif return this->_next_table->delete_route(rtmsg, (BGPRouteTable*)this); } else { cp(11); #ifdef AUDIT_ENABLE add_audit(c_format("%s::delete_route peer:%p/%u net:%s not valid", this->tablename().c_str(), rtmsg.origin_peer(), XORP_UINT_CAST(rtmsg.genid()), rtmsg.net().str().c_str())); #endif return 0; } } template int DumpTable::push(BGPRouteTable *caller) { XLOG_ASSERT(caller == this->_parent); cp(12); return this->_next_table->push((BGPRouteTable*)this); } template const SubnetRoute* DumpTable::lookup_route(const IPNet &net, uint32_t& genid, FPAListRef& pa_list) const { cp(13); return this->_parent->lookup_route(net, genid, pa_list); } template void DumpTable::route_used(const SubnetRoute* rt, bool in_use) { cp(14); this->_parent->route_used(rt, in_use); } template string DumpTable::str() const { string s = "DumpTable" + this->tablename(); return s; } template void DumpTable::initiate_background_dump() { XLOG_ASSERT(this->_next_table != NULL); XLOG_ASSERT(!_completed); cp(15); _dumped = 0; _dump_active = true; //delay the actual start of the dump to allow whoever is calling //us to get their act in order before we wake up the downstream //branch _dump_timer = eventloop(). new_oneoff_after_ms(0 /*call back immediately, but after network events or expired timers */, callback(this, &DumpTable::wakeup_downstream)); } template void DumpTable::wakeup_downstream() { //wakeup the folks downstream so they start requesting data from us this->_next_table->wakeup(); } template void DumpTable::wakeup() { //wakeup the folks downstream so they start requesting data from us // this version is called from upstream on a triggered event _triggered_event = true; this->_next_table->wakeup(); _triggered_event = false; } template void DumpTable::suspend_dump() { debug_msg("%s\n", this->tablename().c_str()); if (_dump_active == false) return; cp(16); _dump_active = false; _dump_timer.unschedule(); // suspend is being called because the fanout table is unplumbing us. this->_next_table->set_parent(NULL); // ensure we can't continue to operate this->_next_table = (BGPRouteTable*)0xd0d0; this->_parent = (BGPRouteTable*)0xd0d0; delete this; } template bool DumpTable::do_next_route_dump() { XLOG_ASSERT(!_completed); XLOG_ASSERT(!_triggered_event); // if we get here, the output is not busy and there's no queue of // changes upstream of us, so it's time to do more of the route // dump... debug_msg("dumped %d routes\n", _dumped); if (_dump_iter.is_valid() == false) { if (_dump_iter.waiting_for_deletion_completion()) { // go into final wait state _waiting_for_deletion_completion = true; } else { completed(); } return false; } // debug_msg("dump route with net %p\n", _dump_iter.net().str().c_str()); if (this->_parent->dump_next_route(_dump_iter) == false) { if (_dump_iter.next_peer() == false) { if (_dump_iter.waiting_for_deletion_completion()) { // go into final wait state _waiting_for_deletion_completion = true; } else { completed(); } return false; } } return true; } template bool DumpTable::get_next_message(BGPRouteTable *next_table) { XLOG_ASSERT(next_table == this->_next_table); bool messages_queued; if (_completed) { //we completed a while ago, but haven't removed ourselves yet. //typically this is because we're waiting for the queue //upstream to drain before it's safe to do so. messages_queued = this->_parent-> get_next_message(static_cast*>(this)); if (messages_queued == false) { //the queue upstream has now drained //we can finally delete ourselves schedule_unplumb_self(); } return messages_queued; } if (_waiting_for_deletion_completion) { //just stay out of the way return this->_parent->get_next_message(this); } else { // retrieve all queued routes from upstream before we dump the // next route messages_queued = this->_parent-> get_next_message(static_cast*>(this)); if (messages_queued) { return true; } else if (!_triggered_event) { // Only dump the next chunk if we have been woken up as a // background task. Otherwise it's not safe to dump a // chunk, as we may be in the middle of processing an // event elsewhere, and we need to be careful never to do // a dump between events that some other route table // assumes are atomic return do_next_route_dump(); } else { return false; } } } template void DumpTable::peering_is_down(const PeerHandler *peer, uint32_t genid) { debug_msg("\n %s\n peer: %p genid: %u\n", this->tablename().c_str(), peer, XORP_UINT_CAST(genid)); if (peer != _peer) { //we only care about peers other than our own one - we won't //received routes for our own peer _dump_iter.peering_is_down(peer, genid); } } template void DumpTable::peering_went_down(const PeerHandler *peer, uint32_t genid, BGPRouteTable *caller) { debug_msg("\n %s\n caller: %s\n peer: %p genid: %u\n", this->tablename().c_str(), caller->tablename().c_str(), peer, XORP_UINT_CAST(genid)); cp(37); XLOG_ASSERT(this->_parent == caller); XLOG_ASSERT(this->_next_table != NULL); if (peer != _peer) { _dump_iter.peering_went_down(peer, genid); } else { //do nothing, we'll soon be unplumbed. } this->_next_table->peering_went_down(peer, genid, this); } template void DumpTable::peering_down_complete(const PeerHandler *peer, uint32_t genid, BGPRouteTable *caller) { debug_msg("\n %s\n caller: %s\n peer: %p genid: %u\n", this->tablename().c_str(), caller->tablename().c_str(), peer, XORP_UINT_CAST(genid)); cp(38); XLOG_ASSERT(this->_parent == caller); XLOG_ASSERT(this->_next_table != NULL); this->_next_table->peering_down_complete(peer, genid, this); if (_completed) { //we're already done, just waiting for the queue upstream to drain return; } if (peer != _peer) { _dump_iter.peering_down_complete(peer, genid); } else { //do nothing, we'll soon be unplumbed. } // If we were waiting for this signal, we can now unplumb ourselves. if (_waiting_for_deletion_completion && (_dump_iter.waiting_for_deletion_completion() == false)) { cp(39); completed(); } else { cp(40); } } template void DumpTable::peering_came_up(const PeerHandler *peer, uint32_t genid, BGPRouteTable *caller) { debug_msg("\n %s\n caller: %s\n peer: %p genid: %u\n", this->tablename().c_str(), caller->tablename().c_str(), peer, XORP_UINT_CAST(genid)); XLOG_ASSERT(this->_parent == caller); XLOG_ASSERT(this->_next_table != NULL); _dump_iter.peering_came_up(peer, genid); this->_next_table->peering_came_up(peer, genid, this); } template void DumpTable::completed() { debug_msg("Completed: %s\n", this->tablename().c_str()); XLOG_ASSERT(!_completed); _completed = true; // When we started dumping it looks we asserted flow control // on the queue to us in the fanout. // We need to drain the queue for everything to be consistent. // Otherwise there can be stuff in the queue that came from // background deletion that requires our state to be consistent. // Thus we *still* can't go away if we can't drain that queue. bool messages_queued = true; //assume there are messages queued while(!_output_busy) { messages_queued = this->_parent->get_next_message(this); if (messages_queued == false) break; debug_msg("DumpTable::completed processed queued message\n"); }; if (messages_queued) { //we need to stay around. return; } schedule_unplumb_self(); } template void DumpTable::schedule_unplumb_self() { /* it's too scary to unplumb directly here, because this method can be called in the middle of passing messages up and downstream. Having tables just remove themselves in the middle of message processing can cause local state and iterators in other tables to become invalid, with unintended side effects. So we schedule the unplumbing once the current message processing has finished, and do it when nothing else is going on */ _dump_timer = eventloop(). new_oneoff_after_ms(0, callback(this, &DumpTable::unplumb_self)); } template void DumpTable::unplumb_self() { debug_msg("%s: unplumbing self\n", this->tablename().c_str()); XLOG_ASSERT(this->_next_table != NULL); XLOG_ASSERT(this->_parent != NULL || (this->_parent == NULL && _dump_active == false)); _dump_active = false; this->_next_table->set_parent(this->_parent); cp(41); if (this->_parent != NULL) { cp(42); FanoutTable *ftp = dynamic_cast*>(this->_parent); XLOG_ASSERT(ftp); ftp->replace_next_table(static_cast*>(this), this->_next_table); } // ensure we can't continue to operate this->_next_table = reinterpret_cast*>(0xd0d0); this->_parent = reinterpret_cast*>(0xd0d0); delete this; } #ifdef AUDIT_ENABLE template void DumpTable::add_audit(const string& log_entry) { //printf("add_audit: %s\n", log_entry.c_str()); if (_audit_entries == 0) { _audit_entries++; _last_audit = _first_audit = 0; } else { _audit_entries++; _last_audit = (_last_audit + 1) % AUDIT_LEN; //have we caught our tail if (_last_audit == _first_audit) { _first_audit = (_last_audit + 1) % AUDIT_LEN; _audit_entries--; } } _audit_entry[_last_audit] = log_entry; } template void DumpTable::print_and_clear_audit() { for (int i = 0; i < _audit_entries; i++) { printf("%d:%s\n", i, _audit_entry[(i + _first_audit)%AUDIT_LEN].c_str()); } _audit_entries = 0; _first_audit = 0; _last_audit = 0; } #endif template class DumpTable; template class DumpTable; xorp/bgp/bgp_varrw_export.hh0000664000076400007640000000350611421137511016343 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/bgp/bgp_varrw_export.hh,v 1.9 2008/12/16 23:55:41 mjh Exp $ #ifndef __BGP_BGP_VARRW_EXPORT_HH__ #define __BGP_BGP_VARRW_EXPORT_HH__ #include "bgp_varrw.hh" /** * @short Returns the output peering for the neighbor variable. * * The neighbor variable in a dest {} policy block refers to the output peer * where the advertisement is being sent. */ template class BGPVarRWExport : public BGPVarRW { public: /** * same as BGPVarRW but returns a different neighbor. * * @param name the name of the filter printed while tracing. * @param neighbor value to return for neighbor variable. */ BGPVarRWExport(const string& name, const string& neighbor); protected: /** * read the neighbor variable---the peer the advertisement is about to be * sent to. * * @return the neighbor variable. */ Element* read_neighbor(); private: const string _neighbor; }; #endif // __BGP_BGP_VARRW_EXPORT_HH__ xorp/bgp/route_table_cache.cc0000664000076400007640000003516711421137511016377 0ustar greearbgreearb // -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #include "bgp_module.h" #include "libxorp/xlog.h" #include "route_table_cache.hh" #include "bgp.hh" template typename DeleteAllNodes::RouteTables DeleteAllNodes::_route_tables; template int DeleteAllNodes::_deletions_per_call = 1000; template CacheTable::CacheTable(string table_name, Safi safi, BGPRouteTable *parent_table, const PeerHandler *peer) : BGPRouteTable("CacheTable-" + table_name, safi), _peer(peer), _unchanged_added(0), _unchanged_deleted(0), _changed_added(0), _changed_deleted(0) { this->_parent = parent_table; _route_table = new RefTrie >; } template CacheTable::~CacheTable() { if (_route_table->begin() != _route_table->end()) { XLOG_WARNING("CacheTable trie was not empty on deletion\n"); } delete _route_table; } template void CacheTable::flush_cache() { debug_msg("%s\n", this->tablename().c_str()); // _route_table->delete_all_nodes(); new DeleteAllNodes(this->_peer, _route_table); _route_table = new RefTrie >; } template int CacheTable::add_route(InternalMessage &rtmsg, BGPRouteTable *caller) { debug_msg("\n %s\n caller: %s\n rtmsg: %p route: %p\n%s\n", this->tablename().c_str(), caller ? caller->tablename().c_str() : "NULL", &rtmsg, rtmsg.route(), rtmsg.str().c_str()); XLOG_ASSERT(caller == this->_parent); XLOG_ASSERT(this->_next_table != NULL); XLOG_ASSERT(!rtmsg.attributes()->is_locked()); //a cache table is never going to be the last table IPNet net = rtmsg.net(); //XXXX //check we don't already have this cached if (_route_table->lookup_node(net) != _route_table->end()) { crash_dump(); XLOG_UNREACHABLE(); } log(c_format("add_route (changed): %s filters: %p,%p,%p", net.str().c_str(), rtmsg.route()->policyfilter(0).get(), rtmsg.route()->policyfilter(1).get(), rtmsg.route()->policyfilter(2).get())); // We used to store only changed routes. Now we store all routes // here. This is a half-way step to going pull-based, and putting // all this stuff in DecisionTable. const SubnetRoute *msg_route = rtmsg.route(); typename RefTrie >::iterator iter; typename RefTrie >::iterator ti; iter = _route_table->lookup_node(net); if (iter == _route_table->end()) { //it's not stored // need to regenerate the PA list to store it in the CacheRoute rtmsg.attributes()->canonicalize(); PAListRef pa_list_ref = new PathAttributeList(rtmsg.attributes()); pa_list_ref.register_with_attmgr(); SubnetRoute* new_route = new SubnetRoute(msg_route->net(), pa_list_ref, msg_route, msg_route->igp_metric()); new_route->set_nexthop_resolved(msg_route->nexthop_resolved()); ti = _route_table->insert(msg_route->net(), CacheRoute(new_route, rtmsg.genid())); new_route->unref(); } else { // we can't get an add for a route that's stored - we'd expect // a replace instead. XLOG_UNREACHABLE(); } debug_msg("Cache Table: %s\n", this->tablename().c_str()); debug_msg("Caching route: %p net: %s %s\n", msg_route, msg_route->net().str().c_str(), msg_route->str().c_str()); //assert(ti.payload().route() == msg_route); //progogate downstream // we create a new message, but reuse the FPAList from the old one. InternalMessage new_rt_msg(ti.payload().route(), rtmsg.attributes(), rtmsg.origin_peer(), rtmsg.genid()); if (rtmsg.push()) new_rt_msg.set_push(); int result = this->_next_table->add_route(new_rt_msg, (BGPRouteTable*)this); rtmsg.inactivate(); switch (result) { case ADD_USED: ti.payload().route()->set_in_use(true); #ifdef DUMP_FROM_CACHE //Our cached copy was used, but we need to tell upstream //that their unmodified version was unused. return ADD_UNUSED; #else //XXX see comment in dump_entire_table - disable dumping //from the cache, so we need to return ADD_USED for now. return ADD_USED; #endif case ADD_UNUSED: ti.payload().route()->set_in_use(false); return ADD_UNUSED; default: //In the case of a failure, we don't know what to believe. //To be safe we set in_use to true, although it may reduce //efficiency. msg_route->set_in_use(true); return result; } } template int CacheTable::replace_route(InternalMessage &old_rtmsg, InternalMessage &new_rtmsg, BGPRouteTable *caller) { debug_msg("\n %s\n" "caller: %s\n" "old rtmsg: %p new rtmsg: %p " "old route: %p" "new route: %p" "old: %s\n new: %s\n", this->tablename().c_str(), caller ? caller->tablename().c_str() : "NULL", &old_rtmsg, &new_rtmsg, old_rtmsg.route(), new_rtmsg.route(), old_rtmsg.str().c_str(), new_rtmsg.str().c_str()); XLOG_ASSERT(caller == this->_parent); XLOG_ASSERT(this->_next_table != NULL); IPNet net = old_rtmsg.net(); XLOG_ASSERT(net == new_rtmsg.net()); log("replace_route: " + net.str()); SubnetRouteConstRef *old_route_reference = NULL; InternalMessage *old_rtmsg_ptr = &old_rtmsg; int result = ADD_USED; typename RefTrie >::iterator iter; iter = _route_table->lookup_node(net); if (iter == _route_table->end()) { //We don't flush the cache, so this should not happen crash_dump(); XLOG_UNREACHABLE(); } // Preserve the route. Taking a reference will prevent // the route being immediately deleted when it's erased // from the RefTrie. Deletion will occur later when the // reference is deleted. const SubnetRoute *old_route = iter.payload().route(); old_route_reference = new SubnetRouteConstRef(old_route); // take the attribute list from the stored version to get the correct MED. PAListRef old_pa_list = old_route->attributes(); FPAListRef old_fpa_list = new FastPathAttributeList(old_pa_list); old_rtmsg_ptr = new InternalMessage(old_route, old_fpa_list, old_rtmsg.origin_peer(), iter.payload().genid()); //delete it from our cache, _route_table->erase(old_rtmsg.net()); old_pa_list.deregister_with_attmgr(); //It's the responsibility of the recipient of a changed //route to store it or free it. //Free the route from the message. old_rtmsg.inactivate(); const SubnetRoute *msg_new_route = new_rtmsg.route(); //store it locally typename RefTrie >::iterator ti; // need to regenerate the PA list to store it in the CacheRoute new_rtmsg.attributes()->canonicalize(); PAListRef pa_list_ref = new PathAttributeList(new_rtmsg.attributes()); pa_list_ref.register_with_attmgr(); SubnetRoute* new_route = new SubnetRoute(msg_new_route->net(), pa_list_ref, msg_new_route, msg_new_route->igp_metric()); new_route->set_nexthop_resolved(msg_new_route->nexthop_resolved()); ti = _route_table->insert(net, CacheRoute(new_route, new_rtmsg.genid())); new_route->unref(); //progogate downstream InternalMessage new_rtmsg_copy(ti.payload().route(), new_rtmsg.attributes(), new_rtmsg.origin_peer(), new_rtmsg.genid()); if (new_rtmsg.push()) new_rtmsg_copy.set_push(); result = this->_next_table->replace_route(*old_rtmsg_ptr, new_rtmsg_copy, (BGPRouteTable*)this); new_rtmsg.inactivate(); switch (result) { case ADD_USED: ti.payload().route()->set_in_use(true); break; case ADD_UNUSED: ti.payload().route()->set_in_use(false); break; default: //In the case of a failure, we don't know what to believe. //To be safe we set in_use to true, although it may reduce //efficiency. ti.payload().route()->set_in_use(true); } if (old_rtmsg_ptr != &old_rtmsg) { delete old_rtmsg_ptr; XLOG_ASSERT(old_route_reference != NULL); delete old_route_reference; } return result; } template int CacheTable::delete_route(InternalMessage &rtmsg, BGPRouteTable *caller) { int result = 0; debug_msg("\n %s\n caller: %s\n rtmsg: %p route: %p\n%s\n", this->tablename().c_str(), caller ? caller->tablename().c_str() : "NULL", &rtmsg, rtmsg.route(), rtmsg.str().c_str()); XLOG_ASSERT(caller == this->_parent); XLOG_ASSERT(this->_next_table != NULL); IPNet net = rtmsg.net(); log(c_format("delete_route (changed): %s filters: %p,%p,%p", net.str().c_str(), rtmsg.route()->policyfilter(0).get(), rtmsg.route()->policyfilter(1).get(), rtmsg.route()->policyfilter(2).get())); typename RefTrie >::iterator iter; iter = _route_table->lookup_node(net); XLOG_ASSERT(iter != _route_table->end()); const SubnetRoute *existing_route = iter.payload().route(); uint32_t existing_genid = iter.payload().genid(); XLOG_ASSERT(rtmsg.genid() == existing_genid); debug_msg("Found cached route: %s\n", existing_route->str().c_str()); PAListRef old_pa_list = existing_route->attributes(); // Delete it from our cache trie. The actual deletion will // only take place when iter goes out of scope, so // existing_route remains valid til then. _route_table->erase(iter); old_pa_list.deregister_with_attmgr(); // Fix the parent route in case it has changed. This is also // important if the parent route has been zeroed somewhere // upstream to avoid unwanted side effects. const_cast*>(existing_route) ->set_parent_route(rtmsg.route()->parent_route()); // create the FPA list from the stored version FPAListRef fpa_list = new FastPathAttributeList(old_pa_list); InternalMessage old_rt_msg(existing_route, fpa_list, rtmsg.origin_peer(), existing_genid); if (rtmsg.push()) old_rt_msg.set_push(); result = this->_next_table->delete_route(old_rt_msg, (BGPRouteTable*)this); if (rtmsg.copied()) { //It's the responsibility of the recipient of a changed //route to store it or free it. //Free the route from the message. rtmsg.inactivate(); } return result; } template int CacheTable::push(BGPRouteTable *caller) { XLOG_ASSERT(caller == this->_parent); return this->_next_table->push((BGPRouteTable*)this); } template int CacheTable::route_dump(InternalMessage &rtmsg, BGPRouteTable *caller, const PeerHandler *dump_peer) { XLOG_ASSERT(caller == this->_parent); //Check we've got it cached. Clear the changed bit so we //don't confuse anyone downstream. IPNet net = rtmsg.route()->net(); typename RefTrie >::iterator iter; iter = _route_table->lookup_node(net); XLOG_ASSERT(iter != _route_table->end()); XLOG_ASSERT(rtmsg.genid() == iter.payload().genid()); const SubnetRoute *existing_route = iter.payload().route(); //It's the responsibility of the recipient of a changed route //to store or delete it. We don't need it anymore (we found //the cached copy) so we delete it. if (rtmsg.copied()) rtmsg.route()->unref(); // create the FPA list from the stored version PAListRef pa_list = existing_route->attributes(); FPAListRef fpa_list = new FastPathAttributeList(pa_list); //the message we pass on needs to contain our cached //route, because the MED info in it may not be in the original //version of the route. InternalMessage new_msg(existing_route, fpa_list, rtmsg.origin_peer(), rtmsg.genid()); return this->_next_table->route_dump(new_msg, (BGPRouteTable*)this, dump_peer); } template const SubnetRoute* CacheTable::lookup_route(const IPNet &net, uint32_t& genid, FPAListRef& fpa_list) const { // we cache all requests, so if we don't have it, it doesn't exist typename RefTrie >::iterator iter; iter = _route_table->lookup_node(net); if (iter != _route_table->end()) { genid = iter.payload().genid(); // create the FPA list from the stored version PAListRef pa_list = iter.payload().route()->attributes(); fpa_list = new FastPathAttributeList(pa_list); return iter.payload().route(); } return 0; } template void CacheTable::route_used(const SubnetRoute* rt, bool in_use) { this->_parent->route_used(rt, in_use); } template string CacheTable::str() const { string s = "CacheTable" + this->tablename(); return s; } template bool CacheTable::get_next_message(BGPRouteTable *next_table) { XLOG_ASSERT(this->_next_table == next_table); return this->_parent->get_next_message(this); } template string CacheTable::dump_state() const { string s; s = "=================================================================\n"; s += "CacheTable\n"; s += str() + "\n"; s += "=================================================================\n"; s += "Unchanged added: " + c_format("%d\n", _unchanged_added); s += "Unchanged deleted: " + c_format("%d\n", _unchanged_deleted); s += "Changed added: " + c_format("%d\n", _changed_added); s += "Changed deleted: " + c_format("%d\n", _changed_deleted); // s += "In cache: " + c_format("%d\n", _route_table->size()); s += _route_table->str(); s += CrashDumper::dump_state(); return s; } template EventLoop& CacheTable::eventloop() const { return _peer->eventloop(); } template class CacheTable; template class CacheTable; xorp/bgp/route_table_policy_sm.hh0000664000076400007640000000627511421137511017342 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/bgp/route_table_policy_sm.hh,v 1.13 2008/10/02 21:56:20 bms Exp $ #ifndef __BGP_ROUTE_TABLE_POLICY_SM_HH__ #define __BGP_ROUTE_TABLE_POLICY_SM_HH__ #include "route_table_policy.hh" #include "peer_route_pair.hh" #include "libxorp/eventloop.hh" /** * @short SourceMatch table has the aditional ability to perform route dumps. * * The SourceMatch policy table will request the route dumps from the ribin. * These dumps will eventually meet an import policy table and be translated to * an add/replace or delete. */ template class PolicyTableSourceMatch : public PolicyTable { public: /** * @param tablename the name of the table. * @param safi the safi. * @param parent the parent table. * @param pfs a reference to the global policyfilters. * @param ev event loop for this process. */ PolicyTableSourceMatch(const string& tablename, const Safi& safi, BGPRouteTable* parent, PolicyFilters& pfs, EventLoop& ev); ~PolicyTableSourceMatch(); /** * Push routes of all these peers. * * @param peer_list peers for which routes whould be dumped. */ void push_routes(list*>& peer_list); /* * Need to keep track what is going on with dump iterators which peers go * down and up */ void peering_is_down(const PeerHandler *peer, uint32_t genid); void peering_went_down(const PeerHandler *peer, uint32_t genid, BGPRouteTable *caller); void peering_down_complete(const PeerHandler *peer, uint32_t genid, BGPRouteTable *caller); void peering_came_up(const PeerHandler *peer, uint32_t genid, BGPRouteTable *caller); private: /** * Dump the next route. */ void do_next_route_dump(); /** * Stop dumping routes. */ void end_route_dump(); /** * Do a background route dump */ bool do_background_dump(); /** * Check whether a policy push is occuring * * @return true if routes are being pushed */ bool pushing_routes(); private: EventLoop& eventloop(); bool _pushing_routes; DumpIterator* _dump_iter; EventLoop& _ev; XorpTask _dump_task; }; #endif // __BGP_ROUTE_TABLE_POLICY_SM_HH__ xorp/bgp/plumbing.hh0000664000076400007640000002032111540225521014561 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/bgp/plumbing.hh,v 1.46 2008/11/08 06:14:37 mjh Exp $ #ifndef __BGP_PLUMBING_HH__ #define __BGP_PLUMBING_HH__ #include "route_table_ribin.hh" #include "route_table_damping.hh" #include "route_table_deletion.hh" #include "route_table_ribout.hh" #include "route_table_decision.hh" #include "route_table_aggregation.hh" #include "route_table_fanout.hh" #include "route_table_dump.hh" #include "route_table_filter.hh" #include "route_table_cache.hh" #include "route_table_nhlookup.hh" #include "route_table_policy.hh" #include "route_table_policy_sm.hh" #include "route_table_policy_im.hh" #include "route_table_policy_ex.hh" #include "peer.hh" #include "rib_ipc_handler.hh" #include "next_hop_resolver.hh" #include "parameter.hh" #include "policy/backend/policy_filters.hh" #include "path_attribute.hh" class BGPPlumbing; template class RouteTableReader; template class BGPPlumbingAF { public: BGPPlumbingAF(const string& ribname, BGPPlumbing& master, NextHopResolver& next_hop_resolver); ~BGPPlumbingAF(); int add_peering(PeerHandler* peer_handler); int stop_peering(PeerHandler* peer_handler); int peering_went_down(PeerHandler* peer_handler); int peering_came_up(PeerHandler* peer_handler); int delete_peering(PeerHandler* peer_handler); void flush(PeerHandler* peer_handler); int add_route(const IPNet& net, FPAListRef& pa_list, const PolicyTags& policytags, PeerHandler* peer_handler); int delete_route(InternalMessage &rtmsg, PeerHandler* peer_handler); int delete_route(const IPNet &net, PeerHandler* peer_handler); const SubnetRoute* lookup_route(const IPNet &net) const; void push(PeerHandler* peer_handler); void output_no_longer_busy(PeerHandler* peer_handler); /** * @return the number of prefixes in the RIB-IN. */ uint32_t get_prefix_count(PeerHandler* peer_handler) const; /** * Hook to the next hop resolver so that xrl calls from the RIB * can be passed through. */ NextHopResolver& next_hop_resolver() {return _next_hop_resolver;} uint32_t create_route_table_reader(const IPNet& prefix); bool read_next_route(uint32_t token, const SubnetRoute*& route, IPv4& peer_id); /** * Get the status of the Plumbing * * @param reason the human-readable reason for any failure * * @return false if Plumbing has suffered a fatal error, * true otherwise */ bool status(string& reason) const; /** * Push routes through policy filters for re-filtering. */ void push_routes(); private: /** * A peering has just come up dump all the routes to it. */ void dump_entire_table(FilterTable *filter_out, string ribname); void configure_inbound_filter(PeerHandler* peer_handler, FilterTable* filter_in); void configure_outbound_filter(PeerHandler* peer_handler, FilterTable* filter_out); void reconfigure_filters(PeerHandler* peer_handler); const A& get_local_nexthop(const PeerHandler *peer_handler) const; /** * Is the peer directly connected and if it is return the common * subnet and the peer address. */ bool directly_connected(const PeerHandler *peer_handler, IPNet& subnet, A& peer) const; list *> ribin_list() const; map * > _in_map; map *, PeerHandler*> _reverse_out_map; map *> _out_map; DecisionTable *_decision_table; PolicyTableSourceMatch* _policy_sourcematch_table; AggregationTable *_aggregation_table; FanoutTable *_fanout_table; RibInTable *_ipc_rib_in_table; RibOutTable *_ipc_rib_out_table; /* _tables is all the tables not covered above*/ set *> _tables; uint32_t _max_reader_token; map *> _route_table_readers; bool _awaits_push; string _ribname; BGPPlumbing& _master; NextHopResolver& _next_hop_resolver; }; /* * BGP plumbing is the class that sets up all the BGP route tables, * from RIB-In to RIB-Out, plus everything in between. */ class RibIpcHandler; class BGPPlumbing { public: BGPPlumbing(const Safi safi, RibIpcHandler* rib_handler, AggregationHandler* aggr_handler, NextHopResolver&, #ifdef HAVE_IPV6 NextHopResolver&, #endif PolicyFilters&, BGPMain& bgp); int add_peering(PeerHandler* peer_handler); int stop_peering(PeerHandler* peer_handler); int peering_went_down(PeerHandler* peer_handler); int peering_came_up(PeerHandler* peer_handler); int delete_peering(PeerHandler* peer_handler); void flush(PeerHandler* peer_handler); int add_route(const IPv4Net& net, FPAList4Ref& pa_list, const PolicyTags& policytags, PeerHandler* peer_handler); int delete_route(InternalMessage &rtmsg, PeerHandler* peer_handler); int delete_route(const IPNet &net, PeerHandler* peer_handler); template void push(PeerHandler* peer_handler); void output_no_longer_busy(PeerHandler* peer_handler); const SubnetRoute* lookup_route(const IPNet &net) const; /** * @return the number of prefixes in the RIB-IN. */ uint32_t get_prefix_count(const PeerHandler* peer_handler); RibIpcHandler *rib_handler() const {return _rib_handler;} AggregationHandler *aggr_handler() const {return _aggr_handler;} BGPPlumbingAF& plumbing_ipv4() { return _plumbing_ipv4; } template uint32_t create_route_table_reader(const IPNet& prefix); bool read_next_route(uint32_t token, const SubnetRoute*& route, IPv4& peer_id); /** * Get the status of the Plumbing * * @param reason the human-readable reason for any failure * * @return false if Plumbing has suffered a fatal error, * true otherwise */ bool status(string& reason) const; /** * @return Safi of this plumb. */ Safi safi() const {return _safi;} /** * @return Reference to the main bgp class. */ BGPMain& main() const { return _bgp; } /** * Push routes through policy filters for re-filtering. */ void push_routes(); PolicyFilters& policy_filters() { return _policy_filters; } /** IPv6 stuff */ #ifdef HAVE_IPV6 int add_route(const IPv6Net& net, FPAList6Ref& pa_list, const PolicyTags& policytags, PeerHandler* peer_handler); int delete_route(InternalMessage &rtmsg, PeerHandler* peer_handler); int delete_route(const IPNet &net, PeerHandler* peer_handler); const SubnetRoute* lookup_route(const IPNet &net) const; BGPPlumbingAF& plumbing_ipv6() { return _plumbing_ipv6; } bool read_next_route(uint32_t token, const SubnetRoute*& route, IPv4& peer_id); #endif // ipv6 private: BGPMain &_bgp; RibIpcHandler *_rib_handler; AggregationHandler *_aggr_handler; NextHopResolver& _next_hop_resolver_ipv4; const Safi _safi; PolicyFilters& _policy_filters; BGPPlumbingAF _plumbing_ipv4; #ifdef HAVE_IPV6 NextHopResolver& _next_hop_resolver_ipv6; BGPPlumbingAF _plumbing_ipv6; #endif }; #endif // __BGP_PLUMBING_HH__ xorp/bgp/bgp_varrw.cc0000664000076400007640000003733211540224217014736 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "libxorp/xorp.h" #include "bgp_module.h" #include "policy/common/policy_utils.hh" #include "peer_handler.hh" #include "bgp_varrw.hh" #include "policy/backend/version_filters.hh" #include "policy/common/elem_filter.hh" #include "policy/common/elem_bgp.hh" #ifndef XORP_USE_USTL #include #endif using namespace policy_utils; template BGPVarRWCallbacks BGPVarRW::_callbacks; template BGPVarRW::BGPVarRW(const string& name) : _name(name), _rtmsg(NULL), _got_fmsg(false), _ptags(NULL), _wrote_ptags(false), _palist(NULL), _no_modify(false), _modified(false), _route_modify(false) { XLOG_ASSERT( unsigned(VAR_BGPMAX) <= VAR_MAX); for (int i = 0; i < 3; i++) _wrote_pfilter[i] = false; } template BGPVarRW::~BGPVarRW() { cleanup(); } template void BGPVarRW::cleanup() { if (_ptags) delete _ptags; } template void BGPVarRW::attach_route(InternalMessage& rtmsg, bool no_modify) { cleanup(); _rtmsg = &rtmsg; _got_fmsg = false; _ptags = NULL; _wrote_ptags = false; _palist = rtmsg.attributes(); _no_modify = no_modify; _modified = false; _route_modify = false; _aggr_brief_mode = rtmsg.route()->aggr_brief_mode(); _aggr_prefix_len = rtmsg.route()->aggr_prefix_len(); for (int i = 0; i < 3; i++) { if (_wrote_pfilter[i]) _pfilter[i].release(); _wrote_pfilter[i] = false; } } template void BGPVarRW::detach_route(InternalMessage& rtmsg) { UNUSED(rtmsg); _palist = 0; } template Element* BGPVarRW::read_policytags() { return _rtmsg->route()->policytags().element(); } template Element* BGPVarRW::read_tag() { return _rtmsg->route()->policytags().element_tag(); } template Element* BGPVarRW::read_filter_im() { return new ElemFilter(_rtmsg->route()->policyfilter(0)); } template Element* BGPVarRW::read_filter_sm() { return new ElemFilter(_rtmsg->route()->policyfilter(1)); } template Element* BGPVarRW::read_filter_ex() { return new ElemFilter(_rtmsg->route()->policyfilter(2)); } template <> Element* BGPVarRW::read_network4() { return new ElemIPv4Net(_rtmsg->route()->net()); } template <> Element* BGPVarRW::read_network4() { return NULL; } template <> Element* BGPVarRW::read_network6() { return _ef.create(ElemIPv6Net::id, _rtmsg->route()->net().str().c_str()); } template <> Element* BGPVarRW::read_network6() { return NULL; } template <> Element* BGPVarRW::read_nexthop6() { return _ef.create(ElemIPv6NextHop::id, _palist->nexthop().str().c_str()); } template <> Element* BGPVarRW::read_nexthop6() { return NULL; } template <> Element* BGPVarRW::read_nexthop4() { return new ElemIPv4NextHop(_palist->nexthop()); } template <> Element* BGPVarRW::read_nexthop4() { return NULL; } template Element* BGPVarRW::read_aspath() { return new ElemASPath(_palist->aspath()); } template Element* BGPVarRW::read_origin() { uint32_t origin = _palist->origin(); return _ef.create(ElemU32::id, to_str(origin).c_str()); } template Element* BGPVarRW::read_localpref() { const LocalPrefAttribute* lpref = _palist->local_pref_att(); if (lpref) { return _ef.create(ElemU32::id, to_str(lpref->localpref()).c_str()); } else return NULL; } template Element* BGPVarRW::read_community() { const CommunityAttribute* ca = _palist->community_att(); // no community! if (!ca) return NULL; ElemSetCom32* es = new ElemSetCom32; const set& com = ca->community_set(); for (set::const_iterator i = com.begin(); i != com.end(); ++i) es->insert(ElemCom32(*i)); return es; } template Element* BGPVarRW::read_med() { const MEDAttribute* med = _palist->med_att(); if (med) return new ElemU32(med->med()); else return NULL; } template Element* BGPVarRW::read_med_remove() { const MEDAttribute* med = _palist->med_att(); if (med) return new ElemBool(false); // XXX: default is don't remove the MED else return NULL; } template Element* BGPVarRW::read_aggregate_prefix_len() { // No-op. Should never be called. return new ElemU32(_aggr_prefix_len); } template Element* BGPVarRW::read_aggregate_brief_mode() { // No-op. Should never be called. return new ElemU32(_aggr_brief_mode); } template Element* BGPVarRW::read_was_aggregated() { if (_aggr_prefix_len == SR_AGGR_EBGP_WAS_AGGREGATED) return new ElemBool(true); else return new ElemBool(false); } template Element* BGPVarRW::single_read(const Id& id) { ReadCallback cb = _callbacks._read_map[id]; XLOG_ASSERT(cb); return (this->*cb)(); } template void BGPVarRW::write_community(const Element& e) { _route_modify = true; XLOG_ASSERT(e.type() == ElemSetCom32::id); const ElemSetCom32& es = dynamic_cast(e); if (_palist->community_att()) _palist->remove_attribute_by_type(COMMUNITY); CommunityAttribute ca; for (typename ElemSetCom32::const_iterator i = es.begin(); i != es.end(); ++i) { ca.add_community( (*i).val()); } _palist->add_path_attribute(ca); } template Element* BGPVarRW::read_neighbor() { Element* e = NULL; const PeerHandler* ph = _rtmsg->origin_peer(); if (ph != NULL && !ph->originate_route_handler()) { e = _ef.create(ElemIPv4::id, ph->get_peer_addr().c_str()); } return e; } template InternalMessage* BGPVarRW::filtered_message() { XLOG_ASSERT(_modified && _rtmsg); _got_fmsg = true; return _rtmsg; } template void BGPVarRW::write_filter_im(const Element& e) { const ElemFilter& ef = dynamic_cast(e); _pfilter[0] = ef.val(); _wrote_pfilter[0] = true; } template void BGPVarRW::write_filter_sm(const Element& e) { const ElemFilter& ef = dynamic_cast(e); _pfilter[1] = ef.val(); _wrote_pfilter[1] = true; } template void BGPVarRW::write_filter_ex(const Element& e) { const ElemFilter& ef = dynamic_cast(e); _pfilter[2] = ef.val(); _wrote_pfilter[2] = true; } template void BGPVarRW::write_policytags(const Element& e) { if (!_ptags) _ptags = new PolicyTags(_rtmsg->route()->policytags()); _ptags->set_ptags(e); _wrote_ptags = true; // XXX: maybe we should make policytags be like filter pointers... i.e. meta // information, rather than real route information... _route_modify = true; } template void BGPVarRW::write_tag(const Element& e) { if (!_ptags) _ptags = new PolicyTags(_rtmsg->route()->policytags()); _ptags->set_tag(e); _wrote_ptags = true; _route_modify = true; } template <> void BGPVarRW::write_nexthop4(const Element& e) { write_nexthop(e); } template void BGPVarRW::write_nexthop(const Element& e) { _route_modify = true; const ElemNextHop* eip = dynamic_cast*>(&e); XLOG_ASSERT(eip != NULL); A nh; switch (eip->var()) { case ElemNextHop::VAR_NONE: nh = eip->addr(); break; case ElemNextHop::VAR_SELF: XLOG_ASSERT(_self != nh); nh = _self; break; case ElemNextHop::VAR_PEER_ADDRESS: XLOG_ASSERT(_peer != nh); nh = _peer; break; case ElemNextHop::VAR_DISCARD: case ElemNextHop::VAR_REJECT: case ElemNextHop::VAR_NEXT_TABLE: XLOG_ASSERT(!"not implemented"); break; } _palist->replace_nexthop(nh); } template <> void BGPVarRW::write_nexthop4(const Element& /* e */) { } template <> void BGPVarRW::write_nexthop6(const Element& e) { write_nexthop(e); } template <> void BGPVarRW::write_nexthop6(const Element& /* e */) { } template void BGPVarRW::write_aspath(const Element& e) { _route_modify = true; const ElemASPath& aspath = dynamic_cast(e); _palist->replace_AS_path(aspath.val()); } template void BGPVarRW::write_med(const Element& e) { _route_modify = true; if (_palist->med_att()) _palist->remove_attribute_by_type(MED); const ElemU32& u32 = dynamic_cast(e); MEDAttribute med(u32.val()); _palist->add_path_attribute(med); } template void BGPVarRW::write_med_remove(const Element& e) { const ElemBool& med_remove = dynamic_cast(e); if (! med_remove.val()) return; // Don't remove the MED if (_palist->med_att()) _palist->remove_attribute_by_type(MED); _route_modify = true; } template void BGPVarRW::write_aggregate_prefix_len(const Element& e) { // We should not set the aggr_pref_len if already set by someone else! if (_aggr_prefix_len != SR_AGGR_IGNORE) return; const ElemU32& u32 = dynamic_cast(e); if (u32.val() <= 128) { // Only accept valid prefix_len values, since out of range values // might get interpreted later as special markers _aggr_prefix_len = u32.val(); _route_modify = true; } } template void BGPVarRW::write_aggregate_brief_mode(const Element& e) { const ElemBool& brief_mode = dynamic_cast(e); if (!brief_mode.val()) return; _aggr_brief_mode = true; _route_modify = true; } template void BGPVarRW::write_was_aggregated(const Element& e) { // No-op. Should never be called. UNUSED(e); } template void BGPVarRW::write_localpref(const Element& e) { _route_modify = true; if (_palist->local_pref_att()) _palist->remove_attribute_by_type(LOCAL_PREF); const ElemU32& u32 = dynamic_cast(e); LocalPrefAttribute lpref(u32.val()); _palist->add_path_attribute(lpref); } template void BGPVarRW::write_origin(const Element& e) { _route_modify = true; const ElemU32& u32 = dynamic_cast(e); OriginType origin = INCOMPLETE; if (u32.val() > INCOMPLETE) XLOG_FATAL("Unknown origin: %d\n", u32.val()); origin = static_cast(u32.val()); _palist->replace_origin(origin); } template void BGPVarRW::single_write(const Id& id, const Element& e) { if (_no_modify) return; WriteCallback cb = _callbacks._write_map[id]; XLOG_ASSERT(cb); (this->*cb)(e); } template void BGPVarRW::end_write() { // I think there should be a better way of modifying bgp routes... [a copy // constructor, or helper methods possibly]. // OK. The real problem is that you can't clone a subnet route, and assign // path attributes to it! if (_no_modify) { return; } // we used to duplicate the route because we needed to create a // new path attribute list. We don't do this anymore; we just // modify the FastPathAttributeList in situe. const SubnetRoute* route = _rtmsg->route(); // only meta routing stuff changed [i.e. policy filter pointers... so we can // get away without creating a new subnet route, etc] if (!_route_modify) { for (int i = 0; i < 3; i++) { if (_wrote_pfilter[i]) route->set_policyfilter(i, _pfilter[i]); } return; } // propagate policy tags if (_wrote_ptags) route->set_policytags(*_ptags); for (int i = 0; i < 3; i++) { if (_wrote_pfilter[i]) { // XXX we need to change the policyfilter in the ORIGINAL ROUTE too. // i.e. the version should be kept in the ribin subnetroute copy. // If we just store it in the copy we send down stream, next time we // get a push, we will get the wrong filter pointer [it will always // be the original one which was in ribin,.. as we never update it]. route->set_policyfilter(i, _pfilter[i]); } } _rtmsg->set_changed(); if (_aggr_brief_mode) route->set_aggr_brief_mode(); else route->clear_aggr_brief_mode(); _modified = true; } template bool BGPVarRW::modified() { return _modified; } template string BGPVarRW::more_tracelog() { string x = "BGP " + _name + " route: "; uint32_t level = trace(); if (level > 0) x += _rtmsg->net().str(); if (level > 1) { x += " Full route: "; x += _rtmsg->str(); } return x; } template void BGPVarRW::set_peer(const A& peer) { _peer = peer; } template void BGPVarRW::set_self(const A& self) { _self = self; } template BGPVarRWCallbacks::BGPVarRWCallbacks() { init_rw(VarRW::VAR_POLICYTAGS, &BGPVarRW::read_policytags, &BGPVarRW::write_policytags); init_rw(VarRW::VAR_TAG, &BGPVarRW::read_tag, &BGPVarRW::write_tag); init_rw(VarRW::VAR_FILTER_IM, &BGPVarRW::read_filter_im, &BGPVarRW::write_filter_im); init_rw(VarRW::VAR_FILTER_SM, &BGPVarRW::read_filter_sm, &BGPVarRW::write_filter_sm); init_rw(VarRW::VAR_FILTER_EX, &BGPVarRW::read_filter_ex, &BGPVarRW::write_filter_ex); init_rw(BGPVarRW::VAR_NETWORK4, &BGPVarRW::read_network4, NULL); init_rw(BGPVarRW::VAR_NEXTHOP4, &BGPVarRW::read_nexthop4, &BGPVarRW::write_nexthop4); init_rw(BGPVarRW::VAR_NETWORK6, &BGPVarRW::read_network6, NULL); init_rw(BGPVarRW::VAR_NEXTHOP6, &BGPVarRW::read_nexthop6, &BGPVarRW::write_nexthop6); init_rw(BGPVarRW::VAR_ASPATH, &BGPVarRW::read_aspath, &BGPVarRW::write_aspath); init_rw(BGPVarRW::VAR_ORIGIN, &BGPVarRW::read_origin, &BGPVarRW::write_origin); init_rw(BGPVarRW::VAR_NEIGHBOR, &BGPVarRW::read_neighbor_base_cb, NULL); init_rw(BGPVarRW::VAR_LOCALPREF, &BGPVarRW::read_localpref, &BGPVarRW::write_localpref); init_rw(BGPVarRW::VAR_COMMUNITY, &BGPVarRW::read_community, &BGPVarRW::write_community); init_rw(BGPVarRW::VAR_MED, &BGPVarRW::read_med, &BGPVarRW::write_med); init_rw(BGPVarRW::VAR_MED_REMOVE, &BGPVarRW::read_med_remove, &BGPVarRW::write_med_remove); init_rw(BGPVarRW::VAR_AGGREGATE_PREFIX_LEN, &BGPVarRW::read_aggregate_prefix_len, &BGPVarRW::write_aggregate_prefix_len); init_rw(BGPVarRW::VAR_AGGREGATE_BRIEF_MODE, &BGPVarRW::read_aggregate_brief_mode, &BGPVarRW::write_aggregate_brief_mode); init_rw(BGPVarRW::VAR_WAS_AGGREGATED, &BGPVarRW::read_was_aggregated, &BGPVarRW::write_was_aggregated); } template void BGPVarRWCallbacks::init_rw(const VarRW::Id& id, RCB rcb, WCB wcb) { if (rcb) _read_map[id] = rcb; if (wcb) _write_map[id] = wcb; } template class BGPVarRW; template class BGPVarRW; template class BGPVarRWCallbacks; template class BGPVarRWCallbacks; xorp/bgp/route_table_fanout.hh0000664000076400007640000001243111540224220016623 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/bgp/route_table_fanout.hh,v 1.27 2008/11/08 06:14:39 mjh Exp $ #ifndef __BGP_ROUTE_TABLE_FANOUT_HH__ #define __BGP_ROUTE_TABLE_FANOUT_HH__ #include "route_table_base.hh" #include "peer_route_pair.hh" #include "route_queue.hh" #include "crash_dump.hh" template class DumpTable; template class NextTableMapIterator { public: NextTableMapIterator() {}; NextTableMapIterator(const typename multimap*>::iterator& iter) { _iter = iter; } BGPRouteTable* first() { return _iter->second->route_table(); } PeerTableInfo& second() { return *(_iter->second); } void operator ++(int) { _iter++; } bool operator==(const NextTableMapIterator& them) const { return _iter == them._iter; } private: typename multimap *>::iterator _iter; }; /** * NextTableMap behaves more or less like a map between a BGPRouteTable* and a const PeerHandler*, but it gives deterministic ordering semantics so our test suites aren't dependent on the memory allocator. This class basically hides the complexity of maintaining two parallel data structures from the main FanoutTable code */ template class NextTableMap { public: typedef NextTableMapIterator iterator; NextTableMap() {}; ~NextTableMap(); void insert(BGPRouteTable *next_table, const PeerHandler *ph, uint32_t genid); void erase(iterator& iter); iterator find(BGPRouteTable *next_table); iterator begin(); iterator end(); private: map *, PeerTableInfo* > _next_tables; multimap* > _next_table_order; }; template class FanoutTable : public BGPRouteTable, CrashDumper { public: FanoutTable(string tablename, Safi safi, BGPRouteTable *parent, PeerHandler *aggr_handler, BGPRouteTable *aggr_table); ~FanoutTable(); int add_next_table(BGPRouteTable *next_table, const PeerHandler *ph, uint32_t genid); int remove_next_table(BGPRouteTable *next_table); int replace_next_table(BGPRouteTable *old_next_table, BGPRouteTable *new_next_table); int add_route(InternalMessage &rtmsg, BGPRouteTable *caller); int replace_route(InternalMessage &old_rtmsg, InternalMessage &new_rtmsg, BGPRouteTable *caller); int delete_route(InternalMessage &rtmsg, BGPRouteTable *caller); int route_dump(InternalMessage &rtmsg, BGPRouteTable *caller, const PeerHandler *dump_peer); int push(BGPRouteTable *caller); const SubnetRoute *lookup_route(const IPNet &net, uint32_t& genid, FPAListRef& pa_list) const; RouteTableType type() const {return FANOUT_TABLE;} string str() const; void peer_table_info(list*>& peer_list); int dump_entire_table(BGPRouteTable *child_to_dump_to, Safi safi, string ribname); /* mechanisms to implement flow control in the output plumbing */ bool get_next_message(BGPRouteTable *next_table); void peering_went_down(const PeerHandler *peer, uint32_t genid, BGPRouteTable *caller); void peering_down_complete(const PeerHandler *peer, uint32_t genid, BGPRouteTable *caller); void peering_came_up(const PeerHandler *peer, uint32_t genid, BGPRouteTable *caller); void crash_dump() const { CrashDumper::crash_dump(); } string dump_state() const; void print_queue(); private: void add_to_queue(RouteQueueOp operation, InternalMessage &rtmsg, const list*>& queued_peers); void add_replace_to_queue(InternalMessage &old_rtmsg, InternalMessage &new_rtmsg, const list*>& queued_peers); void add_push_to_queue(const list*>& queued_peers, const PeerHandler *origin_peer); void set_queue_positions(const list*>& queued_peers); void skip_entire_queue(BGPRouteTable *next_table); void wakeup_downstream(list *>& queued_peers); void add_dump_table(DumpTable *dump_table); void remove_dump_table(DumpTable *dump_table); NextTableMap _next_tables; list *> _output_queue; set *> _dump_tables; PeerTableInfo *_aggr_peerinfo; }; #endif // __BGP_ROUTE_TABLE_FANOUT_HH__ xorp/bgp/attribute_manager.cc0000664000076400007640000000516511421137511016437 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net //#define DEBUG_LOGGING #include "bgp_module.h" #include "attribute_manager.hh" #if 0 template bool StoredAttributeList::operator<(const StoredAttributeList& them) const { return (memcmp(hash(), them.hash(), 16) < 0); } #endif template AttributeManager::AttributeManager() { _total_references = 0; } template PAListRef AttributeManager::add_attribute_list(PAListRef& palist) { debug_msg("AttributeManager::add_attribute_list\n"); typedef typename set, Att_Ptr_Cmp >::iterator Iter; Iter i = _attribute_lists.find(palist); if (i == _attribute_lists.end()) { _attribute_lists.insert(palist); palist->incr_managed_refcount(1); debug_msg("** new att list\n"); debug_msg("** (+) ref count for %p now %u\n", palist.attributes(), palist->managed_references()); return palist; } (*i)->incr_managed_refcount(1); debug_msg("** old att list\n"); debug_msg("** (+) ref count for %p now %u\n", (*i).attributes(), (*i)->managed_references()); debug_msg("done\n"); return *i; } template void AttributeManager::delete_attribute_list(PAListRef& palist) { debug_msg("AttributeManager::delete_attribute_list %p\n", palist.attributes()); typedef typename set, Att_Ptr_Cmp >::iterator Iter; Iter i = _attribute_lists.find(palist); assert(i != _attribute_lists.end()); XLOG_ASSERT((*i)->managed_references()>=1); (*i)->decr_managed_refcount(1); debug_msg("** (-) ref count for %p now %u\n", (*i).attributes(), (*i)->managed_references()); if ((*i)->managed_references() < 1) { _attribute_lists.erase(i); } } template class AttributeManager; template class AttributeManager; xorp/bgp/next_hop_resolver.cc0000664000076400007640000012373211540225521016511 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #include "bgp_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/timer.hh" #include "xrl/targets/ribclient_base.hh" #include "xrl/interfaces/rib_xif.hh" #include "bgp.hh" #include "next_hop_resolver.hh" #include "route_table_nhlookup.hh" #include "route_table_decision.hh" #include "profile_vars.hh" template NextHopResolver::NextHopResolver(XrlStdRouter *xrl_router, EventLoop& eventloop, BGPMain& bgp) : _xrl_router(xrl_router), _eventloop(eventloop), _bgp(bgp), _next_hop_rib_request(xrl_router, *this, _next_hop_cache, bgp) { } template NextHopResolver::~NextHopResolver() { } template void NextHopResolver::add_decision(DecisionTable *decision) { _decision.push_back(decision); } template bool NextHopResolver::register_ribname(const string& ribname) { _ribname = ribname; return _next_hop_rib_request.register_ribname(ribname); } /* * The next hop lookup table uses this interface to discover if this * next hop is known. We don't care if this nexthop is resolvable or * not only that we have at some point asked the RIB and that we have * an answer. */ template bool NextHopResolver::register_nexthop(A nexthop, IPNet net_from_route, NhLookupTable *requester) { debug_msg("E nexthop %s net %s requested %p\n", nexthop.str().c_str(), net_from_route.str().c_str(), requester); /* ** We haven't hooked up with a rib so just return true. */ if ("" == _ribname) return true; /* ** The common case will be that we know about this next hop and ** its in our cache. Another option is that the nexthop falls into ** a range for which we already have an answer from the RIB. */ if (_next_hop_cache.register_nexthop(nexthop)) return true; /* ** We don't have an answer so make a request of the rib. */ _next_hop_rib_request.register_nexthop(nexthop, net_from_route, requester); return false; } template void NextHopResolver::deregister_nexthop(A nexthop, IPNet net_from_route, NhLookupTable *requester) { debug_msg("E nexthop %s net %s requested %p\n", nexthop.str().c_str(), net_from_route.str().c_str(), requester); /* ** We haven't hooked up with a rib so just return. */ if ("" == _ribname) return; /* ** Somewhere there has to be a record of this registration. It is ** either in the "NextHopCache" or "NextHopRibRequest". Check ** first in the "NextHopCache". */ bool last; A addr; uint32_t prefix_len; if (_next_hop_cache.deregister_nexthop(nexthop, last, addr, prefix_len)) { if (last) { /* ** We no longer have any interest in this entry. */ _next_hop_rib_request.deregister_from_rib(addr, prefix_len); } return; } if (!_next_hop_rib_request.deregister_nexthop(nexthop, net_from_route, requester)) XLOG_FATAL("Unknown nexthop %s", nexthop.str().c_str()); } template bool NextHopResolver::lookup(const A nexthop, bool& resolvable, uint32_t& metric) const { debug_msg("nexthop %s\n", nexthop.str().c_str()); /* ** We haven't hooked up with a rib so just return. */ if ("" == _ribname) { resolvable = true; metric = 1; return true; } /* ** If we are being called by the decision code this next hop must ** be in our cache. */ if (_next_hop_cache.lookup_by_nexthop(nexthop, resolvable, metric)) return true; /* ** There is a chance that this entry was in our cache but was ** invalidated by the RIB. In which case we are currently ** rerequesting the metrics. We will attempt to return the old ** values. As soon as the new values arrive we will call the hook ** in decision. */ if (_next_hop_rib_request.lookup(nexthop, resolvable, metric)) { XLOG_INFO("FYI: Stale metrics supplied"); return true; } return false; } template bool NextHopResolver::rib_client_route_info_changed(const A& addr, const uint32_t& prefix_len, const A& nexthop, const uint32_t& metric) { debug_msg("addr %s prefix_len %u nexthop %s metric %u\n", addr.str().c_str(), XORP_UINT_CAST(prefix_len), nexthop.str().c_str(), XORP_UINT_CAST(metric)); PROFILE(XLOG_TRACE(_bgp.profile().enabled(trace_nexthop_resolution), "addr %s prefix_len %u nexthop %s metric %u\n", addr.str().c_str(), XORP_UINT_CAST(prefix_len), nexthop.str().c_str(), XORP_UINT_CAST(metric))); UNUSED(nexthop); /* ** Change the entries in the cache and make an upcall notifying ** the decision code that the metrics have changed. */ map m = _next_hop_cache.change_entry(addr, prefix_len, metric); typename map::iterator i; for (i = m.begin(); i != m.end(); i++) next_hop_changed(i->first); return true; } template bool NextHopResolver::rib_client_route_info_invalid(const A& addr, const uint32_t& prefix_len) { debug_msg("addr %s prefix_len %u\n", addr.str().c_str(), XORP_UINT_CAST(prefix_len)); PROFILE(XLOG_TRACE(_bgp.profile().enabled(trace_nexthop_resolution), "addr %s prefix_len %u\n", addr.str().c_str(), XORP_UINT_CAST(prefix_len))); /* ** This is a callback from the RIB telling us that this ** addr/prefix_len should no longer be used to satisfy nexthop ** requests. ** 1) As a sanity check verify that we have this add/prefix_len in our ** cache. ** 2) Delete the entry from our cache. ** 3) Reregister interest for this nexthop. It may be the case ** that we have an entry in the nexthop cache that satisfies this ** nexthop. This eventuality will be dealt with in the reregister ** code. Save the old metrics in case a lookup comes in while we ** are waiting for the new values. ** ** Additionally we have to deal with a timing problem. ** 1) It is possible that we register interest for a nexthop while ** the registration is in progress an invalidate is generated by the ** RIB. The invalidate can arrive before the the registration ** completes. Plus the registration will fail. This should be ** dealt with in premature_invalid. ** 2) BGP may be de-registering interest when an invalid is ** generated by the RIB. The de-register will fail as the RIB no ** longer has any knowledge about the nexthop. The invalidate ** arrives after the failed de-registration. This should be dealt ** with in tardy invalid. */ bool resolvable; uint32_t metric; if (!_next_hop_cache.lookup_by_addr(addr, prefix_len, resolvable, metric)) { if (_next_hop_rib_request.premature_invalid(addr, prefix_len)) { return true; } if (_next_hop_rib_request.tardy_invalid(addr, prefix_len)) { return true; } XLOG_WARNING("address not found in next hop cache: %s/%u", addr.str().c_str(), XORP_UINT_CAST(prefix_len)); return false; } map m = _next_hop_cache.delete_entry(addr, prefix_len); typename map::iterator i; for (i = m.begin(); i != m.end(); i++) _next_hop_rib_request.reregister_nexthop(i->first, i->second, resolvable, metric); return true; } template void NextHopResolver::next_hop_changed(A addr) { debug_msg("next hop: %s\n", addr.str().c_str()); if (_decision.empty()) XLOG_FATAL("No pointers to the decision tables."); typename list *>::iterator i; for(i = _decision.begin(); i != _decision.end(); i++) (*i)->igp_nexthop_changed(addr); } template void NextHopResolver::next_hop_changed(A addr, bool old_resolves, uint32_t old_metric) { debug_msg("next hop: %s\n", addr.str().c_str()); if (_decision.empty()) XLOG_FATAL("No pointers to the decision tables."); bool resolvable; uint32_t metric; if (!lookup(addr, resolvable, metric)) XLOG_FATAL("Could not lookup %s", addr.str().c_str()); bool changed = false; // If resolvability has changed we care. if (resolvable != old_resolves) changed = true; // Only if the nexthop resolves do we care if the metric has changed. if (resolvable && metric != old_metric) changed = true; if (changed) { typename list *>::iterator i; for(i = _decision.begin(); i != _decision.end(); i++) (*i)->igp_nexthop_changed(addr); } } /****************************************/ template NextHopCache::~NextHopCache() { /* ** Free all the allocated NextHopEntrys. */ PrefixIterator i; for (i = _next_hop_by_prefix.begin(); i != _next_hop_by_prefix.end(); i++){ NextHopEntry *entry = i.payload(); delete entry; } } template void NextHopCache::add_entry(A addr, A nexthop, int prefix_len, int real_prefix_len, bool resolvable, uint32_t metric) { debug_msg("addr %s prefix_len %d real prefix_len %d\n", addr.str().c_str(), prefix_len, real_prefix_len); XLOG_ASSERT(addr == nexthop.mask_by_prefix_len(prefix_len)); PrefixEntry *entry = new PrefixEntry; entry->_address = addr; #ifdef USE_NEXTHOP entry->_nexthop = nexthop; #endif // Don't create any map entries. // entry->_nexthop_references[nexthop] = 0; // Initial Ref count of 0 entry->_prefix_len = prefix_len; entry->_real_prefix_len = real_prefix_len; entry->_resolvable = resolvable; entry->_metric = metric; // This entry must not already be in the trie. XLOG_ASSERT(_next_hop_by_prefix.lookup_node(IPNet(addr, prefix_len)) == _next_hop_by_prefix.end()); // This entry must not already be in the trie. RealPrefixIterator rpi = _next_hop_by_real_prefix. lookup_node(IPNet(addr, real_prefix_len)); if (rpi == _next_hop_by_real_prefix.end()) { RealPrefixEntry rpe; rpe.insert(entry); _next_hop_by_real_prefix.insert(IPNet(addr, real_prefix_len), rpe); } else { RealPrefixEntry *rpep = &(rpi.payload()); XLOG_ASSERT(0 == rpe_to_pe(*rpep, addr, real_prefix_len)); rpep->insert(entry); } _next_hop_by_prefix.insert(IPNet(addr, prefix_len), entry); } template bool NextHopCache::validate_entry(A addr, A nexthop, int prefix_len, int real_prefix_len) { debug_msg("addr %s nexthop %s prefix_len %d real prefix_len %d\n", addr.str().c_str(), nexthop.str().c_str(), prefix_len, real_prefix_len); PrefixIterator pi = _next_hop_by_prefix.lookup_node(IPNet(addr, prefix_len)); XLOG_ASSERT(pi != _next_hop_by_prefix.end()); PrefixEntry *en = pi.payload(); XLOG_ASSERT(en->_address == addr); #ifdef USE_NEXTHOP XLOG_ASSERT(en->_nexthop == nexthop); #else nexthop = nexthop; #endif XLOG_ASSERT(en->_prefix_len == prefix_len); XLOG_ASSERT(en->_real_prefix_len == real_prefix_len); if (en->_nexthop_references.empty()) { delete_entry(addr, prefix_len); debug_msg("No references\n"); return false; } return true; } #if 0 template map NextHopCache::change_entry(A addr, int real_prefix_len, uint32_t metric) { RealPrefixIterator rpi = _next_hop_by_real_prefix.lookup_node(IPNet(addr, real_prefix_len)); XLOG_ASSERT(rpi != _next_hop_by_real_prefix.end()); RealPrefixEntry rpe = rpi.payload(); PrefixEntry *en = rpe_to_pe(rpe, addr, real_prefix_len); XLOG_ASSERT(en); XLOG_ASSERT(en->_address == addr); XLOG_ASSERT(en->_real_prefix_len == real_prefix_len); // Save all the next hops and reference counts that were covered // by this entry. map ret = en->_nexthop_references; // Update the metric en->_metric = metric; return ret; } #endif template map NextHopCache::change_entry(A addr, int prefix_len, uint32_t metric) { debug_msg("addr %s prefix_len %d\n", addr.str().c_str(), prefix_len); PrefixIterator pi = _next_hop_by_prefix.lookup_node(IPNet(addr, prefix_len)); XLOG_ASSERT(pi != _next_hop_by_prefix.end()); PrefixEntry *en = pi.payload(); XLOG_ASSERT(en); XLOG_ASSERT(en->_address == addr); XLOG_ASSERT(en->_prefix_len == prefix_len); // Save all the next hops and reference counts that were covered // by this entry. map ret = en->_nexthop_references; // Update the metric en->_metric = metric; return ret; } template map NextHopCache::delete_entry(A addr, int prefix_len) { debug_msg("addr %s prefix_len %d\n", addr.str().c_str(), prefix_len); PrefixIterator pi = _next_hop_by_prefix.lookup_node(IPNet(addr, prefix_len)); XLOG_ASSERT(pi != _next_hop_by_prefix.end()); PrefixEntry *en = pi.payload(); XLOG_ASSERT(en->_address == addr); XLOG_ASSERT(en->_prefix_len == prefix_len); RealPrefixIterator rpi = _next_hop_by_real_prefix.lookup_node(IPNet(addr, en->_real_prefix_len)); XLOG_ASSERT(rpi != _next_hop_by_real_prefix.end()); RealPrefixEntry *rpep = &(rpi.payload()); // These should both point at the same entry. if (rpe_to_pe_delete(*rpep, addr, en->_real_prefix_len) != en) XLOG_FATAL("Entry was not present addr %s real_prefix_len %d", addr.str().c_str(), en->_real_prefix_len); // Save all the next hops and reference counts that were covered // by this entry. map ret = en->_nexthop_references; // Delete the entry delete en; // Delete the two pointers to this entry. if (rpep->empty()) _next_hop_by_real_prefix.erase(rpi); _next_hop_by_prefix.erase(pi); return ret; } template bool NextHopCache::lookup_by_addr(A addr, int prefix_len, bool& resolvable, uint32_t& metric) const { PrefixIterator pi = _next_hop_by_prefix.lookup_node(IPNet(addr, prefix_len)); if (pi == _next_hop_by_prefix.end()) return false; NextHopEntry *en = pi.payload(); XLOG_ASSERT(en->_prefix_len == prefix_len); resolvable = en->_resolvable; metric = en->_metric; return true; } template bool NextHopCache::lookup_by_nexthop(A nexthop, bool& resolvable, uint32_t& metric) const { PrefixIterator pi = _next_hop_by_prefix.find(nexthop); if (pi == _next_hop_by_prefix.end()) return false; NextHopEntry *en = pi.payload(); /* ** We have found an entry now check that this next hop is recorded ** in this entry. */ if (en->_nexthop_references.find(nexthop) == en->_nexthop_references.end()) return false; resolvable = en->_resolvable; metric = en->_metric; return true; } template bool NextHopCache::lookup_by_nexthop_without_entry(A nexthop, bool& resolvable, uint32_t& metric) const { PrefixIterator pi = _next_hop_by_prefix.find(nexthop); if (pi == _next_hop_by_prefix.end()) return false; NextHopEntry *en = pi.payload(); resolvable = en->_resolvable; metric = en->_metric; return true; } template bool NextHopCache::register_nexthop(A nexthop, int ref_cnt_incr) { debug_msg("C nexthop %s reference count incr %d\n", nexthop.str().c_str(), ref_cnt_incr); XLOG_ASSERT(0 != ref_cnt_incr); /* ** Look for a covering entry. If there is none just return false. */ PrefixIterator pi = _next_hop_by_prefix.find(nexthop); if (pi == _next_hop_by_prefix.end()) return false; debug_msg("nexthop %s covered\n", nexthop.str().c_str()); NextHopEntry *en = pi.payload(); /* ** This may be a new nexthop in which case create a new map ** entry. If we already know about this nexthop then just ** increment the reference count. */ if (en->_nexthop_references.find(nexthop) == en->_nexthop_references.end()) en->_nexthop_references[nexthop] = ref_cnt_incr; else en->_nexthop_references[nexthop] += ref_cnt_incr; debug_msg("C nexthop %s reference count %d\n", nexthop.str().c_str(), en->_nexthop_references[nexthop]); return true; } template bool NextHopCache::deregister_nexthop(A nexthop, bool& last, A& addr, uint32_t& prefix_len) { debug_msg("C nexthop %s\n", nexthop.str().c_str()); PrefixIterator pi = _next_hop_by_prefix.find(nexthop); if (pi == _next_hop_by_prefix.end()) return false; NextHopEntry *en = pi.payload(); typename map::iterator nhp = en->_nexthop_references.find(nexthop); if (nhp == en->_nexthop_references.end()) return false; debug_msg("C nexthop %s reference count %d\n", nexthop.str().c_str(), en->_nexthop_references[nexthop]); if (0 == --en->_nexthop_references[nexthop]) { en->_nexthop_references.erase(nhp); if (en->_nexthop_references.empty()) { last = true; addr = en->_address; prefix_len = en->_prefix_len; delete_entry(en->_address, en->_prefix_len); return true; } } last = false; return true; } template typename NextHopCache::PrefixEntry * NextHopCache::rpe_to_pe(const RealPrefixEntry& rpe, A addr, int real_prefix_len) const { debug_msg("addr %s real prefix_len %d\n", addr.str().c_str(), real_prefix_len); typename RealPrefixEntry::const_iterator si; #ifdef DEBUG_LOGGING for (si = rpe.begin(); si != rpe.end(); si++) debug_msg("addr %s prefix_len %d real prefix_len %d\n", (*si)->_address.str().c_str(), (*si)->_prefix_len, (*si)->_real_prefix_len); #endif for (si = rpe.begin(); si != rpe.end(); si++) if ((*si)->_real_prefix_len == real_prefix_len && (*si)->_address == addr) return *si; return 0; } template typename NextHopCache::PrefixEntry * NextHopCache::rpe_to_pe_delete(RealPrefixEntry& rpe, A addr, int real_prefix_len) { debug_msg("addr %s real prefix_len %d\n", addr.str().c_str(), real_prefix_len); typename RealPrefixEntry::iterator si; #ifdef DEBUG_LOGGING for (si = rpe.begin(); si != rpe.end(); si++) debug_msg("addr %s prefix_len %d real prefix_len %d\n", (*si)->_address.str().c_str(), (*si)->_prefix_len, (*si)->_real_prefix_len); #endif for (si = rpe.begin(); si != rpe.end(); si++) if ((*si)->_real_prefix_len == real_prefix_len && (*si)->_address == addr) { PrefixEntry *pi = *si; rpe.erase(si); return pi; } return 0; } /****************************************/ template NextHopRibRequest::NextHopRibRequest(XrlStdRouter *xrl_router, NextHopResolver& next_hop_resolver, NextHopCache& next_hop_cache, BGPMain& bgp) : _xrl_router(xrl_router), _next_hop_resolver(next_hop_resolver), _next_hop_cache(next_hop_cache), _bgp(bgp), _busy(false), _invalid(false), _tardy_invalid(false) { } template NextHopRibRequest::~NextHopRibRequest() { /* ** Free all the outstanding queue entries. */ for_each(_queue.begin(), _queue.end(), &NextHopRibRequest::zapper); } template void NextHopRibRequest::register_nexthop(A nexthop, IPNet net_from_route, NhLookupTable *requester) { debug_msg("R nexthop %s net %s requested %p\n", nexthop.str().c_str(), net_from_route.str().c_str(), requester); /* ** Make sure that we are not already waiting for a response for ** this sucker. */ typename list *>::iterator i; for (i = _queue.begin(); i != _queue.end(); i++) { RibRegisterQueueEntry *rr = dynamic_cast *>(*i); if (rr && rr->nexthop() == nexthop) { rr->register_nexthop(net_from_route, requester); debug_msg("This registration is already queued\n"); return; } } debug_msg("Queue registration\n"); /* ** Construct a request. */ RibRegisterQueueEntry *rr = new RibRegisterQueueEntry(nexthop, net_from_route, requester); /* ** Add the request to the queue. */ _queue.push_back(rr); /* ** We allow only one outstanding request to the RIB. The "_busy" ** flag marks if there is currently an outstanding request. */ if (!_busy) { send_next_request(); } } template void NextHopRibRequest::deregister_from_rib(const A& base_addr, uint32_t prefix_len) { debug_msg("R addr %s prefix_len %d\n", base_addr.str().c_str(), prefix_len); /* ** Its possible that we are already trying to deregister for this ** addr/prefix. In which case just return. */ typename list *>::iterator i = _queue.begin(); for ( ;i != _queue.end(); i++) { RibDeregisterQueueEntry *dreg = dynamic_cast *>(*i); if (dreg) { if (dreg->base_addr() == base_addr && dreg->prefix_len() == prefix_len) { debug_msg("This deregistration is already queued\n"); return; } } } debug_msg("Queue deregistration\n"); /* ** Construct a request. */ RibDeregisterQueueEntry *rr = new RibDeregisterQueueEntry(base_addr, prefix_len); /* ** Add the request to the queue. */ _queue.push_back(rr); /* ** We allow only one outstanding request to the RIB. The "_busy" ** flag marks if there is currently an outstanding request. */ if (!_busy) { send_next_request(); } } template<> void NextHopRibRequest::register_interest(IPv4 nexthop) { debug_msg("nexthop %s\n", nexthop.str().c_str()); PROFILE(XLOG_TRACE(_bgp.profile().enabled(trace_nexthop_resolution), "nexthop %s\n", nexthop.str().c_str())); if (0 == _xrl_router) // The test code sets _xrl_router to zero return; XrlRibV0p1Client rib(_xrl_router); rib.send_register_interest4(_ribname.c_str(), _xrl_router->name(), nexthop, ::callback(this, &NextHopRibRequest::register_interest_response, nexthop, c_format("nexthop: %s", nexthop.str().c_str()))); } template void NextHopRibRequest::register_interest_response(const XrlError& error, const bool *resolves, const A *addr, const uint32_t *prefix_len, const uint32_t *real_prefix_len, const A *actual_nexthop, const uint32_t *metric, const A /*nexthop_interest*/, const string comment) { UNUSED(actual_nexthop); /* ** We attempted to register a next hop with the RIB and an error ** ocurred. Its not clear that we should continue. */ switch (error.error_code()) { case OKAY: break; case REPLY_TIMED_OUT: XLOG_FATAL("callback: Use a reliable transport %s %s", comment.c_str(), error.str().c_str()); break; case RESOLVE_FAILED: case SEND_FAILED: case SEND_FAILED_TRANSIENT: case NO_SUCH_METHOD: case BAD_ARGS: case COMMAND_FAILED: case INTERNAL_ERROR: XLOG_FATAL("callback: %s %s", comment.c_str(), error.str().c_str()); break; case NO_FINDER: _bgp.finder_death(__FILE__, __LINE__); break; } debug_msg("Error %s resolves %d addr %s " "prefix_len %u real prefix_len %u actual nexthop %s metric %d %s\n", error.str().c_str(), *resolves, addr->str().c_str(), XORP_UINT_CAST(*prefix_len), XORP_UINT_CAST(*real_prefix_len), actual_nexthop->str().c_str(), XORP_UINT_CAST(*metric), comment.c_str()); PROFILE(XLOG_TRACE(_bgp.profile().enabled(trace_nexthop_resolution), "Error %s resolves %d addr %s " "prefix_len %u real prefix_len %u " "actual nexthop %s metric %d %s\n", error.str().c_str(), *resolves, addr->str().c_str(), XORP_UINT_CAST(*prefix_len), XORP_UINT_CAST(*real_prefix_len), actual_nexthop->str().c_str(), XORP_UINT_CAST(*metric), comment.c_str())); XLOG_ASSERT(*real_prefix_len <= A::addr_bitlen()); /* ** Make sure that there is something on our queue. */ XLOG_ASSERT(!_queue.empty()); RibRegisterQueueEntry *first_rr = dynamic_cast *>(_queue.front()); /* ** Make sure the front entry is a register rather than a deregister */ XLOG_ASSERT(first_rr != NULL); /* ** At this point I would really like to directly compare the ** nexthop that we actually registered interest for with the ** nexthop that was returned. Unfortunately what is returned is ** a base address for the covered region. So the comparison has to ** be masked by the prefix_len that is returned. */ XLOG_ASSERT(IPNet(*addr, *prefix_len) == IPNet(first_rr->nexthop(), *prefix_len)); /* ** It is possible that we register interest in a nexthop and while ** we were are waiting for the response the answer becomes invalid. ** Unfortunately the invalid can arrive before the response. If we ** have previously received an invalidate and it was not in our ** cache then it was saved. If the invalid and response match then ** register again. */ if (_invalid) { XLOG_ASSERT(*addr == _invalid_net.masked_addr() && *prefix_len == _invalid_net.prefix_len()); _invalid = false; send_next_request(); return; } /* ** We have a lot to do here. This response may have satisfied ** other requests on the queue. Also there are two reasons for ** making requests. The simple case is that the next hop table has ** called down to us. In which case we should run the call ** backs. The other reason is that a previous entry has become ** invalid in which case we need to call the next hop changed hook ** in the decision table. It is possible (legal) for an entry to be here ** for both reasons. ** ** The simplest strategy is to insert this result into the ** NextHopCache. Then traverse the queue removing all entries that ** are satisfied by the NextHopCache. */ A first_nexthop = first_rr->nexthop(); _next_hop_cache.add_entry(*addr, first_nexthop, *prefix_len, *real_prefix_len, *resolves, *metric); /* ** As soon as we come across an entry in the queue that we ** can't lookup in the cache we bail. It may be the case that ** there are other entries later in the queue that may ** resolve. Don't worry about it we will get there eventually. */ typename list *>::iterator i; i = _queue.begin(); while (i != _queue.end()) { bool lookup_succeeded; uint32_t m; RibRegisterQueueEntry *rr = dynamic_cast *>(*i); if (rr) { if (_next_hop_cache.lookup_by_nexthop_without_entry(rr->nexthop(), lookup_succeeded, m)) { XLOG_ASSERT(rr->new_register() || rr->reregister()); /* ** See if this request was caused by a downcall from ** the next hop table. */ if (rr->new_register()) { NHRequest* request_data = &rr->requests(); /* ** If nobody is interested then don't register or run ** the callbacks. */ if (0 != request_data->requests()) { _next_hop_cache.register_nexthop(rr->nexthop(), request_data->requests()); typename set *>::const_iterator req_iter; for (req_iter = request_data->requesters().begin(); req_iter != request_data->requesters().end(); req_iter++) { NhLookupTable *requester = (*req_iter); requester->RIB_lookup_done(rr->nexthop(), request_data->request_nets(requester), lookup_succeeded); } } } /* ** See if this request was caused by an upcall from the ** RIB. If it was then notify decision that this next hop ** has changed. */ if (rr->reregister() && 0 != rr->ref_cnt()) { _next_hop_cache.register_nexthop(rr->nexthop(), rr->ref_cnt()); /* ** Start the upcall with the old metrics. Only if the ** state has changed will the upcall be made. */ _next_hop_resolver.next_hop_changed(rr->nexthop(), rr->resolvable(), rr->metric()); } delete rr; i = _queue.erase(i); } else { break; } } else { //skip the deregister i++; } } /* ** A NextHopResolver::register_nexthop caused us to make a request ** of the RIB. In the meantime a NextHopResolver::deregister_nexthop ** call took place. We removed the reference but couldn't stop the ** call to RIB. It may be that this response may satisfy other ** outstanding queries in the queue. If it hasn't then the entry ** will be invalid so deregister interest with the RIB. */ if (!_next_hop_cache.validate_entry(*addr, first_nexthop, *prefix_len, *real_prefix_len)) { deregister_from_rib(*addr, *prefix_len); } /* ** There are entries left on the queue, so, fire off another request. */ send_next_request(); } template void NextHopRibRequest::send_next_request() { if (_queue.empty()) { _busy = false; return; } _busy = true; RibRegisterQueueEntry *rr = dynamic_cast *>(_queue.front()); if (rr) { register_interest(rr->nexthop()); return; } RibDeregisterQueueEntry *rd = dynamic_cast *>(_queue.front()); if (rd) { deregister_interest(rd->base_addr(), rd->prefix_len()); return; } XLOG_UNREACHABLE(); } template bool NextHopRibRequest::premature_invalid(const A& addr, const uint32_t& prefix_len) { if (!_busy) return false; /* ** An invalid has been received for an entry that we don't have in ** our cache. * 1) The current request may have generated an invalid. Extremely * irritating, we make a request and before receiving the response * we get an invalid from the RIB. In which case set the _invalid * flag and _invalid_net and all should be well. * 2) We receive an invalid for an entry that we are no longer * interested in but the deregister is still in the request queue. In * which case remove the deregister from the queue and continue. */ XLOG_ASSERT(!_queue.empty()); RibRegisterQueueEntry *first_rr = dynamic_cast *>(_queue.front()); if (first_rr && (IPNet(addr, prefix_len) == IPNet(first_rr->nexthop(), prefix_len))) { XLOG_ASSERT(_busy); XLOG_ASSERT(!_invalid); _invalid = true; _invalid_net = IPNet(addr, prefix_len); return true; } typename list *>::iterator i = _queue.begin(); for ( ;i != _queue.end(); i++) { RibDeregisterQueueEntry *dreg = dynamic_cast *>(*i); if (dreg) { if (dreg->base_addr() == addr && dreg->prefix_len() == prefix_len) { /* ** Don't erase the first entry in the queue this is an ** ongoing transaction. */ XLOG_INFO("invalid addr %s prefix len %u matched delete %s", cstring(addr), XORP_UINT_CAST(prefix_len), (i == _queue.begin()) ? "front" : "not front"); if (i == _queue.begin()) { XLOG_ASSERT(_busy); XLOG_ASSERT(!_invalid); _invalid = true; _invalid_net = IPNet(addr, prefix_len); } else { delete dreg; _queue.erase(i); } return true; } } } return false; } template bool NextHopRibRequest::tardy_invalid(const A& addr, const uint32_t& prefix_len) { if (_tardy_invalid) { _tardy_invalid = false; if (addr != _tardy_invalid_net.masked_addr() || prefix_len != _tardy_invalid_net.prefix_len()) XLOG_FATAL("Invalidate does not match previous failed " "de-registration addr %s prefix len %u", cstring(addr), XORP_UINT_CAST(prefix_len)); return true; } return false; } template bool NextHopRibRequest::deregister_nexthop(A nexthop, IPNet net_from_route, NhLookupTable *requester) { debug_msg("nexthop %s net %s requested %p\n", nexthop.str().c_str(), net_from_route.str().c_str(), requester); typename list *>::iterator i; /* ** The deregister may mean that there are no more interested parties. ** It may therefore be tempting to remove this entry from the ** queue DON'T as a request to the RIB might be in progress. The ** register_interest_response will tidy up. Worst case we ** occasionally make a request for a next hop for which there are ** no requesters. */ for (i = _queue.begin(); i != _queue.end(); i++) { RibRegisterQueueEntry *rr = dynamic_cast *>(*i); if (rr && rr->nexthop() == nexthop) { if (!rr->deregister_nexthop(net_from_route, requester)) XLOG_WARNING("Removing request %p probably failed", requester); return true; } } return false; } template void NextHopRibRequest::reregister_nexthop(A nexthop, uint32_t ref_cnt, bool resolvable, uint32_t metric) { debug_msg(" nexthop %s ref_cnf %u resolvable %d metric %u\n", nexthop.str().c_str(), XORP_UINT_CAST(ref_cnt), resolvable, XORP_UINT_CAST(metric)); /* ** It may be the case that we already have an entry that covers ** this next hop. */ if (_next_hop_cache.register_nexthop(nexthop, ref_cnt)) { bool new_resolvable; uint32_t new_metric; if (!_next_hop_cache.lookup_by_nexthop(nexthop, new_resolvable, new_metric)) XLOG_FATAL("This nexthop %s must be in the cache", nexthop.str().c_str()); /* ** The entry is already covered but the metrics may have ** changed. If the metrics have changed we need to make an ** upcall to the BGP decision process. */ _next_hop_resolver.next_hop_changed(nexthop, resolvable, metric); return; } /* ** Make sure that we are not already waiting for a response for ** this sucker. */ typename list *>::iterator i; for (i = _queue.begin(); i != _queue.end(); i++) { RibRegisterQueueEntry *rr = dynamic_cast *>(*i); if (rr && rr->nexthop() == nexthop) { rr->reregister_nexthop(ref_cnt, resolvable, metric); return; } } /* ** Construct a request. */ RibRegisterQueueEntry *rr = new RibRegisterQueueEntry(nexthop, ref_cnt, resolvable, metric); /* ** Add the request to the queue. */ _queue.push_back(rr); /* ** We allow only one outstanding request to the RIB. The "_busy" ** flag marks if there is currently an outstanding request. */ if (!_busy) { send_next_request(); } } template bool NextHopRibRequest::lookup(const A& nexthop, bool& resolvable, uint32_t& metric) const { /* ** Make sure that we are not already waiting for a response for ** this sucker. */ typename list *>::const_iterator i; for (i = _queue.begin(); i != _queue.end(); i++) { RibRegisterQueueEntry *rr = dynamic_cast *>(*i); if (rr && rr->reregister() && rr->nexthop() == nexthop) { resolvable = rr->resolvable(); metric = rr->metric(); debug_msg("nexthop %s resolvable %d metric %u\n", nexthop.str().c_str(), resolvable, XORP_UINT_CAST(metric)); return true; } } debug_msg("nexthop %s not resolvable\n", nexthop.str().c_str()); return false; } template<> void NextHopRibRequest::deregister_interest(IPv4 addr, uint32_t prefix_len) { debug_msg("addr %s/%u\n", addr.str().c_str(), XORP_UINT_CAST(prefix_len)); PROFILE(XLOG_TRACE(_bgp.profile().enabled(trace_nexthop_resolution), "addr %s/%u\n", addr.str().c_str(), XORP_UINT_CAST(prefix_len))); if (0 == _xrl_router) // The test code sets _xrl_router to zero return; XrlRibV0p1Client rib(_xrl_router); rib.send_deregister_interest4(_ribname.c_str(), _xrl_router->name(), addr, prefix_len, ::callback(this,&NextHopRibRequest::deregister_interest_response, addr, prefix_len, c_format("deregister_from_rib: addr %s/%u", addr.str().c_str(), XORP_UINT_CAST(prefix_len)))); } template void NextHopRibRequest::deregister_interest_response(const XrlError& error, A addr, uint32_t prefix_len, string comment) { //Check that this answer is for the question on the front of the queue XLOG_ASSERT(!_queue.empty()); RibDeregisterQueueEntry *rd = dynamic_cast *>(_queue.front()); XLOG_ASSERT(rd != NULL); XLOG_ASSERT(addr == rd->base_addr()); XLOG_ASSERT(prefix_len == rd->prefix_len()); debug_msg("%s %s\n", comment.c_str(), error.str().c_str()); switch (error.error_code()) { case OKAY: break; case REPLY_TIMED_OUT: XLOG_FATAL("callback: Use a reliable transport %s %s", comment.c_str(), error.str().c_str()); break; case NO_FINDER: _bgp.finder_death(__FILE__, __LINE__); break; case RESOLVE_FAILED: //A NO_FINDER or FATAL_TRANSPORT_ERROR error is always //unrecoverable. A RESOLVE_FAILED error when it had //previously been successful is also unrecoverable. while (!_queue.empty()) { delete _queue.front(); _queue.pop_front(); } return; break; case SEND_FAILED: XLOG_FATAL("callback: %s %s", comment.c_str(), error.str().c_str()); break; case SEND_FAILED_TRANSIENT: case NO_SUCH_METHOD: case BAD_ARGS: case INTERNAL_ERROR: XLOG_FATAL("callback: %s %s", comment.c_str(), error.str().c_str()); break; case COMMAND_FAILED: /* ** If it possible that we were de-registering interest in this ** nexthop when it became invalid. In this case the command ** may fail as the RIB has already told us it is invalid. */ if (_invalid) { XLOG_ASSERT(addr == _invalid_net.masked_addr() && prefix_len == _invalid_net.prefix_len()); _invalid = false; } else { // The de-registration may have failed because the // RIB has already sent as an invalid for this // registration. The invalid however has not yet been // received by BGP. So rather than generate a warning here // wait until we receive the next invalid. If it does not // match this net generate an error. _tardy_invalid = true; _tardy_invalid_net = IPNet(addr, prefix_len); } break; } //remove this request from the queue delete rd; _queue.pop_front(); //if there's anything else queued, send it. if (_queue.empty()) _busy = false; else send_next_request(); return; } /****************************************/ template NHRequest::NHRequest() : _request_total(0) { } template NHRequest::NHRequest(IPNet net, NhLookupTable *requester) : _request_total(0) { add_request(net, requester); } template void NHRequest::add_request(IPNet net, NhLookupTable *requester) { _request_total++; if (_request_map.find(requester) == _request_map.end()) { _requesters.insert(requester); _request_map[requester].insert(net); } else { _request_map[requester].insert(net); } } template bool NHRequest::remove_request(IPNet net, NhLookupTable *requester) { typename map *, multiset > >::iterator i; i = _request_map.find(requester); if (i == _request_map.end()) return false; multiset >* nets = &(i->second); typename multiset >::iterator nets_iter = nets->find(net); if (nets_iter == nets->end()) return false; nets->erase(nets_iter); _request_total--; return true; } template const set >& NHRequest::request_nets(NhLookupTable* requester) const { typename map *, multiset > >::const_iterator i; i = _request_map.find(requester); assert(i != _request_map.end()); multiset > m = i->second; set > result; typename multiset >::iterator j; for (j = m.begin(); j != m.end(); j++) result.insert(*j); // Internally the set of networks is held as a multiset so we can // correctly handle multiple adds and deletes of the same // network. The caller is expecting to see the winning set. _answer[requester] = result; typename map *, set > >::const_iterator k; k = _answer.find(requester); XLOG_ASSERT(k != _answer.end()); return k->second; } //force these templates to be built template class NextHopResolver; #ifdef HAVE_IPV6 /* IPv6 stuff */ template<> void NextHopRibRequest::register_interest(IPv6 nexthop) { debug_msg("nexthop %s\n", nexthop.str().c_str()); PROFILE(XLOG_TRACE(_bgp.profile().enabled(trace_nexthop_resolution), "nexthop %s\n", nexthop.str().c_str())); if (0 == _xrl_router) // The test code sets _xrl_router to zero return; XrlRibV0p1Client rib(_xrl_router); rib.send_register_interest6(_ribname.c_str(), _xrl_router->name(), nexthop, ::callback(this, &NextHopRibRequest::register_interest_response, nexthop, c_format("nexthop: %s", nexthop.str().c_str()))); } template<> void NextHopRibRequest::deregister_interest(IPv6 addr, uint32_t prefix_len) { debug_msg("addr %s/%u\n", addr.str().c_str(), XORP_UINT_CAST(prefix_len)); PROFILE(XLOG_TRACE(_bgp.profile().enabled(trace_nexthop_resolution), "addr %s/%u\n", addr.str().c_str(), XORP_UINT_CAST(prefix_len))); if (0 == _xrl_router) // The test code sets _xrl_router to zero return; XrlRibV0p1Client rib(_xrl_router); rib.send_deregister_interest6(_ribname.c_str(), _xrl_router->name(), addr, prefix_len, ::callback(this,&NextHopRibRequest::deregister_interest_response, addr, prefix_len, c_format("deregister_from_rib: addr %s/%u", addr.str().c_str(), XORP_UINT_CAST(prefix_len)))); } template class NextHopResolver; #endif //ipv6 xorp/bgp/route_table_debug.cc0000664000076400007640000001563111421137511016414 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net //#define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #include "bgp_module.h" #include "libxorp/xlog.h" #include "route_table_debug.hh" template DebugTable::DebugTable(string table_name, BGPRouteTable *parent_table) : BGPRouteTable("DebugTable-" + table_name, SAFI_UNICAST) { this->_parent = parent_table; _print_tablename = false; _msgs = 0; _ofile = NULL; _get_on_wakeup = false; _set_is_winner = false; } template DebugTable::~DebugTable() { if (_ofile != NULL && _close_on_delete) fclose(_ofile); } template void DebugTable::print_route(const SubnetRoute& route, FPAListRef palist) const { palist->canonicalize(); string s; s = "SubnetRoute:\n"; s += " Net: " + route.net().str() + "\n"; s += " PAList: " + palist->str() + "\n"; fprintf(_ofile, "%s", s.c_str()); } template int DebugTable::add_route(InternalMessage &rtmsg, BGPRouteTable *caller) { assert(caller == this->_parent); if (_print_tablename) fprintf(_ofile, "[** TABLE %s **]\n", this->tablename().c_str()); fprintf(_ofile, "[ADD]\n"); if (rtmsg.changed()) fprintf(_ofile, "CHANGED flag is set\n"); if (rtmsg.push()) fprintf(_ofile, "PUSH flag is set\n"); // fprintf(_ofile, "%s\n", rtmsg.route()->str().c_str()); print_route(*(rtmsg.route()), rtmsg.attributes()); fflush(_ofile); if (_set_is_winner) rtmsg.route()->set_is_winner(1); if (rtmsg.copied()) { rtmsg.inactivate(); } return _canned_response; } template int DebugTable::replace_route(InternalMessage &old_rtmsg, InternalMessage &new_rtmsg, BGPRouteTable *caller) { assert(caller == this->_parent); if (_print_tablename) fprintf(_ofile, "[** TABLE %s **]\n", this->tablename().c_str()); fprintf(_ofile, "[REPLACE]\n"); if (old_rtmsg.changed()) fprintf(_ofile, "CHANGED flag is set\n"); if (old_rtmsg.push()) fprintf(_ofile, "PUSH flag is set\n"); print_route(*(old_rtmsg.route()), old_rtmsg.attributes()); if (new_rtmsg.changed()) fprintf(_ofile, "CHANGED flag is set\n"); if (new_rtmsg.push()) fprintf(_ofile, "PUSH flag is set\n"); print_route(*(new_rtmsg.route()), new_rtmsg.attributes()); fflush(_ofile); if (_set_is_winner) new_rtmsg.route()->set_is_winner(1); if (new_rtmsg.copied()) { new_rtmsg.inactivate(); } if (old_rtmsg.copied()) { old_rtmsg.inactivate(); } return _canned_response; } template int DebugTable::delete_route(InternalMessage &rtmsg, BGPRouteTable *caller) { assert(caller == this->_parent); if (_print_tablename) fprintf(_ofile, "[** TABLE %s **]\n", this->tablename().c_str()); fprintf(_ofile, "[DELETE]\n"); if (rtmsg.changed()) fprintf(_ofile, "CHANGED flag is set\n"); if (rtmsg.push()) fprintf(_ofile, "PUSH flag is set\n"); print_route(*(rtmsg.route()), rtmsg.attributes()); fflush(_ofile); if (rtmsg.copied()) { rtmsg.inactivate(); } return 0; } template int DebugTable::push(BGPRouteTable *caller) { assert(caller == this->_parent); if (_print_tablename) fprintf(_ofile, "[** TABLE %s **]\n", this->tablename().c_str()); fprintf(_ofile, "[PUSH]\n"); return 0; } template int DebugTable::route_dump(InternalMessage &rtmsg, BGPRouteTable *caller, const PeerHandler */*peer*/) { assert(caller == this->_parent); if (_print_tablename) fprintf(_ofile, "[** TABLE %s **]\n", this->tablename().c_str()); fprintf(_ofile, "[DUMP]\n"); fprintf(_ofile, "%s\n", rtmsg.route()->str().c_str()); fflush(_ofile); if (rtmsg.copied()) { rtmsg.inactivate(); } return 0; } template const SubnetRoute* DebugTable::lookup_route(const IPNet &net, uint32_t& genid, FPAListRef& pa_list) const { return this->_parent->lookup_route(net, genid, pa_list); } template void DebugTable::route_used(const SubnetRoute* rt, bool in_use){ this->_parent->route_used(rt, in_use); } template void DebugTable::wakeup() { if (_get_on_wakeup) while (this->_parent->get_next_message(this)) {} } template string DebugTable::str() const { string s = "DebugTable" + this->tablename(); return s; } /* mechanisms to implement flow control in the output plumbing */ template void DebugTable::output_state(bool busy, BGPRouteTable */*next_table*/) { if (busy) fprintf(_ofile, "[OUTPUT_STATE: BUSY]\n"); else fprintf(_ofile, "[OUTPUT_STATE: IDLE]\n"); } template bool DebugTable::get_next_message(BGPRouteTable */*next_table*/) { fprintf(_ofile, "[GET_NEXT_MESSAGE]: "); if (_msgs > 0) { fprintf(_ofile, " RETURNING TRUE\n"); _msgs--; return true; } else { fprintf(_ofile, " RETURNING FALSE\n"); return false ; } } template void DebugTable::peering_went_down(const PeerHandler *peer, uint32_t genid, BGPRouteTable *caller) { XLOG_ASSERT(this->_parent == caller); UNUSED(genid); UNUSED(peer); } template void DebugTable::peering_down_complete(const PeerHandler *peer, uint32_t genid, BGPRouteTable *caller) { XLOG_ASSERT(this->_parent == caller); UNUSED(genid); UNUSED(peer); } template bool DebugTable::set_output_file(const string& filename) { _ofile = fopen(filename.c_str(), "w"); if (_ofile == NULL) { XLOG_FATAL("Failed to open %s for writing %s", filename.c_str(), strerror(errno)); return false; } _close_on_delete = true; return true; } template void DebugTable::set_output_file(FILE *file) { _close_on_delete = false; _ofile = file; } template void DebugTable::write_separator() const { fprintf(_ofile, "[separator]-------------------------------------\n"); fflush(_ofile); } template void DebugTable::write_comment(const string& s) const { fprintf(_ofile, "[comment] %s\n", s.c_str()); fflush(_ofile); } template class DebugTable; xorp/bgp/parameter.hh0000664000076400007640000002051311540224217014730 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/bgp/parameter.hh,v 1.27 2008/10/02 21:56:16 bms Exp $ #ifndef __BGP_PARAMETER_HH__ #define __BGP_PARAMETER_HH__ // address family assignments from // http:// www.iana.org/assignments/address-family-numbers // not a complete list // Only use the following two defines in packet decode routines. #define AFI_IPV4_VAL 1 #define AFI_IPV6_VAL 2 enum Afi { AFI_IPV4 = AFI_IPV4_VAL, AFI_IPV6 = AFI_IPV6_VAL }; // sub-address family assignments from // RFC 2858 // NLRI = Network Layer Reachability Information // Only use the following two defines in packet decode routines. #define SAFI_UNICAST_VAL 1 #define SAFI_MULTICAST_VAL 2 enum Safi { SAFI_UNICAST = SAFI_UNICAST_VAL, SAFI_MULTICAST = SAFI_MULTICAST_VAL, }; inline const char * pretty_string_safi(Safi safi) { switch(safi) { case SAFI_UNICAST: return "Safi(Unicast)"; case SAFI_MULTICAST: return "Safi(Multicast)"; } XLOG_UNREACHABLE(); return 0; } #if 0 class AfiSafi { public: static int afi_safi_index(Afi afi, Safi safi) { switch(afi) { case AFI_IPV4: switch(safi) { case SAFI_UNICAST: return 0; case SAFI_MULTICAST: return 1; } case AFI_IPV6: switch(safi) { case SAFI_UNICAST: return 2; case SAFI_MULTICAST: return 3; } } } static int begin() { return 0; } static int end() { return 4; } }; #endif // #define SAFI_NLRI_UNICASTMULTICAST 3 - Removed in: // draft-ietf-idr-rfc2858bis-03.txt #include #include "libxorp/debug.h" #include "libxorp/ref_ptr.hh" #include "libxorp/asnum.hh" #include "exceptions.hh" enum ParamType { PARAMINVALID = 0, // param type not set yet. PARAMTYPEAUTH = 1, PARAMTYPECAP = 2 }; enum CapType { CAPABILITYMULTIPROTOCOL = 1, CAPABILITYREFRESH = 2, CAPABILITYREFRESHOLD = 128, // here for backwards compatibility now 2. CAPABILITYMULTIROUTE = 4, CAPABILITY4BYTEAS = 65, CAPABILITYUNKNOWN = -1, // used to store unknown cababilities }; class BGPParameter { public: /** * create a new BGPParameter from incoming data. * Takes a chunk of memory of size l, returns an object of the * appropriate type and actual_length is the number of bytes used * from the packet. * Throws an exception on error. */ static BGPParameter *create(const uint8_t* d, uint16_t max_len, size_t& actual_length) throw(CorruptMessage); BGPParameter() : _data(0), _length(0), _type(PARAMINVALID) {} BGPParameter(uint8_t l, const uint8_t* d); BGPParameter(const BGPParameter& param); virtual ~BGPParameter() { delete[] _data; } virtual void decode() = 0; virtual void encode() const = 0; // virtual bool operator==(const BGPParameter&) const = 0; virtual bool compare(const BGPParameter&) const = 0; void dump_contents() const ; void set_type(ParamType t) { debug_msg("_Type set %d\n", t); _type = t; } ParamType type() const { debug_msg("_Type retrieved %d\n", _type); return _type; } void set_length(int l); // XXX check- possible discrepancy between length() and paramlength() uint8_t length() const { debug_msg("_length retrieved %d\n", _length-2); return _length; } uint8_t paramlength() const { debug_msg("_paramlength retrieved %d\n", _length); return _length+2; } uint8_t* data() const { encode(); return _data; } // BGPParameter* clone() const; virtual string str() const = 0; protected: uint8_t* _data; uint8_t _length; ParamType _type; private: }; /* BGP Capabilities Negotiation Parameter - 2 */ class BGPCapParameter: public BGPParameter { public: BGPCapParameter(); BGPCapParameter(uint8_t l, const uint8_t* d); BGPCapParameter(const BGPCapParameter& param); // ~BGPCapParameter(); // virtual void decode() = 0; // virtual void encode() = 0; // bool operator==(const BGPParameter& rhs) const { bool compare(const BGPParameter& rhs) const { const BGPCapParameter *ptr = dynamic_cast(&rhs); if(!ptr) return false; return ptr->_cap_code == _cap_code && ptr->_cap_length == _cap_length; } CapType cap_code() const { return _cap_code; } virtual string str() const = 0; protected: CapType _cap_code; uint8_t _cap_length; private: }; class BGPRefreshCapability : public BGPCapParameter { public: BGPRefreshCapability(); BGPRefreshCapability(uint8_t l, const uint8_t* d); BGPRefreshCapability(const BGPRefreshCapability& cap); void decode(); void encode() const; // bool operator==(const BGPParameter& rhs) const { bool compare(const BGPParameter& rhs) const { const BGPRefreshCapability *ptr = dynamic_cast(&rhs); if(!ptr) return false; return true; } string str() const; protected: private: bool _old_type_code; /* this should be true if the refresh capability used/will use the obsolete capability code for refresh */ }; class BGPMultiProtocolCapability : public BGPCapParameter { public: BGPMultiProtocolCapability(Afi afi, Safi safi); BGPMultiProtocolCapability(uint8_t l, const uint8_t* d); BGPMultiProtocolCapability(const BGPMultiProtocolCapability& cap); void decode(); void encode() const; void set_address_family(Afi f) { _address_family = f; } Afi get_address_family() const { return _address_family; } void set_subsequent_address_family_id(Safi f) { _subsequent_address_family = f; } Safi get_subsequent_address_family_id() const { return _subsequent_address_family; } bool compare(const BGPParameter& rhs) const; string str() const; protected: private: Afi _address_family; Safi _subsequent_address_family; }; class BGPMultiRouteCapability : public BGPCapParameter { public: BGPMultiRouteCapability(); BGPMultiRouteCapability(uint8_t l, const uint8_t* d); BGPMultiRouteCapability(const BGPMultiRouteCapability& cap); void decode(); void encode() const; // void set_address_family(uint16_t f) { _address_family = f; } // uint16_t get_address_family() const { return _address_family; } // void set_subsequent_address_family_id(uint8_t f) { _subsequent_address_family = f; } // uint8_t get_subsequent_address_family_id() const { return _subsequent_address_family; } string str() const { return "BGP Multiple Route Capability\n"; } protected: private: // uint16_t _address_family; // uint8_t _subsequent_address_family; }; class BGP4ByteASCapability : public BGPCapParameter { public: BGP4ByteASCapability(); BGP4ByteASCapability(uint8_t l, const uint8_t* d); BGP4ByteASCapability(const BGP4ByteASCapability& cap); BGP4ByteASCapability(const AsNum& as); void decode(); void encode() const; uint32_t as() const { return _as4; } void set_as(uint32_t as4) { _as4 = as4; } string str() const; protected: private: uint32_t _as4; }; class BGPUnknownCapability : public BGPCapParameter { public: BGPUnknownCapability(); BGPUnknownCapability(uint8_t l, const uint8_t* d); BGPUnknownCapability(const BGPUnknownCapability& cap); void decode(); void encode() const; string str() const { return "Unknown BGP Capability\n"; } CapType unknown_cap_code() const { return _unknown_cap_code; } protected: private: CapType _unknown_cap_code; }; /** * List of parameters */ typedef ref_ptr ParameterNode; typedef list ParameterList; #endif // __BGP_PARAMETER_HH__ xorp/bgp/route_table_base.hh0000664000076400007640000001104511540224220016241 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/bgp/route_table_base.hh,v 1.21 2008/11/08 06:14:38 mjh Exp $ #ifndef __BGP_ROUTE_TABLE_BASE_HH__ #define __BGP_ROUTE_TABLE_BASE_HH__ #include "libxorp/ipv4net.hh" #include "libxorp/ipv4.hh" #include "internal_message.hh" //#include "dump_iterators.hh" template class DumpIterator; enum RouteTableType { RIB_IN_TABLE = 1, RIB_OUT_TABLE = 2, CACHE_TABLE = 3, DECISION_TABLE = 4, FANOUT_TABLE = 5, FILTER_TABLE = 6, DELETION_TABLE = 7, DUMP_TABLE = 8, NHLOOKUP_TABLE = 9, DEBUG_TABLE = 10, POLICY_TABLE = 11, AGGREGATION_TABLE = 12, DAMPING_TABLE = 13 }; #define MAX_TABLE_TYPE 13 #define ADD_USED 1 #define ADD_UNUSED 2 #define ADD_FAILURE 3 #define ADD_FILTERED 4 /** * @short Base class for a stage in BGP's internal plumbing * * The XORP BGP is internally implemented as a set of pipelines. Each * pipeline receives routes from a BGP peer, stores them, and applies * filters to them to modify the routes. Then the pipelines converge * on a single decision process, which decides which route wins * amongst possible alternative routes. After decision, the winning * routes fanout again along a set of pipelines, again being filtered, * before being transmitted to peers. * * Each stage in these pipelines is a BGPRouteTable. BGPRouteTable is * a virtual base class, so all the stages consist of specialized * RouteTable class instances. */ template class BGPRouteTable { public: BGPRouteTable(string tablename, Safi safi); virtual ~BGPRouteTable(); virtual int add_route(InternalMessage &rtmsg, BGPRouteTable *caller) = 0; virtual int replace_route(InternalMessage &old_rtmsg, InternalMessage &new_rtmsg, BGPRouteTable *caller) = 0; virtual int delete_route(InternalMessage &rtmsg, BGPRouteTable *caller) = 0; virtual int route_dump(InternalMessage &rtmsg, BGPRouteTable *caller, const PeerHandler *dump_peer); virtual int push(BGPRouteTable *caller) = 0; virtual const SubnetRoute *lookup_route(const IPNet &net, uint32_t& genid, FPAListRef& pa_list) const = 0; virtual void route_used(const SubnetRoute* /*route*/, bool /*in_use*/){ abort(); } void set_next_table(BGPRouteTable* table) { _next_table = table; } BGPRouteTable *next_table() { return _next_table;} /* parent is only supposed to be called on single-parent tables*/ virtual BGPRouteTable *parent() { return _parent; } virtual void set_parent(BGPRouteTable *parent) { _parent = parent; } virtual RouteTableType type() const = 0; const string& tablename() const {return _tablename;} virtual string str() const = 0; /* mechanisms to implement flow control in the output plumbing */ virtual void wakeup(); virtual bool get_next_message(BGPRouteTable *) {abort(); return false; } virtual bool dump_next_route(DumpIterator& dump_iter); /** * Notification that the status of this next hop has changed. * * @param bgp_nexthop The next hop that has changed. */ virtual void igp_nexthop_changed(const A& bgp_nexthop); virtual void peering_went_down(const PeerHandler *peer, uint32_t genid, BGPRouteTable *caller); virtual void peering_down_complete(const PeerHandler *peer, uint32_t genid, BGPRouteTable *caller); virtual void peering_came_up(const PeerHandler *peer, uint32_t genid, BGPRouteTable *caller); Safi safi() const {return _safi; } protected: BGPRouteTable *_next_table, *_parent; string _tablename; const Safi _safi; }; #endif // __BGP_ROUTE_TABLE_BASE_HH__ xorp/bgp/route_table_policy.hh0000664000076400007640000000705111421137511016634 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/bgp/route_table_policy.hh,v 1.15 2008/11/08 06:14:39 mjh Exp $ #ifndef __BGP_ROUTE_TABLE_POLICY_HH__ #define __BGP_ROUTE_TABLE_POLICY_HH__ #include "route_table_base.hh" #include "bgp_varrw.hh" #include "policy/backend/policy_filters.hh" /** * @short Generic Policy filter table suitable for export filters. * * All policy filter tables in BGP have very similar functionality. Some have * more features than others, but most of the code is shared. * * The export filter is the most "standard" table, so it is used as a base * table for the source match and import filter tables. */ template class PolicyTable : public BGPRouteTable { public: /** * @param tablename name of the table. * @param safi the safi. * @param parent the parent table. * @param pfs a reference to the global policy filters. * @param type the type of policy filter used in this table. */ PolicyTable(const string& tablename, const Safi& safi, BGPRouteTable* parent, PolicyFilters& pfs, const filter::Filter& type); virtual ~PolicyTable(); int add_route(InternalMessage &rtmsg, BGPRouteTable *caller); int replace_route(InternalMessage &old_rtmsg, InternalMessage &new_rtmsg, BGPRouteTable *caller); int delete_route(InternalMessage &rtmsg, BGPRouteTable *caller); virtual int route_dump(InternalMessage &rtmsg, BGPRouteTable *caller, const PeerHandler *dump_peer); int push(BGPRouteTable *caller); void route_used(const SubnetRoute* route, bool in_use); bool get_next_message(BGPRouteTable *next_table); const SubnetRoute *lookup_route(const IPNet &net, uint32_t& genid, FPAListRef& pa_list) const; // XXX: keep one table type for now RouteTableType type() const { return POLICY_TABLE; } string str() const; /** * Performs policy filtering on a route. * * If false is returned, then the route is rejected, otherwise it * was accepted and may have been modified. * * @param rtmsg the route message to filter. * @param no_modify if true, the filter will not modify the route. * @return whether the route was accepted by the filter. */ bool do_filtering(InternalMessage& rtmsg, bool no_modify) const; void enable_filtering(bool on); protected: virtual void init_varrw(); const filter::Filter _filter_type; BGPVarRW* _varrw; private: PolicyFilters& _policy_filters; bool _enable_filtering; }; #endif // __BGP_ROUTE_TABLE_POLICY_HH__ xorp/bgp/route_table_nhlookup.hh0000664000076400007640000001046611421137511017200 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/bgp/route_table_nhlookup.hh,v 1.20 2008/11/08 06:14:39 mjh Exp $ #ifndef __BGP_ROUTE_TABLE_NHLOOKUP_HH__ #define __BGP_ROUTE_TABLE_NHLOOKUP_HH__ #include "route_table_base.hh" #include "libxorp/ref_trie.hh" #include "next_hop_resolver.hh" template class MessageQueueEntry { public: MessageQueueEntry(InternalMessage* add_msg, InternalMessage* delete_msg); MessageQueueEntry(const MessageQueueEntry& original); ~MessageQueueEntry(); typedef enum op { ADD = 1, REPLACE = 2 } Op; Op type() const { if (_add_msg!=NULL && _delete_msg==NULL) return ADD; if (_add_msg!=NULL && _delete_msg!=NULL) return REPLACE; abort(); } InternalMessage* add_msg() const {return _add_msg;} const SubnetRoute* added_route() const {return _add_msg->route();} FPAListRef& added_attributes() const {return _add_msg->attributes();} InternalMessage* delete_msg() const {return _delete_msg;} const SubnetRoute* deleted_route() const {return _delete_msg->route();} FPAListRef& deleted_attributes() const {return _delete_msg->attributes();} const IPNet& net() const {return _add_msg->route()->net();} string str() const; private: void copy_in(InternalMessage* add_msg, InternalMessage* delete_msg); InternalMessage* _add_msg; InternalMessage* _delete_msg; //These references are to ensure that the SubnetRoutes from the //add and delete messages don't get deleted before we're done with //them. SubnetRouteConstRef _added_route_ref; SubnetRouteConstRef _deleted_route_ref; }; template class NhLookupTable : public BGPRouteTable { public: NhLookupTable(string tablename, Safi safi, NextHopResolver *nexthop_resolver, BGPRouteTable *parent); int add_route(InternalMessage &rtmsg, BGPRouteTable *caller); int replace_route(InternalMessage &old_rtmsg, InternalMessage &new_rtmsg, BGPRouteTable *caller); int delete_route(InternalMessage &rtmsg, BGPRouteTable *caller); int push(BGPRouteTable *caller); const SubnetRoute *lookup_route(const IPNet &net, uint32_t& genid, FPAListRef& pa_list) const; void route_used(const SubnetRoute* route, bool in_use); virtual void RIB_lookup_done(const A& nexthop, const set >& nets, bool lookup_succeeded); RouteTableType type() const {return NHLOOKUP_TABLE;} string str() const; private: //access the message queue by subnet or an address on the subnet RefTrie > _queue_by_net; //access the message queue by nexthop multimap *> _queue_by_nexthop; NextHopResolver* _next_hop_resolver; /** * Add the message queue entry to both queues. */ void add_to_queue(const A& nexthop, const IPNet& net, InternalMessage* new_msg, InternalMessage* old_msg); /** * Lookup subnet in the _queue_by_net. * * @param nexthop if non zero compare against the net that is found. * @param net to lookup. * @return a message queue entry if found else zero. */ MessageQueueEntry * lookup_in_queue(const A& nexthop, const IPNet& net) const; /** * Find the message queue entry and remove from both queues. */ void remove_from_queue(const A& nexthop, const IPNet& net); }; #endif // __BGP_ROUTE_TABLE_NHLOOKUP_HH__ xorp/bgp/damping.cc0000664000076400007640000000612311540224217014356 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #include #include "bgp_module.h" #include "libxorp/xlog.h" #include "damping.hh" #include "bgp.hh" Damping::Damping(EventLoop& eventloop) : _eventloop(eventloop), _damping(false), _half_life(15), _max_hold_down(60), _cutoff(3000), _tick(0) { init(); } void Damping::set_damping(bool damping) { _damping = damping; init(); } bool Damping::get_damping() const { return _damping; } void Damping::set_half_life(uint32_t half_life) { _half_life = half_life; init(); } void Damping::set_max_hold_down(uint32_t max_hold_down) { _max_hold_down = max_hold_down; init(); } void Damping::set_reuse(uint32_t reuse) { _reuse = reuse; init(); } void Damping::set_cutoff(uint32_t cutoff) { _cutoff = cutoff; init(); } void Damping::init() { debug_msg("init\n"); if (!_damping) { halt(); return; } size_t array_size = _max_hold_down * 60; // Into seconds. _decay.resize(array_size); double decay_1 = exp((1.0 / (_half_life * 60.0)) * log(1.0/2.0)); double decay_i = decay_1; for (size_t i = 0; i < array_size; i++) { _decay[i] = static_cast(decay_i * FIXED); // printf("%d %d %f\n",i, _decay[i], decay_i); decay_i = pow(decay_1, static_cast(i + 2)); } // Start the timer to incement the tick _tick_tock = _eventloop.new_periodic_ms(1000, callback(this, &Damping::tick)); } void Damping::halt() { _tick_tock.unschedule(); } bool Damping::tick() { _tick++; return true; } uint32_t Damping::compute_merit(uint32_t last_time, uint32_t last_merit) const { debug_msg("last_time %d last_merit %d\n", last_time, last_merit); uint32_t tdiff = get_tick() - last_time; if (tdiff >= _max_hold_down * 60) return FIXED; else return ((last_merit * _decay[tdiff]) / FIXED) + FIXED; } uint32_t Damping::get_reuse_time(uint32_t merit) const { debug_msg("merit %d\n", merit); uint32_t damp_time = (((merit / _reuse) - 1) * _half_life * 60); uint32_t max_time = _max_hold_down * 60; uint32_t reuse = damp_time > max_time ? max_time : damp_time; debug_msg("reuse %d\n", reuse); return reuse; } xorp/bgp/BUGS0000664000076400007640000000640211421137511013111 0ustar greearbgreearb# # $XORP: xorp/bgp/BUGS,v 1.1.1.1 2002/12/11 23:55:49 hodson Exp $ # Known Bugs ========== This file is no longer definitive. BGP bugs should be placed in Bugzilla . 4) Consider the following senario: A peer sends a large number of routes to our BGP process then closes the connection. If our BGP has not been able to keep up with the incoming updates then a TCP queue will have built up. The TCP FIN will be at the end of the buffer. The peer will consider the connection closed but our BGP process will not due to the buffered data. The peer now attempts to make a new connection. Our BGP process will reject the new connection as we are in the established state. We will then send a notify message to the original connection signalling a FSM error. The error is sent on the wrong connection where nothing is listening. Done ==== 1) It doesn't seem like the holdtime that is configured is what sent in the open message. This whole keepalive/holdtime timer stuff needs to be looked at. 2) Our BGP process will accept update messages without all the mandatory attributes. Need to check with the spec to see what the correct behaviour should be. 4) It looks like the Cisco may send update packets containing only withdraw information. Not containing the mandatory attributes. check_update_packet will consider these packets to be bad and drop the peering. Need to check the spec if such packets are legal and change check_update_packet. 2) The BGP code when it receives an OPEN message checks the AS number of the peer against the expected value. If there is a mismatch then the session is dropped. Our current code seems to immediately retry the connection. It should really back off. I think there may be a draft about this (or perhaps there is something in the spec). I think that this BUG may have got fixed when I fixed the propogation of the error variable to the set_state routine. 3) There are two reasons for taking a peering to IDLE. One is that an error has occured, the other is that the peering has been administratively taken to IDLE. If we have moved to IDLE due to an error bgp should attempt to bring up the peering (modulo back off timers). If we have been administratively taken to IDLE then we should not attempt to bring up the peering or allow our peer to bring up the peering. The routine set_state takes an argument that identifies if we are changing state due to an error. In the case that we are going to IDLE this error variable is used to determine if we should stay IDLE or not. We always go to state IDLE from state STOPPED. The STOPPED state exists to allow us to wait for the notification message to be sent. There are three ways of going from STOPPED to IDLE: a) The notification message is sent. The completion method contains the original reason that we were going to IDLE all is fine. b) The notification message is not sent. Then a timer expires and again we have the original reason for going to IDLE. c) A connection event arrives we are in stopped. At the moment we unconditionally accept the connection. If we are in the process of being administratively taken to IDLE, then we *shouldn't* accept the connection (The BGP process is waiting for a CEASE notification to be sent). We need to know why we went to state stopped. xorp/bgp/next_hop_resolver.hh0000664000076400007640000006037511540224217016527 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/bgp/next_hop_resolver.hh,v 1.36 2008/10/02 21:56:16 bms Exp $ #ifndef __BGP_NEXT_HOP_RESOLVER_HH__ #define __BGP_NEXT_HOP_RESOLVER_HH__ #include "libxorp/ipv4.hh" #include "libxorp/ipv6.hh" #include "libxorp/ipnet.hh" #include "libxorp/ref_trie.hh" #include "libxipc/xrl_std_router.hh" template class NhLookupTable; template class NextHopCache; template class NextHopResolver; template class NextHopRibRequest; template class DecisionTable; template class NHRequest; /** * Next hop resolvability and IGP distances are accessed through this class. * * Next hop resolvability and IGP distances are retrieved from the RIB * and cached here in BGP. This retrieval process implicitly registers * interest with the RIB regarding these next hops. Thus any changes in * these next hops is signalled by the RIB to BGP via callbacks. * * If the state of a next hop changes (resolvable/unresolvable), or an * IGP distance changes, then it is possible that a new route may now * win the decision process. The decision process must therefore be * re-run for all routes that are affected by a next hop change. This * re-run of the decision process is achieved calling * "igp_nexthop_changed" on the decision process. * * What questions can be asked about next hops? Is a next hop * resolvable and if it is, what is the IGP distance. * * To answer questions about next hops three interfaces are supported: * 1) An asynchronous interface that registers a callback which will be * called when a response becomes available. For use by the (next * hop) route table before decision. By the time a route gets to * decision it *must* be known if the route is resolvable. * 2) A synchronous interface for use by decision. It is a fatal error * if this interface is called and the next hop is not in the * cache. As by the time decision is called the cache should have been * populated by use of the asynchronous interface. * 3) A synchronous debugging interface. * * Cache maintainance: * Every stored SubnetRoute in every rib in has a next hop. Every * unique next hop has an entry in the cache. If a next hop lookup * through the asynchronous interface causes a cache miss then an entry * is created with a reference count of 1. Subsequent lookups through * the next hop interface will cause the reference count to be * incremented by 1. An interface to increase the reference count by * more than one also exists. All route deletions should explicitly * call a routine in here to decrement the reference count. */ class BGPMain; template class NextHopResolver { public: NextHopResolver(XrlStdRouter *xrl_router, EventLoop& eventloop, BGPMain& bgp); virtual ~NextHopResolver(); /** * Add decision. * * Pass a pointer to the decision table into the next hop * resolver. This pointer is used to notify the decision table * when a next hop metric changes. * * @param decision Pointer to the decision table. */ void add_decision(DecisionTable *decision); /** * Set the rib's name, allows for having a dummy rib or not having * a RIB at all. */ bool register_ribname(const string& r); /** * Register interest in this nexthop. * * @param nexthop Nexthop. * @param net_from_route The net that is associated with this * nexthop in the NextHopLookupTable. Treated as an opaque id. * @param requester Once the registration with the RIB suceeds the * requester is called back. * @return True if the registration succeed. */ virtual bool register_nexthop(A nexthop, IPNet net_from_route, NhLookupTable *requester); /** * De-Register interest in this nexthop. * * @param nexthop Nexthop. * @param net_from_route The net that is associated with this * nexthop in the NextHopLookupTable. Treated as an opaque id. * @param requester Original requester, not used. * @return True if the registration succeed. */ virtual void deregister_nexthop(A nexthop, IPNet net_from_route, NhLookupTable *requester); /** * Lookup next hop. * * If a "register_nexthop" request has been made and callback has * taken place via the "requester" pointer, then the lookup is * guaranteed to work. * * @param nexthop Next hop. * @param resolvable Is this route resolvable. * @param metric If this route is resolvable the metric of this * route. * @return True if this next hop is found. */ virtual bool lookup(const A nexthop, bool& resolvable, uint32_t& metric) const; /** * Call from the RIB to notify us that a metric has changed. */ bool rib_client_route_info_changed(const A& addr, const uint32_t& prefix_len, const A& nexthop, const uint32_t& metric); /** * Call from the RIB to notify us that any registrations with this * address and prefix_len are now invalid. */ bool rib_client_route_info_invalid(const A& addr, const uint32_t& prefix_len); /** * Next hop changed. * * Whenever a next hop changes this method should be called and * the change will be rippled up to the decision process. * * @param nexthop The next hop that has changed. */ void next_hop_changed(A nexthop); /** * Next hop changed. * * Whenever a next hop changes this method should be called and * the change will be rippled up to the decision process. However * if a change occurs but the metrics don't change don't bother to * ripple up the change there is no point. * * @param nexthop The next hop that has changed. * @param old_resolves The old resolve value. * @param old_metric The old metric value. */ void next_hop_changed(A nexthop, bool old_resolves, uint32_t old_metric); /** * Get NextHopRibRequest pointer. * * Used for testing. */ NextHopRibRequest *get_next_hop_rib_request() { return &_next_hop_rib_request; } /** * Get a reference to the main timer list */ EventLoop& eventloop() {return _eventloop;} protected: list *> _decision; private: string _ribname; // RIB name to use in XRL calls XrlStdRouter *_xrl_router; EventLoop& _eventloop; BGPMain& _bgp; NextHopCache _next_hop_cache; NextHopRibRequest _next_hop_rib_request; }; /** * A cache of next hop information. * * BGP requires information regarding resolvabilty and metrics for * next hops. This information is known by the RIB. Questions are * asked of the RIB and the results are cached here. The RIB notes * that questions have been asked and if the state of a next hop * changes then this is reported back to BGP. In order to save space the * RIB does not record information about each next hop but returns an * address/prefix_len range for which the answer is valid. * * Not only can the RIB report changes but can also report that a * previous entry is totally invalid. In the case that an entry is * invalid all the next hops need to be re-requested. * */ template class NextHopCache { public: ~NextHopCache(); /** * Add an entry to our next hop table. * * @param addr Base address. * @param nexthop Next hop that is being added to the trie. * @param prefix_len The prefix_len that is masked with the nexhop. * @param real_prefix_len The actual prefix_len that this next hop * resolves too. This is only used to match with upcalls from the * RIB. * @param resolvable Is this route resolvable. * @param metric If this route is resolvable its metric. */ void add_entry(A addr, A nexthop, int prefix_len, int real_prefix_len, bool resolvable, uint32_t metric = 0); /** * Validate an entry. * * add_entry creates an entry with no nexthop references. The * assumption is that a register_nexthop will follow shortly after * initial creation. It is possible due to a deregister_nexthop * coming in while we are waiting for a response from the RIB that * the register_nexthop never happens. This method checks that the * specified entry is referenced and if it isn't it is deleted. * * @param addr Base address. * @param nexthop Next hop that is being added to the trie. * @param prefix_len The prefix_len that is masked with the nexhop. * @param real_prefix_len The actual prefix_len that this next hop * @return true if the entry is in use. */ bool validate_entry(A addr, A nexthop, int prefix_len, int real_prefix_len); /** * Change an entry in the next hop table. * * @param addr The base address. * @param prefix_len The prefix_len that this next hop * resolves too. This is only used to match with upcalls from the * RIB. * @param metric If this route is resolvable its metric. * @return The map of next hops with reference counts that were * covered by this entry. * */ map change_entry(A addr, int prefix_len, uint32_t metric); /** * Delete an entry from the nexthop table. * * It is a fatal error to attempt to delete an entry that doesn't * exist. * * @param addr Base address that is being removed from the trie. * @param prefix_len The prefix_len. * @return The map of next hops with reference counts that were * covered by this entry. */ map delete_entry(A addr, int prefix_len); /** * Lookup by base address * * @param addr Base address. * @param prefix_len The prefix length. * @param resolvable Is this route resolvable. * @param metric If this route is resolvable the metric of this route. * @return True if this next hop is found. * */ bool lookup_by_addr(A addr, int prefix_len, bool& resolvable, uint32_t& metric) const; /** * Lookup next hop. * * @param nexthop Next hop. * @param resolvable Is this route resolvable. * @param metric If this route is resolvable the metric of this * route. * @return True if this next hop is found. * */ bool lookup_by_nexthop(A nexthop, bool& resolvable, uint32_t& metric) const; /** * Lookup next hop without entry * * This lookup does not require that next hop is already * known. That is the next hop is not in _nexthop_references. * * @param nexthop Next hop. * @param resolvable Is this route resolvable. * @param metric If this route is resolvable the metric of this * route. * @return True if this next hop is found. * */ bool lookup_by_nexthop_without_entry(A nexthop, bool& resolvable, uint32_t& metric) const; /* * Try and register this next hop. * * If this next hop is known or covered then this next hop is * added to map of next hops that is associated with the * NextHopEntry. * * @param nexthop Next hop. * @param ref_cnt_incr How much to increase the reference count by. * @return True if this next hop is known or its in a covered * range. */ bool register_nexthop(A nexthop, int ref_cnt_incr = 1); /* * Deregister this next hop. * * The NextHopEntry has a map of next hops with reference * counts. A deregister causes this next hop to be removed from * its associated NextHopEntry. If the map becomes empty then the * entry can be removed. If the map becomes empty then this fact * is signalled in the return value and this next hop can be * deregistered from the RIB. * * @param nexthop Next hop. * @param last True if this is the last next hop and the entry * has been freed. * @param addr If this was the last entry the base address. * @param prefix_len If this was the last entry the associated prefix_len. * @return True if an entry was found. */ bool deregister_nexthop(A nexthop, bool& last, A& addr, uint32_t& prefix_len); private: struct NextHopEntry { A _address; // Base address as returned by the RIB #ifdef USE_NEXTHOP A _nexthop; // The initial next hop. Used to find entry by // prefix_len #endif map _nexthop_references; int _prefix_len; int _real_prefix_len; bool _resolvable; int _metric; }; typedef set RealPrefixEntry; typedef RefTriePostOrderIterator RealPrefixIterator; typedef NextHopEntry PrefixEntry; typedef RefTriePostOrderIterator PrefixIterator; /** * The NextHopEntry is indexed in two ways either by prefix_len or by * real_prefix_len. * * Both of these data structures need to be kept in sync. */ RefTrie _next_hop_by_prefix; RefTrie _next_hop_by_real_prefix; /** * Given a real prefix_len entry return a prefix_len entry. * * @param pe A real prefix_len entry. * @param addr Address. * @param real_prefix_len The real prefix_len. * @return A prefix_len entry if found 0 otherwise. */ PrefixEntry *rpe_to_pe(const RealPrefixEntry& pe, A addr, int real_prefix_len) const; /** * Given a real prefix_len entry return a prefix_len entry. * * @param pe A real prefix_len entry. * @param addr Address. * @param real_prefix_len The real prefix_len. * @return A prefix_len entry if found 0 otherwise. */ PrefixEntry *rpe_to_pe_delete(RealPrefixEntry& pe, A addr, int real_prefix_len); }; /** * The queue of outstanding requests to the RIB. Requests can have * arrived in this queue in two ways. A simple call down from the next * hop table or due to the previous result being marked invalid by * an upcall from the RIB. The class variables "_register" and * "_reregister" denote how the entry was created. It is possible * that an upcall from the RIB has caused a queue entry, followed * by a downcall from the next hop table in which case both * "_register" and "_reregister" will be true. */ template class RibRequestQueueEntry { public: typedef enum {REGISTER, DEREGISTER} RegisterMode; RibRequestQueueEntry(RegisterMode mode) : _register_mode(mode) {} virtual ~RibRequestQueueEntry() {} protected: RegisterMode _register_mode; }; template class RibRegisterQueueEntry : public RibRequestQueueEntry { public: typedef RibRequestQueueEntry QE; RibRegisterQueueEntry(A nexthop, IPNet net_from_route, NhLookupTable *requester) : RibRequestQueueEntry(QE::REGISTER), _nexthop(nexthop), _new_register(true), _requests(net_from_route, requester), _reregister(false), _ref_cnt(0) {} RibRegisterQueueEntry(A nexthop, uint32_t ref_cnt, bool resolvable, uint32_t metric) : RibRequestQueueEntry(QE::REGISTER), _nexthop(nexthop), _new_register(false), _reregister(true), _ref_cnt(ref_cnt), _resolvable(resolvable), _metric(metric) {} void register_nexthop(IPNet net_from_route, NhLookupTable *requester) { XLOG_ASSERT(true == _reregister || true == _new_register); XLOG_ASSERT(QE::_register_mode == QE::REGISTER); _new_register = true; _requests.add_request(net_from_route, requester); } bool deregister_nexthop(IPNet net_from_route, NhLookupTable *requester) { XLOG_ASSERT(true == _reregister || true == _new_register); XLOG_ASSERT(QE::_register_mode == QE::REGISTER); if (_new_register && _requests.remove_request(net_from_route, requester)) { return true; } if (_reregister) { XLOG_ASSERT(_ref_cnt > 0); _ref_cnt--; return true; } return false; } void reregister_nexthop(uint32_t ref_cnt, bool resolvable, uint32_t metric) { XLOG_ASSERT(false == _reregister); XLOG_ASSERT(0 == _ref_cnt); XLOG_ASSERT(QE::_register_mode == QE::REGISTER); _reregister = true; _ref_cnt = ref_cnt; _resolvable = resolvable; _metric = metric; } bool resolvable() const { assert(QE::_register_mode == QE::REGISTER); return _resolvable; } bool reregister() const { assert(QE::_register_mode == QE::REGISTER); return _reregister; } bool new_register() const { assert(QE::_register_mode == QE::REGISTER); return _new_register; } bool metric() const { assert(QE::_register_mode == QE::REGISTER); return _metric; } const A& nexthop() const { return _nexthop; } uint32_t ref_cnt() const { return _ref_cnt; } NHRequest& requests() { return _requests; } private: /* ** Register info. */ A _nexthop; bool _new_register; NHRequest _requests; /* ** Reregister info. */ bool _reregister; uint32_t _ref_cnt; /** * The old answer if we are in the process of reregistering so * that lookups will be satisfied with this old answer. */ bool _resolvable; uint32_t _metric; }; template class RibDeregisterQueueEntry : public RibRequestQueueEntry { public: typedef RibRequestQueueEntry QE; RibDeregisterQueueEntry(A base_addr, uint32_t prefix_len) : RibRequestQueueEntry(QE::DEREGISTER), _base_addr(base_addr), _prefix_len(prefix_len) {} const A& base_addr() const { return _base_addr;} uint32_t prefix_len() const { return _prefix_len;} private: /* ** Deregister info. */ A _base_addr; uint32_t _prefix_len; }; /** * Make requests of the RIB and get responses. * * At any time there is only ever one outstanding request to the * RIB. Firstly we don't want to overrun the RIB with * requests. Secondly it is possible that different next hops in the * queue of requests may resolve to the same address/prefix_len answer * (see below). */ template class NextHopRibRequest { public: NextHopRibRequest(XrlStdRouter *, NextHopResolver& next_hop_resolver, NextHopCache& next_hop_cache, BGPMain& bgp); ~NextHopRibRequest(); bool register_ribname(const string& r) { _ribname = r; return true; } /** * Register interest with the RIB about this next hop. * * @param nexthop The next hop that we are attempting to resolve. * @param net The subnet that this next hop is associated with. * @param requester The lookup table that wants to be notified * when the response comes back. */ void register_nexthop(A nexthop, IPNet net, NhLookupTable *requester); /** * Send the next queued request */ void send_next_request(); /** * Actually register interest with the RIB. * * A small method that will be specialized to differentiate * between IPv4 and IPv6. * * @param nexthop The next hop that we are attempting to resolve. */ void register_interest(A nexthop); /** * XRL callback from register_interest. */ void register_interest_response(const XrlError& error, const bool *resolves, const A *addr, const uint32_t *prefix_len, const uint32_t *real_prefix_len, const A *actual_nexthop, const uint32_t *metric, const A nexthop_interest, const string comment); /** * An unmatched invalidate has been received. */ bool premature_invalid(const A& addr, const uint32_t& prefix_len); /** * An invalidate has been received after we deregistered interest. */ bool tardy_invalid(const A& addr, const uint32_t& prefix_len); /** * Deregister interest with the RIB about this next hop. * * @param nexthop The next hop that we are attempting to resolve. * @param net The subnet that this next hop is associated with. * @param requester The lookup table that wants to be notified * when the response comes back. * @return True if an entry was found to remove. */ bool deregister_nexthop(A nexthop, IPNet net, NhLookupTable *requester); /** * Reregister interest with the RIB about this next hop. * * This method is used when the RIB tells us that all previous * registrations have become invalid. This forces us to re-request * information. We save the old state (resolvable, metric) just in * case the following events occur: *
     * 1) Register from next hop table.
     * 2) route_info_invalid from RIB.
     * 3) lookup from decision.
     * 
* This ordering of events may not be possible just in case it is * save the old result and return it in a lookup. * * @param nexthop The next hop that we are attempting to resolve. * @param ref_cnt The number of subnets using this nexthop. * @param resolvable Was the previous result resolvable. * @param metric If the previous result was resolvable the metric. */ void reregister_nexthop(A nexthop, uint32_t ref_cnt, bool resolvable, uint32_t metric); /** * lookup next hop. * * @param nexthop Next hop. * @param resolvable Is this route resolvable. * @param metric If this route is resolvable the metric of this * route. * @return True if this next hop is found. * */ bool lookup(const A& nexthop, bool& resolvable, uint32_t& metric) const; /* * Deregister ourselves from the RIB for this next hop * * @param nexthop The next hop. * @param prefix_len The prefix_len we registered with. */ void deregister_from_rib(const A& nexthop, uint32_t prefix_len); /* * Deregister ourselves from the RIB for this next hop * * @param nexthop The next hop. * @param prefix_len The prefix_len we registered with. */ void deregister_interest(A nexthop, uint32_t prefix_len); /** * XRL response method. * * @param error Error returned by xrl call. * @param comment Comment string used for diagnostic purposes. */ void deregister_interest_response(const XrlError& error, A addr, uint32_t prefix_len, string comment); private: string _ribname; XrlStdRouter *_xrl_router; NextHopResolver
& _next_hop_resolver; NextHopCache& _next_hop_cache; BGPMain& _bgp; /** * Are we currently waiting for a response from the RIB. */ bool _busy; bool _invalid; // True if received an unmatched invalid call. IPNet _invalid_net; // Saved invalid subnet. bool _tardy_invalid; // True if we are expecting an invalid // from the RIB. IPNet _tardy_invalid_net;// Saved invalid subnet. /** * The queue of outstanding requests. */ list *> _queue; /** * Used by the destructor to delete all the "RibRequestQueueEntry" objects * that have been allocated. */ static void zapper(RibRequestQueueEntry *req) { delete req; } }; template class NHRequest { public: NHRequest(); NHRequest(IPNet net, NhLookupTable *requester); void add_request(IPNet net, NhLookupTable *requester); bool remove_request(IPNet net, NhLookupTable *requester); const set *>& requesters() const { return _requesters; } const set >& request_nets(NhLookupTable* requester) const; int requests() const { return _request_total; } private: set *> _requesters; map *, multiset > > _request_map; mutable map *, set > > _answer; int _request_total; }; #endif // __BGP_NEXT_HOP_RESOLVER_HH__ xorp/bgp/route_table_base.cc0000664000076400007640000000565011421137511016240 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #include "bgp_module.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "route_table_base.hh" #define DEBUG_ROUTE_TABLE template BGPRouteTable::BGPRouteTable(string tablename, Safi safi) : _tablename(tablename), _safi(safi) { _next_table = NULL; debug_msg("Creating table %s\n", _tablename.c_str()); } template BGPRouteTable::~BGPRouteTable() {} template bool BGPRouteTable::dump_next_route(DumpIterator& dump_iter) { XLOG_ASSERT(_parent != NULL); return _parent->dump_next_route(dump_iter); } template int BGPRouteTable::route_dump(InternalMessage &rtmsg, BGPRouteTable */*caller*/, const PeerHandler *peer) { XLOG_ASSERT(_next_table != NULL); return _next_table->route_dump(rtmsg, (BGPRouteTable*)this, peer); } template void BGPRouteTable::wakeup() { XLOG_ASSERT(_next_table != NULL); _next_table->wakeup(); } template void BGPRouteTable::igp_nexthop_changed(const A& bgp_nexthop) { XLOG_ASSERT(_parent != NULL); return _parent->igp_nexthop_changed(bgp_nexthop); } template void BGPRouteTable::peering_went_down(const PeerHandler *peer, uint32_t genid, BGPRouteTable *caller) { debug_msg("%s\n", _tablename.c_str()); XLOG_ASSERT(_parent == caller); XLOG_ASSERT(_next_table != NULL); _next_table->peering_went_down(peer, genid, this); } template void BGPRouteTable::peering_down_complete(const PeerHandler *peer, uint32_t genid, BGPRouteTable *caller) { XLOG_ASSERT(_parent == caller); XLOG_ASSERT(_next_table != NULL); _next_table->peering_down_complete(peer, genid, this); } template void BGPRouteTable::peering_came_up(const PeerHandler *peer, uint32_t genid, BGPRouteTable *caller) { XLOG_ASSERT(_parent == caller); if (_next_table) _next_table->peering_came_up(peer, genid, this); } template class BGPRouteTable; template class BGPRouteTable; xorp/bgp/crash_dump.hh0000664000076400007640000000307511540225520015077 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2006-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __BGP_CRASH_DUMP_HH__ #define __BGP_CRASH_DUMP_HH__ #include "libxorp/timeval.hh" class CrashDumper; class CrashDumpManager { public: CrashDumpManager(); void register_dumper(CrashDumper *dumper); void unregister_dumper(CrashDumper *dumper); void crash_dump(); private: list _dumpers; }; class CrashDumper { public: CrashDumper(); virtual ~CrashDumper(); virtual void crash_dump() const; virtual string dump_state() const; void log(const string& msg); private: static CrashDumpManager _mgr; vector _log; vector _logtimes; int _logfirst, _loglast; }; #endif // __BGP_CRASH_DUMP_HH__ xorp/bgp/route_table_ribin.hh0000664000076400007640000001215711540224220016437 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/bgp/route_table_ribin.hh,v 1.31 2008/11/08 06:14:40 mjh Exp $ #ifndef __BGP_ROUTE_TABLE_RIBIN_HH__ #define __BGP_ROUTE_TABLE_RIBIN_HH__ #include "libxorp/eventloop.hh" #include "route_table_base.hh" #include "crash_dump.hh" #include "bgp_trie.hh" class EventLoop; /** * @short Specialized BGPRouteTable that stores routes from a BGP peer. * * The XORP BGP is internally implemented as a set of pipelines * consisting of a series of BGPRouteTables. Each pipeline receives * routes from a BGP peer, stores them, and applies filters to them to * modify the routes. Then the pipelines converge on a single * decision process, which decides which route wins amongst possible * alternative routes. After decision, the winning routes fanout * again along a set of pipelines, again being filtered, before being * transmitted to peers. * * RibInTable is the first stage in such a pipeline. It receives * routes from a single BGP peer, and stores them. Changes are passed * down the pipeline as they occur, and route lookups from downstream * are answered by the RibInTable. */ template class RibInTable : public BGPRouteTable, CrashDumper { public: RibInTable(string tablename, Safi safi, const PeerHandler *peer); ~RibInTable(); /** * Remove all the stored routes. Used to flush static routes only. */ void flush(); /* this version is deprecated - we only use messages between stages */ int add_route(InternalMessage& /*rtmsg*/, BGPRouteTable* /*caller*/) { abort(); return 0; } int add_route(const IPNet &net, FPAListRef& pa_list, const PolicyTags& policy_tags); void ribin_peering_went_down(); void ribin_peering_came_up(); /*replace_route doesn't make sense for a RibIn because it's the RibIn that decides whether this is an add or a replace*/ int replace_route(InternalMessage & /*old_rtmsg*/, InternalMessage & /*new_rtmsg*/, BGPRouteTable * /*caller*/ ) { abort(); return 0; } /* this version is deprecated - we only use messages between stages */ int delete_route(InternalMessage& /*rtmsg*/, BGPRouteTable* /*caller*/) { abort(); return 0; } int delete_route(const IPNet &net); int push(BGPRouteTable *caller); int delete_add_routes(); const SubnetRoute *lookup_route(const IPNet &net, uint32_t& genid, FPAListRef& pa_list) const; void route_used(const SubnetRoute* route, bool in_use); BGPRouteTable *parent() { return NULL; } RouteTableType type() const { return RIB_IN_TABLE; } string str() const; bool get_next_message(BGPRouteTable */*next_table*/) { abort(); return false; } void set_peer_is_up() { _peer_is_up = true; } bool dump_next_route(DumpIterator& dump_iter); /*igp_nexthop_changed is called when the IGP routing changes in such a way that IGP information that was being used by BGP for its decision process is affected. We need to scan through the RIB, looking for routes that have this nexthop, and propagate them downstream again to see if the change makes a difference */ void igp_nexthop_changed(const A& bgp_nexthop); int route_count() const { return _route_table->route_count(); } BgpTrie& trie() const { return *_route_table; } const PeerHandler* peer_handler() const { return _peer; } uint32_t genid() const { return _genid; } void crash_dump() const { CrashDumper::crash_dump(); } string dump_state() const; private: EventLoop& eventloop() const; BgpTrie* _route_table; const PeerHandler *_peer; bool _peer_is_up; uint32_t _genid; uint32_t _table_version; // state and methods related to re-sending all the routes related // to a nexthop whose IGP information has changed. set _changed_nexthops; bool _nexthop_push_active; A _current_changed_nexthop; typename BgpTrie::PathmapType::const_iterator _current_chain; XorpTask _push_task; bool push_next_changed_nexthop(); void deletion_nexthop_check(const SubnetRoute* route); void next_chain(); void stop_nexthop_push(); // end of IGP nexthop handing stuff }; #endif // __BGP_ROUTE_TABLE_RIBIN_HH__ xorp/bgp/bgp_module.h0000664000076400007640000000230011421137511014705 0ustar greearbgreearb/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- */ /* * Copyright (c) 2001-2009 XORP, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 2, June * 1991 as published by the Free Software Foundation. Redistribution * and/or modification of this program under the terms of any other * version of the GNU General Public License is not permitted. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, * see the GNU General Public License, Version 2, a copy of which can be * found in the XORP LICENSE.gpl file. * * XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; * http://xorp.net */ /* * $XORP: xorp/bgp/bgp_module.h,v 1.11 2008/10/02 21:56:14 bms Exp $ */ /* * Module definitions. */ #ifndef __BGP_BGP_MODULE_H__ #define __BGP_BGP_MODULE_H__ #ifndef XORP_MODULE_NAME #define XORP_MODULE_NAME "BGP" #endif #ifndef XORP_MODULE_VERSION #define XORP_MODULE_VERSION "0.1" #endif #endif /* __BGP_BGP_MODULE_H__ */ xorp/bgp/plumbing.cc0000664000076400007640000012110011540225521014544 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #define CHECK_TIME #include "bgp_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/timer.hh" #include "libxorp/timespent.hh" #include "route_table_reader.hh" #include "plumbing.hh" #include "bgp.hh" #include "profile_vars.hh" #include "dump_iterators.hh" BGPPlumbing::BGPPlumbing(const Safi safi, RibIpcHandler* ribhandler, AggregationHandler* aggrhandler, NextHopResolver& next_hop_resolver_ipv4, #ifdef HAVE_IPV6 NextHopResolver& next_hop_resolver_ipv6, #endif PolicyFilters& pfs, BGPMain& bgp) : _bgp(bgp), _rib_handler(ribhandler), _aggr_handler(aggrhandler), _next_hop_resolver_ipv4(next_hop_resolver_ipv4), _safi(safi), _policy_filters(pfs), _plumbing_ipv4("[IPv4:" + string(pretty_string_safi(safi)) + "]", *this, _next_hop_resolver_ipv4) #ifdef HAVE_IPV6 , _next_hop_resolver_ipv6(next_hop_resolver_ipv6) , _plumbing_ipv6("[IPv6:" + string(pretty_string_safi(safi)) + "]", *this, _next_hop_resolver_ipv6) #endif { } int BGPPlumbing::add_peering(PeerHandler* peer_handler) { int result = 0; result |= plumbing_ipv4().add_peering(peer_handler); #ifdef HAVE_IPV6 result |= plumbing_ipv6().add_peering(peer_handler); #endif return result; } int BGPPlumbing::stop_peering(PeerHandler* peer_handler) { debug_msg("BGPPlumbing::stop_peering\n"); int result = 0; result |= plumbing_ipv4().stop_peering(peer_handler); #ifdef HAVE_IPV6 result |= plumbing_ipv6().stop_peering(peer_handler); #endif return result; } int BGPPlumbing::peering_went_down(PeerHandler* peer_handler) { debug_msg("BGPPlumbing::peering_went_down\n"); int result = 0; TIMESPENT(); result |= plumbing_ipv4().peering_went_down(peer_handler); TIMESPENT_CHECK(); #ifdef HAVE_IPV6 result |= plumbing_ipv6().peering_went_down(peer_handler); TIMESPENT_CHECK(); #endif return result; } int BGPPlumbing::peering_came_up(PeerHandler* peer_handler) { debug_msg("BGPPlumbing::peering_came_up\n"); int result = 0; result |= plumbing_ipv4().peering_came_up(peer_handler); #ifdef HAVE_IPV6 result |= plumbing_ipv6().peering_came_up(peer_handler); #endif return result; } int BGPPlumbing::delete_peering(PeerHandler* peer_handler) { debug_msg("BGPPlumbing::delete_peering\n"); int result = 0; result |= plumbing_ipv4().delete_peering(peer_handler); #ifdef HAVE_IPV6 result |= plumbing_ipv6().delete_peering(peer_handler); #endif return result; } void BGPPlumbing::flush(PeerHandler* peer_handler) { debug_msg("BGPPlumbing::flush\n"); plumbing_ipv4().flush(peer_handler); #ifdef HAVE_IPV6 plumbing_ipv6().flush(peer_handler); #endif } int BGPPlumbing::add_route(const IPv4Net& net, FPAList4Ref& pa_list, const PolicyTags& policy_tags, PeerHandler* peer_handler) { debug_msg("BGPPlumbing::add_route IPv4\n"); PROFILE(if (main().profile().enabled(profile_route_ribin)) main().profile().log(profile_route_ribin, c_format("add %s", net.str().c_str()))); XLOG_ASSERT(!pa_list->is_locked()); return plumbing_ipv4().add_route(net, pa_list, policy_tags, peer_handler); } int BGPPlumbing::delete_route(InternalMessage &rtmsg, PeerHandler* peer_handler) { PROFILE(if (main().profile().enabled(profile_route_ribin)) main().profile().log(profile_route_ribin, c_format("delete %s", rtmsg.net().str().c_str()))); return plumbing_ipv4().delete_route(rtmsg, peer_handler); } int BGPPlumbing::delete_route(const IPNet& net, PeerHandler* peer_handler) { PROFILE(if (main().profile().enabled(profile_route_ribin)) main().profile().log(profile_route_ribin, c_format("delete %s", net.str().c_str()))); return plumbing_ipv4().delete_route(net, peer_handler); } const SubnetRoute* BGPPlumbing::lookup_route(const IPNet &net) const { return const_cast(this)-> plumbing_ipv4().lookup_route(net); } template<> void BGPPlumbing::push(PeerHandler* peer_handler) { debug_msg("BGPPlumbing::push\n"); plumbing_ipv4().push(peer_handler); } void BGPPlumbing::output_no_longer_busy(PeerHandler *peer_handler) { plumbing_ipv4().output_no_longer_busy(peer_handler); #ifdef HAVE_IPV6 plumbing_ipv6().output_no_longer_busy(peer_handler); #endif } uint32_t BGPPlumbing::get_prefix_count(const PeerHandler *peer_handler) { return plumbing_ipv4(). get_prefix_count(const_cast(peer_handler)) #ifdef HAVE_IPV6 + plumbing_ipv6(). get_prefix_count(const_cast(peer_handler)) #endif ; } template<> uint32_t BGPPlumbing::create_route_table_reader(const IPNet& prefix) { return plumbing_ipv4().create_route_table_reader(prefix); } bool BGPPlumbing::read_next_route(uint32_t token, const SubnetRoute*& route, IPv4& peer_id) { return plumbing_ipv4().read_next_route(token, route, peer_id); } bool BGPPlumbing::status(string& reason) const { if (const_cast(this)-> plumbing_ipv4().status(reason) == false) { return false; } #ifdef HAVE_IPV6 if (const_cast(this)-> plumbing_ipv6().status(reason) == false) { return false; } #endif return true; } void BGPPlumbing::push_routes() { plumbing_ipv4().push_routes(); #ifdef HAVE_IPV6 plumbing_ipv6().push_routes(); #endif } /***********************************************************************/ template BGPPlumbingAF::BGPPlumbingAF(const string& ribname, BGPPlumbing& master, NextHopResolver& next_hop_resolver) : _ribname(ribname), _master(master), _next_hop_resolver(next_hop_resolver) { debug_msg("BGPPlumbingAF constructor called for RIB %s\n", ribname.c_str()); _awaits_push = false; //We want to seed the route table reader token so that if BGP //restarts, an old token is unlikely to be accepted. _max_reader_token = getpid() << 16; /* * Initial plumbing is: * * DecisionTable -> FanoutTable -> FilterTable -> .. * ..-> RibOutTable -> RibIpcHandler * * This is the path taken by routes that we propagate to the * RIB process for local use. * * All the plumbing regarding BGP Peers gets added later. * * The RibIpcHandler resides in the master plumbing class. The * rest is AF specific, so resides here. */ _decision_table = new DecisionTable(ribname + "DecisionTable", _master.safi(), _next_hop_resolver); _next_hop_resolver.add_decision(_decision_table); _policy_sourcematch_table = new PolicyTableSourceMatch(ribname + "PolicyExportSourceMatchTable", _master.safi(), _decision_table, _master.policy_filters(), _master.main().eventloop()); _decision_table->set_next_table(_policy_sourcematch_table); _aggregation_table = new AggregationTable(ribname + "AggregationTable", _master, _policy_sourcematch_table); _policy_sourcematch_table->set_next_table(_aggregation_table); _fanout_table = new FanoutTable(ribname + "FanoutTable", _master.safi(), _aggregation_table, _master.aggr_handler(), _aggregation_table); _aggregation_table->set_next_table(_fanout_table); /* * Plumb the input branch */ _ipc_rib_in_table = new RibInTable(_ribname + "IpcRibInTable", _master.safi(), _master.rib_handler()); _in_map[_master.rib_handler()] = _ipc_rib_in_table; FilterTable* filter_in = new FilterTable(_ribname + "IpcChannelInputFilter", _master.safi(), _ipc_rib_in_table, _next_hop_resolver); filter_in->do_versioning(); _ipc_rib_in_table->set_next_table(filter_in); PolicyTableImport* policy_filter_in = new PolicyTableImport(_ribname + "IpcChannelInputPolicyFilter", _master.safi(), filter_in, _master.policy_filters(), A(), A()); filter_in->set_next_table(policy_filter_in); // No policy import filters on routes coming from the RIB. // XXX We still need the table to do the necessary policy route dump voodoo // though. policy_filter_in->enable_filtering(false); CacheTable* cache_in = new CacheTable(_ribname + "IpcChannelInputCache", _master.safi(), policy_filter_in, _master.rib_handler()); policy_filter_in->set_next_table(cache_in); NhLookupTable *nexthop_in = new NhLookupTable(_ribname + "IpcChannelNhLookup", _master.safi(), &_next_hop_resolver, cache_in); cache_in->set_next_table(nexthop_in); nexthop_in->set_next_table(_decision_table); _decision_table->add_parent(nexthop_in, _master.rib_handler(), _ipc_rib_in_table->genid()); _tables.insert(filter_in); _tables.insert(policy_filter_in); _tables.insert(cache_in); _tables.insert(nexthop_in); /* * Plumb the output branch */ FilterTable *filter_out = new FilterTable(ribname + "IpcChannelOutputFilter", _master.safi(), _fanout_table, _next_hop_resolver); _tables.insert(filter_out); XLOG_ASSERT(_master.rib_handler()); // No policy export filters on routes destined to the RIB // Drop in an aggregation filter - beheave like an IBGP peering filter_out->add_aggregation_filter(true); #if 0 // // outgoing caches are obsolete // CacheTable *cache_out = new CacheTable(ribname + "IpcChannelOutputCache", _master.safi(), filter_out, _master.rib_handler()); filter_out->set_next_table(cache_out); _tables.insert(cache_out); #endif _ipc_rib_out_table = new RibOutTable(ribname + "IpcRibOutTable", _master.safi(), filter_out, _master.rib_handler()); _out_map[_master.rib_handler()] = _ipc_rib_out_table; filter_out->set_next_table(_ipc_rib_out_table); _fanout_table->add_next_table(filter_out, _master.rib_handler(), _ipc_rib_in_table->genid()); } template BGPPlumbingAF::~BGPPlumbingAF() { typename set *>::iterator i; for(i = _tables.begin(); i != _tables.end(); i++) { delete (*i); } delete _decision_table; delete _policy_sourcematch_table; delete _fanout_table; delete _ipc_rib_in_table; delete _ipc_rib_out_table; } template void BGPPlumbingAF::configure_inbound_filter(PeerHandler* peer_handler, FilterTable* filter_in) { PeerType peer_type = peer_handler->get_peer_type(); AsNum my_AS_number = peer_handler->my_AS_number(); /* 1. configure the loop filters */ filter_in->add_simple_AS_filter(my_AS_number); /* 2. Configure local preference filter. Add LOCAL_PREF on receipt from EBGP peer, or when originating. */ if ( peer_type == PEER_TYPE_EBGP || peer_type == PEER_TYPE_EBGP_CONFED || peer_type == PEER_TYPE_INTERNAL ) { filter_in->add_localpref_insertion_filter( LocalPrefAttribute::default_value() ); } /* 3. If this router is a route reflector configure the in bound filters */ LocalData *local_data = _master.main().get_local_data(); if (local_data->get_route_reflector()) { if (peer_type == PEER_TYPE_IBGP || peer_type == PEER_TYPE_IBGP_CLIENT) { IPv4 bgp_id = local_data->get_id(); IPv4 cluster_id = local_data->get_cluster_id(); filter_in->add_route_reflector_input_filter(bgp_id, cluster_id); } } } template void BGPPlumbingAF::configure_outbound_filter(PeerHandler* peer_handler, FilterTable* filter_out) { const AsNum& his_AS_number = peer_handler->AS_number(); const AsNum& my_AS_number = peer_handler->my_AS_number(); PeerType peer_type = peer_handler->get_peer_type(); A my_nexthop(get_local_nexthop(peer_handler)); /* 1. configure aggregation filters */ filter_out->add_aggregation_filter(peer_handler->ibgp()); /* 1.1 configure the loop filters */ filter_out->add_simple_AS_filter(his_AS_number); /* 2. configure as_prepend filters for EBGP peers*/ if (peer_type == PEER_TYPE_EBGP) { filter_out->add_AS_prepend_filter(my_AS_number, false); } if (peer_type == PEER_TYPE_EBGP_CONFED) { filter_out->add_AS_prepend_filter(my_AS_number, true); } /* 2.1 For routes that we originate add our AS if its not already (EBGP peers) present. */ filter_out->add_originate_route_filter(my_AS_number, peer_type); /* 3. Configure MED filter. Remove old MED and add new one on transmission to EBGP peers. */ /* Note: this MUST come before the nexthop rewriter */ if (peer_type != PEER_TYPE_IBGP && peer_type != PEER_TYPE_IBGP_CLIENT) { /* By default, we remove the old MED unless we're sending to an IBGP neighbour */ /* RFC 3065 says we can legally send MED to EBGP_CONFED neighbours, but it's not clear that this is a good default. We choose not to do so - this is safe . */ filter_out->add_med_removal_filter(); } if (peer_type == PEER_TYPE_EBGP) { /* we only add MED when sending to an EBGP peer (not an EBGP confed peer - that doesn't seem to be legal). */ filter_out->add_med_insertion_filter(); } /* 4. configure next_hop rewriter for EBGP peers*/ IPNet subnet; A peer; bool direct = directly_connected(peer_handler, subnet, peer); if (peer_type == PEER_TYPE_EBGP) { filter_out->add_nexthop_rewrite_filter(my_nexthop, direct, subnet); } /* 4.a If the route was originated by this router and the nexthop and the peer address are equal rewrite the nexthop. */ filter_out->add_nexthop_peer_check_filter(my_nexthop, peer); /* 5. Configure local preference filter. Remove LOCAL_PREF on transmission to EBGP peers. */ // Note it is legal to send local pref to a confederation peer. if (peer_type == PEER_TYPE_EBGP) { filter_out->add_localpref_removal_filter(); } /* 6. configure loop filter for IBGP peers, unless this router is a route reflector */ LocalData *local_data = _master.main().get_local_data(); if (local_data->get_route_reflector()) { if (peer_type == PEER_TYPE_IBGP || peer_type == PEER_TYPE_IBGP_CLIENT) { bool client = peer_type == PEER_TYPE_IBGP_CLIENT; IPv4 bgp_id = local_data->get_id(); IPv4 cluster_id = local_data->get_cluster_id(); filter_out->add_route_reflector_ibgp_loop_filter(client, bgp_id, cluster_id); } } else { if (peer_type == PEER_TYPE_IBGP) { filter_out->add_ibgp_loop_filter(); } } // 6.1. Remove route reflector ORIGINATOR_ID and CLUSTER_LIST // attributes that we may have learned from a peer when sending // to an EBGP peer. Route reflector attributes are IBGP specific. if (peer_type == PEER_TYPE_EBGP || peer_type == PEER_TYPE_EBGP_CONFED) { filter_out->add_route_reflector_purge_filter(); } /* 7. configure filter for well-known communities */ filter_out->add_known_community_filter(peer_type); /* 8. Process unknown attributes */ filter_out->add_unknown_filter(); } /** * @short re-instantiate all the static filters. * * Re-instantiate all the static filters in case the config has * changed, and the filter parameters are now different. The * FilterTable will handle consistency issues this might otherwise * raise. */ template void BGPPlumbingAF::reconfigure_filters(PeerHandler* peer_handler) { /* We need to find the outbound filter table */ BGPRouteTable *rt; typename map *>::iterator iter; iter = _out_map.find(peer_handler); if (iter == _out_map.end()) XLOG_FATAL("BGPPlumbingAF::reconfigure_filters: peer %p not found", XORP_UINT_CAST(A::ip_version()), pretty_string_safi(_master.safi()), peer_handler); rt = iter->second; while (1) { XLOG_ASSERT(rt != _fanout_table); if (rt->type() == FILTER_TABLE) { FilterTable *filter_table = (FilterTable *)rt; /* tell the filter table to checkpoint state, and be ready for reconfiguration */ filter_table->reconfigure_filter(); /* add new filters */ configure_outbound_filter(peer_handler, filter_table); break; } rt = rt->parent(); } typename map * >::iterator iter2; iter2 = _in_map.find(peer_handler); if (iter2 == _in_map.end()) XLOG_FATAL("BGPPlumbingAF::reconfigure_filters: peer %p not found", XORP_UINT_CAST(A::ip_version()), pretty_string_safi(_master.safi()), peer_handler); rt = iter2->second; while (1) { XLOG_ASSERT(rt != _decision_table); if (rt->type() == FILTER_TABLE) { FilterTable *filter_table = (FilterTable *)rt; /* tell the filter table to checkpoint state, and be ready for reconfiguration */ filter_table->reconfigure_filter(); /* add new filters */ configure_inbound_filter(peer_handler, filter_table); break; } rt = rt->next_table(); } } template int BGPPlumbingAF::add_peering(PeerHandler* peer_handler) { /* * A new peer just came up. We need to create all the RouteTables * to handle taking routes from this peer, and sending routes out * to the peer. * * New plumbing: * RibInTable -> DampingTable -> FilterTable -> CacheTable ->.. * ..-> NhLookupTable -> DecisionTable. * * FanoutTable -> FilterTable -> CacheTable ->.. * ..-> RibOutTable -> PeerHandler. * */ string peername(peer_handler->peername()); /* * Plumb the input branch */ RibInTable* rib_in = new RibInTable(_ribname + "RibIn" + peername, _master.safi(), peer_handler); _in_map[peer_handler] = rib_in; DampingTable* damping_in = new DampingTable(_ribname + "Damping" + peername, _master.safi(), rib_in, peer_handler, _master.main().get_local_data()->get_damping()); rib_in->set_next_table(damping_in); FilterTable* filter_in = new FilterTable(_ribname + "PeerInputFilter" + peername, _master.safi(), damping_in, _next_hop_resolver); filter_in->do_versioning(); damping_in->set_next_table(filter_in); A peer_addr; peer_handler->get_peer_addr(peer_addr); // XXX add get methods // XXX what do we do with IPv6? A self_addr; try { self_addr = A(peer_handler->get_local_addr().c_str()); } catch (...) { } PolicyTableImport* policy_filter_in = new PolicyTableImport(_ribname + "PeerInputPolicyFilter" + peername, _master.safi(), filter_in, _master.policy_filters(), peer_addr, self_addr); filter_in->set_next_table(policy_filter_in); CacheTable* cache_in = new CacheTable(_ribname + "PeerInputCache" + peername, _master.safi(), policy_filter_in, peer_handler); policy_filter_in->set_next_table(cache_in); NhLookupTable *nexthop_in = new NhLookupTable(_ribname + "NhLookup" + peername, _master.safi(), &_next_hop_resolver, cache_in); cache_in->set_next_table(nexthop_in); nexthop_in->set_next_table(_decision_table); _decision_table->add_parent(nexthop_in, peer_handler, rib_in->genid()); _tables.insert(rib_in); _tables.insert(filter_in); _tables.insert(policy_filter_in); _tables.insert(cache_in); _tables.insert(nexthop_in); /* * Start things up on the input branch */ /* 1. configure filters */ configure_inbound_filter(peer_handler, filter_in); /* 2. cause all the other peerings to know about this one */ rib_in->ribin_peering_came_up(); /* * Plumb the output branch */ FilterTable* filter_out = new FilterTable(_ribname + "PeerOutputFilter" + peername, _master.safi(), _fanout_table, _next_hop_resolver); PolicyTable* policy_filter_out = new PolicyTableExport(_ribname + "PeerOutputPolicyFilter" + peername, _master.safi(), filter_out, _master.policy_filters(), peer_handler->get_peer_addr(), self_addr); filter_out->set_next_table(policy_filter_out); #if 0 // // outbound cache tables are obsolete // CacheTable* cache_out = new CacheTable(_ribname + "PeerOutputCache" + peername, _master.safi(), policy_filter_out, peer_handler); policy_filter_out->set_next_table(cache_out); #endif RibOutTable* rib_out = new RibOutTable(_ribname + "RibOut" + peername, _master.safi(), policy_filter_out, peer_handler); policy_filter_out->set_next_table(rib_out); _out_map[peer_handler] = rib_out; _reverse_out_map[rib_out] = peer_handler; _tables.insert(filter_out); _tables.insert(policy_filter_out); _tables.insert(rib_out); /* * Start things up on the output branch */ /* 1. configure filters */ configure_outbound_filter(peer_handler, filter_out); /* 2. load up damping filters */ /* TBD */ /* 3. finally plumb in the output branch */ _fanout_table->add_next_table(filter_out, peer_handler, rib_in->genid()); /* 4. cause the routing table to be dumped to the new peer */ dump_entire_table(filter_out, _ribname); if (_awaits_push) push(peer_handler); return 0; } template int BGPPlumbingAF::stop_peering(PeerHandler* peer_handler) { /* Work our way back to the fanout table from the RibOut so we can find the relevant output from the fanout table. On the way, flush any caches we find. */ BGPRouteTable *rt, *prevrt; typename map *>::iterator iter; iter = _out_map.find(peer_handler); if (iter == _out_map.end()) XLOG_FATAL("BGPPlumbingAF::stop_peering: peer %p not found", XORP_UINT_CAST(A::ip_version()), pretty_string_safi(_master.safi()), peer_handler); rt = iter->second; prevrt = rt; while (rt != _fanout_table) { debug_msg("rt=%p (%s), _fanout_table=%p\n", rt, rt->tablename().c_str(), _fanout_table); if (rt->type() == CACHE_TABLE) ((CacheTable*)rt)->flush_cache(); prevrt = rt; rt = rt->parent(); if (rt == NULL) { //peering was already stopped. This can happen when we're //doing an ALLSTOP. // XLOG_WARNING("BGPPlumbingAF::stop_peering: " // "NULL parent table in stop_peering", // A::ip_version()); return 0; } } uint32_t genid; typename map * >::iterator iter2; iter2 = _in_map.find(peer_handler); if (iter2 == _in_map.end()) XLOG_FATAL("BGPPlumbingAF::peering_went_down: peer %p not found", peer_handler); RibInTable *rib_in; rib_in = iter2->second; genid = rib_in->genid(); prevrt->peering_went_down(peer_handler, genid, _fanout_table); prevrt->set_parent(NULL); _fanout_table->remove_next_table(prevrt); return 0; } template int BGPPlumbingAF::peering_went_down(PeerHandler* peer_handler) { typename map * >::iterator iter; iter = _in_map.find(peer_handler); if (iter == _in_map.end()) XLOG_FATAL("BGPPlumbingAF::peering_went_down: peer %p not found", peer_handler); RibInTable *rib_in; rib_in = iter->second; //peering went down will be propagated downstream by the RIB-In. TIMESPENT(); rib_in->ribin_peering_went_down(); TIMESPENT_CHECK(); //stop_peering shuts down and disconnects the output branch for this peer stop_peering(peer_handler); TIMESPENT_CHECK(); /* we don't flush the input caches - lookup requests should still be answered until the DeletionTable gets round to telling the downstream tables that the route has been deleted */ return 0; } template int BGPPlumbingAF::peering_came_up(PeerHandler* peer_handler) { reconfigure_filters(peer_handler); //bring the RibIn back up typename map * >::iterator iter2; iter2 = _in_map.find(peer_handler); if (iter2 == _in_map.end()) XLOG_FATAL("BGPPlumbingAF::peering_went_down: peer %p not found", peer_handler); RibInTable *rib_in; rib_in = iter2->second; rib_in->ribin_peering_came_up(); //plumb the output branch back into the fanout table BGPRouteTable *rt, *prevrt; typename map *>::iterator iter; iter = _out_map.find(peer_handler); if (iter == _out_map.end()) XLOG_FATAL("BGPPlumbingAF::peering_came_up: peer %p not found", peer_handler); rt = iter->second; prevrt = rt; while (rt != NULL) { debug_msg("rt=%p (%s), _fanout_table=%p\n", rt, rt->tablename().c_str(), _fanout_table); prevrt = rt; rt = rt->parent(); } debug_msg("type = %d", prevrt->type()); FilterTable *filter_out = dynamic_cast *>(prevrt); XLOG_ASSERT(filter_out != NULL); filter_out->set_parent(_fanout_table); _fanout_table->add_next_table(filter_out, peer_handler, rib_in->genid()); //do the route dump dump_entire_table(filter_out, _ribname); if (_awaits_push) push(peer_handler); return 0; } template int BGPPlumbingAF::delete_peering(PeerHandler* peer_handler) { debug_msg("BGPPlumbingAF::drop_peering\n"); BGPRouteTable *rt, *parent, *child; /* * Steps: * 1. remove the relevant next_table link from the fanout table. * 2. delete add the routes in the RIB_In. * 3. remove the relevant parent table link from the decision table. * 4. tear down the state */ /* * Step 1 - remove the relevant next_table link from the fanout table. * This stops us being able to send any updates to this peer. * We find the relevant link by tracking back from the RibOut. */ stop_peering(peer_handler); /* * Step 2 - delete all the affected routes */ peering_went_down(peer_handler); /* * Step 3 - remove the relevant parent link from the decision table */ typename map * >::iterator iter2; iter2 = _in_map.find(peer_handler); child = iter2->second; rt = child; while(child != _decision_table) { rt = child; child = rt->next_table(); } _decision_table->remove_parent(rt); /* Step 4 - tear down all the tables for this peer */ rt = iter2->second; while(rt != _decision_table) { child = rt->next_table(); _tables.erase(rt); delete rt; rt = child; } typename map *>::iterator iter; iter = _out_map.find(peer_handler); if (iter == _out_map.end()) XLOG_FATAL("BGPPlumbingAF::drop_peering: peer %p not found", peer_handler); iter = _out_map.find(peer_handler); rt = iter->second; while(rt != NULL) { parent = rt->parent(); if (rt->type() == CACHE_TABLE) ((CacheTable*)rt)->flush_cache(); _tables.erase(rt); delete rt; rt = parent; } return 0; } template void BGPPlumbingAF::dump_entire_table(FilterTable *filter_out, string ribname) { debug_msg("BGPPlumbingAF::dump_entire_table\n", XORP_UINT_CAST(A::ip_version()), pretty_string_safi(_master.safi())); _fanout_table->dump_entire_table(filter_out, _master.safi(), ribname); DumpTable *dump_table = dynamic_cast *>(filter_out->parent()); XLOG_ASSERT(dump_table); /* ** It is possible that another peer was in the middle of going ** down when this peering came up. So sweep through the peers and ** look for the deletion tables. Deletion tables can be ** nested. Treat them as if they have just sent a ** peering_went_down. */ typename map * >::iterator iter2; for (iter2 = _in_map.begin(); iter2 != _in_map.end(); iter2++) { RibInTable *rib_in = iter2->second; debug_msg("<%s>\n", rib_in->next_table()->str().c_str()); DeletionTable *deletion_table = dynamic_cast *>(rib_in->next_table()); while (0 != deletion_table) { debug_msg("Found a deletion table\n"); dump_table->peering_is_down(iter2->first, deletion_table->genid()); deletion_table = dynamic_cast *>(deletion_table->next_table()); } } } template void BGPPlumbingAF::flush(PeerHandler* peer_handler) { debug_msg("BGPPlumbingAF::flush\n", XORP_UINT_CAST(A::ip_version()), pretty_string_safi(_master.safi())); RibInTable *rib_in; typename map * >::iterator iter; iter = _in_map.find(peer_handler); if (iter == _in_map.end()) XLOG_FATAL("BGPPlumbingAF: " "flush called for a PeerHandler " "that has no associated RibIn", XORP_UINT_CAST(A::ip_version()), pretty_string_safi(_master.safi())); rib_in = iter->second; // Only allow flushing of static routes XLOG_ASSERT(rib_in == _ipc_rib_in_table); _ipc_rib_in_table->flush(); } template int BGPPlumbingAF::add_route(const IPNet& net, FPAListRef& pa_list, const PolicyTags& policy_tags, PeerHandler* peer_handler) { debug_msg("BGPPlumbingAF::add_route\n", XORP_UINT_CAST(A::ip_version()), pretty_string_safi(_master.safi())); int result = 0; RibInTable *rib_in; typename map * >::iterator iter; iter = _in_map.find(peer_handler); if (iter == _in_map.end()) XLOG_FATAL("BGPPlumbingAF: " "add_route called for a PeerHandler " "that has no associated RibIn", XORP_UINT_CAST(A::ip_version()), pretty_string_safi(_master.safi())); rib_in = iter->second; try { result = rib_in->add_route(net, pa_list, policy_tags); } catch(XorpException &e) { XLOG_WARNING("Exception in add_route: %s, assuming failure\n", e.str().c_str()); result = ADD_FAILURE; } if (result == ADD_USED || result == ADD_UNUSED) { _awaits_push = true; } else { //XXX the add_route returned an error. //Do we want to proactively send a push now? } return result; } template int BGPPlumbingAF::delete_route(InternalMessage &rtmsg, PeerHandler* peer_handler) { int result = 0; RibInTable *rib_in; typename map * >::iterator iter; iter = _in_map.find(peer_handler); if (iter == _in_map.end()) XLOG_FATAL("BGPPlumbingAF: delete_route called for a \ PeerHandler that has no associated RibIn"); rib_in = iter->second; result = rib_in->delete_route(rtmsg, NULL); if (rtmsg.push() == false) { if (result == 0) { _awaits_push = true; } else { //XXX the delete_route returned an error. //Do we want to proactively send a push now? } } return result; } template int BGPPlumbingAF::delete_route(const IPNet& net, PeerHandler* peer_handler) { int result = 0; RibInTable *rib_in; typename map * >::iterator iter; iter = _in_map.find(peer_handler); if (iter == _in_map.end()) XLOG_FATAL("BGPPlumbingAF: delete_route called for a \ PeerHandler that has no associated RibIn"); rib_in = iter->second; #if 0 // no need to go to all the effort to create a full message here, // as we'll do the lookup in ribin anyway uint32_t genid; FPAListRef pa_list; const SubnetRoute *found_route = rib_in->lookup_route(net, genid, pa_list); if (found_route == NULL) { XLOG_WARNING("Attempt to delete non existent route %s", net.str().c_str()); return result; } InternalMessage rtmsg(found_route, pa_list, peer_handler, GENID_UNKNOWN); result = rib_in->delete_route(rtmsg, NULL); #endif result = rib_in->delete_route(net); return result; } template void BGPPlumbingAF::push(PeerHandler* peer_handler) { debug_msg("BGPPlumbingAF::push\n", XORP_UINT_CAST(A::ip_version()), pretty_string_safi(_master.safi())); if (_awaits_push == false) { XLOG_WARNING("push when none needed", XORP_UINT_CAST(A::ip_version()), pretty_string_safi(_master.safi())); return; } RibInTable *rib_in; typename map * >::iterator iter; iter = _in_map.find(peer_handler); if (iter == _in_map.end()) XLOG_FATAL("BGPPlumbingAF: Push called for a PeerHandler \ that has no associated RibIn"); rib_in = iter->second; rib_in->push(NULL); } template void BGPPlumbingAF::output_no_longer_busy(PeerHandler *peer_handler) { RibOutTable *rib_out; typename map * >::iterator iter; iter = _out_map.find(peer_handler); if (iter == _out_map.end()) XLOG_FATAL("BGPPlumbingAF: output_no_longer_busy called for a \ PeerHandler that has no associated RibOut"); rib_out = iter->second; rib_out->output_no_longer_busy(); } template const SubnetRoute* BGPPlumbingAF::lookup_route(const IPNet &net) const { //lookup_route returns the route currently being told to the RIB. //It's possible this differs from the route we tell a peer, //because of output filters that may modify attributes. uint32_t genid; FPAListRef pa_list; return _ipc_rib_out_table->lookup_route(net, genid, pa_list); } template uint32_t BGPPlumbingAF::get_prefix_count(PeerHandler* peer_handler) const { typename map * >::const_iterator iter; iter = _in_map.find(peer_handler); if (iter == _in_map.end()) XLOG_FATAL("BGPPlumbingAF: Get prefix count for a PeerHandler \ that has no associated RibIn"); return iter->second->route_count(); } template <> const IPv4& BGPPlumbingAF::get_local_nexthop(const PeerHandler *peerhandler) const { return peerhandler->my_v4_nexthop(); } template <> bool BGPPlumbingAF::directly_connected(const PeerHandler *peer_handler, IPNet& subnet, IPv4& p) const { // Get the routers interface address. // There is no reason for the transport address for the session to // match the address family, hence the necessity to catch a // possible exception (IPv6 session with IPv4 AFI). try { IPv4 local(peer_handler->get_local_addr().c_str()); IPv4 peer(peer_handler->get_peer_addr().c_str()); uint32_t prefix_len; if (!_master.main().interface_address_prefix_len4(local, prefix_len)) return false; IPNet net(local, prefix_len); if (net.contains(peer)) { subnet = net; p = peer; return true; } } catch(...) { return false; } return false; } template list *> BGPPlumbingAF::ribin_list() const { list *> _ribin_list; typename map * >::const_iterator i; for (i = _in_map.begin(); i != _in_map.end(); i++) { _ribin_list.push_back(i->second); } return _ribin_list; } template uint32_t BGPPlumbingAF::create_route_table_reader(const IPNet& prefix) { //Generate a new token that can't clash with any in use, even if //the space wraps. _max_reader_token++; while (_route_table_readers.find(_max_reader_token) != _route_table_readers.end()) { _max_reader_token++; } RouteTableReader *new_reader = new RouteTableReader(ribin_list(), prefix); _route_table_readers[_max_reader_token] = new_reader; return _max_reader_token; } template bool BGPPlumbingAF::read_next_route(uint32_t token, const SubnetRoute*& route, IPv4& peer_id) { typename map *>::iterator i; i = _route_table_readers.find(token); if (i == _route_table_readers.end()) return false; RouteTableReader *_reader = i->second; bool result = _reader->get_next(route, peer_id); if (result == false) { //we've finished reading the routing table. _route_table_readers.erase(i); delete _reader; } return result; } template bool BGPPlumbingAF::status(string&) const { return true; } template void BGPPlumbingAF::push_routes() { list*> peer_list; _fanout_table->peer_table_info(peer_list); _policy_sourcematch_table->push_routes(peer_list); /* ** It is possible that another peer was in the middle of going ** down when this peering came up. So sweep through the peers and ** look for the deletion tables. Deletion tables can be ** nested. Treat them as if they have just sent a ** peering_went_down. */ typename map * >::iterator iter2; for (iter2 = _in_map.begin(); iter2 != _in_map.end(); iter2++) { RibInTable *rib_in = iter2->second; debug_msg("<%s>\n", rib_in->next_table()->str().c_str()); DeletionTable *deletion_table = dynamic_cast *>(rib_in->next_table()); while (0 != deletion_table) { debug_msg("Found a deletion table\n"); _policy_sourcematch_table ->peering_is_down(iter2->first, deletion_table->genid()); deletion_table = dynamic_cast *>(deletion_table->next_table()); } } } template class BGPPlumbingAF; /** IPv6 stuff */ #ifdef HAVE_IPV6 int BGPPlumbing::add_route(const IPv6Net& net, FPAList6Ref& pa_list, const PolicyTags& policy_tags, PeerHandler* peer_handler) { debug_msg("BGPPlumbing::add_route IPv6\n"); PROFILE(if (main().profile().enabled(profile_route_ribin)) main().profile().log(profile_route_ribin, c_format("add %s", net.str().c_str()))); XLOG_ASSERT(!pa_list->is_locked()); return plumbing_ipv6().add_route(net, pa_list, policy_tags, peer_handler); } int BGPPlumbing::delete_route(InternalMessage &rtmsg, PeerHandler* peer_handler) { PROFILE(if (main().profile().enabled(profile_route_ribin)) main().profile().log(profile_route_ribin, c_format("delete %s", rtmsg.net().str().c_str()))); return plumbing_ipv6().delete_route(rtmsg, peer_handler); } int BGPPlumbing::delete_route(const IPNet& net, PeerHandler* peer_handler) { PROFILE(if (main().profile().enabled(profile_route_ribin)) main().profile().log(profile_route_ribin, c_format("delete %s", net.str().c_str()))); return plumbing_ipv6().delete_route(net, peer_handler); } const SubnetRoute* BGPPlumbing::lookup_route(const IPNet &net) const { return const_cast(this)-> plumbing_ipv6().lookup_route(net); } template<> void BGPPlumbing::push(PeerHandler* peer_handler) { debug_msg("BGPPlumbing::push\n"); plumbing_ipv6().push(peer_handler); } template<> uint32_t BGPPlumbing::create_route_table_reader(const IPNet& prefix) { return plumbing_ipv6().create_route_table_reader(prefix); } bool BGPPlumbing::read_next_route(uint32_t token, const SubnetRoute*& route, IPv4& peer_id) { return plumbing_ipv6().read_next_route(token, route, peer_id); } template <> const IPv6& BGPPlumbingAF::get_local_nexthop(const PeerHandler *peerhandler) const { return peerhandler->my_v6_nexthop(); } template <> bool BGPPlumbingAF::directly_connected(const PeerHandler *peer_handler, IPNet& subnet, IPv6& p) const { // Get the routers interface address. // There is no reason for the transport address for the session to // match the address family, hence the necessity to catch a // possible exception (IPv4 session with IPv6 AFI). try { IPv6 local(peer_handler->get_local_addr().c_str()); IPv6 peer(peer_handler->get_peer_addr().c_str()); uint32_t prefix_len; if (!_master.main().interface_address_prefix_len6(local, prefix_len)) return false; IPNet net(local, prefix_len); if (net.contains(peer)) { subnet = net; p = peer; return true; } } catch(...) { return false; } return false; } template class BGPPlumbingAF; #endif // ipv6 xorp/bgp/update_attrib.hh0000664000076400007640000000645211540224220015577 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/bgp/update_attrib.hh,v 1.16 2008/10/02 21:56:23 bms Exp $ #ifndef __BGP_UPDATE_ATTRIB_HH__ #define __BGP_UPDATE_ATTRIB_HH__ #include "libxorp/ipvxnet.hh" #include "libxorp/exceptions.hh" #include "exceptions.hh" /** * Encoding used in BGP update packets to encode prefixes * (IPv4 only) for withdrawn routes and NLRI information. * * The prefixes are passed on the wire in a compressed format: * 1 byte: prefix length L (in bits) * n bytes: prefix, L bits (n = (L+7)/8) * * Effectively, this class is just an IPv4Net. * We only need methods to encode and decode the objects. */ class BGPUpdateAttrib : public IPv4Net { public: /** * construct from an address d and mask length s */ BGPUpdateAttrib(const IPv4& d, uint8_t s) : IPv4Net(d, s) {} BGPUpdateAttrib(const IPv4Net& p) : IPv4Net(p) {} #ifdef XORP_USE_USTL BGPUpdateAttrib() { } #endif /** * Construct from wire format */ BGPUpdateAttrib(const uint8_t *d); /** * store in memory in wire format */ void copy_out(uint8_t *d) const; /** * total size in encoded format */ size_t wire_size() const { return calc_byte_size() + 1; } // size of next operand in memory static size_t size(const uint8_t *d) throw(CorruptMessage); size_t calc_byte_size() const { return (prefix_len() + 7) / 8; } const IPv4Net& net() const { return (IPv4Net &)(*this); } string str(string nlri_or_withdraw) const { return nlri_or_withdraw + " " + net().str(); } protected: private: }; class BGPUpdateAttribList : public list { public: typedef list ::const_iterator const_iterator; typedef list ::iterator iterator; size_t wire_size() const; uint8_t *encode(size_t &l, uint8_t *buf = 0) const; void decode(const uint8_t *d, size_t len) throw(CorruptMessage); string str(string) const; // XXX this needs to be fixed, we do not want to sort all the times. bool operator== (const BGPUpdateAttribList& other) { if (size() != other.size()) return false; BGPUpdateAttribList me(*this); BGPUpdateAttribList him(other); me.sort(); him.sort(); const_iterator i, j; // only check one iterator as we know length is the same for (i=me.begin(), j=him.begin(); i!=me.end(); ++i, ++j) if ( *i != *j ) return false; return true; } }; #endif // __BGP_UPDATE_ATTRIB_HH__ xorp/bgp/route_table_policy.cc0000664000076400007640000002337311540224220016623 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #include "bgp_module.h" #include "libxorp/xorp.h" #include "route_table_policy.hh" #include "bgp_varrw.hh" #include "route_table_decision.hh" #include "route_table_ribin.hh" template PolicyTable::PolicyTable(const string& tablename, const Safi& safi, BGPRouteTable* parent, PolicyFilters& pfs, const filter::Filter& type) : BGPRouteTable(tablename, safi), _filter_type(type), _varrw(NULL), _policy_filters(pfs), _enable_filtering(true) { this->_parent = parent; // XXX: For clarity, explicitly call the local virtual init_varrw() PolicyTable::init_varrw(); XLOG_ASSERT(_varrw != NULL); } template PolicyTable::~PolicyTable() { delete _varrw; } template void PolicyTable::enable_filtering(bool on) { _enable_filtering = on; } template bool PolicyTable::do_filtering(InternalMessage& rtmsg, bool no_modify) const { if (!_enable_filtering) { return &rtmsg; } _varrw->attach_route(rtmsg, no_modify); try { bool accepted = true; void* pf = NULL; int pfi = 0; switch (_filter_type) { case filter::IMPORT: pfi = 0; break; case filter::EXPORT_SOURCEMATCH: pfi = 1; break; case filter::EXPORT: pfi = 2; break; } pf = rtmsg.route()->policyfilter(pfi).get(); debug_msg("[BGP] running filter %s on route: %s (filter=%p)\n", filter::filter2str(_filter_type), rtmsg.str().c_str(), pf); accepted = _policy_filters.run_filter(_filter_type, *_varrw); pf = rtmsg.route()->policyfilter(pfi).get(); debug_msg("[BGP] filter after filtering=%p\n", pf); // we just did a filtering, so a filter must be assigned to this route! if (!no_modify) { XLOG_ASSERT(pf); } _varrw->detach_route(rtmsg); if (!accepted) { return false; } return true; } catch(const PolicyException& e) { XLOG_FATAL("Policy filter error %s", e.str().c_str()); XLOG_UNFINISHED(); } } template int PolicyTable::add_route(InternalMessage &rtmsg, BGPRouteTable *caller) { XLOG_ASSERT(caller == this->_parent); BGPRouteTable* next = this->_next_table; XLOG_ASSERT(next); debug_msg("[BGP] PolicyTable %s add_route: %s\n", filter::filter2str(_filter_type), rtmsg.str().c_str()); debug_msg("atts before: %s\n", rtmsg.attributes()->str().c_str()); #if 0 // if we are getting an add_route, it must? be a new route... Thus the // policy filter should be null switch (_filter_type) { case filter::IMPORT: XLOG_ASSERT(rtmsg.route()->policyfilter(0).is_empty()); break; case filter::EXPORT_SOURCEMATCH: XLOG_ASSERT(rtmsg.route()->policyfilter(1).is_empty()); break; // when a new peering comes up, the routes will be sent as an ADD! case filter::EXPORT: // XLOG_ASSERT(rtmsg.route()->policyfilter(2).is_empty()); break; } #endif bool accepted = do_filtering(rtmsg, false); #if 0 if (rtmsg.changed() && fmsg != &rtmsg) { debug_msg("[BGP] PolicyTable got modified route, deleting previous\n"); rtmsg.route()->unref(); } #endif if (!accepted) return ADD_FILTERED; debug_msg("atts after: %s\n", rtmsg.attributes()->str().c_str()); int res = next->add_route(rtmsg, this); return res; } template int PolicyTable::replace_route(InternalMessage& old_rtmsg, InternalMessage& new_rtmsg, BGPRouteTable* caller) { XLOG_ASSERT(caller == this->_parent); BGPRouteTable* next = this->_next_table; XLOG_ASSERT(next); debug_msg("[BGP] PolicyTable %s replace_route: %s\nWith: %s\n", filter::filter2str(_filter_type), old_rtmsg.str().c_str(), new_rtmsg.str().c_str()); bool old_accepted = do_filtering(old_rtmsg, false); bool new_accepted = do_filtering(new_rtmsg, false); // XXX: We can probably use the is_filtered flag... int res; // we didn't pass the old one, we don't want the new one if (!old_accepted && !new_accepted) { return ADD_FILTERED; // we passed the old one, but we filtered the new one... } else if (old_accepted && !new_accepted) { next->delete_route(old_rtmsg, this); res = ADD_FILTERED; // we didn't pass the old one, we like the new one } else if (!old_accepted && new_accepted) { res = next->add_route(new_rtmsg, this); // we passed tthe old one, we like the new one } else { res = next->replace_route(old_rtmsg, new_rtmsg, this); } return res; } template int PolicyTable::delete_route(InternalMessage& rtmsg, BGPRouteTable* caller) { XLOG_ASSERT(caller == this->_parent); BGPRouteTable* next = this->_next_table; XLOG_ASSERT(next); debug_msg("[BGP] PolicyTable %s delete_route: %s\n", filter::filter2str(_filter_type), rtmsg.str().c_str()); debug_msg("atts before: %s\n", rtmsg.attributes()->str().c_str()); #if 0 // it must be an existing route... switch (_filter_type) { case filter::IMPORT: XLOG_ASSERT(!rtmsg.route()->policyfilter(0).is_empty()); break; case filter::EXPORT_SOURCEMATCH: XLOG_ASSERT(!rtmsg.route()->policyfilter(1).is_empty()); break; case filter::EXPORT: XLOG_ASSERT(!rtmsg.route()->policyfilter(2).is_empty()); break; } #endif bool accepted = do_filtering(rtmsg, false); if (!accepted) return 0; debug_msg("atts after: %s\n", rtmsg.attributes()->str().c_str()); int res = next->delete_route(rtmsg, this); return res; } template int PolicyTable::route_dump(InternalMessage& rtmsg, BGPRouteTable* caller, const PeerHandler* dump_peer) { XLOG_ASSERT(caller == this->_parent); BGPRouteTable* next = this->_next_table; XLOG_ASSERT(next); debug_msg("[BGP] PolicyTable %s route_dump: %s\n", filter::filter2str(_filter_type), rtmsg.str().c_str()); #if 0 // it must be an existing route... switch (_filter_type) { case filter::IMPORT: XLOG_ASSERT(!rtmsg.route()->policyfilter(0).is_empty()); break; case filter::EXPORT_SOURCEMATCH: XLOG_ASSERT(!rtmsg.route()->policyfilter(1).is_empty()); break; case filter::EXPORT: XLOG_ASSERT(!rtmsg.route()->policyfilter(2).is_empty()); break; } #endif bool accepted = do_filtering(rtmsg, false); if (!accepted) return ADD_FILTERED; int res = next->route_dump(rtmsg, this, dump_peer); return res; } template int PolicyTable::push(BGPRouteTable* caller) { XLOG_ASSERT(caller == this->_parent); BGPRouteTable* next = this->_next_table; XLOG_ASSERT(next); return next->push(this); } template const SubnetRoute* PolicyTable::lookup_route(const IPNet &net, uint32_t& genid, FPAListRef& pa_list) const { BGPRouteTable* parent = this->_parent; XLOG_ASSERT(parent); const SubnetRoute* found = parent->lookup_route(net, genid, pa_list); if (!found) return NULL; // We need the origin peer for matching neighbor! // XXX lame way of obtaining it: XLOG_ASSERT(_filter_type != filter::EXPORT); BGPRouteTable* root = this->_parent; XLOG_ASSERT(root); while (root->parent() != NULL) root = root->parent(); RibInTable* ribin = dynamic_cast* >(root); XLOG_ASSERT(ribin); InternalMessage rtmsg(found, pa_list, ribin->peer_handler(), genid); debug_msg("[BGP] PolicyTable %s lookup_route: %s\n", filter::filter2str(_filter_type), rtmsg.str().c_str()); #if 0 // it must be an existing route... switch (_filter_type) { case filter::IMPORT: XLOG_ASSERT(!rtmsg.route()->policyfilter(0).is_empty()); break; case filter::EXPORT_SOURCEMATCH: XLOG_ASSERT(!rtmsg.route()->policyfilter(1).is_empty()); break; case filter::EXPORT: XLOG_ASSERT(!rtmsg.route()->policyfilter(2).is_empty()); break; } #endif bool accepted = do_filtering(rtmsg, false); if (!accepted) { return NULL; } return found; } template string PolicyTable::str() const { return "not implemented yet"; } template bool PolicyTable::get_next_message(BGPRouteTable* next_table) { BGPRouteTable* parent = this->_parent; XLOG_ASSERT(parent); XLOG_ASSERT(this->_next_table == next_table); return parent->get_next_message(this); } template void PolicyTable::route_used(const SubnetRoute* rt, bool in_use) { BGPRouteTable* parent = this->_parent; XLOG_ASSERT(parent); parent->route_used(rt, in_use); } template void PolicyTable::init_varrw() { if (_varrw != NULL) delete _varrw; _varrw = new BGPVarRW(filter::filter2str(_filter_type)); } template class PolicyTable; template class PolicyTable; xorp/bgp/damping.hh0000664000076400007640000000576111540225520014375 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __BGP_DAMPING_HH__ #define __BGP_DAMPING_HH__ #include "libxorp/timer.hh" #include "libxorp/eventloop.hh" /* Route Flap Damping (RFC2439) * * The damping parameters plus general support code. */ class Damping { public: static const uint32_t FIXED = 1000; Damping(EventLoop& eventloop); void set_damping(bool damping); bool get_damping() const; void set_half_life(uint32_t half_life); void set_max_hold_down(uint32_t max_hold_down); void set_reuse(uint32_t reuse); void set_cutoff(uint32_t cutoff); /** * Get the current clock tick. */ uint32_t get_tick() const { return _tick; } /** * Merit value to use the first time a route is encountered. */ uint32_t get_merit() const { return FIXED; } /** * Compute the merit value given the last time and merit values. */ uint32_t compute_merit(uint32_t last_time, uint32_t last_merit) const; /** * True of the merit value has passed the cutoff threshold. */ bool cutoff(uint32_t merit) const { return merit > _cutoff; } /** * True of the merit value is above the reuse threshold. */ bool reuse(uint32_t merit) const { return merit > _reuse; } /** * Compute how long the route should be damped in seconds. * The time for this figure of merit to decay to the reuse threshold. */ uint32_t get_reuse_time(uint32_t merit) const; private: EventLoop& _eventloop; bool _damping; // True if damping is enabled. uint32_t _half_life; // Half life in minutes. uint32_t _max_hold_down; // Maximum hold down time in minutes. uint32_t _reuse; // Reuse threshold. uint32_t _cutoff; // Cutoff threshold. vector _decay; // Per tick delay. uint32_t _tick; // incremented every tick. XorpTimer _tick_tock; // Timer to increment the tick. /** * Called when damping is enabled. */ void init(); /** * Called when damping is disabled. */ void halt(); /* * Called every clock tick. */ bool tick(); }; #endif // __BGP_DAMPING_HH__ xorp/bgp/socket.cc0000664000076400007640000004161111540225521014227 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #include "bgp_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/exceptions.hh" #include "libxorp/callback.hh" #include "libcomm/comm_api.h" #ifdef HAVE_FCNTL_H #include #endif #ifdef HAVE_NETINET_IN_H #include #endif #include "socket.hh" #include "packet.hh" /* **************** BGPSocket - PUBLIC METHODS *********************** */ Socket::Socket(const Iptuple& iptuple, EventLoop& e) : _iptuple(iptuple), _eventloop(e) { debug_msg("Socket constructor called: %s\n", _iptuple.str().c_str()); // _eventloop = 0; #ifdef DEBUG_PEERNAME _remote_host = "unconnected socket"; #endif } void Socket::create_listener() { debug_msg("create_listener called\n"); size_t len; const struct sockaddr *sin = get_local_socket(len); XLOG_ASSERT(!_s.is_valid()); _s = comm_bind_tcp(sin, COMM_SOCK_NONBLOCKING); if (!_s.is_valid()) { XLOG_ERROR("comm_bind_tcp failed"); } else { // bindtodevice? debug_msg("create_listener, local_interface: %s", get_local_interface().c_str()); if (get_local_interface().size()) { comm_set_bindtodevice(_s, get_local_interface().c_str()); } if (comm_listen(_s, COMM_LISTEN_DEFAULT_BACKLOG) != XORP_OK) { XLOG_ERROR("comm_listen failed"); } } } /* **************** BGPSocket - PROTECTED METHODS *********************** */ void Socket::close_socket() { debug_msg("Close socket %s %s\n", get_remote_host(), _s.str().c_str()); comm_sock_close(_s); _s.clear(); } void Socket::create_socket(const struct sockaddr *sin, int is_blocking) { debug_msg("create_socket called\n"); XLOG_ASSERT(!_s.is_valid()); _s = comm_sock_open(sin->sa_family, SOCK_STREAM, 0, is_blocking); if (!_s.is_valid()) { XLOG_ERROR("comm_sock_open failed"); return; } debug_msg("BGPSocket socket created (sock - %s)\n", _s.str().c_str()); } void Socket::init_sockaddr(string addr, uint16_t local_port, struct sockaddr_storage& ss, size_t& len) { debug_msg("addr %s port %u len = %u\n", addr.c_str(), XORP_UINT_CAST(local_port), XORP_UINT_CAST(len)); string port = c_format("%d", local_port); int error; struct addrinfo hints, *res0; // Need to provide a hint because we are providing a numeric port number. memset(&hints, 0, sizeof(hints)); #ifdef HOST_OS_WINDOWS hints.ai_family = PF_INET; #else hints.ai_family = PF_UNSPEC; #endif hints.ai_socktype = SOCK_STREAM; // addr must be numeric so this can't fail. if ((error = getaddrinfo(addr.c_str(), port.c_str(), &hints, &res0))) { XLOG_FATAL("getaddrinfo(%s,%s,...) failed: %s", addr.c_str(), port.c_str(), gai_strerror(error)); } XLOG_ASSERT(res0->ai_addrlen <= sizeof(ss)); memset(&ss, 0, sizeof(ss)); memcpy(&ss, res0->ai_addr, res0->ai_addrlen); len = res0->ai_addrlen; freeaddrinfo(res0); } /* **************** BGPSocket - PRIVATE METHODS *********************** */ /* **************** BGPSocketClient - PUBLIC METHODS *********************** */ SocketClient::SocketClient(const Iptuple& iptuple, EventLoop& e, bool md5sig) : Socket(iptuple, e), _md5sig(md5sig) { debug_msg("SocketClient constructor called\n"); _async_writer = 0; _async_reader = 0; _disconnecting = false; _connecting = false; } SocketClient::~SocketClient() { async_remove(); if( _connecting) connect_break(); } void SocketClient::disconnect() { debug_msg("Disconnect\n"); XLOG_ASSERT(get_sock().is_valid()); /* ** If you see this assert then the disconnect code has been ** re-entered. The call graph is typically: ** ** Socket::disconnect() ** Socket::async_remove() ** AsyncFileWriter::flush_buffers() ** ** The call to flush_buffers() cause the completion methods to be ** run which in some cases call disconnect() again. ** The solution is to check with SocketClient::disconnecting() ** and just return if we are currently disconnecting. This is ** perfectly safe as the initial call to disconnect() should ** perform all the correct state transitions. ** ** Look at "BGPPeer::send_notification_complete". */ if (_disconnecting) return; // XLOG_ASSERT(!_disconnecting); _disconnecting = true; async_remove(); close_socket(); _disconnecting = false; } void SocketClient::connect(ConnectCallback cb) { debug_msg("SocketClient connecting to remote Peer %s\n", get_remote_host()); // Assert that socket doesn't already exist, as we are about to create it. // XXX: This paranoid assertion existed to catch socket recycling // issues on the Windows platform; commented out for now. //XLOG_ASSERT(!get_sock().is_valid()); size_t len; create_socket(get_local_socket(len), COMM_SOCK_BLOCKING); // bindtodevice? debug_msg("SocketClient::connect, local_interface: %s", get_local_interface().c_str()); if (get_local_interface().size()) { comm_set_bindtodevice(get_sock(), get_local_interface().c_str()); } if (_md5sig) comm_set_tcpmd5(get_sock(), _md5sig); return connect_socket(get_sock(), get_remote_addr(), get_remote_port(), get_local_addr(), cb); } void SocketClient::connect_break() { connect_socket_break(); } void SocketClient::connected(XorpFd s) { #ifdef DEBUG_PEERNAME sockaddr_storage ss; struct sockaddr *sin = reinterpret_cast(ss); socklen_t len = sizeof(ss); if (-1 == getpeername(s, sin, &len)) XLOG_FATAL("getpeername failed: %s", strerror(errno)); char hostname[1024]; int error; if (error = getnameinfo(sin, len, hostname, sizeof(hostname), 0, 0, 0)) XLOG_FATAL("getnameinfo failed: %s", gai_strerror(error)); set_remote_host(hostname); #endif debug_msg("Connected to remote Peer %s\n", get_remote_host()); XLOG_ASSERT(!get_sock().is_valid()); XLOG_ASSERT(!_connecting); // Clean up any old reader/writers we might have around. // This can happen if you kill network connection between two peers // for 10 seconds and then re-start it. async_remove(); set_sock(s); async_add(s); } void SocketClient::flush_transmit_queue() { if (_async_writer) _async_writer->flush_buffers(); } void SocketClient::async_read_start(size_t cnt, size_t offset) { debug_msg("start reading %s\n", get_remote_host()); XLOG_ASSERT(_async_reader); _async_reader-> add_buffer_with_offset(_read_buf, cnt, offset, callback(this, &SocketClient::async_read_message)); _async_reader->start(); } /* * Handler for reading incoming data on a BGP connection. * * When a packet first comes in, we read the default amount of data, * which is BGPPacket::MINPACKETSIZE (i.e. the minimum size of a BGP message). * This callback is then invoked a first time, and it can check the * actual message length. If more bytes are needed, we call again * async_read_start with the desired length (instructing it to skip * whatever we already read). * Once the packet is complete, we invoke the packet decoder with dispatch() */ void SocketClient::async_read_message(AsyncFileWriter::Event ev, const uint8_t *buf, // the base of the buffer const size_t buf_bytes, // desired message size const size_t offset) // where we got so far (next free byte) { debug_msg("async_read_message %d %u %u %s\n", ev, XORP_UINT_CAST(buf_bytes), XORP_UINT_CAST(offset), get_remote_host()); XLOG_ASSERT(_async_reader); switch (ev) { case AsyncFileReader::DATA: XLOG_ASSERT(offset <= buf_bytes); if (offset == buf_bytes) { // message complete so far size_t fh_length = extract_16(buf + BGPPacket::LENGTH_OFFSET); if (fh_length < BGPPacket::MINPACKETSIZE || fh_length > sizeof(_read_buf)) { XLOG_ERROR("Illegal length value %u", XORP_UINT_CAST(fh_length)); if (!_callback->dispatch(BGPPacket::ILLEGAL_MESSAGE_LENGTH, buf, buf_bytes, this)) return; } /* * Keep reading until we have the whole message. */ if (buf_bytes == fh_length) { if (_callback->dispatch(BGPPacket::GOOD_MESSAGE, buf, buf_bytes, this)) async_read_start(); // ready for next message } else { // read rest of the message async_read_start(fh_length, buf_bytes); } } /* ** At this point if we have a valid _async_reader then it should ** have buffers into which we expect data. */ if (_async_reader && 0 == _async_reader->buffers_remaining()) XLOG_WARNING("No outstanding reads %s socket %p async_reader %p", is_connected() ? "connected" : "not connected", this, _async_reader); XLOG_ASSERT(!_async_reader || (_async_reader && _async_reader->buffers_remaining() > 0)); break; case AsyncFileReader::WOULDBLOCK: case AsyncFileReader::FLUSHING: /* * We are not using a dynamic buffer so don't worry. */ break; case AsyncFileReader::OS_ERROR: debug_msg("Read failed: %d\n", _async_reader->error()); _callback->dispatch(BGPPacket::CONNECTION_CLOSED, 0, 0, this); break; case AsyncFileReader::END_OF_FILE: debug_msg("End of file\n"); _callback->dispatch(BGPPacket::CONNECTION_CLOSED, 0, 0, this); break; } } void SocketClient::send_message_complete(AsyncFileWriter::Event ev, const uint8_t *buf, const size_t buf_bytes, const size_t offset, SendCompleteCallback cb) { debug_msg("event %d %s\n", ev, get_remote_host()); switch (ev) { case AsyncFileWriter::DATA: if (offset == buf_bytes) { debug_msg("Message sent\n"); cb->dispatch(SocketClient::DATA, buf); } XLOG_ASSERT(offset <= buf_bytes); break; case AsyncFileWriter::FLUSHING: cb->dispatch(SocketClient::FLUSHING, buf); break; case AsyncFileWriter::OS_ERROR: //XXXX do something //probably need to do some more error handling here cb->dispatch(SocketClient::ERROR, buf); break; case AsyncFileWriter::WOULDBLOCK: case AsyncFileWriter::END_OF_FILE: // Can't possibly happen. break; } } bool SocketClient::send_message(const uint8_t* buf, const size_t cnt, SendCompleteCallback cb) { debug_msg("peer %s bytes = %u\n", get_remote_host(), XORP_UINT_CAST(cnt)); if(!is_connected()) { #ifdef DEBUG_LOGGING XLOG_WARNING("sending message to %s, not connected!!!", get_remote_host()); #else XLOG_WARNING("sending message to %s, not connected!!!", get_remote_addr().c_str()); #endif return false; } XLOG_ASSERT(_async_writer); _async_writer->add_buffer(buf, cnt, callback(this, &SocketClient::send_message_complete, cb)); _async_writer->start(); return true; } bool SocketClient::output_queue_busy() const { //20 is a fairly arbitrary soft limit on how many buffers we want //in the output queue before we start to push back XLOG_ASSERT(_async_writer); if (_async_writer->buffers_remaining() > 20) return true; else return false; } int SocketClient::output_queue_size() const { XLOG_ASSERT(_async_writer); return _async_writer->buffers_remaining(); } /* **************** BGPSocketClient - PROTECTED METHODS *********************** */ /* **************** BGPSocketClient - PRIVATE METHODS *********************** */ void SocketClient::connect_socket(XorpFd sock, string raddr, uint16_t port, string laddr, ConnectCallback cb) { debug_msg("raddt %s port %d laddr %s\n", raddr.c_str(), port, laddr.c_str()); size_t len; const struct sockaddr *local = get_bind_socket(len); /* Bind the local endpoint to the supplied address */ if (XORP_ERROR == comm_sock_bind(sock, local)) { /* ** This endpoint is now screwed so shut it. */ close_socket(); cb->dispatch(false); return; } // bindtodevice? debug_msg("SocketClient::connect_socket, local_interface: %s", get_local_interface().c_str()); if (get_local_interface().size()) { comm_set_bindtodevice(sock, get_local_interface().c_str()); } const struct sockaddr *servername = get_remote_socket(len); if (!eventloop(). add_ioevent_cb(sock, IOT_CONNECT, callback(this, &SocketClient::connect_socket_complete, cb))) { XLOG_ERROR("Failed to add socket %s to eventloop", sock.str().c_str()); } const int blocking = 0; if (XORP_ERROR == comm_sock_set_blocking(sock, blocking)) XLOG_FATAL("Failed to go non-blocking"); // Given the file descriptor is now non-blocking we would expect a // in progress error. XLOG_ASSERT(!_connecting); _connecting = true; int in_progress = 0; if (XORP_ERROR == comm_sock_connect(sock, servername, blocking, &in_progress)) { if (in_progress) { debug_msg("connect failed in progress %s\n", in_progress ? "yes" : "no"); return; } } // 1) If an error apart from in_progress occured call the completion // method which will tidy up. // // 2) A connection may have been made already, this happens in the // loopback case, again the completion method should deal with this. connect_socket_complete(sock, IOT_CONNECT, cb); } void SocketClient::connect_socket_complete(XorpFd sock, IoEventType type, ConnectCallback cb) { int soerror; int is_connected = 0; socklen_t len = sizeof(soerror); debug_msg("connect socket complete %s %d\n", sock.str().c_str(), type); UNUSED(type); XLOG_ASSERT(_connecting); _connecting = false; XLOG_ASSERT(get_sock() == sock); eventloop().remove_ioevent_cb(sock); // Did the connection succeed? if (comm_sock_is_connected(sock, &is_connected) != XORP_OK) { debug_msg("connect failed (comm_sock_is_connected: %s) %s\n", comm_get_last_error_str(), sock.str().c_str()); goto failed; } if (is_connected == 0) { debug_msg("connect failed (comm_sock_is_connected: false) %s\n", sock.str().c_str()); goto failed; } // Check for a pending socket error if one exists. if (getsockopt(sock, SOL_SOCKET, SO_ERROR, XORP_SOCKOPT_CAST(&soerror), &len) != 0) { debug_msg("connect failed (getsockopt) %s\n", sock.str().c_str()); goto failed; } debug_msg("connect suceeded %s\n", sock.str().c_str()); // Clean up any old reader/writers we might have around. // Not sure exactly how this one was triggered. async_remove(); async_add(sock); cb->dispatch(true); return; failed: // XLOG_ERROR("Connect failed: raddr %s port %d", // inet_ntoa(get_remote_addr()), ntohs(get_remote_port())); close_socket(); cb->dispatch(false); } void SocketClient::connect_socket_break() { _connecting = false; eventloop().remove_ioevent_cb(get_sock()); close_socket(); } void SocketClient::async_add(XorpFd sock) { if (XORP_ERROR == comm_sock_set_blocking(sock, COMM_SOCK_NONBLOCKING)) XLOG_FATAL("Failed to go non-blocking"); XLOG_ASSERT(0 == _async_writer); _async_writer = new AsyncFileWriter(eventloop(), sock); XLOG_ASSERT(0 == _async_reader); // // XXX: We use lowest priority for the file reader so that we don't // generally receive data in faster than we can send it back out. // Also, the priority is lower than the tasks' background priority // to avoid being overloaded by high volume data from the peers. // _async_reader = new AsyncFileReader(eventloop(), sock, XorpTask::PRIORITY_BACKGROUND); async_read_start(); } void SocketClient::async_remove() { if(_async_writer) { _async_writer->stop(); _async_writer->flush_buffers(); delete _async_writer; _async_writer = 0; } async_remove_reader(); } void SocketClient::async_remove_reader() { if (_async_reader) { _async_reader->stop(); _async_reader->flush_buffers(); delete _async_reader; _async_reader = 0; } } bool SocketClient::is_connected() { if (_connecting) return false; return get_sock().is_valid(); } bool SocketClient::still_reading() { return _async_reader; } /* **************** BGPSocketServer *********************** */ SocketServer::SocketServer(const Iptuple& iptuple, EventLoop& e) : Socket(iptuple, e) { debug_msg("SocketServer constructor called\n"); } xorp/bgp/route_table_policy_im.cc0000664000076400007640000001463711540224220017313 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #include "bgp_module.h" #include "route_table_policy_im.hh" template PolicyTableImport::PolicyTableImport(const string& tablename, const Safi& safi, BGPRouteTable* parent, PolicyFilters& pfs, const A& peer, const A& self) : PolicyTable(tablename, safi, parent, pfs, filter::IMPORT) { this->_parent = parent; this->_varrw->set_peer(peer); this->_varrw->set_self(self); } template int PolicyTableImport::route_dump(InternalMessage& rtmsg, BGPRouteTable* caller, const PeerHandler* dump_peer) { // XXX: policy route dumping IS A HACK! // it is a "normal dump" if (dump_peer) return PolicyTable::route_dump(rtmsg, caller, dump_peer); // it is a "policy dump" XLOG_ASSERT(caller == this->_parent); debug_msg("[BGP] Policy route dump: %s\n\n", rtmsg.str().c_str()); #if 0 // "old" filter... InternalMessage* fmsg = do_filtering(rtmsg, false); bool was_filtered = (fmsg == NULL); if (fmsg != NULL) { if (fmsg->route() == rtmsg.route()) { // fmsg contains the original route, so we need to clone it, // or we'll get bad interactions when we set the policyfilter // on the new route below because the policyfilter is // propagated back to the parent. SubnetRoute* copy_old_rt = new SubnetRoute(*rtmsg.route()); // clear the parent route, so noone downstream can mess up the // metadata on the route still stored in RibIn. copy_old_rt->set_parent_route(NULL); InternalMessage* copy_fmsg = new InternalMessage(copy_old_rt, rtmsg.origin_peer(), rtmsg.genid()); if (rtmsg.changed()) copy_fmsg->set_changed(); if (rtmsg.push()) copy_fmsg->set_push(); if (rtmsg.from_previous_peering()) copy_fmsg->set_from_previous_peering(); fmsg = copy_fmsg; XLOG_ASSERT(fmsg->route() != rtmsg.route()); } else { // the route is already a copy, but we still need to clear the // parent route to prevent anyone downstream modifying the // metadata on the route still stored in RibIn const_cast*>(fmsg->route())->set_parent_route(NULL); } } // we want current filter rtmsg.route()->set_policyfilter(0, RefPf()); // filter new message InternalMessage* new_msg = do_filtering(rtmsg, false); bool accepted = (new_msg != NULL); #endif // clone the PA list, so we don't process it twice FPAListRef old_fpa_list = new FastPathAttributeList(*rtmsg.attributes()); SubnetRoute* copy_old_route = new SubnetRoute(*rtmsg.route()); copy_old_route->set_parent_route(NULL); InternalMessage *old_rtmsg = new InternalMessage(copy_old_route, old_fpa_list, rtmsg.origin_peer(), rtmsg.genid()); old_rtmsg->set_copied(); // we want current filter rtmsg.route()->set_policyfilter(0, RefPf()); bool old_accepted = this->do_filtering(*old_rtmsg, false); bool new_accepted = this->do_filtering(rtmsg, false); InternalMessage *new_rtmsg = 0; SubnetRoute* copy_new_route = 0; if (new_accepted) { // we don't want anyone downstream to mess with the metadata // on the route in RibIn // copy_new_route = new SubnetRoute(*rtmsg.route()); // copy_new_route->set_parent_route(NULL); // new_rtmsg = new InternalMessage(copy_new_route, new_rtmsg = new InternalMessage(rtmsg.route(), rtmsg.attributes(), rtmsg.origin_peer(), rtmsg.genid()); } debug_msg("[BGP] Policy route dump accepted: %d\n", new_accepted); BGPRouteTable* next = this->_next_table; XLOG_ASSERT(next); int res = XORP_OK; if (new_accepted) { if (!old_accepted) { debug_msg("[BGP] Policy add_route [accepted, was filtered]"); res = next->add_route(*new_rtmsg, this); } else { debug_msg("[BGP] Policy replace_route old=(%s) new=(%s)\n", old_rtmsg->str().c_str(), new_rtmsg->str().c_str()); if (new_rtmsg->attributes() == old_rtmsg->attributes()) { // no change. copy_new_route->unref(); delete new_rtmsg; copy_old_route->unref(); delete old_rtmsg; return ADD_USED; } // XXX don't check return of deleteroute! res = next->delete_route(*old_rtmsg, this); XLOG_ASSERT(new_rtmsg->route()); // current filters for (int i = 1; i < 3; i++) new_rtmsg->route()->set_policyfilter(i, RefPf()); res = next->add_route(*new_rtmsg, this); // XXX this won't work. We need to keep original filter pointers on // old route and null pointers on new route. If we clone the old // route, the cache table will find it, and send out the cached old // route which still has pointer to original parent, which now has // null pointers, in the case we sent them in the new route which // has same parent. [it's a mess... fix soon] // res = next->replace_route(*fmsg, *new_msg, this); } } else { // not accepted if (!old_accepted) { } else { // // XXX: A hack propagating the "not-winner route" flag back to // the original route. // The reason we need this hack here is because of an earlier // hack where the fmsg parent was modified to be NULL. // rtmsg.route()->set_is_not_winner(); next->delete_route(*old_rtmsg, this); } res = ADD_FILTERED; } if (old_rtmsg) delete old_rtmsg; if (new_rtmsg) delete new_rtmsg; return res; } template class PolicyTableImport; template class PolicyTableImport; xorp/bgp/internal_message.cc0000664000076400007640000000457711421137511016270 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "bgp_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "internal_message.hh" template InternalMessage::InternalMessage(const SubnetRoute *rte, const PeerHandler *origin, uint32_t genid) { XLOG_ASSERT(rte); _subnet_route = rte; _origin_peer = origin; _changed = false; _copied = false; _push = false; _from_previous_peering = false; _genid = genid; PAListRef pal = rte->attributes(); _attributes = new FastPathAttributeList(pal); } template InternalMessage::InternalMessage(const SubnetRoute *rte, FPAListRef pa_list, const PeerHandler *origin, uint32_t genid) { XLOG_ASSERT(rte); _subnet_route = rte; _origin_peer = origin; _changed = false; _copied = false; _push = false; _from_previous_peering = false; _genid = genid; _attributes = pa_list; } template InternalMessage::~InternalMessage() { } template const IPNet& InternalMessage::net() const { return _subnet_route->net(); } template string InternalMessage::str() const { string s; s += c_format("GenID is %d\n", XORP_INT_CAST(_genid)); if (_changed) s += "CHANGED flag is set\n"; if (_push) s += "PUSH flag is set\n"; if (_from_previous_peering) s += "FROM_PREVIOUS_PEERING flag is set\n"; s += _subnet_route->str(); return s; } template class InternalMessage; template class InternalMessage; xorp/bgp/dummy_next_hop_resolver.cc0000664000076400007640000000467611421137511017730 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #include "bgp_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "dummy_next_hop_resolver.hh" template DummyNextHopResolver::DummyNextHopResolver(EventLoop& eventloop, BGPMain& bgp) : NextHopResolver(NULL, eventloop, bgp) { } template DummyNextHopResolver::~DummyNextHopResolver() { } template bool DummyNextHopResolver::lookup(const A nexthop, bool& resolvable, uint32_t& metric) const { typename map ::const_iterator i; i = _metrics.find(nexthop); if (i == _metrics.end()) { resolvable = false; debug_msg("Lookup: %s, not resolvable\n", nexthop.str().c_str()); return true; } resolvable = true; metric = i->second; debug_msg("Lookup: %s, metric %d\n", nexthop.str().c_str(), metric); return true; } template void DummyNextHopResolver::set_nexthop_metric(const A nexthop, uint32_t metric) { typename map ::const_iterator i; i = _metrics.find(nexthop); if (i != _metrics.end()) { XLOG_FATAL("Can't find nexthop's metric\n"); } _metrics[nexthop] = metric; } template void DummyNextHopResolver::unset_nexthop_metric(const A nexthop) { typename map ::iterator i; i = _metrics.find(nexthop); if (i == _metrics.end()) { XLOG_FATAL("Can't unset nexthop %s\n", nexthop.str().c_str()); } _metrics.erase(i); } //force these templates to be built template class DummyNextHopResolver; xorp/bgp/local_data.hh0000664000076400007640000000761111540224217015037 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/bgp/local_data.hh,v 1.25 2008/10/02 21:56:16 bms Exp $ #ifndef __BGP_LOCAL_DATA_HH__ #define __BGP_LOCAL_DATA_HH__ #include "bgp_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/ipv4.hh" #include "libxorp/asnum.hh" #include "damping.hh" /** * Data that applies to all BGP peerings. * Currently this is just our AS number and router ID. */ class LocalData { public: LocalData(EventLoop& eventloop) : _as(AsNum::AS_INVALID), _use_4byte_asnums(false), _confed_id(AsNum::AS_INVALID), _route_reflector(false), _damping(eventloop), _jitter(true) {} // LocalData(const AsNum& as, const IPv4& id) // : _as(as), _id(id), _confed_id(AsNum::AS_INVALID) // {} /** * @return This routers AS number. */ const AsNum& get_as() const { return _as; } /** * Set this routers AS number. */ void set_as(const AsNum& a) { _as = a; } /** * @return true if we use 4 byte AS numbers. */ inline bool use_4byte_asnums() const { return _use_4byte_asnums; } /** * Set whether to send 2 or 4 byte AS numbers */ inline void set_use_4byte_asnums(bool use_4byte_asnums) { _use_4byte_asnums = use_4byte_asnums; } /** * @return This routers ID. */ const IPv4& get_id() const { return _id; } /** * Set this routers ID. */ void set_id(const IPv4& i) { _id = i; } /** * @return the confederation ID of this router if set. */ const AsNum& get_confed_id() const { return _confed_id; } /** * Set this routers confederation ID. */ void set_confed_id(const AsNum& confed_id) { _confed_id = confed_id; } /** * @return the cluster ID of this router. */ const IPv4& get_cluster_id() const { XLOG_ASSERT(_route_reflector); return _cluster_id; } /** * Set this routers cluster ID. */ void set_cluster_id(const IPv4& cluster_id) { _cluster_id = cluster_id; } /** * Get the route reflection status. */ const bool& get_route_reflector() const { return _route_reflector; } /** * Set the route reflection status. */ void set_route_reflector(const bool route_reflector) { _route_reflector = route_reflector; } /** * Return all the route flap damping state. */ Damping& get_damping() { return _damping; } void set_jitter(bool jitter) { _jitter = jitter; } bool get_jitter() const { return _jitter; } private: AsNum _as; // This routers AS number. bool _use_4byte_asnums; // Indicates to use 4byte AS numbers. IPv4 _id; // This routers ID. AsNum _confed_id; // Confederation identifier. IPv4 _cluster_id; // Router reflector cluster ID bool _route_reflector; // True if this router is a // route reflector Damping _damping; // Route Flap Damping parameters bool _jitter; // Jitter applied to timers. }; #endif // __BGP_LOCAL_DATA_HH__ xorp/bgp/route_table_decision.hh0000664000076400007640000001422511540224220017127 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/bgp/route_table_decision.hh,v 1.29 2008/11/08 06:14:38 mjh Exp $ #ifndef __BGP_ROUTE_TABLE_DECISION_HH__ #define __BGP_ROUTE_TABLE_DECISION_HH__ #include "route_table_base.hh" #include "dump_iterators.hh" #include "peer_handler.hh" #include "next_hop_resolver.hh" #include "peer_route_pair.hh" /** * Container for a route and the meta-data about the origin of a route * used in the DecisionTable decision process. */ template class RouteData { public: RouteData(const SubnetRoute* route, FPAListRef pa_list, BGPRouteTable* parent_table, const PeerHandler* peer_handler, uint32_t genid) : _route(route), _pa_list(pa_list), _parent_table(parent_table), _peer_handler(peer_handler), _genid(genid) {} /* main reason for defining operator= is to keep the refcount correct on _pa_list */ RouteData& operator=(const RouteData& him) { _route = him._route; _pa_list = him._pa_list; _parent_table = him._parent_table; _peer_handler = him.peer_handler; _genid = him._genid; } void set_is_not_winner() { _parent_table->route_used(_route, false); _route->set_is_not_winner(); } void set_is_winner(int igp_distance) { _parent_table->route_used(_route, true); _route->set_is_winner(igp_distance); } const SubnetRoute* route() const { return _route; } const FPAListRef& attributes() const { return _pa_list; } const PeerHandler* peer_handler() const { return _peer_handler; } BGPRouteTable* parent_table() const { return _parent_table; } uint32_t genid() const { return _genid; } private: const SubnetRoute* _route; FPAListRef _pa_list; BGPRouteTable* _parent_table; const PeerHandler* _peer_handler; uint32_t _genid; }; /** * @short BGPRouteTable which receives routes from all peers and * decided which routes win. * * The XORP BGP is internally implemented as a set of pipelines * consisting of a series of BGPRouteTables. Each pipeline receives * routes from a BGP peer, stores them, and applies filters to them to * modify the routes. Then the pipelines converge on a single * decision process, which decides which route wins amongst possible * alternative routes. * * DecisionTable is a BGPRouteTable which performs this decision * process. It has many upstream BGPRouteTables and a single * downstream BGPRouteTable. Only the winning routes according to the * BGP decision process are propagated downstream. * * When a new route reaches DecisionTable from one peer, we must * lookup that route in all the other upstream branches to see if this * route wins, or even if it doesn't win, if it causes a change of * winning route. Similarly for route deletions coming from a peer, * etc. */ template class DecisionTable : public BGPRouteTable { public: DecisionTable(string tablename, Safi safi, NextHopResolver& next_hop_resolver); ~DecisionTable(); int add_parent(BGPRouteTable *parent, PeerHandler *peer_handler, uint32_t genid); int remove_parent(BGPRouteTable *parent); int add_route(InternalMessage &rtmsg, BGPRouteTable *caller); int replace_route(InternalMessage &old_rtmsg, InternalMessage &new_rtmsg, BGPRouteTable *caller); int delete_route(InternalMessage &rtmsg, BGPRouteTable *caller); int route_dump(InternalMessage &rtmsg, BGPRouteTable *caller, const PeerHandler *peer); int push(BGPRouteTable *caller); const SubnetRoute *lookup_route(const IPNet &net, uint32_t& genid, FPAListRef& pa_list) const; //don't call this on a DecisionTable - it's meaningless BGPRouteTable *parent() { abort(); return NULL; } RouteTableType type() const {return DECISION_TABLE;} string str() const; bool get_next_message(BGPRouteTable */*next_table*/) { abort(); return false; } bool dump_next_route(DumpIterator& dump_iter); /** * Notification that the status of this next hop has changed. * * @param bgp_nexthop The next hop that has changed. */ virtual void igp_nexthop_changed(const A& bgp_nexthop); void peering_went_down(const PeerHandler *peer, uint32_t genid, BGPRouteTable *caller); void peering_down_complete(const PeerHandler *peer, uint32_t genid, BGPRouteTable *caller); void peering_came_up(const PeerHandler *peer, uint32_t genid, BGPRouteTable *caller); private: const SubnetRoute *lookup_route(const BGPRouteTable* ignore_parent, const IPNet &net, const PeerHandler*& best_routes_peer, BGPRouteTable*& best_routes_parent ) const; RouteData* find_alternative_routes(const BGPRouteTable *caller, const IPNet& net, list >& alternatives) const; uint32_t local_pref(const FPAListRef& pa_list) const; uint32_t med(const FPAListRef& pa_list) const; bool resolvable(const A) const; uint32_t igp_distance(const A) const; RouteData* find_winner(list >& alternatives) const; map*, PeerTableInfo* > _parents; map* > _sorted_parents; NextHopResolver& _next_hop_resolver; }; #endif // __BGP_ROUTE_TABLE_DECISION_HH__ xorp/bgp/notification_packet.cc0000664000076400007640000002103311421137511016747 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "bgp_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libproto/packet.hh" #include "packet.hh" #define DEBUG_BGPNotificationPacket /* **************** BGPNotificationPacket *********************** */ NotificationPacket::NotificationPacket(uint8_t ec, uint8_t esc, const uint8_t* ed, size_t elen) { debug_msg("Error code %d sub code %d data %p len %u\n", ec, esc, ed, XORP_UINT_CAST(elen)); if (elen == 0) ed = 0; if (ed == 0) elen = 0; // elen is the length of the error_data to carry _Length = BGPPacket::MINNOTIFICATIONPACKET + elen; _Type = MESSAGETYPENOTIFICATION; _error_code = ec; _error_subcode = esc; if (ed) { uint8_t *error_data = new uint8_t[elen]; memcpy(error_data, ed, elen); _error_data = error_data; } else _error_data = 0; debug_msg("%s", str().c_str()); } NotificationPacket::NotificationPacket(const uint8_t *d, uint16_t l) throw(CorruptMessage) { debug_msg("Data %p len %d\n", d, l); if (l < BGPPacket::MINNOTIFICATIONPACKET) xorp_throw(CorruptMessage, c_format("Notification message too short %d", l), MSGHEADERERR, BADMESSLEN, d + BGPPacket::MARKER_SIZE, 2); _Length = l; _Type = MESSAGETYPENOTIFICATION; d += BGPPacket::COMMON_HEADER_LEN; // skip header _error_code = d[0]; _error_subcode = d[1]; int error_data_len = _Length - BGPPacket::MINNOTIFICATIONPACKET; if (error_data_len > 0) { uint8_t *ed = new uint8_t[error_data_len]; memcpy(ed, d + 2, error_data_len); _error_data = ed; debug_msg("%s", str().c_str()); } else _error_data = NULL; return; } bool NotificationPacket::encode(uint8_t *buf, size_t& len, const BGPPeerData *peerdata) const { UNUSED(peerdata); XLOG_ASSERT(buf != 0); debug_msg("Encode in NotificationPacket called (%u)\n", XORP_UINT_CAST(_Length)); if (len < _Length) return false; len = _Length; // allocate buffer, set length, fill up header buf = basic_encode(len, buf); buf[BGPPacket::COMMON_HEADER_LEN] = _error_code; buf[BGPPacket::COMMON_HEADER_LEN + 1] = _error_subcode; if (_error_data != 0) memcpy(buf + BGPPacket::MINNOTIFICATIONPACKET, _error_data, len - BGPPacket::MINNOTIFICATIONPACKET); return true; } bool NotificationPacket::validate_error_code(const int error, const int subcode) { bool good_error_code = true; bool good_error_subcode = false; switch (error) { case MSGHEADERERR: switch (subcode) { case CONNNOTSYNC: case BADMESSLEN: case BADMESSTYPE: good_error_subcode = true; break; } break; case OPENMSGERROR: switch (subcode) { case UNSUPVERNUM: case BADASPEER: case BADBGPIDENT: case UNSUPOPTPAR: case AUTHFAIL: case UNACCEPTHOLDTIME: good_error_subcode = true; break; } break; case UPDATEMSGERR: switch (subcode) { case MALATTRLIST: case UNRECOGWATTR: case MISSWATTR: case ATTRFLAGS: case ATTRLEN: case INVALORGATTR: case INVALNHATTR: case OPTATTR: case INVALNETFIELD: case MALASPATH: good_error_subcode = true; break; } break; case HOLDTIMEEXP: break; case FSMERROR: break; case CEASE: break; default: good_error_code = false; } if (!good_error_subcode && 0 == subcode) good_error_subcode = true; return good_error_code && good_error_subcode; } string NotificationPacket::pretty_print_error_code(const int error, const int subcode, const uint8_t* error_data, const size_t len) { string s; switch (error) { case MSGHEADERERR: s += c_format("Message Header Error(%d): ", MSGHEADERERR); switch (subcode) { case CONNNOTSYNC: s += c_format("Connection Not Synchronized(%d)", CONNNOTSYNC); break; case BADMESSLEN: if (error_data != NULL) { s += c_format("Bad Message Length(%d) - field: %d", BADMESSLEN, extract_16(error_data)); } else { s += c_format("Bad Message Length(%d) - " "NO ERROR DATA SUPPLIED", BADMESSLEN); } break; case BADMESSTYPE: if (error_data != NULL) s += c_format("Bad Message Type(%d) - field : %d", BADMESSTYPE, error_data[0]); else s += c_format("Bad Message Type(%d) - " "NO ERROR DATA SUPPLIED", BADMESSTYPE); break; } break; case OPENMSGERROR: s += c_format("OPEN Message Error(%d): ", OPENMSGERROR); switch (subcode) { case UNSUPVERNUM: if (error_data != NULL) { s += c_format("Unsupported Version Number(%d) - " "Min supported Version is %d", UNSUPVERNUM, extract_16(error_data)); } else { s += c_format("Unsupported Version Number(%d) - " "NO ERROR DATA SUPPLIED", UNSUPVERNUM); } break; case BADASPEER: s += c_format("Bad Peer AS(%d)", BADASPEER); break; case BADBGPIDENT: s += c_format("Bad BGP Identifier(%d)", BADBGPIDENT); break; case UNSUPOPTPAR: s += c_format("Unsupported Optional Parameter(%d)", UNSUPOPTPAR); break; case AUTHFAIL: s += c_format("Authentication Failure(%d)", AUTHFAIL); break; case UNACCEPTHOLDTIME: s += c_format("Unacceptable Hold Time(%d)", UNACCEPTHOLDTIME); break; case UNSUPCAPABILITY: s += c_format("Unsuported Capability(%d)", UNSUPCAPABILITY); break; } break; case UPDATEMSGERR: s += c_format("UPDATE Message Error(%d): ", UPDATEMSGERR); switch (subcode) { case MALATTRLIST: s += c_format("Malformed Attribute List(%d)", MALATTRLIST); break; case UNRECOGWATTR: if (error_data != NULL) s += c_format("Unrecognized Well-known Attribute(%d) - ", UNRECOGWATTR); else s += c_format("Unrecognized Well-known Attribute(%d) - " "NO ERROR DATA SUPPLIED", UNRECOGWATTR); break; case MISSWATTR: if (error_data != NULL) s += c_format("Missing Well-known Attribute(%d) - Type %d", MISSWATTR, error_data[0]); else s += c_format("Missing Well-known Attribute(%d) - " "NO ERROR DATA SUPPLIED", MISSWATTR); break; case ATTRFLAGS: s += c_format("Attribute Flags Error(%d)", ATTRFLAGS); break; case ATTRLEN: s += c_format("Attribute Length Error(%d)", ATTRLEN); break; case INVALORGATTR: s += c_format("Invalid ORIGIN Attribute(%d)", INVALORGATTR); break; case INVALNHATTR: s += c_format("Invalid NEXT_HOP Attribute(%d)", INVALNHATTR); break; case OPTATTR: s += c_format("Optional Attribute Error(%d)", OPTATTR); break; case INVALNETFIELD: s += c_format("Invalid Network Field(%d)", INVALNETFIELD); break; case MALASPATH: s += c_format("Malformed AS_PATH(%d)", MALASPATH); break; } break; case HOLDTIMEEXP: s += c_format("Hold Timer Expired(%d)", HOLDTIMEEXP); break; case FSMERROR: s += c_format("Finite State Machine Error(%d)", FSMERROR); break; case CEASE: s += c_format("Cease(%d)", CEASE); break; } if (error_data != NULL) { s += c_format(" ["); for (size_t i = 0; i < len; i++) s += c_format("%s%#x", i != 0 ? " " : "", error_data[i]); s += c_format("]"); } return s; } string NotificationPacket::str() const { return "Notification Packet: " + pretty_print_error_code(_error_code, _error_subcode, _error_data, _Length - BGPPacket::MINNOTIFICATIONPACKET) + "\n"; } bool NotificationPacket::operator==(const NotificationPacket& him ) const { if (_error_code != him.error_code()) return false; if (_error_subcode != him.error_subcode()) return false; // common packet headers apply to both length, so only need to compare raw // packet length if (_Length != him._Length) return false; if (0 != memcmp(_error_data, him.error_data(), _Length - BGPPacket::MINNOTIFICATIONPACKET)) return false; return true; } xorp/bgp/route_table_cache.hh0000664000076400007640000001457711540224220016407 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/bgp/route_table_cache.hh,v 1.32 2008/11/08 06:14:38 mjh Exp $ #ifndef __BGP_ROUTE_TABLE_CACHE_HH__ #define __BGP_ROUTE_TABLE_CACHE_HH__ #include "libxorp/timer.hh" #include "route_table_base.hh" #include "crash_dump.hh" #include "libxorp/ref_trie.hh" #include "peer_handler.hh" /** * Used in CacheTable to store a SubnetRoute and the genid of the * RibIn that generated the route. */ template class CacheRoute { public: CacheRoute(const SubnetRoute* route, uint32_t genid) : _routeref(route), _genid(genid) {} const SubnetRoute* route() const { return _routeref.route(); } uint32_t genid() const { return _genid; } private: SubnetRouteConstRef _routeref; uint32_t _genid; }; class EventLoop; /** * @short specialized BGPRouteTable that stores routes modified by a * FilterTable. * * The XORP BGP is internally implemented as a set of pipelines * consisting of a series of BGPRouteTables. Each pipeline receives * routes from a BGP peer, stores them, and applies filters to them to * modify the routes. Then the pipelines converge on a single * decision process, which decides which route wins amongst possible * alternative routes. After decision, the winning routes fanout * again along a set of pipelines, again being filtered, before being * transmitted to peers. * * Typically there are two FilterTables for each peer which modify * routes passing down the pipeline, one in the input branch from that * peer, and one in the output branch to that peer. A FilterTable * does not store the routes it modifies, so a CacheTable is placed * downstream of a FilterTable to store routes that are modified. * * In the current code, a CacheTable isn't strictly necessary, but it * simplifies life for all downstream tables, because they know that * all routes they receive are stored in stable storage and won't go * away without explicit notification. * * In the future, it is likely that the CacheTables in the outgoing * branches will be removed, as they waste quite a bit of memory for * very little gain. However, they have not yet been removed because * their internal sanity checks have served as a valuable debugging * device to ensure consistency in the messages travelling down the * outgoing branches. */ template class CacheTable : public BGPRouteTable, CrashDumper { public: CacheTable(string tablename, Safi safi, BGPRouteTable *parent, const PeerHandler *peer); ~CacheTable(); int add_route(InternalMessage &rtmsg, BGPRouteTable *caller); int replace_route(InternalMessage &old_rtmsg, InternalMessage &new_rtmsg, BGPRouteTable *caller); int delete_route(InternalMessage &rtmsg, BGPRouteTable *caller); int push(BGPRouteTable *caller); int route_dump(InternalMessage &rtmsg, BGPRouteTable *caller, const PeerHandler *dump_peer); void flush_cache(); const SubnetRoute *lookup_route(const IPNet &net, uint32_t& genid, FPAListRef& pa_list) const; void route_used(const SubnetRoute* route, bool in_use); RouteTableType type() const {return CACHE_TABLE;} string str() const; /* mechanisms to implement flow control in the output plumbing */ bool get_next_message(BGPRouteTable *next_table); int route_count() const { return _route_table->route_count(); } string dump_state() const; EventLoop& eventloop() const; private: RefTrie > *_route_table; const PeerHandler *_peer; // stats to help debugging int _unchanged_added, _unchanged_deleted, _changed_added, _changed_deleted; }; /** * @short Delete nodes in the cache table trie. * * Only one instance of this class exists at any time. */ template class DeleteAllNodes { public: typedef RefTrie > RouteTable; typedef queue RouteTables; DeleteAllNodes(const PeerHandler *peer, RouteTable *route_table) : _peer(peer) { bool empty = _route_tables.empty(); _route_tables.push(route_table); if (empty) { _deleter_task = _peer->eventloop().new_task( callback(this, &DeleteAllNodes::delete_some_nodes), XorpTask::PRIORITY_BACKGROUND, XorpTask::WEIGHT_DEFAULT); } else { delete this; } } /** * Background task that deletes nodes in trie. * * @return true if there are routes to delete. */ bool delete_some_nodes() { RouteTable *route_table = _route_tables.front(); typename RouteTable::iterator current = route_table->begin(); for(int i = 0; i < _deletions_per_call; i++) { // In theory if current is invalid then it will move to a // valid entry. Unlike the STL, which this isn't. PAListRef old_pa_list = current.payload().route()->attributes(); old_pa_list.deregister_with_attmgr(); route_table->erase(current); if (current == route_table->end()) { _route_tables.pop(); route_table->delete_self(); break; } } if (_route_tables.empty()) { delete this; return false; } return true; } /** * @return true if route tables exist and are still being deleted. */ static bool running() { return !_route_tables.empty(); } private: static RouteTables _route_tables; // Queue of route tables to delete. XorpTask _deleter_task; // const PeerHandler *_peer; // Handle to the EventLoop. static int _deletions_per_call; // Number of nodes deleted per call. }; #endif // __BGP_ROUTE_TABLE_CACHE_HH__ xorp/bgp/peer_data.hh0000664000076400007640000002672511540224217014707 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/bgp/peer_data.hh,v 1.28 2008/11/08 06:14:37 mjh Exp $ #ifndef __BGP_PEER_DATA_HH__ #define __BGP_PEER_DATA_HH__ #include "libxorp/xorp.h" #include "libxorp/ipv4.hh" #include "libxorp/ipv6.hh" #include "libxorp/asnum.hh" #include "iptuple.hh" #include "parameter.hh" #include "local_data.hh" const size_t BGPVERSION = 4; /** * Types of BGP Peering Sessions */ enum PeerType { PEER_TYPE_EBGP = 0, // Standard E-BGP peering PEER_TYPE_IBGP = 1, // Standard I-BGP peering PEER_TYPE_EBGP_CONFED = 2, // Confederation E-BGP peering PEER_TYPE_IBGP_CLIENT = 3, // Route reflection client I-BGP. PEER_TYPE_UNCONFIGURED = 4, // The peer type has not yet been configured. PEER_TYPE_INTERNAL = 255 // connects to RIB } ; /** * The value of a configuration variable and its state enable/disabled. */ template class ConfigVar { public: ConfigVar(A var, bool enabled = false) : _var(var), _enabled(enabled) {} void set_var(A var) { _var = var; } A get_var() const { return _var; } void set_enabled(bool enabled) { _enabled = enabled; } bool get_enabled() const { return _enabled; } private: A _var; bool _enabled; }; /** * Data that applies to a specific peering. */ class BGPPeerData { public: BGPPeerData(const LocalData& local_data, const Iptuple& iptuple, AsNum as, const IPv4& next_hop, const uint16_t holdtime); ~BGPPeerData(); const Iptuple& iptuple() const { return _iptuple; } const AsNum& as() const { return _as; } void set_as(const AsNum& a) { _as = a; } bool use_4byte_asnums() const { return _use_4byte_asnums; } void set_use_4byte_asnums(bool use) { _use_4byte_asnums = use; } bool we_use_4byte_asnums() const { return _local_data.use_4byte_asnums(); } uint32_t get_hold_duration() const; void set_hold_duration(uint32_t d); uint32_t get_retry_duration() const; void set_retry_duration(uint32_t d); uint32_t get_keepalive_duration() const; void set_keepalive_duration(uint32_t d); /** * The peers BGP ID. */ const IPv4& id() const { return _id; } void set_id(const IPv4& i) { _id = i; } bool route_reflector() const { return _route_reflector; } void set_route_reflector(bool rr) { _route_reflector = rr; } bool confederation() const { return _confederation; } void set_confederation(bool conf) { _confederation = conf; } /** * @return the prefix limit. */ ConfigVar& get_prefix_limit() { return _prefix_limit; } /** * Return this routers AS number. Normally this is simple, but if * this router is in a confederation then the AS number depends on * the the type of the peering. */ const AsNum my_AS_number() const; /** * Compute the type of this peering. */ void compute_peer_type(); string get_peer_type_str() const; PeerType get_peer_type() const; /** * true if peer type is either PEER_TYPE_IBGP or PEER_TYPE_IBGP_CLIENT */ bool ibgp() const; /** * true if peer type is PEER_TYPE_IEBGP */ bool ibgp_vanilla() const; /** * true if peer type is PEER_TYPE_IBGP_CLIENT */ bool ibgp_client() const; /** * true if peer type is PEER_TYPE_EBGP */ bool ebgp_vanilla() const; /** * true if peer type is PEER_TYPE_EBGP_CONFED */ bool ebgp_confed() const; void add_recv_parameter(const ParameterNode& p) { add_parameter(_recv_parameters, p); }; void remove_recv_parameter(const ParameterNode& p) { remove_parameter(_recv_parameters, p); }; void add_sent_parameter(const ParameterNode& p){ add_parameter(_sent_parameters, p); }; void remove_sent_parameter(const ParameterNode& p){ remove_parameter(_sent_parameters, p); }; void add_negotiated_parameter(const ParameterNode& p){ add_parameter(_negotiated_parameters, p); }; void remove_negotiated_parameter(const ParameterNode& p) { remove_parameter(_negotiated_parameters, p); }; const ParameterList& parameter_recv_list() const { return _recv_parameters; } const ParameterList& parameter_sent_list() const { return _sent_parameters; } const ParameterList& parameter_negotiated_list() const { return _negotiated_parameters; } /** * Take the optional parameters out of an open packet and save * them in _recv_parameters. The list is accessible through the * parameter_recv_list method. */ void save_parameters(const ParameterList& parameter_list); /** * Go through the parameters that we have sent and the ones that * our peer has sent us and drop state into the * _negotiated_parameters list. */ void open_negotiation(); /** * Multiprotocol option negotiation. */ enum Direction { SENT = 0, // Sent by us. RECEIVED = 1, // Received from peer. NEGOTIATED = 2, // Both sides agree. // Note: ARRAY_SIZE must be larger than all previous values ARRAY_SIZE = 3 }; /** * Which multiprotocol parameters did we send,receive and negotiate * * @param safi - Subsequent address family identifier * @param d - direction, SENT, RECEIVED or NEGOTIATED * * @return true if this parameter was set */ template bool multiprotocol(Safi safi, Direction d = NEGOTIATED) const { XLOG_ASSERT(static_cast(d) < sizeof(_ipv4_unicast)); XLOG_ASSERT(static_cast(d) < sizeof(_ipv4_multicast)); XLOG_ASSERT(static_cast(d) < sizeof(_ipv6_unicast)); XLOG_ASSERT(static_cast(d) < sizeof(_ipv6_multicast)); switch(A::ip_version()) { case 4: switch(safi) { case SAFI_UNICAST: return _ipv4_unicast[d]; break; case SAFI_MULTICAST: return _ipv4_multicast[d]; break; } break; case 6: switch(safi) { case SAFI_UNICAST: return _ipv6_unicast[d]; break; case SAFI_MULTICAST: return _ipv6_multicast[d]; break; } break; default: XLOG_FATAL("Unknown IP version %u", XORP_UINT_CAST(A::ip_version())); break; } XLOG_UNREACHABLE(); return false; } /* * this is for debugging only */ template void set_multiprotocol(Safi safi, Direction d = NEGOTIATED) { XLOG_ASSERT(static_cast(d) < sizeof(_ipv4_unicast)); XLOG_ASSERT(static_cast(d) < sizeof(_ipv4_multicast)); XLOG_ASSERT(static_cast(d) < sizeof(_ipv6_unicast)); XLOG_ASSERT(static_cast(d) < sizeof(_ipv6_multicast)); switch(A::ip_version()) { case 4: switch(safi) { case SAFI_UNICAST: _ipv4_unicast[d] = true; return; case SAFI_MULTICAST: _ipv4_multicast[d] = true; return; } break; case 6: switch(safi) { case SAFI_UNICAST: _ipv6_unicast[d] = true; return; case SAFI_MULTICAST: _ipv6_multicast[d] = true; return; } break; default: XLOG_FATAL("Unknown IP version %u", XORP_UINT_CAST(A::ip_version())); break; } XLOG_UNREACHABLE(); return; } #if 0 bool ipv4_unicast(Direction d = NEGOTIATED) const { return _ipv4_unicast[d]; } bool ipv6_unicast(Direction d = NEGOTIATED) const { return _ipv6_unicast[d]; } bool ipv4_multicast(Direction d = NEGOTIATED) const { return _ipv4_multicast[d]; } bool ipv6_multicast(Direction d = NEGOTIATED) const { return _ipv6_multicast[d]; } #endif void set_v4_local_addr(const IPv4& addr) { _nexthop_ipv4 = addr; } void set_v6_local_addr(const IPv6& addr) { _nexthop_ipv6 = addr; } const IPv4& get_v4_local_addr() const { return _nexthop_ipv4; } const IPv6& get_v6_local_addr() const { return _nexthop_ipv6; } void set_configured_hold_time(uint16_t hold) { _configured_hold_time = hold; } uint16_t get_configured_hold_time() const { return _configured_hold_time; } void set_delay_open_time(uint32_t delay_open_time) { _delay_open_time = delay_open_time; } uint32_t get_delay_open_time() const { return _delay_open_time; } void set_next_hop_rewrite(const IPv4& next_hop) { _next_hop_rewrite = next_hop; } const IPv4 get_next_hop_rewrite() const { return _next_hop_rewrite; } void set_md5_password(const string &password) { _md5_password = password; } const string& get_md5_password() const { return _md5_password; } /** * Dump the state of the peer data (debugging). */ void dump_peer_data() const; protected: private: const LocalData& _local_data; void add_parameter(ParameterList& p_list, const ParameterNode& p); void remove_parameter(ParameterList& p_list, const ParameterNode& p); /** * Local Interface, Local Server Port, Peer Interface and * Peer Server Port tuple. */ const Iptuple _iptuple; AsNum _as; // Peer's AS number. bool _use_4byte_asnums; // True if we negotiated to use 4byte ASnums. bool _route_reflector; // True if this is a route reflector client. bool _confederation; // True if this is a confederation peer. /** * If enabled the maximum number of prefixes that will be accepted * on a peer before the session is torn down. */ ConfigVar _prefix_limit; /** * Holdtime in seconds. Value sent in open negotiation. */ uint16_t _configured_hold_time; /** * The number of seconds to wait before sending an open message. */ uint32_t _delay_open_time; /** * Peer's BGP ID. */ IPv4 _id; /* ** These values in seconds. */ uint32_t _hold_duration; uint32_t _retry_duration; uint32_t _keepalive_duration; /** * IPv4 nexthop */ IPv4 _nexthop_ipv4; /** * IPv6 nexthop */ IPv6 _nexthop_ipv6; PeerType _peer_type; // the type of this peering session /** * Parameters received by our peer. */ ParameterList _recv_parameters; /** * Parameters that we have sent. */ ParameterList _sent_parameters; /** * The options that we have both agreed on. */ ParameterList _negotiated_parameters; /** * The set of different topologies that we support. */ bool _ipv4_unicast[ARRAY_SIZE]; bool _ipv6_unicast[ARRAY_SIZE]; bool _ipv4_multicast[ARRAY_SIZE]; bool _ipv6_multicast[ARRAY_SIZE]; /* XXX ** Eventually we will have totally programmable filters. As a ** temporary hack store the re-write value here. */ IPv4 _next_hop_rewrite; /** * The password for TCP-MD5 authentication. */ string _md5_password; }; #endif // __BGP_PEER_DATA_HH__ xorp/bgp/harness/0000775000076400007640000000000011631507103014070 5ustar greearbgreearbxorp/bgp/harness/test_peering1.sh0000775000076400007640000013417111421137511017206 0ustar greearbgreearb#!/usr/bin/env bash # # $XORP: xorp/bgp/harness/test_peering1.sh,v 1.66 2007/12/10 23:26:33 mjh Exp $ # # # Test basic BGP error generation and connection creation. # # This script started with no arguments will start all required process and # terminate them at the end of the tests. # # Preconditons # 1) Run a finder process # 2) Run a FEA process. # 3) Run a RIB process. # 4) Run xorp "../xorp_bgp" # 5) Run "./test_peer -s peer1" # 6) Run "./coord" # set -e . ./setup_paths.sh # srcdir is set by make for check target if [ "X${srcdir}" = "X" ] ; then srcdir=`dirname $0` ; fi . ${srcdir}/xrl_shell_funcs.sh "" . $BGP_FUNCS "" . ${srcdir}/notification_codes.sh onexit() { last=$? if [ $last = "0" ] then echo "$0: Tests Succeeded (BGP: $TESTS)" else echo "$0: Tests Failed (BGP: $TESTS)" fi trap '' 0 2 } trap onexit 0 2 HOST=127.0.0.1 LOCALHOST=$HOST ID=192.150.187.78 AS=65008 USE4BYTEAS=false # IBGP - IPV4 PEER1=$HOST PORT1=10001 PEER1_PORT=20001 PEER1_AS=$AS # EBGP - IPV4 PEER2=$HOST PORT2=10002 PEER2_PORT=20002 PEER2_AS=65000 # IBGP - IPV6 PEER3=$HOST PORT3=10003 PEER3_PORT=20003 PEER3_AS=$AS # EBGP - IPV6 PEER4=$HOST PORT4=10004 PEER4_PORT=20004 PEER4_AS=65000 HOLDTIME=30 configure_bgp() { local_config $AS $ID $USE4BYTEAS # Don't try and talk to the rib. register_rib "" # IBGP - IPV4 PEER=$PEER1 NEXT_HOP=192.150.187.78 add_peer lo $LOCALHOST $PORT1 $PEER $PEER1_PORT $PEER1_AS $NEXT_HOP $HOLDTIME set_parameter $LOCALHOST $PORT1 $PEER $PEER1_PORT MultiProtocol.IPv4.Unicast true enable_peer $LOCALHOST $PORT1 $PEER $PEER1_PORT # EBGP - IPV4 PEER=$PEER2 NEXT_HOP=192.150.187.78 add_peer lo $LOCALHOST $PORT2 $PEER $PEER2_PORT $PEER2_AS $NEXT_HOP $HOLDTIME set_parameter $LOCALHOST $PORT2 $PEER $PEER2_PORT MultiProtocol.IPv4.Unicast true enable_peer $LOCALHOST $PORT2 $PEER $PEER2_PORT # IBGP - IPV6 PEER=$PEER3 NEXT_HOP=192.150.187.78 add_peer lo $LOCALHOST $PORT3 $PEER $PEER3_PORT $PEER3_AS $NEXT_HOP $HOLDTIME set_parameter $LOCALHOST $PORT3 $PEER $PEER3_PORT MultiProtocol.IPv6.Unicast true enable_peer $LOCALHOST $PORT3 $PEER $PEER3_PORT # IBGP - IPV6 PEER=$PEER4 NEXT_HOP=192.150.187.78 add_peer lo $LOCALHOST $PORT4 $PEER $PEER4_PORT $PEER4_AS $NEXT_HOP $HOLDTIME set_parameter $LOCALHOST $PORT4 $PEER $PEER4_PORT MultiProtocol.IPv6.Unicast true enable_peer $LOCALHOST $PORT4 $PEER $PEER4_PORT } reset() { coord reset coord target $HOST $PORT1 coord initialise attach peer1 } test1() { echo "TEST1 - Establish a peering with a holdtime of $HOLDTIME wait for expiration" reset # XXX # This test is a little suspect as the number of keepalives sent # by the peer is implementation dependent although it is # recommended that the keepalive interval is determined by # dividing the holdtime by three. There should be a way of # soaking up keepalives. coord peer1 expect packet open asnum $AS bgpid $ID holdtime $HOLDTIME \ afi 1 safi 1 coord peer1 expect packet keepalive coord peer1 expect packet keepalive coord peer1 expect packet keepalive coord peer1 expect packet keepalive coord peer1 expect packet notify $HOLDTIMEEXP coord peer1 establish AS $PEER1_AS \ holdtime $HOLDTIME \ id 192.150.187.100 \ keepalive false sleep $HOLDTIME sleep 2 sleep 2 coord peer1 assert queue 0 } test2() { echo "TEST2 - Make a connection and send an update packet before establishing a session" reset coord peer1 connect sleep 2 coord peer1 expect packet notify $FSMERROR coord peer1 send packet update sleep 2 coord peer1 assert queue 0 } test3() { echo "TEST3 - Try and send an illegal hold time of 1" reset coord peer1 expect packet open asnum $AS bgpid $ID holdtime $HOLDTIME \ afi 1 safi 1 coord peer1 expect packet notify $OPENMSGERROR $UNACCEPTHOLDTIME coord peer1 establish AS $PEER1_AS \ holdtime 1 \ id 192.150.187.100 \ keepalive false sleep 2 coord peer1 assert queue 0 } test4() { echo "TEST4 - Send an update packet and don't get it back" PACKET='packet update origin 2 aspath 1,2,[3,4,5],6,[7,8],9 nexthop 20.20.20.20 nlri 10.10.10.0/24 nlri 20.20.20.20/24 localpref 100' reset coord peer1 establish AS $PEER1_AS holdtime 0 id 192.150.187.100 coord peer1 assert established coord peer1 expect $PACKET coord peer1 send $PACKET sleep 2 coord peer1 assert queue 1 coord peer1 assert established } test5() { echo "TEST5 - Passively wait for a connection." coord reset coord target $HOST $PEER1_PORT coord initialise attach peer1 coord peer1 establish \ active false \ AS $PEER1_AS \ holdtime $HOLDTIME \ id 192.150.187.100 # Toggle the BGP peering to force a connection attempt. disable_peer $LOCALHOST $PORT1 $PEER1 $PEER1_PORT $PEER1_AS enable_peer $LOCALHOST $PORT1 $PEER1 $PEER1_PORT $PEER1_AS sleep 2 coord peer1 assert established } test6() { echo "TEST6 - Send an update packet without an origin" PACKET='packet update aspath 1,2,[3,4,5],6,[7,8],9 nexthop 20.20.20.20 nlri 10.10.10.0/24 nlri 20.20.20.20/24' reset coord peer1 establish AS $PEER1_AS holdtime 0 id 192.150.187.100 coord peer1 expect packet notify $UPDATEMSGERR $MISSWATTR 1 coord peer1 send $PACKET sleep 2 coord peer1 assert queue 0 } test7() { echo "TEST7 - Send an update packet without an aspath" PACKET='packet update origin 1 nexthop 20.20.20.20 nlri 10.10.10.0/24 nlri 20.20.20.20/24' reset coord peer1 establish AS $PEER1_AS holdtime 0 id 192.150.187.100 coord peer1 expect packet notify $UPDATEMSGERR $MISSWATTR 2 coord peer1 send $PACKET sleep 2 coord peer1 assert queue 0 } test8() { echo "TEST8 - Send an update packet without a nexthop" PACKET='packet update origin 1 aspath 1 nlri 10.10.10.0/24 nlri 20.20.20.20/24' reset coord peer1 establish AS $PEER1_AS holdtime 0 id 192.150.187.100 sleep 2 coord peer1 expect packet notify $UPDATEMSGERR $MISSWATTR 3 coord peer1 send $PACKET coord peer1 assert queue 0 } test8_ipv6() { echo "TEST8 IPv6" echo " 1) Establish an EBGP IPv6 peering" echo " 2) Send a multiprotocol nlri with no nexthop" coord reset coord target $HOST $PORT4 coord initialise attach peer1 coord peer1 establish AS $PEER4_AS holdtime 0 id 192.150.187.100 ipv6 true PACKET="packet update origin 1 aspath $PEER4_AS nlri6 2000::/3" coord peer1 expect packet notify $UPDATEMSGERR $MISSWATTR 3 coord peer1 send $PACKET sleep 2 coord peer1 assert queue 0 coord peer1 assert idle } test9() { echo "TEST9:" echo " 1) Send an update packet as an IBGP peer with no local pref" echo " 2) This is not an error. Our BGP emits a warning" PACKET='packet update origin 1 aspath 1 nexthop 20.20.20.20 nlri 10.10.10.0/24 nlri 20.20.20.20/24' reset coord peer1 establish AS $AS holdtime 0 id 192.150.187.100 coord peer1 assert established coord peer1 expect packet notify $UPDATEMSGERR $MISSWATTR 5 coord peer1 send $PACKET sleep 2 coord peer1 assert queue 1 coord peer1 assert established } test10() { echo "TEST10:" echo " 1) Send an update packet as an EBGP peer with local pref" echo " 2) This is not an error. Our BGP emits a warning" PACKET="packet update origin 1 aspath $PEER2_AS nexthop 20.20.20.20 localpref 100 nlri 10.10.10.0/24 nlri 20.20.20.20/24" coord reset coord target $HOST $PORT2 coord initialise attach peer1 coord peer1 establish AS $PEER2_AS holdtime 0 id 192.150.187.100 coord peer1 assert established #we dont expect an error - the line below is to trap one if it does occur coord peer1 expect packet notify $UPDATEMSGERR $MISSWATTR coord peer1 send $PACKET sleep 2 coord peer1 assert queue 1 coord peer1 assert established } test11() { echo "TEST11:" echo " 1) Send an update packet with two NLRIs" echo " 2) Then send an update packet to withdraw both NLRIs" PACKET_1="packet update origin 1 aspath $PEER2_AS nexthop 20.20.20.20 nlri 10.10.10.0/24 nlri 20.20.20.20/24" PACKET_2='packet update withdraw 10.10.10.0/24 withdraw 20.20.20.20/24' coord reset coord target $HOST $PORT2 coord initialise attach peer1 coord peer1 establish AS $PEER2_AS holdtime 0 id 192.150.187.100 coord peer1 assert established coord peer1 expect packet notify $UPDATEMSGERR $MISSWATTR coord peer1 send $PACKET_1 sleep 2 coord peer1 assert queue 1 coord peer1 assert established sleep 2 coord peer1 assert established coord peer1 expect packet notify $UPDATEMSGERR $MISSWATTR coord peer1 send $PACKET_2 sleep 2 coord peer1 assert queue 2 coord peer1 assert established } test12() { echo "TEST12 - Send an update packet on an IBGP peer twice" PACKET='packet update origin 1 aspath 1 localpref 2 nexthop 20.20.20.20 nlri 10.10.10.0/24 nlri 20.20.20.20/24' reset coord peer1 establish AS $AS holdtime 0 id 192.150.187.100 coord peer1 assert established coord peer1 expect packet notify $UPDATEMSGERR $MISSWATTR coord peer1 send $PACKET coord peer1 send $PACKET sleep 2 coord peer1 assert queue 1 coord peer1 assert established } test12_ipv6() { echo "TEST12 IPV6 - Send an update packet on an IBGP peer twice" PACKET="packet update origin 1 aspath $PEER4_AS localpref 2 nexthop6 20:20:20:20:20:20:20:20 nlri6 1000::/3 nlri6 2000::/3" coord reset coord target $HOST $PORT4 coord initialise attach peer1 coord peer1 establish AS $PEER4_AS holdtime 0 id 192.150.187.100 ipv6 true coord peer1 assert established coord peer1 expect packet notify $UPDATEMSGERR $MISSWATTR coord peer1 send $PACKET coord peer1 send $PACKET sleep 2 coord peer1 assert queue 1 coord peer1 assert established } test13() { echo "TEST13:" echo " 1) Send an update packet on an IBGP peer with no local pref twice" echo " 2) This is not an error. Our BGP emits a warnings" PACKET='packet update origin 1 aspath 1 nexthop 20.20.20.20 nlri 10.10.10.0/24 nlri 20.20.20.20/24' reset coord peer1 establish AS $AS holdtime 0 id 192.150.187.100 coord peer1 assert established coord peer1 expect packet notify $UPDATEMSGERR $MISSWATTR coord peer1 send $PACKET coord peer1 send $PACKET sleep 2 coord peer1 assert queue 1 coord peer1 assert established } test14() { echo "TEST14 - Send an update packet on an EBGP peer twice" PACKET="packet update origin 1 aspath $PEER2_AS nexthop 20.20.20.20 nlri 10.10.10.0/24 nlri 20.20.20.20/24" coord reset coord target $HOST $PORT2 coord initialise attach peer1 coord peer1 establish AS $PEER2_AS holdtime 0 id 192.150.187.100 coord peer1 assert established coord peer1 expect packet notify $UPDATEMSGERR $MISSWATTR coord peer1 send $PACKET coord peer1 send $PACKET sleep 2 coord peer1 assert queue 1 coord peer1 assert established } test15() { echo "TEST15 - Establish a connection drop the peering and establish again" for i in 1 2 do coord reset coord target $HOST $PORT2 coord initialise attach peer1 coord peer1 establish AS $PEER2_AS holdtime 0 id 192.150.187.100 coord peer1 assert established done } test16() { echo "TEST16 - Send two update packets on an EBGP peer with local pref set" # Local preference should not be set on an update packet from an EBGP peer. coord reset coord target $HOST $PORT2 coord initialise attach peer1 coord peer1 establish AS $PEER2_AS holdtime 0 id 192.150.187.100 coord peer1 assert established PACKET_1="packet update origin 1 aspath $PEER2_AS nexthop 20.20.20.20 localpref 100 nlri 10.10.10.0/16" PACKET_2="packet update origin 1 aspath $PEER2_AS,2 nexthop 20.20.20.20 localpref 100 nlri 30.30.30.0/24" coord peer1 send $PACKET_1 coord peer1 send $PACKET_2 # Make sure that the session still exists. sleep 2 coord peer1 assert established # Reset the connection coord reset coord target $HOST $PORT2 coord initialise attach peer1 } test17() { echo "TEST17 - Send an update packet with a repeated NLRI." coord reset coord target $HOST $PORT2 coord initialise attach peer1 coord peer1 establish AS $PEER2_AS holdtime 0 id 192.150.187.100 coord peer1 assert established PACKET="packet update origin 1 aspath $PEER2_AS nexthop 20.20.20.20 nlri 30.30.30.0/24 nlri 30.30.30.0/24" coord peer1 send $PACKET # Make sure that the session still exists. sleep 2 coord peer1 assert established # Reset the connection coord reset coord target $HOST $PORT2 coord initialise attach peer1 } test18() { echo "TEST18 - Send two update packets on an EBGP peer." coord reset coord target $HOST $PORT2 coord initialise attach peer1 coord peer1 establish AS $PEER2_AS holdtime 0 id 192.150.187.100 coord peer1 assert established PACKET_1="packet update origin 1 aspath $PEER2_AS nexthop 20.20.20.20 nlri 10.10.10.0/16" PACKET_2="packet update origin 1 aspath $PEER2_AS,2 nexthop 20.20.20.20 nlri 30.30.30.0/24" coord peer1 send $PACKET_1 coord peer1 send $PACKET_2 # Make sure that the session still exists. sleep 2 coord peer1 assert established # Reset the connection coord reset coord target $HOST $PORT2 coord initialise attach peer1 # Make a new connection. sleep 2 coord peer1 establish AS $PEER2_AS holdtime 0 id 192.150.187.100 coord peer1 assert established } test19() { echo "TEST19 - EBGP peer update packet then different withdraw" coord reset coord target $HOST $PORT2 coord initialise attach peer1 coord peer1 establish AS $PEER2_AS holdtime 0 id 192.150.187.100 coord peer1 assert established PACKET_1='packet update origin 1 aspath 1 nexthop 20.20.20.20 nlri 10.10.10.0/16' PACKET_2='packet update withdraw 30.30.30.0/24' #coord peer1 send $PACKET_1 coord peer1 send $PACKET_2 # Make sure that the session still exists. sleep 2 coord peer1 assert established # Reset the connection coord reset coord target $HOST $PORT2 coord initialise attach peer1 sleep 2 # Make a new connection. coord peer1 establish AS $PEER2_AS holdtime 0 id 192.150.187.100 sleep 2 coord peer1 assert established } test20() { echo "TEST20 - EBGP peer single withdraw" coord reset coord target $HOST $PORT2 coord initialise attach peer1 coord peer1 establish AS $PEER2_AS holdtime 0 id 192.150.187.100 coord peer1 assert established PACKET='packet update withdraw 30.30.30.0/24' coord peer1 send $PACKET # Make sure that the session still exists. sleep 2 coord peer1 assert established # Reset the connection coord reset coord target $HOST $PORT2 coord initialise attach peer1 sleep 2 # Make a new connection. coord peer1 establish AS $PEER2_AS holdtime 0 id 192.150.187.100 sleep 2 coord peer1 assert established } test20_ipv6() { echo "TEST20 IPV6 - EBGP peer single withdraw" coord reset coord target $HOST $PORT4 coord initialise attach peer1 coord peer1 establish AS $PEER4_AS holdtime 0 id 192.150.187.100 ipv6 true coord peer1 assert established PACKET='packet update withdraw6 2000::/3' coord peer1 send $PACKET # Make sure that the session still exists. sleep 2 coord peer1 assert established # Reset the connection coord reset coord target $HOST $PORT4 coord initialise attach peer1 sleep 2 # Make a new connection. coord peer1 establish AS $PEER4_AS holdtime 0 id 192.150.187.100 ipv6 true sleep 2 coord peer1 assert established } test21() { echo "TEST21 - EBGP peer update packet then corresponding withdraw" coord reset coord target $HOST $PORT2 coord initialise attach peer1 coord peer1 establish AS $PEER2_AS holdtime 0 id 192.150.187.100 coord peer1 assert established PACKET_1="packet update origin 1 aspath $PEER2_AS nexthop 20.20.20.20 nlri 10.10.10.0/16" PACKET_2='packet update withdraw 10.10.10.0/16' coord peer1 send $PACKET_1 coord peer1 send $PACKET_2 # Make sure that the session still exists. sleep 2 coord peer1 assert established # Reset the connection coord reset coord target $HOST $PORT2 coord initialise attach peer1 sleep 2 # Make a new connection. coord peer1 establish AS $PEER2_AS holdtime 0 id 192.150.187.100 sleep 2 coord peer1 assert established } test22() { echo "TEST22 - EBGP Establish an IPV6 peering" coord reset coord target $HOST $PORT3 coord initialise attach peer1 coord peer1 establish AS $PEER3_AS holdtime 0 id 192.150.187.100 ipv6 true coord peer1 assert established } test23() { echo "TEST23" echo " 1) Establish an IBGP IPV6 peering" echo " 2) Send an IPv6 only update packet" coord reset coord target $HOST $PORT3 coord initialise attach peer1 coord peer1 establish AS $PEER3_AS holdtime 0 id 192.150.187.100 ipv6 true PACKET="packet update origin 1 aspath 1,2,[3,4,5],6,[7,8],9 nexthop6 20:20:20:20:20:20:20:20 nlri6 2000::/3 localpref 100" coord peer1 send $PACKET coord peer1 assert established } test24() { echo "TEST24" echo " 1) Establish an EBGP IPV6 peering" echo " 2) Send an IPv6 only update packet" coord reset coord target $HOST $PORT4 coord initialise attach peer1 coord peer1 establish AS $PEER4_AS holdtime 0 id 192.150.187.100 ipv6 true PACKET="packet update origin 1 aspath $PEER4_AS nexthop6 20:20:20:20:20:20:20:20 nlri6 2000::/3" coord peer1 send $PACKET coord peer1 assert established } test25() { echo "TEST25" echo " 1) Establish an EBGP IPV6 peering" echo " 2) Send an IPv4 and IPv6 update packet" coord reset coord target $HOST $PORT4 coord initialise attach peer1 coord peer1 establish AS $PEER4_AS holdtime 0 id 192.150.187.100 ipv6 true PACKET="packet update origin 1 aspath $PEER4_AS nexthop 20.20.20.20 nlri 10.10.10.0/24 nexthop6 20:20:20:20:20:20:20:20 nlri6 2000::/3" coord peer1 send $PACKET coord peer1 assert established } test26() { # this test is broken - we cannot generate path atribute lists with # duplicate entries now, because we store it as an array indexed by # type. echo "TEST26" echo " 1) Establish an EBGP IPV6 peering" echo " 2) Send an IPv4 and IPv6 update packet repeat both nexthops" coord reset coord target $HOST $PORT4 coord initialise attach peer1 coord peer1 establish AS $PEER4_AS holdtime 0 id 192.150.187.100 ipv6 true PACKET="packet update origin 1 aspath $PEER4_AS nexthop 20.20.20.20 nexthop 20.20.20.20 nlri 10.10.10.0/24 nexthop6 20:20:20:20:20:20:20:20 nexthop6 20:20:20:20:20:20:20:20 nlri6 2000::/3" coord peer1 expect packet notify $UPDATEMSGERR $MALATTRLIST coord peer1 send $PACKET sleep 2 coord peer1 assert queue 0 coord peer1 assert idle } test27() { echo "TEST27 - Verify that routes originated by BGP reach an IBGP peer" coord reset coord target $HOST $PORT1 coord initialise attach peer1 # Introduce a route originate_route4 10.10.10.0/24 20.20.20.20 true false coord peer1 establish AS $PEER1_AS holdtime 0 id 192.150.187.100 sleep 2 coord peer1 trie recv lookup 10.10.10.0/24 aspath empty coord peer1 assert queue 0 coord peer1 assert established } test27_ipv6() { echo "TEST27 (IPV6) - Verify that routes originated by BGP reach an IBGP peer" coord reset coord target $HOST $PORT3 coord initialise attach peer1 # Introduce a route originate_route6 2000::/3 20:20:20:20:20:20:20:20 true false coord peer1 establish AS $PEER1_AS holdtime 0 id 192.150.187.100 ipv6 true sleep 2 coord peer1 trie recv lookup 2000::/3 aspath empty coord peer1 assert queue 0 coord peer1 assert established } test28() { echo "TEST28 - Verify that routes originated by BGP reach an EBGP peer" echo "Also verify that the nexthop is rewritten and the provided nexthop" echo "is ignored" coord reset coord target $HOST $PORT2 coord initialise attach peer1 coord peer1 expect packet open asnum $AS bgpid $ID holdtime $HOLDTIME \ afi 1 safi 1 coord peer1 expect packet keepalive coord peer1 expect packet update \ origin 0 \ nexthop 192.150.187.78 \ aspath 65008 \ med 1 \ nlri 10.10.10.0/24 # Introduce a route originate_route4 10.10.10.0/24 20.20.20.20 true false coord peer1 establish AS $PEER2_AS holdtime 0 id 192.150.187.100 sleep 2 coord peer1 trie recv lookup 10.10.10.0/24 aspath 65008 coord peer1 assert queue 0 coord peer1 assert established } test28_ipv6() { echo "TEST28 (IPV6) - Verify that routes originated by BGP reach an EBGP peer" echo "Test that next-hop of :: fails (but does not crash)" echo "Also verify that the nexthop is rewritten and the provided nexthop" echo "is ignored" coord reset coord target $HOST $PORT4 coord initialise attach peer1 coord peer1 expect packet open asnum $AS bgpid $ID holdtime $HOLDTIME \ afi 2 safi 1 coord peer1 expect packet keepalive coord peer1 expect packet update \ origin 0 \ aspath 65008 \ med 1 \ nlri6 2000::/3 \ nexthop6 :: # Introduce a route originate_route6 2000::/3 20:20:20:20:20:20:20:20 true false coord peer1 establish AS $PEER2_AS holdtime 0 id 192.150.187.100 ipv6 true sleep 2 # This is supposed to fail..nexthop :: is invalid above. # TODO: Figure out how to do test for failure here that doesn't fail # the test. #coord peer1 trie recv lookup 2000::/3 aspath 65008 coord peer1 assert queue 1 coord peer1 assert established } test28_ipv6_ok() { echo "TEST28 (IPV6-OK) - Verify that routes originated by BGP reach an EBGP peer" echo "Also verify that the nexthop is rewritten and the provided nexthop" echo "is ignored" # NOTE: This fails because somehow a route with zero destination # gets into the routing tables. See route_table_ribin::dump_next_route coord reset coord target $HOST $PORT4 coord initialise attach peer1 coord peer1 expect packet open asnum $AS bgpid $ID holdtime $HOLDTIME \ afi 2 safi 1 coord peer1 expect packet keepalive coord peer1 expect packet update \ origin 0 \ aspath 65008 \ med 1 \ nlri6 2000::/3 \ nexthop6 2000:77:88::1 # Introduce a route originate_route6 2000::/3 20:20:20:20:20:20:20:20 true false coord peer1 establish AS $PEER2_AS holdtime 0 id 192.150.187.100 ipv6 true sleep 2 coord peer1 trie recv lookup 2000::/3 aspath 65008 coord peer1 assert queue 0 coord peer1 assert established } test29() { echo "TEST29" echo " 1) Make a connection" echo " 2) Send a notify packet before establishing a session" echo " 3) Verify that we do not get a notify packet in response" echo " 4) Verify that the TCP session has been torn down" reset coord peer1 connect # Verify that we have a connection sleep 2 coord peer1 assert connected sleep 2 # We have to add something to the queue in order to make the # assertion later coord peer1 expect packet notify $FSMERROR coord peer1 send packet notify $UPDATEMSGERR $MALATTRLIST sleep 2 coord peer1 assert queue 1 sleep 2 coord peer1 assert idle } test30() { echo "TEST30 - Bugzilla BUG #32" echo " 1) The test peer performs a bind but no listen" echo " 2) A BGP connection attempt will leave it in connect state" echo " 3) Toggle the peering to ensure BGP can get out of this state" coord reset BASE=peer1 bind $HOST $PEER1_PORT # Toggle the BGP peering to force a connection attempt. disable_peer $LOCALHOST $PORT1 $PEER1 $PEER1_PORT $PEER1_AS enable_peer $LOCALHOST $PORT1 $PEER1 $PEER1_PORT $PEER1_AS # The BGP should be in the CONNECT state now. # Toggle the BGP peering again to verify that it can get out of the # CONNECT state. disable_peer $LOCALHOST $PORT1 $PEER1 $PEER1_PORT $PEER1_AS enable_peer $LOCALHOST $PORT1 $PEER1 $PEER1_PORT $PEER1_AS # Need to find out that the BGP process still exists so make a peering. BASE=peer1 ${srcdir}/xrl_shell_funcs.sh reset sleep 2 coord reset coord target $HOST $PORT1 coord initialise attach peer1 coord peer1 establish \ active true \ AS $PEER1_AS \ holdtime $HOLDTIME \ id 192.150.187.100 sleep 2 coord peer1 assert established } test31() { echo "TEST31 - On an I-BGP peering send an update with an empty aspath" PACKET='packet update origin 1 aspath empty nexthop 20.20.20.20 nlri 10.10.10.0/24' reset coord peer1 establish AS $AS holdtime 0 id 192.150.187.100 coord peer1 assert established coord peer1 send $PACKET sleep 2 coord peer1 assert established } test32() { echo "TEST32 - Bugzilla BUG #139" echo " 1) Originate a route on an I-BGP peering with an empty aspath." echo " 2) Send the same route to the BGP process." echo " 3) The comparison of two routes with empty aspath caused BGP" echo " BGP to fail" coord reset coord target $HOST $PORT1 coord initialise attach peer1 # Introduce a route originate_route4 10.10.10.0/24 20.20.20.20 true false coord peer1 establish AS $PEER1_AS holdtime 0 id 192.150.187.100 sleep 2 coord peer1 trie recv lookup 10.10.10.0/24 aspath empty PACKET='packet update origin 2 aspath empty nexthop 20.20.20.20 nlri 10.10.10.0/24' coord peer1 send $PACKET sleep 2 coord peer1 assert queue 0 coord peer1 assert established } test33() { echo "TEST33 - DampPeerOscillations 10 sessions 11th should fail" for i in 1 2 3 4 5 6 7 8 9 10 do coord reset coord target $HOST $PORT2 coord initialise attach peer1 coord peer1 establish AS $PEER2_AS holdtime 0 id 192.150.187.100 sleep 2 coord peer1 assert established done coord reset coord target $HOST $PORT2 coord initialise attach peer1 coord peer1 establish AS $PEER2_AS holdtime 0 id 192.150.187.100 sleep 2 coord peer1 assert idle } test34() { echo "TEST34 - Illicit a Message Header Error Connection Not Synchronized." echo " 1) Establish a connection" echo " 2) Send a keepalive with a corrupted marker field" echo " 3) Should return a notify Connection Not Synchronized." coord reset coord target $HOST $PORT2 coord initialise attach peer1 coord peer1 establish AS $PEER2_AS holdtime 0 id 192.150.187.100 coord peer1 expect packet notify $MSGHEADERERR $CONNNOTSYNC coord peer1 send packet corrupt 0 0 keepalive sleep 2 coord peer1 assert queue 0 sleep 2 coord peer1 assert idle } test35() { echo "TEST35 - Illicit a Message Header Error Bad Message Length. 0" echo " 1) Establish a connection" echo " 2) Send a keepalive with a short message length" echo " 3) Should return a notify Bad Message Length.." coord reset coord target $HOST $PORT2 coord initialise attach peer1 coord peer1 establish AS $PEER2_AS holdtime 0 id 192.150.187.100 coord peer1 expect packet notify $MSGHEADERERR $BADMESSLEN 0 0 coord peer1 send packet corrupt 17 0 keepalive sleep 2 coord peer1 assert queue 0 sleep 2 coord peer1 assert idle } test36() { echo "TEST36 - Illicit a Message Header Error Bad Message Length. 20" echo " 1) Establish a connection" echo " 2) Send a keepalive with a long message length" echo " 3) Should return a notify Bad Message Length." coord reset coord target $HOST $PORT2 coord initialise attach peer1 coord peer1 establish AS $PEER2_AS holdtime 0 id 192.150.187.100 coord peer1 expect packet notify $MSGHEADERERR $BADMESSLEN 0 20 coord peer1 send packet corrupt 17 20 keepalive # send a second message to force the previous one to be delivered. coord peer1 send packet keepalive sleep 2 coord peer1 assert queue 0 sleep 2 coord peer1 assert idle } test37() { echo "TEST37 - Illicit a Message Header Error Bad Message Type." echo " 1) Establish a connection" echo " 2) Send a message with the type set to 10" echo " 3) Should return a notify Bad Message Type with the 10" coord reset coord target $HOST $PORT2 coord initialise attach peer1 coord peer1 establish AS $PEER2_AS holdtime 0 id 192.150.187.100 coord peer1 expect packet notify $MSGHEADERERR $BADMESSTYPE 10 coord peer1 send packet corrupt 18 10 keepalive sleep 2 coord peer1 assert queue 0 sleep 2 coord peer1 assert idle } test38() { echo "TEST38 - Illicit a UPDATE Message Error Malformed AS_PATH." echo " 1) Establish a connection" echo " 2) Send a malformed aspath" echo " 3) Should return a notify UPDATE Message Error." coord reset coord target $HOST $PORT2 coord initialise attach peer1 coord peer1 establish AS $PEER2_AS holdtime 0 id 192.150.187.100 coord peer1 expect packet notify $UPDATEMSGERR $MALASPATH # # Hit the check in AsPath::decode() # # coord peer1 send packet corrupt 38 0 coord peer1 send packet corrupt 32 0 \ update \ origin 2 \ aspath "[$PEER2_AS],[$PEER2_AS]" \ nlri 10.10.10.0/24 \ nexthop 20.20.20.20 sleep 2 coord peer1 assert queue 0 sleep 2 coord peer1 assert idle } test39() { echo "TEST39 - Send a short open packet. 20" echo " 1) Send an open packet with a length field of 20" echo " 2) Should return a notify Bad Message Length." reset coord peer1 connect sleep 2 coord peer1 expect packet notify $MSGHEADERERR $BADMESSLEN 0 20 coord peer1 send packet corrupt 17 20 \ open \ asnum $PEER1_AS \ bgpid 192.150.187.100 \ holdtime 0 sleep 2 coord peer1 assert queue 0 } test40() { echo "TEST40 - Open message with illegal version number" echo " 1) Send an open message with a version number of 6" echo " 2) Should get a notify with unsupported version number 4" reset coord peer1 connect sleep 2 coord peer1 expect packet notify $OPENMSGERROR $UNSUPVERNUM 0 4 coord peer1 send packet corrupt 19 6 \ open \ asnum $PEER1_AS \ bgpid 192.150.187.100 \ holdtime 0 sleep 2 coord peer1 assert queue 0 } test41() { echo "TEST41 - Open message with an illegal BGP ID" echo " 1) Send an open message with a BGP ID of 0.0.0.0" echo " 2) Should get a notify with Bad BGP Identifier" reset coord peer1 connect sleep 2 coord peer1 expect packet notify $OPENMSGERROR $BADBGPIDENT coord peer1 send packet \ open \ asnum $PEER1_AS \ bgpid 0.0.0.0 \ holdtime 0 sleep 2 coord peer1 assert queue 0 } test42() { echo "TEST42 - Send an update message that has a short length field." echo " 1) Establish a connection" echo " 2) Send an update with a short length 22" echo " 3) Should return a notify Bad Message Length" coord reset coord target $HOST $PORT2 coord initialise attach peer1 coord peer1 establish AS $PEER2_AS holdtime 0 id 192.150.187.100 coord peer1 expect packet notify $MSGHEADERERR $BADMESSLEN 0 22 coord peer1 send packet corrupt 17 22 \ update \ origin 2 \ aspath "[$PEER2_AS],[$PEER2_AS]" \ nlri 10.10.10.0/24 \ nexthop 20.20.20.20 sleep 2 coord peer1 assert queue 0 sleep 2 coord peer1 assert idle } test43() { echo "TEST43 - Send an notify message that has a short length field." echo " 1) Establish a connection" echo " 2) Send a notify with a short length 20" echo " 3) Should return a notify Bad Message Length" coord reset coord target $HOST $PORT2 coord initialise attach peer1 coord peer1 establish AS $PEER2_AS holdtime 0 id 192.150.187.100 coord peer1 expect packet notify $MSGHEADERERR $BADMESSLEN 0 20 coord peer1 send packet corrupt 17 20 notify $CEASE sleep 2 coord peer1 assert queue 0 sleep 2 coord peer1 assert idle } test44() { echo "TEST44 - Send an update message with an origin with bad flags." echo " 1) Establish a connection" echo " 2) Send an update with an origin with bad flags" echo " 3) Should return an UPDATE Message Error Attribute" coord reset coord target $HOST $PORT2 coord initialise attach peer1 coord peer1 establish AS $PEER2_AS holdtime 0 id 192.150.187.100 coord peer1 expect packet notify $UPDATEMSGERR $ATTRFLAGS 0 1 1 1 coord peer1 send packet \ update \ pathattr 0,1,1,1 \ aspath "[$PEER2_AS],[$PEER2_AS]" \ nexthop 20.20.20.20 \ nlri 10.10.10.0/24 sleep 2 coord peer1 assert queue 0 sleep 2 coord peer1 assert idle } test45() { echo "TEST45 - Send an update message with an aspath with bad flags." echo " 1) Establish a connection" echo " 2) Send an update with an aspath with bad flags" echo " 3) Should return an UPDATE Message Error Attribute" coord reset coord target $HOST $PORT2 coord initialise attach peer1 coord peer1 establish AS $PEER2_AS holdtime 0 id 192.150.187.100 coord peer1 expect packet notify $UPDATEMSGERR $ATTRFLAGS 0 2 4 2 1 253 232 # 0 - Attribute Flags (BAD) # 2 - Attribute Type Code (AS_PATH) # 4 - Attribute Length # 2 - Path Segment Type (AS_SEQUENCE) # 1 - Path Segment Length (One AS) # 253 - Path Segment Value (65000 / 256) # 232 - Path Segment Value (65000 % 256) coord peer1 send packet \ update \ origin 2 \ pathattr 0,2,4,2,1,253,232 \ nexthop 20.20.20.20 \ nlri 10.10.10.0/24 sleep 2 coord peer1 assert queue 0 sleep 2 coord peer1 assert idle } test46() { echo "TEST46 - Send an update message with a nexthop with bad flags." echo " 1) Establish a connection" echo " 2) Send an update with a nexthop with bad flags" echo " 3) Should return an UPDATE Message Error Attribute" coord reset coord target $HOST $PORT2 coord initialise attach peer1 coord peer1 establish AS $PEER2_AS holdtime 0 id 192.150.187.100 coord peer1 expect packet notify $UPDATEMSGERR $ATTRFLAGS 0 3 4 20 20 20 20 # 0 - Attribute Flags (BAD) # 3 - Attribute Type Code (NEXT_HOP) # 4 - Attribute Length # 20 - Value # 20 - Value # 20 - Value # 20 - Value coord peer1 send packet \ update \ origin 2 \ pathattr 0,3,4,20,20,20,20 \ nlri 10.10.10.0/24 sleep 2 coord peer1 assert queue 0 sleep 2 coord peer1 assert idle } test47() { echo "TEST47 - Send an update message with a med with bad flags." echo " 1) Establish a connection" echo " 2) Send an update with a med with bad flags" echo " 3) Should return an UPDATE Message Error Attribute" coord reset coord target $HOST $PORT2 coord initialise attach peer1 coord peer1 establish AS $PEER2_AS holdtime 0 id 192.150.187.100 coord peer1 expect packet notify $UPDATEMSGERR $ATTRFLAGS 0 4 4 20 20 20 20 # 0 - Attribute Flags (BAD) # 4 - Attribute Type Code (MULTI_EXIT_DISC) # 4 - Attribute Length # 20 - Value # 20 - Value # 20 - Value # 20 - Value coord peer1 send packet \ update \ origin 2 \ aspath "[$PEER2_AS],[$PEER2_AS]" \ pathattr 0,4,4,20,20,20,20 \ nlri 10.10.10.0/24 sleep 2 coord peer1 assert queue 0 sleep 2 coord peer1 assert idle } test48() { echo "TEST48 - Send an update message with a LOCAL_PREF with bad flags." echo " 1) Establish a connection" echo " 2) Send an update with a LOCAL_PREF with bad flags" echo " 3) Should return an UPDATE Message Error Attribute" coord reset coord target $HOST $PORT2 coord initialise attach peer1 coord peer1 establish AS $PEER2_AS holdtime 0 id 192.150.187.100 coord peer1 expect packet notify $UPDATEMSGERR $ATTRFLAGS 0 5 4 20 20 20 20 # 0 - Attribute Flags (BAD) # 5 - Attribute Type Code (LOCAL_PREF) # 4 - Attribute Length # 20 - Value # 20 - Value # 20 - Value # 20 - Value coord peer1 send packet \ update \ origin 2 \ aspath "[$PEER2_AS],[$PEER2_AS]" \ pathattr 0,5,4,20,20,20,20 \ nlri 10.10.10.0/24 sleep 2 coord peer1 assert queue 0 sleep 2 coord peer1 assert idle } test49() { echo "TEST49 - An update message with an ATOMIC_AGGREGATE with bad flags." echo " 1) Establish a connection" echo " 2) Send an update with a ATOMIC_AGGREGATE with bad flags" echo " 3) Should return an UPDATE Message Error Attribute" coord reset coord target $HOST $PORT2 coord initialise attach peer1 coord peer1 establish AS $PEER2_AS holdtime 0 id 192.150.187.100 coord peer1 expect packet notify $UPDATEMSGERR $ATTRFLAGS 0 6 0 # 0 - Attribute Flags (BAD) # 6 - Attribute Type Code (ATOMIC_AGGREGATE) # 0 - Attribute Length coord peer1 send packet \ update \ origin 2 \ aspath "[$PEER2_AS],[$PEER2_AS]" \ pathattr 0,6,0 \ nlri 10.10.10.0/24 sleep 2 coord peer1 assert queue 0 sleep 2 coord peer1 assert idle } test50() { echo "TEST50 - An update message with an AGGREGATOR with bad flags." echo " 1) Establish a connection" echo " 2) Send an update with a AGGREGATOR with bad flags" echo " 3) Should return an UPDATE Message Error Attribute" coord reset coord target $HOST $PORT2 coord initialise attach peer1 coord peer1 establish AS $PEER2_AS holdtime 0 id 192.150.187.100 coord peer1 expect packet notify $UPDATEMSGERR $ATTRFLAGS \ 0 7 6 1 1 20 20 20 20 # 0 - Attribute Flags (BAD) # 7 - Attribute Type Code (ATOMIC_AGGREGATE) # 6 - Attribute Length # 1 - Last AS # 1 - Last AS # 20 - IP address # 20 - IP address # 20 - IP address # 20 - IP address coord peer1 send packet \ update \ origin 2 \ aspath "[$PEER2_AS],[$PEER2_AS]" \ pathattr 0,7,6,1,1,20,20,20,20 \ nlri 10.10.10.0/24 sleep 2 coord peer1 assert queue 0 sleep 2 coord peer1 assert idle } test51() { echo "TEST51 - An update message with an origin with an illegal value." echo " 1) Establish a connection" echo " 2) Send an update with an origin with an illegal value" echo " 3) Should return an UPDATE Message Error Attribute" coord reset coord target $HOST $PORT2 coord initialise attach peer1 coord peer1 establish AS $PEER2_AS holdtime 0 id 192.150.187.100 coord peer1 expect packet notify $UPDATEMSGERR $INVALORGATTR 64 1 1 5 # 0x40 - Attribute Flags # 1 - Attribute Type Code (ORIGIN) # 1 - Attribute Length # 5 - Attribute value (ILLEGAL) coord peer1 send packet \ update \ pathattr 64,1,1,5 \ aspath "[$PEER2_AS],[$PEER2_AS]" \ nexthop 20.20.20.20 \ nlri 10.10.10.0/24 sleep 2 coord peer1 assert queue 0 sleep 2 coord peer1 assert idle } test52() { echo "TEST52 - An update message with an invalid nexthop" echo " 1) Establish a connection" echo " 2) Send an update with a nexthop with an illegal value" echo " 3) Should return an UPDATE Message Error Attribute" coord reset coord target $HOST $PORT2 coord initialise attach peer1 coord peer1 establish AS $PEER2_AS holdtime 0 id 192.150.187.100 #coord peer1 expect packet notify $UPDATEMSGERR $INVALNHATTR \ # 64 3 4 255 255 255 255 coord peer1 expect packet notify $UPDATEMSGERR $INVALNHATTR # 64 - Attribute Flags # 3 - Attribute Type Code (NEXT_HOP) # 4 - Attribute Length # 255 - Value # 255 - Value # 255 - Value # 255 - Value coord peer1 send packet \ update \ origin 2 \ pathattr 64,3,4,255,255,255,255 \ nlri 10.10.10.0/24 sleep 2 coord peer1 assert queue 0 sleep 2 coord peer1 assert idle } test53() { echo "TEST53 - A NLRI that is syntactically incorrect" echo " 1) Establish a connection" echo " 2) Should return an UPDATE Message Error Attribute" coord reset coord target $HOST $PORT2 coord initialise attach peer1 coord peer1 establish AS $PEER2_AS holdtime 0 id 192.150.187.100 coord peer1 expect packet notify $UPDATEMSGERR $INVALNETFIELD # Make the number of bits 54 coord peer1 send packet corrupt 45 54\ update \ origin 2 \ aspath "[$PEER2_AS],[$PEER2_AS]" \ nexthop 20.20.20.20 \ nlri 10.10.10.0/24 sleep 2 coord peer1 assert queue 0 sleep 2 coord peer1 assert idle } test54() { echo "TEST54 - A mandatory unknown path attribute." echo " 1) Establish a connection" echo " 2) Should get an Unrecognized Well-known Attribute" coord reset coord target $HOST $PORT2 coord initialise attach peer1 coord peer1 establish AS $PEER2_AS holdtime 0 id 192.150.187.100 # 19 in this test is the test unknown path attribute - see path_attribute.hh coord peer1 expect packet notify $UPDATEMSGERR $UNRECOGWATTR 0 19 1 0 coord peer1 send packet \ update \ origin 2 \ aspath "[$PEER2_AS],[$PEER2_AS]" \ nexthop 20.20.20.20 \ nlri 10.10.10.0/24 \ pathattr 0,19,1,0 sleep 2 coord peer1 assert queue 0 sleep 2 coord peer1 assert idle } test55() { echo "TEST55 - Send an AS_PATH with confederation segments." echo " 1) Establish a connection" echo " 2) Should get a Malformed AS_PATH" coord reset coord target $HOST $PORT2 coord initialise attach peer1 coord peer1 establish AS $PEER2_AS holdtime 0 id 192.150.187.100 coord peer1 expect packet notify $UPDATEMSGERR $MALASPATH coord peer1 send packet \ update \ origin 2 \ aspath "[$PEER2_AS],($PEER2_AS)" \ nexthop 20.20.20.20 \ nlri 10.10.10.0/24 sleep 2 coord peer1 assert queue 0 sleep 2 coord peer1 assert idle } test56() { echo "TEST56 - Routes originated by BGP sent to an I-BGP peer:" echo " 1) Should have a local pref added." echo " 2) Should have an empty aspath." echo " 3) Should honour the next hop?" coord reset coord target $HOST $PORT1 coord initialise attach peer1 coord peer1 expect packet open asnum $AS bgpid $ID holdtime $HOLDTIME \ afi 1 safi 1 coord peer1 expect packet keepalive coord peer1 expect packet update \ origin 0 \ nexthop 20.20.20.20 \ aspath empty \ localpref 100 \ nlri 10.10.10.0/24 # Introduce a route originate_route4 10.10.10.0/24 20.20.20.20 true false coord peer1 establish AS $PEER1_AS holdtime 0 id 192.150.187.100 sleep 2 coord peer1 trie recv lookup 10.10.10.0/24 aspath empty coord peer1 assert queue 0 coord peer1 assert established } test56_ipv6() { echo "TEST56 (IPv6) - Routes originated by BGP sent to an I-BGP peer:" echo " 1) Should have a local pref added." echo " 2) Should have an empty aspath." echo " 3) Should honour the next hop?" coord reset coord target $HOST $PORT3 coord initialise attach peer1 coord peer1 expect packet open asnum $AS bgpid $ID holdtime $HOLDTIME \ afi 2 safi 1 coord peer1 expect packet keepalive coord peer1 expect packet update \ origin 0 \ nexthop6 20:20:20:20:20:20:20:20 \ aspath empty \ localpref 100 \ nlri6 2000::/3 # Introduce a route originate_route6 2000::/3 20:20:20:20:20:20:20:20 true false coord peer1 establish AS $PEER3_AS holdtime 0 id 192.150.187.100 ipv6 true sleep 2 coord peer1 trie recv lookup 2000::/3 aspath empty coord peer1 assert queue 0 coord peer1 assert established } test57() { echo "TEST57 - Send an ORIGINATOR_ID on an E-BGP peering." echo " 1) Establish a connection" echo " 2) Should get a Malformed AS_PATH" coord reset coord target $HOST $PORT2 coord initialise attach peer1 coord peer1 establish AS $PEER2_AS holdtime 0 id 192.150.187.100 coord peer1 expect packet notify $UPDATEMSGERR $MALATTRLIST coord peer1 send packet \ update \ origin 2 \ aspath "[$PEER2_AS],[$PEER2_AS]" \ nexthop 20.20.20.20 \ nlri 10.10.10.0/24 \ originatorid 1.2.3.4 sleep 2 coord peer1 assert queue 0 sleep 2 coord peer1 assert idle } test58() { echo "TEST58 - Send CLUSTER_LIST on an E-BGP peering." echo " 1) Establish a connection" echo " 2) Should get a Malformed AS_PATH" coord reset coord target $HOST $PORT2 coord initialise attach peer1 coord peer1 establish AS $PEER2_AS holdtime 0 id 192.150.187.100 coord peer1 expect packet notify $UPDATEMSGERR $MALATTRLIST coord peer1 send packet \ update \ origin 2 \ aspath "[$PEER2_AS],[$PEER2_AS]" \ nexthop 20.20.20.20 \ nlri 10.10.10.0/24 \ clusterlist 1.2.3.4 sleep 2 coord peer1 assert queue 0 sleep 2 coord peer1 assert idle } # These failing tests are handled by test_busted now #TESTS_NOT_FIXED='test26 test28_ipv6_ok test28_ipv6' TESTS='test1 test2 test3 test4 test5 test6 test7 test8 test8_ipv6 test9 test10 test11 test12 test12_ipv6 test13 test14 test15 test16 test17 test18 test19 test20 test20_ipv6 test21 test22 test23 test24 test25 test27 test27_ipv6 test28 test29 test30 test31 test32 test33 test34 test35 test36 test37 test38 test39 test40 test41 test42 test43 test44 test45 test46 test47 test48 test49 test50 test51 test52 test53 test54 test55 test56 test56_ipv6 test57 test58' # Include command line . ${srcdir}/args.sh #START_PROGRAMS='no' if [ $START_PROGRAMS = "yes" ] then CXRL="$CALLXRL -r 10" runit $QUIET $VERBOSE -c "$0 -s -c $*" < #endif #include "libcomm/comm_api.h" #include "libxipc/xrl_std_router.hh" #include "xrl/interfaces/datain_xif.hh" #include "bgp/socket.hh" #include "bgppp.hh" #include "test_peer.hh" static const char SERVER[] = "test_peer";/* This servers name */ static const char SERVER_VERSION[] = "0.1"; #define ZAP(s) zap(s, #s) /* -------------------- XRL -------------------- */ XrlTestPeerTarget::XrlTestPeerTarget(XrlRouter *r, TestPeer& test_peer, bool trace) : XrlTestPeerTargetBase(r), _test_peer(test_peer), _trace(trace) { debug_msg("\n"); } XrlCmdError XrlTestPeerTarget::common_0_1_get_target_name(string& name) { debug_msg("\n"); name = SERVER; return XrlCmdError::OKAY(); } XrlCmdError XrlTestPeerTarget::common_0_1_get_version(string& version) { debug_msg("\n"); version = SERVER_VERSION; return XrlCmdError::OKAY(); } XrlCmdError XrlTestPeerTarget::common_0_1_get_status(// Output values, uint32_t& status, string& reason) { //XXX placeholder only status = PROC_READY; reason = "Ready"; return XrlCmdError::OKAY(); } XrlCmdError XrlTestPeerTarget::common_0_1_shutdown() { string error_string; if(!_test_peer.terminate(error_string)) { return XrlCmdError::COMMAND_FAILED(error_string); } return XrlCmdError::OKAY(); } XrlCmdError XrlTestPeerTarget::test_peer_0_1_register(const string& coordinator, const uint32_t& genid) { debug_msg("\n"); if(_trace) printf("register(%s,%u)\n", coordinator.c_str(), XORP_UINT_CAST(genid)); _test_peer.register_coordinator(coordinator); _test_peer.register_genid(genid); return XrlCmdError::OKAY(); } XrlCmdError XrlTestPeerTarget::test_peer_0_1_packetisation(const string& protocol) { debug_msg("\n"); if(_trace) printf("packetisation(%s)\n", protocol.c_str()); if(!_test_peer.packetisation(protocol)) return XrlCmdError::COMMAND_FAILED(c_format("Unsupported protocol %s", protocol.c_str())); return XrlCmdError::OKAY(); } /* test peer needs to know whether to assume ASPaths are 2-byte or 4-byte encoded */ XrlCmdError XrlTestPeerTarget::test_peer_0_1_use_4byte_asnums(const bool& use) { debug_msg("\n"); if(_trace) printf("use_4byte_asnums(%d)\n", use); _test_peer.use_4byte_asnums(use); return XrlCmdError::OKAY(); } XrlCmdError XrlTestPeerTarget::test_peer_0_1_connect(const string& host, const uint32_t& port) { debug_msg("\n"); if(_trace) printf("connect(%s,%u)\n", host.c_str(), XORP_UINT_CAST(port)); string error_string; if(!_test_peer.connect(host, port, error_string)) { return XrlCmdError::COMMAND_FAILED(error_string); } return XrlCmdError::OKAY(); } XrlCmdError XrlTestPeerTarget::test_peer_0_1_listen(const string& address, const uint32_t& port) { debug_msg("\n"); if(_trace) printf("listen(%s,%u)\n", address.c_str(), XORP_UINT_CAST(port)); string error_string; if(!_test_peer.listen(address, port, error_string)) { return XrlCmdError::COMMAND_FAILED(error_string); } return XrlCmdError::OKAY(); } XrlCmdError XrlTestPeerTarget::test_peer_0_1_bind(const string& address, const uint32_t& port) { debug_msg("\n"); if(_trace) printf("bind(%s,%u)\n", address.c_str(), XORP_UINT_CAST(port)); string error_string; if(!_test_peer.bind(address, port, error_string)) { return XrlCmdError::COMMAND_FAILED(error_string); } return XrlCmdError::OKAY(); } XrlCmdError XrlTestPeerTarget::test_peer_0_1_send(const vector& data) { debug_msg("\n"); if(_trace && MESSAGETYPEOPEN == data[BGPPacket::COMMON_HEADER_LEN - 1]) { printf("send() - open\n"); } string error_string; if(!_test_peer.send(data, error_string)) { return XrlCmdError::COMMAND_FAILED(error_string); } return XrlCmdError::OKAY(); } XrlCmdError XrlTestPeerTarget::test_peer_0_1_disconnect() { debug_msg("\n"); if(_trace) printf("disconnect()\n"); string error_string; if(!_test_peer.disconnect(error_string)) { return XrlCmdError::COMMAND_FAILED(error_string); } return XrlCmdError::OKAY(); } XrlCmdError XrlTestPeerTarget::test_peer_0_1_reset() { debug_msg("\n"); if(_trace) printf("reset()\n"); _test_peer.reset(); return XrlCmdError::OKAY(); } XrlCmdError XrlTestPeerTarget::test_peer_0_1_terminate() { debug_msg("\n"); if(_trace) printf("terminate()\n"); string error_string; if(!_test_peer.terminate(error_string)) { return XrlCmdError::COMMAND_FAILED(error_string); } return XrlCmdError::OKAY(); } /* -------------------- IMPLEMENTATION -------------------- */ TestPeer::TestPeer(EventLoop& eventloop, XrlRouter& xrlrouter, const char *server, bool verbose) : _eventloop(eventloop), _xrlrouter(xrlrouter), _server(server), _verbose(verbose), _done(false), _async_writer(0), _bgp(false), _flying(0), _bgp_bytes(0) { _localdata = new LocalData(eventloop); _localdata->set_as(AsNum(0)); _peerdata = new BGPPeerData(*_localdata, Iptuple(), AsNum(0), IPv4(), 0); // we force IBGP, as this does fewer tests _peerdata->compute_peer_type(); } TestPeer::~TestPeer() { delete _peerdata; delete _localdata; delete _async_writer; } bool TestPeer::done() { return _done; } void TestPeer::register_coordinator(const string& name) { debug_msg("Test Peer: coordinator name = \"%s\"\n", name.c_str()); _coordinator = name; } void TestPeer::register_genid(const uint32_t& genid) { debug_msg("Test Peer: genid = %u\n", XORP_UINT_CAST(genid)); _genid = genid; } bool TestPeer::packetisation(const string& protocol) { debug_msg("\n"); /* ** We only support "bgp". */ if(protocol != "bgp") { return false; } _bgp = true; _bgp_bytes = 0; return true; } void TestPeer::use_4byte_asnums(bool use) { _localdata->set_use_4byte_asnums(use); // normally this is negotiated, but this is a test peer so we want // to be able to force the parsing of ASnums either way. _peerdata->set_use_4byte_asnums(use); } bool TestPeer::connect(const string& host, const uint32_t& port, string& error_string) { debug_msg("\n"); if (_listen.is_valid()) { error_string = "Peer is currently listening"; return false; } if (_bind.is_valid()) { error_string = "Peer is currently bound"; return false; } if (_s.is_valid()) { error_string = "Peer is already connected"; return false; } struct sockaddr_storage peer; size_t len = sizeof(peer); try { Socket::init_sockaddr(host, port, peer, len); } catch(UnresolvableHost e) { error_string = e.why(); return false; } XorpFd s = comm_sock_open(peer.ss_family, SOCK_STREAM, 0, COMM_SOCK_NONBLOCKING); if (!s.is_valid()) { error_string = c_format("comm_sock_open failed: %s\n", comm_get_last_error_str()); return false; } // XXX int in_progress = 0; (void)comm_sock_connect(s, reinterpret_cast(&peer), COMM_SOCK_NONBLOCKING, &in_progress); /* ** If there is a coordinator then add an I/O event callback. */ if (0 != _coordinator.length() && !_eventloop.add_ioevent_cb(s, IOT_READ, callback(this, &TestPeer::receive), XorpTask::PRIORITY_LOWEST)) { comm_sock_close(s); XLOG_WARNING("Failed to add socket %s to eventloop", s.str().c_str()); return false; } /* ** Set up the async writer. */ if (XORP_ERROR == comm_sock_set_blocking(s, COMM_SOCK_NONBLOCKING)) { XLOG_FATAL("Failed to go non-blocking: %s", comm_get_last_error_str()); } delete _async_writer; _async_writer = new AsyncFileWriter(_eventloop, s, XorpTask::PRIORITY_LOWEST); _s = s; return true; } bool TestPeer::listen(const string& host, const uint32_t& port, string& error_string) { debug_msg("\n"); if (_listen.is_valid()) { error_string = "Peer is already listening"; return false; } if (_bind.is_valid()) { error_string = "Peer is currently bound"; return false; } if (_s.is_valid()) { error_string = "Peer is currently connected"; return false; } struct sockaddr_storage local; size_t len = sizeof(local); try { Socket::init_sockaddr(host, port, local, len); } catch(UnresolvableHost e) { error_string = e.why(); return false; } XorpFd s = comm_bind_tcp(reinterpret_cast(&local), COMM_SOCK_NONBLOCKING); if (!s.is_valid()) { error_string = c_format("comm_bind_tcp() failed: %s\n", comm_get_last_error_str()); return false; } if (comm_listen(s, COMM_LISTEN_DEFAULT_BACKLOG) != XORP_OK) { error_string = c_format("comm_listen() failed: %s\n", comm_get_last_error_str()); return false; } if(!_eventloop.add_ioevent_cb(s, IOT_ACCEPT, callback(this, &TestPeer::connect_attempt), XorpTask::PRIORITY_LOWEST)) { comm_sock_close(s); error_string = c_format("Failed to add socket %s to eventloop", s.str().c_str()); return false; } _listen = s; return true; } bool TestPeer::bind(const string& host, const uint32_t& port, string& error_string) { debug_msg("\n"); if (_listen.is_valid()) { error_string = "Peer is currently listening"; return false; } if (_bind.is_valid()) { error_string = "Peer is already bound"; return false; } if (_s.is_valid()) { error_string = "Peer is currently connected"; return false; } struct sockaddr_storage local; size_t len = sizeof(local); try { Socket::init_sockaddr(host, port, local, len); } catch(UnresolvableHost e) { error_string = e.why(); return false; } XorpFd s = comm_sock_open(local.ss_family, SOCK_STREAM, 0, COMM_SOCK_NONBLOCKING); if (!s.is_valid()) { error_string = c_format("comm_sock_open failed: %s", comm_get_last_error_str()); return false; } if (XORP_OK != comm_set_reuseaddr(s, 1)) { error_string = c_format("comm_set_reuseaddr failed: %s\n", comm_get_last_error_str()); return false; } if (comm_sock_bind(s, reinterpret_cast(&local)) != XORP_OK) { comm_sock_close(s); error_string = c_format("comm_sock_bind failed: %s", comm_get_last_error_str()); return false; } _bind = s; return true; } bool TestPeer::send(const vector& data, string& error_string) { debug_msg("len: %u\n", XORP_UINT_CAST(data.size())); if (!_s.is_valid()) { XLOG_WARNING("Not connected"); error_string = "Not connected"; return false; } size_t len = data.size(); uint8_t *buf = new uint8_t[len]; size_t i; for(i = 0; i < len; i++) buf[i] = data[i]; _async_writer->add_buffer(buf, len, callback(this, &TestPeer::send_complete)); _async_writer->start(); if(_verbose && _bgp) printf("Sending: %s", bgppp(buf, len, _peerdata).c_str()); return true; } void TestPeer::send_complete(AsyncFileWriter::Event ev, const uint8_t *buf, const size_t buf_bytes, const size_t offset) { switch (ev) { case AsyncFileOperator::DATA: debug_msg("event: data\n"); if (offset == buf_bytes) delete[] buf; break; case AsyncFileOperator::FLUSHING: debug_msg("event: flushing\n"); debug_msg("Freeing Buffer for sent packet: %p\n", buf); delete[] buf; break; case AsyncFileOperator::OS_ERROR: debug_msg("event: error\n"); /* Don't free the message here we'll get it in the flush */ XLOG_ERROR("Writing buffer failed: %s", strerror(errno)); case AsyncFileOperator::END_OF_FILE: XLOG_ERROR("End of File: %s", strerror(errno)); case AsyncFileOperator::WOULDBLOCK: // do nothing ; } } bool TestPeer::zap(XorpFd& fd, const char *msg) { debug_msg("%s = %s\n", msg, fd.str().c_str()); if (!fd.is_valid()) return true; XorpFd tempfd = fd; fd.clear(); _eventloop.remove_ioevent_cb(tempfd); debug_msg("Removing I/O event cb for fd = %s\n", tempfd.str().c_str()); if (comm_sock_close(tempfd) == -1) { XLOG_WARNING("Close of %s failed: %s", msg, comm_get_last_error_str()); return false; } return true;; } bool TestPeer::disconnect(string& error_string) { debug_msg("\n"); if (!_s.is_valid()) { XLOG_WARNING("Not connected"); error_string = "Not connected"; return false; } delete _async_writer; _async_writer = 0; return ZAP(_s); } void TestPeer::reset() { debug_msg("\n"); delete _async_writer; _async_writer = 0; ZAP(_s); ZAP(_listen); ZAP(_bind); } bool TestPeer::terminate(string& /*error_string*/) { debug_msg("\n"); _done = true; return true; } /* ** Process incoming connection attempts. */ void TestPeer::connect_attempt(XorpFd fd, IoEventType type) { debug_msg("\n"); if (type != IOT_ACCEPT) { XLOG_WARNING("Unexpected IoEventType %d", type); return; } if (_s.is_valid()) { XLOG_WARNING("Connection attempt while already connected"); return; } XLOG_ASSERT(_listen == fd); _listen.clear(); XorpFd connfd = comm_sock_accept(fd); debug_msg("Incoming connection attempt %s\n", connfd.str().c_str()); /* ** We don't want any more connection attempts so remove ourselves ** from the eventloop and close the file descriptor. */ _eventloop.remove_ioevent_cb(fd); debug_msg("Removing I/O event cb for fd = %s\n", fd.str().c_str()); if (XORP_ERROR == comm_sock_close(fd)) XLOG_WARNING("Close failed"); /* ** If there is a coordinator then add an I/O event callback. */ if(0 != _coordinator.length() && !_eventloop.add_ioevent_cb(connfd, IOT_READ, callback(this, &TestPeer::receive), XorpTask::PRIORITY_LOWEST)) { comm_sock_close(connfd); XLOG_WARNING("Failed to add socket %s to eventloop", connfd.str().c_str()); return; } /* ** Set up the async writer. */ if (XORP_ERROR == comm_sock_set_blocking(connfd, COMM_SOCK_NONBLOCKING)) { XLOG_FATAL("Failed to go non-blocking: %s", comm_get_last_error_str()); } delete _async_writer; _async_writer = new AsyncFileWriter(_eventloop, connfd, XorpTask::PRIORITY_LOWEST); _s = connfd; } /* ** Process incoming bytes */ void TestPeer::receive(XorpFd fd, IoEventType type) { debug_msg("\n"); if (type != IOT_READ) { XLOG_WARNING("Unexpected IoEventType %d", type); return; } if (0 == _coordinator.length()) { XLOG_WARNING("No coordinator configured"); return; } /* ** If requested perform BGP packetisation. */ int len; if(!_bgp) { uint8_t buf[64000]; if(-1 == (len = recv(fd, (char *)buf, sizeof(buf), 0))) { string error = c_format("read error: %s", strerror(errno)); XLOG_WARNING("%s", error.c_str()); datain(ERROR, _bgp_buf, _bgp_bytes, error); return; } datain(GOOD, _bgp_buf, _bgp_bytes); return; } /* ** Now doing BGP packetisation. */ int get; if(_bgp_bytes < BGPPacket::COMMON_HEADER_LEN) { get = BGPPacket::COMMON_HEADER_LEN - _bgp_bytes; } else { uint16_t length = extract_16(_bgp_buf + BGPPacket::LENGTH_OFFSET); get = length - _bgp_bytes; } if (-1 == (len = recv(fd, (char *)&_bgp_buf[_bgp_bytes], get, 0))) { string error = c_format("read error: %s", strerror(errno)); XLOG_WARNING("%s", error.c_str()); datain(ERROR, _bgp_buf, _bgp_bytes, error); _bgp_bytes = 0; comm_sock_close(fd); _s.clear(); _eventloop.remove_ioevent_cb(fd); return; } if (0 == len) { if(0 < _bgp_bytes) datain(GOOD, _bgp_buf, _bgp_bytes); datain(CLOSED, 0, 0); _bgp_bytes = 0; comm_sock_close(fd); _s.clear(); _eventloop.remove_ioevent_cb(fd); return; } _bgp_bytes += len; if (_bgp_bytes >= BGPPacket::COMMON_HEADER_LEN) { uint16_t length = extract_16(_bgp_buf + BGPPacket::LENGTH_OFFSET); if (length < BGPPacket::MINPACKETSIZE || length > sizeof(_bgp_buf)) { string error = c_format("Illegal length value %d", length); XLOG_ERROR("%s", error.c_str()); datain(ERROR, _bgp_buf, _bgp_bytes, error); _bgp_bytes = 0; return; } if(length == _bgp_bytes) { datain(GOOD, _bgp_buf, _bgp_bytes); _bgp_bytes = 0; } } } /* ** Send data received on the socket back to the coordinator */ void TestPeer::datain(status st, uint8_t *ptr, size_t len, string error) { debug_msg("status = %d len = %u error = %s\n", st, XORP_UINT_CAST(len), error.c_str()); if (_verbose) { switch(st) { case GOOD: if(_bgp) printf("Received: %s", bgppp(ptr, len, _peerdata).c_str()); break; case CLOSED: printf("Connection closed by peer\n"); break; case ERROR: printf("Error on connection\n"); break; } } queue_data(st, ptr, len, error); } /* ** It is possible to overrun the XRL code by sending too many ** XRL's. Therefore queue the data that needs to be sent and only have ** a small number of outstanding XRL's at any time. */ void TestPeer::queue_data(status st, uint8_t *ptr, size_t len, string error) { Queued q; TimeVal tv; _eventloop.current_time(tv); q.secs = tv.sec(); q.micro = tv.usec(); q.st = st; q.len = len; for(size_t i = 0; i < len; i++) q.v.push_back(ptr[i]); q.error = error; _xrl_queue.push(q); sendit(); } /* ** If there is any queued data and less than the allowed quota is in ** flight send some more. */ void TestPeer::sendit() { if(_flying >= FLYING_LIMIT) return; if(_xrl_queue.empty()) return; Queued q = _xrl_queue.front(); _xrl_queue.pop(); XrlDatainV0p1Client datain(&_xrlrouter); debug_msg("%u\n", XORP_UINT_CAST(q.v.size())); XLOG_ASSERT(q.len == q.v.size()); _flying++; if (q.len == 0) { // // XXX: Note that we don't consider it an error even if // "q.st == ERROR". // This is needed to preserve the original logic that considers // the case of a peer resetting immediately the TCP // connection not an error. // datain.send_closed(_coordinator.c_str(), _server, _genid, callback(this, &TestPeer::xrl_callback)); return; } if (q.st == ERROR) { datain.send_error(_coordinator.c_str(), _server, _genid, q.error, callback(this, &TestPeer::xrl_callback)); return; } datain.send_receive(_coordinator.c_str(), _server, _genid, GOOD == q.st ? true : false, q.secs, q.micro, q.v, callback(this, &TestPeer::xrl_callback)); } void TestPeer::xrl_callback(const XrlError& error) { debug_msg("callback %s\n", error.str().c_str()); if(XrlError::OKAY() != error) { if(XrlError::REPLY_TIMED_OUT() == error) XLOG_ERROR("%s: Try reducing FLYING_LIMIT", error.str().c_str()); XLOG_ERROR("%s", error.str().c_str()); } _flying--; XLOG_ASSERT(_flying >= 0); sendit(); } static void usage(const char *name) { fprintf(stderr, "usage: %s [-h (finder host)] [-s server name] [-v][-t]\n", name); exit(-1); } int main(int argc, char **argv) { XorpUnexpectedHandler x(xorp_unexpected_handler); // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_HIGH); // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); setvbuf(stdout, (char *)NULL, _IOLBF, 0); int c; string finder_host = FinderConstants::FINDER_DEFAULT_HOST().str(); const char *server = SERVER; bool verbose = false; bool trace = false; while((c = getopt (argc, argv, "h:s:vt")) != EOF) { switch(c) { case 'h': finder_host = optarg; break; case 's': server = optarg; break; case 'v': verbose = true; break; case 't': trace = true; break; case '?': usage(argv[0]); } } try { EventLoop eventloop; XrlStdRouter router(eventloop, server, finder_host.c_str()); TestPeer test_peer(eventloop, router, server, verbose); XrlTestPeerTarget xrl_target(&router, test_peer, trace); wait_until_xrl_router_is_ready(eventloop, router); while(!test_peer.done()) { eventloop.run(); } } catch(...) { xorp_catch_standard_exceptions(); } // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); debug_msg("Bye!\n"); return 0; } xorp/bgp/harness/bgppp.hh0000664000076400007640000000217611421137511015526 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/bgp/harness/bgppp.hh,v 1.10 2008/10/02 21:56:26 bms Exp $ #ifndef __BGP_HARNESS_BGPPP_HH__ #define __BGP_HARNESS_BGPPP_HH__ class BGPPeerData; string bgppp(const uint8_t *buf, const size_t len, const BGPPeerData* peerdata); #endif // __BGP_HARNESS_BGPPP_HH__ xorp/bgp/harness/test_peering2.sh0000775000076400007640000006524311421137511017212 0ustar greearbgreearb#!/usr/bin/env bash # # $XORP: xorp/bgp/harness/test_peering2.sh,v 1.64 2008/10/16 02:42:09 atanu Exp $ # # # Feed saved data to our BGP process. # # This script started with no arguments will start all required process and # terminate them at the end of the tests. # # Preconditons # 1) Run a finder process # 2) Run a FEA process. # 3) Run a RIB process. # 4) Run xorp "../xorp_bgp" # 5) Run "./test_peer -s peer1" # 6) Run "./test_peer -s peer2" # 7) Run "./test_peer -s peer3" # 8) Run "./coord" # set -e . ./setup_paths.sh # srcdir is set by make for check target if [ "X${srcdir}" = "X" ] ; then srcdir=`dirname $0` ; fi . ${srcdir}/xrl_shell_funcs.sh "" . $BGP_FUNCS "" . $RIB_FUNCS "" . ${srcdir}/notification_codes.sh onexit() { last=$? if [ $last = "0" ] then echo "$0: Tests Succeeded (BGP: $TESTS)" else echo "$0: Tests Failed (BGP: $TESTS)" fi trap '' 0 2 } trap onexit 0 2 HOST=127.0.0.1 LOCALHOST=$HOST ID=192.150.187.78 AS=65008 USE4BYTEAS=false # IBGP - IPV4 PORT1=10001 PEER1_PORT=20001 PEER1_AS=$AS # EBGP - IPV4 PORT2=10002 PEER2_PORT=20002 PEER2_AS=65000 # EBGP - IPV4 PORT3=10003 PEER3_PORT=20003 PEER3_AS=65003 # IBGP - IPV6 PORT4=10004 PEER4_PORT=20004 PEER4_AS=$AS # EBGP - IPV6 PORT5=10005 PEER5_PORT=20005 PEER5_AS=65000 # EBGP - IPV6 PORT6=10006 PEER6_PORT=20006 PEER6_AS=65003 HOLDTIME=5 TRAFFIC_DIR="${srcdir}/../../../data/bgp" if [ ! -d $TRAFFIC_DIR ] then TRAFFIC_DIR=/usr/local/xorp_data fi TRAFFIC_FILES="${TRAFFIC_DIR}/icsi1.mrtd" # NOTE: The Win32 versions of coord and peer will perform # path conversion and expansion of /tmp internally. TMPDIR=${TMPDIR:-/tmp} EXT=${LOGNAME:-unknown} configure_bgp() { local_config $AS $ID $USE4BYTEAS # Don't try and talk to the rib. register_rib "" # IBGP - IPV4 PEER=$HOST NEXT_HOP=192.150.187.78 add_peer lo $LOCALHOST $PORT1 $PEER $PEER1_PORT $PEER1_AS $NEXT_HOP $HOLDTIME set_parameter $LOCALHOST $PORT1 $PEER $PEER1_PORT MultiProtocol.IPv4.Unicast true enable_peer $LOCALHOST $PORT1 $PEER $PEER1_PORT # EBGP - IPV4 PEER=$HOST NEXT_HOP=192.150.187.78 add_peer lo $LOCALHOST $PORT2 $PEER $PEER2_PORT $PEER2_AS $NEXT_HOP $HOLDTIME set_parameter $LOCALHOST $PORT2 $PEER $PEER2_PORT MultiProtocol.IPv4.Unicast true enable_peer $LOCALHOST $PORT2 $PEER $PEER2_PORT # EBGP - IPV4 PEER=$HOST NEXT_HOP=192.150.187.78 add_peer lo $LOCALHOST $PORT3 $PEER $PEER3_PORT $PEER3_AS $NEXT_HOP $HOLDTIME set_parameter $LOCALHOST $PORT3 $PEER $PEER3_PORT MultiProtocol.IPv4.Unicast true enable_peer $LOCALHOST $PORT3 $PEER $PEER3_PORT # IBGP - IPV6 PEER=$HOST NEXT_HOP=192.150.187.78 add_peer lo $LOCALHOST $PORT4 $PEER $PEER4_PORT $PEER4_AS $NEXT_HOP $HOLDTIME set_parameter $LOCALHOST $PORT4 $PEER $PEER4_PORT MultiProtocol.IPv6.Unicast true enable_peer $LOCALHOST $PORT4 $PEER $PEER4_PORT # EBGP - IPV6 PEER=$HOST NEXT_HOP=192.150.187.78 add_peer lo $LOCALHOST $PORT5 $PEER $PEER5_PORT $PEER5_AS $NEXT_HOP $HOLDTIME set_parameter $LOCALHOST $PORT5 $PEER $PEER5_PORT MultiProtocol.IPv6.Unicast true enable_peer $LOCALHOST $PORT5 $PEER $PEER5_PORT # EBGP - IPV6 PEER=$HOST NEXT_HOP=192.150.187.78 add_peer lo $LOCALHOST $PORT6 $PEER $PEER6_PORT $PEER6_AS $NEXT_HOP $HOLDTIME set_parameter $LOCALHOST $PORT6 $PEER $PEER6_PORT MultiProtocol.IPv6.Unicast true enable_peer $LOCALHOST $PORT6 $PEER $PEER6_PORT } wait_for_peerdown() { # Interact with the BGP process itself to find out when the peerings have # gone down. If we are not testing the XORP bgp then replace with the # sleep. while $PRINT_PEERS show bgp peers detail | grep 'Peer State' | grep ESTABLISHED do sleep 2 done # SLEEPY=30 # echo "sleeping for $SLEEPY seconds" # sleep $SLEEPY } reset() { coord reset coord target $HOST $PORT1 coord initialise attach peer1 coord target $HOST $PORT2 coord initialise attach peer2 coord target $HOST $PORT3 coord initialise attach peer3 bgp_not_established while pending | grep true do sleep 2 done # The test_peer has closed its connection to the BGP process. # If the test_peer was injecting a lot of data to the BGP process only # when all the data has been read and processed by the BGP process will it # see the end of the stream. So add an arbitary delay until the BGP process # sees the end of the stream. If we don't do this connection attempts will # be rejected by the the BGP process, causing the tests to fail. wait_for_peerdown } bgp_not_established() { for i in peer1 peer2 peer3 do status $i while status $i | grep established do sleep 2 done done } bgp_peer_unchanged() { while : do # debug status $1 a=$(status $1) sleep 10 b=$(status $1) if [ "$a" = "$b" ] then break fi done } delay() { # XXX - decrementing the counter to zero generates an error? # So count down to one echo "Sleeping for $1 seconds" let counter=$1+1 while [ $counter != 1 ] do sleep 1 let counter=counter-1 echo -e " $counter \r\c" done return 0 } test1() { TFILE=$1 echo "TEST1 - Inject a saved feed then drop peering - $TFILE" # Reset the peers reset coord peer2 establish AS $PEER2_AS holdtime 0 id 192.150.187.102 coord peer2 assert established NOBLOCK=true coord peer2 send dump mrtd update $TFILE # Wait for the file to be transmitted by the test peer. bgp_peer_unchanged peer2 coord peer2 assert established # Reset the connection reset if [ x"$OSTYPE" != xmsys ]; then uptime echo "NOTE: Occasionally, we fail to make a connection. See the comment in the reset function." fi # Establish the new connection. coord peer2 establish AS $PEER2_AS holdtime 0 id 192.150.187.102 coord peer2 assert established } # This function exists to test saved IPv6 feeds and is not normally run. test1_ipv6() { TFILE=$TFILE echo "TEST1 IPV6 - Inject a saved feed then drop peering - $TFILE" # Reset the peers coord reset coord target $HOST $PORT5 coord initialise attach peer2 coord peer2 establish AS $PEER5_AS holdtime 0 id 192.150.187.102 ipv6 true coord peer2 assert established NOBLOCK=true coord peer2 send dump mrtd update $TFILE # Wait for the file to be transmitted by the test peer. bgp_peer_unchanged peer2 coord peer2 assert established } test2() { TFILE=$1 echo "TEST2 - Inject a saved feed then toggle another peering - $TFILE" # Reset the peers reset # Establish the EBGP peering. coord peer2 establish AS $PEER2_AS holdtime 0 id 192.150.187.102 coord peer2 assert established # send in the saved file NOBLOCK=true coord peer2 send dump mrtd update $TFILE # Wait for the file to be transmitted by the test peer. bgp_peer_unchanged peer2 # Bring up peer1 and wait for it to receive all the updates coord peer1 establish AS $PEER1_AS holdtime 0 id 192.150.187.101 bgp_peer_unchanged peer1 # Bring up another peering to test the dump code. for i in 1 2 do coord peer1 disconnect # Wait for the peering to be dropped while status peer1 | grep established do sleep 2 done sleep 2 coord peer1 establish AS $PEER1_AS holdtime 0 id 192.150.187.101 sleep 2 coord peer1 assert established # Wait for the BGP process to send all the updates to peer1 bgp_peer_unchanged peer1 done coord peer2 disconnect while status peer2 | grep established do sleep 2 done sleep 2 coord peer2 establish AS $PEER2_AS holdtime 0 id 192.150.187.102 coord peer2 assert established # Tearing out peer2 will cause all the routes to be withdrawn, wait bgp_peer_unchanged peer1 # Make sure that the connections are still established. coord peer1 assert established coord peer2 assert established } test3() { TFILE=$1 echo "TEST3:" echo " 1) Start injecting a saved feed (peer2) - $TFILE" echo " 2) Bring in a second peering (peer1) " echo " 3) Drop the injecting feed (peer2) " # Reset the peers reset # Establish the EBGP peering. coord peer2 establish AS $PEER2_AS holdtime 0 id 192.150.187.102 coord peer2 assert established # send in the saved file NOBLOCK=true coord peer2 send dump mrtd update $TFILE # Bring up another peering NOBLOCK=true coord peer1 establish AS $PEER1_AS holdtime 0 id 192.150.187.101 # Allow some routes to be loaded sleep 5 # Drop the injecting feed NOBLOCK=true coord peer2 disconnect # Wait for the BGP to stabilise bgp_peer_unchanged peer1 # Add a delay so if the BGP process core dumps we detect it. sleep 5 # Make sure that the peer1 connection is still established coord peer1 assert established } test4() { TFILE=$1 echo "TEST4:" echo " 1) Start injecting a saved feed (peer2) - $TFILE" echo " 2) Immediately bring up a second peering (peer1) " # Reset the peers reset result=$(status peer1) echo "$result" # Establish the EBGP peering. coord peer2 establish AS $PEER2_AS holdtime 0 id 192.150.187.102 coord peer2 assert established # send in the saved file NOBLOCK=true coord peer2 send dump mrtd update $TFILE # Bring up a second peering and wait for all the updates to arrive coord peer1 establish AS $PEER1_AS holdtime 0 id 192.150.187.101 bgp_peer_unchanged peer1 # Make sure that the peer1 connection is still established coord peer1 assert established coord peer2 assert established } test5() { TFILE=$1 echo "TEST5:" echo " 1) Start injecting a saved feed (peer2) - $TFILE" echo " 2) Immediately bring up a second peering (peer1) " echo " 3) Wait for all the updates to arrive at (peer1) " echo " 4) Drop both peerings " echo " 5) Bring up (peer1) " echo " 6) Peer1 should not receive any update traffic " # Reset the peers reset result=$(status peer1) echo "$result" # Establish the EBGP peering. coord peer2 establish AS $PEER2_AS holdtime 0 id 192.150.187.102 coord peer2 assert established # send in the saved file NOBLOCK=true coord peer2 send dump mrtd update $TFILE # Bring up a second peering and wait for all the updates to arrive coord peer1 establish AS $PEER1_AS holdtime 0 id 192.150.187.101 bgp_peer_unchanged peer1 coord reset # Debugging to demonstrate that the BGP process believes that both peerings # have been taken down. $PRINT_PEERS show bgp peers detail || exit 10 # debug status peer1 status peer2 # Establish peer1 coord target $HOST $PORT1 coord initialise attach peer1 coord peer1 establish AS $PEER1_AS holdtime 0 id 192.150.187.101 # If peer1 receives any updates this is an error a=$(status peer1) sleep 2 b=$(status peer1) if [ "$a" = "$b" ] then : else echo "Peer1 received updates, but this is the only peering?" echo $a echo $b return -1 fi } test6() { TFILE=$1 UPDATE_COUNT=10 echo "TEST6 (testing sending only $UPDATE_COUNT updates):" echo " 1) Start injecting a saved feed (peer2) - $TFILE" echo " 2) Immediately bring up a second peering (peer1) " echo " 3) Wait for all the updates to arrive at (peer1) " echo " 4) Verify that both peering still exist." # Reset the peers reset status peer1 status peer1 # Establish the EBGP peering. coord peer2 establish AS $PEER2_AS holdtime 0 id 192.150.187.102 coord peer2 assert established # send in the saved file NOBLOCK=true coord peer2 send dump mrtd update $TFILE $UPDATE_COUNT # Bring up a second peering and wait for all the updates to arrive coord peer1 establish AS $PEER1_AS holdtime 0 id 192.150.187.101 bgp_peer_unchanged peer1 status peer1 status peer2 coord peer2 assert established coord peer2 assert established } test7() { echo "TEST7 (Simple route propogation test - searching for memory leak):" echo " 1) Bring up peering (peer2) and introduce one route" echo " 2) Bring up a second peering (peer1) check route" echo " 3) Tear down both peerings" reset # Establish an EBGP peering. coord peer2 establish AS $PEER2_AS holdtime 0 id 192.150.187.102 coord peer2 assert established # Inject a route coord peer2 send packet update \ origin 2 \ aspath "$PEER2_AS" \ nexthop 10.10.10.10 \ nlri 10.10.10.0/24 sleep 1 # Bring up a second peering coord peer1 establish AS $PEER1_AS holdtime 0 id 192.150.187.101 sleep 1 coord peer1 assert established coord peer2 assert established reset } test8() { echo "TEST8 (Simple route propogation test - searching for memory leak):" echo " 1) Bring up peering (peer1)" echo " 2) Bring up a second peering (peer2) and introduce a route" echo " 3) Tear down both peerings" reset coord peer1 establish AS $PEER1_AS holdtime 0 id 192.150.187.101 coord peer1 assert established coord peer2 establish AS $PEER2_AS holdtime 0 id 192.150.187.102 coord peer2 assert established # Inject a route coord peer2 send packet update \ origin 2 \ aspath "$PEER2_AS" \ nexthop 10.10.10.10 \ nlri 10.10.10.0/24 sleep 1 coord peer1 assert established coord peer2 assert established reset } test9() { echo "TEST9 (Simple route propogation test - searching for memory leak):" echo " 1) Bring up peering (peer1)" echo " 2) Bring up a second peering (peer2) and introduce a route" echo " 3) Tear down both peerings" reset coord peer2 establish AS $PEER2_AS holdtime 0 id 192.150.187.102 coord peer2 assert established coord peer1 establish AS $PEER1_AS holdtime 0 id 192.150.187.101 coord peer1 assert established # Inject a route coord peer2 send packet update \ origin 2 \ aspath "$PEER2_AS" \ nexthop 10.10.10.10 \ nlri 10.10.10.0/24 sleep 1 coord peer1 assert established coord peer2 assert established reset } test10() { echo "TEST10 (Verify that a router ID of 0.0.0.0 doesn't cause a problem):" echo " 1) Bring up peering (peer1)" reset coord peer2 connect sleep 2 coord peer2 expect packet notify $OPENMSGERROR $BADBGPIDENT coord peer2 send packet \ open \ asnum $PEER2_AS \ bgpid 0.0.0.0 \ holdtime 0 sleep 2 coord peer2 assert queue 0 reset } test11() { echo "TEST11 (Behave sanely when two peers use the same router ID):" echo " 1) Bring up peering (peer1)" echo " 2) Bring up a second peering (peer2)" echo " 3) Tear down both peerings" reset coord peer2 establish AS $PEER2_AS holdtime 0 id 1.1.1.1 coord peer2 assert established coord peer1 establish AS $PEER1_AS holdtime 0 id 1.1.1.1 coord peer1 assert established sleep 1 coord peer1 assert established coord peer2 assert established reset } test12() { echo "TEST12 (Behave sanely when a peer reuses a router ID):" echo " 1) Bring up peering (peer1)" echo " 2) Tear down the peering (peer1)" echo " 2) Bring up a second peering (peer2) with the same router ID" echo " 3) Tear down peering (peer2)" reset coord peer2 establish AS $PEER2_AS holdtime 0 id 1.1.1.1 coord peer2 assert established # Reset the connection and reuse the ID on the other peer,should be legal. reset coord peer1 establish AS $PEER1_AS holdtime 0 id 1.1.1.1 coord peer1 assert established sleep 1 coord peer1 assert established reset } test13() { TFILE=$1 echo "TEST13" echo " 1) Inject a saved peering - $TFILE" echo " 2) Change next-hop resolution" echo " 3) Drop peering" echo " 4) Used to cause BGP to fail" # Reset the peers reset VIF0="vif0" IF1=172.16.1.1 GW1=172.16.1.2 NH1=192.150.187.2 register_rib "rib" new_vif $VIF0 add_vif_addr4 $VIF0 $IF1 $IF1/24 coord peer2 establish AS $PEER2_AS holdtime 0 id 192.150.187.102 coord peer2 assert established sleep 2 NOBLOCK=true coord peer2 send dump mrtd update $TFILE bgp_peer_unchanged peer2 coord peer2 assert established add_igp_table4 is-is isis isis true false add_route4 is-is true false $NH1/24 $GW1 10 # Reset the connection reset # Establish the new connection. coord peer2 establish AS $PEER2_AS holdtime 0 id 192.150.187.102 coord peer2 assert established } test14() { echo "TEST14 (Simple route propogation test with RIB resolving next hop):" echo " 1) Bring up peering (peer1)" echo " 2) Bring up a second peering (peer2)" echo " 3) Introduce a route on (peer1) the IBGP peering" echo " 4) Verify that the route appears on (peer2) the EBGP peering" echo " 5) Tear down both peerings" reset VIF0="vif0" IF1=172.16.1.1 GW1=172.16.1.2 NH1=192.150.187.2 register_rib "rib" new_vif $VIF0 add_vif_addr4 $VIF0 $IF1 $IF1/24 IGP_METRIC=10 add_igp_table4 is-is isis isis true false add_route4 is-is true false $NH1/24 $GW1 $IGP_METRIC coord peer1 establish AS $PEER1_AS holdtime 0 id 192.150.187.101 coord peer1 assert established coord peer2 establish AS $PEER2_AS holdtime 0 id 192.150.187.102 coord peer2 assert established coord peer2 expect packet update \ origin 2 \ aspath 65008,1000 \ med $IGP_METRIC \ nexthop $NEXT_HOP \ nlri 10.10.10.0/24 # Inject a route coord peer1 send packet update \ origin 2 \ aspath 1000 \ localpref 100 \ nexthop $NH1 \ nlri 10.10.10.0/24 sleep 2 coord peer2 assert queue 0 coord peer1 assert established coord peer2 assert established reset } test15() { TFILE=$1 echo "TEST15:" echo " 1) Start injecting a saved feed (peer1 I-BGP) - $TFILE" echo " 2) Wait until the whole feed has been sent" echo " 3) Bring up a second peering (peer3 E-BGP)" echo " 4) Verify all sent packets arrived" echo " 5) Verify all received packets were sent" # Reset the peers reset # Establish the EBGP peering. coord peer1 establish AS $PEER1_AS holdtime 0 id 192.150.187.101 sleep 2 coord peer1 assert established # Save the traffic that we send on peer1 SENT=${TMPDIR}/peer1_sent.text.$EXT rm -f $SENT coord peer1 dump sent text ipv4 traffic $SENT # send in the saved file NOBLOCK=true coord peer1 send dump mrtd update $TFILE # Wait for the whole feed to be sent it may not all have arrived at BGP bgp_peer_unchanged peer1 # XXXX # Put a big delay here if you want all the packets to have been processed # by BGP. # Save the traffic that we receive on peer3 useful for debugging RECV=${TMPDIR}/peer3_recv.text.$EXT rm -f $RECV coord peer3 dump recv text ipv4 traffic $RECV # Bring up a second peering and wait for all the updates to arrive coord peer3 establish AS $PEER3_AS holdtime 0 id 192.150.187.103 # Wait for the whole feed to be received bgp_peer_unchanged peer3 # Check all the sent packets and verify that they arrived at peer3 XRLS=${TMPDIR}/xrls.$EXT echo "Building lookup list in $XRLS" cat $SENT > sentdata cat $SENT | $PYTHON $srcdir/lookup.py --peer peer3 \ --trie recv \ --add 65008 > $XRLS echo "Performing lookup" $CALLXRL -f $XRLS # Check all the received packets and verify that they were sent by peer1 XRLS=${TMPDIR}/xrls.$EXT echo "Building lookup list in $XRLS" cat $RECV > recvdata cat $RECV | $PYTHON $srcdir/lookup.py --peer peer1 \ --trie sent \ --remove 65008 > $XRLS echo "Performing lookup" $CALLXRL -f $XRLS # Make sure that the peer1 connection is still established coord peer1 assert established coord peer3 assert established rm -f $SENT $RECV $XRLS } test16() { TFILE=$1 echo "TEST16:" echo " 1) Start injecting a saved feed (peer2 E-BGP) - $TFILE" echo " 2) Wait until the whole feed has been sent" echo " 3) Bring up a second peering (peer1 I-BGP) " echo " 4) Verify all sent packets arrived" echo " 5) Verify all received packets were sent" # Reset the peers reset # Establish the EBGP peering. coord peer2 establish AS $PEER2_AS holdtime 0 id 192.150.187.102 coord peer2 assert established # Save the traffic that we send on peer2 SENT=${TMPDIR}/peer2_sent.text.$EXT rm -f $SENT coord peer2 dump sent text ipv4 traffic $SENT # send in the saved file NOBLOCK=true coord peer2 send dump mrtd update $TFILE # Wait for the whole feed to be sent it may not all have arrived at BGP bgp_peer_unchanged peer2 # XXXX # Put a big delay here if you want all the packets to have been processed # by BGP. # Save the traffic that we receive on peer1 useful for debugging RECV=${TMPDIR}/peer1_recv.text.$EXT rm -f $RECV coord peer1 dump recv text ipv4 traffic $RECV # Bring up a second peering and wait for all the updates to arrive coord peer1 establish AS $PEER1_AS holdtime 0 id 192.150.187.101 # Wait for the whole feed to arrive bgp_peer_unchanged peer1 # Check all the sent packets and verify that they arrived at peer1 XRLS=${TMPDIR}/xrls.$EXT echo "Building lookup list in $XRLS" cat $SENT | $PYTHON $srcdir/lookup.py --peer peer1 \ --trie recv > $XRLS echo "Performing lookup" $CALLXRL -f $XRLS # Check all the received packets and verify that they were sent by peer2 XRLS=${TMPDIR}/xrls.$EXT echo "Building lookup list in $XRLS" cat $RECV | $PYTHON $srcdir/lookup.py --peer peer2 \ --trie sent \ --remove 65008 > $XRLS echo "Performing lookup" $CALLXRL -f $XRLS # Make sure that the peer1 connection is still established coord peer1 assert established coord peer2 assert established rm -f $SENT $RECV $XRLS } test17() { echo "TEST17:" echo " 1) Run test15 thrice" test15 $* test15 $* test15 $* } test_work_in_progress() { TFILE=$1 echo "TEST:" echo " 1) Start injecting a saved feed (peer1 I-BGP) - $TFILE" echo " 2) Wait until the whole feed has been sent" echo " 3) Bring up a second peering (peer3 E-BGP)" echo " 4) Verify all sent packets arrived" echo " 5) Verify all received packets were sent" # Reset the peers reset # Establish the EBGP peering. coord peer1 establish AS $PEER1_AS holdtime 0 id 192.150.187.101 coord peer1 assert established # Save the traffic that we send on peer1 SENT=${TMPDIR}/peer1_sent.text.$EXT rm -f $SENT coord peer1 dump sent text ipv4 traffic $SENT # send in the saved file UPDATES= NOBLOCK=true coord peer1 send dump mrtd update $TFILE $UPDATES # Wait for the whole feed to be sent it may not all have arrived at BGP bgp_peer_unchanged peer1 # XXXX # Put a big delay here if you want all the packets to have been processed # by BGP. for i in 1 2 do # Drop receive peering the first time round we are not connected coord peer3 disconnect # Save the traffic that we receive on peer3 useful for debugging RECV=${TMPDIR}/peer3_recv.text.$EXT rm -f $RECV coord peer3 dump recv text ipv4 traffic $RECV # Bring up a second peering and wait for all the updates to arrive coord peer3 establish AS $PEER3_AS holdtime 0 id 192.150.187.103 # Wait for the whole feed to be received bgp_peer_unchanged peer3 # Check all the sent packets and verify that they arrived at peer3 XRLS=${TMPDIR}/xrls.$EXT cat $SENT | $PYTHON $srcdir/lookup.py --peer peer3 \ --trie recv \ --add 65008 > $XRLS $CALLXRL -f $XRLS # Check all the received packets and verify that they were sent by peer1 XRLS=${TMPDIR}/xrls.$EXT cat $RECV | $PYTHON $srcdir/lookup.py --peer peer1 \ --trie sent \ --remove 65008 > $XRLS $CALLXRL -f $XRLS # Make sure that the peer1 connection is still established coord peer1 assert established coord peer3 assert established done # Stop saving received packets coord peer3 dump recv text ipv4 traffic for i in 1 2 3 4 5 6 7 8 9 10 do # drop feed peering coord peer1 disconnect # Establish the EBGP peering. coord peer1 establish AS $PEER1_AS holdtime 0 id 192.150.187.101 coord peer1 assert established # Save the traffic that we send on peer1 SENT=${TMPDIR}/peer1_sent.text.$EXT rm -f $SENT coord peer1 dump sent text ipv4 traffic $SENT # send in the saved file UPDATES= NOBLOCK=true coord peer1 send dump mrtd update $TFILE $UPDATES # Wait for the whole feed to be sent it may not all have arrived at BGP bgp_peer_unchanged peer1 # Wait for the whole feed to be received bgp_peer_unchanged peer3 # Check all the sent packets and verify that they arrived at peer3 XRLS=${TMPDIR}/xrls.$EXT cat $SENT | $PYTHON $srcdir/lookup.py --peer peer3 \ --trie recv \ --add 65008 > $XRLS $CALLXRL -f $XRLS # Make sure that the peer1 connection is still established coord peer1 assert established coord peer3 assert established done rm -f $SENT $RECV $XRLS } TESTS_NOT_FIXED='' TESTS='test1 test2 test3 test4 test5 test6 test7 test8 test9 test10 test11 test12 test13 test14 test15 test16 test17' # Sanity check that python is present on the host system. PYTHON=${PYTHON:=python} python_version=$(${PYTHON} -V 2>&1) if [ "X${python_version}" = "X" ]; then exit 2 else export PYTHON fi # Include command line . ${srcdir}/args.sh if [ $START_PROGRAMS = "yes" ] then CXRL="$CALLXRL -r 10" runit $QUIET $VERBOSE -c "$0 -s -c $*" < /dev/null then return fi echo "Operation in coordinator still pending try number: $i" sleep 1 done echo "***OPERATION DID NOT COMPLETE. TRY INCREASING THE TIME IN PENDING***" return -1 } status() { # echo "Status $* " $CALLXRL "finder://coord/coord/0.1/status?peer:txt=$1" | sed -e 's/^status:txt=//' -e 's/+/ /g' } pending() { echo "Pending " $CALLXRL "finder://coord/coord/0.1/pending" } # # Commands to the test peers # register() { echo "Register $* " $CALLXRL "finder://$BASE/test_peer/0.1/register?coordinator:txt=$1" } packetisation() { echo "Packetisation $* " $CALLXRL "finder://$BASE/test_peer/0.1/packetisation?protocol:txt=$1" } use_4byte_asnums() { echo "Packetisation $* " $CALLXRL "finder://$BASE/test_peer/0.1/use_4byte_asnums?use:bool=$1" } connect() { echo "Connect $* " $CALLXRL "finder://$BASE/test_peer/0.1/connect?host:txt=$1&port:u32=$2" } send() { echo "Send $* " $CALLXRL "finder://$BASE/test_peer/0.1/send?data:txt=$*" } listen() { echo "Listen $* " $CALLXRL "finder://$BASE/test_peer/0.1/listen?address:txt=$1&port:u32=$2" } bind() { echo "Bind $* " $CALLXRL "finder://$BASE/test_peer/0.1/bind?address:txt=$1&port:u32=$2" } disconnect() { echo "Disconnect $* " $CALLXRL "finder://$BASE/test_peer/0.1/disconnect" } reset() { echo "Reset $* " $CALLXRL "finder://$BASE/test_peer/0.1/reset" } terminate() { echo "Terminate " $CALLXRL "finder://$BASE/test_peer/0.1/terminate" } # We have arguments. if [ $# != 0 ] then $* fi # Local Variables: # mode: shell-script # sh-indentation: 4 # End: xorp/bgp/harness/test_route_reflection1.sh0000775000076400007640000002106211421137511021117 0ustar greearbgreearb#!/usr/bin/env bash # # Test basic BGP routing reflection. # # This script started with no arguments will start all required process and # terminate them at the end of the tests. # # Preconditons # 1) Run a finder process # 2) Run "../fea/xorp_fea_dummy" # 3) Run xorp "../rib/xorp_rib" # 4) Run xorp "../xorp_bgp" # 5) Run "./test_peer -s peer1" # 6) Run "./test_peer -s peer2" # 7) Run "./test_peer -s peer3" # 8) Run "./coord" # # The BGP process is configured as a route reflector. # Peers 1,2 and 3 are all configured as I-BGP peers. # Peers 2 and 3 are RR clients set -e . ./setup_paths.sh onexit() { last=$? if [ $last = "0" ] then echo "$0: Tests Succeeded (BGP: $TESTS)" else echo "$0: Tests Failed (BGP: $TESTS)" fi trap '' 0 2 } trap onexit 0 2 if [ "X${srcdir}" = "X" ] ; then srcdir=`dirname $0` ; fi . ${srcdir}/xrl_shell_funcs.sh "" . $BGP_FUNCS "" . $RIB_FUNCS "" HOST=127.0.0.1 PORT1=10001 PORT2=10002 PORT3=10003 PORT1_IPV6=10004 PORT2_IPV6=10005 PORT3_IPV6=10006 PEER_PORT1=20001 PEER_PORT2=20002 PEER_PORT3=20003 PEER_PORT1_IPV6=20004 PEER_PORT2_IPV6=20005 PEER_PORT3_IPV6=20006 AS=65008 USE4BYTEAS=false PEER1_AS=$AS PEER2_AS=$AS PEER3_AS=$AS PEER1_AS_IPV6=$AS PEER2_AS_IPV6=$AS PEER3_AS_IPV6=$AS HOLDTIME=0 # Next Hops NH1_IPv4=172.16.1.2 NH2_IPV4=172.16.2.2 NH3_IPv4=172.16.3.2 NH1_IPV6=40:40:40:40:40:40:40:42 NH2_IPV6=50:50:50:50:50:50:50:52 NH3_IPV6=60:60:60:60:60:60:60:62 NEXT_HOP=192.150.187.78 ID=192.150.187.78 CLUSTER_ID=0.0.0.11 configure_bgp() { LOCALHOST=$HOST AS=65008 local_config $AS $ID $USE4BYTEAS route_reflector $CLUSTER_ID false # Don't try and talk to the rib. register_rib "" # IBGP - IPV4 PEER=$HOST PORT=$PORT1;PEER_PORT=$PEER_PORT1;PEER_AS=$PEER1_AS IPTUPLE="$LOCALHOST $PORT $PEER $PEER_PORT" add_peer lo $IPTUPLE $PEER_AS $NEXT_HOP $HOLDTIME set_parameter $IPTUPLE MultiProtocol.IPv4.Unicast true enable_peer $IPTUPLE # IBGP - CLIENT - IPV4 PEER=$HOST PORT=$PORT2;PEER_PORT=$PEER_PORT2;PEER_AS=$PEER2_AS IPTUPLE="$LOCALHOST $PORT $PEER $PEER_PORT" add_peer lo $IPTUPLE $PEER_AS $NEXT_HOP $HOLDTIME set_parameter $IPTUPLE MultiProtocol.IPv4.Unicast true route_reflector_client $IPTUPLE true enable_peer $IPTUPLE # IBGP - CLIENT - IPV4 PEER=$HOST PORT=$PORT3;PEER_PORT=$PEER_PORT3;PEER_AS=$PEER3_AS IPTUPLE="$LOCALHOST $PORT $PEER $PEER_PORT" add_peer lo $IPTUPLE $PEER_AS $NEXT_HOP $HOLDTIME set_parameter $IPTUPLE MultiProtocol.IPv4.Unicast true route_reflector_client $IPTUPLE true enable_peer $IPTUPLE # IBGP - IPV6 PEER=$HOST PORT=$PORT1_IPV6;PEER_PORT=$PEER_PORT1_IPV6;PEER_AS=$PEER1_AS_IPV6 IPTUPLE="$LOCALHOST $PORT $PEER $PEER_PORT" add_peer lo $IPTUPLE $PEER_AS $NEXT_HOP $HOLDTIME set_parameter $IPTUPLE MultiProtocol.IPv6.Unicast true enable_peer $IPTUPLE # IBGP - CLIENT - IPV6 PEER=$HOST PORT=$PORT2_IPV6;PEER_PORT=$PEER_PORT2_IPV6;PEER_AS=$PEER2_AS_IPV6 IPTUPLE="$LOCALHOST $PORT $PEER $PEER_PORT" add_peer lo $IPTUPLE $PEER_AS $NEXT_HOP $HOLDTIME set_parameter $IPTUPLE MultiProtocol.IPv6.Unicast true route_reflector_client $IPTUPLE true enable_peer $IPTUPLE # IBGP - CLIENT - IPV6 PEER=$HOST PORT=$PORT3_IPV6;PEER_PORT=$PEER_PORT3_IPV6;PEER_AS=$PEER3_AS_IPV6 IPTUPLE="$LOCALHOST $PORT $PEER $PEER_PORT" add_peer lo $IPTUPLE $PEER_AS $NEXT_HOP $HOLDTIME set_parameter $IPTUPLE MultiProtocol.IPv6.Unicast true route_reflector_client $IPTUPLE true enable_peer $IPTUPLE } config_peers_ipv4() { coord reset coord target $HOST $PORT1 coord initialise attach peer1 coord peer1 establish AS $PEER1_AS \ holdtime $HOLDTIME \ id 10.10.10.1 \ keepalive false coord peer1 assert established coord target $HOST $PORT2 coord initialise attach peer2 coord peer2 establish AS $PEER2_AS \ holdtime $HOLDTIME \ id 10.10.10.2 \ keepalive false coord peer2 assert established coord target $HOST $PORT3 coord initialise attach peer3 coord peer3 establish AS $PEER3_AS \ holdtime $HOLDTIME \ id 10.10.10.3 \ keepalive false coord peer3 assert established } test1() { echo "TEST1 - Establish three peerings" config_peers_ipv4 } test2() { echo "TEST2 (UNH - BGP_CONF.4.4 PART A)" echo " 1) Send an update packet on peer1" echo " 2) Verify that the packet arrives at peer2 and peer3" config_peers_ipv4 PACKET="packet update origin 2 aspath 1 nexthop $NEXT_HOP nlri 10.10.10.0/24 localpref 10 clusterlist 1.2.3.4" RR_PACKET="$PACKET originatorid 10.10.10.1 clusterlist $CLUSTER_ID" coord peer1 expect $PACKET coord peer2 expect $RR_PACKET coord peer3 expect $RR_PACKET coord peer1 send $PACKET sleep 2 coord peer1 assert queue 1 coord peer2 assert queue 0 coord peer3 assert queue 0 # At the end of the test we expect all the peerings to still be established. coord peer1 assert established coord peer2 assert established coord peer3 assert established } test3() { echo "TEST3 (UNH - BGP_CONF.4.4 PART B)" echo " 1) Send an update packet on peer2" echo " 2) Verify that the packet arrives at peer1 and peer3" config_peers_ipv4 PACKET="packet update origin 2 aspath 1 nexthop $NEXT_HOP nlri 10.10.10.0/24 localpref 10 clusterlist 1.2.3.4" RR_PACKET="$PACKET originatorid 10.10.10.2 clusterlist $CLUSTER_ID" coord peer1 expect $RR_PACKET coord peer2 expect $RR_PACKET coord peer3 expect $RR_PACKET coord peer2 send $PACKET sleep 2 coord peer1 assert queue 0 coord peer2 assert queue 1 coord peer3 assert queue 0 # At the end of the test we expect all the peerings to still be established. coord peer1 assert established coord peer2 assert established coord peer3 assert established } test4() { echo "TEST4" echo " 1) Send an update packet with the routers originator id" echo " 2) This packet should be supressed by the router" config_peers_ipv4 BASE_PACKET="packet update origin 2 aspath 1 nexthop $NEXT_HOP nlri 10.10.10.0/24 localpref 10" PACKET="$BASE_PACKET originatorid $ID clusterlist 1.2.3.4" RR_PACKET="$BASE_PACKET originatorid $ID clusterlist $CLUSTER_ID" coord peer1 expect $RR_PACKET coord peer2 expect $RR_PACKET coord peer3 expect $RR_PACKET coord peer1 send $PACKET sleep 2 coord peer1 assert queue 1 coord peer2 assert queue 1 coord peer3 assert queue 1 # At the end of the test we expect all the peerings to still be established. coord peer1 assert established coord peer2 assert established coord peer3 assert established } test5() { echo "TEST5" echo " 1) Send an update packet with the same cluster id" echo " 2) This packet should be supressed by the router" config_peers_ipv4 PACKET="packet update origin 2 aspath 1 nexthop $NEXT_HOP nlri 10.10.10.0/24 localpref 10 clusterlist $CLUSTER_ID" RR_PACKET="$PACKET originatorid $ID" coord peer1 expect $RR_PACKET coord peer2 expect $RR_PACKET coord peer3 expect $RR_PACKET coord peer1 send $PACKET sleep 2 coord peer1 assert queue 1 coord peer2 assert queue 1 coord peer3 assert queue 1 # At the end of the test we expect all the peerings to still be established. coord peer1 assert established coord peer2 assert established coord peer3 assert established } TESTS_NOT_FIXED='' TESTS='test1 test2 test3 test4 test5' # Include command line . ${srcdir}/args.sh #START_PROGRAMS="no" if [ $START_PROGRAMS = "yes" ] then CXRL="$CALLXRL -r 10" runit $QUIET $VERBOSE -c "$0 -s -c $*" <> 24) & 0xff, ($v >> 16) & 0xff, ($v >> 8) & 0xff, $v & 0xff); } sub handleCmdLineArg { my $arg = $_[0]; my $val = $_[1]; if ($arg eq "ip") { ($start_ip_a, $start_ip_b, $start_ip_c, $start_ip_d) = split(/\./, $val); } elsif ($arg eq "nh") { ($nh_ip_a, $nh_ip_b, $nh_ip_c, $nh_ip_d) = split(/\./, $val); } elsif ($arg eq "sigb") { $sigb = $val; } elsif ($arg eq "ucast") { $ucast = $val; } elsif ($arg eq "mcast") { $mcast = $val; } elsif ($arg eq "amt") { $amt = $val; } # Add support for more cmd line args here. else { print "Cmd-Line arg -:$arg:- not supported!\n$usage"; exit 1; } } xorp/bgp/harness/test_rib_fea1.sh0000775000076400007640000004077611421137511017153 0ustar greearbgreearb#!/usr/bin/env bash # # $XORP: xorp/bgp/harness/test_rib_fea1.sh,v 1.18 2007/12/10 23:26:33 mjh Exp $ # # # Test BGP, RIB and FEA interaction. # # This script started with no arguments will start all required process and # terminate them at the end of the tests. # # Preconditons # 1) Run a finder process # 2) Run a FEA process. # 3) Run a RIB process. # 4) Run xorp "../xorp_bgp" # 5) Run "./test_peer -s peer1" # 6) Run "./test_peer -s peer2" # 7) Run "./test_peer -s peer3" # 8) Run "./coord" # set -e . ./setup_paths.sh onexit() { last=$? if [ $last = "0" ] then echo "$0: Tests Succeeded (BGP/FEA: $TESTS)" else echo "$0: Tests Failed (BGP/FEA: $TESTS)" fi trap '' 0 2 } trap onexit 0 2 # srcdir is set by make for check target if [ "X${srcdir}" = "X" ] ; then srcdir=`dirname $0` ; fi . ${srcdir}/xrl_shell_funcs.sh "" . $BGP_FUNCS "" . $RIB_FUNCS "" HOST=127.0.0.1 AS=65008 USE4BYTEAS=false # EBGP - IPV4 PORT1=10001 PEER_PORT1=20001 PEER1_AS=64001 # EBGP - IPV4 PORT2=10002 PEER_PORT2=20002 PEER2_AS=64002 # IBGP - IPV4 PORT3=10003 PEER_PORT3=20003 PEER3_AS=$AS # EBGP - IPV6 PORT1_IPV6=10004 PEER_PORT1_IPV6=20004 PEER1_AS_IPV6=64004 # EBGP - IPV6 PORT2_IPV6=10005 PEER_PORT2_IPV6=20005 PEER2_AS_IPV6=64005 # IBGP - IPV6 PORT3_IPV6=10006 PEER_PORT3_IPV6=20006 PEER3_AS_IPV6=$AS HOLDTIME=0 # Next Hops #NH1=10.10.10.10 #NH2=20.20.20.20 NH1=172.16.1.1 NH2=172.16.2.1 NH1_IPV6=40:40:40:40:40:40:40:40 NH2_IPV6=50:50:50:50:50:50:50:50 VIF0="vif0" VIF1="vif1" VIF0_IPV6="vif2" VIF1_IPV6="vif3" configure_bgp() { LOCALHOST=$HOST ID=192.150.187.78 local_config $AS $ID $USE4BYTEAS # EBGP - IPV4 PEER=$HOST NEXT_HOP=192.150.187.78 PORT=$PORT1;PEER_PORT=$PEER_PORT1;PEER_AS=$PEER1_AS IPTUPLE="$LOCALHOST $PORT $PEER $PEER_PORT" add_peer lo $IPTUPLE $PEER_AS $NEXT_HOP $HOLDTIME set_parameter $IPTUPLE MultiProtocol.IPv4.Unicast true enable_peer $IPTUPLE # EBGP - IPV4 PEER=$HOST NEXT_HOP=192.150.187.78 PORT=$PORT2;PEER_PORT=$PEER_PORT2;PEER_AS=$PEER2_AS IPTUPLE="$LOCALHOST $PORT $PEER $PEER_PORT" add_peer lo $IPTUPLE $PEER_AS $NEXT_HOP $HOLDTIME set_parameter $IPTUPLE MultiProtocol.IPv4.Unicast true enable_peer $IPTUPLE # IBGP - IPV4 PEER=$HOST NEXT_HOP=192.150.187.78 PORT=$PORT3;PEER_PORT=$PEER_PORT3;PEER_AS=$PEER3_AS IPTUPLE="$LOCALHOST $PORT $PEER $PEER_PORT" add_peer lo $IPTUPLE $PEER_AS $NEXT_HOP $HOLDTIME set_parameter $IPTUPLE MultiProtocol.IPv4.Unicast true enable_peer $IPTUPLE # EBGP - IPV6 PEER=$HOST NEXT_HOP=192.150.187.78 PORT=$PORT1_IPV6;PEER_PORT=$PEER_PORT1_IPV6;PEER_AS=$PEER1_AS_IPV6 IPTUPLE="$LOCALHOST $PORT $PEER $PEER_PORT" add_peer lo $IPTUPLE $PEER_AS $NEXT_HOP $HOLDTIME set_parameter $IPTUPLE MultiProtocol.IPv6.Unicast true enable_peer $IPTUPLE # EBGP - IPV6 PEER=$HOST NEXT_HOP=192.150.187.78 PORT=$PORT2_IPV6;PEER_PORT=$PEER_PORT2_IPV6;PEER_AS=$PEER2_AS_IPV6 IPTUPLE="$LOCALHOST $PORT $PEER $PEER_PORT" add_peer lo $IPTUPLE $PEER_AS $NEXT_HOP $HOLDTIME set_parameter $IPTUPLE MultiProtocol.IPv6.Unicast true enable_peer $IPTUPLE # IBGP - IPV6 PEER=$HOST NEXT_HOP=192.150.187.78 PORT=$PORT3_IPV6;PEER_PORT=$PEER_PORT3_IPV6;PEER_AS=$PEER3_AS_IPV6 IPTUPLE="$LOCALHOST $PORT $PEER $PEER_PORT" add_peer lo $IPTUPLE $PEER_AS $NEXT_HOP $HOLDTIME set_parameter $IPTUPLE MultiProtocol.IPv6.Unicast true enable_peer $IPTUPLE } configure_rib() { echo "Configuring rib" export CALLXRL $RIB_FUNCS make_rib_errors_fatal } configure_fea() { echo "Configuring fea" export CALLXRL local tid=$($FEA_FUNCS start_fea_transaction) $FEA_FUNCS create_interface $tid $VIF0 $FEA_FUNCS create_interface $tid $VIF1 $FEA_FUNCS create_interface $tid $VIF0_IPV6 $FEA_FUNCS create_interface $tid $VIF1_IPV6 $FEA_FUNCS enable_interface $tid $VIF0 $FEA_FUNCS enable_interface $tid $VIF1 $FEA_FUNCS enable_interface $tid $VIF0_IPV6 $FEA_FUNCS enable_interface $tid $VIF1_IPV6 $FEA_FUNCS create_vif $tid $VIF0 $VIF0 $FEA_FUNCS create_vif $tid $VIF1 $VIF1 $FEA_FUNCS create_vif $tid $VIF0_IPV6 $VIF0_IPV6 $FEA_FUNCS create_vif $tid $VIF1_IPV6 $VIF1_IPV6 $FEA_FUNCS enable_vif $tid $VIF0 $VIF0 $FEA_FUNCS enable_vif $tid $VIF1 $VIF1 $FEA_FUNCS enable_vif $tid $VIF0_IPV6 $VIF0_IPV6 $FEA_FUNCS enable_vif $tid $VIF1_IPV6 $VIF1_IPV6 $FEA_FUNCS create_address4 $tid $VIF0 $VIF0 $NH1 $FEA_FUNCS create_address4 $tid $VIF1 $VIF1 $NH2 $FEA_FUNCS create_address6 $tid $VIF0_IPV6 $VIF0_IPV6 $NH1_IPV6 $FEA_FUNCS create_address6 $tid $VIF1_IPV6 $VIF1_IPV6 $NH2_IPV6 $FEA_FUNCS set_prefix4 $tid $VIF0 $VIF0 $NH1 24 $FEA_FUNCS set_prefix4 $tid $VIF1 $VIF1 $NH2 24 $FEA_FUNCS set_prefix6 $tid $VIF0_IPV6 $VIF0_IPV6 $NH1_IPV6 24 $FEA_FUNCS set_prefix6 $tid $VIF1_IPV6 $VIF1_IPV6 $NH2_IPV6 24 $FEA_FUNCS enable_address4 $tid $VIF0 $VIF0 $NH1 $FEA_FUNCS enable_address4 $tid $VIF1 $VIF1 $NH2 $FEA_FUNCS enable_address6 $tid $VIF0_IPV6 $VIF0_IPV6 $NH1_IPV6 $FEA_FUNCS enable_address6 $tid $VIF1_IPV6 $VIF1_IPV6 $NH2_IPV6 $FEA_FUNCS commit_fea_transaction $tid } unconfigure_fea() { echo "Unconfigure fea" } config_peers() { coord reset coord target $HOST $PORT1 coord initialise attach peer1 coord peer1 establish AS $PEER1_AS \ holdtime $HOLDTIME \ id 10.10.10.1 \ keepalive false coord peer1 assert established coord target $HOST $PORT2 coord initialise attach peer2 coord peer2 establish AS $PEER2_AS \ holdtime $HOLDTIME \ id 10.10.10.2 \ keepalive false coord peer2 assert established coord target $HOST $PORT3 coord initialise attach peer3 coord peer3 establish AS $PEER3_AS \ holdtime $HOLDTIME \ id 10.10.10.3 \ keepalive false coord peer3 assert established } config_peers_ipv6() { coord reset coord target $HOST $PORT1_IPV6 coord initialise attach peer1 coord peer1 establish AS $PEER1_AS_IPV6 \ holdtime $HOLDTIME \ id 10.10.10.1 \ keepalive false \ ipv6 true coord peer1 assert established coord target $HOST $PORT2_IPV6 coord initialise attach peer2 coord peer2 establish AS $PEER2_AS_IPV6 \ holdtime $HOLDTIME \ id 10.10.10.2 \ keepalive false \ ipv6 true coord peer2 assert established coord target $HOST $PORT3_IPV6 coord initialise attach peer3 coord peer3 establish AS $PEER3_AS_IPV6 \ holdtime $HOLDTIME \ id 10.10.10.3 \ keepalive false \ ipv6 true coord peer3 assert established } NLRI1=10.10.10.0/24 NLRI2=20.20.20.20/24 NLRI3=30.30.30.30/24 NLRI4=40.40.40.40/24 test1() { echo "TEST1 - Exercise the next hop resolver" config_peers PACKET1="packet update origin 2 aspath $PEER1_AS nexthop 128.16.0.1 nlri $NLRI1 nlri $NLRI2" PACKET2="packet update origin 2 aspath $PEER1_AS nexthop 128.16.1.1 nlri $NLRI3 nlri $NLRI4" # XXX This next hop should not resolve. coord peer1 send $PACKET1 coord peer1 send $PACKET2 echo "Sent routes to BGP, waiting..." sleep 2 coord peer2 trie recv lookup $NLRI1 not coord peer2 trie recv lookup $NLRI2 not coord peer2 trie recv lookup $NLRI3 not coord peer2 trie recv lookup $NLRI4 not # Lets get it to resolve. add_route4 connected true false 128.16.0.0/16 $NH1 # Add a different route. add_route4 connected true false 128.16.0.0/24 $NH2 # Delete the better route. delete_route4 connected true false 128.16.0.0/24 # Try and verify that the correct route has popped out at peer2. sleep 2 coord peer2 trie recv lookup $NLRI1 aspath "$AS,$PEER1_AS" coord peer2 trie recv lookup $NLRI2 aspath "$AS,$PEER1_AS" coord peer2 trie recv lookup $NLRI3 aspath "$AS,$PEER1_AS" coord peer2 trie recv lookup $NLRI4 aspath "$AS,$PEER1_AS" # Make sure that the connections still exist. coord peer1 assert established coord peer2 assert established coord peer3 assert established # Tidy up. Remove any routes that we may have added to the RIB. delete_route4 connected true false 128.16.0.0/16 # At this point the RIB can no longer resolve the nexthop associated # with this net so the net should no longer be present. sleep 2 coord peer2 trie recv lookup $NLRI1 not coord peer2 trie recv lookup $NLRI2 not coord peer2 trie recv lookup $NLRI3 not coord peer2 trie recv lookup $NLRI4 not } NLRI1_IPV6=10:10:10:10:10:00:00:00/80 NLRI2_IPV6=20:20:20:20:20:00:00:00/80 NLRI3_IPV6=30:30:30:30:30:00:00:00/80 NLRI4_IPV6=40:40:40:40:40:00:00:00/80 test1_ipv6() { echo "TEST1 IPV6 - Exercise the next hop resolver" config_peers_ipv6 PACKET1="packet update origin 2 aspath $PEER1_AS_IPV6 nexthop6 128:16::1 nlri6 $NLRI1_IPV6 nlri6 $NLRI2_IPV6" PACKET2="packet update origin 2 aspath $PEER1_AS_IPV6 nexthop6 128:16:1::1 nlri6 $NLRI3_IPV6 nlri6 $NLRI4_IPV6" # XXX This next hop should not resolve. coord peer1 send $PACKET1 coord peer1 send $PACKET2 echo "Sent routes to BGP, waiting..." sleep 2 coord peer2 trie recv lookup $NLRI1_IPV6 not coord peer2 trie recv lookup $NLRI2_IPV6 not coord peer2 trie recv lookup $NLRI3_IPV6 not coord peer2 trie recv lookup $NLRI4_IPV6 not # Lets get it to resolve. add_route6 connected true false 128:16::0/32 $NH1_IPV6 # Add a different route. add_route6 connected true false 128:16::0/48 $NH2_IPV6 # Delete the better route. delete_route6 connected true false 128:16::0/48 # Try and verify that the correct route has popped out at peer2. sleep 2 coord peer2 trie recv lookup $NLRI1_IPV6 aspath "$AS,$PEER1_AS_IPV6" coord peer2 trie recv lookup $NLRI2_IPV6 aspath "$AS,$PEER1_AS_IPV6" coord peer2 trie recv lookup $NLRI3_IPV6 aspath "$AS,$PEER1_AS_IPV6" coord peer2 trie recv lookup $NLRI4_IPV6 aspath "$AS,$PEER1_AS_IPV6" # Make sure that the connections still exist. coord peer1 assert established coord peer2 assert established coord peer3 assert established # Tidy up. Remove any routes that we may have added to the RIB. delete_route6 connected true false 128:16::0/32 # At this point the RIB can no longer resolve the nexthop associated # with this net so the net should no longer be present. sleep 2 coord peer2 trie recv lookup $NLRI1_IPV6 not coord peer2 trie recv lookup $NLRI2_IPV6 not coord peer2 trie recv lookup $NLRI3_IPV6 not coord peer2 trie recv lookup $NLRI4_IPV6 not } test2() { echo "TEST2 - Run test1 twice with the same process" test1 test1 } test2_ipv6() { echo "TEST2 IPV6 - Run test1 twice with the same process" test1_ipv6 test1_ipv6 } test3() { echo "TEST3 - Try and force a deregistration from the RIB" config_peers PACKET1="packet update origin 2 aspath $PEER1_AS nexthop 128.16.0.1 nlri 10.10.10.0/24 nlri 20.20.20.20/24" # XXX This next hop should not resolve. coord peer1 send $PACKET1 sleep 2 # Lets get it to resolve. add_route4 connected true false 128.16.0.0/16 172.16.1.2 1 # Add a different route. add_route4 connected true false 128.16.0.0/24 172.16.2.2 1 # Delete the better route. delete_route4 connected true false 128.16.0.0/24 # Try and verify that the correct route has popped out at peer2. coord peer1 assert established coord peer2 assert established coord peer3 assert established # Tidy up. Remove any routes that we may have added to the RIB. delete_route4 connected true false 128.16.0.0/16 config_peers } test4() { # 1) Send an update packet on peer1 with a nexthop that is not resolvable. # 2) Make the next hop resolvable by adding a route to the RIB. # 3) Verify that an update packet now pops out of peer2 and peer3. echo "TEST4 - Metrics changing for a nexthop" config_peers PACKET="packet update origin 2 aspath $PEER1_AS nexthop 128.16.0.1 nlri 10.10.10.0/24" # At this point the nexthop should not resolve. coord peer1 send $PACKET # Make sure that update packet has popped out even though the nexthops # are not resolvable. sleep 2 coord peer2 trie recv lookup 10.10.10.0/24 not coord peer3 trie recv lookup 10.10.10.0/24 not # Lets get it to resolve. add_route4 connected true false 128.16.0.0/16 172.16.1.2 1 # Try and verify that the correct route has popped out at peer2. sleep 2 coord peer2 trie recv lookup 10.10.10.0/24 aspath "$AS,$PEER1_AS" coord peer3 trie recv lookup 10.10.10.0/24 aspath "$PEER1_AS" # Make sure that the connections still exist. coord peer1 assert established coord peer2 assert established coord peer3 assert established } test5() { echo "TEST5: " echo " 1) Send two update packets with the same route" echo " 2) The nexthops are different one resolves the other doesn't" echo " 3) The route add on the RIB causes the unresolvable nexthop to resolve" config_peers PACKET1="packet update origin 2 aspath $PEER1_AS nexthop 1.2.3.4 nlri 10.10.10.0/24" PACKET2="packet update origin 2 aspath $PEER1_AS nexthop 128.16.0.1 nlri 10.10.10.0/24" # The nexthop for this packet should never be resolvable. coord peer1 send $PACKET1 # Verify that no update packets have been sent to the peers. sleep 2 coord peer2 trie recv lookup 10.10.10.0/24 not coord peer3 trie recv lookup 10.10.10.0/24 not # Make sure that the nexthop carried in PACKET2 will resolve add_route4 connected true false 128.16.0.0/16 172.16.1.2 1 sleep 2 # Sending this used to cause a fatal error in BGP. coord peer1 send $PACKET2 # Try and verify that the correct route has popped out at peer2. sleep 2 coord peer2 trie recv lookup 10.10.10.0/24 aspath "$AS,$PEER1_AS" coord peer3 trie recv lookup 10.10.10.0/24 aspath "$PEER1_AS" # Make sure that the connections still exist. coord peer1 assert established coord peer2 assert established coord peer3 assert established } test6() { echo "TEST6: " echo " 1) Send two update packets with the same route" echo " 2) The nexthops are different one resolves the other doesn't" echo " 3) The route add on the RIB doesn't cause the unresolvable nexthop to resolve" config_peers PACKET1="packet update origin 2 aspath $PEER1_AS nexthop 1.2.3.4 nlri 10.10.10.0/32" PACKET2="packet update origin 2 aspath $PEER1_AS nexthop 128.16.0.1 nlri 10.10.10.0/32" # Make sure that the nexthop carried in PACKET2 will resolve add_route4 connected true false 128.16.0.0/16 172.16.1.2 1 sleep 2 # The nexthop for this packet should never be resolvable. coord peer1 send $PACKET1 # Verify that no update packets have been sent to the peers. sleep 2 coord peer2 trie recv lookup 10.10.10.0/32 not coord peer3 trie recv lookup 10.10.10.0/32 not # Sending this used to cause a fatal error in BGP. coord peer1 send $PACKET2 # Try and verify that the correct route has popped out at peer2. sleep 2 coord peer2 trie recv lookup 10.10.10.0/32 aspath "$AS,$PEER1_AS" coord peer3 trie recv lookup 10.10.10.0/32 aspath "$PEER1_AS" # Make sure that the connections still exist. coord peer1 assert established coord peer2 assert established coord peer3 assert established } TESTS_NOT_FIXED='' TESTS='test1 test1_ipv6 test2 test2_ipv6 test3 test4 test5 test6' # Include command line . ${srcdir}/args.sh if [ $START_PROGRAMS = "yes" ] then CXRL="$CALLXRL -r 10" runit $QUIET $VERBOSE -c "$0 -s -c $*" <& words) throw(InvalidString); void connect(const string& line, const vector& words) throw(InvalidString); void disconnect(const string& line, const vector& words) throw(InvalidString); void establish(const string& line, const vector& words) throw(InvalidString); void send(const string& line, const vector& words) throw(InvalidString); void send_packet(const string& line, const vector& words) throw(InvalidString); void send_dump(const string& line, const vector& words) throw(InvalidString); void trie(const string& line, const vector& words) throw(InvalidString); void expect(const string& line, const vector& words) throw(InvalidString); void assertX(const string& line, const vector& words) throw(InvalidString); void dump(const string& line, const vector& words) throw(InvalidString); void check_expect(BGPPacket *rec); void xrl_callback(const XrlError& error, const char *comment); void xrl_callback_connected(const XrlError& error, const char *comment); void datain(const bool& status, const TimeVal& tv, const vector& data); void datain_error(const string& reason); void datain_closed(); PathAttribute *path_attribute(const char *) const throw(InvalidString); const BGPPacket *packet(const string& line, const vector& words, int index) const throw(InvalidString); protected: typedef XorpCallback1::RefPtr SMCB; SMCB _smcb; void send_message(const uint8_t *buf, const size_t len, SMCB cb); void send_dump_callback(const XrlError& error, FILE *fp, const size_t packet_number, const size_t packets_to_send, const char *comment); void send_open(); void send_keepalive(); private: EventLoop *_eventloop; XrlStdRouter *_xrlrouter; /* we need these because the attribute encode methods reference them */ LocalData *_localdata; BGPPeerData *_peerdata; string _peername; uint32_t _genid; string _target_hostname; string _target_port; bool _up; // True if this peer has not been shutdown TimeVal _shutdown_time; // Time this peer was shutdown uint32_t _busy; // Count of outstanding transactions. bool _connected; // True if we believe a TCP connection session. bool _session; // We are attempting to form a BGP session. bool _passive; // We are passively trying to create a session. bool _established; // True if we believe a session has been formed. bool _keepalive; // If true echo all received keepalives. AsNum _as; // Our AS number. int _holdtime; // The holdtime sent in the BGP open message. IPv4 _id; // The ID sent in the open message. bool _ipv6; // Should we announce an ipv6 capability in // the open message. Trie _trie_recv; // Update messages received. Trie _trie_sent; // Update messages sent. // Number of update messages received or sent since last // disconnect, used to compute the number of messages in the // current session. uint32_t _last_recv; uint32_t _last_sent; typedef XorpCallback3::RefPtr Dumper; Dumper _traffic_recv; Dumper _traffic_sent; struct expect { expect() : _ok(true), _bad(0) { } ~expect() { list::iterator i; for(i = _list.begin(); i != _list.end(); i++) delete *i; delete _bad; } list _list; // Messages that we are expecting. bool _ok; // True while expected messages arrive. const BGPPacket *_bad; // The mismatched packet. } _expect; }; #endif // __BGP_HARNESS_PEER_HH__ xorp/bgp/harness/test_peer.hh0000664000076400007640000001333611540224217016412 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/bgp/harness/test_peer.hh,v 1.20 2008/10/02 21:56:26 bms Exp $ #ifndef __BGP_HARNESS_TEST_PEER_HH__ #define __BGP_HARNESS_TEST_PEER_HH__ #include "xrl/targets/test_peer_base.hh" #include "bgp/packet.hh" class TestPeer { public: TestPeer(EventLoop& eventloop, XrlRouter& xrlrouter, const char *server, bool verbose); ~TestPeer(); bool done(); void register_coordinator(const string& name); void register_genid(const uint32_t& genid); bool packetisation(const string& protocol); void use_4byte_asnums(bool use); bool connect(const string& host, const uint32_t& port, string& error_string); bool listen(const string& host, const uint32_t& port, string& error_string); bool bind(const string& host, const uint32_t& port, string& error_string); bool send(const vector& data, string& error_string); void send_complete(AsyncFileWriter::Event ev, const uint8_t *buf, const size_t buf_bytes, const size_t offset); bool zap(XorpFd &fd, const char *msg); bool disconnect(string& error_string); void reset(); bool terminate(string& error_string); protected: void connect_attempt(XorpFd fd, IoEventType type); void receive(XorpFd fd, IoEventType type); enum status { GOOD, CLOSED, ERROR }; void datain(status st, uint8_t *ptr, size_t len, string error = ""); void queue_data(status st, uint8_t *ptr, size_t len, string error); void sendit(); void xrl_callback(const XrlError& error); private: TestPeer(const TestPeer&); TestPeer& operator=(const TestPeer&); private: EventLoop& _eventloop; XrlRouter& _xrlrouter; /* these are needed so we know how to decode ASnums (4-byte or 2-byte); */ LocalData* _localdata; BGPPeerData* _peerdata; const char *_server; const bool _verbose; bool _done; XorpFd _s; XorpFd _listen; XorpFd _bind; AsyncFileWriter *_async_writer; string _coordinator; uint32_t _genid; bool _bgp; // Perform BGP packetisation bool _use_4byte_asnums; // Assume AS numbers from peer are 4-byte. struct Queued { uint32_t secs; uint32_t micro; status st; size_t len; vector v; string error; }; queue _xrl_queue; static const int FLYING_LIMIT = 100; // XRL's allowed in flight at // one time. int _flying; size_t _bgp_bytes; uint8_t _bgp_buf[BGPPacket::MAXPACKETSIZE]; // Maximum allowed BGP message }; class XrlTestPeerTarget : XrlTestPeerTargetBase { public: XrlTestPeerTarget(XrlRouter *r, TestPeer& test_peer, bool trace); /** * Get name of Xrl Target */ XrlCmdError common_0_1_get_target_name( // Output values, string& name); XrlCmdError common_0_1_startup() { return XrlCmdError::OKAY(); } /** * Get version string from Xrl Target */ XrlCmdError common_0_1_get_version( // Output values, string& version); /** * Get status from Xrl Target */ XrlCmdError common_0_1_get_status( // Output values, uint32_t& status, string& reason); /** * shutdown target */ XrlCmdError common_0_1_shutdown(); /** * Register for receiving packets and events. */ XrlCmdError test_peer_0_1_register( // Input values, const string& coordinator, const uint32_t& genid); /** * Packetisation style. */ XrlCmdError test_peer_0_1_packetisation( // Input values, const string& protocol); /** * Use 4byte asnums. */ XrlCmdError test_peer_0_1_use_4byte_asnums( // Input values, const bool& use); /** * Make a tcp connection to the specified host and port. * * @param host name. * * @param port number. */ XrlCmdError test_peer_0_1_connect( // Input values, const string& host, const uint32_t& port); /** * Accept connections from the specified host on the specified port. * * @param host name. * * @param port number. */ XrlCmdError test_peer_0_1_listen( // Input values, const string& address, const uint32_t& port); /** * Bind the port but don't perform the listen or accept. * * @param address local address. * * @param port local port number. */ XrlCmdError test_peer_0_1_bind( // Input values, const string& address, const uint32_t& port); /** * Send data to the peer. */ XrlCmdError test_peer_0_1_send( // Input values, const vector& data); /** * Disconnect from the peer. */ XrlCmdError test_peer_0_1_disconnect(); /** * Reset the peer. */ XrlCmdError test_peer_0_1_reset(); /** * Terminate the test peer process. */ XrlCmdError test_peer_0_1_terminate(); private: TestPeer& _test_peer; bool _trace; }; #endif // __BGP_HARNESS_TEST_PEER_HH__ xorp/bgp/harness/test_route_reflection2.sh0000775000076400007640000001765711421137511021137 0ustar greearbgreearb#!/usr/bin/env bash # # $XORP: xorp/bgp/harness/test_route_reflection2.sh,v 1.3 2006/08/16 22:10:14 atanu Exp $ # # # Test basic BGP routing reflection. # # This script started with no arguments will start all required process and # terminate them at the end of the tests. # # Preconditons # 1) Run a finder process # 2) Run "../fea/xorp_fea_dummy" # 3) Run xorp "../rib/xorp_rib" # 4) Run xorp "../xorp_bgp" # 5) Run "./test_peer -s peer1" # 6) Run "./test_peer -s peer2" # 7) Run "./test_peer -s peer3" # 8) Run "./coord" # # The BGP process is configured as a route reflector. # Peer 1 I-BGP # Peer 2 I-BGP RR client # Peer 3 E-BGP set -e . ./setup_paths.sh onexit() { last=$? if [ $last = "0" ] then echo "$0: Tests Succeeded (BGP: $TESTS)" else echo "$0: Tests Failed (BGP: $TESTS)" fi trap '' 0 2 } trap onexit 0 2 if [ "X${srcdir}" = "X" ] ; then srcdir=`dirname $0` ; fi . ${srcdir}/xrl_shell_funcs.sh "" . $BGP_FUNCS "" . $RIB_FUNCS "" HOST=127.0.0.1 PORT1=10001 PORT2=10002 PORT3=10003 PORT1_IPV6=10004 PORT2_IPV6=10005 PORT3_IPV6=10006 PEER_PORT1=20001 PEER_PORT2=20002 PEER_PORT3=20003 PEER_PORT1_IPV6=20004 PEER_PORT2_IPV6=20005 PEER_PORT3_IPV6=20006 AS=65008 USE4BYTEAS=false PEER1_AS=$AS PEER2_AS=$AS PEER3_AS=65000 PEER1_AS_IPV6=$AS PEER2_AS_IPV6=$AS PEER3_AS_IPV6=65000 HOLDTIME=0 # Next Hops NH1_IPv4=172.16.1.2 NH2_IPV4=172.16.2.2 NH3_IPv4=172.16.3.2 NH1_IPV6=40:40:40:40:40:40:40:42 NH2_IPV6=50:50:50:50:50:50:50:52 NH3_IPV6=60:60:60:60:60:60:60:62 NEXT_HOP=192.150.187.78 ID=192.150.187.78 CLUSTER_ID=0.0.0.11 configure_bgp() { LOCALHOST=$HOST AS=65008 local_config $AS $ID $USE4BYTEAS route_reflector $CLUSTER_ID false # Don't try and talk to the rib. register_rib "" # IBGP - IPV4 PEER=$HOST PORT=$PORT1;PEER_PORT=$PEER_PORT1;PEER_AS=$PEER1_AS IPTUPLE="$LOCALHOST $PORT $PEER $PEER_PORT" add_peer lo $IPTUPLE $PEER_AS $NEXT_HOP $HOLDTIME set_parameter $IPTUPLE MultiProtocol.IPv4.Unicast true enable_peer $IPTUPLE # IBGP - CLIENT - IPV4 PEER=$HOST PORT=$PORT2;PEER_PORT=$PEER_PORT2;PEER_AS=$PEER2_AS IPTUPLE="$LOCALHOST $PORT $PEER $PEER_PORT" add_peer lo $IPTUPLE $PEER_AS $NEXT_HOP $HOLDTIME set_parameter $IPTUPLE MultiProtocol.IPv4.Unicast true route_reflector_client $IPTUPLE true enable_peer $IPTUPLE # EBGP - IPV4 PEER=$HOST PORT=$PORT3;PEER_PORT=$PEER_PORT3;PEER_AS=$PEER3_AS IPTUPLE="$LOCALHOST $PORT $PEER $PEER_PORT" add_peer lo $IPTUPLE $PEER_AS $NEXT_HOP $HOLDTIME set_parameter $IPTUPLE MultiProtocol.IPv4.Unicast true enable_peer $IPTUPLE # IBGP - IPV6 PEER=$HOST PORT=$PORT1_IPV6;PEER_PORT=$PEER_PORT1_IPV6;PEER_AS=$PEER1_AS_IPV6 IPTUPLE="$LOCALHOST $PORT $PEER $PEER_PORT" add_peer lo $IPTUPLE $PEER_AS $NEXT_HOP $HOLDTIME set_parameter $IPTUPLE MultiProtocol.IPv6.Unicast true enable_peer $IPTUPLE # IBGP - CLIENT - IPV6 PEER=$HOST PORT=$PORT2_IPV6;PEER_PORT=$PEER_PORT2_IPV6;PEER_AS=$PEER2_AS_IPV6 IPTUPLE="$LOCALHOST $PORT $PEER $PEER_PORT" add_peer lo $IPTUPLE $PEER_AS $NEXT_HOP $HOLDTIME set_parameter $IPTUPLE MultiProtocol.IPv6.Unicast true route_reflector_client $IPTUPLE true enable_peer $IPTUPLE # EBGP - IPV6 PEER=$HOST PORT=$PORT3_IPV6;PEER_PORT=$PEER_PORT3_IPV6;PEER_AS=$PEER3_AS_IPV6 IPTUPLE="$LOCALHOST $PORT $PEER $PEER_PORT" add_peer lo $IPTUPLE $PEER_AS $NEXT_HOP $HOLDTIME set_parameter $IPTUPLE MultiProtocol.IPv6.Unicast true enable_peer $IPTUPLE } config_peers_ipv4() { coord reset coord target $HOST $PORT1 coord initialise attach peer1 coord peer1 establish AS $PEER1_AS \ holdtime $HOLDTIME \ id 10.10.10.1 \ keepalive false coord peer1 assert established coord target $HOST $PORT2 coord initialise attach peer2 coord peer2 establish AS $PEER2_AS \ holdtime $HOLDTIME \ id 10.10.10.2 \ keepalive false coord peer2 assert established coord target $HOST $PORT3 coord initialise attach peer3 coord peer3 establish AS $PEER3_AS \ holdtime $HOLDTIME \ id 10.10.10.3 \ keepalive false coord peer3 assert established } test1() { echo "TEST1 - Establish three peerings" config_peers_ipv4 } test2() { echo "TEST2 - Bugzilla BUG #425" echo " 1) Verify that a CLUSTER_LIST is not sent to an E-BGP peer." config_peers_ipv4 PACKET="packet update origin 2 aspath 1 nexthop $NEXT_HOP nlri 10.10.10.0/24 localpref 10 clusterlist 1.2.3.4" RR_PACKET="$PACKET originatorid 10.10.10.1 clusterlist $CLUSTER_ID" EBGP_PACKET="packet update origin 2 aspath 65008,1 nexthop $NEXT_HOP nlri 10.10.10.0/24 med 1" coord peer1 expect $PACKET coord peer2 expect $RR_PACKET coord peer3 expect $EBGP_PACKET coord peer1 send $PACKET sleep 2 coord peer1 assert queue 1 coord peer2 assert queue 0 coord peer3 assert queue 0 # At the end of the test we expect all the peerings to still be established. coord peer1 assert established coord peer2 assert established coord peer3 assert established } test3() { echo "TEST3 - Based on Bugzilla BUG #425" echo " 1) Verify that an ORIGINATOR_ID is not sent to an E-BGP peer." config_peers_ipv4 PACKET="packet update origin 2 aspath 1 nexthop $NEXT_HOP nlri 10.10.10.0/24 localpref 10 originatorid 1.2.3.4" RR_PACKET="$PACKET clusterlist $CLUSTER_ID" EBGP_PACKET="packet update origin 2 aspath 65008,1 nexthop $NEXT_HOP nlri 10.10.10.0/24 med 1" coord peer1 expect $PACKET coord peer2 expect $RR_PACKET coord peer3 expect $EBGP_PACKET coord peer1 send $PACKET sleep 2 coord peer1 assert queue 1 coord peer2 assert queue 0 coord peer3 assert queue 0 # At the end of the test we expect all the peerings to still be established. coord peer1 assert established coord peer2 assert established coord peer3 assert established } test4() { echo "TEST4 - Based on Bugzilla BUG #425" echo " 1) ORIGINATOR_IDs and CLUSTER_LIST attributes shouldn't be sent to EBGP peers include both attributes" config_peers_ipv4 PACKET="packet update origin 2 aspath 1 nexthop $NEXT_HOP nlri 10.10.10.0/24 localpref 10 originatorid 1.2.3.4 clusterlist 1.2.3.4" RR_PACKET="$PACKET clusterlist $CLUSTER_ID" EBGP_PACKET="packet update origin 2 aspath 65008,1 nexthop $NEXT_HOP nlri 10.10.10.0/24 med 1" coord peer1 expect $PACKET coord peer2 expect $RR_PACKET coord peer3 expect $EBGP_PACKET coord peer1 send $PACKET sleep 2 coord peer1 assert queue 1 coord peer2 assert queue 0 coord peer3 assert queue 0 # At the end of the test we expect all the peerings to still be established. coord peer1 assert established coord peer2 assert established coord peer3 assert established } TESTS_NOT_FIXED='' TESTS='test1 test2 test3 test4' # Include command line . ${srcdir}/args.sh if [ $START_PROGRAMS = "yes" ] then CXRL="$CALLXRL -r 10" runit $QUIET $VERBOSE -c "$0 -s -c $*" < class RealTrie { public: RealTrie() : _debug(true), _pretty(true) { } ~RealTrie(); bool empty() const; typedef typename XorpCallback3&, const TimeVal&>::RefPtr TreeWalker; void tree_walk_table(const TreeWalker& tw) const { A topbit; ++topbit; topbit = topbit << A::addr_bitlen() - 1; tree_walk_table(tw, &_head, A::ZERO(), 0, topbit); }; bool insert(const IPNet& net, TriePayload& p); bool insert(A address, size_t mask_length, TriePayload& p); void del() { del(&_head); } bool del(const IPNet& net); bool del(A address, size_t mask_length); TriePayload find(const IPNet& net) const; TriePayload find(A address, size_t mask_length) const; TriePayload find(const A& address) const; private: struct Tree { Tree *ptrs[2]; TriePayload p; Tree() { ptrs[0] = ptrs[1] = 0; } }; void tree_walk_table(const TreeWalker&, const Tree *ptr, A address, size_t prefix_len, A orbit) const; void del(Tree *ptr); bool del(Tree *ptr, A address, size_t mask_length); bool _debug; bool _pretty; Tree _head; }; template RealTrie::~RealTrie() { del(&_head); } template bool RealTrie::empty() const { return (0 ==_head.ptrs[0]) && (0 ==_head.ptrs[1]); } template void RealTrie::tree_walk_table(const TreeWalker& tw, const Tree *ptr, A address, size_t prefix_len, A orbit) const { debug_msg("Enter: %s/%u\n", address.str().c_str(), XORP_UINT_CAST(prefix_len)); if(0 == ptr) { return; } TimeVal tv; const UpdatePacket *update = ptr->p.get(tv); if(0 != update) { debug_msg("Found %s/%u\n", address.str().c_str(), XORP_UINT_CAST(prefix_len)); tw->dispatch(update, IPNet(address, prefix_len), tv); } ++prefix_len; A save_orbit = orbit; orbit = orbit >> 1; tree_walk_table(tw, ptr->ptrs[0], address, prefix_len, orbit); address = address | save_orbit; tree_walk_table(tw, ptr->ptrs[1], address, prefix_len, orbit); } template bool RealTrie::insert(const IPNet& net, TriePayload& p) { return insert(net.masked_addr(), net.prefix_len(), p); } template bool RealTrie::insert(A address, size_t mask_length, TriePayload& p) { debug_msg("%s/%u\n", address.str().c_str(), XORP_UINT_CAST(mask_length)); #ifdef PARANOIA if(0 == p.get()) { debug_msg("insert: Attempt to store an invalid entry\n"); return false; } #endif Tree *ptr = &_head; for(size_t i = 0; i < mask_length; i++) { int index; if(address.bits(A::addr_bitlen() - 1, 1)) index = 1; else index = 0; address = address << 1; debug_msg("address %s prefix_len %u depth %u index %d\n", address.str().c_str(), XORP_UINT_CAST(mask_length), XORP_UINT_CAST(i), index); if(0 == ptr->ptrs[index]) { ptr->ptrs[index] = new Tree(); if(0 == ptr->ptrs[index]) { if(_debug) debug_msg("insert: new failed\n"); return false; } } ptr = ptr->ptrs[index]; } if(0 != ptr->p.get()) { debug_msg("insert: value already assigned\n"); return false; } ptr->p = p; return true; } template void RealTrie::del(Tree *ptr) { if(0 == ptr) return; del(ptr->ptrs[0]); delete ptr->ptrs[0]; ptr->ptrs[0] = 0; del(ptr->ptrs[1]); delete ptr->ptrs[1]; ptr->ptrs[1] = 0; } template bool RealTrie::del(const IPNet& net) { return del(net.masked_addr(), net.prefix_len()); } template bool RealTrie::del(A address, size_t mask_length) { return del(&_head, address, mask_length); } template bool RealTrie::del(Tree *ptr, A address, size_t mask_length) { if((0 == ptr) && (0 != mask_length)) { if(_debug) debug_msg("del:1 not in table\n"); return false; } int index; if(address.bits(A::addr_bitlen() - 1, 1)) index = 1; else index = 0; address = address << 1; if(0 == mask_length) { if(0 == ptr) { if(_debug) debug_msg("del:1 zero pointer\n"); return false; } if(0 == ptr->p.get()) { if(_debug) debug_msg("del:2 not in table\n"); return false; } ptr->p = TriePayload(); // Invalidate this entry. return true; } if(0 == ptr) { if(_debug) debug_msg("del:2 zero pointer\n"); return false; } Tree *next = ptr->ptrs[index]; bool status = del(next, address, --mask_length); if(0 == next) { if(_debug) debug_msg("del: no next pointer\n"); return false; } if((0 == next->p.get()) && (0 == next->ptrs[0]) && (0 == next->ptrs[1])) { delete ptr->ptrs[index]; ptr->ptrs[index] = 0; } return status; } template TriePayload RealTrie::find(const IPNet& net) const { return find(net.masked_addr(), net.prefix_len()); } template TriePayload RealTrie::find(A address, size_t mask_length) const { const Tree *ptr = &_head; Tree *next; debug_msg("find: %s/%u\n", address.str().c_str(), XORP_UINT_CAST(mask_length)); // The loop should not require bounding. Defensive for(size_t i = 0; i <= A::addr_bitlen(); i++) { int index; if(address.bits(A::addr_bitlen() - 1, 1)) index = 1; else index = 0; address = address << 1; if(_pretty) debug_msg("%d", index); TriePayload p = ptr->p; if(mask_length == i) return p; if(0 == (next = ptr->ptrs[index])) { if(_pretty) debug_msg("\n"); return TriePayload(); } ptr = next; } debug_msg("find: should never happen\n"); return TriePayload(); } #endif // __BGP_HARNESS_REAL_TRIE_HH_ xorp/bgp/harness/test_rib1.sh0000775000076400007640000003034411421137511016326 0ustar greearbgreearb#!/usr/bin/env bash # # $XORP: xorp/bgp/harness/test_rib1.sh,v 1.22 2006/09/28 15:52:26 zec Exp $ # # # Test basic BGP and RIB interaction. Note that all the tests are run twice # once with the RIB unconfigured and once with the RIB configured. # # This script started with no arguments will start all required process and # terminate them at the end of the tests. # # Preconditons # 1) Run a finder process # 2) Run a FEA process. # 3) Run a RIB process. # 4) Run xorp "../xorp_bgp" # 5) Run "./test_peer -s peer1" # 6) Run "./coord" # set -e . ./setup_paths.sh onexit() { last=$? if [ $last = "0" ] then echo "$0: Tests Succeeded (BGP/RIB: $TESTS)" else echo "$0: Tests Failed (BGP/RIB: $TESTS)" fi trap '' 0 2 } trap onexit 0 2 # srcdir is set by make for check target if [ "X${srcdir}" = "X" ] ; then srcdir=`dirname $0` ; fi . ${srcdir}/xrl_shell_funcs.sh "" . $BGP_FUNCS "" . $RIB_FUNCS "" HOST=127.0.0.1 AS=65008 USE4BYTEAS=false # IBGP - IPV4 PORT1=10000 PEER1_PORT=20001 PEER1_AS=$AS # EBGP - IPV4 PORT2=10001 PEER2_PORT=20001 PEER2_AS=65009 # IBGP - IPV6 PORT1_IPV6=10002 PEER1_PORT_IPV6=20002 PEER1_AS_IPV6=$AS # EBGP - IPV6 PORT2_IPV6=10003 PEER2_PORT_IPV6=20003 PEER2_AS_IPV6=65009 HOLDTIME=5 configure_bgp() { LOCALHOST=$HOST ID=192.150.187.78 local_config $AS $ID $USE4BYTEAS # IBGP - IPV4 PEER=$HOST NEXT_HOP=192.150.187.78 PORT=$PORT1;PEER_PORT=$PEER1_PORT;PEER_AS=$PEER1_AS IPTUPLE="$LOCALHOST $PORT $PEER $PEER_PORT" add_peer lo $IPTUPLE $PEER_AS $NEXT_HOP $HOLDTIME set_parameter $IPTUPLE MultiProtocol.IPv4.Unicast true enable_peer $IPTUPLE # EBGP - IPV4 PEER=$HOST NEXT_HOP=192.150.187.78 PORT=$PORT2;PEER_PORT=$PEER2_PORT;PEER_AS=$PEER2_AS IPTUPLE="$LOCALHOST $PORT $PEER $PEER_PORT" add_peer lo $IPTUPLE $PEER_AS $NEXT_HOP $HOLDTIME set_parameter $IPTUPLE MultiProtocol.IPv4.Unicast true enable_peer $IPTUPLE # IBGP - IPV6 PEER=$HOST NEXT_HOP=192.150.187.78 PORT=$PORT1_IPV6;PEER_PORT=$PEER1_PORT_IPV6;PEER_AS=$PEER1_AS_IPV6 IPTUPLE="$LOCALHOST $PORT $PEER $PEER_PORT" add_peer lo $IPTUPLE $PEER_AS $NEXT_HOP $HOLDTIME set_parameter $IPTUPLE MultiProtocol.IPv6.Unicast true enable_peer $IPTUPLE # EBGP - IPV6 PEER=$HOST NEXT_HOP=192.150.187.78 PORT=$PORT2_IPV6;PEER_PORT=$PEER2_PORT_IPV6;PEER_AS=$PEER2_AS_IPV6 IPTUPLE="$LOCALHOST $PORT $PEER $PEER_PORT" add_peer lo $IPTUPLE $PEER_AS $NEXT_HOP $HOLDTIME set_parameter $IPTUPLE MultiProtocol.IPv6.Unicast true enable_peer $IPTUPLE } # Next Hops #NH1=10.10.10.10 #NH2=20.20.20.20 NH1=172.16.1.1 NH2=172.16.2.1 NH1_IPV6=40:40:40:40:40:40:40:40 NH2_IPV6=50:50:50:50:50:50:50:50 configure_rib() { make_rib_errors_fatal VIF0="vif0" VIF1="vif1" VIF0_IPV6="vif2" VIF1_IPV6="vif3" new_vif $VIF0 new_vif $VIF1 new_vif $VIF0_IPV6 new_vif $VIF1_IPV6 add_vif_addr4 $VIF0 $NH1 $NH1/24 add_vif_addr4 $VIF1 $NH2 $NH2/24 add_vif_addr6 $VIF0_IPV6 $NH1_IPV6 $NH1_IPV6/24 add_vif_addr6 $VIF1_IPV6 $NH2_IPV6 $NH2_IPV6/24 } reset_ibgp() { coord reset coord target $HOST $PORT1 coord initialise attach peer1 # Allow the initialization to take place sleep 1 } reset_ibgp_ipv6() { coord reset coord target $HOST $PORT1_IPV6 coord initialise attach peer1 # Allow the initialization to take place sleep 1 } reset_ebgp() { coord reset coord target $HOST $PORT2 coord initialise attach peer1 # Allow the initialization to take place sleep 1 } reset_ebgp_ipv6() { coord reset coord target $HOST $PORT2_IPV6 coord initialise attach peer1 # Allow the initialization to take place sleep 1 } test1() { echo "TEST1 - Send the same route to IBGP hence RIB twice" reset_ibgp coord peer1 establish AS $PEER1_AS holdtime 0 id 192.150.187.100 sleep 2 PACKET="packet update origin 2 aspath 1 nexthop $NH1 nlri 10.10.10.0/24 nlri 20.20.20.20/24" coord peer1 send $PACKET coord peer1 send $PACKET coord peer1 assert established } test1_ipv6() { echo "TEST1 IPV6 - Send the same route to IBGP hence RIB twice" reset_ibgp_ipv6 coord peer1 establish AS $PEER1_AS_IPV6 holdtime 0 id 192.150.187.100 sleep 2 NLRI1=10:10:10:10:10:00:00:00/80 NLRI2=20:20:20:20:20:00:00:00/80 PACKET="packet update origin 2 aspath 15 nexthop6 $NH1_IPV6 nlri6 $NLRI1 nlri6 $NLRI2" coord peer1 send $PACKET coord peer1 send $PACKET coord peer1 assert established } test2() { echo "TEST2 - Send the same route to EBGP hence RIB twice" reset_ebgp coord peer1 establish AS $PEER2_AS holdtime 0 id 192.150.187.100 sleep 2 PACKET="packet update origin 2 aspath $PEER2_AS nexthop $NH1 nlri 10.10.10.0/24 nlri 20.20.20.20/24" coord peer1 send $PACKET coord peer1 send $PACKET coord peer1 assert established } test3() { echo "TEST3 - Send the same route to BGP hence RIB twice" reset_ebgp coord peer1 establish AS $PEER2_AS holdtime 0 id 192.150.187.100 sleep 2 PACKET="packet update origin 2 aspath 65009 nexthop 192.150.187.2 nlri 10.10.10.0/24 nlri 20.20.20.20/24" coord peer1 send $PACKET sleep 2 coord peer1 send $PACKET sleep 2 coord peer1 assert established } test4() { if [ ${RIB_CONFIGURED:-""} == "" ] then return fi echo "TEST4 - Exercise the next hop resolver" reset_ebgp coord peer1 establish AS $PEER2_AS holdtime 0 id 192.150.187.100 sleep 2 PACKET1="packet update origin 2 aspath 65009 nexthop 128.16.0.1 nlri 10.10.10.0/24 nlri 20.20.20.20/24" PACKET2="packet update origin 2 aspath 65009 nexthop 128.16.1.1 nlri 30.30.30.30/24 nlri 40.40.40.40/24" # XXX This next hop should not resolve. coord peer1 send $PACKET1 coord peer1 send $PACKET2 sleep 2 # Lets get it to resolve. add_route4 connected true false 128.16.0.0/16 172.16.1.2 1 # Add a different route. add_route4 connected true false 128.16.0.0/24 172.16.2.2 1 # Delete the better route. delete_route4 connected true false 128.16.0.0/24 sleep 2 coord peer1 assert established } test5() { if [ ${RIB_CONFIGURED:-""} == "" ] then return fi echo "TEST5 - Exercise the next hop resolver" reset_ebgp coord peer1 establish AS $PEER2_AS holdtime 0 id 192.150.187.100 sleep 2 PACKET1="packet update origin 2 aspath 65009 nexthop 128.16.0.1 nlri 10.10.10.0/24 nlri 20.20.20.20/24" PACKET2="packet update origin 2 aspath 65009 nexthop 128.16.1.1 nlri 30.30.30.30/24 nlri 40.40.40.40/24" # Lets get it to resolve. add_route4 connected true false 128.16.0.0/16 172.16.1.2 1 # Add a different route. add_route4 connected true false 128.16.0.0/24 172.16.2.2 1 coord peer1 send $PACKET1 coord peer1 send $PACKET2 sleep 2 coord peer1 assert established } test6() { ITER=5 echo "TEST6 - Send an update packet on an EBGP $ITER times" PACKET='packet update origin 1 aspath 65009 nexthop 20.20.20.20 nlri 10.10.10.0/24 nlri 20.20.20.20/24' coord reset coord target $HOST $PORT2 coord initialise attach peer1 # Allow the initialization to take place sleep 1 coord peer1 establish AS $PEER2_AS holdtime 0 id 192.150.187.100 sleep 2 coord peer1 assert established set +e let i=0 while ((i++ < $ITER)) do # Using the (()) syntax seems to have invoked a new shell. # Any errors have to be explicitly propagated. trap 'exit $?' 0 2 set -e echo "Iteration: $i" coord peer1 send $PACKET coord peer1 trie sent lookup 10.10.10.0/24 aspath 65009 coord peer1 send packet update withdraw 10.10.10.0/24 coord peer1 send packet update withdraw 20.20.20.0/24 coord peer1 assert established # Every IPC call uses up more ports. Stop sending IPC's when # there are too few ports left. ports=$(netstat | wc -l) if (($ports > 900)) then echo "$ports sockets in use bailing" break fi done set -e sleep 2 coord peer1 assert established } test7() { if [ ${RIB_CONFIGURED:-""} == "" ] then return fi echo "TEST7 - Try and force BGP to deregister interest from the RIB" reset_ebgp coord peer1 establish AS $PEER2_AS holdtime 0 id 192.150.187.100 PACKET_ADD1="packet update origin 2 aspath 65009 nexthop 128.16.0.1 nlri 10.10.10.0/24" PACKET_ADD2="packet update origin 2 aspath 65009 nexthop 128.16.1.1 nlri 20.20.20.20/24" PACKET_WITHDRAW="packet update withdraw 10.10.10.0/24 withdraw 20.20.20.20/24" # Lets get it to resolve. add_route4 connected true false 128.16.0.0/16 172.16.1.2 1 sleep 2 # Add a different route. add_route4 connected true false 128.16.0.0/24 172.16.2.2 1 sleep 2 coord peer1 send $PACKET_ADD1 coord peer1 send $PACKET_ADD2 sleep 2 # delete the routes delete_route4 connected true false 128.16.0.0/24 delete_route4 connected true false 128.16.0.0/16 coord peer1 send $PACKET_WITHDRAW sleep 2 coord peer1 assert established } test8() { if [ ${RIB_CONFIGURED:-""} == "" ] then return fi echo "TEST8 - Exercise the next hop resolver" reset_ebgp coord peer1 establish AS $PEER2_AS holdtime 0 id 192.150.187.100 sleep 2 PACKET1="packet update origin 2 aspath 65009 nexthop 128.16.0.1 nlri 10.10.10.0/24 nlri 20.20.20.20/24" PACKET2="packet update origin 2 aspath 65009 nexthop 128.16.1.1 nlri 30.30.30.30/24 nlri 40.40.40.40/24" # XXX This next hop should not resolve. coord peer1 send $PACKET1 coord peer1 send $PACKET2 sleep 2 # Lets get it to resolve. add_route4 connected true false 128.16.0.0/16 172.16.1.2 1 # Add a different route. add_route4 connected true false 128.16.0.0/24 172.16.2.2 2 # Delete the better route. delete_route4 connected true false 128.16.0.0/24 sleep 2 coord peer1 assert established } test9() { if [ ${RIB_CONFIGURED:-""} == "" ] then return fi echo "TEST9 - Check nexthop change on originated route" reset_ebgp #we won't use this peering, but at least it checks that BGP is #still running coord peer1 establish AS $PEER2_AS holdtime 0 id 192.150.187.100 sleep 2 originate_route4 10.10.10.0/24 10.69.1.1 true false # Lets get it to resolve. add_route4 connected true false 10.69.1.0/24 172.16.1.2 1 # Delete the route. delete_route4 connected true false 10.69.1.0/24 sleep 2 coord peer1 assert established } test10() { if [ ${RIB_CONFIGURED:-""} == "" ] then return fi echo "TEST10 - Nexthop resolver crashes if queuing a 0.0.0.0/0 nlri" reset_ebgp coord peer1 establish AS $PEER2_AS holdtime 0 id 192.150.187.100 sleep 2 PACKET1="packet update origin 2 aspath 65009 nexthop 128.16.0.1 nlri 0.0.0.0/0 nlri 20.20.20.20/24" # Lets get it to resolve. add_route4 connected true false 128.16.0.0/16 172.16.1.2 1 coord peer1 send $PACKET1 sleep 2 coord peer1 assert established } TESTS_NOT_FIXED='' TESTS='test1 test1_ipv6 test2 test3 test4 test5 test6 test7 test8 test9 test10' # Include command line . ${srcdir}/args.sh . ./setup_paths.sh if [ $START_PROGRAMS = "yes" ] then CXRL="$CALLXRL -r 10" runit $QUIET $VERBOSE -c "$0 -s -c $*" <> sys.stderr, us % sys.argv[0] try: opts, args = getopt.getopt(sys.argv[1:], "hp:t:a:r:", \ ["help", \ "peer=", \ "trie=", \ "add=", \ "remove=", \ ]) except getopt.GetoptError: usage() sys.exit(1) peer = "" trie = "" add = "" remove = "" for o, a in opts: if o in ("-h", "-help"): usage() sys.exit() if o in ("-p", "--peer"): peer = a if o in ("-t", "--trie"): trie = a if o in ("-a", "--add"): add = a if o in ("-r", "--remove"): remove = a if not peer and not trie: usage() sys.exit(1) lookup(peer, trie, add, remove) if __name__ == '__main__': main() xorp/bgp/harness/SConscript0000664000076400007640000001154211631507103016105 0ustar greearbgreearb# Copyright (c) 2009-2011 XORP, Inc and Others # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # $XORP$ import os Import('env') env = env.Clone() env.AppendUnique(CPPPATH = [ '#', '$BUILDDIR', ]) env.AppendUnique(LIBPATH = [ '$BUILDDIR/libxorp', '$BUILDDIR/libcomm', '$BUILDDIR/libxipc', '$BUILDDIR/libfeaclient', '$BUILDDIR/xrl/interfaces', '$BUILDDIR/xrl/targets', '$BUILDDIR/policy/backend', '$BUILDDIR/policy/common', '$BUILDDIR/bgp', '$BUILDDIR/fea', '.', ]) env.AppendUnique(LIBS = [ 'xif_bgp', 'xorp_ipc', 'xorp_core', 'xorp_comm', 'xst_test_peer', 'xorp_bgp', 'xif_test_peer', 'xst_coord', 'xif_coord', 'xif_rib', 'xif_datain', 'xorp_fea_client', 'xorp_policy_backend', 'xorp_policy_common', 'xif_fea_ifmgr_mirror', 'xif_fea_ifmgr_replicator', 'xst_fea_ifmgr_mirror', 'xst_bgp', 'xif_finder_event_notifier', ]) if not (env.has_key('disable_profile') and env['disable_profile']): env.AppendUnique(LIBS = [ 'xif_profile_client', ]) is_shared = env.has_key('SHAREDLIBS') if not is_shared: env.AppendUnique(LIBS = [ "crypto", ]) if not (env.has_key('mingw') and env['mingw']): env.AppendUnique(LIBS = [ "rt", ]) if (env.has_key('mingw') and env['mingw']): env.AppendUnique(LIBS = [ 'ws2_32', 'iphlpapi', # 'mprapi', 'regex', 'winmm', ]) # This is really needed, belive it or not. env.Append(LIBS = [ 'xorp_core', ]); coord_srcs = [ 'bgppp.cc', 'command.cc', 'coord.cc', 'peer.cc', 'trie.cc', ] test_peer_srcs = [ 'test_peer.cc', 'bgppp.cc', ] test_trie_srcs = [ 'test_peer.cc', 'bgppp.cc', ] script_srcs = [ 'args.sh', 'test_path_attribute1.sh', 'test_rib1.sh', 'test_routing2.sh', 'inject.sh', 'test_path_attribute2.sh', 'test_rib_fea1.sh', 'test_terminate.sh', 'notification_codes.sh', 'test_path_attribute3.sh', 'test_route_flap_damping1.sh', 'xrl_shell_funcs.sh', 'setup_paths.sh', 'soak.sh', 'test_busted', 'test_peering1.sh', 'test_route_reflection1.sh', 'test1.sh', 'test_peering2.sh', 'test_route_reflection2.sh', 'test2.sh', 'test_peering3.sh', 'test_routing1.sh', 'harness.py', 'lookup.py', 'NOTES', 'originate_routes.pl', ] coord = env.Program(target = 'coord', source = coord_srcs) test_peer = env.Program(target = 'test_peer', source = test_peer_srcs) test_trie = env.Program(target = 'test_trie', source = test_trie_srcs) harnesspath = '$exec_prefix/bgp/harness' if env['enable_tests']: env.Alias('install', env.InstallProgram(harnesspath, coord)) env.Alias('install', env.InstallProgram(harnesspath, test_peer)) env.Alias('install', env.InstallProgram(harnesspath, test_trie)) for ss in script_srcs: env.Alias('install', env.InstallProgram(harnesspath, env.Entry('%s' % ss))) if 'check' in COMMAND_LINE_TARGETS: from subprocess import call for ss in script_srcs: env.Alias('check', env.Execute(Copy('%s' % ss, '../../../../bgp/harness/%s' %ss))) # Tests to run automatically with 'scons test' # This 'Execute' recipe doesn't work right, because if a program fails, # then scons will exit. Use 'call' instead to force them to be run. #env.Alias('check', env.Execute(env.Action('./test_peering1.sh'))) call("./test_busted") call("./test_peering1.sh") call("./test_peering2.sh") call('./test_peering3.sh') call('./test_routing1.sh') call('./test_routing2.sh') call('./test_rib1.sh') call('./test_rib_fea1.sh') call('./test_path_attribute1.sh') call('./test_path_attribute2.sh') call('./test_path_attribute3.sh') call('./test_route_reflection1.sh') call('./test_route_reflection2.sh') call('./test_route_flap_damping1.sh') call('./test_terminate.sh') #for t in tests: # test_targets.append(env.AutoTest(target = 'test_%s' % t, # source = 'test_%s.cc' % t)) Default(coord, test_peer, test_trie) xorp/bgp/harness/test_busted0000775000076400007640000000260011421137511016340 0ustar greearbgreearb#!/usr/bin/perl # These scripts fail..but normal harness doesn't deal well with # failing scripts..so run them here. use strict; # These commands are bad to hang and otherwise mis-behave. # So, fork before calling them..and kill them if they don't # die in time. my $timeout = 120; sub REAPER { $SIG{CHLD} = \&REAPER; wait; } $SIG{CHLD} = \&REAPER; sub fork_sys { my $cmd = shift; my $timeout = shift; my $pid = fork(); if ($pid == 0) { # child system($cmd); exit(0); } elsif ($pid > 0) { sleep(10); # Let child get started # Parent. my $i; for ($i = 0; $i<$timeout; $i++) { if (-f "/proc/$pid/cmdline") { sleep(1); } else { print "Seems child has exited properly.\n"; last; } } # There should be no test_peer processes running now, kill them if they # exist. print "Killing any existing test_peer processes, child pid: $pid"; print `ps auxwww|grep test_peer`; print "\n"; system("killall -s 9 -r runit"); system("killall -s 9 -r test_peer"); system("killall -s 9 -r runit"); system("killall -s 9 -r test_peer"); } else { print "ERROR: fork failed in test_busted script!\n"; } } fork_sys("./test_peering1.sh -l -t test26", $timeout); fork_sys("./test_peering1.sh -l -t test28_ipv6", $timeout); fork_sys("./test_peering1.sh -l -t test28_ipv6_ok", $timeout); exit(0); xorp/bgp/harness/test1.sh0000775000076400007640000000703511421137511015473 0ustar greearbgreearb#!/bin/sh # # $XORP: xorp/bgp/harness/test1.sh,v 1.1.1.1 2002/12/11 23:55:51 hodson Exp $ # # # Simple test # A bgp process is tested using three peers. # # Peers 1 and 2 are E-BGP Peer 3 is I-BGP # # 1) Add a route (A) via peer1 # 2) Verify that route (A) appears at peer3 # 3) Add a better route (B) via peer2 # 4) Verify that route (B) appears at peer3 # 5) Withdraw route (b) via peer2 # 6) Verify that route (A) appears at peer3 # set -e #set -x . ../xrl_shell_funcs.sh . ./xrl_shell_funcs.sh PORT=10000 configure_bgp() { LOCALHOST=tigger.icir.org AS=65008 USE4BYTEAS=false ID=192.150.187.78 HOLDTIME=60 local_config $AS $ID $USE4BYTEAS register_rib rib PEER=xorp-c4000.icir.org PEER_AS=65000 PEER_PORT=179 NEXT_HOP=192.150.187.78 add_peer $LOCALHOST $PORT $PEER $PEER_PORT $PEER_AS $NEXT_HOP $HOLDTIME enable_peer $LOCALHOST $PORT $PEER $PEER_PORT $PEER_AS PEER=xorp0 PEER_AS=60001 PEER_PORT=10000 NEXT_HOP=192.150.187.100 add_peer $LOCALHOST $PORT $PEER $PEER_PORT $PEER_AS $NEXT_HOP $HOLDTIME enable_peer $LOCALHOST $PORT $PEER $PEER_PORT $PEER_AS PEER=xorp6 PEER_AS=60002 PEER_PORT=10000 NEXT_HOP=192.150.187.106 add_peer $LOCALHOST $PORT $PEER $PEER_PORT $PEER_AS $NEXT_HOP $HOLDTIME enable_peer $LOCALHOST $PORT $PEER $PEER_PORT $PEER_AS PEER=xorp7 PEER_AS=65008 PEER_PORT=10000 NEXT_HOP=192.150.187.107 add_peer $LOCALHOST $PORT $PEER $PEER_PORT $PEER_AS $NEXT_HOP $HOLDTIME enable_peer $LOCALHOST $PORT $PEER $PEER_PORT $PEER_AS } set +e configure_bgp set -e #BASE=peer1 disconnect #BASE=peer1 register coord #BASE=peer1 connect tigger.icir.org 10000 # Send at least 19 bytes. #BASE=peer1 send "1234567890123456789" #BASE=peer2 disconnect #BASE=peer2 connect tigger.icir.org 10000 #BASE=peer3 disconnect #BASE=peer3 connect tigger.icir.org 10000 # # Reset the coordinator. # coord reset # # Select the BGP process which is to be tested # coord target tigger.icir.org $PORT # # Attach to the test peers. # coord initialise attach peer1 coord initialise attach peer2 coord initialise attach peer3 # # Establish a bgp session with the BGP process # coord peer1 establish AS 60001 holdtime 0 id 192.150.187.100 coord peer2 establish AS 60002 holdtime 0 id 192.150.187.106 coord peer3 establish AS 65008 holdtime 0 id 192.150.187.107 # Introduce an update message sleep 2 coord peer1 send packet update \ origin 2 \ aspath "1,2,(3,4,5),6,(7,8),9" \ nexthop 10.10.10.10 \ nlri 10.10.10.0/24 \ nlri 20.20.20.20/24 sleep 2 coord peer1 trie sent lookup 10.10.10.0/24 aspath "1,2,(3,4,5),6,(7,8),9" coord peer2 trie recv lookup 10.10.10.0/24 aspath "65008,1,2,(3,4,5),6,(7,8),9" coord peer3 trie recv lookup 10.10.10.0/24 aspath "1,2,(3,4,5),6,(7,8),9" sleep 2 coord peer2 send packet update \ origin 2 \ aspath "1" \ nexthop 20.20.20.20 \ nlri 10.10.10.10/24 \ nlri 20.20.20.20/24 sleep 2 coord peer1 trie recv lookup 10.10.10.0/24 aspath "65008,1" coord peer2 trie sent lookup 10.10.10.0/24 aspath "1" coord peer3 trie recv lookup 10.10.10.0/24 aspath "1" sleep 2 coord peer2 send packet update \ origin 2 \ aspath "1" \ nexthop 20.20.20.20 \ withdraw 10.10.10.10/24 \ withdraw 20.20.20.20/24 sleep 2 coord peer1 trie sent lookup 10.10.10.0/24 aspath "1,2,(3,4,5),6,(7,8),9" coord peer2 trie recv lookup 10.10.10.0/24 aspath "65008,1,2,(3,4,5),6,(7,8),9" coord peer3 trie recv lookup 10.10.10.0/24 aspath "1,2,(3,4,5),6,(7,8),9" # Local Variables: # mode: shell-script # sh-indentation: 4 # End: xorp/bgp/harness/trie_payload.hh0000664000076400007640000000726311421137511017074 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/bgp/harness/trie_payload.hh,v 1.15 2008/11/08 06:14:45 mjh Exp $ #ifndef __BGP_HARNESS_TRIE_PAYLOAD_HH__ #define __BGP_HARNESS_TRIE_PAYLOAD_HH__ /** * A BGP update packet can have many NLRIs. Each NLRI is stored in a * trie node. Rather than keep multiple copies of a BGP update * packet. A single reference counted copy is kept in TrieData. A * TriePayload is stored in the trie and holds a pointer to the TrieData. */ class TrieData { public: TrieData(const TimeVal& tv, const uint8_t *buf, size_t len, const BGPPeerData *peerdata, TrieData* &first, TrieData* &last) : _tv(tv), _first(first), _last(last) { _packet = new UpdatePacket(buf, len, peerdata, 0, false); _refcnt = 1; _next = 0; _prev = _last; if(0 == _first) _first = this; if(0 != _last) _last->_next = this; _last = this; } void incr_refcnt() { _refcnt++; } bool decr_refcnt() { _refcnt--; XLOG_ASSERT(_refcnt >= 0); return 0 == _refcnt; } const UpdatePacket *data() const { XLOG_ASSERT(_refcnt > 0); return _packet; } const TimeVal& tv() const { return _tv; } const TrieData* next() const { return _next; } ~TrieData() { XLOG_ASSERT(0 == _refcnt); if(this == _first) _first = _next; else _prev->_next = _next; if(this == _last) _last = _prev; if(0 != _next) { _next->_prev = _prev; } // debug_msg("Deleting: %s\n", _packet->str().c_str()); delete _packet; } private: TimeVal _tv; TrieData* &_first; TrieData* &_last; int _refcnt; UpdatePacket *_packet; TrieData *_next; TrieData *_prev; }; /** * The payload of a RealTrie. */ class TriePayload { public: TriePayload() : _data(0) {} TriePayload(const TimeVal& tv, const uint8_t *buf, size_t len, const BGPPeerData *peerdata, TrieData* &first, TrieData* &last) { _data = new TrieData(tv, buf, len, peerdata, first, last); } ~TriePayload() { zap(); } TriePayload(const TriePayload& rhs) { _data = 0; copy(rhs); } TriePayload& operator=(const TriePayload& rhs) { if(&rhs == this) return *this; zap(); copy(rhs); return *this; } void copy(const TriePayload& rhs) { if(rhs._data) { // debug_msg("refcnt: %d %#x\n", rhs._data->_refcnt + 1, rhs._data); rhs._data->incr_refcnt(); _data = rhs._data; } } const UpdatePacket *get() const { if(0 == _data) return 0; return _data->data(); } const UpdatePacket *get(TimeVal& tv) const { if(0 == _data) return 0; tv = _data->tv(); return _data->data(); } void zap() { // if(_data) // debug_msg("refcnt: %d %#x\n", _data->_refcnt - 1, _data); if(_data && _data->decr_refcnt()) { delete _data; } _data = 0; } private: TrieData *_data; }; #endif // __BGP_HARNESS_TRIE_PAYLOAD_HH__ xorp/bgp/harness/trie.cc0000664000076400007640000001763511421137511015355 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #define PARANOIA #include "bgp/bgp_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "trie.hh" const UpdatePacket * Trie::lookup(const string& net) const { IPvXNet n(net.c_str()); if(n.is_ipv4()) return lookup(n.get_ipv4net()); else if(n.is_ipv6()) return lookup(n.get_ipv6net()); else XLOG_FATAL("Unknown family: %s", n.str().c_str()); return 0; } const UpdatePacket * Trie::lookup(const IPv4Net& n) const { TriePayload payload = _head_ipv4.find(n); const UpdatePacket *update = payload.get(); if(0 == update) return 0; /* ** Everything below here is sanity checking. */ /* ** Look for this net in the matching update packet. */ list ::const_iterator ni; ni = update->nlri_list().begin(); for(; ni != update->nlri_list().end(); ni++) if(ni->net() == n) return update; XLOG_FATAL("If we found the packet in the trie" " it should contain the nlri %s %s", n.str().c_str(), update->str().c_str()); return 0; } const UpdatePacket * Trie::lookup(const IPv6Net& n) const { TriePayload payload = _head_ipv6.find(n); const UpdatePacket *update = payload.get(); if(0 == update) return 0; /* ** Everything below here is sanity checking. */ /* ** Look for a multiprotocol path attribute. */ const MPReachNLRIAttribute *mpreach = 0; #if 0 list ::const_iterator pai; for (pai = update->pa_list().begin(); pai != update->pa_list().end(); pai++) { const PathAttribute* pa; pa = *pai; if (dynamic_cast*>(*pai)) { mpreach = dynamic_cast*>(*pai); break; } } #endif mpreach = update->mpreach(SAFI_UNICAST); if (mpreach == 0) mpreach = update->mpreach(SAFI_MULTICAST); if(0 == mpreach) XLOG_FATAL("If we found the packet in the trie" " it should contain the nlri %s %s", n.str().c_str(), update->str().c_str()); /* ** Look for this net in the matching update packet. */ list::const_iterator ni; ni = mpreach->nlri_list().begin(); for(; ni != mpreach->nlri_list().end(); ni++) if(*ni == n) return update; XLOG_FATAL("If we found the packet in the trie" " it should contain the nlri %s %s", n.str().c_str(), update->str().c_str()); return 0; } void Trie::process_update_packet(const TimeVal& tv, const uint8_t *buf, size_t len, const BGPPeerData *peerdata) { _update_cnt++; TriePayload payload(tv, buf, len, peerdata, _first, _last); const UpdatePacket *p = payload.get(); debug_msg("process update packet:\n%s\n", p->str().c_str()); const MPReachNLRIAttribute *mpreach = 0; const MPUNReachNLRIAttribute *mpunreach = 0; #if 0 list ::const_iterator pai; for (pai = p->pa_list().begin(); pai != p->pa_list().end(); pai++) { const PathAttribute* pa; pa = *pai; if (dynamic_cast*>(*pai)) { mpreach = dynamic_cast*>(*pai); continue; } if (dynamic_cast*>(*pai)) { mpunreach = dynamic_cast*>(*pai); continue; } } #endif mpreach = p->mpreach(SAFI_UNICAST); if (!mpreach) mpreach = p->mpreach(SAFI_MULTICAST); mpunreach = p->mpunreach(SAFI_UNICAST); if (!mpunreach) mpunreach = p->mpunreach(SAFI_MULTICAST); /* ** IPv4 Withdraws */ if (!p->wr_list().empty()) { BGPUpdateAttribList::const_iterator wi; for(wi = p->wr_list().begin(); wi != p->wr_list().end(); wi++) del(wi->net(), payload); } /* ** IPv6 Withdraws */ if (mpunreach) { list >::const_iterator wi6; for(wi6 = mpunreach->wr_list().begin(); wi6 != mpunreach->wr_list().end(); wi6++) { del(*wi6, payload); } } /* ** IPv4 Route add. */ if (!p->nlri_list().empty()) { BGPUpdateAttribList::const_iterator ni4; for(ni4 = p->nlri_list().begin(); ni4 != p->nlri_list().end(); ni4++) add(ni4->net(), payload); } /* ** IPv6 Route add. */ if (mpreach) { list >::const_iterator ni6; for(ni6 = mpreach->nlri_list().begin(); ni6 != mpreach->nlri_list().end(); ni6++) add(*ni6, payload); } } void Trie::tree_walk_table(const TreeWalker_ipv4& tw) const { _head_ipv4.tree_walk_table(tw); } void Trie::tree_walk_table(const TreeWalker_ipv6& tw) const { _head_ipv6.tree_walk_table(tw); } void Trie::replay_walk(const ReplayWalker uw, const BGPPeerData *peerdata) const { /* ** It should be sufficient to just walk the list of stored ** packets. However it is possible that we have saved withdraws ** that in effect will be a noop. So insert the packets into a ** local trie, only if the packet changes the local trie do we ** propogate it back to the caller. */ Trie trie; trie.set_warning(false); uint32_t changes = 0; for(const TrieData *p = _first; p; p = p->next()) { changes = trie.changes(); UpdatePacket *packet = const_cast(p->data()); uint8_t data[BGPPacket::MAXPACKETSIZE]; size_t len = BGPPacket::MAXPACKETSIZE; debug_msg("Trie::replay_walk\n"); packet->encode(data, len, peerdata); trie.process_update_packet(p->tv(), data, len, peerdata); if(trie.changes() != changes) uw->dispatch(p->data(), p->tv()); } } template <> void Trie::get_heads(RealTrie*& head, RealTrie*& del) { head = &_head_ipv4; del = &_head_ipv4_del; } template <> void Trie::get_heads(RealTrie*& head, RealTrie*& del) { head = &_head_ipv6; del = &_head_ipv6_del; } template void Trie::add(IPNet net, TriePayload& payload) { debug_msg("%s %s\n", net.str().c_str(), payload.get()->str().c_str()); _changes++; RealTrie *head, *del; get_heads(head, del); /* If a previous entry exists remove it */ const UpdatePacket *up = lookup(net); if(up) if(!head->del(net)) XLOG_FATAL("Could not remove nlri: %s", net.str().c_str()); if(!head->insert(net, payload)) XLOG_FATAL("Could not add nlri: %s", net.str().c_str()); /* ** We save deleted state for replays. */ up = del->find(net).get(); if(up) if(!del->del(net)) XLOG_FATAL("Could not remove nlri: %s", net.str().c_str()); } template void Trie::del(IPNet net, TriePayload& payload) { RealTrie *head, *del; get_heads(head, del); if(!head->del(net)) { if(_warning) XLOG_WARNING("Could not delete %s", net.str().c_str()); } else { _changes++; } /* ** We save deleted state for replays. */ /* If a previous entry exists remove it */ const UpdatePacket *up = del->find(net).get(); if(up) if(!del->del(net)) XLOG_FATAL("Could not remove nlri: %s", net.str().c_str()); if(!del->insert(net, payload)) XLOG_FATAL("Could not add nlri: %s", net.str().c_str()); } xorp/bgp/harness/coord.hh0000664000076400007640000000617111421137511015523 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/bgp/harness/coord.hh,v 1.14 2008/10/02 21:56:26 bms Exp $ #ifndef __BGP_HARNESS_COORD_HH__ #define __BGP_HARNESS_COORD_HH__ #include "xrl/targets/coord_base.hh" #include "command.hh" class Coord { public: Coord(EventLoop& eventloop, Command& command); void command(const string& command); void status(const string& peer, string& status); bool pending(); void datain(const string& peer, const uint32_t& genid, const bool& status, const uint32_t& secs, const uint32_t& micro, const vector& data); void datain_error(const string& peer, const uint32_t& genid, const string& reason); void datain_closed(const string& peer, const uint32_t& genid); bool done(); void mark_done(); private: bool _done; EventLoop& _eventloop; Command& _command; }; class XrlCoordTarget : XrlCoordTargetBase { public: XrlCoordTarget(XrlRouter *r, Coord& coord); /** * Get name of Xrl Target */ XrlCmdError common_0_1_get_target_name( // Output values, string& name); /** * Get version string from Xrl Target */ XrlCmdError common_0_1_get_version( // Output values, string& version); /** * Get status from Xrl Target */ XrlCmdError common_0_1_get_status( // Output values, uint32_t& status, string& reason); XrlCmdError common_0_1_startup() { return XrlCmdError::OKAY(); } /** * shutdown target */ XrlCmdError common_0_1_shutdown(); XrlCmdError coord_0_1_command( // Input values, const string& command); XrlCmdError coord_0_1_status( // Input values, const string& peer, // Output values, string& status); XrlCmdError coord_0_1_pending( // Output values, bool& pending); XrlCmdError datain_0_1_receive( // Input values, const string& peer, const uint32_t& genid, const bool& status, const uint32_t& secs, const uint32_t& micro, const vector& data); XrlCmdError datain_0_1_error( // Input values, const string& peer, const uint32_t& genid, const string& reason); XrlCmdError datain_0_1_closed( // Input values, const string& peer, const uint32_t& genid); private: Coord& _coord; int _incommand; }; #endif // __BGP_HARNESS_COORD_HH__ xorp/bgp/harness/coord.cc0000664000076400007640000001463411421137511015514 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "bgp/bgp_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/status_codes.h" #include "libxorp/eventloop.hh" #ifdef HAVE_GETOPT_H #include #endif #include "libxipc/xrl_std_router.hh" #include "bgp/socket.hh" #include "coord.hh" #include "command.hh" static const char SERVER[] = "coord";/* This servers name */ static const char SERVER_VERSION[] = "0.1"; /* -------------------- XRL -------------------- */ XrlCoordTarget::XrlCoordTarget(XrlRouter *r, Coord& coord) : XrlCoordTargetBase(r), _coord(coord), _incommand(0) { debug_msg("\n"); } XrlCmdError XrlCoordTarget::common_0_1_get_target_name(string& name) { debug_msg("\n"); name = SERVER; return XrlCmdError::OKAY(); } XrlCmdError XrlCoordTarget::common_0_1_get_version(string& version) { debug_msg("\n"); version = SERVER_VERSION; return XrlCmdError::OKAY(); } XrlCmdError XrlCoordTarget::common_0_1_get_status(// Output values, uint32_t& status, string& reason) { //XXX placeholder only status = PROC_READY; reason = "Ready"; return XrlCmdError::OKAY(); } XrlCmdError XrlCoordTarget::common_0_1_shutdown() { _coord.mark_done(); return XrlCmdError::OKAY(); } XrlCmdError XrlCoordTarget::coord_0_1_command(const string& command) { debug_msg("command: <%s>\n", command.c_str()); _incommand++; try { _coord.command(command); } catch(const XorpException& e) { _incommand--; return XrlCmdError::COMMAND_FAILED(e.why() + "\nPending operation: " + bool_c_str(_coord.pending())); } _incommand--; return XrlCmdError::OKAY(); } XrlCmdError XrlCoordTarget::coord_0_1_status(const string& peer, string& status) { debug_msg("status: %s\n", peer.c_str()); _incommand++; try { _coord.status(peer, status); } catch(const XorpException& e) { _incommand--; return XrlCmdError::COMMAND_FAILED(e.why() + "\nPending operation: " + bool_c_str(_coord.pending())); } _incommand--; return XrlCmdError::OKAY(); } XrlCmdError XrlCoordTarget::coord_0_1_pending(bool& pending) { debug_msg("pending:\n"); if(0 != _incommand) pending = true; else pending = _coord.pending(); return XrlCmdError::OKAY(); } XrlCmdError XrlCoordTarget::datain_0_1_receive(const string& peer, const uint32_t& genid, const bool& status, const uint32_t& secs, const uint32_t& micro, const vector& data) { debug_msg("peer: %s genid: %u status: %d secs: %u micro: %u data length: %u\n", peer.c_str(), XORP_UINT_CAST(genid), status, XORP_UINT_CAST(secs), XORP_UINT_CAST(micro), XORP_UINT_CAST(data.size())); _coord.datain(peer, genid, status, secs, micro, data); return XrlCmdError::OKAY(); } XrlCmdError XrlCoordTarget::datain_0_1_error(const string& peer, const uint32_t& genid, const string& reason) { debug_msg("peer: %s genid: %u reason: %s\n", peer.c_str(), XORP_UINT_CAST(genid), reason.c_str()); _coord.datain_error(peer, genid, reason); return XrlCmdError::OKAY(); } XrlCmdError XrlCoordTarget::datain_0_1_closed(const string& peer, const uint32_t& genid) { debug_msg("peer: %s\n", peer.c_str()); _coord.datain_closed(peer, genid); return XrlCmdError::OKAY(); } /* -------------------- IMPLEMENTATION -------------------- */ Coord::Coord(EventLoop& eventloop, Command& command) : _done(false), _eventloop(eventloop), _command(command) { } void Coord::command(const string& command) { _command.command(command); } void Coord::status(const string& peer, string& status) { _command.status(peer, status); } bool Coord::pending() { return _command.pending(); } void Coord::datain(const string& peer, const uint32_t& genid, const bool& status, const uint32_t& secs, const uint32_t& micro, const vector& data) { TimeVal tv(secs, micro); _command.datain(peer, genid, status, tv, data); } void Coord::datain_error(const string& peer, const uint32_t& genid, const string& reason) { _command.datain_error(peer, genid, reason); } void Coord::datain_closed(const string& peer, const uint32_t& genid) { _command.datain_closed(peer, genid); } bool Coord::done() { return _done; } void Coord::mark_done() { _done = true; } static void usage(const char *name) { fprintf(stderr,"usage: %s [-h (finder host)]\n", name); exit(-1); } int main(int argc, char **argv) { XorpUnexpectedHandler x(xorp_unexpected_handler); // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); int c; string finder_host = FinderConstants::FINDER_DEFAULT_HOST().str(); const char *server = SERVER; while((c = getopt (argc, argv, "h:")) != EOF) { switch(c) { case 'h': finder_host = optarg; break; case '?': usage(argv[0]); } } try { EventLoop eventloop; XrlStdRouter router(eventloop, server, finder_host.c_str()); Command com(eventloop, router); Coord coord(eventloop, com); XrlCoordTarget xrl_target(&router, coord); wait_until_xrl_router_is_ready(eventloop, router); while (coord.done() == false) { eventloop.run(); } } catch(...) { xorp_catch_standard_exceptions(); } // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); debug_msg("Bye!\n"); return 0; } xorp/bgp/harness/inject.sh0000775000076400007640000001172211421137511015705 0ustar greearbgreearb#!/usr/bin/env bash # # $XORP: xorp/bgp/harness/inject.sh,v 1.3 2006/11/03 23:32:24 pavlin Exp $ # # Inject a saved conversation in mrtd format to our BGP process using # the BGP harness code. Note that the INJECT_PEER_AS should match the AS # of the peer that the data originally came from. # Preconditons # 1) Run "xorp/libxipc/xorp_finder" # 2) Run "xorp/fea/xorp_fea" or "xorp/fea/xorp_fea_dummy" # 3) Run "xorp/rib/xorp_rib" # 4) Run "xorp/bgp/xorp_bgp" # 5) Run "xorp/bgp/harness/test_peer -s peer1 -v" # 6) Run "xorp/bgp/harness/test_peer -s peer2 -v" # 7) Run "xorp/bgp/harness/coord" set -e . ./setup_paths.sh # Configure an interface and re-write all next hops to go through this # interface VIF0="dc0" ADDR0="10.1.0.1" ADDR_PREFIX0="25" NEXT_HOP_REWRITE1="10.1.0.2" NEXT_HOP_REWRITE2="10.1.0.3" LOCALHOST=$(hostname) if host $LOCALHOST then MY_IP=$(host $LOCALHOST | cut -f 4 -d ' '|head -1) else LOCALHOST=localhost MY_IP=127.0.0.1 fi echo "MY_IP: $MY_IP" MY_AS=65008 MY_ID=$MY_IP MY_NAME=$LOCALHOST MY_PORT=9000 NEXT_HOP=$MY_IP INJECT_PEER_AS=65000 INJECT_PEER_PORT=9002 MY_INJECT_PORT=9005 TEST_PEER_AS=65001 TEST_PEER_PORT=9001 TREETOP=${PWD}/../../../xorp DUMPNAME=${DUMPNAME:-$TREETOP/../data/bgp/icsi1.mrtd} if [ ! -f $DUMPNAME ] then DUMPNAME=/root/icsi1.mrtd if [ ! -f $DUMPNAME ] then echo "ERROR: Cannot find iscsi1.mrtd file." exit 1 fi fi # # Start all the required processes # start_processes() { (cd $TREETOP for i in lib/xorp/sbin/xorp_finder lib/xorp/sbin/xorp_fea_dummy lib/xorp/sbin/xorp_rib lib/xorp/sbin/xorp_bgp \ "bgp/harness/test_peer -s peer1 -v" \ "bgp/harness/test_peer -s peer2 -v" \ bgp/harness/coord do xterm -e $i & sleep 2 done) } # # No variables below here should need changing. # fea() { echo "Configuring fea" local tid=$($FEA_FUNCS start_fea_transaction) $FEA_FUNCS create_interface $tid $VIF0 $FEA_FUNCS enable_interface $tid $VIF0 $FEA_FUNCS create_vif $tid $VIF0 $VIF0 $FEA_FUNCS enable_vif $tid $VIF0 $VIF0 $FEA_FUNCS create_address4 $tid $VIF0 $VIF0 $ADDR0 $FEA_FUNCS enable_address4 $tid $VIF0 $VIF0 $ADDR0 $FEA_FUNCS set_prefix4 $tid $VIF0 $VIF0 $ADDR0 $ADDR_PREFIX0 $FEA_FUNCS commit_fea_transaction $tid $FEA_FUNCS get_configured_vif_addresses4 $VIF0 $VIF0 } bgp() { PORT=$MY_PORT AS=$MY_AS ID=$MY_ID USE4BYTEAS=false HOLDTIME=0 echo "Configuring bgp, AS: $AS ID: $ID USE4BYTEAS: $USE4BYTEAS" $BGP_FUNCS local_config $AS $ID $USE4BYTEAS # register_rib "rib" # Test peer used for route injection MY_DEV="lo" PEER=$MY_IP PEER_AS=$INJECT_PEER_AS PEER_PORT=$INJECT_PEER_PORT IPTUPLE="$MY_IP $MY_INJECT_PORT $PEER $PEER_PORT" $BGP_FUNCS add_peer $MY_DEV $IPTUPLE $PEER_AS $NEXT_HOP $HOLDTIME # Rewrite the next hop onto our test net $BGP_FUNCS next_hop_rewrite_filter $IPTUPLE $NEXT_HOP_REWRITE1 $BGP_FUNCS enable_peer $IPTUPLE # Our test peer. PEER=$MY_IP PEER_AS=$TEST_PEER_AS PEER_PORT=$TEST_PEER_PORT IPTUPLE="$MY_IP $PORT $PEER $PEER_PORT" $BGP_FUNCS add_peer $MY_DEV $IPTUPLE $PEER_AS $NEXT_HOP $HOLDTIME # Rewrite the next hop onto our test net $BGP_FUNCS next_hop_rewrite_filter $IPTUPLE $NEXT_HOP_REWRITE2 $BGP_FUNCS enable_peer $IPTUPLE } bgp_enable() { echo "Enable peering to main router" PORT=$MY_PORT PEER=$BORDER_ROUTER_NAME PEER_AS=$BORDER_ROUTER_AS PEER_PORT=$BORDER_ROUTER_PORT $BGP_FUNCS enable_peer $LOCALHOST $PORT $PEER $PEER_PORT } bgp_disable() { echo "Disable peering to main router" PORT=$MY_PORT PEER=$BORDER_ROUTER_NAME PEER_AS=$BORDER_ROUTER_AS PEER_PORT=$BORDER_ROUTER_PORT $BGP_FUNCS disable_peer $LOCALHOST $PORT $PEER $PEER_PORT } reset() { echo "Resetting coordinator" TEST_FUNCS=$TREETOP/bgp/harness/xrl_shell_funcs.sh $TEST_FUNCS coord reset } test_peer() { echo "Configuring test peer" TEST_FUNCS=$TREETOP/bgp/harness/xrl_shell_funcs.sh PEER=$LOCALHOST PORT=$MY_PORT PEER_AS=$TEST_PEER_AS # $TEST_FUNCS coord reset $TEST_FUNCS coord target $PEER $PORT $TEST_FUNCS coord initialise attach peer1 $TEST_FUNCS coord peer1 establish AS $PEER_AS holdtime 0 id 192.150.187.100 } test_peer_inject() { echo "Send saved feed to Xorp BGP process $1" if [ ! -f $1 ] then echo "File $1 not found" return fi TEST_FUNCS=$TREETOP/bgp/harness/xrl_shell_funcs.sh PEER=$LOCALHOST PORT=$MY_INJECT_PORT PEER_AS=$INJECT_PEER_AS # $TEST_FUNCS coord reset $TEST_FUNCS coord target $PEER $PORT $TEST_FUNCS coord initialise attach peer2 $TEST_FUNCS coord peer2 establish AS $PEER_AS holdtime 0 id 192.150.187.100 $TEST_FUNCS coord peer2 send dump mrtd update $1 } # We have no arguments. if [ $# == 0 ] then start_processes fea bgp reset test_peer test_peer_inject $DUMPNAME else $* fi # Local Variables: # mode: shell-script # sh-indentation: 4 # End: xorp/bgp/harness/command.cc0000664000076400007640000002346511421137511016026 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #include "bgp/bgp_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/eventloop.hh" #include "libxorp/callback.hh" #include "libxipc/xrl_std_router.hh" #include "xrl/interfaces/test_peer_xif.hh" #include "coord.hh" #include "command.hh" Command::Command(EventLoop& eventloop, XrlStdRouter& xrlrouter) : _eventloop(eventloop), _xrlrouter(xrlrouter), _genid(0), _init_count(0) { load_command_map(); } void Command::load_command_map() { _commands.clear(); _commands.insert(StringCommandMap::value_type("reset", &Command::reset)); _commands.insert(StringCommandMap::value_type("target", &Command::target)); _commands.insert(StringCommandMap::value_type("initialise", &Command::initialise)); } void Command::command(const string& line) throw(InvalidString) { debug_msg("command: %s\n", line.c_str()); /* ** Split the line into words split on spaces. */ vector v; tokenize(line, v); if(v.empty()) xorp_throw(InvalidString, "Empty command"); StringCommandMap::iterator cur = _commands.find(v[0].c_str()); if(_commands.end() == cur) xorp_throw(InvalidString, c_format("Unknown command: %s", v[0].c_str())); cur->second.dispatch(this, line, v); } void Command::status(const string& peer, string& status) { debug_msg("status: %s\n", peer.c_str()); /* ** Are we in the peer table. */ PeerList::iterator cur = _peers.begin(); while(cur != _peers.end()) { switch(cur->is_this_you(peer)) { case Peer::YES_ITS_ME: cur->status(status); return; break; case Peer::NO_ITS_NOT_ME: break; case Peer::PLEASE_DELETE_ME: _peers.erase(cur++); continue; break; } ++cur; } } bool Command::pending() { debug_msg("pending\n"); if(_init_count != 0) { return true; } for(PeerList::iterator cur = _peers.begin(); cur != _peers.end(); ++cur){ if(cur->up() && cur->pending()) return true; } return false; } /* ** This is data that has been sent by the BGP process to the test_peer ** then to the coordinating process. Find which peer this data is ** destined for. */ void Command::datain(const string& peer, const uint32_t& genid, const bool& status, const TimeVal& tv, const vector& data) { debug_msg("peer: %s genid: %u status: %d secs: %lu micro: %lu data length: %u\n", peer.c_str(), XORP_UINT_CAST(genid), status, (unsigned long)tv.sec(), (unsigned long)tv.usec(), XORP_UINT_CAST(data.size())); /* ** Are we in the peer table. */ PeerList::iterator cur = _peers.begin(); while(cur != _peers.end()) { switch(cur->is_this_you(peer, genid)) { case Peer::YES_ITS_ME: cur->datain(status, tv, data); return; break; case Peer::NO_ITS_NOT_ME: break; case Peer::PLEASE_DELETE_ME: _peers.erase(cur++); continue; break; } ++cur; } XLOG_WARNING("Data for a peer <%s,%u> that is not in our table", peer.c_str(), XORP_UINT_CAST(genid)); } void Command::datain_error(const string& peer, const uint32_t& genid, const string& reason) { debug_msg("peer: %s genid: %u reason: %s\n", peer.c_str(), XORP_UINT_CAST(genid), reason.c_str()); /* ** Are we in the peer table. */ PeerList::iterator cur = _peers.begin(); while(cur != _peers.end()) { switch(cur->is_this_you(peer, genid)) { case Peer::YES_ITS_ME: cur->datain_error(reason); return; break; case Peer::NO_ITS_NOT_ME: break; case Peer::PLEASE_DELETE_ME: _peers.erase(cur++); continue; break; } ++cur; } XLOG_WARNING("Data for a peer <%s,%u> that is not in our table", peer.c_str(), XORP_UINT_CAST(genid)); } void Command::datain_closed(const string& peer, const uint32_t& genid) { debug_msg("peer: %s genid: %u \n", peer.c_str(), XORP_UINT_CAST(genid)); /* ** Are we in the peer table. */ PeerList::iterator cur = _peers.begin(); while(cur != _peers.end()) { switch(cur->is_this_you(peer, genid)) { case Peer::YES_ITS_ME: cur->datain_closed(); return; break; case Peer::NO_ITS_NOT_ME: break; case Peer::PLEASE_DELETE_ME: _peers.erase(cur++); continue; break; } ++cur; } XLOG_WARNING("Data for a peer <%s,%u> that is not in our table", peer.c_str(), XORP_UINT_CAST(genid)); } /* ** All commands to peers are channelled through here. */ void Command::peer(const string& line, const vector& words) throw(InvalidString) { debug_msg("peer: %s\n", line.c_str()); if(1 == words.size()) xorp_throw(InvalidString, c_format("Insufficient arguments: %s", line.c_str())); /* ** This pointer must be valid. If we are in the command table ** there must be an entry in the peer table. */ PeerList::iterator p = _peers.begin(); for(p = _peers.begin(); p != _peers.end(); ++p){ if(Peer::YES_ITS_ME == p->is_this_you(words[0])) break; } XLOG_ASSERT(_peers.end() != p); const string command = words[1]; if("connect" == command) { p->connect(line, words); } else if("disconnect" == command) { p->disconnect(line, words); } else if("establish" == command) { p->establish(line, words); } else if ("send" == command) { p->send(line, words); } else if ("trie" == command) { p->trie(line, words); } else if ("expect" == command) { p->expect(line, words); } else if ("assert" == command) { p->assertX(line, words); } else if ("dump" == command) { p->dump(line, words); } else { xorp_throw(InvalidString, c_format("Unrecognized command: %s", command.c_str())); } } /* ** This command takes no arguments: ** reset. ** ** Reset all the state in the coordinating process. All scripts should ** start with this command. */ void Command::reset(const string& /*line*/, const vector& /*v*/) { debug_msg("reset:\n"); load_command_map(); _target_hostname = ""; _target_port = ""; /* ** Shutdown all the peers that are currently running. */ for(PeerList::iterator cur = _peers.begin(); cur != _peers.end(); ++cur){ if(cur->up()) cur->shutdown(); } _init_count = 0; } /* ** This command takes two arguments: ** target hostname port. ** ** The host and port number of the BGP process that is being tested. */ void Command::target(const string& line, const vector& v) throw(InvalidString) { debug_msg("target: %s\n", line.c_str()); if(3 != v.size()) xorp_throw(InvalidString, c_format("\"target hostname port\" expected got \"%s\"", line.c_str())); _target_hostname = v[1]; _target_port = v[2]; } /* ** This command takes two arguments: ** initialise attach/create peername ** ** Attach to or create test peers. */ void Command::initialise(const string& line, const vector& v) throw(InvalidString) { debug_msg("target: %s\n", line.c_str()); if(3 != v.size()) xorp_throw(InvalidString, c_format("\"initialise attach/create peer\" expected got \"%s\"", line.c_str())); const char *peername = v[2].c_str(); /* ** Make sure that this peer doesn't already exist. */ PeerList::iterator cur = _peers.begin(); for(; cur != _peers.end(); ++cur) if(Peer::YES_ITS_ME == cur->is_this_you(peername)) break; if(cur != _peers.end()) debug_msg("Peer name %s\n", peername); if(_peers.end() != cur) xorp_throw(InvalidString, c_format("This peer already exists: %s", peername)); /* ** This peer name will be added to the command name map, verify ** that we don't already have a command with this name. */ StringCommandMap::iterator com = _commands.find(peername); if(_commands.end() != com) xorp_throw(InvalidString, c_format("Peername command clash: %s", peername)); if(v[1] == "attach") { /* FINE */ } else if(v[1] == "create") { XLOG_FATAL("initialise create not currently supported"); } else xorp_throw(InvalidString, c_format("Only attach/create allowed not: %s", v[3].c_str())); /* ** If we got here we are attaching. */ /* Attach to and reset the peer. */ debug_msg("About to attach\n"); XrlTestPeerV0p1Client test_peer(&_xrlrouter); test_peer.send_reset(peername, callback(this, &Command::initialise_callback, string(peername))); _init_count++; } void Command::initialise_callback(const XrlError& error, string peername) { debug_msg("callback: %s\n", peername.c_str()); _init_count--; if(XrlError::OKAY() != error) { XLOG_ERROR("%s: Failed to initialise peer %s", error.str().c_str(), peername.c_str()); return; } /* Add to the peer structure */ _peers.push_back(Peer(&_eventloop, &_xrlrouter, peername, _genid++, _target_hostname, _target_port)); /* Add to the command structure */ _commands.insert(StringCommandMap::value_type(peername, &Command::peer)); } xorp/bgp/harness/soak.sh0000775000076400007640000000075211421137511015367 0ustar greearbgreearb#!/usr/bin/env bash # # $XORP: xorp/bgp/harness/soak.sh,v 1.3 2003/01/29 07:03:58 atanu Exp $ # # # Soak test # set -e # srcdir is set by make for check target if [ "X${srcdir}" = "X" ] ; then srcdir=`dirname $0` ; fi TESTS="test_peering1.sh test_peering2.sh test_routing1.sh test_rib1.sh test_rib_fea1.sh test_terminate.sh" while : do for i in $TESTS do ${srcdir}/$i ${srcdir}/$i -l done done # Local Variables: # mode: shell-script # sh-indentation: 4 # End: xorp/bgp/harness/test_path_attribute1.sh0000775000076400007640000002534611421137511020577 0ustar greearbgreearb#!/usr/bin/env bash # # $XORP: xorp/bgp/harness/test_path_attribute1.sh,v 1.11 2007/12/11 01:21:43 mjh Exp $ # # # Test various # # This script started with no arguments will start all required process and # terminate them at the end of the tests. # # Preconditons # 1) Run a finder process # 2) Run a FEA process. # 3) Run a RIB process. # 4) Run xorp "../xorp_bgp" # 5) Run "./test_peer -s peer1" # 6) Run "./test_peer -s peer2" # 7) Run "./test_peer -s peer3" # 8) Run "./coord" # set -e . ./setup_paths.sh # srcdir is set by make for check target if [ "X${srcdir}" = "X" ] ; then srcdir=`dirname $0` ; fi . ${srcdir}/xrl_shell_funcs.sh "" . $BGP_FUNCS "" onexit() { last=$? if [ $last = "0" ] then echo "$0: Tests Succeeded (BGP: $TESTS)" else echo "$0: Tests Failed (BGP: $TESTS)" fi trap '' 0 2 } trap onexit 0 2 HOST=127.0.0.1 LOCALHOST=$HOST ID=192.150.187.78 AS=65008 USE4BYTEAS=false NEXT_HOP=192.150.187.78 # EBGP PORT1=10001 PEER1_PORT=20001 PEER1_AS=65001 # IBGP PORT2=10002 PEER2_PORT=20002 PEER2_AS=$AS # EBGP PORT3=10003 PEER3_PORT=20002 PEER3_AS=65003 HOLDTIME=5 configure_bgp() { local_config $AS $ID $USE4BYTEAS # Don't try and talk to the rib. register_rib "" PEER=$HOST IPTUPLE="$LOCALHOST $PORT1 $PEER $PEER1_PORT" add_peer lo $IPTUPLE $PEER1_AS $NEXT_HOP $HOLDTIME set_parameter $IPTUPLE MultiProtocol.IPv4.Unicast true enable_peer $IPTUPLE PEER=$HOST IPTUPLE="$LOCALHOST $PORT2 $PEER $PEER2_PORT" add_peer lo $IPTUPLE $PEER2_AS $NEXT_HOP $HOLDTIME set_parameter $IPTUPLE MultiProtocol.IPv4.Unicast true enable_peer $IPTUPLE PEER=$HOST IPTUPLE="$LOCALHOST $PORT3 $PEER $PEER3_PORT" add_peer lo $IPTUPLE $PEER3_AS $NEXT_HOP $HOLDTIME set_parameter $IPTUPLE MultiProtocol.IPv4.Unicast true enable_peer $IPTUPLE } reset() { coord reset coord target $HOST $PORT1 coord initialise attach peer1 coord target $HOST $PORT2 coord initialise attach peer2 coord target $HOST $PORT3 coord initialise attach peer3 } test1() { echo "TEST1:" echo " 1) Send an update packet with an optional nontransitive path" echo " attribute. This path attribute should not be propogated" echo " by the BGP process" # Reset the peers reset # Establish a connection coord peer1 establish AS $PEER1_AS holdtime 0 id 192.150.187.100 coord peer1 assert established coord peer2 establish AS $PEER2_AS holdtime 0 id 192.150.187.101 coord peer2 assert established coord peer3 establish AS $PEER3_AS holdtime 0 id 192.150.187.102 coord peer3 assert established ASPATH="$PEER1_AS,1,2,[3,4,5],6,[7,8],9" NEXTHOP="20.20.20.20" # note 19 in these tests is reserved for unknown test attributes # (see path_attribute.hh), but should be changed if 19 is ever # really allocated. PACKET1="packet update origin 2 aspath $ASPATH nexthop $NEXTHOP pathattr 0x80,19,1,1 nlri 10.10.10.0/24 nlri 20.20.20.20/24" PACKET2="packet update origin 2 aspath $ASPATH nexthop $NEXTHOP localpref 100 nlri 10.10.10.0/24 nlri 20.20.20.20/24" PACKET3="packet update origin 2 aspath $AS,$ASPATH nexthop $NEXT_HOP med 1 nlri 10.10.10.0/24 nlri 20.20.20.20/24" coord peer2 expect $PACKET2 coord peer3 expect $PACKET3 coord peer1 send $PACKET1 sleep 2 coord peer2 assert queue 0 coord peer3 assert queue 0 # Verify that the peers are still connected. coord peer1 assert established coord peer2 assert established coord peer3 assert established } test2() { echo "TEST2:" echo " 1) Send an update packet with an optional transitive path" echo " attribute. This path attribute should be propogated" echo " by the BGP process with the partial bit set." # Reset the peers reset # Establish a connection coord peer1 establish AS $PEER1_AS holdtime 0 id 192.150.187.100 coord peer1 assert established coord peer2 establish AS $PEER2_AS holdtime 0 id 192.150.187.101 coord peer2 assert established coord peer3 establish AS $PEER3_AS holdtime 0 id 192.150.187.102 coord peer3 assert established ASPATH="$PEER1_AS,1,2,[3,4,5],6,[7,8],9" NEXTHOP="20.20.20.20" # note 19 in these tests is reserved for unknown test attributes # (see path_attribute.hh), but should be changed if 19 is ever # really allocated. PACKET1="packet update origin 2 aspath $ASPATH nexthop $NEXTHOP pathattr 0xc0,19,1,1 nlri 10.10.10.0/24 nlri 20.20.20.20/24" PACKET2="packet update origin 2 aspath $ASPATH nexthop $NEXTHOP localpref 100 pathattr 0xe0,19,1,1 nlri 10.10.10.0/24 nlri 20.20.20.20/24" PACKET3="packet update origin 2 aspath $AS,$ASPATH nexthop $NEXT_HOP med 1 pathattr 0xe0,19,1,1 nlri 10.10.10.0/24 nlri 20.20.20.20/24" coord peer2 expect $PACKET2 coord peer3 expect $PACKET3 coord peer1 send $PACKET1 sleep 2 coord peer2 assert queue 0 coord peer3 assert queue 0 # Verify that the peers are still connected. coord peer1 assert established coord peer2 assert established coord peer3 assert established } test3() { echo "TEST3:" echo " 1) Send an update packet with two optional path" echo " attributes. One has the transitive bit set the other" echo " doesn't. Only the path attribute with the transitive bit" echo " set should be propogated by the BGP process, with the" echo " partial bit set." # Reset the peers reset # Establish a connection coord peer1 establish AS $PEER1_AS holdtime 0 id 192.150.187.100 coord peer1 assert established coord peer2 establish AS $PEER2_AS holdtime 0 id 192.150.187.101 coord peer2 assert established coord peer3 establish AS $PEER3_AS holdtime 0 id 192.150.187.102 coord peer3 assert established ASPATH="$PEER1_AS,1,2,[3,4,5],6,[7,8],9" NEXTHOP="20.20.20.20" # note 19 in these tests is reserved for unknown test attributes # (see path_attribute.hh), but should be changed if 19 is ever # really allocated. PACKET1="packet update origin 2 aspath $ASPATH nexthop $NEXTHOP pathattr 0xc0,19,1,1 pathattr 0x80,20,1,1 nlri 10.10.10.0/24 nlri 20.20.20.20/24" PACKET2="packet update origin 2 aspath $ASPATH nexthop $NEXTHOP localpref 100 pathattr 0xe0,19,1,1 nlri 10.10.10.0/24 nlri 20.20.20.20/24" PACKET3="packet update origin 2 aspath $AS,$ASPATH nexthop $NEXT_HOP med 1 pathattr 0xe0,19,1,1 nlri 10.10.10.0/24 nlri 20.20.20.20/24" coord peer2 expect $PACKET2 coord peer3 expect $PACKET3 coord peer1 send $PACKET1 sleep 2 coord peer2 assert queue 0 coord peer3 assert queue 0 # Verify that the peers are still connected. coord peer1 assert established coord peer2 assert established coord peer3 assert established } # http://www.xorp.org/bugzilla/show_bug.cgi?id=717 test4() { echo "TEST4:" echo " 1) Send an update packet with a community" echo " attribute of more than 256 bytes" # Reset the peers reset # Establish a connection coord peer1 establish AS $PEER1_AS holdtime 0 id 192.150.187.100 coord peer1 assert established coord peer2 establish AS $PEER2_AS holdtime 0 id 192.150.187.101 coord peer2 assert established coord peer3 establish AS $PEER3_AS holdtime 0 id 192.150.187.102 coord peer3 assert established ASPATH="$PEER1_AS,1,2,[3,4,5],6,[7,8],9" NEXTHOP="20.20.20.20" set +e let i=0 while ((i++ < 256)) do COMMUNITY=$COMMUNITY" community $i" done set -e PACKET1="packet update origin 2 aspath $ASPATH nexthop $NEXTHOP $COMMUNITY nlri 10.10.10.0/24 nlri 20.20.20.20/24" PACKET2="packet update origin 2 aspath $ASPATH nexthop $NEXTHOP localpref 100 $COMMUNITY nlri 10.10.10.0/24 nlri 20.20.20.20/24" PACKET3="packet update origin 2 aspath $AS,$ASPATH nexthop $NEXT_HOP med 1 $COMMUNITY nlri 10.10.10.0/24 nlri 20.20.20.20/24" coord peer2 expect $PACKET2 coord peer3 expect $PACKET3 coord peer1 send $PACKET1 sleep 2 coord peer2 assert queue 0 coord peer3 assert queue 0 # Verify that the peers are still connected. coord peer1 assert established coord peer2 assert established coord peer3 assert established } test5() { echo "TEST5:" echo " 1) Check BGP handles AS4Aggregator correctly." # Reset the peers reset # Establish a connection coord peer1 establish AS $PEER1_AS holdtime 0 id 192.150.187.100 coord peer1 assert established coord peer2 establish AS $PEER2_AS holdtime 0 id 192.150.187.101 coord peer2 assert established coord peer3 establish AS $PEER3_AS holdtime 0 id 192.150.187.102 coord peer3 assert established ASPATH="$PEER1_AS,1,2,[3,4,5],6,[7,8],9" NEXTHOP="20.20.20.20" PACKET1="packet update origin 2 aspath $ASPATH nexthop $NEXTHOP as4aggregator 30.30.30.30 10.10000 nlri 10.10.10.0/24 nlri 20.20.20.20/24" PACKET2="packet update origin 2 aspath $ASPATH nexthop $NEXTHOP localpref 100 as4aggregator 30.30.30.30 10.10000 nlri 10.10.10.0/24 nlri 20.20.20.20/24" PACKET3="packet update origin 2 aspath $AS,$ASPATH nexthop $NEXT_HOP med 1 as4aggregator 30.30.30.30 10.10000 nlri 10.10.10.0/24 nlri 20.20.20.20/24" coord peer2 expect $PACKET2 coord peer3 expect $PACKET3 coord peer1 send $PACKET1 sleep 2 coord peer2 assert queue 0 coord peer3 assert queue 0 # Verify that the peers are still connected. coord peer1 assert established coord peer2 assert established coord peer3 assert established } TESTS_NOT_FIXED='' TESTS='test1 test2 test3 test4 test5' # Include command line . ${srcdir}/args.sh #START_PROGRAMS="no" if [ $START_PROGRAMS = "yes" ] then CXRL="$CALLXRL -r 10" runit $QUIET $VERBOSE -c "$0 -s -c $*" <& data); void datain_error(const string& peer, const uint32_t& genid, const string& reason); void datain_closed(const string& peer, const uint32_t& genid); /* ** All commands to peers sent through here. */ void peer(const string& line, const vector& v) throw(InvalidString); void reset(const string& line, const vector& v); void target(const string& line, const vector& v) throw(InvalidString); void initialise(const string& line, const vector& v) throw(InvalidString); void initialise_callback(const XrlError& error, string peername); private: EventLoop& _eventloop; XrlStdRouter& _xrlrouter; uint32_t _genid; uint32_t _init_count; // Number of initialisations with // test_peers currently in progress. /* ** Supported commands. */ typedef void (Command::* MEMFUN)(const string& line, const vector& v); struct PCmd { PCmd(const Command::MEMFUN& mem_fun) : _mem_fun(mem_fun) {} void dispatch(Command* c, const string& line, const vector&v) { (c->*_mem_fun)(line, v); } Command::MEMFUN _mem_fun; }; typedef map StringCommandMap; StringCommandMap _commands; typedef list PeerList; PeerList _peers; string _target_hostname; string _target_port; }; #endif // __BGP_HARNESS_COMMAND_HH__ xorp/bgp/harness/NOTES0000664000076400007640000002172111421137511014705 0ustar greearbgreearb# # $XORP: xorp/bgp/harness/NOTES,v 1.1.1.1 2002/12/11 23:55:51 hodson Exp $ # # # Example Scripting language for a BGP test harness. # # The testing system is composed of three components: # 1) The BGP process (BGP "target") that is being tested. # 2) One or more BGP session end points (BGP "test peers") that can be used # to send or receive packets from the BGP target. The "test peers" are # "dumb" entities that know nothing about BGP. They are just endpoints # that can be controlled via XRLs. When testing foreign BGP # implementations it may be necessary to have processes running on # separate machines. # 3) The BGP test coordinator (BGP "coordinator"). This process crafts # BGP packets and introduces them via the "test peers". When testing # the xorp BGP process it is not necessary to send packets via the # "test peers", they can be sent directly from the "coordinator". # A BGP process is being tested by creating one or more sessions to # it. Two modes of operation will be supported. # 1) Testing a xorp BGP process in which case there is no need to use # "test peers". # 2) Testing a foreign BGP hence the requirement to communicate via # "test peers". # Init stage. Either make TCP connections directly from the "coord" # process or wait for the "test peers" to start and register with the # "coord". target tigger.icir.org 179 # Reset all the state in the coordinator. reset # The "coordinator" should start the bgp "target" process. xorp bgp[AS num, other config ...] initialise attach/create peer1 initialise attach/create peer2 initialise attach/create peer3 # Make the TCP connection. peer1 connect target port[179] peer2 connect target port[179] peer3 connect target port[179] # # There are two options at this point: # 1) Explicitly construct packets and send them via the "test peer". # peer1 send packet open[Parameters - Possibly bad] peer1 expect packet open[Parameters] peer1 send packet keepalive[Parameters] # 2) The commands can be totally BGP aware. For example bring up the # peering. This case we can used to test the actual routing code. peer1 establish keepalive true peer2 establish keepalive true peer3 establish keepalive true # Totally bring up a peering and generate keepalives when appropriate # to preserve the peering. # Send a route in via one peer and verify that it appears on the other # peerings. peer1 send packet update[Parameters] # Each peer can have a queue of expected packets. Arriving packets are # tested against this queue. If the packets match then all is fine and # the packet is removed from the queue. If an error occurs then an # error is flagged and the offending packet is saved. Subsequent # interrogation will return the error. # peer2 expect packet update[Parameters] peer3 expect packet update[Parameters] peer3 expect check queue 0 # Save all updates from peer1 and peer2 into files in mrtd format. # Read from an mrtd trace file and feed it in through peer 3. peer1 save mrtd[mrtd output file] peer2 save mrtd[mrtd output file] peer3 send mrtd[mrtd format trace file] # Perform some form of verification of the peer3 inputs against the # peer1 and peer2 outputs. Not sure quite how to do do this. verify peer3 peer1 peer2 # Drop the peering peer1 notify cease peer1 disconnect peer2 notify cease peer2 disconnect peer3 notify cease peer3 disconnect # Terminate the coordination process terminate # Terminate the coordination process as well as the test peers. terminate all ISSUES ====== 1) Standalone tests should be possible. 2) Timing: a) It should be possible to test that the peer actually sends keepalive at the prescribed time. Also that a connection is correctly dropped by the peer if we don't send keepalives. The scripting language must either allow reasoning about time, or export timing information. Problem Statement ================= Build a framework that allows the scripting of tests. Which can be run totally from a makefile (for example, to perform regression tests). It should be possible to totally in isolation test the xorp bgp process. TEST PEER XRLs ============== Commands that are accepted by the test peer. Register("coord"): This is an external registration to the test peer. All packets received by the test peer are sent to the "coord". Packetisation("bgp"): Tell the test peer to treat incoming packets as BGP packets packetize them accordingly. Otherwise just packetise the the packets the way they appear from the connection. Connect("host", "port"): Connect to the named host and port. Listen("address", "port"); Listen for connections on this address and port. Send("Data"): Send data on the tcp connection. Disconnect(): Drop the current tcp connection. Terminate: Terminate the process. TEST PEER CLIENT XRLs ===================== Packet("peer", "status", "time", "data"): "peer" - The peer that the packet came from. "status" - If the remote peer had been asked to perform packetisation. Then if a bad message is received signify this. Also after a bad is received packetisation is disabled. "time" - The time when the packet was received in micro seconds since 1970-1-1. "data" - The raw data that was read on the connection. COORD XRLs ========== Command("command string"): Accept commands via XRLs. Packet Format ============= { [ b4 0x3ce035f7 ] TIME: 05/13/02 15:08:51 [ b2 10, b2 1] TYPE: BGP4MP/MESSAGE/Update [ b4 0x43] LENGTH: 0x43 [] FROM: 198.32.200.50 AS6066 [] TO: 198.32.200.157 AS12654 [] ORIGIN: IGP [] ASPATH: 6066 3549 5511 4689 7513 [] NEXT_HOP: 198.32.200.50 [] ANNOUNCE [] 202.222.192.0/18 } Commands currently accepted by coord ==================================== * reset Reset all the state in the coordinating process. * target The bgp process under test. * initialise attach/create peername Form an association with a test_peer. If the second argument is attach then it is assumed that the test_peer is already running. If the second argument is create then the test_peer is started (not currently supported). Peer specific commands ====================== * connect Connect to the target * listen Listen for a connection from the target. * establish active AS keepalive holdtime id The active, AS, keepalive, holdtime and id arguments are optional. Active defaults to being true and actively makes a connection, setting active to false sets up a listener. The AS value is recommended if a connection is wanted. * send packet update origin aspath nexthop localpref nlri withdraw * send dump mrtd update filename Given a file in mrtd dump format send the update packets in this file. * trie lookup aspath * expect packet notify Place a notification packet on the expect queue. The is mandatory. The is optional. * expect packet update origin aspath nexthop localpref nlri withdraw Place an update packet on the expect queue. * expect packet open asnum bgpid holdtime Place an open packet on the expect queue. All fields shown are mandatory. * expect packet notify Place a notify packet on the expect queue. * assert queue Check the queue length of the expect queue. Every message that matches removes an entry from the queue. If an error has previously occured then this call will return the error. The length of the queue check is optional. * assert established Verify that a session has actually been established. Some tests can pass without a bgp process being present. These tests require this interface. * dump A mechanism for saving conversations or dumping routing tables. The received and sent cases can be dealt with independently. Four types of dumps are supported: 1) Traffic. The is basically all the traffic which is sent and received. The dumping can be disabled by making a call with the argument removed. 2) Routeview. The current state of the routing table. 3) Current. Trawls through the routing table and dumps all the update packets that have caused entries in the routing table. The packets are dumped in the order in which they arrived. This is only an approximation as update packets containing only withdraws will not be saved. 4) Debug. Visit all nodes in the trie and dump the update packet that was responsible for this entry. Update packets can have multiple NLRIs associated with them so a packet can be in the dump many times. The save file can be either in mtrd dump format or in xorp text format. xorp/bgp/harness/peer.cc0000664000076400007640000015203611540224217015342 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #include "bgp/bgp_module.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/eventloop.hh" #include "libxorp/ipv4net.hh" #include "libxorp/utils.hh" #include "libproto/packet.hh" #include "libxipc/xrl_std_router.hh" #include "xrl/interfaces/test_peer_xif.hh" #include "bgp/local_data.hh" #include "bgp/packet.hh" #include "peer.hh" #include "bgppp.hh" Peer::~Peer() { debug_msg("XXX Deleting peer %p\n", this); } Peer::Peer(EventLoop *eventloop, XrlStdRouter *xrlrouter, const string& peername, const uint32_t genid, const string& target_hostname, const string& target_port) : _eventloop(eventloop), _xrlrouter(xrlrouter), _peername(peername), _genid(genid), _target_hostname(target_hostname), _target_port(target_port), _up(true), _busy(0), _connected(false), _session(false), _passive(false), _established(false), _keepalive(false), _as(AsNum::AS_INVALID), // XXX _holdtime(0), _ipv6(false), _last_recv(0), _last_sent(0) { debug_msg("XXX Creating peer %p\n", this); Iptuple iptuple; _localdata = new LocalData(*eventloop); _localdata->set_use_4byte_asnums(false); _localdata->set_as(AsNum(0)); _peerdata = new BGPPeerData(*_localdata, iptuple, AsNum(0), IPv4(),0); _peerdata->compute_peer_type(); _peerdata->set_multiprotocol(SAFI_UNICAST, BGPPeerData::NEGOTIATED); _peerdata->set_multiprotocol(SAFI_UNICAST, BGPPeerData::SENT); } Peer::Peer(const Peer& rhs) : _as(AsNum::AS_INVALID) // XXX { debug_msg("Copy constructor\n"); copy(rhs); } Peer& Peer::operator=(const Peer& rhs) { debug_msg("= operator\n"); if(&rhs == this) return *this; copy(rhs); return *this; } void Peer::copy(const Peer& rhs) { debug_msg("peername: %s\n", rhs._peername.c_str()); _eventloop = rhs._eventloop; _xrlrouter = rhs._xrlrouter; _peername = rhs._peername; _genid = rhs._genid; _target_hostname = rhs._target_hostname; _target_port = rhs._target_port; _up = rhs._up; _busy = rhs._busy; _session = rhs._session; _passive = rhs._passive; _keepalive = rhs._keepalive; _established = rhs._established; _as = rhs._as; _holdtime = rhs._holdtime; _ipv6 = rhs._ipv6; _id = rhs._id; _last_recv = rhs._last_recv; _last_sent = rhs._last_sent; _peerdata = rhs._peerdata; _localdata = rhs._localdata; } Peer::PeerState Peer::is_this_you(const string& peer_name) const { debug_msg("is this you: %s %s\n", _peername.c_str(), peer_name.c_str()); if(_up && peer_name == _peername) { debug_msg("Its me\n"); return Peer::YES_ITS_ME; } /* ** If this peer has been down for ten minutes its a candidate to ** be deleted. There should be no more activity on this peer. */ if(!_up) { TimeVal now; _eventloop->current_time(now); if(now - _shutdown_time > TimeVal(60 * 10, 0)) return Peer::PLEASE_DELETE_ME; } return Peer::NO_ITS_NOT_ME; } Peer::PeerState Peer::is_this_you(const string& peer_name, const uint32_t genid) const { debug_msg("is this you: %s %s %u\n", _peername.c_str(), peer_name.c_str(), XORP_UINT_CAST(genid)); if(_up && peer_name == _peername && genid == _genid) return Peer::YES_ITS_ME; if(!_up) return is_this_you(""); return Peer::NO_ITS_NOT_ME; } void Peer::shutdown() { XLOG_ASSERT(_up); // We should only ever be shutdown once. _up = false; _eventloop->current_time(_shutdown_time); /* ** The corresponding test peer may have a tcp connection with a ** bgp process. Attempt a disconnect. */ _busy++; XrlTestPeerV0p1Client test_peer(_xrlrouter); if(!test_peer.send_reset(_peername.c_str(), callback(this, &Peer::xrl_callback, "reset"))) XLOG_FATAL("reset failed"); } bool Peer::up() const { return _up; } void Peer::status(string& status) { status = _peername + " "; status += _established ? "established" : ""; status += " "; status += "sent: "; uint32_t sent = _trie_sent.update_count(); if (0 == _last_sent) { status += c_format("%u", sent) + " "; } else { status += c_format("%u(%u)", sent, sent - _last_sent) + " "; } status += "received: "; uint32_t recv = _trie_recv.update_count(); if (0 == _last_recv) { status += c_format("%u", recv) + " "; } else { status += c_format("%u(%u)", recv, recv - _last_recv) + " "; } } bool Peer::pending() { debug_msg("pending? : %s\n", bool_c_str(_session && !_established && !_passive)); // if(_xrlrouter->pending()) // return true; if(_busy > 0) return true; // printf("%s: session: %d established: %d passive %d\n", // _peername.c_str(), _session, _established, _passive); if(_session && !_established && !_passive) return true; return false; } void Peer::listen(const string& /*line*/, const vector& /*words*/) throw(InvalidString) { /* Connect the test peer to the target BGP */ debug_msg("About to listen on: %s\n", _peername.c_str()); _busy += 4; XrlTestPeerV0p1Client test_peer(_xrlrouter); if(!test_peer.send_register(_peername.c_str(), _xrlrouter->name(), _genid, callback(this, &Peer::xrl_callback, "register"))) XLOG_FATAL("send_register failed"); if(!test_peer.send_packetisation(_peername.c_str(), "bgp", callback(this, &Peer::xrl_callback, "packetisation"))) XLOG_FATAL("send_packetisation"); if(!test_peer.send_use_4byte_asnums(_peername.c_str(), _localdata->use_4byte_asnums(), callback(this, &Peer::xrl_callback, "use_4byte_asnums"))) XLOG_FATAL("send_use_4byte_asnums"); if(!test_peer.send_listen(_peername.c_str(), _target_hostname, atoi(_target_port.c_str()), callback(this, &Peer::xrl_callback, "listen"))) XLOG_FATAL("send_listen failed"); } void Peer::connect(const string& /*line*/, const vector& /*words*/) throw(InvalidString) { /* Connect the test peer to the target BGP */ debug_msg("About to connect to: %s\n", _peername.c_str()); _busy += 4; XrlTestPeerV0p1Client test_peer(_xrlrouter); if(!test_peer.send_register(_peername.c_str(), _xrlrouter->name(), _genid, callback(this, &Peer::xrl_callback, "register"))) XLOG_FATAL("send_register failed"); if(!test_peer.send_packetisation(_peername.c_str(), "bgp", callback(this, &Peer::xrl_callback, "packetisation"))) XLOG_FATAL("send_packetisation failed"); if(!test_peer.send_use_4byte_asnums(_peername.c_str(), _localdata->use_4byte_asnums(), callback(this, &Peer::xrl_callback, "use_4byte_asnums"))) XLOG_FATAL("send_use_4byte_asnums"); if(!test_peer.send_connect(_peername.c_str(), _target_hostname, atoi(_target_port.c_str()), callback(this, &Peer::xrl_callback_connected, "connected"))) XLOG_FATAL("send_connect failed"); } void Peer::disconnect(const string& /*line*/, const vector& /*words*/) throw(InvalidString) { /* Disconnect the test peer from the target BGP */ debug_msg("About to disconnect from: %s\n", _peername.c_str()); _busy++; XrlTestPeerV0p1Client test_peer(_xrlrouter); _session = false; _established = false; _last_sent = _trie_sent.update_count(); _last_recv = _trie_recv.update_count(); if(!test_peer.send_disconnect(_peername.c_str(), callback(this, &Peer::xrl_callback, "disconnect"))) XLOG_FATAL("send_disconnect failed"); } /* ** Form of command: ** peer1 establish active AS ** */ void Peer::establish(const string& line, const vector& words) throw(InvalidString) { debug_msg("About to establish a connection %s\n", _peername.c_str()); bool active = true; _session = true; /* ** Parse the command line. ** The arguments for this command come in pairs: ** name value */ string as = ""; size_t size = words.size(); if(0 != (size % 2)) xorp_throw(InvalidString, c_format("Incorrect number of arguments:\n[%s]", line.c_str())); for(size_t i = 2; i < words.size(); i += 2) { debug_msg("name: %s value: %s\n", words[i].c_str(), words[i + 1].c_str()); if("active" == words[i]) { string aarg = words[i+1]; if("true" == aarg) active = true; else if("false" == aarg) active = false; else xorp_throw(InvalidString, c_format("Illegal argument to active: <%s>\n[%s]", aarg.c_str(), line.c_str())); } else if("AS" == words[i]) { as = words[i + 1]; } else if("keepalive" == words[i]) { string kaarg = words[i+1]; if("true" == kaarg) _keepalive = true; else if("false" == kaarg) _keepalive = false; else xorp_throw(InvalidString, c_format("Illegal argument to keepalive: <%s>\n[%s]", kaarg.c_str(), line.c_str())); } else if("holdtime" == words[i]) { _holdtime = atoi(words[i + 1].c_str()); } else if("id" == words[i]) { _id = IPv4(words[i + 1].c_str()); } else if("ipv6" == words[i]) { string ipv6arg = words[i+1]; if("true" == ipv6arg) _ipv6 = true; else if("false" == ipv6arg) _ipv6 = false; else xorp_throw(InvalidString, c_format("Illegal argument to ipv6: <%s>\n[%s]", ipv6arg.c_str(), line.c_str())); } else if("use_4byte_asnums" == words[i]) { string use4bytearg = words[i+1]; if("true" == use4bytearg) { _localdata->set_use_4byte_asnums(true); _peerdata->set_use_4byte_asnums(true); } else if("false" == use4bytearg) { _localdata->set_use_4byte_asnums(false); _peerdata->set_use_4byte_asnums(true); } else xorp_throw(InvalidString, c_format("Illegal argument to use_4byte_asnums: <%s>\n[%s]", use4bytearg.c_str(), line.c_str())); } else xorp_throw(InvalidString, c_format("Illegal argument to establish: <%s>\n[%s]", words[i].c_str(), line.c_str())); } if("" == as) xorp_throw(InvalidString, c_format("No AS number specified:\n[%s]", line.c_str())); _as = AsNum(as); if(!active) { _passive = true; listen(line, words); return; } connect(line, words); send_open(); } void Peer::send(const string& line, const vector& words) throw(InvalidString) { size_t size = words.size(); if(size < 3) xorp_throw(InvalidString, c_format("Insufficient number of arguments:\n[%s]", line.c_str())); const char PACKET[] = "packet"; const char DUMP[] = "dump"; if(PACKET == words[2]) { send_packet(line, words); } else if(DUMP == words[2]) { send_dump(line, words); } else { xorp_throw(InvalidString, c_format( "Second argument should be %s or %s not <%s>\n[%s]", PACKET, DUMP, words[2].c_str(), line.c_str())); } } /** * A corruption entry. * * The offset at which to corrupt a packet and the value with which to corrupt. */ class Corrupt { public: Corrupt(uint32_t offset, uint8_t val) : _offset(offset), _val(val) {} uint32_t offset() const { return _offset; } uint32_t val() const { return _val; } private: uint32_t _offset; uint8_t _val; }; /* ** Form of command: ** peer1 send packet update ... ** peer1 send packet notify ... ** peer1 send packet open ... ** peer1 send packet keepalive ** peer1 send packet corrupt offset byte offset byte ... update ** notify ** open ** keepalive ** ** See the packet method for the rest of the commands */ void Peer::send_packet(const string& line, const vector& words) throw(InvalidString) { size_t size = words.size(); uint32_t word = 3; list _corrupt; if ("corrupt" == words[3]) { word += 1; // Pairs of offset byte should be in the stream until we reach // packet. while (word + 2 < size) { if (!xorp_isdigit(words[word][0])) { xorp_throw(InvalidString, c_format("Should be an offset not %s\n[%s]", words[word].c_str(), line.c_str())); } if (!xorp_isdigit(words[word + 1][0])) { xorp_throw(InvalidString, c_format("Should be a value not %s\n[%s]", words[word + 1].c_str(), line.c_str())); } _corrupt.push_back(Corrupt(atoi(words[word].c_str()), atoi(words[word+1].c_str()))); word += 2; if (!xorp_isdigit(words[word][0])) break; } } const BGPPacket *pkt = Peer::packet(line, words, word); size_t len = BGPPacket::MAXPACKETSIZE; uint8_t buf[BGPPacket::MAXPACKETSIZE]; XLOG_ASSERT(pkt->encode(buf, len, _peerdata)); printf("packet: %s\n", pkt->str().c_str()); delete pkt; if (!_corrupt.empty()) { list::const_iterator i; for (i = _corrupt.begin(); i != _corrupt.end(); i++) { if (i->offset() >= len) xorp_throw(InvalidString, c_format("Offset %u larger than packet %u\n[%s]", i->offset(), XORP_UINT_CAST(len), line.c_str())); buf[i->offset()] = i->val(); } } /* ** If this is an update message, save it in the sent trie. */ const char update[] = "update"; if(update == words[3]) { TimeVal tv; _eventloop->current_time(tv); try { _trie_sent.process_update_packet(tv, buf, len, _peerdata); } catch(CorruptMessage& c) { /* ** A corrupt message is being sent so catch the decode exception. */ XLOG_WARNING("BAD Message: %s", c.why().c_str()); } } _busy++; send_message(buf, len, callback(this, &Peer::xrl_callback, "send packet")); } struct mrt_header { uint32_t time; uint16_t type; uint16_t subtype; uint32_t length; }; struct mrt_update { uint16_t source_as; uint16_t dest_as; uint16_t ifindex; uint16_t af; uint32_t source_ip; uint32_t dest_ip; }; inline const uint8_t * mrtd_traffic_file_read(FILE *fp, size_t& len) { mrt_header header; if(fread(&header, sizeof(header), 1, fp) != 1) { if(feof(fp)) return 0; XLOG_WARNING("fread failed:%s", strerror(errno)); return 0; } len = ntohl(header.length) - sizeof(mrt_update); mrt_update update; if(fread(&update, sizeof(update), 1, fp) != 1) { if(feof(fp)) return 0; XLOG_WARNING("fread failed:%s", strerror(errno)); return 0; } uint8_t *buf = new uint8_t[len]; if(fread(buf, len, 1, fp) != 1) { if(feof(fp)) return 0; XLOG_WARNING("fread failed:%s", strerror(errno)); return 0; } return buf; } #if 0 // Code to read from a MRTD table dump. // In order to hook this code up: // 1) Modify send_dump to support "routeview" as well as "update". // 2) Compare with mrtd_routeview_dump and rename to mrtd_routview_read. // 3) Update the manual. // Contributed by Ratul Mahajan struct mrt_table { uint16_t view; uint16_t seqno; uint32_t prefix; uint8_t prefix_len; uint8_t status; uint32_t time; uint32_t peerip; uint16_t peeras; uint16_t att_len; }; inline const uint8_t * mrtd_table_file_read(FILE *fp, size_t& len) { mrt_header header; if(fread(&header, sizeof(header), 1, fp) != 1) { if(feof(fp)) return 0; XLOG_WARNING("fread failed:%s", strerror(errno)); return 0; } uint16_t type = ntohs(header.type); if (type == 12) { // TABLE_DUMP mrt_table table; //sizeof(mrt_table) gives 24 which is incorrect size_t sizeof_table = 22; /* ** this did not work for me: the struct was not being populated ** correctly. no clue why! */ // if(fread(&table, sizeof_table, 1, fp) != 1) { // if(feof(fp)) // return 0; // XLOG_WARNING("fread failed:%s",strerror(errno)); // return 0; // } //uglier version of the above if (! (fread(&table.view, 2, 1, fp) == 1 && fread(&table.seqno, 2, 1, fp) == 1 && fread(&table.prefix, 4, 1, fp) == 1 && fread(&table.prefix_len, 1, 1, fp) == 1 && fread(&table.status, 1, 1, fp) == 1 && fread(&table.time, 4, 1, fp) == 1 && fread(&table.peerip, 4, 1, fp) == 1 && fread(&table.peeras, 2, 1, fp) == 1 && fread(&table.att_len, 2, 1, fp) == 1 ) ) { if (feof(fp)) return 0; XLOG_WARNING("fread failed:%s", strerror(errno)); return 0; } UpdatePacket update; size_t pa_len = ntohl(header.length) - sizeof_table; uint8_t * attributes = new uint8_t[pa_len]; if(fread(attributes, pa_len, 1, fp) != 1) { if(feof(fp)) return 0; XLOG_WARNING("fread failed:%s", strerror(errno)); return 0; } // this didn't work for me. i thought it should have, but didn't // look closely when it didn't. //update.add_nlri(BGPUpdateAttrib(IPv4(ntohl(table.prefix)), // ntohs(table.prefix_len))); //stupid way to do the above uint8_t *pfx = new uint8_t[5]; memcpy(pfx, &(table.prefix_len), 1); memcpy(pfx+1, &(table.prefix), 4); BGPUpdateAttrib nlri(pfx); update.add_nlri(nlri); delete [] pfx; uint8_t * d = attributes; while (pa_len > 0) { size_t used = 0; PathAttribute *pa = PathAttribute::create(d, pa_len, used); if (used == 0) xorp_throw(CorruptMessage, c_format("failed to read path attribute"), UPDATEMSGERR, ATTRLEN); update.add_pathatt(pa); d += used; pa_len -= used; } delete [] attributes; debug_msg("update packet = %s\n", update.str().c_str()); const uint8_t *buf = update.encode(len); return buf; } else { XLOG_WARNING("incorrect record type: %d", type); return 0; } } #endif /* ** peer send dump mrtd update fname ** 0 1 2 3 4 5 6 */ void Peer::send_dump(const string& line, const vector& words) throw(InvalidString) { if(6 != words.size() && 7 != words.size()) xorp_throw(InvalidString, c_format("Incorrect number of arguments:\n[%s]", line.c_str())); const char MRTD[] = "mrtd"; if(MRTD != words[3]) xorp_throw(InvalidString, c_format("Third argument should be %s not <%s>\n[%s]", MRTD, words[3].c_str(), line.c_str())); const char UPDATE[] = "update"; if(UPDATE != words[4]) xorp_throw(InvalidString, c_format("Fourth argument should be %s not <%s>\n[%s]", UPDATE, words[4].c_str(), line.c_str())); string fname = words[5]; FILE *fp = fopen(fname.c_str(), "r"); if(0 == fp) xorp_throw(InvalidString, c_format("fopen of %s failed: %s\n[%s]", fname.c_str(), strerror(errno), line.c_str())); size_t packets_to_send = 0; if(7 == words.size()) packets_to_send = atoi(words[6].c_str()); // XXX - We don't check for this to be an integer, we assume // failure returns zero. In which case the whole file is sent. /* ** It could take quite a while to send a large file so we need to ** set up the transfer and then return. */ send_dump_callback(XrlError::OKAY(), fp, 0, packets_to_send, "mrtd_traffic_send"); } void Peer::send_dump_callback(const XrlError& error, FILE *fp, const size_t packet_number, const size_t packets_to_send, const char *comment) { debug_msg("callback %s %s\n", comment, error.str().c_str()); if(XrlError::OKAY() != error) { XLOG_WARNING("callback: %s %s", comment, error.str().c_str()); fclose(fp); return; } if(packets_to_send != 0 && packet_number == packets_to_send) { fclose(fp); return; } size_t len; const uint8_t *buf; while(0 != (buf = mrtd_traffic_file_read(fp, len))) { uint8_t type = extract_8(buf + BGPPacket::TYPE_OFFSET); if(MESSAGETYPEUPDATE == type) { /* ** Save the update message in the sent trie. */ TimeVal tv; _eventloop->current_time(tv); _trie_sent.process_update_packet(tv, buf, len, _peerdata); _smcb = callback(this, &Peer::send_dump_callback, fp, packet_number + 1, packets_to_send, "mrtd_traffic_send"); send_message(buf, len, _smcb); return; } else { delete [] buf; } } fclose(fp); } void Peer::trie(const string& line, const vector& words) throw(InvalidString) { if(words.size() < 4) xorp_throw(InvalidString, c_format("Insufficient arguments:\n[%s]", line.c_str())); /* ** Each peer holds two tries. One holds updates sent the other ** holds updates received. Determine which trie we are about to ** operate on. */ Trie *op; if("sent" == words[2]) { op = &_trie_sent; } else if("recv" == words[2]) { op = &_trie_recv; } else xorp_throw(InvalidString, c_format("\"sent\" or \"recv\" accepted not <%s>\n[%s]", words[2].c_str(), line.c_str())); /* ** The only operation currently supported is lookup */ if("lookup" != words[3]) xorp_throw(InvalidString, c_format("\"lookup\" expected not <%s>\n[%s]", words[3].c_str(), line.c_str())); if(words.size() < 5) xorp_throw(InvalidString, c_format("No arguments for \"lookup\"\n[%s]", line.c_str())); const UpdatePacket *bgpupdate = op->lookup(words[4]); if(0 == bgpupdate) { if((words.size() == 6) && ("not" == words[5])) return; xorp_throw(InvalidString, c_format("Lookup failed [%s]", line.c_str())); } if((words.size() == 6) && ("not" == words[5])) xorp_throw(InvalidString, c_format("Lookup failed entry exists [%s]", line.c_str())); debug_msg("Found: %s\n", bgpupdate->str().c_str()); size_t size = words.size(); if(0 == (size % 2)) xorp_throw(InvalidString, c_format("Incorrect number of arguments:\n[%s]", line.c_str())); for(size_t i = 5; i < size; i += 2) { debug_msg("attribute: %s value: %s\n", words[i].c_str(), words[i + 1].c_str()); if("aspath" == words[i]) { /* ** Search for the AS Path in the update packet. */ FPAList4Ref palist = const_cast(bgpupdate)->pa_list(); list::const_iterator pai; const ASPath *aspath = 0; if (palist->aspath_att()) aspath = &(palist->aspath()); else xorp_throw(InvalidString, c_format("NO AS Path associated with route\n[%s]", line.c_str())); string aspath_search; if("empty" != words[i + 1]) aspath_search = words[i + 1]; if(*aspath != ASPath(aspath_search.c_str())) xorp_throw(InvalidString, c_format("Looking for Path: <%s> Found: <%s>\n[%s]", words[i + 1].c_str(), aspath->str().c_str(), line.c_str())); } else xorp_throw(InvalidString, c_format("Illegal attribute: <%s>\n[%s]", words[i].c_str(), line.c_str())); } } /* ** peer expect packet ... ** 0 1 2 */ void Peer::expect(const string& line, const vector& words) throw(InvalidString) { if(words.size() < 3) xorp_throw(InvalidString, c_format("Insufficient arguments:\n[%s]", line.c_str())); if("packet" == words[2]) { _expect._list.push_back(packet(line, words, 3)); } else xorp_throw(InvalidString, c_format( "\"packet\" accepted not <%s>\n[%s]", words[2].c_str(), line.c_str())); } /* ** peer assert queue ** 0 1 2 ** ** peer assert queue ** 0 1 2 3 ** ** peer assert connected ** 0 1 2 ** ** peer assert established ** 0 1 2 ** ** peer assert idle ** 0 1 2 */ void Peer::assertX(const string& line, const vector& words) throw(InvalidString) { if(words.size() < 3) xorp_throw(InvalidString, c_format("Insufficient arguments:\n[%s]", line.c_str())); if("queue" == words[2]) { if(!_expect._ok) xorp_throw(InvalidString, c_format("Expect queue violated\nExpect: %sReceived: %s", _expect._list.front()->str().c_str(), _expect._bad->str().c_str())); switch(words.size()) { case 3: break; case 4: if(static_cast(atoi(words[3].c_str())) != _expect._list.size()) xorp_throw(InvalidString, c_format("Expected list size to be %d actual %u", atoi(words[3].c_str()), XORP_UINT_CAST(_expect._list.size()))); break; default: xorp_throw(InvalidString, c_format("Illegal number of arguments to \"queue\"\n[%s]", line.c_str())); break; } } else if("connected" == words[2]) { if(!_connected) xorp_throw(InvalidString, c_format("No TCP session established")); } else if("established" == words[2]) { if(!_established) xorp_throw(InvalidString, c_format("No BGP session established")); } else if("idle" == words[2]) { if(_connected) xorp_throw(InvalidString, c_format("Session established")); } else xorp_throw(InvalidString, c_format( "\"queue\" \"established\" \"idle\"accepted not <%s>\n[%s]", words[2].c_str(), line.c_str())); } void mrtd_traffic_dump(const uint8_t *buf, const size_t len , const TimeVal tv, const string fname) { FILE *fp = fopen(fname.c_str(), "a"); if(0 == fp) XLOG_FATAL("fopen of %s failed: %s", fname.c_str(), strerror(errno)); mrt_header header; header.time = htonl(tv.sec()); header.type = htons(16); header.subtype = htons(1); header.length = htonl(len + sizeof(mrt_update)); if(fwrite(&header, sizeof(header), 1, fp) != 1) XLOG_FATAL("fwrite of %s failed: %s", fname.c_str(), strerror(errno)); mrt_update update; memset(&update, 0, sizeof(update)); update.af = htons(1); /* IPv4 */ if(fwrite(&update, sizeof(update), 1, fp) != 1) XLOG_FATAL("fwrite of %s failed: %s", fname.c_str(), strerror(errno)); if(fwrite(buf, len, 1, fp) != 1) XLOG_FATAL("fwrite of %s failed: %s", fname.c_str(), strerror(errno)); fclose(fp); } template void mrtd_routview_dump(const UpdatePacket* p, const IPNet& net, const TimeVal tv, const string fname, const int sequence, const BGPPeerData *peerdata) { FILE *fp = fopen(fname.c_str(), "a"); if(0 == fp) XLOG_FATAL("fopen of %s failed: %s", fname.c_str(), strerror(errno)); /* ** Figure out the total length of the attributes. */ size_t pa_len = BGPPacket::MAXPACKETSIZE; uint16_t length = 0; #if 0 list ::const_iterator pai; for (pai = p->pa_list().begin(); pai != p->pa_list().end(); pai++) { const PathAttribute* pa; pa = *pai; uint8_t buf[BGPPacket::MAXPACKETSIZE]; size_t pa_len = BGPPacket::MAXPACKETSIZE; XLOG_ASSERT(pa->encode(buf, pa_len, peerdata)); length += pa_len; } #endif uint8_t buf[BGPPacket::MAXPACKETSIZE]; p->encode(buf, pa_len, peerdata); length = pa_len; /* ** Due to alignment problems I can't use a structure overlay. */ uint8_t viewbuf[18 + A::addr_bytelen()], *ptr; mrt_header header; header.time = /*htonl(tv.sec())*/0; header.type = htons(12); if(4 == A::ip_version()) header.subtype = htons(AFI_IPV4); else if(6 == A::ip_version()) header.subtype = htons(AFI_IPV6); else XLOG_FATAL("unknown ip version %d", XORP_UINT_CAST(A::ip_version())); header.length = htonl(length + sizeof(viewbuf)); if(fwrite(&header, sizeof(header), 1, fp) != 1) XLOG_FATAL("fwrite of %s failed: %s", fname.c_str(), strerror(errno)); memset(&viewbuf[0], 0, sizeof(viewbuf)); ptr = &viewbuf[0]; // View number embed_16(ptr, 0); ptr += 2; // Sequence number embed_16(ptr, sequence); ptr += 2; // Prefix net.masked_addr().copy_out(ptr); ptr += A::addr_bytelen(); // Prefix length *reinterpret_cast(ptr) = net.prefix_len(); ptr += 1; // Status *reinterpret_cast(ptr) = 0x1; ptr += 1; // Uptime embed_32(ptr, tv.sec()); ptr += 4; // Peer address embed_32(ptr, 0); ptr += 4; // Peer AS embed_16(ptr, 0); ptr += 2; // Attribute length embed_16(ptr, length); ptr += 2; XLOG_ASSERT(ptr == &viewbuf[sizeof(viewbuf)]); if(fwrite(&viewbuf[0], sizeof(viewbuf), 1, fp) != 1) XLOG_FATAL("fwrite of %s failed: %s", fname.c_str(), strerror(errno)); #if 0 for (pai = p->pa_list().begin(); pai != p->pa_list().end(); pai++) { const PathAttribute* pa; pa = *pai; uint8_t buf[BGPPacket::MAXPACKETSIZE]; size_t pa_len = BGPPacket::MAXPACKETSIZE; XLOG_ASSERT(pa->encode(buf, pa_len, peerdata)); if(fwrite(buf, pa_len, 1, fp) != 1) XLOG_FATAL("fwrite of %s failed: %s", fname.c_str(), strerror(errno)); } #endif if(fwrite(buf, pa_len, 1, fp) != 1) XLOG_FATAL("fwrite of %s failed: %s", fname.c_str(), strerror(errno)); fclose(fp); } void text_traffic_dump(const uint8_t *buf, const size_t len, const TimeVal, const string fname, const BGPPeerData *peerdata) { FILE *fp = fopen(fname.c_str(), "a"); if(0 == fp) XLOG_FATAL("fopen of %s failed: %s", fname.c_str(), strerror(errno)); fprintf(fp, "%s", bgppp(buf, len, peerdata).c_str()); fclose(fp); } template void mrtd_routeview_dump(const UpdatePacket* p, const IPNet& net, const TimeVal& tv, const string fname, int *sequence, const BGPPeerData *peerdata) { mrtd_routview_dump(p, net, tv, fname, *sequence, peerdata); (*sequence)++; } template void mrtd_debug_dump(const UpdatePacket* p, const IPNet& /*net*/, const TimeVal& tv, const string fname, const BGPPeerData *peerdata) { size_t len = 4096; uint8_t buf[4096]; assert(p->encode(buf, len, peerdata)); mrtd_traffic_dump(buf, len , tv, fname); } template void text_debug_dump(const UpdatePacket* p, const IPNet& net, const TimeVal& tv, const string fname) { FILE *fp = fopen(fname.c_str(), "a"); if(0 == fp) XLOG_FATAL("fopen of %s failed: %s", fname.c_str(), strerror(errno)); fprintf(fp, "%s\n%s\n%s\n", net.str().c_str(), tv.pretty_print().c_str(), p->str().c_str()); fclose(fp); } void mrtd_replay_dump(const UpdatePacket* p, const TimeVal& tv, const string fname, const BGPPeerData *peerdata) { size_t len = BGPPacket::MAXPACKETSIZE; uint8_t buf[BGPPacket::MAXPACKETSIZE]; XLOG_ASSERT(p->encode(buf, len, peerdata)); mrtd_traffic_dump(buf, len, tv, fname); } void text_replay_dump(const UpdatePacket* p, const TimeVal& /*tv*/, const string fname) { FILE *fp = fopen(fname.c_str(), "a"); if(0 == fp) XLOG_FATAL("fopen of %s failed: %s", fname.c_str(), strerror(errno)); fprintf(fp, "%s\n", p->str().c_str()); fclose(fp); } /* ** peer dump ** 0 1 2 3 4 ** ** ** 5 6 */ void Peer::dump(const string& line, const vector& words) throw(InvalidString) { if(words.size() < 6) xorp_throw(InvalidString, c_format("Insufficient arguments:\n[%s]", line.c_str())); /* ** Each peer holds two tries. One holds updates sent the other ** holds updates received. Determine which trie we are about to ** operate on. */ Trie *op; Dumper *dumper; if("sent" == words[2]) { op = &_trie_sent; dumper = &_traffic_sent; } else if("recv" == words[2]) { op = &_trie_recv; dumper = &_traffic_recv; } else xorp_throw(InvalidString, c_format("\"sent\" or \"recv\" accepted not <%s>\n[%s]", words[2].c_str(), line.c_str())); bool mrtd; if("mrtd" == words[3]) { mrtd = true; } else if("text" == words[3]) { mrtd = false; } else xorp_throw(InvalidString, c_format("\"mrtd\" or \"text\" accepted not <%s>\n[%s]", words[3].c_str(), line.c_str())); bool ipv4 = true; if("ipv4" == words[4]) { ipv4 = true; } else if("ipv6" == words[4]) { ipv4 = false; } else xorp_throw(InvalidString, c_format("\"ipv4\" or \"ipv6\" accepted not <%s>\n[%s]", words[4].c_str(), line.c_str())); string filename; if(words.size() == 7) filename = words[6]; #ifdef HOST_OS_WINDOWS // // If run from an MSYS shell, we need to perform UNIX->NT path // conversion and expansion of /tmp by ourselves. // filename = unix_path_to_native(filename); static const char tmpdirprefix[] = "\\tmp\\"; if (0 == _strnicmp(filename.c_str(), tmpdirprefix, strlen(tmpdirprefix))) { char tmpexp[MAXPATHLEN]; size_t size = GetTempPathA(sizeof(tmpexp), tmpexp); if (size == 0 || size >= sizeof(tmpexp)) { xorp_throw(InvalidString, c_format("Internal error during tmpdir expansion")); } filename.replace(0, strlen(tmpdirprefix), string(tmpexp)); } #endif if("traffic" == words[5]) { if("" == filename) { dumper->release(); return; } if(mrtd) *dumper = callback(mrtd_traffic_dump, filename); else *dumper = callback(text_traffic_dump, filename, (const BGPPeerData*)_peerdata); } else if("routeview" == words[5]) { if("" == filename) { xorp_throw(InvalidString, c_format("no filename provided\n[%s]", line.c_str())); } int sequence = 0; if(ipv4) { Trie::TreeWalker_ipv4 tw_ipv4; if(mrtd) tw_ipv4 = callback(mrtd_routeview_dump, filename, &sequence, (const BGPPeerData*)_peerdata); else tw_ipv4 = callback(text_debug_dump, filename); op->tree_walk_table(tw_ipv4); } else { Trie::TreeWalker_ipv6 tw_ipv6; if(mrtd) tw_ipv6 = callback(mrtd_routeview_dump, filename, &sequence, (const BGPPeerData*)_peerdata); else tw_ipv6 = callback(text_debug_dump, filename); op->tree_walk_table(tw_ipv6); } } else if("replay" == words[5]) { if("" == filename) { xorp_throw(InvalidString, c_format("no filename provided\n[%s]", line.c_str())); } Trie::ReplayWalker rw; if(mrtd) rw = callback(mrtd_replay_dump, filename, (const BGPPeerData*)_peerdata); else rw = callback(text_replay_dump, filename); op->replay_walk(rw, _peerdata); } else if("debug" == words[5]) { if("" == filename) { xorp_throw(InvalidString, c_format("no filename provided\n[%s]", line.c_str())); } if(ipv4) { Trie::TreeWalker_ipv4 tw_ipv4; if(mrtd) tw_ipv4 = callback(mrtd_debug_dump, filename, (const BGPPeerData*)_peerdata); else tw_ipv4 = callback(text_debug_dump, filename); op->tree_walk_table(tw_ipv4); } else { Trie::TreeWalker_ipv6 tw_ipv6; if(mrtd) tw_ipv6 = callback(mrtd_debug_dump, filename, (const BGPPeerData*)_peerdata); else tw_ipv6 = callback(text_debug_dump, filename); op->tree_walk_table(tw_ipv6); } } else xorp_throw(InvalidString, c_format( "\"traffic\" or \"routeview\" or \"replay\" or \"debug\" accepted not <%s>\n[%s]", words[5].c_str(), line.c_str())); } bool compare_packets(const BGPPacket *base_one, const BGPPacket *base_two) { if(base_one->type() != base_two->type()) return false; switch(base_one->type()) { case MESSAGETYPEOPEN: { const OpenPacket *one = dynamic_cast(base_one); const OpenPacket *two = dynamic_cast(base_two); return *one == *two; } break; case MESSAGETYPEUPDATE: { const UpdatePacket *one = dynamic_cast(base_one); const UpdatePacket *two = dynamic_cast(base_two); return *one == *two; } break; case MESSAGETYPENOTIFICATION: { const NotificationPacket *one = dynamic_cast(base_one); const NotificationPacket *two = dynamic_cast(base_two); return *one == *two; } break; case MESSAGETYPEKEEPALIVE: { const KeepAlivePacket *one = dynamic_cast(base_one); const KeepAlivePacket *two = dynamic_cast(base_two); return *one == *two; } break; default: XLOG_FATAL("Unexpected BGP message type %d", base_one->type()); } return false; } /* ** We just received a BGP message. Check our expected queue for a match. */ void Peer::check_expect(BGPPacket *rec) { /* ** If its already gone bad just return. */ if(!_expect._ok) return; /* ** If there is nothing on the queue return. */ if(_expect._list.empty()) return; const BGPPacket *exp = _expect._list.front(); debug_msg("Expecting: %s\n", exp->str().c_str()); if(compare_packets(rec, exp)) { _expect._list.pop_front(); delete exp; } else { debug_msg("Received packet %s did not match Expected\n", rec->str().c_str()); _expect._ok = false; /* ** Need to go through this performance in order to copy a ** packet. */ size_t rec_len = BGPPacket::MAXPACKETSIZE; uint8_t *rec_buf = new uint8_t[BGPPacket::MAXPACKETSIZE]; XLOG_ASSERT(rec->encode(rec_buf, rec_len, _peerdata)); switch(rec->type()) { case MESSAGETYPEOPEN: { OpenPacket *pac = new OpenPacket(rec_buf, rec_len); _expect._bad = pac; } break; case MESSAGETYPEUPDATE: { UpdatePacket *pac = new UpdatePacket(rec_buf, rec_len, _peerdata, 0, false); _expect._bad = pac; } break; case MESSAGETYPENOTIFICATION: { NotificationPacket *pac = new NotificationPacket(rec_buf, rec_len); _expect._bad = pac; } break; case MESSAGETYPEKEEPALIVE: _expect._bad = new KeepAlivePacket(rec_buf, rec_len); break; default: XLOG_FATAL("Unexpected BGP message type %d", rec->type()); } delete [] rec_buf; } } void Peer::xrl_callback(const XrlError& error, const char *comment) { debug_msg("callback %s %s\n", comment, error.str().c_str()); XLOG_ASSERT(0 != _busy); _busy--; if(XrlError::OKAY() != error) { XLOG_WARNING("callback: %s %s", comment, error.str().c_str()); _session = false; _established = false; } } void Peer::xrl_callback_connected(const XrlError& error, const char *comment) { XLOG_INFO("%s: callback_connected, OK: %i %s %s\n", _peername.c_str(), (int)(error.isOK()), comment, error.str().c_str()); if (error.isOK()) { _connected = true; } else { _connected = false; } xrl_callback(error, comment); } /* ** This method receives data from the BGP process under test via the ** test_peer. The test_peer attempts to packetise the data into BGP ** messages. */ void Peer::datain(const bool& status, const TimeVal& tv, const vector& data) { debug_msg("status: %d secs: %lu micro: %lu data length: %u\n", status, (unsigned long)tv.sec(), (unsigned long)tv.usec(), XORP_UINT_CAST(data.size())); /* ** A bgp error has occured. */ if(false == status) { /* ** BGP has closed the connection to the test_peer. */ if(0 == data.size()) { XLOG_WARNING("TCP connection from test_peer: %s to: %s closed", _peername.c_str(), _target_hostname.c_str()); _connected = false; _session = false; _established = false; return; } XLOG_FATAL("Bad BGP message received by test_peer: %s from: %s", _peername.c_str(), _target_hostname.c_str()); } size_t length = data.size(); uint8_t *buf = new uint8_t[length]; for(size_t i = 0; i < length; i++) buf[i] = data[i]; uint8_t type = extract_8(buf + BGPPacket::TYPE_OFFSET); if (!_traffic_recv.is_empty()) _traffic_recv->dispatch(buf, length, tv); try { switch(type) { case MESSAGETYPEOPEN: { debug_msg("OPEN Packet RECEIVED\n"); OpenPacket pac(buf, length); debug_msg("%s", pac.str().c_str()); if(_session && !_established) { if(_passive) { send_open(); } else { send_keepalive(); _established = true; } } check_expect(&pac); } break; case MESSAGETYPEKEEPALIVE: { debug_msg("KEEPALIVE Packet RECEIVED %u\n", XORP_UINT_CAST(length)); KeepAlivePacket pac(buf, length); debug_msg("%s", pac.str().c_str()); /* XXX ** Send any received keepalive right back. ** At the moment we are not bothering to maintain a timer ** to send keepalives. An incoming keepalive prompts a ** keepalive response. */ if(_keepalive || (_session && !_established)) { XrlTestPeerV0p1Client test_peer(_xrlrouter); debug_msg("KEEPALIVE Packet SENT\n"); _busy++; if(!test_peer.send_send(_peername.c_str(), data, callback(this, &Peer::xrl_callback, "keepalive"))) XLOG_FATAL("send_send failed"); } _established = true; check_expect(&pac); } break; case MESSAGETYPEUPDATE: { debug_msg("UPDATE Packet RECEIVED\n"); UpdatePacket pac(buf, length, _peerdata, 0, false); debug_msg("%s", pac.str().c_str()); /* ** Save the update message in the receive trie. */ _trie_recv.process_update_packet(tv, buf, length, _peerdata); check_expect(&pac); } break; case MESSAGETYPENOTIFICATION: { debug_msg("NOTIFICATION Packet RECEIVED\n"); NotificationPacket pac(buf, length); debug_msg("%s", pac.str().c_str()); check_expect(&pac); } break; default: /* ** Send a notification to the peer. This is a bad message type. */ XLOG_ERROR("Unknown packet type %d", type); } } catch(CorruptMessage c) { /* ** This peer had sent us a bad message. */ XLOG_WARNING("From peer %s: %s", _peername.c_str(), c.why().c_str()); } delete [] buf; } void Peer::datain_error(const string& reason) { UNUSED(reason); XLOG_WARNING("Error on TCP connection from test_peer: %s to %s reason: %s", _peername.c_str(), _target_hostname.c_str(), reason.c_str()); } void Peer::datain_closed() { XLOG_WARNING("TCP connection from test_peer: %s to %s closed", _peername.c_str(), _target_hostname.c_str()); _connected = false; _session = false; _established = false; } void Peer::send_message(const uint8_t *buf, const size_t len, SMCB cb) { debug_msg("len: %u\n", XORP_UINT_CAST(len)); if(!_traffic_sent.is_empty()) { TimeVal tv; _eventloop->current_time(tv); _traffic_sent->dispatch(buf, len, tv); } vector v(len); for(size_t i = 0; i < len; i++) v[i] = buf[i]; XrlTestPeerV0p1Client test_peer(_xrlrouter); if(!test_peer.send_send(_peername.c_str(), v, cb)) XLOG_FATAL("send_send failed"); } void Peer::send_open() { /* ** Create an open packet and send it in. */ OpenPacket bgpopen(_as, _id, _holdtime); if(_ipv6) bgpopen.add_parameter( new BGPMultiProtocolCapability(AFI_IPV6, SAFI_UNICAST)); if(_localdata->use_4byte_asnums()) bgpopen.add_parameter(new BGP4ByteASCapability(_as)); size_t len = BGPPacket::MAXPACKETSIZE; uint8_t *buf = new uint8_t[BGPPacket::MAXPACKETSIZE]; XLOG_ASSERT(bgpopen.encode(buf, len, _peerdata)); debug_msg("OPEN Packet SENT len:%u\n%s", (uint32_t)len, bgpopen.str().c_str()); _busy++; send_message(buf, len, callback(this, &Peer::xrl_callback, "open")); } void Peer::send_keepalive() { /* ** Create an keepalive packet and send it in. */ KeepAlivePacket keepalive; size_t len = BGPPacket::MAXPACKETSIZE; uint8_t *buf = new uint8_t[BGPPacket::MAXPACKETSIZE]; XLOG_ASSERT(keepalive.encode(buf, len, _peerdata)); debug_msg("KEEPALIVE Packet SENT\n"); _busy++; send_message(buf, len, callback(this, &Peer::xrl_callback, "keepalive")); } /* ** The standard BGP code that deals with path attributes contains ** fairly stringent checking. For testing we want to be able to create ** arbitary path attributes. */ class AnyAttribute : public PathAttribute { public: /* ** The attr string should be a comma separated list of numbers. */ AnyAttribute(const char *attr) throw(InvalidString) // In order to protect against accidents in the BGP code, // PathAttribute does not have a default constructor. However, // we need to manipulate a PathAttribute so pass in a valid // constructor argument. : PathAttribute(&_valid[0]) { _init_string = attr; string line = attr; vector v; tokenize(line, v, ","); _data = new uint8_t [v.size()]; for(size_t i = 0; i < v.size(); i++) { _data[i] = strtol(v[i].c_str(), 0, 0); // fprintf(stderr, "%#x ", _data[i]); } // fprintf(stderr, "\n"); _size = length(_data); if (_data[0] & Extended) { _wire_size = 4 + _size; } else { _wire_size = 3 + _size; } _type = _data[1]; }; PathAttribute *clone() const { return new AnyAttribute(_init_string.c_str()); } bool encode(uint8_t *buf, size_t &wire_size, const BGPPeerData* peerdata) const { debug_msg("AnyAttribute::encode\n"); UNUSED(peerdata); // check it will fit in the buffer if (wire_size < _wire_size) return false; memcpy(buf, _data, _wire_size); wire_size = _wire_size; return true; } private: uint8_t *_data; size_t _size; size_t _wire_size; static const uint8_t _valid[]; string _init_string; }; const uint8_t AnyAttribute::_valid[] = {0x80|0x40, 255, 1, 1}; /* ** The input is a comma separated list of numbers that are turned into ** an array. */ PathAttribute * Peer::path_attribute(const char *) const throw(InvalidString) { const uint8_t path[] = {0x80|0x40, 255, 1, 1}; uint16_t max_len = sizeof(path); size_t actual_length; return PathAttribute::create(&path[0], max_len, actual_length, _peerdata, 4); } /** * Helper function to decipher a community string */ uint32_t community_interpret(const string& community) { char *endptr; uint32_t val = strtoul(community.c_str(), &endptr, 0); if (0 == *endptr) return val; if ("NO_EXPORT" == community) return CommunityAttribute::NO_EXPORT; else if ("NO_ADVERTISE" == community) return CommunityAttribute::NO_ADVERTISE; else if ("NO_EXPORT_SUBCONFED" == community) return CommunityAttribute::NO_EXPORT_SUBCONFED; else xorp_throw(InvalidString, c_format("Invalid community name %s", community.c_str())); return val; } /* ** Generate a BGP message from the textual description. ** Note: It is up to the caller of this method to delete the packet ** that has been created. */ const BGPPacket * Peer::packet(const string& line, const vector& words, int index) const throw(InvalidString) { BGPPacket *pac = 0; try { if("notify" == words[index]) { switch(words.size() - (index + 1)) { case 1: pac = new NotificationPacket(atoi(words[index + 1].c_str())); break; case 2: pac = new NotificationPacket(atoi(words[index + 1].c_str()), atoi(words[index + 2].c_str())); break; default: { int errlen = words.size() - (index + 3); if (errlen < 1) xorp_throw(InvalidString, c_format( "Incorrect number of arguments to notify:\n[%s]", line.c_str())); uint8_t buf[errlen]; for (int i=0; i< errlen; i++) { int value = atoi(words[index + 3 + i].c_str()); if (value < 0 || value > 255) xorp_throw(InvalidString, c_format( "Incorrect byte value to notify:\n[%s]", line.c_str())); buf[i] = (uint8_t)value; pac = new NotificationPacket(atoi(words[index+1].c_str()), atoi(words[index+2].c_str()), buf, errlen); } } } } else if("update" == words[index]) { size_t size = words.size(); #if 0 if(0 != ((size - (index + 1)) % 2)) xorp_throw(InvalidString, c_format("Incorrect number of arguments to update:\n[%s]", line.c_str())); #endif UpdatePacket *bgpupdate = new UpdatePacket(); MPReachNLRIAttribute mpipv6_nlri(SAFI_UNICAST); MPUNReachNLRIAttribute mpipv6_withdraw(SAFI_UNICAST); ClusterListAttribute cl; CommunityAttribute community; for(size_t i = index + 1; i < size; i += 2) { debug_msg("name: %s value: <%s>\n", words[i].c_str(), words[i + 1].c_str()); if("origin" == words[i]) { OriginAttribute oa(static_cast (atoi(words[i + 1].c_str()))); bgpupdate->add_pathatt(oa); } else if("aspath" == words[i]) { string aspath = words[i+1]; if ("empty" == aspath) aspath = ""; ASPathAttribute aspa(ASPath(aspath.c_str())); bgpupdate->add_pathatt(aspa); debug_msg("aspath: %s\n", ASPath(aspath.c_str()).str().c_str()); } else if("as4path" == words[i]) { string as4path = words[i+1]; if ("empty" == as4path) as4path = ""; AS4PathAttribute aspa(AS4Path(as4path.c_str())); bgpupdate->add_pathatt(aspa); debug_msg("as4path: %s\n", AS4Path(as4path.c_str()).str().c_str()); } else if("nexthop" == words[i]) { IPv4NextHopAttribute nha(IPv4((const char*) (words[i+1].c_str()))); bgpupdate->add_pathatt(nha); } else if("nexthop6" == words[i]) { mpipv6_nlri.set_nexthop(IPv6((const char*) (words[i+1].c_str()))); } else if("localpref" == words[i]) { LocalPrefAttribute lpa(atoi(words[i+1].c_str())); bgpupdate->add_pathatt(lpa); } else if("nlri" == words[i]) { BGPUpdateAttrib upa(IPv4Net((const char*) (words[i+1].c_str()))); bgpupdate->add_nlri(upa); } else if("nlri6" == words[i]) { mpipv6_nlri.add_nlri(words[i+1].c_str()); } else if("withdraw" == words[i]) { BGPUpdateAttrib upa(IPv4Net((const char*) (words[i+1].c_str()))); bgpupdate->add_withdrawn(upa); } else if("withdraw6" == words[i]) { mpipv6_withdraw.add_withdrawn(IPv6Net(words[i+1].c_str())); } else if("med" == words[i]) { MEDAttribute ma(atoi(words[i+1].c_str())); bgpupdate->add_pathatt(ma); } else if("originatorid" == words[i]) { OriginatorIDAttribute oid(IPv4((const char *) (words[i+1].c_str()))); bgpupdate->add_pathatt(oid); } else if("clusterlist" == words[i]) { cl.prepend_cluster_id(IPv4((const char *) (words[i+1].c_str()))); } else if("community" == words[i]) { community.add_community(community_interpret(words[i+1])); } else if("as4aggregator" == words[i]) { IPv4 as4aggid(words[i+1].c_str()); AsNum as4agg(words[i+2]); AS4AggregatorAttribute asaggatt(as4aggid, as4agg); bgpupdate->add_pathatt(asaggatt); debug_msg("as4aggregator: %s %s\n", as4aggid.str().c_str(), as4agg.str().c_str()); i++; } else if("pathattr" == words[i]) { AnyAttribute aa(words[i+1].c_str()); bgpupdate->add_pathatt(aa); } else xorp_throw(InvalidString, c_format("Illegal argument to update: <%s>\n[%s]", words[i].c_str(), line.c_str())); } if(!mpipv6_nlri.nlri_list().empty()) { //XXX mpipv6_nlri.encode(); //don't think we still need this bgpupdate->add_pathatt(mpipv6_nlri); } if(!mpipv6_withdraw.wr_list().empty()) { //XXX mpipv6_withdraw.encode();//don't think we still need this bgpupdate->add_pathatt(mpipv6_withdraw); } if(!cl.cluster_list().empty()) { bgpupdate->add_pathatt(cl); } if(!community.community_set().empty()) { bgpupdate->add_pathatt(community); } pac = bgpupdate; } else if("open" == words[index]) { size_t size = words.size(); if(0 != ((size - (index + 1)) % 2)) xorp_throw(InvalidString, c_format("Incorrect number of arguments to open:\n[%s]", line.c_str())); string asnum; string bgpid; string holdtime; string afi; string safi; for(size_t i = index + 1; i < size; i += 2) { debug_msg("name: %s value: %s\n", words[i].c_str(), words[i + 1].c_str()); if("asnum" == words[i]) { asnum = words[i + 1]; } else if("bgpid" == words[i]) { bgpid = words[i + 1]; } else if("holdtime" == words[i]) { holdtime = words[i + 1]; } else if("afi" == words[i]) { afi = words[i + 1]; } else if("safi" == words[i]) { safi = words[i + 1]; } else xorp_throw(InvalidString, c_format("Illegal argument to open: <%s>\n[%s]", words[i].c_str(), line.c_str())); } if("" == asnum) { xorp_throw(InvalidString, c_format("AS number not supplied to open:\n[%s]", line.c_str())); } if("" == bgpid) { xorp_throw(InvalidString, c_format("BGP ID not supplied to open:\n[%s]", line.c_str())); } if("" == holdtime) { xorp_throw(InvalidString, c_format("Holdtime not supplied to open:\n[%s]", line.c_str())); } if(("" != afi && "" == safi) || ("" == afi && "" != safi)) { xorp_throw(InvalidString, c_format("Both AFI and SAFI must be set/not set to open:\n[%s]", line.c_str())); } OpenPacket *open = new OpenPacket(AsNum( static_cast(atoi(asnum.c_str()))), IPv4(bgpid.c_str()), atoi(holdtime.c_str())); if("" != afi && "" != safi) { open->add_parameter( new BGPMultiProtocolCapability( (Afi)atoi(afi.c_str()), (Safi)atoi(safi.c_str()))); } pac = open; } else if("keepalive" == words[index]) { pac = new KeepAlivePacket(); } else xorp_throw(InvalidString, c_format("\"notify\" or" " \"update\" or" " \"open\" or" " \"keepalive\" accepted" " not <%s>\n[%s]", words[index].c_str(), line.c_str())); } catch(CorruptMessage c) { xorp_throw(InvalidString, c_format("Unable to construct packet " "%s\n[%s])", c.why().c_str(), line.c_str())); } debug_msg("%s\n", pac->str().c_str()); return pac; } xorp/bgp/harness/test_route_flap_damping1.sh0000775000076400007640000001763711421137511021423 0ustar greearbgreearb#!/usr/bin/env bash # # $XORP: xorp/bgp/harness/test_route_flap_damping1.sh,v 1.3 2006/08/16 22:10:14 atanu Exp $ # # # Test BGP Route Flap Damping (RFC 2439) # # This script started with no arguments will start all required process and # terminate them at the end of the tests. # # Preconditons # 1) Run a finder process # 2) Run "../fea/xorp_fea_dummy" # 3) Run xorp "../rib/xorp_rib" # 4) Run xorp "../xorp_bgp" # 5) Run "./test_peer -s peer1" # 6) Run "./test_peer -s peer2" # 7) Run "./test_peer -s peer3" # 8) Run "./coord" # # Peer 1 E-BGP # Peer 2 E-BGP # Peer 3 I-BGP set -e . ./setup_paths.sh onexit() { last=$? if [ $last = "0" ] then echo "$0: Tests Succeeded (BGP route flapping: $TESTS)" else echo "$0: Tests Failed (BGP route flapping: $TESTS)" fi trap '' 0 2 } trap onexit 0 2 if [ "X${srcdir}" = "X" ] ; then srcdir=`dirname $0` ; fi . ${srcdir}/xrl_shell_funcs.sh "" . $BGP_FUNCS "" . $RIB_FUNCS "" HOST=127.0.0.1 PORT1=10001 PORT2=10002 PORT3=10003 PORT1_IPV6=10004 PORT2_IPV6=10005 PORT3_IPV6=10006 PEER_PORT1=20001 PEER_PORT2=20002 PEER_PORT3=20003 PEER_PORT1_IPV6=20004 PEER_PORT2_IPV6=20005 PEER_PORT3_IPV6=20006 AS=65008 USE4BYTEAS=false PEER1_AS=65001 PEER2_AS=65002 PEER3_AS=$AS PEER1_AS_IPV6=65001 PEER2_AS_IPV6=65002 PEER3_AS_IPV6=$AS HOLDTIME=0 # Next Hops NH1_IPv4=172.16.1.2 NH2_IPV4=172.16.2.2 NH3_IPv4=172.16.3.2 NH1_IPV6=40:40:40:40:40:40:40:42 NH2_IPV6=50:50:50:50:50:50:50:52 NH3_IPV6=60:60:60:60:60:60:60:62 NEXT_HOP=192.150.187.78 ID=192.150.187.78 configure_bgp() { LOCALHOST=$HOST AS=65008 local_config $AS $ID $USE4BYTEAS # Don't try and talk to the rib. register_rib "" # EBGP - IPV4 PEER=$HOST PORT=$PORT1;PEER_PORT=$PEER_PORT1;PEER_AS=$PEER1_AS IPTUPLE="$LOCALHOST $PORT $PEER $PEER_PORT" add_peer lo $IPTUPLE $PEER_AS $NEXT_HOP $HOLDTIME set_parameter $IPTUPLE MultiProtocol.IPv4.Unicast true enable_peer $IPTUPLE # EBGP - IPV4 PEER=$HOST PORT=$PORT2;PEER_PORT=$PEER_PORT2;PEER_AS=$PEER2_AS IPTUPLE="$LOCALHOST $PORT $PEER $PEER_PORT" add_peer lo $IPTUPLE $PEER_AS $NEXT_HOP $HOLDTIME set_parameter $IPTUPLE MultiProtocol.IPv4.Unicast true enable_peer $IPTUPLE # IEBGP - IPV4 PEER=$HOST PORT=$PORT3;PEER_PORT=$PEER_PORT3;PEER_AS=$PEER3_AS IPTUPLE="$LOCALHOST $PORT $PEER $PEER_PORT" add_peer lo $IPTUPLE $PEER_AS $NEXT_HOP $HOLDTIME set_parameter $IPTUPLE MultiProtocol.IPv4.Unicast true enable_peer $IPTUPLE # IBGP - IPV6 PEER=$HOST PORT=$PORT1_IPV6;PEER_PORT=$PEER_PORT1_IPV6;PEER_AS=$PEER1_AS_IPV6 IPTUPLE="$LOCALHOST $PORT $PEER $PEER_PORT" add_peer lo $IPTUPLE $PEER_AS $NEXT_HOP $HOLDTIME set_parameter $IPTUPLE MultiProtocol.IPv6.Unicast true enable_peer $IPTUPLE # IBGP - IPV6 PEER=$HOST PORT=$PORT2_IPV6;PEER_PORT=$PEER_PORT2_IPV6;PEER_AS=$PEER2_AS_IPV6 IPTUPLE="$LOCALHOST $PORT $PEER $PEER_PORT" add_peer lo $IPTUPLE $PEER_AS $NEXT_HOP $HOLDTIME set_parameter $IPTUPLE MultiProtocol.IPv6.Unicast true enable_peer $IPTUPLE # IBGP - IPV6 PEER=$HOST PORT=$PORT3_IPV6;PEER_PORT=$PEER_PORT3_IPV6;PEER_AS=$PEER3_AS_IPV6 IPTUPLE="$LOCALHOST $PORT $PEER $PEER_PORT" add_peer lo $IPTUPLE $PEER_AS $NEXT_HOP $HOLDTIME set_parameter $IPTUPLE MultiProtocol.IPv6.Unicast true enable_peer $IPTUPLE } configure_bgp_damping_default() { half_life=15 max_suppress=60 reuse=750 suppress=3000 set_damping $half_life $max_suppress $reuse $suppress false } config_peers_ipv4() { coord reset coord target $HOST $PORT1 coord initialise attach peer1 coord peer1 establish AS $PEER1_AS \ holdtime $HOLDTIME \ id 10.10.10.1 \ keepalive false coord peer1 assert established coord target $HOST $PORT2 coord initialise attach peer2 coord peer2 establish AS $PEER2_AS \ holdtime $HOLDTIME \ id 10.10.10.2 \ keepalive false coord peer2 assert established coord target $HOST $PORT3 coord initialise attach peer3 coord peer3 establish AS $PEER3_AS \ holdtime $HOLDTIME \ id 10.10.10.3 \ keepalive false coord peer3 assert established } test1() { echo "TEST1 - Establish three peerings" config_peers_ipv4 } test2() { echo "TEST2 - Bugzilla BUG #471" echo " 1) Introduce a route on an E-BGP peering." echo " 2) Enable route flap damping on the BGP process." echo " 3) Withdraw orignal route. (used to cause a BGP assert)" config_peers_ipv4 PACKET="packet update origin 2 aspath $PEER1_AS nexthop $NEXT_HOP nlri 10.10.10.0/24" PACKET_IBGP_PEER="packet update origin 2 aspath $PEER1_AS nexthop $NEXT_HOP localpref 100 nlri 10.10.10.0/24" PACKET_EBGP_PEER="packet update origin 2 aspath $AS,$PEER1_AS nexthop $NEXT_HOP med 1 nlri 10.10.10.0/24" coord peer1 expect $PACKET coord peer2 expect $PACKET_EBGP_PEER coord peer3 expect $PACKET_IBGP_PEER coord peer1 send $PACKET sleep 2 coord peer1 assert queue 1 coord peer2 assert queue 0 coord peer3 assert queue 0 configure_bgp_damping_default WITHDRAW="packet update withdraw 10.10.10.0/24" coord peer1 expect $WITHDRAW coord peer2 expect $WITHDRAW coord peer3 expect $WITHDRAW coord peer1 send $WITHDRAW sleep 2 coord peer1 assert queue 2 coord peer2 assert queue 0 coord peer3 assert queue 0 # At the end of the test we expect all the peerings to still be established. coord peer1 assert established coord peer2 assert established coord peer3 assert established } test3() { echo "TEST3 - Variation on test2" echo " 1) Introduce a route on an E-BGP peering." echo " 2) Enable route flap damping on the BGP process." echo " 3) Introduce same route (used to cause a BGP assert)" config_peers_ipv4 PACKET="packet update origin 2 aspath $PEER1_AS nexthop $NEXT_HOP nlri 10.10.10.0/24" PACKET_IBGP_PEER="packet update origin 2 aspath $PEER1_AS nexthop $NEXT_HOP localpref 100 nlri 10.10.10.0/24" PACKET_EBGP_PEER="packet update origin 2 aspath $AS,$PEER1_AS nexthop $NEXT_HOP med 1 nlri 10.10.10.0/24" coord peer1 expect $PACKET coord peer2 expect $PACKET_EBGP_PEER coord peer3 expect $PACKET_IBGP_PEER coord peer1 send $PACKET sleep 2 coord peer1 assert queue 1 coord peer2 assert queue 0 coord peer3 assert queue 0 configure_bgp_damping_default coord peer1 expect $PACKET coord peer2 expect $PACKET_EBGP_PEER coord peer3 expect $PACKET_IBGP_PEER coord peer1 send $PACKET sleep 2 coord peer1 assert queue 2 coord peer2 assert queue 0 coord peer3 assert queue 0 # At the end of the test we expect all the peerings to still be established. coord peer1 assert established coord peer2 assert established coord peer3 assert established } TESTS_NOT_FIXED='' TESTS='test1 test2 test3' # Include command line . ${srcdir}/args.sh if [ $START_PROGRAMS = "yes" ] then CXRL="$CALLXRL -r 10" runit $QUIET $VERBOSE -c "$0 -s -c $*" <&1) if [ "X${python_version}" = "X" ]; then exit 2 else export PYTHON fi # Include command line . ${srcdir}/args.sh if [ $START_PROGRAMS = "yes" ] then CXRL="$CALLXRL -r 10" runit $QUIET $VERBOSE -c "$0 -s -c $*" <> sys.stderr, "Still pending" def pending(): """ Check the previous command has completed """ ret=call_xrl("finder://coord/coord/0.1/pending") if ret == "pending:bool=false\n": return 0 else: return 1 def establish(host, myas, myid, ipv4, logfile, remove_logfile): """ Establish a session with host and conditionally log the whole session. """ coord("reset") coord("target %s 179" % host) coord("initialise attach peer1") if logfile: if remove_logfile: os.unlink(logfile) coord("peer1 dump sent mrtd ipv4 traffic %s" % logfile) coord("peer1 dump recv mrtd ipv4 traffic %s" % logfile) if ipv4: family = "" else: family = "ipv6 true" coord("peer1 establish AS %s holdtime 0 id %s keepalive false %s" % (myas, myid, family)) def inject(injectfile, count): """ Inject count update packets into the session. If count is zero inject whole file. """ if 0 == count: coord("peer1 send dump mrtd update %s" % injectfile) else: coord("peer1 send dump mrtd update %s %s" % (injectfile, count)) def stop(): """ Stop collecting data into the log file """ coord("peer1 dump sent mrtd ipv4 traffic") coord("peer1 dump recv mrtd ipv4 traffic") us= \ """\ usage: %s [-h|--help] \t -e -p peer -a AS -b BGP-ID [-i injectfile] [-c route_count] [-l logfile] \t -r \t -s \t -4 \t -6 """ def main(): def usage(): global us print us % sys.argv[0] try: opts, args = getopt.getopt(sys.argv[1:], "hei:c:p:a:b:l:rs46", \ ["help", \ "establish", \ "inject=", \ "count=", \ "peer=", \ "asnum=", \ "bgp-id=", \ "logfile=", \ "remove", \ "stop", \ "ipv4", \ "ipv6", \ ]) except getopt.GetoptError: usage() sys.exit(1) establishp = False injectp = False stopp = False ipv4 = True peer = "" asnum = "0" router_id = "10.0.0.1" injectfile = None route_count = 0 logfile = None remove_logfile = False for o, a in opts: if o in ("-h", "--help"): usage() sys.exit() if o in ("-e", "--establish"): establishp = True if o in ("-i", "--inject"): injectp = True injectfile = a if o in ("-c", "--count"): route_count = a if o in ("-p", "--peer"): peer = a if o in ("-a", "--asnum"): asnum = a if o in ("-b", "--bgp-id"): router_id = a if o in ("-l", "--logfile"): logfile = a if o in ("-r", "--remove"): remove_logfile = True if o in ("-s", "--stop"): stopp = True if o in ("-4", "--ipv4"): ipv4 = True if o in ("-6", "--ipv6"): ipv4 = False if establishp: establish(peer, asnum, router_id, ipv4, logfile, remove_logfile) if injectp: inject(injectfile, route_count) if stopp: stop() if (not establish) and (not injectp) and (not stopp): usage() sys.exit(1) if __name__ == '__main__': main() xorp/bgp/harness/test_trie.cc0000664000076400007640000004167711540224217016421 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #include "bgp/bgp_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/test_main.hh" #include "trie.hh" template struct ltstr { bool operator()(IPNet s1, IPNet s2) const { // printf("ltstr: %s %s %d\n", // s1.str().c_str(), s2.str().c_str(), s1 < s2); return s1 < s2; } }; template class Nmap { public: // typedef map, A> nmap; typedef map, A, ltstr > nmap; }; typedef vector Ulist; /* ** Walk the trie and verify that all the networks in the trie are the expected. ** ** XXX - We should also check that the nexthops are good. They are in ** the map after all. */ template void tree_walker(const UpdatePacket *p, const IPNet& net, const TimeVal&, TestInfo info, typename Nmap::nmap nlri) { DOUT(info) << info.test_name() << endl << p->str() << "net: " << net.str() << "" << endl; typename Nmap::nmap::const_iterator i; for(i = nlri.begin(); i != nlri.end(); i++) { DOUT(info) << info.test_name() << " " << (*i).first.str() << endl; if(net == (*i).first) return; } XLOG_FATAL("Net: %s not found in map", net.str().c_str()); } /* ** Used to verify that a trie is empty. If this function is called ** something is amiss. */ template void tree_walker_empty(const UpdatePacket *p, const IPNet& net, const TimeVal&, TestInfo info, A dummy_addr) { DOUT(info) << info.test_name() << endl << p->str() << "net: " << net.str() << "" << endl; XLOG_FATAL("There should be no entries in this trie\n %s:%s", p->str().c_str(), net.str().c_str()); // // XXX: Dummy address needed to fix template-related deduction problem // exposed by the Intel C++ compiler. // UNUSED(dummy_addr); } template void replay_walker(const UpdatePacket *p, const TimeVal&, TestInfo info, size_t *pos, Ulist ulist, A dummy_addr) { DOUT(info) << info.test_name() << " " << (*pos) << endl << p->str(); if(ulist.size() <= *pos) XLOG_FATAL("vector limit exceeded: %u %u", XORP_UINT_CAST(ulist.size()), XORP_UINT_CAST(*pos)); if(*p != *ulist[*pos]) XLOG_FATAL("%s NOT EQUAL TO %s", p->str().c_str(), ulist[*pos]->str().c_str()); (*pos)++; // // XXX: Dummy address needed to fix template-related deduction problem // exposed by the Intel C++ compiler. // UNUSED(dummy_addr); } template void add_nlri(UpdatePacket *p, IPNet net); template void withdraw_nlri(UpdatePacket *p, IPNet net); template void add_nexthop(UpdatePacket *p, A nexthop); template <> void add_nlri(UpdatePacket *p, IPNet net) { BGPUpdateAttrib upa(net); p->add_nlri(upa); } template <> void withdraw_nlri(UpdatePacket *p, IPNet net) { BGPUpdateAttrib upa(net); p->add_withdrawn(upa); } template <> void add_nexthop(UpdatePacket *p, IPv4 nexthop) { IPv4NextHopAttribute nha(nexthop); p->add_pathatt(nha); } template <> void add_nlri(UpdatePacket *p, IPNet net) { debug_msg("%s <%s>\n", p->str().c_str(), net.str().c_str()); /* ** Look for a multiprotocol path attribute, if there is one ** present just add the net. Otherwise add a multiprotocol path ** attribute and then add the net. Note: if we add the path ** attribute we need operate on a pointer hence the goto. */ top: MPReachNLRIAttribute *mpreach = 0; #if 0 list ::const_iterator pai; for (pai = p->pa_list().begin(); pai != p->pa_list().end(); pai++) { const PathAttribute* pa; pa = *pai; if (dynamic_cast*>(*pai)) { mpreach = dynamic_cast*>(*pai); mpreach->add_nlri(net); //mpreach->encode(); debug_msg("%s\n", p->str().c_str()); return; } } #endif mpreach = p->pa_list()->mpreach(SAFI_UNICAST); if(0 == mpreach) { MPReachNLRIAttribute mp(SAFI_UNICAST); p->add_pathatt(mp); goto top; } else { mpreach->add_nlri(net); } } template <> void withdraw_nlri(UpdatePacket *p, IPNet net) { debug_msg("%s <%s>\n", p->str().c_str(), net.str().c_str()); /* ** Look for a multiprotocol path attribute, if there is one ** present just add the net. Otherwise add a multiprotocol path ** attribute and then add the net. Note: if we add the path ** attribute we need operate on a pointer hence the goto. */ top: MPUNReachNLRIAttribute *mpunreach = 0; #if 0 list ::const_iterator pai; for (pai = p->pa_list().begin(); pai != p->pa_list().end(); pai++) { const PathAttribute* pa; pa = *pai; if (dynamic_cast*>(*pai)) { mpunreach = dynamic_cast*>(*pai); mpunreach->add_withdrawn(net); //mpunreach->encode(); debug_msg("%s\n", p->str().c_str()); return; } } #endif if (!p->pa_list().is_empty()) mpunreach = p->pa_list()->mpunreach(SAFI_UNICAST); else { XLOG_UNREACHABLE(); } if(0 == mpunreach) { MPUNReachNLRIAttribute mp(SAFI_UNICAST); p->add_pathatt(mp); goto top; } else { mpunreach->add_withdrawn(net); } } template <> void add_nexthop(UpdatePacket *p, IPv6 nexthop) { /* ** Look for a multiprotocol path attribute, if there is one ** present just add the net. Otherwise add a multiprotocol path ** attribute and then add the nexthop. Note: if we add the path ** attribute we need operate on a pointer hence the goto. */ top: MPReachNLRIAttribute *mpreach = 0; #if 0 list ::const_iterator pai; for (pai = p->pa_list().begin(); pai != p->pa_list().end(); pai++) { const PathAttribute* pa; pa = *pai; if (dynamic_cast*>(*pai)) { mpreach = dynamic_cast*>(*pai); mpreach->set_nexthop(nexthop); //mpreach->encode(); debug_msg("%s\n", p->str().c_str()); return; } } #endif mpreach = p->pa_list()->mpreach(SAFI_UNICAST); if(0 == mpreach) { MPReachNLRIAttribute mp(SAFI_UNICAST); p->add_pathatt(mp); goto top; } else { mpreach->set_nexthop(nexthop); } } template bool test_single_update(TestInfo& info, A nexthop, IPNet net) { DOUT(info) << info.test_name() << endl; Trie trie; /* need peer data to be able to encode */ EventLoop eventloop; Iptuple iptuple; LocalData localdata(eventloop); localdata.set_as(AsNum(0)); localdata.set_use_4byte_asnums(false); BGPPeerData peerdata(localdata, iptuple, AsNum(0), IPv4(),0); // we force IBGP, as this does fewer tests peerdata.compute_peer_type(); // force negotiated, or our parser will strip out the v6 attributes peerdata.template set_multiprotocol(SAFI_UNICAST, BGPPeerData::NEGOTIATED); peerdata.template set_multiprotocol(SAFI_UNICAST, BGPPeerData::SENT); /* ** Verify that the trie is empty. */ trie.tree_walk_table(callback(tree_walker_empty, info, A::ZERO())); /* ** The trie should be empty make sure that a lookup fails. */ const UpdatePacket *p = trie.lookup(net.str()); if(0 != p) { DOUT(info) << "lookup suceeded in empty trie!!!\n" << p->str() << endl; return false; } /* ** Create an update packet with a single NLRI. */ UpdatePacket *bgpupdate; bgpupdate = new UpdatePacket(); add_nlri(bgpupdate, net); /* ** Add an origin, aspath and next hop to keep it legal. */ OriginAttribute oa(IGP); bgpupdate->add_pathatt(oa); ASPathAttribute aspa(ASPath("1,2,3")); bgpupdate->add_pathatt(aspa); add_nexthop(bgpupdate, nexthop); /* ** Pass to the trie. */ TimeVal tv; size_t len = BGPPacket::MAXPACKETSIZE; uint8_t *data = new uint8_t[BGPPacket::MAXPACKETSIZE]; assert(bgpupdate->encode(data, len, &peerdata)); trie.process_update_packet(tv, data, len, &peerdata); /* ** Verify that this net is in the trie. */ p = trie.lookup(net.str()); if(0 == p) { DOUT(info) << "lookup of " << net.str() << " failed\n"; return false; } if(*bgpupdate != *p) { DOUT(info) << endl << bgpupdate->str() << "NOT EQUAL TO\n" << p->str() << endl; return false; } /* ** Walk the trie and check that the net is there. */ typename Nmap::nmap nlri; nlri[net] = nexthop; trie.tree_walk_table(callback(tree_walker, info, nlri)); /* ** Generate a withdraw to remove the entry from the trie. */ delete [] data; delete bgpupdate; bgpupdate = new UpdatePacket(); withdraw_nlri(bgpupdate, net); len = BGPPacket::MAXPACKETSIZE; data = new uint8_t[BGPPacket::MAXPACKETSIZE]; assert(bgpupdate->encode(data, len, &peerdata)); trie.process_update_packet(tv, data, len, &peerdata); /* ** Check that the net has been removed from the trie. */ p = trie.lookup(net.str()); if(0 != p) { DOUT(info) << "lookup suceeded in empty trie!!!\n" << p->str() << endl; return false; } delete [] data; delete bgpupdate; // Free up the packet. /* ** Verify that the trie is empty. */ trie.tree_walk_table(callback(tree_walker_empty, info, A::ZERO())); return true; } template bool test_replay(TestInfo& info, A nexthop, IPNet net) { DOUT(info) << info.test_name() << endl; Trie trie; /* need peer data to be able to encode */ EventLoop eventloop; Iptuple iptuple; LocalData localdata(eventloop); localdata.set_use_4byte_asnums(false); localdata.set_as(AsNum(0)); BGPPeerData peerdata(localdata, iptuple, AsNum(0), IPv4(),0); // we force IBGP, as this does fewer tests peerdata.compute_peer_type(); // force negotiated, or our parser will strip out the v6 attributes peerdata.template set_multiprotocol(SAFI_UNICAST, BGPPeerData::NEGOTIATED); peerdata.template set_multiprotocol(SAFI_UNICAST, BGPPeerData::SENT); /* ** Verify that the trie is empty. */ trie.tree_walk_table(callback(tree_walker_empty, info, A::ZERO())); /* ** The trie should be empty make sure that a lookup fails. */ const UpdatePacket *p = trie.lookup(net.str()); if(0 != p) { DOUT(info) << "lookup suceeded in empty trie!!!\n" << p->str() << endl; return false; } UpdatePacket *packet_nlri, *packet_w1, *packet_w2; uint8_t data_nlri[BGPPacket::MAXPACKETSIZE]; uint8_t data_w1[BGPPacket::MAXPACKETSIZE]; uint8_t data_w2[BGPPacket::MAXPACKETSIZE]; size_t len_nlri, len_w1, len_w2; /* ** Create an update packet with two NLRIs. */ packet_nlri = new UpdatePacket(); add_nlri(packet_nlri, net); IPNet net2 = ++net;--net; add_nlri(packet_nlri, net2); /* ** Add an origin, aspath and next hop to keep it legal. */ OriginAttribute oa(IGP); packet_nlri->add_pathatt(oa); ASPathAttribute aspa(ASPath("1,2,3")); packet_nlri->add_pathatt(aspa); add_nexthop(packet_nlri, nexthop); /* ** Pass to the trie. */ TimeVal tv; len_nlri = BGPPacket::MAXPACKETSIZE; assert(packet_nlri->encode(data_nlri, len_nlri, &peerdata)); trie.process_update_packet(tv, data_nlri, len_nlri, &peerdata); /* ** Verify that net is in the trie. */ p = trie.lookup(net.str()); if(0 == p) { DOUT(info) << "lookup of " << net.str() << " failed\n"; return false; } if(*packet_nlri != *p) { DOUT(info) << endl << packet_nlri->str() << "NOT EQUAL TO\n" << p->str() << endl; return false; } /* ** Verify that net2 is in the trie. */ p = trie.lookup(net2.str()); if(0 == p) { DOUT(info) << "lookup of " << net2.str() << " failed\n"; return false; } if(*packet_nlri != *p) { DOUT(info) << endl << packet_nlri->str() << "NOT EQUAL TO\n" << p->str() << endl; return false; } /* ** Walk the trie and check that both nets are there. */ typename Nmap::nmap nlri; nlri[net] = nexthop; nlri[net2] = nexthop; trie.tree_walk_table(callback(tree_walker, info, nlri)); /* ** Generate a withdraw to remove one entry from the trie. */ packet_w1 = new UpdatePacket(); withdraw_nlri(packet_w1, net); len_w1 = BGPPacket::MAXPACKETSIZE; assert(packet_w1->encode(data_w1, len_w1, &peerdata)); trie.process_update_packet(tv, data_w1, len_w1, &peerdata); /* ** Check that net is no longer in the trie. */ p = trie.lookup(net.str()); if(0 != p) { DOUT(info) << "lookup suceeded in empty trie!!!\n" << p->str() << endl; return false; } /* ** Now check the replay code. */ size_t pos = 0; Ulist ulist; ulist.push_back(packet_nlri); ulist.push_back(packet_w1); DOUT(info) << "0\t" << ulist[0]->str() << endl; DOUT(info) << "1\t" << ulist[1]->str() << endl; trie.replay_walk(callback(replay_walker, info, &pos, ulist, A::ZERO()), &peerdata); /* ** Build another withdraw to remove the net2. This should empty ** the trie. */ packet_w2 = new UpdatePacket(); withdraw_nlri(packet_w2, net2); len_w2 = BGPPacket::MAXPACKETSIZE; assert(packet_w2->encode(data_w2, len_w2, &peerdata)); trie.process_update_packet(tv, data_w2, len_w2, &peerdata); /* ** The replay list should be empty. */ pos = 0; ulist.clear(); trie.replay_walk(callback(replay_walker, info, &pos, ulist, A::ZERO()), &peerdata); /* ** Push the update packet with the two nlris back in. */ trie.process_update_packet(tv, data_nlri, len_nlri, &peerdata); /* ** Check the replay code. It should now only contain the most ** recent update. */ pos = 0; ulist.clear(); ulist.push_back(packet_nlri); DOUT(info) << "0\t" << ulist[0]->str() << endl; trie.replay_walk(callback(replay_walker, info, &pos, ulist, A::ZERO()), &peerdata); /* ** Empty out the trie. */ trie.process_update_packet(tv, data_w1, len_w1, &peerdata); trie.process_update_packet(tv, data_w2, len_w2, &peerdata); /* ** The replay list should be empty. */ pos = 0; ulist.clear(); trie.replay_walk(callback(replay_walker, info, &pos, ulist, A::ZERO()), &peerdata); /* ** Verify that the trie is empty. */ trie.tree_walk_table(callback(tree_walker_empty, info, A::ZERO())); delete packet_nlri; delete packet_w1; delete packet_w2; return true; } int main(int argc, char** argv) { XorpUnexpectedHandler x(xorp_unexpected_handler); xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); TestMain t(argc, argv); string test_name = t.get_optional_args("-t", "--test", "run only the specified test"); t.complete_args_parsing(); IPNet net1_ipv4("10.10.0.0/16"); IPv4 nexthop1_ipv4("20.20.20.20"); IPNet net1_ipv6("2000::/3"); IPv6 nexthop1_ipv6("20:20:20:20:20:20:20:20"); try { uint8_t edata[2]; edata[0]=1; edata[1]=2; struct test { string test_name; XorpCallback1::RefPtr cb; } tests[] = { {"single_ipv4", callback(test_single_update, nexthop1_ipv4, net1_ipv4)}, {"single_ipv6", callback(test_single_update, nexthop1_ipv6, net1_ipv6)}, {"replay_ipv4", callback(test_replay, nexthop1_ipv4, net1_ipv4)}, {"replay_ipv6", callback(test_replay, nexthop1_ipv6, net1_ipv6)}, }; if("" == test_name) { for(unsigned int i = 0; i < sizeof(tests) / sizeof(struct test); i++) t.run(tests[i].test_name, tests[i].cb); } else { for(unsigned int i = 0; i < sizeof(tests) / sizeof(struct test); i++) if(test_name == tests[i].test_name) { t.run(tests[i].test_name, tests[i].cb); return t.exit(); } t.failed("No test with name " + test_name + " found\n"); } } catch(...) { xorp_catch_standard_exceptions(); } xlog_stop(); xlog_exit(); return t.exit(); } xorp/bgp/harness/test_terminate.sh0000775000076400007640000000452111421137511017457 0ustar greearbgreearb#!/usr/bin/env bash # # $XORP: xorp/bgp/harness/test_terminate.sh,v 1.8 2006/08/16 22:10:14 atanu Exp $ # # # Test BGP termination # # This script started with no arguments will start all required process and # terminate them at the end of the tests. # # Preconditons # 1) Run a finder process # 2) Run "../fea/xorp_fea_dummy" # 3) Run "../rib/xorp_rib" # 4) Run "../xorp_bgp" set -e . ./setup_paths.sh # srcdir is set by make for check target if [ "X${srcdir}" = "X" ] ; then srcdir=`dirname $0` ; fi . ${srcdir}/xrl_shell_funcs.sh "" . $BGP_FUNCS "" onexit() { last=$? if [ $last = "0" ] then echo "$0: Tests Succeeded (BGP: $TESTS)" else echo "$0: Tests Failed (BGP: $TESTS)" fi trap '' 0 2 } trap onexit 0 2 HOST=127.0.0.1 AS=65008 USE4BYTEAS=false configure_bgp() { LOCALHOST=$HOST ID=192.150.187.78 local_config $AS $ID $USE4BYTEAS register_rib ${RIB:-""} } test1() { echo "TEST1 - Verify that BGP shuts down cleanly" CALLXRL=$CALLXRL $BGP_FUNCS shutdown sleep 5 } test2() { echo "TEST2 - Verify that BGP shuts down after enabling three peerings" LOCALHOST=$HOST HOLDTIME=60 NEXT_HOP="127.0.0.1" PEER=$HOST PORT=10001;PEER_PORT=20001;PEER_AS=6401 IPTUPLE="$LOCALHOST $PORT $PEER $PEER_PORT" add_peer lo $IPTUPLE $PEER_AS $NEXT_HOP $HOLDTIME enable_peer $IPTUPLE PORT=10002;PEER_PORT=20002;PEER_AS=6402 IPTUPLE="$LOCALHOST $PORT $PEER $PEER_PORT" add_peer lo $IPTUPLE $PEER_AS $NEXT_HOP $HOLDTIME enable_peer $IPTUPLE PORT=10003;PEER_PORT=20003;PEER_AS=6403 IPTUPLE="$LOCALHOST $PORT $PEER $PEER_PORT" add_peer lo $IPTUPLE $PEER_AS $NEXT_HOP $HOLDTIME enable_peer $IPTUPLE CALLXRL=$CALLXRL $BGP_FUNCS shutdown sleep 5 } TESTS_NOT_FIXED='' TESTS='test1 test2' # Include command line . ${srcdir}/args.sh if [ $START_PROGRAMS = "yes" ] then CXRL="$CALLXRL -r 10" runit $QUIET $VERBOSE -c "$0 -s -c $*" < /dev/null 2>&1 then CALLXRL=call_xrl else CALLXRL=/usr/local/xorp/sbin/call_xrl fi fi fi fi if [ "_$XORP_FINDER" == "_" ] then if [ -x ../../libxipc/xorp_finder ] then XORP_FINDER="../../libxipc/xorp_finder" else XORP_FINDER="../../lib/xorp/sbin/xorp_finder" fi fi if [ "_$XORP_FEA_DUMMY" == "_" ] then if [ -x ../../fea/xorp_fea_dummy ] then XORP_FEA_DUMMY="../../fea/xorp_fea_dummy" else XORP_FEA_DUMMY="../../lib/xorp/sbin/xorp_fea_dummy" fi fi if [ "_$XORP_RIB" == "_" ] then if [ -x ../../rib/xorp_rib ] then XORP_RIB="../../rib/xorp_rib" else XORP_RIB="../../lib/xorp/sbin/xorp_rib" fi fi if [ "_$XORP_BGP" == "_" ] then if [ -x ../xorp_bgp ] then XORP_BGP="../xorp_bgp" else XORP_BGP="../../lib/xorp/sbin/xorp_bgp" fi fi if [ "_$PRINT_PEERS" == "_" ] then if [ -x ../tools/bgp_print_peers ] then PRINT_PEERS="../tools/bgp_print_peers" else PRINT_PEERS="../../lib/xorp/bin/bgp_print_peers" fi fi export CALLXRL export XORP_FINDER export FEA_FUNCS export BGP_FUNCS export RIB_FUNCS export XORP_FEA_DUMMY export XORP_RIB export XORP_BGP export PRINT_PEERS export LD_LIBRARY_PATH #echo "setup-script: LOC: $LOC pwd: `pwd`" #echo " TREETOP: $TREETOP" #echo " LD_LIBRARY_PATH: $LD_LIBRARY_PATH" xorp/bgp/harness/trie.hh0000664000076400007640000000704211421137511015356 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/bgp/harness/trie.hh,v 1.19 2008/10/02 21:56:27 bms Exp $ #ifndef __BGP_HARNESS_TRIE_HH__ #define __BGP_HARNESS_TRIE_HH__ #include #include "libxorp/timeval.hh" #include "libxorp/callback.hh" #include "bgp/packet.hh" #include "trie_payload.hh" #include "real_trie.hh" class BGPPeerData; /** * The trie stores BGP update packets the trie index is the * NLRI. A BGP update packet can contain multiple NLRI's. To save * the overhead of storing an update packet multiple times in the * trie a single copy of the update packet is kept. The Payload is * a reference to this single copy. Each update packet is chained * together on a linked list. New update packets are added to the * end of the list. In theory this ordered chained list structure * should make is very simple to print out the current update * packets that constitute the routing table. The ordering is * important if the same NLRI is contained in two packets then the * later one should be used. */ class Trie { public: Trie() : _first(0), _last(0), _update_cnt(0), _changes(0), _warning(true) { } const UpdatePacket *lookup(const string& net) const; const UpdatePacket *lookup(const IPv4Net& net) const; const UpdatePacket *lookup(const IPv6Net& net) const; void process_update_packet(const TimeVal& tv, const uint8_t *buf, size_t len, const BGPPeerData *peerdata); typedef RealTrie::TreeWalker TreeWalker_ipv4; typedef RealTrie::TreeWalker TreeWalker_ipv6; void tree_walk_table(const TreeWalker_ipv4& tw) const; void tree_walk_table(const TreeWalker_ipv6& tw) const; typedef XorpCallback2::RefPtr ReplayWalker; /** * Generate the set of update packets that would totally populate * this trie. * * @param uw The callback function that is called. */ void replay_walk(const ReplayWalker uw, const BGPPeerData *peerdata) const; uint32_t update_count() { return _update_cnt; } uint32_t changes() { return _changes; } void set_warning(bool warning) { _warning = warning; } private: template void add(IPNet net, TriePayload&); template void del(IPNet net, TriePayload&); template void get_heads(RealTrie*&, RealTrie*&); RealTrie _head_ipv4; RealTrie _head_ipv4_del; RealTrie _head_ipv6; RealTrie _head_ipv6_del; TrieData *_first; TrieData *_last; uint32_t _update_cnt; // Number of update packets seen uint32_t _changes; // Number of trie entry changes. bool _warning; // Print warning messages; }; #endif // __BGP_HARNESS_TRIE_HH__ xorp/bgp/harness/notification_codes.sh0000664000076400007640000000235211421137511020270 0ustar greearbgreearb#!/usr/bin/env bash # # $XORP: xorp/bgp/harness/notification_codes.sh,v 1.1 2005/12/11 20:33:07 atanu Exp $ # # # BGP Notification error codes and subcodes. # MSGHEADERERR=1 # Message Header Error CONNNOTSYNC=1 # Connection Not Synchronized BADMESSLEN=2 # Bad Message Length BADMESSTYPE=3 # Bad Message Type OPENMSGERROR=2 # OPEN Message Error UNSUPVERNUM=1 # Unsupported Version Number BADASPEER=2 # Bad Peer AS BADBGPIDENT=3 # Bad BGP Identifier UNSUPOPTPAR=4 # Unsupported Optional Parameter AUTHFAIL=5 # Authentication Failure UNACCEPTHOLDTIME=6 # Unacceptable Hold Time UNSUPCAPABILITY=7 # Unsupported Capability (RFC 3392) UPDATEMSGERR=3 # Update error MALATTRLIST=1 # Malformed Attribute List UNRECOGWATTR=2 # Unrecognized Well-known Attribute MISSWATTR=3 # Missing Well-known Attribute ATTRFLAGS=4 # Attribute Flags Error ATTRLEN=5 # Attribute Length Error INVALORGATTR=6 # Invalid ORIGIN Attribute INVALNHATTR=8 # Invalid NEXT_HOP Attribute OPTATTR=9 # Optional Attribute Error INVALNETFIELD=10 # Invalid Network Field MALASPATH=11 # Malformed AS_PATH HOLDTIMEEXP=4 # Hold Timer Expired FSMERROR=5 # Finite State Machine Error CEASE=6 # Cease xorp/bgp/dummy_next_hop_resolver.hh0000664000076400007640000000347111421137511017732 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/bgp/dummy_next_hop_resolver.hh,v 1.14 2008/10/02 21:56:15 bms Exp $ #ifndef __BGP_DUMMY_NEXT_HOP_RESOLVER_HH__ #define __BGP_DUMMY_NEXT_HOP_RESOLVER_HH__ #include "next_hop_resolver.hh" template class DummyNextHopResolver : public NextHopResolver { public: DummyNextHopResolver(EventLoop& eventloop, BGPMain& bgp); virtual ~DummyNextHopResolver(); /** * @short lookup next hop. * * @param nexthop. Next hop. * @param resolvable. Is this route resolvable. * @param metric. If this route is resolvable the metric of this * route. * @return True if this next hop is found. * */ bool lookup(const A nexthop, bool& resolvable, uint32_t& metric) const; void set_nexthop_metric(const A nexthop, uint32_t metric); void unset_nexthop_metric(const A nexthop); private: DecisionTable *_decision; map _metrics; }; #endif // __BGP_DUMMY_NEXT_HOP_RESOLVER_HH__ xorp/bgp/bgp.cc0000664000076400007640000015176111540225520013516 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_MAXIMUM_DELAY // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #include "bgp_module.h" #include "libxorp/xorp.h" #include "libxorp/xorpfd.hh" #include "libxorp/eventloop.hh" #include "libxorp/xlog.h" #ifdef HAVE_SYS_TIME_H #include #endif #include "libcomm/comm_api.h" #include "bgp.hh" #include "path_attribute.hh" #include "iptuple.hh" #include "xrl_target.hh" #ifndef XORP_DISABLE_PROFILE #include "profile_vars.hh" #endif // ---------------------------------------------------------------------------- // BGPMain implementation BGPMain::BGPMain(EventLoop& eventloop) : _eventloop(eventloop), _exit_loop(false), _component_count(0), _ifmgr(NULL), _is_ifmgr_ready(false), _first_policy_push(false) { debug_msg("BGPMain object created\n"); /* ** Ideally these data structures should be created inline. However ** we need to finely control the order of destruction. */ _local_data = new LocalData(_eventloop), _peerlist = new BGPPeerList(); _deleted_peerlist = new BGPPeerList(); _xrl_router = new XrlStdRouter(_eventloop, "bgp"); _xrl_target = new XrlBgpTarget(_xrl_router, *this); wait_until_xrl_router_is_ready(_eventloop, *_xrl_router); _rib_ipc_handler = new RibIpcHandler(*_xrl_router, *this); _aggregation_handler = new AggregationHandler(); _next_hop_resolver_ipv4 = new NextHopResolver(_xrl_router, _eventloop, *this); #ifdef HAVE_IPV6 _next_hop_resolver_ipv6 = new NextHopResolver(_xrl_router, _eventloop, *this); PAListRef* palist6 = new PAListRef(0); palist6->create_attribute_manager(); delete palist6; #endif // start up the attribute managers PAListRef* palist4 = new PAListRef(0); palist4->create_attribute_manager(); delete palist4; _plumbing_unicast = new BGPPlumbing(SAFI_UNICAST, _rib_ipc_handler, _aggregation_handler, *_next_hop_resolver_ipv4, #ifdef HAVE_IPV6 *_next_hop_resolver_ipv6, #endif _policy_filters, *this); _plumbing_multicast = new BGPPlumbing(SAFI_MULTICAST, _rib_ipc_handler, _aggregation_handler, *_next_hop_resolver_ipv4, #ifdef HAVE_IPV6 *_next_hop_resolver_ipv6, #endif _policy_filters, *this); _rib_ipc_handler->set_plumbing(_plumbing_unicast, _plumbing_multicast); _process_watch = new ProcessWatch(_xrl_router, _eventloop, bgp_mib_name().c_str(), ::callback(this, &BGPMain::terminate)); // Initialize the interface manager // TODO: the FEA targetname is hardcoded here!! _ifmgr = new IfMgrXrlMirror(_eventloop, "fea", _xrl_router->finder_address(), _xrl_router->finder_port()); _ifmgr->set_observer(this); _ifmgr->attach_hint_observer(this); // // Ideally, we want to startup after the FEA birth event. // startup(); PROFILE(initialize_profiling_variables(_profile)); comm_init(); } BGPMain::~BGPMain() { // // Ideally, we want to shutdown gracefully before we call the destructor. // shutdown(); _is_ifmgr_ready = false; _ifmgr->detach_hint_observer(this); _ifmgr->unset_observer(this); delete _ifmgr; _ifmgr = NULL; /* ** Stop accepting network connections. */ stop_all_servers(); debug_msg("-------------------------------------------\n"); debug_msg("Stopping All Peers\n"); _peerlist->all_stop(); debug_msg("-------------------------------------------\n"); debug_msg("Waiting for all peers to go to idle\n"); int start = time(NULL); int now; while (_peerlist->not_all_idle() || _rib_ipc_handler->busy() || DeleteAllNodes::running() #ifdef HAVE_IPV6 || DeleteAllNodes::running() #endif ) { eventloop().run(); if (_peerlist->not_all_idle()) { // Kick them again XLOG_WARNING("Stopping all peers in ~BGPMain cleanup loop.\n"); _peerlist->all_stop(); } now = time(NULL); if (now > start + 2) { XLOG_WARNING("xrl router still has pending peer-idle operations after %i seconds," " not-all-idle: %i rib_ipc_handler busy: %i delete-all-nodes-running: %i" " continuing...", now - start, (int)(_peerlist->not_all_idle()), (int)(_rib_ipc_handler->busy()), (int)(DeleteAllNodes::running())); #ifdef HAVE_IPV6 if (DeleteAllNodes::running()) { XLOG_WARNING("delete-all-ipv6-nodes is running."); } #endif break; } } /* ** NOTE: We expect one timer to be pending. The timer is in the xrl_router. */ if (eventloop().timer_list_length() > 1) XLOG_INFO("EVENT: timers %u", XORP_UINT_CAST(eventloop().timer_list_length())); /* ** Force the table de-registration from the RIB. Otherwise the ** destructor for the rib_ipc_handler will complain. */ _rib_ipc_handler->register_ribname(""); start = time(NULL); while(_xrl_router->pending()) { eventloop().run(); now = time(NULL); if (now > start + 2) { XLOG_WARNING("xrl router still has pending operations after %i seconds, RIB deregister, giving up, xrl_router: %s", now - start, _xrl_router->toString().c_str()); break; } if (now > start) { XLOG_WARNING("xrl router still has pending operations, after %i seconds, RIB deregister, will retry.", now - start); } } debug_msg("-------------------------------------------\n"); debug_msg("Deleting rib_ipc_handler\n"); delete _rib_ipc_handler; start = time(NULL); while(_xrl_router->pending()) { eventloop().run(); now = time(NULL); if (now > start + 2) { XLOG_WARNING("xrl router still has pending operations after %i seconds, delete RIB IPC, giving up, xrl_router: %s", now - start, _xrl_router->toString().c_str()); break; } if (now > start) { XLOG_WARNING("xrl router still has pending operations, after %i seconds, delete RIB IPC, will retry.", now - start); } } debug_msg("-------------------------------------------\n"); debug_msg("Deleting Xrl Target\n"); delete _xrl_target; debug_msg("-------------------------------------------\n"); debug_msg("Deleting xrl_router\n"); delete _xrl_router; debug_msg("-------------------------------------------\n"); debug_msg("Deleting peerlist\n"); delete _peerlist; debug_msg("-------------------------------------------\n"); debug_msg("Deleting localdata\n"); delete _local_data; debug_msg("-------------------------------------------\n"); debug_msg("Deleting plumbing unicast\n"); delete _plumbing_unicast; debug_msg("-------------------------------------------\n"); debug_msg("Deleting plumbing multicast\n"); delete _plumbing_multicast; debug_msg("-------------------------------------------\n"); debug_msg("Deleting next hop resolver IPv4\n"); delete _next_hop_resolver_ipv4; #ifdef HAVE_IPV6 debug_msg("-------------------------------------------\n"); debug_msg("Deleting next hop resolver IPv6\n"); delete _next_hop_resolver_ipv6; #endif debug_msg("-------------------------------------------\n"); debug_msg("Deleting process watcher\n"); delete _process_watch; debug_msg("-------------------------------------------\n"); debug_msg("Delete BGPMain complete\n"); debug_msg("-------------------------------------------\n"); comm_exit(); } ProcessStatus BGPMain::status(string& reason) { ProcessStatus s = PROC_READY; reason = "Ready"; if (_plumbing_unicast->status(reason) == false || _plumbing_multicast->status(reason) == false) { s = PROC_FAILED; } else if (_exit_loop == true) { s = PROC_SHUTDOWN; reason = "Shutting Down"; } else if (! _is_ifmgr_ready) { s = PROC_NOT_READY; reason = "Waiting for interface manager"; } else if (!_first_policy_push) { s = PROC_NOT_READY; reason = "Waiting for policy manager"; } return s; } int BGPMain::startup() { // // XXX: when the startup is completed, // IfMgrHintObserver::tree_complete() will be called. // if (_ifmgr->startup() != XORP_OK) { ServiceBase::set_status(SERVICE_FAILED); return (XORP_ERROR); } component_up("startup"); register_address_status(callback(this, &BGPMain::address_status_change4)); #ifdef HAVE_IPV6 register_address_status(callback(this, &BGPMain::address_status_change6)); #endif return (XORP_OK); } int BGPMain::shutdown() { // // XXX: when the shutdown is completed, BGPMain::status_change() // will be called. // component_down("shutdown"); _is_ifmgr_ready = false; return (_ifmgr->shutdown()); } void BGPMain::component_up(const string& component_name) { UNUSED(component_name); _component_count++; // // TODO: if all components are up, then we should call // ServiceBase::set_status(SERVICE_RUNNING); // } void BGPMain::component_down(const string& component_name) { UNUSED(component_name); XLOG_ASSERT(_component_count > 0); _component_count--; if (0 == _component_count) ServiceBase::set_status(SERVICE_SHUTDOWN); else ServiceBase::set_status(SERVICE_SHUTTING_DOWN); } void BGPMain::status_change(ServiceBase* service, ServiceStatus old_status, ServiceStatus new_status) { if (old_status == new_status) return; if (SERVICE_RUNNING == new_status) component_up(service->service_name()); if (SERVICE_SHUTDOWN == new_status) component_down(service->service_name()); if (service == ifmgr_mirror_service_base()) { if ((old_status == SERVICE_SHUTTING_DOWN) && (new_status == SERVICE_SHUTDOWN)) { // XXX: ifmgr shutdown // // TODO: if we have to do anything after the ifmgr shutdown // has completed, this is the place. // } } } bool BGPMain::is_interface_enabled(const string& interface) const { debug_msg("Interface %s\n", interface.c_str()); const IfMgrIfAtom* fi = ifmgr_iftree().find_interface(interface); if (fi == NULL) return false; return (fi->enabled() && (! fi->no_carrier())); } bool BGPMain::is_vif_enabled(const string& interface, const string& vif) const { debug_msg("Interface %s Vif %s\n", interface.c_str(), vif.c_str()); if (! is_interface_enabled(interface)) return false; const IfMgrVifAtom* fv = ifmgr_iftree().find_vif(interface, vif); if (fv == NULL) return false; return (fv->enabled()); } bool BGPMain::is_address_enabled(const string& interface, const string& vif, const IPv4& address) const { debug_msg("Interface %s Vif %s Address %s\n", interface.c_str(), vif.c_str(), cstring(address)); if (! is_vif_enabled(interface, vif)) return false; const IfMgrIPv4Atom* fa = ifmgr_iftree().find_addr(interface, vif, address); if (fa == NULL) return false; return (fa->enabled()); } uint32_t BGPMain::get_prefix_length(const string& interface, const string& vif, const IPv4& address) { debug_msg("Interface %s Vif %s Address %s\n", interface.c_str(), vif.c_str(), cstring(address)); const IfMgrIPv4Atom* fa = ifmgr_iftree().find_addr(interface, vif, address); if (fa == NULL) return 0; return (fa->prefix_len()); } uint32_t BGPMain::get_mtu(const string& interface) { debug_msg("Interface %s\n", interface.c_str()); const IfMgrIfAtom* fi = ifmgr_iftree().find_interface(interface); if (fi == NULL) return 0; return (fi->mtu()); } void BGPMain::tree_complete() { _is_ifmgr_ready = true; // // XXX: we use same actions when the tree is completed or updates are made // updates_made(); } void BGPMain::updates_made() { IfMgrIfTree::IfMap::const_iterator ii; IfMgrIfAtom::VifMap::const_iterator vi; IfMgrVifAtom::IPv4Map::const_iterator ai4; const IfMgrIfAtom* if_atom; const IfMgrIfAtom* other_if_atom; const IfMgrVifAtom* vif_atom; const IfMgrVifAtom* other_vif_atom; const IfMgrIPv4Atom* addr_atom4; const IfMgrIPv4Atom* other_addr_atom4; #ifdef HAVE_IPV6 IfMgrVifAtom::IPv6Map::const_iterator ai6; const IfMgrIPv6Atom* addr_atom6; const IfMgrIPv6Atom* other_addr_atom6; #endif // // Check whether the old interfaces, vifs and addresses are still there // for (ii = _iftree.interfaces().begin(); ii != _iftree.interfaces().end(); ++ii) { bool is_old_interface_enabled = false; bool is_new_interface_enabled = false; bool is_old_vif_enabled = false; bool is_new_vif_enabled = false; bool is_old_address_enabled = false; bool is_new_address_enabled = false; if_atom = &ii->second; is_old_interface_enabled = if_atom->enabled(); is_old_interface_enabled &= (! if_atom->no_carrier()); // Check the interface other_if_atom = ifmgr_iftree().find_interface(if_atom->name()); if (other_if_atom == NULL) { // The interface has disappeared is_new_interface_enabled = false; } else { is_new_interface_enabled = other_if_atom->enabled(); is_new_interface_enabled &= (! other_if_atom->no_carrier()); } if ((is_old_interface_enabled != is_new_interface_enabled) && (! _interface_status_cb.is_empty())) { // The interface's enabled flag has changed _interface_status_cb->dispatch(if_atom->name(), is_new_interface_enabled); } for (vi = if_atom->vifs().begin(); vi != if_atom->vifs().end(); ++vi) { vif_atom = &vi->second; is_old_vif_enabled = vif_atom->enabled(); is_old_vif_enabled &= is_old_interface_enabled; // Check the vif other_vif_atom = ifmgr_iftree().find_vif(if_atom->name(), vif_atom->name()); if (other_vif_atom == NULL) { // The vif has disappeared is_new_vif_enabled = false; } else { is_new_vif_enabled = other_vif_atom->enabled(); } is_new_vif_enabled &= is_new_interface_enabled; if ((is_old_vif_enabled != is_new_vif_enabled) && (! _vif_status_cb.is_empty())) { // The vif's enabled flag has changed _vif_status_cb->dispatch(if_atom->name(), vif_atom->name(), is_new_vif_enabled); } for (ai4 = vif_atom->ipv4addrs().begin(); ai4 != vif_atom->ipv4addrs().end(); ++ai4) { addr_atom4 = &ai4->second; is_old_address_enabled = addr_atom4->enabled(); is_old_address_enabled &= is_old_vif_enabled; // Check the address other_addr_atom4 = ifmgr_iftree().find_addr(if_atom->name(), vif_atom->name(), addr_atom4->addr()); if (other_addr_atom4 == NULL) { // The address has disappeared is_new_address_enabled = false; } else { is_new_address_enabled = other_addr_atom4->enabled(); } is_new_address_enabled &= is_new_vif_enabled; if ((is_old_address_enabled != is_new_address_enabled) && (! _address_status4_cb.is_empty())) { // The address's enabled flag has changed _address_status4_cb->dispatch(if_atom->name(), vif_atom->name(), addr_atom4->addr(), addr_atom4->prefix_len(), is_new_address_enabled); } } #ifdef HAVE_IPV6 for (ai6 = vif_atom->ipv6addrs().begin(); ai6 != vif_atom->ipv6addrs().end(); ++ai6) { addr_atom6 = &ai6->second; is_old_address_enabled = addr_atom6->enabled(); is_old_address_enabled &= is_old_vif_enabled; // Check the address other_addr_atom6 = ifmgr_iftree().find_addr(if_atom->name(), vif_atom->name(), addr_atom6->addr()); if (other_addr_atom6 == NULL) { // The address has disappeared is_new_address_enabled = false; } else { is_new_address_enabled = other_addr_atom6->enabled(); } is_new_address_enabled &= is_new_vif_enabled; if ((is_old_address_enabled != is_new_address_enabled) && (! _address_status6_cb.is_empty())) { // The address's enabled flag has changed _address_status6_cb->dispatch(if_atom->name(), vif_atom->name(), addr_atom6->addr(), addr_atom6->prefix_len(), is_new_address_enabled); } } #endif } } // // Check for new interfaces, vifs and addresses // for (ii = ifmgr_iftree().interfaces().begin(); ii != ifmgr_iftree().interfaces().end(); ++ii) { if_atom = &ii->second; // Check the interface other_if_atom = _iftree.find_interface(if_atom->name()); if (other_if_atom == NULL) { // A new interface if (if_atom->enabled() && (! if_atom->no_carrier()) && (! _interface_status_cb.is_empty())) { _interface_status_cb->dispatch(if_atom->name(), true); } } for (vi = if_atom->vifs().begin(); vi != if_atom->vifs().end(); ++vi) { vif_atom = &vi->second; // Check the vif other_vif_atom = _iftree.find_vif(if_atom->name(), vif_atom->name()); if (other_vif_atom == NULL) { // A new vif if (if_atom->enabled() && (! if_atom->no_carrier()) && (vif_atom->enabled()) && (! _vif_status_cb.is_empty())) { _vif_status_cb->dispatch(if_atom->name(), vif_atom->name(), true); } } for (ai4 = vif_atom->ipv4addrs().begin(); ai4 != vif_atom->ipv4addrs().end(); ++ai4) { addr_atom4 = &ai4->second; // Check the address other_addr_atom4 = _iftree.find_addr(if_atom->name(), vif_atom->name(), addr_atom4->addr()); if (other_addr_atom4 == NULL) { // A new address if (if_atom->enabled() && (! if_atom->no_carrier()) && (vif_atom->enabled()) && (addr_atom4->enabled()) && (! _address_status4_cb.is_empty())) { _address_status4_cb->dispatch(if_atom->name(), vif_atom->name(), addr_atom4->addr(), addr_atom4->prefix_len(), true); } } } #ifdef HAVE_IPV6 for (ai6 = vif_atom->ipv6addrs().begin(); ai6 != vif_atom->ipv6addrs().end(); ++ai6) { addr_atom6 = &ai6->second; // Check the address other_addr_atom6 = _iftree.find_addr(if_atom->name(), vif_atom->name(), addr_atom6->addr()); if (other_addr_atom6 == NULL) { // A new address if (if_atom->enabled() && (! if_atom->no_carrier()) && (vif_atom->enabled()) && (addr_atom6->enabled()) && (! _address_status6_cb.is_empty())) { _address_status6_cb->dispatch(if_atom->name(), vif_atom->name(), addr_atom6->addr(), addr_atom6->prefix_len(), true); } } } #endif } } // // Update the local copy of the interface tree // _iftree = ifmgr_iftree(); } void BGPMain::address_status_change4(const string& interface, const string& vif, const IPv4& source, uint32_t prefix_len, bool state) { debug_msg("interface %s vif %s address %s prefix_len %u state %s\n", interface.c_str(), vif.c_str(), cstring(source), prefix_len, bool_c_str(state)); if (state) { _interfaces_ipv4.insert(make_pair(source, prefix_len)); } else { _interfaces_ipv4.erase(source); } local_ip_changed(source.str()); } bool BGPMain::interface_address4(const IPv4& address) const { return _interfaces_ipv4.end() != _interfaces_ipv4.find(address); } bool BGPMain::interface_address_prefix_len4(const IPv4& address, uint32_t& prefix_len) const { map::const_iterator iter; prefix_len = 0; // XXX: always reset iter = _interfaces_ipv4.find(address); if (iter == _interfaces_ipv4.end()) return (false); prefix_len = iter->second; return (true); } #ifdef DEBUG_MAXIMUM_DELAY #ifndef DEBUG_LOGGING #error "Must use DEBUG_LOGGING in conjunction with DEBUG_MAXIMUM_DELAY" #endif /* DEBUG_LOGGING */ inline bool check_callback_duration() { static time_t last = time(0); static time_t largest = 0; time_t current = time(0); time_t diff = current - last; debug_msg("current %ld largest %ld\n", diff, largest); last = current; largest = diff > largest ? diff : largest; return true; } #endif void BGPMain::main_loop() { debug_msg("BGPMain::main_loop started\n"); #if defined(DEBUG_MAXIMUM_DELAY) static XorpTimer t = eventloop(). new_periodic_ms(1000, callback(check_callback_duration)); #endif while ( xorp_do_run ) { #if defined(DEBUG_MAXIMUM_DELAY) static time_t last = 0; time_t current = time(0); char* date = ctime(¤t); debug_msg("Eventloop: %.*s, %ld\n", static_cast(strlen(date) - 1), date, current - last); #endif eventloop().run(); #if defined (DEBUG_MAXIMUM_DELAY) last = current;; #endif } #if defined(DEBUG_MAXIMUM_DELAY) t.unschedule(); #endif } void BGPMain::local_config(const uint32_t& as, const IPv4& id, bool use_4byte_asnums) { LocalData *local = get_local_data(); local->set_as(AsNum(as)); local->set_id(id); local->set_use_4byte_asnums(use_4byte_asnums); // _plumbing_unicast->set_my_as_number(local->as()); // _plumbing_multicast->set_my_as_number(local->as()); _peerlist->all_stop(true /* restart */); } void BGPMain::set_confederation_identifier(const uint32_t& as, bool disable) { LocalData *local = get_local_data(); if (disable) { local->set_confed_id(AsNum(AsNum::AS_INVALID)); } else { local->set_confed_id(AsNum(as)); } _peerlist->all_stop(true /* restart */); } void BGPMain::set_cluster_id(const IPv4& cluster_id, bool disable) { LocalData *local = get_local_data(); local->set_cluster_id(cluster_id); local->set_route_reflector(!disable); _peerlist->all_stop(true /* restart */); } void BGPMain::set_damping(uint32_t half_life, uint32_t max_suppress,uint32_t reuse, uint32_t suppress, bool disable) { Damping& damping = get_local_data()->get_damping(); damping.set_half_life(half_life); damping.set_max_hold_down(max_suppress); damping.set_reuse(reuse); damping.set_cutoff(suppress); damping.set_damping(!disable); } /* ** Callback registered with the asyncio code. */ void BGPMain::connect_attempt(XorpFd fd, IoEventType type, string laddr, uint16_t lport) { int error; struct sockaddr_storage ss; socklen_t sslen = sizeof(ss); char peer_hostname[MAXHOSTNAMELEN]; if (type != IOT_ACCEPT) { XLOG_WARNING("Unexpected I/O event type %d", type); return; } // Accept the incoming connection. XorpFd connfd = comm_sock_accept(fd); if (!connfd.is_valid()) { XLOG_WARNING("accept failed: %s", comm_get_last_error_str()); return; } debug_msg("Incoming connection attempt %s\n", connfd.str().c_str()); // Query the socket for the peer's address. error = getpeername(connfd, (struct sockaddr *)&ss, &sslen); if (error != 0) { XLOG_FATAL("getpeername() failed: %s", comm_get_last_error_str()); } // Format the peer's address as an ASCII string. if ((error = getnameinfo((struct sockaddr *)&ss, sslen, peer_hostname, sizeof(peer_hostname), 0, 0, NI_NUMERICHOST))) { XLOG_FATAL("getnameinfo() failed: %s", gai_strerror(error)); } _peerlist->dump_list(); // Look for the peer in the list of known peers. list& peers = _peerlist->get_list(); list::iterator i; for (i = peers.begin(); i != peers.end(); i++) { const Iptuple& iptuple = (*i)->peerdata()->iptuple(); debug_msg("Trying: %s ", iptuple.str().c_str()); if (iptuple.get_local_port() == lport && iptuple.get_local_addr() == laddr && iptuple.get_peer_addr() == peer_hostname) { debug_msg(" Succeeded\n"); (*i)->connected(connfd); return; } debug_msg(" Failed\n"); } XLOG_INFO("Connection by %s denied", peer_hostname); if (comm_close(connfd) != XORP_OK) { XLOG_WARNING("Close failed: %s", comm_get_last_error_str()); } /* ** If at some later point in time we want to allow some form of ** promiscuous peering then enable this code. ** NOTE: ** The code below will allow an arbitary host to bring up a BGP ** session. If the peer tears down the session, the current code ** will attempt to re-establish contact. This is probably not what ** is wanted. The connect code requires ** _SocketClient->_Hostname to be set which it isn't. So expect a ** core dump. If we are supporting promiscuous peering maybe we ** never want to attempt to re-establish a connection. So don't ** bother to assign the hostname, just use it as a flag to totally ** drop the peering. */ #if 0 // create new peer debug_msg("Creating new peer after socket connection\n"); BGPPeer *peer = new BGPPeer(_localdata, new BGPPeerData, this, connfd); peer->event_open(); add_peer(peer); #endif } void BGPMain::attach_peer(BGPPeer* peer) { debug_msg("Peer added (fd %s)\n", peer->get_sock().str().c_str()); _peerlist->add_peer(peer); } void BGPMain::detach_peer(BGPPeer* peer) { debug_msg("Peer removed (fd %s)\n", peer->get_sock().str().c_str()); _peerlist->detach_peer(peer); } void BGPMain::attach_deleted_peer(BGPPeer* peer) { debug_msg("Peer added (fd %s)\n", peer->get_sock().str().c_str()); _deleted_peerlist->add_peer(peer); } void BGPMain::detach_deleted_peer(BGPPeer* peer) { debug_msg("Peer removed (fd %s)\n", peer->get_sock().str().c_str()); _deleted_peerlist->detach_peer(peer); } BGPPeer * BGPMain::find_peer(const Iptuple& search, list& peers) { debug_msg("Searching for: %s\n", search.str().c_str()); list::iterator i; for (i = peers.begin(); i != peers.end(); i++) { const Iptuple& iptuple = (*i)->peerdata()->iptuple(); debug_msg("Trying: %s ", iptuple.str().c_str()); if (search == iptuple) { debug_msg(" Succeeded\n"); return *i; } debug_msg(" Failed\n"); } debug_msg("No match\n"); return 0; } BGPPeer * BGPMain::find_peer(const Iptuple& search) { return find_peer(search, _peerlist->get_list()); } BGPPeer * BGPMain::find_deleted_peer(const Iptuple& search) { return find_peer(search, _deleted_peerlist->get_list()); } bool BGPMain::create_peer(BGPPeerData *pd) { debug_msg("Creating new Peer: %s\n", pd->iptuple().str().c_str()); debug_msg("Using local address %s for our nexthop\n", pd->get_v4_local_addr().str().c_str()); pd->dump_peer_data(); // It is possible that a peer that an attempt is being made to // revive a previously dead peer. BGPPeer *p = find_deleted_peer(pd->iptuple()); if (0 != p) { debug_msg("Reviving deleted peer %s\n", cstring(*p)); p->zero_stats(); delete p->swap_peerdata(pd); attach_peer(p); detach_deleted_peer(p); return true; } /* ** Check that we don't already know about this peer. */ if (find_peer(pd->iptuple())) { XLOG_WARNING("This peer already exists: %s %s", pd->iptuple().str().c_str(), pd->as().str().c_str()); return false; } bool md5sig = !pd->get_md5_password().empty(); SocketClient *sock = new SocketClient(pd->iptuple(), eventloop(), md5sig); p = new BGPPeer(_local_data, pd, sock, this); // sock->set_eventloop(eventloop()); sock->set_callback(callback(p, &BGPPeer::get_message)); attach_peer(p); // p->event_start(); return true; } bool BGPMain::delete_peer(const Iptuple& iptuple) { BGPPeer *peer = find_peer(iptuple); if (peer == 0) { XLOG_WARNING("Could not find peer: %s", iptuple.str().c_str()); return false; } if (peer->get_current_peer_state()) { if (!disable_peer(iptuple)) { XLOG_WARNING("Disable peer failed: %s", iptuple.str().c_str()); } } /* XXX - Atanu ** ** A request has been made to delete this peer unfortunately ** disabling a peer is not synchronous. A cease notification needs ** to be sent to the peer if the session is established. Free'ing ** the memory for the peer will stop the notification message ** being sent. Plus there may be issues in tear down all the plumbing. ** ** Move the deleted peer onto the deleted peer list and revive the ** peer if an attempt is made to create the peer again. Care ** should also be taken if the iptuple parameters are changed to ** check that a deleted peer is not matched. ** ** Currently the peers on the deleted peer list are not garbage ** collected. A callback should be implemented from the peer that ** notifies this class that the peer can be deleted once the ** unplumbing concerns are addressed. */ attach_deleted_peer(peer); detach_peer(peer); return true; } bool BGPMain::enable_peer(const Iptuple& iptuple) { BGPPeer *peer = find_peer(iptuple); if (peer == 0) { XLOG_WARNING("Could not find peer: %s", iptuple.str().c_str()); return false; } // // XXX: Clear the last error. // TODO: Maybe we should use zero_stats() instead? // peer->clear_last_error(); peer->event_start(); start_server(iptuple); // Start a server for this peer. peer->set_current_peer_state(true); return true; } bool BGPMain::disable_peer(const Iptuple& iptuple) { BGPPeer *peer = find_peer(iptuple); if (peer == 0) { XLOG_WARNING("Could not find peer: %s", iptuple.str().c_str()); return false; } peer->event_stop(); stop_server(iptuple); // Stop the server for this peer. peer->set_current_peer_state(false); return true; } bool BGPMain::bounce_peer(const Iptuple& iptuple) { BGPPeer *peer = find_peer(iptuple); if (peer == 0) { XLOG_WARNING("Could not find peer: %s", iptuple.str().c_str()); return false; } if (peer->get_current_peer_state() && STATEIDLE == peer->state()) peer->event_start(); else peer->event_stop(true /* restart */); return true; } void BGPMain::local_ip_changed(string local_address) { list& peers = _peerlist->get_list(); list::iterator i; for (i = peers.begin(); i != peers.end(); i++) { const Iptuple& iptuple = (*i)->peerdata()->iptuple(); debug_msg("Trying: %s ", iptuple.str().c_str()); if (iptuple.get_local_addr() == local_address) { XLOG_INFO("Interface address %s changed bouncing peer", local_address.c_str()); bounce_peer(iptuple); } } } bool BGPMain::change_tuple(const Iptuple& iptuple, const Iptuple& nptuple) { BGPPeer *peer = find_peer(iptuple); if (peer == 0) { XLOG_WARNING("Could not find peer: %s", iptuple.str().c_str()); return false; } if (iptuple == nptuple && iptuple.get_peer_port() == nptuple.get_peer_port()) return true; BGPPeerData *pd = new BGPPeerData(*_local_data, nptuple, peer->peerdata()->as(), peer->peerdata()->get_v4_local_addr(), peer->peerdata()->get_configured_hold_time()); if (!create_peer(pd)) { delete pd; return false; } bool state = peer->get_current_peer_state(); delete_peer(iptuple); if (state) { enable_peer(nptuple); } return true; } bool BGPMain::find_tuple_179(string peer_addr, Iptuple& otuple) { list& peers = _peerlist->get_list(); list::iterator i; for (i = peers.begin(); i != peers.end(); i++) { const Iptuple& iptuple = (*i)->peerdata()->iptuple(); debug_msg("Trying: %s ", iptuple.str().c_str()); if (iptuple.get_local_port() == 179 && iptuple.get_peer_port() == 179 && iptuple.get_peer_addr() == peer_addr) { otuple = iptuple; return true; } debug_msg(" Failed\n"); } debug_msg("No match\n"); return false; } bool BGPMain::change_local_ip(const Iptuple& iptuple, const string& local_ip, const string& local_dev) { Iptuple nptuple(local_dev.c_str(), local_ip.c_str(), iptuple.get_local_port(), iptuple.get_peer_addr().c_str(), iptuple.get_peer_port()); // XXX // The change routines were designed to take the original tuple // values that define a peering and then a new value within the // tuple. Unfortunately the router manager cannot at this time // send the old and new values of a variable. // If the old and new setting match then search looking only at // the peer address, assuming both port numbers are 179. The // changing of ports has been disabled in the template file. // if (iptuple.get_local_addr() == local_ip) { Iptuple otuple; if (!find_tuple_179(iptuple.get_peer_addr(), otuple)) { return false; } return change_tuple(otuple, nptuple); } return change_tuple(iptuple, nptuple); } bool BGPMain::change_local_port(const Iptuple& iptuple, uint32_t local_port) { Iptuple nptuple(iptuple.get_local_interface().c_str(), iptuple.get_local_addr().c_str(), local_port, iptuple.get_peer_addr().c_str(), iptuple.get_peer_port()); return change_tuple(iptuple, nptuple); } bool BGPMain::change_peer_port(const Iptuple& iptuple, uint32_t peer_port) { Iptuple nptuple(iptuple.get_local_interface().c_str(), iptuple.get_local_addr().c_str(), iptuple.get_local_port(), iptuple.get_peer_addr().c_str(), peer_port); return change_tuple(iptuple, nptuple); } bool BGPMain::set_peer_as(const Iptuple& iptuple, uint32_t peer_as) { BGPPeer *peer = find_peer(iptuple); if (peer == 0) { XLOG_WARNING("Could not find peer: %s", iptuple.str().c_str()); return false; } AsNum as(peer_as); if ((peer->peerdata())->as() == as) return true; const_cast(peer->peerdata())->set_as(as); bounce_peer(iptuple); return true; } bool BGPMain::set_holdtime(const Iptuple& iptuple, uint32_t holdtime) { BGPPeer *peer = find_peer(iptuple); if (peer == 0) { XLOG_WARNING("Could not find peer: %s", iptuple.str().c_str()); return false; } if (peer->peerdata()->get_configured_hold_time() == holdtime) return true; const_cast(peer->peerdata())-> set_configured_hold_time(holdtime); bounce_peer(iptuple); return true; } bool BGPMain::set_delay_open_time(const Iptuple& iptuple, uint32_t delay_open_time) { BGPPeer *peer = find_peer(iptuple); if (peer == 0) { XLOG_WARNING("Could not find peer: %s", iptuple.str().c_str()); return false; } if (peer->peerdata()->get_delay_open_time() == delay_open_time) return true; const_cast(peer->peerdata())-> set_delay_open_time(delay_open_time); // No need to bounce the peer. // bounce_peer(iptuple); return true; } bool BGPMain::set_route_reflector_client(const Iptuple& iptuple, bool rr) { BGPPeer *peer = find_peer(iptuple); if (peer == 0) { XLOG_WARNING("Could not find peer: %s", iptuple.str().c_str()); return false; } if (peer->peerdata()->route_reflector() == rr) return true; const_cast(peer->peerdata())->set_route_reflector(rr); bounce_peer(iptuple); return true; } bool BGPMain::set_confederation_member(const Iptuple& iptuple, bool conf) { BGPPeer *peer = find_peer(iptuple); if (peer == 0) { XLOG_WARNING("Could not find peer: %s", iptuple.str().c_str()); return false; } if (peer->peerdata()->confederation() == conf) return true; const_cast(peer->peerdata())->set_confederation(conf); bounce_peer(iptuple); return true; } bool BGPMain::set_prefix_limit(const Iptuple& iptuple, uint32_t maximum, bool state) { BGPPeer *peer = find_peer(iptuple); if (peer == 0) { XLOG_WARNING("Could not find peer: %s", iptuple.str().c_str()); return false; } ConfigVar &prefix_limit = const_cast(peer->peerdata())->get_prefix_limit(); prefix_limit.set_var(maximum); prefix_limit.set_enabled(state); return true; } bool BGPMain::set_nexthop4(const Iptuple& iptuple, const IPv4& next_hop) { BGPPeer *peer = find_peer(iptuple); if (peer == 0) { XLOG_WARNING("Could not find peer: %s", iptuple.str().c_str()); return false; } const_cast(peer->peerdata())->set_v4_local_addr(next_hop); bounce_peer(iptuple); return true; } bool BGPMain::set_peer_state(const Iptuple& iptuple, bool state) { BGPPeer *peer = find_peer(iptuple); if (peer == 0) { XLOG_WARNING("Could not find peer: %s", iptuple.str().c_str()); return false; } peer->set_next_peer_state(state); if (false == peer->get_activate_state()) return true; return activate(iptuple); } bool BGPMain::activate(const Iptuple& iptuple) { BGPPeer *peer = find_peer(iptuple); if (peer == 0) { XLOG_WARNING("Could not find peer: %s", iptuple.str().c_str()); return false; } peer->set_activate_state(true); // Delay the activation of the peers until we have seen the first // policy push. if (!_first_policy_push) return true; if (peer->get_current_peer_state() == peer->get_next_peer_state()) return true; if (peer->get_next_peer_state()) { enable_peer(iptuple); } else { disable_peer(iptuple); } return true; } bool BGPMain::activate_all_peers() { list& peers = _peerlist->get_list(); list::iterator i; for (i = peers.begin(); i != peers.end(); i++) { const Iptuple& iptuple = (*i)->peerdata()->iptuple(); debug_msg("Trying: %s ", iptuple.str().c_str()); if ((*i)->get_current_peer_state() == (*i)->get_next_peer_state()) continue; if ((*i)->get_next_peer_state()) { enable_peer(iptuple); } else { disable_peer(iptuple); } } return true; } bool BGPMain::set_peer_md5_password(const Iptuple& iptuple, const string& password) { BGPPeer *peer = find_peer(iptuple); if (peer == NULL) { XLOG_WARNING("Could not find peer: %s", iptuple.str().c_str()); return false; } // The md5-password property has to belong to BGPPeerData, because // it is instantiated before BGPPeer and before SocketClient. BGPPeerData* pd = const_cast(peer->peerdata()); pd->set_md5_password(password); return true; } bool BGPMain::next_hop_rewrite_filter(const Iptuple& iptuple, const IPv4& next_hop) { BGPPeer *peer = find_peer(iptuple); if (0 == peer) { XLOG_WARNING("Could not find peer: %s", iptuple.str().c_str()); return false; } BGPPeerData* pd = const_cast(peer->peerdata()); pd->set_next_hop_rewrite(next_hop); return true; } bool BGPMain::get_peer_list_start(uint32_t& token) { return _peerlist->get_peer_list_start(token); } bool BGPMain::get_peer_list_next(const uint32_t& token, string& local_ip, uint32_t& local_port, string& peer_ip, uint32_t& peer_port) { return _peerlist->get_peer_list_next(token, local_ip, local_port, peer_ip, peer_port); } bool BGPMain::get_peer_id(const Iptuple& iptuple, IPv4& peer_id) { BGPPeer *peer = find_peer(iptuple); if (0 == peer) { XLOG_WARNING("Could not find peer: %s", iptuple.str().c_str()); return false; } peer_id = peer->peerdata()->id(); return true; } bool BGPMain::get_peer_status(const Iptuple& iptuple, uint32_t& peer_state, uint32_t& admin_status) { BGPPeer *peer = find_peer(iptuple); if (0 == peer) { XLOG_WARNING("Could not find peer: %s", iptuple.str().c_str()); return false; } peer_state = peer->state(); //XXX state stopped is only for our internal use. if (peer_state == STATESTOPPED) peer_state = STATEIDLE; admin_status = peer->get_current_peer_state() ? 2 : 1; return true; } bool BGPMain::get_peer_negotiated_version(const Iptuple& iptuple, int32_t& neg_version) { BGPPeer *peer = find_peer(iptuple); if (0 == peer) { XLOG_WARNING("Could not find peer: %s", iptuple.str().c_str()); return false; } if (peer->state() == STATEESTABLISHED) neg_version = 4; /* we only support BGP 4 */ else neg_version = 0; /* XXX the MIB doesn't say what version should be reported when the connection isn't established */ return true; } bool BGPMain::get_peer_as(const Iptuple& iptuple, uint32_t& peer_as) { BGPPeer *peer = find_peer(iptuple); if (0 == peer) { XLOG_WARNING("Could not find peer: %s", iptuple.str().c_str()); return false; } const BGPPeerData* pd = peer->peerdata(); // XXX is it appropriate to return an extended AS number in this // context? peer_as = pd->as().as4(); return true; } bool BGPMain::get_peer_msg_stats(const Iptuple& iptuple, uint32_t& in_updates, uint32_t& out_updates, uint32_t& in_msgs, uint32_t& out_msgs, uint16_t& last_error, uint32_t& in_update_elapsed) { BGPPeer *peer = find_peer(iptuple); if (0 == peer) { XLOG_WARNING("Could not find peer: %s", iptuple.str().c_str()); return false; } peer->get_msg_stats(in_updates, out_updates, in_msgs, out_msgs, last_error, in_update_elapsed); return true; } bool BGPMain::get_peer_established_stats(const Iptuple& iptuple, uint32_t& transitions, uint32_t& established_time) { BGPPeer *peer = find_peer(iptuple); if (0 == peer) { XLOG_WARNING("Could not find peer: %s", iptuple.str().c_str()); return false; } transitions = peer->get_established_transitions(); established_time = peer->get_established_time(); return true; } bool BGPMain::get_peer_timer_config(const Iptuple& iptuple, uint32_t& retry_interval, uint32_t& hold_time, uint32_t& keep_alive, uint32_t& hold_time_configured, uint32_t& keep_alive_configured, uint32_t& min_as_origination_interval, uint32_t& min_route_adv_interval) { BGPPeer *peer = find_peer(iptuple); if (0 == peer) { XLOG_WARNING("Could not find peer: %s", iptuple.str().c_str()); return false; } const BGPPeerData* pd = peer->peerdata(); retry_interval = pd->get_retry_duration(); hold_time = pd->get_hold_duration(); keep_alive = pd->get_keepalive_duration(); hold_time_configured = pd->get_configured_hold_time(); keep_alive_configured = hold_time_configured/3; //XXX min_as_origination_interval = 0; //XXX min_route_adv_interval = 0; //XXX return true; } bool BGPMain::register_ribname(const string& name) { debug_msg("%s\n", name.c_str()); if (!_rib_ipc_handler->register_ribname(name)) return false; if (!plumbing_unicast()-> plumbing_ipv4().next_hop_resolver().register_ribname(name)) return false; #ifdef HAVE_IPV6 if (!plumbing_unicast()-> plumbing_ipv6().next_hop_resolver().register_ribname(name)) return false; #endif return true; } XorpFd BGPMain::create_listener(const Iptuple& iptuple) { SocketServer s = SocketServer(iptuple, eventloop()); s.create_listener(); return s.get_sock(); } LocalData* BGPMain::get_local_data() { return _local_data; } void BGPMain::start_server(const Iptuple& iptuple) { debug_msg("starting server on %s\n", iptuple.str().c_str()); /* ** Its possible to be called with the same iptuple multiple ** times. Because repeated calls to enable are possible. ** Filter out the repeat calls. */ bool match = false; list::iterator i; for (i = _serverfds.begin(); i != _serverfds.end(); i++) { list::iterator j; for (j = i->_tuples.begin(); j != i->_tuples.end(); j++) { if (*j == iptuple) // This tuple already in list. return; if ((j->get_local_addr() == iptuple.get_local_addr() && j->get_local_port() == iptuple.get_local_port())) match = true; } /* ** If we are already listening on this address/port pair then ** add the iptuple to the list and return. */ if (match) { i->_tuples.push_back(iptuple); return; } } XorpFd sfd = create_listener(iptuple); if (!sfd.is_valid()) { return; } if (!eventloop(). add_ioevent_cb(sfd, IOT_ACCEPT, callback(this, &BGPMain::connect_attempt, iptuple.get_local_addr(), iptuple.get_local_port()))) { XLOG_ERROR("Failed to add socket %s to eventloop", sfd.str().c_str()); } _serverfds.push_back(Server(sfd, iptuple)); } void BGPMain::stop_server(const Iptuple& iptuple) { debug_msg("stopping server on %s\n", iptuple.str().c_str()); list::iterator i; for (i = _serverfds.begin(); i != _serverfds.end(); i++) { list::iterator j; for (j = i->_tuples.begin(); j != i->_tuples.end(); j++) { if (*j == iptuple) { i->_tuples.erase(j); if (i->_tuples.empty()) { eventloop().remove_ioevent_cb(i->_serverfd); comm_close(i->_serverfd); _serverfds.erase(i); } return; } } } XLOG_WARNING("Attempt to remove a listener that doesn't exist: %s", iptuple.str().c_str()); } void BGPMain::stop_all_servers() { debug_msg("stop_all_servers\n"); list::iterator i; for (i = _serverfds.begin(); i != _serverfds.end();) { debug_msg("%s\n", i->str().c_str()); eventloop().remove_ioevent_cb(i->_serverfd); comm_close(i->_serverfd); _serverfds.erase(i++); } } bool BGPMain::originate_route(const IPv4Net& nlri, const IPv4& next_hop, const bool& unicast, const bool& multicast, const PolicyTags& policytags) { debug_msg("nlri %s next hop %s unicast %d multicast %d\n", nlri.str().c_str(), next_hop.str().c_str(), unicast, multicast); ASPath aspath; return _rib_ipc_handler->originate_route(IGP, aspath, nlri, next_hop, unicast, multicast, policytags); } bool BGPMain::withdraw_route(const IPv4Net& nlri, const bool& unicast, const bool& multicast) const { debug_msg("nlri %s unicast %d multicast %d\n", nlri.str().c_str(), unicast, multicast); return _rib_ipc_handler->withdraw_route(nlri, unicast, multicast); } template <> BGPMain::RoutingTableToken& BGPMain::get_token_table() { return _table_ipv4; } bool BGPMain::rib_client_route_info_changed4(const IPv4& addr, const uint32_t& prefix_len, const IPv4& nexthop, const uint32_t& metric) { debug_msg("rib_client_route_info_changed4:" " addr %s prefix_len %u nexthop %s metric %u\n", addr.str().c_str(), XORP_UINT_CAST(prefix_len), nexthop.str().c_str(), XORP_UINT_CAST(metric)); return plumbing_unicast()->plumbing_ipv4(). next_hop_resolver().rib_client_route_info_changed(addr, prefix_len, nexthop, metric); } bool BGPMain::rib_client_route_info_invalid4(const IPv4& addr, const uint32_t& prefix_len) { debug_msg("rib_client_route_info_invalid4:" " addr %s prefix_len %u\n", addr.str().c_str(), XORP_UINT_CAST(prefix_len)); return plumbing_unicast()->plumbing_ipv4(). next_hop_resolver().rib_client_route_info_invalid(addr, prefix_len); } bool BGPMain::set_parameter(const Iptuple& iptuple , const string& parameter, const bool toggle) { BGPPeer *peer; // BGPPeerData *peerdata; if ((0 == (peer = find_peer(iptuple)))) { XLOG_WARNING("Could not find peer: %s", iptuple.str().c_str()); return false; } ParameterNode node; if (strcmp(parameter.c_str(),"Refresh_Capability") == 0) { /* ** This is a place holder and example as to how to set ** parameters/capabilities on a per per basis. Currently Route ** Refresh isn't supported, but it is a nice example. APG - 2002-10-21 */ XLOG_WARNING("No support for route refresh (yet)."); // debug_msg("Setting Refresh capability for peer %s.", // iptuple.str().c_str()); // peerdata = const_cast(peer->peerdata()); // peerdata->add_sent_parameter( new BGPRefreshCapability() ); } else if (strcmp(parameter.c_str(),"MultiProtocol.IPv4.Unicast") == 0) { debug_msg("IPv4 Unicast\n"); node = new BGPMultiProtocolCapability(AFI_IPV4, SAFI_UNICAST); } else if (strcmp(parameter.c_str(),"MultiProtocol.IPv4.Multicast") == 0) { debug_msg("IPv4 Multicast\n"); node = new BGPMultiProtocolCapability(AFI_IPV4, SAFI_MULTICAST); #ifdef HAVE_IPV6 } else if (strcmp(parameter.c_str(),"MultiProtocol.IPv6.Unicast") == 0) { debug_msg("IPv6 Unicast\n"); node = new BGPMultiProtocolCapability(AFI_IPV6, SAFI_UNICAST); } else if (strcmp(parameter.c_str(),"MultiProtocol.IPv6.Multicast") == 0) { debug_msg("IPv6 Multicast\n"); node = new BGPMultiProtocolCapability(AFI_IPV6, SAFI_MULTICAST); #endif } else { XLOG_WARNING("Unable to set unknown parameter: <%s>.", parameter.c_str()); return false; } debug_msg("Peer %s. %s\n", iptuple.str().c_str(), bool_c_str(toggle)); BGPPeerData *peerdata = const_cast(peer->peerdata()); if (toggle) { peerdata->add_sent_parameter(node); } else { peerdata->remove_sent_parameter(node); } return true; } void BGPMain::configure_filter(const uint32_t& filter, const string& conf) { _policy_filters.configure(filter,conf); } void BGPMain::reset_filter(const uint32_t& filter) { _policy_filters.reset(filter); } void BGPMain::push_routes() { _plumbing_unicast->push_routes(); _plumbing_multicast->push_routes(); if (!_first_policy_push) { _first_policy_push = true; activate_all_peers(); } } /** IPv6 stuff */ #ifdef HAVE_IPV6 bool BGPMain::is_address_enabled(const string& interface, const string& vif, const IPv6& address) const { debug_msg("Interface %s Vif %s Address %s\n", interface.c_str(), vif.c_str(), cstring(address)); if (! is_vif_enabled(interface, vif)) return false; const IfMgrIPv6Atom* fa = ifmgr_iftree().find_addr(interface, vif, address); if (fa == NULL) return false; return (fa->enabled()); } uint32_t BGPMain::get_prefix_length(const string& interface, const string& vif, const IPv6& address) { debug_msg("Interface %s Vif %s Address %s\n", interface.c_str(), vif.c_str(), cstring(address)); const IfMgrIPv6Atom* fa = ifmgr_iftree().find_addr(interface, vif, address); if (fa == NULL) return 0; return (fa->prefix_len()); } void BGPMain::address_status_change6(const string& interface, const string& vif, const IPv6& source, uint32_t prefix_len, bool state) { debug_msg("interface %s vif %s address %s prefix_len %u state %s\n", interface.c_str(), vif.c_str(), cstring(source), prefix_len, bool_c_str(state)); if (state) { _interfaces_ipv6.insert(make_pair(source, prefix_len)); } else { _interfaces_ipv6.erase(source); } local_ip_changed(source.str()); } bool BGPMain::interface_address6(const IPv6& address) const { return _interfaces_ipv6.end() != _interfaces_ipv6.find(address); } bool BGPMain::interface_address_prefix_len6(const IPv6& address, uint32_t& prefix_len) const { map::const_iterator iter; prefix_len = 0; // XXX: always reset iter = _interfaces_ipv6.find(address); if (iter == _interfaces_ipv6.end()) return (false); prefix_len = iter->second; return (true); } bool BGPMain::set_nexthop6(const Iptuple& iptuple, const IPv6& next_hop) { BGPPeer *peer = find_peer(iptuple); if (peer == 0) { XLOG_WARNING("Could not find peer: %s", iptuple.str().c_str()); return false; } const_cast(peer->peerdata())->set_v6_local_addr(next_hop); bounce_peer(iptuple); return true; } bool BGPMain::get_nexthop6(const Iptuple& iptuple, IPv6& next_hop) { BGPPeer *peer = find_peer(iptuple); if (peer == 0) { XLOG_WARNING("Could not find peer: %s", iptuple.str().c_str()); return false; } next_hop = peer->peerdata()->get_v6_local_addr(); return true; } bool BGPMain::originate_route(const IPv6Net& nlri, const IPv6& next_hop, const bool& unicast, const bool& multicast, const PolicyTags& policytags) { debug_msg("nlri %s next hop %s unicast %d multicast %d\n", nlri.str().c_str(), next_hop.str().c_str(), unicast, multicast); ASPath aspath; return _rib_ipc_handler->originate_route(IGP, aspath, nlri, next_hop, unicast, multicast, policytags); } bool BGPMain::withdraw_route(const IPv6Net& nlri, const bool& unicast, const bool& multicast) const { debug_msg("nlri %s unicast %d multicast %d\n", nlri.str().c_str(), unicast, multicast); return _rib_ipc_handler->withdraw_route(nlri, unicast, multicast); } template <> BGPMain::RoutingTableToken& BGPMain::get_token_table() { return _table_ipv6; } bool BGPMain::rib_client_route_info_changed6(const IPv6& addr, const uint32_t& prefix_len, const IPv6& nexthop, const uint32_t& metric) { debug_msg("rib_client_route_info_changed6:" " addr %s prefix_len %u nexthop %s metric %u\n", addr.str().c_str(), XORP_UINT_CAST(prefix_len), nexthop.str().c_str(), XORP_UINT_CAST(metric)); return plumbing_unicast()->plumbing_ipv6(). next_hop_resolver().rib_client_route_info_changed(addr, prefix_len, nexthop, metric); } bool BGPMain::rib_client_route_info_invalid6(const IPv6& addr, const uint32_t& prefix_len) { debug_msg("rib_client_route_info_invalid6:" " addr %s prefix_len %u\n", addr.str().c_str(), XORP_UINT_CAST(prefix_len)); return plumbing_unicast()->plumbing_ipv6(). next_hop_resolver().rib_client_route_info_invalid(addr, prefix_len); } #endif //ipv6 xorp/bgp/route_table_aggregation.hh0000664000076400007640000001777511421137511017642 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/bgp/route_table_aggregation.hh,v 1.19 2008/11/08 06:14:38 mjh Exp $ #ifndef __BGP_ROUTE_TABLE_AGGREGATION_HH__ #define __BGP_ROUTE_TABLE_AGGREGATION_HH__ #include "peer_handler.hh" #include "route_table_base.hh" #include "libxorp/ref_trie.hh" /** * @short classes used for BGP prefix aggregation. * * The main aggregation processing occurs and all the state is kept * in an AggregationTable stage that is plumbed between the decission * and fanout tables. Additionally, static filters immediately following * the fanout table may filter out certain routes marked by the * AggregationTable, depending on whether the outbound branch is of IBGP * or EBGP type. * +---------+ * ->| Filters |->IBGP... * \ / +---------+ * ->+-----------+ +--------+ +--------+/ +---------+ * -->| Decission |->| Aggreg.|->| Fanout |--->| Filters |->IBGP... * ->+-----------+ +--------+ +--------+\ +---------+ * / \ +---------+ * Processing, ->| Filters |->EBGP... * state, +---------+ * marking Static filters * * Routes can be marked as candidates for aggregation using the generic * policy framework before they enter the Aggregation stage. Routes not * marked for aggregation, or those improperly marked, will be * propagated through both the aggregation and static filtering stages * without any processing whatsoever, regardless on the downstream * peering type (IBGP or EBGP). * * Routes that can trigger instantiation of an aggregate route (those * that have been properly marked) will be copied and copies stored in * the AggregationTable for further processing. Regardless of the * outcome of the aggregation processing, every original route will be * marked as IBGP_ONLY and propagated downstream. Filtering stages * residing on IBGP paths will propagate such routes further downstream, * while on EBGP paths all routes marked as IBGP_ONLY will be discarded. * * In addition to original routes marked as described in the previous * paragraph, the AggregationStage can emit both the aggregate and * copies of the original routes. Any such routes will be marked with * an appropriate EBGP_ONLY_* marker, instructing the filtering stages * on IBGP branches to unconditionally drop them. On EBGP branches... * * * Open issues: * * - return values for add/delete/replace routes * * - aggregate route tagging / dumping when a new EBGP peering comes up * * - {de|re}aggragetion of large aggregates -> timer based vs. atomic */ class XrlStdRouter; template class DumpIterator; template class AggregationTable; template class ComponentRoute { public: ComponentRoute(const SubnetRoute* route, const PeerHandler *origin, uint32_t genid, bool from_previous_peering) : _routeref(route) { _origin_peer = origin; _genid = genid; _from_previous_peering = from_previous_peering; } const SubnetRoute* route() const { return _routeref.route(); } const PeerHandler* origin_peer() const { return _origin_peer; } uint32_t genid() const { return _genid; } bool from_previous_peering() const { return _from_previous_peering; } private: SubnetRouteConstRef _routeref; const PeerHandler *_origin_peer; uint32_t _genid; bool _from_previous_peering; }; template class AggregateRoute { public: AggregateRoute(IPNet net, bool brief_mode, IPv4 bgp_id, AsNum asnum) : _net(net), _brief_mode(brief_mode), _was_announced(0), _is_suppressed(0) { OriginAttribute origin_att(IGP); FPAListRef fpa_list = new FastPathAttributeList(A::ZERO(), ASPath(), origin_att); _pa_list = new PathAttributeList(fpa_list); _aggregator_attribute = new AggregatorAttribute(bgp_id, asnum); } ~AggregateRoute() { if (_components_table.begin() != _components_table.end()) XLOG_WARNING("ComponentsTable trie was not empty on deletion\n"); delete _aggregator_attribute; } PAListRef pa_list() const { return _pa_list; } const IPNet net() const { return _net; } bool was_announced() const { return _was_announced; } bool is_suppressed() const { return _is_suppressed; } bool brief_mode() const { return _brief_mode; } void was_announced(bool value) { _was_announced = value; } void is_suppressed(bool value) { _is_suppressed = value; } void brief_mode(bool value) { _brief_mode = value; } void reevaluate(AggregationTable *parent); RefTrie > *components_table() { return &_components_table; } private: const IPNet _net; bool _brief_mode; AggregatorAttribute *_aggregator_attribute; RefTrie > _components_table; PAListRef _pa_list; bool _was_announced; bool _is_suppressed; }; template class AggregationTable : public BGPRouteTable { public: AggregationTable(string tablename, BGPPlumbing& master, BGPRouteTable *parent); ~AggregationTable(); int add_route(InternalMessage &rtmsg, BGPRouteTable *caller); int replace_route(InternalMessage &old_rtmsg, InternalMessage &new_rtmsg, BGPRouteTable *caller); int delete_route(InternalMessage &rtmsg, BGPRouteTable *caller); int push(BGPRouteTable *caller); bool dump_next_route(DumpIterator& dump_iter); int route_dump(InternalMessage &rtmsg, BGPRouteTable *caller, const PeerHandler *dump_peer); const SubnetRoute *lookup_route(const IPNet &net, uint32_t& genid, FPAListRef& pa_list) const; void route_used(const SubnetRoute* route, bool in_use); RouteTableType type() const {return AGGREGATION_TABLE;} string str() const; /* mechanisms to implement flow control in the output plumbing */ bool get_next_message(BGPRouteTable *next_table); int route_count() const { return _aggregates_table.route_count(); // XXX is this OK? } void peering_went_down(const PeerHandler *peer, uint32_t genid, BGPRouteTable *caller); void peering_down_complete(const PeerHandler *peer, uint32_t genid, BGPRouteTable *caller); void peering_came_up(const PeerHandler *peer, uint32_t genid, BGPRouteTable *caller); private: friend class AggregateRoute; RefTrie > _aggregates_table; BGPPlumbing& _master_plumbing; }; class AggregationHandler : public PeerHandler { public: AggregationHandler(); virtual PeerType get_peer_type() const { return PEER_TYPE_INTERNAL; } uint32_t get_unique_id() const { return _fake_unique_id; } virtual bool originate_route_handler() const { return true; } const uint32_t _fake_unique_id; }; #endif // __BGP_ROUTE_TABLE_AGGREGATION_HH__ xorp/fib2mrib/0000775000076400007640000000000011631505551013356 5ustar greearbgreearbxorp/fib2mrib/fib2mrib_node.cc0000664000076400007640000010222011540224225016356 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // Fib2mrib node implementation. // #include "fib2mrib_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvx.hh" #include "fib2mrib_node.hh" #include "fib2mrib_varrw.hh" Fib2mribNode::Fib2mribNode(EventLoop& eventloop) : ServiceBase("Fib2mrib"), _eventloop(eventloop), _protocol_name("fib2mrib"), // TODO: must be known by RIB _is_enabled(true), // XXX: enabled by default _startup_requests_n(0), _shutdown_requests_n(0), _is_log_trace(true) // XXX: default to print trace logs { set_node_status(PROC_STARTUP); } Fib2mribNode::~Fib2mribNode() { shutdown(); } int Fib2mribNode::startup() { // // Test the service status // if ((ServiceBase::status() == SERVICE_STARTING) || (ServiceBase::status() == SERVICE_RUNNING)) { return (XORP_OK); } if (ServiceBase::status() != SERVICE_READY) { return (XORP_ERROR); } // // Transition to SERVICE_RUNNING occurs when all transient startup // operations are completed (e.g., after we have the interface/vif/address // state available, after we have registered with the RIB, etc.) // ServiceBase::set_status(SERVICE_STARTING); // // Set the node status // set_node_status(PROC_STARTUP); // // Register with the FEA // fea_register_startup(); // // Register with the RIB // rib_register_startup(); return (XORP_OK); } int Fib2mribNode::shutdown() { // // 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); } // // Transition to SERVICE_SHUTDOWN occurs when all transient shutdown // operations are completed (e.g., after we have deregistered with the FEA // and the RIB, etc.) // ServiceBase::set_status(SERVICE_SHUTTING_DOWN); // // De-register with the RIB // rib_register_shutdown(); // // De-register with the FEA // fea_register_shutdown(); // // Set the node status // set_node_status(PROC_SHUTDOWN); // // Update the node status // update_status(); return (XORP_OK); } void Fib2mribNode::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 set_node_status(PROC_READY); return; } if ((old_status == SERVICE_SHUTTING_DOWN) && (new_status == SERVICE_SHUTDOWN)) { // The shutdown process has completed 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)) { decr_shutdown_requests_n(); } } } void Fib2mribNode::incr_startup_requests_n() { _startup_requests_n++; XLOG_ASSERT(_startup_requests_n > 0); } void Fib2mribNode::decr_startup_requests_n() { XLOG_ASSERT(_startup_requests_n > 0); _startup_requests_n--; update_status(); } void Fib2mribNode::incr_shutdown_requests_n() { _shutdown_requests_n++; XLOG_ASSERT(_shutdown_requests_n > 0); } void Fib2mribNode::decr_shutdown_requests_n() { XLOG_ASSERT(_shutdown_requests_n > 0); _shutdown_requests_n--; update_status(); } void Fib2mribNode::update_status() { // // Test if the startup process has completed // if (ServiceBase::status() == SERVICE_STARTING) { if (_startup_requests_n > 0) return; // The startup process has completed ServiceBase::set_status(SERVICE_RUNNING); set_node_status(PROC_READY); return; } // // Test if the shutdown process has completed // if (ServiceBase::status() == SERVICE_SHUTTING_DOWN) { if (_shutdown_requests_n > 0) return; // The shutdown process has completed ServiceBase::set_status(SERVICE_SHUTDOWN); set_node_status(PROC_DONE); return; } // // Test if we have failed // if (ServiceBase::status() == SERVICE_FAILED) { set_node_status(PROC_DONE); return; } } ProcessStatus Fib2mribNode::node_status(string& reason_msg) { ProcessStatus status = _node_status; // Set the return message with the reason reason_msg = ""; switch (status) { case PROC_NULL: // Can't be running and in this state XLOG_UNREACHABLE(); break; case PROC_STARTUP: // Get the message about the startup progress reason_msg = c_format("Waiting for %u startup events", XORP_UINT_CAST(_startup_requests_n)); break; case PROC_NOT_READY: // XXX: this state is unused XLOG_UNREACHABLE(); break; case PROC_READY: reason_msg = c_format("Node is READY"); break; case PROC_SHUTDOWN: // Get the message about the shutdown progress reason_msg = c_format("Waiting for %u shutdown events", XORP_UINT_CAST(_shutdown_requests_n)); break; case PROC_FAILED: // XXX: this state is unused XLOG_UNREACHABLE(); break; case PROC_DONE: // Process has completed operation break; default: // Unknown status XLOG_UNREACHABLE(); break; } return (status); } void Fib2mribNode::tree_complete() { decr_startup_requests_n(); // // XXX: we use same actions when the tree is completed or updates are made // updates_made(); } void Fib2mribNode::updates_made() { multimap::iterator route_iter; list add_routes, replace_routes, delete_routes; list::iterator pending_iter; for (route_iter = _fib2mrib_routes.begin(); route_iter != _fib2mrib_routes.end(); ++route_iter) { Fib2mribRoute& fib2mrib_route = route_iter->second; bool is_old_up = false; bool is_new_up = false; string old_ifname, old_vifname, new_ifname, new_vifname; update_route(ifmgr_iftree(), fib2mrib_route); if (fib2mrib_route.is_interface_route()) { // // Calculate whether the interface was UP before and now. // const IfMgrIfAtom* if_atom; const IfMgrVifAtom* vif_atom; if_atom = _iftree.find_interface(fib2mrib_route.ifname()); vif_atom = _iftree.find_vif(fib2mrib_route.ifname(), fib2mrib_route.vifname()); if ((if_atom != NULL) && (if_atom->enabled()) && (! if_atom->no_carrier()) && (vif_atom != NULL) && (vif_atom->enabled())) { is_old_up = true; } if_atom = ifmgr_iftree().find_interface(fib2mrib_route.ifname()); vif_atom = ifmgr_iftree().find_vif(fib2mrib_route.ifname(), fib2mrib_route.vifname()); if ((if_atom != NULL) && (if_atom->enabled()) && (! if_atom->no_carrier()) && (vif_atom != NULL) && (vif_atom->enabled())) { is_new_up = true; } } else { // // Calculate whether the next-hop router was directly connected // before and now. // if (_iftree.is_directly_connected(fib2mrib_route.nexthop(), old_ifname, old_vifname)) { is_old_up = true; } if (ifmgr_iftree().is_directly_connected(fib2mrib_route.nexthop(), new_ifname, new_vifname)) { is_new_up = true; } } if ((is_old_up == is_new_up) && (old_ifname == new_ifname) && (old_vifname == new_vifname)) { continue; // Nothing changed } if ((! is_old_up) && (! is_new_up)) { // // The interface s still down, so nothing to do // continue; } if ((! is_old_up) && (is_new_up)) { // // The interface is now up, hence add the route // add_routes.push_back(&fib2mrib_route); continue; } if ((is_old_up) && (! is_new_up)) { // // The interface went down, hence cancel all pending requests, // and withdraw the route. // delete_routes.push_back(&fib2mrib_route); continue; } if (is_old_up && is_new_up) { // // The interface remains up, hence probably the interface or // the vif name has changed. // Delete the route and then add it again so the information // in the RIB will be updated. // replace_routes.push_back(&fib2mrib_route); continue; } } // // Update the local copy of the interface tree // _iftree = ifmgr_iftree(); // // Process all pending "add route" requests // for (pending_iter = add_routes.begin(); pending_iter != add_routes.end(); ++pending_iter) { Fib2mribRoute& orig_route = *(*pending_iter); Fib2mribRoute copy_route = orig_route; prepare_route_for_transmission(orig_route, copy_route); copy_route.set_add_route(); inform_rib(copy_route); } // // Process all pending "replace route" requests // for (pending_iter = replace_routes.begin(); pending_iter != replace_routes.end(); ++pending_iter) { Fib2mribRoute& orig_route = *(*pending_iter); Fib2mribRoute copy_route = orig_route; // First delete the route, then add the route prepare_route_for_transmission(orig_route, copy_route); copy_route.set_delete_route(); inform_rib(copy_route); copy_route = orig_route; prepare_route_for_transmission(orig_route, copy_route); copy_route.set_add_route(); inform_rib(copy_route); } // // Process all pending "delete route" requests // for (pending_iter = delete_routes.begin(); pending_iter != delete_routes.end(); ++pending_iter) { Fib2mribRoute& orig_route = *(*pending_iter); cancel_rib_route_change(orig_route); Fib2mribRoute copy_route = orig_route; prepare_route_for_transmission(orig_route, copy_route); copy_route.set_delete_route(); inform_rib(copy_route); } } /** * Enable/disable node operation. * * Note that for the time being it affects only whether the routes * are installed into RIB. In the future it may affect the interaction * with other modules as well. * * @param enable if true then enable node operation, otherwise disable it. */ void Fib2mribNode::set_enabled(bool enable) { if (enable == is_enabled()) return; // XXX: nothing changed if (enable) { _is_enabled = true; push_pull_rib_routes(true); } else { push_pull_rib_routes(false); _is_enabled = false; } } /** * Add an IPv4 route. * * @param network the network address prefix this route applies to. * @param nexthop the address of the next-hop router for this route. * @param ifname the name of the physical interface toward the * destination. * @param vifname the name of the virtual interface toward the * destination. * @param metric the routing metric for this route. * @param admin_distance the administratively defined distance for this * route. * @param protocol_origin the name of the protocol that originated this * route. * @param xorp_route true if this route was installed by XORP. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int Fib2mribNode::add_route4(const IPv4Net& network, const IPv4& nexthop, const string& ifname, const string& vifname, uint32_t metric, uint32_t admin_distance, const string& protocol_origin, bool xorp_route, string& error_msg) { Fib2mribRoute fib2mrib_route(network, nexthop, ifname, vifname, metric, admin_distance, protocol_origin, xorp_route); fib2mrib_route.set_add_route(); return (add_route(fib2mrib_route, error_msg)); } /** * Add an IPv6 route. * * @param network the network address prefix this route applies to. * @param nexthop the address of the next-hop router for this route. * @param ifname the name of the physical interface toward the * destination. * @param vifname the name of the virtual interface toward the * destination. * @param metric the routing metric for this route. * @param admin_distance the administratively defined distance for this * route. * @param protocol_origin the name of the protocol that originated this * route. * @param xorp_route true if this route was installed by XORP. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int Fib2mribNode::add_route6(const IPv6Net& network, const IPv6& nexthop, const string& ifname, const string& vifname, uint32_t metric, uint32_t admin_distance, const string& protocol_origin, bool xorp_route, string& error_msg) { Fib2mribRoute fib2mrib_route(network, nexthop, ifname, vifname, metric, admin_distance, protocol_origin, xorp_route); fib2mrib_route.set_add_route(); return (add_route(fib2mrib_route, error_msg)); } /** * Replace an IPv4 route. * * @param network the network address prefix this route applies to. * @param nexthop the address of the next-hop router for this route. * @param ifname the name of the physical interface toward the * destination. * @param vifname the name of the virtual interface toward the * destination. * @param metric the routing metric for this route. * @param admin_distance the administratively defined distance for this * route. * @param protocol_origin the name of the protocol that originated this * route. * @param xorp_route true if this route was installed by XORP. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int Fib2mribNode::replace_route4(const IPv4Net& network, const IPv4& nexthop, const string& ifname, const string& vifname, uint32_t metric, uint32_t admin_distance, const string& protocol_origin, bool xorp_route, string& error_msg) { Fib2mribRoute fib2mrib_route(network, nexthop, ifname, vifname, metric, admin_distance, protocol_origin, xorp_route); fib2mrib_route.set_replace_route(); return (replace_route(fib2mrib_route, error_msg)); } /** * Replace an IPv6 route. * * @param network the network address prefix this route applies to. * @param nexthop the address of the next-hop router for this route. * @param ifname the name of the physical interface toward the * destination. * @param vifname the name of the virtual interface toward the * destination. * @param metric the routing metric for this route. * @param admin_distance the administratively defined distance for this * route. * @param protocol_origin the name of the protocol that originated this * route. * @param xorp_route true if this route was installed by XORP. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int Fib2mribNode::replace_route6(const IPv6Net& network, const IPv6& nexthop, const string& ifname, const string& vifname, uint32_t metric, uint32_t admin_distance, const string& protocol_origin, bool xorp_route, string& error_msg) { Fib2mribRoute fib2mrib_route(network, nexthop, ifname, vifname, metric, admin_distance, protocol_origin, xorp_route); fib2mrib_route.set_replace_route(); return (replace_route(fib2mrib_route, error_msg)); } /** * Delete an IPv4 route. * * @param network the network address prefix this route applies to. * @param ifname the name of the physical interface toward the * destination. * @param vifname the name of the virtual interface toward the * destination. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int Fib2mribNode::delete_route4(const IPv4Net& network, const string& ifname, const string& vifname, string& error_msg) { Fib2mribRoute fib2mrib_route(network, network.masked_addr().ZERO(), ifname, vifname, 0, 0, "", false); fib2mrib_route.set_delete_route(); return (delete_route(fib2mrib_route, error_msg)); } /** * Delete an IPv6 route. * * @param network the network address prefix this route applies to. * @param ifname the name of the physical interface toward the * destination. * @param vifname the name of the virtual interface toward the * destination. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int Fib2mribNode::delete_route6(const IPv6Net& network, const string& ifname, const string& vifname, string& error_msg) { Fib2mribRoute fib2mrib_route(network, network.masked_addr().ZERO(), ifname, vifname, 0, 0, "", false); fib2mrib_route.set_delete_route(); return (delete_route(fib2mrib_route, error_msg)); } /** * Add an IPvX route. * * @param fib2mrib_route the route to add. * @see Fib2mribRoute * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int Fib2mribNode::add_route(const Fib2mribRoute& fib2mrib_route, string& error_msg) { Fib2mribRoute updated_route = fib2mrib_route; // // Update the route // update_route(_iftree, updated_route); // // Check the entry // if (updated_route.is_valid_entry(error_msg) != true) { error_msg = c_format("Cannot add route for %s: %s", updated_route.network().str().c_str(), error_msg.c_str()); return XORP_ERROR; } // // Check if the route was added already // multimap::iterator iter; iter = _fib2mrib_routes.find(updated_route.network()); for ( ; iter != _fib2mrib_routes.end(); ++iter) { Fib2mribRoute& orig_route = iter->second; if (orig_route.network() != updated_route.network()) break; // // XXX: Route found. Ideally, if we receive add_route() from // the FEA, we should have received delete_route() before. // However, on FreeBSD-4.10 (at least), if a route points // to the local address of an interface, and if we delete // that address, the kernel automatically removes all affected // routes without reporting the routing information change // to the process(es) listening on routing sockets. // Therefore, to deal with such (mis)behavior of the FEA, // we just replace the previously received route with the // new one. // updated_route.set_replace_route(); return (replace_route(updated_route, error_msg)); } // // Add the route // iter = _fib2mrib_routes.insert(make_pair(updated_route.network(), updated_route)); // // Create a copy of the route and inform the RIB if necessary // Fib2mribRoute& orig_route = iter->second; Fib2mribRoute copy_route = orig_route; prepare_route_for_transmission(orig_route, copy_route); // // Inform the RIB about the change // inform_rib(copy_route); return XORP_OK; } /** * Replace an IPvX route. * * @param fib2mrib_route the replacement route. * @see Fib2mribRoute * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int Fib2mribNode::replace_route(const Fib2mribRoute& fib2mrib_route, string& error_msg) { Fib2mribRoute updated_route = fib2mrib_route; Fib2mribRoute* route_to_replace_ptr = NULL; // // Update the route // update_route(_iftree, updated_route); // // Check the entry // if (updated_route.is_valid_entry(error_msg) != true) { error_msg = c_format("Cannot replace route for %s: %s", updated_route.network().str().c_str(), error_msg.c_str()); return XORP_ERROR; } // // Find the route and replace it. // If there is a route with the same ifname and vifname, then update // its value. Otherwise, update the first route for the same subnet. // multimap::iterator iter; iter = _fib2mrib_routes.find(updated_route.network()); for ( ; iter != _fib2mrib_routes.end(); ++iter) { Fib2mribRoute& orig_route = iter->second; if (orig_route.network() != updated_route.network()) break; if ((orig_route.ifname() != updated_route.ifname()) || (orig_route.vifname() != updated_route.vifname())) { if (route_to_replace_ptr == NULL) { // First route for same subnet route_to_replace_ptr = &orig_route; } continue; } route_to_replace_ptr = &orig_route; break; } if (route_to_replace_ptr == NULL) { // // Couldn't find the route to replace // error_msg = c_format("Cannot replace route for %s: " "no such route", updated_route.network().str().c_str()); return XORP_ERROR; } // // Route found. Overwrite its value. // do { Fib2mribRoute& orig_route = *route_to_replace_ptr; bool was_accepted = orig_route.is_accepted_by_rib(); orig_route = updated_route; // // Create a copy of the route and inform the RIB if necessary // Fib2mribRoute copy_route = orig_route; prepare_route_for_transmission(orig_route, copy_route); // // XXX: If necessary, change the type of the route. // E.g., "replace route" may become "add route" or "delete route". // if (copy_route.is_accepted_by_rib()) { if (was_accepted) { copy_route.set_replace_route(); } else { copy_route.set_add_route(); } } else { if (was_accepted) { copy_route.set_delete_route(); } else { return XORP_OK; } } // // Inform the RIB about the change // inform_rib(copy_route); } while (false); return XORP_OK; } /** * Delete an IPvX route. * * @param fib2mrib_route the route to delete. * @see Fib2mribRoute * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int Fib2mribNode::delete_route(const Fib2mribRoute& fib2mrib_route, string& error_msg) { Fib2mribRoute updated_route = fib2mrib_route; multimap::iterator route_to_delete_iter = _fib2mrib_routes.end(); // // Update the route // update_route(_iftree, updated_route); // // Check the entry // if (updated_route.is_valid_entry(error_msg) != true) { error_msg = c_format("Cannot delete route for %s: %s", updated_route.network().str().c_str(), error_msg.c_str()); return XORP_ERROR; } // // Find the route and delete it. // If there is a route with the same ifname and vifname, then delete it. // Otherwise, if the route to delete is not interface-specific, // delete the first route for the same subnet. // multimap::iterator iter; iter = _fib2mrib_routes.find(updated_route.network()); for ( ; iter != _fib2mrib_routes.end(); ++iter) { Fib2mribRoute& orig_route = iter->second; if (orig_route.network() != updated_route.network()) break; if ((orig_route.ifname() != updated_route.ifname()) || (orig_route.vifname() != updated_route.vifname())) { if (route_to_delete_iter == _fib2mrib_routes.end()) { // First route for same subnet if (! updated_route.is_interface_route()) route_to_delete_iter = iter; } continue; } route_to_delete_iter = iter; break; } if (route_to_delete_iter == _fib2mrib_routes.end()) { // // Couldn't find the route to delete // error_msg = c_format("Cannot delete route for %s: " "no such route", updated_route.network().str().c_str()); return XORP_ERROR; } // // Route found. Create a copy of it and erase it. // do { Fib2mribRoute& orig_route = route_to_delete_iter->second; bool was_accepted = orig_route.is_accepted_by_rib(); Fib2mribRoute copy_route = orig_route; prepare_route_for_transmission(orig_route, copy_route); _fib2mrib_routes.erase(route_to_delete_iter); copy_route.set_delete_route(); // // If the original route wasn't transmitted, then the RIB doesn't // know about it. // if (! was_accepted) return XORP_OK; // // Inform the RIB about the change // inform_rib(copy_route); } while (false); return XORP_OK; } /** * Check whether the route entry is valid. * * @param error_msg the error message (if error). * @return true if the route entry is valid, otherwise false. */ bool Fib2mribRoute::is_valid_entry(string& error_msg) const { UNUSED(error_msg); return true; } /** * Test whether the route is accepted for transmission to the RIB. * * @return true if route is accepted for transmission to the RIB, * otherwise false. */ bool Fib2mribRoute::is_accepted_by_rib() const { return (is_accepted_by_nexthop() && (! is_filtered())); } void Fib2mribNode::configure_filter(const uint32_t& filter, const string& conf) { _policy_filters.configure(filter, conf); } void Fib2mribNode::reset_filter(const uint32_t& filter) { _policy_filters.reset(filter); } void Fib2mribNode::push_routes() { multimap::iterator iter; // XXX: not a background task for (iter = _fib2mrib_routes.begin(); iter != _fib2mrib_routes.end(); ++iter) { Fib2mribRoute& orig_route = iter->second; bool was_accepted = orig_route.is_accepted_by_rib(); // // Create a copy of the route and inform the RIB if necessary // Fib2mribRoute copy_route = orig_route; prepare_route_for_transmission(orig_route, copy_route); // // XXX: If necessary, change the type of the route. // E.g., "replace route" may become "add route" or "delete route". // if (copy_route.is_accepted_by_rib()) { if (was_accepted) { copy_route.set_replace_route(); } else { copy_route.set_add_route(); } } else { if (was_accepted) { copy_route.set_delete_route(); } else { continue; } } // // Inform the RIB about the change // inform_rib(copy_route); } } void Fib2mribNode::push_pull_rib_routes(bool is_push) { multimap::iterator iter; // XXX: not a background task for (iter = _fib2mrib_routes.begin(); iter != _fib2mrib_routes.end(); ++iter) { Fib2mribRoute& orig_route = iter->second; // // Create a copy of the route and inform the RIB if necessary // Fib2mribRoute copy_route = orig_route; prepare_route_for_transmission(orig_route, copy_route); // // XXX: Only routes that are accepted by RIB should be added or deleted // if (! copy_route.is_accepted_by_rib()) continue; if (is_push) { copy_route.set_add_route(); } else { copy_route.set_delete_route(); } // // Inform the RIB about the change // inform_rib(copy_route); } } bool Fib2mribNode::is_accepted_by_nexthop(const Fib2mribRoute& route) const { if (route.is_interface_route()) { const IfMgrIfAtom* if_atom; const IfMgrVifAtom* vif_atom; bool is_up = false; if_atom = _iftree.find_interface(route.ifname()); vif_atom = _iftree.find_vif(route.ifname(), route.vifname()); if ((if_atom != NULL) && (if_atom->enabled()) && (! if_atom->no_carrier()) && (vif_atom != NULL) && (vif_atom->enabled())) { is_up = true; } if (is_up) { return (true); } } else { string ifname, vifname; if (_iftree.is_directly_connected(route.nexthop(), ifname, vifname)) { return (true); } } return (false); } void Fib2mribNode::prepare_route_for_transmission(Fib2mribRoute& orig_route, Fib2mribRoute& copy_route) { // // We do not want to modify original route, so we may re-filter routes on // filter configuration changes. Hence, copy the route. // copy_route = orig_route; // Do policy filtering and other acceptance tests bool filtered = (! do_filtering(copy_route)); bool accepted_by_nexthop = is_accepted_by_nexthop(copy_route); copy_route.set_filtered(filtered); copy_route.set_accepted_by_nexthop(accepted_by_nexthop); // Tag the original route orig_route.set_filtered(filtered); orig_route.set_accepted_by_nexthop(accepted_by_nexthop); } void Fib2mribNode::inform_rib(const Fib2mribRoute& route) { if (! is_enabled()) return; // // Inform the RIB about the change // if (route.is_add_route() || route.is_replace_route()) { if (route.is_accepted_by_rib()) inform_rib_route_change(route); } if (route.is_delete_route()) { inform_rib_route_change(route); } } /** * Update a route received from the FEA. * * This method is needed as a work-around of FEA-related problems * with the routes the FEA sends to interested parties such as FIB2MRIB. * A route is updated with interface-related information or next-hop * address. * * This method is needed as a work-around of FEA-related problems * with the routes the FEA sends to interested parties such as FIB2MRIB. * * The routes received from the FEA for the directly-connected subnets * may not contain next-hop information and network interface information. * If the route is for a directly-connected subnet, and if it is missing * that information, then add the interface and next-hop router * information. * Furthermore, on startup, the routes received from the FEA may * contain interface-related information, but later updates of those * routes may be missing this information. This may create a mismatch, * therefore all routes are updated (if possible) to contain * interface-related information. * * @param iftree the tree with the interface state to update the route. * @param route the route to update. * @return true if the route was updated, otherwise false. */ bool Fib2mribNode::update_route(const IfMgrIfTree& iftree, Fib2mribRoute& route) { // // Test if the route needs to be updated // if (route.is_interface_route()) return (false); // // First find if the next-hop address is one of our interfaces. // string ifname, vifname; if (iftree.is_my_addr(route.nexthop(), ifname, vifname)) { route.set_ifname(ifname); route.set_vifname(vifname); return (true); } // // Find if there is a directly-connected subnet that matches the route // or the address of the next-hop router. // IfMgrIfTree::IfMap::const_iterator if_iter; for (if_iter = iftree.interfaces().begin(); if_iter != iftree.interfaces().end(); ++if_iter) { const IfMgrIfAtom& iface = if_iter->second; IfMgrIfAtom::VifMap::const_iterator vif_iter; for (vif_iter = iface.vifs().begin(); vif_iter != iface.vifs().end(); ++vif_iter) { const IfMgrVifAtom& vif = vif_iter->second; // // Check the IPv4 subnets // if (route.is_ipv4()) { IfMgrVifAtom::IPv4Map::const_iterator a4_iter; for (a4_iter = vif.ipv4addrs().begin(); a4_iter != vif.ipv4addrs().end(); ++a4_iter) { const IfMgrIPv4Atom& a4 = a4_iter->second; IPvXNet ipvxnet(a4.addr(), a4.prefix_len()); // Update a directly-connected route if (ipvxnet == route.network()) { route.set_ifname(iface.name()); route.set_vifname(vif.name()); if (route.nexthop().is_zero()) route.set_nexthop(a4.addr()); return (true); } // Update the route if a directly-connected next-hop if (ipvxnet.contains(route.nexthop()) && (! route.nexthop().is_zero())) { route.set_ifname(iface.name()); route.set_vifname(vif.name()); return (true); } } } // // Check the IPv6 subnets // if (route.is_ipv6()) { IfMgrVifAtom::IPv6Map::const_iterator a6_iter; for (a6_iter = vif.ipv6addrs().begin(); a6_iter != vif.ipv6addrs().end(); ++a6_iter) { const IfMgrIPv6Atom& a6 = a6_iter->second; IPvXNet ipvxnet(a6.addr(), a6.prefix_len()); // Update a directly-connected route if (ipvxnet == route.network()) { route.set_ifname(iface.name()); route.set_vifname(vif.name()); if (route.nexthop().is_zero()) route.set_nexthop(a6.addr()); return (true); } // Update the route if a directly-connected next-hop if (ipvxnet.contains(route.nexthop()) && (! route.nexthop().is_zero())) { route.set_ifname(iface.name()); route.set_vifname(vif.name()); return (true); } } } } } return (false); } bool Fib2mribNode::do_filtering(Fib2mribRoute& route) { try { Fib2mribVarRW varrw(route); // Import filtering bool accepted; debug_msg("[FIB2MRIB] Running filter: %s on route: %s\n", filter::filter2str(filter::IMPORT), route.network().str().c_str()); accepted = _policy_filters.run_filter(filter::IMPORT, varrw); route.set_filtered(!accepted); // Route Rejected if (!accepted) return accepted; Fib2mribVarRW varrw2(route); // Export source-match filtering debug_msg("[FIB2MRIB] Running filter: %s on route: %s\n", filter::filter2str(filter::EXPORT_SOURCEMATCH), route.network().str().c_str()); _policy_filters.run_filter(filter::EXPORT_SOURCEMATCH, varrw2); return accepted; } catch(const PolicyException& e) { XLOG_FATAL("PolicyException: %s", e.str().c_str()); // FIXME: What do we do ? XLOG_UNFINISHED(); } } xorp/fib2mrib/command_fib2mrib0000775000076400007640000000212311421137511016466 0ustar greearbgreearb#!/bin/sh # # $XORP: xorp/static_routes/command_static_routes,v 1.1 2004/03/30 08:27:43 pavlin Exp $ # # # Send commands to a running Fib2Mrib process. # MODULE_NAME="Fib2Mrib" HOSTNAME=`hostname` echo "Sending command to $MODULE_NAME on $HOSTNAME..." # Conditionally set ${srcdir} if it wasn't assigned (e.g., by `gmake check`) if [ "X${srcdir}" = "X" ] ; then srcdir=`dirname $0` ; fi # # The optional first argument should be either -4 or -6 to specify # IPv4 or IPv6 command # # The next argument should be the command to call. # The rest of the arguments should be the arguments to the command to call. # usage() { echo "Usage: $0 [-4 | -6 ] [xrl_command_arguments ... ]" } if [ $# -lt 1 ] ; then usage; exit 1 fi case "${1}" in -4) if [ $# -lt 2 ] ; then usage; exit 1 fi IP_VERSION=IPV4 shift ;; -6) if [ $# -lt 2 ] ; then usage; exit 1 fi IP_VERSION=IPV6 shift ;; *) # XXX: IP version defaults to IPv4 IP_VERSION=IPV4 ;; esac . ${srcdir}/../utils/xrl_shell_lib.sh . ${srcdir}/../fib2mrib/xrl_fib2mrib_shell_funcs.sh $* exit $? xorp/fib2mrib/xrl_fib2mrib_node.cc0000664000076400007640000017114611540225525017264 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fib2mrib_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvx.hh" #include "libxorp/status_codes.h" #include "fib2mrib_node.hh" #include "xrl_fib2mrib_node.hh" const TimeVal XrlFib2mribNode::RETRY_TIMEVAL = TimeVal(1, 0); XrlFib2mribNode::XrlFib2mribNode(EventLoop& eventloop, const string& class_name, const string& finder_hostname, uint16_t finder_port, const string& finder_target, const string& fea_target, const string& rib_target) : Fib2mribNode(eventloop), XrlStdRouter(eventloop, class_name.c_str(), finder_hostname.c_str(), finder_port), XrlFib2mribTargetBase(&xrl_router()), _eventloop(eventloop), _xrl_fea_fti_client(&xrl_router()), _xrl_fea_fib_client(&xrl_router()), _xrl_rib_client(&xrl_router()), _finder_target(finder_target), _fea_target(fea_target), _rib_target(rib_target), _ifmgr(eventloop, fea_target.c_str(), xrl_router().finder_address(), xrl_router().finder_port()), _xrl_finder_client(&xrl_router()), _is_finder_alive(false), _is_fea_alive(false), _is_fea_registered(false), _is_fea_registering(false), _is_fea_deregistering(false), #ifdef HAVE_IPV6 _is_fea_have_ipv6_tested(false), _fea_have_ipv6(false), _is_fea_fib_client6_registered(false), _is_rib_igp_table6_registered(false), #endif _is_fea_have_ipv4_tested(false), _fea_have_ipv4(false), _is_fea_fib_client4_registered(false), _is_rib_alive(false), _is_rib_registered(false), _is_rib_registering(false), _is_rib_deregistering(false), _is_rib_igp_table4_registered(false) { _ifmgr.set_observer(dynamic_cast(this)); _ifmgr.attach_hint_observer(dynamic_cast(this)); } XrlFib2mribNode::~XrlFib2mribNode() { shutdown(); _ifmgr.detach_hint_observer(dynamic_cast(this)); _ifmgr.unset_observer(dynamic_cast(this)); } int XrlFib2mribNode::startup() { return (Fib2mribNode::startup()); } int XrlFib2mribNode::shutdown() { return (Fib2mribNode::shutdown()); } // // Finder-related events // /** * Called when Finder connection is established. * * Note that this method overwrites an XrlRouter virtual method. */ void XrlFib2mribNode::finder_connect_event() { _is_finder_alive = true; } /** * Called when Finder disconnect occurs. * * Note that this method overwrites an XrlRouter virtual method. */ void XrlFib2mribNode::finder_disconnect_event() { XLOG_ERROR("Finder disconnect event. Exiting immediately..."); _is_finder_alive = false; Fib2mribNode::shutdown(); } // // Register with the FEA // void XrlFib2mribNode::fea_register_startup() { bool success; _fea_register_startup_timer.unschedule(); _fea_register_shutdown_timer.unschedule(); if (! _is_finder_alive) return; // The Finder is dead if (_is_fea_registered) return; // Already registered if (! _is_fea_registering) { Fib2mribNode::incr_startup_requests_n(); // XXX: for the ifmgr if (! _is_fea_fib_client4_registered) Fib2mribNode::incr_startup_requests_n(); #ifdef HAVE_IPV6 if (! _is_fea_fib_client6_registered) Fib2mribNode::incr_startup_requests_n(); #endif _is_fea_registering = true; } // // Register interest in the FEA with the Finder // success = _xrl_finder_client.send_register_class_event_interest( _finder_target.c_str(), xrl_router().instance_name(), _fea_target, callback(this, &XrlFib2mribNode::finder_register_interest_fea_cb)); if (! success) { // // If an error, then start a timer to try again. // _fea_register_startup_timer = _eventloop.new_oneoff_after( RETRY_TIMEVAL, callback(this, &XrlFib2mribNode::fea_register_startup)); return; } } void XrlFib2mribNode::finder_register_interest_fea_cb(const XrlError& xrl_error) { switch (xrl_error.error_code()) { case OKAY: // // If success, then the FEA birth event will startup the ifmgr // _is_fea_registering = false; _is_fea_registered = true; break; case COMMAND_FAILED: // // If a command failed because the other side rejected it, this is // fatal. // XLOG_FATAL("Cannot register interest in Finder events: %s", xrl_error.str().c_str()); break; case NO_FINDER: case RESOLVE_FAILED: case SEND_FAILED: // // A communication error that should have been caught elsewhere // (e.g., by tracking the status of the Finder and the other targets). // Probably we caught it here because of event reordering. // In some cases we print an error. In other cases our job is done. // XLOG_ERROR("XRL communication error: %s", xrl_error.str().c_str()); break; case BAD_ARGS: case NO_SUCH_METHOD: case INTERNAL_ERROR: // // An error that should happen only if there is something unusual: // e.g., there is XRL mismatch, no enough internal resources, etc. // We don't try to recover from such errors, hence this is fatal. // XLOG_FATAL("Fatal XRL error: %s", xrl_error.str().c_str()); break; case REPLY_TIMED_OUT: case SEND_FAILED_TRANSIENT: // // If a transient error, then start a timer to try again // (unless the timer is already running). // if (! _fea_register_startup_timer.scheduled()) { XLOG_ERROR("Failed to register interest in Finder events: %s. " "Will try again.", xrl_error.str().c_str()); _fea_register_startup_timer = _eventloop.new_oneoff_after( RETRY_TIMEVAL, callback(this, &XrlFib2mribNode::fea_register_startup)); } break; } } // // De-register with the FEA // void XrlFib2mribNode::fea_register_shutdown() { bool success; _fea_register_startup_timer.unschedule(); _fea_register_shutdown_timer.unschedule(); if (! _is_finder_alive) return; // The Finder is dead if (! _is_fea_alive) return; // The FEA is not there anymore if (! _is_fea_registered) return; // Not registered if (! _is_fea_deregistering) { Fib2mribNode::incr_shutdown_requests_n(); // XXX: for the ifmgr if (_is_fea_fib_client4_registered) Fib2mribNode::incr_shutdown_requests_n(); #ifdef HAVE_IPV6 if (_is_fea_fib_client6_registered) Fib2mribNode::incr_shutdown_requests_n(); #endif _is_fea_deregistering = true; } // // De-register interest in the FEA with the Finder // success = _xrl_finder_client.send_deregister_class_event_interest( _finder_target.c_str(), xrl_router().instance_name(), _fea_target, callback(this, &XrlFib2mribNode::finder_deregister_interest_fea_cb)); if (! success) { // // If an error, then start a timer to try again. // _fea_register_shutdown_timer = _eventloop.new_oneoff_after( RETRY_TIMEVAL, callback(this, &XrlFib2mribNode::fea_register_shutdown)); return; } // // XXX: when the shutdown is completed, Fib2mribNode::status_change() // will be called. // _ifmgr.shutdown(); send_fea_delete_fib_client(); } void XrlFib2mribNode::finder_deregister_interest_fea_cb(const XrlError& xrl_error) { switch (xrl_error.error_code()) { case OKAY: // // If success, then we are done // _is_fea_deregistering = false; _is_fea_registered = false; break; case COMMAND_FAILED: // // If a command failed because the other side rejected it, this is // fatal. // XLOG_FATAL("Cannot deregister interest in Finder events: %s", xrl_error.str().c_str()); break; case NO_FINDER: case RESOLVE_FAILED: case SEND_FAILED: // // A communication error that should have been caught elsewhere // (e.g., by tracking the status of the Finder and the other targets). // Probably we caught it here because of event reordering. // In some cases we print an error. In other cases our job is done. // _is_fea_deregistering = false; _is_fea_registered = false; break; case BAD_ARGS: case NO_SUCH_METHOD: case INTERNAL_ERROR: // // An error that should happen only if there is something unusual: // e.g., there is XRL mismatch, no enough internal resources, etc. // We don't try to recover from such errors, hence this is fatal. // XLOG_FATAL("Fatal XRL error: %s", xrl_error.str().c_str()); break; case REPLY_TIMED_OUT: case SEND_FAILED_TRANSIENT: // // If a transient error, then start a timer to try again // (unless the timer is already running). // if (! _fea_register_shutdown_timer.scheduled()) { XLOG_ERROR("Failed to deregister interest in Finder events: %s. " "Will try again.", xrl_error.str().c_str()); _fea_register_shutdown_timer = _eventloop.new_oneoff_after( RETRY_TIMEVAL, callback(this, &XrlFib2mribNode::fea_register_shutdown)); } break; } } // // Register with the RIB // void XrlFib2mribNode::rib_register_startup() { bool success; _rib_register_startup_timer.unschedule(); _rib_register_shutdown_timer.unschedule(); if (! _is_finder_alive) return; // The Finder is dead if (_is_rib_registered) return; // Already registered if (! _is_rib_registering) { if (! _is_rib_igp_table4_registered) Fib2mribNode::incr_startup_requests_n(); #ifdef HAVE_IPV6 if (! _is_rib_igp_table6_registered) Fib2mribNode::incr_startup_requests_n(); #endif _is_rib_registering = true; } // // Register interest in the RIB with the Finder // success = _xrl_finder_client.send_register_class_event_interest( _finder_target.c_str(), xrl_router().instance_name(), _rib_target, callback(this, &XrlFib2mribNode::finder_register_interest_rib_cb)); if (! success) { // // If an error, then start a timer to try again. // _rib_register_startup_timer = _eventloop.new_oneoff_after( RETRY_TIMEVAL, callback(this, &XrlFib2mribNode::rib_register_startup)); return; } } void XrlFib2mribNode::finder_register_interest_rib_cb(const XrlError& xrl_error) { switch (xrl_error.error_code()) { case OKAY: // // If success, then the RIB birth event will startup the RIB // registration. // _is_rib_registering = false; _is_rib_registered = true; break; case COMMAND_FAILED: // // If a command failed because the other side rejected it, this is // fatal. // XLOG_FATAL("Cannot register interest in Finder events: %s", xrl_error.str().c_str()); break; case NO_FINDER: case RESOLVE_FAILED: case SEND_FAILED: // // A communication error that should have been caught elsewhere // (e.g., by tracking the status of the Finder and the other targets). // Probably we caught it here because of event reordering. // In some cases we print an error. In other cases our job is done. // XLOG_ERROR("XRL communication error: %s", xrl_error.str().c_str()); break; case BAD_ARGS: case NO_SUCH_METHOD: case INTERNAL_ERROR: // // An error that should happen only if there is something unusual: // e.g., there is XRL mismatch, no enough internal resources, etc. // We don't try to recover from such errors, hence this is fatal. // XLOG_FATAL("Fatal XRL error: %s", xrl_error.str().c_str()); break; case REPLY_TIMED_OUT: case SEND_FAILED_TRANSIENT: // // If a transient error, then start a timer to try again // (unless the timer is already running). // if (! _rib_register_startup_timer.scheduled()) { XLOG_ERROR("Failed to register interest in Finder events: %s. " "Will try again.", xrl_error.str().c_str()); _rib_register_startup_timer = _eventloop.new_oneoff_after( RETRY_TIMEVAL, callback(this, &XrlFib2mribNode::rib_register_startup)); } break; } } // // De-register with the RIB // void XrlFib2mribNode::rib_register_shutdown() { bool success; _rib_register_startup_timer.unschedule(); _rib_register_shutdown_timer.unschedule(); if (! _is_finder_alive) return; // The Finder is dead if (! _is_rib_alive) return; // The RIB is not there anymore if (! _is_rib_registered) return; // Not registered if (! _is_rib_deregistering) { if (_is_rib_igp_table4_registered) Fib2mribNode::incr_shutdown_requests_n(); #ifdef HAVE_IPV6 if (_is_rib_igp_table6_registered) Fib2mribNode::incr_shutdown_requests_n(); #endif _is_rib_deregistering = true; } // // De-register interest in the RIB with the Finder // success = _xrl_finder_client.send_deregister_class_event_interest( _finder_target.c_str(), xrl_router().instance_name(), _rib_target, callback(this, &XrlFib2mribNode::finder_deregister_interest_rib_cb)); if (! success) { // // If an error, then start a timer to try again. // _rib_register_shutdown_timer = _eventloop.new_oneoff_after( RETRY_TIMEVAL, callback(this, &XrlFib2mribNode::rib_register_shutdown)); return; } send_rib_delete_tables(); } void XrlFib2mribNode::finder_deregister_interest_rib_cb(const XrlError& xrl_error) { switch (xrl_error.error_code()) { case OKAY: // // If success, then we are done // _is_rib_deregistering = false; _is_rib_registered = false; break; case COMMAND_FAILED: // // If a command failed because the other side rejected it, this is // fatal. // XLOG_FATAL("Cannot deregister interest in Finder events: %s", xrl_error.str().c_str()); break; case NO_FINDER: case RESOLVE_FAILED: case SEND_FAILED: // // A communication error that should have been caught elsewhere // (e.g., by tracking the status of the Finder and the other targets). // Probably we caught it here because of event reordering. // In some cases we print an error. In other cases our job is done. // _is_rib_deregistering = false; _is_rib_registered = false; break; case BAD_ARGS: case NO_SUCH_METHOD: case INTERNAL_ERROR: // // An error that should happen only if there is something unusual: // e.g., there is XRL mismatch, no enough internal resources, etc. // We don't try to recover from such errors, hence this is fatal. // XLOG_FATAL("Fatal XRL error: %s", xrl_error.str().c_str()); break; case REPLY_TIMED_OUT: case SEND_FAILED_TRANSIENT: // // If a transient error, then start a timer to try again // (unless the timer is already running). // if (! _rib_register_shutdown_timer.scheduled()) { XLOG_ERROR("Failed to deregister interest in Finder events: %s. " "Will try again.", xrl_error.str().c_str()); _rib_register_shutdown_timer = _eventloop.new_oneoff_after( RETRY_TIMEVAL, callback(this, &XrlFib2mribNode::rib_register_shutdown)); } break; } } // // Register as an FEA FIB client // void XrlFib2mribNode::send_fea_add_fib_client() { bool success = true; if (! _is_finder_alive) return; // The Finder is dead // // Test whether the underlying system supports IPv4 // if (! _is_fea_have_ipv4_tested) { success = _xrl_fea_fti_client.send_have_ipv4( _fea_target.c_str(), callback(this, &XrlFib2mribNode::fea_fti_client_send_have_ipv4_cb)); if (success) return; XLOG_ERROR("Failed to test using the FEA whether the system " "supports IPv4. " "Will try again."); goto start_timer_label; } #ifdef HAVE_IPV6 // // Test whether the underlying system supports IPv6 // if (! _is_fea_have_ipv6_tested) { success = _xrl_fea_fti_client.send_have_ipv6( _fea_target.c_str(), callback(this, &XrlFib2mribNode::fea_fti_client_send_have_ipv6_cb)); if (success) return; XLOG_ERROR("Failed to test using the FEA whether the system " "supports IPv6. " "Will try again."); goto start_timer_label; } #endif if (_fea_have_ipv4 && ! _is_fea_fib_client4_registered) { success = _xrl_fea_fib_client.send_add_fib_client4( _fea_target.c_str(), xrl_router().class_name(), true, /* send_updates */ false, /* send_resolves */ callback(this, &XrlFib2mribNode::fea_fib_client_send_add_fib_client4_cb)); if (success) return; XLOG_ERROR("Failed to register IPv4 FIB client with the FEA. " "Will try again."); goto start_timer_label; } #ifdef HAVE_IPV6 if (_fea_have_ipv6 && ! _is_fea_fib_client6_registered) { success = _xrl_fea_fib_client.send_add_fib_client6( _fea_target.c_str(), xrl_router().class_name(), true, /* send_updates */ false, /* send_resolves */ callback(this, &XrlFib2mribNode::fea_fib_client_send_add_fib_client6_cb)); if (success) return; XLOG_ERROR("Failed to register IPv6 FIB client with the FEA. " "Will try again."); goto start_timer_label; } #endif if (! success) { // // If an error, then start a timer to try again. // start_timer_label: _fea_fib_client_registration_timer = _eventloop.new_oneoff_after( RETRY_TIMEVAL, callback(this, &XrlFib2mribNode::send_fea_add_fib_client)); } } void XrlFib2mribNode::fea_fti_client_send_have_ipv4_cb(const XrlError& xrl_error, const bool* result) { switch (xrl_error.error_code()) { case OKAY: // // If success, then we are done // _is_fea_have_ipv4_tested = true; _fea_have_ipv4 = *result; send_fea_add_fib_client(); // XXX: if the underying system doesn't support IPv4, then we are done if (! _fea_have_ipv4) Fib2mribNode::decr_startup_requests_n(); break; case COMMAND_FAILED: // // If a command failed because the other side rejected it, this is // fatal. // XLOG_FATAL("Cannot test using the FEA whether the system " "supports IPv4: %s", xrl_error.str().c_str()); break; case NO_FINDER: case RESOLVE_FAILED: case SEND_FAILED: // // A communication error that should have been caught elsewhere // (e.g., by tracking the status of the Finder and the other targets). // Probably we caught it here because of event reordering. // In some cases we print an error. In other cases our job is done. // XLOG_ERROR("XRL communication error: %s", xrl_error.str().c_str()); break; case BAD_ARGS: case NO_SUCH_METHOD: case INTERNAL_ERROR: // // An error that should happen only if there is something unusual: // e.g., there is XRL mismatch, no enough internal resources, etc. // We don't try to recover from such errors, hence this is fatal. // XLOG_FATAL("Fatal XRL error: %s", xrl_error.str().c_str()); break; case REPLY_TIMED_OUT: case SEND_FAILED_TRANSIENT: // // If a transient error, then start a timer to try again // (unless the timer is already running). // if (! _fea_fib_client_registration_timer.scheduled()) { XLOG_ERROR("Failed to test using the FEA whether the system " "supports IPv4: %s. " "Will try again.", xrl_error.str().c_str()); _fea_fib_client_registration_timer = _eventloop.new_oneoff_after( RETRY_TIMEVAL, callback(this, &XrlFib2mribNode::send_fea_add_fib_client)); } break; } } void XrlFib2mribNode::fea_fib_client_send_add_fib_client4_cb( const XrlError& xrl_error) { switch (xrl_error.error_code()) { case OKAY: // // If success, then we are done // _is_fea_fib_client4_registered = true; send_fea_add_fib_client(); Fib2mribNode::decr_startup_requests_n(); break; case COMMAND_FAILED: // // If a command failed because the other side rejected it, this is // fatal. // XLOG_FATAL("Cannot add IPv4 FIB client to the FEA: %s", xrl_error.str().c_str()); break; case NO_FINDER: case RESOLVE_FAILED: case SEND_FAILED: // // A communication error that should have been caught elsewhere // (e.g., by tracking the status of the Finder and the other targets). // Probably we caught it here because of event reordering. // In some cases we print an error. In other cases our job is done. // XLOG_ERROR("XRL communication error: %s", xrl_error.str().c_str()); break; case BAD_ARGS: case NO_SUCH_METHOD: case INTERNAL_ERROR: // // An error that should happen only if there is something unusual: // e.g., there is XRL mismatch, no enough internal resources, etc. // We don't try to recover from such errors, hence this is fatal. // XLOG_FATAL("Fatal XRL error: %s", xrl_error.str().c_str()); break; case REPLY_TIMED_OUT: case SEND_FAILED_TRANSIENT: // // If a transient error, then start a timer to try again // (unless the timer is already running). // if (! _fea_fib_client_registration_timer.scheduled()) { XLOG_ERROR("Failed to add IPv4 FIB client to the FEA: %s. " "Will try again.", xrl_error.str().c_str()); _fea_fib_client_registration_timer = _eventloop.new_oneoff_after( RETRY_TIMEVAL, callback(this, &XrlFib2mribNode::send_fea_add_fib_client)); } break; } } // // De-register as an FEA FIB client // void XrlFib2mribNode::send_fea_delete_fib_client() { bool success = true; if (! _is_finder_alive) return; // The Finder is dead if (_is_fea_fib_client4_registered) { bool success4; success4 = _xrl_fea_fib_client.send_delete_fib_client4( _fea_target.c_str(), xrl_router().class_name(), callback(this, &XrlFib2mribNode::fea_fib_client_send_delete_fib_client4_cb)); if (success4 != true) { XLOG_ERROR("Failed to deregister IPv4 FIB client with the FEA. " "Will give up."); success = false; } } #ifdef HAVE_IPV6 if (_is_fea_fib_client6_registered) { bool success6; success6 = _xrl_fea_fib_client.send_delete_fib_client6( _fea_target.c_str(), xrl_router().class_name(), callback(this, &XrlFib2mribNode::fea_fib_client_send_delete_fib_client6_cb)); if (success6 != true) { XLOG_ERROR("Failed to deregister IPv6 FIB client with the FEA. " "Will give up."); success = false; } } #endif if (! success) { Fib2mribNode::set_status(SERVICE_FAILED); Fib2mribNode::update_status(); } } void XrlFib2mribNode::fea_fib_client_send_delete_fib_client4_cb( const XrlError& xrl_error) { switch (xrl_error.error_code()) { case OKAY: // // If success, then we are done // _is_fea_have_ipv4_tested = false; _fea_have_ipv4 = false; _is_fea_fib_client4_registered = false; Fib2mribNode::decr_shutdown_requests_n(); break; case COMMAND_FAILED: // // If a command failed because the other side rejected it, this is // fatal. // XLOG_FATAL("Failed to deregister IPv4 FIB client with the FEA: %s", xrl_error.str().c_str()); break; case NO_FINDER: case RESOLVE_FAILED: case SEND_FAILED: // // A communication error that should have been caught elsewhere // (e.g., by tracking the status of the Finder and the other targets). // Probably we caught it here because of event reordering. // In some cases we print an error. In other cases our job is done. // _is_fea_have_ipv4_tested = false; _fea_have_ipv4 = false; _is_fea_fib_client4_registered = false; Fib2mribNode::decr_shutdown_requests_n(); break; case BAD_ARGS: case NO_SUCH_METHOD: case INTERNAL_ERROR: // // An error that should happen only if there is something unusual: // e.g., there is XRL mismatch, no enough internal resources, etc. // We don't try to recover from such errors, hence this is fatal. // XLOG_FATAL("Fatal XRL error: %s", xrl_error.str().c_str()); break; case REPLY_TIMED_OUT: case SEND_FAILED_TRANSIENT: // // If a transient error, then start a timer to try again // (unless the timer is already running). // if (! _rib_register_shutdown_timer.scheduled()) { XLOG_ERROR("Cannot deregister IPv4 FIB client with the FEA: %s. " "Will try again.", xrl_error.str().c_str()); _rib_register_shutdown_timer = _eventloop.new_oneoff_after( RETRY_TIMEVAL, callback(this, &XrlFib2mribNode::rib_register_shutdown)); } break; } } // // Add tables with the RIB // void XrlFib2mribNode::send_rib_add_tables() { bool success = true; if (! _is_finder_alive) return; // The Finder is dead if (! _is_rib_igp_table4_registered) { success = _xrl_rib_client.send_add_igp_table4( _rib_target.c_str(), Fib2mribNode::protocol_name(), xrl_router().class_name(), xrl_router().instance_name(), false, /* unicast */ true, /* multicast */ callback(this, &XrlFib2mribNode::rib_client_send_add_igp_table4_cb)); if (success) return; XLOG_ERROR("Failed to register IPv4 IGP table with the RIB. " "Will try again."); goto start_timer_label; } #ifdef HAVE_IPV6 if (! _is_rib_igp_table6_registered) { success = _xrl_rib_client.send_add_igp_table6( _rib_target.c_str(), Fib2mribNode::protocol_name(), xrl_router().class_name(), xrl_router().instance_name(), false, /* unicast */ true, /* multicast */ callback(this, &XrlFib2mribNode::rib_client_send_add_igp_table6_cb)); if (success) return; XLOG_ERROR("Failed to register IPv6 IGP table with the RIB. " "Will try again."); goto start_timer_label; } #endif if (! success) { // // If an error, then start a timer to try again. // start_timer_label: _rib_igp_table_registration_timer = _eventloop.new_oneoff_after( RETRY_TIMEVAL, callback(this, &XrlFib2mribNode::send_rib_add_tables)); } } void XrlFib2mribNode::rib_client_send_add_igp_table4_cb(const XrlError& xrl_error) { switch (xrl_error.error_code()) { case OKAY: // // If success, then we are done // _is_rib_igp_table4_registered = true; send_rib_add_tables(); Fib2mribNode::decr_startup_requests_n(); break; case COMMAND_FAILED: // // If a command failed because the other side rejected it, this is // fatal. // XLOG_FATAL("Cannot add IPv4 IGP table to the RIB: %s", xrl_error.str().c_str()); break; case NO_FINDER: case RESOLVE_FAILED: case SEND_FAILED: // // A communication error that should have been caught elsewhere // (e.g., by tracking the status of the Finder and the other targets). // Probably we caught it here because of event reordering. // In some cases we print an error. In other cases our job is done. // XLOG_ERROR("XRL communication error: %s", xrl_error.str().c_str()); break; case BAD_ARGS: case NO_SUCH_METHOD: case INTERNAL_ERROR: // // An error that should happen only if there is something unusual: // e.g., there is XRL mismatch, no enough internal resources, etc. // We don't try to recover from such errors, hence this is fatal. // XLOG_FATAL("Fatal XRL error: %s", xrl_error.str().c_str()); break; case REPLY_TIMED_OUT: case SEND_FAILED_TRANSIENT: // // If a transient error, then start a timer to try again // (unless the timer is already running). // if (! _rib_igp_table_registration_timer.scheduled()) { XLOG_ERROR("Failed to add IPv4 IGP table to the RIB: %s. " "Will try again.", xrl_error.str().c_str()); _rib_igp_table_registration_timer = _eventloop.new_oneoff_after( RETRY_TIMEVAL, callback(this, &XrlFib2mribNode::send_rib_add_tables)); } break; } } // // Delete tables with the RIB // void XrlFib2mribNode::send_rib_delete_tables() { bool success = true; if (! _is_finder_alive) return; // The Finder is dead if (_is_rib_igp_table4_registered) { bool success4; success4 = _xrl_rib_client.send_delete_igp_table4( _rib_target.c_str(), Fib2mribNode::protocol_name(), xrl_router().class_name(), xrl_router().instance_name(), false, /* unicast */ true, /* multicast */ callback(this, &XrlFib2mribNode::rib_client_send_delete_igp_table4_cb)); if (success4 != true) { XLOG_ERROR("Failed to deregister IPv4 IGP table with the RIB. " "Will give up."); success = false; } } #ifdef HAVE_IPV6 if (_is_rib_igp_table6_registered) { bool success6; success6 = _xrl_rib_client.send_delete_igp_table6( _rib_target.c_str(), Fib2mribNode::protocol_name(), xrl_router().class_name(), xrl_router().instance_name(), false, /* unicast */ true, /* multicast */ callback(this, &XrlFib2mribNode::rib_client_send_delete_igp_table6_cb)); if (success6 != true) { XLOG_ERROR("Failed to deregister IPv6 IGP table with the RIB. " "Will give up."); success = false; } } #endif if (! success) { Fib2mribNode::set_status(SERVICE_FAILED); Fib2mribNode::update_status(); } } void XrlFib2mribNode::rib_client_send_delete_igp_table4_cb( const XrlError& xrl_error) { switch (xrl_error.error_code()) { case OKAY: // // If success, then we are done // _is_rib_igp_table4_registered = false; Fib2mribNode::decr_shutdown_requests_n(); break; case COMMAND_FAILED: // // If a command failed because the other side rejected it, this is // fatal. // XLOG_FATAL("Cannot deregister IPv4 IGP table with the RIB: %s", xrl_error.str().c_str()); break; case NO_FINDER: case RESOLVE_FAILED: case SEND_FAILED: // // A communication error that should have been caught elsewhere // (e.g., by tracking the status of the Finder and the other targets). // Probably we caught it here because of event reordering. // In some cases we print an error. In other cases our job is done. // _is_rib_igp_table4_registered = false; Fib2mribNode::decr_shutdown_requests_n(); break; case BAD_ARGS: case NO_SUCH_METHOD: case INTERNAL_ERROR: // // An error that should happen only if there is something unusual: // e.g., there is XRL mismatch, no enough internal resources, etc. // We don't try to recover from such errors, hence this is fatal. // XLOG_FATAL("Fatal XRL error: %s", xrl_error.str().c_str()); break; case REPLY_TIMED_OUT: case SEND_FAILED_TRANSIENT: // // If a transient error, then start a timer to try again // (unless the timer is already running). // if (! _rib_register_shutdown_timer.scheduled()) { XLOG_ERROR("Failed to deregister IPv4 IGP table with the RIB: %s. " "Will try again.", xrl_error.str().c_str()); _rib_register_shutdown_timer = _eventloop.new_oneoff_after( RETRY_TIMEVAL, callback(this, &XrlFib2mribNode::rib_register_shutdown)); } break; } } // // XRL target methods // /** * Get name of Xrl Target */ XrlCmdError XrlFib2mribNode::common_0_1_get_target_name( // Output values, string& name) { name = xrl_router().class_name(); return XrlCmdError::OKAY(); } /** * Get version string from Xrl Target */ XrlCmdError XrlFib2mribNode::common_0_1_get_version( // Output values, string& version) { version = XORP_MODULE_VERSION; return XrlCmdError::OKAY(); } /** * Get status of Xrl Target */ XrlCmdError XrlFib2mribNode::common_0_1_get_status( // Output values, uint32_t& status, string& reason) { status = Fib2mribNode::node_status(reason); return XrlCmdError::OKAY(); } /** * Request clean shutdown of Xrl Target */ XrlCmdError XrlFib2mribNode::common_0_1_shutdown() { string error_msg; if (shutdown() != XORP_OK) { error_msg = c_format("Failed to shutdown Fib2mrib"); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFib2mribNode::common_0_1_startup() { if (startup() != XORP_OK) { string error_msg = c_format("Failed to startup Fib2mrib"); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } /** * Announce target birth to observer. * * @param target_class the target class name. * * @param target_instance the target instance name. */ XrlCmdError XrlFib2mribNode::finder_event_observer_0_1_xrl_target_birth( // Input values, const string& target_class, const string& target_instance) { if (target_class == _fea_target) { // // XXX: when the startup is completed, // IfMgrHintObserver::tree_complete() will be called. // _is_fea_alive = true; if (_ifmgr.startup() != XORP_OK) { Fib2mribNode::ServiceBase::set_status(SERVICE_FAILED); Fib2mribNode::update_status(); } else { send_fea_add_fib_client(); } } if (target_class == _rib_target) { _is_rib_alive = true; send_rib_add_tables(); } return XrlCmdError::OKAY(); UNUSED(target_instance); } /** * Announce target death to observer. * * @param target_class the target class name. * * @param target_instance the target instance name. */ XrlCmdError XrlFib2mribNode::finder_event_observer_0_1_xrl_target_death( // Input values, const string& target_class, const string& target_instance) { bool do_shutdown = false; UNUSED(target_instance); if (target_class == _fea_target) { XLOG_ERROR("FEA (instance %s) has died, shutting down.", target_instance.c_str()); _is_fea_alive = false; do_shutdown = true; } if (target_class == _rib_target) { XLOG_ERROR("RIB (instance %s) has died, shutting down.", target_instance.c_str()); _is_rib_alive = false; do_shutdown = true; } if (do_shutdown) Fib2mribNode::shutdown(); return XrlCmdError::OKAY(); } /** * Add a route. * * @param network the network address prefix of the route to add. * * @param nexthop the address of the next-hop router toward the * destination. * * @param ifname the name of the physical interface toward the * destination. * * @param vifname the name of the virtual interface toward the * destination. * * @param metric the routing metric toward the destination. * * @param admin_distance the administratively defined distance toward the * destination. * * @param protocol_origin the name of the protocol that originated this * route. * * @param xorp_route true if this route was installed by XORP. */ XrlCmdError XrlFib2mribNode::fea_fib_client_0_1_add_route4( // Input values, const IPv4Net& network, const IPv4& nexthop, const string& ifname, const string& vifname, const uint32_t& metric, const uint32_t& admin_distance, const string& protocol_origin, const bool& xorp_route) { string error_msg; debug_msg("fea_fib_client_0_1_add_route4(): " "network = %s nexthop = %s ifname = %s vifname = %s " "metric = %u admin_distance = %u protocol_origin = %s " "xorp_route = %s\n", network.str().c_str(), nexthop.str().c_str(), ifname.c_str(), vifname.c_str(), XORP_UINT_CAST(metric), XORP_UINT_CAST(admin_distance), protocol_origin.c_str(), bool_c_str(xorp_route)); if (Fib2mribNode::add_route4(network, nexthop, ifname, vifname, metric, admin_distance, protocol_origin, xorp_route, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } /** * Replace a route. * * @param network the network address prefix of the route to replace. * * @param nexthop the address of the next-hop router toward the * destination. * * @param ifname the name of the physical interface toward the * destination. * * @param vifname the name of the virtual interface toward the * destination. * * @param metric the routing metric toward the destination. * * @param admin_distance the administratively defined distance toward the * destination. * * @param protocol_origin the name of the protocol that originated this * route. * * @param xorp_route true if this route was installed by XORP. */ XrlCmdError XrlFib2mribNode::fea_fib_client_0_1_replace_route4( // Input values, const IPv4Net& network, const IPv4& nexthop, const string& ifname, const string& vifname, const uint32_t& metric, const uint32_t& admin_distance, const string& protocol_origin, const bool& xorp_route) { string error_msg; debug_msg("fea_fib_client_0_1_replace_route4(): " "network = %s nexthop = %s ifname = %s vifname = %s " "metric = %u admin_distance = %u protocol_origin = %s " "xorp_route = %s\n", network.str().c_str(), nexthop.str().c_str(), ifname.c_str(), vifname.c_str(), XORP_UINT_CAST(metric), XORP_UINT_CAST(admin_distance), protocol_origin.c_str(), bool_c_str(xorp_route)); if (Fib2mribNode::replace_route4(network, nexthop, ifname, vifname, metric, admin_distance, protocol_origin, xorp_route, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } /** * Delete a route. * * @param network the network address prefix of the route to delete. * * @param ifname the name of the physical interface toward the * destination. * * @param vifname the name of the virtual interface toward the */ XrlCmdError XrlFib2mribNode::fea_fib_client_0_1_delete_route4( // Input values, const IPv4Net& network, const string& ifname, const string& vifname) { string error_msg; debug_msg("fea_fib_client_0_1_delete_route4(): " "network = %s ifname = %s vifname = %s\n", network.str().c_str(), ifname.c_str(), vifname.c_str()); if (Fib2mribNode::delete_route4(network, ifname, vifname, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } /** * Route resolve notification. * * @param network the network address prefix of the lookup * which failed or for which upper layer intervention is * requested from the FIB. */ XrlCmdError XrlFib2mribNode::fea_fib_client_0_1_resolve_route4( // Input values, const IPv4Net& network) { UNUSED(network); return XrlCmdError::OKAY(); } /** * Enable/disable/start/stop Fib2mrib. * * @param enable if true, then enable Fib2mrib, otherwise disable it. */ XrlCmdError XrlFib2mribNode::fib2mrib_0_1_enable_fib2mrib( // Input values, const bool& enable) { Fib2mribNode::set_enabled(enable); return XrlCmdError::OKAY(); } XrlCmdError XrlFib2mribNode::fib2mrib_0_1_start_fib2mrib() { // XLOG_UNFINISHED(); return XrlCmdError::OKAY(); } XrlCmdError XrlFib2mribNode::fib2mrib_0_1_stop_fib2mrib() { XLOG_UNFINISHED(); return XrlCmdError::OKAY(); } /** * Enable/disable the Fib2mrib trace log for all operations. * * @param enable if true, then enable the trace log, otherwise disable it. */ XrlCmdError XrlFib2mribNode::fib2mrib_0_1_enable_log_trace_all( // Input values, const bool& enable) { Fib2mribNode::set_log_trace(enable); return XrlCmdError::OKAY(); } /** * Inform the RIB about a route change. * * @param fib2mrib_route the route with the information about the change. */ void XrlFib2mribNode::inform_rib_route_change(const Fib2mribRoute& fib2mrib_route) { // Add the request to the queue _inform_rib_queue.push_back(fib2mrib_route); // If the queue was empty before, start sending the routes if (_inform_rib_queue.size() == 1) { send_rib_route_change(); } } /** * Cancel a pending request to inform the RIB about a route change. * * @param fib2mrib_route the route with the request that would be canceled. */ void XrlFib2mribNode::cancel_rib_route_change(const Fib2mribRoute& fib2mrib_route) { list::iterator iter; for (iter = _inform_rib_queue.begin(); iter != _inform_rib_queue.end(); ++iter) { Fib2mribRoute& tmp_fib2mrib_route = *iter; if (tmp_fib2mrib_route == fib2mrib_route) tmp_fib2mrib_route.set_ignored(true); } } void XrlFib2mribNode::send_rib_route_change() { bool success = true; if (! _is_finder_alive) return; // The Finder is dead do { // Pop-up all routes that are to be ignored if (_inform_rib_queue.empty()) return; // No more route changes to send Fib2mribRoute& tmp_fib2mrib_route = _inform_rib_queue.front(); if (tmp_fib2mrib_route.is_ignored()) { _inform_rib_queue.pop_front(); continue; } break; } while (true); Fib2mribRoute& fib2mrib_route = _inform_rib_queue.front(); // // Check whether we have already registered with the RIB // if (fib2mrib_route.is_ipv4() && (! _is_rib_igp_table4_registered)) { success = false; goto start_timer_label; } #ifdef HAVE_IPV6 if (fib2mrib_route.is_ipv6() && (! _is_rib_igp_table6_registered)) { success = false; goto start_timer_label; } #endif // // Send the appropriate XRL // if (fib2mrib_route.is_add_route()) { if (fib2mrib_route.is_ipv4()) { if (fib2mrib_route.is_interface_route()) { success = _xrl_rib_client.send_add_interface_route4( _rib_target.c_str(), Fib2mribNode::protocol_name(), false, /* unicast */ true, /* multicast */ fib2mrib_route.network().get_ipv4net(), fib2mrib_route.nexthop().get_ipv4(), fib2mrib_route.ifname(), fib2mrib_route.vifname(), fib2mrib_route.metric(), fib2mrib_route.policytags().xrl_atomlist(), callback(this, &XrlFib2mribNode::send_rib_route_change_cb)); if (success) return; } else { success = _xrl_rib_client.send_add_route4( _rib_target.c_str(), Fib2mribNode::protocol_name(), false, /* unicast */ true, /* multicast */ fib2mrib_route.network().get_ipv4net(), fib2mrib_route.nexthop().get_ipv4(), fib2mrib_route.metric(), fib2mrib_route.policytags().xrl_atomlist(), callback(this, &XrlFib2mribNode::send_rib_route_change_cb)); if (success) return; } } #ifdef HAVE_IPV6 if (fib2mrib_route.is_ipv6()) { if (fib2mrib_route.is_interface_route()) { success = _xrl_rib_client.send_add_interface_route6( _rib_target.c_str(), Fib2mribNode::protocol_name(), false, /* unicast */ true, /* multicast */ fib2mrib_route.network().get_ipv6net(), fib2mrib_route.nexthop().get_ipv6(), fib2mrib_route.ifname(), fib2mrib_route.vifname(), fib2mrib_route.metric(), fib2mrib_route.policytags().xrl_atomlist(), callback(this, &XrlFib2mribNode::send_rib_route_change_cb)); if (success) return; } else { success = _xrl_rib_client.send_add_route6( _rib_target.c_str(), Fib2mribNode::protocol_name(), false, /* unicast */ true, /* multicast */ fib2mrib_route.network().get_ipv6net(), fib2mrib_route.nexthop().get_ipv6(), fib2mrib_route.metric(), fib2mrib_route.policytags().xrl_atomlist(), callback(this, &XrlFib2mribNode::send_rib_route_change_cb)); if (success) return; } } #endif } if (fib2mrib_route.is_replace_route()) { if (fib2mrib_route.is_ipv4()) { if (fib2mrib_route.is_interface_route()) { success = _xrl_rib_client.send_replace_interface_route4( _rib_target.c_str(), Fib2mribNode::protocol_name(), false, /* unicast */ true, /* multicast */ fib2mrib_route.network().get_ipv4net(), fib2mrib_route.nexthop().get_ipv4(), fib2mrib_route.ifname(), fib2mrib_route.vifname(), fib2mrib_route.metric(), fib2mrib_route.policytags().xrl_atomlist(), callback(this, &XrlFib2mribNode::send_rib_route_change_cb)); if (success) return; } else { success = _xrl_rib_client.send_replace_route4( _rib_target.c_str(), Fib2mribNode::protocol_name(), false, /* unicast */ true, /* multicast */ fib2mrib_route.network().get_ipv4net(), fib2mrib_route.nexthop().get_ipv4(), fib2mrib_route.metric(), fib2mrib_route.policytags().xrl_atomlist(), callback(this, &XrlFib2mribNode::send_rib_route_change_cb)); if (success) return; } } #ifdef HAVE_IPV6 if (fib2mrib_route.is_ipv6()) { if (fib2mrib_route.is_interface_route()) { success = _xrl_rib_client.send_replace_interface_route6( _rib_target.c_str(), Fib2mribNode::protocol_name(), false, /* unicast */ true, /* multicast */ fib2mrib_route.network().get_ipv6net(), fib2mrib_route.nexthop().get_ipv6(), fib2mrib_route.ifname(), fib2mrib_route.vifname(), fib2mrib_route.metric(), fib2mrib_route.policytags().xrl_atomlist(), callback(this, &XrlFib2mribNode::send_rib_route_change_cb)); if (success) return; } else { success = _xrl_rib_client.send_replace_route6( _rib_target.c_str(), Fib2mribNode::protocol_name(), false, /* unicast */ true, /* multicast */ fib2mrib_route.network().get_ipv6net(), fib2mrib_route.nexthop().get_ipv6(), fib2mrib_route.metric(), fib2mrib_route.policytags().xrl_atomlist(), callback(this, &XrlFib2mribNode::send_rib_route_change_cb)); if (success) return; } } #endif } if (fib2mrib_route.is_delete_route()) { if (fib2mrib_route.is_ipv4()) { success = _xrl_rib_client.send_delete_route4( _rib_target.c_str(), Fib2mribNode::protocol_name(), false, /* unicast */ true, /* multicast */ fib2mrib_route.network().get_ipv4net(), callback(this, &XrlFib2mribNode::send_rib_route_change_cb)); if (success) return; } #ifdef HAVE_IPV6 if (fib2mrib_route.is_ipv6()) { success = _xrl_rib_client.send_delete_route6( _rib_target.c_str(), Fib2mribNode::protocol_name(), false, /* unicast */ true, /* multicast */ fib2mrib_route.network().get_ipv6net(), callback(this, &XrlFib2mribNode::send_rib_route_change_cb)); if (success) return; } #endif } if (! success) { // // If an error, then start a timer to try again. // XLOG_ERROR("Failed to %s route for %s with the RIB. " "Will try again.", (fib2mrib_route.is_add_route())? "add" : (fib2mrib_route.is_replace_route())? "replace" : "delete", fib2mrib_route.network().str().c_str()); start_timer_label: _inform_rib_queue_timer = _eventloop.new_oneoff_after( RETRY_TIMEVAL, callback(this, &XrlFib2mribNode::send_rib_route_change)); } } void XrlFib2mribNode::send_rib_route_change_cb(const XrlError& xrl_error) { switch (xrl_error.error_code()) { case OKAY: // // If success, then send the next route change // _inform_rib_queue.pop_front(); send_rib_route_change(); break; case COMMAND_FAILED: // // If a command failed because the other side rejected it, // then print an error and send the next one. // XLOG_ERROR("Cannot %s a routing entry with the RIB: %s", (_inform_rib_queue.front().is_add_route())? "add" : (_inform_rib_queue.front().is_replace_route())? "replace" : "delete", xrl_error.str().c_str()); _inform_rib_queue.pop_front(); send_rib_route_change(); break; case NO_FINDER: case RESOLVE_FAILED: case SEND_FAILED: // // A communication error that should have been caught elsewhere // (e.g., by tracking the status of the Finder and the other targets). // Probably we caught it here because of event reordering. // In some cases we print an error. In other cases our job is done. // XLOG_ERROR("Cannot %s a routing entry with the RIB: %s", (_inform_rib_queue.front().is_add_route())? "add" : (_inform_rib_queue.front().is_replace_route())? "replace" : "delete", xrl_error.str().c_str()); _inform_rib_queue.pop_front(); send_rib_route_change(); break; case BAD_ARGS: case NO_SUCH_METHOD: case INTERNAL_ERROR: // // An error that should happen only if there is something unusual: // e.g., there is XRL mismatch, no enough internal resources, etc. // We don't try to recover from such errors, hence this is fatal. // XLOG_FATAL("Fatal XRL error: %s", xrl_error.str().c_str()); break; case REPLY_TIMED_OUT: case SEND_FAILED_TRANSIENT: // // If a transient error, then start a timer to try again // (unless the timer is already running). // if (! _inform_rib_queue_timer.scheduled()) { XLOG_ERROR("Failed to %s a routing entry with the RIB: %s. " "Will try again.", (_inform_rib_queue.front().is_add_route())? "add" : (_inform_rib_queue.front().is_replace_route())? "replace" : "delete", xrl_error.str().c_str()); _inform_rib_queue_timer = _eventloop.new_oneoff_after( RETRY_TIMEVAL, callback(this, &XrlFib2mribNode::send_rib_route_change)); } break; } } XrlCmdError XrlFib2mribNode::policy_backend_0_1_configure(const uint32_t& filter, const string& conf) { try { Fib2mribNode::configure_filter(filter, conf); } catch(const PolicyException& e) { return XrlCmdError::COMMAND_FAILED("Filter configure failed: " + e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlFib2mribNode::policy_backend_0_1_reset(const uint32_t& filter) { try { Fib2mribNode::reset_filter(filter); } catch(const PolicyException& e) { // Will never happen... but for the future... return XrlCmdError::COMMAND_FAILED("Filter reset failed: " + e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlFib2mribNode::policy_backend_0_1_push_routes() { Fib2mribNode::push_routes(); return XrlCmdError::OKAY(); } /** IPv6 stuff */ #ifdef HAVE_IPV6 XrlCmdError XrlFib2mribNode::fea_fib_client_0_1_add_route6( // Input values, const IPv6Net& network, const IPv6& nexthop, const string& ifname, const string& vifname, const uint32_t& metric, const uint32_t& admin_distance, const string& protocol_origin, const bool& xorp_route) { string error_msg; debug_msg("fea_fib_client_0_1_add_route6(): " "network = %s nexthop = %s ifname = %s vifname = %s " "metric = %u admin_distance = %u protocol_origin = %s " "xorp_route = %s\n", network.str().c_str(), nexthop.str().c_str(), ifname.c_str(), vifname.c_str(), XORP_UINT_CAST(metric), XORP_UINT_CAST(admin_distance), protocol_origin.c_str(), bool_c_str(xorp_route)); if (Fib2mribNode::add_route6(network, nexthop, ifname, vifname, metric, admin_distance, protocol_origin, xorp_route, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFib2mribNode::fea_fib_client_0_1_replace_route6( // Input values, const IPv6Net& network, const IPv6& nexthop, const string& ifname, const string& vifname, const uint32_t& metric, const uint32_t& admin_distance, const string& protocol_origin, const bool& xorp_route) { string error_msg; debug_msg("fea_fib_client_0_1_replace_route6(): " "network = %s nexthop = %s ifname = %s vifname = %s " "metric = %u admin_distance = %u protocol_origin = %s " "xorp_route = %s\n", network.str().c_str(), nexthop.str().c_str(), ifname.c_str(), vifname.c_str(), XORP_UINT_CAST(metric), XORP_UINT_CAST(admin_distance), protocol_origin.c_str(), bool_c_str(xorp_route)); if (Fib2mribNode::replace_route6(network, nexthop, ifname, vifname, metric, admin_distance, protocol_origin, xorp_route, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFib2mribNode::fea_fib_client_0_1_delete_route6( // Input values, const IPv6Net& network, const string& ifname, const string& vifname) { string error_msg; debug_msg("fea_fib_client_0_1_delete_route6(): " "network = %s ifname = %s vifname = %s\n", network.str().c_str(), ifname.c_str(), vifname.c_str()); if (Fib2mribNode::delete_route6(network, ifname, vifname, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFib2mribNode::fea_fib_client_0_1_resolve_route6( // Input values, const IPv6Net& network) { UNUSED(network); return XrlCmdError::OKAY(); } void XrlFib2mribNode::fea_fti_client_send_have_ipv6_cb(const XrlError& xrl_error, const bool* result) { switch (xrl_error.error_code()) { case OKAY: // // If success, then we are done // _is_fea_have_ipv6_tested = true; _fea_have_ipv6 = *result; send_fea_add_fib_client(); // XXX: if the underying system doesn't support IPv6, then we are done if (! _fea_have_ipv6) Fib2mribNode::decr_startup_requests_n(); break; case COMMAND_FAILED: // // If a command failed because the other side rejected it, this is // fatal. // XLOG_FATAL("Cannot test using the FEA whether the system " "supports IPv6: %s", xrl_error.str().c_str()); break; case NO_FINDER: case RESOLVE_FAILED: case SEND_FAILED: // // A communication error that should have been caught elsewhere // (e.g., by tracking the status of the Finder and the other targets). // Probably we caught it here because of event reordering. // In some cases we print an error. In other cases our job is done. // XLOG_ERROR("XRL communication error: %s", xrl_error.str().c_str()); break; case BAD_ARGS: case NO_SUCH_METHOD: case INTERNAL_ERROR: // // An error that should happen only if there is something unusual: // e.g., there is XRL mismatch, no enough internal resources, etc. // We don't try to recover from such errors, hence this is fatal. // XLOG_FATAL("Fatal XRL error: %s", xrl_error.str().c_str()); break; case REPLY_TIMED_OUT: case SEND_FAILED_TRANSIENT: // // If a transient error, then start a timer to try again // (unless the timer is already running). // if (! _fea_fib_client_registration_timer.scheduled()) { XLOG_ERROR("Failed to test using the FEA whether the system " "supports IPv6: %s. " "Will try again.", xrl_error.str().c_str()); _fea_fib_client_registration_timer = _eventloop.new_oneoff_after( RETRY_TIMEVAL, callback(this, &XrlFib2mribNode::send_fea_add_fib_client)); } break; } } void XrlFib2mribNode::fea_fib_client_send_add_fib_client6_cb( const XrlError& xrl_error) { switch (xrl_error.error_code()) { case OKAY: // // If success, then we are done // _is_fea_fib_client6_registered = true; send_fea_add_fib_client(); Fib2mribNode::decr_startup_requests_n(); break; case COMMAND_FAILED: // // If a command failed because the other side rejected it, this is // fatal. // XLOG_FATAL("Cannot add IPv6 FIB client to the FEA: %s", xrl_error.str().c_str()); break; case NO_FINDER: case RESOLVE_FAILED: case SEND_FAILED: // // A communication error that should have been caught elsewhere // (e.g., by tracking the status of the Finder and the other targets). // Probably we caught it here because of event reordering. // In some cases we print an error. In other cases our job is done. // XLOG_ERROR("XRL communication error: %s", xrl_error.str().c_str()); break; case BAD_ARGS: case NO_SUCH_METHOD: case INTERNAL_ERROR: // // An error that should happen only if there is something unusual: // e.g., there is XRL mismatch, no enough internal resources, etc. // We don't try to recover from such errors, hence this is fatal. // XLOG_FATAL("Fatal XRL error: %s", xrl_error.str().c_str()); break; case REPLY_TIMED_OUT: case SEND_FAILED_TRANSIENT: // // If a transient error, then start a timer to try again // (unless the timer is already running). // if (! _fea_fib_client_registration_timer.scheduled()) { XLOG_ERROR("Failed to add IPv6 FIB client to the FEA: %s. " "Will try again.", xrl_error.str().c_str()); _fea_fib_client_registration_timer = _eventloop.new_oneoff_after( RETRY_TIMEVAL, callback(this, &XrlFib2mribNode::send_fea_add_fib_client)); } break; } } void XrlFib2mribNode::fea_fib_client_send_delete_fib_client6_cb( const XrlError& xrl_error) { switch (xrl_error.error_code()) { case OKAY: // // If success, then we are done // _is_fea_have_ipv6_tested = false; _fea_have_ipv6 = false; _is_fea_fib_client6_registered = false; Fib2mribNode::decr_shutdown_requests_n(); break; case COMMAND_FAILED: // // If a command failed because the other side rejected it, this is // fatal. // XLOG_FATAL("Failed to deregister IPv6 FIB client with the FEA: %s", xrl_error.str().c_str()); break; case NO_FINDER: case RESOLVE_FAILED: case SEND_FAILED: // // A communication error that should have been caught elsewhere // (e.g., by tracking the status of the Finder and the other targets). // Probably we caught it here because of event reordering. // In some cases we print an error. In other cases our job is done. // _is_fea_have_ipv6_tested = false; _fea_have_ipv6 = false; _is_fea_fib_client6_registered = false; Fib2mribNode::decr_shutdown_requests_n(); break; case BAD_ARGS: case NO_SUCH_METHOD: case INTERNAL_ERROR: // // An error that should happen only if there is something unusual: // e.g., there is XRL mismatch, no enough internal resources, etc. // We don't try to recover from such errors, hence this is fatal. // XLOG_FATAL("Fatal XRL error: %s", xrl_error.str().c_str()); break; case REPLY_TIMED_OUT: case SEND_FAILED_TRANSIENT: // // If a transient error, then start a timer to try again // (unless the timer is already running). // if (! _rib_register_shutdown_timer.scheduled()) { XLOG_ERROR("Cannot deregister IPv6 FIB client with the FEA: %s. " "Will try again.", xrl_error.str().c_str()); _rib_register_shutdown_timer = _eventloop.new_oneoff_after( RETRY_TIMEVAL, callback(this, &XrlFib2mribNode::rib_register_shutdown)); } break; } } void XrlFib2mribNode::rib_client_send_add_igp_table6_cb(const XrlError& xrl_error) { switch (xrl_error.error_code()) { case OKAY: // // If success, then we are done // _is_rib_igp_table6_registered = true; send_rib_add_tables(); Fib2mribNode::decr_startup_requests_n(); break; case COMMAND_FAILED: // // If a command failed because the other side rejected it, this is // fatal. // XLOG_FATAL("Cannot add IPv6 IGP table to the RIB: %s", xrl_error.str().c_str()); break; case NO_FINDER: case RESOLVE_FAILED: case SEND_FAILED: // // A communication error that should have been caught elsewhere // (e.g., by tracking the status of the Finder and the other targets). // Probably we caught it here because of event reordering. // In some cases we print an error. In other cases our job is done. // XLOG_ERROR("XRL communication error: %s", xrl_error.str().c_str()); break; case BAD_ARGS: case NO_SUCH_METHOD: case INTERNAL_ERROR: // // An error that should happen only if there is something unusual: // e.g., there is XRL mismatch, no enough internal resources, etc. // We don't try to recover from such errors, hence this is fatal. // XLOG_FATAL("Fatal XRL error: %s", xrl_error.str().c_str()); break; case REPLY_TIMED_OUT: case SEND_FAILED_TRANSIENT: // // If a transient error, then start a timer to try again // (unless the timer is already running). // if (! _rib_igp_table_registration_timer.scheduled()) { XLOG_ERROR("Failed to add IPv6 IGP table to the RIB: %s. " "Will try again.", xrl_error.str().c_str()); _rib_igp_table_registration_timer = _eventloop.new_oneoff_after( RETRY_TIMEVAL, callback(this, &XrlFib2mribNode::send_rib_add_tables)); } break; } } void XrlFib2mribNode::rib_client_send_delete_igp_table6_cb( const XrlError& xrl_error) { switch (xrl_error.error_code()) { case OKAY: // // If success, then we are done // _is_rib_igp_table6_registered = false; Fib2mribNode::decr_shutdown_requests_n(); break; case COMMAND_FAILED: // // If a command failed because the other side rejected it, this is // fatal. // XLOG_FATAL("Cannot deregister IPv6 IGP table with the RIB: %s", xrl_error.str().c_str()); break; case NO_FINDER: case RESOLVE_FAILED: case SEND_FAILED: // // A communication error that should have been caught elsewhere // (e.g., by tracking the status of the Finder and the other targets). // Probably we caught it here because of event reordering. // In some cases we print an error. In other cases our job is done. // _is_rib_igp_table6_registered = false; Fib2mribNode::decr_shutdown_requests_n(); break; case BAD_ARGS: case NO_SUCH_METHOD: case INTERNAL_ERROR: // // An error that should happen only if there is something unusual: // e.g., there is XRL mismatch, no enough internal resources, etc. // We don't try to recover from such errors, hence this is fatal. // XLOG_FATAL("Fatal XRL error: %s", xrl_error.str().c_str()); break; case REPLY_TIMED_OUT: case SEND_FAILED_TRANSIENT: // // If a transient error, then start a timer to try again // (unless the timer is already running). // if (! _rib_register_shutdown_timer.scheduled()) { XLOG_ERROR("Failed to deregister IPv6 IGP table with the RIB: %s. " "Will try again.", xrl_error.str().c_str()); _rib_register_shutdown_timer = _eventloop.new_oneoff_after( RETRY_TIMEVAL, callback(this, &XrlFib2mribNode::rib_register_shutdown)); } break; } } #endif // ipv6 xorp/fib2mrib/xrl_fib2mrib_node.hh0000664000076400007640000003004411540225526017266 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fib2mrib/xrl_fib2mrib_node.hh,v 1.24 2008/10/02 21:57:14 bms Exp $ #ifndef __FIB2MRIB_XRL_FIB2MRIB_NODE_HH__ #define __FIB2MRIB_XRL_FIB2MRIB_NODE_HH__ // // Fib2mrib XRL-aware node definition. // #include "libxipc/xrl_std_router.hh" #include "libfeaclient/ifmgr_xrl_mirror.hh" #include "xrl/interfaces/finder_event_notifier_xif.hh" #include "xrl/interfaces/fti_xif.hh" #include "xrl/interfaces/fea_fib_xif.hh" #include "xrl/interfaces/rib_xif.hh" #include "xrl/targets/fib2mrib_base.hh" #include "fib2mrib_node.hh" // // The top-level class that wraps-up everything together under one roof // class XrlFib2mribNode : public Fib2mribNode, public XrlStdRouter, public XrlFib2mribTargetBase { public: XrlFib2mribNode(EventLoop& eventloop, const string& class_name, const string& finder_hostname, uint16_t finder_port, const string& finder_target, const string& fea_target, const string& rib_target); ~XrlFib2mribNode(); /** * Startup the node operation. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int startup(); /** * Shutdown the node operation. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int shutdown(); /** * Get a reference to the XrlRouter instance. * * @return a reference to the XrlRouter (@ref XrlRouter) instance. */ XrlRouter& xrl_router() { return *this; } protected: // // XRL target methods // /** * Get name of Xrl Target */ XrlCmdError common_0_1_get_target_name( // Output values, string& name); /** * Get version string from Xrl Target */ XrlCmdError common_0_1_get_version( // Output values, string& version); /** * Get status of Xrl Target */ XrlCmdError common_0_1_get_status( // Output values, uint32_t& status, string& reason); /** * Request clean shutdown of Xrl Target */ XrlCmdError common_0_1_shutdown(); XrlCmdError common_0_1_startup(); /** * Announce target birth to observer. * * @param target_class the target class name. * * @param target_instance the target instance name. */ XrlCmdError finder_event_observer_0_1_xrl_target_birth( // Input values, const string& target_class, const string& target_instance); /** * Announce target death to observer. * * @param target_class the target class name. * * @param target_instance the target instance name. */ XrlCmdError finder_event_observer_0_1_xrl_target_death( // Input values, const string& target_class, const string& target_instance); /** * Add a route. * * @param network the network address prefix of the route to add. * * @param nexthop the address of the next-hop router toward the * destination. * * @param ifname the name of the physical interface toward the * destination. * * @param vifname the name of the virtual interface toward the * destination. * * @param metric the routing metric toward the destination. * * @param admin_distance the administratively defined distance toward the * destination. * * @param protocol_origin the name of the protocol that originated this * route. * * @param xorp_route true if this route was installed by XORP. */ XrlCmdError fea_fib_client_0_1_add_route4( // Input values, const IPv4Net& network, const IPv4& nexthop, const string& ifname, const string& vifname, const uint32_t& metric, const uint32_t& admin_distance, const string& protocol_origin, const bool& xorp_route); /** * Replace a route. * * @param network the network address prefix of the route to replace. * * @param nexthop the address of the next-hop router toward the * destination. * * @param ifname the name of the physical interface toward the * destination. * * @param vifname the name of the virtual interface toward the * destination. * * @param metric the routing metric toward the destination. * * @param admin_distance the administratively defined distance toward the * destination. * * @param protocol_origin the name of the protocol that originated this * route. * * @param xorp_route true if this route was installed by XORP. */ XrlCmdError fea_fib_client_0_1_replace_route4( // Input values, const IPv4Net& network, const IPv4& nexthop, const string& ifname, const string& vifname, const uint32_t& metric, const uint32_t& admin_distance, const string& protocol_origin, const bool& xorp_route); /** * Delete a route. * * @param network the network address prefix of the route to delete. * * @param ifname the name of the physical interface toward the * destination. * * @param vifname the name of the virtual interface toward the * destination. */ XrlCmdError fea_fib_client_0_1_delete_route4( // Input values, const IPv4Net& network, const string& ifname, const string& vifname); /** * Route resolve notification. * * @param network the network address prefix of the lookup * which failed or for which upper layer intervention is * requested from the FIB. */ XrlCmdError fea_fib_client_0_1_resolve_route4( // Input values, const IPv4Net& network); /** * Enable/disable/start/stop Fib2mrib. * * @param enable if true, then enable Fib2mrib, otherwise disable it. */ XrlCmdError fib2mrib_0_1_enable_fib2mrib( // Input values, const bool& enable); XrlCmdError fib2mrib_0_1_start_fib2mrib(); XrlCmdError fib2mrib_0_1_stop_fib2mrib(); /** * Enable/disable the Fib2mrib trace log for all operations. * * @param enable if true, then enable the trace log, otherwise disable it. */ XrlCmdError fib2mrib_0_1_enable_log_trace_all( // Input values, const bool& enable); /** * Configure a policy filter. * * @param filter Id of filter to configure. * @param conf Configuration of filter. */ XrlCmdError policy_backend_0_1_configure( // Input values, const uint32_t& filter, const string& conf); /** * Reset a policy filter. * * @param filter Id of filter to reset. */ XrlCmdError policy_backend_0_1_reset( // Input values, const uint32_t& filter); /** * Push routes through policy filters for re-filtering. */ XrlCmdError policy_backend_0_1_push_routes(); #ifdef HAVE_IPV6 XrlCmdError fea_fib_client_0_1_add_route6( // Input values, const IPv6Net& network, const IPv6& nexthop, const string& ifname, const string& vifname, const uint32_t& metric, const uint32_t& admin_distance, const string& protocol_origin, const bool& xorp_route); XrlCmdError fea_fib_client_0_1_replace_route6( // Input values, const IPv6Net& network, const IPv6& nexthop, const string& ifname, const string& vifname, const uint32_t& metric, const uint32_t& admin_distance, const string& protocol_origin, const bool& xorp_route); XrlCmdError fea_fib_client_0_1_delete_route6( // Input values, const IPv6Net& network, const string& ifname, const string& vifname); XrlCmdError fea_fib_client_0_1_resolve_route6( // Input values, const IPv6Net& network); #endif private: const ServiceBase* ifmgr_mirror_service_base() const { return dynamic_cast(&_ifmgr); } const IfMgrIfTree& ifmgr_iftree() const { return _ifmgr.iftree(); } /** * Called when Finder connection is established. * * Note that this method overwrites an XrlRouter virtual method. */ virtual void finder_connect_event(); /** * Called when Finder disconnect occurs. * * Note that this method overwrites an XrlRouter virtual method. */ virtual void finder_disconnect_event(); void fea_register_startup(); void finder_register_interest_fea_cb(const XrlError& xrl_error); void fea_register_shutdown(); void finder_deregister_interest_fea_cb(const XrlError& xrl_error); void send_fea_add_fib_client(); void fea_fti_client_send_have_ipv4_cb(const XrlError& xrl_error, const bool* result); #ifdef HAVE_IPV6 void fea_fti_client_send_have_ipv6_cb(const XrlError& xrl_error, const bool* result); void fea_fib_client_send_add_fib_client6_cb(const XrlError& xrl_error); void fea_fib_client_send_delete_fib_client6_cb(const XrlError& xrl_error); void rib_client_send_add_igp_table6_cb(const XrlError& xrl_error); void rib_client_send_delete_igp_table6_cb(const XrlError& xrl_error); #endif void fea_fib_client_send_add_fib_client4_cb(const XrlError& xrl_error); void send_fea_delete_fib_client(); void fea_fib_client_send_delete_fib_client4_cb(const XrlError& xrl_error); void rib_register_startup(); void finder_register_interest_rib_cb(const XrlError& xrl_error); void rib_register_shutdown(); void finder_deregister_interest_rib_cb(const XrlError& xrl_error); void send_rib_add_tables(); void rib_client_send_add_igp_table4_cb(const XrlError& xrl_error); void send_rib_delete_tables(); void rib_client_send_delete_igp_table4_cb(const XrlError& xrl_error); /** * Inform the RIB about a route change. * * @param fib2mrib_route the route with the information about the change. */ void inform_rib_route_change(const Fib2mribRoute& fib2mrib_route); /** * Cancel a pending request to inform the RIB about a route change. * * @param fib2mrib_route the route with the request that would be canceled. */ void cancel_rib_route_change(const Fib2mribRoute& fib2mrib_route); void send_rib_route_change(); void send_rib_route_change_cb(const XrlError& xrl_error); EventLoop& _eventloop; XrlFtiV0p2Client _xrl_fea_fti_client; XrlFeaFibV0p1Client _xrl_fea_fib_client; XrlRibV0p1Client _xrl_rib_client; const string _finder_target; const string _fea_target; const string _rib_target; IfMgrXrlMirror _ifmgr; list _inform_rib_queue; XorpTimer _inform_rib_queue_timer; XrlFinderEventNotifierV0p1Client _xrl_finder_client; static const TimeVal RETRY_TIMEVAL; bool _is_finder_alive; bool _is_fea_alive; bool _is_fea_registered; bool _is_fea_registering; bool _is_fea_deregistering; XorpTimer _fea_register_startup_timer; XorpTimer _fea_register_shutdown_timer; #ifdef HAVE_IPV6 bool _is_fea_have_ipv6_tested; bool _fea_have_ipv6; bool _is_fea_fib_client6_registered; bool _is_rib_igp_table6_registered; #endif bool _is_fea_have_ipv4_tested; bool _fea_have_ipv4; bool _is_fea_fib_client4_registered; XorpTimer _fea_fib_client_registration_timer; bool _is_rib_alive; bool _is_rib_registered; bool _is_rib_registering; bool _is_rib_deregistering; bool _is_rib_igp_table4_registered; XorpTimer _rib_register_startup_timer; XorpTimer _rib_register_shutdown_timer; XorpTimer _rib_igp_table_registration_timer; }; #endif // __FIB2MRIB_XRL_FIB2MRIB_NODE_HH__ xorp/fib2mrib/fib2mrib_node.hh0000664000076400007640000006565111540224225016410 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fib2mrib/fib2mrib_node.hh,v 1.25 2008/10/02 21:57:14 bms Exp $ #ifndef __FIB2MRIB_FIB2MRIB_NODE_HH__ #define __FIB2MRIB_FIB2MRIB_NODE_HH__ // // Fib2mrib node definition. // #include "libxorp/service.hh" #include "libxorp/status_codes.h" #include "libfeaclient/ifmgr_xrl_mirror.hh" #include "policy/backend/policytags.hh" #include "policy/backend/policy_filters.hh" class EventLoop; /** * @short A Fib2mrib helper class. * * This class is used to store a routing entry. */ class Fib2mribRoute { public: /** * Constructor for a given IPv4 route. * * @param network the network address prefix this route applies to. * @param nexthop the address of the next-hop router for this route. * @param ifname the name of the physical interface toward the * destination. * @param vifname the name of the virtual interface toward the * destination. * @param metric the routing metric for this route. * @param admin_distance the administratively defined distance for this * route. * @param protocol_origin the name of the protocol that originated this * route. * @param xorp_route true if this route was installed by XORP. */ Fib2mribRoute(const IPv4Net& network, const IPv4& nexthop, const string& ifname, const string& vifname, uint32_t metric, uint32_t admin_distance, const string& protocol_origin, bool xorp_route) : _network(network), _nexthop(nexthop), _ifname(ifname), _vifname(vifname), _metric(metric), _admin_distance(admin_distance), _protocol_origin(protocol_origin), _xorp_route(xorp_route), _route_type(IDLE_ROUTE), _is_ignored(false), _is_filtered(false), _is_accepted_by_nexthop(false) {} /** * Constructor for a given IPv6 route. * * @param network the network address prefix this route applies to. * @param nexthop the address of the next-hop router for this route. * @param ifname the name of the physical interface toward the * destination. * @param vifname the name of the virtual interface toward the * destination. * @param metric the routing metric for this route. * @param admin_distance the administratively defined distance for this * route. * @param protocol_origin the name of the protocol that originated this * route. * @param xorp_route true if this route was installed by XORP. */ Fib2mribRoute(const IPv6Net& network, const IPv6& nexthop, const string& ifname, const string& vifname, uint32_t metric, uint32_t admin_distance, const string& protocol_origin, bool xorp_route) : _network(network), _nexthop(nexthop), _ifname(ifname), _vifname(vifname), _metric(metric), _admin_distance(admin_distance), _protocol_origin(protocol_origin), _xorp_route(xorp_route), _route_type(IDLE_ROUTE), _is_ignored(false), _is_filtered(false), _is_accepted_by_nexthop(false) {} #ifdef XORP_USE_USTL Fib2mribRoute() { } #endif /** * Equality Operator * * @param other the right-hand operand to compare against. * @return true if the left-hand operand is numerically same as the * right-hand operand. */ bool operator==(const Fib2mribRoute& other) const { return ((_network == other.network()) && (_nexthop == other.nexthop()) && (_ifname == other.ifname()) && (_vifname == other.vifname()) && (_metric == other.metric()) && (_route_type == other._route_type) && (_policytags == other._policytags)); } /** * Test if this is an IPv4 route. * * @return true if this is an IPv4 route, otherwise false. */ bool is_ipv4() const { return _network.is_ipv4(); } /** * Test if this is an IPv6 route. * * @return true if this is an IPv6 route, otherwise false. */ bool is_ipv6() const { return _network.is_ipv6(); } /** * Get the network address prefix this route applies to. * * @return the network address prefix this route appies to. */ const IPvXNet& network() const { return _network; } /** * Get the address of the next-hop router for this route. * * @return the address of the next-hop router for this route. */ const IPvX& nexthop() const { return _nexthop; } /** * Set the address of the next-hop router for this route. * * @param v the address of the next-hop router for this route. */ void set_nexthop(const IPvX& v) { _nexthop = v; } /** * Get the name of the physical interface toward the destination. * * @return the name of the physical interface toward the destination. */ const string& ifname() const { return _ifname; } /** * Set the name of the physical interface toward the destination. * * @param v the name of the physical interface toward the destination. */ void set_ifname(const string& v) { _ifname = v; } /** * Get the name of the virtual interface toward the destination. * * @return the name of the virtual interface toward the destination. */ const string& vifname() const { return _vifname; } /** * Set the name of the virtual interface toward the destination. * * @param v the name of the virtual interface toward the destination. */ void set_vifname(const string& v) { _vifname = v; } /** * Get the the administratively defined distance for this route. * * @return the administratively defined distance for this route. */ uint32_t admin_distance() const { return _admin_distance; } /** * Get the metric distance for this route. * * @return the metric distance for this route. */ uint32_t metric() const { return _metric; } /** * Get the name of the protocol that originated this route. * * @return the name of the protocol that originated this route. */ const string& protocol_origin() const { return _protocol_origin; } /** * Test if this route was installed by XORP. * * @return true if this route was installed by XORP. */ bool xorp_route() const { return _xorp_route; } /** * Test if this is a route to add. * * @return true if this is a route to add, otherwise false. */ bool is_add_route() const { return (_route_type == ADD_ROUTE); } /** * Test if this is a replacement route. * * @return true if this is a replacement route, otherwise false. */ bool is_replace_route() const { return (_route_type == REPLACE_ROUTE); } /** * Test if this is a route to delete. * * @return true if this is a route to delete, otherwise false. */ bool is_delete_route() const { return (_route_type == DELETE_ROUTE); } /** * Set the type of this route to "a route to add". */ void set_add_route() { _route_type = ADD_ROUTE; } /** * Set the type of this route to "a replacement route". */ void set_replace_route() { _route_type = REPLACE_ROUTE; } /** * Set the type of this route to "a route to delete". */ void set_delete_route() { _route_type = DELETE_ROUTE; } /** * Test if the route is interface-specific (e.g., if the interface * is explicitly specified). * * @return true if the route is interface-specific, otherwise false. */ bool is_interface_route() const { return ! (_ifname.empty() && _vifname.empty()); } /** * Check whether the route entry is valid. * * @param error_msg the error message (if error). * @return true if the route entry is valid, otherwise false. */ bool is_valid_entry(string& error_msg) const; /** * Test if the route is to be ignored. * * This method is used only for internal purpose when passing the route * around. * * @return true if the route is to be ignored, otherwise false. */ bool is_ignored() const { return _is_ignored; } /** * Set whether the route is to be ignored. * * This method is used only for internal purpose when passing the route * around. * * @param v true if the route is to be ignored, otherwise false. */ void set_ignored(bool v) { _is_ignored = v; } /** * @return policy-tags for this route. */ PolicyTags& policytags() { return _policytags; } /** * @return whether route has been rejected by policy filter. * * @return true if route has been rejected by a policy filter, otherwise * false. */ bool is_filtered() const { return _is_filtered; } /** * Set a flag that indicates whether the route is to be considered * filtered [rejected by the policy filter]. * * @param v true if the route should be considered filtered, otherwise * false. */ void set_filtered(bool v) { _is_filtered = v; } /** * Test whether the route is accepted based on its next-hop information. * * @return true if the route is accepted based on its next-hop * information, otherwise false. */ bool is_accepted_by_nexthop() const { return _is_accepted_by_nexthop; } /** * Set a flag that indicates whether the route is accepted based * on its next-hop information. * * @param v true if the route is accepted based on its next-hop * information, otherwise false. */ void set_accepted_by_nexthop(bool v) { _is_accepted_by_nexthop = v; } /** * Test whether the route is accepted for transmission to the RIB. * * @return true if route is accepted for transmission to the RIB, * otherwise false. */ bool is_accepted_by_rib() const; private: IPvXNet _network; IPvX _nexthop; string _ifname; string _vifname; uint32_t _metric; uint32_t _admin_distance; string _protocol_origin; bool _xorp_route; enum RouteType { IDLE_ROUTE, ADD_ROUTE, REPLACE_ROUTE, DELETE_ROUTE }; RouteType _route_type; bool _is_ignored; // True if the route is to be ignored bool _is_filtered; // True if rejected by a policy filter bool _is_accepted_by_nexthop; // True if the route is accepted based on its next-hop information PolicyTags _policytags; }; /** * @short The Fib2mrib node class. * * There should be one node per Fib2mrib instance. */ class Fib2mribNode : public IfMgrHintObserver, public ServiceBase, public ServiceChangeObserverBase { public: /** * Constructor for a given event loop. * * @param eventloop the event loop to use. */ Fib2mribNode(EventLoop& eventloop); /** * Destructor */ virtual ~Fib2mribNode(); /** * Get the event loop this node is added to. * * @return the event loop this node is added to. */ EventLoop& eventloop() { return _eventloop; } /** * Get the protocol name. * * @return a string with the protocol name. */ const string& protocol_name() const { return _protocol_name; } /** * Startup the node operation. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int startup(); /** * Shutdown the node operation. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int shutdown(); /** * Get the node status (see @ref ProcessStatus). * * @param reason_msg return-by-reference string that contains * human-readable information about the status. * @return the node status (see @ref ProcessStatus). */ ProcessStatus node_status(string& reason_msg); /** * Test if the node processing is done. * * @return true if the node processing is done, otherwise false. */ bool is_done() const { return (_node_status == PROC_DONE); } /** * Test whether the node operation is enabled. * * @return true if the node operation is enabled, otherwise false. */ bool is_enabled() const { return _is_enabled; } /** * Enable/disable node operation. * * Note that for the time being it affects only whether the routes * are installed into RIB. In the future it may affect the interaction * with other modules as well. * * @param enable if true then enable node operation, otherwise disable it. */ void set_enabled(bool enable); /** * Add an IPv4 route. * * @param network the network address prefix this route applies to. * @param nexthop the address of the next-hop router for this route. * @param ifname the name of the physical interface toward the * destination. * @param vifname the name of the virtual interface toward the * destination. * @param metric the routing metric for this route. * @param admin_distance the administratively defined distance for this * route. * @param protocol_origin the name of the protocol that originated this * route. * @param xorp_route true if this route was installed by XORP. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_route4(const IPv4Net& network, const IPv4& nexthop, const string& ifname, const string& vifname, uint32_t metric, uint32_t admin_distance, const string& protocol_origin, bool xorp_route, string& error_msg); /** * Add an IPv6 route. * * @param network the network address prefix this route applies to. * @param nexthop the address of the next-hop router for this route. * @param ifname the name of the physical interface toward the * destination. * @param vifname the name of the virtual interface toward the * destination. * @param metric the routing metric for this route. * @param admin_distance the administratively defined distance for this * route. * @param protocol_origin the name of the protocol that originated this * route. * @param xorp_route true if this route was installed by XORP. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_route6(const IPv6Net& network, const IPv6& nexthop, const string& ifname, const string& vifname, uint32_t metric, uint32_t admin_distance, const string& protocol_origin, bool xorp_route, string& error_msg); /** * Replace an IPv4 route. * * @param network the network address prefix this route applies to. * @param nexthop the address of the next-hop router for this route. * @param ifname the name of the physical interface toward the * destination. * @param vifname the name of the virtual interface toward the * destination. * @param metric the routing metric for this route. * @param admin_distance the administratively defined distance for this * route. * @param protocol_origin the name of the protocol that originated this * route. * @param xorp_route true if this route was installed by XORP. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int replace_route4(const IPv4Net& network, const IPv4& nexthop, const string& ifname, const string& vifname, uint32_t metric, uint32_t admin_distance, const string& protocol_origin, bool xorp_route, string& error_msg); /** * Replace an IPv6 route. * * @param network the network address prefix this route applies to. * @param nexthop the address of the next-hop router for this route. * @param ifname the name of the physical interface toward the * destination. * @param vifname the name of the virtual interface toward the * destination. * @param metric the routing metric for this route. * @param admin_distance the administratively defined distance for this * route. * @param protocol_origin the name of the protocol that originated this * route. * @param xorp_route true if this route was installed by XORP. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int replace_route6(const IPv6Net& network, const IPv6& nexthop, const string& ifname, const string& vifname, uint32_t metric, uint32_t admin_distance, const string& protocol_origin, bool xorp_route, string& error_msg); /** * Delete an IPv4 route. * * @param network the network address prefix this route applies to. * @param ifname the name of the physical interface toward the * destination. * @param vifname the name of the virtual interface toward the * destination. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int delete_route4(const IPv4Net& network, const string& ifname, const string& vifname, string& error_msg); /** * Delete an IPv6 route. * * @param network the network address prefix this route applies to. * @param ifname the name of the physical interface toward the * destination. * @param vifname the name of the virtual interface toward the * destination. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int delete_route6(const IPv6Net& network, const string& ifname, const string& vifname, string& error_msg); // // Debug-related methods // /** * Test if trace log is enabled. * * This method is used to test whether to output trace log debug messges. * * @return true if trace log is enabled, otherwise false. */ bool is_log_trace() const { return (_is_log_trace); } /** * Enable/disable trace log. * * This method is used to enable/disable trace log debug messages output. * * @param is_enabled if true, trace log is enabled, otherwise is disabled. */ void set_log_trace(bool is_enabled) { _is_log_trace = is_enabled; } /** * Configure a policy filter. * * Will throw an exception on error. * * Export filter is not supported by fib2mrib routes. * * @param filter identifier of filter to configure. * @param conf configuration of the filter. */ void configure_filter(const uint32_t& filter, const string& conf); /** * Reset a policy filter. * * @param filter identifier of filter to reset. */ void reset_filter(const uint32_t& filter); /** * Push all the routes through the policy filters for re-filtering. */ void push_routes(); /** * Push or pull all the routes to/from the RIB. * * @param is_push if true, then push the routes, otherwise pull them */ void push_pull_rib_routes(bool is_push); protected: // // IfMgrHintObserver methods // void tree_complete(); void updates_made(); void incr_startup_requests_n(); void decr_startup_requests_n(); void incr_shutdown_requests_n(); void decr_shutdown_requests_n(); void update_status(); private: /** * A method invoked when the status of a service changes. * * @param service the service whose status has changed. * @param old_status the old status. * @param new_status the new status. */ void status_change(ServiceBase* service, ServiceStatus old_status, ServiceStatus new_status); /** * Get a reference to the service base of the interface manager. * * This is a pure virtual function, and it must be implemented * by the communication-wrapper class that inherits this base class. * * @return a reference to the service base of the interface manager. */ virtual const ServiceBase* ifmgr_mirror_service_base() const = 0; /** * Get a reference to the interface manager tree. * * This is a pure virtual function, and it must be implemented * by the communication-wrapper class that inherits this base class. * * @return a reference to the interface manager tree. */ virtual const IfMgrIfTree& ifmgr_iftree() const = 0; /** * Initiate registration with the FEA. * * This is a pure virtual function, and it must be implemented * by the communication-wrapper class that inherits this base class. */ virtual void fea_register_startup() = 0; /** * Initiate de-registration with the FEA. * * This is a pure virtual function, and it must be implemented * by the communication-wrapper class that inherits this base class. */ virtual void fea_register_shutdown() = 0; /** * Initiate registration with the RIB. * * This is a pure virtual function, and it must be implemented * by the communication-wrapper class that inherits this base class. */ virtual void rib_register_startup() = 0; /** * Initiate de-registration with the RIB. * * This is a pure virtual function, and it must be implemented * by the communication-wrapper class that inherits this base class. */ virtual void rib_register_shutdown() = 0; /** * Add an IPvX route. * * @param fib2mrib_route the route to add. * @see Fib2mribRoute * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_route(const Fib2mribRoute& fib2mrib_route, string& error_msg); /** * Replace a Fib2mrib IPvX route. * * @param fib2mrib_route the replacement route. * @see Fib2mribRoute * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int replace_route(const Fib2mribRoute& fib2mrib_route, string& error_msg); /** * Delete a Fib2mrib IPvX route. * * @param fib2mrib_route the route to delete. * @see Fib2mribRoute * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int delete_route(const Fib2mribRoute& fib2mrib_route, string& error_msg); /** * Prepare a copy of a route for transmission to the RIB. * * Note that the original route will be modified as appropriate. * * @param orig_route the original route to prepare. * @param copy_route the copy of the original route prepared for * transmission to the RIB. */ void prepare_route_for_transmission(Fib2mribRoute& orig_route, Fib2mribRoute& copy_route); /** * Inform the RIB about a route change. * * This is a pure virtual function, and it must be implemented * by the communication-wrapper class that inherits this base class. * * @param fib2mrib_route the route with the information about the change. */ virtual void inform_rib_route_change(const Fib2mribRoute& fib2mrib_route) = 0; /** * Cancel a pending request to inform the RIB about a route change. * * This is a pure virtual function, and it must be implemented * by the communication-wrapper class that inherits this base class. * * @param fib2mrib_route the route with the request that would be canceled. */ virtual void cancel_rib_route_change(const Fib2mribRoute& fib2mrib_route) = 0; /** * Update a route received from the FEA. * * This method is needed as a work-around of FEA-related problems * with the routes the FEA sends to interested parties such as FIB2MRIB. * A route is updated with interface-related information or next-hop * address. * * The routes received from the FEA for the directly-connected subnets * may not contain next-hop information and network interface information. * If the route is for a directly-connected subnet, and if it is missing * that information, then add the interface and next-hop router * information. * Furthermore, on startup, the routes received from the FEA may * contain interface-related information, but later updates of those * routes may be missing this information. This may create a mismatch, * therefore all routes are updated (if possible) to contain * interface-related information. * * @param iftree the tree with the interface state to update the route. * @param route the route to update. * @return true if the route was updated, otherwise false. */ bool update_route(const IfMgrIfTree& iftree, Fib2mribRoute& route); /** * Do policy filtering on a route. * * @param route route to filter. * @return true if route was accepted by policy filter, otherwise false. */ bool do_filtering(Fib2mribRoute& route); /** * Test whether a route is accepted based on its next-hop information. * * @param route the route to test. * @return true if the route is accepted based on its next-hop * information, otherwise false. */ bool is_accepted_by_nexthop(const Fib2mribRoute& route) const; /** * Inform the RIB about a route. * * @param r route which should be updated in the RIB. */ void inform_rib(const Fib2mribRoute& r); /** * Set the node status. * * @param v the new node status. */ void set_node_status(ProcessStatus v) { _node_status = v; } EventLoop& _eventloop; // The event loop ProcessStatus _node_status; // The node/process status const string _protocol_name; // The protocol name bool _is_enabled; // Flag whether node is enabled // // The routes are stored in a multimap, because we allow more than one // route for same subnet destination, but with different interface/vif // name. // E.g., in case of IPv6 we may have the same (link-local) subnet // address pre interface. // multimap _fib2mrib_routes; // // Status-related state // size_t _startup_requests_n; size_t _shutdown_requests_n; // // A local copy with the interface state information // IfMgrIfTree _iftree; // // Debug and test-related state // bool _is_log_trace; // If true, enable XLOG_TRACE() PolicyFilters _policy_filters; // Only one instance of this! }; #endif // __FIB2MRIB_FIB2MRIB_NODE_HH__ xorp/fib2mrib/fib2mrib_module.h0000664000076400007640000000235511421137511016567 0ustar greearbgreearb/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- */ /* * Copyright (c) 2001-2009 XORP, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 2, June * 1991 as published by the Free Software Foundation. Redistribution * and/or modification of this program under the terms of any other * version of the GNU General Public License is not permitted. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, * see the GNU General Public License, Version 2, a copy of which can be * found in the XORP LICENSE.gpl file. * * XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; * http://xorp.net */ /* * $XORP: xorp/fib2mrib/fib2mrib_module.h,v 1.10 2008/10/02 21:57:14 bms Exp $ */ /* * Module definitions. */ #ifndef __FIB2MRIB_FIB2MRIB_MODULE_H__ #define __FIB2MRIB_FIB2MRIB_MODULE_H__ #ifndef XORP_MODULE_NAME #define XORP_MODULE_NAME "FIB2MRIB" #endif #ifndef XORP_MODULE_VERSION #define XORP_MODULE_VERSION "0.1" #endif #endif /* __FIB2MRIB_FIB2MRIB_MODULE_H__ */ xorp/fib2mrib/SConscript0000664000076400007640000000576111631505551015401 0ustar greearbgreearb# Copyright (c) 2009-2011 XORP, Inc and Others # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # $XORP$ import os Import('env') env = env.Clone() is_shared = env.has_key('SHAREDLIBS') env.AppendUnique(CPPPATH = [ '#', '$BUILDDIR', ]) env.PrependUnique(LIBPATH = [ '$BUILDDIR/libxorp', '$BUILDDIR/libcomm', '$BUILDDIR/libxipc', '$BUILDDIR/libproto', '$BUILDDIR/libfeaclient', '$BUILDDIR/policy/backend', '$BUILDDIR/policy/common', '$BUILDDIR/xrl/interfaces', '$BUILDDIR/xrl/targets', '.' ]) env.AppendUnique(LIBS = [ 'xorp_fib2mrib', 'xst_fib2mrib', 'xorp_policy_backend', 'xorp_policy_common', 'xorp_fea_client', 'xif_finder_event_notifier', 'xif_fea_ifmgr_mirror', 'xif_fea_ifmgr_replicator', 'xif_fti', 'xif_fea_fib', 'xif_rib', 'xst_fea_ifmgr_mirror', 'xorp_ipc', 'xorp_core', 'xorp_proto', 'xorp_comm' ]) if not is_shared: env.AppendUnique(LIBS = [ "crypto", ]) if not (env.has_key('mingw') and env['mingw']): env.AppendUnique(LIBS = [ "rt", ]) if (env.has_key('mingw') and env['mingw']): env.AppendUnique(LIBS = [ 'ws2_32', 'iphlpapi', # 'mprapi', 'regex', 'winmm', ]) env.Append(LIBS = ['xorp_core']) env.Replace(RPATH = [ env.Literal(env['xorp_module_rpath']) ]) libxorp_fib2mrib_srcs = [ 'fib2mrib_node.cc', 'fib2mrib_varrw.cc', 'xrl_fib2mrib_node.cc' ] if is_shared: libxorp_fib2mrib = env.SharedLibrary( target = 'libxorp_fib2mrib', source = libxorp_fib2mrib_srcs, LIBS = '') if env['rtld_origin']: for obj in libxorp_fib2mrib: env.AddPostAction(libxorp_fib2mrib, env.Symlink(obj.abspath, os.path.join(env['xorp_alias_libdir'], str(obj)))) else: libxorp_fib2mrib = env.StaticLibrary( target = 'libxorp_fib2mrib', source = libxorp_fib2mrib_srcs, LIBS = '') fib2mribsrcs = [ 'xorp_fib2mrib.cc', ] fib2mrib = env.Program(target = 'xorp_fib2mrib', source = fib2mribsrcs) env.Alias('install', env.InstallProgram(env['xorp_moduledir'], fib2mrib)) if is_shared: env.Alias('install', env.InstallLibrary(env['xorp_libdir'], libxorp_fib2mrib)) Default(fib2mrib) xorp/fib2mrib/xorp_fib2mrib.cc0000664000076400007640000001122311540224225016423 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // XORP Fib2mrib module implementation. // #include "fib2mrib_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/callback.hh" #include "libxorp/eventloop.hh" #include "libxorp/exceptions.hh" #include "xrl_fib2mrib_node.hh" #ifdef HAVE_GETOPT_H #include #endif // // Local functions prototypes // static void usage(const char *argv0, int exit_value); /** * usage: * @argv0: Argument 0 when the program was called (the program name itself). * @exit_value: The exit value of the program. * * Print the program usage. * If @exit_value is 0, the usage will be printed to the standart output, * otherwise to the standart error. **/ static void usage(const char *argv0, int exit_value) { FILE *output; const char *progname = strrchr(argv0, '/'); if (progname != NULL) progname++; // Skip the last '/' if (progname == NULL) progname = argv0; // // If the usage is printed because of error, output to stderr, otherwise // output to stdout. // if (exit_value == 0) output = stdout; else output = stderr; fprintf(output, "Usage: %s [-F [:]]\n", progname); fprintf(output, " -F [:] : finder hostname and port\n"); fprintf(output, " -h : usage (this message)\n"); fprintf(output, "\n"); fprintf(output, "Program name: %s\n", progname); fprintf(output, "Module name: %s\n", XORP_MODULE_NAME); fprintf(output, "Module version: %s\n", XORP_MODULE_VERSION); exit (exit_value); // NOTREACHED } static void fib2mrib_main(const string& finder_hostname, uint16_t finder_port) { setup_dflt_sighandlers(); // // Init stuff // EventLoop eventloop; // // Fib2mrib node // XrlFib2mribNode xrl_fib2mrib_node( eventloop, "fib2mrib", finder_hostname, finder_port, "finder", "fea", "rib"); wait_until_xrl_router_is_ready(eventloop, xrl_fib2mrib_node.xrl_router()); // Startup xrl_fib2mrib_node.startup(); // // Main loop // while (xorp_do_run && !xrl_fib2mrib_node.is_done()) { eventloop.run(); } } int main(int argc, char *argv[]) { int ch; string::size_type idx; const char *argv0 = argv[0]; string finder_hostname = FinderConstants::FINDER_DEFAULT_HOST().str(); uint16_t finder_port = FinderConstants::FINDER_DEFAULT_PORT(); // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); // // Get the program options // while ((ch = getopt(argc, argv, "F:h")) != -1) { switch (ch) { case 'F': // Finder hostname and port finder_hostname = optarg; idx = finder_hostname.find(':'); if (idx != string::npos) { if (idx + 1 >= finder_hostname.length()) { // No port number usage(argv0, 1); // NOTREACHED } char* p = &finder_hostname[idx + 1]; finder_port = static_cast(atoi(p)); finder_hostname = finder_hostname.substr(0, idx); } break; case 'h': case '?': // Help usage(argv0, 0); // NOTREACHED break; default: usage(argv0, 1); // NOTREACHED break; } } argc -= optind; argv += optind; if (argc != 0) { usage(argv0, 1); // NOTREACHED } // // Run everything // try { fib2mrib_main(finder_hostname, finder_port); } catch(...) { xorp_catch_standard_exceptions(); } // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); exit (0); } xorp/fib2mrib/fib2mrib_varrw.hh0000664000076400007640000000344211421137511016611 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fib2mrib/fib2mrib_varrw.hh,v 1.10 2008/10/02 21:57:14 bms Exp $ #ifndef __FIB2MRIB_FIB2MRIB_VARRW_HH__ #define __FIB2MRIB_FIB2MRIB_VARRW_HH__ #include "policy/backend/single_varrw.hh" #include "policy/common/element_factory.hh" #include "fib2mrib_node.hh" /** * @short Allows variables to be written and read from fib2mrib routes. */ class Fib2mribVarRW : public SingleVarRW { public: enum { VAR_NETWORK4 = VAR_PROTOCOL, VAR_NEXTHOP4, VAR_NETWORK6, VAR_NEXTHOP6, VAR_METRIC }; /** * @param route route to read/write values from. */ Fib2mribVarRW(Fib2mribRoute& route); // SingleVarRW inteface: void start_read(); void single_write(const Id& id, const Element& e); Element* single_read(const Id& id); private: Fib2mribRoute& _route; ElementFactory _ef; bool _is_ipv4; bool _is_ipv6; }; #endif // __FIB2MRIB_FIB2MRIB_VARRW_HH__ xorp/fib2mrib/fib2mrib_varrw.cc0000664000076400007640000000413311421137511016575 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fib2mrib_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "fib2mrib_varrw.hh" Fib2mribVarRW::Fib2mribVarRW(Fib2mribRoute& route) : _route(route), _is_ipv4(route.is_ipv4()), _is_ipv6(route.is_ipv6()) { } void Fib2mribVarRW::start_read() { initialize(_route.policytags()); if (_is_ipv4) { initialize(VAR_NETWORK4, _ef.create(ElemIPv4Net::id, _route.network().str().c_str())); initialize(VAR_NEXTHOP4, _ef.create(ElemIPv4NextHop::id, _route.nexthop().str().c_str())); initialize(VAR_NETWORK6, NULL); initialize(VAR_NEXTHOP6, NULL); } if (_is_ipv6) { initialize(VAR_NETWORK6, _ef.create(ElemIPv6Net::id, _route.network().str().c_str())); initialize(VAR_NEXTHOP6, _ef.create(ElemIPv6NextHop::id, _route.nexthop().str().c_str())); initialize(VAR_NETWORK4, NULL); initialize(VAR_NEXTHOP4, NULL); } ostringstream oss; oss << _route.metric(); initialize(VAR_METRIC, _ef.create(ElemU32::id, oss.str().c_str())); } void Fib2mribVarRW::single_write(const Id& /* id */, const Element& /* e */) { } Element* Fib2mribVarRW::single_read(const Id& /*id */) { XLOG_UNREACHABLE(); } xorp/utils/0000775000076400007640000000000011540225535013022 5ustar greearbgreearbxorp/utils/xrl_shell_lib.sh0000664000076400007640000002444111421137511016177 0ustar greearbgreearb#!/bin/sh # # $XORP: xorp/utils/xrl_shell_lib.sh,v 1.6 2006/04/26 05:31:08 pavlin Exp $ # # # Library of functions to sent XRL and process the result. # #set -x CALLXRL="../libxipc/call_xrl" XRL_VARIABLE_SEPARATOR="\&" # # Test if the return result of an XRL contains a particular variable # # Usage: has_xrl_variable # # Return 0 if the XRL contains the variable, otherwise return 1. # has_xrl_variable() { if [ $# -lt 2 ] ; then echo "Usage: $0 " exit 1 fi _xrl_result="$1" _xrl_variable_xrl_type="$2" echo "${_xrl_result}" | \ awk -F"${XRL_VARIABLE_SEPARATOR}" -v xrl_variable_xrl_type="${_xrl_variable_xrl_type}" ' # AWK CODE STARTS # XXX: do NOT put single quotas in the awk code below, otherwise # it may not work!! # # The code assumes that in the command line there are the following options: # -F"${XRL_VARIABLE_SEPARATOR}" # to specify the XRL variable separator (e.g, "\&") # # -v xrl_variable_xrl_type="${_xrl_variable_xrl_type}" # to specify the variable name and type BEGIN { found = 0; } { for (i = 1; i <= NF; i++) { j = split($i, a, "="); if (j < 1) { continue; } if (a[1] == xrl_variable_xrl_type) { found = 1; # FOUND break; } } } END { if (found) { exit 0; } else { exit 1; } } # AWK CODE END ' return $? } # # Get the value of an variable from an XRL. # # Usage: get_xrl_variable_value # # Return the XRL variable value. # get_xrl_variable_value() { if [ $# -lt 2 ] ; then echo "Usage: $0 " exit 1 fi _xrl_result="$1" _xrl_variable_xrl_type="$2" echo "${_xrl_result}" | \ awk -F"${XRL_VARIABLE_SEPARATOR}" -v xrl_variable_xrl_type="${_xrl_variable_xrl_type}" ' # AWK CODE STARTS # XXX: do NOT put single quotas in the awk code below, otherwise # it may not work!! # # Assume that there is in the command line # -F"${XRL_VARIABLE_SEPARATOR}" # to specify the XRL variable separator (e.g, "\&") # # -v xrl_variable_xrl_type="${_xrl_variable_xrl_type}" # to specify the variable name and type BEGIN { found = 0; value = ""; } { for (i = 1; i <= NF; i++) { j = split($i, a, "="); if (j < 1) { continue; } if (a[1] == xrl_variable_xrl_type) { if (j >= 0) { # Non-empty value found = 1; # Concatenate the result in case it was a list value = ""; for (k = 2; k <= j; k++) { value = value "" a[k]; if (k < j) value = value "="; } } else { # Empty value found = 1; } break; # FOUND } } } END { print value; if (found) { exit 0; } else { exit 1; # NOT FOUND: shound NOT happen! } } # AWK CODE END ' } # # Split the value of an XRL variable of type ":list" into a list of values # separated by space. # # Usage: split_xrl_list_values # # Return the list of values separated by space. # split_xrl_list_values() { if [ $# -lt 2 ] ; then echo "Usage: $0 " exit 1 fi _xrl_variable="$1" _xrl_list_values_type="$2" # Separate the values with space _list_separator=",:${_xrl_list_values_type}=" _tmp_result=`echo "${_xrl_variable}" | awk -F"${_list_separator}" '{for (i = 1; i <= NF; i++) {printf("%s", $i); if (i < NF) printf(" "); }}'` # Get rid of the first value-type prefix _list_separator=":${_xrl_list_values_type}=" echo "${_tmp_result}" | awk -F"${_list_separator}" '{for (i = 1; i <= NF; i++) {printf("%s", $i);}}' } # # Test the return result of an XRL whether the XRL has succeeded. # # Usage: test_xrl_result # # Return 0 if no error, otherwise return 1. # test_xrl_result() { if [ $# -lt 4 ] ; then echo "Usage: $0 " exit 1 fi _xrl_result="$1" _xrl_variable_xrl_type="$2" _test_operator="$3" _test_value="$4" if [ "X${_xrl_variable_xrl_type}" = "X" ] ; then # Nothing to test for return 0 fi if [ "X${_test_operator}" = "X" ] ; then _error="missing argument" echo "ERROR: ${_error}" return 1 fi if [ "X${_test_value}" = "X" ] ; then _error="missing argument" echo "ERROR: ${_error}" return 1 fi # Test if the variable was returned has_xrl_variable "${_xrl_result}" "${_xrl_variable_xrl_type}" if [ $? -ne 0 ] ; then _error="cannot find variable type '${_xrl_variable_xrl_type}' inside return string '${_xrl_result}'" echo "ERROR: ${_error}" return 1 fi # Get the return value _xrl_variable_value=`get_xrl_variable_value "${_xrl_result}" "${_xrl_variable_xrl_type}"` # Test the return value if [ "X${_xrl_variable_value}" = "X" ] ; then _error="cannot find variable value-type '${_xrl_variable_xrl_type}' inside return string '${_xrl_result}'" echo "ERROR: ${_error}" return 1 fi if [ "${_xrl_variable_value}" "${_test_operator}" "${_test_value}" ] ; then return 0 else _error="return variable value of '${_xrl_variable_xrl_type}' is '${_xrl_variable_value}', but expected is '${_test_operator} ${_test_value}' inside return string '${_xrl_result}'" echo "ERROR: ${_error}" return 1 fi } # # Print the XRL return result # # Usage: print_xrl_result [ [< | all> ...]] # # Options: The result returned from the XRL. # # < | all> A list of XRL return variable names # whose return value to print. If the keyword 'all' is used, the # return value of all return variables will be print. # Note: this option can be repeated. # # Return 0 if no error, otherwise return 1. # print_xrl_result() { if [ $# -lt 1 ] ; then return 0 # Nothing to print fi _xrl_result="$1" shift while [ $# -gt 0 ] ; do _print_xrl_variable_xrl_type="$1" if [ "X${_print_xrl_variable_xrl_type}" != "X" ] ; then if [ "X${_print_xrl_variable_xrl_type}" = "Xall" ] ; then # Print all result echo "${_xrl_result}" else _print_xrl_variable_value=`get_xrl_variable_value "${_xrl_result}" "${_print_xrl_variable_xrl_type}"` if [ "X${_print_xrl_variable_value}" != "X" ] ; then echo ${_print_xrl_variable_xrl_type}=${_print_xrl_variable_value} else _error="${_print_xrl_variable_xrl_type}: NOT FOUND" echo "ERROR: ${_error}" return 1 fi fi fi shift done return 0 } # # Call an XRL. # # If necessary to test the return value, keep calling the XRL until success. # # Usage: call_xrl_wrapper [-r ] [-p < | all>] [ ] # # Options: -r The maximum number of tries to call the # XRL before giving up. If the value is 0, then the XRL will be # called until success. If this option is omitted, the default # is to try only once. # # -p < | all> The XRL return variable name # whose return value to print. If '-p all' is used, the return # value of all return variables will be print. # Note: this option can be repeated more than once. # # Return 0 if no error, otherwise return 1. # # Example: # MFEA_TARGET="MFEA_4" # mfea_enable_vif() # { # echo "mfea_enable_vif" $* # XRL="finder://${MFEA_TARGET}/mfea/0.1/enable_vif?vif_name:txt=$1" # call_xrl_wrapper -r 0 ${XRL} fail:bool = false # } # # # XXX: there is latency of 1 second between repeated XRL calls. # call_xrl_wrapper() { if [ $# -lt 1 ] ; then echo "Usage: $0 [-r ] [-p < | all>] [ ]" exit 1 fi _max_repeat_number=1 # Default value: try only once _print_list_xrl_variable_xrl_type="" while [ $# -gt 0 ] ; do case "$1" in -r ) # [-r ] if [ $# -lt 2 ] ; then echo "Usage: call_xrl_wrapper [-r ] < | all>] [ ]" exit 1 fi shift _max_repeat_number=$1 ;; -p ) # [-p < | all>] if [ $# -lt 2 ] ; then echo "Usage: call_xrl_wrapper [-r ] < | all>] [ ]" exit 1 fi shift _print_list_xrl_variable_xrl_type="${_print_list_xrl_variable_xrl_type} $1" ;; * ) # Default case break ;; esac shift done if [ $# -lt 1 ] ; then echo "Usage: call_xrl_wrapper [-r ] < | all>] [ ]" exit 1 fi _xrl="$1" # Note: the values below may be empty _xrl_variable_xrl_type="$2" _test_operator="$3" _test_value="$4" if [ "X${_xrl_variable_xrl_type}" != "X" ] ; then if [ "X${_test_operator}" = "X" ] ; then echo "ERROR: missing argument" exit 1 fi if [ "X${_test_value}" = "X" ] ; then echo "ERROR: missing argument" exit 1 fi fi # Initialize the iterator if [ "${_max_repeat_number}" -eq 0 ] ; then _iter=-1 else _iter=0 fi while [ true ] ; do _xrl_result=`"${CALLXRL}" "${_xrl}"` _ret_value=$? if [ ${_ret_value} -ne 0 ] ; then _error="failure calling '${CALLXRL} ${_xrl}'" echo "ERROR: ${_error}" fi if [ ${_ret_value} -eq 0 -a "X${_test_operator}" != "X" -a "X${_test_value}" != "X" ] ; then test_xrl_result "${_xrl_result}" "${_xrl_variable_xrl_type}" "${_test_operator}" "${_test_value}" _ret_value=$? fi if [ ${_ret_value} -eq 0 ] ; then print_xrl_result ${_xrl_result} ${_print_list_xrl_variable_xrl_type} _ret_value=$? if [ ${_ret_value} -ne 0 ] ; then _error="failure printing '${_print_list_xrl_variable_xrl_type}'" echo "ERROR: ${_error}" else # OK break; fi fi if [ "${_max_repeat_number}" -ne 0 ] ; then _iter=$((${_iter}+1)) fi if [ ${_iter} -ge ${_max_repeat_number} ] ; then echo "ERROR: reached maximum number of trials. Giving-up..." return 1 fi echo "Trying again..." sleep 1 done return 0 } # Local Variables: # mode: shell-script # sh-indentation: 4 # End: xorp/utils/rcsid2ident.sh0000775000076400007640000000101711421137511015564 0ustar greearbgreearb#!/bin/sh # # $XORP: xorp/utils/rcsid2ident.sh,v 1.1 2005/04/29 21:43:04 pavlin Exp $ # # # This script replaces the first instance of the static variable "yyrcsid" # that contains the RCS/CVS ident string with #ident. # This replacement is needed because some compilers (e.g., gcc-4.1) # generate a warning about variable "yyrcsid" being unused. # if [ $# -ne 1 ] ; then echo "Usage: $0 " exit 1 fi ed $1 <mtu:u32 # # then the auto-generated wrapper will look like: # # fea_ifmgr_get_configured_mtu() # { # if [ $# -ne 1 ] ; then # echo "Usage: fea_ifmgr_get_configured_mtu " # exit 1 # fi # # XRL="finder://fea/ifmgr/0.1/get_configured_mtu?ifname:txt=$1" # call_xrl_wrapper -p all "${XRL}" # } # # where call_xrl_wrapper is a shell function defined in # $XORP/utils/xrl_shell_lib.sh # if [ $# -ne 1 ] ; then echo "Usage: $0 " exit 1 fi filter_xrls() { if [ $# -ne 1 ] ; then echo "Usage: filter_xrls " exit 1 fi awk -F":" '{if ($1 == "finder") {print $0}}' $1 } get_xrl_target_name() { if [ $# -ne 1 ] ; then echo "Usage: get_xrl_target_name " exit 1 fi echo $1 | awk -F"/" '{print $3}' } get_xrl_interface_name() { if [ $# -ne 1 ] ; then echo "Usage: get_xrl_interface_name " exit 1 fi echo $1 | awk -F"/" '{print $4}' } get_xrl_interface_version() { if [ $# -ne 1 ] ; then echo "Usage: get_xrl_interface_version " exit 1 fi echo $1 | awk -F"/" '{print $5}' } get_xrl_method_name() { if [ $# -ne 1 ] ; then echo "Usage: get_xrl_method_name " exit 1 fi echo $1 | awk -F"/" '{print $6}' | awk -F"?" '{print $1}' | awk -F"-" '{print $1}' } get_xrl_arguments() { if [ $# -ne 1 ] ; then echo "Usage: get_xrl_arguments " exit 1 fi echo $1 | awk -F"/" '{print $6}' | awk -F"?" '{print $2}' | awk -F"-" '{print $1}' } get_xrl_arguments_number() { if [ $# -ne 1 ] ; then echo "Usage: get_xrl_arguments_number " exit 1 fi echo $1 | awk -F"&" '{print NF}' } get_xrl_split_arguments_list() { if [ $# -ne 1 ] ; then echo "Usage: get_xrl_split_arguments_list " exit 1 fi echo $1 | awk -F"&" '{for (i = 1; i <= NF; i++) {printf("%s", $i); if (i < NF) printf(" "); }}' } get_xrl_usage_wrap_arguments_list() { if [ $# -ne 1 ] ; then echo "Usage: get_xrl_usage_wrap_arguments_list " exit 1 fi echo $1 | awk '{for (i = 1; i <= NF; i++) {printf("<%s>", $i); if (i < NF) printf(" "); }}' } get_xrl_call_wrap_arguments_list() { if [ $# -ne 1 ] ; then echo "Usage: get_xrl_call_wrap_arguments_list " exit 1 fi echo $1 | awk '{for (i = 1; i <= NF; i++) {printf("%s=$%d", $i, i); if (i < NF) printf("&"); }}' } get_xrl_return_values() { if [ $# -ne 1 ] ; then echo "Usage: get_xrl_return_values " exit 1 fi echo $1 | awk -F"/" '{print $6}' | awk -F"?" '{print $2}' | awk -F">" '{print $2}' } generate_wrapper() { if [ $# -ne 1 ] ; then echo "Usage: generate_wrapper " exit 1 fi _target_name=`get_xrl_target_name $1` _interface_name=`get_xrl_interface_name $1` _interface_version=`get_xrl_interface_version $1` _method_name=`get_xrl_method_name $1` _arguments=`get_xrl_arguments $1` _arguments_number=`get_xrl_arguments_number "${_arguments}"` _split_arguments_list=`get_xrl_split_arguments_list "${_arguments}"` _usage_wrap_arguments_list=`get_xrl_usage_wrap_arguments_list "${_split_arguments_list}"` #_return_values=`get_xrl_return_values $1` # # The shell function name # _shell_function_name="${_target_name}_${_interface_name}_${_method_name}" # # The usage string # if [ ${_arguments_number} -gt 0 ] ; then _usage_string="${_shell_function_name} ${_usage_wrap_arguments_list}" else _usage_string="${_shell_function_name}" fi # # Construct the XRL # _constructed_xrl="finder://${_target_name}/${_interface_name}/${_interface_version}/${_method_name}" if [ ${_arguments_number} -gt 0 ] ; then # Add the arguments _call_wrap_arguments_list=`get_xrl_call_wrap_arguments_list "${_split_arguments_list}"` _constructed_xrl="${_constructed_xrl}?${_call_wrap_arguments_list}" fi echo "${_shell_function_name}()" echo "{" echo " if [ \$# -ne ${_arguments_number} ] ; then" echo " echo \"Usage: ${_usage_string}\"" echo " exit 1" echo " fi" echo "" echo " XRL=\"${_constructed_xrl}\"" echo " call_xrl_wrapper -p all \"\${XRL}\"" echo "}" echo "" } # # Generate the XRL wrappers # file=$1 xrl_list=`filter_xrls ${file}` for xrl in ${xrl_list} ; do generate_wrapper "${xrl}" done # Local Variables: # mode: shell-script # sh-indentation: 4 # End: xorp/utils/fig2dev-ps-anon.sed0000664000076400007640000000053211421137511016410 0ustar greearbgreearb# # $XORP$ # # This script strips the user and creation specific comments out of # fig2dev generated postscript files. This is basically a hack to # stop needless commits since, for whatever reason, sometimes the # datestamps on the fig files end up newer than the eps files and the # eps files then get regenerated. /^%%Creat/ d /^%%For:/ d xorp/utils/source_pathconf.sh0000664000076400007640000000117411421137511016535 0ustar greearbgreearb#!/bin/sh # # This is an example of how to find and source the pathconf shell script. # The latter sets path related variables. # # # source_pathconf [ ] # # Search for and source pathconf.sh shell script. By default the the # search starts in the present working directory (as specified by # $PWD). This may be overridden by specifying a . # source_pathconf() { local sdir sfile sdir=${1:-${PWD}} while test ! ${sdir} -ef "/" ; do sfile=${sdir}/utils/pathconf.sh if [ -f ${sdir}/utils/pathconf.sh ] ; then . ${sfile} return 0 fi sdir=${sdir}/.. done return 1 }xorp/utils/runit.cc0000664000076400007640000003740711540224236014502 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "libxorp/xorp.h" #include "libxorp/utils.hh" #ifdef HAVE_GETOPT_H #include #endif #ifdef HAVE_PROCESS_H #include #endif #ifdef HAVE_FCNTL_H #include #endif #include #ifdef HAVE_SYS_WAIT_H #include #endif #ifdef HOST_OS_WINDOWS typedef HANDLE PID_T; #define INVALID_PID (INVALID_HANDLE_VALUE) #else typedef pid_t PID_T; #define INVALID_PID (0) #endif #ifndef XORP_WIN32_SH_PATH #define XORP_WIN32_SH_PATH "C:\\MINGW\\BIN\\SH.EXE" #endif /** * For a lot of our testing from shell scripts it is necessary to have * a number of subsidiary programs running before starting the main * script. For example most of out programs require the finder to be * running. * * The absolute path names of the subsidiary programs that need to be * started are passed on the stardard input. * * The absolute pathname of the test script is then passed in as a * command line argument ("-c"). * * Once the test script exits all the subsidiary programs are sent a * SIGTERM. If any of the subsidiary programs exits before the test * script terminates the test script is sent a SIGTERM. * * The exit status from the test script is the exit status returned by * program. * * By default the output of the subsidiary programs is sent to * DEVNULL the "-v" flag stops this redirection. The "-q" sends * all output to DEVNULL. */ #ifdef HOST_OS_WINDOWS static HANDLE hTimer = INVALID_HANDLE_VALUE; #define DEVNULL "NUL:" #define SLEEP_CMD "sleep 2" #else #define DEVNULL "/dev/null" #define SLEEP_CMD "/bin/sleep 2" #endif /** * Split a line into multple tokens. * @param str line. * @param tokens tokens. * @param delimiters optional delimiter. */ void tokenize(const string& str, vector& tokens, const string& delimiters = " ") { string::size_type begin = str.find_first_not_of(delimiters, 0); string::size_type end = str.find_first_of(delimiters, begin); while (string::npos != begin || string::npos != end) { tokens.push_back(str.substr(begin, end - begin)); begin = str.find_first_not_of(delimiters, end); end = str.find_first_of(delimiters, begin); } } /** * Start a new process in the background. * @param process Absolute pathname and arguments. * @param output Optional file to redirect file descriptors 0 and 1. * @return Return the process id of the new process. */ PID_T xorp_spawn(const string& process, const char *output = NULL) { #ifdef HOST_OS_WINDOWS HANDLE houtput = NULL; STARTUPINFOA si; SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE }; PROCESS_INFORMATION pi; GetStartupInfoA(&si); if (output != NULL) { houtput = CreateFileA(output, FILE_READ_DATA | FILE_WRITE_DATA, FILE_SHARE_READ, &sa, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (houtput == INVALID_HANDLE_VALUE) return (0); si.hStdInput = houtput; si.hStdOutput = houtput; si.hStdError = GetStdHandle(STD_ERROR_HANDLE); } else { si.hStdInput = NULL; /* XXX: is this OK? */ si.hStdOutput = NULL; si.hStdError = GetStdHandle(STD_ERROR_HANDLE); } // // XXX: Convert POSIX paths to NT ones, and insert shell if needed. // static const char *exe_suffix = ".exe"; static const char *sh_suffix = ".sh"; static const char *sh_interp = XORP_WIN32_SH_PATH; string::size_type n; string _process = process; //fprintf(stderr, "old process string is: '%s'\n", _process.c_str()); // Strip any leading or trailing white space. n = _process.find_first_not_of("\t\n\v\f\r "); _process.erase(0, n); //fprintf(stderr, "process string after leading space is: '%s'\n", _process.c_str()); string::size_type _cmd_end = _process.find(' '); string _cmd = _process.substr(0, _cmd_end); //fprintf(stderr, "old argv[0] is: '%s'\n", _cmd.c_str()); // Convert slashes. _cmd = unix_path_to_native(_cmd); //fprintf(stderr, "argv[0] after slashify is: '%s'\n", _cmd.c_str()); // Deal with shell scripts. bool is_shell_script = false; if (_cmd.rfind(sh_suffix) != string::npos) is_shell_script = true; if (!is_shell_script && _cmd.rfind(exe_suffix) == string::npos) { _cmd.append(exe_suffix); } if (is_shell_script) { _cmd.insert(0, " "); _cmd.insert(0, sh_interp); } //fprintf(stderr, "new argv[0] is: %s\n", _cmd.c_str()); // Prepend the new argv[0]. _process.erase(0, _cmd_end); _process.insert(0, _cmd); //fprintf(stderr, "XXX: about to launch: '%s'\n", _process.c_str()); if (CreateProcessA(NULL, const_cast(_process.c_str()), NULL, NULL, TRUE, CREATE_DEFAULT_ERROR_MODE | CREATE_SUSPENDED, NULL, NULL, &si, &pi) == 0) { DWORD err = GetLastError(); CloseHandle(houtput); fprintf(stderr, "Failed to exec: %s reason: %ld\n", _process.c_str(), err); return (0); } // XXX //fprintf(stderr, "XXX: process 0x%08lx launched!\n", pi.hProcess); ResumeThread(pi.hThread); return (pi.hProcess); #else /* !HOST_OS_WINDOWS */ PID_T pid; switch (pid = fork()) { case 0: { if (output != NULL) { close(0); close(1); // close(2); open(output, O_RDONLY); open(output, O_WRONLY); // open(output, O_WRONLY); } vector tokens; tokenize(process, tokens); char *argv[tokens.size() + 1]; for (unsigned int i = 0; i < tokens.size(); i++) { argv[i] = const_cast(tokens[i].c_str()); } argv[tokens.size()] = 0; /* ** Unblock any blocked signals. */ sigset_t set; if (0 != sigfillset(&set)) { cerr << "sigfillset failed: " << strerror(errno) << endl; exit(-1); } if (0 != sigprocmask(SIG_UNBLOCK, &set, 0)) { cerr << "sigprockmask failed: " << strerror(errno) << endl; exit(-1); } errno = 0; execvp(argv[0], argv); cerr << "\nFailed to exec: " << process << " errno: " << strerror(errno) << " argv[0]: " << argv[0] << " argv: "; for (unsigned int i = 0; i commands; /** * Process ID of main script/program. */ PID_T cpid; /** * Process ID of wait script. */ PID_T wait_command_pid; string wait_command; bool core_dump = false; #ifndef HOST_OS_WINDOWS /** * Signal handler that reaps dead children. */ void sigchld(int) { int status; PID_T pid = wait(&status); if (wait_command_pid == pid) { wait_command_pid = INVALID_PID; if (status == 0) { return; } cerr << "Error running command: " << wait_command << endl; if (status == -1) { cerr << " (execv of /bin/sh failed), possible error: " << strerror(errno) << endl; } else { if (WIFEXITED(status)) { int child_rv = WEXITSTATUS(status); cerr << " exited with not zero status: " << child_rv << " status: " << status << " possible error: " << strerror(errno) << endl; } else { if (WIFSIGNALED(status)) { int csig = WTERMSIG(status); if (errno == 0) { cerr << " terminated with signal: " << csig << endl; } else { cerr << " terminated with signal: " << csig << " possible error: " << strerror(errno) << endl; } } else { cerr << " terminated without exiting properly" << endl; } } } exit(1); } if (cpid == pid) { if (core_dump) exit(-1); if (WIFEXITED(status)) exit(WEXITSTATUS(status)); else cerr << "Unexpected status"; exit(-1); } cout << "\n******************* "; vector::iterator i; for (i = commands.begin(); i != commands.end(); i++) { if (pid == i->_pid) { if (WIFSIGNALED(status) && WCOREDUMP(status)) { cout << "Command: " << i->_command << " core dumped " << pid << endl; core_dump = true; } else { cout << "Command: " << i->_command << " exited status: " << WEXITSTATUS(status) << " " << pid << endl; } i->_pid = INVALID_PID; // commands.erase(i); return; } } cerr << "Unknown pid: " << pid << endl; } #endif /* !HOST_OS_WINDOWS */ void die(int) { for (unsigned int i = 0; i < commands.size(); i++) cout << "Command: " << commands[i]._command << " " << commands[i]._pid << " did not die\n"; _exit(-1); } #ifdef HOST_OS_WINDOWS CALLBACK void die_wrapper(LPVOID arg, DWORD timerLow, DWORD timerHigh) { die(0); UNUSED(arg); UNUSED(timerLow); UNUSED(timerHigh); } void cleanup_timer(void) { if (hTimer != INVALID_HANDLE_VALUE) { CancelWaitableTimer(hTimer); CloseHandle(hTimer); } } #endif /** * When this process exits kill all the background processes. */ void tidy() { #ifndef HOST_OS_WINDOWS signal(SIGCHLD, SIG_DFL); #endif vector::iterator i; /* ** Traverse the list backwards so as to kill the first program ** started last. */ restart: i = commands.end(); if (commands.begin() != i) { do { i--; if (INVALID_PID != i->_pid) { #ifdef HOST_OS_WINDOWS GenerateConsoleCtrlEvent(CTRL_C_EVENT, GetProcessId(i->_pid)); Sleep(200); TerminateProcess(i->_pid, 0xFF); CloseHandle(i->_pid); #else kill(i->_pid, SIGTERM); #endif } else { commands.erase(i); goto restart; } } while(i != commands.begin()); } /* ** Wait for ten seconds for the processes that we have sent kills ** to to die then exit anyway. */ #ifdef HOST_OS_WINDOWS SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE }; LARGE_INTEGER wt; hTimer = CreateWaitableTimer(&sa, TRUE, NULL); wt.QuadPart = -(10 * 1000 * 1000 * 10); SetWaitableTimer(hTimer, &wt, 0, die_wrapper, NULL, FALSE); #else signal(SIGALRM, die); alarm(10); #endif for (;;) { PID_T pid; vector::iterator i; if (commands.empty()) return; #ifdef HOST_OS_WINDOWS /* * Yes, this is lame. */ HANDLE awhandles[MAXIMUM_WAIT_OBJECTS]; DWORD cnt; DWORD result; for (cnt = 0, i = commands.begin(); cnt < MAXIMUM_WAIT_OBJECTS && i != commands.end(); cnt++, i++) { awhandles[cnt] = i->_pid; } result = WaitForMultipleObjectsEx(cnt, awhandles, FALSE, INFINITE, FALSE); if (result <= WAIT_OBJECT_0 + cnt - 1) { result -= WAIT_OBJECT_0; pid = awhandles[result]; } else return; #else /* !HOST_OS_WINDOWS */ int status; pid = wait(&status); #endif /* HOST_OS_WINDOWS */ for (i = commands.begin(); i != commands.end(); i++) { if (pid == i->_pid) { commands.erase(i); if (commands.empty()) return; break; } } } } void usage(const char *myname) { cerr << "usage: " << myname << " [-q] [-v] -c command" << endl; exit(-1); } int main(int argc, char *argv[]) { const char *silent = NULL; const char *output = DEVNULL; const char *command = 0; int ch; while ((ch = getopt(argc, argv, "qvc:")) != -1) switch (ch) { case 'q': // Absolutely no output (quiet). silent = DEVNULL; break; case 'v': // All the output from the sub processes. output = NULL; break; case 'c': // The main script. command = optarg; break; case '?': default: usage(argv[0]); } if (0 == command) usage(argv[0]); /* ** Read in the commands that we need to start from the standard ** input. If there is an '=' on the line then it is assumed that ** everything after the '=' is a command to run that will exit ** when the real command is ready. */ while (cin) { string line; getline(cin, line); if ("" == line) break; vector tokens; tokenize(line, tokens, "="); switch (tokens.size()) { case 1: { Command c(tokens[0], SLEEP_CMD); commands.push_back(c); } break; case 2: { Command c(tokens[0], tokens[1]); commands.push_back(c); } break; default: cerr << "Too many '=''s " << tokens.size() << endl; exit(-1); } } /* ** Before we spawn the background processes install a signal ** handler. If any of the background processes die while we are ** executing the main script we can immediately flag an error. */ #ifdef SIGCHLD signal(SIGCHLD, sigchld); #endif /* ** Register the function to called on exit. */ #ifdef HOST_OS_WINDOWS atexit(cleanup_timer); #endif atexit(tidy); /* ** Start all the background processes. */ for (unsigned int i = 0; i < commands.size(); i++) { #ifndef HOST_OS_WINDOWS sigset_t set; if (0 != sigemptyset(&set)) { cerr << "sigemptyset failed: " << strerror(errno) << endl; exit(-1); } if (0 != sigaddset(&set, SIGCHLD)) { cerr << "sigaddset failed: " << strerror(errno) << endl; exit(-1); } if (0 != sigprocmask(SIG_BLOCK, &set, 0)) { cerr << "sigprocmask failed: " << strerror(errno) << endl; exit(-1); } #endif /* !HOST_OS_WINDOWS */ commands[i]._pid = xorp_spawn(commands[i]._command, output); #ifndef HOST_OS_WINDOWS if (0 != sigprocmask(SIG_UNBLOCK, &set, 0)) { cerr << "sigprockmask failed: " << strerror(errno) << endl; exit(-1); } sleep(1); #else SleepEx(1 * 1000, FALSE); #endif if ("" != commands[i]._wait_command) { wait_command = commands[i]._wait_command; #ifndef HOST_OS_WINDOWS /* ** Block SIGCHLD delivery until the xorp_spawn command has completed. ** It seems on Linux the child can run to completion. */ if (0 != sigprocmask(SIG_BLOCK, &set, 0)) { cerr << "sigprockmask failed: " << strerror(errno) << endl; exit(-1); } #endif /* !HOST_OS_WINDOWS */ wait_command_pid = xorp_spawn(commands[i]._wait_command.c_str(), output); #ifdef HOST_OS_WINDOWS WaitForSingleObject((HANDLE)wait_command_pid, INFINITE); #else if (0 != sigprocmask(SIG_UNBLOCK, &set, 0)) { cerr << "sigprockmask failed: " << strerror(errno) << endl; exit(-1); } while (INVALID_PID != wait_command_pid) { pause(); } #endif } } /* ** Wait five seconds for the commands that we have started to ** settle. */ #ifdef HOST_OS_WINDOWS SleepEx(5 * 1000, FALSE); #else // sleep(5); #endif /* ** Start the main script. */ cpid = xorp_spawn(command, silent); #ifdef HOST_OS_WINDOWS WaitForSingleObject((HANDLE)cpid, INFINITE); #else for (;;) pause(); #endif return 0; } xorp/utils/args.sh0000664000076400007640000000314611421137511014310 0ustar greearbgreearb# # $XORP: xorp/utils/args.sh,v 1.2 2003/10/24 02:14:27 pavlin Exp $ # # # All the command line processing for the test scripts in one place. # START_PROGRAMS="yes" CONFIGURE="yes" QUIET="" VERBOSE="" RESTART="yes" # Perform Win32 path conversion for runit if required. RUNIT="runit" RUNITDIR=${RUNITDIR:-"../../utils"} RUNITPRE="" if [ x"$OSTYPE" = xmsys ]; then RUNITPRE="cmd //c" RUNITDIR=$(cd ${RUNITDIR} && pwd -W) fi runit() { ${RUNITPRE} ${RUNITDIR}/${RUNIT} "$@" } # -q (Quiet) # -v (Verbose) # -s (Single) Do not start ancillary programs. They must already be running. # -t (Tests) The tests to run. # -a (All) Run all the tests including the ones that trigger bugs. # -b (Bugs) Run only the tests that trigger bugs. # -c (Configure) In (Single) mode BGP and the RIB are not configured. # Force configuration. # -l (Leave) Leave the ancillary programs running between tests. while getopts "qvsbt:abcl" args do case $args in q) QUIET="-q" ;; v) VERBOSE="-v" ;; s) START_PROGRAMS="no" CONFIGURE="no" ;; t) TESTS="$OPTARG" ;; a) TESTS="${TESTS} ${TESTS_NOT_FIXED}" ;; b) TESTS=${TESTS_NOT_FIXED} ;; c) REALLY_CONFIGURE="yes" ;; l) RESTART="no" ;; *) echo "default" ;; esac done if [ ${REALLY_CONFIGURE:-""} = "yes" ] then CONFIGURE="yes" fi if [ $START_PROGRAMS = "yes" -a $RESTART = "yes" ] then for i in $TESTS do COMMAND="$0 $QUIET $VERBOSE -l -t $i" echo "Entering $COMMAND" $COMMAND echo "Leaving $COMMAND" done trap '' 0 exit 0 fi # Local Variables: # mode: shell-script # sh-indentation: 4 # End: xorp/utils/ndbg0000775000076400007640000000114711421137511013657 0ustar greearbgreearb#!/bin/sh # # $XORP: xorp/utils/ndbg,v 1.3 2002/12/09 11:13:07 pavlin Exp $ # # # Execute a command and strip out the preamble from XORP debug_msg # and XLOG_* statements that are generated. # To some minds this makes debugging harder, to others it's cleaner. # # Example usage: # ndbg rib # # To strip the preamble of an existing log file: # ndbg cat logfile # # Note: output trails sed buffering, typically 1 line. # if [ $# -ne 0 ] ; then # # Preamble begins with an opening brace '[' and ends with ']'. Careful to # only filter lines beginning with preamble. # $* 2>&1 | sed -e '/^\[/ s/\[[^]]*\] //' fi xorp/utils/xorpc.sh0000775000076400007640000000064711421137511014515 0ustar greearbgreearb#!/bin/sh # # Create ssh tunnels to all peripherals connected via xorpc which provide # a HTTP interface. # Atanu. # # Put the location of your ssh here. If you have ssh2 you can add the "-N" # flag # SSH1 #SSH="/usr/local/bin/ssh" # SSH2 SSH="/usr/bin/ssh -N " for i in 9000:xorpc:80 9001:xorpsw1:80 9002:xorppwr1:80 9003:xorppwr2:80 do COMMAND="xterm -iconic -title $i -e $SSH -L $i xorpc.icir.org" $COMMAND & done xorp/utils/SConscript0000664000076400007640000000266311540225535015043 0ustar greearbgreearb# Copyright (c) 2009-2011 XORP, Inc and Others # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # $XORP$ import os Import('env') env = env.Clone() is_shared = env.has_key('SHAREDLIBS') env.AppendUnique(CPPPATH = [ '#', '$BUILDDIR', ]) env.PrependUnique(LIBPATH = [ '.', ]) env.AppendUnique(LIBS = [ # Nothing at this time ]) env.Replace(RPATH = [ env.Literal(env['xorp_module_rpath']) ]) runit_srcs = [ 'runit.cc', ] runit = env.Program(target = 'runit', source = runit_srcs) if env['enable_tests']: env.Alias('install', env.InstallProgram(env['xorp_tooldir'], runit)) # Install scripts #env.Alias('install', env.InstallProgram('$exec_prefix/sbin/', env.Entry('bgp_xrl_shell_funcs.sh'))) Default(runit) xorp/utils/attach_gdb0000775000076400007640000000225511421137511015026 0ustar greearbgreearb#!/bin/sh # # $XORP$ # # # A shell script to run gdb and attach it to a running process. # Usage: # attach_gdb [optional_gdb_arguments] # Example: # attach_gdb my_program b my_file.c:55 # will attach gdb to a running binary named "my_program" and will # create a breakpoint in file my_file.c line 55 # atexit() { rm -f /tmp/attach_gdb.$$ rm -f /tmp/attach_gdb_ps.$$ } interrupt() { exit 1 } GDB="gdb" if [ $# -lt 1 ] ; then echo "Usage: $0 program_name [gdb-command]" exit 1 fi program_name=$1 # # Get the PID for the program # ps -a | egrep -v $0 > /tmp/attach_gdb_ps.$$ program_pid=`cat /tmp/attach_gdb_ps.$$ | awk -v PROGRAM_NAME=$program_name '{if ($NF == PROGRAM_NAME) print $1;}'` if [ "X${program_pid}" = "X" ] ; then echo "Program $program_name not found" exit 1 fi shift # # Get the optional gdb command (e.g., where to place a breakpoint) # gdb_command="" if [ $# -gt 0 ] ; then gdb_command=$@ fi if [ "X${gdb_command}" = "X" ] ; then $GDB $program_name $program_pid else trap "atexit" 0 trap "interrupt" 2 3 15 shift echo "$gdb_command" > /tmp/attach_gdb.$$ $GDB -x /tmp/attach_gdb.$$ $program_name $program_pid fi xorp/utils/bogon-be-gone.sh0000775000076400007640000000333111421137511015771 0ustar greearbgreearb#!/bin/sh # # $XORP: xorp/utils/bogon-be-gone.sh,v 1.2 2003/09/25 15:58:34 hodson Exp $ # # # This is a wrapper to give in place sed editing. The role of the sed # script is to coerce code into a more xorp coding style friendly # manner. It targets some of the common xorp developer bogons that # everyone makes and is not an indent replacement or a magic format # fixer. # # When you use this script check the diffs and run compilation and # tests before committing the changes back to the repository. You will # get eye strain looking at the diffs :-) # # NB You may want to run this script multiple times across the same files # because some of the sed expressions overlap and don't do the job fully # otherwise. # # NB2 You should probably give a heads up before commiting changes # proposed by this script since it tends to get lots of nitty things that # may interfere with other people's pending commits. # SCRIPTDIR=`dirname $0` SCRIPTNAME=`basename $0` if [ "${SCRIPTDIR}" = "${SCRIPTNAME}" ] ; then SCRIPTDIR=. fi DEBOGON_SCRIPT=${SCRIPTDIR}/${SCRIPTNAME} # Shell script name ends in "sh", invoked sed script name ends in "sed" SED_SCRIPT=`echo "$DEBOGON_SCRIPT" | sed -e 's@sh$@sed@'` if [ ! -f ${SED_SCRIPT} ] ; then echo "Could not find sed script." >2 exit 1 fi if [ $# -eq 0 ] ; then cat <<-EOF usage: ${SCRIPT_NAME} [ file1.hh file1.cc ... ] Strips assorted xorp developer bogons from source files. EOF exit 1 fi # Poor man's inplace sed for i in $* ; do if [ ! -f $i ] ; then echo "$i is not a file, exiting" >2 exit 1 fi cat $i | sed -f $SED_SCRIPT > $i.debog cmp $i $i.debog >/dev/null if [ $? -ne 0 ] ; then mv $i.debog $i echo "Updating $i" else rm $i.debog fi done xorp/utils/gen-pathconf.sh.in0000664000076400007640000000504711421137511016334 0ustar greearbgreearb#!/bin/sh # @configure_input@ # # This is the input to a generator script. The script it generates # gen-pathconf.sh generates pathcons.sh which is intended for # inclusion by XORP test scripts to device path information. # # configure filters gen-pathconf.sh.in to produce gen-pathconf.sh # configure then runs gen-pathconf.sh to produce pathconf.sh # # This is a two stage process since not all versions of autoconf are able # to provide much path related information (esp. build directory). # # The following cut-line enclosed region of code is used in # gen-pathconf.sh and in pathconf.sh. # start-cutting - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # absolute_path # # Attempt to make a potentially relative path absolute. # absolute_path() { local odir odir=${PWD} cd -P ${1} echo ${PWD} cd ${odir} } # stop-cutting - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - this_dir=`dirname $0` # # Determine top-level build directory. We only have to do this because # early autoconf versions do not make this available. We assume we are in # ${XORP_BUILDDIR}/utils # xorp_top_builddir=`absolute_path ${this_dir}/..` cd ${this_dir} this_script=`basename $0` # # Top-level source dir is available on autoconf versions going back as far # as we might care (2.10), but it needs to be absolutized to be useful for # test scripts that may be run under ${xorp_top_builddir}. # xorp_top_srcdir=`absolute_path @top_srcdir@` # # The following are all available on autoconf versions we might care about. # xorp_prefix=@prefix@ xorp_exec_prefix=@exec_prefix@ xorp_bindir=@bindir@ xorp_sbindir=@sbindir@ xorp_sysconfdir=@sysconfdir@ cat > ./pathconf.sh <> ./pathconf.sh cat >> './pathconf.sh' <<'EOF' # # Assume this script is being sourced from builddir as it would be for the # automake 'check' target. # this_dir=`dirname $0` xorp_srcdir=`absolute_path ${this_dir}` xorp_builddir=`echo ${xorp_srcdir} | sed 's@${xorp_top_srcdir}@${xorp_top_builddir}@'` unset this_dir EOF xorp/utils/bogon-be-gone.sed0000664000076400007640000000510111421137511016124 0ustar greearbgreearb# # $XORP: xorp/utils/bogon-be-gone.sed,v 1.2 2002/12/19 01:13:17 hodson Exp $ # # # This is a script to coerce code into a more xorp coding style friendly # manner. It targets some of the common xorp developer bogons that # everyone makes and is not an indent replacement or a magic format # fixer. # # When you use this script check the diffs and run compilation and # tests before committing the changes back to the repository. You will # get eye strain looking at the diffs :-) # # Remove Trailing whitespaces from lines s/[ ]*$// # Ignore pre-processor directives /^#/ b # Ignore comments at top of file /^\/\// b # Dont process text within C comments /^\/\*/,/\*\/$/ b # Dont futz with enum assignments since aligned values are okay /enum.*{/,/};/ b # Dont futz with const assignments since aligned values are okay /const[^=]*=.*;/ b # Attempt to fix missing spaces around operators with parenthetical expressions, ie if (), for(), etc... # The list of operators is incomplete /^[^/]/ s/\([{(;]\{0,\}\)\([A-Za-z0-9_.()]\{1,\}\)[ ]*\([-=*&^!+<>]\{0,2\}=\)[ ]*\([A-Za-z0-9_.()]\{1,\}\)\([});]\)/\1\2 \3 \4\5/g /^[^/]/ s/\([{(;]\{1,\}\)\([A-Za-z0-9_.()]\{1,\}\)[ ]*\([<>]\)[ ]*\([A-Za-z0-9_.()]\{1,\}\)\([});]\)/\1\2 \3 \4\5/g # Fix things that look like assignments from start of line, eg "s+= x;" -> "s += x;", # The list of operators is incomplete s/^\([ ]*\)\([A-Za-z0-9_.()]\{1,\}\)[ ]*\([-=*&^!+<>]\{0,2\}=\)[ ]*/\1\2 \3 /g s/\([A-z0-9)]\)==\([A-z0-9]\)/\1 == \2/g # Put space between semi-colon/comman and the following character s/\([;,]\)\([-+_A-Za-z0-9]\)/\1 \2/g # Map "{a" -> "{ a" s/{\([A-Za-z_0-9]\)/{ \1/g # Map "a}" -> "a }" s/\([A-Za-z_0-9]\)}/\1 }/g # Map ";}" -> "; }" s/;}/; }/g # Map ";i" -> "; i" s/;\([A-Za-z_0-9]\)/; \1/g # Map "if(" -> "if (" s/\([ ]\{1,\}\)if(/\1if (/ # Map "switch(" -> "switch (" s/\([ ]\{1,\}\)switch(/\1switch (/ # Map "for(" -> "for (" s/\([ ]\{1,\}\)for(/\1for (/ # Map "while(" -> "while (" s/\([ ]\{1,\}\)while(/\1while (/ # Map "; };" -> "; }" s/;\([ ]\{1,\}\)}[ ]*;[ ]*$/;\1}/ # Put a space between a c-plus-plus comment and following text "//foo" -> "// foo" s/\([ ]*\/\/\)\([^ ]\)/\1 \2/ # Put a newline between name of function and starting bracket. Per style guide # and heated debate, ie # # int foo() { -> int foo() # .... { # .... .... # } } # # But be careful not to futz with opening brace after some common decls, eg # class, struct, etc /^struct/ b /^typedef/ b /^class/ b /^enum/ b /^extern/ b /^[A-Za-z].*{[ ]*$/ { s/[ ]*{[ ]*$// a\ { } xorp/win32_pkg.bash0000775000076400007640000000435111620706527014336 0ustar greearbgreearb#!/bin/bash # Script for building and packaging up a xorp windows binary # This needs to be run as root (or via sudo) to have a good # chance of working... # This works on modern Fedora, with these pkgs installed: #mingw32-pdcurses-3.4-8.fc15.noarch #mingw32-cpp-4.5.3-1.fc15.i686 #mingw32-binutils-2.21-1.fc15.i686 #mingw32-w32api-3.15-2.fc15.noarch #mingw32-openssl-1.0.0d-1.fc15.noarch #mingw32-libgnurx-2.5.1-7.fc15.noarch #mingw32-zlib-1.2.5-3.fc15.noarch #mingw32-gcc-c++-4.5.3-1.fc15.i686 #mingw32-gcc-4.5.3-1.fc15.i686 #mingw32-runtime-3.15.2-5.fc13.noarch #mingw32-pthreads-2.8.0-13.fc15.noarch #mingw32-filesystem-69-3.fc15.noarch # In addition, you need the fix listed in this bug: #http://lists.fedoraproject.org/pipermail/mingw/2011-February/003442.html # mingw cross-compile arguments SARGS="strip=yes shared=no build=mingw32 STRIP=i686-pc-mingw32-strip \ CC=i686-pc-mingw32-gcc CXX=i686-pc-mingw32-g++ \ RANLIB=i686-pc-mingw32-ranlib AR=i686-pc-mingw32-ar \ LD=i686-pc-mingw32-ld" JNUM=4 if [ "$1 " != " " ] then JNUM=$1 fi # Clean up any previous installed xorp code. rm -fr /usr/local/xorp # Build echo "Building..." scons -j$JNUM $SARGS || exit 1 if [ "$2 " == "b " ] then exit 0 fi echo "Installing..." scons $SARGS install || exit 2 echo "Copy some files..." # Copy some run-time libraries to the xorp dir for packaging cp /usr/i686-pc-mingw32/sys-root/mingw/bin/libcrypto-10.dll /usr/local/xorp/sbin/ || exit 3 cp /usr/i686-pc-mingw32/sys-root/mingw/bin/zlib1.dll /usr/local/xorp/sbin/ || exit 4 cp /usr/i686-pc-mingw32/sys-root/mingw/bin/libgcc_s_sjlj-1.dll /usr/local/xorp/sbin/ || exit 5 cp /usr/i686-pc-mingw32/sys-root/mingw/bin/libgnurx-0.dll /usr/local/xorp/sbin/ || exit 6 PWD=$(pwd) userdir=$(expr match "$PWD" '\(/home/[0-Z]*/\).*') if [ "_$userdir" == "_" ] then userdir = "./" fi cd /usr/local || exit 7 if [ ! -d ${userdir}tmp ] then echo "Creating directory: ${userdir}tmp to hold xorp package." mkdir -p ${userdir}tmp fi if zip -9 ${userdir}tmp/xorp_win32.zip -r xorp then echo "" echo "Created package: ${userdir}tmp/xorp_win32.zip" echo "" else echo "" echo "ERROR: There were errors trying to create: ${userdir}tmp/xorp_win32.zip" echo "" fi cd - xorp/SConstruct0000664000076400007640000010462511703345405013724 0ustar greearbgreearb#Copyright (c) 2009-2012 XORP, Inc and Others # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # $ID$ # TODO cross compiles. # TODO Fix default include/lib paths, pass in from environment. # TODO Merge scons-unfamiliar syntactic sugar from YHC's sconsfiles. import relpath gnutoolwarning = """ WARNING: The GNU %s was not detected on your system. Some combinations of linker or compiler flags, specific to building XORP, may not function correctly. """ # The XRL tgt-gen and clnt-gen scripts use Python 2.3+'s optparse # class. However, os.path.relpath() requires Python 2.6. EnsurePythonVersion(2, 3) Help(""" builddir= to specify a different build directory. Default is "obj/--". """) import sys import os import string import subprocess import fnmatch import SCons from SCons.Script.SConscript import SConsEnvironment import SCons.Action import SCons.Builder try: # SCons 0.98.4 is the earliest release that we have tested. Earlier # ones may work. If so, please submit a Trac issue so the check can # be relaxed. EnsureSConsVersion(0, 98, 4) except SystemExit: print "WARNING: Actually, SCONS version 0.98.4 or later is _preferred_." print "Attempting to continue with version: " + SCons.__version__ + " but it may not work properly.\n" vars = Variables() vars.AddVariables( BoolVariable('shared', 'Build with shared libraries', True), BoolVariable('strip', 'Strip all installed binaries', False), BoolVariable('rtld_origin', 'Use ORIGIN in dynamically linked programs', True), BoolVariable('ignore_check_errors', 'Ignore errors when building tests', False), BoolVariable('debug_stl', 'Build with checked STL operations', False), BoolVariable('debug_msg', 'Build with debug messages', False), BoolVariable('debug_fn', 'Build with function names in debug_msg output', False), BoolVariable('debug_cb', 'Build with callback debugging', False), BoolVariable('disable_ipv6', 'Force disable of IPv6', False), BoolVariable('disable_libtecla', 'Use some externally compiled libtecla instead.', False), BoolVariable('disable_fw', 'Disable Firewall feature', False), BoolVariable('disable_warninglogs', 'Force disable warning logs', False), BoolVariable('disable_tracelogs', 'Force disable trace logs', False), BoolVariable('disable_fatallogs', 'Force disable fatal logs', False), BoolVariable('disable_infologs', 'Force disable info logs', False), BoolVariable('disable_assertlogs', 'Force disable assert logs', False), BoolVariable('disable_errorlogs', 'Force disable error logs', False), BoolVariable('disable_otherlogs', 'Force disable other logs', False), BoolVariable('disable_profile', 'Disable Xorp Profiler feature', False), BoolVariable('enable_boost', 'Use BOOST', False), BoolVariable('enable_ustl', 'Use uSTL', False), BoolVariable('enable_bgp', 'Build BGP', True), BoolVariable('enable_buildinfo', 'Build Info, see libxorp/create_buildinfo.sh', True), BoolVariable('enable_olsr', 'Build OLSR', True), BoolVariable('enable_ospf', 'Build OSPF', True), BoolVariable('enable_rip', 'Build RIP', True), BoolVariable('enable_vrrp', 'Build VRRP', True), BoolVariable('enable_xorpsh', 'Build xorpsh', True), BoolVariable('enable_tests', 'Build Test Programs', False), BoolVariable('enable_click', 'Build CLICK support', False), BoolVariable('enable_fea_dummy', 'Build fea-dummy target', True), BoolVariable('enable_async_server', 'Permit asynchronous method implementations', False), BoolVariable('debug_xrldb', 'Build with runtime XRL syntax validation in Router Manager', False), EnumVariable('debug', 'Build with debug symbols', 'full', allowed_values=('no', 'yes', 'full', 'override'), map={}, ignorecase=2), EnumVariable('optimize', 'Build with optimization', 'full', allowed_values=('no', 'minimal', 'yes', 'full', 'highest', 'size', 'override'), map={}, ignorecase=2), EnumVariable('profile', 'Build with profiling', 'no', allowed_values=('no', 'gprof', 'pprof', 'override'), map={}, ignorecase=2), EnumVariable('transport', 'Set default XRL transport protocol', 'local', allowed_values=('tcp', 'local'), map={}, ignorecase=2), PathVariable('prefix', 'Install prefix', '/usr/local/xorp', PathVariable.PathAccept), ) def log_args(afname): f = open(afname, 'w') for a in ARGUMENTS.iteritems(): print >>f, "%s=%s" % a, print >>f f.close() def tgt_guess(): p = subprocess.Popen(['./config.guess'], stdout=subprocess.PIPE) o = p.communicate()[0] if p.returncode != 0: Exit(1) return o.strip() def tgt_canonicalize(alias): p = subprocess.Popen(['./config.sub', alias], stdout=subprocess.PIPE) o = p.communicate()[0] if p.returncode != 0: Exit(1) return o.strip() build_alias = ARGUMENTS.get('build', None) if build_alias == None: build_alias = tgt_guess() build = tgt_canonicalize(build_alias) (build_cpu, build_vendor, build_os) = build.split('-', 2) host_alias = ARGUMENTS.get('host', None) if host_alias == None: host_alias = build_alias host = tgt_canonicalize(host_alias) (host_cpu, host_vendor, host_os) = host.split('-', 2) sourcedir=Dir(".").abspath builddir=Dir(ARGUMENTS.get('builddir', '#obj/' + host)).abspath SConsignFile(builddir + '/.sconsign') try: Execute(Mkdir(builddir)) except: pass # Only save args when doing the default build process. if (len(COMMAND_LINE_TARGETS) == 0): log_args(builddir + '/.scons_build_args') # XXX TODO: Make initial CPPPATH/LIBPATH derive from # autodetected host system *or* command line. #env = Environment( # TOOLS = ['default', 'autotest', 'clntgen', 'tgtgen', # 'TOOL_SUBST'], # ENV = os.environ, # BUILDDIR = builddir, # CPPPATH=['/usr/local/include', '$BUILDDIR'], # LIBPATH=['usr/lib', '/usr/local/lib'], # variables = vars) env = Environment( TOOLS = ['default', 'autotest', 'clntgen', 'tgtgen', 'TOOL_SUBST'], ENV = os.environ, BUILDDIR = builddir, CPPPATH=['$BUILDDIR'], LIBPATH=['usr/lib'], variables = vars) prefix = env['prefix'] print 'Build System Type: ', build print 'Host System Type: ', host print 'Source path: ', sourcedir print 'Build path: ', builddir print 'Install prefix: ', prefix env['DESTDIR'] = ARGUMENTS.get('DESTDIR', '') # Provide mechanism to override CC, CXX, etc. if ARGUMENTS.get('CC', None): env['CC'] = ARGUMENTS.get('CC') print 'CC: ', env['CC'] if ARGUMENTS.get('CXX', None): env['CXX'] = ARGUMENTS.get('CXX') print 'CXX: ', env['CXX'] if ARGUMENTS.get('RANLIB', None): env['RANLIB'] = ARGUMENTS.get('RANLIB') print 'RANLIB: ', env['RANLIB'] if ARGUMENTS.get('AR', None): env['AR'] = ARGUMENTS.get('AR') print 'AR: ', env['AR'] if ARGUMENTS.get('LD', None): env['LD'] = ARGUMENTS.get('LD') print 'LD: ', env['LD'] env['STRIP'] = ARGUMENTS.get('STRIP', 'strip') print 'STRIP: ', env['STRIP'] if env['strip']: env['strip'] = True print 'Strip binaries: ', env.has_key('strip') # POSIX strip has no options by the strict letter of the law. # Assume we have GNU binutils strip until proven otherwise. gnustrip = True env['_STRIP_UNNEEDED'] = "" if gnustrip: env['_STRIP_UNNEEDED'] = "--strip-unneeded" # User can override this. Defaults to gcc's -O1, as this trims # most of the template fat. print 'Optimize code: ', env['optimize'] # Default to disable; wrapper for compiler profiling support. print 'Profile code: ', env['profile'] # Default to local (UNIX domain sockets). print 'Default XRL transport:', env['transport'] # Most of our shared library tweaks are specific to GNU ld. # Check if the GNU linker is available, and print a warning if not. if env['shared']: if SCons.Tool.FindTool(['gnulink'], env) is None: print gnutoolwarning % 'ld linker' env['SHAREDLIBS'] = "defined" print 'Shared libraries: ', env.has_key('SHAREDLIBS') print 'Use rtld ORIGIN: ', env['rtld_origin'] # AUTOTEST_SKIP_ERRORS is SetDefault() by site_scons/site_tools/autotest.py, # so print its value here. if env['ignore_check_errors']: env['AUTOTEST_SKIP_ERRORS'] = True print 'Ignore check errors: ', env['AUTOTEST_SKIP_ERRORS'] # NOTE: Enabling debug messages for the whole tree may not be what you want, # as it can lead to premature timeouts. # Enabling callback debugging is currently not advised as it does # inline printfs. print 'Debug symbols: ', env['debug'] print 'Debug STL: ', env['debug_stl'] print 'Debug messages: ', env['debug_msg'] print 'Debug function names: ', env['debug_fn'] print 'Debug callbacks: ', env['debug_cb'] print 'Debug XRL syntax: ', env['debug_xrldb'] print 'Enable OLSR: ', env['enable_olsr'] print 'Enable OSPF: ', env['enable_ospf'] print 'Enable RIP: ', env['enable_rip'] print 'Enable VRRP: ', env['enable_vrrp'] print 'Enable xorpsh ', env['enable_xorpsh'] print 'Enable Test Programs: ', env['enable_tests'] print 'Enable CLICK: ', env['enable_click'] print 'Enable FEA Dummy: ', env['enable_fea_dummy'] print 'Enable async method impls: ', env['enable_async_server'] print 'Enable BGP: ', env['enable_bgp'] print 'Enable BuildInfo: ', env['enable_buildinfo'] print 'Try Enable BOOST: ', env['enable_boost'] print 'Try Enable uSTL : ', env['enable_ustl'] print 'Disable IPv6: ', env['disable_ipv6'] print 'Disable libtecla: ', env['disable_libtecla'] print 'Disable Firewall: ', env['disable_fw'] print 'Disable Profile : ', env['disable_profile'] print 'Disable warning logs : ', env['disable_warninglogs'] print 'Disable error logs : ', env['disable_errorlogs'] print 'Disable trace logs : ', env['disable_tracelogs'] print 'Disable fatal logs : ', env['disable_fatallogs'] print 'Disable info logs : ', env['disable_infologs'] print 'Disable assert logs : ', env['disable_assertlogs'] print 'Disable other logs : ', env['disable_otherlogs'] env['CONFIGURELOG'] = str(builddir) + os.sep + 'config.log' env['CONFIGUREDIR'] = str(builddir) + os.sep + '.sconf_temp' SConsEnvironment.Chmod = SCons.Action.ActionFactory(os.chmod, lambda dest, mode: 'Chmod("%s", 0%o)' % (dest, mode)) def Symlink(src, link_name): try: os.unlink(link_name) except OSError: pass os.symlink(src, link_name) SConsEnvironment.Symlink = SCons.Action.ActionFactory(Symlink, lambda src, link_name: 'Symlink("%s", as "%s")' % (src, link_name)) def InstallProgram(env, dest, files, perm = 0755): if env.has_key('DESTDIR'): # NB: use simple concatenation dest = env.Dir(env['DESTDIR'] + env.Dir(dest).path) obj = env.Install(dest, files) for i in obj: env.AddPostAction(i, env.Chmod(str(i), perm)) if env['strip']: env.AddPostAction(i, Action("$STRIP $TARGET")) return obj SConsEnvironment.InstallProgram = InstallProgram def InstallScript(env, dest, files, perm = 0555): if env.has_key('DESTDIR'): # NB: use simple concatenation dest = env.Dir(env['DESTDIR'] + env.Dir(dest).path) obj = env.Install(dest, files) for i in obj: env.AddPostAction(i, env.Chmod(str(i), perm)) return obj SConsEnvironment.InstallScript = InstallScript def InstallLibrary(env, dest, files, perm = 0644): if env.has_key('DESTDIR'): # NB: use simple concatenation dest = env.Dir(env['DESTDIR'] + env.Dir(dest).path) obj = env.Install(dest, files) for i in obj: env.AddPostAction(i, env.Chmod(str(i), perm)) if env['strip']: env.AddPostAction(i, Action("$STRIP $_STRIP_UNNEEDED $TARGET")) return obj SConsEnvironment.InstallLibrary = InstallLibrary def InstallData(env, dest, files, perm = 0644): if env.has_key('DESTDIR'): # NB: use simple concatenation dest = env.Dir(env['DESTDIR'] + env.Dir(dest).path) obj = env.Install(dest, files) for i in obj: env.AddPostAction(i, env.Chmod(str(i), perm)) return obj SConsEnvironment.InstallData = InstallData # # GNU-style package paths. # # These are not parsed using PathVariable() above, to avoid confusion # on the part of the user, as their default values are derived # from 'prefix'. # Not all of them are used at the moment. # env['exec_prefix'] = ARGUMENTS.get('exec_prefix', '$prefix') env['sbindir'] = ARGUMENTS.get('sbindir', '$exec_prefix/sbin') env['libexecdir'] = ARGUMENTS.get('libexecdir', '$exec_prefix/lib') env['datarootdir'] = ARGUMENTS.get('datarootdir', '$prefix/share') env['datadir'] = ARGUMENTS.get('datadir', '$datarootdir/xorp') env['sysconfdir'] = ARGUMENTS.get('sysconfdir', '$prefix/etc') env['localstatedir'] = ARGUMENTS.get('localstatedir', '$prefix/var') env['libdir'] = ARGUMENTS.get('libdir', '$exec_prefix/lib') env['mandir'] = ARGUMENTS.get('mandir', '$datarootdir/man') # # XORP internal package paths. # # These are the paths actually used to build the package image # when installed in its destination directory. # Most of these paths are derived from the GNU-style variables above. # # The image layout is intended to be FHS 2.3 compliant, for the benefit # of 3rd party packagers and distributors. # env['xorp_rootdir'] = env['exec_prefix'] # used to determine RPATH env['xorp_confdir'] = env['sysconfdir'] # path to xorp.conf env['xorp_libdir'] = env['libdir'] + '/xorp/lib' env['xorp_moduledir'] = env['libdir'] + '/xorp/sbin' # Protocol modules env['xorp_sbindir'] = env['sbindir'] # End-user binaries env['xorp_templatedir'] = env['datadir'] + '/templates' env['xorp_tooldir'] = env['libdir'] + '/xorp/bin' # tools/* env['xorp_xrlsdir'] = env['datadir'] + '/xrl/targets' # *.xrls env['xorp_sourcedir'] = sourcedir # rtrmgr/util.cc and xif need this tst = ARGUMENTS.get('enable_boost', False) if tst and not (tst == "no"): env['enable_boost'] = True else: env['enable_boost'] = False tst = ARGUMENTS.get('enable_ustl', False) if tst and not (tst == "no"): env['enable_ustl'] = True else: env['enable_ustl'] = False tst = ARGUMENTS.get('enable_tests', False) if tst and not (tst == "no"): env['enable_tests'] = True else: env['enable_tests'] = False tst = ARGUMENTS.get('enable_click', False) if tst and not (tst == "no"): env['enable_click'] = True else: env['enable_click'] = False # Default to enabled tst = ARGUMENTS.get('enable_fea_dummy', True) if tst and (tst == "no"): env['enable_fea_dummy'] = False else: env['enable_fea_dummy'] = True # Default to disabled tst = ARGUMENTS.get('enable_async_server', False) if tst and (tst == "no"): env['enable_async_server'] = False else: env['enable_async_server'] = True # Default to enabled tst = ARGUMENTS.get('enable_bgp', True) if tst and (tst == "no"): env['enable_bgp'] = False else: env['enable_bgp'] = True # Default to enabled tst = ARGUMENTS.get('enable_buildinfo', True) if tst and (tst == "no"): env['enable_buildinfo'] = False else: env['enable_buildinfo'] = True # Only generate build-info when we're building # the normal build. if (len(COMMAND_LINE_TARGETS) == 0): if not env.GetOption('clean') and \ not env.GetOption('help'): if os.system("./libxorp/create_buildinfo.sh") != 0: print "ERROR: Failed to generate libxorp/build_info.cc." print "You can disable this feature with: enable_buildinfo=no" exit(1) if env.GetOption('clean'): os.unlink('./libxorp/build_info.cc'); # Default to enabled tst = ARGUMENTS.get('enable_olsr', True) if tst and (tst == "no"): env['enable_olsr'] = False else: env['enable_olsr'] = True # Default to enabled tst = ARGUMENTS.get('enable_ospf', True) if tst and (tst == "no"): env['enable_ospf'] = False else: env['enable_ospf'] = True # Default to enabled tst = ARGUMENTS.get('enable_rip', True) if tst and (tst == "no"): env['enable_rip'] = False else: env['enable_rip'] = True # Default to enabled tst = ARGUMENTS.get('enable_vrrp', True) if tst and (tst == "no"): env['enable_vrrp'] = False else: env['enable_vrrp'] = True # Default to enabled tst = ARGUMENTS.get('enable_xorpsh', True) if tst and (tst == "no"): env['enable_xorpsh'] = False else: env['enable_xorpsh'] = True tst = ARGUMENTS.get('disable_ipv6', False) if tst and not (tst == "no"): env['disable_ipv6'] = True else: env['disable_ipv6'] = False tst = ARGUMENTS.get('disable_fw', False) if tst and not (tst == "no"): env['disable_fw'] = True else: env['disable_fw'] = False tst = ARGUMENTS.get('disable_libtecla', False) if tst and not (tst == "no"): env['disable_libtecla'] = True else: env['disable_libtecla'] = False tst = ARGUMENTS.get('disable_profile', False) if tst and not (tst == "no"): env['disable_profile'] = True else: env['disable_profile'] = False tst = ARGUMENTS.get('disable_warninglogs', False) if tst and not (tst == "no"): env['disable_warninglogs'] = True else: env['disable_warninglogs'] = False tst = ARGUMENTS.get('disable_errorlogs', False) if tst and not (tst == "no"): env['disable_errorlogs'] = True else: env['disable_errorlogs'] = False tst = ARGUMENTS.get('disable_tracelogs', False) if tst and not (tst == "no"): env['disable_tracelogs'] = True else: env['disable_tracelogs'] = False tst = ARGUMENTS.get('disable_fatallogs', False) if tst and not (tst == "no"): env['disable_fatallogs'] = True else: env['disable_fatallogs'] = False tst = ARGUMENTS.get('disable_infologs', False) if tst and not (tst == "no"): env['disable_infologs'] = True else: env['disable_infologs'] = False tst = ARGUMENTS.get('disable_assertlogs', False) if tst and not (tst == "no"): env['disable_assertlogs'] = True else: env['disable_assertlogs'] = False tst = ARGUMENTS.get('disable_otherlogs', False) if tst and not (tst == "no"): env['disable_otherlogs'] = True else: env['disable_otherlogs'] = False ########## start configure magic if env.has_key('LIBS'): pre_LIBS = env['LIBS'] else: pre_LIBS = [] if not env.GetOption('clean') and \ not env.GetOption('help'): env.AppendUnique( CFLAGS = Split(ARGUMENTS.get('CFLAGS', '')) ) env.AppendUnique( CXXFLAGS = Split(ARGUMENTS.get('CXXFLAGS', '')) ) env.AppendUnique( LINKFLAGS = Split(ARGUMENTS.get('LINKFLAGS', '')) ) env['MISSING_DEPS'] = [] env['SKIPPED_DEPS'] = [] from config.endian import CheckEndianness from config.member import CheckTypeMember from config.sysctl import CheckSysctl from config.boost import CheckBoost, CheckBoostLibrary my_custom_tests = { 'CheckBoost' : CheckBoost, 'CheckBoostLibrary' : CheckBoostLibrary, 'CheckEndianness' : CheckEndianness, 'CheckTypeMember' : CheckTypeMember, 'CheckSysctl' : CheckSysctl } conf = env.Configure(config_h = str(builddir) + '/xorp_config.h', log_file = str(builddir) + '/config.log', custom_tests = my_custom_tests) ########## # target os # friendly name of target osname = host_os if fnmatch.fnmatch(host_os, 'freebsd*'): osname = "FreeBSD" conf.Define('HOST_OS_FREEBSD') conf.Define('ENABLE_ADVANCED_MULTICAST_API') elif fnmatch.fnmatch(host_os, 'mingw32*'): osname = "Windows" conf.Define('HOST_OS_WINDOWS') env['mingw'] = True env['PROGSUFFIX'] = '.exe' elif fnmatch.fnmatch(host_os, 'linux*'): osname = "Linux" conf.Define('HOST_OS_LINUX') conf.Define('HAVE_PROC_LINUX') conf.Define('ENABLE_ADVANCED_MULTICAST_API') elif fnmatch.fnmatch(host_os, 'netbsd*'): osname = "NetBSD" conf.Define('HOST_OS_NETBSD') conf.Define('ENABLE_ADVANCED_MULTICAST_API') elif fnmatch.fnmatch(host_os, 'sunos*'): osname = "Solaris" conf.Define('HOST_OS_SOLARIS') else: osname = sys.platform.upper() conf.Define('HOST_OS_NAME', '"' + osname + '"') # toolchain conf.Define('CPP_SUPPORTS_C99_VA_ARGS') conf.Define('CPP_SUPPORTS_GNU_VA_ARGS') if env['disable_fw']: conf.Define("XORP_DISABLE_FIREWALL") if env['disable_profile']: conf.Define("XORP_DISABLE_PROFILE") if env['enable_ustl']: conf.Define("XORP_USE_USTL") if env['enable_boost']: # Boost #env['boost_suffix'] = "-mt" # Not for FreeBSD if not conf.CheckBoost(require_version='1.34'): print "Boost version check for 1.34 failed, disabling boost support.\n" env['enable_boost'] = False #conf.CheckBoostLibrary('system') #conf.CheckBoostLibrary('date_time') #conf.CheckBoostLibrary('iostreams') # Additional Boost libraries #conf.CheckBoostLibrary('filesystem') #conf.CheckBoostLibrary('program_options') #conf.CheckBoostLibrary('regex') #if env['enable_boost'] and not conf.CheckBoostLibrary('regex'): # print "Boost regex library check failed, disabling boost support.\n" # env['enable_boost'] = False #conf.CheckBoostLibrary('signals') #conf.CheckBoostLibrary('thread') if env['enable_boost']: conf.Define("USE_BOOST") # I tried checking this with conf.CheckHeader, but it always returns false, # so just over-ride it here and assume it exists. conf.Define('HAS_BOOST_NONCOPYABLE_INC') # Big ball of mud. from config.allconfig import DoAllConfig DoAllConfig(env, conf, host_os) tst = ARGUMENTS.get('enable_click', False) if tst and not (tst == "no"): conf.Define('XORP_USE_CLICK') tst = ARGUMENTS.get('enable_fea_dummy', True) if tst and not (tst == "no"): conf.Define('XORP_USE_FEA_DUMMY') tst = ARGUMENTS.get('enable_async_server', False) if tst and not (tst == "no"): conf.Define('XORP_ENABLE_ASYNC_SERVER') if env['enable_xorpsh']: conf.Define('XORP_USE_XORPSH') # Note: hard-coded paths pushed down to rtrmgr/util.cc environment. # Print warnings about missing/skipped dependencies. if env['MISSING_DEPS']: print '\nRequired dependencies not found, exiting:\n - %s' % '\n - '.join(env['MISSING_DEPS']) if env['SKIPPED_DEPS']: print 'Optional dependencies not found:\n - %s' % '\n - '.join(env['SKIPPED_DEPS']) Exit(1) if env['SKIPPED_DEPS']: print 'Optional dependencies not found:\n - %s' % '\n - '.join(env['SKIPPED_DEPS']) newenv = conf.Finish() # # Configure will put all libraries in global link line, during checks. # This is not what we want, as it means all binaries get these LIBS. # We should be able to check, for libraries, at SCons level, # by looking in conf.havedict, but that isn't exported. # # It seems we'd be best off defining a class of our own, containing # things we want to see, and passing that in and out of the # config tests when we split up allconfig.py. # For now, let's assume that libraries are present, and link against # them on a piecemeal basis. # post_LIBS = env['LIBS'] env['LIBS'] = pre_LIBS print 'Detected libraries:', for x in post_LIBS: print x, print ########## end configure magic if SCons.Tool.FindTool(['gcc'], env) is None or \ SCons.Tool.FindTool(['g++'], env) is None: print gnutoolwarning % 'gcc or g++ compiler' # If the user didn't override our default optimization, then # sanitize user's CFLAGS/CXXFLAGS to not contain optimization options, # and map to an appropriate GCC flag. if not env['optimize'] == 'override': env.Replace( CFLAGS = filter(lambda s: not s.startswith('-O'), Split(env['CFLAGS']))) env.Replace( CXXFLAGS = filter(lambda s: not s.startswith('-O'), Split(env['CXXFLAGS']))) bigodict = { 'no': '-O0', # 'minimal' denotes only those optimizations # necessary to force gcc to perform the tree_sink # pass, to elide most STL template instantiations. 'minimal': "-O1 -fno-defer-pop -fno-delayed-branch \ -fno-guess-branch-probability -fno-cprop-registers -fno-if-conversion \ -fno-if-conversion2 -fno-tree-ccp -fno-tree-dce -fno-tree-dominator-opts \ -fno-tree-dse -fno-tree-ter -fno-tree-lrs -fno-tree-sra \ -fno-tree-copyrename -fno-tree-fre -fno-tree-ch -fno-unit-at-a-time \ -fno-merge-constants", 'yes': '-O1', 'full': '-O2', 'highest': '-O3', 'size': '-Os' } bigoflag = bigodict[env['optimize']] env.AppendUnique(CFLAGS = [ bigoflag ]) env.AppendUnique(CXXFLAGS = [ bigoflag ]) # At least on Fedora 13, -Os breaks build with strict-aliasing # warnings. Code looks right to me, so going to just disable # strict aliasing. --Ben if env['optimize'] == 'size': env.AppendUnique(CFLAGS = [ '-fno-strict-aliasing' ]) env.AppendUnique(CXXFLAGS = [ '-fno-strict-aliasing' ]) # Do the same for the flags which control debug symbols. if not env['debug'] == 'override': env.Replace( CFLAGS = filter(lambda s: not s.startswith('-g'), Split(env['CFLAGS']))) env.Replace( CXXFLAGS = filter(lambda s: not s.startswith('-g'), Split(env['CXXFLAGS']))) gdict = { 'no': '', 'yes': '-g', 'full': '-g3' } gflag = gdict[env['debug']] if not env['debug'] == 'no': env.AppendUnique(CFLAGS = [ gflag ]) env.AppendUnique(CXXFLAGS = [ gflag ]) # Do the same for the flags which control code profiling. if not env['profile'] == 'override': if env['profile'] == 'gprof' and env.has_key('SHAREDLIBS'): print """ WARNING: You have requested GNU gprof style profiling and shared libraries. This is UNSUPPORTED, and probably will not link. """ strip_pg_flags = [ '-pg', '-finstrument-functions', '-fno-omit-frame-pointer', '-fno-optimize-sibling-calls' ] try: env.Replace( CFLAGS = filter( lambda s: not s.startswith(tuple(strip_pg_flags)), Split(env['CFLAGS'])) ) env.Replace( CXXFLAGS = filter( lambda s: not s.startswith(tuple(strip_pg_flags)), Split(env['CXXFLAGS'])) ) except TypeError: print """ WARNING: Your version of scons and/or python has a syntax issue with this code. It cannot strip out the gprof related flags. If your compile fails, please try disabling gprof profiling and/or shared libraries. If the compile works, then you can ignore this warning. """ # Full use of profiling may require more than one flag, so Split() them. pgdict = {'no': '', 'gprof': '-pg', 'pprof': '', } pgflag = pgdict[env['profile']] if not env['profile'] == 'no': pgflags = pgflag + ' -fno-omit-frame-pointer -fno-optimize-sibling-calls' env.AppendUnique( CFLAGS = Split(pgflags)) env.AppendUnique( CXXFLAGS = Split(pgflags) ) # Using gprof requires we link with the '-pg' option. if env['profile'] == 'gprof': env.AppendUnique( LINKFLAGS = Split(pgflag) ) # Using pprof requires we link with the '-lprofiler' library # from the Google Performance Tools package. To avoid having # to insert calls to the profiler, use the environment # variable CPUPROFILE when running the program. # If you do insert calls, please bracket them with WITH_PPROF. if env['profile'] == 'pprof': env.AppendUnique( LIBS = [ 'profiler' ] ) #env.AppendUnique( CPPDEFINES = [ 'WITH_PPROF' ] ) if env['enable_ustl']: env.AppendUnique( LIBS = [ 'ustl' ] ) # # Apply top-level 'transport' protocol option, which controls the # default transport protocol chosen by libxipc. # # We pass it as an ordinal character constant to avoid shell # quoting issues, as it is specified by 1 character. # # Note: We need to do this at top level to make sure the option # is propagated correctly, because anything which includes # will be affected by this option. # if fnmatch.fnmatch(host_os, 'mingw32*'): # Windows only supports tcp. env.AppendUnique(CPPDEFINES = [ ( 'XRL_PF', ord('t')), ]) else: xrl_pf_dict = { 'tcp': 't', 'local': 'x' } env.AppendUnique(CPPDEFINES = [ ( 'XRL_PF', ord(xrl_pf_dict[env['transport']]) ), ]) # Read our VERSION file and use it's contents to pass the # version into the compile process. vf = open('VERSION', 'r') ver = vf.readline() ver = ver.strip() env.AppendUnique(CPPDEFINES = [ ( 'XORP_VERSION', ver), ]) # Forcibly disable GCC's SSP support., as it may be incompatible # with the XORP code base. #env.AppendUnique(CPPDEFINES = [ # ( '_FORTIFY_SOURCE', 0 ), # ]) if env['enable_boost']: # Forcibly disable Boost's thread support; the XORP code base is # not yet threaded. env.AppendUnique(CPPDEFINES = [ ( 'BOOST_DISABLE_THREADS' ), ]) # Some platforms have alignment warnings that cannot easily be # fixed, so we can't enable Werror for them. if ((build != "i386-pc-mingw32") and (host_cpu == "i686" or host_cpu == "i386" or host_cpu == "x86_64")): env.AppendUnique(CFLAGS = [ '-Werror', ]) env.AppendUnique(CXXFLAGS = [ '-Werror', ]) else: print "WARNING: Detected funky platform, will not enable -Werror compile option: ", host_cpu # NOTE: gcc specific flags. env.AppendUnique(CFLAGS = [ '-W', '-Wall', '-Wwrite-strings', '-Wbad-function-cast', '-Wmissing-prototypes', '-Wcast-qual', '-Wmissing-declarations', '-Wpointer-arith', '-Wcast-align', '-Wstrict-prototypes', '-Wnested-externs', '-pipe', ]) env.AppendUnique(CXXFLAGS = [ '-W', '-Wall', '-Wwrite-strings', '-Wcast-qual', '-Wpointer-arith', '-Wcast-align', '-Woverloaded-virtual', '-ftemplate-depth-25', '-pipe', ]) # Colors screw up display when showing xemacs (my fav editor), so # disable them here if we are using clang. if env['CC'] and env['CC'] == 'clang': env.AppendUnique(CFLAGS = [ '-fno-color-diagnostics', ]) env.AppendUnique(CXXFLAGS = [ '-fno-color-diagnostics', ]) if env['enable_buildinfo']: env.AppendUnique(CXXFLAGS = [ '-DXORP_BUILDINFO', ]); # NOTE: For GNU STL only at this time. if env['debug_stl']: env.AppendUnique(CXXFLAGS = [ '-D_GLIBCXX_DEBUG', '-D_GLIBCXX_DEBUG_PEDANTIC', ]) # NOTE: DEBUG_LOGGING_GLOBAL currently a no-op. if env['debug_msg']: env.AppendUnique(CFLAGS = [ '-DDEBUG_LOGGING', ]) env.AppendUnique(CXXFLAGS = [ '-DDEBUG_LOGGING', ]) if env['debug_fn']: env.AppendUnique(CFLAGS = [ '-DDEBUG_PRINT_FUNCTION_NAME', ]) env.AppendUnique(CXXFLAGS = [ '-DDEBUG_PRINT_FUNCTION_NAME', ]) if env['debug_cb']: env.AppendUnique(CFLAGS = [ '-DDEBUG_CALLBACKS', ]) env.AppendUnique(CXXFLAGS = [ '-DDEBUG_CALLBACKS', ]) # # Fixup shared library paths. # Shared libraries are installed in $xorp_libdir by default. # # If rtld_origin is True, the linker will be passed a relative RPATH. # This allows the package itself to be relocated in the filesystem # as a whole. This also allows us to run binaries before we ship them. # # Go ahead and define some variables here so that SConscript files are not # so repetitive when dealing with RPATH, etc. # Since we are building static, they should be ignored anyway. env['xorp_sbin_rpath'] = env['xorp_libdir'] env['xorp_tool_rpath'] = env['xorp_libdir'] env['xorp_module_rpath'] = env['xorp_libdir'] if env.has_key('SHAREDLIBS'): if env['rtld_origin']: # # Build a subdirectory for holding symlinks to libraries upfront, so # that binaries about to be built can be run from inside the BUILDDIR. # # XXX Assumes that $libdir is below $exec_prefix. If the # user changes this, results are undefined. # # $BUILDIR/lib will contain .so symlinks # xorp_alias_libdir = os.path.join(builddir, 'lib') # XXX workaround Mkdir() failure on EEXIST, SCons < 20090223. try: Execute(Mkdir(xorp_alias_libdir)) except: pass env['xorp_alias_libdir'] = xorp_alias_libdir # # Build a further alias for the benefit of entities which # will be later installed in $xorp_sbindir. # # $BUILDIR/lib/xorp/lib will point to $BUILDIR/lib # xorp_alias_subdir = os.path.join(xorp_alias_libdir, 'xorp') # # XXX workaround Mkdir() failure on EEXIST, SCons < 20090223. xorp_module_alias_libdir = os.path.join(xorp_alias_subdir, 'lib') try: Execute(Mkdir(xorp_alias_subdir)) except: pass Execute(env.Symlink(xorp_alias_libdir, xorp_module_alias_libdir)) env['xorp_module_alias_libdir'] = xorp_module_alias_libdir if not fnmatch.fnmatch(host_os, 'mingw32*'): # Tell rtld to turn on $ORIGIN processing by default. # NOTE: GNU ld specific flag. env.PrependUnique( LINKFLAGS = [ '-Wl,-z,origin' ] ) # Set relative RPATH for each kind of installed XORP component. env['xorp_sbin_rpath'] = os.path.join('\\$$ORIGIN', os.path.relpath(env.Dir('$xorp_libdir').abspath, env.Dir('$xorp_sbindir').abspath)) env['xorp_tool_rpath'] = os.path.join('\\$$ORIGIN', os.path.relpath(env.Dir('$xorp_libdir').abspath, env.Dir('$xorp_tooldir').abspath)) env['xorp_module_rpath'] = os.path.join('\\$$ORIGIN', os.path.relpath(env.Dir('$xorp_libdir').abspath, env.Dir('$xorp_moduledir').abspath)) if not fnmatch.fnmatch(host_os, 'mingw32*'): # Export global symbols as dynamic in executables for runtime backtraces # w/o GDB or corefiles in production deployments. # NOTE: GNU ld specific flag. env.AppendUnique(LINKFLAGS = [ '-rdynamic', ]) env.SConscript(['SConscript'], variant_dir='$BUILDDIR', exports='env', duplicate=0) env.Help(vars.GenerateHelpText(env)) xorp/fea/0000775000076400007640000000000011703345405012415 5ustar greearbgreearbxorp/fea/firewall_transaction.cc0000664000076400007640000000304211421137511017127 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2008-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "firewall_transaction.hh" void FirewallTransactionManager::pre_commit(uint32_t tid) { reset_error(); _tid_exec = tid; } void FirewallTransactionManager::operation_result(bool success, const TransactionOperation& op) { if (success) return; const FirewallTransactionOperation* fto; fto = dynamic_cast(&op); XLOG_ASSERT(fto != NULL); if (_first_error.empty()) { _first_error = c_format("Failed executing: \"%s\": %s", fto->str().c_str(), fto->error_reason().c_str()); flush(_tid_exec); } } xorp/fea/ifconfig_set.hh0000664000076400007640000003004311540224224015367 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/ifconfig_set.hh,v 1.63 2008/10/02 21:56:47 bms Exp $ #ifndef __FEA_IFCONFIG_SET_HH__ #define __FEA_IFCONFIG_SET_HH__ #include "iftree.hh" #include "fea_data_plane_manager.hh" class IfConfig; class IPvX; class IfConfigSet { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ IfConfigSet(FeaDataPlaneManager& fea_data_plane_manager) : _is_running(false), _ifconfig(fea_data_plane_manager.ifconfig()), _fea_data_plane_manager(fea_data_plane_manager) {} /** * Virtual destructor. */ virtual ~IfConfigSet() {} /** * Get the @ref IfConfig instance. * * @return the @ref IfConfig instance. */ IfConfig& ifconfig() { return _ifconfig; } /** * Get the @ref FeaDataPlaneManager instance. * * @return the @ref FeaDataPlaneManager instance. */ FeaDataPlaneManager& fea_data_plane_manager() { return _fea_data_plane_manager; } /** * Test whether this instance is running. * * @return true if the instance is running, otherwise false. */ virtual bool is_running() const { return _is_running; } /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg) = 0; /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg) = 0; /** * Push the network interface configuration into the underlying system. * * Note that on return some of the interface tree configuration state * may be modified. * * @param iftree the interface tree configuration to push. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int push_config(const IfTree& iftree); protected: /** * Determine if the interface's underlying provider implements discard * semantics natively, or if they are emulated through other means. * * @param i the interface item to inspect. * @return true if discard semantics are emulated. */ virtual bool is_discard_emulated(const IfTreeInterface& i) const = 0; /** * Determine if the interface's underlying provider implements unreachable * semantics natively, or if they are emulated through other means. * * @param i the interface item to inspect. * @return true if unreachable semantics are emulated. */ virtual bool is_unreachable_emulated(const IfTreeInterface& i) const = 0; /** * Start the configuration. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int config_begin(string& error_msg) = 0; /** * Complete the configuration. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int config_end(string& error_msg) = 0; /** * Begin the interface configuration. * * @param pulled_ifp pointer to the interface information pulled from * the system. * @param config_iface reference to the interface with the information * to configure. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int config_interface_begin(const IfTreeInterface* pulled_ifp, IfTreeInterface& config_iface, string& error_msg) = 0; /** * End the interface configuration. * * @param pulled_ifp pointer to the interface information pulled from * the system. * @param config_iface reference to the interface with the information * to configure. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int config_interface_end(const IfTreeInterface* pulled_ifp, const IfTreeInterface& config_iface, string& error_msg) = 0; /** * Begin the vif configuration. * * @param pulled_ifp pointer to the interface information pulled from * the system. * @param pulled_vifp pointer to the vif information pulled from * the system. * @param config_iface reference to the interface with the information * to configure. * @param config_vif reference to the vif with the information * to configure. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int config_vif_begin(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeInterface& config_iface, const IfTreeVif& config_vif, string& error_msg) = 0; /** * End the vif configuration. * * @param pulled_ifp pointer to the interface information pulled from * the system. * @param pulled_vifp pointer to the vif information pulled from * the system. * @param config_iface reference to the interface with the information * to configure. * @param config_vif reference to the vif with the information * to configure. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int config_vif_end(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeInterface& config_iface, const IfTreeVif& config_vif, string& error_msg) = 0; /** * Add IPv4 address information. * * If an entry for the same address already exists, is is overwritten * with the new information. * * @param pulled_ifp pointer to the interface information pulled from * the system. * @param pulled_vifp pointer to the vif information pulled from * the system. * @param pulled_addrp pointer to the address information pulled from * the system. * @param config_iface reference to the interface with the information * to configure. * @param config_vif reference to the vif with the information * to configure. * @param config_addr reference to the address with the information * to configure. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int config_add_address(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeAddr4* pulled_addrp, const IfTreeInterface& config_iface, const IfTreeVif& config_vif, const IfTreeAddr4& config_addr, string& error_msg) = 0; /** * Delete IPv4 address information. * * @param pulled_ifp pointer to the interface information pulled from * the system. * @param pulled_vifp pointer to the vif information pulled from * the system. * @param pulled_addrp pointer to the address information pulled from * the system. * @param config_iface reference to the interface with the information * to configure. * @param config_vif reference to the vif with the information * to configure. * @param config_addr reference to the address with the information * to configure. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int config_delete_address(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeAddr4* pulled_addrp, const IfTreeInterface& config_iface, const IfTreeVif& config_vif, const IfTreeAddr4& config_addr, string& error_msg) = 0; /** * Add IPv6 address information. * * If an entry for the same address already exists, is is overwritten * with the new information. * * @param pulled_ifp pointer to the interface information pulled from * the system. * @param pulled_vifp pointer to the vif information pulled from * the system. * @param pulled_addrp pointer to the address information pulled from * the system. * @param config_iface reference to the interface with the information * to configure. * @param config_vif reference to the vif with the information * to configure. * @param config_addr reference to the address with the information * to configure. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int config_add_address(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeAddr6* pulled_addrp, const IfTreeInterface& config_iface, const IfTreeVif& config_vif, const IfTreeAddr6& config_addr, string& error_msg) = 0; /** * Delete IPv6 address information. * * @param pulled_ifp pointer to the interface information pulled from * the system. * @param pulled_vifp pointer to the vif information pulled from * the system. * @param pulled_addrp pointer to the address information pulled from * the system. * @param config_iface reference to the interface with the information * to configure. * @param config_vif reference to the vif with the information * to configure. * @param config_addr reference to the address with the information * to configure. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int config_delete_address(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeAddr6* pulled_addrp, const IfTreeInterface& config_iface, const IfTreeVif& config_vif, const IfTreeAddr6& config_addr, string& error_msg) = 0; protected: // Misc other state bool _is_running; private: void push_iftree_begin(const IfTree& iftree); void push_iftree_end(const IfTree& iftree); void push_interface_begin(const IfTreeInterface* pulled_ifp, IfTreeInterface& config_iface); void push_interface_end(const IfTreeInterface* pulled_ifp, IfTreeInterface& config_iface); void push_if_creation(const IfTreeInterface* system_ifp, IfTreeInterface& config_iface); void push_vif_begin(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, IfTreeInterface& config_iface, IfTreeVif& config_vif); void push_vif_end(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, IfTreeInterface& config_iface, IfTreeVif& config_vif); void push_vif_address(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeAddr4* pulled_addrp, IfTreeInterface& config_iface, IfTreeVif& config_vif, IfTreeAddr4& config_addr); void push_vif_address(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeAddr6* pulled_addrp, IfTreeInterface& config_iface, IfTreeVif& config_vif, IfTreeAddr6& config_addr); IfConfig& _ifconfig; FeaDataPlaneManager& _fea_data_plane_manager; }; #endif // __FEA_IFCONFIG_SET_HH__ xorp/fea/nexthop_port_mapper.cc0000664000076400007640000001725011421137511017020 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipv4.hh" #include "libxorp/ipv6.hh" #include "libxorp/ipv4net.hh" #include "libxorp/ipv6net.hh" #include "nexthop_port_mapper.hh" NexthopPortMapper::NexthopPortMapper() { } NexthopPortMapper::~NexthopPortMapper() { } void NexthopPortMapper::clear() { // // Clear all maps // _interface_map.clear(); _ipv4_map.clear(); _ipv6_map.clear(); _ipv4net_map.clear(); _ipv6net_map.clear(); } int NexthopPortMapper::add_observer(NexthopPortMapperObserver* observer) { if (find(_observers.begin(), _observers.end(), observer) != _observers.end()) { return (XORP_ERROR); } _observers.push_back(observer); return (XORP_OK); } int NexthopPortMapper::delete_observer(NexthopPortMapperObserver* observer) { list::iterator iter; iter = find(_observers.begin(), _observers.end(), observer); if (iter == _observers.end()) { return (XORP_ERROR); } _observers.erase(iter); return (XORP_OK); } int NexthopPortMapper::lookup_nexthop_interface(const string& ifname, const string& vifname) const { if (ifname.empty() && vifname.empty()) return (-1); map, int>::const_iterator iter; iter = _interface_map.find(make_pair(ifname, vifname)); if (iter == _interface_map.end()) return (-1); // No such entry return (iter->second); } int NexthopPortMapper::lookup_nexthop_ipv4(const IPv4& ipv4) const { // // Check first the map with IPv4 addresses // map::const_iterator ipv4_iter; ipv4_iter = _ipv4_map.find(ipv4); if (ipv4_iter != _ipv4_map.end()) return (ipv4_iter->second); // // Check the map with IPv4 subnets // map::const_iterator ipv4net_iter; for (ipv4net_iter = _ipv4net_map.begin(); ipv4net_iter != _ipv4net_map.end(); ++ipv4net_iter) { const IPv4Net& ipv4net = ipv4net_iter->first; if (ipv4net.contains(ipv4)) return (ipv4net_iter->second); } return (-1); // Nothing found } int NexthopPortMapper::lookup_nexthop_ipv6(const IPv6& ipv6) const { // // Check first the map with IPv6 addresses // map::const_iterator ipv6_iter; ipv6_iter = _ipv6_map.find(ipv6); if (ipv6_iter != _ipv6_map.end()) return (ipv6_iter->second); // // Check the map with IPv6 subnets // map::const_iterator ipv6net_iter; for (ipv6net_iter = _ipv6net_map.begin(); ipv6net_iter != _ipv6net_map.end(); ++ipv6net_iter) { const IPv6Net& ipv6net = ipv6net_iter->first; if (ipv6net.contains(ipv6)) return (ipv6net_iter->second); } return (-1); // Nothing found } int NexthopPortMapper::add_interface(const string& ifname, const string& vifname, int port) { if (ifname.empty() && vifname.empty()) return (XORP_ERROR); map, int>::iterator iter; iter = _interface_map.find(make_pair(ifname, vifname)); if (iter != _interface_map.end()) { // Update the port iter->second = port; } else { // Add a new entry _interface_map.insert(make_pair(make_pair(ifname, vifname), port)); } return (XORP_OK); } int NexthopPortMapper::delete_interface(const string& ifname, const string& vifname) { if (ifname.empty() && vifname.empty()) return (XORP_ERROR); map, int>::iterator iter; iter = _interface_map.find(make_pair(ifname, vifname)); if (iter == _interface_map.end()) return (XORP_ERROR); // No such entry _interface_map.erase(iter); return (XORP_OK); } int NexthopPortMapper::add_ipv4(const IPv4& ipv4, int port) { map::iterator iter; iter = _ipv4_map.find(ipv4); if (iter != _ipv4_map.end()) { // Update the port iter->second = port; } else { // Add a new entry _ipv4_map.insert(make_pair(ipv4, port)); } return (XORP_OK); } int NexthopPortMapper::delete_ipv4(const IPv4& ipv4) { map::iterator iter; iter = _ipv4_map.find(ipv4); if (iter == _ipv4_map.end()) return (XORP_ERROR); // No such entry _ipv4_map.erase(iter); return (XORP_OK); } int NexthopPortMapper::add_ipv6(const IPv6& ipv6, int port) { map::iterator iter; iter = _ipv6_map.find(ipv6); if (iter != _ipv6_map.end()) { // Update the port iter->second = port; } else { // Add a new entry _ipv6_map.insert(make_pair(ipv6, port)); } return (XORP_OK); } int NexthopPortMapper::delete_ipv6(const IPv6& ipv6) { map::iterator iter; iter = _ipv6_map.find(ipv6); if (iter == _ipv6_map.end()) return (XORP_ERROR); // No such entry _ipv6_map.erase(iter); return (XORP_OK); } int NexthopPortMapper::add_ipv4net(const IPv4Net& ipv4net, int port) { map::iterator iter; iter = _ipv4net_map.find(ipv4net); if (iter != _ipv4net_map.end()) { // Update the port iter->second = port; } else { // Add a new entry _ipv4net_map.insert(make_pair(ipv4net, port)); } return (XORP_OK); } int NexthopPortMapper::delete_ipv4net(const IPv4Net& ipv4net) { map::iterator iter; iter = _ipv4net_map.find(ipv4net); if (iter == _ipv4net_map.end()) return (XORP_ERROR); // No such entry _ipv4net_map.erase(iter); return (XORP_OK); } int NexthopPortMapper::add_ipv6net(const IPv6Net& ipv6net, int port) { map::iterator iter; iter = _ipv6net_map.find(ipv6net); if (iter != _ipv6net_map.end()) { // Update the port iter->second = port; } else { // Add a new entry _ipv6net_map.insert(make_pair(ipv6net, port)); } return (XORP_OK); } int NexthopPortMapper::delete_ipv6net(const IPv6Net& ipv6net) { map::iterator iter; iter = _ipv6net_map.find(ipv6net); if (iter == _ipv6net_map.end()) return (XORP_ERROR); // No such entry _ipv6net_map.erase(iter); return (XORP_OK); } void NexthopPortMapper::notify_observers() { list::iterator iter; bool changed = is_mapping_changed(); for (iter = _observers.begin(); iter != _observers.end(); ++iter) { NexthopPortMapperObserver* observer = *iter; observer->nexthop_port_mapper_event(changed); } if (changed) { // Save a copy of the maps _old_interface_map = _interface_map; _old_ipv4_map = _ipv4_map; _old_ipv6_map = _ipv6_map; _old_ipv4net_map = _ipv4net_map; _old_ipv6net_map = _ipv6net_map; } } bool NexthopPortMapper::is_mapping_changed() const { if (_interface_map != _old_interface_map) return (true); if (_ipv4_map != _old_ipv4_map) return (true); if (_ipv6_map != _old_ipv6_map) return (true); if (_ipv4net_map != _old_ipv4net_map) return (true); if (_ipv6net_map != _old_ipv6net_map) return (true); return (false); } xorp/fea/0002-bsd-mcast-Properly-detect-support-for-IPv6-multicast.patch0000644000076400007640000001042411642440066026035 0ustar greearbgreearbFrom 49898d42e0cf8b14629e556f1528a7cb62d2c303 Mon Sep 17 00:00:00 2001 From: Ben Greear Date: Tue, 4 Oct 2011 00:56:38 +0200 Subject: [PATCH 2/2] bsd/mcast: Properly detect support for IPv6 multicast routing. Need to conditionally include if_vars.h in the config checks, as netbsd doesn't have that file. Signed-off-by: Ben Greear --- xorp/site_scons/config/allconfig.py | 26 +++++++++++++++++++++----- 1 files changed, 21 insertions(+), 5 deletions(-) diff --git a/xorp/site_scons/config/allconfig.py b/xorp/site_scons/config/allconfig.py index 02abb12..ceff18f 100644 --- a/xorp/site_scons/config/allconfig.py +++ b/xorp/site_scons/config/allconfig.py @@ -589,14 +589,20 @@ def DoAllConfig(env, conf, host_os): if host_os == 'sunos': prereq_netinet_ip_mroute_h = ['sys/types.h', 'sys/ioctl.h', 'sys/socket.h', 'sys/time.h', 'inet/ip.h', 'netinet/in.h'] else: - prereq_netinet_ip_mroute_h = ['sys/types.h', 'sys/ioctl.h', 'sys/socket.h', 'sys/time.h', 'net/if.h', 'net/if_var.h', 'net/route.h', 'netinet/in.h'] + prereq_netinet_ip_mroute_h = ['sys/types.h', 'sys/ioctl.h', 'sys/socket.h', 'sys/time.h', 'net/if.h', 'net/route.h', 'netinet/in.h'] + if has_net_if_var_h: + prereq_netinet_ip_mroute_h.append('net/if_var.h') + netinet_ip_mroute_h = 'netinet/ip_mroute.h' has_netinet_ip_mroute_h = conf.CheckHeader(prereq_netinet_ip_mroute_h + [ netinet_ip_mroute_h ]) if has_netinet_ip_mroute_h: prereq_mroute_h = prereq_netinet_ip_mroute_h mroute_h = netinet_ip_mroute_h - prereq_net_ip_mroute_ip_mroute_h = ['sys/types.h', 'sys/ioctl.h', 'sys/socket.h', 'sys/time.h', 'net/if.h', 'net/if_var.h', 'net/route.h', 'netinet/in.h'] + prereq_net_ip_mroute_ip_mroute_h = ['sys/types.h', 'sys/ioctl.h', 'sys/socket.h', 'sys/time.h', 'net/if.h', 'net/route.h', 'netinet/in.h'] + if has_net_if_var_h: + prereq_net_ip_mroute_ip_mroute_h.append('net/if_var.h') + net_ip_mroute_ip_mroute_h = 'net/ip_mroute/ip_mroute.h' has_net_ip_mroute_ip_mroute_h = conf.CheckHeader(prereq_net_ip_mroute_ip_mroute_h + [ net_ip_mroute_ip_mroute_h ]) if has_net_ip_mroute_ip_mroute_h: @@ -668,7 +674,9 @@ def DoAllConfig(env, conf, host_os): mroute6_h = None # bsd - prereq_netinet6_ip6_mroute_h = ['sys/param.h', 'sys/socket.h', 'sys/time.h', 'net/if.h', 'net/if_var.h', 'net/route.h', 'netinet/in.h'] + prereq_netinet6_ip6_mroute_h = ['sys/param.h', 'sys/socket.h', 'sys/time.h', 'net/if.h', 'net/route.h', 'netinet/in.h'] + if has_net_if_var_h: + prereq_netinet6_ip6_mroute_h.append('net/if_var.h') netinet6_ip6_mroute_h = 'netinet6/ip6_mroute.h' has_netinet6_ip6_mroute_h = conf.CheckHeader(prereq_netinet6_ip6_mroute_h + [ netinet6_ip6_mroute_h ]) if has_netinet6_ip6_mroute_h: @@ -717,8 +725,16 @@ def DoAllConfig(env, conf, host_os): # packet filters has_netinet_ip_compat_h = conf.CheckHeader(['sys/types.h', 'netinet/ip_compat.h']) has_netinet_ip_fil_h = conf.CheckHeader(['sys/types.h', 'sys/ioctl.h', 'sys/socket.h', 'netinet/in.h', 'netinet/in_systm.h', 'netinet/ip.h', 'netinet/ip_compat.h', 'netinet/ip_fil.h']) - has_netinet_ip_fw_h = conf.CheckHeader(['sys/types.h', 'sys/ioctl.h', 'sys/socket.h', 'net/if.h', 'netinet/in.h', 'net/if_var.h', 'netinet/ip_fw.h']) - has_net_pfvar_h = conf.CheckHeader(['sys/param.h', 'sys/file.h', 'sys/ioctl.h', 'sys/socket.h', 'net/if.h', 'netinet/in.h', 'net/if_var.h', 'net/pfvar.h']) + + prereq_ip_fw_h = ['sys/types.h', 'sys/ioctl.h', 'sys/socket.h', 'net/if.h', 'netinet/in.h'] + if has_net_if_var_h: + prereq_ip_fw_h.append('net/if_var.h') + has_netinet_ip_fw_h = conf.CheckHeader(prereq_ip_fw_h + ['netinet/ip_fw.h']) + + prereq_net_pfvar_h = ['sys/param.h', 'sys/file.h', 'sys/ioctl.h', 'sys/socket.h', 'net/if.h', 'netinet/in.h'] + if has_net_if_var_h: + prereq_net_pfvar_h.append('net/if_var.h') + has_net_pfvar_h = conf.CheckHeader(prereq_net_pfvar_h + ['net/pfvar.h']) # Older linux kernel headers for netfilter wouldn't compile with C++ w/out hacking on the headers themselves. has_linux_netfilter_ipv4_ip_tables_h = conf.CheckHeader(['sys/param.h', 'net/if.h', 'netinet/in.h', 'linux/netfilter_ipv4/ip_tables.h'], language = "C++") -- 1.7.6.4 xorp/fea/xrl_fib_client_manager.cc0000664000076400007640000003177411703345405017415 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2012 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "xrl_fib_client_manager.hh" /** * 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 XrlFibClientManager::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); } } XrlCmdError XrlFibClientManager::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 (_fibconfig.get_table4(fte_list) != XORP_OK) { static const string error_msg("Cannot get the IPv4 FIB"); return XrlCmdError::COMMAND_FAILED(error_msg); } fib_client.activate(fte_list); return XrlCmdError::OKAY(); } XrlCmdError XrlFibClientManager::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(); } int XrlFibClientManager::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, &XrlFibClientManager::send_fib_client_add_route4_cb, target_name)); if (success) return XORP_OK; else return XORP_ERROR; } int XrlFibClientManager::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, &XrlFibClientManager::send_fib_client_delete_route4_cb, target_name)); if (success) return XORP_OK; else return XORP_ERROR; } int XrlFibClientManager::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, &XrlFibClientManager::send_fib_client_resolve_route4_cb, target_name)); if (success) return XORP_OK; else return XORP_ERROR; } void XrlFibClientManager::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 XrlFibClientManager::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 XrlFibClientManager::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); } template void XrlFibClientManager::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 XrlFibClientManager::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 = _xfcm->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 = _xfcm->send_fib_client_add_route(_target_name, fte); } else { // Send notification of a route being deleted success = _xfcm->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, &XrlFibClientManager::FibClient::send_fib_client_route_change)); } } template void XrlFibClientManager::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, &XrlFibClientManager::FibClient::send_fib_client_route_change)); } template class XrlFibClientManager::FibClient; #ifdef HAVE_IPV6 /** * 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 XrlFibClientManager::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 XrlFibClientManager::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 (_fibconfig.get_table6(fte_list) != XORP_OK) { string error_msg = "Cannot get the IPv6 FIB"; return XrlCmdError::COMMAND_FAILED(error_msg); } fib_client.activate(fte_list); return XrlCmdError::OKAY(); } XrlCmdError XrlFibClientManager::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 XrlFibClientManager::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, &XrlFibClientManager::send_fib_client_add_route6_cb, target_name)); if (success) return XORP_OK; else return XORP_ERROR; } int XrlFibClientManager::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, &XrlFibClientManager::send_fib_client_delete_route6_cb, target_name)); if (success) return XORP_OK; else return XORP_ERROR; } int XrlFibClientManager::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, &XrlFibClientManager::send_fib_client_resolve_route6_cb, target_name)); if (success) return XORP_OK; else return XORP_ERROR; } void XrlFibClientManager::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 XrlFibClientManager::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 XrlFibClientManager::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 class XrlFibClientManager::FibClient; #endif xorp/fea/io_tcpudp_manager.cc0000664000076400007640000014522311540225525016412 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2007-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxipc/xuid.hh" // XXX: Needed for the purpose of using XUID() #include "fea_node.hh" #include "iftree.hh" #include "io_tcpudp_manager.hh" /* ------------------------------------------------------------------------- */ /* IoTcpUdpComm methods */ IoTcpUdpComm::IoTcpUdpComm(IoTcpUdpManager& io_tcpudp_manager, const IfTree& iftree, int family, bool is_tcp, const string& creator) : IoTcpUdpReceiver(), _io_tcpudp_manager(io_tcpudp_manager), _iftree(iftree), _family(family), _is_tcp(is_tcp), _creator(creator), // XXX: Reuse the XRL implementation for generating an unique ID _sockid(XUID().str()), _peer_host(IPvX::ZERO(family)), _peer_port(0) { } IoTcpUdpComm::IoTcpUdpComm(IoTcpUdpManager& io_tcpudp_manager, const IfTree& iftree, int family, bool is_tcp, const string& creator, const string& listener_sockid, const IPvX& peer_host, uint16_t peer_port) : IoTcpUdpReceiver(), _io_tcpudp_manager(io_tcpudp_manager), _iftree(iftree), _family(family), _is_tcp(is_tcp), _creator(creator), // XXX: Reuse the XRL implementation for generating an unique ID _sockid(XUID().str()), _listener_sockid(listener_sockid), _peer_host(peer_host), _peer_port(peer_port) { } IoTcpUdpComm::~IoTcpUdpComm() { deallocate_io_tcpudp_plugins(); } int IoTcpUdpComm::tcp_open(string& sockid, string& error_msg) { int ret_value = XORP_OK; string error_msg2; if (_io_tcpudp_plugins.empty()) { error_msg = c_format("No I/O TCP/UDP plugin to open TCP socket"); return (XORP_ERROR); } IoTcpUdpPlugins::iterator iter; for (iter = _io_tcpudp_plugins.begin(); iter != _io_tcpudp_plugins.end(); ++iter) { IoTcpUdp* io_tcpudp = iter->second; if (io_tcpudp->tcp_open(error_msg2) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } } if (ret_value == XORP_OK) sockid = _sockid; return (ret_value); } int IoTcpUdpComm::udp_open(string& sockid, string& error_msg) { int ret_value = XORP_OK; string error_msg2; if (_io_tcpudp_plugins.empty()) { error_msg = c_format("No I/O TCP/UDP plugin to open UDP socket"); return (XORP_ERROR); } IoTcpUdpPlugins::iterator iter; for (iter = _io_tcpudp_plugins.begin(); iter != _io_tcpudp_plugins.end(); ++iter) { IoTcpUdp* io_tcpudp = iter->second; if (io_tcpudp->udp_open(error_msg2) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } } if (ret_value == XORP_OK) sockid = _sockid; return (ret_value); } int IoTcpUdpComm::tcp_open_and_bind(const IPvX& local_addr, uint16_t local_port, string& sockid, string& error_msg) { int ret_value = XORP_OK; string error_msg2; if (_io_tcpudp_plugins.empty()) { error_msg = c_format("No I/O TCP/UDP plugin to open and bind " "TCP socket with address %s and port %u", local_addr.str().c_str(), local_port); return (XORP_ERROR); } IoTcpUdpPlugins::iterator iter; for (iter = _io_tcpudp_plugins.begin(); iter != _io_tcpudp_plugins.end(); ++iter) { IoTcpUdp* io_tcpudp = iter->second; if (io_tcpudp->tcp_open_and_bind(local_addr, local_port, error_msg2) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } } if (ret_value == XORP_OK) sockid = _sockid; return (ret_value); } int IoTcpUdpComm::udp_open_and_bind(const IPvX& local_addr, uint16_t local_port, const string& local_dev, int reuse, string& sockid, string& error_msg) { int ret_value = XORP_OK; string error_msg2; if (_io_tcpudp_plugins.empty()) { error_msg = c_format("No I/O TCP/UDP plugin to open and bind " "UDP socket with address %s and port %u", local_addr.str().c_str(), local_port); return (XORP_ERROR); } IoTcpUdpPlugins::iterator iter; for (iter = _io_tcpudp_plugins.begin(); iter != _io_tcpudp_plugins.end(); ++iter) { IoTcpUdp* io_tcpudp = iter->second; if (io_tcpudp->udp_open_and_bind(local_addr, local_port, local_dev, reuse, error_msg2) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } } if (ret_value == XORP_OK) sockid = _sockid; return (ret_value); } int IoTcpUdpComm::udp_open_bind_join(const IPvX& local_addr, uint16_t local_port, const IPvX& mcast_addr, uint8_t ttl, bool reuse, string& sockid, string& error_msg) { int ret_value = XORP_OK; string error_msg2; if (_io_tcpudp_plugins.empty()) { error_msg = c_format("No I/O TCP/UDP plugin to open, bind and join " "UDP socket with address %s and port %u " "with group %s", local_addr.str().c_str(), local_port, mcast_addr.str().c_str()); return (XORP_ERROR); } // // Add the record for the joined multicast group // JoinedGroupsTable::iterator joined_iter; JoinedMulticastGroup init_jmg(local_addr, mcast_addr); joined_iter = _joined_groups_table.find(init_jmg); if (joined_iter == _joined_groups_table.end()) { // // First receiver, hence join the multicast group first to check // for errors. // IoTcpUdpPlugins::iterator plugin_iter; for (plugin_iter = _io_tcpudp_plugins.begin(); plugin_iter != _io_tcpudp_plugins.end(); ++plugin_iter) { IoTcpUdp* io_tcpudp = plugin_iter->second; if (io_tcpudp->udp_open_bind_join(local_addr, local_port, mcast_addr, ttl, reuse, error_msg2) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } } _joined_groups_table.insert(make_pair(init_jmg, init_jmg)); joined_iter = _joined_groups_table.find(init_jmg); } XLOG_ASSERT(joined_iter != _joined_groups_table.end()); // // TODO: If we add support for multiple receivers per socket, then // enable the following code to add a new receiver name. // // JoinedMulticastGroup& jmg = joined_iter->second; // jmg.add_receiver(receiver_name); if (ret_value == XORP_OK) sockid = _sockid; return (ret_value); } int IoTcpUdpComm::tcp_open_bind_connect(const IPvX& local_addr, uint16_t local_port, const IPvX& remote_addr, uint16_t remote_port, string& sockid, string& error_msg) { int ret_value = XORP_OK; string error_msg2; if (_io_tcpudp_plugins.empty()) { error_msg = c_format("No I/O TCP/UDP plugin to open, bind and connect " "TCP socket with address %s and port %u " "with remote address %s and port %u", local_addr.str().c_str(), local_port, remote_addr.str().c_str(), remote_port); return (XORP_ERROR); } IoTcpUdpPlugins::iterator iter; for (iter = _io_tcpudp_plugins.begin(); iter != _io_tcpudp_plugins.end(); ++iter) { IoTcpUdp* io_tcpudp = iter->second; if (io_tcpudp->tcp_open_bind_connect(local_addr, local_port, remote_addr, remote_port, error_msg2) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } } if (ret_value == XORP_OK) sockid = _sockid; return (ret_value); } int IoTcpUdpComm::udp_open_bind_connect(const IPvX& local_addr, uint16_t local_port, const IPvX& remote_addr, uint16_t remote_port, string& sockid, string& error_msg) { int ret_value = XORP_OK; string error_msg2; if (_io_tcpudp_plugins.empty()) { error_msg = c_format("No I/O TCP/UDP plugin to open, bind and connect " "UDP socket with address %s and port %u " "with remote address %s and port %u", local_addr.str().c_str(), local_port, remote_addr.str().c_str(), remote_port); return (XORP_ERROR); } IoTcpUdpPlugins::iterator iter; for (iter = _io_tcpudp_plugins.begin(); iter != _io_tcpudp_plugins.end(); ++iter) { IoTcpUdp* io_tcpudp = iter->second; if (io_tcpudp->udp_open_bind_connect(local_addr, local_port, remote_addr, remote_port, error_msg2) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } } if (ret_value == XORP_OK) sockid = _sockid; return (ret_value); } int IoTcpUdpComm::udp_open_bind_broadcast(const string& ifname, const string& vifname, uint16_t local_port, uint16_t remote_port, bool reuse, bool limited, bool connected, string& sockid, string& error_msg) { int ret_value = XORP_OK; string error_msg2; if (_io_tcpudp_plugins.empty()) { error_msg = c_format("No I/O TCP/UDP plugin to open, bind and " "connect UDP broadcast socket on if/vif %s/%s" "with local port %u and remote port %u", ifname.c_str(), vifname.c_str(), local_port, remote_port); return (XORP_ERROR); } IoTcpUdpPlugins::iterator iter; for (iter = _io_tcpudp_plugins.begin(); iter != _io_tcpudp_plugins.end(); ++iter) { IoTcpUdp* io_tcpudp = iter->second; if (io_tcpudp->udp_open_bind_broadcast(ifname, vifname, local_port, remote_port, reuse, limited, connected, error_msg) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } } if (ret_value == XORP_OK) sockid = _sockid; return (ret_value); } int IoTcpUdpComm::bind(const IPvX& local_addr, uint16_t local_port, string& error_msg) { int ret_value = XORP_OK; string error_msg2; if (_io_tcpudp_plugins.empty()) { error_msg = c_format("No I/O TCP/UDP plugin to bind " "socket with address %s and port %u", local_addr.str().c_str(), local_port); return (XORP_ERROR); } IoTcpUdpPlugins::iterator iter; for (iter = _io_tcpudp_plugins.begin(); iter != _io_tcpudp_plugins.end(); ++iter) { IoTcpUdp* io_tcpudp = iter->second; if (io_tcpudp->bind(local_addr, local_port, error_msg2) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } } return (ret_value); } int IoTcpUdpComm::udp_join_group(const IPvX& mcast_addr, const IPvX& join_if_addr, string& error_msg) { int ret_value = XORP_OK; string error_msg2; if (_io_tcpudp_plugins.empty()) { error_msg = c_format("No I/O TCP/UDP plugin to join " "UDP socket on group %s and interface address %s", mcast_addr.str().c_str(), join_if_addr.str().c_str()); return (XORP_ERROR); } // // Add the record for the joined multicast group // JoinedGroupsTable::iterator joined_iter; JoinedMulticastGroup init_jmg(join_if_addr, mcast_addr); joined_iter = _joined_groups_table.find(init_jmg); if (joined_iter == _joined_groups_table.end()) { // // First receiver, hence join the multicast group first to check // for errors. // IoTcpUdpPlugins::iterator plugin_iter; for (plugin_iter = _io_tcpudp_plugins.begin(); plugin_iter != _io_tcpudp_plugins.end(); ++plugin_iter) { IoTcpUdp* io_tcpudp = plugin_iter->second; if (io_tcpudp->udp_join_group(mcast_addr, join_if_addr, error_msg2) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } } _joined_groups_table.insert(make_pair(init_jmg, init_jmg)); joined_iter = _joined_groups_table.find(init_jmg); } XLOG_ASSERT(joined_iter != _joined_groups_table.end()); // // TODO: If we add support for multiple receivers per socket, then // enable the following code to add a new receiver name. // // JoinedMulticastGroup& jmg = joined_iter->second; // jmg.add_receiver(receiver_name); return (ret_value); } int IoTcpUdpComm::udp_leave_group(const IPvX& mcast_addr, const IPvX& leave_if_addr, string& error_msg) { int ret_value = XORP_OK; string error_msg2; if (_io_tcpudp_plugins.empty()) { error_msg = c_format("No I/O TCP/UDP plugin to leave " "UDP socket on group %s and interface address %s", mcast_addr.str().c_str(), leave_if_addr.str().c_str()); return (XORP_ERROR); } // // Delete the record for the joined multicast group // JoinedGroupsTable::iterator joined_iter; JoinedMulticastGroup init_jmg(leave_if_addr, mcast_addr); joined_iter = _joined_groups_table.find(init_jmg); if (joined_iter == _joined_groups_table.end()) { error_msg = c_format("Cannot leave group %s on interface address %s: " "the group was not joined", mcast_addr.str().c_str(), leave_if_addr.str().c_str()); XLOG_WARNING("%s", error_msg.c_str()); // Don't fail this..would fail the whole commit, and the group isn't joined // anyway, so no loss of validity in the configuration. return XORP_OK; } JoinedMulticastGroup& jmg = joined_iter->second; // // TODO: If we add support for multiple receivers per socket, then // enable the following code to delete the receiver name. // // jmg.delete_receiver(receiver_name); if (jmg.empty()) { // // The last receiver, hence leave the group // _joined_groups_table.erase(joined_iter); IoTcpUdpPlugins::iterator plugin_iter; for (plugin_iter = _io_tcpudp_plugins.begin(); plugin_iter != _io_tcpudp_plugins.end(); ++plugin_iter) { IoTcpUdp* io_tcpudp = plugin_iter->second; if (io_tcpudp->udp_leave_group(mcast_addr, leave_if_addr, error_msg2) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } } } return (ret_value); } int IoTcpUdpComm::close(string& error_msg) { int ret_value = XORP_OK; string error_msg2; if (_io_tcpudp_plugins.empty()) { error_msg = c_format("No I/O TCP/UDP plugin to close socket"); return (XORP_ERROR); } // // XXX: We assume that closing a socket automatically leaves all // previously joined multicast groups on that socket, therefore we // just clear the set of joined multicast groups. // _joined_groups_table.clear(); IoTcpUdpPlugins::iterator iter; for (iter = _io_tcpudp_plugins.begin(); iter != _io_tcpudp_plugins.end(); ++iter) { IoTcpUdp* io_tcpudp = iter->second; if (io_tcpudp->close(error_msg2) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } } return (ret_value); } int IoTcpUdpComm::tcp_listen(uint32_t backlog, string& error_msg) { int ret_value = XORP_OK; string error_msg2; if (_io_tcpudp_plugins.empty()) { error_msg = c_format("No I/O TCP/UDP plugin to listen to TCP socket"); return (XORP_ERROR); } IoTcpUdpPlugins::iterator iter; for (iter = _io_tcpudp_plugins.begin(); iter != _io_tcpudp_plugins.end(); ++iter) { IoTcpUdp* io_tcpudp = iter->second; if (io_tcpudp->tcp_listen(backlog, error_msg2) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } } return (ret_value); } int IoTcpUdpComm::udp_enable_recv(string& error_msg) { int ret_value = XORP_OK; string error_msg2; if (_io_tcpudp_plugins.empty()) { error_msg = c_format("No I/O TCP/UDP plugin to enable reception on" "UDP socket"); return (XORP_ERROR); } IoTcpUdpPlugins::iterator iter; for (iter = _io_tcpudp_plugins.begin(); iter != _io_tcpudp_plugins.end(); ++iter) { IoTcpUdp* io_tcpudp = iter->second; if (io_tcpudp->udp_enable_recv(error_msg2) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } } return (ret_value); } int IoTcpUdpComm::send(const vector& data, string& error_msg) { int ret_value = XORP_OK; string error_msg2; if (_io_tcpudp_plugins.empty()) { error_msg = c_format("No I/O TCP/UDP plugin to send data on socket"); return (XORP_ERROR); } IoTcpUdpPlugins::iterator iter; for (iter = _io_tcpudp_plugins.begin(); iter != _io_tcpudp_plugins.end(); ++iter) { IoTcpUdp* io_tcpudp = iter->second; if (io_tcpudp->send(data, error_msg2) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } } return (ret_value); } int IoTcpUdpComm::send_to(const IPvX& remote_addr, uint16_t remote_port, const vector& data, string& error_msg) { int ret_value = XORP_OK; string error_msg2; if (_io_tcpudp_plugins.empty()) { error_msg = c_format("No I/O TCP/UDP plugin to send data on " "socket to remote address %s and port %u", remote_addr.str().c_str(), remote_port); return (XORP_ERROR); } IoTcpUdpPlugins::iterator iter; for (iter = _io_tcpudp_plugins.begin(); iter != _io_tcpudp_plugins.end(); ++iter) { IoTcpUdp* io_tcpudp = iter->second; if (io_tcpudp->send_to(remote_addr, remote_port, data, error_msg2) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } } return (ret_value); } int IoTcpUdpComm::send_from_multicast_if(const IPvX& group_addr, uint16_t group_port, const IPvX& ifaddr, const vector& data, string& error_msg) { int ret_value = XORP_OK; string error_msg2; if (_io_tcpudp_plugins.empty()) { error_msg = c_format("No I/O TCP/UDP plugin to send data from " "multicast interface with address %s on " "socket to group %s and port %u from ", ifaddr.str().c_str(), group_addr.str().c_str(), group_port); return (XORP_ERROR); } IoTcpUdpPlugins::iterator iter; for (iter = _io_tcpudp_plugins.begin(); iter != _io_tcpudp_plugins.end(); ++iter) { IoTcpUdp* io_tcpudp = iter->second; if (io_tcpudp->send_from_multicast_if(group_addr, group_port, ifaddr, data, error_msg2) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } } return (ret_value); } int IoTcpUdpComm::set_socket_option(const string& optname, uint32_t optval, string& error_msg) { int ret_value = XORP_OK; string error_msg2; if (_io_tcpudp_plugins.empty()) { error_msg = c_format("No I/O TCP/UDP plugin to set %s socket option", optname.c_str()); return (XORP_ERROR); } IoTcpUdpPlugins::iterator iter; for (iter = _io_tcpudp_plugins.begin(); iter != _io_tcpudp_plugins.end(); ++iter) { IoTcpUdp* io_tcpudp = iter->second; if (io_tcpudp->set_socket_option(optname, optval, error_msg2) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } } return (ret_value); } int IoTcpUdpComm::set_socket_option(const string& optname, const string& optval, string& error_msg) { int ret_value = XORP_OK; string error_msg2; if (_io_tcpudp_plugins.empty()) { error_msg = c_format("No I/O TCP/UDP plugin to set %s socket option", optname.c_str()); return (XORP_ERROR); } IoTcpUdpPlugins::iterator iter; for (iter = _io_tcpudp_plugins.begin(); iter != _io_tcpudp_plugins.end(); ++iter) { IoTcpUdp* io_tcpudp = iter->second; if (io_tcpudp->set_socket_option(optname, optval, error_msg2) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } } return (ret_value); } int IoTcpUdpComm::accept_connection(bool is_accepted, string& error_msg) { int ret_value = XORP_OK; string error_msg2; if (_io_tcpudp_plugins.empty()) { error_msg = c_format("No I/O TCP/UDP plugin to %s a connection", (is_accepted)? "accept" : "reject"); return (XORP_ERROR); } IoTcpUdpPlugins::iterator iter; for (iter = _io_tcpudp_plugins.begin(); iter != _io_tcpudp_plugins.end(); ++iter) { IoTcpUdp* io_tcpudp = iter->second; if (io_tcpudp->accept_connection(is_accepted, error_msg2) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } } return (ret_value); } void IoTcpUdpComm::recv_event(const string& if_name, const string& vif_name, const IPvX& src_host, uint16_t src_port, const vector& data) { _io_tcpudp_manager.recv_event(_creator, sockid(), if_name, vif_name, src_host, src_port, data); } void IoTcpUdpComm::inbound_connect_event(const IPvX& src_host, uint16_t src_port, IoTcpUdp* new_io_tcpudp) { IoTcpUdpComm* new_io_tcpudp_comm; new_io_tcpudp_comm = _io_tcpudp_manager.connect_io_tcpudp_comm(_family, _is_tcp, _creator, sockid(), src_host, src_port, new_io_tcpudp); _io_tcpudp_manager.inbound_connect_event(_creator, sockid(), src_host, src_port, new_io_tcpudp_comm->sockid()); } void IoTcpUdpComm::outgoing_connect_event() { _io_tcpudp_manager.outgoing_connect_event(_family, _creator, sockid()); } void IoTcpUdpComm::error_event(const string& error, bool fatal) { _io_tcpudp_manager.error_event(_family, _creator, sockid(), error, fatal); } void IoTcpUdpComm::disconnect_event() { _io_tcpudp_manager.disconnect_event(_family, _creator, sockid()); } void IoTcpUdpComm::allocate_io_tcpudp_plugins() { list::iterator iter; for (iter = _io_tcpudp_manager.fea_data_plane_managers().begin(); iter != _io_tcpudp_manager.fea_data_plane_managers().end(); ++iter) { FeaDataPlaneManager* fea_data_plane_manager = *iter; allocate_io_tcpudp_plugin(fea_data_plane_manager); } } void IoTcpUdpComm::deallocate_io_tcpudp_plugins() { while (! _io_tcpudp_plugins.empty()) { IoTcpUdpPlugins::iterator iter = _io_tcpudp_plugins.begin(); FeaDataPlaneManager* fea_data_plane_manager = iter->first; deallocate_io_tcpudp_plugin(fea_data_plane_manager); } } void IoTcpUdpComm::allocate_io_tcpudp_plugin(FeaDataPlaneManager* fea_data_plane_manager) { IoTcpUdpPlugins::iterator iter; XLOG_ASSERT(fea_data_plane_manager != NULL); for (iter = _io_tcpudp_plugins.begin(); iter != _io_tcpudp_plugins.end(); ++iter) { if (iter->first == fea_data_plane_manager) break; } if (iter != _io_tcpudp_plugins.end()) { return; // XXX: the plugin was already allocated } IoTcpUdp* io_tcpudp = fea_data_plane_manager->allocate_io_tcpudp( _iftree, _family, _is_tcp); if (io_tcpudp == NULL) { XLOG_ERROR("Couldn't allocate plugin for I/O TCP/UDP " "communications for data plane manager %s", fea_data_plane_manager->manager_name().c_str()); return; } _io_tcpudp_plugins.push_back(make_pair(fea_data_plane_manager, io_tcpudp)); } void IoTcpUdpComm::deallocate_io_tcpudp_plugin(FeaDataPlaneManager* fea_data_plane_manager) { IoTcpUdpPlugins::iterator iter; XLOG_ASSERT(fea_data_plane_manager != NULL); for (iter = _io_tcpudp_plugins.begin(); iter != _io_tcpudp_plugins.end(); ++iter) { if (iter->first == fea_data_plane_manager) break; } if (iter == _io_tcpudp_plugins.end()) { XLOG_ERROR("Couldn't deallocate plugin for I/O TCP/UDP " "communications for data plane manager %s: plugin not found", fea_data_plane_manager->manager_name().c_str()); return; } IoTcpUdp* io_tcpudp = iter->second; fea_data_plane_manager->deallocate_io_tcpudp(io_tcpudp); _io_tcpudp_plugins.erase(iter); } void IoTcpUdpComm::add_plugin(IoTcpUdp* new_io_tcpudp) { IoTcpUdpPlugins::iterator iter; XLOG_ASSERT(new_io_tcpudp != NULL); for (iter = _io_tcpudp_plugins.begin(); iter != _io_tcpudp_plugins.end(); ++iter) { if (iter->second == new_io_tcpudp) break; } if (iter != _io_tcpudp_plugins.end()) { return; // XXX: the plugin was already added } _io_tcpudp_plugins.push_back( make_pair(&new_io_tcpudp->fea_data_plane_manager(), new_io_tcpudp)); } void IoTcpUdpComm::start_io_tcpudp_plugins() { IoTcpUdpPlugins::iterator iter; string error_msg; for (iter = _io_tcpudp_plugins.begin(); iter != _io_tcpudp_plugins.end(); ++iter) { IoTcpUdp* io_tcpudp = iter->second; if (io_tcpudp->is_running()) continue; io_tcpudp->register_io_tcpudp_receiver(this); if (io_tcpudp->start(error_msg) != XORP_OK) { XLOG_ERROR("%s", error_msg.c_str()); continue; } // // Push all multicast joins into the new plugin // JoinedGroupsTable::iterator join_iter; for (join_iter = _joined_groups_table.begin(); join_iter != _joined_groups_table.end(); ++join_iter) { JoinedMulticastGroup& joined_multicast_group = join_iter->second; if (io_tcpudp->udp_join_group( joined_multicast_group.group_address(), joined_multicast_group.interface_address(), error_msg) != XORP_OK) { XLOG_ERROR("%s", error_msg.c_str()); } } } } void IoTcpUdpComm::stop_io_tcpudp_plugins() { string error_msg; IoTcpUdpPlugins::iterator iter; for (iter = _io_tcpudp_plugins.begin(); iter != _io_tcpudp_plugins.end(); ++iter) { IoTcpUdp* io_tcpudp = iter->second; io_tcpudp->unregister_io_tcpudp_receiver(); if (io_tcpudp->stop(error_msg) != XORP_OK) { XLOG_ERROR("%s", error_msg.c_str()); } } } // ---------------------------------------------------------------------------- // IoTcpUdpManager code IoTcpUdpManager::IoTcpUdpManager(FeaNode& fea_node, const IfTree& iftree) : IoTcpUdpManagerReceiver(), _fea_node(fea_node), _eventloop(fea_node.eventloop()), _iftree(iftree), _io_tcpudp_manager_receiver(NULL) { } IoTcpUdpManager::~IoTcpUdpManager() { CommTable::iterator iter; // // Delete all IoTcpUdpComm entries // while (! _comm_table4.empty()) { iter = _comm_table4.begin(); delete iter->second; _comm_table4.erase(iter); } while (! _comm_table6.empty()) { iter = _comm_table6.begin(); delete iter->second; _comm_table6.erase(iter); } } IoTcpUdpManager::CommTable& IoTcpUdpManager::comm_table_by_family(int family) { if (family == IPv4::af()) return (_comm_table4); if (family == IPv6::af()) return (_comm_table6); XLOG_FATAL("Invalid address family: %d", family); return (_comm_table4); } void IoTcpUdpManager::erase_comm_handlers_by_creator(int family, const string& creator) { CommTable& comm_table = comm_table_by_family(family); CommTable::iterator iter; // Delete all entries for a given creator name for (iter = comm_table.begin(); iter != comm_table.end(); ) { IoTcpUdpComm* io_tcpudp_comm = iter->second; CommTable::iterator tmp_iter = iter++; if (io_tcpudp_comm->creator() == creator) { comm_table.erase(tmp_iter); delete io_tcpudp_comm; } } } bool IoTcpUdpManager::has_comm_handler_by_creator(const string& creator) const { CommTable::const_iterator iter; // There whether there is an entry for a given creator name for (iter = _comm_table4.begin(); iter != _comm_table4.end(); ++iter) { const IoTcpUdpComm* io_tcpudp_comm = iter->second; if (io_tcpudp_comm->creator() == creator) return (true); } for (iter = _comm_table6.begin(); iter != _comm_table6.end(); ++iter) { const IoTcpUdpComm* io_tcpudp_comm = iter->second; if (io_tcpudp_comm->creator() == creator) return (true); } return (false); } int IoTcpUdpManager::register_data_plane_manager( FeaDataPlaneManager* fea_data_plane_manager, bool is_exclusive) { if (is_exclusive) { // Unregister all registered data plane managers while (! _fea_data_plane_managers.empty()) { unregister_data_plane_manager(_fea_data_plane_managers.front()); } } if (fea_data_plane_manager == NULL) { // XXX: exclusive NULL is used to unregister all data plane managers return (XORP_OK); } if (find(_fea_data_plane_managers.begin(), _fea_data_plane_managers.end(), fea_data_plane_manager) != _fea_data_plane_managers.end()) { // XXX: already registered return (XORP_OK); } _fea_data_plane_managers.push_back(fea_data_plane_manager); // // Allocate all I/O TCP/UDP plugins for the new data plane manager // CommTable::iterator iter; for (iter = _comm_table4.begin(); iter != _comm_table4.end(); ++iter) { IoTcpUdpComm* io_tcpudp_comm = iter->second; io_tcpudp_comm->allocate_io_tcpudp_plugin(fea_data_plane_manager); io_tcpudp_comm->start_io_tcpudp_plugins(); } for (iter = _comm_table6.begin(); iter != _comm_table6.end(); ++iter) { IoTcpUdpComm* io_tcpudp_comm = iter->second; io_tcpudp_comm->allocate_io_tcpudp_plugin(fea_data_plane_manager); io_tcpudp_comm->start_io_tcpudp_plugins(); } return (XORP_OK); } int IoTcpUdpManager::unregister_data_plane_manager( FeaDataPlaneManager* fea_data_plane_manager) { if (fea_data_plane_manager == NULL) return (XORP_ERROR); list::iterator data_plane_manager_iter; data_plane_manager_iter = find(_fea_data_plane_managers.begin(), _fea_data_plane_managers.end(), fea_data_plane_manager); if (data_plane_manager_iter == _fea_data_plane_managers.end()) return (XORP_ERROR); // // Dealocate all I/O TCP/UDP plugins for the unregistered data plane // manager // CommTable::iterator iter; for (iter = _comm_table4.begin(); iter != _comm_table4.end(); ++iter) { IoTcpUdpComm* io_tcpudp_comm = iter->second; io_tcpudp_comm->deallocate_io_tcpudp_plugin(fea_data_plane_manager); } for (iter = _comm_table6.begin(); iter != _comm_table6.end(); ++iter) { IoTcpUdpComm* io_tcpudp_comm = iter->second; io_tcpudp_comm->deallocate_io_tcpudp_plugin(fea_data_plane_manager); } _fea_data_plane_managers.erase(data_plane_manager_iter); return (XORP_OK); } int IoTcpUdpManager::tcp_open(int family, const string& creator, string& sockid, string& error_msg) { IoTcpUdpComm* io_tcpudp_comm; io_tcpudp_comm = open_io_tcpudp_comm(family, true, creator); XLOG_ASSERT(io_tcpudp_comm != NULL); if (io_tcpudp_comm->tcp_open(sockid, error_msg) != XORP_OK) { delete_io_tcpudp_comm(family, io_tcpudp_comm->sockid()); return (XORP_ERROR); } if (_fea_node.fea_io().add_instance_watch(creator, this, error_msg) != XORP_OK) { delete_io_tcpudp_comm(family, io_tcpudp_comm->sockid()); return (XORP_ERROR); } return (XORP_OK); } int IoTcpUdpManager::udp_open(int family, const string& creator, string& sockid, string& error_msg) { IoTcpUdpComm* io_tcpudp_comm; io_tcpudp_comm = open_io_tcpudp_comm(family, false, creator); XLOG_ASSERT(io_tcpudp_comm != NULL); if (io_tcpudp_comm->udp_open(sockid, error_msg) != XORP_OK) { delete_io_tcpudp_comm(family, io_tcpudp_comm->sockid()); return (XORP_ERROR); } if (_fea_node.fea_io().add_instance_watch(creator, this, error_msg) != XORP_OK) { delete_io_tcpudp_comm(family, io_tcpudp_comm->sockid()); return (XORP_ERROR); } return (XORP_OK); } int IoTcpUdpManager::tcp_open_and_bind(int family, const string& creator, const IPvX& local_addr, uint16_t local_port, string& sockid, string& error_msg) { IoTcpUdpComm* io_tcpudp_comm; // // If "local_addr" is not zero, then it must belong to a local interface // if (! local_addr.is_zero()) { if (! is_my_address(local_addr)) { error_msg = c_format("Cannot open and bind a TCP socket " "to address %s: address not found", local_addr.str().c_str()); return (XORP_ERROR); } } io_tcpudp_comm = open_io_tcpudp_comm(family, true, creator); XLOG_ASSERT(io_tcpudp_comm != NULL); if (io_tcpudp_comm->tcp_open_and_bind(local_addr, local_port, sockid, error_msg) != XORP_OK) { delete_io_tcpudp_comm(family, io_tcpudp_comm->sockid()); return (XORP_ERROR); } if (_fea_node.fea_io().add_instance_watch(creator, this, error_msg) != XORP_OK) { delete_io_tcpudp_comm(family, io_tcpudp_comm->sockid()); return (XORP_ERROR); } return (XORP_OK); } int IoTcpUdpManager::udp_open_and_bind(int family, const string& creator, const IPvX& local_addr, uint16_t local_port, const string& local_dev, int reuse, string& sockid, string& error_msg) { IoTcpUdpComm* io_tcpudp_comm; // // If "local_addr" is not zero, then it must belong to a local interface // if (! local_addr.is_zero()) { if (! is_my_address(local_addr)) { error_msg = c_format("Cannot open and bind an UDP socket " "to address %s: address not found", local_addr.str().c_str()); return (XORP_ERROR); } } io_tcpudp_comm = open_io_tcpudp_comm(family, false, creator); XLOG_ASSERT(io_tcpudp_comm != NULL); if (io_tcpudp_comm->udp_open_and_bind(local_addr, local_port, local_dev, reuse, sockid, error_msg) != XORP_OK) { delete_io_tcpudp_comm(family, io_tcpudp_comm->sockid()); return (XORP_ERROR); } if (_fea_node.fea_io().add_instance_watch(creator, this, error_msg) != XORP_OK) { delete_io_tcpudp_comm(family, io_tcpudp_comm->sockid()); return (XORP_ERROR); } return (XORP_OK); } int IoTcpUdpManager::udp_open_bind_join(int family, const string& creator, const IPvX& local_addr, uint16_t local_port, const IPvX& mcast_addr, uint8_t ttl, bool reuse, string& sockid, string& error_msg) { IoTcpUdpComm* io_tcpudp_comm; // // The "local_addr" must not zero, and must belong to a local interface // if (local_addr.is_zero()) { error_msg = c_format("Cannot open, bind and join an UDP socket " "to address ZERO: the address must belong to " "a local interface"); return (XORP_ERROR); } else { if (! is_my_address(local_addr)) { error_msg = c_format("Cannot open, bind and join an UDP socket " "to address %s: address not found", local_addr.str().c_str()); return (XORP_ERROR); } } io_tcpudp_comm = open_io_tcpudp_comm(family, false, creator); XLOG_ASSERT(io_tcpudp_comm != NULL); if (io_tcpudp_comm->udp_open_bind_join(local_addr, local_port, mcast_addr, ttl, reuse, sockid, error_msg) != XORP_OK) { delete_io_tcpudp_comm(family, io_tcpudp_comm->sockid()); return (XORP_ERROR); } if (_fea_node.fea_io().add_instance_watch(creator, this, error_msg) != XORP_OK) { delete_io_tcpudp_comm(family, io_tcpudp_comm->sockid()); return (XORP_ERROR); } return (XORP_OK); } int IoTcpUdpManager::tcp_open_bind_connect(int family, const string& creator, const IPvX& local_addr, uint16_t local_port, const IPvX& remote_addr, uint16_t remote_port, string& sockid, string& error_msg) { IoTcpUdpComm* io_tcpudp_comm; // // If "local_addr" is not zero, then it must belong to a local interface // if (! local_addr.is_zero()) { if (! is_my_address(local_addr)) { error_msg = c_format("Cannot open, bind and connect a TCP socket " "to address %s: address not found", local_addr.str().c_str()); return (XORP_ERROR); } } io_tcpudp_comm = open_io_tcpudp_comm(family, true, creator); XLOG_ASSERT(io_tcpudp_comm != NULL); if (io_tcpudp_comm->tcp_open_bind_connect(local_addr, local_port, remote_addr, remote_port, sockid, error_msg) != XORP_OK) { delete_io_tcpudp_comm(family, io_tcpudp_comm->sockid()); return (XORP_ERROR); } if (_fea_node.fea_io().add_instance_watch(creator, this, error_msg) != XORP_OK) { delete_io_tcpudp_comm(family, io_tcpudp_comm->sockid()); return (XORP_ERROR); } return (XORP_OK); } int IoTcpUdpManager::udp_open_bind_connect(int family, const string& creator, const IPvX& local_addr, uint16_t local_port, const IPvX& remote_addr, uint16_t remote_port, string& sockid, string& error_msg) { IoTcpUdpComm* io_tcpudp_comm; // // If "local_addr" is not zero, then it must belong to a local interface // if (! local_addr.is_zero()) { if (! is_my_address(local_addr)) { error_msg = c_format("Cannot open, bind and connect an UDP socket " "to address %s: address not found", local_addr.str().c_str()); return (XORP_ERROR); } } io_tcpudp_comm = open_io_tcpudp_comm(family, false, creator); XLOG_ASSERT(io_tcpudp_comm != NULL); if (io_tcpudp_comm->udp_open_bind_connect(local_addr, local_port, remote_addr, remote_port, sockid, error_msg) != XORP_OK) { delete_io_tcpudp_comm(family, io_tcpudp_comm->sockid()); return (XORP_ERROR); } if (_fea_node.fea_io().add_instance_watch(creator, this, error_msg) != XORP_OK) { delete_io_tcpudp_comm(family, io_tcpudp_comm->sockid()); return (XORP_ERROR); } return (XORP_OK); } int IoTcpUdpManager::udp_open_bind_broadcast(int family, const string& creator, const string& ifname, const string& vifname, uint16_t local_port, uint16_t remote_port, bool reuse, bool limited, bool connected, string& sockid, string& error_msg) { IoTcpUdpComm* io_tcpudp_comm; if (family != AF_INET) { error_msg = c_format("Unsupported address family: %d", family); return (XORP_ERROR); } if (0 == _iftree.find_vif(ifname, vifname)) { error_msg = c_format("Cannot bind a broadcast socket " "to ifname/vifname %s/%s: vif not found", ifname.c_str(), vifname.c_str()); return (XORP_ERROR); } io_tcpudp_comm = open_io_tcpudp_comm(family, false, creator); XLOG_ASSERT(io_tcpudp_comm != NULL); if (io_tcpudp_comm->udp_open_bind_broadcast(ifname, vifname, local_port, remote_port, reuse, limited, connected, sockid, error_msg) != XORP_OK) { delete_io_tcpudp_comm(family, io_tcpudp_comm->sockid()); return (XORP_ERROR); } if (_fea_node.fea_io().add_instance_watch(creator, this, error_msg) != XORP_OK) { delete_io_tcpudp_comm(family, io_tcpudp_comm->sockid()); return (XORP_ERROR); } return (XORP_OK); } int IoTcpUdpManager::bind(int family, const string& sockid, const IPvX& local_addr, uint16_t local_port, string& error_msg) { IoTcpUdpComm* io_tcpudp_comm; // // If "local_addr" is not zero, then it must belong to a local interface // if (! local_addr.is_zero()) { if (! is_my_address(local_addr)) { error_msg = c_format("Cannot bind a socket " "to address %s: address not found", local_addr.str().c_str()); return (XORP_ERROR); } } io_tcpudp_comm = find_io_tcpudp_comm(family, sockid, error_msg); if (io_tcpudp_comm == NULL) return (XORP_ERROR); return (io_tcpudp_comm->bind(local_addr, local_port, error_msg)); } int IoTcpUdpManager::udp_join_group(int family, const string& sockid, const IPvX& mcast_addr, const IPvX& join_if_addr, string& error_msg) { IoTcpUdpComm* io_tcpudp_comm; // // The "join_if_addr" must not zero, and must belong to a local interface // if (join_if_addr.is_zero()) { error_msg = c_format("Cannot join an UDP socket " "to address ZERO: the address must belong to " "a local interface"); return (XORP_ERROR); } else { if (! is_my_address(join_if_addr)) { error_msg = c_format("Cannot join an UDP socket " "to address %s: address not found", join_if_addr.str().c_str()); return (XORP_ERROR); } } io_tcpudp_comm = find_io_tcpudp_comm(family, sockid, error_msg); if (io_tcpudp_comm == NULL) return (XORP_ERROR); return (io_tcpudp_comm->udp_join_group(mcast_addr, join_if_addr, error_msg)); } int IoTcpUdpManager::udp_leave_group(int family, const string& sockid, const IPvX& mcast_addr, const IPvX& leave_if_addr, string& error_msg) { IoTcpUdpComm* io_tcpudp_comm; // // The "leave_if_addr" must not zero, and must belong to a local interface // if (leave_if_addr.is_zero()) { error_msg = c_format("Cannot leave an UDP socket " "on address ZERO: the address must belong to " "a local interface"); return (XORP_ERROR); } else { if (! is_my_address(leave_if_addr)) { error_msg = c_format("Cannot leave an UDP socket " "on address %s: address not found", leave_if_addr.str().c_str()); return (XORP_ERROR); } } io_tcpudp_comm = find_io_tcpudp_comm(family, sockid, error_msg); if (io_tcpudp_comm == NULL) return (XORP_ERROR); return (io_tcpudp_comm->udp_leave_group(mcast_addr, leave_if_addr, error_msg)); } int IoTcpUdpManager::close(int family, const string& sockid, string& error_msg) { IoTcpUdpComm* io_tcpudp_comm; int ret_value; string creator; io_tcpudp_comm = find_io_tcpudp_comm(family, sockid, error_msg); if (io_tcpudp_comm == NULL) return (XORP_ERROR); creator = io_tcpudp_comm->creator(); ret_value = io_tcpudp_comm->close(error_msg); delete_io_tcpudp_comm(family, io_tcpudp_comm->sockid()); // Deregister interest in watching the creator if (! has_comm_handler_by_creator(creator)) { string dummy_error_msg; _fea_node.fea_io().delete_instance_watch(creator, this, dummy_error_msg); } return (ret_value); } int IoTcpUdpManager::tcp_listen(int family, const string& sockid, uint32_t backlog, string& error_msg) { IoTcpUdpComm* io_tcpudp_comm; io_tcpudp_comm = find_io_tcpudp_comm(family, sockid, error_msg); if (io_tcpudp_comm == NULL) return (XORP_ERROR); return (io_tcpudp_comm->tcp_listen(backlog, error_msg)); } int IoTcpUdpManager::udp_enable_recv(int family, const string& sockid, string& error_msg) { IoTcpUdpComm* io_tcpudp_comm; io_tcpudp_comm = find_io_tcpudp_comm(family, sockid, error_msg); if (io_tcpudp_comm == NULL) return (XORP_ERROR); return (io_tcpudp_comm->udp_enable_recv(error_msg)); } int IoTcpUdpManager::send(int family, const string& sockid, const vector& data, string& error_msg) { IoTcpUdpComm* io_tcpudp_comm; io_tcpudp_comm = find_io_tcpudp_comm(family, sockid, error_msg); if (io_tcpudp_comm == NULL) return (XORP_ERROR); return (io_tcpudp_comm->send(data, error_msg)); } int IoTcpUdpManager::send_to(int family, const string& sockid, const IPvX& remote_addr, uint16_t remote_port, const vector& data, string& error_msg) { IoTcpUdpComm* io_tcpudp_comm; io_tcpudp_comm = find_io_tcpudp_comm(family, sockid, error_msg); if (io_tcpudp_comm == NULL) return (XORP_ERROR); return (io_tcpudp_comm->send_to(remote_addr, remote_port, data, error_msg)); } int IoTcpUdpManager::send_from_multicast_if(int family, const string& sockid, const IPvX& group_addr, uint16_t group_port, const IPvX& ifaddr, const vector& data, string& error_msg) { IoTcpUdpComm* io_tcpudp_comm; // // The "ifaddr" must not zero, and must belong to a local interface // if (ifaddr.is_zero()) { error_msg = c_format("Cannot send on an UDP socket " "from address ZERO: the address must belong to " "a local interface"); return (XORP_ERROR); } else { if (! is_my_address(ifaddr)) { error_msg = c_format("Cannot send on an UDP socket " "from address %s: address not found", ifaddr.str().c_str()); return (XORP_ERROR); } } io_tcpudp_comm = find_io_tcpudp_comm(family, sockid, error_msg); if (io_tcpudp_comm == NULL) return (XORP_ERROR); return (io_tcpudp_comm->send_from_multicast_if(group_addr, group_port, ifaddr, data, error_msg)); } int IoTcpUdpManager::set_socket_option(int family, const string& sockid, const string& optname, uint32_t optval, string& error_msg) { IoTcpUdpComm* io_tcpudp_comm; io_tcpudp_comm = find_io_tcpudp_comm(family, sockid, error_msg); if (io_tcpudp_comm == NULL) return (XORP_ERROR); return (io_tcpudp_comm->set_socket_option(optname, optval, error_msg)); } int IoTcpUdpManager::set_socket_option(int family, const string& sockid, const string& optname, const string& optval, string& error_msg) { IoTcpUdpComm* io_tcpudp_comm; io_tcpudp_comm = find_io_tcpudp_comm(family, sockid, error_msg); if (io_tcpudp_comm == NULL) return (XORP_ERROR); return (io_tcpudp_comm->set_socket_option(optname, optval, error_msg)); } int IoTcpUdpManager::accept_connection(int family, const string& sockid, bool is_accepted, string& error_msg) { IoTcpUdpComm* io_tcpudp_comm; int ret_value = XORP_OK; io_tcpudp_comm = find_io_tcpudp_comm(family, sockid, error_msg); if (io_tcpudp_comm == NULL) return (XORP_ERROR); ret_value = io_tcpudp_comm->accept_connection(is_accepted, error_msg); if (! is_accepted) { // // Connection rejected, therefore close and delete the socket. // string dummy_error_msg; close(family, sockid, dummy_error_msg); } return (ret_value); } void IoTcpUdpManager::recv_event(const string& receiver_name, const string& sockid, const string& if_name, const string& vif_name, const IPvX& src_host, uint16_t src_port, const vector& data) { if (_io_tcpudp_manager_receiver != NULL) _io_tcpudp_manager_receiver->recv_event(receiver_name, sockid, if_name, vif_name, src_host, src_port, data); } void IoTcpUdpManager::inbound_connect_event(const string& receiver_name, const string& sockid, const IPvX& src_host, uint16_t src_port, const string& new_sockid) { if (_io_tcpudp_manager_receiver != NULL) _io_tcpudp_manager_receiver->inbound_connect_event(receiver_name, sockid, src_host, src_port, new_sockid); } void IoTcpUdpManager::outgoing_connect_event(int family, const string& receiver_name, const string& sockid) { if (_io_tcpudp_manager_receiver != NULL) _io_tcpudp_manager_receiver->outgoing_connect_event(family, receiver_name, sockid); } void IoTcpUdpManager::error_event(int family, const string& receiver_name, const string& sockid, const string& error, bool fatal) { if (_io_tcpudp_manager_receiver != NULL) _io_tcpudp_manager_receiver->error_event(family, receiver_name, sockid, error, fatal); if (fatal) { // // Fatal error, therefore close and delete the socket. // string dummy_error_msg; close(family, sockid, dummy_error_msg); } } void IoTcpUdpManager::disconnect_event(int family, const string& receiver_name, const string& sockid) { if (_io_tcpudp_manager_receiver != NULL) _io_tcpudp_manager_receiver->disconnect_event(family, receiver_name, sockid); } void IoTcpUdpManager::instance_birth(const string& instance_name) { // XXX: Nothing to do UNUSED(instance_name); } void IoTcpUdpManager::instance_death(const string& instance_name) { string dummy_error_msg; _fea_node.fea_io().delete_instance_watch(instance_name, this, dummy_error_msg); erase_comm_handlers_by_creator(AF_INET, instance_name); #ifdef HAVE_IPV6 erase_comm_handlers_by_creator(AF_INET6, instance_name); #endif } IoTcpUdpComm* IoTcpUdpManager::connect_io_tcpudp_comm(int family, bool is_tcp, const string& creator, const string& listener_sockid, const IPvX& peer_host, uint16_t peer_port, IoTcpUdp* new_io_tcpudp) { CommTable& comm_table = comm_table_by_family(family); CommTable::iterator iter; IoTcpUdpComm* io_tcpudp_comm = NULL; // // Find a matching IoTcpUdpComm entry that was created by the // connect event from the first plugin. // for (iter = comm_table.begin(); iter != comm_table.end(); ++iter) { io_tcpudp_comm = iter->second; if ((io_tcpudp_comm->listener_sockid() == listener_sockid) && (io_tcpudp_comm->peer_host() == peer_host) && (io_tcpudp_comm->peer_port() == peer_port)) { break; } io_tcpudp_comm = NULL; } if (io_tcpudp_comm == NULL) { // // Entry not found, therefore create it. However, it shouldn't // contain any plugins. // io_tcpudp_comm = open_io_tcpudp_comm(family, is_tcp, creator, false); XLOG_ASSERT(io_tcpudp_comm != NULL); } // // Add the new plugin // io_tcpudp_comm->add_plugin(new_io_tcpudp); io_tcpudp_comm->start_io_tcpudp_plugins(); return (io_tcpudp_comm); } IoTcpUdpComm* IoTcpUdpManager::find_io_tcpudp_comm(int family, const string& sockid, string& error_msg) { CommTable& comm_table = comm_table_by_family(family); CommTable::iterator iter; iter = comm_table.find(sockid); if (iter == comm_table.end()) { error_msg = c_format("Socket not found"); return (NULL); } return (iter->second); } IoTcpUdpComm* IoTcpUdpManager::open_io_tcpudp_comm(int family, bool is_tcp, const string& creator, bool allocate_plugins) { CommTable& comm_table = comm_table_by_family(family); IoTcpUdpComm* io_tcpudp_comm; io_tcpudp_comm = new IoTcpUdpComm(*this, iftree(), family, is_tcp, creator); comm_table[io_tcpudp_comm->sockid()] = io_tcpudp_comm; // // Allocate and start the IoTcpUdp plugins: one per data plane manager. // if (allocate_plugins) { io_tcpudp_comm->allocate_io_tcpudp_plugins(); io_tcpudp_comm->start_io_tcpudp_plugins(); } return (io_tcpudp_comm); } void IoTcpUdpManager::delete_io_tcpudp_comm(int family, const string& sockid) { CommTable& comm_table = comm_table_by_family(family); CommTable::iterator iter; iter = comm_table.find(sockid); if (iter == comm_table.end()) return; // Delete the entry IoTcpUdpComm* io_tcpudp_comm = iter->second; comm_table.erase(iter); delete io_tcpudp_comm; } bool IoTcpUdpManager::is_my_address(const IPvX& local_addr) const { const IfTreeInterface* ifp = NULL; const IfTreeVif* vifp = NULL; return (_iftree.find_interface_vif_by_addr(local_addr, ifp, vifp) == true); } xorp/fea/mfea_dataflow.cc0000664000076400007640000003322711421137511015516 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // MFEA (Multicast Forwarding Engine Abstraction) dataflow 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 "mfea_dataflow.hh" #include "mfea_node.hh" #include "mfea_kernel_messages.hh" // // Exported variables // // // Local constants definitions // // // Local structures/classes, typedefs and macros // // // Local variables // // // Local functions prototypes // MfeaDft::MfeaDft(MfeaNode& mfea_node) : _mfea_node(mfea_node) { } MfeaDft::~MfeaDft() { } int MfeaDft::family() const { return (_mfea_node.family()); } int MfeaDft::add_entry(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) { MfeaDfe *mfea_dfe; MfeaDfeLookup *mfea_dfe_lookup; mfea_dfe_lookup = find(source, group); if (mfea_dfe_lookup == NULL) { // Create and add a new dataflow lookup entry mfea_dfe_lookup = new MfeaDfeLookup(*this, source, group); insert(mfea_dfe_lookup); } // Search for a dataflow entry. mfea_dfe = mfea_dfe_lookup->find(threshold_interval, threshold_packets, threshold_bytes, is_threshold_in_packets, is_threshold_in_bytes, is_geq_upcall, is_leq_upcall); if (mfea_dfe != NULL) { // Already have this entry return (XORP_OK); } // Create a new entry mfea_dfe = new MfeaDfe(*mfea_dfe_lookup, threshold_interval, threshold_packets, threshold_bytes, is_threshold_in_packets, is_threshold_in_bytes, is_geq_upcall, is_leq_upcall); mfea_dfe->init_sg_count(); if (! mfea_dfe->is_valid()) { delete mfea_dfe; if (mfea_dfe_lookup->is_empty()) { remove(mfea_dfe_lookup); delete mfea_dfe_lookup; } error_msg = c_format("Cannot add dataflow monitor for (%s, %s): " "invalid request", cstring(source), cstring(group)); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } mfea_dfe_lookup->insert(mfea_dfe); mfea_dfe->start_measurement(); return (XORP_OK); } int MfeaDft::delete_entry(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) { MfeaDfe *mfea_dfe; MfeaDfeLookup *mfea_dfe_lookup; mfea_dfe_lookup = find(source, group); if (mfea_dfe_lookup == NULL) { 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); // Not found } // Search for a dataflow entry. mfea_dfe = mfea_dfe_lookup->find(threshold_interval, threshold_packets, threshold_bytes, is_threshold_in_packets, is_threshold_in_bytes, is_geq_upcall, is_leq_upcall); if (mfea_dfe == NULL) { error_msg = c_format("Cannot delete dataflow monitor for (%s, %s): " "monitor not found", cstring(source), cstring(group)); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); // Not found } if (delete_entry(mfea_dfe) != XORP_OK) { error_msg = c_format("Cannot delete dataflow monitor for (%s, %s): " "internal error", cstring(source), cstring(group)); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } return (XORP_OK); } int MfeaDft::delete_entry(const IPvX& source, const IPvX& group) { MfeaDfeLookup *mfea_dfe_lookup; mfea_dfe_lookup = find(source, group); if (mfea_dfe_lookup == NULL) return (XORP_ERROR); // Nothing found remove(mfea_dfe_lookup); delete mfea_dfe_lookup; return (XORP_OK); } int MfeaDft::delete_entry(MfeaDfe *mfea_dfe) { MfeaDfeLookup *mfea_dfe_lookup = &mfea_dfe->mfea_dfe_lookup(); mfea_dfe_lookup->remove(mfea_dfe); delete mfea_dfe; if (mfea_dfe_lookup->is_empty()) { remove(mfea_dfe_lookup); delete mfea_dfe_lookup; } return (XORP_OK); } MfeaDfeLookup::MfeaDfeLookup(MfeaDft& mfea_dft, const IPvX& source, const IPvX& group) : Mre(source, group), _mfea_dft(mfea_dft) { } MfeaDfeLookup::~MfeaDfeLookup() { delete_pointers_list(_mfea_dfe_list); } int MfeaDfeLookup::family() const { return (_mfea_dft.family()); } MfeaDfe * MfeaDfeLookup::find(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) { list::const_iterator iter; for (iter = _mfea_dfe_list.begin(); iter != _mfea_dfe_list.end(); ++iter) { MfeaDfe *mfea_dfe = *iter; if (mfea_dfe->is_same(threshold_interval, threshold_packets, threshold_bytes, is_threshold_in_packets, is_threshold_in_bytes, is_geq_upcall, is_leq_upcall)) return (mfea_dfe); } return (NULL); } void MfeaDfeLookup::insert(MfeaDfe *mfea_dfe) { _mfea_dfe_list.push_back(mfea_dfe); } void MfeaDfeLookup::remove(MfeaDfe *mfea_dfe) { // XXX: presumably, the list will be very short, so for simplicity // we use the _theoretically_ less-efficient remove() instead of find() _mfea_dfe_list.remove(mfea_dfe); } MfeaDfe::MfeaDfe(MfeaDfeLookup& mfea_dfe_lookup, 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) : _mfea_dfe_lookup(mfea_dfe_lookup), _threshold_interval(threshold_interval), _threshold_packets(threshold_packets), _threshold_bytes(threshold_bytes), _is_threshold_in_packets(is_threshold_in_packets), _is_threshold_in_bytes(is_threshold_in_bytes), _is_geq_upcall(is_geq_upcall), _is_leq_upcall(is_leq_upcall) { _delta_sg_count_index = 0; _is_bootstrap_completed = false; _measurement_interval = _threshold_interval / MFEA_DATAFLOW_TEST_FREQUENCY; for (size_t i = 0; i < sizeof(_start_time)/sizeof(_start_time[0]); i++) _start_time[i] = TimeVal::ZERO(); } MfeaDfe::~MfeaDfe() { } MfeaDft& MfeaDfe::mfea_dft() const { return (_mfea_dfe_lookup.mfea_dft()); } EventLoop& MfeaDfe::eventloop() const { return (mfea_dft().mfea_node().eventloop()); } int MfeaDfe::family() const { return (_mfea_dfe_lookup.family()); } bool MfeaDfe::is_valid() const { // TODO: the minimum interval is hard-coded int min_sec = 3; // XXX: the minimum threshold interval value int min_usec = 0; return ((_is_threshold_in_packets || _is_threshold_in_bytes) && (_is_geq_upcall ^ _is_leq_upcall) && (_threshold_interval >= TimeVal(min_sec, min_usec)) && _last_sg_count.is_valid()); } bool MfeaDfe::is_same(const TimeVal& threshold_interval_test, uint32_t threshold_packets_test, uint32_t threshold_bytes_test, bool is_threshold_in_packets_test, bool is_threshold_in_bytes_test, bool is_geq_upcall_test, bool is_leq_upcall_test) const { if (is_threshold_in_packets_test) if (threshold_packets_test != _threshold_packets) return (false); if (is_threshold_in_bytes_test) if (threshold_bytes_test != _threshold_bytes) return (false); return ((threshold_interval_test == _threshold_interval) && (is_threshold_in_packets_test == _is_threshold_in_packets) && (is_threshold_in_bytes_test == _is_threshold_in_bytes) && (is_geq_upcall_test == _is_geq_upcall) && (is_leq_upcall_test == _is_leq_upcall)); } void MfeaDfe::init_sg_count() { mfea_dft().mfea_node().get_sg_count(source_addr(), group_addr(), _last_sg_count); } // // Read the count from the kernel, and test if it is above/below the threshold. // XXX: if both packets and bytes are enabled, then return true if the test // is positive for either. // bool MfeaDfe::test_sg_count() { SgCount saved_last_sg_count = _last_sg_count; uint32_t diff_value, threshold_value; bool ret_value = false; // // Perform the measurement // if (mfea_dft().mfea_node().get_sg_count(source_addr(), group_addr(), _last_sg_count) != XORP_OK) { // Error return (false); // TODO: what do we do when error occured? } // // Compute the delta since the last measurement // if ((_is_threshold_in_packets && (saved_last_sg_count.pktcnt() > _last_sg_count.pktcnt())) || (_is_threshold_in_bytes && (saved_last_sg_count.bytecnt() > _last_sg_count.bytecnt()))) { // XXX: very likely the counter has round-up. We can try to be // smart and compute the difference between the old value and // the maximum possible value, and then just add that difference // to the new value. // However, the maximum possible value depends on the original size // of the counter. In case FreeBSD-4.5 it is u_long for IPv4, // but u_quad_t for IPv6. // Hence, we just ignore this measurement... Sigh... _delta_sg_count[_delta_sg_count_index].reset(); ret_value = false; goto ret_label; } _delta_sg_count[_delta_sg_count_index] = _last_sg_count; _delta_sg_count[_delta_sg_count_index] -= saved_last_sg_count; // // Increment the counter to point to the next entry (to be used next time) // _delta_sg_count_index++; if (_delta_sg_count_index >= MFEA_DATAFLOW_TEST_FREQUENCY) { _delta_sg_count_index %= MFEA_DATAFLOW_TEST_FREQUENCY; _is_bootstrap_completed = true; } // // Compute the difference for the last threshold interval // _measured_sg_count.reset(); if (_is_bootstrap_completed) { for (size_t i = 0; i < MFEA_DATAFLOW_TEST_FREQUENCY; i++) { _measured_sg_count += _delta_sg_count[i]; } } else { for (size_t i = 0; i < _delta_sg_count_index; i++) { _measured_sg_count += _delta_sg_count[i]; } } if (_is_threshold_in_packets) { threshold_value = _threshold_packets; diff_value = _measured_sg_count.pktcnt(); if (_is_geq_upcall) { if (diff_value >= threshold_value) { ret_value = true; goto ret_label; } } if (_is_leq_upcall && _is_bootstrap_completed) { if (diff_value <= threshold_value) { ret_value = true; goto ret_label; } } } if (_is_threshold_in_bytes) { threshold_value = _threshold_bytes; diff_value = _measured_sg_count.bytecnt(); if (_is_geq_upcall) { if (diff_value >= threshold_value) { ret_value = true; goto ret_label; } } if (_is_leq_upcall && _is_bootstrap_completed) { if (diff_value <= threshold_value) { ret_value = true; goto ret_label; } } } ret_label: return (ret_value); } void MfeaDfe::start_measurement() { _measurement_timer = eventloop().new_oneoff_after(_measurement_interval, callback(this, &MfeaDfe::measurement_timer_timeout)); TimeVal now; mfea_dft().mfea_node().eventloop().current_time(now); _start_time[_delta_sg_count_index] = now; } void MfeaDfe::dataflow_signal_send() { // XXX: for simplicity, we assume that the threshold interval // is same as the measured interval mfea_dft().mfea_node().signal_dataflow_message_recv( source_addr(), group_addr(), _threshold_interval, _threshold_interval, _threshold_packets, _threshold_bytes, _measured_sg_count.pktcnt(), _measured_sg_count.bytecnt(), _is_threshold_in_packets, _is_threshold_in_bytes, _is_geq_upcall, _is_leq_upcall); } const TimeVal& MfeaDfe::start_time() const { if (! _is_bootstrap_completed) return (_start_time[0]); return (_start_time[_delta_sg_count_index]); } uint32_t MfeaDfe::measured_packets() const { SgCount result; if (_is_bootstrap_completed) { for (size_t i = 0; i < MFEA_DATAFLOW_TEST_FREQUENCY; i++) { result += _delta_sg_count[i]; } } else { for (size_t i = 0; i < _delta_sg_count_index; i++) { result += _delta_sg_count[i]; } } return (result.pktcnt()); } uint32_t MfeaDfe::measured_bytes() const { SgCount result; if (_is_bootstrap_completed) { for (size_t i = 0; i < MFEA_DATAFLOW_TEST_FREQUENCY; i++) { result += _delta_sg_count[i]; } } else { for (size_t i = 0; i < _delta_sg_count_index; i++) { result += _delta_sg_count[i]; } } return (result.bytecnt()); } void MfeaDfe::measurement_timer_timeout() { if (test_sg_count()) { // Time to deliver a signal dataflow_signal_send(); } // Restart the measurements start_measurement(); } xorp/fea/data_plane/0000775000076400007640000000000011540225522014501 5ustar greearbgreearbxorp/fea/data_plane/io/0000775000076400007640000000000011703345405015114 5ustar greearbgreearbxorp/fea/data_plane/io/io_tcpudp_socket.hh0000664000076400007640000003365311540224223020776 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2007-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/data_plane/io/io_tcpudp_socket.hh,v 1.15 2008/10/11 04:20:19 pavlin Exp $ #ifndef __FEA_DATA_PLANE_IO_IO_TCPUDP_SOCKET_HH__ #define __FEA_DATA_PLANE_IO_IO_TCPUDP_SOCKET_HH__ // // I/O TCP/UDP communication support. // // The mechanism is UNIX TCP/UDP sockets. // #include "libxorp/xorp.h" #include "libxorp/eventloop.hh" #include "libxorp/asyncio.hh" #include "fea/io_tcpudp.hh" class AsyncFileWriter; /** * @short A base class for I/O TCP/UDP socket communication. */ class IoTcpUdpSocket : public IoTcpUdp { public: /** * Constructor for a given address family. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). * @param iftree the interface tree to use. * @param family the address family (AF_INET or AF_INET6 for IPv4 and IPv6 * respectively). * @param is_tcp if true this is TCP entry, otherwise UDP. */ IoTcpUdpSocket(FeaDataPlaneManager& fea_data_plane_manager, const IfTree& iftree, int family, bool is_tcp); /** * Virtual destructor. */ virtual ~IoTcpUdpSocket(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int stop(string& error_msg); /** * Open a TCP socket. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int tcp_open(string& error_msg); /** * Open an UDP socket. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int udp_open(string& error_msg); /** * Create a bound TCP socket. * * @param local_addr the interface address to bind socket to. * @param local_port the port to bind socket to. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int tcp_open_and_bind(const IPvX& local_addr, uint16_t local_port, string& error_msg); /** * Create a bound UDP socket. * * @param local_addr the interface address to bind socket to. * @param local_port the port to bind socket to. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int udp_open_and_bind(const IPvX& local_addr, uint16_t local_port, const string& local_dev, int reuse, string& error_msg); /** * Create a bound UDP multicast socket. * * @param local_addr the interface address to bind socket to. * @param local_port the port to bind socket to. * @param mcast_addr the multicast group address to join. * @param ttl the TTL to use for this multicast socket. * @param reuse allow other sockets to bind to same multicast group. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int udp_open_bind_join(const IPvX& local_addr, uint16_t local_port, const IPvX& mcast_addr, uint8_t ttl, bool reuse, string& error_msg); /** * Create a bound and connected TCP socket. * * @param local_addr the interface address to bind socket to. * @param local_port the port to bind socket to. * @param remote_addr the address to connect to. * @param remote_port the remote port to connect to. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int tcp_open_bind_connect(const IPvX& local_addr, uint16_t local_port, const IPvX& remote_addr, uint16_t remote_port, string& error_msg); /** * Create a bound and connected UDP socket. * * @param local_addr the interface address to bind socket to. * @param local_port the port to bind socket to. * @param remote_addr the address to connect to. * @param remote_port the remote port to connect to. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int udp_open_bind_connect(const IPvX& local_addr, uint16_t local_port, const IPvX& remote_addr, uint16_t remote_port, string& error_msg); /** * Create a bound, and optionally connected, UDP broadcast socket. * * @param ifname the interface name to bind socket to. * @param vifname the vif to bind socket to. * @param local_port the port to bind socket to. * @param remote_port the remote port to connect to. * @param reuse allow other sockets to bind to same port. * @param limited set the socket up for transmission to the limited * broadcast address 255.255.255.255. * @param connected connect the socket for use with send() not sendto(). * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int udp_open_bind_broadcast(const string& ifname, const string& vifname, uint16_t local_port, uint16_t remote_port, bool reuse, bool limited, bool connected, string& error_msg); /** * Bind a socket. * * @param local_addr the interface address to bind socket to. * @param local_port the port to bind socket to. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int bind(const IPvX& local_addr, uint16_t local_port, string& error_msg); /** * Join multicast group on already bound socket. * * @param mcast_addr group to join. * @param join_if_addr interface address to perform join on. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int udp_join_group(const IPvX& mcast_addr, const IPvX& join_if_addr, string& error_msg); /** * Leave multicast group on already bound socket. * * @param mcast_addr group to leave. * @param leave_if_addr interface address to perform leave on. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int udp_leave_group(const IPvX& mcast_addr, const IPvX& leave_if_addr, string& error_msg); /** * Close socket. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int close(string& error_msg); /** * Listen for inbound connections on socket. * * When a connection request is received the socket creator will receive * notification. * * @param backlog the maximum number of pending connections. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int tcp_listen(uint32_t backlog, string& error_msg); /** * Enable a UDP socket for datagram reception. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int udp_enable_recv(string& error_msg); /** * Send data on socket. * * @param data block of data to be sent. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int send(const vector& data, string& error_msg); /** * Send data on socket to a given destination. * * The packet is not routed as the forwarding engine sending the packet * may not have access to the full routing table. * * @param remote_addr destination address for data. * @param remote_port destination port for data. * @param data block of data to be sent. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int send_to(const IPvX& remote_addr, uint16_t remote_port, const vector& data, string& error_msg); /** * Send data on socket to a given multicast group from a given interface. * * @param group_addr destination address for data. * @param group_port destination port for data. * @param ifaddr interface address. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int send_from_multicast_if(const IPvX& group_addr, uint16_t group_port, const IPvX& ifaddr, const vector& data, string& error_msg); /** * Set a named socket option with an integer value. * * @param optname name of option to be set. Valid values are: * "onesbcast" (IPv4 only) * "receive_broadcast" (IPv4 only) * "reuseport" * "send_broadcast" (IPv4 only) * "tos" (IPv4 only) * "ttl" * "multicast_loopback" * "multicast_ttl" * @param optval value of option to be set. If value is logically boolean * then zero represents false and any non-zero value true. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_socket_option(const string& optname, uint32_t optval, string& error_msg); /** * Set a named socket option with a text value. * * @param optname name of option to be set. Valid values are: * "bindtodevice" * @param optval string value of option to be set. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_socket_option(const string& optname, const string& optval, string& error_msg); /** * Accept or reject a pending connection. * * @param is_accepted if true, the connection is accepted, otherwise is * rejected. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int accept_connection(bool is_accepted, string& error_msg); protected: /** * Set the file descriptor of the socket. * * @param fd the file descriptor of the socket. */ void set_socket_fd(XorpFd fd) { _socket_fd = fd; } private: /** * Enable receiving of data. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int enable_data_recv(string& error_msg); /** * Enable/disable receiving information about a packet received on the * protocol socket. * * If enabled, values such as interface index will be received as well. * * @param is_enabled if true, set the option, otherwise reset it. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int enable_recv_pktinfo(bool is_enabled, string& error_msg); /** * I/O event callback: new connection is ready to be accepted. * * @param fd the file descriptor of the socket with the new connection. * @param io_event_type the event type (@ref IoEventType). */ void accept_io_cb(XorpFd fd, IoEventType io_event_type); /** * I/O event callback: connection opening to the peer has completed. * * @param fd the file descriptor of the socket with the new connection. * @param io_event_type the event type (@ref IoEventType). */ void connect_io_cb(XorpFd fd, IoEventType io_event_type); /** * I/O event callback: data is received. * * @param fd the file descriptor of the socket with the data. * @param io_event_type the event type (@ref IoEventType). */ void data_io_cb(XorpFd fd, IoEventType io_event_type); /** * Data transmission completed callback. * * @param event the event code (@ref AsyncFileOperator::Event). * @param buffer the buffer with the transmitted data. * @param buffer_bytes the size of the buffer with the data. * @param offset the offset of the last byte written (from the beginning * of the buffer. */ void send_completed_cb(AsyncFileWriter::Event event, const uint8_t* buffer, size_t buffer_bytes, size_t offset); /** * I/O event callback: the peer has closed the connection. * * This callback is used only for Windows, and only for TCP sockets. * * @param fd the file descriptor of the socket with the data. * @param io_event_type the event type (@ref IoEventType). */ void disconnect_io_cb(XorpFd fd, IoEventType io_event_type); XorpFd _socket_fd; IPvX _peer_address; // Peer address (valid for TCP only) uint16_t _peer_port; // Peer port (valid for TCP only) AsyncFileWriter* _async_writer; // Async writer for sending data bool _limited_broadcast_enabled; // true if this is an IPv4/UDP // broadcast socket IPvX _network_broadcast_address; // valid at time of creation for // IPv4/UDP broadcasts only }; #endif // __FEA_DATA_PLANE_IO_IO_TCPUDP_SOCKET_HH__ xorp/fea/data_plane/io/io_tcpudp_socket.cc0000664000076400007640000014464211605416473021001 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8 sw=4: // Copyright (c) 2007-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // I/O TCP/UDP communication support. // // The mechanism is UNIX TCP/UDP sockets. // #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvx.hh" #ifdef HAVE_SYS_UIO_H #include #endif #ifdef HAVE_NET_IF_DL_H #include #endif #include "libcomm/comm_api.h" #include "fea/iftree.hh" #include "io_tcpudp_socket.hh" #ifdef HAVE_TCPUDP_UNIX_SOCKETS /** * Find the physical interface index for an interface for a given local * address. * * @param iftree the interface tree to use. * @param local_addr the local address to search for. * @param error_msg the error message (if error). * @return the physical interface index on success, otherwise 0. */ #ifdef HAVE_IPV6 // XXX: For now the function is used only by the IPv6 code static uint32_t find_pif_index_by_addr(const IfTree& iftree, const IPvX& local_addr, string& error_msg) { const IfTreeInterface* ifp = NULL; const IfTreeVif* vifp = NULL; uint32_t pif_index = 0; // Find the physical interface index if (iftree.find_interface_vif_by_addr(local_addr, ifp, vifp) != true) { error_msg = c_format("Local IP address %s was not found", local_addr.str().c_str()); return (0); } XLOG_ASSERT(vifp != NULL); pif_index = vifp->pif_index(); if (pif_index == 0) { error_msg = c_format("Could not find physical interface index for " "IP address %s", local_addr.str().c_str()); return (0); } return (pif_index); } #endif // HAVE_IPV6 /** * Extract the port number from struct sockaddr_storage. * * @return the port number (in host order). */ static uint16_t get_sockadr_storage_port_number(const struct sockaddr_storage& ss) { uint16_t port = 0; switch (ss.ss_family) { case AF_INET: { const struct sockaddr* sa = sockaddr_storage2sockaddr(&ss); const struct sockaddr_in* sin = sockaddr2sockaddr_in(sa); port = ntohs(sin->sin_port); break; } #ifdef HAVE_IPV6 case AF_INET6: { const struct sockaddr* sa = sockaddr_storage2sockaddr(&ss); const struct sockaddr_in6* sin6 = sockaddr2sockaddr_in6(sa); port = ntohs(sin6->sin6_port); break; } #endif // HAVE_IPV6 default: XLOG_UNREACHABLE(); return (port); } return (port); } IoTcpUdpSocket::IoTcpUdpSocket(FeaDataPlaneManager& fea_data_plane_manager, const IfTree& iftree, int family, bool is_tcp) : IoTcpUdp(fea_data_plane_manager, iftree, family, is_tcp), _peer_address(IPvX::ZERO(family)), _peer_port(0), _async_writer(NULL) { } IoTcpUdpSocket::~IoTcpUdpSocket() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the I/O TCP/UDP UNIX socket mechanism: %s", error_msg.c_str()); } } int IoTcpUdpSocket::start(string& error_msg) { UNUSED(error_msg); if (_is_running) return (XORP_OK); _is_running = true; return (XORP_OK); } int IoTcpUdpSocket::stop(string& error_msg) { if (! _is_running) return (XORP_OK); if (_socket_fd.is_valid()) { if (close(error_msg) != XORP_OK) return (XORP_ERROR); } _is_running = false; return (XORP_OK); } int IoTcpUdpSocket::enable_recv_pktinfo(bool is_enabled, string& error_msg) { switch (family()) { case AF_INET: { // XXX: the setsockopt() argument must be 'int' int bool_flag = is_enabled; // // Interface index // #ifdef IP_RECVIF // XXX: BSD if (setsockopt(_socket_fd, IPPROTO_IP, IP_RECVIF, XORP_SOCKOPT_CAST(&bool_flag), sizeof(bool_flag)) < 0) { XLOG_ERROR("setsockopt(IP_RECVIF, %u) failed: %s", bool_flag, XSTRERROR); return (XORP_ERROR); } #endif // IP_RECVIF #ifdef IP_PKTINFO // XXX: Linux if (setsockopt(_socket_fd, IPPROTO_IP, IP_PKTINFO, XORP_SOCKOPT_CAST(&bool_flag), sizeof(bool_flag)) < 0) { XLOG_ERROR("setsockopt(IP_PKTINFO, %u) failed: %s", bool_flag, XSTRERROR); return (XORP_ERROR); } #endif // IP_PKTINFO UNUSED(bool_flag); break; } #ifdef HAVE_IPV6 case AF_INET6: { // XXX: the setsockopt() argument must be 'int' int bool_flag = is_enabled; // // Interface index and address // #ifdef IPV6_RECVPKTINFO // The new option (applies to receiving only) if (setsockopt(_socket_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, XORP_SOCKOPT_CAST(&bool_flag), sizeof(bool_flag)) < 0) { error_msg = c_format("setsockopt(IPV6_RECVPKTINFO, %u) failed: %s", bool_flag, XSTRERROR); return (XORP_ERROR); } #else // The old option (see RFC-2292) if (setsockopt(_socket_fd, IPPROTO_IPV6, IPV6_PKTINFO, XORP_SOCKOPT_CAST(&bool_flag), sizeof(bool_flag)) < 0) { error_msg = c_format("setsockopt(IPV6_PKTINFO, %u) failed: %s", bool_flag, XSTRERROR); return (XORP_ERROR); } #endif // ! IPV6_RECVPKTINFO } break; #endif // HAVE_IPV6 default: XLOG_UNREACHABLE(); error_msg = c_format("Invalid address family %d", family()); return (XORP_ERROR); } return (XORP_OK); } int IoTcpUdpSocket::tcp_open(string& error_msg) { if (_socket_fd.is_valid()) { error_msg = c_format("The socket is already open"); return (XORP_ERROR); } _socket_fd = comm_open_tcp(family(), COMM_SOCK_NONBLOCKING); if (! _socket_fd.is_valid()) { error_msg = c_format("Cannot open the socket: %s", comm_get_last_error_str()); return (XORP_ERROR); } return (XORP_OK); } int IoTcpUdpSocket::udp_open(string& error_msg) { if (_socket_fd.is_valid()) { error_msg = c_format("The socket is already open"); return (XORP_ERROR); } _socket_fd = comm_open_udp(family(), COMM_SOCK_NONBLOCKING); if (! _socket_fd.is_valid()) { error_msg = c_format("Cannot open the socket: %s", comm_get_last_error_str()); return (XORP_ERROR); } return (XORP_OK); } int IoTcpUdpSocket::tcp_open_and_bind(const IPvX& local_addr, uint16_t local_port, string& error_msg) { XLOG_ASSERT(family() == local_addr.af()); if (_socket_fd.is_valid()) { error_msg = c_format("The socket is already open"); return (XORP_ERROR); } switch (family()) { case AF_INET: { struct in_addr local_in_addr; local_addr.copy_out(local_in_addr); _socket_fd = comm_bind_tcp4(&local_in_addr, htons(local_port), COMM_SOCK_NONBLOCKING); break; } #ifdef HAVE_IPV6 case AF_INET6: { struct in6_addr local_in6_addr; uint32_t pif_index = 0; // Find the physical interface index for link-local addresses if (local_addr.is_linklocal_unicast()) { pif_index = find_pif_index_by_addr(iftree(), local_addr, error_msg); if (pif_index == 0) return (XORP_ERROR); } local_addr.copy_out(local_in6_addr); _socket_fd = comm_bind_tcp6(&local_in6_addr, pif_index, htons(local_port), COMM_SOCK_NONBLOCKING); break; } #endif // HAVE_IPV6 default: error_msg = c_format("Address family %d is not supported", family()); return (XORP_ERROR); } if (! _socket_fd.is_valid()) { error_msg = c_format("Cannot open and bind the socket: %s", comm_get_last_error_str()); return (XORP_ERROR); } // // XXX: Don't enable receiving of data (yet). // This will happen after we start listen() on the socket, and // a new connection request is accept()-ed and allowed by the receiver. // return (XORP_OK); } int IoTcpUdpSocket::udp_open_and_bind(const IPvX& local_addr, uint16_t local_port, const string& local_dev, int reuse_addr, string& error_msg) { XLOG_ASSERT(family() == local_addr.af()); if (_socket_fd.is_valid()) { error_msg = c_format("The socket is already open"); return (XORP_ERROR); } switch (family()) { case AF_INET: { struct in_addr local_in_addr; local_addr.copy_out(local_in_addr); _socket_fd = comm_bind_udp4(&local_in_addr, htons(local_port), COMM_SOCK_NONBLOCKING, reuse_addr); #ifdef SO_BINDTODEVICE if (local_dev.size()) { if (setsockopt(_socket_fd, SOL_SOCKET, SO_BINDTODEVICE, local_dev.c_str(), local_dev.size() + 1)) { XLOG_WARNING("ERROR: IoTcpUdpSocket::udp_open_and_bind, setsockopt (BINDTODEVICE): failed: %s", XSTRERROR); } else { XLOG_INFO("NOTE: Successfully bound socket: %i to vif: %s\n", (int)(_socket_fd), local_dev.c_str()); } } #else UNUSED(local_dev); #endif break; } #ifdef HAVE_IPV6 case AF_INET6: { struct in6_addr local_in6_addr; uint32_t pif_index = 0; // Find the physical interface index for link-local addresses if (local_addr.is_linklocal_unicast()) { pif_index = find_pif_index_by_addr(iftree(), local_addr, error_msg); if (pif_index == 0) return (XORP_ERROR); } local_addr.copy_out(local_in6_addr); _socket_fd = comm_bind_udp6(&local_in6_addr, pif_index, htons(local_port), COMM_SOCK_NONBLOCKING); break; } #endif // HAVE_IPV6 default: error_msg = c_format("Address family %d is not supported", family()); return (XORP_ERROR); } if (! _socket_fd.is_valid()) { error_msg = c_format("Cannot open and bind the socket: %s", comm_get_last_error_str()); return (XORP_ERROR); } return (enable_data_recv(error_msg)); } int IoTcpUdpSocket::udp_open_bind_join(const IPvX& local_addr, uint16_t local_port, const IPvX& mcast_addr, uint8_t ttl, bool reuse, string& error_msg) { XLOG_ASSERT(family() == local_addr.af()); XLOG_ASSERT(family() == mcast_addr.af()); if (_socket_fd.is_valid()) { error_msg = c_format("The socket is already open"); return (XORP_ERROR); } switch (family()) { case AF_INET: { struct in_addr local_in_addr, mcast_in_addr; local_addr.copy_out(local_in_addr); mcast_addr.copy_out(mcast_in_addr); _socket_fd = comm_bind_join_udp4(&mcast_in_addr, &local_in_addr, htons(local_port), reuse, COMM_SOCK_NONBLOCKING); if (! _socket_fd.is_valid()) { error_msg = c_format("Cannot open, bind and join the socket: %s", comm_get_last_error_str()); return (XORP_ERROR); } // Set the default interface for outgoing multicast if (comm_set_iface4(_socket_fd, &local_in_addr) != XORP_OK) { error_msg = c_format("Cannot set the default multicast interface: " "%s", comm_get_last_error_str()); comm_close(_socket_fd); _socket_fd.clear(); return (XORP_ERROR); } break; } #ifdef HAVE_IPV6 case AF_INET6: { struct in6_addr mcast_in6_addr; uint32_t pif_index = 0; // Find the physical interface index pif_index = find_pif_index_by_addr(iftree(), local_addr, error_msg); if (pif_index == 0) return (XORP_ERROR); mcast_addr.copy_out(mcast_in6_addr); _socket_fd = comm_bind_join_udp6(&mcast_in6_addr, pif_index, htons(local_port), reuse, COMM_SOCK_NONBLOCKING); if (! _socket_fd.is_valid()) { error_msg = c_format("Cannot open, bind and join the socket: %s", comm_get_last_error_str()); return (XORP_ERROR); } // Set the default interface for outgoing multicast if (comm_set_iface6(_socket_fd, pif_index) != XORP_OK) { error_msg = c_format("Cannot set the default multicast interface: " "%s", comm_get_last_error_str()); comm_close(_socket_fd); _socket_fd.clear(); return (XORP_ERROR); } break; } #endif // HAVE_IPV6 default: error_msg = c_format("Address family %d is not supported", family()); return (XORP_ERROR); } if (comm_set_multicast_ttl(_socket_fd, ttl) != XORP_OK) { error_msg = c_format("Cannot set the multicast TTL: %s", comm_get_last_error_str()); comm_close(_socket_fd); _socket_fd.clear(); return (XORP_ERROR); } if (comm_set_loopback(_socket_fd, 0) != XORP_OK) { error_msg = c_format("Cannot disable multicast loopback: %s", comm_get_last_error_str()); comm_close(_socket_fd); _socket_fd.clear(); return (XORP_ERROR); } return (enable_data_recv(error_msg)); } int IoTcpUdpSocket::tcp_open_bind_connect(const IPvX& local_addr, uint16_t local_port, const IPvX& remote_addr, uint16_t remote_port, string& error_msg) { int in_progress = 0; XLOG_ASSERT(family() == local_addr.af()); XLOG_ASSERT(family() == remote_addr.af()); if (_socket_fd.is_valid()) { error_msg = c_format("The socket is already open"); return (XORP_ERROR); } switch (family()) { case AF_INET: { struct in_addr local_in_addr, remote_in_addr; local_addr.copy_out(local_in_addr); remote_addr.copy_out(remote_in_addr); _socket_fd = comm_bind_connect_tcp4(&local_in_addr, htons(local_port), &remote_in_addr, htons(remote_port), COMM_SOCK_NONBLOCKING, &in_progress); break; } #ifdef HAVE_IPV6 case AF_INET6: { struct in6_addr local_in6_addr, remote_in6_addr; uint32_t pif_index = 0; // Find the physical interface index for link-local addresses if (local_addr.is_linklocal_unicast()) { pif_index = find_pif_index_by_addr(iftree(), local_addr, error_msg); if (pif_index == 0) return (XORP_ERROR); } local_addr.copy_out(local_in6_addr); remote_addr.copy_out(remote_in6_addr); _socket_fd = comm_bind_connect_tcp6(&local_in6_addr, pif_index, htons(local_port), &remote_in6_addr, htons(remote_port), COMM_SOCK_NONBLOCKING, &in_progress); break; } #endif // HAVE_IPV6 default: error_msg = c_format("Address family %d is not supported", family()); return (XORP_ERROR); } if (! _socket_fd.is_valid()) { error_msg = c_format("Cannot open, bind and connect the socket: %s", comm_get_last_error_str()); return (XORP_ERROR); } // Add the socket to the eventloop if (eventloop().add_ioevent_cb(_socket_fd, IOT_CONNECT, callback(this, &IoTcpUdpSocket::connect_io_cb)) != true) { error_msg = c_format("Failed to add I/O callback to complete outgoing connection"); return (XORP_ERROR); } return (XORP_OK); } int IoTcpUdpSocket::udp_open_bind_connect(const IPvX& local_addr, uint16_t local_port, const IPvX& remote_addr, uint16_t remote_port, string& error_msg) { int in_progress = 0; XLOG_ASSERT(family() == local_addr.af()); XLOG_ASSERT(family() == remote_addr.af()); if (_socket_fd.is_valid()) { error_msg = c_format("The socket is already open"); return (XORP_ERROR); } switch (family()) { case AF_INET: { struct in_addr local_in_addr, remote_in_addr; local_addr.copy_out(local_in_addr); remote_addr.copy_out(remote_in_addr); _socket_fd = comm_bind_connect_udp4(&local_in_addr, htons(local_port), &remote_in_addr, htons(remote_port), COMM_SOCK_NONBLOCKING, &in_progress); break; } #ifdef HAVE_IPV6 case AF_INET6: { struct in6_addr local_in6_addr, remote_in6_addr; uint32_t pif_index = 0; // Find the physical interface index for link-local addresses if (local_addr.is_linklocal_unicast()) { pif_index = find_pif_index_by_addr(iftree(), local_addr, error_msg); if (pif_index == 0) return (XORP_ERROR); } local_addr.copy_out(local_in6_addr); remote_addr.copy_out(remote_in6_addr); _socket_fd = comm_bind_connect_udp6(&local_in6_addr, pif_index, htons(local_port), &remote_in6_addr, htons(remote_port), COMM_SOCK_NONBLOCKING, &in_progress); break; } #endif // HAVE_IPV6 default: error_msg = c_format("Address family %d is not supported", family()); return (XORP_ERROR); } if (! _socket_fd.is_valid()) { error_msg = c_format("Cannot open, bind and connect the socket: %s", comm_get_last_error_str()); return (XORP_ERROR); } return (enable_data_recv(error_msg)); } int IoTcpUdpSocket::udp_open_bind_broadcast(const string& ifname, const string& vifname, uint16_t local_port, uint16_t remote_port, bool reuse, bool limited, bool connected, string& error_msg) { int ret_value = XORP_OK; if (_socket_fd.is_valid()) { error_msg = c_format("The socket is already open"); return (XORP_ERROR); } // 0. Before doing anything else, we need to look up the first // configured network broadcast address on ifname/vifname. // If there is no IPv4 stack configured on that node, then // we need to reject this request outright. const IfTreeInterface* ifp = NULL; const IfTreeVif* vifp = NULL; ifp = iftree().find_interface(ifname); if (ifp == NULL) { error_msg = c_format("No interface %s", ifname.c_str()); return (XORP_ERROR); } vifp = ifp->find_vif(vifname); if (vifp == NULL) { error_msg = c_format("No interface %s vif %s", ifname.c_str(), vifname.c_str()); return (XORP_ERROR); } if (! ifp->enabled()) { error_msg = c_format("Interface %s is down", ifp->ifname().c_str()); return (XORP_ERROR); } if (! vifp->enabled()) { error_msg = c_format("Interface %s vif %s is down", ifp->ifname().c_str(), vifp->vifname().c_str()); return (XORP_ERROR); } if (! vifp->broadcast()) { error_msg = c_format("Interface %s vif %s is not broadcast capable", ifp->ifname().c_str(), vifp->vifname().c_str()); return (XORP_ERROR); } // Find the first IPv4 broadcast address configured on vif. bool is_found = false; for (IfTreeVif::IPv4Map::const_iterator ai = vifp->ipv4addrs().begin(); ai != vifp->ipv4addrs().end(); ++ai) { IfTreeAddr4& ar = *(ai->second); if (ar.enabled() && ar.broadcast()) { _network_broadcast_address = ar.bcast(); is_found = true; break; } } if (! is_found) { error_msg = c_format("Interface %s vif %s has no configured " "IPv4 network broadcast address", ifp->ifname().c_str(), vifp->vifname().c_str()); return (XORP_ERROR); } // 1. Open a UDP socket. _socket_fd = comm_open_udp(family(), COMM_SOCK_NONBLOCKING); if (! _socket_fd.is_valid()) { error_msg = c_format("Cannot open the socket: %s", comm_get_last_error_str()); return (XORP_ERROR); } // 2a. On BSD derived systems, request port re-use (SO_REUSEPORT). // This is a no-op on systems which do not have it, // and harmless on systems which do not require it. if (reuse) { ret_value = comm_set_reuseport(_socket_fd, 1); if (ret_value != XORP_OK) { error_msg = c_format("Cannot enable port re-use: %s", comm_get_last_error_str()); return (XORP_ERROR); } ret_value = comm_set_reuseaddr(_socket_fd, 1); if (ret_value != XORP_OK) { error_msg = c_format("Cannot enable address re-use: %s", comm_get_last_error_str()); return (XORP_ERROR); } } // 2b. On Linux, bind socket to device (SO_BINDTODEVICE). if (comm_bindtodevice_present() == XORP_OK) { ret_value = comm_set_bindtodevice(_socket_fd, vifp->vifname().c_str()); if (ret_value != XORP_OK) { error_msg = c_format("Cannot bind the broadcast socket to " "the underlying vif %s: %s", vifp->vifname().c_str(), comm_get_last_error_str()); return (XORP_ERROR); } } // 3. Bind to address and port (bind()). // On BSD derived systems, if an interface address is specified // for the bind(), received broadcast datagrams will NOT be delivered // to the socket. This behaviour is so old, it's taken for granted, // although it could be argued it's buggy; therefore on such systems // INADDR_ANY is used. struct in_addr local_in_addr; local_in_addr.s_addr = INADDR_ANY; ret_value = comm_sock_bind4(_socket_fd, &local_in_addr, htons(local_port)); if (ret_value != XORP_OK) { error_msg = c_format("Cannot bind the broadcast socket: %s", comm_get_last_error_str()); return (XORP_ERROR); } // 4. Set outgoing ttl to 1 (IP_TTL). // This is the default in order to prevent packet storms. if (comm_unicast_ttl_present() == XORP_OK) { ret_value = comm_set_unicast_ttl(_socket_fd, 1); if (ret_value != XORP_OK) { error_msg = c_format("Cannot set TTL: %s", comm_get_last_error_str()); return (XORP_ERROR); } } // 5a. Enable socket for broadcast send (SO_BROADCAST). // Most implementations require this. ret_value = comm_set_send_broadcast(_socket_fd, 1); if (ret_value != XORP_OK) { error_msg = c_format("Cannot enable broadcast sends: %s", comm_get_last_error_str()); return (XORP_ERROR); } #ifdef notyet // 5b. On Windows Server 2003 and up, IP_RECEIVE_BROADCAST also // needs to be set to receive broadcast datagrams. if (comm_receive_broadcast_present() == XORP_OK) { ret_value = comm_set_receive_broadcast(_socket_fd, 1); if (ret_value != XORP_OK) { error_msg = c_format("Cannot enable broadcast receives: %s", comm_get_last_error_str()); return (XORP_ERROR); } } #endif // 6. On BSD derived systems, if we are going to send to // the limited broadcast address, request IP_ONESBCAST. // This rewrites the network broadcast address to the // limited broadcast address on each send. // // However we need to record the network address in // use at the time of binding. This has the unfortunate // side-effect that if the configured network broadcast // address on the underlying ifnet changes, the mapping // will break. // // [It also means that if we need to connect() the socket // to bind its faddr tuple in the inpcb, we need to specify // the network broadcast address, NOT the limited broadcast // address.] if (limited) { if (comm_onesbcast_present() == XORP_OK) { ret_value = comm_set_onesbcast(_socket_fd, 1); if (ret_value != XORP_OK) { error_msg = c_format("Cannot enable IP_ONESBCAST: %s", comm_get_last_error_str()); return (XORP_ERROR); } _limited_broadcast_enabled = true; debug_msg("enabled onesbcast on fd %s\n", _socket_fd.str().c_str()); //XLOG_WARNING("enabled onesbcast on fd %s\n", // _socket_fd.str().c_str()); } } // Finally, if the creator requested a connected broadcast socket, // make sure the socket is connected to the appropriate address. if (connected) { struct in_addr remote_in_addr; int in_progress = 0; if (limited) { if (comm_onesbcast_present() == XORP_OK && _limited_broadcast_enabled) { // IP_ONESBCAST platform. // connect() to network broadcast address, but ensure // translation is performed for sendto(). _network_broadcast_address.copy_out(remote_in_addr); } else { // Not an IP_ONESBCAST platform. // connect() to limited broadcast address. // XXX This is untested on Windows. IPv4::ALL_ONES().copy_out(remote_in_addr); } } else { // connect() to network broadcast address. _network_broadcast_address.copy_out(remote_in_addr); } ret_value = comm_sock_connect4(_socket_fd, &remote_in_addr, htons(remote_port), COMM_SOCK_NONBLOCKING, &in_progress); if (ret_value != XORP_OK) { error_msg = c_format("Cannot connect the broadcast socket: %s", comm_get_last_error_str()); return (XORP_ERROR); } } return (enable_data_recv(error_msg)); } int IoTcpUdpSocket::bind(const IPvX& local_addr, uint16_t local_port, string& error_msg) { int ret_value = XORP_OK; XLOG_ASSERT(family() == local_addr.af()); if (! _socket_fd.is_valid()) { error_msg = c_format("The socket is not open"); return (XORP_ERROR); } switch (family()) { case AF_INET: { struct in_addr local_in_addr; local_addr.copy_out(local_in_addr); ret_value = comm_sock_bind4(_socket_fd, &local_in_addr, htons(local_port)); break; } #ifdef HAVE_IPV6 case AF_INET6: { struct in6_addr local_in6_addr; uint32_t pif_index = 0; // Find the physical interface index for link-local addresses if (local_addr.is_linklocal_unicast()) { pif_index = find_pif_index_by_addr(iftree(), local_addr, error_msg); if (pif_index == 0) return (XORP_ERROR); } local_addr.copy_out(local_in6_addr); ret_value = comm_sock_bind6(_socket_fd, &local_in6_addr, pif_index, htons(local_port)); break; } #endif // HAVE_IPV6 default: error_msg = c_format("Address family %d is not supported", family()); return (XORP_ERROR); } if (ret_value != XORP_OK) { error_msg = c_format("Cannot bind the socket: %s", comm_get_last_error_str()); return (XORP_ERROR); } return (XORP_OK); } int IoTcpUdpSocket::udp_join_group(const IPvX& mcast_addr, const IPvX& join_if_addr, string& error_msg) { int ret_value = XORP_OK; XLOG_ASSERT(family() == mcast_addr.af()); XLOG_ASSERT(family() == join_if_addr.af()); if (! _socket_fd.is_valid()) { error_msg = c_format("The socket is not open"); return (XORP_ERROR); } switch (family()) { case AF_INET: { struct in_addr local_in_addr, mcast_in_addr; join_if_addr.copy_out(local_in_addr); mcast_addr.copy_out(mcast_in_addr); ret_value = comm_sock_join4(_socket_fd, &mcast_in_addr, &local_in_addr); break; } #ifdef HAVE_IPV6 case AF_INET6: { struct in6_addr mcast_in6_addr; uint32_t pif_index = 0; // Find the physical interface index pif_index = find_pif_index_by_addr(iftree(), join_if_addr, error_msg); if (pif_index == 0) return (XORP_ERROR); mcast_addr.copy_out(mcast_in6_addr); ret_value = comm_sock_join6(_socket_fd, &mcast_in6_addr, pif_index); break; } #endif // HAVE_IPV6 default: error_msg = c_format("Address family %d is not supported", family()); return (XORP_ERROR); } if (ret_value != XORP_OK) { error_msg = c_format("Cannot join on the socket: %s", comm_get_last_error_str()); return (XORP_ERROR); } return (XORP_OK); } int IoTcpUdpSocket::udp_leave_group(const IPvX& mcast_addr, const IPvX& leave_if_addr, string& error_msg) { int ret_value = XORP_OK; XLOG_ASSERT(family() == mcast_addr.af()); XLOG_ASSERT(family() == leave_if_addr.af()); if (! _socket_fd.is_valid()) { error_msg = c_format("The socket is not open"); return (XORP_ERROR); } switch (family()) { case AF_INET: { struct in_addr local_in_addr, mcast_in_addr; leave_if_addr.copy_out(local_in_addr); mcast_addr.copy_out(mcast_in_addr); ret_value = comm_sock_leave4(_socket_fd, &mcast_in_addr, &local_in_addr); break; } #ifdef HAVE_IPV6 case AF_INET6: { struct in6_addr mcast_in6_addr; uint32_t pif_index = 0; // Find the physical interface index pif_index = find_pif_index_by_addr(iftree(), leave_if_addr, error_msg); if (pif_index == 0) return (XORP_ERROR); mcast_addr.copy_out(mcast_in6_addr); ret_value = comm_sock_leave6(_socket_fd, &mcast_in6_addr, pif_index); break; } #endif // HAVE_IPV6 default: error_msg = c_format("Address family %d is not supported", family()); return (XORP_ERROR); } if (ret_value != XORP_OK) { error_msg = c_format("Cannot leave on the socket: %s", comm_get_last_error_str()); return (XORP_ERROR); } return (XORP_OK); } int IoTcpUdpSocket::close(string& error_msg) { if (! _socket_fd.is_valid()) { error_msg = c_format("The socket is not open"); return (XORP_ERROR); } // Remove it just in case, even though it may not be select()-ed eventloop().remove_ioevent_cb(_socket_fd); // Delete the async writer if (_async_writer != NULL) { _async_writer->stop(); _async_writer->flush_buffers(); delete _async_writer; _async_writer = NULL; } if (comm_close(_socket_fd) != XORP_OK) { error_msg = c_format("Cannot close the socket: %s", comm_get_last_error_str()); return (XORP_ERROR); } _socket_fd.clear(); return (XORP_OK); } int IoTcpUdpSocket::tcp_listen(uint32_t backlog, string& error_msg) { if (! _socket_fd.is_valid()) { error_msg = c_format("The socket is not open"); return (XORP_ERROR); } if (comm_listen(_socket_fd, backlog) != XORP_OK) { error_msg = c_format("Cannot listen to the socket: %s", comm_get_last_error_str()); return (XORP_ERROR); } // Add the socket to the eventloop if (eventloop().add_ioevent_cb(_socket_fd, IOT_ACCEPT, callback(this, &IoTcpUdpSocket::accept_io_cb)) != true) { error_msg = c_format("Failed to add I/O callback to accept connections"); return (XORP_ERROR); } return (XORP_OK); } int IoTcpUdpSocket::udp_enable_recv(string& error_msg) { return (enable_data_recv(error_msg)); } int IoTcpUdpSocket::send(const vector& data, string& error_msg) { if (! _socket_fd.is_valid()) { error_msg = c_format("The socket is not open"); return (XORP_ERROR); } // Allocate the async writer if (_async_writer == NULL) { // // XXX: Don't coalesce the buffers. // Note that we shouldn't coalesce for UDP, because it might break // the semantics of protocol control packets that use UDP. // We don't coalesce for TCP as well, but this could be changed in the // future if it improves performance. // int coalesce_buffers_n = 1; _async_writer = new AsyncFileWriter(eventloop(), _socket_fd, coalesce_buffers_n); } // Queue the data for transmission _async_writer->add_data(data, callback(this, &IoTcpUdpSocket::send_completed_cb)); _async_writer->start(); return (XORP_OK); } int IoTcpUdpSocket::send_to(const IPvX& remote_addr, uint16_t remote_port, const vector& data, string& error_msg) { XLOG_ASSERT(family() == remote_addr.af()); if (! _socket_fd.is_valid()) { error_msg = c_format("The socket is not open"); return (XORP_ERROR); } // Allocate the async writer if (_async_writer == NULL) { // // XXX: Don't coalesce the buffers. // Note that we shouldn't coalesce for UDP, because it might break // the semantics of protocol control packets that use UDP. // We don't coalesce for TCP as well, but this could be changed in the // future if it improves performance. // int coalesce_buffers_n = 1; _async_writer = new AsyncFileWriter(eventloop(), _socket_fd, coalesce_buffers_n); } // Queue the data for transmission. if (_limited_broadcast_enabled && comm_onesbcast_present() == XORP_OK && remote_addr == IPv4::ALL_ONES()) { // If this is an IPv4 limited broadcast socket on a platform which // uses the IP_ONESBCAST socket option, we must trap sends to // the limited broadcast address, and rewrite them to use the // network broadcast address. debug_msg("onesbcast enabled on fd %s, rewriting %s to %s.\n", _socket_fd.str().c_str(), remote_addr.str().c_str(), _network_broadcast_address.str().c_str()); //XLOG_WARNING("onesbcast enabled on fd %s, rewriting %s to %s.\n", // _socket_fd.str().c_str(), // remote_addr.str().c_str(), // _network_broadcast_address.str().c_str()); _async_writer->add_data_sendto(data, _network_broadcast_address, remote_port, callback(this, &IoTcpUdpSocket::send_completed_cb)); } else { _async_writer->add_data_sendto(data, remote_addr, remote_port, callback(this, &IoTcpUdpSocket::send_completed_cb)); } _async_writer->start(); return (XORP_OK); } void IoTcpUdpSocket::send_completed_cb(AsyncFileWriter::Event event, const uint8_t* buffer, size_t buffer_bytes, size_t offset) { string error_msg; UNUSED(buffer); UNUSED(buffer_bytes); UNUSED(offset); switch (event) { case AsyncFileWriter::DATA: // I/O occured XLOG_ASSERT(offset <= buffer_bytes); break; case AsyncFileWriter::FLUSHING: // Buffer is being flushed break; case AsyncFileWriter::OS_ERROR: // I/O error has occured error_msg = c_format("Failed to send data: Unknown I/O error"); if (io_tcpudp_receiver() != NULL) io_tcpudp_receiver()->error_event(error_msg, true); break; case AsyncFileWriter::END_OF_FILE: // End of file reached (applies to read only) XLOG_UNREACHABLE(); break; case AsyncFileWriter::WOULDBLOCK: // I/O would block the current thread break; } } int IoTcpUdpSocket::send_from_multicast_if(const IPvX& group_addr, uint16_t group_port, const IPvX& ifaddr, const vector& data, string& error_msg) { int ret_value = XORP_OK; XLOG_ASSERT(family() == group_addr.af()); XLOG_ASSERT(family() == ifaddr.af()); if (! _socket_fd.is_valid()) { error_msg = c_format("The socket is not open"); return (XORP_ERROR); } switch (family()) { case AF_INET: { struct in_addr ifaddr_in_addr; ifaddr.copy_out(ifaddr_in_addr); // TODO: Not needed if we are binding to specific ports. ret_value = comm_set_iface4(_socket_fd, &ifaddr_in_addr); break; } #ifdef HAVE_IPV6 case AF_INET6: { uint32_t pif_index = 0; // Find the physical interface index pif_index = find_pif_index_by_addr(iftree(), ifaddr, error_msg); if (pif_index == 0) return (XORP_ERROR); ret_value = comm_set_iface6(_socket_fd, pif_index); break; } #endif // HAVE_IPV6 default: error_msg = c_format("Address family %d is not supported", family()); return (XORP_ERROR); } if (ret_value != XORP_OK) { error_msg = c_format("Failed to set the multicast interface: %s", comm_get_last_error_str()); return (XORP_ERROR); } return (send_to(group_addr, group_port, data, error_msg)); } int IoTcpUdpSocket::set_socket_option(const string& optname, uint32_t optval, string& error_msg) { int ret_value = XORP_OK; if (! _socket_fd.is_valid()) { error_msg = c_format("The socket is not open"); return (XORP_ERROR); } do { if (strcasecmp(optname.c_str(), "onesbcast") == 0) { ret_value = comm_set_onesbcast(_socket_fd, optval); break; } if (strcasecmp(optname.c_str(), "receive_broadcast") == 0) { ret_value = comm_set_receive_broadcast(_socket_fd, optval); break; } if (strcasecmp(optname.c_str(), "reuseport") == 0) { ret_value = comm_set_reuseport(_socket_fd, optval); break; } if (strcasecmp(optname.c_str(), "send_broadcast") == 0) { ret_value = comm_set_send_broadcast(_socket_fd, optval); break; } if (strcasecmp(optname.c_str(), "tos") == 0) { // XXX: Do not return an error if setting the // type-of-service fields is not supported. if (comm_tos_present() == XORP_OK) { ret_value = comm_set_tos(_socket_fd, optval); } else { ret_value = XORP_OK; } break; } if (strcasecmp(optname.c_str(), "ttl") == 0) { ret_value = comm_set_unicast_ttl(_socket_fd, optval); break; } if (strcasecmp(optname.c_str(), "multicast_loopback") == 0) { ret_value = comm_set_loopback(_socket_fd, optval); break; } if (strcasecmp(optname.c_str(), "multicast_ttl") == 0) { ret_value = comm_set_multicast_ttl(_socket_fd, optval); break; } error_msg = c_format("Unknown socket option: %s", optname.c_str()); return (XORP_ERROR); } while (false); if (ret_value != XORP_OK) { error_msg = c_format("Failed to set socket option %s: %s", optname.c_str(), comm_get_last_error_str()); return (XORP_ERROR); } return (XORP_OK); } int IoTcpUdpSocket::set_socket_option(const string& optname, const string& optval, string& error_msg) { int ret_value = XORP_OK; if (! _socket_fd.is_valid()) { error_msg = c_format("The socket is not open"); return (XORP_ERROR); } do { if (strcasecmp(optname.c_str(), "bindtodevice") == 0) { // XXX: Do not use this option for new code; see note in // comm_set_bindtodevice(). if (comm_bindtodevice_present() == XORP_OK) { ret_value = comm_set_bindtodevice(_socket_fd, optval.c_str()); } else { ret_value = XORP_OK; } break; } error_msg = c_format("Unknown socket option: %s", optname.c_str()); return (XORP_ERROR); } while (false); if (ret_value != XORP_OK) { error_msg = c_format("Failed to set socket option %s: %s", optname.c_str(), comm_get_last_error_str()); return (XORP_ERROR); } return (XORP_OK); } int IoTcpUdpSocket::accept_connection(bool is_accepted, string& error_msg) { if (is_accepted) { // Connection accepted if (! is_running()) { error_msg = c_format("Cannot accept connection: " "the plugin is not running"); return (XORP_ERROR); } return (enable_data_recv(error_msg)); } // Connection rejected return (stop(error_msg)); } int IoTcpUdpSocket::enable_data_recv(string& error_msg) { string dummy_error_msg; if (! is_running()) { error_msg = c_format("Cannot enable receiving of data: " "the plugin is not running"); return (XORP_ERROR); } if (! _socket_fd.is_valid()) { error_msg = c_format("Cannot enable receiving of data: " "invalid socket"); stop(dummy_error_msg); return (XORP_ERROR); } // Show interest in receiving additional information if (enable_recv_pktinfo(true, error_msg) != XORP_OK) { error_msg = c_format("Cannot enable receiving of data: %s", error_msg.c_str()); stop(dummy_error_msg); return (XORP_ERROR); } // Get the peer address and port for TCP connection if (is_tcp()) { // Get the peer address and port struct sockaddr_storage ss; socklen_t ss_len = sizeof(ss); if (getpeername(_socket_fd, reinterpret_cast(&ss), &ss_len) != 0) { error_msg = c_format("Cannot get the peer name: %s", XSTRERROR); stop(dummy_error_msg); return (XORP_ERROR); } XLOG_ASSERT(ss.ss_family == family()); _peer_address.copy_in(ss); _peer_port = get_sockadr_storage_port_number(ss); } if (eventloop().add_ioevent_cb(_socket_fd, IOT_READ, callback(this, &IoTcpUdpSocket::data_io_cb)) != true) { error_msg = c_format("Failed to add I/O callback to receive data"); stop(dummy_error_msg); return (XORP_ERROR); } #ifdef HOST_OS_WINDOWS // XXX: IOT_DISCONNECT is available only on Windows if (is_tcp()) { if (eventloop().add_ioevent_cb(_socket_fd, IOT_DISCONNECT, callback(this, &IoTcpUdpSocket::disconnect_io_cb)) != true) { error_msg = c_format("Failed to add I/O callback to detect peer disconnect"); stop(dummy_error_msg); return (XORP_ERROR); } } #endif // HOST_OS_WINDOWS return (XORP_OK); } void IoTcpUdpSocket::accept_io_cb(XorpFd fd, IoEventType io_event_type) { XorpFd accept_fd; struct sockaddr_storage ss; socklen_t ss_len = sizeof(ss); string error_msg; XLOG_ASSERT(fd == _socket_fd); UNUSED(io_event_type); // // Test whether there is a registered receiver // if (io_tcpudp_receiver() == NULL) { // // XXX: Accept the connection and close it. // This might happen only during startup and should be transient. // XLOG_WARNING("Received connection request, but no receiver is " "registered. Ignoring..."); accept_fd = comm_sock_accept(_socket_fd); if (accept_fd.is_valid()) comm_close(accept_fd); return; } // // Accept the connection // accept_fd = comm_sock_accept(_socket_fd); if (! accept_fd.is_valid()) { io_tcpudp_receiver()->error_event(comm_get_last_error_str(), false); return; } // // Get the peer address and port number // if (getpeername(accept_fd, sockaddr_storage2sockaddr(&ss), &ss_len) != 0) { error_msg = c_format("Error getting the peer name: %s", XSTRERROR); comm_close(accept_fd); io_tcpudp_receiver()->error_event(error_msg, false); return; } XLOG_ASSERT(ss.ss_family == family()); // // Set the socket as non-blocking // if (comm_sock_set_blocking(accept_fd, COMM_SOCK_NONBLOCKING) != XORP_OK) { error_msg = c_format("Error setting the socket as non-blocking: %s", comm_get_last_error_str()); comm_close(accept_fd); io_tcpudp_receiver()->error_event(error_msg, false); return; } IPvX src_host(ss); uint16_t src_port = get_sockadr_storage_port_number(ss); // // Allocate a new handler and start it // IoTcpUdp* io_tcpudp; IoTcpUdpSocket* io_tcpudp_socket; io_tcpudp = fea_data_plane_manager().allocate_io_tcpudp(iftree(), family(), is_tcp()); if (io_tcpudp == NULL) { XLOG_ERROR("Connection request from %s rejected: " "cannot allocate I/O TCP/UDP plugin from data plane " "manager %s.", src_host.str().c_str(), fea_data_plane_manager().manager_name().c_str()); comm_close(accept_fd); return; } io_tcpudp_socket = dynamic_cast(io_tcpudp); if (io_tcpudp_socket == NULL) { XLOG_ERROR("Connection request from %s rejected: " "unrecognized I/O TCP/UDP plugin from data plane " "manager %s.", src_host.str().c_str(), fea_data_plane_manager().manager_name().c_str()); fea_data_plane_manager().deallocate_io_tcpudp(io_tcpudp); comm_close(accept_fd); return; } io_tcpudp_socket->set_socket_fd(accept_fd); // // Send the event to the receiver // io_tcpudp_receiver()->inbound_connect_event(src_host, src_port, io_tcpudp); } void IoTcpUdpSocket::connect_io_cb(XorpFd fd, IoEventType io_event_type) { string error_msg; int is_connected = 0; XLOG_ASSERT(fd == _socket_fd); UNUSED(io_event_type); // // Test whether there is a registered receiver // if (io_tcpudp_receiver() == NULL) { // // This might happen only during startup and should be transient. // XLOG_WARNING("Connection opening to the peer has completed, " "but no receiver is registered."); return; } // // XXX: Remove from the eventloop for connect events. // eventloop().remove_ioevent_cb(_socket_fd, IOT_CONNECT); // Test whether the connection succeeded if (comm_sock_is_connected(_socket_fd, &is_connected) != XORP_OK) { io_tcpudp_receiver()->error_event(comm_get_last_error_str(), true); return; } if (is_connected == 0) { // Socket is not connected error_msg = c_format("Socket connect failed"); io_tcpudp_receiver()->error_event(error_msg, true); return; } if (enable_data_recv(error_msg) != XORP_OK) { io_tcpudp_receiver()->error_event(error_msg, true); return; } // // Send the event to the receiver // io_tcpudp_receiver()->outgoing_connect_event(); } void IoTcpUdpSocket::data_io_cb(XorpFd fd, IoEventType io_event_type) { string if_name; string vif_name; IPvX src_host = IPvX::ZERO(family()); uint16_t src_port = 0; vector data(0xffff); // XXX: The max. payload is 0xffff ssize_t bytes_recv = 0; uint32_t pif_index = 0; bool use_recvmsg = false; string error_msg; XLOG_ASSERT(fd == _socket_fd); UNUSED(io_event_type); // // Test whether there is a registered receiver // if (io_tcpudp_receiver() == NULL) { // // This might happen only during startup and should be transient. // XLOG_WARNING("Received data, but no receiver is registered."); return; } // // Decide whether to use recfrom(2) or recvmsg(2): // - If Windows, use recvfrom(2) // - On all other systems use recvfrom(2) for TCP, and recvmsg(2) for UDP // #ifndef HOST_OS_WINDOWS if (! is_tcp()) { use_recvmsg = true; } #endif // // Receive the data // if (! use_recvmsg) { struct sockaddr_storage ss; socklen_t ss_len = sizeof(ss); bytes_recv = recvfrom(_socket_fd, XORP_BUF_CAST(&data[0]), data.size(), 0, reinterpret_cast(&ss), &ss_len); if (bytes_recv < 0) { error_msg = c_format("Error receiving TCP/UDP data on " "socket %s: %s", _socket_fd.str().c_str(), XSTRERROR); io_tcpudp_receiver()->error_event(error_msg, false); return; } // // Protocol-specific processing: // - Get the sender's address and port // if (is_tcp()) { // TCP data src_host = _peer_address; src_port = _peer_port; } else { // UDP data src_host.copy_in(ss); src_port = get_sockadr_storage_port_number(ss); } } else { #ifdef HOST_OS_WINDOWS XLOG_UNREACHABLE(); #else // // XXX: Use recvmsg(2) to receive additional information // struct msghdr rcvmh; struct iovec rcviov[1]; vector rcvcmsgbuf(0xffff); void* cmsg_data; // XXX: CMSG_DATA() is aligned, hence void ptr rcviov[0].iov_base = reinterpret_cast(&data[0]); rcviov[0].iov_len = data.size(); rcvmh.msg_iov = rcviov; rcvmh.msg_iovlen = 1; rcvmh.msg_control = reinterpret_cast(&rcvcmsgbuf[0]); rcvmh.msg_controllen = rcvcmsgbuf.size(); switch (family()) { case AF_INET: { struct sockaddr_in from4; memset(&from4, 0, sizeof(from4)); rcvmh.msg_name = reinterpret_cast(&from4); rcvmh.msg_namelen = sizeof(from4); bytes_recv = recvmsg(_socket_fd, &rcvmh, 0); if (bytes_recv < 0) { error_msg = c_format("Error receiving TCP/UDP data on " "socket %s: %s", _socket_fd.str().c_str(), XSTRERROR); io_tcpudp_receiver()->error_event(error_msg, false); return; } src_host.copy_in(from4); src_port = ntohs(from4.sin_port); // Get the pif_index for (struct cmsghdr *cmsgp = reinterpret_cast(CMSG_FIRSTHDR(&rcvmh)); cmsgp != NULL; cmsgp = reinterpret_cast(CMSG_NXTHDR(&rcvmh, cmsgp))) { if (cmsgp->cmsg_level != IPPROTO_IP) continue; switch (cmsgp->cmsg_type) { #ifdef IP_RECVIF case IP_RECVIF: { struct sockaddr_dl *sdl = NULL; if (cmsgp->cmsg_len < CMSG_LEN(sizeof(struct sockaddr_dl))) continue; cmsg_data = CMSG_DATA(cmsgp); sdl = reinterpret_cast(cmsg_data); pif_index = sdl->sdl_index; } break; #endif // IP_RECVIF #ifdef IP_PKTINFO case IP_PKTINFO: { struct in_pktinfo *inp = NULL; if (cmsgp->cmsg_len < CMSG_LEN(sizeof(struct in_pktinfo))) continue; cmsg_data = CMSG_DATA(cmsgp); inp = reinterpret_cast(cmsg_data); pif_index = inp->ipi_ifindex; } break; #endif // IP_PKTINFO default: break; } } } break; #ifdef HAVE_IPV6 case AF_INET6: { struct sockaddr_in6 from6; struct in6_pktinfo *pi = NULL; memset(&from6, 0, sizeof(from6)); rcvmh.msg_name = reinterpret_cast(&from6); rcvmh.msg_namelen = sizeof(from6); bytes_recv = recvmsg(_socket_fd, &rcvmh, 0); if (bytes_recv < 0) { error_msg = c_format("Error receiving TCP/UDP data on " "socket %s: %s", _socket_fd.str().c_str(), XSTRERROR); io_tcpudp_receiver()->error_event(error_msg, false); return; } src_host.copy_in(from6); src_port = ntohs(from6.sin6_port); if (rcvmh.msg_flags & MSG_CTRUNC) { error_msg = c_format("Error receiving TCP/UDP data on " "socket %s: " "RX packet from %s with size of %d " "bytes is truncated", _socket_fd.str().c_str(), cstring(src_host), XORP_UINT_CAST(bytes_recv)); io_tcpudp_receiver()->error_event(error_msg, false); return; } size_t controllen = static_cast(rcvmh.msg_controllen); if (controllen < sizeof(struct cmsghdr)) { error_msg = c_format("Error receiving TCP/UDP data on " "socket %s: " "RX packet from %s has too short " "msg_controllen (%u instead of %u)", _socket_fd.str().c_str(), cstring(src_host), XORP_UINT_CAST(controllen), XORP_UINT_CAST(sizeof(struct cmsghdr))); io_tcpudp_receiver()->error_event(error_msg, false); return; } // Get the pif_index for (struct cmsghdr *cmsgp = reinterpret_cast(CMSG_FIRSTHDR(&rcvmh)); cmsgp != NULL; cmsgp = reinterpret_cast(CMSG_NXTHDR(&rcvmh, cmsgp))) { if (cmsgp->cmsg_level != IPPROTO_IPV6) continue; switch (cmsgp->cmsg_type) { case IPV6_PKTINFO: { if (cmsgp->cmsg_len < CMSG_LEN(sizeof(struct in6_pktinfo))) continue; cmsg_data = CMSG_DATA(cmsgp); pi = reinterpret_cast(cmsg_data); pif_index = pi->ipi6_ifindex; // dst_address.copy_in(pi->ipi6_addr); } break; default: break; } } } break; #endif // HAVE_IPV6 default: XLOG_UNREACHABLE(); break; } #endif // ! HOST_OS_WINDOWS } data.resize(bytes_recv); // // Find the interface and the vif this message was received on. // if (pif_index != 0) { const IfTreeVif* vifp = iftree().find_vif(pif_index); if (vifp != NULL) { if_name = vifp->ifname(); vif_name = vifp->vifname(); } } // // Protocol-specific processing: // - If TCP, test whether the connection was closed by the remote host. // if (is_tcp()) { // TCP data if (bytes_recv == 0) { // // XXX: Remove from the eventloop for disconnect events. // // Note that IOT_DISCONNECT is available only on Windows, // hence we need to use IOT_READ instead. // eventloop().remove_ioevent_cb(_socket_fd, IOT_READ); io_tcpudp_receiver()->disconnect_event(); return; } } else { // UDP data } // // Send the event to the receiver // io_tcpudp_receiver()->recv_event(if_name, vif_name, src_host, src_port, data); } void IoTcpUdpSocket::disconnect_io_cb(XorpFd fd, IoEventType io_event_type) { XLOG_ASSERT(fd == _socket_fd); UNUSED(io_event_type); // // Test whether there is a registered receiver // if (io_tcpudp_receiver() == NULL) { // // This might happen only during startup and should be transient. // XLOG_WARNING("Received disconnect event, but no receiver is registered."); return; } // // XXX: Remove from the eventloop for disconnect events. // eventloop().remove_ioevent_cb(_socket_fd, IOT_DISCONNECT); io_tcpudp_receiver()->disconnect_event(); } #endif // HAVE_TCPUDP_UNIX_SOCKETS xorp/fea/data_plane/io/io_tcpudp_dummy.hh0000664000076400007640000002543711421137511020643 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2007-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/data_plane/io/io_tcpudp_dummy.hh,v 1.11 2008/10/11 04:20:19 pavlin Exp $ #ifndef __FEA_DATA_PLANE_IO_IO_TCPUDP_DUMMY_HH__ #define __FEA_DATA_PLANE_IO_IO_TCPUDP_DUMMY_HH__ // // I/O TCP/UDP communication support. // // The mechanism is Dummy (for testing purpose). // #include "fea/io_tcpudp.hh" /** * @short A base class for I/O TCP/UDP Dummy communication. */ class IoTcpUdpDummy : public IoTcpUdp { public: /** * Constructor for a given address family. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). * @param iftree the interface tree to use. * @param family the address family (AF_INET or AF_INET6 for IPv4 and IPv6 * respectively). * @param is_tcp if true this is TCP entry, otherwise UDP. */ IoTcpUdpDummy(FeaDataPlaneManager& fea_data_plane_manager, const IfTree& iftree, int family, bool is_tcp); /** * Virtual destructor. */ virtual ~IoTcpUdpDummy(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int stop(string& error_msg); /** * Open a TCP socket. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int tcp_open(string& error_msg); /** * Open an UDP socket. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int udp_open(string& error_msg); /** * Create a bound TCP socket. * * @param local_addr the interface address to bind socket to. * @param local_port the port to bind socket to. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int tcp_open_and_bind(const IPvX& local_addr, uint16_t local_port, string& error_msg); /** * Create a bound UDP socket. * * @param local_addr the interface address to bind socket to. * @param local_port the port to bind socket to. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int udp_open_and_bind(const IPvX& local_addr, uint16_t local_port, const string& local_dev, int reuse, string& error_msg); /** * Create a bound UDP multicast socket. * * @param local_addr the interface address to bind socket to. * @param local_port the port to bind socket to. * @param mcast_addr the multicast group address to join. * @param ttl the TTL to use for this multicast socket. * @param reuse allow other sockets to bind to same multicast group. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int udp_open_bind_join(const IPvX& local_addr, uint16_t local_port, const IPvX& mcast_addr, uint8_t ttl, bool reuse, string& error_msg); /** * Create a bound and connected TCP socket. * * @param local_addr the interface address to bind socket to. * @param local_port the port to bind socket to. * @param remote_addr the address to connect to. * @param remote_port the remote port to connect to. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int tcp_open_bind_connect(const IPvX& local_addr, uint16_t local_port, const IPvX& remote_addr, uint16_t remote_port, string& error_msg); /** * Create a bound and connected UDP socket. * * @param local_addr the interface address to bind socket to. * @param local_port the port to bind socket to. * @param remote_addr the address to connect to. * @param remote_port the remote port to connect to. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int udp_open_bind_connect(const IPvX& local_addr, uint16_t local_port, const IPvX& remote_addr, uint16_t remote_port, string& error_msg); /** * Create a bound, and optionally connected, UDP broadcast socket. * * @param ifname the interface name to bind socket to. * @param vifname the vif to bind socket to. * @param local_port the port to bind socket to. * @param remote_port the remote port to connect to. * @param reuse allow other sockets to bind to same port. * @param limited set the socket up for transmission to the limited * broadcast address 255.255.255.255. * @param connected connect the socket for use with send() not sendto(). * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int udp_open_bind_broadcast(const string& ifname, const string& vifname, uint16_t local_port, uint16_t remote_port, bool reuse, bool limited, bool connected, string& error_msg); /** * Bind a socket. * * @param local_addr the interface address to bind socket to. * @param local_port the port to bind socket to. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int bind(const IPvX& local_addr, uint16_t local_port, string& error_msg); /** * Join multicast group on already bound socket. * * @param mcast_addr group to join. * @param join_if_addr interface address to perform join on. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int udp_join_group(const IPvX& mcast_addr, const IPvX& join_if_addr, string& error_msg); /** * Leave multicast group on already bound socket. * * @param mcast_addr group to leave. * @param leave_if_addr interface address to perform leave on. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int udp_leave_group(const IPvX& mcast_addr, const IPvX& leave_if_addr, string& error_msg); /** * Close socket. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int close(string& error_msg); /** * Listen for inbound connections on socket. * * When a connection request is received the socket creator will receive * notification. * * @param backlog the maximum number of pending connections. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int tcp_listen(uint32_t backlog, string& error_msg); /** * Enable a UDP socket for datagram reception. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int udp_enable_recv(string& error_msg); /** * Send data on socket. * * @param data block of data to be sent. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int send(const vector& data, string& error_msg); /** * Send data on socket to a given destination. * * The packet is not routed as the forwarding engine sending the packet * may not have access to the full routing table. * * @param remote_addr destination address for data. * @param remote_port destination port for data. * @param data block of data to be sent. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int send_to(const IPvX& remote_addr, uint16_t remote_port, const vector& data, string& error_msg); /** * Send data on socket to a given multicast group from a given interface. * * @param group_addr destination address for data. * @param group_port destination port for data. * @param ifaddr interface address. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int send_from_multicast_if(const IPvX& group_addr, uint16_t group_port, const IPvX& ifaddr, const vector& data, string& error_msg); /** * Set a named socket option with an integer value. * * @param optname name of option to be set. Valid values are: * "onesbcast" (IPv4 only) * "receive_broadcast" (IPv4 only) * "reuseport" * "send_broadcast" (IPv4 only) * "tos" (IPv4 only) * "ttl" * "multicast_loopback" * "multicast_ttl" * @param optval value of option to be set. If value is logically boolean * then zero represents false and any non-zero value true. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_socket_option(const string& optname, uint32_t optval, string& error_msg); /** * Set a named socket option with a text value. * * @param optname name of option to be set. All values are ignored. * @param optval value of option to be set. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_socket_option(const string& optname, const string& optval, string& error_msg); /** * Accept or reject a pending connection. * * @param is_accepted if true, the connection is accepted, otherwise is * rejected. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int accept_connection(bool is_accepted, string& error_msg); private: }; #endif // __FEA_DATA_PLANE_IO_IO_TCPUDP_DUMMY_HH__ xorp/fea/data_plane/io/io_ip_socket.cc0000664000076400007640000023106111703345405020075 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8 sw=4: // Copyright (c) 2001-2012 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // I/O IP raw communication support. // // The mechanism is UNIX raw sockets. // #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvx.hh" #include "libxorp/ipvxnet.hh" #include "libxorp/utils.hh" #ifdef HAVE_SYS_IOCTL_H #include #endif #ifdef HAVE_NET_IF_H #include #endif #ifdef HAVE_NET_IF_VAR_H #include #endif #ifdef HAVE_NET_IF_DL_H #include #endif #ifdef HAVE_NETINET_IN_SYSTM_H #include #endif #ifdef HAVE_NETINET_IP_H #include #endif #ifdef HAVE_NETINET_IP6_H #include #endif #ifdef HAVE_NETINET_ICMP6_H #include #endif #ifdef HAVE_NETINET6_IN6_VAR_H #include #endif #ifdef HOST_OS_WINDOWS #include #include "libxorp/win_io.h" #endif #include "libcomm/comm_api.h" #include "libproto/packet.hh" #include "mrt/include/ip_mroute.h" // XXX: _PIM_VT is needed if we want the extra features of #define _PIM_VT 1 #if defined(HAVE_NETINET_PIM_H) && defined(HAVE_STRUCT_PIM_PIM_VT) #include #else #include "mrt/include/netinet/pim.h" #endif #include "fea/data_plane/control_socket/system_utilities.hh" #include "fea/iftree.hh" // // TODO: "fea/fibconfig.hh" is temporary needed to test whether the table ID // is configured. // #include "fea/fibconfig.hh" #include "io_ip_socket.hh" #ifdef HAVE_IP_RAW_SOCKETS // // 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. rcv socket buffer size #define SO_RCV_BUF_SIZE_MAX (256*1024) // Desired rcv socket buffer size #define SO_SND_BUF_SIZE_MIN (48*1024) // Min. snd socket buffer size #define SO_SND_BUF_SIZE_MAX (256*1024) // Desired snd socket buffer size #ifndef MINTTL #define MINTTL 1 #endif #ifndef IPDEFTTL #define IPDEFTTL 64 #endif #ifndef MAXTTL #define MAXTTL 255 #endif #ifndef MLD_MINLEN # ifdef HAVE_STRUCT_MLD_HDR # define MLD_MINLEN (sizeof(struct mld_hdr)) # else # define MLD_MINLEN 24 # endif #endif // // Local structures/classes, typedefs and macros // #ifdef HOST_OS_WINDOWS #define cmsghdr wsacmsghdr typedef char *caddr_t; #ifdef __MINGW32__ #ifndef _ALIGNBYTES #define _ALIGNBYTES (sizeof(int) - 1) #endif #ifndef _ALIGN #define _ALIGN(p) (((unsigned)(p) + _ALIGNBYTES) & ~_ALIGNBYTES) #endif #define CMSG_DATA(cmsg) \ ((unsigned char *)(cmsg) + _ALIGN(sizeof(struct cmsghdr))) #define CMSG_NXTHDR(mhdr, cmsg) \ (((char *)(cmsg) + _ALIGN((cmsg)->cmsg_len) + \ _ALIGN(sizeof(struct cmsghdr)) > \ (char *)(mhdr)->Control.buf + (mhdr)->Control.len) ? \ (struct cmsghdr *)0 : \ (struct cmsghdr *)((char *)(cmsg) + _ALIGN((cmsg)->cmsg_len))) #define CMSG_FIRSTHDR(mhdr) \ ((mhdr)->Control.len >= sizeof(struct cmsghdr) ? \ (struct cmsghdr *)(mhdr)->Control.buf : \ (struct cmsghdr *)NULL) #define CMSG_SPACE(l) (_ALIGN(sizeof(struct cmsghdr)) + _ALIGN(l)) #define CMSG_LEN(l) (_ALIGN(sizeof(struct cmsghdr)) + (l)) typedef INT (WINAPI * LPFN_WSARECVMSG)(SOCKET, LPWSAMSG, LPDWORD, LPWSAOVERLAPPED, LPWSAOVERLAPPED_COMPLETION_ROUTINE); typedef INT (WINAPI * LPFN_WSASENDMSG)(SOCKET, LPWSAMSG, DWORD, LPDWORD, LPWSAOVERLAPPED, LPWSAOVERLAPPED_COMPLETION_ROUTINE); #define WSAID_WSARECVMSG \ { 0xf689d7c8,0x6f1f,0x436b,{0x8a,0x53,0xe5,0x4f,0xe3,0x51,0xc3,0x22} } #define WSAID_WSASENDMSG \ { 0xa441e712,0x754f,0x43ca,{0x84,0xa7,0x0d,0xee,0x44,0xcf,0x60,0x6d} } #else // ! __MINGW32__ #define CMSG_FIRSTHDR(msg) WSA_CMSG_FIRSTHDR(msg) #define CMSG_NXTHDR(msg, cmsg) WSA_CMSG_NEXTHDR(msg, cmsg) #define CMSG_DATA(cmsg) WSA_CMSG_DATA(cmsg) #define CMSG_SPACE(len) WSA_CMSG_SPACE(len) #define CMSG_LEN(len) WSA_CMSG_LEN(len) #endif // ! __MINGW32__ #ifdef HAVE_IPV6 static const GUID guidWSARecvMsg = WSAID_WSARECVMSG; static const GUID guidWSASendMsg = WSAID_WSASENDMSG; static LPFN_WSARECVMSG lpWSARecvMsg = NULL; static LPFN_WSASENDMSG lpWSASendMsg = NULL; // Windows Longhorn and up #endif // HAVE_IPV6 #endif // HOST_OS_WINDOWS #ifndef CMSG_LEN #define CMSG_LEN(l) (ALIGN(sizeof(struct cmsghdr)) + (l)) // XXX #endif // // Local variables // // IPv4 Router Alert stuff #ifndef IPTOS_PREC_INTERNETCONTROL #define IPTOS_PREC_INTERNETCONTROL 0xc0 #endif #ifndef IPOPT_RA #define IPOPT_RA 148 // 0x94 #endif static uint32_t ra_opt4; // IPv6 Router Alert stuff #ifdef HAVE_IPV6 #ifndef IP6OPT_RTALERT #define IP6OPT_RTALERT 0x05 #endif #ifndef IP6OPT_RTALERT_LEN #define IP6OPT_RTALERT_LEN 4 #endif #ifndef IP6OPT_RTALERT_MLD #define IP6OPT_RTALERT_MLD 0 #endif #ifndef IP6OPT_ROUTER_ALERT // XXX: for compatibility with older systems #define IP6OPT_ROUTER_ALERT IP6OPT_RTALERT #endif #endif // HAVE_IPV6 // #ifdef HAVE_IPV6 static uint16_t rtalert_code6; #ifndef HAVE_RFC3542 static uint8_t ra_opt6[IP6OPT_RTALERT_LEN]; #endif #endif // HAVE_IPV6 IoIpSocket::IoIpSocket(FeaDataPlaneManager& fea_data_plane_manager, const IfTree& ift, int family, uint8_t ip_protocol) : IoIp(fea_data_plane_manager, ift, family, ip_protocol), _is_ip_hdr_included(false), _ip_id(xorp_random()) { // Init Router Alert related option stuff ra_opt4 = htonl((IPOPT_RA << 24) | (0x04 << 16)); #ifdef HAVE_IPV6 rtalert_code6 = htons(IP6OPT_RTALERT_MLD); // XXX: used by MLD only (?) #ifndef HAVE_RFC3542 ra_opt6[0] = IP6OPT_ROUTER_ALERT; ra_opt6[1] = IP6OPT_RTALERT_LEN - 2; memcpy(&ra_opt6[2], (caddr_t)&rtalert_code6, sizeof(rtalert_code6)); #endif // ! HAVE_RFC3542 #endif // HAVE_IPV6 // Allocate the buffers _rcvbuf = new uint8_t[IO_BUF_SIZE]; _sndbuf = new uint8_t[IO_BUF_SIZE]; _rcvcmsgbuf = new uint8_t[CMSG_BUF_SIZE]; _sndcmsgbuf = new uint8_t[CMSG_BUF_SIZE]; memset(_sndcmsgbuf, 0, CMSG_BUF_SIZE); // Scatter/gatter array initialization _rcviov[0].iov_base = (caddr_t)_rcvbuf; _rcviov[0].iov_len = IO_BUF_SIZE; _sndiov[0].iov_base = (caddr_t)_sndbuf; _sndiov[0].iov_len = 0; // recvmsg() and sendmsg() related initialization #ifndef HOST_OS_WINDOWS memset(&_rcvmh, 0, sizeof(_rcvmh)); memset(&_sndmh, 0, sizeof(_sndmh)); 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 XLOG_WARNING("Registering with iftree: %s\n", iftree().getName().c_str()); // Register interest in interface deletions. iftree().registerListener(this); } IoIpSocket::~IoIpSocket() { string error_msg; // Remove from event loop, clean up sockets. close_proto_sockets(error_msg); // Un-register interest in interface deletions. iftree().unregisterListener(this); if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the I/O IP raw socket mechanism: %s", error_msg.c_str()); } // Free the buffers delete[] _rcvbuf; delete[] _sndbuf; delete[] _rcvcmsgbuf; delete[] _sndcmsgbuf; } int IoIpSocket::start(string& error_msg) { if (_is_running) return (XORP_OK); if (open_proto_sockets(error_msg) != XORP_OK) return (XORP_ERROR); _is_running = true; return (XORP_OK); } int IoIpSocket::stop(string& error_msg) { if (! _is_running) return (XORP_OK); if (close_proto_sockets(error_msg) != XORP_OK) return (XORP_ERROR); _is_running = false; return (XORP_OK); } int IoIpSocket::set_multicast_ttl(int ttl, string& error_msg) { switch (family()) { case AF_INET: { u_char ip_ttl = ttl; // XXX: In IPv4 the value argument is 'u_char' if (setsockopt(_proto_socket_out, IPPROTO_IP, IP_MULTICAST_TTL, XORP_SOCKOPT_CAST(&ip_ttl), sizeof(ip_ttl)) < 0) { error_msg = c_format("setsockopt(IP_MULTICAST_TTL, %u) failed: %s", ip_ttl, XSTRERROR); return (XORP_ERROR); } } break; #ifdef HAVE_IPV6 case AF_INET6: { #ifndef HAVE_IPV6_MULTICAST error_msg = c_format("set_multicast_ttl() failed: " "IPv6 multicast not supported"); return (XORP_ERROR); #else int ip_ttl = ttl; if (setsockopt(_proto_socket_out, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, XORP_SOCKOPT_CAST(&ip_ttl), sizeof(ip_ttl)) < 0) { error_msg = c_format("setsockopt(IPV6_MULTICAST_HOPS, %u) failed: %s", ip_ttl, XSTRERROR); return (XORP_ERROR); } #endif // HAVE_IPV6_MULTICAST } break; #endif // HAVE_IPV6 default: XLOG_UNREACHABLE(); error_msg = c_format("Invalid address family %d", family()); return (XORP_ERROR); } return (XORP_OK); } int IoIpSocket::enable_multicast_loopback(bool is_enabled, string& error_msg) { switch (family()) { case AF_INET: { u_char loop = is_enabled; if (setsockopt(_proto_socket_out, IPPROTO_IP, IP_MULTICAST_LOOP, XORP_SOCKOPT_CAST(&loop), sizeof(loop)) < 0) { error_msg = c_format("setsockopt(IP_MULTICAST_LOOP, %u) failed: %s", loop, XSTRERROR); return (XORP_ERROR); } } break; #ifdef HAVE_IPV6 case AF_INET6: { #ifndef HAVE_IPV6_MULTICAST error_msg = c_format("enable_multicast_loop() failed: " "IPv6 multicast not supported"); return (XORP_ERROR); #else uint loop6 = is_enabled; if (setsockopt(_proto_socket_out, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, XORP_SOCKOPT_CAST(&loop6), sizeof(loop6)) < 0) { error_msg = c_format("setsockopt(IPV6_MULTICAST_LOOP, %u) failed: %s", loop6, XSTRERROR); return (XORP_ERROR); } #endif // HAVE_IPV6_MULTICAST } break; #endif // HAVE_IPV6 default: XLOG_UNREACHABLE(); error_msg = c_format("Invalid address family %d", family()); return (XORP_ERROR); } return (XORP_OK); } int IoIpSocket::set_default_multicast_interface(const string& if_name, const string& vif_name, string& error_msg) { const IfTreeVif* vifp; // Find the vif vifp = iftree().find_vif(if_name, vif_name); if (vifp == NULL) { error_msg = c_format("Setting the default multicast interface failed:" "interface %s vif %s not found", if_name.c_str(), vif_name.c_str()); return (XORP_ERROR); } switch (family()) { case AF_INET: { struct in_addr in_addr; // Find the first address IfTreeVif::IPv4Map::const_iterator ai = vifp->ipv4addrs().begin(); if (ai == vifp->ipv4addrs().end()) { error_msg = c_format("Setting the default multicast interface " "failed: " "interface %s vif %s has no address", if_name.c_str(), vif_name.c_str()); return (XORP_ERROR); } const IfTreeAddr4& fa = *(ai->second); fa.addr().copy_out(in_addr); if (setsockopt(_proto_socket_out, IPPROTO_IP, IP_MULTICAST_IF, XORP_SOCKOPT_CAST(&in_addr), sizeof(in_addr)) < 0) { error_msg = c_format("setsockopt(IP_MULTICAST_IF, %s) failed: %s", cstring(fa.addr()), XSTRERROR); return (XORP_ERROR); } } break; #ifdef HAVE_IPV6 case AF_INET6: { #ifndef HAVE_IPV6_MULTICAST error_msg = c_format("set_default_multicast_interface() failed: " "IPv6 multicast not supported"); return (XORP_ERROR); #else u_int pif_index = vifp->pif_index(); if (setsockopt(_proto_socket_out, IPPROTO_IPV6, IPV6_MULTICAST_IF, XORP_SOCKOPT_CAST(&pif_index), sizeof(pif_index)) < 0) { error_msg = c_format("setsockopt(IPV6_MULTICAST_IF, %s/%s) failed: %s", if_name.c_str(), vif_name.c_str(), XSTRERROR); return (XORP_ERROR); } #endif // HAVE_IPV6_MULTICAST } break; #endif // HAVE_IPV6 default: XLOG_UNREACHABLE(); error_msg = c_format("Invalid address family %d", family()); return (XORP_ERROR); } return (XORP_OK); } int IoIpSocket::join_multicast_group(const string& if_name, const string& vif_name, const IPvX& group, string& error_msg) { const IfTreeVif* vifp; XorpFd* _proto_socket_in = NULL; // Find the vif vifp = iftree().find_vif(if_name, vif_name); if (vifp == NULL) { error_msg += c_format("Joining multicast group %s failed: " "interface %s vif %s not found", cstring(group), if_name.c_str(), vif_name.c_str()); goto out_err; } _proto_socket_in = findOrCreateInputSocket(if_name, vif_name, error_msg); if (! _proto_socket_in) { string em = c_format("ERROR: Could not find or create input socket, if_name: %s vif_name: %s error_msg: %s", if_name.c_str(), vif_name.c_str(), error_msg.c_str()); XLOG_WARNING("%s", em.c_str()); error_msg += em; goto out_err; } #if 0 // TODO: enable or disable the enabled() check? if (! vifp->enabled()) { error_msg += c_format("Cannot join group %s on interface %s vif %s: " "interface/vif is DOWN", cstring(group), if_name.c_str(), vif_name.c_str()); goto out_err; } #endif // 0/1 switch (family()) { case AF_INET: { struct ip_mreq mreq; struct in_addr in_addr; // Find the first address IfTreeVif::IPv4Map::const_iterator ai = vifp->ipv4addrs().begin(); if (ai == vifp->ipv4addrs().end()) { error_msg += c_format("Cannot join group %s on interface %s vif %s: " "interface/vif has no address", cstring(group), if_name.c_str(), vif_name.c_str()); goto out_err; } const IfTreeAddr4& fa = *(ai->second); fa.addr().copy_out(in_addr); group.copy_out(mreq.imr_multiaddr); mreq.imr_interface.s_addr = in_addr.s_addr; if (setsockopt(*_proto_socket_in, IPPROTO_IP, IP_ADD_MEMBERSHIP, XORP_SOCKOPT_CAST(&mreq), sizeof(mreq)) < 0) { error_msg += c_format("Cannot join group %s on interface %s vif %s: %s", cstring(group), if_name.c_str(), vif_name.c_str(), XSTRERROR); goto out_err; } else { XLOG_INFO("Joined IPv4 group: %s on interface %s vif %s socket: %i", cstring(group), if_name.c_str(), vif_name.c_str(), (int)(*_proto_socket_in)); } } break; #ifdef HAVE_IPV6 case AF_INET6: { #ifndef HAVE_IPV6_MULTICAST error_msg += c_format("join_multicast_group() failed: " "IPv6 multicast not supported"); goto out_err; #else struct ipv6_mreq mreq6; group.copy_out(mreq6.ipv6mr_multiaddr); mreq6.ipv6mr_interface = vifp->pif_index(); if (setsockopt(*_proto_socket_in, IPPROTO_IPV6, IPV6_JOIN_GROUP, XORP_SOCKOPT_CAST(&mreq6), sizeof(mreq6)) < 0) { error_msg += c_format("Cannot join group %s on interface %s vif %s: %s", cstring(group), if_name.c_str(), vif_name.c_str(), XSTRERROR); goto out_err; } else { XLOG_INFO("Joined IPv6 group: %s on interface %s vif %s socket: %i", cstring(group), if_name.c_str(), vif_name.c_str(), (int)(*_proto_socket_in)); } #endif // HAVE_IPV6_MULTICAST } break; #endif // HAVE_IPV6 default: XLOG_UNREACHABLE(); error_msg += c_format("Invalid address family %d", family()); goto out_err; } return (XORP_OK); out_err: if (error_msg.size()) { XLOG_ERROR("ERROR in join_multicast_group: %s", error_msg.c_str()); } return XORP_ERROR; } XorpFd* IoIpSocket::mcast_protocol_fd_in() { #ifdef USE_SOCKET_PER_IFACE if (! _mcast_proto_socket_in.is_valid()) { // Try to open the socket. _mcast_proto_socket_in = socket(family(), SOCK_RAW, ip_protocol()); if (!_mcast_proto_socket_in.is_valid()) { XLOG_WARNING("Cannot open multicast IP protocol %u raw socket: %s", ip_protocol(), XSTRERROR); } else { string err_msg; initializeInputSocket(&_mcast_proto_socket_in, err_msg); if (err_msg.size()) { XLOG_WARNING("%s", err_msg.c_str()); } } } return &_mcast_proto_socket_in; #else string nll("na"); string err_msg; XorpFd* rv; // Just grabs (or creates) first and only socket when USE_SOCKET_PER_IFACE is not defined. rv = findOrCreateInputSocket(nll, nll, err_msg); if (err_msg.size()) { XLOG_WARNING("%s", err_msg.c_str()); } return rv; #endif } XorpFd* IoIpSocket::findExistingInputSocket(const string& if_name, const string& vif_name) { #ifdef USE_SOCKET_PER_IFACE string k(if_name); k += " "; k += vif_name; map::iterator i = _proto_sockets_in.find(k); #else UNUSED(if_name); UNUSED(vif_name); map::iterator i = _proto_sockets_in.begin(); #endif if (i == _proto_sockets_in.end()) { return NULL; } else { return i->second; } } int IoIpSocket::leave_multicast_group(const string& if_name, const string& vif_name, const IPvX& group, string& error_msg) { const IfTreeVif* vifp; // Find the vif vifp = iftree().find_vif(if_name, vif_name); if (vifp == NULL) { error_msg += c_format("Leaving multicast group %s failed: " "interface %s vif %s not found\n", cstring(group), if_name.c_str(), vif_name.c_str()); return (XORP_ERROR); } XorpFd* _proto_socket_in = findExistingInputSocket(if_name, vif_name); if (!_proto_socket_in) { error_msg += c_format("Leaving multicast group %s failed: " "interface %s vif %s does not have a socket assigned.\n", cstring(group), if_name.c_str(), vif_name.c_str()); return (XORP_ERROR); } #if 0 // TODO: enable or disable the enabled() check? if (! vifp->enabled()) { error_msg += c_format("Cannot leave group %s on interface %s vif %s: " "interface/vif is DOWN\n", cstring(group), if_name.c_str(), vif_name.c_str()); return (XORP_ERROR); } #endif // 0/1 switch (family()) { case AF_INET: { struct ip_mreq mreq; struct in_addr in_addr; // Find the first address IfTreeVif::IPv4Map::const_iterator ai = vifp->ipv4addrs().begin(); if (ai == vifp->ipv4addrs().end()) { error_msg += c_format("Cannot leave group %s on interface %s vif %s: " "interface/vif has no address\n", cstring(group), if_name.c_str(), vif_name.c_str()); return (XORP_ERROR); } const IfTreeAddr4& fa = *(ai->second); fa.addr().copy_out(in_addr); group.copy_out(mreq.imr_multiaddr); mreq.imr_interface.s_addr = in_addr.s_addr; if (setsockopt(*_proto_socket_in, IPPROTO_IP, IP_DROP_MEMBERSHIP, XORP_SOCKOPT_CAST(&mreq), sizeof(mreq)) < 0) { error_msg += c_format("Cannot leave group %s on interface %s vif %s socket: %i: %s\n", cstring(group), if_name.c_str(), vif_name.c_str(), (int)(*_proto_socket_in), XSTRERROR); return (XORP_ERROR); } else { XLOG_INFO("Left group: %s on interface %s vif %s socket: %i", cstring(group), if_name.c_str(), vif_name.c_str(), (int)(*_proto_socket_in)); } } break; #ifdef HAVE_IPV6 case AF_INET6: { #ifndef HAVE_IPV6_MULTICAST error_msg += c_format("leave_multicast_group() failed: " "IPv6 multicast not supported\n"); return (XORP_ERROR); #else struct ipv6_mreq mreq6; group.copy_out(mreq6.ipv6mr_multiaddr); mreq6.ipv6mr_interface = vifp->pif_index(); if (setsockopt(*_proto_socket_in, IPPROTO_IPV6, IPV6_LEAVE_GROUP, XORP_SOCKOPT_CAST(&mreq6), sizeof(mreq6)) < 0) { error_msg += c_format("Cannot leave V6 group %s on interface %s vif %s socket: %i: %s\n", cstring(group), if_name.c_str(), vif_name.c_str(), (int)(*_proto_socket_in), XSTRERROR); return (XORP_ERROR); } #endif // HAVE_IPV6_MULTICAST } break; #endif // HAVE_IPV6 default: XLOG_UNREACHABLE(); error_msg += c_format("Invalid address family %d\n", family()); return (XORP_ERROR); } return (XORP_OK); } XorpFd* IoIpSocket::findOrCreateInputSocket(const string& if_name, const string& vif_name, string& error_msg) { XorpFd* rv = findExistingInputSocket(if_name, vif_name); string key(if_name); key += " "; key += vif_name; if (!rv) { // Create a new one rv = new XorpFd(); *rv = socket(family(), SOCK_RAW, ip_protocol()); if (!rv->is_valid()) { error_msg += c_format("Cannot open IP protocol %u raw socket: %s", ip_protocol(), XSTRERROR); delete rv; return NULL; } else { XLOG_INFO("Successfully created socket: %i on family: %i protocol: %i" " interface: %s input sockets size: %i\n", (int)(*rv), (int)(family()), (int)(ip_protocol()), vif_name.c_str(), (int)(_proto_sockets_in.size())); _proto_sockets_in[key] = rv; } int rslt = initializeInputSocket(rv, error_msg); if (rslt != XORP_OK) { _proto_sockets_in.erase(key); cleanupXorpFd(rv); return NULL; } // Bind to a particular interface. #ifdef USE_SOCKET_PER_IFACE #ifdef SO_BINDTODEVICE if (setsockopt(*rv, SOL_SOCKET, SO_BINDTODEVICE, vif_name.c_str(), vif_name.size() + 1)) { error_msg += c_format("ERROR: IoIpSocket::open_proto_socket, setsockopt (BINDTODEVICE): failed: %s", XSTRERROR); } else { XLOG_INFO("Successfully bound socket: %i to interface: %s input sockets size: %i\n", (int)(*rv), vif_name.c_str(), (int)(_proto_sockets_in.size())); } #endif #endif } return rv; } int IoIpSocket::initializeInputSocket(XorpFd* rv, string& error_msg) { // // Set various socket options // // Make socket non-blocking by default. comm_sock_set_blocking(*rv, COMM_SOCK_NONBLOCKING); // Lots of input buffering if (comm_sock_set_rcvbuf(*rv, SO_RCV_BUF_SIZE_MAX, SO_RCV_BUF_SIZE_MIN) < SO_RCV_BUF_SIZE_MIN) { error_msg += c_format("Cannot set the receiver buffer size: %s", comm_get_last_error_str()); // doesn't seem fatal....continue on. //close_proto_sockets(error_msg); //return (XORP_ERROR); } // Show interest in receiving information from IP header if (enable_recv_pktinfo(rv, true, error_msg) != XORP_OK) { return XORP_ERROR; } // Protocol-specific setup switch (family()) { case AF_INET: break; #ifdef HAVE_IPV6 case AF_INET6: { #ifndef HOST_OS_WINDOWS // Not supported on mingw, at least... if (ip_protocol() == IPPROTO_ICMPV6) { struct icmp6_filter filter; // Pass all ICMPv6 messages ICMP6_FILTER_SETPASSALL(&filter); #ifdef HAVE_IPV6_MULTICAST_ROUTING #if 0 // TODO: XXX: used only for multicast routing purpose by MLD if (module_id() == XORP_MODULE_MLD6IGMP) { // Filter all non-MLD ICMPv6 messages ICMP6_FILTER_SETBLOCKALL(&filter); ICMP6_FILTER_SETPASS(MLD_LISTENER_QUERY, &filter); ICMP6_FILTER_SETPASS(MLD_LISTENER_REPORT, &filter); ICMP6_FILTER_SETPASS(MLD_LISTENER_DONE, &filter); ICMP6_FILTER_SETPASS(MLD_MTRACE_RESP, &filter); ICMP6_FILTER_SETPASS(MLD_MTRACE, &filter); #ifdef MLDV2_LISTENER_REPORT ICMP6_FILTER_SETPASS(MLDV2_LISTENER_REPORT, &filter); #endif } #endif // 0 #endif // HAVE_IPV6_MULTICAST_ROUTING if (setsockopt(*rv, ip_protocol(), ICMP6_FILTER, XORP_SOCKOPT_CAST(&filter), sizeof(filter)) < 0) { error_msg += c_format("setsockopt(ICMP6_FILTER) failed: %s", XSTRERROR); return XORP_ERROR; } } #endif /* windows */ } break; #endif // HAVE_IPV6 default: XLOG_UNREACHABLE(); error_msg += c_format("Invalid address family %d", family()); return XORP_ERROR; } #ifdef HOST_OS_WINDOWS switch (family()) { case AF_INET: break; #ifdef HAVE_IPV6 case AF_INET6: { // Obtain the pointer to the extension function WSARecvMsg() if needed if (lpWSARecvMsg == NULL) { int result; DWORD nbytes; result = WSAIoctl(*rv, SIO_GET_EXTENSION_FUNCTION_POINTER, const_cast(&guidWSARecvMsg), sizeof(guidWSARecvMsg), &lpWSARecvMsg, sizeof(lpWSARecvMsg), &nbytes, NULL, NULL); if (result == SOCKET_ERROR) { XLOG_ERROR("Cannot obtain WSARecvMsg function pointer; " "unable to receive raw IPv6 traffic."); lpWSARecvMsg = NULL; } } // Obtain the pointer to the extension function WSASendMsg() if needed // XXX: Only available on Windows Longhorn. if (lpWSASendMsg == NULL) { int result; DWORD nbytes; result = WSAIoctl(*rv, SIO_GET_EXTENSION_FUNCTION_POINTER, const_cast(&guidWSASendMsg), sizeof(guidWSASendMsg), &lpWSASendMsg, sizeof(lpWSASendMsg), &nbytes, NULL, NULL); if (result == SOCKET_ERROR) { XLOG_ERROR("Cannot obtain WSASendMsg function pointer; " "unable to send raw IPv6 traffic."); lpWSASendMsg = NULL; } } } break; #endif // HAVE_IPV6 default: XLOG_UNREACHABLE(); error_msg = c_format("Invalid address family %d", family()); return (XORP_ERROR); } #endif // HOST_OS_WINDOWS #ifdef HOST_OS_WINDOWS // // Winsock requires that raw sockets be bound, either to the IPv4 // address of a physical interface, or the INADDR_ANY address, // in order to receive traffic or to join multicast groups. // struct sockaddr_in sin; memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = htonl(INADDR_ANY); if (SOCKET_ERROR == bind(*rv, (sockaddr *)&sin, sizeof(sockaddr_in))) { XLOG_WARNING("bind() failed: %s\n", win_strerror(GetLastError())); } #endif // HOST_OS_WINDOWS // Assign a method to read from this socket if (eventloop().add_ioevent_cb(*rv, IOT_READ, callback(this, &IoIpSocket::proto_socket_read)) == false) { error_msg += c_format("Cannot add protocol socket: %i to the set of " "sockets to read from in the event loop", (int)(*rv)); return XORP_ERROR; } return XORP_OK; }//initializeInputSocket int IoIpSocket::open_proto_sockets(string& error_msg) { string dummy_error_msg; // We will open input sockets as interfaces are registered (due to listening for mcast addrs) if (_proto_socket_out.is_valid()) return (XORP_OK); if (! _proto_socket_out.is_valid()) { _proto_socket_out = socket(family(), SOCK_RAW, ip_protocol()); if (!_proto_socket_out.is_valid()) { error_msg = c_format("Cannot open IP protocol %u raw socket: %s", ip_protocol(), XSTRERROR); return (XORP_ERROR); } } // // Set various socket options // // Lots of output buffering if (comm_sock_set_sndbuf(_proto_socket_out, SO_SND_BUF_SIZE_MAX, SO_SND_BUF_SIZE_MIN) < SO_SND_BUF_SIZE_MIN) { error_msg = c_format("Cannot set the sender buffer size: %s", comm_get_last_error_str()); close_proto_sockets(dummy_error_msg); return (XORP_ERROR); } // Very small input buffering, as we never read this, but don't fail if // we can't...it just wastes a bit of memory. comm_sock_set_rcvbuf(_proto_socket_out, 2000, 2000); // Include IP header when sending (XXX: doesn't do anything for IPv6) if (enable_ip_hdr_include(true, error_msg) != XORP_OK) { close_proto_sockets(dummy_error_msg); return (XORP_ERROR); } // Restrict multicast TTL if (set_multicast_ttl(MINTTL, error_msg) != XORP_OK) { close_proto_sockets(dummy_error_msg); return (XORP_ERROR); } // Disable mcast loopback if (enable_multicast_loopback(false, error_msg) != XORP_OK) { close_proto_sockets(dummy_error_msg); return (XORP_ERROR); } return (XORP_OK); } int IoIpSocket::close_proto_sockets(string& error_msg) { error_msg = ""; // // Close the outgoing protocol socket // if (_proto_socket_out.is_valid()) { // It probably wasn't added...but just in case code changes, // keep the cleanup logic here. eventloop().remove_ioevent_cb(_proto_socket_out); comm_close(_proto_socket_out); _proto_socket_out.clear(); } #ifdef USE_SOCKET_PER_IFACE if (_mcast_proto_socket_in.is_valid()) { eventloop().remove_ioevent_cb(_mcast_proto_socket_in); comm_close(_mcast_proto_socket_in); _mcast_proto_socket_in.clear(); } #endif map::iterator i; for (i = _proto_sockets_in.begin(); i != _proto_sockets_in.end(); i++) { XorpFd* fd = i->second; cleanupXorpFd(fd); } _proto_sockets_in.clear(); return (XORP_OK); } int IoIpSocket::cleanupXorpFd(XorpFd* fd) { // // Close the incoming protocol socket // if (fd->is_valid()) { // Remove it just in case, even though it may not be select()-ed eventloop().remove_ioevent_cb(*fd); #ifdef HOST_OS_WINDOWS switch (family()) { case AF_INET: break; #ifdef HAVE_IPV6 case AF_INET6: // Reset the pointer to the WSARecvMsg()/WSASendMsg() functions. lpWSARecvMsg = NULL; lpWSASendMsg = NULL; break; #endif // HAVE_IPV6 default: XLOG_UNREACHABLE(); return (XORP_ERROR); } #endif // HOST_OS_WINDOWS comm_close(*fd); fd->clear(); } delete fd; return XORP_OK; } int IoIpSocket::enable_ip_hdr_include(bool is_enabled, string& error_msg) { UNUSED(is_enabled); switch (family()) { case AF_INET: { #ifdef IP_HDRINCL // XXX: the setsockopt() argument must be 'int' int bool_flag = is_enabled; if (setsockopt(_proto_socket_out, IPPROTO_IP, IP_HDRINCL, XORP_SOCKOPT_CAST(&bool_flag), sizeof(bool_flag)) < 0) { error_msg = c_format("setsockopt(IP_HDRINCL, %u) failed: %s", bool_flag, XSTRERROR); return (XORP_ERROR); } _is_ip_hdr_included = is_enabled; #endif // IP_HDRINCL } break; #ifdef HAVE_IPV6 case AF_INET6: break; // XXX #endif // HAVE_IPV6 default: XLOG_UNREACHABLE(); error_msg = c_format("Invalid address family %d", family()); return (XORP_ERROR); } return (XORP_OK); } int IoIpSocket::enable_recv_pktinfo(XorpFd* input_fd, bool is_enabled, string& error_msg) { switch (family()) { case AF_INET: { // XXX: the setsockopt() argument must be 'int' int bool_flag = is_enabled; // // Interface index // #ifdef IP_RECVIF // XXX: BSD if (setsockopt(*input_fd, IPPROTO_IP, IP_RECVIF, XORP_SOCKOPT_CAST(&bool_flag), sizeof(bool_flag)) < 0) { XLOG_ERROR("setsockopt(IP_RECVIF, %u) failed: %s", bool_flag, XSTRERROR); return (XORP_ERROR); } #endif // IP_RECVIF #ifdef IP_PKTINFO // XXX: Linux if (setsockopt(*input_fd, IPPROTO_IP, IP_PKTINFO, XORP_SOCKOPT_CAST(&bool_flag), sizeof(bool_flag)) < 0) { XLOG_ERROR("setsockopt(IP_PKTINFO, %u) failed: %s", bool_flag, XSTRERROR); return (XORP_ERROR); } #endif // IP_PKTINFO UNUSED(bool_flag); break; } #ifdef HAVE_IPV6 case AF_INET6: { // XXX: the setsockopt() argument must be 'int' int bool_flag = is_enabled; // // Interface index and address // #ifdef IPV6_RECVPKTINFO // The new option (applies to receiving only) if (setsockopt(*input_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, XORP_SOCKOPT_CAST(&bool_flag), sizeof(bool_flag)) < 0) { error_msg = c_format("setsockopt(IPV6_RECVPKTINFO, %u) failed: %s", bool_flag, XSTRERROR); return (XORP_ERROR); } #else // The old option (see RFC-2292) if (setsockopt(*input_fd, IPPROTO_IPV6, IPV6_PKTINFO, XORP_SOCKOPT_CAST(&bool_flag), sizeof(bool_flag)) < 0) { error_msg = c_format("setsockopt(IPV6_PKTINFO, %u) failed: %s", bool_flag, XSTRERROR); return (XORP_ERROR); } #endif // ! IPV6_RECVPKTINFO // // Hop-limit field // #ifdef IPV6_RECVHOPLIMIT if (setsockopt(*input_fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, XORP_SOCKOPT_CAST(&bool_flag), sizeof(bool_flag)) < 0) { error_msg = c_format("setsockopt(IPV6_RECVHOPLIMIT, %u) failed: %s", bool_flag, XSTRERROR); return (XORP_ERROR); } #else #ifndef HOST_OS_WINDOWS /* not supported on mingw TODO: Fix? */ if (setsockopt(*input_fd, IPPROTO_IPV6, IPV6_HOPLIMIT, XORP_SOCKOPT_CAST(&bool_flag), sizeof(bool_flag)) < 0) { error_msg = c_format("setsockopt(IPV6_HOPLIMIT, %u) failed: %s", bool_flag, XSTRERROR); return (XORP_ERROR); } #endif #endif // ! IPV6_RECVHOPLIMIT // // Traffic class value // #ifdef IPV6_RECVTCLASS if (setsockopt(*input_fd, IPPROTO_IPV6, IPV6_RECVTCLASS, XORP_SOCKOPT_CAST(&bool_flag), sizeof(bool_flag)) < 0) { error_msg = c_format("setsockopt(IPV6_RECVTCLASS, %u) failed: %s", bool_flag, XSTRERROR); return (XORP_ERROR); } #endif // IPV6_RECVTCLASS // // Hop-by-hop options // #ifdef IPV6_RECVHOPOPTS if (setsockopt(*input_fd, IPPROTO_IPV6, IPV6_RECVHOPOPTS, XORP_SOCKOPT_CAST(&bool_flag), sizeof(bool_flag)) < 0) { error_msg = c_format("setsockopt(IPV6_RECVHOPOPTS, %u) failed: %s", bool_flag, XSTRERROR); return (XORP_ERROR); } #else #ifndef HOST_OS_WINDOWS /* not supported on mingw TODO: Fix? */ if (setsockopt(*input_fd, IPPROTO_IPV6, IPV6_HOPOPTS, XORP_SOCKOPT_CAST(&bool_flag), sizeof(bool_flag)) < 0) { error_msg = c_format("setsockopt(IPV6_HOPOPTS, %u) failed: %s", bool_flag, XSTRERROR); return (XORP_ERROR); } #endif #endif // ! IPV6_RECVHOPOPTS // // Routing header options // #ifdef IPV6_RECVRTHDR if (setsockopt(*input_fd, IPPROTO_IPV6, IPV6_RECVRTHDR, XORP_SOCKOPT_CAST(&bool_flag), sizeof(bool_flag)) < 0) { error_msg = c_format("setsockopt(IPV6_RECVRTHDR, %u) failed: %s", bool_flag, XSTRERROR); return (XORP_ERROR); } #else #ifndef HOST_OS_WINDOWS /* not supported on mingw TODO: Fix?*/ if (setsockopt(*input_fd, IPPROTO_IPV6, IPV6_RTHDR, XORP_SOCKOPT_CAST(&bool_flag), sizeof(bool_flag)) < 0) { error_msg = c_format("setsockopt(IPV6_RTHDR, %u) failed: %s", bool_flag, XSTRERROR); return (XORP_ERROR); } #endif #endif // ! IPV6_RECVRTHDR // // Destination options // #ifdef IPV6_RECVDSTOPTS if (setsockopt(*input_fd, IPPROTO_IPV6, IPV6_RECVDSTOPTS, XORP_SOCKOPT_CAST(&bool_flag), sizeof(bool_flag)) < 0) { error_msg = c_format("setsockopt(IPV6_RECVDSTOPTS, %u) failed: %s", bool_flag, XSTRERROR); return (XORP_ERROR); } #else #ifndef HOST_OS_WINDOWS // TODO: Implement IPV6_DSTOPS on windows/mingw?? if (setsockopt(*input_fd, IPPROTO_IPV6, IPV6_DSTOPTS, XORP_SOCKOPT_CAST(&bool_flag), sizeof(bool_flag)) < 0) { error_msg = c_format("setsockopt(IPV6_DSTOPTS, %u) failed: %s", bool_flag, XSTRERROR); return (XORP_ERROR); } #endif #endif // ! IPV6_RECVDSTOPTS } break; #endif // HAVE_IPV6 default: XLOG_UNREACHABLE(); error_msg = c_format("Invalid address family %d", family()); return (XORP_ERROR); } return (XORP_OK); } void IoIpSocket::notifyDeletingIface(const string& ifname) { // Only clean up here if we are using multiple input sockets. XLOG_INFO("IoIpSocket::notifyDeletingIface:, this: %p iface: %s\n", this, ifname.c_str()); #ifdef USE_SOCKET_PER_IFACE const IfTreeInterface* ifp = iftree().find_interface(ifname); if (ifp) { for (IfTreeInterface::VifMap::const_iterator i = ifp->vifs().begin(); i != ifp->vifs().end(); i++) { string ifn = i->second->ifname(); string vn = i->second->vifname(); XorpFd* fd = findExistingInputSocket(ifn, vn); if (fd) { string key(ifn); key += " "; key += vn; int _fd = (int)(*fd); UNUSED(_fd); // in case XLOG_INFO is compiled out. _proto_sockets_in.erase(key); cleanupXorpFd(fd); XLOG_INFO("Closed socket: %i on interface: %s:%s because its interface" " is being deleted, input sockets count: %i\n", _fd, ifn.c_str(), vn.c_str(), (int)(_proto_sockets_in.size())); } } } #else UNUSED(ifname); #endif } void IoIpSocket::notifyDeletingVif(const string& ifn, const string& vn) { // Only clean up here if we are using multiple input sockets. XLOG_INFO("IoIpSocket::notifyDeletingVif: %s:%s\n", ifn.c_str(), vn.c_str()); #ifdef USE_SOCKET_PER_IFACE XorpFd* fd = findExistingInputSocket(ifn, vn); if (fd) { string key(ifn); key += " "; key += vn; int _fd = (int)(*fd); UNUSED(_fd); // in case XLOG_INFO is compiled out. _proto_sockets_in.erase(key); cleanupXorpFd(fd); XLOG_INFO("Closed socket: %i on interface: %s:%s because it is being deleted, input sockets count: %i\n", _fd, ifn.c_str(), vn.c_str(), (int)(_proto_sockets_in.size())); } #else UNUSED(ifn); UNUSED(vn); #endif } void IoIpSocket::proto_socket_read(XorpFd fd, IoEventType type) { ssize_t nbytes; size_t ip_hdr_len = 0; size_t ip_data_len = 0; IPvX src_address(family()); IPvX dst_address(family()); int int_val; int32_t ip_ttl = -1; // a.k.a. Hop-Limit in IPv6 int32_t ip_tos = -1; bool ip_router_alert = false; // Router Alert option received bool ip_internet_control = false; // IP Internet Control pkt rcvd uint32_t pif_index = 0; vector ext_headers_type; vector > ext_headers_payload; void* cmsg_data; // XXX: CMSG_DATA() is aligned, hence void ptr UNUSED(fd); UNUSED(type); UNUSED(int_val); UNUSED(cmsg_data); #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: memset(&_from6, 0, sizeof(_from6)); _rcvmh.msg_namelen = sizeof(_from6); break; #endif // HAVE_IPV6 default: XLOG_UNREACHABLE(); return; // Error } // Read from the socket nbytes = recvmsg(fd, &_rcvmh, 0); if (nbytes < 0) { if (errno == EINTR) return; // OK: restart receiving XLOG_ERROR("recvmsg() on socket %s failed: %s", fd.str().c_str(), XSTRERROR); return; // Error } #else // HOST_OS_WINDOWS switch (family()) { case AF_INET: { struct sockaddr_storage from; socklen_t from_len = sizeof(from); nbytes = recvfrom(fd, XORP_BUF_CAST(_rcvbuf), IO_BUF_SIZE, 0, reinterpret_cast(&from), &from_len); debug_msg("Read fd %s, %d bytes\n", fd.str().c_str(), XORP_INT_CAST(nbytes)); if (nbytes < 0) { XLOG_ERROR("recvfrom() failed: %s fd: %s", XSTRERROR, fd.str().c_str()); return; } } break; #ifdef HAVE_IPV6 case AF_INET6: { WSAMSG mh; DWORD error, nrecvd; struct sockaddr_in6 from; mh.name = (LPSOCKADDR)&from; mh.namelen = sizeof(from); mh.lpBuffers = (LPWSABUF)_rcviov; mh.dwBufferCount = 1; mh.Control.len = CMSG_BUF_SIZE; mh.Control.buf = (caddr_t)_rcvcmsgbuf; mh.dwFlags = 0; if (lpWSARecvMsg == NULL) { XLOG_ERROR("lpWSARecvMsg is NULL"); return; // Error } error = lpWSARecvMsg(fd, &mh, &nrecvd, NULL, NULL); nbytes = (ssize_t)nrecvd; debug_msg("Read fd %s, %d bytes\n", fd.str().c_str(), XORP_INT_CAST(nbytes)); if (nbytes < 0) { XLOG_ERROR("lpWSARecvMsg() failed: %s, fd: %s", XSTRERROR, fd.str().c_str()); return; } } break; #endif // HAVE_IPV6 default: XLOG_UNREACHABLE(); return; // Error } #endif // HOST_OS_WINDOWS // // Check whether this is a multicast forwarding related upcall from the // system to the user-level. // switch (family()) { case AF_INET: { if (ip_protocol() != IPPROTO_IGMP) break; #ifdef HAVE_IPV4_MULTICAST_ROUTING if (nbytes < (ssize_t)sizeof(struct igmpmsg)) { // Probably not a system upcall break; } struct igmpmsg* igmpmsg; // XXX: "void" casting to fix alignment warning that can be ignored igmpmsg = reinterpret_cast((void *)_rcvbuf); if (igmpmsg->im_mbz == 0) { // // XXX: Packets sent up from system to daemon have // igmpmsg->im_mbz = ip->ip_p = 0 // vector payload(nbytes); memcpy(&payload[0], _rcvbuf, nbytes); recv_system_multicast_upcall(payload); return; // OK } #endif // HAVE_IPV4_MULTICAST_ROUTING } break; #ifdef HAVE_IPV6 case AF_INET6: { if (ip_protocol() != IPPROTO_ICMPV6) break; #ifdef HAVE_IPV6_MULTICAST_ROUTING if (nbytes < (ssize_t)sizeof(struct mrt6msg)) { // Probably not a system upcall break; } struct mrt6msg* mrt6msg; // XXX: "void" casting to fix alignment warning that can be ignored mrt6msg = reinterpret_cast((void *)_rcvbuf); if ((mrt6msg->im6_mbz == 0) || (_rcvmh.msg_controllen == 0)) { // // XXX: Packets sent up from system 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. // vector payload(nbytes); memcpy(&payload[0], _rcvbuf, nbytes); recv_system_multicast_upcall(payload); return; // OK } #endif // HAVE_IPV6_MULTICAST_ROUTING } break; #endif // HAVE_IPV6 default: XLOG_UNREACHABLE(); return; // Error } // // Not a system upcall. Pass to the registered processing function. // // // Input check. // Get source and destination address, IP TTL (a.k.a. hop-limit), // and (eventually) interface address (IPv6 only). // switch (family()) { case AF_INET: { IpHeader4 ip4(_rcvbuf); bool is_datalen_error = false; // Input check if (nbytes < (ssize_t)ip4.size()) { XLOG_WARNING("proto_socket_read() failed: " "packet size %d is smaller than minimum size %u", XORP_INT_CAST(nbytes), XORP_UINT_CAST(ip4.size())); return; // Error } #ifndef HOST_OS_WINDOWS // TODO: get rid of this and always use ip4.ip_src() ?? src_address.copy_in(_from4); #else src_address = ip4.ip_src(); #endif dst_address = ip4.ip_dst(); ip_ttl = ip4.ip_ttl(); ip_tos = ip4.ip_tos(); if (ip_tos == IPTOS_PREC_INTERNETCONTROL) ip_internet_control = true; ip_hdr_len = ip4.ip_header_len(); #ifdef IPV4_RAW_INPUT_IS_RAW ip_data_len = ip4.ip_len() - ip_hdr_len; #else // // XXX: The original value is in host order, and excludes the // IPv4 header length. // ip_data_len = ip4.ip_len_host(); #endif // ! IPV4_RAW_INPUT_IS_RAW // Check length is_datalen_error = false; do { if (ip_hdr_len + ip_data_len == static_cast(nbytes)) { is_datalen_error = false; break; // OK } if (nbytes < static_cast(ip_hdr_len)) { is_datalen_error = true; break; } if (ip4.ip_p() == IPPROTO_PIM) { if (nbytes < static_cast(ip_hdr_len + PIM_REG_MINLEN)) { is_datalen_error = true; break; } struct pim pim; memcpy(&pim, ip4.data() + ip_hdr_len, sizeof(pim)); if (PIM_VT_T(pim.pim_vt) != PIM_REGISTER) { is_datalen_error = true; break; } // // XXX: the *BSD kernel might truncate the encapsulated data // in PIM Register messages before passing the message up // to user level. The reason for the truncation is to reduce // the bandwidth overhead, but the price for this is // messages with weird IP header. Sigh... // is_datalen_error = false; break; } break; } while (false); if (is_datalen_error) { XLOG_ERROR("proto_socket_read() failed: " "RX packet size from %s to %s with %d bytes instead of " "hdr+datalen=%u+%u=%u, ip_len: %i ip_len_host: %i", cstring(src_address), cstring(dst_address), XORP_INT_CAST(nbytes), XORP_UINT_CAST(ip_hdr_len), XORP_UINT_CAST(ip_data_len), XORP_UINT_CAST(ip_hdr_len + ip_data_len), (int)(ip4.ip_len()), (int)(ip4.ip_len_host())); return; // Error } // // Get the pif_index. // #ifndef HOST_OS_WINDOWS for (struct cmsghdr *cmsgp = reinterpret_cast(CMSG_FIRSTHDR(&_rcvmh)); cmsgp != NULL; cmsgp = reinterpret_cast(CMSG_NXTHDR(&_rcvmh, cmsgp))) { if (cmsgp->cmsg_level != IPPROTO_IP) continue; switch (cmsgp->cmsg_type) { #ifdef IP_RECVIF case IP_RECVIF: { struct sockaddr_dl *sdl = NULL; if (cmsgp->cmsg_len < CMSG_LEN(sizeof(struct sockaddr_dl))) continue; cmsg_data = CMSG_DATA(cmsgp); sdl = reinterpret_cast(cmsg_data); pif_index = sdl->sdl_index; } break; #endif // IP_RECVIF #ifdef IP_PKTINFO case IP_PKTINFO: { struct in_pktinfo *inp = NULL; if (cmsgp->cmsg_len < CMSG_LEN(sizeof(struct in_pktinfo))) continue; cmsg_data = CMSG_DATA(cmsgp); inp = reinterpret_cast(cmsg_data); pif_index = inp->ipi_ifindex; } break; #endif // IP_PKTINFO default: break; } } #endif // ! HOST_OS_WINDOWS // // Check for Router Alert option // do { const uint8_t *option_p = ip4.data() + ip4.size(); uint8_t option_value, option_len; uint32_t test_ip_options_len = ip_hdr_len - ip4.size(); while (test_ip_options_len) { if (test_ip_options_len < 4) break; option_value = *option_p; option_len = *(option_p + 1); if (test_ip_options_len < option_len) break; if ((option_value == IPOPT_RA) && (option_len == 4)) { ip_router_alert = true; break; } if (option_len == 0) break; // XXX: a guard against bogus option_len value test_ip_options_len -= option_len; option_p += option_len; } break; } while (false); } break; #ifdef HAVE_IPV6 case AF_INET6: { src_address.copy_in(_from6); #ifndef HOST_OS_WINDOWS /* TODO: This need fixing for windows ipv6 support?? */ struct in6_pktinfo *pi = NULL; if (_rcvmh.msg_flags & MSG_CTRUNC) { XLOG_ERROR("proto_socket_read() failed: " "RX packet from %s with size of %d bytes is truncated", cstring(src_address), XORP_INT_CAST(nbytes)); return; // Error } size_t controllen = static_cast(_rcvmh.msg_controllen); if (controllen < sizeof(struct cmsghdr)) { XLOG_ERROR("proto_socket_read() failed: " "RX packet from %s has too short msg_controllen " "(%u instead of %u)", cstring(src_address), XORP_UINT_CAST(controllen), XORP_UINT_CAST(sizeof(struct cmsghdr))); return; // Error } // // Get pif_index, hop limit, Router Alert option, etc. // for (struct cmsghdr *cmsgp = reinterpret_cast(CMSG_FIRSTHDR(&_rcvmh)); cmsgp != NULL; cmsgp = reinterpret_cast(CMSG_NXTHDR(&_rcvmh, cmsgp))) { if (cmsgp->cmsg_level != IPPROTO_IPV6) continue; switch (cmsgp->cmsg_type) { case IPV6_PKTINFO: { if (cmsgp->cmsg_len < CMSG_LEN(sizeof(struct in6_pktinfo))) continue; cmsg_data = CMSG_DATA(cmsgp); pi = reinterpret_cast(cmsg_data); pif_index = pi->ipi6_ifindex; dst_address.copy_in(pi->ipi6_addr); } break; case IPV6_HOPLIMIT: { if (cmsgp->cmsg_len < CMSG_LEN(sizeof(int))) continue; int_val = extract_host_int(CMSG_DATA(cmsgp)); ip_ttl = int_val; } break; case IPV6_HOPOPTS: { // // Check for Router Alert option // #ifdef HAVE_RFC3542 struct ip6_hbh *ext; int currentlen; uint8_t type; size_t extlen; socklen_t len; void *databuf; cmsg_data = CMSG_DATA(cmsgp); ext = reinterpret_cast(cmsg_data); extlen = (ext->ip6h_len + 1) * 8; currentlen = 0; while (true) { currentlen = inet6_opt_next(ext, extlen, currentlen, &type, &len, &databuf); if (currentlen == -1) break; if (type == IP6OPT_ROUTER_ALERT) { ip_router_alert = true; break; } } #else // ! HAVE_RFC3542 (i.e., the old advanced API) #ifdef HAVE_IPV6_OPTION_SPACE uint8_t *tptr = NULL; while (inet6_option_next(cmsgp, &tptr) == 0) { if (*tptr == IP6OPT_ROUTER_ALERT) { ip_router_alert = true; break; } } #endif #endif // ! HAVE_RFC3542 } break; #ifdef IPV6_TCLASS case IPV6_TCLASS: { if (cmsgp->cmsg_len < CMSG_LEN(sizeof(int))) continue; int_val = extract_host_int(CMSG_DATA(cmsgp)); ip_tos = int_val; // // TODO: XXX: Here we need to compute ip_internet_control // for IPv6, but we don't know how. // } break; #endif // IPV6_TCLASS #ifdef IPV6_RTHDR case IPV6_RTHDR: { vector opt_payload(cmsgp->cmsg_len); if (cmsgp->cmsg_len < CMSG_LEN(sizeof(struct ip6_rthdr))) continue; cmsg_data = CMSG_DATA(cmsgp); memcpy(&opt_payload[0], cmsg_data, cmsgp->cmsg_len); // XXX: Use the protocol number instead of message type ext_headers_type.push_back(IPPROTO_ROUTING); ext_headers_payload.push_back(opt_payload); } break; #endif // IPV6_RTHDR #ifdef IPV6_DSTOPTS case IPV6_DSTOPTS: { vector opt_payload(cmsgp->cmsg_len); if (cmsgp->cmsg_len < CMSG_LEN(sizeof(struct ip6_dest))) continue; cmsg_data = CMSG_DATA(cmsgp); memcpy(&opt_payload[0], cmsg_data, cmsgp->cmsg_len); // XXX: Use the protocol number instead of message type ext_headers_type.push_back(IPPROTO_DSTOPTS); ext_headers_payload.push_back(opt_payload); } break; #endif // IPV6_DSTOPTS default: break; } }/* for all cmsg headers */ #endif /* windows */ ip_hdr_len = 0; ip_data_len = nbytes; } break; #endif // HAVE_IPV6 default: XLOG_UNREACHABLE(); return; // Error } // Various checks if (! (src_address.is_unicast() || src_address.is_zero())) { // XXX: Accept zero source addresses because of protocols like IGMPv3 XLOG_ERROR("proto_socket_read() failed: " "invalid unicast sender address: %s", cstring(src_address)); return; // Error } if (! (dst_address.is_multicast() || dst_address.is_unicast())) { XLOG_ERROR("proto_socket_read() failed: " "invalid destination address: %s", cstring(dst_address)); return; // Error } if (ip_ttl < 0) { // TODO: what about ip_ttl = 0? Is it OK? XLOG_ERROR("proto_socket_read() failed: " "invalid Hop-Limit (TTL) from %s to %s: %d", cstring(src_address), cstring(dst_address), ip_ttl); return; // Error } if (pif_index == 0) { switch (family()) { case AF_INET: // TODO: take care of Linux?? break; // XXX: in IPv4 (except Linux?) there is no pif_index #ifdef HAVE_IPV6 case AF_INET6: XLOG_ERROR("proto_socket_read() failed: " "invalid interface pif_index from %s to %s: %u", cstring(src_address), cstring(dst_address), XORP_UINT_CAST(pif_index)); return; // Error #endif // HAVE_IPV6 default: XLOG_UNREACHABLE(); return; // Error } } // // Find the interface and the vif this message was received on. // XXX: note that in case of IPv4 (except Linux?) we may be able // only to "guess" by using the sender or the destination address. // const IfTreeInterface* ifp = NULL; const IfTreeVif* vifp = NULL; do { // // Find interface and vif based on src address, if a directly // connected sender, or based on dst address if unicast and one // of my local addresses. // However, check first whether we can use 'pif_index' instead. // if (pif_index != 0) { vifp = iftree().find_vif(pif_index); if (vifp != NULL) ifp = iftree().find_interface(vifp->ifname()); break; } if (dst_address.is_multicast()) { iftree().find_interface_vif_same_subnet_or_p2p(src_address, ifp, vifp); } else { iftree().find_interface_vif_by_addr(dst_address, ifp, vifp); } break; } while (false); if ((ifp == NULL) || (vifp == NULL)) { // No vif found. Ignore this packet. #ifdef USE_SOCKET_PER_IFACE // For multicast packets, there seems no good way to filter // currently, so we just have to ignore them here. Ignoring // this error message to decrease spammage if dest is mcast. if (!dst_address.is_multicast()) { // Don't expect this to happen, so log all errors. XLOG_WARNING("proto_socket_read() failed: " "RX packet from %s to %s pif_index %u: no vif found", cstring(src_address), cstring(dst_address), pif_index); } #else // On a multi-port system, one can rx pkts for devices not in use by this system, // so don't print the error messages so often. static int bad_vifs = 0; if ((bad_vifs++ % 1000) == 0) { XLOG_WARNING("proto_socket_read(), total bad_vifs: %i: " "RX packets from %s to %s pif_index %u: no vif found", bad_vifs, cstring(src_address), cstring(dst_address), pif_index); } #endif return;// Error } if (! (ifp->enabled() || vifp->enabled())) { // This vif is down. Silently ignore this packet. return; // Error } // Process the result vector payload(nbytes - ip_hdr_len); memcpy(&payload[0], _rcvbuf + ip_hdr_len, nbytes - ip_hdr_len); recv_packet(ifp->ifname(), vifp->vifname(), src_address, dst_address, ip_ttl, ip_tos, ip_router_alert, ip_internet_control, ext_headers_type, ext_headers_payload, payload); return; // OK } int IoIpSocket::send_packet(const string& if_name, const string& vif_name, const IPvX& src_address, const IPvX& dst_address, int32_t ip_ttl, int32_t ip_tos, bool ip_router_alert, bool ip_internet_control, const vector& ext_headers_type, const vector >& ext_headers_payload, const vector& payload, string& error_msg) { size_t ip_hdr_len = 0; int ctllen = 0; int int_val; int ret_value = XORP_OK; const IfTreeInterface* ifp = NULL; const IfTreeVif* vifp = NULL; struct cmsghdr* cmsgp; void* cmsg_data; // XXX: CMSG_DATA() is aligned, hence void ptr UNUSED(int_val); UNUSED(cmsgp); UNUSED(cmsg_data); XLOG_ASSERT(ext_headers_type.size() == ext_headers_payload.size()); // Initialize state that might be modified later #ifndef HOST_OS_WINDOWS _sndmh.msg_flags = 0; // Initialize flags to zero _sndmh.msg_control = (caddr_t)_sndcmsgbuf; _sndmh.msg_controllen = 0; #endif ifp = iftree().find_interface(if_name); if (ifp == NULL) { error_msg = c_format("No interface %s in tree: %s", if_name.c_str(), iftree().getName().c_str()); return (XORP_ERROR); } vifp = ifp->find_vif(vif_name); if (vifp == NULL) { error_msg = c_format("No interface %s vif %s", if_name.c_str(), vif_name.c_str()); return (XORP_ERROR); } if (! ifp->enabled()) { error_msg = c_format("Interface %s is down", ifp->ifname().c_str()); return (XORP_ERROR); } if (! vifp->enabled()) { error_msg = c_format("Interface %s vif %s is down", ifp->ifname().c_str(), vifp->vifname().c_str()); return (XORP_ERROR); } if (payload.size() > IO_BUF_SIZE) { error_msg = c_format("proto_socket_write() failed: " "cannot send packet on interface %s vif %s " "from %s to %s: " "too much data: %u octets (max = %u)", if_name.c_str(), vif_name.c_str(), src_address.str().c_str(), dst_address.str().c_str(), XORP_UINT_CAST(payload.size()), XORP_UINT_CAST(IO_BUF_SIZE)); return (XORP_ERROR); } // // Assign the TTL and TOS if they were not specified // switch (family()) { case AF_INET: // Assign the TTL if (ip_ttl < 0) { if (ip_internet_control) ip_ttl = MINTTL; else ip_ttl = IPDEFTTL; } // Assign the TOS if (ip_tos < 0) { if (ip_internet_control) ip_tos = IPTOS_PREC_INTERNETCONTROL; // Internet Control else ip_tos = 0; } break; #ifdef HAVE_IPV6 case AF_INET6: { // Assign the TTL if (ip_ttl < 0) { if (ip_internet_control) ip_ttl = MINTTL; else ip_ttl = IPDEFTTL; } // Assign the TOS if (ip_tos < 0) { if (ip_internet_control) { // TODO: XXX: IPTOS for IPv6 is bogus?? // ip_tos = IPTOS_PREC_INTERNETCONTROL; // Internet Control ip_tos = 0; } else { ip_tos = 0; } } } break; #endif // HAVE_IPV6 default: XLOG_UNREACHABLE(); error_msg = c_format("Invalid address family %d", family()); return (XORP_ERROR); } // // Setup the IP header (including the Router Alert option, if specified) // In case of IPv4, if the IP_HDRINCL socket option is enabled, the IP // header and the data are specified as a single entry in the sndiov[] // scatter/gatter array, otherwise we use socket options to set the IP // header information and a single sndiov[] entry with the payload. // In case of IPv6, the IP header information is specified as // ancillary data. // switch (family()) { case AF_INET: { // // XXX: In case of Linux IP_HDRINCL IP packets are not fragmented and // are limited to the interface MTU. The raw(7) Linux manual is wrong // by saying it is a limitation only in Linux 2.2. It is a limitation // in 2.4 and 2.6 and there is no indication this is going to be fixed // in the future. // Hence, in case of Linux we do the following: // - If the IP packet fits in the MTU, then send the packet using // IP_HDRINCL option. // - Otherwise, if the IP source address belongs to the outgoing // interface, then use various socket options to specify some of // the IP header options. // - Otherwise, use IP_HDRINCL and fragment the IP packet in user // space before transmitting it. Note that in this case we need // to manage the ip_id field in the IP header. // // The reasoning behind the above logic is: (1) If the IP source // address doesn't belong to one of the router's IP addresses, then // we must use IP_HDRINCL; (2) We want to avoid as much as possible // user-level IP packet fragmentation, because managing the ip_id // field in user space does not guarantee the same ip_id is reused // by the kernel as well (for the same tuple of ). // // Note that in case of all other OS-es we always use the IP_HDRINCL // option to transmit the packets. // bool do_fragmentation = false; bool do_ip_hdr_include = true; // // Decide whether we should use IP_HDRINCL and whether we should // do IP packet fragmentation. // do { #ifndef HOST_OS_LINUX break; // XXX: The extra processing below is for Linux only #endif // Calculate the final packet size and whether it fits in the MTU ip_hdr_len = IpHeader4::SIZE; if (ip_router_alert) ip_hdr_len += sizeof(ra_opt4); if (ip_hdr_len + payload.size() <= ifp->mtu()) break; if (vifp->find_addr(src_address.get_ipv4()) != NULL) { do_ip_hdr_include = false; break; } do_fragmentation = true; break; } while (false); if (do_ip_hdr_include != _is_ip_hdr_included) { if (enable_ip_hdr_include(do_ip_hdr_include, error_msg) != XORP_OK) { XLOG_ERROR("%s", error_msg.c_str()); assert(error_msg.size()); return (XORP_ERROR); } } if (! _is_ip_hdr_included) { // // Use socket options to set the IP header information // // // Include the Router Alert option // #ifdef IP_OPTIONS if (ip_router_alert) { if (setsockopt(_proto_socket_out, IPPROTO_IP, IP_OPTIONS, XORP_SOCKOPT_CAST(&ra_opt4), sizeof(ra_opt4)) < 0) { error_msg = c_format("setsockopt(IP_OPTIONS, IPOPT_RA) " "failed: %s", XSTRERROR); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } } #endif // IP_OPTIONS // // Set the TTL // if (comm_unicast_ttl_present()) { int result = comm_set_unicast_ttl(_proto_socket_out, ip_ttl); if (XORP_OK != result) return (result); } // // Set the TOS // if (comm_tos_present()) { int result = comm_set_tos(_proto_socket_out, ip_tos); if (XORP_OK != result) return (result); } // // XXX: Bind to the source address so the outgoing IP packet // will use that address. // struct sockaddr_in sin; src_address.copy_out(sin); if (bind(_proto_socket_out, reinterpret_cast(&sin), sizeof(sin)) < 0) { error_msg = c_format("raw socket bind(%s) failed: %s", cstring(src_address), XSTRERROR); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } // // Now hook the data // memcpy(_sndbuf, &payload[0], payload.size()); // XXX: _sndiov[0].iov_base _sndiov[0].iov_len = payload.size(); #ifndef HOST_OS_WINDOWS // Not using aux data, so zero that out. // This used to be done in the proto_socket_transmit code. _sndmh.msg_controllen = 0; #endif // Transmit the packet ret_value = proto_socket_transmit(ifp, vifp, src_address, dst_address, error_msg); break; } else { // NOTE: BSD doesn't seem to support in_pktinfo...not sure if this needs // a work-around or not. --Ben #ifdef IP_PKTINFO int ctllen = 0; ctllen = CMSG_SPACE(sizeof(struct in_pktinfo)); XLOG_ASSERT(ctllen <= CMSG_BUF_SIZE); // XXX #ifndef HOST_OS_WINDOWS struct cmsghdr *cmsgp; struct in_pktinfo *sndpktinfo; // Linux mostly ignores the IP header sent in with sendmsg, especially the // source IP part. The pktinfo logic below make sure it uses the correct // local IP address. This fixes source-routing problems as well. // --Ben Aug 21, 2008 _sndmh.msg_controllen = ctllen; cmsgp = CMSG_FIRSTHDR(&_sndmh); // Add the IPV4_PKTINFO ancillary data cmsgp->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); cmsgp->cmsg_level = SOL_IP; cmsgp->cmsg_type = IP_PKTINFO; cmsg_data = CMSG_DATA(cmsgp); sndpktinfo = reinterpret_cast(cmsg_data); memset(sndpktinfo, 0, sizeof(*sndpktinfo)); src_address.copy_out(sndpktinfo->ipi_spec_dst); #endif #endif } // // Use raw IP header // // // First, estimate total length of ancillary data // // Space for IP_PKTINFO #ifdef IP_PKTINFO ctllen += CMSG_SPACE(sizeof(struct in_pktinfo)); #endif // // Now setup the ancillary data // XLOG_ASSERT(ctllen <= CMSG_BUF_SIZE); // XXX #ifndef HOST_OS_WINDOWS _sndmh.msg_controllen = ctllen; cmsgp = CMSG_FIRSTHDR(&_sndmh); #endif #ifdef IP_PKTINFO #ifndef HOST_OS_WINDOWS // XXX: Linux { // // XXX: Linux ignores the source address in IP header for the // routing table lookup when the packet is sent with sendmsg(2). // The in_pktinfo logic below makes sure the kernel considers // the source address for the routing table lookup // (e.g., selecting the proper forwarding table when the system // is configured to use multiple forwarding tables), and for // setting the IP source route options. // See ip(7) for description of the usage of IP_PKTINFO // and struct in_pktinfo. // struct in_pktinfo *sndpktinfo; // Add the IP_PKTINFO ancillary data cmsgp->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); cmsgp->cmsg_level = SOL_IP; cmsgp->cmsg_type = IP_PKTINFO; cmsg_data = CMSG_DATA(cmsgp); sndpktinfo = reinterpret_cast(cmsg_data); memset(sndpktinfo, 0, sizeof(*sndpktinfo)); src_address.copy_out(sndpktinfo->ipi_spec_dst); } #endif // ! HOST_OS_WINDOWS #endif // IP_PKTINFO // // Set the IPv4 header // IpHeader4Writer ip4(_sndbuf); if (ip_router_alert) { // Include the Router Alert option uint8_t* ip_option_p = ip4.data() + ip4.size(); // // XXX: ra_opt4 is in network order, hence we write it as-is // by using embed_host_32(). // embed_host_32(ip_option_p, ra_opt4); ip_hdr_len = ip4.size() + sizeof(ra_opt4); } else { ip_hdr_len = ip4.size(); } ip4.set_ip_version(IPVERSION); ip4.set_ip_header_len(ip_hdr_len); ip4.set_ip_tos(ip_tos); ip4.set_ip_id(0); // Let kernel fill in ip4.set_ip_off(0); ip4.set_ip_ttl(ip_ttl); ip4.set_ip_p(ip_protocol()); ip4.set_ip_sum(0); // Let kernel fill in ip4.set_ip_src(src_address.get_ipv4()); ip4.set_ip_dst(dst_address.get_ipv4()); ip4.set_ip_len(ip_hdr_len + payload.size()); // // Now hook the data // memcpy(_sndbuf + ip_hdr_len, &payload[0], payload.size()); // XXX: _sndiov[0].iov_base _sndiov[0].iov_len = ip_hdr_len + payload.size(); if (! do_fragmentation) { // Transmit the packet ret_value = proto_socket_transmit(ifp, vifp, src_address, dst_address, error_msg); break; } // // Perform fragmentation and transmit each fragment // list > fragments; list >::iterator iter; // Calculate and set the IPv4 packet ID _ip_id++; if (_ip_id == 0) _ip_id++; ip4.set_ip_id(_ip_id); if (ip4.fragment(ifp->mtu(), fragments, false, error_msg) != XORP_OK) { assert(error_msg.size()); return (XORP_ERROR); } XLOG_ASSERT(! fragments.empty()); for (iter = fragments.begin(); iter != fragments.end(); ++iter) { vector& ip_fragment = *iter; _sndiov[0].iov_len = ip_fragment.size(); memcpy(_sndbuf, &ip_fragment[0], ip_fragment.size()); ret_value = proto_socket_transmit(ifp, vifp, src_address, dst_address, error_msg); if (ret_value != XORP_OK) break; } } break; #ifdef HAVE_IPV6 case AF_INET6: { int hbhlen = 0; struct in6_pktinfo *sndpktinfo; UNUSED(sndpktinfo); UNUSED(hbhlen); // XXX: might be unused for older OSs // // XXX: unlikely IPv4, in IPv6 the 'header' is specified as // ancillary data. // // // First, estimate total length of ancillary data // // Space for IPV6_PKTINFO ctllen += CMSG_SPACE(sizeof(struct in6_pktinfo)); // Space for Router Alert option if (ip_router_alert) { #ifdef HAVE_RFC3542 if ((hbhlen = inet6_opt_init(NULL, 0)) == -1) { error_msg = c_format("inet6_opt_init(NULL) failed"); return (XORP_ERROR); } if ((hbhlen = inet6_opt_append(NULL, 0, hbhlen, IP6OPT_ROUTER_ALERT, 2, 2, NULL)) == -1) { error_msg = c_format("inet6_opt_append(NULL) failed"); return (XORP_ERROR); } if ((hbhlen = inet6_opt_finish(NULL, 0, hbhlen)) == -1) { error_msg = c_format("inet6_opt_finish(NULL) failed"); return (XORP_ERROR); } ctllen += CMSG_SPACE(hbhlen); #else #ifdef HAVE_IPV6_OPTION_SPACE hbhlen = inet6_option_space(sizeof(ra_opt6)); ctllen += hbhlen; #endif #endif // ! HAVE_RFC3542 } // Space for IPV6_TCLASS #ifdef IPV6_TCLASS ctllen += CMSG_SPACE(sizeof(int)); #endif // Space for IPV6_HOPLIMIT ctllen += CMSG_SPACE(sizeof(int)); // Space for the Extension headers for (size_t i = 0; i < ext_headers_type.size(); i++) { // Ignore the types that are not supported switch (ext_headers_type[i]) { case IPPROTO_ROUTING: break; case IPPROTO_DSTOPTS: break; default: continue; // XXX: all other types are not supported break; } ctllen += CMSG_SPACE(ext_headers_payload[i].size()); } #ifndef HOST_OS_WINDOWS // TODO: Implement IPv6 Ancillary data on windows/mingw // TODO: Implement IPV6_HOPLIMIT on windows/mingw?? // TODO: Implement IPV6_RTHDR, DSTOPS on windows/mingw?? // // Now setup the ancillary data // XLOG_ASSERT(ctllen <= CMSG_BUF_SIZE); // XXX _sndmh.msg_controllen = ctllen; cmsgp = CMSG_FIRSTHDR(&_sndmh); // Add the IPV6_PKTINFO ancillary data cmsgp->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); cmsgp->cmsg_level = IPPROTO_IPV6; cmsgp->cmsg_type = IPV6_PKTINFO; cmsg_data = CMSG_DATA(cmsgp); sndpktinfo = reinterpret_cast(cmsg_data); memset(sndpktinfo, 0, sizeof(*sndpktinfo)); if (dst_address.is_unicast() && !dst_address.is_linklocal_unicast()) { // // XXX: don't set the outgoing interface index if we are // sending an unicast packet to a non-link local unicast address. // Otherwise, the sending may fail with EHOSTUNREACH error. // sndpktinfo->ipi6_ifindex = 0; // Let kernel fill in } else { sndpktinfo->ipi6_ifindex = vifp->pif_index(); } src_address.copy_out(sndpktinfo->ipi6_addr); cmsgp = CMSG_NXTHDR(&_sndmh, cmsgp); assert(cmsgp); // // Include the Router Alert option if needed // if (ip_router_alert) { #ifdef HAVE_RFC3542 int currentlen; void *hbhbuf, *optp = NULL; cmsgp->cmsg_len = CMSG_LEN(hbhlen); cmsgp->cmsg_level = IPPROTO_IPV6; cmsgp->cmsg_type = IPV6_HOPOPTS; hbhbuf = CMSG_DATA(cmsgp); currentlen = inet6_opt_init(hbhbuf, hbhlen); if (currentlen == -1) { error_msg = c_format("inet6_opt_init(len = %d) failed", hbhlen); return (XORP_ERROR); } currentlen = inet6_opt_append(hbhbuf, hbhlen, currentlen, IP6OPT_ROUTER_ALERT, 2, 2, &optp); if (currentlen == -1) { error_msg = c_format("inet6_opt_append(len = %d) failed", currentlen); return (XORP_ERROR); } inet6_opt_set_val(optp, 0, &rtalert_code6, sizeof(rtalert_code6)); if (inet6_opt_finish(hbhbuf, hbhlen, currentlen) == -1) { error_msg = c_format("inet6_opt_finish(len = %d) failed", currentlen); return (XORP_ERROR); } cmsgp = CMSG_NXTHDR(&_sndmh, cmsgp); #else // ! HAVE_RFC3542 (i.e., the old advanced API) #ifdef HAVE_IPV6_OPTION_SPACE if (inet6_option_init((void *)cmsgp, &cmsgp, IPV6_HOPOPTS)) { error_msg = c_format("inet6_option_init(IPV6_HOPOPTS) failed"); return (XORP_ERROR); } assert(cmsgp); if (inet6_option_append(cmsgp, ra_opt6, 4, 0)) { error_msg = c_format("inet6_option_append(Router Alert) failed"); return (XORP_ERROR); } cmsgp = CMSG_NXTHDR(&_sndmh, cmsgp); #endif #endif // ! HAVE_RFC3542 } // // Set the TTL // cmsgp->cmsg_len = CMSG_LEN(sizeof(int)); cmsgp->cmsg_level = IPPROTO_IPV6; cmsgp->cmsg_type = IPV6_HOPLIMIT; int_val = ip_ttl; embed_host_int(CMSG_DATA(cmsgp), int_val); cmsgp = CMSG_NXTHDR(&_sndmh, cmsgp); // // Set the TOS // #ifdef IPV6_TCLASS cmsgp->cmsg_len = CMSG_LEN(sizeof(int)); cmsgp->cmsg_level = IPPROTO_IPV6; cmsgp->cmsg_type = IPV6_TCLASS; int_val = ip_tos; embed_host_int(CMSG_DATA(cmsgp), int_val); cmsgp = CMSG_NXTHDR(&_sndmh, cmsgp); #endif // IPV6_TCLASS // // Set the Extension headers // for (size_t i = 0; i < ext_headers_type.size(); i++) { uint8_t header_type = 0; // Translate protocol number (header type) to message type switch (ext_headers_type[i]) { case IPPROTO_ROUTING: header_type = IPV6_RTHDR; break; case IPPROTO_DSTOPTS: header_type = IPV6_DSTOPTS; break; default: continue; // XXX: all other types are not supported } // Set the header const vector& opt_payload(ext_headers_payload[i]); cmsgp->cmsg_len = CMSG_LEN(opt_payload.size()); cmsgp->cmsg_level = IPPROTO_IPV6; cmsgp->cmsg_type = header_type; cmsg_data = CMSG_DATA(cmsgp); memcpy(cmsg_data, &opt_payload[0], opt_payload.size()); cmsgp = CMSG_NXTHDR(&_sndmh, cmsgp); } #endif /* no winders/mingw support for _sndmh */ // // Now hook the data // memcpy(_sndbuf, &payload[0], payload.size()); // XXX: _sndiov[0].iov_base _sndiov[0].iov_len = payload.size(); // Transmit the packet ret_value = proto_socket_transmit(ifp, vifp, src_address, dst_address, error_msg); } break; #endif // HAVE_IPV6 default: XLOG_UNREACHABLE(); error_msg = c_format("Invalid address family %d", family()); return (XORP_ERROR); } return (ret_value); } int IoIpSocket::proto_socket_transmit(const IfTreeInterface* ifp, const IfTreeVif* vifp, const IPvX& src_address, const IPvX& dst_address, string& error_msg) { bool setloop = false; bool setbind = false; int ret_value = XORP_OK; //XLOG_ERROR("proto_socket_transmit: ifp: %s vifp: %s src: %s dst: %s\n", // ifp->ifname().c_str(), vifp->vifname().c_str(), // src_address.str().c_str(), dst_address.str().c_str()); // // Adjust some IPv4 header fields // #ifndef IPV4_RAW_OUTPUT_IS_RAW if (_is_ip_hdr_included && src_address.is_ipv4()) { // // XXX: The stored value should be in host order, and should // include the IPv4 header length. // IpHeader4Writer ip4(_sndbuf); ip4.set_ip_len_host(ip4.ip_len()); } #endif // ! IPV4_RAW_INPUT_IS_RAW // // Unicast/multicast related setting // if (dst_address.is_multicast()) { // Multicast-related setting if (set_default_multicast_interface(ifp->ifname(), vifp->vifname(), error_msg) != XORP_OK) { ret_value = XORP_ERROR; goto ret_label; } // // XXX: we need to enable the multicast loopback so other processes // on the same host can receive the multicast packets. // if (enable_multicast_loopback(true, error_msg) != XORP_OK) { ret_value = XORP_ERROR; goto ret_label; } setloop = true; } else { // Unicast-related setting // // XXX: We need to bind to a network interface under Linux, but // only if we might be running multiple XORP instances. // The heuristic for that is to test whether the unicast forwarding // table ID is configured. // FibConfig& fibconfig = fea_data_plane_manager().fibconfig(); if (fibconfig.unicast_forwarding_table_id_is_configured(family()) && (! vifp->vifname().empty())) { if (comm_bindtodevice_present() == XORP_OK) { ret_value = comm_set_bindtodevice(_proto_socket_out, vifp->vifname().c_str()); if (ret_value == XORP_ERROR) goto ret_label; setbind = true; } } } // // Transmit the packet // #ifndef HOST_OS_WINDOWS // Set some sendmsg()-related fields if (_sndmh.msg_controllen == 0) _sndmh.msg_control = NULL; switch (family()) { case AF_INET: dst_address.copy_out(_to4); _sndmh.msg_namelen = sizeof(_to4); // Using msg_control to pass ipv4 local IP now. --Ben //_sndmh.msg_control = NULL; //_sndmh.msg_controllen = 0; break; #ifdef HAVE_IPV6 case AF_INET6: dst_address.copy_out(_to6); system_adjust_sockaddr_in6_send(_to6, vifp->pif_index()); break; #endif // HAVE_IPV6 default: XLOG_UNREACHABLE(); error_msg = c_format("Invalid address family %d", family()); ret_value = XORP_ERROR; goto ret_label; } if (sendmsg(_proto_socket_out, &_sndmh, 0) < 0) { ret_value = XORP_ERROR; if (errno == ENETDOWN) { // // TODO: check the interface status. // E.g., vif_state_check(family()); // error_msg = c_format("sendmsg failed, error: %s socket: %i", XSTRERROR, (int)(_proto_socket_out)); } else { error_msg = c_format("sendmsg(proto %d size %u from %s to %s " "on interface %s vif %s) failed: %s", ip_protocol(), XORP_UINT_CAST(_sndiov[0].iov_len), cstring(src_address), cstring(dst_address), ifp->ifname().c_str(), vifp->vifname().c_str(), XSTRERROR); } } #else // HOST_OS_WINDOWS do { // XXX: We may use WSASendMsg() on Longhorn to support IPv6. DWORD sent, error; struct sockaddr_storage to; DWORD buffer_count = 1; int to_len = 0; memset(&to, 0, sizeof(to)); dst_address.copy_out(reinterpret_cast(to)); // Set some family-specific arguments switch (family()) { case AF_INET: to_len = sizeof(struct sockaddr_in); break; #ifdef HAVE_IPV6 case AF_INET6: to_len = sizeof(struct sockaddr_in6); break; #endif // HAVE_IPV6 default: XLOG_UNREACHABLE(); error_msg = c_format("Invalid address family %d", family()); ret_value = XORP_ERROR; goto ret_label; } error = WSASendTo(_proto_socket_out, reinterpret_cast(_sndiov), buffer_count, &sent, 0, reinterpret_cast(&to), to_len, NULL, NULL); if (error != 0) { ret_value = XORP_ERROR; error_msg = c_format("WSASendTo(proto %d size %u from %s to %s " "on interface %s vif %s) failed: %s", ip_protocol(), XORP_UINT_CAST(_sndiov[0].iov_len), cstring(src_address), cstring(dst_address), ifp->ifname().c_str(), vifp->vifname().c_str(), win_strerror(GetLastError())); XLOG_ERROR("%s", error_msg.c_str()); } } while (false); #endif // HOST_OS_WINDOWS ret_label: // // Restore some settings // if (setloop) { // Disable multicast loopback string dummy_error_msg; enable_multicast_loopback(false, dummy_error_msg); } if (comm_bindtodevice_present() == XORP_OK) { if (setbind) { // Unbind the interface on Linux platforms. (void)comm_set_bindtodevice(_proto_socket_out, ""); } } if (ret_value != XORP_OK) { assert(error_msg.size()); } return (ret_value); } #endif // HAVE_IP_RAW_SOCKETS xorp/fea/data_plane/io/io_link_dummy.cc0000664000076400007640000001125411421137511020257 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2007-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // I/O link raw communication support. // // The mechanism is Dummy (for testing purpose). // #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/mac.hh" #include "fea/iftree.hh" #include "io_link_dummy.hh" IoLinkDummy::IoLinkDummy(FeaDataPlaneManager& fea_data_plane_manager, const IfTree& iftree, const string& if_name, const string& vif_name, uint16_t ether_type, const string& filter_program) : IoLink(fea_data_plane_manager, iftree, if_name, vif_name, ether_type, filter_program) { } IoLinkDummy::~IoLinkDummy() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the Dummy I/O Link raw communication mechanism: %s", error_msg.c_str()); } } int IoLinkDummy::start(string& error_msg) { UNUSED(error_msg); if (_is_running) return (XORP_OK); _is_running = true; return (XORP_OK); } int IoLinkDummy::stop(string& error_msg) { UNUSED(error_msg); if (! _is_running) return (XORP_OK); _is_running = false; return (XORP_OK); } int IoLinkDummy::join_multicast_group(const Mac& group, string& error_msg) { const IfTreeVif* vifp; // Find the vif vifp = iftree().find_vif(if_name(), vif_name()); if (vifp == NULL) { error_msg = c_format("Joining multicast group %s failed: " "interface %s vif %s not found", cstring(group), if_name().c_str(), vif_name().c_str()); return (XORP_ERROR); } #if 0 // TODO: enable or disable the enabled() check? if (! vifp->enabled()) { error_msg = c_format("Cannot join group %s on interface %s vif %s: " "interface/vif is DOWN", cstring(group), if_name().c_str(), vif_name().c_str()); return (XORP_ERROR); } #endif // 0/1 // Add the group to the set of joined groups IoLinkComm::JoinedMulticastGroup joined_group(group); _joined_groups_table.insert(joined_group); return (XORP_OK); } int IoLinkDummy::leave_multicast_group(const Mac& group, string& error_msg) { const IfTreeVif* vifp; // Find the vif vifp = iftree().find_vif(if_name(), vif_name()); if (vifp == NULL) { error_msg = c_format("Leaving multicast group %s failed: " "interface %s vif %s not found", cstring(group), if_name().c_str(), vif_name().c_str()); return (XORP_ERROR); } #if 0 // TODO: enable or disable the enabled() check? if (! vifp->enabled()) { error_msg = c_format("Cannot leave group %s on interface %s vif %s: " "interface/vif is DOWN", cstring(group), if_name().c_str(), vif_name().c_str()); return (XORP_ERROR); } #endif // 0/1 // Remove the group from the set of joined groups set::iterator iter; IoLinkComm::JoinedMulticastGroup joined_group(group); iter = find(_joined_groups_table.begin(), _joined_groups_table.end(), joined_group); if (iter == _joined_groups_table.end()) { error_msg = c_format("Multicast group %s is not joined on " "interface %s vif %s", group.str().c_str(), if_name().c_str(), vif_name().c_str()); return (XORP_ERROR); } _joined_groups_table.erase(iter); return (XORP_OK); } int IoLinkDummy::send_packet(const Mac& src_address, const Mac& dst_address, uint16_t ether_type, const vector& payload, string& error_msg) { vector packet; // // Prepare the packet for transmission // // XXX: Assume this is an Ethernet packet if (prepare_ethernet_packet(src_address, dst_address, ether_type, payload, packet, error_msg) != XORP_OK) { return (XORP_ERROR); } // // Transmit the packet // // TODO: XXX: Nothing to do // transmit_packet(port, &packet[0], packet.size()); return (XORP_OK); } xorp/fea/data_plane/io/io_ip_socket.hh0000664000076400007640000003101111540224223020071 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __FEA_DATA_PLANE_IO_IO_IP_SOCKET_HH__ #define __FEA_DATA_PLANE_IO_IO_IP_SOCKET_HH__ // // I/O IP raw communication support. // // The mechanism is UNIX raw sockets. // #include "libxorp/xorp.h" #include "libxorp/eventloop.hh" #ifdef HAVE_SYS_UIO_H #include #endif #include "fea/io_ip.hh" // Shall we use one socket per interface for receiving? Defaulting // to enabled for Linux (its the only thing that supports SO_BINDTODEVICE it seems.) #ifdef SO_BINDTODEVICE #define USE_SOCKET_PER_IFACE #endif /** * @short A base class for I/O IP raw socket communication. * * Each protocol 'registers' for I/O and gets assigned one object * of this class. */ class IoIpSocket : public IoIp, public IfTreeListener { public: /** * Constructor for a given address family and protocol. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). * @param iftree the interface tree to use. * @param family the address family (AF_INET or AF_INET6 for IPv4 and IPv6 * respectively). * @param ip_protocol the IP protocol number (IPPROTO_*). */ IoIpSocket(FeaDataPlaneManager& fea_data_plane_manager, const IfTree& iftree, int family, uint8_t ip_protocol); /** * Virtual destructor. */ virtual ~IoIpSocket(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int stop(string& error_msg); /** * Set the default TTL (or hop-limit in IPv6) for the outgoing multicast * packets. * * @param ttl the desired IP TTL (a.k.a. hop-limit in IPv6) value. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_multicast_ttl(int ttl, string& error_msg); /** * Enable/disable multicast loopback when transmitting multicast packets. * * If the multicast loopback is enabled, a transmitted multicast packet * will be delivered back to this host (assuming the host is a member of * the same multicast group). * * @param is_enabled if true, enable the loopback, otherwise disable it. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int enable_multicast_loopback(bool is_enabled, string& error_msg); /** * Set default interface for transmitting multicast packets. * * @param if_name the name of the interface that would become the default * multicast interface. * @param vif_name the name of the vif that would become the default * multicast interface. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_default_multicast_interface(const string& if_name, const string& vif_name, string& error_msg); /** * Join a multicast group on an interface. * * @param if_name the name of the interface to join the multicast group. * @param vif_name the name of the vif to join the multicast group. * @param group the multicast group to join. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int join_multicast_group(const string& if_name, const string& vif_name, const IPvX& group, string& error_msg); /** * Leave a multicast group on an interface. * * @param if_name the name of the interface to leave the multicast group. * @param vif_name the name of the vif to leave the multicast group. * @param group the multicast group to leave. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int leave_multicast_group(const string& if_name, const string& vif_name, const IPvX& group, string& error_msg); /** * Send a raw IP packet. * * @param if_name the interface to send the packet on. It is essential for * multicast. In the unicast case this field may be empty. * @param vif_name the vif to send the packet on. It is essential for * multicast. In the unicast case this field may be empty. * @param src_address the IP source address. * @param dst_address the IP destination address. * @param ip_ttl the IP TTL (hop-limit). If it has a negative value, * the TTL will be set internally before transmission. * @param ip_tos the Type Of Service (Diffserv/ECN bits for IPv4 or * IP traffic class for IPv6). If it has a negative value, the TOS will be * set internally before transmission. * @param ip_router_alert if true, then add the IP Router Alert option to * the IP packet. * @param ip_internet_control if true, then this is IP control traffic. * @param ext_headers_type a vector of integers with the types of the * optional IPv6 extention headers. * @param ext_headers_payload a vector of payload data, one for each * optional IPv6 extention header. The number of entries must match * ext_headers_type. * @param payload the payload, everything after the IP header and options. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int send_packet(const string& if_name, const string& vif_name, const IPvX& src_address, const IPvX& dst_address, int32_t ip_ttl, int32_t ip_tos, bool ip_router_alert, bool ip_internet_control, const vector& ext_headers_type, const vector >& ext_headers_payload, const vector& payload, string& error_msg); /** * Get the file descriptor for receiving protocol messages on the specified vif. * * @return a reference to the file descriptor for receiving protocol * messages. */ XorpFd* findExistingInputSocket(const string& if_name, const string& vif_name); /** Get the input file descriptor to be used for multicast routing. This * should not be bound to any specific interface. Returns NULL if cannot * find or create one. */ XorpFd* mcast_protocol_fd_in(); /** Interface is going away..clean up the sockets for the associated vif(s) */ void notifyDeletingIface(const string& ifname); void notifyErasingIface(const string& ifname) { notifyDeletingIface(ifname); } /** VIF is going away..clean up the socket */ void notifyDeletingVif(const string& ifname, const string& vifname); void notifyErasingVif(const string& ifname, const string& vifname) { notifyDeletingVif(ifname, vifname); } private: /** * Open the protocol sockets. * * The protocol sockets are specific to the particular protocol of * this entry. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int open_proto_sockets(string& error_msg); /** Returns XORP_ERROR on error. */ int initializeInputSocket(XorpFd* rv, string& error_msg); /** Find or create an input socket for the specified VIF. Returns NULL * if the socket cannot be found or created. */ XorpFd* findOrCreateInputSocket(const string& if_name, const string& vif_name, string& error_msg); /** * Close the protocol sockets. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int close_proto_sockets(string& error_msg); /** Helper method to close a single socket. Does not remove from * socket map structure. Deletes memory associated with 'fd'. */ int cleanupXorpFd(XorpFd* fd); /** * Enable/disable the "Header Included" option (for IPv4) on the outgoing * protocol socket. * * If enabled, the IP header of a raw packet should be created * by the application itself, otherwise the kernel will build it. * Note: used only for IPv4. * In RFC-3542, IPV6_PKTINFO has similar functions, * but because it requires the interface index and outgoing address, * it is of little use for our purpose. Also, in RFC-2292 this option * was a flag, so for compatibility reasons we better not set it * here; instead, we will use sendmsg() to specify the header's field * values. * * @param is_enabled if true, enable the option, otherwise disable it. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int enable_ip_hdr_include(bool is_enabled, string& error_msg); /** * Enable/disable receiving information about a packet received on the * incoming protocol socket. * * If enabled, values such as interface index, destination address and * IP TTL (a.k.a. hop-limit in IPv6), and hop-by-hop options will be * received as well. * * @param is_enabled if true, set the option, otherwise reset it. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int enable_recv_pktinfo(XorpFd* input_fd, bool is_enabled, string& error_msg); /** * Read data from a protocol socket, and then call the appropriate protocol * module to process it. * * This is called as a IoEventCb callback. * @param fd file descriptor that with event caused this method to be * called. * @param type the event type. */ void proto_socket_read(XorpFd fd, IoEventType type); /** * Transmit a packet on a protocol socket. * * @param ifp the interface to send the packet on. * @param vifp the vif to send the packet on. * @param src_address the IP source address. * @param dst_address the IP destination address. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int proto_socket_transmit(const IfTreeInterface* ifp, const IfTreeVif* vifp, const IPvX& src_address, const IPvX& dst_address, string& error_msg); // Private state //XorpFd _proto_socket_in; // The socket to receive protocol message // The key is "if_name vif_name" map _proto_sockets_in; #ifdef USE_SOCKET_PER_IFACE XorpFd _mcast_proto_socket_in; #endif XorpFd _proto_socket_out; // The socket to end protocol message bool _is_ip_hdr_included; // True if IP header is included on send uint16_t _ip_id; // IPv4 Header ID uint8_t* _rcvbuf; // Data buffer for receiving uint8_t* _sndbuf; // Data buffer for sending uint8_t* _rcvcmsgbuf; // Control recv info (IPv6 only) uint8_t* _sndcmsgbuf; // Control send info (IPv6 only) struct iovec _rcviov[1]; // The scatter/gatter array for receiving struct iovec _sndiov[1]; // The scatter/gatter array for sending #ifndef HOST_OS_WINDOWS struct msghdr _rcvmh; // The msghdr structure used by recvmsg() struct msghdr _sndmh; // The msghdr structure used by sendmsg() #endif // ! HOST_OS_WINDOWS struct sockaddr_in _from4; // The source addr of recvmsg() msg (IPv4) struct sockaddr_in _to4; // The dest. addr of sendmsg() msg (IPv4) #ifdef HAVE_IPV6 struct sockaddr_in6 _from6; // The source addr of recvmsg() msg (IPv6) struct sockaddr_in6 _to6; // The dest. addr of sendmsg() msg (IPv6) #endif }; #endif // __FEA_DATA_PLANE_IO_IO_IP_SOCKET_HH__ xorp/fea/data_plane/io/io_ip_dummy.hh0000664000076400007640000001641011540224223017742 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2007-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/data_plane/io/io_ip_dummy.hh,v 1.6 2008/10/11 04:20:19 pavlin Exp $ #ifndef __FEA_DATA_PLANE_IO_IO_IP_DUMMY_HH__ #define __FEA_DATA_PLANE_IO_IO_IP_DUMMY_HH__ // // I/O IP raw communication support. // // The mechanism is Dummy (for testing purpose). // #include "libxorp/xorpfd.hh" #include "fea/io_ip.hh" #include "fea/io_ip_manager.hh" /** * @short A base class for Dummy I/O IP raw communication. * * Each protocol 'registers' for I/O and gets assigned one object * of this class. */ class IoIpDummy : public IoIp { public: /** * Constructor for a given address family and protocol. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). * @param iftree the interface tree to use. * @param family the address family (AF_INET or AF_INET6 for IPv4 and IPv6 * respectively). * @param ip_protocol the IP protocol number (IPPROTO_*). */ IoIpDummy(FeaDataPlaneManager& fea_data_plane_manager, const IfTree& iftree, int family, uint8_t ip_protocol); /** * Virtual destructor. */ virtual ~IoIpDummy(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int stop(string& error_msg); /** * Set the default TTL (or hop-limit in IPv6) for the outgoing multicast * packets. * * @param ttl the desired IP TTL (a.k.a. hop-limit in IPv6) value. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_multicast_ttl(int ttl, string& error_msg); /** * Enable/disable multicast loopback when transmitting multicast packets. * * If the multicast loopback is enabled, a transmitted multicast packet * will be delivered back to this host (assuming the host is a member of * the same multicast group). * * @param is_enabled if true, enable the loopback, otherwise disable it. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int enable_multicast_loopback(bool is_enabled, string& error_msg); /** * Set default interface for transmitting multicast packets. * * @param if_name the name of the interface that would become the default * multicast interface. * @param vif_name the name of the vif that would become the default * multicast interface. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_default_multicast_interface(const string& if_name, const string& vif_name, string& error_msg); /** * Join a multicast group on an interface. * * @param if_name the name of the interface to join the multicast group. * @param vif_name the name of the vif to join the multicast group. * @param group the multicast group to join. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int join_multicast_group(const string& if_name, const string& vif_name, const IPvX& group, string& error_msg); /** * Leave a multicast group on an interface. * * @param if_name the name of the interface to leave the multicast group. * @param vif_name the name of the vif to leave the multicast group. * @param group the multicast group to leave. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int leave_multicast_group(const string& if_name, const string& vif_name, const IPvX& group, string& error_msg); /** * Send a raw IP packet. * * @param if_name the interface to send the packet on. It is essential for * multicast. In the unicast case this field may be empty. * @param vif_name the vif to send the packet on. It is essential for * multicast. In the unicast case this field may be empty. * @param src_address the IP source address. * @param dst_address the IP destination address. * @param ip_ttl the IP TTL (hop-limit). If it has a negative value, * the TTL will be set internally before transmission. * @param ip_tos the Type Of Service (Diffserv/ECN bits for IPv4 or * IP traffic class for IPv6). If it has a negative value, the TOS will be * set internally before transmission. * @param ip_router_alert if true, then add the IP Router Alert option to * the IP packet. * @param ip_internet_control if true, then this is IP control traffic. * @param ext_headers_type a vector of integers with the types of the * optional IPv6 extention headers. * @param ext_headers_payload a vector of payload data, one for each * optional IPv6 extention header. The number of entries must match * ext_headers_type. * @param payload the payload, everything after the IP header and options. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int send_packet(const string& if_name, const string& vif_name, const IPvX& src_address, const IPvX& dst_address, int32_t ip_ttl, int32_t ip_tos, bool ip_router_alert, bool ip_internet_control, const vector& ext_headers_type, const vector >& ext_headers_payload, const vector& payload, string& error_msg); /** * Get the file descriptor for receiving protocol messages. * * @return a reference to the file descriptor for receiving protocol * messages. */ XorpFd* mcast_protocol_fd_in() { return (&_dummy_protocol_fd_in); } private: // Private state XorpFd _dummy_protocol_fd_in; // Dummy protocol file descriptor uint8_t _multicast_ttl; // Default multicast TTL bool _multicast_loopback; // True if mcast loopback is enabled string _default_multicast_interface; string _default_multicast_vif; set _joined_groups_table; }; #endif // __FEA_DATA_PLANE_IO_IO_IP_DUMMY_HH__ xorp/fea/data_plane/io/SConscript0000664000076400007640000000262411540224223017123 0ustar greearbgreearb# Copyright (c) 2009-2011 XORP, Inc and Others # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # $ID$ import os Import("env") env = env.Clone() env.AppendUnique(CPPPATH = [ '#', '$BUILDDIR', ]) is_shared = env.has_key('SHAREDLIBS') sources = [ # C++ files 'io_ip_socket.cc', 'io_link_pcap.cc', 'io_tcpudp_socket.cc', ] if env['enable_fea_dummy']: sources += [ 'io_ip_dummy.cc', 'io_link_dummy.cc', 'io_tcpudp_dummy.cc', ] if is_shared: libxio = env.SharedLibrary(target = 'libxorp_fea_io', source = sources) env.Alias('install', env.InstallLibrary(env['xorp_libdir'], libxio)) else: libxio = env.StaticLibrary(target = 'libxorp_fea_io', source = sources) Default(libxio) xorp/fea/data_plane/io/io_link_pcap.cc0000664000076400007640000004053411540225524020056 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2007-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // I/O link raw communication support. // // The mechanism is pcap(3). // #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/mac.hh" #ifdef HAVE_SYS_IOCTL_H #include #endif #ifdef HAVE_NET_IF_DL_H #include #endif #include "fea/iftree.hh" #include "io_link_pcap.hh" #ifdef HAVE_PCAP_H IoLinkPcap::IoLinkPcap(FeaDataPlaneManager& fea_data_plane_manager, const IfTree& iftree, const string& if_name, const string& vif_name, uint16_t ether_type, const string& filter_program) : IoLink(fea_data_plane_manager, iftree, if_name, vif_name, ether_type, filter_program), _pcap(NULL), _datalink_type(-1), _pcap_errbuf(NULL), _multicast_sock(-1) { } IoLinkPcap::~IoLinkPcap() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the I/O Link raw pcap(3) mechanism: %s", error_msg.c_str()); } // Free the buffers if (_pcap_errbuf != NULL) delete[] _pcap_errbuf; } int IoLinkPcap::start(string& error_msg) { if (_is_running) return (XORP_OK); // // Open the multicast L2 join socket // XLOG_ASSERT(_multicast_sock < 0); _multicast_sock = socket(AF_INET, SOCK_DGRAM, 0); if (_multicast_sock < 0) { error_msg = c_format("Error opening multicast L2 join socket: %s", strerror(errno)); return (XORP_ERROR); } if (open_pcap_access(error_msg) != XORP_OK) { close(_multicast_sock); _multicast_sock = -1; return (XORP_ERROR); } _is_running = true; return (XORP_OK); } int IoLinkPcap::stop(string& error_msg) { if (! _is_running) return (XORP_OK); if (close_pcap_access(error_msg) != XORP_OK) return (XORP_ERROR); // // Close the multicast L2 join socket // XLOG_ASSERT(_multicast_sock >= 0); if (close(_multicast_sock) < 0) { error_msg = c_format("Error closing multicast L2 join socket: %s", strerror(errno)); return (XORP_ERROR); } _multicast_sock = -1; _is_running = false; return (XORP_OK); } int IoLinkPcap::join_multicast_group(const Mac& group, string& error_msg) { return (join_leave_multicast_group(true, group, error_msg)); } int IoLinkPcap::leave_multicast_group(const Mac& group, string& error_msg) { return (join_leave_multicast_group(false, group, error_msg)); } int IoLinkPcap::open_pcap_access(string& error_msg) { string dummy_error_msg; if (_packet_fd.is_valid()) return (XORP_OK); // Allocate the buffers if (_pcap_errbuf == NULL) _pcap_errbuf = new char[PCAP_ERRBUF_SIZE]; // // Open the pcap descriptor // _pcap_errbuf[0] = '\0'; _pcap = pcap_open_live(vif_name().c_str(), L2_MAX_PACKET_SIZE, 0, 1, _pcap_errbuf); if (_pcap == NULL) { error_msg = c_format("Cannot open interface %s vif %s " "for pcap access: %s", if_name().c_str(), vif_name().c_str(), _pcap_errbuf); close_pcap_access(dummy_error_msg); return (XORP_ERROR); } if (_pcap_errbuf[0] != '\0') { XLOG_WARNING("Warning opening interface %s vif %s for pcap access: %s", if_name().c_str(), vif_name().c_str(), _pcap_errbuf); } // // Get the data link type // _datalink_type = pcap_datalink(_pcap); if (_datalink_type < 0) { error_msg = c_format("Cannot get the pcap data link type for " "interface %s vif %s: %s", if_name().c_str(), vif_name().c_str(), pcap_geterr(_pcap)); close_pcap_access(dummy_error_msg); return (XORP_ERROR); } switch (_datalink_type) { case DLT_EN10MB: // Ethernet (10Mb, 100Mb, 1000Mb, and up) break; // XXX: data link type recognized #if 0 // // XXX: List of some of the data link types that might be supported // in the future. // case DLT_NULL: // BSD loopback encapsulation case DLT_IEEE802: // IEEE 802.5 Token Ring case DLT_ARCNET: // ARCNET case DLT_SLIP: // SLIP case DLT_PPP: // PPP case DLT_FDDI: // FDDI case DLT_ATM_RFC1483: // RFC 1483 LLC/SNAP-encapsulated ATM case DLT_RAW: // Raw IP case DLT_PPP_SERIAL: // PPP in HDLC-like framing case DLT_PPP_ETHER: // PPPoE case DLT_C_HDLC: // Cisco PPP with HDLC framing case DLT_IEEE802_11: // IEEE 802.11 wireless LAN case DLT_FRELAY: // Frame Relay case DLT_LOOP: // OpenBSD loopback encapsulation case DLT_LINUX_SLL: // Linux "cooked" capture encapsulation case DLT_LTALK: // Apple LocalTalk case DLT_PFLOG: // OpenBSD pflog case DLT_PRISM_HEADER: // Prism monitor mode info + 802.11 header case DLT_IP_OVER_FC: // RFC 2625 IP-over-Fibre Channel case DLT_SUNATM: // SunATM devices case DLT_IEEE802_11_RADIO: // Link-layer information + 802.11 header case DLT_ARCNET_LINUX: // Linux ARCNET case DLT_LINUX_IRDA: // Linux IrDA #endif // 0 default: error_msg = c_format("Data link type %d (%s) on interface %s vif %s " "is not supported", _datalink_type, pcap_datalink_val_to_name(_datalink_type), if_name().c_str(), vif_name().c_str()); close_pcap_access(dummy_error_msg); return (XORP_ERROR); } // // Put the pcap descriptor in non-blocking mode // if (pcap_setnonblock(_pcap, 1, _pcap_errbuf) != 0) { error_msg = c_format("Cannot set interface %s vif %s in pcap " "non-blocking mode: %s", if_name().c_str(), vif_name().c_str(), pcap_geterr(_pcap)); close_pcap_access(dummy_error_msg); return (XORP_ERROR); } // // Put the pcap descriptor in mode that avoids loops when receiving packets // pcap_breakloop(_pcap); // // Obtain a select()-able file descriptor // _packet_fd = pcap_get_selectable_fd(_pcap); if (! _packet_fd.is_valid()) { error_msg = c_format("Cannot get selectable file descriptor for " "pcap access for interface %s vif %s: %s", if_name().c_str(), vif_name().c_str(), pcap_geterr(_pcap)); close_pcap_access(dummy_error_msg); return (XORP_ERROR); } // // Set the pcap filter // // XXX: The filter is logical AND of the EtherType/DSAP with the // user's optional filter program. // string pcap_filter_program; if (ether_type() > 0) { if (ether_type() < ETHERNET_LENGTH_TYPE_THRESHOLD) { // A filter using the DSAP in IEEE 802.2 LLC frame pcap_filter_program = c_format("(ether[%u] = %u)", ETHERNET_HEADER_SIZE, ether_type()); } else { // A filter using the EtherType pcap_filter_program = c_format("(ether proto %u)", ether_type()); } } if (! filter_program().empty()) { if (! pcap_filter_program.empty()) pcap_filter_program += " and "; pcap_filter_program += c_format("(%s)", filter_program().c_str()); } // // XXX: We can't use pcap_filter_program.c_str() as an argument // to pcap_compile(), because the pcap_compile() specificiation // of the argument is not const-ified. // vector program_buf(pcap_filter_program.size() + 1); strncpy(&program_buf[0], pcap_filter_program.c_str(), program_buf.size() - 1); program_buf[program_buf.size() - 1] = '\0'; char* program_c_str = &program_buf[0]; struct bpf_program bpf_program; if (pcap_compile(_pcap, &bpf_program, program_c_str, 1, 0) < 0) { error_msg = c_format("Cannot compile pcap program '%s': %s", program_c_str, pcap_geterr(_pcap)); close_pcap_access(dummy_error_msg); return (XORP_ERROR); } if (pcap_setfilter(_pcap, &bpf_program) != 0) { pcap_freecode(&bpf_program); error_msg = c_format("Cannot set the pcap filter for " "interface %s vif %s for program '%s': %s", if_name().c_str(), vif_name().c_str(), program_c_str, pcap_geterr(_pcap)); close_pcap_access(dummy_error_msg); return (XORP_ERROR); } pcap_freecode(&bpf_program); // // Set the direction of the traffic to capture. // The default is PCAP_D_INOUT (if supported by the system). // // #ifdef PCAP_D_INOUT if (pcap_setdirection(_pcap, PCAP_D_INOUT) != 0) { error_msg = c_format("Cannot set the pcap packet capture " "direction for interface %s vif %s: %s", if_name().c_str(), vif_name().c_str(), pcap_geterr(_pcap)); close_pcap_access(dummy_error_msg); return (XORP_ERROR); } #endif // PCAP_D_INOUT // // Assign a method to read from this descriptor // if (eventloop().add_ioevent_cb(_packet_fd, IOT_READ, callback(this, &IoLinkPcap::ioevent_read_cb)) == false) { error_msg = c_format("Cannot add a pcap file descriptor to the set of " "sockets to read from in the event loop"); close_pcap_access(dummy_error_msg); return (XORP_ERROR); } return (XORP_OK); } int IoLinkPcap::close_pcap_access(string& error_msg) { error_msg = ""; // // Close the descriptors // if (_packet_fd.is_valid()) { // Remove it just in case, even though it may not be select()-ed eventloop().remove_ioevent_cb(_packet_fd); _packet_fd.clear(); } if (_pcap != NULL) { pcap_close(_pcap); _pcap = NULL; } return (XORP_OK); } int IoLinkPcap::join_leave_multicast_group(bool is_join, const Mac& group, string& error_msg) { const IfTreeVif* vifp; // Find the vif vifp = iftree().find_vif(if_name(), vif_name()); if (vifp == NULL) { error_msg = c_format("%s multicast group %s failed: " "interface %s vif %s not found", (is_join)? "Joining" : "Leaving", cstring(group), if_name().c_str(), vif_name().c_str()); return (XORP_ERROR); } #if 0 // TODO: enable or disable the enabled() check? if (! vifp->enabled()) { error_msg = c_format("Cannot %s group %s on interface %s vif %s: " "interface/vif is DOWN", (is_join)? "join" : "leave", cstring(group), if_name().c_str(), vif_name().c_str()); return (XORP_ERROR); } #endif // 0/1 // // Use ioctl(SIOCADDMULTI, struct ifreq) to add L2 multicast membership. // Use ioctl(SIOCDELMULTI, struct ifreq) to delete L2 multicast membership. // // // On Linux we need to use ifreq.ifr_hwaddr with sa_family of AF_UNSPEC. // // On FreeBSD and DragonFlyBSD we need to use ifreq.ifr_addr with sa_family // of AF_LINK and sockaddr_dl aligned address storage. // On NetBSD and OpenBSD we need to use ifreq.ifr_addr with sa_family // of AF_UNSPEC. // struct { struct ifreq r; struct sockaddr_storage s; } buffer; struct ifreq* ifreq_p = &buffer.r; struct sockaddr* sa = NULL; memset(&buffer, 0, sizeof(buffer)); strlcpy(ifreq_p->ifr_name, vif_name().c_str(), sizeof(ifreq_p->ifr_name)); #ifdef HAVE_STRUCT_IFREQ_IFR_HWADDR sa = &ifreq_p->ifr_hwaddr; #else sa = &ifreq_p->ifr_addr; #endif // // Prepare the MAC address // switch (_datalink_type) { case DLT_EN10MB: // Ethernet (10Mb, 100Mb, 1000Mb, and up) { #if defined(HOST_OS_DRAGONFLYBSD) || defined(HOST_OS_FREEBSD) { struct sockaddr_dl* sdl; struct ether_addr ether_addr; group.copy_out(ether_addr); sdl = reinterpret_cast(sa); sdl->sdl_len = sizeof(*sdl); sdl->sdl_family = AF_LINK; sdl->sdl_alen = sizeof(ether_addr); memcpy(LLADDR(sdl), ðer_addr, sizeof(ether_addr)); } #else group.copy_out(*sa); #endif break; } default: // XXX: The data link type on the interface is not supported error_msg = c_format("Cannot %s group %s on interface %s vif %s: " "data link type %d (%s) is not supported", (is_join)? "join" : "leave", cstring(group), if_name().c_str(), vif_name().c_str(), _datalink_type, pcap_datalink_val_to_name(_datalink_type)); return (XORP_ERROR); } // // XXX: NetBSD and OpenBSD have AF_LINK defined, but they insist // on the sockaddr's sa_family being set to AF_UNSPEC. // Verified on NetBSD-3.1 and OpenBSD-4.1. // #if defined(HOST_OS_NETBSD) || defined(HOST_OS_OPENBSD) sa->sa_family = AF_UNSPEC; #endif int request = (is_join)? SIOCADDMULTI : SIOCDELMULTI; if (ioctl(_multicast_sock, request, ifreq_p) < 0) { error_msg = c_format("Cannot %s group %s on interface %s vif %s: %s", (is_join)? "join" : "leave", cstring(group), if_name().c_str(), vif_name().c_str(), strerror(errno)); return (XORP_ERROR); } return (XORP_OK); } void IoLinkPcap::ioevent_read_cb(XorpFd fd, IoEventType type) { UNUSED(fd); UNUSED(type); recv_data(); } void IoLinkPcap::recv_data() { struct pcap_pkthdr pcap_pkthdr; const u_char* packet; size_t packet_size = 0; // Receive a packet packet = pcap_next(_pcap, &pcap_pkthdr); if (packet == NULL) { XLOG_TRACE(is_log_trace(), "No packet"); // XXX: No need to have the task that tries to read again the data _recv_data_task.unschedule(); return; // OK } packet_size = pcap_pkthdr.caplen; // // XXX: Schedule a task to read again the data in case there are more // packets queued for receiving. // _recv_data_task = eventloop().new_oneoff_task( callback(this, &IoLinkPcap::recv_data)); // // Various checks // if (pcap_pkthdr.caplen < pcap_pkthdr.len) { XLOG_WARNING("Received packet on interface %s vif %s: " "data is too short (captured %u expecting %u octets)", if_name().c_str(), vif_name().c_str(), XORP_UINT_CAST(pcap_pkthdr.caplen), XORP_UINT_CAST(pcap_pkthdr.len)); return; // Error } // // Receive and process the packet // switch (_datalink_type) { case DLT_EN10MB: // Ethernet (10Mb, 100Mb, 1000Mb, and up) { recv_ethernet_packet(packet, packet_size); break; } default: // XXX: The data link type on the interface is not supported return; // Error } } int IoLinkPcap::send_packet(const Mac& src_address, const Mac& dst_address, uint16_t ether_type, const vector& payload, string& error_msg) { vector packet; // // Prepare the packet for transmission // switch (_datalink_type) { case DLT_EN10MB: // Ethernet (10Mb, 100Mb, 1000Mb, and up) { if (prepare_ethernet_packet(src_address, dst_address, ether_type, payload, packet, error_msg) != XORP_OK) { return (XORP_ERROR); } break; } default: error_msg = c_format("Data link type %d (%s) on interface %s vif %s " "is not supported", _datalink_type, pcap_datalink_val_to_name(_datalink_type), if_name().c_str(), vif_name().c_str()); return (XORP_ERROR); } // // Transmit the packet // if (pcap_sendpacket(_pcap, &packet[0], packet.size()) != 0) { error_msg = c_format("Sending packet from %s to %s EtherType %u" "on interface %s vif %s failed: %s", src_address.str().c_str(), dst_address.str().c_str(), ether_type, if_name().c_str(), vif_name().c_str(), pcap_geterr(_pcap)); // // XXX: Maybe the device was brought down invalidating the // socket - try to reopen. // TODO: is there a way to check "errno"? // string dummy_error_msg; if ((reopen_pcap_access(dummy_error_msg) == XORP_OK) && (pcap_sendpacket(_pcap, &packet[0], packet.size()) == 0)) { // Success error_msg = ""; } else { return (XORP_ERROR); } } return (XORP_OK); } int IoLinkPcap::reopen_pcap_access(string& error_msg) { if (close_pcap_access(error_msg) != XORP_OK) return (XORP_ERROR); if (open_pcap_access(error_msg) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } #endif // HAVE_PCAP_H xorp/fea/data_plane/io/io_tcpudp_dummy.cc0000664000076400007640000001564011421137511020624 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2007-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // I/O TCP/UDP communication support. // // The mechanism is Dummy (for testing purpose). // #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvx.hh" #include "io_tcpudp_dummy.hh" IoTcpUdpDummy::IoTcpUdpDummy(FeaDataPlaneManager& fea_data_plane_manager, const IfTree& iftree, int family, bool is_tcp) : IoTcpUdp(fea_data_plane_manager, iftree, family, is_tcp) { } IoTcpUdpDummy::~IoTcpUdpDummy() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the I/O TCP/UDP Dummy mechanism: %s", error_msg.c_str()); } } int IoTcpUdpDummy::start(string& error_msg) { UNUSED(error_msg); if (_is_running) return (XORP_OK); _is_running = true; return (XORP_OK); } int IoTcpUdpDummy::stop(string& error_msg) { UNUSED(error_msg); if (! _is_running) return (XORP_OK); _is_running = false; return (XORP_OK); } int IoTcpUdpDummy::tcp_open(string& error_msg) { UNUSED(error_msg); return (XORP_OK); } int IoTcpUdpDummy::udp_open(string& error_msg) { UNUSED(error_msg); return (XORP_OK); } int IoTcpUdpDummy::tcp_open_and_bind(const IPvX& local_addr, uint16_t local_port, string& error_msg) { XLOG_ASSERT(family() == local_addr.af()); UNUSED(local_addr); UNUSED(local_port); UNUSED(error_msg); return (XORP_OK); } int IoTcpUdpDummy::udp_open_and_bind(const IPvX& local_addr, uint16_t local_port, const string& local_dev, int reuse, string& error_msg) { XLOG_ASSERT(family() == local_addr.af()); UNUSED(local_addr); UNUSED(local_dev); UNUSED(reuse); UNUSED(local_port); UNUSED(error_msg); return (XORP_OK); } int IoTcpUdpDummy::udp_open_bind_join(const IPvX& local_addr, uint16_t local_port, const IPvX& mcast_addr, uint8_t ttl, bool reuse, string& error_msg) { XLOG_ASSERT(family() == local_addr.af()); XLOG_ASSERT(family() == mcast_addr.af()); UNUSED(local_addr); UNUSED(local_port); UNUSED(mcast_addr); UNUSED(ttl); UNUSED(reuse); UNUSED(error_msg); return (XORP_OK); } int IoTcpUdpDummy::tcp_open_bind_connect(const IPvX& local_addr, uint16_t local_port, const IPvX& remote_addr, uint16_t remote_port, string& error_msg) { XLOG_ASSERT(family() == local_addr.af()); XLOG_ASSERT(family() == remote_addr.af()); UNUSED(local_addr); UNUSED(local_port); UNUSED(remote_addr); UNUSED(remote_port); UNUSED(error_msg); return (XORP_OK); } int IoTcpUdpDummy::udp_open_bind_connect(const IPvX& local_addr, uint16_t local_port, const IPvX& remote_addr, uint16_t remote_port, string& error_msg) { XLOG_ASSERT(family() == local_addr.af()); XLOG_ASSERT(family() == remote_addr.af()); UNUSED(local_addr); UNUSED(local_port); UNUSED(remote_addr); UNUSED(remote_port); UNUSED(error_msg); return (XORP_OK); } int IoTcpUdpDummy::udp_open_bind_broadcast(const string& ifname, const string& vifname, uint16_t local_port, uint16_t remote_port, bool reuse, bool limited, bool connected, string& error_msg) { UNUSED(ifname); UNUSED(vifname); UNUSED(local_port); UNUSED(remote_port); UNUSED(reuse); UNUSED(limited); UNUSED(connected); UNUSED(error_msg); return (XORP_OK); } int IoTcpUdpDummy::bind(const IPvX& local_addr, uint16_t local_port, string& error_msg) { XLOG_ASSERT(family() == local_addr.af()); UNUSED(local_addr); UNUSED(local_port); UNUSED(error_msg); return (XORP_OK); } int IoTcpUdpDummy::udp_join_group(const IPvX& mcast_addr, const IPvX& join_if_addr, string& error_msg) { XLOG_ASSERT(family() == mcast_addr.af()); XLOG_ASSERT(family() == join_if_addr.af()); UNUSED(mcast_addr); UNUSED(join_if_addr); UNUSED(error_msg); return (XORP_OK); } int IoTcpUdpDummy::udp_leave_group(const IPvX& mcast_addr, const IPvX& leave_if_addr, string& error_msg) { XLOG_ASSERT(family() == mcast_addr.af()); XLOG_ASSERT(family() == leave_if_addr.af()); UNUSED(mcast_addr); UNUSED(leave_if_addr); UNUSED(error_msg); return (XORP_OK); } int IoTcpUdpDummy::close(string& error_msg) { UNUSED(error_msg); return (XORP_OK); } int IoTcpUdpDummy::tcp_listen(uint32_t backlog, string& error_msg) { UNUSED(backlog); UNUSED(error_msg); return (XORP_OK); } int IoTcpUdpDummy::udp_enable_recv(string& error_msg) { UNUSED(error_msg); return (XORP_OK); } int IoTcpUdpDummy::send(const vector& data, string& error_msg) { UNUSED(data); UNUSED(error_msg); return (XORP_OK); } int IoTcpUdpDummy::send_to(const IPvX& remote_addr, uint16_t remote_port, const vector& data, string& error_msg) { XLOG_ASSERT(family() == remote_addr.af()); UNUSED(remote_addr); UNUSED(remote_port); UNUSED(data); UNUSED(error_msg); return (XORP_OK); } int IoTcpUdpDummy::send_from_multicast_if(const IPvX& group_addr, uint16_t group_port, const IPvX& ifaddr, const vector& data, string& error_msg) { XLOG_ASSERT(family() == group_addr.af()); XLOG_ASSERT(family() == ifaddr.af()); UNUSED(group_addr); UNUSED(group_port); UNUSED(ifaddr); UNUSED(data); UNUSED(error_msg); return (XORP_OK); } int IoTcpUdpDummy::set_socket_option(const string& optname, uint32_t optval, string& error_msg) { UNUSED(optname); UNUSED(optval); UNUSED(error_msg); return (XORP_OK); } int IoTcpUdpDummy::set_socket_option(const string& optname, const string& optval, string& error_msg) { UNUSED(optname); UNUSED(optval); UNUSED(error_msg); return (XORP_OK); } int IoTcpUdpDummy::accept_connection(bool is_accepted, string& error_msg) { if (is_accepted) { // Connection accepted return (XORP_OK); } // Connection rejected return (stop(error_msg)); } xorp/fea/data_plane/io/io_link_pcap.hh0000664000076400007640000001357111421137511020065 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2007-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/data_plane/io/io_link_pcap.hh,v 1.15 2008/10/26 23:25:08 pavlin Exp $ #ifndef __FEA_DATA_PLANE_IO_IO_LINK_PCAP_HH__ #define __FEA_DATA_PLANE_IO_IO_LINK_PCAP_HH__ // // I/O link raw communication support. // // The mechanism is pcap(3). // #include "libxorp/xorp.h" #include "libxorp/eventloop.hh" #ifdef HAVE_PCAP_H #include #endif #include "fea/io_link.hh" #ifndef HAVE_PCAP_H typedef void pcap_t; #endif /** * @short A base class for I/O link raw pcap(3)-based communication. * * Each protocol 'registers' for link raw I/O per interface and vif * and gets assigned one object (per interface and vif) of this class. */ class IoLinkPcap : public IoLink { public: /** * Constructor for link-level access for a given interface and vif. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). * @param iftree the interface tree to use. * @param if_name the interface name. * @param vif_name the vif name. * @param ether_type the EtherType protocol number. If it is 0 then * it is unused. * @param filter_program the optional filter program to be applied on the * received packets. The program uses tcpdump(1) style expression. */ IoLinkPcap(FeaDataPlaneManager& fea_data_plane_manager, const IfTree& iftree, const string& if_name, const string& vif_name, uint16_t ether_type, const string& filter_program); /** * Virtual destructor. */ virtual ~IoLinkPcap(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int stop(string& error_msg); /** * Join a multicast group on an interface. * * @param group the multicast group to join. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int join_multicast_group(const Mac& group, string& error_msg); /** * Leave a multicast group on an interface. * * @param group the multicast group to leave. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int leave_multicast_group(const Mac& group, string& error_msg); /** * Send a link-level packet. * * @param src_address the MAC source address. * @param dst_address the MAC destination address. * @param ether_type the EtherType protocol number. * @param payload the payload, everything after the MAC header. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int send_packet(const Mac& src_address, const Mac& dst_address, uint16_t ether_type, const vector& payload, string& error_msg); private: /** * Open the pcap access. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int open_pcap_access(string& error_msg); /** * Close the pcap access. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int close_pcap_access(string& error_msg); /** * Join or leave a multicast group on an interface. * * @param is_join if true, then join the group, otherwise leave. * @param group the multicast group to join/leave. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int join_leave_multicast_group(bool is_join, const Mac& group, string& error_msg); /** * Callback that is called when there data to read from the system. * * This is called as a IoEventCb callback. * @param fd file descriptor that with event caused this method to be * called. * @param type the event type. */ void ioevent_read_cb(XorpFd fd, IoEventType type); /** * Read data from the system, and then call the appropriate * module to process it. */ void recv_data(); /** * Reopen the pcap access. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int reopen_pcap_access(string& error_msg); // Private state XorpFd _packet_fd; // The file descriptor to send and recv packets pcap_t* _pcap; // The pcap descriptor to send and recv packets int _datalink_type; // The pcap-defined DLT_* data link type char* _pcap_errbuf; // The pcap buffer for error messages int _multicast_sock; // The socket to join L2 multicast groups XorpTask _recv_data_task; // Task for receiving pending data }; #endif // __FEA_DATA_PLANE_IO_IO_LINK_PCAP_HH__ xorp/fea/data_plane/io/io_ip_dummy.cc0000664000076400007640000001526711421137511017742 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2007-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // I/O IP raw communication support. // // The mechanism is Dummy (for testing purpose). // #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvx.hh" #include "libxorp/ipvxnet.hh" #include "libxorp/utils.hh" #include "fea/iftree.hh" #include "io_ip_dummy.hh" // // Local constants definitions // #ifndef MINTTL #define MINTTL 1 #endif #ifndef IPDEFTTL #define IPDEFTTL 64 #endif #ifndef MAXTTL #define MAXTTL 255 #endif IoIpDummy::IoIpDummy(FeaDataPlaneManager& fea_data_plane_manager, const IfTree& iftree, int family, uint8_t ip_protocol) : IoIp(fea_data_plane_manager, iftree, family, ip_protocol), _multicast_ttl(MINTTL), _multicast_loopback(true) { } IoIpDummy::~IoIpDummy() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the Dummy I/O IP raw communication mechanism: %s", error_msg.c_str()); } } int IoIpDummy::start(string& error_msg) { UNUSED(error_msg); if (_is_running) return (XORP_OK); _is_running = true; return (XORP_OK); } int IoIpDummy::stop(string& error_msg) { UNUSED(error_msg); if (! _is_running) return (XORP_OK); _is_running = false; return (XORP_OK); } int IoIpDummy::set_multicast_ttl(int ttl, string& error_msg) { UNUSED(error_msg); _multicast_ttl = ttl; return (XORP_OK); } int IoIpDummy::enable_multicast_loopback(bool is_enabled, string& error_msg) { UNUSED(error_msg); _multicast_loopback = is_enabled; return (XORP_OK); } int IoIpDummy::set_default_multicast_interface(const string& if_name, const string& vif_name, string& error_msg) { const IfTreeVif* vifp; // Find the vif vifp = iftree().find_vif(if_name, vif_name); if (vifp == NULL) { error_msg = c_format("Setting the default multicast interface failed:" "interface %s vif %s not found", if_name.c_str(), vif_name.c_str()); return (XORP_ERROR); } _default_multicast_interface = if_name; _default_multicast_vif = vif_name; return (XORP_OK); } int IoIpDummy::join_multicast_group(const string& if_name, const string& vif_name, const IPvX& group, string& error_msg) { const IfTreeVif* vifp; // Find the vif vifp = iftree().find_vif(if_name, vif_name); if (vifp == NULL) { error_msg = c_format("Joining multicast group %s failed: " "interface %s vif %s not found", cstring(group), if_name.c_str(), vif_name.c_str()); return (XORP_ERROR); } #if 0 // TODO: enable or disable the enabled() check? if (! vifp->enabled()) { error_msg = c_format("Cannot join group %s on interface %s vif %s: " "interface/vif is DOWN", cstring(group), if_name.c_str(), vif_name.c_str()); return (XORP_ERROR); } #endif // 0/1 // Add the group to the set of joined groups IoIpComm::JoinedMulticastGroup joined_group(if_name, vif_name, group); _joined_groups_table.insert(joined_group); return (XORP_OK); } int IoIpDummy::leave_multicast_group(const string& if_name, const string& vif_name, const IPvX& group, string& error_msg) { const IfTreeVif* vifp; // Find the vif vifp = iftree().find_vif(if_name, vif_name); if (vifp == NULL) { error_msg = c_format("Leaving multicast group %s failed: " "interface %s vif %s not found", cstring(group), if_name.c_str(), vif_name.c_str()); return (XORP_ERROR); } #if 0 // TODO: enable or disable the enabled() check? if (! vifp->enabled()) { error_msg = c_format("Cannot leave group %s on interface %s vif %s: " "interface/vif is DOWN", cstring(group), if_name.c_str(), vif_name.c_str()); return (XORP_ERROR); } #endif // 0/1 // Remove the group from the set of joined groups set::iterator iter; IoIpComm::JoinedMulticastGroup joined_group(if_name, vif_name, group); iter = find(_joined_groups_table.begin(), _joined_groups_table.end(), joined_group); if (iter == _joined_groups_table.end()) { error_msg = c_format("Multicast group %s is not joined on " "interface %s vif %s", group.str().c_str(), if_name.c_str(), vif_name.c_str()); return (XORP_ERROR); } _joined_groups_table.erase(iter); return (XORP_OK); } int IoIpDummy::send_packet(const string& if_name, const string& vif_name, const IPvX& src_address, const IPvX& dst_address, int32_t ip_ttl, int32_t ip_tos, bool ip_router_alert, bool ip_internet_control, const vector& ext_headers_type, const vector >& ext_headers_payload, const vector& payload, string& error_msg) { const IfTreeInterface* ifp = NULL; const IfTreeVif* vifp = NULL; XLOG_ASSERT(ext_headers_type.size() == ext_headers_payload.size()); ifp = iftree().find_interface(if_name); if (ifp == NULL) { error_msg = c_format("No interface %s", if_name.c_str()); return (XORP_ERROR); } vifp = ifp->find_vif(vif_name); if (vifp == NULL) { error_msg = c_format("No interface %s vif %s", if_name.c_str(), vif_name.c_str()); return (XORP_ERROR); } if (! ifp->enabled()) { error_msg = c_format("Interface %s is down", ifp->ifname().c_str()); return (XORP_ERROR); } if (! vifp->enabled()) { error_msg = c_format("Interface %s vif %s is down", ifp->ifname().c_str(), vifp->vifname().c_str()); return (XORP_ERROR); } UNUSED(src_address); UNUSED(dst_address); UNUSED(ip_ttl); UNUSED(ip_tos); UNUSED(ip_router_alert); UNUSED(ip_internet_control); UNUSED(ext_headers_type); UNUSED(ext_headers_payload); UNUSED(payload); return (XORP_OK); } xorp/fea/data_plane/io/io_link_dummy.hh0000664000076400007640000000771011540224223020272 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2007-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/data_plane/io/io_link_dummy.hh,v 1.6 2008/10/11 04:20:19 pavlin Exp $ #ifndef __FEA_DATA_PLANE_IO_IO_LINK_DUMMY_HH__ #define __FEA_DATA_PLANE_IO_IO_LINK_DUMMY_HH__ // // I/O link raw communication support. // // The mechanism is Dummy (for testing purpose). // #include "libxorp/xorp.h" #include "fea/io_link.hh" #include "fea/io_link_manager.hh" /** * @short A base class for Dummy I/O link raw communication. * * Each protocol 'registers' for link raw I/O per interface and vif * and gets assigned one object (per interface and vif) of this class. */ class IoLinkDummy : public IoLink { public: /** * Constructor for link-level access for a given interface and vif. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). * @param iftree the interface tree to use. * @param if_name the interface name. * @param vif_name the vif name. * @param ether_type the EtherType protocol number. If it is 0 then * it is unused. * @param filter_program the optional filter program to be applied on the * received packets. The program uses tcpdump(1) style expression. */ IoLinkDummy(FeaDataPlaneManager& fea_data_plane_manager, const IfTree& iftree, const string& if_name, const string& vif_name, uint16_t ether_type, const string& filter_program); /** * Virtual destructor. */ virtual ~IoLinkDummy(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int stop(string& error_msg); /** * Join a multicast group on an interface. * * @param group the multicast group to join. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int join_multicast_group(const Mac& group, string& error_msg); /** * Leave a multicast group on an interface. * * @param group the multicast group to leave. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int leave_multicast_group(const Mac& group, string& error_msg); /** * Send a link-level packet. * * @param src_address the MAC source address. * @param dst_address the MAC destination address. * @param ether_type the EtherType protocol number. * @param payload the payload, everything after the MAC header. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int send_packet(const Mac& src_address, const Mac& dst_address, uint16_t ether_type, const vector& payload, string& error_msg); private: set _joined_groups_table; }; #endif // __FEA_DATA_PLANE_IO_IO_LINK_DUMMY_HH__ xorp/fea/data_plane/control_socket/0000775000076400007640000000000011703345405017535 5ustar greearbgreearbxorp/fea/data_plane/control_socket/click_socket.hh0000664000076400007640000005142411540225522022515 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/data_plane/control_socket/click_socket.hh,v 1.6 2008/10/02 21:56:53 bms Exp $ #ifndef __FEA_DATA_PLANE_CONTROL_SOCKET_CLICK_SOCKET_HH__ #define __FEA_DATA_PLANE_CONTROL_SOCKET_CLICK_SOCKET_HH__ #include #ifdef XORP_USE_CLICK #include "libxorp/eventloop.hh" #include "libxorp/exceptions.hh" #include "libxorp/ipvx.hh" class ClickSocketObserver; struct ClickSocketPlumber; class RunCommand; /** * ClickSocket class opens a Click socket and forwards data arriving * on the socket to ClickSocketObservers. The ClickSocket hooks itself * into the EventLoop and activity usually happens asynchronously. */ class ClickSocket : public NONCOPYABLE { public: ClickSocket(EventLoop& eventloop); ~ClickSocket(); /** * Enable/disable Click support. * * Note that this operation does not start the Click operations. * * @param v if true, then enable Click support, otherwise disable it. */ void enable_click(bool v) { _is_enabled = v; } /** * Test if the Click support is enabled. * * @return true if the Click support is enabled, otherwise false. */ bool is_enabled() const { return (_is_enabled); } /** * Test if duplicating the Click routes to the system kernel is enabled. * * @return true if duplicating the Click routes to the system kernel is * enabled, otherwise false. */ bool is_duplicate_routes_to_kernel_enabled() const { return _duplicate_routes_to_kernel; } /** * Enable/disable duplicating the Click routes to the system kernel. * * @param enable if true, then enable duplicating the Click routes to the * system kernel, otherwise disable it. */ void enable_duplicate_routes_to_kernel(bool v) { _duplicate_routes_to_kernel = v; } /** * Enable/disable kernel-level Click. * * Note that this operation does not start the kernel-level Click * operations. * * @param v if true, enable kernel-level Click, otherwise disable it. */ void enable_kernel_click(bool v) { _is_kernel_click = v; } /** * Enable/disable installing kernel-level Click on startup. * * @param v if true, then install kernel-level Click on startup. */ void enable_kernel_click_install_on_startup(bool v) { _kernel_click_install_on_startup = v; } /** * Specify the list of kernel Click modules to load on startup if * installing kernel-level Click on startup is enabled. * * @param v the list of kernel Click modules to load. */ void set_kernel_click_modules(const list& v) { _kernel_click_modules = v; } /** * Specify the kernel-level Click mount directory. * * @param v the kernel-level Click mount directory. */ void set_kernel_click_mount_directory(const string& v) { _kernel_click_mount_directory = v; } /** * Enable/disable user-level Click. * * Note that this operation does not start the user-level Click * operations. * * @param v if true, enable user-level Click, otherwise disable it. */ void enable_user_click(bool v) { _is_user_click = v; } /** * Test if we are running kernel-level Click operations. * * @return true if kernel-level Click is enabled, otherwise false. */ bool is_kernel_click() const { return _is_kernel_click; } /** * Test if we are running user-level Click operations. * * @return true if user-level Click is enabled, otherwise false. */ bool is_user_click() const { return _is_user_click; } /** * Start the Click socket operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int start(string& error_msg); /** * Stop the Click socket operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int stop(string& error_msg); /** * Get the name of the external program to generate the kernel-level Click * configuration. * * @return the name of the external program to generate the kernel-level * Click configuration. */ const string& kernel_click_config_generator_file() const { return (_kernel_click_config_generator_file); } /** * Specify the external program to generate the kernel-level Click * configuration. * * @param v the name of the external program to generate the kernel-level * Click configuration. */ void set_kernel_click_config_generator_file(const string& v) { _kernel_click_config_generator_file = v; } /** * Specify the user-level Click command file. * * @param v the name of the user-level Click command file. */ void set_user_click_command_file(const string& v) { _user_click_command_file = v; } /** * Specify the extra arguments to the user-level Click command. * * @param v the extra arguments to the user-level Click command. */ void set_user_click_command_extra_arguments(const string& v) { _user_click_command_extra_arguments = v; } /** * Specify whether to execute on startup the user-level Click command. * * @param v if true, then execute the user-level Click command on startup. */ void set_user_click_command_execute_on_startup(bool v) { _user_click_command_execute_on_startup = v; } /** * Specify the configuration file to be used by user-level Click on * startup. * * @param v the name of the configuration file to be used by user-level * Click on startup. */ void set_user_click_startup_config_file(const string& v) { _user_click_startup_config_file = v; } /** * Specify the address to use for control access to the user-level * Click. * * @param v the address to use for control access to the user-level * Click. */ void set_user_click_control_address(const IPv4& v) { _user_click_control_address = v; } /** * Specify the socket port to use for control access to the user-level * Click. * * @param v the socket port to use for control access to the user-level * Click. */ void set_user_click_control_socket_port(uint16_t v) { _user_click_control_socket_port = v; } /** * Get the name of the external program to generate the user-level Click * configuration. * * @return the name of the external program to generate the user-level * Click configuration. */ const string& user_click_config_generator_file() const { return (_user_click_config_generator_file); } /** * Specify the external program to generate the user-level Click * configuration. * * @param v the name of the external program to generate the user-level * Click configuration. */ void set_user_click_config_generator_file(const string& v) { _user_click_config_generator_file = v; } /** * Write Click configuration. * * @param element the Click element to write the configuration to. If it * is an empty string, then we use only the @ref handler to write the * configuration. * @param handler the Click handler to write the configuration to. * @param has_kernel_config true if we wish to write the kernel-level Click * configuration (if kernel-level Click is enabled). * @param kernel_config the kernel-level Click configuration to write. * @param has_user_config true if we wish to write the user-level Click * configuration (if user-level Click is enabled). * @param user_config the user-level Click configuration to write. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int write_config(const string& element, const string& handler, bool has_kernel_config, const string& kernel_config, bool has_user_config, const string& user_config, string& error_msg); /** * Write data to Click socket. * * This method also updates the sequence number associated with * this Click socket. * * @return the number of bytes which were written, or -1 if error. */ ssize_t write(XorpFd fd, const void* data, size_t nbytes); /** * Check the status of a previous command. * * @param is_warning if true, the previous command generated a warning. * @param command_warning if @ref is_warning is true, then it contains * the generated warning message. * @param is_error if true, the previous command generated an error. * @param command_error if @ref is_error is true, then it contains * the generated error message. * @param error_msg if the command status cannot be checked, then it * contains the error message with the reason. * @return XORP_OK on success, otherwise XORP_ERROR. */ int check_user_command_status(bool& is_warning, string& command_warning, bool& is_error, string& command_error, string& error_msg); /** * Get the sequence number for next message written into Click. * * The sequence number is derived from the instance number of this Click * socket and a 16-bit counter. * * @return the sequence number for the next message written into * Click. */ uint32_t seqno() const { return (_instance_no << 16 | _seqno); } /** * Get the cached process identifier value. * * @return the cached process identifier value. */ pid_t pid() const { return _pid; } /** * Force socket to read data from kernel-level Click. * * This usually is performed after writing a request that * Click will answer (e.g., after writing a configuration change). * Use sparingly, with caution, and at your own risk. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int force_kernel_click_read(string& error_msg) { return (force_read(_kernel_fd, error_msg)); } /** * Force socket to read data from user-level Click. * * This usually is performed after writing a request that * Click will answer (e.g., after writing a configuration change). * Use sparingly, with caution, and at your own risk. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int force_user_click_read(string& error_msg) { return (force_read(_user_fd, error_msg)); } private: typedef list ObserverList; /** * Force socket to read data. * * This usually is performed after writing a request that * Click will answer (e.g., after writing a configuration change). * Use sparingly, with caution, and at your own risk. * * @param fd the file descriptor to read from. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int force_read(XorpFd fd, string& error_msg); /** * Read data available for ClickSocket and invoke * ClickSocketObserver::clsock_data() on all observers of Click * socket. */ void io_event(XorpFd fd, IoEventType type); /** * Force socket to read data and return the result. * * Note that unlike method @ref force_read(), this method does not * propagate the data to the socket observers. * * @param fd the file descriptor to read from. * @param message the message with the result. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int force_read_message(XorpFd fd, vector& message, string& error_msg); /** * Load the kernel Click modules. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int load_kernel_click_modules(string& error_msg); /** * Unload the kernel Click modules. * * Note: the modules are unloaded in the reverse order they are loaded. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int unload_kernel_click_modules(string& error_msg); /** * Load a kernel module. * * @param module_filename the kernel module filename to load. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int load_kernel_module(const string& module_filename, string& error_msg); /** * Unload a kernel module. * * Note: the module will not be unloaded if it was not loaded * previously by us. * * @param module_filename the kernel module filename to unload. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int unload_kernel_module(const string& module_filename, string& error_msg); /** * Get the kernel module name from the kernel module filename. * * The module name is composed of the name of the file (without * the directory path) but excluding the suffix if that is a well-known * suffix (e.g., ".o" or ".ko"). * * @param module_filename the module filename. * @return the kernel module name. */ string kernel_module_filename2modulename(const string& module_filename); /** * Mount the Click file system. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int mount_click_file_system(string& error_msg); /** * Unmount the Click file system. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int unmount_click_file_system(string& error_msg); /** * Execute the user-level Click command. * * @param command the command to execute. * @param argument_list the list with the command arguments. * @return XORP_OK on success, otherwise XORP_ERROR. */ int execute_user_click_command(const string& command, const list& argument_list); /** * Terminate the user-level Click command. */ void terminate_user_click_command(); /** * The callback to call when there is data on the stdandard output of the * user-level Click command. */ void user_click_command_stdout_cb(RunCommand* run_command, const string& output); /** * The callback to call when there is data on the stdandard error of the * user-level Click command. */ void user_click_command_stderr_cb(RunCommand* run_command, const string& output); /** * The callback to call when there the user-level Click command * is completed. */ void user_click_command_done_cb(RunCommand* run_command, bool success, const string& error_msg); private: static const size_t CLSOCK_BYTES = 8*1024; // Initial guess at msg size inline const IPv4 DEFAULT_USER_CLICK_CONTROL_ADDRESS() { return (IPv4::LOOPBACK()); } static const uint16_t DEFAULT_USER_CLICK_CONTROL_SOCKET_PORT = 13000; static const int CLICK_MAJOR_VERSION = 1; static const int CLICK_MINOR_VERSION = 1; static const size_t CLICK_COMMAND_RESPONSE_MIN_SIZE = 4; static const size_t CLICK_COMMAND_RESPONSE_CODE_SEPARATOR_INDEX = 3; static const int CLICK_COMMAND_CODE_OK = 200; static const int CLICK_COMMAND_CODE_WARNING_MIN = 201; static const int CLICK_COMMAND_CODE_WARNING_MAX = 299; static const int CLICK_COMMAND_CODE_ERROR_MIN = 500; static const int CLICK_COMMAND_CODE_ERROR_MAX = 599; static const TimeVal USER_CLICK_STARTUP_MAX_WAIT_TIME; static const string PROC_LINUX_MODULES_FILE; static const string LINUX_COMMAND_LOAD_MODULE; static const string LINUX_COMMAND_UNLOAD_MODULE; static const string CLICK_FILE_SYSTEM_TYPE; private: EventLoop& _eventloop; XorpFd _kernel_fd; XorpFd _user_fd; ObserverList _ol; uint16_t _seqno; // Seqno of next write() uint16_t _instance_no; // Instance number of this Click socket static uint16_t _instance_cnt; static pid_t _pid; bool _is_enabled; // True if Click is enabled bool _duplicate_routes_to_kernel; // True if duplicating the Click // routes to the kernel is enabled bool _is_kernel_click; // True if kernel Click is enabled bool _is_user_click; // True if user Click is enabled bool _kernel_click_install_on_startup; list _kernel_click_modules; list _loaded_kernel_click_modules; string _kernel_click_mount_directory; string _mounted_kernel_click_mount_directory; string _kernel_click_config_generator_file; string _user_click_command_file; string _user_click_command_extra_arguments; bool _user_click_command_execute_on_startup; string _user_click_startup_config_file; IPv4 _user_click_control_address; uint16_t _user_click_control_socket_port; string _user_click_config_generator_file; RunCommand* _user_click_run_command; friend class ClickSocketPlumber; // class that hooks observers in and out }; class ClickSocketObserver { public: ClickSocketObserver(ClickSocket& cs); virtual ~ClickSocketObserver(); /** * Receive data from the Click socket. * * Note that this method is called asynchronously when the Click socket * has data to receive, therefore it should never be called directly by * anything else except the Click socket facility itself. * * @param data the buffer with the received data. * @param nbytes the number of bytes in the data buffer. */ virtual void clsock_data(const uint8_t* data, size_t nbytes) = 0; /** * Get ClickSocket associated with Observer. */ ClickSocket& click_socket(); private: ClickSocket& _cs; }; class ClickSocketReader : public ClickSocketObserver { public: ClickSocketReader(ClickSocket& cs); virtual ~ClickSocketReader(); /** * Force the reader to receive kernel-level Click data from the specified * Click socket. * * @param cs the Click socket to receive the data from. * @param seqno the sequence number of the data to receive. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int receive_kernel_click_data(ClickSocket& cs, uint32_t seqno, string& error_msg); /** * Force the reader to receive user-level Click data from the specified * Click socket. * * @param cs the Click socket to receive the data from. * @param seqno the sequence number of the data to receive. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int receive_user_click_data(ClickSocket& cs, uint32_t seqno, string& error_msg); /** * Return the buffer as a string with the data that was received. * * @return a C-style string with the data that was received. */ const string& buffer_str() const { return (_cache_data); } /** * Receive data from the Click socket. * * Note that this method is called asynchronously when the Click socket * has data to receive, therefore it should never be called directly by * anything else except the Click socket facility itself. * * @param data the buffer with the received data. * @param nbytes the number of bytes in the data buffer. */ virtual void clsock_data(const uint8_t* data, size_t nbytes); private: ClickSocket& _cs; bool _cache_valid; // Cache data arrived. uint32_t _cache_seqno; // Seqno of Click socket data to // cache so reading via Click // socket can appear synchronous. string _cache_data; // Cached Click socket data. }; #endif // click #endif // __FEA_DATA_PLANE_CONTROL_SOCKET_CLICK_SOCKET_HH__ xorp/fea/data_plane/control_socket/click_socket.cc0000664000076400007640000010553711703345405022514 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2012 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include #ifdef XORP_USE_CLICK #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/run_command.hh" #include "libxorp/utils.hh" #ifdef HAVE_SYS_LINKER_H #include #endif #ifdef HAVE_SYS_MOUNT_H #include #endif #include "libcomm/comm_api.h" #include "click_socket.hh" const string ClickSocket::PROC_LINUX_MODULES_FILE = "/proc/modules"; const string ClickSocket::LINUX_COMMAND_LOAD_MODULE = "/sbin/insmod"; const string ClickSocket::LINUX_COMMAND_UNLOAD_MODULE = "/sbin/rmmod"; const string ClickSocket::CLICK_FILE_SYSTEM_TYPE = "click"; uint16_t ClickSocket::_instance_cnt = 0; pid_t ClickSocket::_pid = getpid(); const TimeVal ClickSocket::USER_CLICK_STARTUP_MAX_WAIT_TIME = TimeVal(1, 0); // // Click Sockets communication with Click // ClickSocket::ClickSocket(EventLoop& eventloop) : _eventloop(eventloop), _seqno(0), _instance_no(_instance_cnt++), _is_enabled(false), _duplicate_routes_to_kernel(false), _is_kernel_click(false), _is_user_click(false), _kernel_click_install_on_startup(false), _user_click_command_execute_on_startup(false), _user_click_control_address(DEFAULT_USER_CLICK_CONTROL_ADDRESS()), _user_click_control_socket_port(DEFAULT_USER_CLICK_CONTROL_SOCKET_PORT), _user_click_run_command(NULL) { } ClickSocket::~ClickSocket() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the Click socket: %s", error_msg.c_str()); } XLOG_ASSERT(_ol.empty()); } int ClickSocket::start(string& error_msg) { if (is_kernel_click() && !_kernel_fd.is_valid()) { // // Install kernel Click (if necessary) // if (_kernel_click_install_on_startup) { string error_msg2; // Load the kernel Click modules if (load_kernel_click_modules(error_msg) != XORP_OK) { unload_kernel_click_modules(error_msg2); return (XORP_ERROR); } // Mount the Click file system if (mount_click_file_system(error_msg) != XORP_OK) { unload_kernel_click_modules(error_msg2); return (XORP_ERROR); } } #ifdef O_NONBLOCK // // Open the Click error file (for reading error messages) // string click_error_filename; click_error_filename = _kernel_click_mount_directory + "/errors"; _kernel_fd = open(click_error_filename.c_str(), O_RDONLY | O_NONBLOCK); if (!_kernel_fd.is_valid()) { error_msg = c_format("Cannot open kernel Click error file %s: %s", click_error_filename.c_str(), strerror(errno)); return (XORP_ERROR); } #endif } if (is_user_click() && !_user_fd.is_valid()) { // // Execute the Click command (if necessary) // if (_user_click_command_execute_on_startup) { // Compose the command and the arguments string command = _user_click_command_file; list argument_list; argument_list.push_back("-f"); argument_list.push_back(_user_click_startup_config_file); argument_list.push_back("-p"); argument_list.push_back(c_format("%u", _user_click_control_socket_port)); if (! _user_click_command_extra_arguments.empty()) { list l = split(_user_click_command_extra_arguments, ' '); argument_list.insert(argument_list.end(), l.begin(), l.end()); } if (execute_user_click_command(command, argument_list) != XORP_OK) { error_msg = c_format("Could not execute the user-level Click"); return (XORP_ERROR); } } // // Open the socket // struct in_addr in_addr; _user_click_control_address.copy_out(in_addr); // // TODO: XXX: get rid of this hackish mechanism of waiting // pre-defined amount of time until the user-level Click program // starts responding. // TimeVal max_wait_time = USER_CLICK_STARTUP_MAX_WAIT_TIME; TimeVal curr_wait_time(0, 100000); // XXX: 100ms TimeVal total_wait_time; do { // // XXX: try-and-wait a number of times up to "max_wait_time", // because the user-level Click program may not response // immediately. // TimerList::system_sleep(curr_wait_time); total_wait_time += curr_wait_time; int in_progress = 0; _user_fd = comm_connect_tcp4(&in_addr, htons(_user_click_control_socket_port), COMM_SOCK_BLOCKING, &in_progress); if (_user_fd.is_valid()) break; if (total_wait_time < max_wait_time) { // XXX: exponentially increase the wait time curr_wait_time += curr_wait_time; if (total_wait_time + curr_wait_time > max_wait_time) curr_wait_time = max_wait_time - total_wait_time; XLOG_WARNING("Could not open user-level Click socket: %s. " "Trying again...", strerror(errno)); continue; } error_msg = c_format("Could not open user-level Click socket: %s", strerror(errno)); terminate_user_click_command(); return (XORP_ERROR); } while (true); // // Read the expected banner // vector message; string error_msg2; if (force_read_message(_user_fd, message, error_msg2) != XORP_OK) { error_msg = c_format("Could not read on startup from user-level " "Click socket: %s", error_msg2.c_str()); terminate_user_click_command(); comm_close(_user_fd); _user_fd.clear(); return (XORP_ERROR); } // // Check the expected banner. // The banner should look like: "Click::ControlSocket/1.1" // do { string::size_type slash1, slash2, dot1, dot2; string banner = string(reinterpret_cast(&message[0]), message.size()); string version; int major, minor; // Find the version number and check it. slash1 = banner.find('/'); if (slash1 == string::npos) { error_msg = c_format("Invalid user-level Click banner: %s", banner.c_str()); goto error_label; } slash2 = banner.find('/', slash1 + 1); if (slash2 != string::npos) version = banner.substr(slash1 + 1, slash2 - slash1 - 1); else version = banner.substr(slash1 + 1); dot1 = version.find('.'); if (dot1 == string::npos) { error_msg = c_format("Invalid user-level Click version: %s", version.c_str()); goto error_label; } dot2 = version.find('.', dot1 + 1); major = atoi(version.substr(0, dot1).c_str()); if (dot2 != string::npos) minor = atoi(version.substr(dot1 + 1, dot2 - dot1 - 1).c_str()); else minor = atoi(version.substr(dot1 + 1).c_str()); if ((major < CLICK_MAJOR_VERSION) || ((major == CLICK_MAJOR_VERSION) && (minor < CLICK_MINOR_VERSION))) { error_msg = c_format("Invalid user-level Click version: " "expected at least %d.%d " "(found %s)", CLICK_MAJOR_VERSION, CLICK_MINOR_VERSION, version.c_str()); goto error_label; } break; error_label: terminate_user_click_command(); comm_close(_user_fd); _user_fd.clear(); return (XORP_ERROR); } while (false); // // Add the socket to the event loop // if (_eventloop.add_ioevent_cb(_user_fd, IOT_READ, callback(this, &ClickSocket::io_event)) == false) { error_msg = c_format("Failed to add user-level Click socket " "to EventLoop"); terminate_user_click_command(); comm_close(_user_fd); _user_fd.clear(); return (XORP_ERROR); } } return (XORP_OK); } int ClickSocket::stop(string& error_msg) { #ifdef HOST_OS_WINDOWS UNUSED(error_msg) return (XORP_ERROR); #else /* !HOST_OS_WINDOWS */ // // XXX: First we should stop user-level Click, and then kernel-level Click. // Otherwise, the user-level Click process may block the unmounting // of the kernel-level Click file system. The reason for this blocking // is unknown... // if (is_user_click()) { terminate_user_click_command(); if (_user_fd >= 0) { // // Remove the socket from the event loop and close it // _eventloop.remove_ioevent_cb(_user_fd); comm_close(_user_fd); _user_fd.clear(); } } if (is_kernel_click()) { // // Close the Click error file (for reading error messages) // if (_kernel_fd.is_valid()) { close(_kernel_fd); _kernel_fd.clear(); } if (unmount_click_file_system(error_msg) != XORP_OK) { string error_msg2; unload_kernel_click_modules(error_msg2); return (XORP_ERROR); } if (unload_kernel_click_modules(error_msg) != XORP_OK) { return (XORP_ERROR); } } return (XORP_OK); #endif /* HOST_OS_WINDOWS */ } int ClickSocket::load_kernel_click_modules(string& error_msg) { list::iterator iter; for (iter = _kernel_click_modules.begin(); iter != _kernel_click_modules.end(); ++iter) { const string& module_filename = *iter; if (load_kernel_module(module_filename, error_msg) != XORP_OK) return (XORP_ERROR); } return (XORP_OK); } int ClickSocket::unload_kernel_click_modules(string& error_msg) { list::reverse_iterator riter; for (riter = _kernel_click_modules.rbegin(); riter != _kernel_click_modules.rend(); ++riter) { const string& module_filename = *riter; if (unload_kernel_module(module_filename, error_msg) != XORP_OK) return (XORP_ERROR); } return (XORP_OK); } int ClickSocket::load_kernel_module(const string& module_filename, string& error_msg) { if (module_filename.empty()) { error_msg = c_format("Kernel module filename to load is empty"); return (XORP_ERROR); } if (find(_loaded_kernel_click_modules.begin(), _loaded_kernel_click_modules.end(), module_filename) != _loaded_kernel_click_modules.end()) { return (XORP_OK); // Module already loaded } string module_name = kernel_module_filename2modulename(module_filename); if (module_name.empty()) { error_msg = c_format("Invalid kernel module filename: %s", module_filename.c_str()); return (XORP_ERROR); } // // Load the kernel module using system-specific mechanism // #ifdef HOST_OS_FREEBSD // // Test if the kernel module was installed already // if (kldfind(module_name.c_str()) >= 0) { return (XORP_OK); // Module with the same name already loaded } // // Load the kernel module // if (kldload(module_filename.c_str()) < 0) { error_msg = c_format("Cannot load kernel module %s: %s", module_filename.c_str(), strerror(errno)); return (XORP_ERROR); } _loaded_kernel_click_modules.push_back(module_filename); return (XORP_OK); #endif // HOST_OS_FREEBSD #ifdef HOST_OS_LINUX // // Test if the kernel module was installed already // char buf[1024]; char name[1024]; FILE* fh = fopen(PROC_LINUX_MODULES_FILE.c_str(), "r"); if (fh == NULL) { error_msg = c_format("Cannot open file %s for reading: %s", PROC_LINUX_MODULES_FILE.c_str(), strerror(errno)); return (XORP_ERROR); } while (fgets(buf, sizeof(buf), fh) != NULL) { char* n = name; char* p = buf; char* s = NULL; // Get the module name: the first word in the line do { while (xorp_isspace(*p)) p++; if (*p == '\0') { s = NULL; break; } while (*p) { if (xorp_isspace(*p)) break; *n++ = *p++; } *n++ = '\0'; s = p; break; } while (true); if (s == NULL) { XLOG_ERROR("%s: cannot get module name for line %s", PROC_LINUX_MODULES_FILE.c_str(), buf); continue; } if (module_name == string(name)) { fclose(fh); return (XORP_OK); // Module with the same name already loaded } } fclose(fh); // // Load the kernel module // // XXX: unfortunately, Linux doesn't have a consistent system API // for loading kernel modules, so we have to relay on user-land command // to do this. Sigh... // string command_line = LINUX_COMMAND_LOAD_MODULE + " " + module_filename; int ret_value = system(command_line.c_str()); if (ret_value != 0) { if (ret_value < 0) { error_msg = c_format("Cannot execute system command '%s': %s", command_line.c_str(), strerror(errno)); } else { error_msg = c_format("Executing system command '%s' " "returned value '%d'", command_line.c_str(), ret_value); } return (XORP_ERROR); } _loaded_kernel_click_modules.push_back(module_filename); return (XORP_OK); #endif // HOST_OS_LINUX #ifdef HOST_OS_MACOSX // TODO: implement it error_msg = c_format("No mechanism to load a kernel module"); return (XORP_ERROR); #endif // HOST_OS_MACOSX #ifdef HOST_OS_NETBSD // TODO: implement it error_msg = c_format("No mechanism to load a kernel module"); return (XORP_ERROR); #endif // HOST_OS_NETBSD #ifdef HOST_OS_OPENBSD // TODO: implement it error_msg = c_format("No mechanism to load a kernel module"); return (XORP_ERROR); #endif // HOST_OS_OPENBSD #ifdef HOST_OS_SOLARIS // TODO: implement it error_msg = c_format("No mechanism to load a kernel module"); return (XORP_ERROR); #endif // HOST_OS_SOLARIS error_msg = c_format("No mechanism to load a kernel module"); return (XORP_ERROR); } int ClickSocket::unload_kernel_module(const string& module_filename, string& error_msg) { if (module_filename.empty()) { error_msg = c_format("Kernel module filename to unload is empty"); return (XORP_ERROR); } if (find(_loaded_kernel_click_modules.begin(), _loaded_kernel_click_modules.end(), module_filename) == _loaded_kernel_click_modules.end()) { return (XORP_OK); // Module not loaded } string module_name = kernel_module_filename2modulename(module_filename); if (module_name.empty()) { error_msg = c_format("Invalid kernel module filename: %s", module_filename.c_str()); return (XORP_ERROR); } // // Unload the kernel module using system-specific mechanism // #ifdef HOST_OS_FREEBSD // // Find the kernel module ID. // int module_id = kldfind(module_name.c_str()); if (module_id < 0) { error_msg = c_format("Cannot unload kernel module %s: " "module ID not found: %s", module_filename.c_str(), strerror(errno)); return (XORP_ERROR); } // // Unload the kernel module // if (kldunload(module_id) < 0) { error_msg = c_format("Cannot unload kernel module %s: %s", module_filename.c_str(), strerror(errno)); return (XORP_ERROR); } // Remove the module filename from the list of loaded modules list::iterator iter; iter = find(_loaded_kernel_click_modules.begin(), _loaded_kernel_click_modules.end(), module_filename); XLOG_ASSERT(iter != _loaded_kernel_click_modules.end()); _loaded_kernel_click_modules.erase(iter); return (XORP_OK); #endif // HOST_OS_FREEBSD #ifdef HOST_OS_LINUX // // Unload the kernel module // // XXX: unfortunately, Linux doesn't have a consistent system API // for loading kernel modules, so we have to relay on user-land command // to do this. Sigh... // string command_line = LINUX_COMMAND_UNLOAD_MODULE + " " + module_name; int ret_value = system(command_line.c_str()); if (ret_value != 0) { if (ret_value < 0) { error_msg = c_format("Cannot execute system command '%s': %s", command_line.c_str(), strerror(errno)); } else { error_msg = c_format("Executing system command '%s' " "returned value '%d'", command_line.c_str(), ret_value); } return (XORP_ERROR); } // Remove the module filename from the list of loaded modules list::iterator iter; iter = find(_loaded_kernel_click_modules.begin(), _loaded_kernel_click_modules.end(), module_filename); XLOG_ASSERT(iter != _loaded_kernel_click_modules.end()); _loaded_kernel_click_modules.erase(iter); return (XORP_OK); #endif // HOST_OS_LINUX #ifdef HOST_OS_MACOSX // TODO: implement it error_msg = c_format("No mechanism to unload a kernel module"); return (XORP_ERROR); #endif // HOST_OS_MACOSX #ifdef HOST_OS_NETBSD // TODO: implement it error_msg = c_format("No mechanism to unload a kernel module"); return (XORP_ERROR); #endif // HOST_OS_NETBSD #ifdef HOST_OS_OPENBSD // TODO: implement it error_msg = c_format("No mechanism to unload a kernel module"); return (XORP_ERROR); #endif // HOST_OS_OPENBSD #ifdef HOST_OS_SOLARIS // TODO: implement it error_msg = c_format("No mechanism to unload a kernel module"); return (XORP_ERROR); #endif // HOST_OS_SOLARIS error_msg = c_format("No mechanism to unload a kernel module"); return (XORP_ERROR); } string ClickSocket::kernel_module_filename2modulename(const string& module_filename) { string filename, module_name; string::size_type slash, dot; list suffix_list; // Find the file name after the last '/' slash = module_filename.rfind('/'); if (slash == string::npos) filename = module_filename; else filename = module_filename.substr(slash + 1); // // Find the module name by excluding the suffix after the last '.' // if that is a well-known suffix (e.g., ".o" or ".ko"). // suffix_list.push_back(".o"); suffix_list.push_back(".ko"); module_name = filename; list::iterator iter; for (iter = suffix_list.begin(); iter != suffix_list.end(); ++iter) { string suffix = *iter; dot = filename.rfind(suffix); if (dot != string::npos) { if (filename.substr(dot) == suffix) { module_name = filename.substr(0, dot); break; } } } return (module_name); } int ClickSocket::mount_click_file_system(string& error_msg) { #if defined(HOST_OS_WINDOWS) // Whilst Cygwin has a mount(), it is very different. // Windows itself has no mount(). UNUSED(error_msg); return (XORP_ERROR); #else if (_kernel_click_mount_directory.empty()) { error_msg = c_format("Kernel Click mount directory is empty"); return (XORP_ERROR); } if (! _mounted_kernel_click_mount_directory.empty()) { if (_kernel_click_mount_directory == _mounted_kernel_click_mount_directory) { return (XORP_OK); // Directory already mounted } error_msg = c_format("Cannot mount Click file system on directory %s: " "Click file system already mounted on " "directory %s", _kernel_click_mount_directory.c_str(), _mounted_kernel_click_mount_directory.c_str()); return (XORP_ERROR); } // // Test if the Click file system has been installed already. // // We do this by tesing whether we can access a number of Click files // within the Click file system. // list click_files; list::iterator iter; size_t files_found = 0; click_files.push_back("/config"); click_files.push_back("/flatconfig"); click_files.push_back("/packages"); click_files.push_back("/version"); for (iter = click_files.begin(); iter != click_files.end(); ++iter) { string click_filename = _kernel_click_mount_directory + *iter; if (access(click_filename.c_str(), R_OK) == 0) { files_found++; } } if (files_found > 0) { if (files_found == click_files.size()) { return (XORP_OK); // Directory already mounted } error_msg = c_format("Click file system mount directory contains " "some Click files"); return (XORP_ERROR); } // // XXX: Linux has different mount(2) API, hence we need to take // care of this. // int ret_value = -1; #ifdef HOST_OS_LINUX ret_value = mount("none", _kernel_click_mount_directory.c_str(), CLICK_FILE_SYSTEM_TYPE.c_str(), 0, 0); #elif defined(__NetBSD__) && __NetBSD_Version__ >= 499002400 ret_value = mount(CLICK_FILE_SYSTEM_TYPE.c_str(), _kernel_click_mount_directory.c_str(), 0, 0, 0); #else ret_value = mount(CLICK_FILE_SYSTEM_TYPE.c_str(), _kernel_click_mount_directory.c_str(), 0, 0); #endif // ! HOST_OS_LINUX if (ret_value != 0) { error_msg = c_format("Cannot mount Click file system " "on directory %s: %s", _kernel_click_mount_directory.c_str(), strerror(errno)); return (XORP_ERROR); } _mounted_kernel_click_mount_directory = _kernel_click_mount_directory; return (XORP_OK); #endif // HOST_OS_WINDOWS } int ClickSocket::unmount_click_file_system(string& error_msg) { #ifdef HOST_OS_WINDOWS UNUSED(error_msg); return (XORP_OK); #else if (_mounted_kernel_click_mount_directory.empty()) return (XORP_OK); // Directory not mounted // // XXX: Linux and Solaris don't have unmount(2). Instead, they have // umount(2), hence we need to take care of this. // int ret_value = -1; #if defined(HOST_OS_LINUX) || defined(HOST_OS_SOLARIS) ret_value = umount(_mounted_kernel_click_mount_directory.c_str()); #else ret_value = unmount(_mounted_kernel_click_mount_directory.c_str(), 0); #endif if (ret_value != 0) { error_msg = c_format("Cannot unmount Click file system " "from directory %s: %s", _mounted_kernel_click_mount_directory.c_str(), strerror(errno)); return (XORP_ERROR); } _mounted_kernel_click_mount_directory.erase(); return (XORP_OK); #endif // HOST_OS_WINDOWS } int ClickSocket::execute_user_click_command(const string& command, const list& argument_list) { if (_user_click_run_command != NULL) return (XORP_ERROR); // XXX: command is already running _user_click_run_command = new RunCommand(_eventloop, command, argument_list, callback(this, &ClickSocket::user_click_command_stdout_cb), callback(this, &ClickSocket::user_click_command_stderr_cb), callback(this, &ClickSocket::user_click_command_done_cb), false /* redirect_stderr_to_stdout */); if (_user_click_run_command->execute() != XORP_OK) { delete _user_click_run_command; _user_click_run_command = NULL; return (XORP_ERROR); } return (XORP_OK); } void ClickSocket::terminate_user_click_command() { if (_user_click_run_command != NULL) { delete _user_click_run_command; _user_click_run_command = NULL; } } void ClickSocket::user_click_command_stdout_cb(RunCommand* run_command, const string& output) { XLOG_ASSERT(_user_click_run_command == run_command); XLOG_INFO("User-level Click stdout output: %s", output.c_str()); } void ClickSocket::user_click_command_stderr_cb(RunCommand* run_command, const string& output) { XLOG_ASSERT(_user_click_run_command == run_command); XLOG_ERROR("User-level Click stderr output: %s", output.c_str()); } void ClickSocket::user_click_command_done_cb(RunCommand* run_command, bool success, const string& error_msg) { XLOG_ASSERT(_user_click_run_command == run_command); if (! success) { // // XXX: if error_msg is empty, the real error message has been // printed already when it was received on stderr. // string msg = c_format("User-level Click command (%s) failed", run_command->command().c_str()); if (error_msg.size()) msg += c_format(": %s", error_msg.c_str()); XLOG_ERROR("%s", msg.c_str()); } delete _user_click_run_command; _user_click_run_command = NULL; } int ClickSocket::write_config(const string& element, const string& handler, bool has_kernel_config, const string& kernel_config, bool has_user_config, const string& user_config, string& error_msg) { #ifdef HOST_OS_WINDOWS UNUSED(element); UNUSED(handler); UNUSED(has_kernel_config); UNUSED(kernel_config); UNUSED(has_user_config); UNUSED(user_config); UNUSED(error_msg); return (0); #else /* !HOST_OS_WINDOWS */ if (is_kernel_click() && has_kernel_config) { // // Prepare the output handler name // string output_handler = element; if (! output_handler.empty()) output_handler += "/" + handler; else output_handler = handler; output_handler = _kernel_click_mount_directory + "/" + output_handler; // // Prepare the socket to write the configuration // int openflags = O_WRONLY | O_TRUNC; #ifdef O_FSYNC openflags |= O_FSYNC; #endif XorpFd fd = ::open(output_handler.c_str(), openflags); if (!fd.is_valid()) { error_msg = c_format("Cannot open kernel Click handler '%s' " "for writing: %s", output_handler.c_str(), strerror(errno)); return (XORP_ERROR); } // // Write the configuration // if (::write(fd, kernel_config.c_str(), kernel_config.size()) != static_cast(kernel_config.size())) { error_msg = c_format("Error writing to kernel Click " "handler '%s': %s", output_handler.c_str(), strerror(errno)); return (XORP_ERROR); } // XXX: we must close the socket before checking the result int close_ret_value = close(fd); // // Check the command status // char error_buf[8 * 1024]; int error_bytes; error_bytes = ::read(_kernel_fd, error_buf, sizeof(error_buf)); if (error_bytes < 0) { error_msg = c_format("Error verifying the command status after " "writing to kernel Click handler: %s", strerror(errno)); return (XORP_ERROR); } if (error_bytes > 0) { error_msg = c_format("Kernel Click command error: %s", error_buf); return (XORP_ERROR); } if (close_ret_value < 0) { // // XXX: If the close() system call returned an error, this is // an indication that the write to the Click handler failed. // // Note that typically we should have caught any errors by // reading the "/errors" handler, so the check for // the return value of close() is the last line of defense. // // The errno should be EINVAL, but we are liberal about this // additional check. // error_msg = c_format("Kernel Click command error: unknown"); return (XORP_ERROR); } } if (is_user_click() && has_user_config) { // // Prepare the output handler name // string output_handler = element; if (! output_handler.empty()) output_handler += "." + handler; else output_handler = handler; // // Prepare the configuration to write // string config = c_format("WRITEDATA %s %u\n", output_handler.c_str(), XORP_UINT_CAST(user_config.size())); config += user_config; // // Write the configuration // if (ClickSocket::write(_user_fd, config.c_str(), config.size()) != static_cast(config.size())) { error_msg = c_format("Error writing to user-level " "Click socket: %s", strerror(errno)); return (XORP_ERROR); } // // Check the command status // bool is_warning, is_error; string command_warning, command_error; if (check_user_command_status(is_warning, command_warning, is_error, command_error, error_msg) != XORP_OK) { error_msg = c_format("Error verifying the command status after " "writing to user-level Click socket: %s", error_msg.c_str()); return (XORP_ERROR); } if (is_warning) { XLOG_WARNING("User-level Click command warning: %s", command_warning.c_str()); } if (is_error) { error_msg = c_format("User-level Click command error: %s", command_error.c_str()); return (XORP_ERROR); } } return (XORP_OK); #endif /* HOST_OS_WINDOWS */ } ssize_t ClickSocket::write(XorpFd fd, const void* data, size_t nbytes) { #ifdef HOST_OS_WINDOWS UNUSED(fd); UNUSED(data); UNUSED(nbytes); return (0); #else _seqno++; return ::write(fd, data, nbytes); #endif } int ClickSocket::check_user_command_status(bool& is_warning, string& command_warning, bool& is_error, string& command_error, string& error_msg) { vector buffer; is_warning = false; is_error = false; if (force_read_message(_user_fd, buffer, error_msg) != XORP_OK) return (XORP_ERROR); // // Split the message into lines // string buffer_str = string(reinterpret_cast(&buffer[0])); list lines; do { string::size_type idx = buffer_str.find("\n"); if (idx == string::npos) { if (! buffer_str.empty()) lines.push_back(buffer_str); break; } string line = buffer_str.substr(0, idx + 1); lines.push_back(line); buffer_str = buffer_str.substr(idx + 1); } while (true); // // Parse the message line-by-line // list::const_iterator iter; bool is_last_command_response = false; for (iter = lines.begin(); iter != lines.end(); ++iter) { const string& line = *iter; if (is_last_command_response) break; if (line.size() < CLICK_COMMAND_RESPONSE_MIN_SIZE) { error_msg = c_format( "User-level Click command line response is too short " "(expected min size %u received %u): %s", XORP_UINT_CAST(CLICK_COMMAND_RESPONSE_MIN_SIZE), XORP_UINT_CAST(line.size()), line.c_str()); return (XORP_ERROR); } // // Get the error code // char separator = line[CLICK_COMMAND_RESPONSE_CODE_SEPARATOR_INDEX]; if ((separator != ' ') && (separator != '-')) { error_msg = c_format("Invalid user-level Click command line " "response (missing code separator): %s", line.c_str()); return (XORP_ERROR); } int error_code = atoi(line.substr(0, CLICK_COMMAND_RESPONSE_CODE_SEPARATOR_INDEX).c_str()); if (separator == ' ') is_last_command_response = true; // // Test the error code // if (error_code == CLICK_COMMAND_CODE_OK) continue; if ((error_code >= CLICK_COMMAND_CODE_WARNING_MIN) && (error_code <= CLICK_COMMAND_CODE_WARNING_MAX)) { is_warning = true; command_warning += line; continue; } if ((error_code >= CLICK_COMMAND_CODE_ERROR_MIN) && (error_code <= CLICK_COMMAND_CODE_ERROR_MAX)) { is_error = true; command_error += line; continue; } // Unknown error code error_msg = c_format("Unknown user-level Click error code: %s", line.c_str()); return (XORP_ERROR); } return (XORP_OK); } int ClickSocket::force_read(XorpFd fd, string& error_msg) { vector message; if (force_read_message(fd, message, error_msg) != XORP_OK) return (XORP_ERROR); // // Notify observers // for (ObserverList::iterator i = _ol.begin(); i != _ol.end(); i++) { (*i)->clsock_data(&message[0], message.size()); } return (XORP_OK); } int ClickSocket::force_read_message(XorpFd fd, vector& message, string& error_msg) { #ifdef HOST_OS_WINDOWS UNUSED(fd); UNUSED(message); UNUSED(error_msg); return (XORP_ERROR); #else /* !HOST_OS_WINDOWS */ vector buffer(CLSOCK_BYTES); for ( ; ; ) { ssize_t got; // Find how much data is queued in the first message do { got = recv(fd, XORP_BUF_CAST(&buffer[0]), buffer.size(), #ifdef MSG_DONTWAIT MSG_DONTWAIT | #endif MSG_PEEK); if ((got < 0) && (errno == EINTR)) continue; // XXX: the receive was interrupted by a signal if ((got < 0) || (got < (ssize_t)buffer.size())) break; // The buffer is big enough buffer.resize(buffer.size() + CLSOCK_BYTES); } while (true); got = read(fd, &buffer[0], buffer.size()); if (got < 0) { if (errno == EINTR) continue; error_msg = c_format("Click socket read error: %s", strerror(errno)); return (XORP_ERROR); } message.resize(got); memcpy(&message[0], &buffer[0], got); break; } return (XORP_OK); #endif /* HOST_OS_WINDOWS */ } void ClickSocket::io_event(XorpFd fd, IoEventType type) { string error_msg; XLOG_ASSERT((fd == _kernel_fd) || (fd == _user_fd)); XLOG_ASSERT(type == IOT_READ); if (force_read(fd, error_msg) != XORP_OK) { XLOG_ERROR("Error force_read() from Click socket: %s", error_msg.c_str()); } } // // Observe Click sockets activity // struct ClickSocketPlumber { typedef ClickSocket::ObserverList ObserverList; static void plumb(ClickSocket& r, ClickSocketObserver* o) { ObserverList& ol = r._ol; ObserverList::iterator i = find(ol.begin(), ol.end(), o); debug_msg("Plumbing ClickSocketObserver %p to ClickSocket%p\n", o, &r); XLOG_ASSERT(i == ol.end()); ol.push_back(o); } static void unplumb(ClickSocket& r, ClickSocketObserver* o) { ObserverList& ol = r._ol; debug_msg("Unplumbing ClickSocketObserver %p from " "ClickSocket %p\n", o, &r); ObserverList::iterator i = find(ol.begin(), ol.end(), o); XLOG_ASSERT(i != ol.end()); ol.erase(i); } }; ClickSocketObserver::ClickSocketObserver(ClickSocket& cs) : _cs(cs) { ClickSocketPlumber::plumb(cs, this); } ClickSocketObserver::~ClickSocketObserver() { ClickSocketPlumber::unplumb(_cs, this); } ClickSocket& ClickSocketObserver::click_socket() { return _cs; } ClickSocketReader::ClickSocketReader(ClickSocket& cs) : ClickSocketObserver(cs), _cs(cs), _cache_valid(false), _cache_seqno(0) { } ClickSocketReader::~ClickSocketReader() { } /** * Force the reader to receive kernel-level Click data from the specified * Click socket. * * @param cs the Click socket to receive the data from. * @param seqno the sequence number of the data to receive. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int ClickSocketReader::receive_kernel_click_data(ClickSocket& cs, uint32_t seqno, string& error_msg) { _cache_seqno = seqno; _cache_valid = false; while (_cache_valid == false) { if (cs.force_kernel_click_read(error_msg) != XORP_OK) return (XORP_ERROR); } return (XORP_OK); } /** * Force the reader to receive user-level Click data from the specified * Click socket. * * @param cs the Click socket to receive the data from. * @param seqno the sequence number of the data to receive. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int ClickSocketReader::receive_user_click_data(ClickSocket& cs, uint32_t seqno, string& error_msg) { _cache_seqno = seqno; _cache_valid = false; while (_cache_valid == false) { if (cs.force_user_click_read(error_msg) != XORP_OK) return (XORP_ERROR); } return (XORP_OK); } /** * Receive data from the Click socket. * * Note that this method is called asynchronously when the Click socket * has data to receive, therefore it should never be called directly by * anything else except the Click socket facility itself. * * @param data the buffer with the received data. * @param nbytes the number of bytes in the @param data buffer. */ void ClickSocketReader::clsock_data(const uint8_t* data, size_t nbytes) { // // Copy data that has been requested to be cached // _cache_data = string(reinterpret_cast(data), nbytes); // memcpy(&_cache_data[0], data, nbytes); _cache_valid = true; } #endif //click xorp/fea/data_plane/control_socket/windows_rras_support.cc0000664000076400007640000002305711540224221024357 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "windows_rras_support.hh" #ifdef HOST_OS_WINDOWS #include "libxorp/win_io.h" #include "windows_routing_socket.h" #include #include #ifndef RRAS_SERVICE_NAME #define RRAS_SERVICE_NAME TEXT("RemoteAccess") #endif // // Helper method to determine if the Routing and Remote Access Service // is installed and running. // bool WinSupport::is_rras_running() { bool is_installed = false; bool is_running = false; SC_HANDLE h_scm = OpenSCManager(NULL, NULL, GENERIC_READ); if (h_scm != NULL) { SC_HANDLE h_rras = OpenService(h_scm, RRAS_SERVICE_NAME, GENERIC_READ); if (h_rras != NULL) { is_installed = true; SERVICE_STATUS ss; if (0 != ControlService(h_rras, SERVICE_CONTROL_INTERROGATE, &ss)) { is_running = true; } else { DWORD result = GetLastError(); if (result == ERROR_SERVICE_CANNOT_ACCEPT_CTRL) { is_running = true; } else if (result != ERROR_SERVICE_NOT_ACTIVE) { XLOG_WARNING("ControlService() failed: %s", win_strerror(result)); } } CloseServiceHandle(h_rras); } else { DWORD result = GetLastError(); if (result != ERROR_SERVICE_DOES_NOT_EXIST) { XLOG_WARNING("OpenService() failed: %s", win_strerror(result)); } } CloseServiceHandle(h_scm); } else { XLOG_WARNING("OpenSCManager() failed: %s", win_strerror(GetLastError())); } return (is_running && is_installed); } int WinSupport::add_protocol_to_rras(int family) { SHORT XORPRTM_BLOCK_SIZE = 0x0004; XORPRTM_GLOBAL_CONFIG igc; HRESULT hr = S_OK; DWORD dwErr = ERROR_SUCCESS; DWORD dwErrT = ERROR_SUCCESS; MPR_SERVER_HANDLE hMprServer = NULL; HANDLE hMprConfig = NULL; LPBYTE pByte = NULL; LPVOID pHeader = NULL; LPVOID pNewHeader = NULL; DWORD dwSize = 0; HANDLE hTransport = NULL; LPWSTR pswzServerName = NULL; int pid; memset(&igc, 0, sizeof(igc)); #if 0 if (family == AF_INET) { pid = PID_IP; } else { pid = PID_IPV6; } #else pid = PID_IP; UNUSED(family); // XXX: No definition of PID_IPV6 yet. #endif dwErr = MprAdminServerConnect(pswzServerName, &hMprServer); if (dwErr == ERROR_SUCCESS) { dwErr = MprAdminTransportGetInfo(hMprServer, pid, &pByte, &dwSize, NULL, NULL); if (dwErr == ERROR_SUCCESS) { MprInfoDuplicate(pByte, &pHeader); MprAdminBufferFree(pByte); pByte = NULL; dwSize = 0; } } dwErrT = MprConfigServerConnect(pswzServerName, &hMprConfig); if (dwErrT == ERROR_SUCCESS) { dwErrT = MprConfigTransportGetHandle(hMprConfig, pid, &hTransport); } if (dwErr != ERROR_SUCCESS) { MprConfigTransportGetInfo(hMprConfig, hTransport, &pByte, &dwSize, NULL, NULL, NULL); MprInfoDuplicate(pByte, &pHeader); MprConfigBufferFree(pByte); pByte = NULL; dwSize = 0; } MprInfoBlockRemove(pHeader, PROTO_IP_XORPRTM, &pNewHeader); if (pNewHeader != NULL) { MprInfoDelete(pHeader); pHeader = pNewHeader; pNewHeader = NULL; } MprInfoBlockAdd(pHeader, PROTO_IP_XORPRTM, XORPRTM_BLOCK_SIZE, 1, (LPBYTE)&igc, &pNewHeader); MprInfoDelete(pHeader); pHeader = NULL; if (hMprServer) { MprAdminTransportSetInfo(hMprServer, pid, (BYTE*)pNewHeader, MprInfoBlockQuerySize(pNewHeader), NULL, 0); } if (hMprConfig && hTransport) { MprConfigTransportSetInfo(hMprConfig, hTransport, (BYTE*)pNewHeader, MprInfoBlockQuerySize(pNewHeader), NULL, 0, NULL); } if (pHeader) MprInfoDelete(pHeader); if (pNewHeader) MprInfoDelete(pNewHeader); if (hMprConfig) MprConfigServerDisconnect(hMprConfig); if (hMprServer) MprAdminServerDisconnect(hMprServer); return ((int)hr); } int WinSupport::restart_rras() { SERVICE_STATUS ss; SC_HANDLE h_scm; SC_HANDLE h_rras; DWORD result; int tries, fatal; h_scm = OpenSCManager(NULL, NULL, GENERIC_READ); if (h_scm == NULL) { return (-1); } h_rras = OpenService(h_scm, RRAS_SERVICE_NAME, GENERIC_READ); if (h_rras == NULL) { result = GetLastError(); CloseServiceHandle(h_scm); return (-1); } fatal = 0; for (tries = 30; tries > 0; tries++) { // Check if the service is running, stopping, or stopped. result = ControlService(h_rras, SERVICE_CONTROL_INTERROGATE, &ss); if (result == NO_ERROR) { // Stopped; carry on if (ss.dwCurrentState == SERVICE_STOPPED) break; // Stopping; poll until it's done if (ss.dwCurrentState == SERVICE_STOP_PENDING) { Sleep(1000); continue; } } else if (result == ERROR_SERVICE_NOT_ACTIVE) { break; } else { fatal = 1; break; } result = ControlService(h_rras, SERVICE_CONTROL_STOP, &ss); if (result == ERROR_SERVICE_NOT_ACTIVE) { break; } else if (result != NO_ERROR) { fatal = 1; break; } } // XXX: error checking missing result = StartService(h_rras, 0, NULL); // XXX: We should check that the service started. CloseServiceHandle(h_rras); CloseServiceHandle(h_scm); return (0); } static const BYTE DLL_CLSID_IPV4[] = RTMV2_CLSID_IPV4; static const BYTE DLL_CLSID_IPV6[] = RTMV2_CLSID_IPV6; static const BYTE DLL_CONFIG_DLL[] = XORPRTM_CONFIG_DLL_NAME; static const BYTE DLL_NAME_IPV4[] = XORPRTM4_DLL_NAME; static const BYTE DLL_NAME_IPV6[] = XORPRTM6_DLL_NAME; static const DWORD DLL_FLAGS = 0x00000002; static const DWORD DLL_PROTO = PROTO_IP_XORPRTM; static const BYTE DLL_TITLE_IPV4[] = RTMV2_CLSID_IPV4; static const BYTE DLL_TITLE_IPV6[] = RTMV2_CLSID_IPV6; static const BYTE DLL_VENDOR[] = XORPRTM_DLL_VENDOR; static const BYTE TRACING_DIR[] = XORPRTM_TRACING_PATH; // // XXX: There is no error handling in this function whatsoever. // int WinSupport::add_protocol_to_registry(int family) { DWORD result; HKEY hKey; if (family != AF_INET && family != AF_INET6) return (-1); result = RegCreateKeyExA( HKEY_LOCAL_MACHINE, family == AF_INET ? HKLM_XORPRTM4_NAME : HKLM_XORPRTM6_NAME, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, NULL); RegSetValueExA(hKey, "ConfigDll", 0, REG_SZ, DLL_CONFIG_DLL, sizeof(DLL_CONFIG_DLL)); if (family == AF_INET) { RegSetValueExA(hKey, "ConfigClsId", 0, REG_SZ, DLL_CLSID_IPV4, sizeof(DLL_CLSID_IPV4)); RegSetValueExA(hKey, "Title", 0, REG_SZ, DLL_TITLE_IPV4, sizeof(DLL_TITLE_IPV4)); RegSetValueExA(hKey, "DllName", 0, REG_SZ, DLL_NAME_IPV4, sizeof(DLL_NAME_IPV4)); } else if (family == AF_INET6) { RegSetValueExA(hKey, "ConfigClsId", 0, REG_SZ, DLL_CLSID_IPV6, sizeof(DLL_CLSID_IPV6)); RegSetValueExA(hKey, "Title", 0, REG_SZ, DLL_TITLE_IPV6, sizeof(DLL_TITLE_IPV6)); RegSetValueExA(hKey, "DllName", 0, REG_SZ, DLL_NAME_IPV6, sizeof(DLL_NAME_IPV6)); } RegSetValueExA(hKey, "Flags", 0, REG_DWORD, (const BYTE*)&DLL_FLAGS, sizeof(DLL_FLAGS)); RegSetValueExA(hKey, "ProtocolId", 0, REG_DWORD, (const BYTE*)&DLL_PROTO, sizeof(DLL_PROTO)); RegSetValueExA(hKey, "VendorName", 0, REG_SZ, DLL_VENDOR, sizeof(DLL_VENDOR)); RegCloseKey(hKey); #if 1 // // XXX: Enable console tracing for debugging. // result = RegCreateKeyExA( HKEY_LOCAL_MACHINE, family == AF_INET ? HKLM_XORPRTM4_TRACING_NAME : HKLM_XORPRTM6_TRACING_NAME, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, NULL); DWORD foo = 1; RegSetValueExA(hKey, "EnableConsoleTracing", 0, REG_DWORD, (BYTE*)&foo, sizeof(foo)); RegSetValueExA(hKey, "EnableFileTracing", 0, REG_DWORD, (BYTE*)&foo, sizeof(foo)); foo = 0xFFFF0000; RegSetValueExA(hKey, "ConsoleTracingMask", 0, REG_DWORD, (BYTE*)&foo, sizeof(foo)); RegSetValueExA(hKey, "FileTracingMask", 0, REG_DWORD, (BYTE*)&foo, sizeof(foo)); foo = 0x00100000; RegSetValueExA(hKey, "MaxFileSize", 0, REG_DWORD, (BYTE*)&foo, sizeof(foo)); RegSetValueExA(hKey, "FileDirectory", 0, REG_EXPAND_SZ, TRACING_DIR, sizeof(TRACING_DIR)); RegCloseKey(hKey); #endif // TRACING return (0); } #else // !HOST_OS_WINDOWS bool WinSupport::is_rras_running() { return (false); } int WinSupport::restart_rras() { return (0); } int WinSupport::add_protocol_to_rras(int family) { return (0); UNUSED(family); } int WinSupport::add_protocol_to_registry(int family) { return (0); UNUSED(family); } #endif // HOST_OS_WINDOWS xorp/fea/data_plane/control_socket/routing_socket_utilities.hh0000664000076400007640000000520011540225522025201 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __FEA_ROUTING_SOCKET_UTILS_HH__ #define __FEA_ROUTING_SOCKET_UTILS_HH__ #include #if defined(HAVE_ROUTING_SOCKETS) || defined(HOST_OS_WINDOWS) #include "fea/fte.hh" #include "fea/iftree.hh" /** * @short Helper class for various RTM-format related utilities. */ class RtmUtils { public: /** * Convert a message type from routing socket message into * human-readable form. * * @param m message type from routing socket message. * @return human-readable message of the message type. */ static string rtm_msg_type(uint32_t m); /** * Get pointers to set of socket addresses as defined by a mask. * * @param amask the mask that defines the set of socket addresses. * @param sock the pointer to the first socket address. * @param rti_info the array with the pointers to store the result. */ static void get_rta_sockaddr(uint32_t amask, const struct sockaddr* sock, const struct sockaddr* rti_info[]); /** * Get the mask length encoded in sockaddr. * * @param family the address family. * @param sock the socket address with the encoded mask length. * @return the mask length if successfully decoded, otherwise -1. */ static int get_sock_mask_len(int family, const struct sockaddr* sock); /** * Extract the routing information from RTM message. * * @param iftree the interface tree to use. * @param fte the return-by-reference @ref FteX entry to return the result. * @param rtm the RTM routing message. * @return XORP_OK on success, otherwise XORP_ERROR. */ static int rtm_get_to_fte_cfg(const IfTree& iftree, FteX& fte, const struct rt_msghdr* rtm); }; #endif #endif // __FEA_ROUTING_SOCKET_UTILS_HH__ xorp/fea/data_plane/control_socket/netlink_socket_utilities.cc0000664000076400007640000011336111703345405025160 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2012 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include #ifdef HAVE_NETLINK_SOCKETS // Uncomment this to have debug_msg() active in this file. //#define DEBUG_LOGGING #include "fea/fea_module.h" #include "fea/fibconfig.hh" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvx.hh" #ifdef HAVE_NET_IF_H #include #endif #ifdef HAVE_LINUX_TYPES_H #include #endif #ifdef HAVE_LINUX_RTNETLINK_H #include #endif #include "libproto/packet.hh" #include "netlink_socket.hh" #include "netlink_socket_utilities.hh" #include "system_utilities.hh" #include "../ifconfig/ifconfig_media.hh" // // NETLINK format related utilities for manipulating data // (e.g., obtained by netlink(7) sockets mechanism). // string NlmUtils::nlm_print_msg(const vector& buffer) { ostringstream oss; size_t buffer_bytes = buffer.size(); AlignData align_data(buffer); const struct nlmsghdr* nlh; for (nlh = align_data.payload(); NLMSG_OK(nlh, buffer_bytes); nlh = NLMSG_NEXT(const_cast(nlh), buffer_bytes)) { void* nlmsg_data = NLMSG_DATA(const_cast(nlh)); oss << "type: " << NlmUtils::nlm_msg_type(nlh->nlmsg_type); // Decode a few further. switch (nlh->nlmsg_type) { case NLMSG_ERROR: { const struct nlmsgerr* err; err = reinterpret_cast(nlmsg_data); if (nlh->nlmsg_len < NLMSG_LENGTH(sizeof(*err))) { XLOG_ERROR("AF_NETLINK nlmsgerr length error"); break; } errno = -err->error; oss << " AF_NETLINK NLMSG_ERROR message: " << strerror(errno) << endl; break; } case RTM_NEWROUTE: case RTM_DELROUTE: case RTM_GETROUTE: { const struct rtmsg* rtmsg; int rta_len = RTM_PAYLOAD(nlh); if (rta_len < 0) { XLOG_ERROR("AF_NETLINK rtmsg length error"); break; } rtmsg = reinterpret_cast(nlmsg_data); oss << " rtm_type: "; if (rtmsg->rtm_type == RTN_MULTICAST) oss << "MULTICAST"; else if (rtmsg->rtm_type == RTN_BROADCAST) oss << "BROADCAST"; else if (rtmsg->rtm_type == RTN_UNICAST) oss << "UNICAST"; else oss << (int)(rtmsg->rtm_type); const struct rtattr *rtattr; const struct rtattr *rta_array[RTA_MAX + 1]; int family = rtmsg->rtm_family; // // Get the attributes // memset(rta_array, 0, sizeof(rta_array)); rtattr = RTM_RTA(const_cast(rtmsg)); NlmUtils::get_rtattr(rtattr, rta_len, rta_array, sizeof(rta_array) / sizeof(rta_array[0])); int rtmt = rtmsg->rtm_table; #ifdef HAVE_NETLINK_SOCKET_ATTRIBUTE_RTA_TABLE if (rta_array[RTA_TABLE] != NULL) { const uint8_t* p = static_cast( RTA_DATA(const_cast(rta_array[RTA_TABLE]))); rtmt = extract_host_int(p); } #endif oss << " table: " << rtmt; IPvX nexthop_addr(family); IPvX dst_addr(family); if (rta_array[RTA_DST] == NULL) { // The default entry } else { // TODO: fix this!! dst_addr.copy_in(family, (uint8_t *)RTA_DATA(const_cast(rta_array[RTA_DST]))); dst_addr = system_adjust_ipvx_recv(dst_addr); oss << " dest: " << dst_addr.str() << "/" << (int)(rtmsg->rtm_dst_len); } if (rta_array[RTA_GATEWAY] != NULL) { nexthop_addr.copy_in(family, (uint8_t *)RTA_DATA(const_cast(rta_array[RTA_GATEWAY]))); oss << " nexthop: " << nexthop_addr.str(); } if (rtmsg->rtm_protocol == RTPROT_XORP) oss << " proto: XORP"; else oss << " proto: " << rtmsg->rtm_protocol; // Get the interface index if (rta_array[RTA_OIF] != NULL) { const uint8_t* p = static_cast( RTA_DATA(const_cast(rta_array[RTA_OIF]))); oss << " iface: " << extract_host_int(p); } if (rta_array[RTA_PRIORITY] != NULL) { const uint8_t* p = static_cast( RTA_DATA(const_cast(rta_array[RTA_PRIORITY]))); oss << " metric: " << extract_host_int(p); } oss << endl; } default: // ignore oss << endl; } } return oss.str(); }//nlm_print_msg /** * @param m message type from netlink socket message * @return human readable form. */ string NlmUtils::nlm_msg_type(uint32_t m) { struct { uint32_t value; const char* name; } nlm_msg_types[] = { #define RTM_MSG_ENTRY(X) { X, #X } #ifdef NLMSG_ERROR RTM_MSG_ENTRY(NLMSG_ERROR), #endif #ifdef NLMSG_DONE RTM_MSG_ENTRY(NLMSG_DONE), #endif #ifdef NLMSG_NOOP RTM_MSG_ENTRY(NLMSG_NOOP), #endif #ifdef RTM_NEWLINK RTM_MSG_ENTRY(RTM_NEWLINK), #endif #ifdef RTM_DELLINK RTM_MSG_ENTRY(RTM_DELLINK), #endif #ifdef RTM_GETLINK RTM_MSG_ENTRY(RTM_GETLINK), #endif #ifdef RTM_NEWADDR RTM_MSG_ENTRY(RTM_NEWADDR), #endif #ifdef RTM_DELADDR RTM_MSG_ENTRY(RTM_DELADDR), #endif #ifdef RTM_GETADDR RTM_MSG_ENTRY(RTM_GETADDR), #endif #ifdef RTM_NEWROUTE RTM_MSG_ENTRY(RTM_NEWROUTE), #endif #ifdef RTM_DELROUTE RTM_MSG_ENTRY(RTM_DELROUTE), #endif #ifdef RTM_GETROUTE RTM_MSG_ENTRY(RTM_GETROUTE), #endif #ifdef RTM_NEWNEIGH RTM_MSG_ENTRY(RTM_NEWNEIGH), #endif #ifdef RTM_DELNEIGH RTM_MSG_ENTRY(RTM_DELNEIGH), #endif #ifdef RTM_GETNEIGH RTM_MSG_ENTRY(RTM_GETNEIGH), #endif #ifdef RTM_NEWRULE RTM_MSG_ENTRY(RTM_NEWRULE), #endif #ifdef RTM_DELRULE RTM_MSG_ENTRY(RTM_DELRULE), #endif #ifdef RTM_GETRULE RTM_MSG_ENTRY(RTM_GETRULE), #endif #ifdef RTM_NEWQDISC RTM_MSG_ENTRY(RTM_NEWQDISC), #endif #ifdef RTM_DELQDISC RTM_MSG_ENTRY(RTM_DELQDISC), #endif #ifdef RTM_GETQDISC RTM_MSG_ENTRY(RTM_GETQDISC), #endif #ifdef RTM_NEWTCLASS RTM_MSG_ENTRY(RTM_NEWTCLASS), #endif #ifdef RTM_DELTCLASS RTM_MSG_ENTRY(RTM_DELTCLASS), #endif #ifdef RTM_GETTCLASS RTM_MSG_ENTRY(RTM_GETTCLASS), #endif #ifdef RTM_NEWTFILTER RTM_MSG_ENTRY(RTM_NEWTFILTER), #endif #ifdef RTM_DELTFILTER RTM_MSG_ENTRY(RTM_DELTFILTER), #endif #ifdef RTM_GETTFILTER RTM_MSG_ENTRY(RTM_GETTFILTER), #endif #ifdef RTM_MAX RTM_MSG_ENTRY(RTM_MAX), #endif { ~0U, "Unknown" } }; const size_t n_nlm_msgs = sizeof(nlm_msg_types) / sizeof(nlm_msg_types[0]); const char* ret = 0; for (size_t i = 0; i < n_nlm_msgs; i++) { ret = nlm_msg_types[i].name; if (nlm_msg_types[i].value == m) break; } return ret; } void NlmUtils::get_rtattr(const struct rtattr* rtattr, int rta_len, const struct rtattr* rta_array[], size_t rta_array_n) { while (RTA_OK(rtattr, rta_len)) { if (rtattr->rta_type < rta_array_n) rta_array[rtattr->rta_type] = rtattr; rtattr = RTA_NEXT(const_cast(rtattr), rta_len); } if (rta_len) { XLOG_WARNING("get_rtattr() failed: AF_NETLINK deficit in rtattr: " "%d rta_len remaining", rta_len); } } int NlmUtils::nlm_get_to_fte_cfg(const IfTree& iftree, FteX& fte, const struct nlmsghdr* nlh, const struct rtmsg* rtmsg, int rta_len, const FibConfig& fibconfig, string& err_msg) { const struct rtattr *rtattr; const struct rtattr *rta_array[RTA_MAX + 1]; int if_index = 0; // XXX: initialized with an invalid value bool lookup_ifindex = true; string if_name; string vif_name; int family = fte.nexthop().af(); bool is_deleted = false; // Reset the result fte.zero(); // Test if this entry was deleted if (nlh->nlmsg_type == RTM_DELROUTE) is_deleted = true; // // Get the attributes // memset(rta_array, 0, sizeof(rta_array)); rtattr = RTM_RTA(const_cast(rtmsg)); NlmUtils::get_rtattr(rtattr, rta_len, rta_array, sizeof(rta_array) / sizeof(rta_array[0])); // Discard the route if we are using a specific table. if (fibconfig.unicast_forwarding_table_id_is_configured(family)) { int rttable = fibconfig.unicast_forwarding_table_id(family); int rtmt = rtmsg->rtm_table; #ifdef HAVE_NETLINK_SOCKET_ATTRIBUTE_RTA_TABLE if (rta_array[RTA_TABLE] != NULL) { const uint8_t* p = static_cast( RTA_DATA(const_cast(rta_array[RTA_TABLE]))); rtmt = extract_host_int(p); } #endif if ((rtmt != RT_TABLE_UNSPEC) && (rtmt != rttable)) { err_msg += c_format("Ignoring route from table: %i\n", rtmt); return XORP_ERROR; } else { //XLOG_WARNING("Accepting route from table: %i\n", rtmt); } } IPvX nexthop_addr(family); IPvX dst_addr(family); int dst_mask_len = 0; //XLOG_INFO("rtm type: %i\n", (int)(rtmsg->rtm_type)); // // Type-specific processing // switch (rtmsg->rtm_type) { case RTN_LOCAL: // TODO: XXX: PAVPAVPAV: handle it, if needed! err_msg += "RTM type is RTN_LOCAL, ignoring.\n"; return (XORP_ERROR); // TODO: is it really an error? case RTN_BLACKHOLE: case RTN_PROHIBIT: { // // Try to map discard routes back to the first software discard // interface in the tree. If we don't have one, then ignore this route. // We have to scan all interfaces because IfTree elements // are held in a map, and we don't key on this property. // const IfTreeInterface* pi = NULL; for (IfTree::IfMap::const_iterator ii = iftree.interfaces().begin(); ii != iftree.interfaces().end(); ++ii) { if (ii->second->discard()) { pi = ii->second; break; } } if (pi == NULL) { // // XXX: Cannot map a discard route back to an FEA soft discard // interface. // err_msg += "Can't map discard route back to FEA soft discard interface.\n"; return (XORP_ERROR); } if_name = pi->ifname(); vif_name = if_name; // XXX: ifname == vifname // XXX: Do we need to change nexthop_addr? lookup_ifindex = false; break; } case RTN_UNREACHABLE: { // // Try to map unreachable routes back to the first software unreachable // interface in the tree. If we don't have one, then ignore this route. // We have to scan all interfaces because IfTree elements // are held in a map, and we don't key on this property. // const IfTreeInterface* pi = NULL; for (IfTree::IfMap::const_iterator ii = iftree.interfaces().begin(); ii != iftree.interfaces().end(); ++ii) { if (ii->second->unreachable()) { pi = ii->second; break; } } if (pi == NULL) { // // XXX: Cannot map an unreachable route back to an FEA soft // unreachable interface. // err_msg += "Can't map unreachable route back to FEA soft unreachable interface.\n"; return (XORP_ERROR); } if_name = pi->ifname(); vif_name = if_name; // XXX: ifname == vifname // XXX: Do we need to change nexthop_addr? lookup_ifindex = false; break; } case RTN_UNICAST: break; default: XLOG_ERROR("nlm_get_to_fte_cfg() failed: " "unrecognized AF_NETLINK route type: %d", rtmsg->rtm_type); err_msg += c_format("Unrecognized route type: %i\n", (int)(rtmsg->rtm_type)); return (XORP_ERROR); } // // Check the address family // if (rtmsg->rtm_family != family) { err_msg += "Invalid family.\n"; return (XORP_ERROR); } // // Get the destination // if (rta_array[RTA_DST] == NULL) { // The default entry } else { // TODO: fix this!! dst_addr.copy_in(family, (uint8_t *)RTA_DATA(const_cast(rta_array[RTA_DST]))); dst_addr = system_adjust_ipvx_recv(dst_addr); if (! dst_addr.is_unicast()) { // TODO: should we make this check? fte.zero(); err_msg += c_format("Ignoring non-unicast destination: %s\n", dst_addr.str().c_str()); return (XORP_ERROR); } } // // Get the next-hop router address // if (rta_array[RTA_GATEWAY] != NULL) { nexthop_addr.copy_in(family, (uint8_t *)RTA_DATA(const_cast(rta_array[RTA_GATEWAY]))); } // // Get the destination mask length // dst_mask_len = rtmsg->rtm_dst_len; // // Test whether we installed this route // bool xorp_route = false; if (rtmsg->rtm_protocol == RTPROT_XORP) xorp_route = true; // // Get the interface/vif name and index // if (lookup_ifindex) { // Get the interface index if (rta_array[RTA_OIF] != NULL) { const uint8_t* p = static_cast( RTA_DATA(const_cast(rta_array[RTA_OIF]))); if_index = extract_host_int(p); } else { XLOG_ERROR("nlm_get_to_fte_cfg() failed: no interface found"); err_msg += "Could not find interface (no RTA_OIF)\n"; return (XORP_ERROR); } // Get the interface/vif name const IfTreeVif* vifp = iftree.find_vif(if_index); if (vifp != NULL) { if_name = vifp->ifname(); vif_name = vifp->vifname(); } // Test whether the interface/vif name was found if (if_name.empty() || vif_name.empty()) { if (is_deleted) { // // XXX: If the route is deleted and we cannot find // the corresponding interface/vif, this could be because // an interface/vif is deleted from the kernel, and // the kernel might send first the upcall message // that deletes the interface from user space. // Hence the upcall message that followed to delete the // corresponding route for the connected subnet // won't find the interface/vif. // Propagate the route deletion with empty interface // and vif name. // } else { // // XXX: A route was added, but we cannot find the corresponding // interface/vif. // This is probably because the interface in question is not in our // local config. // Another possibility: This might happen because of a race // condition. E.g., an interface was added and then // immediately deleted, but the processing for the addition of // the corresponding connected route was delayed. // Note that the misorder in the processing might happen // because the interface and routing control messages are // received on different control sockets. // For the time being make it a fatal error until there is // enough evidence and the issue is understood. // //IPvXNet dst_subnet(dst_addr, dst_mask_len); err_msg += c_format("WARNING: Decoding for route %s next hop %s failed: " "could not find interface and vif for index %d", dst_addr.str().c_str(), nexthop_addr.str().c_str(), if_index); return XORP_ERROR; } } } // // Get the route metric // uint32_t route_metric = 0xffff; if (rta_array[RTA_PRIORITY] != NULL) { const uint8_t* p = static_cast( RTA_DATA(const_cast(rta_array[RTA_PRIORITY]))); int int_priority = extract_host_int(p); route_metric = int_priority; } // // TODO: define default admin distance instead of 0xffff // try { fte = FteX(IPvXNet(dst_addr, dst_mask_len), nexthop_addr, if_name, vif_name, route_metric, 0xffff, xorp_route); } catch (XorpException& xe) { err_msg += "exception in nlm_get_to_fte_cfg: "; err_msg += xe.str(); err_msg += "\n"; XLOG_ERROR("exception in nlm_get_to_fte_cfg: %s", xe.str().c_str()); return XORP_ERROR; } if (is_deleted) fte.mark_deleted(); //XLOG_INFO("get_fte_cfg, route: %s", fte.str().c_str()); return (XORP_OK); } /** * Check that a previous netlink request has succeeded. * * @param ns_reader the NetlinkSocketReader to use for reading data. * @param ns the NetlinkSocket to use for reading data. * @param seqno the sequence nomer of the netlink request to check for. * @param last_errno the last error number (if error). * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int NlmUtils::check_netlink_request(NetlinkSocketReader& ns_reader, NetlinkSocket& ns, uint32_t seqno, int& last_errno, string& error_msg) { size_t buffer_bytes; const struct nlmsghdr* nlh; last_errno = 0; // XXX: reset the value // // Force to receive data from the kernel, and then parse it // if (ns_reader.receive_data(ns, seqno, error_msg) != XORP_OK) return (XORP_ERROR); const vector& buffer = ns_reader.buffer(); buffer_bytes = buffer.size(); AlignData align_data(buffer); for (nlh = align_data.payload(); NLMSG_OK(nlh, buffer_bytes); nlh = NLMSG_NEXT(const_cast(nlh), buffer_bytes)) { void* nlmsg_data = NLMSG_DATA(const_cast(nlh)); switch (nlh->nlmsg_type) { case NLMSG_ERROR: { const struct nlmsgerr* err; err = reinterpret_cast(nlmsg_data); if (nlh->nlmsg_len < NLMSG_LENGTH(sizeof(*err))) { error_msg += "AF_NETLINK nlmsgerr length error\n"; return (XORP_ERROR); } if (err->error == 0) return (XORP_OK); // No error errno = -err->error; last_errno = errno; error_msg += c_format("AF_NETLINK NLMSG_ERROR message: %s\n", strerror(errno)); return (XORP_ERROR); } break; case NLMSG_DONE: { // End-of-message, and no ACK was received: error. error_msg += "No ACK was received\n"; return (XORP_ERROR); } break; case NLMSG_NOOP: break; default: debug_msg("Unhandled type %s(%d) (%u bytes)\n", nlm_msg_type(nlh->nlmsg_type).c_str(), nlh->nlmsg_type, XORP_UINT_CAST(nlh->nlmsg_len)); break; } } error_msg += "No ACK was received\n"; return (XORP_ERROR); // No ACK was received: error. } int NlmUtils::nlm_decode_ipvx_address(int family, const struct rtattr* rtattr, IPvX& ipvx_addr, bool& is_set, string& error_msg) { is_set = false; if (rtattr == NULL) { error_msg = c_format("Missing address attribute to decode"); return (XORP_ERROR); } // // Get the attribute information // size_t addr_size = RTA_PAYLOAD(rtattr); const uint8_t* addr_data = reinterpret_cast(RTA_DATA(const_cast(rtattr))); // // Test the address length // if (addr_size != IPvX::addr_bytelen(family)) { error_msg = c_format("Invalid address size payload: %u instead of %u", XORP_UINT_CAST(addr_size), XORP_UINT_CAST(IPvX::addr_bytelen(family))); return (XORP_ERROR); } // // Decode the address // ipvx_addr.copy_in(family, addr_data); is_set = true; return (XORP_OK); } int NlmUtils::nlm_decode_ipvx_interface_address(const struct ifinfomsg* ifinfomsg, const struct rtattr* rtattr, IPvX& ipvx_addr, bool& is_set, string& error_msg) { int family = AF_UNSPEC; is_set = false; XLOG_ASSERT(ifinfomsg != NULL); if (rtattr == NULL) { error_msg = c_format("Missing address attribute to decode"); return (XORP_ERROR); } // Decode the attribute type switch (ifinfomsg->ifi_type) { case ARPHRD_ETHER: { // MAC address: processed earlier when decoding the interface return (XORP_OK); } case ARPHRD_TUNNEL: // FALLTHROUGH case ARPHRD_SIT: // FALLTHROUGH case ARPHRD_IPGRE: { // IPv4 address family = IPv4::af(); break; } #ifdef HAVE_IPV6 case ARPHRD_TUNNEL6: { // IPv6 address family = IPv6::af(); break; } #endif // HAVE_IPV6 default: // Unknown address type: ignore return (XORP_OK); } XLOG_ASSERT(family != AF_UNSPEC); return (nlm_decode_ipvx_address(family, rtattr, ipvx_addr, is_set, error_msg)); } void NlmUtils::nlm_cond_newlink_to_fea_cfg(const IfTree& user_cfg, IfTree& iftree, const struct ifinfomsg* ifinfomsg, int rta_len, bool& modified) { const struct rtattr *rtattr; const struct rtattr *rta_array[IFLA_MAX + 1]; uint32_t if_index = 0; string if_name; bool is_newlink = false; // True if really a new link // The attributes memset(rta_array, 0, sizeof(rta_array)); rtattr = IFLA_RTA(ifinfomsg); get_rtattr(rtattr, rta_len, rta_array, sizeof(rta_array) / sizeof(rta_array[0])); // // Get the interface name // if (rta_array[IFLA_IFNAME] == NULL) { // // XXX: The kernel did not provide the interface name. // It could be because this is a wireless event carried to user // space. Such events are encapsulated in the IFLA_WIRELESS field of // a RTM_NEWLINK message. // Older kernels (2.6.18, for instance), don't send the name evidently. // Attempt to determine it from the ifindex. static bool warn_once = true; if (warn_once) { XLOG_WARNING("Could not find interface name for interface index %d in netlink msg.\n Attempting" " work-around by using ifindex to find the name.\n This warning will be printed only" " once.\n", ifinfomsg->ifi_index); warn_once = false; } #ifdef HAVE_IF_INDEXTONAME char buf[IF_NAMESIZE + 1]; char* ifn = if_indextoname(ifinfomsg->ifi_index, buf); if (ifn) { if_name = ifn; } else { XLOG_ERROR("Cannot find ifname for index: %i, unable to process netlink NEWLINK message.\n", ifinfomsg->ifi_index); return; } #else XLOG_ERROR("ERROR: IFLA_IFNAME isn't in netlink msg, and don't have if_indextoname on this\n" " platform..we cannot properly read network device events.\n This is likely a fatal" " error.\n"); return; #endif } else { if_name = (char*)(RTA_DATA(rta_array[IFLA_IFNAME])); } //XLOG_WARNING("newlink, interface: %s tree: %s\n", if_name.c_str(), iftree.getName().c_str()); debug_msg("newlink, interface: %s tree: %s\n", if_name.c_str(), iftree.getName().c_str()); if (! user_cfg.find_interface(if_name)) { XLOG_WARNING("Ignoring interface: %s as it is not found in the local config.\n", if_name.c_str()); return; } modified = true; // this is just for optimization..so if we somehow don't modify things, it's not a big deal. // // Get the interface index // if_index = ifinfomsg->ifi_index; if (if_index == 0) { XLOG_FATAL("Could not find physical interface index " "for interface %s", if_name.c_str()); } debug_msg("interface index: %d\n", if_index); // // Add the interface (if a new one) // IfTreeInterface* ifp = iftree.find_interface(if_name); if (ifp == NULL) { iftree.add_interface(if_name); is_newlink = true; ifp = iftree.find_interface(if_name); XLOG_ASSERT(ifp != NULL); } // // Set the physical interface index for the interface // if (is_newlink || (if_index != ifp->pif_index())) ifp->set_pif_index(if_index); // // Get the MAC address // if (rta_array[IFLA_ADDRESS] != NULL) { size_t addr_size = RTA_PAYLOAD(rta_array[IFLA_ADDRESS]); const uint8_t* addr_data = reinterpret_cast(RTA_DATA(const_cast(rta_array[IFLA_ADDRESS]))); switch (ifinfomsg->ifi_type) { case ARPHRD_ETHER: { // MAC address struct ether_addr ea; if (addr_size != sizeof(ea)) break; memcpy(&ea, addr_data, sizeof(ea)); Mac mac(ea); if (is_newlink || (mac != ifp->mac())) ifp->set_mac(mac); break; } default: // Either unknown type or type that will be processed later break; } } debug_msg("MAC address: %s\n", ifp->mac().str().c_str()); // // Get the MTU // if (rta_array[IFLA_MTU] != NULL) { unsigned int mtu; XLOG_ASSERT(RTA_PAYLOAD(rta_array[IFLA_MTU]) == sizeof(mtu)); mtu = *reinterpret_cast(RTA_DATA(const_cast(rta_array[IFLA_MTU]))); if (is_newlink || (mtu != ifp->mtu())) ifp->set_mtu(mtu); } else { static bool do_once = true; if (do_once) { do_once = false; XLOG_WARNING("WARNING: MTU was not in rta_array, attempting to get it via" "/sys/class/net/%s/mtu instead. Will not print this message again.\n", if_name.c_str()); } // That sucks...try another way. unsigned int mtu = 1500; //default to something probably right. string ifn("/sys/class/net/"); ifn.append(if_name); ifn.append("/mtu"); ifstream ifs(ifn.c_str()); if (ifs) { ifs >> mtu; } else { XLOG_WARNING("WARNING: %s: MTU not in rta_array, and cannot open %s" " Defaulting to 1500\n", if_name.c_str(), ifn.c_str()); } if (is_newlink || (mtu != ifp->mtu())) ifp->set_mtu(mtu); } debug_msg("MTU: %u\n", ifp->mtu()); // // Get the flags // unsigned int flags = ifinfomsg->ifi_flags; if (is_newlink || (flags != ifp->interface_flags())) { ifp->set_interface_flags(flags); ifp->set_enabled(flags & IFF_UP); } debug_msg("enabled: %s\n", bool_c_str(ifp->enabled())); // // Get the link status and baudrate // do { bool no_carrier = false; uint64_t baudrate = 0; string error_msg; if (ifconfig_media_get_link_status(if_name, no_carrier, baudrate, error_msg) != XORP_OK) { // XXX: Use the flags if ((flags & IFF_UP) && !(flags & IFF_RUNNING)) no_carrier = true; else no_carrier = false; } if (is_newlink || (no_carrier != ifp->no_carrier())) ifp->set_no_carrier(no_carrier); if (is_newlink || (baudrate != ifp->baudrate())) ifp->set_baudrate(baudrate); break; } while (false); debug_msg("no_carrier: %s\n", bool_c_str(ifp->no_carrier())); debug_msg("baudrate: %u\n", XORP_UINT_CAST(ifp->baudrate())); // XXX: vifname == ifname on this platform if (is_newlink) ifp->add_vif(if_name); IfTreeVif* vifp = ifp->find_vif(if_name); XLOG_ASSERT(vifp != NULL); // // Set the physical interface index for the vif // if (is_newlink || (if_index != vifp->pif_index())) vifp->set_pif_index(if_index); // // Set the vif flags // if (is_newlink || (flags != vifp->vif_flags())) { vifp->set_vif_flags(flags); vifp->set_enabled(ifp->enabled() && (flags & IFF_UP)); vifp->set_broadcast(flags & IFF_BROADCAST); vifp->set_loopback(flags & IFF_LOOPBACK); vifp->set_point_to_point(flags & IFF_POINTOPOINT); vifp->set_multicast(flags & IFF_MULTICAST); // Propagate the flags to the existing addresses vifp->propagate_flags_to_addresses(); } debug_msg("vif enabled: %s\n", bool_c_str(vifp->enabled())); debug_msg("vif broadcast: %s\n", bool_c_str(vifp->broadcast())); debug_msg("vif loopback: %s\n", bool_c_str(vifp->loopback())); debug_msg("vif point_to_point: %s\n", bool_c_str(vifp->point_to_point())); debug_msg("vif multicast: %s\n", bool_c_str(vifp->multicast())); return; #if 0 // This logic below adds an IP address for GRE tunnels, and perhaps // similar tunnels. This causes trouble because that IP information // has nothing to do with actually using this interface. // // Add any interface-specific addresses // IPvX lcl_addr(AF_INET); IPvX peer_addr(AF_INET); bool has_lcl_addr = false; bool has_peer_addr = false; string error_msg; // Get the local address if (rta_array[IFLA_ADDRESS] != NULL) { if (nlm_decode_ipvx_interface_address(ifinfomsg, rta_array[IFLA_ADDRESS], lcl_addr, has_lcl_addr, error_msg) != XORP_OK) { XLOG_WARNING("Error decoding address for interface %s vif %s: %s", vifp->ifname().c_str(), vifp->vifname().c_str(), error_msg.c_str()); } } if (! has_lcl_addr) return; // XXX: nothing more to do debug_msg("IP address before adjust: %s\n", lcl_addr.str().c_str()); lcl_addr = system_adjust_ipvx_recv(lcl_addr); debug_msg("IP address: %s\n", lcl_addr.str().c_str()); // XXX: No info about the masklen: assume it is the address bitlen size_t mask_len = IPvX::addr_bitlen(lcl_addr.af()); // Get the broadcast/peer address if (rta_array[IFLA_BROADCAST] != NULL) { if (nlm_decode_ipvx_interface_address(ifinfomsg, rta_array[IFLA_BROADCAST], peer_addr, has_peer_addr, error_msg) != XORP_OK) { XLOG_WARNING("Error decoding broadcast/peer address for " "interface %s vif %s: %s", vifp->ifname().c_str(), vifp->vifname().c_str(), error_msg.c_str()); } debug_msg("IP Broadcast/peer address: %s\n", peer_addr.str().c_str()); } if (has_peer_addr) XLOG_ASSERT(lcl_addr.af() == peer_addr.af()); // Add the address switch (lcl_addr.af()) { case AF_INET: { vifp->add_addr(lcl_addr.get_ipv4()); IfTreeAddr4* ap = vifp->find_addr(lcl_addr.get_ipv4()); XLOG_ASSERT(ap != NULL); ap->set_enabled(vifp->enabled()); ap->set_broadcast(vifp->broadcast() && has_peer_addr); ap->set_loopback(vifp->loopback()); ap->set_point_to_point(vifp->point_to_point() && has_peer_addr); ap->set_multicast(vifp->multicast()); if (has_peer_addr) { ap->set_prefix_len(mask_len); if (ap->broadcast()) ap->set_bcast(peer_addr.get_ipv4()); if (ap->point_to_point()) ap->set_endpoint(peer_addr.get_ipv4()); } break; } #ifdef HAVE_IPV6 case AF_INET6: { vifp->add_addr(lcl_addr.get_ipv6()); IfTreeAddr6* ap = vifp->find_addr(lcl_addr.get_ipv6()); XLOG_ASSERT(ap != NULL); ap->set_enabled(vifp->enabled()); ap->set_loopback(vifp->loopback()); ap->set_point_to_point(vifp->point_to_point()); ap->set_multicast(vifp->multicast()); if (has_peer_addr) { ap->set_prefix_len(mask_len); if (ap->point_to_point()) ap->set_endpoint(peer_addr.get_ipv6()); } break; } #endif // HAVE_IPV6 default: break; } #endif } void NlmUtils::nlm_dellink_to_fea_cfg(IfTree& iftree, const struct ifinfomsg* ifinfomsg, int rta_len, bool& modified) { const struct rtattr *rtattr; const struct rtattr *rta_array[IFLA_MAX + 1]; uint32_t if_index = 0; string if_name; // The attributes memset(rta_array, 0, sizeof(rta_array)); rtattr = IFLA_RTA(ifinfomsg); get_rtattr(rtattr, rta_len, rta_array, sizeof(rta_array) / sizeof(rta_array[0])); // // Get the interface name // if (rta_array[IFLA_IFNAME] == NULL) { #ifdef HAVE_IF_INDEXTONAME char buf[IF_NAMESIZE + 1]; char* ifn = if_indextoname(ifinfomsg->ifi_index, buf); if (ifn) { if_name = ifn; } else { XLOG_ERROR("Cannot find ifname for index: %i, unable to process netlink DELLINK message.\n", ifinfomsg->ifi_index); return; } #else XLOG_ERROR("ERROR: IFLA_IFNAME isn't in netlink msg, and don't have if_indextoname on this\n" " platform..we cannot properly read network device events. This is likely a fatal" " error.\n"); return; #endif } else { if_name = (char*)(RTA_DATA(rta_array[IFLA_IFNAME])); } XLOG_WARNING("dellink, interface: %s tree: %s\n", if_name.c_str(), iftree.getName().c_str()); // // Get the interface index // if_index = ifinfomsg->ifi_index; if (if_index == 0) { XLOG_FATAL("Could not find physical interface index " "for interface %s", if_name.c_str()); } debug_msg("Deleting interface index: %u\n", if_index); // // Delete the interface and vif // IfTreeInterface* ifp = iftree.find_interface(if_index); if (ifp != NULL) { if (!ifp->is_marked(IfTree::DELETED)) { iftree.markIfaceDeleted(ifp); modified = true; } } IfTreeVif* vifp = iftree.find_vif(if_index); if (vifp != NULL) { if (!vifp->is_marked(IfTree::DELETED)) { iftree.markVifDeleted(vifp); modified = true; } } } void NlmUtils::nlm_cond_newdeladdr_to_fea_cfg(const IfTree& user_config, IfTree& iftree, const struct ifaddrmsg* ifaddrmsg, int rta_len, bool is_deleted, bool& modified) { const struct rtattr *rtattr; const struct rtattr *rta_array[IFA_MAX + 1]; uint32_t if_index = 0; int family = ifaddrmsg->ifa_family; // // Test the family // switch (family) { case AF_INET: break; #ifdef HAVE_IPV6 case AF_INET6: break; #endif // HAVE_IPV6 default: debug_msg("Ignoring address of family %d\n", family); return; } // The attributes memset(rta_array, 0, sizeof(rta_array)); rtattr = IFA_RTA(ifaddrmsg); get_rtattr(rtattr, rta_len, rta_array, sizeof(rta_array) / sizeof(rta_array[0])); // // Get the interface index // if_index = ifaddrmsg->ifa_index; if (if_index == 0) { XLOG_FATAL("Could not add or delete address for interface " "with unknown index"); } // // Locate the vif to pin data on // IfTreeVif* vifp = iftree.find_vif(if_index); if (vifp == NULL) { if (is_deleted) { // // XXX: In case of Linux kernel we may receive first // a message to delete the interface and then a message to // delete an address on that interface. // However, the first message would remove all state about // that interface (including its addresses). // Hence, we silently ignore messages for deleting addresses // if the interface is not found. // return; } else { const IfTreeVif* vifpc = user_config.find_vif(if_index); if (vifpc) { XLOG_FATAL("Could not find vif with index %u in IfTree", if_index); } //else, not in local config, so ignore it return; } } debug_msg("Address event on interface %s vif %s with interface index %u\n", vifp->ifname().c_str(), vifp->vifname().c_str(), vifp->pif_index()); modified = true; // this is just for optimization..so if we somehow don't modify things, it's not a big deal. // // Get the IP address, netmask, broadcast address, P2P destination // // The default values IPvX lcl_addr = IPvX::ZERO(family); IPvX subnet_mask = IPvX::ZERO(family); IPvX broadcast_addr = IPvX::ZERO(family); IPvX peer_addr = IPvX::ZERO(family); bool has_lcl_addr = false; bool has_broadcast_addr = false; bool has_peer_addr = false; bool is_ifa_address_reassigned = false; string error_msg; // // XXX: re-assign IFA_ADDRESS to IFA_LOCAL (and vice-versa). // This tweak is needed according to the iproute2 source code. // if (rta_array[IFA_LOCAL] == NULL) { rta_array[IFA_LOCAL] = rta_array[IFA_ADDRESS]; } if (rta_array[IFA_ADDRESS] == NULL) { rta_array[IFA_ADDRESS] = rta_array[IFA_LOCAL]; is_ifa_address_reassigned = true; } // Get the IP address if (rta_array[IFA_LOCAL] != NULL) { if (nlm_decode_ipvx_address(family, rta_array[IFA_LOCAL], lcl_addr, has_lcl_addr, error_msg) != XORP_OK) { XLOG_FATAL("Error decoding address for interface %s vif %s: %s", vifp->ifname().c_str(), vifp->vifname().c_str(), error_msg.c_str()); } } if (! has_lcl_addr) { XLOG_FATAL("Missing local address for interface %s vif %s", vifp->ifname().c_str(), vifp->vifname().c_str()); } debug_msg("IP address before adjust: %s\n", lcl_addr.str().c_str()); lcl_addr = system_adjust_ipvx_recv(lcl_addr); debug_msg("IP address: %s\n", lcl_addr.str().c_str()); // Get the netmask subnet_mask = IPvX::make_prefix(family, ifaddrmsg->ifa_prefixlen); debug_msg("IP netmask: %s\n", subnet_mask.str().c_str()); // Get the broadcast address if (vifp->broadcast()) { switch (family) { case AF_INET: if (rta_array[IFA_BROADCAST] != NULL) { if (nlm_decode_ipvx_address(family, rta_array[IFA_BROADCAST], broadcast_addr, has_broadcast_addr, error_msg) != XORP_OK) { XLOG_FATAL("Error decoding broadcast address for " "interface %s vif %s: %s", vifp->ifname().c_str(), vifp->vifname().c_str(), error_msg.c_str()); } } break; #ifdef HAVE_IPV6 case AF_INET6: break; // IPv6 doesn't have the idea of broadcast #endif // HAVE_IPV6 default: XLOG_UNREACHABLE(); break; } debug_msg("Broadcast address: %s\n", broadcast_addr.str().c_str()); } // Get the p2p address if (vifp->point_to_point()) { if ((rta_array[IFA_ADDRESS] != NULL) && !is_ifa_address_reassigned) { if (rta_array[IFA_ADDRESS] != NULL) { if (nlm_decode_ipvx_address(family, rta_array[IFA_ADDRESS], peer_addr, has_peer_addr, error_msg) != XORP_OK) { XLOG_FATAL("Error decoding peer address for " "interface %s vif %s: %s", vifp->ifname().c_str(), vifp->vifname().c_str(), error_msg.c_str()); } } } debug_msg("Peer address: %s\n", peer_addr.str().c_str()); } debug_msg("\n"); // put an empty line between interfaces // Add or delete the address switch (family) { case AF_INET: { vifp->add_addr(lcl_addr.get_ipv4()); IfTreeAddr4* ap = vifp->find_addr(lcl_addr.get_ipv4()); XLOG_ASSERT(ap != NULL); ap->set_enabled(vifp->enabled()); ap->set_broadcast(vifp->broadcast() && has_broadcast_addr); ap->set_loopback(vifp->loopback()); ap->set_point_to_point(vifp->point_to_point() && has_peer_addr); ap->set_multicast(vifp->multicast()); ap->set_prefix_len(subnet_mask.mask_len()); if (ap->broadcast()) ap->set_bcast(broadcast_addr.get_ipv4()); if (ap->point_to_point()) ap->set_endpoint(peer_addr.get_ipv4()); // Mark as deleted if necessary if (is_deleted) ap->mark(IfTreeItem::DELETED); break; } #ifdef HAVE_IPV6 case AF_INET6: { vifp->add_addr(lcl_addr.get_ipv6()); IfTreeAddr6* ap = vifp->find_addr(lcl_addr.get_ipv6()); XLOG_ASSERT(ap != NULL); ap->set_enabled(vifp->enabled()); ap->set_loopback(vifp->loopback()); ap->set_point_to_point(vifp->point_to_point()); ap->set_multicast(vifp->multicast()); ap->set_prefix_len(subnet_mask.mask_len()); if (ap->point_to_point()) ap->set_endpoint(peer_addr.get_ipv6()); // Mark as deleted if necessary if (is_deleted) ap->mark(IfTreeItem::DELETED); break; } #endif // HAVE_IPV6 default: XLOG_UNREACHABLE(); break; } } #endif // HAVE_NETLINK_SOCKETS xorp/fea/data_plane/control_socket/netlink_socket.cc0000664000076400007640000003546711540225522023073 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include #ifdef HAVE_NETLINK_SOCKETS #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_SYS_TIME_H #include #endif #ifdef HAVE_SYS_SOCKET_H #include #endif #ifdef HAVE_NET_IF_H #include #endif #ifdef HAVE_NET_ROUTE_H #include #endif #include #ifdef HAVE_LINUX_TYPES_H #include #endif #ifdef HAVE_LINUX_RTNETLINK_H #include #endif #ifdef HAVE_PCAP_BPF_H #include #endif // standard headers might not be up to date with the latest kernels. #ifndef SKF_AD_OFF #define SKF_AD_OFF (-0x1000) #endif #ifndef SKF_AD_NLATTR #define SKF_AD_NLATTR 12 #endif #include "libcomm/comm_api.h" #include "netlink_socket.hh" #include "netlink_socket_utilities.hh" uint16_t NetlinkSocket::_instance_cnt = 0; // // Netlink Sockets (see netlink(7)) communication with the kernel // NetlinkSocket::NetlinkSocket(EventLoop& eventloop, uint32_t table_id) : _eventloop(eventloop), _fd(-1), _seqno(0), _instance_no(_instance_cnt++), _nl_groups(0), // XXX: no netlink multicast groups _table_id(table_id), _is_multipart_message_read(false), _nlm_count(0) { } NetlinkSocket::~NetlinkSocket() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the netlink socket: %s", error_msg.c_str()); } XLOG_ASSERT(_ol.empty()); } int NetlinkSocket::force_recvmsg(bool only_kernel_messages, string& err_msg) { return force_recvmsg_flgs(MSG_DONTWAIT, only_kernel_messages, err_msg); } int NetlinkSocket::bind_table_id() { #if ! (defined(HAVE_PCAP_BPF_H) && defined(RTA_TABLE)) return XORP_OK; // No big problem, just slightly dif #else if (_table_id) { // Use socket filter. Shouldn't require kernel hackings if it's a recent-ish kernel (2.6.30+ I think). struct bpf_program bpf; bpf.bf_insns = NULL; static struct bpf_insn instructions[] = { { /* A = offset of first attribute */ BPF_LD | BPF_IMM, 0, 0, sizeof(struct nlmsghdr) + NLMSG_ALIGN(sizeof(struct rtmsg)) }, { /* X = RTA_TABLE */ BPF_LDX | BPF_IMM, 0, 0, RTA_TABLE }, { /* A = netlink attribute (X) offset */ BPF_LD | BPF_W | BPF_ABS, 0, 0, SKF_AD_OFF + SKF_AD_NLATTR }, { /* If table offset was not found, then pass it through. */ BPF_JMP | BPF_JEQ | BPF_K, 3, 0, 0 }, { /* X = A (netlink attribute offset) */ BPF_MISC | BPF_TAX, 0, 0, 0 }, { /* A = skb->data[X + k] */ BPF_LD | BPF_W | BPF_IND, 0, 0, sizeof(struct nlattr) }, { /* Exit if wrong routing table */ BPF_JMP | BPF_JEQ | BPF_K, 0, 1, /**/ _table_id }, { /* Packet may pass */ BPF_RET | BPF_K, 0, 0, ~0 }, /* : */ { /* Packet may not pass */ BPF_RET | BPF_K, 0, 0, 0 } }; /* bpf instructions */ bpf.bf_insns = instructions; bpf.bf_len = 9; if (setsockopt(_fd, SOL_SOCKET, SO_ATTACH_FILTER, &bpf, sizeof(bpf)) < 0) { XLOG_WARNING("Failed to set filter on netlink socket, error: %s\n" "The program will run fine, but may be slightly less efficient if\n" "multiple Xorps are run on the same system using different routing tables.\n", strerror(errno)); } else { static bool do_once = true; if (do_once) { XLOG_WARNING("Successfully attached Netlink socket filter for table id: %u on fd: %i", _table_id, _fd); do_once = false; } } } else { if (setsockopt(_fd, SOL_SOCKET, SO_DETACH_FILTER, 0, 0) < 0) { // Not a real problem..fails if we don't have one already attached, for instance. //XLOG_WARNING("Failed to detach filter on netlink socket, error: %s", strerror(errno)); } } return XORP_OK; #endif } /** Routing table ID that we are interested in might have changed. */ int NetlinkSocket::notify_table_id_change(uint32_t new_tbl) { if (new_tbl != _table_id) { _table_id = new_tbl; return bind_table_id(); } return XORP_OK; } int NetlinkSocket::start(string& error_msg) { struct sockaddr_nl snl; socklen_t snl_len = sizeof(snl); if (_fd >= 0) return (XORP_OK); // // Open the socket // // XXX: Older versions of the netlink(7) manual page are incorrect // that for IPv6 we need NETLINK_ROUTE6. // The truth is that for both IPv4 and IPv6 it has to be NETLINK_ROUTE. _fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (_fd < 0) { error_msg = c_format("Could not open netlink socket: %s", strerror(errno)); return (XORP_ERROR); } // // Increase the receiving buffer size of the socket to avoid // loss of data from the kernel. // comm_sock_set_rcvbuf(_fd, SO_RCV_BUF_SIZE_MAX, SO_RCV_BUF_SIZE_MIN); // TODO: do we want to make the socket non-blocking? // // Bind the socket // memset(&snl, 0, snl_len); snl.nl_family = AF_NETLINK; snl.nl_pid = 0; // Let the kernel assign the pid to the socket snl.nl_groups = _nl_groups; if (bind(_fd, reinterpret_cast(&snl), snl_len) < 0) { error_msg = c_format("bind(AF_NETLINK) failed: %s", strerror(errno)); close(_fd); _fd = -1; return (XORP_ERROR); } // // Double-check the result socket is AF_NETLINK // snl_len = sizeof(snl); if (getsockname(_fd, reinterpret_cast(&snl), &snl_len) < 0) { error_msg = c_format("getsockname(AF_NETLINK) failed: %s", strerror(errno)); close(_fd); _fd = -1; return (XORP_ERROR); } if (snl_len != sizeof(snl)) { error_msg = c_format("Wrong address length of AF_NETLINK socket: " "%u instead of %u", XORP_UINT_CAST(snl_len), XORP_UINT_CAST(sizeof(snl))); close(_fd); _fd = -1; return (XORP_ERROR); } if (snl.nl_family != AF_NETLINK) { error_msg = c_format("Wrong address family of AF_NETLINK socket: " "%d instead of %d", snl.nl_family, AF_NETLINK); close(_fd); _fd = -1; return (XORP_ERROR); } // Bind to table-id bind_table_id(); // // Store the pid of the socket for checking the unicast destination of // the netlink(7) messages. // _nl_pid = snl.nl_pid; // // Add the socket to the event loop // if (_eventloop.add_ioevent_cb(_fd, IOT_READ, callback(this, &NetlinkSocket::io_event)) == false) { error_msg = c_format("Failed to add netlink socket to EventLoop"); close(_fd); _fd = -1; return (XORP_ERROR); } return (XORP_OK); } int NetlinkSocket::stop(string& error_msg) { UNUSED(error_msg); if (_fd >= 0) { _eventloop.remove_ioevent_cb(_fd); close(_fd); _fd = -1; } return (XORP_OK); } ssize_t NetlinkSocket::write(const void* data, size_t nbytes) { _seqno++; return ::write(_fd, data, nbytes); } ssize_t NetlinkSocket::sendto(const void* data, size_t nbytes, int flags, const struct sockaddr* to, socklen_t tolen) { _seqno++; return ::sendto(_fd, data, nbytes, flags, to, tolen); } int NetlinkSocket::force_recvmsg_flgs(int flags, bool only_kernel_messages, string& error_msg) { vector message; vector buffer(NETLINK_SOCKET_BYTES); size_t off = 0; size_t last_mh_off = 0; struct iovec iov; struct msghdr msg; struct sockaddr_nl snl; // Set the socket memset(&snl, 0, sizeof(snl)); snl.nl_family = AF_NETLINK; // Init the recvmsg() arguments iov.iov_base = &buffer[0]; iov.iov_len = buffer.size(); msg.msg_name = &snl; msg.msg_namelen = sizeof(snl); msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = NULL; msg.msg_controllen = 0; msg.msg_flags = 0; for ( ; ; ) { ssize_t got; // Find how much data is queued in the first message do { got = recv(_fd, &buffer[0], buffer.size(), MSG_DONTWAIT | MSG_PEEK); if ((got < 0) && (errno == EINTR)) continue; // XXX: the receive was interrupted by a signal if ((got < 0) || (got < (ssize_t)buffer.size())) break; // The buffer is big enough buffer.resize(buffer.size() + NETLINK_SOCKET_BYTES); } while (true); // Re-init the iov argument iov.iov_base = &buffer[0]; iov.iov_len = buffer.size(); got = recvmsg(_fd, &msg, flags); if (got < 0) { // Nothing to read after all, msg was probably filtered. if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) return XORP_ERROR; if (errno == EINTR) continue; error_msg = c_format("Netlink socket recvmsg error: %s", strerror(errno)); return (XORP_ERROR); } else { _nlm_count++; } // // If necessary, ignore messages that were not originated by the kernel // if (only_kernel_messages && (snl.nl_pid != 0)) continue; if (msg.msg_namelen != sizeof(snl)) { error_msg = c_format("Netlink socket recvmsg error: " "sender address length %d instead of %u", XORP_INT_CAST(msg.msg_namelen), XORP_UINT_CAST(sizeof(snl))); return (XORP_ERROR); } message.resize(message.size() + got); memcpy(&message[off], &buffer[0], got); off += got; if ((off - last_mh_off) < (ssize_t)sizeof(struct nlmsghdr)) { error_msg = c_format("Netlink socket recvmsg failed: " "message truncated: " "received %d bytes instead of (at least) %u " "bytes", XORP_INT_CAST(got), XORP_UINT_CAST(sizeof(struct nlmsghdr))); return (XORP_ERROR); } // // If this is a multipart message, it must be terminated by NLMSG_DONE // bool is_end_of_message = true; size_t new_size = off - last_mh_off; AlignData align_data(message); const struct nlmsghdr* mh; for (mh = align_data.payload_by_offset(last_mh_off); NLMSG_OK(mh, new_size); mh = NLMSG_NEXT(mh, new_size)) { XLOG_ASSERT(mh->nlmsg_len <= buffer.size()); if ((mh->nlmsg_flags & NLM_F_MULTI) || _is_multipart_message_read) { is_end_of_message = false; if (mh->nlmsg_type == NLMSG_DONE) is_end_of_message = true; } } last_mh_off = (size_t)(mh) - (size_t)(&message[0]); if (is_end_of_message) break; } XLOG_ASSERT(last_mh_off == message.size()); //XLOG_WARNING("Got a netlink message: %s nlm_count: %u", // NlmUtils::nlm_print_msg(message).c_str(), _nlm_count); // // Notify observers // for (ObserverList::iterator i = _ol.begin(); i != _ol.end(); i++) { (*i)->netlink_socket_data(message); } return (XORP_OK); } void NetlinkSocket::io_event(XorpFd fd, IoEventType type) { string error_msg; XLOG_ASSERT(fd == _fd); XLOG_ASSERT(type == IOT_READ); errno = 0; if (force_recvmsg(true, error_msg) != XORP_OK) { if (!(errno == EWOULDBLOCK || errno == EAGAIN)) { XLOG_ERROR("Error force_recvmsg() from netlink socket: %s", error_msg.c_str()); } } } // // Observe netlink sockets activity // class NetlinkSocketPlumber { public: typedef NetlinkSocket::ObserverList ObserverList; static void plumb(NetlinkSocket& r, NetlinkSocketObserver* o) { ObserverList& ol = r._ol; ObserverList::iterator i = find(ol.begin(), ol.end(), o); debug_msg("Plumbing NetlinkSocketObserver %p to NetlinkSocket%p\n", o, &r); XLOG_ASSERT(i == ol.end()); ol.push_back(o); } static void unplumb(NetlinkSocket& r, NetlinkSocketObserver* o) { ObserverList& ol = r._ol; debug_msg("Unplumbing NetlinkSocketObserver %p from " "NetlinkSocket %p\n", o, &r); ObserverList::iterator i = find(ol.begin(), ol.end(), o); XLOG_ASSERT(i != ol.end()); ol.erase(i); } }; NetlinkSocketObserver::NetlinkSocketObserver(NetlinkSocket& ns) : _ns(ns) { NetlinkSocketPlumber::plumb(ns, this); } NetlinkSocketObserver::~NetlinkSocketObserver() { NetlinkSocketPlumber::unplumb(_ns, this); } NetlinkSocket& NetlinkSocketObserver::netlink_socket() { return _ns; } NetlinkSocketReader::NetlinkSocketReader(NetlinkSocket& ns) : NetlinkSocketObserver(ns), _ns(ns), _cache_valid(false), _cache_seqno(0) { } NetlinkSocketReader::~NetlinkSocketReader() { } /** * Force the reader to receive data from the specified netlink socket. * * @param ns the netlink socket to receive the data from. * @param seqno the sequence number of the data to receive. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int NetlinkSocketReader::receive_data(NetlinkSocket& ns, uint32_t seqno, string& error_msg) { _cache_seqno = seqno; _cache_valid = false; errno = 0; while (_cache_valid == false) { if (ns.force_recvmsg(true, error_msg) != XORP_OK) { if (errno == EWOULDBLOCK || errno == EAGAIN) { if (!_cache_valid) { error_msg += c_format("No more netlink messages to read, but didn't find response for seqno: %i\n", seqno); XLOG_WARNING("%s", error_msg.c_str()); return XORP_ERROR; } else { return XORP_OK; } } return (XORP_ERROR); } } return (XORP_OK); } /** * Receive data from the netlink socket. * * Note that this method is called asynchronously when the netlink socket * has data to receive, therefore it should never be called directly by * anything else except the netlink socket facility itself. * * @param buffer the buffer with the received data. */ void NetlinkSocketReader::netlink_socket_data(const vector& buffer) { size_t d = 0, off = 0; AlignData align_data(buffer); // // Copy data that has been requested to be cached by setting _cache_seqno // _cache_data.resize(buffer.size()); while (d < buffer.size()) { const struct nlmsghdr* nlh; nlh = align_data.payload_by_offset(d); if ((nlh->nlmsg_seq == _cache_seqno) && (nlh->nlmsg_pid == _ns.nl_pid())) { XLOG_ASSERT(buffer.size() - d >= nlh->nlmsg_len); memcpy(&_cache_data[off], nlh, nlh->nlmsg_len); off += nlh->nlmsg_len; _cache_valid = true; } d += nlh->nlmsg_len; } // XXX: shrink _cache_data to contain only the data copied to it _cache_data.resize(off); } #endif // HAVE_NETLINK_SOCKETS xorp/fea/data_plane/control_socket/SConscript0000664000076400007640000000274711540225522021555 0ustar greearbgreearb# Copyright (c) 2009-2011 XORP, Inc and Others # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # $ID$ import os Import("env") env = env.Clone() env.AppendUnique(CPPPATH = [ '#', '$BUILDDIR', ]) is_shared = env.has_key('SHAREDLIBS') sources = [ # C++ files 'netlink_socket.cc', 'netlink_socket_utilities.cc', 'routing_socket.cc', 'routing_socket_utilities.cc', 'windows_rras_support.cc', 'windows_rtm_pipe.cc' ] if env['enable_click']: sources.append('click_socket.cc') if is_shared: libxcs = env.SharedLibrary(target = 'libxorp_fea_control_socket', source = sources) env.Alias('install', env.InstallLibrary(env['xorp_libdir'], libxcs)) else: libxcs = env.StaticLibrary(target = 'libxorp_fea_control_socket', source = sources) Default(libxcs) xorp/fea/data_plane/control_socket/system_utilities.hh0000664000076400007640000001106411421137511023471 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/data_plane/control_socket/system_utilities.hh,v 1.6 2008/10/02 21:56:54 bms Exp $ #ifndef __FEA_DATA_PLANE_CONTROL_SOCKET_SYSTEM_UTILITIES_HH__ #define __FEA_DATA_PLANE_CONTROL_SOCKET_SYSTEM_UTILITIES_HH__ #include "libxorp/xorp.h" #include "libxorp/ipvx.hh" #include "libproto/packet.hh" // // Conditionally re-define some of the system macros that are not // defined properly and might generate alignment-related compilation // warning on some architectures (e.g, ARM/XScale) if we use // "-Wcast-align" compilation flag. // #ifdef HAVE_BROKEN_MACRO_CMSG_NXTHDR #if ((! defined(__CMSG_ALIGN)) && (! defined(_ALIGN))) #error "The system has broken CMSG_NXTHDR, but we don't know how to re-define it. Fix the alignment compilation error of the CMSG_NXTHDR macro in your system header files." #endif #ifndef __CMSG_ALIGN #define __CMSG_ALIGN(n) _ALIGN(n) #endif #undef CMSG_NXTHDR #define CMSG_NXTHDR(mhdr, cmsg) \ (((caddr_t)(cmsg) + __CMSG_ALIGN((cmsg)->cmsg_len) + \ __CMSG_ALIGN(sizeof(struct cmsghdr)) > \ ((caddr_t)(mhdr)->msg_control) + (mhdr)->msg_controllen) ? \ (struct cmsghdr *)NULL : \ (struct cmsghdr *)(void *)((caddr_t)(cmsg) + __CMSG_ALIGN((cmsg)->cmsg_len))) #endif // HAVE_BROKEN_MACRO_CMSG_NXTHDR // // XXX: In case of KAME the local interface index (also the link-local // scope_id) is encoded in the third and fourth octet of an IPv6 // address (for link-local unicast/multicast addresses or // interface-local multicast addresses only). // E.g., see the sa6_recoverscope() implementation inside file // "sys/netinet6/scope6.c" on KAME-derived IPv6 stack. // #ifdef HAVE_IPV6 inline IPv6 system_adjust_ipv6_recv(const IPv6& ipv6) { #ifdef IPV6_STACK_KAME in6_addr in6_addr; ipv6.copy_out(in6_addr); if (IN6_IS_ADDR_LINKLOCAL(&in6_addr) || IN6_IS_ADDR_MC_LINKLOCAL(&in6_addr) || IN6_IS_ADDR_MC_NODELOCAL(&in6_addr)) { in6_addr.s6_addr[2] = 0; in6_addr.s6_addr[3] = 0; return IPv6(in6_addr); } #endif // IPV6_STACK_KAME return ipv6; } #endif // HAVE_IPV6 inline IPvX system_adjust_ipvx_recv(const IPvX& ipvx) { #ifndef HAVE_IPV6 return ipvx; #else if (! ipvx.is_ipv6()) return ipvx; return (system_adjust_ipv6_recv(ipvx.get_ipv6())); #endif // HAVE_IPV6 } // // XXX: In case of KAME the local interface index should be set as // the link-local scope_id ((for link-local unicast/multicast addresses or // interface-local multicast addresses only). // #ifdef HAVE_IPV6 inline void system_adjust_sockaddr_in6_send(struct sockaddr_in6& sin6, uint16_t zone_id) { #ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID #ifdef IPV6_STACK_KAME if (IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr) || IN6_IS_ADDR_MC_LINKLOCAL(&sin6.sin6_addr) || IN6_IS_ADDR_MC_NODELOCAL(&sin6.sin6_addr)) { sin6.sin6_scope_id = zone_id; } #endif // IPV6_STACK_KAME #endif // HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID UNUSED(sin6); UNUSED(zone_id); } #endif // HAVE_IPV6 // // XXX: In case of KAME sometimes the local interface index is encoded // in the third and fourth octet of an IPv6 address (for link-local // unicast/multicast addresses) when we add an unicast route to the system. // E.g., see /usr/src/sbin/route/route.c on FreeBSD-6.2 and search for // the __KAME__ marker. // #ifdef HAVE_IPV6 inline void system_adjust_sockaddr_in6_route(struct sockaddr_in6& sin6, uint16_t iface_id) { #ifdef IPV6_STACK_KAME if (IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr) || IN6_IS_ADDR_MC_LINKLOCAL(&sin6.sin6_addr)) { embed_16(&sin6.sin6_addr.s6_addr[2], iface_id); #ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID sin6.sin6_scope_id = 0; #endif } #endif // IPV6_STACK_KAME UNUSED(sin6); UNUSED(iface_id); } #endif // HAVE_IPV6 #endif // _FEA_DATA_PLANE_CONTROL_SOCKET_SYSTEM_UTILITIES_HH__ xorp/fea/data_plane/control_socket/windows_routing_socket.h0000664000076400007640000002636211540224221024517 0ustar greearbgreearb/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- */ /* vim:set sts=4 ts=8: */ /* * Copyright (c) 2001-2011 XORP, Inc and Others * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 2, June * 1991 as published by the Free Software Foundation. Redistribution * and/or modification of this program under the terms of any other * version of the GNU General Public License is not permitted. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, * see the GNU General Public License, Version 2, a copy of which can be * found in the XORP LICENSE.gpl file. * * XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; * http://xorp.net */ /* * $XORP: xorp/fea/data_plane/control_socket/windows_routing_socket.h,v 1.7 2008/10/02 21:56:54 bms Exp $ */ /* * This header contains definitions and structures for XORP's adapter * code for Microsoft's Router Manager V2 API. A pair of DLLs is installed * into the Routing and Remote Access Service by the FEA which communicate * Windows internal routing information to XORP using the BSD routing * socket message format over named pipes. */ /*- * Copyright (c) 1980, 1986, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)route.h 8.4 (Berkeley) 1/9/95 * $FreeBSD: src/sys/net/route.h,v 1.63.2.1 2006/04/04 20:07:23 andre Exp $ */ #ifndef __FEA_DATA_PLANE_CONTROL_SOCKET_WINDOWS_ROUTING_SOCKET_HH__ #define __FEA_DATA_PLANE_CONTROL_SOCKET_WINDOWS_ROUTING_SOCKET_HH__ #include "libxorp/xorp.h" #ifdef HOST_OS_WINDOWS #ifdef __cplusplus extern "C" { #endif #define XORPRTM4_PIPENAME "\\\\.\\pipe\\XorpRtm4" #define XORPRTM4_LOGNAME "XORPRTM4" #define XORPRTM6_PIPENAME "\\\\.\\pipe\\XorpRtm6" #define XORPRTM6_LOGNAME "XORPRTM6" #ifdef IPV6_DLL #define XORPRTM_PIPENAME XORPRTM6_PIPENAME #define XORPRTM_LOGNAME XORPRTM6_LOGNAME #define XORPRTM_TRACENAME XORPRTM6_LOGNAME #else #define XORPRTM_PIPENAME XORPRTM4_PIPENAME #define XORPRTM_LOGNAME XORPRTM4_LOGNAME #define XORPRTM_TRACENAME XORPRTM4_LOGNAME #endif /* * Registry glue for Router Manager */ #define HKLM_XORPRTM4_NAME \ "SOFTWARE\\Microsoft\\Router\\CurrentVersion\\RouterManagers\\Ip\\XORPRTM4" #define HKLM_XORPRTM6_NAME \ "SOFTWARE\\Microsoft\\Router\\CurrentVersion\\RouterManagers\\Ipv6\\XORPRTM6" #define HKLM_XORPRTM4_TRACING_NAME \ "SOFTWARE\\Microsoft\\Tracing\\XORPRTM4" #define HKLM_XORPRTM6_TRACING_NAME \ "SOFTWARE\\Microsoft\\Tracing\\XORPRTM6" #define RTMV2_CLSID_IPV4 "{C2FE450A-D6C2-11D0-A37B-00C04FC9DA04}" #define RTMV2_CLSID_IPV6 "{C2FE451A-D6C2-11D0-A37B-00C04FC9DA04}" #define XORPRTM_DLL_VENDOR "www.xorp.org" #define XORPRTM_DLL_FLAGS 0x00000002 #define XORPRTM_CONFIG_DLL_NAME "nonexistent.dll" #define XORPRTM4_DLL_NAME "xorprtm4.dll" #define XORPRTM4_DLL_TITLE "Router Manager V2 adapter for XORP (IPv4)" #define XORPRTM6_DLL_NAME "xorprtm6.dll" #define XORPRTM6_DLL_TITLE "Router Manager V2 adapter for XORP (IPv6)" #define XORPRTM_TRACING_PATH "%windir%\\Tracing" /* * Router Manager V2 IDs for XORP */ #define PROTO_IP_XORPRTM 7 #define XORPRTM_PROTOCOL_ID \ PROTOCOL_ID(PROTO_TYPE_UCAST, PROTO_VENDOR_MS0, PROTO_IP_XORPRTM) #define XORPRTM_GLOBAL_CONFIG_ID 1 /* * MS Router Manager info structures */ typedef struct _XORPRTM_GLOBAL_CONFIG { DWORD dummy; } XORPRTM_GLOBAL_CONFIG, *PXORPRTM_GLOBAL_CONFIG; typedef struct _XORPRTM_MIB_SET_INPUT_DATA { DWORD IMSID_TypeID; DWORD IMSID_IfIndex; DWORD IMSID_BufferSize; BYTE IMSID_Buffer[0]; } XORPRTM_MIB_SET_INPUT_DATA, *PXORPRTM_MIB_SET_INPUT_DATA; typedef struct _XORPRTM_MIB_GET_INPUT_DATA { DWORD IMGID_TypeID; DWORD IMGID_IfIndex; } XORPRTM_MIB_GET_INPUT_DATA, *PXORPRTM_MIB_GET_INPUT_DATA; typedef struct _XORPRTM_MIB_GET_OUTPUT_DATA { DWORD IMGOD_TypeID; DWORD IMGOD_IfIndex; BYTE IMGOD_Buffer[0]; } XORPRTM_MIB_GET_OUTPUT_DATA, *PXORPRTM_MIB_GET_OUTPUT_DATA; /* * BSD routing socket interface */ #define RTF_UP 0x1 /* route usable */ #define RTF_GATEWAY 0x2 /* destination is a gateway */ #define RTF_HOST 0x4 /* host entry (net otherwise) */ #define RTF_REJECT 0x8 /* host or net unreachable */ #define RTF_DYNAMIC 0x10 /* created dynamically (by redirect) */ #define RTF_MODIFIED 0x20 /* modified dynamically (by redirect) */ #define RTF_DONE 0x40 /* message confirmed */ /* 0x80 unused, was RTF_DELCLONE */ #define RTF_CLONING 0x100 /* generate new routes on use */ #define RTF_XRESOLVE 0x200 /* external daemon resolves name */ #define RTF_LLINFO 0x400 /* generated by link layer (e.g. ARP) */ #define RTF_STATIC 0x800 /* manually added */ #define RTF_BLACKHOLE 0x1000 /* just discard pkts (during updates) */ #define RTF_PROTO2 0x4000 /* protocol specific routing flag */ #define RTF_PROTO1 0x8000 /* protocol specific routing flag */ #define RTF_WASCLONED 0x20000 /* route generated through cloning */ #define RTF_PROTO3 0x40000 /* protocol specific routing flag */ /* 0x80000 unused */ #define RTF_PINNED 0x100000 /* future use */ #define RTF_LOCAL 0x200000 /* route represents a local address */ #define RTF_BROADCAST 0x400000 /* route represents a bcast address */ #define RTF_MULTICAST 0x800000 /* route represents a mcast address */ /* 0x1000000 and up unassigned */ /* Mask of RTF flags that are allowed to be modified by RTM_CHANGE. */ #define RTF_FMASK \ (RTF_PROTO1 | RTF_PROTO2 | RTF_PROTO3 | RTF_BLACKHOLE | \ RTF_REJECT | RTF_STATIC) struct rt_metrics { DWORD rmx_filler[14]; /* Ignore field names but pad in same way */ }; /* * Structures for routing messages. */ struct rt_msghdr { USHORT rtm_msglen; /* to skip over non-understood messages */ BYTE rtm_version; /* future binary compatibility */ BYTE rtm_type; /* message type */ USHORT rtm_index; /* index for associated ifp */ DWORD rtm_flags; /* flags, incl. kern & message, e.g. DONE */ DWORD rtm_addrs; /* bitmask identifying sockaddrs in msg */ LONG rtm_pid; /* identify sender */ LONG rtm_seq; /* for sender to identify action */ DWORD rtm_errno; /* why failed */ DWORD rtm_fmask; /* bitmask used in RTM_CHANGE message */ #define rtm_use rtm_fmask /* deprecated, use rtm_rmx->rmx_pksent */ DWORD rtm_inits; /* which metrics we are initializing */ struct rt_metrics rtm_rmx; /* metrics themselves */ }; #define RTM_VERSION 66 /* Unique to XORP/Win32 */ /* * Message types. * Only those supported by the Windows subsystem are provided. */ #define RTM_ADD 0x1 /* Add Route */ #define RTM_DELETE 0x2 /* Delete Route */ #define RTM_CHANGE 0x3 /* Change Metrics or flags */ #define RTM_NEWADDR 0xc /* address being added to iface */ #define RTM_DELADDR 0xd /* address being removed from iface */ #define RTM_IFINFO 0xe /* iface going up/down etc. */ #define RTM_IFANNOUNCE 0x11 /* iface arrival/departure */ /* * Bitmask values for rtm_addrs. */ #define RTA_DST 0x1 /* destination sockaddr present */ #define RTA_GATEWAY 0x2 /* gateway sockaddr present */ #define RTA_NETMASK 0x4 /* netmask sockaddr present */ #define RTA_GENMASK 0x8 /* cloning mask sockaddr present */ #define RTA_IFP 0x10 /* interface name sockaddr present */ #define RTA_IFA 0x20 /* interface addr sockaddr present */ #define RTA_AUTHOR 0x40 /* sockaddr for author of redirect */ #define RTA_BRD 0x80 /* for NEWADDR, broadcast or p-p dest addr */ /* * Index offsets for sockaddr array for alternate internal encoding. */ #define RTAX_DST 0 /* destination sockaddr present */ #define RTAX_GATEWAY 1 /* gateway sockaddr present */ #define RTAX_NETMASK 2 /* netmask sockaddr present */ #define RTAX_GENMASK 3 /* cloning mask sockaddr present */ #define RTAX_IFP 4 /* interface name sockaddr present */ #define RTAX_IFA 5 /* interface addr sockaddr present */ #define RTAX_AUTHOR 6 /* sockaddr for author of redirect */ #define RTAX_BRD 7 /* for NEWADDR, broadcast or p-p dest addr */ #define RTAX_MAX 8 /* size of array to allocate */ /* * XXX: The Winsock definition of struct sockaddr does not have * a size value, therefore we use struct sockaddr_storage in its entirety. */ #define SA_SIZE(sa) sizeof(struct sockaddr_storage) /* * XXX: The length of IFNAMSIZ must be consistent across the ABI. * It's different from BSDs to allow for arbitrary FriendlyNames, * up to a length of 256 bytes. */ #ifdef IFNAMSIZ #undef IFNAMSIZ #endif #ifndef IFNAMSIZ #define IFNAMSIZ 256 #endif #define IFAN_ARRIVAL 0 /* interface arrival */ #define IFAN_DEPARTURE 1 /* interface departure */ struct if_announcemsghdr { USHORT ifan_msglen; BYTE ifan_version; BYTE ifan_type; DWORD ifan_index; /* XXX: This is now 32-bits wide */ BYTE ifan_name[IFNAMSIZ]; /* FriendlyName in ANSI text. */ BYTE ifan_what; }; /* * Values for if_link_state. */ #define LINK_STATE_UNKNOWN 0 /* link invalid/unknown */ #define LINK_STATE_DOWN 1 /* link is down */ #define LINK_STATE_UP 2 /* link is up */ /* * XXX: Most of the fields in the original BSD if_data * structure can't be obtained on Windows from the * RTMv2 interface status callback, */ struct if_data { BYTE ifi_link_state; }; struct if_msghdr { USHORT ifm_msglen; BYTE ifm_version; BYTE ifm_type; DWORD ifm_addrs; DWORD ifm_flags; DWORD ifm_index; struct if_data ifm_data; }; struct ifa_msghdr { USHORT ifam_msglen; BYTE ifam_version; BYTE ifam_type; DWORD ifam_addrs; DWORD ifam_flags; DWORD ifam_index; DWORD ifam_metric; }; #ifdef __cplusplus } #endif #endif // HOST_OS_WINDOWS #endif /* __FEA_DATA_PLANE_CONTROL_SOCKET_WINDOWS_ROUTING_SOCKET_HH__ */ xorp/fea/data_plane/control_socket/windows_rras_support.hh0000664000076400007640000000261511540224221024366 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/data_plane/control_socket/windows_rras_support.hh,v 1.5 2008/10/02 21:56:54 bms Exp $ #ifndef __FEA_DATA_PLANE_CONTROL_SOCKET_WINDOWS_RRAS_SUPPORT_HH__ #define __FEA_DATA_PLANE_CONTROL_SOCKET_WINDOWS_RRAS_SUPPORT_HH__ class WinSupport { public: static bool is_rras_running(); static int restart_rras(); static int add_protocol_to_rras(int family); static int add_protocol_to_registry(int family); private: }; #endif // __FEA_DATA_PLANE_CONTROL_SOCKET_WINDOWS_RRAS_SUPPORT_HH__ xorp/fea/data_plane/control_socket/routing_socket_utilities.cc0000664000076400007640000003664011540225522025203 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include #if defined(HAVE_ROUTING_SOCKETS) || defined(HOST_OS_WINDOWS) #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ether_compat.h" #include "libxorp/ipvx.hh" #ifdef HAVE_SYS_IOCTL_H #include #endif #ifdef HAVE_NET_IF_H #include #endif #ifdef HAVE_NET_IF_VAR_H #include #endif #ifdef HAVE_NET_IF_DL_H #include #endif #ifdef HAVE_NET_ROUTE_H #include #endif #ifdef HAVE_NET_IF_TYPES_H #include #endif #ifdef HAVE_NETINET6_IN6_VAR_H #include #endif #ifdef HOST_OS_WINDOWS #include "windows_routing_socket.h" #endif #include "system_utilities.hh" #include "routing_socket_utilities.hh" // // RTM format related utilities for manipulating data // (e.g., obtained by routing sockets or by sysctl(3) mechanism). // /** * @param m message type from routing socket message * @return human readable form. */ string RtmUtils::rtm_msg_type(uint32_t m) { struct { uint32_t value; const char* name; } rtm_msg_types[] = { #define RTM_MSG_ENTRY(X) { X, #X } #ifdef RTM_ADD RTM_MSG_ENTRY(RTM_ADD), #endif #ifdef RTM_DELETE RTM_MSG_ENTRY(RTM_DELETE), #endif #ifdef RTM_CHANGE RTM_MSG_ENTRY(RTM_CHANGE), #endif #ifdef RTM_GET RTM_MSG_ENTRY(RTM_GET), #endif #ifdef RTM_LOSING RTM_MSG_ENTRY(RTM_LOSING), #endif #ifdef RTM_REDIRECT RTM_MSG_ENTRY(RTM_REDIRECT), #endif #ifdef RTM_MISS RTM_MSG_ENTRY(RTM_MISS), #endif #ifdef RTM_LOCK RTM_MSG_ENTRY(RTM_LOCK), #endif #ifdef RTM_OLDADD RTM_MSG_ENTRY(RTM_OLDADD), #endif #ifdef RTM_OLDDEL RTM_MSG_ENTRY(RTM_OLDDEL), #endif #ifdef RTM_RESOLVE RTM_MSG_ENTRY(RTM_RESOLVE), #endif #ifdef RTM_NEWADDR RTM_MSG_ENTRY(RTM_NEWADDR), #endif #ifdef RTM_DELADDR RTM_MSG_ENTRY(RTM_DELADDR), #endif #ifdef RTM_IFINFO RTM_MSG_ENTRY(RTM_IFINFO), #endif #ifdef RTM_NEWMADDR RTM_MSG_ENTRY(RTM_NEWMADDR), #endif #ifdef RTM_DELMADDR RTM_MSG_ENTRY(RTM_DELMADDR), #endif #ifdef RTM_IFANNOUNCE RTM_MSG_ENTRY(RTM_IFANNOUNCE), #endif { ~0U, "Unknown" } }; const size_t n_rtm_msgs = sizeof(rtm_msg_types) / sizeof(rtm_msg_types[0]); const char* ret = 0; for (size_t i = 0; i < n_rtm_msgs; i++) { ret = rtm_msg_types[i].name; if (rtm_msg_types[i].value == m) break; } return ret; } /* * Round up to nearest integer value of step. * Taken from ROUND_UP macro in Stevens. */ static inline size_t round_up(size_t val, size_t step) { return (val & (step - 1)) ? (1 + (val | (step - 1))) : val; } /* * Step to next socket address pointer. * Taken from NEXT_SA macro in Stevens. */ static inline const struct sockaddr* next_sa(const struct sockaddr* sa) { const size_t min_size = sizeof(u_long); size_t sa_size = min_size; #ifdef HOST_OS_WINDOWS /* * XXX: XORP's modified BSD-style routing socket interface * to Router Manager V2 in Windows Longhorn always uses * a fixed sockaddr size of sockaddr_storage. */ sa_size = sizeof(struct sockaddr_storage); #else // ! HOST_OS_WINDOWS #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN sa_size = sa->sa_len ? round_up(sa->sa_len, min_size) : min_size; #else // ! HAVE_STRUCT_SOCKADDR_SA_LEN switch (sa->sa_family) { case AF_INET: sa_size = sizeof(struct sockaddr_in); break; #ifdef HAVE_IPV6 case AF_INET6: sa_size = sizeof(struct sockaddr_in6); break; #endif // HAVE_IPV6 default: sa_size = sizeof(struct sockaddr_in); break; } #endif // ! HAVE_STRUCT_SOCKADDR_SA_LEN #endif // HOST_OS_WINDOWS // XXX: the sa_size offset is aligned, hence we can use a void pointer const void* p = reinterpret_cast(sa) + sa_size; return reinterpret_cast(p); } void RtmUtils::get_rta_sockaddr(uint32_t amask, const struct sockaddr* sock, const struct sockaddr* rti_info[]) { size_t sa_len = sizeof(*sock); for (uint32_t i = 0; i < RTAX_MAX; i++) { if (amask & (1 << i)) { #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN sa_len = sock->sa_len; debug_msg("\tPresent 0x%02x af %d size %u\n", 1 << i, sock->sa_family, XORP_UINT_CAST(sa_len)); #else UNUSED(sa_len); #endif rti_info[i] = sock; sock = next_sa(sock); } else { rti_info[i] = 0; } } } /* * Return the mask length on success, otherwise -1 */ int RtmUtils::get_sock_mask_len(int family, const struct sockaddr* sock) { #ifndef HAVE_STRUCT_SOCKADDR_SA_LEN switch (family) { case AF_INET: { // XXX: sock->sa_family is undefined const struct sockaddr_in* sin = sockaddr2sockaddr_in(sock); IPv4 netmask(sin->sin_addr); return (netmask.mask_len()); } #ifndef HOST_OS_WINDOWS // XXX not yet for windows #ifdef HAVE_IPV6 case AF_INET6: { // XXX: sock->sa_family is undefined const struct sockaddr_in6* sin6 = sockaddr2sockaddr_in6(sock); IPv6 netmask(sin6->sin6_addr); return (netmask.mask_len()); } #endif // HAVE_IPV6 #endif default: XLOG_FATAL("Invalid address family %d", family); } #else // HAVE_STRUCT_SOCKADDR_SA_LEN switch (family) { case AF_INET: { uint8_t buf[4]; buf[0] = buf[1] = buf[2] = buf[3] = 0; const uint8_t* ptr = reinterpret_cast(&sock->sa_data[2]); switch (sock->sa_len) { case 0: return (0); case 8: buf[3] = *(ptr + 3); // FALLTHROUGH case 7: buf[2] = *(ptr + 2); // FALLTHROUGH case 6: buf[1] = *(ptr + 1); // FALLTHROUGH case 5: buf[0] = *(ptr + 0); { IPv4 netmask(buf); return (netmask.mask_len()); } default: // XXX: assume that the whole mask is stored { // XXX: sock->sa_family is undefined const struct sockaddr_in* sin = sockaddr2sockaddr_in(sock); IPv4 netmask(sin->sin_addr); return (netmask.mask_len()); } } } #ifndef HOST_OS_WINDOWS // Not yet for Windows #ifdef HAVE_IPV6 case AF_INET6: { if (sock->sa_len == 0) { // XXX: the default /0 route return (0); } // XXX: sock->sa_family is undefined struct sockaddr_in6 sin6; memset(&sin6, 0, sizeof(sin6)); memcpy(&sin6, sock, sock->sa_len); sin6.sin6_len = sizeof(struct sockaddr_in6); sin6.sin6_family = AF_INET6; IPv6 netmask(sin6.sin6_addr); return (netmask.mask_len()); } #endif // HAVE_IPV6 #endif default: XLOG_FATAL("Invalid address family %d", family); } #endif // HAVE_STRUCT_SOCKADDR_SA_LEN return (-1); } int RtmUtils::rtm_get_to_fte_cfg(const IfTree& iftree, FteX& fte, const struct rt_msghdr* rtm) { const struct sockaddr *sa, *rti_info[RTAX_MAX]; uint32_t if_index = rtm->rtm_index; string if_name; string vif_name; int family = fte.nexthop().af(); bool is_family_match = false; bool is_deleted = false; bool is_unresolved = false; bool lookup_ifindex = true; bool xorp_route = false; bool is_recognized = false; if ((rtm->rtm_type == RTM_ADD) || (rtm->rtm_type == RTM_DELETE) || (rtm->rtm_type == RTM_CHANGE) #ifdef RTM_GET || (rtm->rtm_type == RTM_GET) #endif #ifdef RTM_MISS || (rtm->rtm_type == RTM_MISS) #endif #ifdef RTM_RESOLVE || (rtm->rtm_type == RTM_RESOLVE) #endif ) { is_recognized = true; } XLOG_ASSERT(is_recognized); debug_msg("%p index %d type %s\n", rtm, if_index, rtm_msg_type(rtm->rtm_type).c_str()); if (rtm->rtm_errno != 0) return (XORP_ERROR); // XXX: ignore entries with an error // Reset the result fte.zero(); // Test if this entry was deleted if (rtm->rtm_type == RTM_DELETE) is_deleted = true; // Get the pointers to the corresponding data structures sa = reinterpret_cast(rtm + 1); RtmUtils::get_rta_sockaddr(rtm->rtm_addrs, sa, rti_info); IPvX dst_addr(family); IPvX nexthop_addr(family); int dst_mask_len = 0; // // Get the destination // if ( (sa = rti_info[RTAX_DST]) != NULL) { if (sa->sa_family == family) { dst_addr.copy_in(*rti_info[RTAX_DST]); dst_addr = system_adjust_ipvx_recv(dst_addr); is_family_match = true; } } // // Deal with BSD upcalls. These only ever have RTAX_DST, and // only contain host addresses. // #ifdef RTM_MISS if (rtm->rtm_type == RTM_MISS) is_unresolved = true; #endif #ifdef RTM_RESOLVE if (rtm->rtm_type == RTM_RESOLVE) is_unresolved = true; #endif if (is_unresolved) { nexthop_addr = IPvX::ZERO(family); dst_mask_len = IPvX::addr_bitlen(family); if_name = ""; vif_name = ""; lookup_ifindex = false; } // // Get the next-hop router address // XXX: Windows does not include the 'gateway'. // if ( (sa = rti_info[RTAX_GATEWAY]) != NULL) { if (sa->sa_family == family) { nexthop_addr.copy_in(*rti_info[RTAX_GATEWAY]); nexthop_addr = system_adjust_ipvx_recv(nexthop_addr); is_family_match = true; } } #ifdef RTF_LLINFO if ((rtm->rtm_flags & RTF_LLINFO) && (nexthop_addr == IPvX::ZERO(family))) { // Link-local entry (could be the broadcast address as well) bool not_bcast_addr = true; #ifdef RTF_BROADCAST if (rtm->rtm_flags & RTF_BROADCAST) not_bcast_addr = false; #endif if (not_bcast_addr) nexthop_addr = dst_addr; } #endif /* RTF_LLINFO */ if (! is_family_match) return (XORP_ERROR); // // Get the destination mask length // if ( (sa = rti_info[RTAX_NETMASK]) != NULL) { dst_mask_len = RtmUtils::get_sock_mask_len(family, sa); } // // Patch the destination mask length (if necessary) // if (rtm->rtm_flags & RTF_HOST) { if (dst_mask_len == 0) dst_mask_len = IPvX::addr_bitlen(family); } // // Test whether we installed this route // XXX: Windows does not set this flag currently. // if (rtm->rtm_flags & RTF_PROTO1) xorp_route = true; // // Test whether this route is a discard route. // Older BSD kernels do not implement a software discard interface, // so map this back to a reference to the FEA's notion of one. // // XXX: Windows does not currently support blackhole/reject routes. // if (rtm->rtm_flags & RTF_BLACKHOLE) { // // Try to map discard routes back to the first software discard // interface in the tree. If we don't have one, then ignore this route. // We have to scan all interfaces because IfTree elements // are held in a map, and we don't key on this property. // const IfTreeInterface* pi = NULL; for (IfTree::IfMap::const_iterator ii = iftree.interfaces().begin(); ii != iftree.interfaces().end(); ++ii) { if (ii->second->discard()) { pi = ii->second; break; } } if (pi == NULL) { // // XXX: Cannot map a discard route back to an FEA soft discard // interface. // return (XORP_ERROR); } if_name = pi->ifname(); vif_name = if_name; // XXX: ifname == vifname // XXX: Do we need to change nexthop_addr? lookup_ifindex = false; } // // Test whether this route is an unreachable route. // Older BSD kernels do not implement a software unreachable interface, // so map this back to a reference to the FEA's notion of one. // // XXX: Windows does not currently support blackhole/reject routes. // if (rtm->rtm_flags & RTF_REJECT) { // // Try to map unreachable routes back to the first software // unreachable interface in the tree. If we don't have one, then // ignore this route. // We have to scan all interfaces because IfTree elements // are held in a map, and we don't key on this property. // const IfTreeInterface* pi = NULL; for (IfTree::IfMap::const_iterator ii = iftree.interfaces().begin(); ii != iftree.interfaces().end(); ++ii) { if (ii->second->unreachable()) { pi = ii->second; break; } } if (pi == NULL) { // // XXX: Cannot map an unreachable route back to an FEA soft // unreachable interface. // return (XORP_ERROR); } if_name = pi->ifname(); vif_name = if_name; // XXX: ifname == vifname // XXX: Do we need to change nexthop_addr? lookup_ifindex = false; } // // Get the interface/vif name and index // if (lookup_ifindex) { if (if_index != 0) { const IfTreeVif* vifp = iftree.find_vif(if_index); if (vifp != NULL) { if_name = vifp->ifname(); vif_name = vifp->vifname(); } } if (if_name.empty()) { #ifdef AF_LINK sa = rti_info[RTAX_IFP]; if (sa != NULL) { // Use the RTAX_IFP info to get the interface name if (sa->sa_family != AF_LINK) { // TODO: verify whether this is really an error. XLOG_ERROR("Ignoring RTM_GET for RTAX_IFP with sa_family = %d", sa->sa_family); return (XORP_ERROR); } const struct sockaddr_dl* sdl; sdl = reinterpret_cast(sa); if (sdl->sdl_nlen > 0) { if_name = string(sdl->sdl_data, sdl->sdl_nlen); vif_name = if_name; // TODO: XXX: not true for VLAN } } #endif // AF_LINK } // Test whether the interface/vif name was found if (if_name.empty() || vif_name.empty()) { if (is_deleted) { // // XXX: If the route is deleted and we cannot find // the corresponding interface/vif, this could be because // an interface/vif is deleted from the kernel, and // the kernel might send first the upcall message // that deletes the interface from user space. // Hence the upcall message that followed to delete the // corresponding route for the connected subnet // won't find the interface/vif. // Propagate the route deletion with empty interface // and vif name. // } else { // // XXX: A route was added, but we cannot find the corresponding // interface/vif. This might happen because of a race // condition. E.g., an interface was added and then // immediately deleted, but the processing for the addition of // the corresponding connected route was delayed. // Note that the misorder in the processing might happen // because the interface and routing control messages are // received on different control sockets. // For the time being make it a fatal error until there is // enough evidence and the issue is understood. // IPvXNet dst_subnet(dst_addr, dst_mask_len); XLOG_FATAL("Decoding for route %s next hop %s failed: " "could not find interface and vif for index %d", dst_subnet.str().c_str(), nexthop_addr.str().c_str(), if_index); } } } // // TODO: define default routing metric and admin distance instead of 0xffff // fte = FteX(IPvXNet(dst_addr, dst_mask_len), nexthop_addr, if_name, vif_name, 0xffff, 0xffff, xorp_route); if (is_deleted) fte.mark_deleted(); if (is_unresolved) fte.mark_unresolved(); return (XORP_OK); } #endif // HAVE_ROUTING_SOCKETS xorp/fea/data_plane/control_socket/netlink_socket.hh0000664000076400007640000002125611540225522023074 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __FEA_DATA_PLANE_CONTROL_SOCKET_NETLINK_SOCKET_HH__ #define __FEA_DATA_PLANE_CONTROL_SOCKET_NETLINK_SOCKET_HH__ #include #ifdef HAVE_NETLINK_SOCKETS #include "libxorp/eventloop.hh" #include "libxorp/exceptions.hh" class NetlinkSocketObserver; class NetlinkSocketPlumber; /** * NetlinkSocket class opens a netlink socket and forwards data arriving * on the socket to NetlinkSocketObservers. The NetlinkSocket hooks itself * into the EventLoop and activity usually happens asynchronously. */ class NetlinkSocket : public NONCOPYABLE { public: NetlinkSocket(EventLoop& eventloop, uint32_t table_id); virtual ~NetlinkSocket(); /** * Start the netlink socket operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int start(string& error_msg); /** * Stop the netlink socket operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int stop(string& error_msg); /** * Test if the netlink socket is open. * * This method is needed because NetlinkSocket may fail to open * netlink socket during startup. * * @return true if the netlink socket is open, otherwise false. */ bool is_open() const { return _fd >= 0; } /** * Write data to netlink socket. * * This method also updates the sequence number associated with * this netlink socket. * * @return the number of bytes which were written, or -1 if error. */ ssize_t write(const void* data, size_t nbytes); /** * Sendto data on netlink socket. * * This method also updates the sequence number associated with * this netlink socket. * * @return the number of bytes which were written, or -1 if error. */ ssize_t sendto(const void* data, size_t nbytes, int flags, const struct sockaddr* to, socklen_t tolen); /** * Get the sequence number for next message written into the kernel. * * The sequence number is derived from the instance number of this netlink * socket and a 16-bit counter. * * @return the sequence number for the next message written into the * kernel. */ uint32_t seqno() const { return (_instance_no << 16 | _seqno); } /** * Get cached netlink socket identifier value. * * @return the cached netlink socket identifier value. */ uint32_t nl_pid() const { return _nl_pid; } /** * Force socket to recvmsg data. * * This usually is performed after writing a sendmsg() request that the * kernel will answer (e.g., after writing a route lookup). * Use sparingly, with caution, and at your own risk. * * @param flags the flags argument to the underlying recvmsg(2) * system call. * @param only_kernel_messages if true, accept only messages originated * by the kernel. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int force_recvmsg_flgs(int flags, bool only_kernel_messages, string& error_msg); /** Same as above, but always passes MSG_DONTWAIT as flags so that we don't block * if packet is filtered, for example. */ int force_recvmsg(bool only_kernel_messages, string& err_msg); /** * Set the netlink multicast groups to listen for on the netlink socket. * * Note that this method must be called before method start() is called. * If this method is not called, then the netlink socket will listen * to the default set of netlink multicast groups (the empty set). * * @param v the set of netlink multicast groups to listen for on the * netlink socket. */ void set_nl_groups(uint32_t v) { _nl_groups = v; } /** * Set a flag to expect to read a multipart message that is terminated * with NLMSG_DONE. * * This flag is required to fix a bug with the Linux kernel: * if we try to read the whole forwarding table, the kernel * doesn't set the NLM_F_MULTI flag in each part of the multi-part * message. The problem is similar when we read all addresses on * an interface. * * @param v if true, set the flag to expect to read a multi-part message * that is terminated with NLMSG_DONE. */ void set_multipart_message_read(bool v) { _is_multipart_message_read = v; } /** Routing table ID that we are interested in might have changed. */ virtual int notify_table_id_change(uint32_t new_tbl); private: typedef list ObserverList; /** * Read data available for NetlinkSocket and invoke * NetlinkSocketObserver::netlink_socket_data() on all observers of netlink * socket. */ void io_event(XorpFd fd, IoEventType sm); int bind_table_id(); static const size_t NETLINK_SOCKET_BYTES = 8*1024; // Initial guess at msg size EventLoop& _eventloop; int _fd; ObserverList _ol; uint16_t _seqno; // Seqno of next write() uint16_t _instance_no; // Instance number of this netlink socket static uint16_t _instance_cnt; uint32_t _nl_pid; uint32_t _nl_groups; // The netlink multicast groups to listen for uint32_t _table_id; // routing table.. or 0 if any/all (default behaviour) bool _is_multipart_message_read; // If true, expect to read a multipart message uint32_t _nlm_count; // keep track of how many msgs received. friend class NetlinkSocketPlumber; // class that hooks observers in and out }; class NetlinkSocketObserver { public: NetlinkSocketObserver(NetlinkSocket& ns); virtual ~NetlinkSocketObserver(); /** * Receive data from the netlink socket. * * Note that this method is called asynchronously when the netlink socket * has data to receive, therefore it should never be called directly by * anything else except the netlink socket facility itself. * * @param buffer the buffer with the received data. */ virtual void netlink_socket_data(const vector& buffer) = 0; /** * Get NetlinkSocket associated with Observer. */ NetlinkSocket& netlink_socket(); private: NetlinkSocket& _ns; }; class NetlinkSocketReader : public NetlinkSocketObserver { public: NetlinkSocketReader(NetlinkSocket& ns); virtual ~NetlinkSocketReader(); /** * Force the reader to receive data from the specified netlink socket. * * @param ns the netlink socket to receive the data from. * @param seqno the sequence number of the data to receive. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int receive_data(NetlinkSocket& ns, uint32_t seqno, string& error_msg); /** * Get the buffer with the data that was received. * * @return a reference to the buffer with the data that was received. */ const vector& buffer() const { return (_cache_data); } /** * Receive data from the netlink socket. * * Note that this method is called asynchronously when the netlink socket * has data to receive, therefore it should never be called directly by * anything else except the netlink socket facility itself. * * @param buffer the buffer with the received data. */ virtual void netlink_socket_data(const vector& buffer); private: NetlinkSocket& _ns; bool _cache_valid; // Cache data arrived. uint32_t _cache_seqno; // Seqno of netlink socket data to // cache so reading via netlink // socket can appear synchronous. vector _cache_data; // Cached netlink socket data. }; #endif // HAVE_NETLINK_SOCKETS #endif // __FEA_DATA_PLANE_CONTROL_SOCKET_NETLINK_SOCKET_HH__ xorp/fea/data_plane/control_socket/windows_rtm_pipe.cc0000664000076400007640000001643611540224221023436 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/utils.hh" #include #ifdef HAVE_SYS_TYPES_H #include #endif #include #ifdef HOST_OS_WINDOWS #include "libxorp/win_io.h" #include "windows_routing_socket.h" #endif #include "libcomm/comm_api.h" #include "windows_rtm_pipe.hh" uint16_t WinRtmPipe::_instance_cnt = 0; pid_t WinRtmPipe::_pid = getpid(); // // Routing Sockets communication with the kernel // WinRtmPipe::WinRtmPipe(EventLoop& eventloop) : _eventloop(eventloop), _seqno(0), _instance_no(_instance_cnt++) { } WinRtmPipe::~WinRtmPipe() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the RTMv2 pipe: %s", error_msg.c_str()); } XLOG_ASSERT(_ol.empty()); } #ifndef HOST_OS_WINDOWS int WinRtmPipe::start(int af, string& error_msg) { UNUSED(af); error_msg = c_format("The system does not support Router Manager V2"); XLOG_UNREACHABLE(); return (XORP_ERROR); } int WinRtmPipe::stop(string& error_msg) { UNUSED(error_msg); return (XORP_OK); } ssize_t WinRtmPipe::write(const void* data, size_t nbytes) { return (-1); UNUSED(data); UNUSED(nbytes); } int WinRtmPipe::force_read(string& error_msg) { XLOG_UNREACHABLE(); error_msg = "method not supported"; return (XORP_ERROR); } #else // HOST_OS_WINDOWS int WinRtmPipe::start(int af, string& error_msg) { string pipename; DWORD result; if (af == AF_INET) { pipename = XORPRTM4_PIPENAME; } else if (af == AF_INET6) { pipename = XORPRTM6_PIPENAME; } else { error_msg = c_format("Unknown address family %d.", af); return (XORP_ERROR); } if (!WaitNamedPipeA(pipename.c_str(), NMPWAIT_USE_DEFAULT_WAIT)) { error_msg = c_format("No RTMv2 pipes available."); return (XORP_ERROR); } _fd = CreateFileA(pipename.c_str(), GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); if (!_fd.is_valid()) { result = GetLastError(); error_msg = c_format("Error opening RTMv2 pipe: %s.", win_strerror(result)); return (XORP_ERROR); } if (_eventloop.add_ioevent_cb(_fd, IOT_READ, callback(this, &WinRtmPipe::io_event)) == false) { error_msg = c_format("Failed to add RTMv2 pipe read to EventLoop"); CloseHandle(_fd); _fd.clear(); return (XORP_ERROR); } if (_eventloop.add_ioevent_cb(_fd, IOT_DISCONNECT, callback(this, &WinRtmPipe::io_event)) == false) { error_msg = c_format("Failed to add RTMv2 pipe close to EventLoop"); CloseHandle(_fd); _fd.clear(); return (XORP_ERROR); } return (XORP_OK); } int WinRtmPipe::stop(string& error_msg) { UNUSED(error_msg); if (_fd.is_valid()) { _eventloop.remove_ioevent_cb(_fd, IOT_READ); _eventloop.remove_ioevent_cb(_fd, IOT_DISCONNECT); CloseHandle(_fd); _fd.clear(); } return (XORP_OK); } ssize_t WinRtmPipe::write(const void* data, size_t nbytes) { DWORD byteswritten; DWORD result; if (!_fd.is_valid()) return (-1); _seqno++; result = WriteFile(_fd, data, nbytes, &byteswritten, NULL); return ((size_t)byteswritten); UNUSED(result); } int WinRtmPipe::force_read(string& error_msg) { vector message; vector buffer(ROUTING_SOCKET_BYTES); size_t off = 0; size_t last_mh_off = 0; DWORD nbytes; DWORD result; if (!_fd.is_valid()) return (XORP_ERROR); for (;;) { do { result = PeekNamedPipe(_fd, &buffer[0], buffer.size(), NULL, NULL, &nbytes); if (result == 0 && GetLastError() == ERROR_INSUFFICIENT_BUFFER) { buffer.resize(buffer.size() + ROUTING_SOCKET_BYTES); continue; } else { break; } } while (true); result = ReadFile(_fd, &buffer[0], buffer.size(), &nbytes, NULL); if (result == 0) { result = GetLastError(); error_msg = c_format("Rtmv2 pipe read error: %s", win_strerror(result)); return (XORP_ERROR); } message.resize(message.size() + nbytes); memcpy(&message[off], &buffer[0], nbytes); off += nbytes; if ((off - last_mh_off) < (ssize_t)(sizeof(u_short) + 2 * sizeof(u_char))) { error_msg = c_format("Rtmv2 pipe read failed: " "message truncated: " "received %d bytes instead of (at least) %u " "bytes", XORP_INT_CAST(nbytes), XORP_UINT_CAST(sizeof(u_short) + 2 * sizeof(u_char))); return (XORP_ERROR); } // // Received message (probably) OK // AlignData align_data(message); const struct if_msghdr* mh = align_data.payload(); XLOG_ASSERT(mh->ifm_msglen == message.size()); XLOG_ASSERT(mh->ifm_msglen == nbytes); last_mh_off = off; break; } XLOG_ASSERT(last_mh_off == message.size()); // // Notify observers // for (ObserverList::iterator i = _ol.begin(); i != _ol.end(); i++) { (*i)->routing_socket_data(message); } return (XORP_OK); } void WinRtmPipe::io_event(XorpFd fd, IoEventType type) { string error_msg; XLOG_ASSERT(fd == _fd); if (!_fd.is_valid()) { XLOG_ERROR("Error: io_event called when file descriptor is dead"); error_msg = "RTMv2 pipe disconnected"; stop(error_msg); } if (type == IOT_READ) { if (force_read(error_msg) != XORP_OK) { XLOG_ERROR("Error force_read() from RTMv2 pipe: %s", error_msg.c_str()); } } else if (type == IOT_DISCONNECT) { error_msg = "RTMv2 pipe disconnected"; stop(error_msg); } else { XLOG_UNREACHABLE(); } } #endif // !HOST_OS_WINDOWS // // Observe routing sockets activity // struct WinRtmPipePlumber { typedef WinRtmPipe::ObserverList ObserverList; static void plumb(WinRtmPipe& r, WinRtmPipeObserver* o) { ObserverList& ol = r._ol; ObserverList::iterator i = find(ol.begin(), ol.end(), o); debug_msg("Plumbing WinRtmPipeObserver %p to WinRtmPipe%p\n", o, &r); XLOG_ASSERT(i == ol.end()); ol.push_back(o); } static void unplumb(WinRtmPipe& r, WinRtmPipeObserver* o) { ObserverList& ol = r._ol; debug_msg("Unplumbing WinRtmPipeObserver %p from " "WinRtmPipe %p\n", o, &r); ObserverList::iterator i = find(ol.begin(), ol.end(), o); XLOG_ASSERT(i != ol.end()); ol.erase(i); } }; WinRtmPipeObserver::WinRtmPipeObserver(WinRtmPipe& rs) : _rs(rs) { WinRtmPipePlumber::plumb(rs, this); } WinRtmPipeObserver::~WinRtmPipeObserver() { WinRtmPipePlumber::unplumb(_rs, this); } WinRtmPipe& WinRtmPipeObserver::routing_socket() { return _rs; } xorp/fea/data_plane/control_socket/routing_socket.cc0000664000076400007640000002057611540225522023111 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include #ifdef HAVE_ROUTING_SOCKETS #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/utils.hh" #ifdef HAVE_SYS_TYPES_H #include #endif #include #ifdef HAVE_SYS_TIME_H #include #endif #ifdef HAVE_SYS_SOCKET_H #include #endif #ifdef HAVE_NET_IF_H #include #endif #ifdef HAVE_NET_ROUTE_H #include #endif #include "libcomm/comm_api.h" #include "routing_socket.hh" uint16_t RoutingSocket::_instance_cnt = 0; pid_t RoutingSocket::_pid = getpid(); // // Routing Sockets communication with the kernel // RoutingSocket::RoutingSocket(EventLoop& eventloop) : _eventloop(eventloop), _fd(-1), _seqno(0), _instance_no(_instance_cnt++) { } RoutingSocket::~RoutingSocket() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the routing socket: %s", error_msg.c_str()); } XLOG_ASSERT(_ol.empty()); } int RoutingSocket::start(int af, string& error_msg) { if (_fd >= 0) return (XORP_OK); // // Open the socket // _fd = socket(AF_ROUTE, SOCK_RAW, af); if (_fd < 0) { error_msg = c_format("Could not open routing socket: %s", strerror(errno)); return (XORP_ERROR); } // // Increase the receiving buffer size of the socket to avoid // loss of data from the kernel. // comm_sock_set_rcvbuf(_fd, SO_RCV_BUF_SIZE_MAX, SO_RCV_BUF_SIZE_MIN); // TODO: do we want to make the socket non-blocking? // // Add the socket to the event loop // if (_eventloop.add_ioevent_cb(_fd, IOT_READ, callback(this, &RoutingSocket::io_event)) == false) { error_msg = c_format("Failed to add routing socket to EventLoop"); close(_fd); _fd = -1; return (XORP_ERROR); } return (XORP_OK); } int RoutingSocket::stop(string& error_msg) { UNUSED(error_msg); if (_fd >= 0) { _eventloop.remove_ioevent_cb(_fd); close(_fd); _fd = -1; } return (XORP_OK); } ssize_t RoutingSocket::write(const void* data, size_t nbytes) { _seqno++; return ::write(_fd, data, nbytes); } int RoutingSocket::force_read(string& error_msg) { vector message; vector buffer(ROUTING_SOCKET_BYTES); size_t off = 0; size_t last_mh_off = 0; for ( ; ; ) { ssize_t got; // Find how much data is queued in the first message do { got = recv(_fd, &buffer[0], buffer.size(), MSG_DONTWAIT | MSG_PEEK); if ((got < 0) && (errno == EINTR)) continue; // XXX: the receive was interrupted by a signal if ((got < 0) || (got < (ssize_t)buffer.size())) break; // The buffer is big enough buffer.resize(buffer.size() + ROUTING_SOCKET_BYTES); } while (true); got = read(_fd, &buffer[0], buffer.size()); if (got < 0) { if (errno == EINTR) continue; error_msg = c_format("Routing socket read error: %s", strerror(errno)); return (XORP_ERROR); } message.resize(message.size() + got); memcpy(&message[off], &buffer[0], got); off += got; // // XXX: all messages received on routing sockets must start // with the the following three fields: // { // u_short foo_msglen; // u_char foo_version; // u_char foo_type; // ... // // Hence, the minimum length of a received message is: // sizeof(u_short) + 2 * sizeof(u_char) // if ((off - last_mh_off) < (ssize_t)(sizeof(u_short) + 2 * sizeof(u_char))) { error_msg = c_format("Routing socket read failed: " "message truncated: " "received %d bytes instead of (at least) %u " "bytes", XORP_INT_CAST(got), XORP_UINT_CAST(sizeof(u_short) + 2 * sizeof(u_char))); return (XORP_ERROR); } // // XXX: the routing sockets don't use multipart messages, hence // this should be the end of the message. // // // Received message (probably) OK // AlignData align_data(message); const struct if_msghdr* mh = align_data.payload(); XLOG_ASSERT(mh->ifm_msglen == message.size()); XLOG_ASSERT(mh->ifm_msglen == got); last_mh_off = off; break; } XLOG_ASSERT(last_mh_off == message.size()); // // Notify observers // for (ObserverList::iterator i = _ol.begin(); i != _ol.end(); i++) { (*i)->routing_socket_data(message); } return (XORP_OK); } void RoutingSocket::io_event(XorpFd fd, IoEventType type) { string error_msg; XLOG_ASSERT(fd == _fd); XLOG_ASSERT(type == IOT_READ); if (force_read(error_msg) != XORP_OK) { XLOG_ERROR("Error force_read() from routing socket: %s", error_msg.c_str()); } } // // Observe routing sockets activity // struct RoutingSocketPlumber { typedef RoutingSocket::ObserverList ObserverList; static void plumb(RoutingSocket& r, RoutingSocketObserver* o) { ObserverList& ol = r._ol; ObserverList::iterator i = find(ol.begin(), ol.end(), o); debug_msg("Plumbing RoutingSocketObserver %p to RoutingSocket%p\n", o, &r); XLOG_ASSERT(i == ol.end()); ol.push_back(o); } static void unplumb(RoutingSocket& r, RoutingSocketObserver* o) { ObserverList& ol = r._ol; debug_msg("Unplumbing RoutingSocketObserver %p from " "RoutingSocket %p\n", o, &r); ObserverList::iterator i = find(ol.begin(), ol.end(), o); XLOG_ASSERT(i != ol.end()); ol.erase(i); } }; RoutingSocketObserver::RoutingSocketObserver(RoutingSocket& rs) : _rs(rs) { RoutingSocketPlumber::plumb(rs, this); } RoutingSocketObserver::~RoutingSocketObserver() { RoutingSocketPlumber::unplumb(_rs, this); } RoutingSocket& RoutingSocketObserver::routing_socket() { return _rs; } RoutingSocketReader::RoutingSocketReader(RoutingSocket& rs) : RoutingSocketObserver(rs), _rs(rs), _cache_valid(false), _cache_seqno(0) { } RoutingSocketReader::~RoutingSocketReader() { } /** * Force the reader to receive data from the specified routing socket. * * @param rs the routing socket to receive the data from. * @param seqno the sequence number of the data to receive. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int RoutingSocketReader::receive_data(RoutingSocket& rs, uint32_t seqno, string& error_msg) { _cache_seqno = seqno; _cache_valid = false; while (_cache_valid == false) { if (rs.force_read(error_msg) != XORP_OK) return (XORP_ERROR); } return (XORP_OK); } /** * Receive data from the routing socket. * * Note that this method is called asynchronously when the routing socket * has data to receive, therefore it should never be called directly by * anything else except the routing socket facility itself. * * @param buffer the buffer with the received data. */ void RoutingSocketReader::routing_socket_data(const vector& buffer) { size_t d = 0, off = 0; pid_t my_pid = _rs.pid(); AlignData align_data(buffer); // // Copy data that has been requested to be cached by setting _cache_seqno // _cache_data.resize(buffer.size()); while (d < buffer.size()) { const struct rt_msghdr* rtm; rtm = align_data.payload_by_offset(d); if ((rtm->rtm_pid == my_pid) && (rtm->rtm_seq == (signed)_cache_seqno)) { XLOG_ASSERT(buffer.size() - d >= rtm->rtm_msglen); memcpy(&_cache_data[off], rtm, rtm->rtm_msglen); off += rtm->rtm_msglen; _cache_valid = true; } d += rtm->rtm_msglen; } // XXX: shrink _cache_data to contain only the data copied to it _cache_data.resize(off); } #endif // HAVE_ROUTING_SOCKETS xorp/fea/data_plane/control_socket/routing_socket.hh0000664000076400007640000001523411540225522023116 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __FEA_DATA_PLANE_CONTROL_SOCKET_ROUTING_SOCKET_HH__ #define __FEA_DATA_PLANE_CONTROL_SOCKET_ROUTING_SOCKET_HH__ #include #ifdef HAVE_ROUTING_SOCKETS #include "libxorp/eventloop.hh" #include "libxorp/exceptions.hh" class RoutingSocketObserver; struct RoutingSocketPlumber; /** * RoutingSocket class opens a routing socket and forwards data arriving * on the socket to RoutingSocketObservers. The RoutingSocket hooks itself * into the EventLoop and activity usually happens asynchronously. */ class RoutingSocket : public NONCOPYABLE { public: RoutingSocket(EventLoop& eventloop); ~RoutingSocket(); /** * Start the routing socket operation for a given address family. * * @param af the address family. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int start(int af, string& error_msg); /** * Start the routing socket operation for an unspecified address family. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int start(string& error_msg) { return start(AF_UNSPEC, error_msg); } /** * Stop the routing socket operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int stop(string& error_msg); /** * Test if the routing socket is open. * * This method is needed because RoutingSocket may fail to open * routing socket during startup. * * @return true if the routing socket is open, otherwise false. */ bool is_open() const { return _fd >= 0; } /** * Write data to routing socket. * * This method also updates the sequence number associated with * this routing socket. * * @return the number of bytes which were written, or -1 if error. */ ssize_t write(const void* data, size_t nbytes); /** * Get the sequence number for next message written into the kernel. * * The sequence number is derived from the instance number of this routing * socket and a 16-bit counter. * * @return the sequence number for the next message written into the * kernel. */ uint32_t seqno() const { return (_instance_no << 16 | _seqno); } /** * Get the cached process identifier value. * * @return the cached process identifier value. */ pid_t pid() const { return _pid; } /** * Force socket to read data. * * This usually is performed after writing a request that the * kernel will answer (e.g., after writing a route lookup). * Use sparingly, with caution, and at your own risk. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int force_read(string& error_msg); private: typedef list ObserverList; /** * Read data available for RoutingSocket and invoke * RoutingSocketObserver::routing_socket_data() on all observers of routing * socket. */ void io_event(XorpFd fd, IoEventType type); private: static const size_t ROUTING_SOCKET_BYTES = 8*1024; // Initial guess at msg size private: EventLoop& _eventloop; int _fd; ObserverList _ol; uint16_t _seqno; // Seqno of next write() uint16_t _instance_no; // Instance number of this routing socket static uint16_t _instance_cnt; static pid_t _pid; friend class RoutingSocketPlumber; // class that hooks observers in and out }; class RoutingSocketObserver { public: RoutingSocketObserver(RoutingSocket& rs); virtual ~RoutingSocketObserver(); /** * Receive data from the routing socket. * * Note that this method is called asynchronously when the routing socket * has data to receive, therefore it should never be called directly by * anything else except the routing socket facility itself. * * @param buffer the buffer with the received data. */ virtual void routing_socket_data(const vector& buffer) = 0; /** * Get RoutingSocket associated with Observer. */ RoutingSocket& routing_socket(); private: RoutingSocket& _rs; }; class RoutingSocketReader : public RoutingSocketObserver { public: RoutingSocketReader(RoutingSocket& rs); virtual ~RoutingSocketReader(); /** * Force the reader to receive data from the specified routing socket. * * @param rs the routing socket to receive the data from. * @param seqno the sequence number of the data to receive. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int receive_data(RoutingSocket& rs, uint32_t seqno, string& error_msg); /** * Get the buffer with the data that was received. * * @return a reference to the buffer with the data that was received. */ const vector& buffer() const { return (_cache_data); } /** * Receive data from the routing socket. * * Note that this method is called asynchronously when the routing socket * has data to receive, therefore it should never be called directly by * anything else except the routing socket facility itself. * * @param buffer the buffer with the received data. */ virtual void routing_socket_data(const vector& buffer); private: RoutingSocket& _rs; bool _cache_valid; // Cache data arrived. uint32_t _cache_seqno; // Seqno of routing socket data to // cache so reading via routing // socket can appear synchronous. vector _cache_data; // Cached routing socket data. }; #endif #endif // __FEA_DATA_PLANE_CONTROL_SOCKET_ROUTING_SOCKET_HH__ xorp/fea/data_plane/control_socket/windows_rtm_pipe.hh0000664000076400007640000001204611540224221023441 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/data_plane/control_socket/windows_rtm_pipe.hh,v 1.7 2008/10/02 21:56:54 bms Exp $ #ifndef __FEA_DATA_PLANE_CONTROL_SOCKET_WINDOWS_RTM_PIPE_HH__ #define __FEA_DATA_PLANE_CONTROL_SOCKET_WINDOWS_RTM_PIPE_HH__ #include #include "libxorp/eventloop.hh" #include "libxorp/exceptions.hh" class WinRtmPipeObserver; struct WinRtmPipePlumber; /** * WinRtmPipe class opens a routing socket and forwards data arriving * on the socket to WinRtmPipeObservers. The WinRtmPipe hooks itself * into the EventLoop and activity usually happens asynchronously. */ class WinRtmPipe { public: WinRtmPipe(EventLoop& eventloop); ~WinRtmPipe(); /** * Start the routing socket operation for a given address family. * * @param af the address family. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int start(int af, string& error_msg); /** * Stop the routing socket operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int stop(string& error_msg); /** * Test if the routing socket is open. * * This method is needed because WinRtmPipe may fail to open * routing socket during startup. * * @return true if the routing socket is open, otherwise false. */ bool is_open() const { return _fd.is_valid(); } /** * Write data to routing socket. * * This method also updates the sequence number associated with * this routing socket. * * @return the number of bytes which were written, or -1 if error. */ ssize_t write(const void* data, size_t nbytes); /** * Get the sequence number for next message written into the kernel. * * The sequence number is derived from the instance number of this routing * socket and a 16-bit counter. * * @return the sequence number for the next message written into the * kernel. */ uint32_t seqno() const { return (_instance_no << 16 | _seqno); } /** * Get the cached process identifier value. * * @return the cached process identifier value. */ pid_t pid() const { return _pid; } /** * Force socket to read data. * * This usually is performed after writing a request that the * kernel will answer (e.g., after writing a route lookup). * Use sparingly, with caution, and at your own risk. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int force_read(string& error_msg); private: typedef list ObserverList; /** * Read data available for WinRtmPipe and invoke * WinRtmPipeObserver::routing_socket_data() on all observers of routing * socket. */ void io_event(XorpFd fd, IoEventType type); WinRtmPipe& operator=(const WinRtmPipe&); // Not implemented WinRtmPipe(const WinRtmPipe&); // Not implemented private: static const size_t ROUTING_SOCKET_BYTES = 8*1024; // Initial guess at msg size private: EventLoop& _eventloop; XorpFd _fd; ObserverList _ol; uint16_t _seqno; // Seqno of next write() uint16_t _instance_no; // Instance number of this routing socket static uint16_t _instance_cnt; static pid_t _pid; friend class WinRtmPipePlumber; // class that hooks observers in and out }; class WinRtmPipeObserver { public: WinRtmPipeObserver(WinRtmPipe& rs); virtual ~WinRtmPipeObserver(); /** * Receive data from the routing socket. * * Note that this method is called asynchronously when the routing socket * has data to receive, therefore it should never be called directly by * anything else except the routing socket facility itself. * * @param buffer the buffer with the received data. */ virtual void routing_socket_data(const vector& buffer) = 0; /** * Get WinRtmPipe associated with Observer. */ WinRtmPipe& routing_socket(); private: WinRtmPipe& _rs; }; #endif // __FEA_DATA_PLANE_CONTROL_SOCKET_WINDOWS_RTM_PIPE_HH__ xorp/fea/data_plane/control_socket/netlink_socket_utilities.hh0000664000076400007640000001343211703345405025170 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2012 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __FEA_DATA_PLANE_CONTROL_SOCKET_NETLINK_SOCKET_UTILITIES_HH__ #define __FEA_DATA_PLANE_CONTROL_SOCKET_NETLINK_SOCKET_UTILITIES_HH__ #include #ifdef HAVE_NETLINK_SOCKETS #include "libxorp/xorp.h" #ifdef HAVE_LINUX_TYPES_H #include #endif #ifdef HAVE_LINUX_RTNETLINK_H #include #endif #include "fea/fte.hh" #include "fea/iftree.hh" // // Conditionally re-define some of the netlink-related macros that are not // defined properly and might generate alignment-related compilation // warning on some architectures (e.g, ARM/XScale) if we use // "-Wcast-align" compilation flag. // #ifdef HAVE_BROKEN_MACRO_NLMSG_NEXT #undef NLMSG_NEXT #define NLMSG_NEXT(nlh, len) ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), \ (struct nlmsghdr*)(void*)(((char*)(nlh)) + NLMSG_ALIGN((nlh)->nlmsg_len))) #endif #ifdef HAVE_BROKEN_MACRO_RTA_NEXT #undef RTA_NEXT #define RTA_NEXT(rta, attrlen) ((attrlen) -= RTA_ALIGN((rta)->rta_len), \ (struct rtattr*)(void*)(((char*)(rta)) + RTA_ALIGN((rta)->rta_len))) #endif #ifdef HAVE_BROKEN_MACRO_IFA_RTA #undef IFA_RTA #define IFA_RTA(r) ((struct rtattr*)(void*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg)))) #endif #ifdef HAVE_BROKEN_MACRO_IFLA_RTA #undef IFLA_RTA #define IFLA_RTA(r) ((struct rtattr*)(void*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifinfomsg)))) #endif #ifdef HAVE_BROKEN_MACRO_RTM_RTA #undef RTM_RTA #define RTM_RTA(r) ((struct rtattr*)(void *)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct rtmsg)))) #endif // // TODO: XXX: a temporary definition of RTPROT_XORP (e.g., in case of Linux) // that is used to mark the routes installed by XORP. // XXX: RTPROT_XORP must be unique // (see for definition of all RTPROT_* protocols). // #if defined(RTPROT_UNSPEC) && !defined(RTPROT_XORP) #define RTPROT_XORP 14 #endif class NetlinkSocket; class NetlinkSocketReader; class FibConfig; /** * @short Helper class for various NETLINK-format related utilities. */ class NlmUtils { public: /** * Convert a message type from netlink socket message into * human-readable form. * * @param m message type from netlink socket message. * @return human-readable message of the message type. */ static string nlm_msg_type(uint32_t m); static string nlm_print_msg(const vector& message); /** * Get pointers to set of netlink rtattr entries. * * @param rtattr the pointer to the first rtattr entry. * @param rta_len the length of all rtattr entries. * @param rta_array the array with the pointers to store the result. * @param rta_array_n the maximum number of entries to store * in the array. */ static void get_rtattr(const struct rtattr* rtattr, int rta_len, const struct rtattr* rta_array[], size_t rta_array_n); /** * Extract the routing information from netlink message. * * @param iftree the interface tree to use. * @param fte the return-by-reference @ref FteX entry to return the result. * @param nlh the netlink message header. * @param rtmsg the routing message. * @param rta_len the routing message payload. * @return XORP_OK on success, otherwise XORP_ERROR. */ static int nlm_get_to_fte_cfg(const IfTree& iftree, FteX& fte, const struct nlmsghdr* nlh, const struct rtmsg* rtmsg, int rta_len, const FibConfig& fibconfig, string& err_msg); /** * Check that a previous netlink request has succeeded. * * @param ns_reader the NetlinkSocketReader to use for reading data. * @param ns the NetlinkSocket to use for reading data. * @param seqno the sequence nomer of the netlink request to check for. * @param last_errno the last error number (if error). * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ static int check_netlink_request(NetlinkSocketReader& ns_reader, NetlinkSocket& ns, uint32_t seqno, int& last_errno, string& error_msg); static int nlm_decode_ipvx_address(int family, const struct rtattr* rtattr, IPvX& ipvx_addr, bool& is_set, string& error_msg); static int nlm_decode_ipvx_interface_address(const struct ifinfomsg* ifinfomsg, const struct rtattr* rtattr, IPvX& ipvx_addr, bool& is_set, string& error_msg); static void nlm_cond_newlink_to_fea_cfg(const IfTree& user_cfg, IfTree& iftree, const struct ifinfomsg* ifinfomsg, int rta_len, bool& modified); static void nlm_dellink_to_fea_cfg(IfTree& iftree, const struct ifinfomsg* ifinfomsg, int rta_len, bool& modified); static void nlm_cond_newdeladdr_to_fea_cfg(const IfTree& user_config, IfTree& iftree, const struct ifaddrmsg* ifaddrmsg, int rta_len, bool is_deleted, bool& modified); }; #endif #endif // __FEA_DATA_PLANE_CONTROL_SOCKET_NETLINK_SOCKET_UTILITIES_HH__ xorp/fea/data_plane/firewall/0000775000076400007640000000000011540224222016302 5ustar greearbgreearbxorp/fea/data_plane/firewall/firewall_get_pf.cc0000664000076400007640000002163111421137511021747 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #ifdef HAVE_FCNTL_H #include #endif #ifdef HAVE_SYS_IOCTL_H #include #endif #ifdef HAVE_NET_IF_H #include #endif #ifdef HAVE_NET_PFVAR_H #include #endif #ifdef HAVE_NETINET_IN_H #include #endif #include "fea/firewall_manager.hh" #include "firewall_get_pf.hh" // // Get information about firewall entries from the underlying system. // // The mechanism to obtain the information is PF. // #ifdef HAVE_FIREWALL_PF const string FirewallGetPf::_pf_device_name = "/dev/pf"; FirewallGetPf::FirewallGetPf(FeaDataPlaneManager& fea_data_plane_manager) : FirewallGet(fea_data_plane_manager), _fd(-1) { } FirewallGetPf::~FirewallGetPf() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the PF mechanism to get " "information about firewall entries from the underlying " "system: %s", error_msg.c_str()); } } int FirewallGetPf::start(string& error_msg) { if (_is_running) return (XORP_OK); // // Open the PF device // _fd = open(_pf_device_name.c_str(), O_RDONLY); if (_fd < 0) { error_msg = c_format("Could not open device %s for PF firewall: %s", _pf_device_name.c_str(), strerror(errno)); return (XORP_ERROR); } _is_running = true; return (XORP_OK); } int FirewallGetPf::stop(string& error_msg) { if (! _is_running) return (XORP_OK); if (close(_fd) < 0) { error_msg = c_format("Could not close device %s for PF firewall: %s", _pf_device_name.c_str(), strerror(errno)); _fd = -1; return (XORP_ERROR); } _fd = -1; _is_running = false; return (XORP_OK); } int FirewallGetPf::get_table4(list& firewall_entry_list, string& error_msg) { return (get_table(AF_INET, firewall_entry_list, error_msg)); } int FirewallGetPf::get_table6(list& firewall_entry_list, string& error_msg) { #ifdef HAVE_IPV6 return (get_table(AF_INET6, firewall_entry_list, error_msg)); #else error_msg = c_format("Cannot get the IPv6 firewall table: " "IPv6 is not supported"); return (XORP_ERROR); #endif } int FirewallGetPf::get_table(int family, list& firewall_entry_list, string& error_msg) { struct pfioc_rule pr; uint32_t nr, rules_n; // // Get a ticket and find out how many rules there are // memset(&pr, 0, sizeof(pr)); pr.rule.action = PF_PASS; if (ioctl(_fd, DIOCGETRULES, &pr) < 0) { error_msg = c_format("Could not get the PF firewall table: %s", strerror(errno)); return (XORP_ERROR); } rules_n = pr.nr; // // Get all rules // for (nr = 0; nr < rules_n; nr++) { pr.nr = nr; if (ioctl(_fd, DIOCGETRULE, &pr) < 0) { error_msg = c_format("Could not get entry %u from the PF firewall " "table: %s", nr, strerror(errno)); return (XORP_ERROR); } // // Ignore entries that don't match the address family // if (pr.rule.af != family) continue; // // XXX: Consider only network prefix entries // if ((pr.rule.src.addr.type != PF_ADDR_ADDRMASK) || (pr.rule.src.addr.type != PF_ADDR_ADDRMASK)) { continue; } // // Extract various information from the rule // uint32_t rule_number = FirewallEntry::RULE_NUMBER_DEFAULT; string ifname, vifname; IPvXNet src_network = IPvXNet(family); IPvXNet dst_network = IPvXNet(family); uint32_t ip_protocol = FirewallEntry::IP_PROTOCOL_ANY; uint32_t src_port_begin = FirewallEntry::PORT_MIN; uint32_t src_port_end = FirewallEntry::PORT_MAX; uint32_t dst_port_begin = FirewallEntry::PORT_MIN; uint32_t dst_port_end = FirewallEntry::PORT_MAX; FirewallEntry::Action action = FirewallEntry::ACTION_INVALID; bool is_ignored = false; // // Get the rule number // // XXX: Do nothing, because PF doesn't have rule numbers // // Get the action // switch (pr.rule.action) { case PF_PASS: action = FirewallEntry::ACTION_PASS; break; case PF_DROP: action = FirewallEntry::ACTION_DROP; break; default: XLOG_WARNING("Ingoring firewall entry with action %u: " "unknown action", pr.rule.action); break; } if (action == FirewallEntry::ACTION_INVALID) continue; // // Get the protocol // if (pr.rule.proto != 0) ip_protocol = pr.rule.proto; // // Get the source and destination network addresses // IPvX src_addr(family), src_mask(family); IPvX dst_addr(family), dst_mask(family); src_addr.copy_in(family, pr.rule.src.addr.v.a.addr.addr8); src_mask.copy_in(family, pr.rule.src.addr.v.a.mask.addr8); dst_addr.copy_in(family, pr.rule.dst.addr.v.a.addr.addr8); dst_mask.copy_in(family, pr.rule.dst.addr.v.a.mask.addr8); src_network = IPvXNet(src_addr, src_mask.mask_len()); dst_network = IPvXNet(dst_addr, dst_mask.mask_len()); // // Get the source port number range // is_ignored = false; switch (pr.rule.src.port_op) { case PF_OP_NONE: // No-op break; case PF_OP_RRG: // Range including boundaries operator: ":" src_port_begin = pr.rule.src.port[0]; src_port_end = pr.rule.src.port[1]; break; case PF_OP_IRG: // Range excluding boundaries operator: "><" src_port_begin = pr.rule.src.port[0] + 1; src_port_end = pr.rule.src.port[1] - 1; break; case PF_OP_EQ: // Equal operator: "=" src_port_begin = pr.rule.src.port[0]; src_port_end = src_port_begin; break; case PF_OP_LT: // Less-than operator: "<" src_port_end = pr.rule.src.port[0] - 1; break; case PF_OP_LE: // Less-than or equal operator: "<=" src_port_end = pr.rule.src.port[0]; break; case PF_OP_GT: // Greather-than operator: ">" src_port_begin = pr.rule.src.port[0] + 1; break; case PF_OP_GE: // Greather-than or equal operator: ">=" src_port_begin = pr.rule.src.port[0]; break; case PF_OP_NE: // Unequal operator: "!=" // FALLTHROUGH case PF_OP_XRG: // Except range: <> // FALLTHROUGH default: XLOG_WARNING("Ingoring firewall entry with source port range " "operator %u: unknown operator", pr.rule.src.port_op); is_ignored = true; break; } if (is_ignored) continue; // // Get the destination port number range // is_ignored = false; switch (pr.rule.dst.port_op) { case PF_OP_NONE: // No-op break; case PF_OP_RRG: // Range including boundaries operator: ":" dst_port_begin = pr.rule.dst.port[0]; dst_port_end = pr.rule.dst.port[1]; break; case PF_OP_IRG: // Range excluding boundaries operator: "><" dst_port_begin = pr.rule.dst.port[0] + 1; dst_port_end = pr.rule.dst.port[1] - 1; break; case PF_OP_EQ: // Equal operator: "=" dst_port_begin = pr.rule.dst.port[0]; dst_port_end = dst_port_begin; break; case PF_OP_LT: // Less-than operator: "<" dst_port_end = pr.rule.dst.port[0] - 1; break; case PF_OP_LE: // Less-than or equal operator: "<=" dst_port_end = pr.rule.dst.port[0]; break; case PF_OP_GT: // Greather-than operator: ">" dst_port_begin = pr.rule.dst.port[0] + 1; break; case PF_OP_GE: // Greather-than or equal operator: ">=" dst_port_begin = pr.rule.dst.port[0]; break; case PF_OP_NE: // Unequal operator: "!=" // FALLTHROUGH case PF_OP_XRG: // Except range: <> // FALLTHROUGH default: XLOG_WARNING("Ingoring firewall entry with destination port range " "operator %u: unknown operator", pr.rule.dst.port_op); is_ignored = true; break; } if (is_ignored) continue; // // The interface and vif // if (pr.rule.ifname[0] != '\0') { ifname = string(pr.rule.ifname); vifname = ifname; // XXX: ifname == vifname } // Add the entry to the list FirewallEntry firewall_entry(rule_number, ifname, vifname, src_network, dst_network, ip_protocol, src_port_begin, src_port_end, dst_port_begin, dst_port_end, action); firewall_entry_list.push_back(firewall_entry); } return (XORP_OK); } #endif // HAVE_FIREWALL_PF xorp/fea/data_plane/firewall/firewall_set_pf.hh0000664000076400007640000001516611540224222022001 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/data_plane/firewall/firewall_set_pf.hh,v 1.5 2008/10/02 21:57:03 bms Exp $ #ifndef __FEA_DATA_PLANE_FIREWALL_FIREWALL_SET_PF_HH__ #define __FEA_DATA_PLANE_FIREWALL_FIREWALL_SET_PF_HH__ #include "fea/firewall_set.hh" class FirewallSetPf : public FirewallSet { public: // Firewall entries trie indexed by rule number typedef map FirewallTrie; /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ FirewallSetPf(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~FirewallSetPf(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg); /** * Update the firewall entries by pushing them into the underlying system. * * @param added_entries the entries to add. * @param replaced_entries the entries to replace. * @param deleted_entries the deleted entries. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int update_entries(const list& added_entries, const list& replaced_entries, const list& deleted_entries, string& error_msg); /** * Set the IPv4 firewall table. * * @param firewall_entry_list the list with all entries to install into * the IPv4 firewall table. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int set_table4(const list& firewall_entry_list, string& error_msg); /** * Delete all entries in the IPv4 firewall table. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int delete_all_entries4(string& error_msg); /** * Set the IPv6 firewall table. * * @param firewall_entry_list the list with all entries to install into * the IPv6 firewall table. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int set_table6(const list& firewall_entry_list, string& error_msg); /** * Delete all entries in the IPv6 firewall table. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int delete_all_entries6(string& error_msg); private: /** * Add a single firewall entry. * * @param firewall_entry the entry to add. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int add_entry(const FirewallEntry& firewall_entry, string& error_msg); /** * Replace a single firewall entry. * * @param firewall_entry the replacement entry. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int replace_entry(const FirewallEntry& firewall_entry, string& error_msg); /** * Delete a single firewall entry. * * @param firewall_entry the entry to delete. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int delete_entry(const FirewallEntry& firewall_entry, string& error_msg); /** * Start a transaction for manipulating the firewall table. * * @param ticket return-by-reference ticket for the transaction. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int start_transaction(uint32_t& ticket, string& error_msg); /** * Commit a transaction for manipulating the firewall table. * * @param ticket the ticket for the transaction. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int commit_transaction(uint32_t ticket, string& error_msg); /** * Abort a transaction for manipulating the firewall table. * * @param ticket the ticket for the transaction. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int abort_transaction(uint32_t ticket, string& error_msg); /** * Push all locally stored firewall entries. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int push_entries(string& error_msg); /** * Add or delete a single firewall entry as part of a transaction. * * @param is_add if true, then add the entry, otherwise delete it. * @param ticket the ticket for the transaction. * @param firewall_entry the entry to add. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_delete_transaction_entry(bool is_add, uint32_t ticket, const FirewallEntry& firewall_entry, string& error_msg); int _fd; // The file descriptor for firewall access // The locally saved firewall entries FirewallTrie _firewall_entries4; FirewallTrie _firewall_entries6; static const string _pf_device_name; // The PF device name }; #endif // __FEA_DATA_PLANE_FIREWALL_FIREWALL_SET_PF_HH__ xorp/fea/data_plane/firewall/firewall_get_dummy.cc0000664000076400007640000000660611421137511022502 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "fea/firewall_manager.hh" #include "firewall_get_dummy.hh" #include "firewall_set_dummy.hh" // // Get information about firewall entries from the underlying system. // // The mechanism to obtain the information is Dummy (for testing purpose). // FirewallGetDummy::FirewallGetDummy(FeaDataPlaneManager& fea_data_plane_manager) : FirewallGet(fea_data_plane_manager) { } FirewallGetDummy::~FirewallGetDummy() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the Dummy mechanism to get " "information about firewall entries from the underlying " "system: %s", error_msg.c_str()); } } int FirewallGetDummy::start(string& error_msg) { UNUSED(error_msg); if (_is_running) return (XORP_OK); _is_running = true; return (XORP_OK); } int FirewallGetDummy::stop(string& error_msg) { UNUSED(error_msg); if (! _is_running) return (XORP_OK); _is_running = false; return (XORP_OK); } int FirewallGetDummy::get_table4(list& firewall_entry_list, string& error_msg) { FirewallSetDummy* firewall_set_dummy; FirewallSetDummy::FirewallTrie::const_iterator iter; firewall_set_dummy = dynamic_cast(fea_data_plane_manager().firewall_set()); if (firewall_set_dummy == NULL) { error_msg = c_format("Firewall plugin mismatch: expected " "Dummy firewall set plugin mot found"); return (XORP_ERROR); } for (iter = firewall_set_dummy->firewall_entries4().begin(); iter != firewall_set_dummy->firewall_entries4().end(); ++iter) { const FirewallEntry& firewall_entry = iter->second; firewall_entry_list.push_back(firewall_entry); } return (XORP_OK); } int FirewallGetDummy::get_table6(list& firewall_entry_list, string& error_msg) { FirewallSetDummy* firewall_set_dummy; FirewallSetDummy::FirewallTrie::const_iterator iter; firewall_set_dummy = dynamic_cast(fea_data_plane_manager().firewall_set()); if (firewall_set_dummy == NULL) { error_msg = c_format("Firewall plugin mismatch: expected " "Dummy firewall set plugin mot found"); return (XORP_ERROR); } for (iter = firewall_set_dummy->firewall_entries6().begin(); iter != firewall_set_dummy->firewall_entries6().end(); ++iter) { const FirewallEntry& firewall_entry = iter->second; firewall_entry_list.push_back(firewall_entry); } return (XORP_OK); } xorp/fea/data_plane/firewall/firewall_get_netfilter.hh0000664000076400007640000000651611421137511023355 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/data_plane/firewall/firewall_get_netfilter.hh,v 1.3 2008/10/02 21:57:03 bms Exp $ #ifndef __FEA_DATA_PLANE_FIREWALL_FIREWALL_GET_NETFILTER_HH__ #define __FEA_DATA_PLANE_FIREWALL_FIREWALL_GET_NETFILTER_HH__ #include "fea/firewall_get.hh" class FirewallGetNetfilter : public FirewallGet { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ FirewallGetNetfilter(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~FirewallGetNetfilter(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg); /** * Obtain the IPv4 firewall table. * * @param firewall_entry_list the return-by-reference list with all * entries in the IPv4 firewall table. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int get_table4(list& firewall_entry_list, string& error_msg); /** * Obtain the IPv6 firewall table. * * @param firewall_entry_list the return-by-reference list with all * entries in the IPv6 firewall table. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int get_table6(list& firewall_entry_list, string& error_msg); private: /** * Obtain the firewall table for a specific address family. * * @param family the address family. * @param firewall_entry_list the return-by-reference list with all * entries in the firewall table for the given address family. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int get_table(int family, list& firewall_entry_list, string& error_msg); int _s4; // The socket for IPv4 firewall access int _s6; // The socket for IPv6 firewall access static const string _netfilter_table_name; // The NETFILTER table name }; #endif // __FEA_DATA_PLANE_FIREWALL_FIREWALL_GET_NETFILTER_HH__ xorp/fea/data_plane/firewall/firewall_get_dummy.hh0000664000076400007640000000523711421137511022513 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/data_plane/firewall/firewall_get_dummy.hh,v 1.3 2008/10/02 21:57:02 bms Exp $ #ifndef __FEA_DATA_PLANE_FIREWALL_FIREWALL_GET_DUMMY_HH__ #define __FEA_DATA_PLANE_FIREWALL_FIREWALL_GET_DUMMY_HH__ #include "fea/firewall_get.hh" class FirewallGetDummy : public FirewallGet { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ FirewallGetDummy(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~FirewallGetDummy(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg); /** * Obtain the IPv4 firewall table. * * @param firewall_entry_list the return-by-reference list with all * entries in the IPv4 firewall table. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int get_table4(list& firewall_entry_list, string& error_msg); /** * Obtain the IPv6 firewall table. * * @param firewall_entry_list the return-by-reference list with all * entries in the IPv6 firewall table. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int get_table6(list& firewall_entry_list, string& error_msg); private: }; #endif // __FEA_DATA_PLANE_FIREWALL_FIREWALL_GET_DUMMY_HH__ xorp/fea/data_plane/firewall/firewall_set_ipfw2.cc0000664000076400007640000003210011421137511022376 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libcomm/comm_api.h" #ifdef HAVE_SYS_SYSCTL_H #include #endif #ifdef HAVE_SYS_SOCKET_H #include #endif #ifdef HAVE_SYS_SOCKIO_H #include #endif #ifdef HAVE_NET_IF_H #include #endif #ifdef HAVE_NETINET_IN_SYSTM_H #include #endif #ifdef HAVE_NETINET_IP_H #include #endif // // XXX: RELENG_4 branches of FreeBSD after RELENG_4_9_RELEASE require // that IPFW2 support be explicitly requested by defining the // preprocessor symbol IPFW2 to a non-zero value. // #define IPFW2 1 #ifdef HAVE_NETINET_IP_FW_H #include #endif #include "fea/firewall_manager.hh" #include "firewall_set_ipfw2.hh" // // Set firewall information into the underlying system. // // The mechanism to set the information is IPFW2. // #ifdef HAVE_FIREWALL_IPFW2 FirewallSetIpfw2::FirewallSetIpfw2(FeaDataPlaneManager& fea_data_plane_manager) : FirewallSet(fea_data_plane_manager), _s4(-1), _saved_autoinc_step(0) { } FirewallSetIpfw2::~FirewallSetIpfw2() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the IPFW2 mechanism to set " "firewall information into the underlying system: %s", error_msg.c_str()); } } int FirewallSetIpfw2::start(string& error_msg) { uint32_t step; size_t step_size = sizeof(step); if (_is_running) return (XORP_OK); // // Retrieve the auto-increment step size. This only exists in IPFW2 // and is used to tell it apart from IPFW1, as well as telling if IPFW1 // is loaded at runtime. // if (sysctlbyname("net.inet.ip.fw.autoinc_step", &step, &step_size, NULL, 0) < 0) { error_msg = c_format("Could not get autoinc value and start " "IPFW2 firewall: %s", strerror(errno)); return (XORP_ERROR); } _saved_autoinc_step = step; // // Open a raw IPv4 socket that IPFW2 uses for communication // _s4 = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); if (_s4 < 0) { error_msg = c_format("Could not open a raw socket for IPFW2 firewall: " "%s", strerror(errno)); return (XORP_ERROR); } // // Set autoinc step to 1, so that XORP rules being pushed can be done // with no rule number book-keeping. // step = 1; if (sysctlbyname("net.inet.ip.fw.autoinc_step", NULL, NULL, reinterpret_cast(&step), step_size) < 0) { error_msg = c_format("Could not set autoinc value and start " "IPFW2 firewall: %s", strerror(errno)); comm_close(_s4); _s4 = -1; return (XORP_ERROR); } _is_running = true; return (XORP_OK); } int FirewallSetIpfw2::stop(string& error_msg) { if (! _is_running) return (XORP_OK); if (comm_close(_s4) != XORP_OK) { error_msg = c_format("Could not close raw socket for IPFW2 " "firewall: %s", strerror(errno)); _s4 = -1; return (XORP_ERROR); } _s4 = -1; _is_running = false; return (XORP_OK); } int FirewallSetIpfw2::update_entries(const list& added_entries, const list& replaced_entries, const list& deleted_entries, string& error_msg) { list::const_iterator iter; // // The entries to add // for (iter = added_entries.begin(); iter != added_entries.end(); ++iter) { const FirewallEntry& firewall_entry = *iter; if (add_entry(firewall_entry, error_msg) != XORP_OK) return (XORP_ERROR); } // // The entries to replace // for (iter = replaced_entries.begin(); iter != replaced_entries.end(); ++iter) { const FirewallEntry& firewall_entry = *iter; if (replace_entry(firewall_entry, error_msg) != XORP_OK) return (XORP_ERROR); } // // The entries to delete // for (iter = deleted_entries.begin(); iter != deleted_entries.end(); ++iter) { const FirewallEntry& firewall_entry = *iter; if (delete_entry(firewall_entry, error_msg) != XORP_OK) return (XORP_ERROR); } return (XORP_OK); } int FirewallSetIpfw2::set_table4(const list& firewall_entry_list, string& error_msg) { list empty_list; if (delete_all_entries4(error_msg) != XORP_OK) return (XORP_ERROR); return (update_entries(firewall_entry_list, empty_list, empty_list, error_msg)); } int FirewallSetIpfw2::delete_all_entries4(string& error_msg) { // // Delete all entries // if (setsockopt(_s4, IPPROTO_IP, IP_FW_FLUSH, NULL, 0) < 0) { error_msg = c_format("Could not delete all IPFW2 firewall entries: %s", strerror(errno)); return (XORP_ERROR); } return (XORP_OK); } int FirewallSetIpfw2::set_table6(const list& firewall_entry_list, string& error_msg) { list empty_list; if (delete_all_entries6(error_msg) != XORP_OK) return (XORP_ERROR); return (update_entries(firewall_entry_list, empty_list, empty_list, error_msg)); } int FirewallSetIpfw2::delete_all_entries6(string& error_msg) { // // Delete all entries // if (setsockopt(_s4, IPPROTO_IP, IP_FW_FLUSH, NULL, 0) < 0) { error_msg = c_format("Could not delete all IPFW2 firewall entries: %s", strerror(errno)); return (XORP_ERROR); } return (XORP_OK); } int FirewallSetIpfw2::add_entry(const FirewallEntry& firewall_entry, string& error_msg) { uint32_t rulebuf[255]; uint32_t* rulep; // // Layout of buffer: // // +------- start --------+ <- &rulebuf[0] // | struct ip_fw | // +---- match opcodes ---+ // | O_VIA | // | O_PROTO | // | O_IP_SRC_MASK | // | O_IP_DST_MASK | // | O_IP_SRCPORT | // | O_IP_DSTPORT | // +--- action opcode(s)--+ // | O_ACCEPT or O_DENY | // +----------------------+ // | free space | // +-------- end ---------+ // // // Micro-assemble an IPFW2 instruction sequence // struct ip_fw* ip_fw = reinterpret_cast(&rulebuf[0]); rulep = &rulebuf[0]; memset(rulebuf, 0, sizeof(rulebuf)); ip_fw->rulenum = firewall_entry.rule_number(); // // Advance past the rule structure we instantiate first in vector space // rulep = reinterpret_cast(ip_fw->cmd); // // Begin adding match opcodes // // // Add the interface match opcode // // XXX: On this platform, ifname == vifname. // if (! firewall_entry.vifname().empty()) { ipfw_insn_if* ifinsp = reinterpret_cast(rulep); ifinsp->o.opcode = O_VIA; strlcpy(ifinsp->name, firewall_entry.vifname().c_str(), sizeof(ifinsp->name)); ifinsp->o.len |= F_INSN_SIZE(ipfw_insn_if); rulep += F_INSN_SIZE(ipfw_insn_if); } // // Add the protocol match opcode // if (firewall_entry.ip_protocol() != FirewallEntry::IP_PROTOCOL_ANY) { ipfw_insn* pinsp = reinterpret_cast(rulep); pinsp->opcode = O_PROTO; pinsp->len |= F_INSN_SIZE(ipfw_insn); pinsp->arg1 = firewall_entry.ip_protocol(); rulep += F_INSN_SIZE(ipfw_insn); } // // Add a match source address and mask instruction (mandatory) // if (firewall_entry.is_ipv4()) { ipfw_insn_ip* srcinsp = reinterpret_cast(rulep); srcinsp->o.opcode = O_IP_SRC_MASK; srcinsp->o.len |= F_INSN_SIZE(ipfw_insn_ip); firewall_entry.src_network().masked_addr().copy_out(srcinsp->addr); uint32_t prefix_len = firewall_entry.src_network().prefix_len(); IPv4 prefix = IPv4::make_prefix(prefix_len); prefix.copy_out(srcinsp->mask); rulep += F_INSN_SIZE(ipfw_insn_ip); } if (firewall_entry.is_ipv6()) { ipfw_insn_ip6* srcinsp = reinterpret_cast(rulep); srcinsp->o.opcode = O_IP6_SRC_MASK; srcinsp->o.len |= F_INSN_SIZE(ipfw_insn_ip6); firewall_entry.src_network().masked_addr().copy_out(srcinsp->addr6); uint32_t prefix_len = firewall_entry.src_network().prefix_len(); IPv6 prefix = IPv6::make_prefix(prefix_len); prefix.copy_out(srcinsp->mask6); rulep += F_INSN_SIZE(ipfw_insn_ip6); } // // Add a match destination address and mask instruction (mandatory) // if (firewall_entry.is_ipv4()) { ipfw_insn_ip* dstinsp = reinterpret_cast(rulep); dstinsp->o.opcode = O_IP_DST_MASK; dstinsp->o.len |= F_INSN_SIZE(ipfw_insn_ip); firewall_entry.dst_network().masked_addr().copy_out(dstinsp->addr); uint32_t prefix_len = firewall_entry.dst_network().prefix_len(); IPv4 prefix = IPv4::make_prefix(prefix_len); prefix.copy_out(dstinsp->mask); rulep += F_INSN_SIZE(ipfw_insn_ip); } if (firewall_entry.is_ipv6()) { ipfw_insn_ip6* dstinsp = reinterpret_cast(rulep); dstinsp->o.opcode = O_IP6_DST_MASK; dstinsp->o.len |= F_INSN_SIZE(ipfw_insn_ip6); firewall_entry.dst_network().masked_addr().copy_out(dstinsp->addr6); uint32_t prefix_len = firewall_entry.dst_network().prefix_len(); IPv6 prefix = IPv6::make_prefix(prefix_len); prefix.copy_out(dstinsp->mask6); rulep += F_INSN_SIZE(ipfw_insn_ip6); } // // Insert port match instructions, if required. // if ((firewall_entry.ip_protocol() == IPPROTO_TCP) || (firewall_entry.ip_protocol() == IPPROTO_UDP)) { // Add source port match instruction { ipfw_insn_u16* spinsp = (ipfw_insn_u16 *)rulep; spinsp->o.opcode = O_IP_SRCPORT; spinsp->o.len = F_INSN_SIZE(ipfw_insn_u16); spinsp->ports[0] = firewall_entry.src_port_begin(); spinsp->ports[1] = firewall_entry.src_port_end(); rulep += F_INSN_SIZE(ipfw_insn_u16); } // Add destination port match instruction { ipfw_insn_u16* dpinsp = (ipfw_insn_u16 *)rulep; dpinsp->o.opcode = O_IP_DSTPORT; dpinsp->o.len = F_INSN_SIZE(ipfw_insn_u16); dpinsp->ports[0] = firewall_entry.dst_port_begin(); dpinsp->ports[1] = firewall_entry.dst_port_end(); rulep += F_INSN_SIZE(ipfw_insn_u16); } } // // Add the appropriate action instruction // { // Fill out offset of action in 32-bit words ip_fw->act_ofs = (reinterpret_cast(rulep) - reinterpret_cast(ip_fw->cmd)) / sizeof(uint32_t); ipfw_insn* actinsp = reinterpret_cast(rulep); switch (firewall_entry.action()) { case FirewallEntry::ACTION_PASS: actinsp->opcode = O_ACCEPT; break; case FirewallEntry::ACTION_DROP: actinsp->opcode = O_DENY; break; case FirewallEntry::ACTION_REJECT: actinsp->opcode = O_REJECT; break; case FirewallEntry::ACTION_ANY: case FirewallEntry::ACTION_NONE: actinsp->opcode = O_NOP; break; default: XLOG_FATAL("Unknown firewall entry action code: %u", firewall_entry.action()); break; } actinsp->len |= F_INSN_SIZE(ipfw_insn); rulep += F_INSN_SIZE(ipfw_insn); } // // Compute and store the number of 32-bit words in ip_fw.cmd_len // ip_fw->cmd_len = (reinterpret_cast(rulep) - reinterpret_cast(ip_fw->cmd)) / sizeof(uint32_t); // // Compute the total size, in bytes, of the rule structure // socklen_t size_used = (reinterpret_cast(rulep) - reinterpret_cast(&rulebuf[0])); // // Install the entry // if (getsockopt(_s4, IPPROTO_IP, IP_FW_ADD, &rulebuf[0], &size_used) < 0) { error_msg = c_format("Could not set IPFW2 firewall entry: %s", strerror(errno)); return (XORP_ERROR); } return (XORP_OK); } int FirewallSetIpfw2::replace_entry(const FirewallEntry& firewall_entry, string& error_msg) { string dummy_error_msg; // // XXX: The replacement semantic is implemented by deleting and // then adding the entry. // delete_entry(firewall_entry, dummy_error_msg); return (add_entry(firewall_entry, error_msg)); } int FirewallSetIpfw2::delete_entry(const FirewallEntry& firewall_entry, string& error_msg) { uint32_t rulenum = firewall_entry.rule_number(); // // Delete the entry // if (setsockopt(_s4, IPPROTO_IP, IP_FW_DEL, &rulenum, sizeof(rulenum)) < 0) { error_msg = c_format("Could not delete IPFW2 firewall entry: %s", strerror(errno)); return (XORP_ERROR); } return (XORP_OK); } #endif // HAVE_FIREWALL_IPFW2 xorp/fea/data_plane/firewall/firewall_set_dummy.hh0000664000076400007640000001246111540224222022522 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/data_plane/firewall/firewall_set_dummy.hh,v 1.5 2008/10/02 21:57:03 bms Exp $ #ifndef __FEA_DATA_PLANE_FIREWALL_FIREWALL_SET_DUMMY_HH__ #define __FEA_DATA_PLANE_FIREWALL_FIREWALL_SET_DUMMY_HH__ #include "fea/firewall_set.hh" class FirewallSetDummy : public FirewallSet { public: // Firewall entries trie indexed by rule number typedef map FirewallTrie; /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ FirewallSetDummy(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~FirewallSetDummy(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg); /** * Update the firewall entries by pushing them into the underlying system. * * @param added_entries the entries to add. * @param replaced_entries the entries to replace. * @param deleted_entries the deleted entries. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int update_entries(const list& added_entries, const list& replaced_entries, const list& deleted_entries, string& error_msg); /** * Set the IPv4 firewall table. * * @param firewall_entry_list the list with all entries to install into * the IPv4 firewall table. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int set_table4(const list& firewall_entry_list, string& error_msg); /** * Delete all entries in the IPv4 firewall table. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int delete_all_entries4(string& error_msg); /** * Set the IPv6 firewall table. * * @param firewall_entry_list the list with all entries to install into * the IPv6 firewall table. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int set_table6(const list& firewall_entry_list, string& error_msg); /** * Delete all entries in the IPv6 firewall table. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int delete_all_entries6(string& error_msg); /** * Get a reference to the trie with the IPv4 firewall entries. * * @return a reference to the trie with the IPv4 firewall entries. */ FirewallTrie& firewall_entries4() { return _firewall_entries4; } /** * Get a reference to the trie with the IPv6 firewall entries. * * @return a reference to the trie with the IPv6 firewall entries. */ FirewallTrie& firewall_entries6() { return _firewall_entries6; } private: /** * Add a single firewall entry. * * @param firewall_entry the entry to add. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int add_entry(const FirewallEntry& firewall_entry, string& error_msg); /** * Replace a single firewall entry. * * @param firewall_entry the replacement entry. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int replace_entry(const FirewallEntry& firewall_entry, string& error_msg); /** * Delete a single firewall entry. * * @param firewall_entry the entry to delete. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int delete_entry(const FirewallEntry& firewall_entry, string& error_msg); // The locally saved firewall entries FirewallTrie _firewall_entries4; FirewallTrie _firewall_entries6; }; #endif // __FEA_DATA_PLANE_FIREWALL_FIREWALL_SET_DUMMY_HH__ xorp/fea/data_plane/firewall/firewall_get_ipfw2.hh0000664000076400007640000000625311421137511022406 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/data_plane/firewall/firewall_get_ipfw2.hh,v 1.3 2008/10/02 21:57:02 bms Exp $ #ifndef __FEA_DATA_PLANE_FIREWALL_FIREWALL_GET_IPFW2_HH__ #define __FEA_DATA_PLANE_FIREWALL_FIREWALL_GET_IPFW2_HH__ #include "fea/firewall_get.hh" class FirewallGetIpfw2 : public FirewallGet { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ FirewallGetIpfw2(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~FirewallGetIpfw2(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg); /** * Obtain the IPv4 firewall table. * * @param firewall_entry_list the return-by-reference list with all * entries in the IPv4 firewall table. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int get_table4(list& firewall_entry_list, string& error_msg); /** * Obtain the IPv6 firewall table. * * @param firewall_entry_list the return-by-reference list with all * entries in the IPv6 firewall table. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int get_table6(list& firewall_entry_list, string& error_msg); private: /** * Obtain the firewall table for a specific address family. * * @param family the address family. * @param firewall_entry_list the return-by-reference list with all * entries in the firewall table for the given address family. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int get_table(int family, list& firewall_entry_list, string& error_msg); int _s4; // The socket for firewall access }; #endif // __FEA_DATA_PLANE_FIREWALL_FIREWALL_GET_IPFW2_HH__ xorp/fea/data_plane/firewall/firewall_set_pf.cc0000664000076400007640000003520311421137511021763 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #ifdef HAVE_FCNTL_H #include #endif #ifdef HAVE_SYS_IOCTL_H #include #endif #ifdef HAVE_NET_IF_H #include #endif #ifdef HAVE_NET_PFVAR_H #include #endif #ifdef HAVE_NETINET_IN_H #include #endif #include "fea/firewall_manager.hh" #include "firewall_set_pf.hh" // // Set firewall information into the underlying system. // // The mechanism to set the information is PF. // #ifdef HAVE_FIREWALL_PF const string FirewallSetPf::_pf_device_name = "/dev/pf"; FirewallSetPf::FirewallSetPf(FeaDataPlaneManager& fea_data_plane_manager) : FirewallSet(fea_data_plane_manager), _fd(-1) { } FirewallSetPf::~FirewallSetPf() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the PF mechanism to set " "firewall information into the underlying system: %s", error_msg.c_str()); } } int FirewallSetPf::start(string& error_msg) { if (_is_running) return (XORP_OK); // // Open the PF device // _fd = open(_pf_device_name.c_str(), O_RDWR); if (_fd < 0) { error_msg = c_format("Could not open device %s for PF firewall: %s", _pf_device_name.c_str(), strerror(errno)); return (XORP_ERROR); } // // Enable PF operation // if (ioctl(_fd, DIOCSTART) < 0) { // Test whether PF is already enabled if (errno != EEXIST) { error_msg = c_format("Failed to enable PF firewall operation: %s", strerror(errno)); close(_fd); _fd = -1; return (XORP_ERROR); } } _is_running = true; return (XORP_OK); } int FirewallSetPf::stop(string& error_msg) { if (! _is_running) return (XORP_OK); // // Disable PF operation // if (ioctl(_fd, DIOCSTOP) < 0) { // Test whether PF is already disabled if (errno != ENOENT) { error_msg = c_format("Failed to disable PF firewall operation: %s", strerror(errno)); close(_fd); _fd = -1; return (XORP_ERROR); } } if (close(_fd) < 0) { _fd = -1; error_msg = c_format("Could not close device %s for PF firewall: %s", _pf_device_name.c_str(), strerror(errno)); return (XORP_ERROR); } _fd = -1; _is_running = false; return (XORP_OK); } int FirewallSetPf::update_entries(const list& added_entries, const list& replaced_entries, const list& deleted_entries, string& error_msg) { list::const_iterator iter; // // The entries to add // for (iter = added_entries.begin(); iter != added_entries.end(); ++iter) { const FirewallEntry& firewall_entry = *iter; if (add_entry(firewall_entry, error_msg) != XORP_OK) return (XORP_ERROR); } // // The entries to replace // for (iter = replaced_entries.begin(); iter != replaced_entries.end(); ++iter) { const FirewallEntry& firewall_entry = *iter; if (replace_entry(firewall_entry, error_msg) != XORP_OK) return (XORP_ERROR); } // // The entries to delete // for (iter = deleted_entries.begin(); iter != deleted_entries.end(); ++iter) { const FirewallEntry& firewall_entry = *iter; if (delete_entry(firewall_entry, error_msg) != XORP_OK) return (XORP_ERROR); } // // XXX: Push all entries, because the API for doing incremental changes // adds more complexity. // // If this becomes an issue, then we need to: // - Keep track of the kernel's rule numbers in user space // - Use ioctl(DIOCCHANGERULE) with PF_CHANGE_GET_TICKET // to get the appropriate ticket. // - Use ioctl(DIOCCHANGERULE) as appropriate with one of the // following actions and the appropriate kernel rule number: // PF_CHANGE_ADD_HEAD, PF_CHANGE_ADD_TAIL, F_CHANGE_ADD_BEFORE, // PF_CHANGE_ADD_AFTER, PF_CHANGE_REMOVE. // // XXX: If we need to use a single operation to delete an entry, // the following code can do it. #if 0 // // Use a single operation to delete the entry // bool is_add = false; uint32_t ticket = 0; if (add_delete_transaction_entry(is_add, ticket, firewall_entry, error_msg) != XORP_OK) { return (XORP_ERROR); } #endif // 0 return (push_entries(error_msg)); } int FirewallSetPf::set_table4(const list& firewall_entry_list, string& error_msg) { list empty_list; _firewall_entries4.clear(); return (update_entries(firewall_entry_list, empty_list, empty_list, error_msg)); } int FirewallSetPf::delete_all_entries4(string& error_msg) { list empty_list; _firewall_entries4.clear(); return (update_entries(empty_list, empty_list, empty_list, error_msg)); } int FirewallSetPf::set_table6(const list& firewall_entry_list, string& error_msg) { list empty_list; _firewall_entries6.clear(); return (update_entries(firewall_entry_list, empty_list, empty_list, error_msg)); } int FirewallSetPf::delete_all_entries6(string& error_msg) { list empty_list; _firewall_entries6.clear(); return (update_entries(empty_list, empty_list, empty_list, error_msg)); } int FirewallSetPf::add_entry(const FirewallEntry& firewall_entry, string& error_msg) { FirewallTrie::iterator iter; FirewallTrie* ftp = NULL; uint32_t key = firewall_entry.rule_number(); // XXX: the map key UNUSED(error_msg); if (firewall_entry.is_ipv4()) ftp = &_firewall_entries4; else ftp = &_firewall_entries6; // // XXX: If the entry already exists, then just update it. // Note that the replace_entry() implementation relies on this. // iter = ftp->find(key); if (iter == ftp->end()) { ftp->insert(make_pair(key, firewall_entry)); } else { FirewallEntry& fe_tmp = iter->second; fe_tmp = firewall_entry; } return (XORP_OK); } int FirewallSetPf::replace_entry(const FirewallEntry& firewall_entry, string& error_msg) { // // XXX: The add_entry() method implementation covers the replace_entry() // semantic as well. // return (add_entry(firewall_entry, error_msg)); } int FirewallSetPf::delete_entry(const FirewallEntry& firewall_entry, string& error_msg) { FirewallTrie::iterator iter; FirewallTrie* ftp = NULL; uint32_t key = firewall_entry.rule_number(); // XXX: the map key if (firewall_entry.is_ipv4()) ftp = &_firewall_entries4; else ftp = &_firewall_entries6; // Find the entry iter = ftp->find(key); if (iter == ftp->end()) { error_msg = c_format("Entry not found"); return (XORP_ERROR); } ftp->erase(iter); return (XORP_OK); } int FirewallSetPf::start_transaction(uint32_t& ticket, string& error_msg) { struct pfioc_trans trans; struct pfioc_trans::pfioc_trans_e trans_e; memset(&trans, 0, sizeof(trans)); memset(&trans_e, 0, sizeof(trans_e)); trans.size = 1; trans.esize = sizeof(trans_e); trans.array = &trans_e; trans_e.rs_num = PF_RULESET_FILTER; if (ioctl(_fd, DIOCXBEGIN, &trans) < 0) { error_msg = c_format("Failed to begin transaction for adding rules " "to PF firewall: %s", strerror(errno)); return (XORP_ERROR); } // TODO: Do we need a 'pool ticket'? ticket = trans_e.ticket; return (XORP_OK); } int FirewallSetPf::commit_transaction(uint32_t ticket, string& error_msg) { struct pfioc_trans trans; struct pfioc_trans::pfioc_trans_e trans_e; memset(&trans, 0, sizeof(trans)); memset(&trans_e, 0, sizeof(trans_e)); trans.size = 1; trans.esize = sizeof(trans_e); trans.array = &trans_e; trans_e.rs_num = PF_RULESET_FILTER; trans_e.ticket = ticket; if (ioctl(_fd, DIOCXCOMMIT, &trans) < 0) { error_msg = c_format("Failed to commit transaction for adding rules " "to PF firewall: %s", strerror(errno)); return (XORP_ERROR); } return (XORP_OK); } int FirewallSetPf::abort_transaction(uint32_t ticket, string& error_msg) { struct pfioc_trans trans; struct pfioc_trans::pfioc_trans_e trans_e; memset(&trans, 0, sizeof(trans)); memset(&trans_e, 0, sizeof(trans_e)); trans.size = 1; trans.esize = sizeof(trans_e); trans.array = &trans_e; trans_e.rs_num = PF_RULESET_FILTER; trans_e.ticket = ticket; if (ioctl(_fd, DIOCXROLLBACK, &trans) < 0) { error_msg = c_format("Failed to abort transaction for adding rules " "to PF firewall: %s", strerror(errno)); return (XORP_ERROR); } return (XORP_OK); } int FirewallSetPf::push_entries(string& error_msg) { FirewallTrie::iterator iter; uint32_t ticket = 0; // // Use a transaction to push the entries // if (start_transaction(ticket, error_msg) != XORP_OK) return (XORP_ERROR); // // Add all entries one-by-one. // // Note that we have to push both the IPv4 and IPv6 entries, // because they share the same table. // for (iter = _firewall_entries4.begin(); iter != _firewall_entries4.end(); ++iter) { FirewallEntry& firewall_entry = iter->second; bool is_add = true; if (add_delete_transaction_entry(is_add, ticket, firewall_entry, error_msg) != XORP_OK) { string dummy_error_msg; abort_transaction(ticket, dummy_error_msg); return (XORP_ERROR); } } for (iter = _firewall_entries6.begin(); iter != _firewall_entries6.end(); ++iter) { FirewallEntry& firewall_entry = iter->second; bool is_add = true; if (add_delete_transaction_entry(is_add, ticket, firewall_entry, error_msg) != XORP_OK) { string dummy_error_msg; abort_transaction(ticket, dummy_error_msg); return (XORP_ERROR); } } if (commit_transaction(ticket, error_msg) != XORP_OK) { string dummy_error_msg; abort_transaction(ticket, dummy_error_msg); return (XORP_ERROR); } return (XORP_OK); } int FirewallSetPf::add_delete_transaction_entry(bool is_add, uint32_t ticket, const FirewallEntry& firewall_entry, string& error_msg) { struct pfioc_rule pr; memset(&pr, 0, sizeof(pr)); pr.ticket = ticket; // // Transcribe the XORP rule to a PF rule // // // Set the address family // pr.rule.af = firewall_entry.src_network().af(); // // Set the rule number // // XXX: Do nothing, because PF doesn't have rule numbers // // // Set the action // if (is_add) { // Add the entry switch (firewall_entry.action()) { case FirewallEntry::ACTION_PASS: pr.rule.action = PF_PASS; break; case FirewallEntry::ACTION_DROP: pr.rule.action = PF_DROP; break; case FirewallEntry::ACTION_REJECT: // XXX: PF doesn't distinguish between packet DROP and REJECT pr.rule.action = PF_DROP; break; case FirewallEntry::ACTION_ANY: case FirewallEntry::ACTION_NONE: break; default: XLOG_FATAL("Unknown firewall entry action code: %u", firewall_entry.action()); break; } } else { // Delete the entry pr.action = PF_CHANGE_REMOVE; } // // Set the protocol // if (firewall_entry.ip_protocol() != FirewallEntry::IP_PROTOCOL_ANY) pr.rule.proto = firewall_entry.ip_protocol(); else pr.rule.proto = 0; // // Set the source and destination network addresses // pr.rule.src.addr.type = PF_ADDR_ADDRMASK; pr.rule.src.addr.iflags = 0; // XXX: PFI_AFLAG_NETWORK ?? const IPvX& src_addr = firewall_entry.src_network().masked_addr(); src_addr.copy_out(pr.rule.src.addr.v.a.addr.addr8); IPvX src_mask = IPvX::make_prefix(src_addr.af(), firewall_entry.src_network().prefix_len()); src_mask.copy_out(pr.rule.src.addr.v.a.mask.addr8); pr.rule.dst.addr.type = PF_ADDR_ADDRMASK; pr.rule.dst.addr.iflags = 0; // XXX: PFI_AFLAG_NETWORK ?? const IPvX& dst_addr = firewall_entry.dst_network().masked_addr(); dst_addr.copy_out(pr.rule.dst.addr.v.a.addr.addr8); IPvX dst_mask = IPvX::make_prefix(dst_addr.af(), firewall_entry.dst_network().prefix_len()); dst_mask.copy_out(pr.rule.dst.addr.v.a.mask.addr8); // // Set the source and destination port number ranges // if ((pr.rule.proto == IPPROTO_TCP) || (pr.rule.proto == IPPROTO_UDP)) { pr.rule.src.port[0] = firewall_entry.src_port_begin(); pr.rule.src.port[1] = firewall_entry.src_port_end(); pr.rule.src.port_op = PF_OP_RRG; pr.rule.dst.port[0] = firewall_entry.dst_port_begin(); pr.rule.dst.port[1] = firewall_entry.dst_port_end(); pr.rule.dst.port_op = PF_OP_RRG; } // // Set the interface/vif // // XXX: On this platform, ifname == vifname // if (! firewall_entry.vifname().empty()) { strlcpy(pr.rule.ifname, firewall_entry.vifname().c_str(), sizeof(pr.rule.ifname)); } // // Misc. other state // pr.rule.direction = PF_INOUT; // Add or delete the entry if (is_add) { if (ioctl(_fd, DIOCADDRULE, &pr) < 0) { error_msg = c_format("Failed to add rule to a PF firewall " "transaction: %s", strerror(errno)); return (XORP_ERROR); } } else { // // TODO: XXX: The code below doesn't work, because // pr.nr is not set to the number of rule to delete. // Therefore, we delete entries by pushing the whole table // (same as when we add entries). // XLOG_UNREACHABLE(); // Get the ticket pr.action = PF_CHANGE_GET_TICKET; if (ioctl(_fd, DIOCCHANGERULE, &pr) < 0) { error_msg = c_format("Failed to delete rule from a PF firewall " "transaction: %s", strerror(errno)); return (XORP_ERROR); } pr.action = PF_CHANGE_REMOVE; if (ioctl(_fd, DIOCCHANGERULE, &pr) < 0) { error_msg = c_format("Failed to delete rule from a PF firewall " "transaction: %s", strerror(errno)); return (XORP_ERROR); } } return (XORP_OK); } #endif // HAVE_FIREWALL_PF xorp/fea/data_plane/firewall/SConscript0000664000076400007640000000270011421137511020315 0ustar greearbgreearb# Copyright (c) 2009 XORP, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # $ID$ import os Import("env") env = env.Clone() is_shared = env.has_key('SHAREDLIBS') env.AppendUnique(CPPPATH = [ '#', '$BUILDDIR', ]) sources = [ # C++ files 'firewall_get_dummy.cc', 'firewall_get_ipfw2.cc', 'firewall_get_netfilter.cc', 'firewall_get_pf.cc', 'firewall_set_dummy.cc', 'firewall_set_ipfw2.cc', 'firewall_set_netfilter.cc', 'firewall_set_pf.cc', ] if is_shared: libxfw = env.SharedLibrary(target = 'libxorp_fea_firewall', source = sources) env.Alias('install', env.InstallLibrary(env['xorp_libdir'], libxfw)) else: libxfw = env.StaticLibrary(target = 'libxorp_fea_firewall', source = sources) Default(libxfw) xorp/fea/data_plane/firewall/firewall_set_netfilter.hh0000664000076400007640000002022611540224222023361 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/data_plane/firewall/firewall_set_netfilter.hh,v 1.5 2008/10/02 21:57:03 bms Exp $ #ifndef __FEA_DATA_PLANE_FIREWALL_FIREWALL_SET_NETFILTER_HH__ #define __FEA_DATA_PLANE_FIREWALL_FIREWALL_SET_NETFILTER_HH__ #include "fea/firewall_set.hh" class FirewallSetNetfilter : public FirewallSet { public: // Firewall entries trie indexed by rule number typedef map FirewallTrie; /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ FirewallSetNetfilter(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~FirewallSetNetfilter(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg); /** * Update the firewall entries by pushing them into the underlying system. * * @param added_entries the entries to add. * @param replaced_entries the entries to replace. * @param deleted_entries the deleted entries. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int update_entries(const list& added_entries, const list& replaced_entries, const list& deleted_entries, string& error_msg); /** * Set the IPv4 firewall table. * * @param firewall_entry_list the list with all entries to install into * the IPv4 firewall table. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int set_table4(const list& firewall_entry_list, string& error_msg); /** * Delete all entries in the IPv4 firewall table. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int delete_all_entries4(string& error_msg); /** * Set the IPv6 firewall table. * * @param firewall_entry_list the list with all entries to install into * the IPv6 firewall table. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int set_table6(const list& firewall_entry_list, string& error_msg); /** * Delete all entries in the IPv6 firewall table. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int delete_all_entries6(string& error_msg); private: /** * Add a single firewall entry. * * @param firewall_entry the entry to add. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int add_entry(const FirewallEntry& firewall_entry, string& error_msg); /** * Replace a single firewall entry. * * @param firewall_entry the replacement entry. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int replace_entry(const FirewallEntry& firewall_entry, string& error_msg); /** * Delete a single firewall entry. * * @param firewall_entry the entry to delete. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int delete_entry(const FirewallEntry& firewall_entry, string& error_msg); /** * Encode a single IPv4 firewall chain. * * @param chain_name the name of the chain to encode. * @param buffer the buffer to store the encoded chain. * @param next_data_index the return-by-reference index into the buffer * with the location to store the encoded chain. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int encode_chain4(const string& chain_name, vector& buffer, size_t& next_data_index, string& error_msg); /** * Encode a single IPv6 firewall chain. * * @param chain_name the name of the chain to encode. * @param buffer the buffer to store the encoded chain. * @param next_data_index the return-by-reference index into the buffer * with the location to store the encoded chain. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int encode_chain6(const string& chain_name, vector& buffer, size_t& next_data_index, string& error_msg); /** * Encode a single IPv4 firewall entry. * * @param firewall_entry the entry to encode. * @param buffer the buffer to store the encoded entry. * @param next_data_index the return-by-reference index into the buffer * to store the encoded entry. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int encode_entry4(const FirewallEntry& firewall_entry, vector& buffer, size_t& next_data_index, string& error_msg); /** * Encode a single IPv6 firewall entry. * * @param firewall_entry the entry to encode. * @param buffer the buffer to store the encoded entry. * @param next_data_index the return-by-reference index into the buffer * to store the encoded entry. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int encode_entry6(const FirewallEntry& firewall_entry, vector& buffer, size_t& next_data_index, string& error_msg); /** * Push all locally stored IPv4 firewall entries. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int push_entries4(string& error_msg); /** * Push all locally stored IPv6 firewall entries. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int push_entries6(string& error_msg); int _s4; // The socket for IPv4 firewall access int _s6; // The socket for IPv6 firewall access // The locally saved firewall entries FirewallTrie _firewall_entries4; FirewallTrie _firewall_entries6; // Misc. local state size_t _num_entries; size_t _head_offset; size_t _foot_offset; static const string _netfilter_table_name; // The NETFILTER table name static const string _netfilter_match_tcp; // The TCP match name static const string _netfilter_match_udp; // The UDP match name static const string _netfilter_chain_input; // The INPUT chain name static const string _netfilter_chain_forward; // The FORWARD chain name static const string _netfilter_chain_output; // The OUTPUT chain name }; #endif // __FEA_DATA_PLANE_FIREWALL_FIREWALL_SET_NETFILTER_HH__ xorp/fea/data_plane/firewall/firewall_set_netfilter.cc0000664000076400007640000007531711421137511023364 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libcomm/comm_api.h" #ifdef HAVE_SYS_SYSCTL_H #include #endif #ifdef HAVE_SYS_IOCTL_H #include #endif #ifdef HAVE_SYS_SOCKET_H #include #endif #ifdef HAVE_SYS_SOCKIO_H #include #endif #ifdef HAVE_NETINET_IN_SYSTM_H #include #endif #ifdef HAVE_NETINET_IP_H #include #endif #ifdef HAVE_LINUX_NETFILTER_IPV4_IP_TABLES_H #include #endif #ifdef HAVE_LINUX_NETFILTER_IPV6_IP6_TABLES_H #include #endif #include "fea/firewall_manager.hh" #include "firewall_set_netfilter.hh" // // Set firewall information into the underlying system. // // The mechanism to set the information is NETFILTER. // #ifdef HAVE_FIREWALL_NETFILTER #ifndef _ALIGNBYTES #define _ALIGNBYTES (sizeof(long) - 1) #endif #ifndef _ALIGN #define _ALIGN(p) (((u_long)(p) + _ALIGNBYTES) &~ _ALIGNBYTES) #endif const string FirewallSetNetfilter::_netfilter_table_name = "filter"; const string FirewallSetNetfilter::_netfilter_match_tcp = "tcp"; const string FirewallSetNetfilter::_netfilter_match_udp = "udp"; const string FirewallSetNetfilter::_netfilter_chain_input = "INPUT"; const string FirewallSetNetfilter::_netfilter_chain_forward = "FORWARD"; const string FirewallSetNetfilter::_netfilter_chain_output = "OUTPUT"; // // Local IPv4 structures // // IPv4: The error rule at the beginning and the end of a chain struct local_ipv4_ipt_error_target { struct ipt_entry_target entry_target; char error[IPT_TABLE_MAXNAMELEN]; }; // IPv4: The start of a chain struct local_ipv4_iptcb_chain_start { struct ipt_entry entry; struct local_ipv4_ipt_error_target error_target; }; // IPv4: The end of a chain struct local_ipv4_iptcb_chain_foot { struct ipt_entry entry; struct ipt_standard_target standard_target; }; // IPv4: The end of a table struct local_ipv4_iptcb_chain_error { struct ipt_entry entry; struct local_ipv4_ipt_error_target error_target; }; // IPv6: The error rule at the beginning and the end struct local_ipv6_ipt_error_target { struct ip6t_entry_target entry_target; char error[IP6T_TABLE_MAXNAMELEN]; }; // IPv6: The start of a chain struct local_ipv6_iptcb_chain_start { struct ip6t_entry entry; struct local_ipv6_ipt_error_target error_target; }; // IPv6: The end of a chain struct local_ipv6_iptcb_chain_foot { struct ip6t_entry entry; struct ip6t_standard_target standard_target; }; // IPv6: The end of a table struct local_ipv6_iptcb_chain_error { struct ip6t_entry entry; struct local_ipv6_ipt_error_target error_target; }; FirewallSetNetfilter::FirewallSetNetfilter(FeaDataPlaneManager& fea_data_plane_manager) : FirewallSet(fea_data_plane_manager), _s4(-1), _s6(-1), _num_entries(0), _head_offset(0), _foot_offset(0) { } FirewallSetNetfilter::~FirewallSetNetfilter() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the NETFILTER mechanism to set " "firewall information into the underlying system: %s", error_msg.c_str()); } } int FirewallSetNetfilter::start(string& error_msg) { if (_is_running) return (XORP_OK); // // Check if we have the necessary permission. // If not, print a warning and continue without firewall. // if (geteuid() != 0) { XLOG_WARNING("Cannot start the NETFILTER firewall mechanism: " "must be root; continuing without firewall setup"); return (XORP_OK); } // // Open a raw IPv4 socket that NETFILTER uses for communication // _s4 = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); if (_s4 < 0) { error_msg = c_format("Could not open a raw IPv4 socket for NETFILTER " "firewall: %s", strerror(errno)); return (XORP_ERROR); } #ifdef HAVE_IPV6 // // Open a raw IPv6 socket that NETFILTER uses for communication // _s6 = socket(AF_INET6, SOCK_RAW, IPPROTO_RAW); if (_s6 < 0) { error_msg = c_format("Could not open a raw IPv6 socket for NETFILTER " "firewall: %s", strerror(errno)); comm_close(_s4); _s4 = -1; return (XORP_ERROR); } #endif // HAVE_IPV6 _is_running = true; return (XORP_OK); } int FirewallSetNetfilter::stop(string& error_msg) { int ret_value4 = XORP_OK; int ret_value6 = XORP_OK; if (! _is_running) return (XORP_OK); if (_s4 >= 0) { ret_value4 = comm_close(_s4); _s4 = -1; if (ret_value4 != XORP_OK) { error_msg = c_format("Could not close raw IPv4 socket for " "NETFILTER firewall: %s", strerror(errno)); } } if (_s6 >= 0) { ret_value6 = comm_close(_s6); _s6 = -1; if ((ret_value6 != XORP_OK) && (ret_value4 == XORP_OK)) { error_msg = c_format("Could not close raw IPv6 socket for " "NETFILTER firewall: %s", strerror(errno)); } } if ((ret_value4 != XORP_OK) || (ret_value6 != XORP_OK)) return (XORP_ERROR); _is_running = false; return (XORP_OK); } int FirewallSetNetfilter::update_entries( const list& added_entries, const list& replaced_entries, const list& deleted_entries, string& error_msg) { list::const_iterator iter; bool has_ipv4 = false; bool has_ipv6 = false; // // The entries to add // for (iter = added_entries.begin(); iter != added_entries.end(); ++iter) { const FirewallEntry& firewall_entry = *iter; if (firewall_entry.is_ipv4()) has_ipv4 = true; if (firewall_entry.is_ipv6()) has_ipv6 = true; if (add_entry(firewall_entry, error_msg) != XORP_OK) return (XORP_ERROR); } // // The entries to replace // for (iter = replaced_entries.begin(); iter != replaced_entries.end(); ++iter) { const FirewallEntry& firewall_entry = *iter; if (firewall_entry.is_ipv4()) has_ipv4 = true; if (firewall_entry.is_ipv6()) has_ipv6 = true; if (replace_entry(firewall_entry, error_msg) != XORP_OK) return (XORP_ERROR); } // // The entries to delete // for (iter = deleted_entries.begin(); iter != deleted_entries.end(); ++iter) { const FirewallEntry& firewall_entry = *iter; if (firewall_entry.is_ipv4()) has_ipv4 = true; if (firewall_entry.is_ipv6()) has_ipv6 = true; if (delete_entry(firewall_entry, error_msg) != XORP_OK) return (XORP_ERROR); } // // Push the entries // if (has_ipv4) { if (push_entries4(error_msg) != XORP_OK) return (XORP_ERROR); } if (has_ipv6) { if (push_entries6(error_msg) != XORP_OK) return (XORP_ERROR); } return (XORP_OK); } int FirewallSetNetfilter::set_table4(const list& firewall_entry_list, string& error_msg) { list empty_list; _firewall_entries4.clear(); return (update_entries(firewall_entry_list, empty_list, empty_list, error_msg)); } int FirewallSetNetfilter::set_table6(const list& firewall_entry_list, string& error_msg) { list empty_list; _firewall_entries6.clear(); return (update_entries(firewall_entry_list, empty_list, empty_list, error_msg)); } int FirewallSetNetfilter::delete_all_entries4(string& error_msg) { _firewall_entries4.clear(); return (push_entries4(error_msg)); } int FirewallSetNetfilter::delete_all_entries6(string& error_msg) { _firewall_entries6.clear(); return (push_entries6(error_msg)); } int FirewallSetNetfilter::add_entry(const FirewallEntry& firewall_entry, string& error_msg) { FirewallTrie::iterator iter; FirewallTrie* ftp = NULL; uint32_t key = firewall_entry.rule_number(); // XXX: the map key UNUSED(error_msg); if (firewall_entry.is_ipv4()) ftp = &_firewall_entries4; else ftp = &_firewall_entries6; // // XXX: If the entry already exists, then just update it. // Note that the replace_entry() implementation relies on this. // iter = ftp->find(key); if (iter == ftp->end()) { ftp->insert(make_pair(key, firewall_entry)); } else { FirewallEntry& fe_tmp = iter->second; fe_tmp = firewall_entry; } return (XORP_OK); } int FirewallSetNetfilter::replace_entry(const FirewallEntry& firewall_entry, string& error_msg) { // // XXX: The add_entry() method implementation covers the replace_entry() // semantic as well. // return (add_entry(firewall_entry, error_msg)); } int FirewallSetNetfilter::delete_entry(const FirewallEntry& firewall_entry, string& error_msg) { FirewallTrie::iterator iter; FirewallTrie* ftp = NULL; uint32_t key = firewall_entry.rule_number(); // XXX: the map key if (firewall_entry.is_ipv4()) ftp = &_firewall_entries4; else ftp = &_firewall_entries6; // Find the entry iter = ftp->find(key); if (iter == ftp->end()) { error_msg = c_format("Entry not found"); return (XORP_ERROR); } ftp->erase(iter); return (XORP_OK); } int FirewallSetNetfilter::push_entries4(string& error_msg) { vector buffer; size_t size; size_t next_data_index = 0; struct ipt_replace* ipr; socklen_t socklen; // // Calculate the required buffer space and allocate the buffer // size = _ALIGN(sizeof(struct ipt_entry)) + _ALIGN(sizeof(struct ipt_entry_match)) + _ALIGN(sizeof(struct ipt_standard_target)); size *= _firewall_entries4.size(); size += _ALIGN(sizeof(struct ipt_replace)); size += 3 * _ALIGN(sizeof(struct local_ipv4_iptcb_chain_start)); size += 3 * _ALIGN(sizeof(struct local_ipv4_iptcb_chain_foot)); size += _ALIGN(sizeof(struct local_ipv4_iptcb_chain_error)); buffer.resize(size); for (size_t i = 0; i < buffer.size(); i++) buffer[i] = 0; // // Get information about the old table // struct ipt_getinfo info; memset(&info, 0, sizeof(info)); strlcpy(info.name, _netfilter_table_name.c_str(), sizeof(info.name)); socklen = sizeof(info); if (getsockopt(_s4, IPPROTO_IP, IPT_SO_GET_INFO, &info, &socklen) < 0) { error_msg = c_format("Could not get the NETFILTER IPv4 firewall table: " "%s", strerror(errno)); return (XORP_ERROR); } // // Add a buffer to get information about the old counters // size = sizeof(struct ipt_counters) * info.num_entries; vector counters_buffer(size); for (size_t i = 0; i < counters_buffer.size(); i++) counters_buffer[i] = 0; // // Initialize the replacement header // ipr = reinterpret_cast(&buffer[0]); strlcpy(ipr->name, _netfilter_table_name.c_str(), sizeof(ipr->name)); ipr->valid_hooks = info.valid_hooks; ipr->num_counters = info.num_entries; ipr->num_entries = 0; ipr->counters = reinterpret_cast(&counters_buffer[0]); next_data_index += sizeof(*ipr); _num_entries = 0; // // Add the chains: INPUT, FORWARD, OUTPUT // // The INPUT chain if (encode_chain4(_netfilter_chain_input, buffer, next_data_index, error_msg) != XORP_OK) { return (XORP_ERROR); } ipr->hook_entry[NF_IP_LOCAL_IN] = _head_offset - sizeof(*ipr); ipr->underflow[NF_IP_LOCAL_IN] = _foot_offset - sizeof(*ipr); // The FORWARD chain if (encode_chain4(_netfilter_chain_forward, buffer, next_data_index, error_msg) != XORP_OK) { return (XORP_ERROR); } ipr->hook_entry[NF_IP_FORWARD] = _head_offset - sizeof(*ipr); ipr->underflow[NF_IP_FORWARD] = _foot_offset - sizeof(*ipr); // The OUTPUT chain if (encode_chain4(_netfilter_chain_output, buffer, next_data_index, error_msg) != XORP_OK) { return (XORP_ERROR); } ipr->hook_entry[NF_IP_LOCAL_OUT] = _head_offset - sizeof(*ipr); ipr->underflow[NF_IP_LOCAL_OUT] = _foot_offset - sizeof(*ipr); // // Append the error rule at the end of the table // { struct local_ipv4_iptcb_chain_error* error; uint8_t* ptr = &buffer[next_data_index]; error = reinterpret_cast(ptr); error->entry.target_offset = sizeof(error->entry); error->entry.next_offset = sizeof(error->entry) + _ALIGN(sizeof(error->error_target)); error->error_target.entry_target.u.user.target_size = _ALIGN(sizeof(error->error_target)); strlcpy(error->error_target.entry_target.u.user.name, IPT_ERROR_TARGET, sizeof(error->error_target.entry_target.u.user.name)); strlcpy(error->error_target.error, "ERROR", sizeof(error->error_target.error)); next_data_index += error->entry.next_offset; _num_entries++; } // // Finish the initialization of the replacement header // ipr->num_entries = _num_entries; _num_entries = 0; ipr->size = next_data_index - sizeof(*ipr); socklen = next_data_index; if (setsockopt(_s4, IPPROTO_IP, IPT_SO_SET_REPLACE, &buffer[0], socklen) < 0) { error_msg = c_format("Could not set NETFILTER firewall entries: %s", strerror(errno)); return (XORP_ERROR); } return (XORP_OK); } int FirewallSetNetfilter::push_entries6(string& error_msg) { vector buffer; size_t size; size_t next_data_index = 0; struct ip6t_replace* ipr; socklen_t socklen; // // Calculate the required buffer space and allocate the buffer // size = _ALIGN(sizeof(struct ip6t_entry)) + _ALIGN(sizeof(struct ip6t_entry_match)) + _ALIGN(sizeof(struct ip6t_standard_target)); size *= _firewall_entries6.size(); size += _ALIGN(sizeof(struct ip6t_replace)); size += 3 * _ALIGN(sizeof(struct local_ipv6_iptcb_chain_start)); size += 3 * _ALIGN(sizeof(struct local_ipv6_iptcb_chain_foot)); size += _ALIGN(sizeof(struct local_ipv6_iptcb_chain_error)); buffer.resize(size); for (size_t i = 0; i < buffer.size(); i++) buffer[i] = 0; // // Get information about the old table // struct ip6t_getinfo info; memset(&info, 0, sizeof(info)); strlcpy(info.name, _netfilter_table_name.c_str(), sizeof(info.name)); socklen = sizeof(info); if (getsockopt(_s6, IPPROTO_IPV6, IP6T_SO_GET_INFO, &info, &socklen) < 0) { error_msg = c_format("Could not get the NETFILTER IPv6 firewall table: " "%s", strerror(errno)); return (XORP_ERROR); } // // Add a buffer to get information about the old counters // size = sizeof(struct ip6t_counters) * info.num_entries; vector counters_buffer(size); for (size_t i = 0; i < counters_buffer.size(); i++) counters_buffer[i] = 0; // // Initialize the replacement header // ipr = reinterpret_cast(&buffer[0]); strlcpy(ipr->name, _netfilter_table_name.c_str(), sizeof(ipr->name)); ipr->valid_hooks = info.valid_hooks; ipr->num_counters = info.num_entries; ipr->num_entries = 0; ipr->counters = reinterpret_cast(&counters_buffer[0]); next_data_index += sizeof(*ipr); _num_entries = 0; // // Add the chains: INPUT, FORWARD, OUTPUT // // The INPUT chain if (encode_chain6(_netfilter_chain_input, buffer, next_data_index, error_msg) != XORP_OK) { return (XORP_ERROR); } ipr->hook_entry[NF_IP6_LOCAL_IN] = _head_offset - sizeof(*ipr); ipr->underflow[NF_IP6_LOCAL_IN] = _foot_offset - sizeof(*ipr); // The FORWARD chain if (encode_chain6(_netfilter_chain_forward, buffer, next_data_index, error_msg) != XORP_OK) { return (XORP_ERROR); } ipr->hook_entry[NF_IP6_FORWARD] = _head_offset - sizeof(*ipr); ipr->underflow[NF_IP6_FORWARD] = _foot_offset - sizeof(*ipr); // The OUTPUT chain if (encode_chain6(_netfilter_chain_output, buffer, next_data_index, error_msg) != XORP_OK) { return (XORP_ERROR); } ipr->hook_entry[NF_IP6_LOCAL_OUT] = _head_offset - sizeof(*ipr); ipr->underflow[NF_IP6_LOCAL_OUT] = _foot_offset - sizeof(*ipr); // // Append the error rule at the end of the table // { struct local_ipv6_iptcb_chain_error* error; uint8_t* ptr = &buffer[next_data_index]; error = reinterpret_cast(ptr); error->entry.target_offset = sizeof(error->entry); error->entry.next_offset = sizeof(error->entry) + _ALIGN(sizeof(error->error_target)); error->error_target.entry_target.u.user.target_size = _ALIGN(sizeof(error->error_target)); strlcpy(error->error_target.entry_target.u.user.name, IP6T_ERROR_TARGET, sizeof(error->error_target.entry_target.u.user.name)); strlcpy(error->error_target.error, "ERROR", sizeof(error->error_target.error)); next_data_index += error->entry.next_offset; _num_entries++; } // // Finish the initialization of the replacement header // ipr->num_entries = _num_entries; _num_entries = 0; ipr->size = next_data_index - sizeof(*ipr); // // Write the data to the underlying system // socklen = next_data_index; if (setsockopt(_s6, IPPROTO_IPV6, IP6T_SO_SET_REPLACE, &buffer[0], socklen) < 0) { error_msg = c_format("Could not set NETFILTER firewall entries: %s", strerror(errno)); return (XORP_ERROR); } return (XORP_OK); } int FirewallSetNetfilter::encode_chain4(const string& chain_name, vector& buffer, size_t& next_data_index, string& error_msg) { uint8_t* ptr; // // Add the chain header for user-defined chains // // XXX: We don't use user-defined chains. // bool is_user_defined_chain = false; _head_offset = next_data_index; // XXX if (is_user_defined_chain) { struct local_ipv4_iptcb_chain_start* head; string chain_name; // XXX: user-defined ptr = &buffer[next_data_index]; head = reinterpret_cast(ptr); head->entry.target_offset = sizeof(head->entry); head->entry.next_offset = sizeof(head->entry) + _ALIGN(sizeof(head->error_target)); strlcpy(head->error_target.entry_target.u.user.name, IPT_ERROR_TARGET, sizeof(head->error_target.entry_target.u.user.name)); head->error_target.entry_target.u.target_size = _ALIGN(sizeof(head->error_target)); strlcpy(head->error_target.error, chain_name.c_str(), sizeof(head->error_target.error)); next_data_index += head->entry.next_offset; _num_entries++; } // // Encode all entries one-by-one if the FORWARD chain // if (chain_name == _netfilter_chain_forward) { FirewallTrie::const_iterator iter; for (iter = _firewall_entries4.begin(); iter != _firewall_entries4.end(); ++iter) { const FirewallEntry& firewall_entry = iter->second; if (encode_entry4(firewall_entry, buffer, next_data_index, error_msg) != XORP_OK) { return (XORP_ERROR); } } } // // Add the chain footer // { struct local_ipv4_iptcb_chain_foot* foot; _foot_offset = next_data_index; // XXX ptr = &buffer[next_data_index]; foot = reinterpret_cast(ptr); foot->entry.target_offset = sizeof(foot->entry); foot->entry.next_offset = sizeof(foot->entry) + _ALIGN(sizeof(foot->standard_target)); strlcpy(foot->standard_target.target.u.user.name, IPT_STANDARD_TARGET, sizeof(foot->standard_target.target.u.user.name)); foot->standard_target.target.u.target_size = _ALIGN(sizeof(foot->standard_target)); if (is_user_defined_chain) foot->standard_target.verdict = IPT_RETURN; else foot->standard_target.verdict = -NF_ACCEPT - 1; next_data_index += foot->entry.next_offset; _num_entries++; } return (XORP_OK); } int FirewallSetNetfilter::encode_chain6(const string& chain_name, vector& buffer, size_t& next_data_index, string& error_msg) { uint8_t* ptr; // // Add the chain header for user-defined chains // // XXX: We don't use user-defined chains. // bool is_user_defined_chain = false; _head_offset = next_data_index; // XXX if (is_user_defined_chain) { struct local_ipv6_iptcb_chain_start* head; string chain_name; // XXX: user-defined ptr = &buffer[next_data_index]; head = reinterpret_cast(ptr); head->entry.target_offset = sizeof(head->entry); head->entry.next_offset = sizeof(head->entry) + _ALIGN(sizeof(head->error_target)); strlcpy(head->error_target.entry_target.u.user.name, IP6T_ERROR_TARGET, sizeof(head->error_target.entry_target.u.user.name)); head->error_target.entry_target.u.target_size = _ALIGN(sizeof(head->error_target)); strlcpy(head->error_target.error, chain_name.c_str(), sizeof(head->error_target.error)); next_data_index += head->entry.next_offset; _num_entries++; } // // Encode all entries one-by-one if the FORWARD chain // if (chain_name == _netfilter_chain_forward) { FirewallTrie::const_iterator iter; for (iter = _firewall_entries6.begin(); iter != _firewall_entries6.end(); ++iter) { const FirewallEntry& firewall_entry = iter->second; if (encode_entry6(firewall_entry, buffer, next_data_index, error_msg) != XORP_OK) { return (XORP_ERROR); } } } // // Add the chain footer // { struct local_ipv6_iptcb_chain_foot* foot; _foot_offset = next_data_index; // XXX ptr = &buffer[next_data_index]; foot = reinterpret_cast(ptr); foot->entry.target_offset = sizeof(foot->entry); foot->entry.next_offset = sizeof(foot->entry) + _ALIGN(sizeof(foot->standard_target)); strlcpy(foot->standard_target.target.u.user.name, IP6T_STANDARD_TARGET, sizeof(foot->standard_target.target.u.user.name)); foot->standard_target.target.u.target_size = _ALIGN(sizeof(foot->standard_target)); foot->standard_target.verdict = IP6T_RETURN; if (is_user_defined_chain) foot->standard_target.verdict = IP6T_RETURN; else foot->standard_target.verdict = -NF_ACCEPT - 1; next_data_index += foot->entry.next_offset; _num_entries++; } return (XORP_OK); } int FirewallSetNetfilter::encode_entry4(const FirewallEntry& firewall_entry, vector& buffer, size_t& next_data_index, string& error_msg) { struct ipt_entry* ipt = NULL; struct ipt_entry_match* iem = NULL; struct ipt_standard_target* ist = NULL; uint8_t* ptr; UNUSED(error_msg); ptr = &buffer[next_data_index]; ipt = reinterpret_cast(ptr); // // Transcribe the XORP rule to an NETFILTER rule // ipt->target_offset = sizeof(*ipt); // XXX: netfilter doesn't use _ALIGN() // // Set the rule number // // XXX: Do nothing, because NETFILTER doesn't have rule numbers // // // Set the protocol if (firewall_entry.ip_protocol() != FirewallEntry::IP_PROTOCOL_ANY) ipt->ip.proto = firewall_entry.ip_protocol(); else ipt->ip.proto = 0; // // Set the source and destination network addresses // const IPvX& src_addr = firewall_entry.src_network().masked_addr(); src_addr.copy_out(ipt->ip.src); IPvX src_mask = IPvX::make_prefix(src_addr.af(), firewall_entry.src_network().prefix_len()); src_mask.copy_out(ipt->ip.smsk); const IPvX& dst_addr = firewall_entry.dst_network().masked_addr(); dst_addr.copy_out(ipt->ip.dst); IPvX dst_mask = IPvX::make_prefix(dst_addr.af(), firewall_entry.dst_network().prefix_len()); dst_mask.copy_out(ipt->ip.dmsk); // // Set the interface/vif // // XXX: On this platform, ifname == vifname // if (! firewall_entry.vifname().empty()) { strlcpy(ipt->ip.iniface, firewall_entry.vifname().c_str(), sizeof(ipt->ip.iniface)); } // // Set the source and destination port number ranges // if ((ipt->ip.proto == IPPROTO_TCP) || (ipt->ip.proto == IPPROTO_UDP)) { ptr = reinterpret_cast(ipt); ptr += ipt->target_offset; iem = reinterpret_cast(ptr); iem->u.match_size = _ALIGN(sizeof(*iem)); switch (ipt->ip.proto) { case IPPROTO_TCP: { struct ipt_tcp* ip_tcp; ip_tcp = reinterpret_cast(iem->data); ip_tcp->spts[0] = firewall_entry.src_port_begin(); ip_tcp->spts[1] = firewall_entry.src_port_end(); ip_tcp->dpts[0] = firewall_entry.dst_port_begin(); ip_tcp->dpts[1] = firewall_entry.dst_port_end(); strlcpy(iem->u.user.name, _netfilter_match_tcp.c_str(), sizeof(iem->u.user.name)); iem->u.match_size += _ALIGN(sizeof(*ip_tcp)); break; } case IPPROTO_UDP: { struct ipt_udp* ip_udp; ip_udp = reinterpret_cast(iem->data); ip_udp->spts[0] = firewall_entry.src_port_begin(); ip_udp->spts[1] = firewall_entry.src_port_end(); ip_udp->dpts[0] = firewall_entry.dst_port_begin(); ip_udp->dpts[1] = firewall_entry.dst_port_end(); strlcpy(iem->u.user.name, _netfilter_match_udp.c_str(), sizeof(iem->u.user.name)); iem->u.match_size += _ALIGN(sizeof(*ip_udp)); break; } default: break; } ipt->target_offset += iem->u.match_size; } // // Set the action // ptr = reinterpret_cast(ipt); ptr += ipt->target_offset; ist = reinterpret_cast(ptr); ist->target.u.user.target_size = XT_ALIGN(sizeof(*ist)); strlcpy(ist->target.u.user.name, IPT_STANDARD_TARGET, sizeof(ist->target.u.user.name)); switch (firewall_entry.action()) { case FirewallEntry::ACTION_PASS: ist->verdict = -NF_ACCEPT - 1; break; case FirewallEntry::ACTION_DROP: ist->verdict = -NF_DROP - 1; break; case FirewallEntry::ACTION_REJECT: // XXX: NETFILTER doesn't distinguish between packet DROP and REJECT ist->verdict = -NF_DROP - 1; break; case FirewallEntry::ACTION_ANY: case FirewallEntry::ACTION_NONE: break; default: XLOG_FATAL("Unknown firewall entry action code: %u", firewall_entry.action()); break; } ipt->next_offset = ipt->target_offset + ist->target.u.user.target_size; _num_entries++; next_data_index += ipt->next_offset; return (XORP_OK); } int FirewallSetNetfilter::encode_entry6(const FirewallEntry& firewall_entry, vector& buffer, size_t& next_data_index, string& error_msg) { struct ip6t_entry* ipt = NULL; struct ip6t_entry_match* iem = NULL; struct ip6t_standard_target* ist = NULL; uint8_t* ptr; UNUSED(error_msg); ptr = &buffer[next_data_index]; ipt = reinterpret_cast(ptr); // // Transcribe the XORP rule to an NETFILTER rule // ipt->target_offset = sizeof(*ipt); // XXX: netfilter doesn't use _ALIGN() // // Set the rule number // // XXX: Do nothing, because NETFILTER doesn't have rule numbers // // // Set the protocol if (firewall_entry.ip_protocol() != FirewallEntry::IP_PROTOCOL_ANY) ipt->ipv6.proto = firewall_entry.ip_protocol(); else ipt->ipv6.proto = 0; // // Set the source and destination network addresses // const IPvX& src_addr = firewall_entry.src_network().masked_addr(); src_addr.copy_out(ipt->ipv6.src); IPvX src_mask = IPvX::make_prefix(src_addr.af(), firewall_entry.src_network().prefix_len()); src_mask.copy_out(ipt->ipv6.smsk); const IPvX& dst_addr = firewall_entry.dst_network().masked_addr(); dst_addr.copy_out(ipt->ipv6.dst); IPvX dst_mask = IPvX::make_prefix(dst_addr.af(), firewall_entry.dst_network().prefix_len()); dst_mask.copy_out(ipt->ipv6.dmsk); // // Set the interface/vif // // XXX: On this platform, ifname == vifname // if (! firewall_entry.vifname().empty()) { strlcpy(ipt->ipv6.iniface, firewall_entry.vifname().c_str(), sizeof(ipt->ipv6.iniface)); } // // Set the source and destination port number ranges // if ((ipt->ipv6.proto == IPPROTO_TCP) || (ipt->ipv6.proto == IPPROTO_UDP)) { ptr = reinterpret_cast(ipt); ptr += ipt->target_offset; iem = reinterpret_cast(ptr); iem->u.match_size = _ALIGN(sizeof(*iem)); switch (ipt->ipv6.proto) { case IPPROTO_TCP: { struct ip6t_tcp* ip_tcp; ip_tcp = reinterpret_cast(iem->data); ip_tcp->spts[0] = firewall_entry.src_port_begin(); ip_tcp->spts[1] = firewall_entry.src_port_end(); ip_tcp->dpts[0] = firewall_entry.dst_port_begin(); ip_tcp->dpts[1] = firewall_entry.dst_port_end(); strlcpy(iem->u.user.name, _netfilter_match_tcp.c_str(), sizeof(iem->u.user.name)); iem->u.match_size += _ALIGN(sizeof(*ip_tcp)); break; } case IPPROTO_UDP: { struct ip6t_udp* ip_udp; ip_udp = reinterpret_cast(iem->data); ip_udp->spts[0] = firewall_entry.src_port_begin(); ip_udp->spts[1] = firewall_entry.src_port_end(); ip_udp->dpts[0] = firewall_entry.dst_port_begin(); ip_udp->dpts[1] = firewall_entry.dst_port_end(); strlcpy(iem->u.user.name, _netfilter_match_udp.c_str(), sizeof(iem->u.user.name)); iem->u.match_size += _ALIGN(sizeof(*ip_udp)); break; } default: break; } ipt->target_offset += iem->u.match_size; } // // Set the action // ptr = reinterpret_cast(ipt); ptr += ipt->target_offset; ist = reinterpret_cast(ptr); ist->target.u.user.target_size = XT_ALIGN(sizeof(*ist)); strlcpy(ist->target.u.user.name, IP6T_STANDARD_TARGET, sizeof(ist->target.u.user.name)); switch (firewall_entry.action()) { case FirewallEntry::ACTION_PASS: ist->verdict = -NF_ACCEPT - 1; break; case FirewallEntry::ACTION_DROP: ist->verdict = -NF_DROP - 1; break; case FirewallEntry::ACTION_REJECT: // XXX: NETFILTER doesn't distinguish between packet DROP and REJECT ist->verdict = -NF_DROP - 1; break; case FirewallEntry::ACTION_ANY: case FirewallEntry::ACTION_NONE: break; default: XLOG_FATAL("Unknown firewall entry action code: %u", firewall_entry.action()); break; } ipt->next_offset = ipt->target_offset + ist->target.u.user.target_size; _num_entries++; next_data_index += ipt->next_offset; return (XORP_OK); } #endif // HAVE_FIREWALL_NETFILTER xorp/fea/data_plane/firewall/firewall_get_ipfw2.cc0000664000076400007640000002371311421137511022374 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libcomm/comm_api.h" #ifdef HAVE_SYS_SYSCTL_H #include #endif #ifdef HAVE_SYS_SOCKET_H #include #endif #ifdef HAVE_SYS_SOCKIO_H #include #endif #ifdef HAVE_NET_IF_H #include #endif #ifdef HAVE_NETINET_IN_SYSTM_H #include #endif #ifdef HAVE_NETINET_IP_H #include #endif // // XXX: RELENG_4 branches of FreeBSD after RELENG_4_9_RELEASE require // that IPFW2 support be explicitly requested by defining the // preprocessor symbol IPFW2 to a non-zero value. // #define IPFW2 1 #ifdef HAVE_NETINET_IP_FW_H #include #endif #include "fea/firewall_manager.hh" #include "firewall_get_ipfw2.hh" // // Get information about firewall entries from the underlying system. // // The mechanism to obtain the information is IPFW2. // #ifdef HAVE_FIREWALL_IPFW2 FirewallGetIpfw2::FirewallGetIpfw2(FeaDataPlaneManager& fea_data_plane_manager) : FirewallGet(fea_data_plane_manager), _s4(-1) { } FirewallGetIpfw2::~FirewallGetIpfw2() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the IPFW2 mechanism to get " "information about firewall entries from the underlying " "system: %s", error_msg.c_str()); } } int FirewallGetIpfw2::start(string& error_msg) { if (_is_running) return (XORP_OK); // // Open a raw IPv4 socket that IPFW2 uses for communication // _s4 = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); if (_s4 < 0) { error_msg = c_format("Could not open a raw socket for IPFW2 firewall: " "%s", strerror(errno)); return (XORP_ERROR); } _is_running = true; return (XORP_OK); } int FirewallGetIpfw2::stop(string& error_msg) { if (! _is_running) return (XORP_OK); if (comm_close(_s4) != XORP_OK) { error_msg = c_format("Could not close raw socket for IPFW2 " "firewall: %s", strerror(errno)); _s4 = -1; return (XORP_ERROR); } _s4 = -1; _is_running = false; return (XORP_OK); } #define NEXT_IP_FW(ip_fwp) ((struct ip_fw *)((char *)ip_fwp + RULESIZE(ip_fwp))) int FirewallGetIpfw2::get_table4(list& firewall_entry_list, string& error_msg) { return (get_table(AF_INET, firewall_entry_list, error_msg)); } int FirewallGetIpfw2::get_table6(list& firewall_entry_list, string& error_msg) { #ifdef HAVE_IPV6 return (get_table(AF_INET6, firewall_entry_list, error_msg)); #else error_msg = c_format("Cannot get the IPv6 firewall table: " "IPv6 is not supported"); return (XORP_ERROR); UNUSED(firewall_entry_list); #endif } int FirewallGetIpfw2::get_table(int family, list& firewall_entry_list, string& error_msg) { vector chainbuf; socklen_t nbytes; size_t nalloc, nstat, n; struct ip_fw* ip_fw; nbytes = 1024; nalloc = 1024; // // Play elastic buffer games to get a snapshot of the entire rule chain // chainbuf.reserve(nalloc); while (nbytes >= nalloc) { nbytes = nalloc = nalloc * 2 + 200; chainbuf.reserve(nalloc); if (getsockopt(_s4, IPPROTO_IP, IP_FW_GET, &chainbuf[0], &nbytes) < 0) { error_msg = c_format("Could not get the IPFW2 firewall table: %s", strerror(errno)); return (XORP_ERROR); } } chainbuf.resize(nbytes); // // Count the static rules. // We need to scan the list because they have variable size. // for (nstat = 1, ip_fw = reinterpret_cast(&chainbuf[0]); (ip_fw->rulenum < 65535) && (ip_fw < reinterpret_cast(&chainbuf[nbytes])); ++nstat, ip_fw = NEXT_IP_FW(ip_fw)) { ; // XXX: Do nothing } // // Parse buffer contents for all static rules // for (n = 0, ip_fw = reinterpret_cast(&chainbuf[0]); n < nstat; n++, ip_fw = NEXT_IP_FW(ip_fw)) { size_t len; ipfw_insn* cmd; uint32_t rule_number = FirewallEntry::RULE_NUMBER_DEFAULT; string ifname, vifname; IPvXNet src_network = IPvXNet(family); IPvXNet dst_network = IPvXNet(family); uint32_t ip_protocol = FirewallEntry::IP_PROTOCOL_ANY; uint32_t src_port_begin = FirewallEntry::PORT_MIN; uint32_t src_port_end = FirewallEntry::PORT_MAX; uint32_t dst_port_begin = FirewallEntry::PORT_MIN; uint32_t dst_port_end = FirewallEntry::PORT_MAX; FirewallEntry::Action action = FirewallEntry::ACTION_INVALID; // // Get the rule number // rule_number = ip_fw->rulenum; // // Get the action // cmd = ACTION_PTR(ip_fw); for (len = ip_fw->cmd_len - ip_fw->act_ofs; len > 0; len -= F_LEN(cmd), cmd += F_LEN(cmd)) { switch(cmd->opcode) { case O_ACCEPT: action = FirewallEntry::ACTION_PASS; break; case O_DENY: action = FirewallEntry::ACTION_DROP; break; case O_REJECT: action = FirewallEntry::ACTION_REJECT; break; default: XLOG_WARNING("Ingoring firewall entry with type %u", cmd->opcode); break; } // // XXX: There could be more than one actions, but for now // we care only about the first one. // if (action != FirewallEntry::ACTION_INVALID) break; } if (action == FirewallEntry::ACTION_INVALID) { XLOG_WARNING("Ignoring firewall entry: unknown action"); continue; } // // Get the rest of the entry // bool have_ipv4 = false; bool have_ipv6 = false; for (len = ip_fw->act_ofs, cmd = ip_fw->cmd; len > 0; len -= F_LEN(cmd), cmd += F_LEN(cmd)) { if ((cmd->len & F_OR) || (cmd->len & F_NOT)) continue; switch (cmd->opcode) { case O_IP4: { // The IPv4 address family have_ipv4 = true; break; } case O_IP6: { // The IPv6 address family have_ipv6 = true; break; } case O_IP_SRC_MASK: { // The IPv4 source address and mask ipfw_insn_ip* cmd_ip = reinterpret_cast(cmd); // // XXX: There could be a list of address-mask pairs, but // we consider only the first one. // IPv4 addr(cmd_ip->addr); IPv4 mask(cmd_ip->mask); src_network = IPvXNet(IPvX(addr), mask.mask_len()); break; } case O_IP_DST_MASK: { // The IPv4 destination address and mask ipfw_insn_ip* cmd_ip = reinterpret_cast(cmd); // // XXX: There could be a list of address-mask pairs, but // we consider only the first one. // IPv4 addr(cmd_ip->addr); IPv4 mask(cmd_ip->mask); dst_network = IPvXNet(IPvX(addr), mask.mask_len()); break; } case O_IP6_SRC_MASK: { // The IPv6 source address and mask ipfw_insn_ip6* cmd_ip6 = reinterpret_cast(cmd); // // XXX: There could be a list of address-mask pairs, but // we consider only the first one. // IPv6 addr(cmd_ip6->addr6); IPv6 mask(cmd_ip6->mask6); src_network = IPvXNet(IPvX(addr), mask.mask_len()); break; } case O_IP6_DST_MASK: { // The IPv6 destination address and mask ipfw_insn_ip6* cmd_ip6 = reinterpret_cast(cmd); // // XXX: There could be a list of address-mask pairs, but // we consider only the first one. // IPv6 addr(cmd_ip6->addr6); IPv6 mask(cmd_ip6->mask6); dst_network = IPvXNet(IPvX(addr), mask.mask_len()); break; } case O_IP_SRCPORT: { // The TCP/UDP source port range ipfw_insn_u16* cmd_u16 = reinterpret_cast(cmd); // // XXX: There could be a list of port pairs, but // we consider only the first one. // src_port_begin = cmd_u16->ports[0]; src_port_end = cmd_u16->ports[1]; break; } case O_IP_DSTPORT: { // The TCP/UDP destination port range ipfw_insn_u16* cmd_u16 = reinterpret_cast(cmd); // // XXX: There could be a list of port pairs, but // we consider only the first one. // dst_port_begin = cmd_u16->ports[0]; dst_port_end = cmd_u16->ports[1]; break; } case O_PROTO: { // The protocol ip_protocol = cmd->arg1; break; } case O_VIA: { // The interface and vif ipfw_insn_if* cmd_if = reinterpret_cast(cmd); if (cmd_if->name[0] == '\0') { // // XXX: The "via" could be an IP address, but for now // we support only interface names. // XLOG_WARNING("Ignoring \"via\" firewall option that " "is not an interface name"); break; } ifname = string(cmd_if->name); vifname = ifname; // XXX: ifname == vifname break; } default: break; } } // Ignore entries that don't match the address family bool accept_entry = false; switch (family) { case AF_INET: if (have_ipv4) accept_entry = true; break; #ifdef HAVE_IPV6 case AF_INET6: if (have_ipv6) accept_entry = true; break; #endif default: XLOG_FATAL("Unknown family %d", family); break; } if (! accept_entry) continue; // Add the entry to the list FirewallEntry firewall_entry(rule_number, ifname, vifname, src_network, dst_network, ip_protocol, src_port_begin, src_port_end, dst_port_begin, dst_port_end, action); firewall_entry_list.push_back(firewall_entry); } return (XORP_OK); } #endif // HAVE_FIREWALL_IPFW2 xorp/fea/data_plane/firewall/firewall_get_pf.hh0000664000076400007640000000633711421137511021767 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/data_plane/firewall/firewall_get_pf.hh,v 1.3 2008/10/02 21:57:03 bms Exp $ #ifndef __FEA_DATA_PLANE_FIREWALL_FIREWALL_GET_PF_HH__ #define __FEA_DATA_PLANE_FIREWALL_FIREWALL_GET_PF_HH__ #include "fea/firewall_get.hh" class FirewallGetPf : public FirewallGet { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ FirewallGetPf(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~FirewallGetPf(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg); /** * Obtain the IPv4 firewall table. * * @param firewall_entry_list the return-by-reference list with all * entries in the IPv4 firewall table. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int get_table4(list& firewall_entry_list, string& error_msg); /** * Obtain the IPv6 firewall table. * * @param firewall_entry_list the return-by-reference list with all * entries in the IPv6 firewall table. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int get_table6(list& firewall_entry_list, string& error_msg); private: /** * Obtain the firewall table for a specific address family. * * @param family the address family. * @param firewall_entry_list the return-by-reference list with all * entries in the firewall table for the given address family. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int get_table(int family, list& firewall_entry_list, string& error_msg); int _fd; // The file descriptor for firewall access static const string _pf_device_name; // The PF device name }; #endif // __FEA_DATA_PLANE_FIREWALL_FIREWALL_GET_PF_HH__ xorp/fea/data_plane/firewall/firewall_get_netfilter.cc0000664000076400007640000003225211421137511023337 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libcomm/comm_api.h" #ifdef HAVE_SYS_SYSCTL_H #include #endif #ifdef HAVE_SYS_IOCTL_H #include #endif #ifdef HAVE_SYS_SOCKET_H #include #endif #ifdef HAVE_SYS_SOCKIO_H #include #endif #ifdef HAVE_NETINET_IN_SYSTM_H #include #endif #ifdef HAVE_NETINET_IP_H #include #endif #ifdef HAVE_LINUX_NETFILTER_IPV4_IP_TABLES_H #include #endif #ifdef HAVE_LINUX_NETFILTER_IPV6_IP6_TABLES_H #include #endif #include "fea/firewall_manager.hh" #include "firewall_get_netfilter.hh" // // Get information about firewall entries from the underlying system. // // The mechanism to obtain the information is NETFILTER. // #ifdef HAVE_FIREWALL_NETFILTER const string FirewallGetNetfilter::_netfilter_table_name = "filter"; FirewallGetNetfilter::FirewallGetNetfilter(FeaDataPlaneManager& fea_data_plane_manager) : FirewallGet(fea_data_plane_manager), _s4(-1), _s6(-1) { } FirewallGetNetfilter::~FirewallGetNetfilter() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the NETFILTER mechanism to get " "information about firewall entries from the underlying " "system: %s", error_msg.c_str()); } } int FirewallGetNetfilter::start(string& error_msg) { if (_is_running) return (XORP_OK); // // Check if we have the necessary permission. // If not, print a warning and continue without firewall. // if (geteuid() != 0) { XLOG_WARNING("Cannot start the NETFILTER firewall mechanism: " "must be root; continuing without firewall setup"); return (XORP_OK); } // // Open a raw IPv4 socket that NETFILTER uses for communication // _s4 = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); if (_s4 < 0) { error_msg = c_format("Could not open a raw IPv4 socket for NETFILTER " "firewall: %s", strerror(errno)); return (XORP_ERROR); } #ifdef HAVE_IPV6 // // Open a raw IPv6 socket that NETFILTER uses for communication // _s6 = socket(AF_INET6, SOCK_RAW, IPPROTO_RAW); if (_s6 < 0) { error_msg = c_format("Could not open a raw IPv6 socket for NETFILTER " "firewall: %s", strerror(errno)); comm_close(_s4); _s4 = -1; return (XORP_ERROR); } #endif // HAVE_IPV6 _is_running = true; return (XORP_OK); } int FirewallGetNetfilter::stop(string& error_msg) { int ret_value4 = XORP_OK; int ret_value6 = XORP_OK; if (! _is_running) return (XORP_OK); if (_s4 >= 0) { ret_value4 = comm_close(_s4); _s4 = -1; if (ret_value4 != XORP_OK) { error_msg = c_format("Could not close raw IPv4 socket for " "NETFILTER firewall: %s", strerror(errno)); } } if (_s6 >= 0) { ret_value6 = comm_close(_s6); _s6 = -1; if ((ret_value6 != XORP_OK) && (ret_value4 == XORP_OK)) { error_msg = c_format("Could not close raw IPv6 socket for " "NETFILTER firewall: %s", strerror(errno)); } } if ((ret_value4 != XORP_OK) || (ret_value6 != XORP_OK)) return (XORP_ERROR); _is_running = false; return (XORP_OK); } int FirewallGetNetfilter::get_table4(list& firewall_entry_list, string& error_msg) { struct ipt_getinfo info4; struct ipt_get_entries* entries4_p; socklen_t socklen; memset(&info4, 0, sizeof(info4)); strlcpy(info4.name, _netfilter_table_name.c_str(), sizeof(info4.name)); // // Get information about the size of the table // socklen = sizeof(info4); if (getsockopt(_s4, IPPROTO_IP, IPT_SO_GET_INFO, &info4, &socklen) < 0) { error_msg = c_format("Could not get the NETFILTER IPv4 firewall table: " "%s", strerror(errno)); return (XORP_ERROR); } // // Get the entries // vector entries4(sizeof(*entries4_p) + info4.size); socklen = entries4.size(); if (getsockopt(_s4, IPPROTO_IP, IPT_SO_GET_ENTRIES, &entries4, &socklen) < 0) { error_msg = c_format("Could not get the NETFILTER IPv4 firewall table " "entries: %s", strerror(errno)); return (XORP_ERROR); } // // Parse buffer contents for all rules // for (size_t i = 0; i < entries4.size(); ) { struct ipt_entry* ipt; struct ipt_entry_match* iem; struct ipt_entry_target* iet; struct ipt_standard_target* ist; uint8_t* ptr; ipt = reinterpret_cast(&entries4[i]); i += ipt->next_offset; // // Extract various information from the rule // uint32_t rule_number = FirewallEntry::RULE_NUMBER_DEFAULT; string ifname, vifname; IPvXNet src_network = IPvXNet(IPv4::af()); IPvXNet dst_network = IPvXNet(IPv4::af()); uint32_t ip_protocol = FirewallEntry::IP_PROTOCOL_ANY; uint32_t src_port_begin = FirewallEntry::PORT_MIN; uint32_t src_port_end = FirewallEntry::PORT_MAX; uint32_t dst_port_begin = FirewallEntry::PORT_MIN; uint32_t dst_port_end = FirewallEntry::PORT_MAX; FirewallEntry::Action action = FirewallEntry::ACTION_INVALID; // // Get the rule number // // XXX: Do nothing, because NETFILTER doesn't have rule numbers // at the kernel API level. // // Get the action // ptr = reinterpret_cast(ipt); ptr += ipt->target_offset; iet = reinterpret_cast(ptr); // XXX: Only standard targets are processed if (strncmp(iet->u.user.name, IPT_STANDARD_TARGET, sizeof(iet->u.user.name)) != 0) { XLOG_WARNING("Ingoring firewall entry with non-standard target %s", iet->u.user.name); continue; } ist = reinterpret_cast(ptr); switch (ist->verdict) { case (-NF_ACCEPT - 1): action = FirewallEntry::ACTION_PASS; break; case (-NF_DROP - 1): action = FirewallEntry::ACTION_DROP; break; default: XLOG_WARNING("Ingoring firewall entry with action %u: " "unknown action", ist->verdict); break; } // // Get the protocol // if (ipt->ip.proto != 0) ip_protocol = ipt->ip.proto; // // Get the source and destination network addresses // IPv4 src_addr, dst_addr, src_mask, dst_mask; src_addr.copy_in(ipt->ip.src); dst_addr.copy_in(ipt->ip.dst); src_mask.copy_in(ipt->ip.smsk); src_mask.copy_in(ipt->ip.dmsk); src_network = IPvXNet(IPvX(src_addr), src_mask.mask_len()); dst_network = IPvXNet(IPvX(dst_addr), dst_mask.mask_len()); // // Get the source and destination port number range // for (size_t j = sizeof(*ipt); j < ipt->target_offset; ) { ptr = reinterpret_cast(ipt); ptr += j; iem = reinterpret_cast(ptr); j += iem->u.match_size; switch (ipt->ip.proto) { case IPPROTO_TCP: { struct ipt_tcp* ip_tcp; ip_tcp = reinterpret_cast(iem->data); src_port_begin = ip_tcp->spts[0]; src_port_end = ip_tcp->spts[1]; dst_port_begin = ip_tcp->dpts[0]; dst_port_end = ip_tcp->dpts[1]; break; } case IPPROTO_UDP: { struct ipt_udp* ip_udp; ip_udp = reinterpret_cast(iem->data); src_port_begin = ip_udp->spts[0]; src_port_end = ip_udp->spts[1]; dst_port_begin = ip_udp->dpts[0]; dst_port_end = ip_udp->dpts[1]; break; } default: break; } } // // The interface and vif // if (ipt->ip.iniface[0] != '\0') { ifname = string(ipt->ip.iniface); vifname = ifname; // XXX: ifname == vifname } // Add the entry to the list FirewallEntry firewall_entry(rule_number, ifname, vifname, src_network, dst_network, ip_protocol, src_port_begin, src_port_end, dst_port_begin, dst_port_end, action); firewall_entry_list.push_back(firewall_entry); } return (XORP_OK); } int FirewallGetNetfilter::get_table6(list& firewall_entry_list, string& error_msg) { struct ip6t_getinfo info6; struct ip6t_get_entries* entries6_p; socklen_t socklen; memset(&info6, 0, sizeof(info6)); strlcpy(info6.name, _netfilter_table_name.c_str(), sizeof(info6.name)); // // Get information about the size of the table // socklen = sizeof(info6); if (getsockopt(_s6, IPPROTO_IPV6, IP6T_SO_GET_INFO, &info6, &socklen) < 0) { error_msg = c_format("Could not get the NETFILTER IPv6 firewall table: " "%s", strerror(errno)); return (XORP_ERROR); } // // Get the entries // vector entries6(sizeof(*entries6_p) + info6.size); socklen = entries6.size(); if (getsockopt(_s6, IPPROTO_IPV6, IP6T_SO_GET_ENTRIES, &entries6, &socklen) < 0) { error_msg = c_format("Could not get the NETFILTER IPv6 firewall table " "entries: %s", strerror(errno)); return (XORP_ERROR); } // // Parse buffer contents for all rules // for (size_t i = 0; i < entries6.size(); ) { struct ip6t_entry* ipt; struct ip6t_entry_match* iem; struct ip6t_entry_target* iet; struct ip6t_standard_target* ist; uint8_t* ptr; ipt = reinterpret_cast(&entries6[i]); i += ipt->next_offset; // // Extract various information from the rule // uint32_t rule_number = FirewallEntry::RULE_NUMBER_DEFAULT; string ifname, vifname; IPvXNet src_network = IPvXNet(IPv6::af()); IPvXNet dst_network = IPvXNet(IPv6::af()); uint32_t ip_protocol = FirewallEntry::IP_PROTOCOL_ANY; uint32_t src_port_begin = FirewallEntry::PORT_MIN; uint32_t src_port_end = FirewallEntry::PORT_MAX; uint32_t dst_port_begin = FirewallEntry::PORT_MIN; uint32_t dst_port_end = FirewallEntry::PORT_MAX; FirewallEntry::Action action = FirewallEntry::ACTION_INVALID; // // Get the rule number // // XXX: Do nothing, because NETFILTER doesn't have rule numbers // at the kernel API level. // // Get the action // ptr = reinterpret_cast(ipt); ptr += ipt->target_offset; iet = reinterpret_cast(ptr); // XXX: Only standard targets are processed if (strncmp(iet->u.user.name, IP6T_STANDARD_TARGET, sizeof(iet->u.user.name)) != 0) { XLOG_WARNING("Ingoring firewall entry with non-standard target %s", iet->u.user.name); continue; } ist = reinterpret_cast(iet->data); switch (ist->verdict) { case (-NF_ACCEPT - 1): action = FirewallEntry::ACTION_PASS; break; case (-NF_DROP - 1): action = FirewallEntry::ACTION_DROP; break; default: XLOG_WARNING("Ingoring firewall entry with action %u: " "unknown action", ist->verdict); break; } // // Get the protocol // if (ipt->ipv6.proto != 0) ip_protocol = ipt->ipv6.proto; // // Get the source and destination network addresses // IPv6 src_addr, dst_addr, src_mask, dst_mask; src_addr.copy_in(ipt->ipv6.src); dst_addr.copy_in(ipt->ipv6.dst); src_mask.copy_in(ipt->ipv6.smsk); src_mask.copy_in(ipt->ipv6.dmsk); src_network = IPvXNet(IPvX(src_addr), src_mask.mask_len()); dst_network = IPvXNet(IPvX(dst_addr), dst_mask.mask_len()); // // Get the source and destination port number range // for (size_t j = sizeof(*ipt); j < ipt->target_offset; ) { ptr = reinterpret_cast(ipt); ptr += j; iem = reinterpret_cast(ptr); j += iem->u.match_size; switch (ipt->ipv6.proto) { case IPPROTO_TCP: { struct ip6t_tcp* ip_tcp; ip_tcp = reinterpret_cast(iem->data); src_port_begin = ip_tcp->spts[0]; src_port_end = ip_tcp->spts[1]; dst_port_begin = ip_tcp->dpts[0]; dst_port_end = ip_tcp->dpts[1]; break; } case IPPROTO_UDP: { struct ip6t_udp* ip_udp; ip_udp = reinterpret_cast(iem->data); src_port_begin = ip_udp->spts[0]; src_port_end = ip_udp->spts[1]; dst_port_begin = ip_udp->dpts[0]; dst_port_end = ip_udp->dpts[1]; break; } default: break; } } // // The interface and vif // if (ipt->ipv6.iniface[0] != '\0') { ifname = string(ipt->ipv6.iniface); vifname = ifname; // XXX: ifname == vifname } // Add the entry to the list FirewallEntry firewall_entry(rule_number, ifname, vifname, src_network, dst_network, ip_protocol, src_port_begin, src_port_end, dst_port_begin, dst_port_end, action); firewall_entry_list.push_back(firewall_entry); } return (XORP_OK); } #endif // HAVE_FIREWALL_NETFILTER xorp/fea/data_plane/firewall/firewall_set_ipfw2.hh0000664000076400007640000001132611540224222022415 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/data_plane/firewall/firewall_set_ipfw2.hh,v 1.5 2008/10/02 21:57:03 bms Exp $ #ifndef __FEA_DATA_PLANE_FIREWALL_FIREWALL_SET_IPFW2_HH__ #define __FEA_DATA_PLANE_FIREWALL_FIREWALL_SET_IPFW2_HH__ #include "fea/firewall_set.hh" class FirewallSetIpfw2 : public FirewallSet { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ FirewallSetIpfw2(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~FirewallSetIpfw2(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg); /** * Update the firewall entries by pushing them into the underlying system. * * @param added_entries the entries to add. * @param replaced_entries the entries to replace. * @param deleted_entries the deleted entries. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int update_entries(const list& added_entries, const list& replaced_entries, const list& deleted_entries, string& error_msg); /** * Set the IPv4 firewall table. * * @param firewall_entry_list the list with all entries to install into * the IPv4 firewall table. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int set_table4(const list& firewall_entry_list, string& error_msg); /** * Delete all entries in the IPv4 firewall table. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int delete_all_entries4(string& error_msg); /** * Set the IPv6 firewall table. * * @param firewall_entry_list the list with all entries to install into * the IPv6 firewall table. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int set_table6(const list& firewall_entry_list, string& error_msg); /** * Delete all entries in the IPv6 firewall table. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int delete_all_entries6(string& error_msg); private: /** * Add a single firewall entry. * * @param firewall_entry the entry to add. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int add_entry(const FirewallEntry& firewall_entry, string& error_msg); /** * Replace a single firewall entry. * * @param firewall_entry the replacement entry. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int replace_entry(const FirewallEntry& firewall_entry, string& error_msg); /** * Delete a single firewall entry. * * @param firewall_entry the entry to delete. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int delete_entry(const FirewallEntry& firewall_entry, string& error_msg); int _s4; // The socket for firewall access uint32_t _saved_autoinc_step; }; #endif // __FEA_DATA_PLANE_FIREWALL_FIREWALL_SET_IPFW2_HH__ xorp/fea/data_plane/firewall/firewall_set_dummy.cc0000664000076400007640000001242511421137511022512 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "fea/firewall_manager.hh" #include "firewall_set_dummy.hh" // // Set firewall information into the underlying system. // // The mechanism to set the information is Dummy (for testing purpose). // FirewallSetDummy::FirewallSetDummy(FeaDataPlaneManager& fea_data_plane_manager) : FirewallSet(fea_data_plane_manager) { } FirewallSetDummy::~FirewallSetDummy() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the Dummy mechanism to set " "firewall information into the underlying system: %s", error_msg.c_str()); } } int FirewallSetDummy::start(string& error_msg) { UNUSED(error_msg); if (_is_running) return (XORP_OK); _is_running = true; return (XORP_OK); } int FirewallSetDummy::stop(string& error_msg) { UNUSED(error_msg); if (! _is_running) return (XORP_OK); _is_running = false; return (XORP_OK); } int FirewallSetDummy::update_entries(const list& added_entries, const list& replaced_entries, const list& deleted_entries, string& error_msg) { list::const_iterator iter; // // The entries to add // for (iter = added_entries.begin(); iter != added_entries.end(); ++iter) { const FirewallEntry& firewall_entry = *iter; if (add_entry(firewall_entry, error_msg) != XORP_OK) return (XORP_ERROR); } // // The entries to replace // for (iter = replaced_entries.begin(); iter != replaced_entries.end(); ++iter) { const FirewallEntry& firewall_entry = *iter; if (replace_entry(firewall_entry, error_msg) != XORP_OK) return (XORP_ERROR); } // // The entries to delete // for (iter = deleted_entries.begin(); iter != deleted_entries.end(); ++iter) { const FirewallEntry& firewall_entry = *iter; if (delete_entry(firewall_entry, error_msg) != XORP_OK) return (XORP_ERROR); } return (XORP_OK); } int FirewallSetDummy::set_table4(const list& firewall_entry_list, string& error_msg) { list empty_list; if (delete_all_entries4(error_msg) != XORP_OK) return (XORP_ERROR); return (update_entries(firewall_entry_list, empty_list, empty_list, error_msg)); } int FirewallSetDummy::delete_all_entries4(string& error_msg) { UNUSED(error_msg); _firewall_entries4.clear(); return (XORP_OK); } int FirewallSetDummy::set_table6(const list& firewall_entry_list, string& error_msg) { list empty_list; if (delete_all_entries6(error_msg) != XORP_OK) return (XORP_ERROR); return (update_entries(firewall_entry_list, empty_list, empty_list, error_msg)); } int FirewallSetDummy::delete_all_entries6(string& error_msg) { UNUSED(error_msg); _firewall_entries6.clear(); return (XORP_OK); } int FirewallSetDummy::add_entry(const FirewallEntry& firewall_entry, string& error_msg) { FirewallTrie::iterator iter; FirewallTrie* ftp = NULL; uint32_t key = firewall_entry.rule_number(); // XXX: the map key UNUSED(error_msg); if (firewall_entry.is_ipv4()) ftp = &_firewall_entries4; else ftp = &_firewall_entries6; // // XXX: If the entry already exists, then just update it. // Note that the replace_entry() implementation relies on this. // iter = ftp->find(key); if (iter == ftp->end()) { ftp->insert(make_pair(key, firewall_entry)); } else { FirewallEntry& fe_tmp = iter->second; fe_tmp = firewall_entry; } return (XORP_OK); } int FirewallSetDummy::replace_entry(const FirewallEntry& firewall_entry, string& error_msg) { // // XXX: The add_entry() method implementation covers the replace_entry() // semantic as well. // return (add_entry(firewall_entry, error_msg)); } int FirewallSetDummy::delete_entry(const FirewallEntry& firewall_entry, string& error_msg) { FirewallTrie::iterator iter; FirewallTrie* ftp = NULL; uint32_t key = firewall_entry.rule_number(); // XXX: the map key if (firewall_entry.is_ipv4()) ftp = &_firewall_entries4; else ftp = &_firewall_entries6; // Find the entry iter = ftp->find(key); if (iter == ftp->end()) { error_msg = c_format("Entry not found"); return (XORP_ERROR); } ftp->erase(iter); return (XORP_OK); } xorp/fea/data_plane/SConscript0000664000076400007640000000203311540225522016511 0ustar greearbgreearb# Copyright (c) 2009-2011 XORP, Inc and Others # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # $XORP$ import os Import('env') subdirs = [ 'ifconfig', 'fibconfig', 'io', 'control_socket', 'managers' ] if not (env.has_key('disable_fw') and env['disable_fw']): subdirs.append('firewall') SConscript(dirs = subdirs, exports='env') xorp/fea/data_plane/managers/0000775000076400007640000000000011703345405016302 5ustar greearbgreearbxorp/fea/data_plane/managers/fea_data_plane_manager_linux.hh0000664000076400007640000001012611540225524024435 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2007-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/data_plane/managers/fea_data_plane_manager_linux.hh,v 1.8 2008/10/02 21:57:13 bms Exp $ #ifndef __FEA_DATA_PLANE_MANAGERS_FEA_DATA_PLANE_MANAGER_LINUX_HH__ #define __FEA_DATA_PLANE_MANAGERS_FEA_DATA_PLANE_MANAGER_LINUX_HH__ #include "fea/fea_data_plane_manager.hh" class IfConfigGetIoctl; /** * FEA data plane manager class for Linux. */ class FeaDataPlaneManagerLinux : public FeaDataPlaneManager { public: /** * Constructor. * * @param fea_node the @ref FeaNode this manager belongs to. */ FeaDataPlaneManagerLinux(FeaNode& fea_node); /** * Virtual destructor. */ virtual ~FeaDataPlaneManagerLinux(); /** * Load the plugins. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int load_plugins(string& error_msg); /** * Unload the plugins. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int unload_plugins(string& error_msg); /** * Register the plugins. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int register_plugins(string& error_msg); /** * Allocate IoLink plugin instance. * * @param iftree the interface tree to use. * @param if_name the interface name. * @param vif_name the vif name. * @param ether_type the EtherType protocol number. If it is 0 then * it is unused. * @param filter_program the option filter program to be applied on the * received packets. The program uses tcpdump(1) style expression. * @return a new instance of @ref IoLink plugin on success, otherwise NULL. */ IoLink* allocate_io_link(const IfTree& iftree, const string& if_name, const string& vif_name, uint16_t ether_type, const string& filter_program); /** * Allocate IoIp plugin instance. * * @param iftree the interface tree to use. * @param family the address family (AF_INET or AF_INET6 for IPv4 and IPv6 * respectively). * @param ip_protocol the IP protocol number (IPPROTO_*). * @return a new instance of @ref IoIp plugin on success, otherwise NULL. */ IoIp* allocate_io_ip(const IfTree& iftree, int family, uint8_t ip_protocol); /** * Allocate IoTcpUdp plugin instance. * * @param iftree the interface tree to use. * @param family the address family (AF_INET or AF_INET6 for IPv4 and IPv6 * respectively). * @param is_tcp if true allocate a TCP entry, otherwise UDP. * @return a new instance of @ref IoTcpUdp plugin on success, * otherwise NULL. */ IoTcpUdp* allocate_io_tcpudp(const IfTree& iftree, int family, bool is_tcp); #if defined(HAVE_PROC_LINUX) && defined(HAVE_IOCTL_SIOCGIFCONF) and !defined(HAVE_NETLINK_SOCKETS) /** * Get the IfConfigGetIoctl plugin. * * @return the @ref IfConfigGetIoctl plugin. */ IfConfigGetIoctl* ifconfig_get_ioctl() { return (_ifconfig_get_ioctl); } #endif private: IfConfigGetIoctl* _ifconfig_get_ioctl; }; #endif // __FEA_DATA_PLANE_MANAGERS_FEA_DATA_PLANE_MANAGER_LINUX_HH__ xorp/fea/data_plane/managers/fea_data_plane_manager_click.cc0000664000076400007640000005263211703345405024363 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2007-2012 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include #ifdef XORP_USE_CLICK #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "fea/ifconfig.hh" #include "fea/fibconfig.hh" #include "fea/firewall_manager.hh" #include "fea/data_plane/ifconfig/ifconfig_property_dummy.hh" #include "fea/data_plane/ifconfig/ifconfig_get_click.hh" #include "fea/data_plane/ifconfig/ifconfig_set_click.hh" #include "fea/data_plane/firewall/firewall_get_dummy.hh" #include "fea/data_plane/firewall/firewall_set_dummy.hh" #include "fea/data_plane/fibconfig/fibconfig_forwarding_dummy.hh" #include "fea/data_plane/fibconfig/fibconfig_entry_get_click.hh" #include "fea/data_plane/fibconfig/fibconfig_entry_set_click.hh" #include "fea/data_plane/fibconfig/fibconfig_table_get_click.hh" #include "fea/data_plane/fibconfig/fibconfig_table_set_click.hh" #include "fea/data_plane/io/io_link_dummy.hh" #include "fea/data_plane/io/io_ip_dummy.hh" #include "fea/data_plane/io/io_tcpudp_dummy.hh" #include "fea_data_plane_manager_click.hh" // // FEA data plane manager class for Click: // http://www.read.cs.ucla.edu/click/ // FeaDataPlaneManagerClick::FeaDataPlaneManagerClick(FeaNode& fea_node) : FeaDataPlaneManager(fea_node, "Click"), _ifconfig_property_dummy(NULL), _ifconfig_get_click(NULL), _ifconfig_set_click(NULL), _firewall_get_dummy(NULL), _firewall_set_dummy(NULL), _fibconfig_forwarding_dummy(NULL), _fibconfig_entry_get_click(NULL), _fibconfig_entry_set_click(NULL), _fibconfig_table_get_click(NULL), _fibconfig_table_set_click(NULL) { } FeaDataPlaneManagerClick::~FeaDataPlaneManagerClick() { } int FeaDataPlaneManagerClick::load_plugins(string& error_msg) { UNUSED(error_msg); if (_is_loaded_plugins) return (XORP_OK); XLOG_ASSERT(_ifconfig_property_dummy == NULL); XLOG_ASSERT(_ifconfig_get_click == NULL); XLOG_ASSERT(_ifconfig_set_click == NULL); XLOG_ASSERT(_firewall_get_dummy == NULL); XLOG_ASSERT(_firewall_set_dummy == NULL); XLOG_ASSERT(_fibconfig_forwarding_dummy == NULL); XLOG_ASSERT(_fibconfig_entry_get_click == NULL); XLOG_ASSERT(_fibconfig_entry_set_click == NULL); XLOG_ASSERT(_fibconfig_table_get_click == NULL); XLOG_ASSERT(_fibconfig_table_set_click == NULL); // // Load the plugins // // // TODO: XXX: For the time being some of the plugins // used by Click are dummy. // _ifconfig_property_dummy = new IfConfigPropertyDummy(*this); _ifconfig_get_click = new IfConfigGetClick(*this); _ifconfig_set_click = new IfConfigSetClick(*this); // _firewall_get_dummy = new FirewallGetDummy(*this); _firewall_set_dummy = new FirewallSetDummy(*this); // _fibconfig_forwarding_dummy = new FibConfigForwardingDummy(*this); _fibconfig_entry_get_click = new FibConfigEntryGetClick(*this); _fibconfig_entry_set_click = new FibConfigEntrySetClick(*this); _fibconfig_table_get_click = new FibConfigTableGetClick(*this); _fibconfig_table_set_click = new FibConfigTableSetClick(*this); // _ifconfig_property = _ifconfig_property_dummy; _ifconfig_get = _ifconfig_get_click; _ifconfig_set = _ifconfig_set_click; _firewall_get = _firewall_get_dummy; _firewall_set = _firewall_set_dummy; _fibconfig_forwarding = _fibconfig_forwarding_dummy; _fibconfig_entry_get = _fibconfig_entry_get_click; _fibconfig_entry_set = _fibconfig_entry_set_click; _fibconfig_table_get = _fibconfig_table_get_click; _fibconfig_table_set = _fibconfig_table_set_click; _is_loaded_plugins = true; return (XORP_OK); } int FeaDataPlaneManagerClick::unload_plugins(string& error_msg) { if (! _is_loaded_plugins) return (XORP_OK); XLOG_ASSERT(_ifconfig_property_dummy != NULL); XLOG_ASSERT(_ifconfig_get_click != NULL); XLOG_ASSERT(_ifconfig_set_click != NULL); XLOG_ASSERT(_firewall_get_dummy != NULL); XLOG_ASSERT(_firewall_set_dummy != NULL); XLOG_ASSERT(_fibconfig_forwarding_dummy != NULL); XLOG_ASSERT(_fibconfig_entry_get_click != NULL); XLOG_ASSERT(_fibconfig_entry_set_click != NULL); XLOG_ASSERT(_fibconfig_table_get_click != NULL); XLOG_ASSERT(_fibconfig_table_set_click != NULL); // // Unload the plugins // if (FeaDataPlaneManager::unload_plugins(error_msg) != XORP_OK) return (XORP_ERROR); // // Reset the state // _ifconfig_property_dummy = NULL; _ifconfig_get_click = NULL; _ifconfig_set_click = NULL; _firewall_get_dummy = NULL; _firewall_set_dummy = NULL; _fibconfig_forwarding_dummy = NULL; _fibconfig_entry_get_click = NULL; _fibconfig_entry_set_click = NULL; _fibconfig_table_get_click = NULL; _fibconfig_table_set_click = NULL; return (XORP_OK); } int FeaDataPlaneManagerClick::register_plugins(string& error_msg) { string dummy_error_msg; if (_ifconfig_property != NULL) { if (ifconfig().register_ifconfig_property(_ifconfig_property, false) != XORP_OK) { error_msg = c_format("Cannot register IfConfigProperty plugin " "for data plane manager %s", manager_name().c_str()); unregister_plugins(dummy_error_msg); return (XORP_ERROR); } } if (_ifconfig_get != NULL) { if (ifconfig().register_ifconfig_get(_ifconfig_get, false) != XORP_OK) { error_msg = c_format("Cannot register IfConfigGet plugin " "for data plane manager %s", manager_name().c_str()); unregister_plugins(dummy_error_msg); return (XORP_ERROR); } } if (_ifconfig_set != NULL) { if (ifconfig().register_ifconfig_set(_ifconfig_set, false) != XORP_OK) { error_msg = c_format("Cannot register IfConfigSet plugin " "for data plane manager %s", manager_name().c_str()); unregister_plugins(dummy_error_msg); return (XORP_ERROR); } } if (_firewall_get != NULL) { if (firewall_manager().register_firewall_get(_firewall_get, false) != XORP_OK) { error_msg = c_format("Cannot register FirewallGet plugin " "for data plane manager %s", manager_name().c_str()); unregister_plugins(dummy_error_msg); return (XORP_ERROR); } } if (_firewall_set != NULL) { if (firewall_manager().register_firewall_set(_firewall_set, false) != XORP_OK) { error_msg = c_format("Cannot register FirewallSet plugin " "for data plane manager %s", manager_name().c_str()); unregister_plugins(dummy_error_msg); return (XORP_ERROR); } } if (_fibconfig_forwarding != NULL) { if (fibconfig().register_fibconfig_forwarding(_fibconfig_forwarding, false) != XORP_OK) { error_msg = c_format("Cannot register FibConfigForwarding plugin " "for data plane manager %s", manager_name().c_str()); unregister_plugins(dummy_error_msg); return (XORP_ERROR); } } if (_fibconfig_entry_get != NULL) { // // XXX: We register the FibConfigEntryGet plugin as the exclusive "get" // method, because Click should be the ultimate place to read the route // info from. // The kernel itself may contain some left-over stuff. // if (fibconfig().register_fibconfig_entry_get(_fibconfig_entry_get, true) != XORP_OK) { error_msg = c_format("Cannot register FibConfigEntryGet plugin " "for data plane manager %s", manager_name().c_str()); unregister_plugins(dummy_error_msg); return (XORP_ERROR); } } if (_fibconfig_entry_set != NULL) { XLOG_ASSERT(_fibconfig_entry_set_click != NULL); bool is_exclusive = true; if (_fibconfig_entry_set_click->is_duplicate_routes_to_kernel_enabled()) is_exclusive = false; if (fibconfig().register_fibconfig_entry_set(_fibconfig_entry_set, is_exclusive) != XORP_OK) { error_msg = c_format("Cannot register FibConfigEntrySet plugin " "for data plane manager %s", manager_name().c_str()); unregister_plugins(dummy_error_msg); return (XORP_ERROR); } } if (_fibconfig_table_get != NULL) { // // XXX: We register the FibConfigTableGet plugin as the exclusive "get" // method, because Click should be the ultimate place to read the route // info from. // The kernel itself may contain some left-over stuff. // if (fibconfig().register_fibconfig_table_get(_fibconfig_table_get, true) != XORP_OK) { error_msg = c_format("Cannot register FibConfigTableGet plugin " "for data plane manager %s", manager_name().c_str()); unregister_plugins(dummy_error_msg); return (XORP_ERROR); } } if (_fibconfig_table_set != NULL) { XLOG_ASSERT(_fibconfig_table_set_click != NULL); bool is_exclusive = true; if (_fibconfig_table_set_click->is_duplicate_routes_to_kernel_enabled()) is_exclusive = false; if (fibconfig().register_fibconfig_table_set(_fibconfig_table_set, is_exclusive) != XORP_OK) { error_msg = c_format("Cannot register FibConfigTableSet plugin " "for data plane manager %s", manager_name().c_str()); unregister_plugins(dummy_error_msg); return (XORP_ERROR); } } return (XORP_OK); } IoLink* FeaDataPlaneManagerClick::allocate_io_link(const IfTree& iftree, const string& if_name, const string& vif_name, uint16_t ether_type, const string& filter_program) { IoLink* io_link = NULL; // // TODO: XXX: For the time being Click uses the IoLinkDummy plugin. // io_link = new IoLinkDummy(*this, iftree, if_name, vif_name, ether_type, filter_program); _io_link_list.push_back(io_link); return (io_link); } IoIp* FeaDataPlaneManagerClick::allocate_io_ip(const IfTree& iftree, int family, uint8_t ip_protocol) { IoIp* io_ip = NULL; // // TODO: XXX: For the time being Click uses the IoIpDummy plugin. // io_ip = new IoIpDummy(*this, iftree, family, ip_protocol); _io_ip_list.push_back(io_ip); return (io_ip); } IoTcpUdp* FeaDataPlaneManagerClick::allocate_io_tcpudp(const IfTree& iftree, int family, bool is_tcp) { IoTcpUdp* io_tcpudp = NULL; // // TODO: XXX: For the time being Click uses the IoTcpUdpDummy plugin. // io_tcpudp = new IoTcpUdpDummy(*this, iftree, family, is_tcp); _io_tcpudp_list.push_back(io_tcpudp); return (io_tcpudp); } int FeaDataPlaneManagerClick::enable_click(bool enable, string& error_msg) { if (! _is_loaded_plugins) { error_msg = c_format("Data plane manager %s plugins are not loaded", manager_name().c_str()); return (XORP_ERROR); } _ifconfig_get_click->enable_click(enable); _ifconfig_set_click->enable_click(enable); _fibconfig_entry_get_click->enable_click(enable); _fibconfig_entry_set_click->enable_click(enable); _fibconfig_table_get_click->enable_click(enable); _fibconfig_table_set_click->enable_click(enable); return (XORP_OK); } int FeaDataPlaneManagerClick::enable_kernel_click(bool enable, string& error_msg) { if (! _is_loaded_plugins) { error_msg = c_format("Data plane manager %s plugins are not loaded", manager_name().c_str()); return (XORP_ERROR); } _ifconfig_get_click->enable_kernel_click(enable); _ifconfig_set_click->enable_kernel_click(enable); _fibconfig_entry_get_click->enable_kernel_click(enable); _fibconfig_entry_set_click->enable_kernel_click(enable); _fibconfig_table_get_click->enable_kernel_click(enable); _fibconfig_table_set_click->enable_kernel_click(enable); return (XORP_OK); } int FeaDataPlaneManagerClick::set_kernel_click_config_generator_file( const string& v, string& error_msg) { if (! _is_loaded_plugins) { error_msg = c_format("Data plane manager %s plugins are not loaded", manager_name().c_str()); return (XORP_ERROR); } _ifconfig_get_click->set_kernel_click_config_generator_file(v); _ifconfig_set_click->set_kernel_click_config_generator_file(v); _fibconfig_entry_get_click->set_kernel_click_config_generator_file(v); _fibconfig_entry_set_click->set_kernel_click_config_generator_file(v); _fibconfig_table_get_click->set_kernel_click_config_generator_file(v); _fibconfig_table_set_click->set_kernel_click_config_generator_file(v); return (XORP_OK); } int FeaDataPlaneManagerClick::enable_duplicate_routes_to_kernel(bool enable, string& error_msg) { if (! _is_loaded_plugins) { error_msg = c_format("Data plane manager %s plugins are not loaded", manager_name().c_str()); return (XORP_ERROR); } _ifconfig_get_click->enable_duplicate_routes_to_kernel(enable); _ifconfig_set_click->enable_duplicate_routes_to_kernel(enable); _fibconfig_entry_get_click->enable_duplicate_routes_to_kernel(enable); _fibconfig_entry_set_click->enable_duplicate_routes_to_kernel(enable); _fibconfig_table_get_click->enable_duplicate_routes_to_kernel(enable); _fibconfig_table_set_click->enable_duplicate_routes_to_kernel(enable); return (XORP_OK); } int FeaDataPlaneManagerClick::enable_kernel_click_install_on_startup(bool enable, string& error_msg) { if (! _is_loaded_plugins) { error_msg = c_format("Data plane manager %s plugins are not loaded", manager_name().c_str()); return (XORP_ERROR); } // XXX: only IfConfigGet should install the kernel-level Click _ifconfig_get_click->enable_kernel_click_install_on_startup(enable); _ifconfig_set_click->enable_kernel_click_install_on_startup(false); _fibconfig_entry_get_click->enable_kernel_click_install_on_startup(false); _fibconfig_entry_set_click->enable_kernel_click_install_on_startup(false); _fibconfig_table_get_click->enable_kernel_click_install_on_startup(false); _fibconfig_table_set_click->enable_kernel_click_install_on_startup(false); return (XORP_OK); } int FeaDataPlaneManagerClick::set_kernel_click_modules(const list& modules, string& error_msg) { if (! _is_loaded_plugins) { error_msg = c_format("Data plane manager %s plugins are not loaded", manager_name().c_str()); return (XORP_ERROR); } _ifconfig_get_click->set_kernel_click_modules(modules); _ifconfig_set_click->set_kernel_click_modules(modules); _fibconfig_entry_get_click->set_kernel_click_modules(modules); _fibconfig_entry_set_click->set_kernel_click_modules(modules); _fibconfig_table_get_click->set_kernel_click_modules(modules); _fibconfig_table_set_click->set_kernel_click_modules(modules); return (XORP_OK); } int FeaDataPlaneManagerClick::set_kernel_click_mount_directory( const string& directory, string& error_msg) { if (! _is_loaded_plugins) { error_msg = c_format("Data plane manager %s plugins are not loaded", manager_name().c_str()); return (XORP_ERROR); } _ifconfig_get_click->set_kernel_click_mount_directory(directory); _ifconfig_set_click->set_kernel_click_mount_directory(directory); _fibconfig_entry_get_click->set_kernel_click_mount_directory(directory); _fibconfig_entry_set_click->set_kernel_click_mount_directory(directory); _fibconfig_table_get_click->set_kernel_click_mount_directory(directory); _fibconfig_table_set_click->set_kernel_click_mount_directory(directory); return (XORP_OK); } int FeaDataPlaneManagerClick::enable_user_click(bool enable, string& error_msg) { if (! _is_loaded_plugins) { error_msg = c_format("Data plane manager %s plugins are not loaded", manager_name().c_str()); return (XORP_ERROR); } _ifconfig_get_click->enable_user_click(enable); _ifconfig_set_click->enable_user_click(enable); _fibconfig_entry_get_click->enable_user_click(enable); _fibconfig_entry_set_click->enable_user_click(enable); _fibconfig_table_get_click->enable_user_click(enable); _fibconfig_table_set_click->enable_user_click(enable); return (XORP_OK); } int FeaDataPlaneManagerClick::set_user_click_command_file(const string& v, string& error_msg) { if (! _is_loaded_plugins) { error_msg = c_format("Data plane manager %s plugins are not loaded", manager_name().c_str()); return (XORP_ERROR); } _ifconfig_get_click->set_user_click_command_file(v); _ifconfig_set_click->set_user_click_command_file(v); _fibconfig_entry_get_click->set_user_click_command_file(v); _fibconfig_entry_set_click->set_user_click_command_file(v); _fibconfig_table_get_click->set_user_click_command_file(v); _fibconfig_table_set_click->set_user_click_command_file(v); return (XORP_OK); } int FeaDataPlaneManagerClick::set_user_click_command_extra_arguments( const string& v, string& error_msg) { if (! _is_loaded_plugins) { error_msg = c_format("Data plane manager %s plugins are not loaded", manager_name().c_str()); return (XORP_ERROR); } _ifconfig_get_click->set_user_click_command_extra_arguments(v); _ifconfig_set_click->set_user_click_command_extra_arguments(v); _fibconfig_entry_get_click->set_user_click_command_extra_arguments(v); _fibconfig_entry_set_click->set_user_click_command_extra_arguments(v); _fibconfig_table_get_click->set_user_click_command_extra_arguments(v); _fibconfig_table_set_click->set_user_click_command_extra_arguments(v); return (XORP_OK); } int FeaDataPlaneManagerClick::set_user_click_command_execute_on_startup( bool v, string& error_msg) { if (! _is_loaded_plugins) { error_msg = c_format("Data plane manager %s plugins are not loaded", manager_name().c_str()); return (XORP_ERROR); } // XXX: only IfConfigGet should execute the user-level Click command _ifconfig_get_click->set_user_click_command_execute_on_startup(v); _ifconfig_set_click->set_user_click_command_execute_on_startup(false); _fibconfig_entry_get_click->set_user_click_command_execute_on_startup(false); _fibconfig_entry_set_click->set_user_click_command_execute_on_startup(false); _fibconfig_table_get_click->set_user_click_command_execute_on_startup(false); _fibconfig_table_set_click->set_user_click_command_execute_on_startup(false); return (XORP_OK); } int FeaDataPlaneManagerClick::set_user_click_control_address(const IPv4& v, string& error_msg) { if (! _is_loaded_plugins) { error_msg = c_format("Data plane manager %s plugins are not loaded", manager_name().c_str()); return (XORP_ERROR); } _ifconfig_get_click->set_user_click_control_address(v); _ifconfig_set_click->set_user_click_control_address(v); _fibconfig_entry_get_click->set_user_click_control_address(v); _fibconfig_entry_set_click->set_user_click_control_address(v); _fibconfig_table_get_click->set_user_click_control_address(v); _fibconfig_table_set_click->set_user_click_control_address(v); return (XORP_OK); } int FeaDataPlaneManagerClick::set_user_click_control_socket_port(uint32_t v, string& error_msg) { if (! _is_loaded_plugins) { error_msg = c_format("Data plane manager %s plugins are not loaded", manager_name().c_str()); return (XORP_ERROR); } _ifconfig_get_click->set_user_click_control_socket_port(v); _ifconfig_set_click->set_user_click_control_socket_port(v); _fibconfig_entry_get_click->set_user_click_control_socket_port(v); _fibconfig_entry_set_click->set_user_click_control_socket_port(v); _fibconfig_table_get_click->set_user_click_control_socket_port(v); _fibconfig_table_set_click->set_user_click_control_socket_port(v); return (XORP_OK); } int FeaDataPlaneManagerClick::set_user_click_startup_config_file(const string& v, string& error_msg) { if (! _is_loaded_plugins) { error_msg = c_format("Data plane manager %s plugins are not loaded", manager_name().c_str()); return (XORP_ERROR); } _ifconfig_get_click->set_user_click_startup_config_file(v); _ifconfig_set_click->set_user_click_startup_config_file(v); _fibconfig_entry_get_click->set_user_click_startup_config_file(v); _fibconfig_entry_set_click->set_user_click_startup_config_file(v); _fibconfig_table_get_click->set_user_click_startup_config_file(v); _fibconfig_table_set_click->set_user_click_startup_config_file(v); return (XORP_OK); } int FeaDataPlaneManagerClick::set_user_click_config_generator_file(const string& v, string& error_msg) { if (! _is_loaded_plugins) { error_msg = c_format("Data plane manager %s plugins are not loaded", manager_name().c_str()); return (XORP_ERROR); } _ifconfig_get_click->set_user_click_config_generator_file(v); _ifconfig_set_click->set_user_click_config_generator_file(v); _fibconfig_entry_get_click->set_user_click_config_generator_file(v); _fibconfig_entry_set_click->set_user_click_config_generator_file(v); _fibconfig_table_get_click->set_user_click_config_generator_file(v); _fibconfig_table_set_click->set_user_click_config_generator_file(v); return (XORP_OK); } #endif // click xorp/fea/data_plane/managers/fea_data_plane_manager_dummy.hh0000664000076400007640000000703011421137511024425 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2007-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/data_plane/managers/fea_data_plane_manager_dummy.hh,v 1.10 2008/10/02 21:57:12 bms Exp $ #ifndef __FEA_DATA_PLANE_MANAGERS_FEA_DATA_PLANE_MANAGER_DUMMY_HH__ #define __FEA_DATA_PLANE_MANAGERS_FEA_DATA_PLANE_MANAGER_DUMMY_HH__ #include "fea/fea_data_plane_manager.hh" /** * FEA data plane manager class for Dummy FEA. */ class FeaDataPlaneManagerDummy : public FeaDataPlaneManager { public: /** * Constructor. * * @param fea_node the @ref FeaNode this manager belongs to. */ FeaDataPlaneManagerDummy(FeaNode& fea_node); /** * Virtual destructor. */ virtual ~FeaDataPlaneManagerDummy(); /** * Load the plugins. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int load_plugins(string& error_msg); /** * Register the plugins. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int register_plugins(string& error_msg); /** * Allocate IoLink plugin instance. * * @param iftree the interface tree to use. * @param if_name the interface name. * @param vif_name the vif name. * @param ether_type the EtherType protocol number. If it is 0 then * it is unused. * @param filter_program the option filter program to be applied on the * received packets. The program uses tcpdump(1) style expression. * @return a new instance of @ref IoLink plugin on success, otherwise NULL. */ IoLink* allocate_io_link(const IfTree& iftree, const string& if_name, const string& vif_name, uint16_t ether_type, const string& filter_program); /** * Allocate IoIp plugin instance. * * @param iftree the interface tree to use. * @param family the address family (AF_INET or AF_INET6 for IPv4 and IPv6 * respectively). * @param ip_protocol the IP protocol number (IPPROTO_*). * @return a new instance of @ref IoIp plugin on success, otherwise NULL. */ IoIp* allocate_io_ip(const IfTree& iftree, int family, uint8_t ip_protocol); /** * Allocate IoTcpUdp plugin instance. * * @param iftree the interface tree to use. * @param family the address family (AF_INET or AF_INET6 for IPv4 and IPv6 * respectively). * @param is_tcp if true allocate a TCP entry, otherwise UDP. * @return a new instance of @ref IoTcpUdp plugin on success, * otherwise NULL. */ IoTcpUdp* allocate_io_tcpudp(const IfTree& iftree, int family, bool is_tcp); private: }; #endif // __FEA_DATA_PLANE_MANAGERS_FEA_DATA_PLANE_MANAGER_DUMMY_HH__ xorp/fea/data_plane/managers/fea_data_plane_manager_dummy.cc0000664000076400007640000001235511540225524024425 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2007-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "fea/data_plane/ifconfig/ifconfig_property_dummy.hh" #include "fea/data_plane/ifconfig/ifconfig_get_dummy.hh" #include "fea/data_plane/ifconfig/ifconfig_set_dummy.hh" #include "fea/data_plane/ifconfig/ifconfig_observer_dummy.hh" #include "fea/data_plane/ifconfig/ifconfig_vlan_get_linux.hh" #include "fea/data_plane/ifconfig/ifconfig_vlan_set_linux.hh" #ifndef XORP_DISABLE_FIREWALL #include "fea/data_plane/firewall/firewall_get_dummy.hh" #include "fea/data_plane/firewall/firewall_set_dummy.hh" #endif #include "fea/data_plane/fibconfig/fibconfig_forwarding_dummy.hh" #include "fea/data_plane/fibconfig/fibconfig_entry_get_dummy.hh" #include "fea/data_plane/fibconfig/fibconfig_entry_set_dummy.hh" #include "fea/data_plane/fibconfig/fibconfig_entry_observer_dummy.hh" #include "fea/data_plane/fibconfig/fibconfig_table_get_dummy.hh" #include "fea/data_plane/fibconfig/fibconfig_table_set_dummy.hh" #include "fea/data_plane/fibconfig/fibconfig_table_observer_dummy.hh" #include "fea/data_plane/io/io_link_dummy.hh" #include "fea/data_plane/io/io_ip_dummy.hh" #include "fea/data_plane/io/io_tcpudp_dummy.hh" #include "fea_data_plane_manager_dummy.hh" // // FEA data plane manager class for Dummy FEA. // FeaDataPlaneManagerDummy::FeaDataPlaneManagerDummy(FeaNode& fea_node) : FeaDataPlaneManager(fea_node, "Dummy") { } FeaDataPlaneManagerDummy::~FeaDataPlaneManagerDummy() { } int FeaDataPlaneManagerDummy::load_plugins(string& error_msg) { UNUSED(error_msg); if (_is_loaded_plugins) return (XORP_OK); XLOG_ASSERT(_ifconfig_property == NULL); XLOG_ASSERT(_ifconfig_get == NULL); XLOG_ASSERT(_ifconfig_set == NULL); XLOG_ASSERT(_ifconfig_observer == NULL); XLOG_ASSERT(_ifconfig_vlan_get == NULL); XLOG_ASSERT(_ifconfig_vlan_set == NULL); #ifndef XORP_DISABLE_FIREWALL XLOG_ASSERT(_firewall_get == NULL); XLOG_ASSERT(_firewall_set == NULL); #endif XLOG_ASSERT(_fibconfig_forwarding == NULL); XLOG_ASSERT(_fibconfig_entry_get == NULL); XLOG_ASSERT(_fibconfig_entry_set == NULL); XLOG_ASSERT(_fibconfig_entry_observer == NULL); XLOG_ASSERT(_fibconfig_table_get == NULL); XLOG_ASSERT(_fibconfig_table_set == NULL); XLOG_ASSERT(_fibconfig_table_observer == NULL); // // Load the plugins // _ifconfig_property = new IfConfigPropertyDummy(*this); _ifconfig_get = new IfConfigGetDummy(*this); _ifconfig_set = new IfConfigSetDummy(*this); _ifconfig_observer = new IfConfigObserverDummy(*this); _ifconfig_vlan_get = new IfConfigVlanGetLinux(*this, true); _ifconfig_vlan_set = new IfConfigVlanSetLinux(*this, true); #ifndef XORP_DISABLE_FIREWALL _firewall_get = new FirewallGetDummy(*this); _firewall_set = new FirewallSetDummy(*this); #endif _fibconfig_forwarding = new FibConfigForwardingDummy(*this); _fibconfig_entry_get = new FibConfigEntryGetDummy(*this); _fibconfig_entry_set = new FibConfigEntrySetDummy(*this); _fibconfig_entry_observer = new FibConfigEntryObserverDummy(*this); _fibconfig_table_get = new FibConfigTableGetDummy(*this); _fibconfig_table_set = new FibConfigTableSetDummy(*this); _fibconfig_table_observer = new FibConfigTableObserverDummy(*this); _is_loaded_plugins = true; return (XORP_OK); } int FeaDataPlaneManagerDummy::register_plugins(string& error_msg) { return (FeaDataPlaneManager::register_all_plugins(true, error_msg)); } IoLink* FeaDataPlaneManagerDummy::allocate_io_link(const IfTree& iftree, const string& if_name, const string& vif_name, uint16_t ether_type, const string& filter_program) { IoLink* io_link = NULL; io_link = new IoLinkDummy(*this, iftree, if_name, vif_name, ether_type, filter_program); _io_link_list.push_back(io_link); return (io_link); } IoIp* FeaDataPlaneManagerDummy::allocate_io_ip(const IfTree& iftree, int family, uint8_t ip_protocol) { IoIp* io_ip = NULL; io_ip = new IoIpDummy(*this, iftree, family, ip_protocol); _io_ip_list.push_back(io_ip); return (io_ip); } IoTcpUdp* FeaDataPlaneManagerDummy::allocate_io_tcpudp(const IfTree& iftree, int family, bool is_tcp) { IoTcpUdp* io_tcpudp = NULL; io_tcpudp = new IoTcpUdpDummy(*this, iftree, family, is_tcp); _io_tcpudp_list.push_back(io_tcpudp); return (io_tcpudp); } xorp/fea/data_plane/managers/fea_data_plane_manager_bsd.hh0000664000076400007640000000726611540225524024061 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2007-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/data_plane/managers/fea_data_plane_manager_bsd.hh,v 1.8 2008/10/02 21:57:12 bms Exp $ #ifndef __FEA_DATA_PLANE_MANAGERS_FEA_DATA_PLANE_MANAGER_BSD_HH__ #define __FEA_DATA_PLANE_MANAGERS_FEA_DATA_PLANE_MANAGER_BSD_HH__ #include #if defined(HOST_OS_BSDI) \ || defined(HOST_OS_DRAGONFLYBSD) \ || defined(HOST_OS_FREEBSD) \ || defined(HOST_OS_MACOSX) \ || defined(HOST_OS_NETBSD) \ || defined(HOST_OS_OPENBSD) #include "fea/fea_data_plane_manager.hh" /** * FEA data plane manager class for BSD. */ class FeaDataPlaneManagerBsd : public FeaDataPlaneManager { public: /** * Constructor. * * @param fea_node the @ref FeaNode this manager belongs to. */ FeaDataPlaneManagerBsd(FeaNode& fea_node); /** * Virtual destructor. */ virtual ~FeaDataPlaneManagerBsd(); /** * Load the plugins. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int load_plugins(string& error_msg); /** * Register the plugins. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int register_plugins(string& error_msg); /** * Allocate IoLink plugin instance. * * @param iftree the interface tree to use. * @param if_name the interface name. * @param vif_name the vif name. * @param ether_type the EtherType protocol number. If it is 0 then * it is unused. * @param filter_program the option filter program to be applied on the * received packets. The program uses tcpdump(1) style expression. * @return a new instance of @ref IoLink plugin on success, otherwise NULL. */ IoLink* allocate_io_link(const IfTree& iftree, const string& if_name, const string& vif_name, uint16_t ether_type, const string& filter_program); /** * Allocate IoIp plugin instance. * * @param iftree the interface tree to use. * @param family the address family (AF_INET or AF_INET6 for IPv4 and IPv6 * respectively). * @param ip_protocol the IP protocol number (IPPROTO_*). * @return a new instance of @ref IoIp plugin on success, otherwise NULL. */ IoIp* allocate_io_ip(const IfTree& iftree, int family, uint8_t ip_protocol); /** * Allocate IoTcpUdp plugin instance. * * @param iftree the interface tree to use. * @param family the address family (AF_INET or AF_INET6 for IPv4 and IPv6 * respectively). * @param is_tcp if true allocate a TCP entry, otherwise UDP. * @return a new instance of @ref IoTcpUdp plugin on success, * otherwise NULL. */ IoTcpUdp* allocate_io_tcpudp(const IfTree& iftree, int family, bool is_tcp); private: }; #endif #endif // __FEA_DATA_PLANE_MANAGERS_FEA_DATA_PLANE_MANAGER_BSD_HH__ xorp/fea/data_plane/managers/SConscript0000664000076400007640000000305011540225524020310 0ustar greearbgreearb# Copyright (c) 2009-2011 XORP, Inc and Others # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # $ID$ import os Import("env") env = env.Clone() env.AppendUnique(CPPPATH = [ '#', '$BUILDDIR', ]) is_shared = env.has_key('SHAREDLIBS') sources = [ # C++ files 'fea_data_plane_manager_bsd.cc', 'fea_data_plane_manager_linux.cc', 'fea_data_plane_manager_windows.cc', ] if env['enable_click']: sources.append('fea_data_plane_manager_click.cc') if env['enable_fea_dummy']: sources.append('fea_data_plane_manager_dummy.cc') if is_shared: libxdpm = env.SharedLibrary(target = 'libxorp_fea_data_plane_managers', source = sources) else: libxdpm = env.StaticLibrary(target = 'libxorp_fea_data_plane_managers', source = sources) if is_shared: env.Alias('install', env.InstallLibrary(env['xorp_libdir'], libxdpm)) Default(libxdpm) xorp/fea/data_plane/managers/fea_data_plane_manager_click.hh0000664000076400007640000002415411703345405024373 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2007-2012 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __FEA_DATA_PLANE_MANAGERS_FEA_DATA_PLANE_MANAGER_CLICK_HH__ #define __FEA_DATA_PLANE_MANAGERS_FEA_DATA_PLANE_MANAGER_CLICK_HH__ #include #ifdef XORP_USE_CLICK #include "fea/fea_data_plane_manager.hh" class FibConfigEntryGetClick; class FibConfigEntrySetClick; class FibConfigForwardingDummy; class FibConfigTableGetClick; class FibConfigTableSetClick; class FirewallGetDummy; class FirewallSetDummy; class IfConfigGetClick; class IfConfigPropertyDummy; class IfConfigSetClick; class IPv4; /** * FEA data plane manager class for Click. */ class FeaDataPlaneManagerClick : public FeaDataPlaneManager { public: /** * Constructor. * * @param fea_node the @ref FeaNode this manager belongs to. */ FeaDataPlaneManagerClick(FeaNode& fea_node); /** * Virtual destructor. */ virtual ~FeaDataPlaneManagerClick(); /** * Load the plugins. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int load_plugins(string& error_msg); /** * Unload the plugins. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int unload_plugins(string& error_msg); /** * Register the plugins. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int register_plugins(string& error_msg); /** * Allocate IoLink plugin instance. * * @param iftree the interface tree to use. * @param if_name the interface name. * @param vif_name the vif name. * @param ether_type the EtherType protocol number. If it is 0 then * it is unused. * @param filter_program the option filter program to be applied on the * received packets. The program uses tcpdump(1) style expression. * @return a new instance of @ref IoLink plugin on success, otherwise NULL. */ IoLink* allocate_io_link(const IfTree& iftree, const string& if_name, const string& vif_name, uint16_t ether_type, const string& filter_program); /** * Allocate IoIp plugin instance. * * @param iftree the interface tree to use. * @param family the address family (AF_INET or AF_INET6 for IPv4 and IPv6 * respectively). * @param ip_protocol the IP protocol number (IPPROTO_*). * @return a new instance of @ref IoIp plugin on success, otherwise NULL. */ IoIp* allocate_io_ip(const IfTree& iftree, int family, uint8_t ip_protocol); /** * Allocate IoTcpUdp plugin instance. * * @param iftree the interface tree to use. * @param family the address family (AF_INET or AF_INET6 for IPv4 and IPv6 * respectively). * @param is_tcp if true allocate a TCP entry, otherwise UDP. * @return a new instance of @ref IoTcpUdp plugin on success, * otherwise NULL. */ IoTcpUdp* allocate_io_tcpudp(const IfTree& iftree, int family, bool is_tcp); // // Click-specific methods // /** * Enable/disable Click support. * * @param enable if true, then enable Click support, otherwise disable it. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int enable_click(bool enable, string& error_msg); /** * Enable/disable kernel-level Click support. * * @param enable if true, then enable the kernel-level Click support, * otherwise disable it. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int enable_kernel_click(bool enable, string& error_msg); /** * Specify the external program to generate the kernel-level Click * configuration. * * @param v the name of the external program to generate the kernel-level * Click configuration. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_kernel_click_config_generator_file(const string& v, string& error_msg); /** * Enable/disable duplicating the Click routes to the system kernel. * * @param enable if true, then enable duplicating the Click routes to the * system kernel, otherwise disable it. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int enable_duplicate_routes_to_kernel(bool enable, string& error_msg); /** * Enable/disable installing kernel-level Click on startup. * * @param enable if true, then install kernel-level Click on startup. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int enable_kernel_click_install_on_startup(bool enable, string& error_msg); /** * Specify the list of kernel Click modules to load on startup if * installing kernel-level Click on startup is enabled. * * @param modules the list of kernel Click modules to load. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_kernel_click_modules(const list& modules, string& error_msg); /** * Specify the kernel-level Click mount directory. * * @param directory the kernel-level Click mount directory. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_kernel_click_mount_directory(const string& directory, string& error_msg); /** * Enable/disable user-level Click support. * * @param enable if true, then enable the user-level Click support, * otherwise disable it. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int enable_user_click(bool enable, string& error_msg); /** * Specify the user-level Click command file. * * @param v the name of the user-level Click command file. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_user_click_command_file(const string& v, string& error_msg); /** * Specify the extra arguments to the user-level Click command. * * @param v the extra arguments to the user-level Click command. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_user_click_command_extra_arguments(const string& v, string& error_msg); /** * Specify whether to execute on startup the user-level Click command. * * @param v if true, then execute the user-level Click command on startup. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_user_click_command_execute_on_startup(bool v, string& error_msg); /** * Specify the address to use for control access to the user-level * Click. * * @param v the address to use for control access to the user-level Click. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_user_click_control_address(const IPv4& v, string& error_msg); /** * Specify the socket port to use for control access to the user-level * Click. * * @param v the socket port to use for control access to the user-level * Click. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_user_click_control_socket_port(uint32_t v, string& error_msg); /** * Specify the configuration file to be used by user-level Click on * startup. * * @param v the name of the configuration file to be used by user-level * Click on startup. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_user_click_startup_config_file(const string& v, string& error_msg); /** * Specify the external program to generate the user-level Click * configuration. * * @param v the name of the external program to generate the user-level * Click configuration. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_user_click_config_generator_file(const string& v, string& error_msg); private: // // The plugins // // // TODO: XXX: For the time being some of the plugins // used by Click are dummy. // IfConfigPropertyDummy* _ifconfig_property_dummy; IfConfigGetClick* _ifconfig_get_click; IfConfigSetClick* _ifconfig_set_click; FirewallGetDummy* _firewall_get_dummy; FirewallSetDummy* _firewall_set_dummy; FibConfigForwardingDummy* _fibconfig_forwarding_dummy; FibConfigEntryGetClick* _fibconfig_entry_get_click; FibConfigEntrySetClick* _fibconfig_entry_set_click; FibConfigTableGetClick* _fibconfig_table_get_click; FibConfigTableSetClick* _fibconfig_table_set_click; }; #endif // click #endif // _FEA_DATA_PLANE_MANAGERS_FEA_DATA_PLANE_MANAGER_CLICK_HH__ xorp/fea/data_plane/managers/fea_data_plane_manager_windows.hh0000664000076400007640000000741111540224223024766 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2007-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/data_plane/managers/fea_data_plane_manager_windows.hh,v 1.9 2008/10/02 21:57:13 bms Exp $ #ifndef __FEA_DATA_PLANE_MANAGERS_FEA_DATA_PLANE_MANAGER_WINDOWS_HH__ #define __FEA_DATA_PLANE_MANAGERS_FEA_DATA_PLANE_MANAGER_WINDOWS_HH__ #include "fea/fea_data_plane_manager.hh" /** * FEA data plane manager class for Windows. */ class FeaDataPlaneManagerWindows : public FeaDataPlaneManager { public: /** * Constructor. * * @param fea_node the @ref FeaNode this manager belongs to. */ FeaDataPlaneManagerWindows(FeaNode& fea_node); /** * Virtual destructor. */ virtual ~FeaDataPlaneManagerWindows(); /** * Start data plane manager operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int start_manager(string& error_msg); /** * Load the plugins. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int load_plugins(string& error_msg); /** * Register the plugins. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int register_plugins(string& error_msg); /** * Allocate IoLink plugin instance. * * @param iftree the interface tree to use. * @param if_name the interface name. * @param vif_name the vif name. * @param ether_type the EtherType protocol number. If it is 0 then * it is unused. * @param filter_program the option filter program to be applied on the * received packets. The program uses tcpdump(1) style expression. * @return a new instance of @ref IoLink plugin on success, otherwise NULL. */ IoLink* allocate_io_link(const IfTree& iftree, const string& if_name, const string& vif_name, uint16_t ether_type, const string& filter_program); /** * Allocate IoIp plugin instance. * * @param iftree the interface tree to use. * @param family the address family (AF_INET or AF_INET6 for IPv4 and IPv6 * respectively). * @param ip_protocol the IP protocol number (IPPROTO_*). * @return a new instance of @ref IoIp plugin on success, otherwise NULL. */ IoIp* allocate_io_ip(const IfTree& iftree, int family, uint8_t ip_protocol); /** * Allocate IoTcpUdp plugin instance. * * @param iftree the interface tree to use. * @param family the address family (AF_INET or AF_INET6 for IPv4 and IPv6 * respectively). * @param is_tcp if true allocate a TCP entry, otherwise UDP. * @return a new instance of @ref IoTcpUdp plugin on success, * otherwise NULL. */ IoTcpUdp* allocate_io_tcpudp(const IfTree& iftree, int family, bool is_tcp); private: }; #endif // __FEA_DATA_PLANE_MANAGERS_FEA_DATA_PLANE_MANAGER_WINDOWS_HH__ xorp/fea/data_plane/managers/fea_data_plane_manager_bsd.cc0000664000076400007640000001632211540225524024040 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2007-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include #if defined(HOST_OS_BSDI) \ || defined(HOST_OS_DRAGONFLYBSD) \ || defined(HOST_OS_FREEBSD) \ || defined(HOST_OS_MACOSX) \ || defined(HOST_OS_NETBSD) \ || defined(HOST_OS_OPENBSD) #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "fea/data_plane/ifconfig/ifconfig_property_bsd.hh" #include "fea/data_plane/ifconfig/ifconfig_get_getifaddrs.hh" #include "fea/data_plane/ifconfig/ifconfig_get_ioctl.hh" #include "fea/data_plane/ifconfig/ifconfig_get_sysctl.hh" #include "fea/data_plane/ifconfig/ifconfig_set_ioctl.hh" #include "fea/data_plane/ifconfig/ifconfig_observer_routing_socket.hh" #include "fea/data_plane/ifconfig/ifconfig_vlan_get_linux.hh" #include "fea/data_plane/ifconfig/ifconfig_vlan_set_linux.hh" #include "fea/data_plane/firewall/firewall_get_ipfw2.hh" #include "fea/data_plane/firewall/firewall_get_pf.hh" #include "fea/data_plane/firewall/firewall_set_ipfw2.hh" #include "fea/data_plane/firewall/firewall_set_pf.hh" #include "fea/data_plane/fibconfig/fibconfig_forwarding_sysctl.hh" #include "fea/data_plane/fibconfig/fibconfig_entry_get_routing_socket.hh" #include "fea/data_plane/fibconfig/fibconfig_entry_set_routing_socket.hh" #include "fea/data_plane/fibconfig/fibconfig_entry_observer_routing_socket.hh" #include "fea/data_plane/fibconfig/fibconfig_table_get_sysctl.hh" #include "fea/data_plane/fibconfig/fibconfig_table_set_routing_socket.hh" #include "fea/data_plane/fibconfig/fibconfig_table_observer_routing_socket.hh" #include "fea/data_plane/io/io_link_pcap.hh" #include "fea/data_plane/io/io_ip_socket.hh" #include "fea/data_plane/io/io_tcpudp_socket.hh" #include "fea_data_plane_manager_bsd.hh" // // FEA data plane manager class for BSD FEA. // #if 0 extern "C" FeaDataPlaneManager* create(FeaNode& fea_node) { return (new FeaDataPlaneManagerBsd(fea_node)); } extern "C" void destroy(FeaDataPlaneManager* fea_data_plane_manager) { delete fea_data_plane_manager; } #endif // 0 FeaDataPlaneManagerBsd::FeaDataPlaneManagerBsd(FeaNode& fea_node) : FeaDataPlaneManager(fea_node, "BSD") { } FeaDataPlaneManagerBsd::~FeaDataPlaneManagerBsd() { } int FeaDataPlaneManagerBsd::load_plugins(string& error_msg) { UNUSED(error_msg); if (_is_loaded_plugins) return (XORP_OK); XLOG_ASSERT(_ifconfig_property == NULL); XLOG_ASSERT(_ifconfig_get == NULL); XLOG_ASSERT(_ifconfig_set == NULL); XLOG_ASSERT(_ifconfig_observer == NULL); XLOG_ASSERT(_ifconfig_vlan_get == NULL); XLOG_ASSERT(_ifconfig_vlan_set == NULL); XLOG_ASSERT(_firewall_get == NULL); XLOG_ASSERT(_firewall_set == NULL); XLOG_ASSERT(_fibconfig_forwarding == NULL); XLOG_ASSERT(_fibconfig_entry_get == NULL); XLOG_ASSERT(_fibconfig_entry_set == NULL); XLOG_ASSERT(_fibconfig_entry_observer == NULL); XLOG_ASSERT(_fibconfig_table_get == NULL); XLOG_ASSERT(_fibconfig_table_set == NULL); XLOG_ASSERT(_fibconfig_table_observer == NULL); // // Load the plugins // _ifconfig_property = new IfConfigPropertyBsd(*this); #if defined(HAVE_GETIFADDRS) _ifconfig_get = new IfConfigGetGetifaddrs(*this); #elif defined(HAVE_SYSCTL_NET_RT_IFLIST) _ifconfig_get = new IfConfigGetSysctl(*this); #elif defined(HAVE_IOCTL_SIOCGIFCONF) _ifconfig_get = new IfConfigGetIoctl(*this); #endif #if defined(HAVE_IOCTL_SIOCGIFCONF) _ifconfig_set = new IfConfigSetIoctl(*this); #endif #if defined(HAVE_ROUTING_SOCKETS) _ifconfig_observer = new IfConfigObserverRoutingSocket(*this); #endif #if defined(HAVE_VLAN_BSD) _ifconfig_vlan_get = new IfConfigVlanGetLinux(*this, false); _ifconfig_vlan_set = new IfConfigVlanSetLinux(*this, false); #endif // // XXX: FreeBSD provides both IPFW2 and PF. // Currently the preferred one is IPFW2 (for no particular reason). // #if defined(HAVE_FIREWALL_IPFW2) _firewall_get = new FirewallGetIpfw2(*this); _firewall_set = new FirewallSetIpfw2(*this); #elif defined(HAVE_FIREWALL_PF) _firewall_get = new FirewallGetPf(*this); _firewall_set = new FirewallSetPf(*this); #endif #if defined(HAVE_SYSCTL_IPCTL_FORWARDING) || defined(HAVE_SYSCTL_IPV6CTL_FORWARDING) || defined(HAVE_SYSCTL_IPV6CTL_ACCEPT_RTADV) _fibconfig_forwarding = new FibConfigForwardingSysctl(*this); #endif #if defined(HAVE_ROUTING_SOCKETS) _fibconfig_entry_get = new FibConfigEntryGetRoutingSocket(*this); #endif #if defined(HAVE_ROUTING_SOCKETS) _fibconfig_entry_set = new FibConfigEntrySetRoutingSocket(*this); #endif #if defined(HAVE_ROUTING_SOCKETS) _fibconfig_entry_observer = new FibConfigEntryObserverRoutingSocket(*this); #endif #if defined(HAVE_SYSCTL_NET_RT_DUMP) _fibconfig_table_get = new FibConfigTableGetSysctl(*this); #endif #if defined(HAVE_ROUTING_SOCKETS) _fibconfig_table_set = new FibConfigTableSetRoutingSocket(*this); #endif #if defined(HAVE_ROUTING_SOCKETS) _fibconfig_table_observer = new FibConfigTableObserverRoutingSocket(*this); #endif _is_loaded_plugins = true; return (XORP_OK); } int FeaDataPlaneManagerBsd::register_plugins(string& error_msg) { return (FeaDataPlaneManager::register_all_plugins(true, error_msg)); } IoLink* FeaDataPlaneManagerBsd::allocate_io_link(const IfTree& iftree, const string& if_name, const string& vif_name, uint16_t ether_type, const string& filter_program) { IoLink* io_link = NULL; UNUSED(iftree); UNUSED(if_name); UNUSED(vif_name); UNUSED(ether_type); UNUSED(filter_program); #ifdef HAVE_PCAP_H io_link = new IoLinkPcap(*this, iftree, if_name, vif_name, ether_type, filter_program); _io_link_list.push_back(io_link); #endif return (io_link); } IoIp* FeaDataPlaneManagerBsd::allocate_io_ip(const IfTree& iftree, int family, uint8_t ip_protocol) { IoIp* io_ip = NULL; UNUSED(iftree); UNUSED(family); UNUSED(ip_protocol); #ifdef HAVE_IP_RAW_SOCKETS io_ip = new IoIpSocket(*this, iftree, family, ip_protocol); _io_ip_list.push_back(io_ip); #endif return (io_ip); } IoTcpUdp* FeaDataPlaneManagerBsd::allocate_io_tcpudp(const IfTree& iftree, int family, bool is_tcp) { IoTcpUdp* io_tcpudp = NULL; UNUSED(iftree); UNUSED(family); #ifdef HAVE_TCPUDP_UNIX_SOCKETS io_tcpudp = new IoTcpUdpSocket(*this, iftree, family, is_tcp); _io_tcpudp_list.push_back(io_tcpudp); #endif return (io_tcpudp); } #endif // bsd xorp/fea/data_plane/managers/fea_data_plane_manager_linux.cc0000664000076400007640000001717211540225524024433 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2007-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "fea/data_plane/ifconfig/ifconfig_property_linux.hh" #include "fea/data_plane/ifconfig/ifconfig_get_getifaddrs.hh" #include "fea/data_plane/ifconfig/ifconfig_get_ioctl.hh" #include "fea/data_plane/ifconfig/ifconfig_get_netlink_socket.hh" #include "fea/data_plane/ifconfig/ifconfig_get_proc_linux.hh" #include "fea/data_plane/ifconfig/ifconfig_set_ioctl.hh" #include "fea/data_plane/ifconfig/ifconfig_set_netlink_socket.hh" #include "fea/data_plane/ifconfig/ifconfig_observer_netlink_socket.hh" #include "fea/data_plane/ifconfig/ifconfig_vlan_get_linux.hh" #include "fea/data_plane/ifconfig/ifconfig_vlan_set_linux.hh" #ifndef XORP_DISABLE_FIREWALL #include "fea/data_plane/firewall/firewall_get_netfilter.hh" #include "fea/data_plane/firewall/firewall_set_netfilter.hh" #endif #include "fea/data_plane/fibconfig/fibconfig_forwarding_proc_linux.hh" #include "fea/data_plane/fibconfig/fibconfig_entry_get_netlink_socket.hh" #include "fea/data_plane/fibconfig/fibconfig_entry_set_netlink_socket.hh" #include "fea/data_plane/fibconfig/fibconfig_entry_observer_netlink_socket.hh" #include "fea/data_plane/fibconfig/fibconfig_table_get_netlink_socket.hh" #include "fea/data_plane/fibconfig/fibconfig_table_set_netlink_socket.hh" #include "fea/data_plane/fibconfig/fibconfig_table_observer_netlink_socket.hh" #include "fea/data_plane/io/io_link_pcap.hh" #include "fea/data_plane/io/io_ip_socket.hh" #include "fea/data_plane/io/io_tcpudp_socket.hh" #include "fea_data_plane_manager_linux.hh" // // FEA data plane manager class for Linux FEA. // #if 0 extern "C" FeaDataPlaneManager* create(FeaNode& fea_node) { return (new FeaDataPlaneManagerLinux(fea_node)); } extern "C" void destroy(FeaDataPlaneManager* fea_data_plane_manager) { delete fea_data_plane_manager; } #endif // 0 FeaDataPlaneManagerLinux::FeaDataPlaneManagerLinux(FeaNode& fea_node) : FeaDataPlaneManager(fea_node, "Linux"), _ifconfig_get_ioctl(NULL) { } FeaDataPlaneManagerLinux::~FeaDataPlaneManagerLinux() { } int FeaDataPlaneManagerLinux::load_plugins(string& error_msg) { UNUSED(error_msg); if (_is_loaded_plugins) return (XORP_OK); XLOG_ASSERT(_ifconfig_property == NULL); XLOG_ASSERT(_ifconfig_get == NULL); XLOG_ASSERT(_ifconfig_set == NULL); XLOG_ASSERT(_ifconfig_observer == NULL); XLOG_ASSERT(_ifconfig_vlan_get == NULL); XLOG_ASSERT(_ifconfig_vlan_set == NULL); #ifndef XORP_DISABLE_FIREWALL XLOG_ASSERT(_firewall_get == NULL); XLOG_ASSERT(_firewall_set == NULL); #endif XLOG_ASSERT(_fibconfig_forwarding == NULL); XLOG_ASSERT(_fibconfig_entry_get == NULL); XLOG_ASSERT(_fibconfig_entry_set == NULL); XLOG_ASSERT(_fibconfig_entry_observer == NULL); XLOG_ASSERT(_fibconfig_table_get == NULL); XLOG_ASSERT(_fibconfig_table_set == NULL); XLOG_ASSERT(_fibconfig_table_observer == NULL); // // Load the plugins // #if defined(HOST_OS_LINUX) _ifconfig_property = new IfConfigPropertyLinux(*this); #endif #if defined(HAVE_NETLINK_SOCKETS) _ifconfig_get = new IfConfigGetNetlinkSocket(*this); #elif defined(HAVE_PROC_LINUX) && defined(HAVE_IOCTL_SIOCGIFCONF) // XXX: the IfConfigGetProcLinux plugin depends on IfConfigGetIoctl _ifconfig_get = new IfConfigGetProcLinux(*this); _ifconfig_get_ioctl = new IfConfigGetIoctl(*this); #elif defined(HAVE_GETIFADDRS) _ifconfig_get = new IfConfigGetGetifaddrs(*this); #elif defined(HAVE_IOCTL_SIOCGIFCONF) _ifconfig_get = new IfConfigGetIoctl(*this); #endif #if defined(HAVE_NETLINK_SOCKETS) _ifconfig_set = new IfConfigSetNetlinkSocket(*this); #elif defined(HAVE_IOCTL_SIOCGIFCONF) _ifconfig_set = new IfConfigSetIoctl(*this); #endif #if defined(HAVE_NETLINK_SOCKETS) _ifconfig_observer = new IfConfigObserverNetlinkSocket(*this); #endif #if defined(HAVE_VLAN_LINUX) _ifconfig_vlan_get = new IfConfigVlanGetLinux(*this, false); _ifconfig_vlan_set = new IfConfigVlanSetLinux(*this, false); #endif #ifndef XORP_DISABLE_FIREWALL #if defined(HAVE_FIREWALL_NETFILTER) _firewall_get = new FirewallGetNetfilter(*this); _firewall_set = new FirewallSetNetfilter(*this); #endif #endif #if defined(HAVE_PROC_LINUX) _fibconfig_forwarding = new FibConfigForwardingProcLinux(*this); #endif #if defined(HAVE_NETLINK_SOCKETS) _fibconfig_entry_get = new FibConfigEntryGetNetlinkSocket(*this); #endif #if defined(HAVE_NETLINK_SOCKETS) _fibconfig_entry_set = new FibConfigEntrySetNetlinkSocket(*this); #endif #if defined(HAVE_NETLINK_SOCKETS) _fibconfig_entry_observer = new FibConfigEntryObserverNetlinkSocket(*this); #endif #if defined(HAVE_NETLINK_SOCKETS) _fibconfig_table_get = new FibConfigTableGetNetlinkSocket(*this); #endif #if defined(HAVE_NETLINK_SOCKETS) _fibconfig_table_set = new FibConfigTableSetNetlinkSocket(*this); #endif #if defined(HAVE_NETLINK_SOCKETS) _fibconfig_table_observer = new FibConfigTableObserverNetlinkSocket(*this); #endif _is_loaded_plugins = true; return (XORP_OK); } int FeaDataPlaneManagerLinux::unload_plugins(string& error_msg) { if (! _is_loaded_plugins) return (XORP_OK); #if defined(HAVE_PROC_LINUX) && defined(HAVE_IOCTL_SIOCGIFCONF) and !defined(HAVE_NETLINK_SOCKETS) if (_ifconfig_get_ioctl != NULL) { delete _ifconfig_get_ioctl; _ifconfig_get_ioctl = NULL; } #endif return (FeaDataPlaneManager::unload_plugins(error_msg)); } int FeaDataPlaneManagerLinux::register_plugins(string& error_msg) { return (FeaDataPlaneManager::register_all_plugins(true, error_msg)); } IoLink* FeaDataPlaneManagerLinux::allocate_io_link(const IfTree& iftree, const string& if_name, const string& vif_name, uint16_t ether_type, const string& filter_program) { IoLink* io_link = NULL; UNUSED(iftree); UNUSED(if_name); UNUSED(vif_name); UNUSED(ether_type); UNUSED(filter_program); #ifdef HAVE_PCAP_H io_link = new IoLinkPcap(*this, iftree, if_name, vif_name, ether_type, filter_program); _io_link_list.push_back(io_link); #endif return (io_link); } IoIp* FeaDataPlaneManagerLinux::allocate_io_ip(const IfTree& iftree, int family, uint8_t ip_protocol) { IoIp* io_ip = NULL; UNUSED(iftree); UNUSED(family); UNUSED(ip_protocol); #ifdef HAVE_IP_RAW_SOCKETS io_ip = new IoIpSocket(*this, iftree, family, ip_protocol); _io_ip_list.push_back(io_ip); #endif return (io_ip); } IoTcpUdp* FeaDataPlaneManagerLinux::allocate_io_tcpudp(const IfTree& iftree, int family, bool is_tcp) { IoTcpUdp* io_tcpudp = NULL; UNUSED(iftree); UNUSED(family); #ifdef HAVE_TCPUDP_UNIX_SOCKETS io_tcpudp = new IoTcpUdpSocket(*this, iftree, family, is_tcp); _io_tcpudp_list.push_back(io_tcpudp); #endif return (io_tcpudp); } xorp/fea/data_plane/managers/fea_data_plane_manager_windows.cc0000664000076400007640000001534111540224223024755 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2007-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #ifdef HOST_OS_WINDOWS #include "fea/data_plane/control_socket/windows_rras_support.hh" #endif #include "fea/data_plane/ifconfig/ifconfig_property_windows.hh" #include "fea/data_plane/ifconfig/ifconfig_get_iphelper.hh" #include "fea/data_plane/ifconfig/ifconfig_set_iphelper.hh" #include "fea/data_plane/ifconfig/ifconfig_observer_iphelper.hh" #include "fea/data_plane/fibconfig/fibconfig_forwarding_windows.hh" #include "fea/data_plane/fibconfig/fibconfig_entry_get_iphelper.hh" #include "fea/data_plane/fibconfig/fibconfig_entry_get_rtmv2.hh" #include "fea/data_plane/fibconfig/fibconfig_entry_set_iphelper.hh" #include "fea/data_plane/fibconfig/fibconfig_entry_set_rtmv2.hh" #include "fea/data_plane/fibconfig/fibconfig_entry_observer_iphelper.hh" #include "fea/data_plane/fibconfig/fibconfig_entry_observer_rtmv2.hh" #include "fea/data_plane/fibconfig/fibconfig_table_get_iphelper.hh" #include "fea/data_plane/fibconfig/fibconfig_table_set_iphelper.hh" #include "fea/data_plane/fibconfig/fibconfig_table_set_rtmv2.hh" #include "fea/data_plane/fibconfig/fibconfig_table_observer_iphelper.hh" #include "fea/data_plane/fibconfig/fibconfig_table_observer_rtmv2.hh" #include "fea/data_plane/io/io_link_pcap.hh" #include "fea/data_plane/io/io_ip_socket.hh" #include "fea/data_plane/io/io_tcpudp_socket.hh" #include "fea_data_plane_manager_windows.hh" // // FEA data plane manager class for Windows FEA. // #if 0 extern "C" FeaDataPlaneManager* create(FeaNode& fea_node) { return (new FeaDataPlaneManagerWindows(fea_node)); } extern "C" void destroy(FeaDataPlaneManager* fea_data_plane_manager) { delete fea_data_plane_manager; } #endif // 0 FeaDataPlaneManagerWindows::FeaDataPlaneManagerWindows(FeaNode& fea_node) : FeaDataPlaneManager(fea_node, "Windows") { } FeaDataPlaneManagerWindows::~FeaDataPlaneManagerWindows() { } int FeaDataPlaneManagerWindows::start_manager(string& error_msg) { #if defined(HOST_OS_WINDOWS) // // Load support code for Windows Router Manager V2, if it // is detected as running and/or configured. // if (WinSupport::is_rras_running()) { WinSupport::add_protocol_to_registry(AF_INET); #if 0 WinSupport::add_protocol_to_registry(AF_INET6); #endif WinSupport::restart_rras(); WinSupport::add_protocol_to_rras(AF_INET); #if 0 WinSupport::add_protocol_to_rras(AF_INET6); #endif TimerList::system_sleep(TimeVal(2, 0)); } #endif // HOST_OS_WINDOWS return (FeaDataPlaneManager::start_manager(error_msg)); } int FeaDataPlaneManagerWindows::load_plugins(string& error_msg) { UNUSED(error_msg); if (_is_loaded_plugins) return (XORP_OK); XLOG_ASSERT(_ifconfig_property == NULL); XLOG_ASSERT(_ifconfig_get == NULL); XLOG_ASSERT(_ifconfig_set == NULL); XLOG_ASSERT(_ifconfig_observer == NULL); XLOG_ASSERT(_fibconfig_forwarding == NULL); XLOG_ASSERT(_fibconfig_entry_get == NULL); XLOG_ASSERT(_fibconfig_entry_set == NULL); XLOG_ASSERT(_fibconfig_entry_observer == NULL); XLOG_ASSERT(_fibconfig_table_get == NULL); XLOG_ASSERT(_fibconfig_table_set == NULL); XLOG_ASSERT(_fibconfig_table_observer == NULL); // // Load the plugins // #if defined(HOST_OS_WINDOWS) bool is_rras_running = WinSupport::is_rras_running(); _ifconfig_property = new IfConfigPropertyWindows(*this); _ifconfig_get = new IfConfigGetIPHelper(*this); _ifconfig_set = new IfConfigSetIPHelper(*this); _ifconfig_observer = new IfConfigObserverIPHelper(*this); _fibconfig_forwarding = new FibConfigForwardingWindows(*this); _fibconfig_entry_get = new FibConfigEntryGetIPHelper(*this); // _fibconfig_entry_get = new FibConfigEntryGetRtmV2(*this); if (is_rras_running) { _fibconfig_entry_set = new FibConfigEntrySetRtmV2(*this); } else { _fibconfig_entry_set = new FibConfigEntrySetIPHelper(*this); } _fibconfig_entry_observer = new FibConfigEntryObserverIPHelper(*this); // _fibconfig_entry_observer = new FibConfigEntryObserverRtmV2(*this); _fibconfig_table_get = new FibConfigTableGetIPHelper(*this); _fibconfig_table_set = new FibConfigTableSetIPHelper(*this); // _fibconfig_table_set = new FibConfigTableSetRtmV2(*this); if (is_rras_running) { _fibconfig_table_observer = new FibConfigTableObserverRtmV2(*this); } else { _fibconfig_table_observer = new FibConfigTableObserverIPHelper(*this); } #endif // HOST_OS_WINDOWS _is_loaded_plugins = true; return (XORP_OK); } int FeaDataPlaneManagerWindows::register_plugins(string& error_msg) { return (FeaDataPlaneManager::register_all_plugins(true, error_msg)); } IoLink* FeaDataPlaneManagerWindows::allocate_io_link(const IfTree& iftree, const string& if_name, const string& vif_name, uint16_t ether_type, const string& filter_program) { IoLink* io_link = NULL; UNUSED(iftree); UNUSED(if_name); UNUSED(vif_name); UNUSED(ether_type); UNUSED(filter_program); #ifdef HAVE_PCAP io_link = new IoLinkPcap(*this, iftree, if_name, vif_name, ether_type, filter_program); _io_link_list.push_back(io_link); #endif return (io_link); } IoIp* FeaDataPlaneManagerWindows::allocate_io_ip(const IfTree& iftree, int family, uint8_t ip_protocol) { IoIp* io_ip = NULL; UNUSED(iftree); UNUSED(family); UNUSED(ip_protocol); #ifdef HAVE_IP_RAW_SOCKETS io_ip = new IoIpSocket(*this, iftree, family, ip_protocol); _io_ip_list.push_back(io_ip); #endif return (io_ip); } IoTcpUdp* FeaDataPlaneManagerWindows::allocate_io_tcpudp(const IfTree& iftree, int family, bool is_tcp) { IoTcpUdp* io_tcpudp = NULL; UNUSED(iftree); UNUSED(family); #ifdef HAVE_TCPUDP_UNIX_SOCKETS io_tcpudp = new IoTcpUdpSocket(*this, iftree, family, is_tcp); _io_tcpudp_list.push_back(io_tcpudp); #endif return (io_tcpudp); } xorp/fea/data_plane/fibconfig/0000775000076400007640000000000011703345405016433 5ustar greearbgreearbxorp/fea/data_plane/fibconfig/fibconfig_forwarding_solaris.hh0000664000076400007640000001027711421137511024661 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2007-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/data_plane/fibconfig/fibconfig_forwarding_solaris.hh,v 1.6 2008/10/02 21:56:58 bms Exp $ #ifndef __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_FORWARDING_SOLARIS_HH__ #define __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_FORWARDING_SOLARIS_HH__ #include "fea/fibconfig_forwarding.hh" class FibConfigForwardingSolaris : public FibConfigForwarding { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ FibConfigForwardingSolaris(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~FibConfigForwardingSolaris(); /** * Test whether the IPv4 unicast forwarding engine is enabled or disabled * to forward packets. * * @param ret_value if true on return, then the IPv4 unicast forwarding * is enabled, otherwise is disabled. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int unicast_forwarding_enabled4(bool& ret_value, string& error_msg) const; /** * Test whether the IPv6 unicast forwarding engine is enabled or disabled * to forward packets. * * @param ret_value if true on return, then the IPv6 unicast forwarding * is enabled, otherwise is disabled. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int unicast_forwarding_enabled6(bool& ret_value, string& error_msg) const; /** * Test whether the acceptance of IPv6 Router Advertisement messages is * enabled or disabled. * * @param ret_value if true on return, then the acceptance of IPv6 Router * Advertisement messages is enabled, otherwise is disabled. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int accept_rtadv_enabled6(bool& ret_value, string& error_msg) const; /** * Set the IPv4 unicast forwarding engine to enable or disable forwarding * of packets. * * @param v if true, then enable IPv4 unicast forwarding, otherwise * disable it. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int set_unicast_forwarding_enabled4(bool v, string& error_msg); /** * Set the IPv6 unicast forwarding engine to enable or disable forwarding * of packets. * * @param v if true, then enable IPv6 unicast forwarding, otherwise * disable it. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int set_unicast_forwarding_enabled6(bool v, string& error_msg); /** * Enable or disable the acceptance of IPv6 Router Advertisement messages * from other routers. It should be enabled for hosts, and disabled for * routers. * * @param v if true, then enable the acceptance of IPv6 Router * Advertisement messages, otherwise disable it. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int set_accept_rtadv_enabled6(bool v, string& error_msg); private: }; #endif // __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_FORWARDING_SOLARIS_HH__ xorp/fea/data_plane/fibconfig/fibconfig_entry_set_rtmv2.cc0000664000076400007640000002277111540224222024117 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #ifdef HOST_OS_WINDOWS #include "fea/data_plane/control_socket/windows_routing_socket.h" #endif #ifdef HAVE_IPHLPAPI_H #include #endif #ifdef HAVE_ROUTPROT_H #include #endif #include "fea/fibconfig.hh" #ifdef HOST_OS_WINDOWS #include "fea/data_plane/control_socket/windows_rtm_pipe.hh" #include "fea/data_plane/control_socket/windows_rras_support.hh" #endif #include "fibconfig_entry_set_rtmv2.hh" // // Set single-entry information into the unicast forwarding table. // // The mechanism to set the information is Router Manager V2. // #ifdef HOST_OS_WINDOWS FibConfigEntrySetRtmV2::FibConfigEntrySetRtmV2(FeaDataPlaneManager& fea_data_plane_manager) : FibConfigEntrySet(fea_data_plane_manager), _rs4(NULL), _rs6(NULL) { if (!WinSupport::is_rras_running()) { XLOG_WARNING("RRAS is not running; disabling FibConfigEntrySetRtmV2."); return; } _rs4 = new WinRtmPipe(fea_data_plane_manager.eventloop()); #ifdef HAVE_IPV6 _rs6 = new WinRtmPipe(fea_data_plane_manager.eventloop()); #endif } FibConfigEntrySetRtmV2::~FibConfigEntrySetRtmV2() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the Router Manager V2 mechanism to set " "information about forwarding table from the underlying " "system: %s", error_msg.c_str()); } #ifdef HAVE_IPV6 if (_rs6 != NULL) delete _rs6; #endif if (_rs4 != NULL) delete _rs4; } int FibConfigEntrySetRtmV2::start(string& error_msg) { if (_is_running) return (XORP_OK); if (_rs4 == NULL || (_rs4->start(AF_INET, error_msg) != XORP_OK)) return (XORP_ERROR); #if 0 #ifdef HAVE_IPV6 if (_rs6 == NULL || (_rs6->start(AF_INET6, error_msg) != XORP_OK)) return (XORP_ERROR); #endif #endif _is_running = true; return (XORP_OK); } int FibConfigEntrySetRtmV2::stop(string& error_msg) { int result; if (! _is_running) return (XORP_OK); if (_rs4 == NULL || (_rs4->stop(error_msg) != XORP_OK)) result = XORP_ERROR; #if 0 #ifdef HAVE_IPV6 if (rs6 == NULL || (_rs6->stop(error_msg) != XORP_OK)) result = XORP_ERROR; #endif #endif _is_running = false; return (XORP_OK); } int FibConfigEntrySetRtmV2::add_entry4(const Fte4& fte) { FteX ftex(fte); return (add_entry(ftex)); } int FibConfigEntrySetRtmV2::delete_entry4(const Fte4& fte) { FteX ftex(fte); return (delete_entry(ftex)); } int FibConfigEntrySetRtmV2::add_entry6(const Fte6& fte) { FteX ftex(fte); return (add_entry(ftex)); } int FibConfigEntrySetRtmV2::delete_entry6(const Fte6& fte) { FteX ftex(fte); return (delete_entry(ftex)); } int FibConfigEntrySetRtmV2::add_entry(const FteX& fte) { static const size_t buffer_size = sizeof(struct rt_msghdr) + (sizeof(struct sockaddr_storage) * 3); union { uint8_t data[buffer_size]; struct rt_msghdr rtm; } buffer; struct rt_msghdr* rtm = &buffer.rtm; struct sockaddr_storage* ss; struct sockaddr_in* sin_dst = NULL; struct sockaddr_in* sin_netmask = NULL; struct sockaddr_in* sin_nexthop = NULL; int family = fte.net().af(); IPvX fte_nexthop = fte.nexthop(); int result; debug_msg("add_entry (network = %s nexthop = %s)\n", fte.net().str().c_str(), fte_nexthop.str().c_str()); // Check that the family is supported do { if (fte_nexthop.is_ipv4()) { if (! fea_data_plane_manager().have_ipv4()) return (XORP_ERROR); break; } if (fte_nexthop.is_ipv6()) { if (! fea_data_plane_manager().have_ipv6()) return (XORP_ERROR); break; } break; } while (false); if (fte.is_connected_route()) return (XORP_OK); // XXX: don't add/remove directly-connected routes #if 0 if (! fte.ifname().empty()) is_interface_route = true; #endif #if 0 do { // // Check for a discard or unreachable route. // The referenced ifname must have respectively the discard or // unreachable property. The next-hop is forcibly rewritten to be the // loopback address, in order for the RTF_BLACKHOLE or RTF_REJECT // flag to take effect on BSD platforms. // if (fte.ifname().empty()) break; const IfTree& iftree = fibconfig().merged_config_iftree(); const IfTreeInterface* ifp = iftree.find_interface(fte.ifname()); if (ifp == NULL) { XLOG_ERROR("Invalid interface name: %s", fte.ifname().c_str()); return (XORP_ERROR); } if (ifp->discard()) { is_discard_route = true; fte_nexthop = IPvX::LOOPBACK(family); } if (ifp->unreachable()) { is_unreachable_route = true; fte_nexthop = IPvX::LOOPBACK(family); } break; } while (false); #endif // // Set the request // memset(&buffer, 0, sizeof(buffer)); rtm->rtm_msglen = sizeof(*rtm); if (family != AF_INET && family != AF_INET6) XLOG_UNREACHABLE(); ss = (struct sockaddr_storage *)(rtm + 1); sin_dst = (struct sockaddr_in *)(ss); sin_nexthop = (struct sockaddr_in *)(ss + 1); sin_netmask = (struct sockaddr_in *)(ss + 2); rtm->rtm_msglen += (3 * sizeof(struct sockaddr_storage)); rtm->rtm_version = RTM_VERSION; rtm->rtm_type = RTM_ADD; rtm->rtm_addrs = (RTA_DST | RTA_GATEWAY | RTA_NETMASK); rtm->rtm_pid = ((family == AF_INET ? _rs4 : _rs6))->pid(); rtm->rtm_seq = ((family == AF_INET ? _rs4 : _rs6))->seqno(); // Copy the interface index. const IfTree& iftree = fibconfig().merged_config_iftree(); const IfTreeVif* vifp = iftree.find_vif(fte.ifname(), fte.vifname()); XLOG_ASSERT(vifp != NULL); rtm->rtm_index = vifp->pif_index(); // Copy the destination, the nexthop, and the netmask addresses fte.net().masked_addr().copy_out(*sin_dst); if (sin_nexthop != NULL) { fte_nexthop.copy_out(*sin_nexthop); } fte.net().netmask().copy_out(*sin_netmask); result = ((family == AF_INET ? _rs4 : _rs6))->write(rtm, rtm->rtm_msglen); if (result != rtm->rtm_msglen) { XLOG_ERROR("Error writing to Rtmv2 pipe: %s", strerror(errno)); return (XORP_ERROR); } // // TODO: here we should check the routing socket output whether the write // succeeded. // return (XORP_OK); } int FibConfigEntrySetRtmV2::delete_entry(const FteX& fte) { static const size_t buffer_size = sizeof(struct rt_msghdr) + (sizeof(struct sockaddr_storage) * 2); union { uint8_t data[buffer_size]; struct rt_msghdr rtm; } buffer; struct rt_msghdr* rtm = &buffer.rtm; struct sockaddr_storage* ss; struct sockaddr_in* sin_dst = NULL; struct sockaddr_in* sin_netmask = NULL; int family = fte.net().af(); int result; debug_msg("delete_entry (network = %s nexthop = %s)\n", fte.net().str().c_str(), fte.nexthop().str().c_str()); // Check that the family is supported do { if (fte.nexthop().is_ipv4()) { if (! fea_data_plane_manager().have_ipv4()) return (XORP_ERROR); break; } if (fte.nexthop().is_ipv6()) { if (! fea_data_plane_manager().have_ipv6()) return (XORP_ERROR); break; } break; } while (false); if (fte.is_connected_route()) return (XORP_OK); // XXX: don't add/remove directly-connected routes // // Set the request // memset(&buffer, 0, sizeof(buffer)); rtm->rtm_msglen = sizeof(*rtm); if (family != AF_INET && family != AF_INET6) XLOG_UNREACHABLE(); ss = (struct sockaddr_storage *)(rtm + 1); sin_dst = (struct sockaddr_in *)(ss); sin_netmask = (struct sockaddr_in *)(ss + 1); rtm->rtm_msglen += (2 * sizeof(struct sockaddr_storage)); rtm->rtm_version = RTM_VERSION; rtm->rtm_type = RTM_DELETE; rtm->rtm_addrs = RTA_DST; rtm->rtm_addrs |= RTA_NETMASK; rtm->rtm_flags = 0; rtm->rtm_pid = ((family == AF_INET ? _rs4 : _rs6))->pid(); rtm->rtm_seq = ((family == AF_INET ? _rs4 : _rs6))->seqno(); // Copy the interface index. const IfTree& iftree = fibconfig().merged_config_iftree(); const IfTreeVif* vifp = iftree.find_vif(fte.ifname(), fte.vifname()); XLOG_ASSERT(vifp != NULL); rtm->rtm_index = vifp->pif_index(); // Copy the destination, and the netmask addresses (if needed) fte.net().masked_addr().copy_out(*sin_dst); fte.net().netmask().copy_out(*sin_netmask); result = ((family == AF_INET ? _rs4 : _rs6))->write(rtm, rtm->rtm_msglen); if (result != rtm->rtm_msglen) { XLOG_ERROR("Error writing to Rtmv2 pipe: %s", strerror(errno)); return (XORP_ERROR); } // // TODO: here we should check the routing socket output whether the write // succeeded. // return (XORP_OK); } #endif // HOST_OS_WINDOWS xorp/fea/data_plane/fibconfig/fibconfig_table_get_sysctl.cc0000664000076400007640000001112111421137511024265 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #ifdef HAVE_SYS_SYSCTL_H #include #endif #include "fea/fibconfig.hh" #include "fibconfig_table_get_sysctl.hh" // // Get the whole table information from the unicast forwarding table. // // The mechanism to obtain the information is sysctl(3). // #ifdef HAVE_SYSCTL_NET_RT_DUMP FibConfigTableGetSysctl::FibConfigTableGetSysctl(FeaDataPlaneManager& fea_data_plane_manager) : FibConfigTableGet(fea_data_plane_manager) { } FibConfigTableGetSysctl::~FibConfigTableGetSysctl() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the sysctl(3) mechanism to get " "whole forwarding table from the underlying " "system: %s", error_msg.c_str()); } } int FibConfigTableGetSysctl::start(string& error_msg) { UNUSED(error_msg); if (_is_running) return (XORP_OK); _is_running = true; return (XORP_OK); } int FibConfigTableGetSysctl::stop(string& error_msg) { UNUSED(error_msg); if (! _is_running) return (XORP_OK); _is_running = false; return (XORP_OK); } int FibConfigTableGetSysctl::get_table4(list& fte_list) { list ftex_list; // Get the table if (get_table(AF_INET, ftex_list) != XORP_OK) return (XORP_ERROR); // Copy the result back to the original list list::iterator iter; for (iter = ftex_list.begin(); iter != ftex_list.end(); ++iter) { FteX& ftex = *iter; fte_list.push_back(ftex.get_fte4()); } return (XORP_OK); } int FibConfigTableGetSysctl::get_table6(list& fte_list) { #ifndef HAVE_IPV6 UNUSED(fte_list); return (XORP_ERROR); #else list ftex_list; // Get the table if (get_table(AF_INET6, ftex_list) != XORP_OK) return (XORP_ERROR); // Copy the result back to the original list list::iterator iter; for (iter = ftex_list.begin(); iter != ftex_list.end(); ++iter) { FteX& ftex = *iter; fte_list.push_back(ftex.get_fte6()); } return (XORP_OK); #endif // HAVE_IPV6 } int FibConfigTableGetSysctl::get_table(int family, list& fte_list) { int mib[6]; // Check that the family is supported switch(family) { case AF_INET: if (! fea_data_plane_manager().have_ipv4()) return (XORP_ERROR); break; #ifdef HAVE_IPV6 case AF_INET6: if (! fea_data_plane_manager().have_ipv6()) return (XORP_ERROR); break; #endif // HAVE_IPV6 default: XLOG_UNREACHABLE(); break; } mib[0] = CTL_NET; mib[1] = AF_ROUTE; mib[2] = 0; // protocol number - always 0 mib[3] = family; mib[4] = NET_RT_DUMP; mib[5] = 0; // no flags // Get the table size size_t sz; if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), NULL, &sz, NULL, 0) != 0) { XLOG_ERROR("sysctl(NET_RT_DUMP) failed: %s", strerror(errno)); return (XORP_ERROR); } // // XXX: we need to fetch the data in a loop, because its size // may increase between the two sysctl() calls. // for ( ; ; ) { vector buffer(sz); // Get the data if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), &buffer[0], &sz, NULL, 0) == 0) { // Check the new size if (buffer.size() < sz) continue; // XXX: shouldn't happen, but just in case if (buffer.size() > sz) buffer.resize(sz); // Parse the result return (parse_buffer_routing_socket(family, fibconfig().system_config_iftree(), fte_list, buffer, FibMsg::GETS)); } // Error if (errno == ENOMEM) { // Buffer is too small. Try again. continue; } XLOG_ERROR("sysctl(NET_RT_DUMP) failed: %s", strerror(errno)); return (XORP_ERROR); } } #endif // HAVE_SYSCTL_NET_RT_DUMP xorp/fea/data_plane/fibconfig/fibconfig_entry_set_click.hh0000664000076400007640000001215411540225523024143 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/data_plane/fibconfig/fibconfig_entry_set_click.hh,v 1.9 2008/10/02 21:56:57 bms Exp $ #ifndef __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_ENTRY_SET_CLICK_HH__ #define __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_ENTRY_SET_CLICK_HH__ #include #ifdef XORP_USE_CLICK #include "libxorp/ipv4net.hh" #include "libxorp/ipv6net.hh" #include "libxorp/time_slice.hh" #include "libxorp/timer.hh" #include "fea/fibconfig_entry_set.hh" #include "fea/nexthop_port_mapper.hh" #include "fea/data_plane/control_socket/click_socket.hh" class FibConfigEntrySetClick : public FibConfigEntrySet, public ClickSocket, public NexthopPortMapperObserver { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ FibConfigEntrySetClick(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~FibConfigEntrySetClick(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg); /** * Add a single IPv4 forwarding entry. * * Must be within a configuration interval. * * @param fte the entry to add. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int add_entry4(const Fte4& fte); /** * Delete a single IPv4 forwarding entry. * * Must be with a configuration interval. * * @param fte the entry to delete. Only destination and netmask are used. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int delete_entry4(const Fte4& fte); /** * Add a single IPv6 forwarding entry. * * Must be within a configuration interval. * * @param fte the entry to add. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int add_entry6(const Fte6& fte); /** * Delete a single IPv6 forwarding entry. * * Must be within a configuration interval. * * @param fte the entry to delete. Only destination and netmask are used. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int delete_entry6(const Fte6& fte); /** * Obtain a reference to the table with the IPv4 forwarding entries. * * @return a reference to the table with the IPv4 forwarding entries. */ const map& fte_table4() const { return _fte_table4; } /** * Obtain a reference to the table with the IPv6 forwarding entries. * * @return a reference to the table with the IPv6 forwarding entries. */ const map& fte_table6() const { return _fte_table6; } /** Routing table ID that we are interested in might have changed. */ virtual int notify_table_id_change(uint32_t new_tbl) { UNUSED(new_tbl); return XORP_OK; } private: virtual void nexthop_port_mapper_event(bool is_mapping_changed); int add_entry(const FteX& fte); int delete_entry(const FteX& fte); // Methods related to reinstalling all IPv4 and IPv6 forwarding entries void start_task_reinstall_all_entries(); void run_task_reinstall_all_entries(); /** * @return true if there are more entries to install, otherwise false. */ bool reinstall_all_entries4(); /** * @return true if there are more entries to install, otherwise false. */ bool reinstall_all_entries6(); ClickSocketReader _cs_reader; map _fte_table4; map _fte_table6; // State related to reinstalling all IPv4 and IPv6 forwarding entries XorpTimer _reinstall_all_entries_timer; TimeSlice _reinstall_all_entries_time_slice; bool _start_reinstalling_fte_table4; bool _is_reinstalling_fte_table4; bool _start_reinstalling_fte_table6; bool _is_reinstalling_fte_table6; IPv4Net _reinstalling_ipv4net; IPv6Net _reinstalling_ipv6net; }; #endif #endif // __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_ENTRY_SET_CLICK_HH__ xorp/fea/data_plane/fibconfig/fibconfig_forwarding_windows.cc0000664000076400007640000002300511540224222024654 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #ifdef HOST_OS_WINDOWS #include "libxorp/win_io.h" #endif #include "libcomm/comm_api.h" #ifdef HAVE_IPHLPAPI_H #include #endif #include "fea/fibconfig.hh" #include "fea/data_plane/control_socket/windows_rras_support.hh" #include "fibconfig_forwarding_windows.hh" #ifdef __MINGW32__ #define MIB_IP_FORWARDING 1 #define MIB_IP_NOT_FORWARDING 2 #endif // // Configure unicast forwarding. // // The mechanism to get/set the information is Windows. // #ifdef HOST_OS_WINDOWS FibConfigForwardingWindows::FibConfigForwardingWindows( FeaDataPlaneManager& fea_data_plane_manager) : FibConfigForwarding(fea_data_plane_manager), _event(NULL), _enablecnt(0) { memset(&_overlapped, 0, sizeof(_overlapped)); } FibConfigForwardingWindows::~FibConfigForwardingWindows() { } int FibConfigForwardingWindows::start(string& error_msg) { int ret_value; if (_event != NULL) return (XORP_OK); // Already started _event = CreateEvent(NULL, FALSE, FALSE, NULL); if (_event == NULL) XLOG_FATAL("Could not create Win32 event object."); memset(&_overlapped, 0, sizeof(_overlapped)); _overlapped.hEvent = _event; _enablecnt = 0; ret_value = FibConfigForwarding::start(error_msg); if (ret_value != XORP_OK) { string dummy_error_msg; stop(dummy_error_msg); } return (ret_value); } int FibConfigForwardingWindows::stop(string& error_msg) { if (_event == NULL) return (XORP_OK); // Not running if (_enablecnt > 0) { XLOG_WARNING("EnableRouter() without %d matching " "UnenableRouter() calls.", _enablecnt); } CloseHandle(_event); _event = NULL; return (FibConfigForwarding::stop(error_msg)); } int FibConfigForwardingWindows::unicast_forwarding_enabled4(bool& ret_value, string& error_msg) const { int enabled = 0; MIB_IPSTATS ipstats; DWORD error; if (! fea_data_plane_manager().have_ipv4()) { ret_value = false; error_msg = c_format("Cannot test whether IPv4 unicast forwarding " "is enabled: IPv4 is not supported"); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } // // Read the value // error = GetIpStatistics(&ipstats); if (error != NO_ERROR) { error_msg = c_format("GetIpStatistics() failed: %s", win_strerror(GetLastError())); return (XORP_ERROR); } enabled = (int)(ipstats.dwForwarding == MIB_IP_FORWARDING); if (enabled > 0) ret_value = true; else ret_value = false; return (XORP_OK); } int FibConfigForwardingWindows::unicast_forwarding_enabled6(bool& ret_value, string& error_msg) const { int enabled = 0; MIB_IPSTATS ipstats; DWORD error; if (! fea_data_plane_manager().have_ipv6()) { ret_value = false; error_msg = c_format("Cannot test whether IPv6 unicast forwarding " "is enabled: IPv6 is not supported"); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } UNUSED(ipstats); UNUSED(error); #if 0 // XXX: Not in MinGW w32api yet. error = GetIpStatisticsEx(&ipstats, AF_INET6); if (error != NO_ERROR) { error_msg = c_format("GetIpStatisticsEx() failed: %s", win_strerror(GetLastError())); return (XORP_ERROR); } enabled = (int)(ipstats.dwForwarding == MIB_IP_FORWARDING); #endif // 0 if (enabled > 0) ret_value = true; else ret_value = false; return (XORP_OK); } int FibConfigForwardingWindows::accept_rtadv_enabled6(bool& ret_value, string& error_msg) const { int enabled = 0; if (! fea_data_plane_manager().have_ipv6()) { ret_value = false; error_msg = c_format("Cannot test whether the acceptance of IPv6 " "Router Advertisement messages is enabled: " "IPv6 is not supported"); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } #ifdef HAVE_IPV6 // TODO: Implement this (Windows) #warning "Don't know how to test whether the acceptance of IPv6 " #warning "Router Advertisement messages is enabled" #endif // HAVE_IPV6 if (enabled > 0) ret_value = true; else ret_value = false; return (XORP_OK); } int FibConfigForwardingWindows::set_unicast_forwarding_enabled4(bool v, string& error_msg) { int enable = (v) ? 1 : 0; bool old_value; if (! fea_data_plane_manager().have_ipv4()) { if (! v) { // // XXX: we assume that "not supported" == "disable", hence // return OK. // return (XORP_OK); } error_msg = c_format("Cannot set IPv4 unicast forwarding to %s: " "IPv4 is not supported", bool_c_str(v)); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } // // Get the old value // if (unicast_forwarding_enabled4(old_value, error_msg) != XORP_OK) return (XORP_ERROR); if (old_value == v) return (XORP_OK); // Nothing changed // // Set the value // if (enable) { if (WinSupport::is_rras_running()) { XLOG_WARNING("RRAS is running; ignoring request to enable " "IPv4 forwarding."); return (XORP_OK); } HANDLE hFwd; DWORD result = EnableRouter(&hFwd, &_overlapped); if (result != ERROR_IO_PENDING) { error_msg = c_format("Error '%s' from EnableRouter", win_strerror(GetLastError())); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_OK); // XXX: This error is non-fatal. } ++_enablecnt; } else { if (WinSupport::is_rras_running()) { XLOG_WARNING("RRAS is running; ignoring request to disable " "IPv4 forwarding."); return (XORP_OK); } if (_enablecnt == 0) { error_msg = c_format("UnenableRouter() called without any previous " "call to EnableRouter()"); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_OK); // XXX: This error is non-fatal. } DWORD result = UnenableRouter(&_overlapped, NULL); if (result != NO_ERROR) { error_msg = c_format("Error '%s' from UnenableRouter", win_strerror(GetLastError())); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_OK); // XXX: This error is non-fatal. } --_enablecnt; } return (XORP_OK); } int FibConfigForwardingWindows::set_unicast_forwarding_enabled6(bool v, string& error_msg) { bool old_value, old_value_accept_rtadv; MIB_IPSTATS ipstats; DWORD error; if (! fea_data_plane_manager().have_ipv6()) { if (! v) { // // XXX: we assume that "not supported" == "disable", hence // return OK. // return (XORP_OK); } error_msg = c_format("Cannot set IPv6 unicast forwarding to %s: " "IPv6 is not supported", bool_c_str(v)); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } // // Get the old value // if (unicast_forwarding_enabled6(old_value, error_msg) != XORP_OK) return (XORP_ERROR); if (accept_rtadv_enabled6(old_value_accept_rtadv, error_msg) != XORP_OK) return (XORP_ERROR); if ((old_value == v) && (old_value_accept_rtadv == !v)) return (XORP_OK); // Nothing changed // // Set the IPv6 Router Advertisement value // if (set_accept_rtadv_enabled6(!v, error_msg) != XORP_OK) return (XORP_ERROR); UNUSED(ipstats); UNUSED(error); #if 0 // XXX: Not yet in MinGW w32api error = GetIpStatisticsEx(&ipstats, AF_INET6); if (error != NO_ERROR) { error_msg = c_format("GetIpStatisticsEx() failed: %s", win_strerror(GetLastError())); return (XORP_ERROR); } ipstats.dwForwarding = (enable != 0) ? 1 : 0; ipstats.dwDefaultTTL = MIB_USE_CURRENT_TTL; error = SetIpStatisticsEx(&ipstats, AF_INET6); if (error != NO_ERROR) { error_msg = c_format("SetIpStatisticsEx() failed: %s", win_strerror(GetLastError())); return (XORP_ERROR); } #endif // 0 return (XORP_OK); } int FibConfigForwardingWindows::set_accept_rtadv_enabled6(bool v, string& error_msg) { bool old_value; if (! fea_data_plane_manager().have_ipv6()) { if (! v) { // // XXX: we assume that "not supported" == "disable", hence // return OK. // return (XORP_OK); } error_msg = c_format("Cannot set the acceptance of IPv6 " "Router Advertisement messages to %s: " "IPv6 is not supported", bool_c_str(v)); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } // // Get the old value // if (accept_rtadv_enabled6(old_value, error_msg) != XORP_OK) return (XORP_ERROR); if (old_value == v) return (XORP_OK); // Nothing changed #ifdef HAVE_IPV6 // TODO: Implement this (Windows) #warning "Don't know how to enable/disable the acceptance of IPv6 " #warning "Router Advertisement messages" #endif // HAVE_IPV6 return (XORP_OK); } #endif // HOST_OS_WINDOWS xorp/fea/data_plane/fibconfig/fibconfig_entry_set_rtmv2.hh0000664000076400007640000000623511540224222024126 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_ENTRY_SET_RTMV2_HH__ #define __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_ENTRY_SET_RTMV2_HH__ #include "fea/fibconfig_entry_set.hh" class WinRtmPipe; class FibConfigEntrySetRtmV2 : public FibConfigEntrySet { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ FibConfigEntrySetRtmV2(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~FibConfigEntrySetRtmV2(); /** * Start operation. * * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg); /** * Add a single IPv4 forwarding entry. * * Must be within a configuration interval. * * @param fte the entry to add. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int add_entry4(const Fte4& fte); /** * Delete a single IPv4 forwarding entry. * * Must be with a configuration interval. * * @param fte the entry to delete. Only destination and netmask are used. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int delete_entry4(const Fte4& fte); /** * Add a single IPv6 forwarding entry. * * Must be within a configuration interval. * * @param fte the entry to add. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int add_entry6(const Fte6& fte); /** * Delete a single IPv6 forwarding entry. * * Must be within a configuration interval. * * @param fte the entry to delete. Only destination and netmask are used. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int delete_entry6(const Fte6& fte); virtual int notify_table_id_change(uint32_t new_tbl) { UNUSED(new_tbl); return 0; } private: int add_entry(const FteX& fte); int delete_entry(const FteX& fte); WinRtmPipe* _rs4; WinRtmPipe* _rs6; }; #endif // __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_ENTRY_SET_RTMV2_HH__ xorp/fea/data_plane/fibconfig/fibconfig_table_observer_rtmv2.cc0000664000076400007640000001064011540224222025071 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "fea/fibconfig.hh" #ifdef HOST_OS_WINDOWS #include "fea/data_plane/control_socket/windows_rras_support.hh" #endif #include "fibconfig_table_get_sysctl.hh" #include "fibconfig_table_observer_rtmv2.hh" // // Observe whole-table information change about the unicast forwarding table. // // E.g., if the forwarding table has changed, then the information // received by the observer would NOT specify the particular entry that // has changed. // // The mechanism to observe the information is Router Manager V2. // #ifdef HOST_OS_WINDOWS FibConfigTableObserverRtmV2::FibConfigTableObserverRtmV2(FeaDataPlaneManager& fea_data_plane_manager) : FibConfigTableObserver(fea_data_plane_manager), _rs4(NULL), _rso4(NULL), _rs6(NULL), _rso6(NULL) { if (!WinSupport::is_rras_running()) { XLOG_WARNING("RRAS is not running; disabling FibConfigTableObserverRtmV2."); return; } _rs4 = new WinRtmPipe(fea_data_plane_manager.eventloop()); _rso4 = new RtmV2Observer(*_rs4, AF_INET, *this); #ifdef HAVE_IPV6 _rs6 = new WinRtmPipe(fea_data_plane_manager.eventloop()); _rso6 = new RtmV2Observer(*_rs6, AF_INET6, *this); #endif } FibConfigTableObserverRtmV2::~FibConfigTableObserverRtmV2() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the Router Manager V2 mechanism to observe " "whole forwarding table from the underlying " "system: %s", error_msg.c_str()); } #ifdef HAVE_IPV6 if (_rso6) delete _rso6; if (_rs6) delete _rs6; #endif if (_rso4) delete _rso4; if (_rs4) delete _rs4; } int FibConfigTableObserverRtmV2::start(string& error_msg) { if (_is_running) return (XORP_OK); if (_rs4 == NULL || (_rs4->start(AF_INET, error_msg) != XORP_OK)) return (XORP_ERROR); #if 0 #ifdef HAVE_IPV6 if (_rs6 == NULL || (_rs6->start(AF_INET6, error_msg) != XORP_OK)) return (XORP_ERROR); #endif #endif _is_running = true; return (XORP_OK); } int FibConfigTableObserverRtmV2::stop(string& error_msg) { int result = XORP_OK; if (! _is_running) return (result); if (_rs4 == NULL || (_rs4->stop(error_msg) != XORP_OK)) result = XORP_ERROR; #if 0 #ifdef HAVE_IPV6 if (rs6 == NULL || (_rs6->stop(error_msg) != XORP_OK)) result = XORP_ERROR; #endif #endif _is_running = false; return (result); } void FibConfigTableObserverRtmV2::receive_data(const vector& buffer) { list fte_list; FibConfigTableGetSysctl::FibMsgSet filter; filter = FibConfigTableGetSysctl::FibMsg::UPDATES | FibConfigTableGetSysctl::FibMsg::GETS; // // Get the IPv4 routes // if (fea_data_plane_manager().have_ipv4() && _rs4->is_open()) { FibConfigTableGetSysctl::parse_buffer_routing_socket(AF_INET, fibconfig().system_config_iftree(), fte_list, buffer, filter); if (! fte_list.empty()) { fibconfig().propagate_fib_changes(fte_list, this); fte_list.clear(); } } #ifdef HAVE_IPV6 // // Get the IPv6 routes // if (fea_data_plane_manager().have_ipv6() && _rs6->is_open()) { FibConfigTableGetSysctl::parse_buffer_routing_socket(AF_INET6, fibconfig().system_config_iftree(), fte_list, buffer, filter); if (! fte_list.empty()) { fibconfig().propagate_fib_changes(fte_list, this); fte_list.clear(); } } #endif // HAVE_IPV6 } #endif // HOST_OS_WINDOWS xorp/fea/data_plane/fibconfig/fibconfig_entry_observer_netlink_socket.hh0000664000076400007640000000515211540225523027126 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_ENTRY_OBSERVER_NETLINK_SOCKET_HH__ #define __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_ENTRY_OBSERVER_NETLINK_SOCKET_HH__ #include #ifdef HAVE_NETLINK_SOCKETS #include "fea/fibconfig_entry_observer.hh" #include "fea/data_plane/control_socket/netlink_socket.hh" class FibConfigEntryObserverNetlinkSocket : public FibConfigEntryObserver, public NetlinkSocket, public NetlinkSocketObserver { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ FibConfigEntryObserverNetlinkSocket(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~FibConfigEntryObserverNetlinkSocket(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg); /** * Receive data from the underlying system. * * @param buffer the buffer with the received data. */ virtual void receive_data(const vector& buffer); void netlink_socket_data(const vector& buffer); /** Routing table ID that we are interested in might have changed. */ virtual int notify_table_id_change(uint32_t new_tbl) { return NetlinkSocket::notify_table_id_change(new_tbl); } private: }; #endif #endif // __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_ENTRY_OBSERVER_NETLINK_SOCKET_HH__ xorp/fea/data_plane/fibconfig/fibconfig_table_get_iphelper.cc0000664000076400007640000001265311540224222024565 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/win_io.h" #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_IPHLPAPI_H #include #endif #ifdef HAVE_ROUTPROT_H #include #endif #ifdef HAVE_IPRTRMIB_H #include #endif #include "fea/fibconfig.hh" #include "fibconfig_table_get_iphelper.hh" #ifndef MIB_IPROUTE_TYPE_DIRECT #define MIB_IPROUTE_TYPE_DIRECT (3) #endif // // Get the whole table information from the unicast forwarding table. // // The mechanism to obtain the information is the IP Helper API for // Windows (IPHLPAPI.DLL). // #ifdef HOST_OS_WINDOWS FibConfigTableGetIPHelper::FibConfigTableGetIPHelper(FeaDataPlaneManager& fea_data_plane_manager) : FibConfigTableGet(fea_data_plane_manager) { } FibConfigTableGetIPHelper::~FibConfigTableGetIPHelper() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the IP Helper API mechanism to get " "whole forwarding table from the underlying " "system: %s", error_msg.c_str()); } } int FibConfigTableGetIPHelper::start(string& error_msg) { UNUSED(error_msg); if (_is_running) return (XORP_OK); _is_running = true; return (XORP_OK); } int FibConfigTableGetIPHelper::stop(string& error_msg) { UNUSED(error_msg); if (! _is_running) return (XORP_OK); _is_running = false; return (XORP_OK); } int FibConfigTableGetIPHelper::get_table4(list& fte_list) { list ftex_list; // Get the table if (get_table(AF_INET, ftex_list) != XORP_OK) return (XORP_ERROR); // Copy the result back to the original list list::iterator iter; for (iter = ftex_list.begin(); iter != ftex_list.end(); ++iter) { FteX& ftex = *iter; fte_list.push_back(ftex.get_fte4()); } return (XORP_OK); } int FibConfigTableGetIPHelper::get_table6(list& fte_list) { UNUSED(fte_list); return (XORP_ERROR); } int FibConfigTableGetIPHelper::get_table(int family, list& fte_list) { // Check that the family is supported switch(family) { case AF_INET: if (! fea_data_plane_manager().have_ipv4()) return (XORP_ERROR); break; #ifdef HAVE_IPV6 case AF_INET6: return (XORP_ERROR); break; #endif // HAVE_IPV6 default: XLOG_UNREACHABLE(); break; } PMIB_IPFORWARDTABLE pFwdTable = NULL; DWORD result, tries; ULONG dwSize; tries = 0; result = ERROR_INSUFFICIENT_BUFFER; dwSize = sizeof(MIB_IPFORWARDTABLE); do { pFwdTable = (PMIB_IPFORWARDTABLE) ((tries == 0) ? malloc(dwSize) : realloc(pFwdTable, dwSize)); if (pFwdTable == NULL) break; result = GetIpForwardTable(pFwdTable, &dwSize, TRUE); } while ((++tries < 3) || (result == ERROR_INSUFFICIENT_BUFFER)); if (result != NO_ERROR) { XLOG_ERROR("GetIpForwardTable(): %s\n", win_strerror(result)); if (pFwdTable != NULL) free(pFwdTable); return (XORP_ERROR); } IPv4 dst_addr; IPv4 dst_mask; IPv4 nexthop_addr; bool xorp_route; for (unsigned int i = 0; i < pFwdTable->dwNumEntries; i++) { PMIB_IPFORWARDROW pFwdRow = &pFwdTable->table[i]; dst_addr.copy_in(reinterpret_cast(&pFwdRow->dwForwardDest)); dst_mask.copy_in(reinterpret_cast(&pFwdRow->dwForwardMask)); nexthop_addr.copy_in(reinterpret_cast( &pFwdRow->dwForwardNextHop)); // XXX: No easy way of telling XORP routes apart from manually // added statics in NT. if (pFwdRow->dwForwardProto == PROTO_IP_NETMGMT) xorp_route = true; uint32_t ifindex = static_cast(pFwdRow->dwForwardIfIndex); const IfTree& iftree = fibconfig().merged_config_iftree(); const IfTreeVif* vifp = iftree.find_vif(ifindex); string if_name; string vif_name; if (vifp != NULL) { if_name = vifp->ifname(); vif_name = vifp->vifname(); } else { if_name = "unknown"; vif_name = "unknown"; XLOG_WARNING("Route via unknown interface index %u, iftree: %s", XORP_UINT_CAST(ifindex), iftree.getName().c_str()); } // TODO: define default routing metric and admin distance. Fte4 fte4 = Fte4(IPv4Net(dst_addr, dst_mask.mask_len()), nexthop_addr, if_name, vif_name, 0xffff, 0xffff, xorp_route); // If the next hop is the final destination, then consider // the route to be a 'connected' route. if (pFwdRow->dwForwardType == MIB_IPROUTE_TYPE_DIRECT) fte4.mark_connected_route(); debug_msg("adding entry %s\n", fte4.str().c_str()); fte_list.push_back(fte4); } free(pFwdTable); return (XORP_OK); } #endif // HOST_OS_WINDOWS xorp/fea/data_plane/fibconfig/fibconfig_entry_get_iphelper.hh0000664000076400007640000000742611540224221024652 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_ENTRY_GET_IPHELPER_HH__ #define __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_ENTRY_GET_IPHELPER_HH__ #include "fea/fibconfig_entry_get.hh" class FibConfigEntryGetIPHelper : public FibConfigEntryGet { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ FibConfigEntryGetIPHelper(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~FibConfigEntryGetIPHelper(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg); /** * Lookup an IPv4 route by destination address. * * @param dst host address to resolve. * @param fte return-by-reference forwarding table entry. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int lookup_route_by_dest4(const IPv4& dst, Fte4& fte); /** * Lookup an IPv4 route by network address. * * @param dst network address to resolve. * @param fte return-by-reference forwarding table entry. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int lookup_route_by_network4(const IPv4Net& dst, Fte4& fte); /** * Lookup an IPv6 route by destination address. * * @param dst host address to resolve. * @param fte return-by-reference forwarding table entry. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int lookup_route_by_dest6(const IPv6& dst, Fte6& fte); /** * Lookup an IPv6 route by network address. * * @param dst network address to resolve. * @param fte return-by-reference forwarding table entry. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int lookup_route_by_network6(const IPv6Net& dst, Fte6& fte); virtual int notify_table_id_change(uint32_t new_tbl) { UNUSED(new_tbl); return 0; } private: /** * Lookup a route by destination address. * * @param dst host address to resolve. * @param fte return-by-reference forwarding table entry. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int lookup_route_by_dest(const IPvX& dst, FteX& fte); /** * Lookup a route by network address. * * @param dst network address to resolve. * @param fte return-by-reference forwarding table entry. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int lookup_route_by_network(const IPvXNet& dst, FteX& fte); }; #endif // __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_ENTRY_GET_IPHELPER_HH__ xorp/fea/data_plane/fibconfig/fibconfig_entry_parse_routing_socket.cc0000664000076400007640000000701311540225523026420 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include #ifdef HAVE_ROUTING_SOCKETS #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #ifdef HAVE_NET_ROUTE_H #include #endif #ifdef HOST_OS_WINDOWS #include "fea/data_plane/control_socket/windows_routing_socket.h" #endif #include "fea/fibconfig.hh" #include "fea/data_plane/control_socket/routing_socket_utilities.hh" #include "fibconfig_entry_get_routing_socket.hh" // // Parse information about forwarding entry information received from // the underlying system. // // The information to parse is in RTM format // (e.g., obtained by routing sockets or by sysctl(3) mechanism). // // Reading route(4) manual page is a good start for understanding this // int FibConfigEntryGetRoutingSocket::parse_buffer_routing_socket( const IfTree& iftree, FteX& fte, const vector& buffer, FibMsgSet filter) { AlignData align_data(buffer); const struct rt_msghdr* rtm; size_t offset; rtm = align_data.payload(); for (offset = 0; offset < buffer.size(); offset += rtm->rtm_msglen) { bool filter_match = false; rtm = align_data.payload_by_offset(offset); if (rtm->rtm_version != RTM_VERSION) { #if defined(RTM_OVERSION) && defined(HOST_OS_OPENBSD) // // XXX: Silently ignore old messages. // The OpenBSD kernel sends each message twice, once as // RTM_VERSION and once as RTM_OVERSION, hence we need to ignore // the RTM_OVERSION duplicates. // if (rtm->rtm_version == RTM_OVERSION) continue; #endif // RTM_OVERSION && HOST_OS_OPENBSD XLOG_ERROR("RTM version mismatch: expected %d got %d", RTM_VERSION, rtm->rtm_version); continue; } // XXX: ignore entries with an error if (rtm->rtm_errno != 0) continue; // Caller wants route gets to be parsed. if (filter & FibMsg::GETS) { #ifdef RTM_GET if ((rtm->rtm_type == RTM_GET) && (rtm->rtm_flags & RTF_UP)) filter_match = true; #endif } // Caller wants route resolves to be parsed. // Resolves may not be supported in some implementations. if (filter & FibMsg::RESOLVES) { #ifdef RTM_MISS if (rtm->rtm_type == RTM_MISS) filter_match = true; #endif #ifdef RTM_RESOLVE if (rtm->rtm_type == RTM_RESOLVE) filter_match = true; #endif } // Caller wants routing table updates to be parsed. if (filter & FibMsg::UPDATES) { if ((rtm->rtm_type == RTM_ADD) || (rtm->rtm_type == RTM_DELETE) || (rtm->rtm_type == RTM_CHANGE)) filter_match = true; } if (filter_match) return (RtmUtils::rtm_get_to_fte_cfg(iftree, fte, rtm)); } return (XORP_ERROR); } #endif // HAVE_ROUTING_SOCKETS xorp/fea/data_plane/fibconfig/fibconfig_forwarding_proc_linux.cc0000664000076400007640000002105111421137511025345 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libcomm/comm_api.h" #include "fea/fibconfig.hh" #include "fibconfig_forwarding_proc_linux.hh" const string FibConfigForwardingProcLinux::PROC_LINUX_FORWARDING_FILE_V4 = "/proc/sys/net/ipv4/ip_forward"; const string FibConfigForwardingProcLinux::PROC_LINUX_FORWARDING_FILE_V6 = "/proc/sys/net/ipv6/conf/all/forwarding"; // // Configure unicast forwarding. // // The mechanism to get/set the information is Linux "/proc" file system. // #ifdef HAVE_PROC_LINUX FibConfigForwardingProcLinux::FibConfigForwardingProcLinux( FeaDataPlaneManager& fea_data_plane_manager) : FibConfigForwarding(fea_data_plane_manager) { } FibConfigForwardingProcLinux::~FibConfigForwardingProcLinux() { } int FibConfigForwardingProcLinux::unicast_forwarding_enabled4(bool& ret_value, string& error_msg) const { int enabled = 0; FILE* fh; if (! fea_data_plane_manager().have_ipv4()) { ret_value = false; error_msg = c_format("Cannot test whether IPv4 unicast forwarding " "is enabled: IPv4 is not supported"); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } // // Read the value from the corresponding "/proc" file system entry // fh = fopen(PROC_LINUX_FORWARDING_FILE_V4.c_str(), "r"); if (fh == NULL) { error_msg = c_format("Cannot open file %s for reading: %s", PROC_LINUX_FORWARDING_FILE_V4.c_str(), strerror(errno)); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } if (fscanf(fh, "%d", &enabled) != 1) { error_msg = c_format("Error reading file %s: %s", PROC_LINUX_FORWARDING_FILE_V4.c_str(), strerror(errno)); XLOG_ERROR("%s", error_msg.c_str()); fclose(fh); return (XORP_ERROR); } fclose(fh); if (enabled > 0) ret_value = true; else ret_value = false; return (XORP_OK); } int FibConfigForwardingProcLinux::unicast_forwarding_enabled6(bool& ret_value, string& error_msg) const { int enabled = 0; FILE* fh; if (! fea_data_plane_manager().have_ipv6()) { ret_value = false; error_msg = c_format("Cannot test whether IPv6 unicast forwarding " "is enabled: IPv6 is not supported"); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } // // Read the value from the corresponding "/proc" file system entry // fh = fopen(PROC_LINUX_FORWARDING_FILE_V6.c_str(), "r"); if (fh == NULL) { error_msg = c_format("Cannot open file %s for reading: %s", PROC_LINUX_FORWARDING_FILE_V6.c_str(), strerror(errno)); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } if (fscanf(fh, "%d", &enabled) != 1) { error_msg = c_format("Error reading file %s: %s", PROC_LINUX_FORWARDING_FILE_V6.c_str(), strerror(errno)); XLOG_ERROR("%s", error_msg.c_str()); fclose(fh); return (XORP_ERROR); } fclose(fh); if (enabled > 0) ret_value = true; else ret_value = false; return (XORP_OK); } int FibConfigForwardingProcLinux::accept_rtadv_enabled6(bool& ret_value, string& error_msg) const { int enabled = 0; if (! fea_data_plane_manager().have_ipv6()) { ret_value = false; error_msg = c_format("Cannot test whether the acceptance of IPv6 " "Router Advertisement messages is enabled: " "IPv6 is not supported"); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } // XXX: nothing to do in case of Linux if (enabled > 0) ret_value = true; else ret_value = false; return (XORP_OK); } int FibConfigForwardingProcLinux::set_unicast_forwarding_enabled4(bool v, string& error_msg) { int enable = (v) ? 1 : 0; bool old_value; FILE* fh; if (! fea_data_plane_manager().have_ipv4()) { if (! v) { // // XXX: we assume that "not supported" == "disable", hence // return OK. // return (XORP_OK); } error_msg = c_format("Cannot set IPv4 unicast forwarding to %s: " "IPv4 is not supported", bool_c_str(v)); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } // // Get the old value // if (unicast_forwarding_enabled4(old_value, error_msg) != XORP_OK) return (XORP_ERROR); if (old_value == v) return (XORP_OK); // Nothing changed // // Write the value to the corresponding "/proc" file system entry // fh = fopen(PROC_LINUX_FORWARDING_FILE_V4.c_str(), "w"); if (fh == NULL) { error_msg = c_format("Cannot open file %s for writing: %s", PROC_LINUX_FORWARDING_FILE_V4.c_str(), strerror(errno)); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } if (fprintf(fh, "%d", enable) != 1) { error_msg = c_format("Error writing %d to file %s: %s", enable, PROC_LINUX_FORWARDING_FILE_V4.c_str(), strerror(errno)); XLOG_ERROR("%s", error_msg.c_str()); fclose(fh); return (XORP_ERROR); } fclose(fh); return (XORP_OK); } int FibConfigForwardingProcLinux::set_unicast_forwarding_enabled6(bool v, string& error_msg) { int enable = (v) ? 1 : 0; bool old_value, old_value_accept_rtadv; FILE* fh; if (! fea_data_plane_manager().have_ipv6()) { if (! v) { // // XXX: we assume that "not supported" == "disable", hence // return OK. // return (XORP_OK); } error_msg = c_format("Cannot set IPv6 unicast forwarding to %s: " "IPv6 is not supported", bool_c_str(v)); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } // // Get the old value // if (unicast_forwarding_enabled6(old_value, error_msg) != XORP_OK) return (XORP_ERROR); if (accept_rtadv_enabled6(old_value_accept_rtadv, error_msg) != XORP_OK) return (XORP_ERROR); if ((old_value == v) && (old_value_accept_rtadv == !v)) return (XORP_OK); // Nothing changed // // Set the IPv6 Router Advertisement value // if (set_accept_rtadv_enabled6(!v, error_msg) != XORP_OK) return (XORP_ERROR); // // Write the value to the corresponding "/proc" file system entry // fh = fopen(PROC_LINUX_FORWARDING_FILE_V6.c_str(), "w"); if (fh == NULL) { error_msg = c_format("Cannot open file %s for writing: %s", PROC_LINUX_FORWARDING_FILE_V6.c_str(), strerror(errno)); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } if (fprintf(fh, "%d", enable) != 1) { error_msg = c_format("Error writing %d to file %s: %s", enable, PROC_LINUX_FORWARDING_FILE_V6.c_str(), strerror(errno)); XLOG_ERROR("%s", error_msg.c_str()); // Restore the old accept_rtadv value if (old_value_accept_rtadv != !v) { string dummy_error_msg; set_accept_rtadv_enabled6(old_value_accept_rtadv, dummy_error_msg); } fclose(fh); return (XORP_ERROR); } fclose(fh); return (XORP_OK); } int FibConfigForwardingProcLinux::set_accept_rtadv_enabled6(bool v, string& error_msg) { int enable = (v) ? 1 : 0; bool old_value; if (! fea_data_plane_manager().have_ipv6()) { if (! v) { // // XXX: we assume that "not supported" == "disable", hence // return OK. // return (XORP_OK); } error_msg = c_format("Cannot set the acceptance of IPv6 " "Router Advertisement messages to %s: " "IPv6 is not supported", bool_c_str(v)); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } // // Get the old value if (accept_rtadv_enabled6(old_value, error_msg) != XORP_OK) return (XORP_ERROR); if (old_value == v) return (XORP_OK); // Nothing changed // XXX: nothing to do in case of Linux UNUSED(enable); return (XORP_OK); } #endif // HAVE_PROC_LINUX xorp/fea/data_plane/fibconfig/fibconfig_table_get_sysctl.hh0000664000076400007640000000773211540225523024317 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/data_plane/fibconfig/fibconfig_table_get_sysctl.hh,v 1.9 2008/10/02 21:56:59 bms Exp $ #ifndef __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_TABLE_GET_SYSCTL_HH__ #define __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_TABLE_GET_SYSCTL_HH__ #include "fea/fibconfig_table_get.hh" class FibConfigTableGetSysctl : public FibConfigTableGet { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ FibConfigTableGetSysctl(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~FibConfigTableGetSysctl(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg); /** * Obtain the IPv4 unicast forwarding table. * * @param fte_list the return-by-reference list with all entries in * the IPv4 unicast forwarding table. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int get_table4(list& fte_list); /** * Obtain the IPv6 unicast forwarding table. * * @param fte_list the return-by-reference list with all entries in * the IPv6 unicast forwarding table. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int get_table6(list& fte_list); /** * Flag values used to tell underlying FIB message parsing routines * which messages the caller is interested in. */ struct FibMsg { enum { UPDATES = 1 << 0, GETS = 1 << 1, RESOLVES = 1 << 2 }; }; typedef uint32_t FibMsgSet; /** * Parse information about routing table information received from * the underlying system. * * The information to parse is in RTM format * (e.g., obtained by routing sockets or by sysctl(3) mechanism). * * @param family the address family to consider only ((e.g., AF_INET * or AF_INET6 for IPv4 and IPv6 respectively). * @param iftree the interface tree to use. * @param fte_list the list with the Fte entries to store the result. * @param buffer the buffer with the data to parse. * @param filter the set of messages that caller is interested in. * @return XORP_OK on success, otherwise XORP_ERROR. * @see FteX. */ static int parse_buffer_routing_socket(int family, const IfTree& iftree, list& fte_list, const vector& buffer, FibMsgSet filter); /** Routing table ID that we are interested in might have changed. */ virtual int notify_table_id_change(uint32_t new_tbl) { // Virtual routing tables not implemented on BSD UNUSED(new_tbl); return XORP_OK; } private: int get_table(int family, list& fte_list); }; #endif // __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_TABLE_GET_SYSCTL_HH__ xorp/fea/data_plane/fibconfig/fibconfig_entry_observer_dummy.cc0000664000076400007640000000441411421137511025230 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "fea/fibconfig.hh" #include "fibconfig_entry_observer_dummy.hh" // // Observe single-entry information change about the unicast forwarding table. // // E.g., if the forwarding table has changed, then the information // received by the observer would specify the particular entry that // has changed. // // The mechanism to observe the information is Dummy (for testing purpose). // FibConfigEntryObserverDummy::FibConfigEntryObserverDummy(FeaDataPlaneManager& fea_data_plane_manager) : FibConfigEntryObserver(fea_data_plane_manager) { } FibConfigEntryObserverDummy::~FibConfigEntryObserverDummy() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the Dummy mechanism to observe " "information about forwarding table from the underlying " "system: %s", error_msg.c_str()); } } int FibConfigEntryObserverDummy::start(string& error_msg) { UNUSED(error_msg); if (_is_running) return (XORP_OK); _is_running = true; return (XORP_OK); } int FibConfigEntryObserverDummy::stop(string& error_msg) { UNUSED(error_msg); if (! _is_running) return (XORP_OK); _is_running = false; return (XORP_OK); } void FibConfigEntryObserverDummy::receive_data(const vector& buffer) { // TODO: use it? UNUSED(buffer); } xorp/fea/data_plane/fibconfig/fibconfig_entry_get_routing_socket.cc0000664000076400007640000002333311540225522026067 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include #ifdef HAVE_ROUTING_SOCKETS #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvxnet.hh" #ifdef HAVE_NET_ROUTE_H #include #endif #ifdef HAVE_NET_IF_DL_H #include #endif #include "fea/fibconfig.hh" #include "fibconfig_entry_get_routing_socket.hh" // // Get single-entry information from the unicast forwarding table. // // The mechanism to obtain the information is routing sockets. // FibConfigEntryGetRoutingSocket::FibConfigEntryGetRoutingSocket(FeaDataPlaneManager& fea_data_plane_manager) : FibConfigEntryGet(fea_data_plane_manager), RoutingSocket(fea_data_plane_manager.eventloop()), _rs_reader(*(RoutingSocket *)this) { } FibConfigEntryGetRoutingSocket::~FibConfigEntryGetRoutingSocket() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the routing sockets mechanism to get " "information about forwarding table from the underlying " "system: %s", error_msg.c_str()); } } int FibConfigEntryGetRoutingSocket::start(string& error_msg) { if (_is_running) return (XORP_OK); if (RoutingSocket::start(error_msg) != XORP_OK) return (XORP_ERROR); _is_running = true; return (XORP_OK); } int FibConfigEntryGetRoutingSocket::stop(string& error_msg) { if (! _is_running) return (XORP_OK); if (RoutingSocket::stop(error_msg) != XORP_OK) return (XORP_ERROR); _is_running = false; return (XORP_OK); } int FibConfigEntryGetRoutingSocket::lookup_route_by_dest4(const IPv4& dst, Fte4& fte) { FteX ftex(dst.af()); int ret_value = XORP_ERROR; ret_value = lookup_route_by_dest(IPvX(dst), ftex); fte = ftex.get_fte4(); return (ret_value); } int FibConfigEntryGetRoutingSocket::lookup_route_by_network4(const IPv4Net& dst, Fte4& fte) { FteX ftex(dst.af()); int ret_value = XORP_ERROR; ret_value = lookup_route_by_network(IPvXNet(dst), ftex); fte = ftex.get_fte4(); return (ret_value); } int FibConfigEntryGetRoutingSocket::lookup_route_by_dest6(const IPv6& dst, Fte6& fte) { FteX ftex(dst.af()); int ret_value = XORP_ERROR; ret_value = lookup_route_by_dest(IPvX(dst), ftex); fte = ftex.get_fte6(); return (ret_value); } int FibConfigEntryGetRoutingSocket::lookup_route_by_network6(const IPv6Net& dst, Fte6& fte) { FteX ftex(dst.af()); int ret_value = XORP_ERROR; ret_value = lookup_route_by_network(IPvXNet(dst), ftex); fte = ftex.get_fte6(); return (ret_value); } int FibConfigEntryGetRoutingSocket::lookup_route_by_dest(const IPvX& dst, FteX& fte) { static const size_t buffer_size = sizeof(struct rt_msghdr) + 512; union { uint8_t data[buffer_size]; struct rt_msghdr rtm; } buffer; struct rt_msghdr* rtm = &buffer.rtm; struct sockaddr_in* sin; RoutingSocket& rs = *this; // Zero the return information fte.zero(); // Check that the family is supported do { if (dst.is_ipv4()) { if (! fea_data_plane_manager().have_ipv4()) return (XORP_ERROR); break; } if (dst.is_ipv6()) { if (! fea_data_plane_manager().have_ipv6()) return (XORP_ERROR); break; } break; } while (false); // Check that the destination address is valid if (! dst.is_unicast()) { return (XORP_ERROR); } // // Set the request // memset(&buffer, 0, sizeof(buffer)); switch (dst.af()) { case AF_INET: rtm->rtm_msglen = sizeof(*rtm) + sizeof(struct sockaddr_in); break; #ifdef HAVE_IPV6 case AF_INET6: rtm->rtm_msglen = sizeof(*rtm) + sizeof(struct sockaddr_in6); break; #endif // HAVE_IPV6 default: XLOG_UNREACHABLE(); break; } rtm->rtm_version = RTM_VERSION; rtm->rtm_type = RTM_GET; rtm->rtm_addrs = (RTA_DST | RTA_IFP); rtm->rtm_flags = RTF_UP; rtm->rtm_pid = rs.pid(); rtm->rtm_seq = rs.seqno(); // Copy the destination address sin = reinterpret_cast(rtm + 1); dst.copy_out(*sin); // // Add extra space for sockaddr_dl that corresponds to the RTA_IFP flag. // Required if the OS is very strict in the arguments checking // (e.g., NetBSD). // #ifdef AF_LINK do { // Set the data-link socket struct sockaddr_dl* sdl; rtm->rtm_msglen += sizeof(struct sockaddr_dl); switch (dst.af()) { case AF_INET: sdl = ADD_POINTER(sin, sizeof(struct sockaddr_in), struct sockaddr_dl*); break; #ifdef HAVE_IPV6 case AF_INET6: sdl = ADD_POINTER(sin, sizeof(struct sockaddr_in6), struct sockaddr_dl*); break; #endif // HAVE_IPV6 default: XLOG_UNREACHABLE(); break; } sdl->sdl_family = AF_LINK; #ifdef HAVE_STRUCT_SOCKADDR_DL_SDL_LEN sdl->sdl_len = sizeof(struct sockaddr_dl); #endif } while (false); #endif // AF_LINK if (rs.write(rtm, rtm->rtm_msglen) != rtm->rtm_msglen) { XLOG_ERROR("Error writing to routing socket: %s", strerror(errno)); return (XORP_ERROR); } // // Force to receive data from the kernel, and then parse it // string error_msg; if (_rs_reader.receive_data(rs, rtm->rtm_seq, error_msg) != XORP_OK) { XLOG_ERROR("Error reading from routing socket: %s", error_msg.c_str()); return (XORP_ERROR); } if (parse_buffer_routing_socket(fibconfig().system_config_iftree(), fte, _rs_reader.buffer(), FibMsg::GETS) != XORP_OK) { return (XORP_ERROR); } return (XORP_OK); } int FibConfigEntryGetRoutingSocket::lookup_route_by_network(const IPvXNet& dst, FteX& fte) { static const size_t buffer_size = sizeof(struct rt_msghdr) + 512; union { uint8_t data[buffer_size]; struct rt_msghdr rtm; } buffer; struct rt_msghdr* rtm = &buffer.rtm; struct sockaddr_in* sin; RoutingSocket& rs = *this; // Zero the return information fte.zero(); // Check that the family is supported do { if (dst.is_ipv4()) { if (! fea_data_plane_manager().have_ipv4()) return (XORP_ERROR); break; } if (dst.is_ipv6()) { if (! fea_data_plane_manager().have_ipv6()) return (XORP_ERROR); break; } break; } while (false); // Check that the destination address is valid if (! dst.is_unicast()) { return (XORP_ERROR); } // // Set the request // memset(&buffer, 0, sizeof(buffer)); switch (dst.af()) { case AF_INET: rtm->rtm_msglen = sizeof(*rtm) + 2 * sizeof(struct sockaddr_in); break; #ifdef HAVE_IPV6 case AF_INET6: rtm->rtm_msglen = sizeof(*rtm) + 2 * sizeof(struct sockaddr_in6); break; #endif // HAVE_IPV6 default: XLOG_UNREACHABLE(); break; } rtm->rtm_version = RTM_VERSION; rtm->rtm_type = RTM_GET; rtm->rtm_addrs = (RTA_DST | RTA_NETMASK | RTA_IFP); rtm->rtm_flags = RTF_UP; rtm->rtm_pid = rs.pid(); rtm->rtm_seq = rs.seqno(); // Copy the destination address sin = reinterpret_cast(rtm + 1); dst.masked_addr().copy_out(*sin); // Copy the network mask switch (dst.af()) { case AF_INET: sin = ADD_POINTER(sin, sizeof(struct sockaddr_in), struct sockaddr_in*); break; #ifdef HAVE_IPV6 case AF_INET6: sin = ADD_POINTER(sin, sizeof(struct sockaddr_in6), struct sockaddr_in*); break; #endif // HAVE_IPV6 default: XLOG_UNREACHABLE(); break; } IPvX netmask = IPvX::make_prefix(dst.af(), dst.prefix_len()); netmask.copy_out(*sin); // // Add extra space for sockaddr_dl that corresponds to the RTA_IFP flag. // Required if the OS is very strict in the arguments checking // (e.g., NetBSD). // #ifdef AF_LINK do { // Set the data-link socket struct sockaddr_dl* sdl; rtm->rtm_msglen += sizeof(struct sockaddr_dl); switch (dst.af()) { case AF_INET: sdl = ADD_POINTER(sin, sizeof(struct sockaddr_in), struct sockaddr_dl*); break; #ifdef HAVE_IPV6 case AF_INET6: sdl = ADD_POINTER(sin, sizeof(struct sockaddr_in6), struct sockaddr_dl*); break; #endif // HAVE_IPV6 default: XLOG_UNREACHABLE(); break; } sdl->sdl_family = AF_LINK; #ifdef HAVE_STRUCT_SOCKADDR_DL_SDL_LEN sdl->sdl_len = sizeof(struct sockaddr_dl); #endif } while (false); #endif // AF_LINK if (rs.write(rtm, rtm->rtm_msglen) != rtm->rtm_msglen) { XLOG_ERROR("Error writing to routing socket: %s", strerror(errno)); return (XORP_ERROR); } // // Force to receive data from the kernel, and then parse it // string error_msg; if (_rs_reader.receive_data(rs, rtm->rtm_seq, error_msg) != XORP_OK) { XLOG_ERROR("Error reading from routing socket: %s", error_msg.c_str()); return (XORP_ERROR); } if (parse_buffer_routing_socket(fibconfig().system_config_iftree(), fte, _rs_reader.buffer(), FibMsg::GETS) != XORP_OK) { return (XORP_ERROR); } return (XORP_OK); } #endif // HAVE_ROUTING_SOCKETS xorp/fea/data_plane/fibconfig/fibconfig_forwarding_sysctl.hh0000664000076400007640000001027011421137511024517 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2007-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/data_plane/fibconfig/fibconfig_forwarding_sysctl.hh,v 1.6 2008/10/02 21:56:58 bms Exp $ #ifndef __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_FORWARDING_SYSCTL_HH__ #define __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_FORWARDING_SYSCTL_HH__ #include "fea/fibconfig_forwarding.hh" class FibConfigForwardingSysctl : public FibConfigForwarding { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ FibConfigForwardingSysctl(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~FibConfigForwardingSysctl(); /** * Test whether the IPv4 unicast forwarding engine is enabled or disabled * to forward packets. * * @param ret_value if true on return, then the IPv4 unicast forwarding * is enabled, otherwise is disabled. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int unicast_forwarding_enabled4(bool& ret_value, string& error_msg) const; /** * Test whether the IPv6 unicast forwarding engine is enabled or disabled * to forward packets. * * @param ret_value if true on return, then the IPv6 unicast forwarding * is enabled, otherwise is disabled. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int unicast_forwarding_enabled6(bool& ret_value, string& error_msg) const; /** * Test whether the acceptance of IPv6 Router Advertisement messages is * enabled or disabled. * * @param ret_value if true on return, then the acceptance of IPv6 Router * Advertisement messages is enabled, otherwise is disabled. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int accept_rtadv_enabled6(bool& ret_value, string& error_msg) const; /** * Set the IPv4 unicast forwarding engine to enable or disable forwarding * of packets. * * @param v if true, then enable IPv4 unicast forwarding, otherwise * disable it. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int set_unicast_forwarding_enabled4(bool v, string& error_msg); /** * Set the IPv6 unicast forwarding engine to enable or disable forwarding * of packets. * * @param v if true, then enable IPv6 unicast forwarding, otherwise * disable it. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int set_unicast_forwarding_enabled6(bool v, string& error_msg); /** * Enable or disable the acceptance of IPv6 Router Advertisement messages * from other routers. It should be enabled for hosts, and disabled for * routers. * * @param v if true, then enable the acceptance of IPv6 Router * Advertisement messages, otherwise disable it. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int set_accept_rtadv_enabled6(bool v, string& error_msg); private: }; #endif // __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_FORWARDING_SYSCTL_HH__ xorp/fea/data_plane/fibconfig/fibconfig_entry_observer_routing_socket.hh0000664000076400007640000000521111540225523027145 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_ENTRY_OBSERVER_ROUTING_SOCKET_HH__ #define __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_ENTRY_OBSERVER_ROUTING_SOCKET_HH__ #include #ifdef HAVE_ROUTING_SOCKETS #include "fea/fibconfig_entry_observer.hh" #include "fea/data_plane/control_socket/routing_socket.hh" class FibConfigEntryObserverRoutingSocket : public FibConfigEntryObserver, public RoutingSocket, public RoutingSocketObserver { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ FibConfigEntryObserverRoutingSocket(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~FibConfigEntryObserverRoutingSocket(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg); /** * Receive data from the underlying system. * * @param buffer the buffer with the received data. */ virtual void receive_data(const vector& buffer); void routing_socket_data(const vector& buffer); /** Routing table ID that we are interested in might have changed. */ virtual int notify_table_id_change(uint32_t new_tbl) { // Virtual routing tables not implemented on BSD UNUSED(new_tbl); return XORP_OK; } private: }; #endif #endif // __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_ENTRY_OBSERVER_ROUTING_SOCKET_HH__ xorp/fea/data_plane/fibconfig/fibconfig_entry_get_rtmv2.hh0000664000076400007640000000741511540224221024112 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/data_plane/fibconfig/fibconfig_entry_get_rtmv2.hh,v 1.9 2008/10/02 21:56:56 bms Exp $ #ifndef __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_ENTRY_GET_RTMV2_HH__ #define __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_ENTRY_GET_RTMV2_HH__ #include "fea/fibconfig_entry_get.hh" class FibConfigEntryGetRtmV2 : public FibConfigEntryGet { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (ref FeaDataPlaneManager). */ FibConfigEntryGetRtmV2(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~FibConfigEntryGetRtmV2(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg); /** * Lookup an IPv4 route by destination address. * * @param dst host address to resolve. * @param fte return-by-reference forwarding table entry. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int lookup_route_by_dest4(const IPv4& dst, Fte4& fte); /** * Lookup an IPv4 route by network address. * * @param dst network address to resolve. * @param fte return-by-reference forwarding table entry. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int lookup_route_by_network4(const IPv4Net& dst, Fte4& fte); /** * Lookup an IPv6 route by destination address. * * @param dst host address to resolve. * @param fte return-by-reference forwarding table entry. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int lookup_route_by_dest6(const IPv6& dst, Fte6& fte); /** * Lookup an IPv6 route by network address. * * @param dst network address to resolve. * @param fte return-by-reference forwarding table entry. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int lookup_route_by_network6(const IPv6Net& dst, Fte6& fte); private: /** * Lookup a route by destination address. * * @param dst host address to resolve. * @param fte return-by-reference forwarding table entry. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int lookup_route_by_dest(const IPvX& dst, FteX& fte); /** * Lookup a route by network address. * * @param dst network address to resolve. * @param fte return-by-reference forwarding table entry. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int lookup_route_by_network(const IPvXNet& dst, FteX& fte); }; #endif // __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_ENTRY_GET_RTMV2_HH__ xorp/fea/data_plane/fibconfig/fibconfig_table_set_iphelper.hh0000664000076400007640000000601411540224222024605 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_TABLE_SET_IPHELPER_HH__ #define __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_TABLE_SET_IPHELPER_HH__ #include "fea/fibconfig_table_set.hh" class FibConfigTableSetIPHelper : public FibConfigTableSet { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ FibConfigTableSetIPHelper(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~FibConfigTableSetIPHelper(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg); /** * Set the IPv4 unicast forwarding table. * * @param fte_list the list with all entries to install into * the IPv4 unicast forwarding table. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int set_table4(const list& fte_list); /** * Delete all entries in the IPv4 unicast forwarding table. * * Must be within a configuration interval. * * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int delete_all_entries4(); /** * Set the IPv6 unicast forwarding table. * * @param fte_list the list with all entries to install into * the IPv6 unicast forwarding table. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int set_table6(const list& fte_list); /** * Delete all entries in the IPv6 unicast forwarding table. * * Must be within a configuration interval. * * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int delete_all_entries6(); virtual int notify_table_id_change(uint32_t new_tbl) { UNUSED(new_tbl); return 0; } }; #endif // __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_TABLE_SET_IPHELPER_HH__ xorp/fea/data_plane/fibconfig/fibconfig_table_observer_rtmv2.hh0000664000076400007640000000544611540224222025113 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_TABLE_OBSERVER_RTMV2_HH__ #define __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_TABLE_OBSERVER_RTMV2_HH__ #include "fea/fibconfig_table_observer.hh" #include "fea/data_plane/control_socket/windows_rtm_pipe.hh" class WinRtmPipe; class RtmV2Observer; class FibConfigTableObserverRtmV2 : public FibConfigTableObserver { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ FibConfigTableObserverRtmV2(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~FibConfigTableObserverRtmV2(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg); /** * Receive data from the underlying system. * * @param buffer the buffer with the received data. */ virtual void receive_data(const vector& buffer); virtual int notify_table_id_change(uint32_t new_tbl) { UNUSED(new_tbl); return 0; } private: class RtmV2Observer : public WinRtmPipeObserver { public: RtmV2Observer(WinRtmPipe& rs, int af, FibConfigTableObserverRtmV2& rtmo) : WinRtmPipeObserver(rs), _af(af), _rtmo(rtmo) {} virtual ~RtmV2Observer() {} void routing_socket_data(const vector& buffer) { _rtmo.receive_data(buffer); } private: int _af; FibConfigTableObserverRtmV2& _rtmo; }; WinRtmPipe* _rs4; RtmV2Observer* _rso4; WinRtmPipe* _rs6; RtmV2Observer* _rso6; }; #endif // __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_TABLE_OBSERVER_RTMV2_HH__ xorp/fea/data_plane/fibconfig/fibconfig_entry_set_routing_socket.cc0000664000076400007640000004006211703345405026105 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2012 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include #ifdef HAVE_ROUTING_SOCKETS #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #ifdef HAVE_NET_ROUTE_H #include #endif #ifdef HAVE_NET_IF_DL_H #include #endif #include "fea/fibconfig.hh" #include "fea/data_plane/control_socket/system_utilities.hh" #include "fibconfig_entry_set_routing_socket.hh" // // Set single-entry information into the unicast forwarding table. // // The mechanism to set the information is routing sockets. // FibConfigEntrySetRoutingSocket::FibConfigEntrySetRoutingSocket(FeaDataPlaneManager& fea_data_plane_manager) : FibConfigEntrySet(fea_data_plane_manager), RoutingSocket(fea_data_plane_manager.eventloop()) { } FibConfigEntrySetRoutingSocket::~FibConfigEntrySetRoutingSocket() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the routing sockets mechanism to set " "information about forwarding table from the underlying " "system: %s", error_msg.c_str()); } } int FibConfigEntrySetRoutingSocket::start(string& error_msg) { if (_is_running) return (XORP_OK); if (RoutingSocket::start(error_msg) != XORP_OK) return (XORP_ERROR); _is_running = true; return (XORP_OK); } int FibConfigEntrySetRoutingSocket::stop(string& error_msg) { if (! _is_running) return (XORP_OK); if (RoutingSocket::stop(error_msg) != XORP_OK) return (XORP_ERROR); _is_running = false; return (XORP_OK); } int FibConfigEntrySetRoutingSocket::add_entry4(const Fte4& fte) { FteX ftex(fte); return (add_entry(ftex)); } int FibConfigEntrySetRoutingSocket::delete_entry4(const Fte4& fte) { FteX ftex(fte); return (delete_entry(ftex)); } int FibConfigEntrySetRoutingSocket::add_entry6(const Fte6& fte) { FteX ftex(fte); return (add_entry(ftex)); } int FibConfigEntrySetRoutingSocket::delete_entry6(const Fte6& fte) { FteX ftex(fte); return (delete_entry(ftex)); } // // XXX: Define a number of constants with the size of the corresponding // sockaddr structure rounded-up to the next sizeof(long) boundary. // This is needed, because the routing socket expects the sockaddr // structures to begin on the boundary of (long) words. // #define LONG_ROUNDUP_SIZEOF(type) \ ((sizeof(type) % sizeof(long)) ? \ (sizeof(type) + sizeof(long) - (sizeof(type) % sizeof(long))) \ : (sizeof(type))) static const size_t SOCKADDR_IN_ROUNDUP_LEN = LONG_ROUNDUP_SIZEOF(struct sockaddr_in); #ifdef HAVE_IPV6 static const size_t SOCKADDR_IN6_ROUNDUP_LEN = LONG_ROUNDUP_SIZEOF(struct sockaddr_in6); #endif #ifdef AF_LINK static const size_t SOCKADDR_DL_ROUNDUP_LEN = LONG_ROUNDUP_SIZEOF(struct sockaddr_dl); #endif #undef LONG_ROUNDUP_SIZEOF int FibConfigEntrySetRoutingSocket::add_entry(const FteX& fte) { static const size_t buffer_size = sizeof(struct rt_msghdr) + 512; union { uint8_t data[buffer_size]; struct rt_msghdr rtm; } buffer; struct rt_msghdr* rtm = &buffer.rtm; struct sockaddr_in* sin_dst = NULL; struct sockaddr_in* sin_netmask = NULL; struct sockaddr_in* sin_nexthop = NULL; RoutingSocket& rs = *this; int family = fte.net().af(); #ifdef AF_LINK struct sockaddr_dl* sdl = NULL; size_t sdl_len = 0; #endif size_t sin_dst_len = 0; size_t sin_nexthop_len = 0; size_t sin_netmask_len = 0; bool is_host_route = fte.is_host_route(); bool is_interface_route = false; bool is_nexthop_sockaddr_dl = false; bool is_discard_route = false; bool is_unreachable_route = false; IPvX fte_nexthop = fte.nexthop(); debug_msg("add_entry " "(network = %s nexthop = %s)", fte.net().str().c_str(), fte_nexthop.str().c_str()); // Check that the family is supported do { if (fte_nexthop.is_ipv4()) { if (! fea_data_plane_manager().have_ipv4()) return (XORP_ERROR); break; } if (fte_nexthop.is_ipv6()) { if (! fea_data_plane_manager().have_ipv6()) return (XORP_ERROR); break; } break; } while (false); if (fte.is_connected_route()) return (XORP_OK); // XXX: don't add/remove directly-connected routes if (! fte.ifname().empty()) is_interface_route = true; do { // // Check for a discard or unreachable route. // The referenced ifname must have respectively the discard or // unreachable property. The next-hop is forcibly rewritten to be the // loopback address, in order for the RTF_BLACKHOLE or RTF_REJECT // flag to take effect on BSD platforms. // if (fte.ifname().empty()) break; const IfTree& iftree = fibconfig().merged_config_iftree(); const IfTreeInterface* ifp = iftree.find_interface(fte.ifname()); if (ifp == NULL) { XLOG_ERROR("Invalid interface name: %s", fte.ifname().c_str()); return (XORP_ERROR); } if (ifp->discard()) { is_discard_route = true; fte_nexthop = IPvX::LOOPBACK(family); } if (ifp->unreachable()) { is_unreachable_route = true; fte_nexthop = IPvX::LOOPBACK(family); } break; } while (false); #ifdef AF_LINK // // XXX: If we want to add an interface-specific route, and if // there is no nexthop IP address, then the nexthop (RTA_GATEWAY) // must be "struct sockaddr_dl" with the interface information. // Note: this check must ba after the discard or unreachable route check, // because the discard or unreachable route check may overwrite // fte_nexthop. // if (is_interface_route && (fte_nexthop == IPvX::ZERO(family))) { is_nexthop_sockaddr_dl = true; } #endif // AF_LINK // // Set the request // memset(&buffer, 0, sizeof(buffer)); rtm->rtm_msglen = sizeof(*rtm); switch (family) { case AF_INET: sin_dst = (struct sockaddr_in *)(rtm + 1); sin_dst_len = SOCKADDR_IN_ROUNDUP_LEN; rtm->rtm_msglen += sin_dst_len; if (is_nexthop_sockaddr_dl) { #ifdef AF_LINK sdl = ADD_POINTER(sin_dst, sin_dst_len, struct sockaddr_dl *); sdl_len = SOCKADDR_DL_ROUNDUP_LEN; sin_netmask = ADD_POINTER(sdl, sdl_len, struct sockaddr_in *); sin_netmask_len = SOCKADDR_IN_ROUNDUP_LEN; rtm->rtm_msglen += sdl_len + sin_netmask_len; #endif // AF_LINK } else { sin_nexthop = ADD_POINTER(sin_dst, sin_dst_len, struct sockaddr_in *); sin_nexthop_len = SOCKADDR_IN_ROUNDUP_LEN; sin_netmask = ADD_POINTER(sin_nexthop, sin_nexthop_len, struct sockaddr_in *); sin_netmask_len = SOCKADDR_IN_ROUNDUP_LEN; rtm->rtm_msglen += sin_nexthop_len + sin_netmask_len; } break; #ifdef HAVE_IPV6 case AF_INET6: sin_dst = (struct sockaddr_in *)(rtm + 1); sin_dst_len = SOCKADDR_IN6_ROUNDUP_LEN; rtm->rtm_msglen += sin_dst_len; if (is_nexthop_sockaddr_dl) { #ifdef AF_LINK sdl = ADD_POINTER(sin_dst, sin_dst_len, struct sockaddr_dl *); sdl_len = SOCKADDR_DL_ROUNDUP_LEN; sin_netmask = ADD_POINTER(sdl, sdl_len, struct sockaddr_in *); sin_netmask_len = SOCKADDR_IN6_ROUNDUP_LEN; rtm->rtm_msglen += sdl_len + sin_netmask_len; #endif // AF_LINK } else { sin_nexthop = ADD_POINTER(sin_dst, sin_dst_len, struct sockaddr_in *); sin_nexthop_len = SOCKADDR_IN6_ROUNDUP_LEN; sin_netmask = ADD_POINTER(sin_nexthop, sin_nexthop_len, struct sockaddr_in *); sin_netmask_len = SOCKADDR_IN6_ROUNDUP_LEN; rtm->rtm_msglen += sin_nexthop_len + sin_netmask_len; } break; #endif // HAVE_IPV6 default: XLOG_UNREACHABLE(); break; } rtm->rtm_version = RTM_VERSION; rtm->rtm_type = RTM_ADD; rtm->rtm_index = 0; // XXX: not used by the kernel (?) rtm->rtm_addrs = (RTA_DST | RTA_GATEWAY | RTA_NETMASK); if (is_host_route) rtm->rtm_flags |= RTF_HOST; if (is_discard_route) rtm->rtm_flags |= RTF_BLACKHOLE; if (is_unreachable_route) rtm->rtm_flags |= RTF_REJECT; if ((fte_nexthop != IPvX::ZERO(family)) && (! is_nexthop_sockaddr_dl)) rtm->rtm_flags |= RTF_GATEWAY; rtm->rtm_flags |= RTF_PROTO1; // XXX: mark this as a XORP route rtm->rtm_flags |= RTF_UP; // XXX: mark this route as UP rtm->rtm_pid = rs.pid(); rtm->rtm_seq = rs.seqno(); // Copy the destination, the nexthop, and the netmask addresses if (family == AF_INET) { fte.net().masked_addr().copy_out(*sin_dst); if (sin_nexthop != NULL) fte_nexthop.copy_out(*sin_nexthop); fte.net().netmask().copy_out(*sin_netmask); } #ifdef HAVE_IPV6 else { // Keep the stupid ipvx.cc code from throwing an exception. fte.net().masked_addr().copy_out(*((struct sockaddr_in6*)(sin_dst))); if (sin_nexthop != NULL) fte_nexthop.copy_out(*((struct sockaddr_in6*)(sin_nexthop))); fte.net().netmask().copy_out(*((struct sockaddr_in6*)(sin_netmask))); } #endif if (is_interface_route) { // // This is an interface-specific route. // Set the interface-related information. // uint32_t pif_index = 0; // Get the physical interface index do { const IfTree& iftree = fibconfig().merged_config_iftree(); const IfTreeVif* vifp = iftree.find_vif(fte.ifname(), fte.vifname()); if (vifp == NULL) { XLOG_ERROR("Invalid interface name %s vif name %s", fte.ifname().c_str(), fte.vifname().c_str()); return (XORP_ERROR); } pif_index = vifp->pif_index(); } while (false); // Adjust the nexthop address (if necessary) if (sin_nexthop != NULL) { switch (family) { case AF_INET: break; #ifdef HAVE_IPV6 case AF_INET6: { struct sockaddr_in6* sin6_nexthop; sin6_nexthop = reinterpret_cast(sin_nexthop); system_adjust_sockaddr_in6_route(*sin6_nexthop, pif_index); } break; #endif // HAVE_IPV6 default: XLOG_UNREACHABLE(); break; } } #ifdef AF_LINK if (sdl == NULL) { // // Add extra space for data-link sockaddr_dl for the RTA_IFP flag // sdl = ADD_POINTER(sin_netmask, sin_netmask_len, struct sockaddr_dl *); sdl_len = SOCKADDR_DL_ROUNDUP_LEN; rtm->rtm_msglen += sdl_len; rtm->rtm_addrs |= RTA_IFP; } sdl->sdl_family = AF_LINK; #ifdef HAVE_STRUCT_SOCKADDR_DL_SDL_LEN sdl->sdl_len = sizeof(*sdl); #endif sdl->sdl_index = pif_index; strncpy(sdl->sdl_data, fte.vifname().c_str(), sizeof(sdl->sdl_data)); if (fte.vifname().size() < sizeof(sdl->sdl_data)) { sdl->sdl_nlen = fte.vifname().size(); sdl->sdl_data[sizeof(sdl->sdl_data) - 1] = '\0'; } else { sdl->sdl_nlen = sizeof(sdl->sdl_data); } #endif // AF_LINK } errno = 0; if (rs.write(rtm, rtm->rtm_msglen) != rtm->rtm_msglen) { XLOG_ERROR("Error writing to routing socket: %s", strerror(errno)); // if error is EEXIST, then don't return error..we don't want to fail // the entire commit needlessly. if (errno == EEXIST) { return XORP_OK; } return XORP_ERROR; } // // TODO: here we should check the routing socket output whether the write // succeeded. // return (XORP_OK); } int FibConfigEntrySetRoutingSocket::delete_entry(const FteX& fte) { static const size_t buffer_size = sizeof(struct rt_msghdr) + 512; union { uint8_t data[buffer_size]; struct rt_msghdr rtm; } buffer; struct rt_msghdr* rtm = &buffer.rtm; struct sockaddr_in* sin_dst = NULL; struct sockaddr_in* sin_netmask = NULL; RoutingSocket& rs = *this; int family = fte.net().af(); size_t sin_dst_len = 0; size_t sin_netmask_len = 0; bool is_host_route = fte.is_host_route(); debug_msg("delete_entry " "(network = %s nexthop = %s)", fte.net().str().c_str(), fte.nexthop().str().c_str()); // Check that the family is supported do { if (fte.nexthop().is_ipv4()) { if (! fea_data_plane_manager().have_ipv4()) return (XORP_ERROR); break; } if (fte.nexthop().is_ipv6()) { if (! fea_data_plane_manager().have_ipv6()) return (XORP_ERROR); break; } break; } while (false); if (fte.is_connected_route()) return (XORP_OK); // XXX: don't add/remove directly-connected routes // // Set the request // memset(&buffer, 0, sizeof(buffer)); rtm->rtm_msglen = sizeof(*rtm); switch (family) { case AF_INET: sin_dst = (struct sockaddr_in *)(rtm + 1); sin_dst_len = SOCKADDR_IN_ROUNDUP_LEN; rtm->rtm_msglen += sin_dst_len; if (! is_host_route) { sin_netmask = ADD_POINTER(sin_dst, sin_dst_len, struct sockaddr_in *); sin_netmask_len = SOCKADDR_IN_ROUNDUP_LEN; rtm->rtm_msglen += sin_netmask_len; } break; #ifdef HAVE_IPV6 case AF_INET6: sin_dst = (struct sockaddr_in *)(rtm + 1); sin_dst_len = SOCKADDR_IN6_ROUNDUP_LEN; rtm->rtm_msglen += sin_dst_len; if (! is_host_route) { sin_netmask = ADD_POINTER(sin_dst, sin_dst_len, struct sockaddr_in *); sin_netmask_len = SOCKADDR_IN6_ROUNDUP_LEN; rtm->rtm_msglen += sin_netmask_len; } break; #endif // HAVE_IPV6 default: XLOG_UNREACHABLE(); break; } rtm->rtm_version = RTM_VERSION; rtm->rtm_type = RTM_DELETE; rtm->rtm_index = 0; // XXX: not used by the kernel (?) rtm->rtm_addrs = RTA_DST; if (! is_host_route) rtm->rtm_addrs |= RTA_NETMASK; rtm->rtm_flags = 0; rtm->rtm_pid = rs.pid(); rtm->rtm_seq = rs.seqno(); // Copy the destination, and the netmask addresses (if needed) if (family == AF_INET) { fte.net().masked_addr().copy_out(*sin_dst); if (! is_host_route) fte.net().netmask().copy_out(*sin_netmask); } #ifdef HAVE_IPV6 else { fte.net().masked_addr().copy_out(*((struct sockaddr_in6*)(sin_dst))); if (! is_host_route) fte.net().netmask().copy_out(*((struct sockaddr_in6*)(sin_netmask))); } #endif errno = 0; if (rs.write(rtm, rtm->rtm_msglen) != rtm->rtm_msglen) { // // XXX: If the outgoing interface was taken down earlier, then // most likely the kernel has removed the matching forwarding // entries on its own. Hence, check whether all of the following // is true: // - the error code matches // - the outgoing interface is down // // If all conditions are true, then ignore the error and consider // the deletion was success. // Note that we could add to the following list the check whether // the forwarding entry is not in the kernel, but this is probably // an overkill. If such check should be performed, we should // use the corresponding FibConfigTableGetNetlink plugin. // // NOTE: This seems too harsh to me. If we get ESRCH, then return // OK, regardless of other things. --Ben // Check whether the error code matches if (errno == ESRCH) { // // XXX: The "No such process" error code is used by the // kernel to indicate there is no such forwarding entry // to delete. // return XORP_OK; // Check whether the interface is down //if (fte.ifname().empty()) // break; // No interface to check //const IfTree& iftree = fibconfig().system_config_iftree(); //const IfTreeVif* vifp = iftree.find_vif(fte.ifname(), // fte.vifname()); //if ((vifp != NULL) && vifp->enabled()) // break; // The interface is UP } XLOG_ERROR("Error writing to routing socket, trying to delete route: %s, error:: %s(%i)", fte.str().c_str(), strerror(errno), errno); return (XORP_ERROR); } // // TODO: here we should check the routing socket output whether the write // succeeded. // return (XORP_OK); } #endif // HAVE_ROUTING_SOCKETS xorp/fea/data_plane/fibconfig/fibconfig_entry_observer_netlink_socket.cc0000664000076400007640000000674711540225523027127 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include #ifdef HAVE_NETLINK_SOCKETS #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #ifdef HAVE_LINUX_TYPES_H #include #endif #ifdef HAVE_LINUX_RTNETLINK_H #include #endif #include "fea/fibconfig.hh" #include "fibconfig_entry_observer_netlink_socket.hh" // // Observe single-entry information change about the unicast forwarding table. // // E.g., if the forwarding table has changed, then the information // received by the observer would specify the particular entry that // has changed. // // The mechanism to observe the information is netlink(7) sockets. // FibConfigEntryObserverNetlinkSocket::FibConfigEntryObserverNetlinkSocket(FeaDataPlaneManager& fea_data_plane_manager) : FibConfigEntryObserver(fea_data_plane_manager), NetlinkSocket(fea_data_plane_manager.eventloop(), fea_data_plane_manager.fibconfig().get_netlink_filter_table_id()), NetlinkSocketObserver(*(NetlinkSocket *)this) { } FibConfigEntryObserverNetlinkSocket::~FibConfigEntryObserverNetlinkSocket() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the netlink(7) sockets mechanism to observe " "information about forwarding table from the underlying " "system: %s", error_msg.c_str()); } } int FibConfigEntryObserverNetlinkSocket::start(string& error_msg) { uint32_t nl_groups = 0; if (_is_running) return (XORP_OK); // // Listen to the netlink multicast group for IPv4 forwarding entries // if (fea_data_plane_manager().have_ipv4()) nl_groups |= RTMGRP_IPV4_ROUTE; #ifdef HAVE_IPV6 // // Listen to the netlink multicast group for IPv6 forwarding entries // if (fea_data_plane_manager().have_ipv6()) nl_groups |= RTMGRP_IPV6_ROUTE; #endif // HAVE_IPV6 // // Set the netlink multicast groups to listen for on the netlink socket // NetlinkSocket::set_nl_groups(nl_groups); if (NetlinkSocket::start(error_msg) != XORP_OK) return (XORP_ERROR); _is_running = true; return (XORP_OK); } int FibConfigEntryObserverNetlinkSocket::stop(string& error_msg) { if (! _is_running) return (XORP_OK); if (NetlinkSocket::stop(error_msg) != XORP_OK) return (XORP_ERROR); _is_running = false; return (XORP_OK); } void FibConfigEntryObserverNetlinkSocket::receive_data(const vector& buffer) { // TODO: XXX: PAVPAVPAV: use it? UNUSED(buffer); } void FibConfigEntryObserverNetlinkSocket::netlink_socket_data(const vector& buffer) { receive_data(buffer); } #endif // HAVE_NETLINK_SOCKETS xorp/fea/data_plane/fibconfig/fibconfig_entry_set_click.cc0000664000076400007640000003466011540225523024137 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include #ifdef XORP_USE_CLICK #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "fea/fibconfig.hh" #include "fibconfig_entry_set_click.hh" // // Set single-entry information into the unicast forwarding table. // // The mechanism to set the information is Click: // http://www.read.cs.ucla.edu/click/ // FibConfigEntrySetClick::FibConfigEntrySetClick(FeaDataPlaneManager& fea_data_plane_manager) : FibConfigEntrySet(fea_data_plane_manager), ClickSocket(fea_data_plane_manager.eventloop()), _cs_reader(*(ClickSocket *)this), _reinstall_all_entries_time_slice(100000, 20), // 100ms, test every 20th iter _start_reinstalling_fte_table4(false), _is_reinstalling_fte_table4(false), _start_reinstalling_fte_table6(false), _is_reinstalling_fte_table6(false), _reinstalling_ipv4net(IPv4::ZERO(), 0), _reinstalling_ipv6net(IPv6::ZERO(), 0) { } FibConfigEntrySetClick::~FibConfigEntrySetClick() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the Click mechanism to set " "information about forwarding table from the underlying " "system: %s", error_msg.c_str()); } } int FibConfigEntrySetClick::start(string& error_msg) { if (! ClickSocket::is_enabled()) return (XORP_OK); if (_is_running) return (XORP_OK); if (ClickSocket::start(error_msg) != XORP_OK) return (XORP_ERROR); // XXX: add myself as an observer to the NexthopPortMapper fibconfig().nexthop_port_mapper().add_observer(this); _is_running = true; return (XORP_OK); } int FibConfigEntrySetClick::stop(string& error_msg) { int ret_value = XORP_OK; if (! _is_running) return (XORP_OK); // XXX: delete myself as an observer to the NexthopPortMapper fibconfig().nexthop_port_mapper().delete_observer(this); ret_value = ClickSocket::stop(error_msg); _is_running = false; return (ret_value); } int FibConfigEntrySetClick::add_entry4(const Fte4& fte) { int ret_value; if (fte.is_connected_route()) { // XXX: accept directly-connected routes } FteX ftex(fte); ret_value = add_entry(ftex); // Add the entry to the local copy of the forwarding table if (ret_value == XORP_OK) { map::iterator iter; // Erase the entry with the same key (if exists) iter = _fte_table4.find(fte.net()); if (iter != _fte_table4.end()) _fte_table4.erase(iter); _fte_table4.insert(make_pair(fte.net(), fte)); } return (ret_value); } int FibConfigEntrySetClick::delete_entry4(const Fte4& fte) { int ret_value; if (fte.is_connected_route()) { // XXX: accept directly-connected routes } FteX ftex(fte); ret_value = delete_entry(ftex); // Delete the entry from the local copy of the forwarding table if (ret_value == XORP_OK) { map::iterator iter; // Erase the entry with the same key (if exists) iter = _fte_table4.find(fte.net()); if (iter != _fte_table4.end()) _fte_table4.erase(iter); } return (ret_value); } int FibConfigEntrySetClick::add_entry6(const Fte6& fte) { int ret_value; if (fte.is_connected_route()) { // XXX: accept directly-connected routes } FteX ftex(fte); ret_value = add_entry(ftex); // Add the entry to the local copy of the forwarding table if (ret_value == XORP_OK) { map::iterator iter; // Erase the entry with the same key (if exists) iter = _fte_table6.find(fte.net()); if (iter != _fte_table6.end()) _fte_table6.erase(iter); _fte_table6.insert(make_pair(fte.net(), fte)); } return (ret_value); } int FibConfigEntrySetClick::delete_entry6(const Fte6& fte) { int ret_value; if (fte.is_connected_route()) { // XXX: accept directly-connected routes } FteX ftex(fte); ret_value = delete_entry(ftex); // Delete the entry from the local copy of the forwarding table if (ret_value == XORP_OK) { map::iterator iter; // Erase the entry with the same key (if exists) iter = _fte_table6.find(fte.net()); if (iter != _fte_table6.end()) _fte_table6.erase(iter); } return (ret_value); } int FibConfigEntrySetClick::add_entry(const FteX& fte) { int port = -1; string element; string handler = "add"; string error_msg; debug_msg("add_entry " "(network = %s nexthop = %s)", fte.net().str().c_str(), fte.nexthop().str().c_str()); // Check that the family is supported do { if (fte.nexthop().is_ipv4()) { if (! fea_data_plane_manager().have_ipv4()) return (XORP_ERROR); element = "_xorp_rt4"; break; } if (fte.nexthop().is_ipv6()) { if (! fea_data_plane_manager().have_ipv6()) return (XORP_ERROR); element = "_xorp_rt6"; break; } XLOG_UNREACHABLE(); break; } while (false); // // Get the outgoing port number // do { NexthopPortMapper& m = fibconfig().nexthop_port_mapper(); const IPvX& nexthop = fte.nexthop(); bool lookup_nexthop_interface_first = true; if (fte.is_connected_route() && (IPvXNet(nexthop, nexthop.addr_bitlen()) == fte.net())) { // // XXX: if a directly-connected route, then check whether the // next-hop address is same as the destination address. This // could be the case of either: // (a) the other side of a p2p link // OR // (b) a connected route for a /32 or /128 configured interface // We are interested in discovering case (b) only, but anyway // in both cases we then lookup the MextPortMapper by trying // first the nexthop address. // // Note that we don't want to map all "connected" routes // by the next-hop address, because typically the next-hop // address will be local IP address, and we don't want to // mis-route the traffic for all directly connected destinations // to the local delivery port of the Click lookup element. // lookup_nexthop_interface_first = false; } if (lookup_nexthop_interface_first) { port = m.lookup_nexthop_interface(fte.ifname(), fte.vifname()); if (port >= 0) break; } if (nexthop.is_ipv4()) { port = m.lookup_nexthop_ipv4(nexthop.get_ipv4()); if (port >= 0) break; } if (nexthop.is_ipv6()) { port = m.lookup_nexthop_ipv6(nexthop.get_ipv6()); if (port >= 0) break; } if (! lookup_nexthop_interface_first) { port = m.lookup_nexthop_interface(fte.ifname(), fte.vifname()); if (port >= 0) break; } break; } while (false); if (port < 0) { XLOG_ERROR("Cannot find outgoing port number for the Click forwarding " "table element to add entry %s", fte.str().c_str()); return (XORP_ERROR); } // // Write the configuration // string config; if (fte.is_connected_route()) { config = c_format("%s %d\n", fte.net().str().c_str(), port); } else { config = c_format("%s %s %d\n", fte.net().str().c_str(), fte.nexthop().str().c_str(), port); } // // XXX: here we always write the same config to both kernel and // user-level Click. // bool has_kernel_config = true; bool has_user_config = true; if (ClickSocket::write_config(element, handler, has_kernel_config, config, has_user_config, config, error_msg) != XORP_OK) { XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } return (XORP_OK); } int FibConfigEntrySetClick::delete_entry(const FteX& fte) { int port = -1; string element; string handler = "remove"; string error_msg; debug_msg("delete_entry " "(network = %s nexthop = %s)", fte.net().str().c_str(), fte.nexthop().str().c_str()); // Check that the family is supported do { if (fte.nexthop().is_ipv4()) { if (! fea_data_plane_manager().have_ipv4()) return (XORP_ERROR); element = "_xorp_rt4"; break; } if (fte.nexthop().is_ipv6()) { if (! fea_data_plane_manager().have_ipv6()) return (XORP_ERROR); element = "_xorp_rt6"; break; } XLOG_UNREACHABLE(); break; } while (false); // // Get the outgoing port number // do { NexthopPortMapper& m = fibconfig().nexthop_port_mapper(); port = m.lookup_nexthop_interface(fte.ifname(), fte.vifname()); if (port >= 0) break; if (fte.nexthop().is_ipv4()) { port = m.lookup_nexthop_ipv4(fte.nexthop().get_ipv4()); if (port >= 0) break; } if (fte.nexthop().is_ipv6()) { port = m.lookup_nexthop_ipv6(fte.nexthop().get_ipv6()); if (port >= 0) break; } break; } while (false); #if 0 // TODO: XXX: currently, we don't have the next-hop info, hence // we allow to delete an entry without the port number. if (port < 0) { XLOG_ERROR("Cannot find outgoing port number for the Click forwarding " "table element to delete entry %s", fte.str().c_str()); return (XORP_ERROR); } #endif // 0 // // Write the configuration // string config; if (port < 0) { // XXX: remove all routes for a given prefix // // TODO: XXX: currently, we don't have the next-hop info, // hence we delete all routes for a given prefix. After all, // in case of XORP+Click we always install no more than // a single route per prefix. // config = c_format("%s\n", fte.net().str().c_str()); } else { // XXX: remove a specific route if (fte.is_connected_route()) { config = c_format("%s %d\n", fte.net().str().c_str(), port); } else { config = c_format("%s %s %d\n", fte.net().str().c_str(), fte.nexthop().str().c_str(), port); } } // // XXX: here we always write the same config to both kernel and // user-level Click. // bool has_kernel_config = true; bool has_user_config = true; if (ClickSocket::write_config(element, handler, has_kernel_config, config, has_user_config, config, error_msg) != XORP_OK) { XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } return (XORP_OK); } void FibConfigEntrySetClick::nexthop_port_mapper_event(bool is_mapping_changed) { UNUSED(is_mapping_changed); // // XXX: always reinstall all entries, because they were lost // when the new Click config was written. // start_task_reinstall_all_entries(); } void FibConfigEntrySetClick::start_task_reinstall_all_entries() { // Initialize the reinstalling from the beginning _start_reinstalling_fte_table4 = true; _is_reinstalling_fte_table4 = false; _start_reinstalling_fte_table6 = true; _is_reinstalling_fte_table6 = false; run_task_reinstall_all_entries(); } void FibConfigEntrySetClick::run_task_reinstall_all_entries() { _reinstall_all_entries_time_slice.reset(); // // Reinstall all IPv4 entries. If the time slice expires, then schedule // a timer to continue later. // if (_start_reinstalling_fte_table4 || _is_reinstalling_fte_table4) { if (reinstall_all_entries4() == true) { _reinstall_all_entries_timer = fibconfig().eventloop().new_oneoff_after( TimeVal(0, 1), callback(this, &FibConfigEntrySetClick::run_task_reinstall_all_entries)); return; } } // // Reinstall all IPv6 entries. If the time slice expires, then schedule // a timer to continue later. // if (_start_reinstalling_fte_table6 || _is_reinstalling_fte_table6) { if (reinstall_all_entries6() == true) { _reinstall_all_entries_timer = fibconfig().eventloop().new_oneoff_after( TimeVal(0, 1), callback(this, &FibConfigEntrySetClick::run_task_reinstall_all_entries)); return; } } return; } bool FibConfigEntrySetClick::reinstall_all_entries4() { map::const_iterator iter4, iter4_begin, iter4_end; // Set the begin and end iterators if (_start_reinstalling_fte_table4) { iter4_begin = _fte_table4.begin(); } else if (_is_reinstalling_fte_table4) { iter4_begin = _fte_table4.lower_bound(_reinstalling_ipv4net); } else { return (false); // XXX: nothing to do } iter4_end = _fte_table4.end(); _start_reinstalling_fte_table4 = false; _is_reinstalling_fte_table4 = true; for (iter4 = iter4_begin; iter4 != iter4_end; ) { const FteX& ftex = FteX(iter4->second); ++iter4; add_entry(ftex); if (_reinstall_all_entries_time_slice.is_expired()) { // Time slice has expired. Save state if necessary and return. if (iter4 != iter4_end) { const Fte4& fte4_next = iter4->second; _reinstalling_ipv4net = fte4_next.net(); } else { _is_reinstalling_fte_table4 = false; } return (true); } } _is_reinstalling_fte_table4 = false; return (false); } bool FibConfigEntrySetClick::reinstall_all_entries6() { map::const_iterator iter6, iter6_begin, iter6_end; // Set the begin and end iterators if (_start_reinstalling_fte_table6) { iter6_begin = _fte_table6.begin(); } else if (_is_reinstalling_fte_table6) { iter6_begin = _fte_table6.lower_bound(_reinstalling_ipv6net); } else { return (false); // XXX: nothing to do } iter6_end = _fte_table6.end(); _start_reinstalling_fte_table6 = false; _is_reinstalling_fte_table6 = true; for (iter6 = iter6_begin; iter6 != iter6_end; ) { const FteX& ftex = FteX(iter6->second); ++iter6; add_entry(ftex); if (_reinstall_all_entries_time_slice.is_expired()) { // Time slice has expired. Save state if necessary and return. if (iter6 != iter6_end) { const Fte6& fte6_next = iter6->second; _reinstalling_ipv6net = fte6_next.net(); } else { _is_reinstalling_fte_table6 = false; } return (true); } } _is_reinstalling_fte_table6 = false; return (false); } #endif // click xorp/fea/data_plane/fibconfig/fibconfig_table_parse_routing_socket.cc0000664000076400007640000001043411540225523026347 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include #if defined(HAVE_ROUTING_SOCKETS) || defined(HOST_OS_WINDOWS) #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #ifdef HAVE_NET_ROUTE_H #include #endif #ifdef HOST_OS_WINDOWS #include "fea/data_plane/control_socket/windows_routing_socket.h" #endif #include "fea/fibconfig.hh" #include "fea/data_plane/control_socket/routing_socket_utilities.hh" #include "fibconfig_table_get_sysctl.hh" // // Parse information about routing table information received from // the underlying system. // // The information to parse is in RTM format // (e.g., obtained by routing sockets or by sysctl(3) mechanism). // // Reading route(4) manual page is a good start for understanding this // // // XXX: The FibConfigTableGetSysctl::parse_buffer_routing_socket() // static method is used by the FibConfigTableObserverRtmV2 // Windows implementation as well even though Windows doesn't have // routing sockets. // int FibConfigTableGetSysctl::parse_buffer_routing_socket(int family, const IfTree& iftree, list& fte_list, const vector& buffer, FibMsgSet filter) { AlignData align_data(buffer); const struct rt_msghdr* rtm; size_t offset; rtm = align_data.payload(); for (offset = 0; offset < buffer.size(); offset += rtm->rtm_msglen) { bool filter_match = false; rtm = align_data.payload_by_offset(offset); if (rtm->rtm_version != RTM_VERSION) { #if defined(RTM_OVERSION) && defined(HOST_OS_OPENBSD) // // XXX: Silently ignore old messages. // The OpenBSD kernel sends each message twice, once as // RTM_VERSION and once as RTM_OVERSION, hence we need to ignore // the RTM_OVERSION duplicates. // if (rtm->rtm_version == RTM_OVERSION) continue; #endif // RTM_OVERSION && HOST_OS_OPENBSD XLOG_ERROR("RTM version mismatch: expected %d got %d", RTM_VERSION, rtm->rtm_version); continue; } // XXX: ignore entries with an error if (rtm->rtm_errno != 0) continue; if (filter & FibMsg::GETS) { #ifdef RTM_GET if ((rtm->rtm_type == RTM_GET) && (rtm->rtm_flags & RTF_UP)) filter_match = true; #endif } // Upcalls may not be supported in some BSD derived implementations. if (filter & FibMsg::RESOLVES) { #ifdef RTM_MISS if (rtm->rtm_type == RTM_MISS) filter_match = true; #endif #ifdef RTM_RESOLVE if (rtm->rtm_type == RTM_RESOLVE) filter_match = true; #endif } if (filter & FibMsg::UPDATES) { if ((rtm->rtm_type == RTM_ADD) || (rtm->rtm_type == RTM_DELETE) || (rtm->rtm_type == RTM_CHANGE)) filter_match = true; } if (!filter_match) continue; #ifdef RTF_LLINFO if (rtm->rtm_flags & RTF_LLINFO) continue; // Ignore ARP table entries. #endif #ifdef RTF_WASCLONED if (rtm->rtm_flags & RTF_WASCLONED) continue; // XXX: ignore cloned entries #endif #ifdef RTF_CLONED if (rtm->rtm_flags & RTF_CLONED) continue; // XXX: ignore cloned entries #endif #ifdef RTF_MULTICAST if (rtm->rtm_flags & RTF_MULTICAST) continue; // XXX: ignore multicast entries #endif #ifdef RTF_BROADCAST if (rtm->rtm_flags & RTF_BROADCAST) continue; // XXX: ignore broadcast entries #endif FteX fte(family); if (RtmUtils::rtm_get_to_fte_cfg(iftree, fte, rtm) == XORP_OK) { fte_list.push_back(fte); } } return (XORP_OK); } #endif // HAVE_ROUTING_SOCKETS || HOST_OS_WINDOWS xorp/fea/data_plane/fibconfig/fibconfig_entry_get_netlink_socket.cc0000664000076400007640000002535611540225522026053 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include #ifdef HAVE_NETLINK_SOCKETS #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvxnet.hh" #ifdef HAVE_LINUX_TYPES_H #include #endif #ifdef HAVE_LINUX_RTNETLINK_H #include #endif #include "fea/fibconfig.hh" #include "fea/data_plane/control_socket/netlink_socket_utilities.hh" #include "fibconfig_entry_get_netlink_socket.hh" // // Get single-entry information from the unicast forwarding table. // // The mechanism to obtain the information is netlink(7) sockets. // FibConfigEntryGetNetlinkSocket::FibConfigEntryGetNetlinkSocket(FeaDataPlaneManager& fea_data_plane_manager) : FibConfigEntryGet(fea_data_plane_manager), NetlinkSocket(fea_data_plane_manager.eventloop(), fea_data_plane_manager.fibconfig().get_netlink_filter_table_id()), _ns_reader(*(NetlinkSocket *)this) { } FibConfigEntryGetNetlinkSocket::~FibConfigEntryGetNetlinkSocket() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the netlink(7) sockets mechanism to get " "information about forwarding table from the underlying " "system: %s", error_msg.c_str()); } } int FibConfigEntryGetNetlinkSocket::start(string& error_msg) { if (_is_running) return (XORP_OK); if (NetlinkSocket::start(error_msg) != XORP_OK) return (XORP_ERROR); _is_running = true; return (XORP_OK); } int FibConfigEntryGetNetlinkSocket::stop(string& error_msg) { if (! _is_running) return (XORP_OK); if (NetlinkSocket::stop(error_msg) != XORP_OK) return (XORP_ERROR); _is_running = false; return (XORP_OK); } int FibConfigEntryGetNetlinkSocket::lookup_route_by_dest4(const IPv4& dst, Fte4& fte) { FteX ftex(dst.af()); int ret_value = XORP_ERROR; ret_value = lookup_route_by_dest(IPvX(dst), ftex); fte = ftex.get_fte4(); return (ret_value); } int FibConfigEntryGetNetlinkSocket::lookup_route_by_network4(const IPv4Net& dst, Fte4& fte) { list fte_list4; if (fibconfig().get_table4(fte_list4) != XORP_OK) return (XORP_ERROR); list::iterator iter4; for (iter4 = fte_list4.begin(); iter4 != fte_list4.end(); ++iter4) { Fte4& fte4 = *iter4; if (fte4.net() == dst) { fte = fte4; return (XORP_OK); } } return (XORP_ERROR); } int FibConfigEntryGetNetlinkSocket::lookup_route_by_dest6(const IPv6& dst, Fte6& fte) { FteX ftex(dst.af()); int ret_value = XORP_ERROR; ret_value = lookup_route_by_dest(IPvX(dst), ftex); fte = ftex.get_fte6(); return (ret_value); } int FibConfigEntryGetNetlinkSocket::lookup_route_by_network6(const IPv6Net& dst, Fte6& fte) { list fte_list6; if (fibconfig().get_table6(fte_list6) != XORP_OK) return (XORP_ERROR); list::iterator iter6; for (iter6 = fte_list6.begin(); iter6 != fte_list6.end(); ++iter6) { Fte6& fte6 = *iter6; if (fte6.net() == dst) { fte = fte6; return (XORP_OK); } } return (XORP_ERROR); } int FibConfigEntryGetNetlinkSocket::lookup_route_by_dest(const IPvX& dst, FteX& fte) { static const size_t buffer_size = sizeof(struct nlmsghdr) + sizeof(struct rtmsg) + sizeof(struct rtattr) + 512; union { uint8_t data[buffer_size]; struct nlmsghdr nlh; } buffer; struct nlmsghdr* nlh = &buffer.nlh; struct sockaddr_nl snl; struct rtmsg* rtmsg; struct rtattr* rtattr; int rta_len; uint8_t* data; NetlinkSocket& ns = *this; int family = dst.af(); void* rta_align_data; uint32_t table_id = RT_TABLE_UNSPEC; // Default value for lookup UNUSED(data); UNUSED(rta_align_data); // Zero the return information fte.zero(); // Check that the family is supported do { if (dst.is_ipv4()) { if (! fea_data_plane_manager().have_ipv4()) return (XORP_ERROR); break; } if (dst.is_ipv6()) { if (! fea_data_plane_manager().have_ipv6()) return (XORP_ERROR); break; } break; } while (false); // Check that the destination address is valid if (! dst.is_unicast()) { return (XORP_ERROR); } // // Set the request. First the socket, then the request itself. // // Set the socket memset(&snl, 0, sizeof(snl)); snl.nl_family = AF_NETLINK; snl.nl_pid = 0; // nl_pid = 0 if destination is the kernel snl.nl_groups = 0; // Set the request memset(&buffer, 0, sizeof(buffer)); nlh->nlmsg_len = NLMSG_LENGTH(sizeof(*rtmsg)); nlh->nlmsg_type = RTM_GETROUTE; nlh->nlmsg_flags = NLM_F_REQUEST; nlh->nlmsg_seq = ns.seqno(); nlh->nlmsg_pid = ns.nl_pid(); rtmsg = reinterpret_cast(NLMSG_DATA(nlh)); rtmsg->rtm_family = family; rtmsg->rtm_dst_len = IPvX::addr_bitlen(family); // Add the 'ipaddr' address as an attribute rta_len = RTA_LENGTH(IPvX::addr_bytelen(family)); if (NLMSG_ALIGN(nlh->nlmsg_len) + rta_len > sizeof(buffer)) { XLOG_FATAL("AF_NETLINK buffer size error: %u instead of %u", XORP_UINT_CAST(sizeof(buffer)), XORP_UINT_CAST(NLMSG_ALIGN(nlh->nlmsg_len) + rta_len)); } rtattr = RTM_RTA(rtmsg); rtattr->rta_type = RTA_DST; rtattr->rta_len = rta_len; dst.copy_out(reinterpret_cast(RTA_DATA(rtattr))); nlh->nlmsg_len = NLMSG_ALIGN(nlh->nlmsg_len) + rta_len; rtmsg->rtm_tos = 0; // XXX: what is this TOS? rtmsg->rtm_protocol = RTPROT_UNSPEC; rtmsg->rtm_scope = RT_SCOPE_UNIVERSE; rtmsg->rtm_type = RTN_UNSPEC; rtmsg->rtm_flags = 0; // // Set the routing/forwarding table ID. // If the table ID is <= 0xff, then we set it in rtmsg->rtm_table, // otherwise we set rtmsg->rtm_table to RT_TABLE_UNSPEC and add the // real value as an RTA_TABLE attribute. // if (fibconfig().unicast_forwarding_table_id_is_configured(family)) table_id = fibconfig().unicast_forwarding_table_id(family); if (table_id <= 0xff) rtmsg->rtm_table = table_id; else rtmsg->rtm_table = RT_TABLE_UNSPEC; #ifdef HAVE_NETLINK_SOCKET_ATTRIBUTE_RTA_TABLE // Add the table ID as an attribute if (table_id > 0xff) { uint32_t uint32_table_id = table_id; rta_len = RTA_LENGTH(sizeof(uint32_table_id)); if (NLMSG_ALIGN(nlh->nlmsg_len) + rta_len > sizeof(buffer)) { XLOG_FATAL("AF_NETLINK buffer size error: %u instead of %u", XORP_UINT_CAST(sizeof(buffer)), XORP_UINT_CAST(NLMSG_ALIGN(nlh->nlmsg_len) + rta_len)); } rta_align_data = reinterpret_cast(rtattr) + RTA_ALIGN(rtattr->rta_len); rtattr = static_cast(rta_align_data); rtattr->rta_type = RTA_TABLE; rtattr->rta_len = rta_len; data = static_cast(RTA_DATA(rtattr)); memcpy(data, &uint32_table_id, sizeof(uint32_table_id)); nlh->nlmsg_len = NLMSG_ALIGN(nlh->nlmsg_len) + rta_len; } #endif // HAVE_NETLINK_SOCKET_ATTRIBUTE_RTA_TABLE if (ns.sendto(&buffer, nlh->nlmsg_len, 0, reinterpret_cast(&snl), sizeof(snl)) != (ssize_t)nlh->nlmsg_len) { XLOG_ERROR("Error writing to netlink socket: %s", strerror(errno)); return (XORP_ERROR); } // // Force to receive data from the kernel, and then parse it // string error_msg; if (_ns_reader.receive_data(ns, nlh->nlmsg_seq, error_msg) != XORP_OK) { XLOG_ERROR("Error reading from netlink socket: %s", error_msg.c_str()); return (XORP_ERROR); } if (parse_buffer_netlink_socket(fibconfig().system_config_iftree(), fte, _ns_reader.buffer(), true, fibconfig()) != XORP_OK) { return (XORP_ERROR); } return (XORP_OK); } int FibConfigEntryGetNetlinkSocket::parse_buffer_netlink_socket( const IfTree& iftree, FteX& fte, const vector& buffer, bool is_nlm_get_only, const FibConfig& fibconfig) { size_t buffer_bytes = buffer.size(); AlignData align_data(buffer); const struct nlmsghdr* nlh; for (nlh = align_data.payload(); NLMSG_OK(nlh, buffer_bytes); nlh = NLMSG_NEXT(const_cast(nlh), buffer_bytes)) { void* nlmsg_data = NLMSG_DATA(const_cast(nlh)); //XLOG_INFO("Received fibconfig netlink msg, type: %i\n", // (int)(nlh->nlmsg_type)); switch (nlh->nlmsg_type) { case NLMSG_ERROR: { const struct nlmsgerr* err; err = reinterpret_cast(nlmsg_data); if (nlh->nlmsg_len < NLMSG_LENGTH(sizeof(*err))) { XLOG_ERROR("AF_NETLINK nlmsgerr length error"); break; } errno = -err->error; XLOG_ERROR("AF_NETLINK NLMSG_ERROR message: %s", strerror(errno)); } break; case NLMSG_DONE: { return (XORP_ERROR); // XXX: entry not found } break; case NLMSG_NOOP: break; case RTM_NEWROUTE: case RTM_DELROUTE: case RTM_GETROUTE: { if (is_nlm_get_only) { // // Consider only the "get" entries returned by RTM_GETROUTE. // XXX: RTM_NEWROUTE below instead of RTM_GETROUTE is not // a mistake, but an artifact of Linux logistics. // if (nlh->nlmsg_type != RTM_NEWROUTE) break; } const struct rtmsg* rtmsg; int rta_len = RTM_PAYLOAD(nlh); if (rta_len < 0) { XLOG_ERROR("AF_NETLINK rtmsg length error"); break; } rtmsg = reinterpret_cast(nlmsg_data); if (rtmsg->rtm_type == RTN_MULTICAST) break; // XXX: ignore multicast entries if (rtmsg->rtm_type == RTN_BROADCAST) break; // XXX: ignore broadcast entries string err_msg; int rv = NlmUtils::nlm_get_to_fte_cfg(iftree, fte, nlh, rtmsg, rta_len, fibconfig, err_msg); //if (rv != XORP_OK) { //XLOG_WARNING("WARNING: nlm_get_to_fte_cfg failed: %s\n", err_msg.c_str()); //} return rv; } break; default: debug_msg("Unhandled type %s(%d) (%d bytes)\n", NlmUtils::nlm_msg_type(nlh->nlmsg_type).c_str(), nlh->nlmsg_type, nlh->nlmsg_len); } } return (XORP_ERROR); } #endif // HAVE_NETLINK_SOCKETS xorp/fea/data_plane/fibconfig/fibconfig_entry_set_iphelper.cc0000664000076400007640000002515111620706527024664 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/win_io.h" #ifdef HAVE_IPHLPAPI_H #include #endif #ifdef HAVE_ROUTPROT_H #include #endif #include "fea/fibconfig.hh" #include "fibconfig_entry_set_iphelper.hh" #ifndef MIB_IPROUTE_TYPE_OTHER #define MIB_IPROUTE_TYPE_OTHER (1) #endif #ifndef MIB_IPROUTE_METRIC_UNUSED #define MIB_IPROUTE_METRIC_UNUSED ((DWORD)-1) #endif // // Set single-entry information into the unicast forwarding table. // // The mechanism to set the information is the IP helper API for // Windows (IPHLPAPI.DLL). // #ifdef HOST_OS_WINDOWS FibConfigEntrySetIPHelper::FibConfigEntrySetIPHelper(FeaDataPlaneManager& fea_data_plane_manager) : FibConfigEntrySet(fea_data_plane_manager) { } FibConfigEntrySetIPHelper::~FibConfigEntrySetIPHelper() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the IP Helper mechanism to set " "information about forwarding table from the underlying " "system: %s", error_msg.c_str()); } } int FibConfigEntrySetIPHelper::start(string& error_msg) { UNUSED(error_msg); if (_is_running) return (XORP_OK); _is_running = true; return (XORP_OK); } int FibConfigEntrySetIPHelper::stop(string& error_msg) { UNUSED(error_msg); if (! _is_running) return (XORP_OK); _is_running = false; return (XORP_OK); } int FibConfigEntrySetIPHelper::add_entry4(const Fte4& fte) { FteX ftex(fte); return (add_entry(ftex)); } int FibConfigEntrySetIPHelper::delete_entry4(const Fte4& fte) { FteX ftex(fte); return (delete_entry(ftex)); } int FibConfigEntrySetIPHelper::add_entry6(const Fte6& fte) { FteX ftex(fte); return (add_entry(ftex)); } int FibConfigEntrySetIPHelper::delete_entry6(const Fte6& fte) { FteX ftex(fte); return (delete_entry(ftex)); } int FibConfigEntrySetIPHelper::add_entry(const FteX& fte) { MIB_IPFORWARDROW ipfwdrow; int family = fte.net().af(); debug_msg("add_entry (network = %s nexthop = %s)\n", fte.net().str().c_str(), fte.nexthop().str().c_str()); // Check that the family is supported do { if (fte.nexthop().is_ipv4()) { if (! fea_data_plane_manager().have_ipv4()) return (XORP_ERROR); break; } if (fte.nexthop().is_ipv6()) { if (! fea_data_plane_manager().have_ipv6()) return (XORP_ERROR); break; } break; } while (false); if (fte.is_connected_route()) return (XORP_OK); // XXX: don't add/remove directly-connected routes switch (family) { case AF_INET: break; #ifdef HAVE_IPV6 case AF_INET6: return (XORP_ERROR); break; #endif // HAVE_IPV6 default: XLOG_UNREACHABLE(); break; } bool is_existing = false; IPAddr tmpdest; IPAddr tmpnexthop; IPAddr tmpmask; DWORD result; fte.net().masked_addr().get_ipv4().copy_out(reinterpret_cast( &tmpdest)); fte.nexthop().get_ipv4().copy_out(reinterpret_cast(&tmpnexthop)); if (!fte.is_host_route()) { fte.net().netmask().get_ipv4().copy_out(reinterpret_cast( &tmpmask)); } else { tmpmask = 0xFFFFFFFF; } // Check for an already existing specific route to this destination. result = GetBestRoute(tmpdest, 0, &ipfwdrow); if (result == NO_ERROR && ipfwdrow.dwForwardDest == tmpdest && ipfwdrow.dwForwardMask == tmpmask && ipfwdrow.dwForwardNextHop == tmpnexthop) { is_existing = true; } else { memset(&ipfwdrow, 0, sizeof(ipfwdrow)); } const IfTree& iftree = fibconfig().merged_config_iftree(); const IfTreeVif* vifp = iftree.find_vif(fte.ifname(), fte.vifname()); XLOG_ASSERT(vifp != NULL); ipfwdrow.dwForwardDest = tmpdest; ipfwdrow.dwForwardMask = tmpmask; ipfwdrow.dwForwardNextHop = tmpnexthop; ipfwdrow.dwForwardIfIndex = vifp->pif_index(); ipfwdrow.dwForwardProto = PROTO_IP_NETMGMT; ipfwdrow.dwForwardType = MIB_IPROUTE_TYPE_OTHER; // // The following fields *must* be filled out if RRAS is running. // XXX: Should obtain interface metric and use that; we use a hard // coded metric of 20 because other values do not work. // ipfwdrow.dwForwardAge = INFINITE; ipfwdrow.dwForwardMetric1 = 20; #if 0 // Filling out the fields in this way doesn't work; // seems we must set a valid metric for the first metric field ipfwdrow.dwForwardMetric1 = ipfwdrow.dwForwardMetric2 = ipfwdrow.dwForwardMetric3 = ipfwdrow.dwForwardMetric4 = ipfwdrow.dwForwardMetric5 = MIB_IPROUTE_METRIC_UNUSED; #endif #if 0 // XXX: Not enough arguments in debug_msg for this debugging message: XLOG_INFO("ipfwdrow: %08lx/%08lx pol %lu nh %08lx index %lu t %lu p %lu a %lu as %lu metrics %lu %lu %lu %lu %lu", ipfwdrow.dwForwardDest, ipfwdrow.dwForwardMask, ipfwdrow.dwForwardPolicy, ipfwdrow.dwForwardNextHop, ipfwdrow.dwForwardIfIndex, ipfwdrow.dwForwardType, ipfwdrow.dwForwardProto, ipfwdrow.dwForwardAge, ipfwdrow.dwForwardNextHopAS, ipfwdrow.dwForwardMetric1, ipfwdrow.dwForwardMetric2, ipfwdrow.dwForwardMetric3, ipfwdrow.dwForwardMetric4, ipfwdrow.dwForwardMetric5); #endif if (is_existing) { result = SetIpForwardEntry(&ipfwdrow); } else { result = CreateIpForwardEntry(&ipfwdrow); } if (result != NO_ERROR) { XLOG_ERROR("%sIpForwardEntry() failed, error: %s (network = %s nexthop = %s)\n", is_existing ? "Set" : "Create", win_strerror(result), fte.net().str().c_str(), fte.nexthop().str().c_str()); return (XORP_ERROR); } return (XORP_OK); } int FibConfigEntrySetIPHelper::delete_entry(const FteX& fte) { MIB_IPFORWARDROW ipfwdrow; int family = fte.net().af(); debug_msg("delete_entry (network = %s nexthop = %s)\n", fte.net().str().c_str(), fte.nexthop().str().c_str()); // Check that the family is supported do { if (fte.nexthop().is_ipv4()) { if (! fea_data_plane_manager().have_ipv4()) return (XORP_ERROR); break; } if (fte.nexthop().is_ipv6()) { if (! fea_data_plane_manager().have_ipv6()) return (XORP_ERROR); break; } break; } while (false); if (fte.is_connected_route()) return (XORP_OK); // XXX: don't add/remove directly-connected routes switch (family) { case AF_INET: break; #ifdef HAVE_IPV6 case AF_INET6: return (XORP_ERROR); break; #endif // HAVE_IPV6 default: XLOG_UNREACHABLE(); break; } // // Fully fill out the MIB_IPFORWARDROW members in order. // memset(&ipfwdrow, 0, sizeof(ipfwdrow)); fte.net().masked_addr().get_ipv4().copy_out(reinterpret_cast( &ipfwdrow.dwForwardDest)); fte.net().netmask().get_ipv4().copy_out(reinterpret_cast( &ipfwdrow.dwForwardMask)); ipfwdrow.dwForwardPolicy = 0; fte.nexthop().get_ipv4().copy_out(reinterpret_cast( &ipfwdrow.dwForwardNextHop)); // // If the RIB did not tell us which interface this route was // previously added on, there is a programming error. // XLOG_ASSERT(!fte.ifname().empty()); // // Look up the interface in the live IfTree. // The FIB needs to know the physical IfIndex for removal. // If the FEA does not know about this interface, there is a // programming error. // const IfTree& iftree = fibconfig().merged_config_iftree(); const IfTreeVif* vifp = iftree.find_vif(fte.ifname(), fte.vifname()); if (!vifp) { // Maybe VIF is already deleted or we are not configured to use it. XLOG_WARNING("Trying to delete route for iface: %s on tree: %s, but cannot find iface. Will continue.\n", fte.ifname().c_str(), iftree.getName().c_str()); return XORP_OK; } XLOG_ASSERT(vifp != NULL); ipfwdrow.dwForwardIfIndex = vifp->pif_index(); // // The FIB fills out the dwForwardType field strictly according // to RFC 1354. Because we don't specify a specific ForwardType // on creation, specify the non-specific manifest constant // MIB_IPROUTE_TYPE_OTHER when removing the route. // ipfwdrow.dwForwardType = MIB_IPROUTE_TYPE_OTHER; ipfwdrow.dwForwardProto = PROTO_IP_NETMGMT; // // It seems to be OK to ignore the age field when deleting an // PROTO_IP_NETMGMT route. // ipfwdrow.dwForwardAge = 0; ipfwdrow.dwForwardNextHopAS = 0; // // Don't specify any of the metric fields. // // We are supposed to specify the dwForwardMetric1 field for // PROTO_IP_NETMGMT and leave the others as MIB_IPROUTE_METRIC_UNUSED. // // Currently we don't do this, as to do so would require maintaining // multipath state here. We don't specify it for FIB entry creation- // so Windows will pick the metric from the interface's default metric. // ipfwdrow.dwForwardMetric1 = ipfwdrow.dwForwardMetric2 = ipfwdrow.dwForwardMetric3 = ipfwdrow.dwForwardMetric4 = ipfwdrow.dwForwardMetric5 = MIB_IPROUTE_METRIC_UNUSED; // XXX: debug_msg() does not support >=12 arguments at this time debug_msg("DeleteIpForwardEntry(%p) : %08lx %08lx %lu %08lx %08lx %lu " "%lu %lu %lu %lu\n", &ipfwdrow, ipfwdrow.dwForwardDest, ipfwdrow.dwForwardMask, ipfwdrow.dwForwardPolicy, ipfwdrow.dwForwardNextHop, ipfwdrow.dwForwardIfIndex, ipfwdrow.dwForwardType, // format string break above ipfwdrow.dwForwardProto, ipfwdrow.dwForwardAge, ipfwdrow.dwForwardNextHopAS, ipfwdrow.dwForwardMetric1); DWORD result = DeleteIpForwardEntry(&ipfwdrow); if (result != NO_ERROR) { XLOG_ERROR("DeleteIpForwardEntry() failed, error: %s(%i) (network = %s nexthop = %s)\n", win_strerror(result), (int)(result), fte.net().str().c_str(), fte.nexthop().str().c_str()); return (XORP_ERROR); } return (XORP_OK); } #endif // HOST_OS_WINDOWS xorp/fea/data_plane/fibconfig/fibconfig_entry_set_routing_socket.hh0000664000076400007640000000704111540225523026114 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/data_plane/fibconfig/fibconfig_entry_set_routing_socket.hh,v 1.9 2008/10/02 21:56:57 bms Exp $ #ifndef __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_ENTRY_SET_ROUTING_SOCKET_HH__ #define __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_ENTRY_SET_ROUTING_SOCKET_HH__ #include #ifdef HAVE_ROUTING_SOCKETS #include "fea/fibconfig_entry_set.hh" #include "fea/data_plane/control_socket/routing_socket.hh" class FibConfigEntrySetRoutingSocket : public FibConfigEntrySet, public RoutingSocket { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ FibConfigEntrySetRoutingSocket(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~FibConfigEntrySetRoutingSocket(); /** * Start operation. * * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg); /** * Add a single IPv4 forwarding entry. * * Must be within a configuration interval. * * @param fte the entry to add. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int add_entry4(const Fte4& fte); /** * Delete a single IPv4 forwarding entry. * * Must be with a configuration interval. * * @param fte the entry to delete. Only destination and netmask are used. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int delete_entry4(const Fte4& fte); /** * Add a single IPv6 forwarding entry. * * Must be within a configuration interval. * * @param fte the entry to add. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int add_entry6(const Fte6& fte); /** * Delete a single IPv6 forwarding entry. * * Must be within a configuration interval. * * @param fte the entry to delete. Only destination and netmask are used. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int delete_entry6(const Fte6& fte); /** Routing table ID that we are interested in might have changed. */ virtual int notify_table_id_change(uint32_t new_tbl) { // Virtual routing tables not implemented on BSD UNUSED(new_tbl); return XORP_OK; } private: int add_entry(const FteX& fte); int delete_entry(const FteX& fte); }; #endif #endif // __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_ENTRY_SET_ROUTING_SOCKET_HH__ xorp/fea/data_plane/fibconfig/fibconfig_forwarding_windows.hh0000664000076400007640000001141211540224222024665 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2007-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/data_plane/fibconfig/fibconfig_forwarding_windows.hh,v 1.6 2008/10/02 21:56:58 bms Exp $ #ifndef __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_FORWARDING_WINDOWS_HH__ #define __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_FORWARDING_WINDOWS_HH__ #include "fea/fibconfig_forwarding.hh" class FibConfigForwardingWindows : public FibConfigForwarding { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ FibConfigForwardingWindows(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~FibConfigForwardingWindows(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg); /** * Test whether the IPv4 unicast forwarding engine is enabled or disabled * to forward packets. * * @param ret_value if true on return, then the IPv4 unicast forwarding * is enabled, otherwise is disabled. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int unicast_forwarding_enabled4(bool& ret_value, string& error_msg) const; /** * Test whether the IPv6 unicast forwarding engine is enabled or disabled * to forward packets. * * @param ret_value if true on return, then the IPv6 unicast forwarding * is enabled, otherwise is disabled. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int unicast_forwarding_enabled6(bool& ret_value, string& error_msg) const; /** * Test whether the acceptance of IPv6 Router Advertisement messages is * enabled or disabled. * * @param ret_value if true on return, then the acceptance of IPv6 Router * Advertisement messages is enabled, otherwise is disabled. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int accept_rtadv_enabled6(bool& ret_value, string& error_msg) const; /** * Set the IPv4 unicast forwarding engine to enable or disable forwarding * of packets. * * @param v if true, then enable IPv4 unicast forwarding, otherwise * disable it. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int set_unicast_forwarding_enabled4(bool v, string& error_msg); /** * Set the IPv6 unicast forwarding engine to enable or disable forwarding * of packets. * * @param v if true, then enable IPv6 unicast forwarding, otherwise * disable it. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int set_unicast_forwarding_enabled6(bool v, string& error_msg); /** * Enable or disable the acceptance of IPv6 Router Advertisement messages * from other routers. It should be enabled for hosts, and disabled for * routers. * * @param v if true, then enable the acceptance of IPv6 Router * Advertisement messages, otherwise disable it. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int set_accept_rtadv_enabled6(bool v, string& error_msg); private: #ifdef HOST_OS_WINDOWS // // State for Windows EnableRouter() API function. // HANDLE _event; OVERLAPPED _overlapped; int _enablecnt; #endif }; #endif // __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_FORWARDING_WINDOWS_HH__ xorp/fea/data_plane/fibconfig/fibconfig_entry_observer_rtmv2.cc0000664000076400007640000000536311540224222025151 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "fea/fibconfig.hh" #include "fibconfig_entry_observer_rtmv2.hh" // // Observe single-entry information change about the unicast forwarding table. // // E.g., if the forwarding table has changed, then the information // received by the observer would specify the particular entry that // has changed. // // The mechanism to observe the information is Router Manager V2. // #ifdef HOST_OS_WINDOWS FibConfigEntryObserverRtmV2::FibConfigEntryObserverRtmV2(FeaDataPlaneManager& fea_data_plane_manager) : FibConfigEntryObserver(fea_data_plane_manager) #if 0 WinRtmPipe(fea_data_plane_manager.eventloop()), WinRtmPipeObserver(*(WinRtmPipe *)this) #endif { } FibConfigEntryObserverRtmV2::~FibConfigEntryObserverRtmV2() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the Router Manager V2 mechanism to observe " "information about forwarding table from the underlying " "system: %s", error_msg.c_str()); } } int FibConfigEntryObserverRtmV2::start(string& error_msg) { #if 0 if (_is_running) return (XORP_OK); if (WinRtmPipe::start(error_msg) != XORP_OK) return (XORP_ERROR); _is_running = true; #endif return (XORP_OK); UNUSED(error_msg); } int FibConfigEntryObserverRtmV2::stop(string& error_msg) { #if 0 if (! _is_running) return (XORP_OK); if (WinRtmPipe::stop(error_msg) != XORP_OK) return (XORP_ERROR); _is_running = false; #endif return (XORP_OK); UNUSED(error_msg); } #if 0 void FibConfigEntryObserverRtmV2::receive_data(const vector& buffer) { // TODO: XXX: PAVPAVPAV: use it? UNUSED(buffer); } void FibConfigEntryObserverRtmV2::routing_socket_data(const vector& buffer) { receive_data(buffer); } #endif // 0 #endif // HOST_OS_WINDOWS xorp/fea/data_plane/fibconfig/fibconfig_entry_observer_routing_socket.cc0000664000076400007640000000540411540225523027137 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include #ifdef HAVE_ROUTING_SOCKETS #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "fea/fibconfig.hh" #include "fibconfig_entry_observer_routing_socket.hh" // // Observe single-entry information change about the unicast forwarding table. // // E.g., if the forwarding table has changed, then the information // received by the observer would specify the particular entry that // has changed. // // The mechanism to observe the information is routing sockets. // FibConfigEntryObserverRoutingSocket::FibConfigEntryObserverRoutingSocket(FeaDataPlaneManager& fea_data_plane_manager) : FibConfigEntryObserver(fea_data_plane_manager), RoutingSocket(fea_data_plane_manager.eventloop()), RoutingSocketObserver(*(RoutingSocket *)this) { } FibConfigEntryObserverRoutingSocket::~FibConfigEntryObserverRoutingSocket() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the routing sockets mechanism to observe " "information about forwarding table from the underlying " "system: %s", error_msg.c_str()); } } int FibConfigEntryObserverRoutingSocket::start(string& error_msg) { if (_is_running) return (XORP_OK); if (RoutingSocket::start(error_msg) != XORP_OK) return (XORP_ERROR); _is_running = true; return (XORP_OK); } int FibConfigEntryObserverRoutingSocket::stop(string& error_msg) { if (! _is_running) return (XORP_OK); if (RoutingSocket::stop(error_msg) != XORP_OK) return (XORP_ERROR); _is_running = false; return (XORP_OK); } void FibConfigEntryObserverRoutingSocket::receive_data(const vector& buffer) { // TODO: XXX: PAVPAVPAV: use it? UNUSED(buffer); } void FibConfigEntryObserverRoutingSocket::routing_socket_data(const vector& buffer) { receive_data(buffer); } #endif // HAVE_ROUTING_SOCKETS xorp/fea/data_plane/fibconfig/fibconfig_entry_get_click.hh0000664000076400007640000000702511540225522024127 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/data_plane/fibconfig/fibconfig_entry_get_click.hh,v 1.9 2008/10/02 21:56:55 bms Exp $ #ifndef __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_ENTRY_GET_CLICK_HH__ #define __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_ENTRY_GET_CLICK_HH__ #include #ifdef XORP_USE_CLICK #include "fea/fibconfig_entry_get.hh" #include "fea/data_plane/control_socket/click_socket.hh" class FibConfigEntryGetClick : public FibConfigEntryGet, public ClickSocket { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ FibConfigEntryGetClick(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~FibConfigEntryGetClick(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg); /** * Lookup an IPv4 route by destination address. * * @param dst host address to resolve. * @param fte return-by-reference forwarding table entry. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int lookup_route_by_dest4(const IPv4& dst, Fte4& fte); /** * Lookup an IPv4 route by network address. * * @param dst network address to resolve. * @param fte return-by-reference forwarding table entry. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int lookup_route_by_network4(const IPv4Net& dst, Fte4& fte); /** * Lookup an IPv6 route by destination address. * * @param dst host address to resolve. * @param fte return-by-reference forwarding table entry. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int lookup_route_by_dest6(const IPv6& dst, Fte6& fte); /** * Lookup an IPv6 route by network address. * * @param dst network address to resolve. * @param fte return-by-reference forwarding table entry. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int lookup_route_by_network6(const IPv6Net& dst, Fte6& fte); /** Routing table ID that we are interested in might have changed. */ virtual int notify_table_id_change(uint32_t new_tbl) { UNUSED(new_tbl); return XORP_OK; } private: ClickSocketReader _cs_reader; }; #endif #endif // __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_ENTRY_GET_CLICK_HH__ xorp/fea/data_plane/fibconfig/fibconfig_table_get_dummy.hh0000664000076400007640000000535211540225523024125 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/data_plane/fibconfig/fibconfig_table_get_dummy.hh,v 1.9 2008/10/02 21:56:59 bms Exp $ #ifndef __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_TABLE_GET_DUMMY_HH__ #define __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_TABLE_GET_DUMMY_HH__ #include "fea/fibconfig_table_get.hh" class FibConfigTableGetDummy : public FibConfigTableGet { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ FibConfigTableGetDummy(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~FibConfigTableGetDummy(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg); /** * Obtain the IPv4 unicast forwarding table. * * @param fte_list the return-by-reference list with all entries in * the IPv4 unicast forwarding table. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int get_table4(list& fte_list); /** * Obtain the IPv6 unicast forwarding table. * * @param fte_list the return-by-reference list with all entries in * the IPv6 unicast forwarding table. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int get_table6(list& fte_list); /** Routing table ID that we are interested in might have changed. */ virtual int notify_table_id_change(uint32_t new_tbl) { UNUSED(new_tbl); return XORP_OK; } private: }; #endif // __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_TABLE_GET_DUMMY_HH__ xorp/fea/data_plane/fibconfig/fibconfig_entry_get_dummy.hh0000664000076400007640000000654411540225522024202 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/data_plane/fibconfig/fibconfig_entry_get_dummy.hh,v 1.9 2008/10/02 21:56:55 bms Exp $ #ifndef __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_ENTRY_GET_DUMMY_HH__ #define __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_ENTRY_GET_DUMMY_HH__ #include "fea/fibconfig_entry_get.hh" class FibConfigEntryGetDummy : public FibConfigEntryGet { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ FibConfigEntryGetDummy(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~FibConfigEntryGetDummy(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg); /** * Lookup an IPv4 route by destination address. * * @param dst host address to resolve. * @param fte return-by-reference forwarding table entry. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int lookup_route_by_dest4(const IPv4& dst, Fte4& fte); /** * Lookup an IPv4 route by network address. * * @param dst network address to resolve. * @param fte return-by-reference forwarding table entry. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int lookup_route_by_network4(const IPv4Net& dst, Fte4& fte); /** * Lookup an IPv6 route by destination address. * * @param dst host address to resolve. * @param fte return-by-reference forwarding table entry. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int lookup_route_by_dest6(const IPv6& dst, Fte6& fte); /** * Lookup an IPv6 route by network address. * * @param dst network address to resolve. * @param fte return-by-reference forwarding table entry. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int lookup_route_by_network6(const IPv6Net& dst, Fte6& fte); /** Routing table ID that we are interested in might have changed. */ virtual int notify_table_id_change(uint32_t new_tbl) { UNUSED(new_tbl); return XORP_OK; } private: }; #endif // __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_ENTRY_GET_DUMMY_HH__ xorp/fea/data_plane/fibconfig/fibconfig_entry_get_routing_socket.hh0000664000076400007640000001231611540225522026100 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/data_plane/fibconfig/fibconfig_entry_get_routing_socket.hh,v 1.9 2008/10/02 21:56:56 bms Exp $ #ifndef __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_ENTRY_GET_ROUTING_SOCKET_HH__ #define __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_ENTRY_GET_ROUTING_SOCKET_HH__ #include #ifdef HAVE_ROUTING_SOCKETS #include "fea/fibconfig_entry_get.hh" #include "fea/data_plane/control_socket/routing_socket.hh" class FibConfigEntryGetRoutingSocket : public FibConfigEntryGet, public RoutingSocket { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ FibConfigEntryGetRoutingSocket(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~FibConfigEntryGetRoutingSocket(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg); /** * Lookup an IPv4 route by destination address. * * @param dst host address to resolve. * @param fte return-by-reference forwarding table entry. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int lookup_route_by_dest4(const IPv4& dst, Fte4& fte); /** * Lookup an IPv4 route by network address. * * @param dst network address to resolve. * @param fte return-by-reference forwarding table entry. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int lookup_route_by_network4(const IPv4Net& dst, Fte4& fte); /** * Lookup an IPv6 route by destination address. * * @param dst host address to resolve. * @param fte return-by-reference forwarding table entry. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int lookup_route_by_dest6(const IPv6& dst, Fte6& fte); /** * Lookup an IPv6 route by network address. * * @param dst network address to resolve. * @param fte return-by-reference forwarding table entry. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int lookup_route_by_network6(const IPv6Net& dst, Fte6& fte); /** * Flag values used to tell underlying FIB message parsing routines * which messages the caller is interested in. */ struct FibMsg { enum { UPDATES = 1 << 0, GETS = 1 << 1, RESOLVES = 1 << 2 }; }; typedef uint32_t FibMsgSet; /** * Parse information about forwarding entry information received from * the underlying system. * * The information to parse is in RTM format * (e.g., obtained by routing sockets or by sysctl(3) mechanism). * * @param iftree the interface tree to use. * @param fte the Fte storage to store the parsed information. * @param buffer the buffer with the data to parse. * @param filter the set of messages that caller is interested in. * @return XORP_OK on success, otherwise XORP_ERROR. * @see FteX. */ static int parse_buffer_routing_socket(const IfTree& iftree, FteX& fte, const vector& buffer, FibMsgSet filter); /** Routing table ID that we are interested in might have changed. */ virtual int notify_table_id_change(uint32_t new_tbl) { // Virtual routing tables not implemented on BSD UNUSED(new_tbl); return XORP_OK; } private: /** * Lookup a route by destination address. * * @param dst host address to resolve. * @param fte return-by-reference forwarding table entry. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int lookup_route_by_dest(const IPvX& dst, FteX& fte); /** * Lookup a route by network address. * * @param dst network address to resolve. * @param fte return-by-reference forwarding table entry. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int lookup_route_by_network(const IPvXNet& dst, FteX& fte); RoutingSocketReader _rs_reader; }; #endif #endif // __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_ENTRY_GET_ROUTING_SOCKET_HH__ xorp/fea/data_plane/fibconfig/fibconfig_table_parse_netlink_socket.cc0000664000076400007640000001021211540225523026316 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include #ifdef HAVE_NETLINK_SOCKETS #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #ifdef HAVE_LINUX_TYPES_H #include #endif #ifdef HAVE_LINUX_RTNETLINK_H #include #endif #include "fea/fibconfig.hh" #include "fea/data_plane/control_socket/netlink_socket_utilities.hh" #include "fibconfig_table_get_netlink_socket.hh" // // Parse information about routing table information received from // the underlying system. // // The information to parse is in NETLINK format // (e.g., obtained by netlink(7) sockets mechanism). // // Reading netlink(3) manual page is a good start for understanding this // int FibConfigTableGetNetlinkSocket::parse_buffer_netlink_socket( int family, const IfTree& iftree, list& fte_list, const vector& buffer, bool is_nlm_get_only, const FibConfig& fibconfig) { size_t buffer_bytes = buffer.size(); AlignData align_data(buffer); const struct nlmsghdr* nlh; for (nlh = align_data.payload(); NLMSG_OK(nlh, buffer_bytes); nlh = NLMSG_NEXT(const_cast(nlh), buffer_bytes)) { void* nlmsg_data = NLMSG_DATA(const_cast(nlh)); //XLOG_INFO("nlh->msg-type: %s (%i)", // NlmUtils::nlm_msg_type(nlh->nlmsg_type).c_str(), // (int)(nlh->nlmsg_type)); switch (nlh->nlmsg_type) { case NLMSG_ERROR: { const struct nlmsgerr* err; err = reinterpret_cast(nlmsg_data); if (nlh->nlmsg_len < NLMSG_LENGTH(sizeof(*err))) { XLOG_ERROR("AF_NETLINK nlmsgerr length error"); break; } errno = -err->error; XLOG_ERROR("AF_NETLINK NLMSG_ERROR message: %s", strerror(errno)); } break; case NLMSG_DONE: { return (XORP_OK); // XXX: OK even if there were no entries } break; case NLMSG_NOOP: break; case RTM_NEWROUTE: case RTM_DELROUTE: case RTM_GETROUTE: { if (is_nlm_get_only) { // // Consider only the "get" entries returned by RTM_GETROUTE. // XXX: RTM_NEWROUTE below instead of RTM_GETROUTE is not // a mistake, but an artifact of Linux logistics. // if (nlh->nlmsg_type != RTM_NEWROUTE) break; } const struct rtmsg* rtmsg; int rta_len = RTM_PAYLOAD(nlh); if (rta_len < 0) { XLOG_ERROR("AF_NETLINK rtmsg length error"); break; } rtmsg = reinterpret_cast(nlmsg_data); if (rtmsg->rtm_family != family) break; if (rtmsg->rtm_flags & RTM_F_CLONED) break; // XXX: ignore cloned entries if (rtmsg->rtm_type == RTN_MULTICAST) break; // XXX: ignore multicast entries if (rtmsg->rtm_type == RTN_BROADCAST) break; // XXX: ignore broadcast entries FteX fte(family); string err_msg; if (NlmUtils::nlm_get_to_fte_cfg(iftree, fte, nlh, rtmsg, rta_len, fibconfig, err_msg) == XORP_OK) { fte_list.push_back(fte); } else { //XLOG_INFO("nlm_get_to_fte_cfg had error: %s", err_msg.c_str()); } } break; default: debug_msg("Unhandled type %s(%d) (%d bytes)\n", NlmUtils::nlm_msg_type(nlh->nlmsg_type).c_str(), nlh->nlmsg_type, nlh->nlmsg_len); } } return (XORP_OK); // XXX: OK even if there were no entries } #endif // HAVE_NETLINK_SOCKETS xorp/fea/data_plane/fibconfig/SConscript0000664000076400007640000000613711540225522020450 0ustar greearbgreearb# Copyright (c) 2009-2011 XORP, Inc and Others # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # $ID$ import os Import("env") env = env.Clone() env.AppendUnique(CPPPATH = [ '#', '$BUILDDIR', ]) is_shared = env.has_key('SHAREDLIBS') sources = [ # C++ files 'fibconfig_entry_get_iphelper.cc', 'fibconfig_entry_get_routing_socket.cc', 'fibconfig_entry_get_rtmv2.cc', 'fibconfig_entry_observer_iphelper.cc', 'fibconfig_entry_observer_routing_socket.cc', 'fibconfig_entry_observer_rtmv2.cc', 'fibconfig_entry_parse_routing_socket.cc', 'fibconfig_entry_set_iphelper.cc', 'fibconfig_entry_set_routing_socket.cc', 'fibconfig_entry_set_rtmv2.cc', 'fibconfig_forwarding_proc_linux.cc', 'fibconfig_forwarding_solaris.cc', 'fibconfig_forwarding_sysctl.cc', 'fibconfig_forwarding_windows.cc', 'fibconfig_table_get_iphelper.cc', 'fibconfig_table_get_sysctl.cc', 'fibconfig_table_observer_iphelper.cc', 'fibconfig_table_observer_routing_socket.cc', 'fibconfig_table_observer_rtmv2.cc', 'fibconfig_table_parse_routing_socket.cc', 'fibconfig_table_set_iphelper.cc', 'fibconfig_table_set_routing_socket.cc', 'fibconfig_table_set_rtmv2.cc', ] if not (env.has_key('mingw') and env['mingw']): sources.append('fibconfig_table_set_netlink_socket.cc') sources.append('fibconfig_table_get_netlink_socket.cc') sources.append('fibconfig_table_parse_netlink_socket.cc') sources.append('fibconfig_table_observer_netlink_socket.cc') sources.append('fibconfig_entry_get_netlink_socket.cc') sources.append('fibconfig_entry_set_netlink_socket.cc') sources.append('fibconfig_entry_observer_netlink_socket.cc') if env['enable_click']: sources.append('fibconfig_entry_get_click.cc') sources.append('fibconfig_entry_set_click.cc') sources.append('fibconfig_table_set_click.cc') sources.append('fibconfig_table_get_click.cc') if env['enable_fea_dummy']: sources += [ 'fibconfig_entry_get_dummy.cc', 'fibconfig_entry_observer_dummy.cc', 'fibconfig_entry_set_dummy.cc', 'fibconfig_forwarding_dummy.cc', 'fibconfig_table_get_dummy.cc', 'fibconfig_table_observer_dummy.cc', 'fibconfig_table_set_dummy.cc', ] if is_shared: libxfc = env.SharedLibrary(target = 'libxorp_fea_fibconfig', source = sources) env.Alias('install', env.InstallLibrary(env['xorp_libdir'], libxfc)) else: libxfc = env.StaticLibrary(target = 'libxorp_fea_fibconfig', source = sources) Default(libxfc) xorp/fea/data_plane/fibconfig/fibconfig_table_get_netlink_socket.cc0000664000076400007640000001664611540225523026004 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include #ifdef HAVE_NETLINK_SOCKETS #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #ifdef HAVE_LINUX_TYPES_H #include #endif #ifdef HAVE_LINUX_RTNETLINK_H #include #endif #include "fea/fibconfig.hh" #include "fibconfig_table_get_netlink_socket.hh" // // Get the whole table information from the unicast forwarding table. // // The mechanism to obtain the information is netlink(7) sockets. // FibConfigTableGetNetlinkSocket::FibConfigTableGetNetlinkSocket(FeaDataPlaneManager& fea_data_plane_manager) : FibConfigTableGet(fea_data_plane_manager), NetlinkSocket(fea_data_plane_manager.eventloop(), fea_data_plane_manager.fibconfig().get_netlink_filter_table_id()), _ns_reader(*(NetlinkSocket *)this) { } FibConfigTableGetNetlinkSocket::~FibConfigTableGetNetlinkSocket() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the netlink(7) sockets mechanism to get " "whole forwarding table from the underlying " "system: %s", error_msg.c_str()); } } int FibConfigTableGetNetlinkSocket::start(string& error_msg) { if (_is_running) return (XORP_OK); if (NetlinkSocket::start(error_msg) != XORP_OK) return (XORP_ERROR); _is_running = true; return (XORP_OK); } int FibConfigTableGetNetlinkSocket::stop(string& error_msg) { if (! _is_running) return (XORP_OK); if (NetlinkSocket::stop(error_msg) != XORP_OK) return (XORP_ERROR); _is_running = false; return (XORP_OK); } int FibConfigTableGetNetlinkSocket::get_table4(list& fte_list) { list ftex_list; // Get the table if (get_table(AF_INET, ftex_list) != XORP_OK) return (XORP_ERROR); // Copy the result back to the original list list::iterator iter; for (iter = ftex_list.begin(); iter != ftex_list.end(); ++iter) { FteX& ftex = *iter; fte_list.push_back(ftex.get_fte4()); } return (XORP_OK); } int FibConfigTableGetNetlinkSocket::get_table6(list& fte_list) { #ifndef HAVE_IPV6 UNUSED(fte_list); return (XORP_ERROR); #else list ftex_list; // Get the table if (get_table(AF_INET6, ftex_list) != XORP_OK) return (XORP_ERROR); // Copy the result back to the original list list::iterator iter; for (iter = ftex_list.begin(); iter != ftex_list.end(); ++iter) { FteX& ftex = *iter; fte_list.push_back(ftex.get_fte6()); } return (XORP_OK); #endif // HAVE_IPV6 } int FibConfigTableGetNetlinkSocket::get_table(int family, list& fte_list) { static const size_t buffer_size = sizeof(struct nlmsghdr) + sizeof(struct rtmsg) + 512; union { uint8_t data[buffer_size]; struct nlmsghdr nlh; } buffer; struct nlmsghdr* nlh = &buffer.nlh; struct sockaddr_nl snl; struct rtgenmsg* rtgenmsg; NetlinkSocket& ns = *this; // Check that the family is supported switch(family) { case AF_INET: if (! fea_data_plane_manager().have_ipv4()) return (XORP_ERROR); break; #ifdef HAVE_IPV6 case AF_INET6: if (! fea_data_plane_manager().have_ipv6()) return (XORP_ERROR); break; #endif // HAVE_IPV6 default: XLOG_UNREACHABLE(); break; } // // Set the request. First the socket, then the request itself. // // Set the socket memset(&snl, 0, sizeof(snl)); snl.nl_family = AF_NETLINK; snl.nl_pid = 0; // nl_pid = 0 if destination is the kernel snl.nl_groups = 0; // Set the request memset(&buffer, 0, sizeof(buffer)); nlh->nlmsg_len = NLMSG_LENGTH(sizeof(*rtgenmsg)); nlh->nlmsg_type = RTM_GETROUTE; nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT; // Get the whole table nlh->nlmsg_seq = ns.seqno(); nlh->nlmsg_pid = ns.nl_pid(); rtgenmsg = reinterpret_cast(NLMSG_DATA(nlh)); rtgenmsg->rtgen_family = family; struct rtmsg* rtmsg = reinterpret_cast(NLMSG_DATA(nlh)); uint32_t table_id = RT_TABLE_UNSPEC; // Default value for lookup // // Set the routing/forwarding table ID. // If the table ID is <= 0xff, then we set it in rtmsg->rtm_table, // otherwise we set rtmsg->rtm_table to RT_TABLE_UNSPEC and add the // real value as an RTA_TABLE attribute. // if (fibconfig().unicast_forwarding_table_id_is_configured(family)) table_id = fibconfig().unicast_forwarding_table_id(family); if (table_id <= 0xff) rtmsg->rtm_table = table_id; else rtmsg->rtm_table = RT_TABLE_UNSPEC; #ifdef HAVE_NETLINK_SOCKET_ATTRIBUTE_RTA_TABLE struct rtattr* rtattr = RTM_RTA(rtmsg); // Add the table ID as an attribute if (table_id > 0xff) { uint8_t* data; uint32_t uint32_table_id = table_id; int rta_len = RTA_LENGTH(sizeof(uint32_table_id)); if (NLMSG_ALIGN(nlh->nlmsg_len) + rta_len > sizeof(buffer)) { XLOG_FATAL("AF_NETLINK buffer size error: %u instead of %u", XORP_UINT_CAST(sizeof(buffer)), XORP_UINT_CAST(NLMSG_ALIGN(nlh->nlmsg_len) + rta_len)); } void* rta_align_data = reinterpret_cast(rtattr) + RTA_ALIGN(rtattr->rta_len); rtattr = static_cast(rta_align_data); rtattr->rta_type = RTA_TABLE; rtattr->rta_len = rta_len; data = static_cast(RTA_DATA(rtattr)); memcpy(data, &uint32_table_id, sizeof(uint32_table_id)); nlh->nlmsg_len = NLMSG_ALIGN(nlh->nlmsg_len) + rta_len; } #endif // HAVE_NETLINK_SOCKET_ATTRIBUTE_RTA_TABLE if (ns.sendto(&buffer, nlh->nlmsg_len, 0, reinterpret_cast(&snl), sizeof(snl)) != (ssize_t)nlh->nlmsg_len) { XLOG_ERROR("Error writing to netlink socket: %s", strerror(errno)); return (XORP_ERROR); } // // Force to receive data from the kernel, and then parse it // // // XXX: setting the flag below is a work-around hack because of a // Linux kernel bug: when we read the forwarding table the kernel // doesn't set the NLM_F_MULTI flag for the multipart messages. // ns.set_multipart_message_read(true); string error_msg; if (_ns_reader.receive_data(ns, nlh->nlmsg_seq, error_msg) != XORP_OK) { ns.set_multipart_message_read(false); XLOG_ERROR("Error reading from netlink socket: %s", error_msg.c_str()); return (XORP_ERROR); } // XXX: reset the multipart message read hackish flag ns.set_multipart_message_read(false); if (parse_buffer_netlink_socket(family, fibconfig().system_config_iftree(), fte_list, _ns_reader.buffer(), true, fibconfig()) != XORP_OK) { return (XORP_ERROR); } return (XORP_OK); } #endif // HAVE_NETLINK_SOCKETS xorp/fea/data_plane/fibconfig/fibconfig_entry_observer_iphelper.hh0000664000076400007640000000432211540224222025713 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_ENTRY_OBSERVER_IPHELPER_HH__ #define __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_ENTRY_OBSERVER_IPHELPER_HH__ #include "fea/fibconfig_entry_observer.hh" class FibConfigEntryObserverIPHelper : public FibConfigEntryObserver { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ FibConfigEntryObserverIPHelper(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~FibConfigEntryObserverIPHelper(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg); /** * Receive data from the underlying system. * * @param buffer the buffer with the received data. */ virtual void receive_data(const vector& buffer); virtual int notify_table_id_change(uint32_t new_tbl) { UNUSED(new_tbl); return 0; } }; #endif // __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_ENTRY_OBSERVER_IPHELPER_HH__ xorp/fea/data_plane/fibconfig/fibconfig_table_get_click.cc0000664000076400007640000001123211540225523024037 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include #ifdef XORP_USE_CLICK #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvxnet.hh" #include "fea/fibconfig.hh" #include "fea/fea_data_plane_manager.hh" #include "fibconfig_entry_set_click.hh" #include "fibconfig_table_get_click.hh" // // Get the whole table information from the unicast forwarding table. // // The mechanism to obtain the information is Click: // http://www.read.cs.ucla.edu/click/ // FibConfigTableGetClick::FibConfigTableGetClick(FeaDataPlaneManager& fea_data_plane_manager) : FibConfigTableGet(fea_data_plane_manager), ClickSocket(fea_data_plane_manager.eventloop()), _cs_reader(*(ClickSocket *)this) { } FibConfigTableGetClick::~FibConfigTableGetClick() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the Click mechanism to get " "whole forwarding table from the underlying " "system: %s", error_msg.c_str()); } } int FibConfigTableGetClick::start(string& error_msg) { if (! ClickSocket::is_enabled()) return (XORP_OK); if (_is_running) return (XORP_OK); if (ClickSocket::start(error_msg) != XORP_OK) return (XORP_ERROR); _is_running = true; return (XORP_OK); } int FibConfigTableGetClick::stop(string& error_msg) { int ret_value = XORP_OK; if (! _is_running) return (XORP_OK); ret_value = ClickSocket::stop(error_msg); _is_running = false; return (ret_value); } int FibConfigTableGetClick::get_table4(list& fte_list) { // // XXX: Get the table from the FibConfigEntrySetClick instance. // The reason for that is because it is practically // impossible to read the Click configuration and parse it to restore // the original table. // FibConfigEntrySet* fibconfig_entry_set = fea_data_plane_manager().fibconfig_entry_set(); if ((fibconfig_entry_set == NULL) || (! fibconfig_entry_set->is_running())) return (XORP_ERROR); FibConfigEntrySetClick* fibconfig_entry_set_click; fibconfig_entry_set_click = dynamic_cast(fibconfig_entry_set); if (fibconfig_entry_set_click == NULL) { // // XXX: The FibConfigEntrySet plugin was probably changed to // something else which we don't know how to deal with. // return (XORP_ERROR); } const map& fte_table4 = fibconfig_entry_set_click->fte_table4(); map::const_iterator iter; for (iter = fte_table4.begin(); iter != fte_table4.end(); ++iter) { fte_list.push_back(iter->second); } return (XORP_OK); } int FibConfigTableGetClick::get_table6(list& fte_list) { #ifndef HAVE_IPV6 UNUSED(fte_list); return (XORP_ERROR); #else // // XXX: Get the table from the FibConfigEntrySetClick instance. // The reason for that is because it is practically // impossible to read the Click configuration and parse it to restore // the original table. // FibConfigEntrySet* fibconfig_entry_set = fea_data_plane_manager().fibconfig_entry_set(); if ((fibconfig_entry_set == NULL) || (! fibconfig_entry_set->is_running())) return (XORP_ERROR); FibConfigEntrySetClick* fibconfig_entry_set_click; fibconfig_entry_set_click = dynamic_cast(fibconfig_entry_set); if (fibconfig_entry_set_click == NULL) { // // XXX: The FibConfigEntrySet plugin was probably changed to // something else which we don't know how to deal with. // return (XORP_ERROR); } const map& fte_table6 = fibconfig_entry_set_click->fte_table6(); map::const_iterator iter; for (iter = fte_table6.begin(); iter != fte_table6.end(); ++iter) { fte_list.push_back(iter->second); } return (XORP_OK); #endif // HAVE_IPV6 } #endif // click xorp/fea/data_plane/fibconfig/fibconfig_table_set_routing_socket.hh0000664000076400007640000000665411540225523026053 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/data_plane/fibconfig/fibconfig_table_set_routing_socket.hh,v 1.9 2008/10/02 21:57:01 bms Exp $ #ifndef __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_TABLE_SET_ROUTING_SOCKET_HH__ #define __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_TABLE_SET_ROUTING_SOCKET_HH__ #include #ifdef HAVE_ROUTING_SOCKETS #include "fea/fibconfig_table_set.hh" #include "fea/data_plane/control_socket/routing_socket.hh" class FibConfigTableSetRoutingSocket : public FibConfigTableSet { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ FibConfigTableSetRoutingSocket(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~FibConfigTableSetRoutingSocket(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg); /** * Set the IPv4 unicast forwarding table. * * @param fte_list the list with all entries to install into * the IPv4 unicast forwarding table. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int set_table4(const list& fte_list); /** * Delete all entries in the IPv4 unicast forwarding table. * * Must be within a configuration interval. * * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int delete_all_entries4(); /** * Set the IPv6 unicast forwarding table. * * @param fte_list the list with all entries to install into * the IPv6 unicast forwarding table. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int set_table6(const list& fte_list); /** * Delete all entries in the IPv6 unicast forwarding table. * * Must be within a configuration interval. * * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int delete_all_entries6(); /** Routing table ID that we are interested in might have changed. */ virtual int notify_table_id_change(uint32_t new_tbl) { // Virtual routing tables not implemented on BSD UNUSED(new_tbl); return XORP_OK; } private: }; #endif #endif // __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_TABLE_SET_ROUTING_SOCKET_HH__ xorp/fea/data_plane/fibconfig/fibconfig_table_set_rtmv2.cc0000664000076400007640000001026411540224222024037 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "fea/fibconfig.hh" #include "fibconfig_table_set_rtmv2.hh" // // Set whole-table information into the unicast forwarding table. // // The mechanism to set the information is Router Manager V2. // #ifdef HOST_OS_WINDOWS FibConfigTableSetRtmV2::FibConfigTableSetRtmV2(FeaDataPlaneManager& fea_data_plane_manager) : FibConfigTableSet(fea_data_plane_manager) { } FibConfigTableSetRtmV2::~FibConfigTableSetRtmV2() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the Router Manager V2 mechanism to set " "whole forwarding table from the underlying " "system: %s", error_msg.c_str()); } } int FibConfigTableSetRtmV2::start(string& error_msg) { UNUSED(error_msg); if (_is_running) return (XORP_OK); // Cleanup any leftover entries from previously run XORP instance if (! fibconfig().unicast_forwarding_entries_retain_on_startup4()) delete_all_entries4(); if (! fibconfig().unicast_forwarding_entries_retain_on_startup6()) delete_all_entries6(); // // XXX: This mechanism relies on the FibConfigEntrySet mechanism // to set the forwarding table, hence there is nothing else to do. // _is_running = true; return (XORP_OK); } int FibConfigTableSetRtmV2::stop(string& error_msg) { UNUSED(error_msg); if (! _is_running) return (XORP_OK); // Delete the XORP entries if (! fibconfig().unicast_forwarding_entries_retain_on_shutdown4()) delete_all_entries4(); if (! fibconfig().unicast_forwarding_entries_retain_on_shutdown6()) delete_all_entries6(); // // XXX: This mechanism relies on the FibConfigEntrySet mechanism // to set the forwarding table, hence there is nothing else to do. // _is_running = false; return (XORP_OK); } int FibConfigTableSetRtmV2::set_table4(const list& fte_list) { list::const_iterator iter; // Add the entries one-by-one for (iter = fte_list.begin(); iter != fte_list.end(); ++iter) { const Fte4& fte = *iter; fibconfig().add_entry4(fte); } return (XORP_OK); } int FibConfigTableSetRtmV2::delete_all_entries4() { list fte_list; list::const_iterator iter; // Get the list of all entries fibconfig().get_table4(fte_list); // Delete the entries one-by-one for (iter = fte_list.begin(); iter != fte_list.end(); ++iter) { const Fte4& fte = *iter; if (fte.xorp_route()) fibconfig().delete_entry4(fte); } return (XORP_OK); } int FibConfigTableSetRtmV2::set_table6(const list& fte_list) { list::const_iterator iter; // Add the entries one-by-one for (iter = fte_list.begin(); iter != fte_list.end(); ++iter) { const Fte6& fte = *iter; fibconfig().add_entry6(fte); } return (XORP_OK); } int FibConfigTableSetRtmV2::delete_all_entries6() { list fte_list; list::const_iterator iter; // Get the list of all entries fibconfig().get_table6(fte_list); // Delete the entries one-by-one for (iter = fte_list.begin(); iter != fte_list.end(); ++iter) { const Fte6& fte = *iter; if (fte.xorp_route()) fibconfig().delete_entry6(fte); } return (XORP_OK); } #endif // HOST_OS_WINDOWS xorp/fea/data_plane/fibconfig/fibconfig_table_observer_iphelper.hh0000664000076400007640000000433211540224222025642 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_TABLE_OBSERVER_IPHELPER_HH__ #define __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_TABLE_OBSERVER_IPHELPER_HH__ #include "fea/fibconfig_table_observer.hh" class FibConfigTableObserverIPHelper : public FibConfigTableObserver { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ FibConfigTableObserverIPHelper(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~FibConfigTableObserverIPHelper(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg); /** * Receive data from the underlying system. * * @param buffer the buffer with the received data. */ virtual void receive_data(const vector& buffer); virtual int notify_table_id_change(uint32_t new_tbl) { UNUSED(new_tbl); return 0; } private: }; #endif // __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_TABLE_OBSERVER_IPHELPER_HH__ xorp/fea/data_plane/fibconfig/fibconfig_entry_set_dummy.hh0000664000076400007640000000640611540225523024214 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/data_plane/fibconfig/fibconfig_entry_set_dummy.hh,v 1.9 2008/10/02 21:56:57 bms Exp $ #ifndef __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_ENTRY_SET_DUMMY_HH__ #define __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_ENTRY_SET_DUMMY_HH__ #include "fea/fibconfig_entry_set.hh" class FibConfigEntrySetDummy : public FibConfigEntrySet { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ FibConfigEntrySetDummy(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~FibConfigEntrySetDummy(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg); /** * Add a single IPv4 forwarding entry. * * Must be within a configuration interval. * * @param fte the entry to add. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int add_entry4(const Fte4& fte); /** * Delete a single IPv4 forwarding entry. * * Must be with a configuration interval. * * @param fte the entry to delete. Only destination and netmask are used. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int delete_entry4(const Fte4& fte); /** * Add a single IPv6 forwarding entry. * * Must be within a configuration interval. * * @param fte the entry to add. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int add_entry6(const Fte6& fte); /** * Delete a single IPv6 forwarding entry. * * Must be within a configuration interval. * * @param fte the entry to delete. Only destination and netmask are used. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int delete_entry6(const Fte6& fte); /** Routing table ID that we are interested in might have changed. */ virtual int notify_table_id_change(uint32_t new_tbl) { UNUSED(new_tbl); return XORP_OK; } private: }; #endif // __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_ENTRY_SET_DUMMY_HH__ xorp/fea/data_plane/fibconfig/fibconfig_table_set_netlink_socket.cc0000664000076400007640000001046211540225523026006 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include #ifdef HAVE_NETLINK_SOCKETS #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "fea/fibconfig.hh" #include "fibconfig_table_set_netlink_socket.hh" // // Set whole-table information into the unicast forwarding table. // // The mechanism to set the information is netlink(7) sockets. // FibConfigTableSetNetlinkSocket::FibConfigTableSetNetlinkSocket(FeaDataPlaneManager& fea_data_plane_manager) : FibConfigTableSet(fea_data_plane_manager) { } FibConfigTableSetNetlinkSocket::~FibConfigTableSetNetlinkSocket() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the netlink(7) sockets mechanism to set " "whole forwarding table from the underlying " "system: %s", error_msg.c_str()); } } int FibConfigTableSetNetlinkSocket::start(string& error_msg) { UNUSED(error_msg); if (_is_running) return (XORP_OK); // Cleanup any leftover entries from previously run XORP instance if (! fibconfig().unicast_forwarding_entries_retain_on_startup4()) delete_all_entries4(); if (! fibconfig().unicast_forwarding_entries_retain_on_startup6()) delete_all_entries6(); // // XXX: This mechanism relies on the FibConfigEntrySet mechanism // to set the forwarding table, hence there is nothing else to do. // _is_running = true; return (XORP_OK); } int FibConfigTableSetNetlinkSocket::stop(string& error_msg) { UNUSED(error_msg); if (! _is_running) return (XORP_OK); // Delete the XORP entries if (! fibconfig().unicast_forwarding_entries_retain_on_shutdown4()) delete_all_entries4(); if (! fibconfig().unicast_forwarding_entries_retain_on_shutdown6()) delete_all_entries6(); // // XXX: This mechanism relies on the FibConfigEntrySet mechanism // to set the forwarding table, hence there is nothing else to do. // _is_running = false; return (XORP_OK); } int FibConfigTableSetNetlinkSocket::set_table4(const list& fte_list) { list::const_iterator iter; // Add the entries one-by-one for (iter = fte_list.begin(); iter != fte_list.end(); ++iter) { const Fte4& fte = *iter; fibconfig().add_entry4(fte); } return (XORP_OK); } int FibConfigTableSetNetlinkSocket::delete_all_entries4() { list fte_list; list::const_iterator iter; // Get the list of all entries fibconfig().get_table4(fte_list); // Delete the entries one-by-one for (iter = fte_list.begin(); iter != fte_list.end(); ++iter) { const Fte4& fte = *iter; if (fte.xorp_route()) fibconfig().delete_entry4(fte); } return (XORP_OK); } int FibConfigTableSetNetlinkSocket::set_table6(const list& fte_list) { list::const_iterator iter; // Add the entries one-by-one for (iter = fte_list.begin(); iter != fte_list.end(); ++iter) { const Fte6& fte = *iter; fibconfig().add_entry6(fte); } return (XORP_OK); } int FibConfigTableSetNetlinkSocket::delete_all_entries6() { list fte_list; list::const_iterator iter; // Get the list of all entries fibconfig().get_table6(fte_list); // Delete the entries one-by-one for (iter = fte_list.begin(); iter != fte_list.end(); ++iter) { const Fte6& fte = *iter; if (fte.xorp_route()) fibconfig().delete_entry6(fte); } return (XORP_OK); } #endif // HAVE_NETLINK_SOCKETS xorp/fea/data_plane/fibconfig/fibconfig_table_set_iphelper.cc0000664000076400007640000001106711540224222024577 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/win_io.h" #include "fea/fibconfig.hh" #ifdef HOST_OS_WINDOWS #include "fea/data_plane/control_socket/windows_rras_support.hh" #endif #include "fibconfig_table_set_iphelper.hh" // // Set whole-table information into the unicast forwarding table. // // The mechanism to set the information is the IP Helper API for // Windows (IPHLPAPI.DLL). // #ifdef HOST_OS_WINDOWS FibConfigTableSetIPHelper::FibConfigTableSetIPHelper(FeaDataPlaneManager& fea_data_plane_manager) : FibConfigTableSet(fea_data_plane_manager) { if (WinSupport::is_rras_running()) { XLOG_WARNING("Windows Routing and Remote Access Service is running.\n" "Some change operations through IP Helper may not work."); } } FibConfigTableSetIPHelper::~FibConfigTableSetIPHelper() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the IP Helper mechanism to set " "whole forwarding table from the underlying " "system: %s", error_msg.c_str()); } } int FibConfigTableSetIPHelper::start(string& error_msg) { UNUSED(error_msg); if (_is_running) return (XORP_OK); // Cleanup any leftover entries from previously run XORP instance if (! fibconfig().unicast_forwarding_entries_retain_on_startup4()) delete_all_entries4(); if (! fibconfig().unicast_forwarding_entries_retain_on_startup6()) delete_all_entries6(); // // XXX: This mechanism relies on the FibConfigEntrySet mechanism // to set the forwarding table, hence there is nothing else to do. // _is_running = true; return (XORP_OK); } int FibConfigTableSetIPHelper::stop(string& error_msg) { UNUSED(error_msg); if (! _is_running) return (XORP_OK); // Delete the XORP entries if (! fibconfig().unicast_forwarding_entries_retain_on_shutdown4()) delete_all_entries4(); if (! fibconfig().unicast_forwarding_entries_retain_on_shutdown6()) delete_all_entries6(); // // XXX: This mechanism relies on the FibConfigEntrySet mechanism // to set the forwarding table, hence there is nothing else to do. // _is_running = false; return (XORP_OK); } int FibConfigTableSetIPHelper::set_table4(const list& fte_list) { list::const_iterator iter; // Add the entries one-by-one for (iter = fte_list.begin(); iter != fte_list.end(); ++iter) { const Fte4& fte = *iter; fibconfig().add_entry4(fte); } return (XORP_OK); } int FibConfigTableSetIPHelper::delete_all_entries4() { list fte_list; list::const_iterator iter; // Get the list of all entries fibconfig().get_table4(fte_list); // Delete the entries one-by-one for (iter = fte_list.begin(); iter != fte_list.end(); ++iter) { const Fte4& fte = *iter; if (fte.xorp_route()) fibconfig().delete_entry4(fte); } return (XORP_OK); } int FibConfigTableSetIPHelper::set_table6(const list& fte_list) { list::const_iterator iter; // Add the entries one-by-one for (iter = fte_list.begin(); iter != fte_list.end(); ++iter) { const Fte6& fte = *iter; fibconfig().add_entry6(fte); } return (XORP_OK); } int FibConfigTableSetIPHelper::delete_all_entries6() { list fte_list; list::const_iterator iter; // Get the list of all entries fibconfig().get_table6(fte_list); // Delete the entries one-by-one for (iter = fte_list.begin(); iter != fte_list.end(); ++iter) { const Fte6& fte = *iter; if (fte.xorp_route()) fibconfig().delete_entry6(fte); } return (XORP_OK); } #endif // HOST_OS_WINDOWS xorp/fea/data_plane/fibconfig/fibconfig_table_observer_netlink_socket.cc0000664000076400007640000001041111540225523027034 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include #ifdef HAVE_NETLINK_SOCKETS #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #ifdef HAVE_LINUX_TYPES_H #include #endif #ifdef HAVE_LINUX_RTNETLINK_H #include #endif #include "fea/fibconfig.hh" #include "fibconfig_table_get_netlink_socket.hh" #include "fibconfig_table_observer_netlink_socket.hh" // // Observe whole-table information change about the unicast forwarding table. // // E.g., if the forwarding table has changed, then the information // received by the observer would NOT specify the particular entry that // has changed. // // The mechanism to observe the information is netlink(7) sockets. // FibConfigTableObserverNetlinkSocket::FibConfigTableObserverNetlinkSocket(FeaDataPlaneManager& fea_data_plane_manager) : FibConfigTableObserver(fea_data_plane_manager), NetlinkSocket(fea_data_plane_manager.eventloop(), fea_data_plane_manager.fibconfig().get_netlink_filter_table_id()), NetlinkSocketObserver(*(NetlinkSocket *)this) { } FibConfigTableObserverNetlinkSocket::~FibConfigTableObserverNetlinkSocket() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the netlink(7) sockets mechanism to observe " "whole forwarding table from the underlying " "system: %s", error_msg.c_str()); } } int FibConfigTableObserverNetlinkSocket::start(string& error_msg) { uint32_t nl_groups = 0; if (_is_running) return (XORP_OK); // // Listen to the netlink multicast group for IPv4 forwarding entries // if (fea_data_plane_manager().have_ipv4()) nl_groups |= RTMGRP_IPV4_ROUTE; #ifdef HAVE_IPV6 // // Listen to the netlink multicast group for IPv6 forwarding entries // if (fea_data_plane_manager().have_ipv6()) nl_groups |= RTMGRP_IPV6_ROUTE; #endif // HAVE_IPV6 // // Set the netlink multicast groups to listen for on the netlink socket // NetlinkSocket::set_nl_groups(nl_groups); if (NetlinkSocket::start(error_msg) != XORP_OK) return (XORP_ERROR); _is_running = true; return (XORP_OK); } int FibConfigTableObserverNetlinkSocket::stop(string& error_msg) { if (! _is_running) return (XORP_OK); if (NetlinkSocket::stop(error_msg) != XORP_OK) return (XORP_ERROR); _is_running = false; return (XORP_OK); } void FibConfigTableObserverNetlinkSocket::receive_data(const vector& buffer) { list fte_list; // // Get the IPv4 routes // if (fea_data_plane_manager().have_ipv4()) { FibConfigTableGetNetlinkSocket::parse_buffer_netlink_socket( AF_INET, fibconfig().system_config_iftree(), fte_list, buffer, false, fibconfig()); if (! fte_list.empty()) { fibconfig().propagate_fib_changes(fte_list, this); fte_list.clear(); } } #ifdef HAVE_IPV6 // // Get the IPv6 routes // if (fea_data_plane_manager().have_ipv6()) { FibConfigTableGetNetlinkSocket::parse_buffer_netlink_socket( AF_INET6, fibconfig().system_config_iftree(), fte_list, buffer, false, fibconfig()); if (! fte_list.empty()) { fibconfig().propagate_fib_changes(fte_list, this); fte_list.clear(); } } #endif // HAVE_IPV6 } void FibConfigTableObserverNetlinkSocket::netlink_socket_data(const vector& buffer) { receive_data(buffer); } #endif // HAVE_NETLINK_SOCKETS xorp/fea/data_plane/fibconfig/fibconfig_forwarding_dummy.cc0000664000076400007640000000517411421137511024326 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "fea/fibconfig.hh" #include "fibconfig_forwarding_dummy.hh" // // Configure unicast forwarding. // // The mechanism to get/set the information is Dummy (for testing purpose). // FibConfigForwardingDummy::FibConfigForwardingDummy( FeaDataPlaneManager& fea_data_plane_manager) : FibConfigForwarding(fea_data_plane_manager), _unicast_forwarding_enabled4(false), _unicast_forwarding_enabled6(false), _accept_rtadv_enabled6(false) { } FibConfigForwardingDummy::~FibConfigForwardingDummy() { } int FibConfigForwardingDummy::unicast_forwarding_enabled4(bool& ret_value, string& error_msg) const { UNUSED(error_msg); ret_value = _unicast_forwarding_enabled4; return (XORP_OK); } int FibConfigForwardingDummy::unicast_forwarding_enabled6(bool& ret_value, string& error_msg) const { UNUSED(error_msg); ret_value = _unicast_forwarding_enabled6; return (XORP_OK); } int FibConfigForwardingDummy::accept_rtadv_enabled6(bool& ret_value, string& error_msg) const { UNUSED(error_msg); ret_value = _accept_rtadv_enabled6; return (XORP_OK); } int FibConfigForwardingDummy::set_unicast_forwarding_enabled4(bool v, string& error_msg) { UNUSED(error_msg); _unicast_forwarding_enabled4 = v; return (XORP_OK); } int FibConfigForwardingDummy::set_unicast_forwarding_enabled6(bool v, string& error_msg) { UNUSED(error_msg); _unicast_forwarding_enabled6 = v; return (XORP_OK); } int FibConfigForwardingDummy::set_accept_rtadv_enabled6(bool v, string& error_msg) { UNUSED(error_msg); _accept_rtadv_enabled6 = v; return (XORP_OK); } xorp/fea/data_plane/fibconfig/fibconfig_table_observer_routing_socket.cc0000664000076400007640000000733611540225523027073 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include #ifdef HAVE_ROUTING_SOCKETS #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "fea/fibconfig.hh" #include "fibconfig_table_get_sysctl.hh" #include "fibconfig_table_observer_routing_socket.hh" // // Observe whole-table information change about the unicast forwarding table. // // E.g., if the forwarding table has changed, then the information // received by the observer would NOT specify the particular entry that // has changed. // // The mechanism to observe the information is routing sockets. // FibConfigTableObserverRoutingSocket::FibConfigTableObserverRoutingSocket(FeaDataPlaneManager& fea_data_plane_manager) : FibConfigTableObserver(fea_data_plane_manager), RoutingSocket(fea_data_plane_manager.eventloop()), RoutingSocketObserver(*(RoutingSocket *)this) { } FibConfigTableObserverRoutingSocket::~FibConfigTableObserverRoutingSocket() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the routing sockets mechanism to observe " "whole forwarding table from the underlying " "system: %s", error_msg.c_str()); } } int FibConfigTableObserverRoutingSocket::start(string& error_msg) { if (_is_running) return (XORP_OK); if (RoutingSocket::start(error_msg) != XORP_OK) return (XORP_ERROR); _is_running = true; return (XORP_OK); } int FibConfigTableObserverRoutingSocket::stop(string& error_msg) { if (! _is_running) return (XORP_OK); if (RoutingSocket::stop(error_msg) != XORP_OK) return (XORP_ERROR); _is_running = false; return (XORP_OK); } void FibConfigTableObserverRoutingSocket::receive_data(const vector& buffer) { list fte_list; FibConfigTableGetSysctl::FibMsgSet filter; filter = FibConfigTableGetSysctl::FibMsg::UPDATES | FibConfigTableGetSysctl::FibMsg::GETS | FibConfigTableGetSysctl::FibMsg::RESOLVES; // // Get the IPv4 routes // if (fea_data_plane_manager().have_ipv4()) { FibConfigTableGetSysctl::parse_buffer_routing_socket(AF_INET, fibconfig().system_config_iftree(), fte_list, buffer, filter); if (! fte_list.empty()) { fibconfig().propagate_fib_changes(fte_list, this); fte_list.clear(); } } #ifdef HAVE_IPV6 // // Get the IPv6 routes // if (fea_data_plane_manager().have_ipv6()) { FibConfigTableGetSysctl::parse_buffer_routing_socket(AF_INET6, fibconfig().system_config_iftree(), fte_list, buffer, filter); if (! fte_list.empty()) { fibconfig().propagate_fib_changes(fte_list, this); fte_list.clear(); } } #endif // HAVE_IPV6 } void FibConfigTableObserverRoutingSocket::routing_socket_data(const vector& buffer) { receive_data(buffer); } #endif // HAVE_ROUTING_SOCKETS xorp/fea/data_plane/fibconfig/fibconfig_entry_get_netlink_socket.hh0000664000076400007640000001123711540225522026056 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/data_plane/fibconfig/fibconfig_entry_get_netlink_socket.hh,v 1.9 2008/10/02 21:56:55 bms Exp $ #ifndef __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_ENTRY_GET_NETLINK_SOCKET_HH__ #define __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_ENTRY_GET_NETLINK_SOCKET_HH__ #include #ifdef HAVE_NETLINK_SOCKETS #include "fea/fibconfig_entry_get.hh" #include "fea/data_plane/control_socket/netlink_socket.hh" class FibConfigEntryGetNetlinkSocket : public FibConfigEntryGet, public NetlinkSocket { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ FibConfigEntryGetNetlinkSocket(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~FibConfigEntryGetNetlinkSocket(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg); /** * Lookup an IPv4 route by destination address. * * @param dst host address to resolve. * @param fte return-by-reference forwarding table entry. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int lookup_route_by_dest4(const IPv4& dst, Fte4& fte); /** * Lookup an IPv4 route by network address. * * @param dst network address to resolve. * @param fte return-by-reference forwarding table entry. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int lookup_route_by_network4(const IPv4Net& dst, Fte4& fte); /** * Lookup an IPv6 route by destination address. * * @param dst host address to resolve. * @param fte return-by-reference forwarding table entry. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int lookup_route_by_dest6(const IPv6& dst, Fte6& fte); /** * Lookup an IPv6 route by network address. * * @param dst network address to resolve. * @param fte return-by-reference forwarding table entry. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int lookup_route_by_network6(const IPv6Net& dst, Fte6& fte); /** * Parse information about forwarding entry information received from * the underlying system. * * The information to parse is in NETLINK format * (e.g., obtained by netlink(7) sockets mechanism). * * @param iftree the interface tree to use. * @param fte the Fte storage to store the parsed information. * @param buffer the buffer with the data to parse. * @param is_nlm_get_only if true, consider only the entries obtained * by RTM_GETROUTE. * @return XORP_OK on success, otherwise XORP_ERROR. * @see FteX. */ static int parse_buffer_netlink_socket(const IfTree& iftree, FteX& fte, const vector& buffer, bool is_nlm_get_only, const FibConfig& fibconfig); /** Routing table ID that we are interested in might have changed. */ virtual int notify_table_id_change(uint32_t new_tbl) { return NetlinkSocket::notify_table_id_change(new_tbl); } private: /** * Lookup a route by destination address. * * @param dst host address to resolve. * @param fte return-by-reference forwarding table entry. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int lookup_route_by_dest(const IPvX& dst, FteX& fte); NetlinkSocketReader _ns_reader; }; #endif #endif // __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_ENTRY_GET_NETLINK_SOCKET_HH__ xorp/fea/data_plane/fibconfig/fibconfig_table_observer_routing_socket.hh0000664000076400007640000000520511540225523027076 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_TABLE_OBSERVER_ROUTING_SOCKET_HH__ #define __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_TABLE_OBSERVER_ROUTING_SOCKET_HH__ #include #ifdef HAVE_ROUTING_SOCKETS #include "fea/fibconfig_table_observer.hh" #include "fea/data_plane/control_socket/routing_socket.hh" class FibConfigTableObserverRoutingSocket : public FibConfigTableObserver, public RoutingSocket, public RoutingSocketObserver { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ FibConfigTableObserverRoutingSocket(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~FibConfigTableObserverRoutingSocket(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg); /** * Receive data from the underlying system. * * @param buffer the buffer with the received data. */ virtual void receive_data(const vector& buffer); void routing_socket_data(const vector& buffer); /** Routing table ID that we are interested in might have changed. */ virtual int notify_table_id_change(uint32_t new_tbl) { // Virtual routing tables not implemented on BSD UNUSED(new_tbl); return XORP_OK; } private: }; #endif #endif // __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_TABLE_OBSERVER_ROUTING_SOCKET_HH__ xorp/fea/data_plane/fibconfig/fibconfig_entry_set_netlink_socket.hh0000664000076400007640000000703711540225523026076 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/data_plane/fibconfig/fibconfig_entry_set_netlink_socket.hh,v 1.9 2008/10/02 21:56:57 bms Exp $ #ifndef __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_ENTRY_SET_NETLINK_SOCKET_HH__ #define __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_ENTRY_SET_NETLINK_SOCKET_HH__ #include #ifdef HAVE_NETLINK_SOCKETS #include "fea/fibconfig_entry_set.hh" #include "fea/data_plane/control_socket/netlink_socket.hh" class FibConfigEntrySetNetlinkSocket : public FibConfigEntrySet, public NetlinkSocket { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ FibConfigEntrySetNetlinkSocket(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~FibConfigEntrySetNetlinkSocket(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg); /** * Add a single IPv4 forwarding entry. * * Must be within a configuration interval. * * @param fte the entry to add. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int add_entry4(const Fte4& fte); /** * Delete a single IPv4 forwarding entry. * * Must be with a configuration interval. * * @param fte the entry to delete. Only destination and netmask are used. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int delete_entry4(const Fte4& fte); /** * Add a single IPv6 forwarding entry. * * Must be within a configuration interval. * * @param fte the entry to add. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int add_entry6(const Fte6& fte); /** * Delete a single IPv6 forwarding entry. * * Must be within a configuration interval. * * @param fte the entry to delete. Only destination and netmask are used. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int delete_entry6(const Fte6& fte); /** Routing table ID that we are interested in might have changed. */ virtual int notify_table_id_change(uint32_t new_tbl); private: int add_entry(const FteX& fte); int delete_entry(const FteX& fte); NetlinkSocketReader _ns_reader; }; #endif #endif // __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_ENTRY_SET_NETLINK_SOCKET_HH__ xorp/fea/data_plane/fibconfig/fibconfig_table_set_dummy.cc0000664000076400007640000000560711421137511024127 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "fea/fibconfig.hh" #include "fibconfig_table_set_dummy.hh" // // Set whole-table information into the unicast forwarding table. // // The mechanism to set the information is Dummy (for testing purpose). // FibConfigTableSetDummy::FibConfigTableSetDummy(FeaDataPlaneManager& fea_data_plane_manager) : FibConfigTableSet(fea_data_plane_manager) { } FibConfigTableSetDummy::~FibConfigTableSetDummy() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the Dummy mechanism to set " "whole forwarding table from the underlying " "system: %s", error_msg.c_str()); } } int FibConfigTableSetDummy::start(string& error_msg) { UNUSED(error_msg); if (_is_running) return (XORP_OK); _is_running = true; return (XORP_OK); } int FibConfigTableSetDummy::stop(string& error_msg) { UNUSED(error_msg); if (! _is_running) return (XORP_OK); _is_running = false; return (XORP_OK); } int FibConfigTableSetDummy::set_table4(const list& fte_list) { list::const_iterator iter; // Add the entries one-by-one for (iter = fte_list.begin(); iter != fte_list.end(); ++iter) { const Fte4& fte = *iter; fibconfig().add_entry4(fte); } return (XORP_OK); } int FibConfigTableSetDummy::delete_all_entries4() { if (in_configuration() == false) return (XORP_ERROR); fibconfig().trie4().delete_all_nodes(); return (XORP_OK); } int FibConfigTableSetDummy::set_table6(const list& fte_list) { list::const_iterator iter; // Add the entries one-by-one for (iter = fte_list.begin(); iter != fte_list.end(); ++iter) { const Fte6& fte = *iter; fibconfig().add_entry6(fte); } return (XORP_OK); } int FibConfigTableSetDummy::delete_all_entries6() { if (in_configuration() == false) return (XORP_ERROR); fibconfig().trie6().delete_all_nodes(); return (XORP_OK); } xorp/fea/data_plane/fibconfig/fibconfig_table_set_routing_socket.cc0000664000076400007640000001045511540225523026033 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include #ifdef HAVE_ROUTING_SOCKETS #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "fea/fibconfig.hh" #include "fibconfig_table_set_routing_socket.hh" // // Set whole-table information into the unicast forwarding table. // // The mechanism to set the information is routing sockets. // FibConfigTableSetRoutingSocket::FibConfigTableSetRoutingSocket(FeaDataPlaneManager& fea_data_plane_manager) : FibConfigTableSet(fea_data_plane_manager) { } FibConfigTableSetRoutingSocket::~FibConfigTableSetRoutingSocket() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the routing sockets mechanism to set " "whole forwarding table from the underlying " "system: %s", error_msg.c_str()); } } int FibConfigTableSetRoutingSocket::start(string& error_msg) { UNUSED(error_msg); if (_is_running) return (XORP_OK); // Cleanup any leftover entries from previously run XORP instance if (! fibconfig().unicast_forwarding_entries_retain_on_startup4()) delete_all_entries4(); if (! fibconfig().unicast_forwarding_entries_retain_on_startup6()) delete_all_entries6(); // // XXX: This mechanism relies on the FibConfigEntrySet mechanism // to set the forwarding table, hence there is nothing else to do. // _is_running = true; return (XORP_OK); } int FibConfigTableSetRoutingSocket::stop(string& error_msg) { UNUSED(error_msg); if (! _is_running) return (XORP_OK); // Delete the XORP entries if (! fibconfig().unicast_forwarding_entries_retain_on_shutdown4()) delete_all_entries4(); if (! fibconfig().unicast_forwarding_entries_retain_on_shutdown6()) delete_all_entries6(); // // XXX: This mechanism relies on the FibConfigEntrySet mechanism // to set the forwarding table, hence there is nothing else to do. // _is_running = false; return (XORP_OK); } int FibConfigTableSetRoutingSocket::set_table4(const list& fte_list) { list::const_iterator iter; // Add the entries one-by-one for (iter = fte_list.begin(); iter != fte_list.end(); ++iter) { const Fte4& fte = *iter; fibconfig().add_entry4(fte); } return (XORP_OK); } int FibConfigTableSetRoutingSocket::delete_all_entries4() { list fte_list; list::const_iterator iter; // Get the list of all entries fibconfig().get_table4(fte_list); // Delete the entries one-by-one for (iter = fte_list.begin(); iter != fte_list.end(); ++iter) { const Fte4& fte = *iter; if (fte.xorp_route()) fibconfig().delete_entry4(fte); } return (XORP_OK); } int FibConfigTableSetRoutingSocket::set_table6(const list& fte_list) { list::const_iterator iter; // Add the entries one-by-one for (iter = fte_list.begin(); iter != fte_list.end(); ++iter) { const Fte6& fte = *iter; fibconfig().add_entry6(fte); } return (XORP_OK); } int FibConfigTableSetRoutingSocket::delete_all_entries6() { list fte_list; list::const_iterator iter; // Get the list of all entries fibconfig().get_table6(fte_list); // Delete the entries one-by-one for (iter = fte_list.begin(); iter != fte_list.end(); ++iter) { const Fte6& fte = *iter; if (fte.xorp_route()) fibconfig().delete_entry6(fte); } return (XORP_OK); } #endif // HAVE_ROUTING_SOCKETS xorp/fea/data_plane/fibconfig/fibconfig_table_get_iphelper.hh0000664000076400007640000000516011540224222024572 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_TABLE_GET_IPHELPER_HH__ #define __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_TABLE_GET_IPHELPER_HH__ #include "fea/fibconfig_table_get.hh" class FibConfigTableGetIPHelper : public FibConfigTableGet { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ FibConfigTableGetIPHelper(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~FibConfigTableGetIPHelper(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg); /** * Obtain the IPv4 unicast forwarding table. * * @param fte_list the return-by-reference list with all entries in * the IPv4 unicast forwarding table. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int get_table4(list& fte_list); /** * Obtain the IPv6 unicast forwarding table. * * @param fte_list the return-by-reference list with all entries in * the IPv6 unicast forwarding table. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int get_table6(list& fte_list); virtual int notify_table_id_change(uint32_t new_tbl) { UNUSED(new_tbl); return 0; } private: int get_table(int family, list& fte_list); }; #endif // __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_TABLE_GET_IPHELPER_HH__ xorp/fea/data_plane/fibconfig/fibconfig_table_observer_dummy.cc0000664000076400007640000000441411421137511025156 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "fea/fibconfig.hh" #include "fibconfig_table_observer_dummy.hh" // // Observe whole-table information change about the unicast forwarding table. // // E.g., if the forwarding table has changed, then the information // received by the observer would NOT specify the particular entry that // has changed. // // The mechanism to observe the information is Dummy (for testing purpose). // FibConfigTableObserverDummy::FibConfigTableObserverDummy(FeaDataPlaneManager& fea_data_plane_manager) : FibConfigTableObserver(fea_data_plane_manager) { } FibConfigTableObserverDummy::~FibConfigTableObserverDummy() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the Dummy mechanism to observe " "whole forwarding table from the underlying " "system: %s", error_msg.c_str()); } } int FibConfigTableObserverDummy::start(string& error_msg) { UNUSED(error_msg); if (_is_running) return (XORP_OK); _is_running = true; return (XORP_OK); } int FibConfigTableObserverDummy::stop(string& error_msg) { UNUSED(error_msg); if (! _is_running) return (XORP_OK); _is_running = false; return (XORP_OK); } void FibConfigTableObserverDummy::receive_data(const vector& buffer) { // TODO: use it if needed UNUSED(buffer); } xorp/fea/data_plane/fibconfig/fibconfig_entry_get_rtmv2.cc0000664000076400007640000002346111540224221024077 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvxnet.hh" #include "fea/fibconfig.hh" #ifdef HOST_OS_WINDOWS #include "fea/data_plane/control_socket/windows_routing_socket.h" #endif #include "fibconfig_entry_get_rtmv2.hh" // // Get single-entry information from the unicast forwarding table. // // The mechanism to obtain the information is Router Manager V2. // #ifdef HOST_OS_WINDOWS FibConfigEntryGetRtmV2::FibConfigEntryGetRtmV2(FeaDataPlaneManager& fea_data_plane_manager) : FibConfigEntryGet(fea_data_plane_manager) { } FibConfigEntryGetRtmV2::~FibConfigEntryGetRtmV2() { #if 0 string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the Router Manager V2 mechanism to get " "information about forwarding table from the underlying " "system: %s", error_msg.c_str()); } #endif } int FibConfigEntryGetRtmV2::start(string& error_msg) { #if 0 if (_is_running) return (XORP_OK); if (WinRtmPipe::start(error_msg) != XORP_OK) return (XORP_ERROR); _is_running = true; #endif return (XORP_OK); UNUSED(error_msg); } int FibConfigEntryGetRtmV2::stop(string& error_msg) { #if 0 if (! _is_running) return (XORP_OK); if (WinRtmPipe::stop(error_msg) != XORP_OK) return (XORP_ERROR); _is_running = false; #endif return (XORP_OK); UNUSED(error_msg); } int FibConfigEntryGetRtmV2::lookup_route_by_dest4(const IPv4& dst, Fte4& fte) { FteX ftex(dst.af()); int ret_value = XORP_ERROR; ret_value = lookup_route_by_dest(IPvX(dst), ftex); fte = ftex.get_fte4(); return (ret_value); } int FibConfigEntryGetRtmV2::lookup_route_by_network4(const IPv4Net& dst, Fte4& fte) { FteX ftex(dst.af()); int ret_value = XORP_ERROR; ret_value = lookup_route_by_network(IPvXNet(dst), ftex); fte = ftex.get_fte4(); return (ret_value); } int FibConfigEntryGetRtmV2::lookup_route_by_dest6(const IPv6& dst, Fte6& fte) { FteX ftex(dst.af()); int ret_value = XORP_ERROR; ret_value = lookup_route_by_dest(IPvX(dst), ftex); fte = ftex.get_fte6(); return (ret_value); } int FibConfigEntryGetRtmV2::lookup_route_by_network6(const IPv6Net& dst, Fte6& fte) { FteX ftex(dst.af()); int ret_value = XORP_ERROR; ret_value = lookup_route_by_network(IPvXNet(dst), ftex); fte = ftex.get_fte6(); return (ret_value); } int FibConfigEntryGetRtmV2::lookup_route_by_dest(const IPvX& dst, FteX& fte) { #if 1 /* * XXX: The RTM_GET stuff isn't supported on Windows yet. */ UNUSED(dst); UNUSED(fte); return (XORP_ERROR); #else static const size_t buffer_size = sizeof(struct rt_msghdr) + 512; union { uint8_t data[buffer_size]; struct rt_msghdr rtm; } buffer; struct rt_msghdr* rtm = &buffer.rtm; struct sockaddr_in* sin; WinRtmPipe& rs = *this; // Zero the return information fte.zero(); // Check that the family is supported do { if (dst.is_ipv4()) { if (! fea_data_plane_manager().have_ipv4()) return (XORP_ERROR); break; } if (dst.is_ipv6()) { if (! fea_data_plane_manager().have_ipv6()) return (XORP_ERROR); break; } break; } while (false); // Check that the destination address is valid if (! dst.is_unicast()) { return (XORP_ERROR); } // // Set the request // memset(&buffer, 0, sizeof(buffer)); switch (dst.af()) { case AF_INET: rtm->rtm_msglen = sizeof(*rtm) + sizeof(struct sockaddr_in); break; #ifdef HAVE_IPV6 case AF_INET6: rtm->rtm_msglen = sizeof(*rtm) + sizeof(struct sockaddr_in6); break; #endif // HAVE_IPV6 default: XLOG_UNREACHABLE(); break; } rtm->rtm_version = RTM_VERSION; rtm->rtm_type = RTM_GET; rtm->rtm_addrs = (RTA_DST | RTA_IFP); rtm->rtm_flags = RTF_UP; rtm->rtm_pid = rs.pid(); rtm->rtm_seq = rs.seqno(); // Copy the destination address sin = reinterpret_cast(rtm + 1); dst.copy_out(*sin); // // Add extra space for sockaddr_dl that corresponds to the RTA_IFP flag. // Required if the OS is very strict in the arguments checking // (e.g., NetBSD). // #ifdef AF_LINK do { // Set the data-link socket struct sockaddr_dl* sdl; rtm->rtm_msglen += sizeof(struct sockaddr_dl); switch (dst.af()) { case AF_INET: sdl = ADD_POINTER(sin, sizeof(struct sockaddr_in), struct sockaddr_dl*); break; #ifdef HAVE_IPV6 case AF_INET6: sdl = ADD_POINTER(sin, sizeof(struct sockaddr_in6), struct sockaddr_dl*); break; #endif // HAVE_IPV6 default: XLOG_UNREACHABLE(); break; } sdl->sdl_family = AF_LINK; #ifdef HAVE_STRUCT_SOCKADDR_DL_SDL_LEN sdl->sdl_len = sizeof(struct sockaddr_dl); #endif } while (false); #endif // AF_LINK if (rs.write(rtm, rtm->rtm_msglen) != rtm->rtm_msglen) { XLOG_ERROR("Error writing to Rtmv2 pipe: %s", strerror(errno)); return (XORP_ERROR); } // // Force to receive data from the kernel, and then parse it // string error_msg; if (_rs_reader.receive_data(rs, rtm->rtm_seq, error_msg) != XORP_OK) { XLOG_ERROR("Error reading from Rtmv2 pipe: %s", error_msg.c_str()); return (XORP_ERROR); } if (parse_buffer_routing_socket(fibconfig().system_config_iftree(), fte, _rs_reader.buffer(), FibMsg::GETS) != XORP_OK) { return (XORP_ERROR); } return (XORP_OK); #endif // 0 } int FibConfigEntryGetRtmV2::lookup_route_by_network(const IPvXNet& dst, FteX& fte) { #if 1 /* * XXX: The RTM_GET stuff isn't supported on Windows yet. */ UNUSED(dst); UNUSED(fte); return (XORP_ERROR); #else static const size_t buffer_size = sizeof(struct rt_msghdr) + 512; union { uint8_t data[buffer_size]; struct rt_msghdr rtm; } buffer; struct rt_msghdr* rtm = &buffer.rtm; struct sockaddr_in* sin; WinRtmPipe& rs = *this; // Zero the return information fte.zero(); // Check that the family is supported do { if (dst.is_ipv4()) { if (! fea_data_plane_manager().have_ipv4()) return (XORP_ERROR); break; } if (dst.is_ipv6()) { if (! fea_data_plane_manager().have_ipv6()) return (XORP_ERROR); break; } break; } while (false); // Check that the destination address is valid if (! dst.is_unicast()) { return (XORP_ERROR); } // // Set the request // memset(&buffer, 0, sizeof(buffer)); switch (dst.af()) { case AF_INET: rtm->rtm_msglen = sizeof(*rtm) + 2 * sizeof(struct sockaddr_in); break; #ifdef HAVE_IPV6 case AF_INET6: rtm->rtm_msglen = sizeof(*rtm) + 2 * sizeof(struct sockaddr_in6); break; #endif // HAVE_IPV6 default: XLOG_UNREACHABLE(); break; } rtm->rtm_version = RTM_VERSION; rtm->rtm_type = RTM_GET; rtm->rtm_addrs = (RTA_DST | RTA_NETMASK | RTA_IFP); rtm->rtm_flags = RTF_UP; rtm->rtm_pid = rs.pid(); rtm->rtm_seq = rs.seqno(); // Copy the destination address sin = reinterpret_cast(rtm + 1); dst.masked_addr().copy_out(*sin); // Copy the network mask switch (dst.af()) { case AF_INET: sin = ADD_POINTER(sin, sizeof(struct sockaddr_in), struct sockaddr_in*); break; #ifdef HAVE_IPV6 case AF_INET6: sin = ADD_POINTER(sin, sizeof(struct sockaddr_in6), struct sockaddr_in*); break; #endif // HAVE_IPV6 default: XLOG_UNREACHABLE(); break; } IPvX netmask = IPvX::make_prefix(dst.af(), dst.prefix_len()); netmask.copy_out(*sin); // // Add extra space for sockaddr_dl that corresponds to the RTA_IFP flag. // Required if the OS is very strict in the arguments checking // (e.g., NetBSD). // #ifdef AF_LINK do { // Set the data-link socket struct sockaddr_dl* sdl; rtm->rtm_msglen += sizeof(struct sockaddr_dl); switch (dst.af()) { case AF_INET: sdl = ADD_POINTER(sin, sizeof(struct sockaddr_in), struct sockaddr_dl*); break; #ifdef HAVE_IPV6 case AF_INET6: sdl = ADD_POINTER(sin, sizeof(struct sockaddr_in6), struct sockaddr_dl*); break; #endif // HAVE_IPV6 default: XLOG_UNREACHABLE(); break; } sdl->sdl_family = AF_LINK; #ifdef HAVE_STRUCT_SOCKADDR_DL_SDL_LEN sdl->sdl_len = sizeof(struct sockaddr_dl); #endif } while (false); #endif // AF_LINK if (rs.write(rtm, rtm->rtm_msglen) != rtm->rtm_msglen) { XLOG_ERROR("Error writing to Rtmv2 pipe: %s", strerror(errno)); return (XORP_ERROR); } // // Force to receive data from the kernel, and then parse it // string error_msg; if (_rs_reader.receive_data(rs, rtm->rtm_seq, error_msg) != XORP_OK) { XLOG_ERROR("Error reading from Rtmv2 pipe: %s", error_msg.c_str()); return (XORP_ERROR); } if (parse_buffer_routing_socket(fibconfig().system_config_iftree(), fte, _rs_reader.buffer(), FibMsg::GETS) != XORP_OK) { return (XORP_ERROR); } return (XORP_OK); #endif // 0 } #endif // HOST_OS_WINDOWS xorp/fea/data_plane/fibconfig/fibconfig_table_get_netlink_socket.hh0000664000076400007640000000752711540225523026014 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_TABLE_GET_NETLINK_SOCKET_HH__ #define __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_TABLE_GET_NETLINK_SOCKET_HH__ #include #ifdef HAVE_NETLINK_SOCKETS #include "fea/fibconfig_table_get.hh" #include "fea/data_plane/control_socket/netlink_socket.hh" class FibConfigTableGetNetlinkSocket : public FibConfigTableGet, public NetlinkSocket { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ FibConfigTableGetNetlinkSocket(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~FibConfigTableGetNetlinkSocket(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg); /** * Obtain the IPv4 unicast forwarding table. * * @param fte_list the return-by-reference list with all entries in * the IPv4 unicast forwarding table. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int get_table4(list& fte_list); /** * Obtain the IPv6 unicast forwarding table. * * @param fte_list the return-by-reference list with all entries in * the IPv6 unicast forwarding table. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int get_table6(list& fte_list); /** * Parse information about routing table information received from * the underlying system. * * The information to parse is in NETLINK format * (e.g., obtained by netlink(7) sockets mechanism). * * @param family the address family to consider only ((e.g., AF_INET * or AF_INET6 for IPv4 and IPv6 respectively). * @param iftree the interface tree to use. * @param fte_list the list with the Fte entries to store the result. * @param buffer the buffer with the data to parse. * @param is_nlm_get_only if true, consider only the entries obtained * by RTM_GETROUTE. * @return XORP_OK on success, otherwise XORP_ERROR. * @see FteX. */ static int parse_buffer_netlink_socket(int family, const IfTree& iftree, list& fte_list, const vector& buffer, bool is_nlm_get_only, const FibConfig& fibconfig); /** Routing table ID that we are interested in might have changed. */ virtual int notify_table_id_change(uint32_t new_tbl) { return NetlinkSocket::notify_table_id_change(new_tbl); } private: int get_table(int family, list& fte_list); NetlinkSocketReader _ns_reader; }; #endif #endif // __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_TABLE_GET_NETLINK_SOCKET_HH__ xorp/fea/data_plane/fibconfig/fibconfig_table_set_click.hh0000664000076400007640000000657211540225523024100 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/data_plane/fibconfig/fibconfig_table_set_click.hh,v 1.9 2008/10/02 21:57:00 bms Exp $ #ifndef __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_TABLE_SET_CLICK_HH__ #define __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_TABLE_SET_CLICK_HH__ #include #ifdef XORP_USE_CLICK #include "fea/fibconfig_table_set.hh" #include "fea/data_plane/control_socket/click_socket.hh" class FibConfigTableSetClick : public FibConfigTableSet, public ClickSocket { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ FibConfigTableSetClick(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~FibConfigTableSetClick(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg); /** * Set the IPv4 unicast forwarding table. * * @param fte_list the list with all entries to install into * the IPv4 unicast forwarding table. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int set_table4(const list& fte_list); /** * Delete all entries in the IPv4 unicast forwarding table. * * Must be within a configuration interval. * * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int delete_all_entries4(); /** * Set the IPv6 unicast forwarding table. * * @param fte_list the list with all entries to install into * the IPv6 unicast forwarding table. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int set_table6(const list& fte_list); /** * Delete all entries in the IPv6 unicast forwarding table. * * Must be within a configuration interval. * * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int delete_all_entries6(); /** Routing table ID that we are interested in might have changed. */ virtual int notify_table_id_change(uint32_t new_tbl) { UNUSED(new_tbl); return XORP_OK; } private: ClickSocketReader _cs_reader; }; #endif // click #endif // __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_TABLE_SET_CLICK_HH__ xorp/fea/data_plane/fibconfig/fibconfig_entry_observer_iphelper.cc0000664000076400007640000000462211540224221025703 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "fea/fibconfig.hh" #include "fibconfig_entry_observer_iphelper.hh" // // Observe single-entry information change about the unicast forwarding table. // // E.g., if the forwarding table has changed, then the information // received by the observer would specify the particular entry that // has changed. // // The mechanism to observe the information is the IP Helper API for // Windows (IPHLPAPI.DLL). // #ifdef HOST_OS_WINDOWS FibConfigEntryObserverIPHelper::FibConfigEntryObserverIPHelper(FeaDataPlaneManager& fea_data_plane_manager) : FibConfigEntryObserver(fea_data_plane_manager) { } FibConfigEntryObserverIPHelper::~FibConfigEntryObserverIPHelper() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the IP Helper mechanism to observe " "information about forwarding table from the underlying " "system: %s", error_msg.c_str()); } } int FibConfigEntryObserverIPHelper::start(string& error_msg) { UNUSED(error_msg); if (_is_running) return (XORP_OK); _is_running = true; return (XORP_OK); } int FibConfigEntryObserverIPHelper::stop(string& error_msg) { UNUSED(error_msg); if (! _is_running) return (XORP_OK); _is_running = false; return (XORP_OK); } void FibConfigEntryObserverIPHelper::receive_data(const vector& buffer) { debug_msg("called\n"); // Do nothing. UNUSED(buffer); } #endif // HOST_OS_WINDOWS xorp/fea/data_plane/fibconfig/fibconfig_forwarding_sysctl.cc0000664000076400007640000002506211421137511024512 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libcomm/comm_api.h" #ifdef HAVE_SYS_SYSCTL_H #include #endif #include "fea/fibconfig.hh" #include "fibconfig_forwarding_sysctl.hh" // // Configure unicast forwarding. // // The mechanism to get/set the information is sysctl(3). // #if defined(HAVE_SYSCTL_IPCTL_FORWARDING) || defined(HAVE_SYSCTL_IPV6CTL_FORWARDING) || defined(HAVE_SYSCTL_IPV6CTL_ACCEPT_RTADV) FibConfigForwardingSysctl::FibConfigForwardingSysctl( FeaDataPlaneManager& fea_data_plane_manager) : FibConfigForwarding(fea_data_plane_manager) { } FibConfigForwardingSysctl::~FibConfigForwardingSysctl() { } int FibConfigForwardingSysctl::unicast_forwarding_enabled4(bool& ret_value, string& error_msg) const { #ifndef HAVE_SYSCTL_IPCTL_FORWARDING ret_value = false; error_msg = c_format("Cannot test whether IPv4 unicast forwarding " "is enabled: sysctl(IPCTL_FORWARDING) is not supported"); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); #else // HAVE_SYSCTL_IPCTL_FORWARDING int enabled = 0; if (! fea_data_plane_manager().have_ipv4()) { ret_value = false; error_msg = c_format("Cannot test whether IPv4 unicast forwarding " "is enabled: IPv4 is not supported"); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } // // Read the value from the MIB // size_t sz = sizeof(enabled); int mib[4]; mib[0] = CTL_NET; mib[1] = AF_INET; mib[2] = IPPROTO_IP; mib[3] = IPCTL_FORWARDING; if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), &enabled, &sz, NULL, 0) != 0) { error_msg = c_format("Get sysctl(IPCTL_FORWARDING) failed: %s", strerror(errno)); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } if (enabled > 0) ret_value = true; else ret_value = false; return (XORP_OK); #endif // HAVE_SYSCTL_IPCTL_FORWARDING } int FibConfigForwardingSysctl::unicast_forwarding_enabled6(bool& ret_value, string& error_msg) const { #ifndef HAVE_SYSCTL_IPV6CTL_FORWARDING ret_value = false; error_msg = c_format("Cannot test whether IPv6 unicast forwarding " "is enabled: sysctl(IPV6CTL_FORWARDING) is not supported"); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); #else // HAVE_SYSCTL_IPV6CTL_FORWARDING int enabled = 0; if (! fea_data_plane_manager().have_ipv6()) { ret_value = false; error_msg = c_format("Cannot test whether IPv6 unicast forwarding " "is enabled: IPv6 is not supported"); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } // // Read the value from the MIB // size_t sz = sizeof(enabled); int mib[4]; mib[0] = CTL_NET; mib[1] = AF_INET6; mib[2] = IPPROTO_IPV6; mib[3] = IPV6CTL_FORWARDING; if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), &enabled, &sz, NULL, 0) != 0) { error_msg = c_format("Get sysctl(IPV6CTL_FORWARDING) failed: %s", strerror(errno)); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } if (enabled > 0) ret_value = true; else ret_value = false; return (XORP_OK); #endif // HAVE_SYSCTL_IPV6CTL_FORWARDING } int FibConfigForwardingSysctl::accept_rtadv_enabled6(bool& ret_value, string& error_msg) const { #ifndef HAVE_SYSCTL_IPV6CTL_ACCEPT_RTADV ret_value = false; error_msg = c_format("Cannot test whether the acceptance of IPv6 " "Router Advertisement messages is enabled: " "sysctl(IPV6CTL_ACCEPT_RTADV) is not supported"); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); #else // HAVE_SYSCTL_IPV6CTL_ACCEPT_RTADV int enabled = 0; if (! fea_data_plane_manager().have_ipv6()) { ret_value = false; error_msg = c_format("Cannot test whether the acceptance of IPv6 " "Router Advertisement messages is enabled: " "IPv6 is not supported"); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } // // Read the value from the MIB // size_t sz = sizeof(enabled); int mib[4]; mib[0] = CTL_NET; mib[1] = AF_INET6; mib[2] = IPPROTO_IPV6; mib[3] = IPV6CTL_ACCEPT_RTADV; if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), &enabled, &sz, NULL, 0) != 0) { error_msg = c_format("Get sysctl(IPV6CTL_ACCEPT_RTADV) failed: %s", strerror(errno)); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } if (enabled > 0) ret_value = true; else ret_value = false; return (XORP_OK); #endif // HAVE_SYSCTL_IPV6CTL_ACCEPT_RTADV } int FibConfigForwardingSysctl::set_unicast_forwarding_enabled4(bool v, string& error_msg) { #ifndef HAVE_SYSCTL_IPCTL_FORWARDING error_msg = c_format("Cannot set IPv4 unicast forwarding to %s: " "sysctl(IPCTL_FORWARDING) is not supported", bool_c_str(v)); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); #else // HAVE_SYSCTL_IPCTL_FORWARDING int enable = (v) ? 1 : 0; bool old_value; if (! fea_data_plane_manager().have_ipv4()) { if (! v) { // // XXX: we assume that "not supported" == "disable", hence // return OK. // return (XORP_OK); } error_msg = c_format("Cannot set IPv4 unicast forwarding to %s: " "IPv4 is not supported", bool_c_str(v)); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } // // Get the old value // if (unicast_forwarding_enabled4(old_value, error_msg) != XORP_OK) return (XORP_ERROR); if (old_value == v) return (XORP_OK); // Nothing changed // // Write the value to the MIB // size_t sz = sizeof(enable); int mib[4]; mib[0] = CTL_NET; mib[1] = AF_INET; mib[2] = IPPROTO_IP; mib[3] = IPCTL_FORWARDING; if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), NULL, NULL, &enable, sz) != 0) { error_msg = c_format("Set sysctl(IPCTL_FORWARDING) to %s failed: %s", bool_c_str(v), strerror(errno)); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } return (XORP_OK); #endif // HAVE_SYSCTL_IPCTL_FORWARDING } int FibConfigForwardingSysctl::set_unicast_forwarding_enabled6(bool v, string& error_msg) { #ifndef HAVE_SYSCTL_IPV6CTL_FORWARDING error_msg = c_format("Cannot set IPv6 unicast forwarding to %s: " "sysctl(IPV6CTL_FORWARDING) is not supported", bool_c_str(v)); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); #else // HAVE_SYSCTL_IPV6CTL_FORWARDING int enable = (v) ? 1 : 0; bool old_value, old_value_accept_rtadv; if (! fea_data_plane_manager().have_ipv6()) { if (! v) { // // XXX: we assume that "not supported" == "disable", hence // return OK. // return (XORP_OK); } error_msg = c_format("Cannot set IPv6 unicast forwarding to %s: " "IPv6 is not supported", bool_c_str(v)); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } // // Get the old value // if (unicast_forwarding_enabled6(old_value, error_msg) != XORP_OK) return (XORP_ERROR); if (accept_rtadv_enabled6(old_value_accept_rtadv, error_msg) != XORP_OK) return (XORP_ERROR); if ((old_value == v) && (old_value_accept_rtadv == !v)) return (XORP_OK); // Nothing changed // // Set the IPv6 Router Advertisement value // if (set_accept_rtadv_enabled6(!v, error_msg) != XORP_OK) return (XORP_ERROR); // // Write the value to the MIB // size_t sz = sizeof(enable); int mib[4]; mib[0] = CTL_NET; mib[1] = AF_INET6; mib[2] = IPPROTO_IPV6; mib[3] = IPV6CTL_FORWARDING; if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), NULL, NULL, &enable, sz) != 0) { error_msg = c_format("Set sysctl(IPV6CTL_FORWARDING) to %s failed: %s", bool_c_str(v), strerror(errno)); XLOG_ERROR("%s", error_msg.c_str()); // Restore the old accept_rtadv value if (old_value_accept_rtadv != !v) { string dummy_error_msg; set_accept_rtadv_enabled6(old_value_accept_rtadv, dummy_error_msg); } return (XORP_ERROR); } return (XORP_OK); #endif // HAVE_SYSCTL_IPV6CTL_FORWARDING } int FibConfigForwardingSysctl::set_accept_rtadv_enabled6(bool v, string& error_msg) { #ifndef HAVE_SYSCTL_IPV6CTL_ACCEPT_RTADV error_msg = c_format("Cannot set the acceptance of IPv6 " "Router Advertisement messages to %s: " "sysctl(IPV6CTL_ACCEPT_RTADV) is not supported", bool_c_str(v)); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); #else // HAVE_SYSCTL_IPV6CTL_ACCEPT_RTADV int enable = (v) ? 1 : 0; bool old_value; if (! fea_data_plane_manager().have_ipv6()) { if (! v) { // // XXX: we assume that "not supported" == "disable", hence // return OK. // return (XORP_OK); } error_msg = c_format("Cannot set the acceptance of IPv6 " "Router Advertisement messages to %s: " "IPv6 is not supported", bool_c_str(v)); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } // // Get the old value // if (accept_rtadv_enabled6(old_value, error_msg) != XORP_OK) return (XORP_ERROR); if (old_value == v) return (XORP_OK); // Nothing changed // // Write the value to the MIB // size_t sz = sizeof(enable); int mib[4]; mib[0] = CTL_NET; mib[1] = AF_INET6; mib[2] = IPPROTO_IPV6; mib[3] = IPV6CTL_ACCEPT_RTADV; if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), NULL, NULL, &enable, sz) != 0) { error_msg = c_format("Set sysctl(IPV6CTL_ACCEPT_RTADV) to %s failed: %s", bool_c_str(v), strerror(errno)); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } return (XORP_OK); #endif // HAVE_SYSCTL_IPV6CTL_ACCEPT_RTADV } #endif // HAVE_SYSCTL_IPCTL_FORWARDING || HAVE_SYSCTL_IPV6CTL_FORWARDING || HAVE_SYSCTL_IPV6CTL_ACCEPT_RTADV xorp/fea/data_plane/fibconfig/fibconfig_entry_observer_rtmv2.hh0000664000076400007640000000403511540224222025156 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/data_plane/fibconfig/fibconfig_entry_observer_rtmv2.hh,v 1.7 2008/10/02 21:56:57 bms Exp $ #ifndef __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_ENTRY_OBSERVER_RTMV2_HH__ #define __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_ENTRY_OBSERVER_RTMV2_HH__ #include "fea/fibconfig_entry_observer.hh" class FibConfigEntryObserverRtmV2 : public FibConfigEntryObserver { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ FibConfigEntryObserverRtmV2(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~FibConfigEntryObserverRtmV2(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg); private: }; #endif // __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_ENTRY_OBSERVER_RTMV2_HH__ xorp/fea/data_plane/fibconfig/fibconfig_forwarding_dummy.hh0000664000076400007640000001044011421137511024330 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2007-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/data_plane/fibconfig/fibconfig_forwarding_dummy.hh,v 1.6 2008/10/02 21:56:58 bms Exp $ #ifndef __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_FORWARDING_DUMMY_HH__ #define __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_FORWARDING_DUMMY_HH__ #include "fea/fibconfig_forwarding.hh" class FibConfigForwardingDummy : public FibConfigForwarding { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ FibConfigForwardingDummy(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~FibConfigForwardingDummy(); /** * Test whether the IPv4 unicast forwarding engine is enabled or disabled * to forward packets. * * @param ret_value if true on return, then the IPv4 unicast forwarding * is enabled, otherwise is disabled. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int unicast_forwarding_enabled4(bool& ret_value, string& error_msg) const; /** * Test whether the IPv6 unicast forwarding engine is enabled or disabled * to forward packets. * * @param ret_value if true on return, then the IPv6 unicast forwarding * is enabled, otherwise is disabled. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int unicast_forwarding_enabled6(bool& ret_value, string& error_msg) const; /** * Test whether the acceptance of IPv6 Router Advertisement messages is * enabled or disabled. * * @param ret_value if true on return, then the acceptance of IPv6 Router * Advertisement messages is enabled, otherwise is disabled. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int accept_rtadv_enabled6(bool& ret_value, string& error_msg) const; /** * Set the IPv4 unicast forwarding engine to enable or disable forwarding * of packets. * * @param v if true, then enable IPv4 unicast forwarding, otherwise * disable it. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int set_unicast_forwarding_enabled4(bool v, string& error_msg); /** * Set the IPv6 unicast forwarding engine to enable or disable forwarding * of packets. * * @param v if true, then enable IPv6 unicast forwarding, otherwise * disable it. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int set_unicast_forwarding_enabled6(bool v, string& error_msg); /** * Enable or disable the acceptance of IPv6 Router Advertisement messages * from other routers. It should be enabled for hosts, and disabled for * routers. * * @param v if true, then enable the acceptance of IPv6 Router * Advertisement messages, otherwise disable it. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int set_accept_rtadv_enabled6(bool v, string& error_msg); private: bool _unicast_forwarding_enabled4; bool _unicast_forwarding_enabled6; bool _accept_rtadv_enabled6; }; #endif // __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_FORWARDING_DUMMY_HH__ xorp/fea/data_plane/fibconfig/fibconfig_table_set_rtmv2.hh0000664000076400007640000000602211540224222024046 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/data_plane/fibconfig/fibconfig_table_set_rtmv2.hh,v 1.9 2008/10/02 21:57:01 bms Exp $ #ifndef __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_TABLE_SET_RTMV2_HH__ #define __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_TABLE_SET_RTMV2_HH__ #include "fea/fibconfig_table_set.hh" class FibConfigTableSetRtmV2 : public FibConfigTableSet { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ FibConfigTableSetRtmV2(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~FibConfigTableSetRtmV2(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg); /** * Set the IPv4 unicast forwarding table. * * @param fte_list the list with all entries to install into * the IPv4 unicast forwarding table. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int set_table4(const list& fte_list); /** * Delete all entries in the IPv4 unicast forwarding table. * * Must be within a configuration interval. * * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int delete_all_entries4(); /** * Set the IPv6 unicast forwarding table. * * @param fte_list the list with all entries to install into * the IPv6 unicast forwarding table. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int set_table6(const list& fte_list); /** * Delete all entries in the IPv6 unicast forwarding table. * * Must be within a configuration interval. * * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int delete_all_entries6(); private: }; #endif // __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_TABLE_SET_RTMV2_HH__ xorp/fea/data_plane/fibconfig/fibconfig_entry_get_dummy.cc0000664000076400007640000000576011421137511024165 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvxnet.hh" #include "fea/fibconfig.hh" #include "fibconfig_entry_get_dummy.hh" // // Get single-entry information from the unicast forwarding table. // // The mechanism to obtain the information is Dummy (for testing purpose). // FibConfigEntryGetDummy::FibConfigEntryGetDummy(FeaDataPlaneManager& fea_data_plane_manager) : FibConfigEntryGet(fea_data_plane_manager) { } FibConfigEntryGetDummy::~FibConfigEntryGetDummy() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the Dummy mechanism to get " "information about forwarding table from the underlying " "system: %s", error_msg.c_str()); } } int FibConfigEntryGetDummy::start(string& error_msg) { UNUSED(error_msg); if (_is_running) return (XORP_OK); _is_running = true; return (XORP_OK); } int FibConfigEntryGetDummy::stop(string& error_msg) { UNUSED(error_msg); if (! _is_running) return (XORP_OK); _is_running = false; return (XORP_OK); } int FibConfigEntryGetDummy::lookup_route_by_dest4(const IPv4& dst, Fte4& fte) { Trie4::iterator ti = fibconfig().trie4().find(dst); if (ti != fibconfig().trie4().end()) { fte = ti.payload(); return (XORP_OK); } return (XORP_ERROR); } int FibConfigEntryGetDummy::lookup_route_by_network4(const IPv4Net& dst, Fte4& fte) { Trie4::iterator ti = fibconfig().trie4().find(dst); if (ti != fibconfig().trie4().end()) { fte = ti.payload(); return (XORP_OK); } return (XORP_ERROR); } int FibConfigEntryGetDummy::lookup_route_by_dest6(const IPv6& dst, Fte6& fte) { Trie6::iterator ti = fibconfig().trie6().find(dst); if (ti != fibconfig().trie6().end()) { fte = ti.payload(); return (XORP_OK); } return (XORP_ERROR); } int FibConfigEntryGetDummy::lookup_route_by_network6(const IPv6Net& dst, Fte6& fte) { Trie6::iterator ti = fibconfig().trie6().find(dst); if (ti != fibconfig().trie6().end()) { fte = ti.payload(); return (XORP_OK); } return (XORP_ERROR); } xorp/fea/data_plane/fibconfig/fibconfig_entry_observer_dummy.hh0000664000076400007640000000443411540225522025246 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_ENTRY_OBSERVER_DUMMY_HH__ #define __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_ENTRY_OBSERVER_DUMMY_HH__ #include "fea/fibconfig_entry_observer.hh" class FibConfigEntryObserverDummy : public FibConfigEntryObserver { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ FibConfigEntryObserverDummy(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~FibConfigEntryObserverDummy(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg); /** * Receive data from the underlying system. * * @param buffer the buffer with the received data. */ virtual void receive_data(const vector& buffer); /** Routing table ID that we are interested in might have changed. */ virtual int notify_table_id_change(uint32_t new_tbl) { UNUSED(new_tbl); return XORP_OK; } private: }; #endif // __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_ENTRY_OBSERVER_DUMMY_HH__ xorp/fea/data_plane/fibconfig/fibconfig_table_get_click.hh0000664000076400007640000000563711540225523024065 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/data_plane/fibconfig/fibconfig_table_get_click.hh,v 1.9 2008/10/02 21:56:58 bms Exp $ #ifndef __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_TABLE_GET_CLICK_HH__ #define __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_TABLE_GET_CLICK_HH__ #include #ifdef XORP_USE_CLICK #include "fea/fibconfig_table_get.hh" #include "fea/data_plane/control_socket/click_socket.hh" class FibConfigTableGetClick : public FibConfigTableGet, public ClickSocket { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ FibConfigTableGetClick(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~FibConfigTableGetClick(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg); /** * Obtain the IPv4 unicast forwarding table. * * @param fte_list the return-by-reference list with all entries in * the IPv4 unicast forwarding table. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int get_table4(list& fte_list); /** * Obtain the IPv6 unicast forwarding table. * * @param fte_list the return-by-reference list with all entries in * the IPv6 unicast forwarding table. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int get_table6(list& fte_list); /** Routing table ID that we are interested in might have changed. */ virtual int notify_table_id_change(uint32_t new_tbl) { UNUSED(new_tbl); return XORP_OK; } private: ClickSocketReader _cs_reader; }; #endif // click #endif // __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_TABLE_GET_CLICK_HH__ xorp/fea/data_plane/fibconfig/fibconfig_table_set_netlink_socket.hh0000664000076400007640000000656511540225523026031 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/data_plane/fibconfig/fibconfig_table_set_netlink_socket.hh,v 1.9 2008/10/02 21:57:01 bms Exp $ #ifndef __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_TABLE_SET_NETLINK_SOCKET_HH__ #define __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_TABLE_SET_NETLINK_SOCKET_HH__ #include #ifdef HAVE_NETLINK_SOCKETS #include "fea/fibconfig_table_set.hh" #include "fea/data_plane/control_socket/netlink_socket.hh" class FibConfigTableSetNetlinkSocket : public FibConfigTableSet { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ FibConfigTableSetNetlinkSocket(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~FibConfigTableSetNetlinkSocket(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg); /** * Set the IPv4 unicast forwarding table. * * @param fte_list the list with all entries to install into * the IPv4 unicast forwarding table. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int set_table4(const list& fte_list); /** * Delete all entries in the IPv4 unicast forwarding table. * * Must be within a configuration interval. * * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int delete_all_entries4(); /** * Set the IPv6 unicast forwarding table. * * @param fte_list the list with all entries to install into * the IPv6 unicast forwarding table. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int set_table6(const list& fte_list); /** * Delete all entries in the IPv6 unicast forwarding table. * * Must be within a configuration interval. * * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int delete_all_entries6(); /** Routing table ID that we are interested in might have changed. */ virtual int notify_table_id_change(uint32_t new_tbl) { UNUSED(new_tbl); return XORP_OK; } private: }; #endif #endif // __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_TABLE_SET_NETLINK_SOCKET_HH__ xorp/fea/data_plane/fibconfig/fibconfig_table_observer_dummy.hh0000664000076400007640000000443411540225523025175 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_TABLE_OBSERVER_DUMMY_HH__ #define __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_TABLE_OBSERVER_DUMMY_HH__ #include "fea/fibconfig_table_observer.hh" class FibConfigTableObserverDummy : public FibConfigTableObserver { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ FibConfigTableObserverDummy(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~FibConfigTableObserverDummy(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg); /** * Receive data from the underlying system. * * @param buffer the buffer with the received data. */ virtual void receive_data(const vector& buffer); /** Routing table ID that we are interested in might have changed. */ virtual int notify_table_id_change(uint32_t new_tbl) { UNUSED(new_tbl); return XORP_OK; } private: }; #endif // __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_TABLE_OBSERVER_DUMMY_HH__ xorp/fea/data_plane/fibconfig/fibconfig_entry_get_iphelper.cc0000664000076400007640000002015111540224221024626 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/win_io.h" #include "libxorp/ipvxnet.hh" #ifdef HAVE_IPHLPAPI_H #include #endif #ifdef HAVE_ROUTPROT_H #include #endif #include "fea/fibconfig.hh" #include "fibconfig_entry_get_iphelper.hh" // // Get single-entry information from the unicast forwarding table. // // The mechanism to obtain the information is the IP Helper API for // Windows (IPHLPAPI.DLL). // #ifdef HOST_OS_WINDOWS FibConfigEntryGetIPHelper::FibConfigEntryGetIPHelper(FeaDataPlaneManager& fea_data_plane_manager) : FibConfigEntryGet(fea_data_plane_manager) { } FibConfigEntryGetIPHelper::~FibConfigEntryGetIPHelper() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the IP Helper mechanism to get " "information about forwarding table from the underlying " "system: %s", error_msg.c_str()); } } int FibConfigEntryGetIPHelper::start(string& error_msg) { UNUSED(error_msg); if (_is_running) return (XORP_OK); _is_running = true; return (XORP_OK); } int FibConfigEntryGetIPHelper::stop(string& error_msg) { UNUSED(error_msg); if (! _is_running) return (XORP_OK); _is_running = false; return (XORP_OK); } int FibConfigEntryGetIPHelper::lookup_route_by_dest4(const IPv4& dst, Fte4& fte) { FteX ftex(dst.af()); int ret_value = XORP_ERROR; ret_value = lookup_route_by_dest(IPvX(dst), ftex); fte = ftex.get_fte4(); return (ret_value); } int FibConfigEntryGetIPHelper::lookup_route_by_network4(const IPv4Net& dst, Fte4& fte) { FteX ftex(dst.af()); int ret_value = XORP_ERROR; ret_value = lookup_route_by_network(IPvXNet(dst), ftex); fte = ftex.get_fte4(); return (ret_value); } int FibConfigEntryGetIPHelper::lookup_route_by_dest6(const IPv6& dst, Fte6& fte) { FteX ftex(dst.af()); int ret_value = XORP_ERROR; ret_value = lookup_route_by_dest(IPvX(dst), ftex); fte = ftex.get_fte6(); return (ret_value); } int FibConfigEntryGetIPHelper::lookup_route_by_network6(const IPv6Net& dst, Fte6& fte) { FteX ftex(dst.af()); int ret_value = XORP_ERROR; ret_value = lookup_route_by_network(IPvXNet(dst), ftex); fte = ftex.get_fte6(); return (ret_value); } int FibConfigEntryGetIPHelper::lookup_route_by_dest(const IPvX& dst, FteX& fte) { // Zero the return information fte.zero(); // Check that the family is supported do { if (dst.is_ipv4()) { if (! fea_data_plane_manager().have_ipv4()) return (XORP_ERROR); break; } if (dst.is_ipv6()) { if (! fea_data_plane_manager().have_ipv6()) return (XORP_ERROR); break; } break; } while (false); // Check that the destination address is valid if (! dst.is_unicast()) { return (XORP_ERROR); } switch (dst.af()) { case AF_INET: break; #ifdef HAVE_IPV6 case AF_INET6: return (XORP_ERROR); break; #endif // HAVE_IPV6 default: XLOG_UNREACHABLE(); break; } PMIB_IPFORWARDTABLE pfwdtable = NULL; DWORD result, tries; ULONG dwSize; tries = 0; result = ERROR_INSUFFICIENT_BUFFER; dwSize = sizeof(MIB_IPFORWARDTABLE); do { pfwdtable = (PMIB_IPFORWARDTABLE) ((tries == 0) ? malloc(dwSize) : realloc(pfwdtable, dwSize)); if (pfwdtable == NULL) break; result = GetIpForwardTable(pfwdtable, &dwSize, TRUE); } while ((++tries < 3) || (result == ERROR_INSUFFICIENT_BUFFER)); if (result != NO_ERROR) { XLOG_ERROR("GetIpForwardTable(): %s\n", win_strerror(result)); if (pfwdtable != NULL) free(pfwdtable); return (XORP_ERROR); } IPv4 dest; IPv4 nexthop; IPv4 mask; IPv4Net destnet; bool found; for (uint32_t i = 0; i < pfwdtable->dwNumEntries; i++) { // XXX: Windows can have multiple routes to the same destination. // Here, we only return the first match. if (dst.get_ipv4().addr() == pfwdtable->table[i].dwForwardDest) { dest.copy_in(reinterpret_cast( &pfwdtable->table[i].dwForwardDest)); mask.copy_in(reinterpret_cast( &pfwdtable->table[i].dwForwardMask)); destnet = IPv4Net(dest, mask.mask_len()); nexthop.copy_in(reinterpret_cast( &pfwdtable->table[i].dwForwardNextHop)); uint32_t ifindex = static_cast( pfwdtable->table[i].dwForwardIfIndex); const IfTree& iftree = fibconfig().system_config_iftree(); const IfTreeVif* vifp = iftree.find_vif(ifindex); XLOG_ASSERT(vifp != NULL); // // XXX: The old test for a XORP route was: // pfwdtable->table[i].dwForwardType == PROTO_IP_NETMGMT // For now, just pass true; we will deal with this better // once RTMv2 is supported. // fte = FteX(destnet, nexthop, vifp->ifname(), vifp->vifname(), 0xffff, 0xffff, true); found = true; break; } } if (! found) return (XORP_ERROR); return (XORP_OK); } int FibConfigEntryGetIPHelper::lookup_route_by_network(const IPvXNet& dst, FteX& fte) { // Zero the return information fte.zero(); // Check that the family is supported do { if (dst.is_ipv4()) { if (! fea_data_plane_manager().have_ipv4()) return (XORP_ERROR); break; } if (dst.is_ipv6()) { if (! fea_data_plane_manager().have_ipv6()) return (XORP_ERROR); break; } break; } while (false); switch (dst.af()) { case AF_INET: break; #ifdef HAVE_IPV6 case AF_INET6: return (XORP_ERROR); break; #endif // HAVE_IPV6 default: XLOG_UNREACHABLE(); break; } PMIB_IPFORWARDTABLE pfwdtable = NULL; DWORD result, tries; ULONG dwSize; tries = 0; result = ERROR_INSUFFICIENT_BUFFER; dwSize = sizeof(MIB_IPFORWARDTABLE); do { pfwdtable = (PMIB_IPFORWARDTABLE) ((tries == 0) ? malloc(dwSize) : realloc(pfwdtable, dwSize)); if (pfwdtable == NULL) break; result = GetIpForwardTable(pfwdtable, &dwSize, TRUE); } while ((++tries < 3) || (result == ERROR_INSUFFICIENT_BUFFER)); if (result != NO_ERROR) { XLOG_ERROR("GetIpForwardTable(): %s\n", win_strerror(result)); if (pfwdtable != NULL) free(pfwdtable); return (XORP_ERROR); } IPv4Net destnet; IPv4 nexthop, mask, dest; bool found; for (unsigned int i = 0; i < pfwdtable->dwNumEntries; i++) { // XXX: Windows can have multiple routes to the same destination. // Here, we only return the first match. if (dst.masked_addr().get_ipv4().addr() == pfwdtable->table[i].dwForwardDest) { dest.copy_in((uint8_t*)&(pfwdtable->table[i].dwForwardDest)); mask.copy_in((uint8_t*)&(pfwdtable->table[i].dwForwardMask)); destnet = IPv4Net(dest, mask.mask_len()); nexthop.copy_in((uint8_t*)&(pfwdtable->table[i].dwForwardNextHop)); uint32_t ifindex = static_cast( pfwdtable->table[i].dwForwardIfIndex); const IfTree& iftree = fibconfig().system_config_iftree(); const IfTreeVif* vifp = iftree.find_vif(ifindex); XLOG_ASSERT(vifp != NULL); fte = FteX(destnet, nexthop, vifp->ifname(), vifp->vifname(), 0xffff, 0xffff, true); found = true; break; } } if (! found) return (XORP_ERROR); return (XORP_OK); } #endif // HOST_OS_WINDOWS xorp/fea/data_plane/fibconfig/fibconfig_entry_set_netlink_socket.cc0000664000076400007640000004375111540225523026067 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include #ifdef HAVE_NETLINK_SOCKETS #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #ifdef HAVE_NET_IF_H #include #endif #ifdef HAVE_LINUX_TYPES_H #include #endif #ifdef HAVE_LINUX_RTNETLINK_H #include #endif #include "fea/fibconfig.hh" #include "fea/data_plane/control_socket/netlink_socket_utilities.hh" #include "fibconfig_entry_set_netlink_socket.hh" // // Set single-entry information into the unicast forwarding table. // // The mechanism to set the information is netlink(7) sockets. // FibConfigEntrySetNetlinkSocket::FibConfigEntrySetNetlinkSocket(FeaDataPlaneManager& fea_data_plane_manager) : FibConfigEntrySet(fea_data_plane_manager), NetlinkSocket(fea_data_plane_manager.eventloop(), fea_data_plane_manager.fibconfig().get_netlink_filter_table_id()), _ns_reader(*(NetlinkSocket *)this) { } FibConfigEntrySetNetlinkSocket::~FibConfigEntrySetNetlinkSocket() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the netlink(7) sockets mechanism to set " "information about forwarding table from the underlying " "system: %s", error_msg.c_str()); } } /** Routing table ID that we are interested in might have changed. */ int FibConfigEntrySetNetlinkSocket::notify_table_id_change(uint32_t new_tbl) { return NetlinkSocket::notify_table_id_change(new_tbl); } int FibConfigEntrySetNetlinkSocket::start(string& error_msg) { if (_is_running) return (XORP_OK); if (NetlinkSocket::start(error_msg) != XORP_OK) return (XORP_ERROR); _is_running = true; return (XORP_OK); } int FibConfigEntrySetNetlinkSocket::stop(string& error_msg) { if (! _is_running) return (XORP_OK); if (NetlinkSocket::stop(error_msg) != XORP_OK) return (XORP_ERROR); _is_running = false; return (XORP_OK); } int FibConfigEntrySetNetlinkSocket::add_entry4(const Fte4& fte) { FteX ftex(fte); return (add_entry(ftex)); } int FibConfigEntrySetNetlinkSocket::delete_entry4(const Fte4& fte) { FteX ftex(fte); return (delete_entry(ftex)); } int FibConfigEntrySetNetlinkSocket::add_entry6(const Fte6& fte) { FteX ftex(fte); return (add_entry(ftex)); } int FibConfigEntrySetNetlinkSocket::delete_entry6(const Fte6& fte) { FteX ftex(fte); return (delete_entry(ftex)); } int FibConfigEntrySetNetlinkSocket::add_entry(const FteX& fte) { static const size_t buffer_size = sizeof(struct rtmsg) + 3*sizeof(struct rtattr) + sizeof(int) + 512; union { uint8_t data[buffer_size]; struct nlmsghdr nlh; } buffer; struct nlmsghdr* nlh = &buffer.nlh; struct sockaddr_nl snl; struct rtmsg* rtmsg; struct rtattr* rtattr; int rta_len; uint8_t* data; NetlinkSocket& ns = *this; int family = fte.net().af(); uint32_t if_index = 0; void* rta_align_data; uint32_t table_id = RT_TABLE_MAIN; // Default value debug_msg("add_entry " "(network = %s nexthop = %s)", fte.net().str().c_str(), fte.nexthop().str().c_str()); // Check that the family is supported do { if (fte.nexthop().is_ipv4()) { if (! fea_data_plane_manager().have_ipv4()) return (XORP_ERROR); break; } if (fte.nexthop().is_ipv6()) { if (! fea_data_plane_manager().have_ipv6()) return (XORP_ERROR); break; } break; } while (false); if (fte.is_connected_route()) return (XORP_OK); // XXX: don't add/remove directly-connected routes memset(&buffer, 0, sizeof(buffer)); // Set the socket memset(&snl, 0, sizeof(snl)); snl.nl_family = AF_NETLINK; snl.nl_pid = 0; // nl_pid = 0 if destination is the kernel snl.nl_groups = 0; // // Set the request // nlh->nlmsg_len = NLMSG_LENGTH(sizeof(*rtmsg)); nlh->nlmsg_type = RTM_NEWROUTE; nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE | NLM_F_ACK; nlh->nlmsg_seq = ns.seqno(); nlh->nlmsg_pid = ns.nl_pid(); rtmsg = static_cast(NLMSG_DATA(nlh)); rtmsg->rtm_family = family; rtmsg->rtm_dst_len = fte.net().prefix_len(); // The destination mask length rtmsg->rtm_src_len = 0; rtmsg->rtm_tos = 0; rtmsg->rtm_protocol = RTPROT_XORP; // Mark this as a XORP route rtmsg->rtm_scope = RT_SCOPE_UNIVERSE; rtmsg->rtm_type = RTN_UNICAST; rtmsg->rtm_flags = RTM_F_NOTIFY; // // Set the routing/forwarding table ID. // If the table ID is <= 0xff, then we set it in rtmsg->rtm_table, // otherwise we set rtmsg->rtm_table to RT_TABLE_UNSPEC and add the // real value as an RTA_TABLE attribute. // if (fibconfig().unicast_forwarding_table_id_is_configured(family)) table_id = fibconfig().unicast_forwarding_table_id(family); if (table_id <= 0xff) rtmsg->rtm_table = table_id; else rtmsg->rtm_table = RT_TABLE_UNSPEC; // Add the destination address as an attribute rta_len = RTA_LENGTH(fte.net().masked_addr().addr_bytelen()); if (NLMSG_ALIGN(nlh->nlmsg_len) + rta_len > sizeof(buffer)) { XLOG_FATAL("AF_NETLINK buffer size error: %u instead of %u", XORP_UINT_CAST(sizeof(buffer)), XORP_UINT_CAST(NLMSG_ALIGN(nlh->nlmsg_len) + rta_len)); } rtattr = RTM_RTA(rtmsg); rtattr->rta_type = RTA_DST; rtattr->rta_len = rta_len; data = static_cast(RTA_DATA(rtattr)); fte.net().masked_addr().copy_out(data); nlh->nlmsg_len = NLMSG_ALIGN(nlh->nlmsg_len) + rta_len; // Add the nexthop address as an attribute if (fte.nexthop() != IPvX::ZERO(family)) { rta_len = RTA_LENGTH(fte.nexthop().addr_bytelen()); if (NLMSG_ALIGN(nlh->nlmsg_len) + rta_len > sizeof(buffer)) { XLOG_FATAL("AF_NETLINK buffer size error: %u instead of %u", XORP_UINT_CAST(sizeof(buffer)), XORP_UINT_CAST(NLMSG_ALIGN(nlh->nlmsg_len) + rta_len)); } rta_align_data = reinterpret_cast(rtattr) + RTA_ALIGN(rtattr->rta_len); rtattr = static_cast(rta_align_data); rtattr->rta_type = RTA_GATEWAY; rtattr->rta_len = rta_len; data = static_cast(RTA_DATA(rtattr)); fte.nexthop().copy_out(data); nlh->nlmsg_len = NLMSG_ALIGN(nlh->nlmsg_len) + rta_len; } // Get the interface index, if it exists do { // // Check for discard and unreachable routes. // The referenced ifname must have respectively the discard or the // unreachable property. These use a separate route type in netlink // land. Unlike BSD, it need not reference a loopback interface. // Because this interface exists only in the FEA, it has no index. // if (fte.ifname().empty()) break; const IfTree& iftree = fibconfig().merged_config_iftree(); const IfTreeInterface* ifp = iftree.find_interface(fte.ifname()); if (ifp == NULL) { XLOG_ERROR("Invalid interface name: %s", fte.ifname().c_str()); return (XORP_ERROR); } if (ifp->discard()) { rtmsg->rtm_type = RTN_BLACKHOLE; break; } if (ifp->unreachable()) { rtmsg->rtm_type = RTN_UNREACHABLE; break; } const IfTreeVif* vifp = ifp->find_vif(fte.vifname()); if (vifp == NULL) { XLOG_ERROR("Invalid interface name %s vif name: %s", fte.ifname().c_str(), fte.vifname().c_str()); return (XORP_ERROR); } if_index = vifp->pif_index(); if (if_index != 0) break; XLOG_ERROR("Could not find interface index for interface %s vif %s", fte.ifname().c_str(), fte.vifname().c_str()); return XORP_ERROR; } while (false); // // If the interface has an index in the host stack, add it // as an attribute. // if (if_index != 0) { int int_if_index = if_index; rta_len = RTA_LENGTH(sizeof(int_if_index)); if (NLMSG_ALIGN(nlh->nlmsg_len) + rta_len > sizeof(buffer)) { XLOG_FATAL("AF_NETLINK buffer size error: %u instead of %u", XORP_UINT_CAST(sizeof(buffer)), XORP_UINT_CAST(NLMSG_ALIGN(nlh->nlmsg_len) + rta_len)); } rta_align_data = reinterpret_cast(rtattr) + RTA_ALIGN(rtattr->rta_len); rtattr = static_cast(rta_align_data); rtattr->rta_type = RTA_OIF; rtattr->rta_len = rta_len; data = static_cast(RTA_DATA(rtattr)); memcpy(data, &int_if_index, sizeof(int_if_index)); nlh->nlmsg_len = NLMSG_ALIGN(nlh->nlmsg_len) + rta_len; } // Add the route priority as an attribute int int_priority = fte.metric(); rta_len = RTA_LENGTH(sizeof(int_priority)); if (NLMSG_ALIGN(nlh->nlmsg_len) + rta_len > sizeof(buffer)) { XLOG_FATAL("AF_NETLINK buffer size error: %u instead of %u", XORP_UINT_CAST(sizeof(buffer)), XORP_UINT_CAST(NLMSG_ALIGN(nlh->nlmsg_len) + rta_len)); } rta_align_data = reinterpret_cast(rtattr) + RTA_ALIGN(rtattr->rta_len); rtattr = static_cast(rta_align_data); rtattr->rta_type = RTA_PRIORITY; rtattr->rta_len = rta_len; data = static_cast(RTA_DATA(rtattr)); memcpy(data, &int_priority, sizeof(int_priority)); nlh->nlmsg_len = NLMSG_ALIGN(nlh->nlmsg_len) + rta_len; #ifdef HAVE_NETLINK_SOCKET_ATTRIBUTE_RTA_TABLE // Add the table ID as an attribute if (table_id > 0xff) { uint32_t uint32_table_id = table_id; rta_len = RTA_LENGTH(sizeof(uint32_table_id)); if (NLMSG_ALIGN(nlh->nlmsg_len) + rta_len > sizeof(buffer)) { XLOG_FATAL("AF_NETLINK buffer size error: %u instead of %u", XORP_UINT_CAST(sizeof(buffer)), XORP_UINT_CAST(NLMSG_ALIGN(nlh->nlmsg_len) + rta_len)); } rta_align_data = reinterpret_cast(rtattr) + RTA_ALIGN(rtattr->rta_len); rtattr = static_cast(rta_align_data); rtattr->rta_type = RTA_TABLE; rtattr->rta_len = rta_len; data = static_cast(RTA_DATA(rtattr)); memcpy(data, &uint32_table_id, sizeof(uint32_table_id)); nlh->nlmsg_len = NLMSG_ALIGN(nlh->nlmsg_len) + rta_len; } #endif // HAVE_NETLINK_SOCKET_ATTRIBUTE_RTA_TABLE // // XXX: the Linux kernel doesn't keep the admin distance, hence // we don't add it. // string error_msg; int last_errno = 0; if (ns.sendto(&buffer, nlh->nlmsg_len, 0, reinterpret_cast(&snl), sizeof(snl)) != (ssize_t)nlh->nlmsg_len) { XLOG_ERROR("Error writing to netlink socket: %s", strerror(errno)); return (XORP_ERROR); } if (NlmUtils::check_netlink_request(_ns_reader, ns, nlh->nlmsg_seq, last_errno, error_msg) != XORP_OK) { XLOG_ERROR("Error checking netlink request: %s", error_msg.c_str()); return (XORP_ERROR); } return (XORP_OK); } int FibConfigEntrySetNetlinkSocket::delete_entry(const FteX& fte) { static const size_t buffer_size = sizeof(struct rtmsg) + 3*sizeof(struct rtattr) + sizeof(int) + 512; union { uint8_t data[buffer_size]; struct nlmsghdr nlh; } buffer; struct nlmsghdr* nlh = &buffer.nlh; struct sockaddr_nl snl; struct rtmsg* rtmsg; struct rtattr* rtattr; int rta_len; uint8_t* data; NetlinkSocket& ns = *this; int family = fte.net().af(); void* rta_align_data; uint32_t table_id = RT_TABLE_MAIN; // Default value UNUSED(rta_align_data); debug_msg("delete_entry " "(network = %s nexthop = %s)", fte.net().str().c_str(), fte.nexthop().str().c_str()); // Check that the family is supported do { if (fte.nexthop().is_ipv4()) { if (! fea_data_plane_manager().have_ipv4()) return (XORP_ERROR); break; } if (fte.nexthop().is_ipv6()) { if (! fea_data_plane_manager().have_ipv6()) return (XORP_ERROR); break; } break; } while (false); if (fte.is_connected_route()) return (XORP_OK); // XXX: don't add/remove directly-connected routes memset(&buffer, 0, sizeof(buffer)); // Set the socket memset(&snl, 0, sizeof(snl)); snl.nl_family = AF_NETLINK; snl.nl_pid = 0; // nl_pid = 0 if destination is the kernel snl.nl_groups = 0; // // Set the request // nlh->nlmsg_len = NLMSG_LENGTH(sizeof(*rtmsg)); nlh->nlmsg_type = RTM_DELROUTE; nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE | NLM_F_ACK; nlh->nlmsg_seq = ns.seqno(); nlh->nlmsg_pid = ns.nl_pid(); rtmsg = static_cast(NLMSG_DATA(nlh)); rtmsg->rtm_family = family; rtmsg->rtm_dst_len = fte.net().prefix_len(); // The destination mask length rtmsg->rtm_src_len = 0; rtmsg->rtm_tos = 0; rtmsg->rtm_protocol = RTPROT_XORP; // Mark this as a XORP route rtmsg->rtm_scope = RT_SCOPE_UNIVERSE; rtmsg->rtm_type = RTN_UNICAST; rtmsg->rtm_flags = RTM_F_NOTIFY; // // Set the routing/forwarding table ID. // If the table ID is <= 0xff, then we set it in rtmsg->rtm_table, // otherwise we set rtmsg->rtm_table to RT_TABLE_UNSPEC and add the // real value as an RTA_TABLE attribute. // if (fibconfig().unicast_forwarding_table_id_is_configured(family)) table_id = fibconfig().unicast_forwarding_table_id(family); if (table_id <= 0xff) rtmsg->rtm_table = table_id; else rtmsg->rtm_table = RT_TABLE_UNSPEC; // Add the destination address as an attribute rta_len = RTA_LENGTH(fte.net().masked_addr().addr_bytelen()); if (NLMSG_ALIGN(nlh->nlmsg_len) + rta_len > sizeof(buffer)) { XLOG_FATAL("AF_NETLINK buffer size error: %u instead of %u", XORP_UINT_CAST(sizeof(buffer)), XORP_UINT_CAST(NLMSG_ALIGN(nlh->nlmsg_len) + rta_len)); } rtattr = RTM_RTA(rtmsg); rtattr->rta_type = RTA_DST; rtattr->rta_len = rta_len; data = static_cast(RTA_DATA(rtattr)); fte.net().masked_addr().copy_out(data); nlh->nlmsg_len = NLMSG_ALIGN(nlh->nlmsg_len) + rta_len; #ifdef HAVE_NETLINK_SOCKET_ATTRIBUTE_RTA_TABLE // Add the table ID as an attribute if (table_id > 0xff) { uint32_t uint32_table_id = table_id; rta_len = RTA_LENGTH(sizeof(uint32_table_id)); if (NLMSG_ALIGN(nlh->nlmsg_len) + rta_len > sizeof(buffer)) { XLOG_FATAL("AF_NETLINK buffer size error: %u instead of %u", XORP_UINT_CAST(sizeof(buffer)), XORP_UINT_CAST(NLMSG_ALIGN(nlh->nlmsg_len) + rta_len)); } rta_align_data = reinterpret_cast(rtattr) + RTA_ALIGN(rtattr->rta_len); rtattr = static_cast(rta_align_data); rtattr->rta_type = RTA_TABLE; rtattr->rta_len = rta_len; data = static_cast(RTA_DATA(rtattr)); memcpy(data, &uint32_table_id, sizeof(uint32_table_id)); nlh->nlmsg_len = NLMSG_ALIGN(nlh->nlmsg_len) + rta_len; } #endif // HAVE_NETLINK_SOCKET_ATTRIBUTE_RTA_TABLE do { // // When deleting a route which points to a discard or unreachable // interface, pass the correct route type to the kernel. // if (fte.ifname().empty()) break; const IfTree& iftree = fibconfig().merged_config_iftree(); const IfTreeInterface* ifp = iftree.find_interface(fte.ifname()); // // XXX: unlike adding a route, we don't use XLOG_ASSERT() // to check whether the interface is configured in the system. // E.g., on startup we may attempt to clean-up leftover XORP // routes while we still don't have any interface tree configuration. // if (ifp != NULL) { if (ifp->discard()) { rtmsg->rtm_type = RTN_BLACKHOLE; break; } if (ifp->unreachable()) { rtmsg->rtm_type = RTN_UNREACHABLE; break; } } break; } while (false); int last_errno = 0; string error_msg; if (ns.sendto(&buffer, nlh->nlmsg_len, 0, reinterpret_cast(&snl), sizeof(snl)) != (ssize_t)nlh->nlmsg_len) { XLOG_ERROR("Error writing to netlink socket: %s", strerror(errno)); return (XORP_ERROR); } if (NlmUtils::check_netlink_request(_ns_reader, ns, nlh->nlmsg_seq, last_errno, error_msg) != XORP_OK) { // // XXX: If the outgoing interface was taken down earlier, then // most likely the kernel has removed the matching forwarding // entries on its own. Hence, check whether all of the following // is true: // - the error code matches // - the outgoing interface is down // // If all conditions are true, then ignore the error and consider // the deletion was success. // Note that we could add to the following list the check whether // the forwarding entry is not in the kernel, but this is probably // an overkill. If such check should be performed, we should // use the corresponding FibConfigTableGetNetlink plugin. // // If the route doesn't exist, maybe something else deleted it // for some reason. Don't fail commits on this particular error. // --Ben if (last_errno == ESRCH) { XLOG_WARNING("Delete route entry failed, route was already gone (will continue), route: %s", fte.str().c_str()); return XORP_OK; } #if 0 do { // Check whether the error code matches if (last_errno != ESRCH) { // // XXX: The "No such process" error code is used by the // kernel to indicate there is no such forwarding entry // to delete. // break; } // Check whether the interface is down if (fte.ifname().empty()) break; // No interface to check const IfTree& iftree = fibconfig().system_config_iftree(); const IfTreeVif* vifp = iftree.find_vif(fte.ifname(), fte.vifname()); if ((vifp != NULL) && vifp->enabled()) break; // The interface is UP return (XORP_OK); } while (false); #endif XLOG_ERROR("Error checking netlink delete_entry request: %s", error_msg.c_str()); return (XORP_ERROR); } return (XORP_OK); } #endif // HAVE_NETLINK_SOCKETS xorp/fea/data_plane/fibconfig/fibconfig_table_observer_netlink_socket.hh0000664000076400007640000000515311540225523027055 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_TABLE_OBSERVER_NETLINK_SOCKET_HH__ #define __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_TABLE_OBSERVER_NETLINK_SOCKET_HH__ #include #ifdef HAVE_NETLINK_SOCKETS #include "fea/fibconfig_table_observer.hh" #include "fea/data_plane/control_socket/netlink_socket.hh" class FibConfigTableObserverNetlinkSocket : public FibConfigTableObserver, public NetlinkSocket, public NetlinkSocketObserver { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ FibConfigTableObserverNetlinkSocket(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~FibConfigTableObserverNetlinkSocket(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg); /** * Receive data from the underlying system. * * @param buffer the buffer with the received data. */ virtual void receive_data(const vector& buffer); void netlink_socket_data(const vector& buffer); /** Routing table ID that we are interested in might have changed. */ virtual int notify_table_id_change(uint32_t new_tbl) { return NetlinkSocket::notify_table_id_change(new_tbl); } private: }; #endif #endif // __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_TABLE_OBSERVER_NETLINK_SOCKET_HH__ xorp/fea/data_plane/fibconfig/fibconfig_table_get_dummy.cc0000664000076400007640000000471011421137511024105 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "fea/fibconfig.hh" #include "fibconfig_table_get_dummy.hh" // // Get the whole table information from the unicast forwarding table. // // The mechanism to obtain the information is Dummy (for testing purpose). // FibConfigTableGetDummy::FibConfigTableGetDummy(FeaDataPlaneManager& fea_data_plane_manager) : FibConfigTableGet(fea_data_plane_manager) { } FibConfigTableGetDummy::~FibConfigTableGetDummy() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the Dummy mechanism to get " "whole forwarding table from the underlying " "system: %s", error_msg.c_str()); } } int FibConfigTableGetDummy::start(string& error_msg) { UNUSED(error_msg); if (_is_running) return (XORP_OK); _is_running = true; return (XORP_OK); } int FibConfigTableGetDummy::stop(string& error_msg) { UNUSED(error_msg); if (! _is_running) return (XORP_OK); _is_running = false; return (XORP_OK); } int FibConfigTableGetDummy::get_table4(list& fte_list) { Trie4::iterator ti; for (ti = fibconfig().trie4().begin(); ti != fibconfig().trie4().end(); ++ti) { const Fte4& fte = ti.payload(); fte_list.push_back(fte); } return (XORP_OK); } int FibConfigTableGetDummy::get_table6(list& fte_list) { Trie6::iterator ti; for (ti = fibconfig().trie6().begin(); ti != fibconfig().trie6().end(); ++ti) { const Fte6& fte = ti.payload(); fte_list.push_back(fte); } return (XORP_OK); } xorp/fea/data_plane/fibconfig/fibconfig_entry_set_iphelper.hh0000664000076400007640000000624211540224222024662 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_ENTRY_SET_IPHELPER_HH__ #define __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_ENTRY_SET_IPHELPER_HH__ #include "fea/fibconfig_entry_set.hh" class FibConfigEntrySetIPHelper : public FibConfigEntrySet { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ FibConfigEntrySetIPHelper(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~FibConfigEntrySetIPHelper(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg); /** * Add a single IPv4 forwarding entry. * * Must be within a configuration interval. * * @param fte the entry to add. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int add_entry4(const Fte4& fte); /** * Delete a single IPv4 forwarding entry. * * Must be with a configuration interval. * * @param fte the entry to delete. Only destination and netmask are used. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int delete_entry4(const Fte4& fte); /** * Add a single IPv6 forwarding entry. * * Must be within a configuration interval. * * @param fte the entry to add. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int add_entry6(const Fte6& fte); /** * Delete a single IPv6 forwarding entry. * * Must be within a configuration interval. * * @param fte the entry to delete. Only destination and netmask are used. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int delete_entry6(const Fte6& fte); virtual int notify_table_id_change(uint32_t new_tbl) { UNUSED(new_tbl); return 0; } private: int add_entry(const FteX& fte); int delete_entry(const FteX& fte); }; #endif // __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_ENTRY_SET_IPHELPER_HH__ xorp/fea/data_plane/fibconfig/fibconfig_entry_get_click.cc0000664000076400007640000001153711540225522024120 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include #ifdef XORP_USE_CLICK #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvxnet.hh" #include "fea/fibconfig.hh" #include "fibconfig_entry_get_click.hh" // // Get single-entry information from the unicast forwarding table. // // The mechanism to obtain the information is Click: // http://www.read.cs.ucla.edu/click/ // FibConfigEntryGetClick::FibConfigEntryGetClick(FeaDataPlaneManager& fea_data_plane_manager) : FibConfigEntryGet(fea_data_plane_manager), ClickSocket(fea_data_plane_manager.eventloop()), _cs_reader(*(ClickSocket *)this) { } FibConfigEntryGetClick::~FibConfigEntryGetClick() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the Click mechanism to get " "information about forwarding table from the underlying " "system: %s", error_msg.c_str()); } } int FibConfigEntryGetClick::start(string& error_msg) { if (! ClickSocket::is_enabled()) return (XORP_OK); if (_is_running) return (XORP_OK); if (ClickSocket::start(error_msg) != XORP_OK) return (XORP_ERROR); _is_running = true; return (XORP_OK); } int FibConfigEntryGetClick::stop(string& error_msg) { int ret_value = XORP_OK; if (! _is_running) return (XORP_OK); ret_value = ClickSocket::stop(error_msg); _is_running = false; return (ret_value); } int FibConfigEntryGetClick::lookup_route_by_dest4(const IPv4& dst, Fte4& fte) { list fte_list4; bool found = false; // // XXX: Get the whole table, and then scan it entry-by-entry // for the longest-prefix match. // TODO: This implementation is very inefficient. // if (fibconfig().get_table4(fte_list4) != XORP_OK) return (XORP_ERROR); list::iterator iter4; for (iter4 = fte_list4.begin(); iter4 != fte_list4.end(); ++iter4) { Fte4& fte4 = *iter4; if (! fte4.net().contains(dst)) continue; if ((! found) || fte.net().contains(fte4.net())) { fte = fte4; found = true; } } if (! found) return (XORP_ERROR); return (XORP_OK); } int FibConfigEntryGetClick::lookup_route_by_network4(const IPv4Net& dst, Fte4& fte) { list fte_list4; // // XXX: Get the whole table, and then scan it entry-by-entry // for the exact match. // TODO: This implementation is very inefficient. // if (fibconfig().get_table4(fte_list4) != XORP_OK) return (XORP_ERROR); list::iterator iter4; for (iter4 = fte_list4.begin(); iter4 != fte_list4.end(); ++iter4) { Fte4& fte4 = *iter4; if (fte4.net() == dst) { fte = fte4; return (XORP_OK); } } return (XORP_ERROR); } int FibConfigEntryGetClick::lookup_route_by_dest6(const IPv6& dst, Fte6& fte) { list fte_list6; bool found = false; // // XXX: Get the whole table, and then scan it entry-by-entry // for the longest-prefix match. // TODO: This implementation is very inefficient. // if (fibconfig().get_table6(fte_list6) != XORP_OK) return (XORP_ERROR); list::iterator iter6; for (iter6 = fte_list6.begin(); iter6 != fte_list6.end(); ++iter6) { Fte6& fte6 = *iter6; if (! fte6.net().contains(dst)) continue; if ((! found) || fte.net().contains(fte6.net())) { fte = fte6; found = true; } } if (! found) return (XORP_ERROR); return (XORP_OK); } int FibConfigEntryGetClick::lookup_route_by_network6(const IPv6Net& dst, Fte6& fte) { list fte_list6; // // XXX: Get the whole table, and then scan it entry-by-entry // for the longest-prefix match. // TODO: This implementation is very inefficient. // if (fibconfig().get_table6(fte_list6) != XORP_OK) return (XORP_ERROR); list::iterator iter6; for (iter6 = fte_list6.begin(); iter6 != fte_list6.end(); ++iter6) { Fte6& fte6 = *iter6; if (fte6.net() == dst) { fte = fte6; return (XORP_OK); } } return (XORP_ERROR); } #endif //click xorp/fea/data_plane/fibconfig/fibconfig_entry_set_dummy.cc0000664000076400007640000000750211421137511024175 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "fea/fibconfig.hh" #include "fibconfig_entry_set_dummy.hh" // // Set single-entry information into the unicast forwarding table. // // The mechanism to set the information is Dummy (for testing purpose). // FibConfigEntrySetDummy::FibConfigEntrySetDummy(FeaDataPlaneManager& fea_data_plane_manager) : FibConfigEntrySet(fea_data_plane_manager) { } FibConfigEntrySetDummy::~FibConfigEntrySetDummy() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the Dummy mechanism to set " "information about forwarding table from the underlying " "system: %s", error_msg.c_str()); } } int FibConfigEntrySetDummy::start(string& error_msg) { UNUSED(error_msg); if (_is_running) return (XORP_OK); _is_running = true; return (XORP_OK); } int FibConfigEntrySetDummy::stop(string& error_msg) { UNUSED(error_msg); if (! _is_running) return (XORP_OK); _is_running = false; return (XORP_OK); } int FibConfigEntrySetDummy::add_entry4(const Fte4& fte) { if (in_configuration() == false) return (XORP_ERROR); if (fte.is_connected_route()) { // XXX: accept directly-connected routes } int rc = fibconfig().trie4().route_count(); XLOG_ASSERT(rc >= 0); fibconfig().trie4().insert(fte.net(), fte); if (fibconfig().trie4().route_count() == rc) { XLOG_WARNING("add_entry4 is overriding old entry for %s (%d %d)", fte.net().str().c_str(), rc, fibconfig().trie4().route_count()); } return (XORP_OK); } int FibConfigEntrySetDummy::delete_entry4(const Fte4& fte) { if (in_configuration() == false) return (XORP_ERROR); if (fte.is_connected_route()) { // XXX: accept directly-connected routes } Trie4::iterator ti = fibconfig().trie4().find(fte.net()); if (ti == fibconfig().trie4().end()) return (XORP_ERROR); fibconfig().trie4().erase(ti); return (XORP_OK); } int FibConfigEntrySetDummy::add_entry6(const Fte6& fte) { if (in_configuration() == false) return (XORP_ERROR); if (fte.is_connected_route()) { // XXX: accept directly-connected routes } int rc = fibconfig().trie6().route_count(); XLOG_ASSERT(rc >= 0); fibconfig().trie6().insert(fte.net(), fte); if (fibconfig().trie6().route_count() == rc) { XLOG_WARNING("add_entry6 is overriding old entry for %s (%d %d)", fte.net().str().c_str(), rc, fibconfig().trie6().route_count()); } return (XORP_OK); } int FibConfigEntrySetDummy::delete_entry6(const Fte6& fte) { if (in_configuration() == false) return (XORP_ERROR); if (fte.is_connected_route()) { // XXX: accept directly-connected routes } Trie6::iterator ti = fibconfig().trie6().find(fte.net()); if (ti == fibconfig().trie6().end()) return (XORP_ERROR); fibconfig().trie6().erase(ti); return (XORP_OK); } xorp/fea/data_plane/fibconfig/fibconfig_table_set_click.cc0000664000076400007640000001152111540225523024054 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include #ifdef XORP_USE_CLICK #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "fea/fibconfig.hh" #include "fibconfig_table_set_click.hh" // // Set whole-table information into the unicast forwarding table. // // The mechanism to set the information is Click: // http://www.read.cs.ucla.edu/click/ // FibConfigTableSetClick::FibConfigTableSetClick(FeaDataPlaneManager& fea_data_plane_manager) : FibConfigTableSet(fea_data_plane_manager), ClickSocket(fea_data_plane_manager.eventloop()), _cs_reader(*(ClickSocket *)this) { } FibConfigTableSetClick::~FibConfigTableSetClick() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the Click mechanism to set " "whole forwarding table from the underlying " "system: %s", error_msg.c_str()); } } int FibConfigTableSetClick::start(string& error_msg) { if (! ClickSocket::is_enabled()) return (XORP_OK); if (_is_running) return (XORP_OK); if (ClickSocket::start(error_msg) != XORP_OK) return (XORP_ERROR); // Cleanup any leftover entries from previously run XORP instance if (! fibconfig().unicast_forwarding_entries_retain_on_startup4()) delete_all_entries4(); if (! fibconfig().unicast_forwarding_entries_retain_on_startup6()) delete_all_entries6(); _is_running = true; // // XXX: Push the current config into the new method // list fte_list4; if (fibconfig().get_table4(fte_list4) == XORP_OK) { if (set_table4(fte_list4) != XORP_OK) { XLOG_ERROR("Cannot push the current IPv4 forwarding table " "into the FibConfigTableSetClick plugin for setting " "the forwarding table"); } } #ifdef HAVE_IPV6 list fte_list6; if (fibconfig().get_table6(fte_list6) == XORP_OK) { if (set_table6(fte_list6) != XORP_OK) { XLOG_ERROR("Cannot push the current IPv6 forwarding table " "into the FibConfigTableSetClick plugin for setting " "the forwarding table"); } } #endif // HAVE_IPV6 return (XORP_OK); } int FibConfigTableSetClick::stop(string& error_msg) { int ret_value = XORP_OK; if (! _is_running) return (XORP_OK); // Delete the XORP entries if (! fibconfig().unicast_forwarding_entries_retain_on_shutdown4()) delete_all_entries4(); if (! fibconfig().unicast_forwarding_entries_retain_on_shutdown6()) delete_all_entries6(); ret_value = ClickSocket::stop(error_msg); _is_running = false; return (ret_value); } int FibConfigTableSetClick::set_table4(const list& fte_list) { list::const_iterator iter; // Add the entries one-by-one for (iter = fte_list.begin(); iter != fte_list.end(); ++iter) { const Fte4& fte = *iter; fibconfig().add_entry4(fte); } return (XORP_OK); } int FibConfigTableSetClick::delete_all_entries4() { list fte_list; list::const_iterator iter; // Get the list of all entries fibconfig().get_table4(fte_list); // Delete the entries one-by-one for (iter = fte_list.begin(); iter != fte_list.end(); ++iter) { const Fte4& fte = *iter; if (fte.xorp_route()) fibconfig().delete_entry4(fte); } return (XORP_OK); } int FibConfigTableSetClick::set_table6(const list& fte_list) { list::const_iterator iter; // Add the entries one-by-one for (iter = fte_list.begin(); iter != fte_list.end(); ++iter) { const Fte6& fte = *iter; fibconfig().add_entry6(fte); } return (XORP_OK); } int FibConfigTableSetClick::delete_all_entries6() { list fte_list; list::const_iterator iter; // Get the list of all entries fibconfig().get_table6(fte_list); // Delete the entries one-by-one for (iter = fte_list.begin(); iter != fte_list.end(); ++iter) { const Fte6& fte = *iter; if (fte.xorp_route()) fibconfig().delete_entry6(fte); } return (XORP_OK); } #endif // click xorp/fea/data_plane/fibconfig/fibconfig_forwarding_proc_linux.hh0000664000076400007640000001047711421137511025371 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2007-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/data_plane/fibconfig/fibconfig_forwarding_proc_linux.hh,v 1.6 2008/10/02 21:56:58 bms Exp $ #ifndef __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_FORWARDING_PROC_LINUX_HH__ #define __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_FORWARDING_PROC_LINUX_HH__ #include "fea/fibconfig_forwarding.hh" class FibConfigForwardingProcLinux : public FibConfigForwarding { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ FibConfigForwardingProcLinux(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~FibConfigForwardingProcLinux(); /** * Test whether the IPv4 unicast forwarding engine is enabled or disabled * to forward packets. * * @param ret_value if true on return, then the IPv4 unicast forwarding * is enabled, otherwise is disabled. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int unicast_forwarding_enabled4(bool& ret_value, string& error_msg) const; /** * Test whether the IPv6 unicast forwarding engine is enabled or disabled * to forward packets. * * @param ret_value if true on return, then the IPv6 unicast forwarding * is enabled, otherwise is disabled. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int unicast_forwarding_enabled6(bool& ret_value, string& error_msg) const; /** * Test whether the acceptance of IPv6 Router Advertisement messages is * enabled or disabled. * * @param ret_value if true on return, then the acceptance of IPv6 Router * Advertisement messages is enabled, otherwise is disabled. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int accept_rtadv_enabled6(bool& ret_value, string& error_msg) const; /** * Set the IPv4 unicast forwarding engine to enable or disable forwarding * of packets. * * @param v if true, then enable IPv4 unicast forwarding, otherwise * disable it. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int set_unicast_forwarding_enabled4(bool v, string& error_msg); /** * Set the IPv6 unicast forwarding engine to enable or disable forwarding * of packets. * * @param v if true, then enable IPv6 unicast forwarding, otherwise * disable it. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int set_unicast_forwarding_enabled6(bool v, string& error_msg); /** * Enable or disable the acceptance of IPv6 Router Advertisement messages * from other routers. It should be enabled for hosts, and disabled for * routers. * * @param v if true, then enable the acceptance of IPv6 Router * Advertisement messages, otherwise disable it. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int set_accept_rtadv_enabled6(bool v, string& error_msg); private: static const string PROC_LINUX_FORWARDING_FILE_V4; static const string PROC_LINUX_FORWARDING_FILE_V6; }; #endif // __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_FORWARDING_PROC_LINUX_HH__ xorp/fea/data_plane/fibconfig/fibconfig_forwarding_solaris.cc0000664000076400007640000003645211421137511024652 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libcomm/comm_api.h" #ifdef HAVE_FCNTL_H #include #endif #ifdef HAVE_STROPTS_H #include #endif #ifdef HAVE_INET_ND_H #include #endif #include "fea/fibconfig.hh" #include "fibconfig_forwarding_solaris.hh" #define DEV_SOLARIS_DRIVER_FORWARDING_V4 "/dev/ip" #define DEV_SOLARIS_DRIVER_FORWARDING_V6 "/dev/ip6" #define DEV_SOLARIS_DRIVER_PARAMETER_FORWARDING_V4 "ip_forwarding" #define DEV_SOLARIS_DRIVER_PARAMETER_FORWARDING_V6 "ip6_forwarding" #define DEV_SOLARIS_DRIVER_PARAMETER_IGNORE_REDIRECT_V6 "ip6_ignore_redirect" // // Configure unicast forwarding. // // The mechanism to get/set the information is Solaris /dev device driver. // #ifdef HOST_OS_SOLARIS FibConfigForwardingSolaris::FibConfigForwardingSolaris( FeaDataPlaneManager& fea_data_plane_manager) : FibConfigForwarding(fea_data_plane_manager) { } FibConfigForwardingSolaris::~FibConfigForwardingSolaris() { } int FibConfigForwardingSolaris::unicast_forwarding_enabled4(bool& ret_value, string& error_msg) const { int enabled = 0; struct strioctl strioctl; char buf[256]; int fd, r; if (! fea_data_plane_manager().have_ipv4()) { ret_value = false; error_msg = c_format("Cannot test whether IPv4 unicast forwarding " "is enabled: IPv4 is not supported"); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } // // Open the device // fd = open(DEV_SOLARIS_DRIVER_FORWARDING_V4, O_RDONLY); if (fd < 0) { error_msg = c_format("Cannot open file %s for reading: %s", DEV_SOLARIS_DRIVER_FORWARDING_V4, strerror(errno)); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } r = isastream(fd); if (r < 0) { error_msg = c_format("Error testing whether file %s is a stream: %s", DEV_SOLARIS_DRIVER_FORWARDING_V4, strerror(errno)); XLOG_ERROR("%s", error_msg.c_str()); close(fd); return (XORP_ERROR); } if (r == 0) { error_msg = c_format("File %s is not a stream", DEV_SOLARIS_DRIVER_FORWARDING_V4); XLOG_ERROR("%s", error_msg.c_str()); close(fd); return (XORP_ERROR); } // // Read the value from the device // memset(&strioctl, 0, sizeof(strioctl)); memset(buf, 0, sizeof(buf)); strncpy(buf, DEV_SOLARIS_DRIVER_PARAMETER_FORWARDING_V4, sizeof(buf) - 1); strioctl.ic_cmd = ND_GET; strioctl.ic_timout = 0; strioctl.ic_len = sizeof(buf); strioctl.ic_dp = buf; if (ioctl(fd, I_STR, &strioctl) < 0) { error_msg = c_format("Error testing whether IPv4 unicast " "forwarding is enabled: %s", strerror(errno)); XLOG_ERROR("%s", error_msg.c_str()); close(fd); return (XORP_ERROR); } if (sscanf(buf, "%d", &enabled) != 1) { error_msg = c_format("Error reading result %s: %s", buf, strerror(errno)); XLOG_ERROR("%s", error_msg.c_str()); close(fd); return (XORP_ERROR); } close(fd); if (enabled > 0) ret_value = true; else ret_value = false; return (XORP_OK); } int FibConfigForwardingSolaris::unicast_forwarding_enabled6(bool& ret_value, string& error_msg) const { int enabled = 0; struct strioctl strioctl; char buf[256]; int fd, r; if (! fea_data_plane_manager().have_ipv6()) { ret_value = false; error_msg = c_format("Cannot test whether IPv6 unicast forwarding " "is enabled: IPv6 is not supported"); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } // // Open the device // fd = open(DEV_SOLARIS_DRIVER_FORWARDING_V6, O_RDONLY); if (fd < 0) { error_msg = c_format("Cannot open file %s for reading: %s", DEV_SOLARIS_DRIVER_FORWARDING_V6, strerror(errno)); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } r = isastream(fd); if (r < 0) { error_msg = c_format("Error testing whether file %s is a stream: %s", DEV_SOLARIS_DRIVER_FORWARDING_V6, strerror(errno)); XLOG_ERROR("%s", error_msg.c_str()); close(fd); return (XORP_ERROR); } if (r == 0) { error_msg = c_format("File %s is not a stream", DEV_SOLARIS_DRIVER_FORWARDING_V6); XLOG_ERROR("%s", error_msg.c_str()); close(fd); return (XORP_ERROR); } // // Read the value from the device memset(&strioctl, 0, sizeof(strioctl)); memset(buf, 0, sizeof(buf)); strncpy(buf, DEV_SOLARIS_DRIVER_PARAMETER_FORWARDING_V6, sizeof(buf) - 1); strioctl.ic_cmd = ND_GET; strioctl.ic_timout = 0; strioctl.ic_len = sizeof(buf); strioctl.ic_dp = buf; if (ioctl(fd, I_STR, &strioctl) < 0) { error_msg = c_format("Error testing whether IPv6 unicast " "forwarding is enabled: %s", strerror(errno)); XLOG_ERROR("%s", error_msg.c_str()); close(fd); return (XORP_ERROR); } if (sscanf(buf, "%d", &enabled) != 1) { error_msg = c_format("Error reading result %s: %s", buf, strerror(errno)); XLOG_ERROR("%s", error_msg.c_str()); close(fd); return (XORP_ERROR); } close(fd); if (enabled > 0) ret_value = true; else ret_value = false; return (XORP_OK); } int FibConfigForwardingSolaris::accept_rtadv_enabled6(bool& ret_value, string& error_msg) const { int enabled = 0; struct strioctl strioctl; char buf[256]; int fd, r; int ignore_redirect = 0; if (! fea_data_plane_manager().have_ipv6()) { ret_value = false; error_msg = c_format("Cannot test whether the acceptance of IPv6 " "Router Advertisement messages is enabled: " "IPv6 is not supported"); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } // // Open the device // fd = open(DEV_SOLARIS_DRIVER_FORWARDING_V6, O_RDONLY); if (fd < 0) { error_msg = c_format("Cannot open file %s for reading: %s", DEV_SOLARIS_DRIVER_FORWARDING_V6, strerror(errno)); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } r = isastream(fd); if (r < 0) { error_msg = c_format("Error testing whether file %s is a stream: %s", DEV_SOLARIS_DRIVER_FORWARDING_V6, strerror(errno)); XLOG_ERROR("%s", error_msg.c_str()); close(fd); return (XORP_ERROR); } if (r == 0) { error_msg = c_format("File %s is not a stream", DEV_SOLARIS_DRIVER_FORWARDING_V6); XLOG_ERROR("%s", error_msg.c_str()); close(fd); return (XORP_ERROR); } // // Read the value from the device // memset(&strioctl, 0, sizeof(strioctl)); memset(buf, 0, sizeof(buf)); strncpy(buf, DEV_SOLARIS_DRIVER_PARAMETER_IGNORE_REDIRECT_V6, sizeof(buf) - 1); strioctl.ic_cmd = ND_GET; strioctl.ic_timout = 0; strioctl.ic_len = sizeof(buf); strioctl.ic_dp = buf; if (ioctl(fd, I_STR, &strioctl) < 0) { error_msg = c_format("Error testing whether the acceptance of " "IPv6 Router Advertisement messages is " "enabled: %s", strerror(errno)); XLOG_ERROR("%s", error_msg.c_str()); close(fd); return (XORP_ERROR); } if (sscanf(buf, "%d", &ignore_redirect) != 1) { error_msg = c_format("Error reading result %s: %s", buf, strerror(errno)); XLOG_ERROR("%s", error_msg.c_str()); close(fd); return (XORP_ERROR); } close(fd); // // XXX: The logic of "Accept IPv6 Router Advertisement" is just the // opposite of "Ignore Redirect". // if (ignore_redirect == 0) enabled = 1; else enabled = 0; if (enabled > 0) ret_value = true; else ret_value = false; return (XORP_OK); } int FibConfigForwardingSolaris::set_unicast_forwarding_enabled4(bool v, string& error_msg) { int enable = (v) ? 1 : 0; bool old_value; struct strioctl strioctl; char buf[256]; int fd, r; if (! fea_data_plane_manager().have_ipv4()) { if (! v) { // // XXX: we assume that "not supported" == "disable", hence // return OK. // return (XORP_OK); } error_msg = c_format("Cannot set IPv4 unicast forwarding to %s: " "IPv4 is not supported", bool_c_str(v)); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } // // Get the old value // if (unicast_forwarding_enabled4(old_value, error_msg) != XORP_OK) return (XORP_ERROR); if (old_value == v) return (XORP_OK); // Nothing changed // // Open the device // fd = open(DEV_SOLARIS_DRIVER_FORWARDING_V4, O_WRONLY); if (fd < 0) { error_msg = c_format("Cannot open file %s for writing: %s", DEV_SOLARIS_DRIVER_FORWARDING_V4, strerror(errno)); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } r = isastream(fd); if (r < 0) { error_msg = c_format("Error testing whether file %s is a stream: %s", DEV_SOLARIS_DRIVER_FORWARDING_V4, strerror(errno)); XLOG_ERROR("%s", error_msg.c_str()); close(fd); return (XORP_ERROR); } if (r == 0) { error_msg = c_format("File %s is not a stream", DEV_SOLARIS_DRIVER_FORWARDING_V4); XLOG_ERROR("%s", error_msg.c_str()); close(fd); return (XORP_ERROR); } // // Write the value to the device // memset(&strioctl, 0, sizeof(strioctl)); memset(buf, 0, sizeof(buf)); snprintf(buf, sizeof(buf) - 1, "%s %d", DEV_SOLARIS_DRIVER_PARAMETER_FORWARDING_V4, enable); strioctl.ic_cmd = ND_SET; strioctl.ic_timout = 0; strioctl.ic_len = sizeof(buf); strioctl.ic_dp = buf; if (ioctl(fd, I_STR, &strioctl) < 0) { error_msg = c_format("Cannot set IPv4 unicast forwarding to %s: %s", bool_c_str(v), strerror(errno)); XLOG_ERROR("%s", error_msg.c_str()); close(fd); return (XORP_ERROR); } close(fd); return (XORP_OK); } int FibConfigForwardingSolaris::set_unicast_forwarding_enabled6(bool v, string& error_msg) { int enable = (v) ? 1 : 0; bool old_value, old_value_accept_rtadv; struct strioctl strioctl; char buf[256]; int fd, r; if (! fea_data_plane_manager().have_ipv6()) { if (! v) { // // XXX: we assume that "not supported" == "disable", hence // return OK. // return (XORP_OK); } error_msg = c_format("Cannot set IPv6 unicast forwarding to %s: " "IPv6 is not supported", bool_c_str(v)); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } // // Get the old value // if (unicast_forwarding_enabled6(old_value, error_msg) != XORP_OK) return (XORP_ERROR); if (accept_rtadv_enabled6(old_value_accept_rtadv, error_msg) != XORP_OK) return (XORP_ERROR); if ((old_value == v) && (old_value_accept_rtadv == !v)) return (XORP_OK); // Nothing changed // // Set the IPv6 Router Advertisement value // if (set_accept_rtadv_enabled6(!v, error_msg) != XORP_OK) return (XORP_ERROR); // // Open the device // fd = open(DEV_SOLARIS_DRIVER_FORWARDING_V6, O_WRONLY); if (fd < 0) { error_msg = c_format("Cannot open file %s for writing: %s", DEV_SOLARIS_DRIVER_FORWARDING_V6, strerror(errno)); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } r = isastream(fd); if (r < 0) { error_msg = c_format("Error testing whether file %s is a stream: %s", DEV_SOLARIS_DRIVER_FORWARDING_V6, strerror(errno)); XLOG_ERROR("%s", error_msg.c_str()); close(fd); return (XORP_ERROR); } if (r == 0) { error_msg = c_format("File %s is not a stream", DEV_SOLARIS_DRIVER_FORWARDING_V6); XLOG_ERROR("%s", error_msg.c_str()); close(fd); return (XORP_ERROR); } // // Write the value to the device // memset(&strioctl, 0, sizeof(strioctl)); memset(buf, 0, sizeof(buf)); snprintf(buf, sizeof(buf) - 1, "%s %d", DEV_SOLARIS_DRIVER_PARAMETER_FORWARDING_V6, enable); strioctl.ic_cmd = ND_SET; strioctl.ic_timout = 0; strioctl.ic_len = sizeof(buf); strioctl.ic_dp = buf; if (ioctl(fd, I_STR, &strioctl) < 0) { error_msg = c_format("Cannot set IPv6 unicast forwarding to %s: %s", bool_c_str(v), strerror(errno)); XLOG_ERROR("%s", error_msg.c_str()); close(fd); return (XORP_ERROR); } close(fd); return (XORP_OK); } int FibConfigForwardingSolaris::set_accept_rtadv_enabled6(bool v, string& error_msg) { int enable = (v) ? 1 : 0; bool old_value; struct strioctl strioctl; char buf[256]; int fd, r; int ignore_redirect = 0; if (! fea_data_plane_manager().have_ipv6()) { if (! v) { // // XXX: we assume that "not supported" == "disable", hence // return OK. // return (XORP_OK); } error_msg = c_format("Cannot set the acceptance of IPv6 " "Router Advertisement messages to %s: " "IPv6 is not supported", bool_c_str(v)); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } // // Get the old value // if (accept_rtadv_enabled6(old_value, error_msg) != XORP_OK) return (XORP_ERROR); if (old_value == v) return (XORP_OK); // Nothing changed // // XXX: The logic of "Accept IPv6 Router Advertisement" is just the // opposite of "Ignore Redirect". // if (enable == 0) ignore_redirect = 1; else ignore_redirect = 0; // // Open the device // fd = open(DEV_SOLARIS_DRIVER_FORWARDING_V6, O_WRONLY); if (fd < 0) { error_msg = c_format("Cannot open file %s for writing: %s", DEV_SOLARIS_DRIVER_FORWARDING_V6, strerror(errno)); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } r = isastream(fd); if (r < 0) { error_msg = c_format("Error testing whether file %s is a stream: %s", DEV_SOLARIS_DRIVER_FORWARDING_V6, strerror(errno)); XLOG_ERROR("%s", error_msg.c_str()); close(fd); return (XORP_ERROR); } if (r == 0) { error_msg = c_format("File %s is not a stream", DEV_SOLARIS_DRIVER_FORWARDING_V6); XLOG_ERROR("%s", error_msg.c_str()); close(fd); return (XORP_ERROR); } // // Write the value to the device // memset(&strioctl, 0, sizeof(strioctl)); memset(buf, 0, sizeof(buf)); snprintf(buf, sizeof(buf) - 1, "%s %d", DEV_SOLARIS_DRIVER_PARAMETER_IGNORE_REDIRECT_V6, ignore_redirect); strioctl.ic_cmd = ND_SET; strioctl.ic_timout = 0; strioctl.ic_len = sizeof(buf); strioctl.ic_dp = buf; if (ioctl(fd, I_STR, &strioctl) < 0) { error_msg = c_format("Cannot set IPv6 unicast forwarding to %s: %s", bool_c_str(v), strerror(errno)); XLOG_ERROR("%s", error_msg.c_str()); close(fd); return (XORP_ERROR); } close(fd); return (XORP_OK); } #endif // HOST_OS_SOLARIS xorp/fea/data_plane/fibconfig/fibconfig_table_set_dummy.hh0000664000076400007640000000630011540225523024133 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/data_plane/fibconfig/fibconfig_table_set_dummy.hh,v 1.9 2008/10/02 21:57:00 bms Exp $ #ifndef __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_TABLE_SET_DUMMY_HH__ #define __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_TABLE_SET_DUMMY_HH__ #include "fea/fibconfig_table_set.hh" class FibConfigTableSetDummy : public FibConfigTableSet { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ FibConfigTableSetDummy(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~FibConfigTableSetDummy(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg); /** * Set the IPv4 unicast forwarding table. * * @param fte_list the list with all entries to install into * the IPv4 unicast forwarding table. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int set_table4(const list& fte_list); /** * Delete all entries in the IPv4 unicast forwarding table. * * Must be within a configuration interval. * * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int delete_all_entries4(); /** * Set the IPv6 unicast forwarding table. * * @param fte_list the list with all entries to install into * the IPv6 unicast forwarding table. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int set_table6(const list& fte_list); /** * Delete all entries in the IPv6 unicast forwarding table. * * Must be within a configuration interval. * * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int delete_all_entries6(); /** Routing table ID that we are interested in might have changed. */ virtual int notify_table_id_change(uint32_t new_tbl) { UNUSED(new_tbl); return XORP_OK; } private: }; #endif // __FEA_DATA_PLANE_FIBCONFIG_FIBCONFIG_TABLE_SET_DUMMY_HH__ xorp/fea/data_plane/fibconfig/fibconfig_table_observer_iphelper.cc0000664000076400007640000000456511540224222025640 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "fea/fibconfig.hh" #include "fibconfig_table_observer_iphelper.hh" // // Observe whole-table information change about the unicast forwarding table. // // E.g., if the forwarding table has changed, then the information // received by the observer would NOT specify the particular entry that // has changed. // // The mechanism to observe the information is the IP helper API for // Windows (IPHLPAPI.DLL). // #ifdef HOST_OS_WINDOWS FibConfigTableObserverIPHelper::FibConfigTableObserverIPHelper(FeaDataPlaneManager& fea_data_plane_manager) : FibConfigTableObserver(fea_data_plane_manager) { } FibConfigTableObserverIPHelper::~FibConfigTableObserverIPHelper() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the IP Helper mechanism to observe " "whole forwarding table from the underlying " "system: %s", error_msg.c_str()); } } int FibConfigTableObserverIPHelper::start(string& error_msg) { UNUSED(error_msg); if (_is_running) return (XORP_OK); _is_running = true; return (XORP_OK); } int FibConfigTableObserverIPHelper::stop(string& error_msg) { UNUSED(error_msg); if (! _is_running) return (XORP_OK); _is_running = false; return (XORP_OK); } void FibConfigTableObserverIPHelper::receive_data(const vector& buffer) { debug_msg("called\n"); UNUSED(buffer); } #endif // HOST_OS_WINDOWS xorp/fea/data_plane/ifconfig/0000775000076400007640000000000011703345405016271 5ustar greearbgreearbxorp/fea/data_plane/ifconfig/ifconfig_observer_routing_socket.cc0000664000076400007640000000700411540225524025411 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include #ifdef HAVE_ROUTING_SOCKETS #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "fea/ifconfig.hh" #include "ifconfig_get_sysctl.hh" #include "ifconfig_observer_routing_socket.hh" // // Observe information change about network interface configuration from // the underlying system. // // The mechanism to observe the information is routing sockets. // IfConfigObserverRoutingSocket::IfConfigObserverRoutingSocket(FeaDataPlaneManager& fea_data_plane_manager) : IfConfigObserver(fea_data_plane_manager), RoutingSocket(fea_data_plane_manager.eventloop()), RoutingSocketObserver(*(RoutingSocket *)this) { } IfConfigObserverRoutingSocket::~IfConfigObserverRoutingSocket() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the routing sockets mechanism to observe " "information about network interfaces from the underlying " "system: %s", error_msg.c_str()); } } int IfConfigObserverRoutingSocket::start(string& error_msg) { if (_is_running) return (XORP_OK); if (RoutingSocket::start(error_msg) != XORP_OK) return (XORP_ERROR); _is_running = true; return (XORP_OK); } int IfConfigObserverRoutingSocket::stop(string& error_msg) { if (! _is_running) return (XORP_OK); if (RoutingSocket::stop(error_msg) != XORP_OK) return (XORP_ERROR); _is_running = false; return (XORP_OK); } void IfConfigObserverRoutingSocket::receive_data(const vector& buffer) { // Pre-processing cleanup ifconfig().system_config().finalize_state(); if (IfConfigGetSysctl::parse_buffer_routing_socket( ifconfig(), ifconfig().system_config(), buffer) != XORP_OK) { return; } // // Get the VLAN vif info // IfConfigVlanGet* ifconfig_vlan_get; ifconfig_vlan_get = fea_data_plane_manager().ifconfig_vlan_get(); if (ifconfig_vlan_get != NULL) { bool modified = false; // Ignore this...assume it's always modified. if (ifconfig_vlan_get->pull_config(ifconfig().system_config(), modified) != XORP_OK) { XLOG_ERROR("Unknown error while pulling VLAN information"); } } // // Propagate the changes from the system config to the merged config // IfTree& merged_config = ifconfig().merged_config(); merged_config.align_with_observed_changes(ifconfig().system_config(), ifconfig().user_config()); ifconfig().report_updates(merged_config); merged_config.finalize_state(); } void IfConfigObserverRoutingSocket::routing_socket_data(const vector& buffer) { receive_data(buffer); } #endif // HAVE_ROUTING_SOCKETS xorp/fea/data_plane/ifconfig/ifconfig_get_ioctl.hh0000664000076400007640000000616311540225523022432 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/data_plane/ifconfig/ifconfig_get_ioctl.hh,v 1.11 2008/10/02 21:57:04 bms Exp $ #ifndef __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_GET_IOCTL_HH__ #define __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_GET_IOCTL_HH__ #include #if defined(HAVE_IOCTL_SIOCGIFCONF) && !defined(HAVE_NETLINK_SOCKETS) #include "fea/ifconfig_get.hh" class IfConfigGetIoctl : public IfConfigGet { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ IfConfigGetIoctl(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~IfConfigGetIoctl(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg); /** * Pull the network interface information from the underlying system. * * @param iftree the IfTree storage to store the pulled information. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int pull_config(const IfTree* local_config, IfTree& iftree); /** * Parse information about network interface configuration change from * the underlying system. * * The information to parse is in "struct ifreq" format * (e.g., obtained by ioctl(SIOCGIFCONF) mechanism). * * @param ifconfig the IfConfig instance. * @param iftree the IfTree storage to store the parsed information. * @param family the address family to consider only (e.g., AF_INET * or AF_INET6 for IPv4 and IPv6 respectively). * @param buffer the buffer with the data to parse. * @return XORP_OK on success, otherwise XORP_ERROR. * @see IfTree. */ static int parse_buffer_ioctl(IfConfig& ifconfig, IfTree& iftree, int family, const vector& buffer); private: int read_config(IfTree& iftree); int _s4; int _s6; }; #endif #endif // __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_GET_IOCTL_HH__ xorp/fea/data_plane/ifconfig/ifconfig_get_dummy.hh0000664000076400007640000000432211421137511022443 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/data_plane/ifconfig/ifconfig_get_dummy.hh,v 1.10 2008/10/02 21:57:04 bms Exp $ #ifndef __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_GET_DUMMY_HH__ #define __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_GET_DUMMY_HH__ #include "fea/ifconfig_get.hh" class IfConfigGetDummy : public IfConfigGet { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ IfConfigGetDummy(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~IfConfigGetDummy(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg); /** * Pull the network interface information from the underlying system. * * @param iftree the IfTree storage to store the pulled information. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int pull_config(const IfTree* local_config, IfTree& iftree); private: }; #endif // __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_GET_DUMMY_HH__ xorp/fea/data_plane/ifconfig/ifconfig_set_iphelper.cc0000664000076400007640000003763511540224223023136 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/win_io.h" #include "libxorp/ipvx.hh" #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_IPHLPAPI_H #include #endif #include "fea/ifconfig.hh" #include "ifconfig_set_iphelper.hh" // // Set information about network interfaces configuration with the // underlying system. // // The mechanism to set the information is the IP Helper API for // Windows (IPHLPAPI.DLL). // #ifdef HOST_OS_WINDOWS IfConfigSetIPHelper::IfConfigSetIPHelper(FeaDataPlaneManager& fea_data_plane_manager) : IfConfigSet(fea_data_plane_manager) { } IfConfigSetIPHelper::~IfConfigSetIPHelper() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the IP Helper API mechanism to set " "information about network interfaces into the underlying " "system: %s", error_msg.c_str()); } } int IfConfigSetIPHelper::start(string& error_msg) { UNUSED(error_msg); if (_is_running) return (XORP_OK); _is_running = true; return (XORP_OK); } int IfConfigSetIPHelper::stop(string& error_msg) { UNUSED(error_msg); if (! _is_running) return (XORP_OK); _is_running = false; return (XORP_OK); } bool IfConfigSetIPHelper::is_discard_emulated(const IfTreeInterface& i) const { UNUSED(i); return (false); } bool IfConfigSetIPHelper::is_unreachable_emulated(const IfTreeInterface& i) const { UNUSED(i); return (false); } int IfConfigSetIPHelper::config_begin(string& error_msg) { // XXX: nothing to do UNUSED(error_msg); return (XORP_OK); } int IfConfigSetIPHelper::config_end(string& error_msg) { // XXX: nothing to do UNUSED(error_msg); return (XORP_OK); } int IfConfigSetIPHelper::config_interface_begin(const IfTreeInterface* pulled_ifp, IfTreeInterface& config_iface, string& error_msg) { if (pulled_ifp == NULL) { // Nothing to do: the interface has been deleted from the system return (XORP_OK); } // // Set the MTU // if (config_iface.mtu() != pulled_ifp->mtu()) { error_msg = c_format("Cannot set the MTU to %u on " "interface %s: method not supported", config_iface.mtu(), config_iface.ifname().c_str()); return (XORP_ERROR); } // // Set the MAC address // if (config_iface.mac() != pulled_ifp->mac()) { error_msg = c_format("Cannot set the MAC address to %s " "on interface %s: method not supported", config_iface.mac().str().c_str(), config_iface.ifname().c_str()); return (XORP_ERROR); } return (XORP_OK); } int IfConfigSetIPHelper::config_interface_end(const IfTreeInterface* pulled_ifp, const IfTreeInterface& config_iface, string& error_msg) { if (pulled_ifp == NULL) { // Nothing to do: the interface has been deleted from the system return (XORP_OK); } // // Set the interface status // if (config_iface.enabled() != pulled_ifp->enabled()) { if (set_interface_status(config_iface.ifname(), config_iface.pif_index(), config_iface.interface_flags(), config_iface.enabled(), error_msg) != XORP_OK) { return (XORP_ERROR); } } return (XORP_OK); } int IfConfigSetIPHelper::config_vif_begin(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeInterface& config_iface, const IfTreeVif& config_vif, string& error_msg) { UNUSED(pulled_ifp); UNUSED(config_iface); UNUSED(config_vif); UNUSED(error_msg); if (pulled_vifp == NULL) { // Nothing to do: the vif has been deleted from the system return (XORP_OK); } // XXX: nothing to do return (XORP_OK); } int IfConfigSetIPHelper::config_vif_end(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeInterface& config_iface, const IfTreeVif& config_vif, string& error_msg) { UNUSED(pulled_ifp); if (pulled_vifp == NULL) { // Nothing to do: the vif has been deleted from the system return (XORP_OK); } // // XXX: If the interface name and vif name are different, then // they might have different status: the interface can be UP, while // the vif can be DOWN. // if (config_iface.ifname() != config_vif.vifname()) { // // Set the vif status // if (config_vif.enabled() != pulled_vifp->enabled()) { // // XXX: The interface and vif status setting mechanism is // equivalent for this platform. // if (set_interface_status(config_vif.vifname(), config_vif.pif_index(), config_vif.vif_flags(), config_vif.enabled(), error_msg) != XORP_OK) { return (XORP_ERROR); } } } return (XORP_OK); } int IfConfigSetIPHelper::config_add_address(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeAddr4* pulled_addrp, const IfTreeInterface& config_iface, const IfTreeVif& config_vif, const IfTreeAddr4& config_addr, string& error_msg) { UNUSED(pulled_ifp); UNUSED(pulled_vifp); // // Test whether a new address // do { if (pulled_addrp == NULL) break; if (pulled_addrp->addr() != config_addr.addr()) break; if (pulled_addrp->broadcast() != config_addr.broadcast()) break; if (pulled_addrp->broadcast() && (pulled_addrp->bcast() != config_addr.bcast())) { break; } if (pulled_addrp->point_to_point() != config_addr.point_to_point()) break; if (pulled_addrp->point_to_point() && (pulled_addrp->endpoint() != config_addr.endpoint())) { break; } if (pulled_addrp->prefix_len() != config_addr.prefix_len()) break; // XXX: Same address, therefore ignore it return (XORP_OK); } while (false); // // Delete the old address if necessary // if (pulled_addrp != NULL) { if (delete_addr(config_iface.ifname(), config_vif.vifname(), config_vif.pif_index(), config_addr.addr(), config_addr.prefix_len(), error_msg) != XORP_OK) { return (XORP_ERROR); } } // // Add the address // if (add_addr(config_iface.ifname(), config_vif.vifname(), config_vif.pif_index(), config_addr.addr(), config_addr.prefix_len(), config_addr.broadcast(), config_addr.bcast(), config_addr.point_to_point(), config_addr.endpoint(), error_msg) != XORP_OK) { return (XORP_ERROR); } return (XORP_OK); } int IfConfigSetIPHelper::config_delete_address(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeAddr4* pulled_addrp, const IfTreeInterface& config_iface, const IfTreeVif& config_vif, const IfTreeAddr4& config_addr, string& error_msg) { UNUSED(pulled_ifp); UNUSED(pulled_vifp); UNUSED(pulled_addrp); // // Delete the address // if (delete_addr(config_iface.ifname(), config_vif.vifname(), config_vif.pif_index(), config_addr.addr(), config_addr.prefix_len(), error_msg) != XORP_OK) { return (XORP_ERROR); } return (XORP_OK); } int IfConfigSetIPHelper::config_add_address(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeAddr6* pulled_addrp, const IfTreeInterface& config_iface, const IfTreeVif& config_vif, const IfTreeAddr6& config_addr, string& error_msg) { UNUSED(pulled_ifp); UNUSED(pulled_vifp); // // Test whether a new address // do { if (pulled_addrp == NULL) break; if (pulled_addrp->addr() != config_addr.addr()) break; if (pulled_addrp->point_to_point() != config_addr.point_to_point()) break; if (pulled_addrp->point_to_point() && (pulled_addrp->endpoint() != config_addr.endpoint())) { break; } if (pulled_addrp->prefix_len() != config_addr.prefix_len()) break; // XXX: Same address, therefore ignore it return (XORP_OK); } while (false); // // Delete the old address if necessary // if (pulled_addrp != NULL) { if (delete_addr(config_iface.ifname(), config_vif.vifname(), config_vif.pif_index(), config_addr.addr(), config_addr.prefix_len(), error_msg) != XORP_OK) { return (XORP_ERROR); } } // // Add the address // if (add_addr(config_iface.ifname(), config_vif.vifname(), config_vif.pif_index(), config_addr.addr(), config_addr.prefix_len(), config_addr.point_to_point(), config_addr.endpoint(), error_msg) != XORP_OK) { return (XORP_ERROR); } return (XORP_OK); } int IfConfigSetIPHelper::config_delete_address(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeAddr6* pulled_addrp, const IfTreeInterface& config_iface, const IfTreeVif& config_vif, const IfTreeAddr6& config_addr, string& error_msg) { UNUSED(pulled_ifp); UNUSED(pulled_vifp); UNUSED(pulled_addrp); // // Delete the address // if (delete_addr(config_iface.ifname(), config_vif.vifname(), config_vif.pif_index(), config_addr.addr(), config_addr.prefix_len(), error_msg) != XORP_OK) { return (XORP_ERROR); } return (XORP_OK); } int IfConfigSetIPHelper::set_interface_status(const string& ifname, uint32_t if_index, uint32_t interface_flags, bool is_enabled, string& error_msg) { // TODO: implement/test it! UNUSED(ifname); UNUSED(if_index); UNUSED(interface_flags); UNUSED(is_enabled); UNUSED(error_msg); #if 0 MIB_IFROW ifrow; DWORD result; UNUSED(interface_flags); memset(&ifrow, 0, sizeof(ifrow)); ifrow.dwIndex = if_index; result = GetIfEntry(&ifrow); if (result != NO_ERROR) { error_msg = c_format("Cannot obtain existing MIB_IFROW for " "interface %s: error %d\n", ifname.c_str(), (int)result); return (XORP_ERROR); } if (is_enabled) ifrow.dwAdminStatus = MIB_IF_ADMIN_STATUS_UP; else ifrow.dwAdminStatus = MIB_IF_ADMIN_STATUS_DOWN; result = SetIfEntry(&ifrow); if (result != NO_ERROR) { error_msg = c_format("Cannot set administrative status of " "interface %s: error %d\n", ifname.c_str(), (int)result); return (XORP_ERROR); } #endif // 0 return (XORP_OK); } int IfConfigSetIPHelper::add_addr(const string& ifname, const string& vifname, uint32_t if_index, const IPv4& addr, uint32_t prefix_len, bool is_broadcast, const IPv4& broadcast_addr, bool is_point_to_point, const IPv4& endpoint_addr, string& error_msg) { PMIB_IPADDRTABLE pAddrTable = NULL; DWORD result, tries; ULONG dwSize; IPAddr ipaddr; IPMask ipmask; UNUSED(is_broadcast); UNUSED(broadcast_addr); UNUSED(is_point_to_point); UNUSED(endpoint_addr); addr.copy_out((uint8_t*)&ipaddr); IPv4 prefix_addr = IPv4::make_prefix(prefix_len); prefix_addr.copy_out((uint8_t*)&ipmask); tries = 0; result = ERROR_INSUFFICIENT_BUFFER; dwSize = sizeof(*pAddrTable) + (30 * sizeof(MIB_IPADDRROW)); do { pAddrTable = (PMIB_IPADDRTABLE) ((tries == 0) ? malloc(dwSize) : realloc(pAddrTable, dwSize)); if (pAddrTable == NULL) break; result = GetIpAddrTable(pAddrTable, &dwSize, FALSE); if (pAddrTable == NULL) break; } while ((++tries < 3) || (result == ERROR_INSUFFICIENT_BUFFER)); if (result != NO_ERROR) { XLOG_ERROR("GetIpAddrTable(): %s", win_strerror(result)); if (pAddrTable != NULL) free(pAddrTable); return (XORP_OK); } for (unsigned int i = 0; i < pAddrTable->dwNumEntries; i++) { if (pAddrTable->table[i].dwAddr == ipaddr) { XLOG_WARNING("IP address %s already exists on interface " "with index %lu", addr.str().c_str(), pAddrTable->table[i].dwIndex); return (XORP_OK); } } ULONG ntectx = 0, nteinst = 0; result = AddIPAddress(ipaddr, ipmask, if_index, &ntectx, &nteinst); if (result != NO_ERROR) { error_msg = c_format("Cannot add address '%s' " "on interface '%s' vif '%s': error %d", addr.str().c_str(), ifname.c_str(), vifname.c_str(), (int)result); return (XORP_OK); } // // We can't delete IP addresses using IP Helper unless we cache the // returned NTE context. This means we can only delete addresses // which were created during the lifetime of the FEA. // // Also, these entries have to be keyed by the address we added, // so we can figure out which ntectx value to use when deleting it. // _nte_map.insert(make_pair(make_pair(if_index, (IPAddr)addr.addr()), ntectx)); return (XORP_OK); } int IfConfigSetIPHelper::delete_addr(const string& ifname, const string& vifname, uint32_t if_index, const IPv4& addr, uint32_t prefix_len, string& error_msg) { map, ULONG>::iterator ii; UNUSED(prefix_len); ii = _nte_map.find(make_pair(if_index, (IPAddr)addr.addr())); if (ii == _nte_map.end()) { error_msg = c_format("Cannot delete address '%s' " "on interface '%s' vif '%s': " "address not found in internal table", addr.str().c_str(), ifname.c_str(), vifname.c_str()); return (XORP_ERROR); } ULONG ntectx = ii->second; DWORD result = DeleteIPAddress(ntectx); if (result != NO_ERROR) { error_msg = c_format("Cannot delete address '%s' " "on interface '%s' vif '%s': error %d", addr.str().c_str(), ifname.c_str(), vifname.c_str(), (int)result); return (XORP_ERROR); } return (XORP_OK); } int IfConfigSetIPHelper::add_addr(const string& ifname, const string& vifname, uint32_t if_index, const IPv6& addr, uint32_t prefix_len, bool is_point_to_point, const IPv6& endpoint_addr, string& error_msg) { #ifndef HAVE_IPV6 UNUSED(ifname); UNUSED(vifname); UNUSED(if_index); UNUSED(addr); UNUSED(prefix_len); UNUSED(is_point_to_point); UNUSED(endpoint_addr); error_msg = "IPv6 is not supported"; return (XORP_ERROR); #else // HAVE_IPV6 // // No mechanism to add the address // UNUSED(ifname); UNUSED(vifname); UNUSED(if_index); UNUSED(addr); UNUSED(prefix_len); UNUSED(is_point_to_point); UNUSED(endpoint_addr); error_msg = c_format("No mechanism to add an IPv6 address " "on an interface"); return (XORP_ERROR); #endif // HAVE_IPV6 } int IfConfigSetIPHelper::delete_addr(const string& ifname, const string& vifname, uint32_t if_index, const IPv6& addr, uint32_t prefix_len, string& error_msg) { #ifndef HAVE_IPV6 UNUSED(ifname); UNUSED(vifname); UNUSED(if_index); UNUSED(addr); UNUSED(prefix_len); error_msg = "IPv6 is not supported"; return (XORP_ERROR); #else // HAVE_IPV6 // // No mechanism to delete the address // UNUSED(ifname); UNUSED(vifname); UNUSED(if_index); UNUSED(addr); UNUSED(prefix_len); error_msg = c_format("No mechanism to delete an IPv6 address " "on an interface"); return (XORP_ERROR); #endif } #endif // HOST_OS_WINDOWS xorp/fea/data_plane/ifconfig/ifconfig_get_proc_linux.cc0000664000076400007640000004004211540225524023463 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include #if defined(HAVE_IOCTL_SIOCGIFCONF) && defined(HAVE_PROC_LINUX) && !defined(HAVE_NETLINK_SOCKETS) #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ether_compat.h" #ifdef HAVE_SYS_IOCTL_H #include #endif #ifdef HAVE_NET_IF_H #include #endif #include "fea/ifconfig.hh" #include "fea/data_plane/control_socket/system_utilities.hh" #include "fea/data_plane/managers/fea_data_plane_manager_linux.hh" #include "ifconfig_get_ioctl.hh" #include "ifconfig_get_proc_linux.hh" #include "ifconfig_media.hh" const string IfConfigGetProcLinux::PROC_LINUX_NET_DEVICES_FILE_V4 = "/proc/net/dev"; const string IfConfigGetProcLinux::PROC_LINUX_NET_DEVICES_FILE_V6 = "/proc/net/if_inet6"; // // Get information about network interfaces from the underlying system. // // The mechanism to obtain the information is Linux's /proc/net/dev (for IPv4) // or /proc/net/if_inet6 (for IPv6). // static char* get_proc_linux_iface_name(char* name, char* p); static int proc_read_ifconf_linux(IfConfig& ifconfig, IfTree& iftree, int family, const string& proc_linux_net_device_file); static int if_fetch_linux_v4(IfConfig& ifconfig, IfTree& iftree, const string& proc_linux_net_device_file); #ifdef HAVE_IPV6 static int if_fetch_linux_v6(IfConfig& ifconfig, IfTree& iftree, const string& proc_linux_net_device_file); #endif IfConfigGetProcLinux::IfConfigGetProcLinux(FeaDataPlaneManager& fea_data_plane_manager) : IfConfigGet(fea_data_plane_manager), _ifconfig_get_ioctl(NULL) { } IfConfigGetProcLinux::~IfConfigGetProcLinux() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the Linux's /proc mechanism to get " "information about network interfaces from the underlying " "system: %s", error_msg.c_str()); } } int IfConfigGetProcLinux::start(string& error_msg) { if (_is_running) return (XORP_OK); FeaDataPlaneManagerLinux* manager_linux; manager_linux = dynamic_cast(&fea_data_plane_manager()); if (manager_linux == NULL) { error_msg = c_format("Cannot start the IfConfigGetProcLinux plugin, " "because the data plane manager is not " "FeaDataPlaneManagerLinux"); return (XORP_ERROR); } _ifconfig_get_ioctl = manager_linux->ifconfig_get_ioctl(); if (_ifconfig_get_ioctl == NULL) { error_msg = c_format("Cannot start the IfConfigGetProcLinux plugin, " "because the IfConfigGetIoctl plugin it depends " "on cannot be found"); return (XORP_ERROR); } // XXX: this method relies on the ioctl() method if (_ifconfig_get_ioctl->start(error_msg) != XORP_OK) return (XORP_ERROR); _is_running = true; return (XORP_OK); } int IfConfigGetProcLinux::stop(string& error_msg) { if (! _is_running) return (XORP_OK); // XXX: this method relies on the ioctl() method if (_ifconfig_get_ioctl->stop(error_msg) != XORP_OK) return (XORP_ERROR); _is_running = false; return (XORP_OK); } int IfConfigGetProcLinux::pull_config(const IfTree* local_config, IfTree& iftree) { return read_config(local_config, iftree); } int IfConfigGetProcLinux::read_config(const IfTree* local_config, IfTree& iftree) { // XXX: this method relies on the ioctl() method _ifconfig_get_ioctl->pull_config(local_config, iftree); // // The IPv4 information // if (fea_data_plane_manager().have_ipv4()) { if (proc_read_ifconf_linux(ifconfig(), iftree, AF_INET, PROC_LINUX_NET_DEVICES_FILE_V4) != XORP_OK) return (XORP_ERROR); } #ifdef HAVE_IPV6 // // The IPv6 information // if (fea_data_plane_manager().have_ipv6()) { if (proc_read_ifconf_linux(ifconfig(), iftree, AF_INET6, PROC_LINUX_NET_DEVICES_FILE_V6) != XORP_OK) return (XORP_ERROR); } #endif // HAVE_IPV6 // // Get the VLAN vif info // IfConfigVlanGet* ifconfig_vlan_get; ifconfig_vlan_get = fea_data_plane_manager().ifconfig_vlan_get(); if (ifconfig_vlan_get != NULL) { bool modified = false; if (ifconfig_vlan_get->pull_config(iftree, modified) != XORP_OK) return (XORP_ERROR); } return (XORP_OK); } // // Derived from Redhat Linux ifconfig code // static int proc_read_ifconf_linux(IfConfig& ifconfig, IfTree& iftree, int family, const string& proc_linux_net_device_file) { switch (family) { case AF_INET: // // The IPv4 information // if_fetch_linux_v4(ifconfig, iftree, proc_linux_net_device_file); break; #ifdef HAVE_IPV6 // // The IPv6 information // case AF_INET6: if_fetch_linux_v6(ifconfig, iftree, proc_linux_net_device_file); break; #endif // HAVE_IPV6 default: XLOG_UNREACHABLE(); break; } return (XORP_OK); } static char* get_proc_linux_iface_name(char* name, char* p) { while (xorp_isspace(*p)) p++; if (*p == '\0') return (NULL); while (*p) { if (xorp_isspace(*p)) break; if (*p == ':') { /* could be an alias */ char *dot = p, *dotname = name; *name++ = *p++; while (xorp_isdigit(*p)) *name++ = *p++; if (*p != ':') { /* it wasn't, backup */ p = dot; name = dotname; } if (*p == '\0') return (NULL); p++; break; } *name++ = *p++; } *name++ = '\0'; return (p); } // // IPv4 interface info fetch // // We get only the list of interfaces from /proc/net/dev. // Then, for each interface in that list we check if it is already // in the IfTree that is partially filled-in with // IfConfigGetIoctl::read_config(). // If the interface is in that list, then we ignore it. If the interface is // NOT in that list, then we call IfConfigGetIoctl::parse_buffer_ioctl() // to get the rest of the information about it (such as the interface hardware // address, MTU, etc) by calling various ioctl()'s. // // Why do we need to do this in such complicated way? // Because on Linux ioctl(SIOCGIFCONF) doesn't return information about // tunnel interfaces, and because /proc/net/dev doesn't return information // about IP address aliases (e.g., interface names like eth0:0). // Hence, we have to follow the model used by Linux's ifconfig // (as part of the net-tools collection) to merge the information from // both mechanisms. Enjoy! // static int if_fetch_linux_v4(IfConfig& ifconfig, IfTree& iftree, const string& proc_linux_net_device_file) { FILE *fh; char buf[512]; char *s, ifname[IFNAMSIZ]; fh = fopen(proc_linux_net_device_file.c_str(), "r"); if (fh == NULL) { XLOG_FATAL("Cannot open file %s for reading: %s", proc_linux_net_device_file.c_str(), strerror(errno)); return (XORP_ERROR); } fgets(buf, sizeof(buf), fh); // lose starting 2 lines of comments s = get_proc_linux_iface_name(ifname, buf); if (strcmp(ifname, "Inter-|") != 0) { XLOG_ERROR("%s: improper file contents", proc_linux_net_device_file.c_str()); fclose(fh); return (XORP_ERROR); } fgets(buf, sizeof(buf), fh); s = get_proc_linux_iface_name(ifname, buf); if (strcmp(ifname, "face") != 0) { XLOG_ERROR("%s: improper file contents", proc_linux_net_device_file.c_str()); fclose(fh); return (XORP_ERROR); } while (fgets(buf, sizeof(buf), fh) != NULL) { // // Get the interface name // s = get_proc_linux_iface_name(ifname, buf); if (s == NULL) { XLOG_ERROR("%s: cannot get interface name for line %s", proc_linux_net_device_file.c_str(), buf); continue; } if (iftree.find_interface(ifname) != NULL) { // Already have this interface. Ignore it. continue; } struct ifreq ifreq; memset(&ifreq, 0, sizeof(ifreq)); strncpy(ifreq.ifr_name, ifname, sizeof(ifreq.ifr_name) - 1); vector buffer(sizeof(struct ifreq)); memcpy(&buffer[0], &ifreq, sizeof(ifreq)); IfConfigGetIoctl::parse_buffer_ioctl(ifconfig, iftree, AF_INET, buffer); } if (ferror(fh)) { XLOG_ERROR("%s read failed: %s", proc_linux_net_device_file.c_str(), strerror(errno)); } // Clean up fclose(fh); return (XORP_OK); } // // IPv6 interface info fetch // // Unlike the IPv4 case (see the comments about if_fetch_linux_v4()), // we get the list of interfaces and all the IPv6 address information // from /proc/net/if_inet6. // Then, we use ioctl()'s to obtain information such as interface // hardware address, MTU, etc. // // The reason that we do NOT call IfConfigGetIoctl::parse_buffer_ioctl() // to fill-in the missing information is because in Linux the in6_ifreq // structure has completely different format from the IPv4 equivalent // of "struct ifreq" (i.e., there is no in6_ifreq.ifr_name field), and // that structure is completely different from the BSD structure with // the same name. No comment. // #ifdef HAVE_IPV6 static int if_fetch_linux_v6(IfConfig& ifconfig, IfTree& iftree, const string& proc_linux_net_device_file) { FILE *fh; char devname[IFNAMSIZ+20+1]; char addr6p[8][5], addr6[40]; int plen, scope, dad_status, if_idx; struct ifreq ifreq; XorpFd sock; UNUSED(ifconfig); fh = fopen(proc_linux_net_device_file.c_str(), "r"); if (fh == NULL) { XLOG_FATAL("Cannot open file %s for reading: %s", proc_linux_net_device_file.c_str(), strerror(errno)); return (XORP_ERROR); } sock = socket(AF_INET6, SOCK_DGRAM, 0); if (sock < 0) { XLOG_FATAL("Could not initialize ioctl() socket"); fclose(fh); return (XORP_ERROR); } while (fscanf(fh, "%4s%4s%4s%4s%4s%4s%4s%4s %02x %02x %02x %02x %20s\n", addr6p[0], addr6p[1], addr6p[2], addr6p[3], addr6p[4], addr6p[5], addr6p[6], addr6p[7], &if_idx, &plen, &scope, &dad_status, devname) != EOF) { uint32_t if_index = 0; string if_name, alias_if_name; // // Get the interface name // char tmp_if_name[IFNAMSIZ+1]; strncpy(tmp_if_name, devname, sizeof(tmp_if_name) - 1); tmp_if_name[sizeof(tmp_if_name) - 1] = '\0'; char *cptr; if ( (cptr = strchr(tmp_if_name, ':')) != NULL) { // Replace colon with null. Needed because in Solaris and Linux // the interface name changes for aliases. *cptr = '\0'; } if_name = string(devname); alias_if_name = string(tmp_if_name); debug_msg("interface: %s\n", if_name.c_str()); debug_msg("alias interface: %s\n", alias_if_name.c_str()); // // Get the physical interface index // if_index = if_idx; if (if_index == 0) { XLOG_FATAL("Could not find physical interface index " "for interface %s", if_name.c_str()); } debug_msg("interface index: %u\n", if_index); // // Add the interface (if a new one) // iftree.add_interface(alias_if_name); IfTreeInterface* ifp = iftree.find_interface(alias_if_name); XLOG_ASSERT(ifp != NULL); // // Set the physical interface index for the interface // ifp->set_pif_index(if_index); // // Get the MAC address // do { #ifdef SIOCGIFHWADDR memset(&ifreq, 0, sizeof(ifreq)); strncpy(ifreq.ifr_name, if_name.c_str(), sizeof(ifreq.ifr_name) - 1); if (ioctl(sock, SIOCGIFHWADDR, &ifreq) < 0) { XLOG_ERROR("ioctl(SIOCGIFHWADDR) for interface %s failed: %s", if_name.c_str(), strerror(errno)); } else { struct ether_addr ea; memcpy(&ea, ifreq.ifr_hwaddr.sa_data, sizeof(ea)); ifp->set_mac(Mac(ea)); break; } #endif // SIOCGIFHWADDR break; } while (false); debug_msg("MAC address: %s\n", ifp->mac().str().c_str()); // // Get the MTU // int mtu = 0; memset(&ifreq, 0, sizeof(ifreq)); strncpy(ifreq.ifr_name, if_name.c_str(), sizeof(ifreq.ifr_name) - 1); if (ioctl(sock, SIOCGIFMTU, &ifreq) < 0) { XLOG_ERROR("ioctl(SIOCGIFMTU) for interface %s failed: %s", if_name.c_str(), strerror(errno)); } else { mtu = ifreq.ifr_mtu; } ifp->set_mtu(mtu); debug_msg("MTU: %d\n", ifp->mtu()); // // Get the flags // int flags = 0; // int flags6 = dad_status; memset(&ifreq, 0, sizeof(ifreq)); strncpy(ifreq.ifr_name, if_name.c_str(), sizeof(ifreq.ifr_name) - 1); if (ioctl(sock, SIOCGIFFLAGS, &ifreq) < 0) { XLOG_ERROR("ioctl(SIOCGIFFLAGS) for interface %s failed: %s", if_name.c_str(), strerror(errno)); } else { flags = ifreq.ifr_flags; } ifp->set_interface_flags(flags); ifp->set_enabled(flags & IFF_UP); debug_msg("enabled: %s\n", bool_c_str(ifp->enabled())); // // Get the link status and baudrate // do { bool no_carrier = false; uint64_t baudrate = 0; string error_msg; if (ifconfig_media_get_link_status(if_name, no_carrier, baudrate, error_msg) != XORP_OK) { // XXX: Use the flags if ((flags & IFF_UP) && !(flags & IFF_RUNNING)) no_carrier = true; else no_carrier = false; } ifp->set_no_carrier(no_carrier); ifp->set_baudrate(baudrate); break; } while (false); debug_msg("no_carrier: %s\n", bool_c_str(ifp->no_carrier())); debug_msg("baudrate: %u\n", XORP_UINT_CAST(ifp->baudrate())); // XXX: vifname == ifname on this platform ifp->add_vif(alias_if_name); IfTreeVif* vifp = ifp->find_vif(alias_if_name); XLOG_ASSERT(vifp != NULL); // // Set the physical interface index for the vif // vifp->set_pif_index(if_index); // // Set the vif flags // vifp->set_enabled(ifp->enabled() && (flags & IFF_UP)); vifp->set_broadcast(flags & IFF_BROADCAST); vifp->set_loopback(flags & IFF_LOOPBACK); vifp->set_point_to_point(flags & IFF_POINTOPOINT); vifp->set_multicast(flags & IFF_MULTICAST); vifp->set_vif_flags(flags); debug_msg("vif enabled: %s\n", bool_c_str(vifp->enabled())); debug_msg("vif broadcast: %s\n", bool_c_str(vifp->broadcast())); debug_msg("vif loopback: %s\n", bool_c_str(vifp->loopback())); debug_msg("vif point_to_point: %s\n", bool_c_str(vifp->point_to_point())); debug_msg("vif multicast: %s\n", bool_c_str(vifp->multicast())); // // Get the IP address, netmask, P2P destination // // The default values IPv6 lcl_addr; IPv6 subnet_mask; IPv6 peer_addr; bool has_peer_addr = false; // Get the IP address snprintf(addr6, sizeof(addr6), "%s:%s:%s:%s:%s:%s:%s:%s", addr6p[0], addr6p[1], addr6p[2], addr6p[3], addr6p[4], addr6p[5], addr6p[6], addr6p[7]); lcl_addr = IPv6(addr6); lcl_addr = system_adjust_ipv6_recv(lcl_addr); debug_msg("IP address: %s\n", lcl_addr.str().c_str()); // Get the netmask subnet_mask = IPv6::make_prefix(plen); debug_msg("IP netmask: %s\n", subnet_mask.str().c_str()); // Get the p2p address if (vifp->point_to_point()) { XLOG_FATAL("IPv6 point-to-point address unimplemented"); has_peer_addr = true; debug_msg("Peer address: %s\n", peer_addr.str().c_str()); } debug_msg("\n"); // put an empty line between interfaces // Add the address vifp->add_addr(lcl_addr); IfTreeAddr6* ap = vifp->find_addr(lcl_addr); XLOG_ASSERT(ap != NULL); ap->set_enabled(vifp->enabled() && (flags & IFF_UP)); ap->set_loopback(vifp->loopback() && (flags & IFF_LOOPBACK)); ap->set_point_to_point(vifp->point_to_point() && (flags & IFF_POINTOPOINT)); ap->set_multicast(vifp->multicast() && (flags & IFF_MULTICAST)); ap->set_prefix_len(subnet_mask.mask_len()); if (ap->point_to_point()) ap->set_endpoint(peer_addr); } if (ferror(fh)) { XLOG_ERROR("%s read failed: %s", proc_linux_net_device_file.c_str(), strerror(errno)); } close(sock); fclose(fh); return (XORP_OK); } #endif // HAVE_IPV6 #endif // HAVE_PROC_LINUX xorp/fea/data_plane/ifconfig/ifconfig_property_dummy.hh0000664000076400007640000000371111421137511023551 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2007-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/data_plane/ifconfig/ifconfig_property_dummy.hh,v 1.5 2008/10/02 21:57:06 bms Exp $ #ifndef __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_PROPERTY_DUMMY_HH__ #define __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_PROPERTY_DUMMY_HH__ #include "fea/ifconfig_property.hh" class IfConfigPropertyDummy : public IfConfigProperty { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ IfConfigPropertyDummy(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~IfConfigPropertyDummy(); private: /** * Test whether the underlying system supports IPv4. * * @return true if the underlying system supports IPv4, otherwise false. */ virtual bool test_have_ipv4() const; /** * Test whether the underlying system supports IPv6. * * @return true if the underlying system supports IPv6, otherwise false. */ virtual bool test_have_ipv6() const; }; #endif // __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_PROPERTY_DUMMY_HH__ xorp/fea/data_plane/ifconfig/ifconfig_get_netlink_socket.hh0000664000076400007640000000773611540225523024343 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/data_plane/ifconfig/ifconfig_get_netlink_socket.hh,v 1.11 2008/10/02 21:57:05 bms Exp $ #ifndef __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_GET_NETLINK_SOCKET_HH__ #define __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_GET_NETLINK_SOCKET_HH__ #include #ifdef HAVE_NETLINK_SOCKETS #include "fea/ifconfig_get.hh" #include "fea/data_plane/control_socket/netlink_socket.hh" class IfConfigGetNetlinkSocket : public IfConfigGet, public NetlinkSocket { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ IfConfigGetNetlinkSocket(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~IfConfigGetNetlinkSocket(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg); /** * Pull the network interface information from the underlying system. * * @param iftree the IfTree storage to store the pulled information. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int pull_config(const IfTree* local_cfg, IfTree& iftree); virtual bool can_pull_one() { return can_get_single != 0; } /** If_index can be -1 if unknown: We will try to resolve it from ifname. */ virtual int pull_config_one(IfTree& iftree, const char* ifname, int if_index); /** * Parse information about network interface configuration change from * the underlying system. * * The information to parse is in NETLINK format * (e.g., obtained by netlink(7) sockets mechanism). * * @param ifconfig the IfConfig instance. * @param iftree the IfTree storage to store the parsed information. * @param buffer the buffer with the data to parse. * @return XORP_OK on success, otherwise XORP_ERROR. * @see IfTree. */ static int parse_buffer_netlink_socket(IfConfig& ifconfig, IfTree& iftree, const vector& buffer) { bool modified = false; int nl_errno = 0; return parse_buffer_netlink_socket(ifconfig, iftree, buffer, modified, nl_errno); } /** Same as above, but also return whether or not something was actually modified in iftree. */ static int parse_buffer_netlink_socket(IfConfig& ifconfig, IfTree& iftree, const vector& buffer, bool& modified, int& nl_errno); private: int read_config(const IfTree* local_config, IfTree& iftree); int read_config_all(IfTree& iftree); int read_config_one(IfTree& iftree, const char* ifname, int if_index, int& nl_errno); int try_read_config_one(IfTree& iftree, const char* ifname, int if_index); int can_get_single; NetlinkSocketReader _ns_reader; }; #endif #endif // __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_GET_NETLINK_SOCKET_HH__ xorp/fea/data_plane/ifconfig/ifconfig_set.cc0000664000076400007640000005274711703345405021256 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2012 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvx.hh" #include "fea/ifconfig.hh" #include "fea/ifconfig_set.hh" // // Set information about network interfaces configuration with the // underlying system. // // This is (almost) platform independent mechanism that can be // overwritten by each system-specific implementation by // (re)implementing the IfConfigSet::push_config() virtual method // in the class that inherits from IfConfigSet. // // // Copy some of the interfrace state from the pulled configuration // static void copy_interface_state(const IfTreeInterface* pulled_ifp, IfTreeInterface& config_iface) { if (pulled_ifp == NULL) return; if (config_iface.pif_index() != pulled_ifp->pif_index()) config_iface.set_pif_index(pulled_ifp->pif_index()); if (config_iface.no_carrier() != pulled_ifp->no_carrier()) config_iface.set_no_carrier(pulled_ifp->no_carrier()); if (config_iface.baudrate() != pulled_ifp->baudrate()) config_iface.set_baudrate(pulled_ifp->baudrate()); if (config_iface.mtu() == 0) { if (config_iface.mtu() != pulled_ifp->mtu()) config_iface.set_mtu(pulled_ifp->mtu()); } if (config_iface.mac().is_zero()) { if (config_iface.mac() != pulled_ifp->mac()) config_iface.set_mac(pulled_ifp->mac()); } if (config_iface.interface_flags() != pulled_ifp->interface_flags()) config_iface.set_interface_flags(pulled_ifp->interface_flags()); } // // Copy some of the vif state from the pulled configuration // static void copy_vif_state(const IfTreeVif* pulled_vifp, IfTreeVif& config_vif) { if (pulled_vifp == NULL) return; if (config_vif.pif_index() != pulled_vifp->pif_index()) config_vif.set_pif_index(pulled_vifp->pif_index()); if (config_vif.broadcast() != pulled_vifp->broadcast()) config_vif.set_broadcast(pulled_vifp->broadcast()); if (config_vif.loopback() != pulled_vifp->loopback()) config_vif.set_loopback(pulled_vifp->loopback()); if (config_vif.point_to_point() != pulled_vifp->point_to_point()) config_vif.set_point_to_point(pulled_vifp->point_to_point()); if (config_vif.multicast() != pulled_vifp->multicast()) config_vif.set_multicast(pulled_vifp->multicast()); if (config_vif.vif_flags() != pulled_vifp->vif_flags()) config_vif.set_vif_flags(pulled_vifp->vif_flags()); } int IfConfigSet::push_config(const IfTree& iftree) { IfTree::IfMap::const_iterator ii; IfTreeInterface::VifMap::iterator vi; IfConfigErrorReporterBase& error_reporter = ifconfig().ifconfig_error_reporter(); const IfTree& system_iftree = ifconfig().system_config(); // Clear errors associated with error reporter error_reporter.reset(); // // Pre-configuration processing: // - Sanity check config - bail on bad interface and bad vif names. // - Set "soft" flag for interfaces. // - Propagate "DELETED" status from interfaces to vifs. // - Propagate "DELETED" status from vifs to addresses. // for (ii = iftree.interfaces().begin(); ii != iftree.interfaces().end(); ++ii) { IfTreeInterface& config_iface = *(ii->second); // // Set the "soft" flag for interfaces that are emulated // if ((config_iface.discard() && is_discard_emulated(config_iface)) || (config_iface.unreachable() && is_unreachable_emulated(config_iface))) { config_iface.set_soft(true); } // // Skip the rest of processing if the interface has no mapping to // an existing interface in the system. // if (config_iface.is_soft()) continue; // // Skip configuration for default system config interfaces // if (config_iface.default_system_config()) continue; // // Check that the interface is recognized by the system // if (system_iftree.find_interface(config_iface.ifname()) == NULL) { if (config_iface.state() == IfTreeItem::DELETED) { // XXX: ignore deleted interfaces that are not recognized continue; } // Maybe it was already deleted from xorp due to OS removal. We should // just ignore this one //error_reporter.interface_error(config_iface.ifname(), // "interface not recognized"); //break; } // // Check the interface and vif name // for (vi = config_iface.vifs().begin(); vi != config_iface.vifs().end(); ++vi) { IfTreeVif& config_vif = *(vi->second); if (config_vif.vifname() != config_iface.ifname()) { error_reporter.vif_error(config_iface.ifname(), config_vif.vifname(), "bad vif name, must match iface name"); break; } } if (error_reporter.error_count() > 0) break; // // Propagate the "DELETED" status from interfaces to vifs and addresses // for (vi = config_iface.vifs().begin(); vi != config_iface.vifs().end(); ++vi) { IfTreeVif& config_vif = *(vi->second); if (config_iface.state() == IfTreeItem::DELETED) config_vif.mark(IfTreeItem::DELETED); // Propagate the "DELETE" status to the IPv4 addresses IfTreeVif::IPv4Map::iterator a4i; for (a4i = config_vif.ipv4addrs().begin(); a4i != config_vif.ipv4addrs().end(); ++a4i) { IfTreeAddr4& config_addr = *(a4i->second); if (config_vif.state() == IfTreeItem::DELETED) config_addr.mark(IfTreeItem::DELETED); } // Propagate the "DELETE" status to the IPv6 addresses #ifdef HAVE_IPV6 IfTreeVif::IPv6Map::iterator a6i; for (a6i = config_vif.ipv6addrs().begin(); a6i != config_vif.ipv6addrs().end(); ++a6i) { IfTreeAddr6& config_addr = *(a6i->second); if (config_vif.state() == IfTreeItem::DELETED) config_addr.mark(IfTreeItem::DELETED); } #endif // HAVE_IPV6 } } if (error_reporter.error_count() > 0) { XLOG_ERROR("%s", error_reporter.last_error().c_str()); return (XORP_ERROR); } // // Push the config: // 1. Push the vif creation/deletion (e.g., VLAN). // 2. Pull the config from the system (e.g., to obtain information // such as interface indexes for newly created interfaces/vifs). // 3. Push the interface/vif/address information. // push_iftree_begin(iftree); // // 1. Push the vif creation/deletion (e.g., VLAN). // for (ii = iftree.interfaces().begin(); ii != iftree.interfaces().end(); ++ii) { IfTreeInterface& config_iface = *(ii->second); const IfTreeInterface* system_ifp = NULL; system_ifp = system_iftree.find_interface(config_iface.ifname()); // // Skip interfaces that should never be pushed: // - Soft interfaces // - Default system config interfaces // if (config_iface.is_soft()) continue; if (config_iface.default_system_config()) continue; push_if_creation(system_ifp, config_iface); } // // 2. Pull the config from the system (e.g., to obtain information // such as interface indexes for newly created interfaces/vifs). // ifconfig().pull_config(NULL, -1); // // 3. Push the interface/vif/address configuration. // for (ii = iftree.interfaces().begin(); ii != iftree.interfaces().end(); ++ii) { IfTreeInterface& config_iface = *(ii->second); const IfTreeInterface* system_ifp = NULL; system_ifp = system_iftree.find_interface(config_iface.ifname()); // // Skip interfaces that should never be pushed: // - Soft interfaces // - Default system config interfaces // if (config_iface.is_soft()) continue; if (config_iface.default_system_config()) continue; if ((system_ifp == NULL) && (config_iface.state() == IfTreeItem::DELETED)) { // XXX: ignore deleted interfaces that are not recognized continue; } push_interface_begin(system_ifp, config_iface); for (vi = config_iface.vifs().begin(); vi != config_iface.vifs().end(); ++vi) { IfTreeVif& config_vif = *(vi->second); const IfTreeVif* system_vifp = NULL; if (system_ifp != NULL) system_vifp = system_ifp->find_vif(config_vif.vifname()); push_vif_begin(system_ifp, system_vifp, config_iface, config_vif); // // Push the IPv4 addresses // IfTreeVif::IPv4Map::iterator a4i; for (a4i = config_vif.ipv4addrs().begin(); a4i != config_vif.ipv4addrs().end(); ++a4i) { IfTreeAddr4& config_addr = *(a4i->second); const IfTreeAddr4* system_addrp = NULL; if (system_vifp != NULL) system_addrp = system_vifp->find_addr(config_addr.addr()); push_vif_address(system_ifp, system_vifp, system_addrp, config_iface, config_vif, config_addr); } #ifdef HAVE_IPV6 // // Push the IPv6 addresses // IfTreeVif::IPv6Map::iterator a6i; for (a6i = config_vif.ipv6addrs().begin(); a6i != config_vif.ipv6addrs().end(); ++a6i) { IfTreeAddr6& config_addr = *(a6i->second); const IfTreeAddr6* system_addrp = NULL; if (system_vifp != NULL) system_addrp = system_vifp->find_addr(config_addr.addr()); push_vif_address(system_ifp, system_vifp, system_addrp, config_iface, config_vif, config_addr); } #endif // HAVE_IPV6 push_vif_end(system_ifp, system_vifp, config_iface, config_vif); } push_interface_end(system_ifp, config_iface); } push_iftree_end(iftree); if (error_reporter.error_count() != 0) return (XORP_ERROR); return (XORP_OK); } void IfConfigSet::push_iftree_begin(const IfTree& iftree) { string error_msg; IfConfigErrorReporterBase& error_reporter = ifconfig().ifconfig_error_reporter(); UNUSED(iftree); // // Begin the configuration // if (config_begin(error_msg) != XORP_OK) { error_msg = c_format("Failed to begin configuration: %s", error_msg.c_str()); } if (! error_msg.empty()) { error_reporter.config_error(error_msg); XLOG_ERROR("%s", error_reporter.last_error().c_str()); return; } } void IfConfigSet::push_iftree_end(const IfTree& iftree) { string error_msg; IfConfigErrorReporterBase& error_reporter = ifconfig().ifconfig_error_reporter(); UNUSED(iftree); // // End the configuration // if (config_end(error_msg) != XORP_OK) { error_msg = c_format("Failed to end configuration: %s", error_msg.c_str()); } if (! error_msg.empty()) { error_reporter.config_error(error_msg); XLOG_ERROR("%s", error_reporter.last_error().c_str()); return; } } void IfConfigSet::push_interface_begin(const IfTreeInterface* system_ifp, IfTreeInterface& config_iface) { string error_msg; IfConfigErrorReporterBase& error_reporter = ifconfig().ifconfig_error_reporter(); if ((system_ifp == NULL) && config_iface.is_marked(IfTreeItem::DELETED)) { // Nothing to do: the interface has been deleted from the system return; } // Copy some of the state from the system configuration copy_interface_state(system_ifp, config_iface); // // Begin the interface configuration // if (config_interface_begin(system_ifp, config_iface, error_msg) != XORP_OK) { error_msg = c_format("Failed to begin interface configuration: %s", error_msg.c_str()); } if (! error_msg.empty()) { error_reporter.interface_error(config_iface.ifname(), error_msg); XLOG_ERROR("%s", error_reporter.last_error().c_str()); return; } } void IfConfigSet::push_interface_end(const IfTreeInterface* system_ifp, IfTreeInterface& config_iface) { string error_msg; IfConfigErrorReporterBase& error_reporter = ifconfig().ifconfig_error_reporter(); // // End the interface configuration // if (config_interface_end(system_ifp, config_iface, error_msg) != XORP_OK) { error_msg = c_format("Failed to end interface configuration: %s", error_msg.c_str()); } if (! error_msg.empty()) { error_reporter.interface_error(config_iface.ifname(), error_msg); XLOG_ERROR("%s", error_reporter.last_error().c_str()); return; } } void IfConfigSet::push_if_creation(const IfTreeInterface* system_ifp, IfTreeInterface& config_if) { // Only try to create/delete VLAN interfaces. Could update // this if/when we support Linux VETH, BSD epair, etc. if (!config_if.is_vlan()) { return; } string error_msg; IfConfigErrorReporterBase& error_reporter = ifconfig().ifconfig_error_reporter(); IfConfigVlanSet* ifconfig_vlan_set; // Get the plugin for VLAN setup ifconfig_vlan_set = fea_data_plane_manager().ifconfig_vlan_set(); if (ifconfig_vlan_set == NULL) { error_msg = c_format("Failed to apply VLAN setup to " "interface %s : no plugin found", config_if.ifname().c_str()); goto done; } // // Push the VLAN configuration: either add/update or delete it. // if (config_if.is_marked(IfTreeItem::DELETED)) { // Delete the VLAN (only if xorp created it) if (config_if.cr_by_xorp()) { if (ifconfig_vlan_set->config_delete_vlan(config_if, error_msg) != XORP_OK) { error_msg = c_format("Failed to delete VLAN: %s reason: %s ", config_if.ifname().c_str(), error_msg.c_str()); } } } else { // Add/update the VLAN bool created_if = false; if (ifconfig_vlan_set->config_add_vlan(system_ifp, config_if, created_if, error_msg) != XORP_OK) { error_msg = c_format("Failed to add VLAN to " "interface %s reason: %s", config_if.ifname().c_str(), error_msg.c_str()); } else { if (created_if) config_if.set_cr_by_xorp(true); } } done: if (! error_msg.empty()) { error_reporter.vif_error(config_if.ifname(), config_if.ifname(), error_msg); XLOG_ERROR("%s", error_reporter.last_error().c_str()); return; } } void IfConfigSet::push_vif_begin(const IfTreeInterface* system_ifp, const IfTreeVif* system_vifp, IfTreeInterface& config_iface, IfTreeVif& config_vif) { string error_msg; IfConfigErrorReporterBase& error_reporter = ifconfig().ifconfig_error_reporter(); if ((system_vifp == NULL) && config_vif.is_marked(IfTreeItem::DELETED)) { // Nothing to do: the vif has been deleted from the system return; } // Copy some of the state from the system configuration copy_interface_state(system_ifp, config_iface); copy_vif_state(system_vifp, config_vif); // // Begin the vif configuration // if (config_vif_begin(system_ifp, system_vifp, config_iface, config_vif, error_msg) != XORP_OK) { error_msg = c_format("Failed to begin vif configuration: %s", error_msg.c_str()); } if (! error_msg.empty()) { error_reporter.vif_error(config_iface.ifname(), config_vif.vifname(), error_msg); XLOG_ERROR("%s", error_reporter.last_error().c_str()); return; } } void IfConfigSet::push_vif_end(const IfTreeInterface* system_ifp, const IfTreeVif* system_vifp, IfTreeInterface& config_iface, IfTreeVif& config_vif) { string error_msg; IfConfigErrorReporterBase& error_reporter = ifconfig().ifconfig_error_reporter(); // // End the vif configuration // if (config_vif_end(system_ifp, system_vifp, config_iface, config_vif, error_msg) != XORP_OK) { error_msg = c_format("Failed to end vif configuration: %s", error_msg.c_str()); } if (! error_msg.empty()) { error_reporter.vif_error(config_iface.ifname(), config_vif.vifname(), error_msg); XLOG_ERROR("%s", error_reporter.last_error().c_str()); return; } } void IfConfigSet::push_vif_address(const IfTreeInterface* system_ifp, const IfTreeVif* system_vifp, const IfTreeAddr4* system_addrp, IfTreeInterface& config_iface, IfTreeVif& config_vif, IfTreeAddr4& config_addr) { string error_msg; IfConfigErrorReporterBase& error_reporter = ifconfig().ifconfig_error_reporter(); bool is_add = true; if (! fea_data_plane_manager().have_ipv4()) { error_msg = "IPv4 is not supported"; goto done; } if (config_addr.is_marked(IfTreeItem::DELETED) || (! config_addr.enabled())) { // XXX: Disabling an address is same as deleting it is_add = false; } // // XXX: If the broadcast address was omitted, recompute and set it here. // Note that we recompute it only if the underlying vif is // broadcast-capable. // if ((system_vifp != NULL) && system_vifp->broadcast() && (config_addr.prefix_len() > 0) && (! (config_addr.broadcast() || config_addr.point_to_point()))) { IPv4 mask = IPv4::make_prefix(config_addr.prefix_len()); IPv4 broadcast_addr = config_addr.addr() | ~mask; config_addr.set_bcast(broadcast_addr); config_addr.set_broadcast(true); } // // Push the address configuration: either add/update or delete it. // if (is_add) { // // Add/update the address // if (config_add_address(system_ifp, system_vifp, system_addrp, config_iface, config_vif, config_addr, error_msg) != XORP_OK) { if (strstr(error_msg.c_str(), "No such device")) { XLOG_ERROR("Failed to configure address because of device not found: %s", error_msg.c_str()); // We can't pass this back as an error to the cli because the device is gone, // and if we fail this set, the whole commit will fail. This commit could be // deleting *another* device, with a (very near) future set of xorpsh commands // coming in to delete *this* interface in question. // This is a hack...we really need a way to pass warnings back to the // cli but NOT fail the commit if the logical state is correct but the // (transient, unpredictable, asynchronously discovered) state of the actual // OS network devices is currently out of state. // --Ben error_msg = ""; } else { error_msg = c_format("Failed to add address, not device-no-found error: %s", error_msg.c_str()); } } } else { // // Delete the address // if (system_addrp == NULL) return; // XXX: nothing to delete if (config_delete_address(system_ifp, system_vifp, system_addrp, config_iface, config_vif, config_addr, error_msg) != XORP_OK) { error_msg = c_format("Failed to delete address: %s", error_msg.c_str()); } } done: if (! error_msg.empty()) { error_reporter.vifaddr_error(config_iface.ifname(), config_vif.vifname(), config_addr.addr(), error_msg); XLOG_ERROR("%s", error_reporter.last_error().c_str()); return; } } #ifdef HAVE_IPV6 void IfConfigSet::push_vif_address(const IfTreeInterface* system_ifp, const IfTreeVif* system_vifp, const IfTreeAddr6* system_addrp, IfTreeInterface& config_iface, IfTreeVif& config_vif, IfTreeAddr6& config_addr) { string error_msg; IfConfigErrorReporterBase& error_reporter = ifconfig().ifconfig_error_reporter(); bool is_add = true; if (! fea_data_plane_manager().have_ipv6()) { error_msg = "IPv6 is not supported"; goto done; } if (config_addr.is_marked(IfTreeItem::DELETED) || (! config_addr.enabled())) { // XXX: Disabling an address is same as deleting it is_add = false; } // // XXX: For whatever reason a prefix length of zero does not cut it, so // initialize prefix to 64. This is exactly what ifconfig(8) does. // if (config_addr.prefix_len() == 0) config_addr.set_prefix_len(64); // // Push the address configuration: either add/update or delete it. // if (is_add) { // // Add/update the address // if (config_add_address(system_ifp, system_vifp, system_addrp, config_iface, config_vif, config_addr, error_msg) != XORP_OK) { if (strstr(error_msg.c_str(), "No such device")) { XLOG_ERROR("Failed to configure address because of device not found: %s", error_msg.c_str()); // We can't pass this back as an error to the cli because the device is gone, // and if we fail this set, the whole commit will fail. This commit could be // deleting *another* device, with a (very near) future set of xorpsh commands // coming in to delete *this* interface in question. // This is a hack...we really need a way to pass warnings back to the // cli but NOT fail the commit if the logical state is correct but the // (transient, unpredictable, asynchronously discovered) state of the actual // OS network devices is currently out of state. // --Ben error_msg = ""; } else { error_msg = c_format("Failed to configure address, not device-no-found error: %s", error_msg.c_str()); } } } else { // // Delete the address // if (system_addrp == NULL) return; // XXX: nothing to delete if (config_delete_address(system_ifp, system_vifp, system_addrp, config_iface, config_vif, config_addr, error_msg) != XORP_OK) { error_msg = c_format("Failed to delete address: %s", error_msg.c_str()); } } done: if (! error_msg.empty()) { error_reporter.vifaddr_error(config_iface.ifname(), config_vif.vifname(), config_addr.addr(), error_msg); XLOG_ERROR("%s", error_reporter.last_error().c_str()); return; } } #endif // HAVE_IPV6 xorp/fea/data_plane/ifconfig/ifconfig_set_ioctl.cc0000664000076400007640000006473311540225524022444 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include #if defined(HAVE_IOCTL_SIOCGIFCONF) && !defined(HAVE_NETLINK_SOCKETS) #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ether_compat.h" #include "libxorp/ipvx.hh" #include "libcomm/comm_api.h" #ifdef HAVE_SYS_IOCTL_H #include #endif #ifdef HAVE_SYS_SOCKIO_H #include #endif #ifdef HAVE_NET_IF_H #include #endif #ifdef HAVE_NET_IF_ARP_H #include #endif #ifdef HAVE_NET_IF_VAR_H #include #endif #ifdef HAVE_NETINET_IN_VAR_H #include #endif #ifdef HAVE_NETINET6_IN6_VAR_H #include #endif #ifdef HAVE_NETINET6_ND6_H #ifdef HAVE_BROKEN_CXX_NETINET6_ND6_H // XXX: a hack needed if is not C++ friendly #define prf_ra in6_prflags::prf_ra #endif #include #endif #include "fea/ifconfig.hh" #include "ifconfig_set_ioctl.hh" #ifdef HAVE_IPV6 #ifdef HOST_OS_LINUX // // XXX: In case of Linux, we have "struct in6_ifreq" defined // in . However, we cannot include that file along // with because of replicated structure definitions // in and where the latter one is // included by . // Hence, we have no choice but explicitly define here "struct in6_ifreq". // BTW, please note that the Linux struct in6_ifreq has nothing in common // with the original KAME's "struct in6_ifreq". // struct in6_ifreq { struct in6_addr ifr6_addr; uint32_t ifr6_prefixlen; unsigned int ifr6_ifindex; }; #endif // HOST_OS_LINUX #endif // HAVE_IPV6 // // Set information about network interfaces configuration with the // underlying system. // // The mechanism to set the information is ioctl(2). // IfConfigSetIoctl::IfConfigSetIoctl(FeaDataPlaneManager& fea_data_plane_manager) : IfConfigSet(fea_data_plane_manager), _s4(-1), _s6(-1) { } IfConfigSetIoctl::~IfConfigSetIoctl() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the ioctl(2) mechanism to set " "information about network interfaces into the underlying " "system: %s", error_msg.c_str()); } } int IfConfigSetIoctl::start(string& error_msg) { if (_is_running) return (XORP_OK); if (fea_data_plane_manager().have_ipv4()) { if (_s4 < 0) { _s4 = socket(AF_INET, SOCK_DGRAM, 0); if (_s4 < 0) { error_msg = c_format("Could not initialize IPv4 ioctl() " "socket: %s", strerror(errno)); XLOG_FATAL("%s", error_msg.c_str()); } } } #ifdef HAVE_IPV6 if (fea_data_plane_manager().have_ipv6()) { if (_s6 < 0) { _s6 = socket(AF_INET6, SOCK_DGRAM, 0); if (_s6 < 0) { error_msg = c_format("Could not initialize IPv6 ioctl() " "socket: %s", strerror(errno)); XLOG_FATAL("%s", error_msg.c_str()); } } } #endif // HAVE_IPV6 _is_running = true; return (XORP_OK); } int IfConfigSetIoctl::stop(string& error_msg) { int ret_value4 = XORP_OK; int ret_value6 = XORP_OK; if (! _is_running) return (XORP_OK); if (_s4 >= 0) { ret_value4 = comm_close(_s4); _s4 = -1; if (ret_value4 != XORP_OK) { error_msg = c_format("Could not close IPv4 ioctl() socket: %s", comm_get_last_error_str()); } } if (_s6 >= 0) { ret_value6 = comm_close(_s6); _s6 = -1; if ((ret_value6 != XORP_OK) && (ret_value4 == XORP_OK)) { error_msg = c_format("Could not close IPv6 ioctl() socket: %s", comm_get_last_error_str()); } } if ((ret_value4 != XORP_OK) || (ret_value6 != XORP_OK)) return (XORP_ERROR); _is_running = false; return (XORP_OK); } bool IfConfigSetIoctl::is_discard_emulated(const IfTreeInterface& i) const { UNUSED(i); #if defined(HOST_OS_BSDI) \ || defined(HOST_OS_FREEBSD) \ || defined(HOST_OS_MACOSX) \ || defined(HOST_OS_NETBSD) \ || defined(HOST_OS_OPENBSD) \ || defined(HOST_OS_DRAGONFLYBSD) return (true); #else return (false); #endif } bool IfConfigSetIoctl::is_unreachable_emulated(const IfTreeInterface& i) const { UNUSED(i); #if defined(HOST_OS_BSDI) \ || defined(HOST_OS_FREEBSD) \ || defined(HOST_OS_MACOSX) \ || defined(HOST_OS_NETBSD) \ || defined(HOST_OS_OPENBSD) \ || defined(HOST_OS_DRAGONFLYBSD) return (true); #else return (false); #endif } int IfConfigSetIoctl::config_begin(string& error_msg) { // XXX: nothing to do UNUSED(error_msg); return (XORP_OK); } int IfConfigSetIoctl::config_end(string& error_msg) { // XXX: nothing to do UNUSED(error_msg); return (XORP_OK); } int IfConfigSetIoctl::config_interface_begin(const IfTreeInterface* pulled_ifp, IfTreeInterface& config_iface, string& error_msg) { int ret_value = XORP_OK; bool was_disabled = false; bool should_disable = false; if (pulled_ifp == NULL) { // Nothing to do: the interface has been deleted from the system return (XORP_OK); } #ifdef HOST_OS_LINUX // // XXX: Set the interface DOWN otherwise we may not be able to // set the MAC address or the MTU (limitation imposed by the Linux kernel). // if (pulled_ifp->enabled()) should_disable = true; #endif // // Set the MTU // if (config_iface.mtu() != pulled_ifp->mtu()) { if (should_disable && (! was_disabled)) { if (set_interface_status(config_iface.ifname(), config_iface.pif_index(), config_iface.interface_flags(), false, error_msg) != XORP_OK) { ret_value = XORP_ERROR; goto done; } was_disabled = true; } if (set_interface_mtu(config_iface.ifname(), config_iface.mtu(), error_msg) != XORP_OK) { ret_value = XORP_ERROR; goto done; } } // // Set the MAC address // if (config_iface.mac() != pulled_ifp->mac()) { if (should_disable && (! was_disabled)) { if (set_interface_status(config_iface.ifname(), config_iface.pif_index(), config_iface.interface_flags(), false, error_msg) != XORP_OK) { ret_value = XORP_ERROR; goto done; } was_disabled = true; } if (set_interface_mac_address(config_iface.ifname(), config_iface.mac(), error_msg) != XORP_OK) { ret_value = XORP_ERROR; goto done; } } done: if (was_disabled) { if (set_interface_status(config_iface.ifname(), config_iface.pif_index(), config_iface.interface_flags(), true, error_msg) != XORP_OK) { return (XORP_ERROR); } } return (ret_value); } int IfConfigSetIoctl::config_interface_end(const IfTreeInterface* pulled_ifp, const IfTreeInterface& config_iface, string& error_msg) { if (pulled_ifp == NULL) { // Nothing to do: the interface has been deleted from the system return (XORP_OK); } // // Set the interface status // if (config_iface.enabled() != pulled_ifp->enabled()) { if (set_interface_status(config_iface.ifname(), config_iface.pif_index(), config_iface.interface_flags(), config_iface.enabled(), error_msg) != XORP_OK) { return (XORP_ERROR); } } return (XORP_OK); } int IfConfigSetIoctl::config_vif_begin(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeInterface& config_iface, const IfTreeVif& config_vif, string& error_msg) { UNUSED(pulled_ifp); UNUSED(config_iface); UNUSED(config_vif); UNUSED(error_msg); if (pulled_vifp == NULL) { // Nothing to do: the vif has been deleted from the system return (XORP_OK); } // XXX: nothing to do return (XORP_OK); } int IfConfigSetIoctl::config_vif_end(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeInterface& config_iface, const IfTreeVif& config_vif, string& error_msg) { UNUSED(pulled_ifp); if (pulled_vifp == NULL) { // Nothing to do: the vif has been deleted from the system return (XORP_OK); } // // XXX: If the interface name and vif name are different, then // they might have different status: the interface can be UP, while // the vif can be DOWN. // if (config_iface.ifname() != config_vif.vifname()) { // // Set the vif status // if (config_vif.enabled() != pulled_vifp->enabled()) { // // XXX: The interface and vif status setting mechanism is // equivalent for this platform. // if (set_interface_status(config_vif.vifname(), config_vif.pif_index(), config_vif.vif_flags(), config_vif.enabled(), error_msg) != XORP_OK) { return (XORP_ERROR); } } } return (XORP_OK); } int IfConfigSetIoctl::config_add_address(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeAddr4* pulled_addrp, const IfTreeInterface& config_iface, const IfTreeVif& config_vif, const IfTreeAddr4& config_addr, string& error_msg) { UNUSED(pulled_ifp); UNUSED(pulled_vifp); // // Test whether a new address // do { if (pulled_addrp == NULL) break; if (pulled_addrp->addr() != config_addr.addr()) break; if (pulled_addrp->broadcast() != config_addr.broadcast()) break; if (pulled_addrp->broadcast() && (pulled_addrp->bcast() != config_addr.bcast())) { break; } if (pulled_addrp->point_to_point() != config_addr.point_to_point()) break; if (pulled_addrp->point_to_point() && (pulled_addrp->endpoint() != config_addr.endpoint())) { break; } if (pulled_addrp->prefix_len() != config_addr.prefix_len()) break; // XXX: Same address, therefore ignore it return (XORP_OK); } while (false); // // Delete the old address if necessary // if (pulled_addrp != NULL) { if (delete_addr(config_iface.ifname(), config_vif.vifname(), config_vif.pif_index(), config_addr.addr(), config_addr.prefix_len(), error_msg) != XORP_OK) { return (XORP_ERROR); } } // // Add the address // if (add_addr(config_iface.ifname(), config_vif.vifname(), config_vif.pif_index(), config_addr.addr(), config_addr.prefix_len(), config_addr.broadcast(), config_addr.bcast(), config_addr.point_to_point(), config_addr.endpoint(), error_msg) != XORP_OK) { return (XORP_ERROR); } return (XORP_OK); } int IfConfigSetIoctl::config_delete_address(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeAddr4* pulled_addrp, const IfTreeInterface& config_iface, const IfTreeVif& config_vif, const IfTreeAddr4& config_addr, string& error_msg) { UNUSED(pulled_ifp); UNUSED(pulled_vifp); UNUSED(pulled_addrp); // // Delete the address // if (delete_addr(config_iface.ifname(), config_vif.vifname(), config_vif.pif_index(), config_addr.addr(), config_addr.prefix_len(), error_msg) != XORP_OK) { return (XORP_ERROR); } return (XORP_OK); } int IfConfigSetIoctl::config_add_address(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeAddr6* pulled_addrp, const IfTreeInterface& config_iface, const IfTreeVif& config_vif, const IfTreeAddr6& config_addr, string& error_msg) { UNUSED(pulled_ifp); UNUSED(pulled_vifp); // // Test whether a new address // do { if (pulled_addrp == NULL) break; if (pulled_addrp->addr() != config_addr.addr()) break; if (pulled_addrp->point_to_point() != config_addr.point_to_point()) break; if (pulled_addrp->point_to_point() && (pulled_addrp->endpoint() != config_addr.endpoint())) { break; } if (pulled_addrp->prefix_len() != config_addr.prefix_len()) break; // XXX: Same address, therefore ignore it return (XORP_OK); } while (false); // // Delete the old address if necessary // if (pulled_addrp != NULL) { if (delete_addr(config_iface.ifname(), config_vif.vifname(), config_vif.pif_index(), config_addr.addr(), config_addr.prefix_len(), error_msg) != XORP_OK) { return (XORP_ERROR); } } // // Add the address // if (add_addr(config_iface.ifname(), config_vif.vifname(), config_vif.pif_index(), config_addr.addr(), config_addr.prefix_len(), config_addr.point_to_point(), config_addr.endpoint(), error_msg) != XORP_OK) { return (XORP_ERROR); } return (XORP_OK); } int IfConfigSetIoctl::config_delete_address(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeAddr6* pulled_addrp, const IfTreeInterface& config_iface, const IfTreeVif& config_vif, const IfTreeAddr6& config_addr, string& error_msg) { UNUSED(pulled_ifp); UNUSED(pulled_vifp); UNUSED(pulled_addrp); // // Delete the address // if (delete_addr(config_iface.ifname(), config_vif.vifname(), config_vif.pif_index(), config_addr.addr(), config_addr.prefix_len(), error_msg) != XORP_OK) { return (XORP_ERROR); } return (XORP_OK); } int IfConfigSetIoctl::set_interface_status(const string& ifname, uint32_t if_index, uint32_t interface_flags, bool is_enabled, string& error_msg) { struct ifreq ifreq; UNUSED(if_index); // // Update the interface flags // if (is_enabled) interface_flags |= IFF_UP; else interface_flags &= ~IFF_UP; memset(&ifreq, 0, sizeof(ifreq)); strncpy(ifreq.ifr_name, ifname.c_str(), sizeof(ifreq.ifr_name) - 1); ifreq.ifr_flags = interface_flags; if (ioctl(_s4, SIOCSIFFLAGS, &ifreq) < 0) { error_msg = c_format("Cannot set the interface flags to 0x%x on " "interface %s: %s", interface_flags, ifname.c_str(), strerror(errno)); return (XORP_ERROR); } return (XORP_OK); } int IfConfigSetIoctl::set_interface_mac_address(const string& ifname, const Mac& mac, string& error_msg) { struct ether_addr ether_addr; struct ifreq ifreq; mac.copy_out(ether_addr); memset(&ifreq, 0, sizeof(ifreq)); strncpy(ifreq.ifr_name, ifname.c_str(), sizeof(ifreq.ifr_name) - 1); #if defined(SIOCSIFLLADDR) // // FreeBSD // ifreq.ifr_addr.sa_family = AF_LINK; memcpy(ifreq.ifr_addr.sa_data, ðer_addr, sizeof(ether_addr)); #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN ifreq.ifr_addr.sa_len = sizeof(ether_addr); #endif if (ioctl(_s4, SIOCSIFLLADDR, &ifreq) < 0) { error_msg = c_format("Cannot set the MAC address to %s " "on interface %s: %s", mac.str().c_str(), ifname.c_str(), strerror(errno)); return (XORP_ERROR); } return (XORP_OK); #elif defined(SIOCSIFHWADDR) // // Linux // ifreq.ifr_hwaddr.sa_family = ARPHRD_ETHER; memcpy(ifreq.ifr_hwaddr.sa_data, ðer_addr, ETH_ALEN); #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN ifreq.ifr_hwaddr.sa_len = ETH_ALEN; #endif if (ioctl(_s4, SIOCSIFHWADDR, &ifreq) < 0) { error_msg = c_format("Cannot set the MAC address %s " "on interface %s: %s", mac.str().c_str(), ifname.c_str(), strerror(errno)); return (XORP_ERROR); } return (XORP_OK); #else // // No mechanism: NetBSD and OpenBSD, et. al. // // XXX: currently (NetBSD-1.6.1 and OpenBSD-3.3) do not support // setting the MAC address. // error_msg = c_format("No mechanism to set the MAC address " "on an interface"); return (XORP_ERROR); #endif } int IfConfigSetIoctl::set_interface_mtu(const string& ifname, uint32_t mtu, string& error_msg) { struct ifreq ifreq; memset(&ifreq, 0, sizeof(ifreq)); strncpy(ifreq.ifr_name, ifname.c_str(), sizeof(ifreq.ifr_name) - 1); ifreq.ifr_mtu = mtu; if (ioctl(_s4, SIOCSIFMTU, &ifreq) < 0) { error_msg = c_format("Cannot set the MTU to %u on " "interface %s: %s", mtu, ifname.c_str(), strerror(errno)); return (XORP_ERROR); } return (XORP_OK); } int IfConfigSetIoctl::add_addr(const string& ifname, const string& vifname, uint32_t if_index, const IPv4& addr, uint32_t prefix_len, bool is_broadcast, const IPv4& broadcast_addr, bool is_point_to_point, const IPv4& endpoint_addr, string& error_msg) { // // XXX: If the system has ioctl(SIOCAIFADDR) (e.g., FreeBSD), then // an interface address is added as an alias, otherwise it overwrites the // previous address (if such exists). // #if defined(SIOCAIFADDR) // // Add an alias address // struct in_aliasreq ifra; UNUSED(if_index); memset(&ifra, 0, sizeof(ifra)); strncpy(ifra.ifra_name, vifname.c_str(), sizeof(ifra.ifra_name) - 1); addr.copy_out(ifra.ifra_addr); if (is_broadcast) broadcast_addr.copy_out(ifra.ifra_broadaddr); if (is_point_to_point) endpoint_addr.copy_out(ifra.ifra_dstaddr); IPv4 prefix_addr = IPv4::make_prefix(prefix_len); prefix_addr.copy_out(ifra.ifra_mask); if (ioctl(_s4, SIOCAIFADDR, &ifra) < 0) { error_msg = c_format("Cannot add address '%s' " "on interface '%s' vif '%s': %s", addr.str().c_str(), ifname.c_str(), vifname.c_str(), strerror(errno)); return (XORP_ERROR); } return (XORP_OK); #elif defined(SIOCSIFADDR) // // Set a new address // struct ifreq ifreq; UNUSED(if_index); memset(&ifreq, 0, sizeof(ifreq)); strncpy(ifreq.ifr_name, vifname.c_str(), sizeof(ifreq.ifr_name) - 1); // Set the address addr.copy_out(ifreq.ifr_addr); if (ioctl(_s4, SIOCSIFADDR, &ifreq) < 0) { error_msg = c_format("Cannot add address '%s' " "on interface '%s' vif '%s': %s", addr.str().c_str(), ifname.c_str(), vifname.c_str(), strerror(errno)); return (XORP_ERROR); } // Set the netmask IPv4 prefix_addr = IPv4::make_prefix(prefix_len); prefix_addr.copy_out(ifreq.ifr_addr); if (ioctl(_s4, SIOCSIFNETMASK, &ifreq) < 0) { error_msg = c_format("Cannot add network mask '%s' to address '%s' " "on interface '%s' vif '%s': %s", prefix_addr.str().c_str(), addr.str().c_str(), ifname.c_str(), vifname.c_str(), strerror(errno)); return (XORP_ERROR); } // Set the broadcast or point-to-point address if (is_broadcast) { broadcast_addr.copy_out(ifreq.ifr_broadaddr); if (ioctl(_s4, SIOCSIFBRDADDR, &ifreq) < 0) { error_msg = c_format("Cannot add broadcast address '%s' " "to address '%s' " "on interface '%s' vif '%s': %s", broadcast_addr.str().c_str(), addr.str().c_str(), ifname.c_str(), vifname.c_str(), strerror(errno)); return (XORP_ERROR); } } if (is_point_to_point) { endpoint_addr.copy_out(ifreq.ifr_dstaddr); if (ioctl(_s4, SIOCSIFDSTADDR, &ifreq) < 0) { error_msg = c_format("Cannot add endpoint address '%s' " "to address '%s' " "on interface '%s' vif '%s': %s", endpoint_addr.str().c_str(), addr.str().c_str(), ifname.c_str(), vifname.c_str(), strerror(errno)); return (XORP_ERROR); } } return (XORP_OK); #else // // No mechanism to add the address // UNUSED(ifname); UNUSED(vifname); UNUSED(if_index); UNUSED(addr); UNUSED(prefix_len); UNUSED(is_broadcast); UNUSED(broadcast_addr); UNUSED(is_point_to_point); UNUSED(endpoint_addr); error_msg = c_format("No mechanism to add an IPv4 address " "on an interface"); return (XORP_ERROR); #endif } int IfConfigSetIoctl::delete_addr(const string& ifname, const string& vifname, uint32_t if_index, const IPv4& addr, uint32_t prefix_len, string& error_msg) { struct ifreq ifreq; memset(&ifreq, 0, sizeof(ifreq)); strncpy(ifreq.ifr_name, vifname.c_str(), sizeof(ifreq.ifr_name) - 1); #if defined(HOST_OS_LINUX) // // XXX: In case of Linux, SIOCDIFADDR doesn't delete IPv4 addresses. // Hence, we use SIOCSIFADDR to add 0.0.0.0 as an address. The // effect of this hack is that the IPv4 address on that interface // is deleted. Sigh... // UNUSED(if_index); UNUSED(prefix_len); IPv4::ZERO().copy_out(ifreq.ifr_addr); if (ioctl(_s4, SIOCSIFADDR, &ifreq) < 0) { error_msg = c_format("Cannot delete address '%s' " "on interface '%s' vif '%s': %s", addr.str().c_str(), ifname.c_str(), vifname.c_str(), strerror(errno)); return (XORP_ERROR); } return (XORP_OK); #elif defined(SIOCDIFADDR) UNUSED(if_index); UNUSED(prefix_len); addr.copy_out(ifreq.ifr_addr); if (ioctl(_s4, SIOCDIFADDR, &ifreq) < 0) { error_msg = c_format("Cannot delete address '%s' " "on interface '%s' vif '%s': %s", addr.str().c_str(), ifname.c_str(), vifname.c_str(), strerror(errno)); return (XORP_ERROR); } return (XORP_OK); #else // // No mechanism to delete the address // UNUSED(ifname); UNUSED(vifname); UNUSED(if_index); UNUSED(addr); UNUSED(prefix_len); error_msg = c_format("No mechanism to delete an IPv4 address " "on an interface"); return (XORP_ERROR); #endif } int IfConfigSetIoctl::add_addr(const string& ifname, const string& vifname, uint32_t if_index, const IPv6& addr, uint32_t prefix_len, bool is_point_to_point, const IPv6& endpoint_addr, string& error_msg) { #ifndef HAVE_IPV6 UNUSED(ifname); UNUSED(vifname); UNUSED(if_index); UNUSED(addr); UNUSED(prefix_len); UNUSED(is_point_to_point); UNUSED(endpoint_addr); error_msg = "IPv6 is not supported"; return (XORP_ERROR); #else // HAVE_IPV6 #if defined(SIOCAIFADDR_IN6) // // Add an alias address // struct in6_aliasreq ifra; UNUSED(if_index); memset(&ifra, 0, sizeof(ifra)); strncpy(ifra.ifra_name, vifname.c_str(), sizeof(ifra.ifra_name) - 1); addr.copy_out(ifra.ifra_addr); if (is_point_to_point) endpoint_addr.copy_out(ifra.ifra_dstaddr); IPv6 prefix_addr = IPv6::make_prefix(prefix_len); prefix_addr.copy_out(ifra.ifra_prefixmask); ifra.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME; ifra.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME; if (ioctl(_s6, SIOCAIFADDR_IN6, &ifra) < 0) { error_msg = c_format("Cannot add address '%s' " "on interface '%s' vif '%s': %s", addr.str().c_str(), ifname.c_str(), vifname.c_str(), strerror(errno)); return (XORP_ERROR); } return (XORP_OK); #elif defined(SIOCSIFADDR) // // Set a new address // // // XXX: Linux uses a weird struct in6_ifreq to do this, and this // name clashes with the KAME in6_ifreq. // For now, we don't make this code specific only to Linux. // struct in6_ifreq in6_ifreq; memset(&in6_ifreq, 0, sizeof(in6_ifreq)); in6_ifreq.ifr6_ifindex = if_index; // Set the address and the prefix length addr.copy_out(in6_ifreq.ifr6_addr); in6_ifreq.ifr6_prefixlen = prefix_len; if (ioctl(_s6, SIOCSIFADDR, &in6_ifreq) < 0) { error_msg = c_format("Cannot add address '%s' " "on interface '%s' vif '%s': %s", addr.str().c_str(), ifname.c_str(), vifname.c_str(), strerror(errno)); return (XORP_ERROR); } // Set the p2p address if (is_point_to_point) { endpoint_addr.copy_out(in6_ifreq.ifr6_addr); if (ioctl(_s6, SIOCSIFDSTADDR, &in6_ifreq) < 0) { error_msg = c_format("Cannot add endpoint address '%s' " "to address '%s' " "on interface '%s' vif '%s': %s", endpoint_addr.str().c_str(), addr.str().c_str(), ifname.c_str(), vifname.c_str(), strerror(errno)); return (XORP_ERROR); } } return (XORP_OK); #else // // No mechanism to add the address // UNUSED(ifname); UNUSED(vifname); UNUSED(if_index); UNUSED(addr); UNUSED(prefix_len); UNUSED(is_point_to_point); UNUSED(endpoint_addr); error_msg = c_format("No mechanism to add an IPv6 address " "on an interface"); return (XORP_ERROR); #endif #endif // HAVE_IPV6 } int IfConfigSetIoctl::delete_addr(const string& ifname, const string& vifname, uint32_t if_index, const IPv6& addr, uint32_t prefix_len, string& error_msg) { #ifndef HAVE_IPV6 UNUSED(ifname); UNUSED(vifname); UNUSED(if_index); UNUSED(addr); UNUSED(prefix_len); error_msg = "IPv6 is not supported"; return (XORP_ERROR); #else // HAVE_IPV6 #if defined(HOST_OS_LINUX) // // XXX: Linux uses a weird struct in6_ifreq to do this, and this // name clashes with the KAME in6_ifreq. // struct in6_ifreq in6_ifreq; memset(&in6_ifreq, 0, sizeof(in6_ifreq)); in6_ifreq.ifr6_ifindex = if_index; addr.copy_out(in6_ifreq.ifr6_addr); in6_ifreq.ifr6_prefixlen = prefix_len; if (ioctl(_s6, SIOCDIFADDR, &in6_ifreq) < 0) { error_msg = c_format("Cannot delete address '%s' " "on interface '%s' vif '%s': %s", addr.str().c_str(), ifname.c_str(), vifname.c_str(), strerror(errno)); return (XORP_ERROR); } return (XORP_OK); #elif defined(SIOCDIFADDR_IN6) struct in6_ifreq in6_ifreq; UNUSED(if_index); UNUSED(prefix_len); memset(&in6_ifreq, 0, sizeof(in6_ifreq)); strncpy(in6_ifreq.ifr_name, vifname.c_str(), sizeof(in6_ifreq.ifr_name) - 1); addr.copy_out(in6_ifreq.ifr_addr); if (ioctl(_s6, SIOCDIFADDR_IN6, &in6_ifreq) < 0) { error_msg = c_format("Cannot delete address '%s' " "on interface '%s' vif '%s': %s", addr.str().c_str(), ifname.c_str(), vifname.c_str(), strerror(errno)); return (XORP_ERROR); } return (XORP_OK); #else // // No mechanism to delete the address // UNUSED(ifname); UNUSED(vifname); UNUSED(if_index); UNUSED(addr); UNUSED(prefix_len); error_msg = c_format("No mechanism to delete an IPv6 address " "on an interface"); return (XORP_ERROR); #endif #endif // HAVE_IPV6 } #endif // HAVE_IOCTL_SIOCGIFCONF xorp/fea/data_plane/ifconfig/ifconfig_set_netlink_socket.cc0000664000076400007640000010117311540225524024334 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include #ifdef HAVE_NETLINK_SOCKETS #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ether_compat.h" #include "libxorp/ipvx.hh" #ifdef HAVE_SYS_IOCTL_H #include #endif #ifdef HAVE_NET_IF_H #include #endif #ifdef HAVE_NET_IF_ARP_H #include #endif #ifdef HAVE_LINUX_TYPES_H #include #endif #ifdef HAVE_LINUX_RTNETLINK_H #include #endif #include "fea/ifconfig.hh" #include "fea/fibconfig.hh" #include "fea/data_plane/control_socket/netlink_socket_utilities.hh" #include "ifconfig_set_netlink_socket.hh" // // Set information about network interfaces configuration with the // underlying system. // // The mechanism to set the information is netlink(7) sockets. // IfConfigSetNetlinkSocket::IfConfigSetNetlinkSocket(FeaDataPlaneManager& fea_data_plane_manager) : IfConfigSet(fea_data_plane_manager), NetlinkSocket(fea_data_plane_manager.eventloop(), fea_data_plane_manager.fibconfig().get_netlink_filter_table_id()), _ns_reader(*(NetlinkSocket *)this) { } IfConfigSetNetlinkSocket::~IfConfigSetNetlinkSocket() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the netlink(7) sockets mechanism to set " "information about network interfaces into the underlying " "system: %s", error_msg.c_str()); } } int IfConfigSetNetlinkSocket::start(string& error_msg) { if (_is_running) return (XORP_OK); if (NetlinkSocket::start(error_msg) != XORP_OK) return (XORP_ERROR); _is_running = true; return (XORP_OK); } int IfConfigSetNetlinkSocket::stop(string& error_msg) { if (! _is_running) return (XORP_OK); if (NetlinkSocket::stop(error_msg) != XORP_OK) return (XORP_ERROR); _is_running = false; return (XORP_OK); } bool IfConfigSetNetlinkSocket::is_discard_emulated(const IfTreeInterface& i) const { UNUSED(i); #ifdef HOST_OS_LINUX return (true); #else return (false); #endif } bool IfConfigSetNetlinkSocket::is_unreachable_emulated(const IfTreeInterface& i) const { UNUSED(i); #ifdef HOST_OS_LINUX return (true); #else return (false); #endif } int IfConfigSetNetlinkSocket::config_begin(string& error_msg) { // XXX: nothing to do UNUSED(error_msg); return (XORP_OK); } int IfConfigSetNetlinkSocket::config_end(string& error_msg) { // XXX: nothing to do UNUSED(error_msg); return (XORP_OK); } int IfConfigSetNetlinkSocket::config_interface_begin( const IfTreeInterface* pulled_ifp, IfTreeInterface& config_iface, string& error_msg) { int ret_value = XORP_OK; bool was_disabled = false; bool should_disable = false; if (pulled_ifp == NULL) { // Nothing to do: the interface has been deleted from the system return (XORP_OK); } #ifdef HOST_OS_LINUX // // XXX: Set the interface DOWN otherwise we may not be able to // set the MAC address or the MTU (limitation imposed by the Linux kernel). // if (pulled_ifp->enabled()) should_disable = true; #endif // // Set the MTU // if (config_iface.mtu() != pulled_ifp->mtu()) { if (should_disable && (! was_disabled)) { if (set_interface_status(config_iface.ifname(), config_iface.pif_index(), config_iface.interface_flags(), false, error_msg) != XORP_OK) { ret_value = XORP_ERROR; goto done; } was_disabled = true; } if (set_interface_mtu(config_iface.ifname(), config_iface.pif_index(), config_iface.mtu(), error_msg) != XORP_OK) { ret_value = XORP_ERROR; goto done; } } // // Set the MAC address // if (config_iface.mac() != pulled_ifp->mac()) { if (should_disable && (! was_disabled)) { if (set_interface_status(config_iface.ifname(), config_iface.pif_index(), config_iface.interface_flags(), false, error_msg) != XORP_OK) { ret_value = XORP_ERROR; goto done; } was_disabled = true; } if (set_interface_mac_address(config_iface.ifname(), config_iface.pif_index(), config_iface.mac(), error_msg) != XORP_OK) { ret_value = XORP_ERROR; goto done; } } done: if (was_disabled) { // We need to "hide" the Linux quirk of toggling the interface down / up // and make sure that our configuration is up to date. Otherwise we'll // later be notified (by the observer) that the interface went down // unexpectedly and things may break, when instead it is only a // transient state. wait_interface_status(pulled_ifp, false); if (set_interface_status(config_iface.ifname(), config_iface.pif_index(), config_iface.interface_flags(), true, error_msg) != XORP_OK) { return (XORP_ERROR); } wait_interface_status(pulled_ifp, true); } return (ret_value); } int IfConfigSetNetlinkSocket::config_interface_end( const IfTreeInterface* pulled_ifp, const IfTreeInterface& config_iface, string& error_msg) { if (pulled_ifp == NULL) { // Nothing to do: the interface has been deleted from the system return (XORP_OK); } // // Set the interface status // if (config_iface.enabled() != pulled_ifp->enabled()) { if (set_interface_status(config_iface.ifname(), config_iface.pif_index(), config_iface.interface_flags(), config_iface.enabled(), error_msg) != XORP_OK) { return (XORP_ERROR); } } return (XORP_OK); } int IfConfigSetNetlinkSocket::config_vif_begin(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeInterface& config_iface, const IfTreeVif& config_vif, string& error_msg) { UNUSED(pulled_ifp); UNUSED(config_iface); UNUSED(config_vif); UNUSED(error_msg); if (pulled_vifp == NULL) { // Nothing to do: the vif has been deleted from the system return (XORP_OK); } // XXX: nothing to do return (XORP_OK); } int IfConfigSetNetlinkSocket::config_vif_end(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeInterface& config_iface, const IfTreeVif& config_vif, string& error_msg) { UNUSED(pulled_ifp); if (pulled_vifp == NULL) { // Nothing to do: the vif has been deleted from the system return (XORP_OK); } // // XXX: If the interface name and vif name are different, then // they might have different status: the interface can be UP, while // the vif can be DOWN. // if (config_iface.ifname() != config_vif.vifname()) { // // Set the vif status // if (config_vif.enabled() != pulled_vifp->enabled()) { // // XXX: The interface and vif status setting mechanism is // equivalent for this platform. // if (set_interface_status(config_vif.vifname(), config_vif.pif_index(), config_vif.vif_flags(), config_vif.enabled(), error_msg) != XORP_OK) { return (XORP_ERROR); } } } return (XORP_OK); } int IfConfigSetNetlinkSocket::config_add_address(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeAddr4* pulled_addrp, const IfTreeInterface& config_iface, const IfTreeVif& config_vif, const IfTreeAddr4& config_addr, string& error_msg) { UNUSED(pulled_ifp); UNUSED(pulled_vifp); // // Test whether a new address // do { if (pulled_addrp == NULL) break; if (pulled_addrp->addr() != config_addr.addr()) break; if (pulled_addrp->broadcast() != config_addr.broadcast()) break; if (pulled_addrp->broadcast() && (pulled_addrp->bcast() != config_addr.bcast())) { break; } if (pulled_addrp->point_to_point() != config_addr.point_to_point()) break; if (pulled_addrp->point_to_point() && (pulled_addrp->endpoint() != config_addr.endpoint())) { break; } if (pulled_addrp->prefix_len() != config_addr.prefix_len()) break; // XXX: Same address, therefore ignore it return (XORP_OK); } while (false); // // Delete the old address if necessary // if (pulled_addrp != NULL) { if (delete_addr(config_iface.ifname(), config_vif.vifname(), config_vif.pif_index(), IPvX(config_addr.addr()), config_addr.prefix_len(), error_msg) != XORP_OK) { return (XORP_ERROR); } } // // Add the address // if (add_addr(config_iface.ifname(), config_vif.vifname(), config_vif.pif_index(), IPvX(config_addr.addr()), config_addr.prefix_len(), config_addr.broadcast(), IPvX(config_addr.bcast()), config_addr.point_to_point(), IPvX(config_addr.endpoint()), error_msg) != XORP_OK) { return (XORP_ERROR); } return (XORP_OK); } int IfConfigSetNetlinkSocket::config_delete_address( const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeAddr4* pulled_addrp, const IfTreeInterface& config_iface, const IfTreeVif& config_vif, const IfTreeAddr4& config_addr, string& error_msg) { UNUSED(pulled_ifp); UNUSED(pulled_vifp); UNUSED(pulled_addrp); // // Delete the address // if (delete_addr(config_iface.ifname(), config_vif.vifname(), config_vif.pif_index(), IPvX(config_addr.addr()), config_addr.prefix_len(), error_msg) != XORP_OK) { return (XORP_ERROR); } return (XORP_OK); } int IfConfigSetNetlinkSocket::config_add_address(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeAddr6* pulled_addrp, const IfTreeInterface& config_iface, const IfTreeVif& config_vif, const IfTreeAddr6& config_addr, string& error_msg) { UNUSED(pulled_ifp); UNUSED(pulled_vifp); // // Test whether a new address // do { if (pulled_addrp == NULL) break; if (pulled_addrp->addr() != config_addr.addr()) break; if (pulled_addrp->point_to_point() != config_addr.point_to_point()) break; if (pulled_addrp->point_to_point() && (pulled_addrp->endpoint() != config_addr.endpoint())) { break; } if (pulled_addrp->prefix_len() != config_addr.prefix_len()) break; // XXX: Same address, therefore ignore it return (XORP_OK); } while (false); // // Delete the old address if necessary // if (pulled_addrp != NULL) { if (delete_addr(config_iface.ifname(), config_vif.vifname(), config_vif.pif_index(), IPvX(config_addr.addr()), config_addr.prefix_len(), error_msg) != XORP_OK) { return (XORP_ERROR); } } // // Add the address // if (add_addr(config_iface.ifname(), config_vif.vifname(), config_vif.pif_index(), IPvX(config_addr.addr()), config_addr.prefix_len(), false, IPvX::ZERO(config_addr.addr().af()), config_addr.point_to_point(), IPvX(config_addr.endpoint()), error_msg) != XORP_OK) { return (XORP_ERROR); } return (XORP_OK); } int IfConfigSetNetlinkSocket::config_delete_address( const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeAddr6* pulled_addrp, const IfTreeInterface& config_iface, const IfTreeVif& config_vif, const IfTreeAddr6& config_addr, string& error_msg) { UNUSED(pulled_ifp); UNUSED(pulled_vifp); UNUSED(pulled_addrp); // // Delete the address // if (delete_addr(config_iface.ifname(), config_vif.vifname(), config_vif.pif_index(), IPvX(config_addr.addr()), config_addr.prefix_len(), error_msg) != XORP_OK) { return (XORP_ERROR); } return (XORP_OK); } int IfConfigSetNetlinkSocket::set_interface_status(const string& ifname, uint32_t if_index, uint32_t interface_flags, bool is_enabled, string& error_msg) { // // Update the interface flags // if (is_enabled) interface_flags |= IFF_UP; else interface_flags &= ~IFF_UP; struct ifreq ifreq; int s = -1; bool test_ioctl = false; // Evidently, some older systems don't return errors, but just do not // actually work. static int netlink_set_status_works = -1; if (netlink_set_status_works != 0) { static const size_t buffer_size = sizeof(struct nlmsghdr) + sizeof(struct ifinfomsg) + 2*sizeof(struct rtattr) + 512; union { uint8_t data[buffer_size]; struct nlmsghdr nlh; } buffer; struct nlmsghdr* nlh = &buffer.nlh; struct sockaddr_nl snl; struct ifinfomsg* ifinfomsg; NetlinkSocket& ns = *this; int last_errno = 0; memset(&buffer, 0, sizeof(buffer)); // Set the socket memset(&snl, 0, sizeof(snl)); snl.nl_family = AF_NETLINK; snl.nl_pid = 0; // nl_pid = 0 if destination is the kernel snl.nl_groups = 0; // // Set the request // nlh->nlmsg_len = NLMSG_LENGTH(sizeof(*ifinfomsg)); nlh->nlmsg_type = RTM_NEWLINK; nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE | NLM_F_ACK; nlh->nlmsg_seq = ns.seqno(); nlh->nlmsg_pid = ns.nl_pid(); ifinfomsg = static_cast(NLMSG_DATA(nlh)); ifinfomsg->ifi_family = AF_UNSPEC; ifinfomsg->ifi_type = IFLA_UNSPEC; ifinfomsg->ifi_index = if_index; ifinfomsg->ifi_flags = interface_flags; ifinfomsg->ifi_change = 0xffffffff; if (NLMSG_ALIGN(nlh->nlmsg_len) > sizeof(buffer)) { XLOG_FATAL("AF_NETLINK buffer size error: %u instead of %u", XORP_UINT_CAST(sizeof(buffer)), XORP_UINT_CAST(NLMSG_ALIGN(nlh->nlmsg_len))); } nlh->nlmsg_len = NLMSG_ALIGN(nlh->nlmsg_len); if (ns.sendto(&buffer, nlh->nlmsg_len, 0, reinterpret_cast(&snl), sizeof(snl)) != (ssize_t)nlh->nlmsg_len) { error_msg += c_format("Cannot set the interface flags to 0x%x on " "interface %s using netlink, error: %s\n", interface_flags, ifname.c_str(), strerror(errno)); test_ioctl = true; goto use_ioctl; } if (NlmUtils::check_netlink_request(_ns_reader, ns, nlh->nlmsg_seq, last_errno, error_msg) != XORP_OK) { error_msg += c_format("Request to set the interface flags to 0x%x on " "interface %s using netlink failed: %s\n", interface_flags, ifname.c_str(), error_msg.c_str()); test_ioctl = true; goto use_ioctl; // try ioctl method. } if (netlink_set_status_works == -1) { // test that it actually worked using ioctl. s = socket(AF_INET, SOCK_DGRAM, 0); memset(&ifreq, 0, sizeof(ifreq)); strncpy(ifreq.ifr_name, ifname.c_str(), sizeof(ifreq.ifr_name) - 1); ifreq.ifr_flags = interface_flags; if (ioctl(s, SIOCGIFFLAGS, &ifreq) < 0) { error_msg += c_format("Cannot get the interface flags on " "interface %s: %s\n", ifname.c_str(), strerror(errno)); } else { // Make sure we could at least set the enable/disable if ((!!is_enabled) != (!!(ifreq.ifr_flags & IFF_UP))) { error_msg += c_format("WARNING: Settting interface status using netlink failed" " on: %s. Will try to use ioctl method instead.\n", ifname.c_str()); // Seems it really is broken, don't try netlink again..just use ioctl. test_ioctl = true; goto use_ioctl; } else { netlink_set_status_works = 1; // looks like it worked } } } } else { // // XXX: a work-around in case the kernel doesn't support setting // the flags on an interface by using netlink. // In this case, the work-around is to use ioctl(). Sigh... // use_ioctl: if (s < 0) { s = socket(AF_INET, SOCK_DGRAM, 0); } if (s < 0) { XLOG_ERROR("Could not initialize IPv4 ioctl() socket while trying to set status: %s", strerror(errno)); return XORP_ERROR; } memset(&ifreq, 0, sizeof(ifreq)); strncpy(ifreq.ifr_name, ifname.c_str(), sizeof(ifreq.ifr_name) - 1); ifreq.ifr_flags = interface_flags; if (ioctl(s, SIOCSIFFLAGS, &ifreq) < 0) { error_msg += c_format("Cannot set the interface flags to 0x%x on " "interface %s using SIOCSIFFLAGS: %s\n", interface_flags, ifname.c_str(), strerror(errno)); close(s); return (XORP_ERROR); } else { // setting *seemed* to work, but let's check to make sure. if (test_ioctl) { memset(&ifreq, 0, sizeof(ifreq)); strncpy(ifreq.ifr_name, ifname.c_str(), sizeof(ifreq.ifr_name) - 1); ifreq.ifr_flags = interface_flags; if (ioctl(s, SIOCGIFFLAGS, &ifreq) >= 0) { if ((!!is_enabled) == (!!(ifreq.ifr_flags & IFF_UP))) { if (netlink_set_status_works != 0) { // Ok, it all worked. We assume that netlink just doesn't work, so // don't try it again. error_msg += c_format("WARNING: Settting interface status using netlink failed" " on: %s but ioctl method worked. Will use ioctl method from" " now on.\n", ifname.c_str()); netlink_set_status_works = 0; } } } } } close(s); } if (error_msg.size()) { XLOG_WARNING("%s", error_msg.c_str()); error_msg = ""; //we worked around the error..don't pass back warning messages! } return (XORP_OK); } void IfConfigSetNetlinkSocket::wait_interface_status(const IfTreeInterface* ifp, bool is_enabled) { NetlinkSocket* ns = dynamic_cast(fea_data_plane_manager() .ifconfig_observer()); string error_msg; // This is supposed to run only on Linux. I could enforce this more, but // I'll leave the code flexible in case other platforms need such a // mechanism. if (!ns) return; while (ifp->enabled() != is_enabled) { if (ns->force_recvmsg(true, error_msg) != XORP_OK) XLOG_ERROR("Netlink force_recvmsg(): %s", error_msg.c_str()); } } int IfConfigSetNetlinkSocket::set_interface_mac_address(const string& ifname, uint32_t if_index, const Mac& mac, string& error_msg) { struct ether_addr ether_addr; mac.copy_out(ether_addr); #ifdef RTM_SETLINK static const size_t buffer_size = sizeof(struct nlmsghdr) + sizeof(struct ifinfomsg) + 2*sizeof(struct rtattr) + 512; union { uint8_t data[buffer_size]; struct nlmsghdr nlh; } buffer; struct nlmsghdr* nlh = &buffer.nlh; struct sockaddr_nl snl; struct ifinfomsg* ifinfomsg; struct rtattr* rtattr; int rta_len; NetlinkSocket& ns = *this; int last_errno = 0; memset(&buffer, 0, sizeof(buffer)); // Set the socket memset(&snl, 0, sizeof(snl)); snl.nl_family = AF_NETLINK; snl.nl_pid = 0; // nl_pid = 0 if destination is the kernel snl.nl_groups = 0; // // Set the request // nlh->nlmsg_len = NLMSG_LENGTH(sizeof(*ifinfomsg)); nlh->nlmsg_type = RTM_SETLINK; nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE | NLM_F_ACK; nlh->nlmsg_seq = ns.seqno(); nlh->nlmsg_pid = ns.nl_pid(); ifinfomsg = static_cast(NLMSG_DATA(nlh)); ifinfomsg->ifi_family = AF_UNSPEC; ifinfomsg->ifi_type = IFLA_UNSPEC; // TODO: set to ARPHRD_ETHER ?? ifinfomsg->ifi_index = if_index; ifinfomsg->ifi_flags = 0; ifinfomsg->ifi_change = 0xffffffff; // Add the MAC address as an attribute rta_len = RTA_LENGTH(ETH_ALEN); if (NLMSG_ALIGN(nlh->nlmsg_len) + rta_len > sizeof(buffer)) { XLOG_FATAL("AF_NETLINK buffer size error: %u instead of %u", XORP_UINT_CAST(sizeof(buffer)), XORP_UINT_CAST(NLMSG_ALIGN(nlh->nlmsg_len) + rta_len)); } rtattr = IFLA_RTA(ifinfomsg); rtattr->rta_type = IFLA_ADDRESS; rtattr->rta_len = rta_len; memcpy(RTA_DATA(rtattr), ðer_addr, ETH_ALEN); nlh->nlmsg_len = NLMSG_ALIGN(nlh->nlmsg_len) + rta_len; if (ns.sendto(&buffer, nlh->nlmsg_len, 0, reinterpret_cast(&snl), sizeof(snl)) != (ssize_t)nlh->nlmsg_len) { error_msg += c_format("Cannot set the MAC address to %s " "on interface %s: %s\n", mac.str().c_str(), ifname.c_str(), strerror(errno)); return (XORP_ERROR); } string em; if (NlmUtils::check_netlink_request(_ns_reader, ns, nlh->nlmsg_seq, last_errno, em) != XORP_OK) { error_msg += c_format("Cannot set the MAC address to %s " "on interface %s using netlink: %s", mac.str().c_str(), ifname.c_str(), em.c_str()); return (XORP_ERROR); } return (XORP_OK); #elif defined(SIOCSIFHWADDR) // // XXX: a work-around in case the kernel doesn't support setting // the MAC address on an interface by using netlink. // In this case, the work-around is to use ioctl(). Sigh... // struct ifreq ifreq; UNUSED(if_index); int s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) { XLOG_FATAL("Could not initialize IPv4 ioctl() socket"); } memset(&ifreq, 0, sizeof(ifreq)); strncpy(ifreq.ifr_name, ifname.c_str(), sizeof(ifreq.ifr_name) - 1); ifreq.ifr_hwaddr.sa_family = ARPHRD_ETHER; memcpy(ifreq.ifr_hwaddr.sa_data, ðer_addr, ETH_ALEN); #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN ifreq.ifr_hwaddr.sa_len = ETH_ALEN; #endif if (ioctl(s, SIOCSIFHWADDR, &ifreq) < 0) { error_msg += c_format("Cannot set the MAC address to %s " "on interface %s using SIOCSIFHWADDR: %s", mac.str().c_str(), ifname.c_str(), strerror(errno)); close(s); return (XORP_ERROR); } close(s); return (XORP_OK); #else #error No mechanism to set the MAC address on an interface #endif } int IfConfigSetNetlinkSocket::set_interface_mtu(const string& ifname, uint32_t if_index, uint32_t mtu, string& error_msg) { #ifndef HAVE_NETLINK_SOCKETS_SET_MTU_IS_BROKEN static const size_t buffer_size = sizeof(struct nlmsghdr) + sizeof(struct ifinfomsg) + 2*sizeof(struct rtattr) + 512; union { uint8_t data[buffer_size]; struct nlmsghdr nlh; } buffer; struct nlmsghdr* nlh = &buffer.nlh; struct sockaddr_nl snl; struct ifinfomsg* ifinfomsg; struct rtattr* rtattr; int rta_len; NetlinkSocket& ns = *this; int last_errno = 0; memset(&buffer, 0, sizeof(buffer)); // Set the socket memset(&snl, 0, sizeof(snl)); snl.nl_family = AF_NETLINK; snl.nl_pid = 0; // nl_pid = 0 if destination is the kernel snl.nl_groups = 0; // // Set the request // nlh->nlmsg_len = NLMSG_LENGTH(sizeof(*ifinfomsg)); nlh->nlmsg_type = RTM_NEWLINK; nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE | NLM_F_ACK; nlh->nlmsg_seq = ns.seqno(); nlh->nlmsg_pid = ns.nl_pid(); ifinfomsg = static_cast(NLMSG_DATA(nlh)); ifinfomsg->ifi_family = AF_UNSPEC; ifinfomsg->ifi_type = IFLA_UNSPEC; ifinfomsg->ifi_index = if_index; ifinfomsg->ifi_flags = 0; ifinfomsg->ifi_change = 0xffffffff; // Add the MTU as an attribute unsigned int uint_mtu = mtu; rta_len = RTA_LENGTH(sizeof(unsigned int)); if (NLMSG_ALIGN(nlh->nlmsg_len) + rta_len > sizeof(buffer)) { XLOG_FATAL("AF_NETLINK buffer size error: %u instead of %u", XORP_UINT_CAST(sizeof(buffer)), XORP_UINT_CAST(NLMSG_ALIGN(nlh->nlmsg_len) + rta_len)); } rtattr = IFLA_RTA(ifinfomsg); rtattr->rta_type = IFLA_MTU; rtattr->rta_len = rta_len; memcpy(RTA_DATA(rtattr), &uint_mtu, sizeof(uint_mtu)); nlh->nlmsg_len = NLMSG_ALIGN(nlh->nlmsg_len) + rta_len; if (ns.sendto(&buffer, nlh->nlmsg_len, 0, reinterpret_cast(&snl), sizeof(snl)) != (ssize_t)nlh->nlmsg_len) { error_msg = c_format("Cannot set the MTU to %u on " "interface %s: %s", mtu, ifname.c_str(), strerror(errno)); return (XORP_ERROR); } if (NlmUtils::check_netlink_request(_ns_reader, ns, nlh->nlmsg_seq, last_errno, error_msg) != XORP_OK) { error_msg = c_format("Cannot set the MTU to %u on " "interface %s: %s", mtu, ifname.c_str(), error_msg.c_str()); return (XORP_ERROR); } return (XORP_OK); #else // HAVE_NETLINK_SOCKETS_SET_MTU_IS_BROKEN // // XXX: a work-around in case the kernel doesn't support setting // the MTU on an interface by using netlink. // In this case, the work-around is to use ioctl(). Sigh... // struct ifreq ifreq; UNUSED(if_index); int s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) { XLOG_FATAL("Could not initialize IPv4 ioctl() socket"); } memset(&ifreq, 0, sizeof(ifreq)); strncpy(ifreq.ifr_name, ifname.c_str(), sizeof(ifreq.ifr_name) - 1); ifreq.ifr_mtu = mtu; if (ioctl(s, SIOCSIFMTU, &ifreq) < 0) { error_msg = c_format("Cannot set the MTU to %u on " "interface %s: %s", mtu, ifname.c_str(), strerror(errno)); close(s); return (XORP_ERROR); } close(s); return (XORP_OK); #endif // HAVE_NETLINK_SOCKETS_SET_MTU_IS_BROKEN } int IfConfigSetNetlinkSocket::add_addr(const string& ifname, const string& vifname, uint32_t if_index, const IPvX& addr, uint32_t prefix_len, bool is_broadcast, const IPvX& broadcast_addr, bool is_point_to_point, const IPvX& endpoint_addr, string& error_msg) { static const size_t buffer_size = sizeof(struct nlmsghdr) + sizeof(struct ifinfomsg) + 2*sizeof(struct rtattr) + 512; union { uint8_t data[buffer_size]; struct nlmsghdr nlh; } buffer; struct nlmsghdr* nlh = &buffer.nlh; struct sockaddr_nl snl; struct ifaddrmsg* ifaddrmsg; struct rtattr* rtattr; int rta_len; uint8_t* data; NetlinkSocket& ns = *this; void* rta_align_data; int last_errno = 0; memset(&buffer, 0, sizeof(buffer)); // Set the socket memset(&snl, 0, sizeof(snl)); snl.nl_family = AF_NETLINK; snl.nl_pid = 0; // nl_pid = 0 if destination is the kernel snl.nl_groups = 0; // There are all sorts of bugs with how Xorp handles VLANs (as vifs under // a physical interface). // 1: It passes the pif_index..when it should use the vif's ifindex. // 2: It's passing the pif_index as 0, which is wrong in all cases. // Add some hacks to work-around this until we clean up vifs more properly. if ((if_index == 0) || (strcmp(ifname.c_str(), vifname.c_str()) != 0)) { if_index = if_nametoindex(vifname.c_str()); } // // Set the request // nlh->nlmsg_len = NLMSG_LENGTH(sizeof(*ifaddrmsg)); nlh->nlmsg_type = RTM_NEWADDR; nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE | NLM_F_ACK; nlh->nlmsg_seq = ns.seqno(); nlh->nlmsg_pid = ns.nl_pid(); ifaddrmsg = static_cast(NLMSG_DATA(nlh)); ifaddrmsg->ifa_family = addr.af(); ifaddrmsg->ifa_prefixlen = prefix_len; ifaddrmsg->ifa_flags = 0; // TODO: XXX: PAVPAVPAV: OK if 0? ifaddrmsg->ifa_scope = 0; // TODO: XXX: PAVPAVPAV: OK if 0? ifaddrmsg->ifa_index = if_index; // Add the address as an attribute rta_len = RTA_LENGTH(addr.addr_bytelen()); if (NLMSG_ALIGN(nlh->nlmsg_len) + rta_len > sizeof(buffer)) { XLOG_FATAL("AF_NETLINK buffer size error: %u instead of %u", XORP_UINT_CAST(sizeof(buffer)), XORP_UINT_CAST(NLMSG_ALIGN(nlh->nlmsg_len) + rta_len)); } rtattr = IFA_RTA(ifaddrmsg); rtattr->rta_type = IFA_LOCAL; rtattr->rta_len = rta_len; data = static_cast(RTA_DATA(rtattr)); addr.copy_out(data); nlh->nlmsg_len = NLMSG_ALIGN(nlh->nlmsg_len) + rta_len; if (is_broadcast || is_point_to_point) { // Set the broadcast or point-to-point address rta_len = RTA_LENGTH(addr.addr_bytelen()); if (NLMSG_ALIGN(nlh->nlmsg_len) + rta_len > sizeof(buffer)) { XLOG_FATAL("AF_NETLINK buffer size error: %u instead of %u", XORP_UINT_CAST(sizeof(buffer)), XORP_UINT_CAST(NLMSG_ALIGN(nlh->nlmsg_len) + rta_len)); } rta_align_data = reinterpret_cast(rtattr) + RTA_ALIGN(rtattr->rta_len); rtattr = static_cast(rta_align_data); rtattr->rta_type = IFA_UNSPEC; rtattr->rta_len = rta_len; data = static_cast(RTA_DATA(rtattr)); if (is_broadcast) { rtattr->rta_type = IFA_BROADCAST; broadcast_addr.copy_out(data); } if (is_point_to_point) { rtattr->rta_type = IFA_ADDRESS; endpoint_addr.copy_out(data); } nlh->nlmsg_len = NLMSG_ALIGN(nlh->nlmsg_len) + rta_len; } if (ns.sendto(&buffer, nlh->nlmsg_len, 0, reinterpret_cast(&snl), sizeof(snl)) != (ssize_t)nlh->nlmsg_len) { error_msg = c_format("IfConfigSetNetlinkSocket::add_addr: sendto: Cannot add address '%s' " "on interface '%s' vif '%s', if_index: %i: %s", addr.str().c_str(), ifname.c_str(), vifname.c_str(), if_index, strerror(errno)); return (XORP_ERROR); } if (NlmUtils::check_netlink_request(_ns_reader, ns, nlh->nlmsg_seq, last_errno, error_msg) != XORP_OK) { error_msg = c_format("IfConfigSetNetlinkSocket::add_addr: check_nl_req: Cannot add address '%s' " "on interface '%s' vif '%s', if_index: %i : %s", addr.str().c_str(), ifname.c_str(), vifname.c_str(), if_index, error_msg.c_str()); return (XORP_ERROR); } return (XORP_OK); } int IfConfigSetNetlinkSocket::delete_addr(const string& ifname, const string& vifname, uint32_t if_index, const IPvX& addr, uint32_t prefix_len, string& error_msg) { static const size_t buffer_size = sizeof(struct nlmsghdr) + sizeof(struct ifinfomsg) + 2*sizeof(struct rtattr) + 512; union { uint8_t data[buffer_size]; struct nlmsghdr nlh; } buffer; struct nlmsghdr* nlh = &buffer.nlh; struct sockaddr_nl snl; struct ifaddrmsg* ifaddrmsg; struct rtattr* rtattr; int rta_len; uint8_t* data; NetlinkSocket& ns = *this; int last_errno = 0; memset(&buffer, 0, sizeof(buffer)); // Set the socket memset(&snl, 0, sizeof(snl)); snl.nl_family = AF_NETLINK; snl.nl_pid = 0; // nl_pid = 0 if destination is the kernel snl.nl_groups = 0; // There are all sorts of bugs with how Xorp handles VLANs (as vifs under // a physical interface). // 1: It passes the pif_index..when it should use the vif's ifindex. // 2: It's passing the pif_index as 0, which is wrong in all cases. // Add some hacks to work-around this until we clean up vifs more properly. if ((if_index == 0) || (strcmp(ifname.c_str(), vifname.c_str()) != 0)) { if_index = if_nametoindex(vifname.c_str()); } // // Set the request // nlh->nlmsg_len = NLMSG_LENGTH(sizeof(*ifaddrmsg)); nlh->nlmsg_type = RTM_DELADDR; nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE | NLM_F_ACK; nlh->nlmsg_seq = ns.seqno(); nlh->nlmsg_pid = ns.nl_pid(); ifaddrmsg = static_cast(NLMSG_DATA(nlh)); ifaddrmsg->ifa_family = addr.af(); ifaddrmsg->ifa_prefixlen = prefix_len; ifaddrmsg->ifa_flags = 0; // TODO: XXX: PAVPAVPAV: OK if 0? ifaddrmsg->ifa_scope = 0; // TODO: XXX: PAVPAVPAV: OK if 0? ifaddrmsg->ifa_index = if_index; // Add the address as an attribute rta_len = RTA_LENGTH(addr.addr_bytelen()); if (NLMSG_ALIGN(nlh->nlmsg_len) + rta_len > sizeof(buffer)) { XLOG_FATAL("AF_NETLINK buffer size error: %u instead of %u", XORP_UINT_CAST(sizeof(buffer)), XORP_UINT_CAST(NLMSG_ALIGN(nlh->nlmsg_len) + rta_len)); } rtattr = IFA_RTA(ifaddrmsg); rtattr->rta_type = IFA_LOCAL; rtattr->rta_len = rta_len; data = static_cast(RTA_DATA(rtattr)); addr.copy_out(data); nlh->nlmsg_len = NLMSG_ALIGN(nlh->nlmsg_len) + rta_len; if (ns.sendto(&buffer, nlh->nlmsg_len, 0, reinterpret_cast(&snl), sizeof(snl)) != (ssize_t)nlh->nlmsg_len) { error_msg = c_format("Cannot delete address '%s' " "on interface '%s' vif '%s': %s", addr.str().c_str(), ifname.c_str(), vifname.c_str(), strerror(errno)); return (XORP_ERROR); } if (NlmUtils::check_netlink_request(_ns_reader, ns, nlh->nlmsg_seq, last_errno, error_msg) != XORP_OK) { error_msg = c_format("Cannot delete address '%s' " "on interface '%s' vif '%s': %s", addr.str().c_str(), ifname.c_str(), vifname.c_str(), error_msg.c_str()); return (XORP_ERROR); } return (XORP_OK); } #endif // HAVE_NETLINK_SOCKETS xorp/fea/data_plane/ifconfig/ifconfig_observer_netlink_socket.cc0000664000076400007640000001074611540225524025375 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include #ifdef HAVE_NETLINK_SOCKETS #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #ifdef HAVE_LINUX_TYPES_H #include #endif #ifdef HAVE_LINUX_RTNETLINK_H #include #endif #include "fea/ifconfig.hh" #include "fea/fibconfig.hh" #include "ifconfig_get_netlink_socket.hh" #include "ifconfig_observer_netlink_socket.hh" // // Observe information change about network interface configuration from // the underlying system. // // The mechanism to observe the information is netlink(7) sockets. // IfConfigObserverNetlinkSocket::IfConfigObserverNetlinkSocket(FeaDataPlaneManager& fea_data_plane_manager) : IfConfigObserver(fea_data_plane_manager), NetlinkSocket(fea_data_plane_manager.eventloop(), fea_data_plane_manager.fibconfig().get_netlink_filter_table_id()), NetlinkSocketObserver(*(NetlinkSocket *)this) { } IfConfigObserverNetlinkSocket::~IfConfigObserverNetlinkSocket() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the netlink(7) sockets mechanism to observe " "information about network interfaces from the underlying " "system: %s", error_msg.c_str()); } } int IfConfigObserverNetlinkSocket::start(string& error_msg) { uint32_t nl_groups = 0; if (_is_running) return (XORP_OK); // // Listen to the netlink multicast group for network interfaces status // and IPv4 addresses. // if (fea_data_plane_manager().have_ipv4()) nl_groups |= (RTMGRP_LINK | RTMGRP_IPV4_IFADDR); #ifdef HAVE_IPV6 // // Listen to the netlink multicast group for network interfaces status // and IPv6 addresses. // if (fea_data_plane_manager().have_ipv6()) nl_groups |= (RTMGRP_LINK | RTMGRP_IPV6_IFADDR); #endif // HAVE_IPV6 // // Set the netlink multicast groups to listen for on the netlink socket // NetlinkSocket::set_nl_groups(nl_groups); if (NetlinkSocket::start(error_msg) != XORP_OK) return (XORP_ERROR); _is_running = true; return (XORP_OK); } int IfConfigObserverNetlinkSocket::stop(string& error_msg) { if (! _is_running) return (XORP_OK); if (NetlinkSocket::stop(error_msg) != XORP_OK) return (XORP_ERROR); _is_running = false; return (XORP_OK); } void IfConfigObserverNetlinkSocket::receive_data(const vector& buffer) { bool modified = false; int nl_errno = 0; // Pre-processing cleanup ifconfig().system_config().finalize_state(); if (IfConfigGetNetlinkSocket::parse_buffer_netlink_socket( ifconfig(), ifconfig().system_config(), buffer, modified, nl_errno) != XORP_OK) { return; } // TODO: If the parse_buffer call above didn't modify anything, is the vlan pull // below actually needed? // // Get the VLAN vif info // IfConfigVlanGet* ifconfig_vlan_get; ifconfig_vlan_get = fea_data_plane_manager().ifconfig_vlan_get(); if (ifconfig_vlan_get != NULL) { if (ifconfig_vlan_get->pull_config(ifconfig().system_config(), modified) != XORP_OK) { XLOG_ERROR("Unknown error while pulling VLAN information"); } } if (modified) { // // Propagate the changes from the system config to the merged config // IfTree& merged_config = ifconfig().merged_config(); merged_config.align_with_observed_changes(ifconfig().system_config(), ifconfig().user_config()); ifconfig().report_updates(merged_config); merged_config.finalize_state(); } } void IfConfigObserverNetlinkSocket::netlink_socket_data(const vector& buffer) { receive_data(buffer); } #endif // HAVE_NETLINK_SOCKETS xorp/fea/data_plane/ifconfig/ifconfig_get_netlink_socket.cc0000664000076400007640000005666211703345405024336 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2012 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include #ifdef HAVE_NETLINK_SOCKETS #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #ifdef HAVE_LINUX_TYPES_H #include #endif #ifdef HAVE_LINUX_RTNETLINK_H #include #include #include #endif #ifdef HAVE_NET_IF_H #include #endif #include "fea/ifconfig.hh" #include "fea/fibconfig.hh" #include "ifconfig_get_netlink_socket.hh" #include "../control_socket/netlink_socket_utilities.hh" // // Parse information about network interface configuration change from // the underlying system. // // The information to parse is in NETLINK format // (e.g., obtained by netlink(7) sockets mechanism). // // Reading netlink(3) manual page is a good start for understanding this // // // Get information about network interfaces from the underlying system. // // The mechanism to obtain the information is netlink(7) sockets. // IfConfigGetNetlinkSocket::IfConfigGetNetlinkSocket(FeaDataPlaneManager& fea_data_plane_manager) : IfConfigGet(fea_data_plane_manager), NetlinkSocket(fea_data_plane_manager.eventloop(), fea_data_plane_manager.fibconfig().get_netlink_filter_table_id()), _ns_reader(*(NetlinkSocket *)this) { can_get_single = -1; } IfConfigGetNetlinkSocket::~IfConfigGetNetlinkSocket() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the netlink(7) sockets mechanism to get " "information about network interfaces from the underlying " "system: %s", error_msg.c_str()); } } int IfConfigGetNetlinkSocket::start(string& error_msg) { if (_is_running) return (XORP_OK); if (NetlinkSocket::start(error_msg) != XORP_OK) return (XORP_ERROR); _is_running = true; return (XORP_OK); } int IfConfigGetNetlinkSocket::stop(string& error_msg) { if (! _is_running) return (XORP_OK); if (NetlinkSocket::stop(error_msg) != XORP_OK) return (XORP_ERROR); _is_running = false; return (XORP_OK); } int IfConfigGetNetlinkSocket::pull_config(const IfTree* local_cfg, IfTree& iftree) { return read_config(local_cfg, iftree); } int IfConfigGetNetlinkSocket::try_read_config_one(IfTree& iftree, const char* ifname, int ifindex) { int nl_errno = 0; int rv = read_config_one(iftree, ifname, ifindex, nl_errno); if (rv != XORP_OK) { if ((nl_errno == EINVAL) && (can_get_single == -1)) { // Attempt work-around, maybe kernel is too old to do single requests. can_get_single = 0; nl_errno = 0; rv = read_config_one(iftree, ifname, ifindex, nl_errno); if (rv == XORP_OK) { // Might have worked...look for device. if (iftree.find_interface(ifname)) { XLOG_WARNING("WARNING: It seems that we cannot get a single Network device" " via NETLINK, probably due to an older kernel. Will enable" " work-around to grab entire device listing instead. This may" " cause a slight performance hit on systems with lots of interfaces" " but for most users it should not be noticeable."); } else { // Still no luck..assume netlink get single can still work. can_get_single = -1; } } } } else { if (can_get_single == -1) { XLOG_WARNING("NOTE: Netlink get single network device works on this system."); can_get_single = 1; } } return rv; } int IfConfigGetNetlinkSocket::pull_config_one(IfTree& iftree, const char* ifname, int ifindex) { return try_read_config_one(iftree, ifname, ifindex); } int findDeviceIndex(const char* device_name) { struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, device_name, IFNAMSIZ); //max length of a device name. int ioctl_sock = socket(AF_INET, SOCK_STREAM, 0); if (ioctl_sock >= 0) { if (ioctl(ioctl_sock, SIOCGIFINDEX, &ifr) >= 0) { close(ioctl_sock); return ifr.ifr_ifindex; } else { close(ioctl_sock); } } return -1; }//findDeviceIndex int IfConfigGetNetlinkSocket::read_config_one(IfTree& iftree, const char* ifname, int if_index, int& nl_errno) { static const size_t buffer_size = sizeof(struct nlmsghdr) + sizeof(struct ifinfomsg) + sizeof(struct ifaddrmsg) + 512; union { uint8_t data[buffer_size]; struct nlmsghdr nlh; } buffer; struct nlmsghdr* nlh = &buffer.nlh; struct sockaddr_nl snl; struct ifinfomsg* ifinfomsg; NetlinkSocket& ns = *this; //XLOG_WARNING("read_config_one: tree: %s ifname: %s if_index: %i\n", // iftree.getName().c_str(), ifname, if_index); if ((if_index < 1) && ifname) { // Try to resolve it based on ifname if_index = findDeviceIndex(ifname); //XLOG_WARNING("tried to resolve ifindex: %i\n", if_index); } if (if_index < 1) { return XORP_ERROR; } // Set the socket memset(&snl, 0, sizeof(snl)); snl.nl_family = AF_NETLINK; snl.nl_pid = 0; // nl_pid = 0 if destination is the kernel snl.nl_groups = 0; // // Set the request for network interfaces // memset(&buffer, 0, sizeof(buffer)); nlh->nlmsg_len = NLMSG_LENGTH(sizeof(*ifinfomsg)); nlh->nlmsg_type = RTM_GETLINK; nlh->nlmsg_flags = NLM_F_REQUEST; // Get info for single device /** Older kernels (2.6.18, for instance), cannot get a single network device * unless configured for CONFIG_NET_WIRELESS_RTNETLINK. * They just return EINVAL. So, for these, we need to include the NLM_F_ROOT * flag. */ if (can_get_single == 0) { nlh->nlmsg_flags |= NLM_F_ROOT; // get the whole table } nlh->nlmsg_seq = ns.seqno(); nlh->nlmsg_pid = ns.nl_pid(); ifinfomsg = reinterpret_cast(NLMSG_DATA(nlh)); ifinfomsg->ifi_family = AF_UNSPEC; ifinfomsg->ifi_type = IFLA_UNSPEC; ifinfomsg->ifi_index = if_index; ifinfomsg->ifi_flags = 0; ifinfomsg->ifi_change = 0xffffffff; if (ns.sendto(&buffer, nlh->nlmsg_len, 0, reinterpret_cast(&snl), sizeof(snl)) != (ssize_t)nlh->nlmsg_len) { XLOG_ERROR("Error writing to netlink socket: %s", strerror(errno)); return (XORP_ERROR); } // // Force to receive data from the kernel, and then parse it // // string error_msg; if (_ns_reader.receive_data(ns, nlh->nlmsg_seq, error_msg) != XORP_OK) { XLOG_ERROR("Error reading from netlink socket: %s", error_msg.c_str()); return (XORP_ERROR); } bool modified = false; if (parse_buffer_netlink_socket(ifconfig(), iftree, _ns_reader.buffer(), modified, nl_errno) != XORP_OK) { return (XORP_ERROR); } return XORP_OK; } int IfConfigGetNetlinkSocket::parse_buffer_netlink_socket(IfConfig& ifconfig, IfTree& iftree, const vector& buffer, bool& modified, int& nl_errno) { size_t buffer_bytes = buffer.size(); AlignData align_data(buffer); const struct nlmsghdr* nlh; bool recognized = false; for (nlh = align_data.payload(); NLMSG_OK(nlh, buffer_bytes); nlh = NLMSG_NEXT(nlh, buffer_bytes)) { void* nlmsg_data = NLMSG_DATA(nlh); //XLOG_WARNING("nlh msg received, type %s(%d) (%d bytes)\n", // NlmUtils::nlm_msg_type(nlh->nlmsg_type).c_str(), // nlh->nlmsg_type, nlh->nlmsg_len); switch (nlh->nlmsg_type) { case NLMSG_ERROR: { const struct nlmsgerr* err; err = reinterpret_cast(nlmsg_data); if (nlh->nlmsg_len < NLMSG_LENGTH(sizeof(*err))) { XLOG_ERROR("AF_NETLINK nlmsgerr length error"); break; } errno = -err->error; nl_errno = -err->error; XLOG_ERROR("AF_NETLINK NLMSG_ERROR: %s msg->len: %u msg->type: %hu(%s) " " msg->flags: %hu msg->seq: %u msg->pid: %u", strerror(errno), err->msg.nlmsg_len, err->msg.nlmsg_type, NlmUtils::nlm_msg_type(err->msg.nlmsg_type).c_str(), err->msg.nlmsg_flags, err->msg.nlmsg_seq, err->msg.nlmsg_pid); } break; case NLMSG_DONE: { if (! recognized) return (XORP_ERROR); return (XORP_OK); } break; case NLMSG_NOOP: break; case RTM_NEWLINK: case RTM_DELLINK: { const struct ifinfomsg* ifinfomsg; int rta_len = IFLA_PAYLOAD(nlh); if (rta_len < 0) { XLOG_ERROR("AF_NETLINK ifinfomsg length error"); break; } ifinfomsg = reinterpret_cast(nlmsg_data); if (nlh->nlmsg_type == RTM_NEWLINK) NlmUtils::nlm_cond_newlink_to_fea_cfg(ifconfig.user_config(), iftree, ifinfomsg, rta_len, modified); else NlmUtils::nlm_dellink_to_fea_cfg(iftree, ifinfomsg, rta_len, modified); recognized = true; } break; case RTM_NEWADDR: case RTM_DELADDR: { const struct ifaddrmsg* ifaddrmsg; int rta_len = IFA_PAYLOAD(nlh); if (rta_len < 0) { XLOG_ERROR("AF_NETLINK ifaddrmsg length error"); break; } ifaddrmsg = reinterpret_cast(nlmsg_data); if (nlh->nlmsg_type == RTM_NEWADDR) { NlmUtils::nlm_cond_newdeladdr_to_fea_cfg(ifconfig.user_config(), iftree, ifaddrmsg, rta_len, false, modified); } else { NlmUtils::nlm_cond_newdeladdr_to_fea_cfg(ifconfig.user_config(), iftree, ifaddrmsg, rta_len, true, modified); } recognized = true; } break; default: debug_msg("Unhandled type %s(%d) (%d bytes)\n", NlmUtils::nlm_msg_type(nlh->nlmsg_type).c_str(), nlh->nlmsg_type, nlh->nlmsg_len); //XLOG_WARNING("Unhandled type %s(%d) (%d bytes)\n", // NlmUtils::nlm_msg_type(nlh->nlmsg_type).c_str(), // nlh->nlmsg_type, nlh->nlmsg_len); break; } } if (! recognized) return (XORP_ERROR); return (XORP_OK); } int IfConfigGetNetlinkSocket::read_config_all(IfTree& iftree) { static const size_t buffer_size = sizeof(struct nlmsghdr) + sizeof(struct ifinfomsg) + sizeof(struct ifaddrmsg) + 512; union { uint8_t data[buffer_size]; struct nlmsghdr nlh; } buffer; struct nlmsghdr* nlh = &buffer.nlh; struct sockaddr_nl snl; struct ifinfomsg* ifinfomsg; struct ifaddrmsg* ifaddrmsg; NetlinkSocket& ns = *this; // // Set the request. First the socket, then the request itself. // // Note that first we send a request for the network interfaces, // then a request for the IPv4 addresses, and then a request // for the IPv6 addresses. // // Set the socket memset(&snl, 0, sizeof(snl)); snl.nl_family = AF_NETLINK; snl.nl_pid = 0; // nl_pid = 0 if destination is the kernel snl.nl_groups = 0; // // Set the request for network interfaces // memset(&buffer, 0, sizeof(buffer)); nlh->nlmsg_len = NLMSG_LENGTH(sizeof(*ifinfomsg)); nlh->nlmsg_type = RTM_GETLINK; nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT; // Get the whole table nlh->nlmsg_seq = ns.seqno(); nlh->nlmsg_pid = ns.nl_pid(); ifinfomsg = reinterpret_cast(NLMSG_DATA(nlh)); ifinfomsg->ifi_family = AF_UNSPEC; ifinfomsg->ifi_type = IFLA_UNSPEC; ifinfomsg->ifi_index = 0; ifinfomsg->ifi_flags = 0; ifinfomsg->ifi_change = 0xffffffff; if (ns.sendto(&buffer, nlh->nlmsg_len, 0, reinterpret_cast(&snl), sizeof(snl)) != (ssize_t)nlh->nlmsg_len) { XLOG_ERROR("Error writing to netlink socket: %s", strerror(errno)); return (XORP_ERROR); } // // Force to receive data from the kernel, and then parse it // // // XXX: setting the flag below is a work-around hack because of a // Linux kernel bug: when we read the whole table the kernel // may not set the NLM_F_MULTI flag for the multipart messages. // string error_msg; ns.set_multipart_message_read(true); if (_ns_reader.receive_data(ns, nlh->nlmsg_seq, error_msg) != XORP_OK) { ns.set_multipart_message_read(false); XLOG_ERROR("Error reading from netlink socket: %s", error_msg.c_str()); return (XORP_ERROR); } // XXX: reset the multipart message read hackish flag ns.set_multipart_message_read(false); if (parse_buffer_netlink_socket(ifconfig(), iftree, _ns_reader.buffer()) != XORP_OK) { return (XORP_ERROR); } // // Create a list with the interface indexes // list if_index_list; uint32_t if_index; IfTree::IfMap::const_iterator if_iter; for (if_iter = iftree.interfaces().begin(); if_iter != iftree.interfaces().end(); ++if_iter) { const IfTreeInterface& iface = *(if_iter->second); IfTreeInterface::VifMap::const_iterator vif_iter; for (vif_iter = iface.vifs().begin(); vif_iter != iface.vifs().end(); ++vif_iter) { const IfTreeVif& vif = *(vif_iter->second); if_index = vif.pif_index(); if_index_list.push_back(if_index); } } // // Send requests for the addresses of each interface we just found // list::const_iterator if_index_iter; for (if_index_iter = if_index_list.begin(); if_index_iter != if_index_list.end(); ++if_index_iter) { if_index = *if_index_iter; // // Set the request for IPv4 addresses // if (fea_data_plane_manager().have_ipv4()) { memset(&buffer, 0, sizeof(buffer)); nlh->nlmsg_len = NLMSG_LENGTH(sizeof(*ifaddrmsg)); nlh->nlmsg_type = RTM_GETADDR; // Get the whole table nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT; nlh->nlmsg_seq = ns.seqno(); nlh->nlmsg_pid = ns.nl_pid(); ifaddrmsg = reinterpret_cast(NLMSG_DATA(nlh)); ifaddrmsg->ifa_family = AF_INET; ifaddrmsg->ifa_prefixlen = 0; ifaddrmsg->ifa_flags = 0; ifaddrmsg->ifa_scope = 0; ifaddrmsg->ifa_index = if_index; if (ns.sendto(&buffer, nlh->nlmsg_len, 0, reinterpret_cast(&snl), sizeof(snl)) != (ssize_t)nlh->nlmsg_len) { XLOG_ERROR("Error writing to netlink socket: %s", strerror(errno)); return (XORP_ERROR); } // // Force to receive data from the kernel, and then parse it // // // XXX: setting the flag below is a work-around hack because of a // Linux kernel bug: when we read the whole table the kernel // may not set the NLM_F_MULTI flag for the multipart messages. // string error_msg; ns.set_multipart_message_read(true); if (_ns_reader.receive_data(ns, nlh->nlmsg_seq, error_msg) != XORP_OK) { ns.set_multipart_message_read(false); XLOG_ERROR("Error reading from netlink socket: %s", error_msg.c_str()); return (XORP_ERROR); } // XXX: reset the multipart message read hackish flag ns.set_multipart_message_read(false); if (parse_buffer_netlink_socket(ifconfig(), iftree, _ns_reader.buffer()) != XORP_OK) { return (XORP_ERROR); } } #ifdef HAVE_IPV6 // // Set the request for IPv6 addresses // if (fea_data_plane_manager().have_ipv6()) { memset(&buffer, 0, sizeof(buffer)); nlh->nlmsg_len = NLMSG_LENGTH(sizeof(*ifaddrmsg)); nlh->nlmsg_type = RTM_GETADDR; // Get the whole table nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT; nlh->nlmsg_seq = ns.seqno(); nlh->nlmsg_pid = ns.nl_pid(); ifaddrmsg = reinterpret_cast(NLMSG_DATA(nlh)); ifaddrmsg->ifa_family = AF_INET6; ifaddrmsg->ifa_prefixlen = 0; ifaddrmsg->ifa_flags = 0; ifaddrmsg->ifa_scope = 0; ifaddrmsg->ifa_index = if_index; if (ns.sendto(&buffer, nlh->nlmsg_len, 0, reinterpret_cast(&snl), sizeof(snl)) != (ssize_t)nlh->nlmsg_len) { XLOG_ERROR("Error writing to netlink socket: %s", strerror(errno)); return (XORP_ERROR); } // // Force to receive data from the kernel, and then parse it // // // XXX: setting the flag below is a work-around hack because of a // Linux kernel bug: when we read the whole table the kernel // may not set the NLM_F_MULTI flag for the multipart messages. // string error_msg; ns.set_multipart_message_read(true); if (_ns_reader.receive_data(ns, nlh->nlmsg_seq, error_msg) != XORP_OK) { ns.set_multipart_message_read(false); XLOG_ERROR("Error reading from netlink socket: %s", error_msg.c_str()); return (XORP_ERROR); } // XXX: reset the multipart message read hackish flag ns.set_multipart_message_read(false); if (parse_buffer_netlink_socket(ifconfig(), iftree, _ns_reader.buffer()) != XORP_OK) { return (XORP_ERROR); } } #endif // HAVE_IPV6 } // // Get the VLAN vif info // IfConfigVlanGet* ifconfig_vlan_get; ifconfig_vlan_get = fea_data_plane_manager().ifconfig_vlan_get(); if (ifconfig_vlan_get != NULL) { bool modified = false; if (ifconfig_vlan_get->pull_config(iftree, modified) != XORP_OK) return (XORP_ERROR); } return (XORP_OK); }//read_config_all int IfConfigGetNetlinkSocket::read_config(const IfTree* local_cfg, IfTree& iftree) { if ((!local_cfg) || (can_get_single == 0)) { return read_config_all(iftree); } static const size_t buffer_size = sizeof(struct nlmsghdr) + sizeof(struct ifinfomsg) + sizeof(struct ifaddrmsg) + 512; union { uint8_t data[buffer_size]; struct nlmsghdr nlh; } buffer; struct nlmsghdr* nlh = &buffer.nlh; struct sockaddr_nl snl; struct ifaddrmsg* ifaddrmsg; NetlinkSocket& ns = *this; // // Set the request. First the socket, then the request itself. // // Note that first we send a request for the network interfaces, // then a request for the IPv4 addresses, and then a request // for the IPv6 addresses. // IfTree::IfMap::const_iterator if_iter; for (if_iter = local_cfg->interfaces().begin(); if_iter != local_cfg->interfaces().end(); ++if_iter) { const IfTreeInterface& iface = *(if_iter->second); IfTreeInterface::VifMap::const_iterator vif_iter; for (vif_iter = iface.vifs().begin(); vif_iter != iface.vifs().end(); ++vif_iter) { const IfTreeVif& vif = *(vif_iter->second); try_read_config_one(iftree, vif.vifname().c_str(), vif.pif_index()); } } // // Create a list with the interface indexes // list if_index_list; uint32_t if_index; for (if_iter = iftree.interfaces().begin(); if_iter != iftree.interfaces().end(); ++if_iter) { const IfTreeInterface& iface = *(if_iter->second); IfTreeInterface::VifMap::const_iterator vif_iter; for (vif_iter = iface.vifs().begin(); vif_iter != iface.vifs().end(); ++vif_iter) { const IfTreeVif& vif = *(vif_iter->second); if_index = vif.pif_index(); if_index_list.push_back(if_index); } } // Set the socket memset(&snl, 0, sizeof(snl)); snl.nl_family = AF_NETLINK; snl.nl_pid = 0; // nl_pid = 0 if destination is the kernel snl.nl_groups = 0; // // Send requests for the addresses of each interface we just found // list::const_iterator if_index_iter; for (if_index_iter = if_index_list.begin(); if_index_iter != if_index_list.end(); ++if_index_iter) { if_index = *if_index_iter; // // Set the request for IPv4 addresses // if (fea_data_plane_manager().have_ipv4()) { memset(&buffer, 0, sizeof(buffer)); nlh->nlmsg_len = NLMSG_LENGTH(sizeof(*ifaddrmsg)); nlh->nlmsg_type = RTM_GETADDR; // Get the whole table nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT; nlh->nlmsg_seq = ns.seqno(); nlh->nlmsg_pid = ns.nl_pid(); ifaddrmsg = reinterpret_cast(NLMSG_DATA(nlh)); ifaddrmsg->ifa_family = AF_INET; ifaddrmsg->ifa_prefixlen = 0; ifaddrmsg->ifa_flags = 0; ifaddrmsg->ifa_scope = 0; ifaddrmsg->ifa_index = if_index; if (ns.sendto(&buffer, nlh->nlmsg_len, 0, reinterpret_cast(&snl), sizeof(snl)) != (ssize_t)nlh->nlmsg_len) { XLOG_ERROR("Error writing to netlink socket: %s", strerror(errno)); return (XORP_ERROR); } // // Force to receive data from the kernel, and then parse it // // // XXX: setting the flag below is a work-around hack because of a // Linux kernel bug: when we read the whole table the kernel // may not set the NLM_F_MULTI flag for the multipart messages. // string error_msg; ns.set_multipart_message_read(true); if (_ns_reader.receive_data(ns, nlh->nlmsg_seq, error_msg) != XORP_OK) { ns.set_multipart_message_read(false); XLOG_ERROR("Error reading from netlink socket: %s", error_msg.c_str()); return (XORP_ERROR); } // XXX: reset the multipart message read hackish flag ns.set_multipart_message_read(false); if (parse_buffer_netlink_socket(ifconfig(), iftree, _ns_reader.buffer()) != XORP_OK) { return (XORP_ERROR); } } #ifdef HAVE_IPV6 // // Set the request for IPv6 addresses // if (fea_data_plane_manager().have_ipv6()) { memset(&buffer, 0, sizeof(buffer)); nlh->nlmsg_len = NLMSG_LENGTH(sizeof(*ifaddrmsg)); nlh->nlmsg_type = RTM_GETADDR; // Get the whole table nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT; nlh->nlmsg_seq = ns.seqno(); nlh->nlmsg_pid = ns.nl_pid(); ifaddrmsg = reinterpret_cast(NLMSG_DATA(nlh)); ifaddrmsg->ifa_family = AF_INET6; ifaddrmsg->ifa_prefixlen = 0; ifaddrmsg->ifa_flags = 0; ifaddrmsg->ifa_scope = 0; ifaddrmsg->ifa_index = if_index; if (ns.sendto(&buffer, nlh->nlmsg_len, 0, reinterpret_cast(&snl), sizeof(snl)) != (ssize_t)nlh->nlmsg_len) { XLOG_ERROR("Error writing to netlink socket: %s", strerror(errno)); return (XORP_ERROR); } // // Force to receive data from the kernel, and then parse it // // // XXX: setting the flag below is a work-around hack because of a // Linux kernel bug: when we read the whole table the kernel // may not set the NLM_F_MULTI flag for the multipart messages. // string error_msg; ns.set_multipart_message_read(true); if (_ns_reader.receive_data(ns, nlh->nlmsg_seq, error_msg) != XORP_OK) { ns.set_multipart_message_read(false); XLOG_ERROR("Error reading from netlink socket: %s", error_msg.c_str()); return (XORP_ERROR); } // XXX: reset the multipart message read hackish flag ns.set_multipart_message_read(false); if (parse_buffer_netlink_socket(ifconfig(), iftree, _ns_reader.buffer()) != XORP_OK) { return (XORP_ERROR); } } #endif // HAVE_IPV6 } // // Get the VLAN vif info // IfConfigVlanGet* ifconfig_vlan_get; ifconfig_vlan_get = fea_data_plane_manager().ifconfig_vlan_get(); if (ifconfig_vlan_get != NULL) { bool modified = false; if (ifconfig_vlan_get->pull_config(iftree, modified) != XORP_OK) return (XORP_ERROR); } return (XORP_OK); } #endif // HAVE_NETLINK_SOCKETS xorp/fea/data_plane/ifconfig/ifconfig_get_click.hh0000664000076400007640000000467111540225523022407 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/data_plane/ifconfig/ifconfig_get_click.hh,v 1.10 2008/10/02 21:57:04 bms Exp $ #ifndef __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_GET_CLICK_HH__ #define __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_GET_CLICK_HH__ #include #ifdef XORP_USE_CLICK #include "fea/ifconfig_get.hh" #include "fea/data_plane/control_socket/click_socket.hh" class IfConfigGetClick : public IfConfigGet, public ClickSocket { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ IfConfigGetClick(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~IfConfigGetClick(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg); /** * Pull the network interface information from the underlying system. * * @param iftree the IfTree storage to store the pulled information. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int pull_config(const IfTree* local_config, IfTree& iftree); private: int read_config(IfTree& iftree); ClickSocketReader _cs_reader; }; #endif // click #endif // __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_GET_CLICK_HH__ xorp/fea/data_plane/ifconfig/ifconfig_property_bsd.cc0000664000076400007640000000421511540225524023160 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2007-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include #if defined(HOST_OS_BSDI) \ || defined(HOST_OS_DRAGONFLYBSD) \ || defined(HOST_OS_FREEBSD) \ || defined(HOST_OS_MACOSX) \ || defined(HOST_OS_NETBSD) \ || defined(HOST_OS_OPENBSD) #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libcomm/comm_api.h" #include "fea/ifconfig.hh" #include "ifconfig_property_bsd.hh" // // Get information about the data plane property. // // The mechanism to obtain the information is for BSD systems. // IfConfigPropertyBsd::IfConfigPropertyBsd(FeaDataPlaneManager& fea_data_plane_manager) : IfConfigProperty(fea_data_plane_manager) { } IfConfigPropertyBsd::~IfConfigPropertyBsd() { } bool IfConfigPropertyBsd::test_have_ipv4() const { XorpFd s = comm_sock_open(AF_INET, SOCK_DGRAM, 0, 0); if (! s.is_valid()) return (false); comm_close(s); return (true); } bool IfConfigPropertyBsd::test_have_ipv6() const { #ifndef HAVE_IPV6 return (false); #else XorpFd s = comm_sock_open(AF_INET6, SOCK_DGRAM, 0, 0); if (! s.is_valid()) return (false); comm_close(s); return (true); #endif // HAVE_IPV6 } #endif // HOST_OS_BSDI || HOST_OS_DRAGONFLYBSD || HOST_OS_FREEBSD || HOST_OS_MACOSX || HOST_OS_NETBSD || HOST_OS_OPENBSD xorp/fea/data_plane/ifconfig/ifconfig_observer_routing_socket.hh0000664000076400007640000000472711540225524025434 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/data_plane/ifconfig/ifconfig_observer_routing_socket.hh,v 1.9 2008/10/02 21:57:06 bms Exp $ #ifndef __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_OBSERVER_ROUTING_SOCKET_HH__ #define __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_OBSERVER_ROUTING_SOCKET_HH__ #include #ifdef HAVE_ROUTING_SOCKETS #include "fea/ifconfig_observer.hh" #include "fea/data_plane/control_socket/routing_socket.hh" class IfConfigObserverRoutingSocket : public IfConfigObserver, public RoutingSocket, public RoutingSocketObserver { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ IfConfigObserverRoutingSocket(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~IfConfigObserverRoutingSocket(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg); /** * Receive data from the underlying system. * * @param buffer the buffer with the received data. */ virtual void receive_data(const vector& buffer); void routing_socket_data(const vector& buffer); private: }; #endif #endif // __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_OBSERVER_ROUTING_SOCKET_HH__ xorp/fea/data_plane/ifconfig/ifconfig_observer_iphelper.cc0000664000076400007640000000442311540224223024157 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "fea/ifconfig.hh" #include "ifconfig_observer_iphelper.hh" // // Observe information change about network interface configuration from // the underlying system. // // The mechanism to observe the information is the IP Helper API for // Windows (IPHLPAPI.DLL). // #ifdef HOST_OS_WINDOWS IfConfigObserverIPHelper::IfConfigObserverIPHelper(FeaDataPlaneManager& fea_data_plane_manager) : IfConfigObserver(fea_data_plane_manager) { } IfConfigObserverIPHelper::~IfConfigObserverIPHelper() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the IP Helper mechanism to observe " "information about network interfaces from the underlying " "system: %s", error_msg.c_str()); } } int IfConfigObserverIPHelper::start(string& error_msg) { UNUSED(error_msg); if (_is_running) return (XORP_OK); _is_running = true; // // TODO: XXX: Currently, the observer mechanism doesn't track // the underlying changes. // return (XORP_OK); } int IfConfigObserverIPHelper::stop(string& error_msg) { UNUSED(error_msg); if (! _is_running) return (XORP_OK); _is_running = false; return (XORP_OK); } void IfConfigObserverIPHelper::receive_data(const vector& buffer) { UNUSED(buffer); } #endif // HOST_OS_WINDOWS xorp/fea/data_plane/ifconfig/ifconfig_get_getifaddrs.cc0000664000076400007640000000542511421137511023417 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #ifdef HAVE_IFADDRS_H #include #endif #include "fea/ifconfig.hh" #include "ifconfig_get_getifaddrs.hh" // // Get information about network interfaces from the underlying system. // // The mechanism to obtain the information is getifaddrs(3). // #ifdef HAVE_GETIFADDRS IfConfigGetGetifaddrs::IfConfigGetGetifaddrs(FeaDataPlaneManager& fea_data_plane_manager) : IfConfigGet(fea_data_plane_manager) { } IfConfigGetGetifaddrs::~IfConfigGetGetifaddrs() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the getifaddrs(3) mechanism to get " "information about network interfaces from the underlying " "system: %s", error_msg.c_str()); } } int IfConfigGetGetifaddrs::start(string& error_msg) { UNUSED(error_msg); if (_is_running) return (XORP_OK); _is_running = true; return (XORP_OK); } int IfConfigGetGetifaddrs::stop(string& error_msg) { UNUSED(error_msg); if (! _is_running) return (XORP_OK); _is_running = false; return (XORP_OK); } int IfConfigGetGetifaddrs::pull_config(const IfTree* local_config, IfTree& iftree) { UNUSED(local_config); return read_config(iftree); } int IfConfigGetGetifaddrs::read_config(IfTree& iftree) { struct ifaddrs *ifap; if (getifaddrs(&ifap) != 0) { XLOG_ERROR("getifaddrs() failed: %s", strerror(errno)); return (XORP_ERROR); } parse_buffer_getifaddrs(ifconfig(), iftree, ifap); freeifaddrs(ifap); // // Get the VLAN vif info // bool modified = false; IfConfigVlanGet* ifconfig_vlan_get; ifconfig_vlan_get = fea_data_plane_manager().ifconfig_vlan_get(); if (ifconfig_vlan_get != NULL) { if (ifconfig_vlan_get->pull_config(iftree, modified) != XORP_OK) return (XORP_ERROR); } return (XORP_OK); } #endif // HAVE_GETIFADDRS xorp/fea/data_plane/ifconfig/ifconfig_get_iphelper.cc0000664000076400007640000002010611630323453023110 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/win_io.h" #include "libxorp/ipv4net.hh" #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_IPHLPAPI_H #include #endif #include "fea/ifconfig.hh" #include "ifconfig_get_iphelper.hh" // // Get information about network interfaces from the underlying system. // // The mechanism to obtain the information is the IP Helper API for // Windows (IPHLPAPI.DLL). // #ifdef HOST_OS_WINDOWS IfConfigGetIPHelper::IfConfigGetIPHelper(FeaDataPlaneManager& fea_data_plane_manager) : IfConfigGet(fea_data_plane_manager) { } IfConfigGetIPHelper::~IfConfigGetIPHelper() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the IP Helper API mechanism to get " "information about network interfaces from the underlying " "system: %s", error_msg.c_str()); } } int IfConfigGetIPHelper::start(string& error_msg) { UNUSED(error_msg); if (_is_running) return (XORP_OK); _is_running = true; return (XORP_OK); } int IfConfigGetIPHelper::stop(string& error_msg) { UNUSED(error_msg); if (! _is_running) return (XORP_OK); _is_running = false; return (XORP_OK); } int IfConfigGetIPHelper::pull_config(const IfTree* local_config, IfTree& iftree) { return read_config(local_config, iftree); } int IfConfigGetIPHelper::read_config(const IfTree* local_config, IfTree& iftree) { PIP_ADAPTER_ADDRESSES pAdapters = NULL; PIP_ADAPTER_ADDRESSES curAdapter; DWORD result, tries; ULONG dwSize; tries = 0; result = ERROR_INSUFFICIENT_BUFFER; dwSize = sizeof(IP_ADAPTER_ADDRESSES); do { pAdapters = (PIP_ADAPTER_ADDRESSES) ((tries == 0) ? malloc(dwSize) : realloc(pAdapters, dwSize)); if (pAdapters == NULL) break; // XXX: Deal with HAVE_IPV6, anycast and multicast when needed. result = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX | GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER, NULL, pAdapters, &dwSize); if (pAdapters == NULL) break; } while ((++tries < 3) || (result == ERROR_INSUFFICIENT_BUFFER)); if (result != NO_ERROR) { XLOG_ERROR("GetAdaptersAddresses(): %s\n", win_strerror(result)); if (pAdapters != NULL) free(pAdapters); return (XORP_ERROR); } char if_name[MAX_ADAPTER_NAME_LENGTH+4]; uint32_t if_index; bool is_newlink; for (curAdapter = pAdapters; curAdapter != NULL; curAdapter = curAdapter->Next) { if_index = curAdapter->IfIndex; // XXX: require IPv4. IfIndex will be 0 if IPv4 is not active // on this adapter. if (if_index == 0) continue; wcstombs(if_name, curAdapter->FriendlyName, sizeof(if_name)); XLOG_INFO("Found Iface name: %s idx: %i iftree: %s oper-status: %i\n", if_name, (int)(curAdapter->IfIndex), iftree.getName().c_str(), curAdapter->OperStatus); // Name IfTreeInterface* ifp = iftree.find_interface(if_name); if (ifp == NULL) { if (local_config && !local_config->find_interface(if_name)) { // Ignore, not configured to use this interface. continue; } iftree.add_interface(if_name); is_newlink = true; ifp = iftree.find_interface(if_name); XLOG_ASSERT(ifp != NULL); } // Index if (is_newlink || (if_index != ifp->pif_index())) ifp->set_pif_index(if_index); // MAC if (curAdapter->IfType == MIB_IF_TYPE_ETHERNET && curAdapter->PhysicalAddressLength == sizeof(struct ether_addr)) { struct ether_addr ea; memcpy(&ea, curAdapter->PhysicalAddress, sizeof(ea)); Mac mac(ea); if (is_newlink || (mac != ifp->mac())) ifp->set_mac(mac); } // MTU uint32_t mtu = curAdapter->Mtu; if (is_newlink || (mtu != ifp->mtu())) ifp->set_mtu(mtu); // Link status and baudrate bool no_carrier = true; uint64_t baudrate = 0; if (curAdapter->OperStatus == IfOperStatusUp) no_carrier = false; // TODO: find the baudrate if (is_newlink || no_carrier != ifp->no_carrier()) ifp->set_no_carrier(no_carrier); if (is_newlink || baudrate != ifp->baudrate()) ifp->set_baudrate(baudrate); ifp->set_enabled((curAdapter->OperStatus == IfOperStatusUp)); XLOG_INFO("ifp->nocarrier: %i no_carrier: %i\n", (int)(ifp->no_carrier()), (int)(no_carrier)); // XXX: vifname == ifname on this platform if (is_newlink) ifp->add_vif(if_name); IfTreeVif* vifp = ifp->find_vif(if_name); XLOG_ASSERT(vifp != NULL); if (is_newlink || (if_index != vifp->pif_index())) vifp->set_pif_index(if_index); vifp->set_enabled(ifp->enabled() && (curAdapter->OperStatus == IfOperStatusUp)); vifp->set_broadcast(curAdapter->IfType == MIB_IF_TYPE_ETHERNET); vifp->set_multicast(curAdapter->IfType == MIB_IF_TYPE_ETHERNET); vifp->set_loopback(curAdapter->IfType == MIB_IF_TYPE_LOOPBACK); vifp->set_point_to_point(curAdapter->IfType == MIB_IF_TYPE_PPP); // Unicast addresses // XXX: Currently we only support IPv4. IPv4 lcl_addr; IPv4 bcast_addr; IPv4Net net_addr; uint32_t prefix_len; PIP_ADAPTER_UNICAST_ADDRESS curAddress; PIP_ADAPTER_PREFIX curPrefix; struct sockaddr* psa; struct sockaddr* psa2; for (curAddress = curAdapter->FirstUnicastAddress; curAddress != NULL; curAddress = curAddress->Next) { psa = curAddress->Address.lpSockaddr; if (psa->sa_family != AF_INET) continue; lcl_addr.copy_in(*psa); // // Walk the prefix list to find a matching network prefix // for this host address. If found, compute the directed // broadcast address and store the prefix length. // If we don't find a matching prefix, we assume the // address is a host address (/32 prefix). // represent a host-only address. // Skip 0.0.0.0/0 default prefixes returned by GAA(). // XXX: Needs updated to support IPv6. // prefix_len = 32; for (curPrefix = curAdapter->FirstPrefix; curPrefix != NULL; curPrefix = curPrefix->Next) { psa2 = curPrefix->Address.lpSockaddr; if (psa2->sa_family != psa->sa_family) continue; if (curPrefix->PrefixLength == 0) continue; IPv4Net net_addr(IPv4(*psa2), curPrefix->PrefixLength); if (net_addr.contains(lcl_addr)) { prefix_len = curPrefix->PrefixLength; bcast_addr = net_addr.top_addr(); break; } } vifp->add_addr(lcl_addr); IfTreeAddr4* ap = vifp->find_addr(lcl_addr); XLOG_ASSERT(ap != NULL); ap->set_enabled(vifp->enabled()); ap->set_loopback(vifp->loopback()); ap->set_point_to_point(vifp->point_to_point()); ap->set_multicast(vifp->multicast()); ap->set_prefix_len(prefix_len); if (vifp->broadcast()) { ap->set_broadcast(true); ap->set_bcast(bcast_addr); } } } free(pAdapters); // // Get the VLAN vif info // IfConfigVlanGet* ifconfig_vlan_get; ifconfig_vlan_get = fea_data_plane_manager().ifconfig_vlan_get(); if (ifconfig_vlan_get != NULL) { bool mod = false; // doesn't matter. if (ifconfig_vlan_get->pull_config(iftree, mod) != XORP_OK) return (XORP_ERROR); } return (XORP_OK); } #endif // HOST_OS_WINDOWS xorp/fea/data_plane/ifconfig/ifconfig_get_getifaddrs.hh0000664000076400007640000000564111421137511023431 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/data_plane/ifconfig/ifconfig_get_getifaddrs.hh,v 1.11 2008/10/02 21:57:04 bms Exp $ #ifndef __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_GET_GETIFADDRS_HH__ #define __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_GET_GETIFADDRS_HH__ #include "fea/ifconfig_get.hh" class IfConfigGetGetifaddrs : public IfConfigGet { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ IfConfigGetGetifaddrs(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~IfConfigGetGetifaddrs(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg); /** * Pull the network interface information from the underlying system. * * @param iftree the IfTree storage to store the pulled information. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int pull_config(const IfTree* local_config, IfTree& iftree); /** * Parse information about network interface configuration change from * the underlying system. * * The information to parse is in "struct ifaddrs" format * (e.g., obtained by getifaddrs(3) mechanism). * * @param ifconfig the IfConfig instance. * @param iftree the IfTree storage to store the parsed information. * @param ifap a linked list of the network interfaces on the * local machine. * @return XORP_OK on success, otherwise XORP_ERROR. * @see IfTree. */ static int parse_buffer_getifaddrs(IfConfig& ifconfig, IfTree& iftree, const struct ifaddrs* ifap); private: int read_config(IfTree& iftree); }; #endif // __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_GET_GETIFADDRS_HH__ xorp/fea/data_plane/ifconfig/SConscript0000664000076400007640000000431111703345405020302 0ustar greearbgreearb# Copyright (c) 2009-2012 XORP, Inc and Others # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # $ID$ import os Import("env") env = env.Clone() is_shared = env.has_key('SHAREDLIBS') env.AppendUnique(CPPPATH = [ '#', '$BUILDDIR', ]) sources = [ # C++ files 'ifconfig_get_click.cc', 'ifconfig_get_getifaddrs.cc', 'ifconfig_get_ioctl.cc', 'ifconfig_get_iphelper.cc', 'ifconfig_get_netlink_socket.cc', 'ifconfig_get_proc_linux.cc', 'ifconfig_get_sysctl.cc', 'ifconfig_media.cc', 'ifconfig_observer_iphelper.cc', 'ifconfig_observer_netlink_socket.cc', 'ifconfig_observer_routing_socket.cc', 'ifconfig_parse_getifaddrs.cc', 'ifconfig_parse_ioctl.cc', 'ifconfig_parse_routing_socket.cc', 'ifconfig_property_bsd.cc', 'ifconfig_property_linux.cc', 'ifconfig_property_solaris.cc', 'ifconfig_property_windows.cc', 'ifconfig_set.cc', 'ifconfig_set_click.cc', 'ifconfig_set_ioctl.cc', 'ifconfig_set_iphelper.cc', 'ifconfig_set_netlink_socket.cc', 'ifconfig_vlan_get_linux.cc', 'ifconfig_vlan_set_linux.cc', ] if env['enable_fea_dummy']: sources += [ 'ifconfig_get_dummy.cc', 'ifconfig_observer_dummy.cc', 'ifconfig_property_dummy.cc', 'ifconfig_set_dummy.cc', ] if is_shared: libxifc = env.SharedLibrary(target = 'libxorp_fea_ifconfig', source = sources) # XXX missing RPATH. else: libxifc = env.StaticLibrary(target = 'libxorp_fea_ifconfig', source = sources) if is_shared: env.Alias('install', env.InstallLibrary(env['xorp_libdir'], libxifc)) Default(libxifc) xorp/fea/data_plane/ifconfig/ifconfig_get_ioctl.cc0000664000076400007640000001333711703345405022424 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2012 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include #if defined(HAVE_IOCTL_SIOCGIFCONF) && !defined(HAVE_NETLINK_SOCKETS) #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libcomm/comm_api.h" #ifdef HAVE_SYS_IOCTL_H #include #endif #ifdef HAVE_SYS_SOCKIO_H #include #endif #ifdef HAVE_NET_IF_H #include #endif #include "fea/ifconfig.hh" #include "ifconfig_get_ioctl.hh" // // Get information about network interfaces from the underlying system. // // The mechanism to obtain the information is ioctl(2). // static bool ioctl_read_ifconf(int family, ifconf *ifconf); IfConfigGetIoctl::IfConfigGetIoctl(FeaDataPlaneManager& fea_data_plane_manager) : IfConfigGet(fea_data_plane_manager), _s4(-1), _s6(-1) { } IfConfigGetIoctl::~IfConfigGetIoctl() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the ioctl(2) mechanism to get " "information about network interfaces from the underlying " "system: %s", error_msg.c_str()); } } int IfConfigGetIoctl::start(string& error_msg) { if (_is_running) return (XORP_OK); if (fea_data_plane_manager().have_ipv4()) { if (_s4 < 0) { _s4 = socket(AF_INET, SOCK_DGRAM, 0); if (_s4 < 0) { error_msg = c_format("Could not initialize IPv4 ioctl() " "socket: %s", strerror(errno)); XLOG_FATAL("%s", error_msg.c_str()); } } } #ifdef HAVE_IPV6 if (fea_data_plane_manager().have_ipv6()) { if (_s6 < 0) { _s6 = socket(AF_INET6, SOCK_DGRAM, 0); if (_s6 < 0) { error_msg = c_format("Could not initialize IPv6 ioctl() " "socket: %s", strerror(errno)); XLOG_FATAL("%s", error_msg.c_str()); } } } #endif // HAVE_IPV6 _is_running = true; return (XORP_OK); } int IfConfigGetIoctl::stop(string& error_msg) { int ret_value4 = XORP_OK; int ret_value6 = XORP_OK; if (! _is_running) return (XORP_OK); if (_s4 >= 0) { ret_value4 = comm_close(_s4); _s4 = -1; if (ret_value4 != XORP_OK) { error_msg = c_format("Could not close IPv4 ioctl() socket: %s", comm_get_last_error_str()); } } if (_s6 >= 0) { ret_value6 = comm_close(_s6); _s6 = -1; if ((ret_value6 != XORP_OK) && (ret_value4 == XORP_OK)) { error_msg = c_format("Could not close IPv6 ioctl() socket: %s", comm_get_last_error_str()); } } if ((ret_value4 != XORP_OK) || (ret_value6 != XORP_OK)) return (XORP_ERROR); _is_running = false; return (XORP_OK); } int IfConfigGetIoctl::pull_config(const IfTree* local_config, IfTree& iftree) { UNUSED(local_config); return read_config(iftree); } int IfConfigGetIoctl::read_config(IfTree& iftree) { struct ifconf ifconf; // // The IPv4 information // if (fea_data_plane_manager().have_ipv4()) { if (ioctl_read_ifconf(AF_INET, &ifconf) != true) return (XORP_ERROR); vector buffer(ifconf.ifc_len); memcpy(&buffer[0], ifconf.ifc_buf, ifconf.ifc_len); free(ifconf.ifc_buf); ifconf.ifc_buf = NULL; parse_buffer_ioctl(ifconfig(), iftree, AF_INET, buffer); } #ifdef HAVE_IPV6 // // The IPv6 information // if (fea_data_plane_manager().have_ipv6()) { if (ioctl_read_ifconf(AF_INET6, &ifconf) != true) return (XORP_ERROR); vector buffer(ifconf.ifc_len); memcpy(&buffer[0], ifconf.ifc_buf, ifconf.ifc_len); free(ifconf.ifc_buf); ifconf.ifc_buf = NULL; parse_buffer_ioctl(ifconfig(), iftree, AF_INET6, buffer); } #endif // HAVE_IPV6 // // Get the VLAN vif info // bool modified = false; IfConfigVlanGet* ifconfig_vlan_get; ifconfig_vlan_get = fea_data_plane_manager().ifconfig_vlan_get(); if (ifconfig_vlan_get != NULL) { if (ifconfig_vlan_get->pull_config(iftree, modified) != XORP_OK) return (XORP_ERROR); } return (XORP_OK); } static bool ioctl_read_ifconf(int family, ifconf *ifconf) { int s, ifnum, lastlen; s = socket(family, SOCK_DGRAM, 0); if (s < 0) { XLOG_FATAL("Could not initialize ioctl() socket"); } ifnum = 32; // XXX: initial guess ifconf->ifc_buf = NULL; lastlen = 0; // Loop until SIOCGIFCONF success. for ( ; ; ) { ifconf->ifc_len = ifnum * sizeof(struct ifreq); if (ifconf->ifc_buf != NULL) free(ifconf->ifc_buf); ifconf->ifc_buf = (char*)(malloc(ifconf->ifc_len)); if (ioctl(s, SIOCGIFCONF, ifconf) < 0) { // Check UNPv1, 2e, pp 435 for an explanation why we need this if ((errno != EINVAL) || (lastlen != 0)) { XLOG_ERROR("ioctl(SIOCGIFCONF) failed: %s", strerror(errno)); free(ifconf->ifc_buf); ifconf->ifc_buf = NULL; comm_close(s); return false; } } else { if (ifconf->ifc_len == lastlen) break; // success, len has not changed lastlen = ifconf->ifc_len; } ifnum += 10; } comm_close(s); return true; } #endif // HAVE_IOCTL_SIOCGIFCONF xorp/fea/data_plane/ifconfig/ifconfig_property_dummy.cc0000664000076400007640000000277411421137511023547 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2007-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "fea/ifconfig.hh" #include "ifconfig_property_dummy.hh" // // Get information about the data plane property. // // The mechanism to obtain the information is Dummy (for testing purpose). // IfConfigPropertyDummy::IfConfigPropertyDummy(FeaDataPlaneManager& fea_data_plane_manager) : IfConfigProperty(fea_data_plane_manager) { } IfConfigPropertyDummy::~IfConfigPropertyDummy() { } bool IfConfigPropertyDummy::test_have_ipv4() const { return (true); } bool IfConfigPropertyDummy::test_have_ipv6() const { return (true); } xorp/fea/data_plane/ifconfig/ifconfig_parse_routing_socket.cc0000664000076400007640000005435011541752616024712 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include #ifdef HAVE_ROUTING_SOCKETS #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ether_compat.h" #ifdef HAVE_SYS_IOCTL_H #include #endif #ifdef HAVE_NET_IF_H #include #endif #ifdef HAVE_NET_IF_VAR_H #include #endif #ifdef HAVE_NET_IF_DL_H #include #endif #ifdef HAVE_NET_ROUTE_H #include #endif #ifdef HAVE_NET_IF_TYPES_H #include #endif #ifdef HAVE_NETINET6_IN6_VAR_H #include #endif #ifdef HAVE_IFADDRS_H #include #endif #include "fea/ifconfig.hh" #include "fea/data_plane/control_socket/system_utilities.hh" #include "fea/data_plane/control_socket/routing_socket_utilities.hh" #include "ifconfig_get_sysctl.hh" #include "ifconfig_media.hh" // // Parse information about network interface configuration change from // the underlying system. // // The information to parse is in RTM format // (e.g., obtained by routing sockets or by sysctl(3) mechanism). // // Reading route(4) manual page is a good start for understanding this // static void rtm_ifinfo_to_fea_cfg(const struct if_msghdr* ifm, IfTree& iftree, uint32_t& if_index_hint); static void rtm_addr_to_fea_cfg(const struct if_msghdr* ifm, IfTree& iftree, uint32_t if_index_hint); #ifdef RTM_IFANNOUNCE static void rtm_announce_to_fea_cfg(const struct if_msghdr* ifm, IfTree& iftree); #endif // // Get the link status // static int ifm_get_link_status(const struct if_msghdr* ifm, const string& if_name, bool& no_carrier, uint64_t& baudrate, string& error_msg) { if (ifconfig_media_get_link_status(if_name, no_carrier, baudrate, error_msg) == XORP_OK) { return (XORP_OK); } #if defined(LINK_STATE_UP) && defined(LINK_STATE_DOWN) switch (ifm->ifm_data.ifi_link_state) { case LINK_STATE_UNKNOWN: // XXX: link state unknown break; case LINK_STATE_DOWN: no_carrier = true; break; case LINK_STATE_UP: no_carrier = false; break; default: break; } UNUSED(if_name); UNUSED(error_msg); return (XORP_OK); #else UNUSED(ifm); return (XORP_ERROR); #endif // ! LINK_STATE_UP && LINK_STATE_DOWN } /** * Generate ifconfig(8) like flags string * @param flags interface flags value from routing socket message. * @return ifconfig(8) like flags string */ string IfConfigGetSysctl::iff_flags(uint32_t flags) { struct { uint32_t value; const char* name; } iff_fl[] = { #define IFF_FLAG_ENTRY(X) { IFF_##X, #X } #ifdef IFF_UP IFF_FLAG_ENTRY(UP), #endif #ifdef IFF_BROADCAST IFF_FLAG_ENTRY(BROADCAST), #endif #ifdef IFF_DEBUG IFF_FLAG_ENTRY(DEBUG), #endif #ifdef IFF_LOOPBACK IFF_FLAG_ENTRY(LOOPBACK), #endif #ifdef IFF_POINTOPOINT IFF_FLAG_ENTRY(POINTOPOINT), #endif #ifdef IFF_SMART IFF_FLAG_ENTRY(SMART), #endif #ifdef IFF_RUNNING IFF_FLAG_ENTRY(RUNNING), #endif #ifdef IFF_NOARP IFF_FLAG_ENTRY(NOARP), #endif #ifdef IFF_PROMISC IFF_FLAG_ENTRY(PROMISC), #endif #ifdef IFF_ALLMULTI IFF_FLAG_ENTRY(ALLMULTI), #endif #ifdef IFF_OACTIVE IFF_FLAG_ENTRY(OACTIVE), #endif #ifdef IFF_SIMPLEX IFF_FLAG_ENTRY(SIMPLEX), #endif #ifdef IFF_LINK0 IFF_FLAG_ENTRY(LINK0), #endif #ifdef IFF_LINK1 IFF_FLAG_ENTRY(LINK1), #endif #ifdef IFF_LINK2 IFF_FLAG_ENTRY(LINK2), #endif #ifdef IFF_ALTPHYS IFF_FLAG_ENTRY(ALTPHYS), #endif #ifdef IFF_MULTICAST IFF_FLAG_ENTRY(MULTICAST), #endif { 0, "" } // for nitty compilers that don't like trailing "," }; const size_t n_iff_fl = sizeof(iff_fl) / sizeof(iff_fl[0]); string ret("<"); for (size_t i = 0; i < n_iff_fl; i++) { if (0 == (flags & iff_fl[i].value)) continue; flags &= ~iff_fl[i].value; ret += iff_fl[i].name; if (0 == flags) break; ret += ","; } ret += ">"; return ret; } int IfConfigGetSysctl::parse_buffer_routing_socket(IfConfig& ifconfig, IfTree& iftree, const vector& buffer) { AlignData align_data(buffer); bool recognized = false; uint32_t if_index_hint = 0; const struct if_msghdr* ifm; size_t offset; UNUSED(ifconfig); ifm = align_data.payload(); for (offset = 0; offset < buffer.size(); offset += ifm->ifm_msglen) { ifm = align_data.payload_by_offset(offset); if (ifm->ifm_version != RTM_VERSION) { #if defined(RTM_OVERSION) && defined(HOST_OS_OPENBSD) // // XXX: Silently ignore old messages. // The OpenBSD kernel sends each message twice, once as // RTM_VERSION and once as RTM_OVERSION, hence we need to ignore // the RTM_OVERSION duplicates. // if (ifm->ifm_version == RTM_OVERSION) continue; #endif // RTM_OVERSION && HOST_OS_OPENBSD XLOG_ERROR("RTM version mismatch: expected %d got %d", RTM_VERSION, ifm->ifm_version); continue; } switch (ifm->ifm_type) { case RTM_IFINFO: if_index_hint = 0; rtm_ifinfo_to_fea_cfg(ifm, iftree, if_index_hint); recognized = true; break; case RTM_NEWADDR: case RTM_DELADDR: rtm_addr_to_fea_cfg(ifm, iftree, if_index_hint); recognized = true; break; #ifdef RTM_IFANNOUNCE case RTM_IFANNOUNCE: if_index_hint = 0; rtm_announce_to_fea_cfg(ifm, iftree); recognized = true; break; #endif // RTM_IFANNOUNCE case RTM_ADD: case RTM_MISS: case RTM_CHANGE: case RTM_GET: case RTM_LOSING: case RTM_DELETE: if_index_hint = 0; break; default: debug_msg("Unhandled type %s(%d) (%d bytes)\n", RtmUtils::rtm_msg_type(ifm->ifm_type).c_str(), ifm->ifm_type, ifm->ifm_msglen); if_index_hint = 0; break; } } if (! recognized) return (XORP_ERROR); return (XORP_OK); } static void rtm_ifinfo_to_fea_cfg(const struct if_msghdr* ifm, IfTree& iftree, uint32_t& if_index_hint) { XLOG_ASSERT(ifm->ifm_type == RTM_IFINFO); const struct sockaddr *sa, *rti_info[RTAX_MAX]; uint32_t if_index = ifm->ifm_index; bool is_newlink = false; // True if really a new link string error_msg; debug_msg("%p index %u RTM_IFINFO\n", ifm, if_index); // Get the pointers to the corresponding data structures sa = reinterpret_cast(ifm + 1); RtmUtils::get_rta_sockaddr(ifm->ifm_addrs, sa, rti_info); if_index_hint = if_index; if (rti_info[RTAX_IFP] == NULL) { // // Probably an interface being disabled or coming up // following RTM_IFANNOUNCE. // if (if_index == 0) { debug_msg("Ignoring interface with unknown index\n"); return; } debug_msg("interface index: %u\n", if_index); IfTreeInterface* ifp = iftree.find_interface(if_index); if (ifp == NULL) { XLOG_WARNING("Could not find interface with index %u", if_index); return; } if (ifp->is_marked(IfTreeItem::CREATED)) is_newlink = true; // // Set the physical interface index for the interface // if (is_newlink || (if_index != ifp->pif_index())) ifp->set_pif_index(if_index); // // Set the flags // unsigned int flags = ifm->ifm_flags; if (is_newlink || (flags != ifp->interface_flags())) { ifp->set_interface_flags(flags); ifp->set_enabled(flags & IFF_UP); } debug_msg("enabled: %s\n", bool_c_str(ifp->enabled())); IfTreeVif* vifp = iftree.find_vif(if_index); if (vifp == NULL) { XLOG_FATAL("Could not find vif with index %u", if_index); } // // Set the physical interface index for the vif // if (is_newlink || (if_index != vifp->pif_index())) vifp->set_pif_index(if_index); // // Get the link status and the baudrate // bool no_carrier = false; uint64_t baudrate = 0; if (ifm_get_link_status(ifm, ifp->ifname(), no_carrier, baudrate, error_msg) != XORP_OK) { XLOG_ERROR("%s", error_msg.c_str()); } else { if (is_newlink || (no_carrier != ifp->no_carrier())) ifp->set_no_carrier(no_carrier); if (is_newlink || (baudrate != ifp->baudrate())) ifp->set_baudrate(baudrate); } debug_msg("no_carrier: %s\n", bool_c_str(ifp->no_carrier())); debug_msg("baudrate: %u\n", XORP_UINT_CAST(ifp->baudrate())); // // Set the vif flags // if (is_newlink || (flags != vifp->vif_flags())) { vifp->set_vif_flags(flags); vifp->set_enabled(ifp->enabled() && (flags & IFF_UP)); vifp->set_broadcast(flags & IFF_BROADCAST); vifp->set_loopback(flags & IFF_LOOPBACK); vifp->set_point_to_point(flags & IFF_POINTOPOINT); vifp->set_multicast(flags & IFF_MULTICAST); // Propagate the flags to the existing addresses vifp->propagate_flags_to_addresses(); } debug_msg("vif enabled: %s\n", bool_c_str(vifp->enabled())); debug_msg("vif broadcast: %s\n", bool_c_str(vifp->broadcast())); debug_msg("vif loopback: %s\n", bool_c_str(vifp->loopback())); debug_msg("vif point_to_point: %s\n", bool_c_str(vifp->point_to_point())); debug_msg("vif multicast: %s\n", bool_c_str(vifp->multicast())); return; } // // Get the interface name // string if_name; sa = rti_info[RTAX_IFP]; if (sa->sa_family != AF_LINK) { // TODO: verify whether this is really an error. XLOG_ERROR("Ignoring RTM_INFO with sa_family = %d", sa->sa_family); return; } const sockaddr_dl* sdl = reinterpret_cast(sa); if (sdl->sdl_nlen > 0) { if_name = string(sdl->sdl_data, sdl->sdl_nlen); } else { if (if_index == 0) { XLOG_FATAL("Interface with no name and index"); } const char* name = NULL; #ifdef HAVE_IF_INDEXTONAME char name_buf[IF_NAMESIZE]; name = if_indextoname(if_index, name_buf); #endif if (name == NULL) { XLOG_FATAL("Could not find interface corresponding to index %d", if_index); } if_name = string(name); } debug_msg("interface: %s\n", if_name.c_str()); // // Get the physical interface index (if unknown) // do { if (if_index > 0) break; #ifdef HAVE_IF_NAMETOINDEX if_index = if_nametoindex(if_name.c_str()); if (if_index > 0) break; #endif #ifdef SIOCGIFINDEX { int s; struct ifreq ifridx; s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) { XLOG_FATAL("Could not initialize IPv4 ioctl() socket"); } memset(&ifridx, 0, sizeof(ifridx)); strncpy(ifridx.ifr_name, if_name.c_str(), sizeof(ifridx.ifr_name) - 1); if (ioctl(s, SIOCGIFINDEX, &ifridx) < 0) { XLOG_ERROR("ioctl(SIOCGIFINDEX) for interface %s failed: %s", ifridx.ifr_name, strerror(errno)); } else { #ifdef HAVE_STRUCT_IFREQ_IFR_IFINDEX if_index = ifridx.ifr_ifindex; #else if_index = ifridx.ifr_index; #endif } close(s); } #endif // SIOCGIFINDEX if (if_index > 0) break; } while (false); if (if_index == 0) { XLOG_FATAL("Could not find index for interface %s", if_name.c_str()); } if_index_hint = if_index; debug_msg("interface index: %u\n", if_index); // // Add the interface (if a new one) // IfTreeInterface* ifp = iftree.find_interface(if_name); if (ifp == NULL) { iftree.add_interface(if_name); is_newlink = true; ifp = iftree.find_interface(if_name); XLOG_ASSERT(ifp != NULL); } // // Set the physical interface index for the interface // if (is_newlink || (if_index != ifp->pif_index())) ifp->set_pif_index(if_index); // // Set the MAC address // do { if (sdl->sdl_type == IFT_ETHER) { if (sdl->sdl_alen == sizeof(struct ether_addr)) { struct ether_addr ea; memcpy(&ea, sdl->sdl_data + sdl->sdl_nlen, sdl->sdl_alen); Mac mac(ea); if (is_newlink || (mac != ifp->mac())) ifp->set_mac(mac); break; } else if (sdl->sdl_alen != 0) { XLOG_ERROR("Address size %d uncatered for interface %s", sdl->sdl_alen, if_name.c_str()); } } #ifdef SIOCGIFHWADDR { int s; struct ifreq ifridx; memset(&ifridx, 0, sizeof(ifridx)); strncpy(ifridx.ifr_name, if_name.c_str(), sizeof(ifridx.ifr_name) - 1); s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) { XLOG_FATAL("Could not initialize IPv4 ioctl() socket"); } if (ioctl(s, SIOCGIFHWADDR, &ifridx) < 0) { XLOG_ERROR("ioctl(SIOCGIFHWADDR) for interface %s failed: %s", if_name.c_str(), strerror(errno)); } else { struct ether_addr ea; memcpy(&ea, ifridx.ifr_hwaddr.sa_data, sizeof(ea)); Mac mac(ea); if (is_newlink || (mac != ifp->mac())) ifp->set_mac(mac); close(s); break; } close(s); } #endif // SIOCGIFHWADDR break; } while (false); debug_msg("MAC address: %s\n", ifp->mac().str().c_str()); // // Set the MTU // unsigned int mtu = ifm->ifm_data.ifi_mtu; if (is_newlink || (mtu != ifp->mtu())) ifp->set_mtu(mtu); debug_msg("MTU: %d\n", ifp->mtu()); // // Set the flags // unsigned int flags = ifm->ifm_flags; if (is_newlink || (flags != ifp->interface_flags())) { ifp->set_interface_flags(flags); ifp->set_enabled(flags & IFF_UP); } debug_msg("enabled: %s\n", bool_c_str(ifp->enabled())); // // Get the link status and the baudrate // bool no_carrier = false; uint64_t baudrate = 0; if (ifm_get_link_status(ifm, if_name, no_carrier, baudrate, error_msg) != XORP_OK) { XLOG_ERROR("%s", error_msg.c_str()); } else { if (is_newlink || (no_carrier != ifp->no_carrier())) ifp->set_no_carrier(no_carrier); if (is_newlink || (baudrate != ifp->baudrate())) ifp->set_baudrate(baudrate); } debug_msg("no_carrier: %s\n", bool_c_str(ifp->no_carrier())); debug_msg("baudrate: %u\n", XORP_UINT_CAST(ifp->baudrate())); // XXX: vifname == ifname on this platform if (is_newlink) ifp->add_vif(if_name); IfTreeVif* vifp = ifp->find_vif(if_name); XLOG_ASSERT(vifp != NULL); // // Set the physical interface index for the vif // if (is_newlink || (if_index != vifp->pif_index())) vifp->set_pif_index(if_index); // // Set the vif flags // if (is_newlink || (flags != vifp->vif_flags())) { vifp->set_vif_flags(flags); vifp->set_enabled(ifp->enabled() && (flags & IFF_UP)); vifp->set_broadcast(flags & IFF_BROADCAST); vifp->set_loopback(flags & IFF_LOOPBACK); vifp->set_point_to_point(flags & IFF_POINTOPOINT); vifp->set_multicast(flags & IFF_MULTICAST); // Propagate the flags to the existing addresses vifp->propagate_flags_to_addresses(); } debug_msg("vif enabled: %s\n", bool_c_str(vifp->enabled())); debug_msg("vif broadcast: %s\n", bool_c_str(vifp->broadcast())); debug_msg("vif loopback: %s\n", bool_c_str(vifp->loopback())); debug_msg("vif point_to_point: %s\n", bool_c_str(vifp->point_to_point())); debug_msg("vif multicast: %s\n", bool_c_str(vifp->multicast())); } static void rtm_addr_to_fea_cfg(const struct if_msghdr* ifm, IfTree& iftree, uint32_t if_index_hint) { XLOG_ASSERT(ifm->ifm_type == RTM_NEWADDR || ifm->ifm_type == RTM_DELADDR); const ifa_msghdr* ifa = reinterpret_cast(ifm); const struct sockaddr *sa, *rti_info[RTAX_MAX]; uint32_t if_index = ifa->ifam_index; debug_msg_indent(4); if (ifm->ifm_type == RTM_NEWADDR) debug_msg("%p index %u RTM_NEWADDR\n", ifm, if_index); if (ifm->ifm_type == RTM_DELADDR) debug_msg("%p index %u RTM_DELADDR\n", ifm, if_index); // Get the pointers to the corresponding data structures sa = reinterpret_cast(ifa + 1); RtmUtils::get_rta_sockaddr(ifa->ifam_addrs, sa, rti_info); if (if_index == 0) if_index = if_index_hint; // XXX: in case if_index is unknown if (if_index == 0) { XLOG_FATAL("Could not add or delete address for interface " "with unknown index"); } // // Locate the vif to pin data on // IfTreeVif* vifp = iftree.find_vif(if_index); if (vifp == NULL) { XLOG_WARNING("Could not find vif with index %u in IfTree", if_index); // Maybe it's just already deleted, so don't do anything fatal here. return; } debug_msg("Address event on interface %s vif %s with interface index %u\n", vifp->ifname().c_str(), vifp->vifname().c_str(), vifp->pif_index()); if (rti_info[RTAX_IFA] == NULL) { debug_msg("Ignoring addr info with null RTAX_IFA entry"); return; } if (rti_info[RTAX_IFA]->sa_family == AF_INET) { IPv4 lcl_addr(*rti_info[RTAX_IFA]); vifp->add_addr(lcl_addr); debug_msg("IP address: %s\n", lcl_addr.str().c_str()); IfTreeAddr4* ap = vifp->find_addr(lcl_addr); XLOG_ASSERT(ap != NULL); ap->set_enabled(vifp->enabled()); ap->set_broadcast(vifp->broadcast()); ap->set_loopback(vifp->loopback()); ap->set_point_to_point(vifp->point_to_point()); ap->set_multicast(vifp->multicast()); // Get the netmask if (rti_info[RTAX_NETMASK] != NULL) { int mask_len = RtmUtils::get_sock_mask_len(AF_INET, rti_info[RTAX_NETMASK]); ap->set_prefix_len(mask_len); IPv4 subnet_mask(IPv4::make_prefix(mask_len)); UNUSED(subnet_mask); debug_msg("IP netmask: %s\n", subnet_mask.str().c_str()); } // Get the broadcast or point-to-point address bool has_broadcast_addr = false; bool has_peer_addr = false; if ((rti_info[RTAX_BRD] != NULL) && (rti_info[RTAX_BRD]->sa_family == AF_INET)) { IPv4 o(*rti_info[RTAX_BRD]); if (ap->broadcast()) { ap->set_bcast(o); has_broadcast_addr = true; debug_msg("Broadcast address: %s\n", o.str().c_str()); } if (ap->point_to_point()) { ap->set_endpoint(o); has_peer_addr = true; debug_msg("Peer address: %s\n", o.str().c_str()); } } if (! has_broadcast_addr) ap->set_broadcast(false); if (! has_peer_addr) ap->set_point_to_point(false); // Mark as deleted if necessary if (ifa->ifam_type == RTM_DELADDR) ap->mark(IfTreeItem::DELETED); return; } #ifdef HAVE_IPV6 if (rti_info[RTAX_IFA]->sa_family == AF_INET6) { IPv6 lcl_addr(*rti_info[RTAX_IFA]); lcl_addr = system_adjust_ipv6_recv(lcl_addr); vifp->add_addr(lcl_addr); debug_msg("IP address: %s\n", lcl_addr.str().c_str()); IfTreeAddr6* ap = vifp->find_addr(lcl_addr); ap->set_enabled(vifp->enabled()); ap->set_loopback(vifp->loopback()); ap->set_point_to_point(vifp->point_to_point()); ap->set_multicast(vifp->multicast()); // Get the netmask if (rti_info[RTAX_NETMASK] != NULL) { int mask_len = RtmUtils::get_sock_mask_len(AF_INET6, rti_info[RTAX_NETMASK]); ap->set_prefix_len(mask_len); IPv6 subnet_mask(IPv6::make_prefix(mask_len)); UNUSED(subnet_mask); debug_msg("IP netmask: %s\n", subnet_mask.str().c_str()); } // Get the point-to-point address bool has_peer_addr = false; if ((rti_info[RTAX_BRD] != NULL) && (rti_info[RTAX_BRD]->sa_family == AF_INET6)) { if (ap->point_to_point()) { IPv6 o(*rti_info[RTAX_BRD]); ap->set_endpoint(o); has_peer_addr = true; debug_msg("Peer address: %s\n", o.str().c_str()); } } if (! has_peer_addr) ap->set_point_to_point(false); #if 0 // TODO: don't get the flags? do { // // Get IPv6 specific flags // int s; struct in6_ifreq ifrcopy6; s = socket(AF_INET6, SOCK_DGRAM, 0); if (s < 0) { XLOG_FATAL("Could not initialize IPv6 ioctl() socket"); } memset(&ifrcopy6, 0, sizeof(ifrcopy6)); strncpy(ifrcopy6.ifr_name, vifp->vifname().c_str(), sizeof(ifrcopy6.ifr_name) - 1); a.copy_out(ifrcopy6.ifr_addr); if (ioctl(s, SIOCGIFAFLAG_IN6, &ifrcopy6) < 0) { XLOG_ERROR("ioctl(SIOCGIFAFLAG_IN6) for interface %s failed: %s", vifp->vif_name().c_str(), strerror(errno)); } close (s); // // TODO: shall we ignore the anycast addresses? // TODO: what about TENTATIVE, DUPLICATED, DETACHED, DEPRECATED? // if (ifrcopy6.ifr_ifru.ifru_flags6 & IN6_IFF_ANYCAST) { } } while (false); #endif // 0/1 // Mark as deleted if necessary if (ifa->ifam_type == RTM_DELADDR) ap->mark(IfTreeItem::DELETED); return; } #endif // HAVE_IPV6 } #ifdef RTM_IFANNOUNCE static void rtm_announce_to_fea_cfg(const struct if_msghdr* ifm, IfTree& iftree) { XLOG_ASSERT(ifm->ifm_type == RTM_IFANNOUNCE); const if_announcemsghdr* ifan = reinterpret_cast(ifm); uint32_t if_index = ifan->ifan_index; string if_name = string(ifan->ifan_name); debug_msg("RTM_IFANNOUNCE %s on interface %s with interface index %u\n", (ifan->ifan_what == IFAN_DEPARTURE) ? "DEPARTURE" : "ARRIVAL", if_name.c_str(), if_index); switch (ifan->ifan_what) { case IFAN_ARRIVAL: { // // Add interface // debug_msg("Adding interface/vif %s\n", if_name.c_str()); iftree.add_interface(if_name); IfTreeInterface* ifp = iftree.find_interface(if_name); XLOG_ASSERT(ifp != NULL); ifp->set_pif_index(if_index); // XXX: vifname == ifname on this platform ifp->add_vif(if_name); IfTreeVif* vifp = ifp->find_vif(if_name); XLOG_ASSERT(vifp != NULL); vifp->set_pif_index(if_index); break; } case IFAN_DEPARTURE: { // // Delete interface // debug_msg("Deleting interface/vif %s\n", if_name.c_str()); // Delete the interface IfTreeInterface* ifp = NULL; if (if_index != 0) { ifp = iftree.find_interface(if_index); } else { ifp = iftree.find_interface(if_name); } if (ifp != NULL) ifp->mark(IfTree::DELETED); // Delete the vif IfTreeVif* vifp = NULL; if (if_index != 0) { vifp = iftree.find_vif(if_index); } else { // XXX: vifname == ifname on this platform vifp = iftree.find_vif(if_name, if_name); } if (vifp != NULL) vifp->mark(IfTree::DELETED); break; } default: debug_msg("Unknown RTM_IFANNOUNCE message type %d\n", ifan->ifan_what); break; } } #endif // RTM_IFANNOUNCE #endif // HAVE_ROUTING_SOCKETS xorp/fea/data_plane/ifconfig/ifconfig_property_windows.cc0000664000076400007640000000361611540224223024101 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2007-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libcomm/comm_api.h" #include "fea/ifconfig.hh" #include "ifconfig_property_windows.hh" // // Get information about the data plane property. // // The mechanism to obtain the information is for Windows systems. // #ifdef HOST_OS_WINDOWS IfConfigPropertyWindows::IfConfigPropertyWindows(FeaDataPlaneManager& fea_data_plane_manager) : IfConfigProperty(fea_data_plane_manager) { } IfConfigPropertyWindows::~IfConfigPropertyWindows() { } bool IfConfigPropertyWindows::test_have_ipv4() const { XorpFd s = comm_sock_open(AF_INET, SOCK_DGRAM, 0, 0); if (! s.is_valid()) return (false); comm_close(s); return (true); } bool IfConfigPropertyWindows::test_have_ipv6() const { #ifndef HAVE_IPV6 return (false); #else XorpFd s = comm_sock_open(AF_INET6, SOCK_DGRAM, 0, 0); if (! s.is_valid()) return (false); comm_close(s); return (true); #endif // HAVE_IPV6 } #endif // HOST_OS_WINDOWS xorp/fea/data_plane/ifconfig/ifconfig_get_proc_linux.hh0000664000076400007640000000517111540225524023501 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/data_plane/ifconfig/ifconfig_get_proc_linux.hh,v 1.10 2008/10/02 21:57:05 bms Exp $ #ifndef __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_GET_PROC_LINUX_HH__ #define __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_GET_PROC_LINUX_HH__ #include #if defined(HAVE_IOCTL_SIOCGIFCONF) && defined(HAVE_PROC_LINUX) && !defined(HAVE_NETLINK_SOCKETS) #include "fea/ifconfig_get.hh" class IfConfigGetIoctl; class IfConfigGetProcLinux : public IfConfigGet { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ IfConfigGetProcLinux(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~IfConfigGetProcLinux(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg); /** * Pull the network interface information from the underlying system. * * @param iftree the IfTree storage to store the pulled information. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int pull_config(const IfTree* local_config, IfTree& iftree); private: int read_config(const IfTree* local_config, IfTree& iftree); IfConfigGetIoctl* _ifconfig_get_ioctl; static const string PROC_LINUX_NET_DEVICES_FILE_V4; static const string PROC_LINUX_NET_DEVICES_FILE_V6; }; #endif #endif // __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_GET_PROC_LINUX_HH__ xorp/fea/data_plane/ifconfig/ifconfig_vlan_set_linux.cc0000664000076400007640000002716011703345405023504 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2007-2012 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ether_compat.h" #include "libcomm/comm_api.h" #ifdef HAVE_SYS_IOCTL_H #include #endif #ifdef HAVE_NET_IF_H #include #endif #ifdef HAVE_LINUX_SOCKIOS_H #include #endif #ifdef HAVE_LINUX_IF_VLAN_H #include #endif #ifdef HAVE_NET_IF_VLAN_VAR_H #include #endif #ifdef HAVE_NET_IF_VLANVAR_H #include #endif #include "fea/ifconfig.hh" #include "ifconfig_vlan_set_linux.hh" // // Set VLAN information about network interfaces configuration with the // underlying system. // // The mechanism to set the information is Linux-specific ioctl(2). // IfConfigVlanSetLinux::IfConfigVlanSetLinux(FeaDataPlaneManager& fea_data_plane_manager, bool is_dummy) : IfConfigVlanSet(fea_data_plane_manager), _is_dummy(is_dummy), _s4(-1) { } IfConfigVlanSetLinux::~IfConfigVlanSetLinux() { #if defined(HAVE_VLAN_LINUX) or defined(HAVE_VLAN_BSD) if (! _is_dummy) { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the Linux-specific ioctl(2) mechanism to set " "information about VLAN network interfaces into the " "underlying system: %s", error_msg.c_str()); } } #endif } int IfConfigVlanSetLinux::start(string& error_msg) { if (_is_dummy) _is_running = true; if (_is_running) return (XORP_OK); #if defined(HAVE_VLAN_LINUX) or defined(HAVE_VLAN_BSD) if (_s4 < 0) { _s4 = socket(AF_INET, SOCK_DGRAM, 0); if (_s4 < 0) { error_msg = c_format("Could not initialize IPv4 ioctl() " "socket: %s", strerror(errno)); XLOG_FATAL("%s", error_msg.c_str()); } } #else UNUSED(error_msg); #endif _is_running = true; return (XORP_OK); } int IfConfigVlanSetLinux::stop(string& error_msg) { if (_is_dummy) _is_running = false; if (! _is_running) return (XORP_OK); #if defined(HAVE_VLAN_LINUX) or defined(HAVE_VLAN_BSD) int ret_value4 = XORP_OK; if (_s4 >= 0) { ret_value4 = comm_close(_s4); _s4 = -1; if (ret_value4 != XORP_OK) { error_msg = c_format("Could not close IPv4 ioctl() socket: %s", comm_get_last_error_str()); } } if (ret_value4 != XORP_OK) return (XORP_ERROR); #else UNUSED(error_msg); #endif _is_running = false; return (XORP_OK); } int IfConfigVlanSetLinux::config_add_vlan(const IfTreeInterface* system_ifp, const IfTreeInterface& config_if, bool& created_if, string& error_msg) { if (_is_dummy) { created_if = true; //all fake anyway return XORP_OK; } created_if = false; #if defined(HAVE_VLAN_LINUX) or defined(HAVE_VLAN_BSD) // // Test whether the VLAN already exists // if ((system_ifp != NULL) && (system_ifp->parent_ifname() == config_if.parent_ifname()) && (system_ifp->iface_type() == config_if.iface_type()) && (system_ifp->vid() == config_if.vid())) { return (XORP_OK); // XXX: nothing changed } // // Delete the old VLAN if necessary // if (system_ifp != NULL) { if (system_ifp->is_vlan()) { if (delete_vlan(config_if.ifname(), error_msg) != XORP_OK) { error_msg = c_format("Failed to delete VLAN %s, reason: %s", config_if.ifname().c_str(), error_msg.c_str()); return (XORP_ERROR); } } // TODO: Support mac-vlans, etc?? } // // Add the VLAN // if (config_if.is_vlan()) { int vid = atoi(config_if.vid().c_str()); if (vid < 0 || vid >= 4095) { error_msg = c_format("ERROR: VLAN-ID: %s is out of range (0-4094).\n", config_if.vid().c_str()); return XORP_ERROR; } if (add_vlan(config_if.parent_ifname(), config_if.ifname(), vid, created_if, error_msg) != XORP_OK) { error_msg = c_format("Failed to add VLAN %i to interface %s: %s", vid, config_if.parent_ifname().c_str(), error_msg.c_str()); return XORP_ERROR; } return XORP_OK; } // TODO: else, support mac-vlans, etc. error_msg = c_format("Unknown virtual device type: %s\n", config_if.iface_type().c_str()); return XORP_ERROR; #else UNUSED(system_ifp); UNUSED(config_if); UNUSED(error_msg); return XORP_OK; #endif } int IfConfigVlanSetLinux::config_delete_vlan(const IfTreeInterface& config_iface, string& error_msg) { // // Delete the VLAN // return delete_vlan(config_iface.ifname(), error_msg); } int IfConfigVlanSetLinux::add_vlan(const string& parent_ifname, const string& vlan_name, uint16_t vlan_id, bool& created_if, string& error_msg) { created_if = true; if (_is_dummy) return XORP_OK; #ifdef HAVE_VLAN_BSD struct ifreq ifreq; // // Create the VLAN // do { int result; bool is_same_name = false; // This seems to create the VLAN and set the VID all at once. // The old way of using ioctls to create the VLAN was failing // due to failure to set the VID (EBUSY). string cmd(c_format("ifconfig %s.%hu create", parent_ifname.c_str(), vlan_id)); result = system(cmd.c_str()); XLOG_WARNING("Tried to create vlan with command: %s, result: %i", cmd.c_str(), result); cmd = c_format("%s.%hu", parent_ifname.c_str(), vlan_id); if (strcmp(vlan_name.c_str(), cmd.c_str()) == 0) { is_same_name = true; } if ((result >= 0) && is_same_name) { break; // Success } #ifndef SIOCSIFNAME // // No support for interface renaming, therefore return an error // if (result < 0) { error_msg = c_format("Cannot create VLAN interface %s: %s errno: %i", vlan_name.c_str(), strerror(errno), errno); created_if = false; return (XORP_ERROR); } // XXX: The created name didn't match XLOG_ASSERT(is_same_name == false); error_msg = c_format("Cannot create VLAN interface %s: " "the created name (%s) doesn't match", vlan_name.c_str(), ifreq.ifr_name); string dummy_error_msg; delete_vlan(vlan_name, dummy_error_msg); created_if = false; return (XORP_ERROR); #else // SIOCSIFNAME // // Create the VLAN interface with a temporary name // string tmp_vlan_name = c_format("vlan%u", vlan_id); memset(&ifreq, 0, sizeof(ifreq)); strlcpy(ifreq.ifr_name, tmp_vlan_name.c_str(), sizeof(ifreq.ifr_name)); if (ioctl(_s4, SIOCIFCREATE, &ifreq) < 0) { error_msg = c_format("Cannot create VLAN interface %s: %s", tmp_vlan_name.c_str(), strerror(errno)); created_if = false; return (XORP_ERROR); } // // XXX: We don't care if the created name doesn't match the // intended name, because this is just a temporary one. // tmp_vlan_name = string(ifreq.ifr_name); // // Rename the VLAN interface // char new_vlan_name[sizeof(ifreq.ifr_name)]; memset(&ifreq, 0, sizeof(ifreq)); strlcpy(ifreq.ifr_name, tmp_vlan_name.c_str(), sizeof(ifreq.ifr_name)); strlcpy(new_vlan_name, vlan_name.c_str(), sizeof(new_vlan_name)); ifreq.ifr_data = new_vlan_name; if (ioctl(_s4, SIOCSIFNAME, &ifreq) < 0) { error_msg = c_format("Cannot rename VLAN interface %s to %s: %s", tmp_vlan_name.c_str(), new_vlan_name, strerror(errno)); string dummy_error_msg; delete_vlan(tmp_vlan_name.c_str(), dummy_error_msg); created_if = false; return (XORP_ERROR); } #endif // SIOCSIFNAME break; } while (false); #elif defined(HAVE_VLAN_LINUX) /* Linux */ struct vlan_ioctl_args vlanreq; // // Set the VLAN interface naming // memset(&vlanreq, 0, sizeof(vlanreq)); vlanreq.u.name_type = VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD; // eth0.5 vlanreq.cmd = SET_VLAN_NAME_TYPE_CMD; if (ioctl(_s4, SIOCSIFVLAN, &vlanreq) < 0) { error_msg = c_format("Cannot set the VLAN interface name type" "to VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD: %s", strerror(errno)); created_if = false; return XORP_ERROR; } // // Create the VLAN // memset(&vlanreq, 0, sizeof(vlanreq)); strlcpy(vlanreq.device1, parent_ifname.c_str(), sizeof(vlanreq.device1)); vlanreq.u.VID = vlan_id; vlanreq.cmd = ADD_VLAN_CMD; errno = 0; if (ioctl(_s4, SIOCSIFVLAN, &vlanreq) < 0) { created_if = false; if (errno != EEXIST) { error_msg = c_format("Cannot create VLAN interface %s " "(parent = %s VLAN ID = %u): %s", vlan_name.c_str(), parent_ifname.c_str(), vlan_id, strerror(errno)); return (XORP_ERROR); } } // // Rename the VLAN interface if necessary // string tmp_vlan_name = c_format("%s.%u", parent_ifname.c_str(), vlan_id); if (vlan_name != tmp_vlan_name) { #ifndef SIOCSIFNAME // // No support for interface renaming, therefore return an error // error_msg = c_format("Cannot create VLAN interface %s " "(parent = %s VLAN ID = %u): " "no support for interface renaming", vlan_name.c_str(), parent_ifname.c_str(), vlan_id); created_if = false; return (XORP_ERROR); #else // SIOCSIFNAME // // Rename the VLAN interface // struct ifreq ifreq; char new_vlan_name[sizeof(ifreq.ifr_newname)]; memset(&ifreq, 0, sizeof(ifreq)); strlcpy(ifreq.ifr_name, tmp_vlan_name.c_str(), sizeof(ifreq.ifr_name)); strlcpy(new_vlan_name, vlan_name.c_str(), sizeof(new_vlan_name)); strlcpy(ifreq.ifr_newname, new_vlan_name, sizeof(ifreq.ifr_newname)); if (ioctl(_s4, SIOCSIFNAME, &ifreq) < 0) { error_msg = c_format("Cannot rename VLAN interface %s to %s: %s", tmp_vlan_name.c_str(), new_vlan_name, strerror(errno)); string dummy_error_msg; delete_vlan(tmp_vlan_name, dummy_error_msg); created_if = false; return (XORP_ERROR); } #endif // SIOCSIFNAME } #else /* Un-supported VLAN platform (Windows??) */ UNUSED(parent_ifname); UNUSED(vlan_name); UNUSED(vlan_id); UNUSED(error_msg); #endif return (XORP_OK); } int IfConfigVlanSetLinux::delete_vlan(const string& vlan_name, string& error_msg) { if (_is_dummy) return XORP_OK; // // Delete the VLAN // #ifdef HAVE_VLAN_BSD struct ifreq ifreq; memset(&ifreq, 0, sizeof(ifreq)); strlcpy(ifreq.ifr_name, vlan_name.c_str(), sizeof(ifreq.ifr_name)); if (ioctl(_s4, SIOCIFDESTROY, &ifreq) < 0) { error_msg = c_format("Cannot destroy BSD VLAN interface %s: %s", vlan_name.c_str(), strerror(errno)); return (XORP_ERROR); } #elif defined(HAVE_VLAN_LINUX) struct vlan_ioctl_args vlanreq; memset(&vlanreq, 0, sizeof(vlanreq)); strlcpy(vlanreq.device1, vlan_name.c_str(), sizeof(vlanreq.device1)); vlanreq.cmd = DEL_VLAN_CMD; if (ioctl(_s4, SIOCSIFVLAN, &vlanreq) < 0) { error_msg = c_format("Cannot destroy Linux VLAN interface %s: %s", vlan_name.c_str(), strerror(errno)); return (XORP_ERROR); } #else /* Un-supported VLAN platform...Windows?? */ UNUSED(vlan_name); UNUSED(error_msg); #endif return (XORP_OK); } xorp/fea/data_plane/ifconfig/ifconfig_parse_ioctl.cc0000664000076400007640000004071511540225524022755 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include // We use Netlink if we can #if defined(HAVE_IOCTL_SIOCGIFCONF) && !defined(HAVE_NETLINK_SOCKETS) #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ether_compat.h" #include "libcomm/comm_api.h" #ifdef HAVE_SYS_IOCTL_H #include #endif #ifdef HAVE_SYS_SOCKIO_H #include #endif #ifdef HAVE_NET_IF_H #include #endif #ifdef HAVE_NET_IF_VAR_H #include #endif #ifdef HAVE_NET_IF_DL_H #include #endif #ifdef HAVE_NET_IF_TYPES_H #include #endif #ifdef HAVE_NETINET6_IN6_VAR_H #include #endif #include "fea/ifconfig.hh" #include "fea/data_plane/control_socket/system_utilities.hh" #include "ifconfig_get_ioctl.hh" #include "ifconfig_media.hh" // // Parse information about network interface configuration change from // the underlying system. // // The information to parse is in "struct ifreq" format // (e.g., obtained by ioctl(SIOCGIFCONF) mechanism). // int IfConfigGetIoctl::parse_buffer_ioctl(IfConfig& ifconfig, IfTree& iftree, int family, const vector& buffer) { #ifndef HOST_OS_WINDOWS int s; uint32_t if_index = 0; string if_name, alias_if_name; size_t offset; UNUSED(ifconfig); s = socket(family, SOCK_DGRAM, 0); if (s < 0) { XLOG_FATAL("Could not initialize ioctl() socket"); } for (offset = 0; offset < buffer.size(); ) { bool is_newlink = false; // True if really a new link size_t len = 0; struct ifreq ifreq, ifrcopy; memcpy(&ifreq, &buffer[offset], sizeof(ifreq)); // Get the length of the ifreq entry #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN len = max(sizeof(struct sockaddr), static_cast(ifreq.ifr_addr.sa_len)); #else switch (ifreq.ifr_addr.sa_family) { #ifdef HAVE_IPV6 case AF_INET6: len = sizeof(struct sockaddr_in6); break; #endif // HAVE_IPV6 case AF_INET: default: len = sizeof(struct sockaddr); break; } #endif // HAVE_STRUCT_SOCKADDR_SA_LEN len += sizeof(ifreq.ifr_name); len = max(len, sizeof(struct ifreq)); offset += len; // Point to the next entry // // Get the interface name // char tmp_if_name[IFNAMSIZ+1]; strncpy(tmp_if_name, ifreq.ifr_name, sizeof(tmp_if_name) - 1); tmp_if_name[sizeof(tmp_if_name) - 1] = '\0'; char* cptr; if ( (cptr = strchr(tmp_if_name, ':')) != NULL) { // Replace colon with null. Needed because in Solaris and Linux // the interface name changes for aliases. *cptr = '\0'; } if_name = string(ifreq.ifr_name); alias_if_name = string(tmp_if_name); debug_msg("interface: %s\n", if_name.c_str()); debug_msg("alias interface: %s\n", alias_if_name.c_str()); // // Get the physical interface index // do { if_index = 0; #ifdef HAVE_IF_NAMETOINDEX if_index = if_nametoindex(if_name.c_str()); if (if_index > 0) break; #endif // HAVE_IF_NAMETOINDEX #ifdef SIOCGIFINDEX { struct ifreq ifridx; memset(&ifridx, 0, sizeof(ifridx)); strncpy(ifridx.ifr_name, if_name.c_str(), sizeof(ifridx.ifr_name) - 1); if (ioctl(s, SIOCGIFINDEX, &ifridx) < 0) { XLOG_ERROR("ioctl(SIOCGIFINDEX) for interface %s failed: %s", if_name.c_str(), strerror(errno)); } else { #ifdef HAVE_STRUCT_IFREQ_IFR_IFINDEX if_index = ifridx.ifr_ifindex; #else if_index = ifridx.ifr_index; #endif } } if (if_index > 0) break; #endif // SIOCGIFINDEX break; } while (false); if (if_index == 0) { XLOG_FATAL("Could not find physical interface index " "for interface %s", if_name.c_str()); } debug_msg("interface index: %u\n", if_index); // // Add the interface (if a new one) // IfTreeInterface* ifp = iftree.find_interface(alias_if_name); if (ifp == NULL) { iftree.add_interface(alias_if_name); is_newlink = true; ifp = iftree.find_interface(alias_if_name); XLOG_ASSERT(ifp != NULL); } // // Set the physical interface index for the interface // if (is_newlink || (if_index != ifp->pif_index())) ifp->set_pif_index(if_index); // // Get the MAC address // do { #ifdef AF_LINK const struct sockaddr* sa = &ifreq.ifr_addr; if (sa->sa_family == AF_LINK) { const struct sockaddr_dl* sdl = reinterpret_cast(sa); if (sdl->sdl_type == IFT_ETHER) { if (sdl->sdl_alen == sizeof(struct ether_addr)) { struct ether_addr ea; memcpy(&ea, sdl->sdl_data + sdl->sdl_nlen, sdl->sdl_alen); Mac mac(ea); if (is_newlink || (mac != ifp->mac())) ifp->set_mac(mac); break; } else if (sdl->sdl_alen != 0) { XLOG_ERROR("Address size %d uncatered for interface %s", sdl->sdl_alen, if_name.c_str()); } } } #endif // AF_LINK #ifdef SIOCGIFHWADDR memcpy(&ifrcopy, &ifreq, sizeof(ifrcopy)); if (ioctl(s, SIOCGIFHWADDR, &ifrcopy) < 0) { XLOG_ERROR("ioctl(SIOCGIFHWADDR) for interface %s failed: %s", if_name.c_str(), strerror(errno)); } else { struct ether_addr ea; memcpy(&ea, ifrcopy.ifr_hwaddr.sa_data, sizeof(ea)); Mac mac(ea); if (is_newlink || (mac != ifp->mac())) ifp->set_mac(mac); break; } #endif // SIOCGIFHWADDR break; } while (false); debug_msg("MAC address: %s\n", ifp->mac().str().c_str()); // // Get the MTU // unsigned int mtu = 0; memcpy(&ifrcopy, &ifreq, sizeof(ifrcopy)); if (ioctl(s, SIOCGIFMTU, &ifrcopy) < 0) { XLOG_ERROR("ioctl(SIOCGIFMTU) for interface %s failed: %s", if_name.c_str(), strerror(errno)); } else { #ifndef HOST_OS_SOLARIS mtu = ifrcopy.ifr_mtu; #else // // XXX: Solaris supports ioctl(SIOCGIFMTU), but stores the MTU // in the ifr_metric field instead of ifr_mtu. // mtu = ifrcopy.ifr_metric; #endif // HOST_OS_SOLARIS } if (is_newlink || (mtu != ifp->mtu())) ifp->set_mtu(mtu); debug_msg("MTU: %u\n", XORP_UINT_CAST(ifp->mtu())); // // Get the flags // unsigned int flags = 0; memcpy(&ifrcopy, &ifreq, sizeof(ifrcopy)); if (ioctl(s, SIOCGIFFLAGS, &ifrcopy) < 0) { XLOG_ERROR("ioctl(SIOCGIFFLAGS) for interface %s failed: %s", if_name.c_str(), strerror(errno)); } else { flags = ifrcopy.ifr_flags; } if (is_newlink || (flags != ifp->interface_flags())) { ifp->set_interface_flags(flags); ifp->set_enabled(flags & IFF_UP); } debug_msg("enabled: %s\n", bool_c_str(ifp->enabled())); // // Get the link status and baudrate // do { bool no_carrier = false; uint64_t baudrate = 0; string error_msg; if (ifconfig_media_get_link_status(if_name, no_carrier, baudrate, error_msg) != XORP_OK) { // XXX: No other method to retrieve the information } if (is_newlink || (no_carrier != ifp->no_carrier())) ifp->set_no_carrier(no_carrier); if (is_newlink || (baudrate != ifp->baudrate())) ifp->set_baudrate(baudrate); break; } while (false); debug_msg("no_carrier: %s\n", bool_c_str(ifp->no_carrier())); debug_msg("baudrate: %u\n", XORP_UINT_CAST(ifp->baudrate())); // XXX: vifname == ifname on this platform if (is_newlink) ifp->add_vif(alias_if_name); IfTreeVif* vifp = ifp->find_vif(alias_if_name); XLOG_ASSERT(vifp != NULL); // // Set the physical interface index for the vif // if (is_newlink || (if_index != vifp->pif_index())) vifp->set_pif_index(if_index); // // Set the vif flags // if (is_newlink || (flags != vifp->vif_flags())) { vifp->set_vif_flags(flags); vifp->set_enabled(ifp->enabled() && (flags & IFF_UP)); vifp->set_broadcast(flags & IFF_BROADCAST); vifp->set_loopback(flags & IFF_LOOPBACK); #ifdef IFF_POINTOPOINT vifp->set_point_to_point(flags & IFF_POINTOPOINT); #endif vifp->set_multicast(flags & IFF_MULTICAST); // Propagate the flags to the existing addresses vifp->propagate_flags_to_addresses(); } debug_msg("vif enabled: %s\n", bool_c_str(vifp->enabled())); debug_msg("vif broadcast: %s\n", bool_c_str(vifp->broadcast())); debug_msg("vif loopback: %s\n", bool_c_str(vifp->loopback())); debug_msg("vif point_to_point: %s\n", bool_c_str(vifp->point_to_point())); debug_msg("vif multicast: %s\n", bool_c_str(vifp->multicast())); // // Get the interface addresses for the same address family only. // XXX: if the address family is zero, then we query the address. // if ((ifreq.ifr_addr.sa_family != family) && (ifreq.ifr_addr.sa_family != 0)) { continue; } // // Get the IP address, netmask, broadcast address, P2P destination // // The default values IPvX lcl_addr = IPvX::ZERO(family); IPvX subnet_mask = IPvX::ZERO(family); IPvX broadcast_addr = IPvX::ZERO(family); IPvX peer_addr = IPvX::ZERO(family); bool has_broadcast_addr = false; bool has_peer_addr = false; struct ifreq ip_ifrcopy; memcpy(&ip_ifrcopy, &ifreq, sizeof(ip_ifrcopy)); ip_ifrcopy.ifr_addr.sa_family = family; #ifdef SIOCGIFADDR_IN6 struct in6_ifreq ip_ifrcopy6; memcpy(&ip_ifrcopy6, &ifreq, sizeof(ip_ifrcopy6)); ip_ifrcopy6.ifr_ifru.ifru_addr.sin6_family = family; #endif // SIOCGIFADDR_IN6 // Get the IP address if (ifreq.ifr_addr.sa_family == family) { lcl_addr.copy_in(ifreq.ifr_addr); memcpy(&ip_ifrcopy, &ifreq, sizeof(ip_ifrcopy)); #ifdef SIOCGIFADDR_IN6 memcpy(&ip_ifrcopy6, &ifreq, sizeof(ip_ifrcopy6)); #endif } else { // XXX: we need to query the local IP address XLOG_ASSERT(ifreq.ifr_addr.sa_family == 0); switch (family) { case AF_INET: #ifdef SIOCGIFADDR memset(&ifrcopy, 0, sizeof(ifrcopy)); strncpy(ifrcopy.ifr_name, if_name.c_str(), sizeof(ifrcopy.ifr_name) - 1); ifrcopy.ifr_addr.sa_family = family; if (ioctl(s, SIOCGIFADDR, &ifrcopy) < 0) { // XXX: the interface probably has no address. Ignore. continue; } else { lcl_addr.copy_in(ifrcopy.ifr_addr); memcpy(&ip_ifrcopy, &ifrcopy, sizeof(ip_ifrcopy)); } #endif // SIOCGIFADDR break; #ifdef HAVE_IPV6 case AF_INET6: #ifdef SIOCGIFADDR_IN6 { struct in6_ifreq ifrcopy6; memset(&ifrcopy6, 0, sizeof(ifrcopy6)); strncpy(ifrcopy6.ifr_name, if_name.c_str(), sizeof(ifrcopy6.ifr_name) - 1); ifrcopy6.ifr_ifru.ifru_addr.sin6_family = family; if (ioctl(s, SIOCGIFADDR_IN6, &ifrcopy6) < 0) { XLOG_ERROR("ioctl(SIOCGIFADDR_IN6) failed: %s", strerror(errno)); } else { lcl_addr.copy_in(ifrcopy6.ifr_ifru.ifru_addr); memcpy(&ip_ifrcopy6, &ifrcopy6, sizeof(ip_ifrcopy6)); } } #endif // SIOCGIFADDR_IN6 break; #endif // HAVE_IPV6 default: XLOG_UNREACHABLE(); break; } } lcl_addr = system_adjust_ipvx_recv(lcl_addr); debug_msg("IP address: %s\n", lcl_addr.str().c_str()); // Get the netmask switch (family) { case AF_INET: #ifdef SIOCGIFNETMASK memcpy(&ifrcopy, &ip_ifrcopy, sizeof(ifrcopy)); if (ioctl(s, SIOCGIFNETMASK, &ifrcopy) < 0) { if (! vifp->point_to_point()) { XLOG_ERROR("ioctl(SIOCGIFNETMASK) failed: %s", strerror(errno)); } } else { // The interface is configured // XXX: SIOCGIFNETMASK doesn't return proper family ifrcopy.ifr_addr.sa_family = family; subnet_mask.copy_in(ifrcopy.ifr_addr); } #endif // SIOCGIFNETMASK break; #ifdef HAVE_IPV6 case AF_INET6: #ifdef SIOCGIFNETMASK_IN6 { struct in6_ifreq ifrcopy6; memcpy(&ifrcopy6, &ip_ifrcopy6, sizeof(ifrcopy6)); if (ioctl(s, SIOCGIFNETMASK_IN6, &ifrcopy6) < 0) { if (! vifp->point_to_point()) { XLOG_ERROR("ioctl(SIOCGIFNETMASK_IN6) failed: %s", strerror(errno)); } } else { // The interface is configured // XXX: SIOCGIFNETMASK_IN6 doesn't return proper family ifrcopy6.ifr_ifru.ifru_addr.sin6_family = family; subnet_mask.copy_in(ifrcopy6.ifr_ifru.ifru_addr); } } #endif // SIOCGIFNETMASK_IN6 break; #endif // HAVE_IPV6 default: XLOG_UNREACHABLE(); break; } debug_msg("IP netmask: %s\n", subnet_mask.str().c_str()); // Get the broadcast address if (vifp->broadcast()) { switch (family) { case AF_INET: #ifdef SIOCGIFBRDADDR memcpy(&ifrcopy, &ip_ifrcopy, sizeof(ifrcopy)); if (ioctl(s, SIOCGIFBRDADDR, &ifrcopy) < 0) { XLOG_ERROR("ioctl(SIOCGIFBRADDR) failed: %s", strerror(errno)); } else { // XXX: in case it doesn't return proper family ifrcopy.ifr_broadaddr.sa_family = family; broadcast_addr.copy_in(ifrcopy.ifr_addr); has_broadcast_addr = true; } #endif // SIOCGIFBRDADDR break; #ifdef HAVE_IPV6 case AF_INET6: break; // IPv6 doesn't have the idea of broadcast #endif // HAVE_IPV6 default: XLOG_UNREACHABLE(); break; } debug_msg("Broadcast address: %s\n", broadcast_addr.str().c_str()); } // Get the p2p address if (vifp->point_to_point()) { switch (family) { case AF_INET: #ifdef SIOCGIFDSTADDR memcpy(&ifrcopy, &ip_ifrcopy, sizeof(ifrcopy)); if (ioctl(s, SIOCGIFDSTADDR, &ifrcopy) < 0) { // Probably the p2p address is not configured } else { // The p2p address is configured // XXX: in case it doesn't return proper family ifrcopy.ifr_dstaddr.sa_family = family; peer_addr.copy_in(ifrcopy.ifr_dstaddr); has_peer_addr = true; } #endif // SIOCGIFDSTADDR break; #ifdef HAVE_IPV6 case AF_INET6: #ifdef SIOCGIFDSTADDR_IN6 { struct in6_ifreq ifrcopy6; memcpy(&ifrcopy6, &ip_ifrcopy6, sizeof(ifrcopy6)); if (ioctl(s, SIOCGIFDSTADDR_IN6, &ifrcopy6) < 0) { // Probably the p2p address is not configured } else { // The p2p address is configured // Just in case it doesn't return proper family ifrcopy6.ifr_ifru.ifru_dstaddr.sin6_family = family; peer_addr.copy_in(ifrcopy6.ifr_ifru.ifru_dstaddr); has_peer_addr = true; } } #endif // SIOCGIFDSTADDR_IN6 break; #endif // HAVE_IPV6 default: XLOG_UNREACHABLE(); break; } debug_msg("Peer address: %s\n", peer_addr.str().c_str()); } debug_msg("\n"); // put an empty line between interfaces // Add the address switch (family) { case AF_INET: { vifp->add_addr(lcl_addr.get_ipv4()); IfTreeAddr4* ap = vifp->find_addr(lcl_addr.get_ipv4()); XLOG_ASSERT(ap != NULL); ap->set_enabled(vifp->enabled() && (flags & IFF_UP)); ap->set_broadcast(vifp->broadcast() && (flags & IFF_BROADCAST) && has_broadcast_addr); ap->set_loopback(vifp->loopback() && (flags & IFF_LOOPBACK)); #ifdef IFF_POINTOPOINT ap->set_point_to_point(vifp->point_to_point() && (flags & IFF_POINTOPOINT) && has_peer_addr); #else UNUSED(has_peer_addr); #endif ap->set_multicast(vifp->multicast() && (flags & IFF_MULTICAST)); ap->set_prefix_len(subnet_mask.mask_len()); if (ap->broadcast()) ap->set_bcast(broadcast_addr.get_ipv4()); if (ap->point_to_point()) ap->set_endpoint(peer_addr.get_ipv4()); break; } #ifdef HAVE_IPV6 case AF_INET6: { vifp->add_addr(lcl_addr.get_ipv6()); IfTreeAddr6* ap = vifp->find_addr(lcl_addr.get_ipv6()); XLOG_ASSERT(ap != NULL); ap->set_enabled(vifp->enabled() && (flags & IFF_UP)); ap->set_loopback(vifp->loopback() && (flags & IFF_LOOPBACK)); ap->set_point_to_point(vifp->point_to_point() && (flags & IFF_POINTOPOINT)); ap->set_multicast(vifp->multicast() && (flags & IFF_MULTICAST)); ap->set_prefix_len(subnet_mask.mask_len()); if (ap->point_to_point()) ap->set_endpoint(peer_addr.get_ipv6()); break; } #endif // HAVE_IPV6 default: XLOG_UNREACHABLE(); break; } } comm_close(s); return (XORP_OK); #else // HOST_OS_WINDOWS XLOG_FATAL("WinSock2 does not support struct ifreq."); UNUSED(it); UNUSED(family); UNUSED(buffer); return (XORP_ERROR); #endif // HOST_OS_WINDOWS } #endif // HAVE_IOCTL_SIOCGIFCONF and not NETLINK xorp/fea/data_plane/ifconfig/ifconfig_property_windows.hh0000664000076400007640000000373511540224223024115 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2007-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/data_plane/ifconfig/ifconfig_property_windows.hh,v 1.6 2008/10/02 21:57:07 bms Exp $ #ifndef __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_PROPERTY_WINDOWS_HH__ #define __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_PROPERTY_WINDOWS_HH__ #include "fea/ifconfig_property.hh" class IfConfigPropertyWindows : public IfConfigProperty { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ IfConfigPropertyWindows(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~IfConfigPropertyWindows(); private: /** * Test whether the underlying system supports IPv4. * * @return true if the underlying system supports IPv4, otherwise false. */ virtual bool test_have_ipv4() const; /** * Test whether the underlying system supports IPv6. * * @return true if the underlying system supports IPv6, otherwise false. */ virtual bool test_have_ipv6() const; }; #endif // __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_PROPERTY_WINDOWS_HH__ xorp/fea/data_plane/ifconfig/ifconfig_media.cc0000664000076400007640000001747211703345405021536 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2012 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/c_format.hh" #ifdef HAVE_SYS_IOCTL_H #include #endif #ifdef HAVE_SYS_SOCKIO_H #include #endif #ifdef HAVE_NET_IF_H #include #endif #ifdef HAVE_NET_IF_MEDIA_H #include #endif #ifdef HAVE_LINUX_TYPES_H #include #endif #ifdef HAVE_LINUX_SOCKIOS_H #include #endif #ifdef HAVE_LINUX_ETHTOOL_H // // XXX: A hack defining missing (kernel) types that are used in // with some OS distributions. // typedef uint8_t u8; typedef uint16_t u16; typedef uint32_t u32; typedef uint64_t u64; #include #endif // HAVE_LINUX_ETHTOOL_H // // XXX: We should include , but that file is broken for // older Linux versions (e.g., RedHat-7.3 with Linux kernel 2.4.20-28.7smp). // // #ifdef HAVE_LINUX_MII_H // #include // #endif #include "ifconfig_media.hh" // // Network interfaces media related utilities. // // // Get the link status // int ifconfig_media_get_link_status(const string& if_name, bool& no_carrier, uint64_t& baudrate, string& error_msg) { UNUSED(if_name); no_carrier = false; baudrate = 0; #ifdef SIOCGIFMEDIA do { int s; struct ifmediareq ifmr; memset(&ifmr, 0, sizeof(ifmr)); strncpy(ifmr.ifm_name, if_name.c_str(), sizeof(ifmr.ifm_name) - 1); s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) { break; // try another method } if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0) { // // XXX: Most likely the interface doesn't support SIOCGIFMEDIA, // hence this is not an error. // no_carrier = false; close(s); break; // Try another method } close(s); switch (IFM_TYPE(ifmr.ifm_active)) { case IFM_ETHER: #ifdef IFM_FDDI case IFM_FDDI: #endif #ifdef IFM_TOKEN case IFM_TOKEN: #endif case IFM_IEEE80211: if ((ifmr.ifm_status & IFM_AVALID) && (ifmr.ifm_status & IFM_ACTIVE)) { no_carrier = false; } else { no_carrier = true; } break; default: no_carrier = false; break; } // // Get the link baudrate // #ifdef IFM_BAUDRATE_DESCRIPTIONS static const struct ifmedia_baudrate ifm[] = IFM_BAUDRATE_DESCRIPTIONS; for (size_t i = 0; ifm[i].ifmb_word != 0; i++) { if ((ifmr.ifm_active & (IFM_NMASK | IFM_TMASK)) != ifm[i].ifmb_word) { continue; } baudrate = ifm[i].ifmb_baudrate; break; } #endif // IFM_BAUDRATE_DESCRIPTIONS error_msg = ""; // all is forgiven return (XORP_OK); } while (false); #endif // SIOCGIFMEDIA #if defined(SIOCETHTOOL) && defined(ETHTOOL_GLINK) do { int s; struct ifreq ifreq; // // XXX: Do this only if we have the necessary permission // if (geteuid() != 0) { error_msg += c_format("Must be root to query the interface status\n"); break; // try another method } memset(&ifreq, 0, sizeof(ifreq)); strncpy(ifreq.ifr_name, if_name.c_str(), sizeof(ifreq.ifr_name) - 1); s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) { break; //try another method } // Get the link status struct ethtool_value edata; memset(&edata, 0, sizeof(edata)); edata.cmd = ETHTOOL_GLINK; ifreq.ifr_data = reinterpret_cast(&edata); if (ioctl(s, SIOCETHTOOL, &ifreq) < 0) { error_msg += c_format("ioctl(SIOCETHTOOL) for interface %s " "failed: %s\n", if_name.c_str(), strerror(errno)); close(s); break; //try another method } if (edata.data != 0) no_carrier = false; else no_carrier = true; // // Get the link baudrate // #ifdef ETHTOOL_GSET struct ethtool_cmd ecmd; memset(&ecmd, 0, sizeof(ecmd)); ecmd.cmd = ETHTOOL_GSET; ifreq.ifr_data = reinterpret_cast(&ecmd); if (ioctl(s, SIOCETHTOOL, &ifreq) < 0) { // // XXX: ignore any errors, because the non-Ethernet // interfaces might not support this query. // } else { // XXX: The ecmd.speed is returned in Mbps baudrate = ecmd.speed * 1000 * 1000; } #endif // ETHTOOL_GSET close(s); error_msg = ""; // all is forgiven return (XORP_OK); } while (false); #endif // SIOCETHTOOL && ETHTOOL_GLINK #ifdef SIOCGMIIREG do { int s; struct ifreq ifreq; memset(&ifreq, 0, sizeof(ifreq)); strncpy(ifreq.ifr_name, if_name.c_str(), sizeof(ifreq.ifr_name) - 1); // // Define data structure and definitions used for MII ioctl's. // // We need to do this here because some of them are missing // from the system's header file, and because // this header file is broken for older Linux versions // (e.g., RedHat-7.3 with Linux kernel 2.4.20-28.7smp). // #ifndef MII_BMSR #define MII_BMSR 0x01 #endif #ifndef MII_BMSR_LINK_VALID #define MII_BMSR_LINK_VALID 0x0004 #endif struct mii_data { uint16_t phy_id; uint16_t reg_num; uint16_t val_in; uint16_t val_out; } __attribute__((packed)); s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) { break; //try another method } if (ioctl(s, SIOCGMIIPHY, &ifreq) < 0) { error_msg += c_format("ioctl(SIOCGMIIPHY) for interface %s " "failed: %s\n", if_name.c_str(), strerror(errno)); close(s); break; //try another method } // NOTE: We cannot just cast &ifr_addr to an mii // pointer, as it gives strict-aliasing errors on some // compilers when optimization is enabled. So, copy // around the data instead. struct mii_data mii; memset(&mii, 0, sizeof(mii)); mii.reg_num = MII_BMSR; memcpy(&ifreq.ifr_addr, &mii, sizeof(mii)); if (ioctl(s, SIOCGMIIREG, &ifreq) < 0) { error_msg += c_format("ioctl(SIOCGMIIREG) for interface %s " "failed: %s\n", if_name.c_str(), strerror(errno)); close(s); break; //try another method } close(s); memcpy(&mii, &ifreq.ifr_addr, sizeof(mii)); int bmsr = mii.val_out; if (bmsr & MII_BMSR_LINK_VALID) no_carrier = false; else no_carrier = true; error_msg = ""; // all is forgiven return (XORP_OK); } while (false); #endif // SIOCGMIIREG string cfn("/sys/class/net/"); cfn.append(if_name); cfn.append("/carrier"); errno = 0; // Seems that C++ throws an exception if you just do the >> and the // underlying logic returns EINVAL. Use read and then check for good // instead. ifstream cf(cfn.c_str()); char dummy[4]; dummy[0] = 0; cf.read(dummy, 1); if (cf.good()) { if (dummy[0] == '0') { no_carrier = 1; } else if (dummy[0] == '1') { no_carrier = 0; } else { // can't read, or got funky value, cannot detect this way. error_msg += c_format("Got un-known value: 0x%02hx for %s, cannot probe carrier for this device using sysfs.\n", (unsigned short)(dummy[0]), cfn.c_str()); goto notfound; } error_msg = ""; // all is forgiven return XORP_OK; } else { error_msg += c_format("error reading file: %s errno: %i\n", cfn.c_str(), errno); } notfound: error_msg += c_format("No functional mechanism found to test the link status\n"); return (XORP_ERROR); } xorp/fea/data_plane/ifconfig/ifconfig_observer_dummy.cc0000664000076400007640000000411711421137511023503 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "fea/ifconfig.hh" #include "ifconfig_observer_dummy.hh" // // Observe information change about network interface configuration from // the underlying system. // // The mechanism to observe the information is Dummy (for testing // purpose). // IfConfigObserverDummy::IfConfigObserverDummy(FeaDataPlaneManager& fea_data_plane_manager) : IfConfigObserver(fea_data_plane_manager) { } IfConfigObserverDummy::~IfConfigObserverDummy() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the Dummy mechanism to observe " "information about network interfaces from the underlying " "system: %s", error_msg.c_str()); } } int IfConfigObserverDummy::start(string& error_msg) { UNUSED(error_msg); if (_is_running) return (XORP_OK); _is_running = true; return (XORP_OK); } int IfConfigObserverDummy::stop(string& error_msg) { UNUSED(error_msg); if (! _is_running) return (XORP_OK); _is_running = false; return (XORP_OK); } void IfConfigObserverDummy::receive_data(const vector& buffer) { // TODO: use it? UNUSED(buffer); } xorp/fea/data_plane/ifconfig/ifconfig_property_linux.hh0000664000076400007640000000401111540225524023553 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2007-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/data_plane/ifconfig/ifconfig_property_linux.hh,v 1.5 2008/10/02 21:57:07 bms Exp $ #ifndef __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_PROPERTY_LINUX_HH__ #define __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_PROPERTY_LINUX_HH__ #include #ifdef HOST_OS_LINUX #include "fea/ifconfig_property.hh" class IfConfigPropertyLinux : public IfConfigProperty { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ IfConfigPropertyLinux(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~IfConfigPropertyLinux(); private: /** * Test whether the underlying system supports IPv4. * * @return true if the underlying system supports IPv4, otherwise false. */ virtual bool test_have_ipv4() const; /** * Test whether the underlying system supports IPv6. * * @return true if the underlying system supports IPv6, otherwise false. */ virtual bool test_have_ipv6() const; }; #endif #endif // __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_PROPERTY_LINUX_HH__ xorp/fea/data_plane/ifconfig/ifconfig_parse_getifaddrs.cc0000664000076400007640000003422111421137511023746 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ether_compat.h" #include "libxorp/ipv4.hh" #include "libxorp/ipv6.hh" #ifdef HAVE_SYS_IOCTL_H #include #endif #ifdef HAVE_NET_IF_H #include #endif #ifdef HAVE_NET_IF_VAR_H #include #endif #ifdef HAVE_NET_IF_DL_H #include #endif #ifdef HAVE_NET_IF_TYPES_H #include #endif #ifdef HAVE_NETINET6_IN6_VAR_H #include #endif #ifdef HAVE_IFADDRS_H #include #endif #include "fea/ifconfig.hh" #include "fea/data_plane/control_socket/system_utilities.hh" #include "ifconfig_get_getifaddrs.hh" #include "ifconfig_media.hh" // // Parse information about network interface configuration change from // the underlying system. // // The information to parse is in "struct ifaddrs" format // (e.g., obtained by getifaddrs(3) mechanism). // #ifdef HAVE_GETIFADDRS int IfConfigGetGetifaddrs::parse_buffer_getifaddrs(IfConfig& ifconfig, IfTree& iftree, const struct ifaddrs* ifap) { uint32_t if_index = 0; string if_name, alias_if_name; const struct ifaddrs* ifa; UNUSED(ifconfig); for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { bool is_newlink = false; // True if really a new link if (ifa->ifa_name == NULL) { XLOG_ERROR("Ignoring interface with unknown name"); continue; } // // Get the interface name // char tmp_if_name[IFNAMSIZ+1]; strncpy(tmp_if_name, ifa->ifa_name, sizeof(tmp_if_name) - 1); tmp_if_name[sizeof(tmp_if_name) - 1] = '\0'; char* cptr; if ( (cptr = strchr(tmp_if_name, ':')) != NULL) { // Replace colon with null. Needed because in Solaris and Linux // the interface name changes for aliases. *cptr = '\0'; } if_name = string(ifa->ifa_name); alias_if_name = string(tmp_if_name); debug_msg("interface: %s\n", if_name.c_str()); debug_msg("alias interface: %s\n", alias_if_name.c_str()); // // Get the physical interface index // do { if_index = 0; #ifdef AF_LINK if ((ifa->ifa_addr != NULL) && (ifa->ifa_addr->sa_family == AF_LINK)) { // Link-level address const struct sockaddr_dl* sdl = reinterpret_cast(ifa->ifa_addr); if_index = sdl->sdl_index; } if (if_index > 0) break; #endif // AF_LINK #ifdef HAVE_IF_NAMETOINDEX if_index = if_nametoindex(if_name.c_str()); if (if_index > 0) break; #endif // HAVE_IF_NAMETOINDEX #ifdef SIOCGIFINDEX { int s; struct ifreq ifridx; memset(&ifridx, 0, sizeof(ifridx)); strncpy(ifridx.ifr_name, if_name.c_str(), sizeof(ifridx.ifr_name) - 1); s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) { XLOG_FATAL("Could not initialize IPv4 ioctl() socket"); } if (ioctl(s, SIOCGIFINDEX, &ifridx) < 0) { XLOG_ERROR("ioctl(SIOCGIFINDEX) for interface %s failed: %s", if_name.c_str(), strerror(errno)); } else { #ifdef HAVE_STRUCT_IFREQ_IFR_IFINDEX if_index = ifridx.ifr_ifindex; #else if_index = ifridx.ifr_index; #endif } close(s); } if (if_index > 0) break; #endif // SIOCGIFINDEX break; } while (false); if (if_index == 0) { XLOG_FATAL("Could not find physical interface index " "for interface %s", if_name.c_str()); } debug_msg("interface index: %u\n", if_index); // // Add the interface (if a new one) // IfTreeInterface* ifp = iftree.find_interface(alias_if_name); if (ifp == NULL) { iftree.add_interface(alias_if_name); is_newlink = true; ifp = iftree.find_interface(alias_if_name); XLOG_ASSERT(ifp != NULL); } // // Set the physical interface index for the interface // if (is_newlink || (if_index != ifp->pif_index())) ifp->set_pif_index(if_index); // // Get the MAC address // do { #ifdef AF_LINK if ((ifa->ifa_addr != NULL) && (ifa->ifa_addr->sa_family == AF_LINK)) { // Link-level address const struct sockaddr_dl* sdl = reinterpret_cast(ifa->ifa_addr); if (sdl->sdl_type == IFT_ETHER) { if (sdl->sdl_alen == sizeof(struct ether_addr)) { struct ether_addr ea; memcpy(&ea, sdl->sdl_data + sdl->sdl_nlen, sdl->sdl_alen); Mac mac(ea); if (is_newlink || (mac != ifp->mac())) ifp->set_mac(mac); break; } else if (sdl->sdl_alen != 0) { XLOG_ERROR("Address size %d uncatered for interface %s", sdl->sdl_alen, if_name.c_str()); } } } #endif // AF_LINK #ifdef SIOCGIFHWADDR { int s; struct ifreq ifridx; memset(&ifridx, 0, sizeof(ifridx)); strncpy(ifridx.ifr_name, if_name.c_str(), sizeof(ifridx.ifr_name) - 1); s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) { XLOG_FATAL("Could not initialize IPv4 ioctl() socket"); } if (ioctl(s, SIOCGIFHWADDR, &ifridx) < 0) { XLOG_ERROR("ioctl(SIOCGIFHWADDR) for interface %s failed: %s", if_name.c_str(), strerror(errno)); } else { struct ether_addr ea; memcpy(&ea, ifridx.ifr_hwaddr.sa_data, sizeof(ea)); Mac mac(ea); if (is_newlink || (mac != ifp->mac())) ifp->set_mac(mac); close(s); break; } close(s); } #endif // SIOCGIFHWADDR break; } while (false); debug_msg("MAC address: %s\n", ifp->mac().str().c_str()); // // Get the MTU // do { #ifdef AF_LINK if ((ifa->ifa_addr != NULL) && (ifa->ifa_addr->sa_family == AF_LINK)) { // Link-level address if (ifa->ifa_data != NULL) { const struct if_data* if_data = reinterpret_cast(ifa->ifa_data); unsigned int mtu = if_data->ifi_mtu; if (mtu == 0) { XLOG_ERROR("Couldn't get the MTU for interface %s", if_name.c_str()); } if (is_newlink || (mtu != ifp->mtu())) ifp->set_mtu(mtu); break; } } #endif // AF_LINK #ifdef SIOCGIFMTU { int s; struct ifreq ifridx; memset(&ifridx, 0, sizeof(ifridx)); strncpy(ifridx.ifr_name, if_name.c_str(), sizeof(ifridx.ifr_name) - 1); s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) { XLOG_FATAL("Could not initialize IPv4 ioctl() socket"); } if (ioctl(s, SIOCGIFMTU, &ifridx) < 0) { XLOG_ERROR("ioctl(SIOCGIFMTU) for interface %s failed: %s", if_name.c_str(), strerror(errno)); } else { unsigned int mtu = ifridx.ifr_mtu; if (is_newlink || (mtu != ifp->mtu())) ifp->set_mtu(mtu); close(s); break; } close(s); } #endif // SIOCGIFMTU break; } while (false); debug_msg("MTU: %d\n", ifp->mtu()); // // Get the link status and the baudrate // do { bool no_carrier = false; uint64_t baudrate = 0; string error_msg; if (ifconfig_media_get_link_status(if_name, no_carrier, baudrate, error_msg) != XORP_OK) { // XXX: Use the link-state information #if defined(AF_LINK) && defined(LINK_STATE_UP) && defined(LINK_STATE_DOWN) if ((ifa->ifa_addr != NULL) && (ifa->ifa_addr->sa_family == AF_LINK)) { // Link-level address if (ifa->ifa_data != NULL) { const struct if_data* if_data = reinterpret_cast(ifa->ifa_data); switch (if_data->ifi_link_state) { case LINK_STATE_UNKNOWN: // XXX: link state unknown break; case LINK_STATE_DOWN: no_carrier = true; break; case LINK_STATE_UP: no_carrier = false; break; default: break; } break; } } #endif // AF_LINK && LINK_STATE_UP && LINK_STATE_DOWN } if (is_newlink || (no_carrier != ifp->no_carrier())) ifp->set_no_carrier(no_carrier); if (is_newlink || (baudrate != ifp->baudrate())) ifp->set_baudrate(baudrate); break; } while (false); debug_msg("no_carrier: %s\n", bool_c_str(ifp->no_carrier())); debug_msg("baudrate: %u\n", XORP_UINT_CAST(ifp->baudrate())); // // Get the flags // unsigned int flags = ifa->ifa_flags; if (is_newlink || (flags != ifp->interface_flags())) { ifp->set_interface_flags(flags); ifp->set_enabled(flags & IFF_UP); } debug_msg("enabled: %s\n", bool_c_str(ifp->enabled())); // XXX: vifname == ifname on this platform if (is_newlink) ifp->add_vif(alias_if_name); IfTreeVif* vifp = ifp->find_vif(alias_if_name); XLOG_ASSERT(vifp != NULL); // // Set the physical interface index for the vif // if (is_newlink || (if_index != vifp->pif_index())) vifp->set_pif_index(if_index); // // Set the vif flags // if (is_newlink || (flags != vifp->vif_flags())) { vifp->set_vif_flags(flags); vifp->set_enabled(ifp->enabled() && (flags & IFF_UP)); vifp->set_broadcast(flags & IFF_BROADCAST); vifp->set_loopback(flags & IFF_LOOPBACK); vifp->set_point_to_point(flags & IFF_POINTOPOINT); vifp->set_multicast(flags & IFF_MULTICAST); // Propagate the flags to the existing addresses vifp->propagate_flags_to_addresses(); } debug_msg("vif enabled: %s\n", bool_c_str(vifp->enabled())); debug_msg("vif broadcast: %s\n", bool_c_str(vifp->broadcast())); debug_msg("vif loopback: %s\n", bool_c_str(vifp->loopback())); debug_msg("vif point_to_point: %s\n", bool_c_str(vifp->point_to_point())); debug_msg("vif multicast: %s\n", bool_c_str(vifp->multicast())); // // Get the IP address, netmask, broadcast address, P2P destination // if (ifa->ifa_addr == NULL) continue; switch (ifa->ifa_addr->sa_family) { case AF_INET: { // The default values IPv4 lcl_addr; IPv4 subnet_mask; IPv4 broadcast_addr; IPv4 peer_addr; bool has_broadcast_addr = false; bool has_peer_addr = false; // Get the IP address if (ifa->ifa_addr != NULL) { lcl_addr.copy_in(*ifa->ifa_addr); } debug_msg("IP address: %s\n", lcl_addr.str().c_str()); // Get the netmask if (ifa->ifa_netmask != NULL) { const struct sockaddr_in* sin = sockaddr2sockaddr_in(ifa->ifa_netmask); subnet_mask.copy_in(sin->sin_addr); } debug_msg("IP netmask: %s\n", subnet_mask.str().c_str()); // Get the broadcast address if (vifp->broadcast() && (ifa->ifa_broadaddr != NULL)) { const struct sockaddr_in* sin = sockaddr2sockaddr_in(ifa->ifa_broadaddr); broadcast_addr.copy_in(sin->sin_addr); has_broadcast_addr = true; debug_msg("Broadcast address: %s\n", broadcast_addr.str().c_str()); } // Get the p2p address if (vifp->point_to_point() && (ifa->ifa_dstaddr != NULL)) { const struct sockaddr_in* sin = sockaddr2sockaddr_in(ifa->ifa_dstaddr); peer_addr.copy_in(sin->sin_addr); has_peer_addr = true; debug_msg("Peer address: %s\n", peer_addr.str().c_str()); } debug_msg("\n"); // put an empty line between interfaces // Add the address vifp->add_addr(lcl_addr); IfTreeAddr4* ap = vifp->find_addr(lcl_addr); XLOG_ASSERT(ap != NULL); ap->set_enabled(vifp->enabled() && (flags & IFF_UP)); ap->set_broadcast(vifp->broadcast() && (flags & IFF_BROADCAST) && has_broadcast_addr); ap->set_loopback(vifp->loopback() && (flags & IFF_LOOPBACK)); ap->set_point_to_point(vifp->point_to_point() && (flags & IFF_POINTOPOINT) && has_peer_addr); ap->set_multicast(vifp->multicast() && (flags & IFF_MULTICAST)); ap->set_prefix_len(subnet_mask.mask_len()); if (ap->broadcast()) ap->set_bcast(broadcast_addr); if (ap->point_to_point()) ap->set_endpoint(peer_addr); break; } #ifdef HAVE_IPV6 case AF_INET6: { // The default values IPv6 lcl_addr; IPv6 subnet_mask; IPv6 peer_addr; bool has_peer_addr = false; // Get the IP address if (ifa->ifa_addr != NULL) { lcl_addr.copy_in(*ifa->ifa_addr); } lcl_addr = system_adjust_ipv6_recv(lcl_addr); debug_msg("IP address: %s\n", lcl_addr.str().c_str()); // Get the netmask if (ifa->ifa_netmask != NULL) { const struct sockaddr_in6* sin6 = sockaddr2sockaddr_in6(ifa->ifa_netmask); subnet_mask.copy_in(sin6->sin6_addr); } debug_msg("IP netmask: %s\n", subnet_mask.str().c_str()); // Get the p2p address // XXX: note that unlike IPv4, here we check whether the // address family of the destination address is valid. if (vifp->point_to_point() && (ifa->ifa_dstaddr != NULL) && (ifa->ifa_dstaddr->sa_family == AF_INET6)) { const struct sockaddr_in6* sin6 = sockaddr2sockaddr_in6(ifa->ifa_dstaddr); peer_addr.copy_in(sin6->sin6_addr); has_peer_addr = true; debug_msg("Peer address: %s\n", peer_addr.str().c_str()); } debug_msg("\n"); // put an empty line between interfaces // Add the address vifp->add_addr(lcl_addr); IfTreeAddr6* ap = vifp->find_addr(lcl_addr); XLOG_ASSERT(ap != NULL); ap->set_enabled(vifp->enabled() && (flags & IFF_UP)); ap->set_loopback(vifp->loopback() && (flags & IFF_LOOPBACK)); ap->set_point_to_point(vifp->point_to_point() && (flags & IFF_POINTOPOINT) && has_peer_addr); ap->set_multicast(vifp->multicast() && (flags & IFF_MULTICAST)); ap->set_prefix_len(subnet_mask.mask_len()); if (ap->point_to_point()) ap->set_endpoint(peer_addr); // // TODO: do we need to check the IPv6-specific flags, and ignore // anycast addresses? // XXX: what about TENTATIVE, DUPLICATED, DETACHED, DEPRECATED? break; } #endif // HAVE_IPV6 default: break; } } return (XORP_OK); } #endif // HAVE_GETIFADDRS xorp/fea/data_plane/ifconfig/ifconfig_media.hh0000664000076400007640000000236211421137511021532 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/data_plane/ifconfig/ifconfig_media.hh,v 1.6 2008/10/02 21:57:05 bms Exp $ #ifndef __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_MEDIA_HH__ #define __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_MEDIA_HH__ extern int ifconfig_media_get_link_status(const string& if_name, bool& no_carrier, uint64_t& baudrate, string& error_msg); #endif // __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_MEDIA_HH__ xorp/fea/data_plane/ifconfig/ifconfig_set_dummy.hh0000664000076400007640000002446711421137511022473 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/data_plane/ifconfig/ifconfig_set_dummy.hh,v 1.15 2008/10/02 21:57:07 bms Exp $ #ifndef __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_SET_DUMMY_HH__ #define __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_SET_DUMMY_HH__ #include "fea/ifconfig_set.hh" class IfConfigSetDummy : public IfConfigSet { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ IfConfigSetDummy(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~IfConfigSetDummy(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg); /** * Get a reference to the @ref IfTree instance. * * @return a reference to the @ref IfTree instance. */ const IfTree& iftree() const { return _iftree; } /** * Push the network interface configuration into the underlying system. * * Note that on return some of the interface tree configuration state * may be modified. * * @param iftree the interface tree configuration to push. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int push_config(const IfTree& iftree); private: /** * Determine if the interface's underlying provider implements discard * semantics natively, or if they are emulated through other means. * * @param i the interface item to inspect. * @return true if discard semantics are emulated. */ virtual bool is_discard_emulated(const IfTreeInterface& i) const; /** * Determine if the interface's underlying provider implements unreachable * semantics natively, or if they are emulated through other means. * * @param i the interface item to inspect. * @return true if unreachable semantics are emulated. */ virtual bool is_unreachable_emulated(const IfTreeInterface& i) const; /** * Start the configuration. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int config_begin(string& error_msg); /** * Complete the configuration. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int config_end(string& error_msg); /** * Begin the interface configuration. * * @param pulled_ifp pointer to the interface information pulled from * the system. * @param config_iface reference to the interface with the information * to configure. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int config_interface_begin(const IfTreeInterface* pulled_ifp, IfTreeInterface& config_iface, string& error_msg); /** * End the interface configuration. * * @param pulled_ifp pointer to the interface information pulled from * the system. * @param config_iface reference to the interface with the information * to configure. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int config_interface_end(const IfTreeInterface* pulled_ifp, const IfTreeInterface& config_iface, string& error_msg); /** * Begin the vif configuration. * * @param pulled_ifp pointer to the interface information pulled from * the system. * @param pulled_vifp pointer to the vif information pulled from * the system. * @param config_iface reference to the interface with the information * to configure. * @param config_vif reference to the vif with the information * to configure. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int config_vif_begin(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeInterface& config_iface, const IfTreeVif& config_vif, string& error_msg); /** * End the vif configuration. * * @param pulled_ifp pointer to the interface information pulled from * the system. * @param pulled_vifp pointer to the vif information pulled from * the system. * @param config_iface reference to the interface with the information * to configure. * @param config_vif reference to the vif with the information * to configure. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int config_vif_end(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeInterface& config_iface, const IfTreeVif& config_vif, string& error_msg); /** * Add IPv4 address information. * * If an entry for the same address already exists, is is overwritten * with the new information. * * @param pulled_ifp pointer to the interface information pulled from * the system. * @param pulled_vifp pointer to the vif information pulled from * the system. * @param pulled_addrp pointer to the address information pulled from * the system. * @param config_iface reference to the interface with the information * to configure. * @param config_vif reference to the vif with the information * to configure. * @param config_addr reference to the address with the information * to configure. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int config_add_address(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeAddr4* pulled_addrp, const IfTreeInterface& config_iface, const IfTreeVif& config_vif, const IfTreeAddr4& config_addr, string& error_msg); /** * Delete IPv4 address information. * * @param pulled_ifp pointer to the interface information pulled from * the system. * @param pulled_vifp pointer to the vif information pulled from * the system. * @param pulled_addrp pointer to the address information pulled from * the system. * @param config_iface reference to the interface with the information * to configure. * @param config_vif reference to the vif with the information * to configure. * @param config_addr reference to the address with the information * to configure. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int config_delete_address(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeAddr4* pulled_addrp, const IfTreeInterface& config_iface, const IfTreeVif& config_vif, const IfTreeAddr4& config_addr, string& error_msg); /** * Add IPv6 address information. * * If an entry for the same address already exists, is is overwritten * with the new information. * * @param pulled_ifp pointer to the interface information pulled from * the system. * @param pulled_vifp pointer to the vif information pulled from * the system. * @param pulled_addrp pointer to the address information pulled from * the system. * @param config_iface reference to the interface with the information * to configure. * @param config_vif reference to the vif with the information * to configure. * @param config_addr reference to the address with the information * to configure. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int config_add_address(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeAddr6* pulled_addrp, const IfTreeInterface& config_iface, const IfTreeVif& config_vif, const IfTreeAddr6& config_addr, string& error_msg); /** * Delete IPv6 address information. * * @param pulled_ifp pointer to the interface information pulled from * the system. * @param pulled_vifp pointer to the vif information pulled from * the system. * @param pulled_addrp pointer to the address information pulled from * the system. * @param config_iface reference to the interface with the information * to configure. * @param config_vif reference to the vif with the information * to configure. * @param config_addr reference to the address with the information * to configure. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int config_delete_address(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeAddr6* pulled_addrp, const IfTreeInterface& config_iface, const IfTreeVif& config_vif, const IfTreeAddr6& config_addr, string& error_msg); IfTree _iftree; // The configured IfTree }; #endif // __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_SET_DUMMY_HH__ xorp/fea/data_plane/ifconfig/ifconfig_set_click.hh0000664000076400007640000003044011540225524022415 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/data_plane/ifconfig/ifconfig_set_click.hh,v 1.13 2008/10/02 21:57:07 bms Exp $ #ifndef __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_SET_CLICK_HH__ #define __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_SET_CLICK_HH__ #include #ifdef XORP_USE_CLICK #include "fea/ifconfig_set.hh" #include "fea/data_plane/control_socket/click_socket.hh" class RunCommand; class IfConfigSetClick : public IfConfigSet, public ClickSocket { private: class ClickConfigGenerator; public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ IfConfigSetClick(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~IfConfigSetClick(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg); /** * Get a reference to the @ref IfTree instance. * * @return a reference to the @ref IfTree instance. */ const IfTree& iftree() const { return _iftree; } /** * Receive a signal the work of a Click configuration generator is done. * * @param click_config_generator a pointer to the @ref * IfConfigSetClick::ClickConfigGenerator instance that has completed * its work. * @param success if true, then the generator has successfully finished * its work, otherwise false. * @param error_msg the error message (if error). */ void click_config_generator_done( IfConfigSetClick::ClickConfigGenerator* click_config_generator, bool success, const string& error_msg); private: /** * Determine if the interface's underlying provider implements discard * semantics natively, or if they are emulated through other means. * * @param i the interface item to inspect. * @return true if discard semantics are emulated. */ virtual bool is_discard_emulated(const IfTreeInterface& i) const; /** * Determine if the interface's underlying provider implements unreachable * semantics natively, or if they are emulated through other means. * * @param i the interface item to inspect. * @return true if unreachable semantics are emulated. */ virtual bool is_unreachable_emulated(const IfTreeInterface& i) const; /** * Start the configuration. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int config_begin(string& error_msg); /** * Complete the configuration. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int config_end(string& error_msg); /** * Begin the interface configuration. * * @param pulled_ifp pointer to the interface information pulled from * the system. * @param config_iface reference to the interface with the information * to configure. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int config_interface_begin(const IfTreeInterface* pulled_ifp, IfTreeInterface& config_iface, string& error_msg); /** * End the interface configuration. * * @param pulled_ifp pointer to the interface information pulled from * the system. * @param config_iface reference to the interface with the information * to configure. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int config_interface_end(const IfTreeInterface* pulled_ifp, const IfTreeInterface& config_iface, string& error_msg); /** * Begin the vif configuration. * * @param pulled_ifp pointer to the interface information pulled from * the system. * @param pulled_vifp pointer to the vif information pulled from * the system. * @param config_iface reference to the interface with the information * to configure. * @param config_vif reference to the vif with the information * to configure. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int config_vif_begin(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeInterface& config_iface, const IfTreeVif& config_vif, string& error_msg); /** * End the vif configuration. * * @param pulled_ifp pointer to the interface information pulled from * the system. * @param pulled_vifp pointer to the vif information pulled from * the system. * @param config_iface reference to the interface with the information * to configure. * @param config_vif reference to the vif with the information * to configure. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int config_vif_end(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeInterface& config_iface, const IfTreeVif& config_vif, string& error_msg); /** * Add IPv4 address information. * * If an entry for the same address already exists, is is overwritten * with the new information. * * @param pulled_ifp pointer to the interface information pulled from * the system. * @param pulled_vifp pointer to the vif information pulled from * the system. * @param pulled_addrp pointer to the address information pulled from * the system. * @param config_iface reference to the interface with the information * to configure. * @param config_vif reference to the vif with the information * to configure. * @param config_addr reference to the address with the information * to configure. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int config_add_address(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeAddr4* pulled_addrp, const IfTreeInterface& config_iface, const IfTreeVif& config_vif, const IfTreeAddr4& config_addr, string& error_msg); /** * Delete IPv4 address information. * * @param pulled_ifp pointer to the interface information pulled from * the system. * @param pulled_vifp pointer to the vif information pulled from * the system. * @param pulled_addrp pointer to the address information pulled from * the system. * @param config_iface reference to the interface with the information * to configure. * @param config_vif reference to the vif with the information * to configure. * @param config_addr reference to the address with the information * to configure. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int config_delete_address(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeAddr4* pulled_addrp, const IfTreeInterface& config_iface, const IfTreeVif& config_vif, const IfTreeAddr4& config_addr, string& error_msg); /** * Add IPv6 address information. * * If an entry for the same address already exists, is is overwritten * with the new information. * * @param pulled_ifp pointer to the interface information pulled from * the system. * @param pulled_vifp pointer to the vif information pulled from * the system. * @param pulled_addrp pointer to the address information pulled from * the system. * @param config_iface reference to the interface with the information * to configure. * @param config_vif reference to the vif with the information * to configure. * @param config_addr reference to the address with the information * to configure. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int config_add_address(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeAddr6* pulled_addrp, const IfTreeInterface& config_iface, const IfTreeVif& config_vif, const IfTreeAddr6& config_addr, string& error_msg); /** * Delete IPv6 address information. * * @param pulled_ifp pointer to the interface information pulled from * the system. * @param pulled_vifp pointer to the vif information pulled from * the system. * @param pulled_addrp pointer to the address information pulled from * the system. * @param config_iface reference to the interface with the information * to configure. * @param config_vif reference to the vif with the information * to configure. * @param config_addr reference to the address with the information * to configure. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int config_delete_address(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeAddr6* pulled_addrp, const IfTreeInterface& config_iface, const IfTreeVif& config_vif, const IfTreeAddr6& config_addr, string& error_msg); int execute_click_config_generator(string& error_msg); void terminate_click_config_generator(); int write_generated_config(bool is_kernel_click, const string& kernel_config, bool is_user_click, const string& user_config, string& error_msg); string regenerate_xorp_iftree_config() const; string regenerate_xorp_fea_click_config() const; /** * Generate the next-hop to port mapping. * * @return the number of generated ports. */ int generate_nexthop_to_port_mapping(); ClickSocketReader _cs_reader; IfTree _iftree; class ClickConfigGenerator { public: ClickConfigGenerator(IfConfigSetClick& ifconfig_set_click, const string& command_name); ~ClickConfigGenerator(); int execute(const string& xorp_config, string& error_msg); const string& command_name() const { return _command_name; } const string& command_stdout() const { return _command_stdout; } private: void stdout_cb(RunCommand* run_command, const string& output); void stderr_cb(RunCommand* run_command, const string& output); void done_cb(RunCommand* run_command, bool success, const string& error_msg); IfConfigSetClick& _ifconfig_set_click; EventLoop& _eventloop; string _command_name; list _command_argument_list; RunCommand* _run_command; string _command_stdout; string _tmp_filename; }; ClickConfigGenerator* _kernel_click_config_generator; ClickConfigGenerator* _user_click_config_generator; bool _has_kernel_click_config; bool _has_user_click_config; string _generated_kernel_click_config; string _generated_user_click_config; }; #endif #endif // __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_SET_CLICK_HH__ xorp/fea/data_plane/ifconfig/ifconfig_get_dummy.cc0000664000076400007640000000517111421137511022434 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "fea/ifconfig.hh" #include "ifconfig_set_dummy.hh" #include "ifconfig_get_dummy.hh" // // Get information about network interfaces from the underlying system. // // The mechanism to obtain the information is Dummy (for testing purpose). // IfConfigGetDummy::IfConfigGetDummy(FeaDataPlaneManager& fea_data_plane_manager) : IfConfigGet(fea_data_plane_manager) { } IfConfigGetDummy::~IfConfigGetDummy() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the Dummy mechanism to get " "information about network interfaces from the underlying " "system: %s", error_msg.c_str()); } } int IfConfigGetDummy::start(string& error_msg) { UNUSED(error_msg); if (_is_running) return (XORP_OK); _is_running = true; return (XORP_OK); } int IfConfigGetDummy::stop(string& error_msg) { UNUSED(error_msg); if (! _is_running) return (XORP_OK); _is_running = false; return (XORP_OK); } int IfConfigGetDummy::pull_config(const IfTree* local_config, IfTree& iftree) { UNUSED(local_config); // // XXX: Get the tree from the IfConfigSetDummy instance. // IfConfigSet* ifconfig_set = fea_data_plane_manager().ifconfig_set(); if ((ifconfig_set == NULL) || (! ifconfig_set->is_running())) return (XORP_ERROR); IfConfigSetDummy* ifconfig_set_dummy; ifconfig_set_dummy = dynamic_cast(ifconfig_set); if (ifconfig_set_dummy == NULL) { // // XXX: The IfConfigSet plugin was probably changed to something else // which we don't know how to deal with. // return (XORP_ERROR); } iftree = ifconfig_set_dummy->iftree(); return (XORP_OK); } xorp/fea/data_plane/ifconfig/ifconfig_set_netlink_socket.hh0000664000076400007640000003214711540225524024352 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/data_plane/ifconfig/ifconfig_set_netlink_socket.hh,v 1.14 2008/12/18 02:06:57 abittau Exp $ #ifndef __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_SET_NETLINK_SOCKET_HH__ #define __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_SET_NETLINK_SOCKET_HH__ #include #ifdef HAVE_NETLINK_SOCKETS #include "fea/ifconfig_set.hh" #include "fea/data_plane/control_socket/netlink_socket.hh" class IfConfigSetNetlinkSocket : public IfConfigSet, public NetlinkSocket { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ IfConfigSetNetlinkSocket(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~IfConfigSetNetlinkSocket(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg); private: /** * Determine if the interface's underlying provider implements discard * semantics natively, or if they are emulated through other means. * * @param i the interface item to inspect. * @return true if discard semantics are emulated. */ virtual bool is_discard_emulated(const IfTreeInterface& i) const; /** * Determine if the interface's underlying provider implements unreachable * semantics natively, or if they are emulated through other means. * * @param i the interface item to inspect. * @return true if unreachable semantics are emulated. */ virtual bool is_unreachable_emulated(const IfTreeInterface& i) const; /** * Start the configuration. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int config_begin(string& error_msg); /** * Complete the configuration. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int config_end(string& error_msg); /** * Begin the interface configuration. * * @param pulled_ifp pointer to the interface information pulled from * the system. * @param config_iface reference to the interface with the information * to configure. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int config_interface_begin(const IfTreeInterface* pulled_ifp, IfTreeInterface& config_iface, string& error_msg); /** * End the interface configuration. * * @param pulled_ifp pointer to the interface information pulled from * the system. * @param config_iface reference to the interface with the information * to configure. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int config_interface_end(const IfTreeInterface* pulled_ifp, const IfTreeInterface& config_iface, string& error_msg); /** * Begin the vif configuration. * * @param pulled_ifp pointer to the interface information pulled from * the system. * @param pulled_vifp pointer to the vif information pulled from * the system. * @param config_iface reference to the interface with the information * to configure. * @param config_vif reference to the vif with the information * to configure. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int config_vif_begin(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeInterface& config_iface, const IfTreeVif& config_vif, string& error_msg); /** * End the vif configuration. * * @param pulled_ifp pointer to the interface information pulled from * the system. * @param pulled_vifp pointer to the vif information pulled from * the system. * @param config_iface reference to the interface with the information * to configure. * @param config_vif reference to the vif with the information * to configure. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int config_vif_end(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeInterface& config_iface, const IfTreeVif& config_vif, string& error_msg); /** * Add IPv4 address information. * * If an entry for the same address already exists, is is overwritten * with the new information. * * @param pulled_ifp pointer to the interface information pulled from * the system. * @param pulled_vifp pointer to the vif information pulled from * the system. * @param pulled_addrp pointer to the address information pulled from * the system. * @param config_iface reference to the interface with the information * to configure. * @param config_vif reference to the vif with the information * to configure. * @param config_addr reference to the address with the information * to configure. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int config_add_address(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeAddr4* pulled_addrp, const IfTreeInterface& config_iface, const IfTreeVif& config_vif, const IfTreeAddr4& config_addr, string& error_msg); /** * Delete IPv4 address information. * * @param pulled_ifp pointer to the interface information pulled from * the system. * @param pulled_vifp pointer to the vif information pulled from * the system. * @param pulled_addrp pointer to the address information pulled from * the system. * @param config_iface reference to the interface with the information * to configure. * @param config_vif reference to the vif with the information * to configure. * @param config_addr reference to the address with the information * to configure. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int config_delete_address(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeAddr4* pulled_addrp, const IfTreeInterface& config_iface, const IfTreeVif& config_vif, const IfTreeAddr4& config_addr, string& error_msg); /** * Add IPv6 address information. * * If an entry for the same address already exists, is is overwritten * with the new information. * * @param pulled_ifp pointer to the interface information pulled from * the system. * @param pulled_vifp pointer to the vif information pulled from * the system. * @param pulled_addrp pointer to the address information pulled from * the system. * @param config_iface reference to the interface with the information * to configure. * @param config_vif reference to the vif with the information * to configure. * @param config_addr reference to the address with the information * to configure. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int config_add_address(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeAddr6* pulled_addrp, const IfTreeInterface& config_iface, const IfTreeVif& config_vif, const IfTreeAddr6& config_addr, string& error_msg); /** * Delete IPv6 address information. * * @param pulled_ifp pointer to the interface information pulled from * the system. * @param pulled_vifp pointer to the vif information pulled from * the system. * @param pulled_addrp pointer to the address information pulled from * the system. * @param config_iface reference to the interface with the information * to configure. * @param config_vif reference to the vif with the information * to configure. * @param config_addr reference to the address with the information * to configure. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int config_delete_address(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeAddr6* pulled_addrp, const IfTreeInterface& config_iface, const IfTreeVif& config_vif, const IfTreeAddr6& config_addr, string& error_msg); /** * Set the interface status. * * @param ifname the name of the interface. * @param if_index the interface index. * @param interface_flags the interface flags. * @param is_enabled if true then enable the interface, otherwise * disable it. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_interface_status(const string& ifname, uint32_t if_index, uint32_t interface_flags, bool is_enabled, string& error_msg); /** * Busy waits until the interface status changes. * * @param ifp the interface to check. * @param is_enabled if true, will wait for interface to become enabled. */ void wait_interface_status(const IfTreeInterface* ifp, bool is_enabled); /** * Set the interface MAC address. * * @param ifname the name of the interface. * @param if_index the interface index. * @param mac the interface MAC address. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_interface_mac_address(const string& ifname, uint32_t if_index, const Mac& mac, string& error_msg); /** * Set the interface MTU. * * @param ifname the name of the interface. * @param if_index the interface index. * @param mac the interface MTU. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_interface_mtu(const string& ifname, uint32_t if_index, uint32_t mtu, string& error_msg); /** * Add an IPvX address to an interface/vif. * * @param ifname the name of the interface. * @param vifname the name of the vif. * @param if_index the interface/vif index. * @param addr the address to add. * @param prefix_len the prefix length. * @param is_broadcast if true this is a broadcast vif. * @param broadcast_addr the broadcast address if this is a * broadcast vif. * @param is_point_to_point if true, this is a point-to-point vif. * @param endpoint_addr the endpoint address if this is a * point-to-point vif. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_addr(const string& ifname, const string& vifname, uint32_t if_index, const IPvX& addr, uint32_t prefix_len, bool is_broadcast, const IPvX& broadcast_addr, bool is_point_to_point, const IPvX& endpoint_addr, string& error_msg); /** * Delete an IPvX address from an interface/vif. * * @param ifname the name of the interface. * @param vifname the name of the vif. * @param if_index the interface/vif index. * @param addr the address to delete. * @param prefix_len the prefix length. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int delete_addr(const string& ifname, const string& vifname, uint32_t if_index, const IPvX& addr, uint32_t prefix_len, string& error_msg); NetlinkSocketReader _ns_reader; }; #endif #endif // __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_SET_NETLINK_SOCKET_HH__ xorp/fea/data_plane/ifconfig/ifconfig_get_iphelper.hh0000664000076400007640000000431511540224222023120 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_GET_IPHELPER_HH__ #define __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_GET_IPHELPER_HH__ #include "fea/ifconfig_get.hh" class IfConfigGetIPHelper : public IfConfigGet { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ IfConfigGetIPHelper(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~IfConfigGetIPHelper(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg); /** * Pull the network interface information from the underlying system. * * @param iftree the IfTree storage to store the pulled information. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int pull_config(const IfTree* local_config, IfTree& iftree); private: int read_config(const IfTree* local_config, IfTree& iftree); }; #endif // __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_GET_IPHELPER_HH__ xorp/fea/data_plane/ifconfig/ifconfig_observer_iphelper.hh0000664000076400007640000000425211540224223024171 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/data_plane/ifconfig/ifconfig_observer_iphelper.hh,v 1.9 2008/10/02 21:57:06 bms Exp $ #ifndef __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_OBSERVER_IPHELPER_HH__ #define __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_OBSERVER_IPHELPER_HH__ #include "fea/ifconfig_observer.hh" class IfConfigObserverIPHelper : public IfConfigObserver { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ IfConfigObserverIPHelper(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~IfConfigObserverIPHelper(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg); /** * Receive data from the underlying system. * * @param buffer the buffer with the received data. */ virtual void receive_data(const vector& buffer); private: }; #endif // __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_OBSERVER_IPHELPER_HH__ xorp/fea/data_plane/ifconfig/ifconfig_property_linux.cc0000664000076400007640000000362411540225524023552 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2007-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include #ifdef HOST_OS_LINUX #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libcomm/comm_api.h" #include "fea/ifconfig.hh" #include "ifconfig_property_linux.hh" // // Get information about the data plane property. // // The mechanism to obtain the information is for Linux systems. // IfConfigPropertyLinux::IfConfigPropertyLinux(FeaDataPlaneManager& fea_data_plane_manager) : IfConfigProperty(fea_data_plane_manager) { } IfConfigPropertyLinux::~IfConfigPropertyLinux() { } bool IfConfigPropertyLinux::test_have_ipv4() const { XorpFd s = comm_sock_open(AF_INET, SOCK_DGRAM, 0, 0); if (! s.is_valid()) return (false); comm_close(s); return (true); } bool IfConfigPropertyLinux::test_have_ipv6() const { #ifndef HAVE_IPV6 return (false); #else XorpFd s = comm_sock_open(AF_INET6, SOCK_DGRAM, 0, 0); if (! s.is_valid()) return (false); comm_close(s); return (true); #endif // HAVE_IPV6 } #endif // HOST_OS_LINUX xorp/fea/data_plane/ifconfig/ifconfig_set_ioctl.hh0000664000076400007640000003356211540225524022452 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/data_plane/ifconfig/ifconfig_set_ioctl.hh,v 1.13 2008/10/02 21:57:07 bms Exp $ #ifndef __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_SET_IOCTL_HH__ #define __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_SET_IOCTL_HH__ #include #if defined(HAVE_IOCTL_SIOCGIFCONF) && !defined(HAVE_NETLINK_SOCKETS) #include "fea/ifconfig_set.hh" class IfConfigSetIoctl : public IfConfigSet { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ IfConfigSetIoctl(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~IfConfigSetIoctl(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg); private: /** * Determine if the interface's underlying provider implements discard * semantics natively, or if they are emulated through other means. * * @param i the interface item to inspect. * @return true if discard semantics are emulated. */ virtual bool is_discard_emulated(const IfTreeInterface& i) const; /** * Determine if the interface's underlying provider implements unreachable * semantics natively, or if they are emulated through other means. * * @param i the interface item to inspect. * @return true if unreachable semantics are emulated. */ virtual bool is_unreachable_emulated(const IfTreeInterface& i) const; /** * Start the configuration. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int config_begin(string& error_msg); /** * Complete the configuration. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int config_end(string& error_msg); /** * Begin the interface configuration. * * @param pulled_ifp pointer to the interface information pulled from * the system. * @param config_iface reference to the interface with the information * to configure. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int config_interface_begin(const IfTreeInterface* pulled_ifp, IfTreeInterface& config_iface, string& error_msg); /** * End the interface configuration. * * @param pulled_ifp pointer to the interface information pulled from * the system. * @param config_iface reference to the interface with the information * to configure. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int config_interface_end(const IfTreeInterface* pulled_ifp, const IfTreeInterface& config_iface, string& error_msg); /** * Begin the vif configuration. * * @param pulled_ifp pointer to the interface information pulled from * the system. * @param pulled_vifp pointer to the vif information pulled from * the system. * @param config_iface reference to the interface with the information * to configure. * @param config_vif reference to the vif with the information * to configure. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int config_vif_begin(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeInterface& config_iface, const IfTreeVif& config_vif, string& error_msg); /** * End the vif configuration. * * @param pulled_ifp pointer to the interface information pulled from * the system. * @param pulled_vifp pointer to the vif information pulled from * the system. * @param config_iface reference to the interface with the information * to configure. * @param config_vif reference to the vif with the information * to configure. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int config_vif_end(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeInterface& config_iface, const IfTreeVif& config_vif, string& error_msg); /** * Add IPv4 address information. * * If an entry for the same address already exists, is is overwritten * with the new information. * * @param pulled_ifp pointer to the interface information pulled from * the system. * @param pulled_vifp pointer to the vif information pulled from * the system. * @param pulled_addrp pointer to the address information pulled from * the system. * @param config_iface reference to the interface with the information * to configure. * @param config_vif reference to the vif with the information * to configure. * @param config_addr reference to the address with the information * to configure. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int config_add_address(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeAddr4* pulled_addrp, const IfTreeInterface& config_iface, const IfTreeVif& config_vif, const IfTreeAddr4& config_addr, string& error_msg); /** * Delete IPv4 address information. * * @param pulled_ifp pointer to the interface information pulled from * the system. * @param pulled_vifp pointer to the vif information pulled from * the system. * @param pulled_addrp pointer to the address information pulled from * the system. * @param config_iface reference to the interface with the information * to configure. * @param config_vif reference to the vif with the information * to configure. * @param config_addr reference to the address with the information * to configure. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int config_delete_address(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeAddr4* pulled_addrp, const IfTreeInterface& config_iface, const IfTreeVif& config_vif, const IfTreeAddr4& config_addr, string& error_msg); /** * Add IPv6 address information. * * If an entry for the same address already exists, is is overwritten * with the new information. * * @param pulled_ifp pointer to the interface information pulled from * the system. * @param pulled_vifp pointer to the vif information pulled from * the system. * @param pulled_addrp pointer to the address information pulled from * the system. * @param config_iface reference to the interface with the information * to configure. * @param config_vif reference to the vif with the information * to configure. * @param config_addr reference to the address with the information * to configure. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int config_add_address(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeAddr6* pulled_addrp, const IfTreeInterface& config_iface, const IfTreeVif& config_vif, const IfTreeAddr6& config_addr, string& error_msg); /** * Delete IPv6 address information. * * @param pulled_ifp pointer to the interface information pulled from * the system. * @param pulled_vifp pointer to the vif information pulled from * the system. * @param pulled_addrp pointer to the address information pulled from * the system. * @param config_iface reference to the interface with the information * to configure. * @param config_vif reference to the vif with the information * to configure. * @param config_addr reference to the address with the information * to configure. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int config_delete_address(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeAddr6* pulled_addrp, const IfTreeInterface& config_iface, const IfTreeVif& config_vif, const IfTreeAddr6& config_addr, string& error_msg); /** * Set the interface status. * * @param ifname the name of the interface. * @param if_index the interface index. * @param interface_flags the interface flags. * @param is_enabled if true then enable the interface, otherwise * disable it. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_interface_status(const string& ifname, uint32_t if_index, uint32_t interface_flags, bool is_enabled, string& error_msg); /** * Set the interface MAC address. * * @param ifname the name of the interface. * @param mac the interface MAC address. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_interface_mac_address(const string& ifname, const Mac& mac, string& error_msg); /** * Set the interface MTU. * * @param ifname the name of the interface. * @param mac the interface MTU. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_interface_mtu(const string& ifname, uint32_t mtu, string& error_msg); /** * Add an IPv4 address to an interface/vif. * * @param ifname the name of the interface. * @param vifname the name of the vif. * @param if_index the interface/vif index. * @param addr the address to add. * @param prefix_len the prefix length. * @param is_broadcast if true this is a broadcast vif. * @param broadcast_addr the broadcast address if this is a * broadcast vif. * @param is_point_to_point if true, this is a point-to-point vif. * @param endpoint_addr the endpoint address if this is a * point-to-point vif. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_addr(const string& ifname, const string& vifname, uint32_t if_index, const IPv4& addr, uint32_t prefix_len, bool is_broadcast, const IPv4& broadcast_addr, bool is_point_to_point, const IPv4& endpoint_addr, string& error_msg); /** * Delete an IPv4 address from an interface/vif. * * @param ifname the name of the interface. * @param vifname the name of the vif. * @param if_index the interface/vif index. * @param addr the address to delete. * @param prefix_len the prefix length. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int delete_addr(const string& ifname, const string& vifname, uint32_t if_index, const IPv4& addr, uint32_t prefix_len, string& error_msg); /** * Add an IPv6 address to an interface/vif. * * @param ifname the name of the interface. * @param vifname the name of the vif. * @param if_index the interface/vif index. * @param addr the address to add. * @param prefix_len the prefix length. * @param is_point_to_point if true, this is a point-to-point vif. * @param endpoint_addr the endpoint address if this is a * point-to-point vif. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_addr(const string& ifname, const string& vifname, uint32_t if_index, const IPv6& addr, uint32_t prefix_len, bool is_point_to_point, const IPv6& endpoint_addr, string& error_msg); /** * Delete an IPv6 address from an interface/vif. * * @param ifname the name of the interface. * @param vifname the name of the vif. * @param if_index the interface/vif index. * @param addr the address to delete. * @param prefix_len the prefix length. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int delete_addr(const string& ifname, const string& vifname, uint32_t if_index, const IPv6& addr, uint32_t prefix_len, string& error_msg); int _s4; int _s6; }; #endif #endif // __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_SET_IOCTL_HH__ xorp/fea/data_plane/ifconfig/ifconfig_observer_dummy.hh0000664000076400007640000000421711421137511023516 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/data_plane/ifconfig/ifconfig_observer_dummy.hh,v 1.9 2008/10/02 21:57:05 bms Exp $ #ifndef __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_OBSERVER_DUMMY_HH__ #define __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_OBSERVER_DUMMY_HH__ #include "fea/ifconfig_observer.hh" class IfConfigObserverDummy : public IfConfigObserver { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ IfConfigObserverDummy(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~IfConfigObserverDummy(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg); /** * Receive data from the underlying system. * * @param buffer the buffer with the received data. */ virtual void receive_data(const vector& buffer); private: }; #endif // __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_OBSERVER_DUMMY_HH__ xorp/fea/data_plane/ifconfig/ifconfig_get_sysctl.hh0000664000076400007640000000563411421137511022640 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/data_plane/ifconfig/ifconfig_get_sysctl.hh,v 1.10 2008/10/02 21:57:05 bms Exp $ #ifndef __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_GET_SYSCTL_HH__ #define __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_GET_SYSCTL_HH__ #include "fea/ifconfig_get.hh" class IfConfigGetSysctl : public IfConfigGet { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ IfConfigGetSysctl(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~IfConfigGetSysctl(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg); /** * Pull the network interface information from the underlying system. * * @param iftree the IfTree storage to store the pulled information. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int pull_config(const IfTree* local_config, IfTree& iftree); /** * Parse information about network interface configuration change from * the underlying system. * * The information to parse is in RTM format * (e.g., obtained by routing sockets or by sysctl(3) mechanism). * * @param ifconfig the IfConfig instance. * @param iftree the IfTree storage to store the parsed information. * @param buffer the buffer with the data to parse. * @return XORP_OK on success, otherwise XORP_ERROR. * @see IfTree. */ static int parse_buffer_routing_socket(IfConfig& ifconfig, IfTree& iftree, const vector& buffer); private: int read_config(IfTree& iftree); static string iff_flags(uint32_t flags); }; #endif // __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_GET_SYSCTL_HH__ xorp/fea/data_plane/ifconfig/ifconfig_property_solaris.hh0000664000076400007640000000403111540225524024072 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2007-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/data_plane/ifconfig/ifconfig_property_solaris.hh,v 1.5 2008/10/02 21:57:07 bms Exp $ #ifndef __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_PROPERTY_SOLARIS_HH__ #define __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_PROPERTY_SOLARIS_HH__ #include #ifdef HOST_OS_SOLARIS #include "fea/ifconfig_property.hh" class IfConfigPropertySolaris : public IfConfigProperty { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ IfConfigPropertySolaris(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~IfConfigPropertySolaris(); private: /** * Test whether the underlying system supports IPv4. * * @return true if the underlying system supports IPv4, otherwise false. */ virtual bool test_have_ipv4() const; /** * Test whether the underlying system supports IPv6. * * @return true if the underlying system supports IPv6, otherwise false. */ virtual bool test_have_ipv6() const; }; #endif #endif // __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_PROPERTY_SOLARIS_HH__ xorp/fea/data_plane/ifconfig/ifconfig_vlan_get_linux.hh0000664000076400007640000000465711540224223023501 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2007-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/data_plane/ifconfig/ifconfig_vlan_get_linux.hh,v 1.5 2008/10/02 21:57:09 bms Exp $ #ifndef __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_VLAN_GET_LINUX_HH__ #define __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_VLAN_GET_LINUX_HH__ #include "fea/ifconfig_vlan_get.hh" class IfConfigVlanGetLinux : public IfConfigVlanGet { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ IfConfigVlanGetLinux(FeaDataPlaneManager& fea_data_plane_manager, bool is_dummy); /** * Virtual destructor. */ virtual ~IfConfigVlanGetLinux(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg); /** * Pull the VLAN network interface information from the underlying system. * * The VLAN information is added to the existing state in the iftree. * * @param iftree the IfTree storage to store the pulled information. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int pull_config(IfTree& iftree, bool& modified); private: int read_config(IfTree& iftree, bool& modified); bool _is_dummy; int _s4; }; #endif // __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_VLAN_GET_LINUX_HH__ xorp/fea/data_plane/ifconfig/ifconfig_set_click.cc0000664000076400007640000007211711703345405022414 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2012 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include #ifdef XORP_USE_CLICK #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvx.hh" #include "libxorp/run_command.hh" #include "libxorp/utils.hh" #include "fea/ifconfig.hh" #include "fea/nexthop_port_mapper.hh" #include "ifconfig_set_click.hh" #ifdef HOST_OS_WINDOWS #define unlink(x) _unlink(x) #endif // // Set information about network interfaces configuration with the // underlying system. // // The mechanism to set the information is Click: // http://www.read.cs.ucla.edu/click/ // IfConfigSetClick::IfConfigSetClick(FeaDataPlaneManager& fea_data_plane_manager) : IfConfigSet(fea_data_plane_manager), ClickSocket(fea_data_plane_manager.eventloop()), _cs_reader(*(ClickSocket *)this), _iftree("click-set"), _kernel_click_config_generator(NULL), _user_click_config_generator(NULL), _has_kernel_click_config(false), _has_user_click_config(false) { } IfConfigSetClick::~IfConfigSetClick() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the Click mechanism to set " "information about network interfaces into the underlying " "system: %s", error_msg.c_str()); } } int IfConfigSetClick::start(string& error_msg) { if (! ClickSocket::is_enabled()) return (XORP_OK); if (_is_running) return (XORP_OK); if (ClickSocket::start(error_msg) != XORP_OK) return (XORP_ERROR); _is_running = true; // // XXX: Push the existing merged configuration // push_config(ifconfig().merged_config()); return (XORP_OK); } int IfConfigSetClick::stop(string& error_msg) { int ret_value = XORP_OK; if (! _is_running) return (XORP_OK); terminate_click_config_generator(); ret_value = ClickSocket::stop(error_msg); _is_running = false; return (ret_value); } bool IfConfigSetClick::is_discard_emulated(const IfTreeInterface& i) const { UNUSED(i); return (false); // TODO: return correct value } bool IfConfigSetClick::is_unreachable_emulated(const IfTreeInterface& i) const { UNUSED(i); return (false); // TODO: return correct value } int IfConfigSetClick::config_begin(string& error_msg) { UNUSED(error_msg); return (XORP_OK); } int IfConfigSetClick::config_end(string& error_msg) { // // Trigger the generation of the configuration // if (execute_click_config_generator(error_msg) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int IfConfigSetClick::config_interface_begin(const IfTreeInterface* pulled_ifp, IfTreeInterface& config_iface, string& error_msg) { IfTreeInterface* ifp; UNUSED(pulled_ifp); // // Find or add the interface // ifp = _iftree.find_interface(config_iface.ifname()); if (ifp == NULL) { if (_iftree.add_interface(config_iface.ifname()) != XORP_OK) { error_msg = c_format("Cannot add interface '%s'", config_iface.ifname().c_str()); return (XORP_ERROR); } ifp = _iftree.find_interface(config_iface.ifname()); XLOG_ASSERT(ifp != NULL); } // // Update the interface state // // Note that we postpone the setting of the "enabled" and interface flags // for the end of the vif configuration. // ifp->set_pif_index(config_iface.pif_index()); ifp->set_discard(config_iface.discard()); ifp->set_unreachable(config_iface.unreachable()); ifp->set_management(config_iface.management()); ifp->set_mtu(config_iface.mtu()); ifp->set_mac(config_iface.mac()); ifp->set_no_carrier(config_iface.no_carrier()); ifp->set_baudrate(config_iface.baudrate()); return (XORP_OK); } int IfConfigSetClick::config_interface_end(const IfTreeInterface* pulled_ifp, const IfTreeInterface& config_iface, string& error_msg) { IfTreeInterface* ifp; bool is_deleted = false; UNUSED(pulled_ifp); if (config_iface.is_marked(IfTreeItem::DELETED)) { is_deleted = true; } // // Find the interface // ifp = _iftree.find_interface(config_iface.ifname()); if (ifp == NULL) { error_msg = c_format("Cannot configure interface '%s': " "no such interface in the interface tree", config_iface.ifname().c_str()); return (XORP_ERROR); } // // Delete the interface if marked for deletion // if (is_deleted) { _iftree.remove_interface(config_iface.ifname()); return (XORP_OK); } // // Update the remaining interface state // ifp->set_interface_flags(config_iface.interface_flags()); ifp->set_enabled(config_iface.enabled()); return (XORP_OK); } int IfConfigSetClick::config_vif_begin(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeInterface& config_iface, const IfTreeVif& config_vif, string& error_msg) { IfTreeInterface* ifp; IfTreeVif* vifp; UNUSED(pulled_ifp); // // Find the interface // ifp = _iftree.find_interface(config_iface.ifname()); if (ifp == NULL) { error_msg = c_format("Cannot add interface '%s' vif '%s': " "no such interface in the interface tree", config_iface.ifname().c_str(), config_vif.vifname().c_str()); return (XORP_ERROR); } // // Find or add the vif // vifp = ifp->find_vif(config_vif.vifname()); if (vifp == NULL) { if (ifp->add_vif(config_vif.vifname()) != XORP_OK) { error_msg = c_format("Cannot add interface '%s' vif '%s'", config_iface.ifname().c_str(), config_vif.vifname().c_str()); return (XORP_ERROR); } vifp = ifp->find_vif(config_vif.vifname()); XLOG_ASSERT(vifp != NULL); } // // Update the vif state // // Note that we postpone the setting of the "enabled" flag for the end // of the vif configuration. // if (pulled_vifp != NULL) { vifp->set_pif_index(pulled_vifp->pif_index()); vifp->set_broadcast(pulled_vifp->broadcast()); vifp->set_loopback(pulled_vifp->loopback()); vifp->set_point_to_point(pulled_vifp->point_to_point()); vifp->set_multicast(pulled_vifp->multicast()); } return (XORP_OK); } int IfConfigSetClick::config_vif_end(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeInterface& config_iface, const IfTreeVif& config_vif, string& error_msg) { IfTreeInterface* ifp; IfTreeVif* vifp; bool is_deleted = false; UNUSED(pulled_ifp); UNUSED(pulled_vifp); if (config_vif.is_marked(IfTreeItem::DELETED)) { is_deleted = true; } // // Find the interface // ifp = _iftree.find_interface(config_iface.ifname()); if (ifp == NULL) { error_msg = c_format("Cannot configure interface '%s' vif '%s': " "no such interface in the interface tree", config_iface.ifname().c_str(), config_vif.vifname().c_str()); return (XORP_ERROR); } // // Find the vif // vifp = ifp->find_vif(config_vif.vifname()); if (vifp == NULL) { error_msg = c_format("Cannot configure interface '%s' vif '%s': " "no such vif in the interface tree", config_iface.ifname().c_str(), config_vif.vifname().c_str()); return (XORP_ERROR); } // // Delete the vif if marked for deletion // if (is_deleted) { ifp->remove_vif(config_vif.vifname()); ifconfig().nexthop_port_mapper().delete_interface( config_iface.ifname(), config_vif.vifname()); return (XORP_OK); } // // Update the remaining vif state // vifp->set_vif_flags(config_vif.vif_flags()); vifp->set_enabled(config_vif.enabled()); return (XORP_OK); } int IfConfigSetClick::config_add_address(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeAddr4* pulled_addrp, const IfTreeInterface& config_iface, const IfTreeVif& config_vif, const IfTreeAddr4& config_addr, string& error_msg) { IfTreeVif* vifp; IfTreeAddr4* ap; UNUSED(pulled_ifp); UNUSED(pulled_vifp); UNUSED(pulled_addrp); vifp = _iftree.find_vif(config_iface.ifname(), config_vif.vifname()); if (vifp == NULL) { error_msg = c_format("Cannot add address to interface '%s' vif '%s': " "no such vif in the interface tree", config_iface.ifname().c_str(), config_vif.vifname().c_str()); return (XORP_ERROR); } // // Find or add the address // ap = vifp->find_addr(config_addr.addr()); if (ap == NULL) { if (vifp->add_addr(config_addr.addr()) != XORP_OK) { error_msg = c_format("Cannot add address '%s' " "to interface '%s' vif '%s'", config_addr.addr().str().c_str(), config_iface.ifname().c_str(), config_vif.vifname().c_str()); return (XORP_ERROR); } ap = vifp->find_addr(config_addr.addr()); XLOG_ASSERT(ap != NULL); } // // Update the address state // ap->set_broadcast(config_addr.broadcast()); ap->set_loopback(config_addr.loopback()); ap->set_point_to_point(config_addr.point_to_point()); ap->set_multicast(config_addr.multicast()); if (ap->broadcast()) ap->set_bcast(config_addr.bcast()); if (ap->point_to_point()) ap->set_endpoint(config_addr.endpoint()); ap->set_prefix_len(config_addr.prefix_len()); ap->set_enabled(config_addr.enabled()); return (XORP_OK); } int IfConfigSetClick::config_delete_address(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeAddr4* pulled_addrp, const IfTreeInterface& config_iface, const IfTreeVif& config_vif, const IfTreeAddr4& config_addr, string& error_msg) { IfTreeVif* vifp; IfTreeAddr4* ap; UNUSED(pulled_ifp); UNUSED(pulled_vifp); UNUSED(pulled_addrp); vifp = _iftree.find_vif(config_iface.ifname(), config_vif.vifname()); if (vifp == NULL) { error_msg = c_format("Cannot delete address on interface '%s' " "vif '%s': no such vif in the interface tree", config_iface.ifname().c_str(), config_vif.vifname().c_str()); return (XORP_ERROR); } // // Delete the address // const IPv4& addr = config_addr.addr(); ap = vifp->find_addr(addr); if (ap == NULL) { error_msg = c_format("Cannot delete address '%s' " "on interface '%s' vif '%s': " "no such address", addr.str().c_str(), config_iface.ifname().c_str(), config_vif.vifname().c_str()); return (XORP_ERROR); } vifp->remove_addr(addr); ifconfig().nexthop_port_mapper().delete_ipv4(addr); return (XORP_OK); } int IfConfigSetClick::config_add_address(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeAddr6* pulled_addrp, const IfTreeInterface& config_iface, const IfTreeVif& config_vif, const IfTreeAddr6& config_addr, string& error_msg) { IfTreeVif* vifp; IfTreeAddr6* ap; UNUSED(pulled_ifp); UNUSED(pulled_vifp); UNUSED(pulled_addrp); vifp = _iftree.find_vif(config_iface.ifname(), config_vif.vifname()); if (vifp == NULL) { error_msg = c_format("Cannot add address to interface '%s' vif '%s': " "no such vif in the interface tree", config_iface.ifname().c_str(), config_vif.vifname().c_str()); return (XORP_ERROR); } // // Find or add the address // ap = vifp->find_addr(config_addr.addr()); if (ap == NULL) { if (vifp->add_addr(config_addr.addr()) != XORP_OK) { error_msg = c_format("Cannot add address '%s' " "to interface '%s' vif '%s'", config_addr.addr().str().c_str(), config_iface.ifname().c_str(), config_vif.vifname().c_str()); return (XORP_ERROR); } ap = vifp->find_addr(config_addr.addr()); XLOG_ASSERT(ap != NULL); } // // Update the address state // ap->set_loopback(config_addr.loopback()); ap->set_point_to_point(config_addr.point_to_point()); ap->set_multicast(config_addr.multicast()); if (ap->point_to_point()) ap->set_endpoint(config_addr.endpoint()); ap->set_prefix_len(config_addr.prefix_len()); ap->set_enabled(config_addr.enabled()); return (XORP_OK); } int IfConfigSetClick::config_delete_address(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeAddr6* pulled_addrp, const IfTreeInterface& config_iface, const IfTreeVif& config_vif, const IfTreeAddr6& config_addr, string& error_msg) { IfTreeVif* vifp; IfTreeAddr6* ap; UNUSED(pulled_ifp); UNUSED(pulled_vifp); UNUSED(pulled_addrp); vifp = _iftree.find_vif(config_iface.ifname(), config_vif.vifname()); if (vifp == NULL) { error_msg = c_format("Cannot delete address on interface '%s' " "vif '%s': no such vif in the interface tree", config_iface.ifname().c_str(), config_vif.vifname().c_str()); return (XORP_ERROR); } // // Delete the address // const IPv6& addr = config_addr.addr(); ap = vifp->find_addr(addr); if (ap == NULL) { error_msg = c_format("Cannot delete address '%s' " "on interface '%s' vif '%s': " "no such address", addr.str().c_str(), config_iface.ifname().c_str(), config_vif.vifname().c_str()); return (XORP_ERROR); } vifp->remove_addr(addr); ifconfig().nexthop_port_mapper().delete_ipv6(addr); return (XORP_OK); } int IfConfigSetClick::execute_click_config_generator(string& error_msg) { string kernel_generator_file = ClickSocket::kernel_click_config_generator_file(); string user_generator_file = ClickSocket::user_click_config_generator_file(); string arguments; // // Test the Click configuration generator filenames are valid // if (ClickSocket::is_kernel_click()) { if (kernel_generator_file.empty()) { error_msg = c_format( "Cannot execute the kernel-level Click configuration " "generator: " "empty generator file name"); return (XORP_ERROR); } } if (ClickSocket::is_user_click()) { if (user_generator_file.empty()) { error_msg = c_format( "Cannot execute the user-level Click configuration " "generator: " "empty generator file name"); return (XORP_ERROR); } } // // Re-generate the XORP fea/click configuration and // pass it to the Click config generator. // // TODO: XXX: the internal re-generation is a temporary solution // that should go away after the xorpsh takes over the functionality // of calling the Click config generator. // string xorp_config = regenerate_xorp_iftree_config(); xorp_config += regenerate_xorp_fea_click_config(); // // XXX: kill any previously running instances // if (_kernel_click_config_generator != NULL) { delete _kernel_click_config_generator; _kernel_click_config_generator = NULL; } if (_user_click_config_generator != NULL) { delete _user_click_config_generator; _user_click_config_generator = NULL; } // // Cleanup // _has_kernel_click_config = false; _has_user_click_config = false; _generated_kernel_click_config.erase(); _generated_user_click_config.erase(); // // Execute the Click configuration generators // if (ClickSocket::is_kernel_click()) { _kernel_click_config_generator = new ClickConfigGenerator( *this, kernel_generator_file); if (_kernel_click_config_generator->execute(xorp_config, error_msg) != XORP_OK) { delete _kernel_click_config_generator; _kernel_click_config_generator = NULL; return (XORP_ERROR); } } if (ClickSocket::is_user_click()) { _user_click_config_generator = new ClickConfigGenerator( *this, user_generator_file); if (_user_click_config_generator->execute(xorp_config, error_msg) != XORP_OK) { delete _user_click_config_generator; _user_click_config_generator = NULL; return (XORP_ERROR); } } return (XORP_OK); } void IfConfigSetClick::terminate_click_config_generator() { if (_kernel_click_config_generator != NULL) { delete _kernel_click_config_generator; _kernel_click_config_generator = NULL; } if (_user_click_config_generator != NULL) { delete _user_click_config_generator; _user_click_config_generator = NULL; } _has_kernel_click_config = false; _has_user_click_config = false; _generated_kernel_click_config.erase(); _generated_user_click_config.erase(); } void IfConfigSetClick::click_config_generator_done( IfConfigSetClick::ClickConfigGenerator* click_config_generator, bool success, const string& error_msg) { // Check for errors XLOG_ASSERT((click_config_generator == _kernel_click_config_generator) || (click_config_generator == _user_click_config_generator)); if (! success) { XLOG_ERROR("External Click configuration generator (%s) failed: %s", click_config_generator->command_name().c_str(), error_msg.c_str()); } string command_stdout = click_config_generator->command_stdout(); if (click_config_generator == _kernel_click_config_generator) { if (success) { _has_kernel_click_config = true; _generated_kernel_click_config = command_stdout; } _kernel_click_config_generator = NULL; } if (click_config_generator == _user_click_config_generator) { if (success) { _generated_user_click_config = command_stdout; _has_user_click_config = true; } _user_click_config_generator = NULL; } delete click_config_generator; if (! success) return; if ((_kernel_click_config_generator != NULL) || (_user_click_config_generator != NULL)) { // XXX: we are still waiting for some output return; } string write_error_msg; if (write_generated_config(_has_kernel_click_config, _generated_kernel_click_config, _has_user_click_config, _generated_user_click_config, write_error_msg) != XORP_OK) { XLOG_ERROR("Failed to write the Click configuration: %s", write_error_msg.c_str()); } } int IfConfigSetClick::write_generated_config(bool has_kernel_config, const string& kernel_config, bool has_user_config, const string& user_config, string& error_msg) { string element = ""; string handler = "hotconfig"; if (ClickSocket::write_config(element, handler, has_kernel_config, kernel_config, has_user_config, user_config, error_msg) != XORP_OK) { return (XORP_ERROR); } // // Generate the new port mapping // generate_nexthop_to_port_mapping(); // // Notify the NextHopPortMapper observers about any port mapping changes // ifconfig().nexthop_port_mapper().notify_observers(); return (XORP_OK); } string IfConfigSetClick::regenerate_xorp_iftree_config() const { string config, preamble; IfTree::IfMap::const_iterator ii; IfTreeInterface::VifMap::const_iterator vi; IfTreeVif::IPv4Map::const_iterator ai4; IfTreeVif::IPv6Map::const_iterator ai6; // // Configuration section: "interfaces{}" // preamble = ""; config += preamble + c_format("interfaces {\n"); for (ii = _iftree.interfaces().begin(); ii != _iftree.interfaces().end(); ++ii) { const IfTreeInterface& fi = *(ii->second); preamble = " "; config += preamble + c_format("interface %s {\n", fi.ifname().c_str()); preamble = "\t"; config += preamble + c_format("disable: %s\n", bool_c_str(! fi.enabled())); config += preamble + c_format("discard: %s\n", bool_c_str(fi.discard())); config += preamble + c_format("unreachable: %s\n", bool_c_str(fi.unreachable())); config += preamble + c_format("management: %s\n", bool_c_str(fi.management())); config += preamble + c_format("mac: %s\n", fi.mac().str().c_str()); config += preamble + c_format("mtu: %u\n", XORP_UINT_CAST(fi.mtu())); for (vi = fi.vifs().begin(); vi != fi.vifs().end(); ++vi) { const IfTreeVif& fv = *(vi->second); preamble = "\t"; config += preamble + c_format("vif %s {\n", fv.vifname().c_str()); preamble = "\t "; config += preamble + c_format("disable: %s\n", bool_c_str(! fv.enabled())); for (ai4 = fv.ipv4addrs().begin(); ai4 != fv.ipv4addrs().end(); ++ai4) { const IfTreeAddr4& fa4 = *(ai4->second); preamble = "\t "; config += preamble + c_format("address %s {\n", fa4.addr().str().c_str()); preamble = "\t\t"; config += preamble + c_format("prefix-length: %u\n", XORP_UINT_CAST(fa4.prefix_len())); if (fa4.broadcast()) { config += preamble + c_format("broadcast: %s\n", fa4.bcast().str().c_str()); } if (fa4.point_to_point()) { config += preamble + c_format("destination: %s\n", fa4.endpoint().str().c_str()); } config += preamble + c_format("multicast-capable: %s\n", bool_c_str(fa4.multicast())); config += preamble + c_format("point-to-point: %s\n", bool_c_str(fa4.point_to_point())); config += preamble + c_format("loopback: %s\n", bool_c_str(fa4.loopback())); config += preamble + c_format("disable: %s\n", bool_c_str(! fa4.enabled())); preamble = "\t "; config += preamble + c_format("}\n"); } for (ai6 = fv.ipv6addrs().begin(); ai6 != fv.ipv6addrs().end(); ++ai6) { const IfTreeAddr6& fa6 = *(ai6->second); preamble = "\t "; config += preamble + c_format("address %s {\n", fa6.addr().str().c_str()); preamble = "\t\t"; config += preamble + c_format("prefix-length: %u\n", XORP_UINT_CAST(fa6.prefix_len())); if (fa6.point_to_point()) { config += preamble + c_format("destination: %s\n", fa6.endpoint().str().c_str()); } config += preamble + c_format("multicast-capable: %s\n", bool_c_str(fa6.multicast())); config += preamble + c_format("point-to-point: %s\n", bool_c_str(fa6.point_to_point())); config += preamble + c_format("loopback: %s\n", bool_c_str(fa6.loopback())); config += preamble + c_format("disable: %s\n", bool_c_str(! fa6.enabled())); preamble = "\t "; config += preamble + c_format("}\n"); } preamble = "\t"; config += preamble + c_format("}\n"); } preamble = " "; config += preamble + c_format("}\n"); } preamble = ""; config += preamble + c_format("}\n"); return (config); } string IfConfigSetClick::regenerate_xorp_fea_click_config() const { string config, preamble; // // Configuration section: "fea{}" // // XXX: note that we write only a partial configuration: those // that is related to enabling/disabling kernel or user-level Click. // preamble = ""; config += preamble + c_format("fea {\n"); do { preamble = " "; config += preamble + c_format("click {\n"); do { preamble = "\t"; config += preamble + c_format("disable: %s\n", bool_c_str(! ClickSocket::is_enabled())); do { config += preamble + c_format("kernel-click {\n"); preamble = "\t "; config += preamble + c_format("disable: %s\n", bool_c_str(! ClickSocket::is_kernel_click())); preamble = "\t"; config += preamble + c_format("}\n"); } while (false); do { config += preamble + c_format("user-click {\n"); preamble = "\t "; config += preamble + c_format("disable: %s\n", bool_c_str(! ClickSocket::is_user_click())); preamble = "\t"; config += preamble + c_format("}\n"); } while (false); } while (false); preamble = " "; config += preamble + c_format("}\n"); } while (false); preamble = ""; config += preamble + c_format("}\n"); return (config); } /** * Generate the next-hop to port mapping. * * @return the number of generated ports. */ int IfConfigSetClick::generate_nexthop_to_port_mapping() { IfTree::IfMap::const_iterator ii; IfTreeInterface::VifMap::const_iterator vi; IfTreeVif::IPv4Map::const_iterator ai4; IfTreeVif::IPv6Map::const_iterator ai6; int xorp_rt_port, local_xorp_rt_port; // // Calculate the port for local delivery. // XXX: The last port in xorp_rt is reserved for local delivery // local_xorp_rt_port = 0; for (ii = _iftree.interfaces().begin(); ii != _iftree.interfaces().end(); ++ii) { const IfTreeInterface& fi = *(ii->second); for (vi = fi.vifs().begin(); vi != fi.vifs().end(); ++vi) { local_xorp_rt_port++; } } // // Generate the next-hop to port mapping. // Note that all local IP addresses are mapped to the port // designated for local delivery. // ifconfig().nexthop_port_mapper().clear(); xorp_rt_port = 0; for (ii = _iftree.interfaces().begin(); ii != _iftree.interfaces().end(); ++ii) { const IfTreeInterface& fi = *(ii->second); for (vi = fi.vifs().begin(); vi != fi.vifs().end(); ++vi) { const IfTreeVif& fv = *(vi->second); ifconfig().nexthop_port_mapper().add_interface(fi.ifname(), fv.vifname(), xorp_rt_port); for (ai4 = fv.ipv4addrs().begin(); ai4 != fv.ipv4addrs().end(); ++ai4) { const IfTreeAddr4& fa4 = *(ai4->second); ifconfig().nexthop_port_mapper().add_ipv4(fa4.addr(), local_xorp_rt_port); IPv4Net ipv4net(fa4.addr(), fa4.prefix_len()); ifconfig().nexthop_port_mapper().add_ipv4net(ipv4net, xorp_rt_port); if (fa4.point_to_point()) ifconfig().nexthop_port_mapper().add_ipv4(fa4.endpoint(), xorp_rt_port); } for (ai6 = fv.ipv6addrs().begin(); ai6 != fv.ipv6addrs().end(); ++ai6) { const IfTreeAddr6& fa6 = *(ai6->second); ifconfig().nexthop_port_mapper().add_ipv6(fa6.addr(), local_xorp_rt_port); IPv6Net ipv6net(fa6.addr(), fa6.prefix_len()); ifconfig().nexthop_port_mapper().add_ipv6net(ipv6net, xorp_rt_port); if (fa6.point_to_point()) ifconfig().nexthop_port_mapper().add_ipv6(fa6.endpoint(), xorp_rt_port); } xorp_rt_port++; } } return (xorp_rt_port); } IfConfigSetClick::ClickConfigGenerator::ClickConfigGenerator( IfConfigSetClick& ifconfig_set_click, const string& command_name) : _ifconfig_set_click(ifconfig_set_click), _eventloop(ifconfig_set_click.ifconfig().eventloop()), _command_name(command_name), _run_command(NULL) { } IfConfigSetClick::ClickConfigGenerator::~ClickConfigGenerator() { #ifndef HOST_OS_WINDOWS if (_run_command != NULL) delete _run_command; if (! _tmp_filename.empty()) unlink(_tmp_filename.c_str()); #endif } int IfConfigSetClick::ClickConfigGenerator::execute(const string& xorp_config, string& error_msg) { XLOG_ASSERT(_tmp_filename.empty()); // // Create a temporary file // FILE* fp = xorp_make_temporary_file("", "xorp_fea_click", _tmp_filename, error_msg); if (fp == NULL) { error_msg = c_format("Cannot create a temporary file: %s", error_msg.c_str()); return (XORP_ERROR); } if (fwrite(xorp_config.c_str(), sizeof(char), xorp_config.size(), fp) != static_cast(xorp_config.size())) { error_msg = c_format("Error writing to temporary file: %s", strerror(errno)); fclose(fp); return (XORP_ERROR); } fclose(fp); // XXX: the filename is the argument _command_argument_list.clear(); _command_argument_list.push_back(_tmp_filename); _run_command = new RunCommand( _eventloop, _command_name, _command_argument_list, callback(this, &IfConfigSetClick::ClickConfigGenerator::stdout_cb), callback(this, &IfConfigSetClick::ClickConfigGenerator::stderr_cb), callback(this, &IfConfigSetClick::ClickConfigGenerator::done_cb), false /* redirect_stderr_to_stdout */); if (_run_command->execute() != XORP_OK) { delete _run_command; _run_command = NULL; unlink(_tmp_filename.c_str()); error_msg = c_format("Could not execute the Click " "configuration generator"); return (XORP_ERROR); } return (XORP_OK); } void IfConfigSetClick::ClickConfigGenerator::stdout_cb(RunCommand* run_command, const string& output) { XLOG_ASSERT(run_command == _run_command); _command_stdout += output; } void IfConfigSetClick::ClickConfigGenerator::stderr_cb(RunCommand* run_command, const string& output) { XLOG_ASSERT(run_command == _run_command); XLOG_ERROR("External Click configuration generator (%s) stderr output: %s", run_command->command().c_str(), output.c_str()); } void IfConfigSetClick::ClickConfigGenerator::done_cb(RunCommand* run_command, bool success, const string& error_msg) { XLOG_ASSERT(run_command == _run_command); _ifconfig_set_click.click_config_generator_done(this, success, error_msg); } #endif // click xorp/fea/data_plane/ifconfig/ifconfig_observer_netlink_socket.hh0000664000076400007640000000473011540225524025403 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/data_plane/ifconfig/ifconfig_observer_netlink_socket.hh,v 1.9 2008/10/02 21:57:06 bms Exp $ #ifndef __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_OBSERVER_NETLINK_SOCKET_HH__ #define __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_OBSERVER_NETLINK_SOCKET_HH__ #include #ifdef HAVE_NETLINK_SOCKETS #include "fea/ifconfig_observer.hh" #include "fea/data_plane/control_socket/netlink_socket.hh" class IfConfigObserverNetlinkSocket : public IfConfigObserver, public NetlinkSocket, public NetlinkSocketObserver { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ IfConfigObserverNetlinkSocket(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~IfConfigObserverNetlinkSocket(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg); /** * Receive data from the underlying system. * * @param buffer the buffer with the received data. */ virtual void receive_data(const vector& buffer); void netlink_socket_data(const vector& buffer); private: }; #endif #endif // __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_OBSERVER_NETLINK_SOCKET_HH__ xorp/fea/data_plane/ifconfig/ifconfig_get_sysctl.cc0000664000076400007640000000754611540225524022636 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #ifdef HAVE_SYS_SYSCTL_H #include #endif #ifdef HAVE_NET_IF_H #include #endif #ifdef HAVE_NET_IF_VAR_H #include #endif #include "fea/ifconfig.hh" #include "ifconfig_get_sysctl.hh" // // Get information about network interfaces from the underlying system. // // The mechanism to obtain the information is sysctl(3). // #ifdef HAVE_SYSCTL_NET_RT_IFLIST IfConfigGetSysctl::IfConfigGetSysctl(FeaDataPlaneManager& fea_data_plane_manager) : IfConfigGet(fea_data_plane_manager) { } IfConfigGetSysctl::~IfConfigGetSysctl() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the sysctl(3) mechanism to get " "information about network interfaces from the underlying " "system: %s", error_msg.c_str()); } } int IfConfigGetSysctl::start(string& error_msg) { UNUSED(error_msg); if (_is_running) return (XORP_OK); _is_running = true; return (XORP_OK); } int IfConfigGetSysctl::stop(string& error_msg) { UNUSED(error_msg); if (! _is_running) return (XORP_OK); _is_running = false; return (XORP_OK); } int IfConfigGetSysctl::pull_config(const IfTree* local_config, IfTree& iftree) { UNUSED(local_config); return read_config(iftree); } int IfConfigGetSysctl::read_config(IfTree& iftree) { int mib[6]; mib[0] = CTL_NET; mib[1] = AF_ROUTE; mib[2] = 0; // protocol number - always 0 mib[3] = AF_UNSPEC; // Address family: any (XXX: always true?) mib[4] = NET_RT_IFLIST; mib[5] = 0; // no flags // Get the table size size_t sz; if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), NULL, &sz, NULL, 0) != 0) { XLOG_ERROR("sysctl(NET_RT_IFLIST) failed: %s", strerror(errno)); return (XORP_ERROR); } // // XXX: we need to fetch the data in a loop, because its size // may increase between the two sysctl() calls. // for ( ; ; ) { vector buffer(sz); // Get the data if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), &buffer[0], &sz, NULL, 0) == 0) { // Check the new size if (buffer.size() < sz) continue; // XXX: shouldn't happen, but just in case if (buffer.size() > sz) buffer.resize(sz); // Parse the result if (parse_buffer_routing_socket(ifconfig(), iftree, buffer) != XORP_OK) { return (XORP_ERROR); } // // Get the VLAN vif info // IfConfigVlanGet* ifconfig_vlan_get; ifconfig_vlan_get = fea_data_plane_manager().ifconfig_vlan_get(); if (ifconfig_vlan_get != NULL) { bool modified = false; if (ifconfig_vlan_get->pull_config(iftree, modified) != XORP_OK) return (XORP_ERROR); } return (XORP_OK); } // Error if (errno == ENOMEM) { // Buffer is too small. Try again. continue; } XLOG_ERROR("sysctl(NET_RT_IFLIST) failed: %s", strerror(errno)); return (XORP_ERROR); } } #endif // HAVE_SYSCTL_NET_RT_IFLIST xorp/fea/data_plane/ifconfig/ifconfig_vlan_get_linux.cc0000664000076400007640000001561111703345405023466 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2007-2012 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ether_compat.h" #include "libcomm/comm_api.h" #ifdef HAVE_SYS_IOCTL_H #include #endif #ifdef HAVE_LINUX_SOCKIOS_H #include #endif #ifdef HAVE_LINUX_IF_VLAN_H #include #endif #ifdef HAVE_NET_IF_VLAN_VAR_H #include #endif #ifdef HAVE_NET_IF_VLANVAR_H #include #endif #include "fea/ifconfig.hh" #include "ifconfig_vlan_get_linux.hh" // // Get VLAN information about network interfaces from the underlying system. // // The mechanism to obtain the information is Linux-specific ioctl(2). // IfConfigVlanGetLinux::IfConfigVlanGetLinux(FeaDataPlaneManager& fea_data_plane_manager, bool is_dummy) : IfConfigVlanGet(fea_data_plane_manager), _is_dummy(is_dummy), _s4(-1) { } IfConfigVlanGetLinux::~IfConfigVlanGetLinux() { if (_is_dummy) { return; } #if defined(HAVE_VLAN_LINUX) or defined(HAVE_VLAN_BSD) else { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the ioctl(2) mechanism to get " "information about VLAN network interfaces from the " "underlying system: %s", error_msg.c_str()); } } #endif } int IfConfigVlanGetLinux::start(string& error_msg) { if (_is_dummy) _is_running = true; if (_is_running) return (XORP_OK); #if defined(HAVE_VLAN_LINUX) or defined(HAVE_VLAN_BSD) if (_s4 < 0) { _s4 = socket(AF_INET, SOCK_DGRAM, 0); if (_s4 < 0) { error_msg = c_format("Could not initialize IPv4 ioctl() " "socket: %s", strerror(errno)); XLOG_FATAL("%s", error_msg.c_str()); } } #else UNUSED(error_msg); #endif _is_running = true; return (XORP_OK); } int IfConfigVlanGetLinux::stop(string& error_msg) { if (_is_dummy) _is_running = false; if (! _is_running) return (XORP_OK); #if defined(HAVE_VLAN_LINUX) or defined(HAVE_VLAN_BSD) int ret_value4 = XORP_OK; if (_s4 >= 0) { ret_value4 = comm_close(_s4); _s4 = -1; if (ret_value4 != XORP_OK) { error_msg = c_format("Could not close IPv4 ioctl() socket: %s", comm_get_last_error_str()); } } if (ret_value4 != XORP_OK) return (XORP_ERROR); #else UNUSED(error_msg); #endif _is_running = false; return (XORP_OK); } int IfConfigVlanGetLinux::pull_config(IfTree& iftree, bool& modified) { return read_config(iftree, modified); } int IfConfigVlanGetLinux::read_config(IfTree& iftree, bool& modified) { if (_is_dummy) return XORP_OK; IfTree::IfMap::iterator ii; string error_msg; bool mod_on_entry = modified; if (! _is_running) { error_msg = c_format("Cannot read VLAN interface intormation: " "the IfConfigVlanGetLinux plugin is not running"); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } XLOG_ASSERT(_s4 >= 0); // // Check all interfaces whether they are actually VLANs. // If an interface is found to be a VLAN, then its vif state is // copied as a vif child to its physical parent interface. // Note that we keep the original VLAN interface state in the IfTree // so the rest of the FEA is not surprised if that state suddenly is // deleted. // for (ii = iftree.interfaces().begin(); ii != iftree.interfaces().end(); ++ii) { IfTreeInterface* ifp = ii->second; // Ignore interfaces that are to be deleted if (ifp->is_marked(IfTreeItem::DELETED)) continue; // If the iface list was modified on entry (ie, we added/deleted an ifp somewhere // when reading iface config, then it's possible a parent has appeared and so we must // reprobe everything just in case. if (mod_on_entry) { ifp->set_probed_vlan(false); } // If we've already probed this device for vlan-ness, then // no need to probe again I think. if (ifp->probed_vlan()) { continue; } /** we'll have probed it when we return. */ ifp->set_probed_vlan(true); uint16_t vlan_id = 0xFFFF; string parent_ifname; #ifdef HAVE_VLAN_BSD struct ifreq ifreq; struct vlanreq vlanreq; // Test whether a VLAN interface memset(&ifreq, 0, sizeof(ifreq)); memset(&vlanreq, 0, sizeof(vlanreq)); strncpy(ifreq.ifr_name, ifp->ifname().c_str(), sizeof(ifreq.ifr_name) - 1); ifreq.ifr_data = reinterpret_cast(&vlanreq); if (ioctl(_s4, SIOCGETVLAN, (caddr_t)&ifreq) < 0) continue; // XXX: Most likely not a VLAN interface // Get the VLAN information vlan_id = vlanreq.vlr_tag; parent_ifname = vlanreq.vlr_parent; if (parent_ifname.empty()) continue; #elif defined(HAVE_VLAN_LINUX) struct vlan_ioctl_args vlanreq; // Test whether a VLAN interface memset(&vlanreq, 0, sizeof(vlanreq)); strncpy(vlanreq.device1, ifp->ifname().c_str(), sizeof(vlanreq.device1) - 1); vlanreq.cmd = GET_VLAN_REALDEV_NAME_CMD; if (ioctl(_s4, SIOCGIFVLAN, &vlanreq) < 0) { continue; // not a vlan } // Get the parent device parent_ifname = vlanreq.u.device2; if (parent_ifname.empty()) { // BUG XLOG_ERROR("Could not find parent ifname for iface: %s\n", ifp->ifname().c_str()); continue; } // Get the VLAN ID memset(&vlanreq, 0, sizeof(vlanreq)); strncpy(vlanreq.device1, ifp->ifname().c_str(), sizeof(vlanreq.device1) - 1); vlanreq.cmd = GET_VLAN_VID_CMD; if (ioctl(_s4, SIOCGIFVLAN, &vlanreq) < 0) { error_msg = c_format("Cannot get the VLAN ID for interface %s: %s", ifp->ifname().c_str(), strerror(errno)); XLOG_ERROR("%s", error_msg.c_str()); continue; } vlan_id = vlanreq.u.VID; #endif IfTreeVif* vifp = ifp->find_vif(ifp->ifname()); if (vifp == NULL) { ifp->add_vif(ifp->ifname()); modified = true; } if (ifp->parent_ifname() != parent_ifname) { modified = true; ifp->set_parent_ifname(parent_ifname); } // TODO: Use a #define or similar for 'VLAN' string vl("VLAN"); if (ifp->iface_type() != vl) { modified = true; ifp->set_iface_type(vl); } string vid = c_format("%hu", vlan_id); if (ifp->vid() != vid) { modified = true; ifp->set_vid(vid); } } return XORP_OK; } xorp/fea/data_plane/ifconfig/ifconfig_set_dummy.cc0000664000076400007640000001336711421137511022456 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ether_compat.h" #include "libxorp/ipvx.hh" #include "fea/ifconfig.hh" #include "ifconfig_set_dummy.hh" // // Set information about network interfaces configuration with the // underlying system. // // The mechanism to set the information is Dummy (for testing purpose). // IfConfigSetDummy::IfConfigSetDummy(FeaDataPlaneManager& fea_data_plane_manager) : IfConfigSet(fea_data_plane_manager), _iftree("ifcfg-set-dummy") { } IfConfigSetDummy::~IfConfigSetDummy() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the Dummy mechanism to set " "information about network interfaces into the underlying " "system: %s", error_msg.c_str()); } } int IfConfigSetDummy::start(string& error_msg) { UNUSED(error_msg); if (_is_running) return (XORP_OK); _is_running = true; return (XORP_OK); } int IfConfigSetDummy::stop(string& error_msg) { UNUSED(error_msg); if (! _is_running) return (XORP_OK); _is_running = false; return (XORP_OK); } int IfConfigSetDummy::push_config(const IfTree& iftree) { _iftree = iftree; return (XORP_OK); } bool IfConfigSetDummy::is_discard_emulated(const IfTreeInterface& i) const { UNUSED(i); return (false); // TODO: return appropriate value. } bool IfConfigSetDummy::is_unreachable_emulated(const IfTreeInterface& i) const { UNUSED(i); return (false); // TODO: return appropriate value. } int IfConfigSetDummy::config_begin(string& error_msg) { UNUSED(error_msg); return (XORP_OK); } int IfConfigSetDummy::config_end(string& error_msg) { UNUSED(error_msg); return (XORP_OK); } int IfConfigSetDummy::config_interface_begin(const IfTreeInterface* pulled_ifp, IfTreeInterface& config_iface, string& error_msg) { UNUSED(pulled_ifp); UNUSED(config_iface); UNUSED(error_msg); return (XORP_OK); } int IfConfigSetDummy::config_interface_end(const IfTreeInterface* pulled_ifp, const IfTreeInterface& config_iface, string& error_msg) { UNUSED(pulled_ifp); UNUSED(config_iface); UNUSED(error_msg); return (XORP_OK); } int IfConfigSetDummy::config_vif_begin(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeInterface& config_iface, const IfTreeVif& config_vif, string& error_msg) { UNUSED(pulled_ifp); UNUSED(pulled_vifp); UNUSED(config_iface); UNUSED(config_vif); UNUSED(error_msg); return (XORP_OK); } int IfConfigSetDummy::config_vif_end(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeInterface& config_iface, const IfTreeVif& config_vif, string& error_msg) { UNUSED(pulled_ifp); UNUSED(pulled_vifp); UNUSED(config_iface); UNUSED(config_vif); UNUSED(error_msg); return (XORP_OK); } int IfConfigSetDummy::config_add_address(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeAddr4* pulled_addrp, const IfTreeInterface& config_iface, const IfTreeVif& config_vif, const IfTreeAddr4& config_addr, string& error_msg) { UNUSED(pulled_ifp); UNUSED(pulled_vifp); UNUSED(pulled_addrp); UNUSED(config_iface); UNUSED(config_vif); UNUSED(config_addr); UNUSED(error_msg); return (XORP_OK); } int IfConfigSetDummy::config_delete_address(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeAddr4* pulled_addrp, const IfTreeInterface& config_iface, const IfTreeVif& config_vif, const IfTreeAddr4& config_addr, string& error_msg) { UNUSED(pulled_ifp); UNUSED(pulled_vifp); UNUSED(pulled_addrp); UNUSED(config_iface); UNUSED(config_vif); UNUSED(config_addr); UNUSED(error_msg); return (XORP_OK); } int IfConfigSetDummy::config_add_address(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeAddr6* pulled_addrp, const IfTreeInterface& config_iface, const IfTreeVif& config_vif, const IfTreeAddr6& config_addr, string& error_msg) { UNUSED(pulled_ifp); UNUSED(pulled_vifp); UNUSED(pulled_addrp); UNUSED(config_iface); UNUSED(config_vif); UNUSED(config_addr); UNUSED(error_msg); return (XORP_OK); } int IfConfigSetDummy::config_delete_address(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeAddr6* pulled_addrp, const IfTreeInterface& config_iface, const IfTreeVif& config_vif, const IfTreeAddr6& config_addr, string& error_msg) { UNUSED(pulled_ifp); UNUSED(pulled_vifp); UNUSED(pulled_addrp); UNUSED(config_iface); UNUSED(config_vif); UNUSED(config_addr); UNUSED(error_msg); return (XORP_OK); } xorp/fea/data_plane/ifconfig/ifconfig_property_solaris.cc0000664000076400007640000000364611540225524024073 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2007-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include #ifdef HOST_OS_SOLARIS #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libcomm/comm_api.h" #include "fea/ifconfig.hh" #include "ifconfig_property_solaris.hh" // // Get information about the data plane property. // // The mechanism to obtain the information is for Solaris systems. // IfConfigPropertySolaris::IfConfigPropertySolaris(FeaDataPlaneManager& fea_data_plane_manager) : IfConfigProperty(fea_data_plane_manager) { } IfConfigPropertySolaris::~IfConfigPropertySolaris() { } bool IfConfigPropertySolaris::test_have_ipv4() const { XorpFd s = comm_sock_open(AF_INET, SOCK_DGRAM, 0, 0); if (! s.is_valid()) return (false); comm_close(s); return (true); } bool IfConfigPropertySolaris::test_have_ipv6() const { #ifndef HAVE_IPV6 return (false); #else XorpFd s = comm_sock_open(AF_INET6, SOCK_DGRAM, 0, 0); if (! s.is_valid()) return (false); comm_close(s); return (true); #endif // HAVE_IPV6 } #endif // HOST_OS_SOLARIS xorp/fea/data_plane/ifconfig/ifconfig_vlan_set_linux.hh0000664000076400007640000000716111703345405023515 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2007-2012 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_VLAN_SET_LINUX_HH__ #define __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_VLAN_SET_LINUX_HH__ #include "fea/ifconfig_vlan_set.hh" class IfConfigVlanSetLinux : public IfConfigVlanSet { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ IfConfigVlanSetLinux(FeaDataPlaneManager& fea_data_plane_manager, bool is_dummy); /** * Virtual destructor. */ virtual ~IfConfigVlanSetLinux(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg); /** * Add a VLAN. * * If an entry for the same VLAN already exists, is is overwritten * with the new information. * * @param system_ifp pointer to the System's vlan interface, or NULL. * @param config_if Configured VLAN interface information. * @param created_if Did we actually create a new interface in the OS? * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int config_add_vlan(const IfTreeInterface* system_ifp, const IfTreeInterface& config_if, bool& created_if, string& error_msg); /** * Delete a VLAN. * * @param config_iface reference to the interface with the information * to configure. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int config_delete_vlan(const IfTreeInterface& config_iface, string& error_msg); private: /** * Add a VLAN. * * @param parent_ifname the parent interface name. * @param vlan_name the VLAN vif name. * @param vlan_id the VLAN ID. * @param created_if Did we actually create a new interface in the OS? * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_vlan(const string& parent_ifname, const string& vlan_name, uint16_t vlan_id, bool& created_if, string& error_msg); /** * Delete a VLAN. * * @param ifname The vlan to delete. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int delete_vlan(const string& ifname, string& error_msg); bool _is_dummy; int _s4; }; #endif // __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_VLAN_SET_LINUX_HH__ xorp/fea/data_plane/ifconfig/ifconfig_set_iphelper.hh0000664000076400007640000003224411540224223023137 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/data_plane/ifconfig/ifconfig_set_iphelper.hh,v 1.13 2008/10/02 21:57:08 bms Exp $ #ifndef __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_SET_IPHELPER_HH__ #define __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_SET_IPHELPER_HH__ #include #include "fea/ifconfig_set.hh" class IfConfigSetIPHelper : public IfConfigSet { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ IfConfigSetIPHelper(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~IfConfigSetIPHelper(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg); private: /** * Determine if the interface's underlying provider implements discard * semantics natively, or if they are emulated through other means. * * @param i the interface item to inspect. * @return true if discard semantics are emulated. */ virtual bool is_discard_emulated(const IfTreeInterface& i) const; /** * Determine if the interface's underlying provider implements unreachable * semantics natively, or if they are emulated through other means. * * @param i the interface item to inspect. * @return true if unreachable semantics are emulated. */ virtual bool is_unreachable_emulated(const IfTreeInterface& i) const; /** * Start the configuration. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int config_begin(string& error_msg); /** * Complete the configuration. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int config_end(string& error_msg); /** * Begin the interface configuration. * * @param pulled_ifp pointer to the interface information pulled from * the system. * @param config_iface reference to the interface with the information * to configure. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int config_interface_begin(const IfTreeInterface* pulled_ifp, IfTreeInterface& config_iface, string& error_msg); /** * End the interface configuration. * * @param pulled_ifp pointer to the interface information pulled from * the system. * @param config_iface reference to the interface with the information * to configure. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int config_interface_end(const IfTreeInterface* pulled_ifp, const IfTreeInterface& config_iface, string& error_msg); /** * Begin the vif configuration. * * @param pulled_ifp pointer to the interface information pulled from * the system. * @param pulled_vifp pointer to the vif information pulled from * the system. * @param config_iface reference to the interface with the information * to configure. * @param config_vif reference to the vif with the information * to configure. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int config_vif_begin(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeInterface& config_iface, const IfTreeVif& config_vif, string& error_msg); /** * End the vif configuration. * * @param pulled_ifp pointer to the interface information pulled from * the system. * @param pulled_vifp pointer to the vif information pulled from * the system. * @param config_iface reference to the interface with the information * to configure. * @param config_vif reference to the vif with the information * to configure. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int config_vif_end(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeInterface& config_iface, const IfTreeVif& config_vif, string& error_msg); /** * Add IPv4 address information. * * If an entry for the same address already exists, is is overwritten * with the new information. * * @param pulled_ifp pointer to the interface information pulled from * the system. * @param pulled_vifp pointer to the vif information pulled from * the system. * @param pulled_addrp pointer to the address information pulled from * the system. * @param config_iface reference to the interface with the information * to configure. * @param config_vif reference to the vif with the information * to configure. * @param config_addr reference to the address with the information * to configure. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int config_add_address(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeAddr4* pulled_addrp, const IfTreeInterface& config_iface, const IfTreeVif& config_vif, const IfTreeAddr4& config_addr, string& error_msg); /** * Delete IPv4 address information. * * @param pulled_ifp pointer to the interface information pulled from * the system. * @param pulled_vifp pointer to the vif information pulled from * the system. * @param pulled_addrp pointer to the address information pulled from * the system. * @param config_iface reference to the interface with the information * to configure. * @param config_vif reference to the vif with the information * to configure. * @param config_addr reference to the address with the information * to configure. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int config_delete_address(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeAddr4* pulled_addrp, const IfTreeInterface& config_iface, const IfTreeVif& config_vif, const IfTreeAddr4& config_addr, string& error_msg); /** * Add IPv6 address information. * * If an entry for the same address already exists, is is overwritten * with the new information. * * @param pulled_ifp pointer to the interface information pulled from * the system. * @param pulled_vifp pointer to the vif information pulled from * the system. * @param pulled_addrp pointer to the address information pulled from * the system. * @param config_iface reference to the interface with the information * to configure. * @param config_vif reference to the vif with the information * to configure. * @param config_addr reference to the address with the information * to configure. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int config_add_address(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeAddr6* pulled_addrp, const IfTreeInterface& config_iface, const IfTreeVif& config_vif, const IfTreeAddr6& config_addr, string& error_msg); /** * Delete IPv6 address information. * * @param pulled_ifp pointer to the interface information pulled from * the system. * @param pulled_vifp pointer to the vif information pulled from * the system. * @param pulled_addrp pointer to the address information pulled from * the system. * @param config_iface reference to the interface with the information * to configure. * @param config_vif reference to the vif with the information * to configure. * @param config_addr reference to the address with the information * to configure. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int config_delete_address(const IfTreeInterface* pulled_ifp, const IfTreeVif* pulled_vifp, const IfTreeAddr6* pulled_addrp, const IfTreeInterface& config_iface, const IfTreeVif& config_vif, const IfTreeAddr6& config_addr, string& error_msg); /** * Set the interface status. * * @param ifname the name of the interface. * @param if_index the interface index. * @param interface_flags the interface flags. * @param is_enabled if true then enable the interface, otherwise * disable it. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_interface_status(const string& ifname, uint32_t if_index, uint32_t interface_flags, bool is_enabled, string& error_msg); /** * Add an IPv4 address to an interface/vif. * * @param ifname the name of the interface. * @param vifname the name of the vif. * @param if_index the interface/vif index. * @param addr the address to add. * @param prefix_len the prefix length. * @param is_broadcast if true this is a broadcast vif. * @param broadcast_addr the broadcast address if this is a * broadcast vif. * @param is_point_to_point if true, this is a point-to-point vif. * @param endpoint_addr the endpoint address if this is a * point-to-point vif. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_addr(const string& ifname, const string& vifname, uint32_t if_index, const IPv4& addr, uint32_t prefix_len, bool is_broadcast, const IPv4& broadcast_addr, bool is_point_to_point, const IPv4& endpoint_addr, string& error_msg); /** * Delete an IPv4 address from an interface/vif. * * @param ifname the name of the interface. * @param vifname the name of the vif. * @param if_index the interface/vif index. * @param addr the address to delete. * @param prefix_len the prefix length. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int delete_addr(const string& ifname, const string& vifname, uint32_t if_index, const IPv4& addr, uint32_t prefix_len, string& error_msg); /** * Add an IPv6 address to an interface/vif. * * @param ifname the name of the interface. * @param vifname the name of the vif. * @param if_index the interface/vif index. * @param addr the address to add. * @param prefix_len the prefix length. * @param is_point_to_point if true, this is a point-to-point vif. * @param endpoint_addr the endpoint address if this is a * point-to-point vif. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_addr(const string& ifname, const string& vifname, uint32_t if_index, const IPv6& addr, uint32_t prefix_len, bool is_point_to_point, const IPv6& endpoint_addr, string& error_msg); /** * Delete an IPv6 address from an interface/vif. * * @param ifname the name of the interface. * @param vifname the name of the vif. * @param if_index the interface/vif index. * @param addr the address to delete. * @param prefix_len the prefix length. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int delete_addr(const string& ifname, const string& vifname, uint32_t if_index, const IPv6& addr, uint32_t prefix_len, string& error_msg); #ifdef HOST_OS_WINDOWS map, ULONG> _nte_map; #endif }; #endif // __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_SET_IPHELPER_HH__ xorp/fea/data_plane/ifconfig/ifconfig_get_click.cc0000664000076400007640000000637111540225523022374 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include #ifdef XORP_USE_CLICK #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "fea/ifconfig.hh" #include "fea/fea_data_plane_manager.hh" #include "ifconfig_set_click.hh" #include "ifconfig_get_click.hh" // // Get information about network interfaces from the underlying system. // // The mechanism to obtain the information is Click: // http://www.read.cs.ucla.edu/click/ // IfConfigGetClick::IfConfigGetClick(FeaDataPlaneManager& fea_data_plane_manager) : IfConfigGet(fea_data_plane_manager), ClickSocket(fea_data_plane_manager.eventloop()), _cs_reader(*(ClickSocket *)this) { } IfConfigGetClick::~IfConfigGetClick() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the Click mechanism to get " "information about network interfaces from the underlying " "system: %s", error_msg.c_str()); } } int IfConfigGetClick::start(string& error_msg) { if (! ClickSocket::is_enabled()) return (XORP_OK); if (_is_running) return (XORP_OK); if (ClickSocket::start(error_msg) != XORP_OK) return (XORP_ERROR); _is_running = true; return (XORP_OK); } int IfConfigGetClick::stop(string& error_msg) { int ret_value = XORP_OK; if (! _is_running) return (XORP_OK); ret_value = ClickSocket::stop(error_msg); _is_running = false; return (ret_value); } int IfConfigGetClick::pull_config(const IfTree* local_config, IfTree& iftree) { UNUSED(local_config); return read_config(iftree); } int IfConfigGetClick::read_config(IfTree& iftree) { // // XXX: Get the tree from the IfConfigSetClick instance. // The reason for that is because it is practically // impossible to read the Click configuration and parse it to restore // the original IfTree state. // IfConfigSet* ifconfig_set = fea_data_plane_manager().ifconfig_set(); if ((ifconfig_set == NULL) || (! ifconfig_set->is_running())) return (XORP_ERROR); IfConfigSetClick* ifconfig_set_click; ifconfig_set_click = dynamic_cast(ifconfig_set); if (ifconfig_set_click == NULL) { // // XXX: The IfConfigSet plugin was probably changed to something else // which we don't know how to deal with. // return (XORP_ERROR); } iftree = ifconfig_set_click->iftree(); return (XORP_OK); } #endif // click xorp/fea/data_plane/ifconfig/ifconfig_property_bsd.hh0000664000076400007640000000421311540225524023170 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2007-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/data_plane/ifconfig/ifconfig_property_bsd.hh,v 1.5 2008/10/02 21:57:06 bms Exp $ #ifndef __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_PROPERTY_BSD_HH__ #define __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_PROPERTY_BSD_HH__ #if defined(HOST_OS_BSDI) \ || defined(HOST_OS_DRAGONFLYBSD) \ || defined(HOST_OS_FREEBSD) \ || defined(HOST_OS_MACOSX) \ || defined(HOST_OS_NETBSD) \ || defined(HOST_OS_OPENBSD) #include "fea/ifconfig_property.hh" class IfConfigPropertyBsd : public IfConfigProperty { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ IfConfigPropertyBsd(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~IfConfigPropertyBsd(); private: /** * Test whether the underlying system supports IPv4. * * @return true if the underlying system supports IPv4, otherwise false. */ virtual bool test_have_ipv4() const; /** * Test whether the underlying system supports IPv6. * * @return true if the underlying system supports IPv6, otherwise false. */ virtual bool test_have_ipv6() const; }; #endif #endif // __FEA_DATA_PLANE_IFCONFIG_IFCONFIG_PROPERTY_BSD_HH__ xorp/fea/io_tcpudp.cc0000664000076400007640000000602511421137511014707 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2007-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "io_tcpudp.hh" // // FEA I/O TCP/UDP communication base class implementation. // IoTcpUdp::IoTcpUdp(FeaDataPlaneManager& fea_data_plane_manager, const IfTree& iftree, int family, bool is_tcp) : _is_running(false), _io_tcpudp_manager(fea_data_plane_manager.io_tcpudp_manager()), _fea_data_plane_manager(fea_data_plane_manager), _eventloop(fea_data_plane_manager.eventloop()), _iftree(iftree), _family(family), _is_tcp(is_tcp), _io_tcpudp_receiver(NULL) { } IoTcpUdp::~IoTcpUdp() { } void IoTcpUdp::register_io_tcpudp_receiver(IoTcpUdpReceiver* io_tcpudp_receiver) { _io_tcpudp_receiver = io_tcpudp_receiver; } void IoTcpUdp::unregister_io_tcpudp_receiver() { _io_tcpudp_receiver = NULL; } void IoTcpUdp::recv_event(const string& if_name, const string& vif_name, const IPvX& src_host, uint16_t src_port, const vector& data) { if (_io_tcpudp_receiver == NULL) { // XXX: should happen only during transient setup stage return; } _io_tcpudp_receiver->recv_event(if_name, vif_name, src_host, src_port, data); } void IoTcpUdp::inbound_connect_event(const IPvX& src_host, uint16_t src_port, IoTcpUdp* new_io_tcpudp) { if (_io_tcpudp_receiver == NULL) { // XXX: should happen only during transient setup stage return; } _io_tcpudp_receiver->inbound_connect_event(src_host, src_port, new_io_tcpudp); } void IoTcpUdp::outgoing_connect_event() { if (_io_tcpudp_receiver == NULL) { // XXX: should happen only during transient setup stage return; } _io_tcpudp_receiver->outgoing_connect_event(); } void IoTcpUdp::error_event(const string& error, bool fatal) { if (_io_tcpudp_receiver == NULL) { // XXX: should happen only during transient setup stage return; } _io_tcpudp_receiver->error_event(error, fatal); } void IoTcpUdp::disconnect_event() { if (_io_tcpudp_receiver == NULL) { // XXX: should happen only during transient setup stage return; } _io_tcpudp_receiver->disconnect_event(); } xorp/fea/mfea_node.cc0000664000076400007640000017256411703345405014660 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2012 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // 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 "fea_node.hh" #include "mfea_mrouter.hh" #include "mfea_node.hh" #include "mfea_kernel_messages.hh" #include "mfea_vif.hh" /** * MfeaNode::MfeaNode: * @fea_node: The corresponding FeaNode. * @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(FeaNode& fea_node, int family, xorp_module_id module_id, EventLoop& eventloop) : ProtoNode(family, module_id, eventloop), IfConfigUpdateReporterBase(fea_node.ifconfig().ifconfig_update_replicator()), _fea_node(fea_node), _mfea_mrouter(*this, fea_node.fibconfig()), _mfea_dft(*this), _mfea_iftree("mfea-tree"), _mfea_iftree_update_replicator(_mfea_iftree), _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); } // // 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() { // Remove myself from receiving FEA interface information remove_from_replicator(); // // Unset myself as an observer when the node status changes // unset_observer(this); stop(); ProtoNode::set_node_status(PROC_NULL); delete_all_vifs(); } /** * Test if running in dummy mode. * * @return true if running in dummy mode, otherwise false. */ bool MfeaNode::is_dummy() const { return (_fea_node.is_dummy()); } /** * 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); // Add myself to receive FEA interface information add_to_replicator(); // // 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() != XORP_OK) return (XORP_ERROR); // // Set the node status // ProtoNode::set_node_status(PROC_STARTUP); // XXX: needed to update the status state properly incr_startup_requests_n(); // Start the MfeaMrouter _mfea_mrouter.start(); // XXX: needed to update the status state properly decr_startup_requests_n(); 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() != XORP_OK) { 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() != XORP_OK) return (XORP_ERROR); // // Perform misc. MFEA-specific stop operations // // XXX: needed to update the status state properly incr_shutdown_requests_n(); // Stop the vifs stop_all_vifs(); // Stop the MfeaMrouter _mfea_mrouter.stop(); // // Set the node status // ProtoNode::set_node_status(PROC_SHUTDOWN); // // Update the node status // update_status(); // XXX: needed to update the status state properly decr_shutdown_requests_n(); 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() != XORP_OK) 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() != XORP_OK) { 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; } } void MfeaNode::interface_update(const string& ifname, const Update& update) { IfTreeInterface* mfea_ifp; const Vif* node_vif = NULL; bool is_up; string error_msg; switch (update) { case CREATED: // Update the MFEA iftree _mfea_iftree.add_interface(ifname); // XXX: The MFEA vif creation is handled by vif_update(), // so we only update the status. break; // FALLTHROUGH case DELETED: // Update the MFEA iftree XLOG_WARNING("interface_update: Delete: %s\n", ifname.c_str()); // First, make sure our protocols are un-registered on this interface // and all it's vifs. unregister_protocols_for_iface(ifname); _mfea_iftree.remove_interface(ifname); _mfea_iftree_update_replicator.interface_update(ifname, update); // XXX: Ignore any errors, in case the MFEA vif was deleted by // vif_update() ProtoNode::delete_config_vif(ifname, error_msg); return; case CHANGED: break; // FALLTHROUGH } // // Find the state from the FEA tree // const IfTreeInterface* ifp = observed_iftree().find_interface(ifname); if (ifp == NULL) { XLOG_WARNING("Got update for interface not in the FEA tree: %s", ifname.c_str()); return; } // // Update the MFEA iftree // mfea_ifp = _mfea_iftree.find_interface(ifname); if (mfea_ifp == NULL) { XLOG_WARNING("Got update for interface not in the MFEA tree: %s", ifname.c_str()); return; } mfea_ifp->copy_state(*ifp, false); _mfea_iftree_update_replicator.interface_update(ifname, update); // // Update the vif flags (only if the vif already exists) // node_vif = configured_vif_find_by_name(ifname); if (node_vif == NULL) return; // No vif to update // XXX: For any flag updates we need to consider the IfTreeVif as well const IfTreeVif* vifp = ifp->find_vif(node_vif->name()); if (vifp == NULL) return; // No IfTreeVif to consider is_up = ifp->enabled(); is_up &= vifp->enabled(); ProtoNode::set_config_vif_flags(ifname, false, // is_pim_register node_vif->is_p2p(), node_vif->is_loopback(), node_vif->is_multicast_capable(), node_vif->is_broadcast_capable(), is_up, ifp->mtu(), error_msg); } void MfeaNode::vif_update(const string& ifname, const string& vifname, const Update& update) { IfTreeInterface* mfea_ifp; IfTreeVif* mfea_vifp = NULL; const Vif* node_vif = NULL; bool is_up; uint32_t vif_index = Vif::VIF_INDEX_INVALID; string error_msg; switch (update) { case CREATED: // Update the MFEA iftree mfea_ifp = _mfea_iftree.find_interface(ifname); if (mfea_ifp == NULL) { XLOG_WARNING("Got update for vif on interface not in the MFEA tree: " "%s/%s", ifname.c_str(), vifname.c_str()); return; } mfea_ifp->add_vif(vifname); mfea_vifp = mfea_ifp->find_vif(vifname); XLOG_ASSERT(mfea_vifp != NULL); node_vif = configured_vif_find_by_name(ifname); if (node_vif == NULL) { vif_index = find_unused_config_vif_index(); XLOG_ASSERT(vif_index != Vif::VIF_INDEX_INVALID); if (ProtoNode::add_config_vif(vifname, vif_index, error_msg) != XORP_OK) { XLOG_ERROR("Cannot add vif %s to the set of configured " "vifs: %s", vifname.c_str(), error_msg.c_str()); return; } } break; // FALLTHROUGH case DELETED: // Update the MFEA iftree XLOG_ERROR("vif_updated: Delete: %s/%s\n", ifname.c_str(), vifname.c_str()); // Unregister protocols on this VIF unregister_protocols_for_vif(ifname, vifname); mfea_ifp = _mfea_iftree.find_interface(ifname); if (mfea_ifp != NULL) { mfea_ifp->remove_vif(vifname); } _mfea_iftree_update_replicator.vif_update(ifname, vifname, update); if (ProtoNode::delete_config_vif(vifname, error_msg) != XORP_OK) { XLOG_ERROR("Cannot delete vif %s from the set of configured " "vifs: %s", vifname.c_str(), error_msg.c_str()); } return; case CHANGED: mfea_vifp = _mfea_iftree.find_vif(ifname, vifname); if (mfea_vifp == NULL) { XLOG_WARNING("Got update for vif not in the MFEA tree: %s/%s", ifname.c_str(), vifname.c_str()); return; } vif_index = mfea_vifp->vif_index(); break; // FALLTHROUGH } // // Find the state from the FEA tree // const IfTreeInterface* ifp = observed_iftree().find_interface(ifname); if (ifp == NULL) { XLOG_WARNING("Got update for vif on interface not in the FEA tree: " "%s/%s", ifname.c_str(), vifname.c_str()); return; } const IfTreeVif* vifp = ifp->find_vif(vifname); if (vifp == NULL) { XLOG_WARNING("Got update for vif not in the FEA tree: %s/%s", ifname.c_str(), vifname.c_str()); return; } // // Update the MFEA iftree // XLOG_ASSERT(mfea_vifp != NULL); XLOG_ASSERT(vif_index != Vif::VIF_INDEX_INVALID); mfea_vifp->copy_state(*vifp); mfea_vifp->set_vif_index(vif_index); _mfea_iftree_update_replicator.vif_update(ifname, vifname, update); // // Find the MFEA vif to update // node_vif = configured_vif_find_by_name(ifname); if (node_vif == NULL) { XLOG_WARNING("Got update for vif that is not in the MFEA tree: %s/%s", ifname.c_str(), vifname.c_str()); return; } // // Update the pif_index // ProtoNode::set_config_pif_index(vifname, vifp->pif_index(), error_msg); // // Update the vif flags // is_up = ifp->enabled(); is_up &= vifp->enabled(); ProtoNode::set_config_vif_flags(vifname, false, // is_pim_register vifp->point_to_point(), vifp->loopback(), vifp->multicast(), vifp->broadcast(), is_up, ifp->mtu(), error_msg); // See if it wants to start? MfeaVif *mfea_vif = vif_find_by_name(vifname); if (mfea_vif) { mfea_vif->notifyUpdated(); } } void MfeaNode::vifaddr4_update(const string& ifname, const string& vifname, const IPv4& addr, const Update& update) { IfTreeVif* mfea_vifp; IfTreeAddr4* mfea_ap; const Vif* node_vif = NULL; const IPvX addrx(addr); string error_msg; if (! is_ipv4()) return; switch (update) { case CREATED: // Update the MFEA iftree mfea_vifp = _mfea_iftree.find_vif(ifname, vifname); if (mfea_vifp == NULL) { XLOG_WARNING("Got update for address on interface not in the FEA tree: " "%s/%s/%s", ifname.c_str(), vifname.c_str(), addr.str().c_str()); return; } mfea_vifp->add_addr(addr); break; // FALLTHROUGH case DELETED: // Update the MFEA iftree mfea_vifp = _mfea_iftree.find_vif(ifname, vifname); if (mfea_vifp != NULL) { mfea_vifp->remove_addr(addr); } _mfea_iftree_update_replicator.vifaddr4_update(ifname, vifname, addr, update); if (ProtoNode::delete_config_vif_addr(vifname, addrx, error_msg) != XORP_OK) { XLOG_ERROR("Cannot delete address %s from vif %s from the set of " "configured vifs: %s", addr.str().c_str(), vifname.c_str(), error_msg.c_str()); } return; case CHANGED: break; // FALLTHROUGH } // // Find the state from the FEA tree // const IfTreeInterface* ifp = observed_iftree().find_interface(ifname); if (ifp == NULL) { XLOG_WARNING("Got update for address on interface not in the FEA tree: " "%s/%s/%s", ifname.c_str(), vifname.c_str(), addr.str().c_str()); return; } const IfTreeVif* vifp = ifp->find_vif(vifname); if (vifp == NULL) { XLOG_WARNING("Got update for address on vif not in the FEA tree: " "%s/%s/%s", ifname.c_str(), vifname.c_str(), addr.str().c_str()); return; } const IfTreeAddr4* ap = vifp->find_addr(addr); if (ap == NULL) { XLOG_WARNING("Got update for address not in the FEA tree: %s/%s/%s", ifname.c_str(), vifname.c_str(), addr.str().c_str()); return; } // // Update the MFEA iftree // mfea_ap = _mfea_iftree.find_addr(ifname, vifname, addr); if (mfea_ap == NULL) { XLOG_WARNING("Got update for address for vif that is not in the MFEA tree: " "%s/%s/%s", ifname.c_str(), vifname.c_str(), addr.str().c_str()); } mfea_ap->copy_state(*ap); _mfea_iftree_update_replicator.vifaddr4_update(ifname, vifname, addr, update); // // Find the MFEA vif to update // node_vif = configured_vif_find_by_name(ifname); if (node_vif == NULL) { XLOG_WARNING("Got update for address for vif that is not in the MFEA tree: " "%s/%s/%s", ifname.c_str(), vifname.c_str(), addr.str().c_str()); return; } // Calculate various addresses IPvXNet subnet_addr(addrx, ap->prefix_len()); IPvX broadcast_addr(IPvX::ZERO(family())); IPvX peer_addr(IPvX::ZERO(family())); if (ap->broadcast()) broadcast_addr = IPvX(ap->bcast()); if (ap->point_to_point()) peer_addr = IPvX(ap->endpoint()); const VifAddr* node_vif_addr = node_vif->find_address(addrx); if (node_vif_addr == NULL) { // First create the MFEA vif address if (ProtoNode::add_config_vif_addr(vifname, addrx, subnet_addr, broadcast_addr, peer_addr, error_msg) != XORP_OK) { XLOG_ERROR("Cannot add address %s to vif %s from the set of " "configured vifs: %s", addr.str().c_str(), vifname.c_str(), error_msg.c_str()); return; } node_vif_addr = node_vif->find_address(addrx); } // // Update the address. // // First we delete it then add it back with the new values. // If there are no changes, we return immediately. // if ((addrx == 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()))) { return; // Nothing changed } if (ProtoNode::delete_config_vif_addr(vifname, addrx, error_msg) != XORP_OK) { XLOG_ERROR("Cannot delete address %s from vif %s from the set of " "configured vifs: %s", addr.str().c_str(), vifname.c_str(), error_msg.c_str()); } if (ProtoNode::add_config_vif_addr(vifname, addrx, subnet_addr, broadcast_addr, peer_addr, error_msg) != XORP_OK) { XLOG_ERROR("Cannot add address %s to vif %s from the set of " "configured vifs: %s", addr.str().c_str(), vifname.c_str(), error_msg.c_str()); } } #ifdef HAVE_IPV6 void MfeaNode::vifaddr6_update(const string& ifname, const string& vifname, const IPv6& addr, const Update& update) { IfTreeVif* mfea_vifp; IfTreeAddr6* mfea_ap; const Vif* node_vif = NULL; const IPvX addrx(addr); string error_msg; if (! is_ipv6()) return; switch (update) { case CREATED: // Update the MFEA iftree mfea_vifp = _mfea_iftree.find_vif(ifname, vifname); if (mfea_vifp == NULL) { XLOG_WARNING("Got update for address on interface not in the FEA tree: " "%s/%s/%s", ifname.c_str(), vifname.c_str(), addr.str().c_str()); return; } mfea_vifp->add_addr(addr); break; // FALLTHROUGH case DELETED: // Update the MFEA iftree mfea_vifp = _mfea_iftree.find_vif(ifname, vifname); if (mfea_vifp != NULL) { mfea_vifp->remove_addr(addr); } _mfea_iftree_update_replicator.vifaddr6_update(ifname, vifname, addr, update); if (ProtoNode::delete_config_vif_addr(vifname, addrx, error_msg) != XORP_OK) { XLOG_ERROR("Cannot delete address %s from vif %s from the set of " "configured vifs: %s", addr.str().c_str(), vifname.c_str(), error_msg.c_str()); } return; case CHANGED: break; // FALLTHROUGH } // // Find the state from the FEA tree // const IfTreeInterface* ifp = observed_iftree().find_interface(ifname); if (ifp == NULL) { XLOG_WARNING("Got update for address on interface not in the FEA tree: " "%s/%s/%s", ifname.c_str(), vifname.c_str(), addr.str().c_str()); return; } const IfTreeVif* vifp = ifp->find_vif(vifname); if (vifp == NULL) { XLOG_WARNING("Got update for address on vif not in the FEA tree: " "%s/%s/%s", ifname.c_str(), vifname.c_str(), addr.str().c_str()); return; } const IfTreeAddr6* ap = vifp->find_addr(addr); if (ap == NULL) { XLOG_WARNING("Got update for address not in the FEA tree: %s/%s/%s", ifname.c_str(), vifname.c_str(), addr.str().c_str()); return; } // // Update the MFEA iftree // mfea_ap = _mfea_iftree.find_addr(ifname, vifname, addr); if (mfea_ap == NULL) { XLOG_WARNING("Got update for address for vif that is not in the MFEA tree: " "%s/%s/%s", ifname.c_str(), vifname.c_str(), addr.str().c_str()); return; } mfea_ap->copy_state(*ap); _mfea_iftree_update_replicator.vifaddr6_update(ifname, vifname, addr, update); // // Find the MFEA vif to update // node_vif = configured_vif_find_by_name(ifname); if (node_vif == NULL) { XLOG_WARNING("Got update for address for vif that is not in the MFEA tree: " "%s/%s/%s", ifname.c_str(), vifname.c_str(), addr.str().c_str()); return; } // Calculate various addresses IPvXNet subnet_addr(addrx, ap->prefix_len()); IPvX broadcast_addr(IPvX::ZERO(family())); IPvX peer_addr(IPvX::ZERO(family())); if (ap->point_to_point()) peer_addr = IPvX(ap->endpoint()); const VifAddr* node_vif_addr = node_vif->find_address(addrx); if (node_vif_addr == NULL) { // First create the MFEA vif address if (ProtoNode::add_config_vif_addr(vifname, addrx, subnet_addr, broadcast_addr, peer_addr, error_msg) != XORP_OK) { XLOG_ERROR("Cannot add address %s to vif %s from the set of " "configured vifs: %s", addr.str().c_str(), vifname.c_str(), error_msg.c_str()); return; } node_vif_addr = node_vif->find_address(addrx); } // // Update the address. // // First we delete it then add it back with the new values. // If there are no changes, we return immediately. // if ((addrx == 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()))) { return; // Nothing changed } if (ProtoNode::delete_config_vif_addr(vifname, addrx, error_msg) != XORP_OK) { XLOG_ERROR("Cannot delete address %s from vif %s from the set of " "configured vifs: %s", addr.str().c_str(), vifname.c_str(), error_msg.c_str()); } if (ProtoNode::add_config_vif_addr(vifname, addrx, subnet_addr, broadcast_addr, peer_addr, error_msg) != XORP_OK) { XLOG_ERROR("Cannot add address %s to vif %s from the set of " "configured vifs: %s", addr.str().c_str(), vifname.c_str(), error_msg.c_str()); } } #endif void MfeaNode::updates_completed() { string error_msg; // Update the MFEA iftree _mfea_iftree.finalize_state(); _mfea_iftree_update_replicator.updates_completed(); 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); // // XXX: A hack to make it appear that the PIM Register vif // has a valid physical interface index. // This is needed to pass a requirement inside the KAME-derived // IPv6 BSD kernel. // 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) != XORP_OK) { XLOG_ERROR("Cannot add Register vif: %s", error_msg.c_str()); return (XORP_ERROR); } if (add_config_vif(register_vif, error_msg) != XORP_OK) { XLOG_ERROR("Cannot add Register vif to set of configured vifs: %s", error_msg.c_str()); return (XORP_ERROR); } // // Update the MFEA iftree // IfTreeInterface* mfea_ifp; IfTreeVif* mfea_vifp; _mfea_iftree.add_interface(register_vif.name()); mfea_ifp = _mfea_iftree.find_interface(register_vif.name()); XLOG_ASSERT(mfea_ifp != NULL); mfea_ifp->set_pif_index(0); // XXX: invalid pif_index mfea_ifp->set_enabled(register_vif.is_underlying_vif_up()); mfea_ifp->set_mtu(register_vif.mtu()); _mfea_iftree_update_replicator.interface_update(mfea_ifp->ifname(), CREATED); mfea_ifp->add_vif(register_vif.name()); mfea_vifp = mfea_ifp->find_vif(register_vif.name()); XLOG_ASSERT(mfea_vifp != NULL); mfea_vifp->set_pif_index(0); // XXX: invalid pif_index mfea_vifp->set_vif_index(register_vif.vif_index()); mfea_vifp->set_enabled(register_vif.is_underlying_vif_up()); mfea_vifp->set_pim_register(register_vif.is_pim_register()); _mfea_iftree_update_replicator.vif_update(mfea_ifp->ifname(), mfea_vifp->vifname(), CREATED); for (vif_addr_iter = register_vif.addr_list().begin(); vif_addr_iter != register_vif.addr_list().end(); ++vif_addr_iter) { const VifAddr& vif_addr = *vif_addr_iter; const IPvX& ipvx = vif_addr.addr(); if (ipvx.is_ipv4()) { IPv4 ipv4 = ipvx.get_ipv4(); mfea_vifp->add_addr(ipv4); IfTreeAddr4* ap = mfea_vifp->find_addr(ipv4); XLOG_ASSERT(ap != NULL); ap->set_enabled(register_vif.is_underlying_vif_up()); ap->set_prefix_len(ipv4.addr_bitlen()); _mfea_iftree_update_replicator.vifaddr4_update(mfea_ifp->ifname(), mfea_vifp->vifname(), ap->addr(), CREATED); } #ifdef HAVE_IPV6 if (ipvx.is_ipv6()) { IPv6 ipv6 = ipvx.get_ipv6(); mfea_vifp->add_addr(ipv6); IfTreeAddr6* ap = mfea_vifp->find_addr(ipv6); XLOG_ASSERT(ap != NULL); ap->set_enabled(register_vif.is_underlying_vif_up()); ap->set_prefix_len(ipv6.addr_bitlen()); _mfea_iftree_update_replicator.vifaddr6_update(mfea_ifp->ifname(), mfea_vifp->vifname(), ap->addr(), CREATED); } #endif } _mfea_iftree_update_replicator.updates_completed(); } // 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("MfeaNode: 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()); // If it's gone, it's as disabled as it can be. //return (XORP_ERROR); return XORP_OK; } 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 (will continue)", vif_name.c_str()); XLOG_WARNING("%s", error_msg.c_str()); // If it doesn't exist, it's as stopped as it's going to get. Returning // error will cause entire commit to fail. //return (XORP_ERROR); return XORP_OK; } 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; } } int MfeaNode::register_protocol(const string& module_instance_name, const string& if_name, const string& vif_name, uint8_t ip_protocol, string& error_msg) { MfeaVif *mfea_vif = vif_find_by_name(vif_name); if (mfea_vif == NULL) { error_msg = c_format("Cannot register module %s on interface %s " "vif %s: no such vif", module_instance_name.c_str(), if_name.c_str(), vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } if (mfea_vif->register_protocol(module_instance_name, ip_protocol, error_msg) != XORP_OK) { return (XORP_ERROR); } // // Insert into the global state for registered module instance names // and IP protocols. // if ((ip_protocol == IPPROTO_PIM) && (_registered_ip_protocols.find(ip_protocol) == _registered_ip_protocols.end())) { // If necessary, start PIM processing if (mfea_mrouter().start_pim(error_msg) != XORP_OK) { string dummy_error_msg; mfea_vif->unregister_protocol(module_instance_name, dummy_error_msg); error_msg = c_format("Cannot start PIM processing: %s", error_msg.c_str()); return (XORP_ERROR); } } _registered_module_instance_names.insert(module_instance_name); _registered_ip_protocols.insert(ip_protocol); return (XORP_OK); } void MfeaNode::unregister_protocols_for_iface(const string& if_name) { IfTreeInterface* mfea_ifp = _mfea_iftree.find_interface(if_name); if (mfea_ifp) { // This is more paranoid than it currently needs to be. It seems // that there cannot be more than one vif per iface, and vif-name is // probably always ifname. But, it shouldn't hurt to make it look // for additional vifs in case support is someday added. // // Gather up list of strings and process them while NOT iterating // over the lists in case something in the unregister logic // changes the lists. // --Ben list vifs; list module_names; IfTreeInterface::VifMap::const_iterator i; for (i = mfea_ifp->vifs().begin(); i != mfea_ifp->vifs().end(); i++) { vifs.push_back(i->first); // name MfeaVif *mfea_vif = vif_find_by_name(i->first); if (mfea_vif) { module_names.push_back(mfea_vif->registered_module_instance_name()); } // Delete the mcast vif from the kernel. delete_multicast_vif(mfea_vif->vif_index()); } // Now, for all vifs/modules unregister_protocol. string err_msg; list::iterator si; for (si = vifs.begin(); si != vifs.end(); si++) { list::iterator mi; for (mi = module_names.begin(); mi != module_names.end(); mi++) { unregister_protocol(*mi, if_name, *si, err_msg); } } } } void MfeaNode::unregister_protocols_for_vif(const string& ifname, const string& vifname) { MfeaVif *mfea_vif = vif_find_by_name(vifname); if (mfea_vif) { string mn(mfea_vif->registered_module_instance_name()); string err_msg; // Delete the mcast vif from the kernel. delete_multicast_vif(mfea_vif->vif_index()); unregister_protocol(mn, ifname, vifname, err_msg); } } int MfeaNode::unregister_protocol(const string& module_instance_name, const string& if_name, const string& vif_name, string& error_msg) { XLOG_WARNING("unregister_protocol: module: %s iface: %s/%s\n", module_instance_name.c_str(), if_name.c_str(), vif_name.c_str()); MfeaVif *mfea_vif = vif_find_by_name(vif_name); if (mfea_vif == NULL) { error_msg = c_format("Cannot unregister module %s on interface %s " "vif %s: no such vif (will continue)", module_instance_name.c_str(), if_name.c_str(), vif_name.c_str()); XLOG_WARNING("%s", error_msg.c_str()); return XORP_OK; } uint8_t ip_protocol = mfea_vif->registered_ip_protocol(); if (mfea_vif->unregister_protocol(module_instance_name, error_msg) != XORP_OK) { return (XORP_ERROR); } // // If necessary, remove from the global state for registered module // instance names and IP protocols. // bool name_found = false; bool ip_protocol_found = false; vector::iterator iter; for (iter = proto_vifs().begin(); iter != proto_vifs().end(); ++iter) { mfea_vif = (*iter); if (mfea_vif == NULL) continue; if (mfea_vif->registered_module_instance_name() == module_instance_name) { name_found = true; } if (mfea_vif->registered_ip_protocol() == ip_protocol) { ip_protocol_found = true; } if (name_found && ip_protocol_found) break; } if (! name_found) _registered_module_instance_names.erase(module_instance_name); if (! ip_protocol_found) { _registered_ip_protocols.erase(ip_protocol); // If necessary, stop PIM processing if (ip_protocol == IPPROTO_PIM) { if (mfea_mrouter().stop_pim(error_msg) != XORP_OK) { error_msg = c_format("Cannot stop PIM processing: %s", error_msg.c_str()); // XXX: don't return error, but just print the error message XLOG_ERROR("%s", error_msg.c_str()); } } } return (XORP_OK); } /** * MfeaNode::signal_message_recv: * @src_module_instance_name: Unused. * @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, 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)); 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) { XLOG_ERROR("signal_message_recv, can't find mfea_vif, vif_index: %i\n", vif_index); return (XORP_ERROR); } // // Send the signal to all upper-layer protocols that expect it. // set::const_iterator iter; for (iter = _registered_module_instance_names.begin(); iter != _registered_module_instance_names.end(); ++iter) { const string& dst_module_instance_name = (*iter); signal_message_send(dst_module_instance_name, 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. // set::const_iterator iter; for (iter = _registered_module_instance_names.begin(); iter != _registered_module_instance_names.end(); ++iter) { const string& dst_module_instance_name = (*iter); dataflow_signal_send(dst_module_instance_name, 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::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) != XORP_OK) { 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) != XORP_OK) { 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), bool_c_str(is_geq_upcall), bool_c_str(is_leq_upcall)); 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), bool_c_str(is_threshold_in_packets), bool_c_str(is_threshold_in_bytes)); 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) != XORP_OK) { 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) != XORP_OK) { 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), bool_c_str(is_geq_upcall), bool_c_str(is_leq_upcall)); 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), bool_c_str(is_threshold_in_packets), bool_c_str(is_threshold_in_bytes)); 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) != XORP_OK) { 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) != XORP_OK) { 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) != XORP_OK) { 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) != XORP_OK) { 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) != XORP_OK) { 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) != XORP_OK) { 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) != XORP_OK) { 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) != XORP_OK) { return (XORP_ERROR); } return (XORP_OK); } xorp/fea/xrl_mfea_shell_funcs.sh0000664000076400007640000001040611421137511017126 0ustar greearbgreearb#!/bin/sh # # $XORP: xorp/fea/xrl_mfea_shell_funcs.sh,v 1.9 2004/03/05 13:59:43 pavlin Exp $ # # # Library of functions to sent XRLs to a running MFEA process. # # Conditionally set ${srcdir} if it wasn't assigned (e.g., by `gmake check`) if [ "X${srcdir}" = "X" ] ; then srcdir=`dirname $0` ; fi . ${srcdir}/../utils/xrl_shell_lib.sh # # Conditionally set the target name # IP_VERSION=${IP_VERSION:?"IP_VERSION undefined. Must be defined to either IPV4 or IPV6"} case "${IP_VERSION}" in IPV4) MFEA_TARGET=${MFEA_TARGET:="MFEA_4"} ;; IPV6) MFEA_TARGET=${MFEA_TARGET:="MFEA_6"} ;; *) echo "Error: invalid IP_VERSION = ${IP_VERSION}. Must be either IPV4 or IPV6" exit 1 ;; esac mfea_have_multicast_routing4() { if [ $# -lt 0 ] ; then echo "Usage: mfea_have_multicast_routing4" exit 1 fi echo "mfea_have_multicast_routing4" $* XRL="finder://$MFEA_TARGET/mfea/0.1/have_multicast_routing4" XRL_ARGS="" call_xrl_wrapper -p result:bool $XRL$XRL_ARGS } mfea_have_multicast_routing6() { if [ $# -lt 0 ] ; then echo "Usage: mfea_have_multicast_routing6" exit 1 fi echo "mfea_have_multicast_routing6" $* XRL="finder://$MFEA_TARGET/mfea/0.1/have_multicast_routing6" XRL_ARGS="" call_xrl_wrapper -p result:bool $XRL$XRL_ARGS } mfea_enable_vif() { if [ $# -lt 2 ] ; then echo "Usage: mfea_enable_vif " exit 1 fi vif_name=$1 enable=$2 echo "mfea_enable_vif" $* XRL="finder://$MFEA_TARGET/mfea/0.1/enable_vif" XRL_ARGS="?vif_name:txt=$vif_name&enable:bool=$enable" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } mfea_start_vif() { if [ $# -lt 1 ] ; then echo "Usage: mfea_start_vif " exit 1 fi vif_name=$1 echo "mfea_start_vif" $* XRL="finder://$MFEA_TARGET/mfea/0.1/start_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } mfea_stop_vif() { if [ $# -lt 1 ] ; then echo "Usage: mfea_stop_vif " exit 1 fi vif_name=$1 echo "mfea_stop_vif" $* XRL="finder://$MFEA_TARGET/mfea/0.1/stop_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } mfea_enable_all_vifs() { if [ $# -lt 1 ] ; then echo "Usage: mfea_enable_all_vifs " exit 1 fi enable=$1 echo "mfea_enable_all_vifs" $* XRL="finder://$MFEA_TARGET/mfea/0.1/enable_all_vifs" XRL_ARGS="?enable:bool=$enable" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } mfea_start_all_vifs() { echo "mfea_start_all_vifs" $* XRL="finder://$MFEA_TARGET/mfea/0.1/start_all_vifs" XRL_ARGS="" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } mfea_stop_all_vifs() { echo "mfea_stop_all_vifs" $* XRL="finder://$MFEA_TARGET/mfea/0.1/stop_all_vifs" XRL_ARGS="" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } mfea_enable_mfea() { if [ $# -lt 1 ] ; then echo "Usage: mfea_enable_mfea " exit 1 fi enable=$1 echo "mfea_enable_mfea" $* XRL="finder://$MFEA_TARGET/mfea/0.1/enable_mfea" XRL_ARGS="?enable:bool=$enable" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } mfea_start_mfea() { echo "mfea_start_mfea" $* XRL="finder://$MFEA_TARGET/mfea/0.1/start_mfea" XRL_ARGS="" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } mfea_stop_mfea() { echo "mfea_stop_mfea" $* XRL="finder://$MFEA_TARGET/mfea/0.1/stop_mfea" XRL_ARGS="" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } mfea_enable_cli() { if [ $# -lt 1 ] ; then echo "Usage: mfea_enable_cli " exit 1 fi enable=$1 echo "mfea_enable_cli" $* XRL="finder://$MFEA_TARGET/mfea/0.1/enable_cli" XRL_ARGS="?enable:bool=$enable" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } mfea_start_cli() { echo "mfea_start_cli" $* XRL="finder://$MFEA_TARGET/mfea/0.1/start_cli" XRL_ARGS="" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } mfea_stop_cli() { echo "mfea_stop_cli" $* XRL="finder://$MFEA_TARGET/mfea/0.1/stop_cli" XRL_ARGS="" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } mfea_log_trace_all() { if [ $# -lt 1 ] ; then echo "Usage: mfea_log_trace_all " exit 1 fi enable=$1 echo "mfea_log_trace_all" $* XRL="finder://$MFEA_TARGET/mfea/0.1/log_trace_all" XRL_ARGS="?enable:bool=$enable" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } xorp/fea/TODO0000664000076400007640000001225411421137511013103 0ustar greearbgreearb# # $XORP: xorp/fea/TODO,v 1.36 2008/05/08 22:46:34 pavlin Exp $ # * Check ioctl(SIOCGETTUNNEL) usage from Linux net-tools from USAGI's distribution (which returns "struct ip_tunnel_parm"), and ioctl(SIOCGETTUNNEL6) which returns "struct ip6_tunnel_parm". The source/destination addresses are in iptunnel.iph.saddr/iptunnel.iph.daddr and ip6tunnel.git_ip6h.ip6_src/ip6tunnel.git_ip6h.ip6_dst respectively. First, check whether the existing methods on Linux already take care of tunnels, and only if they don't then call the above two ioctls as appropriate. * Check whether a returned MAC addresses of an interface is an Ethernet address before attempting to assign it as an Ethernet address. * Sync by renaming all "IfTree& iftree" to "IfTree& it" (or vice-versa). * Make sure that the interfaces in the interface tree are deleted properly whenever the underlying interfaces in the kernel change. E.g., especially be careful in case an interface disappears, and immediately after that a new interface with different name but same interface index appears. In that case, we need to update properly the local cache that maps interface names to indexes. * Parse and pass up Linux RTN_UNREACHABLE messages using the new resolve upcall mechanism. ============================================================================== ==== MFEA-specific TODO (mostly copied-in from the old stand-alone MFEA) ===== * The MFEA should call MRT_ADD_VIF on an interface only after a multicast routing protocol expresses interest in using that interface. * The IPv4 raw packet input in case of OpenBSD has changed between versions. E.g., if (defined(__OpenBSD__) && (OpenBSD < 200311)) then the processing should be same as FreeBSD and NetBSD. In other words, newer OpenBSD behaves differently. * When start/stop multicast routing, call start_pim/stop_pim XRL as well. * Implement following methods and their friends: - set_allow_kernel_signal_messages(bool v) * Set the pif_index for MfeaVif. * In MfeaDfe::is_valid(), the min threshold value must be defined somewhere else, and it should be consistent with BW_UPCALL_THRESHOLD_INTERVAL_MIN_SEC and BW_UPCALL_THRESHOLD_INTERVAL_MIN_USEC * Rename protocol_name and protocol_id in the send protocol messages to src_module_instance_name and src_module_id (or sth. like that). * The min. required TTL to forward on a MFC entry should be configurable per vif. * Rename "functions" with "methods" in classes inside *.hh (e.g., "Private functions". Same for all *.hh in other directories: mrt, cli, mld6igmp, pim, libproto. * Use start_pim() and stop_pim() as appropriate. * If a protocol deletes itself from the MFEA, make sure that the multicast leave operation will be performed for all groups it has joined. * Fix all messages like the one below to print a more explicit information where the error happened. Apply same for CLI, MLD6IGMP, PIM: if (is_invalid_family) { // Invalid address family reason = c_format("Received protocol message with invalid " "address family: IPv4"); fail = true; return XrlCmdError::OKAY(); } * Wnen added a new protocol, schedule the sending back of the Vif info to happen AFTER the return of the XRL handler (???). If the registering protocol doesn't consider the return value from the registration before accepting any other info, then we don't need that. However, if the return value from the registration is important whether to start accepting info from the MFEA, then we must do the above. * Some of the ProtoComm::_sndbuf0/1 or _rcvbuf0/1 are not used and/or should be much smaller. * A single ProtoComm should be able to handle more than one protocol modules. * 'int proto' (e.g. IPPROTO_IGMP, etc) should be renamed to 'proto_number' or somethinf like that. * Use if_nametoindex(3) when available. * Take care of scope_id for IPv6: struct sockaddr_in6 addr; if (IN6_IS_ADDR_LINKLOCAL(&addr.sin6_addr)) { addr.sin6_scope_id = if_nametoindex(ifa->ifa_name); } * Make the behavior consistent: if we use ioctl() to get the list of interfaces, if there is an error, we return immediately; However, if we use sysctl method and if there is an error, we continue with the next interface. * If closing the _mrouter_socket, make sure that _proto_socket for IGMP or ICMP6 is closed too (??). * When sending a request using ProtoComm, make sure we are using the right socket (e.g., IOCTL, etc). * In function headers, make sure that constants have % in front. E.g.: %XORP_OK %XORP_ERROR. * Send email to KAME. In the mean time detect the problem in configure.in #ifdef GET_TIME #undef GET_TIME // TODO: on FreeBSD-4.3 including both ip_mroute.h // and ip6_mroute.h is broken #endif * Change all "FOO node definition" to "FOO node declaration" (?) * _max_rate_limit should be uint64_t, otherwise cannot create an explicit restriction to forward more than 4Gbps. However, the UNIX kernel uses u_int for that, so we don't have much choice. ============================================================================== ==== Packet ACL-specific TODO ===== * Regression test for top half. xorp/fea/ifconfig_property.hh0000664000076400007640000000700111421137511016456 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2007-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/ifconfig_property.hh,v 1.5 2008/10/02 21:56:47 bms Exp $ #ifndef __FEA_IFCONFIG_PROPERTY_HH__ #define __FEA_IFCONFIG_PROPERTY_HH__ #include "fea_data_plane_manager.hh" class IfConfig; class IfConfigProperty { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ IfConfigProperty(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~IfConfigProperty(); /** * Get the @ref IfConfig instance. * * @return the @ref IfConfig instance. */ IfConfig& ifconfig() { return _ifconfig; } /** * Get the @ref FeaDataPlaneManager instance. * * @return the @ref FeaDataPlaneManager instance. */ FeaDataPlaneManager& fea_data_plane_manager() { return _fea_data_plane_manager; } /** * Test whether this instance is running. * * @return true if the instance is running, otherwise false. */ virtual bool is_running() const { return _is_running; } /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg); /** * Return true if the underlying system supports IPv4. * * @return true if the underlying system supports IPv4, otherwise false. */ virtual bool have_ipv4() const { return (_have_ipv4); } /** * Return true if the underlying system supports IPv6. * * @return true if the underlying system supports IPv6, otherwise false. */ virtual bool have_ipv6() const { return (_have_ipv6); } /** * Test whether the underlying system supports IPv4. * * @return true if the underlying system supports IPv4, otherwise false. */ virtual bool test_have_ipv4() const = 0; /** * Test whether the underlying system supports IPv6. * * @return true if the underlying system supports IPv6, otherwise false. */ virtual bool test_have_ipv6() const = 0; protected: // Misc other state bool _is_running; private: IfConfig& _ifconfig; FeaDataPlaneManager& _fea_data_plane_manager; // Cached state whether the system has IPv4 or IPv6 support bool _have_ipv4; bool _have_ipv6; bool _first_start; // True if started for first time }; #endif // __FEA_IFCONFIG_PROPERTY_HH__ xorp/fea/ifconfig_vlan_get.hh0000664000076400007640000000612711540224224016401 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2007-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __FEA_IFCONFIG_VLAN_GET_HH__ #define __FEA_IFCONFIG_VLAN_GET_HH__ #include "iftree.hh" #include "fea_data_plane_manager.hh" class IfConfig; class IfConfigVlanGet { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ IfConfigVlanGet(FeaDataPlaneManager& fea_data_plane_manager) : _is_running(false), _ifconfig(fea_data_plane_manager.ifconfig()), _fea_data_plane_manager(fea_data_plane_manager) {} /** * Virtual destructor. */ virtual ~IfConfigVlanGet() {} /** * Get the @ref IfConfig instance. * * @return the @ref IfConfig instance. */ IfConfig& ifconfig() { return _ifconfig; } /** * Get the @ref FeaDataPlaneManager instance. * * @return the @ref FeaDataPlaneManager instance. */ FeaDataPlaneManager& fea_data_plane_manager() { return _fea_data_plane_manager; } /** * Test whether this instance is running. * * @return true if the instance is running, otherwise false. */ virtual bool is_running() const { return _is_running; } /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg) = 0; /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg) = 0; /** * Pull the VLAN network interface information from the underlying system. * * The VLAN information is added to the existing state in the iftree. * * @param iftree the IfTree storage to store the pulled information. * @param modified Will be false if it is guaranteed that nothing changed in iftree or any of it's objects. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int pull_config(IfTree& iftree, bool& modified) = 0; protected: // Misc other state bool _is_running; private: IfConfig& _ifconfig; FeaDataPlaneManager& _fea_data_plane_manager; }; #endif // __FEA_IFCONFIG_VLAN_GET_HH__ xorp/fea/fea_data_plane_manager.cc0000664000076400007640000005536111540225524017331 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2007-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "fea_node.hh" #include "fea_data_plane_manager.hh" // // FEA data plane manager base class implementation. // FeaDataPlaneManager::FeaDataPlaneManager(FeaNode& fea_node, const string& manager_name) : _fea_node(fea_node), _ifconfig_property(NULL), _ifconfig_get(NULL), _ifconfig_set(NULL), _ifconfig_observer(NULL), _ifconfig_vlan_get(NULL), _ifconfig_vlan_set(NULL), #ifndef XORP_DISABLE_FIREWALL _firewall_get(NULL), _firewall_set(NULL), #endif _fibconfig_forwarding(NULL), _fibconfig_entry_get(NULL), _fibconfig_entry_set(NULL), _fibconfig_entry_observer(NULL), _fibconfig_table_get(NULL), _fibconfig_table_set(NULL), _fibconfig_table_observer(NULL), _manager_name(manager_name), _is_loaded_plugins(false), _is_running_manager(false), _is_running_plugins(false) { } FeaDataPlaneManager::~FeaDataPlaneManager() { string error_msg; if (stop_manager(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop data plane manager %s: %s", manager_name().c_str(), error_msg.c_str()); } } int FeaDataPlaneManager::start_manager(string& error_msg) { UNUSED(error_msg); if (_is_running_manager) return (XORP_OK); if (load_plugins(error_msg) != XORP_OK) return (XORP_ERROR); _is_running_manager = true; return (XORP_OK); } int FeaDataPlaneManager::stop_manager(string& error_msg) { int ret_value = XORP_OK; string error_msg2; if (! _is_running_manager) return (XORP_OK); if (unload_plugins(error_msg2) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } _is_running_manager = false; return (ret_value); } int FeaDataPlaneManager::unload_plugins(string& error_msg) { string error_msg2; UNUSED(error_msg); if (! _is_loaded_plugins) return (XORP_OK); if (stop_plugins(error_msg2) != XORP_OK) { XLOG_WARNING("Error during unloading the plugins for %s data plane " "manager while stopping the plugins: %s. " "Error ignored.", manager_name().c_str(), error_msg2.c_str()); } // // Unload the plugins // // // IfConfig plugins // if (_ifconfig_property != NULL) { delete _ifconfig_property; _ifconfig_property = NULL; } if (_ifconfig_get != NULL) { delete _ifconfig_get; _ifconfig_get = NULL; } if (_ifconfig_set != NULL) { delete _ifconfig_set; _ifconfig_set = NULL; } if (_ifconfig_observer != NULL) { delete _ifconfig_observer; _ifconfig_observer = NULL; } if (_ifconfig_vlan_get != NULL) { delete _ifconfig_vlan_get; _ifconfig_vlan_get = NULL; } if (_ifconfig_vlan_set != NULL) { delete _ifconfig_vlan_set; _ifconfig_vlan_set = NULL; } #ifndef XORP_DISABLE_FIREWALL // // Firewall plugins // if (_firewall_get != NULL) { delete _firewall_get; _firewall_get = NULL; } if (_firewall_set != NULL) { delete _firewall_set; _firewall_set = NULL; } #endif // // FibConfig plugins // if (_fibconfig_forwarding != NULL) { delete _fibconfig_forwarding; _fibconfig_forwarding = NULL; } if (_fibconfig_entry_get != NULL) { delete _fibconfig_entry_get; _fibconfig_entry_get = NULL; } if (_fibconfig_entry_set != NULL) { delete _fibconfig_entry_set; _fibconfig_entry_set = NULL; } if (_fibconfig_entry_observer != NULL) { delete _fibconfig_entry_observer; _fibconfig_entry_observer = NULL; } if (_fibconfig_table_get != NULL) { delete _fibconfig_table_get; _fibconfig_table_get = NULL; } if (_fibconfig_table_set != NULL) { delete _fibconfig_table_set; _fibconfig_table_set = NULL; } if (_fibconfig_table_observer != NULL) { delete _fibconfig_table_observer; _fibconfig_table_observer = NULL; } // // I/O plugins // delete_pointers_list(_io_link_list); delete_pointers_list(_io_ip_list); delete_pointers_list(_io_tcpudp_list); _is_loaded_plugins = false; return (XORP_OK); } int FeaDataPlaneManager::unregister_plugins(string& error_msg) { UNUSED(error_msg); // // Unregister the plugins in the reverse order they were registered // // // I/O plugins // io_link_manager().unregister_data_plane_manager(this); io_ip_manager().unregister_data_plane_manager(this); io_tcpudp_manager().unregister_data_plane_manager(this); // // FibConfig plugins // if (_fibconfig_table_observer != NULL) fibconfig().unregister_fibconfig_table_observer(_fibconfig_table_observer); if (_fibconfig_table_set != NULL) fibconfig().unregister_fibconfig_table_set(_fibconfig_table_set); if (_fibconfig_table_get != NULL) fibconfig().unregister_fibconfig_table_get(_fibconfig_table_get); if (_fibconfig_entry_observer != NULL) fibconfig().unregister_fibconfig_entry_observer(_fibconfig_entry_observer); if (_fibconfig_entry_set != NULL) fibconfig().unregister_fibconfig_entry_set(_fibconfig_entry_set); if (_fibconfig_entry_get != NULL) fibconfig().unregister_fibconfig_entry_get(_fibconfig_entry_get); if (_fibconfig_forwarding != NULL) fibconfig().unregister_fibconfig_forwarding(_fibconfig_forwarding); #ifndef XORP_DISABLE_FIREWALL // // Firewall plugins // if (_firewall_set != NULL) firewall_manager().unregister_firewall_set(_firewall_set); if (_firewall_get != NULL) firewall_manager().unregister_firewall_get(_firewall_get); #endif // // IfConfig plugins // if (_ifconfig_vlan_set != NULL) ifconfig().unregister_ifconfig_vlan_set(_ifconfig_vlan_set); if (_ifconfig_vlan_get != NULL) ifconfig().unregister_ifconfig_vlan_get(_ifconfig_vlan_get); if (_ifconfig_observer != NULL) ifconfig().unregister_ifconfig_observer(_ifconfig_observer); if (_ifconfig_set != NULL) ifconfig().unregister_ifconfig_set(_ifconfig_set); if (_ifconfig_get != NULL) ifconfig().unregister_ifconfig_get(_ifconfig_get); if (_ifconfig_property != NULL) ifconfig().unregister_ifconfig_property(_ifconfig_property); return (XORP_OK); } int FeaDataPlaneManager::start_plugins(string& error_msg) { string dummy_error_msg; list::iterator link_iter; list::iterator ip_iter; list::iterator tcpudp_iter; if (_is_running_plugins) return (XORP_OK); if (! _is_loaded_plugins) { error_msg = c_format("Data plane manager %s plugins are not loaded", manager_name().c_str()); return (XORP_ERROR); } if (register_plugins(error_msg) != XORP_OK) { error_msg = c_format("Cannot register plugins for data plane " "manager %s: %s", manager_name().c_str(), error_msg.c_str()); return (XORP_ERROR); } // // IfConfig plugins // if (_ifconfig_property != NULL) { if (_ifconfig_property->start(error_msg) != XORP_OK) goto error_label; } if (_ifconfig_get != NULL) { if (_ifconfig_get->start(error_msg) != XORP_OK) goto error_label; } if (_ifconfig_set != NULL) { if (_ifconfig_set->start(error_msg) != XORP_OK) goto error_label; } if (_ifconfig_observer != NULL) { if (_ifconfig_observer->start(error_msg) != XORP_OK) goto error_label; } if (_ifconfig_vlan_get != NULL) { if (_ifconfig_vlan_get->start(error_msg) != XORP_OK) goto error_label; } if (_ifconfig_vlan_set != NULL) { if (_ifconfig_vlan_set->start(error_msg) != XORP_OK) goto error_label; } #ifndef XORP_DISABLE_FIREWALL // // Firewall plugins // if (_firewall_get != NULL) { if (_firewall_get->start(error_msg) != XORP_OK) goto error_label; } if (_firewall_set != NULL) { if (_firewall_set->start(error_msg) != XORP_OK) goto error_label; } #endif // // FibConfig plugins // if (_fibconfig_forwarding != NULL) { if (_fibconfig_forwarding->start(error_msg) != XORP_OK) goto error_label; } if (_fibconfig_entry_get != NULL) { if (_fibconfig_entry_get->start(error_msg) != XORP_OK) goto error_label; } if (_fibconfig_entry_set != NULL) { if (_fibconfig_entry_set->start(error_msg) != XORP_OK) goto error_label; } if (_fibconfig_entry_observer != NULL) { if (_fibconfig_entry_observer->start(error_msg) != XORP_OK) goto error_label; } if (_fibconfig_table_get != NULL) { if (_fibconfig_table_get->start(error_msg) != XORP_OK) goto error_label; } if (_fibconfig_table_set != NULL) { if (_fibconfig_table_set->start(error_msg) != XORP_OK) goto error_label; } if (_fibconfig_table_observer != NULL) { if (_fibconfig_table_observer->start(error_msg) != XORP_OK) goto error_label; } // // I/O plugins // for (link_iter = _io_link_list.begin(); link_iter != _io_link_list.end(); ++link_iter) { IoLink* io_link = *link_iter; if (io_link->start(error_msg) != XORP_OK) goto error_label; } for (ip_iter = _io_ip_list.begin(); ip_iter != _io_ip_list.end(); ++ip_iter) { IoIp* io_ip = *ip_iter; if (io_ip->start(error_msg) != XORP_OK) goto error_label; } for (tcpudp_iter = _io_tcpudp_list.begin(); tcpudp_iter != _io_tcpudp_list.end(); ++tcpudp_iter) { IoTcpUdp* io_tcpudp = *tcpudp_iter; if (io_tcpudp->start(error_msg) != XORP_OK) goto error_label; } _is_running_plugins = true; return (XORP_OK); error_label: stop_all_plugins(dummy_error_msg); unregister_plugins(dummy_error_msg); return (XORP_ERROR); } int FeaDataPlaneManager::stop_plugins(string& error_msg) { int ret_value = XORP_OK; string error_msg2; if (! _is_running_plugins) return (XORP_OK); error_msg.erase(); // // Stop the plugins // if (stop_all_plugins(error_msg2) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } unregister_plugins(error_msg2); _is_running_plugins = false; return (ret_value); } int FeaDataPlaneManager::register_all_plugins(bool is_exclusive, string& error_msg) { string dummy_error_msg; // // IfConfig plugins // if (_ifconfig_property != NULL) { if (ifconfig().register_ifconfig_property(_ifconfig_property, is_exclusive) != XORP_OK) { error_msg = c_format("Cannot register IfConfigProperty plugin " "for data plane manager %s", manager_name().c_str()); unregister_plugins(dummy_error_msg); return (XORP_ERROR); } } if (_ifconfig_get != NULL) { if (ifconfig().register_ifconfig_get(_ifconfig_get, is_exclusive) != XORP_OK) { error_msg = c_format("Cannot register IfConfigGet plugin " "for data plane manager %s", manager_name().c_str()); unregister_plugins(dummy_error_msg); return (XORP_ERROR); } } if (_ifconfig_set != NULL) { if (ifconfig().register_ifconfig_set(_ifconfig_set, is_exclusive) != XORP_OK) { error_msg = c_format("Cannot register IfConfigSet plugin " "for data plane manager %s", manager_name().c_str()); unregister_plugins(dummy_error_msg); return (XORP_ERROR); } } if (_ifconfig_observer != NULL) { if (ifconfig().register_ifconfig_observer(_ifconfig_observer, is_exclusive) != XORP_OK) { error_msg = c_format("Cannot register IfConfigObserver plugin " "for data plane manager %s", manager_name().c_str()); unregister_plugins(dummy_error_msg); return (XORP_ERROR); } } if (_ifconfig_vlan_get != NULL) { if (ifconfig().register_ifconfig_vlan_get(_ifconfig_vlan_get, is_exclusive) != XORP_OK) { error_msg = c_format("Cannot register IfConfigVlanGet plugin " "for data plane manager %s", manager_name().c_str()); unregister_plugins(dummy_error_msg); return (XORP_ERROR); } } if (_ifconfig_vlan_set != NULL) { if (ifconfig().register_ifconfig_vlan_set(_ifconfig_vlan_set, is_exclusive) != XORP_OK) { error_msg = c_format("Cannot register IfConfigVlanSet plugin " "for data plane manager %s", manager_name().c_str()); unregister_plugins(dummy_error_msg); return (XORP_ERROR); } } #ifndef XORP_DISABLE_FIREWALL // // Firewall plugins // if (_firewall_get != NULL) { if (firewall_manager().register_firewall_get(_firewall_get, is_exclusive) != XORP_OK) { error_msg = c_format("Cannot register FirewallGet plugin " "for data plane manager %s", manager_name().c_str()); unregister_plugins(dummy_error_msg); return (XORP_ERROR); } } if (_firewall_set != NULL) { if (firewall_manager().register_firewall_set(_firewall_set, is_exclusive) != XORP_OK) { error_msg = c_format("Cannot register FirewallSet plugin " "for data plane manager %s", manager_name().c_str()); unregister_plugins(dummy_error_msg); return (XORP_ERROR); } } #endif // // FibConfig plugins // if (_fibconfig_forwarding != NULL) { if (fibconfig().register_fibconfig_forwarding(_fibconfig_forwarding, is_exclusive) != XORP_OK) { error_msg = c_format("Cannot register FibConfigForwarding plugin " "for data plane manager %s", manager_name().c_str()); unregister_plugins(dummy_error_msg); return (XORP_ERROR); } } if (_fibconfig_entry_get != NULL) { if (fibconfig().register_fibconfig_entry_get(_fibconfig_entry_get, is_exclusive) != XORP_OK) { error_msg = c_format("Cannot register FibConfigEntryGet plugin " "for data plane manager %s", manager_name().c_str()); unregister_plugins(dummy_error_msg); return (XORP_ERROR); } } if (_fibconfig_entry_set != NULL) { if (fibconfig().register_fibconfig_entry_set(_fibconfig_entry_set, is_exclusive) != XORP_OK) { error_msg = c_format("Cannot register FibConfigEntrySet plugin " "for data plane manager %s", manager_name().c_str()); unregister_plugins(dummy_error_msg); return (XORP_ERROR); } } if (_fibconfig_entry_observer != NULL) { if (fibconfig().register_fibconfig_entry_observer(_fibconfig_entry_observer, is_exclusive) != XORP_OK) { error_msg = c_format("Cannot register FibConfigEntryObserver plugin " "for data plane manager %s", manager_name().c_str()); unregister_plugins(dummy_error_msg); return (XORP_ERROR); } } if (_fibconfig_table_get != NULL) { if (fibconfig().register_fibconfig_table_get(_fibconfig_table_get, is_exclusive) != XORP_OK) { error_msg = c_format("Cannot register FibConfigTableGet plugin " "for data plane manager %s", manager_name().c_str()); unregister_plugins(dummy_error_msg); return (XORP_ERROR); } } if (_fibconfig_table_set != NULL) { if (fibconfig().register_fibconfig_table_set(_fibconfig_table_set, is_exclusive) != XORP_OK) { error_msg = c_format("Cannot register FibConfigTableSet plugin " "for data plane manager %s", manager_name().c_str()); unregister_plugins(dummy_error_msg); return (XORP_ERROR); } } if (_fibconfig_table_observer != NULL) { if (fibconfig().register_fibconfig_table_observer(_fibconfig_table_observer, is_exclusive) != XORP_OK) { error_msg = c_format("Cannot register FibConfigTableObserver plugin " "for data plane manager %s", manager_name().c_str()); unregister_plugins(dummy_error_msg); return (XORP_ERROR); } } // // XXX: The I/O plugins (IoLink, IoIp and IoTcpUdp) are registered // on-demand when a new client is registered. // return (XORP_OK); } int FeaDataPlaneManager::stop_all_plugins(string& error_msg) { list::iterator link_iter; list::iterator ip_iter; list::iterator tcpudp_iter; int ret_value = XORP_OK; string error_msg2; error_msg.erase(); // // XXX: Stop the plugins in the reverse order they were started // // // I/O plugins // for (tcpudp_iter = _io_tcpudp_list.begin(); tcpudp_iter != _io_tcpudp_list.end(); ++tcpudp_iter) { IoTcpUdp* io_tcpudp = *tcpudp_iter; if (io_tcpudp->stop(error_msg) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } } for (ip_iter = _io_ip_list.begin(); ip_iter != _io_ip_list.end(); ++ip_iter) { IoIp* io_ip = *ip_iter; if (io_ip->stop(error_msg) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } } for (link_iter = _io_link_list.begin(); link_iter != _io_link_list.end(); ++link_iter) { IoLink* io_link = *link_iter; if (io_link->stop(error_msg) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } } // // FibConfig plugins // if (_fibconfig_table_observer != NULL) { if (_fibconfig_table_observer->stop(error_msg2) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } } if (_fibconfig_table_set != NULL) { if (_fibconfig_table_set->stop(error_msg2) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } } if (_fibconfig_table_get != NULL) { if (_fibconfig_table_get->stop(error_msg2) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } } if (_fibconfig_entry_observer != NULL) { if (_fibconfig_entry_observer->stop(error_msg2) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } } if (_fibconfig_entry_set != NULL) { if (_fibconfig_entry_set->stop(error_msg2) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } } if (_fibconfig_entry_get != NULL) { if (_fibconfig_entry_get->stop(error_msg2) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } } if (_fibconfig_forwarding != NULL) { if (_fibconfig_forwarding->stop(error_msg2) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } } #ifndef XORP_DISABLE_FIREWALL // // Firewall plugins // if (_firewall_set != NULL) { if (_firewall_set->stop(error_msg2) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } } if (_firewall_get != NULL) { if (_firewall_get->stop(error_msg2) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } } #endif // // IfConfig plugins // if (_ifconfig_vlan_set != NULL) { if (_ifconfig_vlan_set->stop(error_msg2) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } } if (_ifconfig_vlan_get != NULL) { if (_ifconfig_vlan_get->stop(error_msg2) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } } if (_ifconfig_observer != NULL) { if (_ifconfig_observer->stop(error_msg2) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } } if (_ifconfig_set != NULL) { if (_ifconfig_set->stop(error_msg2) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } } if (_ifconfig_get != NULL) { if (_ifconfig_get->stop(error_msg2) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } } if (_ifconfig_property != NULL) { if (_ifconfig_property->stop(error_msg2) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } } return (ret_value); } EventLoop& FeaDataPlaneManager::eventloop() { return (_fea_node.eventloop()); } bool FeaDataPlaneManager::have_ipv4() const { if (_ifconfig_property != NULL) return (_ifconfig_property->have_ipv4()); return (false); } bool FeaDataPlaneManager::have_ipv6() const { if (_ifconfig_property != NULL) return (_ifconfig_property->have_ipv6()); return (false); } IfConfig& FeaDataPlaneManager::ifconfig() { return (_fea_node.ifconfig()); } #ifndef XORP_DISABLE_FIREWALL FirewallManager& FeaDataPlaneManager::firewall_manager() { return (_fea_node.firewall_manager()); } #endif FibConfig& FeaDataPlaneManager::fibconfig() { return (_fea_node.fibconfig()); } IoLinkManager& FeaDataPlaneManager::io_link_manager() { return (_fea_node.io_link_manager()); } IoIpManager& FeaDataPlaneManager::io_ip_manager() { return (_fea_node.io_ip_manager()); } IoTcpUdpManager& FeaDataPlaneManager::io_tcpudp_manager() { return (_fea_node.io_tcpudp_manager()); } void FeaDataPlaneManager::deallocate_io_link(IoLink* io_link) { list::iterator iter; iter = find(_io_link_list.begin(), _io_link_list.end(), io_link); XLOG_ASSERT(iter != _io_link_list.end()); _io_link_list.erase(iter); delete io_link; } void FeaDataPlaneManager::deallocate_io_ip(IoIp* io_ip) { list::iterator iter; iter = find(_io_ip_list.begin(), _io_ip_list.end(), io_ip); XLOG_ASSERT(iter != _io_ip_list.end()); _io_ip_list.erase(iter); delete io_ip; } void FeaDataPlaneManager::deallocate_io_tcpudp(IoTcpUdp* io_tcpudp) { list::iterator iter; iter = find(_io_tcpudp_list.begin(), _io_tcpudp_list.end(), io_tcpudp); XLOG_ASSERT(iter != _io_tcpudp_list.end()); _io_tcpudp_list.erase(iter); delete io_tcpudp; } xorp/fea/mfea_mrouter.cc0000664000076400007640000021455711703345405015427 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2012 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // 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 #endif #ifdef HAVE_SYS_SOCKIO_H #include #endif // // XXX: We need to include before because // the latter is broken on many systems (e.g., Linux Debian-3.1, Linux // Ubuntu-7.04, Gentoo-2006.1): file is needed if // is included, but contains some hacks // to prevent the inclusion of . // #ifdef HAVE_LINUX_TYPES_H #include #endif #ifdef HAVE_SYS_SYSCTL_H #include #endif #ifdef HAVE_NET_IF_H #include #endif #ifdef HAVE_NET_IF_VAR_H #include // if_name() is a macro in FreeBSD 7.0 conflicting with our method names. #ifdef if_name #undef if_name #endif #endif #ifdef HAVE_NETINET_IN_SYSTM_H #include #endif #ifdef HAVE_NETINET_IP_H #include #endif #ifdef HAVE_NETINET_IP6_H #include #endif #ifdef HAVE_NETINET_ICMP6_H #include #endif #ifdef HAVE_NETINET6_IN6_VAR_H #include #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 "fea_node.hh" #include "mfea_node.hh" #include "mfea_vif.hh" #include "mfea_kernel_messages.hh" #include "mfea_osdep.hh" #include "mfea_mrouter.hh" #include "fibconfig.hh" bool new_mcast_tables_api = false; #ifdef USE_MULT_MCAST_TABLES /** In order to support multiple routing tables, the kernel API had to be extended. * Since no distro has this currently in #include files, add private definitions * here. --Ben */ // Assume supported until we know otherwise. bool supports_mcast_tables = true; #define DFLT_MROUTE_TBL 253 /* 'default' routing table id in Linux */ // Support for multiple routing tables. #define SIOCGETVIFCNT_NG (SIOCPROTOPRIVATE+3) #define SIOCGETSGCNT_NG (SIOCPROTOPRIVATE+4) /* For supporting multiple routing tables */ struct vifctl_ng { struct vifctl vif; unsigned int table_id; } __attribute__ ((packed)); struct mfcctl_ng { struct mfcctl mfc; unsigned int table_id; } __attribute__ ((packed)); /* Used with these options: case MRT_INIT: case MRT_DONE: case MRT_ASSERT: #ifdef CONFIG_IP_PIMSM case MRT_PIM: #endif and all getsockopt options */ struct mrt_sockopt_simple { unsigned int optval; unsigned int table_id; }; struct sioc_sg_req_ng { struct sioc_sg_req req; unsigned int table_id; } __attribute__ ((packed)); struct sioc_vif_req_ng { struct sioc_vif_req vif; unsigned int table_id; } __attribute__ ((packed)); /* New mcast API */ #ifndef MRT_TABLE #define MRT_TABLE (MRT_BASE+9) /* Specify mroute table ID */ #endif #ifndef MRT6_TABLE #define MRT6_TABLE (MRT6_BASE+9) /* Specify mroute table ID */ #endif #else /* Don't support multiple mcast routing tables */ bool supports_mcast_tables = false; #endif // // Exported variables // // // Static class members // // // Local constants definitions // // // 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, const FibConfig& fibconfig) : ProtoUnit(mfea_node.family(), mfea_node.module_id()), _mfea_node(mfea_node), _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), _multicast_forwarding_enabled(false), _fibconfig(fibconfig) { string error_msg; // // Get the old state from the underlying system // int ret_value = XORP_OK; switch (family()) { case AF_INET: ret_value = multicast_forwarding_enabled4(_multicast_forwarding_enabled, error_msg); break; #ifdef HAVE_IPV6 case AF_INET6: ret_value = multicast_forwarding_enabled6(_multicast_forwarding_enabled, error_msg); break; #endif // HAVE_IPV6 default: XLOG_UNREACHABLE(); } if (ret_value != XORP_OK) { XLOG_FATAL("%s", error_msg.c_str()); } } MfeaMrouter::~MfeaMrouter() { stop(); } /** * MfeaMrouter::start: * @: * * Start the MfeaMrouter. * * Return value: %XORP_OK on success, otherwize %XORP_ERROR. **/ int MfeaMrouter::start() { string error_msg; #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() != XORP_OK) 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 // Register as multicast upcall receiver IoIpManager& io_ip_manager = mfea_node().fea_node().io_ip_manager(); uint8_t ip_protocol = kernel_mrouter_ip_protocol(); if (io_ip_manager.register_system_multicast_upcall_receiver( family(), ip_protocol, callback(this, &MfeaMrouter::kernel_call_process), _mrouter_socket, error_msg) != XORP_OK) { XLOG_ERROR("Cannot register multicast upcall receiver: %s", error_msg.c_str()); return (XORP_ERROR); } if (! _mrouter_socket.is_valid()) { XLOG_ERROR("Failed to assign the multicast routing socket"); return (XORP_ERROR); } // Start the multicast routing in the kernel if (start_mrt() != XORP_OK) return (XORP_ERROR); return (XORP_OK); } /** * MfeaMrouter::stop: * @: * * Stop the MfeaMrouter. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int MfeaMrouter::stop() { string error_msg; if (is_down()) return (XORP_OK); if (ProtoUnit::stop() != XORP_OK) return (XORP_ERROR); // Stop the multicast routing in the kernel stop_mrt(); // Clear kernel multicast routing access socket _mrouter_socket.clear(); // Unregister as multicast upcall receiver IoIpManager& io_ip_manager = mfea_node().fea_node().io_ip_manager(); uint8_t ip_protocol = kernel_mrouter_ip_protocol(); if (io_ip_manager.unregister_system_multicast_upcall_receiver( family(), ip_protocol, error_msg) != XORP_OK) { XLOG_ERROR("Cannot unregister multicast upcall receiver: %s", error_msg.c_str()); return (XORP_ERROR); } // // Restore the old forwarding state in the underlying system. // int ret_value = XORP_OK; switch (family()) { case AF_INET: ret_value = set_multicast_forwarding_enabled4(_multicast_forwarding_enabled, error_msg); break; #ifdef HAVE_IPV6 case AF_INET6: ret_value = set_multicast_forwarding_enabled6(_multicast_forwarding_enabled, error_msg); break; #endif // HAVE_IPV6 default: XLOG_UNREACHABLE(); } if (ret_value != XORP_OK) { XLOG_ERROR("Cannot restore the multicast forwarding state: %s", error_msg.c_str()); return (XORP_ERROR); } return (XORP_OK); } #ifdef USE_MULT_MCAST_TABLES int MfeaMrouter::getTableId() const { int table_id = DFLT_MROUTE_TBL; if (_fibconfig.unicast_forwarding_table_id_is_configured(family())) { table_id = _fibconfig.unicast_forwarding_table_id(family()); } return table_id; } #endif /** * 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 #ifdef USE_MULT_MCAST_TABLES struct mrt_sockopt_simple tmp; memset(&tmp, 0, sizeof(tmp)); tmp.table_id = getTableId(); tmp.optval = 1; //version #endif 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 // First, try for multiple routing tables. bool do_mrt_init = true; new_mcast_tables_api = false; #ifdef USE_MULT_MCAST_TABLES errno = 0; int rv; // Try old hacked API rv = setsockopt(s, IPPROTO_IP, MRT_INIT, &tmp, sizeof(tmp)); if (rv < 0) { // try new api uint32_t tbl = getTableId(); rv = setsockopt(s, IPPROTO_IP, MRT_TABLE, &tbl, sizeof(tbl)); if (rv >= 0) { new_mcast_tables_api = true; } } else { // old api worked do_mrt_init = false; } if (rv < 0) { // Ok, doesn't support new or old API supports_mcast_tables = false; } else { supports_mcast_tables = true; } #endif if (do_mrt_init) { if (setsockopt(s, IPPROTO_IP, MRT_INIT, &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 } /** * Test whether the IPv4 multicast forwarding engine is enabled or disabled * to forward packets. * * @param ret_value if true on return, then the IPv4 multicast forwarding * is enabled, otherwise is disabled. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int MfeaMrouter::multicast_forwarding_enabled4(bool& ret_value, string& error_msg) const { int enabled = 0; UNUSED(error_msg); // XXX: always return true if running in dummy mode if (mfea_node().is_dummy()) { ret_value = true; return (XORP_OK); } // // XXX: Don't check whether the system supports IPv4 multicast routing, // because this might require to run as a root. // #if defined(CTL_NET) && defined(IPPROTO_IP) && defined(IPCTL_MFORWARDING) { size_t sz = sizeof(enabled); int mib[4]; mib[0] = CTL_NET; mib[1] = AF_INET; mib[2] = IPPROTO_IP; mib[3] = IPCTL_MFORWARDING; if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), &enabled, &sz, NULL, 0) != 0) { error_msg = c_format("Get sysctl(IPCTL_MFORWARDING) failed: %s", strerror(errno)); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } } #else // ! IPCTL_MFORWARDING // XXX: Not all systems have such additional control mechanism to // explicitly enable multicast forwarding, hence assume it is enabled. enabled = 1; #endif // ! IPCTL_MFORWARDING if (enabled > 0) ret_value = true; else ret_value = false; return (XORP_OK); } /** * Test whether the IPv6 multicast forwarding engine is enabled or disabled * to forward packets. * * @param ret_value if true on return, then the IPv6 multicast forwarding * is enabled, otherwise is disabled. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int MfeaMrouter::multicast_forwarding_enabled6(bool& ret_value, string& error_msg) const { int enabled = 0; UNUSED(error_msg); // XXX: always return true if running in dummy mode if (mfea_node().is_dummy()) { ret_value = true; return (XORP_OK); } // // XXX: Don't check whether the system supports IPv6 multicast routing, // because this might require to run as a root. // #if defined(CTL_NET) && defined(IPPROTO_IPV6) && defined(IPV6CTL_MFORWARDING) { size_t sz = sizeof(enabled); int mib[4]; mib[0] = CTL_NET; mib[1] = AF_INET6; mib[2] = IPPROTO_IPV6; mib[3] = IPV6CTL_MFORWARDING; if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), &enabled, &sz, NULL, 0) != 0) { error_msg = c_format("Get sysctl(IPV6CTL_MFORWARDING) failed: %s", strerror(errno)); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } } #else // ! IPV6CTL_MFORWARDING // // XXX: Not all systems have such additional control mechanism to // explicitly enable multicast forwarding, hence assume it is enabled. // enabled = 1; #endif // ! IPV6CTL_MFORWARDING if (enabled > 0) ret_value = true; else ret_value = false; return (XORP_OK); } /** * Set the IPv4 multicast forwarding engine to enable or disable forwarding * of packets. * * @param v if true, then enable IPv4 multicast forwarding, otherwise * disable it. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int MfeaMrouter::set_multicast_forwarding_enabled4(bool v, string& error_msg) { // XXX: don't do anything if running in dummy mode if (mfea_node().is_dummy()) return (XORP_OK); if (! have_multicast_routing4()) { if (! v) { // // XXX: we assume that "not supported" == "disable", hence // return OK. // return (XORP_OK); } error_msg = c_format("Cannot set IPv4 multicast forwarding to %s: " "IPv4 multicast routing is not supported", bool_c_str(v)); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } int enable = (v) ? 1 : 0; bool old_value; UNUSED(enable); if (multicast_forwarding_enabled4(old_value, error_msg) != XORP_OK) return (XORP_ERROR); if (old_value == v) return (XORP_OK); // Nothing changed #if defined(CTL_NET) && defined(IPPROTO_IP) && defined(IPCTL_MFORWARDING) { size_t sz = sizeof(enable); int mib[4]; mib[0] = CTL_NET; mib[1] = AF_INET; mib[2] = IPPROTO_IP; mib[3] = IPCTL_MFORWARDING; if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), NULL, NULL, &enable, sz) != 0) { error_msg = c_format("Set sysctl(IPCTL_MFORWARDING) to %s failed: %s", bool_c_str(v), strerror(errno)); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } } #else // ! IPCTL_MFORWARDING // // XXX: Not all systems have such additional control mechanism to // explicitly enable multicast forwarding, hence don't do anything // #endif // ! IPCTL_MFORWARDING return (XORP_OK); } /** * Set the IPv6 multicast forwarding engine to enable or disable forwarding * of packets. * * @param v if true, then enable IPv6 multicast forwarding, otherwise * disable it. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int MfeaMrouter::set_multicast_forwarding_enabled6(bool v, string& error_msg) { // XXX: don't do anything if running in dummy mode if (mfea_node().is_dummy()) return (XORP_OK); if (! have_multicast_routing6()) { if (! v) { // // XXX: we assume that "not supported" == "disable", hence // return OK. // return (XORP_OK); } error_msg = c_format("Cannot set IPv6 multicast forwarding to %s: " "IPv6 multicast routing is not supported", bool_c_str(v)); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } int enable = (v) ? 1 : 0; bool old_value; UNUSED(enable); if (multicast_forwarding_enabled6(old_value, error_msg) != XORP_OK) return (XORP_ERROR); if (old_value == v) return (XORP_OK); // Nothing changed #if defined(CTL_NET) && defined(IPPROTO_IPV6) && defined(IPV6CTL_MFORWARDING) { size_t sz = sizeof(enable); int mib[4]; mib[0] = CTL_NET; mib[1] = AF_INET6; mib[2] = IPPROTO_IPV6; mib[3] = IPV6CTL_MFORWARDING; if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), NULL, NULL, &enable, sz) != 0) { error_msg = c_format("Set sysctl(IPV6CTL_MFORWARDING) to %s failed: %s", bool_c_str(v), strerror(errno)); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } } #else // ! IPV6CTL_MFORWARDING // // XXX: Not all systems have such additional control mechanism to // explicitly enable multicast forwarding, hence don't do anything // #endif // ! IPV6CTL_MFORWARDING return (XORP_OK); } /** * 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 -1. **/ 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 (-1); } return (-1); } /** * 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 string error_msg; bool do_mrt_init = true; UNUSED(mrouter_version); UNUSED(error_msg); switch (family()) { case AF_INET: #ifndef HAVE_IPV4_MULTICAST_ROUTING UNUSED(do_mrt_init); XLOG_ERROR("start_mrt() failed: " "IPv4 multicast routing not supported"); return (XORP_ERROR); #else if (set_multicast_forwarding_enabled4(true, error_msg) != XORP_OK) { XLOG_ERROR("Cannot enable IPv4 multicast forwarding: %s", error_msg.c_str()); return (XORP_ERROR); } new_mcast_tables_api = false; #ifdef USE_MULT_MCAST_TABLES struct mrt_sockopt_simple tmp; memset(&tmp, 0, sizeof(tmp)); tmp.table_id = getTableId(); tmp.optval = 1; //version if (setsockopt(_mrouter_socket, IPPROTO_IP, MRT_INIT, &tmp, sizeof(tmp)) < 0) { // Ok, doesn't use old API at least uint32_t tbl = getTableId(); if (setsockopt(_mrouter_socket, IPPROTO_IP, MRT_TABLE, &tbl, sizeof(tbl)) < 0) { // Nor this supports_mcast_tables = false; XLOG_ERROR("MROUTE: WARNING: setsockopt(MRT_INIT) does not support multiple routing tables:: %s", strerror(errno)); } else { supports_mcast_tables = true; new_mcast_tables_api = true; XLOG_INFO("NOTE: MROUTE: setsockopt(MRT_TABLE, %d) works! Supports multiple" " mcast routing tables.\n", tbl); } } else { supports_mcast_tables = true; do_mrt_init = false; XLOG_WARNING("NOTE: MROUTE: setsockopt(MRT_INIT) supports multiple routing tables!"); XLOG_WARNING("NOTE: mroute ioctl struct sizes: mfcctl: %i mfcctl_ng: %i mrt_sockopt_simple: %i" " sioc_sg_req: %i sioc_sg_req_ng: %i sioc_vif_req: %i sioc_vif_req_ng: %i\n", (int)(sizeof(struct mfcctl)), (int)(sizeof(struct mfcctl_ng)), (int)(sizeof(struct mrt_sockopt_simple)), (int)(sizeof(struct sioc_sg_req)), (int)(sizeof(struct sioc_sg_req_ng)), (int)(sizeof(struct sioc_vif_req)), (int)(sizeof(struct sioc_vif_req_ng))); } #endif if (do_mrt_init) { 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 (set_multicast_forwarding_enabled6(true, error_msg) != XORP_OK) { XLOG_ERROR("Cannot enable IPv6 multicast forwarding: %s", error_msg.c_str()); return (XORP_ERROR); } #ifdef USE_MULT_MCAST_TABLES uint32_t tbl = getTableId(); if (setsockopt(_mrouter_socket, SOL_IPV6, MRT6_TABLE, &tbl, sizeof(tbl)) < 0) { XLOG_ERROR("MROUTE: WARNING: setsockopt(MRT6_TABLE, %d) does not support" " multiple routing tables:: %s", tbl, strerror(errno)); } else { XLOG_INFO("NOTE: MROUTE: setsockopt(MRT6_TABLE, %d) works! Supports" " multiple mcast-6 routing tables.\n", tbl); } #endif 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() { string error_msg; UNUSED(error_msg); _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); size_t sz = 0; void* o = NULL; switch (family()) { case AF_INET: #ifndef HAVE_IPV4_MULTICAST_ROUTING UNUSED(sz); UNUSED(o); XLOG_ERROR("stop_mrt() failed: " "IPv4 multicast routing not supported"); return (XORP_ERROR); #else if (set_multicast_forwarding_enabled4(false, error_msg) != XORP_OK) { XLOG_ERROR("Cannot disable IPv4 multicast forwarding: %s", error_msg.c_str()); return (XORP_ERROR); } #ifdef USE_MULT_MCAST_TABLES struct mrt_sockopt_simple tmp; memset(&tmp, 0, sizeof(tmp)); tmp.table_id = getTableId(); tmp.optval = 1; //version sz = sizeof(tmp); o = &tmp; if (new_mcast_tables_api || !supports_mcast_tables) { sz = 0; o = NULL; } #endif if (setsockopt(_mrouter_socket, IPPROTO_IP, MRT_DONE, o, sz) < 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 (set_multicast_forwarding_enabled6(false, error_msg) != XORP_OK) { XLOG_ERROR("Cannot disable IPv6 multicast forwarding: %s", error_msg.c_str()); return (XORP_ERROR); } 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); } int MfeaMrouter::start_pim(string& error_msg) { int v = 1; size_t sz = 0; void* o = NULL; switch (family()) { case AF_INET: #ifndef HAVE_IPV4_MULTICAST_ROUTING UNUSED(sz); UNUSED(o); error_msg = c_format("start_pim() failed: " "IPv4 multicast routing not supported"); return (XORP_ERROR); #else #ifdef USE_MULT_MCAST_TABLES struct mrt_sockopt_simple tmp; memset(&tmp, 0, sizeof(tmp)); tmp.table_id = getTableId(); tmp.optval = 1; //pim sz = sizeof(tmp); o = &tmp; if (new_mcast_tables_api || !supports_mcast_tables) { sz = sizeof(v); o = &v; } #else sz = sizeof(v); o = &v; #endif if (setsockopt(_mrouter_socket, IPPROTO_IP, MRT_PIM, o, sz) < 0) { error_msg = c_format("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 error_msg = c_format("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) { error_msg = c_format("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); } int MfeaMrouter::stop_pim(string& error_msg) { int v = 0; size_t sz = 0; void* o = NULL; if (!_mrouter_socket.is_valid()) return (XORP_ERROR); switch (family()) { case AF_INET: #ifndef HAVE_IPV4_MULTICAST_ROUTING UNUSED(sz); UNUSED(o); error_msg = c_format("stop_pim() failed: " "IPv4 multicast routing not supported"); return (XORP_ERROR); #else #ifdef USE_MULT_MCAST_TABLES struct mrt_sockopt_simple tmp; memset(&tmp, 0, sizeof(tmp)); tmp.table_id = getTableId(); tmp.optval = 0; //pim sz = sizeof(tmp); o = &tmp; if (new_mcast_tables_api || !supports_mcast_tables) { sz = sizeof(v); o = &v; } #else sz = sizeof(v); o = &v; #endif if (setsockopt(_mrouter_socket, IPPROTO_IP, MRT_PIM, o, sz) < 0) { error_msg = c_format("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 error_msg = c_format("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) { error_msg = c_format("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); void* sopt_arg = NULL; size_t sz = 0; switch (family()) { case AF_INET: { #ifndef HAVE_IPV4_MULTICAST_ROUTING UNUSED(sz); UNUSED(sopt_arg); XLOG_ERROR("add_multicast_vif() failed: " "IPv4 multicast routing not supported"); return (XORP_ERROR); #else #ifdef USE_MULT_MCAST_TABLES struct vifctl_ng vc_ng; struct vifctl& vc = vc_ng.vif; memset(&vc_ng, 0, sizeof(vc_ng)); sopt_arg = &vc_ng; sz = sizeof(vc_ng); vc_ng.table_id = getTableId(); if (new_mcast_tables_api || !supports_mcast_tables) { sopt_arg = &(vc_ng.vif); sz = sizeof(vc_ng.vif); } #else struct vifctl vc; memset(&vc, 0, sizeof(vc)); sopt_arg = &vc; sz = sizeof(vc); #endif 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, sopt_arg, sz) < 0) { XLOG_ERROR("setsockopt(MRT_ADD_VIF, vif %s) failed: %s sz: %i", mfea_vif->name().c_str(), strerror(errno), (int)(sz)); 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(); #ifdef HAVE_STRUCT_MIF6CTL_VIFC_THRESHOLD /* BSD does not, it seems, newer Linux does */ mc.vifc_threshold = mfea_vif->min_ttl_threshold(); mc.vifc_rate_limit = mfea_vif->max_rate_limit(); #endif if (setsockopt(_mrouter_socket, IPPROTO_IPV6, MRT6_ADD_MIF, (void *)&mc, sizeof(mc)) < 0) { XLOG_ERROR("setsockopt(%i, MRT6_ADD_MIF, vif %s) failed: %s mifi: %i flags: 0x%x pifi: %i", _mrouter_socket.getSocket(), mfea_vif->name().c_str(), strerror(errno), (int)(mc.mif6c_mifi), (int)(mc.mif6c_flags), (int)(mc.mif6c_pifi)); 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) { XLOG_WARNING("Could not find mfea-vif for index: %i\n", vif_index); 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 #ifdef USE_MULT_MCAST_TABLES struct vifctl_ng vc_ng; struct vifctl& vc = vc_ng.vif; memset(&vc_ng, 0, sizeof(vc_ng)); void* sopt_arg = &vc_ng; size_t sz = sizeof(vc_ng); vc_ng.table_id = getTableId(); if (new_mcast_tables_api || !supports_mcast_tables) { sopt_arg = &(vc_ng.vif); sz = sizeof(vc_ng.vif); } #else struct vifctl vc; memset(&vc, 0, sizeof(vc)); void* sopt_arg = &vc; size_t sz = sizeof(vc); #endif vc.vifc_vifi = mfea_vif->vif_index(); ret_value = setsockopt(_mrouter_socket, IPPROTO_IP, MRT_DEL_VIF, sopt_arg, sz); #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; mifi_t vifi = mfea_vif->vif_index(); ret_value = setsockopt(_mrouter_socket, IPPROTO_IPV6, MRT6_DEL_MIF, (void *)&vifi, sizeof(vifi)); 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) { //XLOG_ERROR("MfeaMrouter::add_mfc, source: %s group: %s iif_vif_index: %i rp_addr: %s\n", // source.str().c_str(), group.str().c_str(), iif_vif_index, // rp_addr.str().c_str()); 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 #ifdef USE_MULT_MCAST_TABLES struct mfcctl_ng mc_ng; struct mfcctl& mc = mc_ng.mfc; memset(&mc_ng, 0, sizeof(mc_ng)); void* sopt_arg = &mc_ng; size_t sz = sizeof(mc_ng); mc_ng.table_id = getTableId(); if (new_mcast_tables_api || !supports_mcast_tables) { sopt_arg = &(mc_ng.mfc); sz = sizeof(mc_ng.mfc); } #else #if defined(HAVE_STRUCT_MFCCTL2) && defined(ENABLE_ADVANCED_MULTICAST_API) struct mfcctl2 mc; #else struct mfcctl mc; #endif void* sopt_arg = &mc; size_t sz = sizeof(mc); memset(&mc, 0, sizeof(mc)); #endif 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_STRUCT_MFCCTL2_MFCC_FLAGS) && defined(ENABLE_ADVANCED_MULTICAST_API) mc.mfcc_flags[i] = oifs_flags[i]; #endif } #if defined(HAVE_STRUCT_MFCCTL2_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, sopt_arg, sz) < 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_STRUCT_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_STRUCT_MF6CCTL2_MF6CC_FLAGS) && defined(ENABLE_ADVANCED_MULTICAST_API) mc.mf6cc_flags[i] = oifs_flags[i]; #endif } #if defined(HAVE_STRUCT_MF6CCTL2_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 #ifdef USE_MULT_MCAST_TABLES struct mfcctl_ng mc_ng; struct mfcctl& mc = mc_ng.mfc; memset(&mc_ng, 0, sizeof(mc_ng)); void* sopt_arg = &mc_ng; size_t sz = sizeof(mc_ng); mc_ng.table_id = getTableId(); if (new_mcast_tables_api || !supports_mcast_tables) { sopt_arg = &(mc_ng.mfc); sz = sizeof(mc_ng.mfc); } #else struct mfcctl mc; void* sopt_arg = &mc; size_t sz = sizeof(mc); #endif source.copy_out(mc.mfcc_origin); group.copy_out(mc.mfcc_mcastgrp); if (setsockopt(_mrouter_socket, IPPROTO_IP, MRT_DEL_MFC, sopt_arg, sz) < 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) && defined(MRT_ADD_BW_UPCALL)) 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), bool_c_str(is_geq_upcall), bool_c_str(is_leq_upcall)); 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), bool_c_str(is_threshold_in_packets), bool_c_str(is_threshold_in_bytes)); 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) && defined(MRT_ADD_BW_UPCALL)) 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), bool_c_str(is_geq_upcall), bool_c_str(is_leq_upcall)); 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), bool_c_str(is_threshold_in_packets), bool_c_str(is_threshold_in_bytes)); 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 int ioctl_cmd = SIOCGETSGCNT; #ifdef USE_MULT_MCAST_TABLES struct sioc_sg_req_ng sgreq_ng; memset(&sgreq_ng, 0, sizeof(sgreq_ng)); sgreq_ng.table_id = getTableId(); struct sioc_sg_req& sgreq = (sgreq_ng.req); void* o = &sgreq_ng; ioctl_cmd = SIOCGETSGCNT_NG; if (new_mcast_tables_api || !supports_mcast_tables) { o = &(sgreq_ng.req); ioctl_cmd = SIOCGETSGCNT; } #else struct sioc_sg_req sgreq; memset(&sgreq, 0, sizeof(sgreq)); void* o = &sgreq; #endif 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, ioctl_cmd, o) < 0) || ((sgreq.pktcnt == 0xffffffffU) && (sgreq.bytecnt == 0xffffffffU) && (sgreq.wrong_if == 0xffffffffU))) { XLOG_ERROR("ioctl(SIOCGETSGCNT(%i), (%s %s)) failed: %s", ioctl_cmd, 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 int ioctl_cmd = SIOCGETVIFCNT; #ifdef USE_MULT_MCAST_TABLES struct sioc_vif_req_ng vreq_ng; memset(&vreq_ng, 0, sizeof(vreq_ng)); vreq_ng.table_id = getTableId(); struct sioc_vif_req& vreq = (vreq_ng.vif); void* o = &vreq_ng; ioctl_cmd = SIOCGETVIFCNT_NG; if (new_mcast_tables_api || !supports_mcast_tables) { o = &(vreq_ng.vif); ioctl_cmd = SIOCGETVIFCNT; } #else struct sioc_vif_req vreq; memset(&vreq, 0, sizeof(vreq)); void* o = &vreq; #endif vreq.vifi = mfea_vif->vif_index(); if (ioctl(_mrouter_socket, ioctl_cmd, o) < 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); } /** * 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(const 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 XLOG_ERROR("kernel_call_process, ignoring pkt, can't find mfea_vif by index: %i", iif_vif_index); 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 XLOG_ERROR("kernel_call_process, src and/or dst not valid, src: %s dst: %s", src.str().c_str(), dst.str().c_str()); 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("", 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("", 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); } xorp/fea/profile_vars.cc0000664000076400007640000000265211421137511015416 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/profile.hh" #include "profile_vars.hh" struct profile_vars { string var; string comment; } profile_vars[] = { { profile_route_in, "Routes entering FEA" }, { profile_route_out, "Routes being sent to the KERNEL" } }; void initialize_profiling_variables(Profile& p) { for (size_t i = 0; i < sizeof(profile_vars) / sizeof(struct profile_vars); i++) p.create(profile_vars[i].var, profile_vars[i].comment); } xorp/fea/fea_node.hh0000664000076400007640000001533011703345405014500 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2007-2012 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __FEA_FEA_NODE_HH__ #define __FEA_FEA_NODE_HH__ // // FEA (Forwarding Engine Abstraction) node implementation. // #ifndef XORP_DISABLE_PROFILE #include "libxorp/profile.hh" #endif #include "fibconfig.hh" #ifndef XORP_DISABLE_FIREWALL #include "firewall_manager.hh" #endif #include "ifconfig.hh" #include "io_link_manager.hh" #include "io_ip_manager.hh" #include "io_tcpudp_manager.hh" #include "nexthop_port_mapper.hh" class EventLoop; class FeaIo; /** * @short The FEA (Forwarding Engine Abstraction) node class. * * There should be one node per FEA instance. */ class FeaNode { public: /** * Constructor for a given event loop. * * @param eventloop the event loop to use. * @param fea_io the FeaIo instance to use. * @param is_dummy if true, then run the FEA in dummy mode. */ FeaNode(EventLoop& eventloop, FeaIo& fea_io, bool is_dummy); /** * Destructor */ virtual ~FeaNode(); /** * Startup the service operation. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int startup(); /** * Shutdown the service operation. * * Gracefully shutdown the FEA. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int shutdown(); /** * Test whether the service is running. * * @return true if the service is still running, otherwise false. */ bool is_running() const; /** * Return true if the underlying system supports IPv4. * * @return true if the underlying system supports IPv4, otherwise false. */ bool have_ipv4() const; /** * Return true if the underlying system supports IPv6. * * @return true if the underlying system supports IPv6, otherwise false. */ bool have_ipv6() const; /** * Test if running in dummy mode. * * @return true if running in dummy mode, otherwise false. */ bool is_dummy() const { return _is_dummy; } /** * Get the event loop this service is added to. * * @return the event loop this service is added to. */ EventLoop& eventloop() { return (_eventloop); } #ifndef XORP_DISABLE_PROFILE /** * Get the Profile instance. * * @return a reference to the Profile instance. * @see Profile. */ Profile& profile() { return (_profile); } #endif /** * Get the NexthopPortMapper instance. * * @return a reference to the NexthopPortMapper instance. * @see NexthopPortMapper. */ NexthopPortMapper& nexthop_port_mapper() { return (_nexthop_port_mapper); } /** * Get the IfConfig instance. * * @return a reference to the IfConfig instance. * @see IfConfig. */ IfConfig& ifconfig() { return (_ifconfig); } #ifndef XORP_DISABLE_FIREWALL /** * Get the FirewallManager instance. * * @return a reference to the FirewallManager instance. * @see FirewallManager. */ FirewallManager& firewall_manager() { return (_firewall_manager); } #endif /** * Get the FibConfig instance. * * @return a reference to the FibConfig instance. * @see FibConfig. */ FibConfig& fibconfig() { return (_fibconfig); } /** * Get the IoLinkManager instance. * * @return a reference to the IoLinkManager instance. * @see IoLinkManager. */ IoLinkManager& io_link_manager() { return (_io_link_manager); } /** * Get the IoIpManager instance. * * @return a reference to the IoIpManager instance. * @see IoIpManager. */ IoIpManager& io_ip_manager() { return (_io_ip_manager); } /** * Get the IoTcpUdpManager instance. * * @return a reference to the IoTcpUdpManager instance. * @see IoTcpUdpManager. */ IoTcpUdpManager& io_tcpudp_manager() { return (_io_tcpudp_manager); } /** * Register @ref FeaDataPlaneManager data plane manager. * * @param fea_data_plane_manager the data plane manager to register. * @param is_exclusive if true, the manager is registered as the * exclusive manager, otherwise is added to the list of managers. * @return XORP_OK on success, otherwise XORP_ERROR. */ int register_data_plane_manager(FeaDataPlaneManager* fea_data_plane_manager, bool is_exclusive); /** * Unregister @ref FeaDataPlaneManager data plane manager. * * @param fea_data_plane_manager the data plane manager to unregister. * @return XORP_OK on success, otherwise XORP_ERROR. */ int unregister_data_plane_manager(FeaDataPlaneManager* fea_data_plane_manager); /** * Get the FEA I/O instance. * * @return reference to the FEA I/O instance. */ FeaIo& fea_io() { return (_fea_io); } private: /** * Load the data plane managers. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int load_data_plane_managers(string& error_msg); /** * Unload the data plane managers. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int unload_data_plane_managers(string& error_msg); EventLoop& _eventloop; // The event loop to use bool _is_running; // True if the service is running bool _is_dummy; // True if running in dummy node #ifndef XORP_DISABLE_PROFILE Profile _profile; // Profile entity #endif NexthopPortMapper _nexthop_port_mapper; // Next-hop port mapper IfConfig _ifconfig; #ifndef XORP_DISABLE_FIREWALL FirewallManager _firewall_manager; #endif FibConfig _fibconfig; IoLinkManager _io_link_manager; IoIpManager _io_ip_manager; IoTcpUdpManager _io_tcpudp_manager; list _fea_data_plane_managers; FeaIo& _fea_io; // The FeaIo entry to use }; #endif // __FEA_FEA_NODE_HH__ xorp/fea/fibconfig_table_get.hh0000664000076400007640000000700411540225525016672 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/fibconfig_table_get.hh,v 1.17 2008/10/02 21:56:46 bms Exp $ #ifndef __FEA_FIBCONFIG_TABLE_GET_HH__ #define __FEA_FIBCONFIG_TABLE_GET_HH__ #include "fte.hh" #include "iftree.hh" #include "fea_data_plane_manager.hh" class FibConfig; class FibConfigTableGet { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ FibConfigTableGet(FeaDataPlaneManager& fea_data_plane_manager) : _is_running(false), _fibconfig(fea_data_plane_manager.fibconfig()), _fea_data_plane_manager(fea_data_plane_manager) {} /** * Virtual destructor. */ virtual ~FibConfigTableGet() {} /** * Get the @ref FibConfig instance. * * @return the @ref FibConfig instance. */ FibConfig& fibconfig() { return _fibconfig; } /** * Get the @ref FeaDataPlaneManager instance. * * @return the @ref FeaDataPlaneManager instance. */ FeaDataPlaneManager& fea_data_plane_manager() { return _fea_data_plane_manager; } /** * Test whether this instance is running. * * @return true if the instance is running, otherwise false. */ virtual bool is_running() const { return _is_running; } /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg) = 0; /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg) = 0; /** * Obtain the IPv4 unicast forwarding table. * * @param fte_list the return-by-reference list with all entries in * the IPv4 unicast forwarding table. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int get_table4(list& fte_list) = 0; /** * Obtain the IPv6 unicast forwarding table. * * @param fte_list the return-by-reference list with all entries in * the IPv6 unicast forwarding table. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int get_table6(list& fte_list) = 0; /** Routing table ID that we are interested in might have changed. Maybe something * can filter on this for increased efficiency. */ virtual int notify_table_id_change(uint32_t new_tbl) = 0; protected: // Misc other state bool _is_running; private: FibConfig& _fibconfig; FeaDataPlaneManager& _fea_data_plane_manager; }; #endif // __FEA_FIBCONFIG_TABLE_GET_HH__ xorp/fea/profile_vars.hh0000664000076400007640000000256511540225525015440 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/profile_vars.hh,v 1.8 2008/10/02 21:56:50 bms Exp $ #ifndef __FEA_PROFILE_VARS_HH__ #define __FEA_PROFILE_VARS_HH__ #ifdef XORP_DISABLE_PROFILE #error "BUG BUG BUG Do not include this file if profiling is disabled." #endif /** * Profile variables * See: profile_vars.cc for definitions. */ const string profile_route_in = "route_in"; const string profile_route_out = "route_out"; void initialize_profiling_variables(Profile& p); #endif // __FEA_PROFILE_VARS_HH__ xorp/fea/io_tcpudp.hh0000664000076400007640000004247311540224224014730 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2007-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/io_tcpudp.hh,v 1.12 2008/10/02 21:56:48 bms Exp $ #ifndef __FEA_IO_TCPUDP_HH__ #define __FEA_IO_TCPUDP_HH__ #include "fea_data_plane_manager.hh" // // I/O TCP/UDP communication API. // class EventLoop; class IfTree; class IfTreeInterface; class IfTreeVif; class IoTcpUdpManager; class IoTcpUdpReceiver; class IPvX; /** * @short A base class for I/O TCP/UDP communication. */ class IoTcpUdp { public: /** * Constructor for a given address family. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). * @param iftree the interface tree to use. * @param family the address family (AF_INET or AF_INET6 for IPv4 and IPv6 * respectively). * @param is_tcp if true this is TCP entry, otherwise UDP. */ IoTcpUdp(FeaDataPlaneManager& fea_data_plane_manager, const IfTree& iftree, int family, bool is_tcp); /** * Virtual destructor. */ virtual ~IoTcpUdp(); /** * Get the @ref IoTcpUdpManager instance. * * @return the @ref IoTcpUdpManager instance. */ IoTcpUdpManager& io_tcpudp_manager() { return _io_tcpudp_manager; } /** * Get the @ref FeaDataPlaneManager instance. * * @return the @ref FeaDataPlaneManager instance. */ FeaDataPlaneManager& fea_data_plane_manager() { return _fea_data_plane_manager; } /** * Test whether this instance is running. * * @return true if the instance is running, otherwise false. */ virtual bool is_running() const { return _is_running; } /** * Get the event loop. * * @return the event loop. */ EventLoop& eventloop() { return (_eventloop); } /** * Get the interface tree. * * @return the interface tree. */ const IfTree& iftree() const { return (_iftree); } /** * Get the address family. * * @return the address family. */ virtual int family() const { return (_family); } /** * Test whether this is TCP entry. * * @return true if this is TCP entry, otherwise false (i.e., UDP). */ virtual bool is_tcp() const { return (_is_tcp); } /** * Get the registered receiver. * * @return the registered receiver. */ IoTcpUdpReceiver* io_tcpudp_receiver() { return (_io_tcpudp_receiver); } /** * Register the I/O TCP/UDP data receiver. * * @param io_tcpudp_receiver the receiver to register. */ virtual void register_io_tcpudp_receiver(IoTcpUdpReceiver* io_tcpudp_receiver); /** * Unregister the I/O TCP/UDP data receiver. */ virtual void unregister_io_tcpudp_receiver(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg) = 0; /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg) = 0; /** * Open a TCP socket. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int tcp_open(string& error_msg) = 0; /** * Open an UDP socket. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int udp_open(string& error_msg) = 0; /** * Create a bound TCP socket. * * @param local_addr the interface address to bind socket to. * @param local_port the port to bind socket to. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int tcp_open_and_bind(const IPvX& local_addr, uint16_t local_port, string& error_msg) = 0; /** * Create a bound UDP socket. * * @param local_addr the interface address to bind socket to. * @param local_port the port to bind socket to. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int udp_open_and_bind(const IPvX& local_addr, uint16_t local_port, const string& local_dev, int reuse, string& error_msg) = 0; /** * Create a bound UDP multicast socket. * * @param local_addr the interface address to bind socket to. * @param local_port the port to bind socket to. * @param mcast_addr the multicast group address to join. * @param ttl the TTL to use for this multicast socket. * @param reuse allow other sockets to bind to same multicast group. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int udp_open_bind_join(const IPvX& local_addr, uint16_t local_port, const IPvX& mcast_addr, uint8_t ttl, bool reuse, string& error_msg) = 0; /** * Create a bound and connected TCP socket. * * @param local_addr the interface address to bind socket to. * @param local_port the port to bind socket to. * @param remote_addr the address to connect to. * @param remote_port the remote port to connect to. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int tcp_open_bind_connect(const IPvX& local_addr, uint16_t local_port, const IPvX& remote_addr, uint16_t remote_port, string& error_msg) = 0; /** * Create a bound and connected UDP socket. * * @param local_addr the interface address to bind socket to. * @param local_port the port to bind socket to. * @param remote_addr the address to connect to. * @param remote_port the remote port to connect to. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int udp_open_bind_connect(const IPvX& local_addr, uint16_t local_port, const IPvX& remote_addr, uint16_t remote_port, string& error_msg) = 0; /** * Create a bound, and optionally connected, UDP broadcast socket. * * @param ifname the interface name to bind socket to. * @param vifname the vif to bind socket to. * @param local_port the port to bind socket to. * @param remote_port the remote port to connect to. * @param reuse allow other sockets to bind to same port. * @param limited set the socket up for transmission to the limited * broadcast address 255.255.255.255. * @param connected connect the socket for use with send() not sendto(). * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int udp_open_bind_broadcast(const string& ifname, const string& vifname, uint16_t local_port, uint16_t remote_port, bool reuse, bool limited, bool connected, string& error_msg) = 0; /** * Bind a socket. * * @param local_addr the interface address to bind socket to. * @param local_port the port to bind socket to. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int bind(const IPvX& local_addr, uint16_t local_port, string& error_msg) = 0; /** * Join multicast group on already bound socket. * * @param mcast_addr group to join. * @param join_if_addr interface address to perform join on. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int udp_join_group(const IPvX& mcast_addr, const IPvX& join_if_addr, string& error_msg) = 0; /** * Leave multicast group on already bound socket. * * @param mcast_addr group to leave. * @param leave_if_addr interface address to perform leave on. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int udp_leave_group(const IPvX& mcast_addr, const IPvX& leave_if_addr, string& error_msg) = 0; /** * Close socket. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int close(string& error_msg) = 0; /** * Listen for inbound connections on socket. * * When a connection request is received the socket creator will receive * notification. * * @param backlog the maximum number of pending connections. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int tcp_listen(uint32_t backlog, string& error_msg) = 0; /** * Enable a UDP socket for datagram reception. * * When a connection request is received the socket creator will receive * notification. * * @param backlog the maximum number of pending connections. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int udp_enable_recv(string& error_msg) = 0; /** * Send data on socket. * * @param data block of data to be sent. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int send(const vector& data, string& error_msg) = 0; /** * Send data on socket to a given destination. * * The packet is not routed as the forwarding engine sending the packet * may not have access to the full routing table. * * @param remote_addr destination address for data. * @param remote_port destination port for data. * @param data block of data to be sent. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int send_to(const IPvX& remote_addr, uint16_t remote_port, const vector& data, string& error_msg) = 0; /** * Send data on socket to a given multicast group from a given interface. * * @param group_addr destination address for data. * @param group_port destination port for data. * @param ifaddr interface address. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int send_from_multicast_if(const IPvX& group_addr, uint16_t group_port, const IPvX& ifaddr, const vector& data, string& error_msg) = 0; /** * Set a named socket option with an integer value. * * @param optname name of option to be set. Valid values are: * "onesbcast" (IPv4 only) * "receive_broadcast" (IPv4 only) * "reuseport" * "send_broadcast" (IPv4 only) * "tos" (IPv4 only) * "ttl" * "multicast_loopback" * "multicast_ttl" * @param optval value of option to be set. If value is logically boolean * then zero represents false and any non-zero value true. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int set_socket_option(const string& optname, uint32_t optval, string& error_msg) = 0; /** * Set a named socket option with a string value. * * @param optname name of option to be set. Valid values are: * "bindtodevice" * @param optval value of option to be set. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int set_socket_option(const string& optname, const string& optval, string& error_msg) = 0; /** * Accept or reject a pending connection. * * @param is_accepted if true, the connection is accepted, otherwise is * rejected. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int accept_connection(bool is_accepted, string& error_msg) = 0; protected: /** * Data received event. * * @param if_name the interface name the packet arrived on, if known. * If unknown, then it is an empty string. * @param vif_name the vif name the packet arrived on, if known. * If unknown, then it is an empty string. * @param src_host the originating host IP address. * @param src_port the originating host port number. * @param data the data received. */ virtual void recv_event(const string& if_name, const string& vif_name, const IPvX& src_host, uint16_t src_port, const vector& data); /** * Inbound connection request received event. * * It applies only to TCP sockets. * * @param src_host the originating host IP address. * @param src_port the originating host port number. * @param new_io_tcpudp the handler for the new connection. */ virtual void inbound_connect_event(const IPvX& src_host, uint16_t src_port, IoTcpUdp* new_io_tcpudp); /** * Outgoing connection request completed event. * * It applies only to TCP sockets. */ virtual void outgoing_connect_event(); /** * Error occured event. * * @param error a textual description of the error. * @param fatal indication of whether socket is shutdown because of * error. */ virtual void error_event(const string& error, bool fatal); /** * Connection closed by peer event. * * It applies only to TCP sockets. * This method is not called if the socket is gracefully closed * through close(). */ virtual void disconnect_event(); // Misc other state bool _is_running; private: IoTcpUdpManager& _io_tcpudp_manager; // The I/O TCP/UDP manager FeaDataPlaneManager& _fea_data_plane_manager; // The data plane manager EventLoop& _eventloop; // The event loop to use const IfTree& _iftree; // The interface tree to use const int _family; // The address family const bool _is_tcp; // If true, this is TCP, otherwise UDP IoTcpUdpReceiver* _io_tcpudp_receiver; // The registered receiver }; /** * @short A base class for I/O TCP/UDP data receiver. * * The real receiver must inherit from this class and register with the * corresponding IoTcpUdp entity to receive the TCP/UDP data and data-related * events. * @see IoTcpUdp. */ class IoTcpUdpReceiver { public: /** * Default constructor. */ IoTcpUdpReceiver() {} /** * Virtual destructor. */ virtual ~IoTcpUdpReceiver() {} /** * Data received event. * * @param if_name the interface name the packet arrived on, if known. * If unknown, then it is an empty string. * @param vif_name the vif name the packet arrived on, if known. * If unknown, then it is an empty string. * @param src_host the originating host IP address. * @param src_port the originating host port number. * @param data the data received. */ virtual void recv_event(const string& if_name, const string& vif_name, const IPvX& src_host, uint16_t src_port, const vector& data) = 0; /** * Inbound connection request received event. * * It applies only to TCP sockets. * * @param src_host the originating host IP address. * @param src_port the originating host port number. * @param new_io_tcpudp the handler for the new connection. */ virtual void inbound_connect_event(const IPvX& src_host, uint16_t src_port, IoTcpUdp* Xnew_io_tcpudp) = 0; /** * Outgoing connection request completed event. * * It applies only to TCP sockets. */ virtual void outgoing_connect_event() = 0; /** * Error occured event. * * @param error a textual description of the error. * @param fatal indication of whether socket is shutdown because of * error. */ virtual void error_event(const string& error, bool fatal) = 0; /** * Connection closed by peer event. * * It applies only to TCP sockets. * This method is not called if the socket is gracefully closed * through close(). */ virtual void disconnect_event() = 0; }; #endif // __FEA_IO_TCPUDP_HH__ xorp/fea/libfeaclient_bridge.hh0000664000076400007640000000666711540225525016711 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/libfeaclient_bridge.hh,v 1.18 2008/10/02 21:56:49 bms Exp $ #ifndef __FEA_LIBFEACLIENT_BRIDGE_HH__ #define __FEA_LIBFEACLIENT_BRIDGE_HH__ #include "ifconfig_reporter.hh" class IfConfigUpdateReplicator; class IfMgrXrlReplicationManager; class IfTree; class XrlRouter; /** * @short Bridge class to intervene between the FEA's interface * manager and libfeaclient. * * The LibFeaClientBridge takes updates received from the FEA's * interface manager and forwards them to registered remote * libfeaclient users. For each update received, the bridge gets the * all the state associated with the item being updated, and pushes it * into a contained instance of an @ref IfMgrXrlReplicationManager. * If the data pushed into the IfMgrXrlReplicationManager triggers * state changes in it's internal interface config representation, it * forwards the changes to remote observers. * * In addition to arranging to plumb the LibFeaClientBridge into the * FEA to receive updates, it is imperative that the underlying IfTree * object used represent state be available to the bridge. The bridge * is made aware of this object through @ref iftree. Failure to * call method before an update is received will cause a fatal error. */ class LibFeaClientBridge : public IfConfigUpdateReporterBase { public: LibFeaClientBridge(XrlRouter& rtr, IfConfigUpdateReplicator& update_replicator); ~LibFeaClientBridge(); /** * Add named Xrl target to list to receive libfeaclient updates. * * @param xrl_target_instance_name Xrl target instance name. * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_libfeaclient_mirror(const string& xrl_target_instance_name); /** * Remove named Xrl target from the list to receive libfeaclient updates. * * @param xrl_target_instance_name Xrl target instance name. * @return XORP_OK on success, otherwise XORP_ERROR. */ int remove_libfeaclient_mirror(const string& xrl_target_instance_name); protected: void interface_update(const string& ifname, const Update& update); void vif_update(const string& ifname, const string& vifname, const Update& update); void vifaddr4_update(const string& ifname, const string& vifname, const IPv4& addr, const Update& update); #ifdef HAVE_IPV6 void vifaddr6_update(const string& ifname, const string& vifname, const IPv6& addr, const Update& update); #endif void updates_completed(); protected: IfMgrXrlReplicationManager* _rm; }; #endif // __FEA_LIBFEACLIENT_BRIDGE_HH__ xorp/fea/xrl_io_tcpudp_manager.cc0000664000076400007640000002175111540225525017276 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2007-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxipc/xrl_router.hh" #include "xrl/interfaces/socket4_user_xif.hh" #ifdef HAVE_IPV6 #include "xrl/interfaces/socket6_user_xif.hh" #endif #include "xrl_io_tcpudp_manager.hh" XrlIoTcpUdpManager::XrlIoTcpUdpManager(IoTcpUdpManager& io_tcpudp_manager, XrlRouter& xrl_router) : IoTcpUdpManagerReceiver(), _io_tcpudp_manager(io_tcpudp_manager), _xrl_router(xrl_router) { _io_tcpudp_manager.set_io_tcpudp_manager_receiver(this); } XrlIoTcpUdpManager::~XrlIoTcpUdpManager() { _io_tcpudp_manager.set_io_tcpudp_manager_receiver(NULL); } void XrlIoTcpUdpManager::recv_event(const string& receiver_name, const string& sockid, const string& if_name, const string& vif_name, const IPvX& src_host, uint16_t src_port, const vector& data) { if (src_host.is_ipv4()) { // // Instantiate client sending interface // XrlSocket4UserV0p1Client cl(&xrl_router()); // // Send notification // cl.send_recv_event(receiver_name.c_str(), sockid, if_name, vif_name, src_host.get_ipv4(), src_port, data, callback(this, &XrlIoTcpUdpManager::xrl_send_recv_event_cb, src_host.af(), receiver_name)); } #ifdef HAVE_IPV6 if (src_host.is_ipv6()) { // // Instantiate client sending interface // XrlSocket6UserV0p1Client cl(&xrl_router()); // // Send notification // cl.send_recv_event(receiver_name.c_str(), sockid, if_name, vif_name, src_host.get_ipv6(), src_port, data, callback(this, &XrlIoTcpUdpManager::xrl_send_recv_event_cb, src_host.af(), receiver_name)); } #endif } void XrlIoTcpUdpManager::inbound_connect_event(const string& receiver_name, const string& sockid, const IPvX& src_host, uint16_t src_port, const string& new_sockid) { if (src_host.is_ipv4()) { // // Instantiate client sending interface // XrlSocket4UserV0p1Client cl(&xrl_router()); // // Send notification // cl.send_inbound_connect_event(receiver_name.c_str(), sockid, src_host.get_ipv4(), src_port, new_sockid, callback(this, &XrlIoTcpUdpManager::xrl_send_inbound_connect_event_cb, src_host.af(), new_sockid, receiver_name)); } #ifdef HAVE_IPV6 if (src_host.is_ipv6()) { // // Instantiate client sending interface // XrlSocket6UserV0p1Client cl(&xrl_router()); // // Send notification // cl.send_inbound_connect_event(receiver_name.c_str(), sockid, src_host.get_ipv6(), src_port, new_sockid, callback(this, &XrlIoTcpUdpManager::xrl_send_inbound_connect_event_cb, src_host.af(), new_sockid, receiver_name)); } #endif } void XrlIoTcpUdpManager::outgoing_connect_event(int family, const string& receiver_name, const string& sockid) { if (family == IPv4::af()) { // // Instantiate client sending interface // XrlSocket4UserV0p1Client cl(&xrl_router()); // // Send notification // cl.send_outgoing_connect_event(receiver_name.c_str(), sockid, callback(this, &XrlIoTcpUdpManager::xrl_send_outgoing_connect_event_cb, family, receiver_name)); } #ifdef HAVE_IPV6 if (family == IPv6::af()) { // // Instantiate client sending interface // XrlSocket6UserV0p1Client cl(&xrl_router()); // // Send notification // cl.send_outgoing_connect_event(receiver_name.c_str(), sockid, callback(this, &XrlIoTcpUdpManager::xrl_send_outgoing_connect_event_cb, family, receiver_name)); } #endif } void XrlIoTcpUdpManager::error_event(int family, const string& receiver_name, const string& sockid, const string& error, bool fatal) { if (family == IPv4::af()) { // // Instantiate client sending interface // XrlSocket4UserV0p1Client cl(&xrl_router()); // // Send notification // cl.send_error_event(receiver_name.c_str(), sockid, error, fatal, callback(this, &XrlIoTcpUdpManager::xrl_send_error_event_cb, family, receiver_name)); } #ifdef HAVE_IPV6 if (family == IPv6::af()) { // // Instantiate client sending interface // XrlSocket6UserV0p1Client cl(&xrl_router()); // // Send notification // cl.send_error_event(receiver_name.c_str(), sockid, error, fatal, callback(this, &XrlIoTcpUdpManager::xrl_send_error_event_cb, family, receiver_name)); } #endif } void XrlIoTcpUdpManager::disconnect_event(int family, const string& receiver_name, const string& sockid) { if (family == IPv4::af()) { // // Instantiate client sending interface // XrlSocket4UserV0p1Client cl(&xrl_router()); // // Send notification // cl.send_disconnect_event(receiver_name.c_str(), sockid, callback(this, &XrlIoTcpUdpManager::xrl_send_disconnect_event_cb, family, receiver_name)); } #ifdef HAVE_IPV6 if (family == IPv6::af()) { // // Instantiate client sending interface // XrlSocket6UserV0p1Client cl(&xrl_router()); // // Send notification // cl.send_disconnect_event(receiver_name.c_str(), sockid, callback(this, &XrlIoTcpUdpManager::xrl_send_disconnect_event_cb, family, receiver_name)); } #endif } void XrlIoTcpUdpManager::xrl_send_recv_event_cb(const XrlError& xrl_error, int family, string receiver_name) { UNUSED(family); if (xrl_error == XrlError::OKAY()) return; debug_msg("xrl_send_recv_event_cb: error %s\n", xrl_error.str().c_str()); // // Sending Xrl generated an error. // // Remove all communication handlers associated with this receiver. // _io_tcpudp_manager.instance_death(receiver_name); } void XrlIoTcpUdpManager::xrl_send_inbound_connect_event_cb(const XrlError& xrl_error, const bool* accept, int family, string sockid, string receiver_name) { if (xrl_error == XrlError::OKAY()) { string error_msg; bool is_accepted = *accept; if (_io_tcpudp_manager.accept_connection(family, sockid, is_accepted, error_msg) != XORP_OK) { XLOG_ERROR("Error with %s a connection: %s", (is_accepted)? "accept" : "reject", error_msg.c_str()); } return; } debug_msg("xrl_send_inbound_connect_event_cb: error %s\n", xrl_error.str().c_str()); // // Sending Xrl generated an error. // // Remove all communication handlers associated with this receiver. // _io_tcpudp_manager.instance_death(receiver_name); } void XrlIoTcpUdpManager::xrl_send_outgoing_connect_event_cb(const XrlError& xrl_error, int family, string receiver_name) { UNUSED(family); if (xrl_error == XrlError::OKAY()) return; debug_msg("xrl_send_outgoing_connect_event_cb: error %s\n", xrl_error.str().c_str()); // // Sending Xrl generated an error. // // Remove all communication handlers associated with this receiver. // _io_tcpudp_manager.instance_death(receiver_name); } void XrlIoTcpUdpManager::xrl_send_error_event_cb(const XrlError& xrl_error, int family, string receiver_name) { UNUSED(family); if (xrl_error == XrlError::OKAY()) return; debug_msg("xrl_send_error_event_cb: error %s\n", xrl_error.str().c_str()); // // Sending Xrl generated an error. // // Remove all communication handlers associated with this receiver. // _io_tcpudp_manager.instance_death(receiver_name); } void XrlIoTcpUdpManager::xrl_send_disconnect_event_cb(const XrlError& xrl_error, int family, string receiver_name) { UNUSED(family); if (xrl_error == XrlError::OKAY()) return; debug_msg("xrl_send_disconnect_event_cb: error %s\n", xrl_error.str().c_str()); // // Sending Xrl generated an error. // // Remove all communication handlers associated with this receiver. // _io_tcpudp_manager.instance_death(receiver_name); } xorp/fea/MakefileRootCheck.am0000664000076400007640000000105711421137511016250 0ustar greearbgreearb## Process this file with automake to produce Makefile.in. ## ## $XORP: xorp/fea/MakefileRootCheck.am,v 1.1 2003/10/22 23:22:45 pavlin Exp $ ## # # This makefile contains information about tests that must be run as a root # # -- Test Programs and scripts TESTS = test_add_route.sh test_config_interface.sh ################################################################ ## ## Everything past here is useful to the maintainer, but probably not ## to anybody else ## # A hook to propagate the "-f filename" argument AM_MAKEFLAGS = -f MakefileRootCheck xorp/fea/io_link.cc0000664000076400007640000001733011540225525014353 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2007-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/mac.hh" #include "libproto/packet.hh" #include "fea/iftree.hh" #include "io_link.hh" // // FEA I/O link raw communication base class implementation. // IoLink::IoLink(FeaDataPlaneManager& fea_data_plane_manager, const IfTree& iftree, const string& if_name, const string& vif_name, uint16_t ether_type, const string& filter_program) : _is_running(false), _io_link_manager(fea_data_plane_manager.io_link_manager()), _fea_data_plane_manager(fea_data_plane_manager), _eventloop(fea_data_plane_manager.eventloop()), _iftree(iftree), _if_name(if_name), _vif_name(vif_name), _ether_type(ether_type), _filter_program(filter_program), _io_link_receiver(NULL), _is_log_trace(false) { } IoLink::~IoLink() { } void IoLink::register_io_link_receiver(IoLinkReceiver* io_link_receiver) { _io_link_receiver = io_link_receiver; } void IoLink::unregister_io_link_receiver() { _io_link_receiver = NULL; } void IoLink::recv_packet(const Mac& src_address, const Mac& dst_address, uint16_t ether_type, const vector& payload) { if (_io_link_receiver == NULL) { // XXX: should happen only during transient setup stage return; } XLOG_TRACE(is_log_trace(), "Received link-level packet: " "src = %s dst = %s EtherType = 0x%x payload length = %u", src_address.str().c_str(), dst_address.str().c_str(), ether_type, XORP_UINT_CAST(payload.size())); _io_link_receiver->recv_packet(src_address, dst_address, ether_type, payload); } void IoLink::recv_ethernet_packet(const uint8_t* packet, size_t packet_size) { Mac src_address; Mac dst_address; uint16_t ether_type = 0; size_t payload_size = 0; size_t payload_offset = 0; const uint8_t* ptr = packet; // NOTE: The padded bytes on small packets may not be delivered to // off the NIC, so just make sure we have enough room for the ethernet // header and one byte extra for payload (or dsap). I can't think of // any reason to accept a frame with ONLY an ethernet header..but if there // is one, we can relax the +1 and add safety checks below. static unsigned int min_frame_size = (Mac::ADDR_BYTELEN * 2 + 2 + 1); if (packet_size < min_frame_size) { XLOG_WARNING("Received packet on interface %s vif %s: " "packet is too short " "(captured %u expecting at least %u octets)", if_name().c_str(), vif_name().c_str(), XORP_UINT_CAST(packet_size), min_frame_size); return; // Error } // Extract the MAC destination and source address dst_address.copy_in(ptr); ptr += Mac::ADDR_BYTELEN; src_address.copy_in(ptr); ptr += Mac::ADDR_BYTELEN; // // Extract the EtherType // // XXX: The EtherType field could be either type or length // for Ethernet II (DIX) and IEEE 802.2 LLC frames correspondingly. // ether_type = extract_16(ptr); ptr += sizeof(uint16_t); if (ether_type < ETHERNET_LENGTH_TYPE_THRESHOLD) { // // This is a IEEE 802.2 LLC frame // // // XXX: Return the DSAP from the LLC header as an EtherType value. // Note that there is no colusion, because the DSAP values are // in the range [0, 255], while the EtherType values are in // the range [1536, 65535]. // uint8_t dsap = extract_8(ptr); ether_type = dsap; // // XXX: Don't move the ptr, because we only peek into the LLC // header to get the DSAP value, but keep the header as // part of the payload to the user. // } // Calculate the payload offset and size payload_offset = ptr - packet; payload_size = packet_size - payload_offset; // Process the result // TODO: get rid of this memory copy somehow. vector payload(payload_size); memcpy(&payload[0], packet + payload_offset, payload_size); recv_packet(src_address, dst_address, ether_type, payload); } int IoLink::prepare_ethernet_packet(const Mac& src_address, const Mac& dst_address, uint16_t ether_type, const vector& payload, vector& packet, string& error_msg) { size_t packet_size = 0; size_t pad_size = 0; uint8_t* ptr; const IfTreeInterface* ifp = NULL; const IfTreeVif* vifp = NULL; // // Test whether the interface/vif is enabled // ifp = iftree().find_interface(if_name()); if (ifp == NULL) { error_msg = c_format("No interface %s", if_name().c_str()); return (XORP_ERROR); } vifp = ifp->find_vif(vif_name()); if (vifp == NULL) { error_msg = c_format("No interface %s vif %s", if_name().c_str(), vif_name().c_str()); return (XORP_ERROR); } if (! ifp->enabled()) { error_msg = c_format("Interface %s is down", ifp->ifname().c_str()); return (XORP_ERROR); } if (! vifp->enabled()) { error_msg = c_format("Interface %s vif %s is down", ifp->ifname().c_str(), vifp->vifname().c_str()); return (XORP_ERROR); } // // Prepare the packet // packet.resize(L2_MAX_PACKET_SIZE); ptr = &packet[0]; // // Prepare the Ethernet header // dst_address.copy_out(ptr); ptr += Mac::ADDR_BYTELEN; src_address.copy_out(ptr); ptr += Mac::ADDR_BYTELEN; // // The EtherType // // XXX: The EtherType field could be either type or length // for Ethernet II (DIX) and IEEE 802.2 LLC frames correspondingly. // if (ether_type < ETHERNET_LENGTH_TYPE_THRESHOLD) { // A IEEE 802.2 LLC frame, hence embed the length of the payload embed_16(ptr, payload.size()); ptr += sizeof(uint16_t); } else { // An Ethernet II (DIX frame), hence embed the EtherType itself embed_16(ptr, ether_type); ptr += sizeof(uint16_t); } // // Calculate and test the packet size // packet_size = (ptr - &packet[0]) + payload.size(); if (ether_type < ETHERNET_LENGTH_TYPE_THRESHOLD) { // A IEEE 802.2 LLC frame, hence add padding if necessary if (packet_size < ETHERNET_MIN_FRAME_SIZE) pad_size = ETHERNET_MIN_FRAME_SIZE - packet_size; } if (packet_size > packet.size()) { error_msg = c_format("Sending packet from %s to %s EtherType %u" "on interface %s vif %s failed: " "too much data: %u octets (max = %u)", src_address.str().c_str(), dst_address.str().c_str(), ether_type, if_name().c_str(), vif_name().c_str(), XORP_UINT_CAST(packet_size), XORP_UINT_CAST(packet.size())); return (XORP_ERROR); } // // Copy the payload to the data buffer // packet.resize(packet_size + pad_size); memcpy(ptr, &payload[0], payload.size()); ptr += payload.size(); // Add the pad if necessary if (pad_size > 0) { memset(ptr, 0, pad_size); ptr += pad_size; } return (XORP_OK); } xorp/fea/xrl_fea_node.cc0000664000076400007640000001230011540225525015344 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2007-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // FEA (Forwarding Engine Abstraction) with XRL front-end node implementation. // #include "fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "xrl_fea_node.hh" #include "libxipc/xrl_std_router.hh" #include "cli/xrl_cli_node.hh" #include "fibconfig.hh" #include "ifconfig.hh" #include "libfeaclient_bridge.hh" #include "xrl_mfea_node.hh" XrlFeaNode::XrlFeaNode(EventLoop& eventloop, const string& xrl_fea_targetname, const string& xrl_finder_targetname, const string& finder_hostname, uint16_t finder_port, bool is_dummy) : _eventloop(eventloop), _xrl_router(eventloop, xrl_fea_targetname.c_str(), finder_hostname.c_str(), finder_port), _xrl_fea_io(eventloop, _xrl_router, xrl_finder_targetname), _fea_node(eventloop, _xrl_fea_io, is_dummy), _lib_fea_client_bridge(_xrl_router, _fea_node.ifconfig().ifconfig_update_replicator()), _xrl_fib_client_manager(_fea_node.fibconfig(), _xrl_router), _xrl_io_link_manager(_fea_node.io_link_manager(), _xrl_router), _xrl_io_ip_manager(_fea_node.io_ip_manager(), _xrl_router), _xrl_io_tcpudp_manager(_fea_node.io_tcpudp_manager(), _xrl_router), _cli_node4(AF_INET, XORP_MODULE_CLI, _eventloop), _xrl_cli_node(_eventloop, _cli_node4.module_name(), finder_hostname, finder_port, xrl_finder_targetname, _cli_node4), _xrl_mfea_node4(_fea_node, AF_INET, XORP_MODULE_MFEA, _eventloop, xorp_module_name(AF_INET, XORP_MODULE_MFEA), finder_hostname, finder_port, xrl_finder_targetname), #ifdef HAVE_IPV6_MULTICAST _xrl_mfea_node6(_fea_node, AF_INET6, XORP_MODULE_MFEA, _eventloop, xorp_module_name(AF_INET6, XORP_MODULE_MFEA), finder_hostname, finder_port, xrl_finder_targetname), #endif _xrl_fea_target(_eventloop, _fea_node, _xrl_router, #ifndef XORP_DISABLE_PROFILE _fea_node.profile(), #endif _xrl_fib_client_manager, _lib_fea_client_bridge), _xrl_finder_targetname(xrl_finder_targetname) { _cli_node4.set_cli_port(0); // XXX: disable CLI telnet access } XrlFeaNode::~XrlFeaNode() { shutdown(); } int XrlFeaNode::startup() { wait_until_xrl_router_is_ready(eventloop(), xrl_router()); if (! fea_node().is_dummy()) { // XXX: The multicast-related code doesn't have dummy mode (yet) wait_until_xrl_router_is_ready(eventloop(), _xrl_cli_node.xrl_router()); wait_until_xrl_router_is_ready(eventloop(), _xrl_mfea_node4.xrl_router()); #ifdef HAVE_IPV6_MULTICAST wait_until_xrl_router_is_ready(eventloop(), _xrl_mfea_node6.xrl_router()); #endif } xrl_fea_io().startup(); fea_node().startup(); xrl_fea_target().startup(); if (! fea_node().is_dummy()) { // XXX: temporary enable the built-in CLI access _xrl_cli_node.enable_cli(); _xrl_cli_node.start_cli(); _xrl_mfea_node4.enable_mfea(); // _xrl_mfea_node4.startup(); _xrl_mfea_node4.enable_cli(); _xrl_mfea_node4.start_cli(); #ifdef HAVE_IPV6_MULTICAST _xrl_mfea_node6.enable_mfea(); // _xrl_mfea_node6.startup(); _xrl_mfea_node6.enable_cli(); _xrl_mfea_node6.start_cli(); #endif } return (XORP_OK); } int XrlFeaNode::shutdown() { xrl_fea_io().shutdown(); fea_node().shutdown(); xrl_fea_target().shutdown(); if (! fea_node().is_dummy()) { _xrl_mfea_node4.shutdown(); #ifdef HAVE_IPV6_MULTICAST _xrl_mfea_node6.shutdown(); #endif } return (XORP_OK); } bool XrlFeaNode::is_running() const { if (_xrl_fea_io.is_running()) return (true); if (_fea_node.is_running()) return (true); if (_xrl_fea_target.is_running()) return (true); if (! _fea_node.is_dummy()) { if (! _xrl_mfea_node4.MfeaNode::is_down()) return (true); #ifdef HAVE_IPV6_MULTICAST if (! _xrl_mfea_node6.MfeaNode::is_down()) return (true); #endif } // // Test whether all XRL operations have completed // if (! _fea_node.is_dummy()) { if (_xrl_cli_node.xrl_router().pending()) return (true); if (_xrl_mfea_node4.xrl_router().pending()) return (true); #ifdef HAVE_IPV6_MULTICAST if (_xrl_mfea_node6.xrl_router().pending()) return (true); #endif } if (_xrl_router.pending()) return (true); return (false); } bool XrlFeaNode::is_shutdown_received() const { return (_xrl_fea_target.is_shutdown_received()); } xorp/fea/ifconfig.hh0000664000076400007640000003751611540225525014535 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/ifconfig.hh,v 1.81 2008/10/02 21:56:47 bms Exp $ #ifndef __FEA_IFCONFIG_HH__ #define __FEA_IFCONFIG_HH__ #include "libxorp/status_codes.h" #include "libxorp/transaction.hh" #include "ifconfig_property.hh" #include "ifconfig_get.hh" #include "ifconfig_set.hh" #include "ifconfig_observer.hh" #include "ifconfig_vlan_get.hh" #include "ifconfig_vlan_set.hh" #include "ifconfig_reporter.hh" #include "iftree.hh" class EventLoop; class FeaNode; class IfConfigErrorReporterBase; class IfConfigTransactionManager; class IfConfigUpdateReporterBase; class NexthopPortMapper; /** * Base class for pushing and pulling interface configurations down to * the particular system. */ class IfConfig { public: /** * Constructor. * * @param fea_node the FEA node. */ IfConfig(FeaNode& fea_node); /** * Virtual destructor (in case this class is used as a base class). */ virtual ~IfConfig(); /** * Get a reference to the @ref EventLoop instance. * * @return a reference to the @ref EventLoop instance. */ EventLoop& eventloop() { return _eventloop; } /** * Get the status code. * * @param reason the human-readable reason for any failure. * @return the status code. */ ProcessStatus status(string& reason) const; /** * Start interface-related transaction. * * @param tid the return-by-reference new transaction ID. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int start_transaction(uint32_t& tid, string& error_msg); /** * Commit interface-related transaction. * * @param tid the transaction ID. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int commit_transaction(uint32_t tid, string& error_msg); /** * Abort interface-related transaction. * * @param tid the transaction ID. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int abort_transaction(uint32_t tid, string& error_msg); /** * Add operation to interface-related transaction. * * @param tid the transaction ID. * @param op the operation to add. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_transaction_operation(uint32_t tid, const TransactionManager::Operation& op, string& error_msg); /** These are used by the Transaction Operation callbacks to actually do work. * All of these apply to the user_config unless otherwise specified. */ int add_interface(const char* ifname); int add_interface(const string& ifname) { return add_interface(ifname.c_str()); } int remove_interface(const char* ifname); int remove_interface(const string& ifname) { return remove_interface(ifname.c_str()); } int update_interface(const IfTreeInterface& iface); /** * Get a reference to the @ref NexthopPortMapper instance. * * @return a reference to the @ref NexthopPortMapper instance. */ NexthopPortMapper& nexthop_port_mapper() { return (_nexthop_port_mapper); } /** * Get the IfConfigUpdateReplicator instance. * * @return a reference to the IfConfigUpdateReplicator instance * (@ref IfConfigUpdateReplicator). */ IfConfigUpdateReplicator& ifconfig_update_replicator() { return (_ifconfig_update_replicator); } /** * Get the error reporter associated with IfConfig. * * @return the error reporter associated with IfConfig. */ IfConfigErrorReporterBase& ifconfig_error_reporter() { return (_ifconfig_error_reporter); } /** * Get a reference to the system interface configuration. * * @return a reference to the system interface configuration. */ IfTree& system_config() { return (_system_config); } /** * Set the system interface configuration. * * @param iftree the system interface configuration. */ void set_system_config(const IfTree& iftree) { _system_config = iftree; } /** * Get a reference to the user interface configuration. * * @return a reference to the user interface configuration. */ IfTree& user_config() { return (_user_config); } /** * Set the user interface configuration. * * @param iftree the user interface configuration. */ void set_user_config(const IfTree& iftree) { _user_config = iftree; } /** * Get a reference to the merged system-user configuration. * * @return a reference to the merged system-user configuration. */ IfTree& merged_config() { return (_merged_config); } /** * Set the merged system-user configuration. * * @param iftree the merged system-user configuration. */ void set_merged_config(const IfTree& iftree) { _merged_config = iftree; } /** * Get a reference to the original interface configuration on startup. * * @return a reference to the original interface configuration on startup. */ const IfTree& original_config() { return (_original_config); } /** * Test whether the original configuration should be restored on shutdown. * * @return true of the original configuration should be restored on * shutdown, otherwise false. */ bool restore_original_config_on_shutdown() const { return (_restore_original_config_on_shutdown); } /** * Set the flag whether the original configuration should be restored * on shutdown. * * @param v if true the original configuration should be restored on * shutdown. */ void set_restore_original_config_on_shutdown(bool v) { _restore_original_config_on_shutdown = v; } /** * Register @ref IfConfigProperty plugin. * * @param ifconfig_property the plugin to register. * @param is_exclusive if true, the plugin is registered as the * exclusive plugin, otherwise is added to the list of plugins. * @return XORP_OK on success, otherwise XORP_ERROR. */ int register_ifconfig_property(IfConfigProperty* ifconfig_property, bool is_exclusive); /** * Unregister @ref IfConfigProperty plugin. * * @param ifconfig_property the plugin to unregister. * @return XORP_OK on success, otherwise XORP_ERROR. */ int unregister_ifconfig_property(IfConfigProperty* ifconfig_property); /** * Register @ref IfConfigGet plugin. * * @param ifconfig_get the plugin to register. * @param is_exclusive if true, the plugin is registered as the * exclusive plugin, otherwise is added to the list of plugins. * @return XORP_OK on success, otherwise XORP_ERROR. */ int register_ifconfig_get(IfConfigGet* ifconfig_get, bool is_exclusive); /** * Unregister @ref IfConfigGet plugin. * * @param ifconfig_get the plugin to unregister. * @return XORP_OK on success, otherwise XORP_ERROR. */ int unregister_ifconfig_get(IfConfigGet* ifconfig_get); /** * Register @ref IfConfigSet plugin. * * @param ifconfig_set the plugin to register. * @param is_exclusive if true, the plugin is registered as the * exclusive plugin, otherwise is added to the list of plugins. * @return XORP_OK on success, otherwise XORP_ERROR. */ int register_ifconfig_set(IfConfigSet* ifconfig_set, bool is_exclusive); /** * Unregister @ref IfConfigSet plugin. * * @param ifconfig_set the plugin to unregister. * @return XORP_OK on success, otherwise XORP_ERROR. */ int unregister_ifconfig_set(IfConfigSet* ifconfig_set); /** * Register @ref IfConfigObserver plugin. * * @param ifconfig_observer the plugin to register. * @param is_exclusive if true, the plugin is registered as the * exclusive plugin, otherwise is added to the list of plugins. * @return XORP_OK on success, otherwise XORP_ERROR. */ int register_ifconfig_observer(IfConfigObserver* ifconfig_observer, bool is_exclusive); /** * Unregister @ref IfConfigObserver plugin. * * @param ifconfig_observer the plugin to unregister. * @return XORP_OK on success, otherwise XORP_ERROR. */ int unregister_ifconfig_observer(IfConfigObserver* ifconfig_observer); /** * Register @ref IfConfigVlanGet plugin. * * @param ifconfig_vlan_get the plugin to register. * @param is_exclusive if true, the plugin is registered as the * exclusive plugin, otherwise is added to the list of plugins. * @return XORP_OK on success, otherwise XORP_ERROR. */ int register_ifconfig_vlan_get(IfConfigVlanGet* ifconfig_vlan_get, bool is_exclusive); /** * Unregister @ref IfConfigVlanGet plugin. * * @param ifconfig_vlan_get the plugin to unregister. * @return XORP_OK on success, otherwise XORP_ERROR. */ int unregister_ifconfig_vlan_get(IfConfigVlanGet* ifconfig_vlan_get); /** * Register @ref IfConfigVlanSet plugin. * * @param ifconfig_vlan_set the plugin to register. * @param is_exclusive if true, the plugin is registered as the * exclusive plugin, otherwise is added to the list of plugins. * @return XORP_OK on success, otherwise XORP_ERROR. */ int register_ifconfig_vlan_set(IfConfigVlanSet* ifconfig_vlan_set, bool is_exclusive); /** * Unregister @ref IfConfigVlanSet plugin. * * @param ifconfig_vlan_set the plugin to unregister. * @return XORP_OK on success, otherwise XORP_ERROR. */ int unregister_ifconfig_vlan_set(IfConfigVlanSet* ifconfig_vlan_set); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int stop(string& error_msg); /** * Push interface configuration down to the system. * * Errors are reported via the ifconfig_error_reporter() instance. * Note that on return some of the interface configuration state * may be modified. * * @param iftree the interface configuration to be pushed down. * @return XORP_OK on success, otherwise XORP_ERROR. */ int push_config(const IfTree& iftree); /** * Get the error message associated with a push operation. * * @return the error message associated with a push operation. */ const string& push_error() const; /** * Pull up current interface configuration from the system, but only for interfaces * currently configured by the user. * If ifname is specified, pull only this interface's information. Use NULL to ignore. * if if_index is specified, pull only this interface's information. Use -1 to ignore. * * @return the current interface configuration from the system. */ const IfTree& pull_config(const char* ifname, int if_index); /** NOTE: This will cause the system to read *all* interface information into * the _pulled_config list. If you are trying to optimize by only pulling information * for interfaces configured in the local_config, do not use this method. * * @return a reference to the pulled interface configuration from the * system. */ const IfTree& full_pulled_config(); /** * Check IfTreeInterface and report updates to IfConfigUpdateReporter. * * @param fi the @ref IfTreeInterface interface instance to check. * @return true if there were updates to report, otherwise false. */ bool report_update(const IfTreeInterface& fi); /** * Check IfTreeVif and report updates to IfConfigUpdateReporter. * * @param fi the @ref IfTreeInterface interface instance to check. * @param fv the @ref IfTreeVif vif instance to check. * @return true if there were updates to report, otherwise false. */ bool report_update(const IfTreeInterface& fi, const IfTreeVif& fv); /** * Check IfTreeAddr4 and report updates to IfConfigUpdateReporter. * * @param fi the @ref IfTreeInterface interface instance to check. * @param fv the @ref IfTreeVif vif instance to check. * @param fa the @ref IfTreeAddr4 address instance to check. * @return true if there were updates to report, otherwise false. */ bool report_update(const IfTreeInterface& fi, const IfTreeVif& fv, const IfTreeAddr4& fa); #ifdef HAVE_IPV6 /** * Check IfTreeAddr6 and report updates to IfConfigUpdateReporter. * * @param fi the @ref IfTreeInterface interface instance to check. * @param fv the @ref IfTreeVif vif instance to check. * @param fa the @ref IfTreeAddr6 address instance to check. * @return true if there were updates to report, otherwise false. */ bool report_update(const IfTreeInterface& fi, const IfTreeVif& fv, const IfTreeAddr6& fa); #endif /** * Report that updates were completed to IfConfigUpdateReporter. */ void report_updates_completed(); /** * Check every item within IfTree and report updates to * IfConfigUpdateReporter. * * @param iftree the interface tree instance to check. */ void report_updates(IfTree& iftree); private: /** * Restore the interface configuration. * * @param old_user_config the old user configuration to restore. * @param old_system_config the old system configuration to restore. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int restore_config(const IfTree& old_user_config, const IfTree& old_system_config, string& error_msg); FeaNode& _fea_node; EventLoop& _eventloop; NexthopPortMapper& _nexthop_port_mapper; IfConfigTransactionManager* _itm; // The interface transaction manager IfTree _user_config; // The IfTree with the user config IfTree _system_config; /* The IfTree with the system config. * This usually only holds info for devices in _user_config */ IfTree _merged_config; // The merged system-user config IfTree _original_config; // The IfTree on startup bool _restore_original_config_on_shutdown; // If true, then // restore the original config on shutdown IfConfigUpdateReplicator _ifconfig_update_replicator; IfConfigErrorReporter _ifconfig_error_reporter; // // The registered plugins // list _ifconfig_property_plugins; list _ifconfig_gets; list _ifconfig_sets; list _ifconfig_observers; list _ifconfig_vlan_gets; list _ifconfig_vlan_sets; // // Misc other state // bool _is_running; }; #endif // __FEA_IFCONFIG_HH__ xorp/fea/ifconfig_vlan_set.hh0000664000076400007640000000716311703345405016424 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2007-2012 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __FEA_IFCONFIG_VLAN_SET_HH__ #define __FEA_IFCONFIG_VLAN_SET_HH__ #include "iftree.hh" #include "fea_data_plane_manager.hh" class IfConfig; class IfConfigVlanSet { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ IfConfigVlanSet(FeaDataPlaneManager& fea_data_plane_manager) : _is_running(false), _ifconfig(fea_data_plane_manager.ifconfig()), _fea_data_plane_manager(fea_data_plane_manager) {} /** * Virtual destructor. */ virtual ~IfConfigVlanSet() {} /** * Get the @ref IfConfig instance. * * @return the @ref IfConfig instance. */ IfConfig& ifconfig() { return _ifconfig; } /** * Get the @ref FeaDataPlaneManager instance. * * @return the @ref FeaDataPlaneManager instance. */ FeaDataPlaneManager& fea_data_plane_manager() { return _fea_data_plane_manager; } /** * Test whether this instance is running. * * @return true if the instance is running, otherwise false. */ virtual bool is_running() const { return _is_running; } /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg) = 0; /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg) = 0; /** * Add a VLAN. * * If an entry for the same VLAN already exists, is is overwritten * with the new information. * * @param pulled_ifp pointer to the interface information pulled from * the system. * @param config_iface reference to the interface with the information * to configure. * @param created_if Did we actually create a new interface in the OS? * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int config_add_vlan(const IfTreeInterface* pulled_ifp, const IfTreeInterface& config_iface, bool& created_if, string& error_msg) = 0; /** * Delete a VLAN. * * @param config_iface reference to the interface with the information * to configure. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int config_delete_vlan(const IfTreeInterface& config_iface, string& error_msg) = 0; protected: // Misc other state bool _is_running; private: IfConfig& _ifconfig; FeaDataPlaneManager& _fea_data_plane_manager; }; #endif // __FEA_IFCONFIG_VLAN_SET_HH__ xorp/fea/xrl_io_link_manager.hh0000664000076400007640000000417711421137511016744 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2007-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/xrl_io_link_manager.hh,v 1.5 2008/10/02 21:56:51 bms Exp $ #ifndef __FEA_XRL_IO_LINK_MANAGER_HH__ #define __FEA_XRL_IO_LINK_MANAGER_HH__ #include "io_link_manager.hh" class XrlRouter; /** * @short A class that is the bridge between the raw link I/O communications * and the XORP XRL interface. */ class XrlIoLinkManager : public IoLinkManagerReceiver { public: /** * Constructor. */ XrlIoLinkManager(IoLinkManager& io_link_manager, XrlRouter& xrl_router); /** * Destructor. */ virtual ~XrlIoLinkManager(); /** * Data received event. * * @param receiver_name the name of the receiver to send the * link-level packet to. * @param header the link-level header information. * @param payload the payload, everything after the link-level header. */ void recv_event(const string& receiver_name, const struct MacHeaderInfo& header, const vector& payload); private: XrlRouter& xrl_router() { return _xrl_router; } /** * Method to be called by XRL sending filter invoker */ void xrl_send_recv_cb(const XrlError& xrl_error, string receiver_name); IoLinkManager& _io_link_manager; XrlRouter& _xrl_router; }; #endif // __FEA_XRL_IO_LINK_MANAGER_HH__ xorp/fea/fibconfig_table_observer.hh0000664000076400007640000000622611540225525017747 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/fibconfig_table_observer.hh,v 1.14 2008/10/02 21:56:46 bms Exp $ #ifndef __FEA_FIBCONFIG_TABLE_OBSERVER_HH__ #define __FEA_FIBCONFIG_TABLE_OBSERVER_HH__ #include "fte.hh" #include "iftree.hh" #include "fea_data_plane_manager.hh" class FibConfig; class FibConfigTableObserver { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ FibConfigTableObserver(FeaDataPlaneManager& fea_data_plane_manager) : _is_running(false), _fibconfig(fea_data_plane_manager.fibconfig()), _fea_data_plane_manager(fea_data_plane_manager) {} /** * Virtual destructor. */ virtual ~FibConfigTableObserver() {} /** * Get the @ref FibConfig instance. * * @return the @ref FibConfig instance. */ FibConfig& fibconfig() { return _fibconfig; } /** * Get the @ref FeaDataPlaneManager instance. * * @return the @ref FeaDataPlaneManager instance. */ FeaDataPlaneManager& fea_data_plane_manager() { return _fea_data_plane_manager; } /** * Test whether this instance is running. * * @return true if the instance is running, otherwise false. */ virtual bool is_running() const { return _is_running; } /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg) = 0; /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg) = 0; /** * Receive data from the underlying system. * * @param buffer the buffer with the received data. */ virtual void receive_data(const vector& buffer) = 0; /** Routing table ID that we are interested in might have changed. Maybe something * can filter on this for increased efficiency. */ virtual int notify_table_id_change(uint32_t new_tbl) = 0; protected: // Misc other state bool _is_running; private: FibConfig& _fibconfig; FeaDataPlaneManager& _fea_data_plane_manager; }; #endif // __FEA_FIBCONFIG_TABLE_OBSERVER_HH__ xorp/fea/firewall_entry.hh0000664000076400007640000001567711540224223015775 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2008-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/firewall_entry.hh,v 1.3 2008/10/02 21:56:46 bms Exp $ #ifndef __FEA_FIREWALL_ENTRY_HH__ #define __FEA_FIREWALL_ENTRY_HH__ #include "libxorp/ipv4.hh" #include "libxorp/ipv6.hh" #include "libxorp/ipvx.hh" #include "libxorp/ipv4net.hh" #include "libxorp/ipv6net.hh" #include "libxorp/ipvxnet.hh" /** * @short Firewall Table Entry. * * Representation of a firewall table entry. */ class FirewallEntry { public: // Possible actions for firewall rules enum Action { ACTION_MIN = 0x00, // Min value for action ACTION_ANY = 0x00, // For match comparison during delete ACTION_NONE = 0x01, ACTION_PASS = 0x02, ACTION_DROP = 0x03, ACTION_REJECT = 0x04, ACTION_MAX = 0x05, // Max number of possible actions ACTION_INVALID = 0xff // Invalid string conversion }; // Matching values for firewall rules enum { RULE_NUMBER_DEFAULT = 0, IP_PROTOCOL_MIN = 0, IP_PROTOCOL_MAX = 255, IP_PROTOCOL_ANY = 0, PORT_MIN = 0, PORT_MAX = 65535, }; explicit FirewallEntry(int family) : _rule_number(RULE_NUMBER_DEFAULT), _src_network(family), _dst_network(family), _ip_protocol(IP_PROTOCOL_ANY), _src_port_begin(PORT_MIN), _src_port_end(PORT_MAX), _dst_port_begin(PORT_MIN), _dst_port_end(PORT_MAX), _action(ACTION_INVALID) {} #ifdef XORP_USE_USTL FirewallEntry() { FirewallEntry(AF_INET); } #endif FirewallEntry(uint32_t rule_number, const string& ifname, const string& vifname, const IPvXNet& src_network, const IPvXNet& dst_network, uint8_t ip_protocol, uint16_t src_port_begin, uint16_t src_port_end, uint16_t dst_port_begin, uint16_t dst_port_end, FirewallEntry::Action action) : _rule_number(rule_number), _ifname(ifname), _vifname(vifname), _src_network(src_network), _dst_network(dst_network), _ip_protocol(ip_protocol), _src_port_begin(src_port_begin), _src_port_end(src_port_end), _dst_port_begin(dst_port_begin), _dst_port_end(dst_port_end), _action(action) {} /** * Test whether this is an IPv4 entry. * * @return true if this is an IPv4 entry, otherwise false. */ bool is_ipv4() const { return _src_network.is_ipv4(); } /** * Test whether this is an IPv6 entry. * * @return true if this is an IPv6 entry, otherwise false. */ bool is_ipv6() const { return _src_network.is_ipv6(); } uint32_t rule_number() const { return _rule_number; } const string& ifname() const { return _ifname; } const string& vifname() const { return _vifname; } const IPvXNet& src_network() const { return _src_network; } const IPvXNet& dst_network() const { return _dst_network; } uint8_t ip_protocol() const { return _ip_protocol; } uint32_t src_port_begin() const { return _src_port_begin; } uint32_t src_port_end() const { return _src_port_end; } uint32_t dst_port_begin() const { return _dst_port_begin; } uint32_t dst_port_end() const { return _dst_port_end; } FirewallEntry::Action action() const { return _action; } /** * Reset all members. */ void zero() { _rule_number = RULE_NUMBER_DEFAULT; _ifname.erase(); _vifname.erase(); _src_network = IPvXNet(IPvX::ZERO(_src_network.af()), 0); _dst_network = IPvXNet(IPvX::ZERO(_dst_network.af()), 0); _ip_protocol = IP_PROTOCOL_ANY; _src_port_begin = PORT_MIN; _src_port_end = PORT_MAX; _dst_port_begin = PORT_MIN; _dst_port_end = PORT_MAX; _action = ACTION_INVALID; } /** * Comparison function for an exact match with the entry. * * Note that the action is masked off in the comparison, and only * the rule-match part of the tuple is evaluated. * * @return true if the rule-match portion of the entry is matched, * otherwise false. */ bool match(const FirewallEntry& other) const { return ((_rule_number == other.rule_number()) && (_ifname == other.ifname()) && (_vifname == other.vifname()) && (_src_network == other.src_network()) && (_dst_network == other.dst_network()) && (_ip_protocol == other.ip_protocol()) && (_src_port_begin == other.src_port_begin()) && (_src_port_end == other.src_port_end()) && (_dst_port_begin == other.dst_port_begin()) && (_dst_port_end == other.dst_port_end())); } /** * Convert firewall entry action value to a string representation. * * @param action the action to convert. * @return the string representation of the action value. */ static string action2str(FirewallEntry::Action action); /** * Convert string representation to a firewall entry action value. * * @param name the name of the action. It is one of the following * keywords: "none", "pass", "drop", "reject". * @return the firewall entry action value if the name is valid, * otherwise ACTION_INVALID. */ static FirewallEntry::Action str2action(const string& name); /** * @return a string representation of the entry. */ string str() const { return c_format("rule number = %u ifname = %s vifname = %s " "source network = %s destination network = %s " "IP protocol = %d source port begin = %u " "source port end = %u destination port begin = %u " "destination port end = %u action = %s", _rule_number, _ifname.c_str(), _vifname.c_str(), _src_network.str().c_str(), _dst_network.str().c_str(), _ip_protocol, _src_port_begin, _src_port_end, _dst_port_begin, _dst_port_end, action2str(_action).c_str()); } private: uint32_t _rule_number; // The rule number string _ifname; // Interface name string _vifname; // Virtual interface name IPvXNet _src_network; // Source network address prefix IPvXNet _dst_network; // Destination network address prefix uint8_t _ip_protocol; // IP protocol number: 1-255, // or 0 if wildcard uint16_t _src_port_begin; // Source TCP/UDP begin port: 0-65535 uint32_t _src_port_end; // Source TCP/UDP end port: 0-65535 uint32_t _dst_port_begin; // Dest. TCP/UDP begin port: 0-65535 uint32_t _dst_port_end; // Dest. TCP/UDP end port: 0-65535 FirewallEntry::Action _action; // The action }; #endif // __FEA_FIREWALL_ENTRY_HH__ xorp/fea/fea_node.cc0000664000076400007640000002434211540225524014467 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2007-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // FEA (Forwarding Engine Abstraction) node implementation. // #include "fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/eventloop.hh" #include "libcomm/comm_api.h" #include "fea/data_plane/managers/fea_data_plane_manager_bsd.hh" #ifdef XORP_USE_CLICK #include "fea/data_plane/managers/fea_data_plane_manager_click.hh" #endif #ifdef XORP_USE_FEA_DUMMY #include "fea/data_plane/managers/fea_data_plane_manager_dummy.hh" #endif #include "fea/data_plane/managers/fea_data_plane_manager_linux.hh" #include "fea/data_plane/managers/fea_data_plane_manager_windows.hh" #include "fea_io.hh" #include "fea_node.hh" #ifndef XORP_DISABLE_PROFILE #include "profile_vars.hh" #endif FeaNode::FeaNode(EventLoop& eventloop, FeaIo& fea_io, bool is_dummy) : _eventloop(eventloop), _is_running(false), _is_dummy(is_dummy), _ifconfig(*this), #ifndef XORP_DISABLE_FIREWALL _firewall_manager(*this, ifconfig().merged_config()), #endif _fibconfig(*this, _ifconfig.system_config(), _ifconfig.merged_config()), _io_link_manager(*this, ifconfig().merged_config()), _io_ip_manager(*this, ifconfig().merged_config()), _io_tcpudp_manager(*this, ifconfig().merged_config()), _fea_io(fea_io) { } FeaNode::~FeaNode() { shutdown(); } int FeaNode::startup() { string error_msg; _is_running = false; comm_init(); #ifndef XORP_DISABLE_PROFILE initialize_profiling_variables(_profile); #endif if (load_data_plane_managers(error_msg) != XORP_OK) { XLOG_FATAL("Cannot load the data plane manager(s): %s", error_msg.c_str()); } // // Startup managers // if (_ifconfig.start(error_msg) != XORP_OK) { XLOG_FATAL("Cannot start IfConfig: %s", error_msg.c_str()); } #if 0 // // XXX: The FirewallManager will be started only if needed // when firewall rules are installed. // if (_firewall_manager.start(error_msg) != XORP_OK) { // // XXX: Just print an error in case the firewall support // in the underlying system is not enabled. // XLOG_ERROR("Cannot start FirewallManager: %s", error_msg.c_str()); } #endif // 0 if (_fibconfig.start(error_msg) != XORP_OK) { XLOG_FATAL("Cannot start FibConfig: %s", error_msg.c_str()); } _is_running = true; return (XORP_OK); } int FeaNode::shutdown() { string error_msg; // // Gracefully stop the FEA // // TODO: this may not work if we depend on reading asynchronously // data from sockets. To fix this, we need to run the eventloop // until we get all the data we need. Tricky... // if (_fibconfig.stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop FibConfig: %s", error_msg.c_str()); } #ifndef XORP_DISABLE_FIREWALL if (_firewall_manager.stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop FirewallManager: %s", error_msg.c_str()); } #endif if (_ifconfig.stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop IfConfig: %s", error_msg.c_str()); } if (unload_data_plane_managers(error_msg) != XORP_OK) { XLOG_ERROR("Cannot unload the data plane manager(s): %s", error_msg.c_str()); } comm_exit(); _is_running = false; return (XORP_OK); } bool FeaNode::is_running() const { return (_is_running); } bool FeaNode::have_ipv4() const { if (_fea_data_plane_managers.empty()) return (false); // // XXX: We pull the information by using only the first data plane manager. // In the future we need to rething this and be more flexible. // return (_fea_data_plane_managers.front()->have_ipv4()); } bool FeaNode::have_ipv6() const { if (_fea_data_plane_managers.empty()) return (false); // // XXX: We pull the information by using only the first data plane manager. // In the future we need to rething this and be more flexible. // return (_fea_data_plane_managers.front()->have_ipv6()); } int FeaNode::register_data_plane_manager(FeaDataPlaneManager* fea_data_plane_manager, bool is_exclusive) { string dummy_error_msg; if (is_exclusive) { // Unload and delete the previous data plane managers unload_data_plane_managers(dummy_error_msg); } if ((fea_data_plane_manager != NULL) && (find(_fea_data_plane_managers.begin(), _fea_data_plane_managers.end(), fea_data_plane_manager) == _fea_data_plane_managers.end())) { _fea_data_plane_managers.push_back(fea_data_plane_manager); } return (XORP_OK); } int FeaNode::unregister_data_plane_manager(FeaDataPlaneManager* fea_data_plane_manager) { string dummy_error_msg; if (fea_data_plane_manager == NULL) return (XORP_ERROR); list::iterator iter; iter = find(_fea_data_plane_managers.begin(), _fea_data_plane_managers.end(), fea_data_plane_manager); if (iter == _fea_data_plane_managers.end()) return (XORP_ERROR); io_link_manager().unregister_data_plane_manager(fea_data_plane_manager); io_ip_manager().unregister_data_plane_manager(fea_data_plane_manager); io_tcpudp_manager().unregister_data_plane_manager(fea_data_plane_manager); fea_data_plane_manager->stop_manager(dummy_error_msg); _fea_data_plane_managers.erase(iter); delete fea_data_plane_manager; return (XORP_OK); } int FeaNode::load_data_plane_managers(string& error_msg) { string dummy_error_msg; FeaDataPlaneManager* fea_data_plane_manager = NULL; unload_data_plane_managers(dummy_error_msg); #if 0 // // TODO: XXX: PAVPAVPAV: sample code for dynamic loading // void* plugin_handle = dlopen(library_name.c_str(), RTLD_LAZY); if (plugin_handle == NULL) { error_msg = c_format("Cannot open library %s: %s", library_name.c_str(), dlerror()); return (XORP_ERROR); } XLOG_TRACE(false, "Loaded library %s", library_name.c_str()); typedef FeaDataPlaneManager* (*create_t)(FeaNode& fea_node); create_t create = (create_t)dlsym(plugin_handle, "create"); const char* dlsym_error = dlerror(); if (dlsym_error != NULL) { error_msg = c_format("Cannot load symbol \"create\": %s", dlsym_error); dlclose(plugin_handler); return (XORP_ERROR); } fea_data_plane_manager = create(*this); // // TODO: XXX: PAVPAVPAV: sample code for destroying the dynamic loaded // plugin. // typedef void (*destroy_t)(); destroy_t destroy = (destroy_t)dlsym(plugin_handle, "destroy"); const char* dlsym_error = dlerror(); if (dlsym_error != NULL) { error_msg = c_format("Cannot load symbol \"destroy\": %s", dlsym_error); return (XORP_ERROR); } destroy(); #endif // 0 if (is_dummy()) { #ifdef XORP_USE_FEA_DUMMY fea_data_plane_manager = new FeaDataPlaneManagerDummy(*this); #endif } else { #if defined(HOST_OS_MACOSX) || defined(HOST_OS_DRAGONFLYBSD) || defined(HOST_OS_FREEBSD) || defined(HOST_OS_NETBSD) || defined(HOST_OS_OPENBSD) fea_data_plane_manager = new FeaDataPlaneManagerBsd(*this); #elif defined(HOST_OS_LINUX) fea_data_plane_manager = new FeaDataPlaneManagerLinux(*this); #elif defined(HOST_OS_WINDOWS) fea_data_plane_manager = new FeaDataPlaneManagerWindows(*this); #else #error "No data plane manager to load: unknown system" #endif } if (register_data_plane_manager(fea_data_plane_manager, true) != XORP_OK) { error_msg = c_format("Failed to register the %s data plane manager", fea_data_plane_manager->manager_name().c_str()); delete fea_data_plane_manager; return (XORP_ERROR); } if (fea_data_plane_manager->start_manager(error_msg) != XORP_OK) { error_msg = c_format("Failed to start the %s data plane manager: %s", fea_data_plane_manager->manager_name().c_str(), error_msg.c_str()); unload_data_plane_managers(dummy_error_msg); return (XORP_ERROR); } if (fea_data_plane_manager->register_plugins(error_msg) != XORP_OK) { error_msg = c_format("Failed to register the %s data plane " "manager plugins: %s", fea_data_plane_manager->manager_name().c_str(), error_msg.c_str()); unload_data_plane_managers(dummy_error_msg); return (XORP_ERROR); } if (io_link_manager().register_data_plane_manager(fea_data_plane_manager, true) != XORP_OK) { error_msg = c_format("Failed to register the %s data plane " "manager with the I/O Link manager", fea_data_plane_manager->manager_name().c_str()); unload_data_plane_managers(dummy_error_msg); return (XORP_ERROR); } if (io_ip_manager().register_data_plane_manager(fea_data_plane_manager, true) != XORP_OK) { error_msg = c_format("Failed to register the %s data plane " "manager with the I/O IP manager", fea_data_plane_manager->manager_name().c_str()); unload_data_plane_managers(dummy_error_msg); return (XORP_ERROR); } if (io_tcpudp_manager().register_data_plane_manager(fea_data_plane_manager, true) != XORP_OK) { error_msg = c_format("Failed to register the %s data plane " "manager with the I/O TCP/UDP manager", fea_data_plane_manager->manager_name().c_str()); unload_data_plane_managers(dummy_error_msg); return (XORP_ERROR); } return (XORP_OK); } int FeaNode::unload_data_plane_managers(string& error_msg) { string dummy_error_msg; UNUSED(error_msg); // // Stop and delete the data plane managers // while (! _fea_data_plane_managers.empty()) { unregister_data_plane_manager(_fea_data_plane_managers.front()); } return (XORP_OK); } xorp/fea/firewall_manager.cc0000664000076400007640000004422611421137511016225 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2008-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "fea_node.hh" #include "iftree.hh" #include "firewall_manager.hh" #include "firewall_transaction.hh" // // Firewall configuration manager. // FirewallManager::FirewallManager(FeaNode& fea_node, const IfTree& iftree) : _fea_node(fea_node), _eventloop(fea_node.eventloop()), _iftree(iftree), _ftm(NULL), _next_token(0), _is_running(false) { _ftm = new FirewallTransactionManager(_eventloop); } FirewallManager::~FirewallManager() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the mechanism for manipulating " "the firewall table information: %s", error_msg.c_str()); } while (! _browse_db.empty()) { uint32_t token = _browse_db.begin()->first; delete_browse_state(token); } if (_ftm != NULL) { delete _ftm; _ftm = NULL; } } ProcessStatus FirewallManager::status(string& reason) const { if (_ftm->pending() > 0) { reason = "There are transactions pending"; return (PROC_NOT_READY); } return (PROC_READY); } int FirewallManager::start_transaction(uint32_t& tid, string& error_msg) { if (start(error_msg) != XORP_OK) { error_msg = c_format("Cannot start FirewallManager: %s", error_msg.c_str()); return (XORP_ERROR); } if (_ftm->start(tid) != true) { error_msg = c_format("Resource limit on number of pending transactions hit"); return (XORP_ERROR); } return (XORP_OK); } int FirewallManager::abort_transaction(uint32_t tid, string& error_msg) { if (_ftm->abort(tid) != true) { error_msg = c_format("Expired or invalid transaction ID presented"); return (XORP_ERROR); } // Cleanup state _added_entries.clear(); _replaced_entries.clear(); _deleted_entries.clear(); return (XORP_OK); } int FirewallManager::add_transaction_operation( uint32_t tid, const TransactionManager::Operation& op, string& error_msg) { uint32_t n_ops = 0; if (_ftm->retrieve_size(tid, n_ops) != true) { error_msg = c_format("Expired or invalid transaction ID presented"); return (XORP_ERROR); } // XXX: If necessary, check whether n_ops is above a pre-defined limit. // // In theory, resource shortage is the only thing that could get us here // if (_ftm->add(tid, op) != true) { error_msg = c_format("Unknown resource shortage"); return (XORP_ERROR); } return (XORP_OK); } int FirewallManager::commit_transaction(uint32_t tid, string& error_msg) { int ret_value = XORP_OK; // Cleanup leftover state _added_entries.clear(); _replaced_entries.clear(); _deleted_entries.clear(); if (_ftm->commit(tid) != true) { error_msg = c_format("Expired or invalid transaction ID presented"); return (XORP_ERROR); } if (_ftm->error().empty() != true) { error_msg = _ftm->error(); return (XORP_ERROR); } ret_value = update_entries(error_msg); // Cleanup state _added_entries.clear(); _replaced_entries.clear(); _deleted_entries.clear(); return (ret_value); } int FirewallManager::register_firewall_get(FirewallGet* firewall_get, bool is_exclusive) { if (is_exclusive) _firewall_gets.clear(); if ((firewall_get != NULL) && (find(_firewall_gets.begin(), _firewall_gets.end(), firewall_get) == _firewall_gets.end())) { _firewall_gets.push_back(firewall_get); } return (XORP_OK); } int FirewallManager::unregister_firewall_get(FirewallGet* firewall_get) { if (firewall_get == NULL) return (XORP_ERROR); list::iterator iter; iter = find(_firewall_gets.begin(), _firewall_gets.end(), firewall_get); if (iter == _firewall_gets.end()) return (XORP_ERROR); _firewall_gets.erase(iter); return (XORP_OK); } int FirewallManager::register_firewall_set(FirewallSet* firewall_set, bool is_exclusive) { string error_msg; if (is_exclusive) _firewall_sets.clear(); if ((firewall_set != NULL) && (find(_firewall_sets.begin(), _firewall_sets.end(), firewall_set) == _firewall_sets.end())) { _firewall_sets.push_back(firewall_set); // // XXX: Push the current config into the new method // if (firewall_set->is_running()) { list firewall_entry_list; if (get_table4(firewall_entry_list, error_msg) == XORP_OK) { if (firewall_set->set_table4(firewall_entry_list, error_msg) != XORP_OK) { XLOG_ERROR("Cannot push the current IPv4 firewall table " "into a new mechanism for setting the " "firewall table: %s", error_msg.c_str()); } } #ifdef HAVE_IPV6 firewall_entry_list.clear(); if (get_table6(firewall_entry_list, error_msg) == XORP_OK) { if (firewall_set->set_table6(firewall_entry_list, error_msg) != XORP_OK) { XLOG_ERROR("Cannot push the current IPv6 firewall table " "into a new mechanism for setting the " "firewall table: %s", error_msg.c_str()); } } #endif // HAVE_IPV6 } } return (XORP_OK); } int FirewallManager::unregister_firewall_set(FirewallSet* firewall_set) { if (firewall_set == NULL) return (XORP_ERROR); list::iterator iter; iter = find(_firewall_sets.begin(), _firewall_sets.end(), firewall_set); if (iter == _firewall_sets.end()) return (XORP_ERROR); _firewall_sets.erase(iter); return (XORP_OK); } int FirewallManager::start(string& error_msg) { list::iterator firewall_get_iter; list::iterator firewall_set_iter; if (_is_running) return (XORP_OK); // // Check whether all mechanisms are available // // XXX: The firewall mechanisms are optional hence we don't check // whether they are available. // #if 0 if (_firewall_gets.empty()) { error_msg = c_format("No mechanism to get firewall table entries"); return (XORP_ERROR); } if (_firewall_sets.empty()) { error_msg = c_format("No mechanism to set firewall table entries"); return (XORP_ERROR); } #endif // // Start the FirewallGet methods // for (firewall_get_iter = _firewall_gets.begin(); firewall_get_iter != _firewall_gets.end(); ++firewall_get_iter) { FirewallGet* firewall_get = *firewall_get_iter; if (firewall_get->start(error_msg) != XORP_OK) return (XORP_ERROR); } // // Start the FirewallSet methods // for (firewall_set_iter = _firewall_sets.begin(); firewall_set_iter != _firewall_sets.end(); ++firewall_set_iter) { FirewallSet* firewall_set = *firewall_set_iter; if (firewall_set->start(error_msg) != XORP_OK) return (XORP_ERROR); } _is_running = true; return (XORP_OK); } int FirewallManager::stop(string& error_msg) { list::iterator firewall_get_iter; list::iterator firewall_set_iter; int ret_value = XORP_OK; string error_msg2; if (! _is_running) return (XORP_OK); error_msg.erase(); // // Stop the FirewallSet methods // for (firewall_set_iter = _firewall_sets.begin(); firewall_set_iter != _firewall_sets.end(); ++firewall_set_iter) { FirewallSet* firewall_set = *firewall_set_iter; if (firewall_set->stop(error_msg2) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } } // // Stop the FirewallGet methods // for (firewall_get_iter = _firewall_gets.begin(); firewall_get_iter != _firewall_gets.end(); ++firewall_get_iter) { FirewallGet* firewall_get = *firewall_get_iter; if (firewall_get->stop(error_msg2) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } } _is_running = false; return (ret_value); } int FirewallManager::update_entries(string& error_msg) { list::iterator firewall_set_iter; if (_firewall_sets.empty()) { error_msg = c_format("No firewall plugin to set the entries"); return (XORP_ERROR); } for (firewall_set_iter = _firewall_sets.begin(); firewall_set_iter != _firewall_sets.end(); ++firewall_set_iter) { FirewallSet* firewall_set = *firewall_set_iter; if (firewall_set->update_entries(_added_entries, _replaced_entries, _deleted_entries, error_msg) != XORP_OK) return (XORP_ERROR); } return (XORP_OK); } int FirewallManager::add_entry(const FirewallEntry& firewall_entry, string& error_msg) { UNUSED(error_msg); _added_entries.push_back(firewall_entry); return (XORP_OK); } int FirewallManager::replace_entry(const FirewallEntry& firewall_entry, string& error_msg) { UNUSED(error_msg); _replaced_entries.push_back(firewall_entry); return (XORP_OK); } int FirewallManager::delete_entry(const FirewallEntry& firewall_entry, string& error_msg) { UNUSED(error_msg); _deleted_entries.push_back(firewall_entry); return (XORP_OK); } int FirewallManager::set_table4(const list& firewall_entry_list, string& error_msg) { list::iterator firewall_set_iter; if (_firewall_sets.empty()) { error_msg = c_format("No firewall plugin to set the entries"); return (XORP_ERROR); } for (firewall_set_iter = _firewall_sets.begin(); firewall_set_iter != _firewall_sets.end(); ++firewall_set_iter) { FirewallSet* firewall_set = *firewall_set_iter; if (firewall_set->set_table4(firewall_entry_list, error_msg) != XORP_OK) { return (XORP_ERROR); } } return (XORP_OK); } int FirewallManager::set_table6(const list& firewall_entry_list, string& error_msg) { list::iterator firewall_set_iter; if (_firewall_sets.empty()) { error_msg = c_format("No firewall plugin to set the entries"); return (XORP_ERROR); } for (firewall_set_iter = _firewall_sets.begin(); firewall_set_iter != _firewall_sets.end(); ++firewall_set_iter) { FirewallSet* firewall_set = *firewall_set_iter; if (firewall_set->set_table6(firewall_entry_list, error_msg) != XORP_OK) { return (XORP_ERROR); } } return (XORP_OK); } int FirewallManager::delete_all_entries4(string& error_msg) { list::iterator firewall_set_iter; if (_firewall_sets.empty()) { error_msg = c_format("No firewall plugin to set the entries"); return (XORP_ERROR); } for (firewall_set_iter = _firewall_sets.begin(); firewall_set_iter != _firewall_sets.end(); ++firewall_set_iter) { FirewallSet* firewall_set = *firewall_set_iter; if (firewall_set->delete_all_entries4(error_msg) != XORP_OK) return (XORP_ERROR); } return (XORP_OK); } int FirewallManager::delete_all_entries6(string& error_msg) { list::iterator firewall_set_iter; if (_firewall_sets.empty()) { error_msg = c_format("No firewall plugin to set the entries"); return (XORP_ERROR); } for (firewall_set_iter = _firewall_sets.begin(); firewall_set_iter != _firewall_sets.end(); ++firewall_set_iter) { FirewallSet* firewall_set = *firewall_set_iter; if (firewall_set->delete_all_entries6(error_msg) != XORP_OK) return (XORP_ERROR); } return (XORP_OK); } int FirewallManager::get_table4(list& firewall_entry_list, string& error_msg) { if (_firewall_gets.empty()) { error_msg = c_format("No firewall plugin to get the entries"); return (XORP_ERROR); } // // XXX: We pull the information by using only the first method. // In the future we need to rething this and be more flexible. // if (_firewall_gets.front()->get_table4(firewall_entry_list, error_msg) != XORP_OK) { return (XORP_ERROR); } return (XORP_OK); } int FirewallManager::get_table6(list& firewall_entry_list, string& error_msg) { if (_firewall_gets.empty()) { error_msg = c_format("No firewall plugin to get the entries"); return (XORP_ERROR); } // // XXX: We pull the information by using only the first method. // In the future we need to rething this and be more flexible. // if (_firewall_gets.front()->get_table6(firewall_entry_list, error_msg) != XORP_OK) { return (XORP_ERROR); } return (XORP_OK); } int FirewallManager::get_entry_list_start4(uint32_t& token, bool& more, string& error_msg) { BrowseState* browse_state; generate_token(); token = _next_token; // Create and insert the browse state browse_state = new BrowseState(*this, token); _browse_db.insert(make_pair(token, browse_state)); if (browse_state->get_entry_list_start4(more, error_msg) != XORP_OK) { delete_browse_state(token); return (XORP_ERROR); } // If no entries to list, then cleanup the state if (! more) delete_browse_state(token); return (XORP_OK); } int FirewallManager::get_entry_list_start6(uint32_t& token, bool& more, string& error_msg) { BrowseState* browse_state; generate_token(); token = _next_token; // Create and insert the browse state browse_state = new BrowseState(*this, token); _browse_db.insert(make_pair(token, browse_state)); if (browse_state->get_entry_list_start6(more, error_msg) != XORP_OK) { delete_browse_state(token); return (XORP_ERROR); } // If no entries to list, then cleanup the state if (! more) delete_browse_state(token); return (XORP_OK); } int FirewallManager::get_entry_list_next4(uint32_t token, FirewallEntry& firewall_entry, bool& more, string& error_msg) { BrowseState* browse_state; map::iterator iter; // Test whether the token is valid iter = _browse_db.find(token); if (iter == _browse_db.end()) { more = false; error_msg = c_format("No firewall state to browse for token %u", token); return (XORP_ERROR); } browse_state = iter->second; if (browse_state->get_entry_list_next4(firewall_entry, more, error_msg) != XORP_OK) { delete_browse_state(token); return (XORP_ERROR); } // If no entries to list, then cleanup the state if (! more) delete_browse_state(token); return (XORP_OK); } int FirewallManager::get_entry_list_next6(uint32_t token, FirewallEntry& firewall_entry, bool& more, string& error_msg) { BrowseState* browse_state; map::iterator iter; // Test whether the token is valid iter = _browse_db.find(token); if (iter == _browse_db.end()) { more = false; error_msg = c_format("No firewall state to browse for token %u", token); return (XORP_ERROR); } browse_state = iter->second; if (browse_state->get_entry_list_next6(firewall_entry, more, error_msg) != XORP_OK) { delete_browse_state(token); return (XORP_ERROR); } // If no entries to list, then cleanup the state if (! more) delete_browse_state(token); return (XORP_OK); } void FirewallManager::generate_token() { // Generate a new token that is available do { ++_next_token; if (_browse_db.find(_next_token) == _browse_db.end()) break; } while (true); } void FirewallManager::delete_browse_state(uint32_t token) { map::iterator iter; iter = _browse_db.find(token); XLOG_ASSERT(iter != _browse_db.end()); BrowseState* browse_state = iter->second; _browse_db.erase(iter); delete browse_state; } int FirewallManager::BrowseState::get_entry_list_start4(bool& more, string& error_msg) { more = false; if (_firewall_manager.get_table4(_snapshot, error_msg) != XORP_OK) return (XORP_ERROR); _next_entry_iter = _snapshot.begin(); if (! _snapshot.empty()) { more = true; schedule_timer(); } return (XORP_OK); } int FirewallManager::BrowseState::get_entry_list_start6(bool& more, string& error_msg) { more = false; if (_firewall_manager.get_table6(_snapshot, error_msg) != XORP_OK) return (XORP_ERROR); _next_entry_iter = _snapshot.begin(); if (! _snapshot.empty()) { more = true; schedule_timer(); } return (XORP_OK); } int FirewallManager::BrowseState::get_entry_list_next4(FirewallEntry& firewall_entry, bool& more, string& error_msg) { more = false; if (_next_entry_iter == _snapshot.end()) { error_msg = c_format("No more firewall entries for token %u", _token); return (XORP_ERROR); } // Get the state firewall_entry = *_next_entry_iter; // Prepare for the next request ++_next_entry_iter; if (_next_entry_iter != _snapshot.end()) { more = true; schedule_timer(); } return (XORP_OK); } int FirewallManager::BrowseState::get_entry_list_next6(FirewallEntry& firewall_entry, bool& more, string& error_msg) { more = false; if (_next_entry_iter == _snapshot.end()) { error_msg = c_format("No more firewall entries for token %u", _token); return (XORP_ERROR); } // Get the state firewall_entry = *_next_entry_iter; // Prepare for the next request ++_next_entry_iter; if (_next_entry_iter != _snapshot.end()) { more = true; schedule_timer(); } return (XORP_OK); } void FirewallManager::BrowseState::schedule_timer() { _timeout_timer = _firewall_manager.eventloop().new_oneoff_after_ms( BROWSE_TIMEOUT_MS, callback(this, &FirewallManager::BrowseState::timeout)); } void FirewallManager::BrowseState::timeout() { _firewall_manager.delete_browse_state(_token); } xorp/fea/xrl_io_tcpudp_manager.hh0000664000076400007640000001206211421137511017276 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2007-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/xrl_io_tcpudp_manager.hh,v 1.6 2008/10/02 21:56:51 bms Exp $ #ifndef __FEA_XRL_IO_TCPUDP_MANAGER_HH__ #define __FEA_XRL_IO_TCPUDP_MANAGER_HH__ #include "io_tcpudp_manager.hh" class XrlRouter; /** * @short A class that is the bridge between the I/O TCP/UDP communications * and the XORP XRL interface. */ class XrlIoTcpUdpManager : public IoTcpUdpManagerReceiver { public: /** * Constructor. */ XrlIoTcpUdpManager(IoTcpUdpManager& io_tcpudp_manager, XrlRouter& xrl_router); /** * Destructor. */ virtual ~XrlIoTcpUdpManager(); /** * Data received event. * * @param receiver_name the name of the receiver to send the data to. * @param sockid unique socket ID. * @param if_name the interface name the packet arrived on, if known. * If unknown, then it is an empty string. * @param vif_name the vif name the packet arrived on, if known. * If unknown, then it is an empty string. * @param src_host the originating host IP address. * @param src_port the originating host port number. * @param data the data received. */ void recv_event(const string& receiver_name, const string& sockid, const string& if_name, const string& vif_name, const IPvX& src_host, uint16_t src_port, const vector& data); /** * Inbound connection request received event. * * It applies only to TCP sockets. * * @param receiver_name the name of the receiver to send the event to. * @param sockid unique socket ID. * @param src_host the originating host IP address. * @param src_port the originating host port number. * @param new_sockid the new socket ID. */ void inbound_connect_event(const string& receiver_name, const string& sockid, const IPvX& src_host, uint16_t src_port, const string& new_sockid); /** * Outgoing connection request completed event. * * It applies only to TCP sockets. * * @param family the address family (AF_INET or AF_INET6 for IPv4 and IPv6 * respectively). * @param receiver_name the name of the receiver to send the event to. * @param sockid unique socket ID. */ void outgoing_connect_event(int family, const string& receiver_name, const string& sockid); /** * Error occured event. * * @param family the address family (AF_INET or AF_INET6 for IPv4 and IPv6 * respectively). * @param receiver_name the name of the receiver to send the event to. * @param sockid unique socket ID. * @param error a textual description of the error. * @param fatal indication of whether socket is shutdown because of * error. */ void error_event(int family, const string& receiver_name, const string& sockid, const string& error, bool fatal); /** * Connection closed by peer event. * * It applies only to TCP sockets. * This method is not called if the socket is gracefully closed * through close(). * * @param family the address family (AF_INET or AF_INET6 for IPv4 and IPv6 * respectively). * @param receiver_name the name of the receiver to send the event to. * @param sockid unique socket ID. */ void disconnect_event(int family, const string& receiver_name, const string& sockid); private: XrlRouter& xrl_router() { return _xrl_router; } /** * XRL callbacks */ void xrl_send_recv_event_cb(const XrlError& xrl_error, int family, string receiver_name); void xrl_send_inbound_connect_event_cb(const XrlError& xrl_error, const bool* accept, int family, string sockid, string receiver_name); void xrl_send_outgoing_connect_event_cb(const XrlError& xrl_error, int family, string receiver_name); void xrl_send_error_event_cb(const XrlError& xrl_error, int family, string receiver_name); void xrl_send_disconnect_event_cb(const XrlError& xrl_error, int family, string receiver_name); IoTcpUdpManager& _io_tcpudp_manager; XrlRouter& _xrl_router; }; #endif // __FEA_XRL_IO_TCPUDP_MANAGER_HH__ xorp/fea/fea_xrl_shell_funcs.sh0000775000076400007640000003053611421137511016762 0ustar greearbgreearb#!/bin/sh # Conditionally set ${srcdir} if it wasn't assigned (e.g., by `gmake check`) if [ "X${srcdir}" = "X" ] ; then srcdir=`dirname $0` ; fi if [ "_$CALLXRL" == "_" ] then if which call_xrl > /dev/null 2>&1 then CALLXRL=call_xrl else CALLXRL=/usr/local/xorp/sbin/call_xrl fi fi XRLDIR=${XRLDIR:-${srcdir}/../xrl} get_configured_interface_names() { echo "get_configured_interface_names" $* $CALLXRL "finder://fea/ifmgr/0.1/get_configured_interface_names" } start_fea_transaction() { if [ $# -eq 0 ] ; then tid=`$CALLXRL "finder://fea/ifmgr/0.1/start_transaction" | sed 's/.*=//'` err=$? echo $tid return $err fi cat >&2 <&2 < where is the id of the transaction to be committed. EOF return 255 } abort_fea_transaction() { echo "abort_transaction" $* if [ $# -eq 1 ] ; then $CALLXRL "finder://fea/ifmgr/0.1/abort_transaction?tid:u32=$1" return $? fi cat >&2 < where is the id of the transaction to be aborted. EOF return 255 } create_interface() { echo "create_interface" $* $CALLXRL "finder://fea/ifmgr/0.1/create_interface?tid:u32=$1&ifname:txt=$2" } delete_interface() { echo "delete_interface" $* $CALLXRL "finder://fea/ifmgr/0.1/delete_interface?tid:u32=$1&ifname:txt=$2" } enable_interface() { echo "enable_interface" $* $CALLXRL "finder://fea/ifmgr/0.1/set_interface_enabled?tid:u32=$1&ifname:txt=$2&enabled:bool=true" } disable_interface() { echo "disable_interface" $* $CALLXRL "finder://fea/ifmgr/0.1/set_interface_enabled?tid:u32=$1&ifname:txt=$2&enabled:bool=false" } configure_all_interfaces_from_system() { echo "configure_all_interfaces_from_system" $* $CALLXRL "finder://fea/ifmgr/0.1/configure_all_interfaces_from_system?tid:u32=$1&enable:bool=$2" } configure_interface_from_system() { echo "configure_interface_from_system" $* $CALLXRL "finder://fea/ifmgr/0.1/configure_interface_from_system?tid:u32=$1&ifname:txt=$2&enable:bool=$3" } set_mac() { echo "set_mac" $* $CALLXRL "finder://fea/ifmgr/0.1/set_mac?tid:u32=$1&ifname:txt=$2&mac:mac=$3" } get_configured_mac() { echo "get_configured_mac" $* $CALLXRL "finder://fea/ifmgr/0.1/get_configured_mac?ifname:txt=$1" } set_mtu() { echo "set_mtu" $* $CALLXRL "finder://fea/ifmgr/0.1/set_mtu?tid:u32=$1&ifname:txt=$2&mtu:u32=$3" } get_configured_mtu() { echo "get_configured_mtu" $* $CALLXRL "finder://fea/ifmgr/0.1/get_configured_mtu?ifname:txt=$1" } get_configured_vif_names() { echo "get_configured_vif_names" $* $CALLXRL "finder://fea/ifmgr/0.1/get_configured_vif_names?ifname:txt=$1" } create_vif() { echo "create_vif" $* $CALLXRL "finder://fea/ifmgr/0.1/create_vif?tid:u32=$1&ifname:txt=$2&vif:txt=$3" } delete_vif() { echo "delete_vif" $* $CALLXRL "finder://fea/ifmgr/0.1/delete_vif?tid:u32=$1&ifname:txt=$2&vif:txt=$3" } enable_vif() { echo "enable_vif" $* $CALLXRL "finder://fea/ifmgr/0.1/set_vif_enabled?tid:u32=$1&ifname:txt=$2&vif:txt=$3&enabled:bool=true" } disable_vif() { echo "disable_vif" $* $CALLXRL "finder://fea/ifmgr/0.1/set_vif_enabled?tid:u32=$1&ifname:txt=$2&vif:txt=$3&enabled:bool=false" } get_configured_vif_addresses4() { echo "get_configured_vif_addresses4" $* $CALLXRL "finder://fea/ifmgr/0.1/get_configured_vif_addresses4?ifname:txt=$1&vif:txt=$2" } create_address4() { echo "create_address4" $* $CALLXRL "finder://fea/ifmgr/0.1/create_address4?tid:u32=$1&ifname:txt=$2&vif:txt=$3&address:ipv4=$4" } delete_address4() { echo "delete_address4" $* $CALLXRL "finder://fea/ifmgr/0.1/delete_address4?tid:u32=$1&ifname:txt=$2&vif:txt=$3&address:ipv4=$4" } enable_address4() { echo "enable_address4" $* $CALLXRL "finder://fea/ifmgr/0.1/set_address_enabled4?tid:u32=$1&ifname:txt=$2&vif:txt=$3&address:ipv4=$4&enabled:bool=true" } disable_address4() { echo "disable_address4" $* $CALLXRL "finder://fea/ifmgr/0.1/set_address_enabled4?tid:u32=$1&ifname:txt=$2&vif:txt=$3&address:ipv4=$4&enabled:bool=false" } set_prefix4() { echo "set_prefix4" $* $CALLXRL "finder://fea/ifmgr/0.1/set_prefix4?tid:u32=$1&ifname:txt=$2&vif:txt=$3&address:ipv4=$4&prefix_len:u32=$5" } get_configured_prefix4() { echo "get_configured_prefix4" $* $CALLXRL "finder://fea/ifmgr/0.1/get_configured_prefix4?ifname:txt=$1&vif:txt=$2&address:ipv4=$3" } set_broadcast4() { echo "set_broadcast4" $* $CALLXRL "finder://fea/ifmgr/0.1/set_broadcast4?tid:u32=$1&ifname:txt=$2&vif:txt=$3&address:ipv4=$4&broadcast:ipv4=$5" } get_configured_broadcast4() { echo "get_configured_broadcast4" $* $CALLXRL "finder://fea/ifmgr/0.1/get_configured_broadcast4?ifname:txt=$1&vif:txt=$2&address:ipv4=$3" } get_configured_vif_addresses6() { echo "get_configured_vif_addresses6" $* $CALLXRL "finder://fea/ifmgr/0.1/get_configured_vif_addresses6?ifname:txt=$1&vif:txt=$2" } create_address6() { echo "create_address6" $* $CALLXRL "finder://fea/ifmgr/0.1/create_address6?tid:u32=$1&ifname:txt=$2&vif:txt=$3&address:ipv6=$4" } delete_address6() { echo "delete_address6" $* $CALLXRL "finder://fea/ifmgr/0.1/delete_address6?tid:u32=$1&ifname:txt=$2&vif:txt=$3&address:ipv6=$4" } enable_address6() { echo "enable_address6" $* $CALLXRL "finder://fea/ifmgr/0.1/set_address_enabled6?tid:u32=$1&ifname:txt=$2&vif:txt=$3&address:ipv6=$4&enabled:bool=true" } disable_address6() { echo "disable_address6" $* $CALLXRL "finder://fea/ifmgr/0.1/set_address_enabled6?tid:u32=$1&ifname:txt=$2&vif:txt=$3&address:ipv6=$4&enabled:bool=false" } set_prefix6() { echo "set_prefix6" $* $CALLXRL "finder://fea/ifmgr/0.1/set_prefix6?tid:u32=$1&ifname:txt=$2&vif:txt=$3&address:ipv6=$4&prefix_len:u32=$5" } get_configured_prefix6() { echo "get_configured_prefix6" $* $CALLXRL "finder://fea/ifmgr/0.1/get_configured_prefix6?ifname:txt=$1&vif:txt=$2&address:ipv6=$3" } start_redist_transaction4() { if [ $# -eq 0 ] ; then tid=`$CALLXRL "finder://fea/redist_transaction4/0.1/start_transaction" | sed 's/.*=//'` err=$? echo $tid return $err fi cat >&2 <&2 <&2 < where is the id of the transaction to be committed. EOF return 255 } commit_redist_transaction6() { echo "commit_redist_transaction6" $* if [ $# -eq 1 ] ; then $CALLXRL "finder://fea/redist_transaction6/0.1/commit_transaction?tid:u32=$1" return $? fi cat >&2 < where is the id of the transaction to be committed. EOF return 255 } abort_redist_transaction4() { echo "abort_redist_transaction4" $* if [ $# -eq 1 ] ; then $CALLXRL "finder://fea/redist_transaction4/0.1/abort_transaction?tid:u32=$1" return $? fi cat >&2 < where is the id of the transaction to be aborted. EOF return 255 } abort_redist_transaction6() { echo "abort_redist_transaction6" $* if [ $# -eq 1 ] ; then $CALLXRL "finder://fea/redist_transaction6/0.1/abort_transaction?tid:u32=$1" return $? fi cat >&2 < where is the id of the transaction to be aborted. EOF return 255 } redist_transaction4_add_route() { if [ $# -ne 8 ] ; then cat >&2 < eg: redist_transaction4_add_route 6987662 187.1.0.0/16 164.27.13.1 ed0 10 20 all BGP EOF return 127 fi $CALLXRL "finder://fea/redist_transaction4/0.1/add_route?tid:u32=$1&dst:ipv4net=$2&nexthop:ipv4=$3&ifname:txt=$4&vifname:txt=$5&metric:u32=$6&admin_distance:u32=$7&protocol_origin:txt=$8" } redist_transaction4_delete_route() { if [ $# -ne 2 ] ; then cat >&2 < eg: redist_transaction4_delete_route 276567373 187.1.0.0/16 EOF return 127 fi $CALLXRL "finder://fea/redist_transaction4/0.1/delete_route?tid:u32=$1&network:ipv4net=$2" } lookup_route_by_dest4() { echo -n "lookup_route_by_dest4" $* "-> " $CALLXRL "finder://fea/fti/0.2/lookup_route_by_dest4?dst:ipv4=$1" } lookup_route_by_network4() { echo -n "lookup_route_by_network4" $* "-> " $CALLXRL "finder://fea/fti/0.2/lookup_route_by_network4?dst:ipv4net=$1" } have_ipv4() { echo -n "have_ipv4" $* "-> " $CALLXRL "finder://fea/fti/0.2/have_ipv4" } have_ipv6() { echo -n "have_ipv6" $* "-> " $CALLXRL "finder://fea/fti/0.2/have_ipv6" } get_unicast_forwarding_enabled4() { echo -n "get_unicast_forwarding_enabled4" $* "-> " $CALLXRL "finder://fea/fti/0.2/get_unicast_forwarding_enabled4" } get_unicast_forwarding_enabled6() { echo -n "get_unicast_forwarding_enabled6" $* "-> " $CALLXRL "finder://fea/fti/0.2/get_unicast_forwarding_enabled6" } set_unicast_forwarding_enabled4() { echo "set_unicast_forwarding_enabled4" $* if [ $# -eq 1 ] ; then $CALLXRL "finder://fea/fti/0.2/set_unicast_forwarding_enabled4?enabled:bool=$1" return $? fi cat >&2 < where is set to true if we want to enable IPv4 unicast forwarding, otherwise is set to false. EOF return 255 } set_unicast_forwarding_enabled6() { echo "set_unicast_forwarding_enabled6" $* if [ $# -eq 1 ] ; then $CALLXRL "finder://fea/fti/0.2/set_unicast_forwarding_enabled6?enabled:bool=$1" return $? fi cat >&2 < where is set to true if we want to enable IPv6 unicast forwarding, otherwise is set to false. EOF return 255 } shutdown() { echo "shutdown" $* $CALLXRL "finder://fea/common/0.1/shutdown" } validate_xrls() { # # Check that xrls in this script are valid. Order of arguments is # expected to match those in file containing Xrls: this is a laziness # requirement and not an xrl requirement. A shell is almost certainly # the wrong place for this. # # STOPLOOKING # script_name="${srcdir}/fea_xrl_shell_funcs.sh" script_xrls=`cat ${script_name} | sed -n '1,/STOPLOOKING/p' | sed -n '/finder:\/\// p' | sed 's/[^"]*"\([^"]*\).*/\1/g' | sed 's/=[^-&]*//g' | sed 's/->.*//g'` source_xrl_files="${XRLDIR}/targets/*.xrls" match_count=0 bad_count=0 for i in ${script_xrls} ; do found="no" for file in ${source_xrl_files} ; do source_xrls=`cat ${file} | grep '://' | sed 's/->.*//g'` for j in ${source_xrls} ; do if [ ${i} = ${j} ] ; then found="yes" match_count=`expr ${match_count} + 1` break fi stripped_i=`echo ${i} | sed 's/\?.*//'` stripped_j=`echo ${j} | sed 's/\?.*//'` if [ ${stripped_i} = ${stripped_j} ] ; then found="yes" echo "Warning mismatch in file ${file}:" echo " script has \"${i}\"" echo " file has \"${j}\"" bad_count=`expr ${bad_count} + 1` break fi done if [ "${found}" = "yes" ] ; then break fi done if [ "${found}" = "no" ] ; then echo "No match for ${i} in ${source_xrl_files}" bad_count=`expr ${bad_count} + 1` fi done status="Summary: ${match_count} xrls okay, ${bad_count} xrls bad." rule=`echo ${status} | sed 's/[A-z0-9,:. ]/-/g'` echo ${rule} echo ${status} echo ${rule} echo $* unset script_xrls unset source_xrls return ${bad_count} } # We have arguments. if [ $# != 0 ] then $* fi # Local Variables: # mode: shell-script # sh-indentation: 4 # End: xorp/fea/libfeaclient_bridge.cc0000664000076400007640000002616211642675750016703 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxipc/xrl_router.hh" #include "libfeaclient/ifmgr_atoms.hh" #include "libfeaclient/ifmgr_cmds.hh" #include "libfeaclient/ifmgr_xrl_replicator.hh" #include "iftree.hh" #include "libfeaclient_bridge.hh" // ---------------------------------------------------------------------------- // Debug helpers static const char* update_name(IfConfigUpdateReporterBase::Update u) { switch (u) { case IfConfigUpdateReporterBase::CREATED: return "Creation"; case IfConfigUpdateReporterBase::DELETED: return "Deletion"; case IfConfigUpdateReporterBase::CHANGED: break; // FALLTHROUGH } return "Change"; } // ---------------------------------------------------------------------------- // LibFeaClientBridge implementation LibFeaClientBridge::LibFeaClientBridge(XrlRouter& rtr, IfConfigUpdateReplicator& update_replicator) : IfConfigUpdateReporterBase(update_replicator) { _rm = new IfMgrXrlReplicationManager(rtr); add_to_replicator(); } LibFeaClientBridge::~LibFeaClientBridge() { delete _rm; } int LibFeaClientBridge::add_libfeaclient_mirror(const string& m) { if (_rm->add_mirror(m) != true) return (XORP_ERROR); return (XORP_OK); } int LibFeaClientBridge::remove_libfeaclient_mirror(const string& m) { if (_rm->remove_mirror(m) != true) return (XORP_ERROR); return (XORP_OK); } void LibFeaClientBridge::interface_update(const string& ifname, const Update& update) { debug_msg("%s update for interface %s\n", update_name(update), ifname.c_str()); switch (update) { case CREATED: _rm->push(new IfMgrIfAdd(ifname)); break; // FALLTHROUGH case DELETED: _rm->push(new IfMgrIfRemove(ifname)); return; case CHANGED: break; // FALLTHROUGH } // // Validate interface is in the FEA iftree we're using and // in libfeaclient's equivalent. // const IfMgrIfAtom* ifa = _rm->iftree().find_interface(ifname); if (ifa == NULL) { XLOG_WARNING("Got update for interface not in the libfeaclient tree: %s", ifname.c_str()); return; } const IfTreeInterface* ifp = observed_iftree().find_interface(ifname); if (ifp == NULL) { XLOG_WARNING("Got update for interface not in the FEA tree: %s", ifname.c_str()); return; } // // Copy interface state out of FEA iftree into libfeaclient's // equivalent. // _rm->push(new IfMgrIfSetEnabled(ifname, ifp->enabled())); _rm->push(new IfMgrIfSetDiscard(ifname, ifp->discard())); _rm->push(new IfMgrIfSetUnreachable(ifname, ifp->unreachable())); _rm->push(new IfMgrIfSetManagement(ifname, ifp->management())); _rm->push(new IfMgrIfSetMtu(ifname, ifp->mtu())); _rm->push(new IfMgrIfSetMac(ifname, ifp->mac())); _rm->push(new IfMgrIfSetPifIndex(ifname, ifp->pif_index())); _rm->push(new IfMgrIfSetNoCarrier(ifname, ifp->no_carrier())); _rm->push(new IfMgrIfSetBaudrate(ifname, ifp->baudrate())); _rm->push(new IfMgrIfSetString(ifname, ifp->parent_ifname(), IF_STRING_PARENT_IFNAME)); _rm->push(new IfMgrIfSetString(ifname, ifp->iface_type(), IF_STRING_IFTYPE)); _rm->push(new IfMgrIfSetString(ifname, ifp->vid(), IF_STRING_VID)); } void LibFeaClientBridge::vif_update(const string& ifname, const string& vifname, const Update& update) { debug_msg("%s update for vif %s/%s\n", update_name(update), ifname.c_str(), vifname.c_str()); switch (update) { case CREATED: _rm->push(new IfMgrVifAdd(ifname, vifname)); break; // FALLTHROUGH case DELETED: _rm->push(new IfMgrVifRemove(ifname, vifname)); return; case CHANGED: break; // FALLTHROUGH } // // Validate vif is in the FEA iftree we're using and in // libfeaclient's equivalent. // const IfMgrVifAtom* ifa = _rm->iftree().find_vif(ifname, vifname); if (ifa == NULL) { XLOG_WARNING("Got update for vif not in the libfeaclient tree: %s/%s", ifname.c_str(), vifname.c_str()); return; } const IfTreeInterface* ifp = observed_iftree().find_interface(ifname); if (ifp == NULL) { XLOG_WARNING("Got update for vif on interface not in the FEA tree: " "%s/%s", ifname.c_str(), vifname.c_str()); return; } const IfTreeVif* vifp = ifp->find_vif(vifname); if (vifp == NULL) { XLOG_WARNING("Got update for vif not in the FEA tree: %s/%s", ifname.c_str(), vifname.c_str()); return; } // // Copy vif state out of FEA iftree into libfeaclient's // equivalent. // _rm->push(new IfMgrVifSetEnabled(ifname, vifname, vifp->enabled()) ); _rm->push(new IfMgrVifSetBroadcastCapable(ifname, vifname, vifp->broadcast()) ); _rm->push(new IfMgrVifSetLoopbackCapable(ifname, vifname, vifp->loopback()) ); _rm->push(new IfMgrVifSetP2PCapable(ifname, vifname, vifp->point_to_point()) ); _rm->push(new IfMgrVifSetMulticastCapable(ifname, vifname, vifp->multicast()) ); _rm->push(new IfMgrVifSetPifIndex(ifname, vifname, vifp->pif_index()) ); _rm->push(new IfMgrVifSetVifIndex(ifname, vifname, vifp->vif_index()) ); _rm->push(new IfMgrVifSetPimRegister(ifname, vifname, vifp->pim_register()) ); } void LibFeaClientBridge::vifaddr4_update(const string& ifname, const string& vifname, const IPv4& addr, const Update& update) { debug_msg("%s update for address %s/%s/%s\n", update_name(update), ifname.c_str(), vifname.c_str(), addr.str().c_str()); switch (update) { case CREATED: _rm->push(new IfMgrIPv4Add(ifname, vifname, addr)); break; // FALLTHROUGH case DELETED: _rm->push(new IfMgrIPv4Remove(ifname, vifname, addr)); return; case CHANGED: break; // FALLTHROUGH } // // Validate vif address is in the FEA iftree we're using and in // libfeaclient's equivalent // const IfMgrIPv4Atom* ifa = _rm->iftree().find_addr(ifname, vifname, addr); if (ifa == NULL) { XLOG_WARNING("Got update for address no in the libfeaclient tree: " "%s/%s/%s", ifname.c_str(), vifname.c_str(), addr.str().c_str()); return; } const IfTreeInterface* ifp = observed_iftree().find_interface(ifname); if (ifp == NULL) { XLOG_WARNING("Got update for address on interface not in the FEA tree: " "%s/%s/%s", ifname.c_str(), vifname.c_str(), addr.str().c_str()); return; } const IfTreeVif* vifp = ifp->find_vif(vifname); if (vifp == NULL) { XLOG_WARNING("Got update for address on vif not in the FEA tree: " "%s/%s/%s", ifname.c_str(), vifname.c_str(), addr.str().c_str()); return; } const IfTreeAddr4* ap = vifp->find_addr(addr); if (ap == NULL) { XLOG_WARNING("Got update for address not in the FEA tree: %s/%s/%s", ifname.c_str(), vifname.c_str(), addr.str().c_str()); return; } // // Copy address state out of FEA iftree into libfeaclient's // equivalent. // _rm->push(new IfMgrIPv4SetEnabled(ifname, vifname, addr, ap->enabled()) ); _rm->push(new IfMgrIPv4SetLoopback(ifname, vifname, addr, ap->loopback()) ); _rm->push(new IfMgrIPv4SetMulticastCapable(ifname, vifname, addr, ap->multicast()) ); _rm->push(new IfMgrIPv4SetPrefix(ifname, vifname, addr, ap->prefix_len()) ); if (ap->point_to_point()) { const IPv4& end = ap->endpoint(); _rm->push(new IfMgrIPv4SetEndpoint(ifname, vifname, addr, end)); } else { // XXX: Method IfTreeAddr4::bcast() will return IPv4::ZERO() if // broadcast is not supported. This happens to be the // correct argument for libfeaclient to signify broadcast // is not supported. const IPv4& bcast = ap->bcast(); _rm->push(new IfMgrIPv4SetBroadcast(ifname, vifname, addr, bcast)); } } #ifdef HAVE_IPV6 void LibFeaClientBridge::vifaddr6_update(const string& ifname, const string& vifname, const IPv6& addr, const Update& update) { debug_msg("%s update for address %s/%s/%s\n", update_name(update), ifname.c_str(), vifname.c_str(), addr.str().c_str()); switch (update) { case CREATED: _rm->push(new IfMgrIPv6Add(ifname, vifname, addr)); break; // FALLTHROUGH case DELETED: _rm->push(new IfMgrIPv6Remove(ifname, vifname, addr)); return; case CHANGED: break; // FALLTHROUGH } // // Validate vif address is in the FEA iftree we're using and in // libfeaclient's equivalent // const IfMgrIPv6Atom* ifa = _rm->iftree().find_addr(ifname, vifname, addr); if (ifa == NULL) { XLOG_WARNING("Got update for address no in the libfeaclient tree: " "%s/%s/%s", ifname.c_str(), vifname.c_str(), addr.str().c_str()); return; } const IfTreeInterface* ifp = observed_iftree().find_interface(ifname); if (ifp == NULL) { XLOG_WARNING("Got update for address on interface not in the FEA tree: " "%s/%s/%s", ifname.c_str(), vifname.c_str(), addr.str().c_str()); return; } const IfTreeVif* vifp = ifp->find_vif(vifname); if (vifp == NULL) { XLOG_WARNING("Got update for address on vif not in the FEA tree: " "%s/%s/%s", ifname.c_str(), vifname.c_str(), addr.str().c_str()); return; } const IfTreeAddr6* ap = vifp->find_addr(addr); if (ap == NULL) { XLOG_WARNING("Got update for address not in the FEA tree: %s/%s/%s", ifname.c_str(), vifname.c_str(), addr.str().c_str()); return; } // // Copy address state out of FEA iftree into libfeaclient's // equivalent. // _rm->push(new IfMgrIPv6SetEnabled(ifname, vifname, addr, ap->enabled()) ); _rm->push(new IfMgrIPv6SetLoopback(ifname, vifname, addr, ap->loopback()) ); _rm->push(new IfMgrIPv6SetMulticastCapable(ifname, vifname, addr, ap->multicast()) ); _rm->push(new IfMgrIPv6SetPrefix(ifname, vifname, addr, ap->prefix_len()) ); _rm->push(new IfMgrIPv6SetEndpoint(ifname, vifname, addr, ap->endpoint())); } #endif void LibFeaClientBridge::updates_completed() { debug_msg("Updates completed\n"); _rm->push(new IfMgrHintUpdatesMade()); } xorp/fea/xorp_fea.cc0000664000076400007640000001205211540225525014526 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/eventloop.hh" #include "libxorp/exceptions.hh" #include "libxorp/clock.hh" #include "xrl_fea_node.hh" #ifdef HAVE_GETOPT_H #include #endif // TODO: XXX: those XRL target names should be defined somewhere else static const string xrl_fea_targetname = "fea"; static const string xrl_finder_targetname = "finder"; #ifndef FEA_DUMMY static bool is_dummy = false; #else static bool is_dummy = true; #endif /** * Print the program usage. * * If @param exit_value is 0, the usage will be printed to the standart * output, otherwise to the standart error. * * @param argv0 argument 0 when the program is called (the program name * itself). * @param exit_value the exit value of the program. */ static void usage(const char *argv0, int exit_value) { FILE *output; const char *progname = strrchr(argv0, '/'); if (progname != NULL) progname++; // Skip the last '/' if (progname == NULL) progname = argv0; // // If the usage is printed because of error, output to stderr, otherwise // output to stdout. // if (exit_value == 0) output = stdout; else output = stderr; fprintf(output, "Usage: %s [-F [:]]\n", progname); fprintf(output, " -F [:] : finder hostname and port\n"); fprintf(output, " -h : usage (this message)\n"); fprintf(output, "\n"); fprintf(output, "Program name: %s\n", progname); fprintf(output, "Module name: %s\n", XORP_MODULE_NAME); fprintf(output, "Module version: %s\n", XORP_MODULE_VERSION); exit (exit_value); // NOTREACHED } static void fea_main(const string& finder_hostname, uint16_t finder_port) { setup_dflt_sighandlers(); EventLoop eventloop; XrlFeaNode xrl_fea_node(eventloop, xrl_fea_targetname, xrl_finder_targetname, finder_hostname, finder_port, is_dummy); // Start operations xrl_fea_node.startup(); // // Main loop // while (xorp_do_run && !xrl_fea_node.is_shutdown_received()) { eventloop.run(); } // // Shutdown request received. Shutdown all operations and cleanup. // xrl_fea_node.shutdown(); while (xrl_fea_node.is_running()) { eventloop.run(); } } int main(int argc, char *argv[]) { int ch; string::size_type idx; const char *argv0 = argv[0]; string finder_hostname = FinderConstants::FINDER_DEFAULT_HOST().str(); uint16_t finder_port = FinderConstants::FINDER_DEFAULT_PORT(); // Initialize random number generator. SystemClock sc; TimeVal tv; sc.current_time(tv); xorp_srandom(tv.usec()); // // Initialize and start xlog // xlog_init(argv[0], NULL); //xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages xlog_set_verbose(XLOG_VERBOSE_HIGH); // XXX: verbosity of the error messages temporary increased //xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); //xlog_enable(XLOG_LEVEL_INFO); Doesn't work? --Ben xlog_add_default_output(); xlog_start(); // // Get the program options // while ((ch = getopt(argc, argv, "F:h")) != -1) { switch (ch) { case 'F': // Finder hostname and port finder_hostname = optarg; idx = finder_hostname.find(':'); if (idx != string::npos) { if (idx + 1 >= finder_hostname.length()) { // No port number usage(argv0, 1); // NOTREACHED } char* p = &finder_hostname[idx + 1]; finder_port = static_cast(atoi(p)); finder_hostname = finder_hostname.substr(0, idx); } break; case 'h': case '?': // Help usage(argv0, 0); // NOTREACHED break; default: usage(argv0, 1); // NOTREACHED break; } } argc -= optind; argv += optind; if (argc != 0) { usage(argv0, 1); // NOTREACHED } // // Run everything // try { fea_main(finder_hostname, finder_port); } catch(...) { xorp_catch_standard_exceptions(); } // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); return 0; } xorp/fea/fibconfig.hh0000664000076400007640000006463311703345405014700 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2012 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __FEA_FIBCONFIG_HH__ #define __FEA_FIBCONFIG_HH__ #include "libxorp/xorp.h" #include "libxorp/ipv4.hh" #include "libxorp/ipv6.hh" #include "libxorp/ipv4net.hh" #include "libxorp/ipv6net.hh" #include "libxorp/status_codes.h" #include "libxorp/transaction.hh" #include "libxorp/trie.hh" #include "fte.hh" #include "fibconfig_forwarding.hh" #include "fibconfig_entry_get.hh" #include "fibconfig_entry_set.hh" #include "fibconfig_entry_observer.hh" #include "fibconfig_table_get.hh" #include "fibconfig_table_set.hh" #include "fibconfig_table_observer.hh" class EventLoop; class FeaNode; class FibConfigTransactionManager; class FibTableObserverBase; class IfTree; class NexthopPortMapper; #ifndef XORP_DISABLE_PROFILE class Profile; #endif typedef Trie Trie4; typedef Trie Trie6; /** * @short Forwarding Table Interface. * * Abstract class. */ class FibConfig { public: /** * Constructor. * * @param fea_node the FEA node. * @param system_config_iftree the system interface configuration tree to * use. * @param merged_config_iftree the merged system-user interface * configuration tree to use. */ FibConfig(FeaNode& fea_node, const IfTree& system_config_iftree, const IfTree& merged_config_iftree); /** * Virtual destructor (in case this class is used as a base class). */ virtual ~FibConfig(); /** * Get a reference to the @ref EventLoop instance. * * @return a reference to the @ref EventLoop instance. */ EventLoop& eventloop() { return _eventloop; } /** * Get a reference to the @ref NexthopPortMapper instance. * * @return a reference to the @ref NexthopPortMapper instance. */ NexthopPortMapper& nexthop_port_mapper() { return _nexthop_port_mapper; } /** * Get a reference to the system interface configuration. * * @return a reference to the system interface configuration. */ const IfTree& system_config_iftree() const { return _system_config_iftree; } /** * Get a reference to the merged system-user interface configuration. * * @return a reference to the merged system-user interface configuration. */ const IfTree& merged_config_iftree() const { return _merged_config_iftree; } /** * Get the status code. * * @param reason the human-readable reason for any failure. * @return the status code. */ ProcessStatus status(string& reason) const; /** * Start FIB-related transaction. * * @param tid the return-by-reference new transaction ID. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int start_transaction(uint32_t& tid, string& error_msg); /** * Commit FIB-related transaction. * * @param tid the transaction ID. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int commit_transaction(uint32_t tid, string& error_msg); /** * Abort FIB-related transaction. * * @param tid the transaction ID. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int abort_transaction(uint32_t tid, string& error_msg); /** * Add operation to FIB-related transaction. * * @param tid the transaction ID. * @param op the operation to add. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_transaction_operation(uint32_t tid, const TransactionManager::Operation& op, string& error_msg); /** * Register @ref FibConfigForwarding plugin. * * @param fibconfig_forwarding the plugin to register. * @param is_exclusive if true, the plugin is registered as the * exclusive plugin, otherwise is added to the list of plugins. * @return XORP_OK on success, otherwise XORP_ERROR. */ int register_fibconfig_forwarding(FibConfigForwarding* fibconfig_forwarding, bool is_exclusive); /** * Unregister @ref FibConfigForwarding plugin. * * @param fibconfig_forwarding the plugin to unregister. * @return XORP_OK on success, otherwise XORP_ERROR. */ int unregister_fibconfig_forwarding(FibConfigForwarding* fibconfig_forwarding); /** * Register @ref FibConfigEntryGet plugin. * * @param fibconfig_entry_get the plugin to register. * @param is_exclusive if true, the plugin is registered as the * exclusive plugin, otherwise is added to the list of plugins. * @return XORP_OK on success, otherwise XORP_ERROR. */ int register_fibconfig_entry_get(FibConfigEntryGet* fibconfig_entry_get, bool is_exclusive); /** * Unregister @ref FibConfigEntryGet plugin. * * @param fibconfig_entry_get the plugin to unregister. * @return XORP_OK on success, otherwise XORP_ERROR. */ int unregister_fibconfig_entry_get(FibConfigEntryGet* fibconfig_entry_get); /** * Register @ref FibConfigEntrySet plugin. * * @param fibconfig_entry_set the plugin to register. * @param is_exclusive if true, the plugin is registered as the * exclusive plugin, otherwise is added to the list of plugins. * @return XORP_OK on success, otherwise XORP_ERROR. */ int register_fibconfig_entry_set(FibConfigEntrySet* fibconfig_entry_set, bool is_exclusive); /** * Unregister @ref FibConfigEntrySet plugin. * * @param fibconfig_entry_set the plugin to unregister. * @return XORP_OK on success, otherwise XORP_ERROR. */ int unregister_fibconfig_entry_set(FibConfigEntrySet* fibconfig_entry_set); /** * Register @ref FibConfigEntryObserver plugin. * * @param fibconfig_entry_observer the plugin to register. * @param is_exclusive if true, the plugin is registered as the * exclusive plugin, otherwise is added to the list of plugins. * @return XORP_OK on success, otherwise XORP_ERROR. */ int register_fibconfig_entry_observer(FibConfigEntryObserver* fibconfig_entry_observer, bool is_exclusive); /** * Unregister @ref FibConfigEntryObserver plugin. * * @param fibconfig_entry_observer the plugin to unregister. * @return XORP_OK on success, otherwise XORP_ERROR. */ int unregister_fibconfig_entry_observer(FibConfigEntryObserver* fibconfig_entry_observer); /** * Register @ref FibConfigTableGet plugin. * * @param fibconfig_table_get the plugin to register. * @param is_exclusive if true, the plugin is registered as the * exclusive plugin, otherwise is added to the list of plugins. * @return XORP_OK on success, otherwise XORP_ERROR. */ int register_fibconfig_table_get(FibConfigTableGet* fibconfig_table_get, bool is_exclusive); /** * Unregister @ref FibConfigTableGet plugin. * * @param fibconfig_table_get the plugin to unregister. * @return XORP_OK on success, otherwise XORP_ERROR. */ int unregister_fibconfig_table_get(FibConfigTableGet* fibconfig_table_get); /** * Register @ref FibConfigTableSet plugin. * * @param fibconfig_table_set the plugin to register. * @param is_exclusive if true, the plugin is registered as the * exclusive plugin, otherwise is added to the list of plugins. * @return XORP_OK on success, otherwise XORP_ERROR. */ int register_fibconfig_table_set(FibConfigTableSet* fibconfig_table_set, bool is_exclusive); /** * Unregister @ref FibConfigTableSet plugin. * * @param fibconfig_table_set the plugin to unregister. * @return XORP_OK on success, otherwise XORP_ERROR. */ int unregister_fibconfig_table_set(FibConfigTableSet* fibconfig_table_set); /** * Register @ref FibConfigTableObserver plugin. * * @param fibconfig_table_observer the plugin to register. * @param is_exclusive if true, the plugin is registered as the * exclusive plugin, otherwise is added to the list of plugins. * @return XORP_OK on success, otherwise XORP_ERROR. */ int register_fibconfig_table_observer(FibConfigTableObserver* fibconfig_table_observer, bool is_exclusive); /** * Unregister @ref FibConfigTableObserver plugin. * * @param fibconfig_table_observer the plugin to unregister. * @return XORP_OK on success, otherwise XORP_ERROR. */ int unregister_fibconfig_table_observer(FibConfigTableObserver* fibconfig_table_observer); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int stop(string& error_msg); /** * Start a configuration interval. * * All modifications must be within a marked "configuration" interval. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int start_configuration(string& error_msg); /** * End of configuration interval. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int end_configuration(string& error_msg); /** * Test whether the IPv4 unicast forwarding engine retains existing * XORP forwarding entries on startup. * * @return true if the XORP unicast forwarding entries are retained, * otherwise false. */ bool unicast_forwarding_entries_retain_on_startup4() const { return (_unicast_forwarding_entries_retain_on_startup4); } /** * Test whether the IPv4 unicast forwarding engine retains existing * XORP forwarding entries on shutdown. * * @return true if the XORP unicast forwarding entries are retained, * otherwise false. */ bool unicast_forwarding_entries_retain_on_shutdown4() const { return (_unicast_forwarding_entries_retain_on_shutdown4); } /** * Test whether the IPv6 unicast forwarding engine retains existing * XORP forwarding entries on startup. * * @return true if the XORP unicast forwarding entries are retained, * otherwise false. */ bool unicast_forwarding_entries_retain_on_startup6() const { return (_unicast_forwarding_entries_retain_on_startup6); } /** * Test whether the IPv6 unicast forwarding engine retains existing * XORP forwarding entries on shutdown. * * @return true if the XORP unicast forwarding entries are retained, * otherwise false. */ bool unicast_forwarding_entries_retain_on_shutdown6() const { return (_unicast_forwarding_entries_retain_on_shutdown6); } /** * Set the IPv4 unicast forwarding engine whether to retain existing * XORP forwarding entries on startup. * * @param retain if true, then retain the XORP forwarding entries, * otherwise delete them. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_unicast_forwarding_entries_retain_on_startup4(bool retain, string& error_msg); /** * Set the IPv4 unicast forwarding engine whether to retain existing * XORP forwarding entries on shutdown. * * @param retain if true, then retain the XORP forwarding entries, * otherwise delete them. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_unicast_forwarding_entries_retain_on_shutdown4(bool retain, string& error_msg); /** * Set the IPv6 unicast forwarding engine whether to retain existing * XORP forwarding entries on startup. * * @param retain if true, then retain the XORP forwarding entries, * otherwise delete them. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_unicast_forwarding_entries_retain_on_startup6(bool retain, string& error_msg); /** * Set the IPv6 unicast forwarding engine whether to retain existing * XORP forwarding entries on shutdown. * * @param retain if true, then retain the XORP forwarding entries, * otherwise delete them. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_unicast_forwarding_entries_retain_on_shutdown6(bool retain, string& error_msg); /** * Test whether the unicast forwarding table ID for a given address family * is configured. * * @param family the address family. * @return true if the unicast forwarding table ID for the given address * family is configured, otherwise false. */ bool unicast_forwarding_table_id_is_configured(int family) const; /** * Get the unicast forwarding table ID for a given address family. * * @param family the address family; * @return the unicast forwarding table ID for the given address family. */ uint32_t unicast_forwarding_table_id(int family) const; /** * Test whether the IPv4 unicast forwarding table ID is configured. * * @return true if the IPv4 unicast forwarding table ID is configured, * otherwise false. */ bool unicast_forwarding_table_id4_is_configured() const { return (_unicast_forwarding_table_id4_is_configured); } /** * Get the IPv4 unicast forwarding table ID. * * @return the IPv4 unicast forwarding table ID. */ uint32_t unicast_forwarding_table_id4() const { return (_unicast_forwarding_table_id4); } /** * Test whether the IPv6 unicast forwarding table ID is configured. * * @return true if the IPv6 unicast forwarding table ID is configured, * otherwise false. */ bool unicast_forwarding_table_id6_is_configured() const { return (_unicast_forwarding_table_id6_is_configured); } /** * Get the IPv6 unicast forwarding table ID. * * @return the IPv6 unicast forwarding table ID. */ uint32_t unicast_forwarding_table_id6() const { return (_unicast_forwarding_table_id6); } /** If IPv4 and IPv6 table ids are configured, and configured to the same thing, * we can attempt to filter the netlink route messages on that table id. * Otherwise, return 0 (no filtering) */ uint32_t get_netlink_filter_table_id() const; void propagate_table_id_change(); /** * Set the IPv4 unicast forwarding table ID to be used. * * @param is_configured if true, the forwarding table ID is configured, * otherwise the default table should be used. * @param table_id the IPv4 unicast forwarding table ID to be used. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_unicast_forwarding_table_id4(bool is_configured, uint32_t table_id, string& error_msg); /** * Set the IPv6 unicast forwarding table ID to be used. * * @param is_configured if true, the forwarding table ID is configured, * otherwise the default table should be used. * @param table_id the IPv6 unicast forwarding table ID to be used. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_unicast_forwarding_table_id6(bool is_configured, uint32_t table_id, string& error_msg); /** * Test whether the IPv4 unicast forwarding engine is enabled or disabled * to forward packets. * * @param ret_value if true on return, then the IPv4 unicast forwarding * is enabled, otherwise is disabled. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int unicast_forwarding_enabled4(bool& ret_value, string& error_msg) const; /** * Test whether the IPv6 unicast forwarding engine is enabled or disabled * to forward packets. * * @param ret_value if true on return, then the IPv6 unicast forwarding * is enabled, otherwise is disabled. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int unicast_forwarding_enabled6(bool& ret_value, string& error_msg) const; /** * Test whether the acceptance of IPv6 Router Advertisement messages is * enabled or disabled. * * @param ret_value if true on return, then the acceptance of IPv6 Router * Advertisement messages is enabled, otherwise is disabled. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int accept_rtadv_enabled6(bool& ret_value, string& error_msg) const; /** * Set the IPv4 unicast forwarding engine to enable or disable forwarding * of packets. * * @param v if true, then enable IPv4 unicast forwarding, otherwise * disable it. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_unicast_forwarding_enabled4(bool v, string& error_msg); /** * Set the IPv6 unicast forwarding engine to enable or disable forwarding * of packets. * * @param v if true, then enable IPv6 unicast forwarding, otherwise * disable it. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_unicast_forwarding_enabled6(bool v, string& error_msg); /** * Enable or disable the acceptance of IPv6 Router Advertisement messages * from other routers. It should be enabled for hosts, and disabled for * routers. * * @param v if true, then enable the acceptance of IPv6 Router * Advertisement messages, otherwise disable it. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_accept_rtadv_enabled6(bool v, string& error_msg); /** * Add a single IPv4 forwarding entry. * * Must be within a configuration interval. * * @param fte the entry to add. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int add_entry4(const Fte4& fte); /** * Delete a single IPv4 forwarding entry. * * Must be with a configuration interval. * * @param fte the entry to delete. Only destination and netmask are used. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int delete_entry4(const Fte4& fte); /** * Set the IPv4 unicast forwarding table. * * @param fte_list the list with all entries to install into * the IPv4 unicast forwarding table. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int set_table4(const list& fte_list); /** * Delete all entries in the IPv4 unicast forwarding table. * * Must be within a configuration interval. * * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int delete_all_entries4(); /** * Lookup an IPv4 route by destination address. * * @param dst host address to resolve. * @param fte return-by-reference forwarding table entry. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int lookup_route_by_dest4(const IPv4& dst, Fte4& fte); /** * Lookup an IPv4 route by network address. * * @param dst network address to resolve. * @param fte return-by-reference forwarding table entry. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int lookup_route_by_network4(const IPv4Net& dst, Fte4& fte); /** * Obtain the IPv4 unicast forwarding table. * * @param fte_list the return-by-reference list with all entries in * the IPv4 unicast forwarding table. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int get_table4(list& fte_list); /** * Add a single IPv6 forwarding entry. * * Must be within a configuration interval. * * @param fte the entry to add. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int add_entry6(const Fte6& fte); /** * Set the IPv6 unicast forwarding table. * * @param fte_list the list with all entries to install into * the IPv6 unicast forwarding table. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int set_table6(const list& fte_list); /** * Delete a single IPv6 forwarding entry. * * Must be within a configuration interval. * * @param fte the entry to delete. Only destination and netmask are used. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int delete_entry6(const Fte6& fte); /** * Delete all entries in the IPv6 unicast forwarding table. * * Must be within a configuration interval. * * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int delete_all_entries6(); /** * Lookup an IPv6 route by destination address. * * @param dst host address to resolve. * @param fte return-by-reference forwarding table entry. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int lookup_route_by_dest6(const IPv6& dst, Fte6& fte); /** * Lookup an IPv6 route by network address. * * @param dst network address to resolve. * @param fte return-by-reference forwarding table entry. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int lookup_route_by_network6(const IPv6Net& dst, Fte6& fte); /** * Obtain the IPv6 unicast forwarding table. * * @param fte_list the return-by-reference list with all entries in * the IPv6 unicast forwarding table. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int get_table6(list& fte_list); /** * Add a FIB table observer. * * @param fib_table_observer the FIB table observer to add. * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_fib_table_observer(FibTableObserverBase* fib_table_observer); /** * Delete a FIB table observer. * * @param fib_table_observer the FIB table observer to delete. * @return XORP_OK on success, otherwise XORP_ERROR. */ int delete_fib_table_observer(FibTableObserverBase* fib_table_observer); /** * Propagate FIB changes to all FIB table observers. * * @param fte_list the list with the FIB changes. * @param fibconfig_table_observer the method that reports the FIB changes. */ void propagate_fib_changes(const list& fte_list, const FibConfigTableObserver* fibconfig_table_observer); /** * Get the IPv4 Trie (used for testing purpose). * * @return the IPv4 Trie. */ Trie4& trie4() { return _trie4; } /** * Get the IPv6 Trie (used for testing purpose). * * @return the IPv6 Trie. */ Trie6& trie6() { return _trie6; } protected: Trie4 _trie4; // IPv4 trie (used for testing purpose) Trie6 _trie6; // IPv6 trie (used for testing purpose) private: FeaNode& _fea_node; EventLoop& _eventloop; #ifndef XORP_DISABLE_PROFILE Profile& _profile; #endif NexthopPortMapper& _nexthop_port_mapper; const IfTree& _system_config_iftree; const IfTree& _merged_config_iftree; // // The FIB transaction manager // FibConfigTransactionManager* _ftm; // // The registered plugins // list _fibconfig_forwarding_plugins; list _fibconfig_entry_gets; list _fibconfig_entry_sets; list _fibconfig_entry_observers; list _fibconfig_table_gets; list _fibconfig_table_sets; list _fibconfig_table_observers; // // Configured unicast forwarding entries properties // bool _unicast_forwarding_entries_retain_on_startup4; bool _unicast_forwarding_entries_retain_on_shutdown4; bool _unicast_forwarding_entries_retain_on_startup6; bool _unicast_forwarding_entries_retain_on_shutdown6; uint32_t _unicast_forwarding_table_id4; bool _unicast_forwarding_table_id4_is_configured; uint32_t _unicast_forwarding_table_id6; bool _unicast_forwarding_table_id6_is_configured; // // Misc other state // bool _is_running; list _fib_table_observers; }; /** * A base class that can be used by clients interested in observing * changes in the Forwarding Information Base. */ class FibTableObserverBase { public: FibTableObserverBase() {} virtual ~FibTableObserverBase() {} /** * 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. */ virtual void process_fib_changes(const list& fte_list) = 0; #ifdef HAVE_IPV6 /** * 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. */ virtual void process_fib_changes(const list& fte_list) = 0; #endif private: }; #endif // __FEA_FIBCONFIG_HH__ xorp/fea/xrl_io_ip_manager.hh0000664000076400007640000000416211421137511016411 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/xrl_io_ip_manager.hh,v 1.6 2008/10/02 21:56:51 bms Exp $ #ifndef __FEA_XRL_IO_IP_MANAGER_HH__ #define __FEA_XRL_IO_IP_MANAGER_HH__ #include "io_ip_manager.hh" class XrlRouter; /** * @short A class that is the bridge between the raw IP I/O communications * and the XORP XRL interface. */ class XrlIoIpManager : public IoIpManagerReceiver { public: /** * Constructor. */ XrlIoIpManager(IoIpManager& io_ip_manager, XrlRouter& xrl_router); /** * Destructor. */ virtual ~XrlIoIpManager(); /** * Data received event. * * @param receiver_name the name of the receiver to send the * IP packet to. * @param header the IP header information. * @param payload the payload, everything after the IP header * and options. */ void recv_event(const string& receiver_name, const struct IPvXHeaderInfo& header, const vector& payload); private: XrlRouter& xrl_router() { return _xrl_router; } /** * Method to be called by XRL sending filter invoker */ void xrl_send_recv_cb(const XrlError& xrl_error, int family, string receiver_name); IoIpManager& _io_ip_manager; XrlRouter& _xrl_router; }; #endif // __FEA_XRL_IO_IP_MANAGER_HH__ xorp/fea/xrl_fea_target.cc0000664000076400007640000035534311540225525015726 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2007-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // FEA (Forwarding Engine Abstraction) XRL target implementation. // #define PROFILE_UTILS_REQUIRED #include "fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/status_codes.h" #include "libxorp/eventloop.hh" #include "libproto/packet.hh" #include "libxipc/xrl_std_router.hh" #ifndef XORP_DISABLE_PROFILE #include "xrl/interfaces/profile_client_xif.hh" #endif // // XXX: file "libxorp/profile.hh" must be included after // "libxipc/xrl_std_router.hh" and "xrl/interfaces/profile_client_xif.hh" // Sigh... // #include "libxorp/profile.hh" #include "fea_node.hh" #include "fibconfig_transaction.hh" #ifndef XORP_DISABLE_FIREWALL #include "firewall_transaction.hh" #endif #include "ifconfig_transaction.hh" #include "libfeaclient_bridge.hh" #ifndef XORP_DISABLE_PROFILE #include "profile_vars.hh" #endif #include "xrl_fea_target.hh" #ifdef XORP_USE_CLICK #include "fea/data_plane/managers/fea_data_plane_manager_click.hh" #endif XrlFeaTarget::XrlFeaTarget(EventLoop& eventloop, FeaNode& fea_node, XrlRouter& xrl_router, #ifndef XORP_DISABLE_PROFILE Profile& profile, #endif XrlFibClientManager& xrl_fib_client_manager, LibFeaClientBridge& lib_fea_client_bridge) : XrlFeaTargetBase(&xrl_router), _eventloop(eventloop), _fea_node(fea_node), _xrl_router(xrl_router), #ifndef XORP_DISABLE_PROFILE _profile(profile), #endif _xrl_fib_client_manager(xrl_fib_client_manager), _ifconfig(fea_node.ifconfig()), #ifndef XORP_DISABLE_FIREWALL _firewall_manager(fea_node.firewall_manager()), #endif _fibconfig(fea_node.fibconfig()), _io_link_manager(fea_node.io_link_manager()), _io_ip_manager(fea_node.io_ip_manager()), _io_tcpudp_manager(fea_node.io_tcpudp_manager()), _lib_fea_client_bridge(lib_fea_client_bridge), _is_running(false), _is_shutdown_received(false) #ifdef XORP_USE_CLICK , _fea_data_plane_manager_click(NULL) #endif { } XrlFeaTarget::~XrlFeaTarget() { shutdown(); } int XrlFeaTarget::startup() { _is_running = true; return (XORP_OK); } int XrlFeaTarget::shutdown() { _is_running = false; return (XORP_OK); } bool XrlFeaTarget::is_running() const { return (_is_running); } XrlCmdError XrlFeaTarget::common_0_1_get_target_name( // Output values, string& name) { name = this->get_name(); return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::common_0_1_get_version( // Output values, string& version) { version = this->version(); return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::common_0_1_get_status( // Output values, uint32_t& status, string& reason) { ProcessStatus s; string r; s = _ifconfig.status(r); // If it's bad news, don't bother to ask any other modules. switch (s) { case PROC_FAILED: case PROC_SHUTDOWN: case PROC_DONE: status = s; reason = r; return XrlCmdError::OKAY(); case PROC_NOT_READY: reason = r; break; case PROC_READY: break; case PROC_NULL: //can't be running and in this state abort(); case PROC_STARTUP: //can't be responding to an XRL and in this state abort(); } status = s; if (_is_shutdown_received) { status = PROC_SHUTDOWN; // XXX: the process received shutdown request reason = ""; } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::common_0_1_shutdown() { _is_shutdown_received = true; return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::finder_event_observer_0_1_xrl_target_birth( // Input values, const string& target_class, const string& target_instance) { debug_msg("XRL target birth event %s/%s\n", target_class.c_str(), target_instance.c_str()); _fea_node.fea_io().instance_birth(target_instance); return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::finder_event_observer_0_1_xrl_target_death( // Input values, const string& target_class, const string& target_instance) { debug_msg("XRL target death event %s/%s\n", target_class.c_str(), target_instance.c_str()); _fea_node.fea_io().instance_death(target_instance); return XrlCmdError::OKAY(); } #ifdef XORP_USE_CLICK /** * Load Click FEA support. */ XrlCmdError XrlFeaTarget::fea_click_0_1_load_click() { string error_msg; if (_fea_data_plane_manager_click != NULL) { error_msg = c_format("Data plane manager %s is already loaded", _fea_data_plane_manager_click->manager_name().c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } _fea_data_plane_manager_click = new FeaDataPlaneManagerClick(_fea_node); if (_fea_node.register_data_plane_manager(_fea_data_plane_manager_click, false) != XORP_OK) { error_msg = c_format("Cannot register data plane manager %s", _fea_data_plane_manager_click->manager_name().c_str()); delete _fea_data_plane_manager_click; _fea_data_plane_manager_click = NULL; return XrlCmdError::COMMAND_FAILED(error_msg); } if (_fea_data_plane_manager_click->start_manager(error_msg) != XORP_OK) { error_msg = c_format("Cannot start data plane manager %s: %s", _fea_data_plane_manager_click->manager_name().c_str(), error_msg.c_str()); delete _fea_data_plane_manager_click; _fea_data_plane_manager_click = NULL; return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } /** * Unload Click FEA support. */ XrlCmdError XrlFeaTarget::fea_click_0_1_unload_click() { string error_msg; if (_fea_data_plane_manager_click == NULL) { error_msg = c_format("Data plane manager Click is not loaded"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (_fea_node.unregister_data_plane_manager(_fea_data_plane_manager_click) != XORP_OK) { error_msg = c_format("Cannot unregister data plane manager %s", _fea_data_plane_manager_click->manager_name().c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } // XXX: The entry is deleted when unregistered _fea_data_plane_manager_click = NULL; return XrlCmdError::OKAY(); } /** * Enable/disable Click FEA support. * * @param enable if true, then enable the Click FEA support, otherwise * disable it. */ XrlCmdError XrlFeaTarget::fea_click_0_1_enable_click( // Input values, const bool& enable) { string error_msg; if (_fea_data_plane_manager_click == NULL) { error_msg = c_format("Data plane manager Click is not loaded"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (_fea_data_plane_manager_click->enable_click(enable, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } /** * Start Click FEA support. */ XrlCmdError XrlFeaTarget::fea_click_0_1_start_click() { string error_msg; if (_fea_data_plane_manager_click == NULL) { error_msg = c_format("Data plane manager Click is not loaded"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (_fea_data_plane_manager_click->start_plugins(error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } /** * Stop Click FEA support. */ XrlCmdError XrlFeaTarget::fea_click_0_1_stop_click() { string error_msg; if (_fea_data_plane_manager_click == NULL) { error_msg = c_format("Data plane manager Click is not loaded"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (_fea_data_plane_manager_click->stop_plugins(error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } /** * Enable/disable duplicating the Click routes to the system kernel. * * @param enable if true, then enable duplicating the Click routes to the * system kernel, otherwise disable it. */ XrlCmdError XrlFeaTarget::fea_click_0_1_enable_duplicate_routes_to_kernel( // Input values, const bool& enable) { string error_msg; if (_fea_data_plane_manager_click == NULL) { error_msg = c_format("Data plane manager Click is not loaded"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (_fea_data_plane_manager_click->enable_duplicate_routes_to_kernel( enable, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } /** * Enable/disable kernel-level Click FEA support. * * @param enable if true, then enable the kernel-level Click FEA support, * otherwise disable it. */ XrlCmdError XrlFeaTarget::fea_click_0_1_enable_kernel_click( // Input values, const bool& enable) { string error_msg; if (_fea_data_plane_manager_click == NULL) { error_msg = c_format("Data plane manager Click is not loaded"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (_fea_data_plane_manager_click->enable_kernel_click(enable, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } /** * Enable/disable installing kernel-level Click on startup. * * @param enable if true, then install kernel-level Click on startup. */ XrlCmdError XrlFeaTarget::fea_click_0_1_enable_kernel_click_install_on_startup( // Input values, const bool& enable) { string error_msg; if (_fea_data_plane_manager_click == NULL) { error_msg = c_format("Data plane manager Click is not loaded"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (_fea_data_plane_manager_click->enable_kernel_click_install_on_startup( enable, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } /** * Specify the list of kernel Click modules to load on startup if * installing kernel-level Click on startup is enabled. The file names of * the kernel modules are separated by colon. * * @param modules the list of kernel Click modules (separated by colon) to * load. */ XrlCmdError XrlFeaTarget::fea_click_0_1_set_kernel_click_modules( // Input values, const string& modules) { list modules_list; string error_msg; if (_fea_data_plane_manager_click == NULL) { error_msg = c_format("Data plane manager Click is not loaded"); return XrlCmdError::COMMAND_FAILED(error_msg); } // // Split the string with the names of the modules (separated by colon) // string modules_tmp = modules; string::size_type colon; string name; do { if (modules_tmp.empty()) break; colon = modules_tmp.find(':'); name = modules_tmp.substr(0, colon); if (colon != string::npos) modules_tmp = modules_tmp.substr(colon + 1); else modules_tmp.erase(); if (! name.empty()) modules_list.push_back(name); } while (true); if (_fea_data_plane_manager_click->set_kernel_click_modules(modules_list, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } /** * Specify the kernel-level Click mount directory. * * @param directory the kernel-level Click mount directory. */ XrlCmdError XrlFeaTarget::fea_click_0_1_set_kernel_click_mount_directory( // Input values, const string& directory) { string error_msg; if (_fea_data_plane_manager_click == NULL) { error_msg = c_format("Data plane manager Click is not loaded"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (_fea_data_plane_manager_click->set_kernel_click_mount_directory( directory, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } /** * Specify the external program to generate the kernel-level Click * configuration. * * @param kernel_click_config_generator_file the name of the external * program to generate the kernel-level Click configuration. */ XrlCmdError XrlFeaTarget::fea_click_0_1_set_kernel_click_config_generator_file( // Input values, const string& kernel_click_config_generator_file) { string error_msg; if (_fea_data_plane_manager_click == NULL) { error_msg = c_format("Data plane manager Click is not loaded"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (_fea_data_plane_manager_click->set_kernel_click_config_generator_file( kernel_click_config_generator_file, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } /** * Enable/disable user-level Click FEA support. * * @param enable if true, then enable the user-level Click FEA support, * otherwise disable it. */ XrlCmdError XrlFeaTarget::fea_click_0_1_enable_user_click( // Input values, const bool& enable) { string error_msg; if (_fea_data_plane_manager_click == NULL) { error_msg = c_format("Data plane manager Click is not loaded"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (_fea_data_plane_manager_click->enable_user_click(enable, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } /** * Specify the user-level Click command file. * * @param user_click_command_file the name of the user-level Click command * file. */ XrlCmdError XrlFeaTarget::fea_click_0_1_set_user_click_command_file( // Input values, const string& user_click_command_file) { string error_msg; if (_fea_data_plane_manager_click == NULL) { error_msg = c_format("Data plane manager Click is not loaded"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (_fea_data_plane_manager_click->set_user_click_command_file( user_click_command_file, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } /** * Specify the extra arguments to the user-level Click command. * * @param user_click_command_extra_arguments the extra arguments to the * user-level Click command. */ XrlCmdError XrlFeaTarget::fea_click_0_1_set_user_click_command_extra_arguments( // Input values, const string& user_click_command_extra_arguments) { string error_msg; if (_fea_data_plane_manager_click == NULL) { error_msg = c_format("Data plane manager Click is not loaded"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (_fea_data_plane_manager_click->set_user_click_command_extra_arguments( user_click_command_extra_arguments, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } /** * Specify whether to execute on startup the user-level Click command. * * @param user_click_command_execute_on_startup if true, then execute the * user-level Click command on startup. */ XrlCmdError XrlFeaTarget::fea_click_0_1_set_user_click_command_execute_on_startup( // Input values, const bool& user_click_command_execute_on_startup) { string error_msg; if (_fea_data_plane_manager_click == NULL) { error_msg = c_format("Data plane manager Click is not loaded"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (_fea_data_plane_manager_click->set_user_click_command_execute_on_startup( user_click_command_execute_on_startup, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } /** * Specify the address to use for control access to the user-level * Click. * * @param user_click_control_address the address to use for * control access to the user-level Click. */ XrlCmdError XrlFeaTarget::fea_click_0_1_set_user_click_control_address( // Input values, const IPv4& user_click_control_address) { string error_msg; if (_fea_data_plane_manager_click == NULL) { error_msg = c_format("Data plane manager Click is not loaded"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (_fea_data_plane_manager_click->set_user_click_control_address( user_click_control_address, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } /** * Specify the socket port to use for control access to the user-level * Click. * * @param user_click_control_socket_port the socket port to use for * control access to the user-level Click. */ XrlCmdError XrlFeaTarget::fea_click_0_1_set_user_click_control_socket_port( // Input values, const uint32_t& user_click_control_socket_port) { string error_msg; if (user_click_control_socket_port > 0xffff) { error_msg = c_format("Click control socket port %u is out of range", user_click_control_socket_port); return XrlCmdError::COMMAND_FAILED(error_msg); } if (_fea_data_plane_manager_click == NULL) { error_msg = c_format("Data plane manager Click is not loaded"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (_fea_data_plane_manager_click->set_user_click_control_socket_port( user_click_control_socket_port, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } /** * Specify the configuration file to be used by user-level Click on * startup. * * @param user_click_startup_config_file the name of the configuration * file to be used by user-level Click on startup. */ XrlCmdError XrlFeaTarget::fea_click_0_1_set_user_click_startup_config_file( // Input values, const string& user_click_startup_config_file) { string error_msg; if (_fea_data_plane_manager_click == NULL) { error_msg = c_format("Data plane manager Click is not loaded"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (_fea_data_plane_manager_click->set_user_click_startup_config_file( user_click_startup_config_file, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } /** * Specify the external program to generate the user-level Click * configuration. * * @param user_click_config_generator_file the name of the external * program to generate the user-level Click configuration. */ XrlCmdError XrlFeaTarget::fea_click_0_1_set_user_click_config_generator_file( // Input values, const string& user_click_config_generator_file) { string error_msg; if (_fea_data_plane_manager_click == NULL) { error_msg = c_format("Data plane manager Click is not loaded"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (_fea_data_plane_manager_click->set_user_click_config_generator_file( user_click_config_generator_file, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } #endif //click /** * Add a FIB client. * * @param target_name the target name of the FIB client to add. */ XrlCmdError XrlFeaTarget::fea_fib_0_1_add_fib_client4( // Input values, const string& client_target_name, const bool& send_updates, const bool& send_resolves) { return _xrl_fib_client_manager.add_fib_client4(client_target_name, send_updates, send_resolves); } /** * Delete a FIB client. * * @param target_name the target name of the FIB client to delete. */ XrlCmdError XrlFeaTarget::fea_fib_0_1_delete_fib_client4( // Input values, const string& client_target_name) { return _xrl_fib_client_manager.delete_fib_client4(client_target_name); } #ifdef HAVE_IPV6 XrlCmdError XrlFeaTarget::fea_fib_0_1_add_fib_client6( // Input values, const string& client_target_name, const bool& send_updates, const bool& send_resolves) { return _xrl_fib_client_manager.add_fib_client6(client_target_name, send_updates, send_resolves); } XrlCmdError XrlFeaTarget::fea_fib_0_1_delete_fib_client6( // Input values, const string& client_target_name) { return _xrl_fib_client_manager.delete_fib_client6(client_target_name); } #endif #ifndef XORP_DISABLE_FIREWALL XrlCmdError XrlFeaTarget::fea_firewall_0_1_startup_firewall() { return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::fea_firewall_0_1_start_transaction( // Output values, uint32_t& tid) { string error_msg; if (_firewall_manager.start_transaction(tid, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::fea_firewall_0_1_commit_transaction( // Input values, const uint32_t& tid) { string error_msg; if (_firewall_manager.commit_transaction(tid, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::fea_firewall_0_1_abort_transaction( // Input values, const uint32_t& tid) { string error_msg; if (_firewall_manager.abort_transaction(tid, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::fea_firewall_0_1_add_entry4( // Input values, const uint32_t& tid, const uint32_t& rule_number, const string& ifname, const string& vifname, const IPv4Net& src_network, const IPv4Net& dst_network, const uint32_t& ip_protocol, const uint32_t& src_port_begin, const uint32_t& src_port_end, const uint32_t& dst_port_begin, const uint32_t& dst_port_end, const string& action) { FirewallEntry::Action action_value = FirewallEntry::str2action(action); string error_msg; if (action_value == FirewallEntry::ACTION_INVALID) { error_msg = c_format("Invalid firewall action: %s", action.c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } FirewallEntry firewall_entry(rule_number, ifname, vifname, IPvXNet(src_network), IPvXNet(dst_network), ip_protocol, src_port_begin, src_port_end, dst_port_begin, dst_port_end, action_value); if (_firewall_manager.add_transaction_operation( tid, new FirewallAddEntry4(_firewall_manager, firewall_entry), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::fea_firewall_0_1_replace_entry4( // Input values, const uint32_t& tid, const uint32_t& rule_number, const string& ifname, const string& vifname, const IPv4Net& src_network, const IPv4Net& dst_network, const uint32_t& ip_protocol, const uint32_t& src_port_begin, const uint32_t& src_port_end, const uint32_t& dst_port_begin, const uint32_t& dst_port_end, const string& action) { FirewallEntry::Action action_value = FirewallEntry::str2action(action); string error_msg; if (action_value == FirewallEntry::ACTION_INVALID) { error_msg = c_format("Invalid firewall action: %s", action.c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } FirewallEntry firewall_entry(rule_number, ifname, vifname, IPvXNet(src_network), IPvXNet(dst_network), ip_protocol, src_port_begin, src_port_end, dst_port_begin, dst_port_end, action_value); if (_firewall_manager.add_transaction_operation( tid, new FirewallReplaceEntry4(_firewall_manager, firewall_entry), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::fea_firewall_0_1_delete_entry4( // Input values, const uint32_t& tid, const uint32_t& rule_number, const string& ifname, const string& vifname, const IPv4Net& src_network, const IPv4Net& dst_network, const uint32_t& ip_protocol, const uint32_t& src_port_begin, const uint32_t& src_port_end, const uint32_t& dst_port_begin, const uint32_t& dst_port_end) { string error_msg; FirewallEntry::Action action_value = FirewallEntry::ACTION_ANY; FirewallEntry firewall_entry(rule_number, ifname, vifname, IPvXNet(src_network), IPvXNet(dst_network), ip_protocol, src_port_begin, src_port_end, dst_port_begin, dst_port_end, action_value); if (_firewall_manager.add_transaction_operation( tid, new FirewallDeleteEntry4(_firewall_manager, firewall_entry), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::fea_firewall_0_1_delete_all_entries4( // Input values, const uint32_t& tid) { string error_msg; if (_firewall_manager.add_transaction_operation( tid, new FirewallDeleteAllEntries4(_firewall_manager), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::fea_firewall_0_1_get_entry_list_start4( // Output values, uint32_t& token, bool& more) { string error_msg; if (_firewall_manager.get_entry_list_start4(token, more, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::fea_firewall_0_1_get_entry_list_next4( // Input values, const uint32_t& token, // Output values, uint32_t& rule_number, string& ifname, string& vifname, IPv4Net& src_network, IPv4Net& dst_network, uint32_t& ip_protocol, uint32_t& src_port_begin, uint32_t& src_port_end, uint32_t& dst_port_begin, uint32_t& dst_port_end, string& action, bool& more) { string error_msg; FirewallEntry firewall_entry(IPv4::af()); if (_firewall_manager.get_entry_list_next4(token, firewall_entry, more, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } // Extract the fields rule_number = firewall_entry.rule_number(); ifname = firewall_entry.ifname(); vifname = firewall_entry.vifname(); src_network = firewall_entry.src_network().get_ipv4net(); dst_network = firewall_entry.dst_network().get_ipv4net(); ip_protocol = firewall_entry.ip_protocol(); src_port_begin = firewall_entry.src_port_begin(); src_port_end = firewall_entry.src_port_end(); dst_port_begin = firewall_entry.dst_port_begin(); dst_port_end = firewall_entry.dst_port_end(); action = firewall_entry.action(); return XrlCmdError::OKAY(); } #ifdef HAVE_IPV6 XrlCmdError XrlFeaTarget::fea_firewall_0_1_add_entry6( // Input values, const uint32_t& tid, const uint32_t& rule_number, const string& ifname, const string& vifname, const IPv6Net& src_network, const IPv6Net& dst_network, const uint32_t& ip_protocol, const uint32_t& src_port_begin, const uint32_t& src_port_end, const uint32_t& dst_port_begin, const uint32_t& dst_port_end, const string& action) { FirewallEntry::Action action_value = FirewallEntry::str2action(action); string error_msg; if (action_value == FirewallEntry::ACTION_INVALID) { error_msg = c_format("Invalid firewall action: %s", action.c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } FirewallEntry firewall_entry(rule_number, ifname, vifname, IPvXNet(src_network), IPvXNet(dst_network), ip_protocol, src_port_begin, src_port_end, dst_port_begin, dst_port_end, action_value); if (_firewall_manager.add_transaction_operation( tid, new FirewallAddEntry6(_firewall_manager, firewall_entry), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::fea_firewall_0_1_replace_entry6( // Input values, const uint32_t& tid, const uint32_t& rule_number, const string& ifname, const string& vifname, const IPv6Net& src_network, const IPv6Net& dst_network, const uint32_t& ip_protocol, const uint32_t& src_port_begin, const uint32_t& src_port_end, const uint32_t& dst_port_begin, const uint32_t& dst_port_end, const string& action) { FirewallEntry::Action action_value = FirewallEntry::str2action(action); string error_msg; if (action_value == FirewallEntry::ACTION_INVALID) { error_msg = c_format("Invalid firewall action: %s", action.c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } FirewallEntry firewall_entry(rule_number, ifname, vifname, IPvXNet(src_network), IPvXNet(dst_network), ip_protocol, src_port_begin, src_port_end, dst_port_begin, dst_port_end, action_value); if (_firewall_manager.add_transaction_operation( tid, new FirewallReplaceEntry6(_firewall_manager, firewall_entry), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::fea_firewall_0_1_delete_entry6( // Input values, const uint32_t& tid, const uint32_t& rule_number, const string& ifname, const string& vifname, const IPv6Net& src_network, const IPv6Net& dst_network, const uint32_t& ip_protocol, const uint32_t& src_port_begin, const uint32_t& src_port_end, const uint32_t& dst_port_begin, const uint32_t& dst_port_end) { string error_msg; FirewallEntry::Action action_value = FirewallEntry::ACTION_ANY; FirewallEntry firewall_entry(rule_number, ifname, vifname, IPvXNet(src_network), IPvXNet(dst_network), ip_protocol, src_port_begin, src_port_end, dst_port_begin, dst_port_end, action_value); if (_firewall_manager.add_transaction_operation( tid, new FirewallDeleteEntry6(_firewall_manager, firewall_entry), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::fea_firewall_0_1_delete_all_entries6( // Input values, const uint32_t& tid) { string error_msg; if (_firewall_manager.add_transaction_operation( tid, new FirewallDeleteAllEntries6(_firewall_manager), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::fea_firewall_0_1_get_entry_list_start6( // Output values, uint32_t& token, bool& more) { string error_msg; if (_firewall_manager.get_entry_list_start6(token, more, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::fea_firewall_0_1_get_entry_list_next6( // Input values, const uint32_t& token, // Output values, uint32_t& rule_number, string& ifname, string& vifname, IPv6Net& src_network, IPv6Net& dst_network, uint32_t& ip_protocol, uint32_t& src_port_begin, uint32_t& src_port_end, uint32_t& dst_port_begin, uint32_t& dst_port_end, string& action, bool& more) { string error_msg; FirewallEntry firewall_entry(IPv6::af()); if (_firewall_manager.get_entry_list_next6(token, firewall_entry, more, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } // Extract the fields rule_number = firewall_entry.rule_number(); ifname = firewall_entry.ifname(); vifname = firewall_entry.vifname(); src_network = firewall_entry.src_network().get_ipv6net(); dst_network = firewall_entry.dst_network().get_ipv6net(); ip_protocol = firewall_entry.ip_protocol(); src_port_begin = firewall_entry.src_port_begin(); src_port_end = firewall_entry.src_port_end(); dst_port_begin = firewall_entry.dst_port_begin(); dst_port_end = firewall_entry.dst_port_end(); action = firewall_entry.action(); return XrlCmdError::OKAY(); } #endif //ipv6 #endif // firewall XrlCmdError XrlFeaTarget::ifmgr_0_1_set_restore_original_config_on_shutdown( // Input values, const bool& enable) { _ifconfig.set_restore_original_config_on_shutdown(enable); return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::ifmgr_0_1_get_configured_interface_names( // Output values, XrlAtomList& ifnames) { const IfTree& iftree = _ifconfig.merged_config(); for (IfTree::IfMap::const_iterator ii = iftree.interfaces().begin(); ii != iftree.interfaces().end(); ++ii) { ifnames.append(XrlAtom(ii->second->ifname())); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::ifmgr_0_1_get_configured_vif_names( const string& ifname, // Output values, XrlAtomList& vifnames) { const IfTreeInterface* ifp = NULL; string error_msg; ifp = _ifconfig.merged_config().find_interface(ifname); if (ifp == NULL) { error_msg = c_format("Interface %s not found", ifname.c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } for (IfTreeInterface::VifMap::const_iterator vi = ifp->vifs().begin(); vi != ifp->vifs().end(); ++vi) { vifnames.append(XrlAtom(vi->second->vifname())); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::ifmgr_0_1_get_configured_vif_flags( // Input values, const string& ifname, const string& vifname, // Output values, bool& enabled, bool& broadcast, bool& loopback, bool& point_to_point, bool& multicast) { const IfTreeVif* vifp = NULL; string error_msg; vifp = _ifconfig.merged_config().find_vif(ifname, vifname); if (vifp == NULL) { error_msg = c_format("Interface %s vif %s not found", ifname.c_str(), vifname.c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } enabled = vifp->enabled(); broadcast = vifp->broadcast(); loopback = vifp->loopback(); point_to_point = vifp->point_to_point(); multicast = vifp->multicast(); return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::ifmgr_0_1_get_configured_vif_pif_index( // Input values, const string& ifname, const string& vifname, // Output values, uint32_t& pif_index) { const IfTreeVif* vifp = NULL; string error_msg; vifp = _ifconfig.merged_config().find_vif(ifname, vifname); if (vifp == NULL) { error_msg = c_format("Interface %s vif %s not found", ifname.c_str(), vifname.c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } pif_index = vifp->pif_index(); return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::ifmgr_0_1_get_configured_interface_enabled( // Input values, const string& ifname, // Output values, bool& enabled) { const IfTreeInterface* ifp = NULL; string error_msg; ifp = _ifconfig.merged_config().find_interface(ifname); if (ifp == NULL) { error_msg = c_format("Interface %s not found", ifname.c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } enabled = ifp->enabled(); return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::ifmgr_0_1_get_configured_interface_discard( // Input values, const string& ifname, // Output values, bool& discard) { const IfTreeInterface* ifp = NULL; string error_msg; ifp = _ifconfig.merged_config().find_interface(ifname); if (ifp == NULL) { error_msg = c_format("Interface %s not found", ifname.c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } discard = ifp->discard(); return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::ifmgr_0_1_get_configured_interface_unreachable( // Input values, const string& ifname, // Output values, bool& unreachable) { const IfTreeInterface* ifp = NULL; string error_msg; ifp = _ifconfig.merged_config().find_interface(ifname); if (ifp == NULL) { error_msg = c_format("Interface %s not found", ifname.c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } unreachable = ifp->unreachable(); return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::ifmgr_0_1_get_configured_interface_management( // Input values, const string& ifname, // Output values, bool& management) { const IfTreeInterface* ifp = NULL; string error_msg; ifp = _ifconfig.merged_config().find_interface(ifname); if (ifp == NULL) { error_msg = c_format("Interface %s not found", ifname.c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } management = ifp->management(); return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::ifmgr_0_1_get_configured_mac( // Input values, const string& ifname, Mac& mac) { const IfTreeInterface* ifp = NULL; string error_msg; ifp = _ifconfig.merged_config().find_interface(ifname); if (ifp == NULL) { error_msg = c_format("Interface %s not found", ifname.c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } mac = ifp->mac(); return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::ifmgr_0_1_get_configured_mtu( // Input values, const string& ifname, uint32_t& mtu) { const IfTreeInterface* ifp = NULL; string error_msg; ifp = _ifconfig.merged_config().find_interface(ifname); if (ifp == NULL) { error_msg = c_format("Interface %s not found", ifname.c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } mtu = ifp->mtu(); return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::ifmgr_0_1_get_configured_no_carrier( // Input values, const string& ifname, bool& no_carrier) { const IfTreeInterface* ifp = NULL; string error_msg; ifp = _ifconfig.merged_config().find_interface(ifname); if (ifp == NULL) { error_msg = c_format("Interface %s not found", ifname.c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } no_carrier = ifp->no_carrier(); return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::ifmgr_0_1_get_configured_baudrate( // Input values, const string& ifname, uint64_t& baudrate) { const IfTreeInterface* ifp = NULL; string error_msg; ifp = _ifconfig.merged_config().find_interface(ifname); if (ifp == NULL) { error_msg = c_format("Interface %s not found", ifname.c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } // // TODO: XXX: The returned baudrate variable must be changed to uint64_t // after the XRLs start support u64 type. // baudrate = ifp->baudrate(); return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::ifmgr_0_1_get_configured_vif_enabled( // Input values, const string& ifname, const string& vifname, // Output values, bool& enabled) { const IfTreeVif* vifp = NULL; string error_msg; vifp = _ifconfig.merged_config().find_vif(ifname, vifname); if (vifp == NULL) { error_msg = c_format("Interface %s vif %s not found", ifname.c_str(), vifname.c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } enabled = vifp->enabled(); return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::ifmgr_0_1_get_configured_prefix4( // Input values, const string& ifname, const string& vifname, const IPv4& address, // Output values, uint32_t& prefix_len) { const IfTreeAddr4* ap = NULL; string error_msg; ap = _ifconfig.merged_config().find_addr(ifname, vifname, address); if (ap == NULL) { error_msg = c_format("Interface %s vif %s address %s not found", ifname.c_str(), vifname.c_str(), address.str().c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } prefix_len = ap->prefix_len(); return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::ifmgr_0_1_get_configured_broadcast4( // Input values, const string& ifname, const string& vifname, const IPv4& address, // Output values, IPv4& broadcast) { const IfTreeAddr4* ap = NULL; string error_msg; ap = _ifconfig.merged_config().find_addr(ifname, vifname, address); if (ap == NULL) { error_msg = c_format("Interface %s vif %s address %s not found", ifname.c_str(), vifname.c_str(), address.str().c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } broadcast = ap->bcast(); if ((! ap->broadcast()) || (broadcast == IPv4::ZERO())) { error_msg = c_format("No broadcast address associated with " "interface %s vif %s address %s", ifname.c_str(), vifname.c_str(), address.str().c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::ifmgr_0_1_get_configured_endpoint4( // Input values, const string& ifname, const string& vifname, const IPv4& address, // Output values, IPv4& endpoint) { const IfTreeAddr4* ap = NULL; string error_msg; ap = _ifconfig.merged_config().find_addr(ifname, vifname, address); if (ap == NULL) { error_msg = c_format("Interface %s vif %s address %s not found", ifname.c_str(), vifname.c_str(), address.str().c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } endpoint = ap->endpoint(); if ((! ap->point_to_point()) || (endpoint == IPv4::ZERO())) { error_msg = c_format("No endpoint address associated with " "interface %s vif %s address %s", ifname.c_str(), vifname.c_str(), address.str().c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::ifmgr_0_1_get_configured_vif_addresses4( // Input values, const string& ifname, const string& vifname, // Output values, XrlAtomList& addresses) { const IfTreeVif* vifp = NULL; string error_msg; vifp = _ifconfig.merged_config().find_vif(ifname, vifname); if (vifp == NULL) { error_msg = c_format("Interface %s vif %s not found", ifname.c_str(), vifname.c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } for (IfTreeVif::IPv4Map::const_iterator ai = vifp->ipv4addrs().begin(); ai != vifp->ipv4addrs().end(); ++ai) { addresses.append(XrlAtom(ai->second->addr())); } return XrlCmdError::OKAY(); } #ifdef HAVE_IPV6 XrlCmdError XrlFeaTarget::ifmgr_0_1_get_configured_prefix6( // Input values, const string& ifname, const string& vifname, const IPv6& address, // Output values, uint32_t& prefix_len) { const IfTreeAddr6* ap = NULL; string error_msg; ap = _ifconfig.merged_config().find_addr(ifname, vifname, address); if (ap == NULL) { error_msg = c_format("Interface %s vif %s address %s not found", ifname.c_str(), vifname.c_str(), address.str().c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } prefix_len = ap->prefix_len(); return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::ifmgr_0_1_get_configured_endpoint6( // Input values, const string& ifname, const string& vifname, const IPv6& address, // Output values, IPv6& endpoint) { const IfTreeAddr6* ap = NULL; string error_msg; ap = _ifconfig.merged_config().find_addr(ifname, vifname, address); if (ap == NULL) { error_msg = c_format("Interface %s vif %s address %s not found", ifname.c_str(), vifname.c_str(), address.str().c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } endpoint = ap->endpoint(); if ((! ap->point_to_point()) || (endpoint == IPv6::ZERO())) { error_msg = c_format("No endpoint address associated with " "interface %s vif %s address %s", ifname.c_str(), vifname.c_str(), address.str().c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::ifmgr_0_1_get_configured_vif_addresses6( // Input values, const string& ifname, const string& vifname, // Output values, XrlAtomList& addresses) { const IfTreeVif* vifp = NULL; string error_msg; vifp = _ifconfig.merged_config().find_vif(ifname, vifname); if (vifp == NULL) { error_msg = c_format("Interface %s vif %s not found", ifname.c_str(), vifname.c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } for (IfTreeVif::IPv6Map::const_iterator ai = vifp->ipv6addrs().begin(); ai != vifp->ipv6addrs().end(); ++ai) { addresses.append(XrlAtom(ai->second->addr())); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::ifmgr_0_1_get_configured_address_flags6( // Input values const string& ifname, const string& vifname, const IPv6& address, // Output values bool& up, bool& loopback, bool& point_to_point, bool& multicast) { const IfTreeAddr6* ap = NULL; string error_msg; ap = _ifconfig.merged_config().find_addr(ifname, vifname, address); if (ap == NULL) { error_msg = c_format("Interface %s vif %s address %s not found", ifname.c_str(), vifname.c_str(), address.str().c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } up = ap->enabled(); loopback = ap->loopback(); point_to_point = ap->point_to_point(); multicast = ap->multicast(); return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::ifmgr_0_1_get_configured_address_enabled6( // Input values, const string& ifname, const string& vifname, const IPv6& address, bool& enabled) { const IfTreeAddr6* ap = NULL; string error_msg; ap = _ifconfig.merged_config().find_addr(ifname, vifname, address); if (ap == NULL) { error_msg = c_format("Interface %s vif %s address %s not found", ifname.c_str(), vifname.c_str(), address.str().c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } enabled = ap->enabled(); return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::ifmgr_0_1_create_address6( // Input values, const uint32_t& tid, const string& ifname, const string& vifname, const IPv6& address) { string error_msg; if (_ifconfig.add_transaction_operation( tid, new AddAddr6(_ifconfig, ifname, vifname, address), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::ifmgr_0_1_delete_address6( // Input values, const uint32_t& tid, const string& ifname, const string& vifname, const IPv6& address) { string error_msg; if (_ifconfig.add_transaction_operation( tid, new RemoveAddr6(_ifconfig, ifname, vifname, address), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::ifmgr_0_1_set_address_enabled6( // Input values, const uint32_t& tid, const string& ifname, const string& vifname, const IPv6& address, const bool& enabled) { string error_msg; if (_ifconfig.add_transaction_operation( tid, new SetAddr6Enabled(_ifconfig, ifname, vifname, address, enabled), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::ifmgr_0_1_set_prefix6( // Input values, const uint32_t& tid, const string& ifname, const string& vifname, const IPv6& address, const uint32_t& prefix_len) { string error_msg; if (_ifconfig.add_transaction_operation( tid, new SetAddr6Prefix(_ifconfig, ifname, vifname, address, prefix_len), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::ifmgr_0_1_set_endpoint6( // Input values, const uint32_t& tid, const string& ifname, const string& vifname, const IPv6& address, const IPv6& endpoint) { string error_msg; if (_ifconfig.add_transaction_operation( tid, new SetAddr6Endpoint(_ifconfig, ifname, vifname, address, endpoint), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::fti_0_2_lookup_route_by_dest6( // Input values, const IPv6& dst, // Output values, IPv6Net& netmask, IPv6& nexthop, string& ifname, string& vifname, uint32_t& metric, uint32_t& admin_distance, string& protocol_origin) { Fte6 fte; if (_fibconfig.lookup_route_by_dest6(dst, fte) == XORP_OK) { netmask = fte.net(); nexthop = fte.nexthop(); ifname = fte.ifname(); vifname = fte.vifname(); metric = fte.metric(); admin_distance = fte.admin_distance(); // TODO: set the value of protocol_origin to something meaningful protocol_origin = "NOT_SUPPORTED"; return XrlCmdError::OKAY(); } return XrlCmdError::COMMAND_FAILED("No route for " + dst.str()); } XrlCmdError XrlFeaTarget::fti_0_2_lookup_route_by_network6( // Input values, const IPv6Net& dst, // Output values, IPv6& nexthop, string& ifname, string& vifname, uint32_t& metric, uint32_t& admin_distance, string& protocol_origin) { Fte6 fte; if (_fibconfig.lookup_route_by_network6(dst, fte) == XORP_OK) { nexthop = fte.nexthop(); ifname = fte.ifname(); vifname = fte.vifname(); metric = fte.metric(); admin_distance = fte.admin_distance(); // TODO: set the value of protocol_origin to something meaningful protocol_origin = "NOT_SUPPORTED"; return XrlCmdError::OKAY(); } return XrlCmdError::COMMAND_FAILED("No entry for " + dst.str()); } XrlCmdError XrlFeaTarget::fti_0_2_have_ipv6( // Output values, bool& result) { result = _fea_node.have_ipv6(); return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::fti_0_2_get_unicast_forwarding_enabled6( // Output values, bool& enabled) { string error_msg; if (_fibconfig.unicast_forwarding_enabled6(enabled, error_msg) != XORP_OK) return XrlCmdError::COMMAND_FAILED(error_msg); return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::fti_0_2_set_unicast_forwarding_enabled6( // Input values, const bool& enabled) { string error_msg; if (_fibconfig.set_unicast_forwarding_enabled6(enabled, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::fti_0_2_set_unicast_forwarding_entries_retain_on_startup6( // Input values, const bool& retain) { string error_msg; if (_fibconfig.set_unicast_forwarding_entries_retain_on_startup6( retain, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::fti_0_2_set_unicast_forwarding_entries_retain_on_shutdown6( // Input values, const bool& retain) { string error_msg; if (_fibconfig.set_unicast_forwarding_entries_retain_on_shutdown6( retain, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::fti_0_2_set_unicast_forwarding_table_id6( // Input values, const bool& is_configured, const uint32_t& table_id) { string error_msg; if (_fibconfig.set_unicast_forwarding_table_id6( is_configured, table_id, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::redist_transaction6_0_1_start_transaction( // Output values, uint32_t& tid) { string error_msg; if (_fibconfig.start_transaction(tid, error_msg) != XORP_OK) return XrlCmdError::COMMAND_FAILED(error_msg); return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::redist_transaction6_0_1_commit_transaction( // Input values, const uint32_t& tid) { string error_msg; if (_fibconfig.commit_transaction(tid, error_msg) != XORP_OK) return XrlCmdError::COMMAND_FAILED(error_msg); return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::redist_transaction6_0_1_abort_transaction( // Input values, const uint32_t& tid) { string error_msg; if (_fibconfig.abort_transaction(tid, error_msg) != XORP_OK) return XrlCmdError::COMMAND_FAILED(error_msg); return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::redist_transaction6_0_1_add_route( // Input values, const uint32_t& tid, const IPv6Net& dst, const IPv6& nexthop, const string& ifname, const string& vifname, const uint32_t& metric, const uint32_t& admin_distance, const string& cookie, const string& protocol_origin) { bool is_xorp_route; bool is_connected_route = false; string error_msg; debug_msg("redist_transaction6_0_1_add_route(): " "dst = %s nexthop = %s ifname = %s vifname = %s " "metric = %u admin_distance = %u protocol_origin = %s\n", dst.str().c_str(), nexthop.str().c_str(), ifname.c_str(), vifname.c_str(), XORP_UINT_CAST(metric), XORP_UINT_CAST(admin_distance), protocol_origin.c_str()); UNUSED(cookie); is_xorp_route = true; // XXX: unconditionally set to true // TODO: XXX: get rid of the hard-coded "connected" string here if (protocol_origin == "connected") is_connected_route = true; PROFILE(if (_profile.enabled(profile_route_in)) _profile.log(profile_route_in, c_format("add %s", dst.str().c_str()))); if (_fibconfig.add_transaction_operation( tid, new FibAddEntry6(_fibconfig, dst, nexthop, ifname, vifname, metric, admin_distance, is_xorp_route, is_connected_route), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::redist_transaction6_0_1_delete_route( // Input values, const uint32_t& tid, const IPv6Net& dst, const IPv6& nexthop, const string& ifname, const string& vifname, const uint32_t& metric, const uint32_t& admin_distance, const string& cookie, const string& protocol_origin) { bool is_xorp_route; bool is_connected_route = false; string error_msg; debug_msg("redist_transaction6_0_1_delete_route(): " "dst = %s nexthop = %s ifname = %s vifname = %s " "metric = %u admin_distance = %u protocol_origin = %s\n", dst.str().c_str(), nexthop.str().c_str(), ifname.c_str(), vifname.c_str(), XORP_UINT_CAST(metric), XORP_UINT_CAST(admin_distance), protocol_origin.c_str()); UNUSED(cookie); is_xorp_route = true; // XXX: unconditionally set to true // TODO: XXX: get rid of the hard-coded "connected" string here if (protocol_origin == "connected") is_connected_route = true; PROFILE(if (_profile.enabled(profile_route_in)) _profile.log(profile_route_in, c_format("delete %s", dst.str().c_str()))); if (_fibconfig.add_transaction_operation( tid, new FibDeleteEntry6(_fibconfig, dst, nexthop, ifname, vifname, metric, admin_distance, is_xorp_route, is_connected_route), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::redist_transaction6_0_1_delete_all_routes( // Input values, const uint32_t& tid, const string& cookie) { string error_msg; UNUSED(cookie); if (_fibconfig.add_transaction_operation( tid, new FibDeleteAllEntries6(_fibconfig), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } #endif XrlCmdError XrlFeaTarget::ifmgr_0_1_get_configured_address_flags4( // Input values const string& ifname, const string& vifname, const IPv4& address, // Output values bool& up, bool& broadcast, bool& loopback, bool& point_to_point, bool& multicast) { const IfTreeAddr4* ap = NULL; string error_msg; ap = _ifconfig.merged_config().find_addr(ifname, vifname, address); if (ap == NULL) { error_msg = c_format("Interface %s vif %s address %s not found", ifname.c_str(), vifname.c_str(), address.str().c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } up = ap->enabled(); broadcast = ap->broadcast(); loopback = ap->loopback(); point_to_point = ap->point_to_point(); multicast = ap->multicast(); return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::ifmgr_0_1_get_configured_address_enabled4( // Input values, const string& ifname, const string& vifname, const IPv4& address, bool& enabled) { const IfTreeAddr4* ap = NULL; string error_msg; ap = _ifconfig.merged_config().find_addr(ifname, vifname, address); if (ap == NULL) { error_msg = c_format("Interface %s vif %s address %s not found", ifname.c_str(), vifname.c_str(), address.str().c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } enabled = ap->enabled(); return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::ifmgr_0_1_start_transaction( // Output values, uint32_t& tid) { string error_msg; if (_ifconfig.start_transaction(tid, error_msg) != XORP_OK) return XrlCmdError::COMMAND_FAILED(error_msg); return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::ifmgr_0_1_commit_transaction( // Input values, const uint32_t& tid) { string error_msg; if (_ifconfig.commit_transaction(tid, error_msg) != XORP_OK) return XrlCmdError::COMMAND_FAILED(error_msg); return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::ifmgr_0_1_abort_transaction( // Input values, const uint32_t& tid) { string error_msg; if (_ifconfig.abort_transaction(tid, error_msg) != XORP_OK) return XrlCmdError::COMMAND_FAILED(error_msg); return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::ifmgr_0_1_create_interface( // Input values, const uint32_t& tid, const string& ifname) { string error_msg; if (_ifconfig.add_transaction_operation( tid, new AddInterface(_ifconfig, ifname), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::ifmgr_0_1_delete_interface( // Input values, const uint32_t& tid, const string& ifname) { string error_msg; XLOG_INFO("Deleting interface, ifname: %s\n", ifname.c_str()); // Hack: Remove multicast addrs first. --Ben string empty; _io_ip_manager.leave_all_multicast_groups(ifname, empty, error_msg); if (error_msg.size()) { XLOG_ERROR("%s", error_msg.c_str()); } if (_ifconfig.add_transaction_operation( tid, new RemoveInterface(_ifconfig, ifname), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::ifmgr_0_1_configure_all_interfaces_from_system( // Input values, const uint32_t& tid, const bool& enable) { string error_msg; if (_ifconfig.add_transaction_operation( tid, new ConfigureAllInterfacesFromSystem(_ifconfig, enable), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::ifmgr_0_1_configure_interface_from_system( // Input values, const uint32_t& tid, const string& ifname, const bool& enable) { string error_msg; if (_ifconfig.add_transaction_operation( tid, new ConfigureInterfaceFromSystem(_ifconfig, ifname, enable), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::ifmgr_0_1_set_interface_enabled( // Input values, const uint32_t& tid, const string& ifname, const bool& enabled) { string error_msg; if (_ifconfig.add_transaction_operation( tid, new SetInterfaceEnabled(_ifconfig, ifname, enabled), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::ifmgr_0_1_set_interface_discard( // Input values, const uint32_t& tid, const string& ifname, const bool& discard) { string error_msg; if (_ifconfig.add_transaction_operation( tid, new SetInterfaceDiscard(_ifconfig, ifname, discard), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::ifmgr_0_1_set_interface_unreachable( // Input values, const uint32_t& tid, const string& ifname, const bool& unreachable) { string error_msg; if (_ifconfig.add_transaction_operation( tid, new SetInterfaceUnreachable(_ifconfig, ifname, unreachable), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::ifmgr_0_1_set_interface_management( // Input values, const uint32_t& tid, const string& ifname, const bool& management) { string error_msg; if (_ifconfig.add_transaction_operation( tid, new SetInterfaceManagement(_ifconfig, ifname, management), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::ifmgr_0_1_set_mac( // Input values, const uint32_t& tid, const string& ifname, const Mac& mac) { string error_msg; if (_ifconfig.add_transaction_operation( tid, new SetInterfaceMac(_ifconfig, ifname, mac), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::ifmgr_0_1_create_mac( // Input values, const string& ifname, const Mac& mac) { string error_msg; if (add_remove_mac(true, ifname, mac, error_msg) != XORP_OK) return XrlCmdError::COMMAND_FAILED(error_msg); return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::ifmgr_0_1_delete_address_atomic( // Input values, const string& ifname, const string& vifname, const IPv4& ip) { string error_msg; if (add_remove_address(false, ifname, vifname, ip, 0, error_msg) != XORP_OK) return XrlCmdError::COMMAND_FAILED(error_msg); return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::ifmgr_0_1_create_address_atomic( // Input values, const string& ifname, const string& vifname, const IPv4& ip, const uint32_t& prefix) { string error_msg; if (add_remove_address(true, ifname, vifname, ip, prefix, error_msg) != XORP_OK) return XrlCmdError::COMMAND_FAILED(error_msg); return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::ifmgr_0_1_delete_mac( // Input values, const string& ifname, const Mac& mac) { string error_msg; if (add_remove_mac(false, ifname, mac, error_msg) != XORP_OK) return XrlCmdError::COMMAND_FAILED(error_msg); return XrlCmdError::OKAY(); } int XrlFeaTarget::add_remove_mac(bool add, const string& ifname, const Mac& mac, string& error_msg) { IfTreeInterface* ifp; // // XXX: This whole thing is a hack. // // // There are two entities: // 1) The MAC of the interface. // 2) The multicast MACs of an interface. // // To "add" a MAC, we set the interface's MAC to the newly added MAC, and // put the old MAC in the multicast ones. To "delete", we kill a MAC from // the multicast ones, and stick it as the primary MAC. // // // Grab the set of configured MACs from somewhere. // // XXX This set should be stored elsewhere in process related // state - not the user config state. // ifp = _ifconfig.user_config().find_interface(ifname); if (ifp == NULL) { error_msg = c_format("Cannot %s MAC address %s on interface %s: " "unknown interface", (add) ? "add" : "remove", mac.str().c_str(), ifname.c_str()); return (XORP_ERROR); } // These are the multicast MACs. set& macs = ifp->macs(); // // This is the real MAC. // // XXX: Hopefully the user doesn't change it, otherwise this creates a // problem. To avoid the problem, we just need proper datastructures - a // stack of MACs. This will be fixed once we determine what is needed, // and this becomes less than a hack. // ifp = _ifconfig.merged_config().find_interface(ifname); XLOG_ASSERT(ifp != NULL); Mac current_mac = ifp->mac(); // // One MAC is OK, because we can change the primary MAC. // More MACs rely on the multicast trick. // if (add && macs.size()) { error_msg = c_format("Cannot add MAC address %s on interface %s: " "too many MACs", mac.str().c_str(), ifname.c_str()); return (XORP_ERROR); } if (add) { // Add the MAC // Sanity check if (macs.find(mac) != macs.end() || current_mac == mac) { error_msg = c_format("Cannot add MAC address %s on interface %s: " "MAC already exists, current_mac: %s mac count: %i", mac.str().c_str(), ifname.c_str(), current_mac.str().c_str(), (int)(macs.size())); // This doesn't seem so bad to me...going to log it and pass back success. --Ben XLOG_WARNING("%s", error_msg.c_str()); return XORP_OK; } if (macs.size()) XLOG_WARNING("More than one MAC added - use at your own risk"); if (set_mac(ifname, mac, error_msg) != XORP_OK) { error_msg = c_format("Cannot add MAC address %s on interface %s: %s", mac.str().c_str(), ifname.c_str(), error_msg.c_str()); return (XORP_ERROR); } // Add previous MAC as multicast one macs.insert(current_mac); if (_io_link_manager.add_multicast_mac(ifname, current_mac, error_msg) != XORP_OK) { XLOG_WARNING("Cannot add multicast MAC address %s " "on interface %s: %s", current_mac.str().c_str(), ifname.c_str(), error_msg.c_str()); } } else { // Remove the MAC Mac candidate_mac; // Removing current mac if (mac == current_mac) { if (macs.empty()) { error_msg = c_format("Cannot remove MAC address %s " "on interface %s: last address." " Will create a random MAC address for use on this interface.", mac.str().c_str(), ifname.c_str()); XLOG_WARNING("%s", error_msg.c_str()); uint8_t rnd_mac[6]; rnd_mac[0] = 0; for (unsigned int i = 1; iaddr()); // remove from spare mac-list before setting the new one. macs.erase(macs.begin()); } if (set_mac(ifname, candidate_mac, error_msg) != XORP_OK) { error_msg = c_format("Cannot replace MAC address %s with %s " "on interface %s: %s", mac.str().c_str(), candidate_mac.str().c_str(), ifname.c_str(), error_msg.c_str()); return (XORP_ERROR); } } else { IfTreeInterface::MacSet::iterator i = macs.find(mac); if (i == macs.end()) { error_msg = c_format("Cannot remove MAC address %s on " "interface %s: unknown address", mac.str().c_str(), ifname.c_str()); return (XORP_ERROR); } candidate_mac = *i; } // Remove MAC from multicast list (might not be in the list..no big deal if so) macs.erase(candidate_mac); if (_io_link_manager.remove_multicast_mac(ifname, candidate_mac, error_msg) != XORP_OK) { XLOG_WARNING("Cannot remove multicast MAC address %s " "on interface %s: %s", candidate_mac.str().c_str(), ifname.c_str(), error_msg.c_str()); } } return (XORP_OK); } int XrlFeaTarget::add_remove_address(bool add, const string& ifname, const string& vifname, const IPv4& ip, uint32_t prefix, string& error_msg) { uint32_t tid; XrlCmdError e = XrlCmdError::OKAY(); XLOG_WARNING("add_remove_address, add: %i vif: %s/%s ip: %s\n", (int)(add), ifname.c_str(), vifname.c_str(), ip.str().c_str()); if (!((e = ifmgr_0_1_start_transaction(tid))).isOK()) { error_msg = c_format("Cannot add/remove address %s on interface %s: " "cannot start the transaction, err: %s add: %i", ip.str().c_str(), ifname.c_str(), e.str().c_str(), (int)(add)); return (XORP_ERROR); } if (add) { if (!((e = ifmgr_0_1_create_address4(tid, ifname, vifname, ip))).isOK()) { ifmgr_0_1_abort_transaction(tid); error_msg = c_format("Cannot add IP address %s on interface %s: " "cannot perform the operation, err: %s", ip.str().c_str(), ifname.c_str(), e.str().c_str()); return (XORP_ERROR); } if (!((e = ifmgr_0_1_set_prefix4(tid, ifname, vifname, ip, prefix))).isOK()) { ifmgr_0_1_abort_transaction(tid); error_msg = c_format("Cannot set IP prefix %s/%i on interface %s: " "cannot perform the operation, err: %s", ip.str().c_str(), prefix, ifname.c_str(), e.str().c_str()); return (XORP_ERROR); } bool enabled = true; if (!((e = ifmgr_0_1_set_address_enabled4(tid, ifname, vifname, ip, enabled))).isOK()) { ifmgr_0_1_abort_transaction(tid); error_msg = c_format("Cannot set IP enabled %s/%i on interface %s: " "cannot perform the operation, err: %s", ip.str().c_str(), prefix, ifname.c_str(), e.str().c_str()); return (XORP_ERROR); } } else { if (!((e = ifmgr_0_1_delete_address4(tid, ifname, vifname, ip))).isOK()) { ifmgr_0_1_abort_transaction(tid); error_msg = c_format("Cannot delete IP address %s on interface %s: " "cannot perform the operation, err: %s", ip.str().c_str(), ifname.c_str(), e.str().c_str()); return (XORP_ERROR); } } if (!((e = ifmgr_0_1_commit_transaction(tid))).isOK()) { error_msg = c_format("Cannot add/delete address %s on interface %s: " "cannot commit the transaction, err: %s add: %i", ip.str().c_str(), ifname.c_str(), e.str().c_str(), (int)(add)); return (XORP_ERROR); } #if 0 // TODO: Should we send gratuitous arp here?? if (send_gratuitous_arps(ifname, mac, error_msg) != XORP_OK) { error_msg = c_format("Cannot set MAC address %s on interface %s: %s", ip.str().c_str(), ifname.c_str(), error_msg.c_str()); return (XORP_ERROR); } #endif XLOG_WARNING("returning from add_remove_address, add: %i vif: %s/%s ip: %s\n", (int)(add), ifname.c_str(), vifname.c_str(), ip.str().c_str()); return (XORP_OK); } int XrlFeaTarget::set_mac(const string& ifname, const Mac& mac, string& error_msg) { // // TODO: XXX: We should delegate this to ifconfig, but perhaps lets see // what we need exactly before polluting the rest of the code with // this MAC hack. // uint32_t tid; XrlCmdError e = XrlCmdError::OKAY(); if (!((e = ifmgr_0_1_start_transaction(tid))).isOK()) { error_msg = c_format("Cannot set MAC address %s on interface %s: " "cannot start the transaction, err: %s", mac.str().c_str(), ifname.c_str(), e.str().c_str()); return (XORP_ERROR); } if (!((e = ifmgr_0_1_set_mac(tid, ifname, mac))).isOK()) { ifmgr_0_1_abort_transaction(tid); error_msg = c_format("Cannot set MAC address %s on interface %s: " "cannot perform the operation, err: %s", mac.str().c_str(), ifname.c_str(), e.str().c_str()); return (XORP_ERROR); } if (!((e = ifmgr_0_1_commit_transaction(tid))).isOK()) { error_msg = c_format("Cannot set MAC address %s on interface %s: " "cannot commit the transaction, err: %s", mac.str().c_str(), ifname.c_str(), e.str().c_str()); return (XORP_ERROR); } if (send_gratuitous_arps(ifname, mac, error_msg) != XORP_OK) { error_msg = c_format("Cannot set MAC address %s on interface %s: %s", mac.str().c_str(), ifname.c_str(), error_msg.c_str()); return (XORP_ERROR); } return (XORP_OK); } int XrlFeaTarget::send_gratuitous_arps(const string& ifname, const Mac& mac, string& error_msg) { IfTreeInterface* ifp = _ifconfig.merged_config().find_interface(ifname); XLOG_ASSERT(ifp != NULL); if (! ifp->enabled()) { // TODO: Return success or an error? return (XORP_OK); } for (IfTreeInterface::VifMap::iterator i = ifp->vifs().begin(); i != ifp->vifs().end(); ++i) { const string& vifname = i->first; IfTreeVif* vifp = i->second; if (! vifp->enabled()) continue; for (IfTreeVif::IPv4Map::iterator j = vifp->ipv4addrs().begin(); j != vifp->ipv4addrs().end(); ++j) { const IPv4& ip = j->first; IfTreeAddr4* addr = j->second; if (! addr->enabled()) continue; vector data; ArpHeader::make_gratuitous(data, mac, ip); XrlCmdError e = raw_link_0_1_send(ifname, vifname, mac, Mac::BROADCAST(), ETHERTYPE_ARP, data); if (e != XrlCmdError::OKAY()) error_msg = c_format("Cannot send gratuitous ARP " "for MAC address %s on interface %s: %s", mac.str().c_str(), ifname.c_str(), e.str().c_str()); } // TODO: XXX: IPv6? } return (XORP_OK); } XrlCmdError XrlFeaTarget::ifmgr_0_1_restore_original_mac( // Input values, const uint32_t& tid, const string& ifname) { string error_msg; if (_ifconfig.add_transaction_operation( tid, new RestoreInterfaceMac(_ifconfig, ifname), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::ifmgr_0_1_set_mtu( // Input values, const uint32_t& tid, const string& ifname, const uint32_t& mtu) { string error_msg; if (_ifconfig.add_transaction_operation( tid, new SetInterfaceMtu(_ifconfig, ifname, mtu), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::ifmgr_0_1_restore_original_mtu( // Input values, const uint32_t& tid, const string& ifname) { string error_msg; if (_ifconfig.add_transaction_operation( tid, new RestoreInterfaceMtu(_ifconfig, ifname), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::ifmgr_0_1_create_vif( // Input values, const uint32_t& tid, const string& ifname, const string& vifname) { string error_msg; if (_ifconfig.add_transaction_operation( tid, new AddInterfaceVif(_ifconfig, ifname, vifname), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::ifmgr_0_1_delete_vif( // Input values, const uint32_t& tid, const string& ifname, const string& vifname) { string error_msg; XLOG_ERROR("Deleting vif, ifname: %s vif: %s\n", ifname.c_str(), vifname.c_str()); // Hack: Remove multicast addrs first. --Ben _io_ip_manager.leave_all_multicast_groups(ifname, vifname, error_msg); if (error_msg.size()) { XLOG_ERROR("%s", error_msg.c_str()); } if (_ifconfig.add_transaction_operation( tid, new RemoveInterfaceVif(_ifconfig, ifname, vifname), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::ifmgr_0_1_set_vif_enabled( // Input values, const uint32_t& tid, const string& ifname, const string& vifname, const bool& enabled) { string error_msg; if (_ifconfig.add_transaction_operation( tid, new SetVifEnabled(_ifconfig, ifname, vifname, enabled), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::ifmgr_0_1_set_parent_ifname( // Input values, const uint32_t& tid, const string& ifname, const string& parent_ifname) { string error_msg; if (_ifconfig.add_transaction_operation( tid, new SetIfString(_ifconfig, ifname, parent_ifname, IF_STRING_PARENT_IFNAME), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::ifmgr_0_1_set_iface_type( // Input values, const uint32_t& tid, const string& ifname, const string& iface_type) { string error_msg; if (_ifconfig.add_transaction_operation( tid, new SetIfString(_ifconfig, ifname, iface_type, IF_STRING_IFTYPE), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::ifmgr_0_1_set_vid( // Input values, const uint32_t& tid, const string& ifname, const string& vid) { string error_msg; if (_ifconfig.add_transaction_operation( tid, new SetIfString(_ifconfig, ifname, vid, IF_STRING_VID), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::ifmgr_0_1_create_address4( // Input values, const uint32_t& tid, const string& ifname, const string& vifname, const IPv4& address) { string error_msg; if (_ifconfig.add_transaction_operation( tid, new AddAddr4(_ifconfig, ifname, vifname, address), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::ifmgr_0_1_delete_address4( // Input values, const uint32_t& tid, const string& ifname, const string& vifname, const IPv4& address) { string error_msg; if (_ifconfig.add_transaction_operation( tid, new RemoveAddr4(_ifconfig, ifname, vifname, address), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::ifmgr_0_1_set_address_enabled4( // Input values, const uint32_t& tid, const string& ifname, const string& vifname, const IPv4& address, const bool& enabled) { string error_msg; if (_ifconfig.add_transaction_operation( tid, new SetAddr4Enabled(_ifconfig, ifname, vifname, address, enabled), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::ifmgr_0_1_set_prefix4( // Input values, const uint32_t& tid, const string& ifname, const string& vifname, const IPv4& address, const uint32_t& prefix_len) { string error_msg; if (_ifconfig.add_transaction_operation( tid, new SetAddr4Prefix(_ifconfig, ifname, vifname, address, prefix_len), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::ifmgr_0_1_set_broadcast4( // Input values, const uint32_t& tid, const string& ifname, const string& vifname, const IPv4& address, const IPv4& broadcast) { string error_msg; if (_ifconfig.add_transaction_operation( tid, new SetAddr4Broadcast(_ifconfig, ifname, vifname, address, broadcast), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::ifmgr_0_1_set_endpoint4( // Input values, const uint32_t& tid, const string& ifname, const string& vifname, const IPv4& address, const IPv4& endpoint) { string error_msg; if (_ifconfig.add_transaction_operation( tid, new SetAddr4Endpoint(_ifconfig, ifname, vifname, address, endpoint), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::ifmgr_0_1_startup_ifmgr() { return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::ifmgr_replicator_0_1_register_ifmgr_mirror( // Input values, const string& clientname) { string error_msg; if (_lib_fea_client_bridge.add_libfeaclient_mirror(clientname) != XORP_OK) { error_msg = c_format("Cannot register ifmgr mirror client %s", clientname.c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::ifmgr_replicator_0_1_unregister_ifmgr_mirror( // Input values, const string& clientname) { string error_msg; if (_lib_fea_client_bridge.remove_libfeaclient_mirror(clientname) != XORP_OK) { error_msg = c_format("Cannot unregister ifmgr mirror client %s", clientname.c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } // ---------------------------------------------------------------------------- // FIB Related XrlCmdError XrlFeaTarget::fti_0_2_lookup_route_by_dest4( // Input values, const IPv4& dst, // Output values, IPv4Net& netmask, IPv4& nexthop, string& ifname, string& vifname, uint32_t& metric, uint32_t& admin_distance, string& protocol_origin) { Fte4 fte; if (_fibconfig.lookup_route_by_dest4(dst, fte) == XORP_OK) { netmask = fte.net(); nexthop = fte.nexthop(); ifname = fte.ifname(); vifname = fte.vifname(); metric = fte.metric(); admin_distance = fte.admin_distance(); // TODO: set the value of protocol_origin to something meaningful protocol_origin = "NOT_SUPPORTED"; return XrlCmdError::OKAY(); } return XrlCmdError::COMMAND_FAILED("No route for " + dst.str()); } XrlCmdError XrlFeaTarget::fti_0_2_lookup_route_by_network4( // Input values, const IPv4Net& dst, // Output values, IPv4& nexthop, string& ifname, string& vifname, uint32_t& metric, uint32_t& admin_distance, string& protocol_origin) { Fte4 fte; if (_fibconfig.lookup_route_by_network4(dst, fte) == XORP_OK) { nexthop = fte.nexthop(); ifname = fte.ifname(); vifname = fte.vifname(); metric = fte.metric(); admin_distance = fte.admin_distance(); // TODO: set the value of protocol_origin to something meaningful protocol_origin = "NOT_SUPPORTED"; return XrlCmdError::OKAY(); } return XrlCmdError::COMMAND_FAILED("No entry for " + dst.str()); } XrlCmdError XrlFeaTarget::fti_0_2_have_ipv4( // Output values, bool& result) { result = _fea_node.have_ipv4(); return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::fti_0_2_get_unicast_forwarding_enabled4( // Output values, bool& enabled) { string error_msg; if (_fibconfig.unicast_forwarding_enabled4(enabled, error_msg) != XORP_OK) return XrlCmdError::COMMAND_FAILED(error_msg); return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::fti_0_2_set_unicast_forwarding_enabled4( // Input values, const bool& enabled) { string error_msg; if (_fibconfig.set_unicast_forwarding_enabled4(enabled, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::fti_0_2_set_unicast_forwarding_entries_retain_on_startup4( // Input values, const bool& retain) { string error_msg; if (_fibconfig.set_unicast_forwarding_entries_retain_on_startup4( retain, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::fti_0_2_set_unicast_forwarding_entries_retain_on_shutdown4( // Input values, const bool& retain) { string error_msg; if (_fibconfig.set_unicast_forwarding_entries_retain_on_shutdown4( retain, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::fti_0_2_set_unicast_forwarding_table_id4( // Input values, const bool& is_configured, const uint32_t& table_id) { string error_msg; if (_fibconfig.set_unicast_forwarding_table_id4( is_configured, table_id, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } // // RIB routes redistribution transaction-based XRL interface // XrlCmdError XrlFeaTarget::redist_transaction4_0_1_start_transaction( // Output values, uint32_t& tid) { string error_msg; if (_fibconfig.start_transaction(tid, error_msg) != XORP_OK) return XrlCmdError::COMMAND_FAILED(error_msg); return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::redist_transaction4_0_1_commit_transaction( // Input values, const uint32_t& tid) { string error_msg; if (_fibconfig.commit_transaction(tid, error_msg) != XORP_OK) return XrlCmdError::COMMAND_FAILED(error_msg); return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::redist_transaction4_0_1_abort_transaction( // Input values, const uint32_t& tid) { string error_msg; if (_fibconfig.abort_transaction(tid, error_msg) != XORP_OK) return XrlCmdError::COMMAND_FAILED(error_msg); return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::redist_transaction4_0_1_add_route( // Input values, const uint32_t& tid, const IPv4Net& dst, const IPv4& nexthop, const string& ifname, const string& vifname, const uint32_t& metric, const uint32_t& admin_distance, const string& cookie, const string& protocol_origin) { bool is_xorp_route; bool is_connected_route = false; string error_msg; debug_msg("redist_transaction4_0_1_add_route(): " "dst = %s nexthop = %s ifname = %s vifname = %s " "metric = %u admin_distance = %u protocol_origin = %s\n", dst.str().c_str(), nexthop.str().c_str(), ifname.c_str(), vifname.c_str(), XORP_UINT_CAST(metric), XORP_UINT_CAST(admin_distance), protocol_origin.c_str()); UNUSED(cookie); is_xorp_route = true; // XXX: unconditionally set to true // TODO: XXX: get rid of the hard-coded "connected" string here if (protocol_origin == "connected") is_connected_route = true; PROFILE(if (_profile.enabled(profile_route_in)) _profile.log(profile_route_in, c_format("add %s", dst.str().c_str()))); if (_fibconfig.add_transaction_operation( tid, new FibAddEntry4(_fibconfig, dst, nexthop, ifname, vifname, metric, admin_distance, is_xorp_route, is_connected_route), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::redist_transaction4_0_1_delete_route( // Input values, const uint32_t& tid, const IPv4Net& dst, const IPv4& nexthop, const string& ifname, const string& vifname, const uint32_t& metric, const uint32_t& admin_distance, const string& cookie, const string& protocol_origin) { bool is_xorp_route; bool is_connected_route = false; string error_msg; debug_msg("redist_transaction4_0_1_delete_route(): " "dst = %s nexthop = %s ifname = %s vifname = %s " "metric = %u admin_distance = %u protocol_origin = %s\n", dst.str().c_str(), nexthop.str().c_str(), ifname.c_str(), vifname.c_str(), XORP_UINT_CAST(metric), XORP_UINT_CAST(admin_distance), protocol_origin.c_str()); UNUSED(cookie); is_xorp_route = true; // XXX: unconditionally set to true // TODO: XXX: get rid of the hard-coded "connected" string here if (protocol_origin == "connected") is_connected_route = true; PROFILE(if (_profile.enabled(profile_route_in)) _profile.log(profile_route_in, c_format("delete %s", dst.str().c_str()))); if (_fibconfig.add_transaction_operation( tid, new FibDeleteEntry4(_fibconfig, dst, nexthop, ifname, vifname, metric, admin_distance, is_xorp_route, is_connected_route), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::redist_transaction4_0_1_delete_all_routes( // Input values, const uint32_t& tid, const string& cookie) { string error_msg; UNUSED(cookie); if (_fibconfig.add_transaction_operation( tid, new FibDeleteAllEntries4(_fibconfig), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } // ---------------------------------------------------------------------------- // Raw Link-Level Server Interface related XrlCmdError XrlFeaTarget::raw_link_0_1_send( // Input values, const string& if_name, const string& vif_name, const Mac& src_address, const Mac& dst_address, const uint32_t& ether_type, const vector& payload) { string error_msg; if (_io_link_manager.send(if_name, vif_name, src_address, dst_address, ether_type, payload, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::raw_link_0_1_register_receiver( // Input values, const string& xrl_target_instance_name, const string& if_name, const string& vif_name, const uint32_t& ether_type, const string& filter_program, const bool& enable_multicast_loopback) { string error_msg; XLOG_INFO("register receiver, target: %s iface: %s:%s ether: %i filter: %s mcast_loopback: %i\n", xrl_target_instance_name.c_str(), if_name.c_str(), vif_name.c_str(), ether_type, filter_program.c_str(), (int)(enable_multicast_loopback)); if (_io_link_manager.register_receiver(xrl_target_instance_name, if_name, vif_name, ether_type, filter_program, enable_multicast_loopback, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::raw_link_0_1_unregister_receiver( // Input values, const string& xrl_target_instance_name, const string& if_name, const string& vif_name, const uint32_t& ether_type, const string& filter_program) { string error_msg; XLOG_INFO("unregister receiver, target: %s iface: %s:%s ether: %i filter: %s\n", xrl_target_instance_name.c_str(), if_name.c_str(), vif_name.c_str(), ether_type, filter_program.c_str()); if (_io_link_manager.unregister_receiver(xrl_target_instance_name, if_name, vif_name, ether_type, filter_program, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::raw_link_0_1_join_multicast_group( // Input values, const string& xrl_target_instance_name, const string& if_name, const string& vif_name, const uint32_t& ether_type, const string& filter_program, const Mac& group_address) { string error_msg; if (_io_link_manager.join_multicast_group(xrl_target_instance_name, if_name, vif_name, ether_type, filter_program, group_address, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::raw_link_0_1_leave_multicast_group( // Input values, const string& xrl_target_instance_name, const string& if_name, const string& vif_name, const uint32_t& ether_type, const string& filter_program, const Mac& group_address) { string error_msg; if (_io_link_manager.leave_multicast_group(xrl_target_instance_name, if_name, vif_name, ether_type, filter_program, group_address, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } // ---------------------------------------------------------------------------- // IPv4 Raw Socket related XrlCmdError XrlFeaTarget::raw_packet4_0_1_send( // Input values, const string& if_name, const string& vif_name, const IPv4& src_address, const IPv4& dst_address, const uint32_t& ip_protocol, const int32_t& ip_ttl, const int32_t& ip_tos, const bool& ip_router_alert, const bool& ip_internet_control, const vector& payload) { string error_msg; vector ext_headers_type_vector; vector > ext_headers_payload_vector; if (_io_ip_manager.send(if_name, vif_name, IPvX(src_address), IPvX(dst_address), ip_protocol, ip_ttl, ip_tos, ip_router_alert, ip_internet_control, ext_headers_type_vector, ext_headers_payload_vector, payload, error_msg) != XORP_OK) { assert(error_msg.size()); // don't allow empty error messages. //XLOG_ERROR("Failed raw_packet4_0_1_send, iface: %s/%s error_msg: %s\n", // if_name.c_str(), vif_name.c_str(), error_msg.c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::raw_packet4_0_1_register_receiver( // Input values, const string& xrl_target_instance_name, const string& if_name, const string& vif_name, const uint32_t& ip_protocol, const bool& enable_multicast_loopback) { string error_msg; if (_io_ip_manager.register_receiver(IPv4::af(), xrl_target_instance_name, if_name, vif_name, ip_protocol, enable_multicast_loopback, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::raw_packet4_0_1_unregister_receiver( // Input values, const string& xrl_target_instance_name, const string& if_name, const string& vif_name, const uint32_t& ip_protocol) { string error_msg; if (_io_ip_manager.unregister_receiver(IPv4::af(), xrl_target_instance_name, if_name, vif_name, ip_protocol, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::raw_packet4_0_1_join_multicast_group( // Input values, const string& xrl_target_instance_name, const string& if_name, const string& vif_name, const uint32_t& ip_protocol, const IPv4& group_address) { string error_msg; if (_io_ip_manager.join_multicast_group(xrl_target_instance_name, if_name, vif_name, ip_protocol, IPvX(group_address), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::raw_packet4_0_1_leave_multicast_group( // Input values, const string& xrl_target_instance_name, const string& if_name, const string& vif_name, const uint32_t& ip_protocol, const IPv4& group_address) { string error_msg; if (_io_ip_manager.leave_multicast_group(xrl_target_instance_name, if_name, vif_name, ip_protocol, IPvX(group_address), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } #ifdef HAVE_IPV6 // ---------------------------------------------------------------------------- // IPv6 Raw Socket related XrlCmdError XrlFeaTarget::raw_packet6_0_1_send( // Input values, const string& if_name, const string& vif_name, const IPv6& src_address, const IPv6& dst_address, const uint32_t& ip_protocol, const int32_t& ip_ttl, const int32_t& ip_tos, const bool& ip_router_alert, const bool& ip_internet_control, const XrlAtomList& ext_headers_type, const XrlAtomList& ext_headers_payload, const vector& payload) { string error_msg; // Decompose the external headers info if (ext_headers_type.size() != ext_headers_payload.size()) { error_msg = c_format("External headers mismatch: %u type(s) " "and %u payload(s)", XORP_UINT_CAST(ext_headers_type.size()), XORP_UINT_CAST(ext_headers_payload.size())); return XrlCmdError::COMMAND_FAILED(error_msg); } size_t i; size_t ext_headers_size = ext_headers_type.size(); vector ext_headers_type_vector(ext_headers_size); vector > ext_headers_payload_vector(ext_headers_size); for (i = 0; i < ext_headers_size; i++) { const XrlAtom& atom_type = ext_headers_type.get(i); const XrlAtom& atom_payload = ext_headers_payload.get(i); if (atom_type.type() != xrlatom_uint32) { error_msg = c_format("Element inside ext_headers_type isn't uint32"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (atom_payload.type() != xrlatom_binary) { error_msg = c_format("Element inside ext_headers_payload isn't binary"); return XrlCmdError::COMMAND_FAILED(error_msg); } ext_headers_type_vector[i] = atom_type.uint32(); ext_headers_payload_vector[i] = atom_payload.binary(); } if (_io_ip_manager.send(if_name, vif_name, IPvX(src_address), IPvX(dst_address), ip_protocol, ip_ttl, ip_tos, ip_router_alert, ip_internet_control, ext_headers_type_vector, ext_headers_payload_vector, payload, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::raw_packet6_0_1_register_receiver( // Input values, const string& xrl_target_instance_name, const string& if_name, const string& vif_name, const uint32_t& ip_protocol, const bool& enable_multicast_loopback) { string error_msg; if ( _io_ip_manager.register_receiver(IPv6::af(), xrl_target_instance_name, if_name, vif_name, ip_protocol, enable_multicast_loopback, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::raw_packet6_0_1_unregister_receiver( // Input values, const string& xrl_target_instance_name, const string& if_name, const string& vif_name, const uint32_t& ip_protocol) { string error_msg; if (_io_ip_manager.unregister_receiver(IPv6::af(), xrl_target_instance_name, if_name, vif_name, ip_protocol, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::raw_packet6_0_1_join_multicast_group( // Input values, const string& xrl_target_instance_name, const string& if_name, const string& vif_name, const uint32_t& ip_protocol, const IPv6& group_address) { string error_msg; if (_io_ip_manager.join_multicast_group(xrl_target_instance_name, if_name, vif_name, ip_protocol, IPvX(group_address), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::raw_packet6_0_1_leave_multicast_group( // Input values, const string& xrl_target_instance_name, const string& if_name, const string& vif_name, const uint32_t& ip_protocol, const IPv6& group_address) { string error_msg; if (_io_ip_manager.leave_multicast_group(xrl_target_instance_name, if_name, vif_name, ip_protocol, IPvX(group_address), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } #endif // ---------------------------------------------------------------------------- // TCP/UDP I/O Socket Server Interface XrlCmdError XrlFeaTarget::socket4_0_1_tcp_open( // Input values, const string& creator, // Output values, string& sockid) { string error_msg; if (_io_tcpudp_manager.tcp_open(IPv4::af(), creator, sockid, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::socket4_0_1_udp_open( // Input values, const string& creator, // Output values, string& sockid) { string error_msg; if (_io_tcpudp_manager.udp_open(IPv4::af(), creator, sockid, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::socket4_0_1_tcp_open_and_bind( // Input values, const string& creator, const IPv4& local_addr, const uint32_t& local_port, // Output values, string& sockid) { string error_msg; if (local_port > 0xffff) { error_msg = c_format("Local port %u is out of range", local_port); return XrlCmdError::COMMAND_FAILED(error_msg); } if (_io_tcpudp_manager.tcp_open_and_bind(IPv4::af(), creator, IPvX(local_addr), local_port, sockid, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::socket4_0_1_udp_open_and_bind( // Input values, const string& creator, const IPv4& local_addr, const uint32_t& local_port, const string& local_dev, const uint32_t& reuse, // Output values, string& sockid) { string error_msg; if (local_port > 0xffff) { error_msg = c_format("Local port %u is out of range", local_port); return XrlCmdError::COMMAND_FAILED(error_msg); } if (_io_tcpudp_manager.udp_open_and_bind(IPv4::af(), creator, IPvX(local_addr), local_port, local_dev, reuse, sockid, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::socket4_0_1_udp_open_bind_join( // Input values, const string& creator, const IPv4& local_addr, const uint32_t& local_port, const IPv4& mcast_addr, const uint32_t& ttl, const bool& reuse, // Output values, string& sockid) { string error_msg; if (local_port > 0xffff) { error_msg = c_format("Local port %u is out of range", local_port); return XrlCmdError::COMMAND_FAILED(error_msg); } if (ttl > 0xff) { error_msg = c_format("TTL %u is out of range", ttl); return XrlCmdError::COMMAND_FAILED(error_msg); } if (_io_tcpudp_manager.udp_open_bind_join(IPv4::af(), creator, IPvX(local_addr), local_port, IPvX(mcast_addr), ttl, reuse, sockid, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::socket4_0_1_tcp_open_bind_connect( // Input values, const string& creator, const IPv4& local_addr, const uint32_t& local_port, const IPv4& remote_addr, const uint32_t& remote_port, // Output values, string& sockid) { string error_msg; if (local_port > 0xffff) { error_msg = c_format("Local port %u is out of range", local_port); return XrlCmdError::COMMAND_FAILED(error_msg); } if (remote_port > 0xffff) { error_msg = c_format("Remote port %u is out of range", remote_port); return XrlCmdError::COMMAND_FAILED(error_msg); } if (_io_tcpudp_manager.tcp_open_bind_connect(IPv4::af(), creator, IPvX(local_addr), local_port, IPvX(remote_addr), remote_port, sockid, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::socket4_0_1_udp_open_bind_connect( // Input values, const string& creator, const IPv4& local_addr, const uint32_t& local_port, const IPv4& remote_addr, const uint32_t& remote_port, // Output values, string& sockid) { string error_msg; if (local_port > 0xffff) { error_msg = c_format("Local port %u is out of range", local_port); return XrlCmdError::COMMAND_FAILED(error_msg); } if (remote_port > 0xffff) { error_msg = c_format("Remote port %u is out of range", remote_port); return XrlCmdError::COMMAND_FAILED(error_msg); } if (_io_tcpudp_manager.udp_open_bind_connect(IPv4::af(), creator, IPvX(local_addr), local_port, IPvX(remote_addr), remote_port, sockid, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::socket4_0_1_udp_open_bind_broadcast( // Input values, const string& creator, const string& ifname, const string& vifname, const uint32_t& local_port, const uint32_t& remote_port, const bool& reuse, const bool& limited, const bool& connected, // Output values, string& sockid) { string error_msg; if (local_port > 0xffff) { error_msg = c_format("Local port %u is out of range", local_port); return XrlCmdError::COMMAND_FAILED(error_msg); } if (remote_port > 0xffff) { error_msg = c_format("Remote port %u is out of range", remote_port); return XrlCmdError::COMMAND_FAILED(error_msg); } if (_io_tcpudp_manager.udp_open_bind_broadcast(IPv4::af(), creator, ifname, vifname, local_port, remote_port, reuse, limited, connected, sockid, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::socket4_0_1_bind( // Input values, const string& sockid, const IPv4& local_addr, const uint32_t& local_port) { string error_msg; if (local_port > 0xffff) { error_msg = c_format("Local port %u is out of range", local_port); return XrlCmdError::COMMAND_FAILED(error_msg); } if (_io_tcpudp_manager.bind(IPv4::af(), sockid, IPvX(local_addr), local_port, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::socket4_0_1_udp_join_group( // Input values, const string& sockid, const IPv4& mcast_addr, const IPv4& join_if_addr) { string error_msg; if (_io_tcpudp_manager.udp_join_group(IPv4::af(), sockid, IPvX(mcast_addr), IPvX(join_if_addr), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::socket4_0_1_udp_leave_group( // Input values, const string& sockid, const IPv4& mcast_addr, const IPv4& leave_if_addr) { string error_msg; if (_io_tcpudp_manager.udp_leave_group(IPv4::af(), sockid, IPvX(mcast_addr), IPvX(leave_if_addr), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::socket4_0_1_close( // Input values, const string& sockid) { string error_msg; if (_io_tcpudp_manager.close(IPv4::af(), sockid, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::socket4_0_1_tcp_listen( // Input values, const string& sockid, const uint32_t& backlog) { string error_msg; if (_io_tcpudp_manager.tcp_listen(IPv4::af(), sockid, backlog, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::socket4_0_1_udp_enable_recv( // Input values, const string& sockid) { string error_msg; if (_io_tcpudp_manager.udp_enable_recv(IPv4::af(), sockid, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::socket4_0_1_send( // Input values, const string& sockid, const vector& data) { string error_msg; if (_io_tcpudp_manager.send(IPv4::af(), sockid, data, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::socket4_0_1_send_to( // Input values, const string& sockid, const IPv4& remote_addr, const uint32_t& remote_port, const vector& data) { string error_msg; if (remote_port > 0xffff) { error_msg = c_format("Remote port %u is out of range", remote_port); return XrlCmdError::COMMAND_FAILED(error_msg); } if (_io_tcpudp_manager.send_to(IPv4::af(), sockid, IPvX(remote_addr), remote_port, data, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::socket4_0_1_send_from_multicast_if( // Input values, const string& sockid, const IPv4& group_addr, const uint32_t& group_port, const IPv4& ifaddr, const vector& data) { string error_msg; if (group_port > 0xffff) { error_msg = c_format("Multicast group port %u is out of range", group_port); return XrlCmdError::COMMAND_FAILED(error_msg); } if (_io_tcpudp_manager.send_from_multicast_if(IPv4::af(), sockid, IPvX(group_addr), group_port, ifaddr, data, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::socket4_0_1_set_socket_option( // Input values, const string& sockid, const string& optname, const uint32_t& optval) { string error_msg; if (_io_tcpudp_manager.set_socket_option(IPv4::af(), sockid, optname, optval, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::socket4_0_1_set_socket_option_txt( // Input values, const string& sockid, const string& optname, const string& optval) { string error_msg; if (_io_tcpudp_manager.set_socket_option(IPv4::af(), sockid, optname, optval, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } #ifdef HAVE_IPV6 XrlCmdError XrlFeaTarget::socket6_0_1_tcp_open( // Input values, const string& creator, // Output values, string& sockid) { string error_msg; if (_io_tcpudp_manager.tcp_open(IPv6::af(), creator, sockid, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::socket6_0_1_udp_open( // Input values, const string& creator, // Output values, string& sockid) { string error_msg; if (_io_tcpudp_manager.udp_open(IPv6::af(), creator, sockid, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::socket6_0_1_tcp_open_and_bind( // Input values, const string& creator, const IPv6& local_addr, const uint32_t& local_port, // Output values, string& sockid) { string error_msg; if (local_port > 0xffff) { error_msg = c_format("Local port %u is out of range", local_port); return XrlCmdError::COMMAND_FAILED(error_msg); } if (_io_tcpudp_manager.tcp_open_and_bind(IPv6::af(), creator, IPvX(local_addr), local_port, sockid, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::socket6_0_1_udp_open_and_bind( // Input values, const string& creator, const IPv6& local_addr, const uint32_t& local_port, const string& local_dev, const uint32_t& reuse, // Output values, string& sockid) { string error_msg; if (local_port > 0xffff) { error_msg = c_format("Local port %u is out of range", local_port); return XrlCmdError::COMMAND_FAILED(error_msg); } if (_io_tcpudp_manager.udp_open_and_bind(IPv6::af(), creator, IPvX(local_addr), local_port, local_dev, reuse, sockid, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::socket6_0_1_udp_open_bind_join( // Input values, const string& creator, const IPv6& local_addr, const uint32_t& local_port, const IPv6& mcast_addr, const uint32_t& ttl, const bool& reuse, // Output values, string& sockid) { string error_msg; if (local_port > 0xffff) { error_msg = c_format("Local port %u is out of range", local_port); return XrlCmdError::COMMAND_FAILED(error_msg); } if (ttl > 0xff) { error_msg = c_format("TTL %u is out of range", ttl); return XrlCmdError::COMMAND_FAILED(error_msg); } if (_io_tcpudp_manager.udp_open_bind_join(IPv6::af(), creator, IPvX(local_addr), local_port, IPvX(mcast_addr), ttl, reuse, sockid, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::socket6_0_1_tcp_open_bind_connect( // Input values, const string& creator, const IPv6& local_addr, const uint32_t& local_port, const IPv6& remote_addr, const uint32_t& remote_port, // Output values, string& sockid) { string error_msg; if (local_port > 0xffff) { error_msg = c_format("Local port %u is out of range", local_port); return XrlCmdError::COMMAND_FAILED(error_msg); } if (remote_port > 0xffff) { error_msg = c_format("Remote port %u is out of range", remote_port); return XrlCmdError::COMMAND_FAILED(error_msg); } if (_io_tcpudp_manager.tcp_open_bind_connect(IPv6::af(), creator, IPvX(local_addr), local_port, IPvX(remote_addr), remote_port, sockid, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::socket6_0_1_udp_open_bind_connect( // Input values, const string& creator, const IPv6& local_addr, const uint32_t& local_port, const IPv6& remote_addr, const uint32_t& remote_port, // Output values, string& sockid) { string error_msg; if (local_port > 0xffff) { error_msg = c_format("Local port %u is out of range", local_port); return XrlCmdError::COMMAND_FAILED(error_msg); } if (remote_port > 0xffff) { error_msg = c_format("Remote port %u is out of range", remote_port); return XrlCmdError::COMMAND_FAILED(error_msg); } if (_io_tcpudp_manager.udp_open_bind_connect(IPv6::af(), creator, IPvX(local_addr), local_port, IPvX(remote_addr), remote_port, sockid, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::socket6_0_1_bind( // Input values, const string& sockid, const IPv6& local_addr, const uint32_t& local_port) { string error_msg; if (local_port > 0xffff) { error_msg = c_format("Local port %u is out of range", local_port); return XrlCmdError::COMMAND_FAILED(error_msg); } if (_io_tcpudp_manager.bind(IPv6::af(), sockid, IPvX(local_addr), local_port, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::socket6_0_1_udp_join_group( // Input values, const string& sockid, const IPv6& mcast_addr, const IPv6& join_if_addr) { string error_msg; if (_io_tcpudp_manager.udp_join_group(IPv6::af(), sockid, IPvX(mcast_addr), IPvX(join_if_addr), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::socket6_0_1_udp_leave_group( // Input values, const string& sockid, const IPv6& mcast_addr, const IPv6& leave_if_addr) { string error_msg; if (_io_tcpudp_manager.udp_leave_group(IPv6::af(), sockid, IPvX(mcast_addr), IPvX(leave_if_addr), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::socket6_0_1_close( // Input values, const string& sockid) { string error_msg; if (_io_tcpudp_manager.close(IPv6::af(), sockid, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::socket6_0_1_tcp_listen( // Input values, const string& sockid, const uint32_t& backlog) { string error_msg; if (_io_tcpudp_manager.tcp_listen(IPv6::af(), sockid, backlog, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::socket6_0_1_send( // Input values, const string& sockid, const vector& data) { string error_msg; if (_io_tcpudp_manager.send(IPv6::af(), sockid, data, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::socket6_0_1_send_to( // Input values, const string& sockid, const IPv6& remote_addr, const uint32_t& remote_port, const vector& data) { string error_msg; if (remote_port > 0xffff) { error_msg = c_format("Remote port %u is out of range", remote_port); return XrlCmdError::COMMAND_FAILED(error_msg); } if (_io_tcpudp_manager.send_to(IPv6::af(), sockid, IPvX(remote_addr), remote_port, data, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::socket6_0_1_send_from_multicast_if( // Input values, const string& sockid, const IPv6& group_addr, const uint32_t& group_port, const IPv6& ifaddr, const vector& data) { string error_msg; if (group_port > 0xffff) { error_msg = c_format("Multicast group port %u is out of range", group_port); return XrlCmdError::COMMAND_FAILED(error_msg); } if (_io_tcpudp_manager.send_from_multicast_if(IPv6::af(), sockid, IPvX(group_addr), group_port, ifaddr, data, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::socket6_0_1_set_socket_option( // Input values, const string& sockid, const string& optname, const uint32_t& optval) { string error_msg; if (_io_tcpudp_manager.set_socket_option(IPv6::af(), sockid, optname, optval, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } #endif #ifndef XORP_DISABLE_PROFILE // ---------------------------------------------------------------------------- // Profiling related XrlCmdError XrlFeaTarget::profile_0_1_enable(const string& pname) { debug_msg("enable profile variable %s\n", pname.c_str()); try { _profile.enable(pname); } catch(PVariableUnknown& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } catch(PVariableLocked& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::profile_0_1_disable(const string& pname) { debug_msg("disable profile variable %s\n", pname.c_str()); try { _profile.disable(pname); } catch(PVariableUnknown& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::profile_0_1_get_entries(const string& pname, const string& instance_name) { debug_msg("get profile variable %s instance %s\n", pname.c_str(), instance_name.c_str()); // Lock and initialize. try { _profile.lock_log(pname); } catch(PVariableUnknown& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } catch(PVariableLocked& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } ProfileUtils::transmit_log(pname, dynamic_cast(&_xrl_router), instance_name, &_profile); return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::profile_0_1_clear(const string& pname) { debug_msg("clear profile variable %s\n", pname.c_str()); try { _profile.clear(pname); } catch(PVariableUnknown& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } catch(PVariableLocked& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlFeaTarget::profile_0_1_list(string& info) { debug_msg("\n"); info = _profile.get_list(); return XrlCmdError::OKAY(); } #endif //profile xorp/fea/0001-fea-Auto-create-mcast-socket-if-none-exist.patch0000644000076400007640000000262211642440065023774 0ustar greearbgreearbFrom e24a6687af8d5d583b028a2e12ce5fdfdef9be92 Mon Sep 17 00:00:00 2001 From: Ben Greear Date: Tue, 4 Oct 2011 00:06:08 +0200 Subject: [PATCH 1/2] fea: Auto-create mcast socket if none exist. Linux uses one socket per interface, but BSD cannot do that. This code path has probably just never been tested since I added the linux one-socket logic years ago. I see no reason to not auto-create the socket as needed on BSD. Signed-off-by: Ben Greear --- xorp/fea/data_plane/io/io_ip_socket.cc | 12 +++++++++--- 1 files changed, 9 insertions(+), 3 deletions(-) diff --git a/xorp/fea/data_plane/io/io_ip_socket.cc b/xorp/fea/data_plane/io/io_ip_socket.cc index 113ea17..56e6bd2 100644 --- a/xorp/fea/data_plane/io/io_ip_socket.cc +++ b/xorp/fea/data_plane/io/io_ip_socket.cc @@ -664,9 +664,15 @@ XorpFd* IoIpSocket::mcast_protocol_fd_in() { } return &_mcast_proto_socket_in; #else - string nll; - // Just grabs first and only socket when USE_SOCKET_PER_IFACE is not defined. - return findExistingInputSocket(nll, nll); + string nll("na"); + string err_msg; + XorpFd* rv; + // Just grabs (or creates) first and only socket when USE_SOCKET_PER_IFACE is not defined. + rv = findOrCreateInputSocket(nll, nll, err_msg); + if (err_msg.size()) { + XLOG_WARNING("%s", err_msg.c_str()); + } + return rv; #endif } -- 1.7.6.4 xorp/fea/ifconfig.cc0000664000076400007640000006116511540225525014520 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #ifdef HAVE_NET_IF_H #include #endif #include "ifconfig.hh" #include "ifconfig_transaction.hh" #include "fea_node.hh" // // Network interfaces related configuration. // static bool map_changes(const IfTreeItem::State& fci, IfConfigUpdateReporterBase::Update& u) { switch (fci) { case IfTreeItem::NO_CHANGE: return false; case IfTreeItem::CREATED: u = IfConfigUpdateReporterBase::CREATED; break; case IfTreeItem::DELETED: u = IfConfigUpdateReporterBase::DELETED; break; case IfTreeItem::CHANGED: u = IfConfigUpdateReporterBase::CHANGED; break; default: XLOG_FATAL("Unknown IfTreeItem::State"); break; } return true; } IfConfig::IfConfig(FeaNode& fea_node) : _fea_node(fea_node), _eventloop(fea_node.eventloop()), _nexthop_port_mapper(fea_node.nexthop_port_mapper()), _itm(NULL), //_live_config("live-config"), _user_config("user-config"), _system_config("system-config"), _merged_config("pushed-config"), _original_config("original-config"), _restore_original_config_on_shutdown(false), _ifconfig_update_replicator(merged_config()), _is_running(false) { _itm = new IfConfigTransactionManager(_eventloop); } IfConfig::~IfConfig() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the mechanism for manipulating " "the network interfaces: %s", error_msg.c_str()); } if (_itm != NULL) { delete _itm; _itm = NULL; } } ProcessStatus IfConfig::status(string& reason) const { if (_itm->pending() > 0) { reason = "There are transactions pending"; return (PROC_NOT_READY); } return (PROC_READY); } int IfConfig::start_transaction(uint32_t& tid, string& error_msg) { if (_itm->start(tid) != true) { error_msg = c_format("Resource limit on number of pending transactions hit"); return (XORP_ERROR); } return (XORP_OK); } int IfConfig::abort_transaction(uint32_t tid, string& error_msg) { if (_itm->abort(tid) != true) { error_msg = c_format("Expired or invalid transaction ID presented"); return (XORP_ERROR); } return (XORP_OK); } int IfConfig::add_transaction_operation(uint32_t tid, const TransactionManager::Operation& op, string& error_msg) { uint32_t n_ops = 0; if (_itm->retrieve_size(tid, n_ops) != true) { error_msg = c_format("Expired or invalid transaction ID presented"); return (XORP_ERROR); } // XXX: If necessary, check whether n_ops is above a pre-defined limit. // // In theory, resource shortage is the only thing that could get us here // if (_itm->add(tid, op) != true) { error_msg = c_format("Unknown resource shortage"); return (XORP_ERROR); } return (XORP_OK); } int IfConfig::add_interface(const char* ifname) { IfTreeInterface* ifpl = user_config().find_interface(ifname); //XLOG_WARNING("Add interface: %s current local_cfg: %p\n", ifname, ifpl); if (!ifpl) { // Add to our local config user_config().add_interface(ifname); // Read in the OS's information for this interface. pull_config(ifname, -1); IfTreeInterface* ifp = system_config().find_interface(ifname); if (ifp) { //XLOG_WARNING("Added new interface: %s, found it in pulled config.\n", ifname); user_config().update_interface(*ifp); } else { //XLOG_WARNING("Added new interface: %s, but not found in pulled config.\n", ifname); } } // Add this to our original config if it's not there already. if (!_original_config.find_interface(ifname)) { IfTreeInterface* ifp = _system_config.find_interface(ifname); if (ifp) { _original_config.update_interface(*ifp); } } return XORP_OK; } int IfConfig::remove_interface(const char* ifname) { //XLOG_WARNING("Remove interface: %s\n", ifname); user_config().remove_interface(ifname); system_config().remove_interface(ifname); return XORP_OK; } int IfConfig::update_interface(const IfTreeInterface& iface) { return _user_config.update_interface(iface); } int IfConfig::commit_transaction(uint32_t tid, string& error_msg) { IfTree old_user_config = user_config(); // Copy to restore config IfTree old_merged_config = merged_config(); // Copy to compare changes // // XXX: Pull in advance the current system config, in case it is needed // by some of the transaction operations. // IfTree old_system_config = pull_config(NULL, -1); if (_itm->commit(tid) != true) { error_msg += c_format("Expired or invalid transaction ID presented\n"); return (XORP_ERROR); } if (_itm->error().empty() != true) { error_msg += "IfConfig::commit_transaction: _itm had non-empty error:\n"; error_msg += _itm->error(); return (XORP_ERROR); } // // If we get here we have successfully updated the local copy of the config // // // Prune deleted state that was never added earlier // user_config().prune_bogus_deleted_state(old_user_config); // // Push the configuration // merged_config().align_with_user_config(user_config()); if (push_config(merged_config()) != XORP_OK) { string error_msg2; error_msg += " push_config failed: "; error_msg += push_error(); error_msg += "\n"; // Reverse-back to the previously working configuration if (restore_config(old_user_config, old_system_config, error_msg2) != XORP_OK) { // Failed to reverse back error_msg += c_format("%s [Also, failed to reverse-back to the " "previous config: %s]\n", error_msg.c_str(), error_msg2.c_str()); } return (XORP_ERROR); } // // Pull the new device configuration // pull_config(NULL, -1); debug_msg("SYSTEM CONFIG %s\n", system_config().str().c_str()); debug_msg("USER CONFIG %s\n", user_config().str().c_str()); debug_msg("MERGED CONFIG %s\n", merged_config().str().c_str()); // // Align with system configuration, so that any stuff that failed // in push is not held over in merged config. // merged_config().align_with_pulled_changes(system_config(), user_config()); debug_msg("MERGED CONFIG AFTER ALIGN %s\n", merged_config().str().c_str()); // // Propagate the configuration changes to all listeners // report_updates(merged_config()); // // Flush-out config state // user_config().finalize_state(); merged_config().finalize_state(); return (XORP_OK); } int IfConfig::restore_config(const IfTree& old_user_config, const IfTree& old_system_config, string& error_msg) { IfTree iftree = old_system_config; // Restore the config set_user_config(old_user_config); set_merged_config(old_user_config); pull_config(NULL, -1); // Get the current system config iftree.prepare_replacement_state(system_config()); // Push the config if (push_config(iftree) != XORP_OK) { error_msg = push_error(); return (XORP_ERROR); } // Align the state pull_config(NULL, -1); merged_config().align_with_pulled_changes(system_config(), user_config()); user_config().finalize_state(); merged_config().finalize_state(); return (XORP_OK); } int IfConfig::register_ifconfig_property(IfConfigProperty* ifconfig_property, bool is_exclusive) { if (is_exclusive) _ifconfig_property_plugins.clear(); if ((ifconfig_property != NULL) && (find(_ifconfig_property_plugins.begin(), _ifconfig_property_plugins.end(), ifconfig_property) == _ifconfig_property_plugins.end())) { _ifconfig_property_plugins.push_back(ifconfig_property); } return (XORP_OK); } int IfConfig::unregister_ifconfig_property(IfConfigProperty* ifconfig_property) { if (ifconfig_property == NULL) return (XORP_ERROR); list::iterator iter; iter = find(_ifconfig_property_plugins.begin(), _ifconfig_property_plugins.end(), ifconfig_property); if (iter == _ifconfig_property_plugins.end()) return (XORP_ERROR); _ifconfig_property_plugins.erase(iter); return (XORP_OK); } int IfConfig::register_ifconfig_get(IfConfigGet* ifconfig_get, bool is_exclusive) { if (is_exclusive) _ifconfig_gets.clear(); if ((ifconfig_get != NULL) && (find(_ifconfig_gets.begin(), _ifconfig_gets.end(), ifconfig_get) == _ifconfig_gets.end())) { _ifconfig_gets.push_back(ifconfig_get); } return (XORP_OK); } int IfConfig::unregister_ifconfig_get(IfConfigGet* ifconfig_get) { if (ifconfig_get == NULL) return (XORP_ERROR); list::iterator iter; iter = find(_ifconfig_gets.begin(), _ifconfig_gets.end(), ifconfig_get); if (iter == _ifconfig_gets.end()) return (XORP_ERROR); _ifconfig_gets.erase(iter); return (XORP_OK); } int IfConfig::register_ifconfig_set(IfConfigSet* ifconfig_set, bool is_exclusive) { if (is_exclusive) _ifconfig_sets.clear(); if ((ifconfig_set != NULL) && (find(_ifconfig_sets.begin(), _ifconfig_sets.end(), ifconfig_set) == _ifconfig_sets.end())) { _ifconfig_sets.push_back(ifconfig_set); // // XXX: Push the current config into the new method // if (ifconfig_set->is_running()) ifconfig_set->push_config(merged_config()); } return (XORP_OK); } int IfConfig::unregister_ifconfig_set(IfConfigSet* ifconfig_set) { if (ifconfig_set == NULL) return (XORP_ERROR); list::iterator iter; iter = find(_ifconfig_sets.begin(), _ifconfig_sets.end(), ifconfig_set); if (iter == _ifconfig_sets.end()) return (XORP_ERROR); _ifconfig_sets.erase(iter); return (XORP_OK); } int IfConfig::register_ifconfig_observer(IfConfigObserver* ifconfig_observer, bool is_exclusive) { if (is_exclusive) _ifconfig_observers.clear(); if ((ifconfig_observer != NULL) && (find(_ifconfig_observers.begin(), _ifconfig_observers.end(), ifconfig_observer) == _ifconfig_observers.end())) { _ifconfig_observers.push_back(ifconfig_observer); } return (XORP_OK); } int IfConfig::unregister_ifconfig_observer(IfConfigObserver* ifconfig_observer) { if (ifconfig_observer == NULL) return (XORP_ERROR); list::iterator iter; iter = find(_ifconfig_observers.begin(), _ifconfig_observers.end(), ifconfig_observer); if (iter == _ifconfig_observers.end()) return (XORP_ERROR); _ifconfig_observers.erase(iter); return (XORP_OK); } int IfConfig::register_ifconfig_vlan_get(IfConfigVlanGet* ifconfig_vlan_get, bool is_exclusive) { if (is_exclusive) _ifconfig_vlan_gets.clear(); if ((ifconfig_vlan_get != NULL) && (find(_ifconfig_vlan_gets.begin(), _ifconfig_vlan_gets.end(), ifconfig_vlan_get) == _ifconfig_vlan_gets.end())) { _ifconfig_vlan_gets.push_back(ifconfig_vlan_get); } return (XORP_OK); } int IfConfig::unregister_ifconfig_vlan_get(IfConfigVlanGet* ifconfig_vlan_get) { if (ifconfig_vlan_get == NULL) return (XORP_ERROR); list::iterator iter; iter = find(_ifconfig_vlan_gets.begin(), _ifconfig_vlan_gets.end(), ifconfig_vlan_get); if (iter == _ifconfig_vlan_gets.end()) return (XORP_ERROR); _ifconfig_vlan_gets.erase(iter); return (XORP_OK); } int IfConfig::register_ifconfig_vlan_set(IfConfigVlanSet* ifconfig_vlan_set, bool is_exclusive) { if (is_exclusive) _ifconfig_vlan_sets.clear(); if ((ifconfig_vlan_set != NULL) && (find(_ifconfig_vlan_sets.begin(), _ifconfig_vlan_sets.end(), ifconfig_vlan_set) == _ifconfig_vlan_sets.end())) { _ifconfig_vlan_sets.push_back(ifconfig_vlan_set); // // XXX: Push the current config into the new method // // Note that we use the corresponding IfConfigSet plugin, // because it is the entry poing for pushing interface configuration, // while the IfConfigVlanSet plugin is supporting the IfConfigSet // plugin. // if (ifconfig_vlan_set->is_running()) { FeaDataPlaneManager& m = ifconfig_vlan_set->fea_data_plane_manager(); IfConfigSet* ifconfig_set = m.ifconfig_set(); if (ifconfig_set->is_running()) ifconfig_set->push_config(merged_config()); } } return (XORP_OK); } int IfConfig::unregister_ifconfig_vlan_set(IfConfigVlanSet* ifconfig_vlan_set) { if (ifconfig_vlan_set == NULL) return (XORP_ERROR); list::iterator iter; iter = find(_ifconfig_vlan_sets.begin(), _ifconfig_vlan_sets.end(), ifconfig_vlan_set); if (iter == _ifconfig_vlan_sets.end()) return (XORP_ERROR); _ifconfig_vlan_sets.erase(iter); return (XORP_OK); } int IfConfig::start(string& error_msg) { list::iterator ifconfig_property_iter; list::iterator ifconfig_get_iter; list::iterator ifconfig_set_iter; list::iterator ifconfig_observer_iter; list::iterator ifconfig_vlan_get_iter; list::iterator ifconfig_vlan_set_iter; if (_is_running) return (XORP_OK); // // Check whether all mechanisms are available // if (_ifconfig_property_plugins.empty()) { error_msg = c_format("No mechanism to test the data plane properties"); return (XORP_ERROR); } if (_ifconfig_gets.empty()) { error_msg = c_format("No mechanism to get the interface information"); return (XORP_ERROR); } if (_ifconfig_sets.empty()) { error_msg = c_format("No mechanism to set the interface information"); return (XORP_ERROR); } if (_ifconfig_observers.empty()) { error_msg = c_format("No mechanism to observe the interface information"); return (XORP_ERROR); } // // XXX: Don't check the IfConfigVlanGet and IfConfigVlanSet mechanisms, // because they are optional. // // // Start the IfConfigProperty methods // for (ifconfig_property_iter = _ifconfig_property_plugins.begin(); ifconfig_property_iter != _ifconfig_property_plugins.end(); ++ifconfig_property_iter) { IfConfigProperty* ifconfig_property = *ifconfig_property_iter; if (ifconfig_property->start(error_msg) != XORP_OK) return (XORP_ERROR); } // // Start the IfConfigGet methods // for (ifconfig_get_iter = _ifconfig_gets.begin(); ifconfig_get_iter != _ifconfig_gets.end(); ++ifconfig_get_iter) { IfConfigGet* ifconfig_get = *ifconfig_get_iter; if (ifconfig_get->start(error_msg) != XORP_OK) return (XORP_ERROR); } // // Start the IfConfigSet methods // for (ifconfig_set_iter = _ifconfig_sets.begin(); ifconfig_set_iter != _ifconfig_sets.end(); ++ifconfig_set_iter) { IfConfigSet* ifconfig_set = *ifconfig_set_iter; if (ifconfig_set->start(error_msg) != XORP_OK) return (XORP_ERROR); } // // Start the IfConfigObserver methods // for (ifconfig_observer_iter = _ifconfig_observers.begin(); ifconfig_observer_iter != _ifconfig_observers.end(); ++ifconfig_observer_iter) { IfConfigObserver* ifconfig_observer = *ifconfig_observer_iter; if (ifconfig_observer->start(error_msg) != XORP_OK) return (XORP_ERROR); } // // Start the IfConfigVlanGet methods // for (ifconfig_vlan_get_iter = _ifconfig_vlan_gets.begin(); ifconfig_vlan_get_iter != _ifconfig_vlan_gets.end(); ++ifconfig_vlan_get_iter) { IfConfigVlanGet* ifconfig_vlan_get = *ifconfig_vlan_get_iter; if (ifconfig_vlan_get->start(error_msg) != XORP_OK) return (XORP_ERROR); } // // Start the IfConfigSetSet methods // for (ifconfig_vlan_set_iter = _ifconfig_vlan_sets.begin(); ifconfig_vlan_set_iter != _ifconfig_vlan_sets.end(); ++ifconfig_vlan_set_iter) { IfConfigVlanSet* ifconfig_vlan_set = *ifconfig_vlan_set_iter; if (ifconfig_vlan_set->start(error_msg) != XORP_OK) return (XORP_ERROR); } pull_config(NULL, -1); _system_config.finalize_state(); _original_config = _system_config; _original_config.finalize_state(); debug_msg("Start configuration read: %s\n", _system_config.str().c_str()); debug_msg("\nEnd configuration read.\n"); _is_running = true; return (XORP_OK); } const IfTree& IfConfig::full_pulled_config() { // XXX: We pull the configuration by using only the first method. // In the future we need to rething this and be more flexible. // if (! _ifconfig_gets.empty()) { IfConfigGet* ifconfig_get = _ifconfig_gets.front(); ifconfig_get->pull_config(NULL, _system_config); } return _system_config; } int IfConfig::stop(string& error_msg) { list::iterator ifconfig_property_iter; list::iterator ifconfig_get_iter; list::iterator ifconfig_set_iter; list::iterator ifconfig_observer_iter; list::iterator ifconfig_vlan_get_iter; list::iterator ifconfig_vlan_set_iter; int ret_value = XORP_OK; string error_msg2; if (! _is_running) return (XORP_OK); error_msg.erase(); // // Restore the original config // if (restore_original_config_on_shutdown()) { IfTree tmp_push_tree = _original_config; if (restore_config(tmp_push_tree, tmp_push_tree, error_msg2) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } } // // Stop the IfConfigVlanSet methods // for (ifconfig_vlan_set_iter = _ifconfig_vlan_sets.begin(); ifconfig_vlan_set_iter != _ifconfig_vlan_sets.end(); ++ifconfig_vlan_set_iter) { IfConfigVlanSet* ifconfig_vlan_set = *ifconfig_vlan_set_iter; if (ifconfig_vlan_set->stop(error_msg2) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } } // // Stop the IfConfigVlanGet methods // for (ifconfig_vlan_get_iter = _ifconfig_vlan_gets.begin(); ifconfig_vlan_get_iter != _ifconfig_vlan_gets.end(); ++ifconfig_vlan_get_iter) { IfConfigVlanGet* ifconfig_vlan_get = *ifconfig_vlan_get_iter; if (ifconfig_vlan_get->stop(error_msg2) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } } // // Stop the IfConfigObserver methods // for (ifconfig_observer_iter = _ifconfig_observers.begin(); ifconfig_observer_iter != _ifconfig_observers.end(); ++ifconfig_observer_iter) { IfConfigObserver* ifconfig_observer = *ifconfig_observer_iter; if (ifconfig_observer->stop(error_msg2) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } } // // Stop the IfConfigSet methods // for (ifconfig_set_iter = _ifconfig_sets.begin(); ifconfig_set_iter != _ifconfig_sets.end(); ++ifconfig_set_iter) { IfConfigSet* ifconfig_set = *ifconfig_set_iter; if (ifconfig_set->stop(error_msg2) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } } // // Stop the IfConfigGet methods // for (ifconfig_get_iter = _ifconfig_gets.begin(); ifconfig_get_iter != _ifconfig_gets.end(); ++ifconfig_get_iter) { IfConfigGet* ifconfig_get = *ifconfig_get_iter; if (ifconfig_get->stop(error_msg2) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } } // // Stop the IfConfigProperty methods // for (ifconfig_property_iter = _ifconfig_property_plugins.begin(); ifconfig_property_iter != _ifconfig_property_plugins.end(); ++ifconfig_property_iter) { IfConfigProperty* ifconfig_property = *ifconfig_property_iter; if (ifconfig_property->stop(error_msg2) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } } _is_running = false; return (ret_value); } int IfConfig::push_config(const IfTree& iftree) { list::const_iterator ifconfig_set_iter; if (_ifconfig_sets.empty()) return (XORP_ERROR); // XXX: No plugins for (ifconfig_set_iter = _ifconfig_sets.begin(); ifconfig_set_iter != _ifconfig_sets.end(); ++ifconfig_set_iter) { IfConfigSet* ifconfig_set = *ifconfig_set_iter; if (ifconfig_set->push_config(iftree) != XORP_OK) return (XORP_ERROR); } return (XORP_OK); } // TODO: This can be costly when you have lots of interfaces, so try to // optimize things so this is called rarely. Currently it is called // several times on startup of fea. const IfTree& IfConfig::pull_config(const char* ifname, int if_index) { // // XXX: We pull the configuration by using only the first method. // In the future we need to rething this and be more flexible. // if (! _ifconfig_gets.empty()) { IfConfigGet* ifconfig_get = _ifconfig_gets.front(); if (ifname && ifconfig_get->can_pull_one()) { // Just request a pull of this interface // // Can't pull my_discard, it's a fake device. if (strcmp(ifname, "my_discard") != 0) { //XLOG_WARNING("Pull_config_one for interface: %s\n", ifname); int rv = ifconfig_get->pull_config_one(_system_config, ifname, if_index); if (rv != XORP_OK) { XLOG_WARNING("ERROR: pull_config_one for interface: %s failed: %i\n", ifname, rv); } if (!_system_config.find_interface(ifname)) { XLOG_WARNING("ERROR: Could not find interface: %s after pull_config_one.\n", ifname); } } } else { // Pull all // Clear the old state _system_config.clear(); ifconfig_get->pull_config(&_user_config, _system_config); } } return _system_config; } bool IfConfig::report_update(const IfTreeInterface& fi) { IfConfigUpdateReporterBase::Update u; if (map_changes(fi.state(), u)) { _ifconfig_update_replicator.interface_update(fi.ifname(), u); return (true); } return (false); } bool IfConfig::report_update(const IfTreeInterface& fi, const IfTreeVif& fv) { IfConfigUpdateReporterBase::Update u; if (map_changes(fv.state(), u)) { _ifconfig_update_replicator.vif_update(fi.ifname(), fv.vifname(), u); return (true); } return (false); } bool IfConfig::report_update(const IfTreeInterface& fi, const IfTreeVif& fv, const IfTreeAddr4& fa) { IfConfigUpdateReporterBase::Update u; if (map_changes(fa.state(), u)) { _ifconfig_update_replicator.vifaddr4_update(fi.ifname(), fv.vifname(), fa.addr(), u); return (true); } return (false); } #ifdef HAVE_IPV6 bool IfConfig::report_update(const IfTreeInterface& fi, const IfTreeVif& fv, const IfTreeAddr6& fa) { IfConfigUpdateReporterBase::Update u; if (map_changes(fa.state(), u)) { _ifconfig_update_replicator.vifaddr6_update(fi.ifname(), fv.vifname(), fa.addr(), u); return (true); } return (false); } #endif void IfConfig::report_updates_completed() { _ifconfig_update_replicator.updates_completed(); } void IfConfig::report_updates(IfTree& iftree) { bool updated = false; // // Walk config looking for changes to report // for (IfTree::IfMap::const_iterator ii = iftree.interfaces().begin(); ii != iftree.interfaces().end(); ++ii) { const IfTreeInterface& interface = *(ii->second); updated |= report_update(interface); IfTreeInterface::VifMap::const_iterator vi; for (vi = interface.vifs().begin(); vi != interface.vifs().end(); ++vi) { const IfTreeVif& vif = *(vi->second); updated |= report_update(interface, vif); for (IfTreeVif::IPv4Map::const_iterator ai = vif.ipv4addrs().begin(); ai != vif.ipv4addrs().end(); ai++) { const IfTreeAddr4& addr = *(ai->second); updated |= report_update(interface, vif, addr); } #ifdef HAVE_IPV6 for (IfTreeVif::IPv6Map::const_iterator ai = vif.ipv6addrs().begin(); ai != vif.ipv6addrs().end(); ai++) { const IfTreeAddr6& addr = *(ai->second); updated |= report_update(interface, vif, addr); } #endif } } if (updated) { // Complete the update report_updates_completed(); } } const string& IfConfig::push_error() const { return _ifconfig_error_reporter.first_error(); } xorp/fea/DEVNOTES.mfea0000664000076400007640000000055211421137511014472 0ustar greearbgreearb# # $XORP: xorp/mfea/DEVNOTES,v 1.1.1.1 2002/12/11 23:56:05 hodson Exp $ # * All ProtoComm entries are explicitly enabled by default inside ProtoComm::start() * MfeaVif::start_protocol() would not start the MfeaVif if it was not running before. Not sure it is the right thing to do. The alternative is to require the MFEA client to send start_vif() XRL. xorp/fea/MakefileRootCheck.in0000664000076400007640000002746711421137511016276 0ustar greearbgreearb# MakefileRootCheck.in generated by automake 1.10.1 from MakefileRootCheck.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, # 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ # # This makefile contains information about tests that must be run as a root # VPATH = @srcdir@ pkgdatadir = $(datadir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ target_triplet = @target@ subdir = fea DIST_COMMON = README $(srcdir)/MakefileRootCheck.am \ $(srcdir)/MakefileRootCheck.in TODO ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/config/compiler_flags.m4 \ $(top_srcdir)/config/dmalloc.m4 $(top_srcdir)/configure.in am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs CONFIG_HEADER = $(top_builddir)/xorp_config.h CONFIG_CLEAN_FILES = SOURCES = DIST_SOURCES = DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AR = @AR@ AS = @AS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CLI_CURSES_LIB = @CLI_CURSES_LIB@ CONTRIB_SUBDIRS = @CONTRIB_SUBDIRS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ ECHO = @ECHO@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ F77 = @F77@ FFLAGS = @FFLAGS@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBTOOL_DEPS = @LIBTOOL_DEPS@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MKDIR_P = @MKDIR_P@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ POW_LIB = @POW_LIB@ PYTHON = @PYTHON@ PYTHON_BUILD = @PYTHON_BUILD@ PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ PYTHON_PLATFORM = @PYTHON_PLATFORM@ PYTHON_PREFIX = @PYTHON_PREFIX@ PYTHON_VERSION = @PYTHON_VERSION@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_F77 = @ac_ct_F77@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgpyexecdir = @pkgpyexecdir@ pkgpythondir = @pkgpythondir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ pyexecdir = @pyexecdir@ pythondir = @pythondir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ subdirs = @subdirs@ sysconfdir = @sysconfdir@ target = @target@ target_alias = @target_alias@ target_cpu = @target_cpu@ target_os = @target_os@ target_vendor = @target_vendor@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ # -- Test Programs and scripts TESTS = test_add_route.sh test_config_interface.sh ################################################################ # A hook to propagate the "-f filename" argument AM_MAKEFLAGS = -f MakefileRootCheck all: all-am .SUFFIXES: $(srcdir)/MakefileRootCheck.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/MakefileRootCheck.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ && exit 0; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign fea/MakefileRootCheck'; \ cd $(top_srcdir) && \ $(AUTOMAKE) --foreign fea/MakefileRootCheck .PRECIOUS: MakefileRootCheck MakefileRootCheck: $(srcdir)/MakefileRootCheck.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs tags: TAGS TAGS: ctags: CTAGS CTAGS: check-TESTS: $(TESTS) @failed=0; all=0; xfail=0; xpass=0; skip=0; ws='[ ]'; \ srcdir=$(srcdir); export srcdir; \ list=' $(TESTS) '; \ if test -n "$$list"; then \ for tst in $$list; do \ if test -f ./$$tst; then dir=./; \ elif test -f $$tst; then dir=; \ else dir="$(srcdir)/"; fi; \ if $(TESTS_ENVIRONMENT) $${dir}$$tst; then \ all=`expr $$all + 1`; \ case " $(XFAIL_TESTS) " in \ *$$ws$$tst$$ws*) \ xpass=`expr $$xpass + 1`; \ failed=`expr $$failed + 1`; \ echo "XPASS: $$tst"; \ ;; \ *) \ echo "PASS: $$tst"; \ ;; \ esac; \ elif test $$? -ne 77; then \ all=`expr $$all + 1`; \ case " $(XFAIL_TESTS) " in \ *$$ws$$tst$$ws*) \ xfail=`expr $$xfail + 1`; \ echo "XFAIL: $$tst"; \ ;; \ *) \ failed=`expr $$failed + 1`; \ echo "FAIL: $$tst"; \ ;; \ esac; \ else \ skip=`expr $$skip + 1`; \ echo "SKIP: $$tst"; \ fi; \ done; \ if test "$$failed" -eq 0; then \ if test "$$xfail" -eq 0; then \ banner="All $$all tests passed"; \ else \ banner="All $$all tests behaved as expected ($$xfail expected failures)"; \ fi; \ else \ if test "$$xpass" -eq 0; then \ banner="$$failed of $$all tests failed"; \ else \ banner="$$failed of $$all tests did not behave as expected ($$xpass unexpected passes)"; \ fi; \ fi; \ dashes="$$banner"; \ skipped=""; \ if test "$$skip" -ne 0; then \ skipped="($$skip tests were not run)"; \ test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \ dashes="$$skipped"; \ fi; \ report=""; \ if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \ report="Please report to $(PACKAGE_BUGREPORT)"; \ test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \ dashes="$$report"; \ fi; \ dashes=`echo "$$dashes" | sed s/./=/g`; \ echo "$$dashes"; \ echo "$$banner"; \ test -z "$$skipped" || echo "$$skipped"; \ test -z "$$report" || echo "$$report"; \ echo "$$dashes"; \ test "$$failed" -eq 0; \ else :; fi distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ fi; \ cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ else \ test -f $(distdir)/$$file \ || cp -p $$d/$$file $(distdir)/$$file \ || exit 1; \ fi; \ done check-am: all-am $(MAKE) $(AM_MAKEFLAGS) check-TESTS check: check-am all-am: MakefileRootCheck installdirs: install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ `test -z '$(STRIP)' || \ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool mostlyclean-am distclean: distclean-am -rm -f MakefileRootCheck distclean-am: clean-am distclean-generic dvi: dvi-am dvi-am: html: html-am info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-exec-am: install-html: install-html-am install-info: install-info-am install-man: install-pdf: install-pdf-am install-ps: install-ps-am installcheck-am: maintainer-clean: maintainer-clean-am -rm -f MakefileRootCheck maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-generic mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: .MAKE: install-am install-strip .PHONY: all all-am check check-TESTS check-am clean clean-generic \ clean-libtool distclean distclean-generic distclean-libtool \ distdir dvi dvi-am html html-am info info-am install \ install-am install-data install-data-am install-dvi \ install-dvi-am install-exec install-exec-am install-html \ install-html-am install-info install-info-am install-man \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ uninstall uninstall-am # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: xorp/fea/io_link_manager.cc0000664000076400007640000007372111540225525016053 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2007-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "fea_node.hh" #include "iftree.hh" #include "io_link_manager.hh" // A filter that matches no packets - useful for TX only. class TxOnlyFilter : public IoLinkComm::InputFilter { public: // A MAC address that will "never" match static const Mac filter_mac; TxOnlyFilter(IoLinkManager& io_link_manager, const string& receiver_name, const string& if_name, const string& vif_name, uint16_t ether_type) : IoLinkComm::InputFilter(io_link_manager, receiver_name, if_name, vif_name, ether_type, "ether src " + filter_mac.str()) {} void recv(const struct MacHeaderInfo& header, const vector& payload) { UNUSED(payload); if (header.src_address == filter_mac) return; XLOG_FATAL("Receiving data on a TX only filter"); } void bye() { delete this; } }; const Mac TxOnlyFilter::filter_mac("66:66:66:66:66:66"); // // Filter class for checking incoming raw link-level packets and checking // whether to forward them. // class LinkVifInputFilter : public IoLinkComm::InputFilter { public: LinkVifInputFilter(IoLinkManager& io_link_manager, IoLinkComm& io_link_comm, const string& receiver_name, const string& if_name, const string& vif_name, uint16_t ether_type, const string& filter_program) : IoLinkComm::InputFilter(io_link_manager, receiver_name, if_name, vif_name, ether_type, filter_program), _io_link_comm(io_link_comm), _enable_multicast_loopback(false) {} virtual ~LinkVifInputFilter() { leave_all_multicast_groups(); } void set_enable_multicast_loopback(bool v) { _enable_multicast_loopback = v; } void recv(const struct MacHeaderInfo& header, const vector& payload) { // Check the EtherType protocol if ((ether_type() != 0) && (ether_type() != header.ether_type)) { debug_msg("Ignore packet with EtherType protocol %u (watching for %u)\n", XORP_UINT_CAST(header.ether_type), XORP_UINT_CAST(ether_type())); return; } // Check if multicast loopback is enabled if (header.dst_address.is_multicast() && is_my_address(header.src_address) && (! _enable_multicast_loopback)) { debug_msg("Ignore raw link-level packet with src %s dst %s: " "multicast loopback is disabled\n", header.src_address.str().c_str(), header.dst_address.str().c_str()); return; } // Forward the packet io_link_manager().recv_event(receiver_name(), header, payload); } void bye() {} int join_multicast_group(const Mac& group_address, string& error_msg) { if (! group_address.is_multicast()) { error_msg = c_format("Cannot join group %s: not a multicast " "address", group_address.str().c_str()); return (XORP_ERROR); } if (_io_link_comm.join_multicast_group(group_address, receiver_name(), error_msg) != XORP_OK) { return (XORP_ERROR); } _joined_multicast_groups.insert(group_address); return (XORP_OK); } int leave_multicast_group(const Mac& group_address, string& error_msg) { _joined_multicast_groups.erase(group_address); if (_io_link_comm.leave_multicast_group(group_address, receiver_name(), error_msg) != XORP_OK) { return (XORP_ERROR); } return (XORP_OK); } void leave_all_multicast_groups() { string error_msg; while (! _joined_multicast_groups.empty()) { Mac group_address = *(_joined_multicast_groups.begin()); leave_multicast_group(group_address, error_msg); } } protected: bool is_my_address(const Mac& addr) const { const IfTreeInterface* ifp; ifp = io_link_manager().iftree().find_interface(if_name()); if (ifp == NULL) return (false); if (! ifp->enabled()) return (false); return (ifp->mac() == addr); } IoLinkComm& _io_link_comm; set _joined_multicast_groups; bool _enable_multicast_loopback; }; /* ------------------------------------------------------------------------- */ /* IoLinkComm methods */ IoLinkComm::IoLinkComm(IoLinkManager& io_link_manager, const IfTree& iftree, const string& if_name, const string& vif_name, uint16_t ether_type, const string& filter_program) : IoLinkReceiver(), _io_link_manager(io_link_manager), _iftree(iftree), _if_name(if_name), _vif_name(vif_name), _ether_type(ether_type), _filter_program(filter_program) { } IoLinkComm::~IoLinkComm() { deallocate_io_link_plugins(); while (_input_filters.empty() == false) { InputFilter* i = _input_filters.front(); _input_filters.erase(_input_filters.begin()); i->bye(); } } int IoLinkComm::add_filter(InputFilter* filter) { if (filter == NULL) { XLOG_FATAL("Adding a null filter"); return (XORP_ERROR); } if (find(_input_filters.begin(), _input_filters.end(), filter) != _input_filters.end()) { debug_msg("filter already exists\n"); return (XORP_ERROR); } _input_filters.push_back(filter); // // Allocate and start the IoLink plugins: one per data plane manager. // if (_input_filters.front() == filter) { XLOG_ASSERT(_io_link_plugins.empty()); allocate_io_link_plugins(); start_io_link_plugins(); } return (XORP_OK); } int IoLinkComm::remove_filter(InputFilter* filter) { list::iterator i; i = find(_input_filters.begin(), _input_filters.end(), filter); if (i == _input_filters.end()) { debug_msg("filter does not exist\n"); return (XORP_ERROR); } XLOG_ASSERT(! _io_link_plugins.empty()); _input_filters.erase(i); if (_input_filters.empty()) { deallocate_io_link_plugins(); } return (XORP_OK); } int IoLinkComm::send_packet(const Mac& src_address, const Mac& dst_address, uint16_t ether_type, const vector& payload, string& error_msg) { int ret_value = XORP_OK; string error_msg2; if (_io_link_plugins.empty()) { error_msg = c_format("No I/O Link plugin to send a link raw packet on " "interface %s vif %s from %s to %s EtherType %u", if_name().c_str(), vif_name().c_str(), src_address.str().c_str(), dst_address.str().c_str(), ether_type); return (XORP_ERROR); } IoLinkPlugins::iterator iter; for (iter = _io_link_plugins.begin(); iter != _io_link_plugins.end(); ++iter) { IoLink* io_link = iter->second; if (io_link->send_packet(src_address, dst_address, ether_type, payload, error_msg2) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } } return (ret_value); } void IoLinkComm::recv_packet(const Mac& src_address, const Mac& dst_address, uint16_t ether_type, const vector& payload) { struct MacHeaderInfo header; header.if_name = if_name(); header.vif_name = vif_name(); header.src_address = src_address; header.dst_address = dst_address; header.ether_type = ether_type; for (list::iterator i = _input_filters.begin(); i != _input_filters.end(); ++i) { (*i)->recv(header, payload); } } int IoLinkComm::join_multicast_group(const Mac& group_address, const string& receiver_name, string& error_msg) { int ret_value = XORP_OK; string error_msg2; if (_io_link_plugins.empty()) { error_msg = c_format("No I/O Link plugin to join group %s " "on interface %s vif %s EtherType %u " "receiver name %s", group_address.str().c_str(), if_name().c_str(), vif_name().c_str(), ether_type(), receiver_name.c_str()); return (XORP_ERROR); } // // Check the arguments // // LinkVifInputFilter checks that the group address is multicast. if (receiver_name.empty()) { error_msg = c_format("Cannot join group %s on interface %s vif %s: " "empty receiver name", group_address.str().c_str(), if_name().c_str(), vif_name().c_str()); return (XORP_ERROR); } JoinedGroupsTable::iterator joined_iter; JoinedMulticastGroup init_jmg(group_address); joined_iter = _joined_groups_table.find(init_jmg); if (joined_iter == _joined_groups_table.end()) { // // First receiver, hence join the multicast group first to check // for errors. // IoLinkPlugins::iterator plugin_iter; for (plugin_iter = _io_link_plugins.begin(); plugin_iter != _io_link_plugins.end(); ++plugin_iter) { IoLink* io_link = plugin_iter->second; if (io_link->join_multicast_group(group_address, error_msg2) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } } _joined_groups_table.insert(make_pair(init_jmg, init_jmg)); joined_iter = _joined_groups_table.find(init_jmg); } XLOG_ASSERT(joined_iter != _joined_groups_table.end()); JoinedMulticastGroup& jmg = joined_iter->second; jmg.add_receiver(receiver_name); return (ret_value); } int IoLinkComm::leave_multicast_group(const Mac& group_address, const string& receiver_name, string& error_msg) { int ret_value = XORP_OK; string error_msg2; if (_io_link_plugins.empty()) { error_msg = c_format("No I/O Link plugin to leave group %s " "on interface %s vif %s EtherType %u " "receiver name %s", group_address.str().c_str(), if_name().c_str(), vif_name().c_str(), ether_type(), receiver_name.c_str()); return (XORP_ERROR); } JoinedGroupsTable::iterator joined_iter; JoinedMulticastGroup init_jmg(group_address); joined_iter = _joined_groups_table.find(init_jmg); if (joined_iter == _joined_groups_table.end()) { error_msg = c_format("Cannot leave group %s on interface %s vif %s: " "the group was not joined", group_address.str().c_str(), if_name().c_str(), vif_name().c_str()); XLOG_WARNING("%s", error_msg.c_str()); // Don't fail this..would fail the whole commit, and the group isn't joined // anyway, so no loss of validity in the configuration. return XORP_OK; } JoinedMulticastGroup& jmg = joined_iter->second; jmg.delete_receiver(receiver_name); if (jmg.empty()) { // // The last receiver, hence leave the group // _joined_groups_table.erase(joined_iter); IoLinkPlugins::iterator plugin_iter; for (plugin_iter = _io_link_plugins.begin(); plugin_iter != _io_link_plugins.end(); ++plugin_iter) { IoLink* io_link = plugin_iter->second; if (io_link->leave_multicast_group(group_address, error_msg2) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } } } return (ret_value); } void IoLinkComm::allocate_io_link_plugins() { list::iterator iter; for (iter = _io_link_manager.fea_data_plane_managers().begin(); iter != _io_link_manager.fea_data_plane_managers().end(); ++iter) { FeaDataPlaneManager* fea_data_plane_manager = *iter; allocate_io_link_plugin(fea_data_plane_manager); } } void IoLinkComm::deallocate_io_link_plugins() { while (! _io_link_plugins.empty()) { IoLinkPlugins::iterator iter = _io_link_plugins.begin(); FeaDataPlaneManager* fea_data_plane_manager = iter->first; deallocate_io_link_plugin(fea_data_plane_manager); } } void IoLinkComm::allocate_io_link_plugin(FeaDataPlaneManager* fea_data_plane_manager) { IoLinkPlugins::iterator iter; XLOG_ASSERT(fea_data_plane_manager != NULL); for (iter = _io_link_plugins.begin(); iter != _io_link_plugins.end(); ++iter) { if (iter->first == fea_data_plane_manager) break; } if (iter != _io_link_plugins.end()) { return; // XXX: the plugin was already allocated } IoLink* io_link = fea_data_plane_manager->allocate_io_link(_iftree, _if_name, _vif_name, _ether_type, _filter_program); if (io_link == NULL) { XLOG_ERROR("Couldn't allocate plugin for I/O Link raw " "communications for data plane manager %s", fea_data_plane_manager->manager_name().c_str()); return; } _io_link_plugins.push_back(make_pair(fea_data_plane_manager, io_link)); } void IoLinkComm::deallocate_io_link_plugin(FeaDataPlaneManager* fea_data_plane_manager) { IoLinkPlugins::iterator iter; XLOG_ASSERT(fea_data_plane_manager != NULL); for (iter = _io_link_plugins.begin(); iter != _io_link_plugins.end(); ++iter) { if (iter->first == fea_data_plane_manager) break; } if (iter == _io_link_plugins.end()) { XLOG_ERROR("Couldn't deallocate plugin for I/O Link raw " "communications for data plane manager %s: plugin not found", fea_data_plane_manager->manager_name().c_str()); return; } IoLink* io_link = iter->second; fea_data_plane_manager->deallocate_io_link(io_link); _io_link_plugins.erase(iter); } void IoLinkComm::start_io_link_plugins() { IoLinkPlugins::iterator iter; string error_msg; for (iter = _io_link_plugins.begin(); iter != _io_link_plugins.end(); ++iter) { IoLink* io_link = iter->second; if (io_link->is_running()) continue; io_link->register_io_link_receiver(this); if (io_link->start(error_msg) != XORP_OK) { XLOG_ERROR("%s", error_msg.c_str()); continue; } // // Push all multicast joins into the new plugin // JoinedGroupsTable::iterator join_iter; for (join_iter = _joined_groups_table.begin(); join_iter != _joined_groups_table.end(); ++join_iter) { JoinedMulticastGroup& joined_multicast_group = join_iter->second; if (io_link->join_multicast_group(joined_multicast_group.group_address(), error_msg) != XORP_OK) { XLOG_ERROR("%s", error_msg.c_str()); } } } } void IoLinkComm::stop_io_link_plugins() { string error_msg; IoLinkPlugins::iterator iter; for (iter = _io_link_plugins.begin(); iter != _io_link_plugins.end(); ++iter) { IoLink* io_link = iter->second; io_link->unregister_io_link_receiver(); if (io_link->stop(error_msg) != XORP_OK) { XLOG_ERROR("%s", error_msg.c_str()); } } } // ---------------------------------------------------------------------------- // IoLinkManager code IoLinkManager::IoLinkManager(FeaNode& fea_node, const IfTree& iftree) : IoLinkManagerReceiver(), _fea_node(fea_node), _eventloop(fea_node.eventloop()), _iftree(iftree), _io_link_manager_receiver(NULL) { } IoLinkManager::~IoLinkManager() { erase_filters(_comm_table, _filters, _filters.begin(), _filters.end()); // Erase any TX only entries for (CommTable::iterator i = _comm_table.begin(); i != _comm_table.end(); ++i) delete i->second; } void IoLinkManager::recv_event(const string& receiver_name, const struct MacHeaderInfo& header, const vector& payload) { if (_io_link_manager_receiver != NULL) _io_link_manager_receiver->recv_event(receiver_name, header, payload); } void IoLinkManager::erase_filters_by_receiver_name(const string& receiver_name) { erase_filters(_comm_table, _filters, _filters.lower_bound(receiver_name), _filters.upper_bound(receiver_name)); } bool IoLinkManager::has_filter_by_receiver_name(const string& receiver_name) const { // There whether there is an entry for a given receiver name if (_filters.find(receiver_name) != _filters.end()) return (true); return (false); } void IoLinkManager::erase_filters(CommTable& comm_table, FilterBag& filters, const FilterBag::iterator& begin, const FilterBag::iterator& end) { FilterBag::iterator fi(begin); while (fi != end) { IoLinkComm::InputFilter* filter = fi->second; CommTableKey key(filter->if_name(), filter->vif_name(), filter->ether_type(), filter->filter_program()); CommTable::iterator cti = comm_table.find(key); XLOG_ASSERT(cti != comm_table.end()); IoLinkComm* io_link_comm = cti->second; XLOG_ASSERT(io_link_comm != NULL); io_link_comm->remove_filter(filter); delete filter; filters.erase(fi++); // // Reference counting: if there are now no listeners on // this protocol socket (and hence no filters), remove it // from the table and delete it. // if (io_link_comm->no_input_filters()) { comm_table.erase(key); delete io_link_comm; } } } IoLinkComm& IoLinkManager::find_iolink_comm(const string& if_name, const string& vif_name, uint16_t ether_type) { CommTableKey key(if_name, vif_name, ether_type, ""); // Find the IoLinkComm associated with this protocol CommTable::iterator cti = _comm_table.find(key); // // XXX: Search the whole table in case the protocol was registered // only with some non-empty filter program. // if (cti == _comm_table.end()) { for (cti = _comm_table.begin(); cti != _comm_table.end(); ++cti) { IoLinkComm* c = cti->second; if ((c->if_name() == if_name) && (c->vif_name() == vif_name) && (c->ether_type() == ether_type)) { break; } } } IoLinkComm* io_link_comm = NULL; if (cti == _comm_table.end()) io_link_comm = &add_iolink_comm_txonly(if_name, vif_name, ether_type); else io_link_comm = cti->second; XLOG_ASSERT(io_link_comm != NULL); return *io_link_comm; } int IoLinkManager::send(const string& if_name, const string& vif_name, const Mac& src_address, const Mac& dst_address, uint16_t ether_type, const vector& payload, string& error_msg) { IoLinkComm& io_link_comm = find_iolink_comm(if_name, vif_name, ether_type); return (io_link_comm.send_packet(src_address, dst_address, ether_type, payload, error_msg)); } IoLinkComm& IoLinkManager::add_iolink_comm_txonly(const string& if_name, const string& vif_name, uint16_t ether_type) { // // TODO: XXX: Ideally we'd configure the io plugins to do TX only, but // what we do now is create a "txonlyfilter" - a filter that matches no // packets and hopefully receives nothing. // string receiver_name = "txonly"; string filter_program = ""; TxOnlyFilter* filter = new TxOnlyFilter(*this, receiver_name, if_name, vif_name, ether_type); filter_program = filter->filter_program(); IoLinkComm* io_link_comm = NULL; CommTableKey key(if_name, vif_name, ether_type, filter_program); CommTable::iterator i = _comm_table.find(key); if (_comm_table.find(key) != _comm_table.end()) io_link_comm = i->second; else { io_link_comm = new IoLinkComm(*this, iftree(), if_name, vif_name, ether_type, filter_program); _comm_table[key] = io_link_comm; } XLOG_ASSERT(io_link_comm != NULL); int rc = io_link_comm->add_filter(filter); XLOG_ASSERT(rc == XORP_OK); return *io_link_comm; } int IoLinkManager::add_multicast_mac(const string& if_name, const Mac& mac, string& error_msg) { return (add_remove_multicast_mac(true, if_name, mac, error_msg)); } int IoLinkManager::remove_multicast_mac(const string& if_name, const Mac& mac, string& error_msg) { return (add_remove_multicast_mac(false, if_name, mac, error_msg)); } int IoLinkManager::add_remove_multicast_mac(bool add, const string& if_name, const Mac& mac, string& error_msg) { string vif_name = if_name; uint16_t ether_type = ETHERTYPE_IP; string receiver_name = "add_remove_mac"; int rc; IoLinkComm& io_link_comm = find_iolink_comm(if_name, vif_name, ether_type); if (add) rc = io_link_comm.join_multicast_group(mac, receiver_name, error_msg); else rc = io_link_comm.leave_multicast_group(mac, receiver_name, error_msg); return (rc); } int IoLinkManager::register_receiver(const string& receiver_name, const string& if_name, const string& vif_name, uint16_t ether_type, const string& filter_program, bool enable_multicast_loopback, string& error_msg) { CommTableKey key(if_name, vif_name, ether_type, filter_program); LinkVifInputFilter* filter; error_msg = ""; // // Look in the CommTable for an entry matching this protocol. // If an entry does not yet exist, create one. // CommTable::iterator cti = _comm_table.find(key); IoLinkComm* io_link_comm = NULL; if (cti == _comm_table.end()) { io_link_comm = new IoLinkComm(*this, iftree(), if_name, vif_name, ether_type, filter_program); _comm_table[key] = io_link_comm; } else { io_link_comm = cti->second; } XLOG_ASSERT(io_link_comm != NULL); // // Walk through list of filters looking for matching filter // FilterBag::iterator fi; FilterBag::iterator fi_end = _filters.upper_bound(receiver_name); for (fi = _filters.lower_bound(receiver_name); fi != fi_end; ++fi) { filter = dynamic_cast(fi->second); if (filter == NULL) continue; // Not a vif filter // // Search if we have already the filter // if ((filter->ether_type() == ether_type) && (filter->if_name() == if_name) && (filter->vif_name() == vif_name) && (filter->filter_program() == filter_program)) { // Already have this filter filter->set_enable_multicast_loopback(enable_multicast_loopback); return (XORP_OK); } } // // Create the filter // filter = new LinkVifInputFilter(*this, *io_link_comm, receiver_name, if_name, vif_name, ether_type, filter_program); filter->set_enable_multicast_loopback(enable_multicast_loopback); // Add the filter to the appropriate IoLinkComm entry io_link_comm->add_filter(filter); // Add the filter to those associated with receiver_name _filters.insert(FilterBag::value_type(receiver_name, filter)); // Register interest in watching the receiver if (_fea_node.fea_io().add_instance_watch(receiver_name, this, error_msg) != XORP_OK) { string dummy_error_msg; unregister_receiver(receiver_name, if_name, vif_name, ether_type, filter_program, dummy_error_msg); return (XORP_ERROR); } return (XORP_OK); } int IoLinkManager::unregister_receiver(const string& receiver_name, const string& if_name, const string& vif_name, uint16_t ether_type, const string& filter_program, string& error_msg) { CommTableKey key(if_name, vif_name, ether_type, filter_program); // // Find the IoLinkComm entry associated with this protocol // CommTable::iterator cti = _comm_table.find(key); if (cti == _comm_table.end()) { error_msg = c_format("EtherType protocol %u filter program %s " "is not registered on interface %s vif %s", XORP_UINT_CAST(ether_type), filter_program.c_str(), if_name.c_str(), vif_name.c_str()); return (XORP_ERROR); } IoLinkComm* io_link_comm = cti->second; XLOG_ASSERT(io_link_comm != NULL); // // Walk through list of filters looking for matching interface and vif // FilterBag::iterator fi; FilterBag::iterator fi_end = _filters.upper_bound(receiver_name); for (fi = _filters.lower_bound(receiver_name); fi != fi_end; ++fi) { LinkVifInputFilter* filter; filter = dynamic_cast(fi->second); if (filter == NULL) continue; // Not a vif filter // If filter found, remove it and delete it if ((filter->ether_type() == ether_type) && (filter->if_name() == if_name) && (filter->vif_name() == vif_name) && (filter->filter_program() == filter_program)) { // Remove the filter io_link_comm->remove_filter(filter); // Remove the filter from the group associated with this receiver _filters.erase(fi); // Destruct the filter delete filter; // // Reference counting: if there are now no listeners on // this protocol socket (and hence no filters), remove it // from the table and delete it. // if (io_link_comm->no_input_filters()) { _comm_table.erase(key); delete io_link_comm; } // Deregister interest in watching the receiver if (! has_filter_by_receiver_name(receiver_name)) { string dummy_error_msg; _fea_node.fea_io().delete_instance_watch(receiver_name, this, dummy_error_msg); } return (XORP_OK); } } error_msg = c_format("Cannot find registration for receiver %s " "EtherType protocol %u filter program %s " "interface %s and vif %s", receiver_name.c_str(), XORP_UINT_CAST(ether_type), filter_program.c_str(), if_name.c_str(), vif_name.c_str()); return (XORP_ERROR); } int IoLinkManager::join_multicast_group(const string& receiver_name, const string& if_name, const string& vif_name, uint16_t ether_type, const string& filter_program, const Mac& group_address, string& error_msg) { // // Search if we have already the filter // FilterBag::iterator fi; FilterBag::iterator fi_end = _filters.upper_bound(receiver_name); for (fi = _filters.lower_bound(receiver_name); fi != fi_end; ++fi) { LinkVifInputFilter* filter; filter = dynamic_cast(fi->second); if (filter == NULL) continue; // Not a vif filter if ((filter->ether_type() == ether_type) && (filter->if_name() == if_name) && (filter->vif_name() == vif_name) && (filter->filter_program() == filter_program)) { // Filter found if (filter->join_multicast_group(group_address, error_msg) != XORP_OK) { return (XORP_ERROR); } return (XORP_OK); } } error_msg = c_format("Cannot join group %s on interface %s vif %s " "protocol %u filter program %s receiver %s: " "not registered", group_address.str().c_str(), if_name.c_str(), vif_name.c_str(), XORP_UINT_CAST(ether_type), filter_program.c_str(), receiver_name.c_str()); return (XORP_ERROR); } int IoLinkManager::leave_multicast_group(const string& receiver_name, const string& if_name, const string& vif_name, uint16_t ether_type, const string& filter_program, const Mac& group_address, string& error_msg) { // // Search if we have already the filter // FilterBag::iterator fi; FilterBag::iterator fi_end = _filters.upper_bound(receiver_name); for (fi = _filters.lower_bound(receiver_name); fi != fi_end; ++fi) { LinkVifInputFilter* filter; filter = dynamic_cast(fi->second); if (filter == NULL) continue; // Not a vif filter if ((filter->ether_type() == ether_type) && (filter->if_name() == if_name) && (filter->vif_name() == vif_name) && (filter->filter_program() == filter_program)) { // Filter found if (filter->leave_multicast_group(group_address, error_msg) != XORP_OK) { return (XORP_ERROR); } return (XORP_OK); } } error_msg = c_format("Cannot leave group %s on interface %s vif %s " "protocol %u filter program %s receiver %s: " "not registered", group_address.str().c_str(), if_name.c_str(), vif_name.c_str(), XORP_UINT_CAST(ether_type), filter_program.c_str(), receiver_name.c_str()); return (XORP_ERROR); } int IoLinkManager::register_data_plane_manager(FeaDataPlaneManager* fea_data_plane_manager, bool is_exclusive) { if (is_exclusive) { // Unregister all registered data plane managers while (! _fea_data_plane_managers.empty()) { unregister_data_plane_manager(_fea_data_plane_managers.front()); } } if (fea_data_plane_manager == NULL) { // XXX: exclusive NULL is used to unregister all data plane managers return (XORP_OK); } if (find(_fea_data_plane_managers.begin(), _fea_data_plane_managers.end(), fea_data_plane_manager) != _fea_data_plane_managers.end()) { // XXX: already registered return (XORP_OK); } _fea_data_plane_managers.push_back(fea_data_plane_manager); // // Allocate all I/O Link plugins for the new data plane manager // CommTable::iterator iter; for (iter = _comm_table.begin(); iter != _comm_table.end(); ++iter) { IoLinkComm* io_link_comm = iter->second; io_link_comm->allocate_io_link_plugin(fea_data_plane_manager); io_link_comm->start_io_link_plugins(); } return (XORP_OK); } int IoLinkManager::unregister_data_plane_manager(FeaDataPlaneManager* fea_data_plane_manager) { if (fea_data_plane_manager == NULL) return (XORP_ERROR); list::iterator data_plane_manager_iter; data_plane_manager_iter = find(_fea_data_plane_managers.begin(), _fea_data_plane_managers.end(), fea_data_plane_manager); if (data_plane_manager_iter == _fea_data_plane_managers.end()) return (XORP_ERROR); // // Dealocate all I/O Link plugins for the unregistered data plane manager // CommTable::iterator iter; for (iter = _comm_table.begin(); iter != _comm_table.end(); ++iter) { IoLinkComm* io_link_comm = iter->second; io_link_comm->deallocate_io_link_plugin(fea_data_plane_manager); } _fea_data_plane_managers.erase(data_plane_manager_iter); return (XORP_OK); } void IoLinkManager::instance_birth(const string& instance_name) { // XXX: Nothing to do UNUSED(instance_name); } void IoLinkManager::instance_death(const string& instance_name) { string dummy_error_msg; _fea_node.fea_io().delete_instance_watch(instance_name, this, dummy_error_msg); erase_filters_by_receiver_name(instance_name); } xorp/fea/xrl_io_link_manager.cc0000664000076400007640000000452711421137511016731 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2007-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxipc/xrl_router.hh" #include "xrl/interfaces/fea_rawlink_client_xif.hh" #include "xrl_io_link_manager.hh" XrlIoLinkManager::XrlIoLinkManager(IoLinkManager& io_link_manager, XrlRouter& xrl_router) : IoLinkManagerReceiver(), _io_link_manager(io_link_manager), _xrl_router(xrl_router) { _io_link_manager.set_io_link_manager_receiver(this); } XrlIoLinkManager::~XrlIoLinkManager() { _io_link_manager.set_io_link_manager_receiver(NULL); } void XrlIoLinkManager::recv_event(const string& receiver_name, const struct MacHeaderInfo& header, const vector& payload) { // // Instantiate client sending interface // XrlRawLinkClientV0p1Client cl(&xrl_router()); // // Send notification // cl.send_recv(receiver_name.c_str(), header.if_name, header.vif_name, header.src_address, header.dst_address, header.ether_type, payload, callback(this, &XrlIoLinkManager::xrl_send_recv_cb, receiver_name)); } void XrlIoLinkManager::xrl_send_recv_cb(const XrlError& xrl_error, string receiver_name) { if (xrl_error == XrlError::OKAY()) return; debug_msg("xrl_send_recv_cb: error %s\n", xrl_error.str().c_str()); // // Sending Xrl generated an error. // // Remove all filters associated with this receiver. // _io_link_manager.instance_death(receiver_name); } xorp/fea/mfea_node_cli.hh0000664000076400007640000000537411540224224015505 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/mfea_node_cli.hh,v 1.10 2008/10/02 21:56:49 bms Exp $ #ifndef __FEA_MFEA_NODE_CLI_HH__ #define __FEA_MFEA_NODE_CLI_HH__ // // MFEA (Multicast Forwarding Engine Abstraction) CLI // #include "libproto/proto_node_cli.hh" // // Constants definitions // // // Structures/classes, typedefs and macros // class MfeaNode; /** * @short The class for @ref MfeaNode CLI access. */ class MfeaNodeCli : public ProtoNodeCli { public: /** * Constructor for a given MFEA node. * * @param mfea_node the @ref MfeaNode this node belongs to. */ MfeaNodeCli(MfeaNode& mfea_node); /** * Destructor */ virtual ~MfeaNodeCli(); /** * Start the CLI operation. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int start(); /** * Stop the CLI operation. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int stop(); /** * Enable node operation. * * If an unit is not enabled, it cannot be start, or pending-start. */ void enable(); /** * Disable 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 disable(); /** * Install all MFEA-related CLI commands to the CLI. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_all_cli_commands(); private: MfeaNode& mfea_node() const { return (_mfea_node); } MfeaNode& _mfea_node; // // MFEA CLI commands // int cli_show_mfea_dataflow(const vector& argv); int cli_show_mfea_interface(const vector& argv); int cli_show_mfea_interface_address(const vector& argv); }; // // Global variables // // // Global functions prototypes // #endif // __FEA_MFEA_NODE_CLI_HH__ xorp/fea/xrl_io_ip_manager.cc0000664000076400007640000000740311540225525016405 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxipc/xrl_router.hh" #include "xrl/interfaces/fea_rawpkt4_client_xif.hh" #ifdef HAVE_IPV6 #include "xrl/interfaces/fea_rawpkt6_client_xif.hh" #endif #include "xrl_io_ip_manager.hh" XrlIoIpManager::XrlIoIpManager(IoIpManager& io_ip_manager, XrlRouter& xrl_router) : IoIpManagerReceiver(), _io_ip_manager(io_ip_manager), _xrl_router(xrl_router) { _io_ip_manager.set_io_ip_manager_receiver(this); } XrlIoIpManager::~XrlIoIpManager() { _io_ip_manager.set_io_ip_manager_receiver(NULL); } void XrlIoIpManager::recv_event(const string& receiver_name, const struct IPvXHeaderInfo& header, const vector& payload) { size_t i; // // Create the extention headers info // XLOG_ASSERT(header.ext_headers_type.size() == header.ext_headers_payload.size()); XrlAtomList ext_headers_type_list, ext_headers_payload_list; for (i = 0; i < header.ext_headers_type.size(); i++) { ext_headers_type_list.append(XrlAtom(static_cast(header.ext_headers_type[i]))); ext_headers_payload_list.append(XrlAtom(header.ext_headers_payload[i])); } if (header.src_address.is_ipv4()) { // // Instantiate client sending interface // XrlRawPacket4ClientV0p1Client cl(&xrl_router()); // // Send notification // cl.send_recv(receiver_name.c_str(), header.if_name, header.vif_name, header.src_address.get_ipv4(), header.dst_address.get_ipv4(), header.ip_protocol, header.ip_ttl, header.ip_tos, header.ip_router_alert, header.ip_internet_control, payload, callback(this, &XrlIoIpManager::xrl_send_recv_cb, header.src_address.af(), receiver_name)); } #ifdef HAVE_IPV6 if (header.src_address.is_ipv6()) { // // Instantiate client sending interface // XrlRawPacket6ClientV0p1Client cl(&xrl_router()); // // Send notification // cl.send_recv(receiver_name.c_str(), header.if_name, header.vif_name, header.src_address.get_ipv6(), header.dst_address.get_ipv6(), header.ip_protocol, header.ip_ttl, header.ip_tos, header.ip_router_alert, header.ip_internet_control, ext_headers_type_list, ext_headers_payload_list, payload, callback(this, &XrlIoIpManager::xrl_send_recv_cb, header.src_address.af(), receiver_name)); } #endif } void XrlIoIpManager::xrl_send_recv_cb(const XrlError& xrl_error, int family, string receiver_name) { UNUSED(family); if (xrl_error == XrlError::OKAY()) return; debug_msg("xrl_send_recv_cb: error %s\n", xrl_error.str().c_str()); // // Sending Xrl generated an error. // // Remove all filters associated with this receiver. // _io_ip_manager.instance_death(receiver_name); } xorp/fea/ifconfig_reporter.cc0000664000076400007640000001557211540225525016443 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/c_format.hh" #include "libxorp/ipv4.hh" #include "libxorp/ipv6.hh" #include "ifconfig_reporter.hh" #include "iftree.hh" // // Mechanism for reporting network interface related information to // interested parties. // IfConfigUpdateReporterBase::IfConfigUpdateReporterBase( IfConfigUpdateReplicator& update_replicator) : _update_replicator(update_replicator), _observed_iftree(_update_replicator.observed_iftree()) { } IfConfigUpdateReporterBase::IfConfigUpdateReporterBase( IfConfigUpdateReplicator& update_replicator, const IfTree& observed_iftree) : _update_replicator(update_replicator), _observed_iftree(observed_iftree) { } void IfConfigUpdateReporterBase::add_to_replicator() { _update_replicator.add_reporter(this); } void IfConfigUpdateReporterBase::remove_from_replicator() { _update_replicator.remove_reporter(this); } // ---------------------------------------------------------------------------- // IfConfigUpdateReplicator IfConfigUpdateReplicator::~IfConfigUpdateReplicator() { } int IfConfigUpdateReplicator::add_reporter(IfConfigUpdateReporterBase* rp) { if (find(_reporters.begin(), _reporters.end(), rp) != _reporters.end()) return (XORP_ERROR); _reporters.push_back(rp); // // Propagate all current interface information // Update update = IfConfigUpdateReporterBase::CREATED; IfTree::IfMap::const_iterator if_iter; for (if_iter = observed_iftree().interfaces().begin(); if_iter != observed_iftree().interfaces().end(); ++if_iter) { const IfTreeInterface& iface = *(if_iter->second); rp->interface_update(iface.ifname(), update); IfTreeInterface::VifMap::const_iterator vif_iter; for (vif_iter = iface.vifs().begin(); vif_iter != iface.vifs().end(); ++vif_iter) { const IfTreeVif& vif = *(vif_iter->second); rp->vif_update(iface.ifname(), vif.vifname(), update); IfTreeVif::IPv4Map::const_iterator a4_iter; for (a4_iter = vif.ipv4addrs().begin(); a4_iter != vif.ipv4addrs().end(); ++a4_iter) { const IfTreeAddr4& a4 = *(a4_iter->second); rp->vifaddr4_update(iface.ifname(), vif.vifname(), a4.addr(), update); } #ifdef HAVE_IPV6 IfTreeVif::IPv6Map::const_iterator a6_iter; for (a6_iter = vif.ipv6addrs().begin(); a6_iter != vif.ipv6addrs().end(); ++a6_iter) { const IfTreeAddr6& a6 = *(a6_iter->second); rp->vifaddr6_update(iface.ifname(), vif.vifname(), a6.addr(), update); } #endif } } rp->updates_completed(); return (XORP_OK); } int IfConfigUpdateReplicator::remove_reporter(IfConfigUpdateReporterBase* rp) { list::iterator i = find(_reporters.begin(), _reporters.end(), rp); if (i == _reporters.end()) return (XORP_ERROR); _reporters.erase(i); return (XORP_OK); } void IfConfigUpdateReplicator::interface_update(const string& ifname, const Update& update) { list::iterator i = _reporters.begin(); while (i != _reporters.end()) { IfConfigUpdateReporterBase*& r = *i; r->interface_update(ifname, update); ++i; } } void IfConfigUpdateReplicator::vif_update(const string& ifname, const string& vifname, const Update& update) { list::iterator i = _reporters.begin(); while (i != _reporters.end()) { IfConfigUpdateReporterBase*& r = *i; r->vif_update(ifname, vifname, update); ++i; } } void IfConfigUpdateReplicator::vifaddr4_update(const string& ifname, const string& vifname, const IPv4& addr, const Update& update) { list::iterator i = _reporters.begin(); while (i != _reporters.end()) { IfConfigUpdateReporterBase*& r = *i; r->vifaddr4_update(ifname, vifname, addr, update); ++i; } } #ifdef HAVE_IPV6 void IfConfigUpdateReplicator::vifaddr6_update(const string& ifname, const string& vifname, const IPv6& addr, const Update& update) { list::iterator i = _reporters.begin(); while (i != _reporters.end()) { IfConfigUpdateReporterBase*& r = *i; r->vifaddr6_update(ifname, vifname, addr, update); ++i; } } #endif void IfConfigUpdateReplicator::updates_completed() { list::iterator i = _reporters.begin(); while (i != _reporters.end()) { IfConfigUpdateReporterBase*& r = *i; r->updates_completed(); ++i; } } // ---------------------------------------------------------------------------- // IfConfigErrorReporter methods IfConfigErrorReporter::IfConfigErrorReporter() { } void IfConfigErrorReporter::config_error(const string& error_msg) { string preamble(c_format("Config error: ")); log_error(preamble + error_msg); } void IfConfigErrorReporter::interface_error(const string& ifname, const string& error_msg) { string preamble(c_format("Interface error on %s: ", ifname.c_str())); log_error(preamble + error_msg); } void IfConfigErrorReporter::vif_error(const string& ifname, const string& vifname, const string& error_msg) { string preamble(c_format("Interface/Vif error on %s/%s: ", ifname.c_str(), vifname.c_str())); log_error(preamble + error_msg); } void IfConfigErrorReporter::vifaddr_error(const string& ifname, const string& vifname, const IPv4& addr, const string& error_msg) { string preamble(c_format("Interface/Vif/Address error on %s/%s/%s: ", ifname.c_str(), vifname.c_str(), addr.str().c_str())); log_error(preamble + error_msg); } #ifdef HAVE_IPV6 void IfConfigErrorReporter::vifaddr_error(const string& ifname, const string& vifname, const IPv6& addr, const string& error_msg) { string preamble(c_format("Interface/Vif/Address error on %s/%s/%s: ", ifname.c_str(), vifname.c_str(), addr.str().c_str())); log_error(preamble + error_msg); } #endif xorp/fea/ifconfig_reporter.hh0000664000076400007640000001737111540225525016454 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/ifconfig_reporter.hh,v 1.9 2008/10/02 21:56:47 bms Exp $ #ifndef __FEA_IFCONFIG_REPORTER_HH__ #define __FEA_IFCONFIG_REPORTER_HH__ // // Mechanism for reporting network interface related information to // interested parties. // class IfConfigUpdateReplicator; class IfTree; class IPv4; #ifdef HAVE_IPV6 class IPv6; #endif /** * @short Base class for propagating update information on from IfConfig. * * When the platform @ref IfConfig updates interfaces it can report * updates to an IfConfigUpdateReporter. */ class IfConfigUpdateReporterBase { public: enum Update { CREATED, DELETED, CHANGED }; /** * Constructor for a given replicator. * * @param update_replicator the corresponding replicator * (@ref IfConfigUpdateReplicator). */ IfConfigUpdateReporterBase(IfConfigUpdateReplicator& update_replicator); /** * Constructor for a given replicator and observed tree. * * @param update_replicator the corresponding replicator * (@ref IfConfigUpdateReplicator). * @param observed_iftree the corresponding interface tree (@ref IfTree). */ IfConfigUpdateReporterBase(IfConfigUpdateReplicator& update_replicator, const IfTree& observed_iftree); /** * Destructor. */ virtual ~IfConfigUpdateReporterBase() {} /** * Get a reference to the observed interface tree. * * @return a reference to the observed interface tree (see @IfTree). */ const IfTree& observed_iftree() const { return (_observed_iftree); } /** * Add itself to the replicator (see @IfConfigUpdateReplicator). */ void add_to_replicator(); /** * Remove itself from the replicator (see @IfConfigUpdateReplicator). */ void remove_from_replicator(); virtual void interface_update(const string& ifname, const Update& u) = 0; virtual void vif_update(const string& ifname, const string& vifname, const Update& u) = 0; virtual void vifaddr4_update(const string& ifname, const string& vifname, const IPv4& addr, const Update& u) = 0; #ifdef HAVE_IPV6 virtual void vifaddr6_update(const string& ifname, const string& vifname, const IPv6& addr, const Update& u) = 0; #endif virtual void updates_completed() = 0; private: IfConfigUpdateReplicator& _update_replicator; const IfTree& _observed_iftree; }; /** * @short A class to replicate update notifications to multiple reporters. */ class IfConfigUpdateReplicator : public IfConfigUpdateReporterBase { public: typedef IfConfigUpdateReporterBase::Update Update; public: IfConfigUpdateReplicator(const IfTree& observed_iftree) : IfConfigUpdateReporterBase(*this, observed_iftree) {} virtual ~IfConfigUpdateReplicator(); /** * Add a reporter instance to update notification list. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_reporter(IfConfigUpdateReporterBase* rp); /** * Remove a reporter instance from update notification list. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int remove_reporter(IfConfigUpdateReporterBase* rp); /** * Forward interface update notification to reporter instances on * update notification list. */ void interface_update(const string& ifname, const Update& u); /** * Forward virtual interface update notification to reporter * instances on update notification list. */ void vif_update(const string& ifname, const string& vifname, const Update& u); /** * Forward virtual interface address update notification to * reporter instances on update notification list. */ void vifaddr4_update(const string& ifname, const string& vifname, const IPv4& addr, const Update& u); #ifdef HAVE_IPV6 /** * Forward virtual interface address update notification to * reporter instances on update notification list. */ void vifaddr6_update(const string& ifname, const string& vifname, const IPv6& addr, const Update& u); #endif /** * Forward notification that updates were completed to * reporter instances on update notification list. */ void updates_completed(); protected: list _reporters; }; /** * @short Base class for propagating error information on from IfConfig. * * When the platform @ref IfConfig updates interfaces it can report * errors to an IfConfigErrorReporterBase. The @ref IfConfig instance * takes a pointer to the IfConfigErrorReporterBase object it should use. */ class IfConfigErrorReporterBase { public: IfConfigErrorReporterBase() : _error_cnt(0) {} virtual ~IfConfigErrorReporterBase() {} virtual void config_error(const string& error_msg) = 0; virtual void interface_error(const string& ifname, const string& error_msg) = 0; virtual void vif_error(const string& ifname, const string& vifname, const string& error_msg) = 0; virtual void vifaddr_error(const string& ifname, const string& vifname, const IPv4& addr, const string& error_msg) = 0; #ifdef HAVE_IPV6 virtual void vifaddr_error(const string& ifname, const string& vifname, const IPv6& addr, const string& error_msg) = 0; #endif /** * @return error message of first error encountered. */ const string& first_error() const { return _first_error; } /** * @return error message of last error encountered. */ const string& last_error() const { return _last_error; } /** * @return number of errors reported. */ uint32_t error_count() const { return _error_cnt; } /** * Reset error count and error message. */ void reset() { _first_error.erase(); _last_error.erase(); _error_cnt = 0; } void log_error(const string& s) { if (0 == _error_cnt) _first_error = s; _last_error = s; _error_cnt++; } private: string _last_error; // last error reported string _first_error; // first error reported uint32_t _error_cnt; // number of errors reported }; /** * @short Class for propagating error information during IfConfig operations. * */ class IfConfigErrorReporter : public IfConfigErrorReporterBase { public: IfConfigErrorReporter(); void config_error(const string& error_msg); void interface_error(const string& ifname, const string& error_msg); void vif_error(const string& ifname, const string& vifname, const string& error_msg); void vifaddr_error(const string& ifname, const string& vifname, const IPv4& addr, const string& error_msg); #ifdef HAVE_IPV6 void vifaddr_error(const string& ifname, const string& vifname, const IPv6& addr, const string& error_msg); #endif private: }; #endif // __FEA_IFCONFIG_REPORTER_HH__ xorp/fea/xrl_fib_client_manager.hh0000664000076400007640000002015711703345405017420 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2012 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __FEA_XRL_FIB_CLIENT_MANAGER_HH__ #define __FEA_XRL_FIB_CLIENT_MANAGER_HH__ #include "libxipc/xrl_router.hh" #include "xrl/interfaces/fea_fib_client_xif.hh" #include "fibconfig_transaction.hh" #include "fte.hh" /** * Class for managing clients interested in FIB changes notifications. */ class XrlFibClientManager : public FibTableObserverBase { public: /** * Constructor * * @param fibconfig the FibConfig configuration object (@ref FibConfig). */ XrlFibClientManager(FibConfig& fibconfig, XrlRouter& xrl_router) : _fibconfig(fibconfig), _xrl_fea_fib_client(&xrl_router) { _fibconfig.add_fib_table_observer(this); } virtual ~XrlFibClientManager() { _fibconfig.delete_fib_table_observer(this); } /** * Get a reference to the @ref EventLoop instance. * * @return a reference to the @ref EventLoop instance. */ EventLoop& eventloop() { return _fibconfig.eventloop(); } /** * 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 process_fib_changes(const list& fte_list); /** * Add an IPv4 FIB client. * * @param client_target_name the target name of the client to add. * @param send_updates whether updates should be sent. * @param send_resolves whether resolve requests should be sent. * @return the XRL command error. */ XrlCmdError add_fib_client4(const string& client_target_name, const bool send_updates, const bool send_resolves); /** * Delete an IPv4 FIB client. * * @param client_target_name the target name of the client to delete. * @return the XRL command error. */ XrlCmdError delete_fib_client4(const string& client_target_name); /** * Send an XRL to a FIB client to add an IPv4 route. * * @param target_name the target name of the FIB client. * @param fte the Fte with the route information to add. * @return XORP_OK on success, otherwise XORP_ERROR. * @see Fte4. */ int send_fib_client_add_route(const string& target_name, const Fte4& fte); /** * Send an XRL to a FIB client to delete an IPv4 route. * * @param target_name the target name of the FIB client. * @param fte the Fte with the route information to delete. * @return XORP_OK on success, otherwise XORP_ERROR. * @see Fte4. */ int send_fib_client_delete_route(const string& target_name, const Fte4& fte); /** * Send an XRL to a FIB client to inform it of an IPv4 route miss. * * @param target_name the target name of the FIB client. * @param fte the Fte with the destination to resolve. * @return XORP_OK on success, otherwise XORP_ERROR. * @see Fte4. */ int send_fib_client_resolve_route(const string& target_name, const Fte4& fte); #ifdef HAVE_IPV6 /** * 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 process_fib_changes(const list& fte_list); /** * Add an IPv6 FIB client. * * @param client_target_name the target name of the client to add. * @param send_updates whether updates should be sent. * @param send_resolves whether resolve requests should be sent. * @return the XRL command error. */ XrlCmdError add_fib_client6(const string& client_target_name, const bool send_updates, const bool send_resolves); /** * Delete an IPv6 FIB client. * * @param client_target_name the target name of the client to delete. * @return the XRL command error. */ XrlCmdError delete_fib_client6(const string& client_target_name); /** * Send an XRL to a FIB client to add an IPv6 route. * * @param target_name the target name of the FIB client. * @param fte the Fte with the route information to add. * @return XORP_OK on success, otherwise XORP_ERROR. * @see Fte6. */ int send_fib_client_add_route(const string& target_name, const Fte6& fte); /** * Send an XRL to a FIB client to delete an IPv6 route. * * @param target_name the target name of the FIB client. * @param fte the Fte with the route information to delete. * @return XORP_OK on success, otherwise XORP_ERROR. * @see Fte6. */ int send_fib_client_delete_route(const string& target_name, const Fte6& fte); /** * Send an XRL to a FIB client to inform it of an IPv6 route miss. * * @param target_name the target name of the FIB client. * @param fte the Fte with the destination to resolve. * @return XORP_OK on success, otherwise XORP_ERROR. * @see Fte6. */ int send_fib_client_resolve_route(const string& target_name, const Fte6& fte); #endif protected: FibConfig& _fibconfig; private: void send_fib_client_add_route4_cb(const XrlError& xrl_error, string target_name); void send_fib_client_delete_route4_cb(const XrlError& xrl_error, string target_name); void send_fib_client_resolve_route4_cb(const XrlError& xrl_error, string target_name); #ifdef HAVE_IPV6 void send_fib_client_delete_route6_cb(const XrlError& xrl_error, string target_name); void send_fib_client_add_route6_cb(const XrlError& xrl_error, string target_name); void send_fib_client_resolve_route6_cb(const XrlError& xrl_error, string target_name); #endif /** * A template class for storing FIB client information. */ template class FibClient { public: FibClient(const string& target_name, XrlFibClientManager& xfcm) : _target_name(target_name), _xfcm(&xfcm), _send_updates(false), _send_resolves(false) {} FibClient() { _xfcm = NULL; } FibClient& operator=(const FibClient& rhs) { if (this != &rhs) { _inform_fib_client_queue = rhs._inform_fib_client_queue; _inform_fib_client_queue_timer = rhs._inform_fib_client_queue_timer; _target_name = rhs._target_name; _send_updates = rhs._send_updates; _send_resolves = rhs._send_resolves; } return *this; } void activate(const list& fte_list); void send_fib_client_route_change_cb(const XrlError& xrl_error); bool get_send_updates() const { return _send_updates; } bool get_send_resolves() const { return _send_resolves; } void set_send_updates(const bool sendit) { _send_updates = sendit; } void set_send_resolves(const bool sendit) { _send_resolves = sendit; } private: EventLoop& eventloop() { return _xfcm->eventloop(); } void send_fib_client_route_change(); list _inform_fib_client_queue; XorpTimer _inform_fib_client_queue_timer; string _target_name; // Target name of the client XrlFibClientManager* _xfcm; bool _send_updates; // Event filters bool _send_resolves; }; typedef FibClient FibClient4; map _fib_clients4; #ifdef HAVE_IPV6 typedef FibClient FibClient6; map _fib_clients6; #endif XrlFeaFibClientV0p1Client _xrl_fea_fib_client; }; #endif // __FEA_XRL_FIB_CLIENT_MANAGER_HH__ xorp/fea/mfea_kernel_messages.hh0000664000076400007640000000361511421137511017074 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/mfea_kernel_messages.hh,v 1.9 2008/10/02 21:56:49 bms Exp $ #ifndef __FEA_MFEA_KERNEL_MESSAGES_HH__ #define __FEA_MFEA_KERNEL_MESSAGES_HH__ // // The header file for defining various kernel signal message types. // // Those messages are typically sent from the Multicast FEA to the // multicast routing protocols. // // // Constants definitions // // // The types of the kernel multicast signal messages // // Note that MFEA_KERNEL_MESSAGE_BW_UPCALL is not sent by the // Mfea to the protocol instances, because it is not always supported // by the kernel. Therefore, the Mfea implements a work-around mechanism // at user-level, and it uses separate channel to propagate // this information to the protocol instances. // #define MFEA_KERNEL_MESSAGE_NOCACHE 1 #define MFEA_KERNEL_MESSAGE_WRONGVIF 2 #define MFEA_KERNEL_MESSAGE_WHOLEPKT 3 #define MFEA_KERNEL_MESSAGE_BW_UPCALL 4 // // Structures, typedefs and macros // // // // Global variables // // // Global functions prototypes // #endif // __FEA_MFEA_KERNEL_MESSAGES_HH__ xorp/fea/xrl_mfea_node.cc0000664000076400007640000011417111540225525015532 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "mfea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvx.hh" #include "libxorp/status_codes.h" #include "mfea_node.hh" #include "mfea_node_cli.hh" #include "mfea_kernel_messages.hh" #include "mfea_vif.hh" #include "xrl_mfea_node.hh" // // XrlMfeaNode front-end interface // XrlMfeaNode::XrlMfeaNode(FeaNode& fea_node, int family, xorp_module_id module_id, EventLoop& eventloop, const string& class_name, const string& finder_hostname, uint16_t finder_port, const string& finder_target) : MfeaNode(fea_node, family, module_id, eventloop), XrlStdRouter(eventloop, class_name.c_str(), finder_hostname.c_str(), finder_port), XrlMfeaTargetBase(&xrl_router()), MfeaNodeCli(*static_cast(this)), _eventloop(eventloop), _finder_target(finder_target), _xrl_mfea_client_client(&xrl_router()), _xrl_cli_manager_client(&xrl_router()), _xrl_finder_client(&xrl_router()), _lib_mfea_client_bridge(xrl_router(), MfeaNode::mfea_iftree_update_replicator()), _is_finder_alive(false) { } XrlMfeaNode::~XrlMfeaNode() { shutdown(); } int XrlMfeaNode::startup() { if (start_mfea() != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int XrlMfeaNode::shutdown() { int ret_value = XORP_OK; if (stop_cli() != XORP_OK) ret_value = XORP_ERROR; if (stop_mfea() != XORP_OK) ret_value = XORP_ERROR; return (ret_value); } int XrlMfeaNode::enable_cli() { MfeaNodeCli::enable(); return (XORP_OK); } int XrlMfeaNode::disable_cli() { MfeaNodeCli::disable(); return (XORP_OK); } int XrlMfeaNode::start_cli() { if (MfeaNodeCli::start() != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int XrlMfeaNode::stop_cli() { if (MfeaNodeCli::stop() != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int XrlMfeaNode::enable_mfea() { MfeaNode::enable(); return (XORP_OK); } int XrlMfeaNode::disable_mfea() { MfeaNode::disable(); return (XORP_OK); } int XrlMfeaNode::start_mfea() { if (MfeaNode::start() != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int XrlMfeaNode::stop_mfea() { int ret_code = XORP_OK; if (MfeaNode::stop() != XORP_OK) return (XORP_ERROR); return (ret_code); } // // Finder-related events // /** * Called when Finder connection is established. * * Note that this method overwrites an XrlRouter virtual method. */ void XrlMfeaNode::finder_connect_event() { _is_finder_alive = true; } /** * Called when Finder disconnect occurs. * * Note that this method overwrites an XrlRouter virtual method. */ void XrlMfeaNode::finder_disconnect_event() { XLOG_ERROR("Finder disconnect event. Exiting immediately..."); _is_finder_alive = false; stop_mfea(); } // // Protocol node methods // /** * XrlMfeaNode::signal_message_send: * @dst_module_instance name: The name of the protocol instance-destination * of the message. * @message_type: The message type of the kernel signal. * At this moment, one of the following: * %MFEA_KERNEL_MESSAGE_NOCACHE (if a cache-miss in the kernel) * %MFEA_KERNEL_MESSAGE_WRONGVIF (multicast packet received on wrong vif) * %MFEA_KERNEL_MESSAGE_WHOLEPKT (typically, a packet that should be * encapsulated as a PIM-Register). * %MFEA_KERNEL_MESSAGE_BW_UPCALL (the bandwidth of a predefined * source-group flow is above or below a given threshold). * (XXX: The above types correspond to %IGMPMSG_* or %MRT6MSG_*). * @vif_index: The vif index of the related interface (message-specific * relation). * @src: The source address in the message. * @dst: The destination address in the message. * @sndbuf: The data buffer with the additional information in the message. * @sndlen: The data length in @sndbuf. * * Send a kernel signal to an user-level protocol that expects it. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int XrlMfeaNode::signal_message_send(const string& dst_module_instance_name, int message_type, uint32_t vif_index, const IPvX& src, const IPvX& dst, const uint8_t *sndbuf, size_t sndlen) { MfeaVif *mfea_vif = MfeaNode::vif_find_by_vif_index(vif_index); if (! _is_finder_alive) return (XORP_ERROR); // The Finder is dead if (mfea_vif == NULL) { XLOG_ERROR("Cannot send a kernel signal message on vif with vif_index %d: " "no such vif", vif_index); return (XORP_ERROR); } // Copy 'sndbuf' to a vector vector snd_vector; snd_vector.resize(sndlen); for (size_t i = 0; i < sndlen; i++) snd_vector[i] = sndbuf[i]; do { if (dst.is_ipv4()) { _xrl_mfea_client_client.send_recv_kernel_signal_message4( dst_module_instance_name.c_str(), xrl_router().class_name(), message_type, mfea_vif->name(), vif_index, src.get_ipv4(), dst.get_ipv4(), snd_vector, callback(this, &XrlMfeaNode::mfea_client_client_send_recv_kernel_signal_message_cb)); break; } #ifdef HAVE_IPV6 if (dst.is_ipv6()) { _xrl_mfea_client_client.send_recv_kernel_signal_message6( dst_module_instance_name.c_str(), xrl_router().class_name(), message_type, mfea_vif->name(), vif_index, src.get_ipv6(), dst.get_ipv6(), snd_vector, callback(this, &XrlMfeaNode::mfea_client_client_send_recv_kernel_signal_message_cb)); break; } #endif XLOG_UNREACHABLE(); break; } while(false); return (XORP_OK); } // // Misc. callback functions for setup a vif. // TODO: those are almost identical, and should be replaced by a single // function. // void XrlMfeaNode::mfea_client_client_send_recv_kernel_signal_message_cb( const XrlError& xrl_error) { switch (xrl_error.error_code()) { case OKAY: // // If success, then we are done // break; case COMMAND_FAILED: // // If a command failed because the other side rejected it, this is // fatal. // XLOG_FATAL("Cannot send a kernel signal message to a protocol: %s", xrl_error.str().c_str()); break; case NO_FINDER: case RESOLVE_FAILED: case SEND_FAILED: // // A communication error that should have been caught elsewhere // (e.g., by tracking the status of the Finder and the other targets). // Probably we caught it here because of event reordering. // In some cases we print an error. In other cases our job is done. // XLOG_ERROR("XRL communication error: %s", xrl_error.str().c_str()); break; case BAD_ARGS: case NO_SUCH_METHOD: case INTERNAL_ERROR: // // An error that should happen only if there is something unusual: // e.g., there is XRL mismatch, no enough internal resources, etc. // We don't try to recover from such errors, hence this is fatal. // XLOG_FATAL("Fatal XRL error: %s", xrl_error.str().c_str()); break; case REPLY_TIMED_OUT: case SEND_FAILED_TRANSIENT: // // XXX: if a transient error, then don't try again. // All kernel signal messages are soft-state // (i.e., they are retransmitted as appropriate by the kernel), // hence we don't retransmit them here if there was an error. // XLOG_ERROR("Failed to send a kernel signal message to a protocol: %s", xrl_error.str().c_str()); break; } } int XrlMfeaNode::dataflow_signal_send(const string& dst_module_instance_name, const IPvX& source_addr, const IPvX& group_addr, uint32_t threshold_interval_sec, uint32_t threshold_interval_usec, uint32_t measured_interval_sec, uint32_t measured_interval_usec, 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) { if (! _is_finder_alive) return (XORP_ERROR); // The Finder is dead do { if (source_addr.is_ipv4()) { _xrl_mfea_client_client.send_recv_dataflow_signal4( dst_module_instance_name.c_str(), xrl_router().class_name(), source_addr.get_ipv4(), group_addr.get_ipv4(), 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, callback(this, &XrlMfeaNode::mfea_client_client_send_recv_dataflow_signal_cb)); break; } #ifdef HAVE_IPV6 if (source_addr.is_ipv6()) { _xrl_mfea_client_client.send_recv_dataflow_signal6( dst_module_instance_name.c_str(), xrl_router().class_name(), source_addr.get_ipv6(), group_addr.get_ipv6(), 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, callback(this, &XrlMfeaNode::mfea_client_client_send_recv_dataflow_signal_cb)); break; } XLOG_UNREACHABLE(); #endif break; } while (false); return (XORP_OK); } void XrlMfeaNode::mfea_client_client_send_recv_dataflow_signal_cb( const XrlError& xrl_error) { switch (xrl_error.error_code()) { case OKAY: // // If success, then we are done // break; case COMMAND_FAILED: // // If a command failed because the other side rejected it, this is // fatal. // XLOG_FATAL("Cannot send recv_dataflow_signal to a protocol: %s", xrl_error.str().c_str()); break; case NO_FINDER: case RESOLVE_FAILED: case SEND_FAILED: // // A communication error that should have been caught elsewhere // (e.g., by tracking the status of the Finder and the other targets). // Probably we caught it here because of event reordering. // In some cases we print an error. In other cases our job is done. // XLOG_ERROR("XRL communication error: %s", xrl_error.str().c_str()); break; case BAD_ARGS: case NO_SUCH_METHOD: case INTERNAL_ERROR: // // An error that should happen only if there is something unusual: // e.g., there is XRL mismatch, no enough internal resources, etc. // We don't try to recover from such errors, hence this is fatal. // XLOG_FATAL("Fatal XRL error: %s", xrl_error.str().c_str()); break; case REPLY_TIMED_OUT: case SEND_FAILED_TRANSIENT: // // XXX: if a transient error, then don't try again. // All dataflow signals are soft-state (i.e., they are // retransmitted periodically by the originator), // hence we don't retransmit them here if there was an error. // XLOG_ERROR("Failed to send recv_dataflow_signal to a protocol: %s", xrl_error.str().c_str()); break; } } // // Protocol node CLI methods // int XrlMfeaNode::add_cli_command_to_cli_manager(const char *command_name, const char *command_help, bool is_command_cd, const char *command_cd_prompt, bool is_command_processor ) { bool success = true; if (! _is_finder_alive) return (XORP_ERROR); // The Finder is dead success = _xrl_cli_manager_client.send_add_cli_command( xorp_module_name(family(), XORP_MODULE_CLI), xrl_router().class_name(), string(command_name), string(command_help), is_command_cd, string(command_cd_prompt), is_command_processor, callback(this, &XrlMfeaNode::cli_manager_client_send_add_cli_command_cb)); if (! success) { XLOG_ERROR("Failed to add CLI command '%s' to the CLI manager", command_name); return (XORP_ERROR); } return (XORP_OK); } void XrlMfeaNode::cli_manager_client_send_add_cli_command_cb( const XrlError& xrl_error) { switch (xrl_error.error_code()) { case OKAY: // // If success, then we are done // break; case COMMAND_FAILED: // // If a command failed because the other side rejected it, this is // fatal. // XLOG_FATAL("Cannot add a command to CLI manager: %s", xrl_error.str().c_str()); break; case NO_FINDER: case RESOLVE_FAILED: case SEND_FAILED: // // A communication error that should have been caught elsewhere // (e.g., by tracking the status of the Finder and the other targets). // Probably we caught it here because of event reordering. // In some cases we print an error. In other cases our job is done. // XLOG_ERROR("Cannot add a command to CLI manager: %s", xrl_error.str().c_str()); break; case BAD_ARGS: case NO_SUCH_METHOD: case INTERNAL_ERROR: // // An error that should happen only if there is something unusual: // e.g., there is XRL mismatch, no enough internal resources, etc. // We don't try to recover from such errors, hence this is fatal. // XLOG_FATAL("Fatal XRL error: %s", xrl_error.str().c_str()); break; case REPLY_TIMED_OUT: case SEND_FAILED_TRANSIENT: // // If a transient error, then start a timer to try again // (unless the timer is already running). // // // TODO: if the command failed, then we should retransmit it // XLOG_ERROR("Failed to add a command to CLI manager: %s", xrl_error.str().c_str()); break; } } int XrlMfeaNode::delete_cli_command_from_cli_manager(const char *command_name) { bool success = true; if (! _is_finder_alive) return (XORP_ERROR); // The Finder is dead success = _xrl_cli_manager_client.send_delete_cli_command( xorp_module_name(family(), XORP_MODULE_CLI), xrl_router().class_name(), string(command_name), callback(this, &XrlMfeaNode::cli_manager_client_send_delete_cli_command_cb)); if (! success) { XLOG_ERROR("Failed to delete CLI command '%s' with the CLI manager", command_name); return (XORP_ERROR); } return (XORP_OK); } void XrlMfeaNode::cli_manager_client_send_delete_cli_command_cb( const XrlError& xrl_error) { switch (xrl_error.error_code()) { case OKAY: // // If success, then we are done // break; case COMMAND_FAILED: // // If a command failed because the other side rejected it, this is // fatal. // XLOG_FATAL("Cannot delete a command from CLI manager: %s", xrl_error.str().c_str()); break; case NO_FINDER: case RESOLVE_FAILED: case SEND_FAILED: // // A communication error that should have been caught elsewhere // (e.g., by tracking the status of the Finder and the other targets). // Probably we caught it here because of event reordering. // In some cases we print an error. In other cases our job is done. // XLOG_ERROR("Cannot delete a command from CLI manager: %s", xrl_error.str().c_str()); break; case BAD_ARGS: case NO_SUCH_METHOD: case INTERNAL_ERROR: // // An error that should happen only if there is something unusual: // e.g., there is XRL mismatch, no enough internal resources, etc. // We don't try to recover from such errors, hence this is fatal. // XLOG_FATAL("Fatal XRL error: %s", xrl_error.str().c_str()); break; case REPLY_TIMED_OUT: case SEND_FAILED_TRANSIENT: // // If a transient error, then start a timer to try again // (unless the timer is already running). // // // TODO: if the command failed, then we should retransmit it // XLOG_ERROR("Failed to delete a command from CLI manager: %s", xrl_error.str().c_str()); break; } } // // XRL target methods // XrlCmdError XrlMfeaNode::common_0_1_get_target_name( // Output values, string& name) { name = xrl_router().class_name(); return XrlCmdError::OKAY(); } XrlCmdError XrlMfeaNode::common_0_1_get_version( // Output values, string& version) { version = XORP_MODULE_VERSION; return XrlCmdError::OKAY(); } XrlCmdError XrlMfeaNode::common_0_1_get_status( // Output values, uint32_t& status, string& reason) { status = MfeaNode::node_status(reason); return XrlCmdError::OKAY(); } XrlCmdError XrlMfeaNode::common_0_1_shutdown() { string error_msg; if (shutdown() != XORP_OK) { error_msg = c_format("Failed to shutdown MFEA"); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } /** * Announce target birth to observer. * * @param target_class the target class name. * * @param target_instance the target instance name. */ XrlCmdError XrlMfeaNode::finder_event_observer_0_1_xrl_target_birth( // Input values, const string& target_class, const string& target_instance) { UNUSED(target_class); UNUSED(target_instance); return XrlCmdError::OKAY(); } /** * Announce target death to observer. * * @param target_class the target class name. * * @param target_instance the target instance name. */ XrlCmdError XrlMfeaNode::finder_event_observer_0_1_xrl_target_death( // Input values, const string& target_class, const string& target_instance) { UNUSED(target_class); UNUSED(target_instance); return XrlCmdError::OKAY(); } XrlCmdError XrlMfeaNode::cli_processor_0_1_process_command( // Input values, const string& processor_name, const string& cli_term_name, const uint32_t& cli_session_id, const string& command_name, const string& command_args, // Output values, string& ret_processor_name, string& ret_cli_term_name, uint32_t& ret_cli_session_id, string& ret_command_output) { MfeaNodeCli::cli_process_command(processor_name, cli_term_name, cli_session_id, command_name, command_args, ret_processor_name, ret_cli_term_name, ret_cli_session_id, ret_command_output); return XrlCmdError::OKAY(); } XrlCmdError XrlMfeaNode::ifmgr_replicator_0_1_register_ifmgr_mirror( // Input values, const string& clientname) { string error_msg; if (_lib_mfea_client_bridge.add_libfeaclient_mirror(clientname) != XORP_OK) { error_msg = c_format("Cannot register ifmgr mirror client %s", clientname.c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlMfeaNode::ifmgr_replicator_0_1_unregister_ifmgr_mirror( // Input values, const string& clientname) { string error_msg; if (_lib_mfea_client_bridge.remove_libfeaclient_mirror(clientname) != XORP_OK) { error_msg = c_format("Cannot unregister ifmgr mirror client %s", clientname.c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlMfeaNode::mfea_0_1_have_multicast_routing4( // Output values, bool& result) { result = MfeaNode::have_multicast_routing4(); return XrlCmdError::OKAY(); } #ifdef HAVE_IPV6 XrlCmdError XrlMfeaNode::mfea_0_1_have_multicast_routing6( // Output values, bool& result) { result = MfeaNode::have_multicast_routing6(); return XrlCmdError::OKAY(); } XrlCmdError XrlMfeaNode::mfea_0_1_register_protocol6( // Input values, const string& xrl_sender_name, const string& if_name, const string& vif_name, const uint32_t& ip_protocol) { string error_msg; // // Verify the address family // if (! MfeaNode::is_ipv6()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv6"); return XrlCmdError::COMMAND_FAILED(error_msg); } // // Register the protocol // if (MfeaNode::register_protocol(xrl_sender_name, if_name, vif_name, ip_protocol, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } // // Success // return XrlCmdError::OKAY(); } XrlCmdError XrlMfeaNode::mfea_0_1_unregister_protocol6( // Input values, const string& xrl_sender_name, const string& if_name, const string& vif_name) { string error_msg; // // Verify the address family // if (! MfeaNode::is_ipv6()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv6"); return XrlCmdError::COMMAND_FAILED(error_msg); } // // Register the protocol // if (MfeaNode::unregister_protocol(xrl_sender_name, if_name, vif_name, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } // // Success // return XrlCmdError::OKAY(); } XrlCmdError XrlMfeaNode::mfea_0_1_add_mfc6( // Input values, const string& xrl_sender_name, const IPv6& source_address, const IPv6& group_address, const uint32_t& iif_vif_index, const vector& oiflist, const vector& oiflist_disable_wrongvif, const uint32_t& max_vifs_oiflist, const IPv6& rp_address) { string error_msg; Mifset mifset; Mifset mifset_disable_wrongvif; // // Verify the address family // if (! MfeaNode::is_ipv6()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv6"); return XrlCmdError::COMMAND_FAILED(error_msg); } // Check the number of covered interfaces if (max_vifs_oiflist > mifset.size()) { // Invalid number of covered interfaces error_msg = c_format("Received 'add_mfc' with invalid " "'max_vifs_oiflist' = %u (expected <= %u)", XORP_UINT_CAST(max_vifs_oiflist), XORP_UINT_CAST(mifset.size())); return XrlCmdError::COMMAND_FAILED(error_msg); } // Get the set of outgoing interfaces vector_to_mifset(oiflist, mifset); // Get the set of interfaces to disable the WRONGVIF signal. vector_to_mifset(oiflist_disable_wrongvif, mifset_disable_wrongvif); if (MfeaNode::add_mfc(xrl_sender_name, IPvX(source_address), IPvX(group_address), iif_vif_index, mifset, mifset_disable_wrongvif, max_vifs_oiflist, IPvX(rp_address)) != XORP_OK) { // TODO: must find-out and return the reason for failure error_msg = c_format("Cannot add MFC for " "source %s and group %s " "with iif_vif_index = %u", source_address.str().c_str(), group_address.str().c_str(), XORP_UINT_CAST(iif_vif_index)); return XrlCmdError::COMMAND_FAILED(error_msg); } // // Success // return XrlCmdError::OKAY(); } XrlCmdError XrlMfeaNode::mfea_0_1_delete_mfc6( // Input values, const string& xrl_sender_name, const IPv6& source_address, const IPv6& group_address) { string error_msg; // // Verify the address family // if (! MfeaNode::is_ipv6()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv6"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (MfeaNode::delete_mfc(xrl_sender_name, IPvX(source_address), IPvX(group_address)) != XORP_OK) { // TODO: must find-out and return the reason for failure error_msg = c_format("Cannot delete MFC for " "source %s and group %s", source_address.str().c_str(), group_address.str().c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } // // Success // return XrlCmdError::OKAY(); } XrlCmdError XrlMfeaNode::mfea_0_1_add_dataflow_monitor6( // Input values, const string& xrl_sender_name, const IPv6& source_address, const IPv6& group_address, const uint32_t& threshold_interval_sec, const uint32_t& threshold_interval_usec, const uint32_t& threshold_packets, const uint32_t& threshold_bytes, const bool& is_threshold_in_packets, const bool& is_threshold_in_bytes, const bool& is_geq_upcall, const bool& is_leq_upcall) { string error_msg; // // Verify the address family // if (! MfeaNode::is_ipv6()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv6"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (MfeaNode::add_dataflow_monitor(xrl_sender_name, IPvX(source_address), IPvX(group_address), TimeVal(threshold_interval_sec, threshold_interval_usec), threshold_packets, threshold_bytes, is_threshold_in_packets, is_threshold_in_bytes, is_geq_upcall, is_leq_upcall, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } // // Success // return XrlCmdError::OKAY(); } XrlCmdError XrlMfeaNode::mfea_0_1_delete_dataflow_monitor6( // Input values, const string& xrl_sender_name, const IPv6& source_address, const IPv6& group_address, const uint32_t& threshold_interval_sec, const uint32_t& threshold_interval_usec, const uint32_t& threshold_packets, const uint32_t& threshold_bytes, const bool& is_threshold_in_packets, const bool& is_threshold_in_bytes, const bool& is_geq_upcall, const bool& is_leq_upcall) { string error_msg; // // Verify the address family // if (! MfeaNode::is_ipv6()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv6"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (MfeaNode::delete_dataflow_monitor(xrl_sender_name, IPvX(source_address), IPvX(group_address), TimeVal(threshold_interval_sec, threshold_interval_usec), threshold_packets, threshold_bytes, is_threshold_in_packets, is_threshold_in_bytes, is_geq_upcall, is_leq_upcall, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } // // Success // return XrlCmdError::OKAY(); } XrlCmdError XrlMfeaNode::mfea_0_1_delete_all_dataflow_monitor6( // Input values, const string& xrl_sender_name, const IPv6& source_address, const IPv6& group_address) { string error_msg; // // Verify the address family // if (! MfeaNode::is_ipv6()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv6"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (MfeaNode::delete_all_dataflow_monitor(xrl_sender_name, IPvX(source_address), IPvX(group_address), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } // // Success // return XrlCmdError::OKAY(); } #endif //ipv6 XrlCmdError XrlMfeaNode::mfea_0_1_register_protocol4( // Input values, const string& xrl_sender_name, const string& if_name, const string& vif_name, const uint32_t& ip_protocol) { string error_msg; // // Verify the address family // if (! MfeaNode::is_ipv4()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv4"); return XrlCmdError::COMMAND_FAILED(error_msg); } // // Register the protocol // if (MfeaNode::register_protocol(xrl_sender_name, if_name, vif_name, ip_protocol, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } // // Success // return XrlCmdError::OKAY(); } XrlCmdError XrlMfeaNode::mfea_0_1_unregister_protocol4( // Input values, const string& xrl_sender_name, const string& if_name, const string& vif_name) { string error_msg; // // Verify the address family // if (! MfeaNode::is_ipv4()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv4"); return XrlCmdError::COMMAND_FAILED(error_msg); } // // Register the protocol // if (MfeaNode::unregister_protocol(xrl_sender_name, if_name, vif_name, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } // // Success // return XrlCmdError::OKAY(); } XrlCmdError XrlMfeaNode::mfea_0_1_add_mfc4( // Input values, const string& xrl_sender_name, const IPv4& source_address, const IPv4& group_address, const uint32_t& iif_vif_index, const vector& oiflist, const vector& oiflist_disable_wrongvif, const uint32_t& max_vifs_oiflist, const IPv4& rp_address) { string error_msg; Mifset mifset; Mifset mifset_disable_wrongvif; // // Verify the address family // if (! MfeaNode::is_ipv4()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv4"); return XrlCmdError::COMMAND_FAILED(error_msg); } // Check the number of covered interfaces if (max_vifs_oiflist > mifset.size()) { // Invalid number of covered interfaces error_msg = c_format("Received 'add_mfc' with invalid " "'max_vifs_oiflist' = %u (expected <= %u)", XORP_UINT_CAST(max_vifs_oiflist), XORP_UINT_CAST(mifset.size())); return XrlCmdError::COMMAND_FAILED(error_msg); } // Get the set of outgoing interfaces vector_to_mifset(oiflist, mifset); // Get the set of interfaces to disable the WRONGVIF signal. vector_to_mifset(oiflist_disable_wrongvif, mifset_disable_wrongvif); if (MfeaNode::add_mfc(xrl_sender_name, IPvX(source_address), IPvX(group_address), iif_vif_index, mifset, mifset_disable_wrongvif, max_vifs_oiflist, IPvX(rp_address)) != XORP_OK) { // TODO: must find-out and return the reason for failure error_msg = c_format("Cannot add MFC for " "source %s and group %s " "with iif_vif_index = %u", source_address.str().c_str(), group_address.str().c_str(), XORP_UINT_CAST(iif_vif_index)); return XrlCmdError::COMMAND_FAILED(error_msg); } // // Success // return XrlCmdError::OKAY(); } XrlCmdError XrlMfeaNode::mfea_0_1_delete_mfc4( // Input values, const string& xrl_sender_name, const IPv4& source_address, const IPv4& group_address) { string error_msg; // // Verify the address family // if (! MfeaNode::is_ipv4()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv4"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (MfeaNode::delete_mfc(xrl_sender_name, IPvX(source_address), IPvX(group_address)) != XORP_OK) { // TODO: must find-out and return the reason for failure error_msg = c_format("Cannot delete MFC for " "source %s and group %s", source_address.str().c_str(), group_address.str().c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } // // Success // return XrlCmdError::OKAY(); } XrlCmdError XrlMfeaNode::mfea_0_1_add_dataflow_monitor4( // Input values, const string& xrl_sender_name, const IPv4& source_address, const IPv4& group_address, const uint32_t& threshold_interval_sec, const uint32_t& threshold_interval_usec, const uint32_t& threshold_packets, const uint32_t& threshold_bytes, const bool& is_threshold_in_packets, const bool& is_threshold_in_bytes, const bool& is_geq_upcall, const bool& is_leq_upcall) { string error_msg; // // Verify the address family // if (! MfeaNode::is_ipv4()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv4"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (MfeaNode::add_dataflow_monitor(xrl_sender_name, IPvX(source_address), IPvX(group_address), TimeVal(threshold_interval_sec, threshold_interval_usec), threshold_packets, threshold_bytes, is_threshold_in_packets, is_threshold_in_bytes, is_geq_upcall, is_leq_upcall, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } // // Success // return XrlCmdError::OKAY(); } XrlCmdError XrlMfeaNode::mfea_0_1_delete_dataflow_monitor4( // Input values, const string& xrl_sender_name, const IPv4& source_address, const IPv4& group_address, const uint32_t& threshold_interval_sec, const uint32_t& threshold_interval_usec, const uint32_t& threshold_packets, const uint32_t& threshold_bytes, const bool& is_threshold_in_packets, const bool& is_threshold_in_bytes, const bool& is_geq_upcall, const bool& is_leq_upcall) { string error_msg; // // Verify the address family // if (! MfeaNode::is_ipv4()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv4"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (MfeaNode::delete_dataflow_monitor(xrl_sender_name, IPvX(source_address), IPvX(group_address), TimeVal(threshold_interval_sec, threshold_interval_usec), threshold_packets, threshold_bytes, is_threshold_in_packets, is_threshold_in_bytes, is_geq_upcall, is_leq_upcall, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } // // Success // return XrlCmdError::OKAY(); } XrlCmdError XrlMfeaNode::mfea_0_1_delete_all_dataflow_monitor4( // Input values, const string& xrl_sender_name, const IPv4& source_address, const IPv4& group_address) { string error_msg; // // Verify the address family // if (! MfeaNode::is_ipv4()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv4"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (MfeaNode::delete_all_dataflow_monitor(xrl_sender_name, IPvX(source_address), IPvX(group_address), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } // // Success // return XrlCmdError::OKAY(); } XrlCmdError XrlMfeaNode::mfea_0_1_enable_vif( // Input values, const string& vif_name, const bool& enable) { string error_msg; int ret_code; if (enable) ret_code = MfeaNode::enable_vif(vif_name, error_msg); else ret_code = MfeaNode::disable_vif(vif_name, error_msg); if (ret_code != XORP_OK) { XLOG_ERROR("Mfea, enable/disable vif failed. Allowing commit to succeed anyway since this is likely a race with a deleted interface, error: %s\n", error_msg.c_str()); //return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlMfeaNode::mfea_0_1_start_vif( // Input values, const string& vif_name) { string error_msg; if (MfeaNode::start_vif(vif_name, error_msg) != XORP_OK) return XrlCmdError::COMMAND_FAILED(error_msg); return XrlCmdError::OKAY(); } XrlCmdError XrlMfeaNode::mfea_0_1_stop_vif( // Input values, const string& vif_name) { string error_msg; if (MfeaNode::stop_vif(vif_name, error_msg) != XORP_OK) return XrlCmdError::COMMAND_FAILED(error_msg); return XrlCmdError::OKAY(); } XrlCmdError XrlMfeaNode::mfea_0_1_enable_all_vifs( // Input values, const bool& enable) { string error_msg; int ret_code; if (enable) ret_code = MfeaNode::enable_all_vifs(); else ret_code = MfeaNode::disable_all_vifs(); if (ret_code != XORP_OK) { if (enable) error_msg = c_format("Failed to enable all vifs"); else error_msg = c_format("Failed to disable all vifs"); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlMfeaNode::mfea_0_1_start_all_vifs() { string error_msg; if (MfeaNode::start_all_vifs() != XORP_OK) { error_msg = c_format("Failed to start all vifs"); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlMfeaNode::mfea_0_1_stop_all_vifs() { string error_msg; if (MfeaNode::stop_all_vifs() != XORP_OK) { error_msg = c_format("Failed to stop all vifs"); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlMfeaNode::mfea_0_1_enable_mfea( // Input values, const bool& enable) { string error_msg; int ret_value; if (enable) ret_value = enable_mfea(); else ret_value = disable_mfea(); if (ret_value != XORP_OK) { if (enable) error_msg = c_format("Failed to enable MFEA"); else error_msg = c_format("Failed to disable MFEA"); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlMfeaNode::mfea_0_1_start_mfea() { string error_msg; if (start_mfea() != XORP_OK) { error_msg = c_format("Failed to start MFEA"); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlMfeaNode::mfea_0_1_stop_mfea() { string error_msg; if (stop_mfea() != XORP_OK) { error_msg = c_format("Failed to stop MFEA"); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlMfeaNode::mfea_0_1_enable_cli( // Input values, const bool& enable) { string error_msg; int ret_value; if (enable) ret_value = enable_cli(); else ret_value = disable_cli(); if (ret_value != XORP_OK) { if (enable) error_msg = c_format("Failed to enable MFEA CLI"); else error_msg = c_format("Failed to disable MFEA CLI"); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlMfeaNode::mfea_0_1_start_cli() { string error_msg; if (start_cli() != XORP_OK) { error_msg = c_format("Failed to start MFEA CLI"); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlMfeaNode::mfea_0_1_stop_cli() { string error_msg; if (stop_cli() != XORP_OK) { error_msg = c_format("Failed to start MFEA CLI"); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlMfeaNode::mfea_0_1_log_trace_all( // Input values, const bool& enable) { MfeaNode::set_log_trace(enable); return XrlCmdError::OKAY(); } xorp/fea/SConscript0000664000076400007640000001571611631505405014437 0ustar greearbgreearb# Copyright (c) 2009-2011 XORP, Inc and Others # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # $XORP$ import os Import('env') subdirs = [ 'data_plane', 'tests', 'tools', ] SConscript(dirs = subdirs, exports='env') env = env.Clone() is_shared = env.has_key('SHAREDLIBS') env.AppendUnique(CPPPATH = [ '#', '$BUILDDIR', ]) env.PrependUnique(LIBPATH = [ '$BUILDDIR/fea', '$BUILDDIR/fea/data_plane/managers', '$BUILDDIR/fea/data_plane/fibconfig', '$BUILDDIR/fea/data_plane/ifconfig', '$BUILDDIR/fea/data_plane/io', '$BUILDDIR/fea/data_plane/control_socket', '.', '$BUILDDIR/mrt', '$BUILDDIR/cli', '$BUILDDIR/cli/libtecla', '$BUILDDIR/xrl/interfaces', '$BUILDDIR/xrl/targets', '$BUILDDIR/libfeaclient', '$BUILDDIR/libproto', '$BUILDDIR/libxipc', '$BUILDDIR/libxorp', '$BUILDDIR/libcomm', ]) libxfdp_srcs = [ 'fea_data_plane_manager.cc', 'fibconfig_forwarding.cc', 'ifconfig_property.cc', 'iftree.cc', 'io_ip.cc', 'io_link.cc', 'io_tcpudp.cc', ] libxorp_fea_linkorder = [ 'xorp_fea_data_plane_managers', 'xorp_fea_fibconfig', 'xorp_fea_ifconfig', 'xorp_fea_io', 'xorp_fea_control_socket', ] if not is_shared: libxorp_fea_linkorder.append('libxorp_fea_data_plane_base') libxorp_fea_linkorder += [ 'xif_fea_fib_client', 'xif_fea_rawlink_client', 'xif_fea_rawpkt4_client', 'xif_socket4_user', 'xif_finder_event_notifier', 'xorp_cli', 'xif_cli_processor', 'xorp_fea_client', 'xif_fea_ifmgr_mirror', # XXX? 'xif_fea_ifmgr_replicator', # XXX? 'xst_fea_ifmgr_mirror', # XXX? 'xif_cli_manager', 'xif_mfea_client', 'xst_cli', 'xst_fea', 'xst_mfea', 'xorp_mrt', 'xorp_proto', 'xorp_ipc', 'xorp_comm', 'xorp_core' ] if not (env.has_key('disable_libtecla') and env['disable_libtecla']): libxorp_fea_linkorder += [ 'xorp_tecla', ] else: # External tecla library libxorp_fea_linkorder += [ 'tecla', ] if not (env.has_key('disable_ipv6') and env['disable_ipv6']): libxorp_fea_linkorder += [ 'xif_socket6_user', 'xif_fea_rawpkt6_client', ] if not (env.has_key('disable_fw') and env['disable_fw']): env.PrependUnique(LIBPATH = [ '$BUILDDIR/fea/data_plane/firewall' ]) libxorp_fea_linkorder += [ 'xorp_fea_firewall' ] if not (env.has_key('disable_profile') and env['disable_profile']): libxorp_fea_linkorder += [ 'xif_profile_client' ] # Internal libraries. # Note special use of conditional above. env.PrependUnique(LIBS = libxorp_fea_linkorder) # External libraries. if env.has_key('has_libpcap') and env['has_libpcap']: env.AppendUnique(LIBS = [ 'pcap' ]) # Report unresolved symbol references when building the FEA. if is_shared: env.AppendUnique(LINKFLAGS = [ '-Wl,-z,defs', ]) else: env.AppendUnique(LIBS = [ "crypto", ]) if not (env.has_key('mingw') and env['mingw']): env.AppendUnique(LIBS = [ "rt", ]) if (env.has_key('mingw') and env['mingw']): env.AppendUnique(LIBS = [ 'ws2_32', 'iphlpapi', 'mprapi', 'regex', 'winmm', ]) libxorp_fea_srcs = [ 'fea_io.cc', 'fea_node.cc', 'fibconfig.cc', 'fibconfig_transaction.cc', 'ifconfig.cc', 'ifconfig_reporter.cc', 'ifconfig_transaction.cc', 'io_ip_manager.cc', 'io_link_manager.cc', 'io_tcpudp_manager.cc', 'libfeaclient_bridge.cc', 'mfea_config.cc', 'mfea_dataflow.cc', 'mfea_mrouter.cc', 'mfea_node.cc', 'mfea_node_cli.cc', 'mfea_vif.cc', 'nexthop_port_mapper.cc', 'xrl_fea_io.cc', 'xrl_fea_node.cc', 'xrl_fea_target.cc', 'xrl_fib_client_manager.cc', 'xrl_io_ip_manager.cc', 'xrl_io_link_manager.cc', 'xrl_io_tcpudp_manager.cc', 'xrl_mfea_node.cc' ] if not (env.has_key('disable_fw') and env['disable_fw']): libxorp_fea_srcs += [ 'firewall_entry.cc', 'firewall_manager.cc', 'firewall_transaction.cc', ] if not (env.has_key('disable_profile') and env['disable_profile']): libxorp_fea_srcs += [ 'profile_vars.cc' ] env.Replace(RPATH = [ env.Literal(env['xorp_module_rpath']) ]) if is_shared: libxorp_fea_srcs += libxfdp_srcs libxorp_fea = env.SharedLibrary(target = 'libxorp_fea', source = libxorp_fea_srcs) if env['rtld_origin']: for obj in libxorp_fea: env.AddPostAction(libxorp_fea, env.Symlink(obj.abspath, os.path.join(env['xorp_alias_libdir'], str(obj)))) env.Alias('install', env.InstallLibrary(env['xorp_libdir'], libxorp_fea)) else: # Use an intermediate static lib to deal with some dependencies. libxfdp = env.StaticLibrary(target = 'libxorp_fea_data_plane_base', source = libxfdp_srcs) env.PrependUnique(LIBS = [ libxfdp ]) libxorp_fea = env.StaticLibrary(target = 'libxorp_fea', source = libxorp_fea_srcs) ####################### env = env.Clone() if is_shared: env.PrependUnique(LIBS = [ 'xorp_fea', # -lxorp_fea, not the xorp_fea executable ]) else: env.PrependUnique(LIBS = [ libxorp_fea ]) feasrcs = [ 'xorp_fea.cc', ] fea = env.Program(target = 'xorp_fea', source = feasrcs) env.Alias('install', env.InstallProgram(env['xorp_moduledir'], fea)) ####################### if env['enable_fea_dummy']: env = env.Clone() env['OBJPREFIX'] = 'dummy-' env.AppendUnique(CPPDEFINES = 'FEA_DUMMY=1') feadummysrcs = [ 'xorp_fea.cc', ] feadummy = env.Program(target = 'xorp_fea_dummy', source = feadummysrcs) env.Alias('install', env.InstallProgram(env['xorp_moduledir'], feadummy)) ####################### if env['enable_click']: clickgen = 'fea_click_config_generator' env.Alias('install', env.InstallScript(env['xorp_tooldir'], clickgen)) ####################### # Install scripts env.Alias('install', env.InstallScript('$exec_prefix/sbin/', env.Entry('fea_xrl_shell_funcs.sh'))) if env['enable_fea_dummy']: Default(libxorp_fea, fea, feadummy) else: Default(libxorp_fea, fea) xorp/fea/io_link_manager.hh0000664000076400007640000006065411540225525016066 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2007-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/io_link_manager.hh,v 1.14 2008/10/10 01:23:53 pavlin Exp $ #ifndef __FEA_IO_LINK_MANAGER_HH__ #define __FEA_IO_LINK_MANAGER_HH__ #include "libxorp/callback.hh" #include "libxorp/mac.hh" #include "fea_io.hh" #include "io_link.hh" class FeaDataPlaneManager; class FeaNode; class IoLinkManager; /** * Structure used to store commonly passed MAC header information. */ struct MacHeaderInfo { string if_name; string vif_name; Mac src_address; Mac dst_address; uint16_t ether_type; }; /** * A class that handles raw link I/O communication for a specific protocol. * * It also allows arbitrary filters to receive the raw link-level data for that * protocol. */ class IoLinkComm : public NONCOPYABLE, public IoLinkReceiver { public: /** * Filter class. */ class InputFilter { public: InputFilter(IoLinkManager& io_link_manager, const string& receiver_name, const string& if_name, const string& vif_name, uint16_t ether_type, const string& filter_program) : _io_link_manager(io_link_manager), _receiver_name(receiver_name), _if_name(if_name), _vif_name(vif_name), _ether_type(ether_type), _filter_program(filter_program) {} virtual ~InputFilter() {} /** * Get a reference to the I/O Link manager. * * @return a reference to the I/O Link manager. */ IoLinkManager& io_link_manager() { return (_io_link_manager); } /** * Get a const reference to the I/O Link manager. * * @return a const reference to the I/O Link manager. */ const IoLinkManager& io_link_manager() const { return (_io_link_manager); } /** * Get the receiver name. * * @return the receiver name. */ const string& receiver_name() const { return (_receiver_name); } /** * Get the interface name. * * @return the interface name. */ const string& if_name() const { return (_if_name); } /** * Get the vif name. * * @return the vif name. */ const string& vif_name() const { return (_vif_name); } /** * Get the EtherType protocol number. * * @return the EtherType protocol number. */ uint16_t ether_type() const { return (_ether_type); } /** * Get the filter program. * * @return the filter program. */ const string& filter_program() const { return (_filter_program); } /** * Method invoked when data arrives on associated IoLinkComm instance. */ virtual void recv(const struct MacHeaderInfo& header, const vector& payload) = 0; /** * Method invoked by the destructor of the associated IoLinkComm * instance. This method provides the InputFilter with the * opportunity to delete itself or update its state. * The input filter does not need to call IoLinkComm::remove_filter() * since filter removal is automatically conducted. */ virtual void bye() = 0; private: IoLinkManager& _io_link_manager; string _receiver_name; string _if_name; string _vif_name; uint16_t _ether_type; string _filter_program; }; /** * Joined multicast group class. */ class JoinedMulticastGroup { public: JoinedMulticastGroup(const Mac& group_address) : _group_address(group_address) {} #ifdef XORP_USE_USTL JoinedMulticastGroup() { } #endif virtual ~JoinedMulticastGroup() {} const Mac& group_address() const { return _group_address; } /** * Less-Than Operator * * @param other the right-hand operand to compare against. * @return true if the left-hand operand is numerically smaller * than the right-hand operand. */ bool operator<(const JoinedMulticastGroup& other) const { return (_group_address < other._group_address); } /** * Equality Operator * * @param other the right-hand operand to compare against. * @return true if the left-hand operand is numerically same as the * right-hand operand. */ bool operator==(const JoinedMulticastGroup& other) const { return (_group_address == other._group_address); } /** * Add a receiver. * * @param receiver_name the name of the receiver to add. */ void add_receiver(const string& receiver_name) { _receivers.insert(receiver_name); } /** * Delete a receiver. * * @param receiver_name the name of the receiver to delete. */ void delete_receiver(const string& receiver_name) { _receivers.erase(receiver_name); } /** * @return true if there are no receivers associated with this group. */ bool empty() const { return _receivers.empty(); } set& get_receivers() { return _receivers; } private: Mac _group_address; set _receivers; }; public: /** * Constructor for IoLinkComm. * * @param io_link_manager the corresponding I/O Link manager * (@ref IoLinkManager). * @param iftree the interface tree to use. * @param if_name the interface name. * @param vif_name the vif name. * @param ether_type the EtherType protocol number. * @param filter_program the optional filter program to be applied on * the received packets. */ IoLinkComm(IoLinkManager& io_link_manager, const IfTree& iftree, const string& if_name, const string& vif_name, uint16_t ether_type, const string& filter_program); /** * Virtual destructor. */ virtual ~IoLinkComm(); /** * Allocate the I/O Link plugins (one per data plane manager). */ void allocate_io_link_plugins(); /** * Deallocate the I/O Link plugins (one per data plane manager). */ void deallocate_io_link_plugins(); /** * Allocate an I/O Link plugin for a given data plane manager. * * @param fea_data_plane_manager the data plane manager. */ void allocate_io_link_plugin(FeaDataPlaneManager* fea_data_plane_manager); /** * Deallocate the I/O Link plugin for a given data plane manager. * * @param fea_data_plane_manager the data plane manager. */ void deallocate_io_link_plugin(FeaDataPlaneManager* fea_data_plane_manager); /** * Start all I/O Link plugins. */ void start_io_link_plugins(); /** * Stop all I/O Link plugins. */ void stop_io_link_plugins(); /** * Add a filter to list of input filters. * * The IoLinkComm class assumes that the callee will be responsible for * managing the memory associated with the filter and will call * remove_filter() if the filter is deleted or goes out of scope. * * @param filter the filter to add. * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_filter(InputFilter* filter); /** * Remove filter from list of input filters. * * @param filter the filter to remove. * @return XORP_OK on success, otherwise XORP_ERROR. */ int remove_filter(InputFilter* filter); /** * @return true if there are no filters associated with this instance. */ bool no_input_filters() const { return _input_filters.empty(); } /** * Send a raw link-level packet. * * @param src_address the MAC source address. * @param dst_address the MAC destination address. * @param ether_type the EtherType protocol number. * @param payload the payload, everything after the MAC header. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int send_packet(const Mac& src_address, const Mac& dst_address, uint16_t ether_type, const vector& payload, string& error_msg); /** * Received a raw link-level packet. * * @param src_address the MAC source address. * @param dst_address the MAC destination address. * @param ether_type the EtherType protocol number. * @param packet the payload, everything after the MAC header. */ virtual void recv_packet(const Mac& src_address, const Mac& dst_address, uint16_t ether_type, const vector& payload); /** * Join a MAC multicast group. * * @param group_address the multicast group address to join. * @param receiver_name the name of the receiver. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int join_multicast_group(const Mac& group_address, const string& receiver_name, string& error_msg); /** * Leave a MAC multicast group. * * @param group_address the multicast group address to leave. * @param receiver_name the name of the receiver. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int leave_multicast_group(const Mac& group_address, const string& receiver_name, string& error_msg); /** * Get the interface name. * * @return the interface name. */ const string& if_name() const { return (_if_name); } /** * Get the vif name. * * @return the vif name. */ const string& vif_name() const { return (_vif_name); } /** * Get the EtherType protocol number. * * @return the EtherType protocol number. */ uint16_t ether_type() const { return (_ether_type); } /** * Get the filter program. * * @return the filter program. */ const string& filter_program() const { return (_filter_program); } private: IoLinkComm(const IoLinkComm&); // Not implemented. IoLinkComm& operator=(const IoLinkComm&); // Not implemented. IoLinkManager& _io_link_manager; const IfTree& _iftree; const string _if_name; const string _vif_name; const uint16_t _ether_type; const string _filter_program; typedef list >IoLinkPlugins; IoLinkPlugins _io_link_plugins; list _input_filters; typedef map JoinedGroupsTable; JoinedGroupsTable _joined_groups_table; }; /** * @short Class that implements the API for sending raw link-level packet * to a receiver. */ class IoLinkManagerReceiver { public: /** * Virtual destructor. */ virtual ~IoLinkManagerReceiver() {} /** * Data received event. * * @param receiver_name the name of the receiver to send the * raw link-level packet to. * @param header the MAC header information. * @param payload the payload, everything after the MAC header. */ virtual void recv_event(const string& receiver_name, const struct MacHeaderInfo& header, const vector& payload) = 0; }; /** * @short A class that manages raw link-level I/O. * * The IoLinkManager has two containers: a container for link-level handlers * (@ref IoLinkComm) indexed by the protocol associated with the handler, and * a container for the filters associated with each receiver_name. When * a receiver registers for interest in a particular type of raw * packet a handler (@ref IoLinkComm) is created if necessary, then the * relevent filter is created and associated with the IoLinkComm. */ class IoLinkManager : public IoLinkManagerReceiver, public InstanceWatcher { public: typedef XorpCallback2::RefPtr UpcallReceiverCb; /** * Constructor for IoLinkManager. */ IoLinkManager(FeaNode& fea_node, const IfTree& iftree); /** * Virtual destructor. */ virtual ~IoLinkManager(); /** * Send a raw link-level packet on an interface. * * @param if_name the interface to send the packet on. * @param vif_name the vif to send the packet on. * @param src_address the MAC source address. * @param dst_address the MAC destination address. * @param ether_type the EtherType protocol type or the Destination SAP. * It must be between 1536 and 65535 to specify the EtherType, or between * 1 and 255 to specify the Destination SAP IEEE 802.2 LLC frames. * @param payload the payload, everything after the MAC header. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int send(const string& if_name, const string& vif_name, const Mac& src_address, const Mac& dst_address, uint16_t ether_type, const vector& payload, string& error_msg); /** * Register to receive raw link-level packets. * * @param receiver_name the name of the receiver. * @param if_name the interface through which packets should be accepted. * @param vif_name the vif through which packets should be accepted. * @param ether_type the EtherType protocol number or the Destination SAP * that the receiver is interested in. It must be between 1536 and 65535 * to specify the EtherType, or between 1 and 255 to specify the * Destination SAP for IEEE 802.2 LLC frames. A protocol number of 0 is * used to specify all protocols. * @param filter_program the optional filter program to be applied on the * received packets. The program uses tcpdump(1) style expression. * @param enable_multicast_loopback if true then enable delivering of * multicast datagrams back to this host (assuming the host is a member of * the same multicast group. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int register_receiver(const string& receiver_name, const string& if_name, const string& vif_name, uint16_t ether_type, const string& filter_program, bool enable_multicast_loopback, string& error_msg); /** * Unregister to receive raw link-level packets. * * @param receiver_name the name of the receiver. * @param if_name the interface through which packets should not be * accepted. * @param vif_name the vif through which packets should not be accepted. * @param ether_type the EtherType protocol number or the Destination SAP * that the receiver is not interested in anymore. It must be between 1536 * and 65535 to specify the EtherType, or between 1 and 255 to specify the * Destination SAP for IEEE 802.2 LLC frames. A protocol number of 0 is * used to specify all protocols. * @param filter_program the filter program that was applied on the * received packets. The program uses tcpdump(1) style expression. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int unregister_receiver(const string& receiver_name, const string& if_name, const string& vif_name, uint16_t ether_type, const string& filter_program, string& error_msg); /** * Join a MAC multicast group. * * @param receiver_name the name of the receiver. * @param if_name the interface through which packets should be accepted. * @param vif_name the vif through which packets should be accepted. * @param ether_type the EtherType protocol number or the Destination SAP * that the receiver is interested in. It must be between 1536 and 65535 * to specify the EtherType, or between 1 and 255 to specify the * Destination SAP for IEEE 802.2 LLC frames. A protocol number of 0 is * used to specify all protocols. * @param filter_program the optional filter program to be applied on the * received packets. The program uses tcpdump(1) style expression. * @param group_address the multicast group address to join. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int join_multicast_group(const string& receiver_name, const string& if_name, const string& vif_name, uint16_t ether_type, const string& filter_program, const Mac& group_address, string& error_msg); /** * Leave a MAC multicast group. * * @param receiver_name the name of the receiver. * @param if_name the interface through which packets should not be * accepted. * @param vif_name the vif through which packets should not be accepted. * @param ether_type the EtherType protocol number or the Destination SAP * that the receiver is not interested in anymore. It must be between 1536 * and 65535 to specify the EtherType, or between 1 and 255 to specify the * Destination SAP for IEEE 802.2 LLC frames. A protocol number of 0 is * used to specify all protocols. * @param filter_program the filter program that was applied on the * received packets. The program uses tcpdump(1) style expression. * @param group_address the multicast group address to leave. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int leave_multicast_group(const string& receiver_name, const string& if_name, const string& vif_name, uint16_t ether_type, const string& filter_program, const Mac& group_address, string& error_msg); /** * Data received event. * * @param receiver_name the name of the receiver to send the raw link-level * packet to. * @param header the MAC header information. * @param payload the payload, everything after the MAC header. */ void recv_event(const string& receiver_name, const struct MacHeaderInfo& header, const vector& payload); /** * Inform the watcher that a component instance is alive. * * @param instance_name the name of the instance that is alive. */ void instance_birth(const string& instance_name); /** * Inform the watcher that a component instance is dead. * * @param instance_name the name of the instance that is dead. */ void instance_death(const string& instance_name); /** * Set the instance that is responsible for sending raw link-level packets * to a receiver. */ void set_io_link_manager_receiver(IoLinkManagerReceiver* v) { _io_link_manager_receiver = v; } /** * Get a reference to the interface tree. * * @return a reference to the interface tree (@ref IfTree). */ const IfTree& iftree() const { return _iftree; } /** * Register @ref FeaDataPlaneManager data plane manager. * * @param fea_data_plane_manager the data plane manager to register. * @param is_exclusive if true, the manager is registered as the * exclusive manager, otherwise is added to the list of managers. * @return XORP_OK on success, otherwise XORP_ERROR. */ int register_data_plane_manager(FeaDataPlaneManager* fea_data_plane_manager, bool is_exclusive); /** * Unregister @ref FeaDataPlaneManager data plane manager. * * @param fea_data_plane_manager the data plane manager to unregister. * @return XORP_OK on success, otherwise XORP_ERROR. */ int unregister_data_plane_manager(FeaDataPlaneManager* fea_data_plane_manager); /** * Get the list of registered data plane managers. * * @return the list of registered data plane managers. */ list& fea_data_plane_managers() { return _fea_data_plane_managers; } /** * Add a multicast MAC address to an interface. * * @param if_name the interface name. * @param mac the MAC address to add. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_multicast_mac(const string& if_name, const Mac& mac, string& error_msg); /** * Remove a multicast MAC address from an interface. * * @param if_name the interface name. * @param mac the MAC address to remove. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int remove_multicast_mac(const string& if_name, const Mac& mac, string& error_msg); private: class CommTableKey { public: CommTableKey(const string& if_name, const string& vif_name, uint16_t ether_type, const string& filter_program) : _if_name(if_name), _vif_name(vif_name), _ether_type(ether_type), _filter_program(filter_program) {} #ifdef XORP_USE_USTL CommTableKey() { } #endif bool operator<(const CommTableKey& other) const { if (_ether_type != other._ether_type) return (_ether_type < other._ether_type); if (_if_name != other._if_name) return (_if_name < other._if_name); if (_vif_name != other._vif_name) return (_vif_name < other._vif_name); return (_filter_program < other._filter_program); } private: string _if_name; string _vif_name; uint16_t _ether_type; string _filter_program; }; typedef map CommTable; typedef multimap FilterBag; /** * Erase filters for a given receiver name. * * @param receiver_name the name of the receiver. */ void erase_filters_by_receiver_name(const string& receiver_name); /** * Test whether there is a filter for a given receiver name. * * @param receiver_name the name of the receiver. * @return true if there is a filter for the given receiver name, * otherwise false. */ bool has_filter_by_receiver_name(const string& receiver_name) const; /** * Erase filters for a given CommTable and FilterBag. * * @param comm_table the associated CommTable. * @param filters the associated FilterBag. * @param begin the begin iterator to the FilterBag for the set of * filters to erase. * @param end the end iterator to the FilterBag for the set of filters * to erase. */ void erase_filters(CommTable& comm_table, FilterBag& filters, const FilterBag::iterator& begin, const FilterBag::iterator& end); /** * Add a communication handler that can be used for raw link-level * transmission only. * * @param if_name the interface name. * @param vif_name the vif name. * @param ether_type the EtherType protocol number. * @return a reference to the transmission only communication handler. */ IoLinkComm& add_iolink_comm_txonly(const string& if_name, const string& vif_name, uint16_t ether_type); /** * Find the communication handler for a given interface/vif name * and EtherType. * * Note that if the handler is not found, a new one is created and * returned. * * @param if_name the interface name. * @param vif_name the vif name. * @param ether_type the EtherType protocol number. * @return a reference to the communication handler. */ IoLinkComm& find_iolink_comm(const string& if_name, const string& vif_name, uint16_t ether_type); /** * Add/remove a multicast MAC address on an interface. * * @param add if true, then add the address, otherwise remove it. * @param if_name the interface name. * @param mac the MAC address to add/remove. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_remove_multicast_mac(bool add, const string& if_name, const Mac& mac, string& error_msg); FeaNode& _fea_node; EventLoop& _eventloop; const IfTree& _iftree; // Collection of raw link-level communication handlers keyed by protocol. CommTable _comm_table; // Collection of input filters created by IoLinkManager FilterBag _filters; IoLinkManagerReceiver* _io_link_manager_receiver; list _fea_data_plane_managers; }; #endif // __FEA_IO_LINK_MANAGER_HH__ xorp/fea/mfea_vif.cc0000664000076400007640000001676511703345405014517 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2012 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // 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" /** * 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 _registered_ip_protocol(0) { wants_to_be_started = false; } /** * 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), _min_ttl_threshold(mfea_vif.min_ttl_threshold()), _max_rate_limit(mfea_vif.max_rate_limit()), _registered_ip_protocol(0) { } /** * 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()) { // Start us later. wants_to_be_started = true; XLOG_WARNING("WARNING: Delaying start of mfea-vif: %s because underlying vif is not up.", name().c_str()); return XORP_OK; } if (!(is_pim_register() || is_multicast_capable())) { wants_to_be_started = true; XLOG_WARNING("WARNING: Delaying start of mfea-vif: %s because underlying vif is not multicast capable.", name().c_str()); return XORP_OK; } // // Install in the kernel only if the vif is of the appropriate type: // multicast-capable (loopback excluded), or PIM Register vif. // if (is_loopback()) { error_msg = "mfea-vif: Loopback interfaces cannot be used for multicast."; return (XORP_ERROR); } if (ProtoUnit::start() != XORP_OK) { error_msg = "internal error"; return (XORP_ERROR); } if (mfea_node().add_multicast_vif(vif_index()) != XORP_OK) { 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()); wants_to_be_started = false; //it worked return (XORP_OK); } /** System detected some change. */ void MfeaVif::notifyUpdated() { if (wants_to_be_started) { string err_msg; int rv = start(err_msg); if (rv == XORP_OK) { XLOG_WARNING("notifyUpdated, successfully started mfea_vif: %s", name().c_str()); } else { XLOG_WARNING("notifyUpdated, tried to start vif: %s, but failed: %s", name().c_str(), err_msg.c_str()); } } } /** * 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; wants_to_be_started = false; 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() != XORP_OK) { error_msg = "internal error"; ret_value = XORP_ERROR; } if (ProtoUnit::stop() != XORP_OK) { error_msg = "internal error"; ret_value = XORP_ERROR; } if (mfea_node().delete_multicast_vif(vif_index()) != XORP_OK) { 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() { XLOG_INFO("MfeaVif: Interface enable %s%s", this->str().c_str(), flags_string().c_str()); ProtoUnit::enable(); } /** * 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()); } int MfeaVif::register_protocol(const string& module_instance_name, uint8_t ip_protocol, string& error_msg) { if (! _registered_module_instance_name.empty()) { error_msg = c_format("Cannot register %s on vif %s: " "%s already registered", module_instance_name.c_str(), name().c_str(), _registered_module_instance_name.c_str()); return (XORP_ERROR); } _registered_module_instance_name = module_instance_name; _registered_ip_protocol = ip_protocol; return (XORP_OK); } int MfeaVif::unregister_protocol(const string& module_instance_name, string& error_msg) { if (module_instance_name != _registered_module_instance_name) { error_msg = c_format("Cannot unregister %s on vif %s: " "%s was registered previously", module_instance_name.c_str(), name().c_str(), (_registered_module_instance_name.size())? _registered_module_instance_name.c_str() : "NULL"); return (XORP_ERROR); } _registered_module_instance_name = ""; _registered_ip_protocol = 0; return (XORP_OK); } // 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); } xorp/fea/xrl_fea_node.hh0000664000076400007640000001213711421137511015361 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2007-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/xrl_fea_node.hh,v 1.15 2008/10/02 21:56:50 bms Exp $ #ifndef __FEA_XRL_FEA_NODE_HH__ #define __FEA_XRL_FEA_NODE_HH__ // // FEA (Forwarding Engine Abstraction) with XRL front-end node implementation. // #include "libxorp/xorp.h" #include "libxipc/xrl_std_router.hh" #include "cli/xrl_cli_node.hh" #include "fea_node.hh" #include "libfeaclient_bridge.hh" #include "xrl_fea_io.hh" #include "xrl_fea_target.hh" #include "xrl_io_link_manager.hh" #include "xrl_io_ip_manager.hh" #include "xrl_io_tcpudp_manager.hh" #include "xrl_mfea_node.hh" class EventLoop; /** * @short FEA (Forwarding Engine Abstraction) node class with XRL front-end. * * There should be one node per FEA instance. */ class XrlFeaNode { public: /** * Constructor. * * @param eventloop the event loop to use. * @param xrl_fea_targetname the XRL targetname of the FEA. * @param xrl_finder_targetname the XRL targetname of the Finder. * @param finder_hostname the XRL Finder hostname. * @param finder_port the XRL Finder port. * @param is_dummy if true, then run the FEA in dummy mode. */ XrlFeaNode(EventLoop& eventloop, const string& xrl_fea_targetname, const string& xrl_finder_targetname, const string& finder_hostname, uint16_t finder_port, bool is_dummy); /** * Destructor */ virtual ~XrlFeaNode(); /** * Startup the service operation. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int startup(); /** * Shutdown the service operation. * * Gracefully shutdown the FEA. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int shutdown(); /** * Test whether the service is running. * * @return true if the service is still running, otherwise false. */ bool is_running() const; /** * Test whether a shutdown XRL request has been received. * * @return true if shutdown XRL request has been received, otherwise false. */ bool is_shutdown_received() const; /** * Get the event loop this service is added to. * * @return the event loop this service is added to. */ EventLoop& eventloop() { return (_eventloop); } /** * Get the XRL transmission and reception point. * * @return reference to the XRL transmission and reception point. */ XrlStdRouter& xrl_router() { return (_xrl_router); } /** * Get the FEA I/O XRL instance. * * @return reference to the FEA I/O XRL instance. */ XrlFeaIo& xrl_fea_io() { return (_xrl_fea_io); } /** * Get the FEA node instance. * * @return reference to the FEA node instance. */ FeaNode& fea_node() { return (_fea_node); } /** * Get the FEA XRL target. * * @return reference to the FEA XRL target. */ XrlFeaTarget& xrl_fea_target() { return (_xrl_fea_target); } /** * Get the Finder's XRL target name. * * @return the Finder's XRL target name. */ const string& xrl_finder_targetname() const { return (_xrl_finder_targetname); } private: EventLoop& _eventloop; // The event loop to use XrlStdRouter _xrl_router; // The standard XRL send/recv point XrlFeaIo _xrl_fea_io; // The FEA I/O XRL instance FeaNode _fea_node; // The FEA node LibFeaClientBridge _lib_fea_client_bridge; XrlFibClientManager _xrl_fib_client_manager; // The FIB client manager XrlIoLinkManager _xrl_io_link_manager; // Link raw I/O manager XrlIoIpManager _xrl_io_ip_manager; // IP raw I/O manager XrlIoTcpUdpManager _xrl_io_tcpudp_manager; // TCP/UDP I/O manager // MFEA-related stuff // TODO: XXX: This should be refactored and better integrated with the FEA. // TODO: XXX: For now we don't have a dummy MFEA // // XXX: TODO: The CLI stuff is temporary needed (and used) by the // multicast modules. // CliNode _cli_node4; XrlCliNode _xrl_cli_node; XrlMfeaNode _xrl_mfea_node4; // The IPv4 MFEA #ifdef HAVE_IPV6_MULTICAST XrlMfeaNode _xrl_mfea_node6; // The IPv6 MFEA #endif XrlFeaTarget _xrl_fea_target; // The FEA XRL target const string _xrl_finder_targetname; // The Finder target name }; #endif // __FEA_XRL_FEA_NODE_HH__ xorp/fea/firewall_manager.hh0000664000076400007640000002746011540225525016245 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2008-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __FEA_FIREWALL_MANAGER_HH__ #define __FEA_FIREWALL_MANAGER_HH__ #include "libxorp/ipv4net.hh" #include "libxorp/ipv6net.hh" #include "libxorp/status_codes.h" #include "libxorp/transaction.hh" #include "firewall_entry.hh" #include "firewall_get.hh" #include "firewall_set.hh" class EventLoop; class FeaNode; class FirewallTransactionManager; /** * @short Firewall configuration manager. */ class FirewallManager { public: /** * Constructor. * * @param fea_node the FEA node. * @param iftree the interface configuration tree to use. */ FirewallManager(FeaNode& fea_node, const IfTree& iftree); /** * Virtual destructor. */ virtual ~FirewallManager(); /** * Get a reference to the @ref EventLoop instance. * * @return a reference to the @ref EventLoop instance. */ EventLoop& eventloop() { return _eventloop; } /** * Get a reference to the interface configuration. * * @return a reference to the interface configuration. */ const IfTree& iftree() const { return _iftree; } /** * Get the status code. * * @param reason the human-readable reason for any failure. * @return the status code. */ ProcessStatus status(string& reason) const; /** * Start firewall-related transaction. * * @param tid the return-by-reference new transaction ID. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int start_transaction(uint32_t& tid, string& error_msg); /** * Commit firewall-related transaction. * * @param tid the transaction ID. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int commit_transaction(uint32_t tid, string& error_msg); /** * Abort firewall-related transaction. * * @param tid the transaction ID. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int abort_transaction(uint32_t tid, string& error_msg); /** * Add operation to firewall-related transaction. * * @param tid the transaction ID. * @param op the operation to add. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_transaction_operation(uint32_t tid, const TransactionManager::Operation& op, string& error_msg); /** * Register @ref FirewallGet plugin. * * @param firewall_get the plugin to register. * @param is_exclusive if true, the plugin is registered as the * exclusive plugin, otherwise is added to the list of plugins. * @return XORP_OK on success, otherwise XORP_ERROR. */ int register_firewall_get(FirewallGet* firewall_get, bool is_exclusive); /** * Unregister @ref FirewallGet plugin. * * @param firewall_get the plugin to unregister. * @return XORP_OK on success, otherwise XORP_ERROR. */ int unregister_firewall_get(FirewallGet* firewall_get); /** * Register @ref FirewallSet plugin. * * @param firewall_set the plugin to register. * @param is_exclusive if true, the plugin is registered as the * exclusive plugin, otherwise is added to the list of plugins. * @return XORP_OK on success, otherwise XORP_ERROR. */ int register_firewall_set(FirewallSet* firewall_set, bool is_exclusive); /** * Unregister @ref FirewallSet plugin. * * @param firewall_set the plugin to unregister. * @return XORP_OK on success, otherwise XORP_ERROR. */ int unregister_firewall_set(FirewallSet* firewall_set); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int stop(string& error_msg); /** * Add a single firewall entry that will be pushed into the underlying * system. * * @param firewall_entry the entry to add. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_entry(const FirewallEntry& firewall_entry, string& error_msg); /** * Replace a single firewall entry that will be pushed into the underlying * system. * * @param firewall_entry the entry to replace. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int replace_entry(const FirewallEntry& firewall_entry, string& error_msg); /** * Delete a single firewall entry that will be pushed into the underlying * system. * * @param firewall_entry the entry to delete. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int delete_entry(const FirewallEntry& firewall_entry, string& error_msg); /** * Set the IPv4 firewall table. * * @param firewall_entry_list the list with all entries to install into * the IPv4 firewall table. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_table4(const list& firewall_entry_list, string& error_msg); /** * Set the IPv6 firewall table. * * @param firewall_entry_list the list with all entries to install into * the IPv6 firewall table. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_table6(const list& firewall_entry_list, string& error_msg); /** * Delete all entries in the IPv4 firewall table. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int delete_all_entries4(string& error_msg); /** * Delete all entries in the IPv6 firewall table. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int delete_all_entries6(string& error_msg); /** * Obtain the IPv4 firewall table. * * @param firewall_entry_list the return-by-reference list with all * entries in the IPv4 firewall table. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int get_table4(list& firewall_entry_list, string& error_msg); /** * Obtain the IPv6 firewall table. * * @param firewall_entry_list the return-by-reference list with all * entries in the IPv6 firewall table. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int get_table6(list& firewall_entry_list, string& error_msg); /** * Get a token for a list of IPv4 firewall entries. * * @param token to be provided when calling get_entry_list_next4. * @param more true if the list is not empty. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int get_entry_list_start4(uint32_t& token, bool& more, string& error_msg); /** * Get a token for a list of IPv6 firewall entries. * * @param token to be provided when calling get_entry_list_next6. * @param more true if the list is not empty. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int get_entry_list_start6(uint32_t& token, bool& more, string& error_msg); /** * Get the next item in a list of IPv4 firewall entries. * * @param token returned by a previous call to get_entry_list_start4. * @param firewall_entry the firewall entry. * @param more true if the list has more items remaining. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int get_entry_list_next4(uint32_t token, FirewallEntry& firewall_entry, bool& more, string& error_msg); /** * Get the next item in a list of IPv6 firewall entries. * * @param token returned by a previous call to get_entry_list_start6. * @param firewall_entry the firewall entry. * @param more true if the list has more items remaining. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int get_entry_list_next6(uint32_t token, FirewallEntry& firewall_entry, bool& more, string& error_msg); /** * Delete browse state for a particular token. * * @param token the token for the state to delete. */ void delete_browse_state(uint32_t token); private: /** * Update the firewall entries by pushing them into the underlying system. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int update_entries(string& error_msg); class BrowseState { public: BrowseState(FirewallManager& firewall_manager, uint32_t token) : _firewall_manager(firewall_manager), _token(token), _next_entry_iter(_snapshot.begin()) {} int get_entry_list_start4(bool& more, string& error_msg); int get_entry_list_start6(bool& more, string& error_msg); int get_entry_list_next4(FirewallEntry& firewall_entry, bool& more, string& error_msg); int get_entry_list_next6(FirewallEntry& firewall_entry, bool& more, string& error_msg); private: enum { BROWSE_TIMEOUT_MS = 15000 // XXX: 15 seconds }; /** * Schedule the timer to timeout the browse state. */ void schedule_timer(); /** * Timeout the browse state. */ void timeout(); FirewallManager& _firewall_manager; uint32_t _token; list _snapshot; list::iterator _next_entry_iter; XorpTimer _timeout_timer; }; /** * Generate a new token that is available. */ void generate_token(); FeaNode& _fea_node; EventLoop& _eventloop; const IfTree& _iftree; // // The firewall transaction manager // FirewallTransactionManager* _ftm; // // The registered plugins // list _firewall_gets; list _firewall_sets; // // State browsing information // uint32_t _next_token; map _browse_db; // // State for collecting and updating the firewall entries // list _added_entries; list _replaced_entries; list _deleted_entries; // // Misc other state // bool _is_running; }; #endif // __FEA_FIREWALL_MANAGER_HH__ xorp/fea/fibconfig_transaction.hh0000664000076400007640000002050011421137511017260 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/fibconfig_transaction.hh,v 1.9 2008/10/02 21:56:46 bms Exp $ #ifndef __FEA_FIBCONFIG_TRANSACTION_HH__ #define __FEA_FIBCONFIG_TRANSACTION_HH__ #include "libxorp/ipv4.hh" #include "libxorp/ipv4net.hh" #include "libxorp/ipv6.hh" #include "libxorp/ipv6net.hh" #include "libxorp/transaction.hh" #include "fibconfig.hh" /** * Class to store and execute FibConfig transactions. * * An FibConfig transaction is a sequence of commands that should * executed atomically. */ class FibConfigTransactionManager : public TransactionManager { public: /** * Constructor. * * @param eventloop the event loop to use. * @param fibconfig the FibConfig to use. * @see FibConfig. */ FibConfigTransactionManager(EventLoop& eventloop, FibConfig& fibconfig) : TransactionManager(eventloop, TIMEOUT_MS, MAX_PENDING), _fibconfig(fibconfig) {} /** * Get a reference to the FibConfig. * * @return a reference to the FibConfig. * @see FibConfig. */ FibConfig& fibconfig() { return _fibconfig; } /** * Get the string with the first error during commit. * * @return the string with the first error during commit or an empty * string if no error. */ const string& error() const { return _first_error; } /** * Get the maximum number of operations. * * @return the maximum number of operations. */ size_t max_ops() const { return MAX_OPS; } protected: /** * Pre-commit method that is called before the first operation * in a commit. * * This is an overriding method. * * @param tid the transaction ID. */ virtual void pre_commit(uint32_t tid); /** * Post-commit method that is called after the last operation * in a commit. * * This is an overriding method. * * @param tid the transaction ID. */ virtual void post_commit(uint32_t tid); /** * Method that is called after each operation. * * This is an overriding method. * * @param success set to true if the operation succeeded, otherwise false. * @param op the operation that has been just called. */ virtual void operation_result(bool success, const TransactionOperation& op); private: /** * Set the string with the error. * * Only the string for the first error is recorded. * * @param error the string with the error. * @return XORP_OK if this was the first error, otherwise XORP_ERROR. */ int set_error(const string& error); /** * Reset the string with the error. */ void reset_error() { _first_error.erase(); } FibConfig& _fibconfig; // The FibConfig to use string _first_error; // The string with the first error enum { TIMEOUT_MS = 5000, MAX_PENDING = 10, MAX_OPS = 200 }; }; /** * Base class for operations that can occur during an FibConfig transaction. */ class FibConfigTransactionOperation : public TransactionOperation { public: FibConfigTransactionOperation(FibConfig& fibconfig) : _fibconfig(fibconfig) {} virtual ~FibConfigTransactionOperation() {} protected: FibConfig& fibconfig() { return _fibconfig; } private: FibConfig& _fibconfig; }; /** * Class to store request to add forwarding entry to FibConfig and * dispatch it later. */ class FibAddEntry4 : public FibConfigTransactionOperation { public: FibAddEntry4(FibConfig& fibconfig, const IPv4Net& net, const IPv4& nexthop, const string& ifname, const string& vifname, uint32_t metric, uint32_t admin_distance, bool xorp_route, bool is_connected_route) : FibConfigTransactionOperation(fibconfig), _fte(net, nexthop, ifname, vifname, metric, admin_distance, xorp_route) { if (is_connected_route) _fte.mark_connected_route(); } bool dispatch() { if (fibconfig().add_entry4(_fte) != XORP_OK) return (false); return (true); } string str() const { return c_format("AddEntry4: %s", _fte.str().c_str()); } private: Fte4 _fte; }; /** * Class to store request to delete forwarding entry to FibConfig and * dispatch it later. */ class FibDeleteEntry4 : public FibConfigTransactionOperation { public: FibDeleteEntry4(FibConfig& fibconfig, const IPv4Net& net, const IPv4& nexthop, const string& ifname, const string& vifname, uint32_t metric, uint32_t admin_distance, bool xorp_route, bool is_connected_route) : FibConfigTransactionOperation(fibconfig), _fte(net, nexthop, ifname, vifname, metric, admin_distance, xorp_route) { if (is_connected_route) _fte.mark_connected_route(); } bool dispatch() { if (fibconfig().delete_entry4(_fte) != XORP_OK) return (false); return (true); } string str() const { return c_format("DeleteEntry4: %s", _fte.str().c_str()); } private: Fte4 _fte; }; /** * Class to store request to delete all forwarding entries to FibConfig and * dispatch it later. */ class FibDeleteAllEntries4 : public FibConfigTransactionOperation { public: FibDeleteAllEntries4(FibConfig& fibconfig) : FibConfigTransactionOperation(fibconfig) {} bool dispatch() { if (fibconfig().delete_all_entries4() != XORP_OK) return (false); return (true); } string str() const { return c_format("DeleteAllEntries4"); } }; /** * Class to store request to add forwarding entry to FibConfig and * dispatch it later. */ class FibAddEntry6 : public FibConfigTransactionOperation { public: FibAddEntry6(FibConfig& fibconfig, const IPv6Net& net, const IPv6& nexthop, const string& ifname, const string& vifname, uint32_t metric, uint32_t admin_distance, bool xorp_route, bool is_connected_route) : FibConfigTransactionOperation(fibconfig), _fte(net, nexthop, ifname, vifname, metric, admin_distance, xorp_route) { if (is_connected_route) _fte.mark_connected_route(); } bool dispatch() { if (fibconfig().add_entry6(_fte) != XORP_OK) return (false); return (true); } string str() const { return c_format("AddEntry6: %s", _fte.str().c_str()); } private: Fte6 _fte; }; /** * Class to store request to delete forwarding entry to FibConfig * and dispatch it later. */ class FibDeleteEntry6 : public FibConfigTransactionOperation { public: FibDeleteEntry6(FibConfig& fibconfig, const IPv6Net& net, const IPv6& nexthop, const string& ifname, const string& vifname, uint32_t metric, uint32_t admin_distance, bool xorp_route, bool is_connected_route) : FibConfigTransactionOperation(fibconfig), _fte(net, nexthop, ifname, vifname, metric, admin_distance, xorp_route) { if (is_connected_route) _fte.mark_connected_route(); } bool dispatch() { if (fibconfig().delete_entry6(_fte) != XORP_OK) return (false); return (true); } string str() const { return c_format("DeleteEntry6: %s", _fte.str().c_str()); } private: Fte6 _fte; }; /** * Class to store request to delete all forwarding entries to FibConfig * and dispatch it later. */ class FibDeleteAllEntries6 : public FibConfigTransactionOperation { public: FibDeleteAllEntries6(FibConfig& fibconfig) : FibConfigTransactionOperation(fibconfig) {} bool dispatch() { if (fibconfig().delete_all_entries6() != XORP_OK) return (false); return (true); } string str() const { return c_format("DeleteAllEntries6"); } }; #endif // __FEA_FIBCONFIG_TRANSACTION_HH__ xorp/fea/README0000664000076400007640000000264711421137511013300 0ustar greearbgreearb# # $XORP: xorp/fea/README,v 1.9 2007/06/04 23:17:31 pavlin Exp $ # Forwarding Engine Abstraction ============================= The FEA is intended to fulfill several roles: 1) Manages network interfaces. 2) Reports changes to network interface configurations. 3) Manages the forwarding tables of the underlying data plane. 4) Allows TCP, UDP, raw IP and raw link-layer packets to be sent/received on network interfaces. 5) Adds support for multicast routing (see README.mfea for details). 6) Managers firewall rules of the underlying data plane. Documentation ============= The FEA architecture is described in ${XORP}/docs/fea. For information about the multicast-related MFEA see README.mfea The MFEA architecture is described in ${XORP}/docs/mfea. Status ====== Two FEA implementations exist: ``xorp_fea'' and ``xorp_fea_dummy''. Program ``xorp_fea'' is a generic FEA that contains plug-ins for various systems. Currently, it has plug-ins for the following systems: * BSD-based systems with routing socket interface * Linux-based systems with netlink socket interface, * Click forwarding plane (http://read.cs.ucla.edu/click/) * Windows' IP Helper API See ${XORP}/BUILD_NOTES for a complete list of all OS versions that may be supported by the FEA. Program ``xorp_fea_dummy'' is a functional FEA substitute that doesn't interact with the underlying system, and can be used for testing purpose. xorp/fea/iftree.hh0000664000076400007640000011641511703345405014224 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2012 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __FEA_IFTREE_HH__ #define __FEA_IFTREE_HH__ #include "libxorp/ipv4.hh" #include "libxorp/ipv6.hh" #include "libxorp/mac.hh" class IPvX; /** * Base class for FEA configurable items where the modifications need * to be held over and propagated later, ie changes happen during a * transaction but are propagated during the commit. */ class IfTreeItem : public NONCOPYABLE { public: IfTreeItem() : _st(CREATED), _soft(false) {} virtual ~IfTreeItem() {} public: enum State { NO_CHANGE = 0x00, CREATED = 0x01, DELETED = 0x02, CHANGED = 0x04 }; int set_state(State st) { if (bits(st) > 1) { return (XORP_ERROR); } _st = st; return (XORP_OK); } State state() const { return _st; } virtual int mark(State st) { if (bits(st) > 1) { return (XORP_ERROR); } if (st & (CREATED | DELETED)) { _st = st; return (XORP_OK); } if (_st & (CREATED | DELETED)) { return (XORP_OK); } _st = st; return (XORP_OK); } bool is_marked(State st) const { return st == _st; } void set_soft(bool en) { _soft = en; } bool is_soft() const { return _soft; } /** * Virtual method to be implemented to flush out state associated * objects, ie if an object is marked CREATED or CHANGED it should be * marked NO_CHANGE, if an object is marked DELETED, it should be * removed from the relevant container and destructed. */ virtual void finalize_state() = 0; string str() const; protected: static uint32_t bits(State st) { uint32_t c; for (c = 0; st != NO_CHANGE; c += st & 0x01) st = State(st >> 1); return c; } State _st; bool _soft; }; // Classes derived from IfTreeItem class IfTree; class IfTreeInterface; class IfTreeVif; class IfTreeAddr4; class IfTreeAddr6; enum IfTreeIfaceEventE { IFTREE_DELETE_IFACE, // marked deleted IFTREE_ERASE_IFACE //erased entirely }; enum IfTreeVifEventE { IFTREE_DELETE_VIF, // marked deleted IFTREE_ERASE_VIF //erased entirely }; /** IfTree will make these callbacks to listeners when certain actions * occur. */ class IfTreeListener { public: virtual ~IfTreeListener() { } virtual void notifyDeletingIface(const string& ifname) = 0; // marking iface deleted virtual void notifyErasingIface(const string& ifname) = 0; // erasing iface memory // These may not be called if the parrent iface is being deleted, so // alwaysclean up all VIFs in the iface handler. virtual void notifyDeletingVif(const string& ifname, const string& vifname) = 0; // marking vif deleted virtual void notifyErasingVif(const string& ifname, const string& vifname) = 0; // erasing vif memory // Add more as are needed. }; /** * Container class for FEA Interface objects in a system. */ class IfTree : public IfTreeItem { public: typedef map IfMap; typedef map IfIndexMap; // // XXX: We use a multimap for the index->vif mapping, because a VLAN // vif could be listed in two places: with its parent physical interface, // or along as its own parent interface. // typedef multimap VifIndexMap; /** * Default constructor. */ IfTree(const char* tree_name); /** * Constructor from another IfTree. * * @param other the other IfTree. */ IfTree(const IfTree& other); /** * Destructor. */ virtual ~IfTree(); /** * Assignment operator. * * @param other the other IfTree. */ IfTree& operator=(const IfTree& other); /** * Remove all interface state from the interface tree. */ void clear(); /** Not really const, but better than having to pass non-const * references to other classes. */ void registerListener(IfTreeListener* l) const; /** Not really const, but better than having to pass non-const * references to other classes. */ void unregisterListener(IfTreeListener* l) const; /** * Add recursively a new interface. * * @param other_iface the interface to add recursively. * @param mark_state if true, then mark the state same as the state * from the other interface, otherwise the state will be CREATED. */ void add_recursive_interface(const IfTreeInterface& other_iface, bool mark_state); /** * Create a new interface. * * @param ifname the interface name. * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_interface(const string& ifname); /** * Label interface as ready for deletion. Deletion does not occur * until finalize_state() is called. * * @param ifname the name of the interface to be labelled. * @return XORP_OK on success, otherwise XORP_ERROR. */ int remove_interface(const string& ifname); /** * Recursively create a new interface or update its state if it already * exists. * * @param other_iface the interface with the state to copy from. * @return XORP_OK on success, otherwise XORP_ERROR. */ int update_interface(const IfTreeInterface& other_iface); /** * Find an interface. * * @param ifname the interface name to search for. * @return a pointer to the interface (@ref IfTreeInterface) or NULL * if not found. */ IfTreeInterface* find_interface(const string& ifname); /** * Find a const interface. * * @param ifname the interface name to search for. * @return a const pointer to the interface (@ref IfTreeInterface) or NULL * if not found. */ const IfTreeInterface* find_interface(const string& ifname) const; /** * Find an interface for a given physical index. * * @param pif_index the physical interface index to search for. * @return a pointer to the interface (@ref IfTreeInterface) or NULL * if not found. */ IfTreeInterface* find_interface(uint32_t pif_index); /** * Find a const interface for a given physical index. * * @param pif_index the physical interface index to search for. * @return a const pointer to the interface (@ref IfTreeInterface) or NULL * if not found. */ const IfTreeInterface* find_interface(uint32_t pif_index) const; /** * Find a vif. * * @param ifname the interface name to search for. * @param vifname the vif name to search for. * @return a pointer to the vif (@ref IfTreeVif) or NULL if not found. */ IfTreeVif* find_vif(const string& ifname, const string& vifname); /** * Find a const vif. * * @param ifname the interface name to search for. * @param vifname the vif name to search for. * @return a const pointer to the vif (@ref IfTreeVif) or NULL * if not found. */ const IfTreeVif* find_vif(const string& ifname, const string& vifname) const; /** * Find a vif for a given physical index. * * @param pif_index the physical interface index to search for. * @return a pointer to the vif (@ref IfTreeVif) or NULL if not found. */ IfTreeVif* find_vif(uint32_t pif_index); /** * Find a const vif for a given physical index. * * @param pif_index the physical interface index to search for. * @return a const pointer to the vif (@ref IfTreeVif) or NULL * if not found. */ const IfTreeVif* find_vif(uint32_t pif_index) const; /** * Find an IPv4 address. * * @param ifname the interface name to search for. * @param vifname the vif name to search for. * @param addr the address to search for. * @return a pointer to the vif (@ref IfTreeAddr4) or NULL if not found. */ IfTreeAddr4* find_addr(const string& ifname, const string& vifname, const IPv4& addr); /** * Find a const IPv4 address. * * @param ifname the interface name to search for. * @param vifname the vif name to search for. * @param addr the address to search for. * @return a const pointer to the vif (@ref IfTreeAddr4) or NULL * if not found. */ const IfTreeAddr4* find_addr(const string& ifname, const string& vifname, const IPv4& addr) const; /** * Find an IPv6 address. * * @param ifname the interface name to search for. * @param vifname the vif name to search for. * @param addr the address to search for. * @return a pointer to the vif (@ref IfTreeAddr6) or NULL if not found. */ IfTreeAddr6* find_addr(const string& ifname, const string& vifname, const IPv6& addr); /** * Find a const IPv6 address. * * @param ifname the interface name to search for. * @param vifname the vif name to search for. * @param addr the address to search for. * @return a pointer to the vif (@ref IfTreeAddr6) or NULL if not found. */ const IfTreeAddr6* find_addr(const string& ifname, const string& vifname, const IPv6& addr) const; /** * Find an interface and a vif by an address that belongs to that * interface and vif. * * @param addr the address. * @param ifp return-by-reference a pointer to the interface. * @param vifp return-by-reference a pointer to the vif. * @return true if a match is found, otherwise false. */ bool find_interface_vif_by_addr(const IPvX& addr, const IfTreeInterface*& ifp, const IfTreeVif*& vifp) const; /** * Find an interface and a vif by an address that shares the same subnet * or p2p address. * * @param addr the address. * @param ifp return-by-reference a pointer to the interface. * @param vifp return-by-reference a pointer to the vif. * @return true if a match is found, otherwise false. */ bool find_interface_vif_same_subnet_or_p2p(const IPvX& addr, const IfTreeInterface*& ifp, const IfTreeVif*& vifp) const; /** * Get the map with the stored interfaces. * * @return the map with the stored interfaces. */ IfMap& interfaces() { return _interfaces; } /** * Get the const map with the stored interfaces. * * @return the const map with the stored interfaces. */ const IfMap& interfaces() const { return _interfaces; } /** * Align system-user merged configuration with the pulled changes * in the system configuration. * * Inside the FEA there may be multiple configuration representations, * typically one the user modifies, one that mirrors the hardware, * and one that merges those two (e.g., some of the merged information * comes from the user configuration while other might come * from the underlying system). * Errors may occur pushing the user config down onto the hardware and * we need a method to update the merged config from the h/w config that * exists after the config push. We can't just copy the h/w config since * the user config is restricted to configuration set by the user. * * The alignment works as follows: * 1. If an interface in the local tree is marked as "soft", its * state is not modified and the rest of the processing is ignored. * 2. If an interface in the local tree is marked as * "default_system_config", the rest of the processing is not * applied, and the following rules are used instead: * (a) If the interface is not in the other tree, it is marked * as "disabled" and its vifs are marked for deletion. * (b) Otherwise, its state (and the subtree below it) is copied * as-is from the other tree. * 3. If an item in the local tree is not in the other tree, it is * marked as "disabled" in the local tree. * 4. If an item in the local tree is in the other tree, and its state * is different in the local and the other tree, the state * is copied from the other tree to the local tree. * Also, if the item is disabled in the user config tree, it is * marked as "disabled" in the local tree. * * @param other the configuration tree to align state with. * @param user_config the user configuration tree to reference during * the alignment. * @return modified configuration structure. */ IfTree& align_with_pulled_changes(const IfTree& other, const IfTree& user_config); /** * Align system-user merged configuration with the observed changes * in the system configuration. * * Inside the FEA there may be multiple configuration representations, * typically one the user modifies, one that mirrors the hardware, * and one that merges those two (e.g., some of the merged information * comes from the user configuration while other might come * from the underlying system). * On certain systems there could be asynchronous updates originated * by the system that are captured by the FEA interface observer * (e.g., a cable is unplugged, a tunnel interface is added/deleted, etc). * * This method is used to align those updates with the merged * configuration. * * 1. If an interface in the other tree is not in the local tree, it is * tested whether is in the user configuration tree. If not, the * rest of the processing is not applied and the interface is ignored. * Otherwise it is created, its state is merged from the user config * and other tree, and is marked as "CREATED". * 2. If an interface in the local tree is marked as "soft", its * state is not modified and the rest of the processing is ignored. * 3. If an interface in the local tree is marked as * "default_system_config", the rest of the processing is not * applied, and its state (and the subtree below it) is copied * as-is from the other tree. * 4. If an item in the other tree is not in the local tree, it is * tested whether is in the user configuration tree. If not, the * rest of the processing is not applied and the item is ignored. * Otherwise it is created, its state is merged from the user config * and the other tree, and is marked as "CREATED". * 5. If an item in the other tree is marked as: * (a) "NO_CHANGE": The state of the entry in the other tree is not * propagated to the local tree, but its subtree entries are * processed. * (b) "DELETED": The item in the local tree is disabled, and the * subtree entries are ignored. * (c) "CREATED" or "CHANGED": If the state of the entry is different * in the other and the local tree, it is copied to the local tree, * and the item in the local tree is marked as "CREATED" or * "CHANGED". * unless it was marked earlier as "CREATED". * Also, if the item is disabled in the user config tree, it is * marked as "disabled" in the local tree. * * @param other the configuration tree to align state with. * @param user_config the user configuration tree to reference during * the alignment. * @return modified configuration structure. */ IfTree& align_with_observed_changes(const IfTree& other, const IfTree& user_config); /** * Align system-user merged configuration with the user configuration * changes. * * Inside the FEA there may be multiple configuration representations, * typically one the user modifies, one that mirrors the hardware, * and one that merges those two (e.g., some of the merged information * comes from the user configuration while other might come * from the underlying system). * * This method is used to align the user configuration changes with the * merged configuration. * * The alignment works as follows: * 1. If an item in the other tree is not in the local tree, it is * created in the local tree and its state (and the subtree below it) * is copied as-is from the other tree, and the rest of the processing * is ignored. * 2. If an item in the other tree is marked as: * (a) "DELETED": The item in the local tree is marked as "DELETED", * and the subtree entries are ignored. * (b) All other: If the state of the item is different in the other * and the local tree, it is copied to the local tree. * Note that we compare the state even for "NO_CHANGE" items in * case a previous change to a parent item in the merged tree * has affected the entry (e.g., disabled interface would * disable the vifs and addresses as well). * * @param other the configuration tree to align state with. * @return modified configuration structure. */ IfTree& align_with_user_config(const IfTree& other); /** * Prepare configuration for pushing and replacing previous configuration. * * If the previous configuration is to be replaced with new configuration, * we need to prepare the state that will delete, update, and add the * new state as appropriate. * The preparation works as follows: * - All items in the local tree are preserved and marked as created. * - All items in the other tree that are not in the local tree are * added to the local tree and are marked as deleted. * * @param other the configuration tree to be used to prepare the * replacement state. * @return modified configuration structure. */ IfTree& prepare_replacement_state(const IfTree& other); /** * Prune bogus deleted state. * * If an item in the local tree is marked as deleted, but is not * in the other tree, then it is removed. * * @param old_iftree the old tree with the state that is used as reference. * @return the modified configuration tree. */ IfTree& prune_bogus_deleted_state(const IfTree& old_iftree); /** * Delete interfaces labelled as ready for deletion, call finalize_state() * on remaining interfaces, and set state to NO_CHANGE. */ void finalize_state(); /** * @return string representation of IfTree. */ string str() const; const string& getName() const { return name; } void markVifDeleted(IfTreeVif* ifp); void markIfaceDeleted(IfTreeInterface* ifp); protected: friend class IfTreeInterface; friend class IfTreeVif; void insert_ifindex(IfTreeInterface* ifp); void erase_ifindex(IfTreeInterface* ifp); void insert_vifindex(IfTreeVif* vifp); void erase_vifindex(IfTreeVif* vifp); void sendEvent(IfTreeVifEventE e, IfTreeVif* vifp); void sendEvent(IfTreeIfaceEventE e, IfTreeInterface* ifp); private: string name; // identifier for this tree IfMap _interfaces; IfIndexMap _ifindex_map; // Map of pif_index to interface VifIndexMap _vifindex_map; // Map of pif_index to vif // Make this mutable so that we can past const references to other classes, // but still let them be listeners. mutable list listeners; }; /** * FEA class for holding physical interface state. */ class IfTreeInterface : public IfTreeItem { public: typedef map VifMap; typedef set MacSet; IfTreeInterface(IfTree& iftree, const string& ifname); virtual ~IfTreeInterface(); IfTree& iftree() { return _iftree; } const string& ifname() const { return _ifname; } uint32_t pif_index() const { return _pif_index; } void set_pif_index(uint32_t v) { iftree().erase_ifindex(this); _pif_index = v; mark(CHANGED); iftree().insert_ifindex(this); } virtual int mark(State st) { int rv = IfTreeItem::mark(st); if (st == DELETED) { set_probed_vlan(false); // need to reprobe if this ever goes un-deleted } return rv; } // This relates to vlans. bool cr_by_xorp() const { return _created_by_xorp; } void set_cr_by_xorp(bool b) { _created_by_xorp = b; } bool probed_vlan() const { return _probed_vlan; } void set_probed_vlan(bool b) { _probed_vlan = b; } bool enabled() const { return _enabled; } void set_enabled(bool en) { _enabled = en; mark(CHANGED); } uint32_t mtu() const { return _mtu; } void set_mtu(uint32_t mtu) { _mtu = mtu; mark(CHANGED); } const Mac& mac() const { return _mac; } void set_mac(const Mac& mac) { _mac = mac; mark(CHANGED); } MacSet& macs() { return _macs; } const MacSet& macs() const { return _macs; } bool no_carrier() const { return _no_carrier; } void set_no_carrier(bool v) { _no_carrier = v; mark(CHANGED); } uint64_t baudrate() const { return _baudrate; } void set_baudrate(uint64_t v) { _baudrate = v; mark(CHANGED); } const string& parent_ifname() const { return _parent_ifname; } void set_parent_ifname(string& v) { if (v != _parent_ifname) { _parent_ifname = v; mark(CHANGED); }} bool is_vlan() const; const string& iface_type() const { return _iface_type; } void set_iface_type(string& v) { if (v != _iface_type) { _iface_type = v; mark(CHANGED); }} const string& vid() const { return _vid; } void set_vid(string& v) { if (v != _vid) { _vid = v; mark(CHANGED); }} bool discard() const { return _discard; } void set_discard(bool discard) { _discard = discard; mark(CHANGED); } bool unreachable() const { return _unreachable; } void set_unreachable(bool v) { _unreachable = v; mark(CHANGED); } bool management() const { return _management; } void set_management(bool v) { _management = v; mark(CHANGED); } bool default_system_config() const { return _default_system_config; } void set_default_system_config(bool v) { _default_system_config = v; mark(CHANGED); } /** * Get the system-specific interface flags. * * Typically, this value is read from the underlying system, and is * used only for internal purpose. * * @return the system-specific interface flags. */ uint32_t interface_flags() const { return _interface_flags; } /** * Store the system-specific interface flags. * * Typically, this value is read from the underlying system, and is * used only for internal purpose. * * @param v the value of the system-specific interface flags to store. */ void set_interface_flags(uint32_t v) { _interface_flags = v; mark(CHANGED); } const VifMap& vifs() const { return _vifs; } VifMap& vifs() { return _vifs; } /** * Add recursively a new vif. * * @param other_vif the vif to add recursively. * @param mark_state if true, then mark the state same as the state * from the other vif, otherwise the state will be CREATED. */ void add_recursive_vif(const IfTreeVif& other_vif, bool mark_state); /** * Create a new vif. * * @param vifname the vif name. * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_vif(const string& vifname); /** * Label vif as ready for deletion. Deletion does not occur * until finalize_state() is called. * * @param vifname the name of the vif to be labelled. * @return XORP_OK on success, otherwise XORP_ERROR. */ int remove_vif(const string& vifname); /** * Find a vif. * * @param vifname the vif name to search for. * @return a pointer to the vif (@ref IfTreeVif) or NULL if not found. */ IfTreeVif* find_vif(const string& vifname); /** * Find a const vif. * * @param vifname the vif name to search for. * @return a const pointer to the vif (@ref IfTreeVif) or NULL * if not found. */ const IfTreeVif* find_vif(const string& vifname) const; /** * Find a vif for a given physical index. * * @param pif_index the physical interface index to search for. * @return a pointer to the interface (@ref IfTreeVif) or NULL * if not found. */ IfTreeVif* find_vif(uint32_t pif_index); /** * Find a const vif for a given physical index. * * @param pif_index the physical interface index to search for. * @return a const pointer to the interface (@ref IfTreeVif) or NULL * if not found. */ const IfTreeVif* find_vif(uint32_t pif_index) const; /** * Find an IPv4 address. * * @param vifname the vif name to search for. * @param addr the address to search for. * @return a pointer to the vif (@ref IfTreeAddr4) or NULL if not found. */ IfTreeAddr4* find_addr(const string& vifname, const IPv4& addr); /** * Find a const IPv4 address. * * @param vifname the vif name to search for. * @param addr the address to search for. * @return a const pointer to the vif (@ref IfTreeAddr4) or NULL * if not found. */ const IfTreeAddr4* find_addr(const string& vifname, const IPv4& addr) const; /** * Find an IPv6 address. * * @param vifname the vif name to search for. * @param addr the address to search for. * @return a pointer to the vif (@ref IfTreeAddr6) or NULL if not found. */ IfTreeAddr6* find_addr(const string& vifname, const IPv6& addr); /** * Find a const IPv6 address. * * @param vifname the vif name to search for. * @param addr the address to search for. * @return a pointer to the vif (@ref IfTreeAddr6) or NULL if not found. */ const IfTreeAddr6* find_addr(const string& vifname, const IPv6& addr) const; /** * Copy state of internal variables from another IfTreeInterface. * * @param o the interface to copy from. * @param copy_user_config if true then copy the flags from the * user's configuration. */ void copy_state(const IfTreeInterface& o, bool copy_user_config); /** * Test if the interface-specific internal state is same. * * @param o the IfTreeInterface to compare against. * @return true if the interface-specific internal state is same. */ bool is_same_state(const IfTreeInterface& o); void finalize_state(); string str() const; private: IfTree& _iftree; const string _ifname; /* virtual interface info */ string _parent_ifname; string _iface_type; string _vid; uint32_t _pif_index; bool _created_by_xorp; // for vlans bool _probed_vlan; bool _enabled; bool _discard; bool _unreachable; bool _management; bool _default_system_config; uint32_t _mtu; Mac _mac; bool _no_carrier; uint64_t _baudrate; // The link baudrate uint32_t _interface_flags; // The system-specific interface flags VifMap _vifs; MacSet _macs; // XXX: not part of user config, but used by processes }; /** * FEA class for virtual (logical) interface state. */ class IfTreeVif : public IfTreeItem { public: typedef map IPv4Map; typedef map IPv6Map; IfTreeVif(IfTreeInterface& iface, const string& vifname); virtual ~IfTreeVif(); IfTree& iftree() { return _iface.iftree(); } const string& ifname() const { return _iface.ifname(); } const string& vifname() const { return _vifname; } uint32_t pif_index() const { return _pif_index; } void set_pif_index(uint32_t v) { iftree().erase_vifindex(this); _pif_index = v; mark(CHANGED); iftree().insert_vifindex(this); } uint32_t vif_index() const { return _vif_index; } void set_vif_index(uint32_t v) { _vif_index = v; mark(CHANGED); } bool enabled() const { return _enabled; } bool broadcast() const { return _broadcast; } bool loopback() const { return _loopback; } bool point_to_point() const { return _point_to_point; } bool multicast() const { return _multicast; } bool pim_register() const { return _pim_register; } void set_enabled(bool en) { _enabled = en; mark(CHANGED); } void set_broadcast(bool v) { _broadcast = v; mark(CHANGED); } void set_loopback(bool v) { _loopback = v; mark(CHANGED); } void set_point_to_point(bool v) { _point_to_point = v; mark(CHANGED); } void set_multicast(bool v) { _multicast = v; mark(CHANGED); } void set_pim_register(bool v) { _pim_register = v; mark(CHANGED); } /** * Get the system-specific vif flags. * * Typically, this value is read from the underlying system, and is * used only for internal purpose. * * @return the system-specific vif flags. */ uint32_t vif_flags() const { return _vif_flags; } /** * Store the system-specific vif flags. * * Typically, this value is read from the underlying system, and is * used only for internal purpose. * * @param v the value of the system-specific vif flags to store. */ void set_vif_flags(uint32_t v) { _vif_flags = v; mark(CHANGED); } const IPv4Map& ipv4addrs() const { return _ipv4addrs; } IPv4Map& ipv4addrs() { return _ipv4addrs; } const IPv6Map& ipv6addrs() const { return _ipv6addrs; } IPv6Map& ipv6addrs() { return _ipv6addrs; } /** * Copy recursively from another vif. * * @param other_vif the vif to copy recursively from. */ void copy_recursive_vif(const IfTreeVif& other_vif); /** * Add recursively a new IPv4 address. * * @param other_addr the address to add recursively. * @param mark_state if true, then mark the state same as the state * from the other address, otherwise the state will be CREATED. */ void add_recursive_addr(const IfTreeAddr4& other_addr, bool mark_state); /** * Add recursively a new IPv6 address. * * @param other_addr the address to add recursively. * @param mark_state if true, then mark the state same as the state * from the other address, otherwise the state will be CREATED. */ void add_recursive_addr(const IfTreeAddr6& other_addr, bool mark_state); /** * Add IPv4 address. * * @param addr address to be added. * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_addr(const IPv4& addr); /** * Mark IPv4 address as DELETED. * * Deletion occurs when finalize_state is called. * * @param addr address to labelled. * @return XORP_OK on success, otherwise XORP_ERROR. */ int remove_addr(const IPv4& addr); /** * Add IPv6 address. * * @param addr address to be added. * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_addr(const IPv6& addr); /** * Mark IPv6 address as DELETED. * * Deletion occurs when finalize_state is called. * * @param addr address to labelled. * @return XORP_OK on success, otherwise XORP_ERROR. */ int remove_addr(const IPv6& addr); /** * Find an IPv4 address. * * @param addr the address to search for. * @return a pointer to the vif (@ref IfTreeAddr4) or NULL if not found. */ IfTreeAddr4* find_addr(const IPv4& addr); /** * Find a const IPv4 address. * * @param addr the address to search for. * @return a const pointer to the vif (@ref IfTreeAddr4) or NULL * if not found. */ const IfTreeAddr4* find_addr(const IPv4& addr) const; /** * Find an IPv6 address. * * @param addr the address to search for. * @return a pointer to the vif (@ref IfTreeAddr6) or NULL if not found. */ IfTreeAddr6* find_addr(const IPv6& addr); /** * Find a const IPv6 address. * * @param addr the address to search for. * @return a pointer to the vif (@ref IfTreeAddr6) or NULL if not found. */ const IfTreeAddr6* find_addr(const IPv6& addr) const; /** * Propagate vif flags to the addresses. */ void propagate_flags_to_addresses(); /** * Copy state of internal variables from another IfTreeVif. */ void copy_state(const IfTreeVif& o) { set_pif_index(o.pif_index()); set_vif_index(o.vif_index()); set_enabled(o.enabled()); set_broadcast(o.broadcast()); set_loopback(o.loopback()); set_point_to_point(o.point_to_point()); set_multicast(o.multicast()); set_pim_register(o.pim_register()); set_vif_flags(o.vif_flags()); } /** * Test if the vif-specific internal state is same. * * @param o the IfTreeVif to compare against. * @return true if the vif-specific internal state is same. */ bool is_same_state(const IfTreeVif& o) { return ((pif_index() == o.pif_index()) && (vif_index() == o.vif_index()) && (enabled() == o.enabled()) && (broadcast() == o.broadcast()) && (loopback() == o.loopback()) && (point_to_point() == o.point_to_point()) && (multicast() == o.multicast()) && (pim_register() == o.pim_register()) && (vif_flags() == o.vif_flags())); } void finalize_state(); string str() const; private: IfTreeInterface& _iface; const string _vifname; uint32_t _pif_index; uint32_t _vif_index; bool _enabled; bool _broadcast; bool _loopback; bool _point_to_point; bool _multicast; bool _pim_register; uint32_t _vif_flags; // The system-specific vif flags IPv4Map _ipv4addrs; IPv6Map _ipv6addrs; }; /** * Class for holding an IPv4 interface address and address related items. */ class IfTreeAddr4 : public IfTreeItem { public: IfTreeAddr4(const IPv4& addr) : IfTreeItem(), _addr(addr), _enabled(false), _broadcast(false), _loopback(false), _point_to_point(false), _multicast(false), _prefix_len(0) {} const IPv4& addr() const { return _addr; } bool enabled() const { return _enabled; } bool broadcast() const { return _broadcast; } bool loopback() const { return _loopback; } bool point_to_point() const { return _point_to_point; } bool multicast() const { return _multicast; } void set_enabled(bool en) { _enabled = en; mark(CHANGED); } void set_broadcast(bool v) { _broadcast = v; mark(CHANGED); } void set_loopback(bool v) { _loopback = v; mark(CHANGED); } void set_point_to_point(bool v) { _point_to_point = v; mark(CHANGED); } void set_multicast(bool v) { _multicast = v; mark(CHANGED); } /** * Get prefix length associates with address. */ uint32_t prefix_len() const { return _prefix_len; } /** * Set prefix length associate with address. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_prefix_len(uint32_t prefix_len); /** * Get the broadcast address. * * @return the broadcast address or IPv4::ZERO() if there is no * broadcast address set. */ IPv4 bcast() const; /** * Set the broadcast address. * @param baddr the broadcast address. */ void set_bcast(const IPv4& baddr); /** * Get the endpoint address of a point-to-point link. * @return the broadcast address or IPv4::ZERO() if there is no * broadcast address set. */ IPv4 endpoint() const; /** * Set the endpoint address of a point-to-point link. * @param oaddr the endpoint address. */ void set_endpoint(const IPv4& oaddr); /** * Copy state of internal variables from another IfTreeAddr4. */ void copy_state(const IfTreeAddr4& o) { set_enabled(o.enabled()); set_broadcast(o.broadcast()); set_loopback(o.loopback()); set_point_to_point(o.point_to_point()); set_multicast(o.multicast()); if (o.broadcast()) set_bcast(o.bcast()); if (o.point_to_point()) set_endpoint(o.endpoint()); set_prefix_len(o.prefix_len()); } /** * Test if the address-specific internal state is same. * * @param o the IfTreeAddr4 to compare against. * @return true if the address-specific internal state is same. */ bool is_same_state(const IfTreeAddr4& o) { return ((enabled() == o.enabled()) && (broadcast() == o.broadcast()) && (loopback() == o.loopback()) && (point_to_point() == o.point_to_point()) && (multicast() == o.multicast()) && (bcast() == o.bcast()) && (endpoint() == o.endpoint()) && (prefix_len() == o.prefix_len())); } void finalize_state(); string str() const; private: IPv4 _addr; bool _enabled; bool _broadcast; bool _loopback; bool _point_to_point; bool _multicast; IPv4 _oaddr; // Other address - p2p endpoint or bcast addr uint32_t _prefix_len; // The prefix length }; /** * Class for holding an IPv6 interface address and address related items. */ class IfTreeAddr6 : public IfTreeItem { public: IfTreeAddr6(const IPv6& addr) : IfTreeItem(), _addr(addr), _enabled(false), _loopback(false), _point_to_point(false), _multicast(false), _prefix_len(0) {} const IPv6& addr() const { return _addr; } bool enabled() const { return _enabled; } bool loopback() const { return _loopback; } bool point_to_point() const { return _point_to_point; } bool multicast() const { return _multicast; } void set_enabled(bool en) { _enabled = en; mark(CHANGED); } void set_loopback(bool v) { _loopback = v; mark(CHANGED); } void set_point_to_point(bool v) { _point_to_point = v; mark(CHANGED); } void set_multicast(bool v) { _multicast = v; mark(CHANGED); } /** * Get prefix length associated with address. */ uint32_t prefix_len() const { return _prefix_len; } /** * Set prefix length associate with address. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_prefix_len(uint32_t prefix_len); IPv6 endpoint() const; void set_endpoint(const IPv6& oaddr); /** * Copy state of internal variables from another IfTreeAddr6. */ void copy_state(const IfTreeAddr6& o) { set_enabled(o.enabled()); set_loopback(o.loopback()); set_point_to_point(o.point_to_point()); set_multicast(o.multicast()); if (o.point_to_point()) set_endpoint(o.endpoint()); set_prefix_len(o.prefix_len()); } /** * Test if the address-specific internal state is same. * * @param o the IfTreeAddr6 to compare against. * @return true if the address-specific internal state is same. */ bool is_same_state(const IfTreeAddr6& o) { return ((enabled() == o.enabled()) && (loopback() == o.loopback()) && (point_to_point() == o.point_to_point()) && (multicast() == o.multicast()) && (endpoint() == o.endpoint()) && (prefix_len() == o.prefix_len())); } void finalize_state(); string str() const; private: IPv6 _addr; bool _enabled; bool _loopback; bool _point_to_point; bool _multicast; IPv6 _oaddr; // Other address - p2p endpoint uint32_t _prefix_len; // The prefix length }; #endif // __FEA_IFTREE_HH__ xorp/fea/firewall_set.hh0000664000076400007640000001104311421137511015407 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2008-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/firewall_set.hh,v 1.5 2008/10/02 21:56:46 bms Exp $ #ifndef __FEA_FIREWALL_SET_HH__ #define __FEA_FIREWALL_SET_HH__ #include "firewall_entry.hh" #include "iftree.hh" #include "fea_data_plane_manager.hh" class FirewallManager; class FirewallSet { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ FirewallSet(FeaDataPlaneManager& fea_data_plane_manager) : _is_running(false), _firewall_manager(fea_data_plane_manager.firewall_manager()), _fea_data_plane_manager(fea_data_plane_manager) {} /** * Virtual destructor. */ virtual ~FirewallSet() {} /** * Get the @ref FirewallManager instance. * * @return the @ref FirewallManager instance. */ FirewallManager& firewall_manager() { return _firewall_manager; } /** * Get the @ref FeaDataPlaneManager instance. * * @return the @ref FeaDataPlaneManager instance. */ FeaDataPlaneManager& fea_data_plane_manager() { return _fea_data_plane_manager; } /** * Test whether this instance is running. * * @return true if the instance is running, otherwise false. */ virtual bool is_running() const { return _is_running; } /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg) = 0; /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg) = 0; /** * Update the firewall entries by pushing them into the underlying system. * * @param added_entries the entries to add. * @param replaced_entries the entries to replace. * @param deleted_entries the deleted entries. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int update_entries(const list& added_entries, const list& replaced_entries, const list& deleted_entries, string& error_msg) = 0; /** * Set the IPv4 firewall table. * * @param firewall_entry_list the list with all entries to install into * the IPv4 firewall table. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int set_table4(const list& firewall_entry_list, string& error_msg) = 0; /** * Delete all entries in the IPv4 firewall table. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int delete_all_entries4(string& error_msg) = 0; /** * Set the IPv6 firewall table. * * @param firewall_entry_list the list with all entries to install into * the IPv6 firewall table. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int set_table6(const list& firewall_entry_list, string& error_msg) = 0; /** * Delete all entries in the IPv6 firewall table. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int delete_all_entries6(string& error_msg) = 0; protected: // Misc other state bool _is_running; private: FirewallManager& _firewall_manager; FeaDataPlaneManager& _fea_data_plane_manager; }; #endif // __FEA_FIREWALL_SET_HH__ xorp/fea/ifconfig_property.cc0000664000076400007640000000416411421137511016453 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2007-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "ifconfig.hh" #include "ifconfig_property.hh" // // Data plane property plugin (base class implementation). // IfConfigProperty::IfConfigProperty( FeaDataPlaneManager& fea_data_plane_manager) : _is_running(false), _ifconfig(fea_data_plane_manager.ifconfig()), _fea_data_plane_manager(fea_data_plane_manager), _have_ipv4(false), _have_ipv6(false), _first_start(true) { } IfConfigProperty::~IfConfigProperty() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the mechanism for testing " "the data plane property: %s", error_msg.c_str()); } } int IfConfigProperty::start(string& error_msg) { UNUSED(error_msg); if (_is_running) return (XORP_OK); if (_first_start) { // // Test if the system supports IPv4 and IPv6 respectively // _have_ipv4 = test_have_ipv4(); _have_ipv6 = test_have_ipv6(); _first_start = false; } _is_running = true; return (XORP_OK); } int IfConfigProperty::stop(string& error_msg) { UNUSED(error_msg); if (! _is_running) return (XORP_OK); _is_running = false; return (XORP_OK); } xorp/fea/command_mfea0000775000076400007640000000206311421137511014744 0ustar greearbgreearb#!/bin/sh # # $XORP: xorp/fea/command_mfea,v 1.2 2003/10/15 18:54:28 pavlin Exp $ # # # Send commands to a running MFEA process. # MODULE_NAME="MFEA" HOSTNAME=`hostname` echo "Sending command to $MODULE_NAME on $HOSTNAME..." # Conditionally set ${srcdir} if it wasn't assigned (e.g., by `gmake check`) if [ "X${srcdir}" = "X" ] ; then srcdir=`dirname $0` ; fi # # The optional first argument should be either -4 or -6 to specify # IPv4 or IPv6 command # # The next argument should be the command to call. # The rest of the arguments should be the arguments to the command to call. # usage() { echo "Usage: $0 [-4 | -6 ] [xrl_command_arguments ... ]" } if [ $# -lt 1 ] ; then usage; exit 1 fi case "${1}" in -4) if [ $# -lt 2 ] ; then usage; exit 1 fi IP_VERSION=IPV4 shift ;; -6) if [ $# -lt 2 ] ; then usage; exit 1 fi IP_VERSION=IPV6 shift ;; *) # XXX: IP version defaults to IPv4 IP_VERSION=IPV4 ;; esac . ${srcdir}/../cli/xrl_cli_shell_funcs.sh . ${srcdir}/../fea/xrl_mfea_shell_funcs.sh $* exit $? xorp/fea/fea_click_config_generator0000775000076400007640000007322711421137511017643 0ustar greearbgreearb#!/usr/bin/awk -f # # $XORP: xorp/fea/xorp_fea_click_config_generator,v 1.13 2007/09/27 00:33:34 pavlin Exp $ # # # A sample script to generate Click configuration from XORP configuration. # The first argument is the XORP configuration file name. # # Currently, the script is called on-demand by the FEA whenever # the network interface information changes. # # Requirements: # 1. The Click lookup element names must be "_xorp_rt4" for IPv4 and # "_xorp_rt6" for IPv6. Those elements must support handler interface # compatible to elements LinearIPLookup() and LookupIP6Route() # respectively for adding and removing forwarding entries. # 2. The first network interface/vif must be connected to _xorp_rt4[0] and # _xorp_rt6[0], the second network interface/vif must be connected to # _xorp_rt4[1] and _xorp_rt6[1], and so-on. # The last port in _xorp_rt{4,6} is reserved for local delivery. # Note that the interfaces/vifs are ordered lexigraphically by their names. # # Note that if both kernel-level and user-level Click are enabled # (which is allowed), this script will print the kernel-level Click # configuration. # # TODO: for now the IPv6 path generation is disabled by default, because: # - There are few incomplete IPv6-related pieces (search for the "TODO" # marker in the code below). # - Currently the lastest Click release (1.4.3) does not support an # IPv6 lookup element that has "add" and "delete" handlers to # add/delete routing entries. # Note that the lastest implementation of the LookupIP6Route() # in the CVS tree supports those two handlers. # # To enable IPv6 path generation, modify below: # enable_ipv6 = 0; # to # enable_ipv6 = 1; # BEGIN { # XXX: for now IPv6 generation is disabled enable_ipv6 = 0; # Lookup element flavor selection # IPv4: LinearIPLookup, DirectIPLookup and TrieIPLookup should work ipv4_lookup_element = "LinearIPLookup"; # IPv6: LookupIP6Route is the only option at the moment ipv6_lookup_element = "LookupIP6Route"; # Various constants ipv4_addr_bitlen = 32; # IPv4 address bitmask length ipv6_addr_bitlen = 128; # IPv6 address bitmask length iftree_interfaces_max = 0; iftree_vifs_max = 0; is_click_enabled = 0; is_kernel_click = 0; is_user_click = 0; is_debug = 0; xorp_ip4 = "_xorp_ip4"; xorp_rt4 = "_xorp_rt4"; xorp_ip6 = "_xorp_ip6"; xorp_rt6 = "_xorp_rt6"; xorp_arpt = "_xorp_arpt"; xorp_toh = "_xorp_toh"; # TODO: XXX: fix ether_encap if necessary ether_encap4 = "EtherEncap(0x0800, 1:1:1:1:1:1, 2:2:2:2:2:2)"; ether_encap6 = "EtherEncap(0x86dd, 1:1:1:1:1:1, 2:2:2:2:2:2)"; statement = ""; in_comment = 0; is_syntax_error = 0; is_internal_error = 0; config_level = 0; ignore_level[0] = 1; is_level0_interfaces = 0; is_level0_fea = 0; is_level0_fea_level1_click = 0; is_level0_fea_level1_click_level2_kernel_click = 0; is_level0_fea_level1_click_level2_user_click = 0; is_ipv4_address = 0; is_ipv6_address = 0; max_xorp_rt_port = 0; iftree_interfaces_targetname = "fea"; fea_targetname = "fea"; } function calculate_state() { iftree_vifs_max = 0; xorp_rt_port = 0; # # Clear-up some temporary state # At the same time, compute the value of iftree_vifs_max # for (ii = 0; ii < iftree_interfaces_max; ii++) { ifaces_chosen[ii] = 0; for (vi = 0; vi < iftree_interfaces_vifs_max[ii]; vi++) { ifaces_vifs_chosen[ii,vi] = 0; iftree_vifs_max++; } } # # Select the interfaces and vifs by using string comparison: # the "smaller" name first. # for (ci = 0; ci < iftree_interfaces_max; ci++) { best_ii = -1; for (ii = 0; ii < iftree_interfaces_max; ii++) { if (ifaces_chosen[ii]) continue; if (best_ii == -1) { best_ii = ii; continue; } best_ifname = iftree_interfaces_ifname[best_ii]; if (iftree_interfaces_ifname[ii] < best_ifname) { best_ii = ii; } } if (best_ii < 0) { internal_error("Internal error: cannot sort the interface names\n"); } ifaces_chosen[best_ii] = 1; for (cv = 0; cv < iftree_interfaces_vifs_max[best_ii]; cv++) { best_vi = -1; for (vi = 0; vi < iftree_interfaces_vifs_max[best_ii]; vi++) { if (ifaces_vifs_chosen[best_ii,vi]) continue; if (best_vi == -1) { best_vi = vi; continue; } best_vifname = iftree_interfaces_vifs_vifname[best_ii,best_vi]; if (iftree_interfaces_vifs_vifname[best_ii,vi] < best_vifname) { best_vi = vi; } } if (best_vi < 0) { internal_error("Internal error: cannot sort the vif names\n"); } ifaces_vifs_chosen[best_ii,best_vi] = 1; iftree_interfaces_vifs_port[best_ii,best_vi] = xorp_rt_port++; } } max_xorp_rt_port = xorp_rt_port; } function check_state() { for (ii = 0; ii < iftree_interfaces_max; ii++) { ifname = iftree_interfaces_ifname[ii]; if (iftree_interfaces_has_mac[ii] != 1) { message = "Missing MAC address configuration for interface " ifname; syntax_error(message); } if (iftree_interfaces_has_mtu[ii] != 1) { message = "Missing MTU configuration for interface " ifname; syntax_error(message); } } } function generate_click_config_header() { printf("//\n"); printf("// Generated by XORP FEA\n"); printf("//\n"); } function generate_click_shared_ip_input() { check_ipv4_header = "CheckIPHeader(INTERFACES"; local_ipv4_routes = ""; check_ipv6_header = "CheckIP6Header("; # TODO: is this enough? local_ipv6_routes = ""; is_first_address4 = 1; is_first_address6 = 1; for (ii = 0; ii < iftree_interfaces_max; ii++) { for (vi = 0; vi < iftree_interfaces_vifs_max[ii]; vi++) { for (ai4 = 0; ai4 < iftree_interfaces_vifs_addresses4_max[ii,vi]; ai4++) { addr4 = iftree_interfaces_vifs_addresses4_addr[ii,vi,ai4]; prefix4 = iftree_interfaces_vifs_addresses4_prefix[ii,vi,ai4]; check_ipv4_header = check_ipv4_header " " addr4 "/" prefix4; route = addr4 "/" ipv4_addr_bitlen " " max_xorp_rt_port; if (is_first_address4) { local_ipv4_routes = route; is_first_address4 = 0; } else { local_ipv4_routes = local_ipv4_routes ", " route; } } for (ai6 = 0; ai6 < iftree_interfaces_vifs_addresses6_max[ii,vi]; ai6++) { addr6 = iftree_interfaces_vifs_addresses6_addr[ii,vi,ai6]; prefix6 = iftree_interfaces_vifs_addresses6_prefix[ii,vi,ai6]; # # XXX: unlike the CheckIPHeader() element, the # CheckIP6Header() element arguments are the list of # bad addresses instead of our addresses. # #check_ipv6_header = check_ipv6_header " " addr6 "/" prefix6; route = addr6 "/" ipv6_addr_bitlen " " max_xorp_rt_port; if (is_first_address6) { local_ipv6_routes = route; is_first_address6 = 0; } else { local_ipv6_routes = local_ipv6_routes ", " route; } } } } check_ipv4_header = check_ipv4_header ")"; check_ipv6_header = check_ipv6_header ")"; printf("\n\n"); printf("// Shared IPv4 input path and routing table\n"); printf("\n"); printf(" %s :: Strip(14)\n", xorp_ip4); printf(" -> %s\n", check_ipv4_header); printf(" -> %s :: %s(%s);\n", xorp_rt4, ipv4_lookup_element, local_ipv4_routes); if (enable_ipv6) { printf("\n\n"); printf("// Shared IPv6 input path and routing table\n"); printf("\n"); printf(" %s :: Strip(14)\n", xorp_ip6); printf(" -> %s\n", check_ipv6_header); printf(" -> GetIP6Address(24)\n"); # XXX: need this in IPv6! printf(" -> %s :: %s(%s)\n", xorp_rt6, ipv6_lookup_element, local_ipv6_routes); } } function generate_click_arp_responses() { printf("\n\n"); printf("// ARP responses are copied to each ARPQuerier and the host.\n"); printf("\n"); printf(" %s :: Tee(%u);\n", xorp_arpt, iftree_vifs_max + 1); } function generate_click_input_output_paths() { port = 0; for (ii = 0; ii < iftree_interfaces_max; ii++) { for (vi = 0; vi < iftree_interfaces_vifs_max[ii]; vi++) { xorp_c = "_xorp_c" port; xorp_out = "_xorp_out" port; xorp_to_device = "_xorp_to_device" port; xorp_ar = "_xorp_ar" port; xorp_arpq = "_xorp_arpq" port; xorp_nda = "_xorp_nda" port; xorp_nds = "_xorp_nds" port; if (! iftree_interfaces_vifs_enabled[ii,vi]) { from_device = "NullDevice"; } else { # # TODO: XXX: we should find a way to configure polling # devices. Such devices should use "PollDevice" instead # of "FromDevice". # from_device = "FromDevice(" iftree_interfaces_vifs_vifname[ii,vi] ")"; } if (! iftree_interfaces_vifs_enabled[ii,vi]) { to_device = "Discard"; } else { to_device = "ToDevice(" iftree_interfaces_vifs_vifname[ii,vi] ")"; } // IPv4 ARP arp_responder = "ARPResponder("; mac = iftree_interfaces_mac[ii]; is_begin = 1; for (ai4 = 0; ai4 < iftree_interfaces_vifs_addresses4_max[ii,vi]; ai4++) { addr4 = iftree_interfaces_vifs_addresses4_addr[ii,vi,ai4]; if (is_begin) arp_responder = arp_responder addr4; else arp_responder = arp_responder " " addr4; is_begin = 0; } arp_responder = arp_responder " " mac ")"; first_addr4 = "0.0.0.0"; if (iftree_interfaces_vifs_addresses4_max[ii,vi] > 0) { first_addr4 = iftree_interfaces_vifs_addresses4_addr[ii,vi,0]; } arp_querier = "ARPQuerier(" first_addr4 ", " mac ")"; # IPv6 Neighbor Discovery ip6_ndadvertiser = "IP6NDAdvertiser("; is_begin = 1; for (ai6 = 0; ai6 < iftree_interfaces_vifs_addresses6_max[ii,vi]; ai6++) { if (is_begin) is_begin = 0; else ip6_ndadvertiser = ip6_ndadvertiser ", "; addr6 = iftree_interfaces_vifs_addresses6_addr[ii,vi,ai6]; # TODO: fix prefix length fetching!!! ip6_ndadvertiser = ip6_ndadvertiser addr6 "/64 " mac; } ip6_ndadvertiser = ip6_ndadvertiser ")"; first_addr6 = "0::0"; if (iftree_interfaces_vifs_addresses6_max[ii,vi] > 0) { first_addr6 = iftree_interfaces_vifs_addresses6_addr[ii,vi,0]; } ip6_ndsolicitor = "IP6NDSolicitor(" first_addr6 ", " mac ")"; paint = "Paint(" port + 1 ")"; print_unknown = "Print(\"" iftree_interfaces_vifs_vifname[ii,vi] " unknown protocol\")"; printf("\n\n"); printf("// Input and output paths for %s\n", iftree_interfaces_vifs_vifname[ii,vi]); printf("\n"); printf(" %s -> %s :: Classifier(\n", from_device, xorp_c); printf(" 12/0800, // [0] IPv4 packet\n"); printf(" 12/0806 20/0001, // [1] ARP request\n"); printf(" 12/0806 20/0002, // [2] ARP reply\n"); printf(" 12/86dd 20/3aff 54/87, // [3] IPv6 ICMP ND solicitation\n"); printf(" 12/86dd 20/3aff 54/88, // [4] IPv6 ICMP ND advertisment\n"); printf(" 12/86dd, // [5] IPv6 packet\n"); printf(" -) // [6] Unsupported protocol;\n"); printf(" %s :: Queue(200) -> %s :: %s;\n", xorp_out, xorp_to_device, to_device); # IPv4 printf("\n"); printf(" // IPv4\n"); # Plain IPv4 packets printf(" %s[0] -> %s -> %s;\n", xorp_c, paint, xorp_ip4); # ARP request printf(" %s[1] -> %s -> %s;\n", xorp_c, arp_responder, xorp_out); # ARP reply printf(" %s :: %s -> %s;\n", xorp_arpq, arp_querier, xorp_out); printf(" %s[2] -> %s;\n", xorp_c, xorp_arpt); printf(" %s[%u] -> [1]%s;\n", xorp_arpt, port, xorp_arpq); # IPv6 if (enable_ipv6) { printf("\n"); printf(" // IPv6\n"); # Plain IPv6 packets printf(" %s[5] -> %s -> %s;\n", xorp_c, paint, xorp_ip6); # ICMP Neighbor Discovery Solicitation printf(" %s[3] -> %s -> %s;\n", xorp_c, ip6_ndadvertiser, xorp_out); # ICMP Neighbor Discovery Advertisment printf(" %s :: %s -> %s;\n", xorp_nds, ip6_ndsolicitor, xorp_out); printf(" %s[4] -> [1]%s;\n", xorp_c, xorp_nds); } else { printf("\n"); printf(" // Discard IPv6\n"); # Plain IPv6 packets printf(" %s[5] -> Discard;\n", xorp_c); # ICMP Neighbor Discovery Solicitation printf(" %s[3] -> Discard;\n", xorp_c); printf(" %s[4] -> Discard;\n", xorp_c); } # Unknown protocol printf("\n"); printf(" // Unknown protocol\n"); printf(" %s[6] -> %s -> Discard;\n", xorp_c, print_unknown); port++; } } } function generate_click_send_packets_to_host() { printf("\n\n"); printf("// Local delivery\n"); # TODO: XXX: Fix the Local delivery for *BSD and/or user-level Click printf("\n"); if (is_kernel_click) { # # XXX: Note that if both kernel-level and user-level Click are enabled # (which is allowed), this script will print the kernel-level Click # configuration. # printf(" %s :: ToHost;\n", xorp_toh); } else { printf(" %s :: Discard;\n", xorp_toh); } # XXX: last port in xorp_rt{4,6} is reserved for local delivery printf("\n"); printf(" // IPv4\n"); printf(" %s[%u] -> %s -> %s;\n", xorp_rt4, max_xorp_rt_port, ether_encap4, xorp_toh); printf(" %s[%u] -> %s;\n", xorp_arpt, iftree_vifs_max, xorp_toh); if (enable_ipv6) { printf("\n"); printf(" // IPv6\n"); printf(" %s[%u] -> %s -> %s;\n", xorp_rt6, max_xorp_rt_port, ether_encap6, xorp_toh); } } function generate_click_forwarding_paths() { # # Forwarding paths for each interface # port = 0; for (ii = 0; ii < iftree_interfaces_max; ii++) { for (vi = 0; vi < iftree_interfaces_vifs_max[ii]; vi++) { xorp_cp = "_xorp_cp" port; paint_tee = "PaintTee(" port + 1 ")"; xorp_gio = "_xorp_gio" port; xorp_dt = "_xorp_dt" port; xorp_fr = "_xorp_fr" port; xorp_arpq = "_xorp_arpq" port; xorp_nds = "_xorp_nds" port; ipgw_options = "IPGWOptions("; is_begin = 1; for (ai4 = 0; ai4 < iftree_interfaces_vifs_addresses4_max[ii,vi]; ai4++) { addr4 = iftree_interfaces_vifs_addresses4_addr[ii,vi,ai4]; if (is_begin) ipgw_options = ipgw_options addr4; else ipgw_options = ipgw_options ", " addr4; is_begin = 0; } ipgw_options = ipgw_options ")"; first_addr4 = "0.0.0.0"; if (iftree_interfaces_vifs_addresses4_max[ii,vi] > 0) { first_addr4 = iftree_interfaces_vifs_addresses4_addr[ii,vi,0]; } fix_ip_src = "FixIPSrc(" first_addr4 ")"; mtu = iftree_interfaces_mtu[ii]; ip_fragmenter = "IPFragmenter(" mtu ")"; printf("\n\n"); printf("// Forwarding path for %s\n", iftree_interfaces_vifs_vifname[ii,vi]); xorp_rt_port = iftree_interfaces_vifs_port[ii,vi]; printf("\n"); printf(" // IPv4\n"); printf(" %s[%u] -> DropBroadcasts\n", xorp_rt4, xorp_rt_port); printf(" -> %s :: %s\n", xorp_cp, paint_tee); printf(" -> %s :: %s\n", xorp_gio, ipgw_options); printf(" -> %s\n", fix_ip_src); printf(" -> %s :: DecIPTTL\n", xorp_dt); printf(" -> %s :: %s\n", xorp_fr, ip_fragmenter); printf(" -> [0]%s;\n", xorp_arpq); printf(" %s[1] -> ICMPError(%s, timeexceeded) -> %s;\n", xorp_dt, first_addr4, xorp_rt4); printf(" %s[1] -> ICMPError(%s, unreachable, needfrag) -> %s;\n", xorp_fr, first_addr4, xorp_rt4); printf(" %s[1] -> ICMPError(%s, parameterproblem) -> %s;\n", xorp_gio, first_addr4, xorp_rt4); printf(" %s[1] -> ICMPError(%s, redirect, host) -> %s;\n", xorp_cp, first_addr4, xorp_rt4); if (enable_ipv6) { printf("\n"); printf(" // IPv6\n"); # TODO: Must fix this bellow!!! printf(" %s[%u] -> DropBroadcasts\n", xorp_rt6, xorp_rt_port); printf(" -> DecIP6HLIM\n"); printf(" -> [0]%s\n", xorp_nds); } port++; } } } function is_end_of_statement(token) { # Get the last character l = length(token); s = substr(token, l, 1); if (s == ";") return 1; else return 0; } function is_end_of_string(token) { # Get the last character l = length(token); s = substr(token, l, 1); if (s == "\"") return 1; else return 0; } function is_begin_of_comment(token) { # Get the first two characters l = length(token); if (l < 2) return 0; s = substr(token, 1, 2); if (s == "/*") return 1; else return 0; } function is_end_of_comment(token) { # Get the last two characters l = length(token); if (l < 2) return 0; s = substr(token, l-1, 2); if (s == "*/") return 1; else return 0; } function process_statement(statement) { if (is_debug) printf("Statement: %s\n", statement); tokens_n = split(statement, tokens); for (i = 1; i <= tokens_n; i++) { # printf("Token: %s\n", tokens[i]); } # TODO: need to deal with strings for (i = 1; i <= tokens_n; i++) { if (tokens[i] == "{") { config_level++; continue; } if (tokens[i] == "}") { config_level--; if (config_level < 0) syntax_error("Too many }"); continue; } if (config_level == 0) { ignore_level[0] = 1; is_level0_interfaces = 0; is_level0_fea = 0; is_level0_fea_level1_click = 0; is_level0_fea_level1_click_level2_kernel_click = 0; is_level0_fea_level1_click_level2_user_click = 0; if (tokens[i] == "interfaces") { ignore_level[0] = 0; is_level0_interfaces = 1; } if (tokens[i] == "fea") { ignore_level[0] = 0; is_level0_fea = 1; is_level0_fea_level1_click = 0; is_level0_fea_level1_click_level2_kernel_click = 0; is_level0_fea_level1_click_level2_user_click = 0; } } if (ignore_level[0]) continue; # # Configuration section: "interfaces {}" # if (is_level0_interfaces && (config_level == 1)) { if (tokens[i] == "targetname:") { if (i == tokens_n) syntax_error("Missing target name"); i++; v = tokens[i]; iftree_interfaces_targetname = v; continue; } if (tokens[i] == "interface") { if (i == tokens_n) syntax_error("Missing interface name"); i++; # TODO: check that the interface name is valid v = tokens[i]; iftree_interfaces_max++; p = iftree_interfaces_max - 1; iftree_interfaces_ifname[p] = v; # XXX: by default each interface is enabled iftree_interfaces_enabled[p] = 1; continue; } syntax_error("Invalid keyword"); } if (is_level0_interfaces && (config_level == 2)) { if (tokens[i] == "disable:") { if (i == tokens_n) syntax_error("Missing value"); i++; v = tokens[i]; p = iftree_interfaces_max - 1; if (v == "true") iftree_interfaces_enabled[p] = 0; else iftree_interfaces_enabled[p] = 1; continue; } if (tokens[i] == "description:") { if (i == tokens_n) syntax_error("Missing value"); i++; v = tokens[i]; p = iftree_interfaces_max - 1; while (! is_end_of_string(v)) { if (i == tokens_n) syntax_error("Missing end-of-string"); i++; v = v " " tokens[i]; } iftree_interfaces_description[p] = v; continue; } if (tokens[i] == "discard:") { if (i == tokens_n) syntax_error("Missing value"); i++; v = tokens[i]; p = iftree_interfaces_max - 1; iftree_interfaces_discard[p] = v; continue; } if (tokens[i] == "unreachable:") { if (i == tokens_n) syntax_error("Missing value"); i++; v = tokens[i]; p = iftree_interfaces_max - 1; iftree_interfaces_unreachable[p] = v; continue; } if (tokens[i] == "management:") { if (i == tokens_n) syntax_error("Missing value"); i++; v = tokens[i]; p = iftree_interfaces_max - 1; iftree_interfaces_management[p] = v; continue; } if (tokens[i] == "mac:") { if (i == tokens_n) syntax_error("Missing value"); i++; v = tokens[i]; p = iftree_interfaces_max - 1; iftree_interfaces_mac[p] = v; iftree_interfaces_has_mac[p] = 1; continue; } if (tokens[i] == "mtu:") { if (i == tokens_n) syntax_error("Missing value"); i++; v = tokens[i]; p = iftree_interfaces_max - 1; iftree_interfaces_mtu[p] = v; iftree_interfaces_has_mtu[p] = 1; continue; } if (tokens[i] == "vif") { if (i == tokens_n) syntax_error("Missing vif name"); i++; # TODO: check that the vif name is valid v = tokens[i]; p = iftree_interfaces_max - 1; iftree_interfaces_vifs_max[p]++; q = iftree_interfaces_vifs_max[p] - 1; iftree_interfaces_vifs_vifname[p,q] = v; # XXX: by default each vif is enabled iftree_interfaces_vifs_enabled[p,q] = 1; continue; } syntax_error("Invalid keyword"); } if (is_level0_interfaces && (config_level == 3)) { if (tokens[i] == "disable:") { if (i == tokens_n) syntax_error("Missing value"); i++; v = tokens[i]; p = iftree_interfaces_max - 1; q = iftree_interfaces_vifs_max[p] - 1; if (v == "true") iftree_interfaces_vifs_enabled[p,q] = 0; else iftree_interfaces_vifs_enabled[p,q] = 1; continue; } if (tokens[i] == "address") { if (i == tokens_n) syntax_error("Missing address value"); i++; # TODO: check that the address value is valid v = tokens[i]; p = iftree_interfaces_max - 1; q = iftree_interfaces_vifs_max[p] - 1; is_ipv4_address = 0; is_ipv6_address = 0; if (split(v, tmp_array, ".") == 4) is_ipv4_address = 1; if (split(v, tmp_array, ":") > 1) is_ipv6_address = 1; if (!( is_ipv4_address || is_ipv6_address)) syntax_error("Invalid address value"); if (is_ipv4_address) { iftree_interfaces_vifs_addresses4_max[p,q]++; r = iftree_interfaces_vifs_addresses4_max[p,q] - 1; iftree_interfaces_vifs_addresses4_addr[p,q,r] = v; # XXX: by default each address is enabled iftree_interfaces_vifs_addresses4_enabled[p,q,r] = 1; } if (is_ipv6_address) { iftree_interfaces_vifs_addresses6_max[p,q]++; r = iftree_interfaces_vifs_addresses6_max[p,q] - 1; iftree_interfaces_vifs_addresses6_addr[p,q,r] = v; # XXX: by default each address is enabled iftree_interfaces_vifs_addresses6_enabled[p,q,r] = 1; } continue; } syntax_error("Invalid keyword"); } if (is_level0_interfaces && (config_level == 4)) { if (tokens[i] == "prefix-length:") { if (i == tokens_n) syntax_error("Missing value"); i++; v = tokens[i]; p = iftree_interfaces_max - 1; q = iftree_interfaces_vifs_max[p] - 1; if (is_ipv4_address) { r = iftree_interfaces_vifs_addresses4_max[p,q] - 1; iftree_interfaces_vifs_addresses4_prefix[p,q,r] = v; } if (is_ipv6_address) { r = iftree_interfaces_vifs_addresses6_max[p,q] - 1; iftree_interfaces_vifs_addresses6_prefix[p,q,r] = v; } continue; } if (tokens[i] == "broadcast:") { if (i == tokens_n) syntax_error("Missing value"); i++; v = tokens[i]; p = iftree_interfaces_max - 1; q = iftree_interfaces_vifs_max[p] - 1; if (is_ipv4_address) { r = iftree_interfaces_vifs_addresses4_max[p,q] - 1; iftree_interfaces_vifs_addresses4_destination[p,q,r] = v; } if (is_ipv6_address) { syntax_error("Invalid keyword"); } continue; } if (tokens[i] == "destination:") { if (i == tokens_n) syntax_error("Missing value"); i++; v = tokens[i]; p = iftree_interfaces_max - 1; q = iftree_interfaces_vifs_max[p] - 1; if (is_ipv4_address) { r = iftree_interfaces_vifs_addresses4_max[p,q] - 1; iftree_interfaces_vifs_addresses4_destination[p,q,r] = v; } if (is_ipv6_address) { r = iftree_interfaces_vifs_addresses6_max[p,q] - 1; iftree_interfaces_vifs_addresses6_destination[p,q,r] = v; } continue; } if (tokens[i] == "multicast-capable:") { if (i == tokens_n) syntax_error("Missing value"); i++; v = tokens[i]; p = iftree_interfaces_max - 1; q = iftree_interfaces_vifs_max[p] - 1; if (is_ipv4_address) { r = iftree_interfaces_vifs_addresses4_max[p,q] - 1; iftree_interfaces_vifs_addresses4_multicast[p,q,r] = v; } if (is_ipv6_address) { r = iftree_interfaces_vifs_addresses6_max[p,q] - 1; iftree_interfaces_vifs_addresses6_multicast[p,q,r] = v; } continue; } if (tokens[i] == "point-to-point:") { if (i == tokens_n) syntax_error("Missing value"); i++; v = tokens[i]; p = iftree_interfaces_max - 1; q = iftree_interfaces_vifs_max[p] - 1; if (is_ipv4_address) { r = iftree_interfaces_vifs_addresses4_max[p,q] - 1; iftree_interfaces_vifs_addresses4_p2p[p,q,r] = v; } if (is_ipv6_address) { r = iftree_interfaces_vifs_addresses6_max[p,q] - 1; iftree_interfaces_vifs_addresses6_p2p[p,q,r] = v; } continue; } if (tokens[i] == "loopback:") { if (i == tokens_n) syntax_error("Missing value"); i++; v = tokens[i]; p = iftree_interfaces_max - 1; q = iftree_interfaces_vifs_max[p] - 1; if (is_ipv4_address) { r = iftree_interfaces_vifs_addresses4_max[p,q] - 1; iftree_interfaces_vifs_addresses4_loopback[p,q,r] = v; } if (is_ipv6_address) { r = iftree_interfaces_vifs_addresses6_max[p,q] - 1; iftree_interfaces_vifs_addresses6_loopback[p,q,r] = v; } continue; } if (tokens[i] == "disable:") { if (i == tokens_n) syntax_error("Missing value"); i++; v = tokens[i]; p = iftree_interfaces_max - 1; q = iftree_interfaces_vifs_max[p] - 1; if (is_ipv4_address) { r = iftree_interfaces_vifs_addresses4_max[p,q] - 1; if (v == "true") iftree_interfaces_vifs_addresses4_enabled[p,q,r] = 0; else iftree_interfaces_vifs_addresses4_enabled[p,q,r] = 1; } if (is_ipv6_address) { r = iftree_interfaces_vifs_addresses6_max[p,q] - 1; if (v == "true") iftree_interfaces_vifs_addresses6_enabled[p,q,r] = 0; else iftree_interfaces_vifs_addresses6_enabled[p,q,r] = 1; } continue; } syntax_error("Invalid keyword"); } # # Configuration section: "fea {}" # if (is_level0_fea && (config_level == 1)) { if (tokens[i] == "targetname:") { if (i == tokens_n) syntax_error("Missing target name"); i++; v = tokens[i]; fea_targetname = v; continue; } if (tokens[i] == "click") { is_level0_fea_level1_click = 1; is_level0_fea_level1_click_level2_kernel_click = 0; is_level0_fea_level1_click_level2_user_click = 0; # XXX: by default Click is enabled is_click_enabled = 1; continue; } # XXX: ignore the rest of the configuration continue; } if (is_level0_fea && is_level0_fea_level1_click && (config_level == 2)) { if (tokens[i] == "disable:") { if (i == tokens_n) syntax_error("Missing value"); i++; v = tokens[i]; if (v == "true") is_click_enabled = 0; else is_click_enabled = 1; continue; } if (tokens[i] == "kernel-click") { is_level0_fea_level1_click_level2_kernel_click = 1; is_level0_fea_level1_click_level2_user_click = 0; # XXX: by default kernel-level Click is enabled is_kernel_click = 1; continue; } if (tokens[i] == "user-click") { is_level0_fea_level1_click_level2_kernel_click = 0; is_level0_fea_level1_click_level2_user_click = 1; # XXX: by default user-level Click is enabled is_user_click = 1; continue; } # XXX: ignore the rest of the configuration continue; } if (is_level0_fea && is_level0_fea_level1_click && is_level0_fea_level1_click_level2_kernel_click && (config_level == 3)) { if (tokens[i] == "disable:") { if (i == tokens_n) syntax_error("Missing value"); i++; v = tokens[i]; if (v == "true") { is_kernel_click = 0; } else { is_kernel_click = 1; } continue; } # XXX: ignore the rest of the configuration continue; } if (is_level0_fea && is_level0_fea_level1_click && is_level0_fea_level1_click_level2_user_click && (config_level == 3)) { if (tokens[i] == "disable:") { if (i == tokens_n) syntax_error("Missing value"); i++; v = tokens[i]; if (v == "true") { is_user_click = 0; } else { is_user_click = 1; } continue; } # XXX: ignore the rest of the configuration continue; } } } function syntax_error(message) { is_syntax_error = 1; printf("Syntax error (file %s line %d): %s\n", FILENAME, FNR, message) > "/dev/stderr"; exit(1); } function internal_error(message) { is_internal_error = 1; printf("Internal error (file %s): %s\n", FILENAME, message) > "/dev/stderr"; exit(1); } { for (i = 1; i <= NF; i++) { token = $i; # Filter-out the comments if (is_begin_of_comment(token)) { if (in_comment) syntax_error("Nested comments"); in_comment = 1; } if (in_comment) { if (is_end_of_comment(token)) in_comment = 0; continue; } if (is_end_of_comment(token)) syntax_error("Missing begin-of-comment"); statement = statement " " token; if (is_end_of_statement(token)) { process_statement(statement); statement = ""; } } process_statement(statement); statement = ""; } END { # # Initialize internal state # calculate_state(); check_state(); # # Check for syntax and internal errors # if (is_syntax_error || is_internal_error) exit(1); # # Check if Click is enabled and whether it is user-level or kernel Click # if (! is_click_enabled) exit(0); # XXX: don't print any Click configuration if ((! is_user_click) && (! is_kernel_click)) exit(0); # XXX: don't print any Click configuration generate_click_config_header(); generate_click_shared_ip_input(); generate_click_arp_responses(); generate_click_input_output_paths(); generate_click_send_packets_to_host(); generate_click_forwarding_paths(); # # Check again for syntax and internal errors # if (is_syntax_error || is_internal_error) exit(1); exit(0); } # Local Variables: # mode: AWK # sh-indentation: 4 # End: xorp/fea/fea_io.hh0000664000076400007640000001170511540225524014162 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2007-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __FEA_FEA_IO_HH__ #define __FEA_FEA_IO_HH__ // // FEA (Forwarding Engine Abstraction) I/O interface. // class EventLoop; class FeaNode; class InstanceWatcher; /** * @short FEA (Forwarding Engine Abstraction) I/O class. */ class FeaIo { public: /** * Constructor. * * @param eventloop the event loop to use. */ FeaIo(EventLoop& eventloop); /** * Destructor */ virtual ~FeaIo(); /** * Startup the service operation. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int startup(); /** * Shutdown the service operation. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int shutdown(); /** * Test whether the service is running. * * @return true if the service is still running, otherwise false. */ bool is_running() const; /** * Get the event loop this service is added to. * * @return the event loop this service is added to. */ EventLoop& eventloop() { return (_eventloop); } /** * Add a watcher for the status of a component instance. * * @param instance_name the name of the instance to watch. * @param instance_watcher the watcher that tracks the status of the * instance. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int add_instance_watch(const string& instance_name, InstanceWatcher* instance_watcher, string& error_msg); /** * Delete a watcher for the status of a component instance. * * @param instance_name the name of the instance to stop watching. * @param instance_watcher the watcher that tracks the status of the * instance. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int delete_instance_watch(const string& instance_name, InstanceWatcher* instance_watcher, string& error_msg); /** * A component instance has been born. * * @param instance_name the name of the instance that is born. */ virtual void instance_birth(const string& instance_name); /** * A component instance has died. * * @param instance_name the name of the instance that has died. */ virtual void instance_death(const string& instance_name); protected: /** * Register interest in events relating to a particular instance. * * @param instance_name name of target instance to receive event * notifications for. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int register_instance_event_interest(const string& instance_name, string& error_msg) = 0; /** * Deregister interest in events relating to a particular instance. * * @param instance_name name of target instance to stop event * notifications for. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int deregister_instance_event_interest(const string& instance_name, string& error_msg) = 0; private: EventLoop& _eventloop; // The event loop to use bool _is_running; // True if the service is running list > _instance_watchers; }; // /** * @short Instance watcher base class. * * This is a base class used by other components to add/delete interest * in watching the status of a component instance. */ class InstanceWatcher { public: virtual ~InstanceWatcher() {} /** * Inform the watcher that a component instance is alive. * * @param instance_name the name of the instance that is alive. */ virtual void instance_birth(const string& instance_name) = 0; /** * Inform the watcher that a component instance is dead. * * @param instance_name the name of the instance that is dead. */ virtual void instance_death(const string& instance_name) = 0; }; #endif // __FEA_FEA_IO_HH__ xorp/fea/tests/0000775000076400007640000000000011540225525013556 5ustar greearbgreearbxorp/fea/tests/test_fea_rawlink.cc0000664000076400007640000005021611540225525017412 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/status_codes.h" #include "libxorp/service.hh" #include "libxorp/xorpfd.hh" #ifdef HAVE_GETOPT_H #include #endif #include "libxipc/sockutil.hh" #include "libxipc/xrl_std_router.hh" #include "xrl/interfaces/fea_rawlink_xif.hh" #include "xrl/targets/test_fea_rawlink_base.hh" #include "libfeaclient/ifmgr_xrl_mirror.hh" static const uint8_t FILLER_VALUE = 0xe7; static const uint16_t ETHER_TYPE = 0x9000; // ETHERTYPE_LOOPBACK in BSD // Some globals are necessary (insufficient bound callback slots) static string g_fea_target_name; static IPv4 g_finder_host; static uint16_t g_finder_port; // --------------------------------------------------------------------------- // Verbose output control static bool s_verbose = false; bool verbose() { return s_verbose; } void set_verbose(bool v) { s_verbose = v; } #define verbose_log(x...) \ do { \ if (verbose()) { \ printf("From %s:%d: ", __FILE__, __LINE__); \ printf(x); \ fflush(stdout); \ } \ } while(0) #define verbose_err(x...) \ do { \ printf("From %s:%d: ", __FILE__, __LINE__); \ printf(x); \ fflush(stdout); \ } while(0) // ---------------------------------------------------------------------------- // Raw link client class TestRawLink : public XrlTestFeaRawlinkTargetBase { public: TestRawLink(EventLoop& e, const string& fea_target_name, IPv4 finder_host, uint16_t finder_port, const string& ifname, const string& vifname, const Mac& src, bool is_sender) : _e(e), _fea_target_name(fea_target_name), _ifname(ifname), _vifname(vifname), _src(src), _is_sender(is_sender), _p_snd(0), _p_rcv(0), _b_snd(0), _b_rcv(0), _x_err(0), _unregistered(false) { _r = new XrlStdRouter(_e, "test_fea_rawlink", finder_host, finder_port); set_command_map(_r); _r->finalize(); } ~TestRawLink() { set_command_map(0); delete _r; _r = 0; } uint32_t bytes_received() const { return _b_rcv; } uint32_t bytes_sent() const { return _b_snd; } uint32_t packets_received() const { return _p_rcv; } uint32_t packets_sent() const { return _p_snd; } uint32_t xrl_errors() const { return _x_err; } bool unregistered() const { return _unregistered; } /** * Bind to interface. * * This is an asynchronous request. If the request is * successfully queued for dispatch then true is returned. * * Subsequently, if the request is successful _sockid is set to a valid * socket identifier, and if unsuccessful the number of xrl errors is * bumped by one. */ bool register_link() { XrlRawLinkV0p1Client c(_r); verbose_log("Sending register request (\"%s\", %s/%s/%x)\n", _fea_target_name.c_str(), _ifname.c_str(), _vifname.c_str(), XORP_UINT_CAST(ETHER_TYPE)); bool s = c.send_register_receiver( _fea_target_name.c_str(), _r->instance_name(), _ifname, _vifname, ETHER_TYPE, "", false, callback(this, &TestRawLink::register_cb)); return s; } /** * Request closure of link. * * This is an asychronous request. If the request is successfully * queued for dispatch then true is returned. * * On success _sockid is cleared. On failure the number xrl errors is * bumped by one. */ bool unregister_link() { XrlRawLinkV0p1Client c(_r); verbose_log("Sending unregister request (\"%s\", %s/%s/%x)\n", _fea_target_name.c_str(), _ifname.c_str(), _vifname.c_str(), XORP_UINT_CAST(ETHER_TYPE)); bool s = c.send_unregister_receiver( _fea_target_name.c_str(), _r->instance_name(), _ifname, _vifname, ETHER_TYPE, "", callback(this, &TestRawLink::unregister_cb)); return s; } /** * Start sending packets. * * @param host address to send data to. * @param port to send data to. * @param bytes size of each packet. * @param ipg_ms interpacket gap in milliseconds. */ void start_sending(const Mac& dest, uint32_t bytes, uint32_t ipg_ms) { _t_snd = _e.new_periodic_ms(ipg_ms, callback(this, &TestRawLink::send_data, dest, bytes)); } /** * Stop sending packets. */ void stop_sending() { _t_snd.unschedule(); } protected: bool send_data(Mac dest, uint32_t bytes) { vector d(bytes, FILLER_VALUE); XrlRawLinkV0p1Client c(_r); bool s = c.send_send(_fea_target_name.c_str(), _ifname, _vifname, _src, dest, ETHER_TYPE, d, callback(this, &TestRawLink::send_data_cb)); if (s) { verbose_log("Sent %u(%u) bytes to %s\n", XORP_UINT_CAST(bytes), XORP_UINT_CAST(d.size()), dest.str().c_str()); _b_snd += bytes; _p_snd += 1; return true; } verbose_err("Send failed."); return false; } void send_data_cb(const XrlError& e) { if (e != XrlError::OKAY()) { verbose_err("Xrl Error: %s\n", e.str().c_str()); stop_sending(); _x_err ++; } } void register_cb(const XrlError& e) { if (e != XrlError::OKAY()) { verbose_err("Xrl Error: %s\n", e.str().c_str()); _x_err ++; return; } } void unregister_cb(const XrlError& e) { if (e != XrlError::OKAY()) { verbose_err("Xrl Error: %s\n", e.str().c_str()); _x_err ++; } _unregistered = true; } protected: XrlCmdError common_0_1_get_target_name(string& name) { name = _r->instance_name(); return XrlCmdError::OKAY(); } XrlCmdError common_0_1_get_version(string& version) { version = "0.1"; return XrlCmdError::OKAY(); } XrlCmdError common_0_1_get_status(uint32_t& status, string& reason) { status = PROC_READY; reason = ""; return XrlCmdError::OKAY(); } XrlCmdError common_0_1_shutdown() { return XrlCmdError::COMMAND_FAILED("Not supported"); } /** * Receive a raw link-level packet on an interface. * * @param if_name the interface name the packet arrived on. * @param vif_name the vif name the packet arrived on. * @param src_address the MAC source address. * @param dst_address the MAC destination address. * @param ether_type the EtherType protocol number or the Destination SAP. * It must be between 1536 and 65535 to specify the EtherType, or between * 1 and 255 to specify the Destination SAP for IEEE 802.2 LLC frames. * @param payload the payload, everything after the MAC header. */ XrlCmdError raw_link_client_0_1_recv( // Input values, const string& if_name, const string& vif_name, const Mac& src_address, const Mac& dst_address, const uint32_t& ether_type, const vector& payload) { if (_is_sender) { // If we're sending over loopback, we will always // see our own frames because both the source and // destination endpoint addresses are the same. // So keep RawLink instances strictly to // sending and receiving. return XrlCmdError::OKAY(); } verbose_log("Received %u bytes from %s\n", XORP_UINT_CAST(payload.size()), src_address.str().c_str()); _p_rcv += 1; XLOG_ASSERT(if_name == _ifname); XLOG_ASSERT(vif_name == _vifname); XLOG_ASSERT(dst_address == _src); XLOG_ASSERT(src_address == _src); XLOG_ASSERT(ether_type == ETHER_TYPE); // Now check the payload. for (size_t i = 0; i < payload.size(); i++) { if (payload[i] != FILLER_VALUE) { return XrlCmdError::COMMAND_FAILED("Bad data received"); } } _b_rcv += payload.size(); return XrlCmdError::OKAY(); } private: EventLoop& _e; XrlRouter* _r; string _fea_target_name; string _ifname; string _vifname; Mac _src; bool _is_sender; uint32_t _p_snd; // packets sent uint32_t _p_rcv; // packets received uint32_t _b_snd; // bytes sent uint32_t _b_rcv; // bytes received uint32_t _x_err; // xrl error count bool _unregistered; // link unregistered XorpTimer _t_snd; // send timer }; // ---------------------------------------------------------------------------- // Events static bool ticker() { static const char x[] = { '.', 'o', '0', 'O', '0', 'o' }; static char erase = '\0'; static int p = 0; fprintf(stderr, "%c%c", erase, x[p]); p = (p + 1) % (sizeof(x)); erase = '\b'; return true; } static void create_test_link(EventLoop* pe, string ifname, string vifname, Mac src, bool is_sender, TestRawLink** ppu) { verbose_log("Creating TestRawLink instance.\n"); *ppu = new TestRawLink(*pe, g_fea_target_name, g_finder_host, g_finder_port, ifname, vifname, src, is_sender); } static void destroy_test_link(TestRawLink** ppu) { delete *ppu; *ppu = 0; } static void register_test_link(TestRawLink** ppu, bool *err) { TestRawLink* pu = *ppu; bool s = pu->register_link(); if (s == false) *err = true; } static void start_sending(TestRawLink** ppu, Mac addr) { TestRawLink* pu = *ppu; pu->start_sending(addr, 512, 200); } static void stop_sending(TestRawLink** ppu) { TestRawLink* pu = *ppu; pu->stop_sending(); } static void match_bytes_sent_received(TestRawLink** pps /* sender */, TestRawLink** ppr /* receiver */, bool* eflag) { TestRawLink* ps = *pps; TestRawLink* pr = *ppr; if (ps->bytes_sent() != pr->bytes_received()) { verbose_err("Bytes sender sent %u do not match bytes receiver " "received %u\n", XORP_UINT_CAST(ps->bytes_sent()), XORP_UINT_CAST(pr->bytes_received())); *eflag = true; } } static void match_bytes_not_sent(TestRawLink** pps, size_t count, bool* eflag) { TestRawLink* ps = *pps; if (ps->bytes_sent() == count) { verbose_err("Bytes sender sent %u match expected %u\n", XORP_UINT_CAST(ps->bytes_sent()), XORP_UINT_CAST(count)); *eflag = true; } } static void match_bytes_received(TestRawLink** ppr, size_t count, bool* eflag) { TestRawLink* pr = *ppr; if (pr->bytes_received() != count) { verbose_err("Bytes receiver received %u does not match expected %u\n", XORP_UINT_CAST(pr->bytes_received()), XORP_UINT_CAST(count)); *eflag = true; } } static void unregister_test_link(TestRawLink** ppu) { TestRawLink* pu = *ppu; if (pu->unregister_link() == false) { verbose_err("Failed to send unregister link request.\n"); } verbose_log("Unregistering link\n"); } static void verify_unregistered(TestRawLink** ppu, bool unregistered, bool* eflag) { TestRawLink* pu = *ppu; if (pu->unregistered() != unregistered) { verbose_err("Link unregistered state (%d) does not match expected.\n", pu->unregistered()); *eflag = true; } } // ---------------------------------------------------------------------------- // Test Main int test_main(IPv4 finder_host, uint16_t finder_port, string ifname, string vifname) { EventLoop e; string fea_target_name = "fea"; vector ev; // Vector for timed events bool eflag(false); // Error flag set by timed events Mac mac; #ifndef HAVE_PCAP_H fprintf(stdout, "Test Skipped: No L2 I/O mechanism found: " "HAVE_PCAP_H is not defined.\n"); return 0; #endif // // Wait for FEA bringup and pull the IfTree. // bool expired = false; IfMgrXrlMirror ifmgr(e, fea_target_name.c_str(), finder_host, finder_port); ifmgr.startup(); XorpTimer t = e.set_flag_after_ms(3000, &expired); while (ifmgr.status() != SERVICE_RUNNING) { e.run(); if (expired) { verbose_err("ifmgr did not reach running state: " "state = %s note = \"%s\".\n", service_status_name(ifmgr.status()), ifmgr.status_note().c_str()); return -1; } } // // The interface must exist and be enabled. Retrieve its MAC address. // const IfMgrIfAtom* fi = ifmgr.iftree().find_interface(ifname); if (fi == NULL) { verbose_err("Could not find %s in iftree.\n", ifname.c_str()); ifmgr.shutdown(); return -1; } if (! fi->enabled()) { verbose_err("%s is disabled.\n", ifname.c_str()); ifmgr.shutdown(); return -1; } // For the Linux loopback interface the Mac is likely to be empty. mac = fi->mac(); if (mac.is_zero()) { verbose_log("%s has an empty MAC address.\n", ifname.c_str()); //ifmgr.shutdown(); //return -1; } // // The VIF must exist and be enabled. // const IfMgrVifAtom* fv = ifmgr.iftree().find_vif(ifname, vifname); if (fv == NULL) { verbose_err("Could not find %s/%s in iftree.\n", ifname.c_str(), vifname.c_str()); ifmgr.shutdown(); return -1; } if (! fv->enabled()) { verbose_err("%s/%s is disabled.\n", ifname.c_str(), vifname.c_str()); ifmgr.shutdown(); return -1; } if (verbose()) ev.push_back(e.new_periodic_ms(100, callback(ticker))); // // NOTE WELL: callback() only supports up to 6 bound (stored) // arguments for solitary C++ functions, therefore we end up // using a few global variables here. // g_fea_target_name = fea_target_name; g_finder_host = finder_host; g_finder_port = finder_port; // // Create rl1 sender. // TestRawLink* rl1; ev.push_back(e.new_oneoff_after_ms(1000, callback(create_test_link, &e, ifname, vifname, mac, true, &rl1))); // // Create rl2 listener link and register it. // TestRawLink* rl2; ev.push_back(e.new_oneoff_after_ms(2000, callback(create_test_link, &e, ifname, vifname, mac, false, &rl2))); ev.push_back(e.new_oneoff_after_ms(2500, callback(register_test_link, &rl2, &eflag))); // // Send packets from rl1 to rl2. // ev.push_back(e.new_oneoff_after_ms(10000, callback(start_sending, &rl1, mac))); ev.push_back(e.new_oneoff_after_ms(12000, callback(stop_sending, &rl1))); // // Check bytes from rl1 to rl2 match at either end. // ev.push_back(e.new_oneoff_after_ms(13000, callback(match_bytes_sent_received, &rl1, &rl2, &eflag))); // // Check rl2 is not unregistered // ev.push_back(e.new_oneoff_after_ms(15000, callback(verify_unregistered, &rl2, false, &eflag))); // // Unregister rl2 // ev.push_back(e.new_oneoff_after_ms(15500, callback(unregister_test_link, &rl2))); // // Check rl2 is unregistered. // ev.push_back(e.new_oneoff_after_ms(16000, callback(verify_unregistered, &rl2, true, &eflag))); // // Force exit. // bool finish = false; ev.push_back(e.set_flag_after_ms(26000, &finish)); while (eflag == false && finish == false) { e.run(); } if (eflag) { ifmgr.shutdown(); return 1; } // // Track the last send/receive counts. We need to run the // event loop above because everything is happening asynchronously, // and then run it again below. // size_t rl1_sent = rl1->bytes_sent(); size_t rl2_received = rl2->bytes_received(); ev.clear(); // // Send packets from rl1 to rl2. // ev.push_back(e.new_oneoff_after_ms(4000, callback(start_sending, &rl1, mac))); ev.push_back(e.new_oneoff_after_ms(6000, callback(stop_sending, &rl1))); // // Check rl1 sent something (ie the count changed) // ev.push_back(e.new_oneoff_after_ms(7000, callback(match_bytes_not_sent, &rl1, rl1_sent, &eflag))); // // Check rl2 did not receive any packets (ie the count did not change) // ev.push_back(e.new_oneoff_after_ms(7000, callback(match_bytes_received, &rl2, rl2_received, &eflag))); // // Destroy rl1 and rl2. // ev.push_back(e.new_oneoff_after_ms(9998, callback(destroy_test_link, &rl1))); ev.push_back(e.new_oneoff_after_ms(9998, callback(destroy_test_link, &rl2))); // // Force exit. // finish = false; ev.push_back(e.set_flag_after_ms(12000, &finish)); while (eflag == false && finish == false) { e.run(); } ifmgr.shutdown(); if (eflag) return 1; return 0; } /* ------------------------------------------------------------------------- */ static void usage(const char* progname) { fprintf(stderr, "usage: %s [-F :] [-v] \n", progname); fprintf(stderr, "Options:\n"); fprintf(stderr, " -F [:] " "Specify arguments for external Finder instance\n"); fprintf(stderr, " -v Verbose output\n"); fprintf(stderr, "Runs rawlink test using Xrls over /.\n"); exit(1); } static bool parse_finder_arg(const char* host_colon_port, IPv4& finder_addr, uint16_t& finder_port) { string finder_host; const char* p = strstr(host_colon_port, ":"); if (p) { finder_host = string(host_colon_port, p); finder_port = atoi(p + 1); if (finder_port == 0) { fprintf(stderr, "Invalid port \"%s\"\n", p + 1); return false; } } else { finder_host = string(host_colon_port); finder_port = FinderConstants::FINDER_DEFAULT_PORT(); } try { finder_addr = IPv4(finder_host.c_str()); } catch (const InvalidString& ) { // host string may need resolving in_addr ia; if (address_lookup(finder_host, ia) == false) { fprintf(stderr, "Invalid host \"%s\"\n", finder_host.c_str()); return false; } finder_addr.copy_in(ia); } return true; } // ---------------------------------------------------------------------------- // Main int main(int argc, char* const argv[]) { // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); IPv4 finder_addr = FinderConstants::FINDER_DEFAULT_HOST(); uint16_t finder_port = FinderConstants::FINDER_DEFAULT_PORT(); char* progname = argv[0]; int r = 0; // Return code int ch = 0; while ((ch = getopt(argc, argv, "F:v")) != -1) { switch (ch) { case 'F': if (parse_finder_arg(optarg, finder_addr, finder_port) == false) { usage(progname); r = 1; } break; case 'v': set_verbose(true); break; default: usage(argv[0]); r = 1; } } argc -= optind; argv += optind; if (argc != 2) usage(progname); string ifname(argv[0]); string vifname(argv[1]); try { if (r == 0) { r = test_main(finder_addr, finder_port, ifname, vifname); } } catch (...) { xorp_catch_standard_exceptions(); } // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); if (r != 0) return (1); return (0); } xorp/fea/tests/test_xrl_sockets4_tcp.sh0000775000076400007640000000275411421137511020451 0ustar greearbgreearb#!/usr/bin/env bash # # $XORP: xorp/fea/test_xrl_sockets4_tcp.sh,v 1.6 2007/08/11 01:06:10 pavlin Exp $ # # # Test FEA TCP support. # # This script started with no arguments will start all required process and # terminate them at the end of the tests. # set -e onexit() { last=$? if [ $last = "0" ] then echo "$0: Tests Succeeded" else echo "$0: Tests Failed" fi trap '' 0 2 } trap onexit 0 2 # Conditionally set ${srcdir} if it wasn't assigned (e.g., by `gmake check`) if [ "X${srcdir}" = "X" ] ; then srcdir=`dirname $0` ; fi . ${srcdir}/../utils/xrl_shell_lib.sh configure_fea() { echo "Configuring fea" export CALLXRL FEA_FUNCS=${srcdir}/xrl_shell_funcs.sh local tid=$($FEA_FUNCS start_fea_transaction) $FEA_FUNCS configure_all_interfaces_from_system $tid true $FEA_FUNCS commit_fea_transaction $tid } test_xrl_sockets4_tcp() { ./test_xrl_sockets4_tcp } TESTS_NOT_FIXED="" TESTS="test_xrl_sockets4_tcp" # Include command line RUNITDIR="../utils" . ${srcdir}/../utils/args.sh if [ $START_PROGRAMS = "yes" ] ; then CXRL="$CALLXRL -r 10" runit $QUIET $VERBOSE -c "$0 -s -c $*" < #endif #include "libxipc/sockutil.hh" #include "libxipc/xrl_std_router.hh" #include "xrl/interfaces/socket4_xif.hh" #include "xrl/targets/test_socket4_base.hh" static const uint8_t FILLER_VALUE = 0xe7; // --------------------------------------------------------------------------- // Verbose output control static bool s_verbose = false; bool verbose() { return s_verbose; } void set_verbose(bool v) { s_verbose = v; } #define verbose_log(x...) \ do { \ if (verbose()) { \ printf("From %s:%d: ", __FILE__, __LINE__); \ printf(x); \ fflush(stdout); \ } \ } while(0) #define verbose_err(x...) \ do { \ printf("From %s:%d: ", __FILE__, __LINE__); \ printf(x); \ fflush(stdout); \ } while(0) // ---------------------------------------------------------------------------- // UDP client class TestSocket4UDP : public XrlTestSocket4TargetBase { public: TestSocket4UDP(EventLoop& e, const string& fea_target_name, IPv4 finder_host, uint16_t finder_port) : _e(e), _fea_target_name(fea_target_name), _p_snd(0), _p_rcv(0), _b_snd(0), _b_rcv(0), _x_err(0), _closed(false) { _r = new XrlStdRouter(_e, "test_xrl_socket", finder_host, finder_port); set_command_map(_r); _r->finalize(); } ~TestSocket4UDP() { set_command_map(0); delete _r; _r = 0; } uint32_t bytes_received() const { return _b_rcv; } uint32_t bytes_sent() const { return _b_snd; } uint32_t packets_received() const { return _p_rcv; } uint32_t packets_sent() const { return _p_snd; } uint32_t xrl_errors() const { return _x_err; } bool closed() const { return _closed; } /** * Bind to interface and port. * * This is an asynchronous request. If the request is * successfully queued for dispatch then true is returned. * * Subsequently, if the request is successful _sockid is set to a valid * socket identifier, and if unsuccessful the number of xrl errors is * bumped by one. */ bool bind(IPv4 addr, uint16_t port) { XrlSocket4V0p1Client c(_r); verbose_log("Sending bind request (\"%s\", %s/%u)\n", _fea_target_name.c_str(), addr.str().c_str(), port); bool s = c.send_udp_open_and_bind( _fea_target_name.c_str(), _r->instance_name(), addr, port, callback(this, &TestSocket4UDP::bind_cb)); return s; } /** * Request closure of socket. * * This is an asychronous request. If the request is successfully * queued for dispatch then true is returned. * * On success _sockid is cleared. On failure the number xrl errors is * bumped by one. */ bool close() { XrlSocket4V0p1Client c(_r); bool s = c.send_close(_fea_target_name.c_str(), _sockid, callback(this, &TestSocket4UDP::close_cb)); return s; } /** * Start sending packets. * * @param host address to send data to. * @param port to send data to. * @param bytes size of each packet. * @param ipg_ms interpacket gap in milliseconds. */ void start_sending(IPv4 host, uint16_t port, uint32_t bytes, uint32_t ipg_ms) { _t_snd = _e.new_periodic_ms(ipg_ms, callback(this, &TestSocket4UDP::send_data, host, port, bytes)); } /** * Stop sending packets. */ void stop_sending() { _t_snd.unschedule(); } protected: bool send_data(IPv4 host, uint16_t port, uint32_t bytes) { vector d(bytes, FILLER_VALUE); XrlSocket4V0p1Client c(_r); bool s = c.send_send_to(_fea_target_name.c_str(), _sockid, host, port, d, callback(this, &TestSocket4UDP::send_data_cb)); if (s) { verbose_log("Sent %u bytes to %s/%u\n", bytes, host.str().c_str(), port); _b_snd += bytes; _p_snd += 1; return true; } verbose_err("Send failed."); return false; } void send_data_cb(const XrlError& e) { if (e != XrlError::OKAY()) { verbose_err("Xrl Error: %s\n", e.str().c_str()); stop_sending(); _x_err ++; } } void bind_cb(const XrlError& e, const string* psockid) { if (e != XrlError::OKAY()) { verbose_err("Xrl Error: %s\n", e.str().c_str()); _x_err ++; return; } _sockid = *psockid; } void close_cb(const XrlError& e) { if (e != XrlError::OKAY()) { verbose_err("Xrl Error: %s\n", e.str().c_str()); _x_err ++; } _sockid.erase(); _closed = true; } protected: XrlCmdError common_0_1_get_target_name(string& name) { name = _r->instance_name(); return XrlCmdError::OKAY(); } XrlCmdError common_0_1_get_version(string& version) { version = "0.1"; return XrlCmdError::OKAY(); } XrlCmdError common_0_1_get_status(uint32_t& status, string& reason) { status = PROC_READY; reason = ""; return XrlCmdError::OKAY(); } XrlCmdError common_0_1_shutdown() { return XrlCmdError::COMMAND_FAILED("Not supported"); } XrlCmdError socket4_user_0_1_recv_event(const string& sockid, const string& if_name, const string& vif_name, const IPv4& src_host, const uint32_t& src_port, const vector& data) { UNUSED(if_name); UNUSED(vif_name); XLOG_ASSERT(sockid == _sockid); verbose_log("Received data from %s %u\n", src_host.str().c_str(), src_port); _p_rcv += 1; for (size_t i = 0; i < data.size(); i++) { if (data[i] != FILLER_VALUE) { return XrlCmdError::COMMAND_FAILED("Bad data received"); } } _b_rcv += data.size(); return XrlCmdError::OKAY(); } XrlCmdError socket4_user_0_1_inbound_connect_event(const string& /* sockid */, const IPv4& /* src_host */, const uint32_t& /* src_port */, const string& /* new_sockid */, bool& /* accept */) { return XrlCmdError::COMMAND_FAILED("Not implemented"); } XrlCmdError socket4_user_0_1_outgoing_connect_event(const string& /* sockid */) { return XrlCmdError::COMMAND_FAILED("Not implemented"); } XrlCmdError socket4_user_0_1_error_event(const string& sockid, const string& error, const bool& fatal) { XLOG_ASSERT(sockid == _sockid); verbose_log("error event: %s (fatal = %d)\n", error.c_str(), fatal); return XrlCmdError::OKAY(); } XrlCmdError socket4_user_0_1_disconnect_event(const string& sockid) { XLOG_ASSERT(sockid == _sockid); verbose_log("disconnect event\n"); _closed = true; return XrlCmdError::OKAY(); } private: EventLoop& _e; XrlRouter* _r; string _fea_target_name; string _sockid; uint32_t _p_snd; // packets sent uint32_t _p_rcv; // packets received uint32_t _b_snd; // bytes sent uint32_t _b_rcv; // bytes received uint32_t _x_err; // xrl error count bool _closed; // close received XorpTimer _t_snd; // send timer }; // ---------------------------------------------------------------------------- // Events static bool ticker() { static const char x[] = { '.', 'o', '0', 'O', '0', 'o' }; static char erase = '\0'; static int p = 0; fprintf(stderr, "%c%c", erase, x[p]); p = (p + 1) % (sizeof(x)); erase = '\b'; return true; } static void create_test_socket(EventLoop* pe, string fea_target_name, IPv4 finder_host, uint16_t finder_port, TestSocket4UDP** ppu) { verbose_log("Creating TestSocket4UDP instance.\n"); *ppu = new TestSocket4UDP(*pe, fea_target_name, finder_host, finder_port); } static void destroy_test_socket(TestSocket4UDP** ppu) { delete *ppu; *ppu = 0; } static void bind_test_socket(TestSocket4UDP** ppu, IPv4 addr, uint16_t port, bool *err) { TestSocket4UDP* pu = *ppu; bool s = pu->bind(addr, port); if (s == false) *err = true; } static void start_sending(TestSocket4UDP** ppu, IPv4 addr, uint16_t port) { TestSocket4UDP* pu = *ppu; pu->start_sending(addr, port, 512, 200); } static void stop_sending(TestSocket4UDP** ppu) { TestSocket4UDP* pu = *ppu; pu->stop_sending(); } static void match_bytes_sent_received(TestSocket4UDP** pps /* sender */, TestSocket4UDP** ppr /* receiver */, bool* eflag) { TestSocket4UDP* ps = *pps; TestSocket4UDP* pr = *ppr; if (ps->bytes_sent() != pr->bytes_received()) { verbose_err("Bytes sender sent do not match bytes receiver " "received\n"); *eflag = true; } } static void close_test_socket(TestSocket4UDP** ppu) { TestSocket4UDP* pu = *ppu; if (pu->close() == false) { verbose_err("Failed to send close socket request.\n"); } verbose_log("Closing socket\n"); } static void verify_closed(TestSocket4UDP** ppu, bool closed, bool* eflag) { TestSocket4UDP* pu = *ppu; if (pu->closed() != closed) { verbose_err("Socket close state (%d) does not matched expected.\n", pu->closed()); *eflag = true; } } // ---------------------------------------------------------------------------- // Test Main int test_main(IPv4 finder_host, uint16_t finder_port) { EventLoop e; const string fea_target_name = "fea"; vector ev; // Vector for timed events bool eflag(false); // Error flag set by timed events if (verbose()) ev.push_back(e.new_periodic_ms(100, callback(ticker))); // // Create udp1 socket and bind to port 5000. // TestSocket4UDP* udp1; ev.push_back(e.new_oneoff_after_ms(1000, callback(create_test_socket, &e, fea_target_name, finder_host, finder_port, &udp1))); ev.push_back(e.new_oneoff_after_ms(1500, callback(bind_test_socket, &udp1, IPv4::LOOPBACK(), uint16_t(5000), &eflag))); // // Create udp2 socket and bind to port 5001. // TestSocket4UDP* udp2; ev.push_back(e.new_oneoff_after_ms(2000, callback(create_test_socket, &e, fea_target_name, finder_host, finder_port, &udp2))); ev.push_back(e.new_oneoff_after_ms(2500, callback(bind_test_socket, &udp2, IPv4::LOOPBACK(), uint16_t(5001), &eflag))); // // Send packets from udp2/5001 to udp1/5000 // ev.push_back(e.new_oneoff_after_ms(3000, callback(start_sending, &udp2, IPv4::LOOPBACK(), uint16_t(5000)))); ev.push_back(e.new_oneoff_after_ms(10000, callback(stop_sending, &udp2))); // // Send packets from udp1/5000 to udp2/5001 // ev.push_back(e.new_oneoff_after_ms(10000, callback(start_sending, &udp1, IPv4::LOOPBACK(), uint16_t(5001)))); ev.push_back(e.new_oneoff_after_ms(12000, callback(stop_sending, &udp1))); // // Check bytes from udp2 to udp1 match at either end. // ev.push_back(e.new_oneoff_after_ms(13000, callback(match_bytes_sent_received, &udp2, &udp1, &eflag))); // // Check bytes from udp1 to udp2 match at either end. // ev.push_back(e.new_oneoff_after_ms(13000, callback(match_bytes_sent_received, &udp1, &udp2, &eflag))); // // Check udp1 is not closed // ev.push_back(e.new_oneoff_after_ms(13500, callback(verify_closed, &udp1, false, &eflag))); // // Close udp1 // ev.push_back(e.new_oneoff_after_ms(14000, callback(close_test_socket, &udp1))); // // Check udp1 closed // ev.push_back(e.new_oneoff_after_ms(14500, callback(verify_closed, &udp1, true, &eflag))); // // Check udp2 is not closed // ev.push_back(e.new_oneoff_after_ms(15000, callback(verify_closed, &udp2, false, &eflag))); // // Close udp2 // ev.push_back(e.new_oneoff_after_ms(15500, callback(close_test_socket, &udp2))); // // Check udp2 is closed. // ev.push_back(e.new_oneoff_after_ms(16000, callback(verify_closed, &udp2, true, &eflag))); // // Destroy udp1 and udp2. // ev.push_back(e.new_oneoff_after_ms(19998, callback(destroy_test_socket, &udp1))); ev.push_back(e.new_oneoff_after_ms(19998, callback(destroy_test_socket, &udp2))); // // Force exit. // bool finish(false); ev.push_back(e.set_flag_after_ms(26000, &finish)); while (eflag == false && finish == false) { e.run(); } if (eflag) return 1; return 0; } /* ------------------------------------------------------------------------- */ static void usage(const char* progname) { fprintf(stderr, "usage: %s [-F :] [-v]\n", progname); fprintf(stderr, "Options:\n"); fprintf(stderr, " -F [:] " "Specify arguments for external Finder instance\n"); fprintf(stderr, " -v Verbose output\n"); fprintf(stderr, "Runs remote UDP socket test using Xrls.\n"); exit(1); } static bool parse_finder_arg(const char* host_colon_port, IPv4& finder_addr, uint16_t& finder_port) { string finder_host; const char* p = strstr(host_colon_port, ":"); if (p) { finder_host = string(host_colon_port, p); finder_port = atoi(p + 1); if (finder_port == 0) { fprintf(stderr, "Invalid port \"%s\"\n", p + 1); return false; } } else { finder_host = string(host_colon_port); finder_port = FinderConstants::FINDER_DEFAULT_PORT(); } try { finder_addr = IPv4(finder_host.c_str()); } catch (const InvalidString& ) { // host string may need resolving in_addr ia; if (address_lookup(finder_host, ia) == false) { fprintf(stderr, "Invalid host \"%s\"\n", finder_host.c_str()); return false; } finder_addr.copy_in(ia); } return true; } // ---------------------------------------------------------------------------- // Main int main(int argc, char* const argv[]) { // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); IPv4 finder_addr = FinderConstants::FINDER_DEFAULT_HOST(); uint16_t finder_port = FinderConstants::FINDER_DEFAULT_PORT(); int r = 0; // Return code int ch = 0; while ((ch = getopt(argc, argv, "F:v")) != -1) { switch (ch) { case 'F': if (parse_finder_arg(optarg, finder_addr, finder_port) == false) { usage(argv[0]); r = 1; } break; case 'v': set_verbose(true); break; default: usage(argv[0]); r = 1; } } argc -= optind; argv += optind; try { if (r == 0) { r = test_main(finder_addr, finder_port); } } catch (...) { xorp_catch_standard_exceptions(); } // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); if (r != 0) return (1); return (0); } xorp/fea/tests/test_xrl_sockets4_tcp.cc0000664000076400007640000010466711540224224020427 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2007-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/status_codes.h" #include "libxorp/xorpfd.hh" #ifdef HAVE_GETOPT_H #include #endif #include "libxipc/sockutil.hh" #include "libxipc/xrl_std_router.hh" #include "xrl/interfaces/socket4_xif.hh" #include "xrl/targets/test_socket4_base.hh" static const uint8_t FILLER_VALUE = 0xe7; // --------------------------------------------------------------------------- // Verbose output control static bool s_verbose = false; bool verbose() { return s_verbose; } void set_verbose(bool v) { s_verbose = v; } #define verbose_log(x...) \ do { \ if (verbose()) { \ printf("From %s:%d: ", __FILE__, __LINE__); \ printf(x); \ fflush(stdout); \ } \ } while(0) #define verbose_err(x...) \ do { \ printf("From %s:%d: ", __FILE__, __LINE__); \ printf(x); \ fflush(stdout); \ } while(0) #define file_location() c_format("%s: %d", __FILE__, __LINE__) // // ---------------------------------------------------------------------------- // Scheduling Time class class SchedulingTime { private: int _time; int _increment; public: SchedulingTime(int start_time = 0, int increment = 250) : _time(start_time), _increment(increment) {} int now() { return _time; } int next() { _time += _increment; return _time; } int next(int inc) { _time += inc; return _time; } }; // // ---------------------------------------------------------------------------- // TCP client/server commonalities class class TestSocket4TCP : public XrlTestSocket4TargetBase { public: TestSocket4TCP(EventLoop& e, const string& fea_target_name, IPv4 finder_host, uint16_t finder_port) : _e(e), _fea_target_name(fea_target_name), _p_snd(0), _p_rcv(0), _b_snd(0), _b_rcv(0), _x_err(0) { _r = new XrlStdRouter(_e, "test_xrl_socket", finder_host, finder_port); set_command_map(_r); _r->finalize(); } ~TestSocket4TCP() { set_command_map(0); delete _r; _r = 0; } uint32_t bytes_received() const { return _b_rcv; } uint32_t bytes_sent() const { return _b_snd; } uint32_t packets_received() const { return _p_rcv; } uint32_t packets_sent() const { return _p_snd; } uint32_t xrl_errors() const { return _x_err; } /** * Stop sending packets. */ void stop_sending() { _t_snd.unschedule(); } /** * Send the specified number of bytes of data through the given socket. * * On success, the number of sent bytes and packets is bumped up * accordingly, and true is returned. On failure the number of xrl * errors is bumped by one, and the sending timer stopped. */ bool send_data(string& sockid, uint32_t bytes) { vector data(bytes, FILLER_VALUE); XrlSocket4V0p1Client c(_r); if (c.send_send(_fea_target_name.c_str(), sockid, data, callback(this, &TestSocket4TCP::send_data_cb))) { verbose_log("Sent %u bytes...\n", bytes); _b_snd += bytes; _p_snd += 1; return true; } return false; } protected: virtual bool send_data(uint32_t bytes) = 0; virtual void send_data_cb(const XrlError& e) = 0; public: virtual void start_sending(uint32_t bytes, uint32_t ipg_ms) = 0; protected: XrlCmdError common_0_1_get_target_name(string& name) { name = _r->instance_name(); return XrlCmdError::OKAY(); } XrlCmdError common_0_1_get_version(string& version) { version = "0.1"; return XrlCmdError::OKAY(); } XrlCmdError common_0_1_get_status(uint32_t& status, string& reason) { status = PROC_READY; reason = ""; return XrlCmdError::OKAY(); } XrlCmdError common_0_1_shutdown() { return XrlCmdError::COMMAND_FAILED("Not supported"); } XrlCmdError socket4_user_0_1_recv_event(const string& sockid, const string& if_name, const string& vif_name, const IPv4& src_host, const uint32_t& src_port, const vector& data) = 0; XrlCmdError socket4_user_0_1_inbound_connect_event(const string& sockid, const IPv4& src_host, const uint32_t& src_port, const string& new_sockid, bool& accept) = 0; XrlCmdError socket4_user_0_1_outgoing_connect_event(const string& sockid) = 0; XrlCmdError socket4_user_0_1_error_event(const string& sockid, const string& error, const bool& fatal) = 0; XrlCmdError socket4_user_0_1_disconnect_event(const string& sockid) = 0; EventLoop& _e; XrlRouter* _r; string _fea_target_name; uint32_t _p_snd; // packets sent uint32_t _p_rcv; // packets received uint32_t _b_snd; // bytes sent uint32_t _b_rcv; // bytes received uint32_t _x_err; // xrl error count XorpTimer _t_snd; // send timer }; // // ---------------------------------------------------------------------------- // TCP Server class TestSocket4TCPServer : public TestSocket4TCP { public: TestSocket4TCPServer(EventLoop& e, const string& fea_target_name, IPv4 finder_host, uint16_t finder_port) : TestSocket4TCP(e, fea_target_name, finder_host, finder_port), _server_closed(true), _client_closed(true) {} bool server_closed() const { return _server_closed; } bool client_closed() const { return _client_closed; } /** * Bind to interface and port. * * This is an asynchronous request. If the request is * successfully queued for dispatch then true is returned. * * Subsequently, if the request is successful _server_sockid is set to a * valid socket identifier, and _server_closed set to false. * If unsuccessful the number of xrl errors is bumped by one. */ bool bind(IPv4 addr, uint16_t port) { XrlSocket4V0p1Client c(_r); verbose_log("Sending bind (%s:%u) request.\n", addr.str().c_str(), port); return c.send_tcp_open_and_bind( _fea_target_name.c_str(), _r->instance_name(), addr, port, callback(this, &TestSocket4TCPServer::bind_cb)); } /** * Listen on the server socket. * * This is an asynchronous request. If the request is * successfully queued for dispatch then true is returned. */ bool listen(uint32_t backlog) { XrlSocket4V0p1Client c(_r); verbose_log("Sending listen request.\n"); bool s = c.send_tcp_listen( _fea_target_name.c_str(), _server_sockid, backlog, callback(this, &TestSocket4TCPServer::listen_cb)); return s; } /** * Request closure of the server socket. * * This is an asychronous request. If the request is successfully * queued for dispatch then true is returned. * * On success _server_sockid is cleared. On failure the number xrl * errors is bumped by one. */ bool close_server() { XrlSocket4V0p1Client c(_r); verbose_log("Sending close request for server socket.\n"); return c.send_close(_fea_target_name.c_str(), _server_sockid, callback(this, &TestSocket4TCPServer::close_server_cb)); } /** * Request closure of the client socket (request a disconnect * of the client). * * This is an asychronous request. If the request is successfully * queued for dispatch then true is returned. * * On success _client_sockid is cleared. On failure the number xrl * errors is bumped by one. */ bool close_client() { XrlSocket4V0p1Client c(_r); verbose_log("Sending close request for server client socket.\n"); return c.send_close(_fea_target_name.c_str(), _client_sockid, callback(this, &TestSocket4TCPServer::close_client_cb)); } /** * Send data to the connected client, through the client socket. * * For implementation, see TestSocket4TCP::send_data. */ bool send_data(uint32_t bytes) { return TestSocket4TCP::send_data(_client_sockid, bytes); } /** * Start sending packets. * * @param bytes size of each packet. * @param ipg_ms interpacket gap in milliseconds. */ void start_sending(uint32_t bytes, uint32_t ipg_ms) { _t_snd = _e.new_periodic_ms(ipg_ms, callback(this, &TestSocket4TCPServer::send_data, bytes)); } protected: // // Callback functions // void bind_cb(const XrlError& e, const string* psockid) { if (e != XrlError::OKAY()) { verbose_err("Xrl Error: %s\n", e.str().c_str()); _x_err++; return; } _server_sockid = *psockid; _server_closed = false; } void listen_cb(const XrlError& e) { if (e != XrlError::OKAY()) { verbose_err("Xrl Error: %s\n", e.str().c_str()); _x_err++; return; } } void close_server_cb(const XrlError& e) { if (e != XrlError::OKAY()) { verbose_err("Xrl Error: %s\n", e.str().c_str()); _x_err++; } _server_sockid.erase(); _server_closed = true; } void close_client_cb(const XrlError& e) { if (e != XrlError::OKAY()) { verbose_err("Xrl Error: %s\n", e.str().c_str()); _x_err++; } _client_sockid.erase(); _client_closed = true; } void send_data_cb(const XrlError& e) { if (e != XrlError::OKAY()) { verbose_err("Xrl Error: %s\n", e.str().c_str()); stop_sending(); _x_err++; } } // // Socket User Interface functions // XrlCmdError socket4_user_0_1_recv_event(const string& sockid, const string& if_name, const string& vif_name, const IPv4& src_host, const uint32_t& src_port, const vector& data) { UNUSED(if_name); UNUSED(vif_name); // should only be receiving data on the client socket XLOG_ASSERT(_client_sockid == sockid); verbose_log("Server received %u bytes from %s:%u\n", XORP_UINT_CAST(data.size()), src_host.str().c_str(), src_port); _p_rcv += 1; for (size_t i = 0; i < data.size(); i++) { if (data[i] != FILLER_VALUE) { return XrlCmdError::COMMAND_FAILED("Bad data received."); } } _b_rcv += data.size(); return XrlCmdError::OKAY(); } XrlCmdError socket4_user_0_1_inbound_connect_event(const string& sockid, const IPv4& src_host, const uint32_t& src_port, const string& new_sockid, bool& accept) { // should only be receiving connections from the server socket XLOG_ASSERT(sockid == _server_sockid); verbose_log("Accepting connection from %s:%d\n", src_host.str().c_str(), src_port); accept = true; // Here we would decide whether to accept or reject. _client_sockid = new_sockid; _client_closed = false; return XrlCmdError::OKAY(); } XrlCmdError socket4_user_0_1_outgoing_connect_event(const string& sockid) { // should only be originating connections from the client socket XLOG_ASSERT(sockid == _server_sockid); return XrlCmdError::COMMAND_FAILED("Not supported"); } XrlCmdError socket4_user_0_1_error_event(const string& sockid, const string& error, const bool& fatal) { // socket must be one of the two... XLOG_ASSERT(sockid == _server_sockid || sockid == _client_sockid); verbose_log("Server error event on %s socket: %s (fatal = %d)\n", (sockid == _server_sockid) ? "server" : "client", error.c_str(), fatal); return XrlCmdError::OKAY(); } XrlCmdError socket4_user_0_1_disconnect_event(const string& sockid) { // socket must be one of the two... XLOG_ASSERT(sockid == _server_sockid || sockid == _client_sockid); verbose_log("Server disconnect event on %s socket\n", (sockid == _server_sockid) ? "server" : "client"); if (sockid == _server_sockid) { _server_closed = true; } else { _client_closed = true; } return XrlCmdError::OKAY(); } private: string _server_sockid; string _client_sockid; bool _server_closed; // flag indicating if server socket is closed bool _client_closed; // flag indicating if client socket is closed }; // // ---------------------------------------------------------------------------- // TCP Client class TestSocket4TCPClient : public TestSocket4TCP { public: TestSocket4TCPClient(EventLoop& e, const string& fea_target_name, IPv4 finder_host, uint16_t finder_port) : TestSocket4TCP(e, fea_target_name, finder_host, finder_port), _closed(true), _connected(false) {} bool closed() const { return _closed; } bool connected() const { return _connected; } /** * Bind to interface and port, and connect to remote address and port. * * Subsequently, if the request is successful _sockid is set to * a valid socket identifier, and _closed set to false. * If unsuccessful the number of xrl errors is bumped by one. */ bool bind_connect(IPv4 local_addr, uint16_t local_port, IPv4 remote_addr, uint16_t remote_port) { XrlSocket4V0p1Client c(_r); verbose_log("Sending bind (%s/%u) and connect request (\"%s\", %s/%u)\n", local_addr.str().c_str(), local_port, _fea_target_name.c_str(), remote_addr.str().c_str(), remote_port); return c.send_tcp_open_bind_connect( _fea_target_name.c_str(), _r->instance_name(), local_addr, local_port, remote_addr, remote_port, callback(this, &TestSocket4TCPClient::bind_connect_cb)); } /** * Request closure of the client socket. * * On success _sockid is cleared. On failure the number xrl * errors is bumped by one. */ bool close() { XrlSocket4V0p1Client c(_r); verbose_log("Sending close request for client socket.\n"); return c.send_close(_fea_target_name.c_str(), _sockid, callback(this, &TestSocket4TCPClient::close_cb)); } /** * Send data to the server through the client socket. * * For implementation, see TestSocket4TCP::send_data. */ bool send_data(uint32_t bytes) { return TestSocket4TCP::send_data(_sockid, bytes); } /** * Start sending packets. * * @param bytes size of each packet. * @param ipg_ms interpacket gap in milliseconds. */ void start_sending(uint32_t bytes, uint32_t ipg_ms) { _t_snd = _e.new_periodic_ms(ipg_ms, callback(this, &TestSocket4TCPClient::send_data, bytes)); } protected: // // Callback functions // void bind_connect_cb(const XrlError& e, const string* psockid) { if (e != XrlError::OKAY()) { verbose_err("Xrl Error: %s\n", e.str().c_str()); _x_err++; return; } _sockid = *psockid; _closed = false; } void close_cb(const XrlError& e) { if (e != XrlError::OKAY()) { verbose_err("Xrl Error: %s\n", e.str().c_str()); _x_err++; } _sockid.erase(); _closed = true; _connected = false; } void send_data_cb(const XrlError& e) { if (e != XrlError::OKAY()) { verbose_err("Xrl Error: %s\n", e.str().c_str()); stop_sending(); _x_err++; } } // // Socket User Interface functions // XrlCmdError socket4_user_0_1_recv_event(const string& sockid, const string& if_name, const string& vif_name, const IPv4& src_host, const uint32_t& src_port, const vector& data) { UNUSED(if_name); UNUSED(vif_name); XLOG_ASSERT(_sockid == sockid); verbose_log("Client received %u bytes from %s:%u\n", XORP_UINT_CAST(data.size()), src_host.str().c_str(), src_port); _p_rcv += 1; for (size_t i = 0; i < data.size(); i++) { if (data[i] != FILLER_VALUE) { return XrlCmdError::COMMAND_FAILED("Bad data received."); } } _b_rcv += data.size(); return XrlCmdError::OKAY(); } XrlCmdError socket4_user_0_1_inbound_connect_event(const string& /* sockid */, const IPv4& /* src_host */, const uint32_t& /* src_port */, const string& /* new_sockid */, bool& /* accept */) { return XrlCmdError::COMMAND_FAILED("Not supported"); } XrlCmdError socket4_user_0_1_outgoing_connect_event(const string& sockid) { XLOG_ASSERT(_sockid == sockid); verbose_log("Client connected to server\n"); _connected = true; return XrlCmdError::OKAY(); } XrlCmdError socket4_user_0_1_error_event(const string& sockid, const string& error, const bool& fatal) { XLOG_ASSERT(sockid == _sockid); verbose_log("Client socket error: %s (fatal = %d)\n", error.c_str(), fatal); return XrlCmdError::OKAY(); } XrlCmdError socket4_user_0_1_disconnect_event(const string& sockid) { XLOG_ASSERT(sockid == _sockid); verbose_log("Client disconnect event\n"); _closed = true; _connected = false; return XrlCmdError::OKAY(); } private: string _sockid; // socket used to connect to server bool _closed; // flag indicating whether socket is closed bool _connected; // flag indicating whether the socket is connected }; // // ---------------------------------------------------------------------------- // Main test setup functions static bool ticker() { static const char x[] = { '.', 'o', '0', 'O', '0', 'o' }; static char erase = '\0'; static int p = 0; fprintf(stderr, "%c%c", erase, x[p]); p = (p + 1) % (sizeof(x)); erase = '\b'; return true; } // // ---------------------------------------------------------------------------- // Server functions static void create_server(EventLoop* pe, string fea_target_name, IPv4 finder_host, uint16_t finder_port, TestSocket4TCP** ppu) { verbose_log("Creating TestSocket4TCPServer instance.\n"); *ppu = new TestSocket4TCPServer(*pe, fea_target_name, finder_host, finder_port); } static void bind_server(TestSocket4TCP** ppu, IPv4 addr, uint16_t port, bool *err) { TestSocket4TCPServer* pu = dynamic_cast(*ppu); bool s = pu->bind(addr, port); if (s == false) *err = true; } static void listen_server(TestSocket4TCP** ppu, uint16_t backlog, bool *err) { TestSocket4TCPServer* pu = dynamic_cast(*ppu); bool s = pu->listen(backlog); if (s == false) *err = true; } static void close_server_socket(TestSocket4TCP** ppu) { verbose_log("Closing server socket.\n"); TestSocket4TCPServer* pu = dynamic_cast(*ppu); if (pu->close_server() == false) { verbose_err("Failed to send close server socket request.\n"); } } static void verify_server_closed(TestSocket4TCP** ppu, bool closed, bool* eflag, string location) { TestSocket4TCPServer* pu = dynamic_cast(*ppu); if (pu->server_closed() != closed) { verbose_err("Server socket close state (%s) " "does not matched expected (%s) location %s\n", bool_c_str(pu->server_closed()), bool_c_str(closed), location.c_str()); *eflag = true; } } static void close_server_client_socket(TestSocket4TCP** ppu) { verbose_log("Closing server client socket.\n"); TestSocket4TCPServer* pu = dynamic_cast(*ppu); if (pu->close_client() == false) { verbose_err("Failed to send server close client socket request.\n"); } } static void verify_server_client_closed(TestSocket4TCP** ppu, bool closed, bool* eflag, string location) { TestSocket4TCPServer* pu = dynamic_cast(*ppu); if (pu->client_closed() != closed) { verbose_err("Server client socket close state (%s) " "does not matched expected (%s) location %s\n", bool_c_str(pu->client_closed()), bool_c_str(closed), location.c_str()); *eflag = true; } } // // ---------------------------------------------------------------------------- // Client functions static void create_client(EventLoop* pe, string fea_target_name, IPv4 finder_host, uint16_t finder_port, TestSocket4TCP** ppu) { verbose_log("Creating TestSocket4TCPClient instance.\n"); *ppu = new TestSocket4TCPClient(*pe, fea_target_name, finder_host, finder_port); } static void bind_connect_client(TestSocket4TCP** ppu, IPv4 local_addr, uint16_t local_port, IPv4 remote_addr, uint16_t remote_port, bool *err) { TestSocket4TCPClient* pu = dynamic_cast(*ppu); bool s = pu->bind_connect(local_addr, local_port, remote_addr, remote_port); if (s == false) *err = true; } static void close_client_socket(TestSocket4TCP** ppu) { verbose_log("Closing client socket.\n"); TestSocket4TCPClient* pu = dynamic_cast(*ppu); if (pu->close() == false) { verbose_err("Failed to send close client socket request.\n"); } } static void verify_client_closed(TestSocket4TCP** ppu, bool closed, bool* eflag, string location) { TestSocket4TCPClient* pu = dynamic_cast(*ppu); if (pu->closed() != closed) { verbose_err("Client socket close state (%s) " "does not matched expected (%s) location %s\n", bool_c_str(pu->closed()), bool_c_str(closed), location.c_str()); *eflag = true; } } static void verify_client_connected(TestSocket4TCP** ppu, bool connected, bool* eflag, string location) { TestSocket4TCPClient* pu = dynamic_cast(*ppu); if (pu->connected() != connected) { verbose_err("Client socket connected state (%s) " "does not matched expected (%s) location %s\n", bool_c_str(pu->connected()), bool_c_str(connected), location.c_str()); *eflag = true; } } // // ---------------------------------------------------------------------------- // Common functions static void destroy_test_socket(TestSocket4TCP** ppu) { delete *ppu; *ppu = 0; } static void start_sending(TestSocket4TCP** ppu) { TestSocket4TCP* pu = *ppu; pu->start_sending(512, 200); } static void stop_sending(TestSocket4TCP** ppu) { TestSocket4TCP* pu = *ppu; pu->stop_sending(); } static void match_bytes_sent_received(TestSocket4TCP** pps /* sender */, TestSocket4TCP** ppr /* receiver */, bool* eflag) { TestSocket4TCP* ps = *pps; TestSocket4TCP* pr = *ppr; if (ps->bytes_sent() != pr->bytes_received()) { verbose_err("Bytes sender sent[%d] do not match bytes receiver " "received[%d]\n", ps->bytes_sent(), pr->bytes_received()); *eflag = true; } else { verbose_log("Bytes sent equals bytes received.\n"); } } // ---------------------------------------------------------------------------- // Test Main int test_main(IPv4 finder_host, uint16_t finder_port) { EventLoop e; const string fea_target_name = "fea"; vector ev; // Vector for timed events bool eflag(false); // Error flag set by timed events if (verbose()) ev.push_back(e.new_periodic_ms(100, callback(ticker))); SchedulingTime stime; // // Create server, bind to port 5000, listen, check states. // TestSocket4TCP* server; ev.push_back(e.new_oneoff_after_ms(stime.next(), callback(create_server, &e, fea_target_name, finder_host, finder_port, &server))); ev.push_back(e.new_oneoff_after_ms(stime.next(), callback(verify_server_closed, &server, true, &eflag, file_location()))); ev.push_back(e.new_oneoff_after_ms(stime.next(), callback(verify_server_client_closed, &server, true, &eflag, file_location()))); ev.push_back(e.new_oneoff_after_ms(stime.next(), callback(bind_server, &server, IPv4::LOOPBACK(), uint16_t(5000), &eflag))); ev.push_back(e.new_oneoff_after_ms(stime.next(), callback(listen_server, &server, uint16_t(2), &eflag))); ev.push_back(e.new_oneoff_after_ms(stime.next(2000), callback(verify_server_closed, &server, false, &eflag, file_location()))); ev.push_back(e.new_oneoff_after_ms(stime.next(), callback(verify_server_client_closed, &server, true, &eflag, file_location()))); // // Create client and bind on port 5001, check state. // TestSocket4TCP* client; ev.push_back(e.new_oneoff_after_ms(stime.next(), callback(create_client, &e, fea_target_name, finder_host, finder_port, &client))); ev.push_back(e.new_oneoff_after_ms(stime.next(), callback(verify_client_closed, &client, true, &eflag, file_location()))); ev.push_back(e.new_oneoff_after_ms(stime.next(), callback(bind_connect_client, &client, IPv4::LOOPBACK(), uint16_t(5001), IPv4::LOOPBACK(), uint16_t(5000), &eflag))); ev.push_back(e.new_oneoff_after_ms(stime.next(2000), callback(verify_client_closed, &client, false, &eflag, file_location()))); ev.push_back(e.new_oneoff_after_ms(stime.next(), callback(verify_client_connected, &client, true, &eflag, file_location()))); // // Send packets from client to server, check bytes send/received and states // ev.push_back(e.new_oneoff_after_ms(stime.next(), callback(start_sending, &client))); ev.push_back(e.new_oneoff_after_ms(stime.next(10000), callback(stop_sending, &client))); ev.push_back(e.new_oneoff_after_ms(stime.next(), callback(match_bytes_sent_received, &client, &server, &eflag))); ev.push_back(e.new_oneoff_after_ms(stime.next(), callback(verify_client_closed, &client, false, &eflag, file_location()))); ev.push_back(e.new_oneoff_after_ms(stime.next(), callback(verify_client_connected, &client, true, &eflag, file_location()))); ev.push_back(e.new_oneoff_after_ms(stime.next(), callback(verify_server_closed, &server, false, &eflag, file_location()))); ev.push_back(e.new_oneoff_after_ms(stime.next(), callback(verify_server_client_closed, &server, false, &eflag, file_location()))); // // Send packets from server to client, check bytes send/received and states // ev.push_back(e.new_oneoff_after_ms(stime.next(), callback(start_sending, &server))); ev.push_back(e.new_oneoff_after_ms(stime.next(10000), callback(stop_sending, &server))); ev.push_back(e.new_oneoff_after_ms(stime.next(), callback(match_bytes_sent_received, &server, &client, &eflag))); ev.push_back(e.new_oneoff_after_ms(stime.next(), callback(verify_client_closed, &client, false, &eflag, file_location()))); ev.push_back(e.new_oneoff_after_ms(stime.next(), callback(verify_client_connected, &client, true, &eflag, file_location()))); ev.push_back(e.new_oneoff_after_ms(stime.next(), callback(verify_server_closed, &server, false, &eflag, file_location()))); ev.push_back(e.new_oneoff_after_ms(stime.next(), callback(verify_server_client_closed, &server, false, &eflag, file_location()))); // // Close/disconnect client, verify states // ev.push_back(e.new_oneoff_after_ms(stime.next(), callback(close_client_socket, &client))); ev.push_back(e.new_oneoff_after_ms(stime.next(), callback(verify_client_closed, &client, true, &eflag, file_location()))); ev.push_back(e.new_oneoff_after_ms(stime.next(), callback(verify_client_connected, &client, false, &eflag, file_location()))); ev.push_back(e.new_oneoff_after_ms(stime.next(), callback(verify_server_client_closed, &server, true, &eflag, file_location()))); ev.push_back(e.new_oneoff_after_ms(stime.next(), callback(verify_server_closed, &server, false, &eflag, file_location()))); // // Destroy client and server. // ev.push_back(e.new_oneoff_after_ms(stime.next(), callback(destroy_test_socket, &client))); ev.push_back(e.new_oneoff_after_ms(stime.next(), callback(destroy_test_socket, &server))); // // Create server and client again, and connect as before, and this time // use the wildcard port 0 for the client. // ev.push_back(e.new_oneoff_after_ms(stime.next(2000), callback(create_server, &e, fea_target_name, finder_host, finder_port, &server))); ev.push_back(e.new_oneoff_after_ms(stime.next(), callback(bind_server, &server, IPv4::LOOPBACK(), uint16_t(5000), &eflag))); ev.push_back(e.new_oneoff_after_ms(stime.next(), callback(listen_server, &server, uint16_t(2), &eflag))); ev.push_back(e.new_oneoff_after_ms(stime.next(), callback(create_client, &e, fea_target_name, finder_host, finder_port, &client))); ev.push_back(e.new_oneoff_after_ms(stime.next(), callback(bind_connect_client, &client, IPv4::LOOPBACK(), uint16_t(0), IPv4::LOOPBACK(), uint16_t(5000), &eflag))); // // Verify states, then close client socket on server // ev.push_back(e.new_oneoff_after_ms(stime.next(2000), callback(verify_client_closed, &client, false, &eflag, file_location()))); ev.push_back(e.new_oneoff_after_ms(stime.next(), callback(verify_client_connected, &client, true, &eflag, file_location()))); ev.push_back(e.new_oneoff_after_ms(stime.next(), callback(verify_server_closed, &server, false, &eflag, file_location()))); ev.push_back(e.new_oneoff_after_ms(stime.next(), callback(verify_server_client_closed, &server, false, &eflag, file_location()))); ev.push_back(e.new_oneoff_after_ms(stime.next(), callback(close_server_client_socket, &server))); // // Verify states, then close the server socket // ev.push_back(e.new_oneoff_after_ms(stime.next(), callback(verify_client_closed, &client, true, &eflag, file_location()))); ev.push_back(e.new_oneoff_after_ms(stime.next(), callback(verify_client_connected, &client, false, &eflag, file_location()))); ev.push_back(e.new_oneoff_after_ms(stime.next(), callback(verify_server_closed, &server, false, &eflag, file_location()))); ev.push_back(e.new_oneoff_after_ms(stime.next(), callback(verify_server_client_closed, &server, true, &eflag, file_location()))); ev.push_back(e.new_oneoff_after_ms(stime.next(), callback(close_server_socket, &server))); // // Verify states, then destroy server and client // ev.push_back(e.new_oneoff_after_ms(stime.next(), callback(verify_server_closed, &server, true, &eflag, file_location()))); // // Destroy socket server. // ev.push_back(e.new_oneoff_after_ms(stime.next(), callback(destroy_test_socket, &client))); ev.push_back(e.new_oneoff_after_ms(stime.next(), callback(destroy_test_socket, &server))); // // Force exit. // bool finish(false); ev.push_back(e.set_flag_after_ms(stime.next(1000), &finish)); while (eflag == false && finish == false) { e.run(); } if (eflag) return 1; return 0; } /* ------------------------------------------------------------------------- */ static void usage(const char* progname) { fprintf(stderr, "usage: %s [-F :] [-v]\n", progname); fprintf(stderr, "Options:\n"); fprintf(stderr, " -F [:] " "Specify arguments for external Finder instance\n"); fprintf(stderr, " -v Verbose output\n"); fprintf(stderr, "Runs remote TCP socket test using Xrls.\n"); exit(1); } static bool parse_finder_arg(const char* host_colon_port, IPv4& finder_addr, uint16_t& finder_port) { string finder_host; const char* p = strstr(host_colon_port, ":"); if (p) { finder_host = string(host_colon_port, p); finder_port = atoi(p + 1); if (finder_port == 0) { fprintf(stderr, "Invalid port \"%s\"\n", p + 1); return false; } } else { finder_host = string(host_colon_port); finder_port = FinderConstants::FINDER_DEFAULT_PORT(); } try { finder_addr = IPv4(finder_host.c_str()); } catch (const InvalidString& ) { // host string may need resolving in_addr ia; if (address_lookup(finder_host, ia) == false) { fprintf(stderr, "Invalid host \"%s\"\n", finder_host.c_str()); return false; } finder_addr.copy_in(ia); } return true; } // ---------------------------------------------------------------------------- // Main int main(int argc, char* const argv[]) { // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporarily increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); IPv4 finder_addr = FinderConstants::FINDER_DEFAULT_HOST(); uint16_t finder_port = FinderConstants::FINDER_DEFAULT_PORT(); int r = 0; // Return code int ch = 0; while ((ch = getopt(argc, argv, "F:v")) != -1) { switch (ch) { case 'F': if (parse_finder_arg(optarg, finder_addr, finder_port) == false) { usage(argv[0]); r = 1; } break; case 'v': set_verbose(true); break; default: usage(argv[0]); r = 1; } } argc -= optind; argv += optind; try { if (r == 0) { r = test_main(finder_addr, finder_port); } } catch (...) { xorp_catch_standard_exceptions(); } // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); if (r != 0) return (1); return (0); } xorp/fea/fibconfig_forwarding.hh0000664000076400007640000001323611421137511017105 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2007-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/fibconfig_forwarding.hh,v 1.6 2008/10/02 21:56:46 bms Exp $ #ifndef __FEA_FIBCONFIG_FORWARDING_HH__ #define __FEA_FIBCONFIG_FORWARDING_HH__ #include "fea_data_plane_manager.hh" class FibConfig; class FibConfigForwarding { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ FibConfigForwarding(FeaDataPlaneManager& fea_data_plane_manager); /** * Virtual destructor. */ virtual ~FibConfigForwarding(); /** * Get the @ref FibConfig instance. * * @return the @ref FibConfig instance. */ FibConfig& fibconfig() { return _fibconfig; } /** * Get the @ref FeaDataPlaneManager instance. * * @return the @ref FeaDataPlaneManager instance. */ FeaDataPlaneManager& fea_data_plane_manager() { return _fea_data_plane_manager; } /** * Get the const @ref FeaDataPlaneManager instance. * * @return the const @ref FeaDataPlaneManager instance. */ const FeaDataPlaneManager& fea_data_plane_manager() const { return _fea_data_plane_manager; } /** * Test whether this instance is running. * * @return true if the instance is running, otherwise false. */ virtual bool is_running() const { return _is_running; } /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg); /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg); /** * Test whether the IPv4 unicast forwarding engine is enabled or disabled * to forward packets. * * @param ret_value if true on return, then the IPv4 unicast forwarding * is enabled, otherwise is disabled. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int unicast_forwarding_enabled4(bool& ret_value, string& error_msg) const = 0; /** * Test whether the IPv6 unicast forwarding engine is enabled or disabled * to forward packets. * * @param ret_value if true on return, then the IPv6 unicast forwarding * is enabled, otherwise is disabled. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int unicast_forwarding_enabled6(bool& ret_value, string& error_msg) const = 0; /** * Test whether the acceptance of IPv6 Router Advertisement messages is * enabled or disabled. * * @param ret_value if true on return, then the acceptance of IPv6 Router * Advertisement messages is enabled, otherwise is disabled. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int accept_rtadv_enabled6(bool& ret_value, string& error_msg) const = 0; /** * Set the IPv4 unicast forwarding engine to enable or disable forwarding * of packets. * * @param v if true, then enable IPv4 unicast forwarding, otherwise * disable it. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int set_unicast_forwarding_enabled4(bool v, string& error_msg) = 0; /** * Set the IPv6 unicast forwarding engine to enable or disable forwarding * of packets. * * @param v if true, then enable IPv6 unicast forwarding, otherwise * disable it. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int set_unicast_forwarding_enabled6(bool v, string& error_msg) = 0; /** * Enable or disable the acceptance of IPv6 Router Advertisement messages * from other routers. It should be enabled for hosts, and disabled for * routers. * * @param v if true, then enable the acceptance of IPv6 Router * Advertisement messages, otherwise disable it. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int set_accept_rtadv_enabled6(bool v, string& error_msg) = 0; protected: // Misc other state bool _is_running; private: FibConfig& _fibconfig; FeaDataPlaneManager& _fea_data_plane_manager; // // Original state from the underlying system before the plugin was started // bool _orig_unicast_forwarding_enabled4; bool _orig_unicast_forwarding_enabled6; bool _orig_accept_rtadv_enabled6; bool _first_start; // True if started for first time }; #endif // __FEA_FIBCONFIG_FORWARDING_HH__ xorp/fea/mfea_node.hh0000664000076400007640000006575111540225525014670 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/mfea_node.hh,v 1.51 2008/10/02 21:56:49 bms Exp $ #ifndef __FEA_MFEA_NODE_HH__ #define __FEA_MFEA_NODE_HH__ // // MFEA (Multicast Forwarding Engine Abstraction) node definition. // #include "libxorp/ipvx.hh" #include "libxorp/config_param.hh" #include "libproto/proto_node.hh" #include "mrt/mifset.hh" #include "ifconfig_reporter.hh" #include "iftree.hh" #include "mfea_dataflow.hh" #include "mfea_mrouter.hh" // // Structures/classes, typedefs and macros // class EventLoop; class FeaNode; class MfeaVif; class SgCount; class VifCount; /** * @short The MFEA node class. * * There should be one node per MFEA instance. There should be * one instance per address family. */ class MfeaNode : public ProtoNode, public IfConfigUpdateReporterBase, public ServiceChangeObserverBase { public: /** * Constructor for a given address family, module ID, and event loop. * * @param fea_node the corresponding FeaNode (@ref FeaNode). * @param family the address family (AF_INET or AF_INET6 for * IPv4 and IPv6 respectively). * @param module_id the module ID (@ref xorp_module_id). Should be * equal to XORP_MODULE_MFEA. * @param eventloop the event loop to use. */ MfeaNode(FeaNode& fea_node, int family, xorp_module_id module_id, EventLoop& eventloop); /** * Destructor */ virtual ~MfeaNode(); /** * Get the FEA node instance. * * @return reference to the FEA node instance. */ FeaNode& fea_node() { return (_fea_node); } /** * Test if running in dummy mode. * * @return true if running in dummy mode, otherwise false. */ bool is_dummy() const; /** * Start the node operation. * * After the startup operations are completed, * @ref MfeaNode::final_start() is called internally * to complete the job. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int start(); /** * Stop the node operation. * * After the shutdown operations are completed, * @ref MfeaNode::final_stop() is called internally * to complete the job. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int stop(); /** * Completely start the node operation. * * This method should be called internally after @ref MfeaNode::start() * to complete the job. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int final_start(); /** * Completely stop the node operation. * * This method should be called internally after @ref MfeaNode::stop() * to complete the job. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int final_stop(); /** * Enable node operation. * * If an unit is not enabled, it cannot be start, or pending-start. */ void enable(); /** * Disable 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 disable(); /** * Test if the underlying system supports IPv4 multicast routing. * * @return true if the underlying system supports IPv4 multicast routing, * otherwise false. */ bool have_multicast_routing4() const { return (_mfea_mrouter.have_multicast_routing4()); } #ifdef HAVE_IPV6 /** * Test if the underlying system supports IPv6 multicast routing. * * @return true if the underlying system supports IPv6 multicast routing, * otherwise false. */ bool have_multicast_routing6() const { return (_mfea_mrouter.have_multicast_routing6()); } #endif /** * Install a new MFEA vif. * * @param vif vif information about the new MfeaVif to install. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_vif(const Vif& vif, string& error_msg); /** * Delete an existing MFEA vif. * * @param vif_name the name of the vif to delete. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int delete_vif(const string& vif_name, string& error_msg); /** * Add a configured vif. * * @param vif the vif with the information to add. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_config_vif(const Vif& vif, string& error_msg); /** * Set the vif flags of a configured vif. * * @param vif_name the name of the vif. * @param is_pim_register true if the vif is a PIM Register interface. * @param is_p2p true if the vif is point-to-point interface. * @param is_loopback true if the vif is a loopback interface. * @param is_multicast true if the vif is multicast capable. * @param is_broadcast true if the vif is broadcast capable. * @param is_up true if the underlying vif is UP. * @param mtu the MTU of the vif. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_config_vif_flags(const string& vif_name, bool is_pim_register, bool is_p2p, bool is_loopback, bool is_multicast, bool is_broadcast, bool is_up, uint32_t mtu, string& error_msg); /** * Complete the set of vif configuration changes. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_config_all_vifs_done(string& error_msg); /** * Enable an existing MFEA vif. * * @param vif_name the name of the vif to enable. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int enable_vif(const string& vif_name, string& error_msg); /** * Disable an existing MFEA vif. * * @param vif_name the name of the vif to disable. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int disable_vif(const string& vif_name, string& error_msg); /** * Start an existing MFEA vif. * * @param vif_name the name of the vif to start. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int start_vif(const string& vif_name, string& error_msg); /** * Stop an existing MFEA vif. * * @param vif_name the name of the vif to start. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int stop_vif(const string& vif_name, string& error_msg); /** * Start MFEA on all enabled interfaces. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int start_all_vifs(); /** * Stop MFEA on all interfaces it was running on. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int stop_all_vifs(); /** * Enable MFEA on all interfaces. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int enable_all_vifs(); /** * Disable MFEA on all interfaces. * * All running interfaces are stopped first. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int disable_all_vifs(); /** * Delete all MFEA vifs. */ void delete_all_vifs(); /** * A method called when a vif has completed its shutdown. * * @param vif_name the name of the vif that has completed its shutdown. */ void vif_shutdown_completed(const string& vif_name); /** * Register a protocol on an interface in the Multicast FEA. * * There could be only one registered protocol per interface/vif. * * @param module_instance_name the module instance name of the protocol * to register. * @param if_name the name of the interface to register for the particular * protocol. * @param vif_name the name of the vif to register for the particular * protocol. * @param ip_protocol the IP protocol number. It must be between 1 and * 255. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int register_protocol(const string& module_instance_name, const string& if_name, const string& vif_name, uint8_t ip_protocol, string& error_msg); /** Helper method to unregister all protocols on all vifs for this * interface. This needs to be called before we delete an interface * to ensure the cleanup can happen properly. */ void unregister_protocols_for_iface(const string& ifname); /** Helper method to unregister all protocols on this vif. * This needs to be called before we delete an interface * to ensure the cleanup can happen properly. */ void unregister_protocols_for_vif(const string& ifname, const string& vifname); /** * Unregister a protocol on an interface in the Multicast FEA. * * There could be only one registered protocol per interface/vif. * * @param module_instance_name the module instance name of the protocol * to unregister. * @param if_name the name of the interface to unregister for the * particular protocol. * @param vif_name the name of the vif to unregister for the particular * protocol. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int unregister_protocol(const string& module_instance_name, const string& if_name, const string& vif_name, string& error_msg); /** * UNUSED */ int proto_recv(const string& , // if_name, const string& , // vif_name, const IPvX& , // src_address, const IPvX& , // dst_address, uint8_t , // ip_protocol, int32_t , // ip_ttl, int32_t , // ip_tos, bool , // ip_router_alert, bool , // ip_internet_control, const vector& , // payload, string& // error_msg ) { assert (false); return (XORP_ERROR); } /** * UNUSED */ int proto_send(const string& , // if_name, const string& , // vif_name, const IPvX& , // src_address, const IPvX& , // dst_address, uint8_t , // ip_protocol, int32_t , // ip_ttl, int32_t , // ip_tos, bool , // ip_router_alert, bool , // ip_internet_control, const uint8_t* , // sndbuf, size_t , // sndlen, string& // error_msg ) { assert (false); return (XORP_ERROR); } /** * Process NOCACHE, WRONGVIF/WRONGMIF, WHOLEPKT signals from the * kernel. * * The signal is sent to all user-level protocols that expect it. * * @param src_module_instance_name unused. * @param message_type the message type of the kernel signal (for IPv4 * and IPv6 respectively): *
#define IGMPMSG_NOCACHE         1
#define IGMPMSG_WRONGVIF        2
#define IGMPMSG_WHOLEPKT        3

#define MRT6MSG_NOCACHE         1
#define MRT6MSG_WRONGMIF        2
#define MRT6MSG_WHOLEPKT        3
* * @param vif_index the vif index of the related interface * (message-specific). * * @param src the source address in the message. * @param dst the destination address in the message. * @param rcvbuf the data buffer with the additional information in * the message. * @param rcvlen the data length in @ref rcvbuf. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int signal_message_recv(const string& src_module_instance_name, int message_type, uint32_t vif_index, const IPvX& src, const IPvX& dst, const uint8_t *rcvbuf, size_t rcvlen); /** * Process a dataflow upcall from the kernel or from the MFEA internal * mechanism. * * The MFEA internal bandwidth-estimation mechanism is based on * periodic reading of the kernel multicast forwarding statistics. * The signal is sent to all user-level protocols that expect it. * * Note: either @ref is_threshold_in_packets or @ref is_threshold_in_bytes * (or both) must be true. * Note: either @ref is_geq_upcall or @ref is_leq_upcall * (but not both) must be true. * * @param source the source address. * * @param group the group address. * * @param threshold_interval the dataflow threshold interval. * * @param measured_interval the dataflow measured interval. * * @param threshold_packets the threshold (in number of packets) * to compare against. * * @param threshold_bytes the threshold (in number of bytes) * to compare against. * * @param measured_packets the number of packets measured within * the @ref measured_interval. * * @param measured_bytes the number of bytes measured within * the @ref measured_interval. * * @param is_threshold_in_packets if true, @ref threshold_packets is valid. * * @param is_threshold_in_bytes if true, @ref threshold_bytes is valid. * * @param is_geq_upcall if true, the operation for comparison is ">=". * * @param is_leq_upcall if true, the operation for comparison is "<=". * * @return XORP_OK on success, otherwise XORP_ERROR. */ int 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); /** * Send a signal that a dataflow-related pre-condition is true. * * This is a pure virtual function, and it must be implemented * by the communication-wrapper class that inherits this base class. * * Note: either @ref is_threshold_in_packets or @ref is_threshold_in_bytes * (or both) must be true. * Note: either @ref is_geq_upcall or @ref is_leq_upcall * (but not both) must be true. * * @param dst_module_instance_name the module instance name of the * module-recepient of the message. * * @param source_addr the source address of the dataflow. * * @param group_addr the group address of the dataflow. * * @param threshold_interval_sec the number of seconds in the * interval requested for measurement. * * @param threshold_interval_usec the number of microseconds in the * interval requested for measurement. * * @param measured_interval_sec the number of seconds in the * last measured interval that has triggered the signal. * * @param measured_interval_usec the number of microseconds in the * last measured interval that has triggered the signal. * * @param threshold_packets the threshold value to trigger a signal * (in number of packets). * * @param threshold_bytes the threshold value to trigger a signal * (in bytes). * * @param measured_packets the number of packets measured within * the @ref measured_interval. * * @param measured_bytes the number of bytes measured within * the @ref measured_interval. * * @param is_threshold_in_packets if true, @ref threshold_packets is valid. * * @param is_threshold_in_bytes if true, @ref threshold_bytes is valid. * * @param is_geq_upcall if true, the operation for comparison is ">=". * * @param is_leq_upcall if true, the operation for comparison is "<=". * * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int dataflow_signal_send(const string& dst_module_instance_name, const IPvX& source_addr, const IPvX& group_addr, uint32_t threshold_interval_sec, uint32_t threshold_interval_usec, uint32_t measured_interval_sec, uint32_t measured_interval_usec, 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) = 0; /** * Add Multicast Forwarding Cache (MFC) to the kernel. * * @param module_instance_name the module instance name of the protocol * that adds the MFC. * * @param source the source address. * * @param group the group address. * * @param iif_vif_index the vif index of the incoming interface. * * @param oiflist the bitset with the outgoing interfaces. * * @param oiflist_disable_wrongvif the bitset with the outgoing interfaces * to disable the WRONGVIF signal. * * @param max_vifs_oiflist the number of vifs covered by @ref oiflist * or @ref oiflist_disable_wrongvif. * * @param rp_addr the RP address. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int 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); /** * Delete Multicast Forwarding Cache (MFC) from the kernel. * * Note: all corresponding dataflow entries are also removed. * * @param module_instance_name the module instance name of the protocol * that deletes the MFC. * * @param source the source address. * * @param group the group address. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int delete_mfc(const string& module_instance_name, const IPvX& source, const IPvX& group); /** * Add a dataflow monitor entry. * * Note: either @ref is_threshold_in_packets or @ref is_threshold_in_bytes * (or both) must be true. * Note: either @ref is_geq_upcall or @ref is_leq_upcall * (but not both) must be true. * * @param module_instance_name the module instance name of the protocol * that adds the dataflow monitor entry. * * @param source the source address. * * @param group the group address. * * @param threshold_interval the dataflow threshold interval. * * @param threshold_packets the threshold (in number of packets) to * compare against. * * @param threshold_bytes the threshold (in number of bytes) to * compare against. * * @param is_threshold_in_packets if true, @ref threshold_packets is valid. * * @param is_threshold_in_bytes if true, @ref threshold_bytes is valid. * * @param is_geq_upcall if true, the operation for comparison is ">=". * * @param is_leq_upcall if true, the operation for comparison is "<=". * * @param error_msg the error message (if error). * * @return XORP_OK on success, otherwise XORP_ERROR. */ int 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); /** * Delete a dataflow monitor entry. * * Note: either @ref is_threshold_in_packets or @ref is_threshold_in_bytes * (or both) must be true. * Note: either @ref is_geq_upcall or @ref is_leq_upcall * (but not both) must be true. * * @param module_instance_name the module instance name of the protocol * that deletes the dataflow monitor entry. * * @param source the source address. * * @param group the group address. * * @param threshold_interval the dataflow threshold interval. * * @param threshold_packets the threshold (in number of packets) to * compare against. * * @param threshold_bytes the threshold (in number of bytes) to * compare against. * * @param is_threshold_in_packets if true, @ref threshold_packets is valid. * * @param is_threshold_in_bytes if true, @ref threshold_bytes is valid. * * @param is_geq_upcall if true, the operation for comparison is ">=". * * @param is_leq_upcall if true, the operation for comparison is "<=". * * @param error_msg the error message (if error). * * @return XORP_OK on success, otherwise XORP_ERROR. */ int 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); /** * Delete all dataflow monitor entries for a given source and group * address. * * @param module_instance_name the module instance name of the protocol * that deletes the dataflow monitor entry. * * @param source the source address. * * @param group the group address. * * @param error_msg the error message (if error). * * @return XORP_OK on success, otherwise XORP_ERROR. */ int delete_all_dataflow_monitor(const string& module_instance_name, const IPvX& source, const IPvX& group, string& error_msg); /** * Add a multicast vif to the kernel. * * @param vif_index the vif index of the interface to add. * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_multicast_vif(uint32_t vif_index); /** * Delete a multicast vif from the kernel. * * @param vif_index the vif index of the interface to delete. * @return XORP_OK on success, otherwise XORP_ERROR. */ int delete_multicast_vif(uint32_t vif_index); /** * Get MFC multicast forwarding statistics from the kernel. * * 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. * * @param source the MFC source address. * @param group the MFC group address. * @param sg_count a reference to a @ref 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. * @return XORP_OK on success, otherwise XORP_ERROR. */ int get_sg_count(const IPvX& source, const IPvX& group, SgCount& sg_count); /** * Get interface multicast forwarding statistics from the kernel. * * Get the number of packets and bytes received on, or forwarded on * a particular multicast interface. * * @param vif_index the vif index of the virtual multicast interface whose * statistics we need. * @param vif_count a reference to a @ref VifCount class to store * the result. * @return XORP_OK on success, otherwise XORP_ERROR. */ int get_vif_count(uint32_t vif_index, VifCount& vif_count); /** * Get a reference to the mrouter (@ref MfeaMrouter). * * @return a reference to the mrouter (@ref MfeaMrouter). */ MfeaMrouter& mfea_mrouter() { return (_mfea_mrouter); } /** * Get a reference to the dataflow table (@ref MfeaDft). * * @return a reference to the dataflow table (@ref MfeaDft). */ MfeaDft& mfea_dft() { return (_mfea_dft); } /** * Test if trace log is enabled. * * This method is used to test whether to output trace log debug messges. * * @return true if trace log is enabled, otherwise false. */ bool is_log_trace() const { return (_is_log_trace); } /** * Enable/disable trace log. * * This method is used to enable/disable trace log debug messages output. * * @param is_enabled if true, trace log is enabled, otherwise is disabled. */ void set_log_trace(bool is_enabled) { _is_log_trace = is_enabled; } protected: IfConfigUpdateReplicator& mfea_iftree_update_replicator() { return (_mfea_iftree_update_replicator); } private: void interface_update(const string& ifname, const Update& update); void vif_update(const string& ifname, const string& vifname, const Update& update); void vifaddr4_update(const string& ifname, const string& vifname, const IPv4& addr, const Update& update); #ifdef HAVE_IPV6 void vifaddr6_update(const string& ifname, const string& vifname, const IPv6& addr, const Update& update); #endif void updates_completed(); /** * A method invoked when the status of a service changes. * * @param service the service whose status has changed. * @param old_status the old status. * @param new_status the new status. */ void status_change(ServiceBase* service, ServiceStatus old_status, ServiceStatus new_status); int add_pim_register_vif(); // Private state FeaNode& _fea_node; MfeaMrouter _mfea_mrouter; // The mrouter state MfeaDft _mfea_dft; // The dataflow monitoring table // // The state to register: // - Protocol module instance names // - IP protocol numbers set _registered_module_instance_names; set _registered_ip_protocols; // // Interface tree propagation // IfTree _mfea_iftree; IfConfigUpdateReplicator _mfea_iftree_update_replicator; // // Debug and test-related state // bool _is_log_trace; // If true, enable XLOG_TRACE() }; #endif // __FEA_MFEA_NODE_HH__ xorp/fea/mfea_vif.hh0000664000076400007640000001465511540225525014524 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __FEA_MFEA_VIF_HH__ #define __FEA_MFEA_VIF_HH__ // // MFEA virtual interface definition. // #include "libxorp/vif.hh" #include "libproto/proto_unit.hh" class MfeaNode; /** * @short A class for MFEA-specific virtual interface. */ class MfeaVif : public ProtoUnit, public Vif { public: /** * Constructor for a given MFEA node and a generic virtual interface. * * @param mfea_node the @ref MfeaNode this interface belongs to. * @param vif the generic Vif interface that contains various information. */ MfeaVif(MfeaNode& mfea_node, const Vif& vif); /** * Copy Constructor for a given MFEA node and MFEA-specific virtual * interface. * * @param mfea_node the @ref MfeaNode this interface belongs to. * @param mfea_vif the origin @ref MfeaVif interface that contains * the initialization information. */ MfeaVif(MfeaNode& mfea_node, const MfeaVif& mfea_vif); /** * Destructor */ virtual ~MfeaVif(); /** * Start MFEA on a single virtual interface. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int start(string& error_msg); /** * Stop MFEA on a single virtual interface. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int stop(string& error_msg); /** * Enable MFEA on a single virtual interface. * * If an unit is not enabled, it cannot be start, or pending-start. */ void enable(); /** * 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 disable(); /** System detected some change. */ void notifyUpdated(); /** * Register a protocol on a single virtual interface. * * There could be only one registered protocol per interface/vif. * * @param module_instance_name the module instance name of the protocol * to register. * @param ip_protocol the IP protocol number. It must be between 1 and * 255. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int register_protocol(const string& module_instance_name, uint8_t ip_protocol, string& error_msg); /** * Unregister a protocol on a single virtual interface. * * @param module_instance_name the module instance name of the protocol * to unregister. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int unregister_protocol(const string& module_instance_name, string& error_msg); /** * Get the minimum TTL a multicast packet must have to be forwarded * on this virtual interface. * * @return the minimum TTL a multicast packet must have * to be forwarded on this virtual interface. */ uint8_t min_ttl_threshold() const { return (_min_ttl_threshold); } /** * Set the minimum TTL a multicast packet must have to be forwarded * on this virtual interface. * * @param v the value of the minimum TTL a multicast packet must have * to be forwarded on this virtual interface. */ void set_min_ttl_threshold(uint8_t v) { _min_ttl_threshold = v; } /** * Get the maximum multicast bandwidth rate allowed on this virtual * interface. * * @return the maximum multicast bandwidth rate allowed * on this virtual interface. */ uint32_t max_rate_limit() const { return (_max_rate_limit); } /** * Set the maximum multicast bandwidth rate allowed on this virtual * interface. * * @param v the value of the maximum multicast bandwidth rate allowed * on this virtual interface. */ void set_max_rate_limit(uint32_t v) { _max_rate_limit = v; } /** * Get the registered module instance name. * * @return the registered module instance name. */ const string& registered_module_instance_name() const { return _registered_module_instance_name; } /** * Get the registered IP protocol. * * @return the registered IP protocol. */ uint8_t registered_ip_protocol() const { return (_registered_ip_protocol); } private: // Private functions MfeaNode& mfea_node() const { return (_mfea_node); } /** * Get the string with the flags about the vif status. * * TODO: temporary here. Should go to the Vif class after the Vif * class starts using the Proto class. * * @return the C++ style string with the flags about the vif status * (e.g., UP/DOWN/DISABLED, etc). */ string flags_string() const; // Private state MfeaNode& _mfea_node; // The MFEA node I belong to uint8_t _min_ttl_threshold; // Min. TTL required to forward mcast pkts uint32_t _max_rate_limit; // Max. bw rate allowed to forward mcast // // State to keep information about the registered multicast routing // protocol for this vif. // There can be only one protocol instance registered which will be // responsible for the multicast routing on the vif. // string _registered_module_instance_name; uint8_t _registered_ip_protocol; bool wants_to_be_started; // as soon as we can, ie if the interface appears. }; // // Global variables // // // Global functions prototypes // #endif // __FEA_MFEA_VIF_HH__ xorp/fea/xrl_fea_target.hh0000664000076400007640000023654511540225525015742 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2007-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __FEA_XRL_FEA_TARGET_HH__ #define __FEA_XRL_FEA_TARGET_HH__ // // FEA (Forwarding Engine Abstraction) XRL target implementation. // #include "xrl/targets/fea_base.hh" #include "xrl_fib_client_manager.hh" class EventLoop; #ifdef XORP_USE_CLICK class FeaDataPlaneManagerClick; #endif class FeaNode; class FibConfig; class FirewallManager; class IfConfig; class IoLinkManager; class IoIpManager; class IoTcpUdpManager; class LibFeaClientBridge; class XrlFibClientManager; #ifndef XORP_DISABLE_PROFILE class Profile; #endif /** * @short FEA (Forwarding Engine Abstraction) XRL target class. */ class XrlFeaTarget : public XrlFeaTargetBase { public: /** * Constructor. * * @param eventloop the event loop to use. */ XrlFeaTarget(EventLoop& eventloop, FeaNode& fea_node, XrlRouter& xrl_router, #ifndef XORP_DISABLE_PROFILE Profile& profile, #endif XrlFibClientManager& xrl_fib_client_manager, LibFeaClientBridge& lib_fea_client_bridge); /** * Destructor */ virtual ~XrlFeaTarget(); /** * Startup the service operation. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int startup(); /** * Shutdown the service operation. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int shutdown(); /** * Test whether the service is running. * * @return true if the service is still running, otherwise false. */ bool is_running() const; /** * Test whether a shutdown XRL request has been received. * * @return true if shutdown XRL request has been received, otherwise false. */ bool is_shutdown_received() const { return (_is_shutdown_received); } /** * Get the event loop this service is added to. * * @return the event loop this service is added to. */ EventLoop& eventloop() { return (_eventloop); } XrlCmdError common_0_1_get_target_name( // Output values, string& name); XrlCmdError common_0_1_get_version( // Output values, string& version); /** * Get status from Xrl Target */ XrlCmdError common_0_1_get_status( // Output values, uint32_t& status, string& reason); /** * Shutdown FEA cleanly */ XrlCmdError common_0_1_shutdown(); XrlCmdError common_0_1_startup() { return XrlCmdError::OKAY(); } /** Does nothing, but allows us to have rtrmgr verify startup. */ virtual XrlCmdError ifmgr_0_1_startup_ifmgr(); /** * Announce target birth to observer. * * @param target_class the target class name. * * @param target_instance the target instance name. */ XrlCmdError finder_event_observer_0_1_xrl_target_birth( // Input values, const string& target_class, const string& target_instance); /** * Announce target death to observer. * * @param target_class the target class name. * * @param target_instance the target instance name. */ XrlCmdError finder_event_observer_0_1_xrl_target_death( // Input values, const string& target_class, const string& target_instance); #ifdef XORP_USE_CLICK /** * Load Click FEA support. */ XrlCmdError fea_click_0_1_load_click(); /** * Unload Click FEA support. */ XrlCmdError fea_click_0_1_unload_click(); /** * Enable/disable Click FEA support. * * @param enable if true, then enable the Click FEA support, otherwise * disable it. */ XrlCmdError fea_click_0_1_enable_click( // Input values, const bool& enable); /** * Start Click FEA support. */ XrlCmdError fea_click_0_1_start_click(); /** * Stop Click FEA support. */ XrlCmdError fea_click_0_1_stop_click(); /** * Enable/disable duplicating the Click routes to the system kernel. * * @param enable if true, then enable duplicating the Click routes to the * system kernel, otherwise disable it. */ XrlCmdError fea_click_0_1_enable_duplicate_routes_to_kernel( // Input values, const bool& enable); /** * Enable/disable kernel-level Click FEA support. * * @param enable if true, then enable the kernel-level Click FEA support, * otherwise disable it. */ XrlCmdError fea_click_0_1_enable_kernel_click( // Input values, const bool& enable); /** * Enable/disable installing kernel-level Click on startup. * * @param enable if true, then install kernel-level Click on startup. */ XrlCmdError fea_click_0_1_enable_kernel_click_install_on_startup( // Input values, const bool& enable); /** * Specify the list of kernel Click modules to load on startup if * installing kernel-level Click on startup is enabled. The file names of * the kernel modules are separated by colon. * * @param modules the list of kernel Click modules (separated by colon) to * load. */ XrlCmdError fea_click_0_1_set_kernel_click_modules( // Input values, const string& modules); /** * Specify the kernel-level Click mount directory. * * @param directory the kernel-level Click mount directory. */ XrlCmdError fea_click_0_1_set_kernel_click_mount_directory( // Input values, const string& directory); /** * Specify the external program to generate the kernel-level Click * configuration. * * @param kernel_click_config_generator_file the name of the external * program to generate the kernel-level Click configuration. */ XrlCmdError fea_click_0_1_set_kernel_click_config_generator_file( // Input values, const string& kernel_click_config_generator_file); /** * Enable/disable user-level Click FEA support. * * @param enable if true, then enable the user-level Click FEA support, * otherwise disable it. */ XrlCmdError fea_click_0_1_enable_user_click( // Input values, const bool& enable); /** * Specify the user-level Click command file. * * @param user_click_command_file the name of the user-level Click command * file. */ XrlCmdError fea_click_0_1_set_user_click_command_file( // Input values, const string& user_click_command_file); /** * Specify the extra arguments to the user-level Click command. * * @param user_click_command_extra_arguments the extra arguments to the * user-level Click command. */ XrlCmdError fea_click_0_1_set_user_click_command_extra_arguments( // Input values, const string& user_click_command_extra_arguments); /** * Specify whether to execute on startup the user-level Click command. * * @param user_click_command_execute_on_startup if true, then execute the * user-level Click command on startup. */ XrlCmdError fea_click_0_1_set_user_click_command_execute_on_startup( // Input values, const bool& user_click_command_execute_on_startup); /** * Specify the socket port to use for control access to the user-level * Click. * * @param user_click_control_socket_port the socket port to use for * control access to the user-level Click. */ XrlCmdError fea_click_0_1_set_user_click_control_socket_port( // Input values, const uint32_t& user_click_control_socket_port); /** * Specify the address to use for control access to the user-level * Click. * * @param user_click_control_address the address to use for * control access to the user-level Click. */ XrlCmdError fea_click_0_1_set_user_click_control_address( // Input values, const IPv4& user_click_control_address); /** * Specify the configuration file to be used by user-level Click on * startup. * * @param user_click_startup_config_file the name of the configuration * file to be used by user-level Click on startup. */ XrlCmdError fea_click_0_1_set_user_click_startup_config_file( // Input values, const string& user_click_startup_config_file); /** * Specify the external program to generate the user-level Click * configuration. * * @param user_click_config_generator_file the name of the external * program to generate the user-level Click configuration. */ XrlCmdError fea_click_0_1_set_user_click_config_generator_file( // Input values, const string& user_click_config_generator_file); #endif // click /** * Add a FIB client. * * @param client_target_name the target name of the FIB client to add. * @param send_updates whether updates should be sent. * @param send_resolves whether resolve requests should be sent. */ XrlCmdError fea_fib_0_1_add_fib_client4( // Input values, const string& client_target_name, const bool& send_updates, const bool& send_resolves); /** * Delete a FIB client. * * @param target_name the target name of the FIB client to delete. */ XrlCmdError fea_fib_0_1_delete_fib_client4( // Input values, const string& client_target_name); #ifdef HAVE_IPV6 XrlCmdError fea_fib_0_1_add_fib_client6( // Input values, const string& client_target_name, const bool& send_updates, const bool& send_resolves); XrlCmdError fea_fib_0_1_delete_fib_client6( // Input values, const string& client_target_name); #endif #ifndef XORP_DISABLE_FIREWALL // // FEA firewall interface // /** Does nothing, but allows us to have rtrmgr verify startup. */ virtual XrlCmdError fea_firewall_0_1_startup_firewall(); /** * Start firewall configuration transaction. * * @param tid the transaction ID returned by this operation. */ XrlCmdError fea_firewall_0_1_start_transaction( // Output values, uint32_t& tid); /** * Commit firewall configuration transaction. * * @param tid the transaction ID for this operation. */ XrlCmdError fea_firewall_0_1_commit_transaction( // Input values, const uint32_t& tid); /** * Abort firewall configuration transaction. * * @param tid the transaction ID for this operation. */ XrlCmdError fea_firewall_0_1_abort_transaction( // Input values, const uint32_t& tid); /** * Add an IPv4 firewall entry. * * @param tid the transaction ID for this operation. * * @param rule_number the rule number for this entry. * * @param ifname the name of the interface where this filter is to be * applied. * * @param vifname the name of the vif where this filter is to be applied. * * @param src_network the source IPv4 network address prefix. * * @param dst_network the destination IPv4 network address prefix. * * @param ip_protocol the IP protocol number (1-255, or 0 if wildcard). * * @param src_port_begin the source TCP/UDP begin port (0-65535). * * @param src_port_end the source TCP/UDP end port (0-65535). * * @param dst_port_begin the destination TCP/UDP begin port (0-65535). * * @param dst_port_end the destination TCP/UDP end port (0-65535). * * @param action the action to be taken when this filter is matched. It is * one of the following keywords: "none", "pass", "drop", "reject". */ XrlCmdError fea_firewall_0_1_add_entry4( // Input values, const uint32_t& tid, const uint32_t& rule_number, const string& ifname, const string& vifname, const IPv4Net& src_network, const IPv4Net& dst_network, const uint32_t& ip_protocol, const uint32_t& src_port_begin, const uint32_t& src_port_end, const uint32_t& dst_port_begin, const uint32_t& dst_port_end, const string& action); /** * Replace an IPv4 firewall entry. * * @param tid the transaction ID for this operation. * * @param rule_number the rule number for this entry. * * @param ifname the name of the interface where this filter is to be * applied. * * @param vifname the name of the vif where this filter is to be applied. * * @param src_network the source IPv4 network address prefix. * * @param dst_network the destination IPv4 network address prefix. * * @param ip_protocol the IP protocol number (1-255, or 0 if wildcard). * * @param src_port_begin the source TCP/UDP begin port (0-65535). * * @param src_port_end the source TCP/UDP end port (0-65535). * * @param dst_port_begin the destination TCP/UDP begin port (0-65535). * * @param dst_port_end the destination TCP/UDP end port (0-65535). * * @param action the action to be taken when this filter is matched. It is * one of the following keywords: "none", "pass", "drop", "reject". */ XrlCmdError fea_firewall_0_1_replace_entry4( // Input values, const uint32_t& tid, const uint32_t& rule_number, const string& ifname, const string& vifname, const IPv4Net& src_network, const IPv4Net& dst_network, const uint32_t& ip_protocol, const uint32_t& src_port_begin, const uint32_t& src_port_end, const uint32_t& dst_port_begin, const uint32_t& dst_port_end, const string& action); /** * Delete an IPv4 firewall entry. * * @param tid the transaction ID for this operation. * * @param rule_number the rule number for this entry. * * @param ifname the name of the interface where this filter is to be * deleted. * * @param vifname the name of the vif where this filter is to be deleted. * * @param src_network the source IPv4 network address prefix. * * @param dst_network the destination IPv4 network address prefix. * * @param ip_protocol the IP protocol number (1-255, or 0 if wildcard). * * @param src_port_begin the source TCP/UDP begin port (0-65535). * * @param src_port_end the source TCP/UDP end port (0-65535). * * @param dst_port_begin the destination TCP/UDP begin port (0-65535). * * @param dst_port_end the destination TCP/UDP end port (0-65535). */ XrlCmdError fea_firewall_0_1_delete_entry4( // Input values, const uint32_t& tid, const uint32_t& rule_number, const string& ifname, const string& vifname, const IPv4Net& src_network, const IPv4Net& dst_network, const uint32_t& ip_protocol, const uint32_t& src_port_begin, const uint32_t& src_port_end, const uint32_t& dst_port_begin, const uint32_t& dst_port_end); /** * Delete all IPv4 firewall entries. * * @param tid the transaction ID for this operation. */ XrlCmdError fea_firewall_0_1_delete_all_entries4( // Input values, const uint32_t& tid); /** * Get a token for a list of IPv4 firewall entries. * * @param token to be provided when calling get_entry_list_next4. * * @param more true if the list is not empty. */ XrlCmdError fea_firewall_0_1_get_entry_list_start4( // Output values, uint32_t& token, bool& more); /** * Get the next item in a list of IPv4 firewall entries. * * @param token returned by a previous call to get_entry_list_start4. * * @param rule_number the rule number for this entry. * * @param ifname the name of the interface where this filter exists. * * @param vifname the name of the vif where this filter exists. * * @param src_network the source IPv4 network address prefix. * * @param dst_network the destination IPv4 network address prefix. * * @param ip_protocol the IP protocol number (1-255, or 0 if wildcard). * * @param src_port_begin the source TCP/UDP begin port (0-65535). * * @param src_port_end the source TCP/UDP end port (0-65535). * * @param dst_port_begin the destination TCP/UDP begin port (0-65535). * * @param dst_port_end the destination TCP/UDP end port (0-65535). * * @param action the action taken when this filter is matched. It is one * of the following keywords: "none", "pass", "drop", "reject". * * @param more true if the list has more items remaining. */ XrlCmdError fea_firewall_0_1_get_entry_list_next4( // Input values, const uint32_t& token, // Output values, uint32_t& rule_number, string& ifname, string& vifname, IPv4Net& src_network, IPv4Net& dst_network, uint32_t& ip_protocol, uint32_t& src_port_begin, uint32_t& src_port_end, uint32_t& dst_port_begin, uint32_t& dst_port_end, string& action, bool& more); #ifdef HAVE_IPV6 /** * Add an IPv6 firewall entry. * * @param tid the transaction ID for this operation. * * @param rule_number the rule number for this entry. * * @param ifname the name of the interface where this filter is to be * applied. * * @param vifname the name of the vif where this filter is to be applied. * * @param src_network the source IPv6 network address prefix. * * @param dst_network the destination IPv6 network address prefix. * * @param ip_protocol the IP protocol number (1-255, or 0 if wildcard). * * @param src_port_begin the source TCP/UDP begin port (0-65535). * * @param src_port_end the source TCP/UDP end port (0-65535). * * @param dst_port_begin the destination TCP/UDP begin port (0-65535). * * @param dst_port_end the destination TCP/UDP end port (0-65535). * * @param action the action to be taken when this filter is matched. It is * one of the following keywords: "none", "pass", "drop", "reject". */ XrlCmdError fea_firewall_0_1_add_entry6( // Input values, const uint32_t& tid, const uint32_t& rule_number, const string& ifname, const string& vifname, const IPv6Net& src_network, const IPv6Net& dst_network, const uint32_t& ip_protocol, const uint32_t& src_port_begin, const uint32_t& src_port_end, const uint32_t& dst_port_begin, const uint32_t& dst_port_end, const string& action); /** * Replace an IPv6 firewall entry. * * @param tid the transaction ID for this operation. * * @param rule_number the rule number for this entry. * * @param ifname the name of the interface where this filter is to be * applied. * * @param vifname the name of the vif where this filter is to be applied. * * @param src_network the source IPv6 network address prefix. * * @param dst_network the destination IPv6 network address prefix. * * @param ip_protocol the IP protocol number (1-255, or 0 if wildcard). * * @param src_port_begin the source TCP/UDP begin port (0-65535). * * @param src_port_end the source TCP/UDP end port (0-65535). * * @param dst_port_begin the destination TCP/UDP begin port (0-65535). * * @param dst_port_end the destination TCP/UDP end port (0-65535). * * @param action the action to be taken when this filter is matched. It is * one of the following keywords: "none", "pass", "drop", "reject". */ XrlCmdError fea_firewall_0_1_replace_entry6( // Input values, const uint32_t& tid, const uint32_t& rule_number, const string& ifname, const string& vifname, const IPv6Net& src_network, const IPv6Net& dst_network, const uint32_t& ip_protocol, const uint32_t& src_port_begin, const uint32_t& src_port_end, const uint32_t& dst_port_begin, const uint32_t& dst_port_end, const string& action); /** * Delete an IPv6 firewall entry. * * @param tid the transaction ID for this operation. * * @param rule_number the rule number for this entry. * * @param ifname the name of the interface where this filter is to be * deleted. * * @param vifname the name of the vif where this filter is to be deleted. * * @param src_network the source IPv6 network address prefix. * * @param dst_network the destination IPv6 network address prefix. * * @param ip_protocol the IP protocol number (1-255, or 0 if wildcard). * * @param src_port_begin the source TCP/UDP begin port (0-65535). * * @param src_port_end the source TCP/UDP end port (0-65535). * * @param dst_port_begin the destination TCP/UDP begin port (0-65535). * * @param dst_port_end the destination TCP/UDP end port (0-65535). */ XrlCmdError fea_firewall_0_1_delete_entry6( // Input values, const uint32_t& tid, const uint32_t& rule_number, const string& ifname, const string& vifname, const IPv6Net& src_network, const IPv6Net& dst_network, const uint32_t& ip_protocol, const uint32_t& src_port_begin, const uint32_t& src_port_end, const uint32_t& dst_port_begin, const uint32_t& dst_port_end); /** * Delete all IPv6 firewall entries. * * @param tid the transaction ID for this operation. */ XrlCmdError fea_firewall_0_1_delete_all_entries6( // Input values, const uint32_t& tid); /** * Get a token for a list of IPv6 firewall entries. * * @param token to be provided when calling get_entry_list_next6. * * @param more true if the list is not empty. */ XrlCmdError fea_firewall_0_1_get_entry_list_start6( // Output values, uint32_t& token, bool& more); /** * Get the next item in a list of IPv6 firewall entries. * * @param token returned by a previous call to get_entry_list_start6. * * @param rule_number the rule number for this entry. * * @param ifname the name of the interface where this filter exists. * * @param vifname the name of the vif where this filter exists. * * @param src_network the source IPv6 network address prefix. * * @param dst_network the destination IPv6 network address prefix. * * @param ip_protocol the IP protocol number (1-255, or 0 if wildcard). * * @param src_port_begin the source TCP/UDP begin port (0-65535). * * @param src_port_end the source TCP/UDP end port (0-65535). * * @param dst_port_begin the destination TCP/UDP begin port (0-65535). * * @param dst_port_end the destination TCP/UDP end port (0-65535). * * @param action the action taken when this filter is matched. It is one * of the following keywords: "none", "pass", "drop", "reject". * * @param more true if the list has more items remaining. */ XrlCmdError fea_firewall_0_1_get_entry_list_next6( // Input values, const uint32_t& token, // Output values, uint32_t& rule_number, string& ifname, string& vifname, IPv6Net& src_network, IPv6Net& dst_network, uint32_t& ip_protocol, uint32_t& src_port_begin, uint32_t& src_port_end, uint32_t& dst_port_begin, uint32_t& dst_port_end, string& action, bool& more); #endif //ipv6 #endif //firewall // // FEA network interface management interface // XrlCmdError ifmgr_0_1_set_restore_original_config_on_shutdown( // Input values, const bool& enable); XrlCmdError ifmgr_0_1_get_configured_interface_names( // Output values, XrlAtomList& ifnames); XrlCmdError ifmgr_0_1_get_configured_vif_names( const string& ifname, // Output values, XrlAtomList& vifs); XrlCmdError ifmgr_0_1_get_configured_vif_flags( // Input values, const string& ifname, const string& vif, // Output values, bool& enabled, bool& broadcast, bool& loopback, bool& point_to_point, bool& multicast); XrlCmdError ifmgr_0_1_get_configured_vif_pif_index( // Input values, const string& ifname, const string& vif, // Output values, uint32_t& pif_index); XrlCmdError ifmgr_0_1_start_transaction( // Output values, uint32_t& tid); XrlCmdError ifmgr_0_1_commit_transaction( // Input values, const uint32_t& tid); XrlCmdError ifmgr_0_1_abort_transaction( // Input values, const uint32_t& tid); XrlCmdError ifmgr_0_1_create_interface( // Input values, const uint32_t& tid, const string& ifname); XrlCmdError ifmgr_0_1_delete_interface( // Input values, const uint32_t& tid, const string& ifname); /** * Implicitly configure all interfaces within the FEA by using information * from the underlying system. * * @param tid the transaction ID. * @param enable if true, then enable the implicit configuration, * otherwise disable it. */ XrlCmdError ifmgr_0_1_configure_all_interfaces_from_system( // Input values, const uint32_t& tid, const bool& enable); /** * Implicitly configure an interface within the FEA by using information * from the underlying system. * * @param tid the transaction ID. * @param ifname the name of the interface to configure. * @param enable if true, then enable the implicit configuration, * otherwise disable it. */ XrlCmdError ifmgr_0_1_configure_interface_from_system( // Input values, const uint32_t& tid, const string& ifname, const bool& enable); XrlCmdError ifmgr_0_1_set_interface_enabled( // Input values, const uint32_t& tid, const string& ifname, const bool& enabled); XrlCmdError ifmgr_0_1_get_configured_interface_enabled( // Input values, const string& ifname, // Output values, bool& enabled); XrlCmdError ifmgr_0_1_set_interface_discard( // Input values, const uint32_t& tid, const string& ifname, const bool& discard); XrlCmdError ifmgr_0_1_get_configured_interface_discard( // Input values, const string& ifname, // Output values, bool& discard); XrlCmdError ifmgr_0_1_set_interface_unreachable( // Input values, const uint32_t& tid, const string& ifname, const bool& unreachable); XrlCmdError ifmgr_0_1_get_configured_interface_unreachable( // Input values, const string& ifname, // Output values, bool& unreachable); XrlCmdError ifmgr_0_1_set_interface_management( // Input values, const uint32_t& tid, const string& ifname, const bool& management); XrlCmdError ifmgr_0_1_get_configured_interface_management( // Input values, const string& ifname, // Output values, bool& management); XrlCmdError ifmgr_0_1_set_mac( // Input values, const uint32_t& tid, const string& ifname, const Mac& mac); XrlCmdError ifmgr_0_1_create_mac( // Input values, const string& ifname, const Mac& mac); XrlCmdError ifmgr_0_1_create_address_atomic( // Input values, const string& ifname, const string& vifname, const IPv4& ip, const uint32_t& prefix_length); XrlCmdError ifmgr_0_1_delete_address_atomic( // Input values, const string& ifname, const string& vifname, const IPv4& ip); XrlCmdError ifmgr_0_1_delete_mac( // Input values, const string& ifname, const Mac& mac); XrlCmdError ifmgr_0_1_restore_original_mac( // Input values, const uint32_t& tid, const string& ifname); XrlCmdError ifmgr_0_1_get_configured_mac( // Input values, const string& ifname, // Output values, Mac& mac); XrlCmdError ifmgr_0_1_set_mtu( // Input values, const uint32_t& tid, const string& ifname, const uint32_t& mtu); XrlCmdError ifmgr_0_1_restore_original_mtu( // Input values, const uint32_t& tid, const string& ifname); XrlCmdError ifmgr_0_1_get_configured_mtu( // Input values, const string& ifname, // Output values, uint32_t& mtu); XrlCmdError ifmgr_0_1_get_configured_no_carrier( // Input values, const string& ifname, // Output values, bool& no_carrier); XrlCmdError ifmgr_0_1_get_configured_baudrate( // Input values, const string& ifname, // Output values, uint64_t& baudrate); XrlCmdError ifmgr_0_1_get_configured_address_flags4( // Input values, const string& ifname, const string& vif, const IPv4& address, // Output values, bool& up, bool& broadcast, bool& loopback, bool& point_to_point, bool& multicast); XrlCmdError ifmgr_0_1_create_vif( // Input values, const uint32_t& tid, const string& ifname, const string& vif); XrlCmdError ifmgr_0_1_delete_vif( // Input values, const uint32_t& tid, const string& ifname, const string& vif); XrlCmdError ifmgr_0_1_set_vif_enabled( // Input values, const uint32_t& tid, const string& ifname, const string& vif, const bool& enabled); XrlCmdError ifmgr_0_1_get_configured_vif_enabled( // Input values, const string& ifname, const string& vif, // Output values, bool& enabled); XrlCmdError ifmgr_0_1_get_configured_vif_addresses4( // Input values, const string& ifname, const string& vif, // Output values, XrlAtomList& addresses); XrlCmdError ifmgr_0_1_set_parent_ifname( // Input values, const uint32_t& tid, const string& ifname, const string& parent_ifname); XrlCmdError ifmgr_0_1_set_iface_type( // Input values, const uint32_t& tid, const string& ifname, const string& iface_type); XrlCmdError ifmgr_0_1_set_vid( // Input values, const uint32_t& tid, const string& ifname, const string& vid); XrlCmdError ifmgr_0_1_create_address4( // Input values, const uint32_t& tid, const string& ifname, const string& vif, const IPv4& address); XrlCmdError ifmgr_0_1_delete_address4( // Input values, const uint32_t& tid, const string& ifname, const string& vif, const IPv4& address); XrlCmdError ifmgr_0_1_set_address4( // Input values, const uint32_t& tid, const string& ifname, const string& vif, const IPv4& address); XrlCmdError ifmgr_0_1_set_address_enabled4( // Input values, const uint32_t& tid, const string& ifname, const string& vif, const IPv4& address, const bool& enabled); XrlCmdError ifmgr_0_1_get_configured_address_enabled4( // Input values, const string& ifname, const string& vif, const IPv4& address, bool& enabled); XrlCmdError ifmgr_0_1_set_prefix4( // Input values, const uint32_t& tid, const string& ifname, const string& vif, const IPv4& address, const uint32_t& prefix_len); XrlCmdError ifmgr_0_1_get_configured_prefix4( // Input values, const string& ifname, const string& vif, const IPv4& address, // Output values, uint32_t& prefix_len); XrlCmdError ifmgr_0_1_set_broadcast4( // Input values, const uint32_t& tid, const string& ifname, const string& vif, const IPv4& address, const IPv4& broadcast); XrlCmdError ifmgr_0_1_get_configured_broadcast4( // Input values, const string& ifname, const string& vif, const IPv4& address, // Output values, IPv4& broadcast); XrlCmdError ifmgr_0_1_set_endpoint4( // Input values, const uint32_t& tid, const string& ifname, const string& vif, const IPv4& address, const IPv4& endpoint); XrlCmdError ifmgr_0_1_get_configured_endpoint4( // Input values, const string& ifname, const string& vif, const IPv4& address, // Output values, IPv4& endpoint); #ifdef HAVE_IPV6 XrlCmdError ifmgr_0_1_get_configured_address_flags6( // Input values, const string& ifname, const string& vif, const IPv6& address, // Output values, bool& up, bool& loopback, bool& point_to_point, bool& multicast); XrlCmdError ifmgr_0_1_get_configured_vif_addresses6( // Input values, const string& ifname, const string& vif, // Output values, XrlAtomList& addresses); XrlCmdError ifmgr_0_1_create_address6( // Input values, const uint32_t& tid, const string& ifname, const string& vif, const IPv6& address); XrlCmdError ifmgr_0_1_delete_address6( // Input values, const uint32_t& tid, const string& ifname, const string& vif, const IPv6& address); XrlCmdError ifmgr_0_1_set_address_enabled6( // Input values, const uint32_t& tid, const string& ifname, const string& vif, const IPv6& address, const bool& enabled); XrlCmdError ifmgr_0_1_get_configured_address_enabled6( // Input values, const string& ifname, const string& vif, const IPv6& address, bool& enabled); XrlCmdError ifmgr_0_1_set_prefix6( // Input values, const uint32_t& tid, const string& ifname, const string& vif, const IPv6& address, const uint32_t& prefix_len); XrlCmdError ifmgr_0_1_get_configured_prefix6( // Input values, const string& ifname, const string& vif, const IPv6& address, // Output values, uint32_t& prefix_len); XrlCmdError ifmgr_0_1_set_endpoint6( // Input values, const uint32_t& tid, const string& ifname, const string& vif, const IPv6& address, const IPv6& endpoint); XrlCmdError ifmgr_0_1_get_configured_endpoint6( // Input values, const string& ifname, const string& vif, const IPv6& address, // Output values, IPv6& endpoint); #endif XrlCmdError ifmgr_replicator_0_1_register_ifmgr_mirror( // Input values, const string& clientname); XrlCmdError ifmgr_replicator_0_1_unregister_ifmgr_mirror( // Input values, const string& clientname); // // Forwarding Table Interface // XrlCmdError fti_0_2_lookup_route_by_dest4( // Input values, const IPv4& host, // Output values, IPv4Net& netmask, IPv4& nexthop, string& ifname, string& vifname, uint32_t& metric, uint32_t& admin_distance, string& protocol_origin); XrlCmdError fti_0_2_lookup_route_by_network4( // Input values, const IPv4Net& dst, // Output values, IPv4& nexthop, string& ifname, string& vifname, uint32_t& metric, uint32_t& admin_distance, string& protocol_origin); XrlCmdError fti_0_2_have_ipv4( // Output values, bool& result); XrlCmdError fti_0_2_get_unicast_forwarding_enabled4( // Output values, bool& enabled); XrlCmdError fti_0_2_set_unicast_forwarding_enabled4( // Input values, const bool& enabled); /** * Set the IPv4 unicast forwarding engine whether to retain existing XORP * forwarding entries on startup. * * @param retain if true, then retain the XORP forwarding entries, * otherwise delete them. */ XrlCmdError fti_0_2_set_unicast_forwarding_entries_retain_on_startup4( // Input values, const bool& retain); /** * Set the IPv4 unicast forwarding engine whether to retain existing XORP * forwarding entries on shutdown. * * @param retain if true, then retain the XORP forwarding entries, * otherwise delete them. */ XrlCmdError fti_0_2_set_unicast_forwarding_entries_retain_on_shutdown4( // Input values, const bool& retain); /** * Set the IPv4 unicast forwarding table ID to be used. * * @param is_configured if true, the forwarding table ID is configured, * otherwise the default table should be used. * * @param table_id the IPv4 unicast forwarding table ID to be used. */ XrlCmdError fti_0_2_set_unicast_forwarding_table_id4( // Input values, const bool& is_configured, const uint32_t& table_id); #ifdef HAVE_IPV6 XrlCmdError fti_0_2_lookup_route_by_dest6( // Input values, const IPv6& host, // Output values, IPv6Net& netmask, IPv6& nexthop, string& ifname, string& vifname, uint32_t& metric, uint32_t& admin_distance, string& protocol_origin); XrlCmdError fti_0_2_lookup_route_by_network6( // Input values, const IPv6Net& dst, // Output values, IPv6& nexthop, string& ifname, string& vifname, uint32_t& metric, uint32_t& admin_distance, string& protocol_origin); XrlCmdError fti_0_2_have_ipv6( // Output values, bool& result); XrlCmdError fti_0_2_get_unicast_forwarding_enabled6( // Output values, bool& enabled); XrlCmdError fti_0_2_set_unicast_forwarding_enabled6( // Input values, const bool& enabled); /** * Set the IPv6 unicast forwarding engine whether to retain existing XORP * forwarding entries on startup. * * @param retain if true, then retain the XORP forwarding entries, * otherwise delete them. */ XrlCmdError fti_0_2_set_unicast_forwarding_entries_retain_on_startup6( // Input values, const bool& retain); /** * Set the IPv6 unicast forwarding engine whether to retain existing XORP * forwarding entries on shutdown. * * @param retain if true, then retain the XORP forwarding entries, * otherwise delete them. */ XrlCmdError fti_0_2_set_unicast_forwarding_entries_retain_on_shutdown6( // Input values, const bool& retain); /** * Set the IPv6 unicast forwarding table ID to be used. * * @param is_configured if true, the forwarding table ID is configured, * otherwise the default table should be used. * * @param table_id the IPv6 unicast forwarding table ID to be used. */ XrlCmdError fti_0_2_set_unicast_forwarding_table_id6( // Input values, const bool& is_configured, const uint32_t& table_id); #endif // // RIB routes redistribution transaction-based XRL interface // /** * Start transaction. * * @param tid the transaction ID to use for this transaction. */ XrlCmdError redist_transaction4_0_1_start_transaction( // Output values, uint32_t& tid); /** * Commit transaction. * * @param tid the transaction ID of this transaction. */ XrlCmdError redist_transaction4_0_1_commit_transaction( // Input values, const uint32_t& tid); /** * Abort transaction. * * @param tid the transaction ID of this transaction. */ XrlCmdError redist_transaction4_0_1_abort_transaction( // Input values, const uint32_t& tid); /** * Add/delete a routing entry. * * @param tid the transaction ID of this transaction. * * @param dst destination network. * * @param nexthop nexthop router address. * * @param ifname interface name associated with nexthop. * * @param vifname virtual interface name with nexthop. * * @param metric origin routing protocol metric for route. * * @param admin_distance administrative distance of origin routing * protocol. * * @param cookie value set by the requestor to identify redistribution * source. Typical value is the originating protocol name. * * @param protocol_origin the name of the protocol that originated this * routing entry. */ XrlCmdError redist_transaction4_0_1_add_route( // Input values, const uint32_t& tid, const IPv4Net& dst, const IPv4& nexthop, const string& ifname, const string& vifname, const uint32_t& metric, const uint32_t& admin_distance, const string& cookie, const string& protocol_origin); XrlCmdError redist_transaction4_0_1_delete_route( // Input values, const uint32_t& tid, const IPv4Net& dst, const IPv4& nexthop, const string& ifname, const string& vifname, const uint32_t& metric, const uint32_t& admin_distance, const string& cookie, const string& protocol_origin); /** * Delete all routing entries. * * @param tid the transaction ID of this transaction. * * @param cookie value set by the requestor to identify redistribution * source. Typical value is the originating protocol name. */ XrlCmdError redist_transaction4_0_1_delete_all_routes( // Input values, const uint32_t& tid, const string& cookie); #ifdef HAVE_IPV6 /** * Start transaction. * * @param tid the transaction ID to use for this transaction. */ XrlCmdError redist_transaction6_0_1_start_transaction( // Output values, uint32_t& tid); /** * Commit transaction. * * @param tid the transaction ID of this transaction. */ XrlCmdError redist_transaction6_0_1_commit_transaction( // Input values, const uint32_t& tid); /** * Abort transaction. * * @param tid the transaction ID of this transaction. */ XrlCmdError redist_transaction6_0_1_abort_transaction( // Input values, const uint32_t& tid); /** * Add/delete a routing entry. * * @param tid the transaction ID of this transaction. * * @param dst destination network. * * @param nexthop nexthop router address. * * @param ifname interface name associated with nexthop. * * @param vifname virtual interface name with nexthop. * * @param metric origin routing protocol metric for route. * * @param admin_distance administrative distance of origin routing * protocol. * * @param cookie value set by the requestor to identify redistribution * source. Typical value is the originating protocol name. * * @param protocol_origin the name of the protocol that originated this * routing entry. */ XrlCmdError redist_transaction6_0_1_add_route( // Input values, const uint32_t& tid, const IPv6Net& dst, const IPv6& nexthop, const string& ifname, const string& vifname, const uint32_t& metric, const uint32_t& admin_distance, const string& cookie, const string& protocol_origin); XrlCmdError redist_transaction6_0_1_delete_route( // Input values, const uint32_t& tid, const IPv6Net& dst, const IPv6& nexthop, const string& ifname, const string& vifname, const uint32_t& metric, const uint32_t& admin_distance, const string& cookie, const string& protocol_origin); /** * Delete all routing entries. * * @param tid the transaction ID of this transaction. * * @param cookie value set by the requestor to identify redistribution * source. Typical value is the originating protocol name. */ XrlCmdError redist_transaction6_0_1_delete_all_routes( // Input values, const uint32_t& tid, const string& cookie); #endif //ipv6 // // Raw Link-Level Server Interface // /** * Send a raw link-level packet on an interface. * * @param if_name the interface to send the packet on. * * @param vif_name the vif to send the packet on. * * @param src_address the MAC source address. * * @param dst_address the MAC destination address. * * @param ether_type the EtherType protocol type or the Destination SAP. * It must be between 1536 and 65535 to specify the EtherType, or between * 1 and 255 to specify the Destination SAP IEEE 802.2 LLC frames. * * @param payload the payload, everything after the MAC header. */ XrlCmdError raw_link_0_1_send( // Input values, const string& if_name, const string& vif_name, const Mac& src_address, const Mac& dst_address, const uint32_t& ether_type, const vector& payload); /** * Register to receive raw link-level packets. The receiver is expected to * support raw_link_client/0.1 interface. * * @param xrl_target_instance_name the receiver's XRL target instance * name. * * @param if_name the interface through which packets should be accepted. * * @param vif_name the vif through which packets should be accepted. * * @param ether_type the EtherType protocol number or the Destination SAP * that the receiver is interested in. It must be between 1536 and 65535 * to specify the EtherType, or between 1 and 255 to specify the * Destination SAP for IEEE 802.2 LLC frames. A protocol number of 0 is * used to specify all protocols. * * @param filter_program the filter program to be applied on the received * packets. The program uses tcpdump(1) style expression. * * @param enable_multicast_loopback if true then enable delivering of * multicast datagrams back to this host (assuming the host is a member of * the same multicast group). */ XrlCmdError raw_link_0_1_register_receiver( // Input values, const string& xrl_target_instance_name, const string& if_name, const string& vif_name, const uint32_t& ether_type, const string& filter_program, const bool& enable_multicast_loopback); /** * Unregister to receive raw link-level packets. * * @param xrl_target_instance_name the receiver's XRL target instance * name. * * @param if_name the interface through which packets should not be * accepted. * * @param vif_name the vif through which packets should not be accepted. * * @param ether_type the EtherType protocol number or the Destination SAP * that the receiver is not interested in anymore. It must be between 1536 * and 65535 to specify the EtherType, or between 1 and 255 to specify the * Destination SAP for IEEE 802.2 LLC frames. A protocol number of 0 is * used to specify all protocols. * * @param filter_program the filter program that was applied on the * received packets. The program uses tcpdump(1) style expression. */ XrlCmdError raw_link_0_1_unregister_receiver( // Input values, const string& xrl_target_instance_name, const string& if_name, const string& vif_name, const uint32_t& ether_type, const string& filter_program); /** * Join a MAC multicast group. * * @param xrl_target_instance_name the receiver's XRL target instance * name. * * @param if_name the interface through which packets should be accepted. * * @param vif_name the vif through which packets should be accepted. * * @param ether_type the EtherType protocol number or the Destination SAP * that the receiver is interested in. It must be between 1536 and 65535 * to specify the EtherType, or between 1 and 255 to specify the * Destination SAP for IEEE 802.2 LLC frames. A protocol number of 0 is * used to specify all protocols. * * @param filter_program the filter program to be applied on the received * packets. The program uses tcpdump(1) style expression. * * @param group_address the multicast group address to join. */ XrlCmdError raw_link_0_1_join_multicast_group( // Input values, const string& xrl_target_instance_name, const string& if_name, const string& vif_name, const uint32_t& ether_type, const string& filter_program, const Mac& group_address); /** * Leave a MAC multicast group. * * @param xrl_target_instance_name the receiver's XRL target instance * name. * * @param if_name the interface through which packets should not be * accepted. * * @param vif_name the vif through which packets should not be accepted. * * @param ether_type the EtherType protocol number or the Destination SAP * that the receiver is not interested in anymore. It must be between 1536 * and 65535 to specify the EtherType, or between 1 and 255 to specify the * Destination SAP for IEEE 802.2 LLC frames. A protocol number of 0 is * used to specify all protocols. * * @param filter_program the filter program that was applied on the * received packets. The program uses tcpdump(1) style expression. * * @param group_address the multicast group address to leave. */ XrlCmdError raw_link_0_1_leave_multicast_group( // Input values, const string& xrl_target_instance_name, const string& if_name, const string& vif_name, const uint32_t& ether_type, const string& filter_program, const Mac& group_address); // // IPv4 Raw Socket Server Interface // /** * Send an IPv4 packet on a raw socket. * * @param if_name the interface to send the packet on. It is essential for * multicast. In the unicast case this field may be empty. * * @param vif_name the vif to send the packet on. It is essential for * multicast. In the unicast case this field may be empty. * * @param src_address the IP source address. * * @param dst_address the IP destination address. * * @param ip_protocol the IP protocol number. It must be between 1 and * 255. * * @param ip_ttl the IP TTL (hop-limit). If it has a negative value, the * TTL will be set internally before transmission. * * @param ip_tos the Type Of Service (Diffserv/ECN bits for IPv4). If it * has a negative value, the TOS will be set internally before * transmission. * * @param ip_router_alert if true, then add the IP Router Alert option to * the IP packet. * * @param ip_internet_control if true, then this is IP control traffic. * * @param payload the payload, everything after the IP header and options. */ XrlCmdError raw_packet4_0_1_send( // Input values, const string& if_name, const string& vif_name, const IPv4& src_address, const IPv4& dst_address, const uint32_t& ip_protocol, const int32_t& ip_ttl, const int32_t& ip_tos, const bool& ip_router_alert, const bool& ip_internet_control, const vector& payload); /** * Register to receive IPv4 packets. The receiver is expected to support * raw_packet4_client/0.1 interface. * * @param xrl_target_instance_name the receiver's XRL target instance * name. * * @param if_name the interface through which packets should be accepted. * * @param vif_name the vif through which packets should be accepted. * * @param ip_protocol the IP protocol number that the receiver is * interested in. It must be between 0 and 255. A protocol number of 0 is * used to specify all protocols. * * @param enable_multicast_loopback if true then enable delivering of * multicast datagrams back to this host (assuming the host is a member of * the same multicast group. */ XrlCmdError raw_packet4_0_1_register_receiver( // Input values, const string& xrl_target_instance_name, const string& if_name, const string& vif_name, const uint32_t& ip_protocol, const bool& enable_multicast_loopback); /** * Unregister to receive IPv4 packets. * * @param xrl_target_instance_name the receiver's XRL target instance * name. * * @param if_name the interface through which packets should not be * accepted. * * @param vif_name the vif through which packets should not be accepted. * * @param ip_protocol the IP Protocol number that the receiver is not * interested in anymore. It must be between 0 and 255. A protocol number * of 0 is used to specify all protocols. */ XrlCmdError raw_packet4_0_1_unregister_receiver( // Input values, const string& xrl_target_instance_name, const string& if_name, const string& vif_name, const uint32_t& ip_protocol); /** * Join an IPv4 multicast group. * * @param xrl_target_instance_name the receiver's XRL target instance * name. * * @param if_name the interface through which packets should be accepted. * * @param vif_name the vif through which packets should be accepted. * * @param ip_protocol the IP protocol number that the receiver is * interested in. It must be between 0 and 255. A protocol number of 0 is * used to specify all protocols. * * @param group_address the multicast group address to join. */ XrlCmdError raw_packet4_0_1_join_multicast_group( // Input values, const string& xrl_target_instance_name, const string& if_name, const string& vif_name, const uint32_t& ip_protocol, const IPv4& group_address); /** * Leave an IPv4 multicast group. * * @param xrl_target_instance_name the receiver's XRL target instance * name. * * @param if_name the interface through which packets should not be * accepted. * * @param vif_name the vif through which packets should not be accepted. * * @param ip_protocol the IP protocol number that the receiver is not * interested in anymore. It must be between 0 and 255. A protocol number * of 0 is used to specify all protocols. * * @param group_address the multicast group address to leave. */ XrlCmdError raw_packet4_0_1_leave_multicast_group( // Input values, const string& xrl_target_instance_name, const string& if_name, const string& vif_name, const uint32_t& ip_protocol, const IPv4& group_address); #ifdef HAVE_IPV6 // // IPv6 Raw Socket Server Interface // /** * Send an IPv6 packet on a raw socket. * * @param if_name the interface to send the packet on. It is essential for * multicast. In the unicast case this field may be empty. * * @param vif_name the vif to send the packet on. It is essential for * multicast. In the unicast case this field may be empty. * * @param src_address the IP source address. * * @param dst_address the IP destination address. * * @param ip_protocol the IP protocol number. It must be between 1 and * 255. * * @param ip_ttl the IP TTL (hop-limit). If it has a negative value, the * TTL will be set internally before transmission. * * @param ip_tos the Type Of Service (IP traffic class for IPv6). If it * has a negative value, the TOS will be set internally before * transmission. * * @param ip_router_alert if true, then add the IP Router Alert option to * the IP packet. * * @param ip_internet_control if true, then this is IP control traffic. * * @param ext_headers_type a list of u32 integers with the types of the * optional extention headers. * * @param ext_headers_payload a list of payload data, one for each * optional extention header. The number of entries must match * ext_headers_type. * * @param payload the payload, everything after the IP header and options. */ XrlCmdError raw_packet6_0_1_send( // Input values, const string& if_name, const string& vif_name, const IPv6& src_address, const IPv6& dst_address, const uint32_t& ip_protocol, const int32_t& ip_ttl, const int32_t& ip_tos, const bool& ip_router_alert, const bool& ip_internet_control, const XrlAtomList& ext_headers_type, const XrlAtomList& ext_headers_payload, const vector& payload); /** * Register to receive IPv6 packets. The receiver is expected to support * raw_packet6_client/0.1 interface. * * @param xrl_target_instance_name the receiver's XRL target instance * name. * * @param if_name the interface through which packets should be accepted. * * @param vif_name the vif through which packets should be accepted. * * @param ip_protocol the IP protocol number that the receiver is * interested in. It must be between 0 and 255. A protocol number of 0 is * used to specify all protocols. * * @param enable_multicast_loopback if true then enable delivering of * multicast datagrams back to this host (assuming the host is a member of * the same multicast group. */ XrlCmdError raw_packet6_0_1_register_receiver( // Input values, const string& xrl_target_instance_name, const string& if_name, const string& vif_name, const uint32_t& ip_protocol, const bool& enable_multicast_loopback); /** * Unregister to receive IPv6 packets. * * @param xrl_target_instance_name the receiver's XRL target instance * name. * * @param if_name the interface through which packets should not be * accepted. * * @param vif_name the vif through which packets should not be accepted. * * @param ip_protocol the IP Protocol number that the receiver is not * interested in anymore. It must be between 0 and 255. A protocol number * of 0 is used to specify all protocols. */ XrlCmdError raw_packet6_0_1_unregister_receiver( // Input values, const string& xrl_target_instance_name, const string& if_name, const string& vif_name, const uint32_t& ip_protocol); /** * Join an IPv6 multicast group. * * @param xrl_target_instance_name the receiver's XRL target instance * name. * * @param if_name the interface through which packets should be accepted. * * @param vif_name the vif through which packets should be accepted. * * @param ip_protocol the IP protocol number that the receiver is * interested in. It must be between 0 and 255. A protocol number of 0 is * used to specify all protocols. * * @param group_address the multicast group address to join. */ XrlCmdError raw_packet6_0_1_join_multicast_group( // Input values, const string& xrl_target_instance_name, const string& if_name, const string& vif_name, const uint32_t& ip_protocol, const IPv6& group_address); /** * Leave an IPv6 multicast group. * * @param xrl_target_instance_name the receiver's XRL target instance * name. * * @param if_name the interface through which packets should not be * accepted. * * @param vif_name the vif through which packets should not be accepted. * * @param ip_protocol the IP protocol number that the receiver is not * interested in anymore. It must be between 0 and 255. A protocol number * of 0 is used to specify all protocols. * * @param group_address the multicast group address to leave. */ XrlCmdError raw_packet6_0_1_leave_multicast_group( // Input values, const string& xrl_target_instance_name, const string& if_name, const string& vif_name, const uint32_t& ip_protocol, const IPv6& group_address); #endif // // TCP/UDP I/O Socket Server Interface // /** * Open a TCP socket. * * @param creator the Xrl Target instance name of the socket creator. The * named target must implement socket4_user/0.1. * * @param sockid return parameter that contains unique socket ID when * socket instantiation is successful. */ XrlCmdError socket4_0_1_tcp_open( // Input values, const string& creator, // Output values, string& sockid); /** * Open an UDP socket. * * @param creator the Xrl Target instance name of the socket creator. The * named target must implement socket4_user/0.1. * * @param sockid return parameter that contains unique socket ID when * socket instantiation is successful. */ XrlCmdError socket4_0_1_udp_open( // Input values, const string& creator, // Output values, string& sockid); /** * Create a bound TCP socket. * * @param creator the Xrl Target instance name of the socket creator. The * named target must implement socket4_user/0.1. * * @param local_addr the interface address to bind socket to. * * @param local_port the port to bind socket to. * * @param sockid return parameter that contains unique socket ID when * socket instantiation is successful. */ XrlCmdError socket4_0_1_tcp_open_and_bind( // Input values, const string& creator, const IPv4& local_addr, const uint32_t& local_port, // Output values, string& sockid); /** * Create a bound UDP socket. * * @param creator the Xrl Target instance name of the socket creator. The * named target must implement socket4_user/0.1. * * @param local_addr the interface address to bind socket to. * * @param local_port the port to bind socket to. * * @param sockid return parameter that contains unique socket ID when * socket instantiation is successful. */ XrlCmdError socket4_0_1_udp_open_and_bind( // Input values, const string& creator, const IPv4& local_addr, const uint32_t& local_port, const string& local_dev, const uint32_t& reuse, // Output values, string& sockid); /** * Create a bound UDP multicast socket. * * @param creator the Xrl Target instance name of the socket creator. The * named target must implement socket4_user/0.1. * * @param local_addr the interface address to bind socket to. * * @param local_port the port to bind socket to. * * @param mcast_addr the multicast group address to join. * * @param ttl the TTL to use for this multicast socket. * * @param reuse allow other sockets to bind to same multicast group. * * @param sockid return parameter that contains unique socket ID when * socket instantiation is successful. */ XrlCmdError socket4_0_1_udp_open_bind_join( // Input values, const string& creator, const IPv4& local_addr, const uint32_t& local_port, const IPv4& mcast_addr, const uint32_t& ttl, const bool& reuse, // Output values, string& sockid); /** * Create a bound and connected TCP socket. * * @param creator the Xrl Target instance name of the socket creator. The * named target must implement socket4_user/0.1. * * @param local_addr the interface address to bind socket to. * * @param local_port the port to bind socket to. * * @param remote_addr the address to connect to. * * @param remote_port the remote port to connect to. * * @param sockid return parameter that contains unique socket ID when * socket instantiation is successful. */ XrlCmdError socket4_0_1_tcp_open_bind_connect( // Input values, const string& creator, const IPv4& local_addr, const uint32_t& local_port, const IPv4& remote_addr, const uint32_t& remote_port, // Output values, string& sockid); /** * Create a bound and connected UDP socket. * * @param creator the Xrl Target instance name of the socket creator. The * named target must implement socket4_user/0.1. * * @param local_addr the interface address to bind socket to. * * @param local_port the port to bind socket to. * * @param remote_addr the address to connect to. * * @param remote_port the remote port to connect to. * * @param sockid return parameter that contains unique socket ID when * socket instantiation is successful. */ XrlCmdError socket4_0_1_udp_open_bind_connect( // Input values, const string& creator, const IPv4& local_addr, const uint32_t& local_port, const IPv4& remote_addr, const uint32_t& remote_port, // Output values, string& sockid); /** * Create a bound and connected UDP broadcast socket. * * This socket may be used for sending and receiving IPv4 broadcasts * on a named if/vif. The TTL is always set to 1 on creation. The * creator must specify if this socket is to be used for limited * broadcasts (255.255.255.255) as this is a special case on many * platforms. * * @param creator the Xrl Target instance name of the socket * creator. The named target must implement socket4_user/0.1. * * @param ifname the interface name to bind socket to. * * @param vifname the vif to bind socket to. * * @param local_port the port to bind socket to. * * @param remote_port the remote port to connect to. * * @param reuse allow other sockets to bind to same port. * * @param limited set the socket up for transmission to the limited * broadcast address 255.255.255.255. * * @param connected connect the socket for use with send() not sendto(). * * @param sockid return parameter that contains unique socket ID when * socket instantiation is successful. */ XrlCmdError socket4_0_1_udp_open_bind_broadcast( // Input values, const string& creator, const string& ifname, const string& vifname, const uint32_t& local_port, const uint32_t& remote_port, const bool& reuse, const bool& limited, const bool& connected, // Output values, string& sockid); /** * Bind a socket. * * @param sockid the socket ID of the socket to bind. * * @param local_addr the interface address to bind socket to. * * @param local_port the port to bind socket to. */ XrlCmdError socket4_0_1_bind( // Input values, const string& sockid, const IPv4& local_addr, const uint32_t& local_port); /** * Join multicast group on already bound socket. * * @param sockid unique socket ID. * * @param mcast_addr group to join. * * @param join_if_addr interface address to perform join on. */ XrlCmdError socket4_0_1_udp_join_group( // Input values, const string& sockid, const IPv4& mcast_addr, const IPv4& join_if_addr); /** * Leave multicast group on already bound socket. * * @param sockid unique socket ID. * * @param mcast_addr group to leave. * * @param leave_if_addr interface address to perform leave on. */ XrlCmdError socket4_0_1_udp_leave_group( // Input values, const string& sockid, const IPv4& mcast_addr, const IPv4& leave_if_addr); /** * Close socket. * * @param sockid unique socket ID of socket to be closed. */ XrlCmdError socket4_0_1_close( // Input values, const string& sockid); /** * Listen for inbound connections on socket. When a connection request * received the socket creator will receive notification through * socket4_user/0.1/connect_event. * * @param sockid the unique socket ID of the socket to perform listen. * * @param backlog the maximum number of pending connections. */ XrlCmdError socket4_0_1_tcp_listen( // Input values, const string& sockid, const uint32_t& backlog); /** * Enable a UDP socket for datagram reception. * * If a UDP socket has been created without using the usual convenience * XRLs, it is necessary to hook up its FEA internal input path by * calling this XRL. It is similar in intent to tcp_listen, but named * differently as it never uses the listen() socket API. * * @param sockid the unique socket ID of the socket to enable for * datagram reception. */ XrlCmdError socket4_0_1_udp_enable_recv( // Input values, const string& sockid); /** * Send data on socket. * * @param sockid unique socket ID. * * @param data block of data to be sent. */ XrlCmdError socket4_0_1_send( // Input values, const string& sockid, const vector& data); /** * Send data on socket to a given destination. The packet is not routed as * the forwarding engine sending the packet may not have access to the * full routing table. * * @param sockid unique socket ID. * * @param remote_addr destination address for data. * * @param remote_port destination port for data. * * @param data block of data to be sent. */ XrlCmdError socket4_0_1_send_to( // Input values, const string& sockid, const IPv4& remote_addr, const uint32_t& remote_port, const vector& data); /** * Send data on socket to a given multicast group from a given interface. * * @param sockid unique socket ID. * * @param group_addr destination address for data. * * @param group_port destination port for data. * * @param ifaddr interface address */ XrlCmdError socket4_0_1_send_from_multicast_if( // Input values, const string& sockid, const IPv4& group_addr, const uint32_t& group_port, const IPv4& ifaddr, const vector& data); /** * Set a named socket option with an integer value. * * @param sockid unique socket ID. * * @param optname name of option to be set. Valid values are: * "onesbcast" * "receive_broadcast" * "reuseport" * "send_broadcast" * "tos" * "ttl" * "multicast_loopback" * "multicast_ttl" * * @param optval value of option to be set. If value is logically boolean * then zero represents false and any non-zero value true. */ XrlCmdError socket4_0_1_set_socket_option( // Input values, const string& sockid, const string& optname, const uint32_t& optval); /** * Set a named socket option with a text value. * * XXX: The "bindtodevice" option exists to workaround an architectural * issue in the Linux Ipv4 stack. It SHOULD NOT be used for new code. * * @param sockid unique socket ID. * * @param optname name of option to be set. Valid values are: * "bindtodevice" * * @param optval value of option to be set. If value is logically boolean * then zero represents false and any non-zero value true. */ XrlCmdError socket4_0_1_set_socket_option_txt( // Input values, const string& sockid, const string& optname, const string& optval); #ifdef HAVE_IPV6 /** * Open a TCP socket. * * @param creator the Xrl Target instance name of the socket creator. The * named target must implement socket4_user/0.1. * * @param sockid return parameter that contains unique socket ID when * socket instantiation is successful. */ XrlCmdError socket6_0_1_tcp_open( // Input values, const string& creator, // Output values, string& sockid); /** * Open an UDP socket. * * @param creator the Xrl Target instance name of the socket creator. The * named target must implement socket4_user/0.1. * * @param sockid return parameter that contains unique socket ID when * socket instantiation is successful. */ XrlCmdError socket6_0_1_udp_open( // Input values, const string& creator, // Output values, string& sockid); /** * Create a bound TCP socket. * * @param creator the Xrl Target instance name of the socket creator. The * named target must implement socket6_user/0.1. * * @param local_addr the interface address to bind socket to. * * @param local_port the port to bind socket to. * * @param sockid return parameter that contains unique socket ID when * socket instantiation is successful. */ XrlCmdError socket6_0_1_tcp_open_and_bind( // Input values, const string& creator, const IPv6& local_addr, const uint32_t& local_port, // Output values, string& sockid); /** * Create a bound UDP socket. * * @param creator the Xrl Target instance name of the socket creator. The * named target must implement socket6_user/0.1. * * @param local_addr the interface address to bind socket to. * * @param local_port the port to bind socket to. * * @param sockid return parameter that contains unique socket ID when * socket instantiation is successful. */ XrlCmdError socket6_0_1_udp_open_and_bind( // Input values, const string& creator, const IPv6& local_addr, const uint32_t& local_port, const string& local_dev, const uint32_t& reuse, // Output values, string& sockid); /** * Create a bound UDP multicast socket. * * @param creator the Xrl Target instance name of the socket creator. The * named target must implement socket6_user/0.1. * * @param local_addr the interface address to bind socket to. * * @param local_port the port to bind socket to. * * @param mcast_addr the multicast group address to join. * * @param ttl the TTL to use for this multicast socket. * * @param reuse allow other sockets to bind to same multicast group. * * @param sockid return parameter that contains unique socket ID when * socket instantiation is successful. */ XrlCmdError socket6_0_1_udp_open_bind_join( // Input values, const string& creator, const IPv6& local_addr, const uint32_t& local_port, const IPv6& mcast_addr, const uint32_t& ttl, const bool& reuse, // Output values, string& sockid); /** * Create a bound and connected TCP socket. * * @param creator the Xrl Target instance name of the socket creator. The * named target must implement socket6_user/0.1. * * @param local_addr the interface address to bind socket to. * * @param local_port the port to bind socket to. * * @param remote_addr the address to connect to. * * @param remote_port the remote port to connect to. * * @param sockid return parameter that contains unique socket ID when * socket instantiation is successful. */ XrlCmdError socket6_0_1_tcp_open_bind_connect( // Input values, const string& creator, const IPv6& local_addr, const uint32_t& local_port, const IPv6& remote_addr, const uint32_t& remote_port, // Output values, string& sockid); /** * Create a bound and connected UDP socket. * * @param creator the Xrl Target instance name of the socket creator. The * named target must implement socket6_user/0.1. * * @param local_addr the interface address to bind socket to. * * @param local_port the port to bind socket to. * * @param remote_addr the address to connect to. * * @param remote_port the remote port to connect to. * * @param sockid return parameter that contains unique socket ID when * socket instantiation is successful. */ XrlCmdError socket6_0_1_udp_open_bind_connect( // Input values, const string& creator, const IPv6& local_addr, const uint32_t& local_port, const IPv6& remote_addr, const uint32_t& remote_port, // Output values, string& sockid); /** * Bind a socket. * * @param sockid the socket ID of the socket to bind. * * @param local_addr the interface address to bind socket to. * * @param local_port the port to bind socket to. */ XrlCmdError socket6_0_1_bind( // Input values, const string& sockid, const IPv6& local_addr, const uint32_t& local_port); /** * Join multicast group on already bound socket. * * @param sockid unique socket ID. * * @param mcast_addr group to join. * * @param join_if_addr interface address to perform join on. */ XrlCmdError socket6_0_1_udp_join_group( // Input values, const string& sockid, const IPv6& mcast_addr, const IPv6& join_if_addr); /** * Leave multicast group on already bound socket. * * @param sockid unique socket ID. * * @param mcast_addr group to leave. * * @param leave_if_addr interface address to perform leave on. */ XrlCmdError socket6_0_1_udp_leave_group( // Input values, const string& sockid, const IPv6& mcast_addr, const IPv6& leave_if_addr); /** * Close socket. * * @param sockid unique socket ID of socket to be closed. */ XrlCmdError socket6_0_1_close( // Input values, const string& sockid); /** * Listen for inbound connections on socket. When a connection request * received the socket creator will receive notification through * socket6_user/0.1/connect_event. * * @param sockid the unique socket ID of the socket to perform listen. * * @param backlog the maximum number of pending connections. */ XrlCmdError socket6_0_1_tcp_listen( // Input values, const string& sockid, const uint32_t& backlog); /** * Send data on socket. * * @param sockid unique socket ID. * * @param data block of data to be sent. */ XrlCmdError socket6_0_1_send( // Input values, const string& sockid, const vector& data); /** * Send data on socket to a given destination. The packet is not routed as * the forwarding engine sending the packet may not have access to the * full routing table. * * @param sockid unique socket ID. * * @param remote_addr destination address for data. * * @param remote_port destination port for data. * * @param data block of data to be sent. */ XrlCmdError socket6_0_1_send_to( // Input values, const string& sockid, const IPv6& remote_addr, const uint32_t& remote_port, const vector& data); /** * Send data on socket to a given multicast group from a given interface. * * @param sockid unique socket ID. * * @param group_addr destination address for data. * * @param group_port destination port for data. * * @param ifaddr interface address */ XrlCmdError socket6_0_1_send_from_multicast_if( // Input values, const string& sockid, const IPv6& group_addr, const uint32_t& group_port, const IPv6& ifaddr, const vector& data); /** * Set a named socket option. * * @param sockid unique socket ID. * * @param optname name of option to be set. Valid values are: * "reuseport" * "ttl" * "multicast_loopback" * "multicast_ttl" * * @param optval value of option to be set. If value is logically boolean * then zero represents false and any non-zero value true. */ XrlCmdError socket6_0_1_set_socket_option( // Input values, const string& sockid, const string& optname, const uint32_t& optval); #endif #ifndef XORP_DISABLE_PROFILE // // Profile-related interface // XrlCmdError profile_0_1_enable( // Input values, const string& pname); XrlCmdError profile_0_1_disable( // Input values, const string& pname); XrlCmdError profile_0_1_get_entries( // Input values, const string& pname, const string& instance_name); XrlCmdError profile_0_1_clear( // Input values, const string& pname); XrlCmdError profile_0_1_list( // Output values, string& info); #endif private: /** * Add/remove a multicast MAC address on an interface. * * @param add if true, then add the address, otherwise remove it. * @param ifname the interface name. * @param mac the address to add/remove. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_remove_mac(bool add, const string& ifname, const Mac& mac, string& error_msg); /** * Set the MAC address on an interface. * * @param ifname the interface name. * @param mac the address to set. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_mac(const string& ifname, const Mac& mac, string& error_msg); int add_remove_address(bool add, const string& ifname, const string& vifname, const IPv4& ip, uint32_t prefix, string& error_msg); /** * Send gratuitous ARP packets for all IP addresses on on an interface. * * @param ifname the interface name. * @param mac the MAC address that is to be updated. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int send_gratuitous_arps(const string& ifname, const Mac& mac, string& error_msg); EventLoop& _eventloop; // The event loop to use FeaNode& _fea_node; // The corresponding FeaNode XrlRouter& _xrl_router; #ifndef XORP_DISABLE_PROFILE Profile& _profile; #endif XrlFibClientManager& _xrl_fib_client_manager; IfConfig& _ifconfig; #ifndef XORP_DISABLE_FIREWALL FirewallManager& _firewall_manager; #endif FibConfig& _fibconfig; IoLinkManager& _io_link_manager; IoIpManager& _io_ip_manager; IoTcpUdpManager& _io_tcpudp_manager; LibFeaClientBridge& _lib_fea_client_bridge; bool _is_running; // True if the service is running bool _is_shutdown_received; // True if shutdown XRL request received #ifdef XORP_USE_CLICK // // The externally loadable managers // FeaDataPlaneManagerClick* _fea_data_plane_manager_click; #endif }; #endif // __FEA_XRL_FEA_TARGET_HH__ xorp/fea/fibconfig_entry_set.hh0000664000076400007640000001410511540225525016760 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/fibconfig_entry_set.hh,v 1.17 2008/10/02 21:56:45 bms Exp $ #ifndef __FEA_FIBCONFIG_ENTRY_SET_HH__ #define __FEA_FIBCONFIG_ENTRY_SET_HH__ #include "fte.hh" #include "iftree.hh" #include "fea_data_plane_manager.hh" class FibConfig; class FibConfigEntrySet { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ FibConfigEntrySet(FeaDataPlaneManager& fea_data_plane_manager) : _is_running(false), _fibconfig(fea_data_plane_manager.fibconfig()), _fea_data_plane_manager(fea_data_plane_manager), _in_configuration(false) {} /** * Virtual destructor. */ virtual ~FibConfigEntrySet() {} /** * Get the @ref FibConfig instance. * * @return the @ref FibConfig instance. */ FibConfig& fibconfig() { return _fibconfig; } /** * Get the @ref FeaDataPlaneManager instance. * * @return the @ref FeaDataPlaneManager instance. */ FeaDataPlaneManager& fea_data_plane_manager() { return _fea_data_plane_manager; } /** * Test whether this instance is running. * * @return true if the instance is running, otherwise false. */ virtual bool is_running() const { return _is_running; } /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg) = 0; /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg) = 0; /** * Start a configuration interval. * * All modifications to must be within a marked "configuration" interval. * * This method provides derived classes with a mechanism to perform * any actions necessary before forwarding table modifications can * be made. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start_configuration(string& error_msg) { // Nothing particular to do, just label start. return mark_configuration_start(error_msg); } /** * End of configuration interval. * * This method provides derived classes with a mechanism to * perform any actions necessary at the end of a configuration, eg * write a file. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int end_configuration(string& error_msg) { // Nothing particular to do, just label start. return mark_configuration_end(error_msg); } /** * Add a single IPv4 forwarding entry. * * Must be within a configuration interval. * * @param fte the entry to add. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int add_entry4(const Fte4& fte) = 0; /** * Delete a single IPv4 forwarding entry. * * Must be with a configuration interval. * * @param fte the entry to delete. Only destination and netmask are used. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int delete_entry4(const Fte4& fte) = 0; /** * Add a single IPv6 forwarding entry. * * Must be within a configuration interval. * * @param fte the entry to add. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int add_entry6(const Fte6& fte) = 0; /** * Delete a single IPv6 forwarding entry. * * Must be within a configuration interval. * * @param fte the entry to delete. Only destination and netmask are used. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int delete_entry6(const Fte6& fte) = 0; /** Routing table ID that we are interested in might have changed. Maybe something * can filter on this for increased efficiency. */ virtual int notify_table_id_change(uint32_t new_tbl) = 0; protected: /** * Mark start of a configuration. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int mark_configuration_start(string& error_msg) { if (_in_configuration != true) { _in_configuration = true; return (XORP_OK); } error_msg = c_format("Cannot start configuration: " "configuration in progress"); return (XORP_ERROR); } /** * Mark end of a configuration. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int mark_configuration_end(string& error_msg) { if (_in_configuration != false) { _in_configuration = false; return (XORP_OK); } error_msg = c_format("Cannot end configuration: " "configuration not in progress"); return (XORP_ERROR); } bool in_configuration() const { return _in_configuration; } // Misc other state bool _is_running; private: FibConfig& _fibconfig; FeaDataPlaneManager& _fea_data_plane_manager; bool _in_configuration; }; #endif // __FEA_FIBCONFIG_ENTRY_SET_HH__ xorp/fea/tools/0000775000076400007640000000000011642676211013561 5ustar greearbgreearbxorp/fea/tools/show_interfaces.cc0000664000076400007640000004457711642676211017274 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea/fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/status_codes.h" #include "libxorp/eventloop.hh" #include "show_interfaces.hh" #ifdef HAVE_GETOPT_H #include #endif InterfaceMonitor::InterfaceMonitor(EventLoop& eventloop, const string& class_name, const string& finder_hostname, uint16_t finder_port, const string& fea_target) : ServiceBase(class_name), _eventloop(eventloop), _ifmgr(eventloop, fea_target.c_str(), finder_hostname.c_str(), finder_port), _startup_requests_n(0), _shutdown_requests_n(0) { // // Set myself as an observer when the node status changes // set_observer(this); _ifmgr.set_observer(this); _ifmgr.attach_hint_observer(this); } InterfaceMonitor::~InterfaceMonitor() { // // Unset myself as an observer when the node status changes // unset_observer(this); shutdown(); _ifmgr.detach_hint_observer(this); _ifmgr.unset_observer(this); } int InterfaceMonitor::startup() { // // Test the service status // if ((ServiceBase::status() == SERVICE_STARTING) || (ServiceBase::status() == SERVICE_RUNNING)) return (XORP_OK); if (ServiceBase::status() != SERVICE_READY) return (XORP_ERROR); // // Transition to SERVICE_RUNNING occurs when all transient startup // operations are completed (e.g., after we have the interface/vif/address // state available, etc.) // ServiceBase::set_status(SERVICE_STARTING); // // Startup the interface manager // if (ifmgr_startup() != XORP_OK) { XLOG_ERROR("Cannot startup the interface mirroring manager"); ServiceBase::set_status(SERVICE_FAILED); return (XORP_ERROR); } return (XORP_OK); } int InterfaceMonitor::shutdown() { // // We cannot shutdown if our status is SERVICE_SHUTDOWN or SERVICE_FAILED. // if ((ServiceBase::status() == SERVICE_SHUTDOWN) || (ServiceBase::status() == SERVICE_FAILED)) { return (XORP_OK); } if (ServiceBase::status() != SERVICE_RUNNING) return (XORP_ERROR); // // Transition to SERVICE_SHUTDOWN occurs when all transient shutdown // operations are completed (e.g., after we have deregistered with the // FEA, etc.) // ServiceBase::set_status(SERVICE_SHUTTING_DOWN); // // Shutdown the interface manager // if (ifmgr_shutdown() != XORP_OK) { XLOG_ERROR("Cannot shutdown the interface mirroring manager"); ServiceBase::set_status(SERVICE_FAILED); return (XORP_ERROR); } return (XORP_OK); } void InterfaceMonitor::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 return; } if ((old_status == SERVICE_SHUTTING_DOWN) && (new_status == SERVICE_SHUTDOWN)) { // The shutdown process has completed 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)) { decr_shutdown_requests_n(); } } } void InterfaceMonitor::incr_startup_requests_n() { _startup_requests_n++; XLOG_ASSERT(_startup_requests_n > 0); } void InterfaceMonitor::decr_startup_requests_n() { XLOG_ASSERT(_startup_requests_n > 0); _startup_requests_n--; update_status(); } void InterfaceMonitor::incr_shutdown_requests_n() { _shutdown_requests_n++; XLOG_ASSERT(_shutdown_requests_n > 0); } void InterfaceMonitor::decr_shutdown_requests_n() { XLOG_ASSERT(_shutdown_requests_n > 0); _shutdown_requests_n--; update_status(); } void InterfaceMonitor::update_status() { // // Test if the startup process has completed // if (ServiceBase::status() == SERVICE_STARTING) { if (_startup_requests_n > 0) return; // The startup process has completed ServiceBase::set_status(SERVICE_RUNNING); return; } // // Test if the shutdown process has completed // if (ServiceBase::status() == SERVICE_SHUTTING_DOWN) { if (_shutdown_requests_n > 0) return; // The shutdown process has completed ServiceBase::set_status(SERVICE_SHUTDOWN); return; } // // Test if we have failed // if (ServiceBase::status() == SERVICE_FAILED) { return; } } void InterfaceMonitor::tree_complete() { decr_startup_requests_n(); // // XXX: we use same actions when the tree is completed or updates are made // updates_made(); } void InterfaceMonitor::updates_made() { } int InterfaceMonitor::ifmgr_startup() { int ret_value; // TODO: XXX: we should startup the ifmgr only if it hasn't started yet incr_startup_requests_n(); ret_value = _ifmgr.startup(); // // XXX: when the startup is completed, IfMgrHintObserver::tree_complete() // will be called. // return (ret_value); } int InterfaceMonitor::ifmgr_shutdown() { int ret_value; incr_shutdown_requests_n(); ret_value = _ifmgr.shutdown(); // // XXX: when the shutdown is completed, InterfaceMonitor::status_change() // will be called. // return (ret_value); } void InterfaceMonitor::print_interfaces(const string& print_iface_name) const { bool iface_found = false; IfMgrIfTree::IfMap::const_iterator ifmgr_iface_iter; IfMgrIfAtom::VifMap::const_iterator ifmgr_vif_iter; IfMgrVifAtom::IPv4Map::const_iterator a4_iter; IfMgrVifAtom::IPv6Map::const_iterator a6_iter; debug_msg("Begin iftree loop\n"); // // Iterate for all interface, vifs, and addresses, and print them // for (ifmgr_iface_iter = ifmgr_iftree().interfaces().begin(); ifmgr_iface_iter != ifmgr_iftree().interfaces().end(); ++ifmgr_iface_iter) { const IfMgrIfAtom& ifmgr_iface = ifmgr_iface_iter->second; const string& ifmgr_iface_name = ifmgr_iface.name(); if (print_iface_name.size() > 0) { if (print_iface_name != ifmgr_iface_name) { // Print only the specified interface and ignore the other continue; } iface_found = true; } if (ifmgr_iface.vifs().empty()) { // // Print interface-related information when there are no vifs // bool prev = false; fprintf(stdout, "%s: Flags:<", ifmgr_iface_name.c_str()); if (ifmgr_iface.no_carrier()) { if (prev) fprintf(stdout, ","); fprintf(stdout, "NO-CARRIER"); prev = true; } if (ifmgr_iface.enabled()) { if (prev) fprintf(stdout, ","); fprintf(stdout, "ENABLED"); prev = true; } if (ifmgr_iface.discard()) { if (prev) fprintf(stdout, ","); fprintf(stdout, "DISCARD"); prev = true; } if (ifmgr_iface.unreachable()) { if (prev) fprintf(stdout, ","); fprintf(stdout, "UNREACHABLE"); prev = true; } if (ifmgr_iface.management()) { if (prev) fprintf(stdout, ","); fprintf(stdout, "MANAGEMENT"); prev = true; } fprintf(stdout, "> mtu %d", ifmgr_iface.mtu()); // // Print the baudrate in bps/Kbps/Mbps/Gbps/Tbps // fprintf(stdout, " speed "); if (ifmgr_iface.baudrate() == 0) { fprintf(stdout, "unknown"); } else { do { uint64_t b = ifmgr_iface.baudrate(); // Test if bps if (b / 1000 == 0) { fprintf(stdout, "%u bps", XORP_UINT_CAST(b)); break; } // Test if Kbps b /= 1000; if (b / 1000 == 0) { fprintf(stdout, "%u Kbps", XORP_UINT_CAST(b)); break; } // Test if Mbps b /= 1000; if (b / 1000 == 0) { fprintf(stdout, "%u Mbps", XORP_UINT_CAST(b)); break; } // Test if Gbps b /= 1000; if (b / 1000 == 0) { fprintf(stdout, "%u Gbps", XORP_UINT_CAST(b)); break; } // Test if Tbps b /= 1000; if (b / 1000 == 0) { fprintf(stdout, "%u Tbps", XORP_UINT_CAST(b)); break; } // Default fprintf(stdout, "%u Tbps", XORP_UINT_CAST(b)); break; } while (false); } fprintf(stdout, "\n"); // // Print the physical interface index and MAC address // fprintf(stdout, " physical index %d\n", ifmgr_iface.pif_index()); if (!ifmgr_iface.mac().is_zero()) fprintf(stdout, " ether %s\n", ifmgr_iface.mac().str().c_str()); // // Print the VLAN-related information // if (ifmgr_iface.iface_type().size() || ifmgr_iface.vid().size() || ifmgr_iface.parent_ifname().size()) { fprintf(stdout, " type: %s parent interface: %s vid: %s\n", ifmgr_iface.iface_type().c_str(), ifmgr_iface.parent_ifname().c_str(), ifmgr_iface.vid().c_str()); } } 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(); // // Print the interface name, flags and MTU // bool prev = false; fprintf(stdout, "%s/%s: Flags:<", ifmgr_iface_name.c_str(), ifmgr_vif_name.c_str()); if (ifmgr_iface.no_carrier()) { if (prev) fprintf(stdout, ","); fprintf(stdout, "NO-CARRIER"); prev = true; } if (ifmgr_iface.enabled() && ifmgr_vif.enabled()) { if (prev) fprintf(stdout, ","); fprintf(stdout, "ENABLED"); prev = true; } if (ifmgr_iface.discard()) { if (prev) fprintf(stdout, ","); fprintf(stdout, "DISCARD"); prev = true; } if (ifmgr_iface.unreachable()) { if (prev) fprintf(stdout, ","); fprintf(stdout, "UNREACHABLE"); prev = true; } if (ifmgr_iface.management()) { if (prev) fprintf(stdout, ","); fprintf(stdout, "MANAGEMENT"); prev = true; } if (ifmgr_vif.broadcast_capable()) { if (prev) fprintf(stdout, ","); fprintf(stdout, "BROADCAST"); prev = true; } if (ifmgr_vif.multicast_capable()) { if (prev) fprintf(stdout, ","); fprintf(stdout, "MULTICAST"); prev = true; } if (ifmgr_vif.loopback()) { if (prev) fprintf(stdout, ","); fprintf(stdout, "LOOPBACK"); prev = true; } if (ifmgr_vif.p2p_capable()) { if (prev) fprintf(stdout, ","); fprintf(stdout, "POINTTOPOINT"); prev = true; } if (ifmgr_vif.pim_register()) { if (prev) fprintf(stdout, ","); fprintf(stdout, "PIM-REGISTER"); prev = true; } fprintf(stdout, "> mtu %d", ifmgr_iface.mtu()); // // Print the baudrate in bps/Kbps/Mbps/Gbps/Tbps // fprintf(stdout, " speed "); if (ifmgr_iface.baudrate() == 0) { fprintf(stdout, "unknown"); } else { do { uint64_t b = ifmgr_iface.baudrate(); // Test if bps if (b / 1000 == 0) { fprintf(stdout, "%u bps", XORP_UINT_CAST(b)); break; } // Test if Kbps b /= 1000; if (b / 1000 == 0) { fprintf(stdout, "%u Kbps", XORP_UINT_CAST(b)); break; } // Test if Mbps b /= 1000; if (b / 1000 == 0) { fprintf(stdout, "%u Mbps", XORP_UINT_CAST(b)); break; } // Test if Gbps b /= 1000; if (b / 1000 == 0) { fprintf(stdout, "%u Gbps", XORP_UINT_CAST(b)); break; } // Test if Tbps b /= 1000; if (b / 1000 == 0) { fprintf(stdout, "%u Tbps", XORP_UINT_CAST(b)); break; } // Default fprintf(stdout, "%u Tbps", XORP_UINT_CAST(b)); break; } while (false); } fprintf(stdout, "\n"); // // Print the VLAN-related information // if (ifmgr_iface.iface_type().size() || ifmgr_iface.vid().size() || ifmgr_iface.parent_ifname().size()) { fprintf(stdout, " type: %s parent interface: %s vid: %s\n", ifmgr_iface.iface_type().c_str(), ifmgr_iface.parent_ifname().c_str(), ifmgr_iface.vid().c_str()); } // // Print the IPv6 addresses // for (a6_iter = ifmgr_vif.ipv6addrs().begin(); a6_iter != ifmgr_vif.ipv6addrs().end(); ++a6_iter) { const IfMgrIPv6Atom& a6 = a6_iter->second; const IPv6& addr = a6.addr(); fprintf(stdout, " inet6 %s prefixlen %d", addr.str().c_str(), a6.prefix_len()); if (a6.has_endpoint()) { fprintf(stdout, " --> %s", a6.endpoint_addr().str().c_str()); } if (! a6.enabled()) fprintf(stdout, " "); fprintf(stdout, "\n"); } // // Print the IPv4 addresses // for (a4_iter = ifmgr_vif.ipv4addrs().begin(); a4_iter != ifmgr_vif.ipv4addrs().end(); ++a4_iter) { const IfMgrIPv4Atom& a4 = a4_iter->second; const IPv4& addr = a4.addr(); fprintf(stdout, " inet %s subnet %s", addr.str().c_str(), IPv4Net(addr, a4.prefix_len()).str().c_str()); if (a4.has_broadcast()) { fprintf(stdout, " broadcast %s", a4.broadcast_addr().str().c_str()); } if (a4.has_endpoint()) { fprintf(stdout, " --> %s", a4.endpoint_addr().str().c_str()); } if (! a4.enabled()) fprintf(stdout, " "); fprintf(stdout, "\n"); } // // Print the physical interface index and MAC address // fprintf(stdout, " physical index %d\n", ifmgr_vif.pif_index()); if (!ifmgr_iface.mac().is_zero()) { fprintf(stdout, " ether %s\n", ifmgr_iface.mac().str().c_str()); } } } debug_msg("End iftree loop\n"); if (print_iface_name.size() > 0) { if (! iface_found) { fprintf(stderr, "Interface \"%s\" not found\n", print_iface_name.c_str()); } } } /** * usage: * @argv0: Argument 0 when the program was called (the program name itself). * @exit_value: The exit value of the program. * * Print the program usage. * If @exit_value is 0, the usage will be printed to the standart output, * otherwise to the standart error. **/ static void usage(const char *argv0, int exit_value) { FILE *output; const char *progname = strrchr(argv0, '/'); if (progname != NULL) progname++; // Skip the last '/' if (progname == NULL) progname = argv0; // // If the usage is printed because of error, output to stderr, otherwise // output to stdout. // if (exit_value == 0) output = stdout; else output = stderr; fprintf(output, "Usage: %s [-F [:]] [-i ]\n", progname); fprintf(output, " -F [:] : finder hostname and port\n"); fprintf(output, " -i : the interface name\n"); fprintf(output, " -h : usage (this message)\n"); fprintf(output, "\n"); fprintf(output, "Program name: %s\n", progname); fprintf(output, "Module name: %s\n", XORP_MODULE_NAME); fprintf(output, "Module version: %s\n", XORP_MODULE_VERSION); exit (exit_value); // NOTREACHED } static void interface_monitor_main(const string& finder_hostname, uint16_t finder_port, const string& print_iface_name) { string process_name; process_name = c_format("interface_monitor<%d>", XORP_INT_CAST(getpid())); // // Init stuff // EventLoop eventloop; InterfaceMonitor ifmon(eventloop, process_name, finder_hostname, finder_port, "fea"); // // Startup // ifmon.startup(); // // Main loop // while (ifmon.status() == SERVICE_STARTING) { eventloop.run(); } if (ifmon.status() == SERVICE_RUNNING) { ifmon.print_interfaces(print_iface_name); } // // Shutdown // ifmon.shutdown(); while ((ifmon.status() != SERVICE_SHUTDOWN) && (ifmon.status() != SERVICE_FAILED)) { eventloop.run(); } } int main(int argc, char* const argv[]) { int ch; string::size_type idx; const char *argv0 = argv[0]; string finder_hostname = FinderConstants::FINDER_DEFAULT_HOST().str(); uint16_t finder_port = FinderConstants::FINDER_DEFAULT_PORT(); string print_iface_name = ""; // XXX; empty string means print all string command; // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); // // Get the program options // while ((ch = getopt(argc, argv, "F:i:h")) != -1) { switch (ch) { case 'F': // Finder hostname and port finder_hostname = optarg; idx = finder_hostname.find(':'); if (idx != string::npos) { if (idx + 1 >= finder_hostname.length()) { // No port number usage(argv0, 1); // NOTREACHED } char* p = &finder_hostname[idx + 1]; finder_port = static_cast(atoi(p)); finder_hostname = finder_hostname.substr(0, idx); } break; case 'i': // Network interface name print_iface_name = optarg; break; case 'h': case '?': // Help usage(argv0, 0); // NOTREACHED break; default: usage(argv0, 1); // NOTREACHED break; } } argc -= optind; argv += optind; // Get the command itself while (argc != 0) { if (! command.empty()) command += " "; command += string(argv[0]); argc--; argv++; } // // Run everything // try { interface_monitor_main(finder_hostname, finder_port, print_iface_name); } catch(...) { xorp_catch_standard_exceptions(); } // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); exit (0); } xorp/fea/tools/show_interfaces.hh0000664000076400007640000000775611421137511017272 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/tools/show_interfaces.hh,v 1.11 2008/10/02 21:57:13 bms Exp $ #ifndef __FEA_TOOLS_SHOW_INTERFACES_HH__ #define __FEA_TOOLS_SHOW_INTERFACES_HH__ #include "libxorp/service.hh" #include "libfeaclient/ifmgr_xrl_mirror.hh" class EventLoop; class InterfaceMonitor : public IfMgrHintObserver, public ServiceBase, public ServiceChangeObserverBase { public: /** * InterfaceMonitor constructor * * @param eventloop this process's EventLoop. * @param class_name the XRL class name of this target. * @param finder_hostname the finder's host name. * @param finder_port the finder's port. * @param fea_target the FEA target name. */ InterfaceMonitor(EventLoop& eventloop, const string& class_name, const string& finder_hostname, uint16_t finder_port, const string& fea_target); /** * InterfaceMonitor destructor */ ~InterfaceMonitor(); /** * Startup operation. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int startup(); /** * Shutdown operation. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int shutdown(); /** * Print information about network interfaces that was received * from the FEA. * * @param print_iface_name the name of the interface to print. * If it is the empty string, then print information about all * configured interfaces. */ void print_interfaces(const string& print_iface_name) const; protected: // // IfMgrHintObserver methods // void tree_complete(); void updates_made(); void incr_startup_requests_n(); void decr_startup_requests_n(); void incr_shutdown_requests_n(); void decr_shutdown_requests_n(); void update_status(); private: /** * A method invoked when the status of a service changes. * * @param service the service whose status has changed. * @param old_status the old status. * @param new_status the new status. */ void status_change(ServiceBase* service, ServiceStatus old_status, ServiceStatus new_status); /** * Get a reference to the service base of the interface manager. * * @return a reference to the service base of the interface manager. */ const ServiceBase* ifmgr_mirror_service_base() const { return dynamic_cast(&_ifmgr); } /** * Get a reference to the interface manager tree. * * @return a reference to the interface manager tree. */ const IfMgrIfTree& ifmgr_iftree() const { return _ifmgr.iftree(); } /** * Initiate startup of the interface manager. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int ifmgr_startup(); /** * Initiate shutdown of the interface manager. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int ifmgr_shutdown(); EventLoop& _eventloop; IfMgrXrlMirror _ifmgr; // // Status-related state // size_t _startup_requests_n; size_t _shutdown_requests_n; }; #endif // __FEA_TOOLS_SHOW_INTERFACES_HH__ xorp/fea/tools/SConscript0000664000076400007640000000403511631505245015571 0ustar greearbgreearb# Copyright (c) 2009-2011 XORP, Inc and Others # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # $XORP$ import os Import('env') env = env.Clone() is_shared = env.has_key('SHAREDLIBS') env.AppendUnique(CPPPATH = [ '#', '$BUILDDIR', ]) env.AppendUnique(LIBPATH = [ '$BUILDDIR/libxorp', '$BUILDDIR/libcomm', '$BUILDDIR/libxipc', '$BUILDDIR/libproto', '$BUILDDIR/libfeaclient', '$BUILDDIR/xrl/interfaces', '$BUILDDIR/xrl/targets', '.', ]) env.AppendUnique(LIBS = [ 'xorp_fea_client', 'xif_fea_ifmgr_mirror', 'xif_fea_ifmgr_replicator', 'xst_fea_ifmgr_mirror', 'xorp_ipc', 'xorp_core', 'xorp_proto', 'xorp_comm', ]) if not is_shared: env.AppendUnique(LIBS = [ "crypto", ]) if not (env.has_key('mingw') and env['mingw']): env.AppendUnique(LIBS = [ "rt", ]) if (env.has_key('mingw') and env['mingw']): env.AppendUnique(LIBS = [ 'ws2_32', 'iphlpapi', 'winmm', ]) env.Append(LIBS = [ 'xorp_core', ]) env.Replace(RPATH = [ env.Literal(env['xorp_tool_rpath']) ]) showifsrcs = [ 'show_interfaces.cc' ] showif = env.Program(target = 'fea_show_interfaces', source = showifsrcs) env.Alias('install', env.InstallProgram(env['xorp_tooldir'], showif)) Default(showif) xorp/fea/fea_data_plane_manager.hh0000664000076400007640000002750111540225524017336 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2007-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/fea_data_plane_manager.hh,v 1.13 2008/10/02 21:56:45 bms Exp $ #ifndef __FEA_FEA_DATA_PLANE_MANAGER_HH__ #define __FEA_FEA_DATA_PLANE_MANAGER_HH__ class EventLoop; class FeaNode; class FibConfig; class FibConfigEntryGet; class FibConfigEntryObserver; class FibConfigEntrySet; class FibConfigForwarding; class FibConfigTableGet; class FibConfigTableObserver; class FibConfigTableSet; #ifndef XORP_DISABLE_FIREWALL class FirewallGet; class FirewallManager; class FirewallSet; #endif class IfConfig; class IfConfigGet; class IfConfigObserver; class IfConfigProperty; class IfConfigSet; class IfConfigVlanGet; class IfConfigVlanSet; class IfTree; class IoLink; class IoLinkManager; class IoIp; class IoIpManager; class IoTcpUdp; class IoTcpUdpManager; /** * FEA data plane manager base class. */ class FeaDataPlaneManager { public: /** * Constructor. * * @param fea_node the @ref FeaNode this manager belongs to. * @param manager_name the data plane manager name. */ FeaDataPlaneManager(FeaNode& fea_node, const string& manager_name); /** * Virtual destructor. */ virtual ~FeaDataPlaneManager(); /** * Get the data plane manager name. * * @return the data plane name. */ const string& manager_name() const { return _manager_name; } /** * Start data plane manager operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start_manager(string& error_msg); /** * Stop data plane manager operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop_manager(string& error_msg); /** * Load the plugins. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int load_plugins(string& error_msg) = 0; /** * Unload the plugins. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int unload_plugins(string& error_msg); /** * Register the plugins. * * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int register_plugins(string& error_msg) = 0; /** * Unregister the plugins. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int unregister_plugins(string& error_msg); /** * Start plugins operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start_plugins(string& error_msg); /** * Stop plugins operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop_plugins(string& error_msg); /** * Get the event loop this instance is added to. * * @return the event loop this instance is added to. */ EventLoop& eventloop(); /** * Return true if the underlying system supports IPv4. * * @return true if the underlying system supports IPv4, otherwise false. */ virtual bool have_ipv4() const; /** * Return true if the underlying system supports IPv6. * * @return true if the underlying system supports IPv6, otherwise false. */ virtual bool have_ipv6() const; /** * Get the @ref IfConfig instance. * * @return the @ref IfConfig instance. */ IfConfig& ifconfig(); #ifndef XORP_DISABLE_FIREWALL /** * Get the @ref FirewallManager instance. * * @return the @ref FirewallManager instance. */ FirewallManager& firewall_manager(); /** * Get the FirewallGet plugin. * * @return the @ref FirewallGet plugin. */ FirewallGet* firewall_get() { return _firewall_get; } /** * Get the FirewallSet plugin. * * @return the @ref FirewallSet plugin. */ FirewallSet* firewall_set() { return _firewall_set; } #endif /** * Get the @ref FibConfig instance. * * @return the @ref FibConfig instance. */ FibConfig& fibconfig(); /** * Get the @ref IoLinkManager instance. * * @return the @ref IoLinkManager instance. */ IoLinkManager& io_link_manager(); /** * Get the @ref IoIpManager instance. * * @return the @ref IoIpManager instance. */ IoIpManager& io_ip_manager(); /** * Get the @ref IoTcpUdpManager instance. * * @return the @ref IoTcpUdpManager instance. */ IoTcpUdpManager& io_tcpudp_manager(); /** * Get the IfConfigProperty plugin. * * @return the @ref IfConfigGet plugin. */ IfConfigProperty* ifconfig_property() { return _ifconfig_property; } /** * Get the IfConfigGet plugin. * * @return the @ref IfConfigGet plugin. */ IfConfigGet* ifconfig_get() { return _ifconfig_get; } /** * Get the IfConfigSet plugin. * * @return the @ref IfConfigSet plugin. */ IfConfigSet* ifconfig_set() { return _ifconfig_set; } /** * Get the IfConfigObserver plugin. * * @return the @ref IfConfigObserver plugin. */ IfConfigObserver* ifconfig_observer() { return _ifconfig_observer; } /** * Get the IfConfigVlanGet plugin. * * @return the @ref IfConfigVlanGet plugin. */ IfConfigVlanGet* ifconfig_vlan_get() { return _ifconfig_vlan_get; } /** * Get the IfConfigVlanSet plugin. * * @return the @ref IfConfigVlanSet plugin. */ IfConfigVlanSet* ifconfig_vlan_set() { return _ifconfig_vlan_set; } /** * Get the FibConfigForwarding plugin. * * @return the @ref FibConfigForwarding plugin. */ FibConfigForwarding* fibconfig_forwarding() { return _fibconfig_forwarding; } /** * Get the FibConfigEntryGet plugin. * * @return the @ref FibConfigEntryGet plugin. */ FibConfigEntryGet* fibconfig_entry_get() { return _fibconfig_entry_get; } /** * Get the FibConfigEntrySet plugin. * * @return the @ref FibConfigEntrySet plugin. */ FibConfigEntrySet* fibconfig_entry_set() { return _fibconfig_entry_set; } /** * Get the FibConfigEntryObserver plugin. * * @return the @ref FibConfigEntryObserver plugin. */ FibConfigEntryObserver* fibconfig_entry_observer() { return _fibconfig_entry_observer; } /** * Get the FibConfigTableGet plugin. * * @return the @ref FibConfigEntryGet plugin. */ FibConfigTableGet* fibconfig_table_get() { return _fibconfig_table_get; } /** * Get the FibConfigTableSet plugin. * * @return the @ref FibConfigEntryGet plugin. */ FibConfigTableSet* fibconfig_table_set() { return _fibconfig_table_set; } /** * Get the FibConfigTableObserver plugin. * * @return the @ref FibConfigEntryObserver plugin. */ FibConfigTableObserver* fibconfig_table_observer() { return _fibconfig_table_observer; } /** * Allocate IoLink plugin instance. * * @param iftree the interface tree to use. * @param if_name the interface name. * @param vif_name the vif name. * @param ether_type the EtherType protocol number. If it is 0 then * it is unused. * @param filter_program the option filter program to be applied on the * received packets. The program uses tcpdump(1) style expression. * @return a new instance of @ref IoLink plugin on success, otherwise NULL. */ virtual IoLink* allocate_io_link(const IfTree& iftree, const string& if_name, const string& vif_name, uint16_t ether_type, const string& filter_program) = 0; /** * De-allocate IoLink plugin. * * @param io_link the IoLink plugin to deallocate. */ virtual void deallocate_io_link(IoLink* io_link); /** * Allocate IoIp plugin instance. * * @param iftree the interface tree to use. * @param family the address family (AF_INET or AF_INET6 for IPv4 and IPv6 * respectively). * @param ip_protocol the IP protocol number (IPPROTO_*). * @return a new instance of @ref IoIp plugin on success, otherwise NULL. */ virtual IoIp* allocate_io_ip(const IfTree& iftree, int family, uint8_t ip_protocol) = 0; /** * De-allocate IoIp plugin. * * @param io_ip the IoIp plugin to deallocate. */ virtual void deallocate_io_ip(IoIp* io_ip); /** * Allocate IoTcpUdp plugin instance. * * @param iftree the interface tree to use. * @param family the address family (AF_INET or AF_INET6 for IPv4 and IPv6 * respectively). * @param is_tcp if true allocate a TCP entry, otherwise UDP. * @return a new instance of @ref IoTcpUdp plugin on success, * otherwise NULL. */ virtual IoTcpUdp* allocate_io_tcpudp(const IfTree& iftree, int family, bool is_tcp) = 0; /** * De-allocate IoTcpUdp plugin. * * @param io_tcpudp the IoTcpUdp plugin to deallocate. */ virtual void deallocate_io_tcpudp(IoTcpUdp* io_tcpudp); protected: /** * Register all plugins. * * @param is_exclusive if true, the plugins are registered as the * exclusive plugins, otherwise are added to the lists of plugins. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int register_all_plugins(bool is_exclusive, string& error_msg); /** * Stop all plugins operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int stop_all_plugins(string& error_msg); FeaNode& _fea_node; // // The plugins // IfConfigProperty* _ifconfig_property; IfConfigGet* _ifconfig_get; IfConfigSet* _ifconfig_set; IfConfigObserver* _ifconfig_observer; IfConfigVlanGet* _ifconfig_vlan_get; IfConfigVlanSet* _ifconfig_vlan_set; #ifndef XORP_DISABLE_FIREWALL FirewallGet* _firewall_get; FirewallSet* _firewall_set; #endif FibConfigForwarding* _fibconfig_forwarding; FibConfigEntryGet* _fibconfig_entry_get; FibConfigEntrySet* _fibconfig_entry_set; FibConfigEntryObserver* _fibconfig_entry_observer; FibConfigTableGet* _fibconfig_table_get; FibConfigTableSet* _fibconfig_table_set; FibConfigTableObserver* _fibconfig_table_observer; list _io_link_list; list _io_ip_list; list _io_tcpudp_list; // // Misc other state // const string _manager_name; // The data plane manager name bool _is_loaded_plugins; // True if plugins are loaded bool _is_running_manager; // True if manager is running bool _is_running_plugins; // True if plugins are running }; #endif // __FEA_FEA_DATA_PLANE_MANAGER_HH__ xorp/fea/iftree.cc0000664000076400007640000017234211703345405014213 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2012 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/c_format.hh" #include "libxorp/vif.hh" #include "iftree.hh" /* ------------------------------------------------------------------------- */ /* IfTreeItem code */ string IfTreeItem::str() const { struct { State st; const char* desc; } t[] = { { CREATED, "CREATED" }, { DELETED, "DELETED" }, { CHANGED, "CHANGED" } }; string r; for (size_t i = 0; i < sizeof(t) / sizeof(t[0]); i++) { if ((_st & t[i].st) == 0) continue; if (r.empty() == false) r += ","; r += t[i].desc; } return (r); } /* ------------------------------------------------------------------------- */ /* IfTree code */ IfTree::IfTree(const char* tree_name) : IfTreeItem() { name = tree_name; } IfTree::IfTree(const IfTree& other) : IfTreeItem() { *this = other; name = "copy of "; name += other.name; } IfTree::~IfTree() { clear(); } IfTree& IfTree::operator=(const IfTree& other) { clear(); // Add recursively all interfaces from the other tree IfMap::const_iterator oi; for (oi = other.interfaces().begin(); oi != other.interfaces().end(); ++oi) { const IfTreeInterface& other_iface = *(oi->second); add_recursive_interface(other_iface, true); } set_state(other.state()); return (*this); } void IfTree::clear() { //XLOG_WARNING("Clearing iftree: %s\n", name.c_str()); while (! _interfaces.empty()) { IfTreeInterface* ifp = _interfaces.begin()->second; sendEvent(IFTREE_ERASE_IFACE, ifp); _interfaces.erase(_interfaces.begin()); delete ifp; } XLOG_ASSERT(_ifindex_map.empty()); XLOG_ASSERT(_vifindex_map.empty()); } void IfTree::add_recursive_interface(const IfTreeInterface& other_iface, bool mark_state) { const string& ifname = other_iface.ifname(); IfTreeInterface* ifp; // Add the interface ifp = new IfTreeInterface(*this, ifname); _interfaces.insert(IfMap::value_type(ifname, ifp)); ifp->copy_state(other_iface, true); if (mark_state) ifp->set_state(other_iface.state()); else ifp->mark(CREATED); //XLOG_WARNING("Adding interface: %s recursively to tree: %s\n", ifname.c_str(), name.c_str()); // Add recursively all vifs from the other interface IfTreeInterface::VifMap::const_iterator ov; for (ov = other_iface.vifs().begin(); ov != other_iface.vifs().end(); ++ov) { const IfTreeVif& other_vif = *(ov->second); ifp->add_recursive_vif(other_vif, mark_state); } } int IfTree::add_interface(const string& ifname) { IfTreeInterface* ifp = find_interface(ifname); if (ifp != NULL) { ifp->mark(CREATED); return (XORP_OK); } ifp = new IfTreeInterface(*this, ifname); _interfaces.insert(IfMap::value_type(ifname, ifp)); #ifdef HOST_OS_WINDOWS // Only spam windows for now. XLOG_WARNING("Adding interface: %s to tree: %s\n", ifname.c_str(), name.c_str()); #endif return (XORP_OK); } void IfTree::registerListener(IfTreeListener* l) const { listeners.push_back(l); } void IfTree::unregisterListener(IfTreeListener* l) const { listeners.remove(l); } IfTreeInterface* IfTree::find_interface(const string& ifname) { IfTree::IfMap::iterator iter = _interfaces.find(ifname); if (iter == interfaces().end()) return (NULL); return (iter->second); } const IfTreeInterface* IfTree::find_interface(const string& ifname) const { IfTree::IfMap::const_iterator iter = _interfaces.find(ifname); if (iter == interfaces().end()) return (NULL); return (iter->second); } IfTreeInterface* IfTree::find_interface(uint32_t pif_index) { IfTree::IfIndexMap::iterator iter = _ifindex_map.find(pif_index); if (iter == _ifindex_map.end()) return (NULL); return (iter->second); } const IfTreeInterface* IfTree::find_interface(uint32_t pif_index) const { IfTree::IfIndexMap::const_iterator iter = _ifindex_map.find(pif_index); if (iter == _ifindex_map.end()) return (NULL); return (iter->second); } IfTreeVif* IfTree::find_vif(const string& ifname, const string& vifname) { IfTreeInterface* ifp = find_interface(ifname); if (ifp == NULL) return (NULL); return (ifp->find_vif(vifname)); } const IfTreeVif* IfTree::find_vif(const string& ifname, const string& vifname) const { const IfTreeInterface* ifp = find_interface(ifname); if (ifp == NULL) return (NULL); return (ifp->find_vif(vifname)); } IfTreeVif* IfTree::find_vif(uint32_t pif_index) { IfTree::VifIndexMap::const_iterator iter = _vifindex_map.find(pif_index); if (iter == _vifindex_map.end()) return (NULL); return (iter->second); } const IfTreeVif* IfTree::find_vif(uint32_t pif_index) const { IfTree::VifIndexMap::const_iterator iter = _vifindex_map.find(pif_index); if (iter == _vifindex_map.end()) return (NULL); return (iter->second); } IfTreeAddr4* IfTree::find_addr(const string& ifname, const string& vifname, const IPv4& addr) { IfTreeVif* vifp = find_vif(ifname, vifname); if (vifp == NULL) return (NULL); return (vifp->find_addr(addr)); } const IfTreeAddr4* IfTree::find_addr(const string& ifname, const string& vifname, const IPv4& addr) const { const IfTreeVif* vifp = find_vif(ifname, vifname); if (vifp == NULL) return (NULL); return (vifp->find_addr(addr)); } IfTreeAddr6* IfTree::find_addr(const string& ifname, const string& vifname, const IPv6& addr) { IfTreeVif* vifp = find_vif(ifname, vifname); if (vifp == NULL) return (NULL); return (vifp->find_addr(addr)); } const IfTreeAddr6* IfTree::find_addr(const string& ifname, const string& vifname, const IPv6& addr) const { const IfTreeVif* vifp = find_vif(ifname, vifname); if (vifp == NULL) return (NULL); return (vifp->find_addr(addr)); } bool IfTree::find_interface_vif_by_addr(const IPvX& addr, const IfTreeInterface*& ifp, const IfTreeVif*& vifp) const { IfTree::IfMap::const_iterator ii; IfTreeInterface::VifMap::const_iterator vi; IfTreeVif::IPv4Map::const_iterator ai4; IfTreeVif::IPv6Map::const_iterator ai6; ifp = NULL; vifp = NULL; // // Iterate through all interfaces and vifs // for (ii = interfaces().begin(); ii != interfaces().end(); ++ii) { const IfTreeInterface& fi = *(ii->second); for (vi = fi.vifs().begin(); vi != fi.vifs().end(); ++vi) { const IfTreeVif& fv = *(vi->second); // // IPv4 address test // if (addr.is_ipv4()) { IPv4 addr4 = addr.get_ipv4(); for (ai4 = fv.ipv4addrs().begin(); ai4 != fv.ipv4addrs().end(); ++ai4) { const IfTreeAddr4& a4 = *(ai4->second); if (a4.addr() == addr4) { // Found a match ifp = &fi; vifp = &fv; return (true); } } continue; } // // IPv6 address test // if (addr.is_ipv6()) { IPv6 addr6 = addr.get_ipv6(); for (ai6 = fv.ipv6addrs().begin(); ai6 != fv.ipv6addrs().end(); ++ai6) { const IfTreeAddr6& a6 = *(ai6->second); if (a6.addr() == addr6) { // Found a match ifp = &fi; vifp = &fv; return (true); } } continue; } } } return (false); } bool IfTree::find_interface_vif_same_subnet_or_p2p(const IPvX& addr, const IfTreeInterface*& ifp, const IfTreeVif*& vifp) const { IfTree::IfMap::const_iterator ii; IfTreeInterface::VifMap::const_iterator vi; IfTreeVif::IPv4Map::const_iterator ai4; IfTreeVif::IPv6Map::const_iterator ai6; ifp = NULL; vifp = NULL; // // Iterate through all interfaces and vifs // for (ii = interfaces().begin(); ii != interfaces().end(); ++ii) { const IfTreeInterface& fi = *(ii->second); for (vi = fi.vifs().begin(); vi != fi.vifs().end(); ++vi) { const IfTreeVif& fv = *(vi->second); // // IPv4 address test // if (addr.is_ipv4()) { IPv4 addr4 = addr.get_ipv4(); for (ai4 = fv.ipv4addrs().begin(); ai4 != fv.ipv4addrs().end(); ++ai4) { const IfTreeAddr4& a4 = *(ai4->second); // Test if same subnet IPv4Net subnet(a4.addr(), a4.prefix_len()); if (subnet.contains(addr4)) { // Found a match ifp = &fi; vifp = &fv; return (true); } // Test if same p2p if (! a4.point_to_point()) continue; if ((a4.addr() == addr4) || (a4.endpoint() == addr4)) { // Found a match ifp = &fi; vifp = &fv; return (true); } } continue; } // // IPv6 address test // if (addr.is_ipv6()) { IPv6 addr6 = addr.get_ipv6(); for (ai6 = fv.ipv6addrs().begin(); ai6 != fv.ipv6addrs().end(); ++ai6) { const IfTreeAddr6& a6 = *(ai6->second); // Test if same subnet IPv6Net subnet(a6.addr(), a6.prefix_len()); if (subnet.contains(addr6)) { // Found a match ifp = &fi; vifp = &fv; return (true); } // Test if same p2p if (! a6.point_to_point()) continue; if ((a6.addr() == addr6) || (a6.endpoint() == addr6)) { // Found a match ifp = &fi; vifp = &fv; return (true); } } continue; } } } return (false); } void IfTree::sendEvent(IfTreeIfaceEventE e, IfTreeInterface* ifp) { list::iterator i; for (i = listeners.begin(); i != listeners.end(); i++) { IfTreeListener* l = *i; switch (e) { case IFTREE_DELETE_IFACE: l->notifyDeletingIface(ifp->ifname()); break; case IFTREE_ERASE_IFACE: l->notifyErasingIface(ifp->ifname()); break; default: // Should never be here XLOG_ASSERT(0); }//switch } } void IfTree::sendEvent(IfTreeVifEventE e, IfTreeVif* vifp) { list::iterator i; for (i = listeners.begin(); i != listeners.end(); i++) { IfTreeListener* l = *i; switch (e) { case IFTREE_DELETE_VIF: l->notifyDeletingVif(vifp->ifname(), vifp->vifname()); break; case IFTREE_ERASE_VIF: l->notifyErasingVif(vifp->ifname(), vifp->vifname()); break; default: // Should never be here XLOG_ASSERT(0); }//switch } } void IfTree::markIfaceDeleted(IfTreeInterface* ifp) { sendEvent(IFTREE_DELETE_IFACE, ifp); ifp->mark(DELETED); } void IfTree::markVifDeleted(IfTreeVif* vifp) { sendEvent(IFTREE_DELETE_VIF, vifp); vifp->mark(DELETED); } int IfTree::remove_interface(const string& ifname) { IfTreeInterface* ifp = find_interface(ifname); XLOG_WARNING("Marking interface: %s in tree: %s DELETED\n", ifname.c_str(), name.c_str()); if (ifp == NULL) return (XORP_ERROR); markIfaceDeleted(ifp); return (XORP_OK); } /** * Recursively create a new interface or update its state if it already exists. * * @param other_iface the interface with the state to copy from. * @return XORP_OK on success, otherwise XORP_ERROR. */ int IfTree::update_interface(const IfTreeInterface& other_iface) { IfTreeInterface* this_ifp; IfTreeInterface::VifMap::iterator vi; IfTreeInterface::VifMap::const_iterator ov; // // Add the interface if we don't have it already // this_ifp = find_interface(other_iface.ifname()); if (this_ifp == NULL) { add_interface(other_iface.ifname()); this_ifp = find_interface(other_iface.ifname()); XLOG_ASSERT(this_ifp != NULL); } // // Update the interface state // if (! this_ifp->is_same_state(other_iface)) this_ifp->copy_state(other_iface, false); // // Update existing vifs and addresses, and delete those that don't exist // for (vi = this_ifp->vifs().begin(); vi != this_ifp->vifs().end(); ++vi) { IfTreeVif* this_vifp = vi->second; const IfTreeVif* other_vifp; other_vifp = other_iface.find_vif(this_vifp->vifname()); if ((other_vifp == NULL) || (other_vifp->is_marked(DELETED))) { markVifDeleted(this_vifp); continue; } if (! this_vifp->is_same_state(*other_vifp)) this_vifp->copy_state(*other_vifp); // // Update the IPv4 addresses // IfTreeVif::IPv4Map::iterator ai4; for (ai4 = this_vifp->ipv4addrs().begin(); ai4 != this_vifp->ipv4addrs().end(); ++ai4) { IfTreeAddr4* this_ap = ai4->second; const IfTreeAddr4* other_ap; other_ap = other_vifp->find_addr(this_ap->addr()); if ((other_ap == NULL) || (other_ap->is_marked(DELETED))) { this_ap->mark(DELETED); continue; } if (! this_ap->is_same_state(*other_ap)) this_ap->copy_state(*other_ap); } // // Update the IPv6 addresses // IfTreeVif::IPv6Map::iterator ai6; for (ai6 = this_vifp->ipv6addrs().begin(); ai6 != this_vifp->ipv6addrs().end(); ++ai6) { IfTreeAddr6* this_ap = ai6->second; const IfTreeAddr6* other_ap; other_ap = other_vifp->find_addr(this_ap->addr()); if ((other_ap == NULL) || (other_ap->is_marked(DELETED))) { this_ap->mark(DELETED); continue; } if (! this_ap->is_same_state(*other_ap)) this_ap->copy_state(*other_ap); } } // // Add the new vifs and addresses // for (ov = other_iface.vifs().begin(); ov != other_iface.vifs().end(); ++ov) { const IfTreeVif* other_vifp = ov->second; IfTreeVif* this_vifp; // // Add the vif // this_vifp = this_ifp->find_vif(other_vifp->vifname()); if (this_vifp == NULL) { this_ifp->add_vif(other_vifp->vifname()); this_vifp = this_ifp->find_vif(other_vifp->vifname()); XLOG_ASSERT(this_vifp != NULL); this_vifp->copy_state(*other_vifp); } // // Add the IPv4 addresses // IfTreeVif::IPv4Map::const_iterator oa4; for (oa4 = other_vifp->ipv4addrs().begin(); oa4 != other_vifp->ipv4addrs().end(); ++oa4) { // Add the address const IfTreeAddr4* other_ap = oa4->second; IfTreeAddr4* this_ap; this_ap = this_vifp->find_addr(other_ap->addr()); if (this_ap == NULL) { this_vifp->add_addr(other_ap->addr()); this_ap = this_vifp->find_addr(other_ap->addr()); XLOG_ASSERT(this_ap != NULL); this_ap->copy_state(*other_ap); } } // // Add the IPv6 addresses // IfTreeVif::IPv6Map::const_iterator oa6; for (oa6 = other_vifp->ipv6addrs().begin(); oa6 != other_vifp->ipv6addrs().end(); ++oa6) { const IfTreeAddr6* other_ap = oa6->second; IfTreeAddr6* this_ap; this_ap = this_vifp->find_addr(other_ap->addr()); if (this_ap == NULL) { // Add the address this_vifp->add_addr(other_ap->addr()); this_ap = this_vifp->find_addr(other_ap->addr()); XLOG_ASSERT(this_ap != NULL); this_ap->copy_state(*other_ap); } } } return (XORP_OK); } /** * Delete interfaces labelled as ready for deletion, call finalize_state() * on remaining interfaces, and set state to NO_CHANGE. */ void IfTree::finalize_state() { IfMap::iterator ii = _interfaces.begin(); while (ii != _interfaces.end()) { IfTreeInterface* ifp = ii->second; // If interface is marked as deleted, delete it. if (ifp->is_marked(DELETED)) { sendEvent(IFTREE_ERASE_IFACE, ifp); _interfaces.erase(ii++); XLOG_WARNING("Deleting interface: %s from tree: %s\n", ifp->ifname().c_str(), name.c_str()); delete ifp; continue; } // Call finalize_state on interfaces that remain ifp->finalize_state(); ++ii; } set_state(NO_CHANGE); } string IfTree::str() const { string r(name); r += "\n"; IfMap::const_iterator ii; // // Print the interfaces // for (ii = interfaces().begin(); ii != interfaces().end(); ++ii) { const IfTreeInterface& fi = *(ii->second); r += fi.str() + string("\n"); // // Print the vifs // for (IfTreeInterface::VifMap::const_iterator vi = fi.vifs().begin(); vi != fi.vifs().end(); ++vi) { const IfTreeVif& fv = *(vi->second); r += string(" ") + fv.str() + string("\n"); // // Print the IPv4 addresses // for (IfTreeVif::IPv4Map::const_iterator ai = fv.ipv4addrs().begin(); ai != fv.ipv4addrs().end(); ++ai) { const IfTreeAddr4& a = *(ai->second); r += string(" ") + a.str() + string("\n"); } // // Print the IPv6 addresses // for (IfTreeVif::IPv6Map::const_iterator ai = fv.ipv6addrs().begin(); ai != fv.ipv6addrs().end(); ++ai) { const IfTreeAddr6& a = *(ai->second); r += string(" ") + a.str() + string("\n"); } } } return (r); } /** * Align system-user merged configuration with the pulled changes * in the system configuration. * * Inside the FEA there may be multiple configuration representations, * typically one the user modifies, one that mirrors the hardware, * and one that merges those two (e.g., some of the merged information * comes from the user configuration while other might come * from the underlying system). * Errors may occur pushing the user config down onto the hardware and * we need a method to update the merged config from the h/w config that * exists after the config push. We can't just copy the h/w config since * the user config is restricted to configuration set by the user. * * The alignment works as follows: * 1. If an interface in the local tree is marked as "soft", its * state is not modified and the rest of the processing is ignored. * 2. If an interface in the local tree is marked as * "default_system_config", the rest of the processing is not * applied, and the following rules are used instead: * (a) If the interface is not in the other tree, it is marked * as "disabled" and its vifs are marked for deletion. * (b) Otherwise, its state (and the subtree below it) is copied * as-is from the other tree. * 3. If an item in the local tree is not in the other tree, it is * marked as "disabled" in the local tree. * 4. If an item in the local tree is in the other tree, and its state * is different in the local and the other tree, the state * is copied from the other tree to the local tree. * Also, if the item is disabled in the user config tree, it is * marked as "disabled" in the local tree. * * @param other the configuration tree to align state with. * @param user_config the user configuration tree to reference during * the alignment. * @return modified configuration structure. */ IfTree& IfTree::align_with_pulled_changes(const IfTree& other, const IfTree& user_config) { IfTree::IfMap::iterator ii; // // Iterate through all interfaces // for (ii = interfaces().begin(); ii != interfaces().end(); ++ii) { IfTreeInterface* this_ifp = ii->second; const string& ifname = this_ifp->ifname(); const IfTreeInterface* other_ifp = other.find_interface(ifname); const IfTreeInterface* user_ifp = user_config.find_interface(ifname); // // Ignore "soft" interfaces // if (this_ifp->is_soft()) continue; // // Special processing for "default_system_config" interfaces // if (this_ifp->default_system_config()) { if (other_ifp != NULL) { update_interface(*other_ifp); } else { this_ifp->set_enabled(false); IfTreeInterface::VifMap::iterator vi; for (vi = this_ifp->vifs().begin(); vi != this_ifp->vifs().end(); ++vi) { IfTreeVif* this_vifp = vi->second; markVifDeleted(this_vifp); } } continue; } // // Disable if not in the other tree // if (other_ifp == NULL) { this_ifp->set_enabled(false); continue; } // // Copy state from the other interface // if (! this_ifp->is_same_state(*other_ifp)) { bool enabled = false; if ((user_ifp != NULL) && user_ifp->enabled()) enabled = true; this_ifp->copy_state(*other_ifp, false); if (! enabled) this_ifp->set_enabled(enabled); } // // Align the vif state // IfTreeInterface::VifMap::iterator vi; for (vi = this_ifp->vifs().begin(); vi != this_ifp->vifs().end(); ++vi) { IfTreeVif* this_vifp = vi->second; const string& vifname = this_vifp->vifname(); const IfTreeVif* other_vifp = other_ifp->find_vif(vifname); const IfTreeVif* user_vifp = NULL; if (user_ifp != NULL) user_vifp = user_ifp->find_vif(vifname); // // Disable if not in the other tree // if (other_vifp == NULL) { this_vifp->set_enabled(false); continue; } // // Copy state from the other vif // if (! this_vifp->is_same_state(*other_vifp)) { bool enabled = false; if ((user_vifp != NULL) && user_vifp->enabled()) enabled = true; this_vifp->copy_state(*other_vifp); if (! enabled) this_vifp->set_enabled(enabled); } // // Align the IPv4 address state // IfTreeVif::IPv4Map::iterator ai4; for (ai4 = this_vifp->ipv4addrs().begin(); ai4 != this_vifp->ipv4addrs().end(); ++ai4) { IfTreeAddr4* this_ap = ai4->second; const IPv4& addr = this_ap->addr(); const IfTreeAddr4* other_ap = other_vifp->find_addr(addr); const IfTreeAddr4* user_ap = NULL; if (user_vifp != NULL) user_ap = user_vifp->find_addr(addr); // // Disable if not in the other tree // if (other_ap == NULL) { this_ap->set_enabled(false); continue; } // // Copy state from the other address // if (! this_ap->is_same_state(*other_ap)) { bool enabled = false; if ((user_ap != NULL) && user_ap->enabled()) enabled = true; this_ap->copy_state(*other_ap); if (! enabled) this_ap->set_enabled(enabled); } } // // Align the IPv6 address state // IfTreeVif::IPv6Map::iterator ai6; for (ai6 = this_vifp->ipv6addrs().begin(); ai6 != this_vifp->ipv6addrs().end(); ++ai6) { IfTreeAddr6* this_ap = ai6->second; const IPv6& addr = this_ap->addr(); const IfTreeAddr6* other_ap = other_vifp->find_addr(addr); const IfTreeAddr6* user_ap = NULL; if (user_vifp != NULL) user_ap = user_vifp->find_addr(addr); // // Disable if not in the other tree // if (other_ap == NULL) { this_ap->set_enabled(false); continue; } // // Copy state from the other address // if (! this_ap->is_same_state(*other_ap)) { bool enabled = false; if ((user_ap != NULL) && user_ap->enabled()) enabled = true; this_ap->copy_state(*other_ap); if (! enabled) this_ap->set_enabled(enabled); } } } } return (*this); } /** * Align system-user merged configuration with the observed changes * in the system configuration. * * Inside the FEA there may be multiple configuration representations, * typically one the user modifies, one that mirrors the hardware, * and one that merges those two (e.g., some of the merged information * comes from the user configuration while other might come * from the underlying system). * On certain systems there could be asynchronous updates originated * by the system that are captured by the FEA interface observer * (e.g., a cable is unplugged, a tunnel interface is added/deleted, etc). * * This method is used to align those updates with the merged * configuration. * * The alignment works as follows: * 1. If an interface in the other tree is not in the local tree, it is * tested whether is in the user configuration tree. If not, the * rest of the processing is not applied and the interface is ignored. * Otherwise it is created, its state is merged from the user config * and other tree, and is marked as "CREATED". * 2. If an interface in the local tree is marked as "soft", its * state is not modified and the rest of the processing is ignored. * 3. If an interface in the local tree is marked as * "default_system_config", the rest of the processing is not * applied, and its state (and the subtree below it) is copied * as-is from the other tree. * 4. If an item in the other tree is not in the local tree, it is * tested whether is in the user configuration tree. If not, the * rest of the processing is not applied and the item is ignored. * Otherwise it is created, its state is merged from the user config * and the other tree, and is marked as "CREATED". * 5. If an item in the other tree is marked as: * (a) "NO_CHANGE": The state of the entry in the other tree is not * propagated to the local tree, but its subtree entries are * processed. * (b) "DELETED": The item in the local tree is disabled, and the * subtree entries are ignored. * (c) "CREATED" or "CHANGED": If the state of the entry is different * in the other and the local tree, it is copied to the local tree, * and the item in the local tree is marked as "CREATED" or * "CHANGED". * Also, if the item is disabled in the user config tree, it is * marked as "disabled" in the local tree. * * @param other the configuration tree to align state with. * @param user_config the user configuration tree to reference during * the alignment. * @return modified configuration structure. */ IfTree& IfTree::align_with_observed_changes(const IfTree& other, const IfTree& user_config) { IfTree::IfMap::const_iterator oi; // // Iterate through all interfaces in the other tree // for (oi = other.interfaces().begin(); oi != other.interfaces().end(); ++oi) { const IfTreeInterface* other_ifp = oi->second; const string& ifname = other_ifp->ifname(); IfTreeInterface* this_ifp = find_interface(ifname); const IfTreeInterface* user_ifp = user_config.find_interface(ifname); // // Ignore interfaces that are not in the local or user config tree // if (this_ifp == NULL) { if (user_ifp == NULL) continue; // Create the interface add_interface(ifname); this_ifp = find_interface(ifname); XLOG_ASSERT(this_ifp != NULL); this_ifp->copy_state(*user_ifp, true); this_ifp->copy_state(*other_ifp, false); this_ifp->mark(CREATED); } // // Ignore "soft" interfaces // if (this_ifp->is_soft()) continue; // // Special processing for "default_system_config" interfaces // if (this_ifp->default_system_config()) { update_interface(*other_ifp); continue; } // // Test for "DELETED" entries // if (other_ifp->is_marked(DELETED)) { this_ifp->set_enabled(false); continue; } // // Test for "CREATED" or "CHANGED" entries // if (other_ifp->is_marked(CREATED) || other_ifp->is_marked(CHANGED)) { bool enabled = false; if ((user_ifp != NULL) && user_ifp->enabled()) enabled = true; // // Copy state from the other entry // if (! this_ifp->is_same_state(*other_ifp)) { this_ifp->copy_state(*other_ifp, false); if (! enabled) this_ifp->set_enabled(enabled); this_ifp->mark(CHANGED); // XXX: no-op if it was CREATED } } // // Align the vif state // IfTreeInterface::VifMap::const_iterator ov; for (ov = other_ifp->vifs().begin(); ov != other_ifp->vifs().end(); ++ov) { const IfTreeVif* other_vifp = ov->second; const string& vifname = other_vifp->vifname(); IfTreeVif* this_vifp = this_ifp->find_vif(vifname); const IfTreeVif* user_vifp = NULL; if (user_ifp != NULL) user_vifp = user_ifp->find_vif(vifname); // // Ignore entries that are not in the local or user config tree // if (this_vifp == NULL) { if (user_vifp == NULL) continue; // Create the vif this_ifp->add_vif(vifname); this_vifp = this_ifp->find_vif(vifname); XLOG_ASSERT(this_vifp != NULL); this_vifp->copy_state(*other_vifp); this_vifp->mark(CREATED); } // // Test for "DELETED" entries // if (other_vifp->is_marked(DELETED)) { this_vifp->set_enabled(false); continue; } // // Test for "CREATED" or "CHANGED" entries // if (other_vifp->is_marked(CREATED) || other_vifp->is_marked(CHANGED)) { bool enabled = false; if ((user_vifp != NULL) && user_vifp->enabled()) enabled = true; // // Copy state from the other entry // if (! this_vifp->is_same_state(*other_vifp)) { this_vifp->copy_state(*other_vifp); if (! enabled) this_vifp->set_enabled(enabled); this_vifp->mark(CHANGED); // XXX: no-op if it was CREATED } } // // Align the IPv4 address state // IfTreeVif::IPv4Map::const_iterator oa4; for (oa4 = other_vifp->ipv4addrs().begin(); oa4 != other_vifp->ipv4addrs().end(); ++oa4) { const IfTreeAddr4* other_ap = oa4->second; const IPv4& addr = other_ap->addr(); IfTreeAddr4* this_ap = this_vifp->find_addr(addr); const IfTreeAddr4* user_ap = NULL; if (user_vifp != NULL) user_ap = user_vifp->find_addr(addr); // // Ignore entries that are not in the local or user config tree // if (this_ap == NULL) { if (user_ap == NULL) continue; // Create the address this_vifp->add_addr(addr); this_ap = this_vifp->find_addr(addr); XLOG_ASSERT(this_ap != NULL); this_ap->copy_state(*other_ap); this_ap->mark(CREATED); } // // Test for "DELETED" entries // if (other_ap->is_marked(DELETED)) { this_ap->set_enabled(false); continue; } // // Test for "CREATED" or "CHANGED" entries // if (other_ap->is_marked(CREATED) || other_ap->is_marked(CHANGED)) { bool enabled = false; if ((user_ap != NULL) && user_ap->enabled()) enabled = true; // // Copy state from the other entry // if (! this_ap->is_same_state(*other_ap)) { this_ap->copy_state(*other_ap); if (! enabled) this_ap->set_enabled(enabled); this_ap->mark(CHANGED); // XXX: no-op if it was CREATED } } } // // Align the IPv6 address state // IfTreeVif::IPv6Map::const_iterator oa6; for (oa6 = other_vifp->ipv6addrs().begin(); oa6 != other_vifp->ipv6addrs().end(); ++oa6) { const IfTreeAddr6* other_ap = oa6->second; const IPv6& addr = other_ap->addr(); IfTreeAddr6* this_ap = this_vifp->find_addr(addr); const IfTreeAddr6* user_ap = NULL; if (user_vifp != NULL) user_ap = user_vifp->find_addr(addr); // // Ignore entries that are not in the local or user config tree // if (this_ap == NULL) { if (user_ap == NULL) continue; // Create the address this_vifp->add_addr(addr); this_ap = this_vifp->find_addr(addr); XLOG_ASSERT(this_ap != NULL); this_ap->copy_state(*other_ap); this_ap->mark(CREATED); } // // Test for "DELETED" entries // if (other_ap->is_marked(DELETED)) { this_ap->set_enabled(false); continue; } // // Test for "CREATED" or "CHANGED" entries // if (other_ap->is_marked(CREATED) || other_ap->is_marked(CHANGED)) { bool enabled = false; if ((user_ap != NULL) && user_ap->enabled()) enabled = true; // // Copy state from the other entry // if (! this_ap->is_same_state(*other_ap)) { if (! enabled) this_ap->set_enabled(enabled); this_ap->mark(CHANGED); // XXX: no-op if it was CREATED } } } } } return (*this); } /** * Align system-user merged configuration with the user configuration * changes. * * Inside the FEA there may be multiple configuration representations, * typically one the user modifies, one that mirrors the hardware, * and one that merges those two (e.g., some of the merged information * comes from the user configuration while other might come * from the underlying system). * * This method is used to align the user configuration changes with the * merged configuration. * * The alignment works as follows: * 1. If an item in the other tree is not in the local tree, it is * created in the local tree and its state (and the subtree below it) * is copied as-is from the other tree, and the rest of the processing * is ignored. * 2. If an item in the other tree is marked as: * (a) "DELETED": The item in the local tree is marked as "DELETED", * and the subtree entries are ignored. * (b) All other: If the state of the item is different in the other * and the local tree, it is copied to the local tree. * Note that we compare the state even for "NO_CHANGE" items in * case a previous change to a parent item in the merged tree * has affected the entry (e.g., disabled interface would * disable the vifs and addresses as well). * * @param other the configuration tree to align state with. * @return modified configuration structure. */ IfTree& IfTree::align_with_user_config(const IfTree& other) { IfTree::IfMap::const_iterator oi; // // Iterate through all interfaces in the other tree // for (oi = other.interfaces().begin(); oi != other.interfaces().end(); ++oi) { const IfTreeInterface* other_ifp = oi->second; IfTreeInterface* this_ifp = find_interface(other_ifp->ifname()); // // Recursively add entries that are not in the local tree // if (this_ifp == NULL) { add_recursive_interface(*other_ifp, false); continue; } // // Test for "DELETED" entries // if (other_ifp->is_marked(DELETED)) { this_ifp->mark(DELETED); continue; } // // Copy state from the other entry // if (! this_ifp->is_same_state(*other_ifp)) this_ifp->copy_state(*other_ifp, false); // // Align the vif state // IfTreeInterface::VifMap::const_iterator ov; for (ov = other_ifp->vifs().begin(); ov != other_ifp->vifs().end(); ++ov) { const IfTreeVif* other_vifp = ov->second; IfTreeVif* this_vifp = this_ifp->find_vif(other_vifp->vifname()); // // Recursively add entries that are not in the local tree // if (this_vifp == NULL) { this_ifp->add_recursive_vif(*other_vifp, false); continue; } // // Test for "DELETED" entries // if (other_vifp->is_marked(DELETED)) { this_vifp->mark(DELETED); continue; } // // Copy state from the other entry // if (! this_vifp->is_same_state(*other_vifp)) this_vifp->copy_state(*other_vifp); // // Align the IPv4 address state // IfTreeVif::IPv4Map::const_iterator oa4; for (oa4 = other_vifp->ipv4addrs().begin(); oa4 != other_vifp->ipv4addrs().end(); ++oa4) { const IfTreeAddr4* other_ap = oa4->second; IfTreeAddr4* this_ap = this_vifp->find_addr(other_ap->addr()); // // Recursively add entries that are not in the local tree // if (this_ap == NULL) { this_vifp->add_recursive_addr(*other_ap, false); continue; } // // Test for "DELETED" entries // if (other_ap->is_marked(DELETED)) { this_ap->mark(DELETED); continue; } // // Copy state from the other entry // if (! this_ap->is_same_state(*other_ap)) this_ap->copy_state(*other_ap); } // // Align the IPv6 address state // IfTreeVif::IPv6Map::const_iterator oa6; for (oa6 = other_vifp->ipv6addrs().begin(); oa6 != other_vifp->ipv6addrs().end(); ++oa6) { const IfTreeAddr6* other_ap = oa6->second; IfTreeAddr6* this_ap = this_vifp->find_addr(other_ap->addr()); // // Recursively add entries that are not in the local tree // if (this_ap == NULL) { this_vifp->add_recursive_addr(*other_ap, false); continue; } // // Test for "DELETED" entries // if (other_ap->is_marked(DELETED)) { this_ap->mark(DELETED); continue; } // // Copy state from the other entry // if (! this_ap->is_same_state(*other_ap)) this_ap->copy_state(*other_ap); } } } return (*this); } /** * Prepare configuration for pushing and replacing previous configuration. * * If the previous configuration is to be replaced with new configuration, * we need to prepare the state that will delete, update, and add the * new state as appropriate. * The preparation works as follows: * - All items in the local tree are preserved and are marked as created. * - All items in the other tree that are not in the local tree are * added to the local tree and are marked as deleted. * * @param other the configuration tree to be used to prepare the * replacement state. * @return modified configuration structure. */ IfTree& IfTree::prepare_replacement_state(const IfTree& other) { IfTree::IfMap::iterator ii; IfTree::IfMap::const_iterator oi; // // Mark all entries in the local tree as created // for (ii = interfaces().begin(); ii != interfaces().end(); ++ii) { IfTreeInterface& fi = *(ii->second); fi.mark(CREATED); // Mark all vifs IfTreeInterface::VifMap::iterator vi; for (vi = fi.vifs().begin(); vi != fi.vifs().end(); ++vi) { IfTreeVif& fv = *(vi->second); fv.mark(CREATED); // Mark all IPv4 addresses IfTreeVif::IPv4Map::iterator ai4; for (ai4 = fv.ipv4addrs().begin(); ai4 != fv.ipv4addrs().end(); ++ai4) { IfTreeAddr4& fa = *(ai4->second); fa.mark(CREATED); } // Mark all IPv6 addresses IfTreeVif::IPv6Map::iterator ai6; for (ai6 = fv.ipv6addrs().begin(); ai6 != fv.ipv6addrs().end(); ++ai6) { IfTreeAddr6& fa = *(ai6->second); fa.mark(CREATED); } } } // // Iterate through all interfaces in the other tree // for (oi = other.interfaces().begin(); oi != other.interfaces().end(); ++oi) { const IfTreeInterface& other_iface = *(oi->second); const string& ifname = other_iface.ifname(); IfTreeInterface* ifp = find_interface(ifname); if (ifp == NULL) { // // Add local interface and mark it for deletion. // // Mark local interface for deletion if not present in other. // add_interface(ifname); ifp = find_interface(ifname); XLOG_ASSERT(ifp != NULL); ifp->copy_state(other_iface, false); markIfaceDeleted(ifp); } // // Iterate through all vifs in the other tree // IfTreeInterface::VifMap::const_iterator ov; for (ov = other_iface.vifs().begin(); ov != other_iface.vifs().end(); ++ov) { const IfTreeVif& other_vif = *(ov->second); const string& vifname = other_vif.vifname(); IfTreeVif* vifp = ifp->find_vif(vifname); if (vifp == NULL) { // // Add local vif and mark it for deletion. // ifp->add_vif(vifname); vifp = ifp->find_vif(vifname); XLOG_ASSERT(vifp != NULL); vifp->copy_state(other_vif); markVifDeleted(vifp); } // // Iterate through all IPv4 addresses in the other tree // IfTreeVif::IPv4Map::const_iterator oa4; for (oa4 = other_vif.ipv4addrs().begin(); oa4 != other_vif.ipv4addrs().end(); ++oa4) { const IfTreeAddr4& other_addr = *(oa4->second); IfTreeAddr4* ap = vifp->find_addr(other_addr.addr()); if (ap == NULL) { // // Add local IPv4 address and mark it for deletion. // vifp->add_addr(other_addr.addr()); ap = vifp->find_addr(other_addr.addr()); XLOG_ASSERT(ap != NULL); ap->copy_state(other_addr); ap->mark(DELETED); } } // // Iterate through all IPv6 addresses in the other tree // IfTreeVif::IPv6Map::const_iterator oa6; for (oa6 = other_vif.ipv6addrs().begin(); oa6 != other_vif.ipv6addrs().end(); ++oa6) { const IfTreeAddr6& other_addr = *(oa6->second); IfTreeAddr6* ap = vifp->find_addr(other_addr.addr()); if (ap == NULL) { // // Add local IPv6 address and mark it for deletion. // vifp->add_addr(other_addr.addr()); ap = vifp->find_addr(other_addr.addr()); XLOG_ASSERT(ap != NULL); ap->copy_state(other_addr); ap->mark(DELETED); } } } } return (*this); } /** * Prune bogus deleted state. * * If an item in the local tree is marked as deleted, but is not * in the other tree, then it is removed. * * @param old_iftree the old tree with the state that is used as reference. * @return the modified configuration tree. */ IfTree& IfTree::prune_bogus_deleted_state(const IfTree& old_iftree) { IfTree::IfMap::iterator ii; // // Iterate through all interfaces // ii = _interfaces.begin(); while (ii != _interfaces.end()) { IfTreeInterface* ifp = ii->second; const string& ifname = ifp->ifname(); if (! ifp->is_marked(DELETED)) { ++ii; continue; } const IfTreeInterface* old_ifp; old_ifp = old_iftree.find_interface(ifname); if (old_ifp == NULL) { // Remove this item from the local tree sendEvent(IFTREE_ERASE_IFACE, ifp); _interfaces.erase(ii++); delete ifp; continue; } // // Iterate through all vifs // IfTreeInterface::VifMap::iterator vi; vi = ifp->vifs().begin(); while (vi != ifp->vifs().end()) { IfTreeVif* vifp = vi->second; const string& vifname = vifp->vifname(); if (! vifp->is_marked(DELETED)) { ++vi; continue; } const IfTreeVif* old_vifp; old_vifp = old_ifp->find_vif(vifname); if (old_vifp == NULL) { // Remove this item from the local tree sendEvent(IFTREE_ERASE_VIF, vifp); ifp->vifs().erase(vi++); delete vifp; continue; } // // Iterate through all IPv4 addresses // IfTreeVif::IPv4Map::iterator ai4; ai4 = vifp->ipv4addrs().begin(); while (ai4 != vifp->ipv4addrs().end()) { IfTreeAddr4* ap = ai4->second; if (! ap->is_marked(DELETED)) { ++ai4; continue; } const IfTreeAddr4* old_ap; old_ap = old_vifp->find_addr(ap->addr()); if (old_ap == NULL) { // Remove this item from the local tree vifp->ipv4addrs().erase(ai4++); delete ap; continue; } ++ai4; } // // Iterate through all IPv6 addresses // IfTreeVif::IPv6Map::iterator ai6; ai6 = vifp->ipv6addrs().begin(); while (ai6 != vifp->ipv6addrs().end()) { IfTreeAddr6* ap = ai6->second; if (! ap->is_marked(DELETED)) { ++ai6; continue; } const IfTreeAddr6* old_ap; old_ap = old_vifp->find_addr(ap->addr()); if (old_ap == NULL) { // Remove this item from the local tree vifp->ipv6addrs().erase(ai6++); delete ap; continue; } ++ai6; } ++vi; } ++ii; } return (*this); } void IfTree::insert_ifindex(IfTreeInterface* ifp) { IfIndexMap::iterator iter; XLOG_ASSERT(ifp != NULL); if (ifp->pif_index() == 0) return; // Ignore: invalid pif_index iter = _ifindex_map.find(ifp->pif_index()); if (iter != _ifindex_map.end()) { // OpenBSD has a bad habit of re-using ifindexes if you remove // a virtual device and re-add a new one. This confuses our // hashing. This code used to assert, but instead attempt fixup. XLOG_WARNING("iftree: %s _ifindex_map appears corrupted, found iter->second:" " %p (%d) != ifp: %p for pif_index: %d\n", name.c_str(), iter->second, iter->second->pif_index(), ifp, ifp->pif_index()); XLOG_WARNING("existing interface: %s ifp: %s\n", iter->second->ifname().c_str(), ifp->ifname().c_str()); if (iter->second != ifp) { IfTreeInterface* tmp = iter->second; XLOG_WARNING("Deleting interface: %s from tree: %s\n", tmp->ifname().c_str(), name.c_str()); markIfaceDeleted(tmp); sendEvent(IFTREE_ERASE_IFACE, tmp); _interfaces.erase(tmp->ifname()); delete tmp; } else { // Was a duplicate add..still needs fixing, but perhaps not as critical. return; } } _ifindex_map[ifp->pif_index()] = ifp; } void IfTree::erase_ifindex(IfTreeInterface* ifp) { IfIndexMap::iterator iter; XLOG_ASSERT(ifp != NULL); if (ifp->pif_index() == 0) return; // Ignore: invalid pif_index iter = _ifindex_map.find(ifp->pif_index()); XLOG_ASSERT(iter != _ifindex_map.end()); XLOG_ASSERT(iter->second == ifp); _ifindex_map.erase(iter); } void IfTree::insert_vifindex(IfTreeVif* vifp) { VifIndexMap::const_iterator iter; XLOG_ASSERT(vifp != NULL); if (vifp->pif_index() == 0) return; // Ignore: invalid pif_index // XXX: Check all multimap entries that match to the same pif_index iter = _vifindex_map.find(vifp->pif_index()); while (iter != _vifindex_map.end()) { if (iter->first != vifp->pif_index()) break; if (iter->second == vifp) return; // Entry has been added previously ++iter; } _vifindex_map.insert(make_pair(vifp->pif_index(), vifp)); } void IfTree::erase_vifindex(IfTreeVif* vifp) { VifIndexMap::iterator iter; XLOG_ASSERT(vifp != NULL); if (vifp->pif_index() == 0) return; // Ignore: invalid pif_index iter = _vifindex_map.find(vifp->pif_index()); XLOG_ASSERT(iter != _vifindex_map.end()); // XXX: Check all multimap entries that match to the same pif_index while (iter != _vifindex_map.end()) { if (iter->first != vifp->pif_index()) break; if (iter->second == vifp) { // Entry found _vifindex_map.erase(iter); return; } ++iter; } XLOG_UNREACHABLE(); } /* ------------------------------------------------------------------------- */ /* IfTreeInterface code */ IfTreeInterface::IfTreeInterface(IfTree& iftree, const string& ifname) : IfTreeItem(), _iftree(iftree), _ifname(ifname), _pif_index(0), _created_by_xorp(false), _probed_vlan(false), _enabled(false), _discard(false), _unreachable(false), _management(false), _default_system_config(false), _mtu(0), _no_carrier(false), _baudrate(0), _interface_flags(0) {} IfTreeInterface::~IfTreeInterface() { while (! _vifs.empty()) { IfTreeVif* vifp = _vifs.begin()->second; iftree().sendEvent(IFTREE_ERASE_VIF, vifp); _vifs.erase(_vifs.begin()); delete vifp; } iftree().erase_ifindex(this); } bool IfTreeInterface::is_same_state(const IfTreeInterface& o) { // // XXX: Explicitly don't consider the discard, unreachable, // management, and default_system_config flags, because they // are always set from user's configuration. // return ((pif_index() == o.pif_index()) && (enabled() == o.enabled()) && (mtu() == o.mtu()) && (mac() == o.mac()) && (no_carrier() == o.no_carrier()) && (baudrate() == o.baudrate()) && (_parent_ifname == o._parent_ifname) && (strcasecmp(_iface_type.c_str(), o._iface_type.c_str()) == 0) && (strcasecmp(_vid.c_str(), o._vid.c_str()) == 0) && (interface_flags() == o.interface_flags())); } void IfTreeInterface::copy_state(const IfTreeInterface& o, bool copy_user_config) { // // XXX: Explicitly don't consider the discard, unreachable, // management, and default_system_config flags, because they // are always set from user's configuration. // set_pif_index(o.pif_index()); set_enabled(o.enabled()); set_mtu(o.mtu()); set_mac(o.mac()); set_no_carrier(o.no_carrier()); set_baudrate(o.baudrate()); set_interface_flags(o.interface_flags()); _parent_ifname = o._parent_ifname; _iface_type = o._iface_type; _vid = o._vid; if (copy_user_config) { // Copy the flags from the user configuration set_discard(o.discard()); set_unreachable(o.unreachable()); set_management(o.management()); set_default_system_config(o.default_system_config()); } } void IfTreeInterface::add_recursive_vif(const IfTreeVif& other_vif, bool mark_state) { const string& vifname = other_vif.vifname(); IfTreeVif* vifp; // Add the vif vifp = new IfTreeVif(*this, vifname); _vifs.insert(IfTreeInterface::VifMap::value_type(vifname, vifp)); vifp->copy_state(other_vif); if (mark_state) vifp->set_state(other_vif.state()); else vifp->mark(CREATED); // Add recursively all the IPv4 addresses from the other vif IfTreeVif::IPv4Map::const_iterator oa4; for (oa4 = other_vif.ipv4addrs().begin(); oa4 != other_vif.ipv4addrs().end(); ++oa4) { const IfTreeAddr4& other_addr = *(oa4->second); vifp->add_recursive_addr(other_addr, mark_state); } // Add recursively all the IPv6 addresses from the other vif IfTreeVif::IPv6Map::const_iterator oa6; for (oa6 = other_vif.ipv6addrs().begin(); oa6 != other_vif.ipv6addrs().end(); ++oa6) { const IfTreeAddr6& other_addr = *(oa6->second); vifp->add_recursive_addr(other_addr, mark_state); } } int IfTreeInterface::add_vif(const string& vifname) { IfTreeVif* vifp = find_vif(vifname); if (vifp != NULL) { vifp->mark(CREATED); return (XORP_OK); } vifp = new IfTreeVif(*this, vifname); _vifs.insert(IfTreeInterface::VifMap::value_type(vifname, vifp)); return (XORP_OK); } int IfTreeInterface::remove_vif(const string& vifname) { IfTreeVif* vifp = find_vif(vifname); if (vifp == NULL) return (XORP_ERROR); iftree().markVifDeleted(vifp); return (XORP_OK); } IfTreeVif* IfTreeInterface::find_vif(const string& vifname) { IfTreeInterface::VifMap::iterator iter = _vifs.find(vifname); if (iter == _vifs.end()) return (NULL); return (iter->second); } const IfTreeVif* IfTreeInterface::find_vif(const string& vifname) const { IfTreeInterface::VifMap::const_iterator iter = _vifs.find(vifname); if (iter == _vifs.end()) return (NULL); return (iter->second); } IfTreeVif* IfTreeInterface::find_vif(uint32_t pif_index) { IfTreeInterface::VifMap::iterator iter; for (iter = _vifs.begin(); iter != _vifs.end(); ++iter) { if (iter->second->pif_index() == pif_index) return (iter->second); } return (NULL); } const IfTreeVif* IfTreeInterface::find_vif(uint32_t pif_index) const { IfTreeInterface::VifMap::const_iterator iter; for (iter = _vifs.begin(); iter != _vifs.end(); ++iter) { if (iter->second->pif_index() == pif_index) return (iter->second); } return (NULL); } IfTreeAddr4* IfTreeInterface::find_addr(const string& vifname, const IPv4& addr) { IfTreeVif* vif = find_vif(vifname); if (vif == NULL) return (NULL); return (vif->find_addr(addr)); } const IfTreeAddr4* IfTreeInterface::find_addr(const string& vifname, const IPv4& addr) const { const IfTreeVif* vif = find_vif(vifname); if (vif == NULL) return (NULL); return (vif->find_addr(addr)); } IfTreeAddr6* IfTreeInterface::find_addr(const string& vifname, const IPv6& addr) { IfTreeVif* vif = find_vif(vifname); if (vif == NULL) return (NULL); return (vif->find_addr(addr)); } const IfTreeAddr6* IfTreeInterface::find_addr(const string& vifname, const IPv6& addr) const { const IfTreeVif* vif = find_vif(vifname); if (vif == NULL) return (NULL); return (vif->find_addr(addr)); } void IfTreeInterface::finalize_state() { // // Iterate through all vifs // VifMap::iterator vi = _vifs.begin(); while (vi != _vifs.end()) { IfTreeVif* vifp = vi->second; // If interface is marked as deleted, delete it. if (vifp->is_marked(DELETED)) { iftree().sendEvent(IFTREE_ERASE_VIF, vifp); _vifs.erase(vi++); delete vifp; continue; } // Call finalize_state on vifs that remain vifp->finalize_state(); ++vi; } set_state(NO_CHANGE); } bool IfTreeInterface::is_vlan() const { return (strcasecmp(iface_type().c_str(), "VLAN") == 0); } string IfTreeInterface::str() const { string r = c_format("Interface %s { pif_index = %u } { enabled := %s } " "{ discard := %s } { unreachable := %s } " "{ management = %s } { default_system_config = %s }" "{ mtu := %u } { mac := %s } { no_carrier = %s } " "{ baudrate := %u } { flags := %u }" "{ parent-ifname = %s } { iface-type = %s } { vid = %s }", _ifname.c_str(), XORP_UINT_CAST(_pif_index), bool_c_str(_enabled), bool_c_str(_discard), bool_c_str(_unreachable), bool_c_str(_management), bool_c_str(_default_system_config), XORP_UINT_CAST(_mtu), _mac.str().c_str(), bool_c_str(_no_carrier), XORP_UINT_CAST(_baudrate), XORP_UINT_CAST(_interface_flags), _parent_ifname.c_str(), _iface_type.c_str(), _vid.c_str()); r += string(" ") + IfTreeItem::str(); return (r); } /* ------------------------------------------------------------------------- */ /* IfTreeVif code */ IfTreeVif::IfTreeVif(IfTreeInterface& iface, const string& vifname) : IfTreeItem(), _iface(iface), _vifname(vifname), _pif_index(0), _vif_index(Vif::VIF_INDEX_INVALID), _enabled(false), _broadcast(false), _loopback(false), _point_to_point(false), _multicast(false), _pim_register(false), _vif_flags(0) {} IfTreeVif::~IfTreeVif() { while (! _ipv4addrs.empty()) { IfTreeAddr4* ap = _ipv4addrs.begin()->second; _ipv4addrs.erase(_ipv4addrs.begin()); delete ap; } while (! _ipv6addrs.empty()) { IfTreeAddr6* ap = _ipv6addrs.begin()->second; _ipv6addrs.erase(_ipv6addrs.begin()); delete ap; } iftree().erase_vifindex(this); } void IfTreeVif::copy_recursive_vif(const IfTreeVif& other_vif) { // Remove the old IPv4 addresses while (! _ipv4addrs.empty()) { IfTreeAddr4* ap = _ipv4addrs.begin()->second; _ipv4addrs.erase(_ipv4addrs.begin()); delete ap; } // Remove the old IPv4 addresses while (! _ipv6addrs.empty()) { IfTreeAddr6* ap = _ipv6addrs.begin()->second; _ipv6addrs.erase(_ipv6addrs.begin()); delete ap; } copy_state(other_vif); // Add all the IPv4 addresses from the other vif IfTreeVif::IPv4Map::const_iterator oa4; for (oa4 = other_vif.ipv4addrs().begin(); oa4 != other_vif.ipv4addrs().end(); ++oa4) { const IfTreeAddr4& other_addr = *(oa4->second); const IPv4& addr = other_addr.addr(); IfTreeAddr4* ap = new IfTreeAddr4(addr); _ipv4addrs.insert(IfTreeVif::IPv4Map::value_type(addr, ap)); ap->copy_state(other_addr); } // Add all the IPv6 addresses from the other vif IfTreeVif::IPv6Map::const_iterator oa6; for (oa6 = other_vif.ipv6addrs().begin(); oa6 != other_vif.ipv6addrs().end(); ++oa6) { const IfTreeAddr6& other_addr = *(oa6->second); const IPv6& addr = other_addr.addr(); IfTreeAddr6* ap = new IfTreeAddr6(addr); _ipv6addrs.insert(IfTreeVif::IPv6Map::value_type(addr, ap)); ap->copy_state(other_addr); } } void IfTreeVif::add_recursive_addr(const IfTreeAddr4& other_addr, bool mark_state) { const IPv4& addr = other_addr.addr(); IfTreeAddr4* ap; // Add the address ap = new IfTreeAddr4(addr); _ipv4addrs.insert(IfTreeVif::IPv4Map::value_type(addr, ap)); ap->copy_state(other_addr); if (mark_state) ap->set_state(other_addr.state()); else ap->mark(CREATED); } void IfTreeVif::add_recursive_addr(const IfTreeAddr6& other_addr, bool mark_state) { const IPv6& addr = other_addr.addr(); IfTreeAddr6* ap; // Add the address ap = new IfTreeAddr6(addr); _ipv6addrs.insert(IfTreeVif::IPv6Map::value_type(addr, ap)); ap->copy_state(other_addr); if (mark_state) ap->set_state(other_addr.state()); else ap->mark(CREATED); } int IfTreeVif::add_addr(const IPv4& addr) { IfTreeAddr4* ap = find_addr(addr); if (ap != NULL) { ap->mark(CREATED); return (XORP_OK); } ap = new IfTreeAddr4(addr); _ipv4addrs.insert(IPv4Map::value_type(addr, ap)); return (XORP_OK); } int IfTreeVif::remove_addr(const IPv4& addr) { IfTreeAddr4* ap = find_addr(addr); if (ap == NULL) return (XORP_ERROR); ap->mark(DELETED); return (XORP_OK); } int IfTreeVif::add_addr(const IPv6& addr) { IfTreeAddr6* ap = find_addr(addr); if (ap != NULL) { ap->mark(CREATED); return (XORP_OK); } ap = new IfTreeAddr6(addr); _ipv6addrs.insert(IPv6Map::value_type(addr, ap)); return (XORP_OK); } int IfTreeVif::remove_addr(const IPv6& addr) { IfTreeAddr6* ap = find_addr(addr); if (ap == NULL) return (XORP_ERROR); ap->mark(DELETED); return (XORP_OK); } IfTreeAddr4* IfTreeVif::find_addr(const IPv4& addr) { IfTreeVif::IPv4Map::iterator iter = _ipv4addrs.find(addr); if (iter == _ipv4addrs.end()) return (NULL); return (iter->second); } const IfTreeAddr4* IfTreeVif::find_addr(const IPv4& addr) const { IfTreeVif::IPv4Map::const_iterator iter = _ipv4addrs.find(addr); if (iter == _ipv4addrs.end()) return (NULL); return (iter->second); } IfTreeAddr6* IfTreeVif::find_addr(const IPv6& addr) { IfTreeVif::IPv6Map::iterator iter = _ipv6addrs.find(addr); if (iter == _ipv6addrs.end()) return (NULL); return (iter->second); } const IfTreeAddr6* IfTreeVif::find_addr(const IPv6& addr) const { IfTreeVif::IPv6Map::const_iterator iter = _ipv6addrs.find(addr); if (iter == _ipv6addrs.end()) return (NULL); return (iter->second); } void IfTreeVif::propagate_flags_to_addresses() { // // XXX: For now we propagate only the "enabled" flag, because // the rest of the address flags are not useful and should be deleted // in the future. // // Propagate the flags to the IPv4 addresses IfTreeVif::IPv4Map::iterator iter4; for (iter4 = _ipv4addrs.begin(); iter4 != _ipv4addrs.end(); ++iter4) { IfTreeAddr4* ap = iter4->second; if (enabled() != ap->enabled()) ap->set_enabled(enabled()); } // Propagate the flags to the IPv6 addresses IfTreeVif::IPv6Map::iterator iter6; for (iter6 = _ipv6addrs.begin(); iter6 != _ipv6addrs.end(); ++iter6) { IfTreeAddr6* ap = iter6->second; if (enabled() != ap->enabled()) ap->set_enabled(enabled()); } } void IfTreeVif::finalize_state() { // // Iterate through all IPv4 addresses // for (IPv4Map::iterator ai = _ipv4addrs.begin(); ai != _ipv4addrs.end(); ) { IfTreeAddr4* ap = ai->second; // If address is marked as deleted, delete it. if (ap->is_marked(DELETED)) { _ipv4addrs.erase(ai++); delete ap; continue; } // Call finalize_state on addresses that remain ap->finalize_state(); ++ai; } // // Iterate through all IPv6 addresses // for (IPv6Map::iterator ai = _ipv6addrs.begin(); ai != _ipv6addrs.end(); ) { IfTreeAddr6* ap = ai->second; // If address is marked as deleted, delete it. if (ap->is_marked(DELETED)) { _ipv6addrs.erase(ai++); delete ap; continue; } // Call finalize_state on interfaces that remain ap->finalize_state(); ++ai; } set_state(NO_CHANGE); } string IfTreeVif::str() const { string pim_register_str; string vif_index_str; string vlan_str; // // XXX: Conditionally print the pim_register flag, because it is // used only by the MFEA. // if (_pim_register) { pim_register_str = c_format("{ pim_register := %s } ", bool_c_str(_pim_register)); } // XXX: Conditionally print the vif_index, because it is rarely used if (_vif_index != Vif::VIF_INDEX_INVALID) { vif_index_str = c_format("{ vif_index := %u } ", XORP_UINT_CAST(_vif_index)); } vif_index_str += pim_register_str; vif_index_str += vlan_str; string r = c_format("VIF %s { pif_index = %u } { enabled := %s } " "{ broadcast := %s } { loopback := %s } " "{ point_to_point := %s } { multicast := %s } " "{ flags := %u }", _vifname.c_str(), XORP_UINT_CAST(_pif_index), bool_c_str(_enabled), bool_c_str(_broadcast), bool_c_str(_loopback), bool_c_str(_point_to_point), bool_c_str(_multicast), XORP_UINT_CAST(_vif_flags)); r += vif_index_str + string(" ") + IfTreeItem::str(); return (r); } /* ------------------------------------------------------------------------- */ /* IfTreeAddr4 code */ int IfTreeAddr4::set_prefix_len(uint32_t prefix_len) { if (prefix_len > IPv4::addr_bitlen()) return (XORP_ERROR); _prefix_len = prefix_len; mark(CHANGED); return (XORP_OK); } void IfTreeAddr4::set_bcast(const IPv4& baddr) { _oaddr = baddr; mark(CHANGED); } IPv4 IfTreeAddr4::bcast() const { if (broadcast()) return (_oaddr); return (IPv4::ZERO()); } void IfTreeAddr4::set_endpoint(const IPv4& oaddr) { _oaddr = oaddr; mark(CHANGED); } IPv4 IfTreeAddr4::endpoint() const { if (point_to_point()) return (_oaddr); return (IPv4::ZERO()); } void IfTreeAddr4::finalize_state() { set_state(NO_CHANGE); } string IfTreeAddr4::str() const { string r = c_format("IPv4Addr %s { enabled := %s } { broadcast := %s } " "{ loopback := %s } { point_to_point := %s } " "{ multicast := %s } " "{ prefix_len := %u }", _addr.str().c_str(), bool_c_str(_enabled), bool_c_str(_broadcast), bool_c_str(_loopback), bool_c_str(_point_to_point), bool_c_str(_multicast), XORP_UINT_CAST(_prefix_len)); if (_point_to_point) r += c_format(" { endpoint := %s }", _oaddr.str().c_str()); if (_broadcast) r += c_format(" { broadcast := %s }", _oaddr.str().c_str()); r += string(" ") + IfTreeItem::str(); return (r); } /* ------------------------------------------------------------------------- */ /* IfTreeAddr6 code */ int IfTreeAddr6::set_prefix_len(uint32_t prefix_len) { if (prefix_len > IPv6::addr_bitlen()) return (XORP_ERROR); _prefix_len = prefix_len; mark(CHANGED); return (XORP_OK); } void IfTreeAddr6::set_endpoint(const IPv6& oaddr) { _oaddr = oaddr; mark(CHANGED); } IPv6 IfTreeAddr6::endpoint() const { if (point_to_point()) return (_oaddr); return (IPv6::ZERO()); } void IfTreeAddr6::finalize_state() { set_state(NO_CHANGE); } string IfTreeAddr6::str() const { string r = c_format("IPv6Addr %s { enabled := %s } " "{ loopback := %s } { point_to_point := %s } " "{ multicast := %s } " "{ prefix_len := %u }", _addr.str().c_str(), bool_c_str(_enabled), bool_c_str(_loopback), bool_c_str(_point_to_point), bool_c_str(_multicast), XORP_UINT_CAST(_prefix_len)); if (_point_to_point) r += c_format(" { endpoint := %s }", _oaddr.str().c_str()); r += string(" ") + IfTreeItem::str(); return (r); } xorp/fea/mfea_osdep.hh0000664000076400007640000001370611635757530015061 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __FEA_MFEA_OSDEP_HH__ #define __FEA_MFEA_OSDEP_HH__ // // A header file that adds various definitions that may be missing from the OS // #include "libxorp/xorp.h" #ifdef HAVE_SYS_TIME_H #include #endif #ifdef HAVE_NETINET_IP6_H #include #endif #ifdef HAVE_NETINET_ICMP6_H #include #endif #include "mrt/include/ip_mroute.h" #include "mfea_kernel_messages.hh" // // XXX: misc. (eventually) missing definitions // TODO: those should go somewhere else // #ifndef INADDR_ALLRTRS_GROUP #define INADDR_ALLRTRS_GROUP (uint32_t)0xe0000002U // 224.0.0.2 #endif #ifndef VIFF_REGISTER #define VIFF_REGISTER 0x4 // Vif for PIM Register encap/decap #endif #ifndef IGMPMSG_WHOLEPKT #define IGMPMSG_WHOLEPKT 3 // Whole packet sent from the kernel to // the user-level process (typically // a multicast data packet for PIM // register encapsulation). #endif #ifndef IPPROTO_PIM #define IPPROTO_PIM 103 // Protocol Independent Multicast #endif // // XXX: in *BSD there is only MRT_ASSERT, but in Linux there are both // MRT_ASSERT and MRT_PIM (with MRT_PIM defined as a superset of MRT_ASSERT). // #ifndef MRT_PIM #define MRT_PIM MRT_ASSERT #endif #ifndef IP6OPT_RTALERT #define IP6OPT_RTALERT 0x05 #endif #ifndef IP6OPT_RTALERT_LEN #define IP6OPT_RTALERT_LEN 4 #endif #ifndef IP6OPT_RTALERT_MLD #define IP6OPT_RTALERT_MLD 0 #endif // // MLD-related missing definitions // // Note that on newer systems all MLD-related definitions use // mld_xxx and MLD_XXX instead of mld6_xxx and MLD6_XXX. // #ifdef HAVE_IPV6_MULTICAST_ROUTING #ifndef MLD_LISTENER_QUERY # ifdef MLD6_LISTENER_QUERY # define MLD_LISTENER_QUERY MLD6_LISTENER_QUERY # else # define MLD_LISTENER_QUERY 130 # endif #endif #ifndef MLD_LISTENER_REPORT # ifdef MLD6_LISTENER_REPORT # define MLD_LISTENER_REPORT MLD6_LISTENER_REPORT # else # define MLD_LISTENER_REPORT 131 # endif #endif #ifndef MLD_LISTENER_DONE # ifdef MLD6_LISTENER_DONE # define MLD_LISTENER_DONE MLD6_LISTENER_DONE # else # define MLD_LISTENER_DONE 132 # endif #endif #ifndef MLD_MTRACE_RESP # ifdef MLD6_MTRACE_RESP # define MLD_MTRACE_RESP MLD6_MTRACE_RESP # else # define MLD_MTRACE_RESP 200 # endif #endif #ifndef MLD_MTRACE # ifdef MLD6_MTRACE # define MLD_MTRACE MLD6_MTRACE # else # define MLD_MTRACE 201 # endif #endif #ifndef MLDV2_LISTENER_REPORT # ifdef MLD6V2_LISTENER_REPORT # define MLDV2_LISTENER_REPORT MLD6V2_LISTENER_REPORT # else # define MLDV2_LISTENER_REPORT 143 # endif #endif #ifndef MLD_MINLEN # ifdef HAVE_STRUCT_MLD_HDR # define MLD_MINLEN (sizeof(struct mld_hdr)) # else # define MLD_MINLEN 24 # endif #endif #endif // HAVE_IPV6_MULTICAST_ROUTING // // Test if the kernel multicast signal message types are consistent // between IPv4 and IPv6. // E.g., if (IGMPMSG_NOCACHE == MRT6MSG_NOCACHE) // (IGMPMSG_WRONGVIF == MRT6MSG_WRONGMIF) // (IGMPMSG_WHOLEPKT == MRT6MSG_WHOLEPKT) // (IGMPMSG_BW_UPCALL == MRT6MSG_BW_UPCALL) // Also, check if MFEA_KERNEL_MESSAGE_NOCACHE/WRONGVIF/WHOLEPKT/BW_UPCALL // were defined accurately. // TODO: Yes, I know this is a very, very bad style, but I wanted to have // abstract kernel signal types, and I didn't want the upper layer // protocols to use IGMPMSG/MRT6MSG, and to have special cases // for IPv4 and IPv6. Maybe later this will change... // #ifdef HAVE_IPV6_MULTICAST_ROUTING #if IGMPMSG_NOCACHE != MRT6MSG_NOCACHE # error "MFEA message handling needs fix, because IGMPMSG_NOCACHE != MRT6MSG_NOCACHE" #endif #if IGMPMSG_WRONGVIF != MRT6MSG_WRONGMIF # error "MFEA message handling needs fix, because IGMPMSG_WRONGVIF != MRT6MSG_WRONGMIF" #endif #if IGMPMSG_WHOLEPKT != MRT6MSG_WHOLEPKT # error "MFEA message handling needs fix, because IGMPMSG_WHOLEPKT != MRT6MSG_WHOLEPKT" #endif #if defined(IGMPMSG_BW_UPCALL) && defined(MRT6MSG_BW_UPCALL) #if IGMPMSG_BW_UPCALL != MRT6MSG_BW_UPCALL # error "MFEA message handling needs fix, because IGMPMSG_BW_UPCALL != MRT6MSG_BW_UPCALL" #endif #endif #endif // HAVE_IPV6_MULTICAST_ROUTING #ifdef HAVE_IPV4_MULTICAST_ROUTING #if IGMPMSG_NOCACHE != MFEA_KERNEL_MESSAGE_NOCACHE # error "MFEA message handling needs fix, because IGMPMSG_NOCACHE != MFEA_KERNEL_MESSAGE_NOCACHE" #endif #if IGMPMSG_WRONGVIF != MFEA_KERNEL_MESSAGE_WRONGVIF # error "MFEA message handling needs fix, because IGMPMSG_WRONGVIF != MFEA_KERNEL_MESSAGE_WRONGVIF" #endif #if IGMPMSG_WHOLEPKT != MFEA_KERNEL_MESSAGE_WHOLEPKT # error "MFEA message handling needs fix, because IGMPMSG_WHOLEPKT != MFEA_KERNEL_MESSAGE_WHOLEPKT" #endif #if defined(IGMPMSG_BW_UPCALL) #if IGMPMSG_BW_UPCALL != MFEA_KERNEL_MESSAGE_BW_UPCALL # error "MFEA message handling needs fix, because IGMPMSG_BW_UPCALL != MFEA_KERNEL_MESSAGE_BW_UPCALL" #endif #endif #if defined(MRT6MSG_BW_UPCALL) #if MRT6MSG_BW_UPCALL != MFEA_KERNEL_MESSAGE_BW_UPCALL # error "MFEA message handling needs fix, because MRT6MSG_BW_UPCALL != MFEA_KERNEL_MESSAGE_BW_UPCALL" #endif #endif #endif // HAVE_IPV4_MULTICAST_ROUTING // // Global variables // // // Global functions prototypes // #endif // __FEA_MFEA_OSDEP_HH__ xorp/fea/mfea_config.cc0000664000076400007640000001447611421137511015167 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // TODO: a temporary solution for various MFEA configuration // #include "mfea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvx.hh" #include "mfea_node.hh" #include "mfea_vif.hh" /** * Add a configured vif. * * @param vif the vif with the information to add. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int MfeaNode::add_config_vif(const Vif& vif, string& error_msg) { // // Perform all the micro-operations that are required to add a vif. // if (ProtoNode::add_config_vif(vif.name(), vif.vif_index(), error_msg) != XORP_OK) { return (XORP_ERROR); } list::const_iterator vif_addr_iter; for (vif_addr_iter = vif.addr_list().begin(); vif_addr_iter != vif.addr_list().end(); ++vif_addr_iter) { const VifAddr& vif_addr = *vif_addr_iter; if (ProtoNode::add_config_vif_addr(vif.name(), vif_addr.addr(), vif_addr.subnet_addr(), vif_addr.broadcast_addr(), vif_addr.peer_addr(), error_msg) != XORP_OK) { return (XORP_ERROR); } } if (ProtoNode::set_config_pif_index(vif.name(), vif.pif_index(), error_msg) != XORP_OK) { return (XORP_ERROR); } if (ProtoNode::set_config_vif_flags(vif.name(), vif.is_pim_register(), vif.is_p2p(), vif.is_loopback(), vif.is_multicast_capable(), vif.is_broadcast_capable(), vif.is_underlying_vif_up(), vif.mtu(), error_msg) != XORP_OK) { return (XORP_ERROR); } return (XORP_OK); } /** * Complete the set of vif configuration changes. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int MfeaNode::set_config_all_vifs_done(string& error_msg) { map::iterator vif_iter; map& configured_vifs = ProtoNode::configured_vifs(); string dummy_error_msg; // // Add new vifs, and update existing ones // for (vif_iter = configured_vifs.begin(); vif_iter != configured_vifs.end(); ++vif_iter) { Vif* vif = &vif_iter->second; MfeaVif* node_vif = vif_find_by_name(vif->name()); // // Add a new vif // if (node_vif == NULL) { add_vif(*vif, dummy_error_msg); continue; } // // Get the vif's old primary address and whether the vif is UP // bool old_vif_is_up = node_vif->is_up(); IPvX old_primary_addr = IPvX::ZERO(family()); if (node_vif->addr_ptr() != NULL) old_primary_addr = *node_vif->addr_ptr(); // // Update the vif flags // node_vif->set_p2p(vif->is_p2p()); node_vif->set_loopback(vif->is_loopback()); node_vif->set_multicast_capable(vif->is_multicast_capable()); node_vif->set_broadcast_capable(vif->is_broadcast_capable()); node_vif->set_underlying_vif_up(vif->is_underlying_vif_up()); node_vif->set_mtu(vif->mtu()); // // Add new vif addresses, and update existing ones // { list::const_iterator vif_addr_iter; for (vif_addr_iter = vif->addr_list().begin(); vif_addr_iter != vif->addr_list().end(); ++vif_addr_iter) { const VifAddr& vif_addr = *vif_addr_iter; VifAddr* node_vif_addr = node_vif->find_address(vif_addr.addr()); if (node_vif_addr == NULL) { node_vif->add_address(vif_addr); continue; } // Update the address if (*node_vif_addr != vif_addr) { *node_vif_addr = vif_addr; } } } // // Delete vif addresses that don't exist anymore. // If the vif's primary address is deleted, then first stop the vif. // { list delete_addresses_list; list::const_iterator vif_addr_iter; string dummy_error_msg; 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->find_address(vif_addr.addr()) == 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 (ipvx == old_primary_addr) { // Stop the vif if the primary address is deleted if (old_vif_is_up) node_vif->stop(dummy_error_msg); } node_vif->delete_address(ipvx); } } // // If the vif has no address then stop it. // If the vif's primary address was changed, then restart the vif. // do { string dummy_error_msg; if (node_vif->addr_ptr() == NULL) { node_vif->stop(dummy_error_msg); break; } if (old_primary_addr == *node_vif->addr_ptr()) break; // Nothing changed // Conditionally restart the interface node_vif->stop(dummy_error_msg); if (old_vif_is_up) node_vif->start(dummy_error_msg); break; } while (false); } // // Remove vifs that don't exist anymore // for (uint32_t i = 0; i < maxvifs(); i++) { Vif* node_vif = vif_find_by_vif_index(i); if (node_vif == NULL) continue; if (node_vif->is_pim_register()) continue; // XXX: don't delete the PIM Register vif if (configured_vifs.find(node_vif->name()) == configured_vifs.end()) { // Delete the interface string vif_name = node_vif->name(); delete_vif(vif_name, dummy_error_msg); continue; } } if (end_config(error_msg) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } xorp/fea/fibconfig_transaction.cc0000664000076400007640000000420211421137511017247 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "fibconfig_transaction.hh" int FibConfigTransactionManager::set_error(const string& error) { if (! _first_error.empty()) return (XORP_ERROR); _first_error = error; return (XORP_OK); } void FibConfigTransactionManager::pre_commit(uint32_t /* tid */) { string error_msg; reset_error(); if (fibconfig().start_configuration(error_msg) != XORP_OK) { XLOG_ERROR("Cannot start configuration: %s", error_msg.c_str()); set_error(error_msg); } } void FibConfigTransactionManager::post_commit(uint32_t /* tid */) { string error_msg; if (fibconfig().end_configuration(error_msg) != XORP_OK) { XLOG_ERROR("Cannot end configuration: %s", error_msg.c_str()); set_error(error_msg); } } void FibConfigTransactionManager::operation_result(bool success, const TransactionOperation& op) { if (success) return; const FibConfigTransactionOperation* fto; fto = dynamic_cast(&op); XLOG_ASSERT(fto != NULL); // // Record error and xlog first error only // if (set_error(fto->str()) == XORP_OK) { XLOG_ERROR("FIB transaction commit failed on %s", fto->str().c_str()); } } xorp/fea/ifconfig_observer.hh0000664000076400007640000000555511421137511016435 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/ifconfig_observer.hh,v 1.32 2008/10/02 21:56:47 bms Exp $ #ifndef __FEA_IFCONFIG_OBSERVER_HH__ #define __FEA_IFCONFIG_OBSERVER_HH__ #include "iftree.hh" #include "fea_data_plane_manager.hh" class IfConfig; class IfConfigObserver { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ IfConfigObserver(FeaDataPlaneManager& fea_data_plane_manager) : _is_running(false), _ifconfig(fea_data_plane_manager.ifconfig()), _fea_data_plane_manager(fea_data_plane_manager) {} /** * Virtual destructor. */ virtual ~IfConfigObserver() {} /** * Get the @ref IfConfig instance. * * @return the @ref IfConfig instance. */ IfConfig& ifconfig() { return _ifconfig; } /** * Get the @ref FeaDataPlaneManager instance. * * @return the @ref FeaDataPlaneManager instance. */ FeaDataPlaneManager& fea_data_plane_manager() { return _fea_data_plane_manager; } /** * Test whether this instance is running. * * @return true if the instance is running, otherwise false. */ virtual bool is_running() const { return _is_running; } /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg) = 0; /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg) = 0; /** * Receive data from the underlying system. * * @param buffer the buffer with the received data. */ virtual void receive_data(const vector& buffer) = 0; protected: // Misc other state bool _is_running; private: IfConfig& _ifconfig; FeaDataPlaneManager& _fea_data_plane_manager; }; #endif // __FEA_IFCONFIG_OBSERVER_HH__ xorp/fea/fte.hh0000664000076400007640000001571211540224223013513 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/fte.hh,v 1.26 2008/10/02 21:56:47 bms Exp $ #ifndef __FEA_FTE_HH__ #define __FEA_FTE_HH__ #include "libxorp/ipv4.hh" #include "libxorp/ipv6.hh" #include "libxorp/ipvx.hh" #include "libxorp/ipv4net.hh" #include "libxorp/ipv6net.hh" #include "libxorp/ipvxnet.hh" /** * @short Forwarding Table Entry. * * Representation of a forwarding table entry. */ template class Fte { public: Fte() { zero(); } explicit Fte(int family) : _net(family), _nexthop(family) { zero(); } Fte(const N& net, const A& nexthop, const string& ifname, const string& vifname, uint32_t metric, uint32_t admin_distance, bool xorp_route) : _net(net), _nexthop(nexthop), _ifname(ifname), _vifname(vifname), _metric(metric), _admin_distance(admin_distance), _xorp_route(xorp_route), _is_deleted(false), _is_unresolved(false), _is_connected_route(false) {} Fte(const N& net) : _net(net), _nexthop(A::ZERO(net.af())), _metric(0), _admin_distance(0), _xorp_route(false), _is_deleted(false), _is_unresolved(false), _is_connected_route(false) {} const N& net() const { return _net; } const A& nexthop() const { return _nexthop; } const string& ifname() const { return _ifname; } const string& vifname() const { return _vifname; } uint32_t metric() const { return _metric; } uint32_t admin_distance() const { return _admin_distance; } bool xorp_route() const { return _xorp_route; } bool is_deleted() const { return _is_deleted; } void mark_deleted() { _is_deleted = true; } bool is_unresolved() const { return _is_unresolved; } void mark_unresolved() { _is_unresolved = true; } bool is_connected_route() const { return _is_connected_route; } void mark_connected_route() { _is_connected_route = true; } /** * Reset all members. */ void zero() { _net = N(A::ZERO(_net.af()), 0); _nexthop = A::ZERO(_nexthop.af()); _ifname.erase(); _vifname.erase(); _metric = 0; _admin_distance = 0; _xorp_route = false; _is_deleted = false; _is_unresolved = false; _is_connected_route = false; } /** * @return true if this is a host route. */ bool is_host_route() const { return _net.prefix_len() == _net.masked_addr().addr_bitlen(); } /** * @return a string representation of the entry. */ string str() const { return c_format("net = %s nexthop = %s ifname = %s vifname = %s " "metric = %u admin_distance = %u xorp_route = %s " "is_deleted = %s is_unresolved = %s " "is_connected_route = %s", _net.str().c_str(), _nexthop.str().c_str(), _ifname.c_str(), _vifname.c_str(), XORP_UINT_CAST(_metric), XORP_UINT_CAST(_admin_distance), bool_c_str(_xorp_route), bool_c_str(_is_deleted), bool_c_str(_is_unresolved), bool_c_str(_is_connected_route)); } private: N _net; // Network A _nexthop; // Nexthop address string _ifname; // Interface name string _vifname; // Virtual interface name uint32_t _metric; // Route metric uint32_t _admin_distance; // Route admin distance bool _xorp_route; // This route was installed by XORP bool _is_deleted; // True if the entry was deleted bool _is_unresolved; // True if the entry is actually // a notification of an unresolved // route to the destination. bool _is_connected_route; // True if this is a route for // directly-connected subnet. }; typedef Fte Fte4; typedef Fte Fte6; typedef Fte BaseFteX; class FteX : public BaseFteX { public: /** * Constructor. * * @param net the network address. * @param nexthop the next-hop router address. * @param ifname the interface name. * @param vifname the virtual interface name. * @param metric the route metric. * @param admin_distance the route admin distance. * @param xorp_route true if this is a XORP route. */ FteX(const IPvXNet& net, const IPvX& nexthop, const string& ifname, const string& vifname, uint32_t metric, uint32_t admin_distance, bool xorp_route) : BaseFteX(net, nexthop, ifname, vifname, metric, admin_distance, xorp_route) {} /** * Constructor for a specified address family. */ explicit FteX(int family) : BaseFteX(family) {} #ifdef XORP_USE_USTL FteX() : BaseFteX(AF_INET) {} #endif /** * Copy constructor for Fte4 entry. */ FteX(const Fte4& fte4) : BaseFteX(IPvXNet(fte4.net()), IPvX(fte4.nexthop()), fte4.ifname(), fte4.vifname(), fte4.metric(), fte4.admin_distance(), fte4.xorp_route()) { if (fte4.is_deleted()) mark_deleted(); if (fte4.is_unresolved()) mark_unresolved(); if (fte4.is_connected_route()) mark_connected_route(); } /** * Copy constructor for Fte6 entry. */ FteX(const Fte6& fte6) : BaseFteX(IPvXNet(fte6.net()), IPvX(fte6.nexthop()), fte6.ifname(), fte6.vifname(), fte6.metric(), fte6.admin_distance(), fte6.xorp_route()) { if (fte6.is_deleted()) mark_deleted(); if (fte6.is_unresolved()) mark_unresolved(); if (fte6.is_connected_route()) mark_connected_route(); } /** * Get an Fte4 entry. * * @return the corresponding Fte4 entry. */ Fte4 get_fte4() const throw (InvalidCast) { Fte4 fte4(net().get_ipv4net(), nexthop().get_ipv4(), ifname(), vifname(), metric(), admin_distance(), xorp_route()); if (is_deleted()) fte4.mark_deleted(); if (is_unresolved()) fte4.mark_unresolved(); if (is_connected_route()) fte4.mark_connected_route(); return fte4; } /** * Get an Fte6 entry. * * @return the corresponding Fte6 entry. */ Fte6 get_fte6() const throw (InvalidCast) { Fte6 fte6(net().get_ipv6net(), nexthop().get_ipv6(), ifname(), vifname(), metric(), admin_distance(), xorp_route()); if (is_deleted()) fte6.mark_deleted(); if (is_unresolved()) fte6.mark_unresolved(); if (is_connected_route()) fte6.mark_connected_route(); return fte6; } }; #endif // __FEA_FTE_HH__ xorp/fea/nexthop_port_mapper.hh0000664000076400007640000001663011540224224017033 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/nexthop_port_mapper.hh,v 1.15 2008/10/02 21:56:50 bms Exp $ #ifndef __FEA_NEXTHOP_PORT_MAPPER_HH__ #define __FEA_NEXTHOP_PORT_MAPPER_HH__ #include "libxorp/ipv4.hh" #include "libxorp/ipv6.hh" #include "libxorp/ipv4net.hh" #include "libxorp/ipv6net.hh" class NexthopPortMapperObserver; /** * @short A class for to keep the mapping between next-hop information * and a port number. * * The next-hop information can be one of the following: network interface, * IP host address (local or peer address on point-to-point links), or * IP subnet address (of the directly connected subnet for an interface). * * Note that observers (@ref NexthopPortMapperObserver) can be attached * to monitor changes to the port mapping. The observers notification * is triggered by an explicit call to @ref NexthopPortMapper#notify_observers. */ class NexthopPortMapper { public: NexthopPortMapper(); ~NexthopPortMapper(); /** * Clear all mapping. */ void clear(); /** * Add an observer for observing changes to the mapper. * * @param observer the observer to add. * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_observer(NexthopPortMapperObserver* observer); /** * Delete an observer for observing changes to the mapper. * * @param observer the observer to delete. * @return XORP_OK on success, otherwise XORP_ERROR. */ int delete_observer(NexthopPortMapperObserver* observer); /** * Notify observers about any port mapping changes. */ void notify_observers(); /** * Lookup a next-hop interface/vif name to obtain the corresponding * port number. * * @param ifname the next-hop interface name to lookup. * @param vifname the next-hop vif name to lookup. * @return the port number on success, otherwise -1. */ int lookup_nexthop_interface(const string& ifname, const string& vifname) const; /** * Lookup a next-hop IPv4 address to obtain the corresponding port number. * * @param ipv4 the next-hop address to lookup. * @return the port number on success, otherwise -1. */ int lookup_nexthop_ipv4(const IPv4& ipv4) const; /** * Lookup a next-hop IPv6 address to obtain the corresponding port number. * * @param ipv6 the next-hop address to lookup. * @return the port number on success, otherwise -1. */ int lookup_nexthop_ipv6(const IPv6& ipv6) const; /** * Add an entry for an interface/vif name to port mapping. * * If the entry already exists, then the port will be updated. * * @param ifname the interface name to add. * @param vifname the vif name to add. * @param port the port number to add. * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_interface(const string& ifname, const string& vifname, int port); /** * Delete an entry for an interface/vif name to port mapping. * * @param ifname the interface name to delete. * @param vifname the vif name to delete. * @return XORP_OK on success, otherwise XORP_ERROR. */ int delete_interface(const string& ifname, const string& vifname); /** * Add an entry for an IPv4 address to port mapping. * * If the entry already exists, then the port will be updated. * * @param ipv4 the address to add. * @param port the port number to add. * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_ipv4(const IPv4& ipv4, int port); /** * Delete an entry for an IPv4 address to port mapping. * * @param ipv4 the address to delete. * @return XORP_OK on success, otherwise XORP_ERROR. */ int delete_ipv4(const IPv4& ipv4); /** * Add an entry for an IPv6 address to port mapping. * * If the entry already exists, then the port will be updated. * * @param ipv6 the address to add. * @param port the port number to add. * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_ipv6(const IPv6& ipv6, int port); /** * Delete an entry for an IPv6 address to port mapping. * * @param ipv6 the address to delete. * @return XORP_OK on success, otherwise XORP_ERROR. */ int delete_ipv6(const IPv6& ipv6); /** * Add an entry for an IPv4 subnet to port mapping. * * If the entry already exists, then the port will be updated. * * @param ipv4net the subnet to add. * @param port the port number to add. * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_ipv4net(const IPv4Net& ipv4net, int port); /** * Delete an entry for an IPv4 subnet to port mapping. * * @param ipv4net the subnet to delete. * @return XORP_OK on success, otherwise XORP_ERROR. */ int delete_ipv4net(const IPv4Net& ipv4net); /** * Add an entry for an IPv6 subnet to port mapping. * * If the entry already exists, then the port will be updated. * * @param ipv6net the subnet to add. * @param port the port number to add. * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_ipv6net(const IPv6Net& ipv6net, int port); /** * Delete an entry for an IPv6 subnet to port mapping. * * @param ipv6net the subnet to delete. * @return XORP_OK on success, otherwise XORP_ERROR. */ int delete_ipv6net(const IPv6Net& ipv6net); private: bool is_mapping_changed() const; // The maps with the lookup info map, int> _interface_map; map _ipv4_map; map _ipv6_map; map _ipv4net_map; map _ipv6net_map; // The maps with the old info map, int> _old_interface_map; map _old_ipv4_map; map _old_ipv6_map; map _old_ipv4net_map; map _old_ipv6net_map; list _observers; }; /** * @short A class for observing changes to a @ref NetlinkPortMapper object. */ class NexthopPortMapperObserver { public: /** * Empty virtual destructor */ virtual ~NexthopPortMapperObserver() {} /** * The next-hop port mapping has changed. * * @param is_mapping_changed if true, the port mapping has changed, * otherwise it remains same (i.e., it has been only "touched"). */ virtual void nexthop_port_mapper_event(bool is_mapping_changed) = 0; }; #endif // __FEA_NEXTHOP_PORT_MAPPER_HH__ xorp/fea/fibconfig_entry_get.hh0000664000076400007640000001021411540225524016740 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/fibconfig_entry_get.hh,v 1.17 2008/10/02 21:56:45 bms Exp $ #ifndef __FEA_FIBCONFIG_ENTRY_GET_HH__ #define __FEA_FIBCONFIG_ENTRY_GET_HH__ #include "fte.hh" #include "iftree.hh" #include "fea_data_plane_manager.hh" class FibConfig; class FibConfigEntryGet { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ FibConfigEntryGet(FeaDataPlaneManager& fea_data_plane_manager) : _is_running(false), _fibconfig(fea_data_plane_manager.fibconfig()), _fea_data_plane_manager(fea_data_plane_manager) {} /** * Virtual destructor. */ virtual ~FibConfigEntryGet() {} /** * Get the @ref FibConfig instance. * * @return the @ref FibConfig instance. */ FibConfig& fibconfig() { return _fibconfig; } /** * Get the @ref FeaDataPlaneManager instance. * * @return the @ref FeaDataPlaneManager instance. */ FeaDataPlaneManager& fea_data_plane_manager() { return _fea_data_plane_manager; } /** * Test whether this instance is running. * * @return true if the instance is running, otherwise false. */ virtual bool is_running() const { return _is_running; } /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg) = 0; /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg) = 0; /** * Lookup an IPv4 route by destination address. * * @param dst host address to resolve. * @param fte return-by-reference forwarding table entry. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int lookup_route_by_dest4(const IPv4& dst, Fte4& fte) = 0; /** * Lookup an IPv4 route by network address. * * @param dst network address to resolve. * @param fte return-by-reference forwarding table entry. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int lookup_route_by_network4(const IPv4Net& dst, Fte4& fte) = 0; /** * Lookup an IPv6 route by destination address. * * @param dst host address to resolve. * @param fte return-by-reference forwarding table entry. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int lookup_route_by_dest6(const IPv6& dst, Fte6& fte) = 0; /** * Lookup an IPv6 route by network address. * * @param dst network address to resolve. * @param fte return-by-reference forwarding table entry. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int lookup_route_by_network6(const IPv6Net& dst, Fte6& fte) = 0; /** Routing table ID that we are interested in might have changed. Maybe something * can filter on this for increased efficiency. */ virtual int notify_table_id_change(uint32_t new_tbl) = 0; protected: // Misc other state bool _is_running; private: FibConfig& _fibconfig; FeaDataPlaneManager& _fea_data_plane_manager; }; #endif // __FEA_FIBCONFIG_ENTRY_GET_HH__ xorp/fea/fea_module.h0000664000076400007640000000230011421137511014653 0ustar greearbgreearb/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- */ /* * Copyright (c) 2001-2009 XORP, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 2, June * 1991 as published by the Free Software Foundation. Redistribution * and/or modification of this program under the terms of any other * version of the GNU General Public License is not permitted. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, * see the GNU General Public License, Version 2, a copy of which can be * found in the XORP LICENSE.gpl file. * * XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; * http://xorp.net */ /* * $XORP: xorp/fea/fea_module.h,v 1.11 2008/10/02 21:56:45 bms Exp $ */ /* * Module definitions. */ #ifndef __FEA_FEA_MODULE_H__ #define __FEA_FEA_MODULE_H__ #ifndef XORP_MODULE_NAME #define XORP_MODULE_NAME "FEA" #endif #ifndef XORP_MODULE_VERSION #define XORP_MODULE_VERSION "0.1" #endif #endif /* __FEA_FEA_MODULE_H__ */ xorp/fea/fibconfig_table_set.hh0000664000076400007640000001377411540225525016721 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/fibconfig_table_set.hh,v 1.16 2008/10/02 21:56:46 bms Exp $ #ifndef __FEA_FIBCONFIG_TABLE_SET_HH__ #define __FEA_FIBCONFIG_TABLE_SET_HH__ #include "fte.hh" #include "iftree.hh" #include "fea_data_plane_manager.hh" class FibConfig; class FibConfigTableSet { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ FibConfigTableSet(FeaDataPlaneManager& fea_data_plane_manager) : _is_running(false), _fibconfig(fea_data_plane_manager.fibconfig()), _fea_data_plane_manager(fea_data_plane_manager), _in_configuration(false) {} /** * Virtual destructor. */ virtual ~FibConfigTableSet() {} /** * Get the @ref FibConfig instance. * * @return the @ref FibConfig instance. */ FibConfig& fibconfig() { return _fibconfig; } /** * Get the @ref FeaDataPlaneManager instance. * * @return the @ref FeaDataPlaneManager instance. */ FeaDataPlaneManager& fea_data_plane_manager() { return _fea_data_plane_manager; } /** * Test whether this instance is running. * * @return true if the instance is running, otherwise false. */ virtual bool is_running() const { return _is_running; } /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg) = 0; /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg) = 0; /** * Start a configuration interval. * * All modifications to must be within a marked "configuration" interval. * * This method provides derived classes with a mechanism to perform * any actions necessary before forwarding table modifications can * be made. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start_configuration(string& error_msg) { // Nothing particular to do, just label start. return mark_configuration_start(error_msg); } /** * End of configuration interval. * * This method provides derived classes with a mechanism to * perform any actions necessary at the end of a configuration, eg * write a file. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int end_configuration(string& error_msg) { // Nothing particular to do, just label start. return mark_configuration_end(error_msg); } /** * Set the IPv4 unicast forwarding table. * * @param fte_list the list with all entries to install into * the IPv4 unicast forwarding table. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int set_table4(const list& fte_list) = 0; /** * Delete all entries in the IPv4 unicast forwarding table. * * Must be within a configuration interval. * * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int delete_all_entries4() = 0; /** * Set the IPv6 unicast forwarding table. * * @param fte_list the list with all entries to install into * the IPv6 unicast forwarding table. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int set_table6(const list& fte_list) = 0; /** * Delete all entries in the IPv6 unicast forwarding table. * * Must be within a configuration interval. * * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int delete_all_entries6() = 0; /** Routing table ID that we are interested in might have changed. Maybe something * can filter on this for increased efficiency. */ virtual int notify_table_id_change(uint32_t new_tbl) = 0; protected: /** * Mark start of a configuration. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int mark_configuration_start(string& error_msg) { if (_in_configuration != true) { _in_configuration = true; return (XORP_OK); } error_msg = c_format("Cannot start configuration: " "configuration in progress"); return (XORP_ERROR); } /** * Mark end of a configuration. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int mark_configuration_end(string& error_msg) { if (_in_configuration != false) { _in_configuration = false; return (XORP_OK); } error_msg = c_format("Cannot end configuration: " "configuration not in progress"); return (XORP_ERROR); } bool in_configuration() const { return _in_configuration; } // Misc other state bool _is_running; private: FibConfig& _fibconfig; FeaDataPlaneManager& _fea_data_plane_manager; bool _in_configuration; }; #endif // __FEA_FIBCONFIG_TABLE_SET_HH__ xorp/fea/mfea_node_cli.cc0000664000076400007640000003054611421137511015472 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // MFEA (Multicast Forwarding Engine Abstraction) CLI implementation // #include "mfea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvx.hh" #include "mfea_node.hh" #include "mfea_node_cli.hh" #include "mfea_vif.hh" // // Exported variables // // // Local constants definitions // // // Local structures/classes, typedefs and macros // // // Local variables // // // Local functions prototypes // /** * MfeaNodeCli::MfeaNodeCli: * @mfea_node: The MFEA node to use. * * MfeaNodeCli constructor. **/ MfeaNodeCli::MfeaNodeCli(MfeaNode& mfea_node) : ProtoNodeCli(mfea_node.family(), mfea_node.module_id()), _mfea_node(mfea_node) { } MfeaNodeCli::~MfeaNodeCli() { stop(); } int MfeaNodeCli::start() { if (! is_enabled()) return (XORP_OK); if (is_up() || is_pending_up()) return (XORP_OK); if (ProtoUnit::start() != XORP_OK) return (XORP_ERROR); if (add_all_cli_commands() != XORP_OK) return (XORP_ERROR); XLOG_INFO("CLI started"); return (XORP_OK); } int MfeaNodeCli::stop() { int ret_code = XORP_OK; if (is_down()) return (XORP_OK); if (ProtoUnit::stop() != XORP_OK) return (XORP_ERROR); if (delete_all_cli_commands() != XORP_OK) ret_code = XORP_ERROR; XLOG_INFO("CLI stopped"); return (ret_code); } /** * Enable the node operation. * * If an unit is not enabled, it cannot be start, or pending-start. */ void MfeaNodeCli::enable() { ProtoUnit::enable(); XLOG_INFO("CLI 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 MfeaNodeCli::disable() { stop(); ProtoUnit::disable(); XLOG_INFO("CLI disabled"); } int MfeaNodeCli::add_all_cli_commands() { // XXX: command "show" must have been installed by the CLI itself. if (mfea_node().is_ipv4()) { add_cli_dir_command("show mfea", "Display information about IPv4 MFEA"); add_cli_command("show mfea dataflow", "Display information about MFEA IPv4 dataflow filters", callback(this, &MfeaNodeCli::cli_show_mfea_dataflow)); add_cli_command("show mfea interface", "Display information about MFEA IPv4 interfaces", callback(this, &MfeaNodeCli::cli_show_mfea_interface)); add_cli_command("show mfea interface address", "Display information about addresses of MFEA IPv4 interfaces", callback(this, &MfeaNodeCli::cli_show_mfea_interface_address)); } if (mfea_node().is_ipv6()) { add_cli_dir_command("show mfea6", "Display information about IPv6 MFEA"); add_cli_command("show mfea6 dataflow", "Display information about MFEA IPv6 dataflow filters", callback(this, &MfeaNodeCli::cli_show_mfea_dataflow)); add_cli_command("show mfea6 interface", "Display information about MFEA IPv6 interfaces", callback(this, &MfeaNodeCli::cli_show_mfea_interface)); add_cli_command("show mfea6 interface address", "Display information about addresses of MFEA IPv6 interfaces", callback(this, &MfeaNodeCli::cli_show_mfea_interface_address)); } return (XORP_OK); } // // CLI COMMAND: "show mfea dataflow [group | group-range]" // CLI COMMAND: "show mfea6 dataflow [group | group-range]" // // Display information about the dataflow filters the MFEA knows about. // int MfeaNodeCli::cli_show_mfea_dataflow(const vector& argv) { IPvXNet group_range = IPvXNet::ip_multicast_base_prefix(family()); // Check the optional argument if (argv.size()) { try { group_range = IPvXNet(argv[0].c_str()); } catch (InvalidString) { try { group_range = IPvXNet(IPvX(argv[0].c_str()), IPvX::addr_bitlen(family())); } catch (InvalidString) { cli_print(c_format("ERROR: Invalid group range address: %s\n", argv[0].c_str())); return (XORP_ERROR); } catch (InvalidNetmaskLength) { XLOG_UNREACHABLE(); return (XORP_ERROR); } } catch (InvalidNetmaskLength) { cli_print(c_format("ERROR: Invalid group range netmask length: %s\n", argv[0].c_str())); return (XORP_ERROR); } if (! group_range.is_multicast()) { cli_print(c_format("ERROR: Group range is not multicast: %s\n", cstring(group_range))); return (XORP_ERROR); } } cli_print(c_format("%-39s %-39s\n", "Group", "Source")); // // The MfeaDfe entries // MfeaDft::const_gs_iterator iter_dft, iter_begin_dft, iter_end_dft; iter_begin_dft = mfea_node().mfea_dft().group_by_prefix_begin(group_range); iter_end_dft = mfea_node().mfea_dft().group_by_prefix_end(group_range); TimeVal now; mfea_node().eventloop().current_time(now); for (iter_dft = iter_begin_dft; iter_dft != iter_end_dft; ++iter_dft) { MfeaDfeLookup *mfea_dfe_lookup = iter_dft->second; list::const_iterator iter; cli_print(c_format("%-39s %-39s\n", cstring(mfea_dfe_lookup->group_addr()), cstring(mfea_dfe_lookup->source_addr()))); cli_print(c_format(" %-29s %-4s %-30s %-6s\n", "Measured(Start|Packets|Bytes)", "Type", "Thresh(Interval|Packets|Bytes)", "Remain")); for (iter = mfea_dfe_lookup->mfea_dfe_list().begin(); iter != mfea_dfe_lookup->mfea_dfe_list().end(); ++iter) { string s1, s2; string measured_s, type_s, thresh_s, remain_s; MfeaDfe *mfea_dfe = *iter; TimeVal start_time, threshold_interval, end, delta; start_time = mfea_dfe->start_time(); threshold_interval = mfea_dfe->threshold_interval(); // The measured values if (mfea_dfe->is_threshold_in_packets()) { s1 = c_format("%u", XORP_UINT_CAST(mfea_dfe->measured_packets())); } else { s1 = "?"; } if (mfea_dfe->is_threshold_in_bytes()) { s2 = c_format("%u", XORP_UINT_CAST(mfea_dfe->measured_bytes())); } else { s2 = "?"; } measured_s = c_format("%u.%u|%s|%s", XORP_UINT_CAST(start_time.sec()), XORP_UINT_CAST(start_time.usec()), s1.c_str(), s2.c_str()); // The entry type type_s = c_format("%-3s", mfea_dfe->is_geq_upcall()? ">=" : mfea_dfe->is_leq_upcall()? "<=" : "?"); // The threshold values if (mfea_dfe->is_threshold_in_packets()) s1 = c_format("%u", XORP_UINT_CAST(mfea_dfe->threshold_packets())); else s1 = "?"; if (mfea_dfe->is_threshold_in_bytes()) s2 = c_format("%u", XORP_UINT_CAST(mfea_dfe->threshold_bytes())); else s2 = "?"; thresh_s = c_format("%u.%u|%s|%s", XORP_UINT_CAST(threshold_interval.sec()), XORP_UINT_CAST(threshold_interval.usec()), s1.c_str(), s2.c_str()); // Remaining time end = start_time + threshold_interval; if (now <= end) { delta = end - now; remain_s = c_format("%u.%u", XORP_UINT_CAST(delta.sec()), XORP_UINT_CAST(delta.usec())); } else { // Negative time delta = now - end; remain_s = c_format("-%u.%u", XORP_UINT_CAST(delta.sec()), XORP_UINT_CAST(delta.usec())); } cli_print(c_format(" %-29s %-6s %-30s %-6s\n", measured_s.c_str(), type_s.c_str(), thresh_s.c_str(), remain_s.c_str())); } } return (XORP_OK); } // // CLI COMMAND: "show mfea interface [interface-name]" // CLI COMMAND: "show mfea6 interface [interface-name]" // // Display information about the interfaces the MFEA knows about. // int MfeaNodeCli::cli_show_mfea_interface(const vector& argv) { string interface_name; // Check the optional argument if (argv.size()) { interface_name = argv[0]; if (mfea_node().vif_find_by_name(interface_name) == NULL) { cli_print(c_format("ERROR: Invalid interface name: %s\n", interface_name.c_str())); return (XORP_ERROR); } } cli_print(c_format("%-12s %-8s %12s %-15s %-1s\n", "Interface", "State", "Vif/PifIndex", "Addr", "Flags")); for (uint32_t i = 0; i < mfea_node().maxvifs(); i++) { MfeaVif *mfea_vif = mfea_node().vif_find_by_vif_index(i); if (mfea_vif == NULL) continue; // Test if we should print this entry bool do_print = true; if (interface_name.size()) { do_print = false; if (mfea_vif->name() == interface_name) { do_print = true; } } if (! do_print) continue; // // Create a string with the interface flags // string vif_flags = ""; if (mfea_vif->is_pim_register()) { if (vif_flags.size()) vif_flags += " "; vif_flags += "PIM_REGISTER"; } if (mfea_vif->is_p2p()) { if (vif_flags.size()) vif_flags += " "; vif_flags += "P2P"; } if (mfea_vif->is_loopback()) { if (vif_flags.size()) vif_flags += " "; vif_flags += "LOOPBACK"; } if (mfea_vif->is_multicast_capable()) { if (vif_flags.size()) vif_flags += " "; vif_flags += "MULTICAST"; } if (mfea_vif->is_broadcast_capable()) { if (vif_flags.size()) vif_flags += " "; vif_flags += "BROADCAST"; } if (mfea_vif->is_underlying_vif_up()) { if (vif_flags.size()) vif_flags += " "; vif_flags += "KERN_UP"; } // // Print the interface // list::const_iterator iter = mfea_vif->addr_list().begin(); string dd = c_format("%d/%d", mfea_vif->vif_index(), mfea_vif->pif_index()); cli_print(c_format("%-12s %-8s %12s %-15s %-1s\n", mfea_vif->name().c_str(), mfea_vif->state_str().c_str(), dd.c_str(), (iter != mfea_vif->addr_list().end())? cstring((*iter).addr()): "", vif_flags.c_str())); } return (XORP_OK); } // // CLI COMMAND: "show mfea interface address [interface-name]" // CLI COMMAND: "show mfea6 interface address [interface-name]" // // Display information about the addresses of interfaces the MFEA knows about. // int MfeaNodeCli::cli_show_mfea_interface_address(const vector& argv) { string interface_name; // Check the optional argument if (argv.size()) { interface_name = argv[0]; if (mfea_node().vif_find_by_name(interface_name) == NULL) { cli_print(c_format("ERROR: Invalid interface name: %s\n", interface_name.c_str())); return (XORP_ERROR); } } cli_print(c_format("%-12s %-15s %-18s %-15s %-15s\n", "Interface", "Addr", "Subnet", "Broadcast", "P2Paddr")); for (uint32_t i = 0; i < mfea_node().maxvifs(); i++) { MfeaVif *mfea_vif = mfea_node().vif_find_by_vif_index(i); if (mfea_vif == NULL) continue; // Test if we should print this entry bool do_print = true; if (interface_name.size()) { do_print = false; if (mfea_vif->name() == interface_name) { do_print = true; } } if (! do_print) continue; // // Print the first address // list::const_iterator iter = mfea_vif->addr_list().begin(); cli_print(c_format("%-12s %-15s %-18s %-15s %-15s\n", mfea_vif->name().c_str(), (iter != mfea_vif->addr_list().end())? cstring((*iter).addr()): "", (iter != mfea_vif->addr_list().end())? cstring((*iter).subnet_addr()): "", (iter != mfea_vif->addr_list().end())? cstring((*iter).broadcast_addr()): "", (iter != mfea_vif->addr_list().end())? cstring((*iter).peer_addr()): "")); // // Print the rest of the addresses // if (iter != mfea_vif->addr_list().end()) ++iter; for ( ; iter != mfea_vif->addr_list().end(); ++iter) { cli_print(c_format("%-12s %-15s %-18s %-15s %-15s\n", " ", cstring((*iter).addr()), cstring((*iter).subnet_addr()), cstring((*iter).broadcast_addr()), cstring((*iter).peer_addr()))); } } return (XORP_OK); } xorp/fea/xrl_fea_io.cc0000664000076400007640000000744011540225525015037 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2007-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // FEA (Forwarding Engine Abstraction) XRL-based I/O implementation. // #include "fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxipc/xrl_router.hh" #include "xrl/interfaces/finder_event_notifier_xif.hh" #include "xrl_fea_io.hh" XrlFeaIo::XrlFeaIo(EventLoop& eventloop, XrlRouter& xrl_router, const string& xrl_finder_targetname) : FeaIo(eventloop), _xrl_router(xrl_router), _xrl_finder_targetname(xrl_finder_targetname) { } XrlFeaIo::~XrlFeaIo() { shutdown(); } int XrlFeaIo::startup() { return (FeaIo::startup()); } int XrlFeaIo::shutdown() { return (FeaIo::shutdown()); } bool XrlFeaIo::is_running() const { return (FeaIo::is_running()); } int XrlFeaIo::register_instance_event_interest(const string& instance_name, string& error_msg) { XrlFinderEventNotifierV0p1Client client(&_xrl_router); bool success; success = client.send_register_instance_event_interest( _xrl_finder_targetname.c_str(), _xrl_router.instance_name(), instance_name, callback(this, &XrlFeaIo::register_instance_event_interest_cb, instance_name)); if (success != true) { error_msg = c_format("Failed to register event interest in " "instance %s: could not transmit the request", instance_name.c_str()); // XXX: If an error, then assume the target is dead instance_death(instance_name); return (XORP_ERROR); } return (XORP_OK); } int XrlFeaIo::deregister_instance_event_interest(const string& instance_name, string& error_msg) { XrlFinderEventNotifierV0p1Client client(&_xrl_router); bool success; success = client.send_deregister_instance_event_interest( _xrl_finder_targetname.c_str(), _xrl_router.instance_name(), instance_name, callback(this, &XrlFeaIo::deregister_instance_event_interest_cb, instance_name)); if (success != true) { error_msg = c_format("Failed to deregister event interest in " "instance %s: could not transmit the request", instance_name.c_str()); // // XXX: If we are deregistering, then we don't care whether the // target is dead. // return (XORP_ERROR); } return (XORP_OK); } void XrlFeaIo::register_instance_event_interest_cb(const XrlError& xrl_error, string instance_name) { if (xrl_error != XrlError::OKAY()) { XLOG_ERROR("Failed to register event interest in instance %s: %s", instance_name.c_str(), xrl_error.str().c_str()); // XXX: If an error, then assume the target is dead instance_death(instance_name); } } void XrlFeaIo::deregister_instance_event_interest_cb(const XrlError& xrl_error, string instance_name) { UNUSED(instance_name); if (xrl_error != XrlError::OKAY()) { XLOG_ERROR("Failed to deregister event interest in instance %s: %s", instance_name.c_str(), xrl_error.str().c_str()); // // XXX: If we are deregistering, then we don't care whether the // target is dead. // } } xorp/fea/firewall_entry.cc0000664000076400007640000000343511421137511015751 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2008-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "firewall_entry.hh" static struct { FirewallEntry::Action action; string name; } firewall_action_table[FirewallEntry::ACTION_MAX] = { { FirewallEntry::ACTION_ANY, "any" }, // XXX: not used { FirewallEntry::ACTION_NONE, "none" }, { FirewallEntry::ACTION_PASS, "pass" }, { FirewallEntry::ACTION_DROP, "drop" }, { FirewallEntry::ACTION_REJECT, "reject" } }; string FirewallEntry::action2str(FirewallEntry::Action action) { if ((action < ACTION_MIN) || (action >= ACTION_MAX)) return (string("InvalidAction")); return (firewall_action_table[action].name); } FirewallEntry::Action FirewallEntry::str2action(const string& name) { size_t i; for (i = ACTION_MIN; i < ACTION_MAX; i++) { if (firewall_action_table[i].name == name) return (firewall_action_table[i].action); } return (ACTION_INVALID); } xorp/fea/fibconfig_entry_observer.hh0000664000076400007640000000623311540225525020017 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/fibconfig_entry_observer.hh,v 1.12 2008/10/02 21:56:45 bms Exp $ #ifndef __FEA_FIBCONFIG_ENTRY_OBSERVER_HH__ #define __FEA_FIBCONFIG_ENTRY_OBSERVER_HH__ #include "fte.hh" #include "iftree.hh" #include "fea_data_plane_manager.hh" class FibConfig; class FibConfigEntryObserver { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ FibConfigEntryObserver(FeaDataPlaneManager& fea_data_plane_manager) : _is_running(false), _fibconfig(fea_data_plane_manager.fibconfig()), _fea_data_plane_manager(fea_data_plane_manager) {} /** * Virtual destructor. */ virtual ~FibConfigEntryObserver() {} /** * Get the @ref FibConfig instance. * * @return the @ref FibConfig instance. */ FibConfig& fibconfig() { return _fibconfig; } /** * Get the @ref FeaDataPlaneManager instance. * * @return the @ref FeaDataPlaneManager instance. */ FeaDataPlaneManager& fea_data_plane_manager() { return _fea_data_plane_manager; } /** * Test whether this instance is running. * * @return true if the instance is running, otherwise false. */ virtual bool is_running() const { return _is_running; } /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg) = 0; /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg) = 0; /** * Receive data from the underlying system. * * @param buffer the buffer with the received data. */ virtual void receive_data(const vector& buffer) = 0; /** Routing table ID that we are interested in might have changed. Maybe something * can filter on this for increased efficiency. */ virtual int notify_table_id_change(uint32_t new_tbl) = 0; protected: // Misc other state bool _is_running; private: FibConfig& _fibconfig; FeaDataPlaneManager& _fea_data_plane_manager; }; #endif // __FEA_FIBCONFIG_ENTRY_OBSERVER_HH__ xorp/fea/fea_io.cc0000664000076400007640000001073211421137511014143 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2007-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // FEA (Forwarding Engine Abstraction) I/O interface. // #include "fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/eventloop.hh" #include "fea_io.hh" FeaIo::FeaIo(EventLoop& eventloop) : _eventloop(eventloop), _is_running(false) { } FeaIo::~FeaIo() { shutdown(); } int FeaIo::startup() { _is_running = true; return (XORP_OK); } int FeaIo::shutdown() { _is_running = false; return (XORP_OK); } bool FeaIo::is_running() const { return (_is_running); } int FeaIo::add_instance_watch(const string& instance_name, InstanceWatcher* instance_watcher, string& error_msg) { list >::iterator iter; bool is_watched = false; for (iter = _instance_watchers.begin(); iter != _instance_watchers.end(); ++iter) { const string& name = iter->first; InstanceWatcher* watcher = iter->second; if (name != instance_name) continue; if (watcher == instance_watcher) return (XORP_OK); // Exact match found // The instance is watched by somebody else is_watched = true; } // Add the new watcher _instance_watchers.push_back(make_pair(instance_name, instance_watcher)); if (is_watched) return (XORP_OK); // Somebody else registered for the instance if (register_instance_event_interest(instance_name, error_msg) != XORP_OK) { _instance_watchers.pop_back(); return (XORP_ERROR); } return (XORP_OK); } int FeaIo::delete_instance_watch(const string& instance_name, InstanceWatcher* instance_watcher, string& error_msg) { list >::iterator iter, delete_iter; bool is_watched = false; delete_iter = _instance_watchers.end(); for (iter = _instance_watchers.begin(); iter != _instance_watchers.end(); ++iter) { const string& name = iter->first; InstanceWatcher* watcher = iter->second; if (name != instance_name) continue; if (watcher == instance_watcher) { delete_iter = iter; // Exact match found continue; } // The instance is watched by somebody else is_watched = true; } if (delete_iter == _instance_watchers.end()) { // Entry not found error_msg = c_format("Instance watcher for %s not found", instance_name.c_str()); return (XORP_ERROR); } // Delete the watcher _instance_watchers.erase(delete_iter); if (is_watched) return (XORP_OK); // Somebody else registered for the instance return (deregister_instance_event_interest(instance_name, error_msg)); } void FeaIo::instance_birth(const string& instance_name) { list >::iterator iter; for (iter = _instance_watchers.begin(); iter != _instance_watchers.end(); ) { const string& name = iter->first; InstanceWatcher* watcher = iter->second; // // XXX: We need to increment the iterator in advance, in case // the watcher that is informed about the change decides to delete // the entry the iterator points to. // ++iter; if (name == instance_name) watcher->instance_birth(instance_name); } } void FeaIo::instance_death(const string& instance_name) { list >::iterator iter; for (iter = _instance_watchers.begin(); iter != _instance_watchers.end(); ) { const string& name = iter->first; InstanceWatcher* watcher = iter->second; // // XXX: We need to increment the iterator in advance, in case // the watcher that is informed about the change decides to delete // the entry the iterator points to. // ++iter; if (name == instance_name) watcher->instance_death(instance_name); } } xorp/fea/mfea_module.h0000664000076400007640000000230511421137511015035 0ustar greearbgreearb/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- */ /* * Copyright (c) 2001-2009 XORP, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 2, June * 1991 as published by the Free Software Foundation. Redistribution * and/or modification of this program under the terms of any other * version of the GNU General Public License is not permitted. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, * see the GNU General Public License, Version 2, a copy of which can be * found in the XORP LICENSE.gpl file. * * XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; * http://xorp.net */ /* * $XORP: xorp/fea/mfea_module.h,v 1.10 2008/10/02 21:56:49 bms Exp $ */ /* * Module definitions. */ #ifndef __FEA_MFEA_MODULE_H__ #define __FEA_MFEA_MODULE_H__ #ifndef XORP_MODULE_NAME #define XORP_MODULE_NAME "MFEA" #endif #ifndef XORP_MODULE_VERSION #define XORP_MODULE_VERSION "0.1" #endif #endif /* __FEA_MFEA_MODULE_H__ */ xorp/fea/ifconfig_transaction.hh0000664000076400007640000005656011540225525017142 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __FEA_IFCONFIG_TRANSACTION_HH__ #define __FEA_IFCONFIG_TRANSACTION_HH__ #include "libxorp/c_format.hh" #include "libxorp/transaction.hh" #include "libxorp/fea_share.hh" #include "ifconfig.hh" #include "iftree.hh" class IfConfigTransactionManager : public TransactionManager { public: /** * Constructor. * * @param eventloop the event loop to use. */ IfConfigTransactionManager(EventLoop& eventloop) : TransactionManager(eventloop, TIMEOUT_MS, MAX_PENDING) { _tid_exec = 0xFFFFFFFF; } /** * Get the string with the first error during commit. * * @return the string with the first error during commit or an empty * string if no error. */ const string& error() const { return _first_error; } protected: /** * Pre-commit method that is called before the first operation * in a commit. * * This is an overriding method. * * @param tid the transaction ID. */ virtual void pre_commit(uint32_t tid); /** * Method that is called after each operation. * * This is an overriding method. * * @param success set to true if the operation succeeded, otherwise false. * @param op the operation that has been just called. */ virtual void operation_result(bool success, const TransactionOperation& op); private: /** * Reset the string with the error. */ void reset_error() { _first_error.erase(); } string _first_error; // The string with the first error uint32_t _tid_exec; // The transaction ID enum { TIMEOUT_MS = 5000, MAX_PENDING = 10 }; }; /** * Base class for Interface related operations acting on an * IfTree. */ class IfConfigTransactionOperation : public TransactionOperation { public: IfConfigTransactionOperation(IfConfig& ifconfig, const string& ifname) : _ifconfig(ifconfig), _iftree(ifconfig.user_config()), // XXX: The iftree to modify _ifname(ifname) {} /** * @return space separated path description. */ virtual string path() const { return _ifname; } /** * Get the interface name this operation applies to. * * @return the interface name this operation applies to. */ const string& ifname() const { return _ifname; } protected: /** * Get the corresponding interface manager. * * @return a reference to the corresponding interface manager. */ IfConfig& ifconfig() { return _ifconfig; } /** * Get the interface tree to modify. * * @return a reference to the interface tree to modify. */ IfTree& iftree() { return _iftree; } private: IfConfig& _ifconfig; IfTree& _iftree; const string _ifname; }; /** * Class for adding an interface. */ class AddInterface : public IfConfigTransactionOperation { public: AddInterface(IfConfig& ifconfig, const string& ifname) : IfConfigTransactionOperation(ifconfig, ifname) {} bool dispatch() { if (ifconfig().add_interface(ifname()) != XORP_OK) return (false); return (true); } string str() const { return string("AddInterface: ") + ifname(); } }; /** * Class for removing an interface. */ class RemoveInterface : public IfConfigTransactionOperation { public: RemoveInterface(IfConfig& ifconfig, const string& ifname) : IfConfigTransactionOperation(ifconfig, ifname) { } bool dispatch(); string str() const { return string("RemoveInterface: ") + ifname(); } }; /** * Class for configuring all interfaces within the FEA by using information * from the underlying system. */ class ConfigureAllInterfacesFromSystem : public IfConfigTransactionOperation { public: ConfigureAllInterfacesFromSystem(IfConfig& ifconfig, bool enable) : IfConfigTransactionOperation(ifconfig, ""), _enable(enable) {} bool dispatch() { // Force a read of ALL interfaces, not just ones we are configured // to care about. ifconfig().full_pulled_config(); if (_enable) { // // Configure all interfaces // const IfTree& dev_config = ifconfig().system_config(); IfTree::IfMap::const_iterator iter; for (iter = dev_config.interfaces().begin(); iter != dev_config.interfaces().end(); ++iter) { const IfTreeInterface& iface = *(iter->second); if (ifconfig().update_interface(iface) != XORP_OK) return (false); } } // // Set the "default_system_config" flag for all interfaces // IfTree::IfMap::iterator iter; for (iter = iftree().interfaces().begin(); iter != iftree().interfaces().end(); ++iter) { IfTreeInterface& iface = *(iter->second); iface.set_default_system_config(_enable); } return (true); } string str() const { return c_format("ConfigureAllInterfacesFromSystem: %s", bool_c_str(_enable)); } private: bool _enable; }; /** * Class for configuring an interface within the FEA by using information * from the underlying system. */ class ConfigureInterfaceFromSystem : public IfConfigTransactionOperation { public: ConfigureInterfaceFromSystem(IfConfig& ifconfig, const string& ifname, bool enable) : IfConfigTransactionOperation(ifconfig, ifname), _enable(enable) {} bool dispatch() { IfTreeInterface* ifp = iftree().find_interface(ifname()); if (ifp == NULL) return (false); ifp->set_default_system_config(_enable); return (true); } string str() const { return c_format("ConfigureInterfaceFromSystem: %s %s", ifname().c_str(), bool_c_str(_enable)); } private: bool _enable; }; /** * Base class for interface modifier operations. */ class InterfaceModifier : public IfConfigTransactionOperation { public: InterfaceModifier(IfConfig& ifconfig, const string& ifname) : IfConfigTransactionOperation(ifconfig, ifname) {} protected: IfTreeInterface* interface() { IfTreeInterface* ifp = iftree().find_interface(ifname()); return (ifp); } }; /** * Class for setting the enabled state of an interface. */ class SetInterfaceEnabled : public InterfaceModifier { public: SetInterfaceEnabled(IfConfig& ifconfig, const string& ifname, bool enabled) : InterfaceModifier(ifconfig, ifname), _enabled(enabled) {} bool dispatch() { IfTreeInterface* fi = interface(); if (fi == NULL) return (false); fi->set_enabled(_enabled); return (true); } string str() const { return c_format("SetInterfaceEnabled: %s %s", ifname().c_str(), bool_c_str(_enabled)); } private: bool _enabled; }; /** * Class for setting the discard state of an interface. */ class SetInterfaceDiscard : public InterfaceModifier { public: SetInterfaceDiscard(IfConfig& ifconfig, const string& ifname, bool discard) : InterfaceModifier(ifconfig, ifname), _discard(discard) {} bool dispatch() { IfTreeInterface* fi = interface(); if (fi == NULL) return (false); fi->set_discard(_discard); return (true); } string str() const { return c_format("SetInterfaceDiscard: %s %s", ifname().c_str(), bool_c_str(_discard)); } private: bool _discard; }; /** * Class for setting the unreachable state of an interface. */ class SetInterfaceUnreachable : public InterfaceModifier { public: SetInterfaceUnreachable(IfConfig& ifconfig, const string& ifname, bool unreachable) : InterfaceModifier(ifconfig, ifname), _unreachable(unreachable) {} bool dispatch() { IfTreeInterface* fi = interface(); if (fi == NULL) return (false); fi->set_unreachable(_unreachable); return (true); } string str() const { return c_format("SetInterfaceUnreachable: %s %s", ifname().c_str(), bool_c_str(_unreachable)); } private: bool _unreachable; }; /** * Class for setting the management state of an interface. */ class SetInterfaceManagement : public InterfaceModifier { public: SetInterfaceManagement(IfConfig& ifconfig, const string& ifname, bool management) : InterfaceModifier(ifconfig, ifname), _management(management) {} bool dispatch() { IfTreeInterface* fi = interface(); if (fi == NULL) return (false); fi->set_management(_management); return (true); } string str() const { return c_format("SetInterfaceManagement: %s %s", ifname().c_str(), bool_c_str(_management)); } private: bool _management; }; /** * Class for setting an interface MTU. */ class SetInterfaceMtu : public InterfaceModifier { public: SetInterfaceMtu(IfConfig& ifconfig, const string& ifname, uint32_t mtu) : InterfaceModifier(ifconfig, ifname), _mtu(mtu) {} // Minimum and maximum MTU (as defined in RFC 791 and RFC 1191) static const uint32_t MIN_MTU = 68; static const uint32_t MAX_MTU = 65536; bool dispatch() { IfTreeInterface* fi = interface(); if (fi == NULL) return (false); if (_mtu < MIN_MTU || _mtu > MAX_MTU) return (false); fi->set_mtu(_mtu); return (true); } string str() const { string s = c_format("SetInterfaceMtu: %s %u", ifname().c_str(), XORP_UINT_CAST(_mtu)); if (_mtu < MIN_MTU || _mtu > MAX_MTU) { s += c_format(" (valid range %u--%u)", XORP_UINT_CAST(MIN_MTU), XORP_UINT_CAST(MAX_MTU)); } return s; } private: uint32_t _mtu; }; /** * Class for restoring an interface MTU. */ class RestoreInterfaceMtu : public InterfaceModifier { public: RestoreInterfaceMtu(IfConfig& ifconfig, const string& ifname) : InterfaceModifier(ifconfig, ifname) {} bool dispatch() { // Get the original MTU const IfTree& orig_iftree = ifconfig().original_config(); const IfTreeInterface* orig_fi = orig_iftree.find_interface(ifname()); if (orig_fi == NULL) return (false); uint32_t orig_mtu = orig_fi->mtu(); IfTreeInterface* fi = interface(); if (fi == NULL) return (false); fi->set_mtu(orig_mtu); return (true); } string str() const { return c_format("RestoreInterfaceMtu: %s", ifname().c_str()); } private: }; /** * Class for setting an interface MAC. */ class SetInterfaceMac : public InterfaceModifier { public: SetInterfaceMac(IfConfig& ifconfig, const string& ifname, const Mac& mac) : InterfaceModifier(ifconfig, ifname), _mac(mac) {} bool dispatch() { IfTreeInterface* fi = interface(); if (fi == NULL) return (false); fi->set_mac(_mac); return (true); } string str() const { return c_format("SetInterfaceMac: %s %s", ifname().c_str(), _mac.str().c_str()); } private: Mac _mac; }; /** * Class for restoring an interface MAC. */ class RestoreInterfaceMac : public InterfaceModifier { public: RestoreInterfaceMac(IfConfig& ifconfig, const string& ifname) : InterfaceModifier(ifconfig, ifname) {} bool dispatch() { // Get the original MAC const IfTree& orig_iftree = ifconfig().original_config(); const IfTreeInterface* orig_fi = orig_iftree.find_interface(ifname()); if (orig_fi == NULL) return (false); const Mac& orig_mac = orig_fi->mac(); IfTreeInterface* fi = interface(); if (fi == NULL) return (false); fi->set_mac(orig_mac); return (true); } string str() const { return c_format("RestoreInterfaceMac: %s", ifname().c_str()); } private: }; /** * Class for adding a VIF to an interface. */ class AddInterfaceVif : public InterfaceModifier { public: AddInterfaceVif(IfConfig& ifconfig, const string& ifname, const string& vifname) : InterfaceModifier(ifconfig, ifname), _vifname(vifname) {} bool dispatch() { IfTreeInterface* fi = interface(); if (fi == NULL) return (false); fi->add_vif(_vifname); return (true); } string str() const { return c_format("AddInterfaceVif: %s %s", ifname().c_str(), _vifname.c_str()); } private: const string _vifname; }; /** * Class for removing a VIF from an interface. */ class RemoveInterfaceVif : public InterfaceModifier { public: RemoveInterfaceVif(IfConfig& ifconfig, const string& ifname, const string& vifname) : InterfaceModifier(ifconfig, ifname), _vifname(vifname) {} bool dispatch() { IfTreeInterface* fi = interface(); if (fi == NULL) return (false); if (fi->remove_vif(_vifname) != XORP_OK) return (false); return (true); } string str() const { return c_format("RemoveInterfaceVif: %s %s", ifname().c_str(), _vifname.c_str()); } private: const string _vifname; }; /** * Class for setting the VLAN state of a vif. */ class SetIfString : public InterfaceModifier { public: SetIfString(IfConfig& ifconfig, const string& ifname, const string& str, IfStringTypeE tp) : InterfaceModifier(ifconfig, ifname), _str(str), _tp(tp) {} bool dispatch() { IfTreeInterface* fi = interface(); if (fi == NULL) return false; switch (_tp) { case IF_STRING_PARENT_IFNAME: fi->set_parent_ifname(_str); return true; case IF_STRING_IFTYPE: fi->set_iface_type(_str); return true; case IF_STRING_VID: fi->set_vid(_str); return true; default: XLOG_ERROR("Unknown string type: %i\n", _tp); return false; } } string str() const { return c_format("SetIfString: %s %s %i", path().c_str(), _str.c_str(), _tp); } private: string _str; IfStringTypeE _tp; }; /** * Base class for vif modifier operations. */ class VifModifier : public InterfaceModifier { public: VifModifier(IfConfig& ifconfig, const string& ifname, const string& vifname) : InterfaceModifier(ifconfig, ifname), _vifname(vifname) {} string path() const { return InterfaceModifier::path() + string(" ") + vifname(); } const string& vifname() const { return _vifname; } protected: IfTreeVif* vif() { IfTreeVif* vifp = iftree().find_vif(ifname(), _vifname); return (vifp); } protected: const string _vifname; }; /** * Class for setting the enabled state of a vif. */ class SetVifEnabled : public VifModifier { public: SetVifEnabled(IfConfig& ifconfig, const string& ifname, const string& vifname, bool enabled) : VifModifier(ifconfig, ifname, vifname), _enabled(enabled) {} bool dispatch() { IfTreeVif* fv = vif(); if (fv == NULL) return (false); fv->set_enabled(_enabled); return (true); } string str() const { return c_format("SetVifEnabled: %s %s", path().c_str(), bool_c_str(_enabled)); } private: bool _enabled; }; /** * Class for adding an IPv4 address to a VIF. */ class AddAddr4 : public VifModifier { public: AddAddr4(IfConfig& ifconfig, const string& ifname, const string& vifname, const IPv4& addr) : VifModifier(ifconfig, ifname, vifname), _addr(addr) {} bool dispatch() { IfTreeVif* fv = vif(); if (fv == NULL) return (false); fv->add_addr(_addr); return (true); } string str() const { return c_format("AddAddr4: %s %s", path().c_str(), _addr.str().c_str()); } private: IPv4 _addr; }; /** * Class for removing an IPv4 address to a VIF. */ class RemoveAddr4 : public VifModifier { public: RemoveAddr4(IfConfig& ifconfig, const string& ifname, const string& vifname, const IPv4& addr) : VifModifier(ifconfig, ifname, vifname), _addr(addr) {} bool dispatch() { IfTreeVif* fv = vif(); if (fv == NULL) return (false); if (fv->remove_addr(_addr) != XORP_OK) return (false); return (true); } string str() const { return c_format("RemoveAddr4: %s %s", path().c_str(), _addr.str().c_str()); } private: IPv4 _addr; }; /** * Class for adding an IPv6 address to a VIF. */ class AddAddr6 : public VifModifier { public: AddAddr6(IfConfig& ifconfig, const string& ifname, const string& vifname, const IPv6& addr) : VifModifier(ifconfig, ifname, vifname), _addr(addr) {} bool dispatch() { IfTreeVif* fv = vif(); if (fv == NULL) return (false); if (fv->add_addr(_addr) != XORP_OK) return (false); return (true); } string str() const { return c_format("AddAddr6: %s %s", path().c_str(), _addr.str().c_str()); } private: IPv6 _addr; }; /** * Class for removing an IPv6 address to a VIF. */ class RemoveAddr6 : public VifModifier { public: RemoveAddr6(IfConfig& ifconfig, const string& ifname, const string& vifname, const IPv6& addr) : VifModifier(ifconfig, ifname, vifname), _addr(addr) {} bool dispatch() { IfTreeVif* fv = vif(); if (fv == NULL) return (false); if (fv->remove_addr(_addr) != XORP_OK) return (false); return (true); } string str() const { return c_format("RemoveAddr6: %s %s", path().c_str(), _addr.str().c_str()); } private: IPv6 _addr; }; /** * Base class for IPv4vif address modifier operations. */ class Addr4Modifier : public VifModifier { public: Addr4Modifier(IfConfig& ifconfig, const string& ifname, const string& vifname, const IPv4& addr) : VifModifier(ifconfig, ifname, vifname), _addr(addr) {} string path() const { return VifModifier::path() + string(" ") + _addr.str(); } protected: IfTreeAddr4* addr() { IfTreeAddr4* ap = iftree().find_addr(ifname(), vifname(), _addr); return (ap); } protected: const IPv4 _addr; }; /** * Class to set enable state of an IPv4 address associated with a vif. */ class SetAddr4Enabled : public Addr4Modifier { public: SetAddr4Enabled(IfConfig& ifconfig, const string& ifname, const string& vifname, const IPv4& addr, bool enabled) : Addr4Modifier(ifconfig, ifname, vifname, addr), _enabled(enabled) {} bool dispatch() { IfTreeAddr4* fa = addr(); if (fa == NULL) return (false); fa->set_enabled(_enabled); return (true); } string str() const { return c_format("SetAddr4Enabled: %s %s", path().c_str(), bool_c_str(_enabled)); } protected: bool _enabled; }; /** * Class to set the prefix of an IPv4 address associated with a vif. */ class SetAddr4Prefix : public Addr4Modifier { public: SetAddr4Prefix(IfConfig& ifconfig, const string& ifname, const string& vifname, const IPv4& addr, uint32_t prefix_len) : Addr4Modifier(ifconfig, ifname, vifname, addr), _prefix_len(prefix_len) {} static const uint32_t MAX_PREFIX_LEN = 32; bool dispatch() { IfTreeAddr4* fa = addr(); if (fa == NULL || _prefix_len > MAX_PREFIX_LEN) return (false); if (fa->set_prefix_len(_prefix_len) != XORP_OK) return (false); return (true); } string str() const { string s = c_format("SetAddr4Prefix: %s %u", path().c_str(), XORP_UINT_CAST(_prefix_len)); if (_prefix_len > MAX_PREFIX_LEN) s += c_format(" (valid range 0--%u)", XORP_UINT_CAST(MAX_PREFIX_LEN)); return s; } protected: uint32_t _prefix_len; }; /** * Class to set the endpoint IPv4 address associated with a vif. */ class SetAddr4Endpoint : public Addr4Modifier { public: SetAddr4Endpoint(IfConfig& ifconfig, const string& ifname, const string& vifname, const IPv4& addr, const IPv4& endpoint) : Addr4Modifier(ifconfig, ifname, vifname, addr), _endpoint(endpoint) {} bool dispatch() { IfTreeAddr4* fa = addr(); if (fa == NULL) return (false); fa->set_endpoint(_endpoint); fa->set_point_to_point(true); return (true); } string str() const { return c_format("SetAddr4Endpoint: %s %s", path().c_str(), _endpoint.str().c_str()); } protected: IPv4 _endpoint; }; /** * Class to set the broadcast address IPv4 address associated with a vif. */ class SetAddr4Broadcast : public Addr4Modifier { public: SetAddr4Broadcast(IfConfig& ifconfig, const string& ifname, const string& vifname, const IPv4& addr, const IPv4& bcast) : Addr4Modifier(ifconfig, ifname, vifname, addr), _bcast(bcast) {} bool dispatch() { IfTreeAddr4* fa = addr(); if (fa == NULL) return (false); fa->set_bcast(_bcast); fa->set_broadcast(true); return (true); } string str() const { return c_format("SetAddr4Broadcast: %s %s", path().c_str(), _bcast.str().c_str()); } protected: IPv4 _bcast; }; /** * Base class for IPv6vif address modifier operations. */ class Addr6Modifier : public VifModifier { public: Addr6Modifier(IfConfig& ifconfig, const string& ifname, const string& vifname, const IPv6& addr) : VifModifier(ifconfig, ifname, vifname), _addr(addr) {} string path() const { return VifModifier::path() + string(" ") + _addr.str(); } protected: IfTreeAddr6* addr() { IfTreeAddr6* ap = iftree().find_addr(ifname(), vifname(), _addr); return (ap); } protected: const IPv6 _addr; }; /** * Class to set the enabled state of an IPv6 address associated with a vif. */ class SetAddr6Enabled : public Addr6Modifier { public: SetAddr6Enabled(IfConfig& ifconfig, const string& ifname, const string& vifname, const IPv6& addr, bool enabled) : Addr6Modifier(ifconfig, ifname, vifname, addr), _enabled(enabled) {} bool dispatch() { IfTreeAddr6* fa = addr(); if (fa == NULL) return (false); fa->set_enabled(_enabled); return (true); } string str() const { return c_format("SetAddr6Enabled: %s %s", path().c_str(), bool_c_str(_enabled)); } protected: bool _enabled; }; /** * Class to set the prefix of an IPv6 address associated with a vif. */ class SetAddr6Prefix : public Addr6Modifier { public: SetAddr6Prefix(IfConfig& ifconfig, const string& ifname, const string& vifname, const IPv6& addr, uint32_t prefix_len) : Addr6Modifier(ifconfig, ifname, vifname, addr), _prefix_len(prefix_len) {} static const uint32_t MAX_PREFIX_LEN = 128; bool dispatch() { IfTreeAddr6* fa = addr(); if (fa == NULL || _prefix_len > MAX_PREFIX_LEN) return (false); if (fa->set_prefix_len(_prefix_len) != XORP_OK) return (false); return (true); } string str() const { string s = c_format("SetAddr6Prefix: %s %u", path().c_str(), XORP_UINT_CAST(_prefix_len)); if (_prefix_len > MAX_PREFIX_LEN) s += c_format(" (valid range 0--%u)", XORP_UINT_CAST(MAX_PREFIX_LEN)); return s; } protected: uint32_t _prefix_len; }; /** * Class to set the endpoint IPv6 address associated with a vif. */ class SetAddr6Endpoint : public Addr6Modifier { public: SetAddr6Endpoint(IfConfig& ifconfig, const string& ifname, const string& vifname, const IPv6& addr, const IPv6& endpoint) : Addr6Modifier(ifconfig, ifname, vifname, addr), _endpoint(endpoint) {} bool dispatch() { IfTreeAddr6* fa = addr(); if (fa == NULL) return (false); fa->set_endpoint(_endpoint); fa->set_point_to_point(true); return (true); } string str() const { return c_format("SetAddr6Endpoint: %s %s", path().c_str(), _endpoint.str().c_str()); } protected: IPv6 _endpoint; }; #endif // __FEA_IFCONFIG_TRANSACTION_HH__ xorp/fea/fibconfig.cc0000664000076400007640000012404511642400052014650 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/profile.hh" #ifndef XORP_DISABLE_PROFILE #include "profile_vars.hh" #endif #include "fibconfig.hh" #include "fibconfig_transaction.hh" #include "fea_node.hh" // // Unicast forwarding table related configuration. // FibConfig::FibConfig(FeaNode& fea_node, const IfTree& system_config_iftree, const IfTree& merged_config_iftree) : _fea_node(fea_node), _eventloop(fea_node.eventloop()), #ifndef XORP_DISABLE_PROFILE _profile(fea_node.profile()), #endif _nexthop_port_mapper(fea_node.nexthop_port_mapper()), _system_config_iftree(system_config_iftree), _merged_config_iftree(merged_config_iftree), _ftm(NULL), _unicast_forwarding_entries_retain_on_startup4(false), _unicast_forwarding_entries_retain_on_shutdown4(false), _unicast_forwarding_entries_retain_on_startup6(false), _unicast_forwarding_entries_retain_on_shutdown6(false), _unicast_forwarding_table_id4(0), _unicast_forwarding_table_id4_is_configured(false), _unicast_forwarding_table_id6(0), _unicast_forwarding_table_id6_is_configured(false), _is_running(false) { _ftm = new FibConfigTransactionManager(_eventloop, *this); } FibConfig::~FibConfig() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the mechanism for manipulating " "the forwarding table information: %s", error_msg.c_str()); } if (_ftm != NULL) { delete _ftm; _ftm = NULL; } } ProcessStatus FibConfig::status(string& reason) const { if (_ftm->pending() > 0) { reason = "There are transactions pending"; return (PROC_NOT_READY); } return (PROC_READY); } uint32_t FibConfig::get_netlink_filter_table_id() const { uint32_t tbl_id = 0; if (unicast_forwarding_table_id4_is_configured() || unicast_forwarding_table_id6_is_configured()) { if (unicast_forwarding_table_id4_is_configured()) { tbl_id = unicast_forwarding_table_id4(); if (unicast_forwarding_table_id6_is_configured()) { if (unicast_forwarding_table_id6() != tbl_id) { XLOG_WARNING("WARNING: IPv4 and v6 tables are configured and are different. Cannot filter on netlink table-id, will use default behaviour and listen to all tables.\n"); tbl_id = 0; } } } else { tbl_id = unicast_forwarding_table_id6(); } } //XLOG_WARNING("IPv4 configured: %i %u IPv6: %i %u filter-table: %u", // unicast_forwarding_table_id4_is_configured(), // unicast_forwarding_table_id4(), // unicast_forwarding_table_id6_is_configured(), // unicast_forwarding_table_id6(), tbl_id); return tbl_id; } void FibConfig::propagate_table_id_change() { uint32_t tbl_id = get_netlink_filter_table_id(); // Tell the various plugins our configured table changed in case they can filter. { list::iterator iter = _fibconfig_entry_gets.begin(); for (; iter != _fibconfig_entry_gets.end(); iter++) { (*iter)->notify_table_id_change(tbl_id); } } { list::iterator iter = _fibconfig_entry_sets.begin(); for (; iter != _fibconfig_entry_sets.end(); iter++) { (*iter)->notify_table_id_change(tbl_id); } } { list::iterator iter = _fibconfig_entry_observers.begin(); for (; iter != _fibconfig_entry_observers.end(); iter++) { (*iter)->notify_table_id_change(tbl_id); } } { list::iterator iter = _fibconfig_table_gets.begin(); for (; iter != _fibconfig_table_gets.end(); iter++) { (*iter)->notify_table_id_change(tbl_id); } } { list::iterator iter = _fibconfig_table_sets.begin(); for (; iter != _fibconfig_table_sets.end(); iter++) { (*iter)->notify_table_id_change(tbl_id); } } { list::iterator iter = _fibconfig_table_observers.begin(); for (; iter != _fibconfig_table_observers.end(); iter++) { (*iter)->notify_table_id_change(tbl_id); } } } int FibConfig::start_transaction(uint32_t& tid, string& error_msg) { if (_ftm->start(tid) != true) { error_msg = c_format("Resource limit on number of pending transactions hit"); return (XORP_ERROR); } return (XORP_OK); } int FibConfig::abort_transaction(uint32_t tid, string& error_msg) { if (_ftm->abort(tid) != true) { error_msg = c_format("Expired or invalid transaction ID presented"); return (XORP_ERROR); } return (XORP_OK); } int FibConfig::add_transaction_operation(uint32_t tid, const TransactionManager::Operation& op, string& error_msg) { uint32_t n_ops = 0; if (_ftm->retrieve_size(tid, n_ops) != true) { error_msg = c_format("Expired or invalid transaction ID presented"); return (XORP_ERROR); } if (_ftm->max_ops() <= n_ops) { error_msg = c_format("Resource limit on number of operations in a transaction hit"); return (XORP_ERROR); } // // In theory, resource shortage is the only thing that could get us here // if (_ftm->add(tid, op) != true) { error_msg = c_format("Unknown resource shortage"); return (XORP_ERROR); } return (XORP_OK); } int FibConfig::commit_transaction(uint32_t tid, string& error_msg) { if (_ftm->commit(tid) != true) { error_msg = c_format("Expired or invalid transaction ID presented"); return (XORP_ERROR); } const string& ftm_error_msg = _ftm->error(); if (! ftm_error_msg.empty()) { error_msg = ftm_error_msg; return (XORP_ERROR); } return (XORP_OK); } int FibConfig::register_fibconfig_forwarding(FibConfigForwarding* fibconfig_forwarding, bool is_exclusive) { if (is_exclusive) _fibconfig_forwarding_plugins.clear(); if ((fibconfig_forwarding != NULL) && (find(_fibconfig_forwarding_plugins.begin(), _fibconfig_forwarding_plugins.end(), fibconfig_forwarding) == _fibconfig_forwarding_plugins.end())) { _fibconfig_forwarding_plugins.push_back(fibconfig_forwarding); // // XXX: Push the current config into the new method // if (fibconfig_forwarding->is_running()) { bool v = false; string error_msg; string manager_name = fibconfig_forwarding->fea_data_plane_manager().manager_name(); if (fibconfig_forwarding->fea_data_plane_manager().have_ipv4()) { if (unicast_forwarding_enabled4(v, error_msg) != XORP_OK) { XLOG_ERROR("Cannot push the current IPv4 forwarding " "information state into the %s mechanism, " "because failed to obtain the current state: %s", manager_name.c_str(), error_msg.c_str()); } else { if (fibconfig_forwarding->set_unicast_forwarding_enabled4(v, error_msg) != XORP_OK) { XLOG_ERROR("Cannot push the current IPv4 forwarding " "information state into the %s mechanism: %s", manager_name.c_str(), error_msg.c_str()); } } } #ifdef HAVE_IPV6 if (fibconfig_forwarding->fea_data_plane_manager().have_ipv6()) { if (unicast_forwarding_enabled6(v, error_msg) != XORP_OK) { XLOG_ERROR("Cannot push the current IPv6 forwarding " "information state into the %s mechanism, " "because failed to obtain the current state: %s", manager_name.c_str(), error_msg.c_str()); } else { if (fibconfig_forwarding->set_unicast_forwarding_enabled6(v, error_msg) != XORP_OK) { XLOG_ERROR("Cannot push the current IPv6 forwarding " "information state into the %s mechanism: %s", manager_name.c_str(), error_msg.c_str()); } } if (accept_rtadv_enabled6(v, error_msg) != XORP_OK) { XLOG_ERROR("Cannot push the current IPv6 forwarding " "information state into the %s mechanism, " "because failed to obtain the current state: %s", manager_name.c_str(), error_msg.c_str()); } else { if (fibconfig_forwarding->set_accept_rtadv_enabled6(v, error_msg) != XORP_OK) { XLOG_ERROR("Cannot push the current IPv6 forwarding " "information state into the %s mechanism: %s", manager_name.c_str(), error_msg.c_str()); } } } #endif // HAVE_IPV6 } } return (XORP_OK); } int FibConfig::unregister_fibconfig_forwarding(FibConfigForwarding* fibconfig_forwarding) { if (fibconfig_forwarding == NULL) return (XORP_ERROR); list::iterator iter; iter = find(_fibconfig_forwarding_plugins.begin(), _fibconfig_forwarding_plugins.end(), fibconfig_forwarding); if (iter == _fibconfig_forwarding_plugins.end()) return (XORP_ERROR); _fibconfig_forwarding_plugins.erase(iter); return (XORP_OK); } int FibConfig::register_fibconfig_entry_get(FibConfigEntryGet* fibconfig_entry_get, bool is_exclusive) { if (is_exclusive) _fibconfig_entry_gets.clear(); if ((fibconfig_entry_get != NULL) && (find(_fibconfig_entry_gets.begin(), _fibconfig_entry_gets.end(), fibconfig_entry_get) == _fibconfig_entry_gets.end())) { _fibconfig_entry_gets.push_back(fibconfig_entry_get); } return (XORP_OK); } int FibConfig::unregister_fibconfig_entry_get(FibConfigEntryGet* fibconfig_entry_get) { if (fibconfig_entry_get == NULL) return (XORP_ERROR); list::iterator iter; iter = find(_fibconfig_entry_gets.begin(), _fibconfig_entry_gets.end(), fibconfig_entry_get); if (iter == _fibconfig_entry_gets.end()) return (XORP_ERROR); _fibconfig_entry_gets.erase(iter); return (XORP_OK); } int FibConfig::register_fibconfig_entry_set(FibConfigEntrySet* fibconfig_entry_set, bool is_exclusive) { if (is_exclusive) _fibconfig_entry_sets.clear(); if ((fibconfig_entry_set != NULL) && (find(_fibconfig_entry_sets.begin(), _fibconfig_entry_sets.end(), fibconfig_entry_set) == _fibconfig_entry_sets.end())) { _fibconfig_entry_sets.push_back(fibconfig_entry_set); // // XXX: Push the current config into the new method // if (fibconfig_entry_set->is_running()) { // XXX: nothing to do. // // XXX: note that the forwarding table state is pushed by method // FibConfig::register_fibconfig_table_set(), // hence we don't need to do it here again. However, this is based // on the assumption that for each FibConfigEntrySet method // there is a corresponding FibConfigTableSet method. // } } return (XORP_OK); } int FibConfig::unregister_fibconfig_entry_set(FibConfigEntrySet* fibconfig_entry_set) { if (fibconfig_entry_set == NULL) return (XORP_ERROR); list::iterator iter; iter = find(_fibconfig_entry_sets.begin(), _fibconfig_entry_sets.end(), fibconfig_entry_set); if (iter == _fibconfig_entry_sets.end()) return (XORP_ERROR); _fibconfig_entry_sets.erase(iter); return (XORP_OK); } int FibConfig::register_fibconfig_entry_observer(FibConfigEntryObserver* fibconfig_entry_observer, bool is_exclusive) { if (is_exclusive) _fibconfig_entry_observers.clear(); if ((fibconfig_entry_observer != NULL) && (find(_fibconfig_entry_observers.begin(), _fibconfig_entry_observers.end(), fibconfig_entry_observer) == _fibconfig_entry_observers.end())) { _fibconfig_entry_observers.push_back(fibconfig_entry_observer); } return (XORP_OK); } int FibConfig::unregister_fibconfig_entry_observer(FibConfigEntryObserver* fibconfig_entry_observer) { if (fibconfig_entry_observer == NULL) return (XORP_ERROR); list::iterator iter; iter = find(_fibconfig_entry_observers.begin(), _fibconfig_entry_observers.end(), fibconfig_entry_observer); if (iter == _fibconfig_entry_observers.end()) return (XORP_ERROR); _fibconfig_entry_observers.erase(iter); return (XORP_OK); } int FibConfig::register_fibconfig_table_get(FibConfigTableGet* fibconfig_table_get, bool is_exclusive) { if (is_exclusive) _fibconfig_table_gets.clear(); if ((fibconfig_table_get != NULL) && (find(_fibconfig_table_gets.begin(), _fibconfig_table_gets.end(), fibconfig_table_get) == _fibconfig_table_gets.end())) { _fibconfig_table_gets.push_back(fibconfig_table_get); } return (XORP_OK); } int FibConfig::unregister_fibconfig_table_get(FibConfigTableGet* fibconfig_table_get) { if (fibconfig_table_get == NULL) return (XORP_ERROR); list::iterator iter; iter = find(_fibconfig_table_gets.begin(), _fibconfig_table_gets.end(), fibconfig_table_get); if (iter == _fibconfig_table_gets.end()) return (XORP_ERROR); _fibconfig_table_gets.erase(iter); return (XORP_OK); } int FibConfig::register_fibconfig_table_set(FibConfigTableSet* fibconfig_table_set, bool is_exclusive) { if (is_exclusive) _fibconfig_table_sets.clear(); if ((fibconfig_table_set != NULL) && (find(_fibconfig_table_sets.begin(), _fibconfig_table_sets.end(), fibconfig_table_set) == _fibconfig_table_sets.end())) { _fibconfig_table_sets.push_back(fibconfig_table_set); // // XXX: Push the current config into the new method // if (fibconfig_table_set->is_running()) { list fte_list4; if (get_table4(fte_list4) == XORP_OK) { if (fibconfig_table_set->set_table4(fte_list4) != XORP_OK) { XLOG_ERROR("Cannot push the current IPv4 forwarding table " "into a new mechanism for setting the " "forwarding table"); } } #ifdef HAVE_IPV6 list fte_list6; if (get_table6(fte_list6) == XORP_OK) { if (fibconfig_table_set->set_table6(fte_list6) != XORP_OK) { XLOG_ERROR("Cannot push the current IPv6 forwarding table " "into a new mechanism for setting the " "forwarding table"); } } #endif // HAVE_IPV6 } } return (XORP_OK); } int FibConfig::unregister_fibconfig_table_set(FibConfigTableSet* fibconfig_table_set) { if (fibconfig_table_set == NULL) return (XORP_ERROR); list::iterator iter; iter = find(_fibconfig_table_sets.begin(), _fibconfig_table_sets.end(), fibconfig_table_set); if (iter == _fibconfig_table_sets.end()) return (XORP_ERROR); _fibconfig_table_sets.erase(iter); return (XORP_OK); } int FibConfig::register_fibconfig_table_observer(FibConfigTableObserver* fibconfig_table_observer, bool is_exclusive) { if (is_exclusive) _fibconfig_table_observers.clear(); if ((fibconfig_table_observer != NULL) && (find(_fibconfig_table_observers.begin(), _fibconfig_table_observers.end(), fibconfig_table_observer) == _fibconfig_table_observers.end())) { _fibconfig_table_observers.push_back(fibconfig_table_observer); } return (XORP_OK); } int FibConfig::unregister_fibconfig_table_observer(FibConfigTableObserver* fibconfig_table_observer) { if (fibconfig_table_observer == NULL) return (XORP_ERROR); list::iterator iter; iter = find(_fibconfig_table_observers.begin(), _fibconfig_table_observers.end(), fibconfig_table_observer); if (iter == _fibconfig_table_observers.end()) return (XORP_ERROR); _fibconfig_table_observers.erase(iter); return (XORP_OK); } int FibConfig::start(string& error_msg) { list::iterator fibconfig_forwarding_iter; list::iterator fibconfig_entry_get_iter; list::iterator fibconfig_entry_set_iter; list::iterator fibconfig_entry_observer_iter; list::iterator fibconfig_table_get_iter; list::iterator fibconfig_table_set_iter; list::iterator fibconfig_table_observer_iter; if (_is_running) return (XORP_OK); // // Check whether all mechanisms are available // if (_fibconfig_forwarding_plugins.empty()) { error_msg = c_format("No mechanism to configure unicast forwarding"); return (XORP_ERROR); } if (_fibconfig_entry_gets.empty()) { error_msg = c_format("No mechanism to get forwarding table entries"); return (XORP_ERROR); } if (_fibconfig_entry_sets.empty()) { error_msg = c_format("No mechanism to set forwarding table entries"); return (XORP_ERROR); } if (_fibconfig_entry_observers.empty()) { error_msg = c_format("No mechanism to observe forwarding table entries"); return (XORP_ERROR); } if (_fibconfig_table_gets.empty()) { error_msg = c_format("No mechanism to get the forwarding table"); return (XORP_ERROR); } if (_fibconfig_table_sets.empty()) { error_msg = c_format("No mechanism to set the forwarding table"); return (XORP_ERROR); } if (_fibconfig_table_observers.empty()) { error_msg = c_format("No mechanism to observe the forwarding table"); return (XORP_ERROR); } // // Start the FibConfigForwarding methods // for (fibconfig_forwarding_iter = _fibconfig_forwarding_plugins.begin(); fibconfig_forwarding_iter != _fibconfig_forwarding_plugins.end(); ++fibconfig_forwarding_iter) { FibConfigForwarding* fibconfig_forwarding = *fibconfig_forwarding_iter; if (fibconfig_forwarding->start(error_msg) != XORP_OK) return (XORP_ERROR); } // // Start the FibConfigEntryGet methods // for (fibconfig_entry_get_iter = _fibconfig_entry_gets.begin(); fibconfig_entry_get_iter != _fibconfig_entry_gets.end(); ++fibconfig_entry_get_iter) { FibConfigEntryGet* fibconfig_entry_get = *fibconfig_entry_get_iter; if (fibconfig_entry_get->start(error_msg) != XORP_OK) return (XORP_ERROR); } // // Start the FibConfigEntrySet methods // for (fibconfig_entry_set_iter = _fibconfig_entry_sets.begin(); fibconfig_entry_set_iter != _fibconfig_entry_sets.end(); ++fibconfig_entry_set_iter) { FibConfigEntrySet* fibconfig_entry_set = *fibconfig_entry_set_iter; if (fibconfig_entry_set->start(error_msg) != XORP_OK) return (XORP_ERROR); } // // Start the FibConfigEntryObserver methods // for (fibconfig_entry_observer_iter = _fibconfig_entry_observers.begin(); fibconfig_entry_observer_iter != _fibconfig_entry_observers.end(); ++fibconfig_entry_observer_iter) { FibConfigEntryObserver* fibconfig_entry_observer = *fibconfig_entry_observer_iter; if (fibconfig_entry_observer->start(error_msg) != XORP_OK) return (XORP_ERROR); } // // Start the FibConfigTableGet methods // for (fibconfig_table_get_iter = _fibconfig_table_gets.begin(); fibconfig_table_get_iter != _fibconfig_table_gets.end(); ++fibconfig_table_get_iter) { FibConfigTableGet* fibconfig_table_get = *fibconfig_table_get_iter; if (fibconfig_table_get->start(error_msg) != XORP_OK) return (XORP_ERROR); } // // Start the FibConfigTableSet methods // for (fibconfig_table_set_iter = _fibconfig_table_sets.begin(); fibconfig_table_set_iter != _fibconfig_table_sets.end(); ++fibconfig_table_set_iter) { FibConfigTableSet* fibconfig_table_set = *fibconfig_table_set_iter; if (fibconfig_table_set->start(error_msg) != XORP_OK) return (XORP_ERROR); } // // Start the FibConfigTableObserver methods // for (fibconfig_table_observer_iter = _fibconfig_table_observers.begin(); fibconfig_table_observer_iter != _fibconfig_table_observers.end(); ++fibconfig_table_observer_iter) { FibConfigTableObserver* fibconfig_table_observer = *fibconfig_table_observer_iter; if (fibconfig_table_observer->start(error_msg) != XORP_OK) return (XORP_ERROR); } _is_running = true; return (XORP_OK); } int FibConfig::stop(string& error_msg) { list::iterator fibconfig_forwarding_iter; list::iterator fibconfig_entry_get_iter; list::iterator fibconfig_entry_set_iter; list::iterator fibconfig_entry_observer_iter; list::iterator fibconfig_table_get_iter; list::iterator fibconfig_table_set_iter; list::iterator fibconfig_table_observer_iter; int ret_value = XORP_OK; string error_msg2; if (! _is_running) return (XORP_OK); error_msg.erase(); // // Stop the FibConfigTableObserver methods // for (fibconfig_table_observer_iter = _fibconfig_table_observers.begin(); fibconfig_table_observer_iter != _fibconfig_table_observers.end(); ++fibconfig_table_observer_iter) { FibConfigTableObserver* fibconfig_table_observer = *fibconfig_table_observer_iter; if (fibconfig_table_observer->stop(error_msg2) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } } // // Stop the FibConfigTableSet methods // for (fibconfig_table_set_iter = _fibconfig_table_sets.begin(); fibconfig_table_set_iter != _fibconfig_table_sets.end(); ++fibconfig_table_set_iter) { FibConfigTableSet* fibconfig_table_set = *fibconfig_table_set_iter; if (fibconfig_table_set->stop(error_msg2) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } } // // Stop the FibConfigTableGet methods // for (fibconfig_table_get_iter = _fibconfig_table_gets.begin(); fibconfig_table_get_iter != _fibconfig_table_gets.end(); ++fibconfig_table_get_iter) { FibConfigTableGet* fibconfig_table_get = *fibconfig_table_get_iter; if (fibconfig_table_get->stop(error_msg2) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } } // // Stop the FibConfigEntryObserver methods // for (fibconfig_entry_observer_iter = _fibconfig_entry_observers.begin(); fibconfig_entry_observer_iter != _fibconfig_entry_observers.end(); ++fibconfig_entry_observer_iter) { FibConfigEntryObserver* fibconfig_entry_observer = *fibconfig_entry_observer_iter; if (fibconfig_entry_observer->stop(error_msg2) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } } // // Stop the FibConfigEntrySet methods // for (fibconfig_entry_set_iter = _fibconfig_entry_sets.begin(); fibconfig_entry_set_iter != _fibconfig_entry_sets.end(); ++fibconfig_entry_set_iter) { FibConfigEntrySet* fibconfig_entry_set = *fibconfig_entry_set_iter; if (fibconfig_entry_set->stop(error_msg2) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } } // // Stop the FibConfigEntryGet methods // for (fibconfig_entry_get_iter = _fibconfig_entry_gets.begin(); fibconfig_entry_get_iter != _fibconfig_entry_gets.end(); ++fibconfig_entry_get_iter) { FibConfigEntryGet* fibconfig_entry_get = *fibconfig_entry_get_iter; if (fibconfig_entry_get->stop(error_msg2) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } } // // Stop the FibConfigForwarding methods // for (fibconfig_forwarding_iter = _fibconfig_forwarding_plugins.begin(); fibconfig_forwarding_iter != _fibconfig_forwarding_plugins.end(); ++fibconfig_forwarding_iter) { FibConfigForwarding* fibconfig_forwarding = *fibconfig_forwarding_iter; if (fibconfig_forwarding->stop(error_msg2) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } } _is_running = false; return (ret_value); } int FibConfig::start_configuration(string& error_msg) { list::iterator fibconfig_entry_set_iter; list::iterator fibconfig_table_set_iter; int ret_value = XORP_OK; string error_msg2; error_msg.erase(); // // XXX: We need to call start_configuration() for "entry" and "table", // because the top-level start/end configuration interface // does not distinguish between "entry" and "table" modification. // for (fibconfig_entry_set_iter = _fibconfig_entry_sets.begin(); fibconfig_entry_set_iter != _fibconfig_entry_sets.end(); ++fibconfig_entry_set_iter) { FibConfigEntrySet* fibconfig_entry_set = *fibconfig_entry_set_iter; if (fibconfig_entry_set->start_configuration(error_msg2) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } } for (fibconfig_table_set_iter = _fibconfig_table_sets.begin(); fibconfig_table_set_iter != _fibconfig_table_sets.end(); ++fibconfig_table_set_iter) { FibConfigTableSet* fibconfig_table_set = *fibconfig_table_set_iter; if (fibconfig_table_set->start_configuration(error_msg2) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } } return (ret_value); } int FibConfig::end_configuration(string& error_msg) { list::iterator fibconfig_entry_set_iter; list::iterator fibconfig_table_set_iter; int ret_value = XORP_OK; string error_msg2; error_msg.erase(); // // XXX: We need to call end_configuration() for "entry" and "table", // because the top-level start/end configuration interface // does not distinguish between "entry" and "table" modification. // for (fibconfig_entry_set_iter = _fibconfig_entry_sets.begin(); fibconfig_entry_set_iter != _fibconfig_entry_sets.end(); ++fibconfig_entry_set_iter) { FibConfigEntrySet* fibconfig_entry_set = *fibconfig_entry_set_iter; if (fibconfig_entry_set->end_configuration(error_msg2) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } } for (fibconfig_table_set_iter = _fibconfig_table_sets.begin(); fibconfig_table_set_iter != _fibconfig_table_sets.end(); ++fibconfig_table_set_iter) { FibConfigTableSet* fibconfig_table_set = *fibconfig_table_set_iter; if (fibconfig_table_set->end_configuration(error_msg2) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } } return (ret_value); } int FibConfig::set_unicast_forwarding_entries_retain_on_startup4(bool retain, string& error_msg) { _unicast_forwarding_entries_retain_on_startup4 = retain; error_msg = ""; // XXX: reset return (XORP_OK); } int FibConfig::set_unicast_forwarding_entries_retain_on_shutdown4(bool retain, string& error_msg) { _unicast_forwarding_entries_retain_on_shutdown4 = retain; error_msg = ""; // XXX: reset return (XORP_OK); } int FibConfig::set_unicast_forwarding_entries_retain_on_startup6(bool retain, string& error_msg) { _unicast_forwarding_entries_retain_on_startup6 = retain; error_msg = ""; // XXX: reset return (XORP_OK); } int FibConfig::set_unicast_forwarding_entries_retain_on_shutdown6(bool retain, string& error_msg) { _unicast_forwarding_entries_retain_on_shutdown6 = retain; error_msg = ""; // XXX: reset return (XORP_OK); } bool FibConfig::unicast_forwarding_table_id_is_configured(int family) const { switch (family) { case AF_INET: return (unicast_forwarding_table_id4_is_configured()); #ifdef HAVE_IPV6 case AF_INET6: return (unicast_forwarding_table_id6_is_configured()); #endif default: XLOG_UNREACHABLE(); break; } return (false); } uint32_t FibConfig::unicast_forwarding_table_id(int family) const { switch (family) { case AF_INET: return (unicast_forwarding_table_id4()); #ifdef HAVE_IPV6 case AF_INET6: return (unicast_forwarding_table_id6()); #endif default: XLOG_UNREACHABLE(); break; } return (0); } int FibConfig::set_unicast_forwarding_table_id4(bool is_configured, uint32_t table_id, string& error_msg) { if ((_unicast_forwarding_table_id4_is_configured != is_configured) || (_unicast_forwarding_table_id4 != table_id)) { _unicast_forwarding_table_id4_is_configured = is_configured; _unicast_forwarding_table_id4 = table_id; propagate_table_id_change(); } error_msg = ""; // XXX: reset return (XORP_OK); } int FibConfig::set_unicast_forwarding_table_id6(bool is_configured, uint32_t table_id, string& error_msg) { if ((_unicast_forwarding_table_id6_is_configured != is_configured) || (_unicast_forwarding_table_id6 != table_id)) { _unicast_forwarding_table_id6_is_configured = is_configured; _unicast_forwarding_table_id6 = table_id; propagate_table_id_change(); } error_msg = ""; // XXX: reset return (XORP_OK); } int FibConfig::unicast_forwarding_enabled4(bool& ret_value, string& error_msg) const { if (_fibconfig_forwarding_plugins.empty()) { error_msg = c_format("No plugin to test whether IPv4 unicast " "forwarding is enabled"); return (XORP_ERROR); } // // XXX: We pull the information by using only the first method. // In the future we need to rething this and be more flexible. // if (_fibconfig_forwarding_plugins.front()->unicast_forwarding_enabled4( ret_value, error_msg) != XORP_OK) { return (XORP_ERROR); } return (XORP_OK); } int FibConfig::unicast_forwarding_enabled6(bool& ret_value, string& error_msg) const { if (_fibconfig_forwarding_plugins.empty()) { error_msg = c_format("No plugin to test whether IPv6 unicast " "forwarding is enabled"); return (XORP_ERROR); } // // XXX: We pull the information by using only the first method. // In the future we need to rething this and be more flexible. // if (_fibconfig_forwarding_plugins.front()->unicast_forwarding_enabled6( ret_value, error_msg) != XORP_OK) { return (XORP_ERROR); } return (XORP_OK); } int FibConfig::accept_rtadv_enabled6(bool& ret_value, string& error_msg) const { if (_fibconfig_forwarding_plugins.empty()) { error_msg = c_format("No plugin to test whether IPv6 Router " "Advertisement messages are accepted"); return (XORP_ERROR); } // // XXX: We pull the information by using only the first method. // In the future we need to rething this and be more flexible. // if (_fibconfig_forwarding_plugins.front()->accept_rtadv_enabled6( ret_value, error_msg) != XORP_OK) { return (XORP_ERROR); } return (XORP_OK); } int FibConfig::set_unicast_forwarding_enabled4(bool v, string& error_msg) { list::iterator fibconfig_forwarding_iter; if (_fibconfig_forwarding_plugins.empty()) { error_msg = c_format("No plugin to configure the IPv4 unicast " "forwarding"); return (XORP_ERROR); } for (fibconfig_forwarding_iter = _fibconfig_forwarding_plugins.begin(); fibconfig_forwarding_iter != _fibconfig_forwarding_plugins.end(); ++fibconfig_forwarding_iter) { FibConfigForwarding* fibconfig_forwarding = *fibconfig_forwarding_iter; if (fibconfig_forwarding->set_unicast_forwarding_enabled4(v, error_msg) != XORP_OK) return (XORP_ERROR); } return (XORP_OK); } int FibConfig::set_unicast_forwarding_enabled6(bool v, string& error_msg) { list::iterator fibconfig_forwarding_iter; if (_fibconfig_forwarding_plugins.empty()) { error_msg = c_format("No plugin to configure the IPv6 unicast " "forwarding"); return (XORP_ERROR); } for (fibconfig_forwarding_iter = _fibconfig_forwarding_plugins.begin(); fibconfig_forwarding_iter != _fibconfig_forwarding_plugins.end(); ++fibconfig_forwarding_iter) { FibConfigForwarding* fibconfig_forwarding = *fibconfig_forwarding_iter; if (fibconfig_forwarding->set_unicast_forwarding_enabled6(v, error_msg) != XORP_OK) return (XORP_ERROR); } return (XORP_OK); } int FibConfig::set_accept_rtadv_enabled6(bool v, string& error_msg) { list::iterator fibconfig_forwarding_iter; if (_fibconfig_forwarding_plugins.empty()) { error_msg = c_format("No plugin to configure IPv6 Router " "Advertisement messages acceptance"); return (XORP_ERROR); } for (fibconfig_forwarding_iter = _fibconfig_forwarding_plugins.begin(); fibconfig_forwarding_iter != _fibconfig_forwarding_plugins.end(); ++fibconfig_forwarding_iter) { FibConfigForwarding* fibconfig_forwarding = *fibconfig_forwarding_iter; if (fibconfig_forwarding->set_accept_rtadv_enabled6(v, error_msg) != XORP_OK) return (XORP_ERROR); } return (XORP_OK); } int FibConfig::add_entry4(const Fte4& fte) { list::iterator fibconfig_entry_set_iter; if (_fibconfig_entry_sets.empty()) return (XORP_ERROR); PROFILE(if (_profile.enabled(profile_route_out)) _profile.log(profile_route_out, c_format("add %s", fte.net().str().c_str()))); for (fibconfig_entry_set_iter = _fibconfig_entry_sets.begin(); fibconfig_entry_set_iter != _fibconfig_entry_sets.end(); ++fibconfig_entry_set_iter) { FibConfigEntrySet* fibconfig_entry_set = *fibconfig_entry_set_iter; if (fibconfig_entry_set->add_entry4(fte) != XORP_OK) return (XORP_ERROR); } return (XORP_OK); } int FibConfig::delete_entry4(const Fte4& fte) { list::iterator fibconfig_entry_set_iter; if (_fibconfig_entry_sets.empty()) return (XORP_ERROR); PROFILE(if (_profile.enabled(profile_route_out)) _profile.log(profile_route_out, c_format("delete %s", fte.net().str().c_str()))); for (fibconfig_entry_set_iter = _fibconfig_entry_sets.begin(); fibconfig_entry_set_iter != _fibconfig_entry_sets.end(); ++fibconfig_entry_set_iter) { FibConfigEntrySet* fibconfig_entry_set = *fibconfig_entry_set_iter; if (fibconfig_entry_set->delete_entry4(fte) != XORP_OK) return (XORP_ERROR); } return (XORP_OK); } int FibConfig::set_table4(const list& fte_list) { list::iterator fibconfig_table_set_iter; if (_fibconfig_table_sets.empty()) return (XORP_ERROR); for (fibconfig_table_set_iter = _fibconfig_table_sets.begin(); fibconfig_table_set_iter != _fibconfig_table_sets.end(); ++fibconfig_table_set_iter) { FibConfigTableSet* fibconfig_table_set = *fibconfig_table_set_iter; if (fibconfig_table_set->set_table4(fte_list) != XORP_OK) return (XORP_ERROR); } return (XORP_OK); } int FibConfig::delete_all_entries4() { list::iterator fibconfig_table_set_iter; if (_fibconfig_table_sets.empty()) return (XORP_ERROR); for (fibconfig_table_set_iter = _fibconfig_table_sets.begin(); fibconfig_table_set_iter != _fibconfig_table_sets.end(); ++fibconfig_table_set_iter) { FibConfigTableSet* fibconfig_table_set = *fibconfig_table_set_iter; if (fibconfig_table_set->delete_all_entries4() != XORP_OK) return (XORP_ERROR); } return (XORP_OK); } int FibConfig::lookup_route_by_dest4(const IPv4& dst, Fte4& fte) { if (_fibconfig_entry_gets.empty()) return (XORP_ERROR); // // XXX: We pull the information by using only the first method. // In the future we need to rething this and be more flexible. // if (_fibconfig_entry_gets.front()->lookup_route_by_dest4(dst, fte) != XORP_OK) { return (XORP_ERROR); } return (XORP_OK); } int FibConfig::lookup_route_by_network4(const IPv4Net& dst, Fte4& fte) { if (_fibconfig_entry_gets.empty()) return (XORP_ERROR); // // XXX: We pull the information by using only the first method. // In the future we need to rething this and be more flexible. // if (_fibconfig_entry_gets.front()->lookup_route_by_network4(dst, fte) != XORP_OK) { return (XORP_ERROR); } return (XORP_OK); } int FibConfig::get_table4(list& fte_list) { if (_fibconfig_table_gets.empty()) return (XORP_ERROR); // // XXX: We pull the information by using only the first method. // In the future we need to rething this and be more flexible. // if (_fibconfig_table_gets.front()->get_table4(fte_list) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int FibConfig::add_entry6(const Fte6& fte) { list::iterator fibconfig_entry_set_iter; if (_fibconfig_entry_sets.empty()) return (XORP_ERROR); PROFILE(if (_profile.enabled(profile_route_out)) _profile.log(profile_route_out, c_format("add %s", fte.net().str().c_str()))); for (fibconfig_entry_set_iter = _fibconfig_entry_sets.begin(); fibconfig_entry_set_iter != _fibconfig_entry_sets.end(); ++fibconfig_entry_set_iter) { FibConfigEntrySet* fibconfig_entry_set = *fibconfig_entry_set_iter; if (fibconfig_entry_set->add_entry6(fte) != XORP_OK) return (XORP_ERROR); } return (XORP_OK); } int FibConfig::delete_entry6(const Fte6& fte) { list::iterator fibconfig_entry_set_iter; if (_fibconfig_entry_sets.empty()) return (XORP_ERROR); PROFILE(if (_profile.enabled(profile_route_out)) _profile.log(profile_route_out, c_format("delete %s", fte.net().str().c_str()))); for (fibconfig_entry_set_iter = _fibconfig_entry_sets.begin(); fibconfig_entry_set_iter != _fibconfig_entry_sets.end(); ++fibconfig_entry_set_iter) { FibConfigEntrySet* fibconfig_entry_set = *fibconfig_entry_set_iter; if (fibconfig_entry_set->delete_entry6(fte) != XORP_OK) return (XORP_ERROR); } return (XORP_OK); } int FibConfig::set_table6(const list& fte_list) { list::iterator fibconfig_table_set_iter; if (_fibconfig_table_sets.empty()) return (XORP_ERROR); for (fibconfig_table_set_iter = _fibconfig_table_sets.begin(); fibconfig_table_set_iter != _fibconfig_table_sets.end(); ++fibconfig_table_set_iter) { FibConfigTableSet* fibconfig_table_set = *fibconfig_table_set_iter; if (fibconfig_table_set->set_table6(fte_list) != XORP_OK) return (XORP_ERROR); } return (XORP_OK); } int FibConfig::delete_all_entries6() { list::iterator fibconfig_table_set_iter; if (_fibconfig_table_sets.empty()) return (XORP_ERROR); for (fibconfig_table_set_iter = _fibconfig_table_sets.begin(); fibconfig_table_set_iter != _fibconfig_table_sets.end(); ++fibconfig_table_set_iter) { FibConfigTableSet* fibconfig_table_set = *fibconfig_table_set_iter; if (fibconfig_table_set->delete_all_entries6() != XORP_OK) return (XORP_ERROR); } return (XORP_OK); } int FibConfig::lookup_route_by_dest6(const IPv6& dst, Fte6& fte) { if (_fibconfig_entry_gets.empty()) return (XORP_ERROR); // // XXX: We pull the information by using only the first method. // In the future we need to rething this and be more flexible. // if (_fibconfig_entry_gets.front()->lookup_route_by_dest6(dst, fte) != XORP_OK) { return (XORP_ERROR); } return (XORP_OK); } int FibConfig::lookup_route_by_network6(const IPv6Net& dst, Fte6& fte) { if (_fibconfig_entry_gets.empty()) return (XORP_ERROR); // // XXX: We pull the information by using only the first method. // In the future we need to rething this and be more flexible. // if (_fibconfig_entry_gets.front()->lookup_route_by_network6(dst, fte) != XORP_OK) { return (XORP_ERROR); } return (XORP_OK); } int FibConfig::get_table6(list& fte_list) { if (_fibconfig_table_gets.empty()) return (XORP_ERROR); // // XXX: We pull the information by using only the first method. // In the future we need to rething this and be more flexible. // if (_fibconfig_table_gets.front()->get_table6(fte_list) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int FibConfig::add_fib_table_observer(FibTableObserverBase* fib_table_observer) { if (find(_fib_table_observers.begin(), _fib_table_observers.end(), fib_table_observer) != _fib_table_observers.end()) { // XXX: we have already added that observer return (XORP_OK); } _fib_table_observers.push_back(fib_table_observer); return (XORP_OK); } int FibConfig::delete_fib_table_observer(FibTableObserverBase* fib_table_observer) { list::iterator iter; iter = find(_fib_table_observers.begin(), _fib_table_observers.end(), fib_table_observer); if (iter == _fib_table_observers.end()) { // XXX: observer not found return (XORP_ERROR); } _fib_table_observers.erase(iter); return (XORP_OK); } void FibConfig::propagate_fib_changes(const list& fte_list, const FibConfigTableObserver* fibconfig_table_observer) { list fte_list4; #ifdef HAVE_IPV6 list fte_list6; #endif list::const_iterator ftex_iter; // // XXX: propagate the changes only from the first method. // In the future we need to rething this and be more flexible. // if (_fibconfig_table_observers.empty() || (_fibconfig_table_observers.front() != fibconfig_table_observer)) { return; } if (fte_list.empty()) return; // Copy the FteX list into Fte4 and Fte6 lists for (ftex_iter = fte_list.begin(); ftex_iter != fte_list.end(); ++ftex_iter) { const FteX& ftex = *ftex_iter; if (ftex.net().is_ipv4()) { // IPv4 entry Fte4 fte4 = ftex.get_fte4(); fte_list4.push_back(fte4); } #ifdef HAVE_IPV6 if (ftex.net().is_ipv6()) { // IPv6 entry Fte6 fte6 = ftex.get_fte6(); fte_list6.push_back(fte6); } #endif } // Inform all observers about the changes list::iterator iter; for (iter = _fib_table_observers.begin(); iter != _fib_table_observers.end(); ++iter) { FibTableObserverBase* fib_table_observer = *iter; if (! fte_list4.empty()) fib_table_observer->process_fib_changes(fte_list4); #ifdef HAVE_IPV6 if (! fte_list6.empty()) fib_table_observer->process_fib_changes(fte_list6); #endif } } xorp/fea/xrl_mfea_node.hh0000664000076400007640000004153311540225525015545 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __FEA_XRL_MFEA_NODE_HH__ #define __FEA_XRL_MFEA_NODE_HH__ // // MFEA (Multicast Forwarding Engine Abstraction) XRL-aware node definition. // #include "libxipc/xrl_std_router.hh" #include "xrl/interfaces/finder_event_notifier_xif.hh" #include "xrl/interfaces/mfea_client_xif.hh" #include "xrl/interfaces/cli_manager_xif.hh" #include "xrl/targets/mfea_base.hh" #include "libfeaclient_bridge.hh" #include "mfea_node.hh" #include "mfea_node_cli.hh" // // The top-level class that wraps-up everything together under one roof // class XrlMfeaNode : public MfeaNode, public XrlStdRouter, public XrlMfeaTargetBase, public MfeaNodeCli { public: XrlMfeaNode(FeaNode& fea_node, int family, xorp_module_id module_id, EventLoop& eventloop, const string& class_name, const string& finder_hostname, uint16_t finder_port, const string& finder_target); virtual ~XrlMfeaNode(); /** * Startup the node operation. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int startup(); /** * Shutdown the node operation. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int shutdown(); /** * Get a reference to the XrlRouter instance. * * @return a reference to the XrlRouter (@ref XrlRouter) instance. */ XrlRouter& xrl_router() { return *this; } /** * Get a const reference to the XrlRouter instance. * * @return a const reference to the XrlRouter (@ref XrlRouter) instance. */ const XrlRouter& xrl_router() const { return *this; } // // XrlMfeaNode front-end interface // int enable_cli(); int disable_cli(); int start_cli(); int stop_cli(); int enable_mfea(); int disable_mfea(); int start_mfea(); int stop_mfea(); protected: // // XRL target methods // /** * Get name of Xrl Target */ XrlCmdError common_0_1_get_target_name( // Output values, string& name); /** * Get version string from Xrl Target */ XrlCmdError common_0_1_get_version( // Output values, string& version); /** * Get status from Xrl Target */ XrlCmdError common_0_1_get_status( // Output values, uint32_t& status, string& reason); /** * Shutdown cleanly */ XrlCmdError common_0_1_shutdown(); XrlCmdError common_0_1_startup() { return mfea_0_1_start_mfea(); } /** * Announce target birth to observer. * * @param target_class the target class name. * * @param target_instance the target instance name. */ XrlCmdError finder_event_observer_0_1_xrl_target_birth( // Input values, const string& target_class, const string& target_instance); /** * Announce target death to observer. * * @param target_class the target class name. * * @param target_instance the target instance name. */ XrlCmdError finder_event_observer_0_1_xrl_target_death( // Input values, const string& target_class, const string& target_instance); /** * Process a CLI command. * * @param processor_name the processor name for this command. * * @param cli_term_name the terminal name the command was entered from. * * @param cli_session_id the CLI session ID the command was entered from. * * @param command_name the command name to process. * * @param command_args the command arguments to process. * * @param ret_processor_name the processor name to return back to the CLI. * * @param ret_cli_term_name the terminal name to return back. * * @param ret_cli_session_id the CLI session ID to return back. * * @param ret_command_output the command output to return back. */ XrlCmdError cli_processor_0_1_process_command( // Input values, const string& processor_name, const string& cli_term_name, const uint32_t& cli_session_id, const string& command_name, const string& command_args, // Output values, string& ret_processor_name, string& ret_cli_term_name, uint32_t& ret_cli_session_id, string& ret_command_output); /** * Register remote mirror of interface state. */ XrlCmdError ifmgr_replicator_0_1_register_ifmgr_mirror( // Input values, const string& clientname); /** * Register remote mirror of interface state. */ XrlCmdError ifmgr_replicator_0_1_unregister_ifmgr_mirror( // Input values, const string& clientname); /** * Test if the underlying system supports IPv4 multicast routing. * * @param result true if the underlying system supports IPv4 multicast * routing, otherwise false. */ XrlCmdError mfea_0_1_have_multicast_routing4( // Output values, bool& result); #ifdef HAVE_IPV6 /** * Test if the underlying system supports IPv6 multicast routing. * * @param result true if the underlying system supports IPv6 multicast * routing, otherwise false. */ XrlCmdError mfea_0_1_have_multicast_routing6( // Output values, bool& result); #endif /** * Register a protocol on an interface in the Multicast FEA. There could * be only one registered protocol per interface/vif. * * @param xrl_sender_name the XRL name of the originator of this XRL. * * @param if_name the name of the interface to register for the particular * protocol. * * @param vif_name the name of the vif to register for the particular * protocol. * * @param ip_protocol the IP protocol number. It must be between 1 and * 255. */ XrlCmdError mfea_0_1_register_protocol4( // Input values, const string& xrl_sender_name, const string& if_name, const string& vif_name, const uint32_t& ip_protocol); #ifdef HAVE_IPV6 XrlCmdError mfea_0_1_register_protocol6( // Input values, const string& xrl_sender_name, const string& if_name, const string& vif_name, const uint32_t& ip_protocol); XrlCmdError mfea_0_1_unregister_protocol6( // Input values, const string& xrl_sender_name, const string& if_name, const string& vif_name); XrlCmdError mfea_0_1_add_mfc6( // Input values, const string& xrl_sender_name, const IPv6& source_address, const IPv6& group_address, const uint32_t& iif_vif_index, const vector& oiflist, const vector& oiflist_disable_wrongvif, const uint32_t& max_vifs_oiflist, const IPv6& rp_address); XrlCmdError mfea_0_1_delete_mfc6( // Input values, const string& xrl_sender_name, const IPv6& source_address, const IPv6& group_address); XrlCmdError mfea_0_1_add_dataflow_monitor6( // Input values, const string& xrl_sender_name, const IPv6& source_address, const IPv6& group_address, const uint32_t& threshold_interval_sec, const uint32_t& threshold_interval_usec, const uint32_t& threshold_packets, const uint32_t& threshold_bytes, const bool& is_threshold_in_packets, const bool& is_threshold_in_bytes, const bool& is_geq_upcall, const bool& is_leq_upcall); XrlCmdError mfea_0_1_delete_dataflow_monitor6( // Input values, const string& xrl_sender_name, const IPv6& source_address, const IPv6& group_address, const uint32_t& threshold_interval_sec, const uint32_t& threshold_interval_usec, const uint32_t& threshold_packets, const uint32_t& threshold_bytes, const bool& is_threshold_in_packets, const bool& is_threshold_in_bytes, const bool& is_geq_upcall, const bool& is_leq_upcall); XrlCmdError mfea_0_1_delete_all_dataflow_monitor6( // Input values, const string& xrl_sender_name, const IPv6& source_address, const IPv6& group_address); #endif //ipv6 /** * Unregister a protocol on an interface in the Multicast FEA. There could * be only one registered protocol per interface/vif. * * @param xrl_sender_name the XRL name of the originator of this XRL. * * @param if_name the name of the interface to unregister for the * particular protocol. * * @param vif_name the name of the vif to unregister for the particular * protocol. */ XrlCmdError mfea_0_1_unregister_protocol4( // Input values, const string& xrl_sender_name, const string& if_name, const string& vif_name); /** * Add/delete a Multicast Forwarding Cache with the kernel. * * @param xrl_sender_name the XRL name of the originator of this XRL. * * @param source_address the source address of the MFC to add/delete. * * @param group_address the group address of the MFC to add/delete. * * @param iif_vif_index the index of the vif that is the incoming * interface. * * @param oiflist the bit-vector with the set of outgoing interfaces. * * @param oiflist_disable_wrongvif the bit-vector with the set of outgoing * interfaces to disable WRONGVIF kernel signal. * * @param max_vifs_oiflist the number of vifs covered by oiflist or * oiflist_disable_wrongvif . * * @param rp_address the RP address of the MFC to add. */ XrlCmdError mfea_0_1_add_mfc4( // Input values, const string& xrl_sender_name, const IPv4& source_address, const IPv4& group_address, const uint32_t& iif_vif_index, const vector& oiflist, const vector& oiflist_disable_wrongvif, const uint32_t& max_vifs_oiflist, const IPv4& rp_address); XrlCmdError mfea_0_1_delete_mfc4( // Input values, const string& xrl_sender_name, const IPv4& source_address, const IPv4& group_address); /** * Add/delete a dataflow monitor with the MFEA. * * @param xrl_sender_name the XRL name of the originator of this XRL. * * @param source_address the source address of the dataflow to start/stop * monitoring. * * @param group_address the group address of the dataflow to start/stop * monitoring. * * @param threshold_interval_sec the number of seconds in the interval to * measure. * * @param threshold_interval_usec the number of microseconds in the * interval to measure. * * @param threshold_packets the threshold (in number of packets) to * compare against. * * @param threshold_bytes the threshold (in number of bytes) to compare * against. * * @param is_threshold_in_packets if true, threshold_packets is valid. * * @param is_threshold_in_bytes if true, threshold_bytes is valid. * * @param is_geq_upcall if true, the operation for comparison is ">=". * * @param is_leq_upcall if true, the operation for comparison is "<=". */ XrlCmdError mfea_0_1_add_dataflow_monitor4( // Input values, const string& xrl_sender_name, const IPv4& source_address, const IPv4& group_address, const uint32_t& threshold_interval_sec, const uint32_t& threshold_interval_usec, const uint32_t& threshold_packets, const uint32_t& threshold_bytes, const bool& is_threshold_in_packets, const bool& is_threshold_in_bytes, const bool& is_geq_upcall, const bool& is_leq_upcall); XrlCmdError mfea_0_1_delete_dataflow_monitor4( // Input values, const string& xrl_sender_name, const IPv4& source_address, const IPv4& group_address, const uint32_t& threshold_interval_sec, const uint32_t& threshold_interval_usec, const uint32_t& threshold_packets, const uint32_t& threshold_bytes, const bool& is_threshold_in_packets, const bool& is_threshold_in_bytes, const bool& is_geq_upcall, const bool& is_leq_upcall); XrlCmdError mfea_0_1_delete_all_dataflow_monitor4( // Input values, const string& xrl_sender_name, const IPv4& source_address, const IPv4& group_address); /** * Enable/disable/start/stop a MFEA vif interface. * * @param vif_name the name of the vif to enable/disable/start/stop. * * @param enable if true, then enable the vif, otherwise disable it. */ XrlCmdError mfea_0_1_enable_vif( // Input values, const string& vif_name, const bool& enable); XrlCmdError mfea_0_1_start_vif( // Input values, const string& vif_name); XrlCmdError mfea_0_1_stop_vif( // Input values, const string& vif_name); /** * Enable/disable/start/stop all MFEA vif interfaces. * * @param enable if true, then enable the vifs, otherwise disable them. */ XrlCmdError mfea_0_1_enable_all_vifs( // Input values, const bool& enable); XrlCmdError mfea_0_1_start_all_vifs(); XrlCmdError mfea_0_1_stop_all_vifs(); /** * Enable/disable/start/stop the MFEA. * * @param enable if true, then enable the MFEA, otherwise disable it. */ XrlCmdError mfea_0_1_enable_mfea( // Input values, const bool& enable); XrlCmdError mfea_0_1_start_mfea(); XrlCmdError mfea_0_1_stop_mfea(); /** * Enable/disable/start/stop the MFEA CLI access. * * @param enable if true, then enable the MFEA CLI access, otherwise * disable it. */ XrlCmdError mfea_0_1_enable_cli( // Input values, const bool& enable); XrlCmdError mfea_0_1_start_cli(); XrlCmdError mfea_0_1_stop_cli(); /** * Enable/disable the MFEA trace log for all operations. * * @param enable if true, then enable the trace log, otherwise disable it. */ XrlCmdError mfea_0_1_log_trace_all( // Input values, const bool& enable); private: /** * Called when Finder connection is established. * * Note that this method overwrites an XrlRouter virtual method. */ virtual void finder_connect_event(); /** * Called when Finder disconnect occurs. * * Note that this method overwrites an XrlRouter virtual method. */ virtual void finder_disconnect_event(); // // Protocol node methods // int signal_message_send(const string& dst_module_instance_name, int message_type, uint32_t vif_index, const IPvX& src, const IPvX& dst, const uint8_t *rcvbuf, size_t rcvlen); // // XXX: mfea_client_client_send_recv_kernel_signal_message_cb() in fact // is the callback when signal_message_send() calls // send_recv_kernel_signal_message() // so the destination protocol will receive a kernel message. // Sigh, the 'recv' part in the name is rather confusing, but that // is in the name of consistency between the XRL calling function // and the return result callback... // void mfea_client_client_send_recv_kernel_signal_message_cb(const XrlError& xrl_error); int dataflow_signal_send(const string& dst_module_instance_name, const IPvX& source_addr, const IPvX& group_addr, uint32_t threshold_interval_sec, uint32_t threshold_interval_usec, uint32_t measured_interval_sec, uint32_t measured_interval_usec, 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); void mfea_client_client_send_recv_dataflow_signal_cb(const XrlError& xrl_error); // // Protocol node CLI methods // int add_cli_command_to_cli_manager(const char *command_name, const char *command_help, bool is_command_cd, const char *command_cd_prompt, bool is_command_processor); void cli_manager_client_send_add_cli_command_cb(const XrlError& xrl_error); int delete_cli_command_from_cli_manager(const char *command_name); void cli_manager_client_send_delete_cli_command_cb(const XrlError& xrl_error); int family() const { return (MfeaNode::family()); } EventLoop& _eventloop; const string _finder_target; XrlMfeaClientV0p1Client _xrl_mfea_client_client; XrlCliManagerV0p1Client _xrl_cli_manager_client; XrlFinderEventNotifierV0p1Client _xrl_finder_client; LibFeaClientBridge _lib_mfea_client_bridge; // The MFEA clients bool _is_finder_alive; }; #endif // __FEA_XRL_MFEA_NODE_HH__ xorp/fea/io_ip.cc0000664000076400007640000000506411421137511014022 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2007-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "io_ip.hh" // // FEA I/O IP raw communication base class implementation. // IoIp::IoIp(FeaDataPlaneManager& fea_data_plane_manager, const IfTree& iftree, int family, uint8_t ip_protocol) : _is_running(false), _io_ip_manager(fea_data_plane_manager.io_ip_manager()), _fea_data_plane_manager(fea_data_plane_manager), _eventloop(fea_data_plane_manager.eventloop()), _iftree(iftree), _family(family), _ip_protocol(ip_protocol), _io_ip_receiver(NULL) { } IoIp::~IoIp() { } void IoIp::register_io_ip_receiver(IoIpReceiver* io_ip_receiver) { _io_ip_receiver = io_ip_receiver; } void IoIp::unregister_io_ip_receiver() { _io_ip_receiver = NULL; } void IoIp::recv_packet(const string& if_name, const string& vif_name, const IPvX& src_address, const IPvX& dst_address, int32_t ip_ttl, int32_t ip_tos, bool ip_router_alert, bool ip_internet_control, const vector& ext_headers_type, const vector >& ext_headers_payload, const vector& payload) { if (_io_ip_receiver == NULL) { // XXX: should happen only during transient setup stage return; } _io_ip_receiver->recv_packet(if_name, vif_name, src_address, dst_address, ip_ttl, ip_tos, ip_router_alert, ip_internet_control, ext_headers_type, ext_headers_payload, payload); } void IoIp::recv_system_multicast_upcall(const vector& payload) { if (_io_ip_receiver == NULL) { // XXX: should happen only during transient setup stage return; } _io_ip_receiver->recv_system_multicast_upcall(payload); } xorp/fea/fibconfig_forwarding.cc0000664000076400007640000000761611421137511017100 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "fibconfig.hh" #include "fibconfig_forwarding.hh" // // Configure unicast forwarding plugin (base class implementation). // FibConfigForwarding::FibConfigForwarding( FeaDataPlaneManager& fea_data_plane_manager) : _is_running(false), _fibconfig(fea_data_plane_manager.fibconfig()), _fea_data_plane_manager(fea_data_plane_manager), _orig_unicast_forwarding_enabled4(false), _orig_unicast_forwarding_enabled6(false), _orig_accept_rtadv_enabled6(false), _first_start(true) { } FibConfigForwarding::~FibConfigForwarding() { string error_msg; if (stop(error_msg) != XORP_OK) { XLOG_ERROR("Cannot stop the mechanism for manipulating " "the forwarding table information: %s", error_msg.c_str()); } } int FibConfigForwarding::start(string& error_msg) { if (_is_running) return (XORP_OK); if (_first_start) { // // Get the old state from the underlying system // if (fea_data_plane_manager().have_ipv4()) { if (unicast_forwarding_enabled4(_orig_unicast_forwarding_enabled4, error_msg) != XORP_OK) { XLOG_FATAL("%s", error_msg.c_str()); } } #ifdef HAVE_IPV6 if (fea_data_plane_manager().have_ipv6()) { if (unicast_forwarding_enabled6(_orig_unicast_forwarding_enabled6, error_msg) != XORP_OK) { XLOG_FATAL("%s", error_msg.c_str()); } if (accept_rtadv_enabled6(_orig_accept_rtadv_enabled6, error_msg) != XORP_OK) { XLOG_FATAL("%s", error_msg.c_str()); } } #endif // HAVE_IPV6 _first_start = false; } _is_running = true; return (XORP_OK); } int FibConfigForwarding::stop(string& error_msg) { int ret_value = XORP_OK; string error_msg2; if (! _is_running) return (XORP_OK); error_msg.erase(); // // Restore the old forwarding state in the underlying system. // // XXX: Note that if the XORP forwarding entries are retained on shutdown, // then we don't restore the state. // if (fea_data_plane_manager().have_ipv4()) { if (! fibconfig().unicast_forwarding_entries_retain_on_shutdown4()) { if (set_unicast_forwarding_enabled4(_orig_unicast_forwarding_enabled4, error_msg2) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } } } #ifdef HAVE_IPV6 if (fea_data_plane_manager().have_ipv6()) { if (! fibconfig().unicast_forwarding_entries_retain_on_shutdown6()) { if (set_unicast_forwarding_enabled6(_orig_unicast_forwarding_enabled6, error_msg2) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } if (set_accept_rtadv_enabled6(_orig_accept_rtadv_enabled6, error_msg2) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } } } #endif // HAVE_IPV6 _is_running = false; return (ret_value); } xorp/fea/xrl_fea_io.hh0000664000076400007640000000624611540225525015054 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2007-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __FEA_XRL_FEA_IO_HH__ #define __FEA_XRL_FEA_IO_HH__ // // FEA (Forwarding Engine Abstraction) XRL-based I/O implementation. // #include "fea_io.hh" class EventLoop; class XrlFeaNode; class XrlRouter; /** * @short FEA (Forwarding Engine Abstraction) XRL-based I/O class. */ class XrlFeaIo : public FeaIo { public: /** * Constructor. * * @param eventloop the event loop to use. * @param xrl_router the XRL transmission and reception point. * @param xrl_finder_targetname the XRL targetname of the Finder. */ XrlFeaIo(EventLoop& eventloop, XrlRouter& xrl_router, const string& xrl_finder_targetname); /** * Destructor */ virtual ~XrlFeaIo(); /** * Startup the service operation. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int startup(); /** * Shutdown the service operation. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int shutdown(); /** * Test whether the service is running. * * @return true if the service is still running, otherwise false. */ bool is_running() const; /** * Register interest in events relating to a particular instance. * * @param instance_name name of target instance to receive event * notifications for. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int register_instance_event_interest(const string& instance_name, string& error_msg); /** * Deregister interest in events relating to a particular instance. * * @param instance_name name of target instance to stop event * notifications for. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int deregister_instance_event_interest(const string& instance_name, string& error_msg); private: void register_instance_event_interest_cb(const XrlError& xrl_error, string instance_name); void deregister_instance_event_interest_cb(const XrlError& xrl_error, string instance_name); XrlRouter& _xrl_router; // The standard XRL send/recv point const string _xrl_finder_targetname; // The Finder target name }; #endif // __FEA_XRL_FEA_IO_HH__ xorp/fea/io_ip.hh0000664000076400007640000003172611540224224014040 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2007-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/io_ip.hh,v 1.5 2008/10/02 21:56:48 bms Exp $ #ifndef __FEA_IO_IP_HH__ #define __FEA_IO_IP_HH__ #include "fea_data_plane_manager.hh" // // I/O IP raw communication API. // class EventLoop; class IfTree; class IfTreeInterface; class IfTreeVif; class IoIpManager; class IoIpReceiver; class IPvX; class XorpFd; /** * @short A base class for I/O IP raw communication. * * Each protocol 'registers' for I/O and gets assigned one object * of this class. */ class IoIp { public: /** * Constructor for a given address family and protocol. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). * @param iftree the interface tree to use. * @param family the address family (AF_INET or AF_INET6 for IPv4 and IPv6 * respectively). * @param ip_protocol the IP protocol number (IPPROTO_*). */ IoIp(FeaDataPlaneManager& fea_data_plane_manager, const IfTree& iftree, int family, uint8_t ip_protocol); /** * Virtual destructor. */ virtual ~IoIp(); /** * Get the @ref IoIpManager instance. * * @return the @ref IoIpManager instance. */ IoIpManager& io_ip_manager() { return _io_ip_manager; } /** * Get the @ref FeaDataPlaneManager instance. * * @return the @ref FeaDataPlaneManager instance. */ FeaDataPlaneManager& fea_data_plane_manager() { return _fea_data_plane_manager; } /** * Test whether this instance is running. * * @return true if the instance is running, otherwise false. */ virtual bool is_running() const { return _is_running; } /** * Get the event loop. * * @return the event loop. */ EventLoop& eventloop() { return (_eventloop); } /** * Get the interface tree. * * @return the interface tree. */ const IfTree& iftree() const { return (_iftree); } /** * Get the address family. * * @return the address family. */ virtual int family() const { return (_family); } /** * Get the IP protocol number. * * @return the IP protocol number. */ virtual uint8_t ip_protocol() const { return (_ip_protocol); } /** * Get the registered receiver. * * @return the registered receiver. */ IoIpReceiver* io_ip_receiver() { return (_io_ip_receiver); } /** * Register the I/O IP raw packets receiver. * * @param io_ip_receiver the receiver to register. */ virtual void register_io_ip_receiver(IoIpReceiver* io_ip_receiver); /** * Unregister the I/O IP raw packets receiver. */ virtual void unregister_io_ip_receiver(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg) = 0; /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg) = 0; /** * Set the default TTL (or hop-limit in IPv6) for the outgoing multicast * packets. * * @param ttl the desired IP TTL (a.k.a. hop-limit in IPv6) value. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int set_multicast_ttl(int ttl, string& error_msg) = 0; /** * Enable/disable multicast loopback when transmitting multicast packets. * * If the multicast loopback is enabled, a transmitted multicast packet * will be delivered back to this host (assuming the host is a member of * the same multicast group). * * @param is_enabled if true, enable the loopback, otherwise disable it. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int enable_multicast_loopback(bool is_enabled, string& error_msg) = 0; /** * Set default interface for transmitting multicast packets. * * @param if_name the name of the interface that would become the default * multicast interface. * @param vif_name the name of the vif that would become the default * multicast interface. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int set_default_multicast_interface(const string& if_name, const string& vif_name, string& error_msg) = 0; /** * Join a multicast group on an interface. * * @param if_name the name of the interface to join the multicast group. * @param vif_name the name of the vif to join the multicast group. * @param group the multicast group to join. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int join_multicast_group(const string& if_name, const string& vif_name, const IPvX& group, string& error_msg) = 0; /** * Leave a multicast group on an interface. * * @param if_name the name of the interface to leave the multicast group. * @param vif_name the name of the vif to leave the multicast group. * @param group the multicast group to leave. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int leave_multicast_group(const string& if_name, const string& vif_name, const IPvX& group, string& error_msg) = 0; /** * Send a raw IP packet. * * @param if_name the interface to send the packet on. It is essential for * multicast. In the unicast case this field may be empty. * @param vif_name the vif to send the packet on. It is essential for * multicast. In the unicast case this field may be empty. * @param src_address the IP source address. * @param dst_address the IP destination address. * @param ip_ttl the IP TTL (hop-limit). If it has a negative value, * the TTL will be set internally before transmission. * @param ip_tos the Type Of Service (Diffserv/ECN bits for IPv4 or * IP traffic class for IPv6). If it has a negative value, the TOS will be * set internally before transmission. * @param ip_router_alert if true, then add the IP Router Alert option to * the IP packet. * @param ip_internet_control if true, then this is IP control traffic. * @param ext_headers_type a vector of integers with the types of the * optional IPv6 extention headers. * @param ext_headers_payload a vector of payload data, one for each * optional IPv6 extention header. The number of entries must match * ext_headers_type. * @param payload the payload, everything after the IP header and options. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int send_packet(const string& if_name, const string& vif_name, const IPvX& src_address, const IPvX& dst_address, int32_t ip_ttl, int32_t ip_tos, bool ip_router_alert, bool ip_internet_control, const vector& ext_headers_type, const vector >& ext_headers_payload, const vector& payload, string& error_msg) = 0; /** * Get the file descriptor for receiving protocol messages. * * @return a pointer to the file descriptor for receiving protocol * messages, or NULL if it cannot be found. */ virtual XorpFd* mcast_protocol_fd_in() = 0; protected: /** * Received a raw IP packet. * * @param if_name the interface name the packet arrived on. * @param vif_name the vif name the packet arrived on. * @param src_address the IP source address. * @param dst_address the IP destination address. * @param ip_ttl the IP TTL (hop-limit). If it has a negative value, * then the received value is unknown. * @param ip_tos The type of service (Diffserv/ECN bits for IPv4). If it * has a negative value, then the received value is unknown. * @param ip_router_alert if true, the IP Router Alert option was * included in the IP packet. * @param ip_internet_control if true, then this is IP control traffic. * @param ext_headers_type a vector of integers with the types of the * optional IPv6 extention headers. * @param ext_headers_payload a vector of payload data, one for each * optional IPv6 extention header. The number of entries must match * ext_headers_type. * @param packet the payload, everything after the IP header and * options. */ virtual void recv_packet(const string& if_name, const string& vif_name, const IPvX& src_address, const IPvX& dst_address, int32_t ip_ttl, int32_t ip_tos, bool ip_router_alert, bool ip_internet_control, const vector& ext_headers_type, const vector >& ext_headers_payload, const vector& payload); /** * Received a multicast forwarding related upcall from the system. * * Examples of such upcalls are: "nocache", "wrongiif", "wholepkt", * "bw_upcall". * * @param payload the payload data for the upcall. */ virtual void recv_system_multicast_upcall(const vector& payload); // Misc other state bool _is_running; private: IoIpManager& _io_ip_manager; // The I/O IP manager FeaDataPlaneManager& _fea_data_plane_manager; // The data plane manager EventLoop& _eventloop; // The event loop to use const IfTree& _iftree; // The interface tree to use int _family; // The address family uint8_t _ip_protocol; // The protocol number (IPPROTO_*) IoIpReceiver* _io_ip_receiver; // The registered receiver }; /** * @short A base class for I/O IP raw packets receiver. * * The real receiver must inherit from this class and register with the * corresponding IoIp entity to receive the raw IP packets. * @see IoIp. */ class IoIpReceiver { public: /** * Default constructor. */ IoIpReceiver() {} /** * Virtual destructor. */ virtual ~IoIpReceiver() {} /** * Received a raw IP packet. * * @param if_name the interface name the packet arrived on. * @param vif_name the vif name the packet arrived on. * @param src_address the IP source address. * @param dst_address the IP destination address. * @param ip_ttl the IP TTL (hop-limit). If it has a negative value, * then the received value is unknown. * @param ip_tos The type of service (Diffserv/ECN bits for IPv4). If it * has a negative value, then the received value is unknown. * @param ip_router_alert if true, the IP Router Alert option was * included in the IP packet. * @param ip_internet_control if true, then this is IP control traffic. * @param ext_headers_type a vector of integers with the types of the * optional IPv6 extention headers. * @param ext_headers_payload a vector of payload data, one for each * optional IPv6 extention header. The number of entries must match * ext_headers_type. * @param packet the payload, everything after the IP header and * options. */ virtual void recv_packet(const string& if_name, const string& vif_name, const IPvX& src_address, const IPvX& dst_address, int32_t ip_ttl, int32_t ip_tos, bool ip_router_alert, bool ip_internet_control, const vector& ext_headers_type, const vector >& ext_headers_payload, const vector& payload) = 0; /** * Received a multicast forwarding related upcall from the system. * * Examples of such upcalls are: "nocache", "wrongiif", "wholepkt", * "bw_upcall". * * @param payload the payload data for the upcall. */ virtual void recv_system_multicast_upcall(const vector& payload) = 0; }; #endif // __FEA_IO_IP_HH__ xorp/fea/README.mfea0000664000076400007640000000435411421137511014204 0ustar greearbgreearb# # $XORP: xorp/fea/README.mfea,v 1.20 2007/04/18 06:20:56 pavlin Exp $ # Multicast Forwarding Engine Abstraction ======================================= The main purpose of the MFEA is to abstract the underlying system and the multicast forwarding engine, and to provide a consistent interface to multicast-related modules such as PIM and MLD/IGMP. The MFEA is intended to fulfill several roles on behalf of multicast-related processes such as MLD/IGMP and PIM-SM: 1) Provides access to the multicast forwarding engine for processes such as PIM-SM. 2) Installs dataflow monitors into the kernel (if the underlying system supports it), or reads bandwidth forwarding statistics per dataflow. 3) Receives from the FEA information about network interfaces, and forwards this information to the multicast-related protocols registered with the MFEA. Note that the MFEA is a part of the unicast FEA process, though logically it is a separate entity. Configuration ============= MFEA like most XORP modules does not take its configuration parameters from the command line. Its parameters are provided via XRLs. At the very least a MFEA module must be provided with the set of network interfaces to enable for multicast forwarding. For information how to configure the MFEA see http://www.xorp.org/getting_started.html or http://www.xorp.org/releases/current/docs/user_manual/user_manual.pdf Startup ======= In normal operation, MFEA would be started by the XORP router manager process, not directly from the command line. In that case, make sure that IP forwarding is enabled in the XORP configuration file (see http://www.xorp.org/getting_started.html about information how to enable it). Documentation ============= The MFEA design architecture and code structure are described in: ${XORP}/docs/mfea/ The programming documentation is in: ${XORP}/docs/kdoc/html/fea/ Testing ======= Currently, the MFEA testing is performed manually, indirectly as part of the MLD/IGMP and PIM-SM testing. In the future, automated testing will be added, similar to the BGP testing framework. Status ====== Currently (July 2008), the MFEA supports abstraction for the following systems: * DragonFlyBSD, FreeBSD, NetBSD, OpenBSD * Linux xorp/fea/firewall_get.hh0000664000076400007640000000676211421137511015407 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2008-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/firewall_get.hh,v 1.3 2008/10/02 21:56:46 bms Exp $ #ifndef __FEA_FIREWALL_GET_HH__ #define __FEA_FIREWALL_GET_HH__ #include "firewall_entry.hh" #include "iftree.hh" #include "fea_data_plane_manager.hh" class FirewallManager; class FirewallGet { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ FirewallGet(FeaDataPlaneManager& fea_data_plane_manager) : _is_running(false), _firewall_manager(fea_data_plane_manager.firewall_manager()), _fea_data_plane_manager(fea_data_plane_manager) {} /** * Virtual destructor. */ virtual ~FirewallGet() {} /** * Get the @ref FirewallManager instance. * * @return the @ref FirewallManager instance. */ FirewallManager& firewall_manager() { return _firewall_manager; } /** * Get the @ref FeaDataPlaneManager instance. * * @return the @ref FeaDataPlaneManager instance. */ FeaDataPlaneManager& fea_data_plane_manager() { return _fea_data_plane_manager; } /** * Test whether this instance is running. * * @return true if the instance is running, otherwise false. */ virtual bool is_running() const { return _is_running; } /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg) = 0; /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg) = 0; /** * Obtain the IPv4 firewall table. * * @param firewall_entry_list the return-by-reference list with all * entries in the IPv4 firewall table. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int get_table4(list& firewall_entry_list, string& error_msg) = 0; /** * Obtain the IPv6 firewall table. * * @param firewall_entry_list the return-by-reference list with all * entries in the IPv6 firewall table. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int get_table6(list& firewall_entry_list, string& error_msg) = 0; protected: // Misc other state bool _is_running; private: FirewallManager& _firewall_manager; FeaDataPlaneManager& _fea_data_plane_manager; }; #endif // __FEA_FIREWALL_GET_HH__ xorp/fea/firewall_transaction.hh0000664000076400007640000001664111421137511017152 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2008-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/firewall_transaction.hh,v 1.5 2008/10/02 21:56:47 bms Exp $ #ifndef __FEA_FIREWALL_TRANSACTION_HH__ #define __FEA_FIREWALL_TRANSACTION_HH__ #include "libxorp/ipv4.hh" #include "libxorp/ipv4net.hh" #include "libxorp/ipv6.hh" #include "libxorp/ipv6net.hh" #include "libxorp/transaction.hh" #include "firewall_manager.hh" // // Firewall transactions. // /** * @short A class to store and execute firewall transactions. * * A firewall transaction is a sequence of commands that should * executed atomically. */ class FirewallTransactionManager : public TransactionManager { public: /** * Constructor. * * @param eventloop the event loop to use. * @see FirewallManager. */ FirewallTransactionManager(EventLoop& eventloop) : TransactionManager(eventloop, TIMEOUT_MS, MAX_PENDING) {} /** * Get the string with the first error during commit. * * @return the string with the first error during commit or an empty * string if no error. */ const string& error() const { return _first_error; } protected: /** * Pre-commit method that is called before the first operation * in a commit. * * This is an overriding method. * * @param tid the transaction ID. */ virtual void pre_commit(uint32_t tid); /** * Method that is called after each operation. * * This is an overriding method. * * @param success set to true if the operation succeeded, otherwise false. * @param op the operation that has been just called. */ virtual void operation_result(bool success, const TransactionOperation& op); private: /** * Reset the string with the error. */ void reset_error() { _first_error.erase(); } string _first_error; // The string with the first error uint32_t _tid_exec; // The transaction ID enum { TIMEOUT_MS = 5000, MAX_PENDING = 10 }; }; /** * Base class for operations that can occur during a firewall transaction. */ class FirewallTransactionOperation : public TransactionOperation { public: FirewallTransactionOperation(FirewallManager& firewall_manager) : _firewall_manager(firewall_manager) {} virtual ~FirewallTransactionOperation() {} /** * Get the error message with the reason for a failure. * * @return the error message with the reason for a failure or * an empty string if no failure. */ const string& error_reason() const { return (_error_reason); } protected: FirewallManager& firewall_manager() { return _firewall_manager; } string _error_reason; // The reason for a failure private: FirewallManager& _firewall_manager; }; class FirewallAddEntry4 : public FirewallTransactionOperation { public: FirewallAddEntry4(FirewallManager& firewall_manager, FirewallEntry& firewall_entry) : FirewallTransactionOperation(firewall_manager), _entry(firewall_entry) {} bool dispatch() { if (firewall_manager().add_entry(_entry, _error_reason) != XORP_OK) return (false); return (true); } string str() const { return c_format("AddEntry4: %s", _entry.str().c_str()); } private: FirewallEntry _entry; }; class FirewallReplaceEntry4 : public FirewallTransactionOperation { public: FirewallReplaceEntry4(FirewallManager& firewall_manager, FirewallEntry& firewall_entry) : FirewallTransactionOperation(firewall_manager), _entry(firewall_entry) {} bool dispatch() { if (firewall_manager().replace_entry(_entry, _error_reason) != XORP_OK) return (false); return (true); } string str() const { return c_format("ReplaceEntry4: %s", _entry.str().c_str()); } private: FirewallEntry _entry; }; class FirewallDeleteEntry4 : public FirewallTransactionOperation { public: FirewallDeleteEntry4(FirewallManager& firewall_manager, FirewallEntry& firewall_entry) : FirewallTransactionOperation(firewall_manager), _entry(firewall_entry) {} bool dispatch() { if (firewall_manager().delete_entry(_entry, _error_reason) != XORP_OK) return (false); return (true); } string str() const { return c_format("DeleteEntry4: %s", _entry.str().c_str()); } private: FirewallEntry _entry; }; class FirewallDeleteAllEntries4 : public FirewallTransactionOperation { public: FirewallDeleteAllEntries4(FirewallManager& firewall_manager) : FirewallTransactionOperation(firewall_manager) {} bool dispatch() { if (firewall_manager().delete_all_entries4(_error_reason) != XORP_OK) return (false); return (true); } string str() const { return c_format("DeleteAllEntries4"); } }; class FirewallAddEntry6 : public FirewallTransactionOperation { public: FirewallAddEntry6(FirewallManager& firewall_manager, FirewallEntry& firewall_entry) : FirewallTransactionOperation(firewall_manager), _entry(firewall_entry) {} bool dispatch() { if (firewall_manager().add_entry(_entry, _error_reason) != XORP_OK) return (false); return (true); } string str() const { return c_format("AddEntry6: %s", _entry.str().c_str()); } private: FirewallEntry _entry; }; class FirewallReplaceEntry6 : public FirewallTransactionOperation { public: FirewallReplaceEntry6(FirewallManager& firewall_manager, FirewallEntry& firewall_entry) : FirewallTransactionOperation(firewall_manager), _entry(firewall_entry) {} bool dispatch() { if (firewall_manager().replace_entry(_entry, _error_reason) != XORP_OK) return (false); return (true); } string str() const { return c_format("ReplaceEntry6: %s", _entry.str().c_str()); } private: FirewallEntry _entry; }; class FirewallDeleteEntry6 : public FirewallTransactionOperation { public: FirewallDeleteEntry6(FirewallManager& firewall_manager, FirewallEntry& firewall_entry) : FirewallTransactionOperation(firewall_manager), _entry(firewall_entry) {} bool dispatch() { if (firewall_manager().delete_entry(_entry, _error_reason) != XORP_OK) return (false); return (true); } string str() const { return c_format("DeleteEntry6: %s", _entry.str().c_str()); } private: FirewallEntry _entry; }; class FirewallDeleteAllEntries6 : public FirewallTransactionOperation { public: FirewallDeleteAllEntries6(FirewallManager& firewall_manager) : FirewallTransactionOperation(firewall_manager) {} bool dispatch() { if (firewall_manager().delete_all_entries6(_error_reason) != XORP_OK) return (false); return (true); } string str() const { return c_format("DeleteAllEntries6"); } }; #endif // __FEA_FIREWALL_TRANSACTION_HH__ xorp/fea/ifconfig_transaction.cc0000664000076400007640000000274611421137511017120 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "ifconfig_transaction.hh" void IfConfigTransactionManager::pre_commit(uint32_t tid) { reset_error(); _tid_exec = tid; } void IfConfigTransactionManager::operation_result(bool success, const TransactionOperation& op) { if (false == success && _first_error.empty()) { _first_error = c_format("Failed executing: \"%s\"", op.str().c_str()); flush(_tid_exec); } } bool RemoveInterface::dispatch() { if (ifconfig().remove_interface(ifname()) != XORP_OK) return (false); return (true); } xorp/fea/io_ip_manager.cc0000664000076400007640000011407211703345405015522 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2012 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "fea_node.hh" #include "iftree.hh" #include "io_ip_manager.hh" // // Filter class for checking incoming raw packets and checking whether // to forward them. // class IpVifInputFilter : public IoIpComm::InputFilter { public: IpVifInputFilter(IoIpManager& io_ip_manager, IoIpComm& io_ip_comm, const string& receiver_name, const string& if_name, const string& vif_name, uint8_t ip_protocol) : IoIpComm::InputFilter(io_ip_manager, receiver_name, ip_protocol), _io_ip_comm(io_ip_comm), _if_name(if_name), _vif_name(vif_name), _enable_multicast_loopback(false) {} virtual ~IpVifInputFilter() { leave_all_multicast_groups(); } void set_enable_multicast_loopback(bool v) { _enable_multicast_loopback = v; } void recv(const struct IPvXHeaderInfo& header, const vector& payload) { // Check the protocol if ((ip_protocol() != 0) && (ip_protocol() != header.ip_protocol)) { debug_msg("Ignore packet with protocol %u (watching for %u)\n", XORP_UINT_CAST(header.ip_protocol), XORP_UINT_CAST(ip_protocol())); return; } // Check the interface name if ((! _if_name.empty()) && (_if_name != header.if_name)) { debug_msg("Ignore packet with interface %s (watching for %s)\n", header.if_name.c_str(), _if_name.c_str()); return; } // Check the vif name if ((! _vif_name.empty()) && (_vif_name != header.vif_name)) { debug_msg("Ignore packet with vif %s (watching for %s)\n", header.vif_name.c_str(), _vif_name.c_str()); return; } // Check if multicast loopback is enabled if (header.dst_address.is_multicast() && is_my_address(header.src_address) && (! _enable_multicast_loopback)) { debug_msg("Ignore packet with src %s dst %s: " "multicast loopback is disabled\n", header.src_address.str().c_str(), header.dst_address.str().c_str()); return; } // Forward the packet io_ip_manager().recv_event(receiver_name(), header, payload); } void recv_system_multicast_upcall(const vector& payload) { // XXX: nothing to do UNUSED(payload); } void bye() {} const string& if_name() const { return _if_name; } const string& vif_name() const { return _vif_name; } int join_multicast_group(const IPvX& group_address, string& error_msg) { if (_io_ip_comm.join_multicast_group(if_name(), vif_name(), group_address, receiver_name(), error_msg) != XORP_OK) { return (XORP_ERROR); } _joined_multicast_groups.insert(group_address); return (XORP_OK); } int leave_multicast_group(const IPvX& group_address, string& error_msg) { _joined_multicast_groups.erase(group_address); if (_io_ip_comm.leave_multicast_group(if_name(), vif_name(), group_address, receiver_name(), error_msg) != XORP_OK) { return (XORP_ERROR); } return (XORP_OK); } void leave_all_multicast_groups() { string error_msg; while (! _joined_multicast_groups.empty()) { IPvX group_address = *(_joined_multicast_groups.begin()); leave_multicast_group(group_address, error_msg); } } protected: bool is_my_address(const IPvX& addr) const { const IfTreeInterface* ifp = NULL; const IfTreeVif* vifp = NULL; if (io_ip_manager().iftree().find_interface_vif_by_addr(IPvX(addr), ifp, vifp) != true) { return (false); } if (! (ifp->enabled() && vifp->enabled())) return (false); if (addr.is_ipv4()) { const IfTreeAddr4* ap = vifp->find_addr(addr.get_ipv4()); if ((ap != NULL) && (ap->enabled())) return (true); return (false); } if (addr.is_ipv6()) { const IfTreeAddr6* ap = vifp->find_addr(addr.get_ipv6()); if ((ap != NULL) && (ap->enabled())) return (true); return (false); } return (false); } IoIpComm& _io_ip_comm; const string _if_name; const string _vif_name; set _joined_multicast_groups; bool _enable_multicast_loopback; }; // // Filter class for checking system multicast upcalls and forwarding them. // class SystemMulticastUpcallFilter : public IoIpComm::InputFilter { public: SystemMulticastUpcallFilter(IoIpManager& io_ip_manager, IoIpComm& io_ip_comm, uint8_t ip_protocol, IoIpManager::UpcallReceiverCb& receiver_cb) : IoIpComm::InputFilter(io_ip_manager, "", ip_protocol), _io_ip_comm(io_ip_comm), _receiver_cb(receiver_cb) {} virtual ~SystemMulticastUpcallFilter() {} void set_receiver_cb(IoIpManager::UpcallReceiverCb& receiver_cb) { _receiver_cb = receiver_cb; } void recv(const struct IPvXHeaderInfo& header, const vector& payload) { // XXX: nothing to do UNUSED(header); UNUSED(payload); } void recv_system_multicast_upcall(const vector& payload) { // Forward the upcall if (! _receiver_cb.is_empty()) _receiver_cb->dispatch(&payload[0], payload.size()); } void bye() {} protected: IoIpComm& _io_ip_comm; IoIpManager::UpcallReceiverCb _receiver_cb; }; /* ------------------------------------------------------------------------- */ /* IoIpComm methods */ IoIpComm::IoIpComm(IoIpManager& io_ip_manager, const IfTree& iftree, int family, uint8_t ip_protocol) : IoIpReceiver(), _io_ip_manager(io_ip_manager), _iftree(iftree), _family(family), _ip_protocol(ip_protocol) { XLOG_WARNING("Creating IoIpComm, family: %i protocol: %i, tree: %s this: %p\n", (int)(_family), (int)(_ip_protocol), _iftree.getName().c_str(), this); } IoIpComm::~IoIpComm() { XLOG_WARNING("Deleting IoIpComm, family: %i protocol: %i, iftree: %s this: %p\n", (int)(_family), (int)(_ip_protocol), _iftree.getName().c_str(), this); deallocate_io_ip_plugins(); while (_input_filters.empty() == false) { InputFilter* i = _input_filters.front(); _input_filters.erase(_input_filters.begin()); i->bye(); } } int IoIpComm::add_filter(InputFilter* filter) { if (filter == NULL) { XLOG_FATAL("Adding a null filter"); return (XORP_ERROR); } if (find(_input_filters.begin(), _input_filters.end(), filter) != _input_filters.end()) { debug_msg("filter already exists\n"); return (XORP_ERROR); } _input_filters.push_back(filter); // // Allocate and start the IoIp plugins: one per data plane manager. // if (_input_filters.front() == filter) { XLOG_ASSERT(_io_ip_plugins.empty()); allocate_io_ip_plugins(); start_io_ip_plugins(); } return (XORP_OK); } int IoIpComm::remove_filter(InputFilter* filter) { list::iterator i; i = find(_input_filters.begin(), _input_filters.end(), filter); if (i == _input_filters.end()) { debug_msg("filter does not exist\n"); return (XORP_ERROR); } _input_filters.erase(i); if (_input_filters.empty()) { deallocate_io_ip_plugins(); } return (XORP_OK); } int IoIpComm::send_packet(const string& if_name, const string& vif_name, const IPvX& src_address, const IPvX& dst_address, int32_t ip_ttl, int32_t ip_tos, bool ip_router_alert, bool ip_internet_control, const vector& ext_headers_type, const vector >& ext_headers_payload, const vector& payload, string& error_msg) { int ret_value = XORP_OK; string error_msg2; if (_io_ip_plugins.empty()) { error_msg = c_format("No I/O IP plugin to send a raw IP packet on " "interface %s vif %s from %s to %s protocol %u", if_name.c_str(), vif_name.c_str(), src_address.str().c_str(), dst_address.str().c_str(), _ip_protocol); return (XORP_ERROR); } IoIpPlugins::iterator iter; for (iter = _io_ip_plugins.begin(); iter != _io_ip_plugins.end(); ++iter) { IoIp* io_ip = iter->second; if (io_ip->send_packet(if_name, vif_name, src_address, dst_address, ip_ttl, ip_tos, ip_router_alert, ip_internet_control, ext_headers_type, ext_headers_payload, payload, error_msg2) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += c_format("Error while sending to vif: %s:%s src: %s dest: %s: ", if_name.c_str(), vif_name.c_str(), src_address.str().c_str(), dst_address.str().c_str()); error_msg += error_msg2; } } return (ret_value); } void IoIpComm::recv_packet(const string& if_name, const string& vif_name, const IPvX& src_address, const IPvX& dst_address, int32_t ip_ttl, int32_t ip_tos, bool ip_router_alert, bool ip_internet_control, const vector& ext_headers_type, const vector >& ext_headers_payload, const vector& payload) { struct IPvXHeaderInfo header; header.if_name = if_name; header.vif_name = vif_name; header.src_address = src_address; header.dst_address = dst_address; header.ip_protocol = _ip_protocol; header.ip_ttl = ip_ttl; header.ip_tos = ip_tos; header.ip_router_alert = ip_router_alert; header.ip_internet_control = ip_internet_control; header.ext_headers_type = ext_headers_type; header.ext_headers_payload = ext_headers_payload; for (list::iterator i = _input_filters.begin(); i != _input_filters.end(); ++i) { (*i)->recv(header, payload); } } void IoIpComm::recv_system_multicast_upcall(const vector& payload) { for (list::iterator i = _input_filters.begin(); i != _input_filters.end(); ++i) { (*i)->recv_system_multicast_upcall(payload); } } int IoIpComm::join_multicast_group(const string& if_name, const string& vif_name, const IPvX& group_address, const string& receiver_name, string& error_msg) { int ret_value = XORP_OK; string error_msg2; if (_io_ip_plugins.empty()) { error_msg = c_format("No I/O IP plugin to join group %s " "on interface %s vif %s protocol %u " "receiver name %s", group_address.str().c_str(), if_name.c_str(), vif_name.c_str(), _ip_protocol, receiver_name.c_str()); return (XORP_ERROR); } // // Check the arguments // if (! group_address.is_multicast()) { error_msg = c_format("Cannot join group %s: not a multicast address", group_address.str().c_str()); return (XORP_ERROR); } if (if_name.empty()) { error_msg = c_format("Cannot join group %s: empty interface name", group_address.str().c_str()); return (XORP_ERROR); } if (vif_name.empty()) { error_msg = c_format("Cannot join group %s on interface %s: " "empty vif name", group_address.str().c_str(), if_name.c_str()); return (XORP_ERROR); } if (receiver_name.empty()) { error_msg = c_format("Cannot join group %s on interface %s vif %s: " "empty receiver name", group_address.str().c_str(), if_name.c_str(), vif_name.c_str()); return (XORP_ERROR); } JoinedGroupsTable::iterator joined_iter; JoinedMulticastGroup init_jmg(if_name, vif_name, group_address); joined_iter = _joined_groups_table.find(init_jmg); if (joined_iter == _joined_groups_table.end()) { // // First receiver, hence join the multicast group first to check // for errors. // IoIpPlugins::iterator plugin_iter; for (plugin_iter = _io_ip_plugins.begin(); plugin_iter != _io_ip_plugins.end(); ++plugin_iter) { IoIp* io_ip = plugin_iter->second; if (io_ip->join_multicast_group(if_name, vif_name, group_address, error_msg2) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } } _joined_groups_table.insert(make_pair(init_jmg, init_jmg)); joined_iter = _joined_groups_table.find(init_jmg); } XLOG_ASSERT(joined_iter != _joined_groups_table.end()); JoinedMulticastGroup& jmg = joined_iter->second; jmg.add_receiver(receiver_name); return (ret_value); } int IoIpComm::leave_all_multicast_groups(const string& if_name, const string& vif_name, string& error_msg) { //XLOG_ERROR("IoIpComm::leave_all_multicast_groups, if: %s vif: %s\n", //if_name.c_str(), vif_name.c_str()); start: { JoinedGroupsTable::iterator joined_iter; for (joined_iter = _joined_groups_table.begin(); joined_iter != _joined_groups_table.end(); joined_iter++) { //XLOG_ERROR("Found group, if: %s vif: %s\n", // joined_iter->second.if_name().c_str(), // joined_iter->second.vif_name().c_str()); if ((joined_iter->second.if_name() == if_name) && ((vif_name.size() == 0) || (joined_iter->second.vif_name() == vif_name))) { string tmp_vifn(joined_iter->second.vif_name()); //XLOG_ERROR("Matched, looking for receivers.\n"); set::iterator ri = joined_iter->second.get_receivers().begin(); while (ri != joined_iter->second.get_receivers().end()) { //XLOG_ERROR("Found receiver: %s\n", ri->c_str()); leave_multicast_group(if_name, tmp_vifn, joined_iter->second.group_address(), *ri, error_msg); // Maybe there are more...back out to top and search again. This should keep our iterators // from being corrupted by the leave_multicast_group call. goto start; }//for all receivers }//if this joined group matches our interface and vif (if specified) }//for all joined groups }//scoping brace // We delete all we can find...whatever we found must have been OK. return XORP_OK; } int IoIpComm::leave_multicast_group(const string& if_name, const string& vif_name, const IPvX& group_address, const string& receiver_name, string& error_msg) { int ret_value = XORP_OK; string error_msg2; if (_io_ip_plugins.empty()) { error_msg = c_format("No I/O IP plugin to leave group %s " "on interface %s vif %s protocol %u " "receiver name %s", group_address.str().c_str(), if_name.c_str(), vif_name.c_str(), _ip_protocol, receiver_name.c_str()); return (XORP_ERROR); } JoinedGroupsTable::iterator joined_iter; JoinedMulticastGroup init_jmg(if_name, vif_name, group_address); joined_iter = _joined_groups_table.find(init_jmg); if (joined_iter == _joined_groups_table.end()) { error_msg = c_format("Cannot leave group %s on interface %s vif %s: " "the group was not joined (will continue)", group_address.str().c_str(), if_name.c_str(), vif_name.c_str()); XLOG_WARNING("%s", error_msg.c_str()); // Don't fail this..would fail the whole commit, and the group isn't joined // anyway, so no loss of validity in the configuration. return XORP_OK; } JoinedMulticastGroup& jmg = joined_iter->second; jmg.delete_receiver(receiver_name); if (jmg.empty()) { // // The last receiver, hence leave the group // _joined_groups_table.erase(joined_iter); IoIpPlugins::iterator plugin_iter; for (plugin_iter = _io_ip_plugins.begin(); plugin_iter != _io_ip_plugins.end(); ++plugin_iter) { IoIp* io_ip = plugin_iter->second; if (io_ip->leave_multicast_group(if_name, vif_name, group_address, error_msg2) != XORP_OK) { ret_value = XORP_ERROR; if (! error_msg.empty()) error_msg += " "; error_msg += error_msg2; } } } return (ret_value); } void IoIpComm::allocate_io_ip_plugins() { list::iterator iter; for (iter = _io_ip_manager.fea_data_plane_managers().begin(); iter != _io_ip_manager.fea_data_plane_managers().end(); ++iter) { FeaDataPlaneManager* fea_data_plane_manager = *iter; allocate_io_ip_plugin(fea_data_plane_manager); } } void IoIpComm::deallocate_io_ip_plugins() { while (! _io_ip_plugins.empty()) { IoIpPlugins::iterator iter = _io_ip_plugins.begin(); FeaDataPlaneManager* fea_data_plane_manager = iter->first; deallocate_io_ip_plugin(fea_data_plane_manager); } } void IoIpComm::allocate_io_ip_plugin(FeaDataPlaneManager* fea_data_plane_manager) { IoIpPlugins::iterator iter; XLOG_ASSERT(fea_data_plane_manager != NULL); for (iter = _io_ip_plugins.begin(); iter != _io_ip_plugins.end(); ++iter) { if (iter->first == fea_data_plane_manager) break; } if (iter != _io_ip_plugins.end()) { return; // XXX: the plugin was already allocated } IoIp* io_ip = fea_data_plane_manager->allocate_io_ip(_iftree, _family, _ip_protocol); if (io_ip == NULL) { XLOG_ERROR("Couldn't allocate plugin for I/O IP raw " "communications for data plane manager %s", fea_data_plane_manager->manager_name().c_str()); return; } _io_ip_plugins.push_back(make_pair(fea_data_plane_manager, io_ip)); } void IoIpComm::deallocate_io_ip_plugin(FeaDataPlaneManager* fea_data_plane_manager) { IoIpPlugins::iterator iter; XLOG_ASSERT(fea_data_plane_manager != NULL); for (iter = _io_ip_plugins.begin(); iter != _io_ip_plugins.end(); ++iter) { if (iter->first == fea_data_plane_manager) break; } if (iter == _io_ip_plugins.end()) { XLOG_ERROR("Couldn't deallocate plugin for I/O IP raw " "communications for data plane manager %s: plugin not found", fea_data_plane_manager->manager_name().c_str()); return; } IoIp* io_ip = iter->second; fea_data_plane_manager->deallocate_io_ip(io_ip); _io_ip_plugins.erase(iter); } void IoIpComm::start_io_ip_plugins() { IoIpPlugins::iterator iter; string error_msg; for (iter = _io_ip_plugins.begin(); iter != _io_ip_plugins.end(); ++iter) { IoIp* io_ip = iter->second; if (io_ip->is_running()) continue; io_ip->register_io_ip_receiver(this); if (io_ip->start(error_msg) != XORP_OK) { XLOG_ERROR("%s", error_msg.c_str()); continue; } // // Push all multicast joins into the new plugin // JoinedGroupsTable::iterator join_iter; for (join_iter = _joined_groups_table.begin(); join_iter != _joined_groups_table.end(); ++join_iter) { JoinedMulticastGroup& joined_multicast_group = join_iter->second; if (io_ip->join_multicast_group(joined_multicast_group.if_name(), joined_multicast_group.vif_name(), joined_multicast_group.group_address(), error_msg) != XORP_OK) { XLOG_ERROR("%s", error_msg.c_str()); } } } } void IoIpComm::stop_io_ip_plugins() { string error_msg; IoIpPlugins::iterator iter; for (iter = _io_ip_plugins.begin(); iter != _io_ip_plugins.end(); ++iter) { IoIp* io_ip = iter->second; io_ip->unregister_io_ip_receiver(); if (io_ip->stop(error_msg) != XORP_OK) { XLOG_ERROR("%s", error_msg.c_str()); } } } XorpFd IoIpComm::first_valid_mcast_protocol_fd_in() { XorpFd xorp_fd; // Find the first valid file descriptor and return it IoIpPlugins::iterator iter; for (iter = _io_ip_plugins.begin(); iter != _io_ip_plugins.end(); ++iter) { IoIp* io_ip = iter->second; XorpFd* xfd = io_ip->mcast_protocol_fd_in(); if (xfd && xfd->is_valid()) { xorp_fd = *xfd; return xorp_fd; } } // XXX: nothing found return xorp_fd; } // ---------------------------------------------------------------------------- // IoIpManager code IoIpManager::IoIpManager(FeaNode& fea_node, const IfTree& iftree) : IoIpManagerReceiver(), _fea_node(fea_node), _eventloop(fea_node.eventloop()), _iftree(iftree), _io_ip_manager_receiver(NULL) { } IoIpManager::~IoIpManager() { erase_filters(_comm_table4, _filters4, _filters4.begin(), _filters4.end()); erase_filters(_comm_table6, _filters6, _filters6.begin(), _filters6.end()); } IoIpManager::CommTable& IoIpManager::comm_table_by_family(int family) { if (family == IPv4::af()) return (_comm_table4); if (family == IPv6::af()) return (_comm_table6); XLOG_FATAL("Invalid address family: %d", family); return (_comm_table4); } IoIpManager::FilterBag& IoIpManager::filters_by_family(int family) { if (family == IPv4::af()) return (_filters4); if (family == IPv6::af()) return (_filters6); XLOG_FATAL("Invalid address family: %d", family); return (_filters4); } void IoIpManager::recv_event(const string& receiver_name, const struct IPvXHeaderInfo& header, const vector& payload) { if (_io_ip_manager_receiver != NULL) _io_ip_manager_receiver->recv_event(receiver_name, header, payload); } void IoIpManager::erase_filters_by_receiver_name(int family, const string& receiver_name) { CommTable& comm_table = comm_table_by_family(family); FilterBag& filters = filters_by_family(family); erase_filters(comm_table, filters, filters.lower_bound(receiver_name), filters.upper_bound(receiver_name)); } bool IoIpManager::has_filter_by_receiver_name(const string& receiver_name) const { // There whether there is an entry for a given receiver name if (_filters4.find(receiver_name) != _filters4.end()) return (true); if (_filters6.find(receiver_name) != _filters6.end()) return (true); return (false); } void IoIpManager::erase_filters(CommTable& comm_table, FilterBag& filters, const FilterBag::iterator& begin, const FilterBag::iterator& end) { FilterBag::iterator fi(begin); while (fi != end) { IoIpComm::InputFilter* filter = fi->second; CommTable::iterator cti = comm_table.find(filter->ip_protocol()); XLOG_ASSERT(cti != comm_table.end()); IoIpComm* io_ip_comm = cti->second; XLOG_ASSERT(io_ip_comm != NULL); io_ip_comm->remove_filter(filter); delete filter; filters.erase(fi++); // // Reference counting: if there are now no listeners on // this protocol socket (and hence no filters), remove it // from the table and delete it. // if (io_ip_comm->no_input_filters()) { XLOG_WARNING("Unregister receiver (erase_filters), protocol: %i\n", (int)(io_ip_comm->ip_protocol())); comm_table.erase(io_ip_comm->ip_protocol()); delete io_ip_comm; } } } int IoIpManager::send(const string& if_name, const string& vif_name, const IPvX& src_address, const IPvX& dst_address, uint8_t ip_protocol, int32_t ip_ttl, int32_t ip_tos, bool ip_router_alert, bool ip_internet_control, const vector& ext_headers_type, const vector >& ext_headers_payload, const vector& payload, string& error_msg) { CommTable& comm_table = comm_table_by_family(src_address.af()); // Find the IoIpComm associated with this protocol CommTable::iterator cti = comm_table.find(ip_protocol); if (cti == comm_table.end()) { error_msg = c_format("%s: Protocol %u is not registered", __FUNCTION__, XORP_UINT_CAST(ip_protocol)); return (XORP_ERROR); } IoIpComm* io_ip_comm = cti->second; XLOG_ASSERT(io_ip_comm != NULL); return (io_ip_comm->send_packet(if_name, vif_name, src_address, dst_address, ip_ttl, ip_tos, ip_router_alert, ip_internet_control, ext_headers_type, ext_headers_payload, payload, error_msg)); } int IoIpManager::register_receiver(int family, const string& receiver_name, const string& if_name, const string& vif_name, uint8_t ip_protocol, bool enable_multicast_loopback, string& error_msg) { IpVifInputFilter* filter; CommTable& comm_table = comm_table_by_family(family); FilterBag& filters = filters_by_family(family); error_msg = ""; // // Look in the CommTable for an entry matching this protocol. // If an entry does not yet exist, create one. // CommTable::iterator cti = comm_table.find(ip_protocol); IoIpComm* io_ip_comm = NULL; if (cti == comm_table.end()) { XLOG_WARNING("Creating new receiver, name: %s iface: %s protocol: %i family: %i\n", receiver_name.c_str(), if_name.c_str(), (int)(ip_protocol), family); io_ip_comm = new IoIpComm(*this, iftree(), family, ip_protocol); comm_table[ip_protocol] = io_ip_comm; } else { io_ip_comm = cti->second; } XLOG_ASSERT(io_ip_comm != NULL); // // Walk through list of filters looking for matching filter // FilterBag::iterator fi; const FilterBag::iterator fi_end = filters.upper_bound(receiver_name); for (fi = filters.lower_bound(receiver_name); fi != fi_end; ++fi) { filter = dynamic_cast(fi->second); if (filter == NULL) continue; // Not a vif filter // // Search if we have already the filter // if ((filter->ip_protocol() == ip_protocol) && (filter->if_name() == if_name) && (filter->vif_name() == vif_name)) { // Already have this filter filter->set_enable_multicast_loopback(enable_multicast_loopback); return (XORP_OK); } } // // Create the filter // filter = new IpVifInputFilter(*this, *io_ip_comm, receiver_name, if_name, vif_name, ip_protocol); filter->set_enable_multicast_loopback(enable_multicast_loopback); // Add the filter to the appropriate IoIpComm entry io_ip_comm->add_filter(filter); // Add the filter to those associated with receiver_name filters.insert(FilterBag::value_type(receiver_name, filter)); // Register interest in watching the receiver if (_fea_node.fea_io().add_instance_watch(receiver_name, this, error_msg) != XORP_OK) { string dummy_error_msg; unregister_receiver(family, receiver_name, if_name, vif_name, ip_protocol, dummy_error_msg); return (XORP_ERROR); } return (XORP_OK); } int IoIpManager::unregister_receiver(int family, const string& receiver_name, const string& if_name, const string& vif_name, uint8_t ip_protocol, string& error_msg) { CommTable& comm_table = comm_table_by_family(family); FilterBag& filters = filters_by_family(family); // // Find the IoIpComm entry associated with this protocol // CommTable::iterator cti = comm_table.find(ip_protocol); if (cti == comm_table.end()) { error_msg = c_format("%s: Protocol %u is not registered", __FUNCTION__, XORP_UINT_CAST(ip_protocol)); return (XORP_ERROR); } IoIpComm* io_ip_comm = cti->second; XLOG_ASSERT(io_ip_comm != NULL); // // Walk through list of filters looking for matching interface and vif // FilterBag::iterator fi; FilterBag::iterator fi_end = filters.upper_bound(receiver_name); for (fi = filters.lower_bound(receiver_name); fi != fi_end; ++fi) { IpVifInputFilter* filter; filter = dynamic_cast(fi->second); if (filter == NULL) continue; // Not a vif filter // If filter found, remove it and delete it if ((filter->ip_protocol() == ip_protocol) && (filter->if_name() == if_name) && (filter->vif_name() == vif_name)) { // Remove the filter io_ip_comm->remove_filter(filter); // Remove the filter from the group associated with this receiver filters.erase(fi); // Destruct the filter delete filter; // // Reference counting: if there are now no listeners on // this protocol socket (and hence no filters), remove it // from the table and delete it. // if (io_ip_comm->no_input_filters()) { XLOG_WARNING("Unregister receiver, protocol: %i family: %i\n", (int)(ip_protocol), family); comm_table.erase(ip_protocol); delete io_ip_comm; } // Deregister interest in watching the receiver if (! has_filter_by_receiver_name(receiver_name)) { string dummy_error_msg; _fea_node.fea_io().delete_instance_watch(receiver_name, this, dummy_error_msg); } return (XORP_OK); } } error_msg = c_format("Cannot find registration for receiver %s " "protocol %u interface %s and vif %s", receiver_name.c_str(), XORP_UINT_CAST(ip_protocol), if_name.c_str(), vif_name.c_str()); return (XORP_ERROR); } int IoIpManager::join_multicast_group(const string& receiver_name, const string& if_name, const string& vif_name, uint8_t ip_protocol, const IPvX& group_address, string& error_msg) { FilterBag& filters = filters_by_family(group_address.af()); // // Search if we have already the filter // FilterBag::iterator fi; FilterBag::iterator fi_end = filters.upper_bound(receiver_name); for (fi = filters.lower_bound(receiver_name); fi != fi_end; ++fi) { IpVifInputFilter* filter; filter = dynamic_cast(fi->second); if (filter == NULL) continue; // Not a vif filter if ((filter->ip_protocol() == ip_protocol) && (filter->if_name() == if_name) && (filter->vif_name() == vif_name)) { // Filter found if (filter->join_multicast_group(group_address, error_msg) != XORP_OK) { return (XORP_ERROR); } return (XORP_OK); } } error_msg = c_format("Cannot join group %s on interface %s vif %s " "protocol %u receiver %s: not registered", group_address.str().c_str(), if_name.c_str(), vif_name.c_str(), XORP_UINT_CAST(ip_protocol), receiver_name.c_str()); return (XORP_ERROR); } int IoIpManager::leave_all_multicast_groups(const string& if_name, const string& vif_name, string& error_msg) { CommTable::iterator cti; for (cti = _comm_table4.begin(); cti != _comm_table4.end(); cti++) { IoIpComm* c = cti->second; c->leave_all_multicast_groups(if_name, vif_name, error_msg); } return XORP_OK; } int IoIpManager::leave_multicast_group(const string& receiver_name, const string& if_name, const string& vif_name, uint8_t ip_protocol, const IPvX& group_address, string& error_msg) { FilterBag& filters = filters_by_family(group_address.af()); // // Search if we have already the filter // FilterBag::iterator fi; FilterBag::iterator fi_end = filters.upper_bound(receiver_name); for (fi = filters.lower_bound(receiver_name); fi != fi_end; ++fi) { IpVifInputFilter* filter; filter = dynamic_cast(fi->second); if (filter == NULL) continue; // Not a vif filter if ((filter->ip_protocol() == ip_protocol) && (filter->if_name() == if_name) && (filter->vif_name() == vif_name)) { // Filter found if (filter->leave_multicast_group(group_address, error_msg) != XORP_OK) { return (XORP_ERROR); } return (XORP_OK); } } error_msg = c_format("Cannot leave group %s on interface %s vif %s " "protocol %u receiver %s: not registered", group_address.str().c_str(), if_name.c_str(), vif_name.c_str(), XORP_UINT_CAST(ip_protocol), receiver_name.c_str()); return (XORP_ERROR); } int IoIpManager::register_system_multicast_upcall_receiver( int family, uint8_t ip_protocol, IoIpManager::UpcallReceiverCb receiver_cb, XorpFd& mcast_receiver_fd, string& error_msg) { SystemMulticastUpcallFilter* filter; CommTable& comm_table = comm_table_by_family(family); FilterBag& filters = filters_by_family(family); error_msg = ""; // // Look in the CommTable for an entry matching this protocol. // If an entry does not yet exist, create one. // CommTable::iterator cti = comm_table.find(ip_protocol); IoIpComm* io_ip_comm = NULL; if (cti == comm_table.end()) { XLOG_WARNING("Creating new mcast protocol: %i family: %i\n", (int)(ip_protocol), family); io_ip_comm = new IoIpComm(*this, iftree(), family, ip_protocol); comm_table[ip_protocol] = io_ip_comm; } else { io_ip_comm = cti->second; } XLOG_ASSERT(io_ip_comm != NULL); // // Walk through list of filters looking for matching filter // string receiver_name; // XXX: empty receiver name FilterBag::iterator fi; FilterBag::iterator fi_end = filters.upper_bound(receiver_name); for (fi = filters.lower_bound(receiver_name); fi != fi_end; ++fi) { filter = dynamic_cast(fi->second); if (filter == NULL) continue; // Not an upcall filter // // Search if we have already the filter // if (filter->ip_protocol() == ip_protocol) { // Already have this filter filter->set_receiver_cb(receiver_cb); mcast_receiver_fd = io_ip_comm->first_valid_mcast_protocol_fd_in(); return (XORP_OK); } } // // Create the filter // filter = new SystemMulticastUpcallFilter(*this, *io_ip_comm, ip_protocol, receiver_cb); // Add the filter to the appropriate IoIpComm entry io_ip_comm->add_filter(filter); // Add the filter to those associated with empty receiver_name filters.insert(FilterBag::value_type(receiver_name, filter)); mcast_receiver_fd = io_ip_comm->first_valid_mcast_protocol_fd_in(); return (XORP_OK); } int IoIpManager::unregister_system_multicast_upcall_receiver( int family, uint8_t ip_protocol, string& error_msg) { CommTable& comm_table = comm_table_by_family(family); FilterBag& filters = filters_by_family(family); // // Find the IoIpComm entry associated with this protocol // CommTable::iterator cti = comm_table.find(ip_protocol); if (cti == comm_table.end()) { error_msg = c_format("%s: Protocol %u is not registered", __FUNCTION__, XORP_UINT_CAST(ip_protocol)); return (XORP_ERROR); } IoIpComm* io_ip_comm = cti->second; XLOG_ASSERT(io_ip_comm != NULL); // // Walk through list of filters looking for matching filter // string receiver_name; // XXX: empty receiver name FilterBag::iterator fi; FilterBag::iterator fi_end = filters.upper_bound(receiver_name); for (fi = filters.lower_bound(receiver_name); fi != fi_end; ++fi) { SystemMulticastUpcallFilter* filter; filter = dynamic_cast(fi->second); if (filter == NULL) continue; // Not an upcall filter // If filter found, remove it and delete it if (filter->ip_protocol() == ip_protocol) { // Remove the filter io_ip_comm->remove_filter(filter); // Remove the filter from the group associated with this receiver filters.erase(fi); // Destruct the filter delete filter; // // Reference counting: if there are now no listeners on // this protocol socket (and hence no filters), remove it // from the table and delete it. // if (io_ip_comm->no_input_filters()) { XLOG_WARNING("Unregister mcast receiver, protocol: %i family: %i\n", (int)(ip_protocol), family); comm_table.erase(ip_protocol); delete io_ip_comm; } return (XORP_OK); } } error_msg = c_format("Cannot find registration for upcall receiver " "family %d and protocol %d", family, ip_protocol); return (XORP_ERROR); } int IoIpManager::register_data_plane_manager(FeaDataPlaneManager* fea_data_plane_manager, bool is_exclusive) { if (is_exclusive) { // Unregister all registered data plane managers while (! _fea_data_plane_managers.empty()) { unregister_data_plane_manager(_fea_data_plane_managers.front()); } } if (fea_data_plane_manager == NULL) { // XXX: exclusive NULL is used to unregister all data plane managers return (XORP_OK); } if (find(_fea_data_plane_managers.begin(), _fea_data_plane_managers.end(), fea_data_plane_manager) != _fea_data_plane_managers.end()) { // XXX: already registered return (XORP_OK); } _fea_data_plane_managers.push_back(fea_data_plane_manager); // // Allocate all I/O IP plugins for the new data plane manager // CommTable::iterator iter; for (iter = _comm_table4.begin(); iter != _comm_table4.end(); ++iter) { IoIpComm* io_ip_comm = iter->second; io_ip_comm->allocate_io_ip_plugin(fea_data_plane_manager); io_ip_comm->start_io_ip_plugins(); } for (iter = _comm_table6.begin(); iter != _comm_table6.end(); ++iter) { IoIpComm* io_ip_comm = iter->second; io_ip_comm->allocate_io_ip_plugin(fea_data_plane_manager); io_ip_comm->start_io_ip_plugins(); } return (XORP_OK); } int IoIpManager::unregister_data_plane_manager(FeaDataPlaneManager* fea_data_plane_manager) { if (fea_data_plane_manager == NULL) return (XORP_ERROR); list::iterator data_plane_manager_iter; data_plane_manager_iter = find(_fea_data_plane_managers.begin(), _fea_data_plane_managers.end(), fea_data_plane_manager); if (data_plane_manager_iter == _fea_data_plane_managers.end()) return (XORP_ERROR); // // Dealocate all I/O IP plugins for the unregistered data plane manager // CommTable::iterator iter; for (iter = _comm_table4.begin(); iter != _comm_table4.end(); ++iter) { IoIpComm* io_ip_comm = iter->second; io_ip_comm->deallocate_io_ip_plugin(fea_data_plane_manager); } for (iter = _comm_table6.begin(); iter != _comm_table6.end(); ++iter) { IoIpComm* io_ip_comm = iter->second; io_ip_comm->deallocate_io_ip_plugin(fea_data_plane_manager); } _fea_data_plane_managers.erase(data_plane_manager_iter); return (XORP_OK); } void IoIpManager::instance_birth(const string& instance_name) { // XXX: Nothing to do UNUSED(instance_name); } void IoIpManager::instance_death(const string& instance_name) { string dummy_error_msg; _fea_node.fea_io().delete_instance_watch(instance_name, this, dummy_error_msg); erase_filters_by_receiver_name(AF_INET, instance_name); #ifdef HAVE_IPV6 erase_filters_by_receiver_name(AF_INET6, instance_name); #endif } xorp/fea/mfea_dataflow.hh0000664000076400007640000004246411540224224015533 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/mfea_dataflow.hh,v 1.10 2008/10/02 21:56:49 bms Exp $ #ifndef __FEA_MFEA_DATAFLOW_HH__ #define __FEA_MFEA_DATAFLOW_HH__ // // MFEA (Multicast Forwarding Engine Abstraction) dataflow definition. // #include "libxorp/timer.hh" #include "libproto/proto_unit.hh" #include "mrt/mrt.hh" #include "mfea_mrouter.hh" // // Constants definitions // // // Structures/classes, typedefs and macros // class MfeaDfe; class MfeaDfeLookup; class MfeaNode; /** * @short The MFEA (S,G) dataflow table for monitoring forwarded bandwidth. */ class MfeaDft : public Mrt { public: /** * Constructor for a given MFEA node. * * @param mfea_node the @ref MfeaNode this table belongs to. */ MfeaDft(MfeaNode& mfea_node); /** * Destructor */ virtual ~MfeaDft(); /** * Get a reference to to the MFEA node this table belongs to. * * @return a reference to the MFEA node (@ref MfeaNode) this table * belongs to. */ MfeaNode& mfea_node() const { return (_mfea_node); } /** * Get the address family. * * @return the address family (e.g., AF_INET or AF_INET6 * for IPv4 and IPv6 respectively). */ int family() const; /** * Add a dataflow entry. * * Note: either @ref is_threshold_in_packets or @ref is_threshold_in_bytes * (or both) must be true. * Note: either @ref is_geq_upcall or @ref is_leq_upcall * (but not both) must be true. * * @param source the source address. * @param group the group address. * @param threshold_interval the dataflow threshold interval. * @param threshold_packets the threshold (in number of packets) * to compare against. * @param threshold_bytes the threshold (in number of bytes) * to compare against. * @param is_threshold_in_packets if true, @ref threshold_packets is valid. * @param is_threshold_in_bytes if true, @ref threshold_bytes is valid. * @param is_geq_upcall if true, the operation for comparison is ">=". * @param is_leq_upcall if true, the operation for comparison is "<=". * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_entry(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); /** * Delete a dataflow entry. * * Note: either @ref is_threshold_in_packets or @ref is_threshold_in_bytes * (or both) must be true. * Note: either @ref is_geq_upcall or @ref is_leq_upcall * (but not both) must be true. * * @param source the source address. * @param group the group address. * @param threshold_interval the dataflow threshold interval. * @param threshold_packets the threshold (in number of packets) * to compare against. * @param threshold_bytes the threshold (in number of bytes) * to compare against. * @param is_threshold_in_packets if true, @ref threshold_packets is valid. * @param is_threshold_in_bytes if true, @ref threshold_bytes is valid. * @param is_geq_upcall if true, the operation for comparison is ">=". * @param is_leq_upcall if true, the operation for comparison is "<=". * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int delete_entry(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); /** * Delete all dataflow entries for a given source and group address. * * @param source the source address. * @param group the group address. * @return XORP_OK on success, otherwise XORP_ERROR. */ int delete_entry(const IPvX& source, const IPvX& group); private: /** * Delete a given @ref MfeaDfe dataflow entry. * * @param mfea_dfe the @ref MfeaDfe dataflow entry to delete. * @return XORP_OK on success, otherwise XORP_ERROR. */ int delete_entry(MfeaDfe *mfea_dfe); MfeaNode& _mfea_node; // The Mfea node }; /** * @short A class for storing all dataflow entries per (S,G). */ class MfeaDfeLookup : public Mre { public: /** * Constructor for a given dataflow table, source and group address. * * @param mfea_dft the dataflow table (@ref MfeaDft) this entries * belongs to. * @param source the source address. * @param group the group address. */ MfeaDfeLookup(MfeaDft& mfea_dft, const IPvX& source, const IPvX& group); /** * Destructor */ ~MfeaDfeLookup(); /** * Get a reference to the dataflow table this entry belongs to. * * @return a reference to the dataflow table (@ref MfeaDataflow) this * entry belongs to. */ MfeaDft& mfea_dft() const { return (_mfea_dft); } /** * Get the address family. * * @return the address family (e.g., AF_INET or AF_INET6 for IPv4 and * IPv6 respectively). */ int family() const; /** * Find a @ref MfeaDfe dataflow entry. * * Note: either @ref is_threshold_in_packets or @ref is_threshold_in_bytes * (or both) must be true. * Note: either @ref is_geq_upcall or @ref is_leq_upcall * (but not both) must be true. * * @param threshold_interval the dataflow threshold interval. * @param threshold_packets the threshold (in number of packets) * to compare against. * @param threshold_bytes the threshold (in number of bytes) * to compare against. * @param is_threshold_in_packets if true, @ref threshold_packets is valid. * @param is_threshold_in_bytes if true, @ref threshold_bytes is valid. * @param is_geq_upcall if true, the operation for comparison is ">=". * @param is_leq_upcall if true, the operation for comparison is "<=". * @return the corresponding @ref MfeaDfe dataflow entry on success, * otherwise NULL. */ MfeaDfe *find(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); /** * Insert a @ref MfeaDfe dataflow entry. * * @param mfea_dfe the @ref MfeaDfe dataflow entry to insert. */ void insert(MfeaDfe *mfea_dfe); /** * Remove a @ref MfeaDfe dataflow entry. * * @param mfea_dfe the @ref MfeaDfe dataflow entry to remove. */ void remove(MfeaDfe *mfea_dfe); /** * Test if there are @ref MfeaDfe entries inserted within this entry. * * @return true if there are @ref MfeaDfe entries inserted within this * entry, otherwise false. */ bool is_empty() const { return (_mfea_dfe_list.empty()); } /** * Get the list of @ref MfeaDfe dataflow entries for the same (S,G). * * @return the list of @ref MfeaDfe dataflow entries for the same (S,G). */ list& mfea_dfe_list() { return (_mfea_dfe_list); } private: MfeaDft& _mfea_dft; // The Mfea dataflow table (yuck!) list _mfea_dfe_list; // The list of dataflow monitor entries }; /** * @short Multicast dataflow entry class. * * This entry contains all the information about the condition a dataflow * must satisfy to deliver a signal. It is (S,G)-specific, but there could be * more than one @ref MfeaDfe entries per (S,G). */ class MfeaDfe { public: /** * Constructor with information with the dataflow condition to satisfy. * * Note: either @ref is_threshold_in_packets or @ref is_threshold_in_bytes * (or both) must be true. * Note: either @ref is_geq_upcall or @ref is_leq_upcall * (but not both) must be true. * * @param mfea_dfe_lookup the @ref MfeaDfeLookup entry this entry * belongs to. * @param threshold_interval the dataflow threshold interval. * @param threshold_packets the threshold (in number of packets) * to compare against. * @param threshold_bytes the threshold (in number of bytes) * to compare against. * @param is_threshold_in_packets if true, @ref threshold_packets is valid. * @param is_threshold_in_bytes if true, @ref threshold_bytes is valid. * @param is_geq_upcall if true, the operation for comparison is ">=". * @param is_leq_upcall if true, the operation for comparison is "<=". */ MfeaDfe(MfeaDfeLookup& mfea_dfe_lookup, 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); /** * Destructor */ ~MfeaDfe(); /** * Get a reference to the @ref MfeaDfeLookup entry this entry belongs to. * * @return a reference to the @ref MfeaDfeLookup entry this entry * belongs to. */ MfeaDfeLookup& mfea_dfe_lookup() const { return (_mfea_dfe_lookup); } /** * Get a reference to the @ref MfeaDft dataflow table this entry * belongs to. * * @return a reference to the @ref MfeaDft dataflow table this entry * belongs to. */ MfeaDft& mfea_dft() const; /** * Get a reference to the @ref EventLoop. * * @return a reference to the @ref EventLoop. */ EventLoop& eventloop() const; /** * Get the address family. * * @return the address family (e.g., AF_INET or AF_INET6 * for IPv4 and IPv6 respectively). */ int family() const; /** * Get the source address. * * @return the source address. */ const IPvX& source_addr() const { return (_mfea_dfe_lookup.source_addr());} /** * Get the group address. * * @return the group address. */ const IPvX& group_addr() const { return (_mfea_dfe_lookup.group_addr()); } /** * Test if this entry is valid. * * An entry is valid if, for example: * (a) either @ref is_threshold_in_packets or @ref is_threshold_in_bytes * (or both) must be true. * (b) either @ref is_geq_upcall or @ref is_leq_upcall * (but not both) must be true. * (c) the threshold interval is not too small. * (d) the bandwidth-related statistics do not contain invalid values. * * @return true if this entry is valid, otherwise false. */ bool is_valid() const; /** * Compare whether the information contained within this @ref MfeaDfe * entry is same as the specified information. * * @param threshold_interval_test the dataflow threshold interval. * @param threshold_packets_test the threshold (in number of packets) * to compare against. * @param threshold_bytes_test the threshold (in number of bytes) * to compare against. * @param is_threshold_in_packets_test if true, @ref threshold_packets is * valid. * @param is_threshold_in_bytes_test if true, @ref threshold_bytes is * valid. * @param is_geq_upcall_test if true, the operation for comparison is ">=". * @param is_leq_upcall_test if true, the operation for comparison is "<=". * @return true if the information contained within this @ref MfeaDfe * entry is same as the specified information, otherwise false. */ bool is_same(const TimeVal& threshold_interval_test, uint32_t threshold_packets_test, uint32_t threshold_bytes_test, bool is_threshold_in_packets_test, bool is_threshold_in_bytes_test, bool is_geq_upcall_test, bool is_leq_upcall_test) const; /** * Initialize this entry with the current multicast forwarding information. * * The current multicast forwarding bandwidth information is read from * the kernel. */ void init_sg_count(); /** * Test if the dataflow bandwidth satisfies the pre-defined condition. * * The multicast forwarding bandwidth information is read from * the kernel, and then is tested whether is above/below the * pre-defined threshold. * * @return true if the dataflow bandwidth satisifes the pre-defined * condition, otherwise false. * Note: if both "is_threshold_in_packets" and "is_threshold_in_bytes" * are true, then return true if the test is positive for either unit * (i.e., packets or bytes). */ bool test_sg_count(); /** * Start bandwidth measurement. */ void start_measurement(); /** * Send a dataflow signal that the pre-defined condition is true. */ void dataflow_signal_send(); /** * Get the threshold interval. * * @return the threshold interval for this dataflow entry. */ const TimeVal& threshold_interval() const { return (_threshold_interval); } /** * Get the threshold packets. * * @return the threshold packets for this dataflow entry. */ uint32_t threshold_packets() const { return (_threshold_packets); } /** * Get the threshold bytes. * * @return the threshold bytes for this dataflow entry. */ uint32_t threshold_bytes() const { return (_threshold_bytes); } /** * Test if the threshold is in number of packets. * * @return true if the threshold is in number of packets. */ bool is_threshold_in_packets() const { return (_is_threshold_in_packets); } /** * Test if the threshold is in number of bytes. * * @return true if the threshold is in number of bytes. */ bool is_threshold_in_bytes() const { return (_is_threshold_in_bytes); } /** * Test if the threshold type is "greater-or-equal" (i.e., ">="). * * @return true if the threshold type is "greater-or-equal" (i.e., ">="). */ bool is_geq_upcall() const { return (_is_geq_upcall); } /** * Test if the threshold type is "less-or-equal" (i.e., "<="). * * @return true if the threshold type is "less-or-equal" (i.e., "<="). */ bool is_leq_upcall() const { return (_is_leq_upcall); } /** * Get the start time for the most recent measurement interval window. * * @return the start time for the most recent measurement interval window. */ const TimeVal& start_time() const; /** * Get the number of packets measured in the most recent interval window. * * @return the number of packets measured in the most recent interval * window. */ uint32_t measured_packets() const; /** * Get the number of bytes measured in the most recent interval window. * * @return the number of bytes measured in the most recent interval * window. */ uint32_t measured_bytes() const; private: // Private methods void measurement_timer_timeout(); // Private state MfeaDfeLookup& _mfea_dfe_lookup; // The Mfea dataflow lookup entry (yuck!) TimeVal _threshold_interval; // The threshold interval uint32_t _threshold_packets; // The threshold value (in packets) uint32_t _threshold_bytes; // The threshold value (in bytes) bool _is_threshold_in_packets; // If true, _threshold_packets is // valid bool _is_threshold_in_bytes; // If true, _threshold_bytes is valid bool _is_geq_upcall; // If true, the operation is ">=". bool _is_leq_upcall; // If true, the operation is "<=". #define MFEA_DATAFLOW_TEST_FREQUENCY 4 SgCount _last_sg_count; // Last measurement result SgCount _measured_sg_count; // Measured result SgCount _delta_sg_count[MFEA_DATAFLOW_TEST_FREQUENCY]; // Delta measurement result size_t _delta_sg_count_index; // Index into next '_delta_sg_count' bool _is_bootstrap_completed; TimeVal _measurement_interval; // Interval between two measurements XorpTimer _measurement_timer; // Timer to perform measurements // Time when current measurement window has started // XXX: used for debug purpose only TimeVal _start_time[MFEA_DATAFLOW_TEST_FREQUENCY]; }; // // Global variables // // // Global functions prototypes // #endif // __FEA_MFEA_DATAFLOW_HH__ xorp/fea/io_link.hh0000664000076400007640000002272611540225525014372 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2007-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __FEA_IO_LINK_HH__ #define __FEA_IO_LINK_HH__ #include "fea_data_plane_manager.hh" // // I/O link raw communication API. // class EventLoop; class IfTree; class IfTreeInterface; class IfTreeVif; class IoLinkManager; class IoLinkReceiver; class Mac; /** * @short A base class for I/O link raw communication. * * Each protocol 'registers' for I/O per interface and vif and gets assigned * one object (per interface and vif) of this class. */ class IoLink { public: /** * Constructor for link-level access for a given interface and vif. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). * @param iftree the interface tree to use. * @param if_name the interface name. * @param vif_name the vif name. * @param ether_type the EtherType protocol number. If it is 0 then * it is unused. * @param filter_program the optional filter program to be applied on the * received packets. */ IoLink(FeaDataPlaneManager& fea_data_plane_manager, const IfTree& iftree, const string& if_name, const string& vif_name, uint16_t ether_type, const string& filter_program); /** * Virtual destructor. */ virtual ~IoLink(); /** * Get the @ref IoLinkManager instance. * * @return the @ref IoLinkManager instance. */ IoLinkManager& io_link_manager() { return _io_link_manager; } /** * Get the @ref FeaDataPlaneManager instance. * * @return the @ref FeaDataPlaneManager instance. */ FeaDataPlaneManager& fea_data_plane_manager() { return _fea_data_plane_manager; } /** * Test whether this instance is running. * * @return true if the instance is running, otherwise false. */ virtual bool is_running() const { return _is_running; } /** * Get the event loop. * * @return the event loop. */ EventLoop& eventloop() { return (_eventloop); } /** * Get the interface tree. * * @return the interface tree. */ const IfTree& iftree() const { return (_iftree); } /** * Get the interface name. * * @return the interface name. */ virtual const string& if_name() const { return (_if_name); } /** * Get the vif name. * * @return the vif name. */ virtual const string& vif_name() const { return (_vif_name); } /** * Get the EtherType protocol number. * * @return the EtherType protocol number. If it is 0 then * it is unused. */ virtual uint16_t ether_type() const { return (_ether_type); } /** * Get the optional filter program. * * @return the optional filter program to be applied on the * received packets. */ const string& filter_program() const { return (_filter_program); } /** * Get the registered receiver. * * @return the registered receiver. */ IoLinkReceiver* io_link_receiver() { return (_io_link_receiver); } /** * Register the I/O Link raw packets receiver. * * @param io_link_receiver the receiver to register. */ virtual void register_io_link_receiver(IoLinkReceiver* io_link_receiver); /** * Unregister the I/O Link raw packets receiver. */ virtual void unregister_io_link_receiver(); /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg) = 0; /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg) = 0; /** * Join a multicast group on an interface. * * @param group the multicast group to join. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int join_multicast_group(const Mac& group, string& error_msg) = 0; /** * Leave a multicast group on an interface. * * @param group the multicast group to leave. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int leave_multicast_group(const Mac& group, string& error_msg) = 0; /** * Send a link-level packet. * * @param src_address the MAC source address. * @param dst_address the MAC destination address. * @param ether_type the EtherType protocol number. * @param payload the payload, everything after the MAC header. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int send_packet(const Mac& src_address, const Mac& dst_address, uint16_t ether_type, const vector& payload, string& error_msg) = 0; // // Debug-related methods // /** * Test if trace log is enabled. * * This method is used to test whether to output trace log debug messges. * * @return true if trace log is enabled, otherwise false. */ bool is_log_trace() const { return (_is_log_trace); } /** * Enable/disable trace log. * * This method is used to enable/disable trace log debug messages output. * * @param is_enabled if true, trace log is enabled, otherwise is disabled. */ void set_log_trace(bool is_enabled) { _is_log_trace = is_enabled; } protected: /** * Received a link-level packet. * * @param src_address the MAC source address. * @param dst_address the MAC destination address. * @param ether_type the EtherType protocol number. * @param packet the payload, everything after the MAC header. */ virtual void recv_packet(const Mac& src_address, const Mac& dst_address, uint16_t ether_type, const vector& payload); /** * Receved an Ethernet packet. * * @param packet the packet. * @param packet_size the size of the packet. */ void recv_ethernet_packet(const uint8_t* packet, size_t packet_size); /** * Prepare an Ethernet packet for transmission. * * @param src_address the MAC source address. * @param dst_address the MAC destination address. * @param ether_type the EtherType protocol number. * @param payload the payload, everything after the MAC header. * @param packet the return-by-reference packet prepared for transmission. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int prepare_ethernet_packet(const Mac& src_address, const Mac& dst_address, uint16_t ether_type, const vector& payload, vector& packet, string& error_msg); // Misc other state bool _is_running; // Misc. constants static const uint32_t L2_MAX_PACKET_SIZE = (64*1024); // Max. packet size static const uint16_t ETHERNET_HEADER_SIZE = 14; static const uint16_t ETHERNET_LENGTH_TYPE_THRESHOLD = 1536; static const uint16_t ETHERNET_MIN_FRAME_SIZE = 60; // Excl. CRC private: IoLinkManager& _io_link_manager; // The I/O Link manager FeaDataPlaneManager& _fea_data_plane_manager; // The data plane manager EventLoop& _eventloop; // The event loop to use const IfTree& _iftree; // The interface tree to use const string _if_name; // The interface name const string _vif_name; // The vif name const uint16_t _ether_type; // The EtherType protocol number const string _filter_program; // The filter program IoLinkReceiver* _io_link_receiver; // The registered receiver bool _is_log_trace; // True if trace log is enabled }; /** * @short A base class for I/O Link raw packets receiver. * * The real receiver must inherit from this class and register with the * corresponding IoLink entity to receive the link raw packets. * @see IoLink. */ class IoLinkReceiver { public: /** * Default constructor. */ IoLinkReceiver() {} /** * Virtual destructor. */ virtual ~IoLinkReceiver() {} /** * Received a link-level packet. * * @param src_address the MAC source address. * @param dst_address the MAC destination address. * @param ether_type the EtherType protocol number. * @param packet the payload, everything after the MAC header. */ virtual void recv_packet(const Mac& src_address, const Mac& dst_address, uint16_t ether_type, const vector& payload) = 0; }; #endif // __FEA_IO_LINK_HH__ xorp/fea/io_tcpudp_manager.hh0000664000076400007640000012626511540225525016431 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2007-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/io_tcpudp_manager.hh,v 1.13 2008/10/02 21:56:49 bms Exp $ #ifndef __FEA_IO_TCPUDP_MANAGER_HH__ #define __FEA_IO_TCPUDP_MANAGER_HH__ #include "libxorp/ipvx.hh" #include "fea_io.hh" #include "io_tcpudp.hh" class FeaNode; class FeaDataPlaneManager; /** * A class that handles I/O TCP/UDP communication. */ class IoTcpUdpComm : public NONCOPYABLE, public IoTcpUdpReceiver { public: /** * Joined multicast group class. */ class JoinedMulticastGroup { public: JoinedMulticastGroup(const IPvX& interface_address, const IPvX& group_address) : _interface_address(interface_address), _group_address(group_address) {} #ifdef XORP_USE_USTL JoinedMulticastGroup() { } #endif virtual ~JoinedMulticastGroup() {} const IPvX& interface_address() const { return _interface_address; } const IPvX& group_address() const { return _group_address; } /** * Less-Than Operator * * @param other the right-hand operand to compare against. * @return true if the left-hand operand is numerically smaller * than the right-hand operand. */ bool operator<(const JoinedMulticastGroup& other) const { if (_interface_address != other._interface_address) return (_interface_address < other._interface_address); return (_group_address < other._group_address); } /** * Equality Operator * * @param other the right-hand operand to compare against. * @return true if the left-hand operand is numerically same as the * right-hand operand. */ bool operator==(const JoinedMulticastGroup& other) const { return ((_interface_address == other._interface_address) && (_group_address == other._group_address)); } /** * Add a receiver. * * @param receiver_name the name of the receiver to add. */ void add_receiver(const string& receiver_name) { _receivers.insert(receiver_name); } /** * Delete a receiver. * * @param receiver_name the name of the receiver to delete. */ void delete_receiver(const string& receiver_name) { _receivers.erase(receiver_name); } /** * @return true if there are no receivers associated with this group. */ bool empty() const { return _receivers.empty(); } private: IPvX _interface_address; IPvX _group_address; set _receivers; }; /** * Constructor for IoTcpUdpComm. * * @param io_tcpudp_manager the corresponding I/O TCP/UDP manager * (@ref IoTcpUdpManager). * @param iftree the interface tree to use. * @param family the address family (AF_INET or AF_INET6 for IPv4 and IPv6 * respectively). * @param is_tcp if true this is TCP entry, otherwise UDP. * @param creator the name of the socket creator. */ IoTcpUdpComm(IoTcpUdpManager& io_tcpudp_manager, const IfTree& iftree, int family, bool is_tcp, const string& creator); /** * Constructor for connected IoTcpUdpComm. * * @param io_tcpudp_manager the corresponding I/O TCP/UDP manager * (@ref IoTcpUdpManager). * @param iftree the interface tree to use. * @param family the address family (AF_INET or AF_INET6 for IPv4 and IPv6 * respectively). * @param is_tcp if true this is TCP entry, otherwise UDP. * @param creator the name of the socket creator. * @param listener_sockid the socket ID of the listener socket. * @param peer_host the peer host IP address. * @param peer_port the peer host port number. */ IoTcpUdpComm(IoTcpUdpManager& io_tcpudp_manager, const IfTree& iftree, int family, bool is_tcp, const string& creator, const string& listener_sockid, const IPvX& peer_host, uint16_t peer_port); /** * Virtual destructor. */ virtual ~IoTcpUdpComm(); /** * Allocate the I/O TCP/UDP plugins (one per data plane manager). */ void allocate_io_tcpudp_plugins(); /** * Deallocate the I/O TCP/UDP plugins (one per data plane manager). */ void deallocate_io_tcpudp_plugins(); /** * Allocate an I/O TCP/UDP plugin for a given data plane manager. * * @param fea_data_plane_manager the data plane manager. */ void allocate_io_tcpudp_plugin(FeaDataPlaneManager* fea_data_plane_manager); /** * Deallocate the I/O TCP/UDP plugin for a given data plane manager. * * @param fea_data_plane_manager the data plane manager. */ void deallocate_io_tcpudp_plugin(FeaDataPlaneManager* fea_data_plane_manager); /** * Add a pre-allocated I/O TCP/UDP plugin. * * @param new_io_tcpudp the plugin to add. */ void add_plugin(IoTcpUdp* new_io_tcpudp); /** * Start all I/O TCP/UDP plugins. */ void start_io_tcpudp_plugins(); /** * Stop all I/O TCP/UDP plugins. */ void stop_io_tcpudp_plugins(); /** * Open a TCP socket. * * @param sockid return parameter that contains unique socket ID when * socket instantiation is successful. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int tcp_open(string& sockid, string& error_msg); /** * Open an UDP socket. * * @param sockid return parameter that contains unique socket ID when * socket instantiation is successful. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int udp_open(string& sockid, string& error_msg); /** * Create a bound TCP socket. * * @param local_addr the interface address to bind socket to. * @param local_port the port to bind socket to. * @param sockid return parameter that contains unique socket ID when * socket instantiation is successful. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int tcp_open_and_bind(const IPvX& local_addr, uint16_t local_port, string& sockid, string& error_msg); /** * Create a bound UDP socket. * * @param local_addr the interface address to bind socket to. * @param local_port the port to bind socket to. * @param sockid return parameter that contains unique socket ID when * socket instantiation is successful. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int udp_open_and_bind(const IPvX& local_addr, uint16_t local_port, const string& local_dev, int reuse, string& sockid, string& error_msg); /** * Create a bound UDP multicast socket. * * @param local_addr the interface address to bind socket to. * @param local_port the port to bind socket to. * @param mcast_addr the multicast group address to join. * @param ttl the TTL to use for this multicast socket. * @param reuse allow other sockets to bind to same multicast group. * @param sockid return parameter that contains unique socket ID when * socket instantiation is successful. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int udp_open_bind_join(const IPvX& local_addr, uint16_t local_port, const IPvX& mcast_addr, uint8_t ttl, bool reuse, string& sockid, string& error_msg); /** * Create a bound and connected TCP socket. * * @param local_addr the interface address to bind socket to. * @param local_port the port to bind socket to. * @param remote_addr the address to connect to. * @param remote_port the remote port to connect to. * @param sockid return parameter that contains unique socket ID when * socket instantiation is successful. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int tcp_open_bind_connect(const IPvX& local_addr, uint16_t local_port, const IPvX& remote_addr, uint16_t remote_port, string& sockid, string& error_msg); /** * Create a bound and connected UDP socket. * * @param local_addr the interface address to bind socket to. * @param local_port the port to bind socket to. * @param remote_addr the address to connect to. * @param remote_port the remote port to connect to. * @param sockid return parameter that contains unique socket ID when * socket instantiation is successful. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int udp_open_bind_connect(const IPvX& local_addr, uint16_t local_port, const IPvX& remote_addr, uint16_t remote_port, string& sockid, string& error_msg); /** * Create a bound, and optionally connected, UDP broadcast socket. * * @param ifname the interface name to bind socket to. * @param vifname the vif to bind socket to. * @param local_port the port to bind socket to. * @param remote_port the remote port to connect to. * @param reuse allow other sockets to bind to same port. * @param limited set the socket up for transmission to the limited * broadcast address 255.255.255.255. * @param connected connect the socket for use with send() not sendto(). * @param sockid return parameter that contains unique socket ID when * socket instantiation is successful. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int udp_open_bind_broadcast(const string& ifname, const string& vifname, uint16_t local_port, uint16_t remote_port, bool reuse, bool limited, bool connected, string& sockid, string& error_msg); /** * Bind a socket. * * @param local_addr the interface address to bind socket to. * @param local_port the port to bind socket to. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int bind(const IPvX& local_addr, uint16_t local_port, string& error_msg); /** * Join multicast group on already bound socket. * * @param mcast_addr group to join. * @param join_if_addr interface address to perform join on. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int udp_join_group(const IPvX& mcast_addr, const IPvX& join_if_addr, string& error_msg); /** * Leave multicast group on already bound socket. * * @param mcast_addr group to leave. * @param leave_if_addr interface address to perform leave on. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int udp_leave_group(const IPvX& mcast_addr, const IPvX& leave_if_addr, string& error_msg); /** * Close socket. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int close(string& error_msg); /** * Listen for inbound connections on socket. * * When a connection request is received the socket creator will receive * notification. * * @param backlog the maximum number of pending connections. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int tcp_listen(uint32_t backlog, string& error_msg); /** * Enable a UDP socket for datagram reception. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int udp_enable_recv(string& error_msg); /** * Send data on socket. * * @param data block of data to be sent. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int send(const vector& data, string& error_msg); /** * Send data on socket to a given destination. * * The packet is not routed as the forwarding engine sending the packet * may not have access to the full routing table. * * @param remote_addr destination address for data. * @param remote_port destination port for data. * @param data block of data to be sent. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int send_to(const IPvX& remote_addr, uint16_t remote_port, const vector& data, string& error_msg); /** * Send data on socket to a given multicast group from a given interface. * * @param family the address family (AF_INET or AF_INET6 for IPv4 and IPv6 * respectively). * @param sockid unique socket ID. * @param group_addr destination address for data. * @param group_port destination port for data. * @param ifaddr interface address. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int send_from_multicast_if(const IPvX& group_addr, uint16_t group_port, const IPvX& ifaddr, const vector& data, string& error_msg); /** * Set a named socket option with an integer value. * * @param family the address family (AF_INET or AF_INET6 for IPv4 and IPv6 * respectively). * @param sockid unique socket ID. * @param optname name of option to be set. Valid values are: * "onesbcast" (IPv4 only) * "receive_broadcast" (IPv4 only) * "reuseport" * "send_broadcast" (IPv4 only) * "tos" (IPv4 only) * "ttl" * "multicast_loopback" * "multicast_ttl" * @param optval value of option to be set. If value is logically boolean * then zero represents false and any non-zero value true. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_socket_option(const string& optname, uint32_t optval, string& error_msg); /** * Set a named socket option with a string value. * * @param family the address family (AF_INET or AF_INET6 for IPv4 and IPv6 * respectively). * @param sockid unique socket ID. * @param optname name of option to be set. Valid values are: * "bindtodevice" * @param optval value of option to be set. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_socket_option(const string& optname, const string& optval, string& error_msg); /** * Accept or reject a pending connection. * * @param is_accepted if true, the connection is accepted, otherwise is * rejected. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int accept_connection(bool is_accepted, string& error_msg); /** * Data received event. * * @param if_name the interface name the packet arrived on, if known. * If unknown, then it is an empty string. * @param vif_name the vif name the packet arrived on, if known. * If unknown, then it is an empty string. * @param src_host the originating host IP address. * @param src_port the originating host port number. * @param data the data received. */ void recv_event(const string& if_name, const string& vif_name, const IPvX& src_host, uint16_t src_port, const vector& data); /** * Inbound connection request received event. * * It applies only to TCP sockets. * * @param src_host the originating host IP address. * @param src_port the originating host port number. * @param new_io_tcpudp the handler for the new connection. */ void inbound_connect_event(const IPvX& src_host, uint16_t src_port, IoTcpUdp* new_io_tcpudp); /** * Outgoing connection request completed event. * * It applies only to TCP sockets. */ void outgoing_connect_event(); /** * Error occured event. * * @param error a textual description of the error. * @param fatal indication of whether socket is shutdown because of * error. */ void error_event(const string& error, bool fatal); /** * Connection closed by peer event. * * It applies only to TCP sockets. * This method is not called if the socket is gracefully closed * through close(). */ void disconnect_event(); /** * Get the creator name. * * @return the creator name. */ const string& creator() const { return (_creator); } /** * Get the socket ID. * * @return the socket ID. */ const string& sockid() const { return (_sockid); } /** * Get the listener socket ID. * * @return the listener socket ID. */ const string& listener_sockid() const { return (_listener_sockid); } /** * Get the peer host IP address. * * @return the peer host IP address. */ const IPvX& peer_host() const { return (_peer_host); } /** * Get the peer host port number. * * @return the peer host port number. */ uint16_t peer_port() const { return (_peer_port); } private: IoTcpUdpComm(const IoTcpUdpComm&); // Not implemented. IoTcpUdpComm& operator=(const IoTcpUdpComm&); // Not implemented. IoTcpUdpManager& _io_tcpudp_manager; const IfTree& _iftree; const int _family; const bool _is_tcp; const string _creator; const string _sockid; // State for connected entries const string _listener_sockid; const IPvX _peer_host; const uint16_t _peer_port; typedef list >IoTcpUdpPlugins; IoTcpUdpPlugins _io_tcpudp_plugins; typedef map JoinedGroupsTable; JoinedGroupsTable _joined_groups_table; }; /** * @short Class that implements the API for sending TCP/UDP packets * and related events to a receiver. */ class IoTcpUdpManagerReceiver { public: /** * Virtual destructor. */ virtual ~IoTcpUdpManagerReceiver() {} /** * Data received event. * * @param receiver_name the name of the receiver to send the data to. * @param sockid unique socket ID. * @param if_name the interface name the packet arrived on, if known. * If unknown, then it is an empty string. * @param vif_name the vif name the packet arrived on, if known. * If unknown, then it is an empty string. * @param src_host the originating host IP address. * @param src_port the originating host port number. * @param data the data received. */ virtual void recv_event(const string& receiver_name, const string& sockid, const string& if_name, const string& vif_name, const IPvX& src_host, uint16_t src_port, const vector& data) = 0; /** * Inbound connection request received event. * * It applies only to TCP sockets. * * @param receiver_name the name of the receiver to send the event to. * @param sockid unique socket ID. * @param src_host the originating host IP address. * @param src_port the originating host port number. * @param new_sockid the new socket ID. */ virtual void inbound_connect_event(const string& receiver_name, const string& sockid, const IPvX& src_host, uint16_t src_port, const string& new_sockid) = 0; /** * Outgoing connection request completed event. * * It applies only to TCP sockets. * * @param family the address family (AF_INET or AF_INET6 for IPv4 and IPv6 * respectively). * @param receiver_name the name of the receiver to send the event to. * @param sockid unique socket ID. */ virtual void outgoing_connect_event(int family, const string& receiver_name, const string& sockid) = 0; /** * Error occured event. * * @param family the address family (AF_INET or AF_INET6 for IPv4 and IPv6 * respectively). * @param receiver_name the name of the receiver to send the event to. * @param sockid unique socket ID. * @param error a textual description of the error. * @param fatal indication of whether socket is shutdown because of * error. */ virtual void error_event(int family, const string& receiver_name, const string& sockid, const string& error, bool fatal) = 0; /** * Connection closed by peer event. * * It applies only to TCP sockets. * This method is not called if the socket is gracefully closed * through close(). * * @param family the address family (AF_INET or AF_INET6 for IPv4 and IPv6 * respectively). * @param receiver_name the name of the receiver to send the event to. * @param sockid unique socket ID. */ virtual void disconnect_event(int family, const string& receiver_name, const string& sockid) = 0; }; /** * @short A class that manages I/O TCP/UDP communications. */ class IoTcpUdpManager : public IoTcpUdpManagerReceiver, public InstanceWatcher { public: /** * Constructor for IoTcpUdpManager. */ IoTcpUdpManager(FeaNode& fea_node, const IfTree& iftree); /** * Virtual destructor. */ virtual ~IoTcpUdpManager(); /** * Open a TCP socket. * * @param family the address family (AF_INET or AF_INET6 for IPv4 and IPv6 * respectively). * @param creator the name of the socket creator. * @param sockid return parameter that contains unique socket ID when * socket instantiation is successful. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int tcp_open(int family, const string& creator, string& sockid, string& error_msg); /** * Open an UDP socket. * * @param family the address family (AF_INET or AF_INET6 for IPv4 and IPv6 * respectively). * @param creator the name of the socket creator. * @param sockid return parameter that contains unique socket ID when * socket instantiation is successful. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int udp_open(int family, const string& creator, string& sockid, string& error_msg); /** * Create a bound TCP socket. * * @param family the address family (AF_INET or AF_INET6 for IPv4 and IPv6 * respectively). * @param creator the name of the socket creator. * @param local_addr the interface address to bind socket to. * @param local_port the port to bind socket to. * @param sockid return parameter that contains unique socket ID when * socket instantiation is successful. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int tcp_open_and_bind(int family, const string& creator, const IPvX& local_addr, uint16_t local_port, string& sockid, string& error_msg); /** * Create a bound UDP socket. * * @param family the address family (AF_INET or AF_INET6 for IPv4 and IPv6 * respectively). * @param creator the name of the socket creator. * @param local_addr the interface address to bind socket to. * @param local_port the port to bind socket to. * @param sockid return parameter that contains unique socket ID when * socket instantiation is successful. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int udp_open_and_bind(int family, const string& creator, const IPvX& local_addr, uint16_t local_port, const string& local_dev, int reuse, string& sockid, string& error_msg); /** * Create a bound UDP multicast socket. * * @param family the address family (AF_INET or AF_INET6 for IPv4 and IPv6 * respectively). * @param creator the name of the socket creator. * @param local_addr the interface address to bind socket to. * @param local_port the port to bind socket to. * @param mcast_addr the multicast group address to join. * @param ttl the TTL to use for this multicast socket. * @param reuse allow other sockets to bind to same multicast group. * @param sockid return parameter that contains unique socket ID when * socket instantiation is successful. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int udp_open_bind_join(int family, const string& creator, const IPvX& local_addr, uint16_t local_port, const IPvX& mcast_addr, uint8_t ttl, bool reuse, string& sockid, string& error_msg); /** * Create a bound and connected TCP socket. * * @param family the address family (AF_INET or AF_INET6 for IPv4 and IPv6 * respectively). * @param creator the name of the socket creator. * @param local_addr the interface address to bind socket to. * @param local_port the port to bind socket to. * @param remote_addr the address to connect to. * @param remote_port the remote port to connect to. * @param sockid return parameter that contains unique socket ID when * socket instantiation is successful. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int tcp_open_bind_connect(int family, const string& creator, const IPvX& local_addr, uint16_t local_port, const IPvX& remote_addr, uint16_t remote_port, string& sockid, string& error_msg); /** * Create a bound and connected UDP socket. * * @param family the address family (AF_INET or AF_INET6 for IPv4 and IPv6 * respectively). * @param creator the name of the socket creator. * @param local_addr the interface address to bind socket to. * @param local_port the port to bind socket to. * @param remote_addr the address to connect to. * @param remote_port the remote port to connect to. * @param sockid return parameter that contains unique socket ID when * socket instantiation is successful. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int udp_open_bind_connect(int family, const string& creator, const IPvX& local_addr, uint16_t local_port, const IPvX& remote_addr, uint16_t remote_port, string& sockid, string& error_msg); /** * Create a bound and connected UDP broadcast socket. * * @param family the address family (AF_INET or AF_INET6 for IPv4 and IPv6 * respectively). * @param creator the name of the socket creator. * @param ifname the interface name to bind socket to. * @param vifname the vif to bind socket to. * @param local_port the port to bind socket to. * @param remote_port the remote port to connect to. * @param reuse allow other sockets to bind to same port. * @param limited set the socket up for transmission to the limited * broadcast address 255.255.255.255. * @param connected connect the socket for use with send() not sendto(). * @param sockid return parameter that contains unique socket ID when * socket instantiation is successful. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int udp_open_bind_broadcast(int family, const string& creator, const string& ifname, const string& vifname, uint16_t local_port, uint16_t remote_port, bool reuse, bool limited, bool connected, string& sockid, string& error_msg); /** * Bind a socket. * * @param family the address family (AF_INET or AF_INET6 for IPv4 and IPv6 * respectively). * @param sockid the socket ID of the socket to bind. * @param local_addr the interface address to bind socket to. * @param local_port the port to bind socket to. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int bind(int family, const string& sockid, const IPvX& local_addr, uint16_t local_port, string& error_msg); /** * Join multicast group on already bound socket. * * @param family the address family (AF_INET or AF_INET6 for IPv4 and IPv6 * respectively). * @param sockid unique socket ID. * @param mcast_addr group to join. * @param join_if_addr interface address to perform join on. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int udp_join_group(int family, const string& sockid, const IPvX& mcast_addr, const IPvX& join_if_addr, string& error_msg); /** * Leave multicast group on already bound socket. * * @param family the address family (AF_INET or AF_INET6 for IPv4 and IPv6 * respectively). * @param sockid unique socket ID. * @param mcast_addr group to leave. * @param leave_if_addr interface address to perform leave on. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int udp_leave_group(int family, const string& sockid, const IPvX& mcast_addr, const IPvX& leave_if_addr, string& error_msg); /** * Close socket. * * @param family the address family (AF_INET or AF_INET6 for IPv4 and IPv6 * respectively). * @param sockid unique socket ID of socket to be closed. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int close(int family, const string& sockid, string& error_msg); /** * Listen for inbound connections on socket. * * When a connection request is received the socket creator will receive * notification. * * @param family the address family (AF_INET or AF_INET6 for IPv4 and IPv6 * respectively). * @param sockid the unique socket ID of the socket to perform listen. * @param backlog the maximum number of pending connections. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int tcp_listen(int family, const string& sockid, uint32_t backlog, string& error_msg); /** * Enable a UDP socket for datagram reception. * * @param family the address family (AF_INET or AF_INET6 for IPv4 and IPv6 * respectively). * @param sockid the unique socket ID of the socket to enable * datagram reception for. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int udp_enable_recv(int family, const string& sockid, string& error_msg); /** * Send data on socket. * * @param family the address family (AF_INET or AF_INET6 for IPv4 and IPv6 * respectively). * @param sockid unique socket ID. * @param data block of data to be sent. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int send(int family, const string& sockid, const vector& data, string& error_msg); /** * Send data on socket to a given destination. * * The packet is not routed as the forwarding engine sending the packet * may not have access to the full routing table. * * @param family the address family (AF_INET or AF_INET6 for IPv4 and IPv6 * respectively). * @param sockid unique socket ID. * @param remote_addr destination address for data. * @param remote_port destination port for data. * @param data block of data to be sent. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int send_to(int family, const string& sockid, const IPvX& remote_addr, uint16_t remote_port, const vector& data, string& error_msg); /** * Send data on socket to a given multicast group from a given interface. * * @param family the address family (AF_INET or AF_INET6 for IPv4 and IPv6 * respectively). * @param sockid unique socket ID. * @param group_addr destination address for data. * @param group_port destination port for data. * @param ifaddr interface address. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int send_from_multicast_if(int family, const string& sockid, const IPvX& group_addr, uint16_t group_port, const IPvX& ifaddr, const vector& data, string& error_msg); /** * Set a named socket option with an integer value. * * @param family the address family (AF_INET or AF_INET6 for IPv4 and IPv6 * respectively). * @param sockid unique socket ID. * @param optname name of option to be set. Valid values are: * "onesbcast" * "receive_broadcast" * "reuseport" * "send_broadcast" * "tos" * "ttl" * "multicast_loopback" * "multicast_ttl" * @param optval value of option to be set. If value is logically boolean * then zero represents false and any non-zero value true. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_socket_option(int family, const string& sockid, const string& optname, uint32_t optval, string& error_msg); /** * Set a named socket option with a string value. * * @param family the address family (AF_INET or AF_INET6 for IPv4 and IPv6 * respectively). * @param sockid unique socket ID. * @param optname name of option to be set. Valid values are: * "bindtodevice" * @param optval value of option to be set. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_socket_option(int family, const string& sockid, const string& optname, const string& optval, string& error_msg); /** * Accept or reject a pending connection. * * @param family the address family (AF_INET or AF_INET6 for IPv4 and IPv6 * respectively). * @param sockid unique socket ID. * @param is_accepted if true, the connection is accepted, otherwise is * rejected. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int accept_connection(int family, const string& sockid, bool is_accepted, string& error_msg); /** * Data received event. * * @param receiver_name the name of the receiver to send the data to. * @param sockid unique socket ID. * @param if_name the interface name the packet arrived on, if known. * If unknown, then it is an empty string. * @param vif_name the vif name the packet arrived on, if known. * If unknown, then it is an empty string. * @param src_host the originating host IP address. * @param src_port the originating host port number. * @param data the data received. */ void recv_event(const string& receiver_name, const string& sockid, const string& if_name, const string& vif_name, const IPvX& src_host, uint16_t src_port, const vector& data); /** * Inbound connection request received event. * * It applies only to TCP sockets. * * @param receiver_name the name of the receiver to send the event to. * @param sockid unique socket ID. * @param src_host the originating host IP address. * @param src_port the originating host port number. * @param new_sockid the new socket ID. */ void inbound_connect_event(const string& receiver_name, const string& sockid, const IPvX& src_host, uint16_t src_port, const string& new_sockid); /** * Outgoing connection request completed event. * * It applies only to TCP sockets. * * @param family the address family (AF_INET or AF_INET6 for IPv4 and IPv6 * respectively). * @param receiver_name the name of the receiver to send the event to. * @param sockid unique socket ID. */ void outgoing_connect_event(int family, const string& receiver_name, const string& sockid); /** * Error occured event. * * @param family the address family (AF_INET or AF_INET6 for IPv4 and IPv6 * respectively). * @param receiver_name the name of the receiver to send the event to. * @param sockid unique socket ID. * @param error a textual description of the error. * @param fatal indication of whether socket is shutdown because of * error. */ void error_event(int family, const string& receiver_name, const string& sockid, const string& error, bool fatal); /** * Connection closed by peer event. * * It applies only to TCP sockets. * This method is not called if the socket is gracefully closed * through close(). * * @param family the address family (AF_INET or AF_INET6 for IPv4 and IPv6 * respectively). * @param receiver_name the name of the receiver to send the event to. * @param sockid unique socket ID. */ void disconnect_event(int family, const string& receiver_name, const string& sockid); /** * Inform the watcher that a component instance is alive. * * @param instance_name the name of the instance that is alive. */ void instance_birth(const string& instance_name); /** * Inform the watcher that a component instance is dead. * * @param instance_name the name of the instance that is dead. */ void instance_death(const string& instance_name); /** * Set the instance that is responsible for sending IP packets * to a receiver. */ void set_io_tcpudp_manager_receiver(IoTcpUdpManagerReceiver* v) { _io_tcpudp_manager_receiver = v; } /** * Get a reference to the interface tree. * * @return a reference to the interface tree (@ref IfTree). */ const IfTree& iftree() const { return _iftree; } /** * Register @ref FeaDataPlaneManager data plane manager. * * @param fea_data_plane_manager the data plane manager to register. * @param is_exclusive if true, the manager is registered as the * exclusive manager, otherwise is added to the list of managers. * @return XORP_OK on success, otherwise XORP_ERROR. */ int register_data_plane_manager(FeaDataPlaneManager* fea_data_plane_manager, bool is_exclusive); /** * Unregister @ref FeaDataPlaneManager data plane manager. * * @param fea_data_plane_manager the data plane manager to unregister. * @return XORP_OK on success, otherwise XORP_ERROR. */ int unregister_data_plane_manager(FeaDataPlaneManager* fea_data_plane_manager); /** * Get the list of registered data plane managers. * * @return the list of registered data plane managers. */ list& fea_data_plane_managers() { return _fea_data_plane_managers; } /** * Connect IoTcpUdpComm entry to a new plugin. * * @param family the address family. * @param is_tcp if true this is TCP entry, otherwise UDP. * @param creator the name of the creator. * @param listener_sockid the socket ID of the listener socket. * @param peer_host the peer host IP address. * @param peer_port the peer host port number. * @param new_io_tcpudp the handler for the new connection. * @return an entry (@ref IoTcpUdpComm) to handle the connection * from the new plugin. */ IoTcpUdpComm* connect_io_tcpudp_comm(int family, bool is_tcp, const string& creator, const string& listener_sockid, const IPvX& peer_host, uint16_t peer_port, IoTcpUdp* new_io_tcpudp); private: typedef map CommTable; /** * Find an IoTcpUdpComm entry. * * @param family the address family. * @param sockid the socket ID to search for. * @raturn the IoTcpUdpComm entry if found, otherwise NULL. */ IoTcpUdpComm* find_io_tcpudp_comm(int family, const string& sockid, string& error_msg); /** * Create and open IoTcpUdpComm entry. * * @param family the address family. * @param is_tcp if true this is TCP entry, otherwise UDP. * @param creator the name of the creator. * @param allocate_plugins if true, then allocate the plugin handler(s) * internally, otherwise they will be explicitly added externally. */ IoTcpUdpComm* open_io_tcpudp_comm(int family, bool is_tcp, const string& creator, bool allocate_plugins = true); /** * Delete an existing IoTcpUdoComm entry. * * @param family the address family. * @param sockid the socket ID of the entry to delete. */ void delete_io_tcpudp_comm(int family, const string& sockid); /** * Get the CommTable for an address family. * * @param family the address family. * @return a reference to the CommTable for the address family. */ CommTable& comm_table_by_family(int family); /** * Erase CommTable handlers for a given creator name. * * @param family the address family. * @param creator the name of the creator. */ void erase_comm_handlers_by_creator(int family, const string& creator); /** * Test whether there is a CommTable handler for a given creator name. * * @param creator the name of the creator. * @return true if there is a CommTable handler for the given creator name, * otherwise false. */ bool has_comm_handler_by_creator(const string& creator) const; /** * Test whether an address belongs to this host. * * @param local_addr the address to test. * @return true if the address belongs to this host, otherwise false. */ bool is_my_address(const IPvX& local_addr) const; FeaNode& _fea_node; EventLoop& _eventloop; const IfTree& _iftree; // Collection of IP communication handlers keyed by sockid. CommTable _comm_table4; CommTable _comm_table6; IoTcpUdpManagerReceiver* _io_tcpudp_manager_receiver; list _fea_data_plane_managers; }; #endif // __FEA_IO_TCPUDP_MANAGER_HH__ xorp/fea/ifconfig_get.hh0000664000076400007640000000711111421137511015353 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/fea/ifconfig_get.hh,v 1.49 2008/10/02 21:56:47 bms Exp $ #ifndef __FEA_IFCONFIG_GET_HH__ #define __FEA_IFCONFIG_GET_HH__ #include "iftree.hh" #include "fea_data_plane_manager.hh" class IfConfig; class IfConfigGet { public: /** * Constructor. * * @param fea_data_plane_manager the corresponding data plane manager * (@ref FeaDataPlaneManager). */ IfConfigGet(FeaDataPlaneManager& fea_data_plane_manager) : _is_running(false), _ifconfig(fea_data_plane_manager.ifconfig()), _fea_data_plane_manager(fea_data_plane_manager) {} /** * Virtual destructor. */ virtual ~IfConfigGet() {} /** * Get the @ref IfConfig instance. * * @return the @ref IfConfig instance. */ IfConfig& ifconfig() { return _ifconfig; } /** * Get the @ref FeaDataPlaneManager instance. * * @return the @ref FeaDataPlaneManager instance. */ FeaDataPlaneManager& fea_data_plane_manager() { return _fea_data_plane_manager; } /** * Test whether this instance is running. * * @return true if the instance is running, otherwise false. */ virtual bool is_running() const { return _is_running; } /** * Start operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int start(string& error_msg) = 0; /** * Stop operation. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int stop(string& error_msg) = 0; /** * Pull the network interface information from the underlying system. * * @param local_config If not NULL, optimized ifconfig-get subclasses * may pull interface config for only interfaces found in local_config. * Set to NULl to pull all information from the kernel. * @param iftree the IfTree storage to store the pulled information. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int pull_config(const IfTree* local_config, IfTree& iftree) = 0; /** Child classes that *can* do this should over-ride. */ virtual bool can_pull_one() { return false; } /** If_index can be -1 if unknown: We will try to resolve it from ifname. * Child classes that can do this should implement the method. */ virtual int pull_config_one(IfTree& iftree, const char* ifname, int if_index) { UNUSED(iftree); UNUSED(ifname); UNUSED(if_index); return XORP_ERROR; } protected: // Misc other state bool _is_running; private: IfConfig& _ifconfig; FeaDataPlaneManager& _fea_data_plane_manager; }; #endif // __FEA_IFCONFIG_GET_HH__ xorp/fea/io_ip_manager.hh0000664000076400007640000006513611540225525015541 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __FEA_IO_IP_MANAGER_HH__ #define __FEA_IO_IP_MANAGER_HH__ #include "libxorp/callback.hh" #include "libxorp/ipvx.hh" #include "libxorp/xorpfd.hh" #include "fea_io.hh" #include "io_ip.hh" class FeaDataPlaneManager; class FeaNode; class IoIpManager; /** * Structure used to store commonly passed IPv4 and IPv6 header information. */ struct IPvXHeaderInfo { string if_name; string vif_name; IPvX src_address; IPvX dst_address; uint8_t ip_protocol; int32_t ip_ttl; int32_t ip_tos; bool ip_router_alert; bool ip_internet_control; vector ext_headers_type; vector > ext_headers_payload; }; /** * A class that handles raw IP I/O communication for a specific protocol. * * It also allows arbitrary filters to receive the raw IP data for that * protocol. */ class IoIpComm : public NONCOPYABLE, public IoIpReceiver { public: /** * Filter class. */ class InputFilter { public: InputFilter(IoIpManager& io_ip_manager, const string& receiver_name, uint8_t ip_protocol) : _io_ip_manager(io_ip_manager), _receiver_name(receiver_name), _ip_protocol(ip_protocol) {} virtual ~InputFilter() {} /** * Get a reference to the I/O IP manager. * * @return a reference to the I/O IP manager. */ IoIpManager& io_ip_manager() { return (_io_ip_manager); } /** * Get a const reference to the I/O IP manager. * * @return a const reference to the I/O IP manager. */ const IoIpManager& io_ip_manager() const { return (_io_ip_manager); } /** * Get the receiver name. * * @return the receiver name. */ const string& receiver_name() const { return (_receiver_name); } /** * Get the IP protocol. * * @return the IP protocol. */ uint8_t ip_protocol() const { return (_ip_protocol); } /** * Method invoked when data arrives on associated IoIpComm instance. */ virtual void recv(const struct IPvXHeaderInfo& header, const vector& payload) = 0; /** * Method invoked when a multicast forwarding related upcall is * received from the system. */ virtual void recv_system_multicast_upcall(const vector& payload) = 0; /** * Method invoked by the destructor of the associated IoIpComm * instance. This method provides the InputFilter with the * opportunity to delete itself or update its state. * The input filter does not need to call IoIpComm::remove_filter() * since filter removal is automatically conducted. */ virtual void bye() = 0; private: IoIpManager& _io_ip_manager; string _receiver_name; uint8_t _ip_protocol; }; /** * Joined multicast group class. */ class JoinedMulticastGroup { public: JoinedMulticastGroup(const string& if_name, const string& vif_name, const IPvX& group_address) : _if_name(if_name), _vif_name(vif_name), _group_address(group_address) {} #ifdef XORP_USE_USTL JoinedMulticastGroup() { } #endif virtual ~JoinedMulticastGroup() {} const string& if_name() const { return _if_name; } const string& vif_name() const { return _vif_name; } const IPvX& group_address() const { return _group_address; } /** * Less-Than Operator * * @param other the right-hand operand to compare against. * @return true if the left-hand operand is numerically smaller * than the right-hand operand. */ bool operator<(const JoinedMulticastGroup& other) const { if (_if_name != other._if_name) return (_if_name < other._if_name); if (_vif_name != other._vif_name) return (_vif_name < other._vif_name); return (_group_address < other._group_address); } /** * Equality Operator * * @param other the right-hand operand to compare against. * @return true if the left-hand operand is numerically same as the * right-hand operand. */ bool operator==(const JoinedMulticastGroup& other) const { return ((_if_name == other._if_name) && (_vif_name == other._vif_name) && (_group_address == other._group_address)); } /** * Add a receiver. * * @param receiver_name the name of the receiver to add. */ void add_receiver(const string& receiver_name) { _receivers.insert(receiver_name); } /** * Delete a receiver. * * @param receiver_name the name of the receiver to delete. */ void delete_receiver(const string& receiver_name) { _receivers.erase(receiver_name); } /** * @return true if there are no receivers associated with this group. */ bool empty() const { return _receivers.empty(); } set& get_receivers() { return _receivers; } private: string _if_name; string _vif_name; IPvX _group_address; set _receivers; }; public: /** * Constructor for IoIpComm. * * @param io_ip_manager the corresponding I/O IP manager * (@ref IoIpManager). * @param iftree the interface tree to use. * @param family the address family (AF_INET or AF_INET6 for IPv4 and IPv6 * respectively). * @param ip_protocol the IP protocol number (IPPROTO_*). */ IoIpComm(IoIpManager& io_ip_manager, const IfTree& iftree, int family, uint8_t ip_protocol); /** * Virtual destructor. */ virtual ~IoIpComm(); /** * Allocate the I/O IP plugins (one per data plane manager). */ void allocate_io_ip_plugins(); /** * Deallocate the I/O IP plugins (one per data plane manager). */ void deallocate_io_ip_plugins(); /** * Allocate an I/O IP plugin for a given data plane manager. * * @param fea_data_plane_manager the data plane manager. */ void allocate_io_ip_plugin(FeaDataPlaneManager* fea_data_plane_manager); /** * Deallocate the I/O IP plugin for a given data plane manager. * * @param fea_data_plane_manager the data plane manager. */ void deallocate_io_ip_plugin(FeaDataPlaneManager* fea_data_plane_manager); /** * Start all I/O IP plugins. */ void start_io_ip_plugins(); /** * Stop all I/O IP plugins. */ void stop_io_ip_plugins(); /** * Add a filter to list of input filters. * * The IoIpComm class assumes that the callee will be responsible for * managing the memory associated with the filter and will call * remove_filter() if the filter is deleted or goes out of scope. * * @param filter the filter to add. * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_filter(InputFilter* filter); /** * Remove filter from list of input filters. * * @param filter the filter to remove. * @return XORP_OK on success, otherwise XORP_ERROR. */ int remove_filter(InputFilter* filter); /** * @return true if there are no filters associated with this instance. */ bool no_input_filters() const { return _input_filters.empty(); } /** * Send a raw IP packet. * * @param if_name the interface to send the packet on. It is essential for * multicast. In the unicast case this field may be empty. * @param vif_name the vif to send the packet on. It is essential for * multicast. In the unicast case this field may be empty. * @param src_address the IP source address. * @param dst_address the IP destination address. * @param ip_ttl the IP TTL (hop-limit). If it has a negative value, * the TTL will be set internally before transmission. * @param ip_tos the Type Of Service (IP traffic class for IPv6). * If it has a negative value, the TOS will be * set internally before transmission. * @param ip_router_alert if true, then add the IP Router Alert option to * the IP packet. * @param ip_internet_control if true, then this is IP control traffic. * @param ext_headers_type a vector of integers with the types of the * optional IPv6 extention headers. * @param ext_headers_payload a vector of payload data, one for each * optional IPv6 extention header. The number of entries must match * ext_headers_type. * @param payload the payload, everything after the IP header and options. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int send_packet(const string& if_name, const string& vif_name, const IPvX& src_address, const IPvX& dst_address, int32_t ip_ttl, int32_t ip_tos, bool ip_router_alert, bool ip_internet_control, const vector& ext_headers_type, const vector >& ext_headers_payload, const vector& payload, string& error_msg); /** * Received a raw IP packet. * * @param if_name the interface name the packet arrived on. * @param vif_name the vif name the packet arrived on. * @param src_address the IP source address. * @param dst_address the IP destination address. * @param ip_ttl the IP TTL (hop-limit). If it has a negative value, * then the received value is unknown. * @param ip_tos The type of service (Diffserv/ECN bits for IPv4). If it * has a negative value, then the received value is unknown. * @param ip_router_alert if true, the IP Router Alert option was * included in the IP packet. * @param ip_internet_control if true, then this is IP control traffic. * @param ext_headers_type a vector of integers with the types of the * optional IPv6 extention headers. * @param ext_headers_payload a vector of payload data, one for each * optional IPv6 extention header. The number of entries must match * ext_headers_type. * @param packet the payload, everything after the IP header and * options. */ virtual void recv_packet(const string& if_name, const string& vif_name, const IPvX& src_address, const IPvX& dst_address, int32_t ip_ttl, int32_t ip_tos, bool ip_router_alert, bool ip_internet_control, const vector& ext_headers_type, const vector >& ext_headers_payload, const vector& payload); /** * Received a multicast forwarding related upcall from the system. * * Examples of such upcalls are: "nocache", "wrongiif", "wholepkt", * "bw_upcall". * * @param payload the payload data for the upcall. */ virtual void recv_system_multicast_upcall(const vector& payload); /** * Join an IP multicast group. * * @param if_name the interface through which packets should be accepted. * @param vif_name the vif through which packets should be accepted. * @param group_address the multicast group address to join. * @param receiver_name the name of the receiver. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int join_multicast_group(const string& if_name, const string& vif_name, const IPvX& group_address, const string& receiver_name, string& error_msg); /** * Leave an IP multicast group. * * @param if_name the interface through which packets should not be * accepted. * @param vif_name the vif through which packets should not be accepted. * @param group_address the multicast group address to leave. * @param receiver_name the name of the receiver. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int leave_multicast_group(const string& if_name, const string& vif_name, const IPvX& group_address, const string& receiver_name, string& error_msg); /** * Leave all IP multicast groups on this interface. * * @param if_name the interface through which packets should not be * accepted. * @param vif_name the vif through which packets should not be accepted. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int leave_all_multicast_groups(const string& if_name, const string& vif_name, string& error_msg); /** * Get the IP protocol. * * @return the IP protocol. */ uint8_t ip_protocol() const { return (_ip_protocol); } /** * Get the first valid file descriptor for receiving protocol messages. * * @return the first valid file descriptor for receiving protocol * messages. */ XorpFd first_valid_mcast_protocol_fd_in(); private: IoIpComm(const IoIpComm&); // Not implemented. IoIpComm& operator=(const IoIpComm&); // Not implemented. IoIpManager& _io_ip_manager; const IfTree& _iftree; const int _family; const uint8_t _ip_protocol; typedef list >IoIpPlugins; IoIpPlugins _io_ip_plugins; list _input_filters; typedef map JoinedGroupsTable; JoinedGroupsTable _joined_groups_table; }; /** * @short Class that implements the API for sending IP packet to a * receiver. */ class IoIpManagerReceiver { public: /** * Virtual destructor. */ virtual ~IoIpManagerReceiver() {} /** * Data received event. * * @param receiver_name the name of the receiver to send the * IP packet to. * @param header the IP header information. * @param payload the payload, everything after the IP header * and options. */ virtual void recv_event(const string& receiver_name, const struct IPvXHeaderInfo& header, const vector& payload) = 0; }; /** * @short A class that manages raw IP I/O. * * The IoIpManager has two containers: a container for IP protocol handlers * (@ref IoIpComm) indexed by the protocol associated with the handler, and * a container for the filters associated with each receiver_name. When * a receiver registers for interest in a particular type of raw * packet a handler (@ref IoIpComm) is created if necessary, then the * relevent filter is created and associated with the IoIpComm. */ class IoIpManager : public IoIpManagerReceiver, public InstanceWatcher { public: typedef XorpCallback2::RefPtr UpcallReceiverCb; /** * Constructor for IoIpManager. */ IoIpManager(FeaNode& fea_node, const IfTree& iftree); /** * Virtual destructor. */ virtual ~IoIpManager(); /** * Send a raw IP packet. * * @param if_name the interface to send the packet on. It is essential for * multicast. In the unicast case this field may be empty. * @param vif_name the vif to send the packet on. It is essential for * multicast. In the unicast case this field may be empty. * @param src_address the IP source address. * @param dst_address the IP destination address. * @param ip_protocol the IP protocol number. It must be between 1 and * 255. * @param ip_ttl the IP TTL (hop-limit). If it has a negative value, the * TTL will be set internally before transmission. * @param ip_tos the Type Of Service (IP traffic class for IPv6). If it * has a negative value, the TOS will be set internally before * transmission. * @param ip_router_alert if true, then add the IP Router Alert option to * the IP packet. * @param ip_internet_control if true, then this is IP control traffic. * @param ext_headers_type a vector of integers with the types of the * optional IPv6 extention headers. * @param ext_headers_payload a vector of payload data, one for each * optional IPv6 extention header. The number of entries must match * ext_headers_type. * @param payload the payload, everything after the IP header and options. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int send(const string& if_name, const string& vif_name, const IPvX& src_address, const IPvX& dst_address, uint8_t ip_protocol, int32_t ip_ttl, int32_t ip_tos, bool ip_router_alert, bool ip_internet_control, const vector& ext_headers_type, const vector >& ext_headers_payload, const vector& payload, string& error_msg); /** * Register to receive IP packets. * * @param family the address family (AF_INET or AF_INET6 for * IPv4 and IPv6 respectively). * @param receiver_name the name of the receiver. * @param if_name the interface through which packets should be accepted. * @param vif_name the vif through which packets should be accepted. * @param ip_protocol the IP protocol number that the receiver is * interested in. It must be between 0 and 255. A protocol number of 0 is * used to specify all protocols. * @param enable_multicast_loopback if true then enable delivering of * multicast datagrams back to this host (assuming the host is a member of * the same multicast group. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int register_receiver(int family, const string& receiver_name, const string& if_name, const string& vif_name, uint8_t ip_protocol, bool enable_multicast_loopback, string& error_msg); /** * Unregister to receive IP packets. * * @param family the address family (AF_INET or AF_INET6 for * IPv4 and IPv6 respectively). * @param receiver_name the name of the receiver. * @param if_name the interface through which packets should not be * accepted. * @param vif_name the vif through which packets should not be accepted. * @param ip_protocol the IP Protocol number that the receiver is not * interested in anymore. It must be between 0 and 255. A protocol number * of 0 is used to specify all protocols. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int unregister_receiver(int family, const string& receiver_name, const string& if_name, const string& vif_name, uint8_t ip_protocol, string& error_msg); /** * Join an IP multicast group. * * @param receiver_name the name of the receiver. * @param if_name the interface through which packets should be accepted. * @param vif_name the vif through which packets should be accepted. * @param ip_protocol the IP protocol number that the receiver is * interested in. It must be between 0 and 255. A protocol number of 0 is * used to specify all protocols. * @param group_address the multicast group address to join. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int join_multicast_group(const string& receiver_name, const string& if_name, const string& vif_name, uint8_t ip_protocol, const IPvX& group_address, string& error_msg); /** * Leave an IP multicast group. * * @param receiver_name the name of the receiver. * @param if_name the interface through which packets should not be * accepted. * @param vif_name the vif through which packets should not be accepted. * @param ip_protocol the IP protocol number that the receiver is not * interested in anymore. It must be between 0 and 255. A protocol number * of 0 is used to specify all protocols. * @param group_address the multicast group address to leave. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int leave_multicast_group(const string& receiver_name, const string& if_name, const string& vif_name, uint8_t ip_protocol, const IPvX& group_address, string& error_msg); /** Leave all multicast groups on this vif */ int leave_all_multicast_groups(const string& if_name, const string& vif_name, string& error_msg); /** * Register to receive multicast forwarding related upcalls from the * system. * * @param family the address family (AF_INET or AF_INET6 for * IPv4 and IPv6 respectively). * @param ip_protocol the IP protocol number that the receiver is * interested in. It must be between 0 and 255. A protocol number of 0 is * used to specify all protocols. * @param receiver_cb the receiver callback to be invoked when an * upcall is received. * @param receiver_fd the return-by-reference file descriptor for * the socket that receives the upcalls. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int register_system_multicast_upcall_receiver(int family, uint8_t ip_protocol, IoIpManager::UpcallReceiverCb receiver_cb, XorpFd& mcast_receiver_fd, string& error_msg); /** * Unregister to receive multicast forwarding related upcalls from the * system. * * @param family the address family (AF_INET or AF_INET6 for * IPv4 and IPv6 respectively). * @param ip_protocol the IP Protocol number that the receiver is not * interested in anymore. It must be between 0 and 255. A protocol number * of 0 is used to specify all protocols. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int unregister_system_multicast_upcall_receiver(int family, uint8_t ip_protocol, string& error_msg); /** * Data received event. * * @param receiver_name the name of the receiver to send the IP packet to. * @param header the IP header information. * @param payload the payload, everything after the IP header and options. */ void recv_event(const string& receiver_name, const struct IPvXHeaderInfo& header, const vector& payload); /** * Inform the watcher that a component instance is alive. * * @param instance_name the name of the instance that is alive. */ void instance_birth(const string& instance_name); /** * Inform the watcher that a component instance is dead. * * @param instance_name the name of the instance that is dead. */ void instance_death(const string& instance_name); /** * Set the instance that is responsible for sending IP packets * to a receiver. */ void set_io_ip_manager_receiver(IoIpManagerReceiver* v) { _io_ip_manager_receiver = v; } /** * Get a reference to the interface tree. * * @return a reference to the interface tree (@ref IfTree). */ const IfTree& iftree() const { return _iftree; } /** * Register @ref FeaDataPlaneManager data plane manager. * * @param fea_data_plane_manager the data plane manager to register. * @param is_exclusive if true, the manager is registered as the * exclusive manager, otherwise is added to the list of managers. * @return XORP_OK on success, otherwise XORP_ERROR. */ int register_data_plane_manager(FeaDataPlaneManager* fea_data_plane_manager, bool is_exclusive); /** * Unregister @ref FeaDataPlaneManager data plane manager. * * @param fea_data_plane_manager the data plane manager to unregister. * @return XORP_OK on success, otherwise XORP_ERROR. */ int unregister_data_plane_manager(FeaDataPlaneManager* fea_data_plane_manager); /** * Get the list of registered data plane managers. * * @return the list of registered data plane managers. */ list& fea_data_plane_managers() { return _fea_data_plane_managers; } private: typedef map CommTable; typedef multimap FilterBag; /** * Get the CommTable for an address family. * * @param family the address family. * @return a reference to the CommTable for the address family. */ CommTable& comm_table_by_family(int family); /** * Get the FilterBag for an address family. * * @param family the address family. * @return a reference to the FilterBag for the address family. */ FilterBag& filters_by_family(int family); /** * Erase filters for a given receiver name. * * @param family the address family. * @param receiver_name the name of the receiver. */ void erase_filters_by_receiver_name(int family, const string& receiver_name); /** * Test whether there is a filter for a given receiver name. * * @param receiver_name the name of the receiver. * @return true if there is a filter for the given receiver name, * otherwise false. */ bool has_filter_by_receiver_name(const string& receiver_name) const; /** * Erase filters for a given CommTable and FilterBag. * * @param comm_table the associated CommTable. * @param filters the associated FilterBag. * @param begin the begin iterator to the FilterBag for the set of * filters to erase. * @param end the end iterator to the FilterBag for the set of filters * to erase. */ void erase_filters(CommTable& comm_table, FilterBag& filters, const FilterBag::iterator& begin, const FilterBag::iterator& end); FeaNode& _fea_node; EventLoop& _eventloop; const IfTree& _iftree; // Collection of IP communication handlers keyed by protocol. CommTable _comm_table4; CommTable _comm_table6; // Collection of input filters created by IoIpManager FilterBag _filters4; FilterBag _filters6; IoIpManagerReceiver* _io_ip_manager_receiver; list _fea_data_plane_managers; }; #endif // __FEA_IO_IP_MANAGER_HH__ xorp/fea/mfea_mrouter.hh0000664000076400007640000005010011540225525015416 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __FEA_MFEA_MROUTER_HH__ #define __FEA_MFEA_MROUTER_HH__ // // Multicast routing kernel-access specific definitions. // #ifdef HAVE_SYS_UIO_H #include #endif #include "libxorp/eventloop.hh" #include "libproto/proto_unit.hh" // // Constants definitions // // // Structures/classes, typedefs and macros // class IPvX; class MfeaNode; class SgCount; class TimeVal; class VifCount; class FibConfig; /** * @short A class for multicast routing related I/O communication. * * In case of UNIX kernels, we cannot have more than one MfeaMrouter * per address family (i.e., one per IPv4, and one per IPv6). */ class MfeaMrouter : public ProtoUnit { public: /** * Constructor for given MFEA node. * * @param mfea_node the MFEA node (@ref MfeaNode) this entry belongs to. */ MfeaMrouter(MfeaNode& mfea_node, const FibConfig& fibconfig); /** * Destructor */ virtual ~MfeaMrouter(); /** * Start the @ref MfeaMrouter. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int start(); /** * Stop the @ref MfeaMrouter. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int stop(); #ifdef USE_MULT_MCAST_TABLES /** Get the multicast table id that is currently configured. * Currently, changing configured table-id at run-time will break * things, by the way. */ int getTableId() const; #endif /** * Test if the underlying system supports IPv4 multicast routing. * * @return true if the underlying system supports IPv4 multicast routing, * otherwise false. */ bool have_multicast_routing4() const; /** * Test if the underlying system supports IPv6 multicast routing. * * @return true if the underlying system supports IPv6 multicast routing, * otherwise false. */ bool have_multicast_routing6() const; /** * Test whether the IPv4 multicast forwarding engine is enabled or disabled * to forward packets. * * @param ret_value if true on return, then the IPv4 multicast forwarding * is enabled, otherwise is disabled. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int multicast_forwarding_enabled4(bool& ret_value, string& error_msg) const; /** * Test whether the IPv6 multicast forwarding engine is enabled or disabled * to forward packets. * * @param ret_value if true on return, then the IPv6 multicast forwarding * is enabled, otherwise is disabled. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int multicast_forwarding_enabled6(bool& ret_value, string& error_msg) const; /** * Set the IPv4 multicast forwarding engine to enable or disable forwarding * of packets. * * @param v if true, then enable IPv4 multicast forwarding, otherwise * disable it. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_multicast_forwarding_enabled4(bool v, string& error_msg); /** * Set the IPv6 multicast forwarding engine to enable or disable forwarding * of packets. * * @param v if true, then enable IPv6 multicast forwarding, otherwise * disable it. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_multicast_forwarding_enabled6(bool v, string& error_msg); /** * Get the protocol that would be used in case of mrouter socket. * * Return value: the protocol number on success, otherwise -1. **/ int kernel_mrouter_ip_protocol() const; /** * Get the mrouter socket. * * The mrouter socket is used for various multicast-related access. * * @return the socket value. */ XorpFd mrouter_socket() const { return (_mrouter_socket); } /** * Start/enable the multicast routing in the kernel. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int start_mrt(); /** * Stop/disable the multicast routing in the kernel. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int stop_mrt(); /** * Start/enable PIM processing in the kernel. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int start_pim(string& error_msg); /** * Stop/disable PIM processing in the kernel. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int stop_pim(string& error_msg); /** * Add a virtual multicast interface to the kernel. * * @param vif_index the vif index of the virtual interface to add. * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_multicast_vif(uint32_t vif_index); /** * Delete a virtual multicast interface from the kernel. * * @param vif_index the vif index of the interface to delete. * @return XORP_OK on success, otherwise XORP_ERROR. */ int delete_multicast_vif(uint32_t vif_index); /** * 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. * * @param source the MFC source address. * @param group the MFC group address. * @param iif_vif_index the MFC incoming interface index. * @param oifs_ttl an array with the min. TTL a packet should have to be * forwarded. * @param oifs_flags an array with misc. flags for the MFC to install. * Note that those flags are supported only by the advanced multicast API. * @param rp_addr the RP address. * @return XORP_OK on success, otherwise XORP_ERROR. */ int 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); /** * Delete a Multicast Forwarding Cache (MFC) entry in the kernel. * * @param source the MFC source address. * @param group the MFC group address. * @return XORP_OK on success, otherwise XORP_ERROR. */ int delete_mfc(const IPvX& source, const IPvX& group); /** * Add a dataflow monitor entry in the kernel. * * Note: either @ref is_threshold_in_packets or @ref is_threshold_in_bytes * (or both) must be true. * Note: either @ref is_geq_upcall or @ref is_leq_upcall * (but not both) must be true. * * @param source the source address. * @param group the group address. * @param threshold_interval the dataflow threshold interval. * @param threshold_packets the threshold (in number of packets) to * compare against. * @param threshold_bytes the threshold (in number of bytes) to * compare against. * @param is_threshold_in_packets if true, @ref threshold_packets is valid. * @param is_threshold_in_bytes if true, @ref threshold_bytes is valid. * @param is_geq_upcall if true, the operation for comparison is ">=". * @param is_leq_upcall if true, the operation for comparison is "<=". * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int 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); /** * Delete a dataflow monitor entry from the kernel. * * Note: either @ref is_threshold_in_packets or @ref is_threshold_in_bytes * (or both) must be true. * Note: either @ref is_geq_upcall or @ref is_leq_upcall * (but not both) must be true. * * @param source the source address. * @param group the group address. * @param threshold_interval the dataflow threshold interval. * @param threshold_packets the threshold (in number of packets) to * compare against. * @param threshold_bytes the threshold (in number of bytes) to * compare against. * @param is_threshold_in_packets if true, @ref threshold_packets is valid. * @param is_threshold_in_bytes if true, @ref threshold_bytes is valid. * @param is_geq_upcall if true, the operation for comparison is ">=". * @param is_leq_upcall if true, the operation for comparison is "<=". * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int 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); /** * Delete all dataflow monitor entries from the kernel * for a given source and group address. * * @param source the source address. * @param group the group address. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int delete_all_bw_upcall(const IPvX& source, const IPvX& group, string& error_msg); /** * 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. * * @param source the MFC source address. * @param group the MFC group address. * @param sg_count a reference to a @ref SgCount class to place the result. * @return XORP_OK on success, otherwise XORP_ERROR. */ int get_sg_count(const IPvX& source, const IPvX& group, SgCount& sg_count); /** * Get various counters per virtual interface. * * Get the number of packets and bytes received on, or forwarded on * a particular multicast interface. * * @param vif_index the vif index of the virtual multicast interface whose * statistics we need. * @param vif_count a reference to a @ref VifCount class to store * the result. * @return XORP_OK on success, otherwise XORP_ERROR. */ int get_vif_count(uint32_t vif_index, VifCount& vif_count); /** * Get the flag that indicates whether the kernel supports disabling of * WRONGVIF signal per (S,G) per interface. * * @return true if the kernel supports disabling of WRONGVIF signal * per (S,G) per interface, otherwise false. */ bool mrt_api_mrt_mfc_flags_disable_wrongvif() const { return (_mrt_api_mrt_mfc_flags_disable_wrongvif); } /** * Get the flag that indicates whether the kernel supports setting of * the Border bit flag per (S,G) per interface. * * The Border bit flag is used for PIM-SM Register encapsulation in * the kernel. * * @return true if the kernel supports setting of the Border bit flag * per (S,G) per interface, otherwise false. */ bool mrt_api_mrt_mfc_flags_border_vif() const { return (_mrt_api_mrt_mfc_flags_border_vif); } /** * Get the flag that indicates whether the kernel supports adding * the RP address to the kernel. * * The RP address is used for PIM-SM Register encapsulation in * the kernel. * * @return true if the kernel supports adding the RP address to the kernel, * otherwise false. */ bool mrt_api_mrt_mfc_rp() const { return (_mrt_api_mrt_mfc_rp); } /** * Get the flag that indicates whether the kernel supports the bandwidth * upcall mechanism. * * @return true if the kernel supports the bandwidth upcall mechanism. */ bool mrt_api_mrt_mfc_bw_upcall() const { return (_mrt_api_mrt_mfc_bw_upcall); } /** * 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. * * @param databuf the data buffer. * @param datalen the length of the data in @ref databuf. * @return XORP_OK on success, otherwise XORP_ERROR. */ int kernel_call_process(const uint8_t *databuf, size_t datalen); private: // Private functions MfeaNode& mfea_node() const { return (_mfea_node); } // Private state MfeaNode& _mfea_node; // The MFEA node I belong to XorpFd _mrouter_socket; // The socket for multicast routing access // // Flags about various support by the advanced kernel multicast API: // - support for disabling WRONGVIF signals per vif // - support for the border bit flag (per MFC per vif) // - support for kernel-level PIM Register encapsulation // - support for bandwidth-related upcalls from the kernel // bool _mrt_api_mrt_mfc_flags_disable_wrongvif; bool _mrt_api_mrt_mfc_flags_border_vif; bool _mrt_api_mrt_mfc_rp; bool _mrt_api_mrt_mfc_bw_upcall; // // Original state from the underlying system before the MFEA was started // bool _multicast_forwarding_enabled; const FibConfig& _fibconfig; }; /** * @short Class that contains various counters per (S,G) entry. * * All counters are related to the multicast data packets per (S,G) entry. */ class SgCount { public: /** * Default constructor */ SgCount() : _pktcnt(0), _bytecnt(0), _wrong_if(0) {} /** * Assignment Operator * * @param sg_count the value to assing to this entry. * @return the entry with the new value assigned. */ SgCount& operator=(const SgCount& sg_count) { _pktcnt = sg_count.pktcnt(); _bytecnt = sg_count.bytecnt(); _wrong_if = sg_count.wrong_if(); return (*this); } /** * Assign-Sum Operator * * @param sg_count the value to add to this entry. * @return the entry with the new value after @ref sg_count was added. */ SgCount& operator+=(const SgCount& sg_count) { _pktcnt += sg_count.pktcnt(); _bytecnt += sg_count.bytecnt(); _wrong_if += sg_count.wrong_if(); return (*this); } /** * Assign-Difference Operator * * @param sg_count the value to substract from this entry. * @return the entry with the new value after @ref sg_count was * substracted. */ SgCount& operator-=(const SgCount& sg_count) { _pktcnt -= sg_count.pktcnt(); _bytecnt -= sg_count.bytecnt(); _wrong_if -= sg_count.wrong_if(); return (*this); } /** * Get the packet count. * * @return the packet count. */ size_t pktcnt() const { return (_pktcnt); } /** * Get the byte count. * * @return the byte count. */ size_t bytecnt() const { return (_bytecnt); } /** * Get the number of packets received on wrong interface. * * @return the number of packets received on wrong interface. */ size_t wrong_if() const { return (_wrong_if); } /** * Set the packet count. * * @param v the value to assign to the packet count. */ void set_pktcnt(size_t v) { _pktcnt = v; } /** * Set the byte count. * * @param v the value to assign to the byte count. */ void set_bytecnt(size_t v) { _bytecnt = v; } /** * Set the wrong-interface packet count. * * @param v the value to assign to the wrong-interface packet count. */ void set_wrong_if(size_t v) { _wrong_if = v; } /** * Reset all counters. */ void reset() { _pktcnt = 0; _bytecnt = 0; _wrong_if = 0; } /** * Test if this entry contains valid counters. * * @return true if this entry contains valid counters, otherwise false. */ bool is_valid() const { return (! ((_pktcnt == (size_t)~0) && (_bytecnt == (size_t)~0) && (_wrong_if == (size_t)~0))); } private: size_t _pktcnt; // Number of multicast data packets received size_t _bytecnt; // Number of multicast data bytes received size_t _wrong_if; // Number of multicast data packets received // on wrong iif }; // // A class that contains information about a vif in the kernel. // /** * @short Class that contains various counters per virtual interface. * * All counters are related to the multicast data packets per virtual * interface. */ class VifCount { public: /** * Default constructor */ VifCount() : _icount(0), _ocount(0), _ibytes(0), _obytes(0) {} /** * Assignment Operator * * @param vif_count the value to assign to this entry. * @return the entry with the new value assigned. */ VifCount& operator=(const VifCount& vif_count) { _icount = vif_count.icount(); _ocount = vif_count.ocount(); _ibytes = vif_count.ibytes(); _obytes = vif_count.obytes(); return (*this); } /** * Get the input packet count. * * @return the input packet count. */ size_t icount() const { return (_icount); } /** * Get the output packet count. * * @return the output packet count. */ size_t ocount() const { return (_ocount); } /** * Get the input byte count. * * @return the input byte count. */ size_t ibytes() const { return (_ibytes); } /** * Get the output byte count. * * @return the output byte count. */ size_t obytes() const { return (_obytes); } /** * Set the input packet count. * * @param v the value to assign to the data packet count. */ void set_icount(size_t v) { _icount = v; } /** * Set the output packet count. * * @param v the value to assign to the output packet count. */ void set_ocount(size_t v) { _ocount = v; } /** * Set the input byte count. * * @param v the value to assign to the data byte count. */ void set_ibytes(size_t v) { _ibytes = v; } /** * Set the output byte count. * * @param v the value to assign to the output byte count. */ void set_obytes(size_t v) { _obytes = v; } /** * Test if this entry contains valid counters. * * @return true if this entry contains valid counters, otherwise false. */ bool is_valid() const { return (! ((_icount == (size_t)~0) && (_ocount == (size_t)~0) && (_ibytes == (size_t)~0) && (_obytes == (size_t)~0))); } private: size_t _icount; // Number of input multicast data packets size_t _ocount; // Number of output multicast data packets size_t _ibytes; // Number of input multicast data bytes size_t _obytes; // Number of output multicast data bytes }; // // Global variables // // // Global functions prototypes // #endif // __FEA_MFEA_MROUTER_HH__ xorp/static_routes/0000775000076400007640000000000011631506536014556 5ustar greearbgreearbxorp/static_routes/static_routes_module.h0000664000076400007640000000243211421137511021153 0ustar greearbgreearb/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- */ /* * Copyright (c) 2001-2009 XORP, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 2, June * 1991 as published by the Free Software Foundation. Redistribution * and/or modification of this program under the terms of any other * version of the GNU General Public License is not permitted. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, * see the GNU General Public License, Version 2, a copy of which can be * found in the XORP LICENSE.gpl file. * * XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; * http://xorp.net */ /* * $XORP: xorp/static_routes/static_routes_module.h,v 1.10 2008/10/02 21:58:29 bms Exp $ */ /* * Module definitions. */ #ifndef __STATIC_ROUTES_STATIC_ROUTES_MODULE_H__ #define __STATIC_ROUTES_STATIC_ROUTES_MODULE_H__ #ifndef XORP_MODULE_NAME #define XORP_MODULE_NAME "STATIC_ROUTES" #endif #ifndef XORP_MODULE_VERSION #define XORP_MODULE_VERSION "0.1" #endif #endif /* __STATIC_ROUTES_STATIC_ROUTES_MODULE_H__ */ xorp/static_routes/xrl_static_routes_node.hh0000664000076400007640000003665711540225535021676 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/static_routes/xrl_static_routes_node.hh,v 1.26 2008/10/02 21:58:29 bms Exp $ #ifndef __STATIC_ROUTES_XRL_STATIC_ROUTES_NODE_HH__ #define __STATIC_ROUTES_XRL_STATIC_ROUTES_NODE_HH__ // // StaticRoutes XRL-aware node definition. // #include "libxipc/xrl_std_router.hh" #include "libfeaclient/ifmgr_xrl_mirror.hh" #include "xrl/interfaces/finder_event_notifier_xif.hh" #include "xrl/interfaces/rib_xif.hh" #include "xrl/targets/static_routes_base.hh" #include "static_routes_node.hh" // // The top-level class that wraps-up everything together under one roof // class XrlStaticRoutesNode : public StaticRoutesNode, public XrlStdRouter, public XrlStaticRoutesTargetBase { public: XrlStaticRoutesNode(EventLoop& eventloop, const string& class_name, const string& finder_hostname, uint16_t finder_port, const string& finder_target, const string& fea_target, const string& rib_target); ~XrlStaticRoutesNode(); /** * Startup the node operation. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int startup(); /** * Shutdown the node operation. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int shutdown(); /** * Get a reference to the XrlRouter instance. * * @return a reference to the XrlRouter (@ref XrlRouter) instance. */ XrlRouter& xrl_router() { return *this; } protected: // // XRL target methods // /** * Get name of Xrl Target */ XrlCmdError common_0_1_get_target_name( // Output values, string& name); /** * Get version string from Xrl Target */ XrlCmdError common_0_1_get_version( // Output values, string& version); /** * Get status of Xrl Target */ XrlCmdError common_0_1_get_status( // Output values, uint32_t& status, string& reason); /** * Request clean shutdown of Xrl Target */ XrlCmdError common_0_1_shutdown(); XrlCmdError common_0_1_startup(); /** * Announce target birth to observer. * * @param target_class the target class name. * * @param target_instance the target instance name. */ XrlCmdError finder_event_observer_0_1_xrl_target_birth( // Input values, const string& target_class, const string& target_instance); /** * Announce target death to observer. * * @param target_class the target class name. * * @param target_instance the target instance name. */ XrlCmdError finder_event_observer_0_1_xrl_target_death( // Input values, const string& target_class, const string& target_instance); /** * Enable/disable/start/stop StaticRoutes. * * @param enable if true, then enable StaticRoutes, otherwise disable it. */ XrlCmdError static_routes_0_1_enable_static_routes( // Input values, const bool& enable); XrlCmdError static_routes_0_1_start_static_routes(); XrlCmdError static_routes_0_1_stop_static_routes(); /** * Add/replace/delete a static route. * * @param unicast if true, then the route would be used for unicast * routing. * * @param multicast if true, then the route would be used in the MRIB * (Multicast Routing Information Base) for multicast purpose (e.g., * computing the Reverse-Path Forwarding information). * * @param network the network address prefix this route applies to. * * @param nexthop the address of the next-hop router for this route. * * @param metric the metric distance for this route. */ XrlCmdError static_routes_0_1_add_route4( // Input values, const bool& unicast, const bool& multicast, const IPv4Net& network, const IPv4& nexthop, const uint32_t& metric); XrlCmdError static_routes_0_1_add_route6( // Input values, const bool& unicast, const bool& multicast, const IPv6Net& network, const IPv6& nexthop, const uint32_t& metric); XrlCmdError static_routes_0_1_replace_route4( // Input values, const bool& unicast, const bool& multicast, const IPv4Net& network, const IPv4& nexthop, const uint32_t& metric); XrlCmdError static_routes_0_1_replace_route6( // Input values, const bool& unicast, const bool& multicast, const IPv6Net& network, const IPv6& nexthop, const uint32_t& metric); XrlCmdError static_routes_0_1_delete_route4( // Input values, const bool& unicast, const bool& multicast, const IPv4Net& network, const IPv4& nexthop); XrlCmdError static_routes_0_1_delete_route6( // Input values, const bool& unicast, const bool& multicast, const IPv6Net& network, const IPv6& nexthop); /** * Add/replace/delete a backup static route. * * @param unicast if true, then the route would be used for unicast * routing. * * @param multicast if true, then the route would be used in the MRIB * (Multicast Routing Information Base) for multicast purpose (e.g., * computing the Reverse-Path Forwarding information). * * @param network the network address prefix this route applies to. * * @param nexthop the address of the next-hop router for this route. * * @param metric the metric distance for this route. */ XrlCmdError static_routes_0_1_add_backup_route4( // Input values, const bool& unicast, const bool& multicast, const IPv4Net& network, const IPv4& nexthop, const uint32_t& metric); XrlCmdError static_routes_0_1_add_backup_route6( // Input values, const bool& unicast, const bool& multicast, const IPv6Net& network, const IPv6& nexthop, const uint32_t& metric); XrlCmdError static_routes_0_1_replace_backup_route4( // Input values, const bool& unicast, const bool& multicast, const IPv4Net& network, const IPv4& nexthop, const uint32_t& metric); XrlCmdError static_routes_0_1_replace_backup_route6( // Input values, const bool& unicast, const bool& multicast, const IPv6Net& network, const IPv6& nexthop, const uint32_t& metric); XrlCmdError static_routes_0_1_delete_backup_route4( // Input values, const bool& unicast, const bool& multicast, const IPv4Net& network, const IPv4& nexthop); XrlCmdError static_routes_0_1_delete_backup_route6( // Input values, const bool& unicast, const bool& multicast, const IPv6Net& network, const IPv6& nexthop); /** * Add/replace/delete a static route by explicitly specifying the network * interface toward the destination. * * @param unicast if true, then the route would be used for unicast * routing. * * @param multicast if true, then the route would be used in the MRIB * (Multicast Routing Information Base) for multicast purpose (e.g., * computing the Reverse-Path Forwarding information). * * @param network the network address prefix this route applies to. * * @param nexthop the address of the next-hop router for this route. * * @param ifname of the name of the physical interface toward the * destination. * * @param vifname of the name of the virtual interface toward the * destination. * * @param metric the metric distance for this route. */ XrlCmdError static_routes_0_1_add_interface_route4( // Input values, const bool& unicast, const bool& multicast, const IPv4Net& network, const IPv4& nexthop, const string& ifname, const string& vifname, const uint32_t& metric); XrlCmdError static_routes_0_1_add_interface_route6( // Input values, const bool& unicast, const bool& multicast, const IPv6Net& network, const IPv6& nexthop, const string& ifname, const string& vifname, const uint32_t& metric); XrlCmdError static_routes_0_1_replace_interface_route4( // Input values, const bool& unicast, const bool& multicast, const IPv4Net& network, const IPv4& nexthop, const string& ifname, const string& vifname, const uint32_t& metric); XrlCmdError static_routes_0_1_replace_interface_route6( // Input values, const bool& unicast, const bool& multicast, const IPv6Net& network, const IPv6& nexthop, const string& ifname, const string& vifname, const uint32_t& metric); XrlCmdError static_routes_0_1_delete_interface_route4( // Input values, const bool& unicast, const bool& multicast, const IPv4Net& network, const IPv4& nexthop, const string& ifname, const string& vifname); XrlCmdError static_routes_0_1_delete_interface_route6( // Input values, const bool& unicast, const bool& multicast, const IPv6Net& network, const IPv6& nexthop, const string& ifname, const string& vifname); /** * Add/replace/delete a backup static route by explicitly specifying the * network interface toward the destination. * * @param unicast if true, then the route would be used for unicast * routing. * * @param multicast if true, then the route would be used in the MRIB * (Multicast Routing Information Base) for multicast purpose (e.g., * computing the Reverse-Path Forwarding information). * * @param network the network address prefix this route applies to. * * @param nexthop the address of the next-hop router for this route. * * @param ifname of the name of the physical interface toward the * destination. * * @param vifname of the name of the virtual interface toward the * destination. * * @param metric the metric distance for this route. */ XrlCmdError static_routes_0_1_add_backup_interface_route4( // Input values, const bool& unicast, const bool& multicast, const IPv4Net& network, const IPv4& nexthop, const string& ifname, const string& vifname, const uint32_t& metric); XrlCmdError static_routes_0_1_add_backup_interface_route6( // Input values, const bool& unicast, const bool& multicast, const IPv6Net& network, const IPv6& nexthop, const string& ifname, const string& vifname, const uint32_t& metric); XrlCmdError static_routes_0_1_replace_backup_interface_route4( // Input values, const bool& unicast, const bool& multicast, const IPv4Net& network, const IPv4& nexthop, const string& ifname, const string& vifname, const uint32_t& metric); XrlCmdError static_routes_0_1_replace_backup_interface_route6( // Input values, const bool& unicast, const bool& multicast, const IPv6Net& network, const IPv6& nexthop, const string& ifname, const string& vifname, const uint32_t& metric); XrlCmdError static_routes_0_1_delete_backup_interface_route4( // Input values, const bool& unicast, const bool& multicast, const IPv4Net& network, const IPv4& nexthop, const string& ifname, const string& vifname); XrlCmdError static_routes_0_1_delete_backup_interface_route6( // Input values, const bool& unicast, const bool& multicast, const IPv6Net& network, const IPv6& nexthop, const string& ifname, const string& vifname); /** * Enable/disable the StaticRoutes trace log for all operations. * * @param enable if true, then enable the trace log, otherwise disable it. */ XrlCmdError static_routes_0_1_enable_log_trace_all( // Input values, const bool& enable); /** * Configure a policy filter. * * @param filter Id of filter to configure. * @param conf Configuration of filter. */ XrlCmdError policy_backend_0_1_configure( // Input values, const uint32_t& filter, const string& conf); /** * Reset a policy filter. * * @param filter Id of filter to reset. */ XrlCmdError policy_backend_0_1_reset( // Input values, const uint32_t& filter); /** * Push routes through policy filters for re-filtering. */ XrlCmdError policy_backend_0_1_push_routes(); private: const ServiceBase* ifmgr_mirror_service_base() const { return dynamic_cast(&_ifmgr); } const IfMgrIfTree& ifmgr_iftree() const { return _ifmgr.iftree(); } /** * Called when Finder connection is established. * * Note that this method overwrites an XrlRouter virtual method. */ virtual void finder_connect_event(); /** * Called when Finder disconnect occurs. * * Note that this method overwrites an XrlRouter virtual method. */ virtual void finder_disconnect_event(); void fea_register_startup(); void finder_register_interest_fea_cb(const XrlError& xrl_error); void fea_register_shutdown(); void finder_deregister_interest_fea_cb(const XrlError& xrl_error); void rib_register_startup(); void finder_register_interest_rib_cb(const XrlError& xrl_error); void rib_register_shutdown(); void finder_deregister_interest_rib_cb(const XrlError& xrl_error); void send_rib_add_tables(); void rib_client_send_add_igp_table4_cb(const XrlError& xrl_error); #ifdef HAVE_IPV6 void rib_client_send_add_igp_table6_cb(const XrlError& xrl_error); #endif void send_rib_delete_tables(); void rib_client_send_delete_igp_table4_cb(const XrlError& xrl_error); #ifdef HAVE_IPV6 void rib_client_send_delete_igp_table6_cb(const XrlError& xrl_error); #endif /** * Inform the RIB about a route change. * * @param static_route the route with the information about the change. */ void inform_rib_route_change(const StaticRoute& static_route); /** * Cancel a pending request to inform the RIB about a route change. * * @param static_route the route with the request that would be canceled. */ void cancel_rib_route_change(const StaticRoute& static_route); void send_rib_route_change(); void send_rib_route_change_cb(const XrlError& xrl_error); EventLoop& _eventloop; XrlRibV0p1Client _xrl_rib_client; const string _finder_target; const string _fea_target; const string _rib_target; IfMgrXrlMirror _ifmgr; list _inform_rib_queue; XorpTimer _inform_rib_queue_timer; XrlFinderEventNotifierV0p1Client _xrl_finder_client; static const TimeVal RETRY_TIMEVAL; bool _is_finder_alive; bool _is_fea_alive; bool _is_fea_registered; bool _is_fea_registering; bool _is_fea_deregistering; XorpTimer _fea_register_startup_timer; XorpTimer _fea_register_shutdown_timer; bool _is_rib_alive; bool _is_rib_registered; bool _is_rib_registering; bool _is_rib_deregistering; bool _is_rib_igp_table4_registered; #ifdef HAVE_IPV6 bool _is_rib_igp_table6_registered; #endif XorpTimer _rib_register_startup_timer; XorpTimer _rib_register_shutdown_timer; XorpTimer _rib_igp_table_registration_timer; }; #endif // __STATIC_ROUTES_XRL_STATIC_ROUTES_NODE_HH__ xorp/static_routes/xrl_static_routes_node.cc0000664000076400007640000015402511540225535021652 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "static_routes_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvx.hh" #include "libxorp/status_codes.h" #include "static_routes_node.hh" #include "xrl_static_routes_node.hh" const TimeVal XrlStaticRoutesNode::RETRY_TIMEVAL = TimeVal(1, 0); XrlStaticRoutesNode::XrlStaticRoutesNode(EventLoop& eventloop, const string& class_name, const string& finder_hostname, uint16_t finder_port, const string& finder_target, const string& fea_target, const string& rib_target) : StaticRoutesNode(eventloop), XrlStdRouter(eventloop, class_name.c_str(), finder_hostname.c_str(), finder_port), XrlStaticRoutesTargetBase(&xrl_router()), _eventloop(eventloop), _xrl_rib_client(&xrl_router()), _finder_target(finder_target), _fea_target(fea_target), _rib_target(rib_target), _ifmgr(eventloop, fea_target.c_str(), xrl_router().finder_address(), xrl_router().finder_port()), _xrl_finder_client(&xrl_router()), _is_finder_alive(false), _is_fea_alive(false), _is_fea_registered(false), _is_fea_registering(false), _is_fea_deregistering(false), _is_rib_alive(false), _is_rib_registered(false), _is_rib_registering(false), _is_rib_deregistering(false), _is_rib_igp_table4_registered(false) #ifdef HAVE_IPV6 , _is_rib_igp_table6_registered(false) #endif { _ifmgr.set_observer(dynamic_cast(this)); _ifmgr.attach_hint_observer(dynamic_cast(this)); } XrlStaticRoutesNode::~XrlStaticRoutesNode() { shutdown(); _ifmgr.detach_hint_observer(dynamic_cast(this)); _ifmgr.unset_observer(dynamic_cast(this)); } int XrlStaticRoutesNode::startup() { return StaticRoutesNode::startup(); } int XrlStaticRoutesNode::shutdown() { return StaticRoutesNode::shutdown(); } // // Finder-related events // /** * Called when Finder connection is established. * * Note that this method overwrites an XrlRouter virtual method. */ void XrlStaticRoutesNode::finder_connect_event() { _is_finder_alive = true; } /** * Called when Finder disconnect occurs. * * Note that this method overwrites an XrlRouter virtual method. */ void XrlStaticRoutesNode::finder_disconnect_event() { XLOG_ERROR("Finder disconnect event. Exiting immediately..."); _is_finder_alive = false; StaticRoutesNode::shutdown(); } // // Register with the FEA // void XrlStaticRoutesNode::fea_register_startup() { bool success; _fea_register_startup_timer.unschedule(); _fea_register_shutdown_timer.unschedule(); if (! _is_finder_alive) return; // The Finder is dead if (_is_fea_registered) return; // Already registered if (! _is_fea_registering) { StaticRoutesNode::incr_startup_requests_n(); // XXX: for the ifmgr _is_fea_registering = true; } // // Register interest in the FEA with the Finder // success = _xrl_finder_client.send_register_class_event_interest( _finder_target.c_str(), xrl_router().instance_name(), _fea_target, callback(this, &XrlStaticRoutesNode::finder_register_interest_fea_cb)); if (! success) { // // If an error, then start a timer to try again. // _fea_register_startup_timer = _eventloop.new_oneoff_after( RETRY_TIMEVAL, callback(this, &XrlStaticRoutesNode::fea_register_startup)); return; } } void XrlStaticRoutesNode::finder_register_interest_fea_cb(const XrlError& xrl_error) { switch (xrl_error.error_code()) { case OKAY: // // If success, then the FEA birth event will startup the ifmgr // _is_fea_registering = false; _is_fea_registered = true; break; case COMMAND_FAILED: // // If a command failed because the other side rejected it, this is // fatal. // XLOG_FATAL("Cannot register interest in Finder events: %s", xrl_error.str().c_str()); break; case NO_FINDER: case RESOLVE_FAILED: case SEND_FAILED: // // A communication error that should have been caught elsewhere // (e.g., by tracking the status of the Finder and the other targets). // Probably we caught it here because of event reordering. // In some cases we print an error. In other cases our job is done. // XLOG_ERROR("XRL communication error: %s", xrl_error.str().c_str()); break; case BAD_ARGS: case NO_SUCH_METHOD: case INTERNAL_ERROR: // // An error that should happen only if there is something unusual: // e.g., there is XRL mismatch, no enough internal resources, etc. // We don't try to recover from such errors, hence this is fatal. // XLOG_FATAL("Fatal XRL error: %s", xrl_error.str().c_str()); break; case REPLY_TIMED_OUT: case SEND_FAILED_TRANSIENT: // // If a transient error, then start a timer to try again // (unless the timer is already running). // if (! _fea_register_startup_timer.scheduled()) { XLOG_ERROR("Failed to register interest in Finder events: %s. " "Will try again.", xrl_error.str().c_str()); _fea_register_startup_timer = _eventloop.new_oneoff_after( RETRY_TIMEVAL, callback(this, &XrlStaticRoutesNode::fea_register_startup)); } break; } } // // De-register with the FEA // void XrlStaticRoutesNode::fea_register_shutdown() { bool success; _fea_register_startup_timer.unschedule(); _fea_register_shutdown_timer.unschedule(); if (! _is_finder_alive) return; // The Finder is dead if (! _is_fea_alive) return; // The FEA is not there anymore if (! _is_fea_registered) return; // Not registered if (! _is_fea_deregistering) { StaticRoutesNode::incr_shutdown_requests_n(); // XXX: for the ifmgr _is_fea_deregistering = true; } // // De-register interest in the FEA with the Finder // success = _xrl_finder_client.send_deregister_class_event_interest( _finder_target.c_str(), xrl_router().instance_name(), _fea_target, callback(this, &XrlStaticRoutesNode::finder_deregister_interest_fea_cb)); if (! success) { // // If an error, then start a timer to try again. // _fea_register_shutdown_timer = _eventloop.new_oneoff_after( RETRY_TIMEVAL, callback(this, &XrlStaticRoutesNode::fea_register_shutdown)); return; } // // XXX: when the shutdown is completed, StaticRoutesNode::status_change() // will be called. // _ifmgr.shutdown(); } void XrlStaticRoutesNode::finder_deregister_interest_fea_cb( const XrlError& xrl_error) { switch (xrl_error.error_code()) { case OKAY: // // If success, then we are done // _is_fea_deregistering = false; _is_fea_registered = false; break; case COMMAND_FAILED: // // If a command failed because the other side rejected it, this is // fatal. // XLOG_FATAL("Cannot deregister interest in Finder events: %s", xrl_error.str().c_str()); break; case NO_FINDER: case RESOLVE_FAILED: case SEND_FAILED: // // A communication error that should have been caught elsewhere // (e.g., by tracking the status of the Finder and the other targets). // Probably we caught it here because of event reordering. // In some cases we print an error. In other cases our job is done. // _is_fea_deregistering = false; _is_fea_registered = false; break; case BAD_ARGS: case NO_SUCH_METHOD: case INTERNAL_ERROR: // // An error that should happen only if there is something unusual: // e.g., there is XRL mismatch, no enough internal resources, etc. // We don't try to recover from such errors, hence this is fatal. // XLOG_FATAL("Fatal XRL error: %s", xrl_error.str().c_str()); break; case REPLY_TIMED_OUT: case SEND_FAILED_TRANSIENT: // // If a transient error, then start a timer to try again // (unless the timer is already running). // if (! _fea_register_shutdown_timer.scheduled()) { XLOG_ERROR("Failed to deregister interest in Finder events: %s. " "Will try again.", xrl_error.str().c_str()); _fea_register_shutdown_timer = _eventloop.new_oneoff_after( RETRY_TIMEVAL, callback(this, &XrlStaticRoutesNode::fea_register_shutdown)); } break; } } // // Register with the RIB // void XrlStaticRoutesNode::rib_register_startup() { bool success; _rib_register_startup_timer.unschedule(); _rib_register_shutdown_timer.unschedule(); if (! _is_finder_alive) return; // The Finder is dead if (_is_rib_registered) return; // Already registered if (! _is_rib_registering) { if (! _is_rib_igp_table4_registered) StaticRoutesNode::incr_startup_requests_n(); #ifdef HAVE_IPV6 if (! _is_rib_igp_table6_registered) StaticRoutesNode::incr_startup_requests_n(); #endif _is_rib_registering = true; } // // Register interest in the RIB with the Finder // success = _xrl_finder_client.send_register_class_event_interest( _finder_target.c_str(), xrl_router().instance_name(), _rib_target, callback(this, &XrlStaticRoutesNode::finder_register_interest_rib_cb)); if (! success) { // // If an error, then start a timer to try again. // _rib_register_startup_timer = _eventloop.new_oneoff_after( RETRY_TIMEVAL, callback(this, &XrlStaticRoutesNode::rib_register_startup)); return; } } void XrlStaticRoutesNode::finder_register_interest_rib_cb(const XrlError& xrl_error) { switch (xrl_error.error_code()) { case OKAY: // // If success, then the RIB birth event will startup the RIB // registration. // _is_rib_registering = false; _is_rib_registered = true; break; case COMMAND_FAILED: // // If a command failed because the other side rejected it, this is // fatal. // XLOG_FATAL("Cannot register interest in Finder events: %s", xrl_error.str().c_str()); break; case NO_FINDER: case RESOLVE_FAILED: case SEND_FAILED: // // A communication error that should have been caught elsewhere // (e.g., by tracking the status of the Finder and the other targets). // Probably we caught it here because of event reordering. // In some cases we print an error. In other cases our job is done. // XLOG_ERROR("XRL communication error: %s", xrl_error.str().c_str()); break; case BAD_ARGS: case NO_SUCH_METHOD: case INTERNAL_ERROR: // // An error that should happen only if there is something unusual: // e.g., there is XRL mismatch, no enough internal resources, etc. // We don't try to recover from such errors, hence this is fatal. // XLOG_FATAL("Fatal XRL error: %s", xrl_error.str().c_str()); break; case REPLY_TIMED_OUT: case SEND_FAILED_TRANSIENT: // // If a transient error, then start a timer to try again // (unless the timer is already running). // if (! _rib_register_startup_timer.scheduled()) { XLOG_ERROR("Failed to register interest in Finder events: %s. " "Will try again.", xrl_error.str().c_str()); _rib_register_startup_timer = _eventloop.new_oneoff_after( RETRY_TIMEVAL, callback(this, &XrlStaticRoutesNode::rib_register_startup)); } break; } } // // De-register with the RIB // void XrlStaticRoutesNode::rib_register_shutdown() { bool success; _rib_register_startup_timer.unschedule(); _rib_register_shutdown_timer.unschedule(); if (! _is_finder_alive) return; // The Finder is dead if (! _is_rib_alive) return; // The RIB is not there anymore if (! _is_rib_registered) return; // Not registered if (! _is_rib_deregistering) { if (_is_rib_igp_table4_registered) StaticRoutesNode::incr_shutdown_requests_n(); #ifdef HAVE_IPV6 if (_is_rib_igp_table6_registered) StaticRoutesNode::incr_shutdown_requests_n(); #endif _is_rib_deregistering = true; } // // De-register interest in the RIB with the Finder // success = _xrl_finder_client.send_deregister_class_event_interest( _finder_target.c_str(), xrl_router().instance_name(), _rib_target, callback(this, &XrlStaticRoutesNode::finder_deregister_interest_rib_cb)); if (! success) { // // If an error, then start a timer to try again. // _rib_register_shutdown_timer = _eventloop.new_oneoff_after( RETRY_TIMEVAL, callback(this, &XrlStaticRoutesNode::rib_register_shutdown)); return; } send_rib_delete_tables(); } void XrlStaticRoutesNode::finder_deregister_interest_rib_cb( const XrlError& xrl_error) { switch (xrl_error.error_code()) { case OKAY: // // If success, then we are done // _is_rib_deregistering = false; _is_rib_registered = false; break; case COMMAND_FAILED: // // If a command failed because the other side rejected it, this is // fatal. // XLOG_FATAL("Cannot deregister interest in Finder events: %s", xrl_error.str().c_str()); break; case NO_FINDER: case RESOLVE_FAILED: case SEND_FAILED: // // A communication error that should have been caught elsewhere // (e.g., by tracking the status of the Finder and the other targets). // Probably we caught it here because of event reordering. // In some cases we print an error. In other cases our job is done. // _is_rib_deregistering = false; _is_rib_registered = false; break; case BAD_ARGS: case NO_SUCH_METHOD: case INTERNAL_ERROR: // // An error that should happen only if there is something unusual: // e.g., there is XRL mismatch, no enough internal resources, etc. // We don't try to recover from such errors, hence this is fatal. // XLOG_FATAL("Fatal XRL error: %s", xrl_error.str().c_str()); break; case REPLY_TIMED_OUT: case SEND_FAILED_TRANSIENT: // // If a transient error, then start a timer to try again // (unless the timer is already running). // if (! _rib_register_shutdown_timer.scheduled()) { XLOG_ERROR("Failed to deregister interest in Finder events: %s. " "Will try again.", xrl_error.str().c_str()); _rib_register_shutdown_timer = _eventloop.new_oneoff_after( RETRY_TIMEVAL, callback(this, &XrlStaticRoutesNode::rib_register_shutdown)); } break; } } // // Add tables with the RIB // void XrlStaticRoutesNode::send_rib_add_tables() { bool success = true; if (! _is_finder_alive) return; // The Finder is dead if (! _is_rib_igp_table4_registered) { success = _xrl_rib_client.send_add_igp_table4( _rib_target.c_str(), StaticRoutesNode::protocol_name(), xrl_router().class_name(), xrl_router().instance_name(), true, /* unicast */ true, /* multicast */ callback(this, &XrlStaticRoutesNode::rib_client_send_add_igp_table4_cb)); if (success) return; XLOG_ERROR("Failed to register IPv4 IGP table with the RIB. " "Will try again."); goto start_timer_label; } #ifdef HAVE_IPV6 if (! _is_rib_igp_table6_registered) { success = _xrl_rib_client.send_add_igp_table6( _rib_target.c_str(), StaticRoutesNode::protocol_name(), xrl_router().class_name(), xrl_router().instance_name(), true, /* unicast */ true, /* multicast */ callback(this, &XrlStaticRoutesNode::rib_client_send_add_igp_table6_cb)); if (success) return; XLOG_ERROR("Failed to register IPv6 IGP table with the RIB. " "Will try again."); goto start_timer_label; } #endif if (! success) { // // If an error, then start a timer to try again. // start_timer_label: _rib_igp_table_registration_timer = _eventloop.new_oneoff_after( RETRY_TIMEVAL, callback(this, &XrlStaticRoutesNode::send_rib_add_tables)); } } void XrlStaticRoutesNode::rib_client_send_add_igp_table4_cb( const XrlError& xrl_error) { switch (xrl_error.error_code()) { case OKAY: // // If success, then we are done // _is_rib_igp_table4_registered = true; send_rib_add_tables(); StaticRoutesNode::decr_startup_requests_n(); break; case COMMAND_FAILED: // // If a command failed because the other side rejected it, this is // fatal. // XLOG_FATAL("Cannot add IPv4 IGP table to the RIB: %s", xrl_error.str().c_str()); break; case NO_FINDER: case RESOLVE_FAILED: case SEND_FAILED: // // A communication error that should have been caught elsewhere // (e.g., by tracking the status of the Finder and the other targets). // Probably we caught it here because of event reordering. // In some cases we print an error. In other cases our job is done. // XLOG_ERROR("XRL communication error: %s", xrl_error.str().c_str()); break; case BAD_ARGS: case NO_SUCH_METHOD: case INTERNAL_ERROR: // // An error that should happen only if there is something unusual: // e.g., there is XRL mismatch, no enough internal resources, etc. // We don't try to recover from such errors, hence this is fatal. // XLOG_FATAL("Fatal XRL error: %s", xrl_error.str().c_str()); break; case REPLY_TIMED_OUT: case SEND_FAILED_TRANSIENT: // // If a transient error, then start a timer to try again // (unless the timer is already running). // if (! _rib_igp_table_registration_timer.scheduled()) { XLOG_ERROR("Failed to add IPv4 IGP table to the RIB: %s. " "Will try again.", xrl_error.str().c_str()); _rib_igp_table_registration_timer = _eventloop.new_oneoff_after( RETRY_TIMEVAL, callback(this, &XrlStaticRoutesNode::send_rib_add_tables)); } break; } } #ifdef HAVE_IPV6 void XrlStaticRoutesNode::rib_client_send_add_igp_table6_cb( const XrlError& xrl_error) { switch (xrl_error.error_code()) { case OKAY: // // If success, then we are done // _is_rib_igp_table6_registered = true; send_rib_add_tables(); StaticRoutesNode::decr_startup_requests_n(); break; case COMMAND_FAILED: // // If a command failed because the other side rejected it, this is // fatal. // XLOG_FATAL("Cannot add IPv6 IGP table to the RIB: %s", xrl_error.str().c_str()); break; case NO_FINDER: case RESOLVE_FAILED: case SEND_FAILED: // // A communication error that should have been caught elsewhere // (e.g., by tracking the status of the Finder and the other targets). // Probably we caught it here because of event reordering. // In some cases we print an error. In other cases our job is done. // XLOG_ERROR("XRL communication error: %s", xrl_error.str().c_str()); break; case BAD_ARGS: case NO_SUCH_METHOD: case INTERNAL_ERROR: // // An error that should happen only if there is something unusual: // e.g., there is XRL mismatch, no enough internal resources, etc. // We don't try to recover from such errors, hence this is fatal. // XLOG_FATAL("Fatal XRL error: %s", xrl_error.str().c_str()); break; case REPLY_TIMED_OUT: case SEND_FAILED_TRANSIENT: // // If a transient error, then start a timer to try again // (unless the timer is already running). // if (! _rib_igp_table_registration_timer.scheduled()) { XLOG_ERROR("Failed to add IPv6 IGP table to the RIB: %s. " "Will try again.", xrl_error.str().c_str()); _rib_igp_table_registration_timer = _eventloop.new_oneoff_after( RETRY_TIMEVAL, callback(this, &XrlStaticRoutesNode::send_rib_add_tables)); } break; } } #endif // // Delete tables with the RIB // void XrlStaticRoutesNode::send_rib_delete_tables() { bool success = true; if (! _is_finder_alive) return; // The Finder is dead if (_is_rib_igp_table4_registered) { bool success4; success4 = _xrl_rib_client.send_delete_igp_table4( _rib_target.c_str(), StaticRoutesNode::protocol_name(), xrl_router().class_name(), xrl_router().instance_name(), true, /* unicast */ true, /* multicast */ callback(this, &XrlStaticRoutesNode::rib_client_send_delete_igp_table4_cb)); if (success4 != true) { XLOG_ERROR("Failed to deregister IPv4 IGP table with the RIB. " "Will give up."); success = false; } } #ifdef HAVE_IPV6 if (_is_rib_igp_table6_registered) { bool success6; success6 = _xrl_rib_client.send_delete_igp_table6( _rib_target.c_str(), StaticRoutesNode::protocol_name(), xrl_router().class_name(), xrl_router().instance_name(), true, /* unicast */ true, /* multicast */ callback(this, &XrlStaticRoutesNode::rib_client_send_delete_igp_table6_cb)); if (success6 != true) { XLOG_ERROR("Failed to deregister IPv6 IGP table with the RIB. " "Will give up."); success = false; } } #endif if (! success) { StaticRoutesNode::set_status(SERVICE_FAILED); StaticRoutesNode::update_status(); } } void XrlStaticRoutesNode::rib_client_send_delete_igp_table4_cb( const XrlError& xrl_error) { switch (xrl_error.error_code()) { case OKAY: // // If success, then we are done // _is_rib_igp_table4_registered = false; StaticRoutesNode::decr_shutdown_requests_n(); break; case COMMAND_FAILED: // // If a command failed because the other side rejected it, this is // fatal. // XLOG_FATAL("Cannot deregister IPv4 IGP table with the RIB: %s", xrl_error.str().c_str()); break; case NO_FINDER: case RESOLVE_FAILED: case SEND_FAILED: // // A communication error that should have been caught elsewhere // (e.g., by tracking the status of the Finder and the other targets). // Probably we caught it here because of event reordering. // In some cases we print an error. In other cases our job is done. // _is_rib_igp_table4_registered = false; StaticRoutesNode::decr_shutdown_requests_n(); break; case BAD_ARGS: case NO_SUCH_METHOD: case INTERNAL_ERROR: // // An error that should happen only if there is something unusual: // e.g., there is XRL mismatch, no enough internal resources, etc. // We don't try to recover from such errors, hence this is fatal. // XLOG_FATAL("Fatal XRL error: %s", xrl_error.str().c_str()); break; case REPLY_TIMED_OUT: case SEND_FAILED_TRANSIENT: // // If a transient error, then start a timer to try again // (unless the timer is already running). // if (! _rib_register_shutdown_timer.scheduled()) { XLOG_ERROR("Failed to deregister IPv4 IGP table with the RIB: %s. " "Will try again.", xrl_error.str().c_str()); _rib_register_shutdown_timer = _eventloop.new_oneoff_after( RETRY_TIMEVAL, callback(this, &XrlStaticRoutesNode::rib_register_shutdown)); } break; } } #ifdef HAVE_IPV6 void XrlStaticRoutesNode::rib_client_send_delete_igp_table6_cb( const XrlError& xrl_error) { switch (xrl_error.error_code()) { case OKAY: // // If success, then we are done // _is_rib_igp_table6_registered = false; StaticRoutesNode::decr_shutdown_requests_n(); break; case COMMAND_FAILED: // // If a command failed because the other side rejected it, this is // fatal. // XLOG_FATAL("Cannot deregister IPv6 IGP table with the RIB: %s", xrl_error.str().c_str()); break; case NO_FINDER: case RESOLVE_FAILED: case SEND_FAILED: // // A communication error that should have been caught elsewhere // (e.g., by tracking the status of the Finder and the other targets). // Probably we caught it here because of event reordering. // In some cases we print an error. In other cases our job is done. // _is_rib_igp_table6_registered = false; StaticRoutesNode::decr_shutdown_requests_n(); break; case BAD_ARGS: case NO_SUCH_METHOD: case INTERNAL_ERROR: // // An error that should happen only if there is something unusual: // e.g., there is XRL mismatch, no enough internal resources, etc. // We don't try to recover from such errors, hence this is fatal. // XLOG_FATAL("Fatal XRL error: %s", xrl_error.str().c_str()); break; case REPLY_TIMED_OUT: case SEND_FAILED_TRANSIENT: // // If a transient error, then start a timer to try again // (unless the timer is already running). // if (! _rib_register_shutdown_timer.scheduled()) { XLOG_ERROR("Failed to deregister IPv6 IGP table with the RIB: %s. " "Will try again.", xrl_error.str().c_str()); _rib_register_shutdown_timer = _eventloop.new_oneoff_after( RETRY_TIMEVAL, callback(this, &XrlStaticRoutesNode::rib_register_shutdown)); } break; } } #endif // // XRL target methods // /** * Get name of Xrl Target */ XrlCmdError XrlStaticRoutesNode::common_0_1_get_target_name( // Output values, string& name) { name = xrl_router().class_name(); return XrlCmdError::OKAY(); } /** * Get version string from Xrl Target */ XrlCmdError XrlStaticRoutesNode::common_0_1_get_version( // Output values, string& version) { version = XORP_MODULE_VERSION; return XrlCmdError::OKAY(); } /** * Get status of Xrl Target */ XrlCmdError XrlStaticRoutesNode::common_0_1_get_status( // Output values, uint32_t& status, string& reason) { status = StaticRoutesNode::node_status(reason); return XrlCmdError::OKAY(); } /** * Request clean shutdown of Xrl Target */ XrlCmdError XrlStaticRoutesNode::common_0_1_shutdown() { string error_msg; if (shutdown() != XORP_OK) { error_msg = c_format("Failed to shutdown StaticRoutes"); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } /** * Request clean shutdown of Xrl Target */ XrlCmdError XrlStaticRoutesNode::common_0_1_startup() { if (startup() != XORP_OK) { string error_msg = c_format("Failed to startup StaticRoutes"); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } /** * Announce target birth to observer. * * @param target_class the target class name. * * @param target_instance the target instance name. */ XrlCmdError XrlStaticRoutesNode::finder_event_observer_0_1_xrl_target_birth( // Input values, const string& target_class, const string& target_instance) { if (target_class == _fea_target) { // // XXX: when the startup is completed, // IfMgrHintObserver::tree_complete() will be called. // _is_fea_alive = true; if (_ifmgr.startup() != XORP_OK) { StaticRoutesNode::ServiceBase::set_status(SERVICE_FAILED); StaticRoutesNode::update_status(); } } if (target_class == _rib_target) { _is_rib_alive = true; send_rib_add_tables(); } return XrlCmdError::OKAY(); UNUSED(target_instance); } /** * Announce target death to observer. * * @param target_class the target class name. * * @param target_instance the target instance name. */ XrlCmdError XrlStaticRoutesNode::finder_event_observer_0_1_xrl_target_death( // Input values, const string& target_class, const string& target_instance) { UNUSED(target_instance); bool do_shutdown = false; if (target_class == _fea_target) { XLOG_ERROR("FEA (instance %s) has died, shutting down.", target_instance.c_str()); _is_fea_alive = false; do_shutdown = true; } if (target_class == _rib_target) { XLOG_ERROR("RIB (instance %s) has died, shutting down.", target_instance.c_str()); _is_rib_alive = false; do_shutdown = true; } if (do_shutdown) StaticRoutesNode::shutdown(); return XrlCmdError::OKAY(); } /** * Enable/disable/start/stop StaticRoutes. * * @param enable if true, then enable StaticRoutes, otherwise disable it. */ XrlCmdError XrlStaticRoutesNode::static_routes_0_1_enable_static_routes( // Input values, const bool& enable) { StaticRoutesNode::set_enabled(enable); return XrlCmdError::OKAY(); } XrlCmdError XrlStaticRoutesNode::static_routes_0_1_start_static_routes() { // XLOG_UNFINISHED(); return XrlCmdError::OKAY(); } XrlCmdError XrlStaticRoutesNode::static_routes_0_1_stop_static_routes() { XLOG_UNFINISHED(); return XrlCmdError::OKAY(); } /** * Add/replace/delete a static route. * * @param unicast if true, then the route would be used for unicast * routing. * * @param multicast if true, then the route would be used in the MRIB * (Multicast Routing Information Base) for multicast purpose (e.g., * computing the Reverse-Path Forwarding information). * * @param network the network address prefix this route applies to. * * @param nexthop the address of the next-hop router for this route. * * @param metric the metric distance for this route. */ XrlCmdError XrlStaticRoutesNode::static_routes_0_1_add_route4( // Input values, const bool& unicast, const bool& multicast, const IPv4Net& network, const IPv4& nexthop, const uint32_t& metric) { bool is_backup_route = false; string error_msg; if (StaticRoutesNode::add_route4(unicast, multicast, network, nexthop, "", "", metric, is_backup_route, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlStaticRoutesNode::static_routes_0_1_add_route6( // Input values, const bool& unicast, const bool& multicast, const IPv6Net& network, const IPv6& nexthop, const uint32_t& metric) { bool is_backup_route = false; string error_msg; if (StaticRoutesNode::add_route6(unicast, multicast, network, nexthop, "", "", metric, is_backup_route, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlStaticRoutesNode::static_routes_0_1_replace_route4( // Input values, const bool& unicast, const bool& multicast, const IPv4Net& network, const IPv4& nexthop, const uint32_t& metric) { bool is_backup_route = false; string error_msg; if (StaticRoutesNode::replace_route4(unicast, multicast, network, nexthop, "", "", metric, is_backup_route, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlStaticRoutesNode::static_routes_0_1_replace_route6( // Input values, const bool& unicast, const bool& multicast, const IPv6Net& network, const IPv6& nexthop, const uint32_t& metric) { bool is_backup_route = false; string error_msg; if (StaticRoutesNode::replace_route6(unicast, multicast, network, nexthop, "", "", metric, is_backup_route, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlStaticRoutesNode::static_routes_0_1_delete_route4( // Input values, const bool& unicast, const bool& multicast, const IPv4Net& network, const IPv4& nexthop) { bool is_backup_route = false; string error_msg; if (StaticRoutesNode::delete_route4(unicast, multicast, network, nexthop, "", "", is_backup_route, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlStaticRoutesNode::static_routes_0_1_delete_route6( // Input values, const bool& unicast, const bool& multicast, const IPv6Net& network, const IPv6& nexthop) { bool is_backup_route = false; string error_msg; if (StaticRoutesNode::delete_route6(unicast, multicast, network, nexthop, "", "", is_backup_route, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } /** * Add/replace/delete a backup static route. * * @param unicast if true, then the route would be used for unicast * routing. * * @param multicast if true, then the route would be used in the MRIB * (Multicast Routing Information Base) for multicast purpose (e.g., * computing the Reverse-Path Forwarding information). * * @param network the network address prefix this route applies to. * * @param nexthop the address of the next-hop router for this route. * * @param metric the metric distance for this route. */ XrlCmdError XrlStaticRoutesNode::static_routes_0_1_add_backup_route4( // Input values, const bool& unicast, const bool& multicast, const IPv4Net& network, const IPv4& nexthop, const uint32_t& metric) { bool is_backup_route = true; string error_msg; if (StaticRoutesNode::add_route4(unicast, multicast, network, nexthop, "", "", metric, is_backup_route, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlStaticRoutesNode::static_routes_0_1_add_backup_route6( // Input values, const bool& unicast, const bool& multicast, const IPv6Net& network, const IPv6& nexthop, const uint32_t& metric) { bool is_backup_route = true; string error_msg; if (StaticRoutesNode::add_route6(unicast, multicast, network, nexthop, "", "", metric, is_backup_route, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlStaticRoutesNode::static_routes_0_1_replace_backup_route4( // Input values, const bool& unicast, const bool& multicast, const IPv4Net& network, const IPv4& nexthop, const uint32_t& metric) { bool is_backup_route = true; string error_msg; if (StaticRoutesNode::replace_route4(unicast, multicast, network, nexthop, "", "", metric, is_backup_route, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlStaticRoutesNode::static_routes_0_1_replace_backup_route6( // Input values, const bool& unicast, const bool& multicast, const IPv6Net& network, const IPv6& nexthop, const uint32_t& metric) { bool is_backup_route = true; string error_msg; if (StaticRoutesNode::replace_route6(unicast, multicast, network, nexthop, "", "", metric, is_backup_route, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlStaticRoutesNode::static_routes_0_1_delete_backup_route4( // Input values, const bool& unicast, const bool& multicast, const IPv4Net& network, const IPv4& nexthop) { bool is_backup_route = true; string error_msg; if (StaticRoutesNode::delete_route4(unicast, multicast, network, nexthop, "", "", is_backup_route, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlStaticRoutesNode::static_routes_0_1_delete_backup_route6( // Input values, const bool& unicast, const bool& multicast, const IPv6Net& network, const IPv6& nexthop) { bool is_backup_route = true; string error_msg; if (StaticRoutesNode::delete_route6(unicast, multicast, network, nexthop, "", "", is_backup_route, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } /** * Add/replace a static route by explicitly specifying the network * interface toward the destination. * * @param unicast if true, then the route would be used for unicast * routing. * * @param multicast if true, then the route would be used in the MRIB * (Multicast Routing Information Base) for multicast purpose (e.g., * computing the Reverse-Path Forwarding information). * * @param network the network address prefix this route applies to. * * @param nexthop the address of the next-hop router for this route. * * @param ifname of the name of the physical interface toward the * destination. * * @param vifname of the name of the virtual interface toward the * destination. * * @param metric the metric distance for this route. */ XrlCmdError XrlStaticRoutesNode::static_routes_0_1_add_interface_route4( // Input values, const bool& unicast, const bool& multicast, const IPv4Net& network, const IPv4& nexthop, const string& ifname, const string& vifname, const uint32_t& metric) { bool is_backup_route = false; string error_msg; if (StaticRoutesNode::add_route4(unicast, multicast, network, nexthop, ifname, vifname, metric, is_backup_route, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlStaticRoutesNode::static_routes_0_1_add_interface_route6( // Input values, const bool& unicast, const bool& multicast, const IPv6Net& network, const IPv6& nexthop, const string& ifname, const string& vifname, const uint32_t& metric) { bool is_backup_route = false; string error_msg; if (StaticRoutesNode::add_route6(unicast, multicast, network, nexthop, ifname, vifname, metric, is_backup_route, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlStaticRoutesNode::static_routes_0_1_replace_interface_route4( // Input values, const bool& unicast, const bool& multicast, const IPv4Net& network, const IPv4& nexthop, const string& ifname, const string& vifname, const uint32_t& metric) { bool is_backup_route = false; string error_msg; if (StaticRoutesNode::replace_route4(unicast, multicast, network, nexthop, ifname, vifname, metric, is_backup_route, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlStaticRoutesNode::static_routes_0_1_replace_interface_route6( // Input values, const bool& unicast, const bool& multicast, const IPv6Net& network, const IPv6& nexthop, const string& ifname, const string& vifname, const uint32_t& metric) { bool is_backup_route = false; string error_msg; if (StaticRoutesNode::replace_route6(unicast, multicast, network, nexthop, ifname, vifname, metric, is_backup_route, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlStaticRoutesNode::static_routes_0_1_delete_interface_route4( // Input values, const bool& unicast, const bool& multicast, const IPv4Net& network, const IPv4& nexthop, const string& ifname, const string& vifname) { bool is_backup_route = false; string error_msg; if (StaticRoutesNode::delete_route4(unicast, multicast, network, nexthop, ifname, vifname, is_backup_route, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlStaticRoutesNode::static_routes_0_1_delete_interface_route6( // Input values, const bool& unicast, const bool& multicast, const IPv6Net& network, const IPv6& nexthop, const string& ifname, const string& vifname) { bool is_backup_route = false; string error_msg; if (StaticRoutesNode::delete_route6(unicast, multicast, network, nexthop, ifname, vifname, is_backup_route, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } /** * Add/replace/delete a backup static route by explicitly specifying the * network interface toward the destination. * * @param unicast if true, then the route would be used for unicast * routing. * * @param multicast if true, then the route would be used in the MRIB * (Multicast Routing Information Base) for multicast purpose (e.g., * computing the Reverse-Path Forwarding information). * * @param network the network address prefix this route applies to. * * @param nexthop the address of the next-hop router for this route. * * @param ifname of the name of the physical interface toward the * destination. * * @param vifname of the name of the virtual interface toward the * destination. * * @param metric the metric distance for this route. */ XrlCmdError XrlStaticRoutesNode::static_routes_0_1_add_backup_interface_route4( // Input values, const bool& unicast, const bool& multicast, const IPv4Net& network, const IPv4& nexthop, const string& ifname, const string& vifname, const uint32_t& metric) { bool is_backup_route = true; string error_msg; if (StaticRoutesNode::add_route4(unicast, multicast, network, nexthop, ifname, vifname, metric, is_backup_route, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlStaticRoutesNode::static_routes_0_1_add_backup_interface_route6( // Input values, const bool& unicast, const bool& multicast, const IPv6Net& network, const IPv6& nexthop, const string& ifname, const string& vifname, const uint32_t& metric) { bool is_backup_route = true; string error_msg; if (StaticRoutesNode::add_route6(unicast, multicast, network, nexthop, ifname, vifname, metric, is_backup_route, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlStaticRoutesNode::static_routes_0_1_replace_backup_interface_route4( // Input values, const bool& unicast, const bool& multicast, const IPv4Net& network, const IPv4& nexthop, const string& ifname, const string& vifname, const uint32_t& metric) { bool is_backup_route = true; string error_msg; if (StaticRoutesNode::replace_route4(unicast, multicast, network, nexthop, ifname, vifname, metric, is_backup_route, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlStaticRoutesNode::static_routes_0_1_replace_backup_interface_route6( // Input values, const bool& unicast, const bool& multicast, const IPv6Net& network, const IPv6& nexthop, const string& ifname, const string& vifname, const uint32_t& metric) { bool is_backup_route = true; string error_msg; if (StaticRoutesNode::replace_route6(unicast, multicast, network, nexthop, ifname, vifname, metric, is_backup_route, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlStaticRoutesNode::static_routes_0_1_delete_backup_interface_route4( // Input values, const bool& unicast, const bool& multicast, const IPv4Net& network, const IPv4& nexthop, const string& ifname, const string& vifname) { bool is_backup_route = true; string error_msg; if (StaticRoutesNode::delete_route4(unicast, multicast, network, nexthop, ifname, vifname, is_backup_route, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlStaticRoutesNode::static_routes_0_1_delete_backup_interface_route6( // Input values, const bool& unicast, const bool& multicast, const IPv6Net& network, const IPv6& nexthop, const string& ifname, const string& vifname) { bool is_backup_route = true; string error_msg; if (StaticRoutesNode::delete_route6(unicast, multicast, network, nexthop, ifname, vifname, is_backup_route, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } /** * Enable/disable the StaticRoutes trace log for all operations. * * @param enable if true, then enable the trace log, otherwise disable it. */ XrlCmdError XrlStaticRoutesNode::static_routes_0_1_enable_log_trace_all( // Input values, const bool& enable) { StaticRoutesNode::set_log_trace(enable); return XrlCmdError::OKAY(); } /** * Inform the RIB about a route change. * * @param static_route the route with the information about the change. */ void XrlStaticRoutesNode::inform_rib_route_change(const StaticRoute& static_route) { // Add the request to the queue _inform_rib_queue.push_back(static_route); // If the queue was empty before, start sending the routes if (_inform_rib_queue.size() == 1) { send_rib_route_change(); } } /** * Cancel a pending request to inform the RIB about a route change. * * @param static_route the route with the request that would be canceled. */ void XrlStaticRoutesNode::cancel_rib_route_change(const StaticRoute& static_route) { list::iterator iter; for (iter = _inform_rib_queue.begin(); iter != _inform_rib_queue.end(); ++iter) { StaticRoute& tmp_static_route = *iter; if (tmp_static_route == static_route) tmp_static_route.set_ignored(true); } } void XrlStaticRoutesNode::send_rib_route_change() { bool success = true; if (! _is_finder_alive) return; // The Finder is dead do { // Pop-up all routes that are to be ignored if (_inform_rib_queue.empty()) return; // No more route changes to send StaticRoute& tmp_static_route = _inform_rib_queue.front(); if (tmp_static_route.is_ignored()) { _inform_rib_queue.pop_front(); continue; } break; } while (true); StaticRoute& static_route = _inform_rib_queue.front(); // // Check whether we have already registered with the RIB // if (static_route.is_ipv4() && (! _is_rib_igp_table4_registered)) { success = false; goto start_timer_label; } #ifdef HAVE_IPV6 if (static_route.is_ipv6() && (! _is_rib_igp_table6_registered)) { success = false; goto start_timer_label; } #endif // // Send the appropriate XRL // if (static_route.is_add_route()) { if (static_route.is_ipv4()) { if (static_route.is_interface_route()) { success = _xrl_rib_client.send_add_interface_route4( _rib_target.c_str(), StaticRoutesNode::protocol_name(), static_route.unicast(), static_route.multicast(), static_route.network().get_ipv4net(), static_route.nexthop().get_ipv4(), static_route.ifname(), static_route.vifname(), static_route.metric(), static_route.policytags().xrl_atomlist(), callback(this, &XrlStaticRoutesNode::send_rib_route_change_cb)); if (success) return; } else { success = _xrl_rib_client.send_add_route4( _rib_target.c_str(), StaticRoutesNode::protocol_name(), static_route.unicast(), static_route.multicast(), static_route.network().get_ipv4net(), static_route.nexthop().get_ipv4(), static_route.metric(), static_route.policytags().xrl_atomlist(), callback(this, &XrlStaticRoutesNode::send_rib_route_change_cb)); if (success) return; } } #ifdef HAVE_IPV6 if (static_route.is_ipv6()) { if (static_route.is_interface_route()) { success = _xrl_rib_client.send_add_interface_route6( _rib_target.c_str(), StaticRoutesNode::protocol_name(), static_route.unicast(), static_route.multicast(), static_route.network().get_ipv6net(), static_route.nexthop().get_ipv6(), static_route.ifname(), static_route.vifname(), static_route.metric(), static_route.policytags().xrl_atomlist(), callback(this, &XrlStaticRoutesNode::send_rib_route_change_cb)); if (success) return; } else { success = _xrl_rib_client.send_add_route6( _rib_target.c_str(), StaticRoutesNode::protocol_name(), static_route.unicast(), static_route.multicast(), static_route.network().get_ipv6net(), static_route.nexthop().get_ipv6(), static_route.metric(), static_route.policytags().xrl_atomlist(), callback(this, &XrlStaticRoutesNode::send_rib_route_change_cb)); if (success) return; } } #endif } if (static_route.is_replace_route()) { if (static_route.is_ipv4()) { if (static_route.is_interface_route()) { success = _xrl_rib_client.send_replace_interface_route4( _rib_target.c_str(), StaticRoutesNode::protocol_name(), static_route.unicast(), static_route.multicast(), static_route.network().get_ipv4net(), static_route.nexthop().get_ipv4(), static_route.ifname(), static_route.vifname(), static_route.metric(), static_route.policytags().xrl_atomlist(), callback(this, &XrlStaticRoutesNode::send_rib_route_change_cb)); if (success) return; } else { success = _xrl_rib_client.send_replace_route4( _rib_target.c_str(), StaticRoutesNode::protocol_name(), static_route.unicast(), static_route.multicast(), static_route.network().get_ipv4net(), static_route.nexthop().get_ipv4(), static_route.metric(), static_route.policytags().xrl_atomlist(), callback(this, &XrlStaticRoutesNode::send_rib_route_change_cb)); if (success) return; } } #ifdef HAVE_IPV6 if (static_route.is_ipv6()) { if (static_route.is_interface_route()) { success = _xrl_rib_client.send_replace_interface_route6( _rib_target.c_str(), StaticRoutesNode::protocol_name(), static_route.unicast(), static_route.multicast(), static_route.network().get_ipv6net(), static_route.nexthop().get_ipv6(), static_route.ifname(), static_route.vifname(), static_route.metric(), static_route.policytags().xrl_atomlist(), callback(this, &XrlStaticRoutesNode::send_rib_route_change_cb)); if (success) return; } else { success = _xrl_rib_client.send_replace_route6( _rib_target.c_str(), StaticRoutesNode::protocol_name(), static_route.unicast(), static_route.multicast(), static_route.network().get_ipv6net(), static_route.nexthop().get_ipv6(), static_route.metric(), static_route.policytags().xrl_atomlist(), callback(this, &XrlStaticRoutesNode::send_rib_route_change_cb)); if (success) return; } } #endif } if (static_route.is_delete_route()) { if (static_route.is_ipv4()) { success = _xrl_rib_client.send_delete_route4( _rib_target.c_str(), StaticRoutesNode::protocol_name(), static_route.unicast(), static_route.multicast(), static_route.network().get_ipv4net(), callback(this, &XrlStaticRoutesNode::send_rib_route_change_cb)); if (success) return; } #ifdef HAVE_IPV6 if (static_route.is_ipv6()) { success = _xrl_rib_client.send_delete_route6( _rib_target.c_str(), StaticRoutesNode::protocol_name(), static_route.unicast(), static_route.multicast(), static_route.network().get_ipv6net(), callback(this, &XrlStaticRoutesNode::send_rib_route_change_cb)); if (success) return; } #endif } if (! success) { // // If an error, then start a timer to try again. // XLOG_ERROR("Failed to %s route for %s with the RIB. " "Will try again.", (static_route.is_add_route())? "add" : (static_route.is_replace_route())? "replace" : "delete", static_route.network().str().c_str()); start_timer_label: _inform_rib_queue_timer = _eventloop.new_oneoff_after( RETRY_TIMEVAL, callback(this, &XrlStaticRoutesNode::send_rib_route_change)); } } void XrlStaticRoutesNode::send_rib_route_change_cb(const XrlError& xrl_error) { switch (xrl_error.error_code()) { case OKAY: // // If success, then send the next route change // _inform_rib_queue.pop_front(); send_rib_route_change(); break; case COMMAND_FAILED: // // If a command failed because the other side rejected it, // then print an error and send the next one. // XLOG_ERROR("Cannot %s a routing entry with the RIB: %s", (_inform_rib_queue.front().is_add_route())? "add" : (_inform_rib_queue.front().is_replace_route())? "replace" : "delete", xrl_error.str().c_str()); _inform_rib_queue.pop_front(); send_rib_route_change(); break; case NO_FINDER: case RESOLVE_FAILED: case SEND_FAILED: // // A communication error that should have been caught elsewhere // (e.g., by tracking the status of the Finder and the other targets). // Probably we caught it here because of event reordering. // In some cases we print an error. In other cases our job is done. // XLOG_ERROR("Cannot %s a routing entry with the RIB: %s", (_inform_rib_queue.front().is_add_route())? "add" : (_inform_rib_queue.front().is_replace_route())? "replace" : "delete", xrl_error.str().c_str()); _inform_rib_queue.pop_front(); send_rib_route_change(); break; case BAD_ARGS: case NO_SUCH_METHOD: case INTERNAL_ERROR: // // An error that should happen only if there is something unusual: // e.g., there is XRL mismatch, no enough internal resources, etc. // We don't try to recover from such errors, hence this is fatal. // XLOG_FATAL("Fatal XRL error: %s", xrl_error.str().c_str()); break; case REPLY_TIMED_OUT: case SEND_FAILED_TRANSIENT: // // If a transient error, then start a timer to try again // (unless the timer is already running). // if (! _inform_rib_queue_timer.scheduled()) { XLOG_ERROR("Failed to %s a routing entry with the RIB: %s. " "Will try again.", (_inform_rib_queue.front().is_add_route())? "add" : (_inform_rib_queue.front().is_replace_route())? "replace" : "delete", xrl_error.str().c_str()); _inform_rib_queue_timer = _eventloop.new_oneoff_after( RETRY_TIMEVAL, callback(this, &XrlStaticRoutesNode::send_rib_route_change)); } break; } } XrlCmdError XrlStaticRoutesNode::policy_backend_0_1_configure(const uint32_t& filter, const string& conf) { try { StaticRoutesNode::configure_filter(filter, conf); } catch(const PolicyException& e) { return XrlCmdError::COMMAND_FAILED("Filter configure failed: " + e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlStaticRoutesNode::policy_backend_0_1_reset(const uint32_t& filter) { try { StaticRoutesNode::reset_filter(filter); } catch(const PolicyException& e) { // Will never happen... but for the future... return XrlCmdError::COMMAND_FAILED("Filter reset failed: " + e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlStaticRoutesNode::policy_backend_0_1_push_routes() { StaticRoutesNode::push_routes(); return XrlCmdError::OKAY(); } xorp/static_routes/static_routes_varrw.cc0000664000076400007640000000416611421137511021173 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "static_routes_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "static_routes_varrw.hh" StaticRoutesVarRW::StaticRoutesVarRW(StaticRoute& route) : _route(route), _is_ipv4(route.is_ipv4()), _is_ipv6(route.is_ipv6()) { } void StaticRoutesVarRW::start_read() { initialize(_route.policytags()); if (_is_ipv4) { initialize(VAR_NETWORK4, _ef.create(ElemIPv4Net::id, _route.network().str().c_str())); initialize(VAR_NEXTHOP4, _ef.create(ElemIPv4NextHop::id, _route.nexthop().str().c_str())); initialize(VAR_NETWORK6, NULL); initialize(VAR_NEXTHOP6, NULL); } if (_is_ipv6) { initialize(VAR_NETWORK6, _ef.create(ElemIPv6Net::id, _route.network().str().c_str())); initialize(VAR_NEXTHOP6, _ef.create(ElemIPv6NextHop::id, _route.nexthop().str().c_str())); initialize(VAR_NETWORK4, NULL); initialize(VAR_NEXTHOP4, NULL); } ostringstream oss; oss << _route.metric(); initialize(VAR_METRIC, _ef.create(ElemU32::id, oss.str().c_str())); } void StaticRoutesVarRW::single_write(const Id& /* id */, const Element& /* e */) { } Element* StaticRoutesVarRW::single_read(const Id& /* id */) { XLOG_UNREACHABLE(); } xorp/static_routes/static_routes_varrw.hh0000664000076400007640000000350111421137511021175 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/static_routes/static_routes_varrw.hh,v 1.11 2008/10/02 21:58:29 bms Exp $ #ifndef __STATIC_ROUTES_STATIC_ROUTES_VARRW_HH__ #define __STATIC_ROUTES_STATIC_ROUTES_VARRW_HH__ #include "policy/backend/single_varrw.hh" #include "policy/common/element_factory.hh" #include "static_routes_node.hh" /** * @short Allows variables to be written and read from static routes. */ class StaticRoutesVarRW : public SingleVarRW { public: enum { VAR_NETWORK4 = VAR_PROTOCOL, VAR_NEXTHOP4, VAR_NETWORK6, VAR_NEXTHOP6, VAR_METRIC }; /** * @param route route to read/write values from. */ StaticRoutesVarRW(StaticRoute& route); // SingleVarRW inteface: void start_read(); Element* single_read(const Id& id); void single_write(const Id& id, const Element& e); private: StaticRoute& _route; ElementFactory _ef; bool _is_ipv4; bool _is_ipv6; }; #endif // __STATIC_ROUTES_STATIC_ROUTES_VARRW_HH__ xorp/static_routes/xorp_static_routes.cc0000664000076400007640000001132211540224236021015 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // XORP StaticRoutes module implementation. // #include "static_routes_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/callback.hh" #include "libxorp/eventloop.hh" #include "libxorp/exceptions.hh" #include "xrl_static_routes_node.hh" #ifdef HAVE_GETOPT_H #include #endif // // Local functions prototypes // static void usage(const char *argv0, int exit_value); /** * usage: * @argv0: Argument 0 when the program was called (the program name itself). * @exit_value: The exit value of the program. * * Print the program usage. * If @exit_value is 0, the usage will be printed to the standart output, * otherwise to the standart error. **/ static void usage(const char *argv0, int exit_value) { FILE *output; const char *progname = strrchr(argv0, '/'); if (progname != NULL) progname++; // Skip the last '/' if (progname == NULL) progname = argv0; // // If the usage is printed because of error, output to stderr, otherwise // output to stdout. // if (exit_value == 0) output = stdout; else output = stderr; fprintf(output, "Usage: %s [-F [:]]\n", progname); fprintf(output, " -F [:] : finder hostname and port\n"); fprintf(output, " -h : usage (this message)\n"); fprintf(output, "\n"); fprintf(output, "Program name: %s\n", progname); fprintf(output, "Module name: %s\n", XORP_MODULE_NAME); fprintf(output, "Module version: %s\n", XORP_MODULE_VERSION); exit (exit_value); // NOTREACHED } static void static_routes_main(const string& finder_hostname, uint16_t finder_port) { setup_dflt_sighandlers(); // // Init stuff // EventLoop eventloop; // // StaticRoutes node // XrlStaticRoutesNode xrl_static_routes_node( eventloop, "static_routes", finder_hostname, finder_port, "finder", "fea", "rib"); wait_until_xrl_router_is_ready(eventloop, xrl_static_routes_node.xrl_router()); // Startup xrl_static_routes_node.startup(); // // Main loop // while (xorp_do_run && !xrl_static_routes_node.is_done()) { eventloop.run(); } } int main(int argc, char *argv[]) { int ch; string::size_type idx; const char *argv0 = argv[0]; string finder_hostname = FinderConstants::FINDER_DEFAULT_HOST().str(); uint16_t finder_port = FinderConstants::FINDER_DEFAULT_PORT(); // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); // // Get the program options // while ((ch = getopt(argc, argv, "F:h")) != -1) { switch (ch) { case 'F': // Finder hostname and port finder_hostname = optarg; idx = finder_hostname.find(':'); if (idx != string::npos) { if (idx + 1 >= finder_hostname.length()) { // No port number usage(argv0, 1); // NOTREACHED } char* p = &finder_hostname[idx + 1]; finder_port = static_cast(atoi(p)); finder_hostname = finder_hostname.substr(0, idx); } break; case 'h': case '?': // Help usage(argv0, 0); // NOTREACHED break; default: usage(argv0, 1); // NOTREACHED break; } } argc -= optind; argv += optind; if (argc != 0) { usage(argv0, 1); // NOTREACHED } // // Run everything // try { static_routes_main(finder_hostname, finder_port); } catch(...) { xorp_catch_standard_exceptions(); } // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); exit (0); } xorp/static_routes/static_routes_node.cc0000664000076400007640000010773111540224236020764 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // StaticRoutes node implementation. // #include "static_routes_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvx.hh" #include "static_routes_node.hh" #include "static_routes_varrw.hh" StaticRoutesNode::StaticRoutesNode(EventLoop& eventloop) : ServiceBase("StaticRoutes"), _eventloop(eventloop), _protocol_name("static"), // TODO: must be known by RIB _is_enabled(true), // XXX: enabled by default _startup_requests_n(0), _shutdown_requests_n(0), _is_log_trace(true) // XXX: default to print trace logs { set_node_status(PROC_STARTUP); } StaticRoutesNode::~StaticRoutesNode() { shutdown(); } int StaticRoutesNode::startup() { // // Test the service status // if ((ServiceBase::status() == SERVICE_STARTING) || (ServiceBase::status() == SERVICE_RUNNING)) { return (XORP_OK); } if (ServiceBase::status() != SERVICE_READY) { return (XORP_ERROR); } // // Transition to SERVICE_RUNNING occurs when all transient startup // operations are completed (e.g., after we have the interface/vif/address // state available, after we have registered with the RIB, etc.) // ServiceBase::set_status(SERVICE_STARTING); // // Set the node status // set_node_status(PROC_STARTUP); // // Register with the FEA // fea_register_startup(); // // Register with the RIB // rib_register_startup(); return (XORP_OK); } int StaticRoutesNode::shutdown() { // // 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); } // // Transition to SERVICE_SHUTDOWN occurs when all transient shutdown // operations are completed (e.g., after we have deregistered with the FEA // and the RIB, etc.) // ServiceBase::set_status(SERVICE_SHUTTING_DOWN); // // De-register with the RIB // rib_register_shutdown(); // // De-register with the FEA // fea_register_shutdown(); // // Set the node status // set_node_status(PROC_SHUTDOWN); // // Update the node status // update_status(); return (XORP_OK); } void StaticRoutesNode::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 set_node_status(PROC_READY); return; } if ((old_status == SERVICE_SHUTTING_DOWN) && (new_status == SERVICE_SHUTDOWN)) { // The shutdown process has completed 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)) { decr_shutdown_requests_n(); } } } void StaticRoutesNode::incr_startup_requests_n() { _startup_requests_n++; XLOG_ASSERT(_startup_requests_n > 0); } void StaticRoutesNode::decr_startup_requests_n() { XLOG_ASSERT(_startup_requests_n > 0); _startup_requests_n--; update_status(); } void StaticRoutesNode::incr_shutdown_requests_n() { _shutdown_requests_n++; XLOG_ASSERT(_shutdown_requests_n > 0); } void StaticRoutesNode::decr_shutdown_requests_n() { XLOG_ASSERT(_shutdown_requests_n > 0); _shutdown_requests_n--; update_status(); } void StaticRoutesNode::update_status() { // // Test if the startup process has completed // if (ServiceBase::status() == SERVICE_STARTING) { if (_startup_requests_n > 0) return; // The startup process has completed ServiceBase::set_status(SERVICE_RUNNING); set_node_status(PROC_READY); return; } // // Test if the shutdown process has completed // if (ServiceBase::status() == SERVICE_SHUTTING_DOWN) { if (_shutdown_requests_n > 0) return; // The shutdown process has completed ServiceBase::set_status(SERVICE_SHUTDOWN); set_node_status(PROC_DONE); return; } // // Test if we have failed // if (ServiceBase::status() == SERVICE_FAILED) { set_node_status(PROC_DONE); return; } } ProcessStatus StaticRoutesNode::node_status(string& reason_msg) { ProcessStatus status = _node_status; // Set the return message with the reason reason_msg = ""; switch (status) { case PROC_NULL: // Can't be running and in this state XLOG_UNREACHABLE(); break; case PROC_STARTUP: // Get the message about the startup progress reason_msg = c_format("Waiting for %u startup events", XORP_UINT_CAST(_startup_requests_n)); break; case PROC_NOT_READY: // XXX: this state is unused XLOG_UNREACHABLE(); break; case PROC_READY: reason_msg = c_format("Node is READY"); break; case PROC_SHUTDOWN: // Get the message about the shutdown progress reason_msg = c_format("Waiting for %u shutdown events", XORP_UINT_CAST(_shutdown_requests_n)); break; case PROC_FAILED: // XXX: this state is unused XLOG_UNREACHABLE(); break; case PROC_DONE: // Process has completed operation break; default: // Unknown status XLOG_UNREACHABLE(); break; } return (status); } void StaticRoutesNode::tree_complete() { decr_startup_requests_n(); // // XXX: we use same actions when the tree is completed or updates are made // updates_made(); } void StaticRoutesNode::updates_made() { StaticRoutesNode::Table::iterator route_iter; list add_routes, replace_routes, delete_routes; list::iterator pending_iter; for (route_iter = _static_routes.begin(); route_iter != _static_routes.end(); ++route_iter) { StaticRoute& static_route = route_iter->second; bool is_old_up = false; bool is_new_up = false; string old_ifname, old_vifname, new_ifname, new_vifname; if (static_route.is_interface_route()) { // // Calculate whether the interface was UP before and now. // const IfMgrIfAtom* if_atom; const IfMgrVifAtom* vif_atom; if_atom = _iftree.find_interface(static_route.ifname()); vif_atom = _iftree.find_vif(static_route.ifname(), static_route.vifname()); if ((if_atom != NULL) && (if_atom->enabled()) && (! if_atom->no_carrier()) && (vif_atom != NULL) && (vif_atom->enabled())) { is_old_up = true; } if_atom = ifmgr_iftree().find_interface(static_route.ifname()); vif_atom = ifmgr_iftree().find_vif(static_route.ifname(), static_route.vifname()); if ((if_atom != NULL) && (if_atom->enabled()) && (! if_atom->no_carrier()) && (vif_atom != NULL) && (vif_atom->enabled())) { is_new_up = true; } } else { // // Calculate whether the next-hop router was directly connected // before and now. // if (_iftree.is_directly_connected(static_route.nexthop(), old_ifname, old_vifname)) { is_old_up = true; } if (ifmgr_iftree().is_directly_connected(static_route.nexthop(), new_ifname, new_vifname)) { is_new_up = true; } } if ((is_old_up == is_new_up) && (old_ifname == new_ifname) && (old_vifname == new_vifname)) { continue; // Nothing changed } if ((! is_old_up) && (! is_new_up)) { // // The interface s still down, so nothing to do // continue; } if ((! is_old_up) && (is_new_up)) { // // The interface is now up, hence add the route // add_routes.push_back(&static_route); continue; } if ((is_old_up) && (! is_new_up)) { // // The interface went down, hence cancel all pending requests, // and withdraw the route. // delete_routes.push_back(&static_route); continue; } if (is_old_up && is_new_up) { // // The interface remains up, hence probably the interface or // the vif name has changed. // Delete the route and then add it again so the information // in the RIB will be updated. // replace_routes.push_back(&static_route); continue; } } // // Update the local copy of the interface tree // _iftree = ifmgr_iftree(); // // Process all pending "add route" requests // for (pending_iter = add_routes.begin(); pending_iter != add_routes.end(); ++pending_iter) { StaticRoute& orig_route = *(*pending_iter); StaticRoute copy_route = orig_route; prepare_route_for_transmission(orig_route, copy_route); copy_route.set_add_route(); inform_rib(copy_route); } // // Process all pending "replace route" requests // for (pending_iter = replace_routes.begin(); pending_iter != replace_routes.end(); ++pending_iter) { StaticRoute& orig_route = *(*pending_iter); StaticRoute copy_route = orig_route; // First delete the route, then add the route prepare_route_for_transmission(orig_route, copy_route); copy_route.set_delete_route(); inform_rib(copy_route); copy_route = orig_route; prepare_route_for_transmission(orig_route, copy_route); copy_route.set_add_route(); inform_rib(copy_route); } // // Process all pending "delete route" requests // for (pending_iter = delete_routes.begin(); pending_iter != delete_routes.end(); ++pending_iter) { StaticRoute& orig_route = *(*pending_iter); cancel_rib_route_change(orig_route); StaticRoute copy_route = orig_route; prepare_route_for_transmission(orig_route, copy_route); copy_route.set_delete_route(); inform_rib(copy_route); } } /** * Enable/disable node operation. * * Note that for the time being it affects only whether the routes * are installed into RIB. In the future it may affect the interaction * with other modules as well. * * @param enable if true then enable node operation, otherwise disable it. */ void StaticRoutesNode::set_enabled(bool enable) { if (enable == is_enabled()) return; // XXX: nothing changed if (enable) { _is_enabled = true; push_pull_rib_routes(true); } else { push_pull_rib_routes(false); _is_enabled = false; } } /** * Add a static IPv4 route. * * @param unicast if true, then the route would be used for unicast * routing. * @param multicast if true, then the route would be used in the MRIB * (Multicast Routing Information Base) for multicast purpose (e.g., * computing the Reverse-Path Forwarding information). * @param network the network address prefix this route applies to. * @param nexthop the address of the next-hop router for this route. * @param ifname of the name of the physical interface toward the * destination. * @param vifname of the name of the virtual interface toward the * destination. * @param metric the metric distance for this route. * @param is_backup_route if true, then this is a backup route operation. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int StaticRoutesNode::add_route4(bool unicast, bool multicast, const IPv4Net& network, const IPv4& nexthop, const string& ifname, const string& vifname, uint32_t metric, bool is_backup_route, string& error_msg) { StaticRoute static_route(unicast, multicast, network, nexthop, ifname, vifname, metric, is_backup_route); static_route.set_add_route(); return (add_route(static_route, error_msg)); } /** * Add a static IPv6 route. * * @param unicast if true, then the route would be used for unicast * routing. * @param multicast if true, then the route would be used in the MRIB * (Multicast Routing Information Base) for multicast purpose (e.g., * computing the Reverse-Path Forwarding information). * @param network the network address prefix this route applies to. * @param nexthop the address of the next-hop router for this route. * @param ifname of the name of the physical interface toward the * destination. * @param vifname of the name of the virtual interface toward the * destination. * @param metric the metric distance for this route. * @param is_backup_route if true, then this is a backup route operation. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int StaticRoutesNode::add_route6(bool unicast, bool multicast, const IPv6Net& network, const IPv6& nexthop, const string& ifname, const string& vifname, uint32_t metric, bool is_backup_route, string& error_msg) { StaticRoute static_route(unicast, multicast, network, nexthop, ifname, vifname, metric, is_backup_route); static_route.set_add_route(); return (add_route(static_route, error_msg)); } /** * Replace a static IPv4 route. * * @param unicast if true, then the route would be used for unicast * routing. * @param multicast if true, then the route would be used in the MRIB * (Multicast Routing Information Base) for multicast purpose (e.g., * computing the Reverse-Path Forwarding information). * @param network the network address prefix this route applies to. * @param nexthop the address of the next-hop router for this route. * @param ifname of the name of the physical interface toward the * destination. * @param vifname of the name of the virtual interface toward the * destination. * @param metric the metric distance for this route. * @param is_backup_route if true, then this is a backup route operation. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int StaticRoutesNode::replace_route4(bool unicast, bool multicast, const IPv4Net& network, const IPv4& nexthop, const string& ifname, const string& vifname, uint32_t metric, bool is_backup_route, string& error_msg) { StaticRoute static_route(unicast, multicast, network, nexthop, ifname, vifname, metric, is_backup_route); static_route.set_replace_route(); return (replace_route(static_route, error_msg)); } /** * Replace a static IPv6 route. * * @param unicast if true, then the route would be used for unicast * routing. * @param multicast if true, then the route would be used in the MRIB * (Multicast Routing Information Base) for multicast purpose (e.g., * computing the Reverse-Path Forwarding information). * @param network the network address prefix this route applies to. * @param nexthop the address of the next-hop router for this route. * @param ifname of the name of the physical interface toward the * destination. * @param vifname of the name of the virtual interface toward the * destination. * @param metric the metric distance for this route. * @param is_backup_route if true, then this is a backup route operation. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int StaticRoutesNode::replace_route6(bool unicast, bool multicast, const IPv6Net& network, const IPv6& nexthop, const string& ifname, const string& vifname, uint32_t metric, bool is_backup_route, string& error_msg) { StaticRoute static_route(unicast, multicast, network, nexthop, ifname, vifname, metric, is_backup_route); static_route.set_replace_route(); return (replace_route(static_route, error_msg)); } /** * Delete a static IPv4 route. * * @param unicast if true, then the route would be used for unicast * routing. * @param multicast if true, then the route would be used in the MRIB * (Multicast Routing Information Base) for multicast purpose (e.g., * computing the Reverse-Path Forwarding information). * @param network the network address prefix this route applies to. * @param nexthop the address of the next-hop router for this route. * @param ifname of the name of the physical interface toward the * destination. * @param vifname of the name of the virtual interface toward the * destination. * @param is_backup_route if true, then this is a backup route operation. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int StaticRoutesNode::delete_route4(bool unicast, bool multicast, const IPv4Net& network, const IPv4& nexthop, const string& ifname, const string& vifname, bool is_backup_route, string& error_msg) { StaticRoute static_route(unicast, multicast, network, nexthop, ifname, vifname, 0, is_backup_route); static_route.set_delete_route(); return (delete_route(static_route, error_msg)); } /** * Delete a static IPv6 route. * * @param unicast if true, then the route would be used for unicast * routing. * @param multicast if true, then the route would be used in the MRIB * (Multicast Routing Information Base) for multicast purpose (e.g., * computing the Reverse-Path Forwarding information). * @param network the network address prefix this route applies to. * @param nexthop the address of the next-hop router for this route. * @param ifname of the name of the physical interface toward the * destination. * @param vifname of the name of the virtual interface toward the * destination. * @param is_backup_route if true, then this is a backup route operation. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int StaticRoutesNode::delete_route6(bool unicast, bool multicast, const IPv6Net& network, const IPv6& nexthop, const string& ifname, const string& vifname, bool is_backup_route, string& error_msg) { StaticRoute static_route(unicast, multicast, network, nexthop, ifname, vifname, 0, is_backup_route); static_route.set_delete_route(); return (delete_route(static_route, error_msg)); } /** * Find a route from the routing table. * * @param table the routing table to seach. * @param key_route the route information to search for. * @return a table iterator to the route. If the route is not found, * the iterator will point to the end of the table. */ StaticRoutesNode::Table::iterator StaticRoutesNode::find_route(StaticRoutesNode::Table& table, const StaticRoute& key_route) { StaticRoutesNode::Table::iterator iter; iter = table.find(key_route.network()); for ( ; iter != table.end(); ++iter) { StaticRoute& orig_route = iter->second; if (orig_route.network() != key_route.network()) break; // Check whether the route attributes match if ((orig_route.unicast() != key_route.unicast()) || (orig_route.multicast() != key_route.multicast())) { continue; } if (orig_route.is_backup_route() != key_route.is_backup_route()) { continue; } if (! key_route.is_backup_route()) { // XXX: There can be a single (primary) route to a destination return (iter); } // A backup route: check the next-hop information if ((orig_route.ifname() == key_route.ifname()) && (orig_route.vifname() == key_route.vifname()) && (orig_route.nexthop() == key_route.nexthop())) { // XXX: Exact route found return (iter); } } return (table.end()); // XXX: Nothing found } /** * Find the best accepted route from the routing table. * * @param table the routing table to seach. * @param key_route the route information to search for. * @return a table iterator to the route. If the route is not found, * the iterator will point to the end of the table. */ StaticRoutesNode::Table::iterator StaticRoutesNode::find_best_accepted_route(StaticRoutesNode::Table& table, const StaticRoute& key_route) { StaticRoutesNode::Table::iterator iter, best_iter = table.end(); iter = table.find(key_route.network()); for ( ; iter != table.end(); ++iter) { StaticRoute& orig_route = iter->second; if (orig_route.network() != key_route.network()) break; // Check whether the route attributes match if ((orig_route.unicast() != key_route.unicast()) || (orig_route.multicast() != key_route.multicast())) { continue; } // Consider only routes that are accepted by the RIB if (! orig_route.is_accepted_by_rib()) continue; if (best_iter == table.end()) { // The first matching route best_iter = iter; continue; } // Select the route with the better metric StaticRoute& best_route = best_iter->second; if (orig_route.metric() < best_route.metric()) { best_iter = iter; continue; } } return (best_iter); } /** * Add a static IPvX route. * * @param static_route the route to add. * @see StaticRoute * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int StaticRoutesNode::add_route(const StaticRoute& static_route, string& error_msg) { StaticRoute updated_route = static_route; // // Update the route // update_route(_iftree, updated_route); // // Check the entry // if (updated_route.is_valid_entry(error_msg) != true) { error_msg = c_format("Cannot add route for %s: %s", updated_route.network().str().c_str(), error_msg.c_str()); return XORP_ERROR; } // // Check if the route was added already // StaticRoutesNode::Table::iterator iter = find_route(_static_routes, updated_route); if (iter != _static_routes.end()) { error_msg = c_format("Cannot add %s route for %s: " "the route already exists", (updated_route.unicast())? "unicast" : "multicast", updated_route.network().str().c_str()); return XORP_ERROR; } // // Add the route // iter = _static_routes.insert(make_pair(updated_route.network(), updated_route)); // // Create a copy of the route and inform the RIB if necessary // StaticRoute& orig_route = iter->second; StaticRoute copy_route = orig_route; prepare_route_for_transmission(orig_route, copy_route); // // Inform the RIB about the change // inform_rib(copy_route); return XORP_OK; } /** * Replace a static IPvX route. * * @param static_route the replacement route. * @see StaticRoute * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int StaticRoutesNode::replace_route(const StaticRoute& static_route, string& error_msg) { StaticRoute updated_route = static_route; // // Update the route // update_route(_iftree, updated_route); // // Check the entry // if (updated_route.is_valid_entry(error_msg) != true) { error_msg = c_format("Cannot replace route for %s: %s", updated_route.network().str().c_str(), error_msg.c_str()); return XORP_ERROR; } // // Find the route and replace it. // StaticRoutesNode::Table::iterator iter = find_route(_static_routes, updated_route); if (iter == _static_routes.end()) { // // Couldn't find the route to replace // error_msg = c_format("Cannot replace route for %s: " "no such route", updated_route.network().str().c_str()); return XORP_ERROR; } // // Route found. Overwrite its value. // do { StaticRoute& orig_route = iter->second; bool was_accepted = orig_route.is_accepted_by_rib(); orig_route = updated_route; // // Create a copy of the route and inform the RIB if necessary // StaticRoute copy_route = orig_route; prepare_route_for_transmission(orig_route, copy_route); // // XXX: If necessary, change the type of the route. // E.g., "replace route" may become "add route" or "delete route". // if (copy_route.is_accepted_by_rib()) { if (was_accepted) { copy_route.set_replace_route(); } else { copy_route.set_add_route(); } } else { if (was_accepted) { copy_route.set_delete_route(); } else { return XORP_OK; } } // // Inform the RIB about the change // inform_rib(copy_route); } while (false); return XORP_OK; } /** * Delete a static IPvX route. * * @param static_route the route to delete. * @see StaticRoute * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int StaticRoutesNode::delete_route(const StaticRoute& static_route, string& error_msg) { StaticRoute updated_route = static_route; // // Update the route // update_route(_iftree, updated_route); // // Check the entry // if (updated_route.is_valid_entry(error_msg) != true) { error_msg = c_format("Cannot delete route for %s: %s", updated_route.network().str().c_str(), error_msg.c_str()); return XORP_ERROR; } // // Find the route and delete it. // XXX: If this is a primary route (i.e., not a backup route), then // delete all routes to the same destination. // StaticRoutesNode::Table::iterator iter = find_route(_static_routes, updated_route); if (iter == _static_routes.end()) { // // Couldn't find the route to delete // error_msg = c_format("Cannot delete %s route for %s: " "no such route", (updated_route.unicast())? "unicast" : "multicast", updated_route.network().str().c_str()); return XORP_ERROR; } // // XXX: We need to remove from the table all routes that are to be // deleted and then propagate the deletes to the RIB. Otherwise, // deleting the best route might add the second best route right // before that route is also deleted. // list delete_routes; iter = _static_routes.find(updated_route.network()); while (iter != _static_routes.end()) { StaticRoutesNode::Table::iterator orig_iter = iter; StaticRoute& orig_route = orig_iter->second; ++iter; if (orig_route.network() != updated_route.network()) break; if ((orig_route.unicast() != updated_route.unicast()) || (orig_route.multicast() != updated_route.multicast())) { continue; } if (updated_route.is_backup_route()) { if ((orig_route.is_backup_route() != updated_route.is_backup_route()) || (orig_route.ifname() != updated_route.ifname()) || (orig_route.vifname() != updated_route.vifname()) || (orig_route.nexthop() != updated_route.nexthop())) { continue; } } // // Route found. Add a copy to the list of routes to be deleted // and delete the original route; // delete_routes.push_back(orig_route); _static_routes.erase(orig_iter); } list::iterator delete_iter; for (delete_iter = delete_routes.begin(); delete_iter != delete_routes.end(); ++delete_iter) { StaticRoute& orig_route = *delete_iter; // XXX: actually a copy bool was_accepted = orig_route.is_accepted_by_rib(); StaticRoute copy_route = orig_route; prepare_route_for_transmission(orig_route, copy_route); copy_route.set_delete_route(); // // If the original route wasn't transmitted, then the RIB doesn't // know about it. // if (! was_accepted) continue; // // Inform the RIB about the change // inform_rib(copy_route); } return XORP_OK; } /** * Check whether the route entry is valid. * * @param error_msg the error message (if error). * @return true if the route entry is valid, otherwise false. */ bool StaticRoute::is_valid_entry(string& error_msg) const { // // Check the unicast and multicast flags // if ((_unicast == false) && (_multicast == false)) { error_msg = "the route is neither unicast nor multicast"; return false; } if ((_unicast == true) && (_multicast == true)) { error_msg = "the route must be either unicast or multicast"; return false; } return true; } /** * Test whether the route is accepted for transmission to the RIB. * * @return true if route is accepted for transmission to the RIB, * otherwise false. */ bool StaticRoute::is_accepted_by_rib() const { return (is_accepted_by_nexthop() && (! is_filtered())); } void StaticRoutesNode::configure_filter(const uint32_t& filter, const string& conf) { _policy_filters.configure(filter, conf); } void StaticRoutesNode::reset_filter(const uint32_t& filter) { _policy_filters.reset(filter); } void StaticRoutesNode::push_routes() { StaticRoutesNode::Table::iterator iter; // XXX: not a background task for (iter = _static_routes.begin(); iter != _static_routes.end(); ++iter) { StaticRoute& orig_route = iter->second; bool was_accepted = orig_route.is_accepted_by_rib(); // // Create a copy of the route and inform the RIB if necessary // StaticRoute copy_route = orig_route; prepare_route_for_transmission(orig_route, copy_route); // // XXX: If necessary, change the type of the route. // E.g., "replace route" may become "add route" or "delete route". // if (copy_route.is_accepted_by_rib()) { if (was_accepted) { copy_route.set_replace_route(); } else { copy_route.set_add_route(); } } else { if (was_accepted) { copy_route.set_delete_route(); } else { continue; } } // // Inform the RIB about the change // inform_rib(copy_route); } } void StaticRoutesNode::push_pull_rib_routes(bool is_push) { StaticRoutesNode::Table::iterator iter; // XXX: not a background task for (iter = _static_routes.begin(); iter != _static_routes.end(); ++iter) { StaticRoute& orig_route = iter->second; // // Create a copy of the route and inform the RIB if necessary // StaticRoute copy_route = orig_route; prepare_route_for_transmission(orig_route, copy_route); // // XXX: Only routes that are accepted by RIB should be added or deleted // if (! copy_route.is_accepted_by_rib()) continue; if (is_push) { copy_route.set_add_route(); } else { copy_route.set_delete_route(); } // // Inform the RIB about the change // inform_rib(copy_route); } } bool StaticRoutesNode::is_accepted_by_nexthop(const StaticRoute& route) const { if (route.is_interface_route()) { const IfMgrIfAtom* if_atom; const IfMgrVifAtom* vif_atom; bool is_up = false; if_atom = _iftree.find_interface(route.ifname()); vif_atom = _iftree.find_vif(route.ifname(), route.vifname()); if ((if_atom != NULL) && (if_atom->enabled()) && (! if_atom->no_carrier()) && (vif_atom != NULL) && (vif_atom->enabled())) { is_up = true; } if (is_up) { return (true); } } else { string ifname, vifname; if (_iftree.is_directly_connected(route.nexthop(), ifname, vifname)) { return (true); } } return (false); } void StaticRoutesNode::prepare_route_for_transmission(StaticRoute& orig_route, StaticRoute& copy_route) { // // We do not want to modify original route, so we may re-filter routes on // filter configuration changes. Hence, copy the route. // copy_route = orig_route; // Do policy filtering and other acceptance tests bool filtered = (! do_filtering(copy_route)); bool accepted_by_nexthop = is_accepted_by_nexthop(copy_route); copy_route.set_filtered(filtered); copy_route.set_accepted_by_nexthop(accepted_by_nexthop); // Tag the original route orig_route.set_filtered(filtered); orig_route.set_accepted_by_nexthop(accepted_by_nexthop); } void StaticRoutesNode::inform_rib(const StaticRoute& route) { StaticRoute modified_route(route); StaticRoutesNode::Table::iterator best_iter; map *winning_routes_ptr; map::iterator old_winning_iter; if (! is_enabled()) return; // // Set the pointer to the appropriate table with the winning routes // if (route.unicast()) { winning_routes_ptr = &_winning_routes_unicast; } else { winning_routes_ptr = &_winning_routes_multicast; } // // Find the iterators for the current best route and the old winning route // best_iter = find_best_accepted_route(_static_routes, route); old_winning_iter = winning_routes_ptr->find(route.network()); // // Decide whether we need to inform the RIB about the change. // If yes, then update the table with the winning routes. // bool do_inform = false; if (route.is_add_route() || route.is_replace_route()) { if (route.is_accepted_by_rib()) { XLOG_ASSERT(best_iter != _static_routes.end()); StaticRoute& best_route = best_iter->second; if (route.is_same_route(best_route)) { // // If there was already a route sent to the RIB, then // an "add" route should become a "replace" route. // if (old_winning_iter != winning_routes_ptr->end()) { if (route.is_add_route()) { modified_route.set_replace_route(); } winning_routes_ptr->erase(old_winning_iter); } winning_routes_ptr->insert(make_pair(modified_route.network(), modified_route)); do_inform = true; } else { // // Check whether the best route is now different. If yes, then // send the current best route instead (as a "replace" route). // if (old_winning_iter != winning_routes_ptr->end()) { const StaticRoute& old_winning_route = old_winning_iter->second; if (! best_route.is_same_route(old_winning_route)) { winning_routes_ptr->erase(old_winning_iter); modified_route = best_route; modified_route.set_replace_route(); winning_routes_ptr->insert(make_pair(modified_route.network(), modified_route)); do_inform = true; } } } } } if (route.is_delete_route()) { if (old_winning_iter != winning_routes_ptr->end()) { StaticRoute& old_winning_route = old_winning_iter->second; if (route.is_same_route(old_winning_route)) { winning_routes_ptr->erase(old_winning_iter); do_inform = true; // // Check whether there is another best route. If yes, then // send the current best route instead (as a "replace" route). // if (best_iter != _static_routes.end()) { StaticRoute& best_route = best_iter->second; modified_route = best_route; modified_route.set_replace_route(); winning_routes_ptr->insert(make_pair(modified_route.network(), modified_route)); } } } } // // Inform the RIB about the change // if (do_inform) inform_rib_route_change(modified_route); } /** * Update a route received from the user configuration. * * Currently, this method is a no-op. * * @param iftree the tree with the interface state to update the route. * @param route the route to update. * @return true if the route was updated, otherwise false. */ bool StaticRoutesNode::update_route(const IfMgrIfTree& iftree, StaticRoute& route) { UNUSED(iftree); UNUSED(route); return (false); } bool StaticRoutesNode::do_filtering(StaticRoute& route) { try { StaticRoutesVarRW varrw(route); // Import filtering bool accepted; debug_msg("[STATIC] Running filter: %s on route: %s\n", filter::filter2str(filter::IMPORT), route.network().str().c_str()); accepted = _policy_filters.run_filter(filter::IMPORT, varrw); route.set_filtered(!accepted); // Route Rejected if (!accepted) return accepted; StaticRoutesVarRW varrw2(route); // Export source-match filtering debug_msg("[STATIC] Running filter: %s on route: %s\n", filter::filter2str(filter::EXPORT_SOURCEMATCH), route.network().str().c_str()); _policy_filters.run_filter(filter::EXPORT_SOURCEMATCH, varrw2); return accepted; } catch(const PolicyException& e) { XLOG_FATAL("PolicyException: %s", e.str().c_str()); // FIXME: What do we do ? XLOG_UNFINISHED(); } } xorp/static_routes/SConscript0000664000076400007640000000622711631506536016577 0ustar greearbgreearb# Copyright (c) 2009-2011 XORP, Inc and Others # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # $XORP$ import os Import('env') env = env.Clone() is_shared = env.has_key('SHAREDLIBS') env.AppendUnique(CPPPATH = [ '#', '$BUILDDIR', ]) env.PrependUnique(LIBPATH = [ '$BUILDDIR/libxorp', '$BUILDDIR/libcomm', '$BUILDDIR/libxipc', '$BUILDDIR/libproto', '$BUILDDIR/libfeaclient', '$BUILDDIR/policy/backend', '$BUILDDIR/policy/common', '$BUILDDIR/xrl/interfaces', '$BUILDDIR/xrl/targets', '.' ]) env.AppendUnique(LIBS = [ 'xorp_static_routes', # the library, not the executable 'xorp_fea_client', 'xif_fea_ifmgr_mirror', 'xif_fea_ifmgr_replicator', 'xif_finder_event_notifier', 'xst_fea_ifmgr_mirror', 'xst_static_routes', 'xif_rib', 'xif_finder_event_notifier', 'xorp_policy_backend', 'xorp_policy_common', 'xorp_ipc', 'xorp_core', 'xorp_proto', 'xorp_comm', ]) if not is_shared: env.AppendUnique(LIBS = [ "crypto", ]) if not (env.has_key('mingw') and env['mingw']): env.AppendUnique(LIBS = [ "rt", ]) if (env.has_key('mingw') and env['mingw']): env.AppendUnique(LIBS = [ 'ws2_32', 'iphlpapi', # 'mprapi', 'regex', 'winmm', ]) env.Append(LIBS = ['xorp_core']) env.Replace(RPATH = [ env.Literal(env['xorp_module_rpath']) ]) libxorp_static_routes_srcs = [ 'static_routes_node.cc', 'static_routes_varrw.cc', 'xrl_static_routes_node.cc' ] xorp_static_routes_srcs = [ 'xorp_static_routes.cc' ] if is_shared: libxorp_static_routes = env.SharedLibrary( target = 'libxorp_static_routes', source = libxorp_static_routes_srcs, LIBS = '') if env['rtld_origin']: for obj in libxorp_static_routes: env.AddPostAction(libxorp_static_routes, env.Symlink(obj.abspath, os.path.join(env['xorp_alias_libdir'], str(obj)))) else: libxorp_static_routes = env.StaticLibrary( target = 'libxorp_static_routes', source = libxorp_static_routes_srcs, LIBS = '') xorp_static_routes = env.Program(target = 'xorp_static_routes', source = xorp_static_routes_srcs) env.Alias('install', env.InstallProgram(env['xorp_moduledir'], xorp_static_routes)) if is_shared: env.Alias('install', env.InstallLibrary(env['xorp_libdir'], libxorp_static_routes)) Default(xorp_static_routes) xorp/static_routes/command_static_routes0000775000076400007640000000212111421137511021054 0ustar greearbgreearb#!/bin/sh # # $XORP: xorp/pim/command_pim,v 1.8 2003/10/15 19:02:07 pavlin Exp $ # # # Send commands to a running StaticRoutes process. # MODULE_NAME="StaticRoutes" HOSTNAME=`hostname` echo "Sending command to $MODULE_NAME on $HOSTNAME..." # Conditionally set ${srcdir} if it wasn't assigned (e.g., by `gmake check`) if [ "X${srcdir}" = "X" ] ; then srcdir=`dirname $0` ; fi # # The optional first argument should be either -4 or -6 to specify # IPv4 or IPv6 command # # The next argument should be the command to call. # The rest of the arguments should be the arguments to the command to call. # usage() { echo "Usage: $0 [-4 | -6 ] [xrl_command_arguments ... ]" } if [ $# -lt 1 ] ; then usage; exit 1 fi case "${1}" in -4) if [ $# -lt 2 ] ; then usage; exit 1 fi IP_VERSION=IPV4 shift ;; -6) if [ $# -lt 2 ] ; then usage; exit 1 fi IP_VERSION=IPV6 shift ;; *) # XXX: IP version defaults to IPv4 IP_VERSION=IPV4 ;; esac . ${srcdir}/../utils/xrl_shell_lib.sh . ${srcdir}/../static_routes/xrl_static_routes_shell_funcs.sh $* exit $? xorp/static_routes/static_routes_node.hh0000664000076400007640000007235511540224236021001 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/static_routes/static_routes_node.hh,v 1.32 2008/10/02 21:58:29 bms Exp $ #ifndef __STATIC_ROUTES_STATIC_ROUTES_NODE_HH__ #define __STATIC_ROUTES_STATIC_ROUTES_NODE_HH__ // // StaticRoutes node definition. // #include "libxorp/service.hh" #include "libxorp/status_codes.h" #include "libfeaclient/ifmgr_xrl_mirror.hh" #include "policy/backend/policytags.hh" #include "policy/backend/policy_filters.hh" class EventLoop; /** * @short A StaticRoute helper class. * * This class is used to store a routing entry. */ class StaticRoute { public: /** * Constructor for a given IPv4 static route. * * @param unicast if true, then the route would be used for unicast * routing. * @param multicast if true, then the route would be used in the MRIB * (Multicast Routing Information Base) for multicast purpose (e.g., * computing the Reverse-Path Forwarding information). * @param network the network address prefix this route applies to. * @param nexthop the address of the next-hop router for this route. * @param ifname of the name of the physical interface toward the * destination. * @param vifname of the name of the virtual interface toward the * destination. * @param metric the metric distance for this route. * @param is_backup_route if true, then this is a backup route. */ StaticRoute(bool unicast, bool multicast, const IPv4Net& network, const IPv4& nexthop, const string& ifname, const string& vifname, uint32_t metric, bool is_backup_route) : _unicast(unicast), _multicast(multicast), _network(network), _nexthop(nexthop), _ifname(ifname), _vifname(vifname), _metric(metric), _is_backup_route(is_backup_route), _route_type(IDLE_ROUTE), _is_ignored(false), _is_filtered(false), _is_accepted_by_nexthop(false) {} #ifdef XORP_USE_USTL StaticRoute() { } #endif /** * Constructor for a given IPv6 static route. * * @param unicast if true, then the route would be used for unicast * routing. * @param multicast if true, then the route would be used in the MRIB * (Multicast Routing Information Base) for multicast purpose (e.g., * computing the Reverse-Path Forwarding information). * @param network the network address prefix this route applies to. * @param nexthop the address of the next-hop router for this route. * @param ifname of the name of the physical interface toward the * destination. * @param vifname of the name of the virtual interface toward the * destination. * @param metric the metric distance for this route. * @param is_backup_route if true, then this is a backup route. */ StaticRoute(bool unicast, bool multicast, const IPv6Net& network, const IPv6& nexthop, const string& ifname, const string& vifname, uint32_t metric, bool is_backup_route) : _unicast(unicast), _multicast(multicast), _network(network), _nexthop(nexthop), _ifname(ifname), _vifname(vifname), _metric(metric), _is_backup_route(is_backup_route), _route_type(IDLE_ROUTE), _is_ignored(false), _is_filtered(false), _is_accepted_by_nexthop(false) {} /** * Equality Operator * * @param other the right-hand operand to compare against. * @return true if the left-hand operand is numerically same as the * right-hand operand. */ bool operator==(const StaticRoute& other) const { return (is_same_route(other) && (_route_type == other._route_type) && (_policytags == other._policytags)); } /** * Test whether both routes contain same routing information. * * @param other the route to compare against. * @return true if both routes contain same routing information, * otherwise false. */ bool is_same_route(const StaticRoute& other) const { return ((_unicast == other.unicast()) && (_multicast == other.multicast()) && (_network == other.network()) && (_nexthop == other.nexthop()) && (_ifname == other.ifname()) && (_vifname == other.vifname()) && (_metric == other.metric())); } /** * Test if this is an IPv4 route. * * @return true if this is an IPv4 route, otherwise false. */ bool is_ipv4() const { return _network.is_ipv4(); } /** * Test if this is an IPv6 route. * * @return true if this is an IPv6 route, otherwise false. */ bool is_ipv6() const { return _network.is_ipv6(); } /** * Test if this route would be used for unicast routing. * * @return true if this route would be used for unicast routing, * otherwise false. */ bool unicast() const { return _unicast; } /** * Test if this route would be used for multicast routing. * * @return true if this route would be used for multicast routing, * otherwise false. */ bool multicast() const { return _multicast; } /** * Get the network address prefix this route applies to. * * @return the network address prefix this route appies to. */ const IPvXNet& network() const { return _network; } /** * Get the address of the next-hop router for this route. * * @return the address of the next-hop router for this route. */ const IPvX& nexthop() const { return _nexthop; } /** * Set the address of the next-hop router for this route. * * @param v the address of the next-hop router for this route. */ void set_nexthop(const IPvX& v) { _nexthop = v; } /** * Get the name of the physical interface toward the destination. * * @return the name of the physical interface toward the destination. */ const string& ifname() const { return _ifname; } /** * Set the name of the physical interface toward the destination. * * @param v the name of the physical interface toward the destination. */ void set_ifname(const string& v) { _ifname = v; } /** * Get the name of the virtual interface toward the destination. * * @return the name of the virtual interface toward the destination. */ const string& vifname() const { return _vifname; } /** * Set the name of the virtual interface toward the destination. * * @param v the name of the virtual interface toward the destination. */ void set_vifname(const string& v) { _vifname = v; } /** * Get the metric distance for this route. * * @return the metric distance for this route. */ uint32_t metric() const { return _metric; } /** * Test if this is a backup route. * * @return truf if this is a backup route, otherwise false. */ bool is_backup_route() const { return _is_backup_route; } /** * Test if this is a route to add. * * @return true if this is a route to add, otherwise false. */ bool is_add_route() const { return (_route_type == ADD_ROUTE); } /** * Test if this is a replacement route. * * @return true if this is a replacement route, otherwise false. */ bool is_replace_route() const { return (_route_type == REPLACE_ROUTE); } /** * Test if this is a route to delete. * * @return true if this is a route to delete, otherwise false. */ bool is_delete_route() const { return (_route_type == DELETE_ROUTE); } /** * Set the type of this route to "a route to add". */ void set_add_route() { _route_type = ADD_ROUTE; } /** * Set the type of this route to "a replacement route". */ void set_replace_route() { _route_type = REPLACE_ROUTE; } /** * Set the type of this route to "a route to delete". */ void set_delete_route() { _route_type = DELETE_ROUTE; } /** * Test if the route is interface-specific (e.g., if the interface * is explicitly specified). * * @return true if the route is interface-specific, otherwise false. */ bool is_interface_route() const { return ! (_ifname.empty() && _vifname.empty()); } /** * Check whether the route entry is valid. * * @param error_msg the error message (if error). * @return true if the route entry is valid, otherwise false. */ bool is_valid_entry(string& error_msg) const; /** * Test if the route is to be ignored. * * This method is used only for internal purpose when passing the route * around. * * @return true if the route is to be ignored, otherwise false. */ bool is_ignored() const { return _is_ignored; } /** * Set whether the route is to be ignored. * * This method is used only for internal purpose when passing the route * around. * * @param v true if the route is to be ignored, otherwise false. */ void set_ignored(bool v) { _is_ignored = v; } /** * @return policy-tags for this route. */ PolicyTags& policytags() { return _policytags; } /** * Test whether the route has been rejected by a policy filter. * * @return true if route has been rejected by a policy filter, otherwise * false. */ bool is_filtered() const { return _is_filtered; } /** * Set a flag that indicates whether the route is to be considered * filtered [rejected by the policy filter]. * * @param v true if the route should be considered filtered, otherwise * false. */ void set_filtered(bool v) { _is_filtered = v; } /** * Test whether the route is accepted based on its next-hop information. * * @return true if the route is accepted based on its next-hop * information, otherwise false. */ bool is_accepted_by_nexthop() const { return _is_accepted_by_nexthop; } /** * Set a flag that indicates whether the route is accepted based * on its next-hop information. * * @param v true if the route is accepted based on its next-hop * information, otherwise false. */ void set_accepted_by_nexthop(bool v) { _is_accepted_by_nexthop = v; } /** * Test whether the route is accepted for transmission to the RIB. * * @return true if route is accepted for transmission to the RIB, * otherwise false. */ bool is_accepted_by_rib() const; private: bool _unicast; bool _multicast; IPvXNet _network; IPvX _nexthop; string _ifname; string _vifname; uint32_t _metric; bool _is_backup_route; enum RouteType { IDLE_ROUTE, ADD_ROUTE, REPLACE_ROUTE, DELETE_ROUTE }; RouteType _route_type; bool _is_ignored; // True if the route is to be ignored bool _is_filtered; // True if rejected by a policy filter bool _is_accepted_by_nexthop; // True if the route is accepted based on its next-hop information PolicyTags _policytags; }; /** * @short The StaticRoutes node class. * * There should be one node per StaticRoutes instance. */ class StaticRoutesNode : public IfMgrHintObserver, public ServiceBase, public ServiceChangeObserverBase { public: typedef multimap Table; /** * Constructor for a given event loop. * * @param eventloop the event loop to use. */ StaticRoutesNode(EventLoop& eventloop); /** * Destructor */ virtual ~StaticRoutesNode(); /** * Get the event loop this node is added to. * * @return the event loop this node is added to. */ EventLoop& eventloop() { return _eventloop; } /** * Get the protocol name. * * @return a string with the protocol name. */ const string& protocol_name() const { return _protocol_name; } /** * Startup the node operation. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int startup(); /** * Shutdown the node operation. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int shutdown(); /** * Get the node status (see @ref ProcessStatus). * * @param reason_msg return-by-reference string that contains * human-readable information about the status. * @return the node status (see @ref ProcessStatus). */ ProcessStatus node_status(string& reason_msg); /** * Test if the node processing is done. * * @return true if the node processing is done, otherwise false. */ bool is_done() const { return (_node_status == PROC_DONE); } /** * Test whether the node operation is enabled. * * @return true if the node operation is enabled, otherwise false. */ bool is_enabled() const { return _is_enabled; } /** * Enable/disable node operation. * * Note that for the time being it affects only whether the routes * are installed into RIB. In the future it may affect the interaction * with other modules as well. * * @param enable if true then enable node operation, otherwise disable it. */ void set_enabled(bool enable); /** * Add a static IPv4 route. * * @param unicast if true, then the route would be used for unicast * routing. * @param multicast if true, then the route would be used in the MRIB * (Multicast Routing Information Base) for multicast purpose (e.g., * computing the Reverse-Path Forwarding information). * @param network the network address prefix this route applies to. * @param nexthop the address of the next-hop router for this route. * @param ifname of the name of the physical interface toward the * destination. * @param vifname of the name of the virtual interface toward the * destination. * @param metric the metric distance for this route. * @param is_backup_route if true, then this is a backup route operation. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_route4(bool unicast, bool multicast, const IPv4Net& network, const IPv4& nexthop, const string& ifname, const string& vifname, uint32_t metric, bool is_backup_route, string& error_msg); /** * Add a static IPv6 route. * * @param unicast if true, then the route would be used for unicast * routing. * @param multicast if true, then the route would be used in the MRIB * (Multicast Routing Information Base) for multicast purpose (e.g., * computing the Reverse-Path Forwarding information). * @param network the network address prefix this route applies to. * @param nexthop the address of the next-hop router for this route. * @param ifname of the name of the physical interface toward the * destination. * @param vifname of the name of the virtual interface toward the * destination. * @param metric the metric distance for this route. * @param is_backup_route if true, then this is a backup route operation. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_route6(bool unicast, bool multicast, const IPv6Net& network, const IPv6& nexthop, const string& ifname, const string& vifname, uint32_t metric, bool is_backup_route, string& error_msg); /** * Replace a static IPv4 route. * * @param unicast if true, then the route would be used for unicast * routing. * @param multicast if true, then the route would be used in the MRIB * (Multicast Routing Information Base) for multicast purpose (e.g., * computing the Reverse-Path Forwarding information). * @param network the network address prefix this route applies to. * @param nexthop the address of the next-hop router for this route. * @param ifname of the name of the physical interface toward the * destination. * @param vifname of the name of the virtual interface toward the * destination. * @param metric the metric distance for this route. * @param is_backup_route if true, then this is a backup route operation. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int replace_route4(bool unicast, bool multicast, const IPv4Net& network, const IPv4& nexthop, const string& ifname, const string& vifname, uint32_t metric, bool is_backup_route, string& error_msg); /** * Replace a static IPv6 route. * * @param unicast if true, then the route would be used for unicast * routing. * @param multicast if true, then the route would be used in the MRIB * (Multicast Routing Information Base) for multicast purpose (e.g., * computing the Reverse-Path Forwarding information). * @param network the network address prefix this route applies to. * @param nexthop the address of the next-hop router for this route. * @param ifname of the name of the physical interface toward the * destination. * @param vifname of the name of the virtual interface toward the * destination. * @param metric the metric distance for this route. * @param is_backup_route if true, then this is a backup route operation. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int replace_route6(bool unicast, bool multicast, const IPv6Net& network, const IPv6& nexthop, const string& ifname, const string& vifname, uint32_t metric, bool is_backup_route, string& error_msg); /** * Delete a static IPv4 route. * * @param unicast if true, then the route would be used for unicast * routing. * @param multicast if true, then the route would be used in the MRIB * (Multicast Routing Information Base) for multicast purpose (e.g., * computing the Reverse-Path Forwarding information). * @param network the network address prefix this route applies to. * @param nexthop the address of the next-hop router for this route. * @param ifname of the name of the physical interface toward the * destination. * @param vifname of the name of the virtual interface toward the * destination. * @param is_backup_route if true, then this is a backup route operation. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int delete_route4(bool unicast, bool multicast, const IPv4Net& network, const IPv4& nexthop, const string& ifname, const string& vifname, bool is_backup_route, string& error_msg); /** * Delete a static IPv6 route. * * @param unicast if true, then the route would be used for unicast * routing. * @param multicast if true, then the route would be used in the MRIB * (Multicast Routing Information Base) for multicast purpose (e.g., * computing the Reverse-Path Forwarding information). * @param network the network address prefix this route applies to. * @param nexthop the address of the next-hop router for this route. * @param ifname of the name of the physical interface toward the * destination. * @param vifname of the name of the virtual interface toward the * destination. * @param is_backup_route if true, then this is a backup route operation. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int delete_route6(bool unicast, bool multicast, const IPv6Net& network, const IPv6& nexthop, const string& ifname, const string& vifname, bool is_backup_route, string& error_msg); /** * Find a route from the routing table. * * @param table the routing table to seach. * @param key_route the route information to search for. * @return a table iterator to the route. If the route is not found, * the iterator will point to the end of the table. */ StaticRoutesNode::Table::iterator find_route( StaticRoutesNode::Table& table, const StaticRoute& key_route); /** * Find the best accepted route from the routing table. * * @param table the routing table to seach. * @param key_route the route information to search for. * @return a table iterator to the route. If the route is not found, * the iterator will point to the end of the table. */ StaticRoutesNode::Table::iterator find_best_accepted_route( StaticRoutesNode::Table& table, const StaticRoute& key_route); // // Debug-related methods // /** * Test if trace log is enabled. * * This method is used to test whether to output trace log debug messges. * * @return true if trace log is enabled, otherwise false. */ bool is_log_trace() const { return (_is_log_trace); } /** * Enable/disable trace log. * * This method is used to enable/disable trace log debug messages output. * * @param is_enabled if true, trace log is enabled, otherwise is disabled. */ void set_log_trace(bool is_enabled) { _is_log_trace = is_enabled; } /** * Configure a policy filter. * * Will throw an exception on error. * * Export filter is not supported by static routes. * * @param filter identifier of filter to configure. * @param conf configuration of the filter. */ void configure_filter(const uint32_t& filter, const string& conf); /** * Reset a policy filter. * * @param filter identifier of filter to reset. */ void reset_filter(const uint32_t& filter); /** * Push all the routes through the policy filters for re-filtering. */ void push_routes(); /** * Push or pull all the routes to/from the RIB. * * @param is_push if true, then push the routes, otherwise pull them */ void push_pull_rib_routes(bool is_push); protected: // // IfMgrHintObserver methods // void tree_complete(); void updates_made(); void incr_startup_requests_n(); void decr_startup_requests_n(); void incr_shutdown_requests_n(); void decr_shutdown_requests_n(); void update_status(); private: /** * A method invoked when the status of a service changes. * * @param service the service whose status has changed. * @param old_status the old status. * @param new_status the new status. */ void status_change(ServiceBase* service, ServiceStatus old_status, ServiceStatus new_status); /** * Get a reference to the service base of the interface manager. * * This is a pure virtual function, and it must be implemented * by the communication-wrapper class that inherits this base class. * * @return a reference to the service base of the interface manager. */ virtual const ServiceBase* ifmgr_mirror_service_base() const = 0; /** * Get a reference to the interface manager tree. * * This is a pure virtual function, and it must be implemented * by the communication-wrapper class that inherits this base class. * * @return a reference to the interface manager tree. */ virtual const IfMgrIfTree& ifmgr_iftree() const = 0; /** * Initiate registration with the FEA. * * This is a pure virtual function, and it must be implemented * by the communication-wrapper class that inherits this base class. */ virtual void fea_register_startup() = 0; /** * Initiate de-registration with the FEA. * * This is a pure virtual function, and it must be implemented * by the communication-wrapper class that inherits this base class. */ virtual void fea_register_shutdown() = 0; /** * Initiate registration with the RIB. * * This is a pure virtual function, and it must be implemented * by the communication-wrapper class that inherits this base class. */ virtual void rib_register_startup() = 0; /** * Initiate de-registration with the RIB. * * This is a pure virtual function, and it must be implemented * by the communication-wrapper class that inherits this base class. */ virtual void rib_register_shutdown() = 0; /** * Add a static IPvX route. * * @param static_route the route to add. * @see StaticRoute * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_route(const StaticRoute& static_route, string& error_msg); /** * Replace a static IPvX route. * * @param static_route the replacement route. * @see StaticRoute * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int replace_route(const StaticRoute& static_route, string& error_msg); /** * Delete a static IPvX route. * * @param static_route the route to delete. * @see StaticRoute * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int delete_route(const StaticRoute& static_route, string& error_msg); /** * Prepare a copy of a route for transmission to the RIB. * * Note that the original route will be modified as appropriate. * * @param orig_route the original route to prepare. * @param copy_route the copy of the original route prepared for * transmission to the RIB. */ void prepare_route_for_transmission(StaticRoute& orig_route, StaticRoute& copy_route); /** * Inform the RIB about a route change. * * This is a pure virtual function, and it must be implemented * by the communication-wrapper class that inherits this base class. * * @param static_route the route with the information about the change. */ virtual void inform_rib_route_change(const StaticRoute& static_route) = 0; /** * Cancel a pending request to inform the RIB about a route change. * * This is a pure virtual function, and it must be implemented * by the communication-wrapper class that inherits this base class. * * @param static_route the route with the request that would be canceled. */ virtual void cancel_rib_route_change(const StaticRoute& static_route) = 0; /** * Update a route received from the user configuration. * * Currently, this method is a no-op. * * @param iftree the tree with the interface state to update the route. * @param route the route to update. * @return true if the route was updated, otherwise false. */ bool update_route(const IfMgrIfTree& iftree, StaticRoute& route); /** * Do policy filtering on a route. * * @param route route to filter. * @return true if route was accepted by policy filter, otherwise false. */ bool do_filtering(StaticRoute& route); /** * Test whether a route is accepted based on its next-hop information. * * @param route the route to test. * @return true if the route is accepted based on its next-hop * information, otherwise false. */ bool is_accepted_by_nexthop(const StaticRoute& route) const; /** * Inform the RIB about a route. * * @param r route which should be updated in the RIB. */ void inform_rib(const StaticRoute& r); /** * Set the node status. * * @param v the new node status. */ void set_node_status(ProcessStatus v) { _node_status = v; } EventLoop& _eventloop; // The event loop ProcessStatus _node_status; // The node/process status const string _protocol_name; // The protocol name bool _is_enabled; // Flag whether node is enabled // // The routes are stored in a multimap, because we allow more than one // route for the same subnet destination. We need this to support // floating static routes: routes to same destination but different // next-hop router and metric. // StaticRoutesNode::Table _static_routes; // The routes // The winning routes map _winning_routes_unicast; map _winning_routes_multicast; // // Status-related state // size_t _startup_requests_n; size_t _shutdown_requests_n; // // A local copy with the interface state information // IfMgrIfTree _iftree; // // Debug and test-related state // bool _is_log_trace; // If true, enable XLOG_TRACE() PolicyFilters _policy_filters; // Only one instance of this! }; #endif // __STATIC_ROUTES_STATIC_ROUTES_NODE_HH__ xorp/LICENSE.lgpl0000664000076400007640000006356211421137511013632 0ustar greearbgreearbFor the applicability of this license please check the "LICENSE" file. GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! xorp/RELEASE_NOTES0000664000076400007640000027766011703346457013667 0ustar greearbgreearb XORP RELEASE NOTES This file contains XORP release notes (most recent releases first). Release 1.8.5 * Fix bad OSPF bug that made neighbours very slow to go to Full state in large and/or lossy OSPF router configurations. Thanks to Iqor for access to test systems and for funding this effort. * Compile in build-information to better track versions in logs and bug reports. * Optimize logging to check the log level before doing the work to create the logging messages. Simplify a lot of the related code. Runtime should be faster, but executable size is slightly larger. * netbsd: Fix compile on netbsd, properly detect IPv6 Multicast routing support, fix a few bugs related to multicast. It *might* work now, but seeing spurious failures when trying the MRT6_ADD_MIF call. Thanks to Uwe <6bone@6bone.informatik.uni-leipzig.de> for access to netbsd boxes and testing work. * Fix multicast over GRE tunnels. Anything else over GRE also has a chance to work now. Thanks to J G Miller for bug reporting and testing. * ARM: Fix raw-sockets on some ARM platforms. The scons checks were lame at best. They are still suspect, but now unknown will be treated as supporting IPV4 raw input/output. If we find other systems that do NOT support this (freebsd, netbsd??), we can update the scons accordingly and/or find a way to do the detection more proper. Thanks to J G Miller for doing tests on his ARM box. * Fix compile on Ubuntu 11.10 (and perhaps other recent systems). git shortlog since 1.8.4: Ben Greear (55): Update html, release-process notes. Add some notes in the rpm building script. Update version to 1.8.5-WIP ospf: Log peer state changes. fea: Re-arrange netlink parsing logic. fea: Fix GRE tunnel IP addresses on Linux. multicast: Don't crash if interface configured for mcast doesn't have MULTICAST flag. Add copyright header to bug_cather.hh build-info: Support auto-generated build information. build-info: Defaults to enabled. build-info: Add xorp version string to xorpsh. build: Always add bigoflag unless using override. compile: Use 'full' optimization by default: -O2 mii: Fix strict-aliasing warnings when compilign with -O2 olsr: Fix un-used variable warnings. buildinfo: Make script deal better with missing perl & git. netbsd: Fix sockutil.cc build on netbsd. netbsd: Fix click mount command for netbsd. netbsd: Fix compile problem in ifconfig_get_ioctl. xlog: Simplify xlog framework. ospf: Remove some overly verbose trace debugging. ospf: Set trace logging verbosity to high. ospf: Fix bad lsa retransmit timer bug. ospf: Improve some debugging messages. Update release notes for OSPF bug, logging changes. ospf: Add some debugging for routing-table warnings. netbsd: Fix compile on netbsd. Merge branch 'master' of github.com:greearb/xorp.ct buildinfo: Use sed instead of perl, should still handle "'s click: Enable building click. fea: Add comment to explain strange mii code. fea: Remove dead code, small tweaks. buildinfo: Fix missing \ in sed logic. buildinfo: Fix escaping single backslash Signed-off-by: Ben Greear Merge branch 'master' of github.com:greearb/xorp.ct fea: Fix crash when mcast_protocol_fd_in returns NULL. fea: Auto-create mcast socket if none exist. bsd/mcast: Properly detect support for IPv6 multicast routing. bsd: Fix assert when using ipv6 mcast routing. bsd/fea: Add some debugging for ipv6 mcast socket failures. rls-notes: Note limited netbsd support for ipv6 mcast. Signed-off-by: Ben Greear Fix cruft left in by last commit. bgp: Fix another potential hang when shutting down BGP. bgp/socket: Make BGP error message slightly more useful. livecd: Update web docs for live-cd. fea/firewall: Enable firewall support on linux. Update rls notes to note enabling firewall logic. firewall: Fix suggested code patch for iptables.h ospf: Remove assertion when FINDER is lost. Update 'save' help text..was talking about 'load' instead. fea/mfea: Do a better job of cleaning up multicast on reload. pim/igmp: Fix config-file reload issues. pkg-script: Update .deb build script for latest version. Rename directory with ":" in name to work better on Windows. mld6igmp: Try to not send pkts with 0.0.0.0 source IP. vlans: Create vlans with dev.vid format (eth1.5) fea: Work around some bugs relating to vifs. compile: Add checks for existence of CC and CXX compiler. Fix up gcc/g++ check for older Scons. Start working porting to mingw cross-compile to win32 executable. Tweak a few files to aid mingw compile. Re-enable Windows support for Xorp.CT. Add missing files to support Windows. win32: Fix some path errors, and some fea issues. Fix some non-win32 build breakage introduced in the win32 port. Update notes for building with standard mingw32 packages. More build notes update for win32 windows: Fix build instructions to use normal upstream mingw distribution. html: Update html slightly, add script to upload. windows: Default to tcp transport on windows builds windows: Fix typo in windows startup command. html: Update web page to be 'official' xorp page news.html: Fix typo in web page. vlans: Treat vlans as primary interfaces. html: Update bug link to point to bugzilla. Merge branch 'master' of github.com:greearb/xorp.ct Update rls notes for VLAN changes. Add example OSPF configuration file for Windows. Fix vlan-related compile on Windows-32. Fix test value related to vlans. Merge branch 'master' of github.com:greearb/xorp.ct Fix vlan related build problems on BSD. Fix up RPM packaging script for upcoming 1.8.3 release. Merge branch 'master' of github.com:greearb/xorp.ct Fix typo related to vlan changes. Update README, remove some xorp.ct stuff. Remove some ancient and un-used stuff. Remove old kdoc cruft, update doxygen.cfg file (remove .ct from project name) Fix more vlan test breakage, seems to complete 'scons check' now. Documentation: Notes for building latex, update html for wiki. Change xrl pipe group ownership to be xorp Add a README, remove some stale TODO files. www: Update web page to point to wiki documentation. www: Remove deprecated mailing lists from web page. Update copyright for changed files. Update copyright for files with 'and Others' copyright. greearb (1): scons: Fix openssl/md5.h detection on Fedora core 5. Release 1.8-CT June, 2010 Overview: Merge xorp.ct changes from Ben's private tree into upstream 1.7. Major changes include: * Allow OSPF, RIP, BGP, OSLR, and PIM to better handle interfaces being added to and deleted from a running xorp instance via the 'xorpsh' CLI tool. * PIM: Fix state-machine lockup due to failure to advance state machine in some error cases. * RIP, BGP, PIM: Allow binding the protocol sockets to specific interfaces. * FEA: Support binding to specific interfaces, including multicast protocols. Use Netlink (Linux only) to filter events so that a single instance of xorp pays attention to only it's interfaces. * FEA: Support new Linux kernel API to provide multiple multicast routing tables per kernel instance. This requires kernel 2.6.34 and the latest iproute and iptable tools. These changes are backwards compatible with normal kernels. * OLSR: Enable building OLSR, support binding to a specific interface. * Support building IPv6 multicast support on Linux. Haven't tested functionality yet. git shortlog for commits since Release 1.7. Achmad Basuki: rpms: Add scripts & files to build RPM packages on Fedora Linux (at least). Ben Greear: Remove tags and branches dir. Move trunk/* to ./, remove trunk directory. xorp-ct: Merge old CVS patch into svn repository. olsr: Enable OLSR compile in scons olsr: Fix OLSR install target, properly add OLSR templates if OLSR is enabled. libxorp: Work-around crash in selector.cc due to accessing stale memory. fea: Add method to dump netlink route messages. libxipc: De-obfuscate ReplyPacket (it was just a vector of bytes) libxipc: Give user a better clue as to why xrl router timed out. Fix some uninitialized memory errors found by valgrind. Remove some noisy logging. Add code to clean up pid-file if possible (on exit) Fix logging & tracing to show micro-seconds, greatly aids debugging performance issues. mfea: Fix vif removal race related to mld6igmp startup: Make startup faster by implementing startup methods. fea: Don't fail commit because we can't remove multicast addresses that are already deleted. startup: Put 'startup' method in common.xif. pim: Don't fail commit if pim can't find interface to stop. fea: Don't fail commit if vif is gone when un-registering protocol on interface. mfea: Defer start of mfea_vif if it can't be started when immediately requested. ospf: Add OSPF logging in the path that disables an interface. rtrmgr: Add some logging to rtr-mgr to help track configuration commits. mld6igmp: Defer mld6igmp vif start if vif is DOWN pim: Handle deferred vif_start for pim. mld6igmp: Don't faile igmp if it can't join multicast group. ospf: More logging to help track down OSPF peer link issue. ospf/fea: Add logic to print out the configuration tree. Add xorp_install.bash script Add scripts to generate .deb files on Fedora. cli: Fix memory leak libxorp: Add BugCatcher class to help debug use-after-free and similar errors. rtrmgr: Disable XRLdb run-time verification by default. Should speed up startup. build: Enable gprof fea: Allow filtering netlink messages policy: Fix redist static and connected routes to ospf. Fix compile errors on Ubuntu fea: Replace all instances of HAVE_PCAP with HAVE_PCAP_H OSPF: Interop with RFC4813, fix router-link metric for loopback case. scons: Properly detect multicast header files for older Linux distributions. fea: Properly check for minimum ethernet packet size. fea: Dynamically attempt to use ioctl to set device flags if netlink doesn't work. vrrp: Remove assert that appears to be wrong. ArpHeader/vrrp: Fix a compile error on Sparc, related to ArpHeader. fea: Remove some un-needed casts and assignments related to netlink messages. fea/vrrp: Make some errors about adding/deleting macs non-critical. fea: Detect pcap-bpf.h and don't use the feature if it isn't available. fea/pcap: Don't link against libpcap if it doesn't exist. Fix build on BSD. Move MULT_MCAST table detection logic into allconfig.py instead of hard-coding it. netlink: ifdef out netlink code if not compiled to use netlink sockets. fea: Thoroughly disable all routing_socket code if platform doesn't support it. fea: Don't compile ioctl get/set logic if netlink is defined (we prefer netlink). fea/click: Disable CLICK by default. Saves around 200k of disk space. AlignData cannot be made to copy-data, other code makes assumptions that it does not. Allow disabling of IPv6 via scons...even if OS supports it. ipv6: Conditionally compile much of the IPv6 related code. ipv6: Conditionally compile some more IPv6 code. fea/firewall: Allow to compile out firewall code, saves ~180k stripped. Only generate .scons_build_args when doing a build (not an install, for instance) Build template 'build-script' to strip 'firewall' out of fea.tp.raw. Fix crash when DEBUG_LOGGING was enabled, fix build when DEBUG_LOGING was enabled. Revert "Conditionally compile much of the IPv6 related code.: fea: Conditionally compile Remove socket6 API ipv6: Conditionally compile socket6 logic. Saves around 80k on disk. ipv6/fea: Conditionally compile socket6 API Saves about 80k on disk. ipv6/pim: Don't build pim6 if IPv6 is disabled. Saves about 12k on disk ospfv3/ipv6: Disable ospfv3 when ipv6 is disabled. Saves about 270k on disk. ospf: Fix up build errors introduced earlier, when ipv6 is enabled. Fix compile on really old systems (Fedora 5) fea/netlink: Fix problem with older kernels (FC5, 2.6.28 kernel, for instance) ripng/ipv6: Don't build ripng if not using IPv6 fea/ipv6: Conditionally compile ipv6 stuff in fea-fib-client interface. rib/xif: Re-arrange so that it will be easier to ifdef out the ipv6 functions. static/ipv6: Conditionally compile some ipv6 logic in static. ospf: Group ipv6 code together to make it easier to conditionally compile. ospf/ipv6: Conditionally compile all ipv6 code in xrl_io.cc, saves 50k rib/ipv6: Conditionally compile some ipv6 code in tools. pim/ipv6: Conditionally compile some xrl_pim_node ipv6 code. fib2mrib/ipv6: Conditionally compile ipv6 code, saves 20k ipv6: Conditionally compile (more) ipv6 code for bgp, ospf, rib, and static-routes. vrrp: Fix assert related to packet size. fea/ipv6: Conditionally compile a bunch more fea/rib/pim ipv6 code. vrrp/fea: The commit logic was paying more attention to the error_msg string than return codes. vrrp: Enable creating/deleting secondary IP address for the VRRP masters. vrrp: If vrrp tries to remove the VRRP MAC, and it's the only one that exists ..then create a random MAC to take it's place. Otherwis ipv6/pim: conditionally compile more ipv6 code. rpms: Script to automate building RPMs. fea: Add better decoding for netlink error messages. vrrp: Don't allow user to mis-configure interval. fea: If we try to read netlink for a particular seqno, but do not find it before netlink socket is out of packets to read, then do fea: Add work-around for older kernels that cannot pull single interfaces. fea: Fix link & MTU detection on funky systems. Fix 'scons check' build. carrier-sense: My previous fix for sensing carrier by looking at sysfs files on linux neglected to deal with exceptions thrown by IPMR: Add support for new multiple mcast table API. Logging: Enable conditional compilation of logging code. mfea: Update IPv6 mcast code to modern kernels. Small improvement to memory usage related to xrl_error xrl: Move ipv4 and ipv4Net objects into xrl_atom ipv6/mcast: BSD doesn't have mifc6ctl.vifc_threshold member. selector: Work around assert in selector logic. beautify code: Had to stare at offending line for quite a while to parse it. exceptions: Pass string back by reference. fea,comm: Remove some noisy logging. mld6igmp: Tweak logging and add/delete callback. policy: Add support for exporting routes from fib2mrib to ospf. rib/show_routes: Fix crash due to race. Move parse_netlink_socket logic into get_netlink_socket.cc file. fea: route distribution debugging Package script used by Candela. Final xorp.ct merge Update README for XORP.ct and rls 1.8 olsr/ospf/libxorp: Add some files missed in xorp.ct merge. oslr: Build OLSR by default. Move README to base directory. Update README file. Eric S. Johnson: vrrp: Fix asserts, remove IPs on shutdown. Saurabh IPv6/PIM: conditionally compile more ipv6 code. Release 1.7 May 1, 2010 ============================ ALL: - Compiles on Linux (x86-32, x86-64, Sparc, at least) and BSD variants. - Replaced 'make' build system with 'scons' - New installation layout. - Footprint reduction. - Bug fixes here an there, more queued for release 1.8. - Support shared libraries. OLSR: - Currently broken in 1.7 SNMP: - Removed Release 1.6 (2009/01/07) ============================ ALL: - XORP now builds on Linux CentOS 5.2, Linux openSUSE-11.0, Linux Debian-5.0 (lenny, unreleased), Linux Ubuntu 8.10, Linux Red Hat Enterprise Linux Server release 5 (Tikanga), Linux Fedora 10, and OpenBSD-4.4. - XORP now builds on Linux systems with kernel 2.6.26 which is the first one to support IPv6 multicast routing. CONFIGURATION: - If a PIM-SM router is suppose to receive and process the PIM-SM Bootstrap messages, the configuration must have the "bootstrap {}" block included (even if it is empty): protocols { pimsm4 { bootstrap { } } } protocols { pimsm6 { bootstrap { } } } Previously, the Bootstrap messages were unconditionally processed even if the "bootstrap {}" block contained "disable: true" statement. - The following CLI operational command prefix has been changed: OLD: "show route admin distances" NEW: "show route admin distance" - The CLI "show version" command is supported - Addition of run-time configuration trace options for RIP/RIPng: protocols { rip { traceoptions { disable: false } } } LIBXORP: - Class EtherMac is removed and is replaced/superseded by the existing class Mac. - Performance improvements and various bug fixes in the EventLoop and SelectorList implementation. LIBXIPC: - XRL performance enhancements. (Bug 808) - Issues that might be triggered under heavy system load have been fixed (Bug 800) LIBFEACLIENT: - No significant changes. XRL: - See LIBXIPC RTRMGR: - No significant changes. XORPSH: - Incorrect help "show bgp peers detail" command (Bug 280) - Xorpsh exiting immediately on error doesn't work (Bug 763) - "Show version" CLI supported (Bug 789) - Show route admin distance not working (Bug 812) POLICY: - Tag per route is not reset in case the policies are removed (Bug 567) - Implement per-peer BGP import/export policies (Bug 667) - Enhance policy network-lists to included prefix length (Bug 674) FEA/MFEA: - Bug fix for OpenBSD when transmitting IGMP packets. RIB: - No significant changes RIP/RIPng: - Added traceoption support OLSR: - OSPF: - When the designated router was restarted the MaxAge LSAs that it generated when reflected back would be responded to with a MaxAge LSA, this process could continue indefinitely, leaving the neighbor state in EXCHANGE. (Bug 785) - OSPF virtual links support broken in the 1.5 and 1.6 releases BGP: - Support for Red Hat Linux (Bug 793) STATIC_ROUTES: - No significant changes. MLD/IGMP: - Bug fix when checking the source address of IGMP packet if the source address is allowed to be 0.0.0.0. MLD/IGMP-Lite: - Bug fix when checking the source address of IGMP packet if the source address is allowed to be 0.0.0.0. PIM-SM: Ability to add candidate BSR after inserting Cand-RP with same group (Bug 803) FIB2MRIB: - No significant changes. SNMP: - VRRP: - Initial implementation of VRRP version 2 as described in RFC 3768. - Supports running more than one VRRP instances on the same box although they need to be on different interfaces and different LANs for correct behavior (see ERRATA). - The following master failure modes have been tested and are known to work (i.e., backup becomes master): bringing the master's interface down, rebooting / crashing the master, physically removing the network cable. Note that in the latter case the master may fail to resume operations when the cable is restored (see ERRATA). - Successfully interoperates with vrrpd. Release 1.5 (2008/07/22) ======================== ALL: - XORP now builds on DragonFlyBSD-1.10.1, DragonFlyBSD-1.12.2, FreeBSD-7.0, Linux Debian-4.0 (etch), Linux Fedora 7, Linux Fedora 8, Linux Fedora 9, Linux Gentoo 2008.0, Linux Ubuntu-7.04, Linux Ubuntu-7.10, Linux Ubuntu-8.04.1, NetBSD-4.0, OpenBSD-4.1, OpenBSD-4.2, OpenBSD-4.3, Mac OS X 10.5.2, Mac OS 10.5.3, and Mac OS X 10.5.4. CONFIGURATION: - Addition of new FEA configuration statements to set the IPv4/IPv6 unicast forwarding table IDs: fea { unicast-forwarding4 { table-id: 254 } unicast-forwarding6 { table-id: 254 } } If the table ID is not configured, the FEA will use the default table ID for the system. Note that not all systems support multiple forwarding tables. Currently, they exist only on Linux (among all systems supported by XORP). - The "DISCARD" network interface flag is printed as appropriate when displaying the list of interfaces in the CLI. - Addition of new FEA configuration statement to support "unreachable" interfaces. Such interfaces are similar to "discard" interfaces, except that instead of silently throwing away packets, the system will respond with "ICMP destination unreachable". interfaces { interface my_unreachable { unreachable: true vif my_unreachable { } } } The default value for the "unreachable" statement is false. - Addition of new FEA configuration statement to flag an interface for "management" purpose. An interface that is flagged as "management" might be used in the future by some of the protocols for protocol-specific purpose. interfaces { interface fxp0 { management: true vif fxp0 { address 10.10.10.10 { prefix-length: 24 } } } } The default value for the "management" statement is false. - Addition of support to configure VLANs on an interface. A VLAN is configured by using a "vlan" block that includes the VLAN ID: interfaces { interface fxp0 { vif fxp0 { address 10.10.10.10 { prefix-length: 24 } } vif vlan1 { vlan { vlan-id: 1 } address 10.10.20.20 { prefix-length: 24 } } } } - Addition of preliminary support to configure firewall rules. Firewall rules are configured by using numbered entries: firewall { rule4 100 { action: "drop" protocol: 6 /* TCP */ source { interface: "fxp0" vif: "fxp0" network: 0.0.0.0/0 port-begin: 0 port-end: 65535 } destination { network: 10.10.0.0/24 port-begin: 0 port-end: 1024 } } } Note that compiling firewall support on Linux systems require patching some of the system header files. See ERRATA for details. - The following PIM-SM configuration statements have been deprecated, because PIM-SM doesn't use Router Alert IP option anymore: protocols { pimsm4 { interface foo { vif foo { enable-ip-router-alert-option-check: true } } } } protocols { pimsm6 { interface foo { vif foo { enable-ip-router-alert-option-check: true } } } } LIBXORP: - The local system-independent xorp_random() implemenation is used instead of the random(3) provided by the system. - Improved MAC address support (classes Mac and EtherMac). - More consistent usage of XORP_OK and XORP_ERROR to return error codes. LIBXIPC: - Bug fix in the internal mechanism for obtaining the IPv4 addresses from the system. After the bug fix, a secondary (alias) IP address can be specified with the "-i " command-line option to the xorp_rtrmgr or xorp_finder binaries. LIBFEACLIENT: - No significant changes. XRL: - Critical bug fix that can be triggered by malformatted XRLs. - Addition of support for 64-bit integers: i64 and u64 for signed and unsigned respectively. RTRMGR: - Addition of preliminary mechanism to log events to a file or to a syslog facility. - Addition of support to run XORP in background (in daemon mode). XORPSH: - Bug fix related to assigning the node ID position in case the previous (sibling) node was deleted at the same time a new node was added. This fixes "Found out-of-order term(s) inside policy ..." error inside the policy manager. - The "-c " command line option can be used more than once to run multiple commands. - Fix a long configuration delay when using xorpsh in non-interactive mode (e.g., "cat commands.txt | xorpsh"). - Addition of a new "-e" command line option. It can be used to tell xorpsh to exit immediately if the connection to the Finder fails. POLICY: - No significant changes. FEA/MFEA: - Major refactoring of the FEA/MFEA internals. - Critical bug fix that affects recent NetBSD and OpenBSD releases. - Critical IPv6-related bug fix when adding unicast forwarding entries to the kernel. This bug was exposed only on *BSD systems with 64-bit CPU. - If MFEA is started, it will explicitly enable the multicast forwarding flags that have been added to recent OpenBSD releases: net.inet.ip.mforwarding (for OpenBSD-3.9 and later) and net.inet6.ip6.mforwarding (for OpenBSD-4.0 and later). RIB: - No significant changes. RIP/RIPng: - Addition of support for "show ripng" xorpsh operational commands. - Critical RIPng-related bug fix. Previously the RIPng installed routes had incorrect outgoing interface toward the destination. - Bug fix related to the TTL for RIPng multicast packets: now it is set to 255 as specified in the protocol specification (RFC 2080) instead of 1. OLSR: - Added support for RFC 3626 Optimized Link State Routing Protocol. This is a fully fledged XORP routing process with policy route redistribution capability. The work was generously funded over 2007/2008 by CenGen, Inc. OSPF: - Bug fix related to OSPFv3 link-local scope LSAs. Previously the link-local scope LSAs were incorrectly flooded to links other than the one they belonged to. - Bug fix related to OSPFv3 Inter-Area-Prefix-LSAs. The check for the minimum size of an Inter-Area-Prefix-LSA was incorrect so short prefixes such as the default route would be rejected. - Added a clear database command. - In the OSPFv2 configuration "passive" is no longer a bool but a directive. Previously setting an interface to passive would set the interface to loopback and announce a host route for the interface. Using the passive keyword will still set the interface to loopback but now the network will be announced. If the previous behaviour of of announcing the host route is required the host variable can be set to true. BGP: - Added support for 4-byte AS numbers, as detailed in RFC 4893. From 1st Jan 2009 4-byte AS numbers will be allocated by default by RIPE, so it is desirable that all BGP implementations support four-byte AS numbers by that time. Currently 4-byte support is not enabled in XORP by default, but can be enabled using the "enable-4byte-as-numbers" configuration option. STATIC_ROUTES: - Bug fix that prevented the deletion of interface-specific routes using xorpsh. MLD/IGMP: - No significant changes. MLD/IGMP-Lite: - An implementation of Lightweight IGMP/MLD is available in directory ${XORP}/contrib/mld6igmp_lite. It can be used instead of the existing MLD/IGMP vanilla implementation by using the following command before compiling XORP: ./configure --with-mld6igmp_lite PIM-SM: - No significant changes. FIB2MRIB: - No significant changes. CLI: - No significant changes. SNMP: - No significant changes. Release 1.4 (2007/03/20) ========================= ALL: - XORP now builds on DragonFlyBSD-1.8, FreeBSD-6.2, Linux Fedora Core6, Linux Debian-3.1 (sarge), NetBSD-3.1 and OpenBSD-4.0. - XORP now can be compiled with the Intel C/C++ compiler 9.* on Linux. - XORP now can be cross-compiled for IA-64, MIPS (Broadcom for Linksys WRT54G), PowerPC-603, Sparc64, and XScale processors. - Implementation of OSPFv3 (draft-ietf-ospf-ospfv3-update-23.txt). - Implementation of floating static routes (i.e., static routes for the same prefix with different next hop and metrics). CONFIGURATION: - Allow static routes to have "nexthop4" and "nexthop6" policy matching conditions in the "from" block. - Addition of new FEA configuration statements to retain XORP unicast forwarding entries on startup or shutdown: fea { unicast-forwarding4 { forwarding-entries { retain-on-startup: false retain-on-shutdown: false } } unicast-forwarding6 { forwarding-entries { retain-on-startup: false retain-on-shutdown: false } } } The default value for each statement is false. Note that those statements prevent the FEA itself from deleting the forwarding entries and does not prevent the RIB or any of the unicast routing protocols from deleting the entries on shutdown. - The "elements" policy statements for configuring sets of network routes have been deprecated: policy { network4-list foo { elements: "1.2.0.0/16,3.4.0.0/16" } network6-list bar { elements: "2222::/64,3333::/64" } } The new replacement statement is "network" and can be used to specify one element per line: policy { network4-list foo { network 1.2.0.0/16 network 3.4.0.0/16 } network6-list bar { network 2222::/64 network 3333::/64 } } - The following keywords are supported inside the policy configuration when comparing IPv4 or IPv6 network prefixes: exact, longer, orlonger, shorter, orshorter, not. For example: "network4 exact 10.0.0.0/8" SAME AS "network4 == 10.0.0.0/8" "network4 longer 10.0.0.0/8" SAME AS "network4 < 10.0.0.0/8" "network4 orlonger 10.0.0.0/8" SAME AS "network4 <= 10.0.0.0/8" "network4 shorter 10.0.0.0/8" SAME AS "network4 > 10.0.0.0/8" "network4 orshorter 10.0.0.0/8" SAME AS "network4 >= 10.0.0.0/8" "network4 not 10.0.0.0/8" SAME AS "network4 != 10.0.0.0/8" The original operators are supported as well. - A floating static route (also called "qualified" by some router vendors) can be added with a configuration like: protocols { static { route 10.10.0.0/16 { next-hop: 172.16.0.1 metric: 1 qualified-next-hop 172.17.0.2 { metric: 10 } } interface-route 10.30.30.0/24 { next-hop-interface: "rl0" next-hop-vif: "rl0" next-hop-router: 172.16.0.1 metric: 1 qualified-next-hop-interface rl1 { qualified-next-hop-vif rl1 { next-hop-router: 172.17.0.2 metric: 10 } } } } } LIBXORP: - The XORP scheduler now has support for priority-based tasks. LIBXIPC: - No significant changes. LIBFEACLIENT: - No significant changes. XRL: - No significant changes. RTRMGR: - Bug fix in the semantics of the rtrmgr template %activate keyword. XORPSH: - No significant changes. POLICY: - Bug fix related to creating export policies that match protocol's its own routes (e.g., a policy that modifies the BGP routes exported to its peers). - Various other bug fixes. FEA/MFEA: - Fix the routing socket based mechanism (used by BSD-derived systems) for obtaining the interface name (toward the destination) for a routing entry. - Apply a performance improvement when configuring a large number of interfaces/VIFs, each of them with the "default-system-config" configuration statement. - Bug fix related to atomically modifying the IP address of an interface. RIB: - Bug fix related to (not) installing redundant host-specific entries for the other side of a point-to-point interface if the netmask for the interface covers the host-specific entry. RIP/RIPng: - No significant changes. OSPF: - OSPFv3 is now available. - The OSPFv3 protocol requires that link-local addresses are used, therefore it is necessary to configure a link-local address for each interface, this restriction will be removed in the future. - The OSPFv3 configuration allows multiple instances to be configured however only one instance will be created. Configuring multiple OSPFv3 instances is guaranteed to cause problems. - Bug fix related to the processing of previously generated LSAs on startup has been fixed. Restarting a router that was the designated router could exhibit this problem. - Bug fix on a broadcast interface if the router was not the designated router then the nexthop was incorrectly unconditionally set to the designated router; introducing an unnecessary extra hop. BGP: - BGP has taken advantage of the priority-based tasks in the XORP scheduler and background tasks are run at a low priority; leading to improved performance. STATIC_ROUTES: - Bug fix related to declaring some of the policy matching conditions in the "from" block. MLD/IGMP: - Bug fix related to atomically modifying the IP address of an interface. - Bug fix related to ignoring protocol messages that are not recognized by the configured protocol version on an interface. - Ignore control messages if the source address is not directly connected. - Don't send the periodic Group-Specific or Group-and-Source-Specific Queries for entries that are in IGMPv1 mode. PIM-SM: - Bug fix related to atomically modifying the IP address of an interface. - The PIM-SM control messages do not include the IP Router Alert option anymore, because it has been included from the newer revisions of the PIM-SM protocol specification (RFC 4601 and draft-ietf-pim-sm-bsr-09.txt,.ps). - Don't send PIM Hello message with DR Priority of 0 when shutting down an interface, because this is not part of the protocol specification. FIB2MRIB: - Bug fix related to updating the interface and vif name of a forwarding entry received from the FEA. CLI: - Performance improvement if the CLI is processing a large amount of data. E.g., if xorpsh is used in a pipe like: cat commands.txt | xorpsh SNMP: - Bug fix with the snmpd arguments when sampling whether snmpd can start and its version is >= 5.2. Release 1.3 (2006/08/02) ========================= ALL: - Numerous improvements, bug fixes and cleanup. - XORP now builds on Linux Fedora Core5, DragonFlyBSD-1.4, FreeBSD-6.1. - Implementation of IGMPv3 (RFC 3376) and MLDv2 (RFC 3810). Those are necessary to complete the Source-Specific Multicast support. CONFIGURATION: - Addition of new OSPF configuration statement as part of the MD5 keys: * max-time-drift: u32 (default to 3600, i.e., 1 hour) It is used to set the maximum time drift (in seconds) among all OSPF routers. The allowed values are in the range [0--65535]. If the value is 65535, the time drift is unlimited. - The following statements for configuring static routes have been deprecated: route4, route6, interface-route4, interface-route6, mrib-route4, mrib-route6, mrib-interface-route4, mrib-interface-route6. The new replacement statements are: route, interface-route, mrib-route, mrib-interface-route. Each of the new statements can be used to configure either IPv4Net or IPv6Net route. - The following statements for configuring RIP and RIPng have been renamed: * route-expiry-secs -> route-timeout * route-deletion-secs -> deletion-delay * table-request-secs -> request-interval * interpacket-delay-msecs -> interpacket-delay - The following statements for configuring RIP and RIPng random intervals have been replaced: * triggered-update-min-secs and triggered-update-max-secs with triggered-delay and triggered-jitter * table-announce-min-secs and table-announce-max-secs with update-interval and update-jitter Previously, each interval was specified as [foo-min, foo-max]. Now each interval is specified as [foo - foo * jitter / 100, foo + foo * jitter / 100] where "jitter" is specified as a percentage (an integer in the interval [0, 100]) of the value of "foo". - The "version" statement for configuring an IGMP interface/vif allows values in the range [1-3]. Previously, the allowed range was [1-2]. - The "version" statement for configuring a MLD interface/vif allows values in the range [1-2]. Previously, the allowed range was [1-1]. - The following statement for configuring PIM-SM (pimsm4 and pimsm6) has been renamed: interval-sec -> interval - If a "then" policy block contains "accept" or "reject" statement, now all statements inside the "then" block are evaluated regardless of their position. - Addition of a new "exit" operational mode command that is equivalent to the "quit" operational mode command. - The "create" and "set" configuration commands are merged, so now the new "set" command can be used for setting values and for creating new configuration nodes. For backward compatibility, the obsoleted "create" command is preserved as an alias for the new "set" command, though it may be removed in the future. LIBXORP: - Few bug fixes in the RefTrie implementation. LIBXIPC: - Minor improvement in parsing XRL requests. LIBFEACLIENT: - No significant changes. XRL: - No significant changes. RTRMGR: - Various bug fixes. XORPSH: - Previously, the "commit" command was not available in configuration mode if there were no pending configuration changes. Now the "commit" command is always available, but the following message will be printed instead: "No configuration changes to commit." - Various bug fixes. POLICY: - Various bug fixes. FEA/MFEA: - Bug fix in transmitting large packets on Linux when using IP raw sockets. - Linux-related netlink socket code refactoring and bug fix. - Bug fix in obtaining the incoming interface for raw packets (in case of *BSD). - Bug fix in parsing the ancillary data from recvmsg(). - Accept zeroed source addresses of raw packets, because of protocols like IGMPv3. - Bug fix in restoring kernel routes that were automatically removed when the MAC address or MTU on an interface is modified. - Bug fix in processing IPv4 raw packets if they contain an IP option with a bogus option length. RIB: - Several bug fixes and improvements. RIP/RIPng: - Various bug fixes in the MD5 authentication support. - Remove route flap when applying/deleting RIP-related import policies. - Fix an issue with INFINITY cost routes that might be bounced indefinitely between two XORP routers. OSPF: - Various bug fixes in the MD5 authentication support. BGP: - Prefix limits on a per peer basis. - Various bug fixes. STATIC_ROUTES: - No significant changes. MLD/IGMP: - Implementation of IGMPv3 (RFC 3376) and MLDv2 (RFC 3810). - Unification of the IGMP and MLD execution path. PIM-SM: - Bug fix related to the SPT switch (the bug is *BSD specific). - Use the RPF interface toward the BSR when transmitting a Cand-RP Advertisement message. Previously the first interface that is UP was chosen. - Use the RPF interface toward the RP when transmitting PIM Register messages toward the RP. Previously the interface of the directly connected source was chosen. FIB2MRIB: - No significant changes. CLI: - Bug fix related to tracking the window size when it is resized. SNMP: - No significant changes. Release 1.2 (2006/03/08) ========================= ALL: - Numerous improvements, bug fixes and cleanup. - The third-party ospfd implementation is replaced with a new OSPF implementation developed from scratch. - The integration of the routing policy mechanism with the rest of the system is completed. - XORP now builds on Windows Server 2003 (Service Pack 1), amd64+FreeBSD-6.0, FreeBSD-6.1 (BETA3), NetBSD-3.0, OpenBSD-3.8, MacOS X 10.4.5, Linux Fedora Core4 (among others). CONFIGURATION: - Addition of new interface related configuration statement: * restore-original-config-on-shutdown: This optional statement is used to enable the restoring of the original network interface information on shutdown. - Addition of new PIM-SM related configuration statements: * enable-ip-router-alert-option-check: This optional statement is used to enable the IP Router Alert option check per virtual interface. * cand-bsr-by-vif-addr: and * cand-rp-by-vif-addr: Those optional statements are used together with cand-bsr-by-vif-name and cand-rp-by-vif-name respectively to specify the particular IP address on the configured vif. If they are omitted, a domain-wide address (if exists) that belongs to that interface is chosen by the router itself. * hello-period: This optional statement is used to configure the PIM Hello messages period (in seconds). * hello-triggered-delay: This optional statement is used to configure the randomized triggered delay of the PIM Hello messages (in seconds). - Addition of new MLD/IGMP related configuration statements: * version: This optional statement is used to configure the MLD/IGMP protocol version per virtual interface. * enable-ip-router-alert-option-check: This optional statement is used to enable the IP Router Alert option check per virtual interface. * query-interval: This optional statement is used to specify (per virtual interface) the interval between general queries. * query-last-member-interval: This optional statement is used to specify (per virtual interface) the maximum response time inserted into group-specific queries sent in response to leave group messages. * query-response-interval: This optional statement is used to specify (per virtual interface) the maximum response time inserted into the periodic general queries. * robust-count: This optional statement is used to specify (per virtual interface) the robustness variable count that allows tuning for the expected packet loss on a subnet. - Addition of support for user environmental variables CFLAGS_END and CXXFLAGS_END. Those variables can be used to specify the compiler flags (for the C and C++ compiler respectively) that must be after all other flags. LIBXORP: - Various improvements in the RunCommand implementation. LIBXIPC: - No significant changes. LIBFEACLIENT: - No significant changes. XRL: - No significant changes. RTRMGR: - Generalization of the %mandatory keyword syntax so now it can be used to specify any node or variable (multi-value nodes excluded) in the configuration tree. Previously it could be used to specify only configuration child nodes or variables. - Addition of support for read-only, permanent and user-hidden nodes (specified respectively by the new %read-only, %permanent and %user-hidden template commands). - Modification of the %allow and %allow-range semantics so a help string can be supplied for each allowed value or range. - Removal of the mechanism for specifying the hook for saving a configuration file (the "-s " command-line argument). The mechanism is broken and is superseded by the rtrmgr template support for running external programs. - Various other improvements and bug fixes. XORPSH: - Addition of support to run xorpsh in non-interactive mode. - Modification of the configurational mode "show" command so now it displays parameters only if their value is not same as the default value. - Addition of command "show -all" that shows the complete configuration including the parameters with default values. - Modification to the "show" command output: in configuration mode all deleted (and uncommitted) entries are prefixed with "-". - Modification of the default operational and configuration mode prompts to "user@hostname> " and "user@hostname# " respectively. - Addition of support to modify the operational and configuration mode prompts by environmental variables XORP_PROMPT_OPERATIONAL and XORP_PROMPT_CONFIGURATION respectively. - Addition of support for command-line completion for allowed values. - Various other improvements and bug fixes. POLICY: - Several bug fixes. FEA/MFEA: - Addition of RawSocket{4,6} generic abstraction that is not multicast-specific. RIB: - Addition of support for displaying the routing tables in brief, detailed and terse format. The default format is "brief". - Few bug fixes. RIP/RIPng: - The syntax for configuring the authentication mechanism has changed: OLD: authentication { type: "plaintext" password: "FOO" } OR authentication { type: "md5" password: "FOO" } NEW: authentication { simple-password: "FOO" } OR authentication { md5 1 { /* KeyID: [0, 255] */ password: "FOO" start-time: "YYYY-MM-DD.HH:MM" end-time: "YYYY-MM-DD.HH:MM" } } - Several bug fixes. OSPF: - Initial implementation of OSPF that replaces the third-party ospfd. BGP: - The network4/network6 directives have been deprecated. If you wish to inject static routes into BGP, you must now add these routes to the static_routes protocol block, and then configure the policy engine to redistribute them to BGP. - Proper support for policy filters. - Addition of support for route flap damping. - Addition of support for route aggregation. - Addition of support for route reflection. - Addition of support for confederations. - Bug fix to correctly handle connection collisions. - Addition of default support for NO_EXPORT, NO_ADVERTISE, and NO_EXPORT_SUBCONFED well-known communities. - Addition of the capability to reconfigure a peering (e.g., from IBGP to EBGP) which requires re-configuring the default filters. - Numerous bug fixes that should greatly improve stability. STATIC_ROUTES: - Several bug fixes. MLD/IGMP: - No significant changes. PIM-SM: - Updated to support the lastest PIM-SM specification (draft-ietf-pim-sm-v2-new-11.{ps,txt}). - Addition of support to disable the "wrong iif" kernel upcall on interfaces we don't need to monitor. - Bug fix related to the handling of the deleted MRIB entries. - Bug fix related to transmitting AssertCancel message when a PIM configured interface is gracefully shutdown. FIB2MRIB: - Several bug fixes. CLI: - Various improvements and bug fixes. SNMP: - No significant changes. Release 1.1 (2005/04/13) ========================= ALL: - Numerous improvements, bug fixes and cleanup. - XORP now builds on amd64+OpenBSD-3.6-current. - The --enable-advanced-mcast-api flag to "./configure" has been replaced with the --disable-advanced-multicast-api flag. - Addition of support for code execution profiling. - Currently "gmake" does not build the regression tests. The command "gmake check" should be used to build and run the regression tests. - Addition of two new documents: * "An Introduction to Writing a XORP Process" * "XORP User Manual" CONFIGURATION: - All "enabled: true/false" XORP configuration flags are now renamed to "disable: false/true". - The syntax for configuring the IPv4/IPv6 forwarding has changed: OLD: fea { enable-unicast-forwarding4: true enable-unicast-forwarding6: true } NEW: fea { unicast-forwarding4 { disable: false } unicast-forwarding6 { disable: false } } - The syntax for configuring the AFI/SAFI combinations in BGP has changed: OLD: bgp { peer { enable-ipv4-multicast enable-ipv6-unicast enable-ipv6-multicast } } NEW: bgp { peer { ipv4-unicast: true ipv4-multicast: true ipv6-unicast: true ipv6-multicast: true } } The new syntax allows IPv4 unicast to be disabled which was not previously possible. - The syntax for configuring the nexthop router address for static routes has changed: OLD: nexthop: NEW: next-hop: LIBXORP: - Bug fix in ordering events scheduled at exactly the same time and expiring at exactly the same time. - Various improvements to the eventloop implementation. - Addition of a mechanism for buffered asynchronous reads and writes. LIBXIPC: - Addition of XRL pipelining support. - The Finder client address can be defined by the following variable in the environment: XORP_FINDER_CLIENT_ADDRESS. The Finder server address and port can be defined by the following variables in the environment: XORP_FINDER_SERVER_ADDRESS and XORP_FINDER_SERVER_PORT respectively. Those changes re-enable communicating with remote XORP processes. - Various other improvements (including performance) and bug fixes. LIBFEACLIENT: - Few bug fixes. XRL: - No significant changes. RTRMGR: - Addition of a new rtrmgr template keyword: %deprecated: "Reason". This keyword can be used to deprecate old configuration statements. - Addition of a new rtrmgr keyword: %update. It is similar to %activate, and is called whenever the configuration in the subtree has changed. - Modification to the rtrmgr template semantics: the XRLs per template nodes are sent in the order those nodes are declared in the template files. Previously, the order was alphabetical (by the name of the template nodes). - Various other improvements and bug fixes. XORPSH: - Addition of a mechanism to track the status of the modules, and to provide operational commands for only those modules that are running. - Various other improvements and bug fixes. POLICY: - Initial implementation of a policy manager. It is still being tested, and should not be used. FEA/MFEA: - Implementation of Click FEA support. - Addition of support for discard interfaces and discard routes. - Addition of support for adding interface-specific routes. - Addition of support for ACLs, though currently there is no mechanism to configure them through the XORP configuration file. - Initial support for raw sockets. - Various bug fixes, improvements and cleanup. RIB: - Bug fix in adding point-to-point network interfaces. - Removal of the old mechanism (ExportTable) for propagating the routes to the FEA and all other interested parties. - Removal of hard-wired "static" table. - Various other improvements and bug fixes. RIP/RIPng: - MD5 authentication now works properly. Previously, it was generating the wrong signature. - Cisco compatibility bug fix. BGP: - Addition of support for creating IPv6 TCP connections. - Few bug fixes in the Multi-protocol support. - Major improvements to the flow control mechanism. - Various improvements and bug fixes. STATIC_ROUTES: - Addition of configuration support for interface-specific static routes. - Improvements in handling stored routes if they are affected by network interface information updates. - Addition of support for tracking the state of the relevant processes, and for graceful registering/deregistering with them. - Addition of support for better checking of the XRL error codes. - Few other improvements and bug fixes. MLD/IGMP: - Bug fix in updating the primary address of an interface. - Addition of support for tracking the state of the relevant processes, and for graceful registering/deregistering with them. - Addition of support for better checking of the XRL error codes. - Addition of support for better queueing of the outgoing XRLs so their ordering is preserved. - Few other improvements and bug fixes. PIM-SM: - Bug fixes in handling the MRIB entries and MRIB-related state. - Bug fix in scheduling the internal PimMre tasks. - Bug fix in updating the primary address of an interface. - Bug fix in the computation of the checksum of PIM Register packages received from a Cisco router that itself is not spec-compliant in the checksum computation. - Addition of support for tracking the state of the relevant processes, and for graceful registering/deregistering with them. - Addition of support for better checking of the XRL error codes. - Addition of support for better queueing of the outgoing XRLs so their ordering is preserved. - Various other bug fixes, improvements and cleanup. FIB2MRIB: - Bug fixes when adding and deleting the Fib2Mrib entries. - Improvements in handling stored routes if they are affected by network interface information updates. - Addition of support for tracking the state of the relevant processes, and for graceful registering/deregistering with them. - Addition of support for better checking of the XRL error codes. - Few other bug fixes and improvements. CLI: - Bug fix in auto-completion for sub-commands. - Few other bug fixes and improvements. SNMP: - No significant changes. Release 1.0 (2004/07/08) ========================= ALL: - All the routing processes can now be started and configured via the RTRMGR/XORPSH. LIBXORP: - Addition of support for safe callbacks (e.g., if an object is destroyed, all callbacks that refer to that object are invalidated). LIBXIPC: - Addition of support for event notification if the status of a target changes. LIBFEACLIENT: - Few bug fixes. XRL: - No significant changes. RTRMGR: - Addition of new command-line option "-v" to print verbose information. - Removal of command-line option "-d" that prints default information, because the same information is printed with the "-h" flag. - Addition of support for explicit configuration of the XRL target name of a module. - Addition of support for %help command in the rtrmgr template files. - Addition of support for new methods per module: "startup_method" and "shutdown_method". - Numerous other improvements and bug fixes. XORPSH: - Addition of new command-line option "-v" to print verbose information. - Removal of command-line option "-d" that prints default information, because the same information is printed with the "-h" flag. - Addition of support for help string in the xorpsh operational commands template files. - Addition of support for positional arguments in the xorpsh operational commands template files. - Addition of support to interrupt an operational command. Now if a command is interrupted from the command line by typing Ctrl-C, then the executed binary command itself (and its forked children, if any) is killed. - Numerous other improvements and bug fixes. FEA/MFEA: - Addition of support for propagating the Forwarding Information Base from the underlying system to clients interested in that information. - Addition of support for opening TCP or UDP sockets via the FEA. - Modification to the MFEA to use "libfeaclient" to obtain the interface information from the FEA. - Numerious bug fixes. RIB: - Addition of support for redistributing routes between two internal tables. - Addition of support for obtaining routes directly from some of the internal tables. - Modification to the RIB to use "libfeaclient" to obtain the interface information from the FEA. - Modification to the RIB to use the new RedistTable to propagate the final routes to the FEA and anyone else interested (e.g., PIM-SM). - Few bug fixes. RIP/RIPng: - Packet forwarding and reception via FEA written for RIPv2 and RIPng. RIPv2 should be usable. BGP: - IPv6 has now been tested with peerings to the 6Bone; unicast and multicast SAFIs. - Route origination is now possible from BGP. - The memory leaks from the previous release have been found and fixed. STATIC_ROUTES: - This is a new module for configuring static routes to the unicast or multicast RIB. MLD/IGMP: - During startup, a primary address is selected per configured interface, and this primary address should be the link-local unicast address of that interface. - New CLI commands: "show igmp interface address" and "show mld interface address" - Resend some of the XRLs (e.g., those who do not carry soft-state such as protocol control messages) if there is an error. - Few bug fixes. PIM-SM: - Updated to support the lastest PIM-SM specification (draft-ietf-pim-sm-v2-new-09.{ps,txt}). - Addition of support for "alternative subnet" configuration such that non-local senders appear as senders connected to the same subnet. It is needed as a work-around solution when there are uni-directional interfaces for sending and receiving traffic (e.g., satellite links). - During startup, a primary address and a domain-wide address are selected per configured interface. The primary address should be the link-local unicast address of that interface, and the domain-wide address should be a domain-wide reachable unicast address. - Resend some of the XRLs (e.g., those who do not carry soft-state such as protocol control messages) if there is an error. - Several bug fixes. FIB2MRIB: - This is a new module for propagating the unicast forwarding information obtained from the underlying system via the FEA to the multicast RIB. CLI: - Addition of support to propagate command interruption (e.g., Ctrl-C) from the CLI to the object that handles the command processing by calling a pre-defined callback. - During startup, if the input is a terminal (e.g., xorpsh), then read the terminal size instead of using the default values. - A bug fix related to the CLI paging output: now it can handle properly lines that are longer than the width of the CLI output terminal. - Several other bug fixes. SNMP: - No significant changes. Release 0.5 (2003/11/06) ======================== ALL: - New library libfeaclient to simplify interface configuration replication and event reception. LIBXORP: - Addition of ServiceBase class (service.hh) for asynchronous process components that might provide a service. The ServiceBase contains status information, (e.g. starting, running, shutting down, shutdown) and methods for triggering status changes (e.g.start, shutdown). It also provides an interface for observers to be notified of state changes. - Addition of ctype(3) wrappers that work properly even if the value of the int argument is not representable as an unsigned char and doesn't have the value of EOF. LIBXIPC: - Minor refactoring and code clean-up. - Fixes to XrlAtom binary marshalling methods and test code for checking this functionality in future. LIBFEACLIENT: - Added to project. Provides interface configuration tree mirroring and update event notification. Intended to unify how this data is replicated between processes. XRL: - kdoc generation nits. RTRMGR: - Fix the process name of a started proces to be the same as the binary name. - Minor code cleanup. XORPSH: - No significant changes. FEA/MFEA: - Added Linux Netlink support for writing network interface information, and routing entries to the kernel, and for observing the change of that information in the kernel. - Completed support for Linux /proc parsing to return network interface information. - Added support for NetBSD and OpenBSD to the unicast FEA. - Added compilation-time check whether the underlying system supports IPv6 multicast and IPv6 multicast routing, and isolate the compilation of all MFEA code that is specific to IPv6 multicast and IPv6 multicast routing. - Added support for run-time check whether the underlying system supports IPv4 or IPv6 multicast routing. - Various bug fixes and cleanup RIB: - No significant changes. BGP: - Supports multiprotocol IPv6. - The code for multicast SAFI is enabled but is untested. - MED processing is now deterministic. - A memory leak exists. MLD/IGMP: - No significant changes. PIM-SM: - A bug fix related to the removal of timed-out multicast forwarding entries. CLI: - Change slightly the command-line editing, so now Ctrl-W deletes the word before the cursor. Before, Ctrl-W would delete the whole line. - Apply a fix to libtecla in network mode such that keyboard-generated signals are not propagated to the process we have connected to. SNMP: - No significant changes. RIP/RIPng: - Code added to talk to FEA and RIB. To become functional it still requires the ability to send packets and receive UDP packets through the FEA (work in progress). Release 0.4 (2003/08/28) ======================== ALL: - Rename all process names from "foo" to "xorp_foo": bgp -> xorp_bgp fea -> xorp_fea fea_dummy -> xorp_fea_dummy finder -> xorp_finder ospfd -> xorp_ospf rib -> xorp_rib rtrmgr -> xorp_rtrmgr - Added support for "gmake install" that installs the required XORP pieces under /usr/local/xorp. Currently, the installed subdirectories and files follow the organization in the XORP source code tree. Only the following binaries are installed in subdirectory "bin": call_xrl, xorp_rtrmgr, xorpsh. - Removed old directory "mfea", because it is not needed anymore (all the MFEA code has been merged with the FEA). - The code does not compile anymore on MacOS X 10.2.x (or earlier), due to compiler issues. After Apple starts distributing a better compiler (probably with MacOS X 10.3.x?), then attempt will be made to keep the code compiling again on MacOS X. LIBXORP: - Added pre-order iterators for Trie and RefTrie. LIBXIPC: - Addition of virtual methods in XrlRouter to provide processes with an opportunity to detect finder connection, registration, and disconnection events. XRL: - Minor changes to clnt-gen that changes the names of some typedefs. RTRMGR: - Now all relative paths to templates, xrl files, configuration files, etc are computed relative to the root of the XORP tree. The root is computed in the following order: 1. The shell environment XORP_ROOT (if exists). 2. The parent directory the rtrmgr is run from (only if it contains the etc/templates and the xrl/targets directories). 3. The XORP_ROOT value as defined in config.h (currently this is the installation directory, and it defaults to /usr/local/xorp). XORPSH: - Now all relative paths to executable commands are computed relative to the root of the XORP tree. The root is computed similar to the rtrmgr root (see above), except that in step (2) we consider the parent directory the xorpsh is run instead. FEA/MFEA: - Bug fix: if the multicast protocol to start/stop is PIM, then start/stop PIM multicast routing in the kernel. - Bug fix (Linux-specific): if IGMP/MLD is enabled, then the multicast router will properly receive all IGMP/MLD messages. - Added support to enable/disable unicast forwarding in the kernel via the FEA. Currently, the support is only for FreeBSD, but is not used yet. Hence, the user must explicitly enable unicast forwarding before starting XORP. E.g., in case of FreeBSD run `sysctl net.inet.ip.forwarding=1` as root. In case of Linux run `echo 1 > /proc/sys/net/ipv4/ip_forward` as root. - Fix a compilation problem for NetBSD (courtesy Hitoshi Asaeda and Jun-ichiro itojun Hagino ). - Initial support for Linux /proc parsing to return network interface information (work in progress). - Bug fixes in setting the broadcast or p2p flags and addresses in the FEA internal interface tree (IfTree). - Bug fix in computing the minimum size of a message received on a routing socket. - Change the MFEA configuration scripts so now the IPv4/IPv6 setup is controlled by a single variable IP_VERSION that should be either IPV4 or IPV6. Note that those configuration scripts are temporary solution until the MFEA is integrated with the rtrmgr. RIB: - No significant changes. BGP: - Update packets with unknown path attributes are now correctly handled. MLD/IGMP: - Change the "RX" log messages to include the vif name a message was received on. - Initial support for returning the process status via get_status XRL. - Change the MLD/IGMP configuration scripts so now the IPv4/IPv6 setup is controlled by a single variable IP_VERSION that should be either IPV4 or IPV6. Note that those configuration scripts are temporary solution until the MLD/IGMP is integrated with the rtrmgr. PIM-SM: - Change the "RX" log messages to include the vif name a message was received on. - Change the PIM configuration scripts so now the IPv4/IPv6 setup is controlled by a single variable IP_VERSION that should be either IPV4 or IPV6. Note that those configuration scripts are temporary solution until the PIM is integrated with the rtrmgr. - Implement Join/Prune items fragmentation across Join/Prune messages when generating Join/Prune messages. - Fix some of the XRL names related to static RP configuration in the configuration shell scripts. - Fix the generation of Assert messages when data packets are received on the wrong interface; in addition, the Assert messages triggered by data packets are rate-limited to one Assert message/s (on average, per (S,G) or (*,G) routing entry). - Implement an optimization when generating Assert messages triggered by the data packets received on the wrong interface: suppress the second Assert message that is a duplicate. - Implement bandwidth-prorated SPT switch triggering: The SPT switch can be triggered at the last-hop router if the bandwidth from a given source is above a configured threshold. In addition, the same mechanism is implemented in the RP as well (not in the spec, where the SPT switch in the RP is always triggered by the first packet). - Keep various PIM-related statistics (e.g., number of sent or received PIM control messages per interface, etc), and add the appropriate XRL interface to get or reset those statistics. - Modify slightly the "show pim join" CLI output. E.g., print "Could assert WC:" for all entries, print "Could assert SG:" for (S,G,rpt) as well, etc. - Bug fix: when receiving IPv6 PIM packets, use the IPv6-specific pseudo-header to compute the checksum. CLI: - On exit, restore the original terminal flags in case of stdio-based CLI access. This should fix a bug when running xorpsh from sh/bash and pressing Ctrl-D leaves sh/bash in non-echo mode. - Reverse the key binding of 'j' and 'k' in page mode. Now the binding is same as in "vi/more/less": 'j' scroll down one line, while 'k' scroll up one line. SNMP: - Full implementation of BGP4-MIB module (RFC 1657) including traps. RIP/RIPng: - Implementation functionally operational save communication with the RIB for injecting routes, the FEA to send and receive packets, FEA interface monitoring code, and an XRL interface for configuration. Release 0.3 (2003/06/09) ======================== ALL: - Compilation fixes for GCC3.3 on FreeBSD 4.8R. - Cross directory build and gmake check support. - Purge of abort() calls. Replacement by XLOG_UNFINISHED, XLOG_UNREACHABLE and XLOG_ASSERT as appropriate, or in some cases better error handling code. - Change to autoconf-2.53 and automake-1.5 as standard autotool versions. The older autoconf-2.13 and automake-1.4 should still work. - Remove the multicast-specific timer implementation, and use instead the default Xorp timers (XorpTimer). LIBXORP: - Allocation without construction bug fixed in SelectorList code. Fixes potential for junk values and sanity checking when the number of active file descriptors is large. - Addition of status codes for XORP processes to report operational state (to the rtrmgr). - Addition of XLOG_UNFINISHED and XLOG_UNREACHABLE to xlog.h. - Explicitly disable copy constructor and assignment operator for EventLoop. - Replacement of traditional struct timeval with TimeVal class. TimeVal comes with constructors and numerous helper methods. - IPvXNet methods renaming: get_ipv4Net() -> get_ipv4net() get_ipv6Net() -> get_ipv6net() LIBXIPC: - Removal of 0.1 release Finder code and compatibility shims that allowed newer and older code to co-exist. Renaming of "FinderNG" classes and files to "Finder". - Use of class and instance names and Finder policy for having a primary instance in a class (first registered is primary, then second in case first exits/fails, and so forth). - Support for tunneling XRLs and XRL responses to and from the Finder. - Implementation event notification through the Finder using tunneled XRLs. XRL Targets can now request to be notified of the birth and death of other XRL Targets by the Finder. - Reduced number of XrlErrors to hopefully simplify handling errors. - Addition of new enumated type XrlErrorCode and change XrlError::error_code method to return this type. Allows compiler to detect unhandled error types in switch statements (if default case is not used). - Finder clients no longer reconnect to the Finder should they lose their connection per ${XORP}/docs/design_arch/error_handling.tex. - Increased Finder timeout for call_xrl. - Fix memory corruption following Finder transport error events. - Convert all code to use FinderServer class when they need to instantiate a Finder. - More consistent handling of defaults on send and receive sides of Finder. - Fix xrl_parser_input.cc handling of absolute file names. - Assorted minor clean-ups. XRL: - Addition of get_status and shutdown methods to the common interface. RTRMGR: - Workaround for yacc incompatibilities on newer Redhat releases. - Implement a new TaskManager, which handles actually doing the work during a commit, starting processes and validating that they start, dependency interlocking between processes, and shutting down processes that are no longer needed. - rtrmgr will now shutdown processes after they are not needed. - rtrmgr can now use the common XRL call get_status to monitor process state, and the common XRL shutdown to gracefully shutdown a process. Added new modinfo subcommands to specify when to use XRLs for these tasks. - Use system MD5 for authentication. - Better config file string parser. - Add tinderbox tests to cover some of the basic functionality. - Better error handling (more compliant with XORP Error Handling document). XORPSH: - Ctrl-C now terminates the current command, Ctrl-D exits xorpsh. FEA: - MFEA code merged with the unicast FEA, though logically the MFEA is still a separate entity. - Refactoring of FEA internals regarding reading/writing/observing interfaces-related information and forwarding-table related information. - Support for few more OS-specific methods for reading interface-related information or forwarding table related information. - Addition of IPv6 support (where missing) for reading/writing information regarding network interfaces or the unicast forwarding table. - Addition of a command-line option to ``fea'' and ``fea_dummy'' to specify the finder hostname and port number. - On startup and shutdown remove from the kernel all forwarding entries added by the fea. - Addition of XRLs to obtain information about all network interfaces (i.e., not only those that are configured by the rtrmgr). Should be used only for debugging purpose (though temporary they are used by the MFEA as well). - ``fea_rtsock'' deprecated (replaced with the generic ``fea''). RIB: - Export a RIB table to any target that registers interest. - Replace fea_fti/0.1 XRL interface with fti/0.2. The new interface introduces metric, admin_distance and protocol_origin when RIB information is propagated to the RIB clients. - Refactoring of the Vif manager implementation. - Support for returning the process status through the get_status XRL and shutting down using the shutdown XRL. BGP: - More robust to XRL communication failures. In particular support for retransmission. - Replace calls to abort() with more self-explanatory self-destruction. - Terminate interface removed and replaced with common shutdown XRL interface. - Use system md5 header files. - Peer list sorted by remote IP for snmp. - Uninitialised variable bug fix in NextHopRibRequest. - Initial support for returning the process status via get_status XRL. MFEA: - MFEA code merged with the unicast FEA, though logically the MFEA is still a separate entity. As part of the merging process, the MFEA now obtains the information about network interfaces and the unicast forwarding table from the FEA instead of the the kernel. - Addition of a check for Router Alert IP option. - Enable receiving of hop-by-hop options. - Replace all MLD6_XXX with MLD_XXX (following KAME's recommendation). - Compilation fixes for NetBSD-current (and probably NetBSD-1.6.1) (courtesy Hitoshi Asaeda ). - Initial support for returning the process status via get_status XRL. - Addition of a command-line option to ``test_mfea'' to specify the finder hostname and port number. - Misc. bug fixes, internal code refactoring, and cleanup. MLD/IGMP: - Protocol implementation testing completed. - Replace all MLD6_XXX with MLD_XXX (following KAME's recommendation). - Unify some of the MLD and IGMP protocol-specific code. - Support for configurable setting of the protocol version per vif. - Addition of a command-line option to ``test_mld6igmp'' to specify the finder hostname and port number. - Misc. bug fixes. PIM-SM: - Configuration-related fixes. - Rename RP-related configuration commands and XRLs: OLD: add_config_rp{4,6}, delete_config_rp{4,6}, config_rp_done NEW: add_config_static_rp{4,6}, delete_config_static_rp{4,6}, config_static_rp_done - Addition of XRL interface to receive MRIB-related updates from the RIB; the interface is not used yet. - Initial support for returning the process status via get_status XRL. - Addition of a mechanism that should hold-off PIM-SM status to PROC_NOT_READY until it has received all necessary confirmation from both MFEA and MLD/IGMP. - Addition of PIM configuration option and XRL interface to configure the SPT-switch bandwidth threshold. Currently, the SPT-switch is enabled only for the corner cases: switch on the first packet, or never switch. - Addition of a command-line option to ``test_pim'' to specify the finder hostname and port number. CLI: - Critical bug fix (could be triggered if a program like ${XORP}/pim/test_pim is run in background, and is accessed by the built-in CLI). - Addition of Ctrl-C handling: o If Ctrl-C is hit before , then cancel the current line and present a new prompt. o If Ctrl-C is hit while waiting for command completion, then cancel the wait and present a new prompt. - Removed the 5 seconds timer that would timeout the waiting for command completion (the user can use Ctrl-C to cancel the wait). - Enable receiving Ctrl-C if the input is stdio (e.g., in case of xorpsh). SNMP: - Integrated with Net-SNMP agent. - Implemented bgpVersion and bgpPeerTable from BGP4-MIB (RFC 1657). RIP/RIPng: - Implementation started. Release 0.2 (2003/03/10) ======================== ALL: - Source code compiles under GCC 3.2.1 and on Mac OS X 10.2.x (GCC 3.1) in addition to GCC 2.95.x and 2.96. LIBXORP: - Add libxorp/ref_trie.hh : Implementation of a trie to support route lookups. Based on trie.hh, but with reference-counted storage supporting delayed deletion. LIBXIPC: - Complete re-implementation of the XRL finder, so that registration/deregistration to the finder uses XRLs. RTRMGR: - Add operational-mode commands for monitoring BGP routes and peers, and VIF/interface state. - Re-implement how rtrmgr decides when to start processes. This fixes a few bugs in the dependency tracking - prior to this the RIB would not be started because it requires no direct configuration. FEA: - Bug fixes. RIB: - Ensure that the originating protocol is propagated throughout the RIB. - Fix bug where routes for directly connected interfaces were incorrectly being fed back to the FEA (which didn't expect to see add_route for these subnets). BGP: - XRL interfaces for monitoring BGP peers and the complete BGP routing table added. Helper programs bgp/tools/show_routes and bgp/tools/show_peers added, along with versions of these for running from xorpsh. - Support for adding/removing MEDs added. - BGP message handling and Path Attribute handling re-written to be much cleaner and more efficient. - Re-structured the BGP peer state machine to make the code easier to understand. - Change BGP to use reference-counted tries. This greatly simplifies the implementation of background tasks. - Correctly handle unrecognized optional transitive attributes. - Merge isolation tests into a single binary. - Added BGP/RIB/FEA integration tests. - Too many bug fixes to list. MFEA: - Minor bug fixes. MLD/IGMP: - No changes. PIM-SM: - PIM-SM Bootstrap mechanism reimplemented and tested. Note that the Bootstrap-related configuration commands in pim/configure_pim have changed slightly. - Detailed testing of the basic protocol and the Bootstrap mechanism completed and documented in the XORP PIM-SM Test Suite document. - Numerous bug fixes. Release 0.1 (2002/12/11) ======================== - First public release. - CVS repository opened to the world. xorp/build_rpms.bash0000775000076400007640000000235111636212020014654 0ustar greearbgreearb#!/bin/bash # Thanks to Achmad Basuki for details and initial spec files. # NOTE: You might need to edit ~/.rpmmacros to change the _topdir to be ~/rpmbuild # to have this script work correctly. set -x RPMDIR=~/rpmbuild TMPDIR=/tmp/xorp_ct_rpmdir VERSION=$(cat VERSION) mkdir -p $RPMDIR/SOURCES mkdir -p $RPMDIR/SPECS cp package_files/xorp.ct.spec $RPMDIR/SPECS/ cp package_files/xorp.sysconfig $RPMDIR/SOURCES cp package_files/xorp.redhat $RPMDIR/SOURCES/ cp package_files/xorp.logrotate $RPMDIR/SOURCES/ cp package_files/xorp.conf $RPMDIR/SOURCES/ # Create a clean repo. rm -fr $TMPDIR/xorp.ct.github mkdir -p $TMPDIR git clone ../ $TMPDIR/xorp.ct.github (cd $TMPDIR/xorp.ct.github; cp README xorp/; tar cfa $RPMDIR/SOURCES/xorp-$VERSION.tar.lzma xorp; cd -) # Build rpms rpmbuild -ba $RPMDIR/SPECS/xorp.ct.spec # building binary and the source #rpmbuild -bb $RPMDIR/SPECS/xorp.ct.spec # build just the binary #rpmbuild -bs $RPMDIR/SPECS/xorp.ct.spec # build just the source rpm # Files will be written something like this: #Wrote: /home/greearb/rpmbuild/SRPMS/xorp-1.8.3-1.fc13.src.rpm #Wrote: /home/greearb/rpmbuild/RPMS/i686/xorp-1.8.3-1.fc13.i686.rpm #Wrote: /home/greearb/rpmbuild/RPMS/i686/xorp-debuginfo-1.8.3-1.fc13.i686.rpm xorp/LICENSE.other0000664000076400007640000015246111540225520014012 0ustar greearbgreearb# # $XORP: xorp/LICENSE.other,v 1.6 2008/07/25 21:59:16 pavlin Exp $ # Portions of this software are derived from other sources. The files/directories with derived software and the corresponding copyright messages and licenses are listed below. ======================================================================== cli/libtecla/ : Copyright (c) 2000, 2001 by Martin C. Shepherd. All rights reserved. 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, including without limitation the rights to use, copy, modify, merge, publish, distribute, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, provided that the above copyright notice(s) and this permission notice appear in all copies of the Software and that both the above copyright notice(s) and this permission notice appear in supporting documentation. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. Except as contained in this notice, the name of a copyright holder shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization of the copyright holder. ======================================================================== cli/libtecla/config.guess : # Attempt to guess a canonical system name. # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, # 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA # 02110-1301, USA. # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # Originally written by Per Bothner . ======================================================================== cli/libtecla/config.sub : # Configuration validation subroutine script. # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, # 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. # This file is (in principle) common to ALL GNU software. # The presence of a machine in this file suggests that SOME GNU software # can handle that machine. It does not imply ALL GNU software can. # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA # 02110-1301, USA. # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. ======================================================================== cli/libtecla/configure : # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, # 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. ======================================================================== cli/libtecla/install-sh : # install - install a program, script, or datafile # This comes from X11R5 (mit/util/scripts/install.sh). # # Copyright 1991 by the Massachusetts Institute of Technology # # Permission to use, copy, modify, distribute, and sell this software and its # documentation for any purpose is hereby granted without fee, provided that # the above copyright notice appear in all copies and that both that # copyright notice and this permission notice appear in supporting # documentation, and that the name of M.I.T. not be used in advertising or # publicity pertaining to distribution of the software without specific, # written prior permission. M.I.T. makes no representations about the # suitability of this software for any purpose. It is provided "as is" # without express or implied warranty. ======================================================================== Makefile.in, MakefileRootCheck.in (in many directories) : # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, # 2003, 2004, 2005, 2006 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. ======================================================================== configure : # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, # 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. ======================================================================== config/compile : # Wrapper for compilers which do not understand `-c -o'. # Copyright 1999, 2000 Free Software Foundation, Inc. # Written by Tom Tromey . # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. ======================================================================== config/config.guess : # Attempt to guess a canonical system name. # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, # 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA # 02110-1301, USA. # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # Originally written by Per Bothner . ======================================================================== config/config.sub : # Configuration validation subroutine script. # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, # 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. # This file is (in principle) common to ALL GNU software. # The presence of a machine in this file suggests that SOME GNU software # can handle that machine. It does not imply ALL GNU software can. # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA # 02110-1301, USA. # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. ======================================================================== config/depcomp : # depcomp - compile a program generating dependencies as side-effects # Copyright 1999, 2000 Free Software Foundation, Inc. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA # 02111-1307, USA. # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # Originally written by Alexandre Oliva . ======================================================================== config/install-sh : # install - install a program, script, or datafile # This comes from X11R5 (mit/util/scripts/install.sh). # # Copyright 1991 by the Massachusetts Institute of Technology # # Permission to use, copy, modify, distribute, and sell this software and its # documentation for any purpose is hereby granted without fee, provided that # the above copyright notice appear in all copies and that both that # copyright notice and this permission notice appear in supporting # documentation, and that the name of M.I.T. not be used in advertising or # publicity pertaining to distribution of the software without specific, # written prior permission. M.I.T. makes no representations about the # suitability of this software for any purpose. It is provided "as is" # without express or implied warranty. ======================================================================== config/ltmain.sh : # ltmain.sh - Provide generalized library-building support services. # NOTE: Changing this file will not affect anything until you rerun configure. # # Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005 # Free Software Foundation, Inc. # Originally by Gordon Matzigkeit , 1996 # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. ======================================================================== config/missing : # Common stub for a few missing GNU programs while installing. # Copyright (C) 1996, 1997, 1999, 2000, 2002 Free Software Foundation, Inc. # Originally by Fran,cois Pinard , 1996. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA # 02111-1307, USA. # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. ======================================================================== contrib/mld6igmp_lite/mld6igmp_group_record.cc : contrib/mld6igmp_lite/mld6igmp_group_record.hh : contrib/mld6igmp_lite/mld6igmp_node_cli.cc : contrib/mld6igmp_lite/mld6igmp_vif.cc : // // The Lightweight IGMP/MLD modifications to this file are copyrighted by: // // Copyright (c) 2008 Huawei Technologies Co. Ltd // ======================================================================== contrib/win32/xorprtm/bsdroute.h : /*- * Copyright (c) 1980, 1986, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)route.h 8.4 (Berkeley) 1/9/95 * $FreeBSD: src/sys/net/route.h,v 1.63.2.1 2006/04/04 20:07:23 andre Exp $ */ ======================================================================== contrib/win32/xorprtm/defs.h : contrib/win32/xorprtm/list.h : contrib/win32/xorprtm/loadprotocol.c : contrib/win32/xorprtm/mibmgr.c : contrib/win32/xorprtm/mibmgr.h : contrib/win32/xorprtm/pchsample.h : contrib/win32/xorprtm/rmapi.c : contrib/win32/xorprtm/sync.c : contrib/win32/xorprtm/sync.h : contrib/win32/xorprtm/utils.c : contrib/win32/xorprtm/utils.h : /* * This file is derived from code which is under the following copyright: * * Copyright (c) 1999 - 2000 Microsoft Corporation. * */ ======================================================================== contrib/win32/xorprtm/print_rtmsg.c : /* * Copyright (c) 1983, 1989, 1991, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ ======================================================================== docs/papers/hotnets_2002_paper/sigcomm.cls : % % SIG-ALTERNATE DOCUMENT STYLE % G.K.M. Tobin August-October 1999 % adapted from ARTICLE document style by Ken Traub, Olin Shivers % also using elements of esub2acm.cls % HEAVILY MODIFIED, SUBSEQUENTLY, BY GERRY MURRAY 2000 % ARTICLE DOCUMENT STYLE -- Released 16 March 1988 % for LaTeX version 2.09 % Copyright (C) 1988 by Leslie Lamport ======================================================================== docs/papers/hotnets_2002_talk/elements.mp : % elements.mp -- MetaPost macros for drawing Click configuration graphs % Eddie Kohler % % Copyright (c) 1999-2001 Massachusetts Institute of Technology % Copyright (c) 2001-2008 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 Click 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 Click LICENSE file; the license in that file is % legally binding. ======================================================================== docs/papers/hotnets_2002_talk/elemfig.sty : % elemfig.sty -- LaTeX macros for Click configuration graphs in MetaPost % Eddie Kohler % % Copyright (c) 1999-2001 Massachusetts Institute of Technology % Copyright (c) 2001-2008 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 Click 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 Click LICENSE file; the license in that file is % legally binding. ======================================================================== docs/papers/hotnets_2002_talk/samp04_iprouter.mp : % samp04_iprouter.mp -- MetaPost graph examples: the glorious IP router % Eddie Kohler % % Copyright (c) 1999-2001 Massachusetts Institute of Technology % Copyright (c) 2001-2008 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 Click 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 Click LICENSE file; the license in that file is % legally binding. ======================================================================== docs/snmp/talks/PPRfyma.sty : % Prosper -- (PPRfyma.sty) Style file % A LaTeX class for creating slides % Author: Laurent Jacques % FYMA/UCL % % Copyright (c) 2002 Laurent Jacques % All rights reserved. % % Permission is hereby granted, without written agreement and without % license or royalty fees, to use, copy, modify, and distribute this % software and its documentation for any purpose, provided that the % above copyright notice and the following two paragraphs appear in % all copies of this software. % % IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, % SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF % THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE AUTHOR HAS BEEN ADVISED % OF THE POSSIBILITY OF SUCH DAMAGE. % % THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, % INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY % AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS % ON AN "AS IS" BASIS, AND THE AUTHOR HAS NO OBLIGATION TO % PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. % % CVSId : $Id$ ======================================================================== docs/user_manual/ : Copyright (c) 2004-2011 XORP, Inc and Others Copyright (c) 2004-2005 University College London 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, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. The names and trademarks of copyright holders may not be used in advertising or publicity pertaining to the software without specific prior permission. Title to copyright in this software and any associated documentation will at all times remain with the copyright holders. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ======================================================================== docs/user_manual/xorp_toc.sty : %% %% This is file `tocloft.sty', %% generated with the docstrip utility. %% %% The original source files were: %% %% tocloft.dtx (with options: `usc') %% %% Copyright 1998, 1999 Peter R. Wilson %% %% This program is provided under the terms of the %% LaTeX Project Public License distributed from CTAN %% archives in directory macros/latex/base/lppl.txt. %% %% Author: Peter Wilson (CUA and NIST) %% now at: peter.r.wilson@boeing.com %% ======================================================================== fea/ip.h : /* * ip.h * * Internet Protocol (RFC 791). * * Copyright (c) 2000 Dug Song * * $Id$ */ ======================================================================== fea/data_plane/control_socket/windows_routing_socket.h : /*- * Copyright (c) 1980, 1986, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)route.h 8.4 (Berkeley) 1/9/95 * $FreeBSD: src/sys/net/route.h,v 1.63.2.1 2006/04/04 20:07:23 andre Exp $ */ ======================================================================== libcomm/ : /* * Copyright (c) 2001 * YOID Project. * University of Southern California/Information Sciences Institute. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ ======================================================================== libxipc/hmac_md5.c : libxipc/hmac_md5.h : /* * FILE: hmac.c * AUTHORS: Colin Perkins * * HMAC message authentication (RFC2104) * * Copyright (c) 1998-2000 University College London * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, is permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the Computer Science * Department at University College London * 4. Neither the name of the University nor of the Department may be used * to endorse or promote products derived from this software without * specific prior written permission. * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ ======================================================================== libxorp/strlcpy.c : /* $OpenBSD: strlcpy.c,v 1.10 2005/08/08 08:05:37 espie Exp $ */ /* * Copyright (c) 1998 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ ======================================================================== libxorp/ether_compat.c : /* * Copyright (c) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that: (1) source code distributions * retain the above copyright notice and this paragraph in its entirety, (2) * distributions including binary code include the above copyright notice and * this paragraph in its entirety in the documentation or other materials * provided with the distribution, and (3) all advertising materials mentioning * features or use of this software display the following acknowledgement: * ``This product includes software developed by the University of California, * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of * the University nor the names of its contributors may be used to endorse * or promote products derived from this software without specific prior * written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * Internet, ethernet, port, and protocol string to address * and address to string conversion routines * * $FreeBSD: src/contrib/tcpdump/addrtoname.c,v 1.12 2004/03/31 14:57:24 bms Exp $ */ /* * Copyright (c) 1995 * Bill Paul . All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * ethernet address conversion and lookup routines * * Written by Bill Paul * Center for Telecommunications Research * Columbia University, New York City */ #if 0 __FBSDID("$FreeBSD: /repoman/r/ncvs/src/lib/libc/net/ether_addr.c,v 1.15 2002/04/08 07:51:10 ru Exp $"); #endif ======================================================================== libxorp/getopt.c : /* $NetBSD: getopt.c,v 1.26 2003/08/07 16:43:40 agc Exp $ */ /* * Copyright (c) 1987, 1993, 1994 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * __FBSDID("$FreeBSD: src/lib/libc/stdlib/getopt.c, 2004/03/06 17:05:45 ache Exp $"); */ ======================================================================== libxorp/inet_ntop.c : /* $OpenBSD: inet_ntop.c, 2005/08/06 20:30:03 espie Exp $ */ /* Copyright (c) 1996 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ ======================================================================== libxorp/popen.cc : /* * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software written by Ken Arnold and * published in UNIX Review, Vol. 6, No. 8. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD: src/lib/libc/gen/popen.c,v 1.14 2000/01/27 23:06:19 jasone Exp $ */ ======================================================================== libxorp/random.c : /* * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ ======================================================================== libxorp/timer.cc : // Copyright (c) 1999-2000 Massachusetts Institute of Technology // // 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 Click 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 Click LICENSE file; the license in that file is // legally binding. ======================================================================== libxorp/strptime.c : /* $NetBSD: localedef.h, 2005/11/29 03:12:16 christos Exp $ */ /* * Copyright (c) 1994 Winning Strategies, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Winning Strategies, Inc. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ ======================================================================== mibs/configure : # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, # 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. ======================================================================== mrt/include/netinet/pim_var.h : /* * Copyright (c) 1998-2000 * University of Southern California/Information Sciences Institute. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ ======================================================================== mrt/include/netinet/pim.h : /* * Copyright (c) 1996-2000 * University of Southern California/Information Sciences Institute. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ ======================================================================== rtrmgr/glob_win32.c : /* * File: glob.c * * Purpose: Definition of the glob() API functions for the Win32 platform. * * Created 13th November 2002 * Updated: 17th February 2005 * * Home: http://synesis.com.au/software/ * * Copyright 2002-2005, Matthew Wilson and Synesis Software * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the names of Matthew Wilson and Synesis Software nor the names of * any contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * */ ======================================================================== rtrmgr/glob_win32.h : /* * File: glob.h * * Purpose: Declaration of the glob() API functions and types for the * Win32 platform. * * Created 13th November 2002 * Updated: 10th January 2005 * * Home: http://synesis.com.au/software/ * * Copyright 2002-2005, Matthew Wilson and Synesis Software * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the names of Matthew Wilson and Synesis Software nor the names of * any contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * */ ======================================================================== xorp/Makefile.deb0000644000076400007640000000550711703344153014057 0ustar greearbgreearb # Example custom makefile for creating .deb without using dpkg tools. # # Author: Tim Wegener # # This requires deb_hand.mak # Sources: # SOURCE_DIR - directory containing files to be packaged # ICON_SOURCE - 26x26 icon file for maemo # description.txt - description with summary on first line # preinst, postinst, prerm, postrm - optional control shell scripts # usage: # sudo make PACKAGE=lanforge-xorp -f Makefile.deb create_deb # These fields are used to build the control file. PACKAGE ?= lanforge-xorp VERSION = 1.8.5 LFVER = 5.2.2 ARCH = i386 SECTION = net PRIORITY = optional MAINTAINER = Ben Greear DEPENDS = libpcap0.8,libgcrypt11 #ICON_SOURCE = PACKAGE_DIR ?= tmp create_deb: echo "#!/bin/bash" > preinst echo "set -x" >> preinst echo "adduser xorp || true" >> preinst echo "adduser xorp xorp || true" >> preinst # I compile xorp on FC5, and it has a hard dependency on libpcap.0.9.4 and libcrypto.so.6 # Fake out the link here so that it will start OK. This works on Ubuntu 8.0.4, no idea # if it will work elsewhere. --Ben #echo "if [ ! -f /usr/lib/libpcap.so.0.9.4 ]; then ln -s /usr/lib/libpcap.so.0.9.* /usr/lib/libpcap.so.0.9.4; fi" >> preinst # For Ubuntu 8.10, and compiling on FC8, we need a slightly different hack. echo "if [ ! -f /usr/lib/libpcap.so.0.9 ]; then ln -s /usr/lib/libpcap.so.0.9.8 /usr/lib/libpcap.so.0.9; fi" >> preinst echo "if [ ! -f /lib/libtinfo.so.5 ]; then ln -s /lib/libncurses.so.5 /lib/libtinfo.so.5; fi" >> preinst echo "if [ ! -f /usr/lib/libcrypto.so.6 ]; then ln -s /usr/lib/libcrypto.so.0.9.* /usr/lib/libcrypto.so.6; fi" >> preinst echo "#!/bin/bash" > postinst echo "echo \"Xorp is installed in /usr/local/xorp.\"" >> postinst echo "#!/bin/bash" > prerm echo "# nothing to do here." >> prerm echo "XORP modular router with improvements to better support LANforge." > description.txt echo "More information: http://www.xorp.org" >> description.txt chmod a+x preinst prerm postinst make -f Makefile.deb clobber make -f Makefile.deb ARCH=i386 deb mv tmp/$(PACKAGE)_$(VERSION)_i386.deb ./ cp $(PACKAGE)_$(VERSION)_i386.deb /mnt/d2/pub/candela_cdrom.${LFVER}/debs/dists/candela/multiverse/binary-i386/ @echo "" @echo "On a Ubunto machine:" @echo "cd /mnt/d2/pub/candela_cdrom.${LFVER}/debs" @echo "dpkg-scanpackages . /dev/null | gzip -9c > dists/candela/multiverse/binary-i386/Packages.gz" @echo "apt-get update" @echo "apt-get install $(PACKAGE)" # This assumes a 'make' has already happened, and that make install will deposit files in /usr/local/xorp # This most likely will have to be run as root user. ${PACKAGE_DIR}/data: rm -rf $@ mkdir -p $@/usr/local/ (make install; cd /usr/local; find xorp -name "*" -print|xargs strip; \ tar -cvzf /tmp/xorp.tgz xorp) (cd $@/usr/local; tar -xvzf /tmp/xorp.tgz) include deb_hand.mak xorp/pim/0000775000076400007640000000000011703345405012447 5ustar greearbgreearbxorp/pim/pim_proto_assert.hh0000664000076400007640000000431311421137511016354 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/pim/pim_proto_assert.hh,v 1.12 2008/10/02 21:57:54 bms Exp $ #ifndef __PIM_PIM_PROTO_ASSERT_HH__ #define __PIM_PIM_PROTO_ASSERT_HH__ // // PIM Assert related definitions. // #include "libxorp/ipvx.hh" // // Constants definitions // // // Structures/classes, typedefs and macros // // Assert-related metric class AssertMetric { public: AssertMetric(const IPvX& addr) : _addr(addr) {} bool rpt_bit_flag() const { return (_rpt_bit_flag); } void set_rpt_bit_flag(bool v) { _rpt_bit_flag = v; } uint32_t metric_preference() const { return (_metric_preference); } void set_metric_preference(uint32_t v) { _metric_preference = v; } uint32_t metric() const { return (_metric); } void set_metric(uint32_t v) { _metric = v; } const IPvX& addr() const { return (_addr); } void set_addr(const IPvX& v) { _addr = v; } bool operator>(const AssertMetric& other) const; bool is_assert_cancel_metric() const; private: bool _rpt_bit_flag; // The SPT/RPT bit uint32_t _metric_preference; // The metric preference uint32_t _metric; // The metric IPvX _addr; // The address of the Assert origin }; enum assert_state_t { ASSERT_STATE_NOINFO = 0, ASSERT_STATE_WINNER, ASSERT_STATE_LOSER }; // // Global variables // // // Global functions prototypes // #endif // __PIM_PIM_PROTO_ASSERT_HH__ xorp/pim/pim_node_cli.cc0000664000076400007640000013706711421137511015407 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // PIM protocol CLI implementation // #include "pim_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvx.hh" #include "pim_mfc.hh" #include "pim_mre.hh" #include "pim_node.hh" #include "pim_node_cli.hh" #include "pim_vif.hh" // // Exported variables // // // Local constants definitions // // // Local structures/classes, typedefs and macros // // // Local variables // // // Local functions prototypes // /** * PimNodeCli::PimNodeCli: * @pim_node: The PIM node to use. * * PimNodeCli constructor. **/ PimNodeCli::PimNodeCli(PimNode& pim_node) : ProtoNodeCli(pim_node.family(), pim_node.module_id()), _pim_node(pim_node) { } PimNodeCli::~PimNodeCli() { stop(); } int PimNodeCli::start() { if (! is_enabled()) return (XORP_OK); if (is_up() || is_pending_up()) return (XORP_OK); if (ProtoUnit::start() != XORP_OK) return (XORP_ERROR); if (add_all_cli_commands() != XORP_OK) return (XORP_ERROR); XLOG_INFO("CLI started"); return (XORP_OK); } int PimNodeCli::stop() { int ret_code = XORP_OK; if (is_down()) return (XORP_OK); if (ProtoUnit::stop() != XORP_OK) return (XORP_ERROR); if (delete_all_cli_commands() != XORP_OK) ret_code = XORP_ERROR; XLOG_INFO("CLI stopped"); return (ret_code); } /** * Enable the node operation. * * If an unit is not enabled, it cannot be start, or pending-start. */ void PimNodeCli::enable() { ProtoUnit::enable(); XLOG_INFO("CLI 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 PimNodeCli::disable() { stop(); ProtoUnit::disable(); XLOG_INFO("CLI disabled"); } int PimNodeCli::add_all_cli_commands() { // XXX: command "show" must have been installed by the CLI itself. if (pim_node().is_ipv4()) { add_cli_dir_command("show pim", "Display information about IPv4 PIM"); add_cli_command("show pim bootstrap", "Display information about PIM IPv4 bootstrap routers", callback(this, &PimNodeCli::cli_show_pim_bootstrap)); add_cli_command("show pim bootstrap rps", "Display information about PIM IPv4 bootstrap RPs", callback(this, &PimNodeCli::cli_show_pim_bootstrap_rps)); add_cli_command("show pim interface", "Display information about PIM IPv4 interfaces", callback(this, &PimNodeCli::cli_show_pim_interface)); add_cli_command("show pim interface address", "Display information about addresses of PIM IPv4 interfaces", callback(this, &PimNodeCli::cli_show_pim_interface_address)); add_cli_command("show pim join", "Display information about PIM IPv4 groups", callback(this, &PimNodeCli::cli_show_pim_join)); add_cli_command("show pim join all", "Display information about all PIM IPv4 groups", callback(this, &PimNodeCli::cli_show_pim_join_all)); add_cli_command("show pim mfc", "Display information about PIM Multicast Forwarding Cache", callback(this, &PimNodeCli::cli_show_pim_mfc)); add_cli_command("show pim neighbors", "Display information about PIM IPv4 neighbors", callback(this, &PimNodeCli::cli_show_pim_neighbors)); add_cli_command("show pim mrib", "Display MRIB IPv4 information inside PIM", callback(this, &PimNodeCli::cli_show_pim_mrib)); add_cli_command("show pim rps", "Display information about PIM IPv4 RPs", callback(this, &PimNodeCli::cli_show_pim_rps)); add_cli_command("show pim scope", "Display information about PIM IPv4 scope zones", callback(this, &PimNodeCli::cli_show_pim_scope)); } if (pim_node().is_ipv6()) { add_cli_dir_command("show pim6", "Display information about IPv6 PIM"); add_cli_command("show pim6 bootstrap", "Display information about PIM IPv6 bootstrap routers", callback(this, &PimNodeCli::cli_show_pim_bootstrap)); add_cli_command("show pim6 bootstrap rps", "Display information about PIM IPv6 bootstrap RPs", callback(this, &PimNodeCli::cli_show_pim_bootstrap_rps)); add_cli_command("show pim6 interface", "Display information about PIM IPv6 interfaces", callback(this, &PimNodeCli::cli_show_pim_interface)); add_cli_command("show pim6 interface address", "Display information about addresses of PIM IPv6 interfaces", callback(this, &PimNodeCli::cli_show_pim_interface_address)); add_cli_command("show pim6 join", "Display information about PIM IPv6 groups", callback(this, &PimNodeCli::cli_show_pim_join)); add_cli_command("show pim6 join all", "Display information about all PIM IPv6 groups", callback(this, &PimNodeCli::cli_show_pim_join_all)); add_cli_command("show pim6 mfc", "Display information about PIM Multicast Forwarding Cache", callback(this, &PimNodeCli::cli_show_pim_mfc)); add_cli_command("show pim6 neighbors", "Display information about PIM IPv6 neighbors", callback(this, &PimNodeCli::cli_show_pim_neighbors)); add_cli_command("show pim6 mrib", "Display MRIB IPv6 information inside PIM", callback(this, &PimNodeCli::cli_show_pim_mrib)); add_cli_command("show pim6 rps", "Display information about PIM IPv6 RPs", callback(this, &PimNodeCli::cli_show_pim_rps)); add_cli_command("show pim6 scope", "Display information about PIM IPv6 scope zones", callback(this, &PimNodeCli::cli_show_pim_scope)); } return (XORP_OK); } // // CLI COMMAND: "show pim bootstrap [scope-zone-group-prefix [scoped]]" // CLI COMMAND: "show pim6 bootstrap [scope-zone-group-prefix [scoped]]" // // Display information about PIM bootstrap routers // int PimNodeCli::cli_show_pim_bootstrap(const vector& argv) { PimScopeZoneId zone_id(IPvXNet::ip_multicast_base_prefix(family()), false); bool is_zone_id_set = false; list::const_iterator zone_iter; // Check the optional arguments if (argv.size()) { try { zone_id = PimScopeZoneId(argv[0].c_str(), false); is_zone_id_set = true; if (zone_id.scope_zone_prefix().masked_addr().af() != family()) { cli_print(c_format("ERROR: Address with invalid address family: %s\n", argv[0].c_str())); return (XORP_ERROR); } // Test if the second argument specifies scoped zone if (argv.size() >= 2) { if (argv[1] == "scoped") zone_id = PimScopeZoneId(argv[0].c_str(), true); } } catch (InvalidString) { cli_print(c_format("ERROR: Invalid zone ID address: %s\n", argv[0].c_str())); return (XORP_ERROR); } catch (InvalidNetmaskLength) { cli_print(c_format("ERROR: Invalid zone ID netmask length: %s\n", argv[0].c_str())); return (XORP_ERROR); } } cli_print("Active zones:\n"); cli_print(c_format("%-15s %3s %-15s %3s %-15s %7s %9s\n", "BSR", "Pri", "LocalAddress", "Pri", "State", "Timeout", "SZTimeout")); for (zone_iter = pim_node().pim_bsr().active_bsr_zone_list().begin(); zone_iter != pim_node().pim_bsr().active_bsr_zone_list().end(); ++zone_iter) { BsrZone *bsr_zone = *zone_iter; if (is_zone_id_set && (bsr_zone->zone_id() != zone_id)) continue; string zone_state_string = "Unknown"; switch (bsr_zone->bsr_zone_state()) { case BsrZone::STATE_INIT: zone_state_string = "Init"; break; case BsrZone::STATE_CANDIDATE_BSR: zone_state_string = "Candidate"; break; case BsrZone::STATE_PENDING_BSR: zone_state_string = "Pending"; break; case BsrZone::STATE_ELECTED_BSR: zone_state_string = "Elected"; break; case BsrZone::STATE_NO_INFO: zone_state_string = "NoInfo"; break; case BsrZone::STATE_ACCEPT_ANY: zone_state_string = "AcceptAny"; break; case BsrZone::STATE_ACCEPT_PREFERRED: zone_state_string = "AcceptPreferred"; break; default: zone_state_string = "InvalidState"; XLOG_UNREACHABLE(); return (XORP_ERROR); break; } int scope_zone_left_sec = -1; if (bsr_zone->const_scope_zone_expiry_timer().scheduled()) { TimeVal tv_left; bsr_zone->const_scope_zone_expiry_timer().time_remaining(tv_left); scope_zone_left_sec = tv_left.sec(); } int bsr_zone_left_sec = -1; if (bsr_zone->const_bsr_timer().scheduled()) { TimeVal tv_left; bsr_zone->const_bsr_timer().time_remaining(tv_left); bsr_zone_left_sec = tv_left.sec(); } cli_print(c_format("%-15s %3u %-15s %3u %-15s %7d %9d\n", cstring(bsr_zone->bsr_addr()), XORP_UINT_CAST(bsr_zone->bsr_priority()), cstring(bsr_zone->my_bsr_addr()), XORP_UINT_CAST(bsr_zone->my_bsr_priority()), zone_state_string.c_str(), bsr_zone_left_sec, scope_zone_left_sec)); } cli_print("Expiring zones:\n"); cli_print(c_format("%-15s %3s %-15s %3s %-15s %7s %9s\n", "BSR", "Pri", "LocalAddress", "Pri", "State", "Timeout", "SZTimeout")); for (zone_iter = pim_node().pim_bsr().expire_bsr_zone_list().begin(); zone_iter != pim_node().pim_bsr().expire_bsr_zone_list().end(); ++zone_iter) { BsrZone *bsr_zone = *zone_iter; if (is_zone_id_set && (bsr_zone->zone_id() != zone_id)) continue; string zone_state_string = "Unknown"; switch (bsr_zone->bsr_zone_state()) { case BsrZone::STATE_INIT: zone_state_string = "Init"; break; case BsrZone::STATE_CANDIDATE_BSR: zone_state_string = "Candidate"; break; case BsrZone::STATE_PENDING_BSR: zone_state_string = "Pending"; break; case BsrZone::STATE_ELECTED_BSR: zone_state_string = "Elected"; break; case BsrZone::STATE_NO_INFO: zone_state_string = "NoInfo"; break; case BsrZone::STATE_ACCEPT_ANY: zone_state_string = "AcceptAny"; break; case BsrZone::STATE_ACCEPT_PREFERRED: zone_state_string = "AcceptPreferred"; break; default: zone_state_string = "InvalidState"; XLOG_UNREACHABLE(); return (XORP_ERROR); break; } int scope_zone_left_sec = -1; if (bsr_zone->const_scope_zone_expiry_timer().scheduled()) { TimeVal tv_left; bsr_zone->const_scope_zone_expiry_timer().time_remaining(tv_left); scope_zone_left_sec = tv_left.sec(); } int bsr_zone_left_sec = -1; if (bsr_zone->const_bsr_timer().scheduled()) { TimeVal tv_left; bsr_zone->const_bsr_timer().time_remaining(tv_left); bsr_zone_left_sec = tv_left.sec(); } cli_print(c_format("%-15s %3u %-15s %3u %-15s %7d %9d\n", cstring(bsr_zone->bsr_addr()), XORP_UINT_CAST(bsr_zone->bsr_priority()), cstring(bsr_zone->my_bsr_addr()), XORP_UINT_CAST(bsr_zone->my_bsr_priority()), zone_state_string.c_str(), bsr_zone_left_sec, scope_zone_left_sec)); } cli_print("Configured zones:\n"); cli_print(c_format("%-15s %3s %-15s %3s %-15s %7s %9s\n", "BSR", "Pri", "LocalAddress", "Pri", "State", "Timeout", "SZTimeout")); for (zone_iter = pim_node().pim_bsr().config_bsr_zone_list().begin(); zone_iter != pim_node().pim_bsr().config_bsr_zone_list().end(); ++zone_iter) { BsrZone *bsr_zone = *zone_iter; if (is_zone_id_set && (bsr_zone->zone_id() != zone_id)) continue; string zone_state_string = "Unknown"; switch (bsr_zone->bsr_zone_state()) { case BsrZone::STATE_INIT: zone_state_string = "Init"; break; case BsrZone::STATE_CANDIDATE_BSR: zone_state_string = "Candidate"; break; case BsrZone::STATE_PENDING_BSR: zone_state_string = "Pending"; break; case BsrZone::STATE_ELECTED_BSR: zone_state_string = "Elected"; break; case BsrZone::STATE_NO_INFO: zone_state_string = "NoInfo"; break; case BsrZone::STATE_ACCEPT_ANY: zone_state_string = "AcceptAny"; break; case BsrZone::STATE_ACCEPT_PREFERRED: zone_state_string = "AcceptPreferred"; break; default: zone_state_string = "InvalidState"; XLOG_UNREACHABLE(); return (XORP_ERROR); break; } cli_print(c_format("%-15s %3u %-15s %3u %-15s %7d %9d\n", cstring(bsr_zone->bsr_addr()), XORP_UINT_CAST(bsr_zone->bsr_priority()), cstring(bsr_zone->my_bsr_addr()), XORP_UINT_CAST(bsr_zone->my_bsr_priority()), zone_state_string.c_str(), -1, -1)); } return (XORP_OK); } // // CLI COMMAND: "show pim bootstrap rps [scope-zone-group-prefix [scoped]]" // CLI COMMAND: "show pim6 bootstrap rps [scope-zone-group-prefix [scoped]]" // // Display information about PIM bootstrap RPs // int PimNodeCli::cli_show_pim_bootstrap_rps(const vector& argv) { PimScopeZoneId zone_id(IPvXNet::ip_multicast_base_prefix(family()), false); bool is_zone_id_set = false; list::const_iterator zone_iter; // Check the optional argument if (argv.size()) { try { zone_id = PimScopeZoneId(argv[0].c_str(), false); is_zone_id_set = true; if (zone_id.scope_zone_prefix().masked_addr().af() != family()) { cli_print(c_format("ERROR: Address with invalid address family: %s\n", argv[0].c_str())); return (XORP_ERROR); } // Test if the second argument specifies scoped zone if (argv.size() >= 2) { if (argv[1] == "scoped") zone_id = PimScopeZoneId(argv[0].c_str(), true); } } catch (InvalidString) { cli_print(c_format("ERROR: Invalid zone ID address: %s\n", argv[0].c_str())); return (XORP_ERROR); } catch (InvalidNetmaskLength) { cli_print(c_format("ERROR: Invalid zone ID netmask length: %s\n", argv[0].c_str())); return (XORP_ERROR); } } cli_print("Active RPs:\n"); cli_print(c_format("%-15s %3s %7s %-18s %-15s %16s\n", "RP", "Pri", "Timeout", "GroupPrefix", "BSR", "CandRpAdvTimeout")); for (zone_iter = pim_node().pim_bsr().active_bsr_zone_list().begin(); zone_iter != pim_node().pim_bsr().active_bsr_zone_list().end(); ++zone_iter) { BsrZone *bsr_zone = *zone_iter; if (is_zone_id_set && (bsr_zone->zone_id() != zone_id)) continue; list::const_iterator group_prefix_iter; for (group_prefix_iter = bsr_zone->bsr_group_prefix_list().begin(); group_prefix_iter != bsr_zone->bsr_group_prefix_list().end(); ++group_prefix_iter) { BsrGroupPrefix *bsr_group_prefix = *group_prefix_iter; list::const_iterator rp_iter; for (rp_iter = bsr_group_prefix->rp_list().begin(); rp_iter != bsr_group_prefix->rp_list().end(); ++rp_iter) { BsrRp *bsr_rp = *rp_iter; int left_sec = -1; if (bsr_rp->const_candidate_rp_expiry_timer().scheduled()) { TimeVal tv_left; bsr_rp->const_candidate_rp_expiry_timer().time_remaining(tv_left); left_sec = tv_left.sec(); } cli_print(c_format("%-15s %3u %7d %-18s %-15s %16d\n", cstring(bsr_rp->rp_addr()), XORP_UINT_CAST(bsr_rp->rp_priority()), left_sec, cstring(bsr_group_prefix->group_prefix()), cstring(bsr_zone->bsr_addr()), -1)); } } } cli_print("Expiring RPs:\n"); cli_print(c_format("%-15s %3s %7s %-18s %-15s %16s\n", "RP", "Pri", "Timeout", "GroupPrefix", "BSR", "CandRpAdvTimeout")); for (zone_iter = pim_node().pim_bsr().expire_bsr_zone_list().begin(); zone_iter != pim_node().pim_bsr().expire_bsr_zone_list().end(); ++zone_iter) { BsrZone *bsr_zone = *zone_iter; if (is_zone_id_set && (bsr_zone->zone_id() != zone_id)) continue; list::const_iterator group_prefix_iter; for (group_prefix_iter = bsr_zone->bsr_group_prefix_list().begin(); group_prefix_iter != bsr_zone->bsr_group_prefix_list().end(); ++group_prefix_iter) { BsrGroupPrefix *bsr_group_prefix = *group_prefix_iter; list::const_iterator rp_iter; for (rp_iter = bsr_group_prefix->rp_list().begin(); rp_iter != bsr_group_prefix->rp_list().end(); ++rp_iter) { BsrRp *bsr_rp = *rp_iter; int left_sec = -1; if (bsr_rp->const_candidate_rp_expiry_timer().scheduled()) { TimeVal tv_left; bsr_rp->const_candidate_rp_expiry_timer().time_remaining(tv_left); left_sec = tv_left.sec(); } cli_print(c_format("%-15s %3u %7d %-18s %-15s %16d\n", cstring(bsr_rp->rp_addr()), XORP_UINT_CAST(bsr_rp->rp_priority()), left_sec, cstring(bsr_group_prefix->group_prefix()), cstring(bsr_zone->bsr_addr()), -1)); } } } cli_print("Configured RPs:\n"); cli_print(c_format("%-15s %3s %7s %-18s %-15s %16s\n", "RP", "Pri", "Timeout", "GroupPrefix", "BSR", "CandRpAdvTimeout")); for (zone_iter = pim_node().pim_bsr().config_bsr_zone_list().begin(); zone_iter != pim_node().pim_bsr().config_bsr_zone_list().end(); ++zone_iter) { BsrZone *bsr_zone = *zone_iter; if (is_zone_id_set && (bsr_zone->zone_id() != zone_id)) continue; list::const_iterator group_prefix_iter; for (group_prefix_iter = bsr_zone->bsr_group_prefix_list().begin(); group_prefix_iter != bsr_zone->bsr_group_prefix_list().end(); ++group_prefix_iter) { BsrGroupPrefix *bsr_group_prefix = *group_prefix_iter; list::const_iterator rp_iter; for (rp_iter = bsr_group_prefix->rp_list().begin(); rp_iter != bsr_group_prefix->rp_list().end(); ++rp_iter) { BsrRp *bsr_rp = *rp_iter; int left_sec = -1; if (pim_node().is_my_addr(bsr_rp->rp_addr()) && (bsr_zone->const_candidate_rp_advertise_timer().scheduled())) { TimeVal tv_left; bsr_zone->const_candidate_rp_advertise_timer().time_remaining(tv_left); left_sec = tv_left.sec(); } cli_print(c_format("%-15s %3u %7d %-18s %-15s %16d\n", cstring(bsr_rp->rp_addr()), XORP_UINT_CAST(bsr_rp->rp_priority()), -1, cstring(bsr_group_prefix->group_prefix()), cstring(bsr_zone->bsr_addr()), left_sec)); } } } return (XORP_OK); } // // CLI COMMAND: "show pim interface [interface-name]" // CLI COMMAND: "show pim6 interface [interface-name]" // // Display information about the interfaces on which PIM is configured. // int PimNodeCli::cli_show_pim_interface(const vector& argv) { string interface_name; // Check the optional argument if (argv.size()) { interface_name = argv[0]; if (pim_node().vif_find_by_name(interface_name) == NULL) { cli_print(c_format("ERROR: Invalid interface name: %s\n", interface_name.c_str())); return (XORP_ERROR); } } cli_print(c_format("%-12s %-8s %-6s %1s %-8s %8s %-15s %9s\n", "Interface", "State", "Mode", "V", "PIMstate", "Priority", "DRaddr", "Neighbors")); for (uint32_t i = 0; i < pim_node().maxvifs(); i++) { PimVif *pim_vif = pim_node().vif_find_by_vif_index(i); if (pim_vif == NULL) continue; // Test if we should print this entry bool do_print = true; if (interface_name.size()) { do_print = false; if (pim_vif->name() == interface_name) { do_print = true; } } if (! do_print) continue; cli_print(c_format("%-12s %-8s %-6s %1d %-8s %8u %-15s %9u\n", pim_vif->name().c_str(), pim_vif->state_str().c_str(), pim_vif->proto_is_pimsm()? "Sparse" : "Dense", pim_vif->proto_version(), // TODO: should we print "only P2P" if P2P link? // pim_vif->is_p2p()? "P2P" : pim_vif->i_am_dr()? "DR" : "NotDR", pim_vif->i_am_dr()? "DR" : "NotDR", XORP_UINT_CAST(pim_vif->dr_priority().get()), cstring(pim_vif->dr_addr()), XORP_UINT_CAST(pim_vif->pim_nbrs_number()))); } return (XORP_OK); } // // CLI COMMAND: "show pim interface address [interface-name]" // CLI COMMAND: "show pim6 interface address [interface-name]" // // Display information about the addresses of PIM interfaces // int PimNodeCli::cli_show_pim_interface_address(const vector& argv) { string interface_name; // Check the optional argument if (argv.size()) { interface_name = argv[0]; if (pim_node().vif_find_by_name(interface_name) == NULL) { cli_print(c_format("ERROR: Invalid interface name: %s\n", interface_name.c_str())); return (XORP_ERROR); } } cli_print(c_format("%-12s %-15s %-15s %-15s\n", "Interface", "PrimaryAddr", "DomainWideAddr", "SecondaryAddr")); for (uint32_t i = 0; i < pim_node().maxvifs(); i++) { PimVif *pim_vif = pim_node().vif_find_by_vif_index(i); if (pim_vif == NULL) continue; // Test if we should print this entry bool do_print = true; if (interface_name.size()) { do_print = false; if (pim_vif->name() == interface_name) { do_print = true; } } if (! do_print) continue; // // Create a list with all secondary addresses // list secondary_addr_list; list::const_iterator vif_addr_iter; for (vif_addr_iter = pim_vif->addr_list().begin(); vif_addr_iter != pim_vif->addr_list().end(); ++vif_addr_iter) { const VifAddr& vif_addr = *vif_addr_iter; if (vif_addr.addr() == pim_vif->primary_addr()) continue; if (vif_addr.addr() == pim_vif->domain_wide_addr()) continue; secondary_addr_list.push_back(vif_addr.addr()); } cli_print(c_format("%-12s %-15s %-15s %-15s\n", pim_vif->name().c_str(), cstring(pim_vif->primary_addr()), cstring(pim_vif->domain_wide_addr()), (secondary_addr_list.size())? cstring(secondary_addr_list.front()): "")); // Pop the first secondary address if (secondary_addr_list.size()) secondary_addr_list.pop_front(); // // Print the rest of the secondary addresses // list::iterator secondary_addr_iter; for (secondary_addr_iter = secondary_addr_list.begin(); secondary_addr_iter != secondary_addr_list.end(); ++secondary_addr_iter) { IPvX& secondary_addr = *secondary_addr_iter; cli_print(c_format("%-12s %-15s %-15s %-15s\n", " ", " ", " ", cstring(secondary_addr))); } } return (XORP_OK); } // // CLI COMMAND: "show pim join [group | group-range]" // CLI COMMAND: "show pim6 join [group | group-range]" // // Display information about PIM groups. // int PimNodeCli::cli_show_pim_join(const vector& argv) { IPvXNet group_range = IPvXNet::ip_multicast_base_prefix(family()); // Check the optional argument if (argv.size()) { try { group_range = IPvXNet(argv[0].c_str()); } catch (InvalidString) { try { group_range = IPvXNet(IPvX(argv[0].c_str()), IPvX::addr_bitlen(family())); } catch (InvalidString) { cli_print(c_format("ERROR: Invalid group range address: %s\n", argv[0].c_str())); return (XORP_ERROR); } catch (InvalidNetmaskLength) { cli_print(c_format("ERROR: Invalid group range netmask length: %s\n", argv[0].c_str())); return (XORP_ERROR); } } if (! group_range.is_multicast()) { cli_print(c_format("ERROR: Group range is not multicast: %s\n", cstring(group_range))); return (XORP_ERROR); } } cli_print_pim_mre_entries(group_range, false); return (XORP_OK); } // // CLI COMMAND: "show pim join all [group | group-range]" // CLI COMMAND: "show pim6 join all [group | group-range]" // // Display information about all PIM groups. // int PimNodeCli::cli_show_pim_join_all(const vector& argv) { IPvXNet group_range = IPvXNet::ip_multicast_base_prefix(family()); // Check the optional argument if (argv.size()) { try { group_range = IPvXNet(argv[0].c_str()); } catch (InvalidString) { try { group_range = IPvXNet(IPvX(argv[0].c_str()), IPvX::addr_bitlen(family())); } catch (InvalidString) { cli_print(c_format("ERROR: Invalid group range address: %s\n", argv[0].c_str())); return (XORP_ERROR); } catch (InvalidNetmaskLength) { XLOG_UNREACHABLE(); return (XORP_ERROR); } } catch (InvalidNetmaskLength) { cli_print(c_format("ERROR: Invalid group range netmask length: %s\n", argv[0].c_str())); return (XORP_ERROR); } if (! group_range.is_multicast()) { cli_print(c_format("ERROR: Group range is not multicast: %s\n", cstring(group_range))); return (XORP_ERROR); } } cli_print_pim_mre_entries(group_range, true); return (XORP_OK); } // // CLI COMMAND: "show pim mfc [group | group-range]" // CLI COMMAND: "show pim6 mfc [group | group-range]" // // Display information about PIM Multicast Forwarding Cache. // int PimNodeCli::cli_show_pim_mfc(const vector& argv) { IPvXNet group_range = IPvXNet::ip_multicast_base_prefix(family()); // Check the optional argument if (argv.size()) { try { group_range = IPvXNet(argv[0].c_str()); } catch (InvalidString) { try { group_range = IPvXNet(IPvX(argv[0].c_str()), IPvX::addr_bitlen(family())); } catch (InvalidString) { cli_print(c_format("ERROR: Invalid group range address: %s\n", argv[0].c_str())); return (XORP_ERROR); } catch (InvalidNetmaskLength) { XLOG_UNREACHABLE(); return (XORP_ERROR); } } catch (InvalidNetmaskLength) { cli_print(c_format("ERROR: Invalid group range netmask length: %s\n", argv[0].c_str())); return (XORP_ERROR); } if (! group_range.is_multicast()) { cli_print(c_format("ERROR: Group range is not multicast: %s\n", cstring(group_range))); return (XORP_ERROR); } } cli_print_pim_mfc_entries(group_range); return (XORP_OK); } // // Print the CLI output for the #PimMre entries. // void PimNodeCli::cli_print_pim_mre_entries(const IPvXNet& group_range, bool is_print_all) { // // TODO: XXX: PAVPAVPAV: the printing below is very incomplete. // cli_print(c_format("%-15s %-15s %-15s %-5s\n", "Group", "Source", "RP", "Flags")); // // The (*,*,RP) entries // PimMrtRp::const_sg_iterator iter_rp, iter_begin_rp, iter_end_rp; iter_begin_rp = pim_node().pim_mrt().pim_mrt_rp().sg_begin(); iter_end_rp = pim_node().pim_mrt().pim_mrt_rp().sg_end(); for (iter_rp = iter_begin_rp; iter_rp != iter_end_rp; ++iter_rp) { PimMre *pim_mre_rp = iter_rp->second; if (is_print_all || (! pim_mre_rp->is_not_joined_state())) cli_print_pim_mre(pim_mre_rp); } // // The (*,G) entries // PimMrtG::const_gs_iterator iter_g, iter_begin_g, iter_end_g; iter_begin_g = pim_node().pim_mrt().pim_mrt_g().group_by_prefix_begin(group_range); iter_end_g = pim_node().pim_mrt().pim_mrt_g().group_by_prefix_end(group_range); for (iter_g = iter_begin_g; iter_g != iter_end_g; ++iter_g) { PimMre *pim_mre_g = iter_g->second; cli_print_pim_mre(pim_mre_g); } PimMrtSg::const_gs_iterator iter_s, iter_begin_s, iter_end_s; // // The (S,G,rpt) entries // iter_begin_s = pim_node().pim_mrt().pim_mrt_sg_rpt().group_by_prefix_begin(group_range); iter_end_s = pim_node().pim_mrt().pim_mrt_sg_rpt().group_by_prefix_end(group_range); for (iter_s = iter_begin_s; iter_s != iter_end_s; ++iter_s) { PimMre *pim_mre_s = iter_s->second; cli_print_pim_mre(pim_mre_s); } // The (S,G) entries iter_begin_s = pim_node().pim_mrt().pim_mrt_sg().group_by_prefix_begin(group_range); iter_end_s = pim_node().pim_mrt().pim_mrt_sg().group_by_prefix_end(group_range); for (iter_s = iter_begin_s; iter_s != iter_end_s; ++iter_s) { PimMre *pim_mre_s = iter_s->second; cli_print_pim_mre(pim_mre_s); } } // // Print the CLI output for the #PimMfc entries. // void PimNodeCli::cli_print_pim_mfc_entries(const IPvXNet& group_range) { cli_print(c_format("%-15s %-15s %-15s\n", "Group", "Source", "RP")); // // The PimMfc entries // PimMrtMfc::const_gs_iterator iter_mfc, iter_begin_mfc, iter_end_mfc; iter_begin_mfc = pim_node().pim_mrt().pim_mrt_mfc().group_by_prefix_begin(group_range); iter_end_mfc = pim_node().pim_mrt().pim_mrt_mfc().group_by_prefix_end(group_range); for (iter_mfc = iter_begin_mfc; iter_mfc != iter_end_mfc; ++iter_mfc) { PimMfc *pim_mfc = iter_mfc->second; cli_print_pim_mfc(pim_mfc); } } // // Print the CLI output for a #PimMre entry. // void PimNodeCli::cli_print_pim_mre(const PimMre *pim_mre) { if (pim_mre == NULL) return; // // Compute the entry state flags // string entry_state_flags; if (pim_mre->is_sg()) entry_state_flags += "SG "; if (pim_mre->is_sg_rpt()) entry_state_flags += "SG_RPT "; if (pim_mre->is_wc()) entry_state_flags += "WC "; if (pim_mre->is_rp()) entry_state_flags += "RP "; if (pim_mre->is_spt()) entry_state_flags += "SPT "; if (pim_mre->is_directly_connected_s()) entry_state_flags += "DirectlyConnectedS "; // // Compute the upstream state // string upstream_state; if (pim_mre->is_rp() || pim_mre->is_wc() || pim_mre->is_sg()) { if (pim_mre->is_joined_state()) upstream_state += "Joined "; if (pim_mre->is_not_joined_state()) upstream_state += "NotJoined "; } if (pim_mre->is_sg_rpt()) { if (pim_mre->is_rpt_not_joined_state()) upstream_state += "RPTNotJoined "; if (pim_mre->is_pruned_state()) upstream_state += "Pruned "; if (pim_mre->is_not_pruned_state()) upstream_state += "NotPruned "; } // // Compute the Register state // string register_state; if (pim_mre->is_sg() && pim_mre->is_directly_connected_s()) { if (pim_mre->is_register_noinfo_state()) register_state += "RegisterNoinfo "; if (pim_mre->is_register_join_state()) register_state += "RegisterJoin "; if (pim_mre->is_register_prune_state()) register_state += "RegisterPrune "; if (pim_mre->is_register_join_pending_state()) register_state += "RegisterJoinPending "; if (pim_mre->is_could_register_sg()) register_state += "RegisterCouldRegister "; if (pim_mre->is_not_could_register_sg()) register_state += "RegisterNotCouldRegister "; } // Compute the RPF interface uint32_t vif_index_s, vif_index_rp; PimVif *iif_pim_vif_s = NULL; PimVif *iif_pim_vif_rp = NULL; vif_index_s = pim_mre->rpf_interface_s(); vif_index_rp = pim_mre->rpf_interface_rp(); iif_pim_vif_s = pim_node().vif_find_by_vif_index(vif_index_s); iif_pim_vif_rp = pim_node().vif_find_by_vif_index(vif_index_rp); cli_print(c_format("%-15s %-15s %-15s %-5s\n", cstring(pim_mre->group_addr()), cstring(pim_mre->source_addr()), pim_mre->rp_addr_string().c_str(), entry_state_flags.c_str())); if (pim_mre->is_sg() || pim_mre->is_sg_rpt()) { cli_print(c_format(" Upstream interface (S): %s\n", (iif_pim_vif_s != NULL)? iif_pim_vif_s->name().c_str() : "UNKNOWN")); } cli_print(c_format(" Upstream interface (RP): %s\n", (iif_pim_vif_rp != NULL)? iif_pim_vif_rp->name().c_str() : "UNKNOWN")); cli_print(c_format(" Upstream MRIB next hop (RP): %s\n", (pim_mre->nbr_mrib_next_hop_rp() != NULL)? cstring(pim_mre->nbr_mrib_next_hop_rp()->primary_addr()) : "UNKNOWN")); if (pim_mre->is_sg()) { cli_print(c_format(" Upstream MRIB next hop (S): %s\n", (pim_mre->nbr_mrib_next_hop_s() != NULL)? cstring(pim_mre->nbr_mrib_next_hop_s()->primary_addr()) : "UNKNOWN")); } if (pim_mre->is_wc()) { cli_print(c_format(" Upstream RPF'(*,G): %s\n", (pim_mre->rpfp_nbr_wc() != NULL)? cstring(pim_mre->rpfp_nbr_wc()->primary_addr()) : "UNKNOWN")); } if (pim_mre->is_sg()) { cli_print(c_format(" Upstream RPF'(S,G): %s\n", (pim_mre->rpfp_nbr_sg() != NULL)? cstring(pim_mre->rpfp_nbr_sg()->primary_addr()) : "UNKNOWN")); } if (pim_mre->is_sg_rpt()) { cli_print(c_format(" Upstream RPF'(S,G,rpt): %s\n", (pim_mre->rpfp_nbr_sg_rpt() != NULL)? cstring(pim_mre->rpfp_nbr_sg_rpt()->primary_addr()) : "UNKNOWN")); } cli_print(c_format(" Upstream state: %s\n", upstream_state.c_str())); if (pim_mre->is_sg() && pim_mre->is_directly_connected_s()) { cli_print(c_format(" Register state: %s\n", register_state.c_str())); } if (pim_mre->is_sg_rpt()) { int left_sec = -1; if (pim_mre->const_override_timer().scheduled()) { TimeVal tv_left; pim_mre->const_override_timer().time_remaining(tv_left); left_sec = tv_left.sec(); } cli_print(c_format(" Override timer: %d\n", left_sec)); } else { int left_sec = -1; if (pim_mre->const_join_timer().scheduled()) { TimeVal tv_left; pim_mre->const_join_timer().time_remaining(tv_left); left_sec = tv_left.sec(); } cli_print(c_format(" Join timer: %d\n", left_sec)); } if (pim_mre->is_sg()) { cli_print(c_format(" KAT(S,G) running: %s\n", bool_c_str(pim_mre->is_keepalive_timer_running()))); } // // The downstream interfaces // if (pim_mre->is_wc() || pim_mre->is_sg() || pim_mre->is_sg_rpt()) { cli_print(c_format(" Local receiver include WC: %s\n", mifset_str(pim_mre->local_receiver_include_wc()).c_str())); } if (pim_mre->is_sg()) { cli_print(c_format(" Local receiver include SG: %s\n", mifset_str(pim_mre->local_receiver_include_sg()).c_str())); cli_print(c_format(" Local receiver exclude SG: %s\n", mifset_str(pim_mre->local_receiver_exclude_sg()).c_str())); } cli_print(c_format(" Joins RP: %s\n", mifset_str(pim_mre->joins_rp()).c_str())); if (pim_mre->is_wc() || pim_mre->is_sg() || pim_mre->is_sg_rpt()) { cli_print(c_format(" Joins WC: %s\n", mifset_str(pim_mre->joins_wc()).c_str())); } if (pim_mre->is_sg()) { cli_print(c_format(" Joins SG: %s\n", mifset_str(pim_mre->joins_sg()).c_str())); } if (pim_mre->is_sg_rpt()) { cli_print(c_format(" Prunes SG_RPT: %s\n", mifset_str(pim_mre->prunes_sg_rpt()).c_str())); } cli_print(c_format(" Join state: %s\n", mifset_str(pim_mre->downstream_join_state()).c_str())); cli_print(c_format(" Prune state: %s\n", mifset_str(pim_mre->downstream_prune_state()).c_str())); cli_print(c_format(" Prune pending state: %s\n", mifset_str(pim_mre->downstream_prune_pending_state()).c_str())); if (pim_mre->is_sg_rpt()) { cli_print(c_format(" Prune tmp state: %s\n", mifset_str(pim_mre->downstream_prune_tmp_state()).c_str())); cli_print(c_format(" Prune pending tmp state: %s\n", mifset_str(pim_mre->downstream_prune_pending_tmp_state()).c_str())); } if (pim_mre->is_wc() || pim_mre->is_sg()) { cli_print(c_format(" I am assert winner state: %s\n", mifset_str(pim_mre->i_am_assert_winner_state()).c_str())); cli_print(c_format(" I am assert loser state: %s\n", mifset_str(pim_mre->i_am_assert_loser_state()).c_str())); } if (pim_mre->is_wc() || pim_mre->is_sg() || pim_mre->is_sg_rpt()) { cli_print(c_format(" Assert winner WC: %s\n", mifset_str(pim_mre->i_am_assert_winner_wc()).c_str())); } if (pim_mre->is_sg()) { cli_print(c_format(" Assert winner SG: %s\n", mifset_str(pim_mre->i_am_assert_winner_sg()).c_str())); } if (pim_mre->is_wc() || pim_mre->is_sg() || pim_mre->is_sg_rpt()) { cli_print(c_format(" Assert lost WC: %s\n", mifset_str(pim_mre->lost_assert_wc()).c_str())); } if (pim_mre->is_sg()) { cli_print(c_format(" Assert lost SG: %s\n", mifset_str(pim_mre->lost_assert_sg()).c_str())); } if (pim_mre->is_sg() || pim_mre->is_sg_rpt()) { cli_print(c_format(" Assert lost SG_RPT: %s\n", mifset_str(pim_mre->lost_assert_sg_rpt()).c_str())); } if (pim_mre->is_wc()) { cli_print(c_format(" Assert tracking WC: %s\n", mifset_str(pim_mre->assert_tracking_desired_wc()).c_str())); } if (pim_mre->is_sg()) { cli_print(c_format(" Assert tracking SG: %s\n", mifset_str(pim_mre->assert_tracking_desired_sg()).c_str())); } cli_print(c_format(" Could assert WC: %s\n", mifset_str(pim_mre->could_assert_wc()).c_str())); if (pim_mre->is_sg() || pim_mre->is_sg_rpt()) { cli_print(c_format(" Could assert SG: %s\n", mifset_str(pim_mre->could_assert_sg()).c_str())); } cli_print(c_format(" I am DR: %s\n", mifset_str(pim_mre->i_am_dr()).c_str())); cli_print(c_format(" Immediate olist RP: %s\n", mifset_str(pim_mre->immediate_olist_rp()).c_str())); if (pim_mre->is_wc() || pim_mre->is_sg() || pim_mre->is_sg_rpt()) { cli_print(c_format(" Immediate olist WC: %s\n", mifset_str(pim_mre->immediate_olist_wc()).c_str())); } if (pim_mre->is_sg()) { cli_print(c_format(" Immediate olist SG: %s\n", mifset_str(pim_mre->immediate_olist_sg()).c_str())); } cli_print(c_format(" Inherited olist SG: %s\n", mifset_str(pim_mre->inherited_olist_sg()).c_str())); cli_print(c_format(" Inherited olist SG_RPT: %s\n", mifset_str(pim_mre->inherited_olist_sg_rpt()).c_str())); if (pim_mre->is_wc() || pim_mre->is_sg() || pim_mre->is_sg_rpt()) { cli_print(c_format(" PIM include WC: %s\n", mifset_str(pim_mre->pim_include_wc()).c_str())); } if (pim_mre->is_sg()) { cli_print(c_format(" PIM include SG: %s\n", mifset_str(pim_mre->pim_include_sg()).c_str())); cli_print(c_format(" PIM exclude SG: %s\n", mifset_str(pim_mre->pim_exclude_sg()).c_str())); } } // // Print the CLI output for a #PimMfc entry. // void PimNodeCli::cli_print_pim_mfc(const PimMfc *pim_mfc) { if (pim_mfc == NULL) return; // Compute the IIF interface PimVif *iif_pim_vif = pim_node().vif_find_by_vif_index(pim_mfc->iif_vif_index()); cli_print(c_format("%-15s %-15s %-15s\n", cstring(pim_mfc->group_addr()), cstring(pim_mfc->source_addr()), cstring(pim_mfc->rp_addr()))); cli_print(c_format(" Incoming interface : %s\n", (iif_pim_vif != NULL)? iif_pim_vif->name().c_str() : "UNKNOWN")); // // The outgoing interfaces // cli_print(c_format(" Outgoing interfaces: %s\n", mifset_str(pim_mfc->olist()).c_str())); } // // CLI COMMAND: "show pim neighbors [interface-name]" // CLI COMMAND: "show pim6 neighbors [interface-name]" // // Display information about PIM neighbors. // int PimNodeCli::cli_show_pim_neighbors(const vector& argv) { string interface_name; // Check the optional argument if (argv.size()) { interface_name = argv[0]; if (pim_node().vif_find_by_name(interface_name) == NULL) { cli_print(c_format("ERROR: Invalid interface name: %s\n", interface_name.c_str())); return (XORP_ERROR); } } cli_print(c_format("%-12s %10s %-15s %1s %-6s %8s %7s\n", "Interface", "DRpriority", "NeighborAddr", "V", "Mode", "Holdtime", "Timeout")); for (uint32_t i = 0; i < pim_node().maxvifs(); i++) { PimVif *pim_vif = pim_node().vif_find_by_vif_index(i); if (pim_vif == NULL) continue; // Test if we should print this entry bool do_print = true; if (interface_name.size()) { do_print = false; if (pim_vif->name() == interface_name) { do_print = true; } } if (! do_print) continue; list::iterator iter; for (iter = pim_vif->pim_nbrs().begin(); iter != pim_vif->pim_nbrs().end(); ++iter) { PimNbr *pim_nbr = *iter; string dr_priority_string; if (pim_nbr->is_dr_priority_present()) { dr_priority_string = c_format( "%u", XORP_UINT_CAST(pim_nbr->dr_priority())); } else { dr_priority_string = "none"; } string nbr_timeout_sec_string; if (pim_nbr->const_neighbor_liveness_timer().scheduled()) { TimeVal tv_left; pim_nbr->const_neighbor_liveness_timer().time_remaining(tv_left); nbr_timeout_sec_string = c_format( "%d", XORP_INT_CAST(tv_left.sec())); } else { nbr_timeout_sec_string = "None"; } cli_print(c_format("%-12s %10s %-15s %1d %-6s %8u %7s\n", pim_vif->name().c_str(), dr_priority_string.c_str(), cstring(pim_nbr->primary_addr()), pim_nbr->proto_version(), pim_vif->proto_is_pimsm()? "Sparse" : "Dense", XORP_UINT_CAST(pim_nbr->hello_holdtime()), nbr_timeout_sec_string.c_str())); // Print the secondary addresses list::const_iterator list_iter; for (list_iter = pim_nbr->secondary_addr_list().begin(); list_iter != pim_nbr->secondary_addr_list().end(); ++list_iter) { const IPvX& secondary_addr = *list_iter; cli_print(c_format("%-12s %10s %-15s\n", "", "", cstring(secondary_addr))); } } } return (XORP_OK); } // // CLI COMMAND: "show pim mrib [dest-address]" // CLI COMMAND: "show pim6 mrib [dest-address]" // // Display MRIB information inside PIM. // int PimNodeCli::cli_show_pim_mrib(const vector& argv) { string dest_address_name; IPvX dest_address(family()); // Check the optional argument if (argv.size()) { dest_address_name = argv[0]; try { dest_address = IPvX(dest_address_name.c_str()); } catch (InvalidString) { cli_print(c_format("ERROR: Invalid destination address: %s\n", dest_address_name.c_str())); return (XORP_ERROR); } } // Test if we should print a single entry only if (dest_address_name.size()) { Mrib *mrib = pim_node().pim_mrib_table().find(dest_address); if (mrib == NULL) { cli_print(c_format("No matching MRIB entry for %s\n", dest_address_name.c_str())); return (XORP_ERROR); } cli_print(c_format("%-18s %-15s %-7s %-8s %10s %6s\n", "DestPrefix", "NextHopRouter", "VifName", "VifIndex", "MetricPref", "Metric")); string vif_name = "UNKNOWN"; Vif *vif = pim_node().vif_find_by_vif_index(mrib->next_hop_vif_index()); if (vif != NULL) vif_name = vif->name(); cli_print(c_format("%-18s %-15s %-7s %-8u %10u %6u\n", cstring(mrib->dest_prefix()), cstring(mrib->next_hop_router_addr()), vif_name.c_str(), XORP_UINT_CAST(mrib->next_hop_vif_index()), XORP_UINT_CAST(mrib->metric_preference()), XORP_UINT_CAST(mrib->metric()))); return (XORP_OK); } cli_print(c_format("%-18s %-15s %-7s %-8s %10s %6s\n", "DestPrefix", "NextHopRouter", "VifName", "VifIndex", "MetricPref", "Metric")); PimMribTable::iterator iter; for (iter = pim_node().pim_mrib_table().begin(); iter != pim_node().pim_mrib_table().end(); ++iter) { Mrib *mrib = *iter; if (mrib == NULL) continue; string vif_name = "UNKNOWN"; Vif *vif = pim_node().vif_find_by_vif_index(mrib->next_hop_vif_index()); if (vif != NULL) vif_name = vif->name(); cli_print(c_format("%-18s %-15s %-7s %-8u %10u %6u\n", cstring(mrib->dest_prefix()), cstring(mrib->next_hop_router_addr()), vif_name.c_str(), XORP_UINT_CAST(mrib->next_hop_vif_index()), XORP_UINT_CAST(mrib->metric_preference()), XORP_UINT_CAST(mrib->metric()))); } return (XORP_OK); } // // CLI COMMAND: "show pim rps [group-address]" // CLI COMMAND: "show pim6 rps [group-address]" // // Display information about PIM RPs // int PimNodeCli::cli_show_pim_rps(const vector& argv) { PimRp *show_pim_rp = NULL; // Check the optional argument if (argv.size()) { try { IPvX group_addr(argv[0].c_str()); if (group_addr.af() != family()) { cli_print(c_format("ERROR: Address with invalid address family: %s\n", argv[0].c_str())); return (XORP_ERROR); } // // Try to find the RP for that group. // show_pim_rp = pim_node().rp_table().rp_find(group_addr); if (show_pim_rp == NULL) { cli_print(c_format("ERROR: no matching RP for group %s\n", cstring(group_addr))); return (XORP_ERROR); } } catch (InvalidString) { cli_print(c_format("ERROR: Invalid group address: %s\n", argv[0].c_str())); return (XORP_ERROR); } } cli_print(c_format("%-15s %-9s %3s %8s %7s %12s %-18s\n", "RP", "Type", "Pri", "Holdtime", "Timeout", "ActiveGroups", "GroupPrefix")); list::const_iterator iter; for (iter = pim_node().rp_table().rp_list().begin(); iter != pim_node().rp_table().rp_list().end(); ++iter) { PimRp *pim_rp = *iter; if ((show_pim_rp != NULL) && (show_pim_rp != pim_rp)) continue; // Get the string-name of the RP type (i.e., how we learned about it) string rp_type; switch (pim_rp->rp_learned_method()) { case PimRp::RP_LEARNED_METHOD_AUTORP: rp_type = "auto-rp"; break; case PimRp::RP_LEARNED_METHOD_BOOTSTRAP: rp_type = "bootstrap"; break; case PimRp::RP_LEARNED_METHOD_STATIC: rp_type = "static"; break; default: rp_type = "unknown"; break; } // // Compute the 'holdtime' and 'timeout' for this RP (if applicable) // int holdtime = -1; int left_sec = -1; switch (pim_rp->rp_learned_method()) { case PimRp::RP_LEARNED_METHOD_AUTORP: break; case PimRp::RP_LEARNED_METHOD_BOOTSTRAP: do { // Try first a scoped zone, then a non-scoped zone BsrRp *bsr_rp; bsr_rp = pim_node().pim_bsr().find_rp(pim_rp->group_prefix(), true, pim_rp->rp_addr()); if (bsr_rp == NULL) { bsr_rp = pim_node().pim_bsr().find_rp(pim_rp->group_prefix(), false, pim_rp->rp_addr()); } if (bsr_rp == NULL) break; holdtime = bsr_rp->rp_holdtime(); if (bsr_rp->const_candidate_rp_expiry_timer().scheduled()) { TimeVal tv_left; bsr_rp->const_candidate_rp_expiry_timer().time_remaining(tv_left); left_sec = tv_left.sec(); } } while (false); break; case PimRp::RP_LEARNED_METHOD_STATIC: break; default: break; } cli_print(c_format("%-15s %-9s %3u %8d %7d %12u %-18s\n", cstring(pim_rp->rp_addr()), rp_type.c_str(), XORP_UINT_CAST(pim_rp->rp_priority()), holdtime, left_sec, XORP_UINT_CAST(pim_rp->pim_mre_wc_list().size() + pim_rp->processing_pim_mre_wc_list().size()), cstring(pim_rp->group_prefix()))); } return (XORP_OK); } // // CLI COMMAND: "show pim scope" // CLI COMMAND: "show pim6 scope" // // Display information about PIM RPs // int PimNodeCli::cli_show_pim_scope(const vector& argv) { // Check the optional argument if (argv.size()) { cli_print(c_format("ERROR: Unexpected argument: %s\n", argv[0].c_str())); return (XORP_ERROR); } cli_print(c_format("%-43s %-14s\n", "GroupPrefix", "Interface")); list::const_iterator iter; for (iter = pim_node().pim_scope_zone_table().pim_scope_zone_list().begin(); iter != pim_node().pim_scope_zone_table().pim_scope_zone_list().end(); ++iter) { const PimScopeZone& pim_scope_zone = *iter; for (uint32_t i = 0; i < pim_node().maxvifs(); i++) { if (pim_scope_zone.is_set(i)) { PimVif *pim_vif = pim_node().vif_find_by_vif_index(i); if (pim_vif == NULL) continue; cli_print(c_format("%-43s %-14s\n", cstring(pim_scope_zone.scope_zone_prefix()), pim_vif->name().c_str())); } } } return (XORP_OK); } xorp/pim/DEVNOTES0000664000076400007640000001122111421137511013610 0ustar greearbgreearb# # $XORP: xorp/pim/DEVNOTES,v 1.6 2005/05/04 01:53:01 pavlin Exp $ # * Always call PimMre::entry_try_remove() for (S,G,rpt) routing entry whenever one of the following happens: - its Override Timer timeout or is canceled: PimMre::override_timer(); - the upstream state machine moves to RPTNotJoined or NotPruned state: PimMre::set_rpt_not_joined_state() PimMre::set_not_pruned_state(); - the downstream interface state machine moves to NoInfo state PimMre::set_downstream_noinfo_state() * If accept_nohello_neighbors configuration is set to true, all messages enumerated below will be accepted from a neighbor that didn't send a Hello first: Join/Prune, Bootstrap, Assert, Graft, GraftAck. Further, the Hello holdtime value for such neighbors will be assumed to be MAX(Default_Hello_Holdtime, Default J/P Holdtime = 3.5*t_periodic) * PimRpfTable::find(const IPvX& address) returns NULL if the RPF PimVif is not UP. * The SPT-bit is set only for (S,G) PimMre entry. Fortunately, needs to be set only after an (S,G) Join or (S,G) Assert message is received. Hence, the side effect of this is that if there is (S,G) asssert, we must create (S,G) PimMre entry. If the (S,G) Assert was the only reason to create a (S,G) PimMre entry, we must delete that entry if the Assert state expires. * If an output_state_t action for PimMre tracking state needs to be applied to more than one type of PimMre entry, then in PimMreTrackState::output_state_foo() call the corresponding track_state_foo() more than once: once for each PimMre entry type. * The macros in Section "4.3. Designated Routers (DR) and Hello Messages" are not added to the PimMre::track_dependency_*() methods. * PimMre::set_rp_entry() is set only for (*,G) entries. The (S,G) and (S,G,rpt) entries get rp_entry() indirectly through their own (*,G) entry. * PimMrt table contains four separate lookup tables: (S,G), (S,G,rpt), (*,G), and (*,*,RP) tables In addition, there is a separate MFC table. * If (S,G) Assert is received, we keep it only in an (S,G) PimMre entry, even if this means that we may have to create it first (e.g., when we are forwarding on (S,G,rpt) entry. * If there is an (S,G) entry, we may not have (*,G) entry. * A PimRp entry contains a list of all (*,G) entries that match to that RP, but two lists of only those (S,G) and (S,G,rpt) entries that don't have (*,G) entry. * Whenever pim_mre_find() is used to create a new PimMre entry, make sure that pim_mre->recompute_is_join_desired_FOO(); is called in case none of the downstream-related state is changed. * All the tasks added by PimMrt::add_task_mrib_changed() must "clean-up" all pointers that point to Mrib entries removed from the MribTable (e.g., currently, those are PimMre::mrib_rp() and PimMre::mrib_s()). The pointers should be updated to point to the new corresponding Mrib entries. The reason is because whenever there is any change to the MribTable, the last task to add is PimMrt::add_task_delete_mrib_entries(), and this task will delete the old Mrib entries. * The spec says the following about transmitting PruneEcho messages. E.g., in case of PruneEcho(*,*,RP): "A PruneEcho(*,*,RP) need not be sent on an interface that contains only a single PIM neighbor during the time this state machine was in Prune-Pending state." However, it is rather difficult to track the number of neighbors during all the time the state machine is in Prune-Pending state. Hence, we check the number of neighbors only when it is time to send the PruneEcho message. Indeed, in some specific scenarios this may suppress the PruneEcho message when it shouldn't, but this is not a source of concern, because those are transient scenarios. A possible alternative would be to always send PruneEcho without suppressing it, because the suppression itself is just an optimization. * According to the spec, if we receive AssertCancel on the RPF interface, then its metric will be compared against my_assert_metric(). However, given that CouldAssert for the RPF interface is false, my_assert_metric() will return infinite_assert_metric(), therefore AssertCancel will fail to perform its duty. This is fixed in the implementation by testing first if the received Assert message contains AssertCancel metric, and if yes, the local router is automatically declared the winner. Note: This fix is based on the following email to the PIM Working Group mailing list: Date: Fri, 9 Jul 2004 11:44:41 -0700 (PDT) From: Venugopal Hemige Subject: [pim] Hello HoldTime and Assert questions http://www1.ietf.org/mail-archive/web/pim/current/msg00206.html xorp/pim/xrl_pim_node.cc0000664000076400007640000051611011634760770015452 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "pim_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvx.hh" #include "libxorp/status_codes.h" #include "libxorp/utils.hh" #include "pim_mfc.hh" #include "pim_node.hh" #include "pim_node_cli.hh" #include "pim_mrib_table.hh" #include "pim_vif.hh" #include "xrl_pim_node.hh" const TimeVal XrlPimNode::RETRY_TIMEVAL = TimeVal(1, 0); // // XrlPimNode front-end interface // XrlPimNode::XrlPimNode(int family, xorp_module_id module_id, EventLoop& eventloop, const string& class_name, const string& finder_hostname, uint16_t finder_port, const string& finder_target, const string& fea_target, const string& mfea_target, const string& rib_target, const string& mld6igmp_target) : PimNode(family, module_id, eventloop), XrlStdRouter(eventloop, class_name.c_str(), finder_hostname.c_str(), finder_port), XrlPimTargetBase(&xrl_router()), PimNodeCli(*static_cast(this)), _eventloop(eventloop), _finder_target(finder_target), _fea_target(fea_target), _mfea_target(mfea_target), _rib_target(rib_target), _mld6igmp_target(mld6igmp_target), _ifmgr(eventloop, mfea_target.c_str(), xrl_router().finder_address(), xrl_router().finder_port()), _mrib_transaction_manager(eventloop), _xrl_fea_client4(&xrl_router()), #ifdef HAVE_IPV6 _xrl_fea_client6(&xrl_router()), #endif _xrl_mfea_client(&xrl_router()), _xrl_rib_client(&xrl_router()), _xrl_mld6igmp_client(&xrl_router()), _xrl_cli_manager_client(&xrl_router()), _xrl_finder_client(&xrl_router()), _is_finder_alive(false), _is_fea_alive(false), _is_fea_registered(false), _is_mfea_alive(false), _is_mfea_registered(false), _is_rib_alive(false), _is_rib_registered(false), _is_rib_registering(false), _is_rib_deregistering(false), _is_rib_redist_transaction_enabled(false), _is_mld6igmp_alive(false), _is_mld6igmp_registered(false), _is_mld6igmp_registering(false), _is_mld6igmp_deregistering(false) { _ifmgr.set_observer(dynamic_cast(this)); _ifmgr.attach_hint_observer(dynamic_cast(this)); } void XrlPimNode::destruct_me() { shutdown(); _ifmgr.detach_hint_observer(dynamic_cast(this)); _ifmgr.unset_observer(dynamic_cast(this)); delete_pointers_list(_xrl_tasks_queue); PimNode::destruct_me(); } XrlPimNode::~XrlPimNode() { destruct_me(); } int XrlPimNode::startup() { if (start_pim() != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int XrlPimNode::shutdown() { int ret_value = XORP_OK; if (stop_cli() != XORP_OK) ret_value = XORP_ERROR; if (stop_pim() != XORP_OK) ret_value = XORP_ERROR; return (ret_value); } int XrlPimNode::enable_cli() { PimNodeCli::enable(); return (XORP_OK); } int XrlPimNode::disable_cli() { PimNodeCli::disable(); return (XORP_OK); } int XrlPimNode::start_cli() { if (PimNodeCli::start() != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int XrlPimNode::stop_cli() { if (PimNodeCli::stop() != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int XrlPimNode::enable_pim() { PimNode::enable(); return (XORP_OK); } int XrlPimNode::disable_pim() { PimNode::disable(); return (XORP_OK); } int XrlPimNode::start_pim() { if (PimNode::start() != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int XrlPimNode::stop_pim() { if (PimNode::stop() != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int XrlPimNode::enable_bsr() { PimNode::enable_bsr(); return (XORP_OK); } int XrlPimNode::disable_bsr() { PimNode::disable_bsr(); return (XORP_OK); } int XrlPimNode::start_bsr() { if (PimNode::start_bsr() != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int XrlPimNode::stop_bsr() { if (PimNode::stop_bsr() != XORP_OK) return (XORP_ERROR); return (XORP_OK); } // // Finder-related events // /** * Called when Finder connection is established. * * Note that this method overwrites an XrlRouter virtual method. */ void XrlPimNode::finder_connect_event() { _is_finder_alive = true; } /** * Called when Finder disconnect occurs. * * Note that this method overwrites an XrlRouter virtual method. */ void XrlPimNode::finder_disconnect_event() { XLOG_ERROR("Finder disconnect event. Exiting immediately..."); _is_finder_alive = false; stop_pim(); } // // Task-related methods // void XrlPimNode::add_task(XrlTaskBase* xrl_task) { _xrl_tasks_queue.push_back(xrl_task); // If the queue was empty before, start sending the changes if (_xrl_tasks_queue.size() == 1) send_xrl_task(); } void XrlPimNode::send_xrl_task() { if (_xrl_tasks_queue.empty()) return; XrlTaskBase* xrl_task_base = _xrl_tasks_queue.front(); XLOG_ASSERT(xrl_task_base != NULL); xrl_task_base->dispatch(); } void XrlPimNode::pop_xrl_task() { XLOG_ASSERT(! _xrl_tasks_queue.empty()); XrlTaskBase* xrl_task_base = _xrl_tasks_queue.front(); XLOG_ASSERT(xrl_task_base != NULL); delete xrl_task_base; _xrl_tasks_queue.pop_front(); } void XrlPimNode::retry_xrl_task() { if (_xrl_tasks_queue_timer.scheduled()) return; // XXX: already scheduled _xrl_tasks_queue_timer = _eventloop.new_oneoff_after( RETRY_TIMEVAL, callback(this, &XrlPimNode::send_xrl_task)); } // // Register with the FEA // void XrlPimNode::fea_register_startup() { if (! _is_finder_alive) return; // The Finder is dead if (_is_fea_registered) return; // Already registered PimNode::incr_startup_requests_n(); // XXX: for FEA registration PimNode::incr_startup_requests_n(); // XXX: for FEA birth // // Register interest in the FEA with the Finder // add_task(new RegisterUnregisterInterest(*this, _fea_target, true)); } // // Register with the MFEA // void XrlPimNode::mfea_register_startup() { if (! _is_finder_alive) return; // The Finder is dead if (_is_mfea_registered) return; // Already registered PimNode::incr_startup_requests_n(); // XXX: for MFEA registration PimNode::incr_startup_requests_n(); // XXX: for MFEA birth PimNode::incr_startup_requests_n(); // XXX: for the ifmgr // // Register interest in the FEA with the Finder // add_task(new RegisterUnregisterInterest(*this, _mfea_target, true)); } // // De-register with the FEA // void XrlPimNode::fea_register_shutdown() { if (! _is_finder_alive) return; // The Finder is dead if (! _is_fea_alive) return; // The FEA is not there anymore if (! _is_fea_registered) return; // Not registered PimNode::incr_shutdown_requests_n(); // XXX: for FEA deregistration // // De-register interest in the FEA with the Finder // add_task(new RegisterUnregisterInterest(*this, _fea_target, false)); } // // De-register with the MFEA // void XrlPimNode::mfea_register_shutdown() { if (! _is_finder_alive) return; // The Finder is dead if (! _is_mfea_alive) return; // The MFEA is not there anymore if (! _is_mfea_registered) return; // Not registered PimNode::incr_shutdown_requests_n(); // XXX: for MFEA deregistration PimNode::incr_shutdown_requests_n(); // XXX: for the ifmgr // // De-register interest in the MFEA with the Finder // add_task(new RegisterUnregisterInterest(*this, _mfea_target, false)); // // XXX: when the shutdown is completed, PimNode::status_change() // will be called. // _ifmgr.shutdown(); } void XrlPimNode::send_register_unregister_interest() { bool success = true; if (! _is_finder_alive) return; // The Finder is dead XLOG_ASSERT(! _xrl_tasks_queue.empty()); XrlTaskBase* xrl_task_base = _xrl_tasks_queue.front(); RegisterUnregisterInterest* entry; entry = dynamic_cast(xrl_task_base); XLOG_ASSERT(entry != NULL); if (entry->is_register()) { // Register interest success = _xrl_finder_client.send_register_class_event_interest( _finder_target.c_str(), _instance_name, entry->target_name(), callback(this, &XrlPimNode::finder_send_register_unregister_interest_cb)); } else { // Unregister interest success = _xrl_finder_client.send_deregister_class_event_interest( _finder_target.c_str(), _instance_name, entry->target_name(), callback(this, &XrlPimNode::finder_send_register_unregister_interest_cb)); } if (! success) { // // If an error, then try again // XLOG_ERROR("Failed to %s register interest in %s with the Finder. " "Will try again.", entry->operation_name(), entry->target_name().c_str()); retry_xrl_task(); return; } } void XrlPimNode::finder_send_register_unregister_interest_cb(const XrlError& xrl_error) { XLOG_ASSERT(! _xrl_tasks_queue.empty()); XrlTaskBase* xrl_task_base = _xrl_tasks_queue.front(); RegisterUnregisterInterest* entry; entry = dynamic_cast(xrl_task_base); XLOG_ASSERT(entry != NULL); switch (xrl_error.error_code()) { case OKAY: // // If success, then schedule the next task // if (entry->is_register()) { // // Register interest // if (entry->target_name() == _fea_target) { // // If success, then the FEA birth event will startup the FEA // registration. // _is_fea_registered = true; PimNode::decr_startup_requests_n(); // XXX: for FEA registration } if (entry->target_name() == _mfea_target) { // // If success, then the MFEA birth event will startup the MFEA // registration and the ifmgr. // _is_mfea_registered = true; PimNode::decr_startup_requests_n(); // XXX: for MFEA registration } } else { // // Unregister interest // if (entry->target_name() == _fea_target) { _is_fea_registered = false; PimNode::decr_shutdown_requests_n(); // XXX: for the FEA } if (entry->target_name() == _mfea_target) { _is_mfea_registered = false; PimNode::decr_shutdown_requests_n(); // XXX: for the MFEA } } break; case COMMAND_FAILED: // // If a command failed because the other side rejected it, this is // fatal. // XLOG_FATAL("Cannot %s interest in Finder events: %s", entry->operation_name(), xrl_error.str().c_str()); break; case NO_FINDER: case RESOLVE_FAILED: case SEND_FAILED: // // A communication error that should have been caught elsewhere // (e.g., by tracking the status of the Finder and the other targets). // Probably we caught it here because of event reordering. // In some cases we print an error. In other cases our job is done. // if (entry->is_register()) { XLOG_ERROR("XRL communication error: %s", xrl_error.str().c_str()); } else { if (entry->target_name() == _fea_target) { _is_fea_registered = false; } if (entry->target_name() == _mfea_target) { _is_mfea_registered = false; } } break; case BAD_ARGS: case NO_SUCH_METHOD: case INTERNAL_ERROR: // // An error that should happen only if there is something unusual: // e.g., there is XRL mismatch, no enough internal resources, etc. // We don't try to recover from such errors, hence this is fatal. // XLOG_FATAL("Fatal XRL error: %s", xrl_error.str().c_str()); break; case REPLY_TIMED_OUT: case SEND_FAILED_TRANSIENT: // // If a transient error, then try again // XLOG_ERROR("Failed to %s interest in Finder envents: %s. " "Will try again.", entry->operation_name(), xrl_error.str().c_str()); retry_xrl_task(); return; } pop_xrl_task(); send_xrl_task(); } // // Register with the RIB // void XrlPimNode::rib_register_startup() { bool success; _rib_register_startup_timer.unschedule(); _rib_register_shutdown_timer.unschedule(); if (! _is_finder_alive) return; // The Finder is dead if (_is_rib_registered) return; // Already registered if (! _is_rib_registering) { if (! _is_rib_redist_transaction_enabled) PimNode::incr_startup_requests_n(); // XXX: for the RIB _is_rib_registering = true; } // // Register interest in the RIB with the Finder // success = _xrl_finder_client.send_register_class_event_interest( _finder_target.c_str(), _instance_name, _rib_target, callback(this, &XrlPimNode::finder_register_interest_rib_cb)); if (! success) { // // If an error, then try again // _rib_register_startup_timer = _eventloop.new_oneoff_after( RETRY_TIMEVAL, callback(this, &XrlPimNode::rib_register_startup)); return; } } void XrlPimNode::finder_register_interest_rib_cb(const XrlError& xrl_error) { switch (xrl_error.error_code()) { case OKAY: // // If success, then the RIB birth event will startup the RIB // registration. // _is_rib_registering = false; _is_rib_registered = true; break; case COMMAND_FAILED: // // If a command failed because the other side rejected it, this is // fatal. // XLOG_FATAL("Cannot register interest in Finder events: %s", xrl_error.str().c_str()); break; case NO_FINDER: case RESOLVE_FAILED: case SEND_FAILED: // // A communication error that should have been caught elsewhere // (e.g., by tracking the status of the Finder and the other targets). // Probably we caught it here because of event reordering. // In some cases we print an error. In other cases our job is done. // XLOG_ERROR("XRL communication error: %s", xrl_error.str().c_str()); break; case BAD_ARGS: case NO_SUCH_METHOD: case INTERNAL_ERROR: // // An error that should happen only if there is something unusual: // e.g., there is XRL mismatch, no enough internal resources, etc. // We don't try to recover from such errors, hence this is fatal. // XLOG_FATAL("Fatal XRL error: %s", xrl_error.str().c_str()); break; case REPLY_TIMED_OUT: case SEND_FAILED_TRANSIENT: // // If a transient error, then try again // if (! _rib_register_startup_timer.scheduled()) { XLOG_ERROR("Failed to register interest in Finder events: %s. " "Will try again.", xrl_error.str().c_str()); _rib_register_startup_timer = _eventloop.new_oneoff_after( RETRY_TIMEVAL, callback(this, &XrlPimNode::rib_register_startup)); } break; } } // // De-register with the RIB // void XrlPimNode::rib_register_shutdown() { bool success; _rib_register_startup_timer.unschedule(); _rib_register_shutdown_timer.unschedule(); if (! _is_finder_alive) return; // The Finder is dead if (! _is_rib_alive) return; // The RIB is not there anymore if (! _is_rib_registered) return; // Not registered if (! _is_rib_deregistering) { if (_is_rib_redist_transaction_enabled) { PimNode::incr_shutdown_requests_n(); // XXX: for the RIB } _is_rib_deregistering = true; } // // De-register interest in the RIB with the Finder // success = _xrl_finder_client.send_deregister_class_event_interest( _finder_target.c_str(), _instance_name, _rib_target, callback(this, &XrlPimNode::finder_deregister_interest_rib_cb)); if (! success) { // // If an error, then try again // _rib_register_shutdown_timer = _eventloop.new_oneoff_after( RETRY_TIMEVAL, callback(this, &XrlPimNode::rib_register_shutdown)); return; } send_rib_redist_transaction_disable(); } void XrlPimNode::finder_deregister_interest_rib_cb(const XrlError& xrl_error) { switch (xrl_error.error_code()) { case OKAY: // // If success, then we are done // _is_rib_deregistering = false; _is_rib_registered = false; break; case COMMAND_FAILED: // // If a command failed because the other side rejected it, this is // fatal. // XLOG_FATAL("Cannot deregister interest in Finder events: %s", xrl_error.str().c_str()); break; case NO_FINDER: case RESOLVE_FAILED: case SEND_FAILED: // // A communication error that should have been caught elsewhere // (e.g., by tracking the status of the Finder and the other targets). // Probably we caught it here because of event reordering. // In some cases we print an error. In other cases our job is done. // _is_rib_deregistering = false; _is_rib_registered = false; break; case BAD_ARGS: case NO_SUCH_METHOD: case INTERNAL_ERROR: // // An error that should happen only if there is something unusual: // e.g., there is XRL mismatch, no enough internal resources, etc. // We don't try to recover from such errors, hence this is fatal. // XLOG_FATAL("Fatal XRL error: %s", xrl_error.str().c_str()); break; case REPLY_TIMED_OUT: case SEND_FAILED_TRANSIENT: // // If a transient error, then try again // if (! _rib_register_shutdown_timer.scheduled()) { XLOG_ERROR("Failed to deregister interest in Finder events: %s. " "Will try again.", xrl_error.str().c_str()); _rib_register_shutdown_timer = _eventloop.new_oneoff_after( RETRY_TIMEVAL, callback(this, &XrlPimNode::rib_register_shutdown)); } break; } } // // Enable receiving MRIB information from the RIB // void XrlPimNode::send_rib_redist_transaction_enable() { bool success = true; if (! _is_finder_alive) return; // The Finder is dead if (! _is_rib_redist_transaction_enabled) { if (PimNode::is_ipv4()) { success = _xrl_rib_client.send_redist_transaction_enable4( _rib_target.c_str(), xrl_router().class_name(), string("all"), // TODO: XXX: hard-coded value false, /* unicast */ true, /* multicast */ IPv4Net(IPv4::ZERO(), 0), // XXX: get the whole table string("all"), // TODO: XXX: hard-coded value callback(this, &XrlPimNode::rib_client_send_redist_transaction_enable_cb)); if (success) return; } #ifdef HAVE_IPV6 if (PimNode::is_ipv6()) { success = _xrl_rib_client.send_redist_transaction_enable6( _rib_target.c_str(), xrl_router().class_name(), string("all"), // TODO: XXX: hard-coded value false, /* unicast */ true, /* multicast */ IPv6Net(IPv6::ZERO(), 0), // XXX: get the whole table string("all"), // TODO: XXX: hard-coded value callback(this, &XrlPimNode::rib_client_send_redist_transaction_enable_cb)); if (success) return; } #endif } if (! success) { // // If an error, then try again // XLOG_ERROR("Failed to enable receiving MRIB information from the RIB. " "Will try again."); _rib_redist_transaction_enable_timer = _eventloop.new_oneoff_after( RETRY_TIMEVAL, callback(this, &XrlPimNode::send_rib_redist_transaction_enable)); return; } } void XrlPimNode::rib_client_send_redist_transaction_enable_cb( const XrlError& xrl_error) { switch (xrl_error.error_code()) { case OKAY: // // If success, then we are done // _is_rib_redist_transaction_enabled = true; PimNode::decr_startup_requests_n(); // XXX: for the RIB break; case COMMAND_FAILED: // // If a command failed because the other side rejected it, this is // fatal. // XLOG_FATAL("Cannot enable receiving MRIB information from the RIB: %s", xrl_error.str().c_str()); break; case NO_FINDER: case RESOLVE_FAILED: case SEND_FAILED: // // A communication error that should have been caught elsewhere // (e.g., by tracking the status of the Finder and the other targets). // Probably we caught it here because of event reordering. // In some cases we print an error. In other cases our job is done. // XLOG_ERROR("XRL communication error: %s", xrl_error.str().c_str()); break; case BAD_ARGS: case NO_SUCH_METHOD: case INTERNAL_ERROR: // // An error that should happen only if there is something unusual: // e.g., there is XRL mismatch, no enough internal resources, etc. // We don't try to recover from such errors, hence this is fatal. // XLOG_FATAL("Fatal XRL error: %s", xrl_error.str().c_str()); break; case REPLY_TIMED_OUT: case SEND_FAILED_TRANSIENT: // // If a transient error, then try again // if (! _rib_redist_transaction_enable_timer.scheduled()) { XLOG_ERROR("Failed to enable receiving MRIB information from the RIB: %s. " "Will try again.", xrl_error.str().c_str()); _rib_redist_transaction_enable_timer = _eventloop.new_oneoff_after( RETRY_TIMEVAL, callback(this, &XrlPimNode::send_rib_redist_transaction_enable)); } break; } } // // Disable receiving MRIB information from the RIB // void XrlPimNode::send_rib_redist_transaction_disable() { bool success = true; if (! _is_finder_alive) return; // The Finder is dead if (_is_rib_redist_transaction_enabled) { if (PimNode::is_ipv4()) { bool success4; success4 = _xrl_rib_client.send_redist_transaction_disable4( _rib_target.c_str(), xrl_router().class_name(), string("all"), // TODO: XXX: hard-coded value false, /* unicast */ true, /* multicast */ string("all"), // TODO: XXX: hard-coded value callback(this, &XrlPimNode::rib_client_send_redist_transaction_disable_cb)); if (success4 != true) success = false; } #ifdef HAVE_IPV6 if (PimNode::is_ipv6()) { bool success6; success6 = _xrl_rib_client.send_redist_transaction_disable6( _rib_target.c_str(), xrl_router().class_name(), string("all"), // TODO: XXX: hard-coded value false, /* unicast */ true, /* multicast */ string("all"), // TODO: XXX: hard-coded value callback(this, &XrlPimNode::rib_client_send_redist_transaction_disable_cb)); if (success6 != true) success = false; } #endif } if (! success) { XLOG_ERROR("Failed to disable receiving MRIB information from the RIB. " "Will give up."); PimNode::set_status(SERVICE_FAILED); PimNode::update_status(); } } void XrlPimNode::rib_client_send_redist_transaction_disable_cb( const XrlError& xrl_error) { switch (xrl_error.error_code()) { case OKAY: // // If success, then we are done // _is_rib_redist_transaction_enabled = false; PimNode::decr_shutdown_requests_n(); // XXX: for the RIB break; case COMMAND_FAILED: // // If a command failed because the other side rejected it, this is // fatal. // XLOG_FATAL("Cannot disable receiving MRIB information from the RIB: %s", xrl_error.str().c_str()); break; case NO_FINDER: case RESOLVE_FAILED: case SEND_FAILED: // // A communication error that should have been caught elsewhere // (e.g., by tracking the status of the Finder and the other targets). // Probably we caught it here because of event reordering. // In some cases we print an error. In other cases our job is done. // _is_rib_redist_transaction_enabled = false; PimNode::decr_shutdown_requests_n(); // XXX: for the RIB break; case BAD_ARGS: case NO_SUCH_METHOD: case INTERNAL_ERROR: // // An error that should happen only if there is something unusual: // e.g., there is XRL mismatch, no enough internal resources, etc. // We don't try to recover from such errors, hence this is fatal. // XLOG_FATAL("Fatal XRL error: %s", xrl_error.str().c_str()); break; case REPLY_TIMED_OUT: case SEND_FAILED_TRANSIENT: // // If a transient error, then try again // if (! _rib_register_shutdown_timer.scheduled()) { XLOG_ERROR("Failed to disable receiving MRIB information from the RIB: %s. " "Will try again.", xrl_error.str().c_str()); _rib_register_shutdown_timer = _eventloop.new_oneoff_after( RETRY_TIMEVAL, callback(this, &XrlPimNode::rib_register_shutdown)); } break; } } int XrlPimNode::register_receiver(const string& if_name, const string& vif_name, uint8_t ip_protocol, bool enable_multicast_loopback) { PimNode::incr_startup_requests_n(); // XXX: for FEA-receiver add_task(new RegisterUnregisterReceiver(*this, if_name, vif_name, ip_protocol, enable_multicast_loopback, true)); return (XORP_OK); } int XrlPimNode::unregister_receiver(const string& if_name, const string& vif_name, uint8_t ip_protocol) { PimNode::incr_shutdown_requests_n(); // XXX: for FEA-non-receiver add_task(new RegisterUnregisterReceiver(*this, if_name, vif_name, ip_protocol, false, // XXX: ignored false)); return (XORP_OK); } void XrlPimNode::send_register_unregister_receiver() { bool success = true; if (! _is_finder_alive) return; // The Finder is dead XLOG_ASSERT(! _xrl_tasks_queue.empty()); XrlTaskBase* xrl_task_base = _xrl_tasks_queue.front(); RegisterUnregisterReceiver* entry; entry = dynamic_cast(xrl_task_base); XLOG_ASSERT(entry != NULL); // // Check whether we have already registered with the FEA // if (! _is_fea_registered) { retry_xrl_task(); return; } if (entry->is_register()) { // Register a receiver with the FEA if (PimNode::is_ipv4()) { success = _xrl_fea_client4.send_register_receiver( _fea_target.c_str(), xrl_router().instance_name(), entry->if_name(), entry->vif_name(), entry->ip_protocol(), entry->enable_multicast_loopback(), callback(this, &XrlPimNode::fea_client_send_register_unregister_receiver_cb)); if (success) return; } #ifdef HAVE_IPV6 if (PimNode::is_ipv6()) { success = _xrl_fea_client6.send_register_receiver( _fea_target.c_str(), xrl_router().instance_name(), entry->if_name(), entry->vif_name(), entry->ip_protocol(), entry->enable_multicast_loopback(), callback(this, &XrlPimNode::fea_client_send_register_unregister_receiver_cb)); if (success) return; } #endif } else { // Unregister a receiver with the FEA if (PimNode::is_ipv4()) { success = _xrl_fea_client4.send_unregister_receiver( _fea_target.c_str(), xrl_router().instance_name(), entry->if_name(), entry->vif_name(), entry->ip_protocol(), callback(this, &XrlPimNode::fea_client_send_register_unregister_receiver_cb)); if (success) return; } #ifdef HAVE_IPV6 if (PimNode::is_ipv6()) { success = _xrl_fea_client6.send_unregister_receiver( _fea_target.c_str(), xrl_router().instance_name(), entry->if_name(), entry->vif_name(), entry->ip_protocol(), callback(this, &XrlPimNode::fea_client_send_register_unregister_receiver_cb)); if (success) return; } #endif } if (! success) { // // If an error, then try again // XLOG_ERROR("Failed to %s register receiver on interface %s vif %s " "IP protocol %u with the FEA. " "Will try again.", entry->operation_name(), entry->if_name().c_str(), entry->vif_name().c_str(), entry->ip_protocol()); retry_xrl_task(); return; } } void XrlPimNode::fea_client_send_register_unregister_receiver_cb( const XrlError& xrl_error) { XLOG_ASSERT(! _xrl_tasks_queue.empty()); XrlTaskBase* xrl_task_base = _xrl_tasks_queue.front(); RegisterUnregisterReceiver* entry; entry = dynamic_cast(xrl_task_base); XLOG_ASSERT(entry != NULL); switch (xrl_error.error_code()) { case OKAY: // // If success, then schedule the next task // if (entry->is_register()) PimNode::decr_startup_requests_n(); // XXX: for FEA-receiver else { PimNode::decr_shutdown_requests_n(); // XXX: for FEA-non-receiver } break; case COMMAND_FAILED: // // If a command failed because the other side rejected it, this is // fatal. // XLOG_FATAL("Cannot %s receiver with the FEA: %s", entry->operation_name(), xrl_error.str().c_str()); break; case NO_FINDER: case RESOLVE_FAILED: case SEND_FAILED: // // A communication error that should have been caught elsewhere // (e.g., by tracking the status of the Finder and the other targets). // Probably we caught it here because of event reordering. // In some cases we print an error. In other cases our job is done. // if (entry->is_register()) { XLOG_ERROR("XRL communication error: %s", xrl_error.str().c_str()); } else { PimNode::decr_shutdown_requests_n(); // XXX: for FEA-non-receiver } break; case BAD_ARGS: case NO_SUCH_METHOD: case INTERNAL_ERROR: // // An error that should happen only if there is something unusual: // e.g., there is XRL mismatch, no enough internal resources, etc. // We don't try to recover from such errors, hence this is fatal. // XLOG_FATAL("Fatal XRL error: %s", xrl_error.str().c_str()); break; case REPLY_TIMED_OUT: case SEND_FAILED_TRANSIENT: // // If a transient error, then try again // XLOG_ERROR("Failed to %s receiver with the FEA: %s. " "Will try again.", entry->operation_name(), xrl_error.str().c_str()); retry_xrl_task(); return; } pop_xrl_task(); send_xrl_task(); } int XrlPimNode::register_protocol(const string& if_name, const string& vif_name, uint8_t ip_protocol) { PimNode::incr_startup_requests_n(); // XXX: for MFEA-protocol add_task(new RegisterUnregisterProtocol(*this, if_name, vif_name, ip_protocol, true)); return (XORP_OK); } int XrlPimNode::unregister_protocol(const string& if_name, const string& vif_name) { PimNode::incr_shutdown_requests_n(); // XXX: for MFEA-non-protocol add_task(new RegisterUnregisterProtocol(*this, if_name, vif_name, 0, // XXX: ignored false)); return (XORP_OK); } void XrlPimNode::send_register_unregister_protocol() { bool success = true; if (! _is_finder_alive) return; // The Finder is dead XLOG_ASSERT(! _xrl_tasks_queue.empty()); XrlTaskBase* xrl_task_base = _xrl_tasks_queue.front(); RegisterUnregisterProtocol* entry; entry = dynamic_cast(xrl_task_base); XLOG_ASSERT(entry != NULL); // // Check whether we have already registered with the MFEA // if (! _is_mfea_registered) { retry_xrl_task(); return; } if (entry->is_register()) { // Register a protocol with the MFEA if (PimNode::is_ipv4()) { success = _xrl_mfea_client.send_register_protocol4( _mfea_target.c_str(), xrl_router().class_name(), entry->if_name(), entry->vif_name(), entry->ip_protocol(), callback(this, &XrlPimNode::mfea_client_send_register_unregister_protocol_cb)); if (success) return; } #ifdef HAVE_IPV6 if (PimNode::is_ipv6()) { success = _xrl_mfea_client.send_register_protocol6( _mfea_target.c_str(), xrl_router().class_name(), entry->if_name(), entry->vif_name(), entry->ip_protocol(), callback(this, &XrlPimNode::mfea_client_send_register_unregister_protocol_cb)); if (success) return; } #endif } else { // Unregister a protocol with the MFEA if (PimNode::is_ipv4()) { success = _xrl_mfea_client.send_unregister_protocol4( _mfea_target.c_str(), xrl_router().class_name(), entry->if_name(), entry->vif_name(), callback(this, &XrlPimNode::mfea_client_send_register_unregister_protocol_cb)); if (success) return; } #ifdef HAVE_IPV6 if (PimNode::is_ipv6()) { success = _xrl_mfea_client.send_unregister_protocol6( _mfea_target.c_str(), xrl_router().class_name(), entry->if_name(), entry->vif_name(), callback(this, &XrlPimNode::mfea_client_send_register_unregister_protocol_cb)); if (success) return; } #endif } if (! success) { // // If an error, then try again // XLOG_ERROR("Failed to %s register protocol on interface %s vif %s " "IP protocol %u with the MFEA. " "Will try again.", entry->operation_name(), entry->if_name().c_str(), entry->vif_name().c_str(), entry->ip_protocol()); retry_xrl_task(); return; } } void XrlPimNode::mfea_client_send_register_unregister_protocol_cb( const XrlError& xrl_error) { XLOG_ASSERT(! _xrl_tasks_queue.empty()); XrlTaskBase* xrl_task_base = _xrl_tasks_queue.front(); RegisterUnregisterProtocol* entry; entry = dynamic_cast(xrl_task_base); XLOG_ASSERT(entry != NULL); switch (xrl_error.error_code()) { case OKAY: // // If success, then schedule the next task // if (entry->is_register()) PimNode::decr_startup_requests_n(); // XXX: for MFEA-protocol else PimNode::decr_shutdown_requests_n(); // XXX: for MFEA-non-protocol break; case COMMAND_FAILED: // // If a command failed because the other side rejected it, this is // fatal. // Shouldn't be fatal, network device can suddenly dissappear, for instance. // XLOG_ERROR("Cannot %s protocol with the MFEA: %s", entry->operation_name(), xrl_error.str().c_str()); break; case NO_FINDER: case RESOLVE_FAILED: case SEND_FAILED: // // A communication error that should have been caught elsewhere // (e.g., by tracking the status of the Finder and the other targets). // Probably we caught it here because of event reordering. // In some cases we print an error. In other cases our job is done. // if (entry->is_register()) { XLOG_ERROR("XRL communication error: %s", xrl_error.str().c_str()); } else { PimNode::decr_shutdown_requests_n(); // XXX: for MFEA-non-protocol } break; case BAD_ARGS: case NO_SUCH_METHOD: case INTERNAL_ERROR: // // An error that should happen only if there is something unusual: // e.g., there is XRL mismatch, no enough internal resources, etc. // We don't try to recover from such errors, hence this is fatal. // XLOG_FATAL("Fatal XRL error: %s", xrl_error.str().c_str()); break; case REPLY_TIMED_OUT: case SEND_FAILED_TRANSIENT: // // If a transient error, then try again // XLOG_ERROR("Failed to %s protocol with the MFEA: %s. " "Will try again.", entry->operation_name(), xrl_error.str().c_str()); retry_xrl_task(); return; } pop_xrl_task(); send_xrl_task(); } int XrlPimNode::join_multicast_group(const string& if_name, const string& vif_name, uint8_t ip_protocol, const IPvX& group_address) { PimNode::incr_startup_requests_n(); // XXX: for FEA-join add_task(new JoinLeaveMulticastGroup(*this, if_name, vif_name, ip_protocol, group_address, true)); return (XORP_OK); } int XrlPimNode::leave_multicast_group(const string& if_name, const string& vif_name, uint8_t ip_protocol, const IPvX& group_address) { PimNode::incr_shutdown_requests_n(); // XXX: for FEA-leave add_task(new JoinLeaveMulticastGroup(*this, if_name, vif_name, ip_protocol, group_address, false)); return (XORP_OK); } void XrlPimNode::send_join_leave_multicast_group() { bool success = true; if (! _is_finder_alive) return; // The Finder is dead XLOG_ASSERT(! _xrl_tasks_queue.empty()); XrlTaskBase* xrl_task_base = _xrl_tasks_queue.front(); JoinLeaveMulticastGroup* entry; entry = dynamic_cast(xrl_task_base); XLOG_ASSERT(entry != NULL); // // Check whether we have already registered with the FEA // if (! _is_fea_registered) { retry_xrl_task(); return; } if (entry->is_join()) { // Join a multicast group with the FEA if (PimNode::is_ipv4()) { success = _xrl_fea_client4.send_join_multicast_group( _fea_target.c_str(), xrl_router().instance_name(), entry->if_name(), entry->vif_name(), entry->ip_protocol(), entry->group_address().get_ipv4(), callback(this, &XrlPimNode::fea_client_send_join_leave_multicast_group_cb)); if (success) return; } #ifdef HAVE_IPV6 if (PimNode::is_ipv6()) { success = _xrl_fea_client6.send_join_multicast_group( _fea_target.c_str(), xrl_router().instance_name(), entry->if_name(), entry->vif_name(), entry->ip_protocol(), entry->group_address().get_ipv6(), callback(this, &XrlPimNode::fea_client_send_join_leave_multicast_group_cb)); if (success) return; } #endif } else { // Leave a multicast group with the FEA if (PimNode::is_ipv4()) { success = _xrl_fea_client4.send_leave_multicast_group( _fea_target.c_str(), xrl_router().instance_name(), entry->if_name(), entry->vif_name(), entry->ip_protocol(), entry->group_address().get_ipv4(), callback(this, &XrlPimNode::fea_client_send_join_leave_multicast_group_cb)); if (success) return; } #ifdef HAVE_IPV6 if (PimNode::is_ipv6()) { success = _xrl_fea_client6.send_leave_multicast_group( _fea_target.c_str(), xrl_router().instance_name(), entry->if_name(), entry->vif_name(), entry->ip_protocol(), entry->group_address().get_ipv6(), callback(this, &XrlPimNode::fea_client_send_join_leave_multicast_group_cb)); if (success) return; } #endif } if (! success) { // // If an error, then try again // XLOG_ERROR("Failed to %s group %s on interface/vif %s/%s with the FEA. " "Will try again.", entry->operation_name(), entry->group_address().str().c_str(), entry->if_name().c_str(), entry->vif_name().c_str()); retry_xrl_task(); return; } } void XrlPimNode::fea_client_send_join_leave_multicast_group_cb( const XrlError& xrl_error) { XLOG_ASSERT(! _xrl_tasks_queue.empty()); XrlTaskBase* xrl_task_base = _xrl_tasks_queue.front(); JoinLeaveMulticastGroup* entry; entry = dynamic_cast(xrl_task_base); XLOG_ASSERT(entry != NULL); switch (xrl_error.error_code()) { case OKAY: // // If success, then schedule the next task // if (entry->is_join()) PimNode::decr_startup_requests_n(); // XXX: for FEA-join else PimNode::decr_shutdown_requests_n(); // XXX: for FEA-leave break; case COMMAND_FAILED: // // If a command failed because the other side rejected it, this is // fatal. // XLOG_WARNING("Cannot %s a multicast group with the FEA: %s", entry->operation_name(), xrl_error.str().c_str()); break; case NO_FINDER: case RESOLVE_FAILED: case SEND_FAILED: // // A communication error that should have been caught elsewhere // (e.g., by tracking the status of the Finder and the other targets). // Probably we caught it here because of event reordering. // In some cases we print an error. In other cases our job is done. // if (entry->is_join()) { XLOG_ERROR("XRL communication error: %s", xrl_error.str().c_str()); } else { PimNode::decr_shutdown_requests_n(); // XXX: for FEA-leave } break; case BAD_ARGS: case NO_SUCH_METHOD: case INTERNAL_ERROR: // // An error that should happen only if there is something unusual: // e.g., there is XRL mismatch, no enough internal resources, etc. // We don't try to recover from such errors, hence this is fatal. // XLOG_FATAL("Fatal XRL error: %s", xrl_error.str().c_str()); break; case REPLY_TIMED_OUT: case SEND_FAILED_TRANSIENT: // // If a transient error, then try again // XLOG_ERROR("Failed to %s group %s on interface/vif %s/%s " "with the FEA: %s. " "Will try again.", entry->operation_name(), entry->group_address().str().c_str(), entry->if_name().c_str(), entry->vif_name().c_str(), xrl_error.str().c_str()); retry_xrl_task(); return; } pop_xrl_task(); send_xrl_task(); } int XrlPimNode::add_mfc_to_kernel(const PimMfc& pim_mfc) { add_task(new AddDeleteMfc(*this, pim_mfc, true)); return (XORP_OK); } int XrlPimNode::delete_mfc_from_kernel(const PimMfc& pim_mfc) { add_task(new AddDeleteMfc(*this, pim_mfc, false)); return (XORP_OK); } void XrlPimNode::send_add_delete_mfc() { bool success = true; if (! _is_finder_alive) return; // The Finder is dead XLOG_ASSERT(! _xrl_tasks_queue.empty()); XrlTaskBase* xrl_task_base = _xrl_tasks_queue.front(); AddDeleteMfc* entry; entry = dynamic_cast(xrl_task_base); XLOG_ASSERT(entry != NULL); size_t max_vifs_oiflist = entry->olist().size(); const IPvX& source_addr = entry->source_addr(); const IPvX& group_addr = entry->group_addr(); uint32_t iif_vif_index = entry->iif_vif_index(); const IPvX& rp_addr = entry->rp_addr(); vector oiflist_vector(max_vifs_oiflist); vector oiflist_disable_wrongvif_vector(max_vifs_oiflist); mifset_to_vector(entry->olist(), oiflist_vector); mifset_to_vector(entry->olist_disable_wrongvif(), oiflist_disable_wrongvif_vector); // // Check whether we have already registered with the MFEA // if (! _is_mfea_registered) { retry_xrl_task(); return; } if (entry->is_add()) { // Add a MFC with the MFEA if (PimNode::is_ipv4()) { success = _xrl_mfea_client.send_add_mfc4( _mfea_target.c_str(), xrl_router().class_name(), source_addr.get_ipv4(), group_addr.get_ipv4(), iif_vif_index, oiflist_vector, oiflist_disable_wrongvif_vector, max_vifs_oiflist, rp_addr.get_ipv4(), callback(this, &XrlPimNode::mfea_client_send_add_delete_mfc_cb)); if (success) return; } #ifdef HAVE_IPV6 if (PimNode::is_ipv6()) { success = _xrl_mfea_client.send_add_mfc6( _mfea_target.c_str(), xrl_router().class_name(), source_addr.get_ipv6(), group_addr.get_ipv6(), iif_vif_index, oiflist_vector, oiflist_disable_wrongvif_vector, max_vifs_oiflist, rp_addr.get_ipv6(), callback(this, &XrlPimNode::mfea_client_send_add_delete_mfc_cb)); if (success) return; } #endif } else { // Delete a MFC with the MFEA if (PimNode::is_ipv4()) { success = _xrl_mfea_client.send_delete_mfc4( _mfea_target.c_str(), xrl_router().class_name(), source_addr.get_ipv4(), group_addr.get_ipv4(), callback(this, &XrlPimNode::mfea_client_send_add_delete_mfc_cb)); if (success) return; } #ifdef HAVE_IPV6 if (PimNode::is_ipv6()) { success = _xrl_mfea_client.send_delete_mfc6( _mfea_target.c_str(), xrl_router().class_name(), source_addr.get_ipv6(), group_addr.get_ipv6(), callback(this, &XrlPimNode::mfea_client_send_add_delete_mfc_cb)); if (success) return; } #endif } if (! success) { // // If an error, then try again // XLOG_ERROR("Failed to %s MFC entry for (%s, %s) with the MFEA. " "Will try again.", entry->operation_name(), cstring(source_addr), cstring(group_addr)); retry_xrl_task(); } } void XrlPimNode::mfea_client_send_add_delete_mfc_cb( const XrlError& xrl_error) { XLOG_ASSERT(! _xrl_tasks_queue.empty()); XrlTaskBase* xrl_task_base = _xrl_tasks_queue.front(); AddDeleteMfc* entry; entry = dynamic_cast(xrl_task_base); XLOG_ASSERT(entry != NULL); switch (xrl_error.error_code()) { case OKAY: // // If success, then schedule the next task // break; case COMMAND_FAILED: // // If a command failed because the other side rejected it, // then print an error and send the next one. // XLOG_ERROR("Cannot %s a multicast forwarding entry with the MFEA: %s", entry->operation_name(), xrl_error.str().c_str()); break; case NO_FINDER: case RESOLVE_FAILED: case SEND_FAILED: // // A communication error that should have been caught elsewhere // (e.g., by tracking the status of the Finder and the other targets). // Probably we caught it here because of event reordering. // In some cases we print an error. In other cases our job is done. // XLOG_ERROR("XRL communication error: %s", xrl_error.str().c_str()); break; case BAD_ARGS: case NO_SUCH_METHOD: case INTERNAL_ERROR: // // An error that should happen only if there is something unusual: // e.g., there is XRL mismatch, no enough internal resources, etc. // We don't try to recover from such errors, hence this is fatal. // XLOG_FATAL("Fatal XRL error: %s", xrl_error.str().c_str()); break; case REPLY_TIMED_OUT: case SEND_FAILED_TRANSIENT: // // If a transient error, then try again // XLOG_ERROR("Failed to add/delete a multicast forwarding entry " "with the MFEA: %s. " "Will try again.", xrl_error.str().c_str()); retry_xrl_task(); return; } pop_xrl_task(); send_xrl_task(); } int XrlPimNode::add_dataflow_monitor(const IPvX& source_addr, const IPvX& group_addr, uint32_t threshold_interval_sec, uint32_t threshold_interval_usec, 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) { add_task(new AddDeleteDataflowMonitor(*this, source_addr, group_addr, threshold_interval_sec, threshold_interval_usec, threshold_packets, threshold_bytes, is_threshold_in_packets, is_threshold_in_bytes, is_geq_upcall, is_leq_upcall, true)); return (XORP_OK); } int XrlPimNode::delete_dataflow_monitor(const IPvX& source_addr, const IPvX& group_addr, uint32_t threshold_interval_sec, uint32_t threshold_interval_usec, 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) { add_task(new AddDeleteDataflowMonitor(*this, source_addr, group_addr, threshold_interval_sec, threshold_interval_usec, threshold_packets, threshold_bytes, is_threshold_in_packets, is_threshold_in_bytes, is_geq_upcall, is_leq_upcall, false)); return (XORP_OK); } int XrlPimNode::delete_all_dataflow_monitor(const IPvX& source_addr, const IPvX& group_addr) { add_task(new AddDeleteDataflowMonitor(*this, source_addr, group_addr)); return (XORP_OK); } void XrlPimNode::send_add_delete_dataflow_monitor() { bool success = true; if (! _is_finder_alive) return; // The Finder is dead XLOG_ASSERT(! _xrl_tasks_queue.empty()); XrlTaskBase* xrl_task_base = _xrl_tasks_queue.front(); AddDeleteDataflowMonitor* entry; entry = dynamic_cast(xrl_task_base); XLOG_ASSERT(entry != NULL); // // Check whether we have already registered with the MFEA // if (! _is_mfea_registered) { retry_xrl_task(); return; } if (entry->is_delete_all()) { // Delete all dataflow monitors for a source and a group addresses if (PimNode::is_ipv4()) { success = _xrl_mfea_client.send_delete_all_dataflow_monitor4( _mfea_target.c_str(), xrl_router().class_name(), entry->source_addr().get_ipv4(), entry->group_addr().get_ipv4(), callback(this, &XrlPimNode::mfea_client_send_add_delete_dataflow_monitor_cb)); if (success) return; } #ifdef HAVE_IPV6 if (PimNode::is_ipv6()) { success = _xrl_mfea_client.send_delete_all_dataflow_monitor6( _mfea_target.c_str(), xrl_router().class_name(), entry->source_addr().get_ipv6(), entry->group_addr().get_ipv6(), callback(this, &XrlPimNode::mfea_client_send_add_delete_dataflow_monitor_cb)); if (success) return; } #endif } else { if (entry->is_add()) { // Add a dataflow monitor with the MFEA if (PimNode::is_ipv4()) { success = _xrl_mfea_client.send_add_dataflow_monitor4( _mfea_target.c_str(), xrl_router().class_name(), entry->source_addr().get_ipv4(), entry->group_addr().get_ipv4(), entry->threshold_interval_sec(), entry->threshold_interval_usec(), entry->threshold_packets(), entry->threshold_bytes(), entry->is_threshold_in_packets(), entry->is_threshold_in_bytes(), entry->is_geq_upcall(), entry->is_leq_upcall(), callback(this, &XrlPimNode::mfea_client_send_add_delete_dataflow_monitor_cb)); if (success) return; } #ifdef HAVE_IPV6 if (PimNode::is_ipv6()) { success = _xrl_mfea_client.send_add_dataflow_monitor6( _mfea_target.c_str(), xrl_router().class_name(), entry->source_addr().get_ipv6(), entry->group_addr().get_ipv6(), entry->threshold_interval_sec(), entry->threshold_interval_usec(), entry->threshold_packets(), entry->threshold_bytes(), entry->is_threshold_in_packets(), entry->is_threshold_in_bytes(), entry->is_geq_upcall(), entry->is_leq_upcall(), callback(this, &XrlPimNode::mfea_client_send_add_delete_dataflow_monitor_cb)); if (success) return; } #endif } else { // Delete a dataflow monitor with the MFEA if (PimNode::is_ipv4()) { success = _xrl_mfea_client.send_delete_dataflow_monitor4( _mfea_target.c_str(), xrl_router().class_name(), entry->source_addr().get_ipv4(), entry->group_addr().get_ipv4(), entry->threshold_interval_sec(), entry->threshold_interval_usec(), entry->threshold_packets(), entry->threshold_bytes(), entry->is_threshold_in_packets(), entry->is_threshold_in_bytes(), entry->is_geq_upcall(), entry->is_leq_upcall(), callback(this, &XrlPimNode::mfea_client_send_add_delete_dataflow_monitor_cb)); if (success) return; } #ifdef HAVE_IPV6 if (PimNode::is_ipv6()) { success = _xrl_mfea_client.send_delete_dataflow_monitor6( _mfea_target.c_str(), xrl_router().class_name(), entry->source_addr().get_ipv6(), entry->group_addr().get_ipv6(), entry->threshold_interval_sec(), entry->threshold_interval_usec(), entry->threshold_packets(), entry->threshold_bytes(), entry->is_threshold_in_packets(), entry->is_threshold_in_bytes(), entry->is_geq_upcall(), entry->is_leq_upcall(), callback(this, &XrlPimNode::mfea_client_send_add_delete_dataflow_monitor_cb)); if (success) return; } #endif } } if (! success) { // // If an error, then try again // XLOG_ERROR("Failed to %s dataflow monitor entry for (%s, %s) " "with the MFEA. " "Will try again.", entry->operation_name(), cstring(entry->source_addr()), cstring(entry->group_addr())); retry_xrl_task(); } } void XrlPimNode::mfea_client_send_add_delete_dataflow_monitor_cb( const XrlError& xrl_error) { XLOG_ASSERT(! _xrl_tasks_queue.empty()); XrlTaskBase* xrl_task_base = _xrl_tasks_queue.front(); AddDeleteDataflowMonitor* entry; entry = dynamic_cast(xrl_task_base); XLOG_ASSERT(entry != NULL); switch (xrl_error.error_code()) { case OKAY: // // If success, then schedule the next task // break; case COMMAND_FAILED: XLOG_ERROR("Cannot %s dataflow monitor entry with the MFEA: %s", entry->operation_name(), xrl_error.str().c_str()); break; case NO_FINDER: case RESOLVE_FAILED: case SEND_FAILED: // // A communication error that should have been caught elsewhere // (e.g., by tracking the status of the Finder and the other targets). // Probably we caught it here because of event reordering. // In some cases we print an error. In other cases our job is done. // XLOG_ERROR("XRL communication error: %s", xrl_error.str().c_str()); break; case BAD_ARGS: case NO_SUCH_METHOD: case INTERNAL_ERROR: // // An error that should happen only if there is something unusual: // e.g., there is XRL mismatch, no enough internal resources, etc. // We don't try to recover from such errors, hence this is fatal. // XLOG_FATAL("Fatal XRL error: %s", xrl_error.str().c_str()); break; case REPLY_TIMED_OUT: case SEND_FAILED_TRANSIENT: // // If a transient error, then try again // XLOG_ERROR("Failed to %s dataflow monitor entry with the MFEA: %s. " "Will try again.", entry->operation_name(), xrl_error.str().c_str()); retry_xrl_task(); return; } pop_xrl_task(); send_xrl_task(); } int XrlPimNode::add_protocol_mld6igmp(uint32_t vif_index) { PimVif *pim_vif = PimNode::vif_find_by_vif_index(vif_index); if (pim_vif == NULL) { XLOG_ERROR("Cannot add protocol with MLD6IGMP " "for vif with vif_index %u: " "no such vif", XORP_UINT_CAST(vif_index)); return (XORP_ERROR); } PimNode::incr_startup_requests_n(); // XXX: for MLD6IGMP-add _add_delete_protocol_mld6igmp_queue.push_back(make_pair(vif_index, true)); _add_protocol_mld6igmp_vif_index_set.insert(vif_index); // If the queue was empty before, start sending the changes if (_add_delete_protocol_mld6igmp_queue.size() == 1) { send_add_delete_protocol_mld6igmp(); } return (XORP_OK); } int XrlPimNode::delete_protocol_mld6igmp(uint32_t vif_index) { PimVif *pim_vif = PimNode::vif_find_by_vif_index(vif_index); if (pim_vif == NULL) { XLOG_ERROR("Cannot delete protocol with MLD6IGMP " "for vif with vif_index %u: " "no such vif", XORP_UINT_CAST(vif_index)); return (XORP_ERROR); } PimNode::incr_shutdown_requests_n(); // XXX: for MLD6IGMP-delete _add_delete_protocol_mld6igmp_queue.push_back(make_pair(vif_index, false)); _add_protocol_mld6igmp_vif_index_set.erase(vif_index); // If the queue was empty before, start sending the changes if (_add_delete_protocol_mld6igmp_queue.size() == 1) { send_add_delete_protocol_mld6igmp(); } return (XORP_OK); } void XrlPimNode::send_add_delete_protocol_mld6igmp() { bool success = true; PimVif *pim_vif = NULL; if (! _is_finder_alive) return; // The Finder is dead if (_add_delete_protocol_mld6igmp_queue.empty()) return; // No more changes uint32_t vif_index = _add_delete_protocol_mld6igmp_queue.front().first; bool is_add = _add_delete_protocol_mld6igmp_queue.front().second; pim_vif = PimNode::vif_find_by_vif_index(vif_index); if (pim_vif == NULL) { XLOG_ERROR("Cannot %s vif with vif_index %u with the MLD6IGMP: " "no such vif", (is_add)? "add" : "delete", XORP_UINT_CAST(vif_index)); _add_delete_protocol_mld6igmp_queue.pop_front(); goto start_timer_label; } if (is_add) { // // Register the protocol with the MLD6IGMP for membership // change on this interface. // if (PimNode::is_ipv4()) { success = _xrl_mld6igmp_client.send_add_protocol4( _mld6igmp_target.c_str(), xrl_router().class_name(), string(PimNode::module_name()), PimNode::module_id(), pim_vif->name(), vif_index, callback(this, &XrlPimNode::mld6igmp_client_send_add_delete_protocol_mld6igmp_cb)); if (success) return; } if (PimNode::is_ipv6()) { success = _xrl_mld6igmp_client.send_add_protocol6( _mld6igmp_target.c_str(), xrl_router().class_name(), string(PimNode::module_name()), PimNode::module_id(), pim_vif->name(), vif_index, callback(this, &XrlPimNode::mld6igmp_client_send_add_delete_protocol_mld6igmp_cb)); if (success) return; } } else { // // De-register the protocol with the MLD6IGMP for membership // change on this interface. // if (PimNode::is_ipv4()) { success = _xrl_mld6igmp_client.send_delete_protocol4( _mld6igmp_target.c_str(), xrl_router().class_name(), string(PimNode::module_name()), PimNode::module_id(), pim_vif->name(), vif_index, callback(this, &XrlPimNode::mld6igmp_client_send_add_delete_protocol_mld6igmp_cb)); if (success) return; } if (PimNode::is_ipv6()) { success = _xrl_mld6igmp_client.send_delete_protocol6( _mld6igmp_target.c_str(), xrl_router().class_name(), string(PimNode::module_name()), PimNode::module_id(), pim_vif->name(), vif_index, callback(this, &XrlPimNode::mld6igmp_client_send_add_delete_protocol_mld6igmp_cb)); if (success) return; } } if (! success) { // // If an error, then try again // XLOG_ERROR("Cannot %s vif %s with the MLD6IGMP. " "Will try again.", (is_add)? "add" : "delete", pim_vif->name().c_str()); start_timer_label: _add_delete_protocol_mld6igmp_queue_timer = _eventloop.new_oneoff_after( RETRY_TIMEVAL, callback(this, &XrlPimNode::send_add_delete_protocol_mld6igmp)); } } void XrlPimNode::mld6igmp_client_send_add_delete_protocol_mld6igmp_cb( const XrlError& xrl_error) { bool is_add = _add_delete_protocol_mld6igmp_queue.front().second; switch (xrl_error.error_code()) { case OKAY: // // If success, then schedule the next task // if (is_add) PimNode::decr_startup_requests_n(); // XXX: for MLD6IGMP-add else PimNode::decr_shutdown_requests_n(); // XXX: for MLD6IGMP-delete _add_delete_protocol_mld6igmp_queue.pop_front(); send_add_delete_protocol_mld6igmp(); break; case COMMAND_FAILED: // // These errors can be caused by VIFs suddenly dissappearing. For now, // register failure is still considered fatal. // if (is_add) { XLOG_FATAL("Cannot register with the MLD6IGMP: %s", xrl_error.str().c_str()); } else { XLOG_WARNING("Cannot deregister with the MLD6IGMP: %s", xrl_error.str().c_str()); } break; case NO_FINDER: case RESOLVE_FAILED: case SEND_FAILED: // // A communication error that should have been caught elsewhere // (e.g., by tracking the status of the Finder and the other targets). // Probably we caught it here because of event reordering. // In some cases we print an error. In other cases our job is done. // if (is_add) { XLOG_ERROR("XRL communication error: %s", xrl_error.str().c_str()); } else { PimNode::decr_shutdown_requests_n(); // XXX: for MLD6IGMP-delete _add_delete_protocol_mld6igmp_queue.pop_front(); send_add_delete_protocol_mld6igmp(); } break; case BAD_ARGS: case NO_SUCH_METHOD: case INTERNAL_ERROR: // // An error that should happen only if there is something unusual: // e.g., there is XRL mismatch, no enough internal resources, etc. // We don't try to recover from such errors, hence this is fatal. // XLOG_FATAL("Fatal XRL error: %s", xrl_error.str().c_str()); break; case REPLY_TIMED_OUT: case SEND_FAILED_TRANSIENT: // // If a transient error, then try again // if (! _add_delete_protocol_mld6igmp_queue_timer.scheduled()) { XLOG_ERROR("Failed to %s with the MLD6IGMP: %s. " "Will try again.", (is_add)? "register" : "deregister", xrl_error.str().c_str()); _add_delete_protocol_mld6igmp_queue_timer = _eventloop.new_oneoff_after( RETRY_TIMEVAL, callback(this, &XrlPimNode::send_add_delete_protocol_mld6igmp)); } break; } } // // Schedule protocol registration right after MLD/IGMP becomes alive // void XrlPimNode::schedule_add_protocol_mld6igmp() { set::const_iterator set_iter; list >::const_iterator list_iter; // // Create a copy of the set with the vifs to add, so we can edit it later // set tmp_set = _add_protocol_mld6igmp_vif_index_set; // // Remove from the copy those vifs that are already scheduled to be added. // for (list_iter = _add_delete_protocol_mld6igmp_queue.begin(); list_iter != _add_delete_protocol_mld6igmp_queue.end(); ++list_iter) { uint32_t vif_index = list_iter->first; bool is_add = list_iter->second; if (! is_add) continue; tmp_set.erase(vif_index); } // // Add the remaining vifs // for (set_iter = tmp_set.begin(); set_iter != tmp_set.end(); ++set_iter) { uint32_t vif_index = *set_iter; add_protocol_mld6igmp(vif_index); } } // // Protocol node methods // int XrlPimNode::proto_send(const string& if_name, const string& vif_name, const IPvX& src_address, const IPvX& dst_address, uint8_t ip_protocol, int32_t ip_ttl, int32_t ip_tos, bool ip_router_alert, bool ip_internet_control, const uint8_t* sndbuf, size_t sndlen, string& error_msg) { add_task(new SendProtocolMessage(*this, if_name, vif_name, src_address, dst_address, ip_protocol, ip_ttl, ip_tos, ip_router_alert, ip_internet_control, sndbuf, sndlen)); error_msg = ""; return (XORP_OK); } /** * Send a protocol message through the FEA. **/ void XrlPimNode::send_protocol_message() { bool success = true; if (! _is_finder_alive) { XLOG_ERROR("ERROR: XrlPimNode::send_protocol_message, finder is NOT alive!\n"); return; // The Finder is dead } XLOG_ASSERT(! _xrl_tasks_queue.empty()); XrlTaskBase* xrl_task_base = _xrl_tasks_queue.front(); SendProtocolMessage* entry; entry = dynamic_cast(xrl_task_base); XLOG_ASSERT(entry != NULL); //XLOG_ERROR("XrlPimNode::send_protocol_message interface/vif %s/%s. ", // entry->if_name().c_str(), // entry->vif_name().c_str()); // // Check whether we have already registered with the FEA // if (! _is_fea_registered) { XLOG_ERROR("ERROR: XrlPimNode::send_protocol_message, finder is NOT registered!\n"); retry_xrl_task(); return; } // // Send the protocol message // do { if (PimNode::is_ipv4()) { success = _xrl_fea_client4.send_send( _fea_target.c_str(), entry->if_name(), entry->vif_name(), entry->src_address().get_ipv4(), entry->dst_address().get_ipv4(), entry->ip_protocol(), entry->ip_ttl(), entry->ip_tos(), entry->ip_router_alert(), entry->ip_internet_control(), entry->payload(), callback(this, &XrlPimNode::fea_client_send_protocol_message_cb)); if (success) return; break; } #ifdef HAVE_IPV6 if (PimNode::is_ipv6()) { // XXX: no Extention headers XrlAtomList ext_headers_type; XrlAtomList ext_headers_payload; success = _xrl_fea_client6.send_send( _fea_target.c_str(), entry->if_name(), entry->vif_name(), entry->src_address().get_ipv6(), entry->dst_address().get_ipv6(), entry->ip_protocol(), entry->ip_ttl(), entry->ip_tos(), entry->ip_router_alert(), entry->ip_internet_control(), ext_headers_type, ext_headers_payload, entry->payload(), callback(this, &XrlPimNode::fea_client_send_protocol_message_cb)); if (success) return; break; } XLOG_UNREACHABLE(); #endif break; } while (false); if (! success) { // // If an error, then try again // XLOG_ERROR("Failed to send a protocol message on interface/vif %s/%s. " "Will try again.", entry->if_name().c_str(), entry->vif_name().c_str()); retry_xrl_task(); return; } } void XrlPimNode::fea_client_send_protocol_message_cb(const XrlError& xrl_error) { XLOG_ASSERT(! _xrl_tasks_queue.empty()); XrlTaskBase* xrl_task_base = _xrl_tasks_queue.front(); SendProtocolMessage* entry; entry = dynamic_cast(xrl_task_base); XLOG_ASSERT(entry != NULL); switch (xrl_error.error_code()) { case OKAY: // // If success, then schedule the next task // break; case COMMAND_FAILED: // // If a command failed because the other side rejected it, // then print an error and send the next one. // // XXX: The FEA may fail to send a protocol message, therefore // we don't call XLOG_FATAL() here. For example, the transimssion // by the FEA it may fail if there is no buffer space or if an // unicast destination is not reachable. // Furthermore, all protocol messages are soft-state (i.e., they are // retransmitted periodically by the protocol), // hence we don't retransmit them here if there was an error. // XLOG_ERROR("Cannot send a protocol message: %s", xrl_error.str().c_str()); break; case NO_FINDER: case RESOLVE_FAILED: case SEND_FAILED: // // A communication error that should have been caught elsewhere // (e.g., by tracking the status of the Finder and the other targets). // Probably we caught it here because of event reordering. // In some cases we print an error. In other cases our job is done. // XLOG_ERROR("Cannot send a protocol message: %s", xrl_error.str().c_str()); break; case BAD_ARGS: case NO_SUCH_METHOD: case INTERNAL_ERROR: // // An error that should happen only if there is something unusual: // e.g., there is XRL mismatch, no enough internal resources, etc. // We don't try to recover from such errors, hence this is fatal. // XLOG_FATAL("Fatal XRL error: %s", xrl_error.str().c_str()); break; case REPLY_TIMED_OUT: case SEND_FAILED_TRANSIENT: // // XXX: if a transient error, then don't try again. // All protocol messages are soft-state (i.e., they are // retransmitted periodically by the protocol), // hence we don't retransmit them here if there was an error. // XLOG_ERROR("Failed to send a protocol message: %s", xrl_error.str().c_str()); break; } // In all cases, do the next task. pop_xrl_task(); send_xrl_task(); } // // Protocol node CLI methods // int XrlPimNode::add_cli_command_to_cli_manager(const char *command_name, const char *command_help, bool is_command_cd, const char *command_cd_prompt, bool is_command_processor ) { bool success = true; if (! _is_finder_alive) return (XORP_ERROR); // The Finder is dead success = _xrl_cli_manager_client.send_add_cli_command( xorp_module_name(family(), XORP_MODULE_CLI), xrl_router().class_name(), string(command_name), string(command_help), is_command_cd, string(command_cd_prompt), is_command_processor, callback(this, &XrlPimNode::cli_manager_client_send_add_cli_command_cb)); if (! success) { XLOG_ERROR("Failed to add CLI command '%s' to the CLI manager", command_name); return (XORP_ERROR); } return (XORP_OK); } void XrlPimNode::cli_manager_client_send_add_cli_command_cb( const XrlError& xrl_error) { switch (xrl_error.error_code()) { case OKAY: // // If success, then we are done // break; case COMMAND_FAILED: // // If a command failed because the other side rejected it, this is // fatal. // XLOG_FATAL("Cannot add a command to CLI manager: %s", xrl_error.str().c_str()); break; case NO_FINDER: case RESOLVE_FAILED: case SEND_FAILED: // // A communication error that should have been caught elsewhere // (e.g., by tracking the status of the Finder and the other targets). // Probably we caught it here because of event reordering. // In some cases we print an error. In other cases our job is done. // XLOG_ERROR("Cannot add a command to CLI manager: %s", xrl_error.str().c_str()); break; case BAD_ARGS: case NO_SUCH_METHOD: case INTERNAL_ERROR: // // An error that should happen only if there is something unusual: // e.g., there is XRL mismatch, no enough internal resources, etc. // We don't try to recover from such errors, hence this is fatal. // XLOG_FATAL("Fatal XRL error: %s", xrl_error.str().c_str()); break; case REPLY_TIMED_OUT: case SEND_FAILED_TRANSIENT: // // If a transient error, then try again // // // TODO: if the command failed, then we should retransmit it // XLOG_ERROR("Failed to add a command to CLI manager: %s", xrl_error.str().c_str()); break; } } int XrlPimNode::delete_cli_command_from_cli_manager(const char *command_name) { bool success = true; if (! _is_finder_alive) return (XORP_ERROR); // The Finder is dead success = _xrl_cli_manager_client.send_delete_cli_command( xorp_module_name(family(), XORP_MODULE_CLI), xrl_router().class_name(), string(command_name), callback(this, &XrlPimNode::cli_manager_client_send_delete_cli_command_cb)); if (! success) { XLOG_ERROR("Failed to delete CLI command '%s' with the CLI manager", command_name); return (XORP_ERROR); } return (XORP_OK); } void XrlPimNode::cli_manager_client_send_delete_cli_command_cb( const XrlError& xrl_error) { switch (xrl_error.error_code()) { case OKAY: // // If success, then we are done // break; case COMMAND_FAILED: // // If a command failed because the other side rejected it, this is // fatal. // XLOG_FATAL("Cannot delete a command from CLI manager: %s", xrl_error.str().c_str()); break; case NO_FINDER: case RESOLVE_FAILED: case SEND_FAILED: // // A communication error that should have been caught elsewhere // (e.g., by tracking the status of the Finder and the other targets). // Probably we caught it here because of event reordering. // In some cases we print an error. In other cases our job is done. // XLOG_ERROR("Cannot delete a command from CLI manager: %s", xrl_error.str().c_str()); break; case BAD_ARGS: case NO_SUCH_METHOD: case INTERNAL_ERROR: // // An error that should happen only if there is something unusual: // e.g., there is XRL mismatch, no enough internal resources, etc. // We don't try to recover from such errors, hence this is fatal. // XLOG_FATAL("Fatal XRL error: %s", xrl_error.str().c_str()); break; case REPLY_TIMED_OUT: case SEND_FAILED_TRANSIENT: // // If a transient error, then try again // // // TODO: if the command failed, then we should retransmit it // XLOG_ERROR("Failed to delete a command from CLI manager: %s", xrl_error.str().c_str()); break; } } // // XRL target methods // XrlCmdError XrlPimNode::common_0_1_get_target_name( // Output values, string& name) { name = xrl_router().class_name(); return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::common_0_1_get_version( // Output values, string& version) { version = XORP_MODULE_VERSION; return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::common_0_1_get_status(// Output values, uint32_t& status, string& reason) { status = PimNode::node_status(reason); return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::common_0_1_shutdown() { if (shutdown() != XORP_OK) { string error_msg = c_format("Failed to shutdown PIM"); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::common_0_1_startup() { if (startup() != XORP_OK) { string error_msg = c_format("Failed to startup PIM"); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } /** * Announce target birth to observer. * * @param target_class the target class name. * * @param target_instance the target instance name. */ XrlCmdError XrlPimNode::finder_event_observer_0_1_xrl_target_birth( // Input values, const string& target_class, const string& target_instance) { if (target_class == _fea_target) { _is_fea_alive = true; PimNode::decr_startup_requests_n(); // XXX: for FEA birth } if (target_class == _mfea_target) { _is_mfea_alive = true; PimNode::decr_startup_requests_n(); // XXX: for MFEA birth // // XXX: when the startup is completed, // IfMgrHintObserver::tree_complete() will be called. // if (_ifmgr.startup() != XORP_OK) { PimNode::set_status(SERVICE_FAILED); PimNode::update_status(); } } if (target_class == _rib_target) { _is_rib_alive = true; send_rib_redist_transaction_enable(); } if (target_class == _mld6igmp_target) { _is_mld6igmp_alive = true; send_add_delete_protocol_mld6igmp(); schedule_add_protocol_mld6igmp(); } return XrlCmdError::OKAY(); UNUSED(target_instance); } /** * Announce target death to observer. * * @param target_class the target class name. * * @param target_instance the target instance name. */ XrlCmdError XrlPimNode::finder_event_observer_0_1_xrl_target_death( // Input values, const string& target_class, const string& target_instance) { bool do_shutdown = false; UNUSED(target_instance); if (target_class == _fea_target) { XLOG_ERROR("FEA (instance %s) has died, shutting down.", target_instance.c_str()); _is_fea_alive = false; do_shutdown = true; } if (target_class == _mfea_target) { XLOG_ERROR("MFEA (instance %s) has died, shutting down.", target_instance.c_str()); _is_mfea_alive = false; do_shutdown = true; } if (target_class == _rib_target) { XLOG_ERROR("RIB (instance %s) has died, shutting down.", target_instance.c_str()); _is_rib_alive = false; do_shutdown = true; } if (target_class == _mld6igmp_target) { XLOG_INFO("MLD/IGMP (instance %s) has died.", target_instance.c_str()); _is_mld6igmp_alive = false; } if (do_shutdown) stop_pim(); return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::cli_processor_0_1_process_command( // Input values, const string& processor_name, const string& cli_term_name, const uint32_t& cli_session_id, const string& command_name, const string& command_args, // Output values, string& ret_processor_name, string& ret_cli_term_name, uint32_t& ret_cli_session_id, string& ret_command_output) { PimNodeCli::cli_process_command(processor_name, cli_term_name, cli_session_id, command_name, command_args, ret_processor_name, ret_cli_term_name, ret_cli_session_id, ret_command_output); return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::raw_packet4_client_0_1_recv( // Input values, const string& if_name, const string& vif_name, const IPv4& src_address, const IPv4& dst_address, const uint32_t& ip_protocol, const int32_t& ip_ttl, const int32_t& ip_tos, const bool& ip_router_alert, const bool& ip_internet_control, const vector& payload) { string error_msg; // // Verify the address family // if (! PimNode::is_ipv4()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv4"); return XrlCmdError::COMMAND_FAILED(error_msg); } // // Receive the message // PimNode::proto_recv(if_name, vif_name, IPvX(src_address), IPvX(dst_address), ip_protocol, ip_ttl, ip_tos, ip_router_alert, ip_internet_control, payload, error_msg); // XXX: no error returned, because if there is any, it is at the // protocol level, and the FEA shoudn't care about it. // // Success // return XrlCmdError::OKAY(); } #ifdef HAVE_IPV6 XrlCmdError XrlPimNode::raw_packet6_client_0_1_recv( // Input values, const string& if_name, const string& vif_name, const IPv6& src_address, const IPv6& dst_address, const uint32_t& ip_protocol, const int32_t& ip_ttl, const int32_t& ip_tos, const bool& ip_router_alert, const bool& ip_internet_control, const XrlAtomList& ext_headers_type, const XrlAtomList& ext_headers_payload, const vector& payload) { string error_msg; UNUSED(ext_headers_type); UNUSED(ext_headers_payload); // // Verify the address family // if (! PimNode::is_ipv6()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv6"); return XrlCmdError::COMMAND_FAILED(error_msg); } // // Receive the message // PimNode::proto_recv(if_name, vif_name, IPvX(src_address), IPvX(dst_address), ip_protocol, ip_ttl, ip_tos, ip_router_alert, ip_internet_control, payload, error_msg); // XXX: no error returned, because if there is any, it is at the // protocol level, and the FEA shoudn't care about it. // // Success // return XrlCmdError::OKAY(); } #endif XrlCmdError XrlPimNode::mfea_client_0_1_recv_kernel_signal_message4( // Input values, const string& xrl_sender_name, const uint32_t& message_type, const string& , // vif_name, const uint32_t& vif_index, const IPv4& source_address, const IPv4& dest_address, const vector& protocol_message) { string error_msg; // // Verify the address family // if (! PimNode::is_ipv4()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv4"); return XrlCmdError::COMMAND_FAILED(error_msg); } // // Receive the kernel signal message // PimNode::signal_message_recv(xrl_sender_name, message_type, vif_index, IPvX(source_address), IPvX(dest_address), &protocol_message[0], protocol_message.size()); // XXX: no error returned, because if there is any, it is at the // protocol level, and the MFEA shoudn't care about it. // // Success // return XrlCmdError::OKAY(); } #ifdef HAVE_IPV6 XrlCmdError XrlPimNode::mfea_client_0_1_recv_kernel_signal_message6( // Input values, const string& xrl_sender_name, const uint32_t& message_type, const string& , // vif_name, const uint32_t& vif_index, const IPv6& source_address, const IPv6& dest_address, const vector& protocol_message) { string error_msg; // // Verify the address family // if (! PimNode::is_ipv6()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv6"); return XrlCmdError::COMMAND_FAILED(error_msg); } // // Receive the kernel signal message // PimNode::signal_message_recv(xrl_sender_name, message_type, vif_index, IPvX(source_address), IPvX(dest_address), &protocol_message[0], protocol_message.size()); // XXX: no error returned, because if there is any, it is at the // protocol level, and the MFEA shoudn't care about it. // // Success // return XrlCmdError::OKAY(); } #endif XrlCmdError XrlPimNode::mfea_client_0_1_recv_dataflow_signal4( // Input values, const string& , // xrl_sender_name, const IPv4& source_address, const IPv4& group_address, const uint32_t& threshold_interval_sec, const uint32_t& threshold_interval_usec, const uint32_t& measured_interval_sec, const uint32_t& measured_interval_usec, const uint32_t& threshold_packets, const uint32_t& threshold_bytes, const uint32_t& measured_packets, const uint32_t& measured_bytes, const bool& is_threshold_in_packets, const bool& is_threshold_in_bytes, const bool& is_geq_upcall, const bool& is_leq_upcall) { string error_msg; // // Verify the address family // if (! PimNode::is_ipv4()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv4"); return XrlCmdError::COMMAND_FAILED(error_msg); } // // Deliver a signal that a dataflow-related pre-condition is true. // PimNode::pim_mrt().signal_dataflow_recv( IPvX(source_address), IPvX(group_address), 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); // XXX: we don't care if the signal delivery failed return XrlCmdError::OKAY(); } #ifdef HAVE_IPV6 XrlCmdError XrlPimNode::mfea_client_0_1_recv_dataflow_signal6( // Input values, const string& , // xrl_sender_name, const IPv6& source_address, const IPv6& group_address, const uint32_t& threshold_interval_sec, const uint32_t& threshold_interval_usec, const uint32_t& measured_interval_sec, const uint32_t& measured_interval_usec, const uint32_t& threshold_packets, const uint32_t& threshold_bytes, const uint32_t& measured_packets, const uint32_t& measured_bytes, const bool& is_threshold_in_packets, const bool& is_threshold_in_bytes, const bool& is_geq_upcall, const bool& is_leq_upcall) { string error_msg; // // Verify the address family // if (! PimNode::is_ipv6()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv6"); return XrlCmdError::COMMAND_FAILED(error_msg); } // // Deliver a signal that a dataflow-related pre-condition is true. // PimNode::pim_mrt().signal_dataflow_recv( IPvX(source_address), IPvX(group_address), 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); // XXX: we don't care if the signal delivery failed return XrlCmdError::OKAY(); } #endif XrlCmdError XrlPimNode::redist_transaction4_0_1_start_transaction( // Output values, uint32_t& tid) { string error_msg; // // Verify the address family // if (! PimNode::is_ipv4()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv4"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (_mrib_transaction_manager.start(tid) != true) { error_msg = c_format("Resource limit on number of pending " "transactions hit"); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::redist_transaction4_0_1_commit_transaction( // Input values, const uint32_t& tid) { string error_msg; // // Verify the address family // if (! PimNode::is_ipv4()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv4"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (_mrib_transaction_manager.commit(tid) != true) { error_msg = c_format("Cannot commit MRIB transaction for tid %u", XORP_UINT_CAST(tid)); return XrlCmdError::COMMAND_FAILED(error_msg); } PimNode::pim_mrib_table().commit_pending_transactions(tid); return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::redist_transaction4_0_1_abort_transaction( // Input values, const uint32_t& tid) { string error_msg; // // Verify the address family // if (! PimNode::is_ipv4()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv4"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (_mrib_transaction_manager.abort(tid) != true) { error_msg = c_format("Cannot abort MRIB transaction for tid %u", XORP_UINT_CAST(tid)); return XrlCmdError::COMMAND_FAILED(error_msg); } PimNode::pim_mrib_table().abort_pending_transactions(tid); return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::redist_transaction4_0_1_add_route( // Input values, const uint32_t& tid, const IPv4Net& dst, const IPv4& nexthop, const string& ifname, const string& vifname, const uint32_t& metric, const uint32_t& admin_distance, const string& cookie, const string& protocol_origin) { string error_msg; uint32_t vif_index = Vif::VIF_INDEX_INVALID; PimVif *pim_vif = PimNode::vif_find_by_name(vifname); UNUSED(ifname); UNUSED(cookie); UNUSED(protocol_origin); // // Verify the address family // if (! PimNode::is_ipv4()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv4"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (pim_vif != NULL) vif_index = pim_vif->vif_index(); // // Create the Mrib entry // Mrib mrib = Mrib(IPvXNet(dst)); mrib.set_next_hop_router_addr(IPvX(nexthop)); mrib.set_next_hop_vif_index(vif_index); mrib.set_metric_preference(admin_distance); mrib.set_metric(metric); // // Add the Mrib to the list of pending transactions as an 'insert()' entry // PimNode::pim_mrib_table().add_pending_insert(tid, mrib, vifname); // // Success // return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::redist_transaction4_0_1_delete_route( // Input values, const uint32_t& tid, const IPv4Net& dst, const IPv4& nexthop, const string& ifname, const string& vifname, const uint32_t& metric, const uint32_t& admin_distance, const string& cookie, const string& protocol_origin) { string error_msg; UNUSED(nexthop); UNUSED(ifname); UNUSED(vifname); UNUSED(metric); UNUSED(admin_distance); UNUSED(cookie); UNUSED(protocol_origin); // // Verify the address family // if (! PimNode::is_ipv4()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv4"); return XrlCmdError::COMMAND_FAILED(error_msg); } // // Create the Mrib entry // Mrib mrib = Mrib(IPvXNet(dst)); // // Add the Mrib to the list of pending transactions as an 'remove()' entry // PimNode::pim_mrib_table().add_pending_remove(tid, mrib); // // Success // return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::redist_transaction4_0_1_delete_all_routes( // Input values, const uint32_t& tid, const string& cookie) { string error_msg; UNUSED(cookie); // // Verify the address family // if (! PimNode::is_ipv4()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv4"); return XrlCmdError::COMMAND_FAILED(error_msg); } // // Add a transaction item to remove all entries // PimNode::pim_mrib_table().add_pending_remove_all_entries(tid); // // Success // return XrlCmdError::OKAY(); } #ifdef HAVE_IPV6 XrlCmdError XrlPimNode::redist_transaction6_0_1_start_transaction( // Output values, uint32_t& tid) { string error_msg; // // Verify the address family // if (! PimNode::is_ipv6()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv6"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (_mrib_transaction_manager.start(tid) != true) { error_msg = c_format("Resource limit on number of pending " "transactions hit"); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::redist_transaction6_0_1_commit_transaction( // Input values, const uint32_t& tid) { string error_msg; // // Verify the address family // if (! PimNode::is_ipv6()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv6"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (_mrib_transaction_manager.commit(tid) != true) { error_msg = c_format("Cannot commit MRIB transaction for tid %u", XORP_UINT_CAST(tid)); return XrlCmdError::COMMAND_FAILED(error_msg); } PimNode::pim_mrib_table().commit_pending_transactions(tid); return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::redist_transaction6_0_1_abort_transaction( // Input values, const uint32_t& tid) { string error_msg; // // Verify the address family // if (! PimNode::is_ipv6()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv6"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (_mrib_transaction_manager.abort(tid) != true) { error_msg = c_format("Cannot abort MRIB transaction for tid %u", XORP_UINT_CAST(tid)); return XrlCmdError::COMMAND_FAILED(error_msg); } PimNode::pim_mrib_table().abort_pending_transactions(tid); return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::redist_transaction6_0_1_add_route( // Input values, const uint32_t& tid, const IPv6Net& dst, const IPv6& nexthop, const string& ifname, const string& vifname, const uint32_t& metric, const uint32_t& admin_distance, const string& cookie, const string& protocol_origin) { string error_msg; uint32_t vif_index = Vif::VIF_INDEX_INVALID; PimVif *pim_vif = PimNode::vif_find_by_name(vifname); UNUSED(ifname); UNUSED(cookie); UNUSED(protocol_origin); // // Verify the address family // if (! PimNode::is_ipv6()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv6"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (pim_vif != NULL) vif_index = pim_vif->vif_index(); // // Create the Mrib entry // Mrib mrib = Mrib(IPvXNet(dst)); mrib.set_next_hop_router_addr(IPvX(nexthop)); mrib.set_next_hop_vif_index(vif_index); mrib.set_metric_preference(admin_distance); mrib.set_metric(metric); // // Add the Mrib to the list of pending transactions as an 'insert()' entry // PimNode::pim_mrib_table().add_pending_insert(tid, mrib, vifname); // // Success // return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::redist_transaction6_0_1_delete_route( // Input values, const uint32_t& tid, const IPv6Net& dst, const IPv6& nexthop, const string& ifname, const string& vifname, const uint32_t& metric, const uint32_t& admin_distance, const string& cookie, const string& protocol_origin) { string error_msg; UNUSED(nexthop); UNUSED(ifname); UNUSED(vifname); UNUSED(metric); UNUSED(admin_distance); UNUSED(cookie); UNUSED(protocol_origin); // // Verify the address family // if (! PimNode::is_ipv6()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv6"); return XrlCmdError::COMMAND_FAILED(error_msg); } // // Create the Mrib entry // Mrib mrib = Mrib(IPvXNet(dst)); // // Add the Mrib to the list of pending transactions as an 'remove()' entry // PimNode::pim_mrib_table().add_pending_remove(tid, mrib); // // Success // return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::redist_transaction6_0_1_delete_all_routes( // Input values, const uint32_t& tid, const string& cookie) { string error_msg; UNUSED(cookie); // // Verify the address family // if (! PimNode::is_ipv6()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv6"); return XrlCmdError::COMMAND_FAILED(error_msg); } // // Add a transaction item to remove all entries // PimNode::pim_mrib_table().add_pending_remove_all_entries(tid); // // Success // return XrlCmdError::OKAY(); } #endif XrlCmdError XrlPimNode::mld6igmp_client_0_1_add_membership4( // Input values, const string& , // xrl_sender_name, const string& vif_name, const uint32_t& vif_index, const IPv4& source, const IPv4& group) { string error_msg; // // Verify the address family // if (! PimNode::is_ipv4()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv4"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (PimNode::add_membership(vif_index, IPvX(source), IPvX(group)) != XORP_OK) { error_msg = c_format("Failed to add membership for (%s, %s)" "on vif %s: %s", cstring(source), cstring(group), vif_name.c_str(), error_msg.c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::mld6igmp_client_0_1_add_membership6( // Input values, const string& , // xrl_sender_name, const string& vif_name, const uint32_t& vif_index, const IPv6& source, const IPv6& group) { string error_msg; // // Verify the address family // if (! PimNode::is_ipv6()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv6"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (PimNode::add_membership(vif_index, IPvX(source), IPvX(group)) != XORP_OK) { error_msg = c_format("Failed to add membership for (%s, %s)" "on vif %s: %s", cstring(source), cstring(group), vif_name.c_str(), error_msg.c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::mld6igmp_client_0_1_delete_membership4( // Input values, const string& , // xrl_sender_name, const string& vif_name, const uint32_t& vif_index, const IPv4& source, const IPv4& group) { string error_msg; // // Verify the address family // if (! PimNode::is_ipv4()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv4"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (PimNode::delete_membership(vif_index, IPvX(source), IPvX(group)) != XORP_OK) { error_msg = c_format("Failed to delete membership for (%s, %s)" "on vif %s: %s", cstring(source), cstring(group), vif_name.c_str(), error_msg.c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::mld6igmp_client_0_1_delete_membership6( // Input values, const string& , // xrl_sender_name, const string& vif_name, const uint32_t& vif_index, const IPv6& source, const IPv6& group) { string error_msg; // // Verify the address family // if (! PimNode::is_ipv6()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv6"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (PimNode::delete_membership(vif_index, IPvX(source), IPvX(group)) != XORP_OK) { error_msg = c_format("Failed to delete membership for (%s, %s)" "on vif %s: %s", cstring(source), cstring(group), vif_name.c_str(), error_msg.c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_enable_vif( // Input values, const string& vif_name, const bool& enable) { string error_msg; int ret_value; if (enable) ret_value = PimNode::enable_vif(vif_name, error_msg); else ret_value = PimNode::disable_vif(vif_name, error_msg); if (ret_value != XORP_OK) return XrlCmdError::COMMAND_FAILED(error_msg); return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_start_vif( // Input values, const string& vif_name) { string error_msg; if (PimNode::start_vif(vif_name, error_msg) != XORP_OK) return XrlCmdError::COMMAND_FAILED(error_msg); return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_stop_vif( // Input values, const string& vif_name) { string error_msg; if (PimNode::stop_vif(vif_name, error_msg) != XORP_OK) return XrlCmdError::COMMAND_FAILED(error_msg); return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_enable_all_vifs( // Input values, const bool& enable) { string error_msg; int ret_value; if (enable) ret_value = PimNode::enable_all_vifs(); else ret_value = PimNode::enable_all_vifs(); if (ret_value != XORP_OK) { if (enable) error_msg = c_format("Failed to enable all vifs"); else error_msg = c_format("Failed to disable all vifs"); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_start_all_vifs() { string error_msg; if (PimNode::start_all_vifs() != XORP_OK) { error_msg = c_format("Failed to start all vifs"); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_stop_all_vifs() { string error_msg; if (PimNode::stop_all_vifs() != XORP_OK) { error_msg = c_format("Failed to stop all vifs"); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_enable_pim( // Input values, const bool& enable) { string error_msg; int ret_value; if (enable) ret_value = enable_pim(); else ret_value = disable_pim(); if (ret_value != XORP_OK) { if (enable) error_msg = c_format("Failed to enable PIM"); else error_msg = c_format("Failed to disable PIM"); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_start_pim() { string error_msg; if (start_pim() != XORP_OK) { error_msg = c_format("Failed to start PIM"); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_stop_pim() { string error_msg; if (stop_pim() != XORP_OK) { error_msg = c_format("Failed to stop PIM"); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_enable_cli( // Input values, const bool& enable) { string error_msg; int ret_value; if (enable) ret_value = enable_cli(); else ret_value = disable_cli(); if (ret_value != XORP_OK) { if (enable) error_msg = c_format("Failed to enable PIM CLI"); else error_msg = c_format("Failed to disable PIM CLI"); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_start_cli() { string error_msg; if (start_cli() != XORP_OK) { error_msg = c_format("Failed to start PIM CLI"); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_stop_cli() { string error_msg; if (stop_cli() != XORP_OK) { error_msg = c_format("Failed to stop PIM CLI"); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_enable_bsr( // Input values, const bool& enable) { string error_msg; int ret_value; if (enable) ret_value = enable_bsr(); else ret_value = disable_bsr(); if (ret_value != XORP_OK) { if (enable) error_msg = c_format("Failed to enable PIM BSR"); else error_msg = c_format("Failed to disable PIM BSR"); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_start_bsr() { string error_msg; if (start_bsr() != XORP_OK) { error_msg = c_format("Failed to start PIM BSR"); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_stop_bsr() { string error_msg; if (stop_bsr() != XORP_OK) { error_msg = c_format("Failed to stop PIM BSR"); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_apply_bsr_changes() { string error_msg; if (PimNode::apply_bsr_changes(error_msg) != XORP_OK) return XrlCmdError::COMMAND_FAILED(error_msg); return XrlCmdError::OKAY(); } // // PIM configuration // XrlCmdError XrlPimNode::pim_0_1_add_config_scope_zone_by_vif_name4( // Input values, const IPv4Net& scope_zone_id, const string& vif_name) { string error_msg; // // Verify the address family // if (! PimNode::is_ipv4()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv4"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (PimNode::add_config_scope_zone_by_vif_name(IPvXNet(scope_zone_id), vif_name, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } #ifdef HAVE_IPV6 XrlCmdError XrlPimNode::pim_0_1_add_config_scope_zone_by_vif_name6( // Input values, const IPv6Net& scope_zone_id, const string& vif_name) { string error_msg; // // Verify the address family // if (! PimNode::is_ipv6()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv6"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (PimNode::add_config_scope_zone_by_vif_name(IPvXNet(scope_zone_id), vif_name, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_add_config_scope_zone_by_vif_addr6( // Input values, const IPv6Net& scope_zone_id, const IPv6& vif_addr) { string error_msg; // // Verify the address family // if (! PimNode::is_ipv6()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv6"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (PimNode::add_config_scope_zone_by_vif_addr(IPvXNet(scope_zone_id), IPvX(vif_addr), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_delete_config_scope_zone_by_vif_name6( // Input values, const IPv6Net& scope_zone_id, const string& vif_name) { string error_msg; // // Verify the address family // if (! PimNode::is_ipv6()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv6"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (PimNode::delete_config_scope_zone_by_vif_name(IPvXNet(scope_zone_id), vif_name, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } #endif XrlCmdError XrlPimNode::pim_0_1_add_config_scope_zone_by_vif_addr4( // Input values, const IPv4Net& scope_zone_id, const IPv4& vif_addr) { string error_msg; // // Verify the address family // if (! PimNode::is_ipv4()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv4"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (PimNode::add_config_scope_zone_by_vif_addr(IPvXNet(scope_zone_id), IPvX(vif_addr), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_delete_config_scope_zone_by_vif_name4( // Input values, const IPv4Net& scope_zone_id, const string& vif_name) { string error_msg; // // Verify the address family // if (! PimNode::is_ipv4()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv4"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (PimNode::delete_config_scope_zone_by_vif_name(IPvXNet(scope_zone_id), vif_name, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_delete_config_scope_zone_by_vif_addr4( // Input values, const IPv4Net& scope_zone_id, const IPv4& vif_addr) { string error_msg; // // Verify the address family // if (! PimNode::is_ipv4()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv4"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (PimNode::delete_config_scope_zone_by_vif_addr(IPvXNet(scope_zone_id), IPvX(vif_addr), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } #ifdef HAVE_IPV6 XrlCmdError XrlPimNode::pim_0_1_delete_config_scope_zone_by_vif_addr6( // Input values, const IPv6Net& scope_zone_id, const IPv6& vif_addr) { string error_msg; // // Verify the address family // if (! PimNode::is_ipv6()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv6"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (PimNode::delete_config_scope_zone_by_vif_addr(IPvXNet(scope_zone_id), IPvX(vif_addr), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_add_config_cand_bsr6( // Input values, const IPv6Net& scope_zone_id, const bool& is_scope_zone, const string& vif_name, const IPv6& vif_addr, const uint32_t& bsr_priority, const uint32_t& hash_mask_len) { string error_msg; // // Verify the address family // if (! PimNode::is_ipv6()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv6"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (bsr_priority > 0xff) { error_msg = c_format("Invalid BSR priority = %u", XORP_UINT_CAST(bsr_priority)); return XrlCmdError::COMMAND_FAILED(error_msg); } if (hash_mask_len > 0xff) { error_msg = c_format("Invalid hash mask length = %u", XORP_UINT_CAST(hash_mask_len)); return XrlCmdError::COMMAND_FAILED(error_msg); } if (PimNode::add_config_cand_bsr(IPvXNet(scope_zone_id), is_scope_zone, vif_name, IPvX(vif_addr), (uint8_t)(bsr_priority), (uint8_t)(hash_mask_len), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_delete_config_cand_bsr6( // Input values, const IPv6Net& scope_zone_id, const bool& is_scope_zone) { string error_msg; // // Verify the address family // if (! PimNode::is_ipv6()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv6"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (PimNode::delete_config_cand_bsr(IPvXNet(scope_zone_id), is_scope_zone, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } #endif XrlCmdError XrlPimNode::pim_0_1_add_config_cand_bsr4( // Input values, const IPv4Net& scope_zone_id, const bool& is_scope_zone, const string& vif_name, const IPv4& vif_addr, const uint32_t& bsr_priority, const uint32_t& hash_mask_len) { string error_msg; // // Verify the address family // if (! PimNode::is_ipv4()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv4"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (bsr_priority > 0xff) { error_msg = c_format("Invalid BSR priority = %u", XORP_UINT_CAST(bsr_priority)); return XrlCmdError::COMMAND_FAILED(error_msg); } if (hash_mask_len > 0xff) { error_msg = c_format("Invalid hash mask length = %u", XORP_UINT_CAST(hash_mask_len)); return XrlCmdError::COMMAND_FAILED(error_msg); } if (PimNode::add_config_cand_bsr(IPvXNet(scope_zone_id), is_scope_zone, vif_name, IPvX(vif_addr), (uint8_t)(bsr_priority), (uint8_t)(hash_mask_len), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_delete_config_cand_bsr4( // Input values, const IPv4Net& scope_zone_id, const bool& is_scope_zone) { string error_msg; // // Verify the address family // if (! PimNode::is_ipv4()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv4"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (PimNode::delete_config_cand_bsr(IPvXNet(scope_zone_id), is_scope_zone, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_add_config_cand_rp4( // Input values, const IPv4Net& group_prefix, const bool& is_scope_zone, const string& vif_name, const IPv4& vif_addr, const uint32_t& rp_priority, const uint32_t& rp_holdtime) { string error_msg; // // Verify the address family // if (! PimNode::is_ipv4()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv4"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (rp_priority > 0xff) { error_msg = c_format("Invalid RP priority = %u", XORP_UINT_CAST(rp_priority)); return XrlCmdError::COMMAND_FAILED(error_msg); } if (rp_holdtime > 0xffff) { error_msg = c_format("Invalid RP holdtime = %u", XORP_UINT_CAST(rp_holdtime)); return XrlCmdError::COMMAND_FAILED(error_msg); } if (PimNode::add_config_cand_rp(IPvXNet(group_prefix), is_scope_zone, vif_name, vif_addr, (uint8_t)(rp_priority), (uint16_t)(rp_holdtime), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } #ifdef HAVE_IPV6 XrlCmdError XrlPimNode::pim_0_1_add_config_cand_rp6( // Input values, const IPv6Net& group_prefix, const bool& is_scope_zone, const string& vif_name, const IPv6& vif_addr, const uint32_t& rp_priority, const uint32_t& rp_holdtime) { string error_msg; // // Verify the address family // if (! PimNode::is_ipv6()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv6"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (rp_priority > 0xff) { error_msg = c_format("Invalid RP priority = %u", XORP_UINT_CAST(rp_priority)); return XrlCmdError::COMMAND_FAILED(error_msg); } if (rp_holdtime > 0xffff) { error_msg = c_format("Invalid RP holdtime = %u", XORP_UINT_CAST(rp_holdtime)); return XrlCmdError::COMMAND_FAILED(error_msg); } if (PimNode::add_config_cand_rp(IPvXNet(group_prefix), is_scope_zone, vif_name, vif_addr, (uint8_t)(rp_priority), (uint16_t)(rp_holdtime), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_delete_config_cand_rp6( // Input values, const IPv6Net& group_prefix, const bool& is_scope_zone, const string& vif_name, const IPv6& vif_addr) { string error_msg; // // Verify the address family // if (! PimNode::is_ipv6()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv6"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (PimNode::delete_config_cand_rp(IPvXNet(group_prefix), is_scope_zone, vif_name, IPvX(vif_addr), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } #endif XrlCmdError XrlPimNode::pim_0_1_delete_config_cand_rp4( // Input values, const IPv4Net& group_prefix, const bool& is_scope_zone, const string& vif_name, const IPv4& vif_addr) { string error_msg; // // Verify the address family // if (! PimNode::is_ipv4()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv4"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (PimNode::delete_config_cand_rp(IPvXNet(group_prefix), is_scope_zone, vif_name, IPvX(vif_addr), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_add_config_static_rp4( // Input values, const IPv4Net& group_prefix, const IPv4& rp_addr, const uint32_t& rp_priority, const uint32_t& hash_mask_len) { string error_msg; // // Verify the address family // if (! PimNode::is_ipv4()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv4"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (rp_priority > 0xff) { error_msg = c_format("Invalid RP priority = %u", XORP_UINT_CAST(rp_priority)); return XrlCmdError::COMMAND_FAILED(error_msg); } if (hash_mask_len > 0xff) { error_msg = c_format("Invalid hash mask length = %u", XORP_UINT_CAST(hash_mask_len)); return XrlCmdError::COMMAND_FAILED(error_msg); } if (PimNode::add_config_static_rp(IPvXNet(group_prefix), IPvX(rp_addr), (uint8_t)(rp_priority), (uint8_t)(hash_mask_len), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } #ifdef HAVE_IPV6 XrlCmdError XrlPimNode::pim_0_1_add_config_static_rp6( // Input values, const IPv6Net& group_prefix, const IPv6& rp_addr, const uint32_t& rp_priority, const uint32_t& hash_mask_len) { string error_msg; // // Verify the address family // if (! PimNode::is_ipv6()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv6"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (rp_priority > 0xff) { error_msg = c_format("Invalid RP priority = %u", XORP_UINT_CAST(rp_priority)); return XrlCmdError::COMMAND_FAILED(error_msg); } if (hash_mask_len > 0xff) { error_msg = c_format("Invalid hash mask length = %u", XORP_UINT_CAST(hash_mask_len)); return XrlCmdError::COMMAND_FAILED(error_msg); } if (PimNode::add_config_static_rp(IPvXNet(group_prefix), IPvX(rp_addr), (uint8_t)(rp_priority), (uint8_t)(hash_mask_len), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_delete_config_static_rp6( // Input values, const IPv6Net& group_prefix, const IPv6& rp_addr) { string error_msg; // // Verify the address family // if (! PimNode::is_ipv6()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv6"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (PimNode::delete_config_static_rp(IPvXNet(group_prefix), IPvX(rp_addr), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_delete_config_all_static_group_prefixes_rp6( // Input values, const IPv6& rp_addr) { string error_msg; // // Verify the address family // if (! PimNode::is_ipv6()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv6"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (PimNode::delete_config_all_static_group_prefixes_rp(IPvX(rp_addr), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } #endif XrlCmdError XrlPimNode::pim_0_1_delete_config_static_rp4( // Input values, const IPv4Net& group_prefix, const IPv4& rp_addr) { string error_msg; // // Verify the address family // if (! PimNode::is_ipv4()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv4"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (PimNode::delete_config_static_rp(IPvXNet(group_prefix), IPvX(rp_addr), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_delete_config_all_static_group_prefixes_rp4( // Input values, const IPv4& rp_addr) { string error_msg; // // Verify the address family // if (! PimNode::is_ipv4()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv4"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (PimNode::delete_config_all_static_group_prefixes_rp(IPvX(rp_addr), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_delete_config_all_static_rps() { string error_msg; if (PimNode::delete_config_all_static_rps(error_msg) != XORP_OK) return XrlCmdError::COMMAND_FAILED(error_msg); return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_config_static_rp_done() { string error_msg; if (PimNode::config_static_rp_done(error_msg) != XORP_OK) return XrlCmdError::COMMAND_FAILED(error_msg); return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_get_vif_proto_version( // Input values, const string& vif_name, // Output values, uint32_t& proto_version) { string error_msg; int v; if (PimNode::get_vif_proto_version(vif_name, v, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } proto_version = v; return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_set_vif_proto_version( // Input values, const string& vif_name, const uint32_t& proto_version) { string error_msg; if (PimNode::set_vif_proto_version(vif_name, proto_version, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_reset_vif_proto_version( // Input values, const string& vif_name) { string error_msg; if (PimNode::reset_vif_proto_version(vif_name, error_msg) != XORP_OK) return XrlCmdError::COMMAND_FAILED(error_msg); return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_get_vif_hello_triggered_delay( // Input values, const string& vif_name, // Output values, uint32_t& hello_triggered_delay) { string error_msg; uint16_t v; if (PimNode::get_vif_hello_triggered_delay(vif_name, v, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } hello_triggered_delay = v; return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_set_vif_hello_triggered_delay( // Input values, const string& vif_name, const uint32_t& hello_triggered_delay) { string error_msg; if (hello_triggered_delay > 0xffff) { error_msg = c_format("Invalid Hello triggered delay value %u: " "max allowed is %u", XORP_UINT_CAST(hello_triggered_delay), 0xffffU); return XrlCmdError::COMMAND_FAILED(error_msg); } if (PimNode::set_vif_hello_triggered_delay(vif_name, hello_triggered_delay, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_reset_vif_hello_triggered_delay( // Input values, const string& vif_name) { string error_msg; if (PimNode::reset_vif_hello_triggered_delay(vif_name, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_get_vif_hello_period( // Input values, const string& vif_name, // Output values, uint32_t& hello_period) { string error_msg; uint16_t v; if (PimNode::get_vif_hello_period(vif_name, v, error_msg) != XORP_OK) return XrlCmdError::COMMAND_FAILED(error_msg); hello_period = v; return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_set_vif_hello_period( // Input values, const string& vif_name, const uint32_t& hello_period) { string error_msg; if (hello_period > 0xffff) { error_msg = c_format("Invalid Hello period value %u: " "max allowed is %u", XORP_UINT_CAST(hello_period), 0xffffU); return XrlCmdError::COMMAND_FAILED(error_msg); } if (PimNode::set_vif_hello_period(vif_name, hello_period, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_reset_vif_hello_period( // Input values, const string& vif_name) { string error_msg; if (PimNode::reset_vif_hello_period(vif_name, error_msg) != XORP_OK) return XrlCmdError::COMMAND_FAILED(error_msg); return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_get_vif_hello_holdtime( // Input values, const string& vif_name, // Output values, uint32_t& hello_holdtime) { string error_msg; uint16_t v; if (PimNode::get_vif_hello_holdtime(vif_name, v, error_msg) != XORP_OK) return XrlCmdError::COMMAND_FAILED(error_msg); hello_holdtime = v; return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_set_vif_hello_holdtime( // Input values, const string& vif_name, const uint32_t& hello_holdtime) { string error_msg; if (hello_holdtime > 0xffff) { error_msg = c_format("Invalid Hello holdtime value %u: " "max allowed is %u", XORP_UINT_CAST(hello_holdtime), 0xffffU); return XrlCmdError::COMMAND_FAILED(error_msg); } if (PimNode::set_vif_hello_holdtime(vif_name, hello_holdtime, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_reset_vif_hello_holdtime( // Input values, const string& vif_name) { string error_msg; if (PimNode::reset_vif_hello_holdtime(vif_name, error_msg) != XORP_OK) return XrlCmdError::COMMAND_FAILED(error_msg); return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_get_vif_dr_priority( // Input values, const string& vif_name, // Output values, uint32_t& dr_priority) { string error_msg; uint32_t v; if (PimNode::get_vif_dr_priority(vif_name, v, error_msg) != XORP_OK) return XrlCmdError::COMMAND_FAILED(error_msg); dr_priority = v; return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_set_vif_dr_priority( // Input values, const string& vif_name, const uint32_t& dr_priority) { string error_msg; if (dr_priority > 0xffffffffU) { error_msg = c_format("Invalid DR priority value %u: " "max allowed is %u", XORP_UINT_CAST(dr_priority), 0xffffffffU); return XrlCmdError::COMMAND_FAILED(error_msg); } if (PimNode::set_vif_dr_priority(vif_name, dr_priority, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_reset_vif_dr_priority( // Input values, const string& vif_name) { string error_msg; if (PimNode::reset_vif_dr_priority(vif_name, error_msg) != XORP_OK) return XrlCmdError::COMMAND_FAILED(error_msg); return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_get_vif_propagation_delay( // Input values, const string& vif_name, // Output values, uint32_t& propagation_delay) { string error_msg; uint16_t v; if (PimNode::get_vif_propagation_delay(vif_name, v, error_msg) != XORP_OK) return XrlCmdError::COMMAND_FAILED(error_msg); propagation_delay = v; return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_set_vif_propagation_delay( // Input values, const string& vif_name, const uint32_t& propagation_delay) { string error_msg; if (propagation_delay > 0xffff) { error_msg = c_format("Invalid Propagation delay value %u: " "max allowed is %u", XORP_UINT_CAST(propagation_delay), 0xffffU); return XrlCmdError::COMMAND_FAILED(error_msg); } if (PimNode::set_vif_propagation_delay(vif_name, propagation_delay, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_reset_vif_propagation_delay( // Input values, const string& vif_name) { string error_msg; if (PimNode::reset_vif_propagation_delay(vif_name, error_msg) != XORP_OK) return XrlCmdError::COMMAND_FAILED(error_msg); return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_get_vif_override_interval( // Input values, const string& vif_name, // Output values, uint32_t& override_interval) { string error_msg; uint16_t v; if (PimNode::get_vif_override_interval(vif_name, v, error_msg) != XORP_OK) return XrlCmdError::COMMAND_FAILED(error_msg); override_interval = v; return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_set_vif_override_interval( // Input values, const string& vif_name, const uint32_t& override_interval) { string error_msg; if (override_interval > 0xffff) { error_msg = c_format("Invalid Override interval value %u: " "max allowed is %u", XORP_UINT_CAST(override_interval), 0xffffU); return XrlCmdError::COMMAND_FAILED(error_msg); } if (PimNode::set_vif_override_interval(vif_name, override_interval, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_reset_vif_override_interval( // Input values, const string& vif_name) { string error_msg; if (PimNode::reset_vif_override_interval(vif_name, error_msg) != XORP_OK) return XrlCmdError::COMMAND_FAILED(error_msg); return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_get_vif_is_tracking_support_disabled( // Input values, const string& vif_name, // Output values, bool& is_tracking_support_disabled) { string error_msg; bool v; if (PimNode::get_vif_is_tracking_support_disabled(vif_name, v, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } is_tracking_support_disabled = v; return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_set_vif_is_tracking_support_disabled( // Input values, const string& vif_name, const bool& is_tracking_support_disabled) { string error_msg; if (PimNode::set_vif_is_tracking_support_disabled( vif_name, is_tracking_support_disabled, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_reset_vif_is_tracking_support_disabled( // Input values, const string& vif_name) { string error_msg; if (PimNode::reset_vif_is_tracking_support_disabled(vif_name, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_get_vif_accept_nohello_neighbors( // Input values, const string& vif_name, // Output values, bool& accept_nohello_neighbors) { string error_msg; bool v; if (PimNode::get_vif_accept_nohello_neighbors(vif_name, v, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } accept_nohello_neighbors = v; return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_set_vif_accept_nohello_neighbors( // Input values, const string& vif_name, const bool& accept_nohello_neighbors) { string error_msg; if (PimNode::set_vif_accept_nohello_neighbors(vif_name, accept_nohello_neighbors, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_reset_vif_accept_nohello_neighbors( // Input values, const string& vif_name) { string error_msg; if (PimNode::reset_vif_accept_nohello_neighbors(vif_name, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_get_vif_join_prune_period( // Input values, const string& vif_name, // Output values, uint32_t& join_prune_period) { string error_msg; uint16_t v; if (PimNode::get_vif_join_prune_period(vif_name, v, error_msg) != XORP_OK) return XrlCmdError::COMMAND_FAILED(error_msg); join_prune_period = v; return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_set_vif_join_prune_period( // Input values, const string& vif_name, const uint32_t& join_prune_period) { string error_msg; if (join_prune_period > 0xffff) { error_msg = c_format("Invalid Join/Prune period value %u: " "max allowed is %u", XORP_UINT_CAST(join_prune_period), 0xffffU); return XrlCmdError::COMMAND_FAILED(error_msg); } if (PimNode::set_vif_join_prune_period(vif_name, join_prune_period, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_reset_vif_join_prune_period( // Input values, const string& vif_name) { string error_msg; if (PimNode::reset_vif_join_prune_period(vif_name, error_msg) != XORP_OK) return XrlCmdError::COMMAND_FAILED(error_msg); return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_get_switch_to_spt_threshold( // Output values, bool& is_enabled, uint32_t& interval_sec, uint32_t& bytes) { string error_msg; bool v1; uint32_t v2, v3; if (PimNode::get_switch_to_spt_threshold(v1, v2, v3, error_msg) != XORP_OK) return XrlCmdError::COMMAND_FAILED(error_msg); is_enabled = v1; interval_sec = v2; bytes = v3; return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_set_switch_to_spt_threshold( // Input values, const bool& is_enabled, const uint32_t& interval_sec, const uint32_t& bytes) { string error_msg; if (PimNode::set_switch_to_spt_threshold(is_enabled, interval_sec, bytes, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_reset_switch_to_spt_threshold() { string error_msg; if (PimNode::reset_switch_to_spt_threshold(error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_add_alternative_subnet4( // Input values, const string& vif_name, const IPv4Net& subnet) { string error_msg; // // Verify the address family // if (! PimNode::is_ipv4()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv4"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (PimNode::add_alternative_subnet(vif_name, IPvXNet(subnet), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } #ifdef HAVE_IPV6 XrlCmdError XrlPimNode::pim_0_1_add_alternative_subnet6( // Input values, const string& vif_name, const IPv6Net& subnet) { string error_msg; // // Verify the address family // if (! PimNode::is_ipv6()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv6"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (PimNode::add_alternative_subnet(vif_name, IPvXNet(subnet), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_delete_alternative_subnet6( // Input values, const string& vif_name, const IPv6Net& subnet) { string error_msg; // // Verify the address family // if (! PimNode::is_ipv6()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv6"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (PimNode::delete_alternative_subnet(vif_name, IPvXNet(subnet), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } #endif XrlCmdError XrlPimNode::pim_0_1_delete_alternative_subnet4( // Input values, const string& vif_name, const IPv4Net& subnet) { string error_msg; // // Verify the address family // if (! PimNode::is_ipv4()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv4"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (PimNode::delete_alternative_subnet(vif_name, IPvXNet(subnet), error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_remove_all_alternative_subnets( // Input values, const string& vif_name) { string error_msg; if (PimNode::remove_all_alternative_subnets(vif_name, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_log_trace_all( // Input values, const bool& enable) { PimNode::set_log_trace(enable); return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_add_test_jp_entry4( // Input values, const IPv4& source_addr, const IPv4& group_addr, const uint32_t& group_mask_len, const string& mrt_entry_type, const string& action_jp, const uint32_t& holdtime, const bool& is_new_group) { string error_msg; mrt_entry_type_t entry_type = MRT_ENTRY_UNKNOWN; action_jp_t action_type; // // Verify the address family // if (! PimNode::is_ipv4()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv4"); return XrlCmdError::COMMAND_FAILED(error_msg); } // // Find the entry type // do { if (mrt_entry_type == "SG") { entry_type = MRT_ENTRY_SG; break; } if (mrt_entry_type == "SG_RPT") { entry_type = MRT_ENTRY_SG_RPT; break; } if (mrt_entry_type == "WC") { entry_type = MRT_ENTRY_WC; break; } if (mrt_entry_type == "RP") { entry_type = MRT_ENTRY_RP; break; } // Invalid entry error_msg = c_format("Invalid entry type = %s", mrt_entry_type.c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } while (false); // // Find the action type // do { if (action_jp == "JOIN") { action_type = ACTION_JOIN; break; } if (action_jp == "PRUNE") { action_type = ACTION_PRUNE; break; } // Invalid action error_msg = c_format("Invalid action = %s", action_jp.c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } while (false); if (group_mask_len > 0xff) { error_msg = c_format("Invalid group mask length = %u", XORP_UINT_CAST(group_mask_len)); return XrlCmdError::COMMAND_FAILED(error_msg); } // XXX: Dupe? if (group_mask_len > 0xff) { error_msg = c_format("Invalid group mask length = %u", XORP_UINT_CAST(group_mask_len)); return XrlCmdError::COMMAND_FAILED(error_msg); } if (holdtime > 0xffff) { error_msg = c_format("Invalid holdtime = %u", XORP_UINT_CAST(holdtime)); return XrlCmdError::COMMAND_FAILED(error_msg); } if (PimNode::add_test_jp_entry(IPvX(source_addr), IPvX(group_addr), (uint8_t)(group_mask_len), entry_type, action_type, (uint16_t)(holdtime), is_new_group) != XORP_OK) { error_msg = c_format("Failed to add Join/Prune test entry " "for (%s, %s)", cstring(source_addr), cstring(group_addr)); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } #ifdef HAVE_IPV6 XrlCmdError XrlPimNode::pim_0_1_add_test_jp_entry6( // Input values, const IPv6& source_addr, const IPv6& group_addr, const uint32_t& group_mask_len, const string& mrt_entry_type, const string& action_jp, const uint32_t& holdtime, const bool& is_new_group) { string error_msg; mrt_entry_type_t entry_type = MRT_ENTRY_UNKNOWN; action_jp_t action_type; // // Verify the address family // if (! PimNode::is_ipv6()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv6"); return XrlCmdError::COMMAND_FAILED(error_msg); } // // Find the entry type // do { if (mrt_entry_type == "SG") { entry_type = MRT_ENTRY_SG; break; } if (mrt_entry_type == "SG_RPT") { entry_type = MRT_ENTRY_SG_RPT; break; } if (mrt_entry_type == "WC") { entry_type = MRT_ENTRY_WC; break; } if (mrt_entry_type == "RP") { entry_type = MRT_ENTRY_RP; break; } // Invalid entry error_msg = c_format("Invalid entry type = %s", mrt_entry_type.c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } while (false); // // Find the action type // do { if (action_jp == "JOIN") { action_type = ACTION_JOIN; break; } if (action_jp == "PRUNE") { action_type = ACTION_PRUNE; break; } // Invalid action error_msg = c_format("Invalid action = %s", action_jp.c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } while (false); if (group_mask_len > 0xff) { error_msg = c_format("Invalid group mask length = %u", XORP_UINT_CAST(group_mask_len)); return XrlCmdError::COMMAND_FAILED(error_msg); } if (holdtime > 0xffff) { error_msg = c_format("Invalid holdtime = %u", XORP_UINT_CAST(holdtime)); return XrlCmdError::COMMAND_FAILED(error_msg); } if (PimNode::add_test_jp_entry(IPvX(source_addr), IPvX(group_addr), (uint8_t)(group_mask_len), entry_type, action_type, (uint16_t)(holdtime), is_new_group) != XORP_OK) { error_msg = c_format("Failed to add Join/Prune test entry " "for (%s, %s)", cstring(source_addr), cstring(group_addr)); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_send_test_jp_entry6( // Input values, const string& vif_name, const IPv6& nbr_addr) { string error_msg; // // Verify the address family // if (! PimNode::is_ipv6()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv6"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (PimNode::send_test_jp_entry(vif_name, IPvX(nbr_addr), error_msg) != XORP_OK) { error_msg = c_format("Failed to send Join/Prune test message to %s " "on vif %s: %s", cstring(nbr_addr), vif_name.c_str(), error_msg.c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_send_test_assert6( // Input values, const string& vif_name, const IPv6& source_addr, const IPv6& group_addr, const bool& rpt_bit, const uint32_t& metric_preference, const uint32_t& metric) { string error_msg; // // Verify the address family // if (! PimNode::is_ipv6()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv6"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (PimNode::send_test_assert(vif_name, IPvX(source_addr), IPvX(group_addr), rpt_bit, metric_preference, metric, error_msg) != XORP_OK) { error_msg = c_format("Failed to send Assert test message " "for (%s, %s) on vif %s: %s", cstring(source_addr), cstring(group_addr), vif_name.c_str(), error_msg.c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } #endif XrlCmdError XrlPimNode::pim_0_1_send_test_jp_entry4( // Input values, const string& vif_name, const IPv4& nbr_addr) { string error_msg; // // Verify the address family // if (! PimNode::is_ipv4()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv4"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (PimNode::send_test_jp_entry(vif_name, IPvX(nbr_addr), error_msg) != XORP_OK) { error_msg = c_format("Failed to send Join/Prune test message to %s " "on vif %s: %s", cstring(nbr_addr), vif_name.c_str(), error_msg.c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_send_test_assert4( // Input values, const string& vif_name, const IPv4& source_addr, const IPv4& group_addr, const bool& rpt_bit, const uint32_t& metric_preference, const uint32_t& metric) { string error_msg; // // Verify the address family // if (! PimNode::is_ipv4()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv4"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (PimNode::send_test_assert(vif_name, IPvX(source_addr), IPvX(group_addr), rpt_bit, metric_preference, metric, error_msg) != XORP_OK) { error_msg = c_format("Failed to send Assert test message " "for (%s, %s) on vif %s: %s", cstring(source_addr), cstring(group_addr), vif_name.c_str(), error_msg.c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_add_test_bsr_zone4( // Input values, const IPv4Net& zone_id_scope_zone_prefix, const bool& zone_id_is_scope_zone, const IPv4& bsr_addr, const uint32_t& bsr_priority, const uint32_t& hash_mask_len, const uint32_t& fragment_tag) { string error_msg; // // Verify the address family // if (! PimNode::is_ipv4()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv4"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (bsr_priority > 0xff) { error_msg = c_format("Invalid BSR priority = %u", XORP_UINT_CAST(bsr_priority)); return XrlCmdError::COMMAND_FAILED(error_msg); } if (hash_mask_len > 0xff) { error_msg = c_format("Invalid hash mask length = %u", XORP_UINT_CAST(hash_mask_len)); return XrlCmdError::COMMAND_FAILED(error_msg); } if (fragment_tag > 0xffff) { error_msg = c_format("Invalid fragment tag = %u", XORP_UINT_CAST(fragment_tag)); return XrlCmdError::COMMAND_FAILED(error_msg); } if (PimNode::add_test_bsr_zone(PimScopeZoneId(zone_id_scope_zone_prefix, zone_id_is_scope_zone), IPvX(bsr_addr), (uint8_t)(bsr_priority), (uint8_t)(hash_mask_len), (uint16_t)(fragment_tag)) != XORP_OK) { error_msg = c_format("Failed to add BSR test zone %s " "with BSR address %s", cstring(PimScopeZoneId(zone_id_scope_zone_prefix, zone_id_is_scope_zone)), cstring(bsr_addr)); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } #ifdef HAVE_IPV6 XrlCmdError XrlPimNode::pim_0_1_add_test_bsr_zone6( // Input values, const IPv6Net& zone_id_scope_zone_prefix, const bool& zone_id_is_scope_zone, const IPv6& bsr_addr, const uint32_t& bsr_priority, const uint32_t& hash_mask_len, const uint32_t& fragment_tag) { string error_msg; // // Verify the address family // if (! PimNode::is_ipv6()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv6"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (bsr_priority > 0xff) { error_msg = c_format("Invalid BSR priority = %u", XORP_UINT_CAST(bsr_priority)); return XrlCmdError::COMMAND_FAILED(error_msg); } if (hash_mask_len > 0xff) { error_msg = c_format("Invalid hash mask length = %u", XORP_UINT_CAST(hash_mask_len)); return XrlCmdError::COMMAND_FAILED(error_msg); } if (fragment_tag > 0xffff) { error_msg = c_format("Invalid fragment tag = %u", XORP_UINT_CAST(fragment_tag)); return XrlCmdError::COMMAND_FAILED(error_msg); } if (PimNode::add_test_bsr_zone(PimScopeZoneId(zone_id_scope_zone_prefix, zone_id_is_scope_zone), IPvX(bsr_addr), (uint8_t)(bsr_priority), (uint8_t)(hash_mask_len), (uint16_t)(fragment_tag)) != XORP_OK) { error_msg = c_format("Failed to add BSR test zone %s " "with BSR address %s", cstring(PimScopeZoneId(zone_id_scope_zone_prefix, zone_id_is_scope_zone)), cstring(bsr_addr)); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_add_test_bsr_group_prefix6( // Input values, const IPv6Net& zone_id_scope_zone_prefix, const bool& zone_id_is_scope_zone, const IPv6Net& group_prefix, const bool& is_scope_zone, const uint32_t& expected_rp_count) { string error_msg; // // Verify the address family // if (! PimNode::is_ipv6()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv6"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (expected_rp_count > 0xff) { error_msg = c_format("Invalid expected RP count = %u", XORP_UINT_CAST(expected_rp_count)); return XrlCmdError::COMMAND_FAILED(error_msg); } if (PimNode::add_test_bsr_group_prefix( PimScopeZoneId(zone_id_scope_zone_prefix, zone_id_is_scope_zone), IPvXNet(group_prefix), is_scope_zone, (uint8_t)(expected_rp_count)) != XORP_OK) { error_msg = c_format("Failed to add group prefix %s " "for BSR test zone %s", cstring(group_prefix), cstring(PimScopeZoneId(zone_id_scope_zone_prefix, zone_id_is_scope_zone))); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } #endif XrlCmdError XrlPimNode::pim_0_1_add_test_bsr_group_prefix4( // Input values, const IPv4Net& zone_id_scope_zone_prefix, const bool& zone_id_is_scope_zone, const IPv4Net& group_prefix, const bool& is_scope_zone, const uint32_t& expected_rp_count) { string error_msg; // // Verify the address family // if (! PimNode::is_ipv4()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv4"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (expected_rp_count > 0xff) { error_msg = c_format("Invalid expected RP count = %u", XORP_UINT_CAST(expected_rp_count)); return XrlCmdError::COMMAND_FAILED(error_msg); } if (PimNode::add_test_bsr_group_prefix( PimScopeZoneId(zone_id_scope_zone_prefix, zone_id_is_scope_zone), IPvXNet(group_prefix), is_scope_zone, (uint8_t)(expected_rp_count)) != XORP_OK) { error_msg = c_format("Failed to add group prefix %s " "for BSR test zone %s", cstring(group_prefix), cstring(PimScopeZoneId(zone_id_scope_zone_prefix, zone_id_is_scope_zone))); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_add_test_bsr_rp4( // Input values, const IPv4Net& zone_id_scope_zone_prefix, const bool& zone_id_is_scope_zone, const IPv4Net& group_prefix, const IPv4& rp_addr, const uint32_t& rp_priority, const uint32_t& rp_holdtime) { string error_msg; // // Verify the address family // if (! PimNode::is_ipv4()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv4"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (rp_priority > 0xff) { error_msg = c_format("Invalid RP priority = %u", XORP_UINT_CAST(rp_priority)); return XrlCmdError::COMMAND_FAILED(error_msg); } if (rp_holdtime > 0xffff) { error_msg = c_format("Invalid RP holdtime = %u", XORP_UINT_CAST(rp_holdtime)); return XrlCmdError::COMMAND_FAILED(error_msg); } if (PimNode::add_test_bsr_rp(PimScopeZoneId(zone_id_scope_zone_prefix, zone_id_is_scope_zone), IPvXNet(group_prefix), IPvX(rp_addr), (uint8_t)(rp_priority), (uint16_t)(rp_holdtime)) != XORP_OK) { error_msg = c_format("Failed to add test Cand-RP %s " "for group prefix %s for BSR zone %s", cstring(rp_addr), cstring(group_prefix), cstring(PimScopeZoneId(zone_id_scope_zone_prefix, zone_id_is_scope_zone))); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } #ifdef HAVE_IPV6 XrlCmdError XrlPimNode::pim_0_1_add_test_bsr_rp6( // Input values, const IPv6Net& zone_id_scope_zone_prefix, const bool& zone_id_is_scope_zone, const IPv6Net& group_prefix, const IPv6& rp_addr, const uint32_t& rp_priority, const uint32_t& rp_holdtime) { string error_msg; // // Verify the address family // if (! PimNode::is_ipv6()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv6"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (rp_priority > 0xff) { error_msg = c_format("Invalid RP priority = %u", XORP_UINT_CAST(rp_priority)); return XrlCmdError::COMMAND_FAILED(error_msg); } if (rp_holdtime > 0xffff) { error_msg = c_format("Invalid RP holdtime = %u", XORP_UINT_CAST(rp_holdtime)); return XrlCmdError::COMMAND_FAILED(error_msg); } if (PimNode::add_test_bsr_rp(PimScopeZoneId(zone_id_scope_zone_prefix, zone_id_is_scope_zone), IPvXNet(group_prefix), IPvX(rp_addr), (uint8_t)(rp_priority), (uint16_t)(rp_holdtime)) != XORP_OK) { error_msg = c_format("Failed to add test Cand-RP %s " "for group prefix %s for BSR zone %s", cstring(rp_addr), cstring(group_prefix), cstring(PimScopeZoneId(zone_id_scope_zone_prefix, zone_id_is_scope_zone))); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_send_test_bootstrap_by_dest6( // Input values, const string& vif_name, const IPv6& dest_addr) { string error_msg; // // Verify the address family // if (! PimNode::is_ipv6()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv6"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (PimNode::send_test_bootstrap_by_dest(vif_name, IPvX(dest_addr), error_msg) != XORP_OK) { error_msg = c_format("Failed to send Bootstrap test message " "on vif %s to address %s: %s", vif_name.c_str(), cstring(dest_addr), error_msg.c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_pimstat_interface6( // Input values, const string& vif_name, // Output values, uint32_t& pim_version, bool& is_dr, uint32_t& dr_priority, IPv6& dr_address, uint32_t& pim_nbrs_number) { string error_msg; // // Verify the address family // if (! PimNode::is_ipv6()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv6"); return XrlCmdError::COMMAND_FAILED(error_msg); } PimVif *pim_vif = PimNode::vif_find_by_name(vif_name); if (pim_vif == NULL) { error_msg = c_format("Cannot get information about vif %s: " "no such vif", vif_name.c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } pim_version = pim_vif->proto_version(); is_dr = pim_vif->i_am_dr(); dr_priority = pim_vif->dr_priority().get(); dr_address = pim_vif->dr_addr().get_ipv6(); pim_nbrs_number = pim_vif->pim_nbrs_number(); return XrlCmdError::OKAY(); } #endif XrlCmdError XrlPimNode::pim_0_1_send_test_bootstrap( // Input values, const string& vif_name) { string error_msg; if (PimNode::send_test_bootstrap(vif_name, error_msg) != XORP_OK) { error_msg = c_format("Failed to send Bootstrap test message " "on vif %s: %s", vif_name.c_str(), error_msg.c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_send_test_bootstrap_by_dest4( // Input values, const string& vif_name, const IPv4& dest_addr) { string error_msg; // // Verify the address family // if (! PimNode::is_ipv4()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv4"); return XrlCmdError::COMMAND_FAILED(error_msg); } if (PimNode::send_test_bootstrap_by_dest(vif_name, IPvX(dest_addr), error_msg) != XORP_OK) { error_msg = c_format("Failed to send Bootstrap test message " "on vif %s to address %s: %s", vif_name.c_str(), cstring(dest_addr), error_msg.c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_send_test_cand_rp_adv() { string error_msg; if (PimNode::send_test_cand_rp_adv() != XORP_OK) { error_msg = c_format("Failed to send Cand-RP-Adv test message"); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_pimstat_neighbors4( // Output values, uint32_t& nbrs_number, XrlAtomList& vifs, XrlAtomList& addresses, XrlAtomList& pim_versions, XrlAtomList& dr_priorities, XrlAtomList& holdtimes, XrlAtomList& timeouts, XrlAtomList& uptimes) { string error_msg; // // Verify the address family // if (! PimNode::is_ipv4()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv4"); return XrlCmdError::COMMAND_FAILED(error_msg); } TimeVal now; TimerList::system_gettimeofday(&now); nbrs_number = 0; for (uint32_t i = 0; i < PimNode::maxvifs(); i++) { PimVif *pim_vif = PimNode::vif_find_by_vif_index(i); if (pim_vif == NULL) continue; if (pim_vif->primary_addr() == IPvX::ZERO(family())) continue; // XXX: ignore vifs with no address list::iterator iter; for (iter = pim_vif->pim_nbrs().begin(); iter != pim_vif->pim_nbrs().end(); ++iter) { PimNbr *pim_nbr = *iter; nbrs_number++; vifs.append(XrlAtom(pim_vif->name())); addresses.append(XrlAtom(pim_vif->primary_addr().get_ipv4())); pim_versions.append(XrlAtom((int32_t)pim_nbr->proto_version())); if (pim_nbr->is_dr_priority_present()) dr_priorities.append(XrlAtom((int32_t)pim_nbr->dr_priority())); else dr_priorities.append(XrlAtom((int32_t)-1)); holdtimes.append(XrlAtom((int32_t)pim_nbr->hello_holdtime())); if (pim_nbr->const_neighbor_liveness_timer().scheduled()) { TimeVal tv_left; pim_nbr->const_neighbor_liveness_timer().time_remaining(tv_left); timeouts.append(XrlAtom((int32_t)tv_left.sec())); } else { timeouts.append(XrlAtom((int32_t)-1)); } TimeVal startup_time = pim_nbr->startup_time(); TimeVal delta_time = now - startup_time; uptimes.append(XrlAtom((int32_t)delta_time.sec())); } } return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_pimstat_neighbors6( // Output values, uint32_t& nbrs_number, XrlAtomList& vifs, XrlAtomList& addresses, XrlAtomList& pim_versions, XrlAtomList& dr_priorities, XrlAtomList& holdtimes, XrlAtomList& timeouts, XrlAtomList& uptimes) { string error_msg; // // Verify the address family // if (! PimNode::is_ipv6()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv6"); return XrlCmdError::COMMAND_FAILED(error_msg); } TimeVal now; TimerList::system_gettimeofday(&now); nbrs_number = 0; for (uint32_t i = 0; i < PimNode::maxvifs(); i++) { PimVif *pim_vif = PimNode::vif_find_by_vif_index(i); if (pim_vif == NULL) continue; if (pim_vif->primary_addr() == IPvX::ZERO(family())) continue; // XXX: ignore vifs with no address list::iterator iter; for (iter = pim_vif->pim_nbrs().begin(); iter != pim_vif->pim_nbrs().end(); ++iter) { PimNbr *pim_nbr = *iter; nbrs_number++; vifs.append(XrlAtom(pim_vif->name())); addresses.append(XrlAtom(pim_vif->primary_addr().get_ipv6())); pim_versions.append(XrlAtom((int32_t)pim_nbr->proto_version())); if (pim_nbr->is_dr_priority_present()) dr_priorities.append(XrlAtom((int32_t)pim_nbr->dr_priority())); else dr_priorities.append(XrlAtom((int32_t)-1)); holdtimes.append(XrlAtom((int32_t)pim_nbr->hello_holdtime())); if (pim_nbr->const_neighbor_liveness_timer().scheduled()) { TimeVal tv_left; pim_nbr->const_neighbor_liveness_timer().time_remaining(tv_left); timeouts.append(XrlAtom((int32_t)tv_left.sec())); } else { timeouts.append(XrlAtom((int32_t)-1)); } TimeVal startup_time = pim_nbr->startup_time(); TimeVal delta_time = now - startup_time; uptimes.append(XrlAtom((int32_t)delta_time.sec())); } } return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_pimstat_interface4( // Input values, const string& vif_name, // Output values, uint32_t& pim_version, bool& is_dr, uint32_t& dr_priority, IPv4& dr_address, uint32_t& pim_nbrs_number) { string error_msg; // // Verify the address family // if (! PimNode::is_ipv4()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv4"); return XrlCmdError::COMMAND_FAILED(error_msg); } PimVif *pim_vif = PimNode::vif_find_by_name(vif_name); if (pim_vif == NULL) { error_msg = c_format("Cannot get information about vif %s: " "no such vif", vif_name.c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } pim_version = pim_vif->proto_version(); is_dr = pim_vif->i_am_dr(); dr_priority = pim_vif->dr_priority().get(); dr_address = pim_vif->dr_addr().get_ipv4(); pim_nbrs_number = pim_vif->pim_nbrs_number(); return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_pimstat_rps4( // Output values, uint32_t& rps_number, XrlAtomList& addresses, XrlAtomList& types, XrlAtomList& priorities, XrlAtomList& holdtimes, XrlAtomList& timeouts, XrlAtomList& group_prefixes) { string error_msg; // // Verify the address family // if (! PimNode::is_ipv4()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv4"); return XrlCmdError::COMMAND_FAILED(error_msg); } rps_number = 0; list::const_iterator iter; for (iter = PimNode::rp_table().rp_list().begin(); iter != PimNode::rp_table().rp_list().end(); ++iter) { PimRp *pim_rp = *iter; string rp_type; int holdtime = -1; int left_sec = -1; switch (pim_rp->rp_learned_method()) { case PimRp::RP_LEARNED_METHOD_BOOTSTRAP: rp_type = "bootstrap"; do { // Try first a scoped zone, then a non-scoped zone BsrRp *bsr_rp; bsr_rp = PimNode::pim_bsr().find_rp(pim_rp->group_prefix(), true, pim_rp->rp_addr()); if (bsr_rp == NULL) { bsr_rp = PimNode::pim_bsr().find_rp(pim_rp->group_prefix(), false, pim_rp->rp_addr()); } if (bsr_rp == NULL) break; holdtime = bsr_rp->rp_holdtime(); if (bsr_rp->const_candidate_rp_expiry_timer().scheduled()) { TimeVal tv_left; bsr_rp->const_candidate_rp_expiry_timer().time_remaining(tv_left); left_sec = tv_left.sec(); } } while (false); break; case PimRp::RP_LEARNED_METHOD_STATIC: rp_type = "static"; break; default: rp_type = "unknown"; break; } addresses.append(XrlAtom(pim_rp->rp_addr().get_ipv4())); types.append(XrlAtom(rp_type)); priorities.append(XrlAtom((int32_t)pim_rp->rp_priority())); holdtimes.append(XrlAtom((int32_t)holdtime)); timeouts.append(XrlAtom((int32_t)left_sec)); group_prefixes.append(XrlAtom(pim_rp->group_prefix().get_ipv4net())); } return XrlCmdError::OKAY(); } #ifdef HAVE_IPV6 XrlCmdError XrlPimNode::pim_0_1_pimstat_rps6( // Output values, uint32_t& rps_number, XrlAtomList& addresses, XrlAtomList& types, XrlAtomList& priorities, XrlAtomList& holdtimes, XrlAtomList& timeouts, XrlAtomList& group_prefixes) { string error_msg; // // Verify the address family // if (! PimNode::is_ipv6()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv6"); return XrlCmdError::COMMAND_FAILED(error_msg); } rps_number = 0; list::const_iterator iter; for (iter = PimNode::rp_table().rp_list().begin(); iter != PimNode::rp_table().rp_list().end(); ++iter) { PimRp *pim_rp = *iter; string rp_type; int holdtime = -1; int left_sec = -1; switch (pim_rp->rp_learned_method()) { case PimRp::RP_LEARNED_METHOD_BOOTSTRAP: rp_type = "bootstrap"; do { // Try first a scoped zone, then a non-scoped zone BsrRp *bsr_rp; bsr_rp = PimNode::pim_bsr().find_rp(pim_rp->group_prefix(), true, pim_rp->rp_addr()); if (bsr_rp == NULL) { bsr_rp = PimNode::pim_bsr().find_rp(pim_rp->group_prefix(), false, pim_rp->rp_addr()); } if (bsr_rp == NULL) break; holdtime = bsr_rp->rp_holdtime(); if (bsr_rp->const_candidate_rp_expiry_timer().scheduled()) { TimeVal tv_left; bsr_rp->const_candidate_rp_expiry_timer().time_remaining(tv_left); left_sec = tv_left.sec(); } } while (false); break; case PimRp::RP_LEARNED_METHOD_STATIC: rp_type = "static"; break; default: rp_type = "unknown"; break; } addresses.append(XrlAtom(pim_rp->rp_addr().get_ipv6())); types.append(XrlAtom(rp_type)); priorities.append(XrlAtom((int32_t)pim_rp->rp_priority())); holdtimes.append(XrlAtom((int32_t)holdtime)); timeouts.append(XrlAtom((int32_t)left_sec)); group_prefixes.append(XrlAtom(pim_rp->group_prefix().get_ipv6net())); } return XrlCmdError::OKAY(); } #endif XrlCmdError XrlPimNode::pim_0_1_clear_pim_statistics() { PimNode::clear_pim_statistics(); return XrlCmdError::OKAY(); } XrlCmdError XrlPimNode::pim_0_1_clear_pim_statistics_per_vif( // Input values, const string& vif_name) { string error_msg; if (PimNode::clear_pim_statistics_per_vif(vif_name, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } // // Statistics-related counters and values // #define XRL_GET_PIMSTAT_PER_NODE(stat_name) \ XrlCmdError \ XrlPimNode::pim_0_1_pimstat_##stat_name( \ /* Output values , */ \ uint32_t& value) \ { \ value = PimNode::pimstat_##stat_name(); \ \ return XrlCmdError::OKAY(); \ } XRL_GET_PIMSTAT_PER_NODE(hello_messages_received) XRL_GET_PIMSTAT_PER_NODE(hello_messages_sent) XRL_GET_PIMSTAT_PER_NODE(hello_messages_rx_errors) XRL_GET_PIMSTAT_PER_NODE(register_messages_received) XRL_GET_PIMSTAT_PER_NODE(register_messages_sent) XRL_GET_PIMSTAT_PER_NODE(register_messages_rx_errors) XRL_GET_PIMSTAT_PER_NODE(register_stop_messages_received) XRL_GET_PIMSTAT_PER_NODE(register_stop_messages_sent) XRL_GET_PIMSTAT_PER_NODE(register_stop_messages_rx_errors) XRL_GET_PIMSTAT_PER_NODE(join_prune_messages_received) XRL_GET_PIMSTAT_PER_NODE(join_prune_messages_sent) XRL_GET_PIMSTAT_PER_NODE(join_prune_messages_rx_errors) XRL_GET_PIMSTAT_PER_NODE(bootstrap_messages_received) XRL_GET_PIMSTAT_PER_NODE(bootstrap_messages_sent) XRL_GET_PIMSTAT_PER_NODE(bootstrap_messages_rx_errors) XRL_GET_PIMSTAT_PER_NODE(assert_messages_received) XRL_GET_PIMSTAT_PER_NODE(assert_messages_sent) XRL_GET_PIMSTAT_PER_NODE(assert_messages_rx_errors) XRL_GET_PIMSTAT_PER_NODE(graft_messages_received) XRL_GET_PIMSTAT_PER_NODE(graft_messages_sent) XRL_GET_PIMSTAT_PER_NODE(graft_messages_rx_errors) XRL_GET_PIMSTAT_PER_NODE(graft_ack_messages_received) XRL_GET_PIMSTAT_PER_NODE(graft_ack_messages_sent) XRL_GET_PIMSTAT_PER_NODE(graft_ack_messages_rx_errors) XRL_GET_PIMSTAT_PER_NODE(candidate_rp_messages_received) XRL_GET_PIMSTAT_PER_NODE(candidate_rp_messages_sent) XRL_GET_PIMSTAT_PER_NODE(candidate_rp_messages_rx_errors) // XRL_GET_PIMSTAT_PER_NODE(unknown_type_messages) XRL_GET_PIMSTAT_PER_NODE(unknown_version_messages) XRL_GET_PIMSTAT_PER_NODE(neighbor_unknown_messages) XRL_GET_PIMSTAT_PER_NODE(bad_length_messages) XRL_GET_PIMSTAT_PER_NODE(bad_checksum_messages) XRL_GET_PIMSTAT_PER_NODE(bad_receive_interface_messages) XRL_GET_PIMSTAT_PER_NODE(rx_interface_disabled_messages) XRL_GET_PIMSTAT_PER_NODE(rx_register_not_rp) XRL_GET_PIMSTAT_PER_NODE(rp_filtered_source) XRL_GET_PIMSTAT_PER_NODE(unknown_register_stop) XRL_GET_PIMSTAT_PER_NODE(rx_join_prune_no_state) XRL_GET_PIMSTAT_PER_NODE(rx_graft_graft_ack_no_state) XRL_GET_PIMSTAT_PER_NODE(rx_graft_on_upstream_interface) XRL_GET_PIMSTAT_PER_NODE(rx_candidate_rp_not_bsr) XRL_GET_PIMSTAT_PER_NODE(rx_bsr_when_bsr) XRL_GET_PIMSTAT_PER_NODE(rx_bsr_not_rpf_interface) XRL_GET_PIMSTAT_PER_NODE(rx_unknown_hello_option) XRL_GET_PIMSTAT_PER_NODE(rx_data_no_state) XRL_GET_PIMSTAT_PER_NODE(rx_rp_no_state) XRL_GET_PIMSTAT_PER_NODE(rx_aggregate) XRL_GET_PIMSTAT_PER_NODE(rx_malformed_packet) XRL_GET_PIMSTAT_PER_NODE(no_rp) XRL_GET_PIMSTAT_PER_NODE(no_route_upstream) XRL_GET_PIMSTAT_PER_NODE(rp_mismatch) XRL_GET_PIMSTAT_PER_NODE(rpf_neighbor_unknown) // XRL_GET_PIMSTAT_PER_NODE(rx_join_rp) XRL_GET_PIMSTAT_PER_NODE(rx_prune_rp) XRL_GET_PIMSTAT_PER_NODE(rx_join_wc) XRL_GET_PIMSTAT_PER_NODE(rx_prune_wc) XRL_GET_PIMSTAT_PER_NODE(rx_join_sg) XRL_GET_PIMSTAT_PER_NODE(rx_prune_sg) XRL_GET_PIMSTAT_PER_NODE(rx_join_sg_rpt) XRL_GET_PIMSTAT_PER_NODE(rx_prune_sg_rpt) #undef XRL_GET_PIMSTAT_PER_NODE #define XRL_GET_PIMSTAT_PER_VIF(stat_name) \ XrlCmdError \ XrlPimNode::pim_0_1_pimstat_##stat_name##_per_vif( \ /* Input values, */ \ const string& vif_name, \ /* Output values, */ \ uint32_t& value) \ { \ string error_msg; \ \ if (PimNode::pimstat_##stat_name##_per_vif(vif_name, value, error_msg) != XORP_OK) { \ return XrlCmdError::COMMAND_FAILED(error_msg); \ } \ \ return XrlCmdError::OKAY(); \ } XRL_GET_PIMSTAT_PER_VIF(hello_messages_received) XRL_GET_PIMSTAT_PER_VIF(hello_messages_sent) XRL_GET_PIMSTAT_PER_VIF(hello_messages_rx_errors) XRL_GET_PIMSTAT_PER_VIF(register_messages_received) XRL_GET_PIMSTAT_PER_VIF(register_messages_sent) XRL_GET_PIMSTAT_PER_VIF(register_messages_rx_errors) XRL_GET_PIMSTAT_PER_VIF(register_stop_messages_received) XRL_GET_PIMSTAT_PER_VIF(register_stop_messages_sent) XRL_GET_PIMSTAT_PER_VIF(register_stop_messages_rx_errors) XRL_GET_PIMSTAT_PER_VIF(join_prune_messages_received) XRL_GET_PIMSTAT_PER_VIF(join_prune_messages_sent) XRL_GET_PIMSTAT_PER_VIF(join_prune_messages_rx_errors) XRL_GET_PIMSTAT_PER_VIF(bootstrap_messages_received) XRL_GET_PIMSTAT_PER_VIF(bootstrap_messages_sent) XRL_GET_PIMSTAT_PER_VIF(bootstrap_messages_rx_errors) XRL_GET_PIMSTAT_PER_VIF(assert_messages_received) XRL_GET_PIMSTAT_PER_VIF(assert_messages_sent) XRL_GET_PIMSTAT_PER_VIF(assert_messages_rx_errors) XRL_GET_PIMSTAT_PER_VIF(graft_messages_received) XRL_GET_PIMSTAT_PER_VIF(graft_messages_sent) XRL_GET_PIMSTAT_PER_VIF(graft_messages_rx_errors) XRL_GET_PIMSTAT_PER_VIF(graft_ack_messages_received) XRL_GET_PIMSTAT_PER_VIF(graft_ack_messages_sent) XRL_GET_PIMSTAT_PER_VIF(graft_ack_messages_rx_errors) XRL_GET_PIMSTAT_PER_VIF(candidate_rp_messages_received) XRL_GET_PIMSTAT_PER_VIF(candidate_rp_messages_sent) XRL_GET_PIMSTAT_PER_VIF(candidate_rp_messages_rx_errors) // XRL_GET_PIMSTAT_PER_VIF(unknown_type_messages) XRL_GET_PIMSTAT_PER_VIF(unknown_version_messages) XRL_GET_PIMSTAT_PER_VIF(neighbor_unknown_messages) XRL_GET_PIMSTAT_PER_VIF(bad_length_messages) XRL_GET_PIMSTAT_PER_VIF(bad_checksum_messages) XRL_GET_PIMSTAT_PER_VIF(bad_receive_interface_messages) XRL_GET_PIMSTAT_PER_VIF(rx_interface_disabled_messages) XRL_GET_PIMSTAT_PER_VIF(rx_register_not_rp) XRL_GET_PIMSTAT_PER_VIF(rp_filtered_source) XRL_GET_PIMSTAT_PER_VIF(unknown_register_stop) XRL_GET_PIMSTAT_PER_VIF(rx_join_prune_no_state) XRL_GET_PIMSTAT_PER_VIF(rx_graft_graft_ack_no_state) XRL_GET_PIMSTAT_PER_VIF(rx_graft_on_upstream_interface) XRL_GET_PIMSTAT_PER_VIF(rx_candidate_rp_not_bsr) XRL_GET_PIMSTAT_PER_VIF(rx_bsr_when_bsr) XRL_GET_PIMSTAT_PER_VIF(rx_bsr_not_rpf_interface) XRL_GET_PIMSTAT_PER_VIF(rx_unknown_hello_option) XRL_GET_PIMSTAT_PER_VIF(rx_data_no_state) XRL_GET_PIMSTAT_PER_VIF(rx_rp_no_state) XRL_GET_PIMSTAT_PER_VIF(rx_aggregate) XRL_GET_PIMSTAT_PER_VIF(rx_malformed_packet) XRL_GET_PIMSTAT_PER_VIF(no_rp) XRL_GET_PIMSTAT_PER_VIF(no_route_upstream) XRL_GET_PIMSTAT_PER_VIF(rp_mismatch) XRL_GET_PIMSTAT_PER_VIF(rpf_neighbor_unknown) // XRL_GET_PIMSTAT_PER_VIF(rx_join_rp) XRL_GET_PIMSTAT_PER_VIF(rx_prune_rp) XRL_GET_PIMSTAT_PER_VIF(rx_join_wc) XRL_GET_PIMSTAT_PER_VIF(rx_prune_wc) XRL_GET_PIMSTAT_PER_VIF(rx_join_sg) XRL_GET_PIMSTAT_PER_VIF(rx_prune_sg) XRL_GET_PIMSTAT_PER_VIF(rx_join_sg_rpt) XRL_GET_PIMSTAT_PER_VIF(rx_prune_sg_rpt) #undef XRL_GET_PIMSTAT_PER_VIF xorp/pim/pim_proto_join_prune_message.hh0000664000076400007640000002011011540224232020717 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __PIM_PIM_PROTO_JOIN_PRUNE_MESSAGE_HH__ #define __PIM_PIM_PROTO_JOIN_PRUNE_MESSAGE_HH__ // // PIM Join/Prune message creation implementation-specific definitions. // #include "mrt/buffer.h" #include "mrt/multicast_defs.h" #include "pim_proto.h" #include "pim_mre.hh" // // Constants definitions // // // When we compute the Join/Prune message size whether it fits within // a buffer, we need to take into account the expected max. number of // multicast sources. Here we define this number. // TODO: get rid of this predefined number // #define PIM_JP_EXTRA_SOURCES_N 32 // // Structures/classes, typedefs and macros // // The type of entries: (*,*,RP), (*,G), (S,G), (S,G,rpt) enum mrt_entry_type_t { MRT_ENTRY_UNKNOWN = 0, MRT_ENTRY_RP = PIM_MRE_RP, MRT_ENTRY_WC = PIM_MRE_WC, MRT_ENTRY_SG = PIM_MRE_SG, MRT_ENTRY_SG_RPT = PIM_MRE_SG_RPT }; // // Various structures to handle the assembly of source-group entries // that are pending to be (re)send to a neighbor in a Join/Prune (or a Graft) // message. // XXX: the various numbers fields in a PIM Join/Prune message are no // larger than 16 bits. The purpose of the structures below // is to collect all necessary information about the pending source-group // entries instead of completely spliting them according to the protocol // messages they are going to be included into. Thus, the final job of // assigning which entries go into which J/P protocol message can be // performed by an independent block which may take care of controlling // the overall control bandwidth for example. // // // Class to keep list of the source-group entries that are pending // to be (re)send to a neighbor in a Join-Prune (or Graft) message. // class IPvX; class PimJpGroup; class PimMrt; class PimNbr; class PimNode; class PimJpHeader { public: PimJpHeader(PimNode* pim_node); #ifdef XORP_USE_USTL PimJpHeader(); #endif ~PimJpHeader(); void reset(); PimNode* pim_node() const { return _pim_node; } int family() const { return (_family); } PimMrt& pim_mrt() const; size_t message_size() const { return ( sizeof(struct pim) + ENCODED_UNICAST_ADDR_SIZE(_family) + 2 * sizeof(uint8_t) + sizeof(uint16_t) + _jp_groups_n * (ENCODED_GROUP_ADDR_SIZE(_family) + 2 * sizeof(uint16_t)) + _jp_sources_n * (ENCODED_SOURCE_ADDR_SIZE(_family))); } size_t extra_source_size() const { return (ENCODED_SOURCE_ADDR_SIZE(_family)); } int jp_entry_add(const IPvX& source_addr, const IPvX& group_addr, uint8_t group_mask_len, mrt_entry_type_t mrt_entry_type, action_jp_t action_jp, uint16_t holdtime, bool is_new_group); int mrt_commit(PimVif *pim_vif, const IPvX& target_nbr_addr); int network_commit(PimVif *pim_vif, const IPvX& target_nbr_addr, string& error_msg); int network_send(PimVif *pim_vif, const IPvX& target_nbr_addr, string& error_msg); uint32_t jp_groups_n() const { return (_jp_groups_n); } uint32_t jp_sources_n() const { return (_jp_sources_n); } void set_jp_groups_n(uint32_t v) { _jp_groups_n = v; } void incr_jp_groups_n() { _jp_groups_n++; } void decr_jp_groups_n() { _jp_groups_n--; } void set_jp_sources_n(uint32_t v) { _jp_sources_n = v; } void incr_jp_sources_n() { _jp_sources_n++; } void decr_jp_sources_n() { _jp_sources_n--; } private: PimNode* _pim_node; // The PIM node int _family; // The address family list _jp_groups_list;// The list of groups uint32_t _jp_groups_n; // Total number of groups uint32_t _jp_sources_n; // Total number of sources (all groups) uint16_t _holdtime; // The Holdtime in the J/P message }; // // Class to keep info about the list of sources entries to Join/Prune // class PimJpSources { public: PimJpSources() : _j_n(0), _p_n(0) {} ~PimJpSources() {} list& j_list() { return (_j_list); } list& p_list() { return (_p_list); } bool j_list_found(const IPvX& ipaddr); bool p_list_found(const IPvX& ipaddr); bool j_list_remove(const IPvX& ipaddr); bool p_list_remove(const IPvX& ipaddr); uint32_t j_n() const { return (_j_n); } void set_j_n(uint32_t v) { _j_n = v; } void incr_j_n() { _j_n++; } void decr_j_n() { _j_n--; } uint32_t p_n() const { return (_p_n); } void set_p_n(uint32_t v) { _p_n = v; } void incr_p_n() { _p_n++; } void decr_p_n() { _p_n--; } private: list _j_list; // List of Join entries list _p_list; // List of Prune entries uint32_t _j_n; // Number of Join entries uint32_t _p_n; // Number of Prune entries }; // // Class to keep info about a group entry that is pending to be // (re)send to a neighbor in a Join-Prune (or Graft) message, // or to be commited to the Multicast Routing Table. // class PimJpGroup { public: PimJpGroup(PimJpHeader& jp_header, int family); int family() const { return (_family); } PimJpHeader& jp_header() { return (_jp_header); } const IPvX& group_addr() const { return (_group_addr); } void set_group_addr(const IPvX& v) { _group_addr = v; } uint8_t group_mask_len() const { return (_group_mask_len); } void set_group_mask_len(uint8_t v) { _group_mask_len = v; } void incr_jp_groups_n() { jp_header().incr_jp_groups_n(); } void decr_jp_groups_n() { jp_header().decr_jp_groups_n(); } uint32_t j_sources_n() const { return (_j_sources_n); } uint32_t p_sources_n() const { return (_p_sources_n); } void set_j_sources_n(uint32_t v) { _j_sources_n = v; } void set_p_sources_n(uint32_t v) { _p_sources_n = v; } void incr_j_sources_n() { _j_sources_n++; jp_header().incr_jp_sources_n(); } void decr_j_sources_n() { _j_sources_n--; jp_header().decr_jp_sources_n(); } void incr_p_sources_n() { _p_sources_n++; jp_header().incr_jp_sources_n(); } void decr_p_sources_n() { _p_sources_n--; jp_header().decr_jp_sources_n(); } PimJpSources *rp() { return (&_rp); } PimJpSources *wc() { return (&_wc); } PimJpSources *sg() { return (&_sg); } PimJpSources *sg_rpt() { return (&_sg_rpt); } size_t message_size() const { return ( ENCODED_GROUP_ADDR_SIZE(_family) + 2 * sizeof(uint16_t) + (ENCODED_SOURCE_ADDR_SIZE(_family) * (_rp.j_n() + _rp.p_n() + _wc.j_n() + _wc.p_n() + _sg.j_n() + _sg.p_n() + _sg_rpt.j_n() + _sg_rpt.p_n()))); } private: PimJpHeader& _jp_header; // The J/P header for the groups // to (re)send int _family; // The address family IPvX _group_addr; // The address of the multicast group uint8_t _group_mask_len; // The 'group_addr' mask length uint32_t _j_sources_n; // The total number of Joined sources uint32_t _p_sources_n; // The total number of Pruned sources // The lists for each type of entry: (*,*,RP), (*,G), (S,G), (S,G,rpt) PimJpSources _rp; // The (*,*,RP) Join/Prune entries PimJpSources _wc; // The (*,G) Join/Prune entries PimJpSources _sg; // The (S,G) Join/Prune entries PimJpSources _sg_rpt; // The (S,G,rpt) Join/Prune entries }; // // Global variables // // // Global functions prototypes // #endif // __PIM_PIM_PROTO_JOIN_PRUNE_MESSAGE_HH__ xorp/pim/xrl_pim_shell_funcs.sh0000664000076400007640000025002211421137511017035 0ustar greearbgreearb#!/bin/sh # # $XORP: xorp/pim/xrl_pim_shell_funcs.sh,v 1.31 2007/04/20 00:14:53 pavlin Exp $ # # # Library of functions to sent XRLs to a running PIM process. # # Conditionally set ${srcdir} if it wasn't assigned (e.g., by `gmake check`) if [ "X${srcdir}" = "X" ] ; then srcdir=`dirname $0` ; fi . ${srcdir}/../utils/xrl_shell_lib.sh # # Conditionally set the target name # IP_VERSION=${IP_VERSION:?"IP_VERSION undefined. Must be defined to either IPV4 or IPV6"} case "${IP_VERSION}" in IPV4) PIM_TARGET=${PIM_TARGET:="PIMSM_4"} ;; IPV6) PIM_TARGET=${PIM_TARGET:="PIMSM_6"} ;; *) echo "Error: invalid IP_VERSION = ${IP_VERSION}. Must be either IPV4 or IPV6" exit 1 ;; esac pim_enable_vif() { if [ $# -lt 2 ] ; then echo "Usage: pim_enable_vif " exit 1 fi vif_name=$1 enable=$2 echo "pim_enable_vif" $* XRL="finder://$PIM_TARGET/pim/0.1/enable_vif" XRL_ARGS="?vif_name:txt=$vif_name&enable:bool=$enable" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } pim_start_vif() { if [ $# -lt 1 ] ; then echo "Usage: pim_start_vif " exit 1 fi vif_name=$1 echo "pim_start_vif" $* XRL="finder://$PIM_TARGET/pim/0.1/start_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } pim_stop_vif() { if [ $# -lt 1 ] ; then echo "Usage: pim_stop_vif " exit 1 fi vif_name=$1 echo "pim_stop_vif" $* XRL="finder://$PIM_TARGET/pim/0.1/stop_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } pim_enable_all_vifs() { if [ $# -lt 1 ] ; then echo "Usage: pim_enable_all_vifs " exit 1 fi enable=$1 echo "pim_enable_all_vifs" $* XRL="finder://$PIM_TARGET/pim/0.1/enable_all_vifs" XRL_ARGS="?enable:bool=$enable" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } pim_start_all_vifs() { echo "pim_start_all_vifs" $* XRL="finder://$PIM_TARGET/pim/0.1/start_all_vifs" XRL_ARGS="" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } pim_stop_all_vifs() { echo "pim_stop_all_vifs" $* XRL="finder://$PIM_TARGET/pim/0.1/stop_all_vifs" XRL_ARGS="" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } pim_enable_pim() { if [ $# -lt 1 ] ; then echo "Usage: pim_enable_pim " exit 1 fi enable=$1 echo "pim_enable_pim" $* XRL="finder://$PIM_TARGET/pim/0.1/enable_pim" XRL_ARGS="?enable:bool=$enable" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } pim_start_pim() { echo "pim_start_pim" $* XRL="finder://$PIM_TARGET/pim/0.1/start_pim" XRL_ARGS="" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } pim_stop_pim() { echo "pim_stop_pim" $* XRL="finder://$PIM_TARGET/pim/0.1/stop_pim" XRL_ARGS="" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } pim_enable_cli() { if [ $# -lt 1 ] ; then echo "Usage: pim_enable_cli " exit 1 fi enable=$1 echo "pim_enable_cli" $* XRL="finder://$PIM_TARGET/pim/0.1/enable_cli" XRL_ARGS="?enable:bool=$enable" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } pim_start_cli() { echo "pim_start_cli" $* XRL="finder://$PIM_TARGET/pim/0.1/start_cli" XRL_ARGS="" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } pim_stop_cli() { echo "pim_stop_cli" $* XRL="finder://$PIM_TARGET/pim/0.1/stop_cli" XRL_ARGS="" call_xrl_wrapper -r 0 $XRL$XRL_ARG } pim_enable_bsr() { if [ $# -lt 1 ] ; then echo "Usage: pim_enable_bsr " exit 1 fi enable=$1 echo "pim_enable_bsr" $* XRL="finder://$PIM_TARGET/pim/0.1/enable_bsr" XRL_ARGS="?enable:bool=$enable" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } pim_start_bsr() { echo "pim_start_bsr" $* XRL="finder://$PIM_TARGET/pim/0.1/start_bsr" XRL_ARGS="" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } pim_stop_bsr() { echo "pim_stop_bsr" $* XRL="finder://$PIM_TARGET/pim/0.1/stop_bsr" XRL_ARGS="" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } # # Add/delete scope zone configuration. # pim_add_config_scope_zone_by_vif_name4() { if [ $# -lt 2 ] ; then echo "Usage: pim_add_config_scope_zone_by_vif_name4 " exit 1 fi scope_zone_id=$1 vif_name=$2 echo "pim_add_config_scope_zone_by_vif_name4" $* XRL="finder://$PIM_TARGET/pim/0.1/add_config_scope_zone_by_vif_name4" XRL_ARGS="?scope_zone_id:ipv4net=$scope_zone_id&vif_name:txt=$vif_name" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } pim_add_config_scope_zone_by_vif_name6() { if [ $# -lt 2 ] ; then echo "Usage: pim_add_config_scope_zone_by_vif_name6 " exit 1 fi scope_zone_id=$1 vif_name=$2 echo "pim_add_config_scope_zone_by_vif_name6" $* XRL="finder://$PIM_TARGET/pim/0.1/add_config_scope_zone_by_vif_name6" XRL_ARGS="?scope_zone_id:ipv6net=$scope_zone_id&vif_name:txt=$vif_name" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } pim_add_config_scope_zone_by_vif_addr4() { if [ $# -lt 2 ] ; then echo "Usage: pim_add_config_scope_zone_by_vif_addr4 " exit 1 fi scope_zone_id=$1 vif_addr=$2 echo "pim_add_config_scope_zone_by_vif_addr4" $* XRL="finder://$PIM_TARGET/pim/0.1/add_config_scope_zone_by_vif_addr4" XRL_ARGS="?scope_zone_id:ipv4net=$scope_zone_id&vif_addr:ipv4=$vif_addr" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } pim_add_config_scope_zone_by_vif_addr6() { if [ $# -lt 2 ] ; then echo "Usage: pim_add_config_scope_zone_by_vif_addr6 " exit 1 fi scope_zone_id=$1 vif_addr=$2 echo "pim_add_config_scope_zone_by_vif_addr6" $* XRL="finder://$PIM_TARGET/pim/0.1/add_config_scope_zone_by_vif_addr6" XRL_ARGS="?scope_zone_id:ipv6net=$scope_zone_id&vif_addr:ipv6=$vif_addr" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } pim_delete_config_scope_zone_by_vif_name4() { if [ $# -lt 2 ] ; then echo "Usage: pim_delete_config_scope_zone_by_vif_name4 " exit 1 fi scope_zone_id=$1 vif_name=$2 echo "pim_delete_config_scope_zone_by_vif_name4" $* XRL="finder://$PIM_TARGET/pim/0.1/delete_config_scope_zone_by_vif_name4" XRL_ARGS="?scope_zone_id:ipv4net=$scope_zone_id&vif_name:txt=$vif_name" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } pim_delete_config_scope_zone_by_vif_name6() { if [ $# -lt 2 ] ; then echo "Usage: pim_delete_config_scope_zone_by_vif_name6 " exit 1 fi scope_zone_id=$1 vif_name=$2 echo "pim_delete_config_scope_zone_by_vif_name6" $* XRL="finder://$PIM_TARGET/pim/0.1/delete_config_scope_zone_by_vif_name6" XRL_ARGS="?scope_zone_id:ipv6net=$scope_zone_id&vif_name:txt=$vif_name" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } pim_delete_config_scope_zone_by_vif_addr4() { if [ $# -lt 2 ] ; then echo "Usage: pim_delete_config_scope_zone_by_vif_addr4 " exit 1 fi scope_zone_id=$1 vif_addr=$2 echo "pim_delete_config_scope_zone_by_vif_addr4" $* XRL="finder://$PIM_TARGET/pim/0.1/delete_config_scope_zone_by_vif_addr4" XRL_ARGS="?scope_zone_id:ipv4net=$scope_zone_id&vif_addr:ipv4=$vif_addr" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } pim_delete_config_scope_zone_by_vif_addr6() { if [ $# -lt 2 ] ; then echo "Usage: pim_delete_config_scope_zone_by_vif_addr6 " exit 1 fi scope_zone_id=$1 vif_addr=$2 echo "pim_delete_config_scope_zone_by_vif_addr6" $* XRL="finder://$PIM_TARGET/pim/0.1/delete_config_scope_zone_by_vif_addr6" XRL_ARGS="?scope_zone_id:ipv6net=$scope_zone_id&vif_addr:ipv6=$vif_addr" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } # # Add/delete Candidate-RP configuration. # pim_add_config_cand_bsr4() { if [ $# -lt 6 ] ; then echo "Usage: pim_add_config_cand_bsr4 " exit 1 fi scope_zone_id=$1 is_scope_zone=$2 vif_name=$3 vif_addr=$4 bsr_priority=$5 hash_mask_len=$6 echo "pim_add_config_cand_bsr4" $* XRL="finder://$PIM_TARGET/pim/0.1/add_config_cand_bsr4" XRL_ARGS="?scope_zone_id:ipv4net=$scope_zone_id&is_scope_zone:bool=$is_scope_zone&vif_name:txt=$vif_name&vif_addr:ipv4=$vif_addr&bsr_priority:u32=$bsr_priority&hash_mask_len:u32=$hash_mask_len" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } pim_add_config_cand_bsr6() { if [ $# -lt 6 ] ; then echo "Usage: pim_add_config_cand_bsr6 " exit 1 fi scope_zone_id=$1 is_scope_zone=$2 vif_name=$3 vif_addr=$4 bsr_priority=$5 hash_mask_len=$6 echo "pim_add_config_cand_bsr6" $* XRL="finder://$PIM_TARGET/pim/0.1/add_config_cand_bsr6" XRL_ARGS="?scope_zone_id:ipv6net=$scope_zone_id&is_scope_zone:bool=$is_scope_zone&vif_name:txt=$vif_name&vif_addr:ipv6=$vif_addr&bsr_priority:u32=$bsr_priority&hash_mask_len:u32=$hash_mask_len" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } pim_delete_config_cand_bsr4() { if [ $# -lt 2 ] ; then echo "Usage: pim_delete_config_cand_bsr4 " exit 1 fi scope_zone_id=$1 is_scope_zone=$2 echo "pim_delete_config_cand_bsr4" $* XRL="finder://$PIM_TARGET/pim/0.1/delete_config_cand_bsr4" XRL_ARGS="?scope_zone_id:ipv4net=$scope_zone_id&is_scope_zone:bool=$is_scope_zone" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } pim_delete_config_cand_bsr6() { if [ $# -lt 2 ] ; then echo "Usage: pim_delete_config_cand_bsr6 " exit 1 fi scope_zone_id=$1 is_scope_zone=$2 echo "pim_delete_config_cand_bsr6" $* XRL="finder://$PIM_TARGET/pim/0.1/delete_config_cand_bsr6" XRL_ARGS="?scope_zone_id:ipv6net=$scope_zone_id&is_scope_zone:bool=$is_scope_zone" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } pim_add_config_cand_rp4() { if [ $# -lt 6 ] ; then echo "Usage: pim_add_config_cand_rp4 " exit 1 fi group_prefix=$1 is_scope_zone=$2 vif_name=$3 vif_addr=$4 rp_priority=$5 rp_holdtime=$6 echo "pim_add_config_cand_rp4" $* XRL="finder://$PIM_TARGET/pim/0.1/add_config_cand_rp4" XRL_ARGS="?group_prefix:ipv4net=$group_prefix&is_scope_zone:bool=$is_scope_zone&vif_name:txt=$vif_name&vif_addr:ipv4=$vif_addr&rp_priority:u32=$rp_priority&rp_holdtime:u32=$rp_holdtime" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } pim_add_config_cand_rp6() { if [ $# -lt 6 ] ; then echo "Usage: pim_add_config_cand_rp6 " exit 1 fi group_prefix=$1 is_scope_zone=$2 vif_name=$3 vif_addr=$4 rp_priority=$5 rp_holdtime=$6 echo "pim_add_config_cand_rp6" $* XRL="finder://$PIM_TARGET/pim/0.1/add_config_cand_rp6" XRL_ARGS="?group_prefix:ipv6net=$group_prefix&is_scope_zone:bool=$is_scope_zone&vif_name:txt=$vif_name&vif_addr:ipv6=$vif_addr&rp_priority:u32=$rp_priority&rp_holdtime:u32=$rp_holdtime" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } pim_delete_config_cand_rp4() { if [ $# -lt 3 ] ; then echo "Usage: pim_delete_config_cand_rp4 " exit 1 fi group_prefix=$1 is_scope_zone=$2 vif_name=$3 vif_addr=$4 echo "pim_delete_config_cand_rp4" $* XRL="finder://$PIM_TARGET/pim/0.1/delete_config_cand_rp4" XRL_ARGS="?group_prefix:ipv4net=$group_prefix&is_scope_zone:bool=$is_scope_zone&vif_name:txt=$vif_name&vif_addr:ipv4=$vif_addr" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } pim_delete_config_cand_rp6() { if [ $# -lt 3 ] ; then echo "Usage: pim_delete_config_cand_rp6 " exit 1 fi group_prefix=$1 is_scope_zone=$2 vif_name=$3 vif_addr=$4 echo "pim_delete_config_cand_rp6" $* XRL="finder://$PIM_TARGET/pim/0.1/delete_config_cand_rp6" XRL_ARGS="?group_prefix:ipv6net=$group_prefix&is_scope_zone:bool=$is_scope_zone&vif_name:txt=$vif_name&vif_addr:ipv6=$vif_addr" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } pim_add_config_static_rp4() { if [ $# -lt 4 ] ; then echo "Usage: pim_add_config_static_rp4 " exit 1 fi group_prefix=$1 rp_addr=$2 rp_priority=$3 hash_mask_len=$4 echo "pim_add_config_static_rp4" $* XRL="finder://$PIM_TARGET/pim/0.1/add_config_static_rp4" XRL_ARGS="?group_prefix:ipv4net=$group_prefix&rp_addr:ipv4=$rp_addr&rp_priority:u32=$rp_priority&hash_mask_len:u32=$hash_mask_len" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } pim_add_config_static_rp6() { if [ $# -lt 4 ] ; then echo "Usage: pim_add_config_static_rp6 " exit 1 fi group_prefix=$1 rp_addr=$2 rp_priority=$3 hash_mask_len=$4 echo "pim_add_config_static_rp6" $* XRL="finder://$PIM_TARGET/pim/0.1/add_config_static_rp6" XRL_ARGS="?group_prefix:ipv6net=$group_prefix&rp_addr:ipv6=$rp_addr&rp_priority:u32=$rp_priority&hash_mask_len:u32=$hash_mask_len" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } pim_delete_config_static_rp4() { if [ $# -lt 2 ] ; then echo "Usage: pim_delete_config_static_rp4 " exit 1 fi group_prefix=$1 rp_addr=$2 echo "pim_delete_config_static_rp4" $* XRL="finder://$PIM_TARGET/pim/0.1/delete_config_static_rp4" XRL_ARGS="?group_prefix:ipv4net=$group_prefix&rp_addr:ipv4=$rp_addr" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } pim_delete_config_static_rp6() { if [ $# -lt 2 ] ; then echo "Usage: pim_delete_config_static_rp6 " exit 1 fi group_prefix=$1 rp_addr=$2 echo "pim_delete_config_static_rp6" $* XRL="finder://$PIM_TARGET/pim/0.1/delete_config_static_rp6" XRL_ARGS="?group_prefix:ipv6net=$group_prefix&rp_addr:ipv6=$rp_addr" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } pim_delete_config_all_static_group_prefixes_rp4() { if [ $# -lt 1 ] ; then echo "Usage: pim_delete_config_all_static_group_prefixes_rp4 " exit 1 fi rp_addr=$1 echo "pim_delete_config_all_static_group_prefixes_rp4" $* XRL="finder://$PIM_TARGET/pim/0.1/delete_config_all_static_group_prefixes_rp4" XRL_ARGS="?rp_addr:ipv4=$rp_addr" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } pim_delete_config_all_static_rps() { if [ $# -lt 0 ] ; then echo "Usage: pim_delete_config_all_static_rps" exit 1 fi echo "pim_delete_config_all_static_rps" $* XRL="finder://$PIM_TARGET/pim/0.1/delete_config_all_static_rps" XRL_ARGS="" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } pim_config_static_rp_done() { if [ $# -lt 0 ] ; then echo "Usage: pim_config_static_rp_done" exit 1 fi echo "pim_config_static_rp_done" $* XRL="finder://$PIM_TARGET/pim/0.1/config_static_rp_done" XRL_ARGS="" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } # # Configure PIM Hello-related metrics. # pim_get_vif_proto_version() { if [ $# -lt 1 ] ; then echo "Usage: pim_get_vif_proto_version " exit 1 fi vif_name=$1 echo "pim_get_vif_proto_version" $* XRL="finder://$PIM_TARGET/pim/0.1/get_vif_proto_version" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p proto_version:u32 $XRL$XRL_ARGS } pim_set_vif_proto_version() { if [ $# -lt 2 ] ; then echo "Usage: pim_set_vif_proto_version " exit 1 fi vif_name=$1 proto_version=$2 echo "pim_set_vif_proto_version" $* XRL="finder://$PIM_TARGET/pim/0.1/set_vif_proto_version" XRL_ARGS="?vif_name:txt=$vif_name&proto_version:u32=$proto_version" call_xrl_wrapper $XRL$XRL_ARGS } pim_reset_vif_proto_version() { if [ $# -lt 1 ] ; then echo "Usage: pim_reset_vif_proto_version " exit 1 fi vif_name=$1 echo "pim_reset_vif_proto_version" $* XRL="finder://$PIM_TARGET/pim/0.1/reset_vif_proto_version" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper $XRL$XRL_ARGS } pim_get_vif_hello_triggered_delay() { if [ $# -lt 1 ] ; then echo "Usage: pim_get_vif_hello_triggered_delay " exit 1 fi vif_name=$1 echo "pim_get_vif_hello_triggered_delay" $* XRL="finder://$PIM_TARGET/pim/0.1/get_vif_hello_triggered_delay" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p hello_triggered_delay:u32 $XRL$XRL_ARGS } pim_set_vif_hello_triggered_delay() { if [ $# -lt 2 ] ; then echo "Usage: pim_set_vif_hello_triggered_delay " exit 1 fi vif_name=$1 hello_triggered_delay=$2 echo "pim_set_vif_hello_triggered_delay" $* XRL="finder://$PIM_TARGET/pim/0.1/set_vif_hello_triggered_delay" XRL_ARGS="?vif_name:txt=$vif_name&hello_triggered_delay:u32=$hello_triggered_delay" call_xrl_wrapper $XRL$XRL_ARGS } pim_reset_vif_hello_triggered_delay() { if [ $# -lt 1 ] ; then echo "Usage: pim_reset_vif_hello_triggered_delay " exit 1 fi vif_name=$1 echo "pim_reset_vif_hello_triggered_delay" $* XRL="finder://$PIM_TARGET/pim/0.1/reset_vif_hello_triggered_delay" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper $XRL$XRL_ARGS } pim_get_vif_hello_period() { if [ $# -lt 1 ] ; then echo "Usage: pim_get_vif_hello_period " exit 1 fi vif_name=$1 echo "pim_get_vif_hello_period" $* XRL="finder://$PIM_TARGET/pim/0.1/get_vif_hello_period" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p hello_period:u32 $XRL$XRL_ARGS } pim_set_vif_hello_period() { if [ $# -lt 2 ] ; then echo "Usage: pim_set_vif_hello_period " exit 1 fi vif_name=$1 hello_period=$2 echo "pim_set_vif_hello_period" $* XRL="finder://$PIM_TARGET/pim/0.1/set_vif_hello_period" XRL_ARGS="?vif_name:txt=$vif_name&hello_period:u32=$hello_period" call_xrl_wrapper $XRL$XRL_ARGS } pim_reset_vif_hello_period() { if [ $# -lt 1 ] ; then echo "Usage: pim_reset_vif_hello_period " exit 1 fi vif_name=$1 echo "pim_reset_vif_hello_period" $* XRL="finder://$PIM_TARGET/pim/0.1/reset_vif_hello_period" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper $XRL$XRL_ARGS } pim_get_vif_hello_holdtime() { if [ $# -lt 1 ] ; then echo "Usage: pim_get_vif_hello_holdtime " exit 1 fi vif_name=$1 echo "pim_get_vif_hello_holdtime" $* XRL="finder://$PIM_TARGET/pim/0.1/get_vif_hello_holdtime" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p hello_holdtime:u32 $XRL$XRL_ARGS } pim_set_vif_hello_holdtime() { if [ $# -lt 2 ] ; then echo "Usage: pim_set_vif_hello_holdtime " exit 1 fi vif_name=$1 hello_holdtime=$2 echo "pim_set_vif_hello_holdtime" $* XRL="finder://$PIM_TARGET/pim/0.1/set_vif_hello_holdtime" XRL_ARGS="?vif_name:txt=$vif_name&hello_holdtime:u32=$hello_holdtime" call_xrl_wrapper $XRL$XRL_ARGS } pim_reset_vif_hello_holdtime() { if [ $# -lt 1 ] ; then echo "Usage: pim_reset_vif_hello_holdtime " exit 1 fi vif_name=$1 echo "pim_reset_vif_hello_holdtime" $* XRL="finder://$PIM_TARGET/pim/0.1/reset_vif_hello_holdtime" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper $XRL$XRL_ARGS } pim_get_vif_dr_priority() { if [ $# -lt 1 ] ; then echo "Usage: pim_get_vif_dr_priority " exit 1 fi vif_name=$1 echo "pim_get_vif_dr_priority" $* XRL="finder://$PIM_TARGET/pim/0.1/get_vif_dr_priority" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p dr_priority:u32 $XRL$XRL_ARGS } pim_set_vif_dr_priority() { if [ $# -lt 2 ] ; then echo "Usage: pim_set_vif_dr_priority " exit 1 fi vif_name=$1 dr_priority=$2 echo "pim_set_vif_dr_priority" $* XRL="finder://$PIM_TARGET/pim/0.1/set_vif_dr_priority" XRL_ARGS="?vif_name:txt=$vif_name&dr_priority:u32=$dr_priority" call_xrl_wrapper $XRL$XRL_ARGS } pim_reset_vif_dr_priority() { if [ $# -lt 1 ] ; then echo "Usage: pim_reset_vif_dr_priority " exit 1 fi vif_name=$1 echo "pim_reset_vif_dr_priority" $* XRL="finder://$PIM_TARGET/pim/0.1/reset_vif_dr_priority" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper $XRL$XRL_ARGS } pim_get_vif_propagation_delay() { if [ $# -lt 1 ] ; then echo "Usage: pim_get_vif_propagation_delay " exit 1 fi vif_name=$1 echo "pim_get_vif_propagation_delay" $* XRL="finder://$PIM_TARGET/pim/0.1/get_vif_propagation_delay" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p propagation_delay:u32 $XRL$XRL_ARGS } pim_set_vif_propagation_delay() { if [ $# -lt 2 ] ; then echo "Usage: pim_set_vif_propagation_delay " exit 1 fi vif_name=$1 propagation_delay=$2 echo "pim_set_vif_propagation_delay" $* XRL="finder://$PIM_TARGET/pim/0.1/set_vif_propagation_delay" XRL_ARGS="?vif_name:txt=$vif_name&propagation_delay:u32=$propagation_delay" call_xrl_wrapper $XRL$XRL_ARGS } pim_reset_vif_propagation_delay() { if [ $# -lt 1 ] ; then echo "Usage: pim_reset_vif_propagation_delay " exit 1 fi vif_name=$1 echo "pim_reset_vif_propagation_delay" $* XRL="finder://$PIM_TARGET/pim/0.1/reset_vif_propagation_delay" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper $XRL$XRL_ARGS } pim_get_vif_override_interval() { if [ $# -lt 1 ] ; then echo "Usage: pim_get_vif_override_interval " exit 1 fi vif_name=$1 echo "pim_get_vif_override_interval" $* XRL="finder://$PIM_TARGET/pim/0.1/get_vif_override_interval" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p override_interval:u32 $XRL$XRL_ARGS } pim_set_vif_override_interval() { if [ $# -lt 2 ] ; then echo "Usage: pim_set_vif_override_interval " exit 1 fi vif_name=$1 override_interval=$2 echo "pim_set_vif_override_interval" $* XRL="finder://$PIM_TARGET/pim/0.1/set_vif_override_interval" XRL_ARGS="?vif_name:txt=$vif_name&override_interval:u32=$override_interval" call_xrl_wrapper $XRL$XRL_ARGS } pim_reset_vif_override_interval() { if [ $# -lt 1 ] ; then echo "Usage: pim_reset_vif_override_interval " exit 1 fi vif_name=$1 echo "pim_reset_vif_override_interval" $* XRL="finder://$PIM_TARGET/pim/0.1/reset_vif_override_interval" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper $XRL$XRL_ARGS } pim_get_vif_is_tracking_support_disabled() { if [ $# -lt 1 ] ; then echo "Usage: pim_get_vif_is_tracking_support_disabled " exit 1 fi vif_name=$1 echo "pim_get_vif_is_tracking_support_disabled" $* XRL="finder://$PIM_TARGET/pim/0.1/get_vif_is_tracking_support_disabled" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p is_tracking_support_disabled:bool $XRL$XRL_ARGS } pim_set_vif_is_tracking_support_disabled() { if [ $# -lt 2 ] ; then echo "Usage: pim_set_vif_is_tracking_support_disabled " exit 1 fi vif_name=$1 is_tracking_support_disabled=$2 echo "pim_set_vif_is_tracking_support_disalbed" $* XRL="finder://$PIM_TARGET/pim/0.1/set_vif_is_tracking_support_disabled" XRL_ARGS="?vif_name:txt=$vif_name&is_tracking_support_disabled:bool=$is_tracking_support_disabled" call_xrl_wrapper $XRL$XRL_ARGS } pim_reset_vif_is_tracking_support_disabled() { if [ $# -lt 1 ] ; then echo "Usage: pim_reset_vif_is_tracking_support_disabled " exit 1 fi vif_name=$1 echo "pim_reset_vif_is_tracking_support_disabled" $* XRL="finder://$PIM_TARGET/pim/0.1/reset_vif_is_tracking_support_disabled" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper $XRL$XRL_ARGS } pim_get_vif_accept_nohello_neighbors() { if [ $# -lt 1 ] ; then echo "Usage: pim_get_vif_accept_nohello_neighbors " exit 1 fi vif_name=$1 echo "pim_get_vif_accept_nohello_neighbors" $* XRL="finder://$PIM_TARGET/pim/0.1/get_vif_accept_nohello_neighbors" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p accept_nohello_neighbors:bool $XRL$XRL_ARGS } pim_set_vif_accept_nohello_neighbors() { if [ $# -lt 2 ] ; then echo "Usage: pim_set_vif_accept_nohello_neighbors " exit 1 fi vif_name=$1 accept_nohello_neighbors=$2 echo "pim_set_vif_accept_nohello_neighbors" $* XRL="finder://$PIM_TARGET/pim/0.1/set_vif_accept_nohello_neighbors" XRL_ARGS="?vif_name:txt=$vif_name&accept_nohello_neighbors:bool=$accept_nohello_neighbors" call_xrl_wrapper $XRL$XRL_ARGS } pim_reset_vif_accept_nohello_neighbors() { if [ $# -lt 1 ] ; then echo "Usage: pim_reset_vif_accept_nohello_neighbors " exit 1 fi vif_name=$1 echo "pim_reset_vif_accept_nohello_neighbors" $* XRL="finder://$PIM_TARGET/pim/0.1/reset_vif_accept_nohello_neighbors" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper $XRL$XRL_ARGS } pim_get_vif_join_prune_period() { if [ $# -lt 1 ] ; then echo "Usage: pim_get_vif_join_prune_period " exit 1 fi vif_name=$1 echo "pim_get_vif_join_prune_period" $* XRL="finder://$PIM_TARGET/pim/0.1/get_vif_join_prune_period" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p join_prune_period:u32 $XRL$XRL_ARGS } pim_set_vif_join_prune_period() { if [ $# -lt 2 ] ; then echo "Usage: pim_set_vif_join_prune_period " exit 1 fi vif_name=$1 join_prune_period=$2 echo "pim_set_vif_join_prune_period" $* XRL="finder://$PIM_TARGET/pim/0.1/set_vif_join_prune_period" XRL_ARGS="?vif_name:txt=$vif_name&join_prune_period:u32=$join_prune_period" call_xrl_wrapper $XRL$XRL_ARGS } pim_reset_vif_join_prune_period() { if [ $# -lt 1 ] ; then echo "Usage: pim_reset_vif_join_prune_period " exit 1 fi vif_name=$1 echo "pim_reset_vif_join_prune_period" $* XRL="finder://$PIM_TARGET/pim/0.1/reset_vif_join_prune_period" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper $XRL$XRL_ARGS } pim_get_switch_to_spt_threshold() { if [ $# -lt 0 ] ; then echo "Usage: pim_get_switch_to_spt_threshold" exit 1 fi echo "pim_get_switch_to_spt_threshold" $* XRL="finder://$PIM_TARGET/pim/0.1/get_switch_to_spt_threshold" XRL_ARGS="" call_xrl_wrapper -p is_enabled:bool -p interval_sec:u32 -p bytes:u32 $XRL$XRL_ARGS } pim_set_switch_to_spt_threshold() { if [ $# -lt 3 ] ; then echo "Usage: pim_set_switch_to_spt_threshold " exit 1 fi is_enabled=$1 interval_sec=$2 bytes=$3 echo "pim_set_switch_to_spt_threshold" $* XRL="finder://$PIM_TARGET/pim/0.1/set_switch_to_spt_threshold" XRL_ARGS="?is_enabled:bool=$is_enabled&interval_sec:u32=$interval_sec&bytes:u32=$bytes" call_xrl_wrapper $XRL$XRL_ARGS } pim_reset_switch_to_spt_threshold() { if [ $# -lt 0 ] ; then echo "Usage: pim_reset_switch_to_spt_threshold" exit 1 fi echo "pim_reset_switch_to_spt_threshold" $* XRL="finder://$PIM_TARGET/pim/0.1/reset_switch_to_spt_threshold" XRL_ARGS="" call_xrl_wrapper $XRL$XRL_ARGS } pim_add_alternative_subnet4() { if [ $# -lt 2 ] ; then echo "Usage: pim_add_alternative_subnet4 " exit 1 fi vif_name=$1 subnet=$2 echo "pim_add_alternative_subnet4" $* XRL="finder://$PIM_TARGET/pim/0.1/add_alternative_subnet4" XRL_ARGS="?vif_name:txt=$vif_name&subnet:ipv4net=$subnet" call_xrl_wrapper $XRL$XRL_ARGS } pim_add_alternative_subnet6() { if [ $# -lt 2 ] ; then echo "Usage: pim_add_alternative_subnet6 " exit 1 fi vif_name=$1 subnet=$2 echo "pim_add_alternative_subnet6" $* XRL="finder://$PIM_TARGET/pim/0.1/add_alternative_subnet6" XRL_ARGS="?vif_name:txt=$vif_name&subnet:ipv6net=$subnet" call_xrl_wrapper $XRL$XRL_ARGS } pim_delete_alternative_subnet4() { if [ $# -lt 2 ] ; then echo "Usage: pim_delete_alternative_subnet4 " exit 1 fi vif_name=$1 subnet=$2 echo "pim_delete_alternative_subnet4" $* XRL="finder://$PIM_TARGET/pim/0.1/delete_alternative_subnet4" XRL_ARGS="?vif_name:txt=$vif_name&subnet:ipv4net=$subnet" call_xrl_wrapper $XRL$XRL_ARGS } pim_delete_alternative_subnet6() { if [ $# -lt 2 ] ; then echo "Usage: pim_delete_alternative_subnet6 " exit 1 fi vif_name=$1 subnet=$2 echo "pim_delete_alternative_subnet6" $* XRL="finder://$PIM_TARGET/pim/0.1/delete_alternative_subnet6" XRL_ARGS="?vif_name:txt=$vif_name&subnet:ipv6net=$subnet" call_xrl_wrapper $XRL$XRL_ARGS } pim_remove_all_alternative_subnets() { if [ $# -lt 1 ] ; then echo "Usage: pim_remove_all_alternative_subnets " exit 1 fi vif_name=$1 echo "pim_remove_all_alternative_subnets" $* XRL="finder://$PIM_TARGET/pim/0.1/remove_all_alternative_subnets" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper $XRL$XRL_ARGS } pim_log_trace_all() { if [ $# -lt 1 ] ; then echo "Usage: pim_log_trace_all " exit 1 fi enable=$1 echo "pim_log_trace_all" $* XRL="finder://$PIM_TARGET/pim/0.1/log_trace_all" XRL_ARGS="?enable:bool=$enable" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } pim_add_test_jp_entry4() { if [ $# -lt 7 ] ; then echo "Usage: pim_add_test_jp_entry4 " exit 1 fi source_addr=$1 group_addr=$2 group_mask_len=$3 mrt_entry_type=$4 # Must be one of: SG, SG_RPT, WC, RP action_jp=$5 # Must be one of: JOIN, PRUNE holdtime=$6 is_new_group=$7 echo "pim_add_test_jp_entry4" $* XRL="finder://$PIM_TARGET/pim/0.1/add_test_jp_entry4" XRL_ARGS="?source_addr:ipv4=$source_addr&group_addr:ipv4=$group_addr&group_mask_len:u32=$group_mask_len&mrt_entry_type:txt=$mrt_entry_type&action_jp:txt=$action_jp&holdtime:u32=$holdtime&is_new_group:bool=$is_new_group" call_xrl_wrapper $XRL$XRL_ARGS } pim_add_test_jp_entry6() { if [ $# -lt 7 ] ; then echo "Usage: pim_add_test_jp_entry6 " exit 1 fi source_addr=$1 group_addr=$2 group_mask_len=$3 mrt_entry_type=$4 # Must be one of: SG, SG_RPT, WC, RP action_jp=$5 # Must be one of: JOIN, PRUNE holdtime=$6 is_new_group=$7 echo "pim_add_test_jp_entry6" $* XRL="finder://$PIM_TARGET/pim/0.1/add_test_jp_entry6" XRL_ARGS="?source_addr:ipv6=$source_addr&group_addr:ipv6=$group_addr&group_mask_len:u32=$group_mask_len&mrt_entry_type:txt=$mrt_entry_type&action_jp:txt=$action_jp&holdtime:u32=$holdtime&is_new_group:bool=$is_new_group" call_xrl_wrapper $XRL$XRL_ARGS } pim_send_test_jp_entry4() { if [ $# -lt 2 ] ; then echo "Usage: pim_send_test_jp_entry4 " exit 1 fi vif_name=$1 nbr_addr=$2 echo "pim_send_test_jp_entry4" $* XRL="finder://$PIM_TARGET/pim/0.1/send_test_jp_entry4" XRL_ARGS="?vif_name:txt=$vif_name&nbr_addr:ipv4=$nbr_addr" call_xrl_wrapper $XRL$XRL_ARGS } pim_send_test_jp_entry6() { if [ $# -lt 2 ] ; then echo "Usage: pim_send_test_jp_entry6 " exit 1 fi vif_name=$1 nbr_addr=$2 echo "pim_send_test_jp_entry6" $* XRL="finder://$PIM_TARGET/pim/0.1/send_test_jp_entry6" XRL_ARGS="?vif_name:txt=$vif_name&nbr_addr:ipv6=$nbr_addr" call_xrl_wrapper $XRL$XRL_ARGS } pim_send_test_assert4() { if [ $# -lt 6 ] ; then echo "Usage: pim_send_test_assert4 " exit 1 fi vif_name=$1 source_addr=$2 group_addr=$3 rpt_bit=$4 metric_preference=$5 metric=$6 echo "pim_send_test_assert4" $* XRL="finder://$PIM_TARGET/pim/0.1/send_test_assert4" XRL_ARGS="?vif_name:txt=$vif_name&source_addr:ipv4=$source_addr&group_addr:ipv4=$group_addr&rpt_bit:bool=$rpt_bit&metric_preference:u32=$metric_preference&metric:u32=$metric" call_xrl_wrapper $XRL$XRL_ARGS } pim_send_test_assert6() { if [ $# -lt 6 ] ; then echo "Usage: pim_send_test_assert6 " exit 1 fi vif_name=$1 source_addr=$2 group_addr=$3 rpt_bit=$4 metric_preference=$5 metric=$6 echo "pim_send_test_assert6" $* XRL="finder://$PIM_TARGET/pim/0.1/send_test_assert6" XRL_ARGS="?vif_name:txt=$vif_name&source_addr:ipv6=$source_addr&group_addr:ipv6=$group_addr&rpt_bit:bool=$rpt_bit&metric_preference:u32=$metric_preference&metric:u32=$metric" call_xrl_wrapper $XRL$XRL_ARGS } pim_add_test_bsr_zone4() { if [ $# -lt 6 ] ; then echo "Usage: pim_add_test_bsr_zone4 " exit 1 fi zone_id_scope_zone_prefix=$1 zone_id_is_scope_zone=$2 bsr_addr=$3 bsr_priority=$4 hash_mask_len=$5 fragment_tag=$6 echo "pim_add_test_bsr_zone4" $* XRL="finder://$PIM_TARGET/pim/0.1/add_test_bsr_zone4" XRL_ARGS="?zone_id_scope_zone_prefix:ipv4net=$zone_id_scope_zone_prefix&zone_id_is_scope_zone:bool=$zone_id_is_scope_zone&bsr_addr:ipv4=$bsr_addr&bsr_priority:u32=$bsr_priority&hash_mask_len:u32=$hash_mask_len&fragment_tag:u32=$fragment_tag" call_xrl_wrapper $XRL$XRL_ARGS } pim_add_test_bsr_zone6() { if [ $# -lt 6 ] ; then echo "Usage: pim_add_test_bsr_zone6 " exit 1 fi zone_id_scope_zone_prefix=$1 zone_id_is_scope_zone=$2 bsr_addr=$3 bsr_priority=$4 hash_mask_len=$5 fragment_tag=$6 echo "pim_add_test_bsr_zone6" $* XRL="finder://$PIM_TARGET/pim/0.1/add_test_bsr_zone6" XRL_ARGS="?zone_id_scope_zone_prefix:ipv6net=$zone_id_scope_zone_prefix&zone_id_is_scope_zone:bool=$zone_id_is_scope_zone&bsr_addr:ipv6=$bsr_addr&bsr_priority:u32=$bsr_priority&hash_mask_len:u32=$hash_mask_len&fragment_tag:u32=$fragment_tag" call_xrl_wrapper $XRL$XRL_ARGS } pim_add_test_bsr_group_prefix4() { if [ $# -lt 5 ] ; then echo "Usage: pim_add_test_bsr_group_prefix4 " exit 1 fi zone_id_scope_zone_prefix=$1 zone_id_is_scope_zone=$2 group_prefix=$3 is_scope_zone=$4 expected_rp_count=$5 echo "pim_add_test_bsr_group_prefix4" $* XRL="finder://$PIM_TARGET/pim/0.1/add_test_bsr_group_prefix4" XRL_ARGS="?zone_id_scope_zone_prefix:ipv4net=$zone_id_scope_zone_prefix&zone_id_is_scope_zone:bool=$zone_id_is_scope_zone&group_prefix:ipv4net=$group_prefix&is_scope_zone:bool=$is_scope_zone&expected_rp_count:u32=$expected_rp_count" call_xrl_wrapper $XRL$XRL_ARGS } pim_add_test_bsr_group_prefix6() { if [ $# -lt 5 ] ; then echo "Usage: pim_add_test_bsr_group_prefix6 " exit 1 fi zone_id_scope_zone_prefix=$1 zone_id_is_scope_zone=$2 group_prefix=$3 is_scope_zone=$4 expected_rp_count=$5 echo "pim_add_test_bsr_group_prefix6" $* XRL="finder://$PIM_TARGET/pim/0.1/add_test_bsr_group_prefix6" XRL_ARGS="?zone_id_scope_zone_prefix:ipv6net=$zone_id_scope_zone_prefix&zone_id_is_scope_zone:bool=$zone_id_is_scope_zone&group_prefix:ipv6net=$group_prefix&is_scope_zone:bool=$is_scope_zone&expected_rp_count:u32=$expected_rp_count" call_xrl_wrapper $XRL$XRL_ARGS } pim_add_test_bsr_rp4() { if [ $# -lt 6 ] ; then echo "Usage: pim_add_test_bsr_rp4 " exit 1 fi zone_id_scope_zone_prefix=$1 zone_id_is_scope_zone=$2 group_prefix=$3 rp_addr=$4 rp_priority=$5 rp_holdtime=$6 echo "pim_add_test_bsr_rp4" $* XRL="finder://$PIM_TARGET/pim/0.1/add_test_bsr_rp4" XRL_ARGS="?zone_id_scope_zone_prefix:ipv4net=$zone_id_scope_zone_prefix&zone_id_is_scope_zone:bool=$zone_id_is_scope_zone&group_prefix:ipv4net=$group_prefix&rp_addr:ipv4=$rp_addr&rp_priority:u32=$rp_priority&rp_holdtime:u32=$rp_holdtime" call_xrl_wrapper $XRL$XRL_ARGS } pim_add_test_bsr_rp6() { if [ $# -lt 6 ] ; then echo "Usage: pim_add_test_bsr_rp6 " exit 1 fi zone_id_scope_zone_prefix=$1 zone_id_is_scope_zone=$2 group_prefix=$3 rp_addr=$4 rp_priority=$5 rp_holdtime=$6 echo "pim_add_test_bsr_rp6" $* XRL="finder://$PIM_TARGET/pim/0.1/add_test_bsr_rp6" XRL_ARGS="?zone_id_scope_zone_prefix:ipv6net=$zone_id_scope_zone_prefix&zone_id_is_scope_zone:bool=$zone_id_is_scope_zone&group_prefix:ipv6net=$group_prefix&rp_addr:ipv6=$rp_addr&rp_priority:u32=$rp_priority&rp_holdtime:u32=$rp_holdtime" call_xrl_wrapper $XRL$XRL_ARGS } pim_send_test_bootstrap() { if [ $# -lt 1 ] ; then echo "Usage: pim_send_test_bootstrap " exit 1 fi vif_name=$1 echo "pim_send_test_bootstrap" $* XRL="finder://$PIM_TARGET/pim/0.1/send_test_bootstrap" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper $XRL$XRL_ARGS } pim_send_test_bootstrap_by_dest4() { if [ $# -lt 2 ] ; then echo "Usage: pim_send_test_bootstrap_by_dest4 " exit 1 fi vif_name=$1 dest_addr=$2 echo "pim_send_test_bootstrap_by_dest4" $* XRL="finder://$PIM_TARGET/pim/0.1/send_test_bootstrap_by_dest4" XRL_ARGS="?vif_name:txt=$vif_name&dest_addr:ipv4=$dest_addr" call_xrl_wrapper $XRL$XRL_ARGS } pim_send_test_bootstrap_by_dest6() { if [ $# -lt 2 ] ; then echo "Usage: pim_send_test_bootstrap_by_dest6 " exit 1 fi vif_name=$1 dest_addr=$2 echo "pim_send_test_bootstrap_by_dest6" $* XRL="finder://$PIM_TARGET/pim/0.1/send_test_bootstrap_by_dest6" XRL_ARGS="?vif_name:txt=$vif_name&dest_addr:ipv6=$dest_addr" call_xrl_wrapper $XRL$XRL_ARGS } pim_send_test_cand_rp_adv() { if [ $# -lt 0 ] ; then echo "Usage: pim_send_test_bootstrap" exit 1 fi echo "pim_send_test_cand_rp_adv" $* XRL="finder://$PIM_TARGET/pim/0.1/send_test_cand_rp_adv" XRL_ARGS="" call_xrl_wrapper $XRL$XRL_ARGS } pim_pimstat_neighbors4() { if [ $# -lt 0 ] ; then echo "Usage: pim_pimstat_neighbors4" exit 1 fi echo "pim_pimstat_neighbors4" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_neighbors4" XRL_ARGS="" call_xrl_wrapper -p all $XRL$XRL_ARGS } pim_pimstat_neighbors6() { if [ $# -lt 0 ] ; then echo "Usage: pim_pimstat_neighbors6" exit 1 fi echo "pim_pimstat_neighbors6" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_neighbors6" XRL_ARGS="" call_xrl_wrapper -p all $XRL$XRL_ARGS } pim_pimstat_interface4() { if [ $# -lt 1 ] ; then echo "Usage: pim_pimstat_interface4 " exit 1 fi vif_name=$1 echo "pim_pimstat_interface4" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_interface4" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p all $XRL$XRL_ARGS } pim_pimstat_interface6() { if [ $# -lt 1 ] ; then echo "Usage: pim_pimstat_interface6 " exit 1 fi vif_name=$1 echo "pim_pimstat_interface6" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_interface6" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p all $XRL$XRL_ARGS } pim_pimstat_rps4() { if [ $# -lt 0 ] ; then echo "Usage: pim_pimstat_rps4" exit 1 fi echo "pim_pimstat_rps4" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_rps4" XRL_ARGS="" call_xrl_wrapper -p all $XRL$XRL_ARGS } pim_pimstat_rps6() { if [ $# -lt 0 ] ; then echo "Usage: pim_pimstat_rps6" exit 1 fi echo "pim_pimstat_rps6" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_rps6" XRL_ARGS="" call_xrl_wrapper -p all $XRL$XRL_ARGS } pim_clear_pim_statistics() { if [ $# -lt 0 ] ; then echo "Usage: pim_clear_pim_statistics" exit 1 fi echo "pim_clear_pim_statistics" $* XRL="finder://$PIM_TARGET/pim/0.1/clear_pim_statistics" XRL_ARGS="" call_xrl_wrapper $XRL$XRL_ARGS } pim_clear_pim_statistics_per_vif() { if [ $# -lt 1 ] ; then echo "Usage: pim_clear_pim_statistics_per_vif " exit 1 fi vif_name=$1 echo "pim_clear_pim_statistics_per_vif" $* XRL="finder://$PIM_TARGET/pim/0.1/clear_pim_statistics_per_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper $XRL$XRL_ARGS } # # Statistics-related counters and values # pim_pimstat_hello_messages_received() { if [ $# -lt 0 ] ; then echo "Usage: pim_pimstat_hello_messages_received" exit 1 fi echo "pim_pimstat_hello_messages_received" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_hello_messages_received" XRL_ARGS="" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_hello_messages_sent() { if [ $# -lt 0 ] ; then echo "Usage: pim_pimstat_hello_messages_sent" exit 1 fi echo "pim_pimstat_hello_messages_sent" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_hello_messages_sent" XRL_ARGS="" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_hello_messages_rx_errors() { if [ $# -lt 0 ] ; then echo "Usage: pim_pimstat_hello_messages_rx_errors" exit 1 fi echo "pim_pimstat_hello_messages_rx_errors" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_hello_messages_rx_errors" XRL_ARGS="" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_register_messages_received() { if [ $# -lt 0 ] ; then echo "Usage: pim_pimstat_register_messages_received" exit 1 fi echo "pim_pimstat_register_messages_received" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_register_messages_received" XRL_ARGS="" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_register_messages_sent() { if [ $# -lt 0 ] ; then echo "Usage: pim_pimstat_register_messages_sent" exit 1 fi echo "pim_pimstat_register_messages_sent" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_register_messages_sent" XRL_ARGS="" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_register_messages_rx_errors() { if [ $# -lt 0 ] ; then echo "Usage: pim_pimstat_register_messages_rx_errors" exit 1 fi echo "pim_pimstat_register_messages_rx_errors" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_register_messages_rx_errors" XRL_ARGS="" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_register_stop_messages_received() { if [ $# -lt 0 ] ; then echo "Usage: pim_pimstat_register_stop_messages_received" exit 1 fi echo "pim_pimstat_register_stop_messages_received" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_register_stop_messages_received" XRL_ARGS="" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_register_stop_messages_sent() { if [ $# -lt 0 ] ; then echo "Usage: pim_pimstat_register_stop_messages_sent" exit 1 fi echo "pim_pimstat_register_stop_messages_sent" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_register_stop_messages_sent" XRL_ARGS="" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_register_stop_messages_rx_errors() { if [ $# -lt 0 ] ; then echo "Usage: pim_pimstat_register_stop_messages_rx_errors" exit 1 fi echo "pim_pimstat_register_stop_messages_rx_errors" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_register_stop_messages_rx_errors" XRL_ARGS="" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_join_prune_messages_received() { if [ $# -lt 0 ] ; then echo "Usage: pim_pimstat_join_prune_messages_received" exit 1 fi echo "pim_pimstat_join_prune_messages_received" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_join_prune_messages_received" XRL_ARGS="" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_join_prune_messages_sent() { if [ $# -lt 0 ] ; then echo "Usage: pim_pimstat_join_prune_messages_sent" exit 1 fi echo "pim_pimstat_join_prune_messages_sent" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_join_prune_messages_sent" XRL_ARGS="" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_join_prune_messages_rx_errors() { if [ $# -lt 0 ] ; then echo "Usage: pim_pimstat_join_prune_messages_rx_errors" exit 1 fi echo "pim_pimstat_join_prune_messages_rx_errors" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_join_prune_messages_rx_errors" XRL_ARGS="" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_bootstrap_messages_received() { if [ $# -lt 0 ] ; then echo "Usage: pim_pimstat_bootstrap_messages_received" exit 1 fi echo "pim_pimstat_bootstrap_messages_received" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_bootstrap_messages_received" XRL_ARGS="" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_bootstrap_messages_sent() { if [ $# -lt 0 ] ; then echo "Usage: pim_pimstat_bootstrap_messages_sent" exit 1 fi echo "pim_pimstat_bootstrap_messages_sent" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_bootstrap_messages_sent" XRL_ARGS="" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_bootstrap_messages_rx_errors() { if [ $# -lt 0 ] ; then echo "Usage: pim_pimstat_bootstrap_messages_rx_errors" exit 1 fi echo "pim_pimstat_bootstrap_messages_rx_errors" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_bootstrap_messages_rx_errors" XRL_ARGS="" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_assert_messages_received() { if [ $# -lt 0 ] ; then echo "Usage: pim_pimstat_assert_messages_received" exit 1 fi echo "pim_pimstat_assert_messages_received" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_assert_messages_received" XRL_ARGS="" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_assert_messages_sent() { if [ $# -lt 0 ] ; then echo "Usage: pim_pimstat_assert_messages_sent" exit 1 fi echo "pim_pimstat_assert_messages_sent" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_assert_messages_sent" XRL_ARGS="" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_assert_messages_rx_errors() { if [ $# -lt 0 ] ; then echo "Usage: pim_pimstat_assert_messages_rx_errors" exit 1 fi echo "pim_pimstat_assert_messages_rx_errors" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_assert_messages_rx_errors" XRL_ARGS="" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_graft_messages_received() { if [ $# -lt 0 ] ; then echo "Usage: pim_pimstat_graft_messages_received" exit 1 fi echo "pim_pimstat_graft_messages_received" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_graft_messages_received" XRL_ARGS="" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_graft_messages_sent() { if [ $# -lt 0 ] ; then echo "Usage: pim_pimstat_graft_messages_sent" exit 1 fi echo "pim_pimstat_graft_messages_sent" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_graft_messages_sent" XRL_ARGS="" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_graft_messages_rx_errors() { if [ $# -lt 0 ] ; then echo "Usage: pim_pimstat_graft_messages_rx_errors" exit 1 fi echo "pim_pimstat_graft_messages_rx_errors" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_graft_messages_rx_errors" XRL_ARGS="" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_graft_ack_messages_received() { if [ $# -lt 0 ] ; then echo "Usage: pim_pimstat_graft_ack_messages_received" exit 1 fi echo "pim_pimstat_graft_ack_messages_received" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_graft_ack_messages_received" XRL_ARGS="" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_graft_ack_messages_sent() { if [ $# -lt 0 ] ; then echo "Usage: pim_pimstat_graft_ack_messages_sent" exit 1 fi echo "pim_pimstat_graft_ack_messages_sent" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_graft_ack_messages_sent" XRL_ARGS="" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_graft_ack_messages_rx_errors() { if [ $# -lt 0 ] ; then echo "Usage: pim_pimstat_graft_ack_messages_rx_errors" exit 1 fi echo "pim_pimstat_graft_ack_messages_rx_errors" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_graft_ack_messages_rx_errors" XRL_ARGS="" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_candidate_rp_messages_received() { if [ $# -lt 0 ] ; then echo "Usage: pim_pimstat_candidate_rp_messages_received" exit 1 fi echo "pim_pimstat_candidate_rp_messages_received" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_candidate_rp_messages_received" XRL_ARGS="" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_candidate_rp_messages_sent() { if [ $# -lt 0 ] ; then echo "Usage: pim_pimstat_candidate_rp_messages_sent" exit 1 fi echo "pim_pimstat_candidate_rp_messages_sent" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_candidate_rp_messages_sent" XRL_ARGS="" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_candidate_rp_messages_rx_errors() { if [ $# -lt 0 ] ; then echo "Usage: pim_pimstat_candidate_rp_messages_rx_errors" exit 1 fi echo "pim_pimstat_candidate_rp_messages_rx_errors" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_candidate_rp_messages_rx_errors" XRL_ARGS="" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_unknown_type_messages() { if [ $# -lt 0 ] ; then echo "Usage: pim_pimstat_unknown_type_messages" exit 1 fi echo "pim_pimstat_unknown_type_messages" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_unknown_type_messages" XRL_ARGS="" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_unknown_version_messages() { if [ $# -lt 0 ] ; then echo "Usage: pim_pimstat_unknown_version_messages" exit 1 fi echo "pim_pimstat_unknown_version_messages" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_unknown_version_messages" XRL_ARGS="" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_neighbor_unknown_messages() { if [ $# -lt 0 ] ; then echo "Usage: pim_pimstat_neighbor_unknown_messages" exit 1 fi echo "pim_pimstat_neighbor_unknown_messages" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_neighbor_unknown_messages" XRL_ARGS="" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_bad_length_messages() { if [ $# -lt 0 ] ; then echo "Usage: pim_pimstat_bad_length_messages" exit 1 fi echo "pim_pimstat_bad_length_messages" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_bad_length_messages" XRL_ARGS="" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_bad_checksum_messages() { if [ $# -lt 0 ] ; then echo "Usage: pim_pimstat_bad_checksum_messages" exit 1 fi echo "pim_pimstat_bad_checksum_messages" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_bad_checksum_messages" XRL_ARGS="" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_bad_receive_interface_messages() { if [ $# -lt 0 ] ; then echo "Usage: pim_pimstat_bad_receive_interface_messages" exit 1 fi echo "pim_pimstat_bad_receive_interface_messages" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_bad_receive_interface_messages" XRL_ARGS="" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_rx_interface_disabled_messages() { if [ $# -lt 0 ] ; then echo "Usage: pim_pimstat_rx_interface_disabled_messages" exit 1 fi echo "pim_pimstat_rx_interface_disabled_messages" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_rx_interface_disabled_messages" XRL_ARGS="" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_rx_register_not_rp() { if [ $# -lt 0 ] ; then echo "Usage: pim_pimstat_rx_register_not_rp" exit 1 fi echo "pim_pimstat_rx_register_not_rp" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_rx_register_not_rp" XRL_ARGS="" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_rp_filtered_source() { if [ $# -lt 0 ] ; then echo "Usage: pim_pimstat_rp_filtered_source" exit 1 fi echo "pim_pimstat_rp_filtered_source" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_rp_filtered_source" XRL_ARGS="" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_unknown_register_stop() { if [ $# -lt 0 ] ; then echo "Usage: pim_pimstat_unknown_register_stop" exit 1 fi echo "pim_pimstat_unknown_register_stop" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_unknown_register_stop" XRL_ARGS="" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_rx_join_prune_no_state() { if [ $# -lt 0 ] ; then echo "Usage: pim_pimstat_rx_join_prune_no_state" exit 1 fi echo "pim_pimstat_rx_join_prune_no_state" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_rx_join_prune_no_state" XRL_ARGS="" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_rx_graft_graft_ack_no_state() { if [ $# -lt 0 ] ; then echo "Usage: pim_pimstat_rx_graft_graft_ack_no_state" exit 1 fi echo "pim_pimstat_rx_graft_graft_ack_no_state" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_rx_graft_graft_ack_no_state" XRL_ARGS="" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_rx_graft_on_upstream_interface() { if [ $# -lt 0 ] ; then echo "Usage: pim_pimstat_rx_graft_on_upstream_interface" exit 1 fi echo "pim_pimstat_rx_graft_on_upstream_interface" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_rx_graft_on_upstream_interface" XRL_ARGS="" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_rx_candidate_rp_not_bsr() { if [ $# -lt 0 ] ; then echo "Usage: pim_pimstat_rx_candidate_rp_not_bsr" exit 1 fi echo "pim_pimstat_rx_candidate_rp_not_bsr" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_rx_candidate_rp_not_bsr" XRL_ARGS="" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_rx_bsr_when_bsr() { if [ $# -lt 0 ] ; then echo "Usage: pim_pimstat_rx_bsr_when_bsr" exit 1 fi echo "pim_pimstat_rx_bsr_when_bsr" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_rx_bsr_when_bsr" XRL_ARGS="" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_rx_bsr_not_rpf_interface() { if [ $# -lt 0 ] ; then echo "Usage: pim_pimstat_rx_bsr_not_rpf_interface" exit 1 fi echo "pim_pimstat_rx_bsr_not_rpf_interface" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_rx_bsr_not_rpf_interface" XRL_ARGS="" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_rx_unknown_hello_option() { if [ $# -lt 0 ] ; then echo "Usage: pim_pimstat_rx_unknown_hello_option" exit 1 fi echo "pim_pimstat_rx_unknown_hello_option" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_rx_unknown_hello_option" XRL_ARGS="" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_rx_data_no_state() { if [ $# -lt 0 ] ; then echo "Usage: pim_pimstat_rx_data_no_state" exit 1 fi echo "pim_pimstat_rx_data_no_state" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_rx_data_no_state" XRL_ARGS="" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_rx_rp_no_state() { if [ $# -lt 0 ] ; then echo "Usage: pim_pimstat_rx_rp_no_state" exit 1 fi echo "pim_pimstat_rx_rp_no_state" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_rx_rp_no_state" XRL_ARGS="" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_rx_aggregate() { if [ $# -lt 0 ] ; then echo "Usage: pim_pimstat_rx_aggregate" exit 1 fi echo "pim_pimstat_rx_aggregate" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_rx_aggregate" XRL_ARGS="" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_rx_malformed_packet() { if [ $# -lt 0 ] ; then echo "Usage: pim_pimstat_rx_malformed_packet" exit 1 fi echo "pim_pimstat_rx_malformed_packet" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_rx_malformed_packet" XRL_ARGS="" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_no_rp() { if [ $# -lt 0 ] ; then echo "Usage: pim_pimstat_no_rp" exit 1 fi echo "pim_pimstat_no_rp" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_no_rp" XRL_ARGS="" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_no_route_upstream() { if [ $# -lt 0 ] ; then echo "Usage: pim_pimstat_no_route_upstream" exit 1 fi echo "pim_pimstat_no_route_upstream" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_no_route_upstream" XRL_ARGS="" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_rp_mismatch() { if [ $# -lt 0 ] ; then echo "Usage: pim_pimstat_rp_mismatch" exit 1 fi echo "pim_pimstat_rp_mismatch" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_rp_mismatch" XRL_ARGS="" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_rpf_neighbor_unknown() { if [ $# -lt 0 ] ; then echo "Usage: pim_pimstat_rpf_neighbor_unknown" exit 1 fi echo "pim_pimstat_rpf_neighbor_unknown" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_rpf_neighbor_unknown" XRL_ARGS="" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_rx_join_rp() { if [ $# -lt 0 ] ; then echo "Usage: pim_pimstat_rx_join_rp" exit 1 fi echo "pim_pimstat_rx_join_rp" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_rx_join_rp" XRL_ARGS="" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_rx_prune_rp() { if [ $# -lt 0 ] ; then echo "Usage: pim_pimstat_rx_prune_rp" exit 1 fi echo "pim_pimstat_rx_prune_rp" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_rx_prune_rp" XRL_ARGS="" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_rx_join_wc() { if [ $# -lt 0 ] ; then echo "Usage: pim_pimstat_rx_join_wc" exit 1 fi echo "pim_pimstat_rx_join_wc" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_rx_join_wc" XRL_ARGS="" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_rx_prune_wc() { if [ $# -lt 0 ] ; then echo "Usage: pim_pimstat_rx_prune_wc" exit 1 fi echo "pim_pimstat_rx_prune_wc" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_rx_prune_wc" XRL_ARGS="" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_rx_join_sg() { if [ $# -lt 0 ] ; then echo "Usage: pim_pimstat_rx_join_sg" exit 1 fi echo "pim_pimstat_rx_join_sg" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_rx_join_sg" XRL_ARGS="" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_rx_prune_sg() { if [ $# -lt 0 ] ; then echo "Usage: pim_pimstat_rx_prune_sg" exit 1 fi echo "pim_pimstat_rx_prune_sg" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_rx_prune_sg" XRL_ARGS="" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_rx_join_sg_rpt() { if [ $# -lt 0 ] ; then echo "Usage: pim_pimstat_rx_join_sg_rpt" exit 1 fi echo "pim_pimstat_rx_join_sg_rpt" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_rx_join_sg_rpt" XRL_ARGS="" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_rx_prune_sg_rpt() { if [ $# -lt 0 ] ; then echo "Usage: pim_pimstat_rx_prune_sg_rpt" exit 1 fi echo "pim_pimstat_rx_prune_sg_rpt" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_rx_prune_sg_rpt" XRL_ARGS="" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_hello_messages_received_per_vif() { if [ $# -lt 1 ] ; then echo "Usage: pim_pimstat_hello_messages_received_per_vif " exit 1 fi vif_name=$1 echo "pim_pimstat_hello_messages_received_per_vif" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_hello_messages_received_per_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_hello_messages_sent_per_vif() { if [ $# -lt 1 ] ; then echo "Usage: pim_pimstat_hello_messages_sent_per_vif " exit 1 fi vif_name=$1 echo "pim_pimstat_hello_messages_sent_per_vif" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_hello_messages_sent_per_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_hello_messages_rx_errors_per_vif() { if [ $# -lt 1 ] ; then echo "Usage: pim_pimstat_hello_messages_rx_errors_per_vif " exit 1 fi vif_name=$1 echo "pim_pimstat_hello_messages_rx_errors_per_vif" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_hello_messages_rx_errors_per_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_register_messages_received_per_vif() { if [ $# -lt 1 ] ; then echo "Usage: pim_pimstat_register_messages_received_per_vif " exit 1 fi vif_name=$1 echo "pim_pimstat_register_messages_received_per_vif" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_register_messages_received_per_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_register_messages_sent_per_vif() { if [ $# -lt 1 ] ; then echo "Usage: pim_pimstat_register_messages_sent_per_vif " exit 1 fi vif_name=$1 echo "pim_pimstat_register_messages_sent_per_vif" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_register_messages_sent_per_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_register_messages_rx_errors_per_vif() { if [ $# -lt 1 ] ; then echo "Usage: pim_pimstat_register_messages_rx_errors_per_vif " exit 1 fi vif_name=$1 echo "pim_pimstat_register_messages_rx_errors_per_vif" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_register_messages_rx_errors_per_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_register_stop_messages_received_per_vif() { if [ $# -lt 1 ] ; then echo "Usage: pim_pimstat_register_stop_messages_received_per_vif " exit 1 fi vif_name=$1 echo "pim_pimstat_register_stop_messages_received_per_vif" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_register_stop_messages_received_per_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_register_stop_messages_sent_per_vif() { if [ $# -lt 1 ] ; then echo "Usage: pim_pimstat_register_stop_messages_sent_per_vif " exit 1 fi vif_name=$1 echo "pim_pimstat_register_stop_messages_sent_per_vif" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_register_stop_messages_sent_per_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_register_stop_messages_rx_errors_per_vif() { if [ $# -lt 1 ] ; then echo "Usage: pim_pimstat_register_stop_messages_rx_errors_per_vif " exit 1 fi vif_name=$1 echo "pim_pimstat_register_stop_messages_rx_errors_per_vif" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_register_stop_messages_rx_errors_per_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_join_prune_messages_received_per_vif() { if [ $# -lt 1 ] ; then echo "Usage: pim_pimstat_join_prune_messages_received_per_vif " exit 1 fi vif_name=$1 echo "pim_pimstat_join_prune_messages_received_per_vif" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_join_prune_messages_received_per_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_join_prune_messages_sent_per_vif() { if [ $# -lt 1 ] ; then echo "Usage: pim_pimstat_join_prune_messages_sent_per_vif " exit 1 fi vif_name=$1 echo "pim_pimstat_join_prune_messages_sent_per_vif" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_join_prune_messages_sent_per_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_join_prune_messages_rx_errors_per_vif() { if [ $# -lt 1 ] ; then echo "Usage: pim_pimstat_join_prune_messages_rx_errors_per_vif " exit 1 fi vif_name=$1 echo "pim_pimstat_join_prune_messages_rx_errors_per_vif" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_join_prune_messages_rx_errors_per_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_bootstrap_messages_received_per_vif() { if [ $# -lt 1 ] ; then echo "Usage: pim_pimstat_bootstrap_messages_received_per_vif " exit 1 fi vif_name=$1 echo "pim_pimstat_bootstrap_messages_received_per_vif" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_bootstrap_messages_received_per_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_bootstrap_messages_sent_per_vif() { if [ $# -lt 1 ] ; then echo "Usage: pim_pimstat_bootstrap_messages_sent_per_vif " exit 1 fi vif_name=$1 echo "pim_pimstat_bootstrap_messages_sent_per_vif" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_bootstrap_messages_sent_per_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_bootstrap_messages_rx_errors_per_vif() { if [ $# -lt 1 ] ; then echo "Usage: pim_pimstat_bootstrap_messages_rx_errors_per_vif " exit 1 fi vif_name=$1 echo "pim_pimstat_bootstrap_messages_rx_errors_per_vif" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_bootstrap_messages_rx_errors_per_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_assert_messages_received_per_vif() { if [ $# -lt 1 ] ; then echo "Usage: pim_pimstat_assert_messages_received_per_vif " exit 1 fi vif_name=$1 echo "pim_pimstat_assert_messages_received_per_vif" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_assert_messages_received_per_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_assert_messages_sent_per_vif() { if [ $# -lt 1 ] ; then echo "Usage: pim_pimstat_assert_messages_sent_per_vif " exit 1 fi vif_name=$1 echo "pim_pimstat_assert_messages_sent_per_vif" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_assert_messages_sent_per_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_assert_messages_rx_errors_per_vif() { if [ $# -lt 1 ] ; then echo "Usage: pim_pimstat_assert_messages_rx_errors_per_vif " exit 1 fi vif_name=$1 echo "pim_pimstat_assert_messages_rx_errors_per_vif" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_assert_messages_rx_errors_per_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_graft_messages_received_per_vif() { if [ $# -lt 1 ] ; then echo "Usage: pim_pimstat_graft_messages_received_per_vif " exit 1 fi vif_name=$1 echo "pim_pimstat_graft_messages_received_per_vif" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_graft_messages_received_per_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_graft_messages_sent_per_vif() { if [ $# -lt 1 ] ; then echo "Usage: pim_pimstat_graft_messages_sent_per_vif " exit 1 fi vif_name=$1 echo "pim_pimstat_graft_messages_sent_per_vif" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_graft_messages_sent_per_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_graft_messages_rx_errors_per_vif() { if [ $# -lt 1 ] ; then echo "Usage: pim_pimstat_graft_messages_rx_errors_per_vif " exit 1 fi vif_name=$1 echo "pim_pimstat_graft_messages_rx_errors_per_vif" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_graft_messages_rx_errors_per_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_graft_ack_messages_received_per_vif() { if [ $# -lt 1 ] ; then echo "Usage: pim_pimstat_graft_ack_messages_received_per_vif " exit 1 fi vif_name=$1 echo "pim_pimstat_graft_ack_messages_received_per_vif" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_graft_ack_messages_received_per_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_graft_ack_messages_sent_per_vif() { if [ $# -lt 1 ] ; then echo "Usage: pim_pimstat_graft_ack_messages_sent_per_vif " exit 1 fi vif_name=$1 echo "pim_pimstat_graft_ack_messages_sent_per_vif" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_graft_ack_messages_sent_per_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_graft_ack_messages_rx_errors_per_vif() { if [ $# -lt 1 ] ; then echo "Usage: pim_pimstat_graft_ack_messages_rx_errors_per_vif " exit 1 fi vif_name=$1 echo "pim_pimstat_graft_ack_messages_rx_errors_per_vif" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_graft_ack_messages_rx_errors_per_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_candidate_rp_messages_received_per_vif() { if [ $# -lt 1 ] ; then echo "Usage: pim_pimstat_candidate_rp_messages_received_per_vif " exit 1 fi vif_name=$1 echo "pim_pimstat_candidate_rp_messages_received_per_vif" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_candidate_rp_messages_received_per_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_candidate_rp_messages_sent_per_vif() { if [ $# -lt 1 ] ; then echo "Usage: pim_pimstat_candidate_rp_messages_sent_per_vif " exit 1 fi vif_name=$1 echo "pim_pimstat_candidate_rp_messages_sent_per_vif" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_candidate_rp_messages_sent_per_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_candidate_rp_messages_rx_errors_per_vif() { if [ $# -lt 1 ] ; then echo "Usage: pim_pimstat_candidate_rp_messages_rx_errors_per_vif " exit 1 fi vif_name=$1 echo "pim_pimstat_candidate_rp_messages_rx_errors_per_vif" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_candidate_rp_messages_rx_errors_per_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_unknown_type_messages_per_vif() { if [ $# -lt 1 ] ; then echo "Usage: pim_pimstat_unknown_type_messages_per_vif " exit 1 fi vif_name=$1 echo "pim_pimstat_unknown_type_messages_per_vif" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_unknown_type_messages_per_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_unknown_version_messages_per_vif() { if [ $# -lt 1 ] ; then echo "Usage: pim_pimstat_unknown_version_messages_per_vif " exit 1 fi vif_name=$1 echo "pim_pimstat_unknown_version_messages_per_vif" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_unknown_version_messages_per_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_neighbor_unknown_messages_per_vif() { if [ $# -lt 1 ] ; then echo "Usage: pim_pimstat_neighbor_unknown_messages_per_vif " exit 1 fi vif_name=$1 echo "pim_pimstat_neighbor_unknown_messages_per_vif" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_neighbor_unknown_messages_per_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_bad_length_messages_per_vif() { if [ $# -lt 1 ] ; then echo "Usage: pim_pimstat_bad_length_messages_per_vif " exit 1 fi vif_name=$1 echo "pim_pimstat_bad_length_messages_per_vif" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_bad_length_messages_per_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_bad_checksum_messages_per_vif() { if [ $# -lt 1 ] ; then echo "Usage: pim_pimstat_bad_checksum_messages_per_vif " exit 1 fi vif_name=$1 echo "pim_pimstat_bad_checksum_messages_per_vif" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_bad_checksum_messages_per_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_bad_receive_interface_messages_per_vif() { if [ $# -lt 1 ] ; then echo "Usage: pim_pimstat_bad_receive_interface_messages_per_vif " exit 1 fi vif_name=$1 echo "pim_pimstat_bad_receive_interface_messages_per_vif" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_bad_receive_interface_messages_per_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_rx_interface_disabled_messages_per_vif() { if [ $# -lt 1 ] ; then echo "Usage: pim_pimstat_rx_interface_disabled_messages_per_vif " exit 1 fi vif_name=$1 echo "pim_pimstat_rx_interface_disabled_messages_per_vif" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_rx_interface_disabled_messages_per_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_rx_register_not_rp_per_vif() { if [ $# -lt 1 ] ; then echo "Usage: pim_pimstat_rx_register_not_rp_per_vif " exit 1 fi vif_name=$1 echo "pim_pimstat_rx_register_not_rp_per_vif" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_rx_register_not_rp_per_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_rp_filtered_source_per_vif() { if [ $# -lt 1 ] ; then echo "Usage: pim_pimstat_rp_filtered_source_per_vif " exit 1 fi vif_name=$1 echo "pim_pimstat_rp_filtered_source_per_vif" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_rp_filtered_source_per_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_unknown_register_stop_per_vif() { if [ $# -lt 1 ] ; then echo "Usage: pim_pimstat_unknown_register_stop_per_vif " exit 1 fi vif_name=$1 echo "pim_pimstat_unknown_register_stop_per_vif" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_unknown_register_stop_per_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_rx_join_prune_no_state_per_vif() { if [ $# -lt 1 ] ; then echo "Usage: pim_pimstat_rx_join_prune_no_state_per_vif " exit 1 fi vif_name=$1 echo "pim_pimstat_rx_join_prune_no_state_per_vif" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_rx_join_prune_no_state_per_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_rx_graft_graft_ack_no_state_per_vif() { if [ $# -lt 1 ] ; then echo "Usage: pim_pimstat_rx_graft_graft_ack_no_state_per_vif " exit 1 fi vif_name=$1 echo "pim_pimstat_rx_graft_graft_ack_no_state_per_vif" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_rx_graft_graft_ack_no_state_per_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_rx_graft_on_upstream_interface_per_vif() { if [ $# -lt 1 ] ; then echo "Usage: pim_pimstat_rx_graft_on_upstream_interface_per_vif " exit 1 fi vif_name=$1 echo "pim_pimstat_rx_graft_on_upstream_interface_per_vif" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_rx_graft_on_upstream_interface_per_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_rx_candidate_rp_not_bsr_per_vif() { if [ $# -lt 1 ] ; then echo "Usage: pim_pimstat_rx_candidate_rp_not_bsr_per_vif " exit 1 fi vif_name=$1 echo "pim_pimstat_rx_candidate_rp_not_bsr_per_vif" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_rx_candidate_rp_not_bsr_per_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_rx_bsr_when_bsr_per_vif() { if [ $# -lt 1 ] ; then echo "Usage: pim_pimstat_rx_bsr_when_bsr_per_vif " exit 1 fi vif_name=$1 echo "pim_pimstat_rx_bsr_when_bsr_per_vif" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_rx_bsr_when_bsr_per_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_rx_bsr_not_rpf_interface_per_vif() { if [ $# -lt 1 ] ; then echo "Usage: pim_pimstat_rx_bsr_not_rpf_interface_per_vif " exit 1 fi vif_name=$1 echo "pim_pimstat_rx_bsr_not_rpf_interface_per_vif" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_rx_bsr_not_rpf_interface_per_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_rx_unknown_hello_option_per_vif() { if [ $# -lt 1 ] ; then echo "Usage: pim_pimstat_rx_unknown_hello_option_per_vif " exit 1 fi vif_name=$1 echo "pim_pimstat_rx_unknown_hello_option_per_vif" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_rx_unknown_hello_option_per_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_rx_data_no_state_per_vif() { if [ $# -lt 1 ] ; then echo "Usage: pim_pimstat_rx_data_no_state_per_vif " exit 1 fi vif_name=$1 echo "pim_pimstat_rx_data_no_state_per_vif" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_rx_data_no_state_per_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_rx_rp_no_state_per_vif() { if [ $# -lt 1 ] ; then echo "Usage: pim_pimstat_rx_rp_no_state_per_vif " exit 1 fi vif_name=$1 echo "pim_pimstat_rx_rp_no_state_per_vif" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_rx_rp_no_state_per_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_rx_aggregate_per_vif() { if [ $# -lt 1 ] ; then echo "Usage: pim_pimstat_rx_aggregate_per_vif " exit 1 fi vif_name=$1 echo "pim_pimstat_rx_aggregate_per_vif" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_rx_aggregate_per_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_rx_malformed_packet_per_vif() { if [ $# -lt 1 ] ; then echo "Usage: pim_pimstat_rx_malformed_packet_per_vif " exit 1 fi vif_name=$1 echo "pim_pimstat_rx_malformed_packet_per_vif" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_rx_malformed_packet_per_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_no_rp_per_vif() { if [ $# -lt 1 ] ; then echo "Usage: pim_pimstat_no_rp_per_vif " exit 1 fi vif_name=$1 echo "pim_pimstat_no_rp_per_vif" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_no_rp_per_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_no_route_upstream_per_vif() { if [ $# -lt 1 ] ; then echo "Usage: pim_pimstat_no_route_upstream_per_vif " exit 1 fi vif_name=$1 echo "pim_pimstat_no_route_upstream_per_vif" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_no_route_upstream_per_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_rp_mismatch_per_vif() { if [ $# -lt 1 ] ; then echo "Usage: pim_pimstat_rp_mismatch_per_vif " exit 1 fi vif_name=$1 echo "pim_pimstat_rp_mismatch_per_vif" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_rp_mismatch_per_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_rpf_neighbor_unknown_per_vif() { if [ $# -lt 1 ] ; then echo "Usage: pim_pimstat_rpf_neighbor_unknown_per_vif " exit 1 fi vif_name=$1 echo "pim_pimstat_rpf_neighbor_unknown_per_vif" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_rpf_neighbor_unknown_per_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_rx_join_rp_per_vif() { if [ $# -lt 1 ] ; then echo "Usage: pim_pimstat_rx_join_rp_per_vif " exit 1 fi vif_name=$1 echo "pim_pimstat_rx_join_rp_per_vif" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_rx_join_rp_per_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_rx_prune_rp_per_vif() { if [ $# -lt 1 ] ; then echo "Usage: pim_pimstat_rx_prune_rp_per_vif " exit 1 fi vif_name=$1 echo "pim_pimstat_rx_prune_rp_per_vif" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_rx_prune_rp_per_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_rx_join_wc_per_vif() { if [ $# -lt 1 ] ; then echo "Usage: pim_pimstat_rx_join_wc_per_vif " exit 1 fi vif_name=$1 echo "pim_pimstat_rx_join_wc_per_vif" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_rx_join_wc_per_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_rx_prune_wc_per_vif() { if [ $# -lt 1 ] ; then echo "Usage: pim_pimstat_rx_prune_wc_per_vif " exit 1 fi vif_name=$1 echo "pim_pimstat_rx_prune_wc_per_vif" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_rx_prune_wc_per_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_rx_join_sg_per_vif() { if [ $# -lt 1 ] ; then echo "Usage: pim_pimstat_rx_join_sg_per_vif " exit 1 fi vif_name=$1 echo "pim_pimstat_rx_join_sg_per_vif" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_rx_join_sg_per_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_rx_prune_sg_per_vif() { if [ $# -lt 1 ] ; then echo "Usage: pim_pimstat_rx_prune_sg_per_vif " exit 1 fi vif_name=$1 echo "pim_pimstat_rx_prune_sg_per_vif" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_rx_prune_sg_per_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_rx_join_sg_rpt_per_vif() { if [ $# -lt 1 ] ; then echo "Usage: pim_pimstat_rx_join_sg_rpt_per_vif " exit 1 fi vif_name=$1 echo "pim_pimstat_rx_join_sg_rpt_per_vif" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_rx_join_sg_rpt_per_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } pim_pimstat_rx_prune_sg_rpt_per_vif() { if [ $# -lt 1 ] ; then echo "Usage: pim_pimstat_rx_prune_sg_rpt_per_vif " exit 1 fi vif_name=$1 echo "pim_pimstat_rx_prune_sg_rpt_per_vif" $* XRL="finder://$PIM_TARGET/pim/0.1/pimstat_rx_prune_sg_rpt_per_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p value:u32 $XRL$XRL_ARGS } xorp/pim/TODO0000664000076400007640000013301311421137511013132 0ustar greearbgreearb# # $XORP: xorp/pim/TODO,v 1.116 2005/06/16 19:40:40 pavlin Exp $ # * PIM-SM SPEC: Fix the following: OLD: inherited_olist(S,G,rpt) is the outgoing interface for packets forwarded on (*,*,RP) or (*,G) state taking into account (S,G,rpt) prune state, and asserts, etc. NEW: "is the outgoing interface" -> "is the outgoing interface list" OLD: A secondary function of the MRIB is to provide routing metrics for destination addresses, these metrics are used when sending and processing Assert messages. NEW: [add ; after "addresses"] * PIM IMPL: If the PIM-SM module doesn't know the RP address, then it shouldn't include the register_vif in the MFC entry's outgoing interface set. * BSR SPEC + IMPL + TESTSUITE: On startup, or after adding Cand-BSR configuration, the Bootstrap Timer should be set to a small random value instead of the fixed value of BS_Timeout (130s). If it is fixed value, we want to reduce the syncronization and BSR trashing after, say, a power failure. Also, the current value of 130s is a bit too long because on startup we won't have BSR info for 130 seconds. * PIM BIDIR SPEC: Apply the following comments based on an email to the PIM ML: From: Teco Boot Date: Wed, 26 Jan 2005 18:04:28 +0100 Subject: [pim] PIM bidir Offer_Period on long delay links The draft-ietf-pim-bidir-07.txt states an Offer_Period of 100ms. Is bidir applicable on long delay links like satcom (RTT ~600ms)? Another remark on the draft text, it doesn't define the tiebreaker on DF election with equal metrics for the offered RPA. The PIM-SM assert (section 4.6.3.) definition could be used in this case also, but I think it's better to update the text in the bidir specification specifying PIM-SM Assert rules in all cases. * PIM-SM SPEC: Section 4.9.5.1. "Group Set Source List Rules" of the PIM-SM spec contains the following text. "o The combination of a (S,G) Prune and a (S,G,rpt) Join could possibly be used by a router to switch from receiving a particular source on the shortest-path tree back to receiving it on the shared tree (provided that the RPF neighbor for the shortest-path and shared trees is common). However Sparse-Mode PIM does not provide a mechanism for switching back to the shared tree." However, in PIM-SM we can switch back to the shared tree once the (S,G) state expires. Hence, the above text needs to clarify that. [ Credit the following email to the PIM ML: Date: Tue, 18 Jan 2005 17:59:53 -0800 From: "Loa, Kanchei" Subject: [pim] Patch the share tree ] * PIM-SM SPEC: Have a look in the following email to the PIM ML. The author has a point (and other people who have replied agree), though this is not critical: Date: Tue, 4 Jan 2005 11:14:14 -0500 From: "Nicholas, Jonathan" Subject: [pim] PIM-SM Nit I have been going over the PIM-SM spec in some detail, and I noticed that in section 4.5.4 no action is taken in the Prune-Pending state when a Prune(S,G,rpt) is recieved. I believe in this case the Expiry Timer should be restarted in behaviour identical to that in the Prune state. * PIM-SM SPEC: Have a look in the following email to the PIM ML. It looks like the observation is correct. However, it appears it doesn't make any difference that we use pim_include(S,G) as is right now even though it takes into account the lost (S,G) asserts. From: Christopher Thomas Brown Date: Wed, 17 Nov 2004 17:19:42 -0500 Subject: [pim] Error in CouldAssert(S,G,I)? In CouldAssert(S,G,I), there is the following: ... + joins(S,G) + pim_include(S,G) ) ) The textual description says this: CouldAssert(S,G,I) is true for downstream interface which would be in the inherited_list(S,G) if (S,G) assert information was not taken into account. However, pim_include(S,G) takes into account (S,G) assert information. With the above definition CouldAssert(S,G,I) will be false on an interface where I have just a local receiver but have lost the assert. It seems like the text description is correct and the macro is incorrect. Should pim_include(S,G) just be replaced with (local_receiver_include(S,G,I) AND I_am_DR(I))? * PIM-BIDIR SPEC: Apply the following comments based on an email to the PIM ML: Date: Thu, 02 Sep 2004 13:51:44 +0200 From: Mark Doll Subject: [pim] BIDIR-PIM: Win State: case "DFT Expires and MC = Election_Robustness" missing I think in draft-ietf-pim-bidir-07.txt section 3.5.3.5 the case "DFT Expires and MC = Election_Robustness" in Win State is missing (actions would be: -> Win; Send Winner). P.S.: Some more syntactic comments: 3.5.3.5: In change history section 6 you claim to follow the format of the new PIM-SM spec. In PIM-SM "-> State" is also used for the cases where the state remains unchanged. IMHO this is clearer than "-". The terms "BOperiod", "Robustness", "Timer" and "OPlow_int" are unspecified although it is clear that "Backoff_Period", "Election_Robustness", "DFT" and "OPlow" are meant. So maybe simply substitute s/OPlow_int/OPlow/ (2x), s/Timer/DFT/ (2x), s/Robustness/Election_Robustness/ and add a note to the state tables that BOperiod means Backoff_Period (since Backoff_Period does not fit directly into the tables). Or alternatively change the value name to BOPeriod (change in section 3.5.2.2 s/Backoff_Period/Backoff Period/ (3x) and in section 3.6: s/Backoff_Period/BOperiod/, s/Period that/Backoff Period that/ (in Explanation))?!. 3.4.1: s/no the DF/not the DF/ 3.5.3.1: s/Path message/Pass message/ * PIM-SM SPEC + IMPL + TESTSUITE: There is an issue with timer value computation for join suppression. From Email exchange on the PIM mailing list: Date: Thu, 12 Aug 2004 14:40:36 +0200 From: Mark Doll Subject: [pim] PIM-SM: timer value computation for join suppression ... From: James Lingard Subject: RE: [pim] PIM-SM: timer value computation for join suppression Date: Thu, 14 Oct 2004 20:26:29 +0100 ... Date: Fri, 15 Oct 2004 11:17:33 +0200 From: Mark Doll Subject: Re: [pim] PIM-SM: timer value computation for join suppression The problem is with Join(*,*,RP)/(*,G)/(S,G) timer value computation when suppressing joins. The spec states: Let t_joinsuppress be the minimum of t_suppressed and the HoldTime from the Join/Prune message triggering this event. If the Join Timer is set to expire in less than t_joinsuppress seconds, reset it so that it expires after t_joinsuppress seconds. If the Join Timer is set to expire in more than t_joinsuppress seconds, leave it unchanged. This may result in Join implosion and/or upstream state loss if the J/P Holdtime of the router that is originating the J/P messages is very short and it suddenly stops sending the J/P messages. The proposal by Mark Doll is to use the following solution: Reset the Join Timer to the minimum of t_suppressed and HoldTime/3.5*rand(1.1,1.4), where HoldTime is the HoldTime from the Join/Prune message triggering this event. If the Join Timer is set to expire in more than t_joinsuppress seconds, leave it unchanged. * PIM-SM SPEC: Change 6 = Graft (used in PIM-DM only) Multicast to ALL-PIM-ROUTERS to 6 = Graft (used in PIM-DM only) Unicast to the the RPF'(S) Noted in the following email to the PIM mailing list: Date: Mon, 19 Jul 2004 22:01:31 -0700 From: PrashantJhingran 70625 Subject: Re: [pim] Changes from -09 to -10 in PIM-SM spec * PIM SPEC + TESTSUITE: According to the spec, if we receive AssertCancel on the RPF interface, then its metric will be compared against my_assert_metric(). However, given that CouldAssert for the RPF interface is false, my_assert_metric() will return infinite_assert_metric(), therefore AssertCancel will fail to perform its duty. This is fixed in the implementation by testing first if the received Assert message contains AssertCancel metric, and if yes, the local router is automatically declared the winner. Note: This fix is based on the following email to the PIM Working Group mailing list: Date: Fri, 9 Jul 2004 11:44:41 -0700 (PDT) From: Venugopal Hemige Subject: [pim] Hello HoldTime and Assert questions http://www1.ietf.org/mail-archive/web/pim/current/msg00206.html * BSR SPEC + IMPL + TESTSUITE: Check that the following comments from Stig to the PIM ML (an email from 5 Jul 2004 Subject: Re: [pim] Doubt reg. BSR) are in the spec, and if yes, then implement them "We plan to add text saying that an empty BSM must not delete the current RP-set. A new BSR would first send an empty BSM, and by the time it sends the next BSM, it has hopefully received C-RP advs from all the C-RPs. Currently the default periods for BSMs and C-RP advs are both 60s. So it might be possible to send two BSMs and just missing an C-RP adv. I see two possible solutions to that. One is making the C-RP adv period shorter. The other would be to have C-RPs send C-RP adv immediately (or with small random delay) when a new BSR is elected. With the changes above the new BSR should have complete RP information by the time it sends the second BSM, and the routers shouldn't lose any RP information during the switchover." * BSR SPEC + IMPL + TESTSUITE: - Why the lastest -05 I-D doesn't describe what to do with the BIDIR flag if the router doesn't implement Bidir-PIM? There was text about this in the -04 I-D. - Implement the BIDIR flag. * BSR IMPL + TESTSUITE: Implement and test the following from the -05 I-D: "If the mask length of the first group range in a scoped IPv6 BSM is less than 16, the message MUST be dropped and a warning SHOULD is logged." * PIM SPEC: In the lastest -11 I-D there are the following two paragraphs (in Section 4.5.6 and 4.5.7 respectively): RPF'(*,G) changes not due to an Assert An event occurred which caused the next hop towards the RP for G to change. This may be caused by a change in the MRIB routing database or the group-to-RP mapping. Note that this transition does not occur if an Assert is active and the upstream interface does not change. ... RPF'(S,G) changes not due to an Assert An event occurred which caused the next hop towards S to change. Note that this transition does not occur if an Assert is active and the upstream interface does not change. What is the formal definition of "Assert is active" in the above context? My interpretation is that it is equivalent to: I_Am_Assert_Loser(*, G, I) (or I_Am_Assert_Loser(S, G, I) respectively) is true for the upstream interface. In any case, the above text may be clearer if it is modified to use a more formal description as it is done everywhere else in the spec. * PIM SPEC + IMPL: In the lastest -11 I-D, on page 71 (the PS version), Figure 11, two of the transitions from the "I am Assert Loser" state are: - "Receive acceptable assert from current winner with RPTbit set [A2]" - "Receive preferred assert with RPTbit [A2]" However, in the corresponding table on page 72, and in the text on page 74, the "with RPTbit set" condition is missing. Indeed, the text on page 74 describes those transitions by saying "We receive a (*,G) assert..." which implies that the RPTbit is set. What the original intention was (I presume the "with RPTbit set" condition is suppose to be there)? Note that currently the implementation assumes that the RPTbit is set, but if this is invalid then the implementation needs to be fixed. * PIM IMPL: Implement the following from the BSR spec: "As an optimation, a router MAY choose not to forward a BSM out of the interface the message was received on if that interface is a point-to-point interface. On interfaces with multiple PIM neighbors, a router MUST forward an accepted BSM onto the interface that BSM was received on, but if the number of PIM neighbors on that interface is large, it MAY delay forwarding a BSM onto that interface by a small randomized interval to prevent message implosion." * PIM IMPL: Implement the security section. E.g., one of the things that need to be implemented is: "An implementation SHOULD provide a mechanism to allow an RP to restrict the range of source addresses from which it accepts Register- encapsulated packets." * PIM IMPL: Implement the following from the spec: If the the Register_Suppression_Time or the Register_Probe_Time are configured to values other than the defaults it MUST be ensured that the value of the Register_Probe_Time is less than half the value of the Register_Suppression_Time to prevent a possible negative value in the setting of the Register-Stop Timer. * PIM IMPL: Implement the following whenever an invalid "no" combination of J/P entries is received: "An error message MAY be logged to the administrator in a rate limited manner." * PIM SPEC: The table with the Join/Prune rules combinations (in the section that describes the J/P messages format) is assymetric after recent changes to the I-D spec: - Row "Join(S,G,rpt)" vs column "Prune(S,G)" was changed from "yes" to "?", but row "Prune(S,G)" vs column "Join(S,G,rpt)" still remains "yes". - Row "Prune(S,G,rpt)" vs column "Prune(S,G)" was changed from "?" to "yes", but row "Prune(S,G)" vs row "Prune(S,G,rpt)" still remains "?". * PIM SPEC: The PIM (S,G) assert state machine has a state transition that happens whenever a Join message is received: "We receive a Join(S,G) that has the Upstream Neighbor Address field set to my primary IP address on interface I." However, the corresponding text in the (*,G) assert state machine says: "We receive a Join(*,G) or a Join(*,*,RP(G)) that has the Upstream Neighbor Address field set to my IP address on interface I." Note that the the (*,G) text does not say "primary". Is this an unintended omission, or in the (*,G) case the (*,G) Join message's event will be applied even if the message uses a secondary IP address for the Upstream Neighbor Address? Similar mismatch exists in "Summary of Assert Rules and Rationale": Rationale 6 says "... a Join(S,G) with an Upstream Neighbor Address that is its primary IP address on that interface...", but Rationale 7 says "... a Join(*,G) or a Join(*,*,RP(G)) with an Upstream Neighbor Address that is one of its addresses on that interface...". * PIM SPEC: - Edit Figure 10 (Per-interface (S,G) Assert State machine) so the text at the bottom of the figure doesn't overlap. - Fix the formatting for the PS table for Figure 10 above. * PIM IMPL+SPEC: The spec says that the Unicast Upstream Neighbor Address in Join/Prune messages is the primary address associated with the interface. What happens if the Unicast Upstream Neighbor Address in the J/P message is one of the secondary addresses? The spec should clarify it and the implementation should follow the clarification. * PIM IMPL + KERNEL IMPL: Check that the following is true for both the user-level and kernel-level implementation: The TTL of a forwarded data packet is decremented before it is encapsulated in the Register Tunnel. The encapsulating packet uses the normal TTL that the router would use for any locally-generated IP packet. The IP ECN bits should be copied from the original packet to the IP header of the encapsulating packet. They SHOULD NOT be set independently by the encapsulating router. The Diffserv Code Point (DSCP) should be copied from the original packet to the IP header of the encapsulating packet. It MAY be set independently by the encapsulating router, based upon static configuration or traffic classification. See [10] for more discussion on setting the DSCP on tunnels. * PIM SPEC: "Interface_Address_List Option" -> "interface Address_List Option" * PIM SPEC: "# See section 4.6 for details." -> "# See Section 4.6 for details." * PIM IMPL: Get rid of PimNode::pim_nbr_find_global(const IPvX& nbr_addr), because the neighbor address may not be unique across all interfaces. To get rid of PimNode::pim_nbr_find_global(), we have to make sure that all valid MRIB entries have a valid next-hop vif index. * PIM SPEC: draft-11, Section 4.1.4: "On an RP, the PMBR value must be cleared when the Keepalive Timer expires." Make sure that the above text is written down in the Keepalive Timer handling. XXX: where is this? * PIM SPEC: draft-11, section 4.1.3 and section 4.1.4: The drafts say that we have to store the Assert Winner State per interface, and that the Assert Winner State includes Assert winner's IP address (AssertWinner) and Assert winner's Assert Metric (AssertWinnerMetric). However, the definition of assert_metric itself includes ip_address, so there is redundancy when specifying that we have to store the AssertWinner as well (because the IP address is already included in the AssertWinnerMetric. * IMPL: Get rid of the gcc-ism declaration of an array with non-const size inside pim_proto_register.cc. Currently, this includes: uint8_t frag_buf[rcvlen]; // The buffer for the fragments uint8_t first_frag[mtu]; * IMPL: Add support for scoped zone configuration (e.g., to the Bootstrap configuration) per interface. * PIM SPEC: Replace (*,*) with (*,*,RP) inside section 11.2 "Sources Internal to the PIM-SM Domain" * PIM MIB SPEC: Look into the following emails from "Nicholas, Jonathan" Date: Mon, 4 Feb 2002 Subject: [pim] PIM MIB Date: Wed, 27 Mar 2002 Subject: [pim] PIM MIB * IMPL: Check again that various addresses are not link-local (e.g., the Cand-BSR address, Cand-RP address, etc). The related methods to consider again are Vif::is_my_addr() vif_find_by_addr() and vif_find_same_subnet_or_p2p() * IMPL: Test all scenarios in Section 4.5.10: Background: (*,*,RP) and (S,G,rpt) interaction. * IMPL: Implement mixing of IPv4/IPv6 addresses within PIM Join/Prune messages as specified in the spec (see below). Currently, if a Join/Prune message mixes the address family of the included addresses, the whole message is ignored. Within one PIM Join/Prune message, all the Multicast Group Addresses, Joined Source addresses and Pruned Source addresses MUST be of the same address family. It is NOT PERMITTED to mix IPv4 and IPv6 addresses within the same message. In addition, the address family of the fields in the message SHOULD be the same as the IP source and destination addresses of the packet. This permits maximum implementation flexibility for dual-stack IPv4/IPv6 routers. If a router receives a message with mixed family addresses, it SHOULD only process the addresses which are of the same family as the unicast upstream neighbor address. * IMPL: If a PIM control message is received witn an unrecognized version or type field, then the error message should be rate-limited. * IMPL: Implement the following from the spec: In IPv6, the DR MUST perform Path MTU discovery, and an ICMP Packet Too Big message MUST be sent by the encapsulating DR if it receives a packet that will not fit in the effective MTU of the tunnel. If the MTU between the DR and the RP results in the effective tunnel MTU being smaller than 1280 (the IPv6 minimum MTU), the DR MUST send Fragmentation Required messages with an MTU value of 1280 and MUST fragment its PIM register messages as required, using an IPv6 fragmentation header between the outer IPv6 header and the PIM Register header. * IMPL: If the IP address of an interface changes, and if the router has not sent yet a Hello message, then if a Join/Prune or Assert message message needs to be send, a Hello message must be sent first (See Section 4.3.1). Note that sending first a Hello message on startup is implemented. Need to verify that the right thing happens if the IP address changes. * IMPL: If a PimVif interface fails to start/stop/etc, then the error message should be meaningful and contain the exact reason for the failure. The same should apply for MLD/IGMP and MFEA. * IMPL+PIM_ARCH_DOCUMENT: - Add new class PimStat or so to keep statstics per vif, per node, etc. - Add this new PimStat class to the design figure, and to the test description. * BSR SPEC + IMPL: If the BSR goes down, and its state expires in the rest of the routers, clarify whether they remove the RP-Set received from that BSR. Maybe they keep it until all Cand-RP timers expire? Or delete all BSR related info on BSR timer expiry? Incorporate the following text from the PIM mailing list: "It was proposed that "When the BSR times out or dies, and no alternative BSR is elected, routers continue to use their cached RP set, even though it has timed out." The next thing that might happen is that a new BSR comes up and participates in the election with an empty BSM. In that case routers continue to use the cached RP set. * IMPL: Implement the PMBR behavior in Section 11 (Appendix A) * IMPL: Add a discard interface that is needed to implement the PMBR behavior. * BSR SPEC+IMPL+TESTSUITE: Make sure that Mark's email to PIM Mailing list "Re: [pim] PIM BSR Draft" on May 8, 2003 is specified in the spec, implemented and tested. Also, read other emails on the subject, and probably incorporate the solutions into the spec. * IMPL+BSR SPEC+TESTSUITE(?): In the bootstrap spec, if we receive and accept an unicast BSM, should we forward it? I guess yes; however, this may create in BSM duplication downstream from a rebooted router. Hence, the spec probably should say something about detecting duplicated messages, and (silently?) ignoring them(?) * IMPL+BSR SPEC+TESTSUITE: In the Bootstrap spec, if a Cand-BSR is in Candidate BSR state: clarify what exactly means "Receive non-preferred BSM from Elected BSR". I.e., non-preferred compared to the previous metrics of the elected BSR, or non-preferred compared to the Cand-BSR itself? * IMPL+BSR SPEC: If Cand-RP received by BSR, when to send the next BSM with the updated RP-Set? Currently, the spec doesn't say anything; the implementation sends the new BSM immediately. However, the problem with that is that whenever there is a new BSR, all Cand-RPs start sending to it the Cand-RP messages as soon as they learn about the new BSR. On the other hand, the BSR then generates new BSM for each new Cand-RP. Result: sudden explosion of Cand-RP and Bootstrap messages. * TESTSUITE: clean-up all test setup procedures: move all common stuff to "Test Setup". * TESTSUITE: Test Section 4.9, the last case of Part J: Prune override behavior in NotPruned(S,G,rpt) state when RPF'(S,G,rpt) -> RPF'(*,G) (in case of (*,*,RP) Joins). Testing this scenario is pending spec-related clarification about changing RPF'(S,G,rpt) with (S,G)Asserts. * BSR SPEC+IMPL: When accepting an unicast Bootstrap message, should accept it only if it comes from a neighbor on the RPF interface. Otherwise, we may receive obsolete info from downstream neighbors (wrt the BSR) which were partitioned because of this router's reboot. * IMPL: Bugs in the implementation of the Bootstrap mechanism: ----X----- | | R1---R2---R3---RP Setup: In R2, the RPF toward the RP is X. However, X is not running PIM-SM. If R2 is restarted, after the restart it receives by unicast Bootstrap message from R3, and R1 (lets assume in this order). Also, if R2 was long enough for the RP-set inside R1 to timeout, but still to keep the BSR info, then the unicast Bootstrap message R2 receives from R1 will have RPcnt=1 FRPcnt=0. PROBLEM: when R2 receives the Bootstrap message from R3, bsr_zone.set_accepted_previous_bsm(true), is not called properly, PROBLEM: R1 sends such message, which appears fragmented simply because one of the RPs has time-out. therefore R2 will accept the unicast Bootstrap message from R1 as well. If the tag of the Bootstrap message from R1 was different from the tag of the Bootstrap message from R3, then the message from R1 will "wipe-out" the previously received Bootstrap info from R3. PROBLEM: when the Bootstrap message from R1 "wipe-out" the previously existing info from R3, this happens even though the Bootstrap message from R1 is incomplete (i.e., it appears fragmented). PROBLEM: the wiping-out doesn't remove the missing RP from the RpTable table; as a result there is an entry in that table, even though there is no entry for it in the PimBsr table, and there is no timer running to time it out. * BSR SPEC+IMPL: DR--------R1----RP | R2 Lets assume R2 is sending (*,G) Join to R1 toward RP. If R1 is restarted, right after the restart R2 will send Hello, and then (*,G) Join. However, if we use the BSR mechanism, the Hello+Bootstrap message by the DR to R1 will be delayed a bit (up to Triggered_Hello_Delay). As a result, the R1 will receive the (*,G) Join from R2, but it will not have RP-Set yet, and therefore the (*,G) Join will be ignored. The fix should be that whenever a neighbor is forced to immediately send a Hello message because of Join or Assert messages it has to send, it must be the one to unicast the Bootstrap message(s) as well right after the Hello (even though it might not be the DR). * IMPL: when a non-DR router is (re)started, right after the DR unicasts to it the Bootstrap message, the non-DR might unicast it back. Fix this so the Bootstrap message is not unicasted back. * TESTSUTE: if the MRIB.next_hop(RP) changes, should we send first the (*,*,RP) Join first, or the (*,*,RP) Prune first? The description in the text is different from the figure and the table. Same fix is needed for (*,G) and (S,G) Note: the fix was applied to the spec and the implementation. Take care of the testsuite. * IMPL+SPEC: Add this text somewhere else: ECN bits setup for PIM Registers. Issue: we don't know whether the encapsulating router (the DR in PIM) supports the full-functionality option as described in RFC 2481. Hence: when encapsulating, copy the ECN bits from the inner header to the outer header. When decapsulating: * If the ECT-bit in the inner header is not set, don't do anything * If the ECT-bit in the inner header is set, then: * If the ECT-bit in the outer header is not set, don't do anything * If the ECT-bit in the outer header is set, then: * The CE bit on the outer header is ORed with the CE bit of the inner header to update the CE bit of the packet NO! See Kame's ip_ecn.c and RFC 3168 * IMPL: Currently, all log traces are controlled by one single variable, but in the future we may want to enable/disable log traces per protocol message type, etc. This is true for MFEA, MLD6IMGP, PIM * TESTSUITE: rerun all tests that use "show pim join" command up to Section 4.3 (included), so the output examples are consistent. * IMPL: OK NOT to remove (S,G) entry with olist=NULL if the KeepaliveTimer(S,G) is running? E.g., the entry will be removed after the Keepalive Timer expires? However check the scenario when there will be data traffic for (S,G) on the IIF because of another router: in that case will the Keepalive Timer expire, so the (S,G) entry can be deleted? * TESTSUITE: in case when a Prune message is received, but the router has no (*,*,RP) or (*,G) or (S,G) or (S,G,rpt) matching entry, a no-info entry might be created. The current implementation creates such entry for (S,G,rpt), which is deleted after the pruned interface timeout. Previously, (*,*,RP), (*,G) or (S,G) entries were created as well, and they were never deleted. Add the appropriate test scenarion in the testsuite to test that. * IMPL: add PimConfig class to PimNode. * IMPL: all signals from the kernel should be parsed by the MFEA before sent to PIM. * IMPL: In all *.hh files, use same distance from return argument of a method and the method name (this applies everywhere not only PIM). * IMPL BUG: if register_vif at the DR is started in PIM after NOCACHE signal is received for a group, then later when we receive the Bootstrap information, the RP for the group we received the NOCACHE signal is not set properly. Similar problem occurs at the RP as well: if the register_vif is started after IGMP Membership Join is received. * SPEC+IMPL+PIMKERN+TESTSUITE: How to set the outer TTL for PIM Register packets: (a) Fixed value (similar to cisco) (b) Set to the inner TTL on encasulation, then copy back to inner TTL on decapsulation (c) Set to inner TTL on encapsulation. On decapsulation, if outer TTL is larger than the inner TTL, then don't copy it (Mark's suggestion) * IMPL: decide what exactly to do when reconfiguring the Cand-BSR, Cand-RP, etc : e.g. stop everything about RPs, merge, etc... * IMPL: Add dont_fragment:bool to the send_protocol_message[4,6] XRLs from protocol modules to the MFEA. If set, the IP_DF bit will be set (applies only for IPv4). * BSR SPEC: If the DR reboots (e.g., its GenID changes), then the neighbor that should unicast to it the Bootstrap message should be the neighbor that _WOULD_ had become the DR if the real DR was not there. * TESTSUITE: Take care of the change in the processing of: "On receipt of data from S to G on interface iif:" * TESTSUITE: Take care ot the change in the processing of: "packet_arrives_on_rp_tunnel" * IMPL+SPEC (Bootstrap): Should the triggered Bootstrap message be sent immediately, or waiting for a while after the first random Hello message is OK? If the latter, then sending immediately Join or Assert messages may not make much sense. Note: currently, in the implementation if a Join/Prune/Assert/Bootstrap needs to be sent to a neighbor, a Hello message is sent first. The sending of the Hello message itself triggers the sending of the unicast Bootstrap messages. * TESTSUITE: Add "LAN Prune Delay" test. (Isn't already tested indirecty?? Search the Latex for "LAN Prune Delay") * SPEC+IMPL: If our Hello holdtime is 0xffff, then do we still send periodically the Hello messages according to that value, or blocking those messages is outside PIM?? * SPEC: if the Hello holdtime or period is changed: // Send immediately a Hello message, and schedule the next one // at random in the interval [0, hello_period) If any other Hello option is changed, send immediately a Hello message, but don't reschedule when to send the next one. * KERNEL+SPEC: Add Mark's suggestion re. PIM version field check: "My guess is that the reason for changing the version field is that the protocol is not backward-compatible. I would expect that any version increase would be signalled in Hello packets, and newer versions requried to fall back to v2, or refuse to communicate with v2, as they choose. But I think a v2-only implementation should drop anything other than a Hello with version greater than 2, and log a warning." * IMPL: the following ConfigParam in PimVif don't have XRL config interface: - join_prune_holdtime (indirectly set from join_prune_period) - assert_time - assert_override_interval * IMPL: parameterize following (per PimNode): PIM_KEEPALIVE_PERIOD_DEFAULT PIM_RP_KEEPALIVE_PERIOD_DEFAULT PIM_REGISTER_SUPPRESSION_TIME_DEFAULT PIM_REGISTER_PROBE_TIME_DEFAULT (and all PIM_CAND_RP_ADV and PIM_BOOTSTRAP related stuff) * IMPL: use PIM_JOIN_PRUNE_OIF_HOLDTIME_FOREVER * IMPL: rename lan_delay to lan_delay_msec rename override_interval to override_interval_msec * IMPL: check that the Join/prune period should not be larger than 64k/3.5, otherwise the holdtime will overflow. * SPEC+IMPL+TESTSUITE: Modify: Bool CouldRegister(S,G) { return ( I_am_DR( RPF_interface(S) ) AND KeepaliveTimer(S,G) is running AND DirectlyConnected(S) == TRUE ) } to Bool CouldRegister(S,G) { return ( I_am_DR( RPF_interface(S) ) AND KeepaliveTimer(S,G) is running AND DirectlyConnected(S) == TRUE AND !I_am_RP(G) AND RP(G) != NULL) } Note: the implementation already takes care of !I_am_RP(G), but it doesn't implement yet the RP(G) != NULL part. [Note: the newest -11 I-D spec uses bool instead of Bool] * IMPL: fix following error due to fact that temp. sometimes we don't install any dataflow monitoring in the MFEA: [ 2002/08/04 16:37:35 ERROR test_pim:15545 PIM +775 xrl_pim_node.cc xrl_result_ delete_dataflow_monitor ] Failure delete dataflow monitor with the MFEA: Cannot delete dataflow monitoring for source 10.4.0.2 and group 224.0.1.20 * IMPL: Juniper configuration re. Cand-RP configures the RP holdtime. However, it makes more sense to configure the C-RP-Adv-Period instead, while the holdtime is 2.5*C-RP-Adv-Period. * IMPL: Add a CLI command to show config information per PimVif (e.g. hello_period, etc.) * IMPL: When setting a new protocol version on an interface, or on the node, do we restart the vif and/or node or do we let it run without interruption? * IMPL: Finish implementing gracefully start for PimVif (e.g., send all pending Join/Prune messages, BSR-related messages, etc) * IMPL: When deleting some BSR configuration, don't send-out Bootstrap or Cand-RP messages with holdtime=0 for all configurations * IMPL: When stopping a PimVif, send-out Bootstrap and Cand-RP messages with holdtime=0 for all configurations that use the address of that PimVif. * IMPL: in all comments should rename "function" to "method". This applies also for CLI, MFEA, MLD6IGMP, LIBPROTO * IMPL and SPEC: When computing JoinDesired (is_join_desired_), should we return false if the iif is DOWN? Maybe the answer is NO, and how we suppress the Join messages if the interface is DOWN is implementation-specific issue only. * IMPL: if we stop PimRegister, should pim_register_vif_index() then return VIF_INDEX_INVALID ?? * IMPL: remove the following input state ?? INPUT_STATE_NBR_MRIB_NEXT_HOP_RP_CHANGED, // 3 INPUT_STATE_NBR_MRIB_NEXT_HOP_RP_G_CHANGED, // 5 INPUT_STATE_NBR_MRIB_NEXT_HOP_S_CHANGED, // 6 (Maybe yes, because it is never used??) * SPEC+IMPL: If an interface goes down: - if IIF, then thread it as the new IIF = INFINITY, and then the protocol machinery will take care of sending Prune, Asserts, etc. - if OIF, then send Hello with Holdtime of 0 (as the protocol spec says), and clean-up internal state (e.g, olist joins, etc). - If the interface has the IP address of the Cand-BSR and/or the Cand-RP (e.g., if we are not using a loopback interface with a global IP address as the Cand-BSR or Cand-RP address). - Send Bootstrap message and/or Cand-RP message with the new IP address - Send Bootstrap message and/or Cand-RP message with the old IP address and Holdtime = 0. NOTE: If the interface address has changed, it may not be possible to send-out a Bootstrap and/or Cand-RP message with the old address, if the OS does not allow us (e.g, if it doesn't allow IP address spoofing). * BSR SPEC: PIM-SM spec Section 4.3.1 says that if no Hello message has been sent yet on an interface, but we must send Join/Prune or Assert message, then a Hello message MUST immediately be sent first without waiting for the Hello timer to expire. Then, what about other LAN-only PIM messages like Bootstrap messages? * IMPL: when we start/stop/enable/disable an unit, is it OK or ERROR to perform the same action twice once after another? This applies to the XRL return result as well. * IMPL: Rename '*delete*' and '*remove*' methods: Use '*remove*' when an element is removed from a container, but the element itself is not deleted/destroyed. Use '*delete*' when an element is both removed from a container and then deleted/destroyed. * IMPL: If I am the RP for PimMre, do we want to explicity test for that and set/return the PimNbr and the Mrib to NULL? E.g., in PimMre::compute_nbr_mrib_next_hop_rp(), PimMre::compute_rpfp_nbr_wc() Similar for S, if a directly-connected S, or I am the S. * IMPL: take care of recomputing PimMfc::olist_disable_wrongvif() * PIMKERN: Add to ip_mroute.h (for FreeBSD, NetBSD, OpenBSD) (for consistency with IPv6 Kame implementation, and with Linux): #define MRT_PIM 107 /* enable PIM processing */ Note that the particular value is same as MRT_ASSERT. * IMPL: Add XRLs for setting each of the config. parameters such as misc. timer values, priorities, etc, so the default values don't have to be always explicitly set by outside. * SPEC: Do we allow SPT switch if only (*,*,RP) entry, but not (*,G) entry? See CheckSwitchToSpt(S,G) Maybe no, because by definition in Section 11 (Appendix A) the border router must generate (*,G) Join for each active group, hence it will contain (*,G) entries. * SPEC+IMPL: When a packet is received in Section 4.2 (Data Packet Forwarding Rules), why in one case the action is "set KeepaliveTimer(S,G) to Keepalive_Period" but in another it is just: "restart KeepaliveTimer(S,G)" Aren't both actions same? * IMPL: find a solution for the MFEA_KERNEL_MESSAGE_* values so inside pim_node.cc we don't need to include "fea/mfea_kernel_messages.hh" * IMPL: fix the following error: [ 2002/06/25 11:51:03 WARNING test_pim PIM ] Cannot timeout BSR zone 224.0.0.0/4 (non-scoped): no such configured zone * IMPL: All error messages when responding to an XRL must be returned back as a reason for failure. * IMPL: Use the following default values as appropriate: PIM_CAND_RP_ADV_RP_PRIORITY_DEFAULT, PIM_CAND_RP_ADV_RP_HOLDTIME_DEFAULT, (XXX: currently, the priority and the holdtime are explicitly set when the Cand-RP is configured, hence no need for this? Or maybe we should allow configuration XRLs that don't setup the priority and the holdtime?) * SPEC+IMPL: explicity specify not to route on link-local multicast groups and/or add a pointer to the document that says that. * IMPL: figure-out what to do for (S,G) MLD/IGMP Leave: remove from local_receiver_include or add to local_receiver_exclude or something else? * IMPL: Unite INPUT_STATE_MRIB_RP_CHANGED and INPUT_STATE_MRIB_S_CHANGED ?? (XXX: not need for this??) * IMPL: when updating an (S,G,rpt) entry with a new RP or MRIB to the RP, (re)use the (*,G) information instead of computing the new info for each (S,G,rpt) entry for that (*,G) entry. * IMPL: get rid of the INPUT_STATE_RPFP_*_CHANGED input changes. (XXX: not need for this??) * IMPL: Check whether for all PimMreTask operations that require vif_index, we can compute the required argument(s) in advance. * IMPL: when removing a PimNbr, make sure it is not in use anymore by any PimMre or other entry. * SPEC+IMPL: When the RP changes, then it could be that both events occur for the upstream (*,G) state machine: "JoinDesired(*,G) -> False" and "RPF'(*,G) changes not due to an Assert" In that case, does it matter in what order the actions are performed? * IMPL: Unite in PimMreTrackState NBR_MRIB_NEXT_HOP_RP with NBR_MRIB_NEXT_HOP_RP_G (XXX: not need for this??) * IMPL: Check email message sent to snap-users@kame.net about IPv6 related bug in pim6sd from Alexandre Fenyo : "(KAME-snap 6432) Bug in the bootstrap protocol..." and apply the fix to the IPv6 code. Also, check earlier email from the kame list from pekkas AT netcore.fi Wed May 29 21:02:59 JST 2002 suz AT crl.hitachi.co.jp * kame/kame/pim6sd/config.c,vif.[ch],routesock.c: fixed RPF algorithm bugs for P2P connected routes and static interface routes (reported from pekkas AT netcore.fi) * SPEC: what about sending Assert based on an (S,G,rpt) or (S,G) that is pending SPT switch? * IMPL: Do we want to create methods that take care of (S,G,rpt) entries when both the MRIB(S) and MRIB(RP) change at the same time? * IMPL: Make the set of neighbors (per PimVif) a instead of . Needed if the number of neighbors become quite large, so the neighbor lookup will become much faster. * SPEC+IMPL (OLD?) [this entry may be obsoleted by the implementation of the new "Address List" Hello option]. SPEC: The unicast routing may show one MRIB address, but the upstream router may send the Hello messages using only one of the local IP addresses. Hence, which IP address to use in the Hello messages? IMPL: when receiving a PIM message, accept it not only if it is destined to the primary IP address of that interface, but to any address. * IMPL: If the primary IP address is changed (i.e., deleted and then immediately set to a new address), make sure that in the process of deleting and adding the address we don't end-up with a vif that is shutdown. In other words, we need a new method that just updates the address rather than deleting it and then adding it. Deleting the primary address address will shutdown the vif hence we have to be careful. * IMPL: Check if multicast add_vif on Linux and Solaris allow VIF_REGISTER to have any address (e.g., 127.0.0.2). If allowed, then modify the MFEA to assign that address itself, and remove it from the XRL interface. * IMPL: When receiving PIM Bootstrap message and check them whether they can be added, if all the info was received already, then just silently drop that message instead of printing a warning. * IMPL: Check whether KAME's pim6sd RP hash computation that uses CRC in a complicated way is same result as the XOR that is specified in the spec. * SPEC+IMPL: On startup, am I the DR by default, or wait some period of time before self-promote as the DR? * IMPL+BSR SPEC: Can an intermediate router that forwards a Bootstrap message fragments it for whatever reason? * IMPL+PIM SPEC + BSR SPEC: When we are dealing with scoped group ranges, should the spec say that those group ranges must come from scoped addresses: e.g., 239.255.0.0/16 (the IPv4 Local Scope), or anything that is not the global scope in case of IPv6. * IMPL: Implement RFC 1256 (Router Solicitation) * IMPL+BSRSPEC: When the BSR collects all cand. RPs for a prefix, if their number is larger than 255, distribute only the best 255 RPs. * IMPL: take care of the SZ Timer in the BSR spec: unclear when set, what means "always running", etc. See page 8 and 9 of the spec. (??) * IMPL+SPEC: PIM MIB: I just checked RFC 2934, and indeed it seems that PimRPSetEntry does not include the RP priority, which IMHO should be there. I don't know whether the authors have intentionally left it out for some reasons, or it was an unintentional miss. If it was the latter, probably it should be added when it comes for a revision of this experimental RFC. * IMPL+BSR SPEC: What if the "Hash Mask len" is different for different prefixes in the Bootstrap message? Any issues if different?? * IMPL+PIMKERN > One limitation with using this option is, it won't allow fragmentation > of unicast packets also (man page says this). This we found , during RP > sending a huge CRP-ADV packets to BSR. i.e CRP configured with huge > RP-SET . A question (I am not sure about the answer without checking it). If we use IP_HDR_INCLUDE, and the packet size is more than the MTU, the kernel won't fragment it using IP-fragmentation? If this is true, in that case it would be better if we don't use IP_HDR_INCLUDE, but let the kernel prepare the IP header. I will put this on my TODO list for both the newer PIM-SM implementation, and for the kernel patches as well. * IMPL: When "wrong iif" signal arrives, if it is not on one of our oifs, and if we don't care about it, disable the "wrong iif" signal from the kernel for that (S,G) state (assuming the kernel supports that API). * IMPL: All constants defined in pim_proto.h should be used as configurable variables inside the code. * SPEC+IMPL: do we need to override a Prune even if this was a Prune from a router that has not sent Hello yet? * IMPL: limit: - the number of groups created by a given source S - rate-limit incoming joins and leaves - rate-limit RP register messages at the RP ====================OLD pimdmd TODO BEGIN================ * PIM-DM SPEC: is it safe enough to assume that every Graft-ACK is always exactly same as the Graft it acknowledges. I.e., is it allowed to Graft-ACK the (S,G) entries one-by-one. * PIM-DM SPEC: If an Assert is received on a non-oif (i.e. in prune mode), do we really want to just ignore it? After all, this interface may soon become an oif, and eventually itself will trigger Assert. If we don't ignore the Assert received on a non-oif, then this interface will not unnecessarily become an oif. * PIM-DM SPEC: If winning Assert received on oif, the asserted oif is pruned, but the spec later says that a pruned oif timer is set to the received PIM_PRUNE holdtime. Obviously, there is a PIM_PRUNE message when we are prunning oif because of an Assert. The asserted/prune oif timer is probably set to PIMDM_JOIN_PRUNE_HOLDTIME_DEFAULT which is eventually set to PIM_DATA_TIMEOUT_DEFAULT. The spec is not clear here. * PIM-DM SPEC: Asserts rate-limit of (approx) 1 Assert/s on same oif has to be per routing entry, i.e., (S,G) or (*,G). * PIM-DM SPEC: if a pimdm_mrt entry expires, and it was an assert winner, send Assert with max. priority, to 'cancel' the previous winner. * PIM-DM SPEC: The PIM message Prune holdtime should be longer than PIM_JOIN_PRUNE_OIF_PRUNE_SCHEDULE, otherwise ignore the Prune(?). * PIM-DM SPEC: What to do if a PIM-DM Graft message contains Prune addresses? ====================OLD pimdmd TODO END================ xorp/pim/pim_vif.hh0000664000076400007640000007116311540225531014425 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/pim/pim_vif.hh,v 1.48 2008/10/02 21:57:55 bms Exp $ #ifndef __PIM_PIM_VIF_HH__ #define __PIM_PIM_VIF_HH__ // // PIM virtual interface definition. // #include "libxorp/config_param.hh" #include "libxorp/vif.hh" #include "libxorp/timer.hh" #include "libproto/proto_unit.hh" #include "mrt/buffer.h" #include "mrt/mifset.hh" #include "mrt/multicast_defs.h" #include "pim_nbr.hh" #include "pim_proto_join_prune_message.hh" // // Constants definitions // // // Structures/classes, typedefs and macros // class AssertMetric; class BsrZone; class PimJpGroup; class PimJpHeader; class PimNbr; class PimNode; /** * @short A class for PIM-specific virtual interface. */ class PimVif : public ProtoUnit, public Vif { public: /** * Constructor for a given PIM node and a generic virtual interface. * * @param pim_node the @ref PimNode this interface belongs to. * @param vif the generic Vif interface that contains various information. */ PimVif(PimNode* pim_node, const Vif& vif); /** * Destructor */ virtual ~PimVif(); /** * Set configuration to default values. */ void set_default_config(); /** * Set the current protocol version. * * The protocol version must be in the interval * [PIM_VERSION_MIN, PIM_VERSION_MAX]. * * @param proto_version the protocol version to set. * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_proto_version(int proto_version); /** * Start PIM on a single virtual interface. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int start(string& error_msg); /** Attempt deferred start. */ void notifyUpdated(); /** * Gracefully stop PIM on a single virtual interface. * *The graceful stop will attempt to send Join/Prune, Assert, etc. * messages for all multicast routing entries to gracefully clean-up * state with neighbors. * After the multicast routing entries cleanup is completed, * PimVif::final_stop() is called to complete the job. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int stop(string& error_msg); /** * Completely stop PIM on a single virtual interface. * * This method should be called after @ref PimVif::stop() to complete * the job. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int final_stop(string& error_msg); /** * Enable PIM on a single virtual interface. * * If an unit is not enabled, it cannot be start, or pending-start. */ void enable(); /** * Disable PIM 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 disable(); /** * Receive a protocol message. * * @param src the source address of the message. * @param dst the destination address of the message. * @param buffer the data buffer with the received message. * @return XORP_OK on success, otherwise XORP_ERROR. */ int pim_recv(const IPvX& src, const IPvX& dst, buffer_t *buffer); /** * Get the string with the flags about the vif status. * * TODO: temporary here. Should go to the Vif class after the Vif * class starts using the Proto class. * * @return the C++ style string with the flags about the vif status * (e.g., UP/DOWN/DISABLED, etc). */ string flags_string() const; /** * Get the PIM node (@ref PimNode). * * @return a reference to the PIM node (@ref PimNode). */ PimNode* pim_node() const { return _pim_node; } /** * Get the PIM Multicast Routing Table (@ref PimMrt). * * @return a reference to the PIM Multicast Routing Table (@ref PimMrt). */ PimMrt& pim_mrt() const; /** * Get the PIM neighbor information (@ref PimNbr) about myself. * * @return a reference to the PIM neighbor information (@ref PimNbr) * about myself. */ PimNbr& pim_nbr_me() { return (_pim_nbr_me); } /** * Start the PIM Hello operation. */ void pim_hello_start(); /** * Stop the PIM Hello operation. */ void pim_hello_stop(); /** * Elect a Designated Router on this interface. */ void pim_dr_elect(); /** * Compute if I may become the Designated Router on this interface * if one of the PIM neighbor addresses is not considered. * * Compute if I may become the DR on this interface if @ref exclude_addr * is excluded. * * @param exclude_addr the address to exclude in the computation. * @return true if I may become the DR on this interface, otherwise * false. */ bool i_may_become_dr(const IPvX& exclude_addr); /** * Get my primary address on this interface. * * @return my primary address on this interface. */ const IPvX& primary_addr() const { return (_pim_nbr_me.primary_addr()); } /** * Get my domain-wide reachable address on this interface. * * @return my domain-wide reachable address on this interface. */ const IPvX& domain_wide_addr() const { return (_domain_wide_addr); } /** * Set my domain-wide reachable address on this interface. * * @param v the value of the domain-wide reachable address. */ void set_domain_wide_addr(const IPvX& v) { _domain_wide_addr = v; } /** * Update the primary and the domain-wide reachable addresses. * * The primary address should be a link-local unicast address, and * is used for transmitting the multicast control packets on the LAN. * The domain-wide reachable address is the address that should be * reachable by all PIM-SM routers in the domain * (e.g., the Cand-BSR, or the Cand-RP address). * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int update_primary_and_domain_wide_address(string& error_msg); /** * Get the address of the Designated Router on this interface. * * @return the address of the Designated Router on this interface. */ const IPvX& dr_addr() const { return (_dr_addr); } // // Hello-related configuration parameters // ConfigParam& hello_triggered_delay() { return (_hello_triggered_delay); } ConfigParam& hello_period() { return (_hello_period); } ConfigParam& hello_holdtime() { return (_hello_holdtime); } ConfigParam& dr_priority() { return (_dr_priority); } ConfigParam& propagation_delay() { return (_propagation_delay); } ConfigParam& override_interval() { return (_override_interval); } ConfigParam& is_tracking_support_disabled() { return (_is_tracking_support_disabled); } ConfigParam& accept_nohello_neighbors() { return (_accept_nohello_neighbors); } // // Hello-related non-configurable parameters // ConfigParam& genid() { return (_genid); } // // Join/Prune-related configuration parameters // ConfigParam& join_prune_period() { return (_join_prune_period); } ConfigParam& join_prune_holdtime() { return (_join_prune_holdtime); } // // Assert-related configuration parameters // ConfigParam& assert_time() { return (_assert_time); } ConfigParam& assert_override_interval() { return (_assert_override_interval); } // // Functions for sending protocol messages. // int pim_send(const IPvX& src, const IPvX& dst, uint8_t message_type, buffer_t *buffer, string& error_msg); int pim_hello_send(string& error_msg); int pim_hello_first_send(); int pim_join_prune_send(PimNbr *pim_nbr, PimJpHeader *jp_header, string& error_msg); int pim_assert_mre_send(PimMre *pim_mre, const IPvX& assert_source_addr, string& error_msg); int pim_assert_cancel_send(PimMre *pim_mre, string& error_msg); int pim_assert_send(const IPvX& assert_source_addr, const IPvX& assert_group_addr, bool rpt_bit, uint32_t metric_preference, uint32_t metric, string& error_msg); int pim_register_send(const IPvX& rp_addr, const IPvX& source_addr, const IPvX& group_addr, const uint8_t *rcvbuf, size_t rcvlen, string& error_msg); int pim_register_null_send(const IPvX& rp_addr, const IPvX& source_addr, const IPvX& group_addr, string& error_msg); int pim_register_stop_send(const IPvX& dr_addr, const IPvX& source_addr, const IPvX& group_addr, string& error_msg); int pim_bootstrap_send(const IPvX& dst_addr, const BsrZone& bsr_zone, string& error_msg); buffer_t *pim_bootstrap_send_prepare(const IPvX& src_addr, const IPvX& dst_addr, const BsrZone& bsr_zone, bool is_first_fragment); int pim_cand_rp_adv_send(const IPvX& bsr_addr, const BsrZone& bsr_zone); void hello_timer_start(uint32_t sec, uint32_t usec); void hello_timer_start_random(uint32_t sec, uint32_t usec); bool is_lan_delay_enabled() const; // Link-related time intervals const TimeVal& effective_propagation_delay() const; const TimeVal& effective_override_interval() const; bool is_lan_suppression_state_enabled() const; const TimeVal& upstream_join_timer_t_suppressed() const; const TimeVal& upstream_join_timer_t_override() const; // Misc. functions const TimeVal& jp_override_interval() const; list& pim_nbrs() { return (_pim_nbrs); } size_t pim_nbrs_number() const { return (_pim_nbrs.size()); } bool i_am_dr() const; void set_i_am_dr(bool v); PimNbr *pim_nbr_find(const IPvX& nbr_addr); void add_pim_nbr(PimNbr *pim_nbr); int delete_pim_nbr(PimNbr *pim_nbr); void delete_pim_nbr_from_nbr_list(PimNbr *pim_nbr); const list& alternative_subnet_list() const { return _alternative_subnet_list; } void add_alternative_subnet(const IPvXNet& subnet); void delete_alternative_subnet(const IPvXNet& subnet); void remove_all_alternative_subnets(); // Usage-related functions size_t usage_by_pim_mre_task() const { return (_usage_by_pim_mre_task); } void incr_usage_by_pim_mre_task(); void decr_usage_by_pim_mre_task(); /** * Calculate the checksum of an IPv6 "pseudo-header" as described * in RFC 2460. * * @param src the source address of the pseudo-header. * @param dst the destination address of the pseudo-header. * @param len the upper-layer packet length of the pseudo-header * (in host-order). * @param protocol the upper-layer protocol number. * @return the checksum of the IPv6 "pseudo-header". */ uint16_t calculate_ipv6_pseudo_header_checksum(const IPvX& src, const IPvX& dst, size_t len, uint8_t protocol); buffer_t *buffer_send_prepare(); buffer_t *buffer_send_prepare(buffer_t *buffer); // // Statistics-related counters and values // void clear_pim_statistics(); // uint32_t pimstat_hello_messages_received() const { return _pimstat_hello_messages_received.get(); } uint32_t pimstat_hello_messages_sent() const { return _pimstat_hello_messages_sent.get(); } uint32_t pimstat_hello_messages_rx_errors() const { return _pimstat_hello_messages_rx_errors.get(); } uint32_t pimstat_register_messages_received() const { return _pimstat_register_messages_received.get(); } uint32_t pimstat_register_messages_sent() const { return _pimstat_register_messages_sent.get(); } uint32_t pimstat_register_messages_rx_errors() const { return _pimstat_register_messages_rx_errors.get(); } uint32_t pimstat_register_stop_messages_received() const { return _pimstat_register_stop_messages_received.get(); } uint32_t pimstat_register_stop_messages_sent() const { return _pimstat_register_stop_messages_sent.get(); } uint32_t pimstat_register_stop_messages_rx_errors() const { return _pimstat_register_stop_messages_rx_errors.get(); } uint32_t pimstat_join_prune_messages_received() const { return _pimstat_join_prune_messages_received.get(); } uint32_t pimstat_join_prune_messages_sent() const { return _pimstat_join_prune_messages_sent.get(); } uint32_t pimstat_join_prune_messages_rx_errors() const { return _pimstat_join_prune_messages_rx_errors.get(); } uint32_t pimstat_bootstrap_messages_received() const { return _pimstat_bootstrap_messages_received.get(); } uint32_t pimstat_bootstrap_messages_sent() const { return _pimstat_bootstrap_messages_sent.get(); } uint32_t pimstat_bootstrap_messages_rx_errors() const { return _pimstat_bootstrap_messages_rx_errors.get(); } uint32_t pimstat_assert_messages_received() const { return _pimstat_assert_messages_received.get(); } uint32_t pimstat_assert_messages_sent() const { return _pimstat_assert_messages_sent.get(); } uint32_t pimstat_assert_messages_rx_errors() const { return _pimstat_assert_messages_rx_errors.get(); } uint32_t pimstat_graft_messages_received() const { return _pimstat_graft_messages_received.get(); } uint32_t pimstat_graft_messages_sent() const { return _pimstat_graft_messages_sent.get(); } uint32_t pimstat_graft_messages_rx_errors() const { return _pimstat_graft_messages_rx_errors.get(); } uint32_t pimstat_graft_ack_messages_received() const { return _pimstat_graft_ack_messages_received.get(); } uint32_t pimstat_graft_ack_messages_sent() const { return _pimstat_graft_ack_messages_sent.get(); } uint32_t pimstat_graft_ack_messages_rx_errors() const { return _pimstat_graft_ack_messages_rx_errors.get(); } uint32_t pimstat_candidate_rp_messages_received() const { return _pimstat_candidate_rp_messages_received.get(); } uint32_t pimstat_candidate_rp_messages_sent() const { return _pimstat_candidate_rp_messages_sent.get(); } uint32_t pimstat_candidate_rp_messages_rx_errors() const { return _pimstat_candidate_rp_messages_rx_errors.get(); } // uint32_t pimstat_unknown_type_messages() const { return _pimstat_unknown_type_messages.get(); } uint32_t pimstat_unknown_version_messages() const { return _pimstat_unknown_version_messages.get(); } uint32_t pimstat_neighbor_unknown_messages() const { return _pimstat_neighbor_unknown_messages.get(); } uint32_t pimstat_bad_length_messages() const { return _pimstat_bad_length_messages.get(); } uint32_t pimstat_bad_checksum_messages() const { return _pimstat_bad_checksum_messages.get(); } uint32_t pimstat_bad_receive_interface_messages() const { return _pimstat_bad_receive_interface_messages.get(); } uint32_t pimstat_rx_interface_disabled_messages() const { return _pimstat_rx_interface_disabled_messages.get(); } uint32_t pimstat_rx_register_not_rp() const { return _pimstat_rx_register_not_rp.get(); } uint32_t pimstat_rp_filtered_source() const { return _pimstat_rp_filtered_source.get(); } uint32_t pimstat_unknown_register_stop() const { return _pimstat_unknown_register_stop.get(); } uint32_t pimstat_rx_join_prune_no_state() const { return _pimstat_rx_join_prune_no_state.get(); } uint32_t pimstat_rx_graft_graft_ack_no_state() const { return _pimstat_rx_graft_graft_ack_no_state.get(); } uint32_t pimstat_rx_graft_on_upstream_interface() const { return _pimstat_rx_graft_on_upstream_interface.get(); } uint32_t pimstat_rx_candidate_rp_not_bsr() const { return _pimstat_rx_candidate_rp_not_bsr.get(); } uint32_t pimstat_rx_bsr_when_bsr() const { return _pimstat_rx_bsr_when_bsr.get(); } uint32_t pimstat_rx_bsr_not_rpf_interface() const { return _pimstat_rx_bsr_not_rpf_interface.get(); } uint32_t pimstat_rx_unknown_hello_option() const { return _pimstat_rx_unknown_hello_option.get(); } uint32_t pimstat_rx_data_no_state() const { return _pimstat_rx_data_no_state.get(); } uint32_t pimstat_rx_rp_no_state() const { return _pimstat_rx_rp_no_state.get(); } uint32_t pimstat_rx_aggregate() const { return _pimstat_rx_aggregate.get(); } uint32_t pimstat_rx_malformed_packet() const { return _pimstat_rx_malformed_packet.get(); } uint32_t pimstat_no_rp() const { return _pimstat_no_rp.get(); } uint32_t pimstat_no_route_upstream() const { return _pimstat_no_route_upstream.get(); } uint32_t pimstat_rp_mismatch() const { return _pimstat_rp_mismatch.get(); } uint32_t pimstat_rpf_neighbor_unknown() const { return _pimstat_rpf_neighbor_unknown.get(); } // uint32_t pimstat_rx_join_rp() const { return _pimstat_rx_join_rp.get(); } uint32_t pimstat_rx_prune_rp() const { return _pimstat_rx_prune_rp.get(); } uint32_t pimstat_rx_join_wc() const { return _pimstat_rx_join_wc.get(); } uint32_t pimstat_rx_prune_wc() const { return _pimstat_rx_prune_wc.get(); } uint32_t pimstat_rx_join_sg() const { return _pimstat_rx_join_sg.get(); } uint32_t pimstat_rx_prune_sg() const { return _pimstat_rx_prune_sg.get(); } uint32_t pimstat_rx_join_sg_rpt() const { return _pimstat_rx_join_sg_rpt.get(); } uint32_t pimstat_rx_prune_sg_rpt() const { return _pimstat_rx_prune_sg_rpt.get(); } private: // Private functions void hello_timer_timeout(); void hello_once_timer_timeout(); // // Callbacks for configuration and non-configurable parameters // void set_hello_period_callback(uint16_t v) { uint16_t old_hello_holdtime_divided = (uint16_t) (_hello_holdtime.get() / PIM_HELLO_HELLO_HOLDTIME_PERIOD_RATIO); if (v != old_hello_holdtime_divided) _hello_holdtime.set( (uint16_t)(v * PIM_HELLO_HELLO_HOLDTIME_PERIOD_RATIO)); _pim_nbr_me.set_hello_holdtime(_hello_holdtime.get()); } void set_hello_holdtime_callback(uint16_t v) { uint16_t new_hello_period = (uint16_t)(v / PIM_HELLO_HELLO_HOLDTIME_PERIOD_RATIO); if (_hello_period.get() != new_hello_period) _hello_period.set(new_hello_period); _pim_nbr_me.set_hello_holdtime(_hello_holdtime.get()); } void set_dr_priority_callback(uint32_t v) { _pim_nbr_me.set_dr_priority(v); _pim_nbr_me.set_is_dr_priority_present(true); } void set_propagation_delay_callback(uint16_t v) { _pim_nbr_me.set_propagation_delay(v); _pim_nbr_me.set_is_lan_prune_delay_present(true); } void set_override_interval_callback(uint16_t v) { _pim_nbr_me.set_override_interval(v); _pim_nbr_me.set_is_lan_prune_delay_present(true); } void set_is_tracking_support_disabled_callback(bool v) { _pim_nbr_me.set_is_tracking_support_disabled(v); } void set_genid_callback(uint32_t v) { _pim_nbr_me.set_genid(v); _pim_nbr_me.set_is_genid_present(true); } void set_join_prune_period_callback(uint16_t v) { _join_prune_holdtime.set( (uint16_t)(v * PIM_JOIN_PRUNE_HOLDTIME_PERIOD_RATIO)); } int jp_entry_add(const IPvX& source_addr, const IPvX& group_addr, mrt_entry_type_t mrt_entry_type, action_jp_t action_jp, uint16_t holdtime); int jp_entry_flush(); bool is_send_unicast_bootstrap() const { return (! _send_unicast_bootstrap_nbr_list.empty()); } void add_send_unicast_bootstrap_nbr(const IPvX& nbr_addr) { _send_unicast_bootstrap_nbr_list.push_back(nbr_addr); } const list& send_unicast_bootstrap_nbr_list() const { return (_send_unicast_bootstrap_nbr_list); } void delete_send_unicast_bootstrap_nbr_list() { _send_unicast_bootstrap_nbr_list.clear(); } bool should_send_pim_hello() const { return (_should_send_pim_hello); } void set_should_send_pim_hello(bool v) { _should_send_pim_hello = v; } // Private state PimNode* _pim_node; // The PIM node I belong to buffer_t *_buffer_send; // Buffer for sending messages buffer_t *_buffer_send_hello; // Buffer for sending Hello messages buffer_t *_buffer_send_bootstrap;// Buffer for sending Bootstrap msgs enum { PIM_VIF_DR = 1 << 0 // I am the Designated Router }; uint32_t _proto_flags; // Various flags (PIM_VIF_*) IPvX _dr_addr; // IP address of the current DR XorpTimer _hello_timer; // Timer to send a HELLO message XorpTimer _hello_once_timer; // Timer to send once a HELLO message list _pim_nbrs; // List of all PIM neighbors PimNbr _pim_nbr_me; // Myself (for misc. purpose) IPvX _domain_wide_addr; // The domain-wide reachable address on // this vif list _send_unicast_bootstrap_nbr_list; // List of new nbrs to // unicast to them the // Bootstrap message. // The alternative subnets on a vif. Used to make incoming traffic with a // non-local source address to appear as it is coming from a local subnet. list _alternative_subnet_list; // // Misc configuration parameters // // // Hello-related configuration parameters // ConfigParam _hello_triggered_delay; // The Triggered_Hello_Delay ConfigParam _hello_period; // The Hello_Period ConfigParam _hello_holdtime; // The Hello_Holdtime ConfigParam _dr_priority; // The DR Priority ConfigParam _propagation_delay; // The Propagation_Delay ConfigParam _override_interval; // The Override_Interval ConfigParam _is_tracking_support_disabled; // The T-bit ConfigParam _accept_nohello_neighbors; // If true, accept // neighbors that didn't send // a Hello message first // // Hello-related non-configurable parameters // ConfigParam _genid; // The Generation ID // // Join/Prune-related configuration parameters // ConfigParam _join_prune_period; // The period between J/P msgs ConfigParam _join_prune_holdtime; // The holdtime in J/P msgs // // Assert-related configuration parameters // ConfigParam _assert_time; // The Assert_Time ConfigParam _assert_override_interval; // The Assert_Override_Interval bool _should_send_pim_hello; // True if PIM_HELLO should be sent // before any other control messages // // Statistics-related counters and values // ConfigParam _pimstat_hello_messages_received; ConfigParam _pimstat_hello_messages_sent; ConfigParam _pimstat_hello_messages_rx_errors; ConfigParam _pimstat_register_messages_received; ConfigParam _pimstat_register_messages_sent; ConfigParam _pimstat_register_messages_rx_errors; ConfigParam _pimstat_register_stop_messages_received; ConfigParam _pimstat_register_stop_messages_sent; ConfigParam _pimstat_register_stop_messages_rx_errors; ConfigParam _pimstat_join_prune_messages_received; ConfigParam _pimstat_join_prune_messages_sent; ConfigParam _pimstat_join_prune_messages_rx_errors; ConfigParam _pimstat_bootstrap_messages_received; ConfigParam _pimstat_bootstrap_messages_sent; ConfigParam _pimstat_bootstrap_messages_rx_errors; ConfigParam _pimstat_assert_messages_received; ConfigParam _pimstat_assert_messages_sent; ConfigParam _pimstat_assert_messages_rx_errors; ConfigParam _pimstat_graft_messages_received; ConfigParam _pimstat_graft_messages_sent; ConfigParam _pimstat_graft_messages_rx_errors; ConfigParam _pimstat_graft_ack_messages_received; ConfigParam _pimstat_graft_ack_messages_sent; ConfigParam _pimstat_graft_ack_messages_rx_errors; ConfigParam _pimstat_candidate_rp_messages_received; ConfigParam _pimstat_candidate_rp_messages_sent; ConfigParam _pimstat_candidate_rp_messages_rx_errors; // ConfigParam _pimstat_unknown_type_messages; ConfigParam _pimstat_unknown_version_messages; ConfigParam _pimstat_neighbor_unknown_messages; ConfigParam _pimstat_bad_length_messages; ConfigParam _pimstat_bad_checksum_messages; ConfigParam _pimstat_bad_receive_interface_messages; // XXX: unused ConfigParam _pimstat_rx_interface_disabled_messages; ConfigParam _pimstat_rx_register_not_rp; ConfigParam _pimstat_rp_filtered_source; // XXX: unused ConfigParam _pimstat_unknown_register_stop; ConfigParam _pimstat_rx_join_prune_no_state; // XXX: unused ConfigParam _pimstat_rx_graft_graft_ack_no_state; // XXX: unused ConfigParam _pimstat_rx_graft_on_upstream_interface; // XXX: unused ConfigParam _pimstat_rx_candidate_rp_not_bsr; ConfigParam _pimstat_rx_bsr_when_bsr; // XXX: unused ConfigParam _pimstat_rx_bsr_not_rpf_interface; ConfigParam _pimstat_rx_unknown_hello_option; ConfigParam _pimstat_rx_data_no_state; // XXX: unused ConfigParam _pimstat_rx_rp_no_state; // XXX: unused ConfigParam _pimstat_rx_aggregate; // XXX: unused ConfigParam _pimstat_rx_malformed_packet; ConfigParam _pimstat_no_rp; // XXX: unused ConfigParam _pimstat_no_route_upstream; // XXX: unused ConfigParam _pimstat_rp_mismatch; // XXX: unused ConfigParam _pimstat_rpf_neighbor_unknown; // XXX: unused // ConfigParam _pimstat_rx_join_rp; ConfigParam _pimstat_rx_prune_rp; ConfigParam _pimstat_rx_join_wc; ConfigParam _pimstat_rx_prune_wc; ConfigParam _pimstat_rx_join_sg; ConfigParam _pimstat_rx_prune_sg; ConfigParam _pimstat_rx_join_sg_rpt; ConfigParam _pimstat_rx_prune_sg_rpt; size_t _usage_by_pim_mre_task; // Counter for usage by PimMreTask // Not-so handy private functions that should go somewhere else // PIM control messages recv functions int pim_hello_recv(PimNbr *pim_nbr, const IPvX& src, const IPvX& dst, buffer_t *buffer, int nbr_proto_version); int pim_register_recv(PimNbr *pim_nbr, const IPvX& src, const IPvX& dst, buffer_t *buffer); int pim_register_stop_recv(PimNbr *pim_nbr, const IPvX& src, const IPvX& dst, buffer_t *buffer); int pim_join_prune_recv(PimNbr *pim_nbr, const IPvX& src, const IPvX& dst, buffer_t *buffer, uint8_t message_type); int pim_bootstrap_recv(PimNbr *pim_nbr, const IPvX& src, const IPvX& dst, buffer_t *buffer); int pim_assert_recv(PimNbr *pim_nbr, const IPvX& src, const IPvX& dst, buffer_t *buffer); int pim_graft_recv(PimNbr *pim_nbr, const IPvX& src, const IPvX& dst, buffer_t *buffer); int pim_graft_ack_recv(PimNbr *pim_nbr, const IPvX& src, const IPvX& dst, buffer_t *buffer); int pim_cand_rp_adv_recv(PimNbr *pim_nbr, const IPvX& src, const IPvX& dst, buffer_t *buffer); // PIM control messages process functions int pim_process(const IPvX& src, const IPvX& dst, buffer_t *buffer); int pim_assert_process(PimNbr *pim_nbr, const IPvX& src, const IPvX& dst, const IPvX& assert_source_addr, const IPvX& assert_group_addr, uint8_t assert_group_mask_len, AssertMetric *assert_metric); int pim_register_stop_process(const IPvX& rp_addr, const IPvX& source_addr, const IPvX& group_addr, uint8_t group_mask_len); bool wants_to_be_started; // as soon as we can, ie if the interface appears. }; // // Global variables // // // Global functions prototypes // #endif // __PIM_PIM_VIF_HH__ xorp/pim/pim_mrt.hh0000664000076400007640000002314011635757530014451 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __PIM_PIM_MRT_HH__ #define __PIM_PIM_MRT_HH__ // // PIM Multicast Routing Table header file. // #include "libproto/proto_unit.hh" #include "mrt/mifset.hh" #include "mrt/mrt.hh" #include "pim_mre_track_state.hh" class IPvX; class PimMfc; class PimMre; class PimMreTask; class PimMrt; class PimNode; class PimVif; class PimMribTable; // // PIM-specific (S,G) Multicast Routing Table // class PimMrtSg : public Mrt { public: PimMrtSg(PimMrt& pim_mrt); virtual ~PimMrtSg(); PimMrt& pim_mrt() const { return (_pim_mrt); } private: PimMrt& _pim_mrt; // The PIM Multicast Routing Table }; // // PIM-specific (*,G) Multicast Routing Table // class PimMrtG : public Mrt { public: PimMrtG(PimMrt& pim_mrt); virtual ~PimMrtG(); PimMrt& pim_mrt() const { return (_pim_mrt); } private: PimMrt& _pim_mrt; // The PIM Multicast Routing Table }; // // PIM-specific (*,*,RP) Multicast Routing Table // class PimMrtRp : public Mrt { public: PimMrtRp(PimMrt& pim_mrt); virtual ~PimMrtRp(); PimMrt& pim_mrt() const { return (_pim_mrt); } private: PimMrt& _pim_mrt; // The PIM Multicast Routing Table }; // // PIM-specific Multicast Forwarding Cache Table // class PimMrtMfc : public Mrt { public: PimMrtMfc(PimMrt& pim_mrt); virtual ~PimMrtMfc(); PimMrt& pim_mrt() const { return (_pim_mrt); } private: PimMrt& _pim_mrt; // The PIM Multicast Routing Table }; // // PIM-specific Multicast Routing Table // class PimMrt : public BugCatcher { public: PimMrt(PimNode* pim_node); virtual ~PimMrt(); PimNode* pim_node() const { return (_pim_node); } PimMrtSg& pim_mrt_sg() { return (_pim_mrt_sg); } PimMrtSg& pim_mrt_sg_rpt() { return (_pim_mrt_sg_rpt); } PimMrtG& pim_mrt_g() { return (_pim_mrt_g); } PimMrtRp& pim_mrt_rp() { return (_pim_mrt_rp); } PimMrtMfc& pim_mrt_mfc() { return (_pim_mrt_mfc); } void clear(); PimMre *pim_mre_find(const IPvX& source, const IPvX& group, uint32_t lookup_flags, uint32_t create_flags); PimMfc *pim_mfc_find(const IPvX& source, const IPvX& group, bool is_creation_allowed); int remove_pim_mre(PimMre *pim_mre); int remove_pim_mfc(PimMfc *pim_mfc); // // MFC-related methods // int signal_message_nocache_recv(const string& src_module_instance_name, uint32_t vif_index, const IPvX& src, const IPvX& dst); int signal_message_wrongvif_recv(const string& src_module_instance_name, uint32_t vif_index, const IPvX& src, const IPvX& dst); int signal_message_wholepkt_recv(const string& src_module_instance_name, uint32_t vif_index, const IPvX& src, const IPvX& dst, const uint8_t *rcvbuf, size_t rcvlen); void receive_data(uint32_t iif_vif_index, const IPvX& src, const IPvX& dst); int signal_dataflow_recv(const IPvX& source_addr, const IPvX& group_addr, uint32_t threshold_interval_sec, uint32_t threshold_interval_usec, uint32_t measured_interval_sec, uint32_t measured_interval_usec, 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); // Redirection functions (to the pim_node) int family() const; PimMribTable& pim_mrib_table(); Mifset& i_am_dr(); PimVif *vif_find_by_vif_index(uint32_t vif_index); PimVif *vif_find_pim_register(); uint32_t pim_register_vif_index() const; // // Track-state related methods // const PimMreTrackState& pim_mre_track_state() const { return (_pim_mre_track_state); } void track_state_print_actions_name() const { _pim_mre_track_state.print_actions_name(); } void track_state_print_actions_num() const { _pim_mre_track_state.print_actions_num(); } // // Tasks related methods // void add_task(PimMreTask *pim_mre_task); void delete_task(PimMreTask *pim_mre_task); void schedule_task(); // // The "add_task_*" methods // void add_task_rp_changed(const IPvX& affected_rp_addr); void add_task_mrib_changed(const IPvXNet& modified_prefix_addr); void add_task_delete_mrib_entries(const list& mrib_list); void add_task_nbr_mrib_next_hop_changed(const IPvXNet& modified_prefix_addr); void add_task_nbr_mrib_next_hop_rp_gen_id_changed(const IPvX& rp_addr); void add_task_pim_nbr_changed(uint32_t vif_index, const IPvX& pim_nbr_addr); void add_task_pim_nbr_gen_id_changed(uint32_t vif_index, const IPvX& pim_nbr_addr); void add_task_assert_rpf_interface_wc(uint32_t old_rpf_interface_rp, const IPvX& group_addr); void add_task_assert_rpf_interface_sg(uint32_t old_rpf_interface_s, const IPvX& source_addr, const IPvX& group_addr); void add_task_receive_join_rp(uint32_t vif_index, const IPvX& rp_addr); void add_task_receive_join_wc(uint32_t vif_index, const IPvX& group_addr); void add_task_receive_join_sg(uint32_t vif_index, const IPvX& source_addr, const IPvX& group_addr); void add_task_receive_join_sg_rpt(uint32_t vif_index, const IPvX& source_addr, const IPvX& group_addr); void add_task_receive_prune_rp(uint32_t vif_index, const IPvX& rp_addr); void add_task_receive_prune_wc(uint32_t vif_index, const IPvX& group_addr); void add_task_see_prune_wc(uint32_t vif_index, const IPvX& group_addr, const IPvX& target_nbr_addr); void add_task_receive_prune_sg(uint32_t vif_index, const IPvX& source_addr, const IPvX& group_addr); void add_task_receive_prune_sg_rpt(uint32_t vif_index, const IPvX& source_addr, const IPvX& group_addr); void add_task_receive_end_of_message_sg_rpt(uint32_t vif_index, const IPvX& group_addr); void add_task_downstream_jp_state_rp(uint32_t vif_index, const IPvX& rp_addr); void add_task_downstream_jp_state_wc(uint32_t vif_index, const IPvX& group_addr); void add_task_downstream_jp_state_sg(uint32_t vif_index, const IPvX& source_addr, const IPvX& group_addr); void add_task_downstream_jp_state_sg_rpt(uint32_t vif_index, const IPvX& source_addr, const IPvX& group_addr); void add_task_upstream_jp_state_sg(const IPvX& source_addr, const IPvX& group_addr); void add_task_local_receiver_include_wc(uint32_t vif_index, const IPvX& group_addr); void add_task_local_receiver_include_sg(uint32_t vif_index, const IPvX& source_addr, const IPvX& group_addr); void add_task_local_receiver_exclude_sg(uint32_t vif_index, const IPvX& source_addr, const IPvX& group_addr); void add_task_assert_state_wc(uint32_t vif_index, const IPvX& group_addr); void add_task_assert_state_sg(uint32_t vif_index, const IPvX& source_addr, const IPvX& group_addr); void add_task_i_am_dr(uint32_t vif_index); void add_task_my_ip_address(uint32_t vif_index); void add_task_my_ip_subnet_address(uint32_t vif_index); void add_task_spt_switch_threshold_changed(); void add_task_was_switch_to_spt_desired_sg(const IPvX& source_addr, const IPvX& group_addr); void add_task_keepalive_timer_sg(const IPvX& source_addr, const IPvX& group_addr); void add_task_sptbit_sg(const IPvX& source_addr, const IPvX& group_addr); void add_task_start_vif(uint32_t vif_index); void add_task_stop_vif(uint32_t vif_index); void add_task_add_pim_mre(PimMre *pim_mre); void add_task_delete_pim_mre(PimMre *pim_mre); void add_task_delete_pim_mfc(PimMfc *pim_mfc); list& pim_mre_task_list() { return (_pim_mre_task_list); } private: void pim_mre_task_timer_timeout(); PimNode* _pim_node; // The PIM node // // The lookup tables // // XXX: all entries in the (*,G) table have source address = IPvX::ZERO() // XXX: all entries in the (*,*,RP) table have group address = IPvX::ZERO() // PimMrtSg _pim_mrt_sg; // The PIM-specific (S,G) MRT PimMrtSg _pim_mrt_sg_rpt; // The PIM-specific (S,G,rpt) MRT PimMrtG _pim_mrt_g; // The PIM-specific (*,G) MRT PimMrtRp _pim_mrt_rp; // The PIM-specific (*,*,RP) MRT PimMrtMfc _pim_mrt_mfc; // The PIM-specific MFC MRT PimMreTrackState _pim_mre_track_state; // The state-tracking information list _pim_mre_task_list; // The list of tasks // Timer to schedule the processing for the next task or time slice. XorpTimer _pim_mre_task_timer; }; #endif // __PIM_PIM_MRT_HH__ xorp/pim/pim_proto_register.cc0000664000076400007640000004462211540224232016673 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // PIM PIM_REGISTER messages processing. // #include "pim_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvx.hh" #include "libproto/checksum.h" #include "libproto/packet.hh" #include "pim_mfc.hh" #include "pim_node.hh" #include "pim_vif.hh" // // Exported variables // // // Local constants definitions // // // Local structures/classes, typedefs and macros // // // Local variables // // // Local functions prototypes // /** * PimVif::pim_register_recv: * @pim_nbr: The PIM neighbor message originator (or NULL if not a neighbor). * @src: The message source address. * @dst: The message destination address. * @buffer: The buffer with the message. * * Receive PIM_REGISTER message. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int PimVif::pim_register_recv(PimNbr *pim_nbr, const IPvX& src, const IPvX& dst, buffer_t *buffer) { uint32_t pim_register_flags; bool is_null_register, is_border_register; IPvX inner_src(family()), inner_dst(family()); uint32_t lookup_flags; PimMre *pim_mre, *pim_mre_sg; PimMfc *pim_mfc = NULL; bool is_sptbit_set = false; bool sent_register_stop = false; bool is_keepalive_timer_restarted = false; uint32_t keepalive_timer_sec = PIM_KEEPALIVE_PERIOD_DEFAULT; uint32_t register_vif_index = pim_node()->pim_register_vif_index(); string dummy_error_msg; // // Parse the message // BUFFER_GET_HOST_32(pim_register_flags, buffer); // The Border bit and the Null-Register bit if (pim_register_flags & PIM_BORDER_REGISTER) is_border_register = true; else is_border_register = false; if (pim_register_flags & PIM_NULL_REGISTER) is_null_register = true; else is_null_register = false; // // Get the inner source and destination addresses // switch (family()) { case AF_INET: { uint8_t ip_header4_buffer[IpHeader4::SIZE]; IpHeader4 ip4(ip_header4_buffer); BUFFER_GET_DATA(ip_header4_buffer, buffer, IpHeader4::SIZE); inner_src = IPvX(ip4.ip_src()); inner_dst = IPvX(ip4.ip_dst()); if (is_null_register) { // // If the inner header checksum is non-zero, then // check the checksum. // if (ip4.ip_sum() != 0) { uint16_t cksum = inet_checksum(ip4.data(), ip4.size()); if (cksum != 0) { XLOG_WARNING("RX %s%s from %s to %s: " "inner dummy IP header checksum error", PIMTYPE2ASCII(PIM_REGISTER), (is_null_register)? "(Null)" : "", cstring(src), cstring(dst)); ++_pimstat_bad_checksum_messages; return (XORP_ERROR); } } } break; } #ifdef HAVE_IPV6 case AF_INET6: { uint8_t ip_header6_buffer[IpHeader6::SIZE]; IpHeader6 ip6(ip_header6_buffer); uint16_t inner_data_len; BUFFER_GET_DATA(ip_header6_buffer, buffer, IpHeader6::SIZE); inner_src = IPvX(ip6.ip_src()); inner_dst = IPvX(ip6.ip_dst()); inner_data_len = ip6.ip_plen(); if (is_null_register) { // // If the dummy PIM header is present, then // check the checksum. // if (inner_data_len == sizeof(struct pim)) { struct pim pim_header; uint8_t *cp = (uint8_t *)&pim_header; BUFFER_GET_DATA(cp, buffer, sizeof(pim_header)); uint16_t cksum, cksum2; cksum = inet_checksum( reinterpret_cast(&pim_header), sizeof(pim_header)); cksum2 = calculate_ipv6_pseudo_header_checksum(inner_src, inner_dst, sizeof(struct pim), IPPROTO_PIM); cksum = inet_checksum_add(cksum, cksum2); if (cksum != 0) { XLOG_WARNING("RX %s%s from %s to %s: " "inner dummy IP header checksum error", PIMTYPE2ASCII(PIM_REGISTER), (is_null_register)? "(Null)" : "", cstring(src), cstring(dst)); ++_pimstat_bad_checksum_messages; return (XORP_ERROR); } } } break; } #endif // HAVE_IPV6 default: XLOG_UNREACHABLE(); return (XORP_ERROR); } // // Check the inner source and destination addresses // if (! inner_src.is_unicast()) { XLOG_WARNING("RX %s%s from %s to %s: " "inner source address = %s must be unicast", PIMTYPE2ASCII(PIM_REGISTER), (is_null_register)? "(Null)" : "", cstring(src), cstring(dst), cstring(inner_src)); return (XORP_ERROR); } if (! inner_dst.is_multicast()) { XLOG_WARNING("RX %s%s from %s to %s: " "inner destination address = %s must be multicast", PIMTYPE2ASCII(PIM_REGISTER), (is_null_register)? "(Null)" : "", cstring(src), cstring(dst), cstring(inner_dst)); return (XORP_ERROR); } if (inner_dst.is_linklocal_multicast() || inner_dst.is_interfacelocal_multicast()) { XLOG_WARNING("RX %s%s from %s to %s: " "inner destination address = %s must not be " "link or interface-local multicast group", PIMTYPE2ASCII(PIM_REGISTER), (is_null_register)? "(Null)" : "", cstring(src), cstring(dst), cstring(inner_dst)); return (XORP_ERROR); } // // XXX: the code below implements the logic in // packet_arrives_on_rp_tunnel( pkt ) from the spec. // // // Find if I am the RP for this group // if (register_vif_index == Vif::VIF_INDEX_INVALID) { // I don't have a PIM Register vif // // "send Register-Stop(S,G) to outer.src" // pim_register_stop_send(src, inner_src, inner_dst, dummy_error_msg); ++_pimstat_rx_register_not_rp; return (XORP_OK); } lookup_flags = PIM_MRE_RP | PIM_MRE_WC | PIM_MRE_SG | PIM_MRE_SG_RPT; pim_mre = pim_node()->pim_mrt().pim_mre_find(inner_src, inner_dst, lookup_flags, 0); // // Test if I am the RP for the multicast group // if ((pim_mre == NULL) || (pim_mre->rp_addr_ptr() == NULL) || (! pim_mre->i_am_rp()) || (dst != *pim_mre->rp_addr_ptr())) { // // "send Register-Stop(S,G) to outer.src" // pim_register_stop_send(src, inner_src, inner_dst, dummy_error_msg); ++_pimstat_rx_register_not_rp; return (XORP_OK); } // // I am the RP for the multicast group // // // Get the (S,G) entry if exists // pim_mre_sg = NULL; do { if (pim_mre->is_sg()) { pim_mre_sg = pim_mre; break; } if (pim_mre->is_sg_rpt()) { pim_mre_sg = pim_mre->sg_entry(); if (pim_mre_sg != NULL) break; } } while (false); is_sptbit_set = false; if ((pim_mre_sg != NULL) && pim_mre_sg->is_spt()) is_sptbit_set = true; // // The code below implements the core logic inside // packet_arrives_on_rp_tunnel() // sent_register_stop = false; if (is_border_register) { if ((pim_mre_sg != NULL) && pim_mre_sg->is_pmbr_addr_set() && (pim_mre_sg->pmbr_addr() != src)) { // // "send Register-Stop(S,G) to outer.src // drop the packet silently." // pim_register_stop_send(src, inner_src, inner_dst, dummy_error_msg); return (XORP_OK); } } // // Note that below we call is_switch_to_spt_desired_sg() with a time // interval of zero seconds and a threshold of zero bytes. // I.e., this check will evaluate to true if the SPT switch is enabled // and the threshold for the switch is 0 bytes (i.e., switch // immediately). // // If the configured threshold is larger, we are anyway going to // install a bandwidth monitor so that monitor will take care of // triggering the Register-Stop(S,G) if the bandwidth of the Register // messages reaches the configured threshold. // if (is_sptbit_set || (pim_mre->is_switch_to_spt_desired_sg(0, 0) && pim_mre->inherited_olist_sg().none())) { // // "send Register-Stop(S,G) to outer.src" // pim_register_stop_send(src, inner_src, inner_dst, dummy_error_msg); sent_register_stop = true; } if (is_sptbit_set || pim_mre->is_switch_to_spt_desired_sg(0, 0)) { // Create an (S,G) entry that will keep the Keepalive Timer running if (pim_mre_sg == NULL) { pim_mre_sg = pim_node()->pim_mrt().pim_mre_find(inner_src, inner_dst, PIM_MRE_SG, PIM_MRE_SG); } if (sent_register_stop) { // "restart KeepaliveTimer(S,G) to RP_Keepalive_Period" keepalive_timer_sec = PIM_RP_KEEPALIVE_PERIOD_DEFAULT; pim_mre_sg->set_is_kat_set_to_rp_keepalive_period(true); } else { // "restart KeepaliveTimer(S,G) to Keepalive_Period" keepalive_timer_sec = PIM_KEEPALIVE_PERIOD_DEFAULT; pim_mre_sg->set_is_kat_set_to_rp_keepalive_period(false); } pim_mre_sg->start_keepalive_timer(); is_keepalive_timer_restarted = true; } if (is_border_register) { // // Update the PMBR address // if (pim_mre_sg != NULL) { if (! pim_mre_sg->is_pmbr_addr_set()) pim_mre_sg->set_pmbr_addr(src); } } if ((! is_sptbit_set) && (! is_null_register)) { // // "decapsulate and forward the inner packet to // inherited_olist(S,G,rpt)" // // XXX: This will happen inside the kernel. // Here we only install the forwarding entry. // pim_mfc = pim_node()->pim_mrt().pim_mfc_find(inner_src, inner_dst, false); if (pim_mfc == NULL) { pim_mfc = pim_node()->pim_mrt().pim_mfc_find(inner_src, inner_dst, true); pim_mfc->update_mfc(register_vif_index, pim_mre->inherited_olist_sg_rpt(), pim_mre_sg); } } // // Restart KeepaliveTimer(S,G) // if (pim_mre_sg != NULL) { if (is_keepalive_timer_restarted || ((pim_mfc != NULL) && (! pim_mfc->has_idle_dataflow_monitor()))) { if (pim_mfc == NULL) { pim_mfc = pim_node()->pim_mrt().pim_mfc_find(inner_src, inner_dst, true); if (is_sptbit_set) { pim_mfc->update_mfc(pim_mre_sg->rpf_interface_s(), pim_mre->inherited_olist_sg(), pim_mre_sg); } else { pim_mfc->update_mfc(register_vif_index, pim_mre->inherited_olist_sg_rpt(), pim_mre_sg); } } // // Add a dataflow monitor to expire idle (S,G) PimMre state // and/or idle PimMfc+MFC state // if (! pim_mfc->has_idle_dataflow_monitor()) { pim_mfc->add_dataflow_monitor(keepalive_timer_sec, 0, 0, // threshold_packets 0, // threshold_bytes true, // is_threshold_in_packets false, // is_threshold_in_bytes false, // is_geq_upcall ">=" true); // is_leq_upcall "<=" } } return (XORP_OK); } // // If necessary, add a dataflow monitor to monitor whether it is // time to switch to the SPT. // if (pim_mfc == NULL) { pim_mfc = pim_node()->pim_mrt().pim_mfc_find(inner_src, inner_dst, false); } if (pim_mfc == NULL) return (XORP_OK); if (pim_node()->is_switch_to_spt_enabled().get() && (pim_mre != NULL) && (pim_mre->is_monitoring_switch_to_spt_desired_sg(pim_mre_sg)) && (! pim_mfc->has_spt_switch_dataflow_monitor())) { // Install the monitor uint32_t sec = pim_node()->switch_to_spt_threshold_interval_sec().get(); uint32_t bytes = pim_node()->switch_to_spt_threshold_bytes().get(); pim_mfc->add_dataflow_monitor(sec, 0, 0, // threshold_packets bytes, // threshold_bytes false, // is_threshold_in_packets true, // is_threshold_in_bytes true, // is_geq_upcall ">=" false); // is_leq_upcall "<=" } return (XORP_OK); UNUSED(pim_nbr); // Various error processing rcvlen_error: XLOG_WARNING("RX %s from %s to %s: " "invalid message length", PIMTYPE2ASCII(PIM_REGISTER), cstring(src), cstring(dst)); ++_pimstat_rx_malformed_packet; return (XORP_ERROR); } int PimVif::pim_register_send(const IPvX& rp_addr, const IPvX& source_addr, const IPvX& group_addr, const uint8_t *rcvbuf, size_t rcvlen, string& error_msg) { IpHeader4 ip4(rcvbuf); buffer_t *buffer; uint32_t flags = 0; size_t mtu = 0; string dummy_error_msg; UNUSED(group_addr); if (ip4.ip_version() != source_addr.ip_version()) { error_msg = c_format("Cannot encapsulate IP packet: " "inner IP version (%u) != expected IP version (%u)", XORP_UINT_CAST(ip4.ip_version()), XORP_UINT_CAST(source_addr.ip_version())); XLOG_WARNING("%s", error_msg.c_str()); return (XORP_ERROR); } // TODO: XXX: PAVPAVPAV: if a border router, set the Border-bit to flags // // Calculate the MTU. // // Note that we handle only the case when the encapsulated // packet size will become larger than the maximum packet size. // switch (family()) { case AF_INET: { mtu = 0xffff // IPv4 max packet size - (0xf << 2) // IPv4 max header size - sizeof(struct pim) - sizeof(uint32_t); break; } #ifdef HAVE_IPV6 case AF_INET6: { mtu = 0xffff // IPv6 max payload size (jumbo payload excluded) - sizeof(struct pim) - sizeof(uint32_t); break; } #endif // HAVE_IPV6 default: XLOG_UNREACHABLE(); error_msg = c_format("Invalid address family: %d", family()); return (XORP_ERROR); } // // If the data packet is small enough, just send it // if (rcvlen <= mtu) { buffer = buffer_send_prepare(); // Write all data to the buffer BUFFER_PUT_HOST_32(flags, buffer); // TODO: XXX: PAVPAVPAV: check the data length, and the inner IP pkt. BUFFER_PUT_DATA(rcvbuf, buffer, rcvlen); return (pim_send(domain_wide_addr(), rp_addr, PIM_REGISTER, buffer, error_msg)); } // // Fragment the inner packet, then encapsulate and send each fragment // if (family() == AF_INET) { list > fragments; list >::iterator iter; if (ip4.fragment(mtu, fragments, true, error_msg) != XORP_OK) { // // XXX: If fragmentation is forbidded, we don't send // ICMP "fragmentation needed" back to the sender, because in // IPv4 ICMP error messages are not sent back for datagrams // destinated to an multicast address. // return (XORP_ERROR); } // // XXX: we already checked that the data packet is larger than // the MTU, so the packet must have been fragmented. // XLOG_ASSERT(! fragments.empty()); // Encapsulate and send the fragments for (iter = fragments.begin(); iter != fragments.end(); ++iter) { vector& ip_fragment = *iter; buffer = buffer_send_prepare(); BUFFER_PUT_HOST_32(flags, buffer); BUFFER_PUT_DATA(&ip_fragment[0], buffer, ip_fragment.size()); pim_send(domain_wide_addr(), rp_addr, PIM_REGISTER, buffer, dummy_error_msg); } } #ifdef HAVE_IPV6 if (family() == AF_INET6) { // In IPv6 routers do not fragment packets that they forward. // Hence, just send ICMP back to the sender // TODO: XXX: PAVPAVPAV: send back ICMP "Packet Too Big" } #endif // HAVE_IPV6 return (XORP_OK); buflen_error: XLOG_UNREACHABLE(); error_msg = c_format("TX %s from %s to %s: " "packet cannot fit into sending buffer", PIMTYPE2ASCII(PIM_REGISTER), cstring(domain_wide_addr()), cstring(rp_addr)); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } int PimVif::pim_register_null_send(const IPvX& rp_addr, const IPvX& source_addr, const IPvX& group_addr, string& error_msg) { buffer_t *buffer = buffer_send_prepare(); uint32_t flags = 0; // Write all data to the buffer flags |= PIM_NULL_REGISTER; BUFFER_PUT_HOST_32(flags, buffer); // Create the dummy IP header and write it to the buffer switch (family()) { case AF_INET: { uint8_t ip_header4_buffer[IpHeader4::SIZE]; memset(ip_header4_buffer, 0, sizeof(ip_header4_buffer)); IpHeader4Writer ip4(ip_header4_buffer); ip4.set_ip_version(IpHeader4::IP_VERSION); ip4.set_ip_header_len(IpHeader4::SIZE); ip4.set_ip_tos(0); ip4.set_ip_id(0); ip4.set_ip_off(0); ip4.set_ip_p(IPPROTO_PIM); ip4.set_ip_len(IpHeader4::SIZE); ip4.set_ip_ttl(0); ip4.set_ip_src(source_addr.get_ipv4()); ip4.set_ip_dst(group_addr.get_ipv4()); ip4.compute_checksum(); BUFFER_PUT_DATA(ip_header4_buffer, buffer, IpHeader4::SIZE); break; } #ifdef HAVE_IPV6 case AF_INET6: { // // First generate the dummy IPv6 header, and then the dummy PIM header // uint8_t ip_header6_buffer[IpHeader6::SIZE]; memset(ip_header6_buffer, 0, sizeof(ip_header6_buffer)); // Generate the dummy IPv6 header IpHeader6Writer ip6(ip_header6_buffer); ip6.set_ip_vtc_flow(0); ip6.set_ip_version(IpHeader6::IP_VERSION); ip6.set_ip_plen(sizeof(struct pim)); ip6.set_ip_hlim(0); ip6.set_ip_nxt(IPPROTO_PIM); ip6.set_ip_src(source_addr.get_ipv6()); ip6.set_ip_dst(group_addr.get_ipv6()); BUFFER_PUT_DATA(ip_header6_buffer, buffer, IpHeader6::SIZE); // Generate the dummy PIM header uint16_t cksum, cksum2; struct pim pim_header; uint8_t *cp = (uint8_t *)&pim_header; memset(&pim_header, 0, sizeof(pim_header)); cksum = inet_checksum(reinterpret_cast(&pim_header), sizeof(pim_header)); // XXX: no-op cksum2 = calculate_ipv6_pseudo_header_checksum(source_addr, group_addr, sizeof(struct pim), IPPROTO_PIM); cksum = inet_checksum_add(cksum, cksum2); pim_header.pim_cksum = cksum; BUFFER_PUT_DATA(cp, buffer, sizeof(pim_header)); break; } #endif // HAVE_IPV6 default: XLOG_UNREACHABLE(); error_msg = c_format("Invalid address family: %d", family()); return (XORP_ERROR); } return (pim_send(domain_wide_addr(), rp_addr, PIM_REGISTER, buffer, error_msg)); buflen_error: XLOG_UNREACHABLE(); error_msg = c_format("TX %s%s from %s to %s: " "packet cannot fit into sending buffer", PIMTYPE2ASCII(PIM_REGISTER), "(Null)", cstring(domain_wide_addr()), cstring(rp_addr)); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } xorp/pim/pim_config.cc0000664000076400007640000014451311540224231015070 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // TODO: a temporary solution for various PIM configuration // #include "pim_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/random.h" #include "libxorp/ipvx.hh" #include "pim_node.hh" #include "pim_vif.hh" int PimNode::set_config_all_vifs_done(string& error_msg) { map::iterator vif_iter; map& configured_vifs = ProtoNode::configured_vifs(); set send_pim_hello_vifs; string dummy_error_msg; // // Add new vifs and update existing ones // for (vif_iter = configured_vifs.begin(); vif_iter != configured_vifs.end(); ++vif_iter) { Vif* vif = &vif_iter->second; Vif* node_vif = vif_find_by_name(vif->name()); // // Add a new vif // if (node_vif == NULL) { add_vif(*vif, dummy_error_msg); continue; } // // Update the vif flags // set_vif_flags(vif->name(), vif->is_pim_register(), vif->is_p2p(), vif->is_loopback(), vif->is_multicast_capable(), vif->is_broadcast_capable(), vif->is_underlying_vif_up(), vif->mtu(), dummy_error_msg); } // // Add new vif addresses, update existing ones, and remove old addresses // for (vif_iter = configured_vifs.begin(); vif_iter != configured_vifs.end(); ++vif_iter) { Vif* vif = &vif_iter->second; Vif* node_vif = vif_find_by_name(vif->name()); list::const_iterator vif_addr_iter; if (node_vif == NULL) continue; // // Add new vif addresses and update existing ones // for (vif_addr_iter = vif->addr_list().begin(); vif_addr_iter != vif->addr_list().end(); ++vif_addr_iter) { const VifAddr& vif_addr = *vif_addr_iter; bool should_send_pim_hello = false; add_vif_addr(vif->name(), vif_addr.addr(), vif_addr.subnet_addr(), vif_addr.broadcast_addr(), vif_addr.peer_addr(), should_send_pim_hello, dummy_error_msg); if (should_send_pim_hello) send_pim_hello_vifs.insert(vif->name()); } // // Delete vif addresses that don't exist anymore // { list delete_addresses_list; 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->find_address(vif_addr.addr()) == 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; bool should_send_pim_hello = false; delete_vif_addr(vif->name(), ipvx, should_send_pim_hello, dummy_error_msg); if (should_send_pim_hello) send_pim_hello_vifs.insert(vif->name()); } } } // // Remove vifs that don't exist anymore // for (uint32_t i = 0; i < maxvifs(); i++) { Vif* node_vif = vif_find_by_vif_index(i); if (node_vif == NULL) continue; if (node_vif->is_pim_register()) continue; // XXX: don't delete the PIM Register vif if (configured_vifs.find(node_vif->name()) == configured_vifs.end()) { // Delete the interface string vif_name = node_vif->name(); delete_vif(vif_name, dummy_error_msg); continue; } } // // Spec: // "If an interface changes one of its secondary IP addresses, // a Hello message with an updated Address_List option and a // non-zero HoldTime should be sent immediately." // set::iterator set_iter; for (set_iter = send_pim_hello_vifs.begin(); set_iter != send_pim_hello_vifs.end(); ++set_iter) { string vif_name = *set_iter; PimVif *pim_vif = vif_find_by_name(vif_name); if ((pim_vif != NULL) && pim_vif->is_up()) { if (! pim_vif->is_pim_register()) pim_vif->pim_hello_send(dummy_error_msg); } } if (end_config(error_msg) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int PimNode::get_vif_proto_version(const string& vif_name, int& proto_version, string& error_msg) { PimVif *pim_vif = vif_find_by_name(vif_name); if (pim_vif == NULL) { error_msg = c_format("Cannot get protocol version for vif %s: " "no such vif", vif_name.c_str()); return (XORP_ERROR); } proto_version = pim_vif->proto_version(); return (XORP_OK); } int PimNode::set_vif_proto_version(const string& vif_name, int proto_version, string& error_msg) { PimVif *pim_vif = vif_find_by_name(vif_name); if (start_config(error_msg) != XORP_OK) return (XORP_ERROR); if (pim_vif == NULL) { end_config(error_msg); error_msg = c_format("Cannot set protocol version for vif %s: " "no such vif", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } if (pim_vif->set_proto_version(proto_version) != XORP_OK) { end_config(error_msg); error_msg = c_format("Cannot set protocol version for vif %s: " "invalid protocol version %d", vif_name.c_str(), proto_version); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } if (end_config(error_msg) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int PimNode::reset_vif_proto_version(const string& vif_name, string& error_msg) { PimVif *pim_vif = vif_find_by_name(vif_name); if (start_config(error_msg) != XORP_OK) return (XORP_ERROR); if (pim_vif == NULL) { end_config(error_msg); error_msg = c_format("Cannot reset protocol version for vif %s: " "no such vif", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } pim_vif->set_proto_version(pim_vif->proto_version_default()); if (end_config(error_msg) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int PimNode::get_vif_hello_triggered_delay(const string& vif_name, uint16_t& hello_triggered_delay, string& error_msg) { PimVif *pim_vif = vif_find_by_name(vif_name); if (pim_vif == NULL) { error_msg = c_format("Cannot get Hello triggered delay for vif %s: " "no such vif", vif_name.c_str()); return (XORP_ERROR); } hello_triggered_delay = pim_vif->hello_triggered_delay().get(); return (XORP_OK); } int PimNode::set_vif_hello_triggered_delay(const string& vif_name, uint16_t hello_triggered_delay, string& error_msg) { PimVif *pim_vif = vif_find_by_name(vif_name); if (start_config(error_msg) != XORP_OK) return (XORP_ERROR); if (pim_vif == NULL) { end_config(error_msg); error_msg = c_format("Cannot set Hello triggered delay for vif %s: " "no such vif", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } pim_vif->hello_triggered_delay().set(hello_triggered_delay); if (end_config(error_msg) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int PimNode::reset_vif_hello_triggered_delay(const string& vif_name, string& error_msg) { PimVif *pim_vif = vif_find_by_name(vif_name); if (start_config(error_msg) != XORP_OK) return (XORP_ERROR); if (pim_vif == NULL) { end_config(error_msg); error_msg = c_format("Cannot reset Hello triggered delay for vif %s: " "no such vif", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } pim_vif->hello_triggered_delay().reset(); if (end_config(error_msg) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int PimNode::get_vif_hello_period(const string& vif_name, uint16_t& hello_period, string& error_msg) { PimVif *pim_vif = vif_find_by_name(vif_name); if (pim_vif == NULL) { error_msg = c_format("Cannot get Hello period for vif %s: " "no such vif", vif_name.c_str()); return (XORP_ERROR); } hello_period = pim_vif->hello_period().get(); return (XORP_OK); } int PimNode::set_vif_hello_period(const string& vif_name, uint16_t hello_period, string& error_msg) { PimVif *pim_vif = vif_find_by_name(vif_name); string dummy_error_msg; if (start_config(error_msg) != XORP_OK) return (XORP_ERROR); if (pim_vif == NULL) { end_config(error_msg); error_msg = c_format("Cannot set Hello period for vif %s: " "no such vif", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } pim_vif->hello_period().set(hello_period); if (! pim_vif->is_pim_register()) { // // Send immediately a Hello message, and schedule the next one // at random in the interval [0, hello_period) // pim_vif->pim_hello_send(dummy_error_msg); pim_vif->hello_timer_start_random(pim_vif->hello_period().get(), 0); } if (end_config(error_msg) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int PimNode::reset_vif_hello_period(const string& vif_name, string& error_msg) { PimVif *pim_vif = vif_find_by_name(vif_name); string dummy_error_msg; if (start_config(error_msg) != XORP_OK) return (XORP_ERROR); if (pim_vif == NULL) { end_config(error_msg); error_msg = c_format("Cannot reset Hello period for vif %s: " "no such vif", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } pim_vif->hello_period().reset(); if (! pim_vif->is_pim_register()) { // // Send immediately a Hello message, and schedule the next one // at random in the interval [0, hello_period) // pim_vif->pim_hello_send(dummy_error_msg); pim_vif->hello_timer_start_random(pim_vif->hello_period().get(), 0); } if (end_config(error_msg) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int PimNode::get_vif_hello_holdtime(const string& vif_name, uint16_t& hello_holdtime, string& error_msg) { PimVif *pim_vif = vif_find_by_name(vif_name); if (pim_vif == NULL) { error_msg = c_format("Cannot get Hello holdtime for vif %s: " "no such vif", vif_name.c_str()); return (XORP_ERROR); } hello_holdtime = pim_vif->hello_holdtime().get(); return (XORP_OK); } int PimNode::set_vif_hello_holdtime(const string& vif_name, uint16_t hello_holdtime, string& error_msg) { PimVif *pim_vif = vif_find_by_name(vif_name); string dummy_error_msg; if (start_config(error_msg) != XORP_OK) return (XORP_ERROR); if (pim_vif == NULL) { end_config(error_msg); error_msg = c_format("Cannot set Hello holdtime for vif %s: " "no such vif", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } pim_vif->hello_holdtime().set(hello_holdtime); if (! pim_vif->is_pim_register()) { // // Send immediately a Hello message, and schedule the next one // at random in the interval [0, hello_period) // pim_vif->pim_hello_send(dummy_error_msg); pim_vif->hello_timer_start_random(pim_vif->hello_period().get(), 0); } if (end_config(error_msg) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int PimNode::reset_vif_hello_holdtime(const string& vif_name, string& error_msg) { PimVif *pim_vif = vif_find_by_name(vif_name); string dummy_error_msg; if (start_config(error_msg) != XORP_OK) return (XORP_ERROR); if (pim_vif == NULL) { end_config(error_msg); error_msg = c_format("Cannot reset Hello holdtime for vif %s: " "no such vif", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } pim_vif->hello_holdtime().reset(); if (! pim_vif->is_pim_register()) { // // Send immediately a Hello message, and schedule the next one // at random in the interval [0, hello_period) // pim_vif->pim_hello_send(dummy_error_msg); pim_vif->hello_timer_start_random(pim_vif->hello_period().get(), 0); } if (end_config(error_msg) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int PimNode::get_vif_dr_priority(const string& vif_name, uint32_t& dr_priority, string& error_msg) { PimVif *pim_vif = vif_find_by_name(vif_name); if (pim_vif == NULL) { error_msg = c_format("Cannot get DR priority for vif %s: " "no such vif", vif_name.c_str()); return (XORP_ERROR); } dr_priority = pim_vif->dr_priority().get(); return (XORP_OK); } int PimNode::set_vif_dr_priority(const string& vif_name, uint32_t dr_priority, string& error_msg) { PimVif *pim_vif = vif_find_by_name(vif_name); string dummy_error_msg; if (start_config(error_msg) != XORP_OK) return (XORP_ERROR); if (pim_vif == NULL) { end_config(error_msg); error_msg = c_format("Cannot set DR priority for vif %s: " "no such vif", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } pim_vif->dr_priority().set(dr_priority); if (! pim_vif->is_pim_register()) { // Send immediately a Hello message with the new value pim_vif->pim_hello_send(dummy_error_msg); // (Re)elect the DR pim_vif->pim_dr_elect(); } if (end_config(error_msg) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int PimNode::reset_vif_dr_priority(const string& vif_name, string& error_msg) { PimVif *pim_vif = vif_find_by_name(vif_name); string dummy_error_msg; if (start_config(error_msg) != XORP_OK) return (XORP_ERROR); if (pim_vif == NULL) { end_config(error_msg); error_msg = c_format("Cannot reset DR priority for vif %s: " "no such vif", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } pim_vif->dr_priority().reset(); if (! pim_vif->is_pim_register()) { // Send immediately a Hello message with the new value pim_vif->pim_hello_send(dummy_error_msg); // (Re)elect the DR pim_vif->pim_dr_elect(); } if (end_config(error_msg) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int PimNode::get_vif_propagation_delay(const string& vif_name, uint16_t& propagation_delay, string& error_msg) { PimVif *pim_vif = vif_find_by_name(vif_name); if (pim_vif == NULL) { error_msg = c_format("Cannot get Propagation delay for vif %s: " "no such vif", vif_name.c_str()); return (XORP_ERROR); } propagation_delay = pim_vif->propagation_delay().get(); return (XORP_OK); } int PimNode::set_vif_propagation_delay(const string& vif_name, uint16_t propagation_delay, string& error_msg) { PimVif *pim_vif = vif_find_by_name(vif_name); string dummy_error_msg; if (start_config(error_msg) != XORP_OK) return (XORP_ERROR); if (pim_vif == NULL) { end_config(error_msg); error_msg = c_format("Cannot set Propagation delay for vif %s: " "no such vif", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } pim_vif->propagation_delay().set(propagation_delay); if (! pim_vif->is_pim_register()) { // Send immediately a Hello message with the new value pim_vif->pim_hello_send(dummy_error_msg); } if (end_config(error_msg) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int PimNode::reset_vif_propagation_delay(const string& vif_name, string& error_msg) { PimVif *pim_vif = vif_find_by_name(vif_name); string dummy_error_msg; if (start_config(error_msg) != XORP_OK) return (XORP_ERROR); if (pim_vif == NULL) { end_config(error_msg); error_msg = c_format("Cannot reset Propagation delay for vif %s: " "no such vif", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } pim_vif->propagation_delay().reset(); if (! pim_vif->is_pim_register()) { // Send immediately a Hello message with the new value pim_vif->pim_hello_send(dummy_error_msg); } if (end_config(error_msg) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int PimNode::get_vif_override_interval(const string& vif_name, uint16_t& override_interval, string& error_msg) { PimVif *pim_vif = vif_find_by_name(vif_name); if (pim_vif == NULL) { error_msg = c_format("Cannot get Override interval for vif %s: " "no such vif", vif_name.c_str()); return (XORP_ERROR); } override_interval = pim_vif->override_interval().get(); return (XORP_OK); } int PimNode::set_vif_override_interval(const string& vif_name, uint16_t override_interval, string& error_msg) { PimVif *pim_vif = vif_find_by_name(vif_name); string dummy_error_msg; if (start_config(error_msg) != XORP_OK) return (XORP_ERROR); if (pim_vif == NULL) { end_config(error_msg); error_msg = c_format("Cannot set Override interval for vif %s: " "no such vif", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } pim_vif->override_interval().set(override_interval); if (! pim_vif->is_pim_register()) { // Send immediately a Hello message with the new value pim_vif->pim_hello_send(dummy_error_msg); } if (end_config(error_msg) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int PimNode::reset_vif_override_interval(const string& vif_name, string& error_msg) { PimVif *pim_vif = vif_find_by_name(vif_name); string dummy_error_msg; if (start_config(error_msg) != XORP_OK) return (XORP_ERROR); if (pim_vif == NULL) { end_config(error_msg); error_msg = c_format("Cannot reset Override interval for vif %s: " "no such vif", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } pim_vif->override_interval().reset(); if (! pim_vif->is_pim_register()) { // Send immediately a Hello message with the new value pim_vif->pim_hello_send(dummy_error_msg); } if (end_config(error_msg) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int PimNode::get_vif_is_tracking_support_disabled(const string& vif_name, bool& is_tracking_support_disabled, string& error_msg) { PimVif *pim_vif = vif_find_by_name(vif_name); if (pim_vif == NULL) { error_msg = c_format("Cannot get Tracking support disabled flag for vif %s: " "no such vif", vif_name.c_str()); return (XORP_ERROR); } is_tracking_support_disabled = pim_vif->is_tracking_support_disabled().get(); return (XORP_OK); } int PimNode::set_vif_is_tracking_support_disabled(const string& vif_name, bool is_tracking_support_disabled, string& error_msg) { PimVif *pim_vif = vif_find_by_name(vif_name); string dummy_error_msg; if (start_config(error_msg) != XORP_OK) return (XORP_ERROR); if (pim_vif == NULL) { end_config(error_msg); error_msg = c_format("Cannot set Tracking support disabled flag for vif %s: " "no such vif", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } pim_vif->is_tracking_support_disabled().set(is_tracking_support_disabled); if (! pim_vif->is_pim_register()) { // Send immediately a Hello message with the new value pim_vif->pim_hello_send(dummy_error_msg); } if (end_config(error_msg) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int PimNode::reset_vif_is_tracking_support_disabled(const string& vif_name, string& error_msg) { PimVif *pim_vif = vif_find_by_name(vif_name); string dummy_error_msg; if (start_config(error_msg) != XORP_OK) return (XORP_ERROR); if (pim_vif == NULL) { end_config(error_msg); error_msg = c_format("Cannot reset Tracking support disabled flag for vif %s: " "no such vif", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } pim_vif->is_tracking_support_disabled().reset(); if (! pim_vif->is_pim_register()) { // Send immediately a Hello message with the new value pim_vif->pim_hello_send(dummy_error_msg); } if (end_config(error_msg) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int PimNode::get_vif_accept_nohello_neighbors(const string& vif_name, bool& accept_nohello_neighbors, string& error_msg) { PimVif *pim_vif = vif_find_by_name(vif_name); if (pim_vif == NULL) { error_msg = c_format("Cannot get Accept nohello neighbors flag for vif %s: " "no such vif", vif_name.c_str()); return (XORP_ERROR); } accept_nohello_neighbors = pim_vif->accept_nohello_neighbors().get(); return (XORP_OK); } int PimNode::set_vif_accept_nohello_neighbors(const string& vif_name, bool accept_nohello_neighbors, string& error_msg) { PimVif *pim_vif = vif_find_by_name(vif_name); if (start_config(error_msg) != XORP_OK) return (XORP_ERROR); if (pim_vif == NULL) { end_config(error_msg); error_msg = c_format("Cannot set Accept nohello neighbors flag for vif %s: " "no such vif", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } if (accept_nohello_neighbors && (! pim_vif->is_p2p())) { XLOG_WARNING("Accepting no-Hello neighbors should not be enabled " "on non-point-to-point interfaces"); } pim_vif->accept_nohello_neighbors().set(accept_nohello_neighbors); if (end_config(error_msg) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int PimNode::reset_vif_accept_nohello_neighbors(const string& vif_name, string& error_msg) { PimVif *pim_vif = vif_find_by_name(vif_name); if (start_config(error_msg) != XORP_OK) return (XORP_ERROR); if (pim_vif == NULL) { end_config(error_msg); error_msg = c_format("Cannot reset Accept nohello neighbors flag for vif %s: " "no such vif", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } pim_vif->accept_nohello_neighbors().reset(); if (end_config(error_msg) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int PimNode::get_vif_join_prune_period(const string& vif_name, uint16_t& join_prune_period, string& error_msg) { PimVif *pim_vif = vif_find_by_name(vif_name); if (pim_vif == NULL) { error_msg = c_format("Cannot get Join/Prune period for vif %s: " "no such vif", vif_name.c_str()); return (XORP_ERROR); } join_prune_period = pim_vif->join_prune_period().get(); return (XORP_OK); } int PimNode::set_vif_join_prune_period(const string& vif_name, uint16_t join_prune_period, string& error_msg) { PimVif *pim_vif = vif_find_by_name(vif_name); if (start_config(error_msg) != XORP_OK) return (XORP_ERROR); if (pim_vif == NULL) { end_config(error_msg); error_msg = c_format("Cannot set Join/Prune period for vif %s: " "no such vif", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } pim_vif->join_prune_period().set(join_prune_period); if (end_config(error_msg) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int PimNode::reset_vif_join_prune_period(const string& vif_name, string& error_msg) { PimVif *pim_vif = vif_find_by_name(vif_name); if (start_config(error_msg) != XORP_OK) return (XORP_ERROR); if (pim_vif == NULL) { end_config(error_msg); error_msg = c_format("Cannot reset Join/Prune period for vif %s: " "no such vif", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } pim_vif->join_prune_period().reset(); if (end_config(error_msg) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int PimNode::get_switch_to_spt_threshold(bool& is_enabled, uint32_t& interval_sec, uint32_t& bytes, string& error_msg) { UNUSED(error_msg); is_enabled = is_switch_to_spt_enabled().get(); interval_sec = switch_to_spt_threshold_interval_sec().get(); bytes = switch_to_spt_threshold_bytes().get(); return (XORP_OK); } int PimNode::set_switch_to_spt_threshold(bool is_enabled, uint32_t interval_sec, uint32_t bytes, string& error_msg) { if (start_config(error_msg) != XORP_OK) return (XORP_ERROR); if ((is_switch_to_spt_enabled().get() != is_enabled) || (switch_to_spt_threshold_interval_sec().get() != interval_sec) || (switch_to_spt_threshold_bytes().get() != bytes)) { is_switch_to_spt_enabled().set(is_enabled); switch_to_spt_threshold_interval_sec().set(interval_sec); switch_to_spt_threshold_bytes().set(bytes); // Add the task to update the SPT-switch threshold pim_mrt().add_task_spt_switch_threshold_changed(); } if (end_config(error_msg) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int PimNode::reset_switch_to_spt_threshold(string& error_msg) { if (start_config(error_msg) != XORP_OK) return (XORP_ERROR); do { bool is_enabled = is_switch_to_spt_enabled().get(); uint32_t interval_sec = switch_to_spt_threshold_interval_sec().get(); uint32_t bytes = switch_to_spt_threshold_bytes().get(); // Reset the values is_switch_to_spt_enabled().reset(); switch_to_spt_threshold_interval_sec().reset(); switch_to_spt_threshold_bytes().reset(); if ((is_switch_to_spt_enabled().get() != is_enabled) || (switch_to_spt_threshold_interval_sec().get() != interval_sec) || (switch_to_spt_threshold_bytes().get() != bytes)) { // Add the task to update the SPT-switch threshold pim_mrt().add_task_spt_switch_threshold_changed(); } } while (false); if (end_config(error_msg) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int PimNode::add_config_scope_zone_by_vif_name(const IPvXNet& scope_zone_id, const string& vif_name, string& error_msg) { PimVif *pim_vif = vif_find_by_name(vif_name); if (start_config(error_msg) != XORP_OK) return (XORP_ERROR); if (pim_vif == NULL) { end_config(error_msg); error_msg = c_format("Cannot add configure scope zone with vif %s: " "no such vif", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } pim_scope_zone_table().add_scope_zone(scope_zone_id, pim_vif->vif_index()); if (end_config(error_msg) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int PimNode::add_config_scope_zone_by_vif_addr(const IPvXNet& scope_zone_id, const IPvX& vif_addr, string& error_msg) { PimVif *pim_vif = vif_find_by_addr(vif_addr); if (start_config(error_msg) != XORP_OK) return (XORP_ERROR); if (pim_vif == NULL) { end_config(error_msg); error_msg = c_format("Cannot add configure scope zone with vif address %s: " "no such vif", cstring(vif_addr)); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } pim_scope_zone_table().add_scope_zone(scope_zone_id, pim_vif->vif_index()); if (end_config(error_msg) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int PimNode::delete_config_scope_zone_by_vif_name(const IPvXNet& scope_zone_id, const string& vif_name, string& error_msg) { PimVif *pim_vif = vif_find_by_name(vif_name); if (start_config(error_msg) != XORP_OK) return (XORP_ERROR); if (pim_vif == NULL) { end_config(error_msg); error_msg = c_format("Cannot delete configure scope zone with vif %s: " "no such vif", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } pim_scope_zone_table().delete_scope_zone(scope_zone_id, pim_vif->vif_index()); if (end_config(error_msg) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int PimNode::delete_config_scope_zone_by_vif_addr(const IPvXNet& scope_zone_id, const IPvX& vif_addr, string& error_msg) { PimVif *pim_vif = vif_find_by_addr(vif_addr); if (start_config(error_msg) != XORP_OK) return (XORP_ERROR); if (pim_vif == NULL) { end_config(error_msg); error_msg = c_format("Cannot delete configure scope zone with vif address %s: " "no such vif", cstring(vif_addr)); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } pim_scope_zone_table().delete_scope_zone(scope_zone_id, pim_vif->vif_index()); if (end_config(error_msg) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } // // Add myself as a Cand-BSR // Return: %XORP_OK on success, otherwise %XORP_ERROR. // int PimNode::add_config_cand_bsr(const IPvXNet& scope_zone_id, bool is_scope_zone, const string& vif_name, const IPvX& vif_addr, uint8_t bsr_priority, uint8_t hash_mask_len, string& error_msg) { PimVif *pim_vif = vif_find_by_name(vif_name); IPvX my_cand_bsr_addr = vif_addr; uint16_t fragment_tag = xorp_random() % 0xffff; string local_error_msg = ""; PimScopeZoneId zone_id(scope_zone_id, is_scope_zone); if (start_config(error_msg) != XORP_OK) return (XORP_ERROR); if (pim_vif == NULL) { end_config(error_msg); error_msg = c_format("Cannot add configure BSR with vif %s: " "no such vif", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } if (my_cand_bsr_addr == IPvX::ZERO(family())) { // Use the domain-wide address for the vif if (pim_vif->domain_wide_addr() == IPvX::ZERO(family())) { end_config(error_msg); error_msg = c_format("Cannot add configure BSR with vif %s: " "the vif has no configured address", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); // The vif has no address yet } my_cand_bsr_addr = pim_vif->domain_wide_addr(); } else { // Test that the specified address belongs to the vif if (! pim_vif->is_my_addr(my_cand_bsr_addr)) { end_config(error_msg); error_msg = c_format("Cannot add configure BSR with vif %s " "and address %s: " "the address does not belong to this vif", vif_name.c_str(), cstring(my_cand_bsr_addr)); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); // Invalid address } } // XXX: if hash_mask_len is 0, then set its value to default if (hash_mask_len == 0) hash_mask_len = PIM_BOOTSTRAP_HASH_MASK_LEN_DEFAULT(family()); BsrZone new_bsr_zone(pim_bsr(), my_cand_bsr_addr, bsr_priority, hash_mask_len, fragment_tag); new_bsr_zone.set_zone_id(zone_id); new_bsr_zone.set_i_am_candidate_bsr(true, pim_vif->vif_index(), my_cand_bsr_addr, bsr_priority); if (vif_addr != IPvX::ZERO(family())) new_bsr_zone.set_is_my_bsr_addr_explicit(true); if (pim_bsr().add_config_bsr_zone(new_bsr_zone, local_error_msg) == NULL) { string dummy_error_msg; end_config(dummy_error_msg); error_msg = c_format("Cannot add configure BSR with vif %s address %s " "for zone %s: %s", vif_name.c_str(), cstring(my_cand_bsr_addr), cstring(zone_id), local_error_msg.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } if (end_config(error_msg) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } // // Return: %XORP_OK on success, otherwise %XORP_ERROR. // int PimNode::delete_config_cand_bsr(const IPvXNet& scope_zone_id, bool is_scope_zone, string& error_msg) { BsrZone *bsr_zone = NULL; bool is_up = false; PimScopeZoneId zone_id(scope_zone_id, is_scope_zone); if (start_config(error_msg) != XORP_OK) return (XORP_ERROR); // // Find the BSR zone // bsr_zone = pim_bsr().find_config_bsr_zone(zone_id); if (bsr_zone == NULL) { end_config(error_msg); error_msg = c_format("Cannot delete configure BSR for zone %s: " "zone not found", cstring(zone_id)); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } // // Stop the BSR, delete the BSR zone, and restart the BSR if necessary // is_up = pim_bsr().is_up(); pim_bsr().stop(); if (bsr_zone->bsr_group_prefix_list().empty()) { // No Cand-RP, therefore delete the zone. pim_bsr().delete_config_bsr_zone(bsr_zone); } else { // There is Cand-RP configuration, therefore only reset the Cand-BSR // configuration. bsr_zone->set_i_am_candidate_bsr(false, Vif::VIF_INDEX_INVALID, IPvX::ZERO(family()), 0); } if (is_up) pim_bsr().start(); // XXX: restart the BSR if (end_config(error_msg) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } // // Add myself as a Cand-RP // Return: %XORP_OK on success, otherwise %XORP_ERROR. // int PimNode::add_config_cand_rp(const IPvXNet& group_prefix, bool is_scope_zone, const string& vif_name, const IPvX& vif_addr, uint8_t rp_priority, uint16_t rp_holdtime, string& error_msg) { PimVif *pim_vif = vif_find_by_name(vif_name); IPvX my_cand_rp_addr = vif_addr; BsrZone *config_bsr_zone = NULL; BsrRp *bsr_rp = NULL; string local_error_msg = ""; bool is_new_zone = false; if (start_config(error_msg) != XORP_OK) return (XORP_ERROR); if (pim_vif == NULL) { end_config(error_msg); error_msg = c_format("Cannot add configure Cand-RP with vif %s: " "no such vif", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } if (my_cand_rp_addr == IPvX::ZERO(family())) { // Use the domain-wide address for the vif if (pim_vif->domain_wide_addr() == IPvX::ZERO(family())) { end_config(error_msg); error_msg = c_format("Cannot add configure Cand-RP with vif %s: " "the vif has no configured address", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); // The vif has no address yet } my_cand_rp_addr = pim_vif->domain_wide_addr(); } else { // Test that the specified address belongs to the vif if (! pim_vif->is_my_addr(my_cand_rp_addr)) { string error_msg; end_config(error_msg); error_msg = c_format("Cannot add configure Cand-RP with vif %s " "and address %s: " "the address does not belong to this vif", vif_name.c_str(), cstring(my_cand_rp_addr)); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); // Invalid address } } config_bsr_zone = pim_bsr().find_config_bsr_zone_by_prefix(group_prefix, is_scope_zone); if (config_bsr_zone == NULL) { PimScopeZoneId zone_id(group_prefix, is_scope_zone); if (! is_scope_zone) { zone_id = PimScopeZoneId(IPvXNet::ip_multicast_base_prefix(family()), is_scope_zone); } BsrZone new_bsr_zone(pim_bsr(), zone_id); config_bsr_zone = pim_bsr().add_config_bsr_zone(new_bsr_zone, local_error_msg); if (config_bsr_zone == NULL) { string dummy_error_msg; end_config(dummy_error_msg); error_msg = c_format("Cannot add configure Cand-RP for " "zone group prefix %s (%s): %s", cstring(group_prefix), (is_scope_zone)? "scoped" : "non-scoped", local_error_msg.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } is_new_zone = true; } bsr_rp = config_bsr_zone->add_rp(group_prefix, is_scope_zone, my_cand_rp_addr, rp_priority, rp_holdtime, local_error_msg); if (bsr_rp == NULL) { string dummy_error_msg; end_config(dummy_error_msg); error_msg = c_format("Cannot add configure Cand-RP address %s for " "zone group prefix %s (%s): %s", cstring(my_cand_rp_addr), cstring(group_prefix), (is_scope_zone)? "scoped" : "non-scoped", local_error_msg.c_str()); XLOG_ERROR("%s", error_msg.c_str()); if (is_new_zone) pim_bsr().delete_config_bsr_zone(config_bsr_zone); return (XORP_ERROR); } bsr_rp->set_my_vif_index(pim_vif->vif_index()); if (vif_addr != IPvX::ZERO(family())) bsr_rp->set_is_my_rp_addr_explicit(true); if (end_config(error_msg) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } // // Delete myself as a Cand-RP // Return: %XORP_OK on success, otherwise %XORP_ERROR. // int PimNode::delete_config_cand_rp(const IPvXNet& group_prefix, bool is_scope_zone, const string& vif_name, const IPvX& vif_addr, string& error_msg) { PimVif *pim_vif = vif_find_by_name(vif_name); IPvX my_cand_rp_addr = vif_addr; BsrZone *bsr_zone = NULL; BsrGroupPrefix *bsr_group_prefix = NULL; BsrRp *bsr_rp = NULL; bool is_up = false; if (start_config(error_msg) != XORP_OK) return (XORP_ERROR); if (pim_vif == NULL) { end_config(error_msg); error_msg = c_format("Cannot delete configure Cand-RP with vif %s: " "no such vif", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } if (my_cand_rp_addr == IPvX::ZERO(family())) { // Use the domain-wide address for the vif if (pim_vif->domain_wide_addr() == IPvX::ZERO(family())) { end_config(error_msg); error_msg = c_format("Cannot delete configure Cand-RP with vif %s: " "the vif has no configured address", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); // The vif has no address yet } my_cand_rp_addr = pim_vif->domain_wide_addr(); } else { // // XXX: don't test that the specified address belongs to the vif // because the vif may have been reconfigured already and the // address may have been deleted. // } // // Find the BSR zone // bsr_zone = pim_bsr().find_config_bsr_zone_by_prefix(group_prefix, is_scope_zone); if (bsr_zone == NULL) { end_config(error_msg); error_msg = c_format("Cannot delete configure Cand-RP for zone for " "group prefix %s (%s): zone not found", cstring(group_prefix), (is_scope_zone)? "scoped" : "non-scoped"); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } // // Find the BSR group prefix // bsr_group_prefix = bsr_zone->find_bsr_group_prefix(group_prefix); if (bsr_group_prefix == NULL) { end_config(error_msg); error_msg = c_format("Cannot delete configure Cand-RP for zone for " "group prefix %s (%s): prefix not found", cstring(group_prefix), (is_scope_zone)? "scoped" : "non-scoped"); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } // // Find the RP // bsr_rp = bsr_group_prefix->find_rp(my_cand_rp_addr); if (bsr_rp == NULL) { end_config(error_msg); error_msg = c_format("Cannot delete configure Cand-RP for zone for " "group prefix %s (%s) and RP %s: RP not found", cstring(group_prefix), (is_scope_zone)? "scoped" : "non-scoped", cstring(my_cand_rp_addr)); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } // // Stop the BSR, delete the RP zone, and restart the BSR if necessary // is_up = pim_bsr().is_up(); pim_bsr().stop(); bsr_group_prefix->delete_rp(bsr_rp); bsr_rp = NULL; // Delete the BSR group prefix if not needed anymore if (bsr_group_prefix->rp_list().empty()) { bsr_zone->delete_bsr_group_prefix(bsr_group_prefix); bsr_group_prefix = NULL; } if (bsr_zone->bsr_group_prefix_list().empty() && (! bsr_zone->i_am_candidate_bsr())) { // No Cand-RP, and no Cand-BSR, therefore delete the zone. pim_bsr().delete_config_bsr_zone(bsr_zone); bsr_zone = NULL; } if (is_up) pim_bsr().start(); // XXX: restart the BSR if (end_config(error_msg) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } // // Add a statically configured RP // // Return: %XORP_OK on success, otherwise %XORP_ERROR. // // XXX: we don't call end_config(), because config_static_rp_done() will // do it when the RP configuration is completed. // int PimNode::add_config_static_rp(const IPvXNet& group_prefix, const IPvX& rp_addr, uint8_t rp_priority, uint8_t hash_mask_len, string& error_msg) { if (start_config(error_msg) != XORP_OK) return (XORP_ERROR); if (! group_prefix.is_multicast()) { // XXX: don't call end_config(error_msg); error_msg = c_format("Cannot add configure static RP with address %s " "for group prefix %s: " "not a multicast address", cstring(rp_addr), cstring(group_prefix)); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } if (! rp_addr.is_unicast()) { // XXX: don't call end_config(error_msg); error_msg = c_format("Cannot add configure static RP with address %s: " "not an unicast address", cstring(rp_addr)); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } // XXX: if hash_mask_len is 0, then set its value to default if (hash_mask_len == 0) hash_mask_len = PIM_BOOTSTRAP_HASH_MASK_LEN_DEFAULT(family()); if (rp_table().add_rp(rp_addr, rp_priority, group_prefix, hash_mask_len, PimRp::RP_LEARNED_METHOD_STATIC) == NULL) { // XXX: don't call end_config(error_msg); error_msg = c_format("Cannot add configure static RP with address %s " "and priority %d for group prefix %s", cstring(rp_addr), rp_priority, cstring(group_prefix)); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } // XXX: config_static_rp_done() will complete the configuration setup return (XORP_OK); } // // Delete a statically configured RP // // Return: %XORP_OK on success, otherwise %XORP_ERROR. // // XXX: we don't call end_config(), because config_static_rp_done() will // do it when the RP configuration is completed. // int PimNode::delete_config_static_rp(const IPvXNet& group_prefix, const IPvX& rp_addr, string& error_msg) { if (start_config(error_msg) != XORP_OK) return (XORP_ERROR); if (rp_table().delete_rp(rp_addr, group_prefix, PimRp::RP_LEARNED_METHOD_STATIC) != XORP_OK) { // XXX: don't call end_config(error_msg); error_msg = c_format("Cannot delete configure static RP with address %s " "for group prefix %s", cstring(rp_addr), cstring(group_prefix)); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } // XXX: config_static_rp_done() will complete the configuration setup return (XORP_OK); } // // Delete a statically configured RP for all group prefixes // // Return: %XORP_OK on success, otherwise %XORP_ERROR. // // XXX: we don't call end_config(), because config_static_rp_done() will // do it when the RP configuration is completed. // int PimNode::delete_config_all_static_group_prefixes_rp(const IPvX& rp_addr, string& error_msg) { if (start_config(error_msg) != XORP_OK) return (XORP_ERROR); if (rp_table().delete_all_group_prefixes_rp(rp_addr, PimRp::RP_LEARNED_METHOD_STATIC) != XORP_OK) { // XXX: don't call end_config(error_msg); error_msg = c_format("Cannot delete configure static RP with address %s", cstring(rp_addr)); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } // XXX: config_static_rp_done() will complete the configuration setup return (XORP_OK); } // // Delete all statically configured RPs // // Return: %XORP_OK on success, otherwise %XORP_ERROR. // // XXX: we don't call end_config(), because config_static_rp_done() will // do it when the RP configuration is completed. // int PimNode::delete_config_all_static_rps(string& error_msg) { if (start_config(error_msg) != XORP_OK) return (XORP_ERROR); if (rp_table().delete_all_rps(PimRp::RP_LEARNED_METHOD_STATIC) != XORP_OK) { // XXX: don't call end_config(error_msg); error_msg = c_format("Cannot delete configure all static RPs"); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } // XXX: config_static_rp_done() will complete the configuration setup return (XORP_OK); } // // Finish with the static RP configuration. // // Return: %XORP_OK on success, otherwise %XORP_ERROR. // int PimNode::config_static_rp_done(string& error_msg) { rp_table().apply_rp_changes(); if (end_config(error_msg) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } // // Add an alternative subnet on a PIM vif. // // An alternative subnet is used to make incoming traffic with a non-local // source address appear as it is coming from a local subnet. // Note: add alternative subnets with extreme care, only if you know what // you are really doing! // // Return: %XORP_OK on success, otherwise %XORP_ERROR. // int PimNode::add_alternative_subnet(const string& vif_name, const IPvXNet& subnet, string& error_msg) { PimVif *pim_vif = vif_find_by_name(vif_name); if (pim_vif == NULL) { error_msg = c_format("Cannot add alternative subnet to vif %s: " "no such vif", vif_name.c_str()); return (XORP_ERROR); } pim_vif->add_alternative_subnet(subnet); return (XORP_OK); } // // Delete an alternative subnet on a PIM vif. // // Return: %XORP_OK on success, otherwise %XORP_ERROR. // int PimNode::delete_alternative_subnet(const string& vif_name, const IPvXNet& subnet, string& error_msg) { PimVif *pim_vif = vif_find_by_name(vif_name); if (pim_vif == NULL) { error_msg = c_format("Cannot delete alternative subnet from vif %s: " "no such vif", vif_name.c_str()); return (XORP_ERROR); } pim_vif->delete_alternative_subnet(subnet); return (XORP_OK); } // // Remove all alternative subnets on a PIM vif. // // Return: %XORP_OK on success, otherwise %XORP_ERROR. // int PimNode::remove_all_alternative_subnets(const string& vif_name, string& error_msg) { PimVif *pim_vif = vif_find_by_name(vif_name); if (pim_vif == NULL) { error_msg = c_format("Cannot remove all alternative subnets from vif %s: " "no such vif", vif_name.c_str()); return (XORP_ERROR); } pim_vif->remove_all_alternative_subnets(); return (XORP_OK); } // // Add a J/P entry to the _test_jp_headers_list // Return: %XORP_OK on success, otherwise %XORP_ERROR. // int PimNode::add_test_jp_entry(const IPvX& source_addr, const IPvX& group_addr, uint8_t group_mask_len, mrt_entry_type_t mrt_entry_type, action_jp_t action_jp, uint16_t holdtime, bool is_new_group) { int ret_value; if (_test_jp_headers_list.empty() || is_new_group) _test_jp_headers_list.push_back(PimJpHeader(this)); PimJpHeader& pim_jp_header = _test_jp_headers_list.back(); ret_value = pim_jp_header.jp_entry_add(source_addr, group_addr, group_mask_len, mrt_entry_type, action_jp, holdtime, is_new_group); return (ret_value); } // // Send the accumulated state in the _test_jp_headers_list // XXX: the _test_jp_headers_list entries are reset by the sending method // Return: %XORP_OK on success, otherwise %XORP_ERROR. // int PimNode::send_test_jp_entry(const string& vif_name, const IPvX& nbr_addr, string& error_msg) { int ret_value = XORP_OK; PimVif *pim_vif = vif_find_by_name(vif_name); if (pim_vif == NULL) return (XORP_ERROR); list::iterator iter; for (iter = _test_jp_headers_list.begin(); iter != _test_jp_headers_list.end(); ++iter) { PimJpHeader& pim_jp_header = *iter; if (pim_jp_header.network_commit(pim_vif, nbr_addr, error_msg) != XORP_OK) { ret_value = XORP_ERROR; break; } } _test_jp_headers_list.clear(); return (ret_value); } // // Send test Assert message on an interface. // Return: %XORP_OK on success, otherwise %XORP_ERROR. // int PimNode::send_test_assert(const string& vif_name, const IPvX& source_addr, const IPvX& group_addr, bool rpt_bit, uint32_t metric_preference, uint32_t metric, string& error_msg) { PimVif *pim_vif = vif_find_by_name(vif_name); if (pim_vif == NULL) { error_msg = c_format("Cannot send Test-Assert on vif %s: no such vif", vif_name.c_str()); return (XORP_ERROR); } if (pim_vif->pim_assert_send(source_addr, group_addr, rpt_bit, metric_preference, metric, error_msg) != XORP_OK) { return (XORP_ERROR); } return (XORP_OK); } int PimNode::add_test_bsr_zone(const PimScopeZoneId& zone_id, const IPvX& bsr_addr, uint8_t bsr_priority, uint8_t hash_mask_len, uint16_t fragment_tag) { if (pim_bsr().add_test_bsr_zone(zone_id, bsr_addr, bsr_priority, hash_mask_len, fragment_tag) == NULL) { return (XORP_ERROR); } return (XORP_OK); } int PimNode::add_test_bsr_group_prefix(const PimScopeZoneId& zone_id, const IPvXNet& group_prefix, bool is_scope_zone, uint8_t expected_rp_count) { if (pim_bsr().add_test_bsr_group_prefix(zone_id, group_prefix, is_scope_zone, expected_rp_count) == NULL) { return (XORP_ERROR); } return (XORP_OK); } int PimNode::add_test_bsr_rp(const PimScopeZoneId& zone_id, const IPvXNet& group_prefix, const IPvX& rp_addr, uint8_t rp_priority, uint16_t rp_holdtime) { if (pim_bsr().add_test_bsr_rp(zone_id, group_prefix, rp_addr, rp_priority, rp_holdtime) == NULL) { return (XORP_ERROR); } return (XORP_OK); } int PimNode::send_test_bootstrap(const string& vif_name, string& error_msg) { if (pim_bsr().send_test_bootstrap(vif_name, error_msg) != XORP_OK) { return (XORP_ERROR); } return (XORP_OK); } int PimNode::send_test_bootstrap_by_dest(const string& vif_name, const IPvX& dest_addr, string& error_msg) { if (pim_bsr().send_test_bootstrap_by_dest(vif_name, dest_addr, error_msg) != XORP_OK) { return (XORP_ERROR); } return (XORP_OK); } int PimNode::send_test_cand_rp_adv() { if (pim_bsr().send_test_cand_rp_adv() != XORP_OK) { return (XORP_ERROR); } return (XORP_OK); } xorp/pim/pim_mfc.hh0000664000076400007640000001274611635757530014426 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __PIM_PIM_MFC_HH__ #define __PIM_PIM_MFC_HH__ // // PIM Multicast Forwarding Cache definitions. // #include "mrt/mifset.hh" #include "mrt/mrt.hh" class IPvX; class PimMre; class PimMrt; class PimNode; // PIM-specific Multicast Forwarding Cache class PimMfc : public Mre { public: PimMfc(PimMrt* pim_mrt, const IPvX& source, const IPvX& group); virtual ~PimMfc(); // General info: PimNode, PimMrt, family, etc. PimNode* pim_node() const; PimMrt* pim_mrt() const { return _pim_mrt; } int family() const; const IPvX& rp_addr() const { return (_rp_addr); } void set_rp_addr(const IPvX& v); void uncond_set_rp_addr(const IPvX& v); uint32_t iif_vif_index() const { return (_iif_vif_index); } void set_iif_vif_index(uint32_t v) { _iif_vif_index = v; } const Mifset& olist() const { return (_olist); } const Mifset& olist_disable_wrongvif() const { return (_olist_disable_wrongvif); } bool is_set_oif(uint32_t vif_index) const { return (_olist.test(vif_index)); } void set_olist(const Mifset& v) { _olist = v; } void set_olist_disable_wrongvif(const Mifset& v) { _olist_disable_wrongvif = v; } void set_oif(uint32_t vif_index, bool v) { if (v) _olist.set(vif_index); else _olist.reset(vif_index); } void recompute_rp_mfc(); void recompute_iif_olist_mfc(); bool recompute_update_sptbit_mfc(); void recompute_spt_switch_threshold_changed_mfc(); void recompute_monitoring_switch_to_spt_desired_mfc(); void install_spt_switch_dataflow_monitor_mfc(PimMre *pim_mre); void update_mfc(uint32_t new_iif_vif_index, const Mifset& new_olist, const PimMre* pim_mre_sg); int add_mfc_to_kernel(); int delete_mfc_from_kernel(); int add_dataflow_monitor(uint32_t threshold_interval_sec, uint32_t threshold_interval_usec, 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); int delete_dataflow_monitor(uint32_t threshold_interval_sec, uint32_t threshold_interval_usec, 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); int delete_all_dataflow_monitor(); bool entry_try_remove(); bool entry_can_remove() const; void remove_pim_mfc_entry_mfc(); bool is_task_delete_pending() const { return (_flags & PIM_MFC_TASK_DELETE_PENDING); } void set_is_task_delete_pending(bool v) { if (v) _flags |= PIM_MFC_TASK_DELETE_PENDING; else _flags &= ~PIM_MFC_TASK_DELETE_PENDING; } bool is_task_delete_done() const { return (_flags & PIM_MFC_TASK_DELETE_DONE); } void set_is_task_delete_done(bool v) { if (v) _flags |= PIM_MFC_TASK_DELETE_DONE; else _flags &= ~PIM_MFC_TASK_DELETE_DONE; } bool has_idle_dataflow_monitor() const { return (_flags & PIM_MFC_HAS_IDLE_DATAFLOW_MONITOR); } void set_has_idle_dataflow_monitor(bool v) { if (v) _flags |= PIM_MFC_HAS_IDLE_DATAFLOW_MONITOR; else _flags &= ~PIM_MFC_HAS_IDLE_DATAFLOW_MONITOR; } bool has_spt_switch_dataflow_monitor() const { return (_flags & PIM_MFC_HAS_SPT_SWITCH_DATAFLOW_MONITOR); } void set_has_spt_switch_dataflow_monitor(bool v) { if (v) _flags |= PIM_MFC_HAS_SPT_SWITCH_DATAFLOW_MONITOR; else _flags &= ~PIM_MFC_HAS_SPT_SWITCH_DATAFLOW_MONITOR; } bool has_forced_deletion() const { return (_flags & PIM_MFC_HAS_FORCED_DELETION); } void set_has_forced_deletion(bool v) { if (v) _flags |= PIM_MFC_HAS_FORCED_DELETION; else _flags &= ~PIM_MFC_HAS_FORCED_DELETION; } private: PimMrt* _pim_mrt; // The PIM MRT (yuck!) IPvX _rp_addr; // The RP address uint32_t _iif_vif_index; // The incoming interface Mifset _olist; // The outgoing interfaces Mifset _olist_disable_wrongvif;// The outgoing interfaces for which // the WRONGVIF kernel signal is // disabled. // PimMfc _flags enum { PIM_MFC_TASK_DELETE_PENDING = 1 << 0, // Entry is pending deletion PIM_MFC_TASK_DELETE_DONE = 1 << 1, // Entry is ready to be deleted PIM_MFC_HAS_IDLE_DATAFLOW_MONITOR = 1 << 2, // Entry has an idle dataflow monitor PIM_MFC_HAS_SPT_SWITCH_DATAFLOW_MONITOR = 1 << 3, // Entry has a SPT-switch dataflow monitor PIM_MFC_HAS_FORCED_DELETION = 1 << 4 // Entry is forced to be deleted }; uint32_t _flags; // Various flags (see PIM_MFC_* above) }; #endif // __PIM_PIM_MFC_HH__ xorp/pim/pim_proto_join_prune_message.cc0000664000076400007640000010637011613330517020727 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // PIM PIM_JOIN_PRUNE control messages supporting functions. // #include "pim_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvx.hh" #include "libxorp/utils.hh" #include "pim_mre.hh" #include "pim_node.hh" #include "pim_proto.h" #include "pim_proto_join_prune_message.hh" #include "pim_vif.hh" PimJpHeader::PimJpHeader(PimNode* pim_node) : _pim_node(pim_node), _family(pim_node->family()), _jp_groups_n(0), _jp_sources_n(0), _holdtime(PIM_JOIN_PRUNE_HOLDTIME_DEFAULT) // XXX { } #ifdef XORP_USE_USTL PimJpHeader::PimJpHeader() : _pim_node(NULL), _family(AF_INET), _jp_groups_n(0), _jp_sources_n(0), _holdtime(PIM_JOIN_PRUNE_HOLDTIME_DEFAULT) // XXX { } #endif PimJpHeader::~PimJpHeader() { // Delete all 'jp_group' entries delete_pointers_list(_jp_groups_list); } void PimJpHeader::reset() { delete_pointers_list(_jp_groups_list); _jp_groups_n = 0; _jp_sources_n = 0; _holdtime = PIM_JOIN_PRUNE_HOLDTIME_DEFAULT; // XXX } PimMrt& PimJpHeader::pim_mrt() const { return (_pim_node->pim_mrt()); } // Return true if @ipaddr found bool PimJpSources::j_list_found(const IPvX& ipaddr) { list::iterator iter; for (iter = j_list().begin(); iter != j_list().end(); ++iter) { if (ipaddr == *iter) return (true); } return (false); } // Return true if @ipaddr found bool PimJpSources::p_list_found(const IPvX& ipaddr) { list::iterator iter; for (iter = p_list().begin(); iter != p_list().end(); ++iter) { if (ipaddr == *iter) return (true); } return (false); } // Remove entry for @ipaddr, and return true if such entry was removed bool PimJpSources::j_list_remove(const IPvX& ipaddr) { list::iterator iter; for (iter = j_list().begin(); iter != j_list().end(); ++iter) { if (ipaddr == *iter) { j_list().erase(iter); return (true); } } return (false); } // Remove entry for @ipaddr, and return true if such entry was removed bool PimJpSources::p_list_remove(const IPvX& ipaddr) { list::iterator iter; for (iter = p_list().begin(); iter != p_list().end(); ++iter) { if (ipaddr == *iter) { p_list().erase(iter); return (true); } } return (false); } PimJpGroup::PimJpGroup(PimJpHeader& jp_header, int family) : _jp_header(jp_header), _family(family), _group_addr(family) { _group_mask_len = IPvX::addr_bitlen(family); _j_sources_n = 0; _p_sources_n = 0; } // Return: XORP_ERROR if the addition of this entry is inconsistent // XXX: the (*,*,RP) entries are first in the chain. // @is_new_group: if true, create a new PimJpGroup(). int PimJpHeader::jp_entry_add(const IPvX& source_addr, const IPvX& group_addr, uint8_t group_mask_len, mrt_entry_type_t mrt_entry_type, action_jp_t action_jp, uint16_t holdtime, bool is_new_group) { bool is_jp_group_found = false; PimJpGroup *jp_group = NULL; PimJpSources *jp_sources = NULL; if (! is_new_group) { // Allow to merge together all entries for the same group. // Try to find the group entry. list::iterator iter; for (iter = _jp_groups_list.begin(); iter != _jp_groups_list.end(); ++iter) { jp_group = *iter; if ( (group_addr != jp_group->group_addr()) || (group_mask_len != jp_group->group_mask_len())) continue; is_jp_group_found = true; break; } } if ( ! is_jp_group_found) { // Create a new entry jp_group = new PimJpGroup(*this, family()); _jp_groups_list.push_back(jp_group); jp_group->set_group_addr(group_addr); jp_group->set_group_mask_len(group_mask_len); incr_jp_groups_n(); } _holdtime = holdtime; // XXX: the older holdtime may be modified XLOG_ASSERT(jp_group != NULL); // // Perform sanity check for conflicting entries, // and at the same time find the type of entry. // XXX: the "?" entries in the J/P rules table are accepted, but // the redundant entries are removed. // (see the J/P messages format section in the spec). // switch(mrt_entry_type) { case MRT_ENTRY_RP: if (action_jp == ACTION_JOIN) { // (*,*,RP) Join if (jp_group->rp()->j_list_found(source_addr)) return (XORP_OK); // Already added; ignore. if (jp_group->rp()->p_list_found(source_addr)) return (XORP_ERROR); // Combination not allowed } else { // (*,*,RP) Prune if (jp_group->rp()->j_list_found(source_addr)) return (XORP_ERROR); // Combination not allowed if (jp_group->rp()->p_list_found(source_addr)) return (XORP_OK); // Already added; ignore. } jp_sources = jp_group->rp(); break; case MRT_ENTRY_WC: if (action_jp == ACTION_JOIN) { // (*,G) Join if (jp_group->wc()->j_list_found(source_addr)) return (XORP_OK); // Already added; ignore. if (jp_group->wc()->p_list_found(source_addr)) return (XORP_ERROR); // Combination not allowed // Remove redundant entries: all (S,G,rpt)J while (! jp_group->sg_rpt()->j_list().empty()) { const IPvX& addr = *jp_group->sg_rpt()->j_list().begin(); jp_group->sg_rpt()->j_list_remove(addr); } } else { // (*,G) Prune if (jp_group->wc()->j_list_found(source_addr)) return (XORP_ERROR); // Combination not allowed if (jp_group->wc()->p_list_found(source_addr)) return (XORP_OK); // Already added; ignore. // Remove redundant entries: all (S,G,rpt)J while (! jp_group->sg_rpt()->j_list().empty()) { const IPvX& addr = *jp_group->sg_rpt()->j_list().begin(); jp_group->sg_rpt()->j_list_remove(addr); } // Remove redundant entries: all (S,G,rpt)P while (! jp_group->sg_rpt()->p_list().empty()) { const IPvX& addr = *jp_group->sg_rpt()->p_list().begin(); jp_group->sg_rpt()->p_list_remove(addr); } } jp_sources = jp_group->wc(); break; case MRT_ENTRY_SG_RPT: if (action_jp == ACTION_JOIN) { if (! jp_group->wc()->j_list().empty()) return (XORP_OK); // Redundant; ignore. if (! jp_group->wc()->p_list().empty()) return (XORP_OK); // Redundant; ignore. // (S,G,rpt) Join if (jp_group->sg_rpt()->j_list_found(source_addr)) return (XORP_OK); // Already added; ignore. if (jp_group->sg_rpt()->p_list_found(source_addr)) return (XORP_ERROR); // Combination not allowed if (jp_group->sg()->p_list_found(source_addr)) return (XORP_OK); // Redundant; ignore. } else { // (S,G,rpt) Prune if (! jp_group->wc()->p_list().empty()) return (XORP_OK); // Redundant; ignore. if (jp_group->sg_rpt()->j_list_found(source_addr)) return (XORP_ERROR); // Combination not allowed if (jp_group->sg_rpt()->p_list_found(source_addr)) return (XORP_OK); // Already added; ignore. if (jp_group->sg()->j_list_found(source_addr)) return (XORP_OK); // Redundant; ignore. } jp_sources = jp_group->sg_rpt(); break; case MRT_ENTRY_SG: if (action_jp == ACTION_JOIN) { // (S,G) Join if (jp_group->sg()->j_list_found(source_addr)) return (XORP_OK); // Already added; ignore. if (jp_group->sg()->p_list_found(source_addr)) return (XORP_ERROR); // Combination not allowed // Remove redundant entries: (S,G,rpt)P jp_group->sg_rpt()->p_list_remove(source_addr); } else { // (S,G) Prune if (jp_group->sg()->j_list_found(source_addr)) return (XORP_ERROR); // Combination not allowed if (jp_group->sg()->p_list_found(source_addr)) return (XORP_OK); // Already added; ignore. // Remove redundant entries: (S,G,rpt)J jp_group->sg_rpt()->j_list_remove(source_addr); } jp_sources = jp_group->sg(); break; default: XLOG_UNREACHABLE(); return (XORP_ERROR); } XLOG_ASSERT(jp_sources != NULL); // Add the new entry and record the fact. if (action_jp == ACTION_JOIN) { jp_sources->j_list().push_back(source_addr); jp_sources->incr_j_n(); jp_group->incr_j_sources_n(); } else { jp_sources->p_list().push_back(source_addr); jp_sources->incr_p_n(); jp_group->incr_p_sources_n(); } return (XORP_OK); } int PimJpHeader::mrt_commit(PimVif *pim_vif, const IPvX& target_nbr_addr) { bool i_am_target_router = true; uint32_t lookup_flags = 0, create_flags = 0; uint32_t vif_index; uint16_t holdtime; uint8_t group_mask_len; IPvX source_addr(family()), group_addr(family()); list::iterator iter; PimMre *pim_mre; map groups_map, join_wc_map; vif_index = pim_vif->vif_index(); // // Test if I am the target router; e.g., in case I need to // perform Join/Prune suppression. // XXX: on p2p interfaces, target_nbr_addr of all zeros is also accepted if ((target_nbr_addr == pim_vif->primary_addr()) || (pim_vif->is_p2p() && target_nbr_addr == IPvX::ZERO(family()))) { i_am_target_router = true; } else { i_am_target_router = false; } // // Create the map with all group addresses // if (i_am_target_router) { for (iter = _jp_groups_list.begin(); iter != _jp_groups_list.end(); ++iter) { PimJpGroup *jp_group = *iter; group_addr = jp_group->group_addr(); group_mask_len = jp_group->group_mask_len(); if (group_mask_len != group_addr.addr_bitlen()) continue; // XXX: exclude (e.g., probably (*,*,RP) entry) groups_map.insert(pair(group_addr, group_addr)); } } // // The main loop // for (iter = _jp_groups_list.begin(); iter != _jp_groups_list.end(); ++iter) { list::iterator iter2; PimJpGroup *jp_group = *iter; group_addr = jp_group->group_addr(); group_mask_len = jp_group->group_mask_len(); holdtime = _holdtime; // // Build the map for all (*,G) Joins so far // if (i_am_target_router) { if (! jp_group->wc()->j_list().empty()) join_wc_map.insert(pair(group_addr, group_addr)); } // (*,*,RP) Join for (iter2 = jp_group->rp()->j_list().begin(); iter2 != jp_group->rp()->j_list().end(); ++iter2) { source_addr = *iter2; if (i_am_target_router) pim_mrt().add_task_receive_join_rp(vif_index, source_addr); lookup_flags = PIM_MRE_RP; if (i_am_target_router) create_flags = lookup_flags; else create_flags = 0; pim_mre = pim_mrt().pim_mre_find(source_addr, IPvX::ZERO(family()), lookup_flags, create_flags); if (pim_mre == NULL) { if (create_flags) goto pim_mre_find_error; } else { if (i_am_target_router) { pim_mre->receive_join_rp(vif_index, holdtime); } else { pim_mre->rp_see_join_rp(vif_index, holdtime, target_nbr_addr); } } } // (*,*,RP) Prune for (iter2 = jp_group->rp()->p_list().begin(); iter2 != jp_group->rp()->p_list().end(); ++iter2) { source_addr = *iter2; if (i_am_target_router) pim_mrt().add_task_receive_prune_rp(vif_index, source_addr); lookup_flags = PIM_MRE_RP; // XXX: if no entry, the (*,*,RP) Prune should not create one create_flags = 0; pim_mre = pim_mrt().pim_mre_find(source_addr, IPvX::ZERO(family()), lookup_flags, create_flags); if (pim_mre == NULL) { if (create_flags) goto pim_mre_find_error; } else { if (i_am_target_router) { pim_mre->receive_prune_rp(vif_index, holdtime); } else { pim_mre->rp_see_prune_rp(vif_index, holdtime, target_nbr_addr); } } } // (*,G) Join for (iter2 = jp_group->wc()->j_list().begin(); iter2 != jp_group->wc()->j_list().end(); ++iter2) { source_addr = *iter2; if (i_am_target_router) pim_mrt().add_task_receive_join_wc(vif_index, group_addr); lookup_flags = PIM_MRE_WC; if (i_am_target_router) create_flags = lookup_flags; else create_flags = 0; pim_mre = pim_mrt().pim_mre_find(source_addr, group_addr, lookup_flags, create_flags); if (pim_mre == NULL) { if (create_flags) goto pim_mre_find_error; } else { if (i_am_target_router) { pim_mre->receive_join_wc(vif_index, holdtime); } else { pim_mre->wc_see_join_wc(vif_index, holdtime, target_nbr_addr); } } } // (*,G) Prune for (iter2 = jp_group->wc()->p_list().begin(); iter2 != jp_group->wc()->p_list().end(); ++iter2) { source_addr = *iter2; if (i_am_target_router) { pim_mrt().add_task_receive_prune_wc(vif_index, group_addr); } else { pim_mrt().add_task_see_prune_wc(vif_index, group_addr, target_nbr_addr); } lookup_flags = PIM_MRE_WC; // XXX: if no entry, the (*,G) Prune should not create one create_flags = 0; pim_mre = pim_mrt().pim_mre_find(source_addr, group_addr, lookup_flags, create_flags); if (pim_mre == NULL) { if (create_flags) goto pim_mre_find_error; } else { if (i_am_target_router) { pim_mre->receive_prune_wc(vif_index, holdtime); } else { pim_mre->wc_see_prune_wc(vif_index, holdtime, target_nbr_addr); } } } // (S,G,rpt) Join for (iter2 = jp_group->sg_rpt()->j_list().begin(); iter2 != jp_group->sg_rpt()->j_list().end(); ++iter2) { source_addr = *iter2; if (i_am_target_router) { pim_mrt().add_task_receive_join_sg_rpt(vif_index, source_addr, group_addr); } lookup_flags = PIM_MRE_SG_RPT; // XXX: if no entry, the (S,G,rpt) Join should not create one create_flags = 0; pim_mre = pim_mrt().pim_mre_find(source_addr, group_addr, lookup_flags, create_flags); if (pim_mre == NULL) { if (create_flags) goto pim_mre_find_error; } else { if (i_am_target_router) { pim_mre->receive_join_sg_rpt(vif_index, holdtime); } else { pim_mre->sg_rpt_see_join_sg_rpt(vif_index, holdtime, target_nbr_addr); } } } // (S,G,rpt) Prune for (iter2 = jp_group->sg_rpt()->p_list().begin(); iter2 != jp_group->sg_rpt()->p_list().end(); ++iter2) { source_addr = *iter2; if (i_am_target_router) { pim_mrt().add_task_receive_prune_sg_rpt(vif_index, source_addr, group_addr); } lookup_flags = PIM_MRE_SG_RPT; // XXX: even if no entry, the (S,G,rpt) Prune should create one // XXX: even if the (S,G,rpt) Prune is not for me, try to create // an entry. if (i_am_target_router) create_flags = lookup_flags; else create_flags = lookup_flags; pim_mre = pim_mrt().pim_mre_find(source_addr, group_addr, lookup_flags, create_flags); if (pim_mre == NULL) { if (create_flags) goto pim_mre_find_error; } else { bool is_join_wc_received; if (jp_group->wc()->j_list().empty()) is_join_wc_received = false; else is_join_wc_received = true; if (! is_join_wc_received) { is_join_wc_received = (join_wc_map.find(group_addr) != join_wc_map.end()); } if (i_am_target_router) { pim_mre->receive_prune_sg_rpt(vif_index, holdtime, is_join_wc_received); } else { pim_mre->sg_rpt_see_prune_sg_rpt(vif_index, holdtime, target_nbr_addr); pim_mre->entry_try_remove(); } } // // Take care of the (S,G) entry that // "See Prune (S,G,rpt) to RPF'(S,G)" // if (! i_am_target_router) { PimMre *pim_mre_sg = NULL; if (pim_mre != NULL) { pim_mre_sg = pim_mre->sg_entry(); } else { pim_mre_sg = pim_mrt().pim_mre_find(source_addr, group_addr, PIM_MRE_SG, 0); } if (pim_mre_sg != NULL) pim_mre_sg->sg_see_prune_sg_rpt(vif_index, holdtime, target_nbr_addr); } } // (S,G) Join for (iter2 = jp_group->sg()->j_list().begin(); iter2 != jp_group->sg()->j_list().end(); ++iter2) { source_addr = *iter2; if (i_am_target_router) { pim_mrt().add_task_receive_join_sg(vif_index, source_addr, group_addr); } lookup_flags = PIM_MRE_SG; if (i_am_target_router) create_flags = lookup_flags; else create_flags = 0; pim_mre = pim_mrt().pim_mre_find(source_addr, group_addr, lookup_flags, create_flags); if (pim_mre == NULL) { if (create_flags) goto pim_mre_find_error; } else { if (i_am_target_router) { pim_mre->receive_join_sg(vif_index, holdtime); } else { pim_mre->sg_see_join_sg(vif_index, holdtime, target_nbr_addr); } } } // (S,G) Prune for (iter2 = jp_group->sg()->p_list().begin(); iter2 != jp_group->sg()->p_list().end(); ++iter2) { source_addr = *iter2; if (i_am_target_router) { pim_mrt().add_task_receive_prune_sg(vif_index, source_addr, group_addr); } lookup_flags = PIM_MRE_SG; // XXX: if no entry, the (S,G) Prune should not create one create_flags = 0; pim_mre = pim_mrt().pim_mre_find(source_addr, group_addr, lookup_flags, create_flags); if (pim_mre == NULL) { if (create_flags) goto pim_mre_find_error; } else { if (i_am_target_router) { pim_mre->receive_prune_sg(vif_index, holdtime); } else { pim_mre->sg_see_prune_sg(vif_index, holdtime, target_nbr_addr); } } // // Take care of the (S,G,rpt) entry that // "See Prune (S,G) to RPF'(S,G,rpt)" // if (! i_am_target_router) { PimMre *pim_mre_sg_rpt = NULL; if (pim_mre != NULL) pim_mre_sg_rpt = pim_mre->sg_rpt_entry(); if (pim_mre_sg_rpt == NULL) { // XXX: always create the (S,G,rpt) entry even // if the (S,G) Prune is not for me lookup_flags = PIM_MRE_SG_RPT; create_flags = lookup_flags; pim_mre_sg_rpt = pim_mrt().pim_mre_find(source_addr, group_addr, lookup_flags, create_flags); if (pim_mre_sg_rpt == NULL) { if (create_flags) goto pim_mre_find_error; } } if (pim_mre_sg_rpt != NULL) { pim_mre_sg_rpt->sg_rpt_see_prune_sg(vif_index, holdtime, target_nbr_addr); pim_mre_sg_rpt->entry_try_remove(); } } } } // // Take care of the (S,G,rpt) entries that see // "End of Message" // if (i_am_target_router) { map::iterator map_iter; for (map_iter = groups_map.begin(); map_iter != groups_map.end(); ++map_iter) { group_addr = map_iter->second; pim_mrt().add_task_receive_end_of_message_sg_rpt(vif_index, group_addr); } } return (XORP_OK); pim_mre_find_error: XLOG_UNREACHABLE(); XLOG_ERROR("INTERNAL PimMrt ERROR: " "cannot create entry for (%s, %s) create_flags = %#x", cstring(source_addr), cstring(group_addr), XORP_UINT_CAST(create_flags)); return (XORP_ERROR); } int PimJpHeader::network_commit(PimVif *pim_vif, const IPvX& target_nbr_addr, string& error_msg) { const size_t max_packet_size = PIM_MAXPACKET(family()); IPvX source_addr(family()); PimJpHeader jp_header(pim_node()); list::iterator iter; // // Add first all (S,G,rpt) entries that need to be included // with the (*,G) Join messages. // for (iter = _jp_groups_list.begin(); iter != _jp_groups_list.end(); ++iter) { list::iterator iter2; PimJpGroup *jp_group = *iter; // (*,G) Join for (iter2 = jp_group->wc()->j_list().begin(); iter2 != jp_group->wc()->j_list().end(); ++iter2) { PimMre *pim_mre_wc = pim_mrt().pim_mre_find(IPvX::ZERO(family()), jp_group->group_addr(), PIM_MRE_WC, 0); if (pim_mre_wc == NULL) continue; // // Add all necessary (S,G,rpt) entries for this group // First we go through all (S,G) entries, and then through // the (S,G,rpt) entries. // // // Go through the (S,G) entries and add (S,G,rpt) Prune // if needed // PimMrtSg::const_gs_iterator iter3_begin, iter3_end, iter3; iter3_begin = pim_mrt().pim_mrt_sg().group_by_addr_begin(pim_mre_wc->group_addr()); iter3_end = pim_mrt().pim_mrt_sg().group_by_addr_end(pim_mre_wc->group_addr()); for (iter3 = iter3_begin; iter3 != iter3_end; ++iter3) { PimMre *pim_mre_sg = iter3->second; if (pim_mre_sg->is_spt()) { // Note: If receiving (S,G) on the SPT, we only prune off // the shared tree if the RPF neighbors differ, i.e. // if ( RPF'(*,G) != RPF'(S,G) ) if (pim_mre_wc->rpfp_nbr_wc() != pim_mre_sg->rpfp_nbr_sg()) goto add_prune_sg_rpt_label1; } continue; add_prune_sg_rpt_label1: bool is_new_group = false; jp_entry_add(pim_mre_sg->source_addr(), pim_mre_sg->group_addr(), IPvX::addr_bitlen(family()), MRT_ENTRY_SG_RPT, ACTION_PRUNE, _holdtime, is_new_group); } // // Go through the (S,G,rpt) entries and add (S,G,rpt) Prune // if needed. // iter3_begin = pim_mrt().pim_mrt_sg_rpt().group_by_addr_begin(pim_mre_wc->group_addr()); iter3_end = pim_mrt().pim_mrt_sg_rpt().group_by_addr_end(pim_mre_wc->group_addr()); for (iter3 = iter3_begin; iter3 != iter3_end; ++iter3) { PimMre *pim_mre_sg_rpt = iter3->second; if (pim_mre_sg_rpt->inherited_olist_sg_rpt().none()) { // Note: all (*,G) olist interfaces received RPT prunes // for (S,G). goto add_prune_sg_rpt_label2; } else if (pim_mre_wc->rpfp_nbr_wc() != pim_mre_sg_rpt->rpfp_nbr_sg_rpt()) { // Note: we joined the shared tree, but there was // an (S,G) assert and the source tree RPF neighbor // is different. goto add_prune_sg_rpt_label2; } continue; add_prune_sg_rpt_label2: do { // // XXX: check if already we added the entry because // of an (S,G) entry. // PimMre *pim_mre_sg = pim_mre_sg_rpt->sg_entry(); if ((pim_mre_sg != NULL) && (pim_mre_sg->is_spt())) { // Note: If receiving (S,G) on the SPT, we only prune // off the shared tree if the RPF neighbors differ, // i.e. if ( RPF'(*,G) != RPF'(S,G) ) if (pim_mre_wc->rpfp_nbr_wc() != pim_mre_sg->rpfp_nbr_sg()) continue; // XXX: already added } } while (false); bool is_new_group = false; jp_entry_add(pim_mre_sg_rpt->source_addr(), pim_mre_sg_rpt->group_addr(), IPvX::addr_bitlen(family()), MRT_ENTRY_SG_RPT, ACTION_PRUNE, _holdtime, is_new_group); } } } // // Create the output message(s) and send them one-by-one. // for (iter = _jp_groups_list.begin(); iter != _jp_groups_list.end(); ++iter) { list::iterator iter2; PimJpGroup *jp_group = *iter; // // Check if the group number is too large // if (jp_header.jp_groups_n() == 0xff) { // Send what we have already if (jp_header.network_send(pim_vif, target_nbr_addr, error_msg) != XORP_OK) { return (XORP_ERROR); } jp_header.reset(); } // // Check the result message size if we add this group // if (jp_header.message_size() + jp_group->message_size() > max_packet_size) { if (jp_header.jp_groups_n() > 0) { // Send what we have already if (jp_header.network_send(pim_vif, target_nbr_addr, error_msg) != XORP_OK) { return (XORP_ERROR); } jp_header.reset(); } } // // Start adding the Join/Prune entries for the group // size_t j_sources_n = 0; // Number of join sources per group size_t p_sources_n = 0; // Number of prune sources per group // // Add as many (*,*,RP) Join/Prune sources (i.e., the RPs) as we can // // (*,*,RP) Join for (iter2 = jp_group->rp()->j_list().begin(); iter2 != jp_group->rp()->j_list().end(); ++iter2) { if ((j_sources_n == 0xffff) || (jp_header.message_size() + jp_header.extra_source_size() > max_packet_size)) { // Send what we have already if (jp_header.network_send(pim_vif, target_nbr_addr, error_msg) != XORP_OK) { return (XORP_ERROR); } jp_header.reset(); j_sources_n = 0; p_sources_n = 0; } j_sources_n++; source_addr = *iter2; jp_header.jp_entry_add(source_addr, jp_group->group_addr(), jp_group->group_mask_len(), MRT_ENTRY_RP, ACTION_JOIN, _holdtime, false); } // (*,*,RP) Prune for (iter2 = jp_group->rp()->p_list().begin(); iter2 != jp_group->rp()->p_list().end(); ++iter2) { if ((p_sources_n == 0xffff) || (jp_header.message_size() + jp_header.extra_source_size() > max_packet_size)) { // Send what we have already if (jp_header.network_send(pim_vif, target_nbr_addr, error_msg) != XORP_OK) { return (XORP_ERROR); } jp_header.reset(); j_sources_n = 0; p_sources_n = 0; } p_sources_n++; source_addr = *iter2; jp_header.jp_entry_add(source_addr, jp_group->group_addr(), jp_group->group_mask_len(), MRT_ENTRY_RP, ACTION_PRUNE, _holdtime, false); } // // Add as many (*,G) Join/Prune sources (i.e., the RPs) as we can // // (*,G) Join for (iter2 = jp_group->wc()->j_list().begin(); iter2 != jp_group->wc()->j_list().end(); ++iter2) { if ((j_sources_n == 0xffff) || (jp_header.message_size() + jp_header.extra_source_size() > max_packet_size)) { // Send what we have already if (jp_header.network_send(pim_vif, target_nbr_addr, error_msg) != XORP_OK) { return (XORP_ERROR); } jp_header.reset(); j_sources_n = 0; p_sources_n = 0; } j_sources_n++; source_addr = *iter2; jp_header.jp_entry_add(source_addr, jp_group->group_addr(), jp_group->group_mask_len(), MRT_ENTRY_WC, ACTION_JOIN, _holdtime, false); } // (*,G) Prune for (iter2 = jp_group->wc()->p_list().begin(); iter2 != jp_group->wc()->p_list().end(); ++iter2) { if ((p_sources_n == 0xffff) || (jp_header.message_size() + jp_header.extra_source_size() > max_packet_size)) { // Send what we have already if (jp_header.network_send(pim_vif, target_nbr_addr, error_msg) != XORP_OK) { return (XORP_ERROR); } jp_header.reset(); j_sources_n = 0; p_sources_n = 0; } p_sources_n++; source_addr = *iter2; jp_header.jp_entry_add(source_addr, jp_group->group_addr(), jp_group->group_mask_len(), MRT_ENTRY_WC, ACTION_PRUNE, _holdtime, false); } // // Add as many (S,G,rpt) Join/Prune sources as we can // // (S,G,rpt) Prune // XXX: if we are sending (*,G) Join, and if we can fit only N // (S,G,rpt) Prune entries, then we MUST choose to include // the first N (numerically smallest) addresses. // Hence, first we order the (S,G,rpt) addresses, and then we add them. map addrs_map; map::iterator map_iter; // Order the addresses for (iter2 = jp_group->sg_rpt()->p_list().begin(); iter2 != jp_group->sg_rpt()->p_list().end(); ++iter2) { source_addr = *iter2; addrs_map.insert(pair(source_addr, source_addr)); } // Add as many addresses as we can for (map_iter = addrs_map.begin(); map_iter != addrs_map.end(); ++map_iter) { if ((p_sources_n == 0xffff) || (jp_header.message_size() + jp_header.extra_source_size() > max_packet_size)) { // Send what we have already if (jp_header.network_send(pim_vif, target_nbr_addr, error_msg) != XORP_OK) { return (XORP_ERROR); } jp_header.reset(); j_sources_n = 0; p_sources_n = 0; // If we have (*,G) Join entry, do not add the rest of the // (S,G,rpt) Prune entries. if (! jp_group->wc()->j_list().empty()) break; } p_sources_n++; source_addr = map_iter->second; jp_header.jp_entry_add(source_addr, jp_group->group_addr(), jp_group->group_mask_len(), MRT_ENTRY_SG_RPT, ACTION_PRUNE, _holdtime, false); } // (S,G,rpt) Join for (iter2 = jp_group->sg_rpt()->j_list().begin(); iter2 != jp_group->sg_rpt()->j_list().end(); ++iter2) { if ((j_sources_n == 0xffff) || (jp_header.message_size() + jp_header.extra_source_size() > max_packet_size)) { // Send what we have already if (jp_header.network_send(pim_vif, target_nbr_addr, error_msg) != XORP_OK) { return (XORP_ERROR); } jp_header.reset(); j_sources_n = 0; p_sources_n = 0; } j_sources_n++; source_addr = *iter2; jp_header.jp_entry_add(source_addr, jp_group->group_addr(), jp_group->group_mask_len(), MRT_ENTRY_SG_RPT, ACTION_JOIN, _holdtime, false); } // // Add as many (S,G) Join/Prune sources as we can // // (S,G) Join for (iter2 = jp_group->sg()->j_list().begin(); iter2 != jp_group->sg()->j_list().end(); ++iter2) { if ((j_sources_n == 0xffff) || (jp_header.message_size() + jp_header.extra_source_size() > max_packet_size)) { // Send what we have already if (jp_header.network_send(pim_vif, target_nbr_addr, error_msg) != XORP_OK) { return (XORP_ERROR); } jp_header.reset(); j_sources_n = 0; p_sources_n = 0; } j_sources_n++; source_addr = *iter2; jp_header.jp_entry_add(source_addr, jp_group->group_addr(), jp_group->group_mask_len(), MRT_ENTRY_SG, ACTION_JOIN, _holdtime, false); } // (S,G) Prune for (iter2 = jp_group->sg()->p_list().begin(); iter2 != jp_group->sg()->p_list().end(); ++iter2) { if ((p_sources_n == 0xffff) || (jp_header.message_size() + jp_header.extra_source_size() > max_packet_size)) { // Send what we have already if (jp_header.network_send(pim_vif, target_nbr_addr, error_msg) != XORP_OK) { return (XORP_ERROR); } jp_header.reset(); j_sources_n = 0; p_sources_n = 0; } p_sources_n++; source_addr = *iter2; jp_header.jp_entry_add(source_addr, jp_group->group_addr(), jp_group->group_mask_len(), MRT_ENTRY_SG, ACTION_PRUNE, _holdtime, false); } } // Sent the last fragment (if such) if (jp_header.jp_groups_n() > 0) { if (jp_header.network_send(pim_vif, target_nbr_addr, error_msg) != XORP_OK) { return (XORP_ERROR); } jp_header.reset(); } return (XORP_OK); } int PimJpHeader::network_send(PimVif *pim_vif, const IPvX& target_nbr_addr, string& error_msg) { uint32_t flags; uint8_t source_mask_len; IPvX source_addr(family()); list::iterator iter; uint8_t sparse_bit = pim_node()->proto_is_pimsm() ? ESADDR_S_BIT : 0; buffer_t *buffer = NULL; // // Prepare a new buffer // buffer = pim_vif->buffer_send_prepare(); PUT_ENCODED_UNICAST_ADDR(family(), target_nbr_addr, buffer); BUFFER_PUT_OCTET(0, buffer); // Reserved BUFFER_PUT_OCTET(_jp_groups_n, buffer); // Number of groups BUFFER_PUT_HOST_16(_holdtime, buffer); // Holdtime // // Prepare the message // for (iter = _jp_groups_list.begin(); iter != _jp_groups_list.end(); ++iter) { list::iterator iter2; PimJpGroup *jp_group = *iter; uint8_t group_addr_reserved_flags = 0; PUT_ENCODED_GROUP_ADDR(family(), jp_group->group_addr(), jp_group->group_mask_len(), group_addr_reserved_flags, buffer); // The number of joined sources BUFFER_PUT_HOST_16(jp_group->j_sources_n(), buffer); // The number of pruned sources BUFFER_PUT_HOST_16(jp_group->p_sources_n(), buffer); // (*,*,RP) Join for (iter2 = jp_group->rp()->j_list().begin(); iter2 != jp_group->rp()->j_list().end(); ++iter2) { source_addr = *iter2; source_mask_len = IPvX::addr_bitlen(family()); flags = ESADDR_RPT_BIT | ESADDR_WC_BIT | sparse_bit; PUT_ENCODED_SOURCE_ADDR(family(), source_addr, source_mask_len, flags, buffer); } // (*,*,RP) Prune for (iter2 = jp_group->rp()->p_list().begin(); iter2 != jp_group->rp()->p_list().end(); ++iter2) { source_addr = *iter2; source_mask_len = IPvX::addr_bitlen(family()); flags = ESADDR_RPT_BIT | ESADDR_WC_BIT | sparse_bit; PUT_ENCODED_SOURCE_ADDR(family(), source_addr, source_mask_len, flags, buffer); } // (*,G) Join for (iter2 = jp_group->wc()->j_list().begin(); iter2 != jp_group->wc()->j_list().end(); ++iter2) { source_addr = *iter2; source_mask_len = IPvX::addr_bitlen(family()); flags = ESADDR_RPT_BIT | ESADDR_WC_BIT | sparse_bit; PUT_ENCODED_SOURCE_ADDR(family(), source_addr, source_mask_len, flags, buffer); } // (S,G,rpt) Join for (iter2 = jp_group->sg_rpt()->j_list().begin(); iter2 != jp_group->sg_rpt()->j_list().end(); ++iter2) { source_addr = *iter2; source_mask_len = IPvX::addr_bitlen(family()); flags = ESADDR_RPT_BIT | sparse_bit; PUT_ENCODED_SOURCE_ADDR(family(), source_addr, source_mask_len, flags, buffer); } // (S,G) Join for (iter2 = jp_group->sg()->j_list().begin(); iter2 != jp_group->sg()->j_list().end(); ++iter2) { source_addr = *iter2; source_mask_len = IPvX::addr_bitlen(family()); flags = sparse_bit; PUT_ENCODED_SOURCE_ADDR(family(), source_addr, source_mask_len, flags, buffer); } // (*,G) Prune for (iter2 = jp_group->wc()->p_list().begin(); iter2 != jp_group->wc()->p_list().end(); ++iter2) { source_addr = *iter2; source_mask_len = IPvX::addr_bitlen(family()); flags = ESADDR_RPT_BIT | ESADDR_WC_BIT | sparse_bit; PUT_ENCODED_SOURCE_ADDR(family(), source_addr, source_mask_len, flags, buffer); } // (S,G,rpt) Prune for (iter2 = jp_group->sg_rpt()->p_list().begin(); iter2 != jp_group->sg_rpt()->p_list().end(); ++iter2) { source_addr = *iter2; source_mask_len = IPvX::addr_bitlen(family()); flags = ESADDR_RPT_BIT | sparse_bit; PUT_ENCODED_SOURCE_ADDR(family(), source_addr, source_mask_len, flags, buffer); } // (S,G) Prune for (iter2 = jp_group->sg()->p_list().begin(); iter2 != jp_group->sg()->p_list().end(); ++iter2) { source_addr = *iter2; source_mask_len = IPvX::addr_bitlen(family()); flags = sparse_bit; PUT_ENCODED_SOURCE_ADDR(family(), source_addr, source_mask_len, flags, buffer); } } // // Send the message // if (pim_vif->pim_send(pim_vif->primary_addr(), IPvX::PIM_ROUTERS(family()), PIM_JOIN_PRUNE, buffer, error_msg) != XORP_OK) { return (XORP_ERROR); } return (XORP_OK); invalid_addr_family_error: XLOG_UNREACHABLE(); error_msg = c_format("INTERNAL %s ERROR: " "invalid address family error = %d", PIMTYPE2ASCII(PIM_JOIN_PRUNE), family()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); buflen_error: XLOG_UNREACHABLE(); error_msg = c_format("INTERNAL %s ERROR: " "packet cannot fit into sending buffer", PIMTYPE2ASCII(PIM_JOIN_PRUNE)); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } xorp/pim/pim_proto.h0000664000076400007640000003242011635757530014643 0ustar greearbgreearb/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- */ /* * Copyright (c) 2001-2011 XORP, Inc and Others * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 2, June * 1991 as published by the Free Software Foundation. Redistribution * and/or modification of this program under the terms of any other * version of the GNU General Public License is not permitted. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, * see the GNU General Public License, Version 2, a copy of which can be * found in the XORP LICENSE.gpl file. * * XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; * http://xorp.net */ #ifndef __PIM_PIM_PROTO_H__ #define __PIM_PIM_PROTO_H__ /* * Protocol Independent Multicast protocol-specific definitions * (both for PIM-SMv2 and PIM-DMv2, as per * draft-ietf-pim-sm-v2-new-11.txt and draft-ietf-pim-dm-new-v2-03.txt) */ /* XXX: _PIM_VT is needed if we want the extra features of */ #define _PIM_VT 1 #ifdef HAVE_NETINET_IN_H #include #endif #ifdef HAVE_NETINET_IN_SYSTM_H #include #endif #ifdef HAVE_NETINET_IP_H #include #endif #ifdef HAVE_NETINET_IP6_H #include #endif // XXX: _PIM_VT is needed if we want the extra features of #define _PIM_VT 1 #if defined(HAVE_NETINET_PIM_H) && defined(HAVE_STRUCT_PIM_PIM_VT) #include #else #include "mrt/include/netinet/pim.h" #endif /* * Conditionally define constants that may be missing from */ #ifndef PIM_MINLEN #define PIM_MINLEN 8 /* PIM message min. length */ #endif #ifndef PIM_REG_MINLEN #define PIM_REG_MINLEN (PIM_MINLEN+20) /* PIM Register hdr + inner IPv4 hdr */ #endif #ifndef PIM6_REG_MINLEN #define PIM6_REG_MINLEN (PIM_MINLEN+40) /* PIM Register hdr + inner IPv6 hdr */ #endif /* * Constants definitions */ #ifndef IPPROTO_PIM #define IPPROTO_PIM 103 #endif /* PIM versions definition */ #define PIMSM_V1 1 #define PIMSM_V2 2 #define PIMSM_VERSION_MIN PIMSM_V2 #define PIMSM_VERSION_MAX PIMSM_V2 #define PIMSM_VERSION_DEFAULT PIMSM_V2 #define PIMDM_V1 1 #define PIMDM_V2 2 #define PIMDM_VERSION_MIN PIMDM_V2 #define PIMDM_VERSION_MAX PIMDM_V2 #define PIMDM_VERSION_DEFAULT PIMDM_V2 #define PIM_V1 (proto_is_pimsm() ? \ PIMSM_V1 \ : PIMDM_V1) #define PIM_V2 (proto_is_pimsm() ? \ PIMSM_V2 \ : PIMDM_V2) #define PIM_VERSION_MIN (proto_is_pimsm() ? \ PIMSM_VERSION_MIN \ : PIMDM_VERSION_MIN) #define PIM_VERSION_MAX (proto_is_pimsm() ? \ PIMSM_VERSION_MAX \ : PIMDM_VERSION_MAX) #define PIM_VERSION_DEFAULT (proto_is_pimsm() ? \ PIMSM_VERSION_DEFAULT \ : PIMDM_VERSION_DEFAULT) /* * Protocol messages specific definitions. * XXX: all intervals are in seconds. */ /* PIM_HELLO-related definitions */ #define PIM_HELLO_HOLDTIME_OPTION 1 #define PIM_HELLO_HOLDTIME_LENGTH 2 #define PIM_HELLO_HOLDTIME_FOREVER 0xffff #define PIM_HELLO_LAN_PRUNE_DELAY_OPTION 2 #define PIM_HELLO_LAN_PRUNE_DELAY_LENGTH 4 #define PIM_HELLO_LAN_PRUNE_DELAY_TBIT ((uint16_t)(1 << 15)) #define PIM_HELLO_DR_PRIORITY_OPTION 19 #define PIM_HELLO_DR_PRIORITY_LENGTH 4 #define PIM_HELLO_DR_PRIORITY_DEFAULT 1 #define PIM_HELLO_DR_PRIORITY_LOWEST 0 #define PIM_HELLO_DR_PRIORITY_HIGHEST 0xffffffffU #define PIM_HELLO_GENID_OPTION 20 #define PIM_HELLO_GENID_LENGTH 4 #define PIM_HELLO_ADDRESS_LIST_OPTION 24 #define PIM_HELLO_HELLO_TRIGGERED_DELAY_DEFAULT 5 #define PIM_HELLO_HELLO_PERIOD_DEFAULT 30 #define PIM_HELLO_HELLO_HOLDTIME_PERIOD_RATIO 3.5 #define PIM_HELLO_HELLO_HOLDTIME_DEFAULT ((int)(PIM_HELLO_HELLO_HOLDTIME_PERIOD_RATIO * PIM_HELLO_HELLO_PERIOD_DEFAULT)) #define PIM_PROPAGATION_DELAY_MSEC_DEFAULT 500 /* Propagation_delay_default */ #define PIM_OVERRIDE_INTERVAL_MSEC_DEFAULT 2500 /* t_override_default */ /* PIM_JOIN_PRUNE-related definitions */ #define PIM_JOIN_PRUNE_PERIOD_DEFAULT 60 #define PIM_JOIN_PRUNE_HOLDTIME_PERIOD_RATIO 3.5 #define PIM_JOIN_PRUNE_HOLDTIME_DEFAULT ((int)(PIM_JOIN_PRUNE_HOLDTIME_PERIOD_RATIO * PIM_JOIN_PRUNE_PERIOD_DEFAULT)) #define PIM_JOIN_PRUNE_SUPPRESSION_TIMEOUT_RANDOM_FACTOR_MIN 1.1 #define PIM_JOIN_PRUNE_SUPPRESSION_TIMEOUT_RANDOM_FACTOR_MAX 1.4 #define PIM_JOIN_PRUNE_OIF_HOLDTIME_FOREVER 0xffff /* PIM_ASSERT-related definitions */ #define PIM_ASSERT_ASSERT_TIME_DEFAULT 180 #define PIM_ASSERT_ASSERT_OVERRIDE_INTERVAL_DEFAULT 3 #define PIM_ASSERT_MAX_METRIC_PREFERENCE 0x7fffffffU #define PIM_ASSERT_MAX_METRIC 0xffffffffU #define PIM_ASSERT_OIF_RATE_LIMIT 1 /* 1 pkt/second */ #define PIM_ASSERT_RPT_BIT ((uint32_t)(1 << 31)) /* PIM_REGISTER-related definitions */ #define PIM_REGISTER_SUPPRESSION_TIME_DEFAULT 60 #define PIM_REGISTER_PROBE_TIME_DEFAULT 5 #define PIM_REGISTER_HEADER_LENGTH 8 /* PIM_CAND_RP_ADV-related definitions */ #define PIM_CAND_RP_ADV_PERIOD_DEFAULT 60 #define PIM_CAND_RP_ADV_RP_HOLDTIME_DEFAULT ((int)(2.5 * PIM_CAND_RP_ADV_PERIOD_DEFAULT)) #define PIM_CAND_RP_ADV_RP_PRIORITY_DEFAULT 192 /* PIM_BOOTSTRAP-related definitions */ #define PIM_BOOTSTRAP_LOWEST_PRIORITY 0 /* Larger is better */ #define PIM_BOOTSTRAP_BOOTSTRAP_PERIOD_DEFAULT 60 #define PIM_BOOTSTRAP_BOOTSTRAP_TIMEOUT_DEFAULT ((int)(2 * PIM_BOOTSTRAP_BOOTSTRAP_PERIOD_DEFAULT + 10)) #define PIM_BOOTSTRAP_RAND_OVERRIDE_DEFAULT 5 #define PIM_BOOTSTRAP_SCOPE_ZONE_TIMEOUT_DEFAULT ((int)(10 * PIM_BOOTSTRAP_BOOTSTRAP_TIMEOUT_DEFAULT)) #define PIM_BOOTSTRAP_HASH_MASK_LEN_IPV4_DEFAULT 30 #define PIM_BOOTSTRAP_HASH_MASK_LEN_IPV6_DEFAULT 126 #define PIM_BOOTSTRAP_HASH_MASK_LEN_DEFAULT(ip_family) \ ((ip_family == AF_INET)? \ PIM_BOOTSTRAP_HASH_MASK_LEN_IPV4_DEFAULT \ : PIM_BOOTSTRAP_HASH_MASK_LEN_IPV6_DEFAULT) /* PIM_GRAFT-related definitions */ #define PIM_GRAFT_RETRY_PERIOD_DEFAULT 3 /* PIM-DM-related definitions */ #define PIM_DM_SOURCE_LIFETIME_DEFAULT 210 #define PIM_DM_REFRESH_INTERVAL_DEFAULT 60 /* Other timeout default values */ #define PIM_KEEPALIVE_PERIOD_DEFAULT 210 #define PIM_RP_KEEPALIVE_PERIOD_DEFAULT (3 * PIM_REGISTER_SUPPRESSION_TIME_DEFAULT + PIM_REGISTER_PROBE_TIME_DEFAULT) /* * Structures, typedefs and macros */ /* * Address-family related definitions. See this link for a * complete listing of all address families: * http://www.isi.edu/in-notes/iana/assignments/address-family-numbers */ #define ADDRF_IPv4 1 #define ADDRF_IPv6 2 #define ADDRF_NATIVE_ENCODING 0 /* Type of encoding within * a specific address family */ #ifndef HAVE_IPV6 #define ADDRF2IP_ADDRF(addr_family) \ (((addr_family) == ADDRF_IPv4) ? (AF_INET) : (-1)) #define IP_ADDRF2ADDRF(ip_family) \ (((ip_family) == AF_INET) ? (ADDRF_IPv4) : (-1)) #else #define ADDRF2IP_ADDRF(addr_family) \ (((addr_family) == ADDRF_IPv4) ? (AF_INET) \ : ((addr_family) == ADDRF_IPv6) ? \ (AF_INET6) \ : (-1)) #define IP_ADDRF2ADDRF(ip_family) \ (((ip_family) == AF_INET) ? (ADDRF_IPv4) \ : ((ip_family) == AF_INET6) ? \ (ADDRF_IPv6) \ : (-1)) #endif /* HAVE_IPV6 */ #define ESADDR_RPT_BIT 0x1 #define ESADDR_WC_BIT 0x2 #define ESADDR_S_BIT 0x4 #define EGADDR_Z_BIT 0x1 /* PIM message max. payload (IP header and Router Alert IP option excluded) */ #ifndef HAVE_IPV6 #ifndef HOST_OS_WINDOWS #define PIM_MAXPACKET(ip_family) (((ip_family) == AF_INET) ? \ (IP_MAXPACKET \ - sizeof(struct ip) \ - 4*sizeof(uint8_t)) \ : (0)) #else /* HOST_OS_WINDOWS */ #define PIM_MAXPACKET(ip_family) 65496 #endif /* !HOST_OS_WINDOWS */ #else #ifndef IPV6_MAXPACKET #define IPV6_MAXPACKET 65535 /* ip6 max packet size without Jumbo payload */ #endif #define PIM_MAXPACKET(ip_family) (((ip_family) == AF_INET) ? \ (IP_MAXPACKET \ - sizeof(struct ip) \ - 4*sizeof(uint8_t)) \ : ((ip_family) == AF_INET6) ? \ (IPV6_MAXPACKET \ - 4*sizeof(uint8_t)) \ : (0)) #endif /* HAVE_IPV6 */ /* * Macros to get PIM-specific encoded addresses from a datastream. * * XXX: the macros below assume that the code has the following * labels that can be used to jump and process the particular error: * 'rcvd_family_error' 'rcvd_mask_len_error' 'rcvlen_error' * The also assume that function family() is defined (to return * the IP address family within the current context. */ /* XXX: family2addr_bytelen should be defined somewhere */ #ifndef FAMILY2ADDRSIZE #define FAMILY2ADDRSIZE(ip_family) family2addr_bytelen(ip_family) #endif #ifndef FAMILY2PREFIXLEN #define FAMILY2PREFIXLEN(ip_family) family2addr_bitlen(ip_family) #endif #define GET_ENCODED_UNICAST_ADDR(rcvd_family, unicast_ipaddr, buffer) \ do { \ int addr_family_; \ \ BUFFER_GET_OCTET(addr_family_, (buffer)); \ (rcvd_family) = ADDRF2IP_ADDRF(addr_family_); \ if ((rcvd_family) != family()) \ goto rcvd_family_error; \ BUFFER_GET_SKIP(1, (buffer)); /* Encoding type */ \ BUFFER_GET_IPADDR((rcvd_family), (unicast_ipaddr), (buffer)); \ } while (0) #define ENCODED_UNICAST_ADDR_SIZE(ip_family) \ (2*sizeof(uint8_t) + FAMILY2ADDRSIZE(ip_family)) #define GET_ENCODED_GROUP_ADDR(rcvd_family, group_ipaddr, mask_len, reserved, buffer) \ do { \ int addr_family_; \ \ BUFFER_GET_OCTET(addr_family_, (buffer)); \ (rcvd_family) = ADDRF2IP_ADDRF(addr_family_); \ if ((rcvd_family) != family()) \ goto rcvd_family_error; \ BUFFER_GET_SKIP(1, (buffer)); /* Encoding type */ \ BUFFER_GET_OCTET((reserved), (buffer)); \ BUFFER_GET_OCTET((mask_len), (buffer)); \ BUFFER_GET_IPADDR((rcvd_family), (group_ipaddr), (buffer)); \ if ((u_int)(mask_len) > FAMILY2PREFIXLEN((rcvd_family))) \ goto rcvd_mask_len_error; \ } while (0) #define ENCODED_GROUP_ADDR_SIZE(ip_family) \ (4*sizeof(uint8_t) + FAMILY2ADDRSIZE(ip_family)) #define GET_ENCODED_SOURCE_ADDR(rcvd_family, source_ipaddr, mask_len, flags, buffer) \ do { \ int addr_family_; \ \ BUFFER_GET_OCTET(addr_family_, (buffer)); \ (rcvd_family) = ADDRF2IP_ADDRF(addr_family_); \ if ((rcvd_family) != family()) \ goto rcvd_family_error; \ BUFFER_GET_SKIP(1, (buffer)); /* Encoding type */ \ BUFFER_GET_OCTET((flags), (buffer)); \ BUFFER_GET_OCTET((mask_len), (buffer)); \ BUFFER_GET_IPADDR((rcvd_family), (source_ipaddr), (buffer)); \ if ((u_int)(mask_len) > FAMILY2PREFIXLEN((rcvd_family))) \ goto rcvd_mask_len_error; \ } while (0) #define ENCODED_SOURCE_ADDR_SIZE(ip_family) \ (4*sizeof(uint8_t) + FAMILY2ADDRSIZE(ip_family)) /* * Macros to put PIM-specific encoded addresses to a datastream. * * XXX: the macros below assume that the code has the following * labels that can be used to jump and process the particular error: * 'invalid_addr_family_error' 'buflen_error' */ #define PUT_ENCODED_UNICAST_ADDR(ip_family, unicast_ipaddr, buffer) \ do { \ int addr_family_; \ \ addr_family_ = IP_ADDRF2ADDRF((ip_family)); \ if (addr_family_ < 0) \ goto invalid_addr_family_error; \ BUFFER_PUT_OCTET(addr_family_, (buffer)); \ BUFFER_PUT_OCTET(ADDRF_NATIVE_ENCODING, (buffer)); \ BUFFER_PUT_IPADDR((unicast_ipaddr), (buffer)); \ } while (0) #define PUT_ENCODED_GROUP_ADDR(ip_family, group_ipaddr, mask_len, reserved, buffer)\ do { \ int addr_family_; \ \ addr_family_ = IP_ADDRF2ADDRF((ip_family)); \ if (addr_family_ < 0) \ goto invalid_addr_family_error; \ BUFFER_PUT_OCTET(addr_family_, (buffer)); \ BUFFER_PUT_OCTET(ADDRF_NATIVE_ENCODING, (buffer)); \ BUFFER_PUT_OCTET(reserved, (buffer)); /* E.g., EGADDR_Z_BIT */\ BUFFER_PUT_OCTET((mask_len), (buffer)); \ BUFFER_PUT_IPADDR((group_ipaddr), (buffer)); \ } while (0) #define PUT_ENCODED_SOURCE_ADDR(ip_family, source_ipaddr, mask_len, flags, buffer) \ do { \ int addr_family_; \ \ addr_family_ = IP_ADDRF2ADDRF((ip_family)); \ if (addr_family_ < 0) \ goto invalid_addr_family_error; \ BUFFER_PUT_OCTET(addr_family_, (buffer)); \ BUFFER_PUT_OCTET(ADDRF_NATIVE_ENCODING, (buffer)); \ BUFFER_PUT_OCTET((flags), (buffer)); \ BUFFER_PUT_OCTET((mask_len), (buffer)); \ BUFFER_PUT_IPADDR((source_ipaddr), (buffer)); \ } while (0) #define BUFFER_PUT_SKIP_PIM_HEADER(buffer) \ do { \ BUFFER_PUT_SKIP(sizeof(struct pim), (buffer)); \ } while (0) /* * The ASCII names of the PIM protocol control messages */ #define PIMTYPE2ASCII(t) \ (((t) == PIM_HELLO) ? \ "PIM_HELLO" \ : ((t) == PIM_REGISTER) ? \ "PIM_REGISTER" \ : ((t) == PIM_REGISTER_STOP) ? \ "PIM_REGISTER_STOP" \ : ((t) == PIM_JOIN_PRUNE) ? \ "PIM_JOIN_PRUNE" \ : ((t) == PIM_BOOTSTRAP) ? \ "PIM_BOOTSTRAP" \ : ((t) == PIM_ASSERT) ? \ "PIM_ASSERT" \ : ((t) == PIM_GRAFT) ? \ "PIM_GRAFT" \ : ((t) == PIM_GRAFT_ACK) ? \ "PIM_GRAFT_ACK" \ : ((t) == PIM_CAND_RP_ADV) ? \ "PIM_CAND_RP_ADV" \ : ((t) == PIM_ALL_DF_ELECTION) ? \ "PIM_ALL_DF_ELECTION" \ : "PIM_type_unknown") /* * Global variables */ /* * Global functions prototypes */ __BEGIN_DECLS __END_DECLS #endif /* __PIM_PIM_PROTO_H__ */ xorp/pim/pim_rp.hh0000664000076400007640000001621511540224232014254 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/pim/pim_rp.hh,v 1.14 2008/10/02 21:57:55 bms Exp $ #ifndef __PIM_PIM_RP_HH__ #define __PIM_PIM_RP_HH__ // // PIM RP information definitions // #include "libxorp/ipvx.hh" #include "libxorp/ipvxnet.hh" #include "libxorp/timer.hh" #include "libproto/proto_unit.hh" // // Constants definitions // // // Structures/classes, typedefs and macros // class PimNode; class PimRp; class RpTable; class PimMre; // The PIM RP class class PimRp { public: enum rp_learned_method_t { RP_LEARNED_METHOD_AUTORP, RP_LEARNED_METHOD_BOOTSTRAP, RP_LEARNED_METHOD_STATIC, RP_LEARNED_METHOD_UNKNOWN }; PimRp(RpTable& rp_table, const IPvX& rp_addr, uint8_t rp_priority, const IPvXNet& group_prefix, uint8_t hash_mask_len, rp_learned_method_t rp_learned_method); PimRp(RpTable& rp_table, const PimRp& pim_rp); ~PimRp(); RpTable& rp_table() { return (_rp_table); } const IPvX& rp_addr() const { return (_rp_addr); } uint8_t rp_priority() const { return (_rp_priority); } void set_rp_priority(uint8_t v) { _rp_priority = v; } const IPvXNet& group_prefix() const { return (_group_prefix); } uint8_t hash_mask_len() const { return (_hash_mask_len); } void set_hash_mask_len(uint8_t v) { _hash_mask_len = v; } rp_learned_method_t rp_learned_method() const { return (_rp_learned_method); } static const string rp_learned_method_str(rp_learned_method_t rp_learned_method); const string rp_learned_method_str() const; bool is_updated() const { return (_is_updated); } void set_is_updated(bool v) { _is_updated = v; } list& pim_mre_wc_list() { return (_pim_mre_wc_list); } list& pim_mre_sg_list() { return (_pim_mre_sg_list); } list& pim_mre_sg_rpt_list() { return (_pim_mre_sg_rpt_list); } list& pim_mfc_list() { return (_pim_mfc_list); } list& processing_pim_mre_wc_list() { return (_processing_pim_mre_wc_list); } list& processing_pim_mre_sg_list() { return (_processing_pim_mre_sg_list); } list& processing_pim_mre_sg_rpt_list() { return (_processing_pim_mre_sg_rpt_list); } list& processing_pim_mfc_list() { return (_processing_pim_mfc_list); } void init_processing_pim_mre_wc(); void init_processing_pim_mre_sg(); void init_processing_pim_mre_sg_rpt(); void init_processing_pim_mfc(); bool i_am_rp() const { return (_i_am_rp); } private: RpTable& _rp_table; // The RP table I belong to IPvX _rp_addr; // The RP address uint8_t _rp_priority; // The RP priority IPvXNet _group_prefix; // The group address prefix uint8_t _hash_mask_len; // The RP hash mask length rp_learned_method_t _rp_learned_method; // How learned about this RP bool _is_updated; // True if just created or updated list _pim_mre_wc_list; // List of all related (*,G) entries // for this RP. list _pim_mre_sg_list; // List of all related (S,G) entries // for this RP that have no (*,G) entry list _pim_mre_sg_rpt_list;// List of all related (S,G,rpt) // entries for this RP that have no // (*,G) entry list _pim_mfc_list; // List of all related MFC entries // for this RP. // NOTE: those MFC entries _may_ have // existing (*,G) entry. list _processing_pim_mre_wc_list; // List of all related // (*,G) entries for this RP that are // awaiting to be processed. list _processing_pim_mre_sg_list; // List of all related // (S,G) entries for this RP that // have no (*,G) entry and that are // awaiting to be processed. list _processing_pim_mre_sg_rpt_list; // List of all related // (S,G,rpt) entries for this RP that // have no (*,G) entry and that are // awaiting to be processed. list _processing_pim_mfc_list; // List of all related // MFC entries for this RP that are // awaiting to be processed. // NOTE: those MFC entries _may_ have // existing (*,G) entry. bool _i_am_rp; // True if this RP is me }; // The RP table class class RpTable : public ProtoUnit { public: RpTable(PimNode& pim_node); ~RpTable(); void clear(); int start(); int stop(); PimNode& pim_node() { return (_pim_node); } PimRp *rp_find(const IPvX& group_addr); PimRp *add_rp(const IPvX& rp_addr, uint8_t rp_priority, const IPvXNet& group_prefix, uint8_t hash_mask_len, PimRp::rp_learned_method_t rp_learned_method); int delete_rp(const IPvX& rp_addr, const IPvXNet& group_prefix, PimRp::rp_learned_method_t rp_learned_method); int delete_all_group_prefixes_rp(const IPvX& rp_addr, PimRp::rp_learned_method_t rp_learned_method); int delete_all_rps(PimRp::rp_learned_method_t rp_learned_method); bool apply_rp_changes(); void add_pim_mre(PimMre *pim_mre); void add_pim_mfc(PimMfc *pim_mfc); void delete_pim_mre(PimMre *pim_mre); void delete_pim_mfc(PimMfc *pim_mfc); list& rp_list() { return (_rp_list); } list& processing_rp_list() { return (_processing_rp_list); } void init_processing_pim_mre_wc(const IPvX& rp_addr); void init_processing_pim_mre_sg(const IPvX& rp_addr); void init_processing_pim_mre_sg_rpt(const IPvX& rp_addr); void init_processing_pim_mfc(const IPvX& rp_addr); PimRp *find_processing_pim_mre_wc(const IPvX& rp_addr); PimRp *find_processing_pim_mre_sg(const IPvX& rp_addr); PimRp *find_processing_pim_mre_sg_rpt(const IPvX& rp_addr); PimRp *find_processing_pim_mfc(const IPvX& rp_addr); PimRp *find_processing_rp_by_addr(const IPvX& rp_addr); bool has_rp_addr(const IPvX& rp_addr); private: PimRp *compare_rp(const IPvX& group_addr, PimRp *rp1, PimRp *rp2) const; uint32_t derived_addr(const IPvX& addr) const; PimNode& _pim_node; // The associated PIM node list _rp_list; // The list of RPs list _processing_rp_list; // The list of obsolete RPs to process: // XXX: the PimRp entry with address of IPvX::ZERO(family()) contains // the list of PimMre and PimMfc entries that have no RP. }; // // Global variables // // // Global functions prototypes // #endif // __PIM_PIM_RP_HH__ xorp/pim/pim_node_cli.hh0000664000076400007640000000656111540224231015411 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/pim/pim_node_cli.hh,v 1.15 2008/10/02 21:57:54 bms Exp $ #ifndef __PIM_PIM_NODE_CLI_HH__ #define __PIM_PIM_NODE_CLI_HH__ // // PIM protocol CLI // #include "libproto/proto_node_cli.hh" // // Constants definitions // // // Structures/classes, typedefs and macros // // PIM protocol CLI class class PimNode; class PimNodeCli : public ProtoNodeCli { public: PimNodeCli(PimNode& pim_node); virtual ~PimNodeCli(); /** * Start the node operation. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int start(); /** * Stop the node operation. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int stop(); /** * Enable node operation. * * If an unit is not enabled, it cannot be start, or pending-start. */ void enable(); /** * Disable 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 disable(); int add_all_cli_commands(); private: PimNode& pim_node() const { return (_pim_node); } PimNode& _pim_node; // // Misc. other methods // string mifset_str(const Mifset& mifset) const { string res; for (uint32_t i = 0; i < _pim_node.maxvifs(); i++) { if (mifset.test(i)) res += "O"; else res += "."; } return res; } // // PIM CLI commands // int cli_show_pim_bootstrap(const vector& argv); int cli_show_pim_bootstrap_rps(const vector& argv); int cli_show_pim_interface(const vector& argv); int cli_show_pim_interface_address(const vector& argv); int cli_show_pim_join(const vector& argv); int cli_show_pim_join_all(const vector& argv); int cli_show_pim_mfc(const vector& argv); int cli_show_pim_neighbors(const vector& argv); int cli_show_pim_mrib(const vector& argv); int cli_show_pim_rps(const vector& argv); int cli_show_pim_scope(const vector& argv); // // Methods used by the PIM CLI commands // void cli_print_pim_mre_entries(const IPvXNet& group_range, bool is_print_all); void cli_print_pim_mfc_entries(const IPvXNet& group_range); void cli_print_pim_mre(const PimMre *pim_mre); void cli_print_pim_mfc(const PimMfc *pim_mfc); }; // // Global variables // // // Global functions prototypes // #endif // __PIM_PIM_NODE_CLI_HH__ xorp/pim/pim_mrib_table.cc0000664000076400007640000001630311421137511015720 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // PIM Multicast Routing Information Base Table implementation. // #include "pim_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvx.hh" #include "libxorp/time_slice.hh" #include "pim_mre.hh" #include "pim_mrt.hh" #include "pim_node.hh" #include "pim_mrib_table.hh" #include "pim_vif.hh" PimMribTable::PimMribTable(PimNode& pim_node) : MribTable(pim_node.family()), _pim_node(pim_node) { // // XXX: enable the preserving of the removed Mrib entries, because // they may be in-use even after they are removed from the MribTable. // MribTable::set_is_preserving_removed_mrib_entries(true); } PimMribTable::~PimMribTable() { } int PimMribTable::family() { return (pim_node().family()); } PimMrt& PimMribTable::pim_mrt() { return (pim_node().pim_mrt()); } void PimMribTable::clear() { MribTable::clear(); add_modified_prefix(IPvXNet(IPvX::ZERO(family()), 0)); apply_mrib_changes(); } Mrib * PimMribTable::find(const IPvX& address) const { Mrib *mrib; mrib = MribTable::find(address); if (mrib != NULL) { // // Test if the RPF vif is valid and UP // PimVif *pim_vif = pim_node().vif_find_by_vif_index(mrib->next_hop_vif_index()); if ((pim_vif == NULL) || (! pim_vif->is_up())) return (NULL); } return (mrib); } void PimMribTable::add_pending_insert(uint32_t tid, const Mrib& mrib, const string& next_hop_vif_name) { add_modified_prefix(mrib.dest_prefix()); // // XXX: if the next-hop interface points toward the loopback interface // (e.g., in case of KAME IPv6 stack), or points toward an invalid // interface (e.g., in case the loopback interface is not configured), // and if the MRIB entry is for one of my own addresses, then we // overwrite it with the network interface this address belongs to. // uint32_t vif_index = mrib.next_hop_vif_index(); PimVif *pim_vif = pim_node().vif_find_by_vif_index(vif_index); if ((vif_index == Vif::VIF_INDEX_INVALID) || ((pim_vif != NULL) && pim_vif->is_loopback())) { const IPvXNet& dest_prefix = mrib.dest_prefix(); if (dest_prefix.prefix_len() == IPvX::addr_bitlen(family())) { pim_vif = pim_node().vif_find_by_addr(dest_prefix.masked_addr()); if (pim_vif != NULL) { Mrib modified_mrib(mrib); modified_mrib.set_next_hop_vif_index(pim_vif->vif_index()); MribTable::add_pending_insert(tid, modified_mrib); return; } } } MribTable::add_pending_insert(tid, mrib); if (pim_vif == NULL) add_unresolved_prefix(mrib.dest_prefix(), next_hop_vif_name); } void PimMribTable::add_pending_remove(uint32_t tid, const Mrib& mrib) { add_modified_prefix(mrib.dest_prefix()); MribTable::add_pending_remove(tid, mrib); delete_unresolved_prefix(mrib.dest_prefix()); } void PimMribTable::add_pending_remove_all_entries(uint32_t tid) { add_modified_prefix(IPvXNet(IPvX::ZERO(family()), 0)); MribTable::add_pending_remove_all_entries(tid); } void PimMribTable::commit_pending_transactions(uint32_t tid) { MribTable::commit_pending_transactions(tid); apply_mrib_changes(); } // // Apply changes to the PIM MRT due to MRIB changes. // XXX: a number of tasks (one per modified prefix) are scheduled // to perform the appropriate actions. // void PimMribTable::apply_mrib_changes() { while (! _modified_prefix_list.empty()) { // Get the modified prefix address IPvXNet modified_prefix_addr = _modified_prefix_list.front(); _modified_prefix_list.pop_front(); pim_node().pim_mrt().add_task_mrib_changed(modified_prefix_addr); } // // XXX: Add a task to delete all removed Mrib entries after they are // not needed anymore. // Note that by the time the task is processed the PimMre entries // affected by the removed Mrib entries should have been processed // and they should not point anymore to the removed Mrib entries. // list& mrib_list = MribTable::removed_mrib_entries(); if (mrib_list.empty()) return; pim_node().pim_mrt().add_task_delete_mrib_entries(mrib_list); mrib_list.clear(); } // // Search the list of modified address prefixes and remove // all smaller prefixes. If there is a prefix that is larger // than the modified_prefix, then don't do anything. // void PimMribTable::add_modified_prefix(const IPvXNet& modified_prefix) { // // Search the list for overlapping address prefixes. // list::iterator iter1; for (iter1 = _modified_prefix_list.begin(); iter1 != _modified_prefix_list.end(); ) { list::iterator iter2 = iter1; ++iter1; IPvXNet& old_addr_prefix = *iter2; if (old_addr_prefix.contains(modified_prefix)) return; // Nothing to merge/enlarge if (modified_prefix.contains(old_addr_prefix)) { // Remove the old address prefix, because the new one is larger _modified_prefix_list.erase(iter2); } } // // Add the new address prefix to the list of modifed prefixes // _modified_prefix_list.push_back(modified_prefix); } void PimMribTable::add_unresolved_prefix(const IPvXNet& dest_prefix, const string& next_hop_vif_name) { map::iterator iter; // Erase old unresolved entry (if exists) iter = _unresolved_prefixes.find(dest_prefix); if (iter != _unresolved_prefixes.end()) _unresolved_prefixes.erase(iter); _unresolved_prefixes.insert(make_pair(dest_prefix, next_hop_vif_name)); } void PimMribTable::delete_unresolved_prefix(const IPvXNet& dest_prefix) { map::iterator iter; // Erase unresolved entry (if exists) iter = _unresolved_prefixes.find(dest_prefix); if (iter != _unresolved_prefixes.end()) _unresolved_prefixes.erase(iter); } void PimMribTable::resolve_prefixes_by_vif_name(const string& next_hop_vif_name, uint32_t next_hop_vif_index) { map::iterator iter, iter2; // // Find all unresolved prefixes for the given vif name // and update their vif index. // for (iter = _unresolved_prefixes.begin(); iter != _unresolved_prefixes.end(); ) { iter2 = iter; ++iter; if (iter2->second != next_hop_vif_name) continue; // Update the mrib entry MribTable::update_entry_vif_index(iter2->first, next_hop_vif_index); _modified_prefix_list.push_back(iter2->first); _unresolved_prefixes.erase(iter2); } apply_mrib_changes(); } xorp/pim/pim_proto_join_prune.cc0000664000076400007640000002555411613330336017226 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // PIM PIM_JOIN_PRUNE messages processing. // #include "pim_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvx.hh" #include "pim_node.hh" #include "pim_vif.hh" /** * PimVif::pim_join_prune_recv: * @pim_nbr: The PIM neighbor message originator (or NULL if not a neighbor). * @src: The message source address. * @dst: The message destination address. * @buffer: The buffer with the message. * @message_type: The message type (should be either %PIM_JOIN_PRUNE * or %PIM_GRAFT. * * Receive PIM_JOIN_PRUNE message. * XXX: PIM_GRAFT message has the same format as the * PIM_JOIN_PRUNE message, hence we use the PIM_JOIN_PRUNE processing * function to process PIM_GRAFT as well. * TODO: for now ignore PIM_GRAFT messages logic; will need to come * back to it later. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int PimVif::pim_join_prune_recv(PimNbr *pim_nbr, const IPvX& src, const IPvX& dst, buffer_t *buffer, uint8_t message_type) { int holdtime; int rcvd_family; IPvX target_nbr_addr(family()); IPvX source_addr(family()), group_addr(family()); uint8_t source_mask_len, group_mask_len; uint8_t group_addr_reserved_flags; int groups_n, sources_n, sources_j_n, sources_p_n; uint8_t source_flags; bool is_group_ignored, is_rp_entry, is_new_group; action_jp_t action_jp; mrt_entry_type_t mrt_entry_type; PimJpHeader jp_header(pim_node()); UNUSED(src); UNUSED(dst); UNUSED(message_type); UNUSED(group_addr_reserved_flags); // // Parse the message // GET_ENCODED_UNICAST_ADDR(rcvd_family, target_nbr_addr, buffer); // Check the target neighbor address. // XXX: on point-to-point links we accept target_nbr_addr of all zeros if (! (target_nbr_addr.is_unicast() || (is_p2p() && (target_nbr_addr == IPvX::ZERO(family()))))) { XLOG_WARNING("RX %s from %s to %s: " "invalid 'upstream neighbor' " "address: %s", PIMTYPE2ASCII(message_type), cstring(src), cstring(dst), cstring(target_nbr_addr)); return (XORP_ERROR); } BUFFER_GET_SKIP(1, buffer); // Reserved BUFFER_GET_OCTET(groups_n, buffer); BUFFER_GET_HOST_16(holdtime, buffer); // // XXX: here we should test that (groups_n > 0), but // we should be liberal about such errors. // while (groups_n--) { GET_ENCODED_GROUP_ADDR(rcvd_family, group_addr, group_mask_len, group_addr_reserved_flags, buffer); BUFFER_GET_HOST_16(sources_j_n, buffer); BUFFER_GET_HOST_16(sources_p_n, buffer); // Check the group address is_group_ignored = true; is_rp_entry = false; is_new_group = true; // // Check the group address and mask length // do { if (group_mask_len == IPvX::ip_multicast_base_address_mask_len(family()) && (group_addr == IPvX::MULTICAST_BASE(family()))) { // XXX: (*,*,RP) Join/Prune is_group_ignored = false; is_rp_entry = true; break; } if (! group_addr.is_multicast()) { XLOG_WARNING("RX %s from %s to %s: " "invalid group address: %s", PIMTYPE2ASCII(message_type), cstring(src), cstring(dst), cstring(group_addr)); is_group_ignored = true; break; } if (group_addr.is_linklocal_multicast() || group_addr.is_interfacelocal_multicast()) { XLOG_WARNING("RX %s from %s to %s: " "non-routable link or interface-local " "group address: %s", PIMTYPE2ASCII(message_type), cstring(src), cstring(dst), cstring(group_addr)); is_group_ignored = true; break; } if (group_mask_len != group_addr.addr_bitlen()) { is_group_ignored = true; XLOG_WARNING("RX %s from %s to %s: " "invalid group mask length for group %s: %d", PIMTYPE2ASCII(message_type), cstring(src), cstring(dst), cstring(group_addr), group_mask_len); break; } is_group_ignored = false; break; } while (false); sources_n = sources_j_n + sources_p_n; action_jp = ACTION_JOIN; while (sources_n--) { if (sources_j_n > 0) { action_jp = ACTION_JOIN; sources_j_n--; } else { action_jp = ACTION_PRUNE; sources_p_n--; } GET_ENCODED_SOURCE_ADDR(rcvd_family, source_addr, source_mask_len, source_flags, buffer); if (is_group_ignored) continue; // Check the source address and mask length if (! source_addr.is_unicast()) { XLOG_WARNING("RX %s from %s to %s: " "invalid source/RP address: %s", PIMTYPE2ASCII(message_type), cstring(src), cstring(dst), cstring(source_addr)); continue; } if (source_mask_len != source_addr.addr_bitlen()) { XLOG_WARNING("RX %s from %s to %s: " "invalid source mask length " "for group %s source/RP %s: %d", PIMTYPE2ASCII(message_type), cstring(src), cstring(dst), cstring(group_addr), cstring(source_addr), source_mask_len); continue; } // // Check the source flags if they are valid. // if (proto_is_pimdm()) { if (source_flags & (ESADDR_RPT_BIT | ESADDR_WC_BIT | ESADDR_S_BIT)) { // XXX: PIM-DM does not use those flags. XLOG_WARNING("RX %s from %s to %s: " "invalid source/RP flags " "while running in PIM-DM mode " "for (%s, %s): %#x", PIMTYPE2ASCII(message_type), cstring(src), cstring(dst), cstring(source_addr), cstring(group_addr), source_flags); continue; } } if (proto_is_pimsm()) { if ((source_flags & ESADDR_S_BIT) != ESADDR_S_BIT) { // Missing "Sparse bit" XLOG_WARNING("RX %s from %s to %s: " "missing Sparse-bit flag " "while running in PIM-SM mode " "for (%s, %s)", PIMTYPE2ASCII(message_type), cstring(src), cstring(dst), cstring(source_addr), cstring(group_addr)); continue; } } // // Check the source flags to find the entry type. // mrt_entry_type = MRT_ENTRY_UNKNOWN; // (*,*,RP) entry if (is_rp_entry) { if ((source_flags & (ESADDR_RPT_BIT | ESADDR_WC_BIT)) != (ESADDR_RPT_BIT | ESADDR_WC_BIT)) { XLOG_WARNING("RX %s from %s to %s: " "invalid source/RP flags " "for (*,*,RP) RP = %s: %#x", PIMTYPE2ASCII(message_type), cstring(src), cstring(dst), cstring(source_addr), source_flags); continue; } mrt_entry_type = MRT_ENTRY_RP; goto jp_entry_add_label; } // (*,G) entry if ((source_flags & (ESADDR_RPT_BIT | ESADDR_WC_BIT)) == (ESADDR_RPT_BIT | ESADDR_WC_BIT)) { // // Check if the RP address matches. If not, silently ignore // the (*,G) entry. However, the (S,G) and (S,G,rpt) entries // for same group should still be processed. // PimRp *pim_rp = pim_node()->rp_table().rp_find(group_addr); if ((pim_rp == NULL) || (pim_rp->rp_addr() != source_addr)) { XLOG_TRACE(pim_node()->is_log_trace(), "RX %s from %s to %s: " "(*,G) Join/Prune entry for group %s " "RP address does not match: " "router RP = %s, message RP = %s", PIMTYPE2ASCII(message_type), cstring(src), cstring(dst), cstring(group_addr), pim_rp? cstring(pim_rp->rp_addr()) : "NULL", cstring(source_addr)); continue; } mrt_entry_type = MRT_ENTRY_WC; goto jp_entry_add_label; } // (S,G,rpt) entry if ((source_flags & (ESADDR_RPT_BIT | ESADDR_WC_BIT)) == ESADDR_RPT_BIT) { mrt_entry_type = MRT_ENTRY_SG_RPT; goto jp_entry_add_label; } // (S,G) entry if ((source_flags & (ESADDR_RPT_BIT | ESADDR_WC_BIT)) == 0) { mrt_entry_type = MRT_ENTRY_SG; goto jp_entry_add_label; } // Unknown entry XLOG_WARNING("RX %s from %s to %s: " "invalid source/RP flags for (%s, %s): %#x", PIMTYPE2ASCII(message_type), cstring(src), cstring(dst), cstring(source_addr), cstring(group_addr), source_flags); continue; jp_entry_add_label: // An the entry to the pool of entries pending commit. // TODO: if error, then ignore the whole message?? jp_header.jp_entry_add(source_addr, group_addr, group_mask_len, mrt_entry_type, action_jp, holdtime, is_new_group); is_new_group = false; // Keep statistics per entry type switch (mrt_entry_type) { case MRT_ENTRY_RP: if (action_jp == ACTION_JOIN) ++_pimstat_rx_join_rp; else ++_pimstat_rx_prune_rp; break; case MRT_ENTRY_WC: if (action_jp == ACTION_JOIN) ++_pimstat_rx_join_wc; else ++_pimstat_rx_prune_wc; break; case MRT_ENTRY_SG: if (action_jp == ACTION_JOIN) ++_pimstat_rx_join_sg; else ++_pimstat_rx_prune_sg; break; case MRT_ENTRY_SG_RPT: if (action_jp == ACTION_JOIN) ++_pimstat_rx_join_sg_rpt; else ++_pimstat_rx_prune_sg_rpt; break; default: XLOG_UNREACHABLE(); break; } } } // Commit all Join/Prune requests to the MRT jp_header.mrt_commit(this, target_nbr_addr); jp_header.reset(); UNUSED(pim_nbr); return (XORP_OK); // Various error processing rcvlen_error: XLOG_WARNING("RX %s from %s to %s: " "invalid message length", PIMTYPE2ASCII(PIM_JOIN_PRUNE), cstring(src), cstring(dst)); ++_pimstat_rx_malformed_packet; return (XORP_ERROR); rcvd_mask_len_error: XLOG_WARNING("RX %s from %s to %s: " "invalid group mask length = %d", PIMTYPE2ASCII(PIM_JOIN_PRUNE), cstring(src), cstring(dst), group_mask_len); return (XORP_ERROR); rcvd_family_error: XLOG_WARNING("RX %s from %s to %s: " "invalid address family inside = %d", PIMTYPE2ASCII(PIM_JOIN_PRUNE), cstring(src), cstring(dst), rcvd_family); return (XORP_ERROR); } // // Send Join/Prune message(s) to a PIM neighbor. // int PimVif::pim_join_prune_send(PimNbr *pim_nbr, PimJpHeader *jp_header, string& error_msg) { int ret_value = jp_header->network_commit(this, pim_nbr->primary_addr(), error_msg); jp_header->reset(); return (ret_value); } xorp/pim/pim_bsr.hh0000664000076400007640000004145711635757530014450 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __PIM_PIM_BSR_HH__ #define __PIM_PIM_BSR_HH__ // // PIM Bootstrap Router (BSR) mechanism definitions // #include "libxorp/ipvx.hh" #include "libxorp/ipvxnet.hh" #include "libxorp/timer.hh" #include "libproto/proto_unit.hh" #include "pim_scope_zone_table.hh" // // Constants definitions // // // Structures/classes, typedefs and macros // class PimNode; class PimVif; class BsrGroupPrefix; class BsrZone; class BsrRp; // The PIM BSR class class PimBsr : public ProtoUnit { public: PimBsr(PimNode& pim_node); virtual ~PimBsr(); /** * Clear the entry. */ void clear(); /** * Start operation. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int start(); /** * Stop operation. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int stop(); /** * Enable operation. * * If an unit is not enabled, it cannot be start, or pending-start. */ void enable(); /** * Disable operation. * * If an unit is disabled, it cannot be start or pending-start. * If the unit was runnning, it will be stop first. */ void disable(); /** * Apply BSR configuration changes. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int apply_bsr_changes(string& error_msg); PimNode& pim_node() const { return (_pim_node); } int unicast_pim_bootstrap(PimVif *pim_vif, const IPvX& nbr_addr) const; list& config_bsr_zone_list() { return (_config_bsr_zone_list); } list& active_bsr_zone_list() { return (_active_bsr_zone_list); } list& expire_bsr_zone_list() { return (_expire_bsr_zone_list); } BsrZone *add_config_bsr_zone(const BsrZone& bsr_zone, string& error_msg); BsrZone *add_active_bsr_zone(const BsrZone& bsr_zone, string& error_msg); BsrZone *add_expire_bsr_zone(const BsrZone& bsr_zone); void delete_config_bsr_zone(BsrZone *old_bsr_zone); void delete_active_bsr_zone(BsrZone *old_bsr_zone); void delete_expire_bsr_zone(BsrZone *old_bsr_zone); void delete_all_expire_bsr_zone_by_zone_id(const PimScopeZoneId& zone_id); void delete_expire_bsr_zone_prefix(const IPvXNet& group_prefix, bool is_scope_zone); BsrZone *find_config_bsr_zone(const PimScopeZoneId& zone_id) const; BsrZone *find_active_bsr_zone(const PimScopeZoneId& zone_id) const; BsrZone *find_config_bsr_zone_by_prefix(const IPvXNet& group_prefix, bool is_scope_zone) const; BsrZone *find_active_bsr_zone_by_prefix(const IPvXNet& group_prefix, bool is_scope_zone) const; BsrRp *find_rp(const IPvXNet& group_prefix, bool is_scope_zone, const IPvX& rp_addr) const; void add_rps_to_rp_table(); void schedule_rp_table_apply_rp_changes(); void clean_expire_bsr_zones(); void schedule_clean_expire_bsr_zones(); void add_vif_addr(uint32_t vif_index, const IPvX& vif_addr); void delete_vif_addr(uint32_t vif_index, const IPvX& vif_addr); // // Test-related methods // BsrZone *add_test_bsr_zone(const PimScopeZoneId& zone_id, const IPvX& bsr_addr, uint8_t bsr_priority, uint8_t hash_mask_len, uint16_t fragment_tag); BsrZone *find_test_bsr_zone(const PimScopeZoneId& zone_id) const; BsrGroupPrefix *add_test_bsr_group_prefix(const PimScopeZoneId& zone_id, const IPvXNet& group_prefix, bool is_scope_zone, uint8_t expected_rp_count); BsrRp *add_test_bsr_rp(const PimScopeZoneId& zone_id, const IPvXNet& group_prefix, const IPvX& rp_addr, uint8_t rp_priority, uint16_t rp_holdtime); int send_test_bootstrap(const string& vif_name, string& error_msg); int send_test_bootstrap_by_dest(const string& vif_name, const IPvX& dest_addr, string& error_msg); int send_test_cand_rp_adv(); private: void rp_table_apply_rp_changes_timer_timeout(); void clean_expire_bsr_zones_timer_timeout(); BsrZone *find_bsr_zone_by_prefix_from_list( const list& zone_list, const IPvXNet& group_prefix, bool is_scope_zone) const; bool can_add_config_bsr_zone(const BsrZone& bsr_zone, string& error_msg) const; bool can_add_active_bsr_zone(const BsrZone& bsr_zone, string& error_msg) const; PimNode& _pim_node; // The associated PIM node list _config_bsr_zone_list; // List of configured Cand-BSR // zones and/or Cand-RP info list _active_bsr_zone_list; // List of active BSR zones list _expire_bsr_zone_list; // List of expiring BSR zones list _test_bsr_zone_list; // List of test BSR zones XorpTimer _rp_table_apply_rp_changes_timer; // Timer to apply RP changes // to the RpTable XorpTimer _clean_expire_bsr_zones_timer; // Timer to cleanup expiring // BSR zones }; class BsrZone : public BugCatcher { public: BsrZone(PimBsr& pim_bsr, const BsrZone& bsr_zone); BsrZone(PimBsr& pim_bsr, const PimScopeZoneId& zone_id); BsrZone(PimBsr& pim_bsr, const IPvX& bsr_addr, uint8_t bsr_priority, uint8_t hash_mask_len, uint16_t fragment_tag); ~BsrZone(); PimBsr& pim_bsr() { return (_pim_bsr); } // // BsrZone type // bool is_config_bsr_zone() const { return (_is_config_bsr_zone); } bool is_active_bsr_zone() const { return (_is_active_bsr_zone); } bool is_expire_bsr_zone() const { return (_is_expire_bsr_zone); } bool is_test_bsr_zone() const { return (_is_test_bsr_zone); } void set_config_bsr_zone(bool v); void set_active_bsr_zone(bool v); void set_expire_bsr_zone(bool v); void set_test_bsr_zone(bool v); const IPvX& bsr_addr() const { return (_bsr_addr); } void set_bsr_addr(const IPvX& v) { _bsr_addr = v; } uint8_t bsr_priority() const { return (_bsr_priority); } void set_bsr_priority(uint8_t v) { _bsr_priority = v; } uint8_t hash_mask_len() const { return (_hash_mask_len); } uint16_t fragment_tag() const { return (_fragment_tag); } uint16_t new_fragment_tag() { return (++_fragment_tag); } bool is_accepted_message() const { return (_is_accepted_message); } void set_is_accepted_message(bool v) { _is_accepted_message = v; } bool is_unicast_message() const { return (_is_unicast_message); } const IPvX& unicast_message_src() const { return (_unicast_message_src); } void set_is_unicast_message(bool v, const IPvX& src) { _is_unicast_message = v; _unicast_message_src = src; } bool is_cancel() const { return (_is_cancel); } void set_is_cancel(bool v) { _is_cancel = v; } const PimScopeZoneId& zone_id() const { return (_zone_id); } void set_zone_id(const PimScopeZoneId& zone_id) { _zone_id = zone_id; } // The states used per scope zone enum bsr_zone_state_t { STATE_INIT, // The state after initialization // States if I am a Candidate BSR STATE_CANDIDATE_BSR, STATE_PENDING_BSR, STATE_ELECTED_BSR, // States if I am not a Candidate BSR STATE_NO_INFO, STATE_ACCEPT_ANY, STATE_ACCEPT_PREFERRED }; bsr_zone_state_t bsr_zone_state() const { return (_bsr_zone_state); } void set_bsr_zone_state(bsr_zone_state_t v) { _bsr_zone_state = v; } /** * Update the configuration of a BSR zone. * * @param new_bsr_zone the BSR zone with the new configuration. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int update_config_bsr_zone(const BsrZone& new_bsr_zone, string& error_msg); bool is_consistent(string& error_msg) const; bool can_merge_rp_set(const BsrZone& bsr_zone, string& error_msg) const; void merge_rp_set(const BsrZone& bsr_zone); void store_rp_set(const BsrZone& bsr_zone); XorpTimer& bsr_timer() { return (_bsr_timer); } const XorpTimer& const_bsr_timer() const { return (_bsr_timer); } void expire_bsr_timer(); XorpTimer& scope_zone_expiry_timer() { return (_scope_zone_expiry_timer); } const XorpTimer& const_scope_zone_expiry_timer() const { return (_scope_zone_expiry_timer); } const list& bsr_group_prefix_list() const { return (_bsr_group_prefix_list); } list& bsr_group_prefix_list() { return (_bsr_group_prefix_list); } BsrGroupPrefix *add_bsr_group_prefix(const IPvXNet& group_prefix_init, bool is_scope_zone_init, uint8_t expected_rp_count); void delete_bsr_group_prefix(BsrGroupPrefix *bsr_group_prefix); BsrGroupPrefix *find_bsr_group_prefix(const IPvXNet& group_prefix) const; bool process_candidate_bsr(const BsrZone& cand_bsr_zone); bool i_am_bsr() const; bool is_new_bsr_preferred(const BsrZone& bsr_zone) const; bool is_new_bsr_same_priority(const BsrZone& bsr_zone) const; TimeVal randomized_override_interval(const IPvX& my_addr, uint8_t my_priority) const; bool is_bsm_forward() const { return (_is_bsm_forward); } void set_bsm_forward(bool v) { _is_bsm_forward = v; } bool is_bsm_originate() const { return (_is_bsm_originate); } void set_bsm_originate(bool v) { _is_bsm_originate = v; } bool i_am_candidate_bsr() const { return (_i_am_candidate_bsr); } void set_i_am_candidate_bsr(bool i_am_candidate_bsr, uint32_t my_vif_index, const IPvX& my_bsr_addr, uint8_t my_bsr_priority); uint32_t my_vif_index() const { return (_my_vif_index); } const IPvX& my_bsr_addr() const { return (_my_bsr_addr); } uint8_t my_bsr_priority() const { return (_my_bsr_priority); } bool is_my_bsr_addr_explicit() const { return (_is_my_bsr_addr_explicit); } void set_is_my_bsr_addr_explicit(bool v) { _is_my_bsr_addr_explicit = v; } // // Cand-RP related methods // BsrRp *find_rp(const IPvXNet& group_prefix, const IPvX& rp_addr) const; BsrRp *add_rp(const IPvXNet& group_prefix, bool is_scope_zone_init, const IPvX& rp_addr, uint8_t rp_priority, uint16_t rp_holdtime, string& error_msg); XorpTimer& candidate_rp_advertise_timer() { return (_candidate_rp_advertise_timer); } const XorpTimer& const_candidate_rp_advertise_timer() const { return (_candidate_rp_advertise_timer); } void start_candidate_rp_advertise_timer(); void expire_candidate_rp_advertise_timer(); private: void bsr_timer_timeout(); void scope_zone_expiry_timer_timeout(); void candidate_rp_advertise_timer_timeout(); PimBsr& _pim_bsr; // The PimBsr for this BsrZone // BsrZone type bool _is_config_bsr_zone; // True if config BSR zone bool _is_active_bsr_zone; // True if active BSR zone bool _is_expire_bsr_zone; // True if expire BSR zone bool _is_test_bsr_zone; // True if test BSR zone // State at all routers IPvX _bsr_addr; // The address of the Bootstrap router uint8_t _bsr_priority; // The BSR priority (larger is better) uint8_t _hash_mask_len; // The hash mask length uint16_t _fragment_tag; // The fragment tag bool _is_accepted_message; // True if info accepted already bool _is_unicast_message; // True if info received by unicast IPvX _unicast_message_src; // The source address of the info // (if received by unicast) PimScopeZoneId _zone_id; // The (prefix-based) scope zone ID. // If non-scoped zone, the ID is // IPvXNet:ip_multicast_base_prefix() XorpTimer _bsr_timer; // The Bootstrap Timer list _bsr_group_prefix_list; // The list of group // prefixes for this zone. // XXX: if a scope zone, and if there // is a group-RP prefix for the whole // zone, this prefix must be in front. bsr_zone_state_t _bsr_zone_state; // Scope zone state XorpTimer _scope_zone_expiry_timer; // The Scope-Zone Expiry Timer // State at a Candidate BSR bool _i_am_candidate_bsr; // True if I am Cand-BSR for this zone uint32_t _my_vif_index; // The vif index with my address // if a Cand-BSR IPvX _my_bsr_addr; // My address if a Cand-BSR uint8_t _my_bsr_priority; // My BSR priority if a Cand-BSR bool _is_my_bsr_addr_explicit; // True if my Cand-BSR address was // set explicitly // State at a Candidate RP XorpTimer _candidate_rp_advertise_timer; // The C-RP Adv. Timer // Misc. state bool _is_bsm_forward; // Temp. state: if true, forward BSM bool _is_bsm_originate; // Temp. state: if true, originate BSM bool _is_cancel; // True if we are canceling this zone }; class BsrGroupPrefix { public: BsrGroupPrefix(BsrZone& bsr_zone, const IPvXNet& group_prefix, bool is_scope_zone, uint8_t expected_rp_count); BsrGroupPrefix(BsrZone& bsr_zone, const BsrGroupPrefix& bsr_group_prefix); ~BsrGroupPrefix(); BsrZone& bsr_zone() { return (_bsr_zone); } bool is_scope_zone() const { return (_is_scope_zone); } const IPvXNet& group_prefix() const { return (_group_prefix); } uint8_t expected_rp_count() const { return (_expected_rp_count); } uint8_t received_rp_count() const { return (_received_rp_count); } void set_received_rp_count(uint8_t v) { _received_rp_count = v; } void set_expected_rp_count(uint8_t v) { _expected_rp_count = v; } const list& rp_list() const { return (_rp_list); } list& rp_list() { return (_rp_list); } BsrRp *add_rp(const IPvX& rp_addr, uint8_t rp_priority, uint16_t rp_holdtime); void delete_rp(BsrRp *bsr_rp); BsrRp *find_rp(const IPvX& rp_addr) const; bool is_completed() const { return (_expected_rp_count == _received_rp_count); } // Removal related methods void schedule_bsr_group_prefix_remove(); const XorpTimer& const_remove_timer() const { return (_remove_timer); } private: void remove_timer_timeout(); BsrZone& _bsr_zone; // The BSR zone I belong to // Group prefix state IPvXNet _group_prefix; // The group address prefix bool _is_scope_zone; // True if prefix for admin. scope zone uint8_t _expected_rp_count; // Expected number of RPs uint8_t _received_rp_count; // Received number of RPs so far list _rp_list; // The list of received RPs // Misc. state XorpTimer _remove_timer; // Timer to remove empty prefix }; class BsrRp { public: BsrRp(BsrGroupPrefix& bsr_group_prefix, const IPvX& rp_addr, uint8_t rp_priority, uint16_t rp_holdtime); BsrRp(BsrGroupPrefix& bsr_group_prefix, const BsrRp& bsr_rp); BsrGroupPrefix& bsr_group_prefix() { return (_bsr_group_prefix); } const IPvX& rp_addr() const { return (_rp_addr); } void set_rp_addr(const IPvX& v) { _rp_addr = v; } uint8_t rp_priority() const { return (_rp_priority); } uint16_t rp_holdtime() const { return (_rp_holdtime); } void set_rp_priority(uint8_t v) { _rp_priority = v; } void set_rp_holdtime(uint16_t v) { _rp_holdtime = v; } const XorpTimer& const_candidate_rp_expiry_timer() const { return (_candidate_rp_expiry_timer); } void start_candidate_rp_expiry_timer(); uint32_t my_vif_index() const { return (_my_vif_index); } void set_my_vif_index(uint32_t v) { _my_vif_index = v; } bool is_my_rp_addr_explicit() const { return (_is_my_rp_addr_explicit); } void set_is_my_rp_addr_explicit(bool v) { _is_my_rp_addr_explicit = v; } private: void candidate_rp_expiry_timer_timeout(); BsrGroupPrefix& _bsr_group_prefix; // The BSR prefix I belong to // RP state IPvX _rp_addr; // The address of the RP uint8_t _rp_priority; // RP priority (smaller is better) uint16_t _rp_holdtime; // RP holdtime (in seconds) XorpTimer _candidate_rp_expiry_timer; // The C-RP Expiry Timer uint32_t _my_vif_index; // The vif index with my address // if a Cand-RP bool _is_my_rp_addr_explicit; // True if my Cand-RP address was // set explicitly }; // // Global variables // // // Global functions prototypes // #endif // __PIM_PIM_BSR_HH__ xorp/pim/pim_mrt.cc0000664000076400007640000003076211635757530014447 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // PIM Multicast Routing Table implementation. // #include "pim_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvx.hh" #include "libxorp/utils.hh" #include "pim_mfc.hh" #include "pim_mre.hh" #include "pim_mre_task.hh" #include "pim_mrt.hh" #include "pim_node.hh" #include "pim_vif.hh" PimMrt::PimMrt(PimNode* pim_node) : _pim_node(pim_node), _pim_mrt_sg(*this), _pim_mrt_sg_rpt(*this), _pim_mrt_g(*this), _pim_mrt_rp(*this), _pim_mrt_mfc(*this), _pim_mre_track_state(this) { } PimMrt::~PimMrt() { clear(); } PimMrtSg::PimMrtSg(PimMrt& pim_mrt) : _pim_mrt(pim_mrt) { } PimMrtSg::~PimMrtSg() { } PimMrtG::PimMrtG(PimMrt& pim_mrt) : _pim_mrt(pim_mrt) { } PimMrtG::~PimMrtG() { } PimMrtRp::PimMrtRp(PimMrt& pim_mrt) : _pim_mrt(pim_mrt) { } PimMrtRp::~PimMrtRp() { } PimMrtMfc::PimMrtMfc(PimMrt& pim_mrt) : _pim_mrt(pim_mrt) { } PimMrtMfc::~PimMrtMfc() { } int PimMrt::family() const { return pim_node()->family(); } PimMribTable& PimMrt::pim_mrib_table() { return (pim_node()->pim_mrib_table()); } Mifset& PimMrt::i_am_dr() { return pim_node()->pim_vifs_dr(); } PimVif * PimMrt::vif_find_by_vif_index(uint32_t vif_index) { return (pim_node()->vif_find_by_vif_index(vif_index)); } PimVif * PimMrt::vif_find_pim_register() { return (pim_node()->vif_find_pim_register()); } uint32_t PimMrt::pim_register_vif_index() const { return (pim_node()->pim_register_vif_index()); } void PimMrt::clear() { delete_pointers_list(_pim_mre_task_list); _pim_mrt_mfc.clear(); _pim_mrt_sg.clear(); _pim_mrt_sg_rpt.clear(); _pim_mrt_g.clear(); _pim_mrt_rp.clear(); } // // XXX: @create_flags must be a subset of @lookup_flags // XXX: if no creation allowed, the entry that it returns may be the // next one in the map. // // XXX: if the entry to lookup and/or create is (*,*,RP), then: // - If group is IPvX::ZERO(family()), then we lookup/create the // entry by using 'source' which is the RP address // - If group is NOT IPvX::ZERO(family()), then we lookup/create the // entry by lookup first which is the RP for 'group'. // - Regardless of the group address, the created (*,*,RP) entry // always has a group address of IPvX::MULTICAST_BASE(family()) PimMre * PimMrt::pim_mre_find(const IPvX& source, const IPvX& group, uint32_t lookup_flags, uint32_t create_flags) { PimMre *pim_mre = NULL; create_flags &= lookup_flags; // // Try to lookup if entry was installed already. // XXX: the order is important, because we want the longest-match. // do { if (lookup_flags & PIM_MRE_SG) { // // (S,G) entry // pim_mre = _pim_mrt_sg.find(source, group); if (pim_mre != NULL) break; } if (lookup_flags & PIM_MRE_SG_RPT) { // // (S,G,rpt) entry // pim_mre = _pim_mrt_sg_rpt.find(source, group); if (pim_mre != NULL) break; } if (lookup_flags & PIM_MRE_WC) { // // (*,G) entry // pim_mre = _pim_mrt_g.find(IPvX::ZERO(family()), group); if (pim_mre != NULL) break; } if (lookup_flags & PIM_MRE_RP) { // // (*,*,RP) entry // if (group == IPvX::ZERO(family())) { // XXX: the entry is specified by the RP address ('source') pim_mre = _pim_mrt_rp.find(source, IPvX::MULTICAST_BASE(family())); if (pim_mre != NULL) break; } else { // XXX: the entry is specified by the group address PimRp *pim_rp = pim_node()->rp_table().rp_find(group); if (pim_rp != NULL) pim_mre = _pim_mrt_rp.find(pim_rp->rp_addr(), IPvX::MULTICAST_BASE(family())); } if (pim_mre != NULL) break; } } while (false); if (pim_mre != NULL) return (pim_mre); // // Lookup failed. Create the entry if creation is allowed. // // If creation allowed, create the entry, insert it and return it. // XXX: the order is important, because we want the longest-match. do { if (create_flags & (PIM_MRE_SG)) { // // (S,G) entry // // Create and insert the entry pim_mre = new PimMre(this, source, group); pim_mre->set_sg(true); pim_mre = _pim_mrt_sg.insert(pim_mre); // Set the pointer to the corresponding (*,G) entry (if exists); pim_mre->set_wc_entry(pim_mre_find(source, group, PIM_MRE_WC, 0)); // Set the pointer to the corresponding (S,G,rpt) entry // (if exists), and vice-versa PimMre *pim_mre_sg_rpt = pim_mre_find(source, group, PIM_MRE_SG_RPT, 0); pim_mre->set_sg_rpt_entry(pim_mre_sg_rpt); if (pim_mre_sg_rpt != NULL) pim_mre_sg_rpt->set_sg_entry(pim_mre); // Compute and set the RP-related state if (pim_mre->wc_entry() != NULL) pim_mre->uncond_set_pim_rp(pim_mre->wc_entry()->pim_rp()); else pim_mre->uncond_set_pim_rp(pim_mre->compute_rp()); // Compute and set the MRIB and RPF-related state pim_mre->set_mrib_rp(pim_mre->compute_mrib_rp()); pim_mre->set_mrib_s(pim_mre->compute_mrib_s()); pim_mre->set_nbr_mrib_next_hop_s(pim_mre->compute_nbr_mrib_next_hop_s()); pim_mre->set_rpfp_nbr_sg(pim_mre->compute_rpfp_nbr_sg()); if ((pim_mre->nbr_mrib_next_hop_s() == NULL) || (pim_mre->rpfp_nbr_sg() == NULL)) { pim_node()->add_pim_mre_no_pim_nbr(pim_mre); } // Set source-related state bool v = pim_mre->compute_is_directly_connected_s(); pim_mre->set_directly_connected_s(v); // Add a task to handle any CPU-intensive operations // XXX: not needed for this entry. // add_task_add_pim_mre(pim_mre); break; } if (create_flags & PIM_MRE_SG_RPT) { // // (S,G,rpt) entry // // Create and insert the entry pim_mre = new PimMre(this, source, group); pim_mre->set_sg_rpt(true); pim_mre = _pim_mrt_sg_rpt.insert(pim_mre); // Set the pointer to the corresponding (*,G) entry (if exists); pim_mre->set_wc_entry(pim_mre_find(source, group, PIM_MRE_WC, 0)); // Set the pointer to the corresponding (S,G) entry // (if exists), and vice-versa PimMre *pim_mre_sg = pim_mre_find(source, group, PIM_MRE_SG, 0); pim_mre->set_sg_entry(pim_mre_sg); if (pim_mre_sg != NULL) pim_mre_sg->set_sg_rpt_entry(pim_mre); // Compute and set the RP-related state if (pim_mre->wc_entry() != NULL) pim_mre->uncond_set_pim_rp(pim_mre->wc_entry()->pim_rp()); else pim_mre->uncond_set_pim_rp(pim_mre->compute_rp()); // Compute and set the MRIB and RPF-related state pim_mre->set_mrib_rp(pim_mre->compute_mrib_rp()); pim_mre->set_mrib_s(pim_mre->compute_mrib_s()); pim_mre->set_rpfp_nbr_sg_rpt(pim_mre->compute_rpfp_nbr_sg_rpt()); if (pim_mre->rpfp_nbr_sg_rpt() == NULL) { pim_node()->add_pim_mre_no_pim_nbr(pim_mre); } // Set source-related state bool v = pim_mre->compute_is_directly_connected_s(); pim_mre->set_directly_connected_s(v); // Set the starting state in the upstream state machine if (pim_mre->is_rpt_join_desired_g()) pim_mre->set_not_pruned_state(); else pim_mre->set_rpt_not_joined_state(); // Add a task to handle any CPU-intensive operations // XXX: not needed for this entry. // add_task_add_pim_mre(pim_mre); break; } if (create_flags & PIM_MRE_WC) { // // (*,G) entry // // Create and insert the entry pim_mre = new PimMre(this, IPvX::ZERO(family()), group); pim_mre->set_wc(true); pim_mre = _pim_mrt_g.insert(pim_mre); // Compute and set the RP-related state pim_mre->uncond_set_pim_rp(pim_mre->compute_rp()); // Compute and set the MRIB and RPF-related state pim_mre->set_mrib_rp(pim_mre->compute_mrib_rp()); pim_mre->set_nbr_mrib_next_hop_rp(pim_mre->compute_nbr_mrib_next_hop_rp()); pim_mre->set_rpfp_nbr_wc(pim_mre->compute_rpfp_nbr_wc()); if ((pim_mre->nbr_mrib_next_hop_rp() == NULL) || (pim_mre->rpfp_nbr_wc() == NULL)) { pim_node()->add_pim_mre_no_pim_nbr(pim_mre); } // Add a task to handle any CPU-intensive operations // This task will assign the wc_entry() pointer for // all (S,G) and (S,G,rpt) entries. add_task_add_pim_mre(pim_mre); break; } if (create_flags & PIM_MRE_RP) { // // (*,*,RP) entry // // Create and insert the entry if (group == IPvX::ZERO(family())) { // XXX: the entry is specified by the RP address pim_mre = new PimMre(this, source, IPvX::MULTICAST_BASE(family())); } else { // XXX: the entry is specified by the group address PimRp *pim_rp = pim_node()->rp_table().rp_find(group); if (pim_rp != NULL) pim_mre = new PimMre(this, pim_rp->rp_addr(), IPvX::MULTICAST_BASE(family())); } if (pim_mre == NULL) break; pim_mre->set_rp(true); pim_mre = _pim_mrt_rp.insert(pim_mre); // Compute and set the RP-related state if (pim_node()->is_my_addr(*pim_mre->rp_addr_ptr())) pim_mre->set_i_am_rp(true); else pim_mre->set_i_am_rp(false); // Compute and set the MRIB and RPF-related state pim_mre->set_mrib_rp(pim_mre->compute_mrib_rp()); pim_mre->set_nbr_mrib_next_hop_rp(pim_mre->compute_nbr_mrib_next_hop_rp()); if (pim_mre->nbr_mrib_next_hop_rp() == NULL) { pim_node()->add_pim_mre_no_pim_nbr(pim_mre); } // Add a task to handle any CPU-intensive operations // XXX: not needed for this entry. // add_task_add_pim_mre(pim_mre); // XXX: the rp_entry() pointer for all related (*,G) entries // will be setup by PimMre::set_pim_rp(), which itself // will be called indirectly when a task updating the related (*,G) // entries has been scheduled. This task will be scheduled // whenever the RP-Set has been changed. Note that this will // work because (*,*,RP) entries are always created whenever // a new RP is added to the RP-Set. Thus, all (*,G) entries // for that RP are "assigned" to that RP _after_ the (*,*,RP) // entry is created. break; } } while (false); return (pim_mre); } // XXX: if @is_creation_allowed is true, the entry will be created if it did // not exist before. PimMfc * PimMrt::pim_mfc_find(const IPvX& source, const IPvX& group, bool is_creation_allowed) { PimMfc *pim_mfc = NULL; // // Try to lookup if entry was installed already. // pim_mfc = _pim_mrt_mfc.find(source, group); if (pim_mfc != NULL) return (pim_mfc); // // Lookup failed. Create the entry if creation is allowed. // if (is_creation_allowed) { // Create and insert the entry pim_mfc = new PimMfc(this, source, group); pim_mfc = _pim_mrt_mfc.insert(pim_mfc); // Compute and set the RP-related state PimRp *pim_rp = pim_node()->rp_table().rp_find(group); if (pim_rp != NULL) pim_mfc->uncond_set_rp_addr(pim_rp->rp_addr()); else pim_mfc->uncond_set_rp_addr(IPvX::ZERO(family())); } return (pim_mfc); } int PimMrt::remove_pim_mre(PimMre *pim_mre) { int ret_value = XORP_ERROR; if (pim_mre->is_sg()) { ret_value = _pim_mrt_sg.remove(pim_mre); return (ret_value); } if (pim_mre->is_sg_rpt()) { ret_value = _pim_mrt_sg_rpt.remove(pim_mre); return (ret_value); } if (pim_mre->is_wc()) { ret_value = _pim_mrt_g.remove(pim_mre); return (ret_value); } if (pim_mre->is_rp()) { ret_value = _pim_mrt_rp.remove(pim_mre); return (ret_value); } return (ret_value); } int PimMrt::remove_pim_mfc(PimMfc *pim_mfc) { int ret_value = _pim_mrt_mfc.remove(pim_mfc); return (ret_value); } xorp/pim/command_pim0000775000076400007640000000222111421137511014647 0ustar greearbgreearb#!/bin/sh # # $XORP: xorp/pim/command_pim,v 1.8 2003/10/15 19:02:07 pavlin Exp $ # # # Send commands to a running PIM process. # MODULE_NAME="PIM-SM" HOSTNAME=`hostname` echo "Sending command to $MODULE_NAME on $HOSTNAME..." # Conditionally set ${srcdir} if it wasn't assigned (e.g., by `gmake check`) if [ "X${srcdir}" = "X" ] ; then srcdir=`dirname $0` ; fi # # The optional first argument should be either -4 or -6 to specify # IPv4 or IPv6 command # # The next argument should be the command to call. # The rest of the arguments should be the arguments to the command to call. # usage() { echo "Usage: $0 [-4 | -6 ] [xrl_command_arguments ... ]" } if [ $# -lt 1 ] ; then usage; exit 1 fi case "${1}" in -4) if [ $# -lt 2 ] ; then usage; exit 1 fi IP_VERSION=IPV4 shift ;; -6) if [ $# -lt 2 ] ; then usage; exit 1 fi IP_VERSION=IPV6 shift ;; *) # XXX: IP version defaults to IPv4 IP_VERSION=IPV4 ;; esac . ${srcdir}/../cli/xrl_cli_shell_funcs.sh . ${srcdir}/../fea/xrl_mfea_shell_funcs.sh . ${srcdir}/../mld6igmp/xrl_mld6igmp_shell_funcs.sh . ${srcdir}/../pim/xrl_pim_shell_funcs.sh $* exit $? xorp/pim/pim_node.hh0000664000076400007640000015261311637171153014575 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __PIM_PIM_NODE_HH__ #define __PIM_PIM_NODE_HH__ // // PIM node definition. // #include "libxorp/xorp.h" #include "libxorp/vif.hh" #include "libproto/proto_node.hh" #include "libfeaclient/ifmgr_xrl_mirror.hh" #include "mrt/buffer.h" #include "mrt/mifset.hh" #include "pim_bsr.hh" #include "pim_mrib_table.hh" #include "pim_mrt.hh" #include "pim_rp.hh" #include "pim_scope_zone_table.hh" #include "pim_vif.hh" class EventLoop; class IPvX; class IPvXNet; class PimMrt; class PimNbr; class PimVif; /** * @short The PIM node class. * * There should be one node per PIM instance. There should be * one instance per address family. */ class PimNode : public ProtoNode, public IfMgrHintObserver, public ServiceChangeObserverBase { public: /** * Constructor for a given address family, module ID, and event loop. * * @param family the address family (AF_INET or AF_INET6 for * IPv4 and IPv6 respectively). * @param module_id the module ID (@ref xorp_module_id). Should be * XORP_MODULE_PIMSM Note: if/after PIM-DM is implemented, * XORP_MODULE_PIMDM would be allowed as well. * @param eventloop the event loop to use. */ PimNode(int family, xorp_module_id module_id, EventLoop& eventloop); /** * Destructor */ virtual ~PimNode(); /** Does manual destruction work */ virtual void destruct_me(); /** * Start the node operation. * * Start the PIM protocol. * After the startup operations are completed, * @ref PimNode::final_start() is called internally * to complete the job. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int start(); /** * Stop the node operation. * * Gracefully stop the PIM protocol. * The graceful stop will attempt to send Join/Prune, Assert, etc. * messages for all multicast routing entries to gracefully clean-up * state with neighbors. * After the multicast routing entries cleanup is completed, * @ref PimNode::final_stop() is called internally to complete the job. * This method, unlike start(), will stop the protocol * operation on all interfaces. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int stop(); /** * Completely start the node operation. * * This method should be called internally after @ref PimNode::start() * to complete the job. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int final_start(); /** * Completely stop the node operation. * * This method should be called internally after @ref PimNode::stop() * to complete the job. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int final_stop(); /** * Enable node operation. * * If an unit is not enabled, it cannot be start, or pending-start. */ void enable(); /** * Disable 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 disable(); /** * Get the IP protocol number. * * @return the IP protocol number. */ uint8_t ip_protocol_number() const; /** * Install a new PIM vif. * * @param vif vif information about the new PimVif to install. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_vif(const Vif& vif, string& error_msg); /** * Install a new PIM vif. * * @param vif_name the name of the new vif. * @param vif_index the vif index of the new vif. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_vif(const string& vif_name, uint32_t vif_index, string& error_msg); /** * Delete an existing PIM vif. * * @param vif_name the name of the vif to delete. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int delete_vif(const string& vif_name, string& error_msg); /** * Set flags to a vif. * * @param vif_name the name of the vif. * @param is_pim_register true if this is a PIM Register vif. * @param is_p2p true if this is a point-to-point vif. * @param is_loopback true if this is a loopback interface. * @param is_multicast rue if the vif is multicast-capable. * @param is_broadcast true if the vif is broadcast-capable. * @param is_up true if the vif is UP and running. * @param mtu the MTU of the vif. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_vif_flags(const string& vif_name, bool is_pim_register, bool is_p2p, bool is_loopback, bool is_multicast, bool is_broadcast, bool is_up, uint32_t mtu, string& error_msg); /** * Add a new address to a vif, or update an existing address. * * @param vif_name the name of the vif. * @param addr the unicast address to add. * @param subnet_addr the subnet address to add. * @param broadcast_addr the broadcast address (when applicable). * @param peer_addr the peer address (when applicable). * @param should_send_pim_hello a return-by-reference flag that is set to * true if the caller should send a PIM Hello message. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_vif_addr(const string& vif_name, const IPvX& addr, const IPvXNet& subnet_addr, const IPvX& broadcast_addr, const IPvX& peer_addr, bool& should_send_pim_hello, string& error_msg); /** * Delete an address from a vif. * * @param vif_name the name of the vif. * @param addr the unicast address to delete. * @param should_send_pim_hello a return-by-reference flag that is set to * true if the caller should send a PIM Hello message. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int delete_vif_addr(const string& vif_name, const IPvX& addr, bool& should_send_pim_hello, string& error_msg); /** * Enable an existing PIM vif. * * @param vif_name the name of the vif to enable. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int enable_vif(const string& vif_name, string& error_msg); /** * Disable an existing PIM vif. * * @param vif_name the name of the vif to disable. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int disable_vif(const string& vif_name, string& error_msg); /** * Start an existing PIM vif. * * @param vif_name the name of the vif to start. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int start_vif(const string& vif_name, string& error_msg); /** * Stop an existing PIM vif. * * @param vif_name the name of the vif to start. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int stop_vif(const string& vif_name, string& error_msg); /** * Start PIM on all enabled interfaces. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int start_all_vifs(); /** * Stop PIM on all interfaces it was running on. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int stop_all_vifs(); /** * Enable PIM on all interfaces. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int enable_all_vifs(); /** * Disable PIM on all interfaces. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int disable_all_vifs(); /** * Delete all PIM vifs. */ void delete_all_vifs(); /** * A method called when a vif has completed its shutdown. * * @param vif_name the name of the vif that has completed its shutdown. */ void vif_shutdown_completed(const string& vif_name); /** * Receive a protocol packet. * * @param if_name the interface name the packet arrived on. * @param vif_name the vif name the packet arrived on. * @param src_address the IP source address. * @param dst_address the IP destination address. * @param ip_protocol the IP protocol number. * @param ip_ttl the IP TTL (hop-limit). If it has a negative value, then * the received value is unknown. * @param ip_tos the Type of Service (Diffserv/ECN bits for IPv4). If it * has a negative value, then the received value is unknown. * @param ip_router_alert if true, the IP Router Alert option was included * in the IP packet. * @param ip_internet_control if true, then this is IP control traffic. * @param payload the payload, everything after the IP header and options. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int proto_recv(const string& if_name, const string& vif_name, const IPvX& src_address, const IPvX& dst_address, uint8_t ip_protocol, int32_t ip_ttl, int32_t ip_tos, bool ip_router_alert, bool ip_internet_control, const vector& payload, string& error_msg); /** * Send a protocol packet. * * @param if_name the interface to send the packet on. It is essential for * multicast. In the unicast case this field may be empty. * @param vif_name the vif to send the packet on. It is essential for * multicast. In the unicast case this field may be empty. * @param src_address the IP source address. * @param dst_address the IP destination address. * @param ip_protocol the IP protocol number. It must be between 1 and * 255. * @param ip_ttl the IP TTL (hop-limit). If it has a negative value, the * TTL will be set internally before transmission. * @param ip_tos the Type Of Service (Diffserv/ECN bits for IPv4). If it * has a negative value, the TOS will be set internally before * transmission. * @param ip_router_alert if true, then add the IP Router Alert option to * the IP packet. * @param ip_internet_control if true, then this is IP control traffic. * @param buffer the data buffer with the packet to send. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int pim_send(const string& if_name, const string& vif_name, const IPvX& src_address, const IPvX& dst_address, uint8_t ip_protocol, int32_t ip_ttl, int32_t ip_tos, bool ip_router_alert, bool ip_internet_control, buffer_t *buffer, string& error_msg); /** * Receive a signal message from the kernel. * * @param src_module_instance_name the module instance name of the * module-origin of the message. * * @param message_type the message type. * Currently, the type of messages received from the kernel are: *
#define MFEA_KERNEL_MESSAGE_NOCACHE        1
#define MFEA_KERNEL_MESSAGE_WRONGVIF       2
#define MFEA_KERNEL_MESSAGE_WHOLEPKT       3
* * * @param vif_index the vif index of the related interface * (message-specific relation). * * @param src the source address in the message. * * @param dst the destination address in the message. * * @param rcvbuf the data buffer with the additional information in * the message. * @param rcvlen the data length in @ref rcvbuf. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int signal_message_recv(const string& src_module_instance_name, int message_type, uint32_t vif_index, const IPvX& src, const IPvX& dst, const uint8_t *rcvbuf, size_t rcvlen); /** * Send signal message: not used by PIM. */ int signal_message_send(const string&, // dst_module_instance_name, int , // message_type, uint32_t , // vif_index, const IPvX& , // src, const IPvX& , // dst, const uint8_t * , // sndbuf, size_t // sndlen ) { XLOG_UNREACHABLE(); return (XORP_ERROR); } /** * Register as a receiver to receive packets. * * This is a pure virtual function, and it must be implemented * by the communication-wrapper class that inherits this base class. * * @param if_name the interface through which packets should be accepted. * @param vif_name the vif through which packets should be accepted. * @param ip_protocol the IP protocol number that the receiver is * interested in. It must be between 0 and 255. A protocol number of 0 is * used to specify all protocols. * @param enable_multicast_loopback if true then enable delivering * of multicast datagrams back to this host (assuming the host is * a member of the same multicast group). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int register_receiver(const string& if_name, const string& vif_name, uint8_t ip_protocol, bool enable_multicast_loopback) = 0; /** * Unregister as a receiver to receive packets. * * This is a pure virtual function, and it must be implemented * by the communication-wrapper class that inherits this base class. * * @param if_name the interface through which packets should not be * accepted. * @param vif_name the vif through which packets should not be accepted. * @param ip_protocol the IP Protocol number that the receiver is * not interested in anymore. It must be between 0 and 255. A protocol * number of 0 is used to specify all protocols. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int unregister_receiver(const string& if_name, const string& vif_name, uint8_t ip_protocol) = 0; /** * Register a protocol on an interface in the Multicast FEA. * * @param if_name the name of the interface to register for the * particular protocol. * @param vif_name the name of the vif to register for the * particular protocol. * @param ip_protocol the IP protocol number. It must be between * 1 and 255. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int register_protocol(const string& if_name, const string& vif_name, uint8_t ip_protocol) = 0; /** * Unregister a protocol on an interface in the Multicast FEA. * * @param if_name the name of the interface to unregister for the * particular protocol. * @param vif_name the name of the vif to unregister for the * particular protocol. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int unregister_protocol(const string& if_name, const string& vif_name) = 0; /** * Join a multicast group on an interface. * * This is a pure virtual function, and it must be implemented * by the communication-wrapper class that inherits this base class. * * TODO: add a source address as well!! * * @param if_name the interface name to join. * @param vif_name the vif name to join. * @param ip_protocol the IP protocol number that the receiver is * interested in. * @param group_address the multicast group address to join. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int join_multicast_group(const string& if_name, const string& vif_name, uint8_t ip_protocol, const IPvX& group_address) = 0; /** * Leave a multicast group on an interface. * * This is a pure virtual function, and it must be implemented * by the communication-wrapper class that inherits this base class. * * TODO: add a source address as well!! * * @param if_name the interface name to leave. * @param vif_name the vif name to leave. * @param ip_protocol the IP protocol number that the receiver is * not interested in anymore. * @param group_address the multicast group address to leave. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int leave_multicast_group(const string& if_name, const string& vif_name, uint8_t ip_protocol, const IPvX& group_address) = 0; /** * Add a Multicast Forwarding Cache to the kernel. * * This is a pure virtual function, and it must be implemented * by the communication-wrapper class that inherits this base class. * * @param pim_mfc the @ref PimMfc entry to add. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int add_mfc_to_kernel(const PimMfc& pim_mfc) = 0; /** * Delete a Multicast Forwarding Cache to the kernel. * * This is a pure virtual function, and it must be implemented * by the communication-wrapper class that inherits this base class. * * @param pim_mfc the @ref PimMfc entry to delete. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int delete_mfc_from_kernel(const PimMfc& pim_mfc) = 0; /** * Add a dataflow monitor to the MFEA. * * This is a pure virtual function, and it must be implemented * by the communication-wrapper class that inherits this base class. * * Note: either @ref is_threshold_in_packets or @ref is_threshold_in_bytes * (or both) must be true. * Note: either @ref is_geq_upcall or @ref is_leq_upcall * (but not both) must be true. * * @param source the source address. * * @param group the group address. * * @param threshold_interval_sec the dataflow threshold interval * (seconds). * * @param threshold_interval_usec the dataflow threshold interval * (microseconds). * * @param threshold_packets the threshold (in number of packets) to * compare against. * * @param threshold_bytes the threshold (in number of bytes) to * compare against. * * @param is_threshold_in_packets if true, @ref threshold_packets is valid. * * @param is_threshold_in_bytes if true, @ref threshold_bytes is valid. * * @param is_geq_upcall if true, the operation for comparison is ">=". * * @param is_leq_upcall if true, the operation for comparison is "<=". * * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int add_dataflow_monitor(const IPvX& source_addr, const IPvX& group_addr, uint32_t threshold_interval_sec, uint32_t threshold_interval_usec, 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) = 0; /** * Delete a dataflow monitor from the MFEA. * * This is a pure virtual function, and it must be implemented * by the communication-wrapper class that inherits this base class. * * Note: either @ref is_threshold_in_packets or @ref is_threshold_in_bytes * (or both) must be true. * Note: either @ref is_geq_upcall or @ref is_leq_upcall * (but not both) must be true. * * @param source the source address. * * @param group the group address. * * @param threshold_interval_sec the dataflow threshold interval * (seconds). * * @param threshold_interval_usec the dataflow threshold interval * (microseconds). * * @param threshold_packets the threshold (in number of packets) to * compare against. * * @param threshold_bytes the threshold (in number of bytes) to * compare against. * * @param is_threshold_in_packets if true, @ref threshold_packets is valid. * * @param is_threshold_in_bytes if true, @ref threshold_bytes is valid. * * @param is_geq_upcall if true, the operation for comparison is ">=". * * @param is_leq_upcall if true, the operation for comparison is "<=". * * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int delete_dataflow_monitor(const IPvX& source_addr, const IPvX& group_addr, uint32_t threshold_interval_sec, uint32_t threshold_interval_usec, 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) = 0; /** * Delete all dataflow monitors for a given source and group address from * the MFEA. * * This is a pure virtual function, and it must be implemented * by the communication-wrapper class that inherits this base class. * * @param source_addr the source address. * @param group_addr the group address. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int delete_all_dataflow_monitor(const IPvX& source_addr, const IPvX& group_addr) = 0; /** * Register this protocol with the MLD/IGMP module. * * This is a pure virtual function, and it must be implemented * by the communication-wrapper class that inherits this base class. * * By registering this protocol with the MLD/IGMP module, it will * be notified about multicast group membership events. * * @param vif_index the vif index of the interface to register with * the MLD/IGMP module. * * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int add_protocol_mld6igmp(uint32_t vif_index) = 0; /** * Deregister this protocol with the MLD/IGMP module. * * This is a pure virtual function, and it must be implemented * by the communication-wrapper class that inherits this base class. * * @param vif_index the vif index of the interface to deregister with * the MLD/IGMP module. * * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int delete_protocol_mld6igmp(uint32_t vif_index) = 0; /** * Receive "add membership" from the MLD/IGMP module. * * @param vif_index the vif index of the interface with membership change. * @param source the source address of the (S,G) or (*,G) entry that has * changed membership. In case of Any-Source Multicast, it is IPvX::ZERO(). * @param group the group address. * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_membership(uint32_t vif_index, const IPvX& source, const IPvX& group); /** * Receive "delete membership" from the MLD/IGMP module. * * @param vif_index the vif index of the interface with membership change. * @param source the source address of the (S,G) or (*,G) entry that has * changed membership. In case of Any-Source Multicast, it is IPvX::ZERO(). * @param group the group address. * @return XORP_OK on success, otherwise XORP_ERROR. */ int delete_membership(uint32_t vif_index, const IPvX& source, const IPvX& group); /** * Test if an address is directly connected to a specified virtual * interface. * * Note that the virtual interface the address is directly connected to * must be UP. * * @param pim_vif the virtual interface to test against. * @param ipaddr_test the address to test. * @return true if @ref ipaddr_test is directly connected to @ref vif, * otherwise false. */ bool is_directly_connected(const PimVif& pim_vif, const IPvX& ipaddr_test) const; /** * Get the PIM-Register virtual interface. * * @return the PIM-Register virtual interface if exists, otherwise NULL. */ PimVif *vif_find_pim_register() const; /** * Get the vif index of the PIM-Register virtual interface. * * @return the vif index of the PIM-Register virtual interface if exists, * otherwise @ref Vif::VIF_INDEX_INVALID. */ uint32_t pim_register_vif_index() const { return (_pim_register_vif_index); } /** * Get the PIM Multicast Routing Table. * * @return a reference to the PIM Multicast Routing Table (@ref PimMrt). */ PimMrt& pim_mrt() { return _pim_mrt; } /** * Get the table with the Multicast Routing Information Base used by PIM. * * @return a reference to the table with the Multicast Routing Information * Base used by PIM (@ref PimMribTable). */ PimMribTable& pim_mrib_table() { return (_pim_mrib_table); } /** * Get the PIM Bootstrap entity. * * @return a reference to the PIM Bootstrap entity (@ref PimBsr). */ PimBsr& pim_bsr() { return (_pim_bsr); } /** * Get the PIM RP table. * * @return a reference to the PIM RP table (@ref RpTable). */ RpTable& rp_table() { return (_rp_table); } /** * Get the PIM Scope-Zone table. * * @return a reference to the PIM Scope-Zone table. */ PimScopeZoneTable& pim_scope_zone_table() { return (_pim_scope_zone_table); } /** * Get the set of vifs for which this PIM note is a Designated Router. * * @return the @ref Mifset indicating the vifs for which this PIM node is * a Designated Router. */ Mifset& pim_vifs_dr() { return (_pim_vifs_dr); } /** * Set/reset a virtual interface as a Designated Router. * * @param vif_index the vif index of the virtual interface to set/reset as * a Designated Router. * @param v if true, set the virtual interface as a Designated Router, * otherwise reset it. */ void set_pim_vifs_dr(uint32_t vif_index, bool v); /** * Find the RPF virtual interface for a given destination address. * * @param dst_addr the destination address to lookup. * @return the RPF virtual interface (@ref PimVif) toward @ref dst_addr * if found, otherwise NULL. */ PimVif *pim_vif_rpf_find(const IPvX& dst_addr); /** * Find the RPF PIM neighbor for a given destination address. * * @param dst_addr the destination address to lookup. * @return the RPF PIM neighbor (@ref PimNbr) toward @ref dst_addr * if found, otherwise NULL. */ PimNbr *pim_nbr_rpf_find(const IPvX& dst_addr); /** * Find the RPF PIM neighbor for a given destination address, and * already known @ref Mrib entry. * * @param dst_addr the destination address to lookup. * @param mrib the already known @ref Mrib entry. * @return the RPF PIM neighbor (@ref PimNbr) toward @ref dst_addr * if found, otherwise NULL. */ PimNbr *pim_nbr_rpf_find(const IPvX& dst_addr, const Mrib *mrib); /** * Find a PIM neighbor by its address. * * Note: this method should be used in very limited cases, because * in case of IPv6 a neighbor's IP address may be non-unique within * the PIM neighbor database due to scope issues. * * @param nbr_addr the address of the PIM neighbor. * @return the PIM neighbor (@ref PimNbr) if found, otherwise NULL. */ PimNbr *pim_nbr_find_global(const IPvX& nbr_addr); /** * Enable the PIM Bootstrap mechanism. */ void enable_bsr() { pim_bsr().enable(); } /** * Disable the PIM Bootstrap mechanism. */ void disable_bsr() { pim_bsr().disable(); } /** * Start the Bootstrap mechanism. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int start_bsr() { return (pim_bsr().start()); } /** * Stop the Bootstrap mechanism. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int stop_bsr() { return (pim_bsr().stop()); } /** * Apply BSR configuration changes. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int apply_bsr_changes(string& error_msg) { return (pim_bsr().apply_bsr_changes(error_msg)); } // // Configuration methods // /** * Complete the set of vif configuration changes. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_config_all_vifs_done(string& error_msg); /** * Get the protocol version on an interface. * * @param vif_name the name of the vif to get the protocol version of. * @param proto_version the return-by-reference protocol version. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int get_vif_proto_version(const string& vif_name, int& proto_version, string& error_msg); /** * Set the protocol version on an interface. * * @param vif_name the name of the vif to set the protocol version of. * @param proto_version the new protocol version. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_vif_proto_version(const string& vif_name, int proto_version, string& error_msg); /** * Reset the protocol version on an interface to its default value. * * @param vif_name the name of the vif to reset the protocol version of * to its default value. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int reset_vif_proto_version(const string& vif_name, string& error_msg); // int get_vif_hello_triggered_delay(const string& vif_name, uint16_t& hello_triggered_delay, string& error_msg); int set_vif_hello_triggered_delay(const string& vif_name, uint16_t hello_triggered_delay, string& error_msg); int reset_vif_hello_triggered_delay(const string& vif_name, string& error_msg); // int get_vif_hello_period(const string& vif_name, uint16_t& hello_period, string& error_msg); int set_vif_hello_period(const string& vif_name, uint16_t hello_period, string& error_msg); int reset_vif_hello_period(const string& vif_name, string& error_msg); // int get_vif_hello_holdtime(const string& vif_name, uint16_t& hello_holdtime, string& error_msg); int set_vif_hello_holdtime(const string& vif_name, uint16_t hello_holdtime, string& error_msg); int reset_vif_hello_holdtime(const string& vif_name, string& error_msg); // int get_vif_dr_priority(const string& vif_name, uint32_t& dr_priority, string& error_msg); int set_vif_dr_priority(const string& vif_name, uint32_t dr_priority, string& error_msg); int reset_vif_dr_priority(const string& vif_name, string& error_msg); // int get_vif_propagation_delay(const string& vif_name, uint16_t& propagation_delay, string& error_msg); int set_vif_propagation_delay(const string& vif_name, uint16_t propagation_delay, string& error_msg); int reset_vif_propagation_delay(const string& vif_name, string& error_msg); // int get_vif_override_interval(const string& vif_name, uint16_t& override_interval, string& error_msg); int set_vif_override_interval(const string& vif_name, uint16_t override_interval, string& error_msg); int reset_vif_override_interval(const string& vif_name, string& error_msg); // int get_vif_is_tracking_support_disabled(const string& vif_name, bool& is_tracking_support_disabled, string& error_msg); int set_vif_is_tracking_support_disabled(const string& vif_name, bool is_tracking_support_disabled, string& error_msg); int reset_vif_is_tracking_support_disabled(const string& vif_name, string& error_msg); // int get_vif_accept_nohello_neighbors(const string& vif_name, bool& accept_nohello_neighbors, string& error_msg); int set_vif_accept_nohello_neighbors(const string& vif_name, bool accept_nohello_neighbors, string& error_msg); int reset_vif_accept_nohello_neighbors(const string& vif_name, string& error_msg); // int get_vif_join_prune_period(const string& vif_name, uint16_t& join_prune_period, string& error_msg); int set_vif_join_prune_period(const string& vif_name, uint16_t join_prune_period, string& error_msg); int reset_vif_join_prune_period(const string& vif_name, string& error_msg); // int get_switch_to_spt_threshold(bool& is_enabled, uint32_t& interval_sec, uint32_t& bytes, string& error_msg); int set_switch_to_spt_threshold(bool is_enabled, uint32_t interval_sec, uint32_t bytes, string& error_msg); int reset_switch_to_spt_threshold(string& error_msg); // int add_config_scope_zone_by_vif_name(const IPvXNet& scope_zone_id, const string& vif_name, string& error_msg); int add_config_scope_zone_by_vif_addr(const IPvXNet& scope_zone_id, const IPvX& vif_addr, string& error_msg); int delete_config_scope_zone_by_vif_name(const IPvXNet& scope_zone_id, const string& vif_name, string& error_msg); int delete_config_scope_zone_by_vif_addr(const IPvXNet& scope_zone_id, const IPvX& vif_addr, string& error_msg); // int add_config_cand_bsr(const IPvXNet& scope_zone_id, bool is_scope_zone, const string& vif_name, const IPvX& vif_addr, uint8_t bsr_priority, uint8_t hash_mask_len, string& error_msg); int delete_config_cand_bsr(const IPvXNet& scope_zone_id, bool is_scope_zone, string& error_msg); int add_config_cand_rp(const IPvXNet& group_prefix, bool is_scope_zone, const string& vif_name, const IPvX& vif_addr, uint8_t rp_priority, uint16_t rp_holdtime, string& error_msg); int delete_config_cand_rp(const IPvXNet& group_prefix, bool is_scope_zone, const string& vif_name, const IPvX& vif_addr, string& error_msg); int add_config_static_rp(const IPvXNet& group_prefix, const IPvX& rp_addr, uint8_t rp_priority, uint8_t hash_mask_len, string& error_msg); int delete_config_static_rp(const IPvXNet& group_prefix, const IPvX& rp_addr, string& error_msg); int delete_config_all_static_group_prefixes_rp(const IPvX& rp_addr, string& error_msg); int delete_config_all_static_rps(string& error_msg); int config_static_rp_done(string& error_msg); int add_alternative_subnet(const string& vif_name, const IPvXNet& subnet, string& error_msg); int delete_alternative_subnet(const string& vif_name, const IPvXNet& subnet, string& error_msg); int remove_all_alternative_subnets(const string& vif_name, string& error_msg); // // Debug-related methods // /** * Test if trace log is enabled. * * This method is used to test whether to output trace log debug messges. * * @return true if trace log is enabled, otherwise false. */ bool is_log_trace() const { return (_is_log_trace); } /** * Enable/disable trace log. * * This method is used to enable/disable trace log debug messages output. * * @param is_enabled if true, trace log is enabled, otherwise is disabled. */ void set_log_trace(bool is_enabled) { _is_log_trace = is_enabled; } // // Test-related methods // // Join/Prune test-related methods int add_test_jp_entry(const IPvX& source_addr, const IPvX& group_addr, uint8_t group_mask_len, mrt_entry_type_t mrt_entry_type, action_jp_t action_jp, uint16_t holdtime, bool is_new_group); int send_test_jp_entry(const string& vif_name, const IPvX& nbr_addr, string& error_msg); // Assert test-related methods int send_test_assert(const string& vif_name, const IPvX& source_addr, const IPvX& group_addr, bool rpt_bit, uint32_t metric_preference, uint32_t metric, string& error_msg); // Bootstrap test-related methods int add_test_bsr_zone(const PimScopeZoneId& zone_id, const IPvX& bsr_addr, uint8_t bsr_priority, uint8_t hash_mask_len, uint16_t fragment_tag); int add_test_bsr_group_prefix(const PimScopeZoneId& zone_id, const IPvXNet& group_prefix, bool is_scope_zone, uint8_t expected_rp_count); int add_test_bsr_rp(const PimScopeZoneId& zone_id, const IPvXNet& group_prefix, const IPvX& rp_addr, uint8_t rp_priority, uint16_t rp_holdtime); int send_test_bootstrap(const string& vif_name, string& error_msg); int send_test_bootstrap_by_dest(const string& vif_name, const IPvX& dest_addr, string& error_msg); int send_test_cand_rp_adv(); // // PimNbr-related methods // void add_pim_mre_no_pim_nbr(PimMre *pim_mre); void delete_pim_mre_no_pim_nbr(PimMre *pim_mre); list& processing_pim_nbr_list() { return (_processing_pim_nbr_list); } void init_processing_pim_mre_rp(uint32_t vif_index, const IPvX& pim_nbr_addr); void init_processing_pim_mre_wc(uint32_t vif_index, const IPvX& pim_nbr_addr); void init_processing_pim_mre_sg(uint32_t vif_index, const IPvX& pim_nbr_addr); void init_processing_pim_mre_sg_rpt(uint32_t vif_index, const IPvX& pim_nbr_addr); PimNbr *find_processing_pim_mre_rp(uint32_t vif_index, const IPvX& pim_nbr_addr); PimNbr *find_processing_pim_mre_wc(uint32_t vif_index, const IPvX& pim_nbr_addr); PimNbr *find_processing_pim_mre_sg(uint32_t vif_index, const IPvX& pim_nbr_addr); PimNbr *find_processing_pim_mre_sg_rpt(uint32_t vif_index, const IPvX& pim_nbr_addr); // // Configuration parameters // ConfigParam& is_switch_to_spt_enabled() { return (_is_switch_to_spt_enabled); } ConfigParam& switch_to_spt_threshold_interval_sec() { return (_switch_to_spt_threshold_interval_sec); } ConfigParam& switch_to_spt_threshold_bytes() { return (_switch_to_spt_threshold_bytes); } // // Statistics-related counters and values // void clear_pim_statistics(); int clear_pim_statistics_per_vif(const string& vif_name, string& error_msg); // uint32_t pimstat_hello_messages_received() const; uint32_t pimstat_hello_messages_sent() const; uint32_t pimstat_hello_messages_rx_errors() const; uint32_t pimstat_register_messages_received() const; uint32_t pimstat_register_messages_sent() const; uint32_t pimstat_register_messages_rx_errors() const; uint32_t pimstat_register_stop_messages_received() const; uint32_t pimstat_register_stop_messages_sent() const; uint32_t pimstat_register_stop_messages_rx_errors() const; uint32_t pimstat_join_prune_messages_received() const; uint32_t pimstat_join_prune_messages_sent() const; uint32_t pimstat_join_prune_messages_rx_errors() const; uint32_t pimstat_bootstrap_messages_received() const; uint32_t pimstat_bootstrap_messages_sent() const; uint32_t pimstat_bootstrap_messages_rx_errors() const; uint32_t pimstat_assert_messages_received() const; uint32_t pimstat_assert_messages_sent() const; uint32_t pimstat_assert_messages_rx_errors() const; uint32_t pimstat_graft_messages_received() const; uint32_t pimstat_graft_messages_sent() const; uint32_t pimstat_graft_messages_rx_errors() const; uint32_t pimstat_graft_ack_messages_received() const; uint32_t pimstat_graft_ack_messages_sent() const; uint32_t pimstat_graft_ack_messages_rx_errors() const; uint32_t pimstat_candidate_rp_messages_received() const; uint32_t pimstat_candidate_rp_messages_sent() const; uint32_t pimstat_candidate_rp_messages_rx_errors() const; // uint32_t pimstat_unknown_type_messages() const; uint32_t pimstat_unknown_version_messages() const; uint32_t pimstat_neighbor_unknown_messages() const; uint32_t pimstat_bad_length_messages() const; uint32_t pimstat_bad_checksum_messages() const; uint32_t pimstat_bad_receive_interface_messages() const; uint32_t pimstat_rx_interface_disabled_messages() const; uint32_t pimstat_rx_register_not_rp() const; uint32_t pimstat_rp_filtered_source() const; uint32_t pimstat_unknown_register_stop() const; uint32_t pimstat_rx_join_prune_no_state() const; uint32_t pimstat_rx_graft_graft_ack_no_state() const; uint32_t pimstat_rx_graft_on_upstream_interface() const; uint32_t pimstat_rx_candidate_rp_not_bsr() const; uint32_t pimstat_rx_bsr_when_bsr() const; uint32_t pimstat_rx_bsr_not_rpf_interface() const; uint32_t pimstat_rx_unknown_hello_option() const; uint32_t pimstat_rx_data_no_state() const; uint32_t pimstat_rx_rp_no_state() const; uint32_t pimstat_rx_aggregate() const; uint32_t pimstat_rx_malformed_packet() const; uint32_t pimstat_no_rp() const; uint32_t pimstat_no_route_upstream() const; uint32_t pimstat_rp_mismatch() const; uint32_t pimstat_rpf_neighbor_unknown() const; // uint32_t pimstat_rx_join_rp() const; uint32_t pimstat_rx_prune_rp() const; uint32_t pimstat_rx_join_wc() const; uint32_t pimstat_rx_prune_wc() const; uint32_t pimstat_rx_join_sg() const; uint32_t pimstat_rx_prune_sg() const; uint32_t pimstat_rx_join_sg_rpt() const; uint32_t pimstat_rx_prune_sg_rpt() const; // // int pimstat_hello_messages_received_per_vif(const string& vif_name, uint32_t& result, string& error_msg) const; int pimstat_hello_messages_sent_per_vif(const string& vif_name, uint32_t& result, string& error_msg) const; int pimstat_hello_messages_rx_errors_per_vif(const string& vif_name, uint32_t& result, string& error_msg) const; int pimstat_register_messages_received_per_vif(const string& vif_name, uint32_t& result, string& error_msg) const; int pimstat_register_messages_sent_per_vif(const string& vif_name, uint32_t& result, string& error_msg) const; int pimstat_register_messages_rx_errors_per_vif(const string& vif_name, uint32_t& result, string& error_msg) const; int pimstat_register_stop_messages_received_per_vif(const string& vif_name, uint32_t& result, string& error_msg) const; int pimstat_register_stop_messages_sent_per_vif(const string& vif_name, uint32_t& result, string& error_msg) const; int pimstat_register_stop_messages_rx_errors_per_vif(const string& vif_name, uint32_t& result, string& error_msg) const; int pimstat_join_prune_messages_received_per_vif(const string& vif_name, uint32_t& result, string& error_msg) const; int pimstat_join_prune_messages_sent_per_vif(const string& vif_name, uint32_t& result, string& error_msg) const; int pimstat_join_prune_messages_rx_errors_per_vif(const string& vif_name, uint32_t& result, string& error_msg) const; int pimstat_bootstrap_messages_received_per_vif(const string& vif_name, uint32_t& result, string& error_msg) const; int pimstat_bootstrap_messages_sent_per_vif(const string& vif_name, uint32_t& result, string& error_msg) const; int pimstat_bootstrap_messages_rx_errors_per_vif(const string& vif_name, uint32_t& result, string& error_msg) const; int pimstat_assert_messages_received_per_vif(const string& vif_name, uint32_t& result, string& error_msg) const; int pimstat_assert_messages_sent_per_vif(const string& vif_name, uint32_t& result, string& error_msg) const; int pimstat_assert_messages_rx_errors_per_vif(const string& vif_name, uint32_t& result, string& error_msg) const; int pimstat_graft_messages_received_per_vif(const string& vif_name, uint32_t& result, string& error_msg) const; int pimstat_graft_messages_sent_per_vif(const string& vif_name, uint32_t& result, string& error_msg) const; int pimstat_graft_messages_rx_errors_per_vif(const string& vif_name, uint32_t& result, string& error_msg) const; int pimstat_graft_ack_messages_received_per_vif(const string& vif_name, uint32_t& result, string& error_msg) const; int pimstat_graft_ack_messages_sent_per_vif(const string& vif_name, uint32_t& result, string& error_msg) const; int pimstat_graft_ack_messages_rx_errors_per_vif(const string& vif_name, uint32_t& result, string& error_msg) const; int pimstat_candidate_rp_messages_received_per_vif(const string& vif_name, uint32_t& result, string& error_msg) const; int pimstat_candidate_rp_messages_sent_per_vif(const string& vif_name, uint32_t& result, string& error_msg) const; int pimstat_candidate_rp_messages_rx_errors_per_vif(const string& vif_name, uint32_t& result, string& error_msg) const; // int pimstat_unknown_type_messages_per_vif(const string& vif_name, uint32_t& result, string& error_msg) const; int pimstat_unknown_version_messages_per_vif(const string& vif_name, uint32_t& result, string& error_msg) const; int pimstat_neighbor_unknown_messages_per_vif(const string& vif_name, uint32_t& result, string& error_msg) const; int pimstat_bad_length_messages_per_vif(const string& vif_name, uint32_t& result, string& error_msg) const; int pimstat_bad_checksum_messages_per_vif(const string& vif_name, uint32_t& result, string& error_msg) const; int pimstat_bad_receive_interface_messages_per_vif(const string& vif_name, uint32_t& result, string& error_msg) const; int pimstat_rx_interface_disabled_messages_per_vif(const string& vif_name, uint32_t& result, string& error_msg) const; int pimstat_rx_register_not_rp_per_vif(const string& vif_name, uint32_t& result, string& error_msg) const; int pimstat_rp_filtered_source_per_vif(const string& vif_name, uint32_t& result, string& error_msg) const; int pimstat_unknown_register_stop_per_vif(const string& vif_name, uint32_t& result, string& error_msg) const; int pimstat_rx_join_prune_no_state_per_vif(const string& vif_name, uint32_t& result, string& error_msg) const; int pimstat_rx_graft_graft_ack_no_state_per_vif(const string& vif_name, uint32_t& result, string& error_msg) const; int pimstat_rx_graft_on_upstream_interface_per_vif(const string& vif_name, uint32_t& result, string& error_msg) const; int pimstat_rx_candidate_rp_not_bsr_per_vif(const string& vif_name, uint32_t& result, string& error_msg) const; int pimstat_rx_bsr_when_bsr_per_vif(const string& vif_name, uint32_t& result, string& error_msg) const; int pimstat_rx_bsr_not_rpf_interface_per_vif(const string& vif_name, uint32_t& result, string& error_msg) const; int pimstat_rx_unknown_hello_option_per_vif(const string& vif_name, uint32_t& result, string& error_msg) const; int pimstat_rx_data_no_state_per_vif(const string& vif_name, uint32_t& result, string& error_msg) const; int pimstat_rx_rp_no_state_per_vif(const string& vif_name, uint32_t& result, string& error_msg) const; int pimstat_rx_aggregate_per_vif(const string& vif_name, uint32_t& result, string& error_msg) const; int pimstat_rx_malformed_packet_per_vif(const string& vif_name, uint32_t& result, string& error_msg) const; int pimstat_no_rp_per_vif(const string& vif_name, uint32_t& result, string& error_msg) const; int pimstat_no_route_upstream_per_vif(const string& vif_name, uint32_t& result, string& error_msg) const; int pimstat_rp_mismatch_per_vif(const string& vif_name, uint32_t& result, string& error_msg) const; int pimstat_rpf_neighbor_unknown_per_vif(const string& vif_name, uint32_t& result, string& error_msg) const; // int pimstat_rx_join_rp_per_vif(const string& vif_name, uint32_t& result, string& error_msg) const; int pimstat_rx_prune_rp_per_vif(const string& vif_name, uint32_t& result, string& error_msg) const; int pimstat_rx_join_wc_per_vif(const string& vif_name, uint32_t& result, string& error_msg) const; int pimstat_rx_prune_wc_per_vif(const string& vif_name, uint32_t& result, string& error_msg) const; int pimstat_rx_join_sg_per_vif(const string& vif_name, uint32_t& result, string& error_msg) const; int pimstat_rx_prune_sg_per_vif(const string& vif_name, uint32_t& result, string& error_msg) const; int pimstat_rx_join_sg_rpt_per_vif(const string& vif_name, uint32_t& result, string& error_msg) const; int pimstat_rx_prune_sg_rpt_per_vif(const string& vif_name, uint32_t& result, string& error_msg) const; protected: // // IfMgrHintObserver methods // void tree_complete(); void updates_made(); private: /** * A method invoked when the status of a service changes. * * @param service the service whose status has changed. * @param old_status the old status. * @param new_status the new status. */ void status_change(ServiceBase* service, ServiceStatus old_status, ServiceStatus new_status); /** * Get a reference to the service base of the interface manager. * * This is a pure virtual function, and it must be implemented * by the communication-wrapper class that inherits this base class. * * @return a reference to the service base of the interface manager. */ virtual const ServiceBase* ifmgr_mirror_service_base() const = 0; /** * Get a reference to the interface manager tree. * * This is a pure virtual function, and it must be implemented * by the communication-wrapper class that inherits this base class. * * @return a reference to the interface manager tree. */ virtual const IfMgrIfTree& ifmgr_iftree() const = 0; /** * Initiate registration with the FEA. * * This is a pure virtual function, and it must be implemented * by the communication-wrapper class that inherits this base class. */ virtual void fea_register_startup() = 0; /** * Initiate registration with the MFEA. * * This is a pure virtual function, and it must be implemented * by the communication-wrapper class that inherits this base class. */ virtual void mfea_register_startup() = 0; /** * Initiate registration with the RIB. * * This is a pure virtual function, and it must be implemented * by the communication-wrapper class that inherits this base class. */ virtual void rib_register_startup() = 0; /** * Initiate de-registration with the FEA. * * This is a pure virtual function, and it must be implemented * by the communication-wrapper class that inherits this base class. */ virtual void fea_register_shutdown() = 0; /** * Initiate de-registration with the MFEA. * * This is a pure virtual function, and it must be implemented * by the communication-wrapper class that inherits this base class. */ virtual void mfea_register_shutdown() = 0; /** * Initiate de-registration with the RIB. * * This is a pure virtual function, and it must be implemented * by the communication-wrapper class that inherits this base class. */ virtual void rib_register_shutdown() = 0; PimMrt _pim_mrt; // PIM Multicast Routing Table PimMribTable _pim_mrib_table; // PIM Multicast Routing Information Base table RpTable _rp_table; // The RP table PimScopeZoneTable _pim_scope_zone_table; // The scope zone table PimBsr _pim_bsr; // The BSR state uint32_t _pim_register_vif_index;// The PIM Register vif index Mifset _pim_vifs_dr; // The vifs I am the DR buffer_t *_buffer_recv; // Buffer for receiving messages list _processing_pim_nbr_list; // The list of deleted PimNbr with // PimMre entries to process. // // Configuration parameters // ConfigParam _is_switch_to_spt_enabled; ConfigParam _switch_to_spt_threshold_interval_sec; ConfigParam _switch_to_spt_threshold_bytes; // // A local copy with the interface state information // IfMgrIfTree _iftree; // // Debug and test-related state // bool _is_log_trace; // If true, enable XLOG_TRACE() list _test_jp_headers_list; // J/P headers to send test J/P messages }; #endif // __PIM_PIM_NODE_HH__ xorp/pim/pim_mre_assert.cc0000664000076400007640000015346511635757530016017 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // PIM Multicast Routing Entry Assert handling // #include "pim_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvx.hh" #include "pim_mre.hh" #include "pim_mrt.hh" #include "pim_node.hh" #include "pim_vif.hh" // // Assert state // // Note: applies only for (*,G) and (S,G) void PimMre::set_assert_noinfo_state(uint32_t vif_index) { if (! (is_wc() || is_sg())) return; if (vif_index == Vif::VIF_INDEX_INVALID) return; if (is_assert_noinfo_state(vif_index)) return; // Nothing changed _i_am_assert_winner_state.reset(vif_index); _i_am_assert_loser_state.reset(vif_index); do { if (is_sg()) { pim_mrt()->add_task_assert_state_sg(vif_index, source_addr(), group_addr()); break; } if (is_wc()) { pim_mrt()->add_task_assert_state_wc(vif_index, group_addr()); break; } break; } while (false); // Try to remove the entry entry_try_remove(); } // Note: applies only for (*,G) and (S,G) void PimMre::set_i_am_assert_winner_state(uint32_t vif_index) { if (! (is_wc() || is_sg())) return; if (vif_index == Vif::VIF_INDEX_INVALID) return; if (is_i_am_assert_winner_state(vif_index)) return; // Nothing changed _i_am_assert_winner_state.set(vif_index); _i_am_assert_loser_state.reset(vif_index); do { if (is_sg()) { pim_mrt()->add_task_assert_state_sg(vif_index, source_addr(), group_addr()); break; } if (is_wc()) { pim_mrt()->add_task_assert_state_wc(vif_index, group_addr()); break; } break; } while (false); } // Note: applies only for (*,G) and (S,G) void PimMre::set_i_am_assert_loser_state(uint32_t vif_index) { if (! (is_wc() || is_sg())) return; if (vif_index == Vif::VIF_INDEX_INVALID) return; // XXX: we always perform the setting, because we always want to add // the INPUT_STATE_ASSERT_STATE_* task in case the assert winner // has changed. // if (is_i_am_assert_loser_state(vif_index)) // return; // Nothing changed _i_am_assert_winner_state.reset(vif_index); _i_am_assert_loser_state.set(vif_index); do { if (is_sg()) { pim_mrt()->add_task_assert_state_sg(vif_index, source_addr(), group_addr()); break; } if (is_wc()) { pim_mrt()->add_task_assert_state_wc(vif_index, group_addr()); break; } } while (false); } // Note: applies only for (*,G) and (S,G) bool PimMre::is_assert_noinfo_state(uint32_t vif_index) const { if (! (is_wc() || is_sg())) return (true); // XXX if (vif_index == Vif::VIF_INDEX_INVALID) return (true); // XXX return (! (_i_am_assert_winner_state.test(vif_index) || _i_am_assert_loser_state.test(vif_index)) ); } // Note: applies only for (*,G) and (S,G) bool PimMre::is_i_am_assert_winner_state(uint32_t vif_index) const { if (! (is_wc() || is_sg())) return (false); // XXX if (vif_index == Vif::VIF_INDEX_INVALID) return (false); return (_i_am_assert_winner_state.test(vif_index)); } // Note: applies only for (*,G) and (S,G) bool PimMre::is_i_am_assert_loser_state(uint32_t vif_index) const { if (! (is_wc() || is_sg())) return (false); // XXX if (vif_index == Vif::VIF_INDEX_INVALID) return (false); return (_i_am_assert_loser_state.test(vif_index)); } // Note: works for (*,G), (S,G), (S,G,rpt) const Mifset& PimMre::i_am_assert_winner_wc() const { static Mifset mifs; const PimMre *pim_mre_wc; if (is_wc()) { pim_mre_wc = this; } else { pim_mre_wc = wc_entry(); if (pim_mre_wc == NULL) { mifs.reset(); return (mifs); } } mifs = pim_mre_wc->i_am_assert_winner_state(); return (mifs); } // Note: works only for (S,G) const Mifset& PimMre::i_am_assert_winner_sg() const { static Mifset mifs; if (! is_sg()) { mifs.reset(); return (mifs); } mifs = i_am_assert_winner_state(); return (mifs); } // Note: applies for (*,G), (S,G), (S,G,rpt) const Mifset& PimMre::i_am_assert_loser_wc() const { static Mifset mifs; const PimMre *pim_mre_wc; if (is_wc()) { pim_mre_wc = this; } else { pim_mre_wc = wc_entry(); if (pim_mre_wc == NULL) { mifs.reset(); return (mifs); } } mifs = pim_mre_wc->i_am_assert_loser_state(); return (mifs); } // Note: applies only for (S,G) const Mifset& PimMre::i_am_assert_loser_sg() const { static Mifset mifs; if (! is_sg()) { mifs.reset(); return (mifs); } mifs = i_am_assert_loser_state(); return (mifs); } // Note: applies for (*,G), (S,G), (S,G,rpt) const Mifset& PimMre::lost_assert_wc() const { static Mifset mifs; uint32_t vif_index; if (! (is_wc() || is_sg() || is_sg_rpt())) { mifs.reset(); return (mifs); } // XXX: AssertWinner(*,G,I) != NULL AND AssertWinner(*,G,I) != me mifs = i_am_assert_loser_wc(); vif_index = rpf_interface_rp(); if (vif_index != Vif::VIF_INDEX_INVALID) mifs.reset(vif_index); return (mifs); } // Note: applies only for (S,G) const Mifset& PimMre::lost_assert_sg() const { static Mifset mifs; uint32_t vif_index; if (! is_sg()) { mifs.reset(); return (mifs); } // XXX: AssertWinner(S,G,I) != NULL AND AssertWinner(S,G,I) != me mifs = i_am_assert_loser_sg(); mifs &= assert_winner_metric_is_better_than_spt_assert_metric_sg(); // XXX vif_index = rpf_interface_s(); if (vif_index != Vif::VIF_INDEX_INVALID) mifs.reset(vif_index); return (mifs); } // Note: applies only for (S,G) and (S,G,rpt) const Mifset& PimMre::lost_assert_sg_rpt() const { static Mifset mifs; const PimMre *pim_mre_sg = NULL; uint32_t vif_index; if (! (is_sg() || is_sg_rpt())) { mifs.reset(); return (mifs); } mifs.reset(); do { if (is_sg()) { pim_mre_sg = this; break; } if (is_sg_rpt()) { pim_mre_sg = sg_entry(); break; } XLOG_UNREACHABLE(); } while (false); // XXX: AssertWinner(S,G,I) != NULL AND AssertWinner(S,G,I) != me if (pim_mre_sg != NULL) mifs = pim_mre_sg->i_am_assert_loser_sg(); vif_index = rpf_interface_rp(); if (vif_index != Vif::VIF_INDEX_INVALID) mifs.reset(vif_index); if (pim_mre_sg != NULL) { if (pim_mre_sg->is_spt()) { vif_index = pim_mre_sg->rpf_interface_s(); if (vif_index != Vif::VIF_INDEX_INVALID) mifs.reset(vif_index); } } return (mifs); } // Note: applies only for (*,G) and (S,G) void PimMre::set_could_assert_state(uint32_t vif_index, bool v) { if (vif_index == Vif::VIF_INDEX_INVALID) return; if (v) { if (is_could_assert_state(vif_index)) return; // Nothing changed _could_assert_state.set(vif_index); } else { if (! is_could_assert_state(vif_index)) return; // Nothing changed _could_assert_state.reset(vif_index); } } // Note: applies only for (*,G) and (S,G) bool PimMre::is_could_assert_state(uint32_t vif_index) const { if (vif_index == Vif::VIF_INDEX_INVALID) return (false); return (_could_assert_state.test(vif_index)); } // Note: applies only for (*,G) and (S,G) void PimMre::set_assert_tracking_desired_state(uint32_t vif_index, bool v) { if (vif_index == Vif::VIF_INDEX_INVALID) return; if (v) { if (is_assert_tracking_desired_state(vif_index)) return; // Nothing changed _assert_tracking_desired_state.set(vif_index); } else { if (! is_assert_tracking_desired_state(vif_index)) return; // Nothing changed _assert_tracking_desired_state.reset(vif_index); } } // Note: applies only for (*,G) and (S,G) bool PimMre::is_assert_tracking_desired_state(uint32_t vif_index) const { if (vif_index == Vif::VIF_INDEX_INVALID) return (false); return (_assert_tracking_desired_state.test(vif_index)); } // Note: applies only for (*,G) and (S,G) int PimMre::assert_process(PimVif *pim_vif, AssertMetric *assert_metric) { uint32_t vif_index = pim_vif->vif_index(); int ret_value; assert_state_t assert_state; bool i_am_assert_winner; AssertMetric *my_metric = NULL; if (! (is_sg() || is_wc())) return (XORP_ERROR); if (is_sg()) my_metric = my_assert_metric_sg(vif_index); if (is_wc()) my_metric = my_assert_metric_wc(vif_index); XLOG_ASSERT(my_metric != NULL); // // XXX: According to the spec, if we receive AssertCancel on the RPF // interface, then its metric will be compared against my_assert_metric(). // However, given that CouldAssert for the RPF interface is false, // my_assert_metric() will return infinite_assert_metric(), therefore // AssertCancel will fail to perform its duty. // This is fixed in the implementation by testing first if the received // Assert message contains AssertCancel metric, and if yes, the local // router is automatically declared the winner. // // Note: This fix is based on the following email to the PIM Working Group // mailing list: // Date: Fri, 9 Jul 2004 11:44:41 -0700 (PDT) // From: Venugopal Hemige // Subject: [pim] Hello HoldTime and Assert questions // http://www1.ietf.org/mail-archive/web/pim/current/msg00206.html // if (assert_metric->is_assert_cancel_metric()) i_am_assert_winner = true; else i_am_assert_winner = (*my_metric > *assert_metric); assert_state = ASSERT_STATE_NOINFO; do { if (is_i_am_assert_winner_state(vif_index)) { assert_state = ASSERT_STATE_WINNER; break; } if (is_i_am_assert_loser_state(vif_index)) { assert_state = ASSERT_STATE_LOSER; break; } } while (false); ret_value = XORP_ERROR; if (is_sg()) { ret_value = assert_process_sg(pim_vif, assert_metric, assert_state, i_am_assert_winner); } if (is_wc()) { ret_value = assert_process_wc(pim_vif, assert_metric, assert_state, i_am_assert_winner); } return (ret_value); } // Note: applies only for (*,G) int PimMre::assert_process_wc(PimVif *pim_vif, AssertMetric *assert_metric, assert_state_t assert_state, bool i_am_assert_winner) { uint32_t vif_index = pim_vif->vif_index(); AssertMetric *new_assert_metric; string dummy_error_msg; if (! is_wc()) return (XORP_ERROR); switch (assert_state) { case ASSERT_STATE_NOINFO: if (i_am_assert_winner && assert_metric->rpt_bit_flag() && could_assert_wc().test(vif_index)) { goto a1; } if ( (! i_am_assert_winner) && assert_metric->rpt_bit_flag() && assert_tracking_desired_wc().test(vif_index)) { goto a2; } break; case ASSERT_STATE_WINNER: if (i_am_assert_winner) { // Whoever sent the assert is in error goto a3; } else { // Receive preferred assert goto a2; } break; case ASSERT_STATE_LOSER: if ((*assert_metric > *assert_winner_metric_wc(vif_index)) && assert_metric->rpt_bit_flag()) { // Receive preferred assert with RPTbit set goto a2; } if ((! i_am_assert_winner) && assert_metric->rpt_bit_flag() && (assert_winner_metric_wc(vif_index)->addr() == assert_metric->addr())) { // Receive acceptable assert from current winner with RPTbit set goto a2; } if ((i_am_assert_winner) && (assert_winner_metric_wc(vif_index)->addr() == assert_metric->addr())) { // Receive inferior assert from current winner goto a5; } break; default: XLOG_UNREACHABLE(); return (XORP_ERROR); } return (XORP_ERROR); a1: // * Send Assert(*,G) pim_vif->pim_assert_mre_send(this, IPvX::ZERO(family()), dummy_error_msg); // * Set timer to (Assert_Time - Assert_Override_Interval) _assert_timers[vif_index] = pim_node()->eventloop().new_oneoff_after( TimeVal(pim_vif->assert_time().get(), 0) - TimeVal(pim_vif->assert_override_interval().get(), 0), callback(this, &PimMre::assert_timer_timeout_wc, vif_index)); // * Store self as AssertWinner(*,G,I) // * Store rpt_assert_metric(G,I) as AssertWinnerMetric(*,G,I) new_assert_metric = new AssertMetric(*rpt_assert_metric(vif_index)); set_assert_winner_metric_wc(vif_index, new_assert_metric); set_i_am_assert_winner_state(vif_index); return (XORP_OK); a2: // * Store new assert winner as AssertWinner(*,G,I) and assert winner // metric as AssertWinnerMetric(*,G,I) new_assert_metric = new AssertMetric(*assert_metric); set_assert_winner_metric_wc(vif_index, new_assert_metric); // * Set timer to Assert_Time _assert_timers[vif_index] = pim_node()->eventloop().new_oneoff_after( TimeVal(pim_vif->assert_time().get(), 0), callback(this, &PimMre::assert_timer_timeout_wc, vif_index)); set_i_am_assert_loser_state(vif_index); return (XORP_OK); a3: // * Send Assert(*,G) pim_vif->pim_assert_mre_send(this, IPvX::ZERO(family()), dummy_error_msg); // * Set timer to (Assert_Time - Assert_Override_Interval) _assert_timers[vif_index] = pim_node()->eventloop().new_oneoff_after( TimeVal(pim_vif->assert_time().get(), 0) - TimeVal(pim_vif->assert_override_interval().get(), 0), callback(this, &PimMre::assert_timer_timeout_wc, vif_index)); set_i_am_assert_winner_state(vif_index); return (XORP_OK); // XXX: a4 is not triggered by receiving of an Assert message a5: // * Delete assert info (AssertWinner(*,G,I), // and AssertWinnerMetric(*,G,I) assume default values). delete_assert_winner_metric_wc(vif_index); set_assert_noinfo_state(vif_index); return (XORP_OK); } // Note: applies only for (S,G) int PimMre::assert_process_sg(PimVif *pim_vif, AssertMetric *assert_metric, assert_state_t assert_state, bool i_am_assert_winner) { uint32_t vif_index = pim_vif->vif_index(); AssertMetric *new_assert_metric; string dummy_error_msg; if (! is_sg()) return (XORP_ERROR); switch (assert_state) { case ASSERT_STATE_NOINFO: if (i_am_assert_winner && (! assert_metric->rpt_bit_flag()) && could_assert_sg().test(vif_index)) { goto a1; } if (assert_metric->rpt_bit_flag() && could_assert_sg().test(vif_index)) { goto a1; } if ( (! i_am_assert_winner) && (! assert_metric->rpt_bit_flag()) && assert_tracking_desired_sg().test(vif_index)) { goto a6; // TODO: or probably a2?? } break; case ASSERT_STATE_WINNER: if (i_am_assert_winner) { // Whoever sent the assert is in error goto a3; } else { // Receive preferred assert // TODO: this may affect the value of is_join_desired_sg(); goto a2; } break; case ASSERT_STATE_LOSER: if (*assert_metric > *assert_winner_metric_sg(vif_index)) { // Receive preferred assert goto a2; } if ((! i_am_assert_winner) && (! assert_metric->rpt_bit_flag()) && (assert_winner_metric_sg(vif_index)->addr() == assert_metric->addr())) { // Receive acceptable assert with RPTbit clear from current winner goto a2; } if ((i_am_assert_winner) && (assert_winner_metric_sg(vif_index)->addr() == assert_metric->addr())) { // Receive inferior assert from current winner goto a5; } break; default: XLOG_UNREACHABLE(); return (XORP_ERROR); } return (XORP_ERROR); a1: // * Send Assert(S,G) pim_vif->pim_assert_mre_send(this, source_addr(), dummy_error_msg); // * Set timer to (Assert_Time - Assert_Override_Interval) _assert_timers[vif_index] = pim_node()->eventloop().new_oneoff_after( TimeVal(pim_vif->assert_time().get(), 0) - TimeVal(pim_vif->assert_override_interval().get(), 0), callback(this, &PimMre::assert_timer_timeout_sg, vif_index)); // * Store self as AssertWinner(S,G,I) // * Store spt_assert_metric(S,I) as AssertWinnerMetric(S,G,I) new_assert_metric = new AssertMetric(*spt_assert_metric(vif_index)); set_assert_winner_metric_sg(vif_index, new_assert_metric); set_i_am_assert_winner_state(vif_index); return (XORP_OK); a2: // * Store new assert winner as AssertWinner(S,G,I) and assert winner // metric as AssertWinnerMetric(S,G,I) new_assert_metric = new AssertMetric(*assert_metric); set_assert_winner_metric_sg(vif_index, new_assert_metric); // * Set timer to Assert_Time _assert_timers[vif_index] = pim_node()->eventloop().new_oneoff_after( TimeVal(pim_vif->assert_time().get(), 0), callback(this, &PimMre::assert_timer_timeout_sg, vif_index)); set_i_am_assert_loser_state(vif_index); // XXX: JoinDesired(S,G) and PruneDesired(S,G,rpt) may have changed // TODO: XXX: PAVPAVPAV: make sure that performing sequentially // the actions for each recomputation is OK. // TODO: XXX: PAVPAVPAV: OK TO COMMENT? // recompute_is_join_desired_sg(); // if (sg_rpt_entry() != NULL) // sg_rpt_entry()->recompute_is_prune_desired_sg_rpt(); return (XORP_OK); a3: // * Send Assert(S,G) pim_vif->pim_assert_mre_send(this, source_addr(), dummy_error_msg); // * Set timer to (Assert_Time - Assert_Override_Interval) _assert_timers[vif_index] = pim_node()->eventloop().new_oneoff_after( TimeVal(pim_vif->assert_time().get(), 0) - TimeVal(pim_vif->assert_override_interval().get(), 0), callback(this, &PimMre::assert_timer_timeout_sg, vif_index)); set_i_am_assert_winner_state(vif_index); return (XORP_OK); // XXX: a4 is not triggered by receiving of an Assert message. a5: // * Delete assert info (AssertWinner(S,G,I), // and AssertWinnerMetric(S,G,I) assume default values). delete_assert_winner_metric_sg(vif_index); set_assert_noinfo_state(vif_index); return (XORP_OK); a6: // * Store new assert winner as AssertWinner(S,G,I) and assert winner // metric as AssertWinnerMetric(S,G,I) new_assert_metric = new AssertMetric(*assert_metric); set_assert_winner_metric_sg(vif_index, new_assert_metric); // * Set timer to Assert_Time _assert_timers[vif_index] = pim_node()->eventloop().new_oneoff_after( TimeVal(pim_vif->assert_time().get(), 0), callback(this, &PimMre::assert_timer_timeout_sg, vif_index)); // * If I is RPF_interface(S) // AND (UpstreamJPState(S,G) == true) // then set SPTbit(S,G) to TRUE if ((vif_index == rpf_interface_s()) && is_joined_state()) set_spt(true); set_i_am_assert_loser_state(vif_index); return (XORP_OK); } // Note: applies only for (*,G) int PimMre::wrong_iif_data_arrived_wc(PimVif *pim_vif, const IPvX& assert_source_addr, bool& is_assert_sent) { uint32_t vif_index = pim_vif->vif_index(); string dummy_error_msg; if (! is_wc()) return (XORP_ERROR); if (_asserts_rate_limit.test(vif_index)) return (XORP_OK); // XXX: we are rate-limiting the Asserts // Send Assert(*,G) if (! is_assert_sent) { pim_vif->pim_assert_mre_send(this, assert_source_addr, dummy_error_msg); is_assert_sent = true; } // Set the bit-flag, and restart the rate-limited timer (if not running) // XXX: On average, the data packets trigger no more than one Assert // message per second. _asserts_rate_limit.set(vif_index); if (! _asserts_rate_limit_timer.scheduled()) { _asserts_rate_limit_timer = pim_node()->eventloop().new_oneoff_after( TimeVal(1, 0), callback(this, &PimMre::asserts_rate_limit_timer_timeout)); } return (XORP_OK); } // Note: applies only for (S,G) int PimMre::wrong_iif_data_arrived_sg(PimVif *pim_vif, const IPvX& assert_source_addr, bool& is_assert_sent) { uint32_t vif_index = pim_vif->vif_index(); string dummy_error_msg; if (! is_sg()) return (XORP_ERROR); XLOG_ASSERT(assert_source_addr == source_addr()); if (_asserts_rate_limit.test(vif_index)) return (XORP_OK); // XXX: we are rate-limiting the Asserts // Send Assert(S,G) if (! is_assert_sent) { pim_vif->pim_assert_mre_send(this, source_addr(), dummy_error_msg); is_assert_sent = true; } // Set the bit-flag, and restart the rate-limited timer (if not running) // XXX: On average, the data packets trigger no more than one Assert // message per second. _asserts_rate_limit.set(vif_index); if (! _asserts_rate_limit_timer.scheduled()) { _asserts_rate_limit_timer = pim_node()->eventloop().new_oneoff_after( TimeVal(1, 0), callback(this, &PimMre::asserts_rate_limit_timer_timeout)); } return (XORP_OK); } void PimMre::asserts_rate_limit_timer_timeout() { if (! (is_sg() || is_wc())) return; // Reset the rate-limiting bits _asserts_rate_limit.reset(); // XXX: try to remove the entry if it was created just for // the purpose of rate-limiting the triggered assert messages. entry_try_remove(); } // // Data packet received that may trigger an Assert. // // XXX: should be applied to (S,G) entry (if such exists). If the // entry is not (S,G), then we assume that there is no such entry // in the multicast routing table. // // Note: applies for all entries int PimMre::data_arrived_could_assert(PimVif *pim_vif, const IPvX& src, const IPvX& dst, bool& is_assert_sent) { uint32_t vif_index = pim_vif->vif_index(); int ret_value; // // Data packet received that may trigger an Assert // // First try to apply this event to the (S,G) assert state machine. // Only if the (S,G) assert state machine is in NoInfo state, and // only if there was no change in the (S,G) assert state machine // a result of receiving this message, then apply it to the (*,G) // assert state machine. // do { bool is_sg_noinfo_old, is_sg_noinfo_new; // // XXX: strictly speaking, we should try to create // the (S,G) state, and explicitly compare the old // and new (S,G) assert state. // However, we use the observation that if there is no (S,G) // routing state, then the receiving of the data packet will not change // the (S,G) assert state mchine. Note that this observation is // based on the specific details of the (S,G) assert state machine. // In particular, the action in NoInfo state when // "Data arrives from S to G on I and CouldAssert(S,G,I)". // If there is no (S,G) routing state, then the SPTbit cannot // be true, and therefore CouldAssert(S,G,I) also cannot be true. // if (! is_sg()) break; is_sg_noinfo_old = is_assert_noinfo_state(vif_index); ret_value = data_arrived_could_assert_sg(pim_vif, src, is_assert_sent); is_sg_noinfo_new = is_assert_noinfo_state(vif_index); // // If there was transaction in the (S,G) assert state, // or if the new (S,G) assert state is not NoInfo, then // don't apply this event to the (*,G) assert state machine. // In other words, both the old and the new state in the // (S,G) assert state machine must be in NoInfo state to // apply the event to the (*,G) assert state // machine. // if (is_sg_noinfo_old && is_sg_noinfo_new) break; return (ret_value); } while (false); // // No transaction occured in the (S,G) assert state machine, and // it is in NoInfo state. // Apply the event to the (*,G) assert state machine. // if (is_wc()) { return (data_arrived_could_assert_wc(pim_vif, src, is_assert_sent)); } PimMre *pim_mre_wc; pim_mre_wc = pim_mrt()->pim_mre_find(src, dst, PIM_MRE_WC, PIM_MRE_WC); if (pim_mre_wc == NULL) { XLOG_ERROR("Internal error lookup/creating PIM multicast routing " "entry for source = %s group = %s", cstring(src), cstring(dst)); return (XORP_ERROR); } ret_value = pim_mre_wc->data_arrived_could_assert_wc(pim_vif, src, is_assert_sent); // Try to remove the entry in case we don't need it pim_mre_wc->entry_try_remove(); return (ret_value); } // Note: applies only for (*,G) int PimMre::data_arrived_could_assert_wc(PimVif *pim_vif, const IPvX& assert_source_addr, bool& is_assert_sent) { uint32_t vif_index = pim_vif->vif_index(); AssertMetric *new_assert_metric; Mifset mifs; string dummy_error_msg; if (! is_wc()) return (XORP_ERROR); if (is_assert_noinfo_state(vif_index)) goto assert_noinfo_state_label; return (XORP_OK); assert_noinfo_state_label: // NoInfo state mifs = could_assert_wc(); if (! mifs.test(vif_index)) { return (XORP_OK); // CouldAssert(*,G,I) is false. Ignore. } goto a1; a1: // * Send Assert(*,G) if (! is_assert_sent) { pim_vif->pim_assert_mre_send(this, assert_source_addr, dummy_error_msg); is_assert_sent = true; } // * Set timer to (Assert_Time - Assert_Override_Interval) _assert_timers[vif_index] = pim_node()->eventloop().new_oneoff_after( TimeVal(pim_vif->assert_time().get(), 0) - TimeVal(pim_vif->assert_override_interval().get(), 0), callback(this, &PimMre::assert_timer_timeout_wc, vif_index)); // * Store self as AssertWinner(*,G,I) // * Store rpt_assert_metric(G,I) as AssertWinnerMetric(*,G,I) new_assert_metric = new AssertMetric(*rpt_assert_metric(vif_index)); set_assert_winner_metric_wc(vif_index, new_assert_metric); set_i_am_assert_winner_state(vif_index); return (XORP_OK); } // Note: applies only for (S,G) int PimMre::data_arrived_could_assert_sg(PimVif *pim_vif, const IPvX& assert_source_addr, bool& is_assert_sent) { uint32_t vif_index = pim_vif->vif_index(); AssertMetric *new_assert_metric; Mifset mifs; string dummy_error_msg; if (! is_sg()) return (XORP_ERROR); XLOG_ASSERT(assert_source_addr == source_addr()); if (is_assert_noinfo_state(vif_index)) goto assert_noinfo_state_label; return (XORP_OK); assert_noinfo_state_label: // NoInfo state mifs = could_assert_sg(); if (! mifs.test(vif_index)) { return (XORP_OK); // CouldAssert(S,G,I) is false. Ignore. } goto a1; a1: // * Send Assert(S,G) if (! is_assert_sent) { pim_vif->pim_assert_mre_send(this, source_addr(), dummy_error_msg); is_assert_sent = true; } // * Set timer to (Assert_Time - Assert_Override_Interval) _assert_timers[vif_index] = pim_node()->eventloop().new_oneoff_after( TimeVal(pim_vif->assert_time().get(), 0) - TimeVal(pim_vif->assert_override_interval().get(), 0), callback(this, &PimMre::assert_timer_timeout_sg, vif_index)); // * Store self as AssertWinner(S,G,I) // * Store spt_assert_metric(S,I) as AssertWinnerMetric(S,G,I) new_assert_metric = new AssertMetric(*spt_assert_metric(vif_index)); set_assert_winner_metric_sg(vif_index, new_assert_metric); set_i_am_assert_winner_state(vif_index); return (XORP_OK); } // Note: applies for all entries const Mifset& PimMre::could_assert_wc() const { static Mifset mifs; uint32_t vif_index; mifs = joins_rp(); if (is_wc() || is_sg() || is_sg_rpt()) { mifs |= joins_wc(); mifs |= pim_include_wc(); } vif_index = rpf_interface_rp(); if (vif_index != Vif::VIF_INDEX_INVALID) mifs.reset(vif_index); return (mifs); } // Note: applies only for (*,G) // Return true if state has changed, otherwise return false. bool PimMre::recompute_could_assert_wc() { Mifset old_value, new_value, diff_value; if (! is_wc()) return (false); old_value = could_assert_state(); new_value = could_assert_wc(); if (new_value == old_value) return (false); // Nothing changed diff_value = new_value ^ old_value; for (uint32_t i = 0; i < pim_node()->maxvifs(); i++) { if (diff_value.test(i)) process_could_assert_wc(i, new_value.test(i)); } return (true); } // Note: applies only for (*,G) // Return true if state has changed, otherwise return false. bool PimMre::process_could_assert_wc(uint32_t vif_index, bool new_value) { PimVif *pim_vif = pim_mrt()->vif_find_by_vif_index(vif_index); string dummy_error_msg; if (pim_vif == NULL) return (false); if (! is_wc()) return (false); // Set the new value set_could_assert_state(vif_index, new_value); if (is_i_am_assert_winner_state(vif_index)) goto assert_winner_state_label; // All other states: ignore the change. return (true); assert_winner_state_label: // IamAssertWinner state if (new_value) return (true); // CouldAssert(*,G,I) -> TRUE: ignore // CouldAssert(*,G,I) -> FALSE goto a4; a4: // * Send AssertCancel(*,G) pim_vif->pim_assert_cancel_send(this, dummy_error_msg); // * Delete assert info (AssertWinner(*,G,I), // and AssertWinnerMetric(*,G,I) assume default values). delete_assert_winner_metric_wc(vif_index); set_assert_noinfo_state(vif_index); return (true); } // Note: applies only for (S,G) and (S,G,rpt) const Mifset& PimMre::could_assert_sg() const { static Mifset mifs; Mifset mifs2; PimMre *pim_mre_sg_rpt; uint32_t vif_index; if (! (is_sg() || is_sg_rpt())) { mifs.reset(); return (mifs); } if (is_sg_rpt()) { PimMre *pim_mre_sg = sg_entry(); if (pim_mre_sg == NULL) { // XXX: if no (S,G) entry, then SPTbit(S,G) is false, hence reset mifs.reset(); return (mifs); } return (pim_mre_sg->could_assert_sg()); } if (! is_spt()) { // XXX: SPTbit(S,G) must be true mifs.reset(); return (mifs); } mifs = joins_rp(); mifs |= joins_wc(); pim_mre_sg_rpt = sg_rpt_entry(); if (pim_mre_sg_rpt != NULL) mifs &= ~(pim_mre_sg_rpt->prunes_sg_rpt()); mifs2 = pim_include_wc(); mifs2 &= ~pim_exclude_sg(); mifs |= mifs2; mifs &= ~lost_assert_wc(); mifs |= joins_sg(); mifs |= pim_include_sg(); vif_index = rpf_interface_s(); if (vif_index != Vif::VIF_INDEX_INVALID) mifs.reset(vif_index); return (mifs); } // Note: applies only for (S,G) // Return true if state has changed, otherwise return false. bool PimMre::recompute_could_assert_sg() { Mifset old_value, new_value, diff_value; if (! is_sg()) return (false); old_value = could_assert_state(); new_value = could_assert_sg(); if (new_value == old_value) return (false); // Nothing changed diff_value = new_value ^ old_value; for (uint32_t i = 0; i < pim_node()->maxvifs(); i++) { if (diff_value.test(i)) process_could_assert_sg(i, new_value.test(i)); } return (true); } // Note: applies only for (S,G) // Return true if state has changed, otherwise return false. bool PimMre::process_could_assert_sg(uint32_t vif_index, bool new_value) { PimVif *pim_vif = pim_mrt()->vif_find_by_vif_index(vif_index); string dummy_error_msg; if (pim_vif == NULL) return (false); if (! is_sg()) return (false); // Set the new value set_could_assert_state(vif_index, new_value); if (is_i_am_assert_winner_state(vif_index)) goto assert_winner_state_label; // All other states: ignore the change. return (true); assert_winner_state_label: // IamAssertWinner state if (new_value) return (true); // CouldAssert(S,G,I) -> TRUE: ignore // CouldAssert(S,G,I) -> FALSE goto a4; a4: // * Send AssertCancel(S,G) pim_vif->pim_assert_cancel_send(this, dummy_error_msg); // * Delete assert info (AssertWinner(S,G,I), // and AssertWinnerMetric(S,G,I) assume default values). delete_assert_winner_metric_sg(vif_index); set_assert_noinfo_state(vif_index); return (true); } // Note: applies only for (*,G) void PimMre::assert_timer_timeout_wc(uint32_t vif_index) { PimVif *pim_vif = pim_mrt()->vif_find_by_vif_index(vif_index); string dummy_error_msg; if (pim_vif == NULL) return; if (! is_wc()) return; if (is_i_am_assert_winner_state(vif_index)) goto a3; if (is_i_am_assert_loser_state(vif_index)) goto a5; // Assert NoInfo state return; a3: // * Send Assert(*,G) pim_vif->pim_assert_mre_send(this, IPvX::ZERO(family()), dummy_error_msg); // * Set timer to (Assert_Time - Assert_Override_Interval) _assert_timers[vif_index] = pim_node()->eventloop().new_oneoff_after( TimeVal(pim_vif->assert_time().get(), 0) - TimeVal(pim_vif->assert_override_interval().get(), 0), callback(this, &PimMre::assert_timer_timeout_wc, vif_index)); set_i_am_assert_winner_state(vif_index); return; a5: // * Delete assert info (AssertWinner(*,G,I), // and AssertWinnerMetric(*,G,I) assume default values). delete_assert_winner_metric_wc(vif_index); set_assert_noinfo_state(vif_index); return; } // Note: applies only for (*,G) // TODO: unite actions a3 and a5 below with the original actions earlier void PimMre::assert_timer_timeout_sg(uint32_t vif_index) { PimVif *pim_vif = pim_mrt()->vif_find_by_vif_index(vif_index); string dummy_error_msg; if (pim_vif == NULL) return; if (! is_sg()) return; if (is_i_am_assert_winner_state(vif_index)) goto a3; if (is_i_am_assert_loser_state(vif_index)) goto a5; // Assert NoInfo state return; a3: // * Send Assert(S,G) pim_vif->pim_assert_mre_send(this, source_addr(), dummy_error_msg); // * Set timer to (Assert_Time - Assert_Override_Interval) _assert_timers[vif_index] = pim_node()->eventloop().new_oneoff_after( TimeVal(pim_vif->assert_time().get(), 0) - TimeVal(pim_vif->assert_override_interval().get(), 0), callback(this, &PimMre::assert_timer_timeout_sg, vif_index)); set_i_am_assert_winner_state(vif_index); return; a5: // * Delete assert info (AssertWinner(S,G,I), // and AssertWinnerMetric(S,G,I) assume default values). delete_assert_winner_metric_sg(vif_index); set_assert_noinfo_state(vif_index); return; } // Note: works for (*,G), (S,G) AssertMetric * PimMre::assert_winner_metric_wc(uint32_t vif_index) const { const PimMre *pim_mre_wc; if (vif_index == Vif::VIF_INDEX_INVALID) return (NULL); if (! (is_wc() || is_sg())) return (NULL); if (is_wc()) { pim_mre_wc = this; } else { pim_mre_wc = wc_entry(); if (pim_mre_wc == NULL) return (NULL); } return (pim_mre_wc->assert_winner_metric(vif_index)); } // Note: works for (S,G) AssertMetric * PimMre::assert_winner_metric_sg(uint32_t vif_index) const { if (vif_index == Vif::VIF_INDEX_INVALID) return (NULL); if (! is_sg()) { XLOG_UNREACHABLE(); return (NULL); } return (assert_winner_metric(vif_index)); } // Note: works for (*,G), (S,G) void PimMre::set_assert_winner_metric_wc(uint32_t vif_index, AssertMetric *v) { PimMre *pim_mre_wc; if (vif_index == Vif::VIF_INDEX_INVALID) return; if (! (is_wc() || is_sg())) return; if (is_wc()) { pim_mre_wc = this; } else { pim_mre_wc = wc_entry(); if (pim_mre_wc == NULL) return; } pim_mre_wc->set_assert_winner_metric(vif_index, v); } // Note: works for (S,G) void PimMre::set_assert_winner_metric_sg(uint32_t vif_index, AssertMetric *v) { if (vif_index == Vif::VIF_INDEX_INVALID) return; if (! is_sg()) { XLOG_UNREACHABLE(); return; } set_assert_winner_metric(vif_index, v); // // Set/reset the 'assert_winner_metric_is_better_than_spt_assert_metric_sg' // state. // do { bool set_value = false; if (v != NULL) { AssertMetric *assert_metric = spt_assert_metric(vif_index); if ((assert_metric == NULL) || (*v > *assert_metric)) { set_value = true; } } set_assert_winner_metric_is_better_than_spt_assert_metric_sg( vif_index, set_value); } while (false); } // Note: applies only for (*,G) and (S,G) void PimMre::set_assert_winner_metric(uint32_t vif_index, AssertMetric *v) { AssertMetric *old_assert_metric; if (vif_index == Vif::VIF_INDEX_INVALID) return; old_assert_metric = _assert_winner_metrics[vif_index]; if (old_assert_metric == v) return; // Nothing changed if (old_assert_metric != NULL) delete old_assert_metric; _assert_winner_metrics[vif_index] = v; } // Note: works for (*,G), (S,G) void PimMre::delete_assert_winner_metric_wc(uint32_t vif_index) { PimMre *pim_mre_wc; if (vif_index == Vif::VIF_INDEX_INVALID) return; if (! (is_wc() || is_sg())) return; if (is_wc()) { pim_mre_wc = this; } else { pim_mre_wc = wc_entry(); if (pim_mre_wc == NULL) return; } pim_mre_wc->delete_assert_winner_metric(vif_index); } // Note: works for (S,G) void PimMre::delete_assert_winner_metric_sg(uint32_t vif_index) { if (vif_index == Vif::VIF_INDEX_INVALID) return; if (! is_sg()) { XLOG_UNREACHABLE(); return; } delete_assert_winner_metric(vif_index); // // Reset the 'assert_winner_metric_is_better_than_spt_assert_metric_sg' // state. // set_assert_winner_metric_is_better_than_spt_assert_metric_sg( vif_index, false); } // Note: applies only for (*,G) and (S,G) void PimMre::delete_assert_winner_metric(uint32_t vif_index) { set_assert_winner_metric(vif_index, NULL); } // Note: applies only for (S,G) void PimMre::set_assert_winner_metric_is_better_than_spt_assert_metric_sg( uint32_t vif_index, bool v) { if (vif_index == Vif::VIF_INDEX_INVALID) return; if (v) _assert_winner_metric_is_better_than_spt_assert_metric_sg.set(vif_index); else _assert_winner_metric_is_better_than_spt_assert_metric_sg.reset(vif_index); } // Note: applies only for (S,G) // Return true if state has changed, otherwise return false. bool PimMre::recompute_assert_winner_nbr_sg_gen_id_changed(uint32_t vif_index, const IPvX& nbr_addr) { PimVif *pim_vif = pim_mrt()->vif_find_by_vif_index(vif_index); if (pim_vif == NULL) return (false); if (! is_sg()) return (false); if (is_i_am_assert_loser_state(vif_index)) { if (assert_winner_metric_sg(vif_index)->addr() == nbr_addr) goto a5; // This is not the assert winner. Ignore. return (false); } // All other states: ignore the change. return (false); a5: // * Delete assert info (AssertWinner(S,G,I), // and AssertWinnerMetric(S,G,I) assume default values). delete_assert_winner_metric_sg(vif_index); set_assert_noinfo_state(vif_index); return (true); } // Note: applies only for (*,G) // Return true if state has changed, otherwise return false. bool PimMre::recompute_assert_winner_nbr_wc_gen_id_changed(uint32_t vif_index, const IPvX& nbr_addr) { PimVif *pim_vif = pim_mrt()->vif_find_by_vif_index(vif_index); if (pim_vif == NULL) return (false); if (! is_wc()) return (false); if (is_i_am_assert_loser_state(vif_index)) { if (assert_winner_metric_wc(vif_index)->addr() == nbr_addr) goto a5; // This is not the assert winner. Ignore. return (false); } // All other states: ignore the change. return (false); a5: // * Delete assert info (AssertWinner(*,G,I), // and AssertWinnerMetric(*,G,I) assume default values). delete_assert_winner_metric_wc(vif_index); set_assert_noinfo_state(vif_index); return (true); } // Note: applies only for (S,G) // Return true if state has changed, otherwise return false. bool PimMre::recompute_assert_winner_nbr_sg_nlt_expired(uint32_t vif_index, const IPvX& nbr_addr) { // XXX: the action is the same if the neighbor GenID has changed return (recompute_assert_winner_nbr_sg_gen_id_changed(vif_index, nbr_addr)); } // Note: applies only for (*,G) // Return true if state has changed, otherwise return false. bool PimMre::recompute_assert_winner_nbr_wc_nlt_expired(uint32_t vif_index, const IPvX& nbr_addr) { // XXX: the action is the same if the neighbor GenID has changed return (recompute_assert_winner_nbr_wc_gen_id_changed(vif_index, nbr_addr)); } // Note: applies for (S,G) const Mifset& PimMre::assert_tracking_desired_sg() const { static Mifset mifs; Mifset mifs2; PimMre *pim_mre_sg_rpt; uint32_t vif_index; if (! is_sg()) { mifs.reset(); return (mifs); } mifs = joins_rp(); mifs |= joins_wc(); pim_mre_sg_rpt = sg_rpt_entry(); if (pim_mre_sg_rpt != NULL) mifs &= ~(pim_mre_sg_rpt->prunes_sg_rpt()); mifs2 = pim_include_wc(); mifs2 &= ~pim_exclude_sg(); mifs |= mifs2; mifs2 &= ~lost_assert_wc(); mifs |= joins_sg(); mifs2 = i_am_dr(); mifs2 |= i_am_assert_winner_sg(); mifs2 &= local_receiver_include_sg(); mifs |= mifs2; if (is_join_desired_sg()) { vif_index = rpf_interface_s(); if (vif_index != Vif::VIF_INDEX_INVALID) mifs.set(vif_index); } if (is_join_desired_wc() && (! is_spt())) { vif_index = rpf_interface_rp(); if (vif_index != Vif::VIF_INDEX_INVALID) mifs.set(vif_index); } return (mifs); } // Note: applies only for (S,G) // Return true if state has changed, otherwise return false. bool PimMre::recompute_assert_tracking_desired_sg() { Mifset old_value, new_value, diff_value; if (! is_sg()) return (false); old_value = assert_tracking_desired_state(); new_value = assert_tracking_desired_sg(); if (new_value == old_value) return (false); // Nothing changed diff_value = new_value ^ old_value; for (uint32_t i = 0; i < pim_node()->maxvifs(); i++) { if (diff_value.test(i)) process_assert_tracking_desired_sg(i, new_value.test(i)); } return (true); } // Note: applies only for (S,G) // Return true if state has changed, otherwise return false. bool PimMre::process_assert_tracking_desired_sg(uint32_t vif_index, bool new_value) { if (vif_index == Vif::VIF_INDEX_INVALID) return (false); if (! is_sg()) return (false); // Set the new value set_assert_tracking_desired_state(vif_index, new_value); if (is_i_am_assert_loser_state(vif_index)) goto assert_loser_state_label; // All other states: ignore the change. return (true); assert_loser_state_label: // IamAssertLoser state if (new_value) return (true); // AssertTrackingDesired(S,G,I) -> TRUE: ignore // AssertTrackingDesired(S,G,I) -> FALSE goto a5; a5: // * Delete assert info (AssertWinner(S,G,I), // and AssertWinnerMetric(S,G,I) assume default values). delete_assert_winner_metric_sg(vif_index); set_assert_noinfo_state(vif_index); return (true); } // Note: applies for (*,G) const Mifset& PimMre::assert_tracking_desired_wc() const { static Mifset mifs; Mifset mifs2; uint32_t vif_index; if (! is_wc()) { mifs.reset(); return (mifs); } mifs = could_assert_wc(); mifs2 = i_am_dr(); mifs2 |= i_am_assert_winner_wc(); mifs2 &= local_receiver_include_wc(); mifs |= mifs2; if (is_rpt_join_desired_g()) { vif_index = rpf_interface_rp(); if (vif_index != Vif::VIF_INDEX_INVALID) mifs.set(vif_index); } return (mifs); } // Note: applies only for (*,G) // Return true if state has changed, otherwise return false. bool PimMre::recompute_assert_tracking_desired_wc() { Mifset old_value, new_value, diff_value; if (! is_wc()) return (false); old_value = assert_tracking_desired_state(); new_value = assert_tracking_desired_wc(); if (new_value == old_value) return (false); // Nothing changed diff_value = new_value ^ old_value; for (uint32_t i = 0; i < pim_node()->maxvifs(); i++) { if (diff_value.test(i)) process_assert_tracking_desired_wc(i, new_value.test(i)); } return (true); } // Note: applies only for (*,G) // Return true if state has changed, otherwise return false. bool PimMre::process_assert_tracking_desired_wc(uint32_t vif_index, bool new_value) { if (vif_index == Vif::VIF_INDEX_INVALID) return (false); if (! is_wc()) return (false); // Set the new value set_assert_tracking_desired_state(vif_index, new_value); if (is_i_am_assert_loser_state(vif_index)) goto assert_loser_state_label; // All other states: ignore the change. return (true); assert_loser_state_label: // IamAssertLoser state if (new_value) return (true); // AssertTrackinDesired(*,G,I) -> TRUE: ignore // AssertTrackingDesired(*,G,I) -> FALSE goto a5; a5: // * Delete assert info (AssertWinner(*,G,I), // and AssertWinnerMetric(*,G,I) assume default values). delete_assert_winner_metric_wc(vif_index); set_assert_noinfo_state(vif_index); return (true); } // Note: applies only for (S,G) AssertMetric * PimMre::spt_assert_metric(uint32_t vif_index) const { static AssertMetric assert_metric(IPvX::ZERO(family())); PimVif *pim_vif; if (vif_index == Vif::VIF_INDEX_INVALID) return (NULL); if (! is_sg()) return (NULL); pim_vif = pim_mrt()->vif_find_by_vif_index(vif_index); if (pim_vif == NULL) return (NULL); assert_metric.set_addr(pim_vif->primary_addr()); assert_metric.set_rpt_bit_flag(false); assert_metric.set_metric_preference(metric_preference_s()); assert_metric.set_metric(metric_s()); return (&assert_metric); } // Note: applies only for (*,G) and (S,G) AssertMetric * PimMre::rpt_assert_metric(uint32_t vif_index) const { static AssertMetric assert_metric(IPvX::ZERO(family())); PimVif *pim_vif; if (vif_index == Vif::VIF_INDEX_INVALID) return (NULL); if (! (is_wc() || is_sg())) return (NULL); pim_vif = pim_mrt()->vif_find_by_vif_index(vif_index); if (pim_vif == NULL) return (NULL); assert_metric.set_addr(pim_vif->primary_addr()); assert_metric.set_rpt_bit_flag(true); assert_metric.set_metric_preference(metric_preference_rp()); assert_metric.set_metric(metric_rp()); return (&assert_metric); } // Note: applies only for (*,G) and (S,G) (but is used only for (S,G)) AssertMetric * PimMre::infinite_assert_metric() const { static AssertMetric assert_metric(IPvX::ZERO(family())); // XXX: in case of Assert, the IP address with minimum value is the loser assert_metric.set_addr(IPvX::ZERO(family())); assert_metric.set_rpt_bit_flag(true); assert_metric.set_metric_preference(PIM_ASSERT_MAX_METRIC_PREFERENCE); assert_metric.set_metric(PIM_ASSERT_MAX_METRIC); return (&assert_metric); } // Note: applies only for (S,G) and (S,G,rpt) uint32_t PimMre::metric_preference_s() const { Mrib *mrib = mrib_s(); if (mrib != NULL) return (mrib->metric_preference()); return (PIM_ASSERT_MAX_METRIC_PREFERENCE); } // Note: applies for all entries uint32_t PimMre::metric_preference_rp() const { Mrib *mrib = mrib_rp(); if (mrib != NULL) return (mrib->metric_preference()); return (PIM_ASSERT_MAX_METRIC_PREFERENCE); } // Note: applies only for (S,G) and (S,G,rpt) uint32_t PimMre::metric_s() const { Mrib *mrib = mrib_s(); if (mrib != NULL) return (mrib->metric()); return (PIM_ASSERT_MAX_METRIC); } // Note: applies for all entries uint32_t PimMre::metric_rp() const { Mrib *mrib = mrib_rp(); if (mrib != NULL) return (mrib->metric()); return (PIM_ASSERT_MAX_METRIC); } // Note: applies only for (S,G) AssertMetric * PimMre::my_assert_metric_sg(uint32_t vif_index) const { Mifset mifs; if (vif_index == Vif::VIF_INDEX_INVALID) return (NULL); if (! is_sg()) return (NULL); mifs = could_assert_sg(); if (mifs.test(vif_index)) return (spt_assert_metric(vif_index)); mifs = could_assert_wc(); if (mifs.test(vif_index)) return (rpt_assert_metric(vif_index)); return (infinite_assert_metric()); } // Note: applies only for (*,G) AssertMetric * PimMre::my_assert_metric_wc(uint32_t vif_index) const { Mifset mifs; if (vif_index == Vif::VIF_INDEX_INVALID) return (NULL); if (! is_wc()) return (NULL); mifs = could_assert_wc(); if (mifs.test(vif_index)) return (rpt_assert_metric(vif_index)); return (infinite_assert_metric()); } // Note: applies only for (S,G) // Return true if state has changed, otherwise return false. bool PimMre::recompute_my_assert_metric_sg(uint32_t vif_index) { AssertMetric *my_assert_metric, *winner_metric; if (vif_index == Vif::VIF_INDEX_INVALID) return (false); if (! is_sg()) return (false); if (is_i_am_assert_loser_state(vif_index)) goto assert_loser_state_label; // All other states: ignore the change. return (false); assert_loser_state_label: // IamAssertLoser state my_assert_metric = my_assert_metric_sg(vif_index); winner_metric = assert_winner_metric_sg(vif_index); XLOG_ASSERT(winner_metric != NULL); XLOG_ASSERT(my_assert_metric != NULL); XLOG_ASSERT(my_assert_metric->addr() != winner_metric->addr()); // Test if my metric has become better if (! (*my_assert_metric > *winner_metric)) return (false); goto a5; a5: // * Delete assert info (AssertWinner(S,G,I), // and AssertWinnerMetric(S,G,I) assume default values). delete_assert_winner_metric_sg(vif_index); set_assert_noinfo_state(vif_index); return (true); } // Note: applies only for (*,G) // Return true if state has changed, otherwise return false. bool PimMre::recompute_my_assert_metric_wc(uint32_t vif_index) { AssertMetric *my_assert_metric, *winner_metric; if (vif_index == Vif::VIF_INDEX_INVALID) return (false); if (! is_wc()) return (false); if (is_i_am_assert_loser_state(vif_index)) goto assert_loser_state_label; // All other states: ignore the change. return (false); assert_loser_state_label: // IamAssertLoser state my_assert_metric = rpt_assert_metric(vif_index); winner_metric = assert_winner_metric_wc(vif_index); XLOG_ASSERT(winner_metric != NULL); // TODO: XXX: PAVPAVPAV: is this assert OK? E.g, what about if loser to (S,G) Winner? XLOG_ASSERT(my_assert_metric != NULL); XLOG_ASSERT(my_assert_metric->addr() != winner_metric->addr()); // Test if my metric has become better if (! (*my_assert_metric > *winner_metric)) return (false); goto a5; a5: // * Delete assert info (AssertWinner(*,G,I), // and AssertWinnerMetric(*,G,I) assume default values). delete_assert_winner_metric_wc(vif_index); set_assert_noinfo_state(vif_index); return (true); } // // "RPF_interface(S) stops being I" // // Note: applies only for (S,G) // Return true if state has changed, otherwise return false. bool PimMre::recompute_assert_rpf_interface_sg(uint32_t vif_index) { if (vif_index == Vif::VIF_INDEX_INVALID) return (false); if (! is_sg()) return (false); if (is_i_am_assert_loser_state(vif_index)) goto assert_loser_state_label; // All other states: ignore the change. return (false); assert_loser_state_label: // IamAssertLoser state if (rpf_interface_s() == vif_index) return (false); // Nothing changed goto a5; a5: // * Delete assert info (AssertWinner(S,G,I), // and AssertWinnerMetric(S,G,I) assume default values). delete_assert_winner_metric_sg(vif_index); set_assert_noinfo_state(vif_index); return (true); } // // "RPF_interface(RP(G)) stops being I" // Return true if state has changed, otherwise return false. bool PimMre::recompute_assert_rpf_interface_wc(uint32_t vif_index) { if (vif_index == Vif::VIF_INDEX_INVALID) return (false); if (! is_wc()) return (false); if (is_i_am_assert_loser_state(vif_index)) goto assert_loser_state_label; // All other states: ignore the change. return (false); assert_loser_state_label: // IamAssertLoser state if (rpf_interface_rp() == vif_index) return (false); // Nothing changed goto a5; a5: // * Delete assert info (AssertWinner(*,G,I), // and AssertWinnerMetric(*,G,I) assume default values). delete_assert_winner_metric_wc(vif_index); set_assert_noinfo_state(vif_index); return (true); } // Note: applies only for (S,G) // Return true if state has changed, otherwise return false. bool PimMre::recompute_assert_receive_join_sg(uint32_t vif_index) { if (vif_index == Vif::VIF_INDEX_INVALID) return (false); if (! is_sg()) return (false); if (is_i_am_assert_loser_state(vif_index)) goto assert_loser_state_label; // All other states: ignore the change. return (false); assert_loser_state_label: // IamAssertLoser state goto a5; a5: // * Delete assert info (AssertWinner(S,G,I), // and AssertWinnerMetric(S,G,I) assume default values). delete_assert_winner_metric_sg(vif_index); set_assert_noinfo_state(vif_index); return (true); } // Note: applies only for (*,G) // Return true if state has changed, otherwise return false. bool PimMre::recompute_assert_receive_join_wc(uint32_t vif_index) { if (vif_index == Vif::VIF_INDEX_INVALID) return (false); if (! is_wc()) return (false); if (is_i_am_assert_loser_state(vif_index)) goto assert_loser_state_label; // All other states: ignore the change. return (false); assert_loser_state_label: // IamAssertLoser state goto a5; a5: // * Delete assert info (AssertWinner(*,G,I), // and AssertWinnerMetric(*,G,I) assume default values). delete_assert_winner_metric_wc(vif_index); set_assert_noinfo_state(vif_index); return (true); } xorp/pim/pim_scope_zone_table.hh0000664000076400007640000000712111540224232017142 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/pim/pim_scope_zone_table.hh,v 1.15 2008/10/02 21:57:55 bms Exp $ #ifndef __PIM_PIM_SCOPE_ZONE_TABLE_HH__ #define __PIM_PIM_SCOPE_ZONE_TABLE_HH__ // // PIM scope zone table definition. // #include "libxorp/ipvxnet.hh" #include "mrt/mifset.hh" // // Constants definitions // // // Structures/classes, typedefs and macros // class PimNode; class PimScopeZone; // The PIM Scope Zone ID class PimScopeZoneId { public: PimScopeZoneId(const IPvXNet& scope_zone_prefix, bool is_scope_zone); const IPvXNet& scope_zone_prefix() const { return (_scope_zone_prefix); } bool is_scope_zone() const { return (_is_scope_zone); } bool operator==(const PimScopeZoneId& other) const; bool is_overlap(const PimScopeZoneId& other) const; bool contains(const IPvXNet& ipvxnet) const; bool contains(const IPvX& ipvx) const; string str() const; private: IPvXNet _scope_zone_prefix; // The scope zone address prefix bool _is_scope_zone; // If true, this is admin. scoped zone }; // PIM-specific scope zone table class PimScopeZoneTable { public: PimScopeZoneTable(PimNode& pim_node); virtual ~PimScopeZoneTable(); list& pim_scope_zone_list() { return (_pim_scope_zone_list); } void add_scope_zone(const IPvXNet& scope_zone_prefix, uint32_t vif_index); void delete_scope_zone(const IPvXNet& scope_zone_prefix, uint32_t vif_index); bool is_scoped(const IPvX& addr, uint32_t vif_index) const; bool is_scoped(const PimScopeZoneId& zone_id, uint32_t vif_index) const; bool is_zone_border_router(const IPvXNet& group_prefix) const; PimNode& pim_node() const { return (_pim_node); } private: // Private functions // Private state PimNode& _pim_node; // The PIM node I belong to list _pim_scope_zone_list; // The list with scoped zones }; class PimScopeZone { public: PimScopeZone(const IPvXNet& scope_zone_prefix, const Mifset& scoped_vifs); #ifdef XORP_USE_USTL PimScopeZone() { } #endif virtual ~PimScopeZone(); const IPvXNet& scope_zone_prefix() const { return (_scope_zone_prefix); } void set_scoped_vif(uint32_t vif_index, bool v); bool is_empty() const { return (! _scoped_vifs.any()); } bool is_set(uint32_t vif_index) const; bool is_scoped(const IPvX& addr, uint32_t vif_index) const; bool is_scoped(const PimScopeZoneId& zone_id, uint32_t vif_index) const; bool is_same_scope_zone(const IPvXNet& scope_zone_prefix) const; private: IPvXNet _scope_zone_prefix; // The scoped zone prefix Mifset _scoped_vifs; // The set of scoped vifs }; // // Global variables // // // Global functions prototypes // #endif // __PIM_PIM_SCOPE_ZONE_TABLE_HH__ xorp/pim/pim_mre_task.cc0000664000076400007640000014516711635757530015460 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // PIM Multicast Routing Entry task // #include "pim_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvx.hh" #include "libxorp/time_slice.hh" #include "libxorp/utils.hh" #include "pim_mfc.hh" #include "pim_mre_task.hh" #include "pim_mrt.hh" #include "pim_nbr.hh" #include "pim_node.hh" PimMreTask::PimMreTask(PimMrt* pim_mrt, PimMreTrackState::input_state_t input_state) : _pim_mrt(pim_mrt), _time_slice(100000, 20), // 100ms, test every 20th iter _input_state(input_state), // _is_set_rp_addr_rp(false), _rp_addr_rp(IPvX::ZERO(family())), _is_set_rp_addr_prefix_rp(false), _rp_addr_prefix_rp(IPvX::ZERO(family()), 0), _is_processing_rp(false), _is_processing_rp_addr_rp(false), _processing_rp_addr_rp(IPvX::ZERO(family())), // _is_set_group_addr_wc(false), _group_addr_wc(IPvX::ZERO(family())), _is_set_rp_addr_wc(false), _rp_addr_wc(IPvX::ZERO(family())), _is_set_group_addr_prefix_wc(false), _group_addr_prefix_wc(IPvX::ZERO(family()), 0), _is_processing_wc(false), _is_processing_rp_addr_wc(false), _processing_rp_addr_wc(IPvX::ZERO(family())), _is_processing_group_addr_wc(false), _processing_group_addr_wc(IPvX::ZERO(family())), // _is_set_source_addr_sg_sg_rpt(false), _source_addr_sg_sg_rpt(IPvX::ZERO(family())), _is_set_group_addr_sg_sg_rpt(false), _group_addr_sg_sg_rpt(IPvX::ZERO(family())), _is_set_source_addr_prefix_sg_sg_rpt(false), _source_addr_prefix_sg_sg_rpt(IPvX::ZERO(family()), 0), _is_set_rp_addr_sg_sg_rpt(false), _rp_addr_sg_sg_rpt(IPvX::ZERO(family())), _is_processing_sg_sg_rpt(false), _is_processing_sg_source_addr_sg_sg_rpt(false), _processing_sg_source_addr_sg_sg_rpt(IPvX::ZERO(family())), _is_processing_sg_rpt_source_addr_sg_sg_rpt(false), _processing_sg_rpt_source_addr_sg_sg_rpt(IPvX::ZERO(family())), _is_processing_sg_group_addr_sg_sg_rpt(false), _processing_sg_group_addr_sg_sg_rpt(IPvX::ZERO(family())), _is_processing_sg_rpt_group_addr_sg_sg_rpt(false), _processing_sg_rpt_group_addr_sg_sg_rpt(IPvX::ZERO(family())), _is_processing_rp_addr_sg(false), _is_processing_rp_addr_sg_rpt(false), _processing_rp_addr_sg_sg_rpt(IPvX::ZERO(family())), // _is_set_source_addr_mfc(false), _source_addr_mfc(IPvX::ZERO(family())), _is_set_group_addr_mfc(false), _group_addr_mfc(IPvX::ZERO(family())), _is_set_source_addr_prefix_mfc(false), _source_addr_prefix_mfc(IPvX::ZERO(family()), 0), _is_set_rp_addr_mfc(false), _rp_addr_mfc(IPvX::ZERO(family())), _is_processing_mfc(false), _is_processing_source_addr_mfc(false), _processing_source_addr_mfc(IPvX::ZERO(family())), _is_processing_group_addr_mfc(false), _processing_group_addr_mfc(IPvX::ZERO(family())), _is_processing_rp_addr_mfc(false), _processing_rp_addr_mfc(IPvX::ZERO(family())), // _is_set_pim_nbr_addr_rp(false), _is_set_pim_nbr_addr_wc(false), _is_set_pim_nbr_addr_sg_sg_rpt(false), _pim_nbr_addr(IPvX::ZERO(family())), _is_processing_pim_nbr_addr_rp(false), _is_processing_pim_nbr_addr_wc(false), _is_processing_pim_nbr_addr_sg(false), _is_processing_pim_nbr_addr_sg_rpt(false), // _vif_index(Vif::VIF_INDEX_INVALID), _addr_arg(IPvX::ZERO(family())) { const PimMreTrackState& pim_mre_track_state = _pim_mrt->pim_mre_track_state(); // Get the lists of actions _action_list_rp = pim_mre_track_state.output_action_rp(input_state); _action_list_wc = pim_mre_track_state.output_action_wc(input_state); _action_list_sg_sg_rpt = pim_mre_track_state.output_action_sg_sg_rpt( input_state); _action_list_mfc = pim_mre_track_state.output_action_mfc(input_state); } PimMreTask::~PimMreTask() { // Delete the (*,*,RP) entries pending deletion while (! _pim_mre_rp_delete_list.empty()) { PimMre *pim_mre = _pim_mre_rp_delete_list.front(); _pim_mre_rp_delete_list.pop_front(); if (pim_mre->is_task_delete_done()) delete pim_mre; } // Delete the (*,G) entries pending deletion while (! _pim_mre_wc_delete_list.empty()) { PimMre *pim_mre = _pim_mre_wc_delete_list.front(); _pim_mre_wc_delete_list.pop_front(); if (pim_mre->is_task_delete_done()) delete pim_mre; } // Delete the (S,G) entries pending deletion while (! _pim_mre_sg_delete_list.empty()) { PimMre *pim_mre = _pim_mre_sg_delete_list.front(); _pim_mre_sg_delete_list.pop_front(); if (pim_mre->is_task_delete_done()) delete pim_mre; } // Delete the (S,G,rpt) entries pending deletion while (! _pim_mre_sg_rpt_delete_list.empty()) { PimMre *pim_mre = _pim_mre_sg_rpt_delete_list.front(); _pim_mre_sg_rpt_delete_list.pop_front(); if (pim_mre->is_task_delete_done()) delete pim_mre; } // Delete the PimMfc entries pending deletion while (! _pim_mfc_delete_list.empty()) { PimMfc *pim_mfc = _pim_mfc_delete_list.front(); _pim_mfc_delete_list.pop_front(); if (pim_mfc->is_task_delete_done()) delete pim_mfc; } // Delete the Mrib entries pending deletion delete_pointers_list(_mrib_delete_list); pim_mrt()->delete_task(this); } PimNode* PimMreTask::pim_node() const { return _pim_mrt->pim_node(); } int PimMreTask::family() const { return (_pim_mrt->family()); } // // Run the task. // // Return true if the processing was saved for later because it was taking too // long time, otherwise return false. bool PimMreTask::run_task() { _time_slice.reset(); if (run_task_rp()) { // The time slice has expired. Keep processing this task. return (true); } // // The task has been completed, hence delete the task. // delete this; // XXX: should be right before the return return (false); } // // Run the (*,*,RP) related actions if any. // In addition, call the appropriate method to run the (*,G) related actions. // // Return true if the processing was saved for later because it was taking too // long time, otherwise return false. bool PimMreTask::run_task_rp() { bool ret_value = true; PimMre *pim_mre; PimMrtRp::const_sg_iterator rp_iter, rp_iter_begin, rp_iter_end; // Test if we need to continue processing of (*,G) or (S,G) // or (S,G,rpt) entries from an earlier time slice. // XXX: run_task_wc() will take care of testing to continue // processing for (S,G), (S,G,rpt) or PimMfc entries. if (_is_processing_wc || _is_processing_sg_sg_rpt || _is_processing_mfc) { if (run_task_wc()) return (true); } if (_is_set_rp_addr_rp) { // Actions for a single RP specified by an unicast address. // XXX: for simplicity, we always perform the (*,*,RP) actions without // considering the time slice, simply because there is no more than // one (*,*,RP) entry for the particular RP. pim_mre = pim_mrt()->pim_mre_find(_rp_addr_rp, IPvX::ZERO(family()), PIM_MRE_RP, 0); // Perform the (*,*,RP) actions perform_pim_mre_actions(pim_mre); // Prepare the (*,G) related state _rp_addr_wc = _rp_addr_rp; _is_set_rp_addr_wc = true; } _is_set_rp_addr_rp = false; if (_is_set_rp_addr_prefix_rp) { // Actions for a set of RPs specified by an unicast address prefix. // // Get the iterator boundaries by considering whether // we had to stop processing during an earlier time slice. // rp_iter_end = pim_mrt()->pim_mrt_rp().source_by_prefix_end( _rp_addr_prefix_rp); if (! _is_processing_rp_addr_rp) { rp_iter_begin = pim_mrt()->pim_mrt_rp().source_by_prefix_begin( _rp_addr_prefix_rp); } else { rp_iter_begin = pim_mrt()->pim_mrt_rp().source_by_addr_begin( _processing_rp_addr_rp); } for (rp_iter = rp_iter_begin; rp_iter != rp_iter_end; ) { pim_mre = rp_iter->second; ++rp_iter; _rp_addr_rp = *pim_mre->rp_addr_ptr(); // The RP address // Perform the (*,*,RP) actions perform_pim_mre_actions(pim_mre); // Schedule and perform the (*,G), (S,G), (S,G,rpt), PimMfc // processing (if any) for this RP address prefix. bool time_slice_expired = false; if (! (_action_list_wc.empty() && _action_list_sg_sg_rpt.empty() && _action_list_mfc.empty())) { _rp_addr_wc = _rp_addr_rp; _is_set_rp_addr_wc = true; if (run_task_wc()) time_slice_expired = true; } if (time_slice_expired || _time_slice.is_expired()) { // Time slice has expired. Save state if necessary and return. if (rp_iter != rp_iter_end) { pim_mre = rp_iter->second; _is_processing_rp = true; _is_processing_rp_addr_rp = true; _processing_rp_addr_rp = *pim_mre->rp_addr_ptr(); } return (true); } } _is_processing_rp_addr_rp = false; } _is_set_rp_addr_prefix_rp = false; if (_is_set_pim_nbr_addr_rp) { // Actions for a set of (*,*,RP) specified by a PimNbr address. // Prepare the processing of the (*,*,RP) entries for this PimNbr if (! _is_processing_pim_nbr_addr_rp) pim_node()->init_processing_pim_mre_rp(_vif_index, _pim_nbr_addr); PimNbr *pim_nbr = pim_node()->find_processing_pim_mre_rp(_vif_index, _pim_nbr_addr); if (pim_nbr != NULL) { bool more_processing = !pim_nbr->processing_pim_mre_rp_list().empty(); while (more_processing) { // Move the PimMre entry to the non-processing list PimMre *pim_mre = pim_nbr->processing_pim_mre_rp_list().front(); pim_nbr->processing_pim_mre_rp_list().pop_front(); pim_nbr->pim_mre_rp_list().push_back(pim_mre); // XXX: we need to test the 'processing_pim_mre_rp_list()' // now in case the PimRp entry is deleted during processing. more_processing = !pim_nbr->processing_pim_mre_rp_list().empty(); // Perform the (*,*,RP) actions perform_pim_mre_actions(pim_mre); if (_time_slice.is_expired()) { // Stop processing. Save state to continue later from here. _is_processing_rp = true; _is_processing_pim_nbr_addr_rp = true; // XXX: no need to keep state for the current entry // we are processing because the // PimNbr::processing_pim_mre_rp_list() // keeps track of that. return (true); } } } _is_processing_pim_nbr_addr_rp = false; _is_set_pim_nbr_addr_wc = true; } _is_set_pim_nbr_addr_rp = false; // // Process the list of (*,*,RP) PimMre entries // while (! _pim_mre_rp_list.empty()) { PimMre *pim_mre = _pim_mre_rp_list.front(); _pim_mre_rp_list.pop_front(); // Perform the (*,*,RP) actions perform_pim_mre_actions(pim_mre); // Add to the list of processed entries _pim_mre_rp_processed_list.push_back(pim_mre); XLOG_ASSERT(pim_mre->rp_addr_ptr() != NULL); // Schedule and perform the (*,G), (S,G), (S,G,rpt), PimMfc // processing (if any) for this RP address. bool time_slice_expired = false; if (! (_action_list_wc.empty() && _action_list_sg_sg_rpt.empty() && _action_list_mfc.empty())) { _rp_addr_wc = *pim_mre->rp_addr_ptr(); _is_set_rp_addr_wc = true; if (run_task_wc()) time_slice_expired = true; } if (time_slice_expired || _time_slice.is_expired()) { // Time slice has expired. Save state if necessary and return. return (true); } } // // We are done with the (*,*,RP) processing // _is_processing_rp = false; _is_processing_rp_addr_rp = false; _is_processing_pim_nbr_addr_rp = false; // // Schedule and perform the (*,G), (S,G), (S,G,rpt), PimMfc // processing (if any). // do { if (_action_list_wc.empty() && _action_list_sg_sg_rpt.empty() && _action_list_mfc.empty()) { ret_value = false; break; } if (run_task_wc()) { ret_value = true; break; } ret_value = false; break; } while (false); if (ret_value) return (ret_value); // // Delete the (*,*,RP) PimMre entries that are pending deletion // while (! _pim_mre_rp_delete_list.empty()) { PimMre *pim_mre = _pim_mre_rp_delete_list.front(); _pim_mre_rp_delete_list.pop_front(); if (pim_mre->is_task_delete_done()) delete pim_mre; if (_time_slice.is_expired()) return (true); } return (ret_value); } // // Run the (*,G) related actions if any. // In addition, call the appropriate method to run the (S,G), (S,G,rpt), PimMfc // related actions. // // Return true if the processing was saved for later because it was taking too // long time, otherwise return false. bool PimMreTask::run_task_wc() { bool ret_value = true; PimMre *pim_mre; PimMrtG::const_gs_iterator g_iter, g_iter_begin, g_iter_end; // Test if we need to continue processing of (S,G) or (S,G,rpt) // entries from an earlier time slice. if (_is_processing_sg_sg_rpt || _is_processing_mfc) { if (run_task_sg_sg_rpt()) return (true); } if (_is_set_group_addr_wc) { // Actions for a single group specified by a multicast address. // XXX: for simplicity, we always perform the (*,G) actions without // considering the time slice, simply because in this case there is // only one (*,G) entry. pim_mre = pim_mrt()->pim_mre_find(IPvX::ZERO(family()), _group_addr_wc, PIM_MRE_WC, 0); // Perform the (*,G) actions perform_pim_mre_actions(pim_mre); // Prepare the (S,G) and (S,G,rpt) related state _group_addr_sg_sg_rpt = _group_addr_wc; _is_set_group_addr_sg_sg_rpt = true; } _is_set_group_addr_wc = false; if (_is_set_rp_addr_wc) { // Actions for a set of (*,G) entries specified by the RP address // they match to. PimRp *pim_rp; RpTable& rp_table = pim_node()->rp_table(); // Prepare the processing of the (*,G) entries for this RP address if (! _is_processing_rp_addr_wc) { rp_table.init_processing_pim_mre_wc(_rp_addr_wc); } else { _rp_addr_wc = _processing_rp_addr_wc; } while ((pim_rp = rp_table.find_processing_pim_mre_wc(_rp_addr_wc)) != NULL) { bool more_processing = !pim_rp->processing_pim_mre_wc_list().empty(); while (more_processing) { // Move the PimMre entry to the non-processing list PimMre *pim_mre = pim_rp->processing_pim_mre_wc_list().front(); pim_rp->processing_pim_mre_wc_list().pop_front(); pim_rp->pim_mre_wc_list().push_back(pim_mre); // XXX: we need to test the 'processing_pim_mre_wc_list()' // now in case the PimRp entry is deleted during processing. more_processing = !pim_rp->processing_pim_mre_wc_list().empty(); // Perform the (*,G) actions perform_pim_mre_actions(pim_mre); // Schedule and perform the (*,G), (S,G), (S,G,rpt), PimMfc // processing (if any) for this multicast group. bool time_slice_expired = false; if (! (_action_list_sg_sg_rpt.empty() && _action_list_mfc.empty())) { _is_set_source_addr_sg_sg_rpt = false; _group_addr_sg_sg_rpt = pim_mre->group_addr(); _is_set_group_addr_sg_sg_rpt = true; _is_set_source_addr_prefix_sg_sg_rpt = false; if (run_task_sg_sg_rpt()) time_slice_expired = true; } if (time_slice_expired || _time_slice.is_expired()) { // Stop processing. Save state to continue later from here. if (more_processing) { _is_processing_wc = true; _is_processing_rp_addr_wc = true; _processing_rp_addr_wc = _rp_addr_wc; // XXX: no need to keep state for the current group // we are processing because the // PimRp::processing_pim_mre_wc_list() // keeps track of that. } return (true); } } } _is_processing_rp_addr_wc = false; // Prepare the (S,G) and (S,G,rpt) related state _rp_addr_sg_sg_rpt = _rp_addr_wc; _is_set_rp_addr_sg_sg_rpt = true; } _is_set_rp_addr_wc = false; if (_is_set_group_addr_prefix_wc) { // Actions for a set of (*,G) entries specified by a multicast // address prefix. // // Get the iterator boundaries by considering whether // we had to stop processing during an earlier time slice. // g_iter_end = pim_mrt()->pim_mrt_g().group_by_prefix_end( _group_addr_prefix_wc); if (! _is_processing_group_addr_wc) { g_iter_begin = pim_mrt()->pim_mrt_g().group_by_prefix_begin( _group_addr_prefix_wc); } else { g_iter_begin = pim_mrt()->pim_mrt_g().group_by_addr_begin( _processing_group_addr_wc); } for (g_iter = g_iter_begin; g_iter != g_iter_end; ) { pim_mre = g_iter->second; ++g_iter; // Perform the (*,G) actions perform_pim_mre_actions(pim_mre); // Schedule and perform the (S,G), (S,G,rpt), PimMfc // processing (if any) for this multicast group. bool time_slice_expired = false; if (! (_action_list_sg_sg_rpt.empty() && _action_list_mfc.empty())) { _is_set_source_addr_sg_sg_rpt = false; _group_addr_sg_sg_rpt = pim_mre->group_addr(); _is_set_group_addr_sg_sg_rpt = true; _is_set_source_addr_prefix_sg_sg_rpt = false; if (run_task_sg_sg_rpt()) time_slice_expired = true; } if (time_slice_expired || _time_slice.is_expired()) { // Time slice has expired. Save state if necessary and return. if (g_iter != g_iter_end) { pim_mre = g_iter->second; _processing_group_addr_wc = pim_mre->group_addr(); _is_processing_wc = true; _is_processing_group_addr_wc = true; } return (true); } } _is_processing_group_addr_wc = false; } _is_set_group_addr_prefix_wc = false; if (_is_set_pim_nbr_addr_wc) { // Actions for a set of (*,G) specified by a PimNbr address. // Prepare the processing of the (*,G) entries for this PimNbr if (! _is_processing_pim_nbr_addr_wc) pim_node()->init_processing_pim_mre_wc(_vif_index, _pim_nbr_addr); PimNbr *pim_nbr = pim_node()->find_processing_pim_mre_wc(_vif_index, _pim_nbr_addr); if (pim_nbr != NULL) { bool more_processing = !pim_nbr->processing_pim_mre_wc_list().empty(); while (more_processing) { // Move the PimMre entry to the non-processing list PimMre *pim_mre = pim_nbr->processing_pim_mre_wc_list().front(); pim_nbr->processing_pim_mre_wc_list().pop_front(); pim_nbr->pim_mre_wc_list().push_back(pim_mre); // we need to test the 'processing_pim_mre_wc_list()' // now in case the PimRp entry is deleted during processing. more_processing = !pim_nbr->processing_pim_mre_wc_list().empty(); // Perform the (*,G) actions perform_pim_mre_actions(pim_mre); // Schedule and perform the (S,G), (S,G,rpt), PimMfc // processing (if any) for this multicast group. // XXX: note that this may result in processing // those entries twice. The reason is because the (S,G) // and (S,G,rpt) entries are added to the appropriate list // for this PimNbr even if they may have a (*,G) entry. // If this is not desirable, then remove the code below. // However, if we want to process the (S,G) and (S,G,rpt) // entries right after the corresponding (*,G) entry, // we need the processing below. bool time_slice_expired = false; if (! (_action_list_sg_sg_rpt.empty() && _action_list_mfc.empty())) { _is_set_source_addr_sg_sg_rpt = false; _group_addr_sg_sg_rpt = pim_mre->group_addr(); _is_set_group_addr_sg_sg_rpt = true; _is_set_source_addr_prefix_sg_sg_rpt = false; if (run_task_sg_sg_rpt()) time_slice_expired = true; } if (time_slice_expired || _time_slice.is_expired()) { // Time slice has expired. Save state to continue later // from here. _is_processing_wc = true; _is_processing_pim_nbr_addr_wc = true; // XXX: no need to keep state for the current entry // we are processing because the // PimNbr::processing_pim_mre_wc_list() // keeps track of that. return (true); } } } _is_processing_pim_nbr_addr_wc = false; _is_set_pim_nbr_addr_sg_sg_rpt = true; } _is_set_pim_nbr_addr_wc = false; // // Process the list of (*,G) PimMre entries // while (! _pim_mre_wc_list.empty()) { PimMre *pim_mre = _pim_mre_wc_list.front(); _pim_mre_wc_list.pop_front(); // Perform the (*,G) actions perform_pim_mre_actions(pim_mre); // Add to the list of processed entries _pim_mre_wc_processed_list.push_back(pim_mre); // Schedule and perform the (S,G), (S,G,rpt), PimMfc // processing (if any) for this multicast group. bool time_slice_expired = false; if (! (_action_list_sg_sg_rpt.empty() && _action_list_mfc.empty())) { _is_set_source_addr_sg_sg_rpt = false; _group_addr_sg_sg_rpt = pim_mre->group_addr(); _is_set_group_addr_sg_sg_rpt = true; _is_set_source_addr_prefix_sg_sg_rpt = false; if (run_task_sg_sg_rpt()) time_slice_expired = true; } if (time_slice_expired || _time_slice.is_expired()) { // Time slice has expired. Save state if necessary and return. return (true); } } // // We are done with the (*,G) processing // _is_processing_wc = false; _is_processing_rp_addr_wc = false; _is_processing_group_addr_wc = false; _is_processing_pim_nbr_addr_wc = false; // // Schedule and perform the (S,G), (S,G,rpt), PimMfc processing (if any) // do { if (_action_list_sg_sg_rpt.empty() && _action_list_mfc.empty()) { ret_value = false; break; } if (run_task_sg_sg_rpt()) { ret_value = true; break; } ret_value = false; break; } while (false); if (ret_value) return (ret_value); // // Delete the (*,G) PimMre entries that are pending deletion // while (! _pim_mre_wc_delete_list.empty()) { PimMre *pim_mre = _pim_mre_wc_delete_list.front(); _pim_mre_wc_delete_list.pop_front(); if (pim_mre->is_task_delete_done()) delete pim_mre; if (_time_slice.is_expired()) return (true); } return (ret_value); } // // Run the (S,G) and (S,G,rpt) related actions if any. // // Return true if the processing was saved for later because it was taking too // long time, otherwise return false. bool PimMreTask::run_task_sg_sg_rpt() { bool ret_value = true; PimMre *pim_mre_sg, *pim_mre_sg_rpt; // Test if we need to continue processing of PimMfc // entries from an earlier time slice. if (_is_processing_mfc) { if (run_task_mfc()) return (true); } if (_is_set_source_addr_sg_sg_rpt && _is_set_group_addr_sg_sg_rpt) { // Actions for a single (S,G) and/or (S,G,rpt) entry // XXX: for simplicity, we always perform the (S,G) and/or (S,G,rpt) // actions without considering the time slice, simply because in this // case there are no more than two entries. pim_mre_sg = pim_mrt()->pim_mre_find(_source_addr_sg_sg_rpt, _group_addr_sg_sg_rpt, PIM_MRE_SG, 0); if (pim_mre_sg != NULL) pim_mre_sg_rpt = pim_mre_sg->sg_rpt_entry(); else pim_mre_sg_rpt = pim_mrt()->pim_mre_find(_source_addr_sg_sg_rpt, _group_addr_sg_sg_rpt, PIM_MRE_SG_RPT, 0); // Perform the (S,G) and (S,G,rpt) actions perform_pim_mre_sg_sg_rpt_actions(pim_mre_sg, pim_mre_sg_rpt); // Prepare the PimMfc related state _source_addr_mfc = _source_addr_sg_sg_rpt; _is_set_source_addr_mfc = true; _group_addr_mfc = _group_addr_sg_sg_rpt; _is_set_group_addr_mfc = true; } if ( (! _is_set_source_addr_sg_sg_rpt) && _is_set_group_addr_sg_sg_rpt) { // Actions for a set of (S,G) and/or (S,G,rpt) entries for a // particular multicast group address. // // Perform the actions for all (S,G) and (S,G,rpt) entries // PimMrtSg::const_gs_iterator gs_iter, gs_iter_begin, gs_iter_end; gs_iter_end = pim_mrt()->pim_mrt_sg().group_by_addr_end( _group_addr_sg_sg_rpt); if (! (_is_processing_sg_source_addr_sg_sg_rpt || _is_processing_sg_rpt_source_addr_sg_sg_rpt)) { gs_iter_begin = pim_mrt()->pim_mrt_sg().group_by_addr_begin( _group_addr_sg_sg_rpt); } else { if (_is_processing_sg_source_addr_sg_sg_rpt) { gs_iter_begin = pim_mrt()->pim_mrt_sg().group_source_by_addr_begin( _processing_sg_source_addr_sg_sg_rpt, _group_addr_sg_sg_rpt); } else { // XXX: continue with the (S,G,rpt) processing later gs_iter_begin = gs_iter_end; } } for (gs_iter = gs_iter_begin; gs_iter != gs_iter_end; ) { pim_mre_sg = gs_iter->second; ++gs_iter; pim_mre_sg_rpt = pim_mre_sg->sg_rpt_entry(); // Perform the (S,G) and (S,G,rpt) actions perform_pim_mre_sg_sg_rpt_actions(pim_mre_sg, pim_mre_sg_rpt); if (_time_slice.is_expired()) { // Stop processing. Save state to continue later from here. if (gs_iter != gs_iter_end) { pim_mre_sg = gs_iter->second; _processing_sg_source_addr_sg_sg_rpt = pim_mre_sg->source_addr(); _is_processing_sg_sg_rpt = true; _is_processing_sg_source_addr_sg_sg_rpt = true; } return (true); } } _is_processing_sg_source_addr_sg_sg_rpt = false; // // Perform the actions for only those (S,G,rpt) entries that do not // have a corresponding (S,G) entry. // gs_iter_end = pim_mrt()->pim_mrt_sg_rpt().group_by_addr_end( _group_addr_sg_sg_rpt); if (! _is_processing_sg_rpt_source_addr_sg_sg_rpt) { gs_iter_begin = pim_mrt()->pim_mrt_sg_rpt().group_by_addr_begin( _group_addr_sg_sg_rpt); } else { gs_iter_begin = pim_mrt()->pim_mrt_sg_rpt().group_source_by_addr_begin( _processing_sg_rpt_source_addr_sg_sg_rpt, _group_addr_sg_sg_rpt); } for (gs_iter = gs_iter_begin; gs_iter != gs_iter_end; ) { pim_mre_sg_rpt = gs_iter->second; ++gs_iter; pim_mre_sg = pim_mre_sg_rpt->sg_entry(); if (pim_mre_sg != NULL) continue; // XXX: The (S,G,rpt) entry was already processed // Perform the (S,G,rpt) actions perform_pim_mre_actions(pim_mre_sg_rpt); if (_time_slice.is_expired()) { // Stop processing. Save state to continue later from here. if (gs_iter != gs_iter_end) { pim_mre_sg_rpt = gs_iter->second; _processing_sg_rpt_source_addr_sg_sg_rpt = pim_mre_sg_rpt->source_addr(); _is_processing_sg_sg_rpt = true; _is_processing_sg_rpt_source_addr_sg_sg_rpt = true; } return (true); } } _is_processing_sg_rpt_source_addr_sg_sg_rpt = false; // Prepare the PimMfc related state _group_addr_mfc = _group_addr_sg_sg_rpt; _is_set_group_addr_mfc = true; } _is_set_source_addr_sg_sg_rpt = false; _is_set_group_addr_sg_sg_rpt = false; if (_is_set_source_addr_prefix_sg_sg_rpt) { // Actions for a set of (S,G) and/or (S,G,rpt) entries specified // by an unicast source address prefix. // // Perform the actions for all (S,G) and (S,G,rpt) entries // PimMrtSg::const_sg_iterator sg_iter, sg_iter_begin, sg_iter_end; sg_iter_end = pim_mrt()->pim_mrt_sg().source_by_prefix_end( _source_addr_prefix_sg_sg_rpt); if (! (_is_processing_sg_group_addr_sg_sg_rpt || _is_processing_sg_rpt_group_addr_sg_sg_rpt)) { sg_iter_begin = pim_mrt()->pim_mrt_sg().source_by_prefix_begin( _source_addr_prefix_sg_sg_rpt); } else { if (_is_processing_sg_group_addr_sg_sg_rpt) { sg_iter_begin = pim_mrt()->pim_mrt_sg().source_group_by_addr_begin( _processing_sg_source_addr_sg_sg_rpt, _processing_sg_group_addr_sg_sg_rpt); } else { // XXX: continue with the (S,G,rpt) processing late sg_iter_begin = sg_iter_end; } } for (sg_iter = sg_iter_begin; sg_iter != sg_iter_end; ) { pim_mre_sg = sg_iter->second; ++sg_iter; pim_mre_sg_rpt = pim_mre_sg->sg_rpt_entry(); // Perform the (S,G) and (S,G,rpt) actions perform_pim_mre_sg_sg_rpt_actions(pim_mre_sg, pim_mre_sg_rpt); if (_time_slice.is_expired()) { // Stop processing. Save state to continue later from here. if (sg_iter != sg_iter_end) { pim_mre_sg = sg_iter->second; _processing_sg_source_addr_sg_sg_rpt = pim_mre_sg->source_addr(); _processing_sg_group_addr_sg_sg_rpt = pim_mre_sg->group_addr(); _is_processing_sg_sg_rpt = true; _is_processing_sg_source_addr_sg_sg_rpt = true; _is_processing_sg_group_addr_sg_sg_rpt = true; } return (true); } } _is_processing_sg_source_addr_sg_sg_rpt = false; _is_processing_sg_group_addr_sg_sg_rpt = false; // // Perform the actions for only those (S,G,rpt) entries that do not // have a corresponding (S,G) entry. // sg_iter_end = pim_mrt()->pim_mrt_sg_rpt().source_by_prefix_end( _source_addr_prefix_sg_sg_rpt); if (! _is_processing_sg_rpt_group_addr_sg_sg_rpt) { sg_iter_begin = pim_mrt()->pim_mrt_sg_rpt().source_by_prefix_begin( _source_addr_prefix_sg_sg_rpt); } else { sg_iter_begin = pim_mrt()->pim_mrt_sg_rpt().source_group_by_addr_begin( _processing_sg_rpt_source_addr_sg_sg_rpt, _processing_sg_rpt_group_addr_sg_sg_rpt); } for (sg_iter = sg_iter_begin; sg_iter != sg_iter_end; ) { pim_mre_sg_rpt = sg_iter->second; ++sg_iter; pim_mre_sg = pim_mre_sg_rpt->sg_entry(); if (pim_mre_sg != NULL) continue; // XXX: The (S,G,rpt) entry was already processed // Perform the (S,G,rpt) actions perform_pim_mre_actions(pim_mre_sg_rpt); if (_time_slice.is_expired()) { // Stop processing. Save state to continue later from here. if (sg_iter != sg_iter_end) { pim_mre_sg_rpt = sg_iter->second; _processing_sg_rpt_source_addr_sg_sg_rpt = pim_mre_sg_rpt->source_addr(); _processing_sg_rpt_group_addr_sg_sg_rpt = pim_mre_sg_rpt->group_addr(); _is_processing_sg_sg_rpt = true; _is_processing_sg_rpt_source_addr_sg_sg_rpt = true; _is_processing_sg_rpt_group_addr_sg_sg_rpt = true; } return (true); } } _is_processing_sg_rpt_source_addr_sg_sg_rpt = false; _is_processing_sg_rpt_group_addr_sg_sg_rpt = false; // Prepare the PimMfc related state _source_addr_prefix_mfc = _source_addr_prefix_sg_sg_rpt; _is_set_source_addr_prefix_mfc = true; } _is_set_source_addr_prefix_sg_sg_rpt = false; if (_is_set_rp_addr_sg_sg_rpt) { // Actions for a set of (S,G) and (S,G,rpt) entries specified // by the RP address they match to. // XXX: only those entries that have no (*,G) entry are on the // processing list. PimRp *pim_rp; RpTable& rp_table = pim_node()->rp_table(); // Prepare the processing of the (S,G) entries for this RP address if (! _is_processing_rp_addr_sg) { rp_table.init_processing_pim_mre_sg(_rp_addr_sg_sg_rpt); } else { _rp_addr_sg_sg_rpt = _processing_rp_addr_sg_sg_rpt; } while ((pim_rp = rp_table.find_processing_pim_mre_sg(_rp_addr_sg_sg_rpt)) != NULL) { bool more_processing = !pim_rp->processing_pim_mre_sg_list().empty(); while (more_processing) { // Move the PimMre entry to the non-processing list PimMre *pim_mre_sg = pim_rp->processing_pim_mre_sg_list().front(); pim_rp->processing_pim_mre_sg_list().pop_front(); pim_rp->pim_mre_sg_list().push_back(pim_mre_sg); // XXX: we need to test the 'processing_pim_mre_sg_list()' // now in case the PimRp entry is deleted during processing. more_processing = !pim_rp->processing_pim_mre_sg_list().empty(); _source_addr_sg_sg_rpt = pim_mre_sg->source_addr(); pim_mre_sg_rpt = pim_mre_sg->sg_rpt_entry(); // Perform the (S,G) and (S,G,rpt) actions perform_pim_mre_sg_sg_rpt_actions(pim_mre_sg, pim_mre_sg_rpt); if (_time_slice.is_expired()) { // Stop processing. Save state to continue later from here. if (more_processing) { _is_processing_sg_sg_rpt = true; _is_processing_rp_addr_sg = true; _processing_rp_addr_sg_sg_rpt = _rp_addr_sg_sg_rpt; // XXX: no need to keep state for the current entry // we are processing because the // PimRp::processing_pim_mre_sg_list() // keeps track of that. } return (true); } } } _is_processing_rp_addr_sg = false; // Prepare the processing of the (S,G,rpt) entries for this RP address if (! _is_processing_rp_addr_sg_rpt) { rp_table.init_processing_pim_mre_sg_rpt(_rp_addr_sg_sg_rpt); } else { _rp_addr_sg_sg_rpt = _processing_rp_addr_sg_sg_rpt; } while ((pim_rp = rp_table.find_processing_pim_mre_sg_rpt(_rp_addr_sg_sg_rpt)) != NULL) { bool more_processing = !pim_rp->processing_pim_mre_sg_rpt_list().empty(); while (more_processing) { // Move the PimMre entry to the non-processing list PimMre *pim_mre_sg_rpt = pim_rp->processing_pim_mre_sg_rpt_list().front(); pim_rp->processing_pim_mre_sg_rpt_list().pop_front(); pim_rp->pim_mre_sg_rpt_list().push_back(pim_mre_sg_rpt); // XXX: we need to test the 'processing_pim_mre_sg_rpt_list()' // now in case the PimRp entry is deleted during processing. more_processing = !pim_rp->processing_pim_mre_sg_rpt_list().empty(); _source_addr_sg_sg_rpt = pim_mre_sg_rpt->source_addr(); // Perform the (S,G,rpt) actions only for those entries // that don't have (S,G) entry if (pim_mre_sg_rpt->sg_entry() == NULL) perform_pim_mre_actions(pim_mre_sg_rpt); if (_time_slice.is_expired()) { // Stop processing. Save state to continue later from here. if (more_processing) { _is_processing_sg_sg_rpt = true; _is_processing_rp_addr_sg_rpt = true; _processing_rp_addr_sg_sg_rpt = _rp_addr_sg_sg_rpt; // XXX: no need to keep state for the current entry // we are processing because the // PimRp::processing_pim_mre_sg_list() // keeps track of that. } return (true); } } } _is_processing_rp_addr_sg_rpt = false; // Prepare the PimMfc related state _rp_addr_mfc = _rp_addr_sg_sg_rpt; _is_set_rp_addr_mfc = true; } _is_set_rp_addr_sg_sg_rpt = false; if (_is_set_pim_nbr_addr_sg_sg_rpt) { // Actions for a set of (S,G) and (S,G,rpt) entries specified // by the PimNbr address they match to. // XXX: only those entries that have no (*,G) entry are on the // processing list. // Prepare the processing of the (S,G) entries for this PimNbr if (! _is_processing_pim_nbr_addr_sg) pim_node()->init_processing_pim_mre_sg(_vif_index, _pim_nbr_addr); PimNbr *pim_nbr = pim_node()->find_processing_pim_mre_sg(_vif_index, _pim_nbr_addr); if (pim_nbr != NULL) { bool more_processing = !pim_nbr->processing_pim_mre_sg_list().empty(); while (more_processing) { // Move the PimMre entry to the non-processing list PimMre *pim_mre_sg = pim_nbr->processing_pim_mre_sg_list().front(); pim_nbr->processing_pim_mre_sg_list().pop_front(); pim_nbr->pim_mre_sg_list().push_back(pim_mre_sg); // XXX: we need to test the 'processing_pim_mre_sg_list()' // now in case the PimRp entry is deleted during processing. more_processing = !pim_nbr->processing_pim_mre_sg_list().empty(); _source_addr_sg_sg_rpt = pim_mre_sg->source_addr(); pim_mre_sg_rpt = pim_mre_sg->sg_rpt_entry(); // Perform the (S,G) and (S,G,rpt) actions perform_pim_mre_sg_sg_rpt_actions(pim_mre_sg, pim_mre_sg_rpt); if (_time_slice.is_expired()) { // Stop processing. Save state to continue later from here. if (more_processing) { _is_processing_sg_sg_rpt = true; _is_processing_pim_nbr_addr_sg = true; // XXX: no need to keep state for the current entry // we are processing because the // PimNbr::processing_pim_mre_sg_list() // keeps track of that. } return (true); } } } _is_processing_pim_nbr_addr_sg = false; // Prepare the processing of the (S,G,rpt) entries for this PimNbr if (! _is_processing_pim_nbr_addr_sg_rpt) pim_node()->init_processing_pim_mre_sg_rpt(_vif_index, _pim_nbr_addr); pim_nbr = pim_node()->find_processing_pim_mre_sg_rpt(_vif_index, _pim_nbr_addr); if (pim_nbr != NULL) { bool more_processing = !pim_nbr->processing_pim_mre_sg_rpt_list().empty(); while (more_processing) { // Move the PimMre entry to the non-processing list PimMre *pim_mre_sg_rpt = pim_nbr->processing_pim_mre_sg_rpt_list().front(); pim_nbr->processing_pim_mre_sg_rpt_list().pop_front(); pim_nbr->pim_mre_sg_rpt_list().push_back(pim_mre_sg_rpt); // XXX: we need to test the 'processing_pim_mre_sg_rpt_list()' // now in case the PimRp entry is deleted during processing. more_processing = !pim_nbr->processing_pim_mre_sg_rpt_list().empty(); _source_addr_sg_sg_rpt = pim_mre_sg_rpt->source_addr(); // Perform the (S,G,rpt) actions only for those entries // that don't have (S,G) entry if (pim_mre_sg_rpt->sg_entry() == NULL) perform_pim_mre_actions(pim_mre_sg_rpt); if (_time_slice.is_expired()) { // Stop processing. Save state to continue later from here. if (more_processing) { _is_processing_sg_sg_rpt = true; _is_processing_pim_nbr_addr_sg_rpt = true; // XXX: no need to keep state for the current entry // we are processing because the // PimRp::processing_pim_mre_sg_list() // keeps track of that. } return (true); } } } _is_processing_pim_nbr_addr_sg_rpt = false; } _is_set_pim_nbr_addr_sg_sg_rpt = false; // // Process the list of (S,G) PimMre entries // // TODO: is it OK always to process the (S,G) entries first, and then // the (S,G,rpt) entries? while (! _pim_mre_sg_list.empty()) { PimMre *pim_mre = _pim_mre_sg_list.front(); _pim_mre_sg_list.pop_front(); // Perform the (S,G) actions perform_pim_mre_actions(pim_mre); // Add to the list of processed entries _pim_mre_sg_processed_list.push_back(pim_mre); // Schedule and perform the PimMfc // processing (if any) for this source and multicast group. bool time_slice_expired = false; if (! (_action_list_mfc.empty())) { _source_addr_mfc = pim_mre->source_addr(); _is_set_source_addr_mfc = true; _group_addr_mfc = pim_mre->group_addr(); _is_set_group_addr_mfc = true; if (run_task_mfc()) time_slice_expired = true; } if (time_slice_expired || _time_slice.is_expired()) { // Time slice has expired. Save state if necessary and return. return (true); } } // // Process the list of (S,G,rpt) PimMre entries // while (! _pim_mre_sg_rpt_list.empty()) { PimMre *pim_mre = _pim_mre_sg_rpt_list.front(); _pim_mre_sg_rpt_list.pop_front(); // Perform the (S,G,rpt) actions perform_pim_mre_actions(pim_mre); // Add to the list of processed entries _pim_mre_sg_rpt_processed_list.push_back(pim_mre); // Schedule and perform the PimMfc // processing (if any) for this source and multicast group. bool time_slice_expired = false; if (! (_action_list_mfc.empty())) { _source_addr_mfc = pim_mre->source_addr(); _is_set_source_addr_mfc = true; _group_addr_mfc = pim_mre->group_addr(); _is_set_group_addr_mfc = true; if (run_task_mfc()) time_slice_expired = true; } if (time_slice_expired || _time_slice.is_expired()) { // Time slice has expired. Save state if necessary and return. return (true); } } // // We are done with the (S,G) and (S,G,rpt) processing // _is_processing_sg_sg_rpt = false; _is_processing_sg_source_addr_sg_sg_rpt = false; _is_processing_sg_rpt_source_addr_sg_sg_rpt = false; _is_processing_sg_group_addr_sg_sg_rpt = false; _is_processing_sg_rpt_group_addr_sg_sg_rpt = false; _is_processing_rp_addr_sg = false; _is_processing_rp_addr_sg_rpt = false; _is_processing_pim_nbr_addr_sg = false; _is_processing_pim_nbr_addr_sg_rpt = false; // // Schedule and perform the PimMfc processing (if any) // do { if (_action_list_mfc.empty()) { ret_value = false; break; } if (run_task_mfc()) { ret_value = true; break; } ret_value = false; break; } while (false); if (ret_value) return (ret_value); // // Delete the (S,G) PimMre entries that are pending deletion // while (! _pim_mre_sg_delete_list.empty()) { PimMre *pim_mre = _pim_mre_sg_delete_list.front(); _pim_mre_sg_delete_list.pop_front(); if (pim_mre->is_task_delete_done()) delete pim_mre; if (_time_slice.is_expired()) return (true); } // // Delete the (S,G,rpt) PimMre entries that are pending deletion // while (! _pim_mre_sg_rpt_delete_list.empty()) { PimMre *pim_mre = _pim_mre_sg_rpt_delete_list.front(); _pim_mre_sg_rpt_delete_list.pop_front(); if (pim_mre->is_task_delete_done()) delete pim_mre; if (_time_slice.is_expired()) return (true); } return (ret_value); } // // Run the PimMfc related actions if any. // // Return true if the processing was saved for later because it was taking too // long time, otherwise return false. bool PimMreTask::run_task_mfc() { PimMfc *pim_mfc; if (_is_set_source_addr_mfc && _is_set_group_addr_mfc) { // Actions for a single PimMfc entry // XXX: for simplicity, we always perform the PimMfc // actions without considering the time slice, simply because in this // case there is no more than one entry. pim_mfc = pim_mrt()->pim_mfc_find(_source_addr_mfc, _group_addr_mfc, false); // Perform the PimMfc actions perform_pim_mfc_actions(pim_mfc); } if ( (! _is_set_source_addr_mfc) && _is_set_group_addr_mfc) { // Actions for a set of PimMfc entries for a // particular multicast group address. // // Perform the actions for all PimMfc entries // PimMrtMfc::const_gs_iterator gs_iter, gs_iter_begin, gs_iter_end; gs_iter_end = pim_mrt()->pim_mrt_mfc().group_by_addr_end( _group_addr_mfc); if (! _is_processing_source_addr_mfc) { gs_iter_begin = pim_mrt()->pim_mrt_mfc().group_by_addr_begin( _group_addr_mfc); } else { gs_iter_begin = pim_mrt()->pim_mrt_mfc().group_source_by_addr_begin( _processing_source_addr_mfc, _group_addr_mfc); } for (gs_iter = gs_iter_begin; gs_iter != gs_iter_end; ) { pim_mfc = gs_iter->second; ++gs_iter; // Perform the PimMfc actions perform_pim_mfc_actions(pim_mfc); if (_time_slice.is_expired()) { // Stop processing. Save state to continue later from here. if (gs_iter != gs_iter_end) { pim_mfc = gs_iter->second; _processing_source_addr_mfc = pim_mfc->source_addr(); _is_processing_mfc = true; _is_processing_source_addr_mfc = true; } return (true); } } _is_processing_source_addr_mfc = false; } _is_set_source_addr_mfc = false; _is_set_group_addr_mfc = false; if (_is_set_source_addr_prefix_mfc) { // Actions for a set of PimMfc entries specified // by an unicast source address prefix. // // Perform the actions for all PimMfc entries // PimMrtMfc::const_sg_iterator sg_iter, sg_iter_begin, sg_iter_end; sg_iter_end = pim_mrt()->pim_mrt_mfc().source_by_prefix_end( _source_addr_prefix_mfc); if (! _is_processing_group_addr_mfc) { sg_iter_begin = pim_mrt()->pim_mrt_mfc().source_by_prefix_begin( _source_addr_prefix_mfc); } else { sg_iter_begin = pim_mrt()->pim_mrt_mfc().source_group_by_addr_begin( _processing_source_addr_mfc, _processing_group_addr_mfc); } for (sg_iter = sg_iter_begin; sg_iter != sg_iter_end; ) { pim_mfc = sg_iter->second; ++sg_iter; // Perform the PimMfc actions perform_pim_mfc_actions(pim_mfc); if (_time_slice.is_expired()) { // Stop processing. Save state to continue later from here. if (sg_iter != sg_iter_end) { pim_mfc = sg_iter->second; _processing_source_addr_mfc = pim_mfc->source_addr(); _processing_group_addr_mfc = pim_mfc->group_addr(); _is_processing_mfc = true; _is_processing_source_addr_mfc = true; _is_processing_group_addr_mfc = true; } return (true); } } _is_processing_source_addr_mfc = false; _is_processing_group_addr_mfc = false; } _is_set_source_addr_prefix_mfc = false; if (_is_set_rp_addr_mfc) { // Actions for a set of PimMfc entries specified // by the RP address they match to. // XXX: all entries are on the processing list. PimRp *pim_rp; RpTable& rp_table = pim_node()->rp_table(); // Prepare the processing of the PimMfc entries for this RP address if (! _is_processing_rp_addr_mfc) { rp_table.init_processing_pim_mfc(_rp_addr_mfc); } else { _rp_addr_mfc = _processing_rp_addr_mfc; } while ((pim_rp = rp_table.find_processing_pim_mfc(_rp_addr_mfc)) != NULL) { bool more_processing = !pim_rp->processing_pim_mfc_list().empty(); while (more_processing) { // Move the PimMfc entry to the non-processing list PimMfc *pim_mfc = pim_rp->processing_pim_mfc_list().front(); pim_rp->processing_pim_mfc_list().pop_front(); pim_rp->pim_mfc_list().push_back(pim_mfc); // XXX: we need to test the 'processing_pim_mfc_list()' // now in case the PimRp entry is deleted during processing. more_processing = !pim_rp->processing_pim_mfc_list().empty(); _source_addr_mfc = pim_mfc->source_addr(); // Perform the PimMfc actions perform_pim_mfc_actions(pim_mfc); if (_time_slice.is_expired()) { // Stop processing. Save state to continue later from here. if (more_processing) { _is_processing_mfc = true; _is_processing_rp_addr_mfc = true; _processing_rp_addr_mfc = _rp_addr_mfc; // XXX: no need to keep state for the current entry // we are processing because the // PimRp::processing_pim_mfc_list() // keeps track of that. } return (true); } } } _is_processing_rp_addr_mfc = false; } _is_set_rp_addr_mfc = false; // // Process the list of PimMfc entries // while (! _pim_mfc_list.empty()) { PimMfc *pim_mfc = _pim_mfc_list.front(); _pim_mfc_list.pop_front(); // Perform the PimMfc actions perform_pim_mfc_actions(pim_mfc); // Add to the list of processed entries _pim_mfc_processed_list.push_back(pim_mfc); if (_time_slice.is_expired()) { // Time slice has expired. Save state if necessary and return. return (true); } } // // We are done with the PimMfc processing // _is_processing_mfc = false; _is_processing_source_addr_mfc = false; _is_processing_group_addr_mfc = false; _is_processing_rp_addr_mfc = false; // // Delete the PimMfc entries that are pending deletion // while (! _pim_mfc_delete_list.empty()) { PimMfc *pim_mfc = _pim_mfc_delete_list.front(); _pim_mfc_delete_list.pop_front(); if (pim_mfc->is_task_delete_done()) delete pim_mfc; if (_time_slice.is_expired()) return (true); } return (false); } // // Perform the scheduled actions for a PimMre entry. // void PimMreTask::perform_pim_mre_actions(PimMre *pim_mre) { list::iterator action_iter; if (pim_mre == NULL) return; if (pim_mre->is_rp()) { for (action_iter = _action_list_rp.begin(); action_iter != _action_list_rp.end(); ++action_iter) { PimMreAction action = *action_iter; action.perform_action(*pim_mre, _vif_index, _addr_arg); } return; } if (pim_mre->is_wc()) { for (action_iter = _action_list_wc.begin(); action_iter != _action_list_wc.end(); ++action_iter) { PimMreAction action = *action_iter; action.perform_action(*pim_mre, _vif_index, _addr_arg); } return; } if (pim_mre->is_sg()) { for (action_iter = _action_list_sg_sg_rpt.begin(); action_iter != _action_list_sg_sg_rpt.end(); ++action_iter) { PimMreAction action = *action_iter; if (action.is_sg()) action.perform_action(*pim_mre, _vif_index, _addr_arg); } return; } if (pim_mre->is_sg_rpt()) { for (action_iter = _action_list_sg_sg_rpt.begin(); action_iter != _action_list_sg_sg_rpt.end(); ++action_iter) { PimMreAction action = *action_iter; if (action.is_sg_rpt()) action.perform_action(*pim_mre, _vif_index, _addr_arg); } return; } } // // Perform the (S,G) and (S,G,rpt) actions // void PimMreTask::perform_pim_mre_sg_sg_rpt_actions(PimMre *pim_mre_sg, PimMre *pim_mre_sg_rpt) { list::iterator action_iter; for (action_iter = _action_list_sg_sg_rpt.begin(); action_iter != _action_list_sg_sg_rpt.end(); ++action_iter) { PimMreAction action = *action_iter; if (action.is_sg()) { if (pim_mre_sg != NULL) action.perform_action(*pim_mre_sg, _vif_index, _addr_arg); } else if (action.is_sg_rpt()) { if (pim_mre_sg_rpt != NULL) action.perform_action(*pim_mre_sg_rpt, _vif_index, _addr_arg); } } } // // Perform the scheduled actions for a PimMfc entry. // void PimMreTask::perform_pim_mfc_actions(PimMfc *pim_mfc) { list::iterator action_iter; if (pim_mfc == NULL) return; for (action_iter = _action_list_mfc.begin(); action_iter != _action_list_mfc.end(); ++action_iter) { PimMreAction action = *action_iter; action.perform_action(*pim_mfc); } } // // Perform the scheduled actions for a PimMfc entry specified by // source and group address. // void PimMreTask::perform_pim_mfc_actions(const IPvX& source_addr, const IPvX& group_addr) { PimMfc *pim_mfc; pim_mfc = pim_mrt()->pim_mfc_find(source_addr, group_addr, false); perform_pim_mfc_actions(pim_mfc); } // // Add a PimMre entry to the appropriate list of entries to process // void PimMreTask::add_pim_mre(PimMre *pim_mre) { if (pim_mre->is_rp()) { _pim_mre_rp_list.push_back(pim_mre); return; } if (pim_mre->is_wc()) { _pim_mre_wc_list.push_back(pim_mre); return; } if (pim_mre->is_sg()) { _pim_mre_sg_list.push_back(pim_mre); return; } if (pim_mre->is_sg_rpt()) { _pim_mre_sg_rpt_list.push_back(pim_mre); return; } } // // Add a PimMre entry to the list of entries to delete // void PimMreTask::add_pim_mre_delete(PimMre *pim_mre) { if (pim_mre->is_rp()) { _pim_mre_rp_delete_list.push_back(pim_mre); return; } if (pim_mre->is_wc()) { _pim_mre_wc_delete_list.push_back(pim_mre); return; } if (pim_mre->is_sg()) { _pim_mre_sg_delete_list.push_back(pim_mre); return; } if (pim_mre->is_sg_rpt()) { _pim_mre_sg_rpt_delete_list.push_back(pim_mre); return; } } // // Add a PimMfc entry to the appropriate list of entries to process // void PimMreTask::add_pim_mfc(PimMfc *pim_mfc) { _pim_mfc_list.push_back(pim_mfc); } // // Add a PimMfc entry to the list of entries to delete // void PimMreTask::add_pim_mfc_delete(PimMfc *pim_mfc) { _pim_mfc_delete_list.push_back(pim_mfc); } // // Add a list of Mrib entries to the list of entries to delete // void PimMreTask::add_mrib_delete_list(const list& mrib_list) { _mrib_delete_list.insert(_mrib_delete_list.end(), mrib_list.begin(), mrib_list.end()); } xorp/pim/pim_proto_cand_rp_adv.cc0000664000076400007640000002666711540225531017323 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // PIM PIM_CAND_RP_ADV messages processing. // #include "pim_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvx.hh" #include "pim_bsr.hh" #include "pim_node.hh" #include "pim_vif.hh" // // Exported variables // // // Local constants definitions // // // Local structures/classes, typedefs and macros // // // Local variables // // // Local functions prototypes // /** * PimVif::pim_cand_rp_adv_recv: * @pim_nbr: The PIM neighbor message originator (or NULL if not a neighbor). * @src: The message source address. * @dst: The message destination address. * @buffer: The buffer with the message. * * Receive PIM_CAND_RP_ADV message. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int PimVif::pim_cand_rp_adv_recv(PimNbr *pim_nbr, const IPvX& src, const IPvX& dst, buffer_t *buffer) { uint8_t prefix_count, rp_priority; uint16_t rp_holdtime; IPvX rp_addr(family()); int rcvd_family; IPvX group_addr(family()); uint8_t group_mask_len; IPvXNet group_prefix(family()); uint8_t group_addr_reserved_flags; PimBsr& pim_bsr = pim_node()->pim_bsr(); bool is_scope_zone = false; BsrZone *active_bsr_zone = NULL; BsrRp *bsr_rp; string error_msg = ""; bool bool_add_rps_to_rp_table = false; UNUSED(src); UNUSED(dst); // // XXX: Don't accept Bootstrap-related messages if the BSR is not running // if (! pim_node()->pim_bsr().is_up()) return (XORP_ERROR); // // Parse the message // BUFFER_GET_OCTET(prefix_count, buffer); BUFFER_GET_OCTET(rp_priority, buffer); BUFFER_GET_HOST_16(rp_holdtime, buffer); GET_ENCODED_UNICAST_ADDR(rcvd_family, rp_addr, buffer); if (! rp_addr.is_unicast()) { XLOG_WARNING("RX %s from %s to %s: " "invalid RP address: %s ", PIMTYPE2ASCII(PIM_CAND_RP_ADV), cstring(src), cstring(dst), cstring(rp_addr)); return (XORP_ERROR); } if (prefix_count == 0) { // Prefix count of 0 implies all multicast groups. is_scope_zone = false; group_prefix = IPvXNet::ip_multicast_base_prefix(family()); // Try to find a non-scoped zone for this prefix active_bsr_zone = pim_bsr.find_active_bsr_zone_by_prefix(group_prefix, false); if (active_bsr_zone == NULL) { // XXX: don't know anything about this zone yet ++_pimstat_rx_candidate_rp_not_bsr; return (XORP_ERROR); } if (active_bsr_zone->bsr_zone_state() != BsrZone::STATE_ELECTED_BSR) { // Silently drop the message ++_pimstat_rx_candidate_rp_not_bsr; return (XORP_ERROR); } // // Check if the Cand-RP info has changed // bool is_rp_info_changed = false; do { BsrRp *active_bsr_rp = active_bsr_zone->find_rp(group_prefix, rp_addr); if (active_bsr_rp == NULL) { is_rp_info_changed = true; break; } if (rp_priority != active_bsr_rp->rp_priority()) { is_rp_info_changed = true; break; } if (rp_holdtime != active_bsr_rp->rp_holdtime()) { is_rp_info_changed = true; break; } } while (false); bsr_rp = active_bsr_zone->add_rp(group_prefix, is_scope_zone, rp_addr, rp_priority, rp_holdtime, error_msg); if (bsr_rp == NULL) { XLOG_WARNING("RX %s from %s to %s: " "Cannot add RP %s for group prefix %s " "to BSR zone %s: %s", PIMTYPE2ASCII(PIM_CAND_RP_ADV), cstring(src), cstring(dst), cstring(rp_addr), cstring(group_prefix), cstring(active_bsr_zone->zone_id()), error_msg.c_str()); return (XORP_ERROR); } bsr_rp->start_candidate_rp_expiry_timer(); // XXX: schedule to send immediately a Bootstrap message if needed // and add to the RP table. if (is_rp_info_changed) { active_bsr_zone->expire_bsr_timer(); pim_bsr.add_rps_to_rp_table(); } return (XORP_OK); } while (prefix_count--) { GET_ENCODED_GROUP_ADDR(rcvd_family, group_addr, group_mask_len, group_addr_reserved_flags, buffer); if (group_addr_reserved_flags & EGADDR_Z_BIT) is_scope_zone = true; else is_scope_zone = false; group_prefix = IPvXNet(group_addr, group_mask_len); if (! group_prefix.masked_addr().is_multicast()) { XLOG_WARNING("RX %s from %s to %s: " "invalid group address: %s ", PIMTYPE2ASCII(PIM_CAND_RP_ADV), cstring(src), cstring(dst), cstring(group_prefix)); continue; } // // Try to find a scoped zone for this group prefix. // Only if no scoped zone, then try non-scoped zone. active_bsr_zone = pim_bsr.find_active_bsr_zone_by_prefix(group_prefix, true); if (active_bsr_zone == NULL) { active_bsr_zone = pim_bsr.find_active_bsr_zone_by_prefix(group_prefix, false); } if (active_bsr_zone == NULL) continue; // XXX: don't know anything about this zone yet if (active_bsr_zone->bsr_zone_state() != BsrZone::STATE_ELECTED_BSR) { // Silently ignore the prefix continue; } // // Check if the Cand-RP info has changed // bool is_rp_info_changed = false; do { BsrRp *active_bsr_rp = active_bsr_zone->find_rp(group_prefix, rp_addr); if (active_bsr_rp == NULL) { is_rp_info_changed = true; break; } if (rp_priority != active_bsr_rp->rp_priority()) { is_rp_info_changed = true; break; } if (rp_holdtime != active_bsr_rp->rp_holdtime()) { is_rp_info_changed = true; break; } } while (false); bsr_rp = active_bsr_zone->add_rp(group_prefix, is_scope_zone, rp_addr, rp_priority, rp_holdtime, error_msg); if (bsr_rp == NULL) { XLOG_WARNING("RX %s from %s to %s: " "Cannot add RP %s for group prefix %s " "to BSR zone %s: %s", PIMTYPE2ASCII(PIM_CAND_RP_ADV), cstring(src), cstring(dst), cstring(rp_addr), cstring(group_prefix), cstring(active_bsr_zone->zone_id()), error_msg.c_str()); continue; } bsr_rp->start_candidate_rp_expiry_timer(); // XXX: schedule to send immediately a Bootstrap message if needed if (is_rp_info_changed) { active_bsr_zone->expire_bsr_timer(); bool_add_rps_to_rp_table = true; } } // If needed, add RPs to the RP table. if (bool_add_rps_to_rp_table) pim_bsr.add_rps_to_rp_table(); UNUSED(pim_nbr); return (XORP_OK); // Various error processing rcvlen_error: XLOG_WARNING("RX %s from %s to %s: " "invalid message length", PIMTYPE2ASCII(PIM_CAND_RP_ADV), cstring(src), cstring(dst)); ++_pimstat_rx_malformed_packet; return (XORP_ERROR); rcvd_mask_len_error: XLOG_WARNING("RX %s from %s to %s: " "invalid group mask length = %d", PIMTYPE2ASCII(PIM_CAND_RP_ADV), cstring(src), cstring(dst), group_mask_len); return (XORP_ERROR); rcvd_family_error: XLOG_WARNING("RX %s from %s to %s: " "invalid address family inside = %d", PIMTYPE2ASCII(PIM_CAND_RP_ADV), cstring(src), cstring(dst), rcvd_family); return (XORP_ERROR); } int PimVif::pim_cand_rp_adv_send(const IPvX& bsr_addr, const BsrZone& bsr_zone) { IPvX src_addr = domain_wide_addr(); string dummy_error_msg; // // XXX: Don't transmit Bootstrap-related messages if the BSR is not running // if (! pim_node()->pim_bsr().is_up()) return (XORP_ERROR); // TODO: add a check whether I am a Cand-RP for that zone. // XXX: for now there is no simple check for that, so add a flag to BsrZone if (! bsr_addr.is_unicast()) return (XORP_ERROR); // Invalid BSR address if (bsr_addr == bsr_zone.my_bsr_addr()) return (XORP_ERROR); // Don't send the message to my BSR address // // XXX: for some reason, the 'Priority' and 'Holdtime' fields // are per Cand-RP-Advertise message, instead of per group prefix. // Well, hmmm, the Cand-RP state actually is that RP priority // is per RP per group prefix, so if the priority for each // group prefix is different, we have to send more than one // Cand-RP-Advertise messages. // Here we could try to be smart and combine Cand-RP-Advertise messages // that have same priority and holdtime, but this makes things // more complicated (TODO: add that complexity!) // Hence, we send only one group prefix per Cand-RP-Advertise message. // list::const_iterator iter_prefix; for (iter_prefix = bsr_zone.bsr_group_prefix_list().begin(); iter_prefix != bsr_zone.bsr_group_prefix_list().end(); ++iter_prefix) { BsrGroupPrefix *bsr_group_prefix = *iter_prefix; const IPvXNet& group_prefix = bsr_group_prefix->group_prefix(); bool is_all_multicast_groups, is_zbr; if (group_prefix == IPvXNet::ip_multicast_base_prefix(family())) { is_all_multicast_groups = true; } else { is_all_multicast_groups = false; } // // Test if I am Zone Border Router (ZBR) for this prefix // if (pim_node()->pim_scope_zone_table().is_zone_border_router(group_prefix)) is_zbr = true; else is_zbr = false; list::const_iterator iter_rp; for (iter_rp = bsr_group_prefix->rp_list().begin(); iter_rp != bsr_group_prefix->rp_list().end(); ++iter_rp) { BsrRp *bsr_rp = *iter_rp; buffer_t *buffer; if (! pim_node()->is_my_addr(bsr_rp->rp_addr())) continue; // Ignore addresses that aren't mine buffer = buffer_send_prepare(); // Write all data to the buffer if (is_all_multicast_groups) BUFFER_PUT_OCTET(0, buffer); // XXX: default to all groups else BUFFER_PUT_OCTET(1, buffer); // XXX: send one group prefix BUFFER_PUT_OCTET(bsr_rp->rp_priority(), buffer); if (bsr_zone.is_cancel()) BUFFER_PUT_HOST_16(0, buffer); else BUFFER_PUT_HOST_16(bsr_rp->rp_holdtime(), buffer); PUT_ENCODED_UNICAST_ADDR(family(), bsr_rp->rp_addr(), buffer); if (! is_all_multicast_groups) { uint8_t group_addr_reserved_flags = 0; if (is_zbr) group_addr_reserved_flags |= EGADDR_Z_BIT; PUT_ENCODED_GROUP_ADDR(family(), group_prefix.masked_addr(), group_prefix.prefix_len(), group_addr_reserved_flags, buffer); } pim_send(src_addr, bsr_addr, PIM_CAND_RP_ADV, buffer, dummy_error_msg); } } return (XORP_OK); invalid_addr_family_error: XLOG_UNREACHABLE(); XLOG_ERROR("TX %s from %s to %s: " "invalid address family error = %d", PIMTYPE2ASCII(PIM_CAND_RP_ADV), cstring(src_addr), cstring(bsr_addr), family()); return (XORP_ERROR); buflen_error: XLOG_UNREACHABLE(); XLOG_ERROR("TX %s from %s to %s: " "packet cannot fit into sending buffer", PIMTYPE2ASCII(PIM_CAND_RP_ADV), cstring(src_addr), cstring(bsr_addr)); return (XORP_ERROR); } xorp/pim/xorp_pimsm6.cc0000664000076400007640000001355011634760770015256 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // XORP PIM-SM module implementation. // #include "pim_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/random.h" #include "libxorp/callback.hh" #include "libxorp/eventloop.hh" #include "libxorp/exceptions.hh" #include "xrl_pim_node.hh" #ifdef HAVE_GETOPT_H #include #endif // // Local functions prototypes // static void usage(const char *argv0, int exit_value); /** * usage: * @argv0: Argument 0 when the program was called (the program name itself). * @exit_value: The exit value of the program. * * Print the program usage. * If @exit_value is 0, the usage will be printed to the standart output, * otherwise to the standart error. **/ static void usage(const char *argv0, int exit_value) { FILE *output; const char *progname = strrchr(argv0, '/'); if (progname != NULL) progname++; // Skip the last '/' if (progname == NULL) progname = argv0; // // If the usage is printed because of error, output to stderr, otherwise // output to stdout. // if (exit_value == 0) output = stdout; else output = stderr; fprintf(output, "Usage: %s [-F [:]]\n", progname); fprintf(output, " -F [:] : finder hostname and port\n"); fprintf(output, " -h : usage (this message)\n"); fprintf(output, "\n"); fprintf(output, "Program name: %s\n", progname); fprintf(output, "Module name: %s\n", XORP_MODULE_NAME); fprintf(output, "Module version: %s\n", XORP_MODULE_VERSION); exit (exit_value); // NOTREACHED } static void pim_main(const string& finder_hostname, uint16_t finder_port) { #ifdef HAVE_IPV6 setup_dflt_sighandlers(); // // Init stuff // EventLoop eventloop; // // Initialize the random generator // { TimeVal now; TimerList::system_gettimeofday(&now); xorp_srandom(now.sec()); } // // PIMSM node // XrlPimNode xrl_pimsm_node6(AF_INET6, XORP_MODULE_PIMSM, eventloop, xorp_module_name(AF_INET6, XORP_MODULE_PIMSM), finder_hostname, finder_port, "finder", xorp_module_name(AF_INET6, XORP_MODULE_FEA), xorp_module_name(AF_INET6, XORP_MODULE_MFEA), xorp_module_name(AF_INET6, XORP_MODULE_RIB), xorp_module_name(AF_INET6, XORP_MODULE_MLD6IGMP)); wait_until_xrl_router_is_ready(eventloop, xrl_pimsm_node6.xrl_router()); // // Startup // #ifdef HAVE_IPV6_MULTICAST xrl_pimsm_node6.enable_pim(); // xrl_pimsm_node6.startup(); xrl_pimsm_node6.enable_cli(); xrl_pimsm_node6.start_cli(); #endif // // Main loop // #ifdef HAVE_IPV6_MULTICAST while (xorp_do_run && !xrl_pimsm_node6.is_done()) { eventloop.run(); } while (xrl_pimsm_node6.xrl_router().pending()) { eventloop.run(); } #endif // HAVE_IPV6_MULTICAST // Manually clean up xrl_pim_node. Without this logic, the destructor // will attempt the same, but that will cause some recursive cleanup // that ends up calling a pure virtual on the xlr_pim_node logic, // which throws an exception from deep in glibc and crashes us. xrl_pimsm_node6.delete_all_vifs(); xrl_pimsm_node6.shutdown(); xrl_pimsm_node6.destruct_me(); #endif // HAVE_IPV6 UNUSED(finder_hostname); UNUSED(finder_port); } int main(int argc, char *argv[]) { int ch; string::size_type idx; const char *argv0 = argv[0]; string finder_hostname = FinderConstants::FINDER_DEFAULT_HOST().str(); uint16_t finder_port = FinderConstants::FINDER_DEFAULT_PORT(); // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); // // Get the program options // while ((ch = getopt(argc, argv, "F:h")) != -1) { switch (ch) { case 'F': // Finder hostname and port finder_hostname = optarg; idx = finder_hostname.find(':'); if (idx != string::npos) { if (idx + 1 >= finder_hostname.length()) { // No port number usage(argv0, 1); // NOTREACHED } char* p = &finder_hostname[idx + 1]; finder_port = static_cast(atoi(p)); finder_hostname = finder_hostname.substr(0, idx); } break; case 'h': case '?': // Help usage(argv0, 0); // NOTREACHED break; default: usage(argv0, 1); // NOTREACHED break; } } argc -= optind; argv += optind; if (argc != 0) { usage(argv0, 1); // NOTREACHED } // // Run everything // try { pim_main(finder_hostname, finder_port); } catch(...) { xorp_catch_standard_exceptions(); } // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); exit (0); } xorp/pim/pim_mre_rpf.cc0000664000076400007640000010536011634760770015273 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // PIM Multicast Routing Entry RPF handling // #include "pim_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvx.hh" #include "pim_mre.hh" #include "pim_mrt.hh" #include "pim_nbr.hh" #include "pim_node.hh" #include "pim_proto_join_prune_message.hh" #include "pim_vif.hh" PimNbr * PimMre::nbr_mrib_next_hop_rp() const { if (is_rp() || is_wc()) return (_nbr_mrib_next_hop_rp); if (wc_entry() != NULL) return (wc_entry()->nbr_mrib_next_hop_rp()); if (rp_entry() != NULL) return (rp_entry()->nbr_mrib_next_hop_rp()); return (NULL); } // Note: applies only for (*,G) and (S,G,rpt), but works also for (S,G) // Note that we can compute RPF'(*,G) for (S,G) or (S,G,rpt) entry // even if there is no (*,G) entry; in that case we return // NBR(RPF_interface(RP(G)), MRIB.next_hop(RP(G))) // for the corresponding RP. PimNbr * PimMre::rpfp_nbr_wc() const { if (is_wc()) return (_rpfp_nbr_wc); if (wc_entry() != NULL) return (wc_entry()->rpfp_nbr_wc()); // Return NBR(RPF_interface(RP(G)), MRIB.next_hop(RP(G))) return (nbr_mrib_next_hop_rp()); } uint32_t PimMre::rpf_interface_rp() const { uint32_t vif_index = Vif::VIF_INDEX_INVALID; PimVif *pim_vif; do { if (i_am_rp()) { vif_index = pim_register_vif_index(); break; } if (mrib_rp() == NULL) return (Vif::VIF_INDEX_INVALID); vif_index = mrib_rp()->next_hop_vif_index(); } while (false); // // Check if the PimVif is valid and UP // pim_vif = pim_mrt()->vif_find_by_vif_index(vif_index); if ((pim_vif == NULL) || (! pim_vif->is_up())) return (Vif::VIF_INDEX_INVALID); return (vif_index); } uint32_t PimMre::rpf_interface_s() const { uint32_t vif_index; PimVif *pim_vif; if (mrib_s() == NULL) return (Vif::VIF_INDEX_INVALID); vif_index = mrib_s()->next_hop_vif_index(); // // Check if the PimVif is valid and UP // pim_vif = pim_mrt()->vif_find_by_vif_index(vif_index); if ((pim_vif == NULL) || (! pim_vif->is_up())) return (Vif::VIF_INDEX_INVALID); return (vif_index); } // Return true if @pim_nbr is in use by this PimMre entry, otherwise // return false. bool PimMre::is_pim_nbr_in_use(const PimNbr *pim_nbr) const { if (pim_nbr == NULL) return (false); if (_nbr_mrib_next_hop_rp == pim_nbr) return (true); if (_nbr_mrib_next_hop_s == pim_nbr) return (true); if (_rpfp_nbr_wc == pim_nbr) return (true); if (_rpfp_nbr_sg == pim_nbr) return (true); if (_rpfp_nbr_sg_rpt == pim_nbr) return (true); return (false); } // Return true if there is a missing PimNbr entry, otherwise return false. bool PimMre::is_pim_nbr_missing() const { if (is_rp()) { // (*,*,RP) entry if (_nbr_mrib_next_hop_rp == NULL) return (true); return (false); } if (is_wc()) { // (*,G) entry if ((_nbr_mrib_next_hop_rp == NULL) || (_rpfp_nbr_wc == NULL)) return (true); return (false); } if (is_sg()) { // (S,G) entry if ((_nbr_mrib_next_hop_s == NULL) || (_rpfp_nbr_sg == NULL)) return (true); return (false); } if (is_sg_rpt()) { // (S,G,rpt) entry if (_rpfp_nbr_sg_rpt == NULL) return (true); return (false); } XLOG_UNREACHABLE(); return (false); } // Note: applies only for (*,*,RP) and (*,G) void PimMre::set_nbr_mrib_next_hop_rp(PimNbr *v) { PimNbr *old_pim_nbr = _nbr_mrib_next_hop_rp; if (! (is_rp() || is_wc())) return; if (old_pim_nbr == v) return; // Nothing changed // Set the new value, and if necessary add to the list of PimMre entries // for this neighbor. bool is_new_nbr_in_use = is_pim_nbr_in_use(v); _nbr_mrib_next_hop_rp = v; if ((v != NULL) && (! is_new_nbr_in_use)) { v->add_pim_mre(this); } if (v == NULL) { pim_node()->add_pim_mre_no_pim_nbr(this); } // Remove from the list of PimMre entries for the old neighbor if ((old_pim_nbr != NULL) && (! is_pim_nbr_in_use(old_pim_nbr))) { old_pim_nbr->delete_pim_mre(this); } if ((old_pim_nbr == NULL) && (! is_pim_nbr_missing())) { pim_node()->delete_pim_mre_no_pim_nbr(this); } } // Note: applies only for (S,G) void PimMre::set_nbr_mrib_next_hop_s(PimNbr *v) { PimNbr *old_pim_nbr = _nbr_mrib_next_hop_s; if (! is_sg()) return; if (old_pim_nbr == v) return; // Nothing changed // Set the new value, and if necessary add to the list of PimMre entries // for this neighbor. bool is_new_nbr_in_use = is_pim_nbr_in_use(v); _nbr_mrib_next_hop_s = v; if ((v != NULL) && (! is_new_nbr_in_use)) { v->add_pim_mre(this); } if (v == NULL) { pim_node()->add_pim_mre_no_pim_nbr(this); } // Remove from the list of PimMre entries for the old neighbor if ((old_pim_nbr != NULL) && (! is_pim_nbr_in_use(old_pim_nbr))) { old_pim_nbr->delete_pim_mre(this); } if ((old_pim_nbr == NULL) && (! is_pim_nbr_missing())) { pim_node()->delete_pim_mre_no_pim_nbr(this); } } // Note: applies only for (*,G) void PimMre::set_rpfp_nbr_wc(PimNbr *v) { PimNbr *old_pim_nbr = _rpfp_nbr_wc; if (! is_wc()) return; if (old_pim_nbr == v) return; // Nothing changed // Set the new value, and if necessary add to the list of PimMre entries // for this neighbor. bool is_new_nbr_in_use = is_pim_nbr_in_use(v); _rpfp_nbr_wc = v; if ((v != NULL) && (! is_new_nbr_in_use)) { v->add_pim_mre(this); } if (v == NULL) { pim_node()->add_pim_mre_no_pim_nbr(this); } // Remove from the list of PimMre entries for the old neighbor if ((old_pim_nbr != NULL) && (! is_pim_nbr_in_use(old_pim_nbr))) { old_pim_nbr->delete_pim_mre(this); } if ((old_pim_nbr == NULL) && (! is_pim_nbr_missing())) { pim_node()->delete_pim_mre_no_pim_nbr(this); } } // Note: applies only for (S,G) void PimMre::set_rpfp_nbr_sg(PimNbr *v) { PimNbr *old_pim_nbr = _rpfp_nbr_sg; if (! is_sg()) return; if (old_pim_nbr == v) return; // Nothing changed // Set the new value, and if necessary add to the list of PimMre entries // for this neighbor. bool is_new_nbr_in_use = is_pim_nbr_in_use(v); _rpfp_nbr_sg = v; if ((v != NULL) && (! is_new_nbr_in_use)) { v->add_pim_mre(this); } if (v == NULL) { pim_node()->add_pim_mre_no_pim_nbr(this); } // Remove from the list of PimMre entries for the old neighbor if ((old_pim_nbr != NULL) && (! is_pim_nbr_in_use(old_pim_nbr))) { old_pim_nbr->delete_pim_mre(this); } if ((old_pim_nbr == NULL) && (! is_pim_nbr_missing())) { pim_node()->delete_pim_mre_no_pim_nbr(this); } } // Note: applies only for (S,G,rpt) void PimMre::set_rpfp_nbr_sg_rpt(PimNbr *v) { PimNbr *old_pim_nbr = _rpfp_nbr_sg_rpt; if (! is_sg_rpt()) return; if (old_pim_nbr == v) return; // Nothing changed // Set the new value, and if necessary add to the list of PimMre entries // for this neighbor. bool is_new_nbr_in_use = is_pim_nbr_in_use(v); _rpfp_nbr_sg_rpt = v; if ((v != NULL) && (! is_new_nbr_in_use)) { v->add_pim_mre(this); } if (v == NULL) { pim_node()->add_pim_mre_no_pim_nbr(this); } // Remove from the list of PimMre entries for the old neighbor if ((old_pim_nbr != NULL) && (! is_pim_nbr_in_use(old_pim_nbr))) { old_pim_nbr->delete_pim_mre(this); } if ((old_pim_nbr == NULL) && (! is_pim_nbr_missing())) { pim_node()->delete_pim_mre_no_pim_nbr(this); } } // // Used by (*,G), (S,G), (S,G,rpt) entries // void PimMre::set_pim_rp(PimRp *v) { if (! (is_wc() || is_sg() || is_sg_rpt())) return; if (_pim_rp == v) return; // Nothing changed uncond_set_pim_rp(v); } // // Used by (*,G), (S,G), (S,G,rpt) entries // void PimMre::uncond_set_pim_rp(PimRp *v) { if (! (is_wc() || is_sg() || is_sg_rpt())) return; pim_node()->rp_table().delete_pim_mre(this); _pim_rp = v; if (_pim_rp == NULL) { set_rp_entry(NULL); // No (*,*,RP) entry } else { // Set/reset the state indicating whether I am the RP for the group if (_pim_rp->i_am_rp()) set_i_am_rp(true); else set_i_am_rp(false); // Set the (*,*,RP) entry if (is_wc() || is_sg() || is_sg_rpt()) { set_rp_entry(pim_mrt()->pim_mre_find(source_addr(), group_addr(), PIM_MRE_RP, 0)); } } pim_node()->rp_table().add_pim_mre(this); // // Perform the appropriate actions when "RP changed" at the (S,G) // register state machine. // if (is_sg()) rp_register_sg_changed(); } // Return the PimRp entry for the multicast group. // // Used by (*,G), (S,G), (S,G,rpt) entries // PimRp * PimMre::compute_rp() const { if (! (is_wc() || is_sg() || is_sg_rpt())) return (NULL); return (pim_node()->rp_table().rp_find(group_addr())); } // Used by (*,G) void PimMre::recompute_rp_wc() { PimRp *old_pim_rp = pim_rp(); PimRp *new_pim_rp; if (! is_wc()) return; new_pim_rp = compute_rp(); if (old_pim_rp == new_pim_rp) return; // Nothing changed set_pim_rp(new_pim_rp); } // Used by (S,G) void PimMre::recompute_rp_sg() { PimRp *old_pim_rp = pim_rp(); PimRp *new_pim_rp; if (! is_sg()) return; new_pim_rp = compute_rp(); if (old_pim_rp == new_pim_rp) return; // Nothing changed set_pim_rp(new_pim_rp); } // Used by (S,G,rpt) void PimMre::recompute_rp_sg_rpt() { PimRp *old_pim_rp = pim_rp(); PimRp *new_pim_rp; if (! is_sg_rpt()) return; new_pim_rp = compute_rp(); if (old_pim_rp == new_pim_rp) return; // Nothing changed set_pim_rp(new_pim_rp); } // Used by (*,*,RP) void PimMre::recompute_mrib_rp_rp() { Mrib *old_mrib_rp = mrib_rp(); Mrib *new_mrib_rp; if (! is_rp()) return; new_mrib_rp = compute_mrib_rp(); if (old_mrib_rp == new_mrib_rp) return; // Nothing changed set_mrib_rp(new_mrib_rp); } // Used by (*,G) void PimMre::recompute_mrib_rp_wc() { Mrib *old_mrib_rp = mrib_rp(); Mrib *new_mrib_rp; uint32_t old_rpf_interface_rp, new_rpf_interface_rp; if (! is_wc()) return; new_mrib_rp = compute_mrib_rp(); if (old_mrib_rp == new_mrib_rp) return; // Nothing changed // Compute the old and new RPF_interface(RP(G)) if (old_mrib_rp != NULL) old_rpf_interface_rp = old_mrib_rp->next_hop_vif_index(); else old_rpf_interface_rp = Vif::VIF_INDEX_INVALID; if (new_mrib_rp != NULL) new_rpf_interface_rp = new_mrib_rp->next_hop_vif_index(); else new_rpf_interface_rp = Vif::VIF_INDEX_INVALID; set_mrib_rp(new_mrib_rp); if (old_rpf_interface_rp != new_rpf_interface_rp) { pim_mrt()->add_task_assert_rpf_interface_wc(old_rpf_interface_rp, group_addr()); } } // Used by (S,G) void PimMre::recompute_mrib_rp_sg() { Mrib *old_mrib_rp = mrib_rp(); Mrib *new_mrib_rp; if (! is_sg()) return; new_mrib_rp = compute_mrib_rp(); if (old_mrib_rp == new_mrib_rp) return; // Nothing changed set_mrib_rp(new_mrib_rp); } // Used by (S,G,rpt) void PimMre::recompute_mrib_rp_sg_rpt() { Mrib *old_mrib_rp = mrib_rp(); Mrib *new_mrib_rp; if (! is_sg_rpt()) return; new_mrib_rp = compute_mrib_rp(); if (old_mrib_rp == new_mrib_rp) return; // Nothing changed set_mrib_rp(new_mrib_rp); } // Used by (S,G) void PimMre::recompute_mrib_s_sg() { Mrib *old_mrib_s = mrib_s(); Mrib *new_mrib_s; uint32_t old_rpf_interface_s, new_rpf_interface_s; if (! is_sg()) return; new_mrib_s = compute_mrib_s(); if (old_mrib_s == new_mrib_s) return; // Nothing changed // Compute the old and new RPF_interface(S) if (old_mrib_s != NULL) old_rpf_interface_s = old_mrib_s->next_hop_vif_index(); else old_rpf_interface_s = Vif::VIF_INDEX_INVALID; if (new_mrib_s != NULL) new_rpf_interface_s = new_mrib_s->next_hop_vif_index(); else new_rpf_interface_s = Vif::VIF_INDEX_INVALID; set_mrib_s(new_mrib_s); if (old_rpf_interface_s != new_rpf_interface_s) { pim_mrt()->add_task_assert_rpf_interface_sg(old_rpf_interface_s, source_addr(), group_addr()); } } // Used by (S,G,rpt) void PimMre::recompute_mrib_s_sg_rpt() { Mrib *old_mrib_s = mrib_s(); Mrib *new_mrib_s; if (! is_sg_rpt()) return; new_mrib_s = compute_mrib_s(); if (old_mrib_s == new_mrib_s) return; // Nothing changed set_mrib_s(new_mrib_s); } // Used by all entries Mrib * PimMre::compute_mrib_rp() const { if (pim_rp() != NULL) { return (pim_mrt()->pim_mrib_table().find(pim_rp()->rp_addr())); } if (is_rp()) { return (pim_mrt()->pim_mrib_table().find(*rp_addr_ptr())); } return (NULL); } // Used by (S,G), (S,G,rpt) Mrib * PimMre::compute_mrib_s() const { if (! (is_sg() || is_sg_rpt())) return (NULL); return (pim_mrt()->pim_mrib_table().find(source_addr())); } // // Return the MRIB-based RPF neighbor toward the RP // // Used by (*,*,RP), (*,G), (S,G,rpt) entry. // XXX: used by (S,G,rpt) only to bypass computing of RPF'(*,G) when // there is no (*,G) entry (note the out-of-band indirection in // the computation). // However, it works also for (S,G). // XXX: the return info does NOT take into account the Asserts PimNbr * PimMre::compute_nbr_mrib_next_hop_rp() const { if (rpf_interface_rp() == Vif::VIF_INDEX_INVALID) return (NULL); if (rp_addr_ptr() == NULL) return (NULL); return (pim_node()->pim_nbr_rpf_find(*rp_addr_ptr(), mrib_rp())); } // // Return the MRIB-based RPF neighbor toward the S // // Note: applies only for (S,G) // XXX: the return info does NOT take into account the Asserts // XXX: if the source is directly connected, return NULL. PimNbr * PimMre::compute_nbr_mrib_next_hop_s() const { if (! is_sg()) return (NULL); if (rpf_interface_s() == Vif::VIF_INDEX_INVALID) return (NULL); if (mrib_s() == NULL) return (NULL); // // Find the vif toward the destination address // PimVif *pim_vif = pim_node()->vif_find_by_vif_index(mrib_s()->next_hop_vif_index()); // // If the source is directly connected, return NULL. // if (pim_vif != NULL) { if (pim_node()->is_directly_connected(*pim_vif, source_addr())) return (NULL); } return (pim_node()->pim_nbr_rpf_find(source_addr(), mrib_s())); } // // Return the RPF' neighbor toward the RP // // Note: appies only for (*,G) // XXX: the return info takes into account the Asserts PimNbr * PimMre::compute_rpfp_nbr_wc() const { uint32_t next_hop_vif_index; PimVif *pim_vif; if (! is_wc()) return (NULL); if (mrib_rp() == NULL) return (NULL); next_hop_vif_index = rpf_interface_rp(); if (next_hop_vif_index == Vif::VIF_INDEX_INVALID) return (NULL); pim_vif = pim_mrt()->vif_find_by_vif_index(next_hop_vif_index); if (pim_vif == NULL) return (NULL); if (is_i_am_assert_loser_state(next_hop_vif_index)) { // Return the upstream Assert winner AssertMetric *winner_metric; winner_metric = assert_winner_metric_wc(next_hop_vif_index); XLOG_ASSERT(winner_metric != NULL); return (pim_vif->pim_nbr_find(winner_metric->addr())); } return (compute_nbr_mrib_next_hop_rp()); } // // Return the RPF' neighbor toward the S // // Note: applies only for (S,G) // XXX: the return info takes into account the Asserts // XXX: if the source is directly connected, return NULL. PimNbr * PimMre::compute_rpfp_nbr_sg() const { uint32_t next_hop_vif_index; PimVif *pim_vif; if (! is_sg()) return (NULL); if (mrib_s() == NULL) return (NULL); next_hop_vif_index = rpf_interface_s(); if (next_hop_vif_index == Vif::VIF_INDEX_INVALID) return (NULL); pim_vif = pim_mrt()->vif_find_by_vif_index(next_hop_vif_index); if (pim_vif == NULL) return (NULL); // // If the source is directly connected, return NULL. // if (pim_node()->is_directly_connected(*pim_vif, source_addr())) return (NULL); if (is_i_am_assert_loser_state(next_hop_vif_index)) { // Return the upstream Assert winner AssertMetric *winner_metric; winner_metric = assert_winner_metric_sg(next_hop_vif_index); XLOG_ASSERT(winner_metric != NULL); return (pim_vif->pim_nbr_find(winner_metric->addr())); } return (compute_nbr_mrib_next_hop_s()); } // // Return the RPF' neighbor toward toward the RP // // Note: applies only for (S,G,rpt) // XXX: the return info takes into account the Asserts PimNbr * PimMre::compute_rpfp_nbr_sg_rpt() const { uint32_t next_hop_vif_index; PimVif *pim_vif; PimMre *pim_mre_sg, *pim_mre_wc; if (! is_sg_rpt()) return (NULL); if (mrib_rp() == NULL) return (NULL); next_hop_vif_index = rpf_interface_rp(); if (next_hop_vif_index == Vif::VIF_INDEX_INVALID) return (NULL); pim_vif = pim_mrt()->vif_find_by_vif_index(next_hop_vif_index); if (pim_vif == NULL) return (NULL); pim_mre_sg = sg_entry(); if ((pim_mre_sg != NULL) && pim_mre_sg->is_i_am_assert_loser_state(next_hop_vif_index)) { // Return the upstream Assert winner AssertMetric *winner_metric; winner_metric = pim_mre_sg->assert_winner_metric_sg(next_hop_vif_index); XLOG_ASSERT(winner_metric != NULL); return (pim_vif->pim_nbr_find(winner_metric->addr())); } // return RPF'(*,G) pim_mre_wc = wc_entry(); if (pim_mre_wc != NULL) return (pim_mre_wc->compute_rpfp_nbr_wc()); // // Return NBR(RPF_interface(RP(G)), MRIB.next_hop(RP(G))) // // XXX: Note the indirection in the computation of RPF'(S,G,rpt) which // uses internal knowledge about how RPF'(*,G) is computed. This // indirection is needed to compute RPF'(S,G,rpt) even if there is no // (*,G) routing state. For example, it is possible for a triggered // Prune(S,G,rpt) message to be sent when the router has no (*,G) Join // state. See Section "Background: (*,*,RP) and (S,G,rpt) Interaction" // for details. // return (compute_nbr_mrib_next_hop_rp()); } // // The NBR(RPF_interface(RP), MRIB.next_hop(RP)) has changed. // Take the appropriate action. // Note: applies only for (*,*,RP) entries // void PimMre::recompute_nbr_mrib_next_hop_rp_rp_changed() { PimNbr *old_pim_nbr, *new_pim_nbr; uint16_t join_prune_period = PIM_JOIN_PRUNE_PERIOD_DEFAULT; if (! is_rp()) return; new_pim_nbr = compute_nbr_mrib_next_hop_rp(); if (is_joined_state()) goto joined_state_label; // All other states: just set the new upstream neighbor set_nbr_mrib_next_hop_rp(new_pim_nbr); return; joined_state_label: // Joined state old_pim_nbr = nbr_mrib_next_hop_rp(); if (new_pim_nbr == old_pim_nbr) return; // Nothing changed // // Send Join(*,*,RP) to the new value of // NBR(RPF_interface(RP), MRIB.next_hop(RP)) // if (new_pim_nbr != NULL) { bool is_new_group = false; // Group together all (*,*,RP) entries new_pim_nbr->jp_entry_add(*rp_addr_ptr(), IPvX::MULTICAST_BASE(family()), IPvX::ip_multicast_base_address_mask_len(family()), MRT_ENTRY_RP, ACTION_JOIN, new_pim_nbr->pim_vif()->join_prune_holdtime().get(), is_new_group); join_prune_period = new_pim_nbr->pim_vif()->join_prune_period().get(); } // // Send Prune(*,*,RP) to the old value of // NBR(RPF_interface(RP), MRIB.next_hop(RP)) // if (old_pim_nbr != NULL) { bool is_new_group = false; // Group together all (*,*,RP) entries old_pim_nbr->jp_entry_add(*rp_addr_ptr(), IPvX::MULTICAST_BASE(family()), IPvX::ip_multicast_base_address_mask_len(family()), MRT_ENTRY_RP, ACTION_PRUNE, old_pim_nbr->pim_vif()->join_prune_holdtime().get(), is_new_group); } // Set the new upstream neighbor. set_nbr_mrib_next_hop_rp(new_pim_nbr); // Set Join Timer to t_periodic join_timer() = pim_node()->eventloop().new_oneoff_after( TimeVal(join_prune_period, 0), callback(this, &PimMre::join_timer_timeout)); } // // The GenID of NBR(RPF_interface(RP), MRIB.next_hop(RP)) has changed. // Take the appropriate action // Note: applies only for (*,*,RP) entries // void PimMre::recompute_nbr_mrib_next_hop_rp_gen_id_changed() { PimVif *pim_vif; PimNbr *pim_nbr; if (! is_rp()) return; if (is_joined_state()) goto joined_state_label; // All other states. return; joined_state_label: // Joined state pim_nbr = nbr_mrib_next_hop_rp(); if (pim_nbr == NULL) return; // Restart Join Timer if it is larger than t_override TimeVal t_override, tv_left; pim_vif = pim_nbr->pim_vif(); if (pim_vif == NULL) return; t_override = pim_vif->upstream_join_timer_t_override(); join_timer().time_remaining(tv_left); if (tv_left > t_override) { // Restart the timer with `t_override' join_timer() = pim_node()->eventloop().new_oneoff_after( t_override, callback(this, &PimMre::join_timer_timeout)); } } // // The NBR(RPF_interface(RP(G)), MRIB.next_hop(RP(G))) has changed. // Take the appropriate action. // Note: applies only for (*,G) entries // void PimMre::recompute_nbr_mrib_next_hop_rp_wc_changed() { PimNbr *old_pim_nbr, *new_pim_nbr; if (! is_wc()) return; old_pim_nbr = nbr_mrib_next_hop_rp(); new_pim_nbr = compute_nbr_mrib_next_hop_rp(); if (old_pim_nbr == new_pim_nbr) return; // Nothing changed set_nbr_mrib_next_hop_rp(new_pim_nbr); } // // The NBR(RPF_interface(S), MRIB.next_hop(S)) has changed. // Take the appropriate action. // Note: applies only for (S,G) entries // void PimMre::recompute_nbr_mrib_next_hop_s_changed() { PimNbr *old_pim_nbr, *new_pim_nbr; if (! is_sg()) return; old_pim_nbr = nbr_mrib_next_hop_s(); new_pim_nbr = compute_nbr_mrib_next_hop_s(); if (old_pim_nbr == new_pim_nbr) return; // Nothing changed set_nbr_mrib_next_hop_s(new_pim_nbr); } // // The current next hop towards the RP has changed due to an Assert. // Take the appropriate action. // Note: applies only for (*,G) entries // void PimMre::recompute_rpfp_nbr_wc_assert_changed() { PimNbr *old_pim_nbr, *new_pim_nbr; PimVif *pim_vif; if (! is_wc()) return; new_pim_nbr = compute_rpfp_nbr_wc(); if (is_joined_state()) goto joined_state_label; // All other states: just set the new upstream neighbor set_rpfp_nbr_wc(new_pim_nbr); return; joined_state_label: // Joined state old_pim_nbr = rpfp_nbr_wc(); if (new_pim_nbr == old_pim_nbr) return; // Nothing changed // Set the new upstream set_rpfp_nbr_wc(new_pim_nbr); if (new_pim_nbr == NULL) return; // Restart Join Timer if it is larger than t_override TimeVal t_override, tv_left; pim_vif = new_pim_nbr->pim_vif(); if (pim_vif == NULL) return; t_override = pim_vif->upstream_join_timer_t_override(); join_timer().time_remaining(tv_left); if (tv_left > t_override) { // Restart the timer with `t_override' join_timer() = pim_node()->eventloop().new_oneoff_after( t_override, callback(this, &PimMre::join_timer_timeout)); } } // // The current next hop towards the RP has changed not due to an Assert. // Take the appropriate action. // Note: applies only for (*,G) entries // void PimMre::recompute_rpfp_nbr_wc_not_assert_changed() { PimNbr *old_pim_nbr, *new_pim_nbr; uint16_t join_prune_period = PIM_JOIN_PRUNE_PERIOD_DEFAULT; const IPvX *my_rp_addr_ptr = NULL; if (! is_wc()) return; new_pim_nbr = compute_rpfp_nbr_wc(); if (is_joined_state()) goto joined_state_label; // All other states: just set the new upstream neighbor set_rpfp_nbr_wc(new_pim_nbr); return; joined_state_label: // Joined state old_pim_nbr = rpfp_nbr_wc(); if (new_pim_nbr == old_pim_nbr) return; // Nothing changed // // Note that this transition does not occur if an Assert is active // and the upstream interface does not change. // do { if ((old_pim_nbr == NULL) || (new_pim_nbr == NULL)) break; if (old_pim_nbr->vif_index() != new_pim_nbr->vif_index()) break; if (is_i_am_assert_loser_state(new_pim_nbr->vif_index())) return; break; } while (false); // Send Join(*,G) to the new value of RPF'(*,G) if (new_pim_nbr != NULL) { bool is_new_group = false; // Group together all (*,G) entries my_rp_addr_ptr = rp_addr_ptr(); if (my_rp_addr_ptr == NULL) { XLOG_WARNING("Sending Join(*,G) to new upstream neighbor: " "RP for group %s: not found", cstring(group_addr())); } else { new_pim_nbr->jp_entry_add(*my_rp_addr_ptr, group_addr(), IPvX::addr_bitlen(family()), MRT_ENTRY_WC, ACTION_JOIN, new_pim_nbr->pim_vif()->join_prune_holdtime().get(), is_new_group); } join_prune_period = new_pim_nbr->pim_vif()->join_prune_period().get(); } // Send Prune(*,G) to the old value of RPF'(*,G) if (old_pim_nbr != NULL) { bool is_new_group = false; // Group together all (*,G) entries my_rp_addr_ptr = rp_addr_ptr(); if (my_rp_addr_ptr == NULL) { XLOG_WARNING("Sending Prune(*,G) to old upstream neighbor: " "RP for group %s: not found", cstring(group_addr())); } else { old_pim_nbr->jp_entry_add(*my_rp_addr_ptr, group_addr(), IPvX::addr_bitlen(family()), MRT_ENTRY_WC, ACTION_PRUNE, old_pim_nbr->pim_vif()->join_prune_holdtime().get(), is_new_group); } } // Set the new RPF'(*,G) set_rpfp_nbr_wc(new_pim_nbr); // Set Join Timer to t_periodic join_timer() = pim_node()->eventloop().new_oneoff_after( TimeVal(join_prune_period, 0), callback(this, &PimMre::join_timer_timeout)); } // // The GenID of RPF'(*,G) has changed. // Take the appropriate action // Note: applies only for (*,G) entries // void PimMre::recompute_rpfp_nbr_wc_gen_id_changed() { PimVif *pim_vif; PimNbr *pim_nbr; if (! is_wc()) return; if (is_joined_state()) goto joined_state_label; // All other states. return; joined_state_label: // Joined state pim_nbr = rpfp_nbr_wc(); if (pim_nbr == NULL) return; // Restart Join Timer if it is larger than t_override TimeVal t_override, tv_left; pim_vif = pim_nbr->pim_vif(); if (pim_vif == NULL) return; t_override = pim_vif->upstream_join_timer_t_override(); join_timer().time_remaining(tv_left); if (tv_left > t_override) { // Restart the timer with `t_override' join_timer() = pim_node()->eventloop().new_oneoff_after( t_override, callback(this, &PimMre::join_timer_timeout)); } } // // The current next hop towards the S has changed due to an Assert. // // Take the appropriate action. // Note: applies only for (S,G) entries // void PimMre::recompute_rpfp_nbr_sg_assert_changed() { PimNbr *old_pim_nbr, *new_pim_nbr; PimVif *pim_vif; if (! is_sg()) return; new_pim_nbr = compute_rpfp_nbr_sg(); if (is_joined_state()) goto joined_state_label; // All other states: just set the new upstream neighbor set_rpfp_nbr_sg(new_pim_nbr); return; joined_state_label: // Joined state old_pim_nbr = rpfp_nbr_sg(); if (new_pim_nbr == old_pim_nbr) return; // Nothing changed // Set the new upstream set_rpfp_nbr_sg(new_pim_nbr); if (new_pim_nbr == NULL) return; // Restart Join Timer if it is larger than t_override TimeVal t_override, tv_left; pim_vif = new_pim_nbr->pim_vif(); if (pim_vif == NULL) return; t_override = pim_vif->upstream_join_timer_t_override(); join_timer().time_remaining(tv_left); if (tv_left > t_override) { // Restart the timer with `t_override' join_timer() = pim_node()->eventloop().new_oneoff_after( t_override, callback(this, &PimMre::join_timer_timeout)); } } // // The current next hop towards the S has changed not due to an Assert. // Take the appropriate action. // Note: applies only for (S,G) entries // void PimMre::recompute_rpfp_nbr_sg_not_assert_changed() { PimNbr *old_pim_nbr, *new_pim_nbr; uint16_t join_prune_period = PIM_JOIN_PRUNE_PERIOD_DEFAULT; if (! is_sg()) return; new_pim_nbr = compute_rpfp_nbr_sg(); if (is_joined_state()) goto joined_state_label; // All other states: just set the new upstream neighbor set_rpfp_nbr_sg(new_pim_nbr); return; joined_state_label: // Joined state old_pim_nbr = rpfp_nbr_sg(); if (new_pim_nbr == old_pim_nbr) return; // Nothing changed // // Note that this transition does not occur if an Assert is active // and the upstream interface does not change. // do { if ((old_pim_nbr == NULL) || (new_pim_nbr == NULL)) break; if (old_pim_nbr->vif_index() != new_pim_nbr->vif_index()) break; if (is_i_am_assert_loser_state(new_pim_nbr->vif_index())) return; break; } while (false); // Send Join(S,G) to the new value of RPF'(S,G) if (new_pim_nbr != NULL) { bool is_new_group = false; // Group together all (S,G) entries new_pim_nbr->jp_entry_add(source_addr(), group_addr(), IPvX::addr_bitlen(family()), MRT_ENTRY_SG, ACTION_JOIN, new_pim_nbr->pim_vif()->join_prune_holdtime().get(), is_new_group); join_prune_period = new_pim_nbr->pim_vif()->join_prune_period().get(); } // Send Prune(S,G) to the old value of RPF'(S,G) if (old_pim_nbr != NULL) { bool is_new_group = false; // Group together all (S,G) entries old_pim_nbr->jp_entry_add(source_addr(), group_addr(), IPvX::addr_bitlen(family()), MRT_ENTRY_SG, ACTION_PRUNE, old_pim_nbr->pim_vif()->join_prune_holdtime().get(), is_new_group); } // Set the new RPF'(S,G) set_rpfp_nbr_sg(new_pim_nbr); // Set Join Timer to t_periodic join_timer() = pim_node()->eventloop().new_oneoff_after( TimeVal(join_prune_period, 0), callback(this, &PimMre::join_timer_timeout)); } // // The GenID of RPF'(S,G) has changed. // Take the appropriate action. // Note: applies only for (S,G) entries // void PimMre::recompute_rpfp_nbr_sg_gen_id_changed() { PimVif *pim_vif; PimNbr *pim_nbr; if (! is_sg()) return; if (is_joined_state()) goto joined_state_label; // All other states. return; joined_state_label: // Joined state pim_nbr = rpfp_nbr_sg(); if (pim_nbr == NULL) return; // Restart Join Timer if it is larger than t_override TimeVal t_override, tv_left; pim_vif = pim_nbr->pim_vif(); if (pim_vif == NULL) return; t_override = pim_vif->upstream_join_timer_t_override(); join_timer().time_remaining(tv_left); if (tv_left > t_override) { // Restart the timer with `t_override' join_timer() = pim_node()->eventloop().new_oneoff_after( t_override, callback(this, &PimMre::join_timer_timeout)); } } // // RPF'(S,G,rpt) has changes. // Take the appropriate action. // XXX: action needed only if RPF'(S,G,rpt) has become equal to RPF'(*,G) // void PimMre::recompute_rpfp_nbr_sg_rpt_changed() { PimNbr *old_pim_nbr, *new_pim_nbr; PimVif *pim_vif; if (! is_sg_rpt()) return; new_pim_nbr = compute_rpfp_nbr_sg_rpt(); if (is_not_pruned_state()) goto not_pruned_state_label; // All other states: just set the new upstream neighbor set_rpfp_nbr_sg_rpt(new_pim_nbr); return; not_pruned_state_label: // NotPruned state old_pim_nbr = rpfp_nbr_sg_rpt(); if (new_pim_nbr == old_pim_nbr) return; // Nothing changed // Set the new upstream set_rpfp_nbr_sg_rpt(new_pim_nbr); if (new_pim_nbr != rpfp_nbr_wc()) return; // RPF'(S,G,rpt) != RPF'(*,G) : no action if (new_pim_nbr == NULL) return; // RPF'(S,G,rpt) === RPF'(*,G) // Restart Override Timer if it is larger than t_override TimeVal t_override, tv_left; pim_vif = new_pim_nbr->pim_vif(); if (pim_vif == NULL) return; t_override = pim_vif->upstream_join_timer_t_override(); if (override_timer().scheduled()) override_timer().time_remaining(tv_left); else tv_left = TimeVal::MAXIMUM(); if (tv_left > t_override) { // Restart the timer with `t_override' override_timer() = pim_node()->eventloop().new_oneoff_after( t_override, callback(this, &PimMre::override_timer_timeout)); } } // // RPF'(S,G,rpt) has changes (recomputed via (S,G) state) // Take the appropriate action. // XXX: action needed only if RPF'(S,G,rpt) has become equal to RPF'(*,G) // Note: applies only for (S,G) // void PimMre::recompute_rpfp_nbr_sg_rpt_sg_changed() { PimMre *pim_mre_sg_rpt; if (! is_sg()) return; pim_mre_sg_rpt = sg_rpt_entry(); // // Try to recompute if RPF'(S,G,rpt) has changed indirectly through the // (S,G,rpt) routing entry (if exists). // if (pim_mre_sg_rpt != NULL) { pim_mre_sg_rpt->recompute_rpfp_nbr_sg_rpt_changed(); return; } // // The (S,G,rpt) routing entry doesn't exist, hence create it // and then use it to recompute if RPF'(S,G,rpt) has changed. // pim_mre_sg_rpt = pim_mrt()->pim_mre_find(source_addr(), group_addr(), PIM_MRE_SG_RPT, PIM_MRE_SG_RPT); if (pim_mre_sg_rpt == NULL) { XLOG_UNREACHABLE(); XLOG_ERROR("INTERNAL PimMrt ERROR: " "cannot create entry for (%s, %s) create_flags = %#x", cstring(source_addr()), cstring(group_addr()), PIM_MRE_SG_RPT); return; } pim_mre_sg_rpt->recompute_rpfp_nbr_sg_rpt_changed(); // // Try to remove the (S,G,rpt) entry that was just created (in case // it is not needed). // pim_mre_sg_rpt->entry_try_remove(); } // // Note: applies only for (S,G) and (S,G,rpt) // bool PimMre::compute_is_directly_connected_s() { bool v = false; PimVif *pim_vif = pim_mrt()->vif_find_by_vif_index(rpf_interface_s()); if (pim_vif != NULL) v = pim_node()->is_directly_connected(*pim_vif, source_addr()); return (v); } // // Note: applies only for (S,G) // void PimMre::recompute_is_directly_connected_sg() { bool v = compute_is_directly_connected_s(); set_directly_connected_s(v); } xorp/pim/pim_mfc.cc0000664000076400007640000004575111635757530014416 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // PIM Multicast Forwarding Cache handling // #include "pim_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvx.hh" #include "pim_mfc.hh" #include "pim_node.hh" #include "pim_vif.hh" PimMfc::PimMfc(PimMrt* pim_mrt, const IPvX& source, const IPvX& group) : Mre(source, group), _pim_mrt(pim_mrt), _rp_addr(IPvX::ZERO(family())), _iif_vif_index(Vif::VIF_INDEX_INVALID), _flags(0) { } PimMfc::~PimMfc() { // // XXX: don't trigger any communications with the kernel // if PimNode's destructor is invoked. // if (pim_node()->node_status() != PROC_NULL) delete_mfc_from_kernel(); // // Remove this entry from the RP table. // pim_node()->rp_table().delete_pim_mfc(this); // // Remove this entry from the PimMrt table. // pim_mrt()->remove_pim_mfc(this); // // Cancel the (S,G) Keepalive Timer // PimMre *pim_mre_sg = pim_mrt()->pim_mre_find(source_addr(), group_addr(), PIM_MRE_SG, 0); if ((pim_mre_sg != NULL) && pim_mre_sg->is_keepalive_timer_running()) { pim_mre_sg->cancel_keepalive_timer(); pim_mre_sg->entry_try_remove(); } } PimNode* PimMfc::pim_node() const { if (_pim_mrt) return _pim_mrt->pim_node(); return NULL; } int PimMfc::family() const { return (_pim_mrt->family()); } void PimMfc::set_rp_addr(const IPvX& v) { if (v == _rp_addr) return; // Nothing changed uncond_set_rp_addr(v); } void PimMfc::uncond_set_rp_addr(const IPvX& v) { pim_node()->rp_table().delete_pim_mfc(this); _rp_addr = v; pim_node()->rp_table().add_pim_mfc(this); } void PimMfc::recompute_rp_mfc() { IPvX new_rp_addr(IPvX::ZERO(family())); PimRp *new_pim_rp = pim_node()->rp_table().rp_find(group_addr()); if (new_pim_rp != NULL) new_rp_addr = new_pim_rp->rp_addr(); if (new_rp_addr == rp_addr()) return; // Nothing changed set_rp_addr(new_rp_addr); add_mfc_to_kernel(); // XXX: we just recompute the state, hence no need to add // a dataflow monitor. } void PimMfc::recompute_iif_olist_mfc() { uint32_t new_iif_vif_index = Vif::VIF_INDEX_INVALID; Mifset new_olist; uint32_t lookup_flags; PimMre *pim_mre, *pim_mre_sg, *pim_mre_sg_rpt; lookup_flags = PIM_MRE_RP | PIM_MRE_WC | PIM_MRE_SG | PIM_MRE_SG_RPT; pim_mre = pim_mrt()->pim_mre_find(source_addr(), group_addr(), lookup_flags, 0); if (pim_mre == NULL) { // // No matching multicast routing entry. Remove the PimMfc entry. // TODO: XXX: PAVPAVPAV: do we really want to remove the entry? // E.g., just reset the olist, and leave the entry itself to timeout? // set_has_forced_deletion(true); entry_try_remove(); return; } // Get the (S,G) and the (S,G,rpt) PimMre entries (if exist) pim_mre_sg = NULL; pim_mre_sg_rpt = NULL; do { if (pim_mre->is_sg()) { pim_mre_sg = pim_mre; pim_mre_sg_rpt = pim_mre->sg_rpt_entry(); break; } if (pim_mre->is_sg_rpt()) { pim_mre_sg = pim_mre->sg_entry(); pim_mre_sg_rpt = pim_mre; break; } break; } while (false); // // Compute the new iif and the olist // // XXX: The computation is loosely based on the // "On receipt of data from S to G on interface iif:" procedure. // // Note that the is_directly_connected_s() check is not in the // spec, but for all practical purpose we want it here. // if ((pim_mre_sg != NULL) && (pim_mre_sg->is_spt() || pim_mre_sg->is_directly_connected_s())) { new_iif_vif_index = pim_mre_sg->rpf_interface_s(); new_olist = pim_mre->inherited_olist_sg(); } else { new_iif_vif_index = pim_mre->rpf_interface_rp(); new_olist = pim_mre->inherited_olist_sg_rpt(); // // XXX: Test a special case when we are not forwarding multicast // traffic, but we need the forwarding entry to prevent unnecessary // "no cache" or "wrong iif" upcalls. // if (new_olist.none()) { uint32_t iif_vif_index_s = Vif::VIF_INDEX_INVALID; if (pim_mre_sg != NULL) { iif_vif_index_s = pim_mre_sg->rpf_interface_s(); } else if (pim_mre_sg_rpt != NULL) { iif_vif_index_s = pim_mre_sg_rpt->rpf_interface_s(); } if ((iif_vif_index_s != Vif::VIF_INDEX_INVALID) && (iif_vif_index_s == iif_vif_index())) { new_iif_vif_index = iif_vif_index_s; } } } if (new_iif_vif_index != Vif::VIF_INDEX_INVALID) new_olist.reset(new_iif_vif_index); // XXX: this check should be used even if the iif and oifs didn't change // TODO: track the reason and explain it. if (new_iif_vif_index == Vif::VIF_INDEX_INVALID) { // // TODO: XXX: PAVPAVPAV: completely remove the olist check and comments // || new_olist.none()) { // No incoming interface or outgoing interfaces. // Remove the PimMfc entry. // TODO: XXX: PAVPAVPAV: do we really want to remove the entry? // E.g., just reset the olist, and leave the entry itself to timeout? // set_has_forced_deletion(true); entry_try_remove(); return; } // // XXX: we just recompute the state, hence no need to add // a dataflow monitor. // update_mfc(new_iif_vif_index, new_olist, pim_mre_sg); } // // A method for recomputing and setting the SPTbit that // is triggered by the dependency-tracking machinery. // // Note that the spec says that the recomputation // by calling Update_SPTbit(S,G,iif) should happen when a packet // arrives. However, given that we do not forward the data packets // in user space, we need to emulate that by triggering the // recomputation whenever some of the input statements have changed. // Also, note that this recomputation is not needed if there is not // underlying PimMfc entry; the lack of such entry will anyway trigger // a system uncall once the first multicast packet is received for // forwarding. // // Note: the SPTbit setting applies only if there is an (S,G) PimMfc entry. // Return true if state has changed, otherwise return false. bool PimMfc::recompute_update_sptbit_mfc() { PimMre *pim_mre_sg = pim_mrt()->pim_mre_find(source_addr(), group_addr(), PIM_MRE_SG, 0); // XXX: no (S,G) entry, hence nothing to update if (pim_mre_sg == NULL) return false; if (pim_mre_sg->is_spt()) { // XXX: nothing to do, because the SPTbit is already set return false; } pim_mre_sg->update_sptbit_sg(iif_vif_index()); if (pim_mre_sg->is_spt()) return true; // XXX: the SPTbit has been set return false; } // // The SPT-switch threshold has changed. // XXX: we will unconditionally install/deinstall SPT-switch dataflow monitor, // because we don't keep state about the previous value of the threshold. // void PimMfc::recompute_spt_switch_threshold_changed_mfc() { install_spt_switch_dataflow_monitor_mfc(NULL); } // // Conditionally install/deinstall SPT-switch dataflow monitor // (i.e., only if the conditions whether we need dataflow monitor have changed) // void PimMfc::recompute_monitoring_switch_to_spt_desired_mfc() { PimMre *pim_mre; PimMre *pim_mre_sg; uint32_t lookup_flags; bool has_spt_switch = has_spt_switch_dataflow_monitor(); bool is_spt_switch_desired; lookup_flags = PIM_MRE_RP | PIM_MRE_WC | PIM_MRE_SG | PIM_MRE_SG_RPT; pim_mre = pim_mrt()->pim_mre_find(source_addr(), group_addr(), lookup_flags, 0); if (pim_mre == NULL) return; // Nothing to install // // Get the (S,G) entry (if exists) // pim_mre_sg = NULL; do { if (pim_mre->is_sg()) { pim_mre_sg = pim_mre; break; } if (pim_mre->is_sg_rpt()) { pim_mre_sg = pim_mre->sg_entry(); break; } break; } while (false); is_spt_switch_desired = pim_mre->is_monitoring_switch_to_spt_desired_sg(pim_mre_sg); if ((pim_mre_sg != NULL) && (pim_mre_sg->is_keepalive_timer_running())) is_spt_switch_desired = false; if (has_spt_switch == is_spt_switch_desired) return; // Nothing changed install_spt_switch_dataflow_monitor_mfc(pim_mre); } // // Unconditionally install/deinstall SPT-switch dataflow monitor // void PimMfc::install_spt_switch_dataflow_monitor_mfc(PimMre *pim_mre) { PimMre *pim_mre_sg; bool has_idle = has_idle_dataflow_monitor(); bool has_spt_switch = has_spt_switch_dataflow_monitor(); // If necessary, perform the PimMre table lookup if (pim_mre == NULL) { uint32_t lookup_flags; lookup_flags = PIM_MRE_RP | PIM_MRE_WC | PIM_MRE_SG | PIM_MRE_SG_RPT; pim_mre = pim_mrt()->pim_mre_find(source_addr(), group_addr(), lookup_flags, 0); if (pim_mre == NULL) return; // Nothing to install } // // Get the (S,G) entry (if exists) // pim_mre_sg = NULL; do { if (pim_mre->is_sg()) { pim_mre_sg = pim_mre; break; } if (pim_mre->is_sg_rpt()) { pim_mre_sg = pim_mre->sg_entry(); break; } break; } while (false); // // Delete the existing SPT-switch dataflow monitor (if such) // // XXX: because we don't keep state about the original value of // dataflow_monitor_sec, therefore we remove all dataflows for // this (S,G), and then if necessary we install back the // idle dataflow monitor. // if (has_spt_switch) { delete_all_dataflow_monitor(); if (has_idle) { // Restore the idle dataflow monitor uint32_t expected_dataflow_monitor_sec = PIM_KEEPALIVE_PERIOD_DEFAULT; if ((pim_mre_sg != NULL) && pim_mre_sg->is_kat_set_to_rp_keepalive_period()) { if (expected_dataflow_monitor_sec < PIM_RP_KEEPALIVE_PERIOD_DEFAULT) { expected_dataflow_monitor_sec = PIM_RP_KEEPALIVE_PERIOD_DEFAULT; } } add_dataflow_monitor(expected_dataflow_monitor_sec, 0, 0, // threshold_packets 0, // threshold_bytes true, // is_threshold_in_packets false, // is_threshold_in_bytes false, // is_geq_upcall ">=" true); // is_leq_upcall "<=" } } // // If necessary, install a new SPT-switch dataflow monitor // if (pim_node()->is_switch_to_spt_enabled().get() && (! ((pim_mre_sg != NULL) && (pim_mre_sg->is_keepalive_timer_running())))) { uint32_t sec = pim_node()->switch_to_spt_threshold_interval_sec().get(); uint32_t bytes = pim_node()->switch_to_spt_threshold_bytes().get(); if (pim_mre->is_monitoring_switch_to_spt_desired_sg(pim_mre_sg)) { add_dataflow_monitor(sec, 0, 0, // threshold_packets bytes, // threshold_bytes false, // is_threshold_in_packets true, // is_threshold_in_bytes true, // is_geq_upcall ">=" false); // is_leq_upcall "<=" } } } void PimMfc::update_mfc(uint32_t new_iif_vif_index, const Mifset& new_olist, const PimMre* pim_mre_sg) { bool is_changed = false; if (new_iif_vif_index == Vif::VIF_INDEX_INVALID) { // // XXX: if the new iif is invalid, then we always unconditionally // add the entry to the kernel. This is a work-around in case // we just created a new PimMfc entry with invalid iif and empty // olist and we want to install that entry. // is_changed = true; } if (new_iif_vif_index != iif_vif_index()) { set_iif_vif_index(new_iif_vif_index); is_changed = true; } if (new_olist != olist()) { set_olist(new_olist); is_changed = true; } // // Calculate the set of outgoing interfaces for which the WRONGVIF signal // is disabled. // Mifset new_olist_disable_wrongvif; new_olist_disable_wrongvif.set(); // XXX: by default disable on all vifs new_olist_disable_wrongvif ^= new_olist; // Enable on all outgoing vifs // // If we are in process of switching to the SPT, then enable the WRONGVIF // signal on the expected new incoming interface. // if ((pim_mre_sg != NULL) && (! pim_mre_sg->is_spt()) && (pim_mre_sg->rpf_interface_s() != pim_mre_sg->rpf_interface_rp()) && (pim_mre_sg->was_switch_to_spt_desired_sg() || pim_mre_sg->is_join_desired_sg())) { if (pim_mre_sg->rpf_interface_s() != Vif::VIF_INDEX_INVALID) new_olist_disable_wrongvif.reset(pim_mre_sg->rpf_interface_s()); } if (new_olist_disable_wrongvif != olist_disable_wrongvif()) { set_olist_disable_wrongvif(new_olist_disable_wrongvif); is_changed = true; } if (is_changed) add_mfc_to_kernel(); } int PimMfc::add_mfc_to_kernel() { if (pim_node()->is_log_trace()) { string res, res2; for (uint32_t i = 0; i < pim_node()->maxvifs(); i++) { if (olist().test(i)) res += "O"; else res += "."; if (olist_disable_wrongvif().test(i)) res2 += "O"; else res2 += "."; } XLOG_TRACE(pim_node()->is_log_trace(), "Add MFC entry: (%s, %s) iif = %d olist = %s " "olist_disable_wrongvif = %s", cstring(source_addr()), cstring(group_addr()), iif_vif_index(), res.c_str(), res2.c_str()); } if (pim_node()->add_mfc_to_kernel(*this) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int PimMfc::delete_mfc_from_kernel() { if (!pim_node()) { return XORP_OK; } if (pim_node()->is_log_trace()) { string res; for (uint32_t i = 0; i < pim_node()->maxvifs(); i++) { if (olist().test(i)) res += "O"; else res += "."; } XLOG_TRACE(pim_node()->is_log_trace(), "Delete MFC entry: (%s, %s) iif = %d olist = %s", cstring(source_addr()), cstring(group_addr()), iif_vif_index(), res.c_str()); } // // XXX: we don't call delete_all_dataflow_monitor(), because // the deletion of the MFC entry itself will remove all associated // dataflow monitors. // if (pim_node()->delete_mfc_from_kernel(*this) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int PimMfc::add_dataflow_monitor(uint32_t threshold_interval_sec, uint32_t threshold_interval_usec, 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) { XLOG_TRACE(pim_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_addr()), cstring(group_addr()), 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 (pim_node()->add_dataflow_monitor(source_addr(), group_addr(), threshold_interval_sec, threshold_interval_usec, threshold_packets, threshold_bytes, is_threshold_in_packets, is_threshold_in_bytes, is_geq_upcall, is_leq_upcall) != XORP_OK) { return (XORP_ERROR); } if (is_leq_upcall && ((is_threshold_in_packets && (threshold_packets == 0)) || (is_threshold_in_bytes && (threshold_bytes == 0)))) { set_has_idle_dataflow_monitor(true); } if (is_geq_upcall) { set_has_spt_switch_dataflow_monitor(true); } return (XORP_OK); } int PimMfc::delete_dataflow_monitor(uint32_t threshold_interval_sec, uint32_t threshold_interval_usec, 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) { XLOG_TRACE(pim_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_addr()), cstring(group_addr()), 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 (pim_node()->delete_dataflow_monitor(source_addr(), group_addr(), threshold_interval_sec, threshold_interval_usec, threshold_packets, threshold_bytes, is_threshold_in_packets, is_threshold_in_bytes, is_geq_upcall, is_leq_upcall) != XORP_OK) { return (XORP_ERROR); } if (is_leq_upcall && ((is_threshold_in_packets && (threshold_packets == 0)) || (is_threshold_in_bytes && (threshold_bytes == 0)))) { set_has_idle_dataflow_monitor(false); } if (is_geq_upcall) { set_has_spt_switch_dataflow_monitor(false); } return (XORP_OK); } int PimMfc::delete_all_dataflow_monitor() { XLOG_TRACE(pim_node()->is_log_trace(), "Delete all dataflow monitors: " "source = %s group = %s", cstring(source_addr()), cstring(group_addr())); set_has_idle_dataflow_monitor(false); set_has_spt_switch_dataflow_monitor(false); if (pim_node()->delete_all_dataflow_monitor(source_addr(), group_addr()) != XORP_OK) { return (XORP_ERROR); } return (XORP_OK); } // Try to remove PimMfc entry if not needed anymore. // Return true if entry is scheduled to be removed, othewise false. bool PimMfc::entry_try_remove() { bool ret_value; if (is_task_delete_pending()) return (true); // The entry is already pending deletion ret_value = entry_can_remove(); if (ret_value) pim_mrt()->add_task_delete_pim_mfc(this); return (ret_value); } bool PimMfc::entry_can_remove() const { uint32_t lookup_flags; PimMre *pim_mre; if (has_forced_deletion()) return (true); if (iif_vif_index() == Vif::VIF_INDEX_INVALID) return (true); lookup_flags = PIM_MRE_RP | PIM_MRE_WC | PIM_MRE_SG | PIM_MRE_SG_RPT; pim_mre = pim_mrt()->pim_mre_find(source_addr(), group_addr(), lookup_flags, 0); if (pim_mre == NULL) return (true); return (false); } void PimMfc::remove_pim_mfc_entry_mfc() { if (is_task_delete_pending() && entry_can_remove()) { // // Remove the entry from the PimMrt, and mark it as deletion done // pim_mrt()->remove_pim_mfc(this); set_is_task_delete_done(true); } else { set_is_task_delete_pending(false); set_is_task_delete_done(false); return; } } xorp/pim/pim_mre.cc0000664000076400007640000007511111635757530014425 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // PIM Multicast Routing Entry handling // #include "pim_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvx.hh" #include "pim_mfc.hh" #include "pim_mre.hh" #include "pim_mrt.hh" #include "pim_node.hh" #include "pim_vif.hh" PimMre::PimMre(PimMrt* pim_mrt, const IPvX& source, const IPvX& group) : Mre(source, group), _pim_mrt(pim_mrt), _pim_rp(NULL), _mrib_rp(NULL), _mrib_s(NULL), _nbr_mrib_next_hop_rp(NULL), _nbr_mrib_next_hop_s(NULL), _rpfp_nbr_wc(NULL), _rpfp_nbr_sg(NULL), _rpfp_nbr_sg_rpt(NULL), _wc_entry(NULL), _rp_entry(NULL), _sg_sg_rpt_entry(NULL), _pmbr_addr(IPvX::ZERO(family())), _flags(0) { for (size_t i = 0; i < MAX_VIFS; i++) _assert_winner_metrics[i] = NULL; } PimMre::~PimMre() { // // Reset the pointers of the corresponding (S,G) and (S,G,rpt) entries // to this PimMre entry. // do { if (is_sg()) { if (sg_rpt_entry() != NULL) sg_rpt_entry()->set_sg_entry(NULL); break; } if (is_sg_rpt()) { if (sg_entry() != NULL) sg_entry()->set_sg_rpt_entry(NULL); break; } } while (false); for (size_t i = 0; i < MAX_VIFS; i++) { if (_assert_winner_metrics[i] != NULL) { delete _assert_winner_metrics[i]; _assert_winner_metrics[i] = NULL; } } // // Remove this entry from various lists // remove_pim_mre_lists(); // // Remove this entry from the PimMrt table // pim_mrt()->remove_pim_mre(this); } // // Add the PimMre entry to various lists // void PimMre::add_pim_mre_lists() { // // Add this entry to various lists towards neighbors // do { if (is_rp()) { // (*,*,RP) if (_nbr_mrib_next_hop_rp != NULL) { _nbr_mrib_next_hop_rp->add_pim_mre(this); } else { pim_node()->add_pim_mre_no_pim_nbr(this); } break; } if (is_wc()) { // (*,G) if (_nbr_mrib_next_hop_rp != NULL) { _nbr_mrib_next_hop_rp->add_pim_mre(this); } else { pim_node()->add_pim_mre_no_pim_nbr(this); } if (_rpfp_nbr_wc != _nbr_mrib_next_hop_rp) { if (_rpfp_nbr_wc != NULL) { _rpfp_nbr_wc->add_pim_mre(this); } else { pim_node()->add_pim_mre_no_pim_nbr(this); } } break; } if (is_sg()) { // (S,G) if (_nbr_mrib_next_hop_s != NULL) { _nbr_mrib_next_hop_s->add_pim_mre(this); } else { pim_node()->add_pim_mre_no_pim_nbr(this); } if (_rpfp_nbr_sg != _nbr_mrib_next_hop_s) { if (_rpfp_nbr_sg != NULL) { _rpfp_nbr_sg->add_pim_mre(this); } else { pim_node()->add_pim_mre_no_pim_nbr(this); } } break; } if (is_sg_rpt()) { // (S,G,rpt) if (_rpfp_nbr_sg_rpt != NULL) { _rpfp_nbr_sg_rpt->add_pim_mre(this); } else { pim_node()->add_pim_mre_no_pim_nbr(this); } break; } XLOG_UNREACHABLE(); break; } while (false); // // Add this entry to the RP table // pim_node()->rp_table().add_pim_mre(this); } // // Remove the PimMre entry from various lists // void PimMre::remove_pim_mre_lists() { // // Remove this entry from various lists towards neighbors // do { if (is_rp()) { // (*,*,RP) if (_nbr_mrib_next_hop_rp != NULL) { _nbr_mrib_next_hop_rp->delete_pim_mre(this); } else { pim_node()->delete_pim_mre_no_pim_nbr(this); } _nbr_mrib_next_hop_rp = NULL; break; } if (is_wc()) { // (*,G) if (_nbr_mrib_next_hop_rp != NULL) { _nbr_mrib_next_hop_rp->delete_pim_mre(this); } else { pim_node()->delete_pim_mre_no_pim_nbr(this); } if (_rpfp_nbr_wc != _nbr_mrib_next_hop_rp) { if (_rpfp_nbr_wc != NULL) { _rpfp_nbr_wc->delete_pim_mre(this); } else { pim_node()->delete_pim_mre_no_pim_nbr(this); } } _nbr_mrib_next_hop_rp = NULL; _rpfp_nbr_wc = NULL; break; } if (is_sg()) { // (S,G) if (_nbr_mrib_next_hop_s != NULL) { _nbr_mrib_next_hop_s->delete_pim_mre(this); } else { pim_node()->delete_pim_mre_no_pim_nbr(this); } if (_rpfp_nbr_sg != _nbr_mrib_next_hop_s) { if (_rpfp_nbr_sg != NULL) { _rpfp_nbr_sg->delete_pim_mre(this); } else { pim_node()->delete_pim_mre_no_pim_nbr(this); } } _nbr_mrib_next_hop_s = NULL; _nbr_mrib_next_hop_rp = NULL; break; } if (is_sg_rpt()) { // (S,G,rpt) if (_rpfp_nbr_sg_rpt != NULL) { _rpfp_nbr_sg_rpt->delete_pim_mre(this); } else { pim_node()->delete_pim_mre_no_pim_nbr(this); } _rpfp_nbr_sg_rpt = NULL; break; } XLOG_UNREACHABLE(); break; } while (false); // // Remove this entry from the RP table // pim_node()->rp_table().delete_pim_mre(this); } PimNode* PimMre::pim_node() const { return _pim_mrt->pim_node(); } int PimMre::family() const { return _pim_mrt->family(); } uint32_t PimMre::pim_register_vif_index() const { return (_pim_mrt->pim_register_vif_index()); } // // Mifset functions // void PimMre::set_sg(bool v) { if (v) { set_sg_rpt(false); set_wc(false); set_rp(false); _flags |= PIM_MRE_SG; } else { _flags &= ~PIM_MRE_SG; } } void PimMre::set_sg_rpt(bool v) { if (v) { set_sg(false); set_wc(false); set_rp(false); _flags |= PIM_MRE_SG_RPT; } else { _flags &= ~PIM_MRE_SG_RPT; } } void PimMre::set_wc(bool v) { if (v) { set_sg(false); set_sg_rpt(false); set_rp(false); _flags |= PIM_MRE_WC; } else { _flags &= ~PIM_MRE_WC; } } void PimMre::set_rp(bool v) { if (v) { set_sg(false); set_sg_rpt(false); set_wc(false); _flags |= PIM_MRE_RP; } else { _flags &= ~PIM_MRE_RP; } } // Note: applies only for (S,G) void PimMre::set_spt(bool v) { if (! is_sg()) return; if (is_spt() == v) return; // Nothing changed if (v) { _flags |= PIM_MRE_SPT; } else { _flags &= ~PIM_MRE_SPT; } pim_mrt()->add_task_sptbit_sg(source_addr(), group_addr()); } const IPvX * PimMre::rp_addr_ptr() const { if (is_rp()) return (&source_addr()); // XXX: (*,*,RP) entry if (pim_rp() != NULL) return (&pim_rp()->rp_addr()); return (NULL); } string PimMre::rp_addr_string() const { const IPvX *addr_ptr = rp_addr_ptr(); if (addr_ptr != NULL) return (cstring(*addr_ptr)); else return ("RP_ADDR_UNKNOWN"); } // // Set of state interface functions // (See the "State Summarization Macros" section) // // Note: works for all entries const Mifset& PimMre::joins_rp() const { static Mifset mifs; const PimMre *pim_mre_rp; if (is_rp()) { pim_mre_rp = this; } else { pim_mre_rp = rp_entry(); if (pim_mre_rp == NULL) { mifs.reset(); return (mifs); } } mifs = pim_mre_rp->downstream_join_state(); mifs |= pim_mre_rp->downstream_prune_pending_state(); return (mifs); } // Note: works for (*,G), (S,G), (S,G,rpt) const Mifset& PimMre::joins_wc() const { static Mifset mifs; const PimMre *pim_mre_wc; if (is_wc()) { pim_mre_wc = this; } else { pim_mre_wc = wc_entry(); if (pim_mre_wc == NULL) { mifs.reset(); return (mifs); } } mifs = pim_mre_wc->downstream_join_state(); mifs |= pim_mre_wc->downstream_prune_pending_state(); return (mifs); } // Note: applies only for (S,G) const Mifset& PimMre::joins_sg() const { static Mifset mifs; if (! is_sg()) { mifs.reset(); return (mifs); } mifs = downstream_join_state(); mifs |= downstream_prune_pending_state(); return (mifs); } // Note: applies only for (S,G,rpt) const Mifset& PimMre::prunes_sg_rpt() const { static Mifset mifs; if (! is_sg_rpt()) { mifs.reset(); return (mifs); } mifs = downstream_prune_state(); mifs |= downstream_prune_tmp_state(); return (mifs); } // Note: works for (*,*,RP), (*,G), (S,G), (S,G,rpt) const Mifset& PimMre::immediate_olist_rp() const { return (joins_rp()); } // Note: works for (*,G), (S,G), (S,G,rpt) const Mifset& PimMre::immediate_olist_wc() const { static Mifset mifs; if (! (is_wc() || is_sg() || is_sg_rpt())) { mifs.reset(); return (mifs); } mifs = joins_wc(); mifs |= pim_include_wc(); mifs &= ~lost_assert_wc(); return (mifs); } // Note: works for (S,G) const Mifset& PimMre::immediate_olist_sg() const { static Mifset mifs; if (! is_sg()) { mifs.reset(); return (mifs); } mifs = joins_sg(); mifs |= pim_include_sg(); mifs &= ~lost_assert_sg(); return (mifs); } // Note: applies for (*,*,RP), (*,G), (S,G), (S,G,rpt) const Mifset& PimMre::inherited_olist_sg() const { static Mifset mifs; const PimMre *pim_mre_sg = NULL; mifs = inherited_olist_sg_rpt(); // Get a pointer to the (S,G) entry do { if (is_sg()) { pim_mre_sg = this; break; } if (is_sg_rpt()) { pim_mre_sg = sg_entry(); break; } break; } while (false); if (pim_mre_sg != NULL) { mifs |= pim_mre_sg->joins_sg(); mifs |= pim_mre_sg->pim_include_sg(); mifs &= ~(pim_mre_sg->lost_assert_sg()); } return (mifs); } // Note: applies for (*,*,RP), (*,G), (S,G), (S,G,rpt) const Mifset& PimMre::inherited_olist_sg_rpt() const { static Mifset mifs; Mifset mifs2; mifs.reset(); do { if (is_sg_rpt()) { // // (S,G,rpt) // PimMre *pim_mre_sg = sg_entry(); mifs = joins_rp(); mifs |= joins_wc(); mifs &= ~prunes_sg_rpt(); mifs2 = pim_include_wc(); if (pim_mre_sg != NULL) mifs2 &= ~(pim_mre_sg->pim_exclude_sg()); mifs |= mifs2; mifs2 = lost_assert_wc(); mifs2 |= lost_assert_sg_rpt(); mifs &= ~mifs2; break; } if (is_sg()) { // // (S,G) // PimMre *pim_mre_sg_rpt = sg_rpt_entry(); mifs = joins_rp(); mifs |= joins_wc(); if (pim_mre_sg_rpt != NULL) mifs &= ~(pim_mre_sg_rpt->prunes_sg_rpt()); mifs2 = pim_include_wc(); mifs2 &= ~pim_exclude_sg(); mifs |= mifs2; mifs2 = lost_assert_wc(); mifs2 |= lost_assert_sg_rpt(); // XXX: applies for (S,G) as well mifs &= ~mifs2; break; } if (is_wc()) { // // (*,G) // mifs = joins_rp(); mifs |= joins_wc(); mifs2 = pim_include_wc(); mifs |= mifs2; mifs2 = lost_assert_wc(); mifs &= ~mifs2; break; } if (is_rp()) { // // (*,*,RP) // mifs = joins_rp(); break; } } while (false); return (mifs); } // Note: works for (*,G), (S,G), (S,G,rpt) const Mifset& PimMre::pim_include_wc() const { static Mifset mifs; mifs = i_am_dr(); mifs &= ~lost_assert_wc(); mifs |= i_am_assert_winner_wc(); mifs &= local_receiver_include_wc(); return (mifs); } // Note: works only for (S,G) const Mifset& PimMre::pim_include_sg() const { static Mifset mifs; mifs = i_am_dr(); mifs &= ~lost_assert_sg(); mifs |= i_am_assert_winner_sg(); mifs &= local_receiver_include_sg(); return (mifs); } // Note: works only for (S,G) const Mifset& PimMre::pim_exclude_sg() const { static Mifset mifs; mifs = i_am_dr(); mifs &= ~lost_assert_wc(); mifs |= i_am_assert_winner_wc(); mifs &= local_receiver_exclude_sg(); return (mifs); } // Note: works only for (S,G) const Mifset& PimMre::local_receiver_include_sg() const { static Mifset mifs; if (! is_sg()) { mifs.reset(); return (mifs); } return (local_receiver_include()); } // Note: works for (*,G), (S,G), (S,G,rpt) const Mifset& PimMre::local_receiver_include_wc() const { static Mifset mifs; const PimMre *pim_mre_wc; if (is_wc()) { pim_mre_wc = this; } else { pim_mre_wc = wc_entry(); if (pim_mre_wc == NULL) { mifs.reset(); return (mifs); } } return (pim_mre_wc->local_receiver_include()); } const Mifset& PimMre::local_receiver_exclude_sg() const { static Mifset mifs; if (! is_sg()) { mifs.reset(); return (mifs); } mifs = local_receiver_include_wc(); mifs &= local_receiver_exclude(); return (mifs); } void PimMre::set_local_receiver_include(uint32_t vif_index, bool v) { if (vif_index == Vif::VIF_INDEX_INVALID) return; if (_local_receiver_include.test(vif_index) == v) return; // Nothing changed if (v) _local_receiver_include.set(vif_index); else _local_receiver_include.reset(vif_index); // Add the task to recompute the effect of this and take actions do { if (is_wc()) { pim_mrt()->add_task_local_receiver_include_wc(vif_index, group_addr()); break; } if (is_sg()) { pim_mrt()->add_task_local_receiver_include_sg(vif_index, source_addr(), group_addr()); break; } } while (false); // Try to remove the entry if (! v) entry_try_remove(); } void PimMre::set_local_receiver_exclude(uint32_t vif_index, bool v) { if (vif_index == Vif::VIF_INDEX_INVALID) return; if (_local_receiver_exclude.test(vif_index) == v) return; // Nothing changed if (v) _local_receiver_exclude.set(vif_index); else _local_receiver_exclude.reset(vif_index); // Add the task to recompute the effect of this and take actions do { if (is_sg()) { pim_mrt()->add_task_local_receiver_exclude_sg(vif_index, source_addr(), group_addr()); break; } } while (false); // Try to remove the entry if (! v) entry_try_remove(); } // Try to remove PimMre entry if not needed anymore. // The function is generic and can be applied to any type of PimMre entry. // Note: applies for (*,*,RP), (*,G), (S,G), (S,G,rpt) // Return true if entry is scheduled to be removed, othewise false. bool PimMre::entry_try_remove() { bool ret_value; // TODO: XXX: PAVPAVPAV: make sure it is called // from all appropriate places!! if (is_task_delete_pending()) return (true); // The entry is already pending deletion ret_value = entry_can_remove(); if (ret_value) pim_mrt()->add_task_delete_pim_mre(this); return (ret_value); } // Test if it is OK to remove PimMre entry. // It tests whether all downstream and upstream // states are NoInfo, as well as whether no relevant timers are running. // The function is generic and can be applied to any type of PimMre entry. // Note: applies for (*,*,RP), (*,G), (S,G), (S,G,rpt) // Return true if entry can be removed, othewise false. bool PimMre::entry_can_remove() const { // TODO: XXX: PAVPAVPAV: add more checks if needed if (_local_receiver_include.any()) return (false); if (_local_receiver_exclude.any()) return (false); if (_downstream_join_state.any()) return (false); if (_downstream_prune_state.any()) return (false); if (_downstream_prune_pending_state.any()) return (false); // XXX: we don't really need to test _downstream_tmp_state, because // it is used only in combination with other state, but anyway... if (_downstream_tmp_state.any()) return (false); if (is_rp() || is_wc() || is_sg()) { if (is_joined_state()) return (false); } if (is_rp()) { if (immediate_olist_rp().any()) return (false); if ((rp_addr_ptr() != NULL) && pim_node()->rp_table().has_rp_addr(*rp_addr_ptr())) { return (false); } } if (is_wc()) { if (immediate_olist_wc().any()) return (false); if (pim_include_wc().any()) return (false); } if (is_sg()) { if (immediate_olist_sg().any()) return (false); if (pim_include_sg().any()) return (false); if (pim_exclude_sg().any()) return (false); } // XXX: (S,G,rpt) entry cannot be removed if upstream state is Pruned, // or the Override Timer is running (when in Not Pruned state). if (is_sg_rpt()) { if (is_pruned_state()) return (false); if (is_not_pruned_state() && const_override_timer().scheduled()) return (false); } #if 0 // TODO: XXX: PAVPAVPAV: not needed? if (inherited_olist_sg().any()) return (false); if (inherited_olist_sg_rpt().any()) return (false); #endif // 0 #if 1 // TODO: XXX: PAVPAVPAV: not needed? if (is_sg()) { if (! is_register_noinfo_state()) return (false); } #endif // 1 #if 1 // TODO: XXX: PAVPAVPAV: not needed? if (is_wc() || is_sg()) { if (i_am_assert_winner_state().any() || i_am_assert_loser_state().any()) return (false); } #endif // 1 if (is_sg()) { // TODO: OK NOT to remove if the KeepaliveTimer(S,G) is running? if (is_keepalive_timer_running()) return (false); } return (true); } // The KeepaliveTimer(S,G) // Note: applies only for (S,G) void PimMre::start_keepalive_timer() { if (! is_sg()) return; if (is_keepalive_timer_running()) return; // Nothing changed _flags |= PIM_MRE_KEEPALIVE_TIMER_IS_SET; pim_mrt()->add_task_keepalive_timer_sg(source_addr(), group_addr()); } // The KeepaliveTimer(S,G) // Note: applies only for (S,G) void PimMre::cancel_keepalive_timer() { if (! is_sg()) return; if (! is_keepalive_timer_running()) return; // Nothing changed // // If an RP, the PMBR value must be cleared when the Keepalive Timer // expires. // // XXX: We always clear the PMBR value and we don't use "is_rp()" // to check whether we are the RP, because the PMBR value is not used // on non-RPs, so it doesn't hurt to unconditionally reset it. // Otherwise, we will have to add state dependency whenever the // RP changes. This introduces complexity without any benefit, // hence we don't do it. // clear_pmbr_addr(); _flags &= ~PIM_MRE_KEEPALIVE_TIMER_IS_SET; pim_mrt()->add_task_keepalive_timer_sg(source_addr(), group_addr()); } // The KeepaliveTimer(S,G) // XXX: applies only for (S,G) bool PimMre::is_keepalive_timer_running() const { if (! is_sg()) return (false); return (_flags & PIM_MRE_KEEPALIVE_TIMER_IS_SET); } // The KeepaliveTimer(S,G) // Note: applies only for (S,G) void PimMre::keepalive_timer_timeout() { if (! is_sg()) return; if (! is_keepalive_timer_running()) return; // // If an RP, the PMBR value must be cleared when the Keepalive Timer // expires. // XXX: this will happen inside cancel_keepalive_timer() // cancel_keepalive_timer(); entry_try_remove(); } // The KeepaliveTimer(S,G) // Note: applies only for (S,G) void PimMre::recompute_set_keepalive_timer_sg() { bool should_set_keepalive_timer_sg = false; PimMfc *pim_mfc; if (! is_sg()) return; if (is_keepalive_timer_running()) return; // // Test if there is PimMfc entry. If there is no PimMfc entry, // then there is no (S,G) traffic, and therefore the KeepaliveTimer(S,G) // does not need to be started. // pim_mfc = pim_mrt()->pim_mfc_find(source_addr(), group_addr(), false); if (pim_mfc == NULL) return; do { // // Test the following scenario: // // On receipt of data from S to G on interface iif: // if( DirectlyConnected(S) == TRUE AND iif == RPF_interface(S) ) { // set KeepaliveTimer(S,G) to Keepalive_Period // ... // } // // if( iif == RPF_interface(S) AND UpstreamJPState(S,G) == Joined AND // inherited_olist(S,G) != NULL ) { // set KeepaliveTimer(S,G) to Keepalive_Period // } // if (is_directly_connected_s() && (pim_mfc->iif_vif_index() == rpf_interface_s())) { should_set_keepalive_timer_sg = true; break; } if ((pim_mfc->iif_vif_index() == rpf_interface_s()) && is_joined_state() && inherited_olist_sg().any()) { should_set_keepalive_timer_sg = true; break; } // // Test the following scenario: // // CheckSwitchToSpt(S,G) { // if ( ( pim_include(*,G) (-) pim_exclude(S,G) // (+) pim_include(S,G) != NULL ) // AND SwitchToSptDesired(S,G) ) { // # Note: Restarting the KAT will result in the SPT switch // restart KeepaliveTimer(S,G); // Mifset mifs; mifs = pim_include_wc(); mifs &= ~pim_exclude_sg(); mifs |= pim_include_sg(); if (mifs.any() && was_switch_to_spt_desired_sg()) { should_set_keepalive_timer_sg = true; break; } // // Test the following scenario: // // packet_arrives_on_rp_tunnel( pkt ) { // .... // if( I_am_RP(G) AND outer.dst == RP(G) ) { // ... // if ( SPTbit(S,G) OR SwitchToSptDesired(S,G) ) { // if ( sentRegisterStop == TRUE ) { // restart KeepaliveTimer(S,G) to RP_Keepalive_Period; // } else { // restart KeepaliveTimer(S,G) to Keepalive_Period; // } // } // ... // if (i_am_rp() && (is_spt() || was_switch_to_spt_desired_sg())) { should_set_keepalive_timer_sg = true; break; } break; } while (false); if (should_set_keepalive_timer_sg) { // // Start the (S,G) Keepalive Timer, and add a dataflow monitor // (if it wasn't added yet). // start_keepalive_timer(); if (! pim_mfc->has_idle_dataflow_monitor()) { // // Add a dataflow monitor to expire idle (S,G) PimMre state // and/or idle PimMfc+MFC state // // First, compute the dataflow monitor value, which may be // different for (S,G) entry in the RP that is used for // Register decapsulation. // uint32_t expected_dataflow_monitor_sec = PIM_KEEPALIVE_PERIOD_DEFAULT; if (is_kat_set_to_rp_keepalive_period()) { if (expected_dataflow_monitor_sec < PIM_RP_KEEPALIVE_PERIOD_DEFAULT) { expected_dataflow_monitor_sec = PIM_RP_KEEPALIVE_PERIOD_DEFAULT; } } pim_mfc->add_dataflow_monitor(expected_dataflow_monitor_sec, 0, 0, // threshold_packets 0, // threshold_bytes true, // is_threshold_in_packets false,// is_threshold_in_bytes false,// is_geq_upcall ">=" true);// is_leq_upcall "<=" } } } // // MISC. info // // Note: applies for all entries const Mifset& PimMre::i_am_dr() const { return pim_mrt()->i_am_dr(); } // // PimVif-related methods // // Note: applies for (*,*,RP) void PimMre::recompute_start_vif_rp(uint32_t vif_index) { // XXX: nothing to do UNUSED(vif_index); } // Note: applies for (*,G) void PimMre::recompute_start_vif_wc(uint32_t vif_index) { // XXX: nothing to do UNUSED(vif_index); } // Note: applies for (S,G) void PimMre::recompute_start_vif_sg(uint32_t vif_index) { // XXX: nothing to do UNUSED(vif_index); } // Note: applies for (G,G,rpt) void PimMre::recompute_start_vif_sg_rpt(uint32_t vif_index) { // XXX: nothing to do UNUSED(vif_index); } // Note: applies for (*,*,RP) void PimMre::recompute_stop_vif_rp(uint32_t vif_index) { // // Reset all associated state with 'vif_index' // downstream_prune_pending_timer_timeout_rp(vif_index); _downstream_prune_pending_timers[vif_index].unschedule(); downstream_expiry_timer_timeout_rp(vif_index); _downstream_expiry_timers[vif_index].unschedule(); // XXX: assert-related state doesn't apply for (*,*,RP) entry set_local_receiver_include(vif_index, false); set_local_receiver_exclude(vif_index, false); set_downstream_noinfo_state(vif_index); } // Note: applies for (*,G) void PimMre::recompute_stop_vif_wc(uint32_t vif_index) { // // Reset all associated state with 'vif_index' // downstream_prune_pending_timer_timeout_wc(vif_index); _downstream_prune_pending_timers[vif_index].unschedule(); downstream_expiry_timer_timeout_wc(vif_index); _downstream_expiry_timers[vif_index].unschedule(); // // XXX: don't call assert_timer_timeout_wc(vif_index) // because it may transmit a regular Assert message. // process_could_assert_wc(vif_index, false); delete_assert_winner_metric_wc(vif_index); _assert_timers[vif_index].unschedule(); set_assert_tracking_desired_state(vif_index, false); set_could_assert_state(vif_index, false); delete_assert_winner_metric_wc(vif_index); set_assert_noinfo_state(vif_index); _asserts_rate_limit.reset(vif_index); set_local_receiver_include(vif_index, false); set_local_receiver_exclude(vif_index, false); set_downstream_noinfo_state(vif_index); } // Note: applies for (S,G) void PimMre::recompute_stop_vif_sg(uint32_t vif_index) { // // Reset all associated state with 'vif_index' // downstream_prune_pending_timer_timeout_sg(vif_index); _downstream_prune_pending_timers[vif_index].unschedule(); downstream_expiry_timer_timeout_sg(vif_index); _downstream_expiry_timers[vif_index].unschedule(); // // XXX: don't call assert_timer_timeout_sg(vif_index) // because it may transmit a regular Assert message. // process_could_assert_sg(vif_index, false); delete_assert_winner_metric_sg(vif_index); set_assert_winner_metric_is_better_than_spt_assert_metric_sg(vif_index, false); _assert_timers[vif_index].unschedule(); set_assert_tracking_desired_state(vif_index, false); set_could_assert_state(vif_index, false); delete_assert_winner_metric_sg(vif_index); set_assert_noinfo_state(vif_index); _asserts_rate_limit.reset(vif_index); set_local_receiver_include(vif_index, false); set_local_receiver_exclude(vif_index, false); set_downstream_noinfo_state(vif_index); } // Note: applies for (S,G,rpt) void PimMre::recompute_stop_vif_sg_rpt(uint32_t vif_index) { // // Reset all associated state with 'vif_index' // downstream_prune_pending_timer_timeout_sg_rpt(vif_index); _downstream_prune_pending_timers[vif_index].unschedule(); downstream_expiry_timer_timeout_sg_rpt(vif_index); _downstream_expiry_timers[vif_index].unschedule(); // XXX: assert-related state doesn't apply for (S,G,rpt) entry set_local_receiver_include(vif_index, false); set_local_receiver_exclude(vif_index, false); set_downstream_noinfo_state(vif_index); } // Note: applies for (*,*,RP), (*,G), (S,G), (S,G,rpt) void PimMre::add_pim_mre_rp_entry() { if (is_wc() || is_sg() || is_sg_rpt()) { // XXX: the RP-related state is set by a special task // uncond_set_pim_rp(compute_rp()); } } // Note: applies for (*,G), (S,G), (S,G,rpt) void PimMre::add_pim_mre_wc_entry() { if (is_sg() || is_sg_rpt()) { PimMre *pim_mre_wc = pim_mrt()->pim_mre_find(source_addr(), group_addr(), PIM_MRE_WC, 0); if (pim_mre_wc == wc_entry()) return; // Nothing changed // Remove from the PimNbr and PimRp lists. // // Remove this entry from the RP table lists. // XXX: We don't delete this entry from the PimNbr lists, // because an (S,G) or (S,G,rpt) is on those lists regardless // whether it has a matching (*,G) entry. XLOG_ASSERT(pim_mre_wc != NULL); pim_node()->rp_table().delete_pim_mre(this); set_wc_entry(pim_mre_wc); } } // Note: applies for (S,G), (S,G,rpt) void PimMre::add_pim_mre_sg_entry() { // XXX: the cross-pointers between (S,G) and and (S,G,rpt) are set // when the PimMre entry was created } // Note: applies for (S,G), (S,G,rpt) void PimMre::add_pim_mre_sg_rpt_entry() { // XXX: the cross-pointers between (S,G) and and (S,G,rpt) are set // when the PimMre entry was created } // Note: applies for (*,*,RP), (*,G), (S,G), (S,G,rpt) void PimMre::remove_pim_mre_rp_entry() { if (is_rp()) { if (is_task_delete_pending() && entry_can_remove()) { // // Remove the entry from the PimMrt, and mark it as deletion done // pim_mrt()->remove_pim_mre(this); set_is_task_delete_done(true); } else { set_is_task_delete_pending(false); set_is_task_delete_done(false); return; } } if (is_wc() || is_sg() || is_sg_rpt()) { // XXX: the RP-related state is set by a special task // uncond_set_pim_rp(compute_rp()); } } // Note: applies for (*,G), (S,G), (S,G,rpt) void PimMre::remove_pim_mre_wc_entry() { if (is_wc()) { if (is_task_delete_pending() && entry_can_remove()) { // // Remove the entry from the PimMrt, and mark it as deletion done // pim_mrt()->remove_pim_mre(this); set_is_task_delete_done(true); } else { set_is_task_delete_pending(false); set_is_task_delete_done(false); return; } } if (is_sg() || is_sg_rpt()) { PimMre *pim_mre_wc = pim_mrt()->pim_mre_find(source_addr(), group_addr(), PIM_MRE_WC, 0); if (pim_mre_wc == wc_entry()) return; // Nothing changed set_wc_entry(pim_mre_wc); // Add to the PimNbr and PimRp lists. add_pim_mre_lists(); } } // Note: applies for (S,G), (S,G,rpt) void PimMre::remove_pim_mre_sg_entry() { if (is_sg()) { if (is_task_delete_pending() && entry_can_remove()) { // // Remove the entry from the PimMrt, and mark it as deletion done // pim_mrt()->remove_pim_mre(this); set_is_task_delete_done(true); } else { set_is_task_delete_pending(false); set_is_task_delete_done(false); return; } } if (is_sg_rpt()) { PimMre *pim_mre_sg = pim_mrt()->pim_mre_find(source_addr(), group_addr(), PIM_MRE_SG, 0); if (pim_mre_sg == sg_entry()) return; // Nothing changed set_sg(pim_mre_sg); } } // Note: applies for (S,G), (S,G,rpt) void PimMre::remove_pim_mre_sg_rpt_entry() { if (is_sg_rpt()) { if (is_task_delete_pending() && entry_can_remove()) { // // Remove the entry from the PimMrt, and mark it as deletion done // pim_mrt()->remove_pim_mre(this); set_is_task_delete_done(true); } else { set_is_task_delete_pending(false); set_is_task_delete_done(false); return; } } if (is_sg()) { PimMre *pim_mre_sg_rpt = pim_mrt()->pim_mre_find(source_addr(), group_addr(), PIM_MRE_SG_RPT, 0); if (pim_mre_sg_rpt == sg_rpt_entry()) return; // Nothing changed set_sg_rpt(pim_mre_sg_rpt); } } xorp/pim/pim_rp.cc0000664000076400007640000010057111421137511014242 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // PIM RP information implementation. // PIM-SMv2 (draft-ietf-pim-sm-new-* and draft-ietf-pim-sm-bsr-*) // #include "pim_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvx.hh" #include "libxorp/utils.hh" #include "libxorp/time_slice.hh" #include "pim_mfc.hh" #include "pim_mre.hh" #include "pim_node.hh" #include "pim_rp.hh" #include "pim_vif.hh" // // Exported variables // // // Local constants definitions // // // Local structures/classes, typedefs and macros // // // The RP hash function. Stolen from Eddy Rusty's (eddy@isi.edu) // implementation (for compatibility ;) // #define SEED1 1103515245 #define SEED2 12345 #define RP_HASH_VALUE(G, M, C) (((SEED1) * (((SEED1) * ((G) & (M)) + (SEED2)) ^ (C)) + (SEED2)) % 0x80000000U) #define RP_HASH_VALUE_DERIVED(Gderived, C) (((SEED1) * (((SEED1) * ((Gderived)) + (SEED2)) ^ (C)) + (SEED2)) % 0x80000000U) // // Local variables // // // Local functions prototypes // /** * RpTable::RpTable: * @pim_node: The associated PIM node. * * RpTable constructor. **/ RpTable::RpTable(PimNode& pim_node) : ProtoUnit(pim_node.family(), pim_node.module_id()), _pim_node(pim_node) { } /** * RpTable::~RpTable: * @: * * RpTable destructor. * **/ RpTable::~RpTable() { clear(); } /** * Clear the entry. */ void RpTable::clear() { stop(); } /** * RpTable::start: * @: * * Start the RP table. * * Return value: %XORP_OK on success, otherwize %XORP_ERROR. **/ int RpTable::start() { if (! is_enabled()) return (XORP_OK); if (is_up() || is_pending_up()) return (XORP_OK); if (ProtoUnit::start() != XORP_OK) return (XORP_ERROR); // Perform misc. RP table-specific start operations // TODO: IMPLEMENT IT! return (XORP_OK); } /** * RpTable::stop: * @: * * Stop the RP table. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int RpTable::stop() { if (is_down()) return (XORP_OK); if (ProtoUnit::stop() != XORP_OK) return (XORP_ERROR); // Perform misc. RP table-specific stop operations // TODO: IMPLEMENT IT! // Remove all RP entries // TODO: any other actions? delete_pointers_list(_rp_list); delete_pointers_list(_processing_rp_list); return (XORP_OK); } // // Lookup the RP for @group_addr // PimRp * RpTable::rp_find(const IPvX& group_addr) { list::iterator iter; PimRp *best_rp = NULL; for (iter = _rp_list.begin(); iter != _rp_list.end(); ++iter) { PimRp *pim_rp = *iter; if (! pim_rp->group_prefix().contains(group_addr)) continue; switch (pim_rp->rp_learned_method()) { case PimRp::RP_LEARNED_METHOD_AUTORP: case PimRp::RP_LEARNED_METHOD_BOOTSTRAP: case PimRp::RP_LEARNED_METHOD_STATIC: break; default: // Unknown learned method. Ignore the RP. continue; } // // Found a matching RP // if (best_rp == NULL) { best_rp = pim_rp; continue; } best_rp = compare_rp(group_addr, best_rp, pim_rp); } return (best_rp); } PimRp * RpTable::compare_rp(const IPvX& group_addr, PimRp *rp1, PimRp *rp2) const { // // Choose the longer match (if exist) // if (rp1->group_prefix().prefix_len() > rp2->group_prefix().prefix_len()) return (rp1); if (rp1->group_prefix().prefix_len() < rp2->group_prefix().prefix_len()) return (rp2); // // Compare priorities // // // XXX: Only the Bootstrap-derived RPs priority is defined as // 'smaller is better'. The priority of the RPs derived by alternative // methods is not defined. Typically, a domain would use only one // method to derive the Cand-RP set. If more than one method is // used, extra care is needed that the priority comparison among // all RPs is unambiguous. // switch (rp1->rp_learned_method()) { case PimRp::RP_LEARNED_METHOD_AUTORP: case PimRp::RP_LEARNED_METHOD_BOOTSTRAP: case PimRp::RP_LEARNED_METHOD_STATIC: switch (rp2->rp_learned_method()) { case PimRp::RP_LEARNED_METHOD_AUTORP: case PimRp::RP_LEARNED_METHOD_BOOTSTRAP: case PimRp::RP_LEARNED_METHOD_STATIC: // Smaller is better (XXX: true for BOOTSTRAP) if (rp1->rp_priority() < rp2->rp_priority()) return (rp1); if (rp1->rp_priority() > rp2->rp_priority()) return (rp2); break; default: XLOG_UNREACHABLE(); // Unknown learned method. Ignore the RP. return (rp1); } break; default: XLOG_UNREACHABLE(); // Unknown learned method. Ignore the RP. return (rp2); } // // Compute the hash function values for both RPs // // First compute the group masked address, and then its derived // form (if it is longer than 32 bits) // uint32_t derived_masked_group1 = 0; uint32_t derived_masked_group2 = 0; uint32_t derived_rp1 = 0; uint32_t derived_rp2 = 0; do { IPvXNet group_masked_addr1(group_addr, rp1->hash_mask_len()); IPvXNet group_masked_addr2(group_addr, rp2->hash_mask_len()); derived_masked_group1 = derived_addr(group_masked_addr1.masked_addr()); derived_masked_group2 = derived_addr(group_masked_addr2.masked_addr()); derived_rp1 = derived_addr(rp1->rp_addr()); derived_rp2 = derived_addr(rp2->rp_addr()); } while (false); uint32_t hash_value1 = RP_HASH_VALUE_DERIVED(derived_masked_group1, derived_rp1); uint32_t hash_value2 = RP_HASH_VALUE_DERIVED(derived_masked_group2, derived_rp2); if (hash_value1 > hash_value2) return (rp1); if (hash_value1 < hash_value2) return (rp2); if (rp1->rp_addr() > rp2->rp_addr()) return (rp1); if (rp1->rp_addr() < rp2->rp_addr()) return (rp2); return (rp1); // XXX: both RPs seem identical } // // Compute and return the XOR value of an address as an 32-bit integer value. // uint32_t RpTable::derived_addr(const IPvX& addr) const { size_t addr_bytelen = addr.addr_bytelen()/sizeof(uint32_t); uint32_t addr_array[sizeof(addr)]; // XXX: give us plenty of extra space uint32_t result = 0; addr.copy_out((uint8_t *)addr_array); for (size_t i = 0; i < addr_bytelen; i++) result ^= addr_array[i]; return (result); } // // Return: a pointer to the RP entry if it can be added, otherwise NULL. // PimRp * RpTable::add_rp(const IPvX& rp_addr, uint8_t rp_priority, const IPvXNet& group_prefix, uint8_t hash_mask_len, PimRp::rp_learned_method_t rp_learned_method) { // // Check if we have already an entry for that RP // list::iterator iter; for (iter = _rp_list.begin(); iter != _rp_list.end(); ++iter) { PimRp *pim_rp = *iter; if (pim_rp->rp_addr() != rp_addr) continue; if (pim_rp->group_prefix() != group_prefix) continue; if (pim_rp->rp_learned_method() != rp_learned_method) { // TODO: XXX: do we allow same RP is installed by different // methods? Maybe no, so here we should print a warning instead // and refuse to add that RP. XLOG_WARNING("Cannot add RP %s for group prefix %s " "and learned method %s: already have same RP with " "learned method %s", cstring(rp_addr), cstring(group_prefix), PimRp::rp_learned_method_str(rp_learned_method).c_str(), pim_rp->rp_learned_method_str().c_str()); return (NULL); } if ((pim_rp->rp_priority() == rp_priority) && (pim_rp->hash_mask_len() == hash_mask_len)) { // Found exactly same entry. Return it. return (pim_rp); } // Found an entry for same RP and group prefix. // Update its priority and hash mask length. pim_rp->set_rp_priority(rp_priority); pim_rp->set_hash_mask_len(hash_mask_len); pim_rp->set_is_updated(true); return (pim_rp); } // // Add a new RP entry // PimRp *new_pim_rp = new PimRp(*this, rp_addr, rp_priority, group_prefix, hash_mask_len, rp_learned_method); _rp_list.push_back(new_pim_rp); new_pim_rp->set_is_updated(true); return (new_pim_rp); } // // Delete an RP entry // Return %XORP_OK on success, otherwise %XORP_ERROR. // int RpTable::delete_rp(const IPvX& rp_addr, const IPvXNet& group_prefix, PimRp::rp_learned_method_t rp_learned_method) { // // Find the RP entry // list::iterator iter; for (iter = _rp_list.begin(); iter != _rp_list.end(); ++iter) { PimRp *pim_rp = *iter; if (pim_rp->rp_addr() != rp_addr) continue; if (pim_rp->group_prefix() != group_prefix) continue; if (pim_rp->rp_learned_method() != rp_learned_method) { // TODO: XXX: do we allow same RP is installed by different // methods? Maybe no, so here we should print a warning instead // and refuse to delete that RP. XLOG_WARNING("Cannot delete RP %s for group prefix %s " "and learned method %s: already have same RP with " "learned method %s", cstring(rp_addr), cstring(group_prefix), PimRp::rp_learned_method_str(rp_learned_method).c_str(), pim_rp->rp_learned_method_str().c_str()); continue; } // // Entry found. Delete it from the list of RPs and, if needed, // put it on the list to process its PimMre and PimMfc entries. // _rp_list.erase(iter); if (pim_rp->pim_mre_wc_list().empty() && pim_rp->pim_mre_sg_list().empty() && pim_rp->pim_mre_sg_rpt_list().empty() && pim_rp->pim_mfc_list().empty() && pim_rp->processing_pim_mre_wc_list().empty() && pim_rp->processing_pim_mre_sg_list().empty() && pim_rp->processing_pim_mre_sg_rpt_list().empty() && pim_rp->processing_pim_mfc_list().empty()) { delete pim_rp; } else { _processing_rp_list.push_back(pim_rp); pim_rp->set_is_updated(true); } return (XORP_OK); } return (XORP_ERROR); } // // Delete all RP entries for a given RP address // Return %XORP_OK on success, otherwise %XORP_ERROR. // int RpTable::delete_all_group_prefixes_rp( const IPvX& rp_addr, PimRp::rp_learned_method_t rp_learned_method) { list > delete_list; list >::iterator delete_list_iter; int ret_value = XORP_OK; // // Find all matching RP entries and add them to the list // list::iterator iter; for (iter = _rp_list.begin(); iter != _rp_list.end(); ++iter) { PimRp *pim_rp = *iter; if (pim_rp->rp_addr() != rp_addr) continue; if (pim_rp->rp_learned_method() == rp_learned_method) { delete_list.push_back(make_pair(pim_rp->rp_addr(), pim_rp->group_prefix())); } } // // Delete all RP entries on the list // for (delete_list_iter = delete_list.begin(); delete_list_iter != delete_list.end(); ++delete_list_iter) { const IPvX& ipvx = delete_list_iter->first; const IPvXNet& ipvxnet = delete_list_iter->second; if (delete_rp(ipvx, ipvxnet, rp_learned_method) != XORP_OK) ret_value = XORP_ERROR; } return (ret_value); } // // Delete all RP entries // Return %XORP_OK on success, otherwise %XORP_ERROR. // int RpTable::delete_all_rps(PimRp::rp_learned_method_t rp_learned_method) { list > delete_list; list >::iterator delete_list_iter; int ret_value = XORP_OK; // // Find all matching RP entries and add them to the list // list::iterator iter; for (iter = _rp_list.begin(); iter != _rp_list.end(); ++iter) { PimRp *pim_rp = *iter; if (pim_rp->rp_learned_method() == rp_learned_method) { delete_list.push_back(make_pair(pim_rp->rp_addr(), pim_rp->group_prefix())); } } // // Delete all RP entries on the list // for (delete_list_iter = delete_list.begin(); delete_list_iter != delete_list.end(); ++delete_list_iter) { const IPvX& ipvx = delete_list_iter->first; const IPvXNet& ipvxnet = delete_list_iter->second; if (delete_rp(ipvx, ipvxnet, rp_learned_method) != XORP_OK) ret_value = XORP_ERROR; } return (ret_value); } // // Apply the RP changes to the PIM multicast routing table // Return true if the processing was saved for later because it was taking too // long time, otherwise return false. // bool RpTable::apply_rp_changes() { bool ret_value = false; list::iterator rp_iter1; // // Mark all RP entries that may have been affected. // E.g., if a shorter-match entry has been updated, // all longer-match entries need update too. // for (rp_iter1 = _rp_list.begin(); rp_iter1 != _rp_list.end(); ++rp_iter1) { list::iterator rp_iter2 = rp_iter1; PimRp *pim_rp1 = *rp_iter1; if (! pim_rp1->is_updated()) continue; for (rp_iter2 = _rp_list.begin(); rp_iter2 != _rp_list.end(); ++rp_iter2) { PimRp *pim_rp2 = *rp_iter2; if (pim_rp2->group_prefix().contains(pim_rp1->group_prefix())) pim_rp2->set_is_updated(true); } } // // Schedule the tasks to take care of the RP change // // First process the entries that may have to change to a new RP for (rp_iter1 = _rp_list.begin(); rp_iter1 != _rp_list.end(); ++rp_iter1) { PimRp *pim_rp = *rp_iter1; PimMre *pim_mre; if (! pim_rp->is_updated()) continue; pim_rp->set_is_updated(false); // XXX: add an (*,*,RP) entry even if it is idle. pim_mre = pim_node().pim_mrt().pim_mre_find(pim_rp->rp_addr(), IPvX::ZERO(family()), PIM_MRE_RP, PIM_MRE_RP); XLOG_ASSERT(pim_mre != NULL); pim_node().pim_mrt().add_task_rp_changed(pim_rp->rp_addr()); ret_value = true; } // Next process the entries that have obsolete, or no RP entry for (rp_iter1 = _processing_rp_list.begin(); rp_iter1 != _processing_rp_list.end(); ++rp_iter1) { PimRp *pim_rp = *rp_iter1; // XXX: note that here we don't consider the pim_rp->is_updated() flag // because all PimRp entries on the processing_rp_list need // consideration. pim_node().pim_mrt().add_task_rp_changed(pim_rp->rp_addr()); ret_value = true; } return (ret_value); } // Used by (*,G), (S,G), (S,G,rpt) void RpTable::add_pim_mre(PimMre *pim_mre) { PimRp *new_pim_rp = pim_mre->pim_rp(); if (! (pim_mre->is_wc() || pim_mre->is_sg() || pim_mre->is_sg_rpt())) return; if (pim_mre->is_sg() || pim_mre->is_sg_rpt()) { if (pim_mre->wc_entry() != NULL) return; // XXX: we don't add (S,G) or (S,G,rpt) that have (*,G) } if (new_pim_rp == NULL) { // // Find the special PimRp entry that contains all (*,G) or (S,G) // or (S,G,rpt) or PimMfc entries that have no RP yet. If not found, // create it. // XXX: that special PimRp entry has RP address of IPvX::ZERO() // XXX: there could be one special PimRp entry for (*,G), (S,G), // (S,G,rpt) and PimMfc entries. // new_pim_rp = find_processing_rp_by_addr(IPvX::ZERO(family())); if (new_pim_rp == NULL) { new_pim_rp = new PimRp(*this, IPvX::ZERO(family()), 0, IPvXNet(IPvX::ZERO(family()), 0), 0, PimRp::RP_LEARNED_METHOD_UNKNOWN); _processing_rp_list.push_back(new_pim_rp); } } XLOG_ASSERT(new_pim_rp != NULL); // Add the PimMre entry to the appropriate list do { if (pim_mre->is_wc()) { new_pim_rp->pim_mre_wc_list().push_back(pim_mre); break; } if (pim_mre->is_sg()) { new_pim_rp->pim_mre_sg_list().push_back(pim_mre); break; } if (pim_mre->is_sg_rpt()) { new_pim_rp->pim_mre_sg_rpt_list().push_back(pim_mre); break; } } while (false); } // Used by PimMfc entries void RpTable::add_pim_mfc(PimMfc *pim_mfc) { PimRp *new_pim_rp = rp_find(pim_mfc->group_addr()); if (new_pim_rp == NULL) { // // Find the special PimRp entry that contains all (*,G) or (S,G) // or (S,G,rpt) or PimMfc entries that have no RP yet. If not found, // create it. // XXX: that special PimRp entry has RP address of IPvX::ZERO() // XXX: there could be one special PimRp entry for (*,G), (S,G), // (S,G,rpt) and PimMfc entries. // new_pim_rp = find_processing_rp_by_addr(IPvX::ZERO(family())); if (new_pim_rp == NULL) { new_pim_rp = new PimRp(*this, IPvX::ZERO(family()), 0, IPvXNet(IPvX::ZERO(family()), 0), 0, PimRp::RP_LEARNED_METHOD_UNKNOWN); _processing_rp_list.push_back(new_pim_rp); } } XLOG_ASSERT(new_pim_rp != NULL); // Add the PimMre entry to the appropriate list do { new_pim_rp->pim_mfc_list().push_back(pim_mfc); break; } while (false); } // // XXX: this method SHOULD be called ONLY by PimMre::set_pim_rp(). // If it is called by another method, then that method MUST // take care of PimMre::set_pim_rp() to set appropriately the "PimRp *_pim_rp" // entry. // Used by (*,G), (S,G), (S,G,rpt) void RpTable::delete_pim_mre(PimMre *pim_mre) { PimRp *old_pim_rp = pim_mre->pim_rp(); if (! (pim_mre->is_wc() || pim_mre->is_sg() || pim_mre->is_sg_rpt())) return; // // Remove pim_mre from the list of its RP (or from the no-RP list) // list::iterator pim_mre_iter; if (old_pim_rp == NULL) { // XXX: find the special PimRp entry that contains // the (*,G) or (S,G) or (S,G,rpt) entries that have no RP. old_pim_rp = find_processing_rp_by_addr(IPvX::ZERO(family())); } if (old_pim_rp != NULL) { do { if (pim_mre->is_wc()) { // // (*,G) entry // // // Try the pim_mre_wc_list() // pim_mre_iter = find(old_pim_rp->pim_mre_wc_list().begin(), old_pim_rp->pim_mre_wc_list().end(), pim_mre); if (pim_mre_iter != old_pim_rp->pim_mre_wc_list().end()) { old_pim_rp->pim_mre_wc_list().erase(pim_mre_iter); break; } // // Try the processing_pim_mre_wc_list() // pim_mre_iter = find( old_pim_rp->processing_pim_mre_wc_list().begin(), old_pim_rp->processing_pim_mre_wc_list().end(), pim_mre); if (pim_mre_iter != old_pim_rp->processing_pim_mre_wc_list().end()) { old_pim_rp->processing_pim_mre_wc_list().erase(pim_mre_iter); break; } break; } if (pim_mre->is_sg()) { // // (S,G) entry // // // Try the pim_mre_sg_list() // pim_mre_iter = find(old_pim_rp->pim_mre_sg_list().begin(), old_pim_rp->pim_mre_sg_list().end(), pim_mre); if (pim_mre_iter != old_pim_rp->pim_mre_sg_list().end()) { old_pim_rp->pim_mre_sg_list().erase(pim_mre_iter); break; } // // Try the processing_pim_mre_sg_list() // pim_mre_iter = find( old_pim_rp->processing_pim_mre_sg_list().begin(), old_pim_rp->processing_pim_mre_sg_list().end(), pim_mre); if (pim_mre_iter != old_pim_rp->processing_pim_mre_sg_list().end()) { old_pim_rp->processing_pim_mre_sg_list().erase(pim_mre_iter); break; } break; } if (pim_mre->is_sg_rpt()) { // // (S,G,rpt) entry // // // Try the pim_mre_sg_rpt_list() // pim_mre_iter = find(old_pim_rp->pim_mre_sg_rpt_list().begin(), old_pim_rp->pim_mre_sg_rpt_list().end(), pim_mre); if (pim_mre_iter != old_pim_rp->pim_mre_sg_rpt_list().end()) { old_pim_rp->pim_mre_sg_rpt_list().erase(pim_mre_iter); break; } // // Try the processing_pim_mre_sg_rpt_list() // pim_mre_iter = find( old_pim_rp->processing_pim_mre_sg_rpt_list().begin(), old_pim_rp->processing_pim_mre_sg_rpt_list().end(), pim_mre); if (pim_mre_iter != old_pim_rp->processing_pim_mre_sg_rpt_list().end()) { old_pim_rp->processing_pim_mre_sg_rpt_list().erase(pim_mre_iter); break; } break; } } while (false); } // XXX: we should not call pim_mre->set_pim_rp(NULL) here, // because it will result in a calling loop. // pim_mre->set_pim_rp(NULL); // If the PimRp entry was pending deletion, and if this was the // lastest PimMre entry, delete the PimRp entry. do { if (old_pim_rp == NULL) break; if ( ! (old_pim_rp->pim_mre_wc_list().empty() && old_pim_rp->pim_mre_sg_list().empty() && old_pim_rp->pim_mre_sg_rpt_list().empty() && old_pim_rp->pim_mfc_list().empty() && old_pim_rp->processing_pim_mre_wc_list().empty() && old_pim_rp->processing_pim_mre_sg_list().empty() && old_pim_rp->processing_pim_mre_sg_rpt_list().empty() && old_pim_rp->processing_pim_mfc_list().empty())) { break; // There are still more entries } list::iterator pim_rp_iter; pim_rp_iter = find(_processing_rp_list.begin(), _processing_rp_list.end(), old_pim_rp); if (pim_rp_iter != _processing_rp_list.end()) { _processing_rp_list.erase(pim_rp_iter); delete old_pim_rp; old_pim_rp = NULL; } } while (false); } // Used by PimMfc void RpTable::delete_pim_mfc(PimMfc *pim_mfc) { const IPvX& rp_addr = pim_mfc->rp_addr(); list::iterator pim_rp_iter; list::iterator pim_mfc_iter; PimRp *old_pim_rp = NULL; // // Remove pim_mfc from the list of its RP (or from the no-RP list) // XXX: because we don't know which PimRp entry has the pim_mfc // on its list, we try all PimRp entries that match the RP address. // do { // // Try to remove from the rp_list() // for (pim_rp_iter = _rp_list.begin(); pim_rp_iter != _rp_list.end(); ++pim_rp_iter) { PimRp *pim_rp = *pim_rp_iter; if (pim_rp->rp_addr() != rp_addr) continue; // Try the pim_mfc_list() pim_mfc_iter = find(pim_rp->pim_mfc_list().begin(), pim_rp->pim_mfc_list().end(), pim_mfc); if (pim_mfc_iter != pim_rp->pim_mfc_list().end()) { pim_rp->pim_mfc_list().erase(pim_mfc_iter); old_pim_rp = pim_rp; break; } // Try the processing_pim_mfc_list() pim_mfc_iter = find(pim_rp->processing_pim_mfc_list().begin(), pim_rp->processing_pim_mfc_list().end(), pim_mfc); if (pim_mfc_iter != pim_rp->processing_pim_mfc_list().end()) { pim_rp->processing_pim_mfc_list().erase(pim_mfc_iter); old_pim_rp = pim_rp; break; } } if (old_pim_rp != NULL) break; // // Try to remove from the processing_rp_list() // for (pim_rp_iter = _processing_rp_list.begin(); pim_rp_iter != _processing_rp_list.end(); ++pim_rp_iter) { PimRp *pim_rp = *pim_rp_iter; if (pim_rp->rp_addr() != rp_addr) continue; pim_mfc_iter = find(pim_rp->pim_mfc_list().begin(), pim_rp->pim_mfc_list().end(), pim_mfc); if (pim_mfc_iter != pim_rp->pim_mfc_list().end()) { pim_rp->pim_mfc_list().erase(pim_mfc_iter); old_pim_rp = pim_rp; break; } // Try the processing_pim_mfc_list() pim_mfc_iter = find(pim_rp->processing_pim_mfc_list().begin(), pim_rp->processing_pim_mfc_list().end(), pim_mfc); if (pim_mfc_iter != pim_rp->processing_pim_mfc_list().end()) { pim_rp->processing_pim_mfc_list().erase(pim_mfc_iter); old_pim_rp = pim_rp; break; } } if (old_pim_rp != NULL) break; } while (false); // If the PimRp entry was pending deletion, and if this was the // lastest PimMfc entry, delete the PimRp entry. do { if (old_pim_rp == NULL) break; if ( ! (old_pim_rp->pim_mre_wc_list().empty() && old_pim_rp->pim_mre_sg_list().empty() && old_pim_rp->pim_mre_sg_rpt_list().empty() && old_pim_rp->pim_mfc_list().empty() && old_pim_rp->processing_pim_mre_wc_list().empty() && old_pim_rp->processing_pim_mre_sg_list().empty() && old_pim_rp->processing_pim_mre_sg_rpt_list().empty() && old_pim_rp->processing_pim_mfc_list().empty())) { break; // There are still more entries } list::iterator pim_rp_iter; pim_rp_iter = find(_processing_rp_list.begin(), _processing_rp_list.end(), old_pim_rp); if (pim_rp_iter != _processing_rp_list.end()) { _processing_rp_list.erase(pim_rp_iter); delete old_pim_rp; old_pim_rp = NULL; } } while (false); } // // Prepare all PimRp entries with RP address of @rp_add to // process their (*,G) PimMre entries. // void RpTable::init_processing_pim_mre_wc(const IPvX& rp_addr) { list::iterator iter; for (iter = _rp_list.begin(); iter != _rp_list.end(); ++iter) { PimRp *pim_rp = *iter; if (pim_rp->rp_addr() == rp_addr) pim_rp->init_processing_pim_mre_wc(); } for (iter = _processing_rp_list.begin(); iter != _processing_rp_list.end(); ++iter) { PimRp *pim_rp = *iter; if (pim_rp->rp_addr() == rp_addr) pim_rp->init_processing_pim_mre_wc(); } } // // Prepare all PimRp entries with RP address of @rp_add to // process their (S,G) PimMre entries. // void RpTable::init_processing_pim_mre_sg(const IPvX& rp_addr) { list::iterator iter; for (iter = _rp_list.begin(); iter != _rp_list.end(); ++iter) { PimRp *pim_rp = *iter; if (pim_rp->rp_addr() == rp_addr) pim_rp->init_processing_pim_mre_sg(); } for (iter = _processing_rp_list.begin(); iter != _processing_rp_list.end(); ++iter) { PimRp *pim_rp = *iter; if (pim_rp->rp_addr() == rp_addr) pim_rp->init_processing_pim_mre_sg(); } } // // Prepare all PimRp entries with RP address of @rp_add to // process their (S,G,rpt) PimMre entries. // void RpTable::init_processing_pim_mre_sg_rpt(const IPvX& rp_addr) { list::iterator iter; for (iter = _rp_list.begin(); iter != _rp_list.end(); ++iter) { PimRp *pim_rp = *iter; if (pim_rp->rp_addr() == rp_addr) pim_rp->init_processing_pim_mre_sg_rpt(); } for (iter = _processing_rp_list.begin(); iter != _processing_rp_list.end(); ++iter) { PimRp *pim_rp = *iter; if (pim_rp->rp_addr() == rp_addr) pim_rp->init_processing_pim_mre_sg_rpt(); } } // // Prepare all PimRp entries with RP address of @rp_add to // process their PimMfc entries. // void RpTable::init_processing_pim_mfc(const IPvX& rp_addr) { list::iterator iter; for (iter = _rp_list.begin(); iter != _rp_list.end(); ++iter) { PimRp *pim_rp = *iter; if (pim_rp->rp_addr() == rp_addr) pim_rp->init_processing_pim_mfc(); } for (iter = _processing_rp_list.begin(); iter != _processing_rp_list.end(); ++iter) { PimRp *pim_rp = *iter; if (pim_rp->rp_addr() == rp_addr) pim_rp->init_processing_pim_mfc(); } } // // Return the first PimRp entry with RP address of @rp_addr // that has pending (*,G) entries to be processed. // PimRp * RpTable::find_processing_pim_mre_wc(const IPvX& rp_addr) { list::iterator iter; for (iter = _rp_list.begin(); iter != _rp_list.end(); ++iter) { PimRp *pim_rp = *iter; if ((pim_rp->rp_addr() == rp_addr) && (! pim_rp->processing_pim_mre_wc_list().empty())) return (pim_rp); } for (iter = _processing_rp_list.begin(); iter != _processing_rp_list.end(); ++iter) { PimRp *pim_rp = *iter; if ((pim_rp->rp_addr() == rp_addr) && (! pim_rp->processing_pim_mre_wc_list().empty())) return (pim_rp); } return (NULL); } // // Return the first PimRp entry with RP address of @rp_addr // that has pending (S,G) entries to be processed. // PimRp * RpTable::find_processing_pim_mre_sg(const IPvX& rp_addr) { list::iterator iter; for (iter = _rp_list.begin(); iter != _rp_list.end(); ++iter) { PimRp *pim_rp = *iter; if ((pim_rp->rp_addr() == rp_addr) && (! pim_rp->processing_pim_mre_sg_list().empty())) return (pim_rp); } for (iter = _processing_rp_list.begin(); iter != _processing_rp_list.end(); ++iter) { PimRp *pim_rp = *iter; if ((pim_rp->rp_addr() == rp_addr) && (! pim_rp->processing_pim_mre_sg_list().empty())) return (pim_rp); } return (NULL); } // // Return the first PimRp entry with RP address of @rp_addr // that has pending (S,G,rpt) entries to be processed. // PimRp * RpTable::find_processing_pim_mre_sg_rpt(const IPvX& rp_addr) { list::iterator iter; for (iter = _rp_list.begin(); iter != _rp_list.end(); ++iter) { PimRp *pim_rp = *iter; if ((pim_rp->rp_addr() == rp_addr) && (! pim_rp->processing_pim_mre_sg_rpt_list().empty())) return (pim_rp); } for (iter = _processing_rp_list.begin(); iter != _processing_rp_list.end(); ++iter) { PimRp *pim_rp = *iter; if ((pim_rp->rp_addr() == rp_addr) && (! pim_rp->processing_pim_mre_sg_rpt_list().empty())) return (pim_rp); } return (NULL); } // // Return the first PimRp entry with RP address of @rp_addr // that has pending PimMfc entries to be processed. // PimRp * RpTable::find_processing_pim_mfc(const IPvX& rp_addr) { list::iterator iter; for (iter = _rp_list.begin(); iter != _rp_list.end(); ++iter) { PimRp *pim_rp = *iter; if ((pim_rp->rp_addr() == rp_addr) && (! pim_rp->processing_pim_mfc_list().empty())) return (pim_rp); } for (iter = _processing_rp_list.begin(); iter != _processing_rp_list.end(); ++iter) { PimRp *pim_rp = *iter; if ((pim_rp->rp_addr() == rp_addr) && (! pim_rp->processing_pim_mfc_list().empty())) return (pim_rp); } return (NULL); } // // Return the first PimRp entry with RP address of @rp_addr // that is on the processing_rp_list // PimRp * RpTable::find_processing_rp_by_addr(const IPvX& rp_addr) { list::iterator iter; for (iter = _processing_rp_list.begin(); iter != _processing_rp_list.end(); ++iter) { PimRp *pim_rp = *iter; if (pim_rp->rp_addr() == rp_addr) return (pim_rp); } return (NULL); } // // Return true if address @rp_addr is still contained in the RpTable, // otherwise return false. // bool RpTable::has_rp_addr(const IPvX& rp_addr) { list::iterator iter; for (iter = _rp_list.begin(); iter != _rp_list.end(); ++iter) { PimRp *pim_rp = *iter; if (pim_rp->rp_addr() == rp_addr) return (true); } for (iter = _processing_rp_list.begin(); iter != _processing_rp_list.end(); ++iter) { PimRp *pim_rp = *iter; if (pim_rp->rp_addr() == rp_addr) return (true); } return (false); } PimRp::PimRp(RpTable& rp_table, const IPvX& rp_addr, uint8_t rp_priority, const IPvXNet& group_prefix, uint8_t hash_mask_len, rp_learned_method_t rp_learned_method) : _rp_table(rp_table), _rp_addr(rp_addr), _rp_priority(rp_priority), _group_prefix(group_prefix), _hash_mask_len(hash_mask_len), _rp_learned_method(rp_learned_method), _is_updated(true), _i_am_rp(_rp_table.pim_node().is_my_addr(rp_addr)) { } PimRp::PimRp(RpTable& rp_table, const PimRp& pim_rp) : _rp_table(rp_table), _rp_addr(pim_rp.rp_addr()), _rp_priority(pim_rp.rp_priority()), _group_prefix(pim_rp.group_prefix()), _hash_mask_len(pim_rp.hash_mask_len()), _rp_learned_method(pim_rp.rp_learned_method()), _is_updated(pim_rp.is_updated()), _i_am_rp(pim_rp.i_am_rp()) { } PimRp::~PimRp() { // Try to remove the (*,*,RP) entry if no such RP anymore, // and the entry is not needed. if (! rp_table().has_rp_addr(rp_addr())) { PimMre *pim_mre; pim_mre = rp_table().pim_node().pim_mrt().pim_mre_find( rp_addr(), IPvX::ZERO(rp_table().family()), PIM_MRE_RP, 0); if (pim_mre != NULL) pim_mre->entry_try_remove(); } } const string PimRp::rp_learned_method_str(rp_learned_method_t rp_learned_method) { switch (rp_learned_method) { case RP_LEARNED_METHOD_AUTORP: return ("AUTORP"); case RP_LEARNED_METHOD_BOOTSTRAP: return ("BOOTSTRAP"); case RP_LEARNED_METHOD_STATIC: return ("STATIC"); default: // Unknown learned method. return ("UNKNOWN"); } return ("UNKNOWN"); } const string PimRp::rp_learned_method_str() const { return (rp_learned_method_str(rp_learned_method())); } void PimRp::init_processing_pim_mre_wc() { _processing_pim_mre_wc_list.splice( _processing_pim_mre_wc_list.end(), _pim_mre_wc_list); } void PimRp::init_processing_pim_mre_sg() { _processing_pim_mre_sg_list.splice( _processing_pim_mre_sg_list.end(), _pim_mre_sg_list); } void PimRp::init_processing_pim_mre_sg_rpt() { _processing_pim_mre_sg_rpt_list.splice( _processing_pim_mre_sg_rpt_list.end(), _pim_mre_sg_rpt_list); } void PimRp::init_processing_pim_mfc() { _processing_pim_mfc_list.splice( _processing_pim_mfc_list.end(), _pim_mfc_list); } xorp/pim/SConscript0000664000076400007640000001011711631505636014465 0ustar greearbgreearb# Copyright (c) 2009-2011 XORP, Inc and Others # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # $XORP$ import os Import('env') env = env.Clone() is_shared = env.has_key('SHAREDLIBS') env.AppendUnique(CPPPATH = [ '#', '$BUILDDIR', ]) env.PrependUnique(LIBPATH = [ '$BUILDDIR/libxorp', '$BUILDDIR/libcomm', '$BUILDDIR/libxipc', '$BUILDDIR/libproto', '$BUILDDIR/libfeaclient', '$BUILDDIR/policy/backend', '$BUILDDIR/policy/common', '$BUILDDIR/xrl/interfaces', '$BUILDDIR/xrl/targets', '$BUILDDIR/mrt', '.' ]) env.AppendUnique(LIBS = [ 'xorp_pim', # The library, not the executable. 'xorp_fea_client', 'xif_rib', 'xif_mld6igmp', 'xif_mfea', 'xif_fea_rawpkt4', 'xif_fea_ifmgr_mirror', 'xif_fea_ifmgr_replicator', 'xif_cli_manager', 'xif_finder_event_notifier', 'xst_fea_ifmgr_mirror', 'xst_pim', 'xorp_mrt', 'xorp_proto', 'xorp_ipc', 'xorp_core', 'xorp_comm' ]) if not is_shared: env.AppendUnique(LIBS = [ "crypto", ]) if not (env.has_key('mingw') and env['mingw']): env.AppendUnique(LIBS = [ "rt", ]) if (env.has_key('mingw') and env['mingw']): env.AppendUnique(LIBS = [ 'ws2_32', 'iphlpapi', 'winmm', ]) if not (env.has_key('disable_ipv6') and env['disable_ipv6']): env.AppendUnique(LIBS = [ 'xif_fea_rawpkt6', ]) env.Replace(RPATH = [ env.Literal(env['xorp_module_rpath']) ]) libxorp_pim_srcs = [ 'pim_bsr.cc', 'pim_config.cc', 'pim_mfc.cc', 'pim_mre.cc', 'pim_mre_assert.cc', 'pim_mre_data.cc', 'pim_mre_join_prune.cc', 'pim_mre_register.cc', 'pim_mre_rpf.cc', 'pim_mre_task.cc', 'pim_mre_track_state.cc', 'pim_mrib_table.cc', 'pim_mrt.cc', 'pim_mrt_mfc.cc', 'pim_mrt_task.cc', 'pim_nbr.cc', 'pim_node.cc', 'pim_node_cli.cc', 'pim_proto_assert.cc', 'pim_proto_bootstrap.cc', 'pim_proto_cand_rp_adv.cc', 'pim_proto_graft.cc', 'pim_proto_graft_ack.cc', 'pim_proto_hello.cc', 'pim_proto_join_prune.cc', 'pim_proto_join_prune_message.cc', 'pim_proto_register.cc', 'pim_proto_register_stop.cc', 'pim_rp.cc', 'pim_scope_zone_table.cc', 'pim_vif.cc', 'xrl_pim_node.cc' ] if is_shared: libxorp_pim = env.SharedLibrary(target = 'libxorp_pim', source = libxorp_pim_srcs, LIBS = '') if env['rtld_origin']: for obj in libxorp_pim: env.AddPostAction(libxorp_pim, env.Symlink(obj.abspath, os.path.join(env['xorp_alias_libdir'], str(obj)))) env.Alias('install', env.InstallLibrary(env['xorp_libdir'], libxorp_pim)) else: libxorp_pim = env.StaticLibrary(target = 'libxorp_pim', source = libxorp_pim_srcs, LIBS = '') pim4srcs = [ 'xorp_pimsm4.cc', ] pim4 = env.Program(target = 'xorp_pimsm4', source = pim4srcs) env.Alias('install', env.InstallProgram(env['xorp_moduledir'], pim4)) if not (env.has_key('disable_ipv6') and env['disable_ipv6']): pim6srcs = [ 'xorp_pimsm6.cc', ] pim6 = env.Program(target = 'xorp_pimsm6', source = pim6srcs) env.Alias('install', env.InstallProgram(env['xorp_moduledir'], pim6)) Default(pim4, pim6) else: Default(pim4) xorp/pim/pim_mre_data.cc0000664000076400007640000001206011635757530015410 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // PIM Multicast Routing Entry data handling // #include "pim_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvx.hh" #include "pim_proto.h" #include "pim_mfc.hh" #include "pim_mre.hh" #include "pim_node.hh" #include "pim_vif.hh" // Note: applies only for (S,G) void PimMre::update_sptbit_sg(uint32_t iif_vif_index) { PimMre *pim_mre_wc = wc_entry(); PimNbr *pim_nbr_rpfp_nbr_wc = NULL; if (iif_vif_index == Vif::VIF_INDEX_INVALID) return; if (! is_sg()) return; if (mrib_s() == NULL) return; if (pim_mre_wc != NULL) pim_nbr_rpfp_nbr_wc = pim_mre_wc->rpfp_nbr_wc(); if ((iif_vif_index == rpf_interface_s()) && is_join_desired_sg() && (is_directly_connected_s() || (rpf_interface_s() != rpf_interface_rp()) || (inherited_olist_sg_rpt().none()) || ((rpfp_nbr_sg() == pim_nbr_rpfp_nbr_wc) && (rpfp_nbr_sg() != NULL)) || is_i_am_assert_loser_state(iif_vif_index))) { set_spt(true); } } // // Return true if monitoring switch to SPT is desired, otherwise false. // Note: this implements part of the CheckSwitchToSpt(S,G) macro // (i.e., check_switch_to_spt_sg()). // Note: applies for (*,G), (S,G), (S,G,rpt) // bool PimMre::is_monitoring_switch_to_spt_desired_sg(const PimMre *pim_mre_sg) const { Mifset mifs; // // The last-hop router SPT switch // if (! (is_wc() || is_sg() || is_sg_rpt())) return (false); mifs = pim_include_wc(); if (pim_mre_sg != NULL) { mifs &= ~pim_mre_sg->pim_exclude_sg(); mifs |= pim_mre_sg->pim_include_sg(); } return (mifs.any()); } // Policy-defined SPT switch. // Note: applies for all entries bool PimMre::is_switch_to_spt_desired_sg(uint32_t measured_interval_sec, uint32_t measured_bytes) const { if (! pim_node()->is_switch_to_spt_enabled().get()) return (false); // SPT-switch disabled // // SPT-switch enabled // // Test if the switch was desired already if (was_switch_to_spt_desired_sg()) return (true); // // Test whether the number of forwarded bytes is equal or above // the threshold value, and this is within the boundaries of the // pre-defined interval. // if ((measured_bytes >= pim_node()->switch_to_spt_threshold_bytes().get()) && (measured_interval_sec <= pim_node()->switch_to_spt_threshold_interval_sec().get())) { return (true); } return (false); } // Return true if switch to SPT is desired, otherwise false. // Note: if switch to SPT is desired, then the (S,G) Keepalive Timer is // always restarted. // Note: in theory applies for all entries, but in practice it could // be true only for (*,G), (S,G), (S,G,rpt). bool PimMre::check_switch_to_spt_sg(const IPvX& src, const IPvX& dst, PimMre*& pim_mre_sg, uint32_t measured_interval_sec, uint32_t measured_bytes) { // // XXX: Part of the CheckSwitchToSpt(S,G) macro // is implemented by is_monitoring_switch_to_spt_desired_sg() // if (is_monitoring_switch_to_spt_desired_sg(pim_mre_sg) && is_switch_to_spt_desired_sg(measured_interval_sec, measured_bytes)) { // restart KeepaliveTimer(S,G); if (pim_mre_sg == NULL) { // XXX: create the (S,G) entry pim_mre_sg = pim_node()->pim_mrt().pim_mre_find(src, dst, PIM_MRE_SG, PIM_MRE_SG); } pim_mre_sg->start_keepalive_timer(); pim_mre_sg->set_switch_to_spt_desired_sg(true); return (true); } return (false); } // Note: applies only for (S,G) void PimMre::set_switch_to_spt_desired_sg(bool v) { if (! is_sg()) return; bool old_value; if (_flags & PIM_MRE_SWITCH_TO_SPT_DESIRED) old_value = true; else old_value = false; if (old_value == v) return; // Nothing changed if (v) _flags |= PIM_MRE_SWITCH_TO_SPT_DESIRED; else _flags &= ~PIM_MRE_SWITCH_TO_SPT_DESIRED; pim_mrt()->add_task_was_switch_to_spt_desired_sg(source_addr(), group_addr()); } // Note: applies only for (S,G) bool PimMre::was_switch_to_spt_desired_sg() const { if (! is_sg()) return (false); if (_flags & PIM_MRE_SWITCH_TO_SPT_DESIRED) return (true); else return (false); } xorp/pim/pim_mre_track_state.hh0000664000076400007640000010671211634760770017024 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __PIM_PIM_MRE_TRACK_STATE_HH__ #define __PIM_PIM_MRE_TRACK_STATE_HH__ // // PIM Multicast Routing Entry state tracking definitions. // #include "pim_mre.hh" class PimMfc; class PimMreAction; class PimMrt; class PimNbr; // State tracking for PIM-specific Multicast Routing Entry class PimMreTrackState { public: PimMreTrackState(PimMrt* pim_mrt); // General info: PimNode, PimMrt, family, etc. PimNode* pim_node() const; PimMrt* pim_mrt() const { return _pim_mrt; } int family() const; void print_actions_name() const; void print_actions_num() const; // // The input state // enum input_state_t { INPUT_STATE_RP_CHANGED = 0, // 0 INPUT_STATE_MRIB_RP_CHANGED, // 1 INPUT_STATE_MRIB_S_CHANGED, // 2 INPUT_STATE_NBR_MRIB_NEXT_HOP_RP_CHANGED, // 3 INPUT_STATE_NBR_MRIB_NEXT_HOP_RP_GEN_ID_CHANGED, // 4 INPUT_STATE_NBR_MRIB_NEXT_HOP_RP_G_CHANGED, // 5 INPUT_STATE_NBR_MRIB_NEXT_HOP_S_CHANGED, // 6 INPUT_STATE_RPFP_NBR_WC_CHANGED, // 7 INPUT_STATE_RPFP_NBR_WC_GEN_ID_CHANGED, // 8 INPUT_STATE_RPFP_NBR_SG_CHANGED, // 9 INPUT_STATE_RPFP_NBR_SG_GEN_ID_CHANGED, // 10 INPUT_STATE_RPFP_NBR_SG_RPT_CHANGED, // 11 INPUT_STATE_RECEIVE_JOIN_RP, // 12 INPUT_STATE_RECEIVE_JOIN_WC, // 13 INPUT_STATE_RECEIVE_JOIN_SG, // 14 INPUT_STATE_RECEIVE_JOIN_SG_RPT, // 15 INPUT_STATE_RECEIVE_PRUNE_RP, // 16 INPUT_STATE_RECEIVE_PRUNE_WC, // 17 INPUT_STATE_RECEIVE_PRUNE_SG, // 18 INPUT_STATE_RECEIVE_PRUNE_SG_RPT, // 19 INPUT_STATE_RECEIVE_END_OF_MESSAGE_SG_RPT, // 20 INPUT_STATE_SEE_PRUNE_WC, // 21 INPUT_STATE_DOWNSTREAM_JP_STATE_RP, // 22 INPUT_STATE_DOWNSTREAM_JP_STATE_WC, // 23 INPUT_STATE_DOWNSTREAM_JP_STATE_SG, // 24 INPUT_STATE_DOWNSTREAM_JP_STATE_SG_RPT, // 25 INPUT_STATE_UPSTREAM_JP_STATE_SG, // 26 INPUT_STATE_LOCAL_RECEIVER_INCLUDE_WC, // 27 INPUT_STATE_LOCAL_RECEIVER_INCLUDE_SG, // 28 INPUT_STATE_LOCAL_RECEIVER_EXCLUDE_SG, // 29 INPUT_STATE_ASSERT_STATE_WC, // 30 INPUT_STATE_ASSERT_STATE_SG, // 31 INPUT_STATE_ASSERT_WINNER_NBR_WC_GEN_ID_CHANGED, // 32 INPUT_STATE_ASSERT_WINNER_NBR_SG_GEN_ID_CHANGED, // 33 INPUT_STATE_ASSERT_WINNER_NBR_WC_NLT_EXPIRED, // 34 INPUT_STATE_ASSERT_WINNER_NBR_SG_NLT_EXPIRED, // 35 INPUT_STATE_ASSERT_RPF_INTERFACE_WC_CHANGED, // 36 INPUT_STATE_ASSERT_RPF_INTERFACE_SG_CHANGED, // 37 INPUT_STATE_I_AM_DR, // 38 INPUT_STATE_MY_IP_ADDRESS, // 39 INPUT_STATE_MY_IP_SUBNET_ADDRESS, // 40 INPUT_STATE_SPT_SWITCH_THRESHOLD_CHANGED_MFC, // 41 INPUT_STATE_WAS_SWITCH_TO_SPT_DESIRED_SG, // 42 INPUT_STATE_KEEPALIVE_TIMER_SG, // 43 INPUT_STATE_SPTBIT_SG, // 44 INPUT_STATE_IN_START_VIF, // 45 INPUT_STATE_IN_STOP_VIF, // 46 INPUT_STATE_IN_ADD_PIM_MRE_RP, // 47 INPUT_STATE_IN_ADD_PIM_MRE_WC, // 48 INPUT_STATE_IN_ADD_PIM_MRE_SG, // 49 INPUT_STATE_IN_ADD_PIM_MRE_SG_RPT, // 50 INPUT_STATE_IN_REMOVE_PIM_MRE_RP, // 51 INPUT_STATE_IN_REMOVE_PIM_MRE_WC, // 52 INPUT_STATE_IN_REMOVE_PIM_MRE_SG, // 53 INPUT_STATE_IN_REMOVE_PIM_MRE_SG_RPT, // 54 INPUT_STATE_IN_REMOVE_PIM_MFC, // 55 // // XXX: INPUT_STATE_IN_REMOVE_MISC is a special input state // without output actions that is used only to remove various // entries when the event-handling task itself is deleted. // INPUT_STATE_IN_REMOVE_MISC, // 55 INPUT_STATE_MAX }; // // The output state // enum output_state_t { OUTPUT_STATE_RP_WC = 0, // 0 OUTPUT_STATE_RP_SG, // 1 OUTPUT_STATE_RP_SG_RPT, // 2 OUTPUT_STATE_RP_MFC, // 3 OUTPUT_STATE_MRIB_RP_RP, // 4 OUTPUT_STATE_MRIB_RP_WC, // 5 OUTPUT_STATE_MRIB_RP_SG, // 6 OUTPUT_STATE_MRIB_RP_SG_RPT, // 7 OUTPUT_STATE_MRIB_S_SG, // 8 OUTPUT_STATE_MRIB_S_SG_RPT, // 9 OUTPUT_STATE_IS_JOIN_DESIRED_RP, // 10 OUTPUT_STATE_IS_JOIN_DESIRED_WC, // 11 OUTPUT_STATE_IS_JOIN_DESIRED_SG, // 12 OUTPUT_STATE_IS_PRUNE_DESIRED_SG_RPT, // 13 OUTPUT_STATE_IS_PRUNE_DESIRED_SG_RPT_SG, // 14 OUTPUT_STATE_IS_RPT_JOIN_DESIRED_G, // 15 OUTPUT_STATE_INHERITED_OLIST_SG_RPT, // 16 OUTPUT_STATE_IIF_OLIST_MFC, // 17 OUTPUT_STATE_MONITORING_SWITCH_TO_SPT_DESIRED_MFC, // 18 OUTPUT_STATE_SPT_SWITCH_THRESHOLD_CHANGED_MFC, // 19 OUTPUT_STATE_IS_DIRECTLY_CONNECTED_SG, // 20 OUTPUT_STATE_IS_COULD_REGISTER_SG, // 21 OUTPUT_STATE_ASSERT_TRACKING_DESIRED_SG, // 22 OUTPUT_STATE_ASSERT_TRACKING_DESIRED_WC, // 23 OUTPUT_STATE_COULD_ASSERT_SG, // 24 OUTPUT_STATE_COULD_ASSERT_WC, // 25 OUTPUT_STATE_MY_ASSERT_METRIC_SG, // 26 OUTPUT_STATE_MY_ASSERT_METRIC_WC, // 27 OUTPUT_STATE_ASSERT_RPF_INTERFACE_SG, // 28 OUTPUT_STATE_ASSERT_RPF_INTERFACE_WC, // 29 OUTPUT_STATE_ASSERT_RECEIVE_JOIN_SG, // 30 OUTPUT_STATE_ASSERT_RECEIVE_JOIN_WC, // 31 OUTPUT_STATE_ASSERT_WINNER_NBR_SG_GEN_ID, // 32 OUTPUT_STATE_ASSERT_WINNER_NBR_WC_GEN_ID, // 33 OUTPUT_STATE_ASSERT_WINNER_NBR_SG_NLT, // 34 OUTPUT_STATE_ASSERT_WINNER_NBR_WC_NLT, // 35 OUTPUT_STATE_RECEIVE_JOIN_WC_BY_SG_RPT, // 36 OUTPUT_STATE_RECEIVE_END_OF_MESSAGE_SG_RPT, // 37 OUTPUT_STATE_SG_SEE_PRUNE_WC, // 38 OUTPUT_STATE_RPFP_NBR_WC_ASSERT, // 39 OUTPUT_STATE_RPFP_NBR_WC_NOT_ASSERT, // 40 OUTPUT_STATE_RPFP_NBR_WC_GEN_ID, // 41 OUTPUT_STATE_RPFP_NBR_SG_ASSERT, // 42 OUTPUT_STATE_RPFP_NBR_SG_NOT_ASSERT, // 43 OUTPUT_STATE_RPFP_NBR_SG_GEN_ID, // 44 OUTPUT_STATE_RPFP_NBR_SG_RPT, // 45 OUTPUT_STATE_RPFP_NBR_SG_RPT_SG, // 46 OUTPUT_STATE_NBR_MRIB_NEXT_HOP_RP_RP, // 47 OUTPUT_STATE_NBR_MRIB_NEXT_HOP_RP_WC, // 48 OUTPUT_STATE_NBR_MRIB_NEXT_HOP_RP_GEN_ID, // 49 OUTPUT_STATE_NBR_MRIB_NEXT_HOP_S, // 50 OUTPUT_STATE_OUT_START_VIF_RP, // 51 OUTPUT_STATE_OUT_START_VIF_WC, // 52 OUTPUT_STATE_OUT_START_VIF_SG, // 53 OUTPUT_STATE_OUT_START_VIF_SG_RPT, // 54 OUTPUT_STATE_OUT_STOP_VIF_RP, // 55 OUTPUT_STATE_OUT_STOP_VIF_WC, // 56 OUTPUT_STATE_OUT_STOP_VIF_SG, // 57 OUTPUT_STATE_OUT_STOP_VIF_SG_RPT, // 58 OUTPUT_STATE_OUT_ADD_PIM_MRE_RP_ENTRY_RP, // 59 OUTPUT_STATE_OUT_ADD_PIM_MRE_RP_ENTRY_WC, // 60 OUTPUT_STATE_OUT_ADD_PIM_MRE_RP_ENTRY_SG, // 61 OUTPUT_STATE_OUT_ADD_PIM_MRE_RP_ENTRY_SG_RPT, // 62 OUTPUT_STATE_OUT_ADD_PIM_MRE_WC_ENTRY_WC, // 63 OUTPUT_STATE_OUT_ADD_PIM_MRE_WC_ENTRY_SG, // 64 OUTPUT_STATE_OUT_ADD_PIM_MRE_WC_ENTRY_SG_RPT, // 65 OUTPUT_STATE_OUT_ADD_PIM_MRE_SG_ENTRY_SG, // 66 OUTPUT_STATE_OUT_ADD_PIM_MRE_SG_ENTRY_SG_RPT, // 67 OUTPUT_STATE_OUT_ADD_PIM_MRE_SG_RPT_ENTRY_SG, // 68 OUTPUT_STATE_OUT_ADD_PIM_MRE_SG_RPT_ENTRY_SG_RPT, // 69 OUTPUT_STATE_OUT_REMOVE_PIM_MRE_RP_ENTRY_RP, // 70 OUTPUT_STATE_OUT_REMOVE_PIM_MRE_RP_ENTRY_WC, // 71 OUTPUT_STATE_OUT_REMOVE_PIM_MRE_RP_ENTRY_SG, // 72 OUTPUT_STATE_OUT_REMOVE_PIM_MRE_RP_ENTRY_SG_RPT, // 73 OUTPUT_STATE_OUT_REMOVE_PIM_MRE_WC_ENTRY_WC, // 74 OUTPUT_STATE_OUT_REMOVE_PIM_MRE_WC_ENTRY_SG, // 75 OUTPUT_STATE_OUT_REMOVE_PIM_MRE_WC_ENTRY_SG_RPT, // 76 OUTPUT_STATE_OUT_REMOVE_PIM_MRE_SG_ENTRY_SG, // 77 OUTPUT_STATE_OUT_REMOVE_PIM_MRE_SG_ENTRY_SG_RPT, // 78 OUTPUT_STATE_OUT_REMOVE_PIM_MRE_SG_RPT_ENTRY_SG, // 79 OUTPUT_STATE_OUT_REMOVE_PIM_MRE_SG_RPT_ENTRY_SG_RPT, // 80 OUTPUT_STATE_OUT_REMOVE_PIM_MFC_ENTRY_MFC, // 81 OUTPUT_STATE_UPDATE_SPTBIT_MFC, // 82 OUTPUT_STATE_SET_KEEPALIVE_TIMER_SG, // 83 OUTPUT_STATE_MAX }; // // The input state methods // void input_state_rp_changed(list action_list); void input_state_mrib_rp_changed(list action_list); void input_state_mrib_s_changed(list action_list); void input_state_nbr_mrib_next_hop_rp_changed(list action_list); void input_state_nbr_mrib_next_hop_rp_gen_id_changed(list action_list); void input_state_nbr_mrib_next_hop_rp_g_changed(list action_list); void input_state_nbr_mrib_next_hop_s_changed(list action_list); void input_state_rpfp_nbr_wc_changed(list action_list); void input_state_rpfp_nbr_wc_gen_id_changed(list action_list); void input_state_rpfp_nbr_sg_changed(list action_list); void input_state_rpfp_nbr_sg_gen_id_changed(list action_list); void input_state_rpfp_nbr_sg_rpt_changed(list action_list); void input_state_receive_join_rp(list action_list); void input_state_receive_join_wc(list action_list); void input_state_receive_join_sg(list action_list); void input_state_receive_join_sg_rpt(list action_list); void input_state_receive_prune_rp(list action_list); void input_state_receive_prune_wc(list action_list); void input_state_receive_prune_sg(list action_list); void input_state_receive_prune_sg_rpt(list action_list); void input_state_receive_end_of_message_sg_rpt(list action_list); void input_state_see_prune_wc(list action_list); void input_state_downstream_jp_state_rp(list action_list); void input_state_downstream_jp_state_wc(list action_list); void input_state_downstream_jp_state_sg(list action_list); void input_state_downstream_jp_state_sg_rpt(list action_list); void input_state_upstream_jp_state_sg(list action_list); void input_state_local_receiver_include_wc(list action_list); void input_state_local_receiver_include_sg(list action_list); void input_state_local_receiver_exclude_sg(list action_list); void input_state_assert_state_wc(list action_list); void input_state_assert_state_sg(list action_list); void input_state_assert_winner_nbr_wc_gen_id_changed(list action_list); void input_state_assert_winner_nbr_sg_gen_id_changed(list action_list); void input_state_assert_winner_nbr_wc_nlt_expired(list action_list); void input_state_assert_winner_nbr_sg_nlt_expired(list action_list); void input_state_assert_rpf_interface_wc_changed(list action_list); void input_state_assert_rpf_interface_sg_changed(list action_list); void input_state_i_am_dr(list action_list); void input_state_my_ip_address(list action_list); void input_state_my_ip_subnet_address(list action_list); void input_state_spt_switch_threshold_changed_mfc(list action_list); void input_state_was_switch_to_spt_desired_sg(list action_list); void input_state_keepalive_timer_sg(list action_list); void input_state_sptbit_sg(list action_list); void input_state_in_start_vif(list action_list); void input_state_in_stop_vif(list action_list); void input_state_in_add_pim_mre_rp(list action_list); void input_state_in_add_pim_mre_wc(list action_list); void input_state_in_add_pim_mre_sg(list action_list); void input_state_in_add_pim_mre_sg_rpt(list action_list); void input_state_in_remove_pim_mre_rp(list action_list); void input_state_in_remove_pim_mre_wc(list action_list); void input_state_in_remove_pim_mre_sg(list action_list); void input_state_in_remove_pim_mre_sg_rpt(list action_list); void input_state_in_remove_pim_mfc(list action_list); // // The output state methods // list output_state_rp_wc(list action_list); list output_state_rp_sg(list action_list); list output_state_rp_sg_rpt(list action_list); list output_state_rp_mfc(list action_list); list output_state_mrib_rp_rp(list action_list); list output_state_mrib_rp_wc(list action_list); list output_state_mrib_rp_sg(list action_list); list output_state_mrib_rp_sg_rpt(list action_list); list output_state_mrib_s_sg(list action_list); list output_state_mrib_s_sg_rpt(list action_list); list output_state_is_join_desired_rp(list action_list); list output_state_is_join_desired_wc(list action_list); list output_state_is_join_desired_sg(list action_list); list output_state_is_prune_desired_sg_rpt(list action_list); list output_state_is_prune_desired_sg_rpt_sg(list action_list); list output_state_is_rpt_join_desired_g(list action_list); list output_state_inherited_olist_sg_rpt(list action_list); list output_state_iif_olist_mfc(list action_list); list output_state_monitoring_switch_to_spt_desired_mfc(list action_list); list output_state_spt_switch_threshold_changed_mfc(list action_list); list output_state_is_directly_connected_sg(list action_list); list output_state_is_could_register_sg(list action_list); list output_state_assert_tracking_desired_sg(list action_list); list output_state_assert_tracking_desired_wc(list action_list); list output_state_could_assert_sg(list action_list); list output_state_could_assert_wc(list action_list); list output_state_my_assert_metric_sg(list action_list); list output_state_my_assert_metric_wc(list action_list); list output_state_assert_rpf_interface_sg(list action_list); list output_state_assert_rpf_interface_wc(list action_list); list output_state_assert_receive_join_sg(list action_list); list output_state_assert_receive_join_wc(list action_list); list output_state_assert_winner_nbr_sg_gen_id(list action_list); list output_state_assert_winner_nbr_wc_gen_id(list action_list); list output_state_assert_winner_nbr_sg_nlt(list action_list); list output_state_assert_winner_nbr_wc_nlt(list action_list); list output_state_receive_join_wc_by_sg_rpt(list action_list); list output_state_receive_end_of_message_sg_rpt(list action_list); list output_state_sg_see_prune_wc(list action_list); list output_state_rpfp_nbr_wc_assert(list action_list); list output_state_rpfp_nbr_wc_not_assert(list action_list); list output_state_rpfp_nbr_wc_gen_id(list action_list); list output_state_rpfp_nbr_sg_assert(list action_list); list output_state_rpfp_nbr_sg_not_assert(list action_list); list output_state_rpfp_nbr_sg_gen_id(list action_list); list output_state_rpfp_nbr_sg_rpt(list action_list); list output_state_rpfp_nbr_sg_rpt_sg(list action_list); list output_state_nbr_mrib_next_hop_rp_rp(list action_list); list output_state_nbr_mrib_next_hop_rp_wc(list action_list); list output_state_nbr_mrib_next_hop_rp_gen_id(list action_list); list output_state_nbr_mrib_next_hop_s(list action_list); list output_state_out_start_vif_rp(list action_list); list output_state_out_start_vif_wc(list action_list); list output_state_out_start_vif_sg(list action_list); list output_state_out_start_vif_sg_rpt(list action_list); list output_state_out_stop_vif_rp(list action_list); list output_state_out_stop_vif_wc(list action_list); list output_state_out_stop_vif_sg(list action_list); list output_state_out_stop_vif_sg_rpt(list action_list); list output_state_out_add_pim_mre_rp_entry_rp(list action_list); list output_state_out_add_pim_mre_rp_entry_wc(list action_list); list output_state_out_add_pim_mre_rp_entry_sg(list action_list); list output_state_out_add_pim_mre_rp_entry_sg_rpt(list action_list); list output_state_out_add_pim_mre_wc_entry_wc(list action_list); list output_state_out_add_pim_mre_wc_entry_sg(list action_list); list output_state_out_add_pim_mre_wc_entry_sg_rpt(list action_list); list output_state_out_add_pim_mre_sg_entry_sg(list action_list); list output_state_out_add_pim_mre_sg_entry_sg_rpt(list action_list); list output_state_out_add_pim_mre_sg_rpt_entry_sg(list action_list); list output_state_out_add_pim_mre_sg_rpt_entry_sg_rpt(list action_list); list output_state_out_remove_pim_mre_rp_entry_rp(list action_list); list output_state_out_remove_pim_mre_rp_entry_wc(list action_list); list output_state_out_remove_pim_mre_rp_entry_sg(list action_list); list output_state_out_remove_pim_mre_rp_entry_sg_rpt(list action_list); list output_state_out_remove_pim_mre_wc_entry_wc(list action_list); list output_state_out_remove_pim_mre_wc_entry_sg(list action_list); list output_state_out_remove_pim_mre_wc_entry_sg_rpt(list action_list); list output_state_out_remove_pim_mre_sg_entry_sg(list action_list); list output_state_out_remove_pim_mre_sg_entry_sg_rpt(list action_list); list output_state_out_remove_pim_mre_sg_rpt_entry_sg(list action_list); list output_state_out_remove_pim_mre_sg_rpt_entry_sg_rpt(list action_list); list output_state_out_remove_pim_mfc_entry_mfc(list action_list); list output_state_update_sptbit_mfc(list action_list); list output_state_set_keepalive_timer_sg(list action_list); // // The output actions // const list& output_action_rp(input_state_t input_state) const { return (_output_action_rp[input_state]); } const list& output_action_wc(input_state_t input_state) const { return (_output_action_wc[input_state]); } const list& output_action_sg_sg_rpt(input_state_t input_state) const { return (_output_action_sg_sg_rpt[input_state]); } const list& output_action_mfc(input_state_t input_state) const { return (_output_action_mfc[input_state]); } // // The remove state methods // list remove_state(list action_list); list remove_state_rpfp_nbr_wc_not_assert_changed(list action_list); list remove_state_rpfp_nbr_sg_not_assert_changed(list action_list); private: // // Methods related to actions // int add_action_list(input_state_t input_state, list action_list); int add_action(input_state_t input_state, const PimMreAction& action); bool can_add_action_to_list(const list& action_list, const PimMreAction& action) const; list remove_action_from_list(list action_list, PimMreAction keep_action, PimMreAction remove_action); // The list of the (*,*,RP) actions list _output_action_rp[INPUT_STATE_MAX]; // The list of the (*,G) actions list _output_action_wc[INPUT_STATE_MAX]; // The list of the (S,G) and (S,G,rpt) actions (in order of appearance) list _output_action_sg_sg_rpt[INPUT_STATE_MAX]; // The list of the MFC actions list _output_action_mfc[INPUT_STATE_MAX]; // The list of all actions: // - The (*,*,RP) actions are first. // - The (*,G) actions follow the (*,*,RP) actions. // - The (S,G) and (S,G,rpt) actions (in order of appearance) // follow the (*,G) actions. list _output_action[INPUT_STATE_MAX]; class ActionLists { public: void clear(); void add_action_list(list action_list); list compute_action_list(); private: PimMreAction pop_next_action(); bool is_head_only_action(const PimMreAction& action) const; vector > _action_list_vector; }; ActionLists _action_lists[INPUT_STATE_MAX]; // // The track state methods // // The RP entry void track_state_rp(list action_list); void track_state_rp_wc(list action_list); void track_state_rp_sg(list action_list); void track_state_rp_sg_rpt(list action_list); void track_state_rp_mfc(list action_list); void track_state_mrib_rp_rp(list action_list); void track_state_mrib_rp_wc(list action_list); void track_state_mrib_rp_sg(list action_list); void track_state_mrib_rp_sg_rpt(list action_list); void track_state_mrib_s_sg(list action_list); void track_state_mrib_s_sg_rpt(list action_list); // MRIB info void track_state_mrib_rp(list action_list); void track_state_mrib_s(list action_list); void track_state_rpf_interface_rp(list action_list); void track_state_rpf_interface_s(list action_list); // RPF neighbor info void track_state_nbr_mrib_next_hop_rp_rp(list action_list); void track_state_nbr_mrib_next_hop_rp_wc(list action_list); void track_state_nbr_mrib_next_hop_rp_gen_id(list action_list); void track_state_nbr_mrib_next_hop_rp_g(list action_list); void track_state_nbr_mrib_next_hop_s(list action_list); void track_state_mrib_pref_metric_s(list action_list); void track_state_mrib_pref_metric_rp(list action_list); // JOIN/PRUNE info void track_state_receive_join_rp(list action_list); void track_state_receive_join_wc(list action_list); void track_state_receive_join_sg(list action_list); void track_state_receive_join_sg_rpt(list action_list); void track_state_receive_prune_rp(list action_list); void track_state_receive_prune_wc(list action_list); void track_state_receive_prune_sg(list action_list); void track_state_receive_prune_sg_rpt(list action_list); void track_state_receive_end_of_message_sg_rpt(list action_list); void track_state_sg_see_prune_wc(list action_list); // J/P (downstream) state (per interface) void track_state_downstream_jp_state_rp(list action_list); void track_state_downstream_jp_state_wc(list action_list); void track_state_downstream_jp_state_sg(list action_list); void track_state_downstream_jp_state_sg_rpt(list action_list); // J/P (upstream) state void track_state_upstream_jp_state_sg(list action_list); // Local receivers info void track_state_local_receiver_include_wc(list action_list); void track_state_local_receiver_include_sg(list action_list); void track_state_local_receiver_exclude_sg(list action_list); void track_state_assert_state_wc(list action_list); void track_state_assert_state_sg(list action_list); // MISC. info void track_state_i_am_dr(list action_list); void track_state_my_ip_address(list action_list); void track_state_my_ip_subnet_address(list action_list); void track_state_monitoring_switch_to_spt_desired_mfc(list action_list); void track_state_monitoring_switch_to_spt_desired_sg(list action_list); void track_state_spt_switch_threshold_changed_mfc(list action_list); void track_state_was_switch_to_spt_desired_sg(list action_list); // MISC. timers void track_state_keepalive_timer_sg(list action_list); // J/P state recomputation void track_state_immediate_olist_rp(list action_list); void track_state_immediate_olist_wc(list action_list); void track_state_immediate_olist_sg(list action_list); void track_state_inherited_olist_sg_rpt(list action_list); void track_state_inherited_olist_sg(list action_list); void track_state_iif_olist_mfc(list action_list); void track_state_pim_include_wc(list action_list); void track_state_pim_include_sg(list action_list); void track_state_pim_exclude_sg(list action_list); void track_state_joins_rp(list action_list); void track_state_joins_wc(list action_list); void track_state_joins_sg(list action_list); void track_state_prunes_sg_rpt(list action_list); void track_state_rpfp_nbr_wc(list action_list); void track_state_rpfp_nbr_wc_assert(list action_list); void track_state_rpfp_nbr_wc_not_assert(list action_list); void track_state_rpfp_nbr_wc_gen_id(list action_list); void track_state_rpfp_nbr_sg(list action_list); void track_state_rpfp_nbr_sg_assert(list action_list); void track_state_rpfp_nbr_sg_not_assert(list action_list); void track_state_rpfp_nbr_sg_gen_id(list action_list); void track_state_rpfp_nbr_sg_rpt(list action_list); void track_state_rpfp_nbr_sg_rpt_sg(list action_list); // Data void track_state_sptbit_sg(list action_list); void track_state_is_directly_connected_sg(list action_list); void track_state_is_could_register_sg(list action_list); void track_state_is_join_desired_rp(list action_list); void track_state_is_join_desired_wc(list action_list); void track_state_is_join_desired_sg(list action_list); void track_state_is_prune_desired_sg_rpt(list action_list); void track_state_is_prune_desired_sg_rpt_sg(list action_list); void track_state_is_rpt_join_desired_g(list action_list); void track_state_could_assert_sg(list action_list); void track_state_assert_tracking_desired_sg(list action_list); void track_state_could_assert_wc(list action_list); void track_state_assert_tracking_desired_wc(list action_list); void track_state_my_assert_metric_sg(list action_list); void track_state_my_assert_metric_wc(list action_list); void track_state_spt_assert_metric(list action_list); void track_state_rpt_assert_metric(list action_list); void track_state_lost_assert_sg_rpt(list action_list); void track_state_lost_assert_sg(list action_list); void track_state_lost_assert_wc(list action_list); void track_state_assert_rpf_interface_sg(list action_list); void track_state_assert_rpf_interface_wc(list action_list); void track_state_assert_receive_join_sg(list action_list); void track_state_assert_receive_join_wc(list action_list); void track_state_assert_winner_nbr_sg_gen_id(list action_list); void track_state_assert_winner_nbr_wc_gen_id(list action_list); void track_state_assert_winner_nbr_sg_nlt(list action_list); void track_state_assert_winner_nbr_wc_nlt(list action_list); void track_state_receive_join_wc_by_sg_rpt(list action_list); void track_state_i_am_assert_winner_sg(list action_list); void track_state_i_am_assert_winner_wc(list action_list); void track_state_i_am_assert_loser_sg(list action_list); void track_state_i_am_assert_loser_wc(list action_list); void track_state_assert_winner_sg(list action_list); void track_state_assert_winner_wc(list action_list); void track_state_assert_winner_metric_sg(list action_list); void track_state_assert_winner_metric_wc(list action_list); void track_state_assert_winner_metric_is_better_than_spt_assert_metric_sg(list action_list); // MISC. other stuff void track_state_in_start_vif(list action_list); void track_state_in_stop_vif(list action_list); void track_state_out_start_vif_rp(list action_list); void track_state_out_start_vif_wc(list action_list); void track_state_out_start_vif_sg(list action_list); void track_state_out_start_vif_sg_rpt(list action_list); void track_state_out_stop_vif_rp(list action_list); void track_state_out_stop_vif_wc(list action_list); void track_state_out_stop_vif_sg(list action_list); void track_state_out_stop_vif_sg_rpt(list action_list); void track_state_in_add_pim_mre_rp(list action_list); void track_state_in_add_pim_mre_wc(list action_list); void track_state_in_add_pim_mre_sg(list action_list); void track_state_in_add_pim_mre_sg_rpt(list action_list); void track_state_in_remove_pim_mre_rp(list action_list); void track_state_in_remove_pim_mre_wc(list action_list); void track_state_in_remove_pim_mre_sg(list action_list); void track_state_in_remove_pim_mre_sg_rpt(list action_list); void track_state_in_remove_pim_mfc(list action_list); void track_state_out_add_pim_mre_rp_entry_rp(list action_list); void track_state_out_add_pim_mre_rp_entry_wc(list action_list); void track_state_out_add_pim_mre_rp_entry_sg(list action_list); void track_state_out_add_pim_mre_rp_entry_sg_rpt(list action_list); void track_state_out_add_pim_mre_wc_entry_wc(list action_list); void track_state_out_add_pim_mre_wc_entry_sg(list action_list); void track_state_out_add_pim_mre_wc_entry_sg_rpt(list action_list); void track_state_out_add_pim_mre_sg_entry_sg(list action_list); void track_state_out_add_pim_mre_sg_entry_sg_rpt(list action_list); void track_state_out_add_pim_mre_sg_rpt_entry_sg(list action_list); void track_state_out_add_pim_mre_sg_rpt_entry_sg_rpt(list action_list); void track_state_out_remove_pim_mre_rp_entry_rp(list action_list); void track_state_out_remove_pim_mre_rp_entry_wc(list action_list); void track_state_out_remove_pim_mre_rp_entry_sg(list action_list); void track_state_out_remove_pim_mre_rp_entry_sg_rpt(list action_list); void track_state_out_remove_pim_mre_wc_entry_wc(list action_list); void track_state_out_remove_pim_mre_wc_entry_sg(list action_list); void track_state_out_remove_pim_mre_wc_entry_sg_rpt(list action_list); void track_state_out_remove_pim_mre_sg_entry_sg(list action_list); void track_state_out_remove_pim_mre_sg_entry_sg_rpt(list action_list); void track_state_out_remove_pim_mre_sg_rpt_entry_sg(list action_list); void track_state_out_remove_pim_mre_sg_rpt_entry_sg_rpt(list action_list); void track_state_out_remove_pim_mfc_entry_mfc(list action_list); void track_state_update_sptbit_mfc(list action_list); void track_state_set_keepalive_timer_sg(list action_list); // Private state PimMrt* _pim_mrt; // The PIM MRT }; // Class to keep state for taking actions class PimMreAction { public: // XXX: the @param entry type is one of the following: // PIM_MRE_SG, PIM_MRE_SG_RPT, PIM_MRE_WC, PIM_MRE_RP, PIM_MFC PimMreAction(PimMreTrackState::output_state_t output_state, uint32_t entry_type) : _output_state(output_state), _entry_type (entry_type) {} PimMreAction(const PimMreAction& action) : _output_state(action.output_state()), _entry_type(action.entry_type()) {} #ifdef XORP_USE_USTL PimMreAction() { } #endif PimMreTrackState::output_state_t output_state() const { return (_output_state); } bool is_sg() const { return (_entry_type & PIM_MRE_SG); } bool is_sg_rpt() const { return (_entry_type & PIM_MRE_SG_RPT); } bool is_wc() const { return (_entry_type & PIM_MRE_WC); } bool is_rp() const { return (_entry_type & PIM_MRE_RP); } bool is_mfc() const { return (_entry_type & PIM_MFC); } uint32_t entry_type() const { return (_entry_type); } bool operator==(const PimMreAction& action) const { return ((output_state() == action.output_state()) && (entry_type() == action.entry_type())); } void perform_action(PimMre& pim_mre, uint32_t vif_index, const IPvX& addr_arg); void perform_action(PimMfc& pim_mfc); private: PimMreTrackState::output_state_t _output_state; uint32_t _entry_type; }; #endif // __PIM_PIM_MRE_TRACK_STATE_HH__ xorp/pim/pim_nbr.cc0000664000076400007640000002574211540224231014406 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // PIM neigbor routers handling // #include "pim_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvx.hh" #include "pim_nbr.hh" #include "pim_node.hh" #include "pim_vif.hh" // // Exported variables // // // Local constants definitions // // // Local structures/classes, typedefs and macros // // // Local variables // // // Local functions prototypes // /** * PimNbr::PimNbr: * @pim_vif: The protocol vif towards the neighbor. * @primary_addr: The primary address of the neighbor. * * PIM neighbor constructor. **/ PimNbr::PimNbr(PimVif* pim_vif, const IPvX& primary_addr, int proto_version) : _pim_node(pim_vif->pim_node()), _pim_vif(pim_vif), _primary_addr(primary_addr), _proto_version(proto_version), _jp_header(pim_vif->pim_node()), _startup_time(TimeVal::MAXIMUM()) { reset_received_options(); } /** * PimNbr::~PimNbr: * @: * * PIM neighbor destructor. **/ PimNbr::~PimNbr() { // TODO: do we need to do anything special about _jp_list ?? } /** * PimNbr::reset_received_options: * @: * * Reset received options from this PIM neighbor. **/ void PimNbr::reset_received_options() { _proto_version = _pim_vif->proto_version(); // TODO: 0xffffffffU for _genid should be #define _genid = 0xffffffffU; _is_genid_present = false; set_dr_priority(PIM_HELLO_DR_PRIORITY_DEFAULT); set_is_dr_priority_present(false); _hello_holdtime = PIM_HELLO_HELLO_HOLDTIME_DEFAULT; _neighbor_liveness_timer.unschedule(); _is_lan_prune_delay_present = false; _is_tracking_support_disabled = false; _propagation_delay = 0; _override_interval = 0; _is_nohello_neighbor = false; _secondary_addr_list.clear(); } /** * PimNbr::vif_index: * @: * * Return the corresponding Vif index toward this neighbor. * * Return value: The Vif index toward this neighbor. **/ uint32_t PimNbr::vif_index() const { return (pim_vif()->vif_index()); } void PimNbr::add_secondary_addr(const IPvX& v) { if (find(_secondary_addr_list.begin(), _secondary_addr_list.end(), v) != _secondary_addr_list.end()) { return; // The address is already added } _secondary_addr_list.push_back(v); } void PimNbr::delete_secondary_addr(const IPvX& v) { list::iterator iter; iter = find(_secondary_addr_list.begin(), _secondary_addr_list.end(), v); if (iter != _secondary_addr_list.end()) _secondary_addr_list.erase(iter); } bool PimNbr::has_secondary_addr(const IPvX& secondary_addr) const { return (find(_secondary_addr_list.begin(), _secondary_addr_list.end(), secondary_addr) != _secondary_addr_list.end()); } bool PimNbr::is_my_addr(const IPvX& addr) const { if (addr == _primary_addr) return true; return (has_secondary_addr(addr)); } int PimNbr::jp_entry_add(const IPvX& source_addr, const IPvX& group_addr, uint8_t group_mask_len, mrt_entry_type_t mrt_entry_type, action_jp_t action_jp, uint16_t holdtime, bool is_new_group) { int ret_value; ret_value = _jp_header.jp_entry_add(source_addr, group_addr, group_mask_len, mrt_entry_type, action_jp, holdtime, is_new_group); // (Re)start the timer to send the J/P message after time 0. // XXX: the automatic restarting will postpone the sending of // the message until we have no more entries to add to that message. _jp_send_timer = pim_node()->eventloop().new_oneoff_after( TimeVal(0, 0), callback(this, &PimNbr::jp_send_timer_timeout)); return (ret_value); } void PimNbr::jp_send_timer_timeout() { string dummy_error_msg; pim_vif()->pim_join_prune_send(this, &_jp_header, dummy_error_msg); } /** * PimNbr::neighbor_liveness_timer_timeout: * * Timeout: expire a PIM neighbor. **/ void PimNbr::neighbor_liveness_timer_timeout() { pim_vif()->delete_pim_nbr_from_nbr_list(this); if (pim_vif()->dr_addr() == primary_addr()) { // The neighbor to expire is the DR. Select a new DR. pim_vif()->pim_dr_elect(); } if (pim_vif()->pim_nbrs_number() <= 1) { // XXX: the last neighbor on this vif: take any actions if necessary } pim_vif()->delete_pim_nbr(this); } void PimNbr::init_processing_pim_mre_rp() { _processing_pim_mre_rp_list.splice( _processing_pim_mre_rp_list.end(), _pim_mre_rp_list); } void PimNbr::init_processing_pim_mre_wc() { _processing_pim_mre_wc_list.splice( _processing_pim_mre_wc_list.end(), _pim_mre_wc_list); } void PimNbr::init_processing_pim_mre_sg() { _processing_pim_mre_sg_list.splice( _processing_pim_mre_sg_list.end(), _pim_mre_sg_list); } void PimNbr::init_processing_pim_mre_sg_rpt() { _processing_pim_mre_sg_rpt_list.splice( _processing_pim_mre_sg_rpt_list.end(), _pim_mre_sg_rpt_list); } // // XXX: A PimMre entry may reference to the same PimNbr more than once, // therefore we may call this method more than once for the same PimMre. // Hence, to avoid this, the method that calls add_pim_mre() must make // sure that it wasn't called before: // E.g., we can use PimMre::is_pim_nbr_in_use() to check that. // The alternative is to use find() for each of the list before appending // the entry there, but this adds some unpleasant overhead. // Nevertheless, things can easily become intractable, hence we // use find() to make sure everything is consistent. // Note that (S,G) and (S,G,rpt) entries are always added, regardless whether // they have (*,G) entry. This is needed to take care of source-related RPF // neighbors. void PimNbr::add_pim_mre(PimMre *pim_mre) { // TODO: completely remove? // if (pim_mre->is_sg() || pim_mre->is_sg_rpt()) { // if ((pim_mre->wc_entry() != NULL) && (! ignore_wc_entry)) // return; // XXX: we don't add (S,G) or (S,G,rpt) that have (*,G) // } if (pim_mre->is_rp()) { if (find(_pim_mre_rp_list.begin(), _pim_mre_rp_list.end(), pim_mre) != _pim_mre_rp_list.end()) { return; // Entry is already on the list } _pim_mre_rp_list.push_back(pim_mre); return; } if (pim_mre->is_wc()) { if (find(_pim_mre_wc_list.begin(), _pim_mre_wc_list.end(), pim_mre) != _pim_mre_wc_list.end()) { return; // Entry is already on the list } _pim_mre_wc_list.push_back(pim_mre); return; } if (pim_mre->is_sg()) { if (find(_pim_mre_sg_list.begin(), _pim_mre_sg_list.end(), pim_mre) != _pim_mre_sg_list.end()) { return; // Entry is already on the list } _pim_mre_sg_list.push_back(pim_mre); return; } if (pim_mre->is_sg_rpt()) { if (find(_pim_mre_sg_rpt_list.begin(), _pim_mre_sg_rpt_list.end(), pim_mre) != _pim_mre_sg_rpt_list.end()) { return; // Entry is already on the list } _pim_mre_sg_rpt_list.push_back(pim_mre); return; } } void PimNbr::delete_pim_mre(PimMre *pim_mre) { list::iterator pim_mre_iter; do { if (pim_mre->is_rp()) { // // (*,*,RP) entry // // // Try the pim_mre_rp_list // pim_mre_iter = find(_pim_mre_rp_list.begin(), _pim_mre_rp_list.end(), pim_mre); if (pim_mre_iter != _pim_mre_rp_list.end()) { _pim_mre_rp_list.erase(pim_mre_iter); break; } // // Try the processing_pim_mre_rp_list // pim_mre_iter = find(_processing_pim_mre_rp_list.begin(), _processing_pim_mre_rp_list.end(), pim_mre); if (pim_mre_iter != _processing_pim_mre_rp_list.end()) { _processing_pim_mre_rp_list.erase(pim_mre_iter); break; } } if (pim_mre->is_wc()) { // // (*,G) entry // // // Try the pim_mre_wc_list // pim_mre_iter = find(_pim_mre_wc_list.begin(), _pim_mre_wc_list.end(), pim_mre); if (pim_mre_iter != _pim_mre_wc_list.end()) { _pim_mre_wc_list.erase(pim_mre_iter); break; } // // Try the processing_pim_mre_wc_list // pim_mre_iter = find(_processing_pim_mre_wc_list.begin(), _processing_pim_mre_wc_list.end(), pim_mre); if (pim_mre_iter != _processing_pim_mre_wc_list.end()) { _processing_pim_mre_wc_list.erase(pim_mre_iter); break; } } if (pim_mre->is_sg()) { // // (S,G) entry // // // Try the pim_mre_sg_list // pim_mre_iter = find(_pim_mre_sg_list.begin(), _pim_mre_sg_list.end(), pim_mre); if (pim_mre_iter != _pim_mre_sg_list.end()) { _pim_mre_sg_list.erase(pim_mre_iter); break; } // // Try the processing_pim_mre_sg_list // pim_mre_iter = find(_processing_pim_mre_sg_list.begin(), _processing_pim_mre_sg_list.end(), pim_mre); if (pim_mre_iter != _processing_pim_mre_sg_list.end()) { _processing_pim_mre_sg_list.erase(pim_mre_iter); break; } } if (pim_mre->is_sg_rpt()) { // // (S,G,rpt) entry // // // Try the pim_mre_sg_rpt_list // pim_mre_iter = find(_pim_mre_sg_rpt_list.begin(), _pim_mre_sg_rpt_list.end(), pim_mre); if (pim_mre_iter != _pim_mre_sg_rpt_list.end()) { _pim_mre_sg_rpt_list.erase(pim_mre_iter); break; } // // Try the processing_pim_mre_sg_rpt_list // pim_mre_iter = find(_processing_pim_mre_sg_rpt_list.begin(), _processing_pim_mre_sg_rpt_list.end(), pim_mre); if (pim_mre_iter != _processing_pim_mre_sg_rpt_list.end()) { _processing_pim_mre_sg_rpt_list.erase(pim_mre_iter); break; } } } while (false); // If the PimNbr was pending deletion and if this was the // lastest PimMre entry, delete the PimNbr entry. do { if ( ! (_pim_mre_rp_list.empty() && _processing_pim_mre_rp_list.empty() && _pim_mre_wc_list.empty() && _processing_pim_mre_wc_list.empty() && _pim_mre_sg_list.empty() && _processing_pim_mre_sg_list.empty() && _pim_mre_sg_rpt_list.empty() && _processing_pim_mre_sg_rpt_list.empty())) { break; // There are still more entries } list::iterator pim_nbr_iter; pim_nbr_iter = find(pim_node()->processing_pim_nbr_list().begin(), pim_node()->processing_pim_nbr_list().end(), this); if (pim_nbr_iter != pim_node()->processing_pim_nbr_list().end()) { pim_node()->processing_pim_nbr_list().erase(pim_nbr_iter); delete this; return; } } while (false); } xorp/pim/README0000664000076400007640000000562211421137511013326 0ustar greearbgreearb# # $XORP: xorp/pim/README,v 1.33 2007/04/18 01:16:50 pavlin Exp $ # Protocol Independent Multicast-Sparse Mode (PIM-SM) Implementation ================================================================== This directory contains the XORP implementation of the PIM-SM protocol. Configuration ============= PIM-SM like most XORP processes does not take its configuration parameters from the command line. Its parameters are provided via XRLs. At the very least a PIM-SM process must be provided with the set of network interfaces to enable for multicast. Startup ======= In normal operation, PIM-SM would be started by the XORP router manager process, not directly from the command line. For information how to configure PIM-SM see http://www.xorp.org/getting_started.html or http://www.xorp.org/releases/current/docs/user_manual/user_manual.pdf To use PIM-SM, you must have PIM-capable kernel. Currently, the following systems are known to have PIM-SM kernel support: - IPv4: DragonFlyBSD, FreeBSD-4.9, FreeBSD-5.2 (and later), NetBSD-3.0 (and later), OpenBSD-2.7 (and later), Linux: For DragonFlyBSD or FreeBSD kernel, the following options are required. Note that the PIM kernel option is not needed for FreeBSD-7.0 and later: options MROUTING # Multicast routing options PIM # PIM multicast routing For NetBSD kernel, the following options are required: options MROUTING # IP multicast routing options PIM # Protocol Independent Multicast For OpenBSD kernel, the following options are required: option MROUTING # Multicast router option PIM # Protocol Independent Multicast For Linux kernel, the following options are required: CONFIG_IP_MULTICAST=y CONFIG_IP_MROUTE=y CONFIG_IP_PIMSM_V2=y - IPv6: DragonFlyBSD, FreeBSD, NetBSD, OpenBSD: No kernel options are required. Documentation ============= The PIM-SM design architecture and code structure are described in: ${XORP}/docs/pim/ The programming documentation is in: ${XORP}/docs/kdoc/html/pim/ Testing ======= Currently, the PIM-SM testing is performed manually, by starting PIM-SM on a number of testbed machines. All tests performed so far are described in ${XORP}/docs/pim_testsuite/pim_testsuite.ps In the future, automated testing will be added, similar to the BGP testing framework. Status ====== Currently (July 2008), the PIM-SM implementation is based on the specification in the following documents: * draft-ietf-pim-sm-v2-new-11.{ps,txt} (The core PIM-SM specification). * draft-ietf-pim-sm-bsr-03.{ps,txt} (The Bootstrap mechanism). The only major features not implemented yet are SSM support and security. Many detailed tests have been performed so far (see the "XORP PIM-SM Test Suite" document in ${XORP}/docs/pim_testsute/ or http://www.xorp.org/design_docs.html), and the code appears to be spec-compliant and robust, but no guarantees it is bug-free of course. xorp/pim/pim_mre_join_prune.cc0000664000076400007640000020233311634760770016652 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // PIM Multicast Routing Entry Join/Prune handling // #include "pim_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvx.hh" #include "pim_mre.hh" #include "pim_mrt.hh" #include "pim_node.hh" #include "pim_vif.hh" // // J/P state (per interface) // // Note: applies for (*,*,RP), (*,G), (S,G), (S,G,rpt) void PimMre::set_downstream_noinfo_state(uint32_t vif_index) { if (vif_index == Vif::VIF_INDEX_INVALID) return; if (is_downstream_noinfo_state(vif_index)) return; // Nothing changed _downstream_join_state.reset(vif_index); _downstream_prune_state.reset(vif_index); _downstream_prune_pending_state.reset(vif_index); _downstream_tmp_state.reset(vif_index); do { if (is_sg()) { pim_mrt()->add_task_downstream_jp_state_sg(vif_index, source_addr(), group_addr()); break; } if (is_sg_rpt()) { pim_mrt()->add_task_downstream_jp_state_sg_rpt(vif_index, source_addr(), group_addr()); break; } if (is_wc()) { pim_mrt()->add_task_downstream_jp_state_wc(vif_index, group_addr()); break; } if (is_rp()) { pim_mrt()->add_task_downstream_jp_state_rp(vif_index, *rp_addr_ptr()); break; } } while (false); // Try to remove the entry entry_try_remove(); } // Note: applies for (*,*,RP), (*,G), (S,G), (S,G,rpt) void PimMre::set_downstream_join_state(uint32_t vif_index) { if (vif_index == Vif::VIF_INDEX_INVALID) return; if (is_downstream_join_state(vif_index)) return; // Nothing changed _downstream_join_state.set(vif_index); _downstream_prune_state.reset(vif_index); _downstream_prune_pending_state.reset(vif_index); _downstream_tmp_state.reset(vif_index); do { if (is_sg()) { pim_mrt()->add_task_downstream_jp_state_sg(vif_index, source_addr(), group_addr()); break; } if (is_sg_rpt()) { pim_mrt()->add_task_downstream_jp_state_sg_rpt(vif_index, source_addr(), group_addr()); break; } if (is_wc()) { pim_mrt()->add_task_downstream_jp_state_wc(vif_index, group_addr()); break; } if (is_rp()) { pim_mrt()->add_task_downstream_jp_state_rp(vif_index, *rp_addr_ptr()); break; } } while (false); } // Note: applies for (*,*,RP), (*,G), (S,G), (S,G,rpt) void PimMre::set_downstream_prune_state(uint32_t vif_index) { if (vif_index == Vif::VIF_INDEX_INVALID) return; if (is_downstream_prune_state(vif_index)) return; // Nothing changed _downstream_join_state.reset(vif_index); _downstream_prune_state.set(vif_index); _downstream_prune_pending_state.reset(vif_index); _downstream_tmp_state.reset(vif_index); do { if (is_sg()) { pim_mrt()->add_task_downstream_jp_state_sg(vif_index, source_addr(), group_addr()); break; } if (is_sg_rpt()) { pim_mrt()->add_task_downstream_jp_state_sg_rpt(vif_index, source_addr(), group_addr()); break; } if (is_wc()) { pim_mrt()->add_task_downstream_jp_state_wc(vif_index, group_addr()); break; } if (is_rp()) { pim_mrt()->add_task_downstream_jp_state_rp(vif_index, *rp_addr_ptr()); break; } } while (false); } // Note: applies for (*,*,RP), (*,G), (S,G), (S,G,rpt) void PimMre::set_downstream_prune_pending_state(uint32_t vif_index) { if (vif_index == Vif::VIF_INDEX_INVALID) return; if (is_downstream_prune_pending_state(vif_index)) return; // Nothing changed _downstream_join_state.reset(vif_index); _downstream_prune_state.reset(vif_index); _downstream_prune_pending_state.set(vif_index); _downstream_tmp_state.reset(vif_index); do { if (is_sg()) { pim_mrt()->add_task_downstream_jp_state_sg(vif_index, source_addr(), group_addr()); break; } if (is_sg_rpt()) { pim_mrt()->add_task_downstream_jp_state_sg_rpt(vif_index, source_addr(), group_addr()); break; } if (is_wc()) { pim_mrt()->add_task_downstream_jp_state_wc(vif_index, group_addr()); break; } if (is_rp()) { pim_mrt()->add_task_downstream_jp_state_rp(vif_index, *rp_addr_ptr()); break; } } while (false); } // Note: applies for (S,G,rpt) void PimMre::set_downstream_prune_tmp_state(uint32_t vif_index) { if (vif_index == Vif::VIF_INDEX_INVALID) return; if (! is_sg_rpt()) return; if (is_downstream_prune_tmp_state(vif_index)) return; // Nothing changed _downstream_join_state.reset(vif_index); _downstream_prune_state.set(vif_index); _downstream_prune_pending_state.reset(vif_index); _downstream_tmp_state.set(vif_index); // XXX: this state is transient, hence we don't call // add_task_downstream_jp_state_sg_rpt(); } // Note: applies for (S,G,rpt) void PimMre::set_downstream_prune_pending_tmp_state(uint32_t vif_index) { if (vif_index == Vif::VIF_INDEX_INVALID) return; if (! is_sg_rpt()) return; if (is_downstream_prune_pending_tmp_state(vif_index)) return; // Nothing changed _downstream_join_state.reset(vif_index); _downstream_prune_state.reset(vif_index); _downstream_prune_pending_state.set(vif_index); _downstream_tmp_state.set(vif_index); // XXX: this state is transient, hence we don't call // add_task_downstream_jp_state_sg_rpt(); } // Note: applies for (S,G,rpt) void PimMre::set_downstream_processed_wc_by_sg_rpt(uint32_t vif_index, bool v) { if (vif_index == Vif::VIF_INDEX_INVALID) return; if (! is_sg_rpt()) return; if (v) _downstream_processed_wc_by_sg_rpt.set(vif_index); else _downstream_processed_wc_by_sg_rpt.reset(vif_index); } // Note: applies for (*,*,RP), (*,G), (S,G), (S,G,rpt) bool PimMre::is_downstream_noinfo_state(uint32_t vif_index) const { if (vif_index == Vif::VIF_INDEX_INVALID) return (true); // XXX // XXX: we don't test for _downstream_tmp_state, because it is used // in combination with other state. return (! (_downstream_join_state.test(vif_index) || _downstream_prune_state.test(vif_index) || _downstream_prune_pending_state.test(vif_index)) ); } // Note: applies for (*,*,RP), (*,G), (S,G), (S,G,rpt) bool PimMre::is_downstream_join_state(uint32_t vif_index) const { if (vif_index == Vif::VIF_INDEX_INVALID) return (false); return (_downstream_join_state.test(vif_index)); } // Note: applies for (*,*,RP), (*,G), (S,G), (S,G,rpt) bool PimMre::is_downstream_prune_state(uint32_t vif_index) const { if (vif_index == Vif::VIF_INDEX_INVALID) return (false); return (_downstream_prune_state.test(vif_index) & !_downstream_tmp_state.test(vif_index)); } // Note: applies for (*,*,RP), (*,G), (S,G), (S,G,rpt) bool PimMre::is_downstream_prune_pending_state(uint32_t vif_index) const { if (vif_index == Vif::VIF_INDEX_INVALID) return (false); return (_downstream_prune_pending_state.test(vif_index) & !_downstream_tmp_state.test(vif_index)); } // Note: applies for (S,G,rpt) bool PimMre::is_downstream_prune_tmp_state(uint32_t vif_index) const { if (vif_index == Vif::VIF_INDEX_INVALID) return (false); return (_downstream_prune_state.test(vif_index) & _downstream_tmp_state.test(vif_index)); } // Note: applies for (S,G,rpt) bool PimMre::is_downstream_prune_pending_tmp_state(uint32_t vif_index) const { if (vif_index == Vif::VIF_INDEX_INVALID) return (false); return (_downstream_prune_pending_state.test(vif_index) & _downstream_tmp_state.test(vif_index)); } // Note: applies for (S,G,rpt) bool PimMre::is_downstream_processed_wc_by_sg_rpt(uint32_t vif_index) const { if (vif_index == Vif::VIF_INDEX_INVALID) return (false); return (_downstream_processed_wc_by_sg_rpt.test(vif_index)); } // Note: applies for (*,*,RP), (*,G), (S,G), (S,G,rpt) const Mifset& PimMre::downstream_join_state() const { return (_downstream_join_state); } // Note: applies for (*,*,RP), (*,G), (S,G), (S,G,rpt) const Mifset& PimMre::downstream_prune_state() const { static Mifset mifs; if (! is_sg_rpt()) return (_downstream_prune_state); mifs = _downstream_prune_state & ~_downstream_tmp_state; return (mifs); } // Note: applies for (*,*,RP), (*,G), (S,G), (S,G,rpt) const Mifset& PimMre::downstream_prune_pending_state() const { static Mifset mifs; if (! is_sg_rpt()) return (_downstream_prune_pending_state); mifs = _downstream_prune_pending_state & ~_downstream_tmp_state; return (mifs); } // Note: applies for (S,G,rpt) const Mifset& PimMre::downstream_prune_tmp_state() const { static Mifset mifs; if (! is_sg_rpt()) { mifs.reset(); return (mifs); } mifs = _downstream_prune_state & _downstream_tmp_state; return (mifs); } // Note: applies for (S,G,rpt) const Mifset& PimMre::downstream_prune_pending_tmp_state() const { static Mifset mifs; if (! is_sg_rpt()) { mifs.reset(); return (mifs); } mifs = _downstream_prune_pending_state & _downstream_tmp_state; return (mifs); } // // J/P state for (*,*,RP), (*,G), (S,G) // void PimMre::set_joined_state() { _flags |= PIM_MRE_JOINED_STATE; if (is_sg()) pim_mrt()->add_task_upstream_jp_state_sg(source_addr(), group_addr()); } void PimMre::set_not_joined_state() { _flags &= ~PIM_MRE_JOINED_STATE; if (is_sg()) pim_mrt()->add_task_upstream_jp_state_sg(source_addr(), group_addr()); // Try to remove the entry if (is_rp() || is_wc() || is_sg()) entry_try_remove(); } // // J/P state for (S,G,rpt) // bool PimMre::is_rpt_not_joined_state() const { return (! (_flags & (PIM_MRE_PRUNED_STATE | PIM_MRE_NOT_PRUNED_STATE))); } bool PimMre::is_pruned_state() const { return (_flags & PIM_MRE_PRUNED_STATE); } bool PimMre::is_not_pruned_state() const { return (_flags & PIM_MRE_NOT_PRUNED_STATE); } void PimMre::set_rpt_not_joined_state() { _flags &= ~(PIM_MRE_PRUNED_STATE | PIM_MRE_NOT_PRUNED_STATE); } void PimMre::set_pruned_state() { _flags &= ~PIM_MRE_NOT_PRUNED_STATE; _flags |= PIM_MRE_PRUNED_STATE; } void PimMre::set_not_pruned_state() { _flags &= ~PIM_MRE_PRUNED_STATE; _flags |= PIM_MRE_NOT_PRUNED_STATE; } // // Join/Prune downstream machinery // void PimMre::receive_join_rp(uint32_t vif_index, uint16_t holdtime) { TimeVal tv_left; if (vif_index == Vif::VIF_INDEX_INVALID) return; if (! is_rp()) return; if (is_downstream_join_state(vif_index)) goto join_state_label; if (is_downstream_prune_pending_state(vif_index)) goto prune_pending_state_label; goto noinfo_state_label; noinfo_state_label: // NoInfo state -> Join state _downstream_expiry_timers[vif_index] = pim_node()->eventloop().new_oneoff_after( TimeVal(holdtime, 0), callback(this, &PimMre::downstream_expiry_timer_timeout_rp, vif_index)); set_downstream_join_state(vif_index); return; join_state_label: // Join state _downstream_expiry_timers[vif_index].time_remaining(tv_left); if (tv_left < TimeVal(holdtime, 0)) { _downstream_expiry_timers[vif_index] = pim_node()->eventloop().new_oneoff_after( TimeVal(holdtime, 0), callback(this, &PimMre::downstream_expiry_timer_timeout_rp, vif_index)); } return; prune_pending_state_label: // Prune-Pending state -> Join state // // XXX: canceling the Prune-Pending Timer is not in the spec, but it is // better if we cleanup the state now. // _downstream_prune_pending_timers[vif_index].unschedule(); _downstream_expiry_timers[vif_index].time_remaining(tv_left); if (tv_left < TimeVal(holdtime, 0)) { _downstream_expiry_timers[vif_index] = pim_node()->eventloop().new_oneoff_after( TimeVal(holdtime, 0), callback(this, &PimMre::downstream_expiry_timer_timeout_rp, vif_index)); } set_downstream_join_state(vif_index); return; } void PimMre::receive_prune_rp(uint32_t vif_index, uint16_t holdtime) { PimVif *pim_vif; if (vif_index == Vif::VIF_INDEX_INVALID) return; if (! is_rp()) return; if (is_downstream_join_state(vif_index)) goto join_state_label; if (is_downstream_prune_pending_state(vif_index)) goto prune_pending_state_label; goto noinfo_state_label; noinfo_state_label: // NoInfo state return; // Ignore join_state_label: // Join state -> Prune-Pending state pim_vif = pim_mrt()->vif_find_by_vif_index(vif_index); if (pim_vif == NULL) return; if (pim_vif->pim_nbrs_number() > 1) { TimeVal tv = pim_vif->jp_override_interval(); _downstream_prune_pending_timers[vif_index] = pim_node()->eventloop().new_oneoff_after( tv, callback(this, &PimMre::downstream_prune_pending_timer_timeout_rp, vif_index)); } else { // XXX: force to expire now _downstream_prune_pending_timers[vif_index] = pim_node()->eventloop().new_oneoff_after( TimeVal(0, 0), callback(this, &PimMre::downstream_prune_pending_timer_timeout_rp, vif_index)); } set_downstream_prune_pending_state(vif_index); return; prune_pending_state_label: // Prune-Pending state return; // Nothing to do UNUSED(holdtime); } void PimMre::receive_join_wc(uint32_t vif_index, uint16_t holdtime) { TimeVal tv_left; if (vif_index == Vif::VIF_INDEX_INVALID) return; if (! is_wc()) return; if (is_downstream_join_state(vif_index)) goto join_state_label; if (is_downstream_prune_pending_state(vif_index)) goto prune_pending_state_label; goto noinfo_state_label; noinfo_state_label: // NoInfo state -> Join state _downstream_expiry_timers[vif_index] = pim_node()->eventloop().new_oneoff_after( TimeVal(holdtime, 0), callback(this, &PimMre::downstream_expiry_timer_timeout_wc, vif_index)); set_downstream_join_state(vif_index); return; join_state_label: // Join state _downstream_expiry_timers[vif_index].time_remaining(tv_left); if (tv_left < TimeVal(holdtime, 0)) { _downstream_expiry_timers[vif_index] = pim_node()->eventloop().new_oneoff_after( TimeVal(holdtime, 0), callback(this, &PimMre::downstream_expiry_timer_timeout_wc, vif_index)); } return; prune_pending_state_label: // Prune-Pending state -> Join state // // XXX: canceling the Prune-Pending Timer is not in the spec, but it is // better if we cleanup the state now. // _downstream_prune_pending_timers[vif_index].unschedule(); _downstream_expiry_timers[vif_index].time_remaining(tv_left); if (tv_left < TimeVal(holdtime, 0)) { _downstream_expiry_timers[vif_index] = pim_node()->eventloop().new_oneoff_after( TimeVal(holdtime, 0), callback(this, &PimMre::downstream_expiry_timer_timeout_wc, vif_index)); } set_downstream_join_state(vif_index); return; } void PimMre::receive_prune_wc(uint32_t vif_index, uint16_t holdtime) { PimVif *pim_vif; if (vif_index == Vif::VIF_INDEX_INVALID) return; if (! is_wc()) return; if (is_downstream_join_state(vif_index)) goto join_state_label; if (is_downstream_prune_pending_state(vif_index)) goto prune_pending_state_label; goto noinfo_state_label; noinfo_state_label: // NoInfo state return; // Ignore join_state_label: // Join state -> Prune-Pending state pim_vif = pim_mrt()->vif_find_by_vif_index(vif_index); if (pim_vif == NULL) return; if (pim_vif->pim_nbrs_number() > 1) { TimeVal tv = pim_vif->jp_override_interval(); _downstream_prune_pending_timers[vif_index] = pim_node()->eventloop().new_oneoff_after( tv, callback(this, &PimMre::downstream_prune_pending_timer_timeout_wc, vif_index)); } else { // XXX: force to expire now _downstream_prune_pending_timers[vif_index] = pim_node()->eventloop().new_oneoff_after( TimeVal(0, 0), callback(this, &PimMre::downstream_prune_pending_timer_timeout_wc, vif_index)); } set_downstream_prune_pending_state(vif_index); return; prune_pending_state_label: // Prune-Pending state return; // Nothing to do UNUSED(holdtime); } void PimMre::receive_join_sg(uint32_t vif_index, uint16_t holdtime) { TimeVal tv_left; if (vif_index == Vif::VIF_INDEX_INVALID) return; if (! is_sg()) return; if (is_downstream_join_state(vif_index)) goto join_state_label; if (is_downstream_prune_pending_state(vif_index)) goto prune_pending_state_label; goto noinfo_state_label; noinfo_state_label: // NoInfo state -> Join state _downstream_expiry_timers[vif_index] = pim_node()->eventloop().new_oneoff_after( TimeVal(holdtime, 0), callback(this, &PimMre::downstream_expiry_timer_timeout_sg, vif_index)); set_downstream_join_state(vif_index); return; join_state_label: // Join state _downstream_expiry_timers[vif_index].time_remaining(tv_left); if (tv_left < TimeVal(holdtime, 0)) { _downstream_expiry_timers[vif_index] = pim_node()->eventloop().new_oneoff_after( TimeVal(holdtime, 0), callback(this, &PimMre::downstream_expiry_timer_timeout_sg, vif_index)); } return; prune_pending_state_label: // Prune-Pending state -> Join state // // XXX: canceling the Prune-Pending Timer is not in the spec, but it is // better if we cleanup the state now. // _downstream_prune_pending_timers[vif_index].unschedule(); _downstream_expiry_timers[vif_index].time_remaining(tv_left); if (tv_left < TimeVal(holdtime, 0)) { _downstream_expiry_timers[vif_index] = pim_node()->eventloop().new_oneoff_after( TimeVal(holdtime, 0), callback(this, &PimMre::downstream_expiry_timer_timeout_sg, vif_index)); } set_downstream_join_state(vif_index); return; } void PimMre::receive_prune_sg(uint32_t vif_index, uint16_t holdtime) { PimVif *pim_vif; if (vif_index == Vif::VIF_INDEX_INVALID) return; if (! is_sg()) return; if (is_downstream_join_state(vif_index)) goto join_state_label; if (is_downstream_prune_pending_state(vif_index)) goto prune_pending_state_label; goto noinfo_state_label; noinfo_state_label: // NoInfo state return; // Ignore join_state_label: // Join state -> Prune-Pending state pim_vif = pim_mrt()->vif_find_by_vif_index(vif_index); if (pim_vif == NULL) return; if (pim_vif->pim_nbrs_number() > 1) { TimeVal tv = pim_vif->jp_override_interval(); _downstream_prune_pending_timers[vif_index] = pim_node()->eventloop().new_oneoff_after( tv, callback(this, &PimMre::downstream_prune_pending_timer_timeout_sg, vif_index)); } else { // XXX: force to expire now _downstream_prune_pending_timers[vif_index] = pim_node()->eventloop().new_oneoff_after( TimeVal(0, 0), callback(this, &PimMre::downstream_prune_pending_timer_timeout_sg, vif_index)); } set_downstream_prune_pending_state(vif_index); return; prune_pending_state_label: // Prune-Pending state return; // Nothing to do UNUSED(holdtime); } void PimMre::receive_join_wc_by_sg_rpt(uint32_t vif_index) { if (vif_index == Vif::VIF_INDEX_INVALID) return; if (! is_sg_rpt()) return; if (is_downstream_processed_wc_by_sg_rpt(vif_index)) return; set_downstream_processed_wc_by_sg_rpt(vif_index, true); if (is_downstream_prune_state(vif_index)) goto prune_state_label; if (is_downstream_prune_pending_state(vif_index)) goto prune_pending_state_label; return; // Ignore prune_state_label: // Prune state -> PruneTmp state set_downstream_prune_tmp_state(vif_index); return; prune_pending_state_label: // Prune-Pending state -> Prune-Pending-Tmp state set_downstream_prune_pending_tmp_state(vif_index); return; } void PimMre::receive_join_sg_rpt(uint32_t vif_index, uint16_t holdtime) { if (vif_index == Vif::VIF_INDEX_INVALID) return; if (! is_sg_rpt()) return; if (is_downstream_prune_state(vif_index)) goto prune_state_label; if (is_downstream_prune_pending_state(vif_index)) goto prune_pending_state_label; goto noinfo_state_label; noinfo_state_label: // NoInfo state return; // Ignore prune_state_label: // Prune state -> NoInfo state // // XXX: canceling the Expiry Timer and the Prune-Pending Timer is not // in the spec, but it is better if we cleanup the state now. // _downstream_expiry_timers[vif_index].unschedule(); _downstream_prune_pending_timers[vif_index].unschedule(); set_downstream_noinfo_state(vif_index); return; prune_pending_state_label: // Prune-Pending state -> NoInfo state // // XXX: canceling the Expiry Timer and the Prune-Pending Timer is not // in the spec, but it is better if we cleanup the state now. // _downstream_expiry_timers[vif_index].unschedule(); _downstream_prune_pending_timers[vif_index].unschedule(); set_downstream_noinfo_state(vif_index); return; UNUSED(holdtime); } // @is_join_wc_received is true if there was a (*,G)Join together // with this (S,G,rpt)Prune message. void PimMre::receive_prune_sg_rpt(uint32_t vif_index, uint16_t holdtime, bool is_join_wc_received) { PimVif *pim_vif; TimeVal tv_left; if (vif_index == Vif::VIF_INDEX_INVALID) return; if (! is_sg_rpt()) return; if (is_join_wc_received) receive_join_wc_by_sg_rpt(vif_index); if (is_downstream_prune_state(vif_index)) goto prune_state_label; if (is_downstream_prune_pending_state(vif_index)) goto prune_pending_state_label; if (is_downstream_prune_tmp_state(vif_index)) goto prune_tmp_state_label; if (is_downstream_prune_pending_tmp_state(vif_index)) goto prune_pending_tmp_state_label; goto noinfo_state_label; noinfo_state_label: // NoInfo state -> Prune-Pending state pim_vif = pim_mrt()->vif_find_by_vif_index(vif_index); if (pim_vif == NULL) return; _downstream_expiry_timers[vif_index] = pim_node()->eventloop().new_oneoff_after( TimeVal(holdtime, 0), callback(this, &PimMre::downstream_expiry_timer_timeout_sg_rpt, vif_index)); if (pim_vif->pim_nbrs_number() > 1) { TimeVal tv = pim_vif->jp_override_interval(); _downstream_prune_pending_timers[vif_index] = pim_node()->eventloop().new_oneoff_after( tv, callback(this, &PimMre::downstream_prune_pending_timer_timeout_sg_rpt, vif_index)); } else { // XXX: force to expire now _downstream_prune_pending_timers[vif_index] = pim_node()->eventloop().new_oneoff_after( TimeVal(0, 0), callback(this, &PimMre::downstream_prune_pending_timer_timeout_sg_rpt, vif_index)); } set_downstream_prune_pending_state(vif_index); return; prune_tmp_state_label: // PruneTmp state -> Prune state set_downstream_prune_state(vif_index); // FALLTHROUGH to Prune state (XXX: note that the action is same) prune_state_label: // Prune state _downstream_expiry_timers[vif_index].time_remaining(tv_left); if (tv_left < TimeVal(holdtime, 0)) { _downstream_expiry_timers[vif_index] = pim_node()->eventloop().new_oneoff_after( TimeVal(holdtime, 0), callback(this, &PimMre::downstream_expiry_timer_timeout_sg_rpt, vif_index)); } return; prune_pending_tmp_state_label: // Prune-Pending-Tmp state -> Prune-Pending state set_downstream_prune_pending_state(vif_index); _downstream_expiry_timers[vif_index].time_remaining(tv_left); if (tv_left < TimeVal(holdtime, 0)) { _downstream_expiry_timers[vif_index] = pim_node()->eventloop().new_oneoff_after( TimeVal(holdtime, 0), callback(this, &PimMre::downstream_expiry_timer_timeout_sg_rpt, vif_index)); } // FALLTHROUGH to Prune-Pending state prune_pending_state_label: // Prune-Pending state return; // Nothing to do } // // Receive "End of Message" // Note: applies for (S,G,rpt) // void PimMre::receive_end_of_message_sg_rpt(uint32_t vif_index) { if (vif_index == Vif::VIF_INDEX_INVALID) return; if (! is_sg_rpt()) return; set_downstream_processed_wc_by_sg_rpt(vif_index, false); if (is_downstream_prune_tmp_state(vif_index)) goto prune_tmp_state_label; if (is_downstream_prune_pending_tmp_state(vif_index)) goto prune_pending_tmp_state_label; return; // Ignore prune_tmp_state_label: // PruneTmp state -> NoInfo state // // XXX: canceling the Expiry Timer is not in the spec, but it is // better if we cleanup the state now. // _downstream_expiry_timers[vif_index].unschedule(); set_downstream_noinfo_state(vif_index); return; prune_pending_tmp_state_label: // Prune-Pending-Tmp state -> NoInfo state // // XXX: canceling the Expiry Timer and the Prune-Pending Timer is not // in the spec, but it is better if we cleanup the state now. // _downstream_expiry_timers[vif_index].unschedule(); _downstream_prune_pending_timers[vif_index].unschedule(); set_downstream_noinfo_state(vif_index); return; } // // Downstream J/P timers // void PimMre::downstream_expiry_timer_timeout_rp(uint32_t vif_index) { if (vif_index == Vif::VIF_INDEX_INVALID) return; if (! is_rp()) return; if (is_downstream_join_state(vif_index)) goto join_state_label; if (is_downstream_prune_pending_state(vif_index)) goto prune_pending_label; return; join_state_label: // Join state -> NoInfo state prune_pending_label: // Prune-Pending state -> NoInfo state set_downstream_noinfo_state(vif_index); } void PimMre::downstream_prune_pending_timer_timeout_rp(uint32_t vif_index) { PimVif *pim_vif; PimNbr *pim_nbr_me; if (vif_index == Vif::VIF_INDEX_INVALID) return; if (! is_rp()) return; if (is_downstream_prune_pending_state(vif_index)) goto prune_pending_label; return; prune_pending_label: // Prune-Pending state -> NoInfo state // Send PruneEcho(*,*,RP) if more than one PIM neighbors pim_vif = pim_mrt()->vif_find_by_vif_index(vif_index); if (pim_vif == NULL) return; if (pim_vif->pim_nbrs().size() > 1) { pim_nbr_me = &pim_vif->pim_nbr_me(); bool is_new_group = false; // Group together all (*,*,RP) entries pim_nbr_me->jp_entry_add(*rp_addr_ptr(), IPvX::MULTICAST_BASE(family()), IPvX::ip_multicast_base_address_mask_len(family()), MRT_ENTRY_RP, ACTION_PRUNE, pim_nbr_me->pim_vif()->join_prune_holdtime().get(), is_new_group); } set_downstream_noinfo_state(vif_index); } void PimMre::downstream_expiry_timer_timeout_wc(uint32_t vif_index) { if (vif_index == Vif::VIF_INDEX_INVALID) return; if (! is_wc()) return; if (is_downstream_join_state(vif_index)) goto join_state_label; if (is_downstream_prune_pending_state(vif_index)) goto prune_pending_label; return; join_state_label: // Join state -> NoInfo state prune_pending_label: // Prune-Pending state -> NoInfo state set_downstream_noinfo_state(vif_index); } void PimMre::downstream_prune_pending_timer_timeout_wc(uint32_t vif_index) { PimVif *pim_vif; PimNbr *pim_nbr_me; const IPvX *my_rp_addr_ptr = NULL; if (vif_index == Vif::VIF_INDEX_INVALID) return; if (! is_wc()) return; if (is_downstream_prune_pending_state(vif_index)) goto prune_pending_label; return; prune_pending_label: // Prune-Pending state -> NoInfo state // Send PruneEcho(*,G) if more than one PIM neighbors pim_vif = pim_mrt()->vif_find_by_vif_index(vif_index); if (pim_vif == NULL) return; if (pim_vif->pim_nbrs().size() > 1) { pim_nbr_me = &pim_vif->pim_nbr_me(); bool is_new_group = false; // Group together all (*,G) entries my_rp_addr_ptr = rp_addr_ptr(); if (my_rp_addr_ptr == NULL) { XLOG_WARNING("Sending PruneEcho(*,G): " "RP for group %s: not found", cstring(group_addr())); } else { // We have RP pim_nbr_me->jp_entry_add(*my_rp_addr_ptr, group_addr(), IPvX::addr_bitlen(family()), MRT_ENTRY_WC, ACTION_PRUNE, pim_nbr_me->pim_vif()->join_prune_holdtime().get(), is_new_group); } } set_downstream_noinfo_state(vif_index); } void PimMre::downstream_expiry_timer_timeout_sg(uint32_t vif_index) { if (vif_index == Vif::VIF_INDEX_INVALID) return; if (! is_sg()) return; if (is_downstream_join_state(vif_index)) goto join_state_label; if (is_downstream_prune_pending_state(vif_index)) goto prune_pending_label; return; join_state_label: // Join state -> NoInfo state prune_pending_label: // Prune-Pending state -> NoInfo state set_downstream_noinfo_state(vif_index); } void PimMre::downstream_prune_pending_timer_timeout_sg(uint32_t vif_index) { PimVif *pim_vif; PimNbr *pim_nbr_me; if (vif_index == Vif::VIF_INDEX_INVALID) return; if (! is_sg()) return; if (is_downstream_prune_pending_state(vif_index)) goto prune_pending_label; return; prune_pending_label: // Prune-Pending state -> NoInfo state // Send PruneEcho(S,G) if more than one PIM neighbors pim_vif = pim_mrt()->vif_find_by_vif_index(vif_index); if (pim_vif == NULL) return; if (pim_vif->pim_nbrs().size() > 1) { pim_nbr_me = &pim_vif->pim_nbr_me(); bool is_new_group = false; // Group together all (S,G) entries pim_nbr_me->jp_entry_add(source_addr(), group_addr(), IPvX::addr_bitlen(family()), MRT_ENTRY_SG, ACTION_PRUNE, pim_nbr_me->pim_vif()->join_prune_holdtime().get(), is_new_group); } set_downstream_noinfo_state(vif_index); } void PimMre::downstream_expiry_timer_timeout_sg_rpt(uint32_t vif_index) { if (vif_index == Vif::VIF_INDEX_INVALID) return; if (! is_sg_rpt()) return; if (is_downstream_prune_state(vif_index)) goto prune_state_label; return; prune_state_label: // Prune state -> NoInfo state set_downstream_noinfo_state(vif_index); return; } void PimMre::downstream_prune_pending_timer_timeout_sg_rpt(uint32_t vif_index) { if (vif_index == Vif::VIF_INDEX_INVALID) return; if (! is_sg_rpt()) return; if (is_downstream_prune_pending_state(vif_index)) goto prune_pending_state_label; return; prune_pending_state_label: // Prune-Pending state -> Prune state set_downstream_prune_state(vif_index); return; } // // Upstream J/P (*,*,RP) state machine // void PimMre::rp_see_join_rp(uint32_t vif_index, uint16_t holdtime, const IPvX& target_nbr_addr) { PimNbr *my_nbr_mrib_next_hop_rp; PimVif *pim_vif; if (vif_index == Vif::VIF_INDEX_INVALID) return; if (! is_rp()) return; if (is_joined_state()) goto joined_state_label; return; joined_state_label: my_nbr_mrib_next_hop_rp = nbr_mrib_next_hop_rp(); if (my_nbr_mrib_next_hop_rp == NULL) { // XXX: I don't know the NBR(RPF_interface(RP), MRIB.next_hop(RP)) return; } if (my_nbr_mrib_next_hop_rp->vif_index() != vif_index) return; if (! my_nbr_mrib_next_hop_rp->is_my_addr(target_nbr_addr)) return; // `target_nbr_addr' belongs to NBR(RPF_interface(RP), MRIB.next_hop(RP)) // Increase Join Timer to t_joinsuppress TimeVal t_suppressed, t_joinsuppress, tv_left; pim_vif = pim_mrt()->vif_find_by_vif_index(vif_index); if (pim_vif == NULL) return; t_suppressed = pim_vif->upstream_join_timer_t_suppressed(); t_joinsuppress = TimeVal(holdtime, 0); if (t_suppressed < t_joinsuppress) t_joinsuppress = t_suppressed; join_timer().time_remaining(tv_left); if (tv_left < t_joinsuppress) { // Restart the timer with `t_joinsuppress' join_timer() = pim_node()->eventloop().new_oneoff_after( t_joinsuppress, callback(this, &PimMre::join_timer_timeout)); } } void PimMre::rp_see_prune_rp(uint32_t vif_index, uint16_t holdtime, const IPvX& target_nbr_addr) { PimNbr *my_nbr_mrib_next_hop_rp; PimVif *pim_vif; if (vif_index == Vif::VIF_INDEX_INVALID) return; if (! is_rp()) return; if (is_joined_state()) goto joined_state_label; return; joined_state_label: my_nbr_mrib_next_hop_rp = nbr_mrib_next_hop_rp(); if (my_nbr_mrib_next_hop_rp == NULL) { // XXX: I don't know the NBR(RPF_interface(RP), MRIB.next_hop(RP)) return; } if (my_nbr_mrib_next_hop_rp->vif_index() != vif_index) return; if (! my_nbr_mrib_next_hop_rp->is_my_addr(target_nbr_addr)) return; // `target_nbr_addr' belongs to NBR(RPF_interface(RP), MRIB.next_hop(RP)) // Restart Join Timer if it is larger than t_override TimeVal t_override, tv_left; pim_vif = pim_mrt()->vif_find_by_vif_index(vif_index); if (pim_vif == NULL) return; t_override = pim_vif->upstream_join_timer_t_override(); join_timer().time_remaining(tv_left); if (tv_left > t_override) { // Restart the timer with `t_override' join_timer() = pim_node()->eventloop().new_oneoff_after( t_override, callback(this, &PimMre::join_timer_timeout)); } UNUSED(holdtime); } void PimMre::wc_see_join_wc(uint32_t vif_index, uint16_t holdtime, const IPvX& target_nbr_addr) { PimNbr *my_rpfp_nbr_wc; PimVif *pim_vif; if (vif_index == Vif::VIF_INDEX_INVALID) return; if (! is_wc()) return; if (is_joined_state()) goto joined_state_label; return; joined_state_label: my_rpfp_nbr_wc = rpfp_nbr_wc(); if (my_rpfp_nbr_wc == NULL) return; // XXX: I don't know the RPF'(*,G) nbr if (my_rpfp_nbr_wc->vif_index() != vif_index) return; if (! my_rpfp_nbr_wc->is_my_addr(target_nbr_addr)) return; // `target_nbr_addr' belongs to RPF'(*,G) // Increase Join Timer to t_joinsuppress TimeVal t_suppressed, t_joinsuppress, tv_left; pim_vif = pim_mrt()->vif_find_by_vif_index(vif_index); if (pim_vif == NULL) return; t_suppressed = pim_vif->upstream_join_timer_t_suppressed(); t_joinsuppress = TimeVal(holdtime, 0); if (t_suppressed < t_joinsuppress) t_joinsuppress = t_suppressed; join_timer().time_remaining(tv_left); if (tv_left < t_joinsuppress) { // Restart the timer with `t_joinsuppress' join_timer() = pim_node()->eventloop().new_oneoff_after( t_joinsuppress, callback(this, &PimMre::join_timer_timeout)); } } void PimMre::wc_see_prune_wc(uint32_t vif_index, uint16_t holdtime, const IPvX& target_nbr_addr) { PimNbr *my_rpfp_nbr_wc; PimVif *pim_vif; if (vif_index == Vif::VIF_INDEX_INVALID) return; if (! is_wc()) return; if (is_joined_state()) goto joined_state_label; return; joined_state_label: my_rpfp_nbr_wc = rpfp_nbr_wc(); if (my_rpfp_nbr_wc == NULL) return; // XXX: I don't know the RPF'(*,G) nbr if (my_rpfp_nbr_wc->vif_index() != vif_index) return; if (! my_rpfp_nbr_wc->is_my_addr(target_nbr_addr)) return; // `target_nbr_addr' belongs to RPF'(*,G) // Restart Join Timer if it is larger than t_override TimeVal t_override, tv_left; pim_vif = pim_mrt()->vif_find_by_vif_index(vif_index); if (pim_vif == NULL) return; t_override = pim_vif->upstream_join_timer_t_override(); join_timer().time_remaining(tv_left); if (tv_left > t_override) { // Restart the timer with `t_override' join_timer() = pim_node()->eventloop().new_oneoff_after( t_override, callback(this, &PimMre::join_timer_timeout)); } UNUSED(holdtime); } void PimMre::sg_see_join_sg(uint32_t vif_index, uint16_t holdtime, const IPvX& target_nbr_addr) { PimNbr *my_rpfp_nbr_sg; PimVif *pim_vif; if (vif_index == Vif::VIF_INDEX_INVALID) return; if (! is_sg()) return; if (is_joined_state()) goto joined_state_label; return; joined_state_label: my_rpfp_nbr_sg = rpfp_nbr_sg(); if (my_rpfp_nbr_sg == NULL) return; // XXX: I don't know the RPF'(S,G) nbr if (my_rpfp_nbr_sg->vif_index() != vif_index) return; if (! my_rpfp_nbr_sg->is_my_addr(target_nbr_addr)) return; // `target_nbr_addr' belongs to RPF'(S,G) // Increase Join Timer to t_joinsuppress TimeVal t_suppressed, t_joinsuppress, tv_left; pim_vif = pim_mrt()->vif_find_by_vif_index(vif_index); if (pim_vif == NULL) return; t_suppressed = pim_vif->upstream_join_timer_t_suppressed(); t_joinsuppress = TimeVal(holdtime, 0); if (t_suppressed < t_joinsuppress) t_joinsuppress = t_suppressed; join_timer().time_remaining(tv_left); if (tv_left < t_joinsuppress) { // Restart the timer with `t_joinsuppress' join_timer() = pim_node()->eventloop().new_oneoff_after( t_joinsuppress, callback(this, &PimMre::join_timer_timeout)); } } void PimMre::sg_see_prune_sg(uint32_t vif_index, uint16_t holdtime, const IPvX& target_nbr_addr) { PimNbr *my_rpfp_nbr_sg; PimVif *pim_vif; if (vif_index == Vif::VIF_INDEX_INVALID) return; if (! is_sg()) return; if (is_joined_state()) goto joined_state_label; return; joined_state_label: my_rpfp_nbr_sg = rpfp_nbr_sg(); if (my_rpfp_nbr_sg == NULL) return; // XXX: I don't know the RPF'(S,G) nbr if (my_rpfp_nbr_sg->vif_index() != vif_index) return; if (! my_rpfp_nbr_sg->is_my_addr(target_nbr_addr)) return; // `target_nbr_addr' belongs to RPF'(S,G) // Restart Join Timer if it is larger than t_override TimeVal t_override, tv_left; pim_vif = pim_mrt()->vif_find_by_vif_index(vif_index); if (pim_vif == NULL) return; t_override = pim_vif->upstream_join_timer_t_override(); join_timer().time_remaining(tv_left); if (tv_left > t_override) { // Restart the timer with `t_override' join_timer() = pim_node()->eventloop().new_oneoff_after( t_override, callback(this, &PimMre::join_timer_timeout)); } UNUSED(holdtime); } void PimMre::sg_see_prune_wc(uint32_t vif_index, const IPvX& target_nbr_addr) { PimNbr *my_rpfp_nbr_sg; PimVif *pim_vif; if (vif_index == Vif::VIF_INDEX_INVALID) return; if (! is_sg()) return; if (is_joined_state()) goto joined_state_label; return; joined_state_label: my_rpfp_nbr_sg = rpfp_nbr_sg(); if (my_rpfp_nbr_sg == NULL) return; // XXX: I don't know the RPF'(S,G) nbr if (my_rpfp_nbr_sg->vif_index() != vif_index) return; if (! my_rpfp_nbr_sg->is_my_addr(target_nbr_addr)) return; // `target_nbr_addr' belongs to RPF'(S,G) // Restart Join Timer if it is larger than t_override TimeVal t_override, tv_left; pim_vif = pim_mrt()->vif_find_by_vif_index(vif_index); if (pim_vif == NULL) return; t_override = pim_vif->upstream_join_timer_t_override(); join_timer().time_remaining(tv_left); if (tv_left > t_override) { // Restart the timer with `t_override' join_timer() = pim_node()->eventloop().new_oneoff_after( t_override, callback(this, &PimMre::join_timer_timeout)); } } void PimMre::sg_see_prune_sg_rpt(uint32_t vif_index, uint16_t holdtime, const IPvX& target_nbr_addr) { PimNbr *my_rpfp_nbr_sg; PimVif *pim_vif; if (vif_index == Vif::VIF_INDEX_INVALID) return; if (! is_sg()) return; if (is_joined_state()) goto joined_state_label; return; joined_state_label: my_rpfp_nbr_sg = rpfp_nbr_sg(); if (my_rpfp_nbr_sg == NULL) return; // XXX: I don't know the RPF'(S,G) nbr if (my_rpfp_nbr_sg->vif_index() != vif_index) return; if (! my_rpfp_nbr_sg->is_my_addr(target_nbr_addr)) return; // `target_nbr_addr' belongs to RPF'(S,G) // Restart Join Timer if it is larger than t_override TimeVal t_override, tv_left; pim_vif = pim_mrt()->vif_find_by_vif_index(vif_index); if (pim_vif == NULL) return; t_override = pim_vif->upstream_join_timer_t_override(); join_timer().time_remaining(tv_left); if (tv_left > t_override) { // Restart the timer with `t_override' join_timer() = pim_node()->eventloop().new_oneoff_after( t_override, callback(this, &PimMre::join_timer_timeout)); } UNUSED(holdtime); } void PimMre::sg_rpt_see_join_sg_rpt(uint32_t vif_index, uint16_t holdtime, const IPvX& target_nbr_addr) { PimNbr *my_rpfp_nbr_sg_rpt; if (vif_index == Vif::VIF_INDEX_INVALID) return; if (! is_sg_rpt()) return; if (is_not_pruned_state()) goto not_pruned_state_label; return; not_pruned_state_label: my_rpfp_nbr_sg_rpt = rpfp_nbr_sg_rpt(); if (my_rpfp_nbr_sg_rpt == NULL) return; // XXX: I don't know the RPF'(S,G,rpt) nbr if (my_rpfp_nbr_sg_rpt->vif_index() != vif_index) return; if (! my_rpfp_nbr_sg_rpt->is_my_addr(target_nbr_addr)) return; // `target_nbr_addr' belongs to RPF'(S,G,rpt) // Cancel Override Timer override_timer().unschedule(); // Try to remove the entry entry_try_remove(); UNUSED(holdtime); } void PimMre::sg_rpt_see_prune_sg_rpt(uint32_t vif_index, uint16_t holdtime, const IPvX& target_nbr_addr) { PimNbr *my_rpfp_nbr_sg_rpt; PimVif *pim_vif; if (vif_index == Vif::VIF_INDEX_INVALID) return; if (! is_sg_rpt()) return; if (is_not_pruned_state()) goto not_pruned_state_label; return; not_pruned_state_label: my_rpfp_nbr_sg_rpt = rpfp_nbr_sg_rpt(); if (my_rpfp_nbr_sg_rpt == NULL) return; // XXX: I don't know the RPF'(S,G,rpt) nbr if (my_rpfp_nbr_sg_rpt->vif_index() != vif_index) return; if (! my_rpfp_nbr_sg_rpt->is_my_addr(target_nbr_addr)) return; // `target_nbr_addr' belongs to RPF'(S,G,rpt) // Restart Override Timer if it is larger than t_override TimeVal t_override, tv_left; pim_vif = pim_mrt()->vif_find_by_vif_index(vif_index); if (pim_vif == NULL) return; t_override = pim_vif->upstream_join_timer_t_override(); if (override_timer().scheduled()) override_timer().time_remaining(tv_left); else tv_left = TimeVal::MAXIMUM(); if (tv_left > t_override) { // Restart the timer with `t_override' override_timer() = pim_node()->eventloop().new_oneoff_after( t_override, callback(this, &PimMre::override_timer_timeout)); } UNUSED(holdtime); } void PimMre::sg_rpt_see_prune_sg(uint32_t vif_index, uint16_t holdtime, const IPvX& target_nbr_addr) { PimNbr *my_rpfp_nbr_sg_rpt; PimVif *pim_vif; if (vif_index == Vif::VIF_INDEX_INVALID) return; if (! is_sg_rpt()) return; if (is_not_pruned_state()) goto not_pruned_state_label; return; not_pruned_state_label: my_rpfp_nbr_sg_rpt = rpfp_nbr_sg_rpt(); if (my_rpfp_nbr_sg_rpt == NULL) return; // XXX: I don't know the RPF'(S,G,rpt) nbr if (my_rpfp_nbr_sg_rpt->vif_index() != vif_index) return; if (! my_rpfp_nbr_sg_rpt->is_my_addr(target_nbr_addr)) return; // `target_nbr_addr' belongs to RPF'(S,G,rpt) // Restart Override Timer if it is larger than t_override TimeVal t_override, tv_left; pim_vif = pim_mrt()->vif_find_by_vif_index(vif_index); if (pim_vif == NULL) return; t_override = pim_vif->upstream_join_timer_t_override(); if (override_timer().scheduled()) override_timer().time_remaining(tv_left); else tv_left = TimeVal::MAXIMUM(); if (tv_left > t_override) { // Restart the timer with `t_override' override_timer() = pim_node()->eventloop().new_oneoff_after( t_override, callback(this, &PimMre::override_timer_timeout)); } UNUSED(holdtime); } // // Upstream J/P (*,*,RP) state machine // // // Note: works for (*,*,RP), (*,G), (S,G), (S,G,rpt) bool PimMre::is_join_desired_rp() const { Mifset m; m = immediate_olist_rp(); if (m.any()) return (true); else return (false); } // // Upstream J/P (*,*,RP) state machine // // Note: applies only for (*,*,RP) // Return true if state has changed, otherwise return false. bool PimMre::recompute_is_join_desired_rp() { PimNbr *pim_nbr; uint16_t join_prune_period = PIM_JOIN_PRUNE_PERIOD_DEFAULT; if (! is_rp()) return (false); if (is_not_joined_state()) goto not_joined_state_label; if (is_joined_state()) goto joined_state_label; XLOG_UNREACHABLE(); return (false); not_joined_state_label: // NotJoined state -> Joined state if (! is_join_desired_rp()) return (false); // Nothing changed // Send Join(*,*,RP) to NBR(RPF_interface(RP), MRIB.next_hop(RP)) pim_nbr = nbr_mrib_next_hop_rp(); if (pim_nbr == NULL) { if (! i_am_rp()) { XLOG_WARNING("JoinDesired(*,*,RP) = true: " "upstream neighbor for RP %s: not found", cstring(*rp_addr_ptr())); } } else { bool is_new_group = false; // Group together all (*,*,RP) entries pim_nbr->jp_entry_add(*rp_addr_ptr(), IPvX::MULTICAST_BASE(family()), IPvX::ip_multicast_base_address_mask_len(family()), MRT_ENTRY_RP, ACTION_JOIN, pim_nbr->pim_vif()->join_prune_holdtime().get(), is_new_group); join_prune_period = pim_nbr->pim_vif()->join_prune_period().get(); } // Set Join Timer to t_periodic join_timer() = pim_node()->eventloop().new_oneoff_after( TimeVal(join_prune_period, 0), callback(this, &PimMre::join_timer_timeout)); // Set the new state set_joined_state(); return (true); joined_state_label: // Joined state -> NotJoined state if (is_join_desired_rp()) return (false); // Nothing changed // Send Prune(*,*,RP) to NBR(RPF_interface(RP), MRIB.next_hop(RP)) pim_nbr = nbr_mrib_next_hop_rp(); if (pim_nbr == NULL) { if (! i_am_rp()) { XLOG_WARNING("JoinDesired(*,*,RP) = false: " "upstream neighbor for RP %s: not found", cstring(*rp_addr_ptr())); } } else { bool is_new_group = false; // Group together all (*,*,RP) entries pim_nbr->jp_entry_add(*rp_addr_ptr(), IPvX::MULTICAST_BASE(family()), IPvX::ip_multicast_base_address_mask_len(family()), MRT_ENTRY_RP, ACTION_PRUNE, pim_nbr->pim_vif()->join_prune_holdtime().get(), is_new_group); } // Cancel Join Timer join_timer().unschedule(); // Set the new state set_not_joined_state(); entry_try_remove(); return (true); } // // Upstream J/P (*,G) state machine // // Note: works for (*,G), (S,G), (S,G,rpt) bool PimMre::is_join_desired_wc() const { Mifset m; uint32_t vif_index; const PimMre *pim_mre_wc = NULL; m = immediate_olist_wc(); if (m.any()) return (true); vif_index = rpf_interface_rp(); if (vif_index == Vif::VIF_INDEX_INVALID) return (false); do { if (is_wc()) { pim_mre_wc = this; break; } if (is_sg() || is_sg_rpt()) { pim_mre_wc = wc_entry(); break; } break; } while (false); if (is_join_desired_rp() && ((pim_mre_wc != NULL) && (pim_mre_wc->assert_winner_metric_wc(vif_index) != NULL))) return (true); else return (false); } // // Upstream J/P (*,G) state machine // // Note: applies only for (*,G) // Return true if state has changed, otherwise return false. bool PimMre::recompute_is_join_desired_wc() { PimNbr *pim_nbr; uint16_t join_prune_period = PIM_JOIN_PRUNE_PERIOD_DEFAULT; const IPvX *my_rp_addr_ptr = NULL; if (! is_wc()) return (false); if (is_not_joined_state()) goto not_joined_state_label; if (is_joined_state()) goto joined_state_label; XLOG_UNREACHABLE(); return (false); not_joined_state_label: // NotJoined state -> Joined state if (! is_join_desired_wc()) return (false); // Nothing changed // Send Join(*,G) to RPF'(*,G) my_rp_addr_ptr = rp_addr_ptr(); if (my_rp_addr_ptr == NULL) { XLOG_WARNING("JoinDesired(*,G) = true: " "RP for group %s: not found", cstring(group_addr())); } else { // We have RP pim_nbr = rpfp_nbr_wc(); if (pim_nbr == NULL) { if (! i_am_rp()) { XLOG_WARNING("JoinDesired(*,G) = true: " "upstream neighbor for RP %s for group %s: not found", cstring(*my_rp_addr_ptr), cstring(group_addr())); } } else { bool is_new_group = false; // Allow merging entries for same G pim_nbr->jp_entry_add(*my_rp_addr_ptr, group_addr(), IPvX::addr_bitlen(family()), MRT_ENTRY_WC, ACTION_JOIN, pim_nbr->pim_vif()->join_prune_holdtime().get(), is_new_group); join_prune_period = pim_nbr->pim_vif()->join_prune_period().get(); } } // Set Join Timer to t_periodic join_timer() = pim_node()->eventloop().new_oneoff_after( TimeVal(join_prune_period, 0), callback(this, &PimMre::join_timer_timeout)); // Set the new state set_joined_state(); return (true); joined_state_label: // Joined state -> NotJoined state if (is_join_desired_wc()) return (false); // Nothing changed // Send Prune(*,G) to RPF'(*,G) my_rp_addr_ptr = rp_addr_ptr(); if (my_rp_addr_ptr == NULL) { XLOG_WARNING("JoinDesired(*,G) = false: " "RP for group %s: not found", cstring(group_addr())); } else { // We have RP pim_nbr = rpfp_nbr_wc(); if (pim_nbr == NULL) { if (! i_am_rp()) { XLOG_WARNING("JoinDesired(*,G) = false: " "upstream neighbor for RP %s for group %s: not found", rp_addr_string().c_str(), cstring(group_addr())); } } else { bool is_new_group = false; // Allow merging entries for same G pim_nbr->jp_entry_add(*my_rp_addr_ptr, group_addr(), IPvX::addr_bitlen(family()), MRT_ENTRY_WC, ACTION_PRUNE, pim_nbr->pim_vif()->join_prune_holdtime().get(), is_new_group); } } // Cancel Join Timer join_timer().unschedule(); // Set the new state set_not_joined_state(); entry_try_remove(); return (true); } // // Upstream J/P (*,*,RP) and (*,G) state machine // // Note: works for (*,G), (S,G), (S,G,rpt) bool PimMre::is_rpt_join_desired_g() const { return (is_join_desired_wc() || is_join_desired_rp()); } // // Upstream J/P (S,G) state machine // // Note: works only for (S,G) bool PimMre::is_join_desired_sg() const { Mifset m; if (! is_sg()) return (false); m = immediate_olist_sg(); if (m.any()) return (true); m = inherited_olist_sg(); if (is_keepalive_timer_running() && m.any()) return (true); else return (false); } // // Upstream J/P (S,G) state machine // // Note: applies only for (S,G) // Return true if state has changed, otherwise return false. bool PimMre::recompute_is_join_desired_sg() { PimNbr *pim_nbr; uint16_t join_prune_period = PIM_JOIN_PRUNE_PERIOD_DEFAULT; if (! is_sg()) return (false); if (is_not_joined_state()) goto not_joined_state_label; if (is_joined_state()) goto joined_state_label; XLOG_UNREACHABLE(); return (false); not_joined_state_label: // NotJoined state -> Joined state if (! is_join_desired_sg()) return (false); // Nothing changed // Send Join(S,G) to RPF'(S,G) pim_nbr = rpfp_nbr_sg(); if (pim_nbr == NULL) { if (! is_directly_connected_s()) { XLOG_WARNING("JoinDesired(S,G) = true: " "upstream neighbor for source %s and group %s: not found", cstring(source_addr()), cstring(group_addr())); } } else { bool is_new_group = false; // Allow merging entries for same G pim_nbr->jp_entry_add(source_addr(), group_addr(), IPvX::addr_bitlen(family()), MRT_ENTRY_SG, ACTION_JOIN, pim_nbr->pim_vif()->join_prune_holdtime().get(), is_new_group); join_prune_period = pim_nbr->pim_vif()->join_prune_period().get(); } // Set Join Timer to t_periodic join_timer() = pim_node()->eventloop().new_oneoff_after( TimeVal(join_prune_period, 0), callback(this, &PimMre::join_timer_timeout)); // Set the new state set_joined_state(); return (true); joined_state_label: // Joined state -> NotJoined state if (is_join_desired_sg()) return (false); // Nothing changed // Send Prune(S,G) to RPF'(S,G) pim_nbr = rpfp_nbr_sg(); if (pim_nbr == NULL) { if (! is_directly_connected_s()) { XLOG_WARNING("JoinDesired(S,G) = false: " "upstream neighbor for source %s and group %s: not found", cstring(source_addr()), cstring(group_addr())); } } else { bool is_new_group = false; // Allow merging entries for same group pim_nbr->jp_entry_add(source_addr(), group_addr(), IPvX::addr_bitlen(family()), MRT_ENTRY_SG, ACTION_PRUNE, pim_nbr->pim_vif()->join_prune_holdtime().get(), is_new_group); } // Cancel Join Timer join_timer().unschedule(); // Set SPTbit(S,G) to FALSE set_spt(false); // Set the new state set_not_joined_state(); entry_try_remove(); return (true); } // // Upstream J/P (S,G,rpt) state machine // // Note: works only for (S,G,rpt) bool PimMre::is_prune_desired_sg_rpt() const { Mifset m; PimMre *pim_mre_sg; if (! is_sg_rpt()) return (false); if (! is_rpt_join_desired_g()) return (false); m = inherited_olist_sg_rpt(); if (m.none()) return (true); pim_mre_sg = sg_entry(); if (pim_mre_sg == NULL) return (false); if (pim_mre_sg->is_spt() && (rpfp_nbr_wc() != pim_mre_sg->rpfp_nbr_sg())) { return (true); } return (false); } // // Upstream J/P (S,G,rpt) state machine // // Note: applies only for (S,G,rpt) // Return true if state has changed, otherwise return false. bool PimMre::recompute_is_prune_desired_sg_rpt() { PimNbr *pim_nbr; const IPvX *my_rp_addr_ptr = NULL; if (! is_sg_rpt()) return (false); if (is_rpt_not_joined_state()) goto rpt_not_joined_state_label; if (is_pruned_state()) goto pruned_state_label; if (is_not_pruned_state()) goto not_pruned_state_label; XLOG_UNREACHABLE(); return (false); rpt_not_joined_state_label: // RPTNotJoined state -> [Pruned state] if (! is_prune_desired_sg_rpt()) return (false); // Nothing changed // PruneDesired(S,G,rpt) -> true // Set the new state set_pruned_state(); return (true); pruned_state_label: // Pruned state -> [RPTNotJoined state] [NotPruned state] if (is_prune_desired_sg_rpt()) return (false); // Nothing changed // PruneDesired(S,G,rpt) -> false // XXX: RPTJoinDesired(G) should be true, otherwise this is // a different state transition. if (! is_rpt_join_desired_g()) { // XXX: this situation should have been handled earlier by // recompute_is_rpt_join_desired_g(), but anyway, just in case.... set_rpt_not_joined_state(); entry_try_remove(); return (true); } // RPTJoinDesired(G) == true // Send Join(S,G,rpt) to RPF'(S,G,rpt) my_rp_addr_ptr = rp_addr_ptr(); if (my_rp_addr_ptr == NULL) { XLOG_WARNING("PruneDesired(S,G,rpt) = false: " "RP for group %s: not found", cstring(group_addr())); } else { // We have RP pim_nbr = rpfp_nbr_sg_rpt(); if (pim_nbr == NULL) { if (! i_am_rp()) { XLOG_WARNING("PruneDesired(S,G,rpt) = false: " "upstream neighbor for RP %s for " "source %s and group %s: not found", cstring(*my_rp_addr_ptr), cstring(source_addr()), cstring(group_addr())); } } else { bool is_new_group = false; // Allow merging entries for same G pim_nbr->jp_entry_add(source_addr(), group_addr(), IPvX::addr_bitlen(family()), MRT_ENTRY_SG_RPT, ACTION_JOIN, pim_nbr->pim_vif()->join_prune_holdtime().get(), is_new_group); } } set_not_pruned_state(); entry_try_remove(); return (true); not_pruned_state_label: // NotPruned state -> [Pruned state] if (! is_prune_desired_sg_rpt()) return (false); // Nothing changed // PruneDesired(S,G,rpt) -> true // Send Prune(S,G,rpt) to RPF'(S,G,rpt) my_rp_addr_ptr = rp_addr_ptr(); if (my_rp_addr_ptr == NULL) { XLOG_WARNING("PruneDesired(S,G,rpt) = true: " "RP for group %s: not found", cstring(group_addr())); } else { // We have RP pim_nbr = rpfp_nbr_sg_rpt(); if (pim_nbr == NULL) { if (! i_am_rp()) { XLOG_WARNING("PruneDesired(S,G,rpt) = true: " "upstream neighbor for RP %s for " "source %s and group %s: not found", cstring(*my_rp_addr_ptr), cstring(source_addr()), cstring(group_addr())); } } else { bool is_new_group = false; // Allow merging entries for same G pim_nbr->jp_entry_add(source_addr(), group_addr(), IPvX::addr_bitlen(family()), MRT_ENTRY_SG_RPT, ACTION_PRUNE, pim_nbr->pim_vif()->join_prune_holdtime().get(), is_new_group); } } // Cancel Override Timer override_timer().unschedule(); set_pruned_state(); // XXX: no need to try to remove the (S,G,rpt) routing state, because // it is in Pruned state. return (true); } // // Upstream J/P (S,G,rpt) state machine (recomputed via (S,G) state) // // Note: applies only for (S,G) // Return true if state has changed, otherwise return false. bool PimMre::recompute_is_prune_desired_sg_rpt_sg() { PimMre *pim_mre_sg_rpt; bool ret_value = false; if (! is_sg()) return (false); pim_mre_sg_rpt = sg_rpt_entry(); // // Try to recompute PruneDesired(S,G,rpt) indirectly through the // (S,G,rpt) routing entry (if exists). // if (pim_mre_sg_rpt != NULL) return (pim_mre_sg_rpt->recompute_is_prune_desired_sg_rpt()); // // The (S,G,rpt) routing entry doesn't exist, hence create it // and then use it to recompute PruneDesired(S,G,rpt) // pim_mre_sg_rpt = pim_mrt()->pim_mre_find(source_addr(), group_addr(), PIM_MRE_SG_RPT, PIM_MRE_SG_RPT); if (pim_mre_sg_rpt == NULL) { XLOG_UNREACHABLE(); XLOG_ERROR("INTERNAL PimMrt ERROR: " "cannot create entry for (%s, %s) create_flags = %#x", cstring(source_addr()), cstring(group_addr()), PIM_MRE_SG_RPT); return (false); } ret_value = pim_mre_sg_rpt->recompute_is_prune_desired_sg_rpt(); // // Try to remove the (S,G,rpt) entry that was just created (in case // it is not needed). // pim_mre_sg_rpt->entry_try_remove(); return (ret_value); } // // Note: applies only for (S,G,rpt) // Return true if state has changed, otherwise return false. bool PimMre::recompute_is_rpt_join_desired_g() { if (! is_sg_rpt()) // XXX: Yes, applies only to (S,G,rpt) return (false); if (is_rpt_not_joined_state()) goto rpt_not_joined_state_label; if (is_pruned_state()) goto pruned_state_label; if (is_not_pruned_state()) goto not_pruned_state_label; XLOG_UNREACHABLE(); return (false); rpt_not_joined_state_label: // RPTNotJoined state return (false); // Nothing changed pruned_state_label: // Pruned state -> [RPTNotJoined state] if (is_rpt_join_desired_g()) return (false); // Nothing changed // RPTJoinDesired(G) -> false set_rpt_not_joined_state(); entry_try_remove(); return (true); not_pruned_state_label: // NotPruned state -> [RPTNotJoined state] if (is_rpt_join_desired_g()) return (false); // Nothing changed // RPTJoinDesired(G) -> false override_timer().unschedule(); set_rpt_not_joined_state(); entry_try_remove(); return (true); } // Note: applies for (S,G,rpt) // Return true if state has changed, otherwise return false. bool PimMre::recompute_inherited_olist_sg_rpt() { if (! is_sg_rpt()) return (false); if (is_rpt_not_joined_state()) goto rpt_not_joined_state_label; if (is_pruned_state()) goto pruned_state_label; if (is_not_pruned_state()) goto not_pruned_state_label; XLOG_UNREACHABLE(); return (false); rpt_not_joined_state_label: // RPTNotJoined state -> [NotPruned state] if (! inherited_olist_sg_rpt().any()) return (false); // Nothing changed // inherited_olist(S,G,rpt)->non-NULL set_not_pruned_state(); entry_try_remove(); return (true); pruned_state_label: // Pruned state return (false); // Nothing changed not_pruned_state_label: return (false); // Nothing changed } void PimMre::join_timer_timeout() { PimNbr *pim_nbr = NULL; uint16_t join_prune_period = PIM_JOIN_PRUNE_PERIOD_DEFAULT; const IPvX *my_rp_addr_ptr = NULL; if (is_sg()) goto sg_entry_label; if (is_wc()) goto wc_entry_label; if (is_rp()) goto rp_entry_label; // TODO: XXX: implement the rest if needed // TODO: XXX: PAVPAVPAV: what about is_sg_rpt() ? return; rp_entry_label: // (*,*,RP) state if (! is_joined_state()) return; // Wrong state TODO: trigger state deletion? // Joined state // Send Join(*,*,RP) to NBR(RPF_interface(RP), MRIB.next_hop(RP)) pim_nbr = nbr_mrib_next_hop_rp(); if (pim_nbr == NULL) { if (! i_am_rp()) { XLOG_WARNING("JoinDesired(*,*,RP) = true: " "upstream neighbor for RP %s: not found", cstring(*rp_addr_ptr())); } } else { bool is_new_group = false; // Group together all (*,*,RP) entries pim_nbr->jp_entry_add(*rp_addr_ptr(), IPvX::MULTICAST_BASE(family()), IPvX::ip_multicast_base_address_mask_len(family()), MRT_ENTRY_RP, ACTION_JOIN, pim_nbr->pim_vif()->join_prune_holdtime().get(), is_new_group); join_prune_period = pim_nbr->pim_vif()->join_prune_period().get(); } // Set Join Timer to t_periodic join_timer() = pim_node()->eventloop().new_oneoff_after( TimeVal(join_prune_period, 0), callback(this, &PimMre::join_timer_timeout)); return; wc_entry_label: // (*,G) state if (! is_joined_state()) return; // Wrong state TODO: trigger state deletion? // Joined state // Send Join(*,G) to RPF'(*,G) my_rp_addr_ptr = rp_addr_ptr(); if (my_rp_addr_ptr == NULL) { XLOG_WARNING("JoinDesired(*,G) = true: " "RP for group %s: not found", cstring(group_addr())); } else { // We have RP pim_nbr = rpfp_nbr_wc(); if (pim_nbr == NULL) { if (! i_am_rp()) { XLOG_WARNING("JoinDesired(*,G) = true: " "upstream neighbor for RP %s for group %s: not found", cstring(*my_rp_addr_ptr), cstring(group_addr())); } } else { bool is_new_group = false; // Allow merging entries for same G pim_nbr->jp_entry_add(*my_rp_addr_ptr, group_addr(), IPvX::addr_bitlen(family()), MRT_ENTRY_WC, ACTION_JOIN, pim_nbr->pim_vif()->join_prune_holdtime().get(), is_new_group); join_prune_period = pim_nbr->pim_vif()->join_prune_period().get(); } } // Set Join Timer to t_periodic join_timer() = pim_node()->eventloop().new_oneoff_after( TimeVal(join_prune_period, 0), callback(this, &PimMre::join_timer_timeout)); return; sg_entry_label: // (S,G) state if (! is_joined_state()) return; // Wrong state TODO: trigger state deletion? // Joined state // Send Join(S,G) to RPF'(S,G) pim_nbr = rpfp_nbr_sg(); if (pim_nbr == NULL) { if (! is_directly_connected_s()) { XLOG_WARNING("JoinDesired(S,G) = true: " "upstream neighbor for source %s and group %s: not found", cstring(source_addr()), cstring(group_addr())); } } else { bool is_new_group = false; // Allow merging entries for same group pim_nbr->jp_entry_add(source_addr(), group_addr(), IPvX::addr_bitlen(family()), MRT_ENTRY_SG, ACTION_JOIN, pim_nbr->pim_vif()->join_prune_holdtime().get(), is_new_group); join_prune_period = pim_nbr->pim_vif()->join_prune_period().get(); } // Set Join Timer to t_periodic join_timer() = pim_node()->eventloop().new_oneoff_after( TimeVal(join_prune_period, 0), callback(this, &PimMre::join_timer_timeout)); return; } void PimMre::override_timer_timeout() { PimNbr *pim_nbr = NULL; PimNbr *pim_nbr_rpfp_nbr_wc = NULL; if (is_sg_rpt()) goto sg_rpt_entry_label; // TODO: XXX: implement the rest if needed return; sg_rpt_entry_label: // (S,G,rpt) state if (! is_not_pruned_state()) goto return_label; // Wrong state // NotPruned state // Send Join(S,G,rpt) to RPF'(S,G,rpt) pim_nbr = rpfp_nbr_sg_rpt(); pim_nbr_rpfp_nbr_wc = rpfp_nbr_wc(); if (pim_nbr == NULL) { XLOG_WARNING("Join(S,G,rpt) = true: " "upstream RPT neighbor for RP %s for " "source %s group %s: not found", rp_addr_string().c_str(), cstring(source_addr()), cstring(group_addr())); } else if (pim_nbr != pim_nbr_rpfp_nbr_wc) { // RPF'(S,G,rpt) != RPF'(*,G) // Ignore. goto return_label; } else { // RPF'(S,G,rpt) == RPF'(*,G) // Send Join(S,G,rpt) to RPF'(S,G,rpt) bool is_new_group = false; // Allow merging entries for same group pim_nbr->jp_entry_add(source_addr(), group_addr(), IPvX::addr_bitlen(family()), MRT_ENTRY_SG_RPT, ACTION_JOIN, pim_nbr->pim_vif()->join_prune_holdtime().get(), is_new_group); } return_label: if (is_sg_rpt()) { entry_try_remove(); } } xorp/pim/pim_mre_task.hh0000664000076400007640000002227311634760770015461 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __PIM_PIM_MRE_TASK_HH__ #define __PIM_PIM_MRE_TASK_HH__ // // PIM Multicast Routing Entry task definitions. // #include "libxorp/time_slice.hh" #include "pim_mre.hh" #include "pim_mre_track_state.hh" // // Constants definitions // // // Structures/classes, typedefs and macros // class PimMrt; // Task for PIM-specific Multicast Routing Entry class PimMreTask { public: PimMreTask(PimMrt* pim_mrt, PimMreTrackState::input_state_t input_state); ~PimMreTask(); // General info: PimNode, PimMrt, family, etc. PimNode* pim_node() const; PimMrt* pim_mrt() const { return _pim_mrt; } int family() const; bool run_task(); bool run_task_rp(); bool run_task_wc(); bool run_task_sg_sg_rpt(); bool run_task_mfc(); void perform_pim_mre_actions(PimMre *pim_mre); void perform_pim_mre_sg_sg_rpt_actions(PimMre *pim_mre_sg, PimMre *pim_mre_sg_rpt); void perform_pim_mfc_actions(PimMfc *pim_mfc); void perform_pim_mfc_actions(const IPvX& source_addr, const IPvX& group_addr); void add_pim_mre(PimMre *pim_mre); void add_pim_mre_delete(PimMre *pim_mre); void add_pim_mfc(PimMfc *pim_mfc); void add_pim_mfc_delete(PimMfc *pim_mfc); void add_mrib_delete_list(const list& mrib_list); // // (*,*,RP) state setup // void set_rp_addr_rp(const IPvX& rp_addr) { _rp_addr_rp = rp_addr; _is_set_rp_addr_rp = true; } void set_rp_addr_prefix_rp(const IPvXNet& rp_addr_prefix) { _rp_addr_prefix_rp = rp_addr_prefix; _is_set_rp_addr_prefix_rp = true; } // // (*,G) state setup // void set_group_addr_wc(const IPvX& group_addr) { _group_addr_wc = group_addr; _is_set_group_addr_wc = true; } void set_rp_addr_wc(const IPvX& rp_addr) { _rp_addr_wc = rp_addr; _is_set_rp_addr_wc = true; } void set_group_addr_prefix_wc(const IPvXNet& group_addr_prefix) { _group_addr_prefix_wc = group_addr_prefix; _is_set_group_addr_prefix_wc = true; } // // (S,G) and (S,G,rpt) state setup // void set_source_addr_sg_sg_rpt(const IPvX& source_addr) { _source_addr_sg_sg_rpt = source_addr; _is_set_source_addr_sg_sg_rpt = true; } void set_group_addr_sg_sg_rpt(const IPvX& group_addr) { _group_addr_sg_sg_rpt = group_addr; _is_set_group_addr_sg_sg_rpt = true; } void set_source_addr_prefix_sg_sg_rpt(const IPvXNet& addr_prefix) { _source_addr_prefix_sg_sg_rpt = addr_prefix; _is_set_source_addr_prefix_sg_sg_rpt = true; } void set_rp_addr_sg_sg_rpt(const IPvX& rp_addr) { _rp_addr_sg_sg_rpt = rp_addr; _is_set_rp_addr_sg_sg_rpt = true; } // // PimMfc state setup // void set_source_addr_mfc(const IPvX& source_addr) { _source_addr_mfc = source_addr; _is_set_source_addr_mfc = true; } void set_group_addr_mfc(const IPvX& group_addr) { _group_addr_mfc = group_addr; _is_set_group_addr_mfc = true; } void set_source_addr_prefix_mfc(const IPvXNet& addr_prefix) { _source_addr_prefix_mfc = addr_prefix; _is_set_source_addr_prefix_mfc = true; } void set_rp_addr_mfc(const IPvX& rp_addr) { _rp_addr_mfc = rp_addr; _is_set_rp_addr_mfc = true; } // // Other related setup // void set_pim_nbr_addr_rp(const IPvX& v) { _pim_nbr_addr = v; _is_set_pim_nbr_addr_rp = true; } void set_pim_nbr_addr_wc(const IPvX& v) { _pim_nbr_addr = v; _is_set_pim_nbr_addr_wc = true; } void set_pim_nbr_addr_sg_sg_rpt(const IPvX& v) { _pim_nbr_addr = v; _is_set_pim_nbr_addr_sg_sg_rpt = true; } void set_vif_index(uint32_t v) { _vif_index = v; } void set_addr_arg(const IPvX& v) { _addr_arg = v; } // // Misc. information // uint32_t vif_index() const { return (_vif_index); } const IPvX& addr_arg() const { return (_addr_arg); } PimMreTrackState::input_state_t input_state() const { return (_input_state); } private: // Private state PimMrt* _pim_mrt; // The PIM MRT list _action_list_rp; // The list of (*,*,RP) actions list _action_list_wc; // The list of (*,G) actions list _action_list_sg_sg_rpt; // The list of (S,G) and // (S,G,rpt) actions list _action_list_mfc; // The list of MFC actions TimeSlice _time_slice; // The time slice const PimMreTrackState::input_state_t _input_state; // The input state // // (*,*,RP) related state // bool _is_set_rp_addr_rp; IPvX _rp_addr_rp; bool _is_set_rp_addr_prefix_rp; IPvXNet _rp_addr_prefix_rp; list _pim_mre_rp_list; // (*,*,RP) entries to process list _pim_mre_rp_processed_list; // (*,*,RP) processed entries list _pim_mre_rp_delete_list; // (*,*,RP) entries to delete // State to continue the processing with the next time slice bool _is_processing_rp; bool _is_processing_rp_addr_rp; IPvX _processing_rp_addr_rp; // // (*,G) related state // bool _is_set_group_addr_wc; IPvX _group_addr_wc; bool _is_set_rp_addr_wc; IPvX _rp_addr_wc; bool _is_set_group_addr_prefix_wc; IPvXNet _group_addr_prefix_wc; list _pim_mre_wc_list; // (*,G) entries to process list _pim_mre_wc_processed_list; // (*,G) processed entries list _pim_mre_wc_delete_list; // (*,G) entries to delete // State to continue the processing with the next time slice bool _is_processing_wc; bool _is_processing_rp_addr_wc; IPvX _processing_rp_addr_wc; bool _is_processing_group_addr_wc; IPvX _processing_group_addr_wc; // // (S,G) and (S,G,rpt) related state // bool _is_set_source_addr_sg_sg_rpt; IPvX _source_addr_sg_sg_rpt; bool _is_set_group_addr_sg_sg_rpt; IPvX _group_addr_sg_sg_rpt; bool _is_set_source_addr_prefix_sg_sg_rpt; IPvXNet _source_addr_prefix_sg_sg_rpt; bool _is_set_rp_addr_sg_sg_rpt; IPvX _rp_addr_sg_sg_rpt; list _pim_mre_sg_list; // (S,G) entries to process list _pim_mre_sg_processed_list; // (S,G) processed entries list _pim_mre_sg_delete_list; // (S,G) entries to delete list _pim_mre_sg_rpt_list; // (S,G,rpt) entries to process list _pim_mre_sg_rpt_processed_list;// (S,G,rpt) processed entries list _pim_mre_sg_rpt_delete_list; // (S,G,rpt) entries to delete // State to continue the processing with the next time slice bool _is_processing_sg_sg_rpt; bool _is_processing_sg_source_addr_sg_sg_rpt; IPvX _processing_sg_source_addr_sg_sg_rpt; bool _is_processing_sg_rpt_source_addr_sg_sg_rpt; IPvX _processing_sg_rpt_source_addr_sg_sg_rpt; bool _is_processing_sg_group_addr_sg_sg_rpt; IPvX _processing_sg_group_addr_sg_sg_rpt; bool _is_processing_sg_rpt_group_addr_sg_sg_rpt; IPvX _processing_sg_rpt_group_addr_sg_sg_rpt; bool _is_processing_rp_addr_sg; bool _is_processing_rp_addr_sg_rpt; IPvX _processing_rp_addr_sg_sg_rpt; // // PimMfc related state // bool _is_set_source_addr_mfc; IPvX _source_addr_mfc; bool _is_set_group_addr_mfc; IPvX _group_addr_mfc; bool _is_set_source_addr_prefix_mfc; IPvXNet _source_addr_prefix_mfc; bool _is_set_rp_addr_mfc; IPvX _rp_addr_mfc; list _pim_mfc_list; // PimMfc entries to process list _pim_mfc_processed_list; // PimMfc processed entries list _pim_mfc_delete_list; // PimMfc entries to delete // State to continue the processing with the next time slice bool _is_processing_mfc; bool _is_processing_source_addr_mfc; IPvX _processing_source_addr_mfc; bool _is_processing_group_addr_mfc; IPvX _processing_group_addr_mfc; bool _is_processing_rp_addr_mfc; IPvX _processing_rp_addr_mfc; // // (*,*,RP), (*,G), (S,G), (S,G,rpt) PimNbr related state // bool _is_set_pim_nbr_addr_rp; bool _is_set_pim_nbr_addr_wc; bool _is_set_pim_nbr_addr_sg_sg_rpt; IPvX _pim_nbr_addr; bool _is_processing_pim_nbr_addr_rp; bool _is_processing_pim_nbr_addr_wc; bool _is_processing_pim_nbr_addr_sg; bool _is_processing_pim_nbr_addr_sg_rpt; // The 'occasionally-used' argument(s). uint32_t _vif_index; IPvX _addr_arg; // // Mrib related state // list _mrib_delete_list; }; #endif // __PIM_PIM_MRE_TASK_HH__ xorp/pim/pim_mrt_task.cc0000664000076400007640000010053011635757530015460 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // PIM Multicast Routing Table task-related implementation. // #include "pim_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvx.hh" #include "pim_mfc.hh" #include "pim_mre_task.hh" #include "pim_mrt.hh" #include "pim_node.hh" #include "pim_vif.hh" void PimMrt::add_task(PimMreTask *pim_mre_task) { PimVif *pim_vif; _pim_mre_task_list.push_back(pim_mre_task); // // Record if a PimVif is in use by this task // pim_vif = pim_node()->vif_find_by_vif_index(pim_mre_task->vif_index()); if (pim_vif != NULL) { pim_vif->incr_usage_by_pim_mre_task(); } schedule_task(); } void PimMrt::delete_task(PimMreTask *pim_mre_task) { PimVif *pim_vif; list::iterator iter; list& task_list = pim_mre_task_list(); // // Remove from the list of tasks // iter = find(task_list.begin(), task_list.end(), pim_mre_task); if (iter == task_list.end()) return; // Not found task_list.erase(iter); // // Record if a PimVif is not in use anymore by this task // pim_vif = pim_node()->vif_find_by_vif_index(pim_mre_task->vif_index()); if (pim_vif != NULL) { pim_vif->decr_usage_by_pim_mre_task(); } } // // (Re)schedule the first task to run (again), unless it is scheduled already // void PimMrt::schedule_task() { if (_pim_mre_task_timer.scheduled()) return; // The timer was already scheduled if (_pim_mre_task_list.empty()) return; // No tasks to schedule _pim_mre_task_timer = pim_node()->eventloop().new_oneoff_after( TimeVal(0, 1), callback(this, &PimMrt::pim_mre_task_timer_timeout)); } // // A timeout handler to process (or continue processing) a task. // void PimMrt::pim_mre_task_timer_timeout() { PimMreTask *pim_mre_task; if (_pim_mre_task_list.empty()) return; // No more tasks to process pim_mre_task = _pim_mre_task_list.front(); pim_mre_task->run_task(); schedule_task(); } void PimMrt::add_task_rp_changed(const IPvX& affected_rp_addr) { PimMreTask *pim_mre_task; do { // Schedule the RP-related changes pim_mre_task = new PimMreTask(this, PimMreTrackState::INPUT_STATE_RP_CHANGED); pim_mre_task->set_rp_addr_rp(affected_rp_addr); add_task(pim_mre_task); } while (false); } void PimMrt::add_task_mrib_changed(const IPvXNet& modified_prefix_addr) { PimMreTask *pim_mre_task; do { // Schedule the RP-related changes pim_mre_task = new PimMreTask(this, PimMreTrackState::INPUT_STATE_MRIB_RP_CHANGED); pim_mre_task->set_rp_addr_prefix_rp(modified_prefix_addr); add_task(pim_mre_task); } while (false); do { // Schedule the S-related changes pim_mre_task = new PimMreTask(this, PimMreTrackState::INPUT_STATE_MRIB_S_CHANGED); pim_mre_task->set_source_addr_prefix_sg_sg_rpt(modified_prefix_addr); add_task(pim_mre_task); } while (false); } void PimMrt::add_task_delete_mrib_entries(const list& mrib_list) { PimMreTask *pim_mre_task = NULL; // // If the lastest task is same, just // reuse that task. Otherwise, allocate a new task. // list::reverse_iterator iter; iter = pim_mre_task_list().rbegin(); if (iter != pim_mre_task_list().rend()) { pim_mre_task = *iter; if (pim_mre_task->input_state() == PimMreTrackState::INPUT_STATE_IN_REMOVE_MISC) { pim_mre_task->add_mrib_delete_list(mrib_list); return; } } do { // Schedule the deletion task pim_mre_task = new PimMreTask(this, PimMreTrackState::INPUT_STATE_IN_REMOVE_MISC); pim_mre_task->add_mrib_delete_list(mrib_list); add_task(pim_mre_task); } while (false); } // TODO: not used (not needed?) void PimMrt::add_task_nbr_mrib_next_hop_changed(const IPvXNet& modified_prefix_addr) { PimMreTask *pim_mre_task; do { // Schedule the RP-related changes pim_mre_task = new PimMreTask(this, PimMreTrackState::INPUT_STATE_NBR_MRIB_NEXT_HOP_RP_CHANGED); pim_mre_task->set_rp_addr_prefix_rp(modified_prefix_addr); add_task(pim_mre_task); } while (false); do { // Schedule the G-related changes pim_mre_task = new PimMreTask(this, PimMreTrackState::INPUT_STATE_NBR_MRIB_NEXT_HOP_RP_G_CHANGED); pim_mre_task->set_rp_addr_prefix_rp(modified_prefix_addr); add_task(pim_mre_task); } while (false); do { // Schedule the S-related changes pim_mre_task = new PimMreTask(this, PimMreTrackState::INPUT_STATE_NBR_MRIB_NEXT_HOP_S_CHANGED); pim_mre_task->set_source_addr_prefix_sg_sg_rpt(modified_prefix_addr); add_task(pim_mre_task); } while (false); } // TODO: not used (not needed?) void PimMrt::add_task_nbr_mrib_next_hop_rp_gen_id_changed(const IPvX& rp_addr) { PimMreTask *pim_mre_task; do { // Schedule the RP-related changes pim_mre_task = new PimMreTask(this, PimMreTrackState::INPUT_STATE_NBR_MRIB_NEXT_HOP_RP_GEN_ID_CHANGED); pim_mre_task->set_rp_addr_rp(rp_addr); add_task(pim_mre_task); } while (false); } void PimMrt::add_task_pim_nbr_changed(uint32_t vif_index, const IPvX& pim_nbr_addr) { PimMreTask *pim_mre_task; do { // Schedule the (*,*,RP)-related changes pim_mre_task = new PimMreTask(this, PimMreTrackState::INPUT_STATE_NBR_MRIB_NEXT_HOP_RP_CHANGED); pim_mre_task->set_pim_nbr_addr_rp(pim_nbr_addr); pim_mre_task->set_vif_index(vif_index); add_task(pim_mre_task); } while (false); do { // Schedule the (*,G)-related changes pim_mre_task = new PimMreTask(this, PimMreTrackState::INPUT_STATE_NBR_MRIB_NEXT_HOP_RP_G_CHANGED); pim_mre_task->set_pim_nbr_addr_wc(pim_nbr_addr); pim_mre_task->set_vif_index(vif_index); add_task(pim_mre_task); } while (false); do { // Schedule the (*,G)-related changes pim_mre_task = new PimMreTask(this, PimMreTrackState::INPUT_STATE_RPFP_NBR_WC_CHANGED); pim_mre_task->set_pim_nbr_addr_wc(pim_nbr_addr); pim_mre_task->set_vif_index(vif_index); add_task(pim_mre_task); } while (false); do { // Schedule the (S,G)-related changes pim_mre_task = new PimMreTask(this, PimMreTrackState::INPUT_STATE_NBR_MRIB_NEXT_HOP_S_CHANGED); pim_mre_task->set_pim_nbr_addr_sg_sg_rpt(pim_nbr_addr); pim_mre_task->set_vif_index(vif_index); add_task(pim_mre_task); } while (false); do { // Schedule the (S,G)-related changes pim_mre_task = new PimMreTask(this, PimMreTrackState::INPUT_STATE_RPFP_NBR_SG_CHANGED); pim_mre_task->set_pim_nbr_addr_sg_sg_rpt(pim_nbr_addr); pim_mre_task->set_vif_index(vif_index); add_task(pim_mre_task); } while (false); do { // Schedule the (S,G,rpt)-related changes pim_mre_task = new PimMreTask(this, PimMreTrackState::INPUT_STATE_RPFP_NBR_SG_RPT_CHANGED); pim_mre_task->set_pim_nbr_addr_sg_sg_rpt(pim_nbr_addr); pim_mre_task->set_vif_index(vif_index); add_task(pim_mre_task); } while (false); } void PimMrt::add_task_pim_nbr_gen_id_changed(uint32_t vif_index, const IPvX& pim_nbr_addr) { PimMreTask *pim_mre_task; do { // Schedule the RP-related changes pim_mre_task = new PimMreTask(this, PimMreTrackState::INPUT_STATE_NBR_MRIB_NEXT_HOP_RP_GEN_ID_CHANGED); pim_mre_task->set_pim_nbr_addr_rp(pim_nbr_addr); pim_mre_task->set_vif_index(vif_index); add_task(pim_mre_task); } while (false); do { // Schedule the (*,G)-related changes pim_mre_task = new PimMreTask(this, PimMreTrackState::INPUT_STATE_RPFP_NBR_WC_GEN_ID_CHANGED); pim_mre_task->set_pim_nbr_addr_wc(pim_nbr_addr); pim_mre_task->set_vif_index(vif_index); add_task(pim_mre_task); } while (false); do { // Schedule the (*,G)-related changes pim_mre_task = new PimMreTask(this, PimMreTrackState::INPUT_STATE_ASSERT_WINNER_NBR_WC_GEN_ID_CHANGED); pim_mre_task->set_group_addr_prefix_wc(IPvXNet::ip_multicast_base_prefix(family())); pim_mre_task->set_vif_index(vif_index); pim_mre_task->set_addr_arg(pim_nbr_addr); add_task(pim_mre_task); } while (false); do { // Schedule the (S,G)-related changes pim_mre_task = new PimMreTask(this, PimMreTrackState::INPUT_STATE_RPFP_NBR_SG_GEN_ID_CHANGED); pim_mre_task->set_pim_nbr_addr_sg_sg_rpt(pim_nbr_addr); pim_mre_task->set_vif_index(vif_index); add_task(pim_mre_task); } while (false); do { // Schedule the (S,G)-related changes pim_mre_task = new PimMreTask(this, PimMreTrackState::INPUT_STATE_ASSERT_WINNER_NBR_SG_GEN_ID_CHANGED); pim_mre_task->set_source_addr_prefix_sg_sg_rpt(IPvXNet(family())); pim_mre_task->set_vif_index(vif_index); pim_mre_task->set_addr_arg(pim_nbr_addr); add_task(pim_mre_task); } while (false); } void PimMrt::add_task_assert_rpf_interface_wc(uint32_t old_rpf_interface_rp, const IPvX& group_addr) { PimMreTask *pim_mre_task; do { // Schedule the (*,G)-related changes pim_mre_task = new PimMreTask(this, PimMreTrackState::INPUT_STATE_ASSERT_RPF_INTERFACE_WC_CHANGED); pim_mre_task->set_group_addr_wc(group_addr); pim_mre_task->set_vif_index(old_rpf_interface_rp); add_task(pim_mre_task); } while (false); } void PimMrt::add_task_assert_rpf_interface_sg(uint32_t old_rpf_interface_s, const IPvX& source_addr, const IPvX& group_addr) { PimMreTask *pim_mre_task; do { // Schedule the (S,G)-related changes pim_mre_task = new PimMreTask(this, PimMreTrackState::INPUT_STATE_ASSERT_RPF_INTERFACE_SG_CHANGED); pim_mre_task->set_source_addr_sg_sg_rpt(source_addr); pim_mre_task->set_group_addr_sg_sg_rpt(group_addr); pim_mre_task->set_vif_index(old_rpf_interface_s); add_task(pim_mre_task); } while (false); } // TODO: not used (not needed?) void PimMrt::add_task_receive_join_rp(uint32_t vif_index, const IPvX& rp_addr) { PimMreTask *pim_mre_task; do { // Schedule the (*,*,RP)-related changes pim_mre_task = new PimMreTask(this, PimMreTrackState::INPUT_STATE_RECEIVE_JOIN_RP); pim_mre_task->set_rp_addr_rp(rp_addr); pim_mre_task->set_vif_index(vif_index); add_task(pim_mre_task); } while (false); } void PimMrt::add_task_receive_join_wc(uint32_t vif_index, const IPvX& group_addr) { PimMreTask *pim_mre_task; do { // Schedule the (*,G)-related changes pim_mre_task = new PimMreTask(this, PimMreTrackState::INPUT_STATE_RECEIVE_JOIN_WC); pim_mre_task->set_group_addr_wc(group_addr); pim_mre_task->set_vif_index(vif_index); add_task(pim_mre_task); } while (false); } void PimMrt::add_task_receive_join_sg(uint32_t vif_index, const IPvX& source_addr, const IPvX& group_addr) { PimMreTask *pim_mre_task; do { // Schedule the (S,G)-related changes pim_mre_task = new PimMreTask(this, PimMreTrackState::INPUT_STATE_RECEIVE_JOIN_SG); pim_mre_task->set_source_addr_sg_sg_rpt(source_addr); pim_mre_task->set_group_addr_sg_sg_rpt(group_addr); pim_mre_task->set_vif_index(vif_index); add_task(pim_mre_task); } while (false); } void PimMrt::add_task_receive_join_sg_rpt(uint32_t vif_index, const IPvX& source_addr, const IPvX& group_addr) { PimMreTask *pim_mre_task; do { // Schedule the (S,G,rpt)-related changes pim_mre_task = new PimMreTask(this, PimMreTrackState::INPUT_STATE_RECEIVE_JOIN_SG_RPT); pim_mre_task->set_source_addr_sg_sg_rpt(source_addr); pim_mre_task->set_group_addr_sg_sg_rpt(group_addr); pim_mre_task->set_vif_index(vif_index); add_task(pim_mre_task); } while (false); } void PimMrt::add_task_receive_prune_rp(uint32_t vif_index, const IPvX& rp_addr) { PimMreTask *pim_mre_task; do { // Schedule the (*,*,RP)-related changes pim_mre_task = new PimMreTask(this, PimMreTrackState::INPUT_STATE_RECEIVE_PRUNE_RP); pim_mre_task->set_rp_addr_rp(rp_addr); pim_mre_task->set_vif_index(vif_index); add_task(pim_mre_task); } while (false); } void PimMrt::add_task_receive_prune_wc(uint32_t vif_index, const IPvX& group_addr) { PimMreTask *pim_mre_task; do { // Schedule the (*,G)-related changes pim_mre_task = new PimMreTask(this, PimMreTrackState::INPUT_STATE_RECEIVE_PRUNE_WC); pim_mre_task->set_group_addr_wc(group_addr); pim_mre_task->set_vif_index(vif_index); add_task(pim_mre_task); } while (false); } void PimMrt::add_task_see_prune_wc(uint32_t vif_index, const IPvX& group_addr, const IPvX& target_nbr_addr) { PimMreTask *pim_mre_task; do { // Schedule the (*,G)-related changes pim_mre_task = new PimMreTask(this, PimMreTrackState::INPUT_STATE_SEE_PRUNE_WC); pim_mre_task->set_group_addr_wc(group_addr); pim_mre_task->set_vif_index(vif_index); pim_mre_task->set_addr_arg(target_nbr_addr); // XXX add_task(pim_mre_task); } while (false); } void PimMrt::add_task_receive_prune_sg(uint32_t vif_index, const IPvX& source_addr, const IPvX& group_addr) { PimMreTask *pim_mre_task; do { // Schedule the (S,G)-related changes pim_mre_task = new PimMreTask(this, PimMreTrackState::INPUT_STATE_RECEIVE_PRUNE_SG); pim_mre_task->set_source_addr_sg_sg_rpt(source_addr); pim_mre_task->set_group_addr_sg_sg_rpt(group_addr); pim_mre_task->set_vif_index(vif_index); add_task(pim_mre_task); } while (false); } void PimMrt::add_task_receive_prune_sg_rpt(uint32_t vif_index, const IPvX& source_addr, const IPvX& group_addr) { PimMreTask *pim_mre_task; do { // Schedule the (S,G,rpt)-related changes pim_mre_task = new PimMreTask(this, PimMreTrackState::INPUT_STATE_RECEIVE_PRUNE_SG_RPT); pim_mre_task->set_source_addr_sg_sg_rpt(source_addr); pim_mre_task->set_group_addr_sg_sg_rpt(group_addr); pim_mre_task->set_vif_index(vif_index); add_task(pim_mre_task); } while (false); } void PimMrt::add_task_receive_end_of_message_sg_rpt(uint32_t vif_index, const IPvX& group_addr) { PimMreTask *pim_mre_task; do { // Schedule the (S,G,rpt)-related changes (for a given group) pim_mre_task = new PimMreTask(this, PimMreTrackState::INPUT_STATE_RECEIVE_END_OF_MESSAGE_SG_RPT); pim_mre_task->set_group_addr_sg_sg_rpt(group_addr); pim_mre_task->set_vif_index(vif_index); add_task(pim_mre_task); } while (false); } void PimMrt::add_task_downstream_jp_state_rp(uint32_t vif_index, const IPvX& rp_addr) { PimMreTask *pim_mre_task; do { // Schedule the (*,*,RP)-related changes pim_mre_task = new PimMreTask(this, PimMreTrackState::INPUT_STATE_DOWNSTREAM_JP_STATE_RP); pim_mre_task->set_rp_addr_rp(rp_addr); pim_mre_task->set_vif_index(vif_index); add_task(pim_mre_task); } while (false); } void PimMrt::add_task_downstream_jp_state_wc(uint32_t vif_index, const IPvX& group_addr) { PimMreTask *pim_mre_task; do { // Schedule the (*,G)-related changes pim_mre_task = new PimMreTask(this, PimMreTrackState::INPUT_STATE_DOWNSTREAM_JP_STATE_WC); pim_mre_task->set_group_addr_wc(group_addr); pim_mre_task->set_vif_index(vif_index); add_task(pim_mre_task); } while (false); } void PimMrt::add_task_downstream_jp_state_sg(uint32_t vif_index, const IPvX& source_addr, const IPvX& group_addr) { PimMreTask *pim_mre_task; do { // Schedule the (S,G)-related changes pim_mre_task = new PimMreTask(this, PimMreTrackState::INPUT_STATE_DOWNSTREAM_JP_STATE_SG); pim_mre_task->set_source_addr_sg_sg_rpt(source_addr); pim_mre_task->set_group_addr_sg_sg_rpt(group_addr); pim_mre_task->set_vif_index(vif_index); add_task(pim_mre_task); } while (false); } void PimMrt::add_task_downstream_jp_state_sg_rpt(uint32_t vif_index, const IPvX& source_addr, const IPvX& group_addr) { PimMreTask *pim_mre_task; do { // Schedule the (S,G,rpt)-related changes pim_mre_task = new PimMreTask(this, PimMreTrackState::INPUT_STATE_DOWNSTREAM_JP_STATE_SG_RPT); pim_mre_task->set_source_addr_sg_sg_rpt(source_addr); pim_mre_task->set_group_addr_sg_sg_rpt(group_addr); pim_mre_task->set_vif_index(vif_index); add_task(pim_mre_task); } while (false); } void PimMrt::add_task_upstream_jp_state_sg(const IPvX& source_addr, const IPvX& group_addr) { PimMreTask *pim_mre_task; do { // Schedule the (S,G)-related changes pim_mre_task = new PimMreTask(this, PimMreTrackState::INPUT_STATE_UPSTREAM_JP_STATE_SG); pim_mre_task->set_source_addr_sg_sg_rpt(source_addr); pim_mre_task->set_group_addr_sg_sg_rpt(group_addr); add_task(pim_mre_task); } while (false); } void PimMrt::add_task_local_receiver_include_wc(uint32_t vif_index, const IPvX& group_addr) { PimMreTask *pim_mre_task; do { // Schedule the (*,G)-related changes pim_mre_task = new PimMreTask(this, PimMreTrackState::INPUT_STATE_LOCAL_RECEIVER_INCLUDE_WC); pim_mre_task->set_group_addr_wc(group_addr); pim_mre_task->set_vif_index(vif_index); add_task(pim_mre_task); } while (false); } void PimMrt::add_task_local_receiver_include_sg(uint32_t vif_index, const IPvX& source_addr, const IPvX& group_addr) { PimMreTask *pim_mre_task; do { // Schedule the (S,G)-related changes pim_mre_task = new PimMreTask(this, PimMreTrackState::INPUT_STATE_LOCAL_RECEIVER_INCLUDE_SG); pim_mre_task->set_source_addr_sg_sg_rpt(source_addr); pim_mre_task->set_group_addr_sg_sg_rpt(group_addr); pim_mre_task->set_vif_index(vif_index); add_task(pim_mre_task); } while (false); } void PimMrt::add_task_local_receiver_exclude_sg(uint32_t vif_index, const IPvX& source_addr, const IPvX& group_addr) { PimMreTask *pim_mre_task; do { // Schedule the (S,G)-related changes pim_mre_task = new PimMreTask(this, PimMreTrackState::INPUT_STATE_LOCAL_RECEIVER_EXCLUDE_SG); pim_mre_task->set_source_addr_sg_sg_rpt(source_addr); pim_mre_task->set_group_addr_sg_sg_rpt(group_addr); pim_mre_task->set_vif_index(vif_index); add_task(pim_mre_task); } while (false); } void PimMrt::add_task_assert_state_wc(uint32_t vif_index, const IPvX& group_addr) { PimMreTask *pim_mre_task; do { // Schedule the (*,G)-related changes pim_mre_task = new PimMreTask(this, PimMreTrackState::INPUT_STATE_ASSERT_STATE_WC); pim_mre_task->set_group_addr_wc(group_addr); pim_mre_task->set_vif_index(vif_index); add_task(pim_mre_task); } while (false); } void PimMrt::add_task_assert_state_sg(uint32_t vif_index, const IPvX& source_addr, const IPvX& group_addr) { PimMreTask *pim_mre_task; do { // Schedule the (S,G)-related changes pim_mre_task = new PimMreTask(this, PimMreTrackState::INPUT_STATE_ASSERT_STATE_SG); pim_mre_task->set_source_addr_sg_sg_rpt(source_addr); pim_mre_task->set_group_addr_sg_sg_rpt(group_addr); pim_mre_task->set_vif_index(vif_index); add_task(pim_mre_task); } while (false); } void PimMrt::add_task_i_am_dr(uint32_t vif_index) { PimMreTask *pim_mre_task; do { // Schedule the (*,*,RP)-related changes pim_mre_task = new PimMreTask(this, PimMreTrackState::INPUT_STATE_I_AM_DR); pim_mre_task->set_rp_addr_prefix_rp(IPvXNet(family())); pim_mre_task->set_vif_index(vif_index); add_task(pim_mre_task); } while (false); do { // Schedule the (*,G)-related changes // XXX: most of processing will be redundant pim_mre_task = new PimMreTask(this, PimMreTrackState::INPUT_STATE_I_AM_DR); pim_mre_task->set_group_addr_prefix_wc(IPvXNet::ip_multicast_base_prefix(family())); pim_mre_task->set_vif_index(vif_index); add_task(pim_mre_task); } while (false); do { // Schedule the (S,G) and (S,G,rpt)-related changes // XXX: most of processing will be redundant pim_mre_task = new PimMreTask(this, PimMreTrackState::INPUT_STATE_I_AM_DR); pim_mre_task->set_source_addr_prefix_sg_sg_rpt(IPvXNet(family())); pim_mre_task->set_vif_index(vif_index); add_task(pim_mre_task); } while (false); } void PimMrt::add_task_my_ip_address(uint32_t vif_index) { PimMreTask *pim_mre_task; do { // Schedule the (*,*,RP)-related changes pim_mre_task = new PimMreTask(this, PimMreTrackState::INPUT_STATE_MY_IP_ADDRESS); pim_mre_task->set_rp_addr_prefix_rp(IPvXNet(family())); pim_mre_task->set_vif_index(vif_index); add_task(pim_mre_task); } while (false); do { // Schedule the (*,G)-related changes // XXX: most of processing will be redundant pim_mre_task = new PimMreTask(this, PimMreTrackState::INPUT_STATE_MY_IP_ADDRESS); pim_mre_task->set_group_addr_prefix_wc(IPvXNet::ip_multicast_base_prefix(family())); pim_mre_task->set_vif_index(vif_index); add_task(pim_mre_task); } while (false); do { // Schedule the (S,G) and (S,G,rpt)-related changes // XXX: most of processing will be redundant pim_mre_task = new PimMreTask(this, PimMreTrackState::INPUT_STATE_MY_IP_ADDRESS); pim_mre_task->set_source_addr_prefix_sg_sg_rpt(IPvXNet(family())); pim_mre_task->set_vif_index(vif_index); add_task(pim_mre_task); } while (false); } void PimMrt::add_task_my_ip_subnet_address(uint32_t vif_index) { PimMreTask *pim_mre_task; do { // Schedule the (*,*,RP)-related changes pim_mre_task = new PimMreTask(this, PimMreTrackState::INPUT_STATE_MY_IP_SUBNET_ADDRESS); pim_mre_task->set_rp_addr_prefix_rp(IPvXNet(family())); pim_mre_task->set_vif_index(vif_index); add_task(pim_mre_task); } while (false); do { // Schedule the (*,G)-related changes // XXX: most of processing will be redundant pim_mre_task = new PimMreTask(this, PimMreTrackState::INPUT_STATE_MY_IP_SUBNET_ADDRESS); pim_mre_task->set_group_addr_prefix_wc(IPvXNet::ip_multicast_base_prefix(family())); pim_mre_task->set_vif_index(vif_index); add_task(pim_mre_task); } while (false); do { // Schedule the (S,G) and (S,G,rpt)-related changes // XXX: most of processing will be redundant pim_mre_task = new PimMreTask(this, PimMreTrackState::INPUT_STATE_MY_IP_SUBNET_ADDRESS); pim_mre_task->set_source_addr_prefix_sg_sg_rpt(IPvXNet(family())); pim_mre_task->set_vif_index(vif_index); add_task(pim_mre_task); } while (false); } void PimMrt::add_task_spt_switch_threshold_changed() { PimMreTask *pim_mre_task; do { // Schedule the MFC-related changes pim_mre_task = new PimMreTask(this, PimMreTrackState::INPUT_STATE_SPT_SWITCH_THRESHOLD_CHANGED_MFC); pim_mre_task->set_source_addr_prefix_mfc(IPvXNet(family())); add_task(pim_mre_task); } while (false); } void PimMrt::add_task_was_switch_to_spt_desired_sg(const IPvX& source_addr, const IPvX& group_addr) { PimMreTask *pim_mre_task; do { // Schedule the (S,G)-related changes pim_mre_task = new PimMreTask(this, PimMreTrackState::INPUT_STATE_WAS_SWITCH_TO_SPT_DESIRED_SG); pim_mre_task->set_source_addr_sg_sg_rpt(source_addr); pim_mre_task->set_group_addr_sg_sg_rpt(group_addr); add_task(pim_mre_task); } while (false); } void PimMrt::add_task_keepalive_timer_sg(const IPvX& source_addr, const IPvX& group_addr) { PimMreTask *pim_mre_task; do { // Schedule the (S,G)-related changes pim_mre_task = new PimMreTask(this, PimMreTrackState::INPUT_STATE_KEEPALIVE_TIMER_SG); pim_mre_task->set_source_addr_sg_sg_rpt(source_addr); pim_mre_task->set_group_addr_sg_sg_rpt(group_addr); add_task(pim_mre_task); } while (false); } void PimMrt::add_task_sptbit_sg(const IPvX& source_addr, const IPvX& group_addr) { PimMreTask *pim_mre_task; do { // Schedule the (S,G)-related changes pim_mre_task = new PimMreTask(this, PimMreTrackState::INPUT_STATE_SPTBIT_SG); pim_mre_task->set_source_addr_sg_sg_rpt(source_addr); pim_mre_task->set_group_addr_sg_sg_rpt(group_addr); add_task(pim_mre_task); } while (false); } void PimMrt::add_task_start_vif(uint32_t vif_index) { PimMreTask *pim_mre_task; // // The incoming interfaces // do { // Schedule the RP-related changes pim_mre_task = new PimMreTask(this, PimMreTrackState::INPUT_STATE_MRIB_RP_CHANGED); pim_mre_task->set_rp_addr_prefix_rp(IPvXNet(family())); pim_mre_task->set_vif_index(vif_index); add_task(pim_mre_task); } while (false); do { // Schedule the S-related changes pim_mre_task = new PimMreTask(this, PimMreTrackState::INPUT_STATE_MRIB_S_CHANGED); pim_mre_task->set_source_addr_prefix_sg_sg_rpt(IPvXNet(family())); pim_mre_task->set_vif_index(vif_index); add_task(pim_mre_task); } while (false); // // The outgoing interfaces // do { // Schedule the (*,*,RP)-related changes pim_mre_task = new PimMreTask(this, PimMreTrackState::INPUT_STATE_IN_START_VIF); pim_mre_task->set_rp_addr_prefix_rp(IPvXNet(family())); pim_mre_task->set_vif_index(vif_index); add_task(pim_mre_task); } while (false); do { // Schedule the (*,G)-related changes // XXX: most of processing will be redundant pim_mre_task = new PimMreTask(this, PimMreTrackState::INPUT_STATE_IN_START_VIF); pim_mre_task->set_group_addr_prefix_wc(IPvXNet::ip_multicast_base_prefix(family())); pim_mre_task->set_vif_index(vif_index); add_task(pim_mre_task); } while (false); do { // Schedule the (S,G) and (S,G,rpt)-related changes // XXX: most of processing will be redundant pim_mre_task = new PimMreTask(this, PimMreTrackState::INPUT_STATE_IN_START_VIF); pim_mre_task->set_source_addr_prefix_sg_sg_rpt(IPvXNet(family())); pim_mre_task->set_vif_index(vif_index); add_task(pim_mre_task); } while (false); } void PimMrt::add_task_stop_vif(uint32_t vif_index) { PimMreTask *pim_mre_task; // // The incoming interfaces // do { // Schedule the RP-related changes pim_mre_task = new PimMreTask(this, PimMreTrackState::INPUT_STATE_MRIB_RP_CHANGED); pim_mre_task->set_rp_addr_prefix_rp(IPvXNet(family())); pim_mre_task->set_vif_index(vif_index); add_task(pim_mre_task); } while (false); do { // Schedule the S-related changes pim_mre_task = new PimMreTask(this, PimMreTrackState::INPUT_STATE_MRIB_S_CHANGED); pim_mre_task->set_source_addr_prefix_sg_sg_rpt(IPvXNet(family())); pim_mre_task->set_vif_index(vif_index); add_task(pim_mre_task); } while (false); // // The outgoing interfaces // do { // Schedule the (*,*,RP)-related changes pim_mre_task = new PimMreTask(this, PimMreTrackState::INPUT_STATE_IN_STOP_VIF); pim_mre_task->set_rp_addr_prefix_rp(IPvXNet(family())); pim_mre_task->set_vif_index(vif_index); add_task(pim_mre_task); } while (false); do { // Schedule the (*,G)-related changes // XXX: most of processing will be redundant pim_mre_task = new PimMreTask(this, PimMreTrackState::INPUT_STATE_IN_STOP_VIF); pim_mre_task->set_group_addr_prefix_wc(IPvXNet::ip_multicast_base_prefix(family())); pim_mre_task->set_vif_index(vif_index); add_task(pim_mre_task); } while (false); do { // Schedule the (S,G) and (S,G,rpt)-related changes // XXX: most of processing will be redundant pim_mre_task = new PimMreTask(this, PimMreTrackState::INPUT_STATE_IN_STOP_VIF); pim_mre_task->set_source_addr_prefix_sg_sg_rpt(IPvXNet(family())); pim_mre_task->set_vif_index(vif_index); add_task(pim_mre_task); } while (false); } void PimMrt::add_task_add_pim_mre(PimMre *pim_mre) { PimMreTask *pim_mre_task = NULL; PimMreTrackState::input_state_t input_state = PimMreTrackState::INPUT_STATE_MAX; if (pim_mre->is_task_delete_pending()) { XLOG_UNREACHABLE(); return; } // // Compute the proper input state // do { if (pim_mre->is_rp()) { input_state = PimMreTrackState::INPUT_STATE_IN_ADD_PIM_MRE_RP; break; } if (pim_mre->is_wc()) { input_state = PimMreTrackState::INPUT_STATE_IN_ADD_PIM_MRE_WC; break; } if (pim_mre->is_sg()) { input_state = PimMreTrackState::INPUT_STATE_IN_ADD_PIM_MRE_SG; break; } if (pim_mre->is_sg_rpt()) { input_state = PimMreTrackState::INPUT_STATE_IN_ADD_PIM_MRE_SG_RPT; break; } XLOG_UNREACHABLE(); return; } while (false); // // If the lastest task is same, just // reuse that task. Otherwise, allocate a new task. // list::reverse_iterator iter; iter = pim_mre_task_list().rbegin(); if (iter != pim_mre_task_list().rend()) { pim_mre_task = *iter; if (pim_mre_task->input_state() == input_state) { pim_mre_task->add_pim_mre(pim_mre); return; } } do { // Schedule the PimMre-related changes pim_mre_task = new PimMreTask(this, input_state); pim_mre_task->add_pim_mre(pim_mre); // XXX add_task(pim_mre_task); } while (false); } void PimMrt::add_task_delete_pim_mre(PimMre *pim_mre) { PimMreTask *pim_mre_task = NULL; PimMreTrackState::input_state_t input_state = PimMreTrackState::INPUT_STATE_MAX; if (pim_mre->is_task_delete_pending()) { // The entry is already pending deletion. // Shoudn't happen, but just in case... return; } // // Compute the proper input state // do { if (pim_mre->is_rp()) { input_state = PimMreTrackState::INPUT_STATE_IN_REMOVE_PIM_MRE_RP; break; } if (pim_mre->is_wc()) { input_state = PimMreTrackState::INPUT_STATE_IN_REMOVE_PIM_MRE_WC; break; } if (pim_mre->is_sg()) { input_state = PimMreTrackState::INPUT_STATE_IN_REMOVE_PIM_MRE_SG; break; } if (pim_mre->is_sg_rpt()) { input_state = PimMreTrackState::INPUT_STATE_IN_REMOVE_PIM_MRE_SG_RPT; break; } XLOG_UNREACHABLE(); return; } while (false); // // Mark the entry as pending deletion // pim_mre->set_is_task_delete_pending(true); // // If the lastest task is same, just // reuse that task. Otherwise, allocate a new task. // list::reverse_iterator iter; iter = pim_mre_task_list().rbegin(); if (iter != pim_mre_task_list().rend()) { pim_mre_task = *iter; if (pim_mre_task->input_state() == input_state) { pim_mre_task->add_pim_mre(pim_mre); // XXX pim_mre_task->add_pim_mre_delete(pim_mre); // XXX return; } } do { // Schedule the PimMre-related changes pim_mre_task = new PimMreTask(this, input_state); pim_mre_task->add_pim_mre(pim_mre); // XXX pim_mre_task->add_pim_mre_delete(pim_mre); // XXX add_task(pim_mre_task); } while (false); } void PimMrt::add_task_delete_pim_mfc(PimMfc *pim_mfc) { PimMreTask *pim_mre_task = NULL; if (pim_mfc->is_task_delete_pending()) { // The entry is already pending deletion. // Shoudn't happen, but just in case... return; } // // Mark the entry as pending deletion // pim_mfc->set_is_task_delete_pending(true); // // If the lastest task is same, just // reuse that task. Otherwise, allocate a new task. // list::reverse_iterator iter; iter = pim_mre_task_list().rbegin(); if (iter != pim_mre_task_list().rend()) { pim_mre_task = *iter; if (pim_mre_task->input_state() == PimMreTrackState::INPUT_STATE_IN_REMOVE_PIM_MFC) { pim_mre_task->add_pim_mfc(pim_mfc); // XXX pim_mre_task->add_pim_mfc_delete(pim_mfc); // XXX return; } } do { // Schedule the PimMfc-related changes pim_mre_task = new PimMreTask(this, PimMreTrackState::INPUT_STATE_IN_REMOVE_PIM_MFC); pim_mre_task->add_pim_mfc(pim_mfc); // XXX pim_mre_task->add_pim_mfc_delete(pim_mfc); // XXX add_task(pim_mre_task); } while (false); } xorp/pim/pim_proto_hello.cc0000664000076400007640000004603711540225531016157 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // PIM PIM_HELLO messages processing. // #include "pim_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/random.h" #include "libxorp/ipvx.hh" #include "pim_node.hh" #include "pim_vif.hh" // // Exported variables // // // Local constants definitions // // // Local structures/classes, typedefs and macros // // // Local variables // // // Local functions prototypes // static bool pim_dr_is_better(PimNbr *pim_nbr1, PimNbr *pim_nbr2, bool consider_dr_priority); void PimVif::pim_hello_start() { // Generate a new Gen-ID genid().set(xorp_random() % 0xffffffffU); // On startup, I will become the PIM Designated Router pim_dr_elect(); // Schedule the first PIM_HELLO message at random in the // interval [0, hello_triggered_delay) hello_timer_start_random(hello_triggered_delay().get(), 0); } void PimVif::pim_hello_stop() { uint16_t save_holdtime = hello_holdtime().get(); string dummy_error_msg; hello_holdtime().set(0); // XXX: timeout immediately pim_hello_send(dummy_error_msg); hello_holdtime().set(save_holdtime); } /** * PimVif::pim_hello_recv: * @pim_nbr: The PIM neighbor message originator (or NULL if not a neighbor). * @src: The message source address. * @dst: The message destination address. * @buffer: The buffer with the message. * @nbr_proto_version: The protocol version from this heighbor. * * Receive PIM_HELLO message. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int PimVif::pim_hello_recv(PimNbr *pim_nbr, const IPvX& src, const IPvX& dst, buffer_t *buffer, int nbr_proto_version) { bool holdtime_rcvd = false; bool lan_prune_delay_rcvd = false; bool dr_priority_rcvd = false; bool genid_rcvd = false; bool is_genid_changed = false; uint16_t option_type, option_length, option_length_spec; uint16_t holdtime = 0; uint16_t propagation_delay_tbit = 0; uint16_t lan_prune_delay_tbit = 0; uint16_t override_interval = 0; uint32_t dr_priority = 0; uint32_t genid = 0; bool new_nbr_flag = false; list secondary_addr_list; list::iterator addr_list_iter; int rcvd_family; UNUSED(dst); //XLOG_WARNING("pim_hello_recv: RX %s from %s to %s: ", // PIMTYPE2ASCII(PIM_HELLO), // cstring(src), cstring(dst)); // // Parse the message // while (BUFFER_DATA_SIZE(buffer) > 0) { BUFFER_GET_HOST_16(option_type, buffer); BUFFER_GET_HOST_16(option_length, buffer); if (BUFFER_DATA_SIZE(buffer) < option_length) goto rcvlen_error; switch (option_type) { case PIM_HELLO_HOLDTIME_OPTION: // Holdtime option option_length_spec = PIM_HELLO_HOLDTIME_LENGTH; if (option_length < option_length_spec) { BUFFER_GET_SKIP(option_length, buffer); continue; } BUFFER_GET_HOST_16(holdtime, buffer); BUFFER_GET_SKIP(option_length - option_length_spec, buffer); holdtime_rcvd = true; break; case PIM_HELLO_LAN_PRUNE_DELAY_OPTION: // LAN Prune Delay option option_length_spec = PIM_HELLO_LAN_PRUNE_DELAY_LENGTH; if (option_length < option_length_spec) { BUFFER_GET_SKIP(option_length, buffer); continue; } BUFFER_GET_HOST_16(propagation_delay_tbit, buffer); BUFFER_GET_HOST_16(override_interval, buffer); lan_prune_delay_tbit = (propagation_delay_tbit & PIM_HELLO_LAN_PRUNE_DELAY_TBIT) ? true : false; propagation_delay_tbit &= ~PIM_HELLO_LAN_PRUNE_DELAY_TBIT; BUFFER_GET_SKIP(option_length - option_length_spec, buffer); lan_prune_delay_rcvd = true; break; case PIM_HELLO_DR_PRIORITY_OPTION: // DR_Priority option option_length_spec = PIM_HELLO_DR_PRIORITY_LENGTH; if (option_length < option_length_spec) { BUFFER_GET_SKIP(option_length, buffer); continue; } BUFFER_GET_HOST_32(dr_priority, buffer); BUFFER_GET_SKIP(option_length - option_length_spec, buffer); dr_priority_rcvd = true; break; case PIM_HELLO_GENID_OPTION: // GenID option option_length_spec = PIM_HELLO_GENID_LENGTH; if (option_length < option_length_spec) { BUFFER_GET_SKIP(option_length, buffer); continue; } BUFFER_GET_HOST_32(genid, buffer); BUFFER_GET_SKIP(option_length - option_length_spec, buffer); genid_rcvd = true; break; case PIM_HELLO_ADDRESS_LIST_OPTION: // Address List option while (option_length >= ENCODED_UNICAST_ADDR_SIZE(family())) { IPvX secondary_addr(family()); GET_ENCODED_UNICAST_ADDR(rcvd_family, secondary_addr, buffer); secondary_addr_list.push_back(secondary_addr); option_length -= ENCODED_UNICAST_ADDR_SIZE(family()); } if (option_length > 0) BUFFER_GET_SKIP(option_length, buffer); break; default: // XXX: skip unrecognized options BUFFER_GET_SKIP(option_length, buffer); ++_pimstat_rx_unknown_hello_option; break; } } if ((pim_nbr != NULL) && genid_rcvd) { if ( (! pim_nbr->is_genid_present()) || (pim_nbr->is_genid_present() && genid != pim_nbr->genid())) { // // This neighbor has just restarted (or something similar). // is_genid_changed = true; // // Reset any old Hello information about this neighbor. // pim_nbr->reset_received_options(); } } if (pim_nbr == NULL) { // A new neighbor pim_nbr = new PimNbr(this, src, nbr_proto_version); add_pim_nbr(pim_nbr); new_nbr_flag = true; XLOG_TRACE(pim_node()->is_log_trace(), "Added new neighbor %s on vif %s", cstring(pim_nbr->primary_addr()), name().c_str()); } // Set the protocol version for this neighbor pim_nbr->set_proto_version(nbr_proto_version); // // XXX: pim_hello_holdtime_process() is called with default value // even if no Holdtime option is received. // if (! holdtime_rcvd) holdtime = PIM_HELLO_HELLO_HOLDTIME_DEFAULT; pim_nbr->pim_hello_holdtime_process(holdtime); if (lan_prune_delay_rcvd) pim_nbr->pim_hello_lan_prune_delay_process(lan_prune_delay_tbit, propagation_delay_tbit, override_interval); if (dr_priority_rcvd) pim_nbr->pim_hello_dr_priority_process(dr_priority); if (genid_rcvd) pim_nbr->pim_hello_genid_process(genid); // // Add the secondary addresses // pim_nbr->clear_secondary_addr_list(); // First clear the old addresses for (addr_list_iter = secondary_addr_list.begin(); addr_list_iter != secondary_addr_list.end(); ++addr_list_iter) { IPvX& secondary_addr = *addr_list_iter; if (pim_nbr->primary_addr() == secondary_addr) { // The primary address is in the secondary addresses list. Ignore. continue; } if (pim_nbr->has_secondary_addr(secondary_addr)) { XLOG_WARNING("RX %s from %s to %s: " "duplicated secondary address %s", PIMTYPE2ASCII(PIM_HELLO), cstring(src), cstring(dst), cstring(secondary_addr)); continue; } pim_nbr->add_secondary_addr(secondary_addr); // // Check if the same secondary address was advertised // by other neighbors. // list::iterator nbr_iter; for (nbr_iter = pim_nbrs().begin(); nbr_iter != pim_nbrs().end(); ++nbr_iter) { PimNbr *tmp_pim_nbr = *nbr_iter; if (tmp_pim_nbr == pim_nbr) continue; if (tmp_pim_nbr->has_secondary_addr(secondary_addr)) { XLOG_WARNING("RX %s from %s to %s: " "overriding secondary address %s that was " "advertised previously by neighbor %s", PIMTYPE2ASCII(PIM_HELLO), cstring(src), cstring(dst), cstring(secondary_addr), cstring(tmp_pim_nbr->primary_addr())); tmp_pim_nbr->delete_secondary_addr(secondary_addr); } } } if (new_nbr_flag || is_genid_changed) { if (i_am_dr() || (is_genid_changed && i_may_become_dr(src))) { // Schedule to send an unicast Bootstrap message add_send_unicast_bootstrap_nbr(src); } // // Set the flag that we must send first a Hello message before // any other control messages. // set_should_send_pim_hello(true); // Schedule a PIM_HELLO message at random in the // interval [0, hello_triggered_delay) // XXX: this message should not affect the periodic `hello_timer'. TimeVal tv(hello_triggered_delay().get(), 0); tv = random_uniform(tv); _hello_once_timer = pim_node()->eventloop().new_oneoff_after( tv, callback(this, &PimVif::hello_once_timer_timeout)); // // Add the task that will process all PimMre entries that have no // neighbor. // if (new_nbr_flag) { pim_node()->pim_mrt().add_task_pim_nbr_changed( Vif::VIF_INDEX_INVALID, IPvX::ZERO(family())); } // // Add the task that will process all PimMre entries and take // action because the GenID of this neighbor changed. // if (is_genid_changed) { pim_node()->pim_mrt().add_task_pim_nbr_gen_id_changed( vif_index(), pim_nbr->primary_addr()); } } // (Re)elect the DR pim_dr_elect(); return (XORP_OK); rcvlen_error: XLOG_WARNING("RX %s from %s to %s: " "invalid message length", PIMTYPE2ASCII(PIM_HELLO), cstring(src), cstring(dst)); ++_pimstat_rx_malformed_packet; return (XORP_ERROR); rcvd_family_error: XLOG_WARNING("RX %s from %s to %s: " "invalid address family inside = %d", PIMTYPE2ASCII(PIM_HELLO), cstring(src), cstring(dst), rcvd_family); return (XORP_ERROR); // UNUSED(dst); } /** * PimNbr::pim_hello_holdtime_process: * @holdtime: The holdtime for the neighbor (in seconds). * * Process PIM_HELLO Holdtime option. * XXX: if no Holdtime option is received, this function is still * called with the default holdtime value of %PIM_HELLO_HELLO_HOLDTIME_DEFAULT. **/ void PimNbr::pim_hello_holdtime_process(uint16_t holdtime) { _hello_holdtime = holdtime; switch (holdtime) { case PIM_HELLO_HOLDTIME_FOREVER: // Never expire this neighbor _neighbor_liveness_timer.unschedule(); break; default: // Start the Neighbor Liveness Timer _neighbor_liveness_timer = pim_node()->eventloop().new_oneoff_after( TimeVal(holdtime, 0), callback(this, &PimNbr::neighbor_liveness_timer_timeout)); break; } } void PimNbr::pim_hello_lan_prune_delay_process(bool lan_prune_delay_tbit, uint16_t propagation_delay, uint16_t override_interval) { _is_lan_prune_delay_present = true; _is_tracking_support_disabled = lan_prune_delay_tbit; _propagation_delay = propagation_delay; _override_interval = override_interval; } void PimNbr::pim_hello_dr_priority_process(uint32_t dr_priority) { _is_dr_priority_present = true; _dr_priority = dr_priority; } void PimNbr::pim_hello_genid_process(uint32_t genid) { _is_genid_present = true; _genid = genid; } void PimVif::pim_dr_elect() { PimNbr *dr = &pim_nbr_me(); list::iterator iter; bool consider_dr_priority = pim_nbr_me().is_dr_priority_present(); for (iter = _pim_nbrs.begin(); iter != _pim_nbrs.end(); ++iter) { PimNbr *pim_nbr = *iter; if (! pim_nbr->is_dr_priority_present()) { consider_dr_priority = false; break; } } for (iter = _pim_nbrs.begin(); iter != _pim_nbrs.end(); ++iter) { PimNbr *pim_nbr = *iter; if (! pim_dr_is_better(dr, pim_nbr, consider_dr_priority)) dr = pim_nbr; } if (dr == NULL) { XLOG_FATAL("Cannot elect a DR on interface %s", name().c_str()); return; } _dr_addr = dr->primary_addr(); // Set a flag if I am the DR if (dr_addr() == primary_addr()) { if (! i_am_dr()) { set_i_am_dr(true); // TODO: take the appropriate action } } else { set_i_am_dr(false); } } /** * PimVif::i_may_become_dr: * @exclude_addr: The address to exclude in the computation. * * Compute if I may become the DR on this interface if @exclude_addr * is excluded. * * Return value: True if I may become the DR on this interface, otherwise * false. **/ bool PimVif::i_may_become_dr(const IPvX& exclude_addr) { PimNbr *dr = &pim_nbr_me(); list::iterator iter; bool consider_dr_priority = pim_nbr_me().is_dr_priority_present(); for (iter = _pim_nbrs.begin(); iter != _pim_nbrs.end(); ++iter) { PimNbr *pim_nbr = *iter; if (! pim_nbr->is_dr_priority_present()) { consider_dr_priority = false; break; } } for (iter = _pim_nbrs.begin(); iter != _pim_nbrs.end(); ++iter) { PimNbr *pim_nbr = *iter; if (pim_nbr->primary_addr() == exclude_addr) continue; if (! pim_dr_is_better(dr, pim_nbr, consider_dr_priority)) dr = pim_nbr; } if ((dr != NULL) && (dr->primary_addr() == primary_addr())) return (true); return (false); } /** * pim_dr_is_better: * @pim_nbr1: The first neighbor to compare. * @pim_nbr2: The second neighbor to compare. * @consider_dr_priority: If true, in the comparison we consider * the DR priority, otherwise the DR priority is ignored. * * Compare two PIM neighbors which of them is a better candidate to be a * DR (Designated Router). * * Return value: true if @pim_nbr1 is a better candidate to be a DR, * otherwise %false. **/ static bool pim_dr_is_better(PimNbr *pim_nbr1, PimNbr *pim_nbr2, bool consider_dr_priority) { if (pim_nbr2 == NULL) return (true); if (pim_nbr1 == NULL) return (false); if (consider_dr_priority) { if (pim_nbr1->dr_priority() > pim_nbr2->dr_priority()) return (true); if (pim_nbr1->dr_priority() < pim_nbr2->dr_priority()) return (false); } // Either the DR priority is same, or we have to ignore it if (pim_nbr1->primary_addr() > pim_nbr2->primary_addr()) return (true); return (false); } void PimVif::hello_timer_start(uint32_t sec, uint32_t usec) { _hello_timer = pim_node()->eventloop().new_oneoff_after( TimeVal(sec, usec), callback(this, &PimVif::hello_timer_timeout)); } // Schedule a PIM_HELLO message at random in the // interval [0, (sec, usec)) void PimVif::hello_timer_start_random(uint32_t sec, uint32_t usec) { TimeVal tv(sec, usec); tv = random_uniform(tv); _hello_timer = pim_node()->eventloop().new_oneoff_after( tv, callback(this, &PimVif::hello_timer_timeout)); } void PimVif::hello_timer_timeout() { string dummy_error_msg; pim_hello_send(dummy_error_msg); hello_timer_start(hello_period().get(), 0); } // // XXX: the presumption is that this timeout function is called only // when we have added a new neighbor. If this is not true, the // add_task_foo() task scheduling is not needed. // void PimVif::hello_once_timer_timeout() { pim_hello_first_send(); } int PimVif::pim_hello_first_send() { string dummy_error_msg; pim_hello_send(dummy_error_msg); // // Unicast the Bootstrap message if needed // if (is_send_unicast_bootstrap()) { list::const_iterator nbr_iter; for (nbr_iter = send_unicast_bootstrap_nbr_list().begin(); nbr_iter != send_unicast_bootstrap_nbr_list().end(); ++nbr_iter) { const IPvX& nbr_addr = *nbr_iter; // Unicast the Bootstrap messages pim_node()->pim_bsr().unicast_pim_bootstrap(this, nbr_addr); } delete_send_unicast_bootstrap_nbr_list(); } _hello_once_timer.unschedule(); return (XORP_OK); } int PimVif::pim_hello_send(string& error_msg) { list address_list; // // XXX: note that for PIM Hello messages only we use a separate // buffer for sending the data, because the sending of a Hello message // can be triggered during the sending of another control message. // buffer_t *buffer = buffer_send_prepare(_buffer_send_hello); uint16_t propagation_delay_tbit; #if 0 // XXX: enable if for any reason sending Hello messages is not desirable set_should_send_pim_hello(false); return (XORP_OK); #endif // Holdtime option BUFFER_PUT_HOST_16(PIM_HELLO_HOLDTIME_OPTION, buffer); BUFFER_PUT_HOST_16(PIM_HELLO_HOLDTIME_LENGTH, buffer); BUFFER_PUT_HOST_16(hello_holdtime().get(), buffer); // LAN Prune Delay option BUFFER_PUT_HOST_16(PIM_HELLO_LAN_PRUNE_DELAY_OPTION, buffer); BUFFER_PUT_HOST_16(PIM_HELLO_LAN_PRUNE_DELAY_LENGTH, buffer); propagation_delay_tbit = propagation_delay().get(); if (is_tracking_support_disabled().get()) propagation_delay_tbit |= PIM_HELLO_LAN_PRUNE_DELAY_TBIT; BUFFER_PUT_HOST_16(propagation_delay_tbit, buffer); BUFFER_PUT_HOST_16(override_interval().get(), buffer); // DR_Priority option BUFFER_PUT_HOST_16(PIM_HELLO_DR_PRIORITY_OPTION, buffer); BUFFER_PUT_HOST_16(PIM_HELLO_DR_PRIORITY_LENGTH, buffer); BUFFER_PUT_HOST_32(dr_priority().get(), buffer); // GenID option BUFFER_PUT_HOST_16(PIM_HELLO_GENID_OPTION, buffer); BUFFER_PUT_HOST_16(PIM_HELLO_GENID_LENGTH, buffer); BUFFER_PUT_HOST_32(genid().get(), buffer); // Address List option do { // Get the list of secondary addresses list::const_iterator iter; for (iter = addr_list().begin(); iter != addr_list().end(); ++iter) { const VifAddr& vif_addr = *iter; if (vif_addr.addr() == primary_addr()) { // Ignore the primary address continue; } address_list.push_back(vif_addr.addr()); } } while (false); if (address_list.size() > 0) { size_t length; list::iterator iter; BUFFER_PUT_HOST_16(PIM_HELLO_ADDRESS_LIST_OPTION, buffer); length = address_list.size() * ENCODED_UNICAST_ADDR_SIZE(family()); BUFFER_PUT_HOST_16(length, buffer); for (iter = address_list.begin(); iter != address_list.end(); ++iter) { IPvX& addr = *iter; PUT_ENCODED_UNICAST_ADDR(family(), addr, buffer); } } return pim_send(primary_addr(), IPvX::PIM_ROUTERS(family()), PIM_HELLO, buffer, error_msg); invalid_addr_family_error: XLOG_UNREACHABLE(); error_msg = c_format("TX %s from %s to %s: " "invalid address family error = %d", PIMTYPE2ASCII(PIM_HELLO), cstring(primary_addr()), cstring(IPvX::PIM_ROUTERS(family())), family()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); buflen_error: XLOG_UNREACHABLE(); error_msg = c_format("TX %s from %s to %s: " "packet cannot fit into sending buffer", PIMTYPE2ASCII(PIM_HELLO), cstring(primary_addr()), cstring(IPvX::PIM_ROUTERS(family()))); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } xorp/pim/pim_proto_bootstrap.cc0000664000076400007640000004625211540225531017070 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // PIM PIM_BOOTSTRAP messages processing. // #include "pim_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvx.hh" #include "pim_bsr.hh" #include "pim_node.hh" #include "pim_vif.hh" // // Exported variables // // // Local constants definitions // // // Local structures/classes, typedefs and macros // // // Local variables // // // Local functions prototypes // /** * PimVif::pim_bootstrap_recv: * @pim_nbr: The PIM neighbor message originator (or NULL if not a neighbor). * @src: The message source address. * @dst: The message destination address. * @buffer: The buffer with the message. * * Receive PIM_BOOTSTRAP message. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int PimVif::pim_bootstrap_recv(PimNbr *pim_nbr, const IPvX& src, const IPvX& dst, buffer_t *buffer) { int rcvd_family; PimBsr& pim_bsr = pim_node()->pim_bsr(); bool is_unicast_message = false; IPvX bsr_addr(family()); uint8_t hash_mask_len, bsr_priority; uint16_t fragment_tag; uint8_t group_mask_len = 0; BsrZone *bsr_zone = NULL; BsrZone *active_bsr_zone; size_t group_prefix_n = 0; string error_msg; string dummy_error_msg; int ret_value = XORP_ERROR; // // XXX: Don't accept Bootstrap-related messages if the BSR is not running // if (! pim_node()->pim_bsr().is_up()) return (XORP_ERROR); // // Parse the head of the message // BUFFER_GET_HOST_16(fragment_tag, buffer); BUFFER_GET_OCTET(hash_mask_len, buffer); BUFFER_GET_OCTET(bsr_priority, buffer); GET_ENCODED_UNICAST_ADDR(rcvd_family, bsr_addr, buffer); if (! bsr_addr.is_unicast()) { XLOG_WARNING("RX %s from %s to %s: " "invalid Bootstrap router " "address: %s. " "Ignoring whole message.", PIMTYPE2ASCII(PIM_BOOTSTRAP), cstring(src), cstring(dst), cstring(bsr_addr)); ret_value = XORP_ERROR; goto ret_label; } // // Message check // // XXX: the following checks were performed earlier: // - There is Hello state for BSM.src_ip_address // - The sender is directly connected // RPF check if (dst == IPvX::PIM_ROUTERS(family())) { PimNbr *pim_nbr_rpf = pim_node()->pim_nbr_rpf_find(bsr_addr); if (pim_nbr_rpf != pim_nbr) { // RPF failed ret_value = XORP_ERROR; ++_pimstat_rx_bsr_not_rpf_interface; goto ret_label; } } else if (pim_node()->is_my_addr(dst)) { // BSM.dst_ip_address is one of my addresses is_unicast_message = true; } else { // Either the destination is not the right multicast group, or // somehow an unicast packet for somebody else came to me. // A third possibility is the destition unicast address is // one of mine, but it is not enabled for multicast routing. ret_value = XORP_ERROR; goto ret_label; } // // Process the rest of the message // // // Create a new BsrZone that would contain all information // from this message. // bsr_zone = new BsrZone(pim_bsr, bsr_addr, bsr_priority, hash_mask_len, fragment_tag); bsr_zone->set_is_accepted_message(true); bsr_zone->set_is_unicast_message(is_unicast_message, src); // // Main loop for message parsing // while (BUFFER_DATA_SIZE(buffer) > 0) { IPvX group_addr(family()); uint8_t group_addr_reserved_flags; uint8_t rp_count, fragment_rp_count; bool is_scope_zone; BsrGroupPrefix *bsr_group_prefix = NULL; // // The Group (prefix) address // group_prefix_n++; GET_ENCODED_GROUP_ADDR(rcvd_family, group_addr, group_mask_len, group_addr_reserved_flags, buffer); IPvXNet group_prefix(group_addr, group_mask_len); // Set the scope zone if (group_addr_reserved_flags & EGADDR_Z_BIT) is_scope_zone = true; else is_scope_zone = false; if (is_scope_zone && (group_prefix_n == 1)) { PimScopeZoneId zone_id(group_prefix, is_scope_zone); bsr_zone->set_zone_id(zone_id); } BUFFER_GET_OCTET(rp_count, buffer); BUFFER_GET_OCTET(fragment_rp_count, buffer); BUFFER_GET_SKIP(2, buffer); // Reserved // Add the group prefix bsr_group_prefix = bsr_zone->add_bsr_group_prefix(group_prefix, is_scope_zone, rp_count); XLOG_ASSERT(bsr_group_prefix != NULL); // // The set of RPs // while (fragment_rp_count--) { IPvX rp_addr(family()); uint8_t rp_priority; uint16_t rp_holdtime; GET_ENCODED_UNICAST_ADDR(rcvd_family, rp_addr, buffer); BUFFER_GET_HOST_16(rp_holdtime, buffer); BUFFER_GET_OCTET(rp_priority, buffer); BUFFER_GET_SKIP(1, buffer); // Reserved bsr_group_prefix->add_rp(rp_addr, rp_priority, rp_holdtime); } } // // Test if this is not a bogus unicast Bootstrap message // if (is_unicast_message) { // Find if accepted previous BSM for same scope active_bsr_zone = pim_bsr.find_active_bsr_zone(bsr_zone->zone_id()); if ((active_bsr_zone != NULL) && (active_bsr_zone->is_accepted_message())) { if (! (active_bsr_zone->is_unicast_message() && (active_bsr_zone->unicast_message_src() == bsr_zone->unicast_message_src()) && (active_bsr_zone->fragment_tag() == bsr_zone->fragment_tag()))) { // // Any previous BSM for this scope has been accepted. // The packet was unicast, but this wasn't a quick refresh // on startup. Drop the BS message silently. // ret_value = XORP_ERROR; goto ret_label; } } } // // Test whether the message is crossing Admin Scope border // if (pim_node()->pim_scope_zone_table().is_scoped(bsr_zone->zone_id(), pim_nbr->vif_index())) { // if (the interface the message arrived on is an Admin Scope // border for the BSM.first_group_address) { // drop the BS message silently // } // TODO: XXX: PAVPAVPAV: print a warning if the scoped zone // is NOT the local scoped zone!! ret_value = XORP_ERROR; goto ret_label; } // // Test if the received data is consistent // if (! bsr_zone->is_consistent(error_msg)) { XLOG_WARNING("RX %s from %s to %s: " "inconsistent Bootstrap zone %s: %s", PIMTYPE2ASCII(PIM_BOOTSTRAP), cstring(src), cstring(dst), cstring(bsr_zone->zone_id()), error_msg.c_str()); ret_value = XORP_ERROR; goto ret_label; } // // Try to add the received BSM // active_bsr_zone = pim_bsr.add_active_bsr_zone(*bsr_zone, error_msg); if (active_bsr_zone == NULL) { XLOG_WARNING("RX %s from %s to %s: " "cannot add Bootstrap zone %s: %s", PIMTYPE2ASCII(PIM_BOOTSTRAP), cstring(src), cstring(dst), cstring(bsr_zone->zone_id()), error_msg.c_str()); ret_value = XORP_ERROR; goto ret_label; } // Find the active BSR zone active_bsr_zone = pim_bsr.find_active_bsr_zone(bsr_zone->zone_id()); // // If needed, originate my own BSR message // if ((active_bsr_zone != NULL) && (active_bsr_zone->is_bsm_originate())) { active_bsr_zone->new_fragment_tag(); for (uint32_t i = 0; i < pim_node()->maxvifs(); i++) { PimVif *pim_vif = pim_node()->vif_find_by_vif_index(i); if (pim_vif == NULL) continue; pim_vif->pim_bootstrap_send(IPvX::PIM_ROUTERS(family()), *active_bsr_zone, dummy_error_msg); } // Housekeeping: reset the bsm_originate flag. active_bsr_zone->set_bsm_originate(false); } // // If needed, forward the Bootstrap message // if ((active_bsr_zone != NULL) && (active_bsr_zone->is_bsm_forward())) { for (uint32_t i = 0; i < pim_node()->maxvifs(); i++) { PimVif *pim_vif = pim_node()->vif_find_by_vif_index(i); if (pim_vif == NULL) continue; // XXX: always forward-back on the iif, because the 06 I-D is wrong // if (pim_vif == this) // continue; // Don't forward back on the iif // XXX: use the BSR zone that was received to forward the message pim_vif->pim_bootstrap_send(IPvX::PIM_ROUTERS(family()), *bsr_zone, dummy_error_msg); } // Housekeeping: reset the bsm_forward flag. active_bsr_zone->set_bsm_forward(false); } // // Apply the new RP info // pim_bsr.add_rps_to_rp_table(); ret_value = XORP_OK; goto ret_label; // Various error processing rcvlen_error: XLOG_WARNING("RX %s from %s to %s: " "invalid message length", PIMTYPE2ASCII(PIM_BOOTSTRAP), cstring(src), cstring(dst)); ++_pimstat_rx_malformed_packet; ret_value = XORP_ERROR; goto ret_label; rcvd_mask_len_error: XLOG_WARNING("RX %s from %s to %s: " "invalid group mask length = %d", PIMTYPE2ASCII(PIM_BOOTSTRAP), cstring(src), cstring(dst), group_mask_len); ret_value = XORP_ERROR; goto ret_label; rcvd_family_error: XLOG_WARNING("RX %s from %s to %s: " "invalid address family inside = %d", PIMTYPE2ASCII(PIM_BOOTSTRAP), cstring(src), cstring(dst), rcvd_family); ret_value = XORP_ERROR; goto ret_label; ret_label: if (bsr_zone != NULL) delete bsr_zone; return (ret_value); } // Return: %XORP_OK on success, otherwise %XORP_ERROR int PimVif::pim_bootstrap_send(const IPvX& dst_addr, const BsrZone& bsr_zone, string& error_msg) { size_t avail_buffer_size = 0; IPvX src_addr = primary_addr(); // // XXX: Don't transmit Bootstrap-related messages if the BSR is not running // if (! pim_node()->pim_bsr().is_up()) return (XORP_ERROR); if (is_pim_register()) { error_msg = c_format("Cannot send PIM Bootstrap message on " "PIM Register vif"); return (XORP_ERROR); // Never send on the PIM Register vif } if (bsr_zone.bsr_addr() == dst_addr) { error_msg = c_format("Should not send PIM Bootstrap message back " "to the BSR"); return (XORP_ERROR); // Never send-back to the BSR } if (bsr_zone.bsr_addr() == IPvX::ZERO(family())) { error_msg = c_format("Cannot send PIM Bootstrap message: no BSR yet"); return (XORP_ERROR); // There is no BSR yet } if (pim_nbrs_number() == 0) { error_msg = c_format("Should not send PIM Bootstrap message: " "no PIM neighbors yet"); return (XORP_ERROR); // No PIM neighbors yet, hence don't send it } if (pim_node()->pim_scope_zone_table().is_scoped(bsr_zone.zone_id(), vif_index())) { error_msg = c_format("Should not send PIM Bootstrap message " "across scope zone boundary"); return (XORP_ERROR); // Don't across scope zone boundary } buffer_t *buffer = pim_bootstrap_send_prepare(src_addr, dst_addr, bsr_zone, true); XLOG_ASSERT(buffer != NULL); list::const_iterator iter_prefix; for (iter_prefix = bsr_zone.bsr_group_prefix_list().begin(); iter_prefix != bsr_zone.bsr_group_prefix_list().end(); ++iter_prefix) { BsrGroupPrefix *bsr_group_prefix = *iter_prefix; size_t needed_size = 0; size_t received_rp_count = bsr_group_prefix->received_rp_count(); size_t max_fragment_rp_count = 0; size_t send_fragment_rp_count = 0; size_t send_remain_rp_count = 0; uint8_t group_addr_reserved_flags = 0; if (bsr_group_prefix->is_scope_zone()) group_addr_reserved_flags |= EGADDR_Z_BIT; needed_size = ENCODED_GROUP_ADDR_SIZE(family()) + sizeof(uint8_t) + sizeof(uint8_t) + sizeof(uint16_t); needed_size += received_rp_count * ( ENCODED_UNICAST_ADDR_SIZE(family()) + sizeof(uint16_t) + sizeof(uint8_t) + sizeof(uint8_t)); if (needed_size > BUFFER_AVAIL_TAIL(buffer)) { // // Try to do fragmentation among group prefixes for this zone // if (iter_prefix != bsr_zone.bsr_group_prefix_list().begin()) { // Send the accumulated prefixes so far if (pim_send(src_addr, dst_addr, PIM_BOOTSTRAP, buffer, error_msg) != XORP_OK) { return (XORP_ERROR); } buffer = pim_bootstrap_send_prepare(src_addr, dst_addr, bsr_zone, false); XLOG_ASSERT(buffer != NULL); } } // // Compute the max. number of RPs we can add to the message // avail_buffer_size = BUFFER_AVAIL_TAIL(buffer); if (avail_buffer_size < ENCODED_GROUP_ADDR_SIZE(family()) + sizeof(uint8_t) + sizeof(uint8_t) + sizeof(uint16_t)) { goto buflen_error; } avail_buffer_size -= ENCODED_GROUP_ADDR_SIZE(family()) + sizeof(uint8_t) + sizeof(uint8_t) + sizeof(uint16_t); max_fragment_rp_count = avail_buffer_size / (ENCODED_UNICAST_ADDR_SIZE(family()) + sizeof(uint16_t) + sizeof(uint8_t) + sizeof(uint8_t)); if (max_fragment_rp_count == 0) goto buflen_error; send_fragment_rp_count = max_fragment_rp_count; if (send_fragment_rp_count > received_rp_count) send_fragment_rp_count = received_rp_count; send_remain_rp_count = received_rp_count; PUT_ENCODED_GROUP_ADDR(family(), bsr_group_prefix->group_prefix().masked_addr(), bsr_group_prefix->group_prefix().prefix_len(), group_addr_reserved_flags, buffer); // RP count and Fragment RP count BUFFER_PUT_OCTET(bsr_group_prefix->expected_rp_count(), buffer); BUFFER_PUT_OCTET(send_fragment_rp_count, buffer); BUFFER_PUT_HOST_16(0, buffer); // Reserved // The list of RPs list::const_iterator iter_rp; for (iter_rp = bsr_group_prefix->rp_list().begin(); iter_rp != bsr_group_prefix->rp_list().end(); ++iter_rp) { BsrRp *bsr_rp = *iter_rp; if (send_fragment_rp_count == 0) { // // Send the accumulated RPs so far // if (pim_send(src_addr, dst_addr, PIM_BOOTSTRAP, buffer, error_msg) != XORP_OK) { return (XORP_ERROR); } buffer = pim_bootstrap_send_prepare(src_addr, dst_addr, bsr_zone, false); XLOG_ASSERT(buffer != NULL); avail_buffer_size = BUFFER_AVAIL_TAIL(buffer); if (avail_buffer_size < ENCODED_GROUP_ADDR_SIZE(family()) + sizeof(uint8_t) + sizeof(uint8_t) + sizeof(uint16_t)) { goto buflen_error; } avail_buffer_size -= ENCODED_GROUP_ADDR_SIZE(family()) + sizeof(uint8_t) + sizeof(uint8_t) + sizeof(uint16_t); max_fragment_rp_count = avail_buffer_size / (ENCODED_UNICAST_ADDR_SIZE(family()) + sizeof(uint16_t) + sizeof(uint8_t) + sizeof(uint8_t)); if (max_fragment_rp_count == 0) goto buflen_error; send_fragment_rp_count = max_fragment_rp_count; if (send_fragment_rp_count > send_remain_rp_count) send_fragment_rp_count = send_remain_rp_count; PUT_ENCODED_GROUP_ADDR(family(), bsr_group_prefix->group_prefix().masked_addr(), bsr_group_prefix->group_prefix().prefix_len(), group_addr_reserved_flags, buffer); // RP count and Fragment RP count BUFFER_PUT_OCTET(bsr_group_prefix->expected_rp_count(), buffer); BUFFER_PUT_OCTET(send_fragment_rp_count, buffer); BUFFER_PUT_HOST_16(0, buffer); // Reserved } send_fragment_rp_count--; send_remain_rp_count--; // // Put the RP information // PUT_ENCODED_UNICAST_ADDR(family(), bsr_rp->rp_addr(), buffer); // If this is canceling Bootstrap message, set the Holdtime // to zero for my RP addresses. if (bsr_zone.is_cancel() && (pim_node()->is_my_addr(bsr_rp->rp_addr()))) { BUFFER_PUT_HOST_16(0, buffer); } else { BUFFER_PUT_HOST_16(bsr_rp->rp_holdtime(), buffer); } BUFFER_PUT_OCTET(bsr_rp->rp_priority(), buffer); BUFFER_PUT_OCTET(0, buffer); // Reserved } } // // Send the lastest fragment // if (pim_send(src_addr, dst_addr, PIM_BOOTSTRAP, buffer, error_msg) != XORP_OK) { return (XORP_ERROR); } return (XORP_OK); invalid_addr_family_error: error_msg = c_format("TX %s from %s to %s: " "invalid address family error = %d", PIMTYPE2ASCII(PIM_BOOTSTRAP), cstring(src_addr), cstring(dst_addr), family()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); buflen_error: error_msg = c_format("TX %s from %s to %s: " "packet cannot fit into sending buffer", PIMTYPE2ASCII(PIM_BOOTSTRAP), cstring(src_addr), cstring(dst_addr)); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } // // Prepare the zone-specific data and add it to the buffer to send. // buffer_t * PimVif::pim_bootstrap_send_prepare(const IPvX& src_addr, const IPvX& dst_addr, const BsrZone& bsr_zone, bool is_first_fragment) { buffer_t *buffer = buffer_send_prepare(_buffer_send_bootstrap); uint8_t hash_mask_len = bsr_zone.hash_mask_len(); uint8_t group_addr_reserved_flags = 0; UNUSED(src_addr); UNUSED(dst_addr); // // Write zone-related data to the buffer // BUFFER_PUT_HOST_16(bsr_zone.fragment_tag(), buffer); BUFFER_PUT_OCTET(hash_mask_len, buffer); if (bsr_zone.is_cancel()) BUFFER_PUT_OCTET(PIM_BOOTSTRAP_LOWEST_PRIORITY, buffer); else BUFFER_PUT_OCTET(bsr_zone.bsr_priority(), buffer); PUT_ENCODED_UNICAST_ADDR(family(), bsr_zone.bsr_addr(), buffer); // // Test whether we need a prefix for the entire admin scope // range with no RPs. // do { if (! bsr_zone.zone_id().is_scope_zone()) break; list::const_iterator iter_prefix = bsr_zone.bsr_group_prefix_list().begin(); if (iter_prefix != bsr_zone.bsr_group_prefix_list().end()) { BsrGroupPrefix *bsr_group_prefix = *iter_prefix; if (is_first_fragment && (bsr_group_prefix->group_prefix() == bsr_zone.zone_id().scope_zone_prefix())) // // XXX: the first prefix already covers the entire admin // scope range. // break; } // // Add the entire admin scope range with no RPs. // group_addr_reserved_flags = 0; group_addr_reserved_flags |= EGADDR_Z_BIT; PUT_ENCODED_GROUP_ADDR(family(), bsr_zone.zone_id().scope_zone_prefix().masked_addr(), bsr_zone.zone_id().scope_zone_prefix().prefix_len(), group_addr_reserved_flags, buffer); BUFFER_PUT_OCTET(0, buffer); // RP count BUFFER_PUT_OCTET(0, buffer); // Fragment RP count BUFFER_PUT_HOST_16(0, buffer); // Reserved } while (false); return (buffer); invalid_addr_family_error: XLOG_ERROR("TX %s from %s to %s: " "invalid address family error = %d", PIMTYPE2ASCII(PIM_BOOTSTRAP), cstring(src_addr), cstring(dst_addr), family()); return (NULL); buflen_error: XLOG_ERROR("TX %s from %s to %s: " "packet cannot fit into sending buffer", PIMTYPE2ASCII(PIM_BOOTSTRAP), cstring(src_addr), cstring(dst_addr)); return (NULL); } xorp/pim/pim_vif.cc0000664000076400007640000015170611703345405014421 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2012 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // PIM virtual interfaces implementation. // #include "pim_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/random.h" #include "libxorp/ipvx.hh" #include "libproto/checksum.h" #include "pim_node.hh" #include "pim_vif.hh" /** * PimVif::PimVif: * @pim_node: The PIM node this interface belongs to. * @vif: The generic Vif interface that contains various information. * * PIM protocol vif constructor. **/ PimVif::PimVif(PimNode* pim_node, const Vif& vif) : ProtoUnit(pim_node->family(), pim_node->module_id()), Vif(vif), _pim_node(pim_node), _dr_addr(pim_node->family()), _pim_nbr_me(this, IPvX::ZERO(pim_node->family()), PIM_VERSION_DEFAULT), _domain_wide_addr(IPvX::ZERO(pim_node->family())), _hello_triggered_delay(PIM_HELLO_HELLO_TRIGGERED_DELAY_DEFAULT), _hello_period(PIM_HELLO_HELLO_PERIOD_DEFAULT, callback(this, &PimVif::set_hello_period_callback)), _hello_holdtime(PIM_HELLO_HELLO_HOLDTIME_DEFAULT, callback(this, &PimVif::set_hello_holdtime_callback)), _dr_priority(PIM_HELLO_DR_PRIORITY_DEFAULT, callback(this, &PimVif::set_dr_priority_callback)), _propagation_delay(PIM_PROPAGATION_DELAY_MSEC_DEFAULT, callback(this, &PimVif::set_propagation_delay_callback)), _override_interval(PIM_OVERRIDE_INTERVAL_MSEC_DEFAULT, callback(this, &PimVif::set_override_interval_callback)), _is_tracking_support_disabled(false, callback(this, &PimVif::set_is_tracking_support_disabled_callback)), _accept_nohello_neighbors(false), _genid(xorp_random() % 0xffffffffU, callback(this, &PimVif::set_genid_callback)), _join_prune_period(PIM_JOIN_PRUNE_PERIOD_DEFAULT, callback(this, &PimVif::set_join_prune_period_callback)), _join_prune_holdtime(PIM_JOIN_PRUNE_HOLDTIME_DEFAULT), _assert_time(PIM_ASSERT_ASSERT_TIME_DEFAULT), _assert_override_interval(PIM_ASSERT_ASSERT_OVERRIDE_INTERVAL_DEFAULT), // _pimstat_hello_messages_received(0), _pimstat_hello_messages_sent(0), _pimstat_hello_messages_rx_errors(0), _pimstat_register_messages_received(0), _pimstat_register_messages_sent(0), _pimstat_register_messages_rx_errors(0), _pimstat_register_stop_messages_received(0), _pimstat_register_stop_messages_sent(0), _pimstat_register_stop_messages_rx_errors(0), _pimstat_join_prune_messages_received(0), _pimstat_join_prune_messages_sent(0), _pimstat_join_prune_messages_rx_errors(0), _pimstat_bootstrap_messages_received(0), _pimstat_bootstrap_messages_sent(0), _pimstat_bootstrap_messages_rx_errors(0), _pimstat_assert_messages_received(0), _pimstat_assert_messages_sent(0), _pimstat_assert_messages_rx_errors(0), _pimstat_graft_messages_received(0), _pimstat_graft_messages_sent(0), _pimstat_graft_messages_rx_errors(0), _pimstat_graft_ack_messages_received(0), _pimstat_graft_ack_messages_sent(0), _pimstat_graft_ack_messages_rx_errors(0), _pimstat_candidate_rp_messages_received(0), _pimstat_candidate_rp_messages_sent(0), _pimstat_candidate_rp_messages_rx_errors(0), // _pimstat_unknown_type_messages(0), _pimstat_unknown_version_messages(0), _pimstat_neighbor_unknown_messages(0), _pimstat_bad_length_messages(0), _pimstat_bad_checksum_messages(0), _pimstat_bad_receive_interface_messages(0), _pimstat_rx_interface_disabled_messages(0), _pimstat_rx_register_not_rp(0), _pimstat_rp_filtered_source(0), _pimstat_unknown_register_stop(0), _pimstat_rx_join_prune_no_state(0), _pimstat_rx_graft_graft_ack_no_state(0), _pimstat_rx_graft_on_upstream_interface(0), _pimstat_rx_candidate_rp_not_bsr(0), _pimstat_rx_bsr_when_bsr(0), _pimstat_rx_bsr_not_rpf_interface(0), _pimstat_rx_unknown_hello_option(0), _pimstat_rx_data_no_state(0), _pimstat_rx_rp_no_state(0), _pimstat_rx_aggregate(0), _pimstat_rx_malformed_packet(0), _pimstat_no_rp(0), _pimstat_no_route_upstream(0), _pimstat_rp_mismatch(0), _pimstat_rpf_neighbor_unknown(0), // _pimstat_rx_join_rp(0), _pimstat_rx_prune_rp(0), _pimstat_rx_join_wc(0), _pimstat_rx_prune_wc(0), _pimstat_rx_join_sg(0), _pimstat_rx_prune_sg(0), _pimstat_rx_join_sg_rpt(0), _pimstat_rx_prune_sg_rpt(0), // _usage_by_pim_mre_task(0) { wants_to_be_started = false; _buffer_send = BUFFER_MALLOC(BUF_SIZE_DEFAULT); _buffer_send_hello = BUFFER_MALLOC(BUF_SIZE_DEFAULT); _buffer_send_bootstrap = BUFFER_MALLOC(BUF_SIZE_DEFAULT); _proto_flags = 0; set_proto_version_default(PIM_VERSION_DEFAULT); set_default_config(); set_should_send_pim_hello(true); } /** * PimVif::~PimVif: * @: * * PIM protocol vif destructor. * **/ PimVif::~PimVif() { string error_msg; stop(error_msg); BUFFER_FREE(_buffer_send); BUFFER_FREE(_buffer_send_hello); BUFFER_FREE(_buffer_send_bootstrap); // Remove all PIM neighbor entries while (! _pim_nbrs.empty()) { PimNbr *pim_nbr = _pim_nbrs.front(); _pim_nbrs.pop_front(); // TODO: perform the appropriate actions delete_pim_nbr(pim_nbr); } } /** * PimVif::set_default_config: * @: * * Set configuration to default values. **/ void PimVif::set_default_config() { // Protocol version set_proto_version(proto_version_default()); // Hello-related configurable parameters hello_triggered_delay().reset(); hello_period().reset(); hello_holdtime().reset(); dr_priority().reset(); propagation_delay().reset(); override_interval().reset(); is_tracking_support_disabled().reset(); accept_nohello_neighbors().reset(); // Hello-related non-configurable parameters genid().set(xorp_random() % 0xffffffffU); // Join/Prune-related parameters _join_prune_period.reset(); _join_prune_holdtime.reset(); } /** * PimVif::set_proto_version: * @proto_version: The protocol version to set. * * Set protocol version. * * Return value: %XORP_OK is @proto_version is valid, otherwise %XORP_ERROR. **/ int PimVif::set_proto_version(int proto_version) { if ((proto_version < PIM_VERSION_MIN) || (proto_version > PIM_VERSION_MAX)) return (XORP_ERROR); ProtoUnit::set_proto_version(proto_version); return (XORP_OK); } /** * PimVif::pim_mrt: * @: * * Get the PIM Multicast Routing Table. * * Return value: A reference to the PIM Multicast Routing Table. **/ PimMrt& PimVif::pim_mrt() const { return (_pim_node->pim_mrt()); } /** System detected some change. */ void PimVif::notifyUpdated() { if (wants_to_be_started) { string err_msg; int rv = start(err_msg); if (rv == XORP_OK) { XLOG_WARNING("notifyUpdated, successfully started pim_vif: %s", name().c_str()); } else { XLOG_WARNING("notifyUpdated, tried to start vif: %s, but failed: %s", name().c_str(), err_msg.c_str()); } } } /** * PimVif::start: * @error_msg: The error message (if error). * * Start PIM on a single virtual interface. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int PimVif::start(string& error_msg) { if (! is_enabled()) return (XORP_OK); if (is_up() || is_pending_up()) return (XORP_OK); if (! is_underlying_vif_up()) { wants_to_be_started = true; XLOG_WARNING("WARNING: Delaying start of pim-vif: %s because underlying vif is not up.", name().c_str()); return XORP_OK; } if (! (is_pim_register() || is_multicast_capable())) { wants_to_be_started = true; XLOG_WARNING("WARNING: Delaying start of pim-vif: %s because underlying vif is not multicast capable.", name().c_str()); return XORP_OK; } // // Start the vif only if it is of the appropriate type: // multicast-capable (loopback excluded), or PIM Register vif. // if (is_loopback()) { error_msg = "pim-vif: Loopback interfaces cannot be used for multicast."; return (XORP_ERROR); } if (update_primary_and_domain_wide_address(error_msg) != XORP_OK) return (XORP_ERROR); if (ProtoUnit::start() != XORP_OK) { error_msg = "internal error"; return (XORP_ERROR); } // // Register as a receiver with the kernel // if (pim_node()->register_receiver(name(), name(), pim_node()->ip_protocol_number(), false) != XORP_OK) { error_msg = c_format("cannot register as a receiver on vif %s " "with the kernel", name().c_str()); return (XORP_ERROR); } // // Register as a protocol with the MFEA // if (pim_node()->register_protocol(name(), name(), pim_node()->ip_protocol_number()) != XORP_OK) { error_msg = c_format("cannot register as a protocol on vif %s " "with the MFEA", name().c_str()); return (XORP_ERROR); } if (! is_pim_register()) { // // Join the appropriate multicast groups: ALL-PIM-ROUTERS // const IPvX group = IPvX::PIM_ROUTERS(family()); if (pim_node()->join_multicast_group(name(), name(), pim_node()->ip_protocol_number(), group) != XORP_OK) { error_msg = c_format("cannot join group %s on vif %s", cstring(group), name().c_str()); return (XORP_ERROR); } pim_hello_start(); // // Add MLD6/IGMP membership tracking // pim_node()->add_protocol_mld6igmp(vif_index()); } // // Add the tasks to take care of the PimMre processing // pim_node()->pim_mrt().add_task_start_vif(vif_index()); pim_node()->pim_mrt().add_task_my_ip_address(vif_index()); pim_node()->pim_mrt().add_task_my_ip_subnet_address(vif_index()); XLOG_INFO("Interface started: %s%s", this->str().c_str(), flags_string().c_str()); wants_to_be_started = false; //it worked return (XORP_OK); } /** * PimVif::stop: * @error_msg: The error message (if error). * * Gracefully stop PIM on a single virtual interface. * XXX: The graceful stop will attempt to send Join/Prune, Assert, etc. * messages for all multicast routing entries to gracefully clean-up * state with neighbors. * XXX: After the multicast routing entries cleanup is completed, * PimVif::final_stop() is called to complete the job. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int PimVif::stop(string& error_msg) { int ret_value = XORP_OK; wants_to_be_started = false; //it worked 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() != XORP_OK) { error_msg = "internal error"; ret_value = XORP_ERROR; } // // Add the tasks to take care of the PimMre processing // pim_node()->pim_mrt().add_task_stop_vif(vif_index()); pim_node()->pim_mrt().add_task_my_ip_address(vif_index()); pim_node()->pim_mrt().add_task_my_ip_subnet_address(vif_index()); // // Add the shutdown operation of this vif as a shutdown task // for the node. // pim_node()->incr_shutdown_requests_n(); // XXX: for PIM-stop-vif if (! is_pim_register()) { // // Delete MLD6/IGMP membership tracking // pim_node()->delete_protocol_mld6igmp(vif_index()); set_i_am_dr(false); } _dr_addr = IPvX::ZERO(family()); return (ret_value); } /** * PimVif::final_stop: * @error_msg: The error message (if error). * * Completely stop PIM on a single virtual interface. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int PimVif::final_stop(string& error_msg) { int ret_value = 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 (! is_pim_register()) { // // Delete MLD6/IGMP membership tracking // if (is_up() || is_pending_up()) pim_node()->delete_protocol_mld6igmp(vif_index()); pim_hello_stop(); set_i_am_dr(false); } // // XXX: we don't have to explicitly leave the multicast groups // we have joined on that interface, because this will happen // automatically when we stop the vif through the MFEA. // if (ProtoUnit::stop() != XORP_OK) { error_msg = "internal error"; ret_value = XORP_ERROR; } _dr_addr = IPvX::ZERO(family()); _hello_timer.unschedule(); _hello_once_timer.unschedule(); // Remove all PIM neighbor entries while (! _pim_nbrs.empty()) { PimNbr *pim_nbr = _pim_nbrs.front(); _pim_nbrs.pop_front(); // TODO: perform the appropriate actions delete_pim_nbr(pim_nbr); } // // Unregister as a protocol with the MFEA // if (pim_node()->unregister_protocol(name(), name()) != XORP_OK) { XLOG_ERROR("Cannot unregister as a protocol on vif %s with the MFEA", name().c_str()); ret_value = XORP_ERROR; } // // Unregister as a receiver with the kernel // if (pim_node()->unregister_receiver(name(), name(), pim_node()->ip_protocol_number()) != XORP_OK) { XLOG_ERROR("Cannot unregister as a receiver on 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 // pim_node()->vif_shutdown_completed(name()); // // Remove the shutdown operation of this vif as a shutdown task // for the node. // pim_node()->decr_shutdown_requests_n(); // XXX: for PIM-stop-vif return (ret_value); } /** * Enable PIM on a single virtual interface. * * If an unit is not enabled, it cannot be start, or pending-start. */ void PimVif::enable() { ProtoUnit::enable(); XLOG_INFO("Interface enabled: %s%s", this->str().c_str(), flags_string().c_str()); } /** * Disable PIM 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 PimVif::disable() { string error_msg; stop(error_msg); ProtoUnit::disable(); XLOG_INFO("Interface disabled: %s%s", this->str().c_str(), flags_string().c_str()); } /** * PimVif::pim_send: * @src: The message source address. * @dst: The message destination address. * @message_type: The PIM type of the message. * @buffer: The buffer with the rest of the message. * @error_msg: The error message (if error). * * Send PIM message. * XXX: The beginning of the @buffer must have been reserved * for the PIM header. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int PimVif::pim_send(const IPvX& src, const IPvX& dst, uint8_t message_type, buffer_t *buffer, string& error_msg) { uint8_t pim_vt; uint16_t cksum; uint16_t cksum2 = 0; int ip_tos = -1; int ret_value; size_t datalen; int ttl = MINTTL; bool ip_internet_control = true; // XXX: might be overwritten below if (! (is_up() || is_pending_down())) { error_msg += "Interface: " + name() + " is down or pending down when trying pim_send\n"; debug_msg("Vif %s is currently down\n", name().c_str()); return (XORP_ERROR); } // // Some of the messages should never be send via the PIM Register vif // if (is_pim_register()) { switch (message_type) { case PIM_HELLO: case PIM_JOIN_PRUNE: case PIM_BOOTSTRAP: case PIM_ASSERT: case PIM_GRAFT: case PIM_GRAFT_ACK: error_msg += "Invalid message_type, is_pim_register == true\n"; debug_msg("Invalid message type %d on register vif\n", message_type); return (XORP_ERROR); // Those messages are not allowed case PIM_REGISTER: case PIM_REGISTER_STOP: case PIM_CAND_RP_ADV: break; // Those messages are probably OK default: break; } } // // Some of the messages need to be send by unicast across the domain. // For those messages we need to modify some of the sending values. // if (dst.is_unicast()) { switch (message_type) { case PIM_REGISTER: // XXX: The ip_tos is copied from the inner IP header ip_internet_control = false; // FALLTHROUGH case PIM_REGISTER_STOP: case PIM_CAND_RP_ADV: ttl = IPDEFTTL; break; default: break; } } // // If necessary, send first a Hello message // if (should_send_pim_hello()) { switch (message_type) { case PIM_JOIN_PRUNE: case PIM_BOOTSTRAP: // XXX: not in the spec yet case PIM_ASSERT: pim_hello_first_send(); break; default: break; } } // // Compute the TOS // switch (message_type) { case PIM_REGISTER: // // If PIM Register, then copy the TOS from the inner header // to the outer header. Strictly speaking, we need to do it // only for Registers with data (i.e., not for Null Registers), // but for simplicity we do it for Null Registers as well. // switch (family()) { #ifndef HOST_OS_WINDOWS case AF_INET: { struct ip ip4_header; BUFFER_COPYGET_DATA_OFFSET(&ip4_header, buffer, sizeof(uint32_t), sizeof(ip4_header)); ip_tos = ip4_header.ip_tos; break; } #endif #ifdef HAVE_IPV6 case AF_INET6: { struct ip6_hdr ip6_header; BUFFER_COPYGET_DATA_OFFSET(&ip6_header, buffer, sizeof(uint32_t), sizeof(ip6_header)); // Get the Traffic Class ip_tos = (ntohl(ip6_header.ip6_flow) >> 20) & 0xff; break; } #endif // HAVE_IPV6 default: XLOG_UNREACHABLE(); return (XORP_ERROR); } default: break; } // // Prepare the PIM header // // TODO: XXX: PAVPAVPAV: use 'buffer = buffer_send_prepare()' ??? // Point the buffer to the protocol header datalen = BUFFER_DATA_SIZE(buffer); BUFFER_RESET_TAIL(buffer); // pim_vt = PIM_MAKE_VT(proto_version(), message_type); BUFFER_PUT_OCTET(pim_vt, buffer); // PIM version and message type BUFFER_PUT_OCTET(0, buffer); // Reserved BUFFER_PUT_HOST_16(0, buffer); // Zero the checksum field // Restore the buffer to include the data BUFFER_RESET_TAIL(buffer); BUFFER_PUT_SKIP(datalen, buffer); // // Compute the checksum // if (is_ipv6()) { // // XXX: The checksum for IPv6 includes the IPv6 "pseudo-header" // as described in RFC 2460. // size_t ph_len; if (message_type == PIM_REGISTER) ph_len = PIM_REGISTER_HEADER_LENGTH; else ph_len = BUFFER_DATA_SIZE(buffer); cksum2 = calculate_ipv6_pseudo_header_checksum(src, dst, ph_len, IPPROTO_PIM); } // XXX: The checksum for PIM_REGISTER excludes the encapsulated data packet switch (message_type) { case PIM_REGISTER: cksum = inet_checksum(BUFFER_DATA_HEAD(buffer), PIM_REGISTER_HEADER_LENGTH); break; default: cksum = inet_checksum(BUFFER_DATA_HEAD(buffer), BUFFER_DATA_SIZE(buffer)); break; } cksum = inet_checksum_add(cksum, cksum2); BUFFER_COPYPUT_INET_CKSUM(cksum, buffer, 2); // XXX: the checksum XLOG_TRACE(pim_node()->is_log_trace(), "pim_send: TX %s from %s to %s on vif %s", PIMTYPE2ASCII(message_type), cstring(src), cstring(dst), name().c_str()); // // Send the message // ret_value = pim_node()->pim_send(name(), name(), src, dst, pim_node()->ip_protocol_number(), ttl, ip_tos, false, // router alert is deprecated ip_internet_control, buffer, error_msg); // // Actions after the message is sent // if (ret_value == XORP_OK) { switch (message_type) { case PIM_HELLO: set_should_send_pim_hello(false); break; default: break; } } // // Keep statistics per message type // if (ret_value == XORP_OK) { switch (message_type) { case PIM_HELLO: ++_pimstat_hello_messages_sent; break; case PIM_REGISTER: ++_pimstat_register_messages_sent; break; case PIM_REGISTER_STOP: ++_pimstat_register_stop_messages_sent; break; case PIM_JOIN_PRUNE: ++_pimstat_join_prune_messages_sent; break; case PIM_BOOTSTRAP: ++_pimstat_bootstrap_messages_sent; break; case PIM_ASSERT: ++_pimstat_assert_messages_sent; break; case PIM_GRAFT: ++_pimstat_graft_messages_sent; break; case PIM_GRAFT_ACK: ++_pimstat_graft_ack_messages_sent; break; case PIM_CAND_RP_ADV: ++_pimstat_candidate_rp_messages_sent; break; default: break; } } return (ret_value); buflen_error: XLOG_UNREACHABLE(); XLOG_ERROR("TX %s from %s to %s on vif %s: " "packet cannot fit into sending buffer", PIMTYPE2ASCII(message_type), cstring(src), cstring(dst), name().c_str()); return (XORP_ERROR); rcvlen_error: // XXX: this should not happen. The only way to jump here // is if we are trying to send a PIM Register message that did not // contain an IP header, but this is not a valid PIM Register message. XLOG_UNREACHABLE(); return (XORP_ERROR); } /** * PimVif::pim_recv: * @src: The message source address. * @dst: The message destination address. * @buffer: The buffer with the received message. * * Receive PIM message and pass it for processing. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int PimVif::pim_recv(const IPvX& src, const IPvX& dst, buffer_t *buffer) { int ret_value = XORP_ERROR; if (! is_up()) { ++_pimstat_rx_interface_disabled_messages; return (XORP_ERROR); } ret_value = pim_process(src, dst, buffer); return (ret_value); } /** * PimVif::pim_process: * @src: The message source address. * @dst: The message destination address. * @buffer: The buffer with the message. * * Process PIM message and pass the control to the type-specific functions. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int PimVif::pim_process(const IPvX& src, const IPvX& dst, buffer_t *buffer) { uint8_t pim_vt; uint16_t cksum; uint16_t cksum2 = 0; uint8_t message_type, proto_version; PimNbr *pim_nbr; int ret_value = XORP_ERROR; // Ignore my own PIM messages if (pim_node()->is_my_addr(src)) return (XORP_ERROR); // // Message length check. // if (BUFFER_DATA_SIZE(buffer) < PIM_MINLEN) { XLOG_WARNING("RX packet from %s to %s on vif %s: " "too short data field (%u bytes)", cstring(src), cstring(dst), name().c_str(), XORP_UINT_CAST(BUFFER_DATA_SIZE(buffer))); ++_pimstat_bad_length_messages; return (XORP_ERROR); } // // Get the message type and PIM protocol version. // XXX: First we need the message type to verify correctly the checksum. // BUFFER_GET_OCTET(pim_vt, buffer); BUFFER_GET_SKIP_REVERSE(1, buffer); // Rewind back message_type = PIM_VT_T(pim_vt); proto_version = PIM_VT_V(pim_vt); // // Some of the messages should never be received via the PIM Register vif // if (is_pim_register()) { switch (message_type) { case PIM_HELLO: case PIM_JOIN_PRUNE: case PIM_BOOTSTRAP: case PIM_ASSERT: case PIM_GRAFT: case PIM_GRAFT_ACK: return (XORP_ERROR); // Those messages are not allowed case PIM_REGISTER: case PIM_REGISTER_STOP: case PIM_CAND_RP_ADV: break; // Those messages are probably OK default: break; } } // // Checksum verification. // if (is_ipv6()) { // // XXX: The checksum for IPv6 includes the IPv6 "pseudo-header" // as described in RFC 2460. // size_t ph_len; if (message_type == PIM_REGISTER) ph_len = PIM_REGISTER_HEADER_LENGTH; else ph_len = BUFFER_DATA_SIZE(buffer); cksum2 = calculate_ipv6_pseudo_header_checksum(src, dst, ph_len, IPPROTO_PIM); } switch (message_type) { case PIM_REGISTER: cksum = inet_checksum(BUFFER_DATA_HEAD(buffer), PIM_REGISTER_HEADER_LENGTH); cksum = inet_checksum_add(cksum, cksum2); if (cksum == 0) break; // // XXX: Some non-spec compliant (the PC name for "buggy" :) // PIM-SM implementations compute the PIM_REGISTER // checksum over the whole packet instead of only the first 8 octets. // Hence, if the checksum fails over the first 8 octets, try over // the whole packet. // // FALLTHROUGH default: cksum = inet_checksum(BUFFER_DATA_HEAD(buffer), BUFFER_DATA_SIZE(buffer)); cksum = inet_checksum_add(cksum, cksum2); if (cksum == 0) break; // // If this is a PIM Register packet, and if it was truncated // by the kernel (e.g., in some *BSD systems), then ignore the // checksum error. // if (message_type == PIM_REGISTER) { bool is_truncated = false; switch (family()) { case AF_INET: if (BUFFER_DATA_SIZE(buffer) == PIM_REG_MINLEN) is_truncated = true; break; #ifdef HAVE_IPV6 case AF_INET6: if (BUFFER_DATA_SIZE(buffer) == PIM6_REG_MINLEN) is_truncated = true; break; #endif // HAVE_IPV6 default: XLOG_UNREACHABLE(); return (XORP_ERROR); } if (is_truncated) break; // XXX: accept the truncated PIM Register } XLOG_WARNING("RX packet from %s to %s on vif %s: " "checksum error", cstring(src), cstring(dst), name().c_str()); ++_pimstat_bad_checksum_messages; return (XORP_ERROR); } // // Protocol version check. // // Note about protocol version checking (based on clarification/suggestion // from Mark Handley). // The expectation is that any protocol version increase would be // signalled in PIM Hello messages, and newer versions would be // required to fall back to the version understood by everybody, // or refuse to communicate with older versions (as they choose). // Hence, we drop everything other than a PIM Hello message // with version greather than the largest one we understand // (PIM_VERSION_MAX), but we log a warning. On the other hand, // we don't understand anything about versions smaller than // PIM_VERSION_MIN, hence we drop all messages with that version. if ((proto_version < PIM_VERSION_MIN) || ((proto_version > PIM_VERSION_MAX) && (message_type != PIM_HELLO))) { XLOG_WARNING("RX %s from %s to %s on vif %s: " "invalid PIM version: %d", PIMTYPE2ASCII(message_type), cstring(src), cstring(dst), name().c_str(), proto_version); ++_pimstat_unknown_version_messages; return (XORP_ERROR); } // // Source address check. // if (! src.is_unicast()) { // Source address must always be unicast // The kernel should have checked that, but just in case XLOG_WARNING("RX %s from %s to %s on vif %s: " "source must be unicast", PIMTYPE2ASCII(message_type), cstring(src), cstring(dst), name().c_str()); ret_value = XORP_ERROR; goto ret_label; } if (src.af() != family()) { // Invalid source address family XLOG_WARNING("RX %s from %s to %s on vif %s: " "invalid source address family " "(received %d expected %d)", PIMTYPE2ASCII(message_type), cstring(src), cstring(dst), name().c_str(), src.af(), family()); } switch (message_type) { case PIM_HELLO: case PIM_JOIN_PRUNE: case PIM_ASSERT: case PIM_GRAFT: case PIM_GRAFT_ACK: case PIM_BOOTSTRAP: // Source address must be directly connected if (! pim_node()->is_directly_connected(*this, src)) { XLOG_WARNING("RX %s from %s to %s on vif %s: " "source must be directly connected", PIMTYPE2ASCII(message_type), cstring(src), cstring(dst), name().c_str()); ret_value = XORP_ERROR; goto ret_label; } #if 0 // TODO: this check has to be fixed in case we use GRE tunnels if (! src.is_linklocal_unicast()) { XLOG_WARNING("RX %s from %s to %s on vif %s: " "source is not a link-local address", PIMTYPE2ASCII(message_type), cstring(src), cstring(dst), name().c_str()); ret_value = XORP_ERROR; goto ret_label; } #endif // 0/1 break; case PIM_REGISTER: case PIM_REGISTER_STOP: case PIM_CAND_RP_ADV: // Source address can be anywhere // TODO: any source address check? break; default: break; } // // Destination address check. // if (dst.af() != family()) { // Invalid destination address family XLOG_WARNING("RX %s from %s to %s on vif %s: " "invalid destination address family " "(received %d expected %d)", PIMTYPE2ASCII(message_type), cstring(src), cstring(dst), name().c_str(), dst.af(), family()); } switch (message_type) { case PIM_HELLO: case PIM_JOIN_PRUNE: case PIM_ASSERT: case PIM_GRAFT: // Destination must be multicast if (! dst.is_multicast()) { XLOG_WARNING("RX %s from %s to %s on vif %s: " "destination must be multicast", PIMTYPE2ASCII(message_type), cstring(src), cstring(dst), name().c_str()); ret_value = XORP_ERROR; goto ret_label; } #ifdef HAVE_IPV6 if (is_ipv6()) { // // TODO: Multicast address scope check for IPv6 // } #endif // HAVE_IPV6 if (dst != IPvX::PIM_ROUTERS(family())) { XLOG_WARNING("RX %s from %s to %s on vif %s: " "destination must be ALL-PIM-ROUTERS multicast group", PIMTYPE2ASCII(message_type), cstring(src), cstring(dst), name().c_str()); ret_value = XORP_ERROR; goto ret_label; } break; case PIM_REGISTER: case PIM_REGISTER_STOP: case PIM_GRAFT_ACK: case PIM_CAND_RP_ADV: // Destination must be unicast if (! dst.is_unicast()) { XLOG_WARNING("RX %s from %s to %s on vif %s: " "destination must be unicast", PIMTYPE2ASCII(message_type), cstring(src), cstring(dst), name().c_str()); ret_value = XORP_ERROR; goto ret_label; } break; case PIM_BOOTSTRAP: // Destination can be either unicast or multicast if (! (dst.is_unicast() || dst.is_multicast())) { XLOG_WARNING("RX %s from %s to %s on vif %s: " "destination must be either unicast or multicast", PIMTYPE2ASCII(message_type), cstring(src), cstring(dst), name().c_str()); ret_value = XORP_ERROR; goto ret_label; } #ifdef HAVE_IPV6 if (dst.is_unicast()) { // TODO: address check (if any) } if (dst.is_multicast()) { if (dst != IPvX::PIM_ROUTERS(family())) { XLOG_WARNING("RX %s from %s to %s on vif %s: " "destination must be ALL-PIM-ROUTERS multicast group", PIMTYPE2ASCII(message_type), cstring(src), cstring(dst), name().c_str()); ret_value = XORP_ERROR; goto ret_label; } if (is_ipv6()) { // // TODO: Multicast address scope check for IPv6 // } } #endif // HAVE_IPV6 break; default: break; } // // Message-specific checks. // switch (message_type) { case PIM_HELLO: case PIM_JOIN_PRUNE: case PIM_ASSERT: // PIM-SM and PIM-DM messages break; case PIM_REGISTER: case PIM_REGISTER_STOP: case PIM_BOOTSTRAP: case PIM_CAND_RP_ADV: // PIM-SM only messages if (proto_is_pimdm()) { XLOG_WARNING("RX %s from %s to %s on vif %s: " "message type is PIM-SM specific", PIMTYPE2ASCII(message_type), cstring(src), cstring(dst), name().c_str()); ret_value = XORP_ERROR; goto ret_label; } break; case PIM_GRAFT: case PIM_GRAFT_ACK: // PIM-DM only messages if (proto_is_pimsm()) { XLOG_WARNING("RX %s from %s to %s on vif %s: " "message type is PIM-DM specific", PIMTYPE2ASCII(message_type), cstring(src), cstring(dst), name().c_str()); ret_value = XORP_ERROR; goto ret_label; } break; default: XLOG_WARNING("RX %s from %s to %s on vif %s: " "message type (%d) is unknown", PIMTYPE2ASCII(message_type), cstring(src), cstring(dst), name().c_str(), message_type); ret_value = XORP_ERROR; goto ret_label; } // // Origin router neighbor check. // pim_nbr = pim_nbr_find(src); switch (message_type) { case PIM_HELLO: // This could be a new neighbor break; case PIM_JOIN_PRUNE: case PIM_BOOTSTRAP: case PIM_ASSERT: case PIM_GRAFT: case PIM_GRAFT_ACK: // Those messages must be originated by a neighbor router if (((pim_nbr == NULL) || ((pim_nbr != NULL) && (pim_nbr->is_nohello_neighbor()))) && accept_nohello_neighbors().get()) { // We are configured to interoperate with neighbors that // do not send Hello messages first. // XXX: fake that we have received a Hello message with // large enough Hello holdtime. buffer_t *tmp_hello_buffer = BUFFER_MALLOC(BUF_SIZE_DEFAULT); uint16_t tmp_default_holdtime = max(PIM_HELLO_HELLO_HOLDTIME_DEFAULT, PIM_JOIN_PRUNE_HOLDTIME_DEFAULT); bool is_nohello_neighbor = false; if (pim_nbr == NULL) is_nohello_neighbor = true; BUFFER_RESET(tmp_hello_buffer); BUFFER_PUT_HOST_16(PIM_HELLO_HOLDTIME_OPTION, tmp_hello_buffer); BUFFER_PUT_HOST_16(PIM_HELLO_HOLDTIME_LENGTH, tmp_hello_buffer); BUFFER_PUT_HOST_16(tmp_default_holdtime, tmp_hello_buffer); pim_hello_recv(pim_nbr, src, dst, tmp_hello_buffer, proto_version); BUFFER_FREE(tmp_hello_buffer); pim_nbr = pim_nbr_find(src); if ((pim_nbr != NULL) && is_nohello_neighbor) pim_nbr->set_is_nohello_neighbor(is_nohello_neighbor); } if (pim_nbr == NULL) { XLOG_WARNING("RX %s from %s to %s on vif %s: " "sender is not a PIM-neighbor router", PIMTYPE2ASCII(message_type), cstring(src), cstring(dst), name().c_str()); ++_pimstat_neighbor_unknown_messages; ret_value = XORP_ERROR; goto ret_label; } break; case PIM_REGISTER: case PIM_REGISTER_STOP: case PIM_CAND_RP_ADV: // Those messages may be originated by a remote router break; default: break; } XLOG_TRACE(pim_node()->is_log_trace(), "RX %s from %s to %s on vif %s", PIMTYPE2ASCII(message_type), cstring(src), cstring(dst), name().c_str()); /* * Process each message based on its type. */ BUFFER_GET_SKIP(sizeof(struct pim), buffer); switch (message_type) { case PIM_HELLO: ret_value = pim_hello_recv(pim_nbr, src, dst, buffer, proto_version); break; case PIM_REGISTER: ret_value = pim_register_recv(pim_nbr, src, dst, buffer); break; case PIM_REGISTER_STOP: ret_value = pim_register_stop_recv(pim_nbr, src, dst, buffer); break; case PIM_JOIN_PRUNE: ret_value = pim_join_prune_recv(pim_nbr, src, dst, buffer, message_type); break; case PIM_BOOTSTRAP: ret_value = pim_bootstrap_recv(pim_nbr, src, dst, buffer); break; case PIM_ASSERT: ret_value = pim_assert_recv(pim_nbr, src, dst, buffer); break; case PIM_GRAFT: ret_value = pim_graft_recv(pim_nbr, src, dst, buffer); break; case PIM_GRAFT_ACK: ret_value = pim_graft_ack_recv(pim_nbr, src, dst, buffer); break; case PIM_CAND_RP_ADV: ret_value = pim_cand_rp_adv_recv(pim_nbr, src, dst, buffer); break; default: break; } ret_label: // // Keep statistics per message type // if (ret_value == XORP_OK) { switch (message_type) { case PIM_HELLO: ++_pimstat_hello_messages_received; break; case PIM_REGISTER: ++_pimstat_register_messages_received; break; case PIM_REGISTER_STOP: ++_pimstat_register_stop_messages_received; break; case PIM_JOIN_PRUNE: ++_pimstat_join_prune_messages_received; break; case PIM_BOOTSTRAP: ++_pimstat_bootstrap_messages_received; break; case PIM_ASSERT: ++_pimstat_assert_messages_received; break; case PIM_GRAFT: ++_pimstat_graft_messages_received; break; case PIM_GRAFT_ACK: ++_pimstat_graft_ack_messages_received; break; case PIM_CAND_RP_ADV: ++_pimstat_candidate_rp_messages_received; break; default: ++_pimstat_unknown_type_messages; break; } } else { switch (message_type) { case PIM_HELLO: ++_pimstat_hello_messages_rx_errors; break; case PIM_REGISTER: ++_pimstat_register_messages_rx_errors; break; case PIM_REGISTER_STOP: ++_pimstat_register_stop_messages_rx_errors; break; case PIM_JOIN_PRUNE: ++_pimstat_join_prune_messages_rx_errors; break; case PIM_BOOTSTRAP: ++_pimstat_bootstrap_messages_rx_errors; break; case PIM_ASSERT: ++_pimstat_assert_messages_rx_errors; break; case PIM_GRAFT: ++_pimstat_graft_messages_rx_errors; break; case PIM_GRAFT_ACK: ++_pimstat_graft_ack_messages_rx_errors; break; case PIM_CAND_RP_ADV: ++_pimstat_candidate_rp_messages_rx_errors; break; default: ++_pimstat_unknown_type_messages; break; } } return (ret_value); rcvlen_error: XLOG_UNREACHABLE(); XLOG_WARNING("RX packet from %s to %s on vif %s: " "some fields are too short", cstring(src), cstring(dst), name().c_str()); ++_pimstat_rx_malformed_packet; return (XORP_ERROR); buflen_error: XLOG_UNREACHABLE(); XLOG_WARNING("RX packet from %s to %s on vif %s: " "internal error", cstring(src), cstring(dst), name().c_str()); return (XORP_ERROR); } /** * PimVif::buffer_send_prepare: * @: * * Reset and prepare the default buffer for sending data. * * Return value: The prepared buffer. **/ buffer_t * PimVif::buffer_send_prepare() { return (buffer_send_prepare(_buffer_send)); } /** * PimVif::buffer_send_prepare: * @buffer: The buffer to prepare. * * Reset and prepare buffer @buffer for sendign data. * * Return value: The prepared buffer. **/ buffer_t * PimVif::buffer_send_prepare(buffer_t *buffer) { BUFFER_RESET(buffer); BUFFER_PUT_SKIP_PIM_HEADER(buffer); return (buffer); buflen_error: XLOG_UNREACHABLE(); XLOG_ERROR("INTERNAL buffer_send_prepare() ERROR: buffer size too small"); return (NULL); } /** * PimVif::update_primary_and_domain_wide_address: * @error_msg: The error message (if error). * * Update the primary and the domain-wide reachable addresses. * * The primary address should be a link-local unicast address, and * is used for transmitting the multicast control packets on the LAN. * The domain-wide reachable address is the address that should be * reachable by all PIM-SM routers in the domain * (e.g., the Cand-BSR, or the Cand-RP address). * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int PimVif::update_primary_and_domain_wide_address(string& error_msg) { bool i_was_dr = false; IPvX primary_a(IPvX::ZERO(family())); IPvX domain_wide_a(IPvX::ZERO(family())); // // Reset the primary and the domain-wide addresses if they are not // valid anymore. // if (Vif::find_address(primary_addr()) == NULL) { if (primary_addr() == dr_addr()) { // Reset the DR address _dr_addr = IPvX::ZERO(family()); i_was_dr = true; } pim_nbr_me().set_primary_addr(IPvX::ZERO(family())); } if (Vif::find_address(domain_wide_addr()) == NULL) set_domain_wide_addr(IPvX::ZERO(family())); list::const_iterator iter; for (iter = addr_list().begin(); iter != addr_list().end(); ++iter) { const VifAddr& vif_addr = *iter; const IPvX& addr = vif_addr.addr(); if (! addr.is_unicast()) continue; if (addr.is_linklocal_unicast()) { if (primary_a.is_zero()) primary_a = addr; continue; } // // XXX: assume that everything else can be a domain-wide reachable // address. // if (domain_wide_a.is_zero()) domain_wide_a = addr; } // // XXX: In case of IPv6 if there is no link-local address we may try // to use the the domain-wide address as a primary address, // but the PIM-SM spec is clear that the multicast PIM messages are // to be originated from a link-local address. // Hence, only in case of IPv4 we assign the domain-wide address // to the primary address. // if (is_ipv4()) { if (primary_a.is_zero()) primary_a = domain_wide_a; } // // Check that the interface has a primary and a domain-wide reachable // addresses. // if (primary_addr().is_zero() && primary_a.is_zero()) { error_msg = "invalid primary address"; return (XORP_ERROR); } if (domain_wide_addr().is_zero() && domain_wide_a.is_zero()) { error_msg = "invalid domain-wide address"; return (XORP_ERROR); } if (primary_addr().is_zero()) pim_nbr_me().set_primary_addr(primary_a); // Set my PimNbr address if (domain_wide_addr().is_zero()) set_domain_wide_addr(domain_wide_a); if (i_was_dr) pim_dr_elect(); return (XORP_OK); } /** * PimVif::calculate_ipv6_pseudo_header_checksum: * @src: the source address of the pseudo-header. * @dst: the destination address of the pseudo-header. * @len: the upper-layer packet length of the pseudo-header * (in host-order). * @protocol: the upper-layer protocol number. * * Calculate the checksum of an IPv6 "pseudo-header" as described * in RFC 2460. * * Return value: the checksum of the IPv6 "pseudo-header". **/ uint16_t PimVif::calculate_ipv6_pseudo_header_checksum(const IPvX& src, const IPvX& dst, size_t len, uint8_t protocol) { struct ip6_pseudo_hdr { struct in6_addr ip6_src; // Source address struct in6_addr ip6_dst; // Destination address uint32_t ph_len; // Upper-layer packet length uint8_t ph_zero[3]; // Zero uint8_t ph_next; // Upper-layer protocol number } ip6_pseudo_header; // TODO: may need __attribute__((__packed__)) src.copy_out(ip6_pseudo_header.ip6_src); dst.copy_out(ip6_pseudo_header.ip6_dst); ip6_pseudo_header.ph_len = htonl(len); ip6_pseudo_header.ph_zero[0] = 0; ip6_pseudo_header.ph_zero[1] = 0; ip6_pseudo_header.ph_zero[2] = 0; ip6_pseudo_header.ph_next = protocol; uint16_t cksum = inet_checksum( reinterpret_cast(&ip6_pseudo_header), sizeof(ip6_pseudo_header)); return (cksum); } /** * PimVif::pim_nbr_find: * @nbr_addr: The address of the neighbor to search for. * * Find a PIM neighbor by its address. * * Return value: The #PimNbr entry for the neighbor if found, otherwise %NULL. **/ PimNbr * PimVif::pim_nbr_find(const IPvX& nbr_addr) { list::iterator iter; for (iter = _pim_nbrs.begin(); iter != _pim_nbrs.end(); ++iter) { PimNbr *pim_nbr = *iter; if (pim_nbr->is_my_addr(nbr_addr)) return (pim_nbr); } return (NULL); } void PimVif::add_pim_nbr(PimNbr *pim_nbr) { TimeVal now; TimerList::system_gettimeofday(&now); pim_nbr->set_startup_time(now); _pim_nbrs.push_back(pim_nbr); } void PimVif::delete_pim_nbr_from_nbr_list(PimNbr *pim_nbr) { list::iterator iter; iter = find(_pim_nbrs.begin(), _pim_nbrs.end(), pim_nbr); if (iter != _pim_nbrs.end()) { XLOG_TRACE(pim_node()->is_log_trace(), "Delete neighbor %s on vif %s", cstring(pim_nbr->primary_addr()), name().c_str()); _pim_nbrs.erase(iter); } } int PimVif::delete_pim_nbr(PimNbr *pim_nbr) { delete_pim_nbr_from_nbr_list(pim_nbr); if (find(pim_node()->processing_pim_nbr_list().begin(), pim_node()->processing_pim_nbr_list().end(), pim_nbr) == pim_node()->processing_pim_nbr_list().end()) { // // The PimNbr is not on the processing list, hence move it there // if (pim_nbr->pim_mre_rp_list().empty() && pim_nbr->pim_mre_wc_list().empty() && pim_nbr->pim_mre_sg_list().empty() && pim_nbr->pim_mre_sg_rpt_list().empty() && pim_nbr->processing_pim_mre_rp_list().empty() && pim_nbr->processing_pim_mre_wc_list().empty() && pim_nbr->processing_pim_mre_sg_list().empty() && pim_nbr->processing_pim_mre_sg_rpt_list().empty()) { delete pim_nbr; } else { pim_node()->processing_pim_nbr_list().push_back(pim_nbr); pim_node()->pim_mrt().add_task_pim_nbr_changed(Vif::VIF_INDEX_INVALID, pim_nbr->primary_addr()); } } return (XORP_OK); } bool PimVif::is_lan_delay_enabled() const { list::const_iterator iter; for (iter = _pim_nbrs.begin(); iter != _pim_nbrs.end(); ++iter) { const PimNbr *pim_nbr = *iter; if (! pim_nbr->is_lan_prune_delay_present()) { return (false); } } return (true); } const TimeVal& PimVif::effective_propagation_delay() const { static TimeVal tv; uint16_t delay; do { if (! is_lan_delay_enabled()) { delay = _propagation_delay.get_initial_value(); break; } delay = _propagation_delay.get(); list::const_iterator iter; for (iter = _pim_nbrs.begin(); iter != _pim_nbrs.end(); ++iter) { PimNbr *pim_nbr = *iter; if (pim_nbr->propagation_delay() > delay) delay = pim_nbr->propagation_delay(); } break; } while (false); // XXX: delay is in milliseconds tv = TimeVal(delay / 1000, (delay % 1000) * 1000); return (tv); } const TimeVal& PimVif::effective_override_interval() const { static TimeVal tv; uint16_t delay; do { if (! is_lan_delay_enabled()) { delay = _override_interval.get_initial_value(); break; } delay = _override_interval.get(); list::const_iterator iter; for (iter = _pim_nbrs.begin(); iter != _pim_nbrs.end(); ++iter) { PimNbr *pim_nbr = *iter; if (pim_nbr->override_interval() > delay) delay = pim_nbr->override_interval(); } break; } while (false); // XXX: delay is in milliseconds tv = TimeVal(delay / 1000, (delay % 1000) * 1000); return (tv); } bool PimVif::is_lan_suppression_state_enabled() const { if (! is_lan_delay_enabled()) return (true); list::const_iterator iter; for (iter = _pim_nbrs.begin(); iter != _pim_nbrs.end(); ++iter) { PimNbr *pim_nbr = *iter; if (! pim_nbr->is_tracking_support_disabled()) { return (true); } } return (false); } // // Compute the randomized 't_suppressed' interval: // t_suppressed = rand(1.1 * t_periodic, 1.4 * t_periodic) when // Suppression_Enabled(I) is true, 0 otherwise // const TimeVal& PimVif::upstream_join_timer_t_suppressed() const { static TimeVal tv; if (is_lan_suppression_state_enabled()) { tv = TimeVal(_join_prune_period.get(), 0); tv = random_uniform( tv * PIM_JOIN_PRUNE_SUPPRESSION_TIMEOUT_RANDOM_FACTOR_MIN, tv * PIM_JOIN_PRUNE_SUPPRESSION_TIMEOUT_RANDOM_FACTOR_MAX); } else { tv = TimeVal::ZERO(); } return (tv); } // // Compute the randomized 't_override' interval value for Upstream Join Timer: // t_override = rand(0, Effective_Override_Interval(I)) // const TimeVal& PimVif::upstream_join_timer_t_override() const { static TimeVal tv; // XXX: explicitly assign the value to 'tv' every time this method // is called, because 'tv' is static. tv = effective_override_interval(); // Randomize tv = random_uniform(tv); return (tv); } // Return the J/P_Override_Interval const TimeVal& PimVif::jp_override_interval() const { static TimeVal tv; TimeVal res1, res2; res1 = effective_propagation_delay(); res2 = effective_override_interval(); tv = res1 + res2; return (tv); } /** * PimVif::i_am_dr: * @: * * Test if the protocol instance is a DR (Designated Router) * on a virtual interface. * * Return value: True if the protocol instance is DR on a virtual interface, * otherwise false. **/ bool PimVif::i_am_dr() const { if (_proto_flags & PIM_VIF_DR) return (true); else return (false); } /** * PimVif::set_i_am_dr: * @v: If true, set the %PIM_VIF_DR flag, otherwise reset it. * * Set/reset the %PIM_VIF_DR (Designated Router) flag on a virtual interface. **/ void PimVif::set_i_am_dr(bool v) { if (v) { _proto_flags |= PIM_VIF_DR; } else { _proto_flags &= ~PIM_VIF_DR; } pim_node()->set_pim_vifs_dr(vif_index(), v); } void PimVif::incr_usage_by_pim_mre_task() { _usage_by_pim_mre_task++; } void PimVif::decr_usage_by_pim_mre_task() { string error_msg; XLOG_ASSERT(_usage_by_pim_mre_task > 0); _usage_by_pim_mre_task--; if (_usage_by_pim_mre_task == 0) { if (is_pending_down()) { final_stop(error_msg); } } } void PimVif::add_alternative_subnet(const IPvXNet& subnet) { list::iterator iter; iter = find(_alternative_subnet_list.begin(), _alternative_subnet_list.end(), subnet); if (iter != _alternative_subnet_list.end()) return; // Already added _alternative_subnet_list.push_back(subnet); // // Add the tasks to take care of the PimMre processing // pim_node()->pim_mrt().add_task_my_ip_subnet_address(vif_index()); } void PimVif::delete_alternative_subnet(const IPvXNet& subnet) { list::iterator iter; iter = find(_alternative_subnet_list.begin(), _alternative_subnet_list.end(), subnet); if (iter == _alternative_subnet_list.end()) return; // No such subnet _alternative_subnet_list.erase(iter); // // Add the tasks to take care of the PimMre processing // pim_node()->pim_mrt().add_task_my_ip_subnet_address(vif_index()); } void PimVif::remove_all_alternative_subnets() { if (_alternative_subnet_list.empty()) return; // No alternative subnets to remove _alternative_subnet_list.clear(); // // Add the tasks to take care of the PimMre processing // pim_node()->pim_mrt().add_task_my_ip_subnet_address(vif_index()); } // TODO: temporary here. Should go to the Vif class after the Vif // class starts using the ProtoUnit class string PimVif::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); } void PimVif::clear_pim_statistics() { _pimstat_hello_messages_received.reset(); _pimstat_hello_messages_sent.reset(); _pimstat_hello_messages_rx_errors.reset(); _pimstat_register_messages_received.reset(); _pimstat_register_messages_sent.reset(); _pimstat_register_messages_rx_errors.reset(); _pimstat_register_stop_messages_received.reset(); _pimstat_register_stop_messages_sent.reset(); _pimstat_register_stop_messages_rx_errors.reset(); _pimstat_join_prune_messages_received.reset(); _pimstat_join_prune_messages_sent.reset(); _pimstat_join_prune_messages_rx_errors.reset(); _pimstat_bootstrap_messages_received.reset(); _pimstat_bootstrap_messages_sent.reset(); _pimstat_bootstrap_messages_rx_errors.reset(); _pimstat_assert_messages_received.reset(); _pimstat_assert_messages_sent.reset(); _pimstat_assert_messages_rx_errors.reset(); _pimstat_graft_messages_received.reset(); _pimstat_graft_messages_sent.reset(); _pimstat_graft_messages_rx_errors.reset(); _pimstat_graft_ack_messages_received.reset(); _pimstat_graft_ack_messages_sent.reset(); _pimstat_graft_ack_messages_rx_errors.reset(); _pimstat_candidate_rp_messages_received.reset(); _pimstat_candidate_rp_messages_sent.reset(); _pimstat_candidate_rp_messages_rx_errors.reset(); // _pimstat_unknown_type_messages.reset(); _pimstat_unknown_version_messages.reset(); _pimstat_neighbor_unknown_messages.reset(); _pimstat_bad_length_messages.reset(); _pimstat_bad_checksum_messages.reset(); _pimstat_bad_receive_interface_messages.reset(); _pimstat_rx_interface_disabled_messages.reset(); _pimstat_rx_register_not_rp.reset(); _pimstat_rp_filtered_source.reset(); _pimstat_unknown_register_stop.reset(); _pimstat_rx_join_prune_no_state.reset(); _pimstat_rx_graft_graft_ack_no_state.reset(); _pimstat_rx_graft_on_upstream_interface.reset(); _pimstat_rx_candidate_rp_not_bsr.reset(); _pimstat_rx_bsr_when_bsr.reset(); _pimstat_rx_bsr_not_rpf_interface.reset(); _pimstat_rx_unknown_hello_option.reset(); _pimstat_rx_data_no_state.reset(); _pimstat_rx_rp_no_state.reset(); _pimstat_rx_aggregate.reset(); _pimstat_rx_malformed_packet.reset(); _pimstat_no_rp.reset(); _pimstat_no_route_upstream.reset(); _pimstat_rp_mismatch.reset(); _pimstat_rpf_neighbor_unknown.reset(); // _pimstat_rx_join_rp.reset(); _pimstat_rx_prune_rp.reset(); _pimstat_rx_join_wc.reset(); _pimstat_rx_prune_wc.reset(); _pimstat_rx_join_sg.reset(); _pimstat_rx_prune_sg.reset(); _pimstat_rx_join_sg_rpt.reset(); _pimstat_rx_prune_sg_rpt.reset(); } xorp/pim/pim_proto_graft_ack.cc0000664000076400007640000000346711421137511016773 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // PIM PIM_GRAFT_ACK messages processing. // #include "pim_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvx.hh" #include "pim_vif.hh" // // Exported variables // // // Local constants definitions // // // Local structures/classes, typedefs and macros // // // Local variables // // // Local functions prototypes // /** * PimVif::pim_graft_ack_recv: * @pim_nbr: The PIM neighbor message originator (or NULL if not a neighbor). * @src: The message source address. * @dst: The message destination address. * @buffer: The buffer with the message. * * Receive PIM_GRAFT_ACK message. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int PimVif::pim_graft_ack_recv(PimNbr *pim_nbr, const IPvX& , // src const IPvX& , // dst buffer_t *buffer) { UNUSED(pim_nbr); // UNUSED(src); // UNUSED(dst); UNUSED(buffer); return (XORP_OK); } xorp/pim/pim_proto_register_stop.cc0000664000076400007640000001501711613330633017740 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // PIM PIM_REGISTER_STOP messages processing. // #include "pim_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvx.hh" #include "pim_mre.hh" #include "pim_mrt.hh" #include "pim_vif.hh" /** * PimVif::pim_register_stop_recv: * @pim_nbr: The PIM neighbor message originator (or NULL if not a neighbor). * @src: The message source address. * @dst: The message destination address. * @buffer: The buffer with the message. * * Receive PIM_REGISTER_STOP message. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int PimVif::pim_register_stop_recv(PimNbr *pim_nbr, const IPvX& src, const IPvX& dst, buffer_t *buffer) { int rcvd_family; uint8_t group_addr_reserved_flags; uint8_t group_mask_len; IPvX source_addr(family()), group_addr(family()); UNUSED(dst); UNUSED(group_addr_reserved_flags); // // Parse the message // GET_ENCODED_GROUP_ADDR(rcvd_family, group_addr, group_mask_len, group_addr_reserved_flags, buffer); GET_ENCODED_UNICAST_ADDR(rcvd_family, source_addr, buffer); if (! group_addr.is_multicast()) { XLOG_WARNING("RX %s from %s to %s: " "group address = %s must be multicast", PIMTYPE2ASCII(PIM_REGISTER_STOP), cstring(src), cstring(dst), cstring(group_addr)); return (XORP_ERROR); } if (group_addr.is_linklocal_multicast() || group_addr.is_interfacelocal_multicast()) { XLOG_WARNING("RX %s from %s to %s: " "group address = %s must not be be link or " "interface-local multicast", PIMTYPE2ASCII(PIM_REGISTER_STOP), cstring(src), cstring(dst), cstring(group_addr)); return (XORP_ERROR); } if (! (source_addr.is_unicast() || source_addr.is_zero())) { XLOG_WARNING("RX %s from %s to %s: " "source address = %s must be either unicast or zero", PIMTYPE2ASCII(PIM_REGISTER_STOP), cstring(src), cstring(dst), cstring(source_addr)); return (XORP_ERROR); } // // Process the Register-Stop data // pim_register_stop_process(src, source_addr, group_addr, group_mask_len); UNUSED(pim_nbr); return (XORP_OK); // Various error processing rcvlen_error: XLOG_WARNING("RX %s from %s to %s: " "invalid message length", PIMTYPE2ASCII(PIM_REGISTER_STOP), cstring(src), cstring(dst)); ++_pimstat_rx_malformed_packet; return (XORP_ERROR); rcvd_mask_len_error: XLOG_WARNING("RX %s from %s to %s: " "invalid group mask length = %d", PIMTYPE2ASCII(PIM_REGISTER_STOP), cstring(src), cstring(dst), group_mask_len); return (XORP_ERROR); rcvd_family_error: XLOG_WARNING("RX %s from %s to %s: " "invalid address family inside = %d", PIMTYPE2ASCII(PIM_REGISTER_STOP), cstring(src), cstring(dst), rcvd_family); return (XORP_ERROR); } int PimVif::pim_register_stop_process(const IPvX& rp_addr, const IPvX& source_addr, const IPvX& group_addr, uint8_t group_mask_len) { uint32_t lookup_flags; PimMre *pim_mre; UNUSED(rp_addr); lookup_flags = PIM_MRE_SG; if (group_mask_len != IPvX::addr_bitlen(family())) { XLOG_WARNING("RX %s from %s to %s: " "invalid group mask length = %d " "instead of %u", PIMTYPE2ASCII(PIM_REGISTER_STOP), cstring(rp_addr), cstring(domain_wide_addr()), group_mask_len, XORP_UINT_CAST(IPvX::addr_bitlen(family()))); return (XORP_ERROR); } if (! source_addr.is_zero()) { // (S,G) Register-Stop pim_mre = pim_mrt().pim_mre_find(source_addr, group_addr, lookup_flags, 0); if (pim_mre == NULL) { // XXX: we don't have this (S,G) state. Ignore // TODO: print a warning, or do something else? ++_pimstat_unknown_register_stop; return (XORP_ERROR); } pim_mre->receive_register_stop(); return (XORP_OK); } // (*,G) Register-Stop // Apply to all (S,G) entries for this group that are not in the NoInfo // state. // TODO: XXX: PAVPAVPAV: should schedule a timeslice task for this. PimMrtSg::const_gs_iterator iter_begin, iter_end, iter; iter_begin = pim_mrt().pim_mrt_sg().group_by_addr_begin(group_addr); iter_end = pim_mrt().pim_mrt_sg().group_by_addr_end(group_addr); for (iter = iter_begin; iter != iter_end; ++iter) { PimMre *pim_mre = iter->second; if (! pim_mre->is_register_noinfo_state()) pim_mre->receive_register_stop(); } return (XORP_OK); } int PimVif::pim_register_stop_send(const IPvX& dr_addr, const IPvX& source_addr, const IPvX& group_addr, string& error_msg) { uint8_t group_mask_len = IPvX::addr_bitlen(family()); buffer_t *buffer = buffer_send_prepare(); uint8_t group_addr_reserved_flags = 0; // Write all data to the buffer PUT_ENCODED_GROUP_ADDR(family(), group_addr, group_mask_len, group_addr_reserved_flags, buffer); PUT_ENCODED_UNICAST_ADDR(family(), source_addr, buffer); return (pim_send(domain_wide_addr(), dr_addr, PIM_REGISTER_STOP, buffer, error_msg)); invalid_addr_family_error: XLOG_UNREACHABLE(); error_msg = c_format("TX %s from %s to %s: " "invalid address family error = %d", PIMTYPE2ASCII(PIM_REGISTER_STOP), cstring(domain_wide_addr()), cstring(dr_addr), family()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); buflen_error: XLOG_UNREACHABLE(); error_msg = c_format("TX %s from %s to %s: " "packet cannot fit into sending buffer", PIMTYPE2ASCII(PIM_REGISTER_STOP), cstring(domain_wide_addr()), cstring(dr_addr)); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } xorp/pim/pim_mrt_mfc.cc0000664000076400007640000004737111634760770015277 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // PIM Multicast Routing Table MFC-related implementation. // #include "pim_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvx.hh" #include "pim_mfc.hh" #include "pim_mrt.hh" #include "pim_node.hh" #include "pim_vif.hh" int PimMrt::signal_message_nocache_recv(const string& src_module_instance_name, uint32_t vif_index, const IPvX& src, const IPvX& dst) { XLOG_TRACE(pim_node()->is_log_trace(), "RX NOCACHE signal from %s: vif_index = %d src = %s dst = %s", src_module_instance_name.c_str(), vif_index, cstring(src), cstring(dst)); UNUSED(src_module_instance_name); // in case XLOG_TRACE is compiled out. receive_data(vif_index, src, dst); return (XORP_OK); } int PimMrt::signal_message_wrongvif_recv(const string& src_module_instance_name, uint32_t vif_index, const IPvX& src, const IPvX& dst) { XLOG_TRACE(pim_node()->is_log_trace(), "RX WRONGVIF signal from %s: vif_index = %d src = %s dst = %s", src_module_instance_name.c_str(), vif_index, cstring(src), cstring(dst)); UNUSED(src_module_instance_name); // in case XLOG_TRACE is compiled out. receive_data(vif_index, src, dst); return (XORP_OK); } int PimMrt::signal_message_wholepkt_recv(const string& src_module_instance_name, uint32_t vif_index, const IPvX& src, const IPvX& dst, const uint8_t *rcvbuf, size_t rcvlen) { PimMre *pim_mre_sg; const IPvX *rp_addr_ptr; PimVif *pim_vif = NULL; string dummy_error_msg; UNUSED(src_module_instance_name); XLOG_TRACE(pim_node()->is_log_trace(), "RX WHOLEPKT signal from %s: vif_index = %d " "src = %s dst = %s len = %u", src_module_instance_name.c_str(), vif_index, cstring(src), cstring(dst), XORP_UINT_CAST(rcvlen)); // // Find the corresponding (S,G) multicast routing entry // pim_mre_sg = pim_mre_find(src, dst, PIM_MRE_SG, 0); if (pim_mre_sg == NULL) { // This shoudn't happen, because to receive WHOLEPKT signal, // we must have installed first the appropriate (S,G) MFC entry // that matches a (S,G) PimMre entry. XLOG_ERROR("RX WHOLEPKT signal from %s: vif_index = %d " "src = %s dst = %s len = %u: " "no matching (S,G) multicast routing entry", src_module_instance_name.c_str(), vif_index, cstring(src), cstring(dst), XORP_UINT_CAST(rcvlen)); return (XORP_ERROR); } // // Find the RP address // rp_addr_ptr = pim_mre_sg->rp_addr_ptr(); if (rp_addr_ptr == NULL) { XLOG_WARNING("RX WHOLEPKT signal from %s: vif_index = %d " "src = %s dst = %s len = %u: " "no RP address for this group", src_module_instance_name.c_str(), vif_index, cstring(src), cstring(dst), XORP_UINT_CAST(rcvlen)); return (XORP_ERROR); } // // XXX: If we want to be pedantic, here we might want to lookup // the PimMfc entries and check whether the PIM Register vif is // in the list of outgoing interfaces. // However, this is probably an overkill, because the olist of // the kernel MFC should be exactly same, hence we don't do it. // // // Check the interface toward the directly-connected source // pim_vif = pim_node()->vif_find_by_vif_index(vif_index); if (! ((pim_vif != NULL) && (pim_vif->is_up()))) { XLOG_WARNING("RX WHOLEPKT signal from %s: vif_index = %d " "src = %s dst = %s len = %u: " "no interface directly connected to source", src_module_instance_name.c_str(), vif_index, cstring(src), cstring(dst), XORP_UINT_CAST(rcvlen)); return (XORP_ERROR); } // // Send a PIM Register to the RP using the RPF vif toward it // pim_vif = pim_node()->pim_vif_rpf_find(*rp_addr_ptr); if (! ((pim_vif != NULL) && (pim_vif->is_up()))) { XLOG_WARNING("RX WHOLEPKT signal from %s: vif_index = %d " "src = %s dst = %s len = %u: " "no RPF interface toward the RP (%s)", src_module_instance_name.c_str(), vif_index, cstring(src), cstring(dst), XORP_UINT_CAST(rcvlen), cstring(*rp_addr_ptr)); return (XORP_ERROR); } pim_vif->pim_register_send(*rp_addr_ptr, src, dst, rcvbuf, rcvlen, dummy_error_msg); return (XORP_OK); } // // XXX: Implement the following processing as described in the spec. // "On receipt of data from S to G on interface iif:" // void PimMrt::receive_data(uint32_t iif_vif_index, const IPvX& src, const IPvX& dst) { PimVif *pim_vif; PimMre *pim_mre; PimMre *pim_mre_sg = NULL; PimMre *pim_mre_wc = NULL; PimMfc *pim_mfc = NULL; Mifset olist; uint32_t lookup_flags; bool is_sptbit_set = false; bool is_directly_connected_s = false; bool is_keepalive_timer_restarted = false; bool is_wrong_iif = true; bool is_assert_sent = false; uint32_t directly_connected_rpf_interface_s = Vif::VIF_INDEX_INVALID; if (iif_vif_index == Vif::VIF_INDEX_INVALID) { return; // Invalid vif } pim_vif = vif_find_by_vif_index(iif_vif_index); if ((pim_vif == NULL) || (! pim_vif->is_up())) { return; // No such vif, or the vif is not in operation } // // Lookup the PimMrt for the longest-match // lookup_flags = PIM_MRE_RP | PIM_MRE_WC | PIM_MRE_SG | PIM_MRE_SG_RPT; pim_mre = pim_mre_find(src, dst, lookup_flags, 0); // Test if source is directly connected do { if (pim_mre != NULL) { if (pim_mre->is_sg() || pim_mre->is_sg_rpt()) { if (pim_mre->is_directly_connected_s()) { is_directly_connected_s = true; directly_connected_rpf_interface_s = pim_mre->rpf_interface_s(); break; } } } if (pim_node()->is_directly_connected(*pim_vif, src)) { is_directly_connected_s = true; directly_connected_rpf_interface_s = pim_vif->vif_index(); break; } break; } while (false); // // Get the (*,G) entry (if exists). // if (pim_mre != NULL) { if (pim_mre->is_wc()) { pim_mre_wc = pim_mre; } else { pim_mre_wc = pim_mre->wc_entry(); } } // // Get the (S,G) entry (if exits). // do { if (pim_mre == NULL) break; if (pim_mre->is_sg()) { pim_mre_sg = pim_mre; break; } if (pim_mre->is_sg_rpt()) { pim_mre_sg = pim_mre->sg_entry(); break; } break; } while (false); // // Take action if directly-connected source and the iif matches // if (is_directly_connected_s && (iif_vif_index == directly_connected_rpf_interface_s)) { // Create a (S,G) entry if necessary if (pim_mre_sg == NULL) { pim_mre = pim_mre_find(src, dst, PIM_MRE_SG, PIM_MRE_SG); pim_mre_sg = pim_mre; } // // Take action if directly-connected source // // From the spec: // set KeepaliveTimer(S,G) to Keepalive_Period // # Note: a register state transition or UpstreamJPState(S,G) // # transition may happen as a result of restarting // # KeepaliveTimer, and must be dealt with here. // // Note that starting the KeepaliveTimer(S,G) will automatically // schedule a task that should take care of the register state and // UpstreamJPState(S,G) state transitions. // pim_mre_sg->start_keepalive_timer(); is_keepalive_timer_restarted = true; // TODO: XXX: PAVPAVPAV: OK TO COMMENT? pim_mre_sg->recompute_is_could_register_sg(); pim_mre_sg->recompute_is_join_desired_sg(); } if (pim_mre_sg != NULL) { if ((iif_vif_index == pim_mre_sg->rpf_interface_s()) && pim_mre_sg->is_joined_state() && pim_mre_sg->inherited_olist_sg().any()) { // set KeepaliveTimer(S,G) to Keepalive_Period pim_mre_sg->start_keepalive_timer(); is_keepalive_timer_restarted = true; } } if (pim_mre == NULL) { // XXX: install a MFC in the kernel with NULL oifs pim_mfc = pim_mfc_find(src, dst, true); XLOG_ASSERT(pim_mfc != NULL); pim_mfc->update_mfc(iif_vif_index, pim_mfc->olist(), pim_mre_sg); if (! pim_mfc->has_idle_dataflow_monitor()) { // Add a dataflow monitor to expire idle (S,G) MFC state // XXX: strictly speaking, the period doesn't have // to be PIM_KEEPALIVE_PERIOD_DEFAULT, because it has nothing // to do with PIM, but for simplicity we use it here. pim_mfc->add_dataflow_monitor(PIM_KEEPALIVE_PERIOD_DEFAULT, 0, 0, // threshold_packets 0, // threshold_bytes true, // is_threshold_in_packets false,// is_threshold_in_bytes false,// is_geq_upcall ">=" true);// is_leq_upcall "<=" } return; } // // Update the SPT-bit // if (pim_mre_sg != NULL) { // Update_SPTbit(S,G,iif) pim_mre_sg->update_sptbit_sg(iif_vif_index); is_sptbit_set = pim_mre_sg->is_spt(); } // // Process the "Data arrived" event that may trigger an Assert message. // if (pim_mre_sg != NULL) { pim_mre_sg->data_arrived_could_assert(pim_vif, src, dst, is_assert_sent); } else { pim_mre->data_arrived_could_assert(pim_vif, src, dst, is_assert_sent); } // // Perform the rest of the processing // if ((pim_mre_sg != NULL) && (iif_vif_index == pim_mre_sg->rpf_interface_s()) && is_sptbit_set) { is_wrong_iif = false; olist = pim_mre_sg->inherited_olist_sg(); } else if ((iif_vif_index == pim_mre->rpf_interface_rp()) && (is_sptbit_set == false)) { is_wrong_iif = false; olist = pim_mre->inherited_olist_sg_rpt(); if (pim_mre->check_switch_to_spt_sg(src, dst, pim_mre_sg, 0, 0)) { XLOG_ASSERT(pim_mre_sg != NULL); is_keepalive_timer_restarted = true; } } else { // # Note: RPF check failed // # A transition in an Assert FSM, may cause an Assert(S,G) // # or Assert(*,G) message to be sent out interface iif. if ((is_sptbit_set == true) && pim_mre->inherited_olist_sg().test(iif_vif_index)) { // send Assert(S,G) on iif_vif_index XLOG_ASSERT(pim_mre_sg != NULL); pim_mre_sg->wrong_iif_data_arrived_sg(pim_vif, src, is_assert_sent); } else if ((is_sptbit_set == false) && (pim_mre->inherited_olist_sg_rpt().test( iif_vif_index))) { // send Assert(*,G) on iif_vif_index bool is_new_entry = false; if (pim_mre_wc == NULL) { pim_mre_wc = pim_mre_find(src, dst, PIM_MRE_WC, PIM_MRE_WC); is_new_entry = true; } XLOG_ASSERT(pim_mre_wc != NULL); pim_mre_wc->wrong_iif_data_arrived_wc(pim_vif, src, is_assert_sent); if (is_new_entry) pim_mre_wc->entry_try_remove(); } } olist.reset(iif_vif_index); // Lookup/create a PimMfc entry pim_mfc = pim_mfc_find(src, dst, true); XLOG_ASSERT(pim_mfc != NULL); if ((! is_wrong_iif) || (pim_mfc->iif_vif_index() == Vif::VIF_INDEX_INVALID)) { pim_mfc->update_mfc(iif_vif_index, olist, pim_mre_sg); } if (is_keepalive_timer_restarted || (! pim_mfc->has_idle_dataflow_monitor())) { // // Add a dataflow monitor to expire idle (S,G) PimMre state // and/or idle PimMfc+MFC state // // First, compute the dataflow monitor value, which may be different // for the (S,G) entry in the RP if a Register-Stop was sent. uint32_t expected_dataflow_monitor_sec = PIM_KEEPALIVE_PERIOD_DEFAULT; if (is_keepalive_timer_restarted && (pim_mre_sg != NULL) && (pim_mre_sg->is_kat_set_to_rp_keepalive_period())) { if (expected_dataflow_monitor_sec < PIM_RP_KEEPALIVE_PERIOD_DEFAULT) { expected_dataflow_monitor_sec = PIM_RP_KEEPALIVE_PERIOD_DEFAULT; } } pim_mfc->add_dataflow_monitor(expected_dataflow_monitor_sec, 0, 0, // threshold_packets 0, // threshold_bytes true, // is_threshold_in_packets false, // is_threshold_in_bytes false, // is_geq_upcall ">=" true); // is_leq_upcall "<=" } // // If necessary, add a dataflow monitor to monitor whether it is // time to switch to the SPT. // if (pim_node()->is_switch_to_spt_enabled().get() && (pim_mre_wc != NULL) && (pim_mre_wc->is_monitoring_switch_to_spt_desired_sg(pim_mre_sg)) && (! pim_mfc->has_spt_switch_dataflow_monitor())) { uint32_t sec = pim_node()->switch_to_spt_threshold_interval_sec().get(); uint32_t bytes = pim_node()->switch_to_spt_threshold_bytes().get(); pim_mfc->add_dataflow_monitor(sec, 0, 0, // threshold_packets bytes, // threshold_bytes false, // is_threshold_in_packets true, // is_threshold_in_bytes true, // is_geq_upcall ">=" false); // is_leq_upcall "<=" } } int PimMrt::signal_dataflow_recv(const IPvX& source_addr, const IPvX& group_addr, uint32_t threshold_interval_sec, uint32_t threshold_interval_usec, uint32_t measured_interval_sec, uint32_t measured_interval_usec, 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) { PimMre *pim_mre; PimMre *pim_mre_sg; PimMfc *pim_mfc; uint32_t lookup_flags = PIM_MRE_RP | PIM_MRE_WC | PIM_MRE_SG | PIM_MRE_SG_RPT; uint32_t expected_dataflow_monitor_sec = PIM_KEEPALIVE_PERIOD_DEFAULT; // TODO: Maybe code should really be using this?? UNUSED(measured_interval_usec); // in case XLOG_TRACE is compiled out. XLOG_TRACE(pim_node()->is_log_trace(), "RX DATAFLOW signal: " "source = %s group = %s " "threshold_interval_sec = %u threshold_interval_usec = %u " "measured_interval_sec = %u measured_interval_usec = %u " "threshold_packets = %u threshold_bytes = %u " "measured_packets = %u measured_bytes = %u " "is_threshold_in_packets = %u is_threshold_in_bytes = %u " "is_geq_upcall = %u is_leq_upcall = %u", cstring(source_addr), cstring(group_addr), XORP_UINT_CAST(threshold_interval_sec), XORP_UINT_CAST(threshold_interval_usec), XORP_UINT_CAST(measured_interval_sec), XORP_UINT_CAST(measured_interval_usec), XORP_UINT_CAST(threshold_packets), XORP_UINT_CAST(threshold_bytes), XORP_UINT_CAST(measured_packets), XORP_UINT_CAST(measured_bytes), XORP_UINT_CAST(is_threshold_in_packets), XORP_UINT_CAST(is_threshold_in_bytes), XORP_UINT_CAST(is_geq_upcall), XORP_UINT_CAST(is_leq_upcall)); pim_mfc = pim_mfc_find(source_addr, group_addr, false); if (pim_mfc == NULL) { pim_node()->delete_all_dataflow_monitor(source_addr, group_addr); return (XORP_ERROR); // No such PimMfc entry } pim_mre = pim_mre_find(source_addr, group_addr, lookup_flags, 0); // // Get the (S,G) entry // pim_mre_sg = NULL; do { if (pim_mre == NULL) break; if (pim_mre->is_sg()) { pim_mre_sg = pim_mre; break; } if (pim_mre->is_sg_rpt()) { pim_mre_sg = pim_mre->sg_entry(); break; } break; } while (false); // TODO: pim_mre_wc is un-used...need to see if it should be used, // and if not, just delete this code. --Ben #if 0 // // Get the (*,G) entry // PimMre *pim_mre_wc = NULL; if (pim_mre != NULL) { if (pim_mre->is_wc()) pim_mre_wc = pim_mre; else pim_mre_wc = pim_mre->wc_entry(); } #endif if (is_geq_upcall) goto is_geq_upcall_label; else goto is_leq_upcall_label; is_geq_upcall_label: // // Received >= upcall // // // Check whether we really need SPT-switch dataflow monitor, // and whether this is the right dataflow monitor. // if (! (((pim_mre != NULL) && (pim_mre->is_monitoring_switch_to_spt_desired_sg(pim_mre_sg))) && (! ((pim_mre_sg != NULL) && (pim_mre_sg->is_keepalive_timer_running()))) && pim_node()->is_switch_to_spt_enabled().get() && (is_threshold_in_bytes && (pim_node()->switch_to_spt_threshold_interval_sec().get() == threshold_interval_sec) && (pim_node()->switch_to_spt_threshold_bytes().get() == threshold_bytes)))) { // This dataflow monitor is not needed, hence delete it. if (pim_mfc->has_spt_switch_dataflow_monitor()) { pim_mfc->delete_dataflow_monitor(threshold_interval_sec, threshold_interval_usec, threshold_packets, threshold_bytes, is_threshold_in_packets, is_threshold_in_bytes, is_geq_upcall, is_leq_upcall); } return (XORP_ERROR); // We don't need this dataflow monitor } if ((pim_mre != NULL) && pim_mre->check_switch_to_spt_sg(source_addr, group_addr, pim_mre_sg, measured_interval_sec, measured_bytes)) { // // SPT switch is desired, and is initiated by check_switch_to_spt_sg(). // Remove the dataflow monitor, because we don't need it anymore. // if (pim_mfc->has_spt_switch_dataflow_monitor()) { pim_mfc->delete_dataflow_monitor(threshold_interval_sec, threshold_interval_usec, threshold_packets, threshold_bytes, is_threshold_in_packets, is_threshold_in_bytes, is_geq_upcall, is_leq_upcall); } return (XORP_OK); } return (XORP_OK); is_leq_upcall_label: // // Received <= upcall // // // Compute what is the expected value for the Keepalive Timer // if ((pim_mre_sg != NULL) && pim_mre_sg->is_kat_set_to_rp_keepalive_period()) { if (expected_dataflow_monitor_sec < PIM_RP_KEEPALIVE_PERIOD_DEFAULT) expected_dataflow_monitor_sec = PIM_RP_KEEPALIVE_PERIOD_DEFAULT; } // // Test if time to remove a MFC entry and/or if the Keepalive Timer // has expired. // if ((measured_packets == 0) && (threshold_interval_sec >= expected_dataflow_monitor_sec)) { // Idle source: delete the MFC entry delete pim_mfc; pim_mfc = NULL; if (pim_mre_sg != NULL) { // Idle source: the Keepalive(S,G) timer has expired pim_mre_sg->keepalive_timer_timeout(); return (XORP_OK); } } // // Restart the Keepalive Timer if it has expired prematurely // if ((measured_packets == 0) && (threshold_interval_sec < expected_dataflow_monitor_sec)) { XLOG_ASSERT(pim_mfc != NULL); if (pim_mre_sg != NULL) { // First delete the old dataflow if (pim_mfc->has_idle_dataflow_monitor()) { pim_mfc->delete_dataflow_monitor(threshold_interval_sec, threshold_interval_usec, threshold_packets, threshold_bytes, is_threshold_in_packets, is_threshold_in_bytes, is_geq_upcall, is_leq_upcall); } pim_mfc->add_dataflow_monitor(expected_dataflow_monitor_sec, 0, 0, // threshold_packets 0, // threshold_bytes true, // is_threshold_in_packets false,// is_threshold_in_bytes false,// is_geq_upcall ">=" true);// is_leq_upcall "<=" } } if (pim_mre == NULL) { // No such PimMre entry. Silently delete the PimMfc entry. delete pim_mfc; return (XORP_ERROR); // No such PimMre entry } return (XORP_OK); } xorp/pim/pim_bsr.cc0000664000076400007640000024351411540224231014412 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // PIM Bootstrap Router (BSR) mechanism implementation. // PIM-SMv2 (draft-ietf-pim-sm-bsr-*) // #include #include "pim_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/random.h" #include "libxorp/ipvx.hh" #include "libxorp/utils.hh" #include "pim_proto.h" #include "pim_bsr.hh" #include "pim_node.hh" #include "pim_vif.hh" // // Exported variables // // // Local constants definitions // // // Local structures/classes, typedefs and macros // // // Local variables // // // Local functions prototypes // /** * PimBsr::PimBsr: * @pim_node: The associated PIM node. * * PimBsr constructor. **/ PimBsr::PimBsr(PimNode& pim_node) : ProtoUnit(pim_node.family(), pim_node.module_id()), _pim_node(pim_node) { } /** * PimBsr::~PimBsr: * @void: * * PimBsr destructor. * **/ PimBsr::~PimBsr() { clear(); } /** * Clear the entry. */ void PimBsr::clear() { stop(); // Free the lists delete_pointers_list(_config_bsr_zone_list); delete_pointers_list(_active_bsr_zone_list); delete_pointers_list(_expire_bsr_zone_list); delete_pointers_list(_test_bsr_zone_list); } /** * PimBsr::start: * @: * * Start the PIM Bootsrap protocol. * * Return value: %XORP_OK on success, otherwize %XORP_ERROR. **/ int PimBsr::start() { if (! is_enabled()) return (XORP_OK); if (is_up() || is_pending_up()) return (XORP_OK); if (ProtoUnit::start() != XORP_OK) return (XORP_ERROR); // // Activate all configured BSR zones // list::iterator iter; for (iter = _config_bsr_zone_list.begin(); iter != _config_bsr_zone_list.end(); ++iter) { BsrZone *config_bsr_zone = *iter; if (config_bsr_zone->i_am_candidate_bsr()) { string error_msg = ""; if (add_active_bsr_zone(*config_bsr_zone, error_msg) == NULL) { XLOG_ERROR("Cannot add configured Bootstrap zone %s: %s", cstring(config_bsr_zone->zone_id()), error_msg.c_str()); stop(); return (XORP_ERROR); } } config_bsr_zone->start_candidate_rp_advertise_timer(); } XLOG_INFO("Bootstrap mechanism started"); return (XORP_OK); } /** * PimBsr::stop: * @: * * Stop the PIM Bootstrap protocol. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int PimBsr::stop() { PimVif* pim_vif; string dummy_error_msg; if (is_down()) return (XORP_OK); if (ProtoUnit::stop() != XORP_OK) return (XORP_ERROR); // // Perform misc. PIM-specific stop operations // // // Send Bootstrap message with lowest priority // and Cand-RP-Adv message with holdtime of zero // list::iterator iter; for (iter = _config_bsr_zone_list.begin(); iter != _config_bsr_zone_list.end(); ++iter) { BsrZone *config_bsr_zone = *iter; BsrZone *active_bsr_zone; active_bsr_zone = find_active_bsr_zone(config_bsr_zone->zone_id()); if (active_bsr_zone == NULL) continue; // No active BsrZone yet if (! active_bsr_zone->bsr_addr().is_unicast()) continue; // We don't know the BSR address // // Cancel the Cand-RP-Advertise timer, // and send Cand-RP-Adv messages with holdtime of zero. // do { // // Cancel the Cand-RP-Advertise timer // if (! config_bsr_zone->candidate_rp_advertise_timer().scheduled()) break; // We were not sending Cand-RP-Adv messages config_bsr_zone->candidate_rp_advertise_timer().unschedule(); if (active_bsr_zone->i_am_bsr()) break; // I am the BSR, hence don't send the messages if (config_bsr_zone->bsr_group_prefix_list().empty()) break; // No Cand-RP-Adv to cancel // Test if there is a BSR if (! ((active_bsr_zone->bsr_zone_state() == BsrZone::STATE_CANDIDATE_BSR) || (active_bsr_zone->bsr_zone_state() == BsrZone::STATE_ACCEPT_PREFERRED))) { break; } // // Find the RPF vif toward the BSR // pim_vif = pim_node().pim_vif_rpf_find(active_bsr_zone->bsr_addr()); if ((pim_vif == NULL) || (! pim_vif->is_up())) { XLOG_ERROR("Cannot send Cand-RP Adv message to %s: " "cannot find the RPF vif", cstring(active_bsr_zone->bsr_addr())); break; } // // Send Cand-RP-Adv messages with holdtime of zero // config_bsr_zone->set_is_cancel(true); pim_vif->pim_cand_rp_adv_send(active_bsr_zone->bsr_addr(), *config_bsr_zone); config_bsr_zone->set_is_cancel(false); } while (false); // // Send Bootstrap message with lowest priority // do { if (! config_bsr_zone->i_am_candidate_bsr()) break; // // TODO: XXX: Sending BSM-cancel when in STATE_PENDING_BSR is not // in the spec (04/27/2002). // if (! ((active_bsr_zone->bsr_zone_state() == BsrZone::STATE_ELECTED_BSR) || (active_bsr_zone->bsr_zone_state() == BsrZone::STATE_PENDING_BSR))) { break; } active_bsr_zone->new_fragment_tag(); active_bsr_zone->set_is_cancel(true); for (uint32_t i = 0; i < pim_node().maxvifs(); i++) { pim_vif = pim_node().vif_find_by_vif_index(i); if (pim_vif == NULL) continue; pim_vif->pim_bootstrap_send(IPvX::PIM_ROUTERS(pim_vif->family()), *active_bsr_zone, dummy_error_msg); } active_bsr_zone->set_is_cancel(false); } while (false); } // Remove the lists of active and expiring BsrZone entries delete_pointers_list(_active_bsr_zone_list); delete_pointers_list(_expire_bsr_zone_list); // Cancel unwanted timers _clean_expire_bsr_zones_timer.unschedule(); // // XXX: don't stop the _rp_table_apply_rp_changes_timer // because it is a one-time only timers that is used to // synchronize internal state. // XLOG_INFO("Bootstrap mechanism stopped"); return (XORP_OK); } /** * Enable the node operation. * * If an unit is not enabled, it cannot be start, or pending-start. */ void PimBsr::enable() { ProtoUnit::enable(); XLOG_INFO("Bootstrap mechanism 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 PimBsr::disable() { stop(); ProtoUnit::disable(); XLOG_INFO("Bootstrap mechanism disabled"); } int PimBsr::apply_bsr_changes(string& error_msg) { list::iterator iter; list del_list; if (! is_enabled()) return (XORP_OK); // // Preserve any elected BSR zones // for (iter = _active_bsr_zone_list.begin(); iter != _active_bsr_zone_list.end(); iter++) { BsrZone* tmp_zone = *iter; // If the BSR zone is not elected BSR, just remove it if (tmp_zone->bsr_zone_state() != BsrZone::STATE_ELECTED_BSR) { del_list.push_back(tmp_zone); } else { // // It is an Elected BSR. We will keep the BSR zone in the list // but remove all Cand RPs. Some of them may have been deleted. // The Cand RPs that were not deleted will be added again below. // delete_pointers_list(tmp_zone->bsr_group_prefix_list()); } } for (iter = del_list.begin(); iter != del_list.end(); ++iter) { _active_bsr_zone_list.remove(*iter); } delete_pointers_list(del_list); // // Clean up any deleted zone // for (iter = _active_bsr_zone_list.begin(); iter != _active_bsr_zone_list.end(); ++iter) { BsrZone *active_bsr_zone = *iter; if (active_bsr_zone->bsr_zone_state() == BsrZone::STATE_ELECTED_BSR) { BsrZone *config_bsr_zone; config_bsr_zone = find_config_bsr_zone(active_bsr_zone->zone_id()); if (config_bsr_zone == NULL) { // Zone removed from config del_list.push_back(active_bsr_zone); continue; } // // Zone is no longer BSR candidate (i.e., removed from Cand-BSR but // still has some Cand-RP state). // // Remove it too, as the Cand-RP state is re-added below if (! config_bsr_zone->i_am_candidate_bsr()) { del_list.push_back(active_bsr_zone); continue; } } } // // Delete the vanished items // for (iter = del_list.begin(); iter != del_list.end(); ++iter) { BsrZone *active_bsr_zone = *iter; _active_bsr_zone_list.remove(active_bsr_zone); } delete_pointers_list(del_list); // // Activate all configured BSR zones // for (iter = _config_bsr_zone_list.begin(); iter != _config_bsr_zone_list.end(); ++iter) { BsrZone *config_bsr_zone = *iter; if (config_bsr_zone->i_am_candidate_bsr()) { if (add_active_bsr_zone(*config_bsr_zone, error_msg) == NULL) { XLOG_ERROR("Cannot add configured Bootstrap zone %s: %s", cstring(config_bsr_zone->zone_id()), error_msg.c_str()); stop(); return (XORP_ERROR); } } config_bsr_zone->start_candidate_rp_advertise_timer(); } // // When restarting we want to keep any previously enabled BSR. // Set it to PENDING and expire the bsr_timer immediately. // As soon as the timer runs, it puts the zone back in the ELECTED // state and run the needed actions (calculate RP-Set and send a // BSR Message with the new RP-Set). // for (iter = _active_bsr_zone_list.begin(); iter != _active_bsr_zone_list.end(); ++iter) { BsrZone *active_bsr_zone = *iter; if (active_bsr_zone->bsr_zone_state() == BsrZone::STATE_ELECTED_BSR) { if (active_bsr_zone->i_am_candidate_bsr()) { // zone was not changed active_bsr_zone->set_bsr_zone_state(BsrZone::STATE_PENDING_BSR); } else { // zone is no longer Cand-BSR, but contains Cand-RPs active_bsr_zone->set_bsr_zone_state(BsrZone::STATE_ACCEPT_ANY); } active_bsr_zone->expire_bsr_timer(); } } return (XORP_OK); } // Unicast the Bootstrap message(s) to a (new) neighbor int PimBsr::unicast_pim_bootstrap(PimVif *pim_vif, const IPvX& nbr_addr) const { list::const_iterator iter_zone; string dummy_error_msg; if (pim_vif->pim_nbr_find(nbr_addr) == NULL) return (XORP_ERROR); // // Unicast the messages with the remaining expiring BSR zones // Note that those must be sent BEFORE the active BSR zones, // so if somehow there is overlapping, the active BSR zones will // override the expiring BSR zones. // for (iter_zone = _expire_bsr_zone_list.begin(); iter_zone != _expire_bsr_zone_list.end(); ++iter_zone) { BsrZone *bsr_zone = *iter_zone; pim_vif->pim_bootstrap_send(nbr_addr, *bsr_zone, dummy_error_msg); } // // Unicast the messages with the active BSR zones // for (iter_zone = _active_bsr_zone_list.begin(); iter_zone != _active_bsr_zone_list.end(); ++iter_zone) { BsrZone *bsr_zone = *iter_zone; BsrZone::bsr_zone_state_t bsr_zone_state = bsr_zone->bsr_zone_state(); if ((bsr_zone_state == BsrZone::STATE_CANDIDATE_BSR) || (bsr_zone_state == BsrZone::STATE_ELECTED_BSR) || (bsr_zone_state == BsrZone::STATE_ACCEPT_PREFERRED)) { pim_vif->pim_bootstrap_send(nbr_addr, *bsr_zone, dummy_error_msg); } } return (XORP_OK); } // // Return: a pointer to the BSR zone if it exists and there is no // inconsistency, or if it can and was added, otherwise NULL. // XXX: if this is the global, non-scoped zone, then // the zone ID will be set to IPvXNet::ip_multicast_base_prefix() // BsrZone * PimBsr::add_config_bsr_zone(const BsrZone& bsr_zone, string& error_msg) { BsrZone* old_bsr_zone = NULL; if (! can_add_config_bsr_zone(bsr_zone, error_msg)) return (NULL); // // Add or update the BSR zone // old_bsr_zone = find_config_bsr_zone(bsr_zone.zone_id()); if (old_bsr_zone == NULL) { // Add a new BSR zone BsrZone *new_bsr_zone = new BsrZone(*this, bsr_zone); new_bsr_zone->set_config_bsr_zone(true); _config_bsr_zone_list.push_back(new_bsr_zone); return (new_bsr_zone); } // // Update the existing BSR zone // if (old_bsr_zone->update_config_bsr_zone(bsr_zone, error_msg) != XORP_OK) return (NULL); return (old_bsr_zone); } // Return: a pointer to the BSR zone if it exists and there is no // inconsistency, or if it can and was added, otherwise NULL. // XXX: if this is the global, non-scoped zone, then // the zone ID will be set to IPvXNet::ip_multicast_base_prefix() BsrZone * PimBsr::add_active_bsr_zone(const BsrZone& bsr_zone, string& error_msg) { BsrZone *active_bsr_zone = NULL; if (! can_add_active_bsr_zone(bsr_zone, error_msg)) return (NULL); active_bsr_zone = find_active_bsr_zone(bsr_zone.zone_id()); if (active_bsr_zone == NULL) { active_bsr_zone = new BsrZone(*this, bsr_zone.zone_id()); active_bsr_zone->set_active_bsr_zone(true); _active_bsr_zone_list.push_back(active_bsr_zone); } active_bsr_zone->process_candidate_bsr(bsr_zone); if (active_bsr_zone->bsr_addr() != bsr_zone.bsr_addr()) { // Received message is not from the preferred BSR. return (active_bsr_zone); } // // Start (or restart) the Cand-RR Expiry Timer in 'active_bsr_zone' // for all RPs that were in 'bsr_zone'. // list::const_iterator iter_prefix; for (iter_prefix = bsr_zone.bsr_group_prefix_list().begin(); iter_prefix != bsr_zone.bsr_group_prefix_list().end(); ++iter_prefix) { BsrGroupPrefix *bsr_group_prefix = *iter_prefix; BsrGroupPrefix *active_bsr_group_prefix = active_bsr_zone->find_bsr_group_prefix(bsr_group_prefix->group_prefix()); if (active_bsr_group_prefix == NULL) continue; list::const_iterator iter_rp; for (iter_rp = bsr_group_prefix->rp_list().begin(); iter_rp != bsr_group_prefix->rp_list().end(); ++iter_rp) { BsrRp *bsr_rp = *iter_rp; BsrRp *active_bsr_rp = active_bsr_group_prefix->find_rp(bsr_rp->rp_addr()); if (active_bsr_rp == NULL) continue; if (active_bsr_zone->i_am_bsr()) { // // XXX: If I am the BSR, don't start the timer for my // own Cand-RPs continue; } active_bsr_rp->start_candidate_rp_expiry_timer(); } } return (active_bsr_zone); } // Return: a pointer to the new BSR zone BsrZone * PimBsr::add_expire_bsr_zone(const BsrZone& bsr_zone) { list::const_iterator iter_prefix; // // Create an expire BSR zone that is a copy of the original zone // BsrZone *expire_bsr_zone = new BsrZone(*this, bsr_zone); expire_bsr_zone->set_expire_bsr_zone(true); // // Cancel all timers for this zone. // Note that we do keep the C-RP Expiry timers running // expire_bsr_zone->bsr_timer().unschedule(); expire_bsr_zone->scope_zone_expiry_timer().unschedule(); expire_bsr_zone->candidate_rp_advertise_timer().unschedule(); // // Don't keep prefixes that have no RPs, or incomplete set of RPs. // for (iter_prefix = expire_bsr_zone->bsr_group_prefix_list().begin(); iter_prefix != expire_bsr_zone->bsr_group_prefix_list().end(); ) { BsrGroupPrefix *bsr_group_prefix = *iter_prefix; ++iter_prefix; if ((bsr_group_prefix->rp_list().empty()) || (bsr_group_prefix->received_rp_count() < bsr_group_prefix->expected_rp_count())) { expire_bsr_zone->delete_bsr_group_prefix(bsr_group_prefix); continue; } // // Remove the old RPs for same prefix // delete_expire_bsr_zone_prefix(bsr_group_prefix->group_prefix(), bsr_group_prefix->is_scope_zone()); } if (expire_bsr_zone->bsr_group_prefix_list().empty()) { // No prefixes, hence don't add delete expire_bsr_zone; return (NULL); } // Add the zone to the list of expiring zones _expire_bsr_zone_list.push_back(expire_bsr_zone); return (expire_bsr_zone); } void PimBsr::delete_config_bsr_zone(BsrZone *old_bsr_zone) { // Remove from the list of configured zones _config_bsr_zone_list.remove(old_bsr_zone); delete old_bsr_zone; } void PimBsr::delete_active_bsr_zone(BsrZone *old_bsr_zone) { list::iterator iter; // Try to remove from the list of active zones iter = find(_active_bsr_zone_list.begin(), _active_bsr_zone_list.end(), old_bsr_zone); if (iter != _active_bsr_zone_list.end()) { _active_bsr_zone_list.erase(iter); delete_all_expire_bsr_zone_by_zone_id(old_bsr_zone->zone_id()); delete old_bsr_zone; return; } } void PimBsr::delete_expire_bsr_zone(BsrZone *old_bsr_zone) { list::iterator iter; // Try to remove from the list of expiring zones iter = find(_expire_bsr_zone_list.begin(), _expire_bsr_zone_list.end(), old_bsr_zone); if (iter != _expire_bsr_zone_list.end()) { _expire_bsr_zone_list.erase(iter); delete old_bsr_zone; return; } } void PimBsr::delete_all_expire_bsr_zone_by_zone_id(const PimScopeZoneId& zone_id) { list::iterator iter, old_iter; // Try to remove all expire zones that match the specified zone ID for (iter = _expire_bsr_zone_list.begin(); iter != _expire_bsr_zone_list.end(); ) { BsrZone *bsr_zone = *iter; old_iter = iter; ++iter; if (bsr_zone->zone_id() == zone_id) { _expire_bsr_zone_list.erase(old_iter); delete bsr_zone; } } } // Delete all group prefixes from the expiring zones. void PimBsr::delete_expire_bsr_zone_prefix(const IPvXNet& group_prefix, bool is_scope_zone) { list::iterator iter_zone, old_iter_zone; // Delete the group prefixes for (iter_zone = _expire_bsr_zone_list.begin(); iter_zone != _expire_bsr_zone_list.end(); ) { BsrZone *bsr_zone = *iter_zone; old_iter_zone = iter_zone; ++iter_zone; BsrGroupPrefix *bsr_group_prefix; if (bsr_zone->zone_id().is_scope_zone() != is_scope_zone) continue; bsr_group_prefix = bsr_zone->find_bsr_group_prefix(group_prefix); if (bsr_group_prefix != NULL) { bsr_zone->delete_bsr_group_prefix(bsr_group_prefix); // Delete the expiring BSR zone if no more prefixes if (bsr_zone->bsr_group_prefix_list().empty()) { _expire_bsr_zone_list.erase(old_iter_zone); delete bsr_zone; } } } } // Find the BsrZone that corresponds to @zone_id // from the list of configured BSR zones. BsrZone * PimBsr::find_config_bsr_zone(const PimScopeZoneId& zone_id) const { list::const_iterator iter_zone; for (iter_zone = _config_bsr_zone_list.begin(); iter_zone != _config_bsr_zone_list.end(); ++iter_zone) { BsrZone *bsr_zone = *iter_zone; if (bsr_zone->zone_id() == zone_id) return (bsr_zone); } return (NULL); } // Find the BsrZone that corresponds to @zone_id // from the list of active BSR zones. BsrZone * PimBsr::find_active_bsr_zone(const PimScopeZoneId& zone_id) const { list::const_iterator iter_zone; for (iter_zone = _active_bsr_zone_list.begin(); iter_zone != _active_bsr_zone_list.end(); ++iter_zone) { BsrZone *bsr_zone = *iter_zone; if (bsr_zone->zone_id() == zone_id) return (bsr_zone); } return (NULL); } // Find the BsrZone that corresponds to @group_prefix // and @is_scope_zone from the list of configured BSR zones. BsrZone * PimBsr::find_config_bsr_zone_by_prefix(const IPvXNet& group_prefix, bool is_scope_zone) const { return (find_bsr_zone_by_prefix_from_list(_config_bsr_zone_list, group_prefix, is_scope_zone)); } // Find the BsrZone that corresponds to @group_prefix // and @is_scope_zone from the list of acgive BSR zones. BsrZone * PimBsr::find_active_bsr_zone_by_prefix(const IPvXNet& group_prefix, bool is_scope_zone) const { return (find_bsr_zone_by_prefix_from_list(_active_bsr_zone_list, group_prefix, is_scope_zone)); } // Find the first RP that corresponds for the given group prefix, the // scope-zone flag, and the RP address. // Note that first we search the list of active zones, and then // the list of expiring zones. BsrRp * PimBsr::find_rp(const IPvXNet& group_prefix, bool is_scope_zone, const IPvX& rp_addr) const { BsrZone *bsr_zone; BsrGroupPrefix *bsr_group_prefix; BsrRp *bsr_rp; // Try the list of active zones bsr_zone = find_active_bsr_zone_by_prefix(group_prefix, is_scope_zone); if (bsr_zone != NULL) { bsr_group_prefix = bsr_zone->find_bsr_group_prefix(group_prefix); if (bsr_group_prefix != NULL) { bsr_rp = bsr_group_prefix->find_rp(rp_addr); if (bsr_rp != NULL) return (bsr_rp); } } // Try any of the zones on the list of expiring zones. list::const_iterator iter; for (iter = _expire_bsr_zone_list.begin(); iter != _expire_bsr_zone_list.end(); ++iter) { bsr_zone = *iter; if (bsr_zone->zone_id().is_scope_zone() != is_scope_zone) continue; bsr_group_prefix = bsr_zone->find_bsr_group_prefix(group_prefix); if (bsr_group_prefix != NULL) { bsr_rp = bsr_group_prefix->find_rp(rp_addr); if (bsr_rp != NULL) return (bsr_rp); } } return (NULL); } void PimBsr::add_rps_to_rp_table() { list::iterator iter_zone; // Add from the list of active BSR zones for (iter_zone = _active_bsr_zone_list.begin(); iter_zone != _active_bsr_zone_list.end(); ++iter_zone) { BsrZone *bsr_zone = *iter_zone; list::const_iterator iter_prefix; for (iter_prefix = bsr_zone->bsr_group_prefix_list().begin(); iter_prefix != bsr_zone->bsr_group_prefix_list().end(); ++iter_prefix) { BsrGroupPrefix *bsr_group_prefix = *iter_prefix; if (bsr_group_prefix->received_rp_count() < bsr_group_prefix->expected_rp_count()) { // Not enough RPs continue; } list::const_iterator iter_rp; for (iter_rp = bsr_group_prefix->rp_list().begin(); iter_rp != bsr_group_prefix->rp_list().end(); ++iter_rp) { BsrRp *bsr_rp = *iter_rp; pim_node().rp_table().add_rp(bsr_rp->rp_addr(), bsr_rp->rp_priority(), bsr_group_prefix->group_prefix(), bsr_zone->hash_mask_len(), PimRp::RP_LEARNED_METHOD_BOOTSTRAP); } } } // Add from the list of expiring BSR zones for (iter_zone = _expire_bsr_zone_list.begin(); iter_zone != _expire_bsr_zone_list.end(); ++iter_zone) { BsrZone *bsr_zone = *iter_zone; list::const_iterator iter_prefix; for (iter_prefix = bsr_zone->bsr_group_prefix_list().begin(); iter_prefix != bsr_zone->bsr_group_prefix_list().end(); ++iter_prefix) { BsrGroupPrefix *bsr_group_prefix = *iter_prefix; if (bsr_group_prefix->received_rp_count() < bsr_group_prefix->expected_rp_count()) { // Not enough RPs continue; } list::const_iterator iter_rp; for (iter_rp = bsr_group_prefix->rp_list().begin(); iter_rp != bsr_group_prefix->rp_list().end(); ++iter_rp) { BsrRp *bsr_rp = *iter_rp; pim_node().rp_table().add_rp(bsr_rp->rp_addr(), bsr_rp->rp_priority(), bsr_group_prefix->group_prefix(), bsr_zone->hash_mask_len(), PimRp::RP_LEARNED_METHOD_BOOTSTRAP); } } } // Apply the changes of the RPs to the PIM multicast routing table. pim_node().rp_table().apply_rp_changes(); } void PimBsr::schedule_rp_table_apply_rp_changes() { _rp_table_apply_rp_changes_timer = pim_node().eventloop().new_oneoff_after( TimeVal(0, 0), callback(this, &PimBsr::rp_table_apply_rp_changes_timer_timeout)); } // Time to apply the changes to the RP Table void PimBsr::rp_table_apply_rp_changes_timer_timeout() { // Apply the changes pim_node().rp_table().apply_rp_changes(); } // // Delete all group prefixes that have no RPs. // If an expiring zone has no group prefixes, it is deleted as well. // void PimBsr::clean_expire_bsr_zones() { list::iterator iter_zone; list::const_iterator iter_prefix; for (iter_zone = _expire_bsr_zone_list.begin(); iter_zone != _expire_bsr_zone_list.end(); ) { BsrZone *bsr_zone = *iter_zone; ++iter_zone; for (iter_prefix = bsr_zone->bsr_group_prefix_list().begin(); iter_prefix != bsr_zone->bsr_group_prefix_list().end(); ) { BsrGroupPrefix *bsr_group_prefix = *iter_prefix; ++iter_prefix; if (! bsr_group_prefix->rp_list().empty()) continue; bsr_zone->delete_bsr_group_prefix(bsr_group_prefix); } if (bsr_zone->bsr_group_prefix_list().empty()) delete_expire_bsr_zone(bsr_zone); } } void PimBsr::schedule_clean_expire_bsr_zones() { _clean_expire_bsr_zones_timer = pim_node().eventloop().new_oneoff_after( TimeVal(0, 0), callback(this, &PimBsr::clean_expire_bsr_zones_timer_timeout)); } void PimBsr::clean_expire_bsr_zones_timer_timeout() { // Clean the expiring zones clean_expire_bsr_zones(); } void PimBsr::add_vif_addr(uint32_t vif_index, const IPvX& vif_addr) { bool old_is_up = is_up(); bool found = false; list::iterator bsr_zone_iter; if (vif_index == Vif::VIF_INDEX_INVALID) return; // // Update the Cand-BSR info // for (bsr_zone_iter = config_bsr_zone_list().begin(); bsr_zone_iter != config_bsr_zone_list().end(); ++bsr_zone_iter) { BsrZone *bsr_zone = *bsr_zone_iter; if ((bsr_zone->my_vif_index() != vif_index) || (bsr_zone->is_my_bsr_addr_explicit())) { continue; } // XXX: enable as a Cand-BSR, and set the IP address bsr_zone->set_bsr_addr(vif_addr); bsr_zone->set_i_am_candidate_bsr(true, vif_index, vif_addr, bsr_zone->my_bsr_priority()); found = true; } // // Update the Cand-RP info // for (bsr_zone_iter = config_bsr_zone_list().begin(); bsr_zone_iter != config_bsr_zone_list().end(); ++bsr_zone_iter) { BsrZone *bsr_zone = *bsr_zone_iter; list::iterator bsr_group_prefix_iter; for (bsr_group_prefix_iter = bsr_zone->bsr_group_prefix_list().begin(); bsr_group_prefix_iter != bsr_zone->bsr_group_prefix_list().end(); ++bsr_group_prefix_iter) { BsrGroupPrefix *bsr_group_prefix = *bsr_group_prefix_iter; list::iterator bsr_rp_iter; for (bsr_rp_iter = bsr_group_prefix->rp_list().begin(); bsr_rp_iter != bsr_group_prefix->rp_list().end(); ++bsr_rp_iter) { BsrRp *bsr_rp = *bsr_rp_iter; if ((bsr_rp->my_vif_index() != vif_index) || (bsr_rp->is_my_rp_addr_explicit())) { continue; } // XXX: enable as a Cand-RP and set the IP address bsr_rp->set_rp_addr(vif_addr); found = true; } } } // // If the BSR is running, and we found a matching entry, then restart // if (found && old_is_up) { stop(); start(); } } void PimBsr::delete_vif_addr(uint32_t vif_index, const IPvX& vif_addr) { bool old_is_up = is_up(); bool found = false; list::iterator bsr_zone_iter; if (vif_index == Vif::VIF_INDEX_INVALID) return; // // Update the Cand-BSR info // for (bsr_zone_iter = config_bsr_zone_list().begin(); bsr_zone_iter != config_bsr_zone_list().end(); ++bsr_zone_iter) { BsrZone *bsr_zone = *bsr_zone_iter; if ((bsr_zone->my_vif_index() != vif_index) || (bsr_zone->my_bsr_addr() != vif_addr) || (bsr_zone->is_my_bsr_addr_explicit())) { continue; } // XXX: disable as a Cand-BSR, and reset the IP address bsr_zone->set_i_am_candidate_bsr(false, vif_index, IPvX::ZERO(family()), bsr_zone->my_bsr_priority()); found = true; } // // Update the Cand-RP info // for (bsr_zone_iter = config_bsr_zone_list().begin(); bsr_zone_iter != config_bsr_zone_list().end(); ++bsr_zone_iter) { BsrZone *bsr_zone = *bsr_zone_iter; list::iterator bsr_group_prefix_iter; for (bsr_group_prefix_iter = bsr_zone->bsr_group_prefix_list().begin(); bsr_group_prefix_iter != bsr_zone->bsr_group_prefix_list().end(); ++bsr_group_prefix_iter) { BsrGroupPrefix *bsr_group_prefix = *bsr_group_prefix_iter; list::iterator bsr_rp_iter; for (bsr_rp_iter = bsr_group_prefix->rp_list().begin(); bsr_rp_iter != bsr_group_prefix->rp_list().end(); ++bsr_rp_iter) { BsrRp *bsr_rp = *bsr_rp_iter; if ((bsr_rp->my_vif_index() != vif_index) || (bsr_rp->rp_addr() != vif_addr) || (bsr_rp->is_my_rp_addr_explicit())) { continue; } // XXX: disable as a Cand-RP and reset the IP address bsr_rp->set_rp_addr(IPvX::ZERO(family())); found = true; } } } // // If the BSR is running, and we found a matching entry, then restart // if (found && old_is_up) { stop(); start(); } } // Find the BsrZone that corresponds to @is_scope_zone // and @group_prefix from the list of configured BSR zones. BsrZone * PimBsr::find_bsr_zone_by_prefix_from_list( const list& zone_list, const IPvXNet& group_prefix, bool is_scope_zone) const { list::const_iterator iter_zone; BsrZone *best_bsr_zone = NULL; for (iter_zone = zone_list.begin(); iter_zone != zone_list.end(); ++iter_zone) { BsrZone *bsr_zone = *iter_zone; if (bsr_zone->zone_id().is_scope_zone() != is_scope_zone) continue; if (! bsr_zone->zone_id().contains(group_prefix)) continue; if (best_bsr_zone == NULL) { best_bsr_zone = bsr_zone; continue; } // XXX: we don't really need to do longest-prefix match selection, // because by definition the zones are non-overlapping, but just // in case (e.g., if we anyway accept overlapping zones). if (best_bsr_zone->zone_id().scope_zone_prefix().prefix_len() < bsr_zone->zone_id().scope_zone_prefix().prefix_len()) best_bsr_zone = bsr_zone; } return (best_bsr_zone); } // // Return true if @bsr_zone can be added to the list of configured zones, // otherwise false. // If the return value is false, @error_msg contains a message that // describes the error. // bool PimBsr::can_add_config_bsr_zone(const BsrZone& bsr_zone, string& error_msg) const { list::const_iterator iter_zone; error_msg = ""; // Reset the error message if (bsr_zone.i_am_candidate_bsr()) { const PimVif *pim_vif = NULL; if (! bsr_zone.my_bsr_addr().is_unicast()) { error_msg = c_format("BSR address %s is not an unicast address", cstring(bsr_zone.my_bsr_addr())); return (false); } pim_vif = pim_node().vif_find_by_vif_index(bsr_zone.my_vif_index()); if (pim_vif == NULL) { error_msg = c_format("BSR vif index %d is not a valid index", bsr_zone.my_vif_index()); return (false); } if (! pim_vif->is_my_addr(bsr_zone.my_bsr_addr())) { error_msg = c_format("BSR address %s is not my address on vif %s", cstring(bsr_zone.my_bsr_addr()), pim_vif->name().c_str()); return (false); } } // // Check for consistency // for (iter_zone = _config_bsr_zone_list.begin(); iter_zone != _config_bsr_zone_list.end(); ++iter_zone) { BsrZone *config_bsr_zone = *iter_zone; // // Check whether this is an update for an existing zone. // if (bsr_zone.i_am_candidate_bsr()) { if (config_bsr_zone->zone_id() == bsr_zone.zone_id()) { // Same zone so it will be updated continue; } } // // Check that zones do not overlap // if (config_bsr_zone->i_am_candidate_bsr() && bsr_zone.i_am_candidate_bsr()) { if (config_bsr_zone->zone_id() == bsr_zone.zone_id()) { // Same zone so it will be updated continue; } if (config_bsr_zone->zone_id().is_overlap(bsr_zone.zone_id())) { error_msg = c_format("overlapping zones %s and %s", cstring(config_bsr_zone->zone_id()), cstring(bsr_zone.zone_id())); return (false); } } // // So far so good. Continue checking... // } return (true); // The new BsrZone can be added. } // // Return true if @bsr_zone can be added to the list of active zones, // otherwise false. // If the return value is false, @error_msg contains a message that // describes the error. // bool PimBsr::can_add_active_bsr_zone(const BsrZone& bsr_zone, string& error_msg) const { list::const_iterator iter_zone; error_msg = ""; // Reset the error message // // Check for consistency // for (iter_zone = _active_bsr_zone_list.begin(); iter_zone != _active_bsr_zone_list.end(); ++iter_zone) { BsrZone *active_bsr_zone = *iter_zone; // // Check that zones do not overlap // if (bsr_zone.zone_id() != active_bsr_zone->zone_id()) { if (bsr_zone.zone_id().is_overlap(active_bsr_zone->zone_id())) { error_msg = c_format("overlapping zones %s and %s", cstring(active_bsr_zone->zone_id()), cstring(bsr_zone.zone_id())); return (false); } continue; } if (bsr_zone.bsr_addr() != active_bsr_zone->bsr_addr()) { // A message from a different Bootstrap router. // Such message is OK, because it may replace the old one. continue; } // A message from the same Bootstrap router. Check whether // a fragment from the same message, or a new message. if (bsr_zone.fragment_tag() != active_bsr_zone->fragment_tag()) { // A new message. The new message will replace the old message. continue; } // // A fragment from the same message // if (! active_bsr_zone->can_merge_rp_set(bsr_zone, error_msg)) return (false); // // So far so good. Continue checking... // } return (true); // The new BsrZone can be added. } // // Test-related methods // BsrZone * PimBsr::add_test_bsr_zone(const PimScopeZoneId& zone_id, const IPvX& bsr_addr, uint8_t bsr_priority, uint8_t hash_mask_len, uint16_t fragment_tag) { BsrZone *new_bsr_zone = new BsrZone(*this, bsr_addr, bsr_priority, hash_mask_len, fragment_tag); new_bsr_zone->set_zone_id(zone_id); new_bsr_zone->set_test_bsr_zone(true); _test_bsr_zone_list.push_back(new_bsr_zone); return (new_bsr_zone); } BsrZone * PimBsr::find_test_bsr_zone(const PimScopeZoneId& zone_id) const { list::const_iterator iter_zone; for (iter_zone = _test_bsr_zone_list.begin(); iter_zone != _test_bsr_zone_list.end(); ++iter_zone) { BsrZone *bsr_zone = *iter_zone; if (bsr_zone->zone_id() == zone_id) return (bsr_zone); } return (NULL); } BsrGroupPrefix * PimBsr::add_test_bsr_group_prefix(const PimScopeZoneId& zone_id, const IPvXNet& group_prefix, bool is_scope_zone, uint8_t expected_rp_count) { BsrZone *bsr_zone = NULL; BsrGroupPrefix *bsr_group_prefix = NULL; bsr_zone = find_test_bsr_zone(zone_id); if (bsr_zone == NULL) return (NULL); bsr_group_prefix = bsr_zone->add_bsr_group_prefix(group_prefix, is_scope_zone, expected_rp_count); return (bsr_group_prefix); } BsrRp * PimBsr::add_test_bsr_rp(const PimScopeZoneId& zone_id, const IPvXNet& group_prefix, const IPvX& rp_addr, uint8_t rp_priority, uint16_t rp_holdtime) { BsrZone *bsr_zone = NULL; BsrGroupPrefix *bsr_group_prefix = NULL; BsrRp *bsr_rp = NULL; bsr_zone = find_test_bsr_zone(zone_id); if (bsr_zone == NULL) return (NULL); bsr_group_prefix = bsr_zone->find_bsr_group_prefix(group_prefix); if (bsr_group_prefix == NULL) return (NULL); bsr_rp = bsr_group_prefix->add_rp(rp_addr, rp_priority, rp_holdtime); return (bsr_rp); } int PimBsr::send_test_bootstrap(const string& vif_name, string& error_msg) { return (send_test_bootstrap_by_dest(vif_name, IPvX::PIM_ROUTERS(family()), error_msg)); } int PimBsr::send_test_bootstrap_by_dest(const string& vif_name, const IPvX& dest_addr, string& error_msg) { PimVif *pim_vif = pim_node().vif_find_by_name(vif_name); int ret_value = XORP_ERROR; list::iterator iter_zone; if (pim_vif == NULL) { ret_value = XORP_ERROR; goto ret_label; } // // Send the Bootstrap messages // for (iter_zone = _test_bsr_zone_list.begin(); iter_zone != _test_bsr_zone_list.end(); ++iter_zone) { BsrZone *bsr_zone = *iter_zone; if (pim_vif->pim_bootstrap_send(dest_addr, *bsr_zone, error_msg) != XORP_OK) { ret_value = XORP_ERROR; goto ret_label; } } ret_value = XORP_OK; goto ret_label; ret_label: delete_pointers_list(_test_bsr_zone_list); return (ret_value); } int PimBsr::send_test_cand_rp_adv() { PimVif *pim_vif = NULL; int ret_value = XORP_ERROR; list::iterator iter_zone; // // Check that all Cand-RP addresses are mine // for (iter_zone = _test_bsr_zone_list.begin(); iter_zone != _test_bsr_zone_list.end(); ++iter_zone) { BsrZone *bsr_zone = *iter_zone; list::const_iterator iter_prefix; for (iter_prefix = bsr_zone->bsr_group_prefix_list().begin(); iter_prefix != bsr_zone->bsr_group_prefix_list().end(); ++iter_prefix) { BsrGroupPrefix *bsr_group_prefix = *iter_prefix; list::const_iterator iter_rp; for (iter_rp = bsr_group_prefix->rp_list().begin(); iter_rp != bsr_group_prefix->rp_list().end(); ++iter_rp) { BsrRp *bsr_rp = *iter_rp; if (! pim_node().is_my_addr(bsr_rp->rp_addr())) { ret_value = XORP_ERROR; goto ret_label; } } } } // // Send the Cand-RP-Adv messages // for (iter_zone = _test_bsr_zone_list.begin(); iter_zone != _test_bsr_zone_list.end(); ++iter_zone) { BsrZone *bsr_zone = *iter_zone; // // Find the RPF vif toward the BSR // pim_vif = pim_node().pim_vif_rpf_find(bsr_zone->bsr_addr()); if ((pim_vif == NULL) || (! pim_vif->is_up())) { ret_value = XORP_ERROR; goto ret_label; } if (pim_vif->pim_cand_rp_adv_send(bsr_zone->bsr_addr(), *bsr_zone) != XORP_OK) { ret_value = XORP_ERROR; goto ret_label; } } ret_value = XORP_OK; goto ret_label; ret_label: delete_pointers_list(_test_bsr_zone_list); return (ret_value); } BsrZone::BsrZone(PimBsr& pim_bsr, const BsrZone& bsr_zone) : _pim_bsr(pim_bsr), _is_config_bsr_zone(bsr_zone.is_config_bsr_zone()), _is_active_bsr_zone(bsr_zone.is_active_bsr_zone()), _is_expire_bsr_zone(bsr_zone.is_expire_bsr_zone()), _is_test_bsr_zone(bsr_zone.is_test_bsr_zone()), _bsr_addr(bsr_zone.bsr_addr()), _bsr_priority(bsr_zone.bsr_priority()), _hash_mask_len(bsr_zone.hash_mask_len()), _fragment_tag(bsr_zone.fragment_tag()), _is_accepted_message(bsr_zone.is_accepted_message()), _is_unicast_message(bsr_zone.is_unicast_message()), _unicast_message_src(bsr_zone.unicast_message_src()), _zone_id(bsr_zone.zone_id()), _i_am_candidate_bsr(bsr_zone.i_am_candidate_bsr()), _my_vif_index(bsr_zone.my_vif_index()), _my_bsr_addr(bsr_zone.my_bsr_addr()), _my_bsr_priority(bsr_zone.my_bsr_priority()), _is_my_bsr_addr_explicit(bsr_zone.is_my_bsr_addr_explicit()), _is_bsm_forward(bsr_zone.is_bsm_forward()), _is_bsm_originate(bsr_zone.is_bsm_originate()), _is_cancel(bsr_zone.is_cancel()) { set_bsr_zone_state(bsr_zone.bsr_zone_state()); // Conditionally set the Bootstrap timer if (bsr_zone.const_bsr_timer().scheduled()) { TimeVal tv_left; bsr_zone.const_bsr_timer().time_remaining(tv_left); _bsr_timer = _pim_bsr.pim_node().eventloop().new_oneoff_after( tv_left, callback(this, &BsrZone::bsr_timer_timeout)); } // Conditionally set the Scone-Zone Expiry Timer if (bsr_zone.const_scope_zone_expiry_timer().scheduled()) { TimeVal tv_left; bsr_zone.const_scope_zone_expiry_timer().time_remaining(tv_left); _scope_zone_expiry_timer = _pim_bsr.pim_node().eventloop().new_oneoff_after( tv_left, callback(this, &BsrZone::scope_zone_expiry_timer_timeout)); } // // XXX: Do not set (conditionally) the C-RP Advertise timer, because // it is explicitly start at configured BsrZone only. // // // Copy the RP-set // list::const_iterator iter; for (iter = bsr_zone.bsr_group_prefix_list().begin(); iter != bsr_zone.bsr_group_prefix_list().end(); ++iter) { const BsrGroupPrefix *bsr_group_prefix = *iter; BsrGroupPrefix *bsr_group_prefix_copy = new BsrGroupPrefix(*this, *bsr_group_prefix); _bsr_group_prefix_list.push_back(bsr_group_prefix_copy); } } BsrZone::BsrZone(PimBsr& pim_bsr, const PimScopeZoneId& zone_id) : _pim_bsr(pim_bsr), _is_config_bsr_zone(false), _is_active_bsr_zone(false), _is_expire_bsr_zone(false), _is_test_bsr_zone(false), _bsr_addr(IPvX::ZERO(_pim_bsr.family())), _bsr_priority(0), _hash_mask_len(PIM_BOOTSTRAP_HASH_MASK_LEN_DEFAULT(_pim_bsr.family())), _fragment_tag(xorp_random() % 0xffff), _is_accepted_message(false), _is_unicast_message(false), _unicast_message_src(IPvX::ZERO(_pim_bsr.family())), _zone_id(zone_id), _i_am_candidate_bsr(false), // XXX: disable by default _my_vif_index(Vif::VIF_INDEX_INVALID), _my_bsr_addr(IPvX::ZERO(_pim_bsr.family())), _my_bsr_priority(0), // XXX: lowest priority _is_my_bsr_addr_explicit(false), _is_bsm_forward(false), _is_bsm_originate(false), _is_cancel(false) { set_bsr_zone_state(BsrZone::STATE_INIT); } BsrZone::BsrZone(PimBsr& pim_bsr, const IPvX& bsr_addr, uint8_t bsr_priority, uint8_t hash_mask_len, uint16_t fragment_tag) : _pim_bsr(pim_bsr), _is_config_bsr_zone(false), _is_active_bsr_zone(false), _is_expire_bsr_zone(false), _is_test_bsr_zone(false), _bsr_addr(bsr_addr), _bsr_priority(bsr_priority), _hash_mask_len(hash_mask_len), _fragment_tag(fragment_tag), _is_accepted_message(false), _is_unicast_message(false), _unicast_message_src(IPvX::ZERO(_pim_bsr.family())), _zone_id(PimScopeZoneId(IPvXNet::ip_multicast_base_prefix(_pim_bsr.family()), false)), // XXX: no scope zone by default _i_am_candidate_bsr(false), // XXX: disable by default _my_vif_index(Vif::VIF_INDEX_INVALID), _my_bsr_addr(IPvX::ZERO(_pim_bsr.family())), _my_bsr_priority(0), // XXX: lowest priority _is_my_bsr_addr_explicit(false), _is_bsm_forward(false), _is_bsm_originate(false), _is_cancel(false) { set_bsr_zone_state(BsrZone::STATE_INIT); } BsrZone::~BsrZone() { delete_pointers_list(_bsr_group_prefix_list); } void BsrZone::set_config_bsr_zone(bool v) { _is_config_bsr_zone = v; if (v) { _is_active_bsr_zone = false; _is_expire_bsr_zone = false; _is_test_bsr_zone = false; } } void BsrZone::set_active_bsr_zone(bool v) { _is_active_bsr_zone = v; if (v) { _is_config_bsr_zone = false; _is_expire_bsr_zone = false; _is_test_bsr_zone = false; } } void BsrZone::set_expire_bsr_zone(bool v) { _is_expire_bsr_zone = v; if (v) { _is_config_bsr_zone = false; _is_active_bsr_zone = false; _is_test_bsr_zone = false; } } void BsrZone::set_test_bsr_zone(bool v) { _is_test_bsr_zone = v; if (v) { _is_config_bsr_zone = false; _is_active_bsr_zone = false; _is_expire_bsr_zone = false; } } int BsrZone::update_config_bsr_zone(const BsrZone& new_bsr_zone, string& error_msg) { bool changed = false; UNUSED(error_msg); // // Update all relevant fields // if ((i_am_candidate_bsr() != new_bsr_zone.i_am_candidate_bsr()) || (_my_vif_index != new_bsr_zone.my_vif_index()) || (my_bsr_addr() != new_bsr_zone.my_bsr_addr()) || (my_bsr_priority() != new_bsr_zone.my_bsr_priority())) { set_i_am_candidate_bsr(new_bsr_zone.i_am_candidate_bsr(), new_bsr_zone.my_vif_index(), new_bsr_zone.my_bsr_addr(), new_bsr_zone.my_bsr_priority()); set_bsr_addr(my_bsr_addr()); set_bsr_priority(my_bsr_priority()); changed = true; } if (is_my_bsr_addr_explicit() != new_bsr_zone.is_my_bsr_addr_explicit()) { set_is_my_bsr_addr_explicit(new_bsr_zone.is_my_bsr_addr_explicit()); changed = true; } if (hash_mask_len() != new_bsr_zone.hash_mask_len()) { _hash_mask_len = new_bsr_zone.hash_mask_len(); changed = true; } if (changed) { // // TODO: XXX: schedule a timer to send immediately the Bootstrap // message with the updated information. // } return (XORP_OK); } bool BsrZone::is_consistent(string& error_msg) const { error_msg = ""; // Reset the error message list::const_iterator iter1, iter2; // Check the Bootstrap router address if (! bsr_addr().is_unicast()) { error_msg = c_format("invalid Bootstrap router address: %s", cstring(bsr_addr())); return (false); } // // Check that all group prefixes are multicast, and all RPs are unicast // for (iter1 = _bsr_group_prefix_list.begin(); iter1 != _bsr_group_prefix_list.end(); ++iter1) { const BsrGroupPrefix *bsr_group_prefix = *iter1; if (! bsr_group_prefix->group_prefix().is_multicast()) { error_msg = c_format("invalid group prefix: %s", cstring(bsr_group_prefix->group_prefix())); return (false); } list::const_iterator iter_rp; for (iter_rp = bsr_group_prefix->rp_list().begin(); iter_rp != bsr_group_prefix->rp_list().end(); ++iter_rp) { BsrRp *bsr_rp = *iter_rp; if (! bsr_rp->rp_addr().is_unicast()) { error_msg = c_format("invalid RP address: %s", cstring(bsr_rp->rp_addr())); return (false); } } } // // Check that no two group prefixes are exactly same // for (iter1 = _bsr_group_prefix_list.begin(); iter1 != _bsr_group_prefix_list.end(); ++iter1) { const BsrGroupPrefix *bsr_group_prefix1 = *iter1; iter2 = iter1; for (++iter2; iter2 != _bsr_group_prefix_list.end(); ++iter2) { const BsrGroupPrefix *bsr_group_prefix2 = *iter2; if (bsr_group_prefix1->group_prefix() == bsr_group_prefix2->group_prefix()) { error_msg = c_format("group prefix %s received more than once", cstring(bsr_group_prefix1->group_prefix())); return (false); } } } // // XXX: we could check that an RP address is listed no more than once // per group prefix. However, listing an RP more that once is not // very harmful: the lastest listing of the RP overrides the previous. // if (! zone_id().is_scope_zone()) return (true); // // Scope zone. The first group prefix must be the group range // for the entire scope range. // list::const_iterator iter; iter = _bsr_group_prefix_list.begin(); if (iter == _bsr_group_prefix_list.end()) return (true); // OK: no group prefixes; probably a message // to expire the BSR. // // Test that the remaining group prefixes are a subset of 'zone_id'. // for (++iter; iter != _bsr_group_prefix_list.end(); ++iter) { const BsrGroupPrefix *bsr_group_prefix = *iter; if (! zone_id().contains(bsr_group_prefix->group_prefix())) { error_msg = c_format("group prefix %s is not contained in " "scope zone %s", cstring(bsr_group_prefix->group_prefix()), cstring(zone_id())); return (false); // ERROR: group prefix is not contained. } } return (true); // OK } bool BsrZone::can_merge_rp_set(const BsrZone& bsr_zone, string& error_msg) const { list::const_iterator iter_prefix; list::const_iterator iter_rp; // // Check the new fragment priority for consistency // if (bsr_priority() != bsr_zone.bsr_priority()) { error_msg = c_format("inconsistent fragment: " "old fragment for zone %s has priority %d; " "new fragment has priority %d", cstring(zone_id()), bsr_priority(), bsr_zone.bsr_priority()); return (false); } // // Check the new fragment hash mask length for consistency // if (hash_mask_len() != bsr_zone.hash_mask_len()) { error_msg = c_format("inconsistent fragment: " "old fragment for zone %s has hash mask_len %d; " "new fragment has hash mask_len %d", cstring(zone_id()), hash_mask_len(), bsr_zone.hash_mask_len()); return (false); } // // Check the group prefixes for consistency // for (iter_prefix = bsr_zone.bsr_group_prefix_list().begin(); iter_prefix != bsr_zone.bsr_group_prefix_list().end(); ++iter_prefix) { const BsrGroupPrefix *bsr_group_prefix = *iter_prefix; const BsrGroupPrefix *active_bsr_group_prefix = find_bsr_group_prefix( bsr_group_prefix->group_prefix()); if (active_bsr_group_prefix == NULL) continue; // A new group prefix. Fine. // // Check the number of expected RPs // if (active_bsr_group_prefix->expected_rp_count() != bsr_group_prefix->expected_rp_count()) { error_msg = c_format("inconsistent 'RP count': " "old fragment for zone %s has " "'RP count' of %d; " "in the new fragment the count is %d", cstring(zone_id()), active_bsr_group_prefix->expected_rp_count(), bsr_group_prefix->expected_rp_count()); return (false); } // // Check the list of received RPs // // // Check that the new RPs are not repeating, and that the total // number of RPs is not too large. // uint32_t rp_count_sum = active_bsr_group_prefix->received_rp_count(); for (iter_rp = bsr_group_prefix->rp_list().begin(); iter_rp != bsr_group_prefix->rp_list().end(); ++iter_rp) { BsrRp *bsr_rp = *iter_rp; if (active_bsr_group_prefix->find_rp(bsr_rp->rp_addr()) != NULL) { error_msg = c_format("BSR message fragment for zone %s " "already contains entry for RP %s", cstring(zone_id()), cstring(bsr_rp->rp_addr())); return (false); } rp_count_sum++; } if (rp_count_sum > active_bsr_group_prefix->expected_rp_count()) { error_msg = c_format("inconsistent 'fragment RP count': " "sum of old and new fragments count " "for zone %s is too large: %u while " "the expected count is %u", cstring(zone_id()), XORP_UINT_CAST(rp_count_sum), active_bsr_group_prefix->expected_rp_count()); return (false); } } return (true); } // // Add the list of group prefixes // void BsrZone::merge_rp_set(const BsrZone& bsr_zone) { list::const_iterator iter_prefix; list::const_iterator iter_rp; for (iter_prefix = bsr_zone.bsr_group_prefix_list().begin(); iter_prefix != bsr_zone.bsr_group_prefix_list().end(); ++iter_prefix) { BsrGroupPrefix *bsr_group_prefix = *iter_prefix; BsrGroupPrefix *org_bsr_group_prefix = find_bsr_group_prefix( bsr_group_prefix->group_prefix()); if (org_bsr_group_prefix == NULL) { // A new group prefix. Add it to the list. BsrGroupPrefix *new_bsr_group_prefix; new_bsr_group_prefix = new BsrGroupPrefix(*this, *bsr_group_prefix); // // XXX: if a scope zone, and if there is a group-RP prefix for // the whole zone, this prefix must be in front. // if (new_bsr_group_prefix->is_scope_zone() && (new_bsr_group_prefix->group_prefix() == zone_id().scope_zone_prefix())) { _bsr_group_prefix_list.push_front(new_bsr_group_prefix); } else { _bsr_group_prefix_list.push_back(new_bsr_group_prefix); } continue; } // Add the information about the new RPs. for (iter_rp = bsr_group_prefix->rp_list().begin(); iter_rp != bsr_group_prefix->rp_list().end(); ++iter_rp) { BsrRp *bsr_rp = *iter_rp; org_bsr_group_prefix->add_rp(bsr_rp->rp_addr(), bsr_rp->rp_priority(), bsr_rp->rp_holdtime()); } } // // Remove the old RPs for a received prefix, but only if // all RPs for that prefix are received. // if (is_active_bsr_zone()) { list::const_iterator iter_prefix; for (iter_prefix = bsr_group_prefix_list().begin(); iter_prefix != bsr_group_prefix_list().end(); ++iter_prefix) { const BsrGroupPrefix *bsr_group_prefix = *iter_prefix; if (bsr_group_prefix->received_rp_count() < bsr_group_prefix->expected_rp_count()) { continue; } pim_bsr().delete_expire_bsr_zone_prefix( bsr_group_prefix->group_prefix(), bsr_group_prefix->is_scope_zone()); } } } // // Store the RP-set // void BsrZone::store_rp_set(const BsrZone& bsr_zone) { // // Add a copy of this zone to the expiring zone list // if (is_active_bsr_zone()) pim_bsr().add_expire_bsr_zone(*this); // // Delete the old prefixes // delete_pointers_list(_bsr_group_prefix_list); // // Copy the RP-set // list::const_iterator iter; for (iter = bsr_zone.bsr_group_prefix_list().begin(); iter != bsr_zone.bsr_group_prefix_list().end(); ++iter) { const BsrGroupPrefix *bsr_group_prefix = *iter; BsrGroupPrefix *bsr_group_prefix_copy = new BsrGroupPrefix(*this, *bsr_group_prefix); _bsr_group_prefix_list.push_back(bsr_group_prefix_copy); } // Set the new BSR _bsr_addr = bsr_zone.bsr_addr(); _bsr_priority = bsr_zone.bsr_priority(); _hash_mask_len = bsr_zone.hash_mask_len(); _fragment_tag = bsr_zone.fragment_tag(); _is_accepted_message = bsr_zone.is_accepted_message(), _is_unicast_message = bsr_zone.is_unicast_message(); _unicast_message_src = bsr_zone.unicast_message_src(); // // Remove the old RPs for a received prefix, but only if // all RPs for that prefix are received. // if (is_active_bsr_zone()) { list::const_iterator iter_prefix; for (iter_prefix = bsr_group_prefix_list().begin(); iter_prefix != bsr_group_prefix_list().end(); ++iter_prefix) { const BsrGroupPrefix *bsr_group_prefix = *iter_prefix; if (bsr_group_prefix->received_rp_count() < bsr_group_prefix->expected_rp_count()) { continue; } pim_bsr().delete_expire_bsr_zone_prefix( bsr_group_prefix->group_prefix(), bsr_group_prefix->is_scope_zone()); } } } // Create a new BSR prefix, and return a pointer to it. BsrGroupPrefix * BsrZone::add_bsr_group_prefix(const IPvXNet& group_prefix_init, bool is_scope_zone_init, uint8_t expected_rp_count) { BsrGroupPrefix *bsr_group_prefix; bsr_group_prefix = new BsrGroupPrefix(*this, group_prefix_init, is_scope_zone_init, expected_rp_count); // // XXX: if a scope zone, and if there is a group-RP prefix for the whole // zone, this prefix must be in front. // if (is_scope_zone_init && (bsr_group_prefix->group_prefix() == zone_id().scope_zone_prefix())) { _bsr_group_prefix_list.push_front(bsr_group_prefix); } else { _bsr_group_prefix_list.push_back(bsr_group_prefix); } return (bsr_group_prefix); } void BsrZone::delete_bsr_group_prefix(BsrGroupPrefix *bsr_group_prefix) { _bsr_group_prefix_list.remove(bsr_group_prefix); delete bsr_group_prefix; } // Return: a pointer to the BSR prefix if a matching prefix found, // otherwise NULL. BsrGroupPrefix * BsrZone::find_bsr_group_prefix(const IPvXNet& group_prefix) const { list::const_iterator iter_prefix; for (iter_prefix = bsr_group_prefix_list().begin(); iter_prefix != bsr_group_prefix_list().end(); ++iter_prefix) { BsrGroupPrefix *bsr_group_prefix = *iter_prefix; if (bsr_group_prefix->group_prefix() == group_prefix) return (bsr_group_prefix); } return (NULL); } // Process a Cand-BSR information for that zone. // Return true if @cand_bsr_zone is the new BSR, otherwise false. // XXX: assumes that if necessary, 'Forward BSM' event // will happen in the parent function. bool BsrZone::process_candidate_bsr(const BsrZone& cand_bsr_zone) { XLOG_ASSERT(is_active_bsr_zone()); // TODO: handle the case when reconfiguring the local BSR on-the-fly // (not in the spec). if (bsr_zone_state() == BsrZone::STATE_INIT) { // // A new entry // // // Set my_vif_index, my_bsr_addr and my_bsr_priority // if I am a Cand-BSR for this zone. // BsrZone *config_bsr_zone = pim_bsr().find_config_bsr_zone(zone_id()); if ((config_bsr_zone != NULL) && config_bsr_zone->i_am_candidate_bsr()) { // I am a Cand-BSR for this zone // -> P-BSR state set_bsr_zone_state(BsrZone::STATE_PENDING_BSR); set_i_am_candidate_bsr(config_bsr_zone->i_am_candidate_bsr(), config_bsr_zone->my_vif_index(), config_bsr_zone->my_bsr_addr(), config_bsr_zone->my_bsr_priority()); // Set BS Timer to BS Timeout TimeVal tv(PIM_BOOTSTRAP_BOOTSTRAP_TIMEOUT_DEFAULT, 0); _bsr_timer = pim_bsr().pim_node().eventloop().new_oneoff_after( tv, callback(this, &BsrZone::bsr_timer_timeout)); return (false); } else { // I am not a Cand-BSR for this zone if ((config_bsr_zone != NULL) || (! zone_id().is_scope_zone())) set_bsr_zone_state(BsrZone::STATE_ACCEPT_ANY); else set_bsr_zone_state(BsrZone::STATE_NO_INFO); // XXX: fall-through with the processing below XLOG_ASSERT(! i_am_candidate_bsr()); } } if (i_am_candidate_bsr()) goto candidate_bsr_state_machine_label; goto non_candidate_bsr_state_machine_label; candidate_bsr_state_machine_label: // // Per-Scope-Zone State-machine for a Cand-BSR // if (bsr_zone_state() == BsrZone::STATE_CANDIDATE_BSR) goto bsr_zone_state_candidate_bsr_label; if (bsr_zone_state() == BsrZone::STATE_PENDING_BSR) goto bsr_zone_state_pending_bsr_label; if (bsr_zone_state() == BsrZone::STATE_ELECTED_BSR) goto bsr_zone_state_elected_bsr_label; // Invalid state XLOG_UNREACHABLE(); return (false); bsr_zone_state_candidate_bsr_label: // // Candidate BSR state // if (is_new_bsr_preferred(cand_bsr_zone) || is_new_bsr_same_priority(cand_bsr_zone)) { // XXX: some of actions below are not in the spec // Receive Preferred BSM or BSM from Elected BSR // -> C-BSR state set_bsr_zone_state(BsrZone::STATE_CANDIDATE_BSR); // Forward BSM : will happen in the parent function set_bsm_forward(true); if (is_new_bsr_preferred(cand_bsr_zone)) { // Store RP-Set store_rp_set(cand_bsr_zone); expire_candidate_rp_advertise_timer(); // Send my Cand-RP-Adv } else { // Update the Elected BSR if (fragment_tag() == cand_bsr_zone.fragment_tag()) { // Merge RP-Set merge_rp_set(cand_bsr_zone); } else { // Store RP-Set store_rp_set(cand_bsr_zone); } } // Set BS Timer to BS Timeout _bsr_timer = pim_bsr().pim_node().eventloop().new_oneoff_after( TimeVal(PIM_BOOTSTRAP_BOOTSTRAP_TIMEOUT_DEFAULT, 0), callback(this, &BsrZone::bsr_timer_timeout)); return (true); } if (bsr_addr() == cand_bsr_zone.bsr_addr()) { // Receive Non-preferred BSM from Elected BSR // -> P-BSR state set_bsr_zone_state(BsrZone::STATE_PENDING_BSR); // Update Elected BSR preference (TODO: XXX: not in the spec) _bsr_addr = cand_bsr_zone.bsr_addr(); _bsr_priority = cand_bsr_zone.bsr_priority(); // Set BS Timer to rand_override TimeVal rand_override = randomized_override_interval(_my_bsr_addr, _my_bsr_priority); _bsr_timer = pim_bsr().pim_node().eventloop().new_oneoff_after( rand_override, callback(this, &BsrZone::bsr_timer_timeout)); return (false); } // Receive Non-preferred BSM. Ignore. return (false); bsr_zone_state_pending_bsr_label: // Pending BSR state if (is_new_bsr_preferred(cand_bsr_zone)) { // Receive Preferred BSM // -> C-BSR state set_bsr_zone_state(BsrZone::STATE_CANDIDATE_BSR); // Forward BSM : will happen in the parent function set_bsm_forward(true); // Store RP-Set store_rp_set(cand_bsr_zone); expire_candidate_rp_advertise_timer(); // Send my Cand-RP-Adv // Set BS Timer to BS Timeout _bsr_timer = pim_bsr().pim_node().eventloop().new_oneoff_after( TimeVal(PIM_BOOTSTRAP_BOOTSTRAP_TIMEOUT_DEFAULT, 0), callback(this, &BsrZone::bsr_timer_timeout)); return (true); } // Receive Non-preferred BSM // -> P-BSR state set_bsr_zone_state(BsrZone::STATE_PENDING_BSR); return (false); bsr_zone_state_elected_bsr_label: // Elected BSR state if (is_new_bsr_preferred(cand_bsr_zone)) { // Receive Preferred BSM // -> C-BSR state set_bsr_zone_state(BsrZone::STATE_CANDIDATE_BSR); // Forward BSM : will happen in the parent function set_bsm_forward(true); // Store RP-Set store_rp_set(cand_bsr_zone); expire_candidate_rp_advertise_timer(); // Send my Cand-RP-Adv // Set BS Timer to BS Timeout _bsr_timer = pim_bsr().pim_node().eventloop().new_oneoff_after( TimeVal(PIM_BOOTSTRAP_BOOTSTRAP_TIMEOUT_DEFAULT, 0), callback(this, &BsrZone::bsr_timer_timeout)); return (true); } // Receive Non-preferred BSM // -> E-BSR state set_bsr_zone_state(BsrZone::STATE_ELECTED_BSR); // Originate BSM _is_bsm_originate = true; // Set BS Timer to BS Period _bsr_timer = pim_bsr().pim_node().eventloop().new_oneoff_after( TimeVal(PIM_BOOTSTRAP_BOOTSTRAP_PERIOD_DEFAULT, 0), callback(this, &BsrZone::bsr_timer_timeout)); return (false); non_candidate_bsr_state_machine_label: // // Per-Scope-Zone State-machine for a router not configured as Cand-BSR // if (bsr_zone_state() == BsrZone::STATE_NO_INFO) goto bsr_zone_state_no_info_label; if (bsr_zone_state() == BsrZone::STATE_ACCEPT_ANY) goto bsr_zone_state_accept_any_label; if (bsr_zone_state() == BsrZone::STATE_ACCEPT_PREFERRED) goto bsr_zone_state_accept_preferred_label; // Invalid state XLOG_UNREACHABLE(); return (false); bsr_zone_state_no_info_label: // NoInfo state // -> AP state set_bsr_zone_state(BsrZone::STATE_ACCEPT_PREFERRED); // Forward BSM : will happen in the parent function set_bsm_forward(true); // Store RP-Set store_rp_set(cand_bsr_zone); expire_candidate_rp_advertise_timer(); // Send my Cand-RP-Adv // Set BS Timer to BS Timeout _bsr_timer = pim_bsr().pim_node().eventloop().new_oneoff_after( TimeVal(PIM_BOOTSTRAP_BOOTSTRAP_TIMEOUT_DEFAULT, 0), callback(this, &BsrZone::bsr_timer_timeout)); // Set SZ Timer to SZ Timeout _scope_zone_expiry_timer = pim_bsr().pim_node().eventloop().new_oneoff_after( TimeVal(PIM_BOOTSTRAP_SCOPE_ZONE_TIMEOUT_DEFAULT, 0), callback(this, &BsrZone::scope_zone_expiry_timer_timeout)); return (true); bsr_zone_state_accept_any_label: // Accept Any state // -> AP State set_bsr_zone_state(BsrZone::STATE_ACCEPT_PREFERRED); // Forward BSM : will happen in the parent function set_bsm_forward(true); // Store RP-Set store_rp_set(cand_bsr_zone); expire_candidate_rp_advertise_timer(); // Send my Cand-RP-Adv // Set BS Timer to BS Timeout _bsr_timer = pim_bsr().pim_node().eventloop().new_oneoff_after( TimeVal(PIM_BOOTSTRAP_BOOTSTRAP_TIMEOUT_DEFAULT, 0), callback(this, &BsrZone::bsr_timer_timeout)); // Set SZ Timer to SZ Timeout _scope_zone_expiry_timer = pim_bsr().pim_node().eventloop().new_oneoff_after( TimeVal(PIM_BOOTSTRAP_SCOPE_ZONE_TIMEOUT_DEFAULT, 0), callback(this, &BsrZone::scope_zone_expiry_timer_timeout)); return (true); bsr_zone_state_accept_preferred_label: // Accept Preferred state if (is_new_bsr_preferred(cand_bsr_zone) || is_new_bsr_same_priority(cand_bsr_zone)) { // XXX: some of the actions below are not in the spec // Receive Preferred BSM or BSM from Elected BSR // -> AP State set_bsr_zone_state(BsrZone::STATE_ACCEPT_PREFERRED); // Forward BSM : will happen in the parent function set_bsm_forward(true); if (is_new_bsr_preferred(cand_bsr_zone)) { // Store RP-Set store_rp_set(cand_bsr_zone); expire_candidate_rp_advertise_timer(); // Send my Cand-RP-Adv } else { // Update the Elected BSR if (fragment_tag() == cand_bsr_zone.fragment_tag()) { // Merge RP-Set merge_rp_set(cand_bsr_zone); } else { // Store RP-Set store_rp_set(cand_bsr_zone); } } // Set BS Timer to BS Timeout _bsr_timer = pim_bsr().pim_node().eventloop().new_oneoff_after( TimeVal(PIM_BOOTSTRAP_BOOTSTRAP_TIMEOUT_DEFAULT, 0), callback(this, &BsrZone::bsr_timer_timeout)); // Set SZ Timer to SZ Timeout _scope_zone_expiry_timer = pim_bsr().pim_node().eventloop().new_oneoff_after( TimeVal(PIM_BOOTSTRAP_SCOPE_ZONE_TIMEOUT_DEFAULT, 0), callback(this, &BsrZone::scope_zone_expiry_timer_timeout)); return (true); } // Receive Non-preferred BSM // -> AP state set_bsr_zone_state(BsrZone::STATE_ACCEPT_PREFERRED); return (false); } // Return true if I am the BSR bool BsrZone::i_am_bsr() const { return (i_am_candidate_bsr() && (bsr_addr() == my_bsr_addr())); } // Return true if the received BSR is preferred than the current one. bool BsrZone::is_new_bsr_preferred(const BsrZone& bsr_zone) const { IPvX compare_bsr_addr = bsr_addr(); uint8_t compare_bsr_priority = bsr_priority(); // // If I am in "Pending BSR" state, then use my own address and priority // if (bsr_zone_state() == BsrZone::STATE_PENDING_BSR) { compare_bsr_addr = my_bsr_addr(); compare_bsr_priority = my_bsr_priority(); } if (bsr_zone.bsr_priority() > compare_bsr_priority) return (true); if (bsr_zone.bsr_priority() < compare_bsr_priority) return (false); if (bsr_zone.bsr_addr() > compare_bsr_addr) return (true); return (false); } // Return true if the received BSR is same priority. bool BsrZone::is_new_bsr_same_priority(const BsrZone& bsr_zone) const { IPvX compare_bsr_addr = bsr_addr(); uint8_t compare_bsr_priority = bsr_priority(); // // If I am in "Pending BSR" state, then use my own address and priority // if (bsr_zone_state() == BsrZone::STATE_PENDING_BSR) { compare_bsr_addr = my_bsr_addr(); compare_bsr_priority = my_bsr_priority(); } if ((bsr_zone.bsr_priority() == compare_bsr_priority) && (bsr_zone.bsr_addr() == compare_bsr_addr)) return (true); return (false); } TimeVal BsrZone::randomized_override_interval(const IPvX& my_addr, uint8_t my_priority) const { double addr_delay, delay; uint8_t best_priority = max(bsr_priority(), my_priority); uint8_t priority_diff; uint8_t my_addr_array[sizeof(my_addr)]; uint8_t stored_addr_array[sizeof(my_addr)]; double my_addr_double, stored_addr_double; size_t addr_bitlen = IPvX::addr_bitlen(_pim_bsr.family()); size_t addr_bytelen = IPvX::addr_bytelen(_pim_bsr.family()); // Get the address values my_addr.copy_out(my_addr_array); bsr_addr().copy_out(stored_addr_array); // Get the (double) value of the addresses my_addr_double = 0.0; stored_addr_double = 0.0; for (size_t i = 0; i < addr_bytelen; i++) { my_addr_double = my_addr_double * 256 + (double)my_addr_array[i]; stored_addr_double = stored_addr_double * 256 + (double)stored_addr_array[i]; } // Compute AddrDelay if (bsr_priority() == my_priority) { double addr_diff; if (stored_addr_double > my_addr_double) addr_diff = stored_addr_double - my_addr_double; else addr_diff = 1.0; // XXX addr_delay = log(addr_diff) / log((double)2.0); // log2() addr_delay /= (addr_bitlen / 2); // 16 for IPv4 } else { addr_delay = 2 - (my_addr_double / pow((double)2.0, (double)(addr_bitlen - 1))); } XLOG_ASSERT((addr_delay >= 0.0) && (addr_delay <= 2.0)); if (best_priority >= my_priority) priority_diff = best_priority - my_priority; else priority_diff = 0; // XXX delay = PIM_BOOTSTRAP_RAND_OVERRIDE_DEFAULT + 2*(log((double)(1 + priority_diff))/log((double)2.0)) // log2() + addr_delay; return (TimeVal(delay)); } // // Expire the BsrTimer (i.e., schedule it to expire NOW). // void BsrZone::expire_bsr_timer() { _bsr_timer = pim_bsr().pim_node().eventloop().new_oneoff_after( TimeVal(0, 0), callback(this, &BsrZone::bsr_timer_timeout)); } void BsrZone::bsr_timer_timeout() { string dummy_error_msg; XLOG_ASSERT(is_active_bsr_zone()); if (bsr_zone_state() == BsrZone::STATE_CANDIDATE_BSR) goto bsr_zone_state_candidate_bsr_label; if (bsr_zone_state() == BsrZone::STATE_PENDING_BSR) goto bsr_zone_state_pending_bsr_label; if (bsr_zone_state() == BsrZone::STATE_ELECTED_BSR) goto bsr_zone_state_elected_bsr_label; if (bsr_zone_state() == BsrZone::STATE_ACCEPT_PREFERRED) goto bsr_zone_state_accept_preferred_label; // Invalid state XLOG_UNREACHABLE(); return; bsr_zone_state_candidate_bsr_label: // Candidate BSR state // -> P-BSR state set_bsr_zone_state(BsrZone::STATE_PENDING_BSR); // Set BS Timer to rand_override { TimeVal rand_override; rand_override = randomized_override_interval(my_bsr_addr(), my_bsr_priority()); _bsr_timer = pim_bsr().pim_node().eventloop().new_oneoff_after( rand_override, callback(this, &BsrZone::bsr_timer_timeout)); return; } bsr_zone_state_pending_bsr_label: // Pending BSR state // -> E-BSR state set_bsr_zone_state(BsrZone::STATE_ELECTED_BSR); // Store RP-Set { BsrZone *config_bsr_zone = pim_bsr().find_config_bsr_zone(zone_id()); XLOG_ASSERT(config_bsr_zone != NULL); // TODO: XXX: PAVPAVPAV: need to be careful with the above assert in // case we have reconfigured the BSR on-the-fly store_rp_set(*config_bsr_zone); // TODO: XXX: PAVPAVPAV: do we want to set the fragment_tag to // something new here? // Add the RPs to the RP table pim_bsr().add_rps_to_rp_table(); } // Originate BSM new_fragment_tag(); for (uint32_t i = 0; i < pim_bsr().pim_node().maxvifs(); i++) { PimVif *pim_vif = pim_bsr().pim_node().vif_find_by_vif_index(i); if (pim_vif == NULL) continue; pim_vif->pim_bootstrap_send(IPvX::PIM_ROUTERS(pim_vif->family()), *this, dummy_error_msg); } // Set BS Timer to BS Period _bsr_timer = pim_bsr().pim_node().eventloop().new_oneoff_after( TimeVal(PIM_BOOTSTRAP_BOOTSTRAP_PERIOD_DEFAULT, 0), callback(this, &BsrZone::bsr_timer_timeout)); return; bsr_zone_state_elected_bsr_label: // Elected BSR state // -> E-BSR state set_bsr_zone_state(BsrZone::STATE_ELECTED_BSR); // Originate BSM new_fragment_tag(); for (uint32_t i = 0; i < pim_bsr().pim_node().maxvifs(); i++) { PimVif *pim_vif = pim_bsr().pim_node().vif_find_by_vif_index(i); if (pim_vif == NULL) continue; pim_vif->pim_bootstrap_send(IPvX::PIM_ROUTERS(pim_vif->family()), *this, dummy_error_msg); } // Set BS Timer to BS Period _bsr_timer = pim_bsr().pim_node().eventloop().new_oneoff_after( TimeVal(PIM_BOOTSTRAP_BOOTSTRAP_PERIOD_DEFAULT, 0), callback(this, &BsrZone::bsr_timer_timeout)); return; bsr_zone_state_accept_preferred_label: // Accept Preferred state // -> AA State set_bsr_zone_state(BsrZone::STATE_ACCEPT_ANY); return; } void BsrZone::scope_zone_expiry_timer_timeout() { XLOG_ASSERT(is_active_bsr_zone()); if (bsr_zone_state() == BsrZone::STATE_ACCEPT_ANY) goto bsr_zone_state_accept_any_label; // Invalid state XLOG_UNREACHABLE(); return; bsr_zone_state_accept_any_label: // NoInfo state set_bsr_zone_state(BsrZone::STATE_NO_INFO); // Cancel timers // Clear state pim_bsr().delete_active_bsr_zone(this); return; } void BsrZone::set_i_am_candidate_bsr(bool i_am_candidate_bsr, uint32_t my_vif_index, const IPvX& my_bsr_addr, uint8_t my_bsr_priority) { _i_am_candidate_bsr = i_am_candidate_bsr; _my_vif_index = my_vif_index; _my_bsr_addr = my_bsr_addr; _my_bsr_priority = my_bsr_priority; // // Set the other fields appropriately if I am the elected BSR // if (i_am_bsr()) { _bsr_priority = _my_bsr_priority; } } BsrRp * BsrZone::add_rp(const IPvXNet& group_prefix, bool is_scope_zone_init, const IPvX& rp_addr, uint8_t rp_priority, uint16_t rp_holdtime, string& error_msg) { BsrGroupPrefix *bsr_group_prefix; BsrRp *bsr_rp = NULL; error_msg = ""; if (! group_prefix.is_multicast()) { error_msg = c_format("group prefix %s is not a multicast address", cstring(group_prefix)); return (NULL); } if (! rp_addr.is_unicast()) { error_msg = c_format("RP address %s is not an unicast address", cstring(rp_addr)); return (NULL); } // Check for consistency if ((zone_id().is_scope_zone() != is_scope_zone_init) || (! zone_id().contains(group_prefix))) { error_msg = c_format("scope zone %s does not contain prefix %s", cstring(zone_id()), cstring(group_prefix)); return (NULL); } bsr_group_prefix = find_bsr_group_prefix(group_prefix); if (bsr_group_prefix == NULL) { bsr_group_prefix = add_bsr_group_prefix(group_prefix, is_scope_zone_init, 0); XLOG_ASSERT(bsr_group_prefix != NULL); } bsr_rp = bsr_group_prefix->find_rp(rp_addr); if (bsr_rp != NULL) { // Matching BsrRp entry found. Update it. bsr_rp->set_rp_priority(rp_priority); bsr_rp->set_rp_holdtime(rp_holdtime); return (bsr_rp); } // Create state for the new BsrRp if (bsr_group_prefix->expected_rp_count() == bsr_group_prefix->received_rp_count()) { if (bsr_group_prefix->expected_rp_count() == ((uint8_t)~0U)) { // XXX: too many RPs already // TODO: if the BSR, it should perform more intelligent // selection about which RPs to keep and which to remove // when the number of all Cand-RP is too large. return (NULL); } bsr_group_prefix->set_expected_rp_count( bsr_group_prefix->expected_rp_count() + 1); // XXX: ugly } bsr_rp = bsr_group_prefix->add_rp(rp_addr, rp_priority, rp_holdtime); return (bsr_rp); } // // Find an RP for a BsrZone // BsrRp * BsrZone::find_rp(const IPvXNet& group_prefix, const IPvX& rp_addr) const { BsrGroupPrefix *bsr_group_prefix = find_bsr_group_prefix(group_prefix); if (bsr_group_prefix == NULL) return (NULL); return (bsr_group_prefix->find_rp(rp_addr)); } // // Start the Candidate-RP-Advertise timer // void BsrZone::start_candidate_rp_advertise_timer() { // TODO: instead of PIM_CAND_RP_ADV_PERIOD_DEFAULT we should use // a configurable value _candidate_rp_advertise_timer = pim_bsr().pim_node().eventloop().new_oneoff_after( TimeVal(PIM_CAND_RP_ADV_PERIOD_DEFAULT, 0), callback(this, &BsrZone::candidate_rp_advertise_timer_timeout)); } // // Expire the Candidate-RP-Advertise timer (i.e., schedule it to expire NOW). // However, we use only configured BsrZone to send Cand-RP-Adv. // Therefore, we need to search for the corresponding config BsrZone and // expire the timer in that one instead. // XXX: if no Cand-RP is configured, silently ignore it. // void BsrZone::expire_candidate_rp_advertise_timer() { // // Search for the corresponding config BsrZone // BsrZone *config_bsr_zone = pim_bsr().find_config_bsr_zone(zone_id()); if (config_bsr_zone == NULL) { // Probably I am not configured as a Cand-RP. Ignore. return; } config_bsr_zone->candidate_rp_advertise_timer() = pim_bsr().pim_node().eventloop().new_oneoff_after( TimeVal(0, 0), callback(config_bsr_zone, &BsrZone::candidate_rp_advertise_timer_timeout)); } // Time to send a Cand-RP-Advertise message to the BSR void BsrZone::candidate_rp_advertise_timer_timeout() { PimVif *pim_vif = NULL; const BsrZone *active_bsr_zone = NULL; // // Find the active BsrZone // active_bsr_zone = pim_bsr().find_active_bsr_zone(zone_id()); do { if (active_bsr_zone == NULL) break; // No active BsrZone yet if (! active_bsr_zone->bsr_addr().is_unicast()) break; // We don't know the BSR address if (active_bsr_zone->i_am_bsr()) break; // I am the BSR, hence don't send the message // Test if there is a BSR if (! ((active_bsr_zone->bsr_zone_state() == BsrZone::STATE_CANDIDATE_BSR) || (active_bsr_zone->bsr_zone_state() == BsrZone::STATE_ACCEPT_PREFERRED))) { break; } // // Find the RPF vif toward the BSR // pim_vif = pim_bsr().pim_node().pim_vif_rpf_find(active_bsr_zone->bsr_addr()); if ((pim_vif == NULL) || (! pim_vif->is_up())) { XLOG_ERROR("Cannot send Cand-RP Adv message to %s: " "cannot find the RPF vif", cstring(active_bsr_zone->bsr_addr())); break; } pim_vif->pim_cand_rp_adv_send(active_bsr_zone->bsr_addr(), *this); } while (false); // Restart the timer start_candidate_rp_advertise_timer(); } BsrGroupPrefix::BsrGroupPrefix(BsrZone& bsr_zone, const IPvXNet& group_prefix, bool is_scope_zone, uint8_t expected_rp_count) : _bsr_zone(bsr_zone), _group_prefix(group_prefix), _is_scope_zone(is_scope_zone), _expected_rp_count(expected_rp_count) { _received_rp_count = 0; } BsrGroupPrefix::BsrGroupPrefix(BsrZone& bsr_zone, const BsrGroupPrefix& bsr_group_prefix) : _bsr_zone(bsr_zone), _group_prefix(bsr_group_prefix.group_prefix()), _is_scope_zone(bsr_group_prefix.is_scope_zone()), _expected_rp_count(bsr_group_prefix.expected_rp_count()), _received_rp_count(bsr_group_prefix.received_rp_count()) { // // Copy the list of RPs // list::const_iterator iter; for (iter = bsr_group_prefix.rp_list().begin(); iter != bsr_group_prefix.rp_list().end(); ++iter) { const BsrRp *bsr_rp = *iter; BsrRp *bsr_rp_copy = new BsrRp(*this, *bsr_rp); _rp_list.push_back(bsr_rp_copy); } // Conditionally set the timer to remove this group prefix if (bsr_group_prefix.const_remove_timer().scheduled()) { TimeVal tv_left; bsr_group_prefix.const_remove_timer().time_remaining(tv_left); _remove_timer = _bsr_zone.pim_bsr().pim_node().eventloop().new_oneoff_after( tv_left, callback(this, &BsrGroupPrefix::remove_timer_timeout)); } } BsrGroupPrefix::~BsrGroupPrefix() { list::iterator iter; // Remove the RPs one-by-one do { iter = _rp_list.begin(); if (iter == _rp_list.end()) break; BsrRp *bsr_rp = *iter; delete_rp(bsr_rp); } while (true); } // Return: a pointer to the RP entry if it can be added, otherwise NULL. BsrRp * BsrGroupPrefix::add_rp(const IPvX& rp_addr, uint8_t rp_priority, uint16_t rp_holdtime) { BsrRp *bsr_rp = find_rp(rp_addr); if (bsr_rp != NULL) { // Matching RP entry found. Update its holdtime and priority bsr_rp->set_rp_priority(rp_priority); bsr_rp->set_rp_holdtime(rp_holdtime); return (bsr_rp); } // No matching entry found. Add a new one. bsr_rp = new BsrRp(*this, rp_addr, rp_priority, rp_holdtime); _rp_list.push_back(bsr_rp); _received_rp_count++; return (bsr_rp); } void BsrGroupPrefix::delete_rp(BsrRp *bsr_rp) { for (list::iterator iter = _rp_list.begin(); iter != _rp_list.end(); ++iter) { if (bsr_rp != *iter) continue; // Entry found. Remove the RP entry, and adjust the RP count values. // Also, if necessary, schedule the group prefix for removal. _rp_list.erase(iter); set_received_rp_count(received_rp_count() - 1); if (bsr_zone().i_am_bsr() && bsr_zone().is_active_bsr_zone()) { set_expected_rp_count(expected_rp_count() - 1); if (expected_rp_count() == 0) schedule_bsr_group_prefix_remove(); } // // Schedule the task to clean the expiring bsr zones // if (bsr_zone().is_expire_bsr_zone()) { bsr_zone().pim_bsr().schedule_clean_expire_bsr_zones(); } // // If we don't have this RP anymore, then delete the RP entry // from the RP table // if (bsr_zone().is_expire_bsr_zone() || bsr_zone().is_active_bsr_zone()) { if (bsr_zone().pim_bsr().find_rp(group_prefix(), is_scope_zone(), bsr_rp->rp_addr()) == NULL) { bsr_zone().pim_bsr().pim_node().rp_table().delete_rp( bsr_rp->rp_addr(), group_prefix(), PimRp::RP_LEARNED_METHOD_BOOTSTRAP); bsr_zone().pim_bsr().schedule_rp_table_apply_rp_changes(); } } delete bsr_rp; return; } } BsrRp * BsrGroupPrefix::find_rp(const IPvX& rp_addr) const { // Search if we already have an entry for this RP for (list::const_iterator iter = _rp_list.begin(); iter != _rp_list.end(); ++iter) { BsrRp *bsr_rp = *iter; if (bsr_rp->rp_addr() == rp_addr) return (bsr_rp); } return (NULL); } void BsrGroupPrefix::schedule_bsr_group_prefix_remove() { _remove_timer = bsr_zone().pim_bsr().pim_node().eventloop().new_oneoff_after( TimeVal(PIM_BOOTSTRAP_BOOTSTRAP_TIMEOUT_DEFAULT, 0), callback(this, &BsrGroupPrefix::remove_timer_timeout)); } void BsrGroupPrefix::remove_timer_timeout() { // // Timeout this entry only if I am the BSR, this is an active BSR zone, // and there are no more RPs. // if (! bsr_zone().i_am_bsr()) return; if (! bsr_zone().is_active_bsr_zone()) return; if (! rp_list().empty()) return; // Delete the prefix bsr_zone().delete_bsr_group_prefix(this); } BsrRp::BsrRp(BsrGroupPrefix& bsr_group_prefix, const IPvX& rp_addr, uint8_t rp_priority, uint16_t rp_holdtime) : _bsr_group_prefix(bsr_group_prefix), _rp_addr(rp_addr), _rp_priority(rp_priority), _rp_holdtime(rp_holdtime), _my_vif_index(Vif::VIF_INDEX_INVALID), _is_my_rp_addr_explicit(false) { } BsrRp::BsrRp(BsrGroupPrefix& bsr_group_prefix, const BsrRp& bsr_rp) : _bsr_group_prefix(bsr_group_prefix), _rp_addr(bsr_rp.rp_addr()), _rp_priority(bsr_rp.rp_priority()), _rp_holdtime(bsr_rp.rp_holdtime()), _my_vif_index(bsr_rp.my_vif_index()), _is_my_rp_addr_explicit(bsr_rp.is_my_rp_addr_explicit()) { // Conditionally set the Cand-RP Expiry Timer if (bsr_rp.const_candidate_rp_expiry_timer().scheduled()) { TimeVal tv_left; bsr_rp.const_candidate_rp_expiry_timer().time_remaining(tv_left); _candidate_rp_expiry_timer = _bsr_group_prefix.bsr_zone().pim_bsr().pim_node().eventloop().new_oneoff_after( tv_left, callback(this, &BsrRp::candidate_rp_expiry_timer_timeout)); } } void BsrRp::start_candidate_rp_expiry_timer() { _candidate_rp_expiry_timer = bsr_group_prefix().bsr_zone().pim_bsr().pim_node().eventloop().new_oneoff_after( TimeVal(_rp_holdtime, 0), callback(this, &BsrRp::candidate_rp_expiry_timer_timeout)); } // Time to expire the Cand-RP void BsrRp::candidate_rp_expiry_timer_timeout() { bsr_group_prefix().delete_rp(this); } xorp/pim/xrl_pim_node.hh0000664000076400007640000022017711634760770015471 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __PIM_XRL_PIM_NODE_HH__ #define __PIM_XRL_PIM_NODE_HH__ // // PIM XRL-aware node definition. // #include "libxorp/transaction.hh" #include "libxipc/xrl_std_router.hh" #include "libfeaclient/ifmgr_xrl_mirror.hh" #include "xrl/interfaces/finder_event_notifier_xif.hh" #include "xrl/interfaces/fea_rawpkt4_xif.hh" #ifdef HAVE_IPV6 #include "xrl/interfaces/fea_rawpkt6_xif.hh" #endif #include "xrl/interfaces/mfea_xif.hh" #include "xrl/interfaces/rib_xif.hh" #include "xrl/interfaces/mld6igmp_xif.hh" #include "xrl/interfaces/cli_manager_xif.hh" #include "xrl/targets/pim_base.hh" #include "pim_node.hh" #include "pim_node_cli.hh" #include "pim_mfc.hh" // // The top-level class that wraps-up everything together under one roof // class XrlPimNode : public PimNode, public XrlStdRouter, public XrlPimTargetBase, public PimNodeCli { public: XrlPimNode(int family, xorp_module_id module_id, EventLoop& eventloop, const string& class_name, const string& finder_hostname, uint16_t finder_port, const string& finder_target, const string& fea_target, const string& mfea_target, const string& rib_target, const string& mld6igmp_target); virtual ~XrlPimNode(); /** Does real destruction work. */ virtual void destruct_me(); /** * Startup the node operation. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int startup(); /** * Shutdown the node operation. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int shutdown(); /** * Get a reference to the XrlRouter instance. * * @return a reference to the XrlRouter (@ref XrlRouter) instance. */ XrlRouter& xrl_router() { return *this; } /** * Get a const reference to the XrlRouter instance. * * @return a const reference to the XrlRouter (@ref XrlRouter) instance. */ const XrlRouter& xrl_router() const { return *this; } // // XrlPimNode front-end interface // int enable_cli(); int disable_cli(); int start_cli(); int stop_cli(); int enable_pim(); int disable_pim(); int start_pim(); int stop_pim(); int enable_bsr(); int disable_bsr(); int start_bsr(); int stop_bsr(); // XrlTask relatedMethods that need to be public void send_register_unregister_interest(); void send_register_unregister_receiver(); void send_register_unregister_protocol(); void send_join_leave_multicast_group(); void send_protocol_message(); void send_add_delete_mfc(); void send_add_delete_dataflow_monitor(); protected: // // XRL target methods // /** * Get name of Xrl Target */ XrlCmdError common_0_1_get_target_name( // Output values, string& name); /** * Get version string from Xrl Target */ XrlCmdError common_0_1_get_version( // Output values, string& version); /** * Get status from Xrl Target */ XrlCmdError common_0_1_get_status(// Output values, uint32_t& status, string& reason); /** * Shutdown cleanly */ XrlCmdError common_0_1_shutdown(); XrlCmdError common_0_1_startup(); /** * Announce target birth to observer. * * @param target_class the target class name. * * @param target_instance the target instance name. */ XrlCmdError finder_event_observer_0_1_xrl_target_birth( // Input values, const string& target_class, const string& target_instance); /** * Announce target death to observer. * * @param target_class the target class name. * * @param target_instance the target instance name. */ XrlCmdError finder_event_observer_0_1_xrl_target_death( // Input values, const string& target_class, const string& target_instance); /** * Process a CLI command. * * @param processor_name the processor name for this command. * * @param cli_term_name the terminal name the command was entered from. * * @param cli_session_id the CLI session ID the command was entered from. * * @param command_name the command name to process. * * @param command_args the command arguments to process. * * @param ret_processor_name the processor name to return back to the CLI. * * @param ret_cli_term_name the terminal name to return back. * * @param ret_cli_session_id the CLI session ID to return back. * * @param ret_command_output the command output to return back. */ XrlCmdError cli_processor_0_1_process_command( // Input values, const string& processor_name, const string& cli_term_name, const uint32_t& cli_session_id, const string& command_name, const string& command_args, // Output values, string& ret_processor_name, string& ret_cli_term_name, uint32_t& ret_cli_session_id, string& ret_command_output); /** * Receive an IPv4 packet from a raw socket. * * @param if_name the interface name the packet arrived on. * * @param vif_name the vif name the packet arrived on. * * @param src_address the IP source address. * * @param dst_address the IP destination address. * * @param ip_protocol the IP protocol number. * * @param ip_ttl the IP TTL (hop-limit). If it has a negative value, then * the received value is unknown. * * @param ip_tos the Type of Service (Diffserv/ECN bits for IPv4). If it * has a negative value, then the received value is unknown. * * @param ip_router_alert if true, the IP Router Alert option was included * in the IP packet. * * @param ip_internet_control if true, then this is IP control traffic. */ XrlCmdError raw_packet4_client_0_1_recv( // Input values, const string& if_name, const string& vif_name, const IPv4& src_address, const IPv4& dst_address, const uint32_t& ip_protocol, const int32_t& ip_ttl, const int32_t& ip_tos, const bool& ip_router_alert, const bool& ip_internet_control, const vector& payload); #ifdef HAVE_IPV6 /** * Receive an IPv6 packet from a raw socket. * * @param if_name the interface name the packet arrived on. * * @param vif_name the vif name the packet arrived on. * * @param src_address the IP source address. * * @param dst_address the IP destination address. * * @param ip_protocol the IP protocol number. * * @param ip_ttl the IP TTL (hop-limit). If it has a negative value, then * the received value is unknown. * * @param ip_tos the Type Of Service (IP traffic class for IPv4). If it * has a negative value, then the received value is unknown. * * @param ip_router_alert if true, the IP Router Alert option was included * in the IP packet. * * @param ip_internet_control if true, then this is IP control traffic. * * @param ext_headers_type a list of u32 integers with the types of the * optional extention headers. * * @param ext_headers_payload a list of payload data, one for each * optional extention header. The number of entries must match * ext_headers_type. */ XrlCmdError raw_packet6_client_0_1_recv( // Input values, const string& if_name, const string& vif_name, const IPv6& src_address, const IPv6& dst_address, const uint32_t& ip_protocol, const int32_t& ip_ttl, const int32_t& ip_tos, const bool& ip_router_alert, const bool& ip_internet_control, const XrlAtomList& ext_headers_type, const XrlAtomList& ext_headers_payload, const vector& payload); #endif /** * * Receive a kernel signal message from the MFEA. * * @param xrl_sender_name the XRL name of the originator of this XRL. * * @param message_type the type of the kernel signal message (TODO: * integer for now: the particular types are well-known by both sides). * * @param vif_name the name of the vif the message was received on. * * @param vif_index the index of the vif the message was received on. * * @param source_address the address of the sender. * * @param dest_address the destination address. * * @param protocol_message the protocol message. */ XrlCmdError mfea_client_0_1_recv_kernel_signal_message4( // Input values, const string& xrl_sender_name, const uint32_t& message_type, const string& vif_name, const uint32_t& vif_index, const IPv4& source_address, const IPv4& dest_address, const vector& protocol_message); #ifdef HAVE_IPV6 XrlCmdError mfea_client_0_1_recv_kernel_signal_message6( // Input values, const string& xrl_sender_name, const uint32_t& message_type, const string& vif_name, const uint32_t& vif_index, const IPv6& source_address, const IPv6& dest_address, const vector& protocol_message); #endif /** * A signal that a dataflow-related pre-condition is true. * * @param xrl_sender_name the XRL name of the originator of this XRL. * * @param source_address the source address of the dataflow. * * @param group_address the group address of the dataflow. * * @param threshold_interval_sec the number of seconds in the interval * requested for measurement. * * @param threshold_interval_usec the number of microseconds in the * interval requested for measurement. * * @param measured_interval_sec the number of seconds in the last measured * interval that has triggered the signal. * * @param measured_interval_usec the number of microseconds in the last * measured interval that has triggered the signal. * * @param threshold_packets the threshold value to trigger a signal (in * number of packets). * * @param threshold_bytes the threshold value to trigger a signal (in * bytes). * * @param measured_packets the number of packets measured within the * measured interval. * * @param measured_bytes the number of bytes measured within the measured * interval. * * @param is_threshold_in_packets if true, threshold_packets is valid. * * @param is_threshold_in_bytes if true, threshold_bytes is valid. * * @param is_geq_upcall if true, the operation for comparison is ">=". * * @param is_leq_upcall if true, the operation for comparison is "<=". */ XrlCmdError mfea_client_0_1_recv_dataflow_signal4( // Input values, const string& xrl_sender_name, const IPv4& source_address, const IPv4& group_address, const uint32_t& threshold_interval_sec, const uint32_t& threshold_interval_usec, const uint32_t& measured_interval_sec, const uint32_t& measured_interval_usec, const uint32_t& threshold_packets, const uint32_t& threshold_bytes, const uint32_t& measured_packets, const uint32_t& measured_bytes, const bool& is_threshold_in_packets, const bool& is_threshold_in_bytes, const bool& is_geq_upcall, const bool& is_leq_upcall); #ifdef HAVE_IPV6 XrlCmdError mfea_client_0_1_recv_dataflow_signal6( // Input values, const string& xrl_sender_name, const IPv6& source_address, const IPv6& group_address, const uint32_t& threshold_interval_sec, const uint32_t& threshold_interval_usec, const uint32_t& measured_interval_sec, const uint32_t& measured_interval_usec, const uint32_t& threshold_packets, const uint32_t& threshold_bytes, const uint32_t& measured_packets, const uint32_t& measured_bytes, const bool& is_threshold_in_packets, const bool& is_threshold_in_bytes, const bool& is_geq_upcall, const bool& is_leq_upcall); #endif /** * Start transaction. * * @param tid the transaction ID to use for this transaction. */ XrlCmdError redist_transaction4_0_1_start_transaction( // Output values, uint32_t& tid); /** * Commit transaction. * * @param tid the transaction ID of this transaction. */ XrlCmdError redist_transaction4_0_1_commit_transaction( // Input values, const uint32_t& tid); /** * Abort transaction. * * @param tid the transaction ID of this transaction. */ XrlCmdError redist_transaction4_0_1_abort_transaction( // Input values, const uint32_t& tid); /** * Add/delete a routing entry. * * @param tid the transaction ID of this transaction. * * @param dst destination network. * * @param nexthop nexthop router address. * * @param ifname interface name associated with nexthop. * * @param vifname virtual interface name with nexthop. * * @param metric origin routing protocol metric for route. * * @param admin_distance administrative distance of origin routing * protocol. * * @param cookie value set by the requestor to identify redistribution * source. Typical value is the originating protocol name. * * @param protocol_origin the name of the protocol that originated this * routing entry. */ XrlCmdError redist_transaction4_0_1_add_route( // Input values, const uint32_t& tid, const IPv4Net& dst, const IPv4& nexthop, const string& ifname, const string& vifname, const uint32_t& metric, const uint32_t& admin_distance, const string& cookie, const string& protocol_origin); XrlCmdError redist_transaction4_0_1_delete_route( // Input values, const uint32_t& tid, const IPv4Net& dst, const IPv4& nexthop, const string& ifname, const string& vifname, const uint32_t& metric, const uint32_t& admin_distance, const string& cookie, const string& protocol_origin); /** * Delete all routing entries. * * @param tid the transaction ID of this transaction. * * @param cookie value set by the requestor to identify redistribution * source. Typical value is the originating protocol name. */ XrlCmdError redist_transaction4_0_1_delete_all_routes( // Input values, const uint32_t& tid, const string& cookie); #ifdef HAVE_IPV6 /** * Start transaction. * * @param tid the transaction ID to use for this transaction. */ XrlCmdError redist_transaction6_0_1_start_transaction( // Output values, uint32_t& tid); #endif /** * Commit transaction. * * @param tid the transaction ID of this transaction. */ XrlCmdError redist_transaction6_0_1_commit_transaction( // Input values, const uint32_t& tid); /** * Abort transaction. * * @param tid the transaction ID of this transaction. */ XrlCmdError redist_transaction6_0_1_abort_transaction( // Input values, const uint32_t& tid); /** * Add/delete a routing entry. * * @param tid the transaction ID of this transaction. * * @param dst destination network. * * @param nexthop nexthop router address. * * @param ifname interface name associated with nexthop. * * @param vifname virtual interface name with nexthop. * * @param metric origin routing protocol metric for route. * * @param admin_distance administrative distance of origin routing * protocol. * * @param cookie value set by the requestor to identify redistribution * source. Typical value is the originating protocol name. * * @param protocol_origin the name of the protocol that originated this * routing entry. */ XrlCmdError redist_transaction6_0_1_add_route( // Input values, const uint32_t& tid, const IPv6Net& dst, const IPv6& nexthop, const string& ifname, const string& vifname, const uint32_t& metric, const uint32_t& admin_distance, const string& cookie, const string& protocol_origin); XrlCmdError redist_transaction6_0_1_delete_route( // Input values, const uint32_t& tid, const IPv6Net& dst, const IPv6& nexthop, const string& ifname, const string& vifname, const uint32_t& metric, const uint32_t& admin_distance, const string& cookie, const string& protocol_origin); /** * Delete all routing entries. * * @param tid the transaction ID of this transaction. * * @param cookie value set by the requestor to identify redistribution * source. Typical value is the originating protocol name. */ XrlCmdError redist_transaction6_0_1_delete_all_routes( // Input values, const uint32_t& tid, const string& cookie); /** * Add/delete membership information. * * @param xrl_sender_name the XRL name of the originator of this XRL. * * @param vif_name the name of the new vif. * * @param vif_index the index of the new vif. * * @param source the source address that has been joined/left. * * @param group the group address that has been joined/left. */ XrlCmdError mld6igmp_client_0_1_add_membership4( // Input values, const string& xrl_sender_name, const string& vif_name, const uint32_t& vif_index, const IPv4& source, const IPv4& group); XrlCmdError mld6igmp_client_0_1_add_membership6( // Input values, const string& xrl_sender_name, const string& vif_name, const uint32_t& vif_index, const IPv6& source, const IPv6& group); XrlCmdError mld6igmp_client_0_1_delete_membership4( // Input values, const string& xrl_sender_name, const string& vif_name, const uint32_t& vif_index, const IPv4& source, const IPv4& group); XrlCmdError mld6igmp_client_0_1_delete_membership6( // Input values, const string& xrl_sender_name, const string& vif_name, const uint32_t& vif_index, const IPv6& source, const IPv6& group); /** * Enable/disable/start/stop a PIM vif interface. * * @param vif_name the name of the vif to enable/disable/start/stop. * * @param enable if true, then enable the vif, otherwise disable it. */ XrlCmdError pim_0_1_enable_vif( // Input values, const string& vif_name, const bool& enable); XrlCmdError pim_0_1_start_vif( // Input values, const string& vif_name); XrlCmdError pim_0_1_stop_vif( // Input values, const string& vif_name); /** * Enable/disable/start/stop all PIM vif interfaces. * * @param enable if true, then enable the vifs, otherwise disable them. */ XrlCmdError pim_0_1_enable_all_vifs( // Input values, const bool& enable); XrlCmdError pim_0_1_disable_all_vifs(); XrlCmdError pim_0_1_start_all_vifs(); XrlCmdError pim_0_1_stop_all_vifs(); /** * Enable/disable/start/stop the PIM protocol. * * @param enable if true, then enable the PIM protocol, otherwise disable * it. */ XrlCmdError pim_0_1_enable_pim( // Input values, const bool& enable); XrlCmdError pim_0_1_start_pim(); XrlCmdError pim_0_1_stop_pim(); /** * Enable/disable/start/stop the PIM CLI access. * * @param enable if true, then enable the PIM CLI access, otherwise * disable it. */ XrlCmdError pim_0_1_enable_cli( // Input values, const bool& enable); XrlCmdError pim_0_1_start_cli(); XrlCmdError pim_0_1_stop_cli(); /** * Enable/disable/start/stop BSR. * * @param enable if true, then enable the BSR, otherwise disable it. */ XrlCmdError pim_0_1_enable_bsr( // Input values, const bool& enable); XrlCmdError pim_0_1_start_bsr(); XrlCmdError pim_0_1_stop_bsr(); /** * Apply BSR configuration changes. */ XrlCmdError pim_0_1_apply_bsr_changes(); /** * Add/delete scope zone. * * @param scope_zone_id the ID of the configured zone. * * @param vif_name the name of the vif to use as a bondary of the scope * zone. */ XrlCmdError pim_0_1_add_config_scope_zone_by_vif_name4( // Input values, const IPv4Net& scope_zone_id, const string& vif_name); XrlCmdError pim_0_1_add_config_scope_zone_by_vif_name6( // Input values, const IPv6Net& scope_zone_id, const string& vif_name); XrlCmdError pim_0_1_add_config_scope_zone_by_vif_addr4( // Input values, const IPv4Net& scope_zone_id, const IPv4& vif_addr); XrlCmdError pim_0_1_add_config_scope_zone_by_vif_addr6( // Input values, const IPv6Net& scope_zone_id, const IPv6& vif_addr); XrlCmdError pim_0_1_delete_config_scope_zone_by_vif_name4( // Input values, const IPv4Net& scope_zone_id, const string& vif_name); XrlCmdError pim_0_1_delete_config_scope_zone_by_vif_name6( // Input values, const IPv6Net& scope_zone_id, const string& vif_name); XrlCmdError pim_0_1_delete_config_scope_zone_by_vif_addr4( // Input values, const IPv4Net& scope_zone_id, const IPv4& vif_addr); XrlCmdError pim_0_1_delete_config_scope_zone_by_vif_addr6( // Input values, const IPv6Net& scope_zone_id, const IPv6& vif_addr); /** * Add/delete candidate-BSR configuration. * * @param scope_zone_id the ID of the configured zone. * * @param is_scope_zone true if configuring administratively scoped zone. * * @param vif_name the name of the vif to use its address as a * candidate-BSR. * * @param vif_addr the address of the vif to use as a candidate-BSR. * * @param bsr_priority the BSR priority (larger is better). * * @param hash_mask_len the hash mask length. */ XrlCmdError pim_0_1_add_config_cand_bsr4( // Input values, const IPv4Net& scope_zone_id, const bool& is_scope_zone, const string& vif_name, const IPv4& vif_addr, const uint32_t& bsr_priority, const uint32_t& hash_mask_len); XrlCmdError pim_0_1_add_config_cand_bsr6( // Input values, const IPv6Net& scope_zone_id, const bool& is_scope_zone, const string& vif_name, const IPv6& vif_addr, const uint32_t& bsr_priority, const uint32_t& hash_mask_len); XrlCmdError pim_0_1_delete_config_cand_bsr4( // Input values, const IPv4Net& scope_zone_id, const bool& is_scope_zone); XrlCmdError pim_0_1_delete_config_cand_bsr6( // Input values, const IPv6Net& scope_zone_id, const bool& is_scope_zone); /** * Add/delete Candidate-RP configuration. * * @param group_prefix the group prefix of the configured zone. * * @param is_scope_zone true if configuring administratively scoped zone. * * @param vif_name the name of the vif to use its address as a * candidate-RP. * * @param vif_addr the address of the vif to use as a candidate-RP. * * @param rp_priority the Cand-RP priority (smaller is better). * * @param rp_holdtime the Cand-RP holdtime (in seconds). */ XrlCmdError pim_0_1_add_config_cand_rp4( // Input values, const IPv4Net& group_prefix, const bool& is_scope_zone, const string& vif_name, const IPv4& vif_addr, const uint32_t& rp_priority, const uint32_t& rp_holdtime); XrlCmdError pim_0_1_add_config_cand_rp6( // Input values, const IPv6Net& group_prefix, const bool& is_scope_zone, const string& vif_name, const IPv6& vif_addr, const uint32_t& rp_priority, const uint32_t& rp_holdtime); XrlCmdError pim_0_1_delete_config_cand_rp4( // Input values, const IPv4Net& group_prefix, const bool& is_scope_zone, const string& vif_name, const IPv4& vif_addr); XrlCmdError pim_0_1_delete_config_cand_rp6( // Input values, const IPv6Net& group_prefix, const bool& is_scope_zone, const string& vif_name, const IPv6& vif_addr); /** * Add/delete/complete static RP configuration. * * @param group_prefix the group prefix for the RP. * * @param rp_addr the RP address. * * @param rp_priority the RP priority (smaller is better). * * @param hash_mask_len the hash mask length used in computing an RP for a * group. It should be same across all RPs. If set to zero, the default * one will be used. */ XrlCmdError pim_0_1_add_config_static_rp4( // Input values, const IPv4Net& group_prefix, const IPv4& rp_addr, const uint32_t& rp_priority, const uint32_t& hash_mask_len); XrlCmdError pim_0_1_add_config_static_rp6( // Input values, const IPv6Net& group_prefix, const IPv6& rp_addr, const uint32_t& rp_priority, const uint32_t& hash_mask_len); XrlCmdError pim_0_1_delete_config_static_rp4( // Input values, const IPv4Net& group_prefix, const IPv4& rp_addr); XrlCmdError pim_0_1_delete_config_static_rp6( // Input values, const IPv6Net& group_prefix, const IPv6& rp_addr); XrlCmdError pim_0_1_delete_config_all_static_group_prefixes_rp4( // Input values, const IPv4& rp_addr); XrlCmdError pim_0_1_delete_config_all_static_group_prefixes_rp6( // Input values, const IPv6& rp_addr); XrlCmdError pim_0_1_delete_config_all_static_rps(); XrlCmdError pim_0_1_config_static_rp_done(); /** * Get the configured protocol version per interface. * * @param vif_name the name of the vif to apply to. * * @param proto_version the protocol version. */ XrlCmdError pim_0_1_get_vif_proto_version( // Input values, const string& vif_name, // Output values, uint32_t& proto_version); /** * Set the protocol version per interface. * * @param vif_name the name of the vif to apply to. * * @param proto_version the protocol version. */ XrlCmdError pim_0_1_set_vif_proto_version( // Input values, const string& vif_name, const uint32_t& proto_version); /** * Reset the protocol version per interface to its default value. * * @param vif_name the name of the vif to apply to. */ XrlCmdError pim_0_1_reset_vif_proto_version( // Input values, const string& vif_name); /** * Configure PIM Hello-related metrics. The 'set_foo' XRLs set the * particular values. The 'reset_foo' XRLs reset the metrics to their * default values. * * @param vif_name the name of the vif to configure. * * @param proto_version the protocol version. */ XrlCmdError pim_0_1_get_vif_hello_triggered_delay( // Input values, const string& vif_name, // Output values, uint32_t& hello_triggered_delay); XrlCmdError pim_0_1_set_vif_hello_triggered_delay( // Input values, const string& vif_name, const uint32_t& hello_triggered_delay); XrlCmdError pim_0_1_reset_vif_hello_triggered_delay( // Input values, const string& vif_name); XrlCmdError pim_0_1_get_vif_hello_period( // Input values, const string& vif_name, // Output values, uint32_t& hello_period); XrlCmdError pim_0_1_set_vif_hello_period( // Input values, const string& vif_name, const uint32_t& hello_period); XrlCmdError pim_0_1_reset_vif_hello_period( // Input values, const string& vif_name); XrlCmdError pim_0_1_get_vif_hello_holdtime( // Input values, const string& vif_name, // Output values, uint32_t& hello_holdtime); XrlCmdError pim_0_1_set_vif_hello_holdtime( // Input values, const string& vif_name, const uint32_t& hello_holdtime); XrlCmdError pim_0_1_reset_vif_hello_holdtime( // Input values, const string& vif_name); XrlCmdError pim_0_1_get_vif_dr_priority( // Input values, const string& vif_name, // Output values, uint32_t& dr_priority); XrlCmdError pim_0_1_set_vif_dr_priority( // Input values, const string& vif_name, const uint32_t& dr_priority); XrlCmdError pim_0_1_reset_vif_dr_priority( // Input values, const string& vif_name); XrlCmdError pim_0_1_get_vif_propagation_delay( // Input values, const string& vif_name, // Output values, uint32_t& propagation_delay); XrlCmdError pim_0_1_set_vif_propagation_delay( // Input values, const string& vif_name, const uint32_t& propagation_delay); XrlCmdError pim_0_1_reset_vif_propagation_delay( // Input values, const string& vif_name); XrlCmdError pim_0_1_get_vif_override_interval( // Input values, const string& vif_name, // Output values, uint32_t& override_interval); XrlCmdError pim_0_1_set_vif_override_interval( // Input values, const string& vif_name, const uint32_t& override_interval); XrlCmdError pim_0_1_reset_vif_override_interval( // Input values, const string& vif_name); XrlCmdError pim_0_1_get_vif_is_tracking_support_disabled( // Input values, const string& vif_name, // Output values, bool& is_tracking_support_disabled); XrlCmdError pim_0_1_set_vif_is_tracking_support_disabled( // Input values, const string& vif_name, const bool& is_tracking_support_disabled); XrlCmdError pim_0_1_reset_vif_is_tracking_support_disabled( // Input values, const string& vif_name); XrlCmdError pim_0_1_get_vif_accept_nohello_neighbors( // Input values, const string& vif_name, // Output values, bool& accept_nohello_neighbors); XrlCmdError pim_0_1_set_vif_accept_nohello_neighbors( // Input values, const string& vif_name, const bool& accept_nohello_neighbors); XrlCmdError pim_0_1_reset_vif_accept_nohello_neighbors( // Input values, const string& vif_name); /** * Configure PIM Join/Prune-related metrics. The 'set_foo' XRLs set the * particular values. The 'reset_foo' XRLs reset the metrics to their * default values. * * @param vif_name the name of the vif to configure. * * @param join_prune_period the period between Join/Prune messages. */ XrlCmdError pim_0_1_get_vif_join_prune_period( // Input values, const string& vif_name, // Output values, uint32_t& join_prune_period); XrlCmdError pim_0_1_set_vif_join_prune_period( // Input values, const string& vif_name, const uint32_t& join_prune_period); XrlCmdError pim_0_1_reset_vif_join_prune_period( // Input values, const string& vif_name); /** * Configure SPT-switch threshold. The 'set_foo' XRLs set the particular * values. The 'reset_foo' XRLs reset the metrics to their default values. * * @param is_enabled if true, enable SPT-switch, otherwise disable it. * * @param interval_sec if the SPT-switch is enabled, the interval (in * number of seconds) to measure the bandwidth to consider whether to * switch to the SPT. */ XrlCmdError pim_0_1_get_switch_to_spt_threshold( // Output values, bool& is_enabled, uint32_t& interval_sec, uint32_t& bytes); XrlCmdError pim_0_1_set_switch_to_spt_threshold( // Input values, const bool& is_enabled, const uint32_t& interval_sec, const uint32_t& bytes); XrlCmdError pim_0_1_reset_switch_to_spt_threshold(); /** * Add or delete an alternative subnet on a PIM vif. An alternative subnet * is used to make incoming traffic with a non-local source address appear * as it is coming from a local subnet. Note: add alternative subnets with * extreme care, only if you know what you are really doing! * * @param vif_name the name of the vif to add or delete an alternative * subnet. * * @param subnet the subnet address to add or delete. */ XrlCmdError pim_0_1_add_alternative_subnet4( // Input values, const string& vif_name, const IPv4Net& subnet); XrlCmdError pim_0_1_add_alternative_subnet6( // Input values, const string& vif_name, const IPv6Net& subnet); XrlCmdError pim_0_1_delete_alternative_subnet4( // Input values, const string& vif_name, const IPv4Net& subnet); XrlCmdError pim_0_1_delete_alternative_subnet6( // Input values, const string& vif_name, const IPv6Net& subnet); XrlCmdError pim_0_1_remove_all_alternative_subnets( // Input values, const string& vif_name); /** * Enable/disable the PIM trace log for all operations. * * @param enable if true, then enable the trace log, otherwise disable it. */ XrlCmdError pim_0_1_log_trace_all( // Input values, const bool& enable); /** * Test-related methods: add Join/Prune entries, and send them to a * neighbor. */ XrlCmdError pim_0_1_add_test_jp_entry4( // Input values, const IPv4& source_addr, const IPv4& group_addr, const uint32_t& group_mask_len, const string& mrt_entry_type, const string& action_jp, const uint32_t& holdtime, const bool& is_new_group); XrlCmdError pim_0_1_add_test_jp_entry6( // Input values, const IPv6& source_addr, const IPv6& group_addr, const uint32_t& group_mask_len, const string& mrt_entry_type, const string& action_jp, const uint32_t& holdtime, const bool& is_new_group); XrlCmdError pim_0_1_send_test_jp_entry4( // Input values, const string& vif_name, const IPv4& nbr_addr); XrlCmdError pim_0_1_send_test_jp_entry6( // Input values, const string& vif_name, const IPv6& nbr_addr); /** * Test-related methods: send an Assert message on an interface. * * @param vif_name the name of the vif to send the Assert on. * * @param source_addr the source address inside the Assert message. * * @param group_addr the group address inside the Assert message. * * @param rpt_bit the RPT-bit inside the Assert message. * * @param metric_preference the metric preference inside the Assert * message. * * @param metric the metric inside the Assert message. */ XrlCmdError pim_0_1_send_test_assert4( // Input values, const string& vif_name, const IPv4& source_addr, const IPv4& group_addr, const bool& rpt_bit, const uint32_t& metric_preference, const uint32_t& metric); XrlCmdError pim_0_1_send_test_assert6( // Input values, const string& vif_name, const IPv6& source_addr, const IPv6& group_addr, const bool& rpt_bit, const uint32_t& metric_preference, const uint32_t& metric); /** * Test-related methods: send Bootstrap and Cand-RP-Adv messages. * * @param zone_id_scope_zone_prefix the zone prefix of the zone ID. * * @param zone_id_is_scope_zone true if the zone is scoped. * * @param bsr_addr the address of the Bootstrap router. * * @param bsr_priority the priority of the Bootstrap router. * * @param hash_mask_len the hash mask length inside the Bootstrap * messages. * * @param fragment_tag the fragment tag inside the Bootstrap messages. */ XrlCmdError pim_0_1_add_test_bsr_zone4( // Input values, const IPv4Net& zone_id_scope_zone_prefix, const bool& zone_id_is_scope_zone, const IPv4& bsr_addr, const uint32_t& bsr_priority, const uint32_t& hash_mask_len, const uint32_t& fragment_tag); XrlCmdError pim_0_1_add_test_bsr_zone6( // Input values, const IPv6Net& zone_id_scope_zone_prefix, const bool& zone_id_is_scope_zone, const IPv6& bsr_addr, const uint32_t& bsr_priority, const uint32_t& hash_mask_len, const uint32_t& fragment_tag); XrlCmdError pim_0_1_add_test_bsr_group_prefix4( // Input values, const IPv4Net& zone_id_scope_zone_prefix, const bool& zone_id_is_scope_zone, const IPv4Net& group_prefix, const bool& is_scope_zone, const uint32_t& expected_rp_count); XrlCmdError pim_0_1_add_test_bsr_group_prefix6( // Input values, const IPv6Net& zone_id_scope_zone_prefix, const bool& zone_id_is_scope_zone, const IPv6Net& group_prefix, const bool& is_scope_zone, const uint32_t& expected_rp_count); XrlCmdError pim_0_1_add_test_bsr_rp4( // Input values, const IPv4Net& zone_id_scope_zone_prefix, const bool& zone_id_is_scope_zone, const IPv4Net& group_prefix, const IPv4& rp_addr, const uint32_t& rp_priority, const uint32_t& rp_holdtime); XrlCmdError pim_0_1_add_test_bsr_rp6( // Input values, const IPv6Net& zone_id_scope_zone_prefix, const bool& zone_id_is_scope_zone, const IPv6Net& group_prefix, const IPv6& rp_addr, const uint32_t& rp_priority, const uint32_t& rp_holdtime); XrlCmdError pim_0_1_send_test_bootstrap( // Input values, const string& vif_name); XrlCmdError pim_0_1_send_test_bootstrap_by_dest4( // Input values, const string& vif_name, const IPv4& dest_addr); XrlCmdError pim_0_1_send_test_bootstrap_by_dest6( // Input values, const string& vif_name, const IPv6& dest_addr); XrlCmdError pim_0_1_send_test_cand_rp_adv(); /** * Retrieve information about all PIM neighbors. * * @param nbrs_number the number of PIM neighbors * * @param vifs the list of vif names for all neighbors (one vif name per * neighbor). * * @param pim_versions the list of PIM protocol versions for all neighbors * (one number per neighbor). * * @param dr_priorities the list of DR priorities of all neighbors (one * number per neighbor). * * @param holdtimes the list of configured holdtimes (in seconds) of all * neighbors (one number per neighbor). * * @param timeouts the list of timeout values (in seconds) of all * neighbors (one number per neighbor). * * @param uptimes the list of uptime values (in seconds) of all neighbors * (one number per neighbor). */ XrlCmdError pim_0_1_pimstat_neighbors4( // Output values, uint32_t& nbrs_number, XrlAtomList& vifs, XrlAtomList& addresses, XrlAtomList& pim_versions, XrlAtomList& dr_priorities, XrlAtomList& holdtimes, XrlAtomList& timeouts, XrlAtomList& uptimes); XrlCmdError pim_0_1_pimstat_neighbors6( // Output values, uint32_t& nbrs_number, XrlAtomList& vifs, XrlAtomList& addresses, XrlAtomList& pim_versions, XrlAtomList& dr_priorities, XrlAtomList& holdtimes, XrlAtomList& timeouts, XrlAtomList& uptimes); /** * Retrieve information about PIM interfaces. * * @param vif_name the name of the vif to retrieve information about. * * @param pim_version the PIM protocol version on that vif. * * @param is_dr true if this router is the DR for the subnet the vif is * connected to. * * @param dr_priority the DR priority configured on that vif. * * @param dr_address the address of the DR for the subnet the vif is * connected to. * * @param pim_nbrs_number the number of PIM neighbors on the subnet * the vif is connected to. */ XrlCmdError pim_0_1_pimstat_interface4( // Input values, const string& vif_name, // Output values, uint32_t& pim_version, bool& is_dr, uint32_t& dr_priority, IPv4& dr_address, uint32_t& pim_nbrs_number); XrlCmdError pim_0_1_pimstat_interface6( // Input values, const string& vif_name, // Output values, uint32_t& pim_version, bool& is_dr, uint32_t& dr_priority, IPv6& dr_address, uint32_t& pim_nbrs_number); /** * Retrieve information about the RP-Set. * * @param rps_number the number of RPs in the RP-Set. * * @param addresses the list of addresses of all RPs (one IPv4 or IPv6 * address per RP). * * @param types the list of textual description about the origin of each * RP (one keyword per RP: "bootstrap", "static" or "unknown"). * * @param priorities the list of RP priorities of all RPs (one number per * RP). * * @param holdtimes the list of configured holdtimes (in seconds) of all * RPs (one number per RP). * * @param timeouts the list of timeout values (in seconds) of all RPs (one * number per RP). * * @param group_prefixes the list of all group prefixes (one network * IPv4Net or IPv6Net address per RP). Note that if an RP is configured * for more than one group prefixes, there will be a number of entries for * that RP: one per group prefix. */ XrlCmdError pim_0_1_pimstat_rps4( // Output values, uint32_t& rps_number, XrlAtomList& addresses, XrlAtomList& types, XrlAtomList& priorities, XrlAtomList& holdtimes, XrlAtomList& timeouts, XrlAtomList& group_prefixes); XrlCmdError pim_0_1_pimstat_rps6( // Output values, uint32_t& rps_number, XrlAtomList& addresses, XrlAtomList& types, XrlAtomList& priorities, XrlAtomList& holdtimes, XrlAtomList& timeouts, XrlAtomList& group_prefixes); /** * Clear all statistics */ XrlCmdError pim_0_1_clear_pim_statistics(); /** * Clear all statistics on a specific interface. * * @param vif_name the interface to clear the statistics of. */ XrlCmdError pim_0_1_clear_pim_statistics_per_vif( // Input values, const string& vif_name); /** * Statistics-related counters and values */ XrlCmdError pim_0_1_pimstat_hello_messages_received( // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_hello_messages_sent( // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_hello_messages_rx_errors( // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_register_messages_received( // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_register_messages_sent( // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_register_messages_rx_errors( // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_register_stop_messages_received( // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_register_stop_messages_sent( // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_register_stop_messages_rx_errors( // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_join_prune_messages_received( // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_join_prune_messages_sent( // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_join_prune_messages_rx_errors( // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_bootstrap_messages_received( // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_bootstrap_messages_sent( // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_bootstrap_messages_rx_errors( // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_assert_messages_received( // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_assert_messages_sent( // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_assert_messages_rx_errors( // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_graft_messages_received( // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_graft_messages_sent( // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_graft_messages_rx_errors( // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_graft_ack_messages_received( // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_graft_ack_messages_sent( // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_graft_ack_messages_rx_errors( // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_candidate_rp_messages_received( // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_candidate_rp_messages_sent( // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_candidate_rp_messages_rx_errors( // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_unknown_type_messages( // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_unknown_version_messages( // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_neighbor_unknown_messages( // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_bad_length_messages( // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_bad_checksum_messages( // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_bad_receive_interface_messages( // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_rx_interface_disabled_messages( // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_rx_register_not_rp( // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_rp_filtered_source( // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_unknown_register_stop( // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_rx_join_prune_no_state( // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_rx_graft_graft_ack_no_state( // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_rx_graft_on_upstream_interface( // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_rx_candidate_rp_not_bsr( // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_rx_bsr_when_bsr( // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_rx_bsr_not_rpf_interface( // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_rx_unknown_hello_option( // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_rx_data_no_state( // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_rx_rp_no_state( // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_rx_aggregate( // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_rx_malformed_packet( // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_no_rp( // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_no_route_upstream( // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_rp_mismatch( // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_rpf_neighbor_unknown( // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_rx_join_rp( // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_rx_prune_rp( // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_rx_join_wc( // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_rx_prune_wc( // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_rx_join_sg( // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_rx_prune_sg( // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_rx_join_sg_rpt( // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_rx_prune_sg_rpt( // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_hello_messages_received_per_vif( // Input values, const string& vif_name, // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_hello_messages_sent_per_vif( // Input values, const string& vif_name, // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_hello_messages_rx_errors_per_vif( // Input values, const string& vif_name, // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_register_messages_received_per_vif( // Input values, const string& vif_name, // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_register_messages_sent_per_vif( // Input values, const string& vif_name, // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_register_messages_rx_errors_per_vif( // Input values, const string& vif_name, // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_register_stop_messages_received_per_vif( // Input values, const string& vif_name, // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_register_stop_messages_sent_per_vif( // Input values, const string& vif_name, // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_register_stop_messages_rx_errors_per_vif( // Input values, const string& vif_name, // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_join_prune_messages_received_per_vif( // Input values, const string& vif_name, // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_join_prune_messages_sent_per_vif( // Input values, const string& vif_name, // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_join_prune_messages_rx_errors_per_vif( // Input values, const string& vif_name, // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_bootstrap_messages_received_per_vif( // Input values, const string& vif_name, // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_bootstrap_messages_sent_per_vif( // Input values, const string& vif_name, // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_bootstrap_messages_rx_errors_per_vif( // Input values, const string& vif_name, // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_assert_messages_received_per_vif( // Input values, const string& vif_name, // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_assert_messages_sent_per_vif( // Input values, const string& vif_name, // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_assert_messages_rx_errors_per_vif( // Input values, const string& vif_name, // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_graft_messages_received_per_vif( // Input values, const string& vif_name, // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_graft_messages_sent_per_vif( // Input values, const string& vif_name, // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_graft_messages_rx_errors_per_vif( // Input values, const string& vif_name, // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_graft_ack_messages_received_per_vif( // Input values, const string& vif_name, // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_graft_ack_messages_sent_per_vif( // Input values, const string& vif_name, // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_graft_ack_messages_rx_errors_per_vif( // Input values, const string& vif_name, // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_candidate_rp_messages_received_per_vif( // Input values, const string& vif_name, // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_candidate_rp_messages_sent_per_vif( // Input values, const string& vif_name, // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_candidate_rp_messages_rx_errors_per_vif( // Input values, const string& vif_name, // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_unknown_type_messages_per_vif( // Input values, const string& vif_name, // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_unknown_version_messages_per_vif( // Input values, const string& vif_name, // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_neighbor_unknown_messages_per_vif( // Input values, const string& vif_name, // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_bad_length_messages_per_vif( // Input values, const string& vif_name, // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_bad_checksum_messages_per_vif( // Input values, const string& vif_name, // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_bad_receive_interface_messages_per_vif( // Input values, const string& vif_name, // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_rx_interface_disabled_messages_per_vif( // Input values, const string& vif_name, // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_rx_register_not_rp_per_vif( // Input values, const string& vif_name, // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_rp_filtered_source_per_vif( // Input values, const string& vif_name, // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_unknown_register_stop_per_vif( // Input values, const string& vif_name, // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_rx_join_prune_no_state_per_vif( // Input values, const string& vif_name, // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_rx_graft_graft_ack_no_state_per_vif( // Input values, const string& vif_name, // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_rx_graft_on_upstream_interface_per_vif( // Input values, const string& vif_name, // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_rx_candidate_rp_not_bsr_per_vif( // Input values, const string& vif_name, // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_rx_bsr_when_bsr_per_vif( // Input values, const string& vif_name, // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_rx_bsr_not_rpf_interface_per_vif( // Input values, const string& vif_name, // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_rx_unknown_hello_option_per_vif( // Input values, const string& vif_name, // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_rx_data_no_state_per_vif( // Input values, const string& vif_name, // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_rx_rp_no_state_per_vif( // Input values, const string& vif_name, // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_rx_aggregate_per_vif( // Input values, const string& vif_name, // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_rx_malformed_packet_per_vif( // Input values, const string& vif_name, // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_no_rp_per_vif( // Input values, const string& vif_name, // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_no_route_upstream_per_vif( // Input values, const string& vif_name, // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_rp_mismatch_per_vif( // Input values, const string& vif_name, // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_rpf_neighbor_unknown_per_vif( // Input values, const string& vif_name, // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_rx_join_rp_per_vif( // Input values, const string& vif_name, // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_rx_prune_rp_per_vif( // Input values, const string& vif_name, // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_rx_join_wc_per_vif( // Input values, const string& vif_name, // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_rx_prune_wc_per_vif( // Input values, const string& vif_name, // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_rx_join_sg_per_vif( // Input values, const string& vif_name, // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_rx_prune_sg_per_vif( // Input values, const string& vif_name, // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_rx_join_sg_rpt_per_vif( // Input values, const string& vif_name, // Output values, uint32_t& value); XrlCmdError pim_0_1_pimstat_rx_prune_sg_rpt_per_vif( // Input values, const string& vif_name, // Output values, uint32_t& value); private: class XrlTaskBase; const ServiceBase* ifmgr_mirror_service_base() const { return dynamic_cast(&_ifmgr); } const IfMgrIfTree& ifmgr_iftree() const { return _ifmgr.iftree(); } /** * Called when Finder connection is established. * * Note that this method overwrites an XrlRouter virtual method. */ virtual void finder_connect_event(); /** * Called when Finder disconnect occurs. * * Note that this method overwrites an XrlRouter virtual method. */ virtual void finder_disconnect_event(); // // Methods to handle the XRL tasks // void add_task(XrlTaskBase* xrl_task); void send_xrl_task(); void pop_xrl_task(); void retry_xrl_task(); void fea_register_startup(); void mfea_register_startup(); void fea_register_shutdown(); void mfea_register_shutdown(); void finder_send_register_unregister_interest_cb(const XrlError& xrl_error); void rib_register_startup(); void finder_register_interest_rib_cb(const XrlError& xrl_error); void rib_register_shutdown(); void finder_deregister_interest_rib_cb(const XrlError& xrl_error); void send_rib_redist_transaction_enable(); void rib_client_send_redist_transaction_enable_cb(const XrlError& xrl_error); void send_rib_redist_transaction_disable(); void rib_client_send_redist_transaction_disable_cb(const XrlError& xrl_error); // // Protocol node methods // int register_receiver(const string& if_name, const string& vif_name, uint8_t ip_protocol, bool enable_multicast_loopback); int unregister_receiver(const string& if_name, const string& vif_name, uint8_t ip_protocol); void fea_client_send_register_unregister_receiver_cb(const XrlError& xrl_error); int register_protocol(const string& if_name, const string& vif_name, uint8_t ip_protocol); int unregister_protocol(const string& if_name, const string& vif_name); void mfea_client_send_register_unregister_protocol_cb(const XrlError& xrl_error); int join_multicast_group(const string& if_name, const string& vif_name, uint8_t ip_protocol, const IPvX& group_address); int leave_multicast_group(const string& if_name, const string& vif_name, uint8_t ip_protocol, const IPvX& group_address); void fea_client_send_join_leave_multicast_group_cb(const XrlError& xrl_error); int proto_send(const string& if_name, const string& vif_name, const IPvX& src_address, const IPvX& dst_address, uint8_t ip_protocol, int32_t ip_ttl, int32_t ip_tos, bool ip_router_alert, bool ip_internet_control, const uint8_t* sndbuf, size_t sndlen, string& error_msg); void fea_client_send_protocol_message_cb(const XrlError& xrl_error); int add_mfc_to_kernel(const PimMfc& pim_mfc); int delete_mfc_from_kernel(const PimMfc& pim_mfc); void mfea_client_send_add_delete_mfc_cb(const XrlError& xrl_error); int add_dataflow_monitor(const IPvX& source_addr, const IPvX& group_addr, uint32_t threshold_interval_sec, uint32_t threshold_interval_usec, 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); int delete_dataflow_monitor(const IPvX& source_addr, const IPvX& group_addr, uint32_t threshold_interval_sec, uint32_t threshold_interval_usec, 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); int delete_all_dataflow_monitor(const IPvX& source_addr, const IPvX& group_addr); void mfea_client_send_add_delete_dataflow_monitor_cb(const XrlError& xrl_error); int add_protocol_mld6igmp(uint32_t vif_index); int delete_protocol_mld6igmp(uint32_t vif_index); void send_add_delete_protocol_mld6igmp(); void mld6igmp_client_send_add_delete_protocol_mld6igmp_cb(const XrlError& xrl_error); void schedule_add_protocol_mld6igmp(); // // Protocol node CLI methods // int add_cli_command_to_cli_manager(const char *command_name, const char *command_help, bool is_command_cd, const char *command_cd_prompt, bool is_command_processor); void cli_manager_client_send_add_cli_command_cb(const XrlError& xrl_error); int delete_cli_command_from_cli_manager(const char *command_name); void cli_manager_client_send_delete_cli_command_cb(const XrlError& xrl_error); int family() const { return PimNode::family(); } /** * A base class for handling tasks for sending XRL requests. */ class XrlTaskBase { public: XrlTaskBase(XrlPimNode& xrl_pim_node) : _xrl_pim_node(xrl_pim_node) {} virtual ~XrlTaskBase() {} virtual void dispatch() = 0; virtual const char* operation_name() const = 0; protected: XrlPimNode& _xrl_pim_node; private: }; /** * Class for handling the task to register/unregister interest * in the FEA or MFEA with the Finder. */ class RegisterUnregisterInterest : public XrlTaskBase { public: RegisterUnregisterInterest(XrlPimNode& xrl_pim_node, const string& target_name, bool is_register) : XrlTaskBase(xrl_pim_node), _target_name(target_name), _is_register(is_register) {} void dispatch() { _xrl_pim_node.send_register_unregister_interest(); } const char* operation_name() const { return ((_is_register)? "register" : "unregister"); } const string& target_name() const { return _target_name; } bool is_register() const { return _is_register; } private: string _target_name; bool _is_register; }; /** * Class for handling the task to register/unregister with the FEA * as a receiver on an interface. */ class RegisterUnregisterReceiver : public XrlTaskBase { public: RegisterUnregisterReceiver(XrlPimNode& xrl_pim_node, const string& if_name, const string& vif_name, uint8_t ip_protocol, bool enable_multicast_loopback, bool is_register) : XrlTaskBase(xrl_pim_node), _if_name(if_name), _vif_name(vif_name), _ip_protocol(ip_protocol), _enable_multicast_loopback(enable_multicast_loopback), _is_register(is_register) {} void dispatch() { _xrl_pim_node.send_register_unregister_receiver(); } const char* operation_name() const { return ((_is_register)? "register" : "unregister"); } const string& if_name() const { return _if_name; } const string& vif_name() const { return _vif_name; } uint8_t ip_protocol() const { return _ip_protocol; } bool enable_multicast_loopback() const { return _enable_multicast_loopback; } bool is_register() const { return _is_register; } private: string _if_name; string _vif_name; uint8_t _ip_protocol; bool _enable_multicast_loopback; bool _is_register; }; /** * Class for handling the task to register/unregister with the MFEA * as a protocol on an interface. */ class RegisterUnregisterProtocol : public XrlTaskBase { public: RegisterUnregisterProtocol(XrlPimNode& xrl_pim_node, const string& if_name, const string& vif_name, uint8_t ip_protocol, bool is_register) : XrlTaskBase(xrl_pim_node), _if_name(if_name), _vif_name(vif_name), _ip_protocol(ip_protocol), _is_register(is_register) {} void dispatch() { _xrl_pim_node.send_register_unregister_protocol(); } const char* operation_name() const { return ((_is_register)? "register" : "unregister"); } const string& if_name() const { return _if_name; } const string& vif_name() const { return _vif_name; } uint8_t ip_protocol() const { return _ip_protocol; } bool is_register() const { return _is_register; } private: string _if_name; string _vif_name; uint8_t _ip_protocol; bool _is_register; }; /** * Class for handling the task of join/leave multicast group requests */ class JoinLeaveMulticastGroup : public XrlTaskBase { public: JoinLeaveMulticastGroup(XrlPimNode& xrl_pim_node, const string& if_name, const string& vif_name, uint8_t ip_protocol, const IPvX& group_address, bool is_join) : XrlTaskBase(xrl_pim_node), _if_name(if_name), _vif_name(vif_name), _ip_protocol(ip_protocol), _group_address(group_address), _is_join(is_join) {} void dispatch() { _xrl_pim_node.send_join_leave_multicast_group(); } const char* operation_name() const { return ((_is_join)? "join" : "leave"); } const string& if_name() const { return _if_name; } const string& vif_name() const { return _vif_name; } uint8_t ip_protocol() const { return _ip_protocol; } const IPvX& group_address() const { return _group_address; } bool is_join() const { return _is_join; } private: string _if_name; string _vif_name; uint8_t _ip_protocol; IPvX _group_address; bool _is_join; }; /** * Class for handling the task of sending protocol messages */ class SendProtocolMessage : public XrlTaskBase { public: SendProtocolMessage(XrlPimNode& xrl_pim_node, const string& if_name, const string& vif_name, const IPvX& src_address, const IPvX& dst_address, uint8_t ip_protocol, int32_t ip_ttl, int32_t ip_tos, bool ip_router_alert, bool ip_internet_control, const uint8_t* sndbuf, size_t sndlen) : XrlTaskBase(xrl_pim_node), _if_name(if_name), _vif_name(vif_name), _src_address(src_address), _dst_address(dst_address), _ip_protocol(ip_protocol), _ip_ttl(ip_ttl), _ip_tos(ip_tos), _ip_router_alert(ip_router_alert), _ip_internet_control(ip_internet_control) { _payload.resize(sndlen); for (size_t i = 0; i < sndlen; i++) _payload[i] = sndbuf[i]; } void dispatch() { _xrl_pim_node.send_protocol_message(); } const char* operation_name() const { return ("send"); } const string& if_name() const { return _if_name; } const string& vif_name() const { return _vif_name; } const IPvX& src_address() const { return _src_address; } const IPvX& dst_address() const { return _dst_address; } uint8_t ip_protocol() const { return _ip_protocol; } int32_t ip_ttl() const { return _ip_ttl; } int32_t ip_tos() const { return _ip_tos; } bool ip_router_alert() const { return _ip_router_alert; } bool ip_internet_control() const { return _ip_internet_control; } const vector& payload() const { return _payload; } private: string _if_name; string _vif_name; IPvX _src_address; IPvX _dst_address; uint8_t _ip_protocol; int32_t _ip_ttl; int32_t _ip_tos; bool _ip_router_alert; bool _ip_internet_control; vector _payload; }; /** * Class for handling the task of add/delete MFC requests */ class AddDeleteMfc : public XrlTaskBase { public: AddDeleteMfc(XrlPimNode& xrl_pim_node, const PimMfc& pim_mfc, bool is_add) : XrlTaskBase(xrl_pim_node), _source_addr(pim_mfc.source_addr()), _group_addr(pim_mfc.group_addr()), _rp_addr(pim_mfc.rp_addr()), _iif_vif_index(pim_mfc.iif_vif_index()), _olist(pim_mfc.olist()), _olist_disable_wrongvif(pim_mfc.olist_disable_wrongvif()), _is_add(is_add) {} void dispatch() { _xrl_pim_node.send_add_delete_mfc(); } const char* operation_name() const { return ((_is_add)? "add" : "delete"); } const IPvX& source_addr() const { return _source_addr; } const IPvX& group_addr() const { return _group_addr; } const IPvX& rp_addr() const { return _rp_addr; } uint32_t iif_vif_index() const { return _iif_vif_index; } const Mifset& olist() const { return _olist; } const Mifset& olist_disable_wrongvif() const { return _olist_disable_wrongvif; } bool is_add() const { return _is_add; } private: IPvX _source_addr; IPvX _group_addr; IPvX _rp_addr; uint32_t _iif_vif_index; Mifset _olist; Mifset _olist_disable_wrongvif; bool _is_add; }; /** * Class for handling the task of add/delete dataflow monitor requests */ class AddDeleteDataflowMonitor : public XrlTaskBase { public: AddDeleteDataflowMonitor(XrlPimNode& xrl_pim_node, const IPvX& source_addr, const IPvX& group_addr, uint32_t threshold_interval_sec, uint32_t threshold_interval_usec, 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, bool is_add) : XrlTaskBase(xrl_pim_node), _source_addr(source_addr), _group_addr(group_addr), _threshold_interval_sec(threshold_interval_sec), _threshold_interval_usec(threshold_interval_usec), _threshold_packets(threshold_packets), _threshold_bytes(threshold_bytes), _is_threshold_in_packets(is_threshold_in_packets), _is_threshold_in_bytes(is_threshold_in_bytes), _is_geq_upcall(is_geq_upcall), _is_leq_upcall(is_leq_upcall), _is_add(is_add), _is_delete_all(false) {} AddDeleteDataflowMonitor(XrlPimNode& xrl_pim_node, const IPvX& source_addr, const IPvX& group_addr) : XrlTaskBase(xrl_pim_node), _source_addr(source_addr), _group_addr(group_addr), _threshold_interval_sec(0), _threshold_interval_usec(0), _threshold_packets(0), _threshold_bytes(0), _is_threshold_in_packets(false), _is_threshold_in_bytes(false), _is_geq_upcall(false), _is_leq_upcall(false), _is_add(false), _is_delete_all(true) {} void dispatch() { _xrl_pim_node.send_add_delete_dataflow_monitor(); } const char* operation_name() const { return ((_is_delete_all)? "delete all" : (_is_add)? "add" : "delete"); } const IPvX& source_addr() const { return _source_addr; } const IPvX& group_addr() const { return _group_addr; } uint32_t threshold_interval_sec() const { return _threshold_interval_sec; } uint32_t threshold_interval_usec() const { return _threshold_interval_usec; } uint32_t threshold_packets() const { return _threshold_packets; } uint32_t threshold_bytes() const { return _threshold_bytes; } bool is_threshold_in_packets() const { return _is_threshold_in_packets; } bool is_threshold_in_bytes() const { return _is_threshold_in_bytes; } bool is_geq_upcall() const { return _is_geq_upcall; } bool is_leq_upcall() const { return _is_leq_upcall; } bool is_add() const { return _is_add; } bool is_delete_all() const { return _is_delete_all; } private: IPvX _source_addr; IPvX _group_addr; uint32_t _threshold_interval_sec; uint32_t _threshold_interval_usec; 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; bool _is_add; bool _is_delete_all; }; EventLoop& _eventloop; const string _finder_target; const string _fea_target; const string _mfea_target; const string _rib_target; const string _mld6igmp_target; IfMgrXrlMirror _ifmgr; TransactionManager _mrib_transaction_manager; XrlRawPacket4V0p1Client _xrl_fea_client4; #ifdef HAVE_IPV6 XrlRawPacket6V0p1Client _xrl_fea_client6; #endif XrlMfeaV0p1Client _xrl_mfea_client; XrlRibV0p1Client _xrl_rib_client; XrlMld6igmpV0p1Client _xrl_mld6igmp_client; XrlCliManagerV0p1Client _xrl_cli_manager_client; XrlFinderEventNotifierV0p1Client _xrl_finder_client; static const TimeVal RETRY_TIMEVAL; bool _is_finder_alive; bool _is_fea_alive; bool _is_fea_registered; bool _is_mfea_alive; bool _is_mfea_registered; bool _is_rib_alive; bool _is_rib_registered; bool _is_rib_registering; bool _is_rib_deregistering; XorpTimer _rib_register_startup_timer; XorpTimer _rib_register_shutdown_timer; bool _is_rib_redist_transaction_enabled; XorpTimer _rib_redist_transaction_enable_timer; bool _is_mld6igmp_alive; bool _is_mld6igmp_registered; bool _is_mld6igmp_registering; bool _is_mld6igmp_deregistering; XorpTimer _mld6igmp_register_startup_timer; XorpTimer _mld6igmp_register_shutdown_timer; list _xrl_tasks_queue; XorpTimer _xrl_tasks_queue_timer; list > _add_delete_protocol_mld6igmp_queue; XorpTimer _add_delete_protocol_mld6igmp_queue_timer; // The set of vifs to add to MLD6IGMP if it goes away and comes back set _add_protocol_mld6igmp_vif_index_set; }; #endif // __PIM_XRL_PIM_NODE_HH__ xorp/pim/xorp_pimsm4.cc0000664000076400007640000001333711634760770015257 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // XORP PIM-SM module implementation. // #include "pim_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/random.h" #include "libxorp/callback.hh" #include "libxorp/eventloop.hh" #include "libxorp/exceptions.hh" #include "xrl_pim_node.hh" #ifdef HAVE_GETOPT_H #include #endif // // Local functions prototypes // static void usage(const char *argv0, int exit_value); /** * usage: * @argv0: Argument 0 when the program was called (the program name itself). * @exit_value: The exit value of the program. * * Print the program usage. * If @exit_value is 0, the usage will be printed to the standart output, * otherwise to the standart error. **/ static void usage(const char *argv0, int exit_value) { FILE *output; const char *progname = strrchr(argv0, '/'); if (progname != NULL) progname++; // Skip the last '/' if (progname == NULL) progname = argv0; // // If the usage is printed because of error, output to stderr, otherwise // output to stdout. // if (exit_value == 0) output = stdout; else output = stderr; fprintf(output, "Usage: %s [-F [:]]\n", progname); fprintf(output, " -F [:] : finder hostname and port\n"); fprintf(output, " -h : usage (this message)\n"); fprintf(output, "\n"); fprintf(output, "Program name: %s\n", progname); fprintf(output, "Module name: %s\n", XORP_MODULE_NAME); fprintf(output, "Module version: %s\n", XORP_MODULE_VERSION); exit (exit_value); // NOTREACHED } static void pim_main(const string& finder_hostname, uint16_t finder_port) { setup_dflt_sighandlers(); // // Init stuff // EventLoop eventloop; // // Initialize the random generator // { TimeVal now; TimerList::system_gettimeofday(&now); xorp_srandom(now.sec()); } // // PIMSM node // XrlPimNode xrl_pimsm_node4(AF_INET, XORP_MODULE_PIMSM, eventloop, xorp_module_name(AF_INET, XORP_MODULE_PIMSM), finder_hostname, finder_port, "finder", xorp_module_name(AF_INET, XORP_MODULE_FEA), xorp_module_name(AF_INET, XORP_MODULE_MFEA), xorp_module_name(AF_INET, XORP_MODULE_RIB), xorp_module_name(AF_INET, XORP_MODULE_MLD6IGMP)); wait_until_xrl_router_is_ready(eventloop, xrl_pimsm_node4.xrl_router()); // // Startup // // XXX: temporary enable the built-in CLI access xrl_pimsm_node4.enable_pim(); // xrl_pimsm_node4.startup(); xrl_pimsm_node4.enable_cli(); xrl_pimsm_node4.start_cli(); // // Main loop // while (xorp_do_run && !xrl_pimsm_node4.is_done()) { eventloop.run(); } while (xrl_pimsm_node4.xrl_router().pending()) { eventloop.run(); } // Manually clean up xrl_pim_node. Without this logic, the destructor // will attempt the same, but that will cause some recursive cleanup // that ends up calling a pure virtual on the xlr_pim_node logic, // which throws an exception from deep in glibc and crashes us. xrl_pimsm_node4.delete_all_vifs(); xrl_pimsm_node4.shutdown(); xrl_pimsm_node4.destruct_me(); } int main(int argc, char *argv[]) { int ch; string::size_type idx; const char *argv0 = argv[0]; string finder_hostname = FinderConstants::FINDER_DEFAULT_HOST().str(); uint16_t finder_port = FinderConstants::FINDER_DEFAULT_PORT(); // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); // // Get the program options // while ((ch = getopt(argc, argv, "F:h")) != -1) { switch (ch) { case 'F': // Finder hostname and port finder_hostname = optarg; idx = finder_hostname.find(':'); if (idx != string::npos) { if (idx + 1 >= finder_hostname.length()) { // No port number usage(argv0, 1); // NOTREACHED } char* p = &finder_hostname[idx + 1]; finder_port = static_cast(atoi(p)); finder_hostname = finder_hostname.substr(0, idx); } break; case 'h': case '?': // Help usage(argv0, 0); // NOTREACHED break; default: usage(argv0, 1); // NOTREACHED break; } } argc -= optind; argv += optind; if (argc != 0) { usage(argv0, 1); // NOTREACHED } // // Run everything // try { pim_main(finder_hostname, finder_port); } catch(...) { xorp_catch_standard_exceptions(); } // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); exit (0); } xorp/pim/pim_mre_track_state.cc0000664000076400007640000034672011634760770017017 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // PIM Multicast Routing Entry state tracking // #include "pim_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvx.hh" #include "pim_mfc.hh" #include "pim_mre_track_state.hh" #include "pim_mrt.hh" #include "pim_node.hh" #include "pim_vif.hh" // // Exported variables // // // Local constants definitions // // // Local structures/classes, typedefs and macros // // // Local variables // // // Local functions prototypes // PimMreTrackState::PimMreTrackState(PimMrt* pim_mrt) : _pim_mrt(pim_mrt) { list action_list; // // Init the state dependencies // output_state_rp_wc(action_list); output_state_rp_sg(action_list); output_state_rp_sg_rpt(action_list); output_state_rp_mfc(action_list); output_state_mrib_rp_rp(action_list); output_state_mrib_rp_wc(action_list); output_state_mrib_rp_sg(action_list); output_state_mrib_rp_sg_rpt(action_list); output_state_mrib_s_sg(action_list); output_state_mrib_s_sg_rpt(action_list); output_state_is_join_desired_rp(action_list); output_state_is_join_desired_wc(action_list); output_state_is_join_desired_sg(action_list); output_state_is_prune_desired_sg_rpt(action_list); output_state_is_prune_desired_sg_rpt_sg(action_list); output_state_is_rpt_join_desired_g(action_list); output_state_inherited_olist_sg_rpt(action_list); output_state_iif_olist_mfc(action_list); output_state_monitoring_switch_to_spt_desired_mfc(action_list); output_state_spt_switch_threshold_changed_mfc(action_list); output_state_is_directly_connected_sg(action_list); output_state_is_could_register_sg(action_list); output_state_assert_tracking_desired_sg(action_list); output_state_assert_tracking_desired_wc(action_list); output_state_could_assert_sg(action_list); output_state_could_assert_wc(action_list); output_state_my_assert_metric_sg(action_list); output_state_my_assert_metric_wc(action_list); output_state_assert_rpf_interface_sg(action_list); output_state_assert_rpf_interface_wc(action_list); output_state_assert_receive_join_sg(action_list); output_state_assert_receive_join_wc(action_list); output_state_assert_winner_nbr_sg_gen_id(action_list); output_state_assert_winner_nbr_wc_gen_id(action_list); output_state_assert_winner_nbr_sg_nlt(action_list); output_state_assert_winner_nbr_wc_nlt(action_list); output_state_receive_join_wc_by_sg_rpt(action_list); output_state_receive_end_of_message_sg_rpt(action_list); output_state_sg_see_prune_wc(action_list); output_state_rpfp_nbr_wc_assert(action_list); output_state_rpfp_nbr_wc_not_assert(action_list); output_state_rpfp_nbr_wc_gen_id(action_list); output_state_rpfp_nbr_sg_assert(action_list); output_state_rpfp_nbr_sg_not_assert(action_list); output_state_rpfp_nbr_sg_gen_id(action_list); output_state_rpfp_nbr_sg_rpt(action_list); output_state_rpfp_nbr_sg_rpt_sg(action_list); output_state_nbr_mrib_next_hop_rp_rp(action_list); output_state_nbr_mrib_next_hop_rp_wc(action_list); output_state_nbr_mrib_next_hop_rp_gen_id(action_list); output_state_nbr_mrib_next_hop_s(action_list); output_state_out_start_vif_rp(action_list); output_state_out_start_vif_wc(action_list); output_state_out_start_vif_sg(action_list); output_state_out_start_vif_sg_rpt(action_list); output_state_out_stop_vif_rp(action_list); output_state_out_stop_vif_wc(action_list); output_state_out_stop_vif_sg(action_list); output_state_out_stop_vif_sg_rpt(action_list); output_state_out_add_pim_mre_rp_entry_rp(action_list); output_state_out_add_pim_mre_rp_entry_wc(action_list); output_state_out_add_pim_mre_rp_entry_sg(action_list); output_state_out_add_pim_mre_rp_entry_sg_rpt(action_list); output_state_out_add_pim_mre_wc_entry_wc(action_list); output_state_out_add_pim_mre_wc_entry_sg(action_list); output_state_out_add_pim_mre_wc_entry_sg_rpt(action_list); output_state_out_add_pim_mre_sg_entry_sg(action_list); output_state_out_add_pim_mre_sg_entry_sg_rpt(action_list); output_state_out_add_pim_mre_sg_rpt_entry_sg(action_list); output_state_out_add_pim_mre_sg_rpt_entry_sg_rpt(action_list); output_state_out_remove_pim_mre_rp_entry_rp(action_list); output_state_out_remove_pim_mre_rp_entry_wc(action_list); output_state_out_remove_pim_mre_rp_entry_sg(action_list); output_state_out_remove_pim_mre_rp_entry_sg_rpt(action_list); output_state_out_remove_pim_mre_wc_entry_wc(action_list); output_state_out_remove_pim_mre_wc_entry_sg(action_list); output_state_out_remove_pim_mre_wc_entry_sg_rpt(action_list); output_state_out_remove_pim_mre_sg_entry_sg(action_list); output_state_out_remove_pim_mre_sg_entry_sg_rpt(action_list); output_state_out_remove_pim_mre_sg_rpt_entry_sg(action_list); output_state_out_remove_pim_mre_sg_rpt_entry_sg_rpt(action_list); output_state_out_remove_pim_mfc_entry_mfc(action_list); output_state_update_sptbit_mfc(action_list); output_state_set_keepalive_timer_sg(action_list); // // Create the final result // for (int i = 0; i < INPUT_STATE_MAX; i++) { input_state_t input_state = static_cast(i); list action_list; action_list = _action_lists[input_state].compute_action_list(); action_list = remove_state(action_list); _action_lists[input_state].clear(); list::iterator iter; for (iter = action_list.begin(); iter != action_list.end(); ++iter) { PimMreAction action = *iter; add_action(input_state, action); } } } PimNode* PimMreTrackState::pim_node() const { return _pim_mrt->pim_node(); } int PimMreTrackState::family() const { return (_pim_mrt->family()); } // Return %XORP_OK if the action list has been added, otherwise return %XORP_ERROR. int PimMreTrackState::add_action_list(input_state_t input_state, list action_list) { int ret_value = XORP_ERROR; if (input_state >= INPUT_STATE_MAX) return (XORP_ERROR); _action_lists[input_state].add_action_list(action_list); ret_value = XORP_OK; return (ret_value); } void PimMreTrackState::ActionLists::clear() { _action_list_vector.clear(); } void PimMreTrackState::ActionLists::add_action_list(list action_list) { _action_list_vector.push_back(action_list); } list PimMreTrackState::ActionLists::compute_action_list() { list action_list; // // Remove the duplicates (that follow one-after another), if any, // and reverse the action ordering. // XXX: by definition, there snoudn't be any duplicates, but just // in case we try to remove them anyway (it doesn't cost us anything). // for (size_t i = 0; i < _action_list_vector.size(); i++) { list& l = _action_list_vector[i]; l.erase(unique(l.begin(), l.end()), l.end()); #ifdef XORP_USE_USTL // uSTL doesn't do reverse() list tmp; list::iterator q = l.begin(); while (q != l.end()) { tmp.push_front(*q); q++; } l = tmp; #else l.reverse(); // XXX #endif } // // Get the actions // do { PimMreAction action = pop_next_action(); if (action.output_state() == OUTPUT_STATE_MAX) break; action_list.push_back(action); } while (true); // // Check if all actions were used // for (size_t i = 0; i < _action_list_vector.size(); i++) { list& l = _action_list_vector[i]; if (! l.empty()) { XLOG_FATAL("PimMreTrackState machinery: incomplete action set"); exit (1); } } return (action_list); } // // Return an action that is only in the head of one or more lists // PimMreAction PimMreTrackState::ActionLists::pop_next_action() { PimMreAction invalid_action(OUTPUT_STATE_MAX, PIM_MRE_RP); for (size_t i = 0; i < _action_list_vector.size(); i++) { list& l = _action_list_vector[i]; if (l.empty()) continue; PimMreAction action = l.front(); if (! is_head_only_action(action)) continue; // // Found an action. Remove it from the head of all lists // for (size_t j = 0; j < _action_list_vector.size(); j++) { list& l2 = _action_list_vector[j]; if (l2.empty()) continue; PimMreAction action2 = l2.front(); if (action == action2) l2.pop_front(); } return (action); } return (invalid_action); } // // Test if this action is only in the head of some lists // bool PimMreTrackState::ActionLists::is_head_only_action(const PimMreAction& action) const { for (size_t i = 0; i < _action_list_vector.size(); i++) { const list& l = _action_list_vector[i]; if (l.size() <= 1) continue; list::const_iterator iter; iter = l.begin(); ++iter; if (find(iter, l.end(), action) != l.end()) return (false); } return (true); } // Return %XORP_OK if the action has been added, otherwise return %XORP_ERROR. int PimMreTrackState::add_action(input_state_t input_state, const PimMreAction& action) { int ret_value = XORP_ERROR; if (input_state >= INPUT_STATE_MAX) return (XORP_ERROR); if (action.is_sg() || action.is_sg_rpt()) { // (S,G) or (S,G,rpt) action list& action_list = _output_action_sg_sg_rpt[input_state]; if (! can_add_action_to_list(action_list, action)) { return (XORP_ERROR); } action_list.push_back(action); ret_value = XORP_OK; } if (action.is_wc()) { // (*,G) action list& action_list = _output_action_wc[input_state]; if (! can_add_action_to_list(action_list, action)) { return (XORP_ERROR); } action_list.push_back(action); ret_value = XORP_OK; } if (action.is_rp()) { // (*,*,RP) action list& action_list = _output_action_rp[input_state]; if (! can_add_action_to_list(action_list, action)) { return (XORP_ERROR); } action_list.push_back(action); ret_value = XORP_OK; } if (action.is_mfc()) { // MFC action list& action_list = _output_action_mfc[input_state]; if (! can_add_action_to_list(action_list, action)) { return (XORP_ERROR); } action_list.push_back(action); ret_value = XORP_OK; } if (ret_value == XORP_OK) { // Rebuild the list of all actions: // - The (*,*,RP) actions are first. // - The (*,G) actions follow the (*,*,RP) actions. // - The (S,G) and (S,G,rpt) actions (in order of appearance) // follow the (*,G) actions. _output_action[input_state].clear(); _output_action[input_state].insert( _output_action[input_state].end(), _output_action_rp[input_state].begin(), _output_action_rp[input_state].end()); _output_action[input_state].insert( _output_action[input_state].end(), _output_action_wc[input_state].begin(), _output_action_wc[input_state].end()); _output_action[input_state].insert( _output_action[input_state].end(), _output_action_sg_sg_rpt[input_state].begin(), _output_action_sg_sg_rpt[input_state].end()); _output_action[input_state].insert( _output_action[input_state].end(), _output_action_mfc[input_state].begin(), _output_action_mfc[input_state].end()); } return (ret_value); } // Return true if the action can be added to the list, otherwise return false bool PimMreTrackState::can_add_action_to_list(const list& action_list, const PimMreAction& action) const { if (action_list.empty()) return (true); const PimMreAction& last_action = action_list.back(); if (last_action != action) return (true); return (false); } /** * PimMreTrackState::remove_action_from_list: * @action_list: The list of actions. * @keep_action: The action that defines whether @remove_action should be * removed. * @remove_action: The action to remove if it occurs after @keep_action. * * Remove action @remove action from the list of actions @action_list, * but only if action @keep_action is placed earlier on that list. * If necessary, each occurance of @remove_action is removed. * * Return value: The modified list after/if @remove_action is removed. **/ list PimMreTrackState::remove_action_from_list(list action_list, PimMreAction keep_action, PimMreAction remove_action) { list::iterator iter; iter = find(action_list.begin(), action_list.end(), keep_action); if (iter == action_list.end()) return (action_list); do { list::iterator iter2; iter2 = find(iter, action_list.end(), remove_action); if (iter2 == action_list.end()) break; action_list.erase(iter2); } while (true); return (action_list); } void PimMreTrackState::print_actions_name() const { vector input_state_names(INPUT_STATE_MAX); vector output_state_names(OUTPUT_STATE_MAX); #define INPUT_NAME(enum_name) \ do { \ input_state_names[enum_name] = #enum_name; \ } while (false) #define OUTPUT_NAME(enum_name) \ do { \ output_state_names[enum_name] = #enum_name; \ } while (false) INPUT_NAME(INPUT_STATE_RP_CHANGED); // 0 INPUT_NAME(INPUT_STATE_MRIB_RP_CHANGED); // 1 INPUT_NAME(INPUT_STATE_MRIB_S_CHANGED); // 2 INPUT_NAME(INPUT_STATE_NBR_MRIB_NEXT_HOP_RP_CHANGED); // 3 INPUT_NAME(INPUT_STATE_NBR_MRIB_NEXT_HOP_RP_GEN_ID_CHANGED); // 4 INPUT_NAME(INPUT_STATE_NBR_MRIB_NEXT_HOP_RP_G_CHANGED); // 5 INPUT_NAME(INPUT_STATE_NBR_MRIB_NEXT_HOP_S_CHANGED); // 6 INPUT_NAME(INPUT_STATE_RPFP_NBR_WC_CHANGED); // 7 INPUT_NAME(INPUT_STATE_RPFP_NBR_WC_GEN_ID_CHANGED); // 8 INPUT_NAME(INPUT_STATE_RPFP_NBR_SG_CHANGED); // 9 INPUT_NAME(INPUT_STATE_RPFP_NBR_SG_GEN_ID_CHANGED); // 10 INPUT_NAME(INPUT_STATE_RPFP_NBR_SG_RPT_CHANGED); // 11 INPUT_NAME(INPUT_STATE_RECEIVE_JOIN_RP); // 12 INPUT_NAME(INPUT_STATE_RECEIVE_JOIN_WC); // 13 INPUT_NAME(INPUT_STATE_RECEIVE_JOIN_SG); // 14 INPUT_NAME(INPUT_STATE_RECEIVE_JOIN_SG_RPT); // 15 INPUT_NAME(INPUT_STATE_RECEIVE_PRUNE_RP); // 16 INPUT_NAME(INPUT_STATE_RECEIVE_PRUNE_WC); // 17 INPUT_NAME(INPUT_STATE_RECEIVE_PRUNE_SG); // 18 INPUT_NAME(INPUT_STATE_RECEIVE_PRUNE_SG_RPT); // 19 INPUT_NAME(INPUT_STATE_RECEIVE_END_OF_MESSAGE_SG_RPT); // 20 INPUT_NAME(INPUT_STATE_SEE_PRUNE_WC); // 21 INPUT_NAME(INPUT_STATE_DOWNSTREAM_JP_STATE_RP); // 22 INPUT_NAME(INPUT_STATE_DOWNSTREAM_JP_STATE_WC); // 23 INPUT_NAME(INPUT_STATE_DOWNSTREAM_JP_STATE_SG); // 24 INPUT_NAME(INPUT_STATE_DOWNSTREAM_JP_STATE_SG_RPT); // 25 INPUT_NAME(INPUT_STATE_UPSTREAM_JP_STATE_SG); // 26 INPUT_NAME(INPUT_STATE_LOCAL_RECEIVER_INCLUDE_WC); // 27 INPUT_NAME(INPUT_STATE_LOCAL_RECEIVER_INCLUDE_SG); // 28 INPUT_NAME(INPUT_STATE_LOCAL_RECEIVER_EXCLUDE_SG); // 29 INPUT_NAME(INPUT_STATE_ASSERT_STATE_WC); // 30 INPUT_NAME(INPUT_STATE_ASSERT_STATE_SG); // 31 INPUT_NAME(INPUT_STATE_ASSERT_WINNER_NBR_WC_GEN_ID_CHANGED); // 32 INPUT_NAME(INPUT_STATE_ASSERT_WINNER_NBR_SG_GEN_ID_CHANGED); // 33 INPUT_NAME(INPUT_STATE_ASSERT_WINNER_NBR_WC_NLT_EXPIRED); // 34 INPUT_NAME(INPUT_STATE_ASSERT_WINNER_NBR_SG_NLT_EXPIRED); // 35 INPUT_NAME(INPUT_STATE_ASSERT_RPF_INTERFACE_WC_CHANGED); // 36 INPUT_NAME(INPUT_STATE_ASSERT_RPF_INTERFACE_SG_CHANGED); // 37 INPUT_NAME(INPUT_STATE_I_AM_DR); // 38 INPUT_NAME(INPUT_STATE_MY_IP_ADDRESS); // 39 INPUT_NAME(INPUT_STATE_MY_IP_SUBNET_ADDRESS); // 40 INPUT_NAME(INPUT_STATE_SPT_SWITCH_THRESHOLD_CHANGED_MFC); // 41 INPUT_NAME(INPUT_STATE_WAS_SWITCH_TO_SPT_DESIRED_SG); // 42 INPUT_NAME(INPUT_STATE_KEEPALIVE_TIMER_SG); // 43 INPUT_NAME(INPUT_STATE_SPTBIT_SG); // 44 INPUT_NAME(INPUT_STATE_IN_START_VIF); // 45 INPUT_NAME(INPUT_STATE_IN_STOP_VIF); // 46 INPUT_NAME(INPUT_STATE_IN_ADD_PIM_MRE_RP); // 47 INPUT_NAME(INPUT_STATE_IN_ADD_PIM_MRE_WC); // 48 INPUT_NAME(INPUT_STATE_IN_ADD_PIM_MRE_SG); // 49 INPUT_NAME(INPUT_STATE_IN_ADD_PIM_MRE_SG_RPT); // 50 INPUT_NAME(INPUT_STATE_IN_REMOVE_PIM_MRE_RP); // 51 INPUT_NAME(INPUT_STATE_IN_REMOVE_PIM_MRE_WC); // 52 INPUT_NAME(INPUT_STATE_IN_REMOVE_PIM_MRE_SG); // 53 INPUT_NAME(INPUT_STATE_IN_REMOVE_PIM_MRE_SG_RPT); // 54 INPUT_NAME(INPUT_STATE_IN_REMOVE_PIM_MFC); // 55 INPUT_NAME(INPUT_STATE_IN_REMOVE_MISC); // 56 OUTPUT_NAME(OUTPUT_STATE_RP_WC); // 0 OUTPUT_NAME(OUTPUT_STATE_RP_SG); // 1 OUTPUT_NAME(OUTPUT_STATE_RP_SG_RPT); // 2 OUTPUT_NAME(OUTPUT_STATE_RP_MFC); // 3 OUTPUT_NAME(OUTPUT_STATE_MRIB_RP_RP); // 4 OUTPUT_NAME(OUTPUT_STATE_MRIB_RP_WC); // 5 OUTPUT_NAME(OUTPUT_STATE_MRIB_RP_SG); // 6 OUTPUT_NAME(OUTPUT_STATE_MRIB_RP_SG_RPT); // 7 OUTPUT_NAME(OUTPUT_STATE_MRIB_S_SG); // 8 OUTPUT_NAME(OUTPUT_STATE_MRIB_S_SG_RPT); // 9 OUTPUT_NAME(OUTPUT_STATE_IS_JOIN_DESIRED_RP); // 10 OUTPUT_NAME(OUTPUT_STATE_IS_JOIN_DESIRED_WC); // 11 OUTPUT_NAME(OUTPUT_STATE_IS_JOIN_DESIRED_SG); // 12 OUTPUT_NAME(OUTPUT_STATE_IS_PRUNE_DESIRED_SG_RPT); // 13 OUTPUT_NAME(OUTPUT_STATE_IS_PRUNE_DESIRED_SG_RPT_SG); // 14 OUTPUT_NAME(OUTPUT_STATE_IS_RPT_JOIN_DESIRED_G); // 15 OUTPUT_NAME(OUTPUT_STATE_INHERITED_OLIST_SG_RPT); // 16 OUTPUT_NAME(OUTPUT_STATE_IIF_OLIST_MFC); // 17 OUTPUT_NAME(OUTPUT_STATE_MONITORING_SWITCH_TO_SPT_DESIRED_MFC); // 18 OUTPUT_NAME(OUTPUT_STATE_SPT_SWITCH_THRESHOLD_CHANGED_MFC); // 19 OUTPUT_NAME(OUTPUT_STATE_IS_DIRECTLY_CONNECTED_SG); // 20 OUTPUT_NAME(OUTPUT_STATE_IS_COULD_REGISTER_SG); // 21 OUTPUT_NAME(OUTPUT_STATE_ASSERT_TRACKING_DESIRED_SG); // 22 OUTPUT_NAME(OUTPUT_STATE_ASSERT_TRACKING_DESIRED_WC); // 23 OUTPUT_NAME(OUTPUT_STATE_COULD_ASSERT_SG); // 24 OUTPUT_NAME(OUTPUT_STATE_COULD_ASSERT_WC); // 25 OUTPUT_NAME(OUTPUT_STATE_MY_ASSERT_METRIC_SG); // 26 OUTPUT_NAME(OUTPUT_STATE_MY_ASSERT_METRIC_WC); // 27 OUTPUT_NAME(OUTPUT_STATE_ASSERT_RPF_INTERFACE_SG); // 28 OUTPUT_NAME(OUTPUT_STATE_ASSERT_RPF_INTERFACE_WC); // 29 OUTPUT_NAME(OUTPUT_STATE_ASSERT_RECEIVE_JOIN_SG); // 30 OUTPUT_NAME(OUTPUT_STATE_ASSERT_RECEIVE_JOIN_WC); // 31 OUTPUT_NAME(OUTPUT_STATE_ASSERT_WINNER_NBR_SG_GEN_ID); // 32 OUTPUT_NAME(OUTPUT_STATE_ASSERT_WINNER_NBR_WC_GEN_ID); // 33 OUTPUT_NAME(OUTPUT_STATE_ASSERT_WINNER_NBR_SG_NLT); // 34 OUTPUT_NAME(OUTPUT_STATE_ASSERT_WINNER_NBR_WC_NLT); // 35 OUTPUT_NAME(OUTPUT_STATE_RECEIVE_JOIN_WC_BY_SG_RPT); // 36 OUTPUT_NAME(OUTPUT_STATE_RECEIVE_END_OF_MESSAGE_SG_RPT); // 37 OUTPUT_NAME(OUTPUT_STATE_SG_SEE_PRUNE_WC); // 38 OUTPUT_NAME(OUTPUT_STATE_RPFP_NBR_WC_ASSERT); // 39 OUTPUT_NAME(OUTPUT_STATE_RPFP_NBR_WC_NOT_ASSERT); // 40 OUTPUT_NAME(OUTPUT_STATE_RPFP_NBR_WC_GEN_ID); // 41 OUTPUT_NAME(OUTPUT_STATE_RPFP_NBR_SG_ASSERT); // 42 OUTPUT_NAME(OUTPUT_STATE_RPFP_NBR_SG_NOT_ASSERT); // 43 OUTPUT_NAME(OUTPUT_STATE_RPFP_NBR_SG_GEN_ID); // 44 OUTPUT_NAME(OUTPUT_STATE_RPFP_NBR_SG_RPT); // 45 OUTPUT_NAME(OUTPUT_STATE_RPFP_NBR_SG_RPT_SG); // 46 OUTPUT_NAME(OUTPUT_STATE_NBR_MRIB_NEXT_HOP_RP_RP); // 47 OUTPUT_NAME(OUTPUT_STATE_NBR_MRIB_NEXT_HOP_RP_WC); // 48 OUTPUT_NAME(OUTPUT_STATE_NBR_MRIB_NEXT_HOP_RP_GEN_ID); // 49 OUTPUT_NAME(OUTPUT_STATE_NBR_MRIB_NEXT_HOP_S); // 50 OUTPUT_NAME(OUTPUT_STATE_OUT_START_VIF_RP); // 51 OUTPUT_NAME(OUTPUT_STATE_OUT_START_VIF_WC); // 52 OUTPUT_NAME(OUTPUT_STATE_OUT_START_VIF_SG); // 53 OUTPUT_NAME(OUTPUT_STATE_OUT_START_VIF_SG_RPT); // 54 OUTPUT_NAME(OUTPUT_STATE_OUT_STOP_VIF_RP); // 55 OUTPUT_NAME(OUTPUT_STATE_OUT_STOP_VIF_WC); // 56 OUTPUT_NAME(OUTPUT_STATE_OUT_STOP_VIF_SG); // 57 OUTPUT_NAME(OUTPUT_STATE_OUT_STOP_VIF_SG_RPT); // 58 OUTPUT_NAME(OUTPUT_STATE_OUT_ADD_PIM_MRE_RP_ENTRY_RP); // 59 OUTPUT_NAME(OUTPUT_STATE_OUT_ADD_PIM_MRE_RP_ENTRY_WC); // 60 OUTPUT_NAME(OUTPUT_STATE_OUT_ADD_PIM_MRE_RP_ENTRY_SG); // 61 OUTPUT_NAME(OUTPUT_STATE_OUT_ADD_PIM_MRE_RP_ENTRY_SG_RPT); // 62 OUTPUT_NAME(OUTPUT_STATE_OUT_ADD_PIM_MRE_WC_ENTRY_WC); // 63 OUTPUT_NAME(OUTPUT_STATE_OUT_ADD_PIM_MRE_WC_ENTRY_SG); // 64 OUTPUT_NAME(OUTPUT_STATE_OUT_ADD_PIM_MRE_WC_ENTRY_SG_RPT); // 65 OUTPUT_NAME(OUTPUT_STATE_OUT_ADD_PIM_MRE_SG_ENTRY_SG); // 66 OUTPUT_NAME(OUTPUT_STATE_OUT_ADD_PIM_MRE_SG_ENTRY_SG_RPT); // 67 OUTPUT_NAME(OUTPUT_STATE_OUT_ADD_PIM_MRE_SG_RPT_ENTRY_SG); // 68 OUTPUT_NAME(OUTPUT_STATE_OUT_ADD_PIM_MRE_SG_RPT_ENTRY_SG_RPT); // 69 OUTPUT_NAME(OUTPUT_STATE_OUT_REMOVE_PIM_MRE_RP_ENTRY_RP); // 70 OUTPUT_NAME(OUTPUT_STATE_OUT_REMOVE_PIM_MRE_RP_ENTRY_WC); // 71 OUTPUT_NAME(OUTPUT_STATE_OUT_REMOVE_PIM_MRE_RP_ENTRY_SG); // 72 OUTPUT_NAME(OUTPUT_STATE_OUT_REMOVE_PIM_MRE_RP_ENTRY_SG_RPT); // 73 OUTPUT_NAME(OUTPUT_STATE_OUT_REMOVE_PIM_MRE_WC_ENTRY_WC); // 74 OUTPUT_NAME(OUTPUT_STATE_OUT_REMOVE_PIM_MRE_WC_ENTRY_SG); // 75 OUTPUT_NAME(OUTPUT_STATE_OUT_REMOVE_PIM_MRE_WC_ENTRY_SG_RPT); // 76 OUTPUT_NAME(OUTPUT_STATE_OUT_REMOVE_PIM_MRE_SG_ENTRY_SG); // 77 OUTPUT_NAME(OUTPUT_STATE_OUT_REMOVE_PIM_MRE_SG_ENTRY_SG_RPT); // 78 OUTPUT_NAME(OUTPUT_STATE_OUT_REMOVE_PIM_MRE_SG_RPT_ENTRY_SG); // 79 OUTPUT_NAME(OUTPUT_STATE_OUT_REMOVE_PIM_MRE_SG_RPT_ENTRY_SG_RPT); // 80 OUTPUT_NAME(OUTPUT_STATE_OUT_REMOVE_PIM_MFC_ENTRY_MFC); // 81 OUTPUT_NAME(OUTPUT_STATE_UPDATE_SPTBIT_MFC); // 82 OUTPUT_NAME(OUTPUT_STATE_SET_KEEPALIVE_TIMER_SG); // 83 #undef INPUT_NAME #undef OUTPUT_NAME for (size_t i = 0; i < INPUT_STATE_MAX; i++) { list::const_iterator iter; printf("Input = %s\n", input_state_names[i].c_str()); for (iter = _output_action[i].begin(); iter != _output_action[i].end(); ++iter) { const PimMreAction& action = *iter; string entry_type_str = "UnknownEntryType"; do { if (action.is_sg()) { entry_type_str = "(S,G)"; break; } if (action.is_sg_rpt()) { entry_type_str = "(S,G,rpt)"; break; } if (action.is_wc()) { entry_type_str = "(*,G)"; break; } if (action.is_rp()) { entry_type_str = "(*,*,RP)"; break; } if (action.is_mfc()) { entry_type_str = "(MFC)"; break; } } while (false); printf("%8s%s%*s\n", "", output_state_names[action.output_state()].c_str(), (int)(80 - 8 - output_state_names[action.output_state()].size() - 5), entry_type_str.c_str()); } printf("\n"); } } void PimMreTrackState::print_actions_num() const { for (size_t i = 0; i < INPUT_STATE_MAX; i++) { list::const_iterator iter; printf("Input action = %u Output actions =", XORP_UINT_CAST(i)); for (iter = _output_action[i].begin(); iter != _output_action[i].end(); ++iter) { const PimMreAction& action = *iter; string entry_type_str = "UnknownEntryType"; do { if (action.is_sg()) { entry_type_str = "(S,G)"; break; } if (action.is_sg_rpt()) { entry_type_str = "(S,G,rpt)"; break; } if (action.is_wc()) { entry_type_str = "(*,G)"; break; } if (action.is_rp()) { entry_type_str = "(*,*,RP)"; break; } if (action.is_mfc()) { entry_type_str = "(MFC)"; break; } } while (false); printf(" %d/%s", action.output_state(), entry_type_str.c_str()); } printf("\n"); } } // // Remove state methods // // // The parent method that removes all extra state // list PimMreTrackState::remove_state(list action_list) { // // Remove all extra states // action_list = remove_state_rpfp_nbr_wc_not_assert_changed(action_list); action_list = remove_state_rpfp_nbr_sg_not_assert_changed(action_list); return (action_list); } list PimMreTrackState::remove_state_rpfp_nbr_wc_not_assert_changed(list action_list) { PimMreAction keep_action(OUTPUT_STATE_RPFP_NBR_WC_NOT_ASSERT, PIM_MRE_WC); PimMreAction remove_action(OUTPUT_STATE_RPFP_NBR_WC_ASSERT, PIM_MRE_WC); return (remove_action_from_list(action_list, keep_action, remove_action)); } list PimMreTrackState::remove_state_rpfp_nbr_sg_not_assert_changed(list action_list) { PimMreAction keep_action(OUTPUT_STATE_RPFP_NBR_SG_NOT_ASSERT, PIM_MRE_SG); PimMreAction remove_action(OUTPUT_STATE_RPFP_NBR_SG_ASSERT, PIM_MRE_SG); return (remove_action_from_list(action_list, keep_action, remove_action)); } // // Input state methods // void PimMreTrackState::input_state_rp_changed(list action_list) { add_action_list(INPUT_STATE_RP_CHANGED, action_list); } void PimMreTrackState::input_state_mrib_rp_changed(list action_list) { add_action_list(INPUT_STATE_MRIB_RP_CHANGED, action_list); } void PimMreTrackState::input_state_mrib_s_changed(list action_list) { add_action_list(INPUT_STATE_MRIB_S_CHANGED, action_list); } void PimMreTrackState::input_state_nbr_mrib_next_hop_rp_changed(list action_list) { add_action_list(INPUT_STATE_NBR_MRIB_NEXT_HOP_RP_CHANGED, action_list); } void PimMreTrackState::input_state_nbr_mrib_next_hop_rp_gen_id_changed(list action_list) { add_action_list(INPUT_STATE_NBR_MRIB_NEXT_HOP_RP_GEN_ID_CHANGED, action_list); } void PimMreTrackState::input_state_nbr_mrib_next_hop_rp_g_changed(list action_list) { add_action_list(INPUT_STATE_NBR_MRIB_NEXT_HOP_RP_G_CHANGED, action_list); } void PimMreTrackState::input_state_nbr_mrib_next_hop_s_changed(list action_list) { add_action_list(INPUT_STATE_NBR_MRIB_NEXT_HOP_S_CHANGED, action_list); } void PimMreTrackState::input_state_rpfp_nbr_wc_changed(list action_list) { add_action_list(INPUT_STATE_RPFP_NBR_WC_CHANGED, action_list); } void PimMreTrackState::input_state_rpfp_nbr_wc_gen_id_changed(list action_list) { add_action_list(INPUT_STATE_RPFP_NBR_WC_GEN_ID_CHANGED, action_list); } void PimMreTrackState::input_state_rpfp_nbr_sg_changed(list action_list) { add_action_list(INPUT_STATE_RPFP_NBR_SG_CHANGED, action_list); } void PimMreTrackState::input_state_rpfp_nbr_sg_gen_id_changed(list action_list) { add_action_list(INPUT_STATE_RPFP_NBR_SG_GEN_ID_CHANGED, action_list); } void PimMreTrackState::input_state_rpfp_nbr_sg_rpt_changed(list action_list) { add_action_list(INPUT_STATE_RPFP_NBR_SG_RPT_CHANGED, action_list); } void PimMreTrackState::input_state_receive_join_rp(list action_list) { add_action_list(INPUT_STATE_RECEIVE_JOIN_RP, action_list); } void PimMreTrackState::input_state_receive_join_wc(list action_list) { add_action_list(INPUT_STATE_RECEIVE_JOIN_WC, action_list); } void PimMreTrackState::input_state_receive_join_sg(list action_list) { add_action_list(INPUT_STATE_RECEIVE_JOIN_SG, action_list); } void PimMreTrackState::input_state_receive_join_sg_rpt(list action_list) { add_action_list(INPUT_STATE_RECEIVE_JOIN_SG_RPT, action_list); } void PimMreTrackState::input_state_receive_prune_rp(list action_list) { add_action_list(INPUT_STATE_RECEIVE_PRUNE_RP, action_list); } void PimMreTrackState::input_state_receive_prune_wc(list action_list) { add_action_list(INPUT_STATE_RECEIVE_PRUNE_WC, action_list); } void PimMreTrackState::input_state_receive_prune_sg(list action_list) { add_action_list(INPUT_STATE_RECEIVE_PRUNE_SG, action_list); } void PimMreTrackState::input_state_receive_prune_sg_rpt(list action_list) { add_action_list(INPUT_STATE_RECEIVE_PRUNE_SG_RPT, action_list); } void PimMreTrackState::input_state_receive_end_of_message_sg_rpt(list action_list) { add_action_list(INPUT_STATE_RECEIVE_END_OF_MESSAGE_SG_RPT, action_list); } void PimMreTrackState::input_state_see_prune_wc(list action_list) { add_action_list(INPUT_STATE_SEE_PRUNE_WC, action_list); } void PimMreTrackState::input_state_downstream_jp_state_rp(list action_list) { add_action_list(INPUT_STATE_DOWNSTREAM_JP_STATE_RP, action_list); } void PimMreTrackState::input_state_downstream_jp_state_wc(list action_list) { add_action_list(INPUT_STATE_DOWNSTREAM_JP_STATE_WC, action_list); } void PimMreTrackState::input_state_downstream_jp_state_sg(list action_list) { add_action_list(INPUT_STATE_DOWNSTREAM_JP_STATE_SG, action_list); } void PimMreTrackState::input_state_downstream_jp_state_sg_rpt(list action_list) { add_action_list(INPUT_STATE_DOWNSTREAM_JP_STATE_SG_RPT, action_list); } void PimMreTrackState::input_state_upstream_jp_state_sg(list action_list) { add_action_list(INPUT_STATE_UPSTREAM_JP_STATE_SG, action_list); } void PimMreTrackState::input_state_local_receiver_include_wc(list action_list) { add_action_list(INPUT_STATE_LOCAL_RECEIVER_INCLUDE_WC, action_list); } void PimMreTrackState::input_state_local_receiver_include_sg(list action_list) { add_action_list(INPUT_STATE_LOCAL_RECEIVER_INCLUDE_SG, action_list); } void PimMreTrackState::input_state_local_receiver_exclude_sg(list action_list) { add_action_list(INPUT_STATE_LOCAL_RECEIVER_EXCLUDE_SG, action_list); } void PimMreTrackState::input_state_assert_state_wc(list action_list) { add_action_list(INPUT_STATE_ASSERT_STATE_WC, action_list); } void PimMreTrackState::input_state_assert_state_sg(list action_list) { add_action_list(INPUT_STATE_ASSERT_STATE_SG, action_list); } void PimMreTrackState::input_state_assert_winner_nbr_wc_gen_id_changed(list action_list) { add_action_list(INPUT_STATE_ASSERT_WINNER_NBR_WC_GEN_ID_CHANGED, action_list); } void PimMreTrackState::input_state_assert_winner_nbr_sg_gen_id_changed(list action_list) { add_action_list(INPUT_STATE_ASSERT_WINNER_NBR_SG_GEN_ID_CHANGED, action_list); } void PimMreTrackState::input_state_assert_winner_nbr_wc_nlt_expired(list action_list) { add_action_list(INPUT_STATE_ASSERT_WINNER_NBR_WC_NLT_EXPIRED, action_list); } void PimMreTrackState::input_state_assert_winner_nbr_sg_nlt_expired(list action_list) { add_action_list(INPUT_STATE_ASSERT_WINNER_NBR_SG_NLT_EXPIRED, action_list); } void PimMreTrackState::input_state_assert_rpf_interface_wc_changed(list action_list) { add_action_list(INPUT_STATE_ASSERT_RPF_INTERFACE_WC_CHANGED, action_list); } void PimMreTrackState::input_state_assert_rpf_interface_sg_changed(list action_list) { add_action_list(INPUT_STATE_ASSERT_RPF_INTERFACE_SG_CHANGED, action_list); } void PimMreTrackState::input_state_i_am_dr(list action_list) { add_action_list(INPUT_STATE_I_AM_DR, action_list); } void PimMreTrackState::input_state_my_ip_address(list action_list) { add_action_list(INPUT_STATE_MY_IP_ADDRESS, action_list); } void PimMreTrackState::input_state_my_ip_subnet_address(list action_list) { add_action_list(INPUT_STATE_MY_IP_SUBNET_ADDRESS, action_list); } void PimMreTrackState::input_state_spt_switch_threshold_changed_mfc(list action_list) { add_action_list(INPUT_STATE_SPT_SWITCH_THRESHOLD_CHANGED_MFC, action_list); } void PimMreTrackState::input_state_was_switch_to_spt_desired_sg(list action_list) { add_action_list(INPUT_STATE_WAS_SWITCH_TO_SPT_DESIRED_SG, action_list); } void PimMreTrackState::input_state_keepalive_timer_sg(list action_list) { add_action_list(INPUT_STATE_KEEPALIVE_TIMER_SG, action_list); } void PimMreTrackState::input_state_sptbit_sg(list action_list) { add_action_list(INPUT_STATE_SPTBIT_SG, action_list); } void PimMreTrackState::input_state_in_start_vif(list action_list) { add_action_list(INPUT_STATE_IN_START_VIF, action_list); } void PimMreTrackState::input_state_in_stop_vif(list action_list) { add_action_list(INPUT_STATE_IN_STOP_VIF, action_list); } void PimMreTrackState::input_state_in_add_pim_mre_rp(list action_list) { add_action_list(INPUT_STATE_IN_ADD_PIM_MRE_RP, action_list); } void PimMreTrackState::input_state_in_add_pim_mre_wc(list action_list) { add_action_list(INPUT_STATE_IN_ADD_PIM_MRE_WC, action_list); } void PimMreTrackState::input_state_in_add_pim_mre_sg(list action_list) { add_action_list(INPUT_STATE_IN_ADD_PIM_MRE_SG, action_list); } void PimMreTrackState::input_state_in_add_pim_mre_sg_rpt(list action_list) { add_action_list(INPUT_STATE_IN_ADD_PIM_MRE_SG_RPT, action_list); } void PimMreTrackState::input_state_in_remove_pim_mre_rp(list action_list) { add_action_list(INPUT_STATE_IN_REMOVE_PIM_MRE_RP, action_list); } void PimMreTrackState::input_state_in_remove_pim_mre_wc(list action_list) { add_action_list(INPUT_STATE_IN_REMOVE_PIM_MRE_WC, action_list); } void PimMreTrackState::input_state_in_remove_pim_mre_sg(list action_list) { add_action_list(INPUT_STATE_IN_REMOVE_PIM_MRE_SG, action_list); } void PimMreTrackState::input_state_in_remove_pim_mre_sg_rpt(list action_list) { add_action_list(INPUT_STATE_IN_REMOVE_PIM_MRE_SG_RPT, action_list); } void PimMreTrackState::input_state_in_remove_pim_mfc(list action_list) { add_action_list(INPUT_STATE_IN_REMOVE_PIM_MFC, action_list); } // // Output state methods // list PimMreTrackState::output_state_rp_wc(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_RP_WC, PIM_MRE_WC); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_rp_wc(action_list); return (action_list); } list PimMreTrackState::output_state_rp_sg(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_RP_SG, PIM_MRE_SG); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_rp_sg(action_list); return (action_list); } list PimMreTrackState::output_state_rp_sg_rpt(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_RP_SG_RPT, PIM_MRE_SG_RPT); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_rp_sg_rpt(action_list); return (action_list); } list PimMreTrackState::output_state_rp_mfc(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_RP_MFC, PIM_MFC); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_rp_mfc(action_list); return (action_list); } list PimMreTrackState::output_state_mrib_rp_rp(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_MRIB_RP_RP, PIM_MRE_RP); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_mrib_rp_rp(action_list); return (action_list); } list PimMreTrackState::output_state_mrib_rp_wc(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_MRIB_RP_WC, PIM_MRE_WC); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_mrib_rp_wc(action_list); return (action_list); } list PimMreTrackState::output_state_mrib_rp_sg(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_MRIB_RP_SG, PIM_MRE_SG); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_mrib_rp_sg(action_list); return (action_list); } list PimMreTrackState::output_state_mrib_rp_sg_rpt(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_MRIB_RP_SG_RPT, PIM_MRE_SG_RPT); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_mrib_rp_sg_rpt(action_list); return (action_list); } list PimMreTrackState::output_state_mrib_s_sg(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_MRIB_S_SG, PIM_MRE_SG); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_mrib_s_sg(action_list); return (action_list); } list PimMreTrackState::output_state_mrib_s_sg_rpt(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_MRIB_S_SG_RPT, PIM_MRE_SG_RPT); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_mrib_s_sg_rpt(action_list); return (action_list); } list PimMreTrackState::output_state_is_join_desired_rp(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_IS_JOIN_DESIRED_RP, PIM_MRE_RP); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_is_join_desired_rp(action_list); return (action_list); } list PimMreTrackState::output_state_is_join_desired_wc(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_IS_JOIN_DESIRED_WC, PIM_MRE_WC); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_is_join_desired_wc(action_list); return (action_list); } list PimMreTrackState::output_state_is_join_desired_sg(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_IS_JOIN_DESIRED_SG, PIM_MRE_SG); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_is_join_desired_sg(action_list); return (action_list); } list PimMreTrackState::output_state_is_prune_desired_sg_rpt(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_IS_PRUNE_DESIRED_SG_RPT, PIM_MRE_SG_RPT); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_is_prune_desired_sg_rpt(action_list); return (action_list); } list PimMreTrackState::output_state_is_prune_desired_sg_rpt_sg(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_IS_PRUNE_DESIRED_SG_RPT_SG, PIM_MRE_SG); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_is_prune_desired_sg_rpt_sg(action_list); return (action_list); } // XXX: the action is performed on (S,G,rpt) states list PimMreTrackState::output_state_is_rpt_join_desired_g(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_IS_RPT_JOIN_DESIRED_G, PIM_MRE_SG_RPT); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_is_rpt_join_desired_g(action_list); return (action_list); } list PimMreTrackState::output_state_inherited_olist_sg_rpt(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_INHERITED_OLIST_SG_RPT, PIM_MRE_SG_RPT); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_inherited_olist_sg_rpt(action_list); return (action_list); } list PimMreTrackState::output_state_iif_olist_mfc(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_IIF_OLIST_MFC, PIM_MFC); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_iif_olist_mfc(action_list); return (action_list); } list PimMreTrackState::output_state_monitoring_switch_to_spt_desired_mfc(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_MONITORING_SWITCH_TO_SPT_DESIRED_MFC, PIM_MFC); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_monitoring_switch_to_spt_desired_mfc(action_list); return (action_list); } list PimMreTrackState::output_state_spt_switch_threshold_changed_mfc(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_SPT_SWITCH_THRESHOLD_CHANGED_MFC, PIM_MFC); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_spt_switch_threshold_changed_mfc(action_list); return (action_list); } list PimMreTrackState::output_state_is_directly_connected_sg(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_IS_DIRECTLY_CONNECTED_SG, PIM_MRE_SG); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_is_directly_connected_sg(action_list); return (action_list); } list PimMreTrackState::output_state_is_could_register_sg(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_IS_COULD_REGISTER_SG, PIM_MRE_SG); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_is_could_register_sg(action_list); return (action_list); } list PimMreTrackState::output_state_assert_tracking_desired_sg(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_ASSERT_TRACKING_DESIRED_SG, PIM_MRE_SG); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_assert_tracking_desired_sg(action_list); return (action_list); } list PimMreTrackState::output_state_assert_tracking_desired_wc(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_ASSERT_TRACKING_DESIRED_WC, PIM_MRE_WC); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_assert_tracking_desired_wc(action_list); return (action_list); } list PimMreTrackState::output_state_could_assert_sg(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_COULD_ASSERT_SG, PIM_MRE_SG); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_could_assert_sg(action_list); return (action_list); } list PimMreTrackState::output_state_could_assert_wc(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_COULD_ASSERT_WC, PIM_MRE_WC); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_could_assert_wc(action_list); return (action_list); } list PimMreTrackState::output_state_my_assert_metric_sg(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_MY_ASSERT_METRIC_SG, PIM_MRE_SG); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_my_assert_metric_sg(action_list); return (action_list); } // XXX: not in the spec; a wrapper for rpt_assert_metric(G,I) list PimMreTrackState::output_state_my_assert_metric_wc(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_MY_ASSERT_METRIC_WC, PIM_MRE_WC); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_my_assert_metric_wc(action_list); return (action_list); } // XXX: Assert-state (S,G) machine for "RPF_interface (S) stops being I" list PimMreTrackState::output_state_assert_rpf_interface_sg(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_ASSERT_RPF_INTERFACE_SG, PIM_MRE_SG); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_assert_rpf_interface_sg(action_list); return (action_list); } // XXX: Assert-state (*,G) machine for "RPF_interface (RP(G)) stops being I" list PimMreTrackState::output_state_assert_rpf_interface_wc(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_ASSERT_RPF_INTERFACE_WC, PIM_MRE_WC); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_assert_rpf_interface_wc(action_list); return (action_list); } list PimMreTrackState::output_state_assert_receive_join_sg(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_ASSERT_RECEIVE_JOIN_SG, PIM_MRE_SG); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_assert_receive_join_sg(action_list); return (action_list); } list PimMreTrackState::output_state_assert_receive_join_wc(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_ASSERT_RECEIVE_JOIN_WC, PIM_MRE_WC); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_assert_receive_join_wc(action_list); return (action_list); } list PimMreTrackState::output_state_assert_winner_nbr_sg_gen_id(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_ASSERT_WINNER_NBR_SG_GEN_ID, PIM_MRE_SG); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_assert_winner_nbr_sg_gen_id(action_list); return (action_list); } list PimMreTrackState::output_state_assert_winner_nbr_wc_gen_id(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_ASSERT_WINNER_NBR_WC_GEN_ID, PIM_MRE_WC); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_assert_winner_nbr_wc_gen_id(action_list); return (action_list); } list PimMreTrackState::output_state_assert_winner_nbr_sg_nlt(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_ASSERT_WINNER_NBR_SG_NLT, PIM_MRE_SG); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_assert_winner_nbr_sg_nlt(action_list); return (action_list); } list PimMreTrackState::output_state_assert_winner_nbr_wc_nlt(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_ASSERT_WINNER_NBR_WC_NLT, PIM_MRE_WC); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_assert_winner_nbr_wc_nlt(action_list); return (action_list); } list PimMreTrackState::output_state_receive_join_wc_by_sg_rpt(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_RECEIVE_JOIN_WC_BY_SG_RPT, PIM_MRE_SG_RPT); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_receive_join_wc_by_sg_rpt(action_list); return (action_list); } list PimMreTrackState::output_state_receive_end_of_message_sg_rpt(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_RECEIVE_END_OF_MESSAGE_SG_RPT, PIM_MRE_SG_RPT); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_receive_end_of_message_sg_rpt(action_list); return (action_list); } // XXX: the action is performed on (S,G) states list PimMreTrackState::output_state_sg_see_prune_wc(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_SG_SEE_PRUNE_WC, PIM_MRE_SG); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_sg_see_prune_wc(action_list); return (action_list); } list PimMreTrackState::output_state_rpfp_nbr_wc_assert(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_RPFP_NBR_WC_ASSERT, PIM_MRE_WC); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_rpfp_nbr_wc_assert(action_list); return (action_list); } list PimMreTrackState::output_state_rpfp_nbr_wc_not_assert(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_RPFP_NBR_WC_NOT_ASSERT, PIM_MRE_WC); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_rpfp_nbr_wc_not_assert(action_list); return (action_list); } list PimMreTrackState::output_state_rpfp_nbr_wc_gen_id(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_RPFP_NBR_WC_GEN_ID, PIM_MRE_WC); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_rpfp_nbr_wc_gen_id(action_list); return (action_list); } list PimMreTrackState::output_state_rpfp_nbr_sg_assert(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_RPFP_NBR_SG_ASSERT, PIM_MRE_SG); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_rpfp_nbr_sg_assert(action_list); return (action_list); } list PimMreTrackState::output_state_rpfp_nbr_sg_not_assert(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_RPFP_NBR_SG_NOT_ASSERT, PIM_MRE_SG); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_rpfp_nbr_sg_not_assert(action_list); return (action_list); } list PimMreTrackState::output_state_rpfp_nbr_sg_gen_id(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_RPFP_NBR_SG_GEN_ID, PIM_MRE_SG); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_rpfp_nbr_sg_gen_id(action_list); return (action_list); } list PimMreTrackState::output_state_rpfp_nbr_sg_rpt(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_RPFP_NBR_SG_RPT, PIM_MRE_SG_RPT); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_rpfp_nbr_sg_rpt(action_list); return (action_list); } list PimMreTrackState::output_state_rpfp_nbr_sg_rpt_sg(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_RPFP_NBR_SG_RPT_SG, PIM_MRE_SG); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_rpfp_nbr_sg_rpt_sg(action_list); return (action_list); } list PimMreTrackState::output_state_nbr_mrib_next_hop_rp_rp(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_NBR_MRIB_NEXT_HOP_RP_RP, PIM_MRE_RP); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_nbr_mrib_next_hop_rp_rp(action_list); return (action_list); } list PimMreTrackState::output_state_nbr_mrib_next_hop_rp_wc(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_NBR_MRIB_NEXT_HOP_RP_WC, PIM_MRE_WC); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_nbr_mrib_next_hop_rp_wc(action_list); return (action_list); } list PimMreTrackState::output_state_nbr_mrib_next_hop_rp_gen_id(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_NBR_MRIB_NEXT_HOP_RP_GEN_ID, PIM_MRE_RP); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_nbr_mrib_next_hop_rp_gen_id(action_list); return (action_list); } list PimMreTrackState::output_state_nbr_mrib_next_hop_s(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_NBR_MRIB_NEXT_HOP_S, PIM_MRE_SG); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_nbr_mrib_next_hop_s(action_list); return (action_list); } list PimMreTrackState::output_state_out_start_vif_rp(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_OUT_START_VIF_RP, PIM_MRE_RP); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_out_start_vif_rp(action_list); return (action_list); } list PimMreTrackState::output_state_out_start_vif_wc(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_OUT_START_VIF_WC, PIM_MRE_WC); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_out_start_vif_wc(action_list); return (action_list); } list PimMreTrackState::output_state_out_start_vif_sg(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_OUT_START_VIF_SG, PIM_MRE_SG); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_out_start_vif_sg(action_list); return (action_list); } list PimMreTrackState::output_state_out_start_vif_sg_rpt(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_OUT_START_VIF_SG_RPT, PIM_MRE_SG_RPT); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_out_start_vif_sg_rpt(action_list); return (action_list); } list PimMreTrackState::output_state_out_stop_vif_rp(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_OUT_STOP_VIF_RP, PIM_MRE_RP); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_out_stop_vif_rp(action_list); return (action_list); } list PimMreTrackState::output_state_out_stop_vif_wc(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_OUT_STOP_VIF_WC, PIM_MRE_WC); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_out_stop_vif_wc(action_list); return (action_list); } list PimMreTrackState::output_state_out_stop_vif_sg(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_OUT_STOP_VIF_SG, PIM_MRE_SG); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_out_stop_vif_sg(action_list); return (action_list); } list PimMreTrackState::output_state_out_stop_vif_sg_rpt(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_OUT_STOP_VIF_SG_RPT, PIM_MRE_SG_RPT); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_out_stop_vif_sg_rpt(action_list); return (action_list); } list PimMreTrackState::output_state_out_add_pim_mre_rp_entry_rp(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_OUT_ADD_PIM_MRE_RP_ENTRY_RP, PIM_MRE_RP); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_out_add_pim_mre_rp_entry_rp(action_list); return (action_list); } list PimMreTrackState::output_state_out_add_pim_mre_rp_entry_wc(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_OUT_ADD_PIM_MRE_RP_ENTRY_WC, PIM_MRE_WC); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_out_add_pim_mre_rp_entry_wc(action_list); return (action_list); } list PimMreTrackState::output_state_out_add_pim_mre_rp_entry_sg(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_OUT_ADD_PIM_MRE_RP_ENTRY_SG, PIM_MRE_SG); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_out_add_pim_mre_rp_entry_sg(action_list); return (action_list); } list PimMreTrackState::output_state_out_add_pim_mre_rp_entry_sg_rpt(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_OUT_ADD_PIM_MRE_RP_ENTRY_SG_RPT, PIM_MRE_SG_RPT); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_out_add_pim_mre_rp_entry_sg_rpt(action_list); return (action_list); } list PimMreTrackState::output_state_out_add_pim_mre_wc_entry_wc(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_OUT_ADD_PIM_MRE_WC_ENTRY_WC, PIM_MRE_WC); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_out_add_pim_mre_wc_entry_wc(action_list); return (action_list); } list PimMreTrackState::output_state_out_add_pim_mre_wc_entry_sg(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_OUT_ADD_PIM_MRE_WC_ENTRY_SG, PIM_MRE_SG); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_out_add_pim_mre_wc_entry_sg(action_list); return (action_list); } list PimMreTrackState::output_state_out_add_pim_mre_wc_entry_sg_rpt(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_OUT_ADD_PIM_MRE_WC_ENTRY_SG_RPT, PIM_MRE_SG_RPT); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_out_add_pim_mre_wc_entry_sg_rpt(action_list); return (action_list); } list PimMreTrackState::output_state_out_add_pim_mre_sg_entry_sg(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_OUT_ADD_PIM_MRE_SG_ENTRY_SG, PIM_MRE_SG); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_out_add_pim_mre_sg_entry_sg(action_list); return (action_list); } list PimMreTrackState::output_state_out_add_pim_mre_sg_entry_sg_rpt(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_OUT_ADD_PIM_MRE_SG_ENTRY_SG_RPT, PIM_MRE_SG_RPT); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_out_add_pim_mre_sg_entry_sg_rpt(action_list); return (action_list); } list PimMreTrackState::output_state_out_add_pim_mre_sg_rpt_entry_sg(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_OUT_ADD_PIM_MRE_SG_RPT_ENTRY_SG, PIM_MRE_SG); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_out_add_pim_mre_sg_rpt_entry_sg(action_list); return (action_list); } list PimMreTrackState::output_state_out_add_pim_mre_sg_rpt_entry_sg_rpt(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_OUT_ADD_PIM_MRE_SG_RPT_ENTRY_SG_RPT, PIM_MRE_SG_RPT); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_out_add_pim_mre_sg_rpt_entry_sg_rpt(action_list); return (action_list); } list PimMreTrackState::output_state_out_remove_pim_mre_rp_entry_rp(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_OUT_REMOVE_PIM_MRE_RP_ENTRY_RP, PIM_MRE_RP); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_out_remove_pim_mre_rp_entry_rp(action_list); return (action_list); } list PimMreTrackState::output_state_out_remove_pim_mre_rp_entry_wc(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_OUT_REMOVE_PIM_MRE_RP_ENTRY_WC, PIM_MRE_WC); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_out_remove_pim_mre_rp_entry_wc(action_list); return (action_list); } list PimMreTrackState::output_state_out_remove_pim_mre_rp_entry_sg(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_OUT_REMOVE_PIM_MRE_RP_ENTRY_SG, PIM_MRE_SG); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_out_remove_pim_mre_rp_entry_sg(action_list); return (action_list); } list PimMreTrackState::output_state_out_remove_pim_mre_rp_entry_sg_rpt(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_OUT_REMOVE_PIM_MRE_RP_ENTRY_SG_RPT, PIM_MRE_SG_RPT); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_out_remove_pim_mre_rp_entry_sg_rpt(action_list); return (action_list); } list PimMreTrackState::output_state_out_remove_pim_mre_wc_entry_wc(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_OUT_REMOVE_PIM_MRE_WC_ENTRY_WC, PIM_MRE_WC); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_out_remove_pim_mre_wc_entry_wc(action_list); return (action_list); } list PimMreTrackState::output_state_out_remove_pim_mre_wc_entry_sg(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_OUT_REMOVE_PIM_MRE_WC_ENTRY_SG, PIM_MRE_SG); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_out_remove_pim_mre_wc_entry_sg(action_list); return (action_list); } list PimMreTrackState::output_state_out_remove_pim_mre_wc_entry_sg_rpt(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_OUT_REMOVE_PIM_MRE_WC_ENTRY_SG_RPT, PIM_MRE_SG_RPT); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_out_remove_pim_mre_wc_entry_sg_rpt(action_list); return (action_list); } list PimMreTrackState::output_state_out_remove_pim_mre_sg_entry_sg(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_OUT_REMOVE_PIM_MRE_SG_ENTRY_SG, PIM_MRE_SG); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_out_remove_pim_mre_sg_entry_sg(action_list); return (action_list); } list PimMreTrackState::output_state_out_remove_pim_mre_sg_entry_sg_rpt(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_OUT_REMOVE_PIM_MRE_SG_ENTRY_SG_RPT, PIM_MRE_SG_RPT); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_out_remove_pim_mre_sg_entry_sg_rpt(action_list); return (action_list); } list PimMreTrackState::output_state_out_remove_pim_mre_sg_rpt_entry_sg(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_OUT_REMOVE_PIM_MRE_SG_RPT_ENTRY_SG, PIM_MRE_SG); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_out_remove_pim_mre_sg_rpt_entry_sg(action_list); return (action_list); } list PimMreTrackState::output_state_out_remove_pim_mre_sg_rpt_entry_sg_rpt(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_OUT_REMOVE_PIM_MRE_SG_RPT_ENTRY_SG_RPT, PIM_MRE_SG_RPT); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_out_remove_pim_mre_sg_rpt_entry_sg_rpt(action_list); return (action_list); } list PimMreTrackState::output_state_out_remove_pim_mfc_entry_mfc(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_OUT_REMOVE_PIM_MFC_ENTRY_MFC, PIM_MFC); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_out_remove_pim_mfc_entry_mfc(action_list); return (action_list); } list PimMreTrackState::output_state_update_sptbit_mfc(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_UPDATE_SPTBIT_MFC, PIM_MFC); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_update_sptbit_mfc(action_list); return (action_list); } list PimMreTrackState::output_state_set_keepalive_timer_sg(list action_list) { bool init_flag = action_list.empty(); PimMreAction action(OUTPUT_STATE_SET_KEEPALIVE_TIMER_SG, PIM_MRE_SG); if (can_add_action_to_list(action_list, action)) action_list.push_back(action); if (init_flag) track_state_set_keepalive_timer_sg(action_list); return (action_list); } // // Track state methods // void PimMreTrackState::track_state_rp_wc(list action_list) { action_list = output_state_rp_wc(action_list); track_state_rp(action_list); } void PimMreTrackState::track_state_rp_sg(list action_list) { action_list = output_state_rp_sg(action_list); track_state_rp(action_list); } void PimMreTrackState::track_state_rp_sg_rpt(list action_list) { action_list = output_state_rp_sg_rpt(action_list); track_state_rp(action_list); } void PimMreTrackState::track_state_rp_mfc(list action_list) { action_list = output_state_rp_mfc(action_list); track_state_rp(action_list); } void PimMreTrackState::track_state_mrib_rp_rp(list action_list) { action_list = output_state_mrib_rp_rp(action_list); track_state_mrib_rp(action_list); } void PimMreTrackState::track_state_mrib_rp_wc(list action_list) { action_list = output_state_mrib_rp_wc(action_list); track_state_mrib_rp(action_list); } void PimMreTrackState::track_state_mrib_rp_sg(list action_list) { action_list = output_state_mrib_rp_sg(action_list); track_state_mrib_rp(action_list); } void PimMreTrackState::track_state_mrib_rp_sg_rpt(list action_list) { action_list = output_state_mrib_rp_sg_rpt(action_list); track_state_mrib_rp(action_list); } void PimMreTrackState::track_state_mrib_s_sg(list action_list) { action_list = output_state_mrib_s_sg(action_list); track_state_mrib_s(action_list); } void PimMreTrackState::track_state_mrib_s_sg_rpt(list action_list) { action_list = output_state_mrib_s_sg_rpt(action_list); track_state_mrib_s(action_list); } void PimMreTrackState::track_state_rp(list action_list) { input_state_rp_changed(action_list); } void PimMreTrackState::track_state_mrib_rp(list action_list) { track_state_rp(action_list); input_state_mrib_rp_changed(action_list); } void PimMreTrackState::track_state_mrib_s(list action_list) { input_state_mrib_s_changed(action_list); } void PimMreTrackState::track_state_rpf_interface_rp(list action_list) { track_state_rp(action_list); track_state_mrib_rp(action_list); } void PimMreTrackState::track_state_rpf_interface_s(list action_list) { track_state_mrib_s(action_list); } void PimMreTrackState::track_state_nbr_mrib_next_hop_rp_rp(list action_list) { action_list = output_state_nbr_mrib_next_hop_rp_rp(action_list); track_state_rp(action_list); track_state_mrib_rp(action_list); input_state_nbr_mrib_next_hop_rp_changed(action_list); } void PimMreTrackState::track_state_nbr_mrib_next_hop_rp_wc(list action_list) { action_list = output_state_nbr_mrib_next_hop_rp_wc(action_list); track_state_rp(action_list); track_state_mrib_rp(action_list); input_state_nbr_mrib_next_hop_rp_changed(action_list); } void PimMreTrackState::track_state_nbr_mrib_next_hop_rp_gen_id(list action_list) { action_list = output_state_nbr_mrib_next_hop_rp_gen_id(action_list); input_state_nbr_mrib_next_hop_rp_gen_id_changed(action_list); } void PimMreTrackState::track_state_nbr_mrib_next_hop_rp_g(list action_list) { track_state_nbr_mrib_next_hop_rp_wc(action_list); input_state_nbr_mrib_next_hop_rp_g_changed(action_list); } void PimMreTrackState::track_state_nbr_mrib_next_hop_s(list action_list) { action_list = output_state_nbr_mrib_next_hop_s(action_list); track_state_mrib_s(action_list); input_state_nbr_mrib_next_hop_s_changed(action_list); } void PimMreTrackState::track_state_mrib_pref_metric_s(list action_list) { track_state_mrib_s(action_list); } void PimMreTrackState::track_state_mrib_pref_metric_rp(list action_list) { track_state_rp(action_list); track_state_mrib_rp(action_list); } void PimMreTrackState::track_state_receive_join_rp(list action_list) { input_state_receive_join_rp(action_list); } void PimMreTrackState::track_state_receive_join_wc(list action_list) { input_state_receive_join_wc(action_list); } void PimMreTrackState::track_state_receive_join_sg(list action_list) { input_state_receive_join_sg(action_list); } void PimMreTrackState::track_state_receive_join_sg_rpt(list action_list) { input_state_receive_join_sg_rpt(action_list); } // XXX: unused void PimMreTrackState::track_state_receive_prune_rp(list action_list) { input_state_receive_prune_rp(action_list); } void PimMreTrackState::track_state_receive_prune_wc(list action_list) { input_state_receive_prune_wc(action_list); } void PimMreTrackState::track_state_receive_prune_sg(list action_list) { input_state_receive_prune_sg(action_list); } // XXX: unused void PimMreTrackState::track_state_receive_prune_sg_rpt(list action_list) { input_state_receive_prune_sg_rpt(action_list); } void PimMreTrackState::track_state_receive_end_of_message_sg_rpt(list action_list) { action_list = output_state_receive_end_of_message_sg_rpt(action_list); input_state_receive_end_of_message_sg_rpt(action_list); } void PimMreTrackState::track_state_sg_see_prune_wc(list action_list) { action_list = output_state_sg_see_prune_wc(action_list); input_state_see_prune_wc(action_list); } void PimMreTrackState::track_state_downstream_jp_state_rp(list action_list) { input_state_downstream_jp_state_rp(action_list); } void PimMreTrackState::track_state_downstream_jp_state_wc(list action_list) { track_state_receive_prune_wc(action_list); input_state_downstream_jp_state_wc(action_list); } void PimMreTrackState::track_state_downstream_jp_state_sg(list action_list) { track_state_receive_prune_sg(action_list); input_state_downstream_jp_state_sg(action_list); } void PimMreTrackState::track_state_downstream_jp_state_sg_rpt(list action_list) { input_state_downstream_jp_state_sg_rpt(action_list); } void PimMreTrackState::track_state_upstream_jp_state_sg(list action_list) { input_state_upstream_jp_state_sg(action_list); } void PimMreTrackState::track_state_local_receiver_include_wc(list action_list) { input_state_local_receiver_include_wc(action_list); } void PimMreTrackState::track_state_local_receiver_include_sg(list action_list) { input_state_local_receiver_include_sg(action_list); } void PimMreTrackState::track_state_local_receiver_exclude_sg(list action_list) { input_state_local_receiver_exclude_sg(action_list); } void PimMreTrackState::track_state_assert_state_wc(list action_list) { input_state_assert_state_wc(action_list); } void PimMreTrackState::track_state_assert_state_sg(list action_list) { input_state_assert_state_sg(action_list); } void PimMreTrackState::track_state_i_am_dr(list action_list) { input_state_i_am_dr(action_list); } void PimMreTrackState::track_state_my_ip_address(list action_list) { input_state_my_ip_address(action_list); } void PimMreTrackState::track_state_my_ip_subnet_address(list action_list) { input_state_my_ip_subnet_address(action_list); } void PimMreTrackState::track_state_keepalive_timer_sg(list action_list) { input_state_keepalive_timer_sg(action_list); } // // // void PimMreTrackState::track_state_immediate_olist_rp(list action_list) { track_state_joins_rp(action_list); } void PimMreTrackState::track_state_immediate_olist_wc(list action_list) { track_state_joins_wc(action_list); track_state_pim_include_wc(action_list); track_state_lost_assert_wc(action_list); } void PimMreTrackState::track_state_immediate_olist_sg(list action_list) { track_state_joins_sg(action_list); track_state_pim_include_sg(action_list); track_state_lost_assert_sg(action_list); } void PimMreTrackState::track_state_inherited_olist_sg_rpt(list action_list) { action_list = output_state_inherited_olist_sg_rpt(action_list); track_state_joins_rp(action_list); track_state_joins_wc(action_list); track_state_prunes_sg_rpt(action_list); track_state_pim_include_wc(action_list); track_state_pim_exclude_sg(action_list); track_state_lost_assert_wc(action_list); track_state_lost_assert_sg_rpt(action_list); } void PimMreTrackState::track_state_inherited_olist_sg(list action_list) { track_state_inherited_olist_sg_rpt(action_list); track_state_joins_sg(action_list); track_state_pim_include_sg(action_list); track_state_lost_assert_sg(action_list); } void PimMreTrackState::track_state_iif_olist_mfc(list action_list) { action_list = output_state_iif_olist_mfc(action_list); track_state_sptbit_sg(action_list); track_state_rpf_interface_s(action_list); track_state_upstream_jp_state_sg(action_list); track_state_inherited_olist_sg(action_list); track_state_rpf_interface_rp(action_list); track_state_inherited_olist_sg_rpt(action_list); track_state_was_switch_to_spt_desired_sg(action_list); } void PimMreTrackState::track_state_monitoring_switch_to_spt_desired_mfc(list action_list) { action_list = output_state_monitoring_switch_to_spt_desired_mfc(action_list); track_state_monitoring_switch_to_spt_desired_sg(action_list); } void PimMreTrackState::track_state_monitoring_switch_to_spt_desired_sg(list action_list) { // The last-hop router SPT switch track_state_pim_include_wc(action_list); track_state_pim_exclude_sg(action_list); track_state_pim_include_sg(action_list); } void PimMreTrackState::track_state_spt_switch_threshold_changed_mfc(list action_list) { action_list = output_state_spt_switch_threshold_changed_mfc(action_list); input_state_spt_switch_threshold_changed_mfc(action_list); } void PimMreTrackState::track_state_was_switch_to_spt_desired_sg(list action_list) { input_state_was_switch_to_spt_desired_sg(action_list); } void PimMreTrackState::track_state_pim_include_wc(list action_list) { track_state_i_am_dr(action_list); track_state_lost_assert_wc(action_list); track_state_assert_winner_wc(action_list); track_state_local_receiver_include_wc(action_list); } void PimMreTrackState::track_state_pim_include_sg(list action_list) { track_state_i_am_dr(action_list); track_state_lost_assert_sg(action_list); track_state_assert_winner_sg(action_list); track_state_local_receiver_include_sg(action_list); } void PimMreTrackState::track_state_pim_exclude_sg(list action_list) { track_state_i_am_dr(action_list); track_state_lost_assert_wc(action_list); track_state_assert_winner_wc(action_list); track_state_local_receiver_exclude_sg(action_list); } void PimMreTrackState::track_state_joins_rp(list action_list) { track_state_downstream_jp_state_rp(action_list); } void PimMreTrackState::track_state_joins_wc(list action_list) { track_state_downstream_jp_state_wc(action_list); } void PimMreTrackState::track_state_joins_sg(list action_list) { track_state_downstream_jp_state_sg(action_list); } void PimMreTrackState::track_state_prunes_sg_rpt(list action_list) { track_state_downstream_jp_state_sg_rpt(action_list); } void PimMreTrackState::track_state_rpfp_nbr_wc(list action_list) { track_state_rpfp_nbr_wc_assert(action_list); track_state_rpfp_nbr_wc_not_assert(action_list); } void PimMreTrackState::track_state_rpfp_nbr_wc_assert(list action_list) { action_list = output_state_rpfp_nbr_wc_assert(action_list); track_state_i_am_assert_loser_wc(action_list); track_state_assert_winner_wc(action_list); } void PimMreTrackState::track_state_rpfp_nbr_wc_not_assert(list action_list) { action_list = output_state_rpfp_nbr_wc_not_assert(action_list); track_state_rpf_interface_rp(action_list); track_state_nbr_mrib_next_hop_rp_g(action_list); input_state_rpfp_nbr_wc_changed(action_list); } void PimMreTrackState::track_state_rpfp_nbr_wc_gen_id(list action_list) { action_list = output_state_rpfp_nbr_wc_gen_id(action_list); input_state_rpfp_nbr_wc_gen_id_changed(action_list); } void PimMreTrackState::track_state_rpfp_nbr_sg(list action_list) { track_state_rpfp_nbr_sg_assert(action_list); track_state_rpfp_nbr_sg_not_assert(action_list); } void PimMreTrackState::track_state_rpfp_nbr_sg_assert(list action_list) { action_list = output_state_rpfp_nbr_sg_assert(action_list); track_state_i_am_assert_loser_sg(action_list); track_state_assert_winner_sg(action_list); } void PimMreTrackState::track_state_rpfp_nbr_sg_not_assert(list action_list) { action_list = output_state_rpfp_nbr_sg_not_assert(action_list); track_state_rpf_interface_s(action_list); track_state_nbr_mrib_next_hop_s(action_list); input_state_rpfp_nbr_sg_changed(action_list); } void PimMreTrackState::track_state_rpfp_nbr_sg_gen_id(list action_list) { action_list = output_state_rpfp_nbr_sg_gen_id(action_list); input_state_rpfp_nbr_sg_gen_id_changed(action_list); } void PimMreTrackState::track_state_rpfp_nbr_sg_rpt(list action_list) { action_list = output_state_rpfp_nbr_sg_rpt(action_list); track_state_rpf_interface_rp(action_list); track_state_i_am_assert_loser_sg(action_list); track_state_assert_winner_sg(action_list); track_state_rpfp_nbr_wc(action_list); input_state_rpfp_nbr_sg_rpt_changed(action_list); } void PimMreTrackState::track_state_rpfp_nbr_sg_rpt_sg(list action_list) { action_list = output_state_rpfp_nbr_sg_rpt_sg(action_list); track_state_rpfp_nbr_sg_rpt(action_list); } void PimMreTrackState::track_state_sptbit_sg(list action_list) { // XXX: the SPTbit(S,G) does not depend on other // state for recomputation and is updated only when a data packet // or an Assert(S,G) are received. input_state_sptbit_sg(action_list); } void PimMreTrackState::track_state_is_directly_connected_sg(list action_list) { action_list = output_state_is_directly_connected_sg(action_list); track_state_my_ip_address(action_list); track_state_my_ip_subnet_address(action_list); track_state_mrib_s(action_list); } void PimMreTrackState::track_state_is_could_register_sg(list action_list) { action_list = output_state_is_could_register_sg(action_list); track_state_rpf_interface_s(action_list); track_state_i_am_dr(action_list); track_state_keepalive_timer_sg(action_list); track_state_is_directly_connected_sg(action_list); track_state_rp(action_list); // TODO: XXX: PAVPAVPAV: not in the spec (yet) } void PimMreTrackState::track_state_is_join_desired_rp(list action_list) { action_list = output_state_is_join_desired_rp(action_list); track_state_immediate_olist_rp(action_list); } void PimMreTrackState::track_state_is_join_desired_wc(list action_list) { action_list = output_state_is_join_desired_wc(action_list); track_state_immediate_olist_wc(action_list); track_state_is_join_desired_rp(action_list); track_state_rpf_interface_rp(action_list); track_state_assert_winner_wc(action_list); } void PimMreTrackState::track_state_is_join_desired_sg(list action_list) { action_list = output_state_is_join_desired_sg(action_list); track_state_immediate_olist_sg(action_list); track_state_keepalive_timer_sg(action_list); track_state_inherited_olist_sg(action_list); } void PimMreTrackState::track_state_is_rpt_join_desired_g(list action_list) { action_list = output_state_is_rpt_join_desired_g(action_list); track_state_is_join_desired_wc(action_list); track_state_is_join_desired_rp(action_list); } void PimMreTrackState::track_state_is_prune_desired_sg_rpt(list action_list) { action_list = output_state_is_prune_desired_sg_rpt(action_list); track_state_is_rpt_join_desired_g(action_list); track_state_inherited_olist_sg_rpt(action_list); track_state_sptbit_sg(action_list); track_state_rpfp_nbr_wc(action_list); track_state_rpfp_nbr_sg(action_list); } void PimMreTrackState::track_state_is_prune_desired_sg_rpt_sg(list action_list) { action_list = output_state_is_prune_desired_sg_rpt_sg(action_list); track_state_is_prune_desired_sg_rpt(action_list); } void PimMreTrackState::track_state_could_assert_sg(list action_list) { action_list = output_state_could_assert_sg(action_list); track_state_sptbit_sg(action_list); track_state_rpf_interface_s(action_list); track_state_joins_rp(action_list); track_state_joins_wc(action_list); track_state_prunes_sg_rpt(action_list); track_state_pim_include_wc(action_list); track_state_pim_exclude_sg(action_list); track_state_lost_assert_wc(action_list); track_state_joins_sg(action_list); track_state_pim_include_sg(action_list); } void PimMreTrackState::track_state_assert_tracking_desired_sg(list action_list) { action_list = output_state_assert_tracking_desired_sg(action_list); track_state_joins_rp(action_list); track_state_joins_wc(action_list); track_state_prunes_sg_rpt(action_list); track_state_pim_include_wc(action_list); track_state_pim_exclude_sg(action_list); track_state_lost_assert_wc(action_list); track_state_joins_sg(action_list); track_state_local_receiver_include_sg(action_list); track_state_i_am_dr(action_list); track_state_assert_winner_sg(action_list); track_state_rpf_interface_s(action_list); track_state_is_join_desired_sg(action_list); track_state_rpf_interface_rp(action_list); track_state_is_join_desired_wc(action_list); track_state_sptbit_sg(action_list); } void PimMreTrackState::track_state_could_assert_wc(list action_list) { action_list = output_state_could_assert_wc(action_list); track_state_joins_rp(action_list); track_state_joins_wc(action_list); track_state_pim_include_wc(action_list); track_state_rpf_interface_rp(action_list); } void PimMreTrackState::track_state_assert_tracking_desired_wc(list action_list) { action_list = output_state_assert_tracking_desired_wc(action_list); track_state_could_assert_wc(action_list); track_state_local_receiver_include_wc(action_list); track_state_i_am_dr(action_list); track_state_assert_winner_wc(action_list); track_state_rpf_interface_rp(action_list); track_state_is_rpt_join_desired_g(action_list); } void PimMreTrackState::track_state_my_assert_metric_sg(list action_list) { action_list = output_state_my_assert_metric_sg(action_list); track_state_could_assert_sg(action_list); track_state_spt_assert_metric(action_list); track_state_could_assert_wc(action_list); track_state_rpt_assert_metric(action_list); } void PimMreTrackState::track_state_my_assert_metric_wc(list action_list) { action_list = output_state_my_assert_metric_wc(action_list); track_state_rpt_assert_metric(action_list); } void PimMreTrackState::track_state_spt_assert_metric(list action_list) { track_state_mrib_pref_metric_s(action_list); track_state_my_ip_address(action_list); } void PimMreTrackState::track_state_rpt_assert_metric(list action_list) { track_state_mrib_pref_metric_rp(action_list); track_state_my_ip_address(action_list); } void PimMreTrackState::track_state_lost_assert_sg_rpt(list action_list) { track_state_rpf_interface_rp(action_list); track_state_rpf_interface_s(action_list); track_state_sptbit_sg(action_list); track_state_assert_winner_sg(action_list); } void PimMreTrackState::track_state_lost_assert_sg(list action_list) { track_state_rpf_interface_s(action_list); track_state_assert_winner_sg(action_list); track_state_assert_winner_metric_is_better_than_spt_assert_metric_sg(action_list); } void PimMreTrackState::track_state_lost_assert_wc(list action_list) { track_state_rpf_interface_rp(action_list); track_state_assert_winner_wc(action_list); } void PimMreTrackState::track_state_assert_rpf_interface_sg(list action_list) { action_list = output_state_assert_rpf_interface_sg(action_list); input_state_assert_rpf_interface_sg_changed(action_list); } void PimMreTrackState::track_state_assert_rpf_interface_wc(list action_list) { action_list = output_state_assert_rpf_interface_wc(action_list); input_state_assert_rpf_interface_wc_changed(action_list); } void PimMreTrackState::track_state_assert_receive_join_sg(list action_list) { action_list = output_state_assert_receive_join_sg(action_list); track_state_receive_join_sg(action_list); } void PimMreTrackState::track_state_assert_receive_join_wc(list action_list) { action_list = output_state_assert_receive_join_wc(action_list); track_state_receive_join_wc(action_list); track_state_receive_join_rp(action_list); } void PimMreTrackState::track_state_assert_winner_nbr_sg_gen_id(list action_list) { action_list = output_state_assert_winner_nbr_sg_gen_id(action_list); input_state_assert_winner_nbr_sg_gen_id_changed(action_list); } void PimMreTrackState::track_state_assert_winner_nbr_wc_gen_id(list action_list) { action_list = output_state_assert_winner_nbr_wc_gen_id(action_list); input_state_assert_winner_nbr_wc_gen_id_changed(action_list); } void PimMreTrackState::track_state_assert_winner_nbr_sg_nlt(list action_list) { action_list = output_state_assert_winner_nbr_sg_nlt(action_list); input_state_assert_winner_nbr_sg_nlt_expired(action_list); } void PimMreTrackState::track_state_assert_winner_nbr_wc_nlt(list action_list) { action_list = output_state_assert_winner_nbr_wc_nlt(action_list); input_state_assert_winner_nbr_wc_nlt_expired(action_list); } void PimMreTrackState::track_state_receive_join_wc_by_sg_rpt(list action_list) { action_list = output_state_receive_join_wc_by_sg_rpt(action_list); track_state_receive_join_wc(action_list); } // XXX: unused void PimMreTrackState::track_state_i_am_assert_winner_sg(list action_list) { track_state_assert_state_sg(action_list); } // XXX: unused void PimMreTrackState::track_state_i_am_assert_winner_wc(list action_list) { track_state_assert_state_wc(action_list); } void PimMreTrackState::track_state_i_am_assert_loser_sg(list action_list) { track_state_assert_state_sg(action_list); } void PimMreTrackState::track_state_i_am_assert_loser_wc(list action_list) { track_state_assert_state_wc(action_list); } void PimMreTrackState::track_state_assert_winner_sg(list action_list) { track_state_assert_state_sg(action_list); } void PimMreTrackState::track_state_assert_winner_wc(list action_list) { track_state_assert_state_wc(action_list); } void PimMreTrackState::track_state_assert_winner_metric_sg(list action_list) { track_state_assert_state_sg(action_list); } // XXX: unused void PimMreTrackState::track_state_assert_winner_metric_wc(list action_list) { track_state_assert_state_wc(action_list); } void PimMreTrackState::track_state_assert_winner_metric_is_better_than_spt_assert_metric_sg(list action_list) { track_state_assert_state_sg(action_list); track_state_assert_winner_metric_sg(action_list); track_state_spt_assert_metric(action_list); } void PimMreTrackState::track_state_in_start_vif(list action_list) { input_state_in_start_vif(action_list); } void PimMreTrackState::track_state_in_stop_vif(list action_list) { input_state_in_stop_vif(action_list); } void PimMreTrackState::track_state_in_add_pim_mre_rp(list action_list) { input_state_in_add_pim_mre_rp(action_list); } void PimMreTrackState::track_state_in_add_pim_mre_wc(list action_list) { input_state_in_add_pim_mre_wc(action_list); } void PimMreTrackState::track_state_in_add_pim_mre_sg(list action_list) { input_state_in_add_pim_mre_sg(action_list); } void PimMreTrackState::track_state_in_add_pim_mre_sg_rpt(list action_list) { input_state_in_add_pim_mre_sg_rpt(action_list); } void PimMreTrackState::track_state_in_remove_pim_mre_rp(list action_list) { input_state_in_remove_pim_mre_rp(action_list); } void PimMreTrackState::track_state_in_remove_pim_mre_wc(list action_list) { input_state_in_remove_pim_mre_wc(action_list); } void PimMreTrackState::track_state_in_remove_pim_mre_sg(list action_list) { input_state_in_remove_pim_mre_sg(action_list); } void PimMreTrackState::track_state_in_remove_pim_mre_sg_rpt(list action_list) { input_state_in_remove_pim_mre_sg_rpt(action_list); } void PimMreTrackState::track_state_in_remove_pim_mfc(list action_list) { input_state_in_remove_pim_mfc(action_list); } void PimMreTrackState::track_state_out_start_vif_rp(list action_list) { action_list = output_state_out_start_vif_rp(action_list); track_state_in_start_vif(action_list); } void PimMreTrackState::track_state_out_start_vif_wc(list action_list) { action_list = output_state_out_start_vif_wc(action_list); track_state_in_start_vif(action_list); } void PimMreTrackState::track_state_out_start_vif_sg(list action_list) { action_list = output_state_out_start_vif_sg(action_list); track_state_in_start_vif(action_list); } void PimMreTrackState::track_state_out_start_vif_sg_rpt(list action_list) { action_list = output_state_out_start_vif_sg_rpt(action_list); track_state_in_start_vif(action_list); } void PimMreTrackState::track_state_out_stop_vif_rp(list action_list) { action_list = output_state_out_stop_vif_rp(action_list); track_state_in_stop_vif(action_list); } void PimMreTrackState::track_state_out_stop_vif_wc(list action_list) { action_list = output_state_out_stop_vif_wc(action_list); track_state_in_stop_vif(action_list); } void PimMreTrackState::track_state_out_stop_vif_sg(list action_list) { action_list = output_state_out_stop_vif_sg(action_list); track_state_in_stop_vif(action_list); } void PimMreTrackState::track_state_out_stop_vif_sg_rpt(list action_list) { action_list = output_state_out_stop_vif_sg_rpt(action_list); track_state_in_stop_vif(action_list); } void PimMreTrackState::track_state_out_add_pim_mre_rp_entry_rp(list action_list) { action_list = output_state_out_add_pim_mre_rp_entry_rp(action_list); track_state_in_add_pim_mre_rp(action_list); } void PimMreTrackState::track_state_out_add_pim_mre_rp_entry_wc(list action_list) { action_list = output_state_out_add_pim_mre_rp_entry_wc(action_list); track_state_in_add_pim_mre_rp(action_list); } void PimMreTrackState::track_state_out_add_pim_mre_rp_entry_sg(list action_list) { action_list = output_state_out_add_pim_mre_rp_entry_sg(action_list); track_state_in_add_pim_mre_rp(action_list); } void PimMreTrackState::track_state_out_add_pim_mre_rp_entry_sg_rpt(list action_list) { action_list = output_state_out_add_pim_mre_rp_entry_sg_rpt(action_list); track_state_in_add_pim_mre_rp(action_list); } void PimMreTrackState::track_state_out_add_pim_mre_wc_entry_wc(list action_list) { action_list = output_state_out_add_pim_mre_wc_entry_wc(action_list); track_state_in_add_pim_mre_wc(action_list); } void PimMreTrackState::track_state_out_add_pim_mre_wc_entry_sg(list action_list) { action_list = output_state_out_add_pim_mre_wc_entry_sg(action_list); track_state_in_add_pim_mre_wc(action_list); } void PimMreTrackState::track_state_out_add_pim_mre_wc_entry_sg_rpt(list action_list) { action_list = output_state_out_add_pim_mre_wc_entry_sg_rpt(action_list); track_state_in_add_pim_mre_wc(action_list); } void PimMreTrackState::track_state_out_add_pim_mre_sg_entry_sg(list action_list) { action_list = output_state_out_add_pim_mre_sg_entry_sg(action_list); track_state_in_add_pim_mre_sg(action_list); } void PimMreTrackState::track_state_out_add_pim_mre_sg_entry_sg_rpt(list action_list) { action_list = output_state_out_add_pim_mre_sg_entry_sg_rpt(action_list); track_state_in_add_pim_mre_sg(action_list); } void PimMreTrackState::track_state_out_add_pim_mre_sg_rpt_entry_sg(list action_list) { action_list = output_state_out_add_pim_mre_sg_rpt_entry_sg(action_list); track_state_in_add_pim_mre_sg_rpt(action_list); } void PimMreTrackState::track_state_out_add_pim_mre_sg_rpt_entry_sg_rpt(list action_list) { action_list = output_state_out_add_pim_mre_sg_rpt_entry_sg_rpt(action_list); track_state_in_add_pim_mre_sg_rpt(action_list); } void PimMreTrackState::track_state_out_remove_pim_mre_rp_entry_rp(list action_list) { action_list = output_state_out_remove_pim_mre_rp_entry_rp(action_list); track_state_in_remove_pim_mre_rp(action_list); } void PimMreTrackState::track_state_out_remove_pim_mre_rp_entry_wc(list action_list) { action_list = output_state_out_remove_pim_mre_rp_entry_wc(action_list); track_state_in_remove_pim_mre_rp(action_list); } void PimMreTrackState::track_state_out_remove_pim_mre_rp_entry_sg(list action_list) { action_list = output_state_out_remove_pim_mre_rp_entry_sg(action_list); track_state_in_remove_pim_mre_rp(action_list); } void PimMreTrackState::track_state_out_remove_pim_mre_rp_entry_sg_rpt(list action_list) { action_list = output_state_out_remove_pim_mre_rp_entry_sg_rpt(action_list); track_state_in_remove_pim_mre_rp(action_list); } void PimMreTrackState::track_state_out_remove_pim_mre_wc_entry_wc(list action_list) { action_list = output_state_out_remove_pim_mre_wc_entry_wc(action_list); track_state_in_remove_pim_mre_wc(action_list); } void PimMreTrackState::track_state_out_remove_pim_mre_wc_entry_sg(list action_list) { action_list = output_state_out_remove_pim_mre_wc_entry_sg(action_list); track_state_in_remove_pim_mre_wc(action_list); } void PimMreTrackState::track_state_out_remove_pim_mre_wc_entry_sg_rpt(list action_list) { action_list = output_state_out_remove_pim_mre_wc_entry_sg_rpt(action_list); track_state_in_remove_pim_mre_wc(action_list); } void PimMreTrackState::track_state_out_remove_pim_mre_sg_entry_sg(list action_list) { action_list = output_state_out_remove_pim_mre_sg_entry_sg(action_list); track_state_in_remove_pim_mre_sg(action_list); } void PimMreTrackState::track_state_out_remove_pim_mre_sg_entry_sg_rpt(list action_list) { action_list = output_state_out_remove_pim_mre_sg_entry_sg_rpt(action_list); track_state_in_remove_pim_mre_sg(action_list); } void PimMreTrackState::track_state_out_remove_pim_mre_sg_rpt_entry_sg(list action_list) { action_list = output_state_out_remove_pim_mre_sg_rpt_entry_sg(action_list); track_state_in_remove_pim_mre_sg_rpt(action_list); } void PimMreTrackState::track_state_out_remove_pim_mre_sg_rpt_entry_sg_rpt(list action_list) { action_list = output_state_out_remove_pim_mre_sg_rpt_entry_sg_rpt(action_list); track_state_in_remove_pim_mre_sg_rpt(action_list); } void PimMreTrackState::track_state_out_remove_pim_mfc_entry_mfc(list action_list) { action_list = output_state_out_remove_pim_mfc_entry_mfc(action_list); track_state_in_remove_pim_mfc(action_list); } void PimMreTrackState::track_state_update_sptbit_mfc(list action_list) { action_list = output_state_update_sptbit_mfc(action_list); track_state_iif_olist_mfc(action_list); track_state_rpf_interface_s(action_list); track_state_is_join_desired_sg(action_list); track_state_is_directly_connected_sg(action_list); track_state_rpf_interface_rp(action_list); track_state_inherited_olist_sg_rpt(action_list); track_state_rpfp_nbr_sg(action_list); track_state_rpfp_nbr_wc(action_list); track_state_i_am_assert_loser_sg(action_list); } void PimMreTrackState::track_state_set_keepalive_timer_sg(list action_list) { action_list = output_state_set_keepalive_timer_sg(action_list); track_state_is_directly_connected_sg(action_list); track_state_rpf_interface_s(action_list); track_state_is_join_desired_sg(action_list); track_state_upstream_jp_state_sg(action_list); track_state_pim_include_wc(action_list); track_state_pim_exclude_sg(action_list); track_state_pim_include_sg(action_list); track_state_monitoring_switch_to_spt_desired_sg(action_list); track_state_rp(action_list); track_state_sptbit_sg(action_list); } void PimMreAction::perform_action(PimMre& pim_mre, uint32_t vif_index, const IPvX& addr_arg) { uint32_t i, maxvifs; switch (output_state()) { case PimMreTrackState::OUTPUT_STATE_RP_WC: // 0 pim_mre.recompute_rp_wc(); break; case PimMreTrackState::OUTPUT_STATE_RP_SG: // 1 pim_mre.recompute_rp_sg(); break; case PimMreTrackState::OUTPUT_STATE_RP_SG_RPT: // 2 pim_mre.recompute_rp_sg_rpt(); break; case PimMreTrackState::OUTPUT_STATE_RP_MFC: // 3 XLOG_UNREACHABLE(); // pim_mfc.recompute_rp_mfc(); break; case PimMreTrackState::OUTPUT_STATE_MRIB_RP_RP: // 4 pim_mre.recompute_mrib_rp_rp(); break; case PimMreTrackState::OUTPUT_STATE_MRIB_RP_WC: // 5 pim_mre.recompute_mrib_rp_wc(); break; case PimMreTrackState::OUTPUT_STATE_MRIB_RP_SG: // 6 pim_mre.recompute_mrib_rp_sg(); break; case PimMreTrackState::OUTPUT_STATE_MRIB_RP_SG_RPT: // 7 pim_mre.recompute_mrib_rp_sg_rpt(); break; case PimMreTrackState::OUTPUT_STATE_MRIB_S_SG: // 8 pim_mre.recompute_mrib_s_sg(); break; case PimMreTrackState::OUTPUT_STATE_MRIB_S_SG_RPT: // 9 pim_mre.recompute_mrib_s_sg_rpt(); break; case PimMreTrackState::OUTPUT_STATE_IS_JOIN_DESIRED_RP: // 10 pim_mre.recompute_is_join_desired_rp(); break; case PimMreTrackState::OUTPUT_STATE_IS_JOIN_DESIRED_WC: // 11 pim_mre.recompute_is_join_desired_wc(); break; case PimMreTrackState::OUTPUT_STATE_IS_JOIN_DESIRED_SG: // 12 pim_mre.recompute_is_join_desired_sg(); break; case PimMreTrackState::OUTPUT_STATE_IS_PRUNE_DESIRED_SG_RPT: // 13 pim_mre.recompute_is_prune_desired_sg_rpt(); break; case PimMreTrackState::OUTPUT_STATE_IS_PRUNE_DESIRED_SG_RPT_SG: // 14 pim_mre.recompute_is_prune_desired_sg_rpt_sg(); break; case PimMreTrackState::OUTPUT_STATE_IS_RPT_JOIN_DESIRED_G: // 15 pim_mre.recompute_is_rpt_join_desired_g(); break; case PimMreTrackState::OUTPUT_STATE_INHERITED_OLIST_SG_RPT: // 16 pim_mre.recompute_inherited_olist_sg_rpt(); break; case PimMreTrackState::OUTPUT_STATE_IIF_OLIST_MFC: // 17 XLOG_UNREACHABLE(); // pim_mfc.recompute_iif_olist_mfc(); break; case PimMreTrackState::OUTPUT_STATE_MONITORING_SWITCH_TO_SPT_DESIRED_MFC: // 18 XLOG_UNREACHABLE(); // pim_mfc.recompute_monitoring_switch_to_spt_desired_mfc(); break; case PimMreTrackState::OUTPUT_STATE_SPT_SWITCH_THRESHOLD_CHANGED_MFC: // 19 XLOG_UNREACHABLE(); // pim_mfc.recompute_spt_switch_threshold_changed_mfc(); break; case PimMreTrackState::OUTPUT_STATE_IS_DIRECTLY_CONNECTED_SG: // 20 pim_mre.recompute_is_directly_connected_sg(); break; case PimMreTrackState::OUTPUT_STATE_IS_COULD_REGISTER_SG: // 21 pim_mre.recompute_is_could_register_sg(); break; case PimMreTrackState::OUTPUT_STATE_ASSERT_TRACKING_DESIRED_SG: // 22 pim_mre.recompute_assert_tracking_desired_sg(); break; case PimMreTrackState::OUTPUT_STATE_ASSERT_TRACKING_DESIRED_WC: // 23 pim_mre.recompute_assert_tracking_desired_wc(); break; case PimMreTrackState::OUTPUT_STATE_COULD_ASSERT_SG: // 24 pim_mre.recompute_could_assert_sg(); break; case PimMreTrackState::OUTPUT_STATE_COULD_ASSERT_WC: // 25 pim_mre.recompute_could_assert_wc(); break; case PimMreTrackState::OUTPUT_STATE_MY_ASSERT_METRIC_SG: // 26 if (vif_index != Vif::VIF_INDEX_INVALID) { pim_mre.recompute_my_assert_metric_sg(vif_index); } else { maxvifs = pim_mre.pim_node()->maxvifs(); for (i = 0; i < maxvifs; i++) pim_mre.recompute_my_assert_metric_sg(i); } break; case PimMreTrackState::OUTPUT_STATE_MY_ASSERT_METRIC_WC: // 27 if (vif_index != Vif::VIF_INDEX_INVALID) { pim_mre.recompute_my_assert_metric_wc(vif_index); } else { maxvifs = pim_mre.pim_node()->maxvifs(); for (i = 0; i < maxvifs; i++) pim_mre.recompute_my_assert_metric_wc(i); } break; case PimMreTrackState::OUTPUT_STATE_ASSERT_RPF_INTERFACE_SG: // 28 pim_mre.recompute_assert_rpf_interface_sg(vif_index); break; case PimMreTrackState::OUTPUT_STATE_ASSERT_RPF_INTERFACE_WC: // 29 pim_mre.recompute_assert_rpf_interface_wc(vif_index); break; case PimMreTrackState::OUTPUT_STATE_ASSERT_RECEIVE_JOIN_SG: // 30 XLOG_ASSERT(vif_index != Vif::VIF_INDEX_INVALID); pim_mre.recompute_assert_receive_join_sg(vif_index); break; case PimMreTrackState::OUTPUT_STATE_ASSERT_RECEIVE_JOIN_WC: // 31 XLOG_ASSERT(vif_index != Vif::VIF_INDEX_INVALID); pim_mre.recompute_assert_receive_join_wc(vif_index); break; case PimMreTrackState::OUTPUT_STATE_ASSERT_WINNER_NBR_SG_GEN_ID: // 32 XLOG_ASSERT(vif_index != Vif::VIF_INDEX_INVALID); pim_mre.recompute_assert_winner_nbr_sg_gen_id_changed(vif_index, addr_arg); break; case PimMreTrackState::OUTPUT_STATE_ASSERT_WINNER_NBR_WC_GEN_ID: // 33 XLOG_ASSERT(vif_index != Vif::VIF_INDEX_INVALID); pim_mre.recompute_assert_winner_nbr_wc_gen_id_changed(vif_index, addr_arg); break; case PimMreTrackState::OUTPUT_STATE_ASSERT_WINNER_NBR_SG_NLT: // 34 XLOG_ASSERT(vif_index != Vif::VIF_INDEX_INVALID); pim_mre.recompute_assert_winner_nbr_sg_nlt_expired(vif_index, addr_arg); break; case PimMreTrackState::OUTPUT_STATE_ASSERT_WINNER_NBR_WC_NLT: // 35 XLOG_ASSERT(vif_index != Vif::VIF_INDEX_INVALID); pim_mre.recompute_assert_winner_nbr_wc_nlt_expired(vif_index, addr_arg); break; case PimMreTrackState::OUTPUT_STATE_RECEIVE_JOIN_WC_BY_SG_RPT: // 36 XLOG_ASSERT(vif_index != Vif::VIF_INDEX_INVALID); pim_mre.receive_join_wc_by_sg_rpt(vif_index); break; case PimMreTrackState::OUTPUT_STATE_RECEIVE_END_OF_MESSAGE_SG_RPT: // 37 XLOG_ASSERT(vif_index != Vif::VIF_INDEX_INVALID); pim_mre.receive_end_of_message_sg_rpt(vif_index); break; case PimMreTrackState::OUTPUT_STATE_SG_SEE_PRUNE_WC: // 38 XLOG_ASSERT(vif_index != Vif::VIF_INDEX_INVALID); pim_mre.sg_see_prune_wc(vif_index, addr_arg); break; case PimMreTrackState::OUTPUT_STATE_RPFP_NBR_WC_ASSERT: // 39 pim_mre.recompute_rpfp_nbr_wc_assert_changed(); break; case PimMreTrackState::OUTPUT_STATE_RPFP_NBR_WC_NOT_ASSERT: // 40 pim_mre.recompute_rpfp_nbr_wc_not_assert_changed(); break; case PimMreTrackState::OUTPUT_STATE_RPFP_NBR_WC_GEN_ID: // 41 pim_mre.recompute_rpfp_nbr_wc_gen_id_changed(); break; case PimMreTrackState::OUTPUT_STATE_RPFP_NBR_SG_ASSERT: // 42 pim_mre.recompute_rpfp_nbr_sg_assert_changed(); break; case PimMreTrackState::OUTPUT_STATE_RPFP_NBR_SG_NOT_ASSERT: // 43 pim_mre.recompute_rpfp_nbr_sg_not_assert_changed(); break; case PimMreTrackState::OUTPUT_STATE_RPFP_NBR_SG_GEN_ID: // 44 pim_mre.recompute_rpfp_nbr_sg_gen_id_changed(); break; case PimMreTrackState::OUTPUT_STATE_RPFP_NBR_SG_RPT: // 45 pim_mre.recompute_rpfp_nbr_sg_rpt_changed(); break; case PimMreTrackState::OUTPUT_STATE_RPFP_NBR_SG_RPT_SG: // 46 pim_mre.recompute_rpfp_nbr_sg_rpt_sg_changed(); break; case PimMreTrackState::OUTPUT_STATE_NBR_MRIB_NEXT_HOP_RP_RP: // 47 pim_mre.recompute_nbr_mrib_next_hop_rp_rp_changed(); break; case PimMreTrackState::OUTPUT_STATE_NBR_MRIB_NEXT_HOP_RP_WC: // 48 pim_mre.recompute_nbr_mrib_next_hop_rp_wc_changed(); break; case PimMreTrackState::OUTPUT_STATE_NBR_MRIB_NEXT_HOP_RP_GEN_ID: // 49 pim_mre.recompute_nbr_mrib_next_hop_rp_gen_id_changed(); break; case PimMreTrackState::OUTPUT_STATE_NBR_MRIB_NEXT_HOP_S: // 50 pim_mre.recompute_nbr_mrib_next_hop_s_changed(); break; case PimMreTrackState::OUTPUT_STATE_OUT_START_VIF_RP: // 51 XLOG_ASSERT(vif_index != Vif::VIF_INDEX_INVALID); pim_mre.recompute_start_vif_rp(vif_index); break; case PimMreTrackState::OUTPUT_STATE_OUT_START_VIF_WC: // 52 XLOG_ASSERT(vif_index != Vif::VIF_INDEX_INVALID); pim_mre.recompute_start_vif_wc(vif_index); break; case PimMreTrackState::OUTPUT_STATE_OUT_START_VIF_SG: // 53 XLOG_ASSERT(vif_index != Vif::VIF_INDEX_INVALID); pim_mre.recompute_start_vif_sg(vif_index); break; case PimMreTrackState::OUTPUT_STATE_OUT_START_VIF_SG_RPT: // 54 XLOG_ASSERT(vif_index != Vif::VIF_INDEX_INVALID); pim_mre.recompute_start_vif_sg_rpt(vif_index); break; case PimMreTrackState::OUTPUT_STATE_OUT_STOP_VIF_RP: // 55 XLOG_ASSERT(vif_index != Vif::VIF_INDEX_INVALID); pim_mre.recompute_stop_vif_rp(vif_index); break; case PimMreTrackState::OUTPUT_STATE_OUT_STOP_VIF_WC: // 56 XLOG_ASSERT(vif_index != Vif::VIF_INDEX_INVALID); pim_mre.recompute_stop_vif_wc(vif_index); break; case PimMreTrackState::OUTPUT_STATE_OUT_STOP_VIF_SG: // 57 XLOG_ASSERT(vif_index != Vif::VIF_INDEX_INVALID); pim_mre.recompute_stop_vif_sg(vif_index); break; case PimMreTrackState::OUTPUT_STATE_OUT_STOP_VIF_SG_RPT: // 58 XLOG_ASSERT(vif_index != Vif::VIF_INDEX_INVALID); pim_mre.recompute_stop_vif_sg_rpt(vif_index); break; case PimMreTrackState::OUTPUT_STATE_OUT_ADD_PIM_MRE_RP_ENTRY_RP: // 59 pim_mre.add_pim_mre_rp_entry(); break; case PimMreTrackState::OUTPUT_STATE_OUT_ADD_PIM_MRE_RP_ENTRY_WC: // 60 pim_mre.add_pim_mre_rp_entry(); break; case PimMreTrackState::OUTPUT_STATE_OUT_ADD_PIM_MRE_RP_ENTRY_SG: // 61 pim_mre.add_pim_mre_rp_entry(); break; case PimMreTrackState::OUTPUT_STATE_OUT_ADD_PIM_MRE_RP_ENTRY_SG_RPT: // 62 pim_mre.add_pim_mre_rp_entry(); break; case PimMreTrackState::OUTPUT_STATE_OUT_ADD_PIM_MRE_WC_ENTRY_WC: // 63 pim_mre.add_pim_mre_wc_entry(); break; case PimMreTrackState::OUTPUT_STATE_OUT_ADD_PIM_MRE_WC_ENTRY_SG: // 64 pim_mre.add_pim_mre_wc_entry(); break; case PimMreTrackState::OUTPUT_STATE_OUT_ADD_PIM_MRE_WC_ENTRY_SG_RPT: // 65 pim_mre.add_pim_mre_wc_entry(); break; case PimMreTrackState::OUTPUT_STATE_OUT_ADD_PIM_MRE_SG_ENTRY_SG: // 66 pim_mre.add_pim_mre_sg_entry(); break; case PimMreTrackState::OUTPUT_STATE_OUT_ADD_PIM_MRE_SG_ENTRY_SG_RPT: // 67 pim_mre.add_pim_mre_sg_entry(); break; case PimMreTrackState::OUTPUT_STATE_OUT_ADD_PIM_MRE_SG_RPT_ENTRY_SG: // 68 pim_mre.add_pim_mre_sg_rpt_entry(); break; case PimMreTrackState::OUTPUT_STATE_OUT_ADD_PIM_MRE_SG_RPT_ENTRY_SG_RPT: // 69 pim_mre.add_pim_mre_sg_rpt_entry(); break; case PimMreTrackState::OUTPUT_STATE_OUT_REMOVE_PIM_MRE_RP_ENTRY_RP: // 70 pim_mre.remove_pim_mre_rp_entry(); break; case PimMreTrackState::OUTPUT_STATE_OUT_REMOVE_PIM_MRE_RP_ENTRY_WC: // 71 pim_mre.remove_pim_mre_rp_entry(); break; case PimMreTrackState::OUTPUT_STATE_OUT_REMOVE_PIM_MRE_RP_ENTRY_SG: // 72 pim_mre.remove_pim_mre_rp_entry(); break; case PimMreTrackState::OUTPUT_STATE_OUT_REMOVE_PIM_MRE_RP_ENTRY_SG_RPT: // 73 pim_mre.remove_pim_mre_rp_entry(); break; case PimMreTrackState::OUTPUT_STATE_OUT_REMOVE_PIM_MRE_WC_ENTRY_WC: // 74 pim_mre.remove_pim_mre_wc_entry(); break; case PimMreTrackState::OUTPUT_STATE_OUT_REMOVE_PIM_MRE_WC_ENTRY_SG: // 75 pim_mre.remove_pim_mre_wc_entry(); break; case PimMreTrackState::OUTPUT_STATE_OUT_REMOVE_PIM_MRE_WC_ENTRY_SG_RPT: // 76 pim_mre.remove_pim_mre_wc_entry(); break; case PimMreTrackState::OUTPUT_STATE_OUT_REMOVE_PIM_MRE_SG_ENTRY_SG: // 77 pim_mre.remove_pim_mre_sg_entry(); break; case PimMreTrackState::OUTPUT_STATE_OUT_REMOVE_PIM_MRE_SG_ENTRY_SG_RPT: // 78 pim_mre.remove_pim_mre_sg_entry(); break; case PimMreTrackState::OUTPUT_STATE_OUT_REMOVE_PIM_MRE_SG_RPT_ENTRY_SG: // 79 pim_mre.remove_pim_mre_sg_rpt_entry(); break; case PimMreTrackState::OUTPUT_STATE_OUT_REMOVE_PIM_MRE_SG_RPT_ENTRY_SG_RPT: // 80 pim_mre.remove_pim_mre_sg_rpt_entry(); break; case PimMreTrackState::OUTPUT_STATE_OUT_REMOVE_PIM_MFC_ENTRY_MFC: // 81 XLOG_UNREACHABLE(); // pim_mfc.remove_pim_mfc_entry_mfc(); break; case PimMreTrackState::OUTPUT_STATE_UPDATE_SPTBIT_MFC: // 82 XLOG_UNREACHABLE(); // pim_mfc.recompute_update_sptbit_mfc(); break; case PimMreTrackState::OUTPUT_STATE_SET_KEEPALIVE_TIMER_SG: // 83 pim_mre.recompute_set_keepalive_timer_sg(); break; default: XLOG_UNREACHABLE(); break; } } void PimMreAction::perform_action(PimMfc& pim_mfc) { switch (output_state()) { case PimMreTrackState::OUTPUT_STATE_RP_MFC: // 3 pim_mfc.recompute_rp_mfc(); break; case PimMreTrackState::OUTPUT_STATE_IIF_OLIST_MFC: // 17 pim_mfc.recompute_iif_olist_mfc(); break; case PimMreTrackState::OUTPUT_STATE_MONITORING_SWITCH_TO_SPT_DESIRED_MFC: // 18 pim_mfc.recompute_monitoring_switch_to_spt_desired_mfc(); break; case PimMreTrackState::OUTPUT_STATE_SPT_SWITCH_THRESHOLD_CHANGED_MFC: // 19 pim_mfc.recompute_spt_switch_threshold_changed_mfc(); break; case PimMreTrackState::OUTPUT_STATE_OUT_REMOVE_PIM_MFC_ENTRY_MFC: // 81 pim_mfc.remove_pim_mfc_entry_mfc(); break; case PimMreTrackState::OUTPUT_STATE_UPDATE_SPTBIT_MFC: // 82 pim_mfc.recompute_update_sptbit_mfc(); break; default: XLOG_UNREACHABLE(); break; } } xorp/pim/pim_node.cc0000664000076400007640000020453411703345405014560 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2012 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // Protocol Independent Multicast (both PIM-SM and PIM-DM) // node implementation (common part). // PIM-SMv2 (draft-ietf-pim-sm-new-*), PIM-DM (new draft pending). // #include "pim_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvx.hh" #include "fea/mfea_kernel_messages.hh" // TODO: XXX: yuck! #include "pim_mre.hh" #include "pim_node.hh" #include "pim_vif.hh" /** * PimNode::PimNode: * @family: The address family (%AF_INET or %AF_INET6 * for IPv4 and IPv6 respectively). * @module_id: The module ID (must be either %XORP_MODULE_PIMSM * or %XORP_MODULE_PIMDM). * TODO: XXX: XORP_MODULE_PIMDM is not implemented yet. * @eventloop: The event loop. * * PIM node constructor. **/ PimNode::PimNode(int family, xorp_module_id module_id, EventLoop& eventloop) : ProtoNode(family, module_id, eventloop), _pim_mrt(this), _pim_mrib_table(*this), _rp_table(*this), _pim_scope_zone_table(*this), _pim_bsr(*this), _is_switch_to_spt_enabled(false), // XXX: disabled by defailt _switch_to_spt_threshold_interval_sec(0), _switch_to_spt_threshold_bytes(0), _is_log_trace(false) { // TODO: XXX: PIMDM not implemented yet XLOG_ASSERT(module_id == XORP_MODULE_PIMSM); XLOG_ASSERT((module_id == XORP_MODULE_PIMSM) || (module_id == XORP_MODULE_PIMDM)); if ((module_id != XORP_MODULE_PIMSM) && (module_id != XORP_MODULE_PIMDM)) { XLOG_FATAL("Invalid module ID = %d (must be 'XORP_MODULE_PIMSM' = %d " "or 'XORP_MODULE_PIMDM' = %d", module_id, XORP_MODULE_PIMSM, XORP_MODULE_PIMDM); } _pim_register_vif_index = Vif::VIF_INDEX_INVALID; _buffer_recv = BUFFER_MALLOC(BUF_SIZE_DEFAULT); // // Set the node status // ProtoNode::set_node_status(PROC_STARTUP); // // Set myself as an observer when the node status changes // set_observer(this); } void PimNode::destruct_me() { // // Unset myself as an observer when the node status changes // unset_observer(this); stop(); // // XXX: Explicitly clear the PimBsr and RpTable now to avoid // cross-referencing of lists that may be deleted prematurely // at the end of the PimNode destructor. // _pim_bsr.clear(); _rp_table.clear(); // // XXX: explicitly clear the PimMrt table now, because PimMrt may utilize // some lists in the PimNode class (e.g., _processing_pim_nbr_list) that // may be deleted prematurely at the end of the PimNode destructor // (depending on the declaration ordering). // _pim_mrt.clear(); ProtoNode::set_node_status(PROC_NULL); delete_all_vifs(); if (_buffer_recv) BUFFER_FREE(_buffer_recv); } /** * PimNode::~PimNode: * @: * * PIM node destructor. * **/ PimNode::~PimNode() { destruct_me(); } /** * PimNode::start: * @: * * Start the PIM protocol. * TODO: This function should not start the protocol operation on the * interfaces. The interfaces must be activated separately. * After the startup operations are completed, * PimNode::final_start() is called to complete the job. * * Return value: %XORP_OK on success, otherwize %XORP_ERROR. **/ int PimNode::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() != XORP_OK) return (XORP_ERROR); // // Register with the FEA and MFEA // fea_register_startup(); mfea_register_startup(); // // Register with the RIB // rib_register_startup(); // // Set the node status // ProtoNode::set_node_status(PROC_STARTUP); // // Update the node status // update_status(); return (XORP_OK); } /** * PimNode::final_start: * @: * * Complete the start-up of the PIM protocol. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int PimNode::final_start() { #if 0 // TODO: XXX: PAVPAVPAV if (! is_pending_up()) return (XORP_ERROR); #endif if (ProtoNode::start() != XORP_OK) { ProtoNode::stop(); return (XORP_ERROR); } // Start the pim_vifs start_all_vifs(); // Start the BSR module if (_pim_bsr.start() != XORP_OK) return (XORP_ERROR); XLOG_INFO("Protocol started"); return (XORP_OK); } /** * PimNode::stop: * @: * * Gracefully stop the PIM protocol. * XXX: The graceful stop will attempt to send Join/Prune, Assert, etc. * messages for all multicast routing entries to gracefully clean-up * state with neighbors. * XXX: After the multicast routing entries cleanup is completed, * PimNode::final_stop() is called to complete the job. * XXX: This function, unlike start(), will stop the protocol * operation on all interfaces. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int PimNode::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() != XORP_OK) return (XORP_ERROR); // // Perform misc. PIM-specific stop operations // _pim_bsr.stop(); // Stop the vifs stop_all_vifs(); // // Set the node status // ProtoNode::set_node_status(PROC_SHUTDOWN); // // Update the node status // update_status(); return (XORP_OK); } /** * PimNode::final_stop: * @: * * Completely stop the PIM protocol. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int PimNode::final_stop() { if (! (is_up() || is_pending_up() || is_pending_down())) return (XORP_ERROR); if (ProtoNode::stop() != XORP_OK) return (XORP_ERROR); XLOG_INFO("Protocol stopped"); return (XORP_OK); } /** * Enable the node operation. * * If an unit is not enabled, it cannot be start, or pending-start. */ void PimNode::enable() { ProtoUnit::enable(); XLOG_INFO("Protocol 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 PimNode::disable() { stop(); ProtoUnit::disable(); XLOG_INFO("Protocol disabled"); } /** * Get the IP protocol number. * * @return the IP protocol number. */ uint8_t PimNode::ip_protocol_number() const { return (IPPROTO_PIM); } void PimNode::status_change(ServiceBase* service, ServiceStatus old_status, ServiceStatus new_status) { if (service == this) { if ((old_status == SERVICE_STARTING) && (new_status == SERVICE_RUNNING)) { // The startup process has completed if (final_start() != XORP_OK) { 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)) { decr_shutdown_requests_n(); // XXX: for the ifmgr } } } void PimNode::tree_complete() { decr_startup_requests_n(); // XXX: for the ifmgr // // XXX: we use same actions when the tree is completed or updates are made // updates_made(); } void PimNode::updates_made() { map::iterator pim_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.interfaces().begin(); ifmgr_iface_iter != _iftree.interfaces().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; pim_vif_iter = configured_vifs().find(ifmgr_vif_name); if (pim_vif_iter != configured_vifs().end()) { node_vif = &(pim_vif_iter->second); } // // Add a new vif // if (node_vif == NULL) { uint32_t vif_index = ifmgr_vif.vif_index(); XLOG_ASSERT(vif_index != Vif::VIF_INDEX_INVALID); if (add_config_vif(ifmgr_vif_name, vif_index, error_msg) != XORP_OK) { XLOG_ERROR("Cannot add vif %s to the set of configured " "vifs: %s", ifmgr_vif_name.c_str(), error_msg.c_str()); continue; } pim_vif_iter = configured_vifs().find(ifmgr_vif_name); XLOG_ASSERT(pim_vif_iter != configured_vifs().end()); node_vif = &(pim_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_vif.enabled(); set_config_vif_flags(ifmgr_vif_name, ifmgr_vif.pim_register(), ifmgr_vif.p2p_capable(), ifmgr_vif.loopback(), ifmgr_vif.multicast_capable(), ifmgr_vif.broadcast_capable(), is_up, ifmgr_iface.mtu(), error_msg); } } // // Add new vif addresses, update existing ones, and remove old addresses // for (ifmgr_iface_iter = _iftree.interfaces().begin(); ifmgr_iface_iter != _iftree.interfaces().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 // pim_vif_iter = configured_vifs().find(ifmgr_vif_name); if (pim_vif_iter != configured_vifs().end()) { node_vif = &(pim_vif_iter->second); } if (is_ipv4()) { IfMgrVifAtom::IPv4Map::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) != XORP_OK) { 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) != XORP_OK) { 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) != XORP_OK) { 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::IPv6Map::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) != XORP_OK) { 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) != XORP_OK) { 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) != XORP_OK) { 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) != XORP_OK) { 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 (pim_vif_iter = configured_vifs().begin(); pim_vif_iter != configured_vifs().end(); ++pim_vif_iter) { Vif* node_vif = &pim_vif_iter->second; #if 0 if (node_vif->is_pim_register()) continue; // XXX: don't delete the PIM Register vif #endif 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) != XORP_OK) { 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); } /** * PimNode::add_vif: * @vif: Information about the new PimVif to install. * @error_msg: The error message (if error). * * Install a new PIM vif. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int PimNode::add_vif(const Vif& vif, string& error_msg) { // // Create a new PimVif // PimVif *pim_vif = new PimVif(this, vif); if (ProtoNode::add_vif(pim_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 pim_vif; return (XORP_ERROR); } // Set the PIM Register vif index if needed if (pim_vif->is_pim_register()) _pim_register_vif_index = pim_vif->vif_index(); // // Resolve all destination prefixes whose next-hop vif name was not // resolved earlier (e.g., the vif was unknown). // _pim_mrib_table.resolve_prefixes_by_vif_name(pim_vif->name(), pim_vif->vif_index()); // // Update and check the primary and domain-wide addresses // do { if (pim_vif->update_primary_and_domain_wide_address(error_msg) == XORP_OK) { break; } if (pim_vif->addr_ptr() == NULL) { // XXX: don't print an error if the vif has no addresses break; } if (pim_vif->is_loopback()) { // XXX: don't print an error if this is a loopback interface break; } XLOG_ERROR("Error updating primary and domain-wide addresses " "for vif %s: %s", pim_vif->name().c_str(), error_msg.c_str()); return (XORP_ERROR); } while (false); XLOG_INFO("Interface added: %s", pim_vif->str().c_str()); return (XORP_OK); } /** * PimNode::add_vif: * @vif_name: The name of the new vif. * @vif_index: The vif index of the new vif. * @error_msg: The error message (if error). * * Install a new PIM vif. If the vif exists, nothing is installed. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int PimNode::add_vif(const string& vif_name, uint32_t vif_index, string& error_msg) { PimVif *pim_vif = vif_find_by_vif_index(vif_index); if ((pim_vif != NULL) && (pim_vif->name() == vif_name)) { return (XORP_OK); // Already have this vif } // // Create a new Vif // Vif vif(vif_name); vif.set_vif_index(vif_index); return add_vif(vif, error_msg); } /** * PimNode::delete_vif: * @vif_name: The name of the vif to delete. * @error_msg: The error message (if error). * * Delete an existing PIM vif. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int PimNode::delete_vif(const string& vif_name, string& error_msg) { PimVif *pim_vif = vif_find_by_name(vif_name); if (pim_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(pim_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 pim_vif; return (XORP_ERROR); } // Reset the PIM Register vif index if needed if (_pim_register_vif_index == pim_vif->vif_index()) _pim_register_vif_index = Vif::VIF_INDEX_INVALID; delete pim_vif; XLOG_INFO("Interface deleted: %s", vif_name.c_str()); return (XORP_OK); } int PimNode::set_vif_flags(const string& vif_name, bool is_pim_register, bool is_p2p, bool is_loopback, bool is_multicast, bool is_broadcast, bool is_up, uint32_t mtu, string& error_msg) { bool is_changed = false; PimVif *pim_vif = vif_find_by_name(vif_name); if (pim_vif == NULL) { error_msg = c_format("Cannot set flags vif %s: no such vif", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } if (pim_vif->is_pim_register() != is_pim_register) { pim_vif->set_pim_register(is_pim_register); is_changed = true; } if (pim_vif->is_p2p() != is_p2p) { pim_vif->set_p2p(is_p2p); is_changed = true; } if (pim_vif->is_loopback() != is_loopback) { pim_vif->set_loopback(is_loopback); is_changed = true; } if (pim_vif->is_multicast_capable() != is_multicast) { pim_vif->set_multicast_capable(is_multicast); is_changed = true; } if (pim_vif->is_broadcast_capable() != is_broadcast) { pim_vif->set_broadcast_capable(is_broadcast); is_changed = true; } if (pim_vif->is_underlying_vif_up() != is_up) { pim_vif->set_underlying_vif_up(is_up); is_changed = true; } if (pim_vif->mtu() != mtu) { pim_vif->set_mtu(mtu); is_changed = true; } // Set the PIM Register vif index if needed if (pim_vif->is_pim_register()) _pim_register_vif_index = pim_vif->vif_index(); if (is_changed) { XLOG_INFO("Interface flags changed: %s", pim_vif->str().c_str()); pim_vif->notifyUpdated(); } return (XORP_OK); } int PimNode::add_vif_addr(const string& vif_name, const IPvX& addr, const IPvXNet& subnet_addr, const IPvX& broadcast_addr, const IPvX& peer_addr, bool& should_send_hello, string &error_msg) { PimVif *pim_vif = vif_find_by_name(vif_name); should_send_hello = false; if (pim_vif == NULL) { error_msg = c_format("Cannot add address on vif %s: no such vif", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } const VifAddr vif_addr(addr, subnet_addr, broadcast_addr, peer_addr); // // Check the arguments // if (! addr.is_unicast()) { error_msg = c_format("Cannot add address on vif %s: " "invalid unicast address: %s", vif_name.c_str(), addr.str().c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } if ((addr.af() != family()) || (subnet_addr.af() != family()) || (broadcast_addr.af() != family()) || (peer_addr.af() != family())) { error_msg = c_format("Cannot add address on vif %s: " "invalid address family: %s ", vif_name.c_str(), vif_addr.str().c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } VifAddr* node_vif_addr = pim_vif->find_address(addr); if ((node_vif_addr != NULL) && (*node_vif_addr == vif_addr)) return (XORP_OK); // Already have this address // // Spec: // "Before an interface goes down or changes primary IP address, a Hello // message with a zero HoldTime should be sent immediately (with the old IP // address if the IP address changed)." // // However, by adding or updating an existing address we cannot // change a valid primary address, hence we do nothing here. // if (node_vif_addr != NULL) { // Update the address XLOG_INFO("Updated existing address on interface %s: " "old is %s new is %s", pim_vif->name().c_str(), node_vif_addr->str().c_str(), vif_addr.str().c_str()); *node_vif_addr = vif_addr; } else { // Add a new address pim_vif->add_address(vif_addr); XLOG_INFO("Added new address to interface %s: %s", pim_vif->name().c_str(), vif_addr.str().c_str()); } // // Update and check the primary and domain-wide addresses // do { if (pim_vif->update_primary_and_domain_wide_address(error_msg) == XORP_OK) { break; } if (! (pim_vif->is_up() || pim_vif->is_pending_up())) { // XXX: print an error only if the interface is UP or PENDING_UP break; } if (pim_vif->is_loopback()) { // XXX: don't print an error if this is a loopback interface break; } XLOG_ERROR("Error updating primary and domain-wide addresses " "for vif %s: %s", pim_vif->name().c_str(), error_msg.c_str()); return (XORP_ERROR); } while (false); // // Spec: // "If an interface changes one of its secondary IP addresses, // a Hello message with an updated Address_List option and a // non-zero HoldTime should be sent immediately." // if (pim_vif->is_up()) { // pim_vif->pim_hello_send(); should_send_hello = true; } // Schedule the dependency-tracking tasks pim_mrt().add_task_my_ip_address(pim_vif->vif_index()); pim_mrt().add_task_my_ip_subnet_address(pim_vif->vif_index()); // // Inform the BSR about the change // pim_bsr().add_vif_addr(pim_vif->vif_index(), addr); // Let the VIF know it was updated..might want to start itself // if it was waiting on an IP address. pim_vif->notifyUpdated(); return (XORP_OK); } int PimNode::delete_vif_addr(const string& vif_name, const IPvX& addr, bool& should_send_hello, string& error_msg) { PimVif *pim_vif = vif_find_by_name(vif_name); should_send_hello = false; if (pim_vif == NULL) { error_msg = c_format("Cannot delete address on vif %s: no such vif", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } const VifAddr *tmp_vif_addr = pim_vif->find_address(addr); if (tmp_vif_addr == NULL) { error_msg = c_format("Cannot delete address on vif %s: " "invalid address %s", vif_name.c_str(), addr.str().c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } VifAddr vif_addr = *tmp_vif_addr; // Get a copy // // Get the vif's old primary address and whether the vif is UP // bool old_vif_is_up = pim_vif->is_up() || pim_vif->is_pending_up(); IPvX old_primary_addr = pim_vif->primary_addr(); // // Spec: // "Before an interface goes down or changes primary IP address, a Hello // message with a zero HoldTime should be sent immediately (with the old IP // address if the IP address changed)." // if (pim_vif->is_up()) { if (pim_vif->primary_addr() == addr) { pim_vif->pim_hello_stop(); } } // // If an interface's primary address is deleted, first stop the vif. // if (old_vif_is_up) { if (pim_vif->primary_addr() == addr) { string dummy_error_msg; pim_vif->stop(dummy_error_msg); } } if (pim_vif->delete_address(addr) != XORP_OK) { XLOG_UNREACHABLE(); return (XORP_ERROR); } XLOG_INFO("Deleted address on interface %s: %s", pim_vif->name().c_str(), vif_addr.str().c_str()); // // Update and check the primary and domain-wide addresses. // If the vif has no primary or a domain-wide address, then stop it. // If the vif's primary address was changed, then restart the vif. // do { string dummy_error_msg; if (pim_vif->update_primary_and_domain_wide_address(error_msg) != XORP_OK) { XLOG_ERROR("Error updating primary and domain-wide addresses " "for vif %s: %s", pim_vif->name().c_str(), error_msg.c_str()); } if (pim_vif->primary_addr().is_zero() || pim_vif->domain_wide_addr().is_zero()) { pim_vif->stop(dummy_error_msg); break; } if (old_primary_addr == pim_vif->primary_addr()) break; // Nothing changed // Conditionally restart the interface pim_vif->stop(dummy_error_msg); if (old_vif_is_up) pim_vif->start(dummy_error_msg); break; } while (false); // // Spec: // "If an interface changes one of its secondary IP addresses, // a Hello message with an updated Address_List option and a // non-zero HoldTime should be sent immediately." // if (pim_vif->is_up()) { // pim_vif->pim_hello_send(); should_send_hello = true; } // Schedule the dependency-tracking tasks pim_mrt().add_task_my_ip_address(pim_vif->vif_index()); pim_mrt().add_task_my_ip_subnet_address(pim_vif->vif_index()); // // Inform the BSR about the change // pim_bsr().delete_vif_addr(pim_vif->vif_index(), addr); return (XORP_OK); } /** * PimNode::enable_vif: * @vif_name: The name of the vif to enable. * @error_msg: The error message (if error). * * Enable an existing PIM vif. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int PimNode::enable_vif(const string& vif_name, string& error_msg) { PimVif *pim_vif = vif_find_by_name(vif_name); if (pim_vif == NULL) { // Seems we have some sort of race...it's asked to be enabled before it is // created on config-file reload. // For now, create it manually. // TODO: Fix enable-vif on cfg-file load. // NOTE: mld6igmp has similar issue and similar work-around. error_msg = c_format("PimNode: Cannot enable vif %s: no such vif (will try to create one)", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); int if_index = -1; errno = 0; #ifdef HAVE_IF_NAMETOINDEX if_index = if_nametoindex(vif_name.c_str()); #endif if (if_index < 0) { XLOG_ERROR("Could not convert vif_name to ifindex: %s possible error: %s\n", vif_name.c_str(), strerror(errno)); return XORP_ERROR; } else { add_vif(vif_name, if_index, error_msg); pim_vif = vif_find_by_name(vif_name); } } pim_vif->enable(); return XORP_OK; } /** * PimNode::disable_vif: * @vif_name: The name of the vif to disable. * @error_msg: The error message (if error). * * Disable an existing PIM vif. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int PimNode::disable_vif(const string& vif_name, string& error_msg) { PimVif *pim_vif = vif_find_by_name(vif_name); if (pim_vif == NULL) { error_msg = c_format("Cannot disable vif %s: no such vif", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); // It's as disabled as it's going to get..don't fail the commit. error_msg = ""; return XORP_OK; } pim_vif->disable(); return (XORP_OK); } /** * PimNode::start_vif: * @vif_name: The name of the vif to start. * @error_msg: The error message (if error). * * Start an existing PIM vif. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int PimNode::start_vif(const string& vif_name, string& error_msg) { PimVif *pim_vif = vif_find_by_name(vif_name); if (pim_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 (pim_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); } return (XORP_OK); } /** * PimNode::stop_vif: * @vif_name: The name of the vif to stop. * @error_msg: The error message (if error). * * Stop an existing PIM vif. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int PimNode::stop_vif(const string& vif_name, string& error_msg) { PimVif *pim_vif = vif_find_by_name(vif_name); if (pim_vif == NULL) { error_msg = c_format("Cannot stop vif %s: no such vif (will continue)", vif_name.c_str()); XLOG_INFO("%s", error_msg.c_str()); return XORP_OK; } if (pim_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); } /** * PimNode::start_all_vifs: * @: * * Start PIM on all enabled interfaces. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int PimNode::start_all_vifs() { vector::iterator iter; string error_msg; int ret_value = XORP_OK; for (iter = proto_vifs().begin(); iter != proto_vifs().end(); ++iter) { PimVif *pim_vif = (*iter); if (pim_vif == NULL) continue; if (start_vif(pim_vif->name(), error_msg) != XORP_OK) ret_value = XORP_ERROR; } return (ret_value); } /** * PimNode::stop_all_vifs: * @: * * Stop PIM on all interfaces it was running on. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int PimNode::stop_all_vifs() { vector::iterator iter; string error_msg; int ret_value = XORP_OK; for (iter = proto_vifs().begin(); iter != proto_vifs().end(); ++iter) { PimVif *pim_vif = (*iter); if (pim_vif == NULL) continue; if (stop_vif(pim_vif->name(), error_msg) != XORP_OK) ret_value = XORP_ERROR; } return (ret_value); } /** * PimNode::enable_all_vifs: * @: * * Enable PIM on all interfaces. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int PimNode::enable_all_vifs() { vector::iterator iter; string error_msg; int ret_value = XORP_OK; for (iter = proto_vifs().begin(); iter != proto_vifs().end(); ++iter) { PimVif *pim_vif = (*iter); if (pim_vif == NULL) continue; if (enable_vif(pim_vif->name(), error_msg) != XORP_OK) ret_value = XORP_ERROR; } return (ret_value); } /** * PimNode::disable_all_vifs: * @: * * Disable PIM on all interfaces. All running interfaces are stopped first. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int PimNode::disable_all_vifs() { vector::iterator iter; string error_msg; int ret_value = XORP_OK; for (iter = proto_vifs().begin(); iter != proto_vifs().end(); ++iter) { PimVif *pim_vif = (*iter); if (pim_vif == NULL) continue; if (disable_vif(pim_vif->name(), error_msg) != XORP_OK) ret_value = XORP_ERROR; } return (ret_value); } /** * PimNode::delete_all_vifs: * @: * * Delete all PIM vifs. **/ void PimNode::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) { PimVif *pim_vif = (*iter); if (pim_vif != NULL) { string vif_name = pim_vif->name(); vif_names.push_back(pim_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 PimNode::vif_shutdown_completed(const string& vif_name) { vector::iterator iter; // // If all vifs have completed the shutdown, then de-register with // the RIB and the MFEA. // for (iter = proto_vifs().begin(); iter != proto_vifs().end(); ++iter) { PimVif *pim_vif = *iter; if (pim_vif == NULL) continue; if (! pim_vif->is_down()) return; } if (ServiceBase::status() == SERVICE_SHUTTING_DOWN) { // // De-register with the RIB // rib_register_shutdown(); // // De-register with the FEA and MFEA // mfea_register_shutdown(); fea_register_shutdown(); } UNUSED(vif_name); } int PimNode::proto_recv(const string& if_name, const string& vif_name, const IPvX& src_address, const IPvX& dst_address, uint8_t ip_protocol, int32_t ip_ttl, int32_t ip_tos, bool ip_router_alert, bool ip_internet_control, const vector& payload, string& error_msg) { PimVif *pim_vif = NULL; int ret_value = XORP_ERROR; debug_msg("Received message on %s/%s from %s to %s: " "ip_ttl = %d ip_tos = %#x ip_router_alert = %d " "ip_internet_control = %d rcvlen = %u\n", if_name.c_str(), vif_name.c_str(), cstring(src_address), cstring(dst_address), ip_ttl, ip_tos, ip_router_alert, ip_internet_control, XORP_UINT_CAST(payload.size())); UNUSED(if_name); UNUSED(ip_ttl); UNUSED(ip_tos); UNUSED(ip_router_alert); UNUSED(ip_internet_control); // // XXX: We registered to receive only one protocol, hence we ignore // the ip_protocol value. // UNUSED(ip_protocol); // // Check whether the node is up. // if (! is_up()) { error_msg = c_format("PIM node is not UP"); return (XORP_ERROR); } // // Find the vif for that packet // pim_vif = vif_find_by_name(vif_name); if (pim_vif == NULL) { error_msg = c_format("Cannot find vif with vif_name = %s", vif_name.c_str()); return (XORP_ERROR); } // Copy the data to the receiving #buffer_t BUFFER_RESET(_buffer_recv); BUFFER_PUT_DATA(&payload[0], _buffer_recv, payload.size()); // Process the data by the vif ret_value = pim_vif->pim_recv(src_address, dst_address, _buffer_recv); return (ret_value); buflen_error: XLOG_UNREACHABLE(); return (XORP_ERROR); } int PimNode::pim_send(const string& if_name, const string& vif_name, const IPvX& src_address, const IPvX& dst_address, uint8_t ip_protocol, int32_t ip_ttl, int32_t ip_tos, bool ip_router_alert, bool ip_internet_control, buffer_t *buffer, string& error_msg) { if (! (is_up() || is_pending_down())) { error_msg = c_format("PimNode::pim_send MLD/IGMP node is not UP"); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } if (proto_send(if_name, vif_name, src_address, dst_address, ip_protocol, ip_ttl, ip_tos, ip_router_alert, ip_internet_control, BUFFER_DATA_HEAD(buffer), BUFFER_DATA_SIZE(buffer), error_msg) != XORP_OK) { return (XORP_ERROR); } return (XORP_OK); } /** * PimNode::signal_message_recv: * @src_module_instance_name: The module instance name of the module-origin * of the message. * @message_type: The message type of the kernel signal. * At this moment, one of the following: * %MFEA_KERNEL_MESSAGE_NOCACHE (if a cache-miss in the kernel) * %MFEA_KERNEL_MESSAGE_WRONGVIF (multicast packet received on wrong vif) * %MFEA_KERNEL_MESSAGE_WHOLEPKT (typically, a packet that should be * encapsulated as a PIM-Register). * @vif_index: The vif index of the related interface (message-specific * relation). * @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. * * Receive a signal from the kernel (e.g., NOCACHE, WRONGVIF, WHOLEPKT). * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int PimNode::signal_message_recv(const string& src_module_instance_name, int message_type, uint32_t vif_index, const IPvX& src, const IPvX& dst, const uint8_t *rcvbuf, size_t rcvlen) { int ret_value = XORP_ERROR; do { if (message_type == MFEA_KERNEL_MESSAGE_NOCACHE) { ret_value = pim_mrt().signal_message_nocache_recv( src_module_instance_name, vif_index, src, dst); break; } if (message_type == MFEA_KERNEL_MESSAGE_WRONGVIF) { ret_value = pim_mrt().signal_message_wrongvif_recv( src_module_instance_name, vif_index, src, dst); break; } if (message_type == MFEA_KERNEL_MESSAGE_WHOLEPKT) { ret_value = pim_mrt().signal_message_wholepkt_recv( src_module_instance_name, vif_index, src, dst, rcvbuf, rcvlen); break; } XLOG_WARNING("RX unknown signal from %s: " "vif_index = %d src = %s dst = %s message_type = %d", src_module_instance_name.c_str(), vif_index, cstring(src), cstring(dst), message_type); return (XORP_ERROR); } while (false); return (ret_value); } /** * PimNode::add_membership: * @vif_index: The vif_index of the interface to add membership. * @source: The source address to add membership for * (IPvX::ZERO() for IGMPv1,2 and MLDv1). * @group: The group address to add membership for. * * Add multicast membership on vif with vif_index of @vif_index for * source address of @source and group address of @group. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int PimNode::add_membership(uint32_t vif_index, const IPvX& source, const IPvX& group) { uint32_t lookup_flags = 0; uint32_t create_flags = 0; PimVif *pim_vif = NULL; bool is_ssm = false; if (source != IPvX::ZERO(family())) is_ssm = true; // // Check the arguments: the vif, source and group addresses. // pim_vif = vif_find_by_vif_index(vif_index); if (pim_vif == NULL) return (XORP_ERROR); if (! (pim_vif->is_up() || pim_vif->is_pending_up())) { return (XORP_ERROR); // The vif is (going) DOWN } if (source != IPvX::ZERO(family())) { if (! source.is_unicast()) return (XORP_ERROR); } if (! group.is_multicast()) return (XORP_ERROR); if (group.is_linklocal_multicast() || group.is_interfacelocal_multicast()) { // XXX: don't route link or interface-local groups return (XORP_OK); } XLOG_TRACE(is_log_trace(), "Add membership for (%s, %s) on vif %s", cstring(source), cstring(group), pim_vif->name().c_str()); // // Setup the MRE lookup and create flags // if (is_ssm) lookup_flags |= PIM_MRE_SG; else lookup_flags |= PIM_MRE_WC; create_flags = lookup_flags; PimMre *pim_mre = pim_mrt().pim_mre_find(source, group, lookup_flags, create_flags); if (pim_mre == NULL) return (XORP_ERROR); // // Modify to the local membership state // if (is_ssm) { // // (S, G) Join // // XXX: If the source was excluded, then don't exclude it anymore. // Otherwise, include the source. // XLOG_ASSERT(pim_mre->is_sg()); if (pim_mre->local_receiver_exclude_sg().test(vif_index)) { pim_mre->set_local_receiver_exclude(vif_index, false); } else { pim_mre->set_local_receiver_include(vif_index, true); } } else { // // (*,G) Join // pim_mre->set_local_receiver_include(vif_index, true); } return (XORP_OK); } /** * PimNode::delete_membership: * @vif_index: The vif_index of the interface to delete membership. * @source: The source address to delete membership for * (IPvX::ZERO() for IGMPv1,2 and MLDv1). * @group: The group address to delete membership for. * * Delete multicast membership on vif with vif_index of @vif_index for * source address of @source and group address of @group. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int PimNode::delete_membership(uint32_t vif_index, const IPvX& source, const IPvX& group) { uint32_t lookup_flags = 0; uint32_t create_flags = 0; PimVif *pim_vif = NULL; bool is_ssm = false; if (source != IPvX::ZERO(family())) is_ssm = true; // // Check the arguments: the vif, source and group addresses. // pim_vif = vif_find_by_vif_index(vif_index); if (pim_vif == NULL) return (XORP_ERROR); if (! (pim_vif->is_up() || pim_vif->is_pending_down() || pim_vif->is_pending_up())) { return (XORP_ERROR); // The vif is DOWN } if (source != IPvX::ZERO(family())) { if (! source.is_unicast()) return (XORP_ERROR); } if (! group.is_multicast()) return (XORP_ERROR); if (group.is_linklocal_multicast() || group.is_interfacelocal_multicast()) { // XXX: don't route link or interface-local groups return (XORP_OK); } XLOG_TRACE(is_log_trace(), "Delete membership for (%s, %s) on vif %s", cstring(source), cstring(group), pim_vif->name().c_str()); // // Setup the MRE lookup and create flags // if (is_ssm) { lookup_flags |= PIM_MRE_SG; create_flags = lookup_flags; // XXX: create an entry for (S,G) Prune } else { lookup_flags |= PIM_MRE_WC; create_flags = 0; } PimMre *pim_mre = pim_mrt().pim_mre_find(source, group, lookup_flags, create_flags); if (pim_mre == NULL) return (XORP_ERROR); // // Modify the local membership state // if (is_ssm) { // // (S, G) Prune // // XXX: If the source was included, then don't include it anymore. // Otherwise, exclude the source. // XLOG_ASSERT(pim_mre->is_sg()); if (pim_mre->local_receiver_include_sg().test(vif_index)) { pim_mre->set_local_receiver_include(vif_index, false); } else { pim_mre->set_local_receiver_exclude(vif_index, true); } } else { // // (*,G) Prune // pim_mre->set_local_receiver_include(vif_index, false); } return (XORP_OK); } /** * PimNode::is_directly_connected: * @pim_vif: The virtual interface to test against. * @ipaddr_test: The address to test. * * Note that the virtual interface the address is directly connected to * must be UP. * * Return value: True if @ipaddr_test is directly connected to @pim_vif, * otherwise false. **/ bool PimNode::is_directly_connected(const PimVif& pim_vif, const IPvX& ipaddr_test) const { if (! pim_vif.is_up()) return (false); // // Test the alternative subnets // list::const_iterator iter; for (iter = pim_vif.alternative_subnet_list().begin(); iter != pim_vif.alternative_subnet_list().end(); ++iter) { const IPvXNet& ipvxnet = *iter; if (ipvxnet.contains(ipaddr_test)) return true; } // // Test the same subnet addresses, or the P2P addresses // return (pim_vif.is_same_subnet(ipaddr_test) || pim_vif.is_same_p2p(ipaddr_test)); } /** * PimNode::vif_find_pim_register: * @: * * Return the PIM Register virtual interface. * * Return value: The PIM Register virtual interface if exists, otherwise NULL. **/ PimVif * PimNode::vif_find_pim_register() const { return (vif_find_by_vif_index(pim_register_vif_index())); } /** * PimNode::set_pim_vifs_dr: * @vif_index: The vif index to set/reset the DR flag. * @v: true if set the DR flag, otherwise false. * * Set/reset the DR flag for vif index @vif_index. **/ void PimNode::set_pim_vifs_dr(uint32_t vif_index, bool v) { if (vif_index >= pim_vifs_dr().size()) return; // TODO: return an error instead? if (pim_vifs_dr().test(vif_index) == v) return; // Nothing changed if (v) pim_vifs_dr().set(vif_index); else pim_vifs_dr().reset(vif_index); pim_mrt().add_task_i_am_dr(vif_index); } /** * PimNode::pim_vif_rpf_find: * @dst_addr: The address of the destination to search for. * * Find the RPF virtual interface for a destination address. * * Return value: The #PimVif entry for the RPF virtual interface to @dst_addr * if found, otherwise %NULL. **/ PimVif * PimNode::pim_vif_rpf_find(const IPvX& dst_addr) { Mrib *mrib; PimVif *pim_vif; // // Do the MRIB lookup // mrib = pim_mrib_table().find(dst_addr); if (mrib == NULL) return (NULL); // // Find the vif toward the destination address // pim_vif = vif_find_by_vif_index(mrib->next_hop_vif_index()); return (pim_vif); } /** * PimNode::pim_nbr_rpf_find: * @dst_addr: The address of the destination to search for. * * Find the RPF PIM neighbor for a destination address. * * Return value: The #PimNbr entry for the RPF neighbor to @dst_addr if found, * otherwise %NULL. **/ PimNbr * PimNode::pim_nbr_rpf_find(const IPvX& dst_addr) { Mrib *mrib; // // Do the MRIB lookup // mrib = pim_mrib_table().find(dst_addr); // // Seach for the RPF neighbor router // return (pim_nbr_rpf_find(dst_addr, mrib)); } /** * PimNode::pim_nbr_rpf_find: * @dst_addr: The address of the destination to search for. * @mrib: The MRIB information that was lookup already. * * Find the RPF PIM neighbor for a destination address by using the * information in a pre-lookup MRIB. * * Return value: The #PimNbr entry for the RPF neighbor to @dst_addr if found, * otherwise %NULL. **/ PimNbr * PimNode::pim_nbr_rpf_find(const IPvX& dst_addr, const Mrib *mrib) { bool is_same_subnet = false; PimNbr *pim_nbr = NULL; // // Check the MRIB information // if (mrib == NULL) return (NULL); // // Find the vif toward the destination address // PimVif *pim_vif = vif_find_by_vif_index(mrib->next_hop_vif_index()); // // Test if the destination is on the same subnet. // // Note that we need to capture the case if the next-hop router // address toward a destination on the same subnet is set to one // of the addresses of the interface for that subnet. // do { if (mrib->next_hop_router_addr() == IPvX::ZERO(family())) { is_same_subnet = true; break; } if ((pim_vif != NULL) && pim_vif->is_my_addr(mrib->next_hop_router_addr())) { is_same_subnet = true; break; } break; } while (false); // // Search for the neighbor router // if (is_same_subnet) { // A destination on the same subnet if (pim_vif != NULL) { pim_nbr = pim_vif->pim_nbr_find(dst_addr); } else { // // TODO: XXX: try to remove all calls to pim_nbr_find_global(). // The reason we don't want to search for a neighbor across // all network interfaces only by the neighbor's IP address is // because in case of IPv6 the link-local addresses are unique // only per subnet. In other words, there could be more than one // neighbor routers with the same link-local address. // To get rid of PimNode::pim_nbr_find_global(), we have to make // sure that all valid MRIB entries have a valid next-hop vif // index. // pim_nbr = pim_nbr_find_global(dst_addr); } } else { // Not a destination on the same subnet if (pim_vif != NULL) pim_nbr = pim_vif->pim_nbr_find(mrib->next_hop_router_addr()); } return (pim_nbr); } /** * PimNode::pim_nbr_find_global: * @nbr_addr: The address of the neighbor to search for. * * Find a PIM neighbor by its address. * * Note: this method should be used in very limited cases, because * in case of IPv6 a neighbor's IP address may not be unique within * the PIM neighbor database due to scope issues. * * Return value: The #PimNbr entry for the neighbor if found, otherwise %NULL. **/ PimNbr * PimNode::pim_nbr_find_global(const IPvX& nbr_addr) { for (uint32_t i = 0; i < maxvifs(); i++) { PimVif *pim_vif = vif_find_by_vif_index(i); if (pim_vif == NULL) continue; // Exclude the PIM Register vif (as a safe-guard) if (pim_vif->is_pim_register()) continue; PimNbr *pim_nbr = pim_vif->pim_nbr_find(nbr_addr); if (pim_nbr != NULL) return (pim_nbr); } return (NULL); } // // Add the PimMre to the dummy PimNbr with primary address of IPvX::ZERO() // void PimNode::add_pim_mre_no_pim_nbr(PimMre *pim_mre) { IPvX ipvx_zero(IPvX::ZERO(family())); PimNbr *pim_nbr = NULL; // Find the dummy PimNbr with primary address of IPvX::ZERO() list::iterator iter; for (iter = processing_pim_nbr_list().begin(); iter != processing_pim_nbr_list().end(); ++iter) { pim_nbr = *iter; if (pim_nbr->primary_addr() == ipvx_zero) break; else pim_nbr = NULL; } if (pim_nbr == NULL) { // Find the first vif. Note that the PIM Register vif is excluded. PimVif *pim_vif = NULL; for (uint32_t i = 0; i < maxvifs(); i++) { pim_vif = vif_find_by_vif_index(i); if (pim_vif == NULL) continue; if (pim_vif->is_pim_register()) continue; break; } XLOG_ASSERT(pim_vif != NULL); pim_nbr = new PimNbr(pim_vif, ipvx_zero, PIM_VERSION_DEFAULT); processing_pim_nbr_list().push_back(pim_nbr); } XLOG_ASSERT(pim_nbr != NULL); pim_nbr->add_pim_mre(pim_mre); } // // Delete the PimMre from the dummy PimNbr with primary address of IPvX::ZERO() // void PimNode::delete_pim_mre_no_pim_nbr(PimMre *pim_mre) { IPvX ipvx_zero(IPvX::ZERO(family())); PimNbr *pim_nbr = NULL; // Find the dummy PimNbr with primary address of IPvX::ZERO() list::iterator iter; for (iter = processing_pim_nbr_list().begin(); iter != processing_pim_nbr_list().end(); ++iter) { pim_nbr = *iter; if (pim_nbr->primary_addr() == ipvx_zero) break; else pim_nbr = NULL; } if (pim_nbr != NULL) pim_nbr->delete_pim_mre(pim_mre); } // // Prepare all PimNbr entries with neighbor address of @pim_nbr_add to // process their (*,*,RP) PimMre entries. // void PimNode::init_processing_pim_mre_rp(uint32_t vif_index, const IPvX& pim_nbr_addr) { do { if (vif_index != Vif::VIF_INDEX_INVALID) { PimVif *pim_vif = vif_find_by_vif_index(vif_index); if (pim_vif == NULL) break; PimNbr *pim_nbr = pim_vif->pim_nbr_find(pim_nbr_addr); if (pim_nbr == NULL) break; pim_nbr->init_processing_pim_mre_rp(); return; } } while (false); list::iterator iter; for (iter = processing_pim_nbr_list().begin(); iter != processing_pim_nbr_list().end(); ++iter) { PimNbr *pim_nbr = *iter; if (pim_nbr->primary_addr() == pim_nbr_addr) pim_nbr->init_processing_pim_mre_rp(); } } // // Prepare all PimNbr entries with neighbor address of @pim_nbr_add to // process their (*,G) PimMre entries. // void PimNode::init_processing_pim_mre_wc(uint32_t vif_index, const IPvX& pim_nbr_addr) { do { if (vif_index != Vif::VIF_INDEX_INVALID) { PimVif *pim_vif = vif_find_by_vif_index(vif_index); if (pim_vif == NULL) break; PimNbr *pim_nbr = pim_vif->pim_nbr_find(pim_nbr_addr); if (pim_nbr == NULL) break; pim_nbr->init_processing_pim_mre_wc(); return; } } while (false); list::iterator iter; for (iter = processing_pim_nbr_list().begin(); iter != processing_pim_nbr_list().end(); ++iter) { PimNbr *pim_nbr = *iter; if (pim_nbr->primary_addr() == pim_nbr_addr) pim_nbr->init_processing_pim_mre_wc(); } } // // Prepare all PimNbr entries with neighbor address of @pim_nbr_add to // process their (S,G) PimMre entries. // void PimNode::init_processing_pim_mre_sg(uint32_t vif_index, const IPvX& pim_nbr_addr) { do { if (vif_index != Vif::VIF_INDEX_INVALID) { PimVif *pim_vif = vif_find_by_vif_index(vif_index); if (pim_vif == NULL) break; PimNbr *pim_nbr = pim_vif->pim_nbr_find(pim_nbr_addr); if (pim_nbr == NULL) break; pim_nbr->init_processing_pim_mre_sg(); return; } } while (false); list::iterator iter; for (iter = processing_pim_nbr_list().begin(); iter != processing_pim_nbr_list().end(); ++iter) { PimNbr *pim_nbr = *iter; if (pim_nbr->primary_addr() == pim_nbr_addr) pim_nbr->init_processing_pim_mre_sg(); } } // // Prepare all PimNbr entries with neighbor address of @pim_nbr_add to // process their (S,G,rpt) PimMre entries. // void PimNode::init_processing_pim_mre_sg_rpt(uint32_t vif_index, const IPvX& pim_nbr_addr) { do { if (vif_index != Vif::VIF_INDEX_INVALID) { PimVif *pim_vif = vif_find_by_vif_index(vif_index); if (pim_vif == NULL) break; PimNbr *pim_nbr = pim_vif->pim_nbr_find(pim_nbr_addr); if (pim_nbr == NULL) break; pim_nbr->init_processing_pim_mre_sg_rpt(); return; } } while (false); list::iterator iter; for (iter = processing_pim_nbr_list().begin(); iter != processing_pim_nbr_list().end(); ++iter) { PimNbr *pim_nbr = *iter; if (pim_nbr->primary_addr() == pim_nbr_addr) pim_nbr->init_processing_pim_mre_sg_rpt(); } } PimNbr * PimNode::find_processing_pim_mre_rp(uint32_t vif_index, const IPvX& pim_nbr_addr) { if (vif_index != Vif::VIF_INDEX_INVALID) { PimVif *pim_vif = vif_find_by_vif_index(vif_index); if (pim_vif == NULL) return (NULL); PimNbr *pim_nbr = pim_vif->pim_nbr_find(pim_nbr_addr); if (pim_nbr == NULL) return (NULL); if (pim_nbr->processing_pim_mre_rp_list().empty()) return (NULL); return (pim_nbr); } list::iterator iter; for (iter = processing_pim_nbr_list().begin(); iter != processing_pim_nbr_list().end(); ++iter) { PimNbr *pim_nbr = *iter; if (pim_nbr->primary_addr() != pim_nbr_addr) continue; if (pim_nbr->processing_pim_mre_rp_list().empty()) continue; return (pim_nbr); } return (NULL); } PimNbr * PimNode::find_processing_pim_mre_wc(uint32_t vif_index, const IPvX& pim_nbr_addr) { if (vif_index != Vif::VIF_INDEX_INVALID) { PimVif *pim_vif = vif_find_by_vif_index(vif_index); if (pim_vif == NULL) return (NULL); PimNbr *pim_nbr = pim_vif->pim_nbr_find(pim_nbr_addr); if (pim_nbr == NULL) return (NULL); if (pim_nbr->processing_pim_mre_wc_list().empty()) return (NULL); return (pim_nbr); } list::iterator iter; for (iter = processing_pim_nbr_list().begin(); iter != processing_pim_nbr_list().end(); ++iter) { PimNbr *pim_nbr = *iter; if (pim_nbr->primary_addr() != pim_nbr_addr) continue; if (pim_nbr->processing_pim_mre_wc_list().empty()) continue; return (pim_nbr); } return (NULL); } PimNbr * PimNode::find_processing_pim_mre_sg(uint32_t vif_index, const IPvX& pim_nbr_addr) { if (vif_index != Vif::VIF_INDEX_INVALID) { PimVif *pim_vif = vif_find_by_vif_index(vif_index); if (pim_vif == NULL) return (NULL); PimNbr *pim_nbr = pim_vif->pim_nbr_find(pim_nbr_addr); if (pim_nbr == NULL) return (NULL); if (pim_nbr->processing_pim_mre_sg_list().empty()) return (NULL); return (pim_nbr); } list::iterator iter; for (iter = processing_pim_nbr_list().begin(); iter != processing_pim_nbr_list().end(); ++iter) { PimNbr *pim_nbr = *iter; if (pim_nbr->primary_addr() != pim_nbr_addr) continue; if (pim_nbr->processing_pim_mre_sg_list().empty()) continue; return (pim_nbr); } return (NULL); } PimNbr * PimNode::find_processing_pim_mre_sg_rpt(uint32_t vif_index, const IPvX& pim_nbr_addr) { if (vif_index != Vif::VIF_INDEX_INVALID) { PimVif *pim_vif = vif_find_by_vif_index(vif_index); if (pim_vif == NULL) return (NULL); PimNbr *pim_nbr = pim_vif->pim_nbr_find(pim_nbr_addr); if (pim_nbr == NULL) return (NULL); if (pim_nbr->processing_pim_mre_sg_rpt_list().empty()) return (NULL); return (pim_nbr); } list::iterator iter; for (iter = processing_pim_nbr_list().begin(); iter != processing_pim_nbr_list().end(); ++iter) { PimNbr *pim_nbr = *iter; if (pim_nbr->primary_addr() != pim_nbr_addr) continue; if (pim_nbr->processing_pim_mre_sg_rpt_list().empty()) continue; return (pim_nbr); } return (NULL); } // // Statistics-related counters and values // void PimNode::clear_pim_statistics() { for (uint32_t i = 0; i < maxvifs(); i++) { PimVif *pim_vif = vif_find_by_vif_index(i); if (pim_vif == NULL) continue; pim_vif->clear_pim_statistics(); } } int PimNode::clear_pim_statistics_per_vif(const string& vif_name, string& error_msg) { PimVif *pim_vif = vif_find_by_name(vif_name); if (pim_vif == NULL) { error_msg = c_format("Cannot get statistics for vif %s: no such vif", vif_name.c_str()); return (XORP_ERROR); } pim_vif->clear_pim_statistics(); return (XORP_OK); } #define GET_PIMSTAT_PER_NODE(stat_name) \ uint32_t \ PimNode::pimstat_##stat_name() const \ { \ uint32_t sum = 0; \ \ for (uint32_t i = 0; i < maxvifs(); i++) { \ PimVif *pim_vif = vif_find_by_vif_index(i); \ if (pim_vif == NULL) \ continue; \ sum += pim_vif->pimstat_##stat_name(); \ } \ \ return (sum); \ } GET_PIMSTAT_PER_NODE(hello_messages_received) GET_PIMSTAT_PER_NODE(hello_messages_sent) GET_PIMSTAT_PER_NODE(hello_messages_rx_errors) GET_PIMSTAT_PER_NODE(register_messages_received) GET_PIMSTAT_PER_NODE(register_messages_sent) GET_PIMSTAT_PER_NODE(register_messages_rx_errors) GET_PIMSTAT_PER_NODE(register_stop_messages_received) GET_PIMSTAT_PER_NODE(register_stop_messages_sent) GET_PIMSTAT_PER_NODE(register_stop_messages_rx_errors) GET_PIMSTAT_PER_NODE(join_prune_messages_received) GET_PIMSTAT_PER_NODE(join_prune_messages_sent) GET_PIMSTAT_PER_NODE(join_prune_messages_rx_errors) GET_PIMSTAT_PER_NODE(bootstrap_messages_received) GET_PIMSTAT_PER_NODE(bootstrap_messages_sent) GET_PIMSTAT_PER_NODE(bootstrap_messages_rx_errors) GET_PIMSTAT_PER_NODE(assert_messages_received) GET_PIMSTAT_PER_NODE(assert_messages_sent) GET_PIMSTAT_PER_NODE(assert_messages_rx_errors) GET_PIMSTAT_PER_NODE(graft_messages_received) GET_PIMSTAT_PER_NODE(graft_messages_sent) GET_PIMSTAT_PER_NODE(graft_messages_rx_errors) GET_PIMSTAT_PER_NODE(graft_ack_messages_received) GET_PIMSTAT_PER_NODE(graft_ack_messages_sent) GET_PIMSTAT_PER_NODE(graft_ack_messages_rx_errors) GET_PIMSTAT_PER_NODE(candidate_rp_messages_received) GET_PIMSTAT_PER_NODE(candidate_rp_messages_sent) GET_PIMSTAT_PER_NODE(candidate_rp_messages_rx_errors) // GET_PIMSTAT_PER_NODE(unknown_type_messages) GET_PIMSTAT_PER_NODE(unknown_version_messages) GET_PIMSTAT_PER_NODE(neighbor_unknown_messages) GET_PIMSTAT_PER_NODE(bad_length_messages) GET_PIMSTAT_PER_NODE(bad_checksum_messages) GET_PIMSTAT_PER_NODE(bad_receive_interface_messages) GET_PIMSTAT_PER_NODE(rx_interface_disabled_messages) GET_PIMSTAT_PER_NODE(rx_register_not_rp) GET_PIMSTAT_PER_NODE(rp_filtered_source) GET_PIMSTAT_PER_NODE(unknown_register_stop) GET_PIMSTAT_PER_NODE(rx_join_prune_no_state) GET_PIMSTAT_PER_NODE(rx_graft_graft_ack_no_state) GET_PIMSTAT_PER_NODE(rx_graft_on_upstream_interface) GET_PIMSTAT_PER_NODE(rx_candidate_rp_not_bsr) GET_PIMSTAT_PER_NODE(rx_bsr_when_bsr) GET_PIMSTAT_PER_NODE(rx_bsr_not_rpf_interface) GET_PIMSTAT_PER_NODE(rx_unknown_hello_option) GET_PIMSTAT_PER_NODE(rx_data_no_state) GET_PIMSTAT_PER_NODE(rx_rp_no_state) GET_PIMSTAT_PER_NODE(rx_aggregate) GET_PIMSTAT_PER_NODE(rx_malformed_packet) GET_PIMSTAT_PER_NODE(no_rp) GET_PIMSTAT_PER_NODE(no_route_upstream) GET_PIMSTAT_PER_NODE(rp_mismatch) GET_PIMSTAT_PER_NODE(rpf_neighbor_unknown) // GET_PIMSTAT_PER_NODE(rx_join_rp) GET_PIMSTAT_PER_NODE(rx_prune_rp) GET_PIMSTAT_PER_NODE(rx_join_wc) GET_PIMSTAT_PER_NODE(rx_prune_wc) GET_PIMSTAT_PER_NODE(rx_join_sg) GET_PIMSTAT_PER_NODE(rx_prune_sg) GET_PIMSTAT_PER_NODE(rx_join_sg_rpt) GET_PIMSTAT_PER_NODE(rx_prune_sg_rpt) #undef GET_PIMSTAT_PER_NODE #define GET_PIMSTAT_PER_VIF(stat_name) \ int \ PimNode::pimstat_##stat_name##_per_vif(const string& vif_name, uint32_t& result, string& error_msg) const \ { \ result = 0; \ \ PimVif *pim_vif = vif_find_by_name(vif_name); \ if (pim_vif == NULL) { \ error_msg = c_format("Cannot get statistics for vif %s: no such vif", \ vif_name.c_str()); \ return (XORP_ERROR); \ } \ \ result = pim_vif->pimstat_##stat_name(); \ return (XORP_OK); \ } GET_PIMSTAT_PER_VIF(hello_messages_received) GET_PIMSTAT_PER_VIF(hello_messages_sent) GET_PIMSTAT_PER_VIF(hello_messages_rx_errors) GET_PIMSTAT_PER_VIF(register_messages_received) GET_PIMSTAT_PER_VIF(register_messages_sent) GET_PIMSTAT_PER_VIF(register_messages_rx_errors) GET_PIMSTAT_PER_VIF(register_stop_messages_received) GET_PIMSTAT_PER_VIF(register_stop_messages_sent) GET_PIMSTAT_PER_VIF(register_stop_messages_rx_errors) GET_PIMSTAT_PER_VIF(join_prune_messages_received) GET_PIMSTAT_PER_VIF(join_prune_messages_sent) GET_PIMSTAT_PER_VIF(join_prune_messages_rx_errors) GET_PIMSTAT_PER_VIF(bootstrap_messages_received) GET_PIMSTAT_PER_VIF(bootstrap_messages_sent) GET_PIMSTAT_PER_VIF(bootstrap_messages_rx_errors) GET_PIMSTAT_PER_VIF(assert_messages_received) GET_PIMSTAT_PER_VIF(assert_messages_sent) GET_PIMSTAT_PER_VIF(assert_messages_rx_errors) GET_PIMSTAT_PER_VIF(graft_messages_received) GET_PIMSTAT_PER_VIF(graft_messages_sent) GET_PIMSTAT_PER_VIF(graft_messages_rx_errors) GET_PIMSTAT_PER_VIF(graft_ack_messages_received) GET_PIMSTAT_PER_VIF(graft_ack_messages_sent) GET_PIMSTAT_PER_VIF(graft_ack_messages_rx_errors) GET_PIMSTAT_PER_VIF(candidate_rp_messages_received) GET_PIMSTAT_PER_VIF(candidate_rp_messages_sent) GET_PIMSTAT_PER_VIF(candidate_rp_messages_rx_errors) // GET_PIMSTAT_PER_VIF(unknown_type_messages) GET_PIMSTAT_PER_VIF(unknown_version_messages) GET_PIMSTAT_PER_VIF(neighbor_unknown_messages) GET_PIMSTAT_PER_VIF(bad_length_messages) GET_PIMSTAT_PER_VIF(bad_checksum_messages) GET_PIMSTAT_PER_VIF(bad_receive_interface_messages) GET_PIMSTAT_PER_VIF(rx_interface_disabled_messages) GET_PIMSTAT_PER_VIF(rx_register_not_rp) GET_PIMSTAT_PER_VIF(rp_filtered_source) GET_PIMSTAT_PER_VIF(unknown_register_stop) GET_PIMSTAT_PER_VIF(rx_join_prune_no_state) GET_PIMSTAT_PER_VIF(rx_graft_graft_ack_no_state) GET_PIMSTAT_PER_VIF(rx_graft_on_upstream_interface) GET_PIMSTAT_PER_VIF(rx_candidate_rp_not_bsr) GET_PIMSTAT_PER_VIF(rx_bsr_when_bsr) GET_PIMSTAT_PER_VIF(rx_bsr_not_rpf_interface) GET_PIMSTAT_PER_VIF(rx_unknown_hello_option) GET_PIMSTAT_PER_VIF(rx_data_no_state) GET_PIMSTAT_PER_VIF(rx_rp_no_state) GET_PIMSTAT_PER_VIF(rx_aggregate) GET_PIMSTAT_PER_VIF(rx_malformed_packet) GET_PIMSTAT_PER_VIF(no_rp) GET_PIMSTAT_PER_VIF(no_route_upstream) GET_PIMSTAT_PER_VIF(rp_mismatch) GET_PIMSTAT_PER_VIF(rpf_neighbor_unknown) // GET_PIMSTAT_PER_VIF(rx_join_rp) GET_PIMSTAT_PER_VIF(rx_prune_rp) GET_PIMSTAT_PER_VIF(rx_join_wc) GET_PIMSTAT_PER_VIF(rx_prune_wc) GET_PIMSTAT_PER_VIF(rx_join_sg) GET_PIMSTAT_PER_VIF(rx_prune_sg) GET_PIMSTAT_PER_VIF(rx_join_sg_rpt) GET_PIMSTAT_PER_VIF(rx_prune_sg_rpt) #undef GET_PIMSTAT_PER_VIF xorp/pim/pim_proto_graft.cc0000664000076400007640000000576411421137511016157 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // PIM PIM_GRAFT messages processing. // #include "pim_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvx.hh" #include "pim_vif.hh" // // Exported variables // // // Local constants definitions // // // Local structures/classes, typedefs and macros // // // Local variables // // // Local functions prototypes // /** * PimVif::pim_graft_recv: * @pim_nbr: The PIM neighbor message originator (or NULL if not a neighbor). * @src: The message source address. * @dst: The message destination address. * @buffer: The buffer with the message. * * Receive PIM_GRAFT message. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int PimVif::pim_graft_recv(PimNbr *pim_nbr, const IPvX& src, const IPvX& , // dst buffer_t *buffer) { int ret_value; buffer_t *buffer2; string dummy_error_msg; // // Must unicast back a Graft-Ack to the originator of this Graft. // buffer2 = buffer_send_prepare(); BUFFER_PUT_DATA(BUFFER_DATA_HEAD(buffer), buffer2, BUFFER_DATA_SIZE(buffer)); ret_value = pim_send(domain_wide_addr(), src, PIM_GRAFT_ACK, buffer2, dummy_error_msg); UNUSED(pim_nbr); // UNUSED(dst); return (ret_value); // Various error processing buflen_error: XLOG_UNREACHABLE(); dummy_error_msg = c_format("TX %s from %s to %s: " "packet cannot fit into sending buffer", PIMTYPE2ASCII(PIM_GRAFT_ACK), cstring(domain_wide_addr()), cstring(src)); XLOG_ERROR("%s", dummy_error_msg.c_str()); return (XORP_ERROR); } #if 0 // TODO: XXX: implement/use it int PimVif::pim_graft_send(const IPvX& dst, buffer_t *buffer) { int ret_value; IPvX src = dst.is_unicast()? domain_wide_addr() : primary_addr(); ret_value = pim_send(src, dst, PIM_GRAFT, buffer); return (ret_value); // Various error processing buflen_error: XLOG_UNREACHABLE(); XLOG_ERROR("TX %s from %s to %s: " "packet cannot fit into sending buffer", PIMTYPE2ASCII(PIM_GRAFT), cstring(src), cstring(dst)); } #endif /* 0 */ xorp/pim/pim_mre.hh0000664000076400007640000010700211635757530014432 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __PIM_PIM_MRE_HH__ #define __PIM_PIM_MRE_HH__ // // PIM Multicast Routing Entry definitions. // #include "libxorp/timer.hh" #include "mrt/mifset.hh" #include "mrt/mrt.hh" #include "pim_mrib_table.hh" #include "pim_proto_assert.hh" class AssertMetric; class PimMre; class PimMrt; class PimNbr; class PimRp; class PimVif; // PimMre _flags // TODO: move it inside PimMre class?? enum { // Multicast Routing Entry type PIM_MRE_SG = 1 << 0, // (S,G) entry PIM_MRE_SG_RPT = 1 << 1, // (S,G,rpt) entry PIM_MRE_WC = 1 << 2, // (*,G) entry PIM_MRE_RP = 1 << 3, // (*,*,RP) entry // PIM_MRE_SPT = 1 << 4, // (S,G) entry switched to SPT PIM_MFC = 1 << 5, // Multicast Forwarding Cache // entry: enumerated here for // consistency. // State machine PIM_MRE_JOINED_STATE = 1 << 8, // The state is Joined: // (*,*,RP) (*,G) (S,G) PIM_MRE_PRUNED_STATE = 1 << 9, // The state is Pruned: // (S,G,rpt) PIM_MRE_NOT_PRUNED_STATE = 1 << 10, // The state is NotPruned: // (S,G,rpt) PIM_MRE_REGISTER_JOIN_STATE = 1 << 11, // The Register tunnel for // (S,G) is in Join state PIM_MRE_REGISTER_PRUNE_STATE = 1 << 12, // The Register tunnel for // (S,G) is in Prune state PIM_MRE_REGISTER_JOIN_PENDING_STATE = 1 << 13, // The Register tunnel for // (S,G) is in Join-Pending // state PIM_MRE_COULD_REGISTER_SG = 1 << 14, // The macro "CouldRegister(S,G)" // is true // Misc. PIM_MRE_GRAFTED = 1 << 16, // For PIM-DM PIM_MRE_DIRECTLY_CONNECTED_S = 1 << 18, // Directly-connected S PIM_MRE_I_AM_RP = 1 << 19, // I am the RP for the group PIM_MRE_KEEPALIVE_TIMER_IS_SET = 1 << 20,// The (S,G) Keepalive Timer is // running PIM_MRE_TASK_DELETE_PENDING = 1 << 21, // Entry is pending deletion PIM_MRE_TASK_DELETE_DONE = 1 << 22, // Entry is ready to be deleted PIM_MRE_SWITCH_TO_SPT_DESIRED = 1 << 23, // SwitchToSptDesired(S,G) is true PIM_MRE_IS_KAT_SET_TO_RP_KEEPALIVE_PERIOD = 1 << 24 // At the RP, when // Register-Stop is sent }; // PIM-specific Multicast Routing Entry // XXX: the source_addr() for (*,*,RP) entry contains the RP address class PimMre : public Mre, BugCatcher, NONCOPYABLE { public: PimMre(PimMrt* pim_mrt, const IPvX& source, const IPvX& group); virtual ~PimMre(); void add_pim_mre_lists(); void remove_pim_mre_lists(); // General info: PimNode, PimMrt, family, etc. PimNode* pim_node() const; PimMrt* pim_mrt() const { return _pim_mrt; } int family() const; uint32_t pim_register_vif_index() const; // // Type of PimMre entry and related info // // The four entry types: (S,G), (S,G,rpt), (*,G), (*,*,RP) bool is_sg() const { return (_flags & PIM_MRE_SG); } bool is_sg_rpt() const { return (_flags & PIM_MRE_SG_RPT); } bool is_wc() const { return (_flags & PIM_MRE_WC); } bool is_rp() const { return (_flags & PIM_MRE_RP); } // Entry characteristics // Note: applies only for (S,G) bool is_spt() const { return (_flags & PIM_MRE_SPT); } void set_sg(bool v); void set_sg_rpt(bool v); void set_wc(bool v); void set_rp(bool v); // Note: applies only for (S,G) void set_spt(bool v); const IPvX* rp_addr_ptr() const; // The RP address string rp_addr_string() const; // C++ string with the RP address // or "RP_ADDR_UNKNOWN" // // The RP entry // PimRp *pim_rp() const { return (_pim_rp); } void set_pim_rp(PimRp *v); // Used by (*,G) (S,G) (S,G,rpt) void uncond_set_pim_rp(PimRp *v); // Used by (*,G) (S,G) (S,G,rpt) PimRp *compute_rp() const; // Used by (*,G) (S,G) (S,G,rpt) void recompute_rp_wc(); // Used by (*,G) void recompute_rp_sg(); // Used by (S,G) void recompute_rp_sg_rpt(); // Used by (S,G,rpt) // // // MRIB info // // Note: mrib_s(), rpf_interface_s() and related *_s() methods // are used only by (S,G) and (S,G,rpt) entry. // Note: mrib_rp(), rpf_interface_rp(), set_mrib_rp() apply for all entries Mrib *mrib_rp() const { return (_mrib_rp); } Mrib *mrib_s() const { return (_mrib_s); } uint32_t rpf_interface_rp() const; uint32_t rpf_interface_s() const; void set_mrib_rp(Mrib *v) { _mrib_rp = v; } void set_mrib_s(Mrib *v) { _mrib_s = v; } Mrib *compute_mrib_rp() const; Mrib *compute_mrib_s() const; void recompute_mrib_rp_rp(); // Used by (*,*,RP) void recompute_mrib_rp_wc(); // Used by (*,G) void recompute_mrib_rp_sg(); // Used by (S,G) void recompute_mrib_rp_sg_rpt(); // Used by (S,G,rpt) void recompute_mrib_s_sg(); // Used by (S,G) void recompute_mrib_s_sg_rpt(); // Used by (S,G,rpt) // // // RPF and RPF' neighbor info // // Note: applies only for (*,*,RP) and (*,G), but works also for (S,G) // and (S,G,rpt) PimNbr *nbr_mrib_next_hop_rp() const; // Note: applies only for (S,G) PimNbr *nbr_mrib_next_hop_s() const { return (_nbr_mrib_next_hop_s); } // Note: applies only for (*,G) ans (S,G,rpt) but works also for (S,G) PimNbr *rpfp_nbr_wc() const; // Note: applies only for (S,G) PimNbr *rpfp_nbr_sg() const { return (_rpfp_nbr_sg); } // Note: applies only for (S,G,rpt) PimNbr *rpfp_nbr_sg_rpt() const { return (_rpfp_nbr_sg_rpt); } // Note: applies for all entries bool is_pim_nbr_in_use(const PimNbr *pim_nbr) const; // Note: applies for all entries bool is_pim_nbr_missing() const; // Note: applies only for (*,*,RP) and (*,G) void set_nbr_mrib_next_hop_rp(PimNbr *v); // Note: applies only for (S,G) void set_nbr_mrib_next_hop_s(PimNbr *v); // Note: applies only for (*,G) void set_rpfp_nbr_wc(PimNbr *v); // Note: applies only for (S,G) void set_rpfp_nbr_sg(PimNbr *v); // Note: applies only for (S,G,rpt) void set_rpfp_nbr_sg_rpt(PimNbr *v); // Note: applies only for (*,*,RP), (*,G), (S,G,rpt), but works also // for (S,G). PimNbr *compute_nbr_mrib_next_hop_rp() const; // Note: applies only for (S,G) PimNbr *compute_nbr_mrib_next_hop_s() const; // Note: applies only for (*,G) PimNbr *compute_rpfp_nbr_wc() const; // Note: applies only for (S,G) PimNbr *compute_rpfp_nbr_sg() const; // Note: applies only for (S,G,rpt) PimNbr *compute_rpfp_nbr_sg_rpt() const; // (*,*,RP)-related upstream changes void recompute_nbr_mrib_next_hop_rp_rp_changed(); void recompute_nbr_mrib_next_hop_rp_gen_id_changed(); // (*,G)-related upstream changes void recompute_nbr_mrib_next_hop_rp_wc_changed(); void recompute_rpfp_nbr_wc_assert_changed(); void recompute_rpfp_nbr_wc_not_assert_changed(); void recompute_rpfp_nbr_wc_gen_id_changed(); // (S,G)-related upstream changes void recompute_nbr_mrib_next_hop_s_changed(); void recompute_rpfp_nbr_sg_assert_changed(); void recompute_rpfp_nbr_sg_not_assert_changed(); void recompute_rpfp_nbr_sg_gen_id_changed(); // (S,G,rpt)-related upstream changes void recompute_rpfp_nbr_sg_rpt_changed(); // (S,G,rpt)-related upstream changes (recomputed via (S,G) to (S,G,rpt)) void recompute_rpfp_nbr_sg_rpt_sg_changed(); // Misc. other RPF-related info // Note: applies for (S,G) and (S,G,rpt) bool compute_is_directly_connected_s(); // Note: applies for (S,G) void recompute_is_directly_connected_sg(); // // Related entries: (*,G), (*,*,RP) (may be NULL). // PimMre *wc_entry() const { return (_wc_entry); } PimMre *rp_entry() const { if (_rp_entry != NULL) return (_rp_entry); if (wc_entry() != NULL) return (wc_entry()->rp_entry()); // XXX: get it through (*,G) return (NULL); } PimMre *sg_entry() const { if (is_sg_rpt()) return (_sg_sg_rpt_entry); return (NULL); } PimMre *sg_rpt_entry() const { if (is_sg()) return (_sg_sg_rpt_entry); return (NULL); } void set_wc_entry(PimMre *v) { _wc_entry = v; } void set_rp_entry(PimMre *v) { _rp_entry = v; } void set_sg_entry(PimMre *v) { _sg_sg_rpt_entry = v; } void set_sg_rpt_entry(PimMre *v) { _sg_sg_rpt_entry = v; } // // ASSERT-related route metric and metric preference // // Note: applies only for (S,G) and (S,G,rpt) uint32_t metric_preference_s() const; // Note: applies for all entries uint32_t metric_preference_rp() const; // Note: applies only for (S,G) and (S,G,rpt) uint32_t metric_s() const; // Note: applies for all entries uint32_t metric_rp() const; // // Local receivers info // // Note: applies only for (*,G), (S,G) and (S,G,rpt) const Mifset& local_receiver_include_wc() const; // Note: applies only for (S,G) const Mifset& local_receiver_include_sg() const; // Note: applies only for (S,G) const Mifset& local_receiver_exclude_sg() const; // Note: applies only for (*,G) and (S,G); has only internal PimMre meaning const Mifset& local_receiver_include() const { return (_local_receiver_include); } const Mifset& local_receiver_exclude() const { return (_local_receiver_exclude); } void set_local_receiver_include(uint32_t vif_index, bool v); void set_local_receiver_exclude(uint32_t vif_index, bool t); // // JOIN/PRUNE info // // Note: applies only for (*,*,RP), (*,G), (S,G) XorpTimer& join_timer() { return (_join_or_override_timer); } // Note: applies only for (*,*,RP), (*,G), (S,G) const XorpTimer& const_join_timer() const { return (_join_or_override_timer); } void join_timer_timeout(); // Note: applies only for (S,G,rpt) XorpTimer& override_timer() { return (_join_or_override_timer); } // Note: applies only for (S,G,rpt) const XorpTimer& const_override_timer() const { return (_join_or_override_timer); } void override_timer_timeout(); // Note: applies only for (*,*,RP) void receive_join_rp(uint32_t vif_index, uint16_t holdtime); // Note: applies only for (*,*,RP) void receive_prune_rp(uint32_t vif_index, uint16_t holdtime); // Note: applies only for (*,G) void receive_join_wc(uint32_t vif_index, uint16_t holdtime); // Note: applies only for (*,G) void receive_prune_wc(uint32_t vif_index, uint16_t holdtime); // Note: applies only for (S,G) void receive_join_sg(uint32_t vif_index, uint16_t holdtime); // Note: applies only for (S,G) void receive_prune_sg(uint32_t vif_index, uint16_t holdtime); // Note: applies only for (S,G,rpt) void receive_join_wc_by_sg_rpt(uint32_t vif_index); // Note: applies only for (S,G,rpt) void receive_join_sg_rpt(uint32_t vif_index, uint16_t holdtime); // Note: applies only for (S,G,rpt) void receive_prune_sg_rpt(uint32_t vif_index, uint16_t holdtime, bool is_join_wc_received); // Note: applies only for (S,G,rpt) void receive_end_of_message_sg_rpt(uint32_t vif_index); // Note: applies only for (*,*,RP) void rp_see_join_rp(uint32_t vif_index, uint16_t holdtime, const IPvX& target_nbr_addr); // Note: applies only for (*,*,RP) void rp_see_prune_rp(uint32_t vif_index, uint16_t holdtime, const IPvX& target_nbr_addr); // Note: applies only for (*,G) void wc_see_join_wc(uint32_t vif_index, uint16_t holdtime, const IPvX& target_nbr_addr); // Note: applies only for (*,G) void wc_see_prune_wc(uint32_t vif_index, uint16_t holdtime, const IPvX& target_nbr_addr); // Note: applies only for (S,G) void sg_see_join_sg(uint32_t vif_index, uint16_t holdtime, const IPvX& target_nbr_addr); // Note: applies only for (S,G) void sg_see_prune_sg(uint32_t vif_index, uint16_t holdtime, const IPvX& target_nbr_addr); // Note: applies only for (S,G) void sg_see_prune_wc(uint32_t vif_index, const IPvX& target_nbr_addr); // Note: applies only for (S,G) void sg_see_prune_sg_rpt(uint32_t vif_index, uint16_t holdtime, const IPvX& target_nbr_addr); // Note: applies only for (S,G,rpt) void sg_rpt_see_join_sg_rpt(uint32_t vif_index, uint16_t holdtime, const IPvX& target_nbr_addr); // Note: applies only for (S,G,rpt) void sg_rpt_see_prune_sg_rpt(uint32_t vif_index, uint16_t holdtime, const IPvX& target_nbr_addr); // Note: applies only for (S,G,rpt) void sg_rpt_see_prune_sg(uint32_t vif_index, uint16_t holdtime, const IPvX& target_nbr_addr); // Note: applies only for (*,*,RP), (*,G), (S,G), (S,G,rpt) bool is_join_desired_rp() const; // Note: applies only for (*,G), (S,G), (S,G,rpt) bool is_join_desired_wc() const; // Note: applies only for (S,G) bool is_join_desired_sg() const; // Note: applies only for (*,G), (S,G), (S,G,rpt) bool is_rpt_join_desired_g() const; // Note: applies only for (S,G,rpt) bool is_prune_desired_sg_rpt() const; // Note: applies only for (*,*,RP) bool recompute_is_join_desired_rp(); // Note: applies only for (*,G) bool recompute_is_join_desired_wc(); // Note: applies only for (S,G) bool recompute_is_join_desired_sg(); // Note: applies only for (S,G,rpt) bool recompute_is_prune_desired_sg_rpt(); // Note: applies only for (S,G) (recomputed via (S,G) to (S,G,rpt)) bool recompute_is_prune_desired_sg_rpt_sg(); // Note: applies only for (S,G,rpt) bool recompute_is_rpt_join_desired_g(); // Note: applies only for (*,*,RP), (*,G), (S,G), (S,G,rpt) const Mifset& joins_rp() const; // Note: works for (*,G), (S,G), (S,G,rpt) const Mifset& joins_wc() const; // Note: applies only for (S,G) const Mifset& joins_sg() const; // Note: applies only for (S,G,rpt) const Mifset& prunes_sg_rpt() const; // // J/P (downstream) state (per interface) // // Note: each method below applies for (*,*,RP), (*,G), (S,G), (S,G,rpt) // (except for the *_tmp_* and *_processed_wc_by_sg_rpt* // methods which apply only for (S,G,rpt)) void set_downstream_noinfo_state(uint32_t vif_index); void set_downstream_join_state(uint32_t vif_index); void set_downstream_prune_state(uint32_t vif_index); void set_downstream_prune_pending_state(uint32_t vif_index); void set_downstream_prune_tmp_state(uint32_t vif_index); void set_downstream_prune_pending_tmp_state(uint32_t vif_index); void set_downstream_processed_wc_by_sg_rpt(uint32_t vif_index, bool v); bool is_downstream_noinfo_state(uint32_t vif_index) const; bool is_downstream_join_state(uint32_t vif_index) const; bool is_downstream_prune_state(uint32_t vif_index) const; bool is_downstream_prune_pending_state(uint32_t vif_index) const; bool is_downstream_prune_tmp_state(uint32_t vif_index) const; bool is_downstream_prune_pending_tmp_state(uint32_t vif_index) const; bool is_downstream_processed_wc_by_sg_rpt(uint32_t vif_index) const; const Mifset& downstream_join_state() const; const Mifset& downstream_prune_state() const; const Mifset& downstream_prune_pending_state() const; const Mifset& downstream_prune_tmp_state() const; const Mifset& downstream_prune_pending_tmp_state() const; // Note: applies only for (*,*,RP) void downstream_expiry_timer_timeout_rp(uint32_t vif_index); // Note: applies only for (*,G) void downstream_expiry_timer_timeout_wc(uint32_t vif_index); // Note: applies only for (S,G) void downstream_expiry_timer_timeout_sg(uint32_t vif_index); // Note: applies only for (S,G,rpt) void downstream_expiry_timer_timeout_sg_rpt(uint32_t vif_index); // Note: applies only for (*,*,RP) void downstream_prune_pending_timer_timeout_rp(uint32_t vif_index); // Note: applies only for (*,G) void downstream_prune_pending_timer_timeout_wc(uint32_t vif_index); // Note: applies only for (S,G) void downstream_prune_pending_timer_timeout_sg(uint32_t vif_index); // Note: applies only for (S,G,rpt) void downstream_prune_pending_timer_timeout_sg_rpt(uint32_t vif_index); // // J/P upstream state for (*,*,RP), (*,G), (S,G) // bool is_joined_state() const { return (_flags & PIM_MRE_JOINED_STATE); } bool is_not_joined_state() const { return (!is_joined_state()); } void set_joined_state(); void set_not_joined_state(); // // J/P upstream state for (S,G,rpt) // bool is_rpt_not_joined_state() const; bool is_pruned_state() const; bool is_not_pruned_state() const; void set_rpt_not_joined_state(); void set_pruned_state(); void set_not_pruned_state(); // // J/P state recomputation // // // Note: works for all entries const Mifset& immediate_olist_rp() const; // Note: applies for (*,G), (S,G), (S,G,rpt) const Mifset& immediate_olist_wc() const; // Note: applies for (S,G) const Mifset& immediate_olist_sg() const; // Note: applies for (*,G), (S,G), (S,G,rpt) const Mifset& pim_include_wc() const; // Note: applies for (S,G) const Mifset& pim_include_sg() const; // Note: applies for (S,G) const Mifset& pim_exclude_sg() const; // Note: applies for (*,*,RP), (*,G), (S,G), (S,G,rpt) const Mifset& inherited_olist_sg() const; // Note: applies for (*,*,RP), (*,G), (S,G), (S,G,rpt) const Mifset& inherited_olist_sg_rpt() const; // Note: applies for (S,G,rpt) bool recompute_inherited_olist_sg_rpt(); // // REGISTER info // // Note: applies for (S,G) void receive_register_stop(); // Perform the "RP changed" action at the (S,G) register state machine // Note that the RP has already changed and assigned by the method that // calls this one, hence we unconditionally take the "RP changed" actions. // Note: applies for (S,G) void rp_register_sg_changed(); // Note: applies for (S,G) void set_register_noinfo_state(); // Note: applies for (S,G) void set_register_join_state(); // Note: applies for (S,G) void set_register_prune_state(); // Note: applies for (S,G) void set_register_join_pending_state(); // Note: applies for (S,G) bool is_register_noinfo_state() const; // Note: applies for (S,G) bool is_register_join_state() const; // Note: applies for (S,G) bool is_register_prune_state() const; // Note: applies for (S,G) bool is_register_join_pending_state() const; // Note: applies for (S,G) bool compute_is_could_register_sg() const; // Note: applies for (S,G) bool recompute_is_could_register_sg(); // Note: applies for (S,G) void add_register_tunnel(); // Note: applies for (S,G) void remove_register_tunnel(); // Note: applies for (S,G) void update_register_tunnel(); // Note: the remaining Register-related methods below should apply // only for (S,G), but for simplicity we we don't check the entry type. bool is_could_register_sg() const { return (_flags & PIM_MRE_COULD_REGISTER_SG); } bool is_not_could_register_sg() const { return (! is_could_register_sg()); } void set_could_register_sg() { _flags |= PIM_MRE_COULD_REGISTER_SG; } void set_not_could_register_sg() { _flags &= ~PIM_MRE_COULD_REGISTER_SG; } XorpTimer& register_stop_timer() { return (_register_stop_timer); } void register_stop_timer_timeout(); // // ASSERT info // // Note: applies only for (*,G) and (S,G) bool is_assert_noinfo_state(uint32_t vif_index) const; // Note: applies only for (*,G) and (S,G) bool is_i_am_assert_winner_state(uint32_t vif_index) const; // Note: applies only for (*,G) and (S,G) bool is_i_am_assert_loser_state(uint32_t vif_index) const; // Note: applies only for (*,G) and (S,G) void set_assert_noinfo_state(uint32_t vif_index); // Note: applies only for (*,G) and (S,G) void set_i_am_assert_winner_state(uint32_t vif_index); // Note: applies only for (*,G) and (S,G) void set_i_am_assert_loser_state(uint32_t vif_index); // Note: applies only for (*,G) and (S,G) const Mifset& i_am_assert_winner_state() const { return (_i_am_assert_winner_state); } // Note: applies only for (*,G) and (S,G) const Mifset& i_am_assert_loser_state() const { return (_i_am_assert_loser_state); } // Note: works for (*,G), (S,G), (S,G,rpt) const Mifset& i_am_assert_winner_wc() const; // Note: works only for (S,G) const Mifset& i_am_assert_winner_sg() const; // Note: applies for (*,G), (S,G), (S,G,rpt) const Mifset& i_am_assert_loser_wc() const; // Note: applies only for (S,G) const Mifset& i_am_assert_loser_sg() const; // Note: applies for (*,G), (S,G), (S,G,rpt) const Mifset& lost_assert_wc() const; // Note: applies only for (S,G) const Mifset& lost_assert_sg() const; // Note: applies only for (S,G) and (S,G,rpt) const Mifset& lost_assert_sg_rpt() const; // Note: applies only for (*,G) void assert_timer_timeout_wc(uint32_t vif_index); // Note: applies only for (S,G) void assert_timer_timeout_sg(uint32_t vif_index); // Note: works for (*,G), (S,G) AssertMetric *assert_winner_metric_wc(uint32_t vif_index) const; // Note: works for (S,G) AssertMetric *assert_winner_metric_sg(uint32_t vif_index) const; // Note: applies only for (*,G) and (S,G) AssertMetric *assert_winner_metric(uint32_t vif_index) const { return (_assert_winner_metrics[vif_index]); } // Note: works for (*,G), (S,G) void set_assert_winner_metric_wc(uint32_t vif_index, AssertMetric *v); // Note: works for (S,G) void set_assert_winner_metric_sg(uint32_t vif_index, AssertMetric *v); // Note: applies only for (*,G) and (S,G) void set_assert_winner_metric(uint32_t vif_index, AssertMetric *v); // Note: works for (*,G), (S,G) void delete_assert_winner_metric_wc(uint32_t vif_index); // Note: works for (S,G) void delete_assert_winner_metric_sg(uint32_t vif_index); // Note: applies only for (*,G) and (S,G) void delete_assert_winner_metric(uint32_t vif_index); // Note: applies only for (S,G) const Mifset& assert_winner_metric_is_better_than_spt_assert_metric_sg() const { return (_assert_winner_metric_is_better_than_spt_assert_metric_sg); } // Note: applies only for (S,G) void set_assert_winner_metric_is_better_than_spt_assert_metric_sg(uint32_t vif_index, bool v); Mifset _assert_winner_metric_is_better_than_spt_assert_metric_sg; // Note: applies for (*,G) const Mifset& assert_tracking_desired_wc() const; // Note: applies for (S,G) const Mifset& assert_tracking_desired_sg() const; // Note: applies only for (*,G) and (S,G) const Mifset& assert_tracking_desired_state() const { return (_assert_tracking_desired_state); } // Note: applies only for (*,G) and (S,G) void set_assert_tracking_desired_state(uint32_t vif_index, bool v); // Note: applies only for (*,G) and (S,G) bool is_assert_tracking_desired_state(uint32_t vif_index) const; // Note: applies only for (*,G) and (S,G) const Mifset& could_assert_state() const { return (_could_assert_state); } // Note: applies only for (*,G) and (S,G) bool is_could_assert_state(uint32_t vif_index) const; // Note: applies only for (*,G) and (S,G) void set_could_assert_state(uint32_t vif_index, bool v); // Note: applies only for (S,G) AssertMetric *my_assert_metric_sg(uint32_t vif_index) const; // Note: applies only for (S,G) AssertMetric *my_assert_metric_wc(uint32_t vif_index) const; // Note: applies only for (S,G) AssertMetric *spt_assert_metric(uint32_t vif_index) const; // Note: applies only for (*,G) and (S,G) AssertMetric *rpt_assert_metric(uint32_t vif_index) const; // Note: applies only for (*,G) and (S,G) (but is used only for (S,G)) AssertMetric *infinite_assert_metric() const; // metrics array. // Note: applies only for (*,G) and (S,G) int assert_process(PimVif *pim_vif, AssertMetric *assert_metric); // Note: applies only for (S,G) int assert_process_sg(PimVif *pim_vif, AssertMetric *assert_metric, assert_state_t assert_state, bool i_am_assert_winner); // Note: applies only for (*,G) int assert_process_wc(PimVif *pim_vif, AssertMetric *assert_metric, assert_state_t state, bool i_am_assert_winner); // Note: applies for all entries int data_arrived_could_assert(PimVif *pim_vif, const IPvX& src, const IPvX& dst, bool& is_assert_sent); // Note: applies only for (S,G) int data_arrived_could_assert_sg(PimVif *pim_vif, const IPvX& assert_source_addr, bool& is_assert_sent); // Note: applies only for (*,G) int data_arrived_could_assert_wc(PimVif *pim_vif, const IPvX& assert_source_addr, bool& is_assert_sent); // Note: applies only for (S,G) int wrong_iif_data_arrived_sg(PimVif *pim_vif, const IPvX& assert_source_addr, bool& is_assert_sent); // Note: applies only for (*,G) int wrong_iif_data_arrived_wc(PimVif *pim_vif, const IPvX& assert_source_addr, bool& is_assert_sent); // Note: applies only for (S,G) bool recompute_assert_tracking_desired_sg(); // Note: applies only for (S,G) bool process_assert_tracking_desired_sg(uint32_t vif_index, bool new_value); // Note: applies only for (*,G) bool recompute_assert_tracking_desired_wc(); // Note: applies only for (*,G) bool process_assert_tracking_desired_wc(uint32_t vif_index, bool new_value); // Note: applies only for (S,G) and (S,G,rpt) const Mifset& could_assert_sg() const; // Note: applies only for (S,G) bool recompute_could_assert_sg(); // Note: applies only for (S,G) bool process_could_assert_sg(uint32_t vif_index, bool new_value); // Note: applies for all entries const Mifset& could_assert_wc() const; // Note: applies only for (*,G) bool recompute_could_assert_wc(); // Note: applies only for (*,G) bool process_could_assert_wc(uint32_t vif_index, bool new_value); // Note: applies only for (S,G) bool recompute_my_assert_metric_sg(uint32_t vif_index); // Note: applies only for (*,G) bool recompute_my_assert_metric_wc(uint32_t vif_index); // Note: applies only for (S,G) bool recompute_assert_rpf_interface_sg(uint32_t vif_index); // Note: applies only for (*,G) bool recompute_assert_rpf_interface_wc(uint32_t vif_index); // Note: applies only for (S,G) bool recompute_assert_receive_join_sg(uint32_t vif_index); // Note: applies only for (*,G) bool recompute_assert_receive_join_wc(uint32_t vif_index); // Note: applies only for (S,G) bool recompute_assert_winner_nbr_sg_gen_id_changed( uint32_t vif_index, const IPvX& nbr_addr); // Note: applies only for (*,G) bool recompute_assert_winner_nbr_wc_gen_id_changed( uint32_t vif_index, const IPvX& nbr_addr); // Note: applies only for (S,G) bool recompute_assert_winner_nbr_sg_nlt_expired( uint32_t vif_index, const IPvX& nbr_addr); // Note: applies only for (*,G) bool recompute_assert_winner_nbr_wc_nlt_expired( uint32_t vif_index, const IPvX& nbr_addr); // Assert rate-limiting stuff void asserts_rate_limit_timer_timeout(); // // PMBR info // // PMBR: the first PMBR to send a Register for this source // with the Border bit set. // Note: applies only for (S,G) const IPvX& pmbr_addr() const { return _pmbr_addr; } void set_pmbr_addr(const IPvX& v) { _pmbr_addr = v; } void clear_pmbr_addr() { _pmbr_addr = IPvX::ZERO(family()); } bool is_pmbr_addr_set() const { return (_pmbr_addr != IPvX::ZERO(family())); } // // MISC. info // // Note: applies for (*,*,RP), (*,G), (S,G), (S,G,rpt) const Mifset& i_am_dr() const; // // Data // // Note: applies only for (S,G) void update_sptbit_sg(uint32_t iif_vif_index); // Note: applies for (*,G), (S,G), (S,G,rpt) bool is_monitoring_switch_to_spt_desired_sg(const PimMre *pim_mre_sg) const; // Note: applies for all entries bool is_switch_to_spt_desired_sg(uint32_t measured_interval_sec, uint32_t measured_bytes) const; // Note: in theory applies for all entries, but in practice it could // be true only for (*,G), (S,G), (S,G,rpt) bool check_switch_to_spt_sg(const IPvX& src, const IPvX& dst, PimMre*& pim_mre_sg, uint32_t measured_interval_sec, uint32_t measured_bytes); // Note: applies only for (S,G) void set_switch_to_spt_desired_sg(bool v); // Note: applies only for (S,G) bool was_switch_to_spt_desired_sg() const; // // MISC. timers // // The KeepaliveTimer(S,G) // Note: applies only for (S,G) void start_keepalive_timer(); // Note: applies only for (S,G) void cancel_keepalive_timer(); // Note: applies only for (S,G) bool is_keepalive_timer_running() const; // Note: applies only for (S,G) void keepalive_timer_timeout(); // Note: applies only for (S,G) void recompute_set_keepalive_timer_sg(); // // MISC. other stuff // // Note: applies for (*,*,RP) void recompute_start_vif_rp(uint32_t vif_index); // Note: applies for (*,G) void recompute_start_vif_wc(uint32_t vif_index); // Note: applies for (S,G) void recompute_start_vif_sg(uint32_t vif_index); // Note: applies for (S,G,rpt) void recompute_start_vif_sg_rpt(uint32_t vif_index); // Note: applies for (*,*,RP) void recompute_stop_vif_rp(uint32_t vif_index); // Note: applies for (*,G) void recompute_stop_vif_wc(uint32_t vif_index); // Note: applies for (S,G) void recompute_stop_vif_sg(uint32_t vif_index); // Note: applies for (S,G,rpt) void recompute_stop_vif_sg_rpt(uint32_t vif_index); // Note: applies for (*,*,RP), (*,G), (S,G), (S,G,rpt) bool entry_try_remove(); // Try to remove the entry if not needed // Note: applies for (*,*,RP), (*,G), (S,G), (S,G,rpt) bool entry_can_remove() const; // Test if OK to remove the entry // Actions to take when a related PimMre entry is added or removed // Note: applies for (*,*,RP), (*,G), (S,G), (S,G,rpt) void add_pim_mre_rp_entry(); // Note: applies for (*,G), (S,G), (S,G,rpt) void add_pim_mre_wc_entry(); // Note: applies for (S,G), (S,G,rpt) void add_pim_mre_sg_entry(); // Note: applies for (S,G), (S,G,rpt) void add_pim_mre_sg_rpt_entry(); // Note: applies for (*,*,RP), (*,G), (S,G), (S,G,rpt) void remove_pim_mre_rp_entry(); // Note: applies for (*,G), (S,G), (S,G,rpt) void remove_pim_mre_wc_entry(); // Note: applies for (S,G), (S,G,rpt) void remove_pim_mre_sg_entry(); // Note: applies for (S,G), (S,G,rpt) void remove_pim_mre_sg_rpt_entry(); // Note: applies for (S,G) bool is_directly_connected_s() const { return (_flags & PIM_MRE_DIRECTLY_CONNECTED_S); } // Note: applies for (S,G) void set_directly_connected_s(bool v) { if (v) _flags |= PIM_MRE_DIRECTLY_CONNECTED_S; else _flags &= ~PIM_MRE_DIRECTLY_CONNECTED_S; } // Note: applies for (*,*,RP), (*,G), (S,G), (S,G,rpt) bool i_am_rp() const { return (_flags & PIM_MRE_I_AM_RP); } // Note: applies for (*,*,RP), (*,G), (S,G), (S,G,rpt) void set_i_am_rp(bool v) { if (v) { _flags |= PIM_MRE_I_AM_RP; } else { // // XXX: Reset the PIM_MRE_IS_KAT_SET_TO_RP_KEEPALIVE_PERIOD flag // as well, because it applies only at the RP. // _flags &= ~(PIM_MRE_I_AM_RP | PIM_MRE_IS_KAT_SET_TO_RP_KEEPALIVE_PERIOD); } } // Note: applies for (S,G) bool is_kat_set_to_rp_keepalive_period() const { return (_flags & PIM_MRE_IS_KAT_SET_TO_RP_KEEPALIVE_PERIOD); } void set_is_kat_set_to_rp_keepalive_period(bool v) { if (v) _flags |= PIM_MRE_IS_KAT_SET_TO_RP_KEEPALIVE_PERIOD; else _flags &= ~PIM_MRE_IS_KAT_SET_TO_RP_KEEPALIVE_PERIOD; } // Note: applies for (*,*,RP), (*,G), (S,G), (S,G,rpt) bool is_task_delete_pending() const { return (_flags & PIM_MRE_TASK_DELETE_PENDING); } // Note: applies for (*,*,RP), (*,G), (S,G), (S,G,rpt) void set_is_task_delete_pending(bool v) { if (v) _flags |= PIM_MRE_TASK_DELETE_PENDING; else _flags &= ~PIM_MRE_TASK_DELETE_PENDING; } // Note: applies for (*,*,RP), (*,G), (S,G), (S,G,rpt) bool is_task_delete_done() const { return (_flags & PIM_MRE_TASK_DELETE_DONE); } // Note: applies for (*,*,RP), (*,G), (S,G), (S,G,rpt) void set_is_task_delete_done(bool v) { if (v) _flags |= PIM_MRE_TASK_DELETE_DONE; else _flags &= ~PIM_MRE_TASK_DELETE_DONE; } private: PimMrt *_pim_mrt; // The PIM MRT (yuck!) PimRp *_pim_rp; // The RP entry // Used by (*,G) (S,G) (S,G,rpt) Mrib *_mrib_rp; // The MRIB info to the RP // Used by all entries Mrib *_mrib_s; // The MRIB info to the source // Used by (S,G) (S,G,rpt) // PimNbr *_nbr_mrib_next_hop_rp; // Applies only for (*,*,RP) and (*,G) PimNbr *_nbr_mrib_next_hop_s; // Applies only for (S,G) PimNbr *_rpfp_nbr_wc; // Applies only for (*,G) PimNbr *_rpfp_nbr_sg; // Applies only for (S,G) PimNbr *_rpfp_nbr_sg_rpt; // Applies only for (S,G,rpt) // PimMre *_wc_entry; // The (*,G) entry PimMre *_rp_entry; // The (*,*,RP) entry PimMre *_sg_sg_rpt_entry; // The (S,G) or (S,G,rpt) entry Mifset _local_receiver_include; // The interfaces with IGMP/MLD6 Join Mifset _local_receiver_exclude; // The interfaces with IGMP/MLD6 Leave XorpTimer _join_or_override_timer; // The Join Timer for // (*,*,RP) (*,G) (S,G); // Also Override Timer for (S,G,rpt) Mifset _downstream_join_state; // Join state Mifset _downstream_prune_pending_state; // Prune-Pending state Mifset _downstream_prune_state; // Prune state Mifset _downstream_tmp_state; // P' and PP' state Mifset _downstream_processed_wc_by_sg_rpt; // (S,G,rpt)J/P processed XorpTimer _downstream_expiry_timers[MAX_VIFS]; // Expiry timers XorpTimer _downstream_prune_pending_timers[MAX_VIFS]; // Prune-Pending timers XorpTimer _register_stop_timer; Mifset _i_am_assert_winner_state; // The interfaces I am Assert winner Mifset _i_am_assert_loser_state; // The interfaces I am Assert loser XorpTimer _assert_timers[MAX_VIFS]; // The Assert (winner/loser) timers Mifset _assert_tracking_desired_state; // To store the // AssertTrackingDesired state Mifset _could_assert_state; // To store the CouldAssert state AssertMetric *_assert_winner_metrics[MAX_VIFS]; // The Assert winner Mifset _asserts_rate_limit; // Bit-flags for Asserts rate limit XorpTimer _asserts_rate_limit_timer; // Timer for Asserts rate limit // support IPvX _pmbr_addr; // The address of the PMBR uint32_t _flags; // Various flags (see PIM_MRE_* above) }; #endif // __PIM_PIM_MRE_HH__ xorp/pim/pim_proto_assert.cc0000664000076400007640000003202311613330166016345 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // PIM PIM_ASSERT messages processing. // #include "pim_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvx.hh" #include "pim_mre.hh" #include "pim_mrt.hh" #include "pim_proto_assert.hh" #include "pim_vif.hh" // // Exported variables // // // Local constants definitions // // // Local structures/classes, typedefs and macros // // // Local variables // // // Local functions prototypes // /** * PimVif::pim_assert_recv: * @pim_nbr: The PIM neighbor message originator (or NULL if not a neighbor). * @src: The message source address. * @dst: The message destination address. * @buffer: The buffer with the message. * * Receive PIM_ASSERT message. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int PimVif::pim_assert_recv(PimNbr *pim_nbr, const IPvX& src, const IPvX& dst, buffer_t *buffer) { int rcvd_family; uint8_t group_mask_len; uint8_t group_addr_reserved_flags; IPvX assert_source_addr(family()); IPvX assert_group_addr(family()); uint32_t metric_preference, metric; AssertMetric assert_metric(src); bool rpt_bit; UNUSED(group_addr_reserved_flags); // // Parse the message // GET_ENCODED_GROUP_ADDR(rcvd_family, assert_group_addr, group_mask_len, group_addr_reserved_flags, buffer); GET_ENCODED_UNICAST_ADDR(rcvd_family, assert_source_addr, buffer); BUFFER_GET_HOST_32(metric_preference, buffer); BUFFER_GET_HOST_32(metric, buffer); // The RPTbit if (metric_preference & PIM_ASSERT_RPT_BIT) rpt_bit = true; else rpt_bit = false; metric_preference &= ~PIM_ASSERT_RPT_BIT; // The assert metrics assert_metric.set_rpt_bit_flag(rpt_bit); assert_metric.set_metric_preference(metric_preference); assert_metric.set_metric(metric); assert_metric.set_addr(src); // // Process the assert data // pim_assert_process(pim_nbr, src, dst, assert_source_addr, assert_group_addr, group_mask_len, &assert_metric); // UNUSED(dst); return (XORP_OK); // Various error processing rcvlen_error: XLOG_WARNING("RX %s from %s to %s: " "invalid message length", PIMTYPE2ASCII(PIM_ASSERT), cstring(src), cstring(dst)); ++_pimstat_rx_malformed_packet; return (XORP_ERROR); rcvd_mask_len_error: XLOG_WARNING("RX %s from %s to %s: " "invalid group mask length = %d", PIMTYPE2ASCII(PIM_ASSERT), cstring(src), cstring(dst), group_mask_len); return (XORP_ERROR); rcvd_family_error: XLOG_WARNING("RX %s from %s to %s: " "invalid address family inside = %d", PIMTYPE2ASCII(PIM_ASSERT), cstring(src), cstring(dst), rcvd_family); return (XORP_ERROR); } int PimVif::pim_assert_process(PimNbr *pim_nbr, const IPvX& src, const IPvX& dst, const IPvX& assert_source_addr, const IPvX& assert_group_addr, uint8_t group_mask_len, AssertMetric *assert_metric) { PimMre *pim_mre_sg, *pim_mre_wc; int ret_value; UNUSED(src); UNUSED(dst); if (group_mask_len != IPvX::addr_bitlen(family())) { XLOG_WARNING("RX %s from %s to %s: " "invalid group mask length = %d " "instead of %u", PIMTYPE2ASCII(PIM_ASSERT), cstring(src), cstring(dst), group_mask_len, XORP_UINT_CAST(IPvX::addr_bitlen(family()))); return (XORP_ERROR); } if (! assert_group_addr.is_multicast()) { XLOG_WARNING("RX %s from %s to %s: " "invalid assert group address = %s", PIMTYPE2ASCII(PIM_ASSERT), cstring(src), cstring(dst), cstring(assert_group_addr)); return (XORP_ERROR); } if (! ((assert_source_addr == IPvX::ZERO(family())) || assert_source_addr.is_unicast())) { XLOG_WARNING("RX %s from %s to %s: " "invalid assert source address = %s", PIMTYPE2ASCII(PIM_ASSERT), cstring(src), cstring(dst), cstring(assert_source_addr)); return (XORP_ERROR); } if (! assert_metric->rpt_bit_flag()) { // (S,G) Assert received. The assert source address must be unicast. if (! assert_source_addr.is_unicast()) { XLOG_WARNING("RX %s from %s to %s: " "invalid unicast assert source address = %s", PIMTYPE2ASCII(PIM_ASSERT), cstring(src), cstring(dst), cstring(assert_source_addr)); return (XORP_ERROR); } } if (assert_metric->rpt_bit_flag()) { // // (*,G) Assert received // // If the source address is not zero, then first try to apply // this assert to the (S,G) assert state machine. // Only if the (S,G) assert state machine is in NoInfo state before // and after consideration of the received message, then apply the // message to the (*,G) assert state machine. // do { bool is_sg_noinfo_old, is_sg_noinfo_new; if (assert_source_addr == IPvX::ZERO(family())) { // // Assert source address is zero, hence don't try to apply // it to the (S,G) assert state machine. // break; } // // XXX: strictly speaking, we should try to create // the (S,G) state, and explicitly test the old // and new (S,G) assert state. // However, we use the observation that if there is no (S,G) // routing state, then the (*,G) assert message will not change // the (S,G) assert state mchine. Note that this observation is // based on the specific details of the (S,G) assert state machine. // In particular, the action in NoInfo state when // "Receive Assert with RPTbit set and CouldAssert(S,G,I)". // If there is no (S,G) routing state, then the SPTbit cannot // be true, and therefore CouldAssert(S,G,I) also cannot be true. // pim_mre_sg = pim_mrt().pim_mre_find(assert_source_addr, assert_group_addr, PIM_MRE_SG, 0); if (pim_mre_sg == NULL) break; // XXX: see the above comment about not // creating the (S,G) routing state. // Compute the old and new (S,G) assert state. is_sg_noinfo_old = pim_mre_sg->is_assert_noinfo_state(vif_index()); ret_value = pim_mre_sg->assert_process(this, assert_metric); is_sg_noinfo_new = pim_mre_sg->is_assert_noinfo_state(vif_index()); // // Only if both the old and new state in the (S,G) assert state // machine are in the NoInfo state, then we apply the (*,G) assert // message to the (*,G) assert state machine. // if (is_sg_noinfo_old && is_sg_noinfo_new) break; return (ret_value); } while (false); // // No transaction occured in the (S,G) assert state machine, and // it is in NoInfo state. // Apply the assert to the (*,G) assert state machine. // pim_mre_wc = pim_mrt().pim_mre_find(assert_source_addr, assert_group_addr, PIM_MRE_WC, PIM_MRE_WC); if (pim_mre_wc == NULL) { XLOG_ERROR("Internal error lookup/creating PIM multicast routing " "entry for source = %s group = %s", cstring(assert_source_addr), cstring(assert_group_addr)); return (XORP_ERROR); } ret_value = pim_mre_wc->assert_process(this, assert_metric); // Try to remove the entry in case we don't need it pim_mre_wc->entry_try_remove(); return (ret_value); } // // (S,G) Assert received // pim_mre_sg = pim_mrt().pim_mre_find(assert_source_addr, assert_group_addr, PIM_MRE_SG, PIM_MRE_SG); if (pim_mre_sg == NULL) { XLOG_ERROR("Internal error lookup/creating PIM multicast routing " "entry for source = %s group = %s", cstring(assert_source_addr), cstring(assert_group_addr)); return (XORP_ERROR); } ret_value = pim_mre_sg->assert_process(this, assert_metric); // Try to remove the entry in case we don't need it. pim_mre_sg->entry_try_remove(); return (ret_value); UNUSED(pim_nbr); } // // XXX: @assert_source_addr is the source address inside the assert message // XXX: applies only for (S,G) and (*,G) // int PimVif::pim_assert_mre_send(PimMre *pim_mre, const IPvX& assert_source_addr, string& error_msg) { IPvX assert_group_addr(family()); uint32_t metric_preference, metric; bool rpt_bit = true; int ret_value; if (! (pim_mre->is_sg() || pim_mre->is_wc())) return (XORP_ERROR); // Prepare the Assert data assert_group_addr = pim_mre->group_addr(); if (pim_mre->is_spt()) { rpt_bit = false; metric_preference = pim_mre->metric_preference_s(); metric = pim_mre->metric_s(); } else { rpt_bit = true; metric_preference = pim_mre->metric_preference_rp(); metric = pim_mre->metric_rp(); } ret_value = pim_assert_send(assert_source_addr, assert_group_addr, rpt_bit, metric_preference, metric, error_msg); return (ret_value); } // XXX: applies only for (S,G) and (*,G) int PimVif::pim_assert_cancel_send(PimMre *pim_mre, string& error_msg) { IPvX assert_source_addr(family()); IPvX assert_group_addr(family()); uint32_t metric_preference, metric; int ret_value; bool rpt_bit = false; if (! (pim_mre->is_sg() || pim_mre->is_wc())) { error_msg = c_format("Invalid PimMre entry type"); return (XORP_ERROR); } // Prepare the Assert data if (pim_mre->is_sg()) { // AssertCancel(S,G) assert_source_addr = pim_mre->source_addr(); } else { // AssertCancel(*,G) assert_source_addr = IPvX::ZERO(family()); } assert_group_addr = pim_mre->group_addr(); rpt_bit = true; metric_preference = PIM_ASSERT_MAX_METRIC_PREFERENCE; metric = PIM_ASSERT_MAX_METRIC; ret_value = pim_assert_send(assert_source_addr, assert_group_addr, rpt_bit, metric_preference, metric, error_msg); return (ret_value); } int PimVif::pim_assert_send(const IPvX& assert_source_addr, const IPvX& assert_group_addr, bool rpt_bit, uint32_t metric_preference, uint32_t metric, string& error_msg) { buffer_t *buffer = buffer_send_prepare(); uint8_t group_addr_reserved_flags = 0; uint8_t group_mask_len = IPvX::addr_bitlen(family()); if (rpt_bit) metric_preference |= PIM_ASSERT_RPT_BIT; else metric_preference &= ~PIM_ASSERT_RPT_BIT; // Write all data to the buffer PUT_ENCODED_GROUP_ADDR(family(), assert_group_addr, group_mask_len, group_addr_reserved_flags, buffer); PUT_ENCODED_UNICAST_ADDR(family(), assert_source_addr, buffer); BUFFER_PUT_HOST_32(metric_preference, buffer); BUFFER_PUT_HOST_32(metric, buffer); return (pim_send(primary_addr(), IPvX::PIM_ROUTERS(family()), PIM_ASSERT, buffer, error_msg)); invalid_addr_family_error: XLOG_UNREACHABLE(); error_msg = c_format("TX %s from %s to %s: " "invalid address family error = %d", PIMTYPE2ASCII(PIM_ASSERT), cstring(primary_addr()), cstring(IPvX::PIM_ROUTERS(family())), family()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); buflen_error: XLOG_UNREACHABLE(); error_msg = c_format("TX %s from %s to %s: " "packet cannot fit into sending buffer", PIMTYPE2ASCII(PIM_ASSERT), cstring(primary_addr()), cstring(IPvX::PIM_ROUTERS(family()))); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } // Return true if I am better metric bool AssertMetric::operator>(const AssertMetric& other) const { // The RPT flag: smaller is better if ( (! rpt_bit_flag()) && other.rpt_bit_flag()) return (true); if (rpt_bit_flag() && (! other.rpt_bit_flag())) return (false); // The metric preference: smaller is better if (metric_preference() < other.metric_preference()) return (true); if (metric_preference() > other.metric_preference()) return (false); // The route metric: smaller is better if (metric() < other.metric()) return (true); if (metric() > other.metric()) return (false); // The IP address: bigger is better if (addr() > other.addr()) return (true); return (false); } // Return true if contains infinite metric sent by AssertCancel message bool AssertMetric::is_assert_cancel_metric() const { // // XXX: note that we don't check whether the address is zero. // We need to ignore the address, because it won't be zero for // AssertCancel messages. // return (_rpt_bit_flag && (_metric_preference == PIM_ASSERT_MAX_METRIC_PREFERENCE) && (_metric == PIM_ASSERT_MAX_METRIC)); } xorp/pim/pim_mrib_table.hh0000664000076400007640000001321511540224231015727 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/pim/pim_mrib_table.hh,v 1.15 2008/10/02 21:57:53 bms Exp $ #ifndef __PIM_PIM_MRIB_TABLE_HH__ #define __PIM_PIM_MRIB_TABLE_HH__ // // PIM Multicast Routing Information Base Table header file. // #include "libxorp/timer.hh" #include "mrt/mrib_table.hh" // // Constants definitions // // // Structures/classes, typedefs and macros // class IPvXNet; class PimMrt; class PimNode; // PIM-specific Multicast Routing Information Base Table /** * @short PIM-specific Multicast Routing Information Base Table */ class PimMribTable : public MribTable { public: /** * Constructor. * * @param pim_node the PimNode this table belongs to. */ PimMribTable(PimNode& pim_node); /** * Destructor. */ virtual ~PimMribTable(); // Redirection functions (to the pim_node) /** * Get the PimNode this table belongs to. * * @return the PimNode this table belongs to. * @see PimTable. */ PimNode& pim_node() const { return (_pim_node); } /** * Get the address family. * * @return the address family (AF_INET or AF_INET6 for * IPv4 and IPv6 respectively). */ int family(); /** * Get the corresponding PIM Multicast Routing Table. * * @return the corresponding PIM Multicast Routing Table. * @see PimMrt. */ PimMrt& pim_mrt(); /** * Clear the table by removing all entries. */ void clear(); /** * Search the table and find the corresponding Mrib entry for a given * destination address. * * @param address the destination address to search for. * @return the Mrib entry for the destination address. * @see Mrib. */ Mrib *find(const IPvX& address) const; /** * Add a MRIB entry to the MRIB table. * * Note that if the MRIB entry is for one of my own addresses, then we * check the next-hop interface. If it points toward the loopback * interface (e.g., in case of KAME IPv6 stack), then we overwrite it * with the network interface this address belongs to. * * @param tid the transaction ID. * @param mrib the MRIB entry to add. * @param next_hop_vif_name the next-hop vif name. It is used for * later resolving of Mrib::_next_hop_vif_index if the vif name * is not known yet. * @see Mrib. */ void add_pending_insert(uint32_t tid, const Mrib& mrib, const string& next_hop_vif_name); /** * Remove a MRIB entry from the MRIB table. * * @param tid the transaction ID. * @param mrib the MRIB entry to remove. */ void add_pending_remove(uint32_t tid, const Mrib& mrib); /** * Remove all MRIB entries from the MRIB table. * * @param tid the transaction ID. */ void add_pending_remove_all_entries(uint32_t tid); /** * Commit all pending MRIB entries to the MRIB table. * * @param tid the transaction ID for the pending MRIB entries to commit. */ void commit_pending_transactions(uint32_t tid); /** * Apply all changes to the table. * * Note that this may trigger various changes to the PIM protocol state * machines. */ void apply_mrib_changes(); /** * Get the list of modified prefixes since the last commit. * * @return the list of modified prefixes since the last commit. */ list& modified_prefix_list() { return (_modified_prefix_list); } /** * Resolve all destination prefixes whose next-hop vif name was not * resolved earlier (e.g., the vif was unknown). * * @param next_hop_vif_name the name of the resolved vif. * @param next_hop_vif_index the index of the resolved vif. */ void resolve_prefixes_by_vif_name(const string& next_hop_vif_name, uint32_t next_hop_vif_index); private: PimNode& _pim_node; // The PIM node this table belongs to. /** * Add/merge a modified prefix to the '_modified_prefix_list'. */ void add_modified_prefix(const IPvXNet& modified_prefix); /** * Add a destination prefix whose next-hop vif name was not resolved * (e.g., the vif is unknown). */ void add_unresolved_prefix(const IPvXNet& dest_prefix, const string& next_hop_vif_name); /** * Delete a destination prefix whose next-hop vif name was not resolved * earlier (e.g., the vif was unknown). */ void delete_unresolved_prefix(const IPvXNet& dest_prefix); // The merged and enlarged list of modified prefixes that need // to be applied to the PimMrt. list _modified_prefix_list; // The map of unresolved prefixes whose next-hop vif name was not resolved map _unresolved_prefixes; }; #endif // __PIM_PIM_MRIB_TABLE_HH__ xorp/pim/pim_mre_register.cc0000664000076400007640000002547211635757530016336 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // PIM Multicast Routing Entry Register handling // #include "pim_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvx.hh" #include "pim_proto.h" #include "pim_mfc.hh" #include "pim_mre.hh" #include "pim_node.hh" #include "pim_vif.hh" // Note: applies for (S,G) bool PimMre::compute_is_could_register_sg() const { uint32_t vif_index; Mifset mifs; if (! is_sg()) return (false); vif_index = rpf_interface_s(); if (vif_index == Vif::VIF_INDEX_INVALID) return (false); mifs = i_am_dr(); return (mifs.test(vif_index) && is_keepalive_timer_running() && is_directly_connected_s() && (! i_am_rp())); // TODO: XXX: PAVPAVPAV: not in the spec (yet) } // // PIM Register state (for the pim_register_vif_index() interface) // // Note: applies for (S,G) void PimMre::set_register_noinfo_state() { if (! is_sg()) return; if (is_register_noinfo_state()) return; // Nothing changed _flags &= ~(PIM_MRE_REGISTER_JOIN_STATE | PIM_MRE_REGISTER_PRUNE_STATE | PIM_MRE_REGISTER_JOIN_PENDING_STATE); // Try to remove the entry entry_try_remove(); } // Note: applies for (S,G) void PimMre::set_register_join_state() { if (! is_sg()) return; if (is_register_join_state()) return; // Nothing changed _flags &= ~(PIM_MRE_REGISTER_JOIN_STATE | PIM_MRE_REGISTER_PRUNE_STATE | PIM_MRE_REGISTER_JOIN_PENDING_STATE); _flags |= PIM_MRE_REGISTER_JOIN_STATE; } // Note: applies for (S,G) void PimMre::set_register_prune_state() { if (! is_sg()) return; if (is_register_prune_state()) return; // Nothing changed _flags &= ~(PIM_MRE_REGISTER_JOIN_STATE | PIM_MRE_REGISTER_PRUNE_STATE | PIM_MRE_REGISTER_JOIN_PENDING_STATE); _flags |= PIM_MRE_REGISTER_PRUNE_STATE; } // Note: applies for (S,G) void PimMre::set_register_join_pending_state() { if (! is_sg()) return; if (is_register_join_pending_state()) return; // Nothing changed _flags &= ~(PIM_MRE_REGISTER_JOIN_STATE | PIM_MRE_REGISTER_PRUNE_STATE | PIM_MRE_REGISTER_JOIN_PENDING_STATE); _flags |= PIM_MRE_REGISTER_JOIN_PENDING_STATE; } // Note: applies for (S,G) bool PimMre::is_register_noinfo_state() const { return (! (_flags & (PIM_MRE_REGISTER_JOIN_STATE | PIM_MRE_REGISTER_PRUNE_STATE | PIM_MRE_REGISTER_JOIN_PENDING_STATE))); } // Note: applies for (S,G) bool PimMre::is_register_join_state() const { return (_flags & PIM_MRE_REGISTER_JOIN_STATE); } // Note: applies for (S,G) bool PimMre::is_register_prune_state() const { return (_flags & PIM_MRE_REGISTER_PRUNE_STATE); } // Note: applies for (S,G) bool PimMre::is_register_join_pending_state() const { return (_flags & PIM_MRE_REGISTER_JOIN_PENDING_STATE); } // Return true if state has changed, otherwise return false. // Note: applies for (S,G) bool PimMre::recompute_is_could_register_sg() { if (! is_sg()) return (false); if (is_not_could_register_sg()) goto not_could_register_sg_label; if (is_could_register_sg()) goto could_register_sg_label; XLOG_UNREACHABLE(); return (false); not_could_register_sg_label: // CouldRegister -> true if (! compute_is_could_register_sg()) return (false); // Nothing changed if (is_register_noinfo_state()) { // Register NoInfo state -> Register Join state set_register_join_state(); // Add reg tunnel add_register_tunnel(); } // Set the new state set_could_register_sg(); return (true); could_register_sg_label: // CouldRegister -> false if (compute_is_could_register_sg()) return (false); // Nothing changed bool was_register_join_state = is_register_join_state(); if (is_register_join_state() || is_register_join_pending_state() || is_register_prune_state()) { // Register Join state -> Register NoInfo state set_register_noinfo_state(); } if (was_register_join_state) { // Remove reg tunnel remove_register_tunnel(); } // Set the new state set_not_could_register_sg(); return (true); } void PimMre::register_stop_timer_timeout() { PimVif *pim_vif = NULL; string dummy_error_msg; if (! is_sg()) return; if (is_register_noinfo_state()) goto register_noinfo_state; if (is_register_join_state()) goto register_join_state; if (is_register_join_pending_state()) goto register_join_pending_state; if (is_register_prune_state()) goto register_prune_state; register_noinfo_state: // Register NoInfo state // Ignore return; register_join_state: // Register Join state // Ignore return; register_join_pending_state: // Register Join-Pending state // Register Join-Pending state -> Register Join state set_register_join_state(); // Add reg tunnel add_register_tunnel(); return; register_prune_state: // Register Prune state // Register Prune state -> Register Join-Pending state set_register_join_pending_state(); // Stop timer(**) (** The Register-Stop Timer is set to Register_Probe_Time register_stop_timer() = pim_node()->eventloop().new_oneoff_after( TimeVal(PIM_REGISTER_PROBE_TIME_DEFAULT, 0), callback(this, &PimMre::register_stop_timer_timeout)); // Send Null Register pim_vif = pim_node()->vif_find_by_vif_index(rpf_interface_s()); if ((pim_vif != NULL) && pim_vif->is_up() && (rp_addr_ptr() != NULL)) { pim_vif->pim_register_null_send(*rp_addr_ptr(), source_addr(), group_addr(), dummy_error_msg); } return; } // Note: applies for (S,G) void PimMre::receive_register_stop() { if (! is_sg()) return; TimeVal register_stop_tv; TimeVal register_probe_tv; if (is_register_noinfo_state()) goto register_noinfo_state_label; if (is_register_join_state()) goto register_join_state_label; if (is_register_join_pending_state()) goto register_join_pending_state_label; if (is_register_prune_state()) goto register_prune_state_label; XLOG_UNREACHABLE(); return; register_noinfo_state_label: // Register NoInfo state return; // Ignore register_join_state_label: // Register Join state // Register Join state -> Register Prune state set_register_prune_state(); // Remove reg tunnel remove_register_tunnel(); // Set Register-Stop Timer register_stop_tv = TimeVal(PIM_REGISTER_SUPPRESSION_TIME_DEFAULT, 0); register_stop_tv = random_uniform(register_stop_tv, 0.5); register_probe_tv = TimeVal(PIM_REGISTER_PROBE_TIME_DEFAULT, 0); register_stop_tv -= register_probe_tv; register_stop_timer() = pim_node()->eventloop().new_oneoff_after( register_stop_tv, callback(this, &PimMre::register_stop_timer_timeout)); return; register_join_pending_state_label: // Register Join-Pending state // Register Join-Pending state -> Register Prune state set_register_prune_state(); // Set Register-Stop Timer register_stop_tv = TimeVal(PIM_REGISTER_SUPPRESSION_TIME_DEFAULT, 0); register_stop_tv = random_uniform(register_stop_tv, 0.5); register_probe_tv = TimeVal(PIM_REGISTER_PROBE_TIME_DEFAULT, 0); register_stop_tv -= register_probe_tv; register_stop_timer() = pim_node()->eventloop().new_oneoff_after( register_stop_tv, callback(this, &PimMre::register_stop_timer_timeout)); return; register_prune_state_label: // Register Prune state return; // Ignore } // // Perform the "RP changed" action at the (S,G) register state machine // Note that the RP has already changed and assigned by the method that // calls this one, hence we unconditionally take the "RP changed" actions. // // Note: applies for (S,G) void PimMre::rp_register_sg_changed() { if (! is_sg()) return; if (is_register_noinfo_state()) goto register_noinfo_state_label; if (is_register_join_state()) goto register_join_state_label; if (is_register_join_pending_state()) goto register_join_pending_state_label; if (is_register_prune_state()) goto register_prune_state_label; XLOG_UNREACHABLE(); return; register_noinfo_state_label: // Register NoInfo state return; // Ignore register_join_state_label: // Register Join state // Register Join state -> Register Join state // Update Register tunnel update_register_tunnel(); return; register_join_pending_state_label: // Register Join-Pending state // Register Join-Pending state -> Register Join state set_register_join_state(); // Add reg tunnel add_register_tunnel(); // Cancel Register-Stop Timer register_stop_timer().unschedule(); return; register_prune_state_label: // Register Prune state // Register Prune state -> Register Join state set_register_join_state(); // Add reg tunnel add_register_tunnel(); // Cancel Register-Stop Timer register_stop_timer().unschedule(); return; } // XXX: The Register vif implementation is not RP-specific, so it is // sufficient to update the DownstreamJpState(S,G,VI) only. // Note: applies for (S,G) void PimMre::add_register_tunnel() { uint32_t register_vif_index; if (! is_sg()) return; register_vif_index = pim_register_vif_index(); if (register_vif_index == Vif::VIF_INDEX_INVALID) return; set_downstream_join_state(register_vif_index); } // XXX: The Register vif implementation is not RP-specific, so it is // sufficient to update the DownstreamJpState(S,G,VI) only. // Note: applies for (S,G) void PimMre::remove_register_tunnel() { uint32_t register_vif_index; if (! is_sg()) return; register_vif_index = pim_register_vif_index(); if (register_vif_index == Vif::VIF_INDEX_INVALID) return; set_downstream_noinfo_state(register_vif_index); } // XXX: nothing to do because the Register vif implementation // is not RP-specific. // Note: applies for (S,G) void PimMre::update_register_tunnel() { if (! is_sg()) return; return; } xorp/pim/pim_nbr.hh0000664000076400007640000001775611540224231014426 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/pim/pim_nbr.hh,v 1.20 2008/10/02 21:57:54 bms Exp $ #ifndef __PIM_PIM_NBR_HH__ #define __PIM_PIM_NBR_HH__ // // PIM router neighbors definitions. // #include "libxorp/ipvx.hh" #include "libxorp/timer.hh" #include "pim_proto_join_prune_message.hh" // // Constants definitions // // // Structures/classes, typedefs and macros // class PimVif; class PimNbr { public: PimNbr(PimVif* pim_vif, const IPvX& primary_addr, int proto_version); ~PimNbr(); void reset_received_options(); PimNode* pim_node() const { return _pim_node; } PimVif* pim_vif() const { return _pim_vif; } uint32_t vif_index() const; const IPvX& primary_addr() const { return (_primary_addr); } void set_primary_addr(const IPvX& v) { _primary_addr = v; } const list& secondary_addr_list() const { return _secondary_addr_list; } void add_secondary_addr(const IPvX& v); void delete_secondary_addr(const IPvX& v); void clear_secondary_addr_list() { _secondary_addr_list.clear(); } bool has_secondary_addr(const IPvX& secondary_addr) const; bool is_my_addr(const IPvX& addr) const; int proto_version() const { return (_proto_version); } void set_proto_version(int v) { _proto_version = v; } uint16_t hello_holdtime() const { return (_hello_holdtime); } void set_hello_holdtime(uint16_t v) { _hello_holdtime = v; } uint32_t genid() const { return (_genid); } void set_genid(uint32_t v) { _genid = v; } bool is_genid_present() const { return (_is_genid_present); } void set_is_genid_present(bool v) { _is_genid_present = v; } uint32_t dr_priority() const { return (_dr_priority); } void set_dr_priority(uint32_t v) { _dr_priority = v; } bool is_dr_priority_present() const { return (_is_dr_priority_present); } void set_is_dr_priority_present(bool v) { _is_dr_priority_present = v; } bool is_tracking_support_disabled() const { return (_is_tracking_support_disabled); } void set_is_tracking_support_disabled(bool v) { _is_tracking_support_disabled = v; } uint16_t propagation_delay() const { return (_propagation_delay); } void set_propagation_delay(uint16_t v) { _propagation_delay = v; } uint16_t override_interval() const { return (_override_interval); } void set_override_interval(uint16_t v) { _override_interval = v; } bool is_lan_prune_delay_present() const { return (_is_lan_prune_delay_present); } void set_is_lan_prune_delay_present(bool v) { _is_lan_prune_delay_present = v; } void pim_hello_holdtime_process(uint16_t holdtime); void pim_hello_lan_prune_delay_process(bool lan_prune_delay_tbit, uint16_t propagation_delay, uint16_t override_interval); void pim_hello_dr_priority_process(uint32_t dr_priority); void pim_hello_genid_process(uint32_t genid); bool is_nohello_neighbor() const { return (_is_nohello_neighbor); } void set_is_nohello_neighbor(bool v) { _is_nohello_neighbor = v; } int jp_entry_add(const IPvX& source_addr, const IPvX& group_addr, uint8_t group_mask_len, mrt_entry_type_t mrt_entry_type, action_jp_t action_jp, uint16_t holdtime, bool is_new_group); const XorpTimer& const_neighbor_liveness_timer() const { return (_neighbor_liveness_timer); } const TimeVal& startup_time() const { return _startup_time; } void set_startup_time(const TimeVal& v) { _startup_time = v; } list& pim_mre_rp_list() { return (_pim_mre_rp_list); } list& pim_mre_wc_list() { return (_pim_mre_wc_list); } list& pim_mre_sg_list() { return (_pim_mre_sg_list); } list& pim_mre_sg_rpt_list() { return (_pim_mre_sg_rpt_list); } list& processing_pim_mre_rp_list() { return (_processing_pim_mre_rp_list); } list& processing_pim_mre_wc_list() { return (_processing_pim_mre_wc_list); } list& processing_pim_mre_sg_list() { return (_processing_pim_mre_sg_list); } list& processing_pim_mre_sg_rpt_list() { return (_processing_pim_mre_sg_rpt_list); } void init_processing_pim_mre_rp(); void init_processing_pim_mre_wc(); void init_processing_pim_mre_sg(); void init_processing_pim_mre_sg_rpt(); void add_pim_mre(PimMre *pim_mre); void delete_pim_mre(PimMre *pim_mre); private: friend class PimVif; void neighbor_liveness_timer_timeout(); void jp_send_timer_timeout(); // Fields to hold information from the PIM_HELLO messages PimNode* _pim_node; // The associated PIM node PimVif* _pim_vif; // The corresponding PIM vif IPvX _primary_addr; // The primary address of the neighbor list _secondary_addr_list; // The secondary addresses of the neighbor int _proto_version; // The protocol version of the neighbor uint32_t _genid; // The Gen-ID of the neighbor bool _is_genid_present; // Is the Gen-ID field is present uint32_t _dr_priority; // The DR priority of the neighbor bool _is_dr_priority_present;// Is the DR priority field present uint16_t _hello_holdtime; // The Holdtime from/for this nbr XorpTimer _neighbor_liveness_timer; // Timer to expire the neighbor // LAN Prune Delay option related info bool _is_tracking_support_disabled; // The T-bit uint16_t _propagation_delay; // The Propagation_Delay uint16_t _override_interval; // The Override_Interval bool _is_lan_prune_delay_present;// Is the LAN Prune Delay present bool _is_nohello_neighbor; // True if no-Hello neighbor XorpTimer _jp_send_timer; // Timer to send the accumulated JP msg PimJpHeader _jp_header; TimeVal _startup_time; // Start-up time of this neighbor // // The lists of all related PimMre entries. // XXX: note that we don't have a list for PimMfc entries, because // they don't depend directly on PimNbr. For example, PimMfc has // a field for the RP address, therefore PimRp contains a list of // PimMfc entries related to that RP. // list _pim_mre_rp_list; // List of all related (*,*,RP) entries // for this PimNbr. list _pim_mre_wc_list; // List of all related (*,G) entries // for this PimNbr. list _pim_mre_sg_list; // List of all related (S,G) entries // for this PimNbr. list _pim_mre_sg_rpt_list;// List of all related (S,G,rpt) // entries for this PimNbr. list _processing_pim_mre_rp_list; // List of all related // (*,*,RP) entries for this PimNbr // that are awaiting to be processed. list _processing_pim_mre_wc_list; // List of all related // (*,G) entries for this PimNbr // that are awaiting to be processed. list _processing_pim_mre_sg_list; // List of all related // (S,G) entries for this PimNbr that // are awaiting to be processed. list _processing_pim_mre_sg_rpt_list;// List of all related // (S,G,rpt) entries for this PimNbr // that are awaiting to be processed. }; // // Global variables // // // Global functions prototypes // #endif // __PIM_PIM_NBR_HH__ xorp/pim/pim_module.h0000664000076400007640000000230011421137511014737 0ustar greearbgreearb/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- */ /* * Copyright (c) 2001-2009 XORP, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 2, June * 1991 as published by the Free Software Foundation. Redistribution * and/or modification of this program under the terms of any other * version of the GNU General Public License is not permitted. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, * see the GNU General Public License, Version 2, a copy of which can be * found in the XORP LICENSE.gpl file. * * XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; * http://xorp.net */ /* * $XORP: xorp/pim/pim_module.h,v 1.11 2008/10/02 21:57:52 bms Exp $ */ /* * Module definitions. */ #ifndef __PIM_PIM_MODULE_H__ #define __PIM_PIM_MODULE_H__ #ifndef XORP_MODULE_NAME #define XORP_MODULE_NAME "PIM" #endif #ifndef XORP_MODULE_VERSION #define XORP_MODULE_VERSION "0.1" #endif #endif /* __PIM_PIM_MODULE_H__ */ xorp/pim/pim_scope_zone_table.cc0000664000076400007640000002552211421137511017136 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // PIM scope zone table implementation. // #include "pim_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvx.hh" #include "pim_scope_zone_table.hh" // // Exported variables // // // Local constants definitions // // // Local structures/classes, typedefs and macros // // // Local variables // // // Local functions prototypes // /** * PimScopeZoneId::PimScopeZoneId: * @scope_zone_prefix: The scope zone address prefix. * @is_scope_zone: If true, this is administratively scoped zone, otherwise * this is the global zone. * * PimScopeZoneId constructor. **/ PimScopeZoneId::PimScopeZoneId(const IPvXNet& scope_zone_prefix, bool is_scope_zone) : _scope_zone_prefix(scope_zone_prefix), _is_scope_zone(is_scope_zone) { } /** * Equality Operator * * @param other the right-hand operand to compare against. * @return true if the left-hand operand is numerically same as the * right-hand operand. */ bool PimScopeZoneId::operator==(const PimScopeZoneId& other) const { return ((scope_zone_prefix() == other.scope_zone_prefix()) && (is_scope_zone() == other.is_scope_zone())); } /** * PimScopeZoneId::is_overlap: * * Tehst whether a scope zone overlaps with another zone. * * @param other the other scope zone ID to compare against. * @return true if both zones are scoped and the the scope zone prefixes * for this zone and @other do overlap. */ bool PimScopeZoneId::is_overlap(const PimScopeZoneId& other) const { return (is_scope_zone() && other.is_scope_zone() && scope_zone_prefix().is_overlap(other.scope_zone_prefix())); } /** * PimScopeZoneId::contains: * @ipvxnet: The subnet address to compare whether is contained within this * scope zone. * * Test whether a scope zone contains a subnet address. * * Return value: true if this scope zone contains @ipvxnet, otherwise false. **/ bool PimScopeZoneId::contains(const IPvXNet& ipvxnet) const { return (scope_zone_prefix().contains(ipvxnet)); } /** * PimScopeZoneId::contains: * @ipvx: The address to compare whether is contained within this * scope zone. * * Test whether a scope zone contains an address. * * Return value: true if this scope zone contains @ipvx, otherwise false. **/ bool PimScopeZoneId::contains(const IPvX& ipvx) const { return (scope_zone_prefix().contains(ipvx)); } /** * PimScopeZoneId::str: * * Convert this scope zone ID from binary for to presentation format. * * Return value: C++ string with the human-readable ASCII representation * of the scope zone ID. **/ string PimScopeZoneId::str() const { return (c_format("%s(%s)", _scope_zone_prefix.str().c_str(), _is_scope_zone? "scoped" : "non-scoped")); } /** * PimScopeZoneTable::PimScopeZoneTable: * @pim_node: The associated PIM node. * * PimScopeZoneTable constructor. **/ PimScopeZoneTable::PimScopeZoneTable(PimNode& pim_node) : _pim_node(pim_node) { } /** * PimScopeZoneTable::~PimScopeZoneTable: * @: * * PimScopeZoneTable destructor. * **/ PimScopeZoneTable::~PimScopeZoneTable() { } /** * PimScopeZoneTable::add_scope_zone: * @scope_zone_prefix: The prefix address of the zone to add. * @vif_index: The vif index of the interface to add as zone boundary. * * Add a scope zone. **/ void PimScopeZoneTable::add_scope_zone(const IPvXNet& scope_zone_prefix, uint32_t vif_index) { // Test first if we have that scope zone already list::iterator iter; for (iter = _pim_scope_zone_list.begin(); iter != _pim_scope_zone_list.end(); ++iter) { PimScopeZone& pim_scope_zone = *iter; if (pim_scope_zone.is_same_scope_zone(scope_zone_prefix)) { // We already have entry for this scope zone pim_scope_zone.set_scoped_vif(vif_index, true); return; } } // Add the new scope Mifset scoped_vifs; scoped_vifs.set(vif_index); PimScopeZone pim_scope_zone(scope_zone_prefix, scoped_vifs); _pim_scope_zone_list.push_back(pim_scope_zone); } /** * PimScopeZoneTable::delete_scope_zone: * @scope_zone_prefix: The prefix address of the zone to delete. * @vif_index: The vif index of the interface to delete as zone boundary. * * Delete a scope zone. **/ void PimScopeZoneTable::delete_scope_zone(const IPvXNet& scope_zone_prefix, uint32_t vif_index) { // Find the scope zone and delete it list::iterator iter; for (iter = _pim_scope_zone_list.begin(); iter != _pim_scope_zone_list.end(); ++iter) { PimScopeZone& pim_scope_zone = *iter; if (pim_scope_zone.is_same_scope_zone(scope_zone_prefix)) { // Found pim_scope_zone.set_scoped_vif(vif_index, false); // If last scope zone boundary, remove the entry if (pim_scope_zone.is_empty()) _pim_scope_zone_list.erase(iter); return; } } } /** * PimScopeZoneTable::is_scoped: * @addr: The address to test whether it is scoped. * @vif_index: The vif index of the interface to test. * * Test if address @addr is scoped on interface with vif index of @vif_index. * Note that we test against all scope zones until a scoping one is found. * * Return value: True if @addr is scoped on @vif_index, otherwise false. **/ bool PimScopeZoneTable::is_scoped(const IPvX& addr, uint32_t vif_index) const { list::const_iterator iter; for (iter = _pim_scope_zone_list.begin(); iter != _pim_scope_zone_list.end(); ++iter) { const PimScopeZone& pim_scope_zone = *iter; if (pim_scope_zone.is_scoped(addr, vif_index)) return (true); } return (false); } /** * PimScopeZoneTable::is_scoped: * @zone_id: The zone ID to test whether it is scoped. * @vif_index: The vif index of the interface to test. * * Test if zone with zone ID of @zone_id is scoped on interface with * vif index of @vif_index. * Note that we test against all scope zones until a scoping one is found. * * Return value: True if @zone_id is scoped on @vif_index, otherwise false. **/ bool PimScopeZoneTable::is_scoped(const PimScopeZoneId& zone_id, uint32_t vif_index) const { if (! zone_id.is_scope_zone()) return (false); list::const_iterator iter; for (iter = _pim_scope_zone_list.begin(); iter != _pim_scope_zone_list.end(); ++iter) { const PimScopeZone& pim_scope_zone = *iter; if (pim_scope_zone.is_scoped(zone_id, vif_index)) return (true); } return (false); } /** * PimScopeZoneTable::is_zone_border_router: * @group_prefix: The group prefix to test. * * Test if the router is a Zone Border Router (ZBR) for @group_prefix. * * Return value: True if the router is a ZBR for @group_prefix, otherwise * false. **/ bool PimScopeZoneTable::is_zone_border_router(const IPvXNet& group_prefix) const { list::const_iterator iter; for (iter = _pim_scope_zone_list.begin(); iter != _pim_scope_zone_list.end(); ++iter) { const PimScopeZone& pim_scope_zone = *iter; if (pim_scope_zone.scope_zone_prefix().contains(group_prefix)) return (true); } return (false); } /** * PimScopeZone::PimScopeZone: * @scope_zone_prefix: The scope zone address prefix. * @scoped_vifs: The bitmask with the vifs that are zone boundary. * * PimScopeZone constructor. **/ PimScopeZone::PimScopeZone(const IPvXNet& scope_zone_prefix, const Mifset& scoped_vifs) : _scope_zone_prefix(scope_zone_prefix), _scoped_vifs(scoped_vifs) { } /** * PimScopeZone::~PimScopeZone: * @: * * PimScopeZone destructor. **/ PimScopeZone::~PimScopeZone() { } /** * PimScopeZone::set_scoped_vif: * @vif_index: The vif index of the interface to set or reset. * @v: If true, set the interface as a boundary for this scope zone, * otherwise reset it. * * Set or reset an interface as a boundary for this scope zone. **/ void PimScopeZone::set_scoped_vif(uint32_t vif_index, bool v) { if (vif_index < _scoped_vifs.size()) { if (v) _scoped_vifs.set(vif_index); else _scoped_vifs.reset(vif_index); } } /** * PimScopeZone::is_scoped: * @addr: The address to test whether it is scoped. * @vif_index: The vif index of the interface to test. * * Test if address @addr is scoped on interface with vif index of @vif_index. * * Return value: True if @addr is scoped on @vif_index, otherwise false. **/ bool PimScopeZone::is_scoped(const IPvX& addr, uint32_t vif_index) const { if (! _scope_zone_prefix.contains(addr)) return (false); return (is_set(vif_index)); } /** * PimScopeZone::is_scoped: * @zone_id: The zone ID to test whether it is scoped. * @vif_index: The vif index of the interface to test. * * Test if zone with zone ID of @zone_id is scoped on interface with * vif index of @vif_index. * * Return value: True if @zone_id is scoped on @vif_index, otherwise false. **/ bool PimScopeZone::is_scoped(const PimScopeZoneId& zone_id, uint32_t vif_index) const { if (! zone_id.is_scope_zone()) return (false); // XXX: scoped zones don't nest, hence if the scope zone prefixes // do overlap, then there is scoping. if (! _scope_zone_prefix.is_overlap(zone_id.scope_zone_prefix())) return (false); return (is_set(vif_index)); } /** * PimScopeZone::is_set: * @vif_index: The vif index of the interface to test. * * Test if an interface is a boundary for this scope zone. * * Return value: True if the tested interface is a boundary for this scope * zone, otherwise false. **/ bool PimScopeZone::is_set(uint32_t vif_index) const { if (vif_index < _scoped_vifs.size()) return (_scoped_vifs.test(vif_index)); return (false); } /** * PimScopeZone::is_same_scope_zone: * @scope_zone_prefix: The prefix address to test if it is the prefix * address of this scope zone. * * Test if this scope zone prefix address is same as @scope_zone_prefix. * * Return value: True if @scope_zone_prefix is same prefix address * as the prefix address of this scope zone, otherwise false. **/ bool PimScopeZone::is_same_scope_zone(const IPvXNet& scope_zone_prefix) const { return (_scope_zone_prefix == scope_zone_prefix); } xorp/devnotes/0000775000076400007640000000000011703346327013515 5ustar greearbgreearbxorp/devnotes/template_lgpl.py0000664000076400007640000000172611421137511016714 0ustar greearbgreearb#!/usr/bin/env python # Copyright (c) 2008-2009 XORP, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License, Version # 2.1, June 1999 as published by the Free Software Foundation. # Redistribution and/or modification of this program under the terms of # any other version of the GNU Lesser General Public License is not # permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU Lesser General Public License, Version 2.1, a copy of # which can be found in the XORP LICENSE.lgpl file. # # XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # $XORP: xorp/devnotes/template_lgpl.py,v 1.1 2008/10/02 21:56:43 bms Exp $ # Local Variables: # mode: python # py-indent-offset: 4 # End: xorp/devnotes/processes3.ps0000664000076400007640000005425711421137511016155 0ustar greearbgreearb%!PS-Adobe-2.0 EPSF-1.2 %%Creator: idraw %%DocumentFonts: Helvetica %%Pages: 1 %%BoundingBox: 6 364 444 748 %%EndComments %%BeginIdrawPrologue /arrowhead { 0 begin transform originalCTM itransform /taily exch def /tailx exch def transform originalCTM itransform /tipy exch def /tipx exch def /dy tipy taily sub def /dx tipx tailx sub def /angle dx 0 ne dy 0 ne or { dy dx atan } { 90 } ifelse def gsave originalCTM setmatrix tipx tipy translate angle rotate newpath arrowHeight neg arrowWidth 2 div moveto 0 0 lineto arrowHeight neg arrowWidth 2 div neg lineto patternNone not { originalCTM setmatrix /padtip arrowHeight 2 exp 0.25 arrowWidth 2 exp mul add sqrt brushWidth mul arrowWidth div def /padtail brushWidth 2 div def tipx tipy translate angle rotate padtip 0 translate arrowHeight padtip add padtail add arrowHeight div dup scale arrowheadpath ifill } if brushNone not { originalCTM setmatrix tipx tipy translate angle rotate arrowheadpath istroke } if grestore end } dup 0 9 dict put def /arrowheadpath { newpath arrowHeight neg arrowWidth 2 div moveto 0 0 lineto arrowHeight neg arrowWidth 2 div neg lineto } def /leftarrow { 0 begin y exch get /taily exch def x exch get /tailx exch def y exch get /tipy exch def x exch get /tipx exch def brushLeftArrow { tipx tipy tailx taily arrowhead } if end } dup 0 4 dict put def /rightarrow { 0 begin y exch get /tipy exch def x exch get /tipx exch def y exch get /taily exch def x exch get /tailx exch def brushRightArrow { tipx tipy tailx taily arrowhead } if end } dup 0 4 dict put def %%EndIdrawPrologue /arrowHeight 8 def /arrowWidth 4 def /IdrawDict 51 dict def IdrawDict begin /reencodeISO { dup dup findfont dup length dict begin { 1 index /FID ne { def }{ pop pop } ifelse } forall /Encoding ISOLatin1Encoding def currentdict end definefont } def /ISOLatin1Encoding [ /.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef /.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef /.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef /.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef /space/exclam/quotedbl/numbersign/dollar/percent/ampersand/quoteright /parenleft/parenright/asterisk/plus/comma/minus/period/slash /zero/one/two/three/four/five/six/seven/eight/nine/colon/semicolon /less/equal/greater/question/at/A/B/C/D/E/F/G/H/I/J/K/L/M/N /O/P/Q/R/S/T/U/V/W/X/Y/Z/bracketleft/backslash/bracketright /asciicircum/underscore/quoteleft/a/b/c/d/e/f/g/h/i/j/k/l/m /n/o/p/q/r/s/t/u/v/w/x/y/z/braceleft/bar/braceright/asciitilde /.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef /.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef /.notdef/dotlessi/grave/acute/circumflex/tilde/macron/breve /dotaccent/dieresis/.notdef/ring/cedilla/.notdef/hungarumlaut /ogonek/caron/space/exclamdown/cent/sterling/currency/yen/brokenbar /section/dieresis/copyright/ordfeminine/guillemotleft/logicalnot /hyphen/registered/macron/degree/plusminus/twosuperior/threesuperior /acute/mu/paragraph/periodcentered/cedilla/onesuperior/ordmasculine /guillemotright/onequarter/onehalf/threequarters/questiondown /Agrave/Aacute/Acircumflex/Atilde/Adieresis/Aring/AE/Ccedilla /Egrave/Eacute/Ecircumflex/Edieresis/Igrave/Iacute/Icircumflex /Idieresis/Eth/Ntilde/Ograve/Oacute/Ocircumflex/Otilde/Odieresis /multiply/Oslash/Ugrave/Uacute/Ucircumflex/Udieresis/Yacute /Thorn/germandbls/agrave/aacute/acircumflex/atilde/adieresis /aring/ae/ccedilla/egrave/eacute/ecircumflex/edieresis/igrave /iacute/icircumflex/idieresis/eth/ntilde/ograve/oacute/ocircumflex /otilde/odieresis/divide/oslash/ugrave/uacute/ucircumflex/udieresis /yacute/thorn/ydieresis ] def /Helvetica reencodeISO def /none null def /numGraphicParameters 17 def /stringLimit 65535 def /Begin { save numGraphicParameters dict begin } def /End { end restore } def /SetB { dup type /nulltype eq { pop false /brushRightArrow idef false /brushLeftArrow idef true /brushNone idef } { /brushDashOffset idef /brushDashArray idef 0 ne /brushRightArrow idef 0 ne /brushLeftArrow idef /brushWidth idef false /brushNone idef } ifelse } def /SetCFg { /fgblue idef /fggreen idef /fgred idef } def /SetCBg { /bgblue idef /bggreen idef /bgred idef } def /SetF { /printSize idef /printFont idef } def /SetP { dup type /nulltype eq { pop true /patternNone idef } { dup -1 eq { /patternGrayLevel idef /patternString idef } { /patternGrayLevel idef } ifelse false /patternNone idef } ifelse } def /BSpl { 0 begin storexyn newpath n 1 gt { 0 0 0 0 0 0 1 1 true subspline n 2 gt { 0 0 0 0 1 1 2 2 false subspline 1 1 n 3 sub { /i exch def i 1 sub dup i dup i 1 add dup i 2 add dup false subspline } for n 3 sub dup n 2 sub dup n 1 sub dup 2 copy false subspline } if n 2 sub dup n 1 sub dup 2 copy 2 copy false subspline patternNone not brushLeftArrow not brushRightArrow not and and { ifill } if brushNone not { istroke } if 0 0 1 1 leftarrow n 2 sub dup n 1 sub dup rightarrow } if end } dup 0 4 dict put def /Circ { newpath 0 360 arc patternNone not { ifill } if brushNone not { istroke } if } def /CBSpl { 0 begin dup 2 gt { storexyn newpath n 1 sub dup 0 0 1 1 2 2 true subspline 1 1 n 3 sub { /i exch def i 1 sub dup i dup i 1 add dup i 2 add dup false subspline } for n 3 sub dup n 2 sub dup n 1 sub dup 0 0 false subspline n 2 sub dup n 1 sub dup 0 0 1 1 false subspline patternNone not { ifill } if brushNone not { istroke } if } { Poly } ifelse end } dup 0 4 dict put def /Elli { 0 begin newpath 4 2 roll translate scale 0 0 1 0 360 arc patternNone not { ifill } if brushNone not { istroke } if end } dup 0 1 dict put def /Line { 0 begin 2 storexyn newpath x 0 get y 0 get moveto x 1 get y 1 get lineto brushNone not { istroke } if 0 0 1 1 leftarrow 0 0 1 1 rightarrow end } dup 0 4 dict put def /MLine { 0 begin storexyn newpath n 1 gt { x 0 get y 0 get moveto 1 1 n 1 sub { /i exch def x i get y i get lineto } for patternNone not brushLeftArrow not brushRightArrow not and and { ifill } if brushNone not { istroke } if 0 0 1 1 leftarrow n 2 sub dup n 1 sub dup rightarrow } if end } dup 0 4 dict put def /Poly { 3 1 roll newpath moveto -1 add { lineto } repeat closepath patternNone not { ifill } if brushNone not { istroke } if } def /Rect { 0 begin /t exch def /r exch def /b exch def /l exch def newpath l b moveto l t lineto r t lineto r b lineto closepath patternNone not { ifill } if brushNone not { istroke } if end } dup 0 4 dict put def /Text { ishow } def /idef { dup where { pop pop pop } { exch def } ifelse } def /ifill { 0 begin gsave patternGrayLevel -1 ne { fgred bgred fgred sub patternGrayLevel mul add fggreen bggreen fggreen sub patternGrayLevel mul add fgblue bgblue fgblue sub patternGrayLevel mul add setrgbcolor eofill } { eoclip originalCTM setmatrix pathbbox /t exch def /r exch def /b exch def /l exch def /w r l sub ceiling cvi def /h t b sub ceiling cvi def /imageByteWidth w 8 div ceiling cvi def /imageHeight h def bgred bggreen bgblue setrgbcolor eofill fgred fggreen fgblue setrgbcolor w 0 gt h 0 gt and { l w add b translate w neg h scale w h true [w 0 0 h neg 0 h] { patternproc } imagemask } if } ifelse grestore end } dup 0 8 dict put def /istroke { gsave brushDashOffset -1 eq { [] 0 setdash 1 setgray } { brushDashArray brushDashOffset setdash fgred fggreen fgblue setrgbcolor } ifelse brushWidth setlinewidth originalCTM setmatrix stroke grestore } def /ishow { 0 begin gsave fgred fggreen fgblue setrgbcolor /fontDict printFont printSize scalefont dup setfont def /descender fontDict begin 0 [FontBBox] 1 get FontMatrix end transform exch pop def /vertoffset 1 printSize sub descender sub def { 0 vertoffset moveto show /vertoffset vertoffset printSize sub def } forall grestore end } dup 0 3 dict put def /patternproc { 0 begin /patternByteLength patternString length def /patternHeight patternByteLength 8 mul sqrt cvi def /patternWidth patternHeight def /patternByteWidth patternWidth 8 idiv def /imageByteMaxLength imageByteWidth imageHeight mul stringLimit patternByteWidth sub min def /imageMaxHeight imageByteMaxLength imageByteWidth idiv patternHeight idiv patternHeight mul patternHeight max def /imageHeight imageHeight imageMaxHeight sub store /imageString imageByteWidth imageMaxHeight mul patternByteWidth add string def 0 1 imageMaxHeight 1 sub { /y exch def /patternRow y patternByteWidth mul patternByteLength mod def /patternRowString patternString patternRow patternByteWidth getinterval def /imageRow y imageByteWidth mul def 0 patternByteWidth imageByteWidth 1 sub { /x exch def imageString imageRow x add patternRowString putinterval } for } for imageString end } dup 0 12 dict put def /min { dup 3 2 roll dup 4 3 roll lt { exch } if pop } def /max { dup 3 2 roll dup 4 3 roll gt { exch } if pop } def /midpoint { 0 begin /y1 exch def /x1 exch def /y0 exch def /x0 exch def x0 x1 add 2 div y0 y1 add 2 div end } dup 0 4 dict put def /thirdpoint { 0 begin /y1 exch def /x1 exch def /y0 exch def /x0 exch def x0 2 mul x1 add 3 div y0 2 mul y1 add 3 div end } dup 0 4 dict put def /subspline { 0 begin /movetoNeeded exch def y exch get /y3 exch def x exch get /x3 exch def y exch get /y2 exch def x exch get /x2 exch def y exch get /y1 exch def x exch get /x1 exch def y exch get /y0 exch def x exch get /x0 exch def x1 y1 x2 y2 thirdpoint /p1y exch def /p1x exch def x2 y2 x1 y1 thirdpoint /p2y exch def /p2x exch def x1 y1 x0 y0 thirdpoint p1x p1y midpoint /p0y exch def /p0x exch def x2 y2 x3 y3 thirdpoint p2x p2y midpoint /p3y exch def /p3x exch def movetoNeeded { p0x p0y moveto } if p1x p1y p2x p2y p3x p3y curveto end } dup 0 17 dict put def /storexyn { /n exch def /y n array def /x n array def n 1 sub -1 0 { /i exch def y i 3 2 roll put x i 3 2 roll put } for } def /SSten { fgred fggreen fgblue setrgbcolor dup true exch 1 0 0 -1 0 6 -1 roll matrix astore } def /FSten { dup 3 -1 roll dup 4 1 roll exch newpath 0 0 moveto dup 0 exch lineto exch dup 3 1 roll exch lineto 0 lineto closepath bgred bggreen bgblue setrgbcolor eofill SSten } def /Rast { exch dup 3 1 roll 1 0 0 -1 0 6 -1 roll matrix astore } def %%EndProlog %I Idraw 10 Grid 4 4 %%Page: 1 1 Begin %I b u %I cfg u %I cbg u %I f u %I p u %I t [ 0.957041 0 0 0.957041 0 0 ] concat /originalCTM matrix currentmatrix def Begin %I Rect none SetB %I b n %I cfg Black 0 0 0 SetCFg %I cbg VLtGray 0.839246 0.839246 0.839246 SetCBg %I p 1 SetP %I t [ 0.998207 -0 -0 0.998207 -69.8745 8.98386 ] concat %I 110 407 510 439 Rect End Begin %I Rect none SetB %I b n %I cfg Black 0 0 0 SetCFg %I cbg VLtGray 0.839246 0.839246 0.839246 SetCBg %I p 1 SetP %I t [ 0.998207 -0 -0 0.998207 -69.8745 8.98386 ] concat %I 406 511 526 679 Rect End Begin %I Rect none SetB %I b n %I cfg Black 0 0 0 SetCFg %I cbg VLtGray 0.839246 0.839246 0.839246 SetCBg %I p 1 SetP %I t [ 0.998207 -0 -0 0.998207 -69.8745 8.98386 ] concat %I 78 695 534 759 Rect End Begin %I Rect none SetB %I b n %I cfg Black 0 0 0 SetCFg %I cbg VLtGray 0.839246 0.839246 0.839246 SetCBg %I p 1 SetP %I t [ 0.998207 -0 -0 0.998207 -69.8745 8.98386 ] concat %I 78 455 198 687 Rect End Begin %I Rect %I b 65535 0 0 0 [] 0 SetB %I cfg Black 0 0 0 SetCFg %I cbg White 1 1 1 SetCBg %I p 1 SetP %I t [ 1 0 0 1 -123.739 -20.9595 ] concat %I 468 653 572 701 Rect End Begin %I Text %I cfg Black 0 0 0 SetCFg %I f -*-helvetica-medium-r-normal-*-14-*-*-*-*-*-*-* Helvetica 14 SetF %I t [ 1 0 0 1 369.761 661.04 ] concat %I [ (PIM-SM) ] Text End Begin %I Rect %I b 65535 0 0 0 [] 0 SetB %I cfg Black 0 0 0 SetCFg %I cbg White 1 1 1 SetCBg %I p 1 SetP %I t [ 1 0 0 1 -115.997 28.0549 ] concat %I 132 500 236 548 Rect End Begin %I Text %I cfg Black 0 0 0 SetCFg %I f -*-helvetica-medium-r-normal-*-14-*-*-*-*-*-*-* Helvetica 14 SetF %I t [ 1 0 0 1 57.003 557.055 ] concat %I [ (RIP) ] Text End Begin %I Rect %I b 65535 0 0 0 [] 0 SetB %I cfg Black 0 0 0 SetCFg %I cbg White 1 1 1 SetCBg %I p 1 SetP %I t [ 1 0 0 1 -83.882 155.98 ] concat %I 276 316 380 364 Rect End Begin %I Text %I cfg Black 0 0 0 SetCFg %I f -*-helvetica-medium-r-normal-*-14-*-*-*-*-*-*-* Helvetica 14 SetF %I t [ 1 0 0 1 230.118 500.98 ] concat %I [ (FEA) ] Text End Begin %I Line %I b 65535 0 1 1 [] 0 SetB %I cfg Black 0 0 0 SetCFg %I cbg White 1 1 1 SetCBg none SetP %I p n %I t [ 1 -0 -0 1 -179.882 2.98035 ] concat %I 388 469 388 445 Line %I 1 End Begin %I Line %I b 65535 0 1 1 [] 0 SetB %I cfg Black 0 0 0 SetCFg %I cbg White 1 1 1 SetCBg none SetP %I p n %I t [ 1 -0 -0 1 -179.882 2.98035 ] concat %I 420 469 420 445 Line %I 1 End Begin %I Line %I b 65535 0 1 1 [] 0 SetB %I cfg Black 0 0 0 SetCFg %I cbg White 1 1 1 SetCBg none SetP %I p n %I t [ 1 -0 -0 1 -179.882 2.98035 ] concat %I 452 469 452 445 Line %I 1 End Begin %I Text %I cfg Black 0 0 0 SetCFg %I f -*-helvetica-medium-r-normal-*-14-*-*-*-*-*-*-* Helvetica 14 SetF %I t [ 1 0 0 1 96.2455 433.984 ] concat %I [ (Forwarding Engine) ] Text End Begin %I Poly %I b 65535 0 0 0 [] 0 SetB %I cfg Black 0 0 0 SetCFg %I cbg White 1 1 1 SetCBg %I p 1 SetP %I t [ 1 0 0 1 -259.739 -53.0744 ] concat %I 4 604 581 708 581 708 629 604 629 4 Poly End Begin %I Text %I cfg Black 0 0 0 SetCFg %I f -*-helvetica-medium-r-normal-*-14-*-*-*-*-*-*-* Helvetica 14 SetF %I t [ 1 0 0 1 360.261 556.926 ] concat %I [ (IGMP/MLD) ] Text End Begin %I Rect %I b 65535 0 0 0 [] 0 SetB %I cfg Black 0 0 0 SetCFg %I cbg White 1 1 1 SetCBg %I p 1 SetP %I t [ 1 0 0 1 108.051 -68.9504 ] concat %I 132 781 236 829 Rect End Begin %I Text %I cfg Black 0 0 0 SetCFg %I f -*-helvetica-medium-r-normal-*-14-*-*-*-*-*-*-* Helvetica 14 SetF %I t [ 1 0 0 1 281.051 741.05 ] concat %I [ (CLI) ] Text End Begin %I Rect %I b 65535 0 0 0 [] 0 SetB %I cfg Black 0 0 0 SetCFg %I cbg White 1 1 1 SetCBg %I p 1 SetP %I t [ 1 0 0 1 220.019 -68.9471 ] concat %I 132 781 236 829 Rect End Begin %I Text %I cfg Black 0 0 0 SetCFg %I f -*-helvetica-medium-r-normal-*-14-*-*-*-*-*-*-* Helvetica 14 SetF %I t [ 1 0 0 1 384.019 741.053 ] concat %I [ (SNMP) ] Text End Begin %I Rect %I b 65535 0 0 0 [] 0 SetB %I cfg Black 0 0 0 SetCFg %I cbg White 1 1 1 SetCBg %I p 1 SetP %I t [ 1 0 0 1 -115.88 -68.9504 ] concat %I 132 781 236 829 Rect End Begin %I Text %I cfg Black 0 0 0 SetCFg %I f -*-helvetica-medium-r-normal-*-14-*-*-*-*-*-*-* Helvetica 14 SetF %I t [ 1 0 0 1 51.1204 748.05 ] concat %I [ (IPC) (finder) ] Text End Begin %I Rect %I b 65535 0 0 0 [] 0 SetB %I cfg Black 0 0 0 SetCFg %I cbg White 1 1 1 SetCBg %I p 1 SetP %I t [ 1 0 0 1 -115.98 20.0692 ] concat %I 132 564 236 612 Rect End Begin %I Text %I cfg Black 0 0 0 SetCFg %I f -*-helvetica-medium-r-normal-*-14-*-*-*-*-*-*-* Helvetica 14 SetF %I t [ 1 0 0 1 49.5201 617.069 ] concat %I [ (OSPF) ] Text End Begin %I Rect %I b 65535 0 0 0 [] 0 SetB %I cfg Black 0 0 0 SetCFg %I cbg White 1 1 1 SetCBg %I p 1 SetP %I t [ 1 0 0 1 -115.994 -27.93 ] concat %I 132 500 236 548 Rect End Begin %I Text %I cfg Black 0 0 0 SetCFg %I f -*-helvetica-medium-r-normal-*-14-*-*-*-*-*-*-* Helvetica 14 SetF %I t [ 1 0 0 1 52.006 503.018 ] concat %I [ (IS-IS) ] Text End Begin %I Rect %I b 65535 0 0 0 [] 0 SetB %I cfg Black 0 0 0 SetCFg %I cbg White 1 1 1 SetCBg %I p 1 SetP %I t [ 1 0 0 1 -3.91762 -68.9537 ] concat %I 132 781 236 829 Rect End Begin %I Text %I cfg Black 0 0 0 SetCFg %I f -*-helvetica-medium-r-normal-*-14-*-*-*-*-*-*-* Helvetica 14 SetF %I t [ 1 0 0 1 152.582 748.046 ] concat %I [ (router) (manager) ] Text End Begin %I Rect %I b 65535 0 0 0 [] 0 SetB %I cfg Black 0 0 0 SetCFg %I cbg White 1 1 1 SetCBg %I p 1 SetP %I t [ 1 0 0 1 -115.997 -140.917 ] concat %I 132 781 236 829 Rect End Begin %I Text %I cfg Black 0 0 0 SetCFg %I f -*-helvetica-medium-r-normal-*-14-*-*-*-*-*-*-* Helvetica 14 SetF %I t [ 1 0 0 1 45.503 669.083 ] concat %I [ (BGP4+) ] Text End Begin %I Rect %I b 65535 0 0 0 [] 0 SetB %I cfg Black 0 0 0 SetCFg %I cbg White 1 1 1 SetCBg %I p 1 SetP %I t [ 1 -0 -0 1 -84.0098 155.98 ] concat %I 276 428 380 476 Rect End Begin %I Text %I cfg Black 0 0 0 SetCFg %I f -*-helvetica-medium-r-normal-*-14-*-*-*-*-*-*-* Helvetica 14 SetF %I t [ 1 0 0 1 232.99 612.98 ] concat %I [ (RIB) ] Text End Begin %I Line %I b 65535 0 0 1 [] 0 SetB %I cfg Black 0 0 0 SetCFg %I cbg White 1 1 1 SetCBg %I p 0 SetP %I t [ 0.998207 -0 -0 0.998207 -69.8745 8.98386 ] concat %I 310 575 310 511 Line %I 1 End Begin %I Line %I b 65535 0 1 1 [] 0 SetB %I cfg Black 0 0 0 SetCFg %I cbg White 1 1 1 SetCBg %I p 0 SetP %I t [ 0.998207 -0 -0 0.998207 -69.8745 8.98386 ] concat %I 422 623 350 512 Line %I 1 End Begin %I Line %I b 65535 0 1 1 [] 0 SetB %I cfg Black 0 0 0 SetCFg %I cbg White 1 1 1 SetCBg %I p 0 SetP %I t [ 0.998207 -0 -0 0.998207 -69.8745 8.98386 ] concat %I 366 503 414 535 Line %I 1 End Begin %I Line %I b 65535 0 0 1 [] 0 SetB %I cfg Black 0 0 0 SetCFg %I cbg Black 0 0 0 SetCBg %I p 1 SetP %I t [ 0.998207 -0 -0 0.998207 -69.8745 8.98386 ] concat %I 366 615 414 647 Line %I 1 End Begin %I Line %I b 65535 0 0 1 [] 0 SetB %I cfg Black 0 0 0 SetCFg %I cbg Black 0 0 0 SetCBg %I p 1 SetP %I t [ 0.998207 -0 -0 0.998207 -69.8745 8.98386 ] concat %I 470 567 470 623 Line %I 1 End Begin %I Line %I b 65535 0 0 1 [] 0 SetB %I cfg Black 0 0 0 SetCFg %I cbg Black 0 0 0 SetCBg %I p 1 SetP %I t [ 0.998207 -0 -0 0.998207 -69.8745 8.98386 ] concat %I 190 487 262 592 Line %I 1 End Begin %I Line %I b 65535 0 0 1 [] 0 SetB %I cfg Black 0 0 0 SetCFg %I cbg White 1 1 1 SetCBg %I p 0 SetP %I t [ 0.998207 -0 -0 0.998207 -69.8745 8.98386 ] concat %I 190 543 262 600 Line %I 1 End Begin %I Line %I b 65535 0 0 1 [] 0 SetB %I cfg Black 0 0 0 SetCFg %I cbg White 1 1 1 SetCBg %I p 0 SetP %I t [ 0.998207 -0 -0 0.998207 -69.8745 8.98386 ] concat %I 190 599 262 608 Line %I 1 End Begin %I Line %I b 65535 0 1 1 [] 0 SetB %I cfg Black 0 0 0 SetCFg %I cbg White 1 1 1 SetCBg %I p 0 SetP %I t [ 0.998207 -0 -0 0.998207 -69.8745 8.98386 ] concat %I 190 655 262 616 Line %I 1 End Begin %I Text %I cfg Black 0 0 0 SetCFg %I f -*-helvetica-medium-r-normal-*-14-*-*-*-*-*-*-* Helvetica 14 SetF %I t [ 1 0 0 1 272 778.007 ] concat %I [ (Management Processes) ] Text End Begin %I Text %I cfg Black 0 0 0 SetCFg %I f -*-helvetica-medium-r-normal-*-14-*-*-*-*-*-*-* Helvetica 14 SetF %I t [ 1 0 0 1 24 468 ] concat %I [ (Unicast Routing) ] Text End Begin %I Text %I cfg Black 0 0 0 SetCFg %I f -*-helvetica-medium-r-normal-*-14-*-*-*-*-*-*-* Helvetica 14 SetF %I t [ 1 0 0 1 343 519.993 ] concat %I [ (Multicast Routing) ] Text End Begin %I Text %I cfg Black 0 0 0 SetCFg %I f -*-helvetica-medium-r-normal-*-14-*-*-*-*-*-*-* Helvetica 14 SetF %I t [ 1 0 0 1 50.9988 408.983 ] concat %I [ (RIB = routing information base) (FEA = forwarding engine abstraction) ] Text End Begin %I Line %I b 65535 0 0 0 [] 0 SetB %I cfg Black 0 0 0 SetCFg %I cbg VLtGray 0.839246 0.839246 0.839246 SetCBg %I p 1 SetP %I t [ 0.998207 -0 -0 0.998207 -69.8745 8.98386 ] concat %I 110 439 510 439 Line %I 1 End Begin %I Rect %I b 65535 0 0 0 [] 0 SetB %I cfg Black 0 0 0 SetCFg %I cbg White 1 1 1 SetCBg %I p 1 SetP %I t [ 0.998207 -0 -0 0.998207 -73.8673 8.98386 ] concat %I 390 415 406 423 Rect End Begin %I Rect %I b 65535 0 0 0 [] 0 SetB %I cfg Black 0 0 0 SetCFg %I cbg White 1 1 1 SetCBg %I p 1 SetP %I t [ 0.998207 -0 -0 0.998207 -61.8888 8.98386 ] concat %I 406 423 422 431 Rect End Begin %I Rect %I b 65535 0 0 0 [] 0 SetB %I cfg Black 0 0 0 SetCFg %I cbg White 1 1 1 SetCBg %I p 1 SetP %I t [ 0.998207 -0 -0 0.998207 -69.8745 12.9767 ] concat %I 414 407 430 415 Rect End Begin %I Rect %I b 65535 0 0 0 [] 0 SetB %I cfg Black 0 0 0 SetCFg %I cbg White 1 1 1 SetCBg %I p 1 SetP %I t [ 0.998207 -0 -0 0.998207 -69.8745 8.98386 ] concat %I 438 423 454 431 Rect End Begin %I Rect %I b 65535 0 0 0 [] 0 SetB %I cfg Black 0 0 0 SetCFg %I cbg White 1 1 1 SetCBg %I p 1 SetP %I t [ 0.998207 -0 -0 0.998207 -77.8601 12.9767 ] concat %I 446 407 462 415 Rect End Begin %I Rect %I b 65535 0 0 0 [] 0 SetB %I cfg Black 0 0 0 SetCFg %I cbg White 1 1 1 SetCBg %I p 1 SetP %I t [ 0.998207 -0 -0 0.998207 -77.8601 8.98386 ] concat %I 470 415 486 423 Rect End Begin %I Rect %I b 65535 0 0 0 [] 0 SetB %I cfg Black 0 0 0 SetCFg %I cbg White 1 1 1 SetCBg %I p 1 SetP %I t [ 0.998207 -0 -0 0.998207 -69.8745 8.98386 ] concat %I 358 423 374 431 Rect End Begin %I Rect %I b 65535 0 0 0 [] 0 SetB %I cfg Black 0 0 0 SetCFg %I cbg White 1 1 1 SetCBg %I p 1 SetP %I t [ 0.998207 -0 -0 0.998207 -69.8745 4.99103 ] concat %I 358 415 374 423 Rect End Begin %I Line %I b 65535 0 0 0 [] 0 SetB %I cfg Black 0 0 0 SetCFg %I cbg White 1 1 1 SetCBg %I p 1 SetP %I t [ 0.249552 -0 -0 0.249552 221.352 311.441 ] concat %I 425 480 425 448 Line %I 4 End Begin %I Line %I b 65535 0 0 0 [] 0 SetB %I cfg Black 0 0 0 SetCFg %I cbg White 1 1 1 SetCBg %I p 1 SetP %I t [ 0.249552 -0 -0 0.249552 221.352 311.441 ] concat %I 409 480 409 448 Line %I 4 End Begin %I Line %I b 65535 0 0 0 [] 0 SetB %I cfg Black 0 0 0 SetCFg %I cbg White 1 1 1 SetCBg %I p 1 SetP %I t [ 0.249552 -0 -0 0.249552 221.352 311.441 ] concat %I 393 480 393 448 Line %I 4 End Begin %I Line %I b 65535 0 0 1 [] 0 SetB %I cfg Black 0 0 0 SetCFg %I cbg White 1 1 1 SetCBg %I p 1 SetP %I t [ 0.249552 -0 -0 0.249552 221.352 311.441 ] concat %I 329 496 377 464 Line %I 4 End Begin %I Line %I b 65535 0 0 1 [] 0 SetB %I cfg Black 0 0 0 SetCFg %I cbg White 1 1 1 SetCBg %I p 1 SetP %I t [ 0.249552 -0 -0 0.249552 221.352 311.441 ] concat %I 329 448 377 464 Line %I 4 End Begin %I Line %I b 65535 0 0 1 [] 0 SetB %I cfg Black 0 0 0 SetCFg %I cbg White 1 1 1 SetCBg %I p 1 SetP %I t [ 0.249552 -0 -0 0.249552 221.352 311.441 ] concat %I 441 464 489 496 Line %I 4 End Begin %I Line %I b 65535 0 0 1 [] 0 SetB %I cfg Black 0 0 0 SetCFg %I cbg White 1 1 1 SetCBg %I p 1 SetP %I t [ 0.249552 -0 -0 0.249552 221.352 311.441 ] concat %I 441 464 489 448 Line %I 4 End Begin %I Line %I b 65535 0 0 1 [] 0 SetB %I cfg Black 0 0 0 SetCFg %I cbg White 1 1 1 SetCBg %I p 1 SetP %I t [ 0.249552 -0 -0 0.249552 221.352 311.441 ] concat %I 553 496 585 496 Line %I 4 End Begin %I Line %I b 65535 0 0 1 [] 0 SetB %I cfg Black 0 0 0 SetCFg %I cbg White 1 1 1 SetCBg %I p 1 SetP %I t [ 0.249552 -0 -0 0.249552 221.352 311.441 ] concat %I 553 448 585 448 Line %I 4 End Begin %I Line %I b 65535 0 0 1 [] 0 SetB %I cfg Black 0 0 0 SetCFg %I cbg White 1 1 1 SetCBg %I p 1 SetP %I t [ 0.249552 -0 -0 0.249552 221.352 311.441 ] concat %I 649 496 681 464 Line %I 4 End Begin %I Line %I b 65535 0 0 1 [] 0 SetB %I cfg Black 0 0 0 SetCFg %I cbg White 1 1 1 SetCBg %I p 1 SetP %I t [ 0.249552 -0 -0 0.249552 221.352 311.441 ] concat %I 649 448 681 464 Line %I 4 End Begin %I Text %I cfg Black 0 0 0 SetCFg %I f -*-helvetica-medium-r-normal-*-14-*-*-*-*-*-*-* Helvetica 14 SetF %I t [ 1 0 0 1 303 420 ] concat %I [ (Click Elements) ] Text End End %I eop showpage %%Trailer end xorp/devnotes/management_diag1.pic0000664000076400007640000000125011421137511017357 0ustar greearbgreearb.PS SNMP: box at 1,0 "SNMP" CLI: box at 1,2 "Cisco CLI" BGP: box at 2,1 "BGP" ob = boxwid; boxwid = boxwid + 0.2 ; BGPI: box invis at 2,1; boxwid = ob PIM: box at 5,1 "PIM" ob = boxwid; boxwid = boxwid + 0.2 ; PIMI: box invis at 5,1; boxwid = ob arrow from SNMP.e to BGP.w arrow from SNMP.e to PIM.w arrow from CLI.e to BGP.w arrow from CLI.e to PIM.w "Management" at BGPI.w rjust above "Interface" at BGPI.w rjust below "Management" at PIMI.w rjust above "Interface" at PIMI.w rjust below arrow from 0,0 to SNMP.w "External" at SNMP.w rjust above "Interface" at SNMP.w rjust below arrow from 0,2 to CLI.w "External" at CLI.w rjust above "Interface" at CLI.w rjust below .PExorp/devnotes/template_gpl.py0000664000076400007640000000167211421137511016540 0ustar greearbgreearb#!/usr/bin/env python # Copyright (c) 2008-2009 XORP, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # $XORP: xorp/devnotes/template_gpl.py,v 1.1 2008/10/02 21:56:43 bms Exp $ # Local Variables: # mode: python # py-indent-offset: 4 # End: xorp/devnotes/template_lgpl.sh0000664000076400007640000000171611421137511016675 0ustar greearbgreearb#!/bin/sh # Copyright (c) 2008-2009 XORP, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License, Version # 2.1, June 1999 as published by the Free Software Foundation. # Redistribution and/or modification of this program under the terms of # any other version of the GNU Lesser General Public License is not # permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU Lesser General Public License, Version 2.1, a copy of # which can be found in the XORP LICENSE.lgpl file. # # XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # $XORP: xorp/devnotes/template_lgpl.sh,v 1.1 2008/10/02 21:56:43 bms Exp $ # Local Variables: # mode: shell-script # sh-indentation: 4 # End: xorp/devnotes/update_changed_copyright.bash0000775000076400007640000000035311633743677021416 0ustar greearbgreearb#!/bin/bash # Called as: $0 [start-git-md5] [end-git-md5] [year] #set -x START=$1 END=$2 YEAR=$3 for i in `git diff --name-only $START $END` do if [ -f $i ] then ./xorp/devnotes/update_copyright.pl $YEAR $i fi done xorp/devnotes/update_cvsroot.sh0000775000076400007640000000423711421137511017111 0ustar greearbgreearb#!/bin/sh # # $XORP: xorp/devnotes/update_cvsroot.sh,v 1.4 2008/04/29 01:53:44 pavlin Exp $ # # # This is a script to update the CVS/Root files. # It must be run from the top directory of a checked-out copy # of the source code, otherwise it may overwrite something else. # # Note: Before running the script you might want to modify the # ANON_CVS_ROOT or DEVEL_CVS_ROOT strings. # # # XXX: MODIFY THIS AS APPROPRIATE! # ANON_CVS_ROOT=":pserver:xorpcvs@anoncvs.xorp.org:/cvs" DEVEL_CVS_ROOT="cvs.xorp.org:/cvsroot" # # Local variables # NEW_CVS_ROOT="" TMP_CVS_ROOT_FILENAME=/tmp/xorp_cvsroot.$$ # # Print usage and exit # usage() { cat < "${TMP_CVS_ROOT_FILENAME}" # # Do the file update. # If both old and new CVS Root are identical, then don't overwrite anything. # find . -name CVS -and -type d -print | while read DIRNAME do FOUND_CVS_ROOT_FILENAME="${DIRNAME}/Root" if [ ! -f "${FOUND_CVS_ROOT_FILENAME}" ] ; then continue fi cmp -s "${TMP_CVS_ROOT_FILENAME}" "${FOUND_CVS_ROOT_FILENAME}" if [ $? -eq 0 ] ; then # Old and new CVS Root are identical echo "Ignoring ${FOUND_CVS_ROOT_FILENAME}" continue fi # Copy the file echo "Updating ${FOUND_CVS_ROOT_FILENAME}" cp -p "${TMP_CVS_ROOT_FILENAME}" "${FOUND_CVS_ROOT_FILENAME}" done xorp/devnotes/template_lgpl.cc0000664000076400007640000000163511421137511016650 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2008-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net xorp/devnotes/policy_todo.tex0000664000076400007640000005043011421137511016553 0ustar greearbgreearb\documentclass[letterpaper]{article} \usepackage{supertabular} \usepackage[left=1in,top=1in,bottom=2in,right=1in]{geometry} \author{Andrea Bittau} \title{XORP Policy Tasks} \date{July 24, 2008} \begin{document} \maketitle \section{Introduction} This document lists tasks that need to be completed in order to bring XORP's policy framework to a more mature state. Both functional aspects, such as the completeness of the framework compared to Juniper's and Cisco's, and non functional aspects ({\em i.e.,} performance) are enumerated. They are listed in the following order / categories: \begin{enumerate} \item Route matching. What are the criteria for identifying particular routes in policies? \item Route actions. Once a route is matched, what actions and modifications can be done on it? \item Other policy aspects. Miscellaneous functionality such as how policies are attached to protocols and so on. \item Performance. What can be done to improve XORP's performance with regards to policy? \end{enumerate} An attempt has been made to capture Juniper's functionality exhaustively. Regarding Cisco, only a best effort has been made to map its functionality to that of Juniper, and no real attempt has been made to verify whether Cisco supports functionality that Juniper does not. It probably is the case though that all that can be done with Cisco can also be done with Juniper, and hence using Juniper as a reference for XORP will most likely yield a satisfactory policy implementation. The indication of working days needed per task is very approximate. Integral numbers are used throughout and many one day tasks indeed can take less time, especially when tasks are correlated or similar in nature. If all tasks need to be completed, the total day count is probably over-estimated by 50\%. However, individual tasks longer than a day have a more optimistic rather than conservative estimate. Once the necessary tasks for completion have been selected, a feasibility study with a more accurate day count will follow. Tasks without a day count are already completed, and for those with question marks it is uncertain what the requirement actually is and whether it is of interest for XORP in the near future. \section{Route matching} \begin{center} \tablehead{ \hline Ref. & Description & Juniper & Cisco & XORP & Days \\ } \tabletail{ \hline } \begin{supertabular}{|r |p{6cm} |p{2cm} |p{2cm} |p{2cm} |r|} \hline 100 & Aggregate contributor. Match routes that are contributing to a configured aggregate. This match condition can be used to suppress a contributor in an aggregate route. & aggregate-contributor & ?? & ?? & ?? \\ \hline 101 & OSPF area identifier. Match a route learned from the specified OSPF area. & area & match route-type? & & 1 \\ \hline 102 & BGP AS path regular expression. & as-path & match as-path & as-path &\\ \hline 103 & BGP AS path group regular expression. & as-path-group & match as-path & & 1 \\ \hline 104 & Color values. The color value can be a number in the range from 0 through 4,294,967,295. A lower number indicates a more preferred route. & color, color2 & match metric? & & 1 \\ \hline 105 & Community. Name of one or more communities. If you list more than one name, only one name needs to match for a match to occur (the matching is effectively a logical OR operation). & community & match community & community &\\ \hline 106 & OSPF external. Match external routes, including routes exported from one level to another. When you specify type, this condition matches only OSPF routes with the specified OSPF metric type. & external $[$type$]$ & match route-type & ebit? &\\ \hline 107 & IP family. Match the address family IP version 4 (IPv4) or IP version 6 (IPv6) of the route. & family & match ip-address? & network4/6 &\\ \hline 108 & Instance. Match a route learned from one of the specified instances. & instance & ?? & ?? & ?? \\ \hline 109 & Interface. Match a route learned from one of the specified interfaces (IP addresses or names). & interface & match interface & & 2 \\ \hline 110 & Internal. Match a routing policy against the internal flag for simplified next-hop self policies. & internal & match route-type & & 1 \\ \hline 111 & IS-IS level. Match a route learned from a specified level. & level & match route-type & no IS-IS in XORP & ?? \\ \hline 112 & BGP local preference. BGP local preference (LOCAL\_PREF) attribute. & local-preference & match local-preference & localpref &\\ \hline 113 & Metric values (1--4). (BGP only) metric corresponds to the multiple exit discriminator (MED), and metric2 corresponds to the interior gateway protocol (IGP) metric if the BGP next hop runs back through another route. & metric$[$1--4$]$ & match metric & med/metric &\\ \hline 114 & Multicast scope value of IPv4 or IPv6 multicast group address. The multicast-scoping name corresponds to an IPv4 prefix. You can match on a specific multicast-scoping prefix or on a range of prefixes. & multicast-scoping & match ip address? & ?? & ?? \\ \hline 115 & Neighbor addresses. For BGP, the address can be a directly connected or indirectly connected peer. For all other protocols, the address is the neighbor from which the advertisement is received. & neighbor & match ip route-source & neighbor (BGP-only) & 2 \\ \hline 116 & Next-hop address or addresses specified in the routing information for a particular route. For BGP routes, matches are performed against each protocol next hop. & next-hop & match next-hop & nexthop4/6 &\\ \hline 117 & LDP generates next hop based on RSVP and IP next hops available to use combined with the forwarding-class mapping. & next-hop-type & ?? & ?? & ?? \\ \hline 118 & BGP origin attribute. Can be EGP, IGP, or incomplete. & origin & match route-type? & origin &\\ \hline 119 & Policy to evaluate as a subroutine. & policy & match policy-list & policy & \\ \hline 120 & Preference value. You can specify a primary preference value (preference) and a secondary preference value (preference2). The preference value can be a number from 0 through 4,294,967,295 (232 – 1). A lower number indicates a more preferred route. & preference$[$1--2$]$ & match metric & & 1 \\ \hline 121 & Named list of IP addresses. You can specify an exact match with incoming routes. & prefix-list & match ip address & network / prefix-length &\\ \hline 122 & Named prefix list. You can specify prefix length qualifiers for the list of prefixes in the prefix list. & prefix-list-filter & match ip address & network-list &\\ \hline 123 & Name of the protocol from which the route was learned or to which the route is being advertised. It can be one of the following: access, access-internal, aggregate, bgp, direct, dvmrp, isis, local, ospf, pim-dense, pim-sparse, rip, ripng, or static. & protocol & match source-protocol & protocol &\\ \hline 124 & Name of a routing table. Can be unicast/multicast IPv4/6, MPLS, particular unicast IPv4 routing instance. & rib & ?? & & 1 \\ \hline 125 & List of destination prefixes. When specifying a destination prefix, you can specify an exact match with a specific route or a less precise match using match types. & route-filter & match ip address & network / prefix-length &\\ \hline 126 & Type of route (internal or external). & route-type & match route-type & & 1 \\ \hline 127 & List of multicast source addresses. When specifying a source address, you can specify an exact match with a specific route or a less precise match using match types. & source-address-filter & match ip address? & ?? & ?? \\ \hline 128 & Tag value. You can specify two tag strings: tag (for the first string) and tag2. These values are local to the router and can be set on configured routes or by using an import routing policy. You can specify multiple tags under one match condition by including the tags within a bracketed list. For OSPF and IS-IS, the tag and tag2 match conditions match the 32-bit tag field in external link-state advertisement (LSA) packets. & tag$[$2$]$ & match tag & & 2 \\ \hline 129 & To base policy routing on the Level 3 length of a packet, use the match length command in route-map configuration mode. & ?? & match length & & 3 \\ \hline 130 & Replace nexthop with discard interface. & nexthop discard & ?? & & 3 \\ \hline 131 & Replace nexthop with ``reject'' interface that returns ICMP unreachable messages. & nexthop reject & ?? & & 3 \\ \hline 132 & Replace nexthop to next routing table. & nexthop next-table & ?? & & ?? \\ \hline 133 & Match against a prefix list. Prefix lists seem to be the ``dumb'' version of route lists, so having route lists probably implies having prefix lists. XORP supports route lists. & prefix-list & ?? & & 3 \\ \hline 134 & Condition statement. Match routes based on a condition in the router ({\em e.g.,} does route exist?). & condition & ?? & & 5 \\ \end{supertabular} \end{center} \section{Route actions} \begin{center} \tablehead{ \hline Ref. & Description & Juniper & Cisco & XORP & Days \\ } \tabletail{ \hline } \begin{supertabular}{|r |p{6cm} |p{2cm} |p{2cm} |p{2cm} |r|} \hline 200 & Accept the route and propagate it. After a route is accepted, no other terms in the routing policy and no other routing policies are evaluated. & accept & permit & accept &\\ \hline 201 & Accept and override any action intrinsic to the protocol. This is a nonterminating policy action. & default-action-accept & ?? & & 2 \\ \hline 202 & Reject the route and do not propagate it. After a route is rejected, no other terms in the routing policy and no other routing policies are evaluated. & reject & deny & reject &\\ \hline 203 & Reject and override any action intrinsic to the protocol. This is a nonterminating policy action. & default-action-reject & ?? & & 2 \\ \hline 204 & Skip to and evaluate the next term in the same routing policy. Any accept or reject action specified in the then statement is skipped. Any actions in the then statement that manipulate route characteristics are applied to the route. & next term & ?? & next term & \\ \hline 205 & Skip to and evaluate the next routing policy. Any accept or reject action specified in the then statement is skipped. Any actions in the then statement that manipulate route characteristics are applied to the route. & next policy & ?? & next policy & \\ \hline 206 & Affix one or more AS numbers at the beginning of the AS path. & as-path-prepend & set as-path prepend & as-path-prepend &\\ \hline 207 & Extract the last AS number in the existing AS path and affix that AS number to the beginning of the AS path n times, where n is a number from 1 through 32. & as-path-expand & ?? & as-path-append &\\ \hline 208 & Apply the specified class-of-service parameters to routes installed into the routing table. & class & ?? & ?? & ?? \\ \hline 209 & Set the color. & color & set tag & & 1 \\ \hline 210 & Change the color. & color add / subtract & ?? & & 1 \\ \hline 211 & Add the specified communities to the set of communities in the route. & community add & set community additive & community-add &\\ \hline 212 & Delete the specified communities from the set of communities in the route. & community delete & set comm-list delete & community-del &\\ \hline 213 & Replace any communities that were in the route in with the specified communities. & community set & set community & community-set &\\ \hline 214 & Set CoS-based next-hop map in forwarding table. & cos-next-hop-map & ?? & ?? & ?? \\ \hline 215 & Apply the specified route-damping parameters to the route. & damping & set dampening & & 5 \\ \hline 216 & Maintain packet counts for a route passing through your network, based on the destination address in the packet. & destination-class & set traffic-index? & ?? & ?? \\ \hline 217 & Calculate a metric based on the current values of metric and metric2. & metric & set metric & & 2 \\ \hline 218 & Set the external metric type for routes exported by OSPF. & external type & set metric-type & external-type & \\ \hline 219 & Create the forwarding class that includes packets based on both the destination address and the source address in the packet. & forwarding-class & ?? & ?? & ?? \\ \hline 220 & Choose which next hops, among a set of equal LSP next hops, are installed in the forwarding table. & install-nexthop & ?? & ?? & ?? \\ \hline 221 & Install all next-hop addresses in the forwarding table and have the forwarding table perform per-packet load balancing. & load-balance per-packet & ?? & ?? & ?? \\ \hline 222 & Set the BGP local preference (LOCAL\_PREF) attribute. & local-preference & set local-preference & localpref &\\ \hline 223 & Change the local preference value by the specified amount. & local-preference add / subtract & ?? & localpref add / sub & \\ \hline 224 & Set the metric. & metric & set metric & metric &\\ \hline 225 & Change the metric value by the specified amount. & metric add / subtract & ?? & metric add / sub & \\ \hline 226 & Change the metric (MED) value by the specified negative or positive offset. & metric igp & set metric-type internal & med add / sub & \\ \hline 227 & Set the next hop (address, peer-address, self). & nexthop & set next-hop & nexthop & \\ \hline 228 & Set the BGP origin attribute. & origin & set origin & origin &\\ \hline 229 & Set the preference value. & preference & set weight? & & 3 \\ \hline 230 & Change the preference value by the specified amount. & preference add / subtract & ?? & & 3 \\ \hline 231 & Maintain packet counts for a route passing through your network, based on the source address. & source-class & set-traffic index? & ?? & ?? \\ \hline 232 & Set the tag value. & tag & set tag & tag & \\ \hline 233 & Change the tag value by the specified amount. & tag add / subtract & ?? & tag add / sub & \\ \hline 234 & To indicate where to forward packets that pass a match clause of a route map for policy routing. & ?? & set interface & & 4 \\ \hline 235 & To indicate where to import routes, use the set level command in route-map configuration mode. & ?? & set level & ?? & ?? \\ \end{supertabular} \end{center} \section{Miscellaneous} \begin{center} \tablehead{ \hline Ref. & Description & Days \\ } \tabletail{ \hline } \begin{supertabular}{|r |p{12cm} |r|} \hline 301 & Tracing policy execution in a file. & 3 \\ \hline 302 & Final action. In addition to specifying an action using the then statement in a named term, you can also specify an action using the then statement in an unnamed term. & \\ \hline 303 & Policy expressions. Policy expressions give the policy framework software a different way to evaluate routing policies. A policy expression uses Boolean logical operators with policies. The logical operators establish rules by which the policies are evaluated. & \\ \hline 304 & Route list actions. If you specify route lists in the from statement, for each route in the list, you can specify an action to take on that individual route directly, without including a then statement. & 5 \\ \hline 305 & Routing policies and forwarding table interactions. Allows you to set ``class of service'' and do load balancing. & ?? \\ \hline 306 & Testing routing policies. Before applying a routing policy in a live environment, you can use the test policy command to ensure that the policy produces the results that you expect. & \\ \hline 307 & Ensuring consistency and compatibility with Juniper. Ensure that commands match, {\em e.g.}, localpref $\to$ local-preference. Ensure that syntax matches, {\em e.g.,} BGP AS-path regular expressions. Ensure that outcome of commands matches that of Juniper. & 5 \\ \hline 308 & Per peer BGP policies. Ability to attach policies to specific BGP peers. & \\ \hline 309 & Per group BGP policies. Attach policies at a group granularity. XORP currently does not support groups. & ?? \\ \hline 310 & Per area OSPF policies. & 5 \\ \hline 311 & Per group RIP policies. XORP does not support RIP groups. & ?? \\ \hline 312 & Aggregate route policies. XORP does not support route aggregation. & ?? \\ \hline 313 & Policies on generated routes. XORP does not support route generation. & ?? \\ \hline 314 & Policies on a routing table / instance (RIB) granularity. & 5 \\ \hline 315 & Make the front-end policy configuration syntax more natural. Currently the syntax for configuring policies is at times awkward due to limitations in XORP's configuration parser. & 10 \\ \hline 316 & Design and implement route pushing. Route pushing ({\em i.e.,} refiltering routes upon a policy change) is currently implemented in an ad-hoc manner in each protocol. This code is very difficult to get right and it indeed causes many bugs. It needs to be designed and implemented by the generic policy framework, and protocols should make use of this single implementation. & 10 \\ \hline 317 & Design and refactor the policy front-end. The policy front-end was never designed properly but was instead just coded in an ad-hoc manner and very quickly. As requirements increased with time, the current functionality of the policy manager is rather complicated. It needs to deal with dependencies between policies, lists and policies, and special cases such as BGP per-peer policies. The code is currently unmantinable and will be a major source of bugs and development slowdown. & 10 \\ \hline 318 & Semantic check in backend protocol filters. Currently the policy manager does a generic semantic check for all policies. Some backend policy implementations though ({\em i.e.,} VarRW) have further restrictions which the policy manager does not know. Backend filters should therefore perform additional semantic checking with protcol VarRWs. & 5 \\ \hline 319 & Route lists can have an action attached ({\em i.e.,} accept / reject) to each route. & 3 \\ \end{supertabular} \end{center} \section{Performance} \begin{center} \tablehead{ \hline Ref. & Description & Days \\ } \tabletail{ \hline } \begin{supertabular}{|r |p{12cm} |r|} \hline 400 & Specialized types and operators. The policy back-end supports very generic types ({\em e.g.} strings) and operations on these can be slow. Creating specialized types and operators will speed up policy execution. & \\ \hline 401 & Bytecode / machine representation for running the policy. Currently policies are represented as a parse-tree. It may be more efficient to represent them as bytecode or indeed native code that can be quickly run. & 15 \\ \hline 402 & Avoid route copying. In some cases ({\em e.g.} BGP), the only way to modify a route is create a new one and modify it. Given that policies often modify routes this incurs both a time and memory overhead. It may be possible to reduce this. & 5 \\ \hline 403 & General performance optimizations. Various optimizations such as, where possible, using directly indexed arrays rather than (say) STL maps. Any other general programming tricks. & \\ \hline 404 & Profiling and optimizing specific cases. This will involve taking individual protocols and studying their overhead at a more microscopic scale in order to eliminate inefficiencies. The policy back-end will be scrutinized too. & 5 \\ \end{supertabular} \end{center} \section{Current policy-related bugs} \begin{center} \tablehead{ \hline Bugzilla ID & Task ref. & Description & Days \\ } \tabletail{ \hline } \begin{supertabular}{|r |r |p{10cm} |r|} \hline 245 & & Show bytes / routes matched per policy. & 4 \\ \hline 252 & & Creating different policy lists with same name results in commit error. & 1 \\ \hline 307 & & Policy prefix-length4 is a range rather than single instance. & 1 \\ \hline 312 & & Policy node "origin" should use ``igp/egp/incomplete'' values instead of numbers. & 1 \\ \hline 357 & & policy statement accepts non-integer ``term'' values. & 1 \\ \hline 373 & & Load configuration fails if policy is applied as import or export. & 1 \\ \hline 568 & 314 & The policy framework does not support filtering or selecting MRIB routes. & 5 \\ \hline 628 & 312 & Add a mechanism to export aggregated routes into routing protocols. & ?? \\ \hline 652 & & Implement something along the lines of Cisco RPL (Routing Policy Language). & 10 \\ \hline \end{supertabular} \end{center} \section{Summary} \begin{center} \begin{tabular}{|l |r|} \hline Component & Days \\ \hline Route match criteria (current completion: 37\%) & 27 \\ Route actions (current completion: 52\%) & 23 \\ \hline Miscellaneous & 61 \\ Performance & 25 \\ \hline Bug fixing & 24 \\ \hline Contingency (10\%) & 16 \\ \hline \hline {\bf Total} & {\bf 176} \\ \hline \end{tabular} \end{center} \end{document} xorp/devnotes/urib-interface.tex0000664000076400007640000001630611421137511017132 0ustar greearbgreearb\documentclass[11pt]{article} \usepackage{times} \usepackage{psfig} \usepackage{amsmath} \textwidth 6.5in \topmargin 0.0in \textheight 8.5in \headheight 0in \headsep 0in \oddsidemargin 0in \date{} \title{URIB XRL Interface} \author{Mark Handley} %\twocolumn \begin{document} \parsep 0ex \parskip 1.0ex \parindent 0em \noindent \maketitle \section{Introduction} This document defines the URIB's external XRL interface. Typically this interface will be called by routing protocols, and by the router manager to do initial configuration. The URIB itself depends on the FEA to be running to allow it to instantiate routes in the kernel. NOTE: THIS DOCUMENT IS NOT GUARANTEED TO BE UP-TO-DATE. \newline The implementation in xorp/urib/xrl\_interface.cc is the definitive specification. \section{XRL Interface} The URIB provides the following XRLs which can be called by other modules: \begin{itemize} \item {\tt urib/add\_igp\_table/protocol[txt]{\it protocol}} \newline {\bf Returns:} {\tt result[i32]{\it result}} \newline This creates a new IGP routing table called {\it protocol} in the URIB. The special routing table ``connected'' is reserved for directly connected interfaces, and should not be avoided. The URIB will return an XrlAtomList containing the result. A result of zero indicates success. Non-zero return codes are failures, but the precise values are not yet standardized. \item {\tt urib/add\_egp\_table/protocol[txt]{\it protocol}} \newline {\bf Returns:} {\tt result[i32]{\it result}} \newline This creates a new EGP routing table called {\it protocol} in the URIB. The difference between an EGP and an IGP is that routes in an IGP table MUST provide a nexthop that is on one of the directly connected interfaces. Routes in an EGP table may include nexthops that are not immediate neighbors; the URIB will use the routes in the IGP tables to resolve non-neighbor nexthops. The URIB will return an XrlAtomList containing the result. A result of zero indicates success. Non-zero return codes are failures, but the precise values are not yet standardized. \item {\tt urib/delete\_igp\_table/protocol[txt]{\it protocol}} \newline {\bf Returns:} {\tt result[i32]{\it result}} \newline This deletes the pre-existing IGP routing table called {\it protocol} from the URIB. Typically this should be called when a routing protocol instance exits. The URIB will return an XrlAtomList containing the result. A result of zero indicates success. Non-zero return codes are failures, but the precise values are not yet standardized. \item {\tt urib/delete\_egp\_table/protocol[txt]{\it protocol}} \newline {\bf Returns:} {\tt result[i32]{\it result}} \newline This deletes the pre-existing EGP routing table called {\it protocol} from the URIB. Typically this should be called when a routing protocol instance exits. The URIB will return an XrlAtomList containing the result. A result of zero indicates success. Non-zero return codes are failures, but the precise values are not yet standardized. \item {\tt urib/add\_route/protocol[txt]{\it protocol}\& netbase[ipv4]{\it net}\& \newline ...\hspace{0.25in}mask_len[i32]{\it mask_len}\& nexthop[ipv4]{\it nexthop}} \newline {\bf Returns:} {\tt result[i32]{\it result}} \newline {\tt add\_route} adds the specified route to the routing table called {\it protocol} in the URIB. The routing table must already have been created, and the route must not be a duplicate of an existing route (although it may overlap existing routes with a different prefix length). {\it netbase} is the base address of the route's subnet. {\it mask_len} is the prefix (mask) length in bits. {\it nexthop} is the router via which packets to this subnet should be forwarded. For example, protocol=''ospf'', netbase=10.1.2.0, mask_len=24 and nexthop=128.16.64.1 would add a route to subnet 10.1.2.0/24 via gateway 128.16.64.1 to the routing table associate with OSPF. The URIB will return an XrlAtomList containing the result. A result of zero indicates success. Non-zero return codes are failures, but the precise values are not yet standardized. \item {\tt urib/delete\_route/protocol[txt]{\it protocol}\& netbase[ipv4]{\it net}\& \newline ...\hspace{0.25in}mask_len[i32]{\it mask_len}} \newline {\bf Returns:} {\tt result[i32]{\it result}} \newline {\tt delete\_route} deletes the specified route from the routing table called {\it protocol} in the URIB. The routing table must already have been created, and the route must already exist. {\it netbase} is the base address of the route's subnet. {\it mask_len} is the prefix (mask) length in bits. {\it nexthop} is the router via which packets to this subnet should be forwarded. The route will only be deleted from the table specified by {\it protocol}, irrespective of whether or not it exists in other routing tables. The URIB will return an XrlAtomList containing the result. A result of zero indicates success. Non-zero return codes are failures, but the precise values are not yet standardized. \item {\tt urib/lookup\_route/protocol[txt]{\it protocol}\& addr[ipv4]{\it addr}} \newline {\bf Success Returns:} {\tt result[i32]0\& nexthop[ipv4]{\it nexthop}} \newline {\bf Failure Returns:} {\tt result[i32]-1} \newline {\tt lookup\_route} performs a route lookup on the URIB's internal routing tables to determine how the URIB believes an IPv4 packet destined for {\it addr} would be routed. If the route lookup was successful, result=0 is returned, along with the IPv4 address of the immediate neighbor that the packet would be forward to. If no route exists, only result=-1 is returned. \item {\tt urib/new\_vif/name[txt]{\it name}\& type[i32]{\it type}\& ipv4addr[ipv4]{\it addr}} \newline {\bf Returns:} {\tt result[i32]{\it result}} \newline {\tt new\_vif} informs the URIB about a new virtual interface. The ``connected'' route table will contain routes for immediately connected interfaces, but the URIB needs to know about the router's own IP address on these interfaces. {\tt new\_vif} is used to inform the URIB about an interface, and must be called before a route for that interface is added. The URIB will return an XrlAtomList containing the result. A result of zero indicates success. Non-zero return codes are failures, but the precise values are not yet standardized. \item {\tt urib/redist\_enable/from[txt]{\it from}\& to[txt]{\it to}} \newline {\bf Returns:} {\tt result[i32]{\it result}} \newline {\tt redist\_enable} enables restribution of routes from routing table {\it from} to routing table {\it to}. Probably doesn't currently work properly. The URIB will return an XrlAtomList containing the result. A result of zero indicates success. Non-zero return codes are failures, but the precise values are not yet standardized. \item {\tt urib/redist\_disable/from[txt]{\it from}\& to[txt]{\it to}} \newline {\bf Returns:} {\tt result[i32]{\it result}} \newline {\tt redist\_disable} disables restribution of routes from routing table {\it from} to routing table {\it to}. Route redistribution must have previously been started with{\tt redist\_enable}. The URIB will return an XrlAtomList containing the result. A result of zero indicates success. Non-zero return codes are failures, but the precise values are not yet standardized. \end{itemize} \end{document} xorp/devnotes/update_copyright.pl0000775000076400007640000000312611633743677017444 0ustar greearbgreearb#!/usr/bin/perl use strict; if (@ARGV < 2) { print "Usage: update_copyright.pl \n"; print "Example: update_copyright.pl 2011 foo.cc\n"; exit(1); } my $year = $ARGV[0]; my $fname = $ARGV[1]; my $tmpfname = "/tmp/xorp_copyright_update.tmp"; my $found_one = 0; my $next_xcr = 0; # Skip some files we should never directly update. if ($fname =~ /^docs\/wiki\//) { print "Skipping wiki file: $fname\n"; exit(0); } open(FH, "<", $fname) or die "Can't open file: $fname : $!"; open(OUTF, ">", "$tmpfname") or die "Can't open output file: $tmpfname : $!"; while() { my $ln = $_; if ($ln =~ /Copyright \(c\) (\d\d\d\d)-(\d\d\d\d) XORP, Inc and Others/) { if ($2 ne $year) { $found_one = 1; } $ln =~ s/Copyright \(c\) (\d\d\d\d)-(\d\d\d\d) XORP, Inc and Others/Copyright \(c\) $1-$year XORP, Inc and Others/g; } elsif ($ln =~ /Copyright \(c\) (\d\d\d\d)-(\d\d\d\d) Mass/) { $next_xcr = 1; } elsif ($ln =~ /Copyright \(c\) (\d\d\d\d)/) { if ($1 ne $year) { $found_one = 1; } $ln =~ s/Copyright \(c\) (\d\d\d\d)/Copyright \(c\) $1-$year XORP, Inc and Others/g; } else { if ($next_xcr == 1) { $found_one = 1; # Need to insert a copyright line for xorp if ($fname =~ /\.c$/) { print OUTF "/* Copyright (c) $year-$year XORP, Inc and Others */\n"; } else { print OUTF "// Copyright (c) $year-$year XORP, Inc and Others\n"; } $next_xcr = 0; } } print OUTF $ln; } if ($found_one) { print "Updated file: $fname\n"; `mv $tmpfname $fname`; } else { print "No changes to file: $fname\n"; } xorp/devnotes/deprecated/0000775000076400007640000000000011421137511015603 5ustar greearbgreearbxorp/devnotes/deprecated/README0000664000076400007640000000036311421137511016465 0ustar greearbgreearb# # $XORP$ # The documents in this directory are deprecated, but may be of interest. error_handling.tex ------------------ Early thoughts on handling errors within a XORP router. Superceded by ${XORP}/docs/design_arch/error_handling.tex xorp/devnotes/deprecated/error_handling.tex0000664000076400007640000003465611421137511021340 0ustar greearbgreearb\documentclass[11pt]{article} \usepackage{xspace} \usepackage{times} \usepackage{psfig} \usepackage{amsmath} \newcommand{\module} {{\em module}\@\xspace} \newcommand{\modules} {{\em modules}\@\xspace} \newcommand{\finder} {{\em Finder}\@\xspace} \newcommand{\cm} {{\em CM}\@\xspace} \textwidth 6.5in \topmargin 0.0in \textheight 8.5in \headheight 0in \headsep 0in \oddsidemargin 0in %\date{} \title{Error Handling in XORP} \author{First pass: Mark Handley} %\twocolumn \begin{document} \parsep 0ex \parskip 1.0ex \parindent 0em \noindent \maketitle \section{Introduction} A XORP router consists of a number of user-space modules communicating via XRLs, a Forwarding Engine Abstraction process (which is effectively a kernel proxy) and a kernel forwarding path. State is communicated between these modules during the normal operation of the router. In this document we attempt to map out what the correct actions should be when XORP modules unexpectedly restart, in terms of who is responsible for removing the now-obsolete state, and who is responsible for re-instantiating the state to get back to operational status. This document assumes roughly the process structure in Figure \ref{modules}. The RouterManager process is responsible for starting up all other processes, and for monitoring their health using periodic XRL status messages. \begin{figure}[htb] \centerline{\psfig{figure=processes3.ps,width=0.6\textwidth}} \caption{XORP Modules} \label{modules} \end{figure} \section{General Infrastructure} \subsection{RouterManager Restart} A router manager restart is considered a fatal event; all router processes will be terminated and restarted. \subsection{Finder Restart} The XRL mechanism should include a keepalive mechanism that allows each process to detect when the Finder has failed. Each process's XRL library will then repeated attempt to re-connect to the Finder. When the reconnect succeeds, the state that was registered with the old Finder instance will then be re-registered, allowing normal router operation to resume. This whole process is handled in the XRL library code, so no explicit action needs to be taken by any process, except for the RouterManager which must restart the Finder process. During the Finder restart process, some services will be temporarily unavailable if a client does not have a cached resolved copy of an XRL for a request it wishes to make. All processes should gracefully handle a NO\_FINDER error during regular operation and simply periodically retry the request. It should be noted that after a Finder restart, there may be a period of time when the new Finder has incomplete state, and may return a FINDER\_INITIALIZING error because the destination module has not yet had a chance to re-register. Processes should gracefully handle such an event, and generally retry the request. Outside of this period, FINDER\_INITIALIZING will not be returned; instead RESOLVE\_FAILED will be returned, and should be treated as an error in the normal way. Since the Finder has no way of knowing what the appropriate end of its initialization period is, it may be able to use the RouterManger to tell it all of the processes it should expect to hear from. The Finder could then infer the end of the initialization period when each process has registered its mappings. The entering of mappings into the Finder by client processes may need modifying so that initial mappings are done in one transaction to facilitate the end of initialization detection. \subsection{CLI Restart} A CLI restart will terminate all terminal sessions with that CLI instance. However, the CLI itself will not generally store configuration state, so a CLI restart should not cause any process other than the RouterManager to take any remedial action. \section{Unicast Routes} \subsection{FEA Restart} \subsubsection*{State teardown} The FEA is stateless with respect to unicast routes. FEA termination does not cause the forwarding kernel to remove forwarding state. The URIB will detect that the FEA has changed from XRL keepalive message timeout, but it takes no immediate action on this event. The RouterManager process will detect the FEA failure, either from a SIGCHILD, or from XRL keepalive message timeout. It should log the event, and attempt to restart the FEA. \subsubsection*{State re-instantiation} The URIB will repeatedly attempt to contact the new FEA (sending a XRL status request) until it is successful. While the FEA is not responding, routing changes may occur; these cannot be passed to the FEA, but this failure should be silent. Upon detecting successful FEA restart, the URIB will send a {\tt flush\_all\_routes}, and then dump its entire forwarding table into the FEA. If the URIB fails to detect FEA restart after a suitable period of time (eg 10 seconds), it should send a {\tt forwarding\_engine\_failure} message to the routing protocol processes that are registered with it. On receipt of such a message, a routing process should send appropriate messages to its peers withdrawing all routing information that may cause forwarding through this router. If the URIB subsequently succeeds in contacting the FEA, it should send a {\tt forwarding\_engine\_alive} message to the routing protocol processes, which re-enables normal operation. \subsection{URIB Restart} \subsubsection*{State Teardown} The FEA is not required to monitor the status of the URIB. Even if it does detect the URIB failure, it should not take any action. Each routing protocol will detect URIB failure from XRL keepalive message timeout. The default behavior on a routing process detecting URIB failure should be to immediately send appropriate messages to its peers withdrawing all routing information that may cause forwarding through this router. In general, the routing protocol should not drop its peerings unless this is the only way to prevent forwarding. The RouterManager process will detect the URIB failure, either from a SIGCHILD, or from XRL keepalive message timeout. It should log the event, and attempt to restart the URIB. \subsubsection*{State re-instantiation} After detecting URIB failure, routing processes should repeatedly attempt to re-contact the URIB (sending a XRL status request) until it is successful. Upon success, they should re-send their routing table to the URIB, and re-register any nexthop information that they need to hear from the URIB. After this is done, they should re-advertise the router to their peers as being available for forwarding. \subsection{Routing Protocol Restart} \subsubsection*{State Teardown} The URIB should detect the failure of a routing protocol from XRL keepalive message timeout. It should then delete all the routes that it has stored from that routing protocol, propagating the deletions to both the kernel and to other routing protocols that have notification entries set for these routes. The RouterManager process will detect the routing process failure, either from a SIGCHILD, or from XRL keepalive message timeout. It should log the event, and attempt to restart the routing process. \subsubsection*{State re-instantiation} Routing state re-instantiation after a routing protocol restart is handled the same as if that routing protocol started up for the first time - no special action is required. \section{Multicast Routes} \subsection{FEA Restart} \subsubsection*{State teardown} The kernel should detect that the routing socket has closed. On closure of the routing socket, all multicast forwarding state is flushed from the kernel. The MFIB process (which may or may not be a part of PIM-SM) will detect the FEA failure from XRL keepalive message timeout, or from timeout of an attempt to modify multicast routing state. The MFIB must assume that the FEA will be restarted, and so it will attempt to re-contact the FEA periodically. PIM-SM should not take any special action. The RouterManager process will detect the FEA failure, either from a SIGCHILD, or from XRL keepalive message timeout. It should log the event, and attempt to restart the FEA. \subsubsection*{State re-instantiation} No special action is required for state re-instantiation after the FEA has restarted. When a multicast data packet arrives, it will generate a CACHEMISS event, which will be signaled to the FEA. The FEA doesn't have state for this (S,G) or (*,G), so the CACHEMISS is then propagated to the MFIB process. \begin{itemize} \item If the MFIB has the appropriate forwarding information, this will be sent to the FEA which will then send it to the kernel so that the packet can be forwarded correctly. \item If the MFIB has state, but the packet arrived on the wrong interface, the MFIB should also send this forwarding information to the FEA, but it should also signal WRONGIIF to the appropriate multicast routing process. \item If the MFIB has no forwarding state that matches this (S,G), CACHEMISS is signaled to the appropriate multicast routing process. \end{itemize} In this way, as data packets arrive, they cause both the FEA and the kernel's multicast forwarding state to be re-instantiated. \section*{XRL Error Handling} Interprocess communication in XORP is achieved using XRLs. In this section we will consider what should be done when an XRL call fails due to a communication error. All XRL calls will ultimately get a response. In the normal case the response returns the status of the call (good or bad). In addition to error responses produced by the application, the XRL library can also return the following error responses: \begin{itemize} \item NO\_FINDER \item RESOLVE\_FAILED \item SEND\_FAILED \item REPLY\_TIMED\_OUT \end{itemize} From an application point or view, the first three errors are equivalent: the XRL was not communicated to the destination. We will discuss these below using the generic term {\em XRL send failure}. However, its not clear what can be inferred from a timeout response. The reasons for a timeout can be: the peer has died, peer is slow to respond, the network cable has been removed. As in all network communications when a timeout occurs we don't know if the last unacknowledged XRL request was received and processed by the peer. If the timeout has occurred because the peer has died we will receive notification of this explicitly and will deal with it as specified in section \ref{pfailure}. Thus an XRL transport error SHOULD NOT be taken as an indication that the peer is dead. If an application cares that the peer has died or restarted, it SHOULD register with the finder to receive notifications of process restarts. This a process SHOULD assume that an XRL transport problem will be transient until it receives an explicit confirmation that the destination has failed. XRLs can be sent over unreliable transports such as UDP or reliable transports such as TCP. The type of transport that should be used will be specified when defining the interface. In the case of reliable transport, the errors above should generally not occur, but in any event we need general rules about how to handle them should something fail in an unexpectedly way. In addition, the way the application uses an XRL interface can by pipelined or non-pipelined. In the pipelined case, multiple requests can be outstanding simultaneously; in the non-pipelined case at most one request can be outstanding at a time. It is useful for us to categorise XRL interfaces along these two axes: reliable/unreliable and pipelined/non-pilelined. \subsubsection*{Unreliable, Non-pipelined} If an XRL send failure occurs, the sending application MAY choose to retransmit the XRL, or ignore the failure as it sees fit. In an XRL timeout occurs, the sending application MAY also choose to retransmit the XRL, or ignore the failure as it sees fit. However, if the application chooses to re-send the XRL, the interface MUST be written in such a way that if this XRL had previously been received, this will not cause a further failure. \subsubsection*{Reliable, Non-pipelined} If an XRL send failure occurs, the sending application SHOULD retransmit the XRL. In an XRL timeout occurs the sending application SHOULD also retransmit the XRL. Further requests using this interface MUST be queued until the XRL has successfully been received. The interface should be written in such a way that if this XRL had previously been received, this will not cause a further failure. An alternative strategy is possible. If the XRL in question changes state at the receiving application, the interface may also support a query mechanism. If the XRL fails with a timeout, the sending application may opt not to blindly re-transmit the XRL, but instead send a query (retransmitted as necessary) to determine whether the state at the remote system is as it would be if the XRL had been received. Only if it the query indicates that the state was not received would the original XRL be retransmitted. This alternative is more complicated, and so it should only be prefered when the consequences of receiving the same XRL twice outweight the additional complexity. \subsubsection*{Unreliable, Pipelined} The same issues apply as with unreliable, non-pipelined, but the situation is more complicated. An interface that uses unreliable transport and pipelining is one that explicitly permits loss and {\em re-ordering} of requests. It is up to the application to choose whether to retransmit XRLs that return XRL send failed or timeout, but the application must only do so if it is certain that the re-ordering caused by retransmission will not be a problem. \subsubsection*{Reliable, Pipelined} Reliable, pipelined interfaces are the most difficult in which to handle XRL errors. Three issues need to be considered: \begin{itemize} \item The XRL that failed due to a transport error may be followed by pipelined XRLs that succeeded. \item The XRL that failed due to a transport error may be followed by pipelined XRLs that failed at the application level due to the state caused by the first failed XRL not being instantiated. \item If a failed XRL was followed by pipelined XRLs that succeeded, retransmitting that XRL will cause a re-ordering that might leave the destination in a different state than it would be if the XRLs had arrived in order. \end{itemize} To avoid all these problems we require that a reliable XRL transport fail to deliver all subsequent XRLs in the pipeline if a single XRL fails. Thus the reliable pipelined interface falls back to a reliable, non-pipelined interface after a failure. A subsequent XRL that succeeds then permits pipelined operation to resume. \bibliographystyle{plain} \bibliography{xorp} \end{document} xorp/devnotes/management.tex0000664000076400007640000001350411421137511016344 0ustar greearbgreearb\documentclass[11pt]{article} \usepackage{xspace} \usepackage{times} \usepackage{psfig} \usepackage{amsmath} \newcommand{\module} {{\em module}\@\xspace} \newcommand{\modules} {{\em modules}\@\xspace} \newcommand{\finder} {{\em Finder}\@\xspace} \newcommand{\cm} {{\em CM}\@\xspace} \textwidth 6.5in \topmargin 0.0in \textheight 8.5in \headheight 0in \headsep 0in \oddsidemargin 0in %\date{} \title{XORP Management and Configuration Interface} \author{Atanu Ghosh} %\twocolumn \begin{document} \parsep 0ex \parskip 1.0ex \parindent 0em \noindent \maketitle \section{Introduction} A XORP router must present a management interface to the world. This document tries to capture some of our initial thoughts on this subject. As discussed at XORP meeting Tuesday 12th June 2001. In order to ease the adoption of XORP routers a Cisco style command line interface needs to exist. A XORP router must also support SNMP. We may also decide to provide a XORP specific command interface. A XORP router \cite{architecture} is made up of separate \modules bound together using XRL's \cite{xrl}. How then does a management interface interact with the separate components? The obvious solution, based on the current architecture is, all \modules register their management interface with the \finder \cite{xrl}. The \finder is analogous to a portmapper in the rpc world, or the rmiregistry in the Java world. Each \module that exports a management interface could have knowledge of SNMP and command line interfaces. This doesn't seem to be a sensible approach as each \module would have to be changed if a new management interface was required. Instead each \module will export a single management interface. Every management type (SNMP, Cisco CLI, HTTP, etc \ldots) will be handled by a management \module. A management \module will be a single point in a XORP router that will be used to channel requests/responses to other XORP \modules. An example of such a configuration is given in Figure \ref{modules}. Each \module could have an XML configuration file associated with it as in Figure \ref{xml}. For a particular accessor method on a \module the mapping to the management interfaces can be provided. A \module therefore just exposes itself via XRL's. \begin{figure} \include{management_diag1} \centerline{\raise 1em\box\graph} \caption{Managment Interactions} \label{modules} \end{figure} \begin{figure} \begin{verbatim} XRL://fea/get_interface_name .iso.org.dod.internet.mgmt.mib-2.interfaces.ifTable.ifEntry.ifDescr show interface name XRL://callback/overflow?XRL://overflowed/ .overflows syslog "%s" \end{verbatim} \caption{XML management interface description exported by \modules} \label{xml} \end{figure} Figure \ref{xml} is not in any way meant to be definitive. It is an example of one possible way if binding the XRL interface exported by \modules and the various management interfaces we will be required to implement. A \module writer will clearly need to know what parts of the \module will need to be exposed for management. Specifically the BGP \module will need to expose the SNMP MIB variables. Rather than expose them in terms of SNMP it would seem to be more flexible to expose them as XRL's. Thus using the standard interprocess communication system. We do not yet have enough knowledge of SNMP to be sure that the mechanism described will be sufficient to build a SNMP \module. Given SNMP has a well defined set of accessor methods it should be relatively easy to define simple mappings. Get and Set should be simple. Traps may turn out to be more problematic. A much harder question to answer is will we be able to define a configuration definition that will capture all the vagaries of the Cisco command line. There are also issues regarding the partitioning of how much can be expressed in configuration files versus logic in the cli \module. Nothing has been said about the \modules registering with the \finder. The fundamental question in this regard is how does the SNMP \module bind with the BGP \module. We need to consider the simple case of how does the system bootstrap itself. We also need to consider new management and/or new other \modules being started. Additionally we need to consider failure of some or all the \modules. Restarting or adding \modules should be a simple process. Our current thinking is that there will be at least two critical/permanent \modules the \finder and a Configuration Manager (\cm). If either of these \modules fail then all bets are off. It may turn out to be the case that the \finder and the \cm should be a single process. The \cm will be responsible for starting the various routing \modules and perhaps even the \finder. For reasons of efficiency management \modules and other \modules will communicate directly using XRL's. The rendezvous process between management \modules and other \modules (BGP, URIB, etc \ldots) will be coordinated by the \cm. All \modules register with the \cm. The management \modules registration basically says ``I am a \module that wants to know about all current and future registrations by \modules exporting management interfaces''. The ``other'' \modules will just register that they are exporting management interfaces. Hopefully this captures all combinations of failures, new management \modules and new ``other'' \modules. \section{Random Thoughts} The GNU ``readline'' library may be a good place to start, when considering building a command line interface. \bibliographystyle{plain} \bibliography{xorp} \end{document} xorp/devnotes/coding-style.txt0000664000076400007640000001570611421137511016656 0ustar greearbgreearb# # $XORP: xorp/devnotes/coding-style.txt,v 1.6 2007/05/23 12:12:33 pavlin Exp $ # Provisional XORP C++ coding style This is a *provisional* attempt to lay down some rules that we can adhere to and moan about. The style is only intended to apply to new code, not code that we import. Hopefully, the style is equidistant from our default coding styles that all are equally peturbed. - DOCTRINE Ignore any section of this document when doing so would enhance readability. Feel free to edit someone else's code into agreement with these principles, or into agreement with your own variant of these principles. Expect that your code may be likewise edited. - FILE PROLOGUES Files should have a prologue containing the copyright, a description of what the file does, an RCS version id, and an RCS string/identifier in implementation files that ident(1) can use. We have a standard templates to work from: devnotes/template.hh devnotes/template.cc devnotes/template.h devnotes/template.c - INDENTATION Use 4 spaces per indent level. Indentation should generally follow "gnu". The default emacs c/c++ style is gnu, can be set with M-x c-set-style gnu). - BRACES Try to avoid gratuitous newlines between statements opening a brace and the opening brace. E.g., prefer: if (abc == 3) { /* XXX */ } to: if (abc == 3) { /* XXX */ } For the sake of readability, try to place white space either side of braces. E.g., prefer: bool foo() { return _some_value; } to: bool foo() {return some_value;} - REFERENCE AND POINTER ANNOTATIONS Prefer: foo(int& x) and bar(int* y) to: foo(int &x) and bar(int *y) The former appears to be more widely used in C++ literature and so we go with this for the sake of consistency. - TEMPORARY REFERENCES Temporary references can help make code more readable. When used with containers to pointer types the resulting code can be more readable and efficient. static void process(const string& s); // Forward declaration static void process_long_names1(list names) { list::const_iterator ci; for (ci = names.begin(); ci != names.end(); ++ci) { if ((*ci)->size() > 10) { // Direct use of iterator process(*(*ci)); } } } static void process_long_names2(list names) { list::const_iterator ci; for (ci = names.begin(); ci != names.end(); ++ci) { const string& s = *(*ci); // Use of temporary ref if (s.size() > 10) { process(s); } } } - COMMENTS Use // for comments. Avoid obvious comments inside routines. Any comments inside routines should be short -- one line, when possible. Prefer single blank lines for separating sections of code. - API DOCUMENTATION (kdoc) All major routines should have a comment briefly describing what they do. We use kdoc comments (similar to javadoc) to generate API overviews and programming documentation. - TERMINAL WIDTH Assume the terminal is 80 characters wide. Sure there'll be odd times when wraps cannot be avoided, but gratuitous wrapping will give you a "wide boy" reputation. - CAPITALIZATION class Wrench { public: Wrench() { ... }; ~Wrench() { ... }; apply_force(); ... private: double _weight; }; Note that the class's public interface comes first. - UNDERSCORING Member variables should begin with an underscore. Regular variables and function names should separate words with underscores. Double underscores should never be used, they are often used by the compiler during name munging. - NAMES FOR MODIFIERS AND ACCESSORS. Often, a private member variable will have associated public member functions that get and/or set its value. If the variable is named "_xxx", name the getter function "xxx()" and the setter function "set_xxx()". The getter function should generally be const. - STRING METHODS When necesary to have a string method, call it str(). In general, try to avoid adding operator string(). - BOOL METHODS In general, always avoid adding operator bool(), unless there is a strong reason for adding it. - METHOD DEFINITIONS When defining C++ in implementation files place the return value and class/method declarations on separate lines. E.g., const char* LuckyEightBall::speak() const { // blah blah } - ABBREVIATIONS Should be avoided, especially in method names. - INTEGER TYPES Use integer types defined in inttypes.h, these are POSIX compatible and portable. - USE OF CONST Decorate member functions with "const" whenever appropriate for purposes of documentation. Member functions that do not alter an object's value should be "const". Sometimes, a member variable is altered by a function that does not conceptually change the object's value. Mark such member variables as "mutable" to enhance the applicability of "const". - USE OF EXCEPTIONS AND STANDARD TEMPLATE LIBRARY Non-kernel modules should use exceptions and the standard template library where appropriate. - USE OF AUTOCONF We will use GNU autoconf to improve portability. The "xorp_config.h" header file will define symbols specifying system properties that may differ between reasonable Unices. This file is included by "libxorp/xorp.h" which itself includes and defines a number of other useful header files and features. The first file included by any XORP source file should be "libxorp/xorp.h". However, if one of the included header files is the module-specific "foo_module.h" (required by "libxorp/xlog.h"), that file should be included before "libxorp/xorp.h". - INCLUDE FILES Include files should be sorted into domain, each domain separated by a blank line: "foo_module.h" "libxorp/xorp.h" kernel includes sys includes net includes default include path includes XORP libraries includes local includes Don't sweat it too hard, however. - SPACES AND PARENTHESES Binary operators should have spaces around them to ease decipherment. Parentheses have to used when precedence dictates and can be used to reduce confusion, particularly with long expressions. Do not put spaces around parentheses. Keywords should likewise have spaces around them. For example, `if (x)', not `if(x)'; and `return x', not `return(x)'. - VARIABLE DECLARATIONS Place variable declarations at the beginning of the relevant scope. This includes placing declarations in the middle of functions, close to their first uses, when appropriate. Declare iteration variables inside the `for' statement when possible; this is most of the time. Declare a test variable inside the `if' test when possible; this happens comparatively rarely. Prefer multiple declarations when defining several variables that require initializers. - PORTABILITY All IPv6-specific code should be "#ifdef HAVE_IPV6" : #ifdef HAVE_IPV6 .... #endif // HAVE_IPV6 If there is only one line of code between #ifdef and #endif, then do not add the comment after "#endif". xorp/devnotes/test_programs_style.txt0000664000076400007640000001124711421137511020362 0ustar greearbgreearb# # $XORP: xorp/devnotes/test_programs_style.txt,v 1.1.1.1 2002/12/11 23:55:54 hodson Exp $ # XORP test programs style The style described below describes a set of rules that should be applied to test programs we write. Those rules should be considered as "work-in-progress". Hopefully, as we write more test programs, we will have some clues about what those programs should be about. - DOCTRINE Write test programs to test your code! If you don't write them, then your code does not work, or will not work when it is modified in the future. Period. - TEST PROGRAM GRANULARITY "Shall I write one big program to test everything, or shall I write many small programs, one for each particular test?" In general, prefer smaller test programs. However, if several tests are tightly related, they can be combined into one test program. Also, if there is a relatively large number of tests, and each test is just few lines, then it is fine to combine them in one test program. - TEST PROGRAM INTERNAL IDENTIFIERS * PROGRAM_NAME: the program filename (excluding the path to it). TODO: shall we include the path as well, so we don't have to worry whether each program has an unique name? * PROGRAM_VERSION_ID: should be in the `number-dot' format, with no blank spaces. Typically, two or three digits would be sufficient. Examples: 0.2 or 1.20.1 Optionally, one of the letters 'a' or 'b' may be added to the end to indicate 'alpha' or 'beta' status of the program. Examples: 0.2a or 1.20.1b - RETURN STATUS Return status is 0 if all tests succeed, 1 if any test fails, and 2 if a test fails due to an internal error. - TEST PROGRAM OUTPUT By default, the test-related output is printed to the standard output, and is generated only if the test program is run with the -v or --verbose command-line option (see below). This option does not apply to the internal error messages, which by default are printed to the standard error stream. - COMMAND-LINE OPTIONS Each test program should support the following command-line options: -h --help Print help information on the standard output, and then exit with return status of 0. -v --verbose Print information about successful tests as well as unsuccessful tests. -V --version Print the program version ID on the standard output, and then exit with return status of 0. At minimum, a test program should support at least -h and -v options (i.e., their LONG_OPTIONS format may be omitted). A test program may support some of the following command-line options: --info Print detailed information about the program on the standard output, and then exit with return status of 0. The information to print should be readable by human, and does not have specific format. Preferably, it should contain the following information about the program: name, description of its purpose, list of supported version (lastest first), last modification date, list of authors, copyright status, etc. Example: Name: test_ipvx Description: Test IPv4 and IPv6 address manipulation Versions: 0.5.1 0.3 0.2.2 Date: Oct 10, 2002 Copyright: See file LICENSE Return: 0 on success, 1 if test error, 2 if internal error --verbose-level= Print information about successful tests as well as unsuccessful tests. The level of verbosity is defined by . The particular assignment of the verbosity levels is program-specific, but higher level should correspond to more verbosity. However, if the level is 0, then the program should not print any test-related information (i.e., if there are no internal errors, it should return the appropriate status only). The verbose level does not apply to the messages related to internal errors. --with-version= Run the program with version , if supported. If the specified version is not supported, the program should print the list of supported versions to the standard error stream, and then exit with return status of 2. A test program may support additional test-specific command-line options. Each command-line option should be specified in the LONG_OPTIONS format (e.g., --help), but it may have a short version as well (e.g., -h). If a program is called with the wrong arguments, this should be treated as an internal error: the program should print the help information (the same one produced with -h) to the standard error stream, and then exit with return status of 2. - TEST SCRIPTS TODO: adapt Eddie's email about file format of test scripts, etc. Command-line arguments for test scripts: --preserve-temporaries Preserve the temporary directory created for the test. xorp/devnotes/template_gpl.hh0000664000076400007640000000206011421137511016477 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2008-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/devnotes/template_gpl.hh,v 1.1 2008/10/02 21:56:43 bms Exp $ #ifndef __PATH_TO_TEMPLATE_HH__ #define __PATH_TO_TEMPLATE_HH__ #endif // __PATH_TO_TEMPLATE_HH__ xorp/devnotes/routing-refs.txt0000664000076400007640000000171611421137511016675 0ustar greearbgreearbBelow is a list of routing references that may be useful to the XORP developers. * The Complete IS-IS Routing Protocol, Hannes Gredler and Walter Goralski, Springer, ISBN 1852338229. It not only describes the IS-IS itself, but describes also the Cisco IOS and Juniper JunOS IS-IS implementations. [See http://www.xorp.org/bugzilla/show_bug.cgi?id=158 for a longer review] * OSPF and IS-IS: Choosing an IGP for Large-Scale Networks, Jeff Doyle, Addison-Wesley, ISBN 0321168798. This books compares OSPF and IS-IS, and includes examples for both Cisco IOS and Juniper JunOS. * Routing TCP/IP, Volume 1 (2nd Edition), Jeff Doyle and Jennifer Carroll, Cisco Press, ISBN 1587052024. Routing TCP/IP, Volume II (CCIE Professional Development), Jeff Doyle and Jennifer DeHaven Carroll, Cisco Press, ISBN 1578700892. From the configuration side they are Cisco specific but they give a good view into most routing protocols. xorp/devnotes/template_lgpl.h0000664000076400007640000000214311421137511016505 0ustar greearbgreearb/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- */ /* vim:set sts=4 ts=8: */ /* * Copyright (c) 2008-2009 XORP, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License, Version * 2.1, June 1999 as published by the Free Software Foundation. * Redistribution and/or modification of this program under the terms of * any other version of the GNU Lesser General Public License is not * permitted. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, * see the GNU Lesser General Public License, Version 2.1, a copy of * which can be found in the XORP LICENSE.lgpl file. * * XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; * http://xorp.net */ /* * $XORP: xorp/devnotes/template_lgpl.h,v 1.1 2008/10/02 21:56:43 bms Exp $ */ #ifndef __PATH_TO_TEMPLATE_H__ #define __PATH_TO_TEMPLATE_H__ #endif /* __PATH_TO_TEMPLATE_H__ */ xorp/devnotes/c++refs.txt0000664000076400007640000000463611421137511015505 0ustar greearbgreearbContained herein you'll find some reading materials that have proved useful or insightful to the Xorp developers with regard to getting to grips with C++. * The C++ Programming Language (3rd Edition), Bjarne Stroustrup, Addison-Wesley. The definitive C++ book. Like the language, it's rather large, and as reading matter it's not terribly entertaining. It's a useful reference book, though tough to read cover to cover. * The ANSI/ISO C++ Professional Programmer's Handbook, Danny Kalev, Que (ISBN 0789720221). An excellent alternative to Stroustrup. Clearly and concisely written. Only 350 some pages. Punchy and to the point. Assumes rudimentary knowledge of C++ at the start and builds around the ANSI C++ standard. * Effective C++: 50 Specific Ways to Improve Your Programs and Designs, Scott Meyers, Addison-Wesley (ISBN 0201924889). More Effective C++: 35 New Ways to Improve Your Programs and Designs, Scott Meyers, Addison-Wesley (ISBN 020163371X). Exceptionally readable and valuable. If you've not programmed in C++ before, or have not programmed in C++ recently, then these are both well worth reading. * SGI STL documentation (http://www.sgi.com/tech/stl/). Hypertext documentation covering the C++ Standard Template Library. Essential at hand reference. Includes design documents and has many examples of the STL in action. * The C++ Standard Library: A Tutorial and Reference, Nicolai M. Josuttis, Addison-Wesley (ISBN 0201379260). More extensive STL documentation. Handy reference (i++ or ++i :-). * Design Patterns, Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides, Addison-Wesley (ISBN 0201633612). Presents a catalog of design patterns. A little dry to read, but a good intro and reference. * Modern C++ Design: Generic Programming and Design Patterns Applied, Andrei Alexandrescu, Addison-Wesley (ISBN 0201704315). This is a hard C++ book. It presents a bedazzling array of template specialization magic. There's not too much in the project that stems directly from this book, but it's mind blowing material. * Technical Report on C++ Performance, ISO/IEC PDTR 18015, WG 21 N1487=03-0070, 2003-08-11. Available from: http://www.research.att.com/~bs/performanceTR.pdf Report on C++ overheads, performance, myths thereof, and techniques for efficient C++. xorp/devnotes/template_gpl.cc0000664000076400007640000000160111421137511016465 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2008-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net xorp/devnotes/template_gpl.h0000664000076400007640000000210411421137511016326 0ustar greearbgreearb/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- */ /* vim:set sts=4 ts=8: */ /* * Copyright (c) 2008-2009 XORP, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 2, June * 1991 as published by the Free Software Foundation. Redistribution * and/or modification of this program under the terms of any other * version of the GNU General Public License is not permitted. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, * see the GNU General Public License, Version 2, a copy of which can be * found in the XORP LICENSE.gpl file. * * XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; * http://xorp.net */ /* * $XORP: xorp/devnotes/template_gpl.h,v 1.1 2008/10/02 21:56:43 bms Exp $ */ #ifndef __PATH_TO_TEMPLATE_H__ #define __PATH_TO_TEMPLATE_H__ #endif /* __PATH_TO_TEMPLATE_H__ */ xorp/devnotes/Makefile0000664000076400007640000000153511421137511015147 0ustar greearbgreearbTARGETS = \ management.ps \ error_handling.ps \ urib-interface.ps all: $(TARGETS) clean: rm -f *.aux *.bbl *.blg *.dvi *.log $(TARGETS) urib-interface.ps: urib-interface.dvi dvips -o urib-interface.ps urib-interface.dvi urib-interface.dvi: urib-interface.tex management.ps: management.dvi dvips -o management.ps management.dvi management.dvi: management.tex management_diag1.tex management_diag1.tex: management_diag1.pic pic -t management_diag1.pic > management_diag1.tex # ----------------------------------------------------------------------------- # Implicit rules # ----------------------------------------------------------------------------- %.ps: %.dvi dvips -o $@ $< %.eps: %.fig fig2dev -Leps $< $@ %.ps: %.fig fig2dev -Lps $< $@ %.dvi: %.tex latex $< -bibtex $* latex $< latex $< xorp/devnotes/work_plan.txt0000664000076400007640000002022711421137511016243 0ustar greearbgreearb# # $XORP: xorp/devnotes/work_plan.txt,v 1.25 2003/08/21 21:34:02 pavlin Exp $ # Priority: - H (High) - M (Medium) - L (Low) - Defer (Deferred) Who: - AG (Atanu Ghosh) - JC (Javier Cardona) - LR (Luigi Rizzo) - MH (Mark Handley) - OH (Orion Hodson) - PR (Pavlin Radoslavov) - FB (Fred Bauer) ================================ FEA ========================================= Immediate Tasks Priority Who --------------- -------- --- For 0.5: * Complete Linux support. M FB+PR * UDP send/receive interface. H OH+PR For 0.x: * Use the library for monitoring the network H Everyone interface state from the FEA * Equal-cost multipath L ?? * Add support for discard interface H FB (pkts routed on that interface are discarded). Longer Term tasks ----------------- * TCP connection relay. ================================ RIB ========================================= Immediate Tasks Priority Who --------------- -------- --- For 0.5: * Handle route process death H AG/MH/PR - see the error-handling document (e.g., exit if the FEA is gone) - Example: if BGP goes away, then the RIB should pull-out all routing state that comes from BGP Also, if the FEA goes away, then RIB should go away too. - PR look into it, and if too compicated, then move it to 0.x * Change the RIB to use the RefTrie L AG For 0.x: * Support recursive BGP Lookups L MH * Add XRL interface to dump routing table state M MH (for monitoring/informational purpose: xorpsh, etc). * Add support for discard interface H PR (pkts routed on that interface are discarded). * Equal-cost multipath M MH - add info when a set of routes should be together; propagate that info down to the FEA * Add route tag support to routing->RIB interface M MH to support policy filtering - ask Mark re. this (PR) Longer Term tasks ----------------- ================================ BGP ========================================= Immediate Tasks Priority Who --------------- -------- --- For 0.5: * IPv6/multi-protocol support H AG * Finish Kdoc'ing BGP L as needed For 0.x: * Test harness vs Cisco and Zebra to validate tests L AG For 1.0: * Filters M ?? - which user-specified filters? - how to do user configuration? - how to handle filter changes? * Originating routes M AG * Handling routes redistributed from IGPs. M AG + MH * Implement MinRouteAdvertisementInterval M ?? Longer Term tasks ----------------- * Communities * Route flap damping * Optionally not storing RIB-IN * Confederations * Route Reflector * Route Refresh * TCP MD5 (wait for FreeBSD-5.2(?)) * TCP connection mediation through FEA ================================ Multicast =================================== Immediate Tasks Priority Who Time --------------- -------- --- ---- For 0.5: * RIB<->PIM integration and testing (after PIM testing) H PR+MH 1-2 weeks * Integration of PIM/MLD/MFEA and RTRMGR H PR 2 weeks * Finish Kdoc'ing PIM, MLD6IGMP, MFEA L PR background For 0.x: * IGMPv3 and MLDv2 implementation L PR+other 1-2m * PIM-SSM support L PR 1 week * Framework for regression tests for multicast H-M PR+other 1 m * IPv6 support and tests for PIM/MLD/MFEA M PR 1-1.5m - Initial support: e.g. no scoping * Port the FreeBSD PIM kernel mods to NetBSD and H PR 1 week OpenBSD Longer Term tasks ----------------- * Bidir-PIM implementation Wish PR 1.5-3m * Implement multicast support for Click Wish PR 2-3 months ================================ StaticRoutes =============================== Immediate Tasks Priority Who Time --------------- -------- --- ---- For 0.5: * Implement Static Routes H PR 2 weeks ================================ Spoof IGP =================================== Immediate Tasks Priority Who Time --------------- -------- --- ---- For 0.5: * Come-up with a better name * Implement Spoof IGP H PR 2 weeks ============================== RTRMGR ======================================== Immediate Tasks Priority Who --------------- -------- --- For 0.5: * Isolation test suite PartlyDone MH+AG For 0.x: * Add target name and instance H MH+PR Longer Term tasks ----------------- * Proper authentication/ACL framework. ================================ XORPSH ===================================== Immediate Tasks Priority Who --------------- -------- --- For 0.5: * BGP user tutorial H Everyone * FEA user tutorial (vifs, etc) H Everyone * Make sure that XORPSH can be started via a shell script H PR For 0.x: For 1.0: * Task interruption. - make sure that the task is interrupted at the H MH+PR server process. XXX: if we hit Ctrl-C in UNIX, what is the expected result? Undetermined, hence do we need to do anything in case of xorpsh? Longer Term tasks ----------------- ================================ XRL ======================================= Immediate Tasks Priority Who --------------- -------- --- For 0.5: * Switch to reliable XRLs Done OH For 1.0: * Interface specification (reliable/non-reliable) M OH ================================ RIP ======================================= Immediate Tasks Priority Who --------------- -------- --- For 0.5: * Implementation or RIPv2 H OH For 0.x: * Implementation of RIPng M OH ================================ SNMP ======================================= Immediate Tasks Priority Who --------------- -------- --- For 0.x: * Improve documentation H ?? * Fix the crash when unloading modules H ?? ================================ TESTS ======================================= Immediate Tasks Priority Who --------------- -------- --- For 0.5: * Use BGP/RIB/FEA test-router on xorp8 H Everyone (think what exactly we want to happen) ================================ Other ======================================= For 1.0: Longer Term tasks ----------------- * Implement Web manager H ?? * Simulation-like environment for running XORP H Defer PR 3-6 months * Rewrite CLI L PR 2-3 weeks ================================ Next Release ================================ * Next release (xorp-0.4): August 28, 2003 Immediate Tasks Priority Who --------------- -------- --- * Add multiprotocol support to BGP M AG ================================ Next-Next Release =========================== * Next release (xorp-0.5): October 16, 2003 Immediate Tasks Priority Who --------------- -------- --- * Add automated process running for testing xorpsh on M FCFS xorp8 (expect scripts, etc) * Finder notifications handling H Everyone (handling death of processes you depend on) BGP: done RIB: todo (PR) multicast: todo (PR) * Need to complete support for error handling as H Everyone specified in error doc (bgp: done; multicast, rib: todo(PR)) * Work-out the conventions for command-line options M OH * Test programs infrastructure L OH+PR * Implement everywhere command-line option for the H PR+Everyone finder: port number, and host name (see xorp/docs/libxipc/finder_command_line.txt) * Add tasks/projects to the web page wishlist L Everyone (for other people) ================================ Next Stable Release ========================= * Next stable release (xorp-1.0): ?? ??, 200? - ?? xorp/devnotes/pkg_xorp.bash0000775000076400007640000000775511703344130016214 0ustar greearbgreearb#!/bin/bash # Build XORP binaries and copy them to the right folder. VER=1.8.5 FC5=192.168.100.23 # v-fc5-32 FC8=192.168.100.24 # v-fc8-32 F11=192.168.100.25 # v-f11-32 F13=192.168.100.20 F14=192.168.100.34 FC8_64=192.168.100.31 F11_64=192.168.100.32 F13_64=192.168.100.3 F14_64=192.168.100.35 CDDIR="/mnt/d2/pub/xorp.$VER" mkdir -p $CDDIR chmod a+rwx $CDDIR echo "Starting build at:" date DOXORP="cd ~/git/xorp.ct.github/xorp && scons -j4 && cd -" DOXORPMING="cd ~/git/xorp.ct.github/xorp && scons -j4 strip=yes shared=no build=mingw32 STRIP=i686-pc-mingw32-strip \ CC=i686-pc-mingw32-gcc CXX=i686-pc-mingw32-g++ \ RANLIB=i686-pc-mingw32-ranlib \ AR=i686-pc-mingw32-ar LD=i686-pc-mingw32-ld && cd -" PULL32="cd ~/git/btbits && git pull && cd - && bash bin/btpull.sh" BUILD32="$PULL32" PULL64="cd ~/btbits/x64_btbits && git pull && cd - && bash bin/btpull.sh" BUILD64="$PULL64" PKGXORP="cd ~greearb/git/xorp.ct.github/xorp && bash ./lf_pkg.bash" PKGXORPMING="cd ~greearb/git/xorp.ct.github/xorp && bash ./win32_pkg.bash" CLEANUP="exit 0" BUILDRPM="cd ~greearb/git/xorp.ct.github/xorp && bash ./build_rpms.bash && mv ~/rpmbuild/RPMS/*/xorp-$VER* /root/rpmbuild/SRPMS/xorp-$VER* $CDDIR" BUILDDEB="cd ~greearb/git/xorp.ct.github/xorp && make -f Makefile.deb && mv lanforge-xorp_$VER* $CDDIR" # Build XORP echo "Building XORP..." ssh $FC5 "$BUILD32 && $DOXORP && $CLEANUP" > build-5.txt 2>&1 || echo "FAILED FC5 build." & ssh $F11 "$BUILD32 && $DOXORP && $CLEANUP" > build-11.txt 2>&1 || echo "FAILED F11 build."& ssh $F13 "$BUILD32 && $DOXORP && $DOXORPMING && $CLEANUP" > build-13.txt 2>&1 || echo "FAILED F13-32 build." & ssh $F14 "$BUILD32 && $DOXORP && $CLEANUP" > build-14.txt 2>&1 || echo "FAILED F14-32 build." & ssh $FC8_64 "$BUILD64 && $DOXORP && $CLEANUP" > build-8-64.txt 2>&1 || echo "FAILED FC8-64 build." & ssh $F11_64 "$BUILD64 && $DOXORP && $CLEANUP" > build-11-64.txt 2>&1 || echo "FAILED F11-64 build." & ssh $F13_64 "$BUILD64 && $DOXORP && $CLEANUP" > build-13-64.txt 2>&1 || echo "FAILED F13-64 build." & ssh $F14_64 "$BUILD64 && $DOXORP && $CLEANUP" > build-14-64.txt 2>&1 || echo "FAILED F14-64 build." & wait # Do xorp packaging echo "Package xorp binaries and build RPMs." ssh root@$FC5 "$PKGXORP && mv ~greearb/tmp/xorp_32.tgz $CDDIR/xorp_32-$VER-FC5.tgz && $CLEANUP" > build-pkg-x5.txt 2>&1 || echo "FAILED FC5 xorp package." & ssh root@$FC8 "$PKGXORP && mv ~greearb/tmp/xorp_32.tgz $CDDIR/xorp_32-$VER-FC8.tgz && $CLEANUP" > build-pkg-x8.txt 2>&1 || echo "FAILED FC8 xorp package." & ssh root@$F11 "$PKGXORP && mv ~greearb/tmp/xorp_32.tgz $CDDIR/xorp_32-$VER-F11.tgz && $CLEANUP" > build-pkg-x11.txt 2>&1 || echo "FAILED F11 xorp package." & ssh root@$F13 "$PKGXORP && mv ~greearb/tmp/xorp_32.tgz $CDDIR/xorp_32-$VER-F13.tgz && $PKGXORPMING && mv ~greearb/tmp/xorp_win32.zip $CDDIR/xorp_win32-$VER.zip && $BUILDRPM && $CLEANUP" > build-pkg-x13.txt 2>&1 || echo "FAILED F13 xorp package." & ssh root@$F14 "$PKGXORP && mv ~greearb/tmp/xorp_32.tgz $CDDIR/xorp_32-$VER-F14.tgz && $BUILDRPM && $BUILDDEB && $CLEANUP" > build-pkg-x14.txt 2>&1 || echo "FAILED F14 xorp package." & ssh root@$FC8_64 "$PKGXORP && mv ~greearb/tmp/xorp_64.tgz $CDDIR/xorp_64-$VER-FC8.tgz && $CLEANUP" > build-pkg-x8-64.txt 2>&1 || echo "FAILED FC8-64 xorp package." & ssh root@$F11_64 "$PKGXORP && mv ~greearb/tmp/xorp_64.tgz $CDDIR/xorp_64-$VER-F11.tgz && $CLEANUP" > build-pkg-x11-64.txt 2>&1 || echo "FAILED F11-64 xorp package." & ssh root@$F13_64 "$PKGXORP && mv ~greearb/tmp/xorp_64.tgz $CDDIR/xorp_64-$VER-F13.tgz && $BUILDRPM && $CLEANUP" > build-pkg-x13-64.txt 2>&1 || echo "FAILED F13-64 xorp package." & ssh root@$F14_64 "$PKGXORP && mv ~greearb/tmp/xorp_64.tgz $CDDIR/xorp_64-$VER-F14.tgz && $BUILDRPM && $CLEANUP" > build-pkg-x14-64.txt 2>&1 || echo "FAILED F14-64 xorp package." & wait echo "Packaging source." rm -fr /tmp/xorp-src-git (cd ~/git/xorp.ct.github; git pull || echo "ERROR: Could not pull xorp.ct!") git clone ~/git/xorp.ct.github /tmp/xorp-src-git cd /tmp/xorp-src-git tar -czf $CDDIR/xorp-$VER-src.tar.gz xorp date xorp/devnotes/template_gpl.sh0000664000076400007640000000166211421137511016521 0ustar greearbgreearb#!/bin/sh # Copyright (c) 2008-2009 XORP, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # $XORP: xorp/devnotes/template_gpl.sh,v 1.1 2008/10/02 21:56:43 bms Exp $ # Local Variables: # mode: shell-script # sh-indentation: 4 # End: xorp/devnotes/template_gpl.c0000664000076400007640000000161611421137511016330 0ustar greearbgreearb/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- */ /* vim:set sts=4 ts=8: */ /* * Copyright (c) 2008-2009 XORP, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 2, June * 1991 as published by the Free Software Foundation. Redistribution * and/or modification of this program under the terms of any other * version of the GNU General Public License is not permitted. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, * see the GNU General Public License, Version 2, a copy of which can be * found in the XORP LICENSE.gpl file. * * XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; * http://xorp.net */ xorp/devnotes/config.txt0000664000076400007640000000115011421137511015506 0ustar greearbgreearbOptions for XORP Configuration What's the problem? - need to have startup configuration for all the daemons stored somewhere. + some of the startup config needs to be shared (eg interface list). - want to be able to change the config of daemons on the fly via CLI, SNMP or whatever. + some of these changes need to be shared (eg bringing up an interface) + would like to be able to look somewhere and see the current config + need to be able to save these changes so the router comes up in this state next time - some of the daemons are written (or modified) by us; some are not. xorp/devnotes/release_process.txt0000664000076400007640000000726611703346327017447 0ustar greearbgreearb======================================================================== Update version. This command seems to find at least most of them, assuming old version is 1.8.3: find . -name "*" -print|grep -v kdoc|grep -v ^./other/|grep -v ^./xorp/obj|grep -v ^./www/images|xargs grep "1\.8\.3" ======================================================================== Update copyright notices. Might want to do a bit of manual inspection to make sure the script works properly: # That first big ugly md5sum is the starting git tag for this dev period, # typically the last commit for the previous release. Use gitk or similar # to figure this out. ./xorp/devnotes/update_changed_copyright.bash 6f0890b35697718e4a48f06c1148792aca1091c8 HEAD 2012 # Inspect changes and commit them. ======================================================================== Edit the Web pages in www/html_src/ * news.html * index.html ======================================================================== Update xorp/RELEASE_NOTES Edit it manually, and also create git shortlog with command similar to: git shortlog xorp-1.8.3..HEAD ======================================================================== Tag the code for this release: git tag xorp-1.8.4-rc ======================================================================== Prepare sofware and documentation tarballs for distribution Build packages (Ben has a script that does this for his targets). # Check the ./publish_html.bash to make sure the CTVER is correct. # Build kdocs and package all web-page related stuff ./publish_html.bash # Upload it: scp xorp_www.tar.bz2 www.candelatech.com: ======================================================================== Add a Bugzilla "Version" entry for the new version. ======================================================================== Update the FreshMeat XORP entry: http://freshmeat.net/projects/xorp/ ======================================================================== Contact the FreeBSD maintainer of net/xorp to update the entry: http://www.freebsd.org/cgi/cvsweb.cgi/ports/net/xorp/ The maintainer's email address is available from the Makefile: http://www.freebsd.org/cgi/cvsweb.cgi/ports/net/xorp/Makefile ======================================================================== Contact the NetBSD maintainer of net/xorp to update the entry: http://cvsweb.netbsd.org/bsdweb.cgi/pkgsrc/net/xorp/ The maintainer's email address is available from the Makefile: http://cvsweb.netbsd.org/bsdweb.cgi/pkgsrc/net/xorp/Makefile If the maintainer's email address is generic, then the following form can be used to submit the request: http://www.netbsd.org/cgi-bin/sendpr.cgi?gndb=netbsd ======================================================================== Contact the OpenBSD maintainer of net/xorp to update the entry: http://www.openbsd.org/cgi-bin/cvsweb/ports/net/xorp/ The maintainer's email address is available from the Makefile: http://www.openbsd.org/cgi-bin/cvsweb/ports/net/xorp/Makefile If the maintainer does not reply, then send an email to ports@openbsd.org ======================================================================== Update the LinuxLinks XORP entry (if necessary): http://www.linuxlinks.com/ http://www.linuxlinks.com/portal/cgi-bin/modify.cgi ======================================================================== Update the Wikipedia XORP entry: http://en.wikipedia.org/wiki/XORP ======================================================================== Update the heise.de XORP entry: http://www.heise.de/software/default.shtml?prg=42502 xorp/devnotes/finder-mapping-syntax.txt0000664000076400007640000000277411421137511020502 0ustar greearbgreearb The Finder provides information about how to contact entities within a system of XORP processes. For purposes of flexibility and compatibility, it is desireable to be able to map one XRL into another, or into a series of XRL's. As a simple example of motivation, envisage a situation where a developer wants to trace when a particular xrl is invoked without any recompilation. By remapping the XRL in question to a logging XRL and followed by the original XRL, this can be achieved with no compilation. * Syntax for mapping The remapping might be communicated to the finder with: finder://fea/interface_enable?running:int=$val { # Call another XRL finder://logger/record_xrl_call?name:txt="fea/interface_enable" # Call original XRL with arguments provided finder://fea/interface_enable?running:int=$val } This example re-maps the forwarding engine abstraction (fea)'s method interface_enable to an alternate routine that logs the xrl invocation and then calls the original interface_enable method. The dollar syntax allows for storage and subsitution of values, here the xrlatom named "running" and type "int" has it's value propagated. We can do specialization overloading too, for instance if we only wanted to overrider interface_enable when the value of running was 1, we could write: finder://fea/interface_enable?running:int=1 { # Call another XRL finder://logger/record_xrl_call?name:txt="fea/interface_enable" # Call original XRL with arguments provided finder://fea/interface_enable?running:int=1 } xorp/devnotes/windows-release-process.txt0000664000076400007640000002360111421137511021032 0ustar greearbgreearb# $XORP$ XORP/Win32 Release Process -------------------------- 1. Check out a fresh XORP tree, with the BGP test data. # xcvs co xorp data This may take around 5 minutes dependent on latency and congestion. 2. Ensure configure step completes OK. # cd xorp && ./configure This takes around 3 minutes. 3. Perform the build. If OK proceed to next step. Most likely problems: You don't have MinGW installed correctly and/or patched for building XORP. # gmake This may take 45 minutes or more. 4. Perform regression tests. If OK proceed to next step. Most likely problems: bash not present in hardcoded path in RunShellCommand class (C:\MSYS\bin\sh.exe). # gmake check This may take 2 hours or more. 5. Perform basic application tests on local host. Bring up a basic router. Steps: 1. Open three cmd.exe windows. 2. set XORP_ROOT=c:\path\to\xorp\build\directory 3. cd into XORP_ROOT 4. Run rtrmgr\xorp_rtrmgr.exe in one window. 5. Run rtrmgr\xorpsh.exe in another. 6. Keep the other cmd.exe window handy for running Windows administration commands. * ENSURE THAT THE ROUTING AND REMOTE ACCESS SERVICE IS NOT RUNNING BEFORE STARTING ANY TEST SCENARIO. U:\XORP> net stop remoteaccess The Routing and Remote Access service is not started. More help is available by typing NET HELPMSG 3521. Scenario 1: Test basic XORP behaviour on Windows. * Create a C:\config.boot file with an interface configuration matching the interfaces present in the 'Network Connections' folder. * Add two static route entries; one candidate default, and one /32 prefix to a remote host with the next-hop as a host (not router) on your network. * Start the router manager. C:\Documents and Settings\Administrator>cd u:\xorp C:\Documents and Settings\Administrator>u: U:\xorp>set XORP_ROOT=U:\XORP U:\xorp>rtrmgr\xorp_rtrmgr -b c:\config.boot * Verify that fea, policy, rib, static_routes modules start up. If RRAS is disabled you should see these messages: [ 2006/07/15 15:03:14 WARNING U:\XORP\fea\xorp_fea.exe FEA ] RRAS is not running ; disabling FtiConfigEntrySetRtmV2. [ 2006/07/15 15:03:14 WARNING U:\XORP\fea\xorp_fea.exe FEA ] RRAS is not running ; disabling FtiConfigTableObserverRtmV2. * Verify that xorpsh can connect to the running router manager locally. U:\xorp>set XORP_ROOT=U:\XORP U:\xorp>rtrmgr\xorpsh [ 2006/07/15 15:10:35 WARNING rtrmgr\xorpsh RTRMGR ] [Operational Command File: U:\XORP/etc/templates/misc.cmds line 19]: Executable file not found: ping6 [ 2006/07/15 15:10:35 WARNING rtrmgr\xorpsh RTRMGR ] [Operational Command File: U:\XORP/etc/templates/misc.cmds line 29]: Executable file not found: traceroute [ 2006/07/15 15:10:35 WARNING rtrmgr\xorpsh RTRMGR ] [Operational Command File: U:\XORP/etc/templates/misc.cmds line 39]: Executable file not found: traceroute6 Welcome to XORP on empiric @empiric> * Verify that XORP's idea of the network interfaces configured on the system matches that in the Windows networking stack. @empiric> show interfaces bge0/bge0: Flags: mtu 1500 inet 192.168.123.199 subnet 192.168.123.0/24 broadcast 192.168.123.255 physical index 2 ether 0:10:c6:dd:6:f3 And in the administration shell: U:\XORP> ipconfig Windows IP Configuration Ethernet adapter bge0: Connection-specific DNS Suffix . : lon.incunabulum.net IP Address. . . . . . . . . . . . : 192.168.123.199 Subnet Mask . . . . . . . . . . . : 255.255.255.0 IP Address. . . . . . . . . . . . : 2001:618:400:1041:210:c6ff:fedd:6f3 IP Address. . . . . . . . . . . . : fe80::210:c6ff:fedd:6f3%6 Default Gateway . . . . . . . . . : 192.168.123.1 fe80::206:2bff:fe03:5c31%6 Ethernet adapter {53741D39-FF7A-4083-A5B4-9D1634535E56}: Media State . . . . . . . . . . . : Media disconnected XXX Scenario 2: Introduce static routes to local FIB via XORP. * Verify that the static routes appear in the Windows FIB. U:\XORP> route print IPv4 Route Table =========================================================================== Interface List 0x1 ........................... MS TCP Loopback interface 0x2 ...00 10 c6 dd 06 f3 ...... Broadcom NetXtreme Gigabit Ethernet - Packet Sch eduler Miniport 0x4 ...00 ff 53 74 1d 39 ...... TAP-Win32 Adapter V8 - Packet Scheduler Miniport =========================================================================== =========================================================================== Active Routes: Network Destination Netmask Gateway Interface Metric 0.0.0.0 0.0.0.0 192.168.123.1 192.168.123.199 20 1.2.3.4 255.255.255.255 192.168.123.6 192.168.123.199 20 ... * When finished, CTRL-C in the CMD.EXE window containing xorp_rtrmgr.exe. This should terminate XORP and all sub-processes cleanly. If not performed from within a CMD.EXE window then inheritance of signal handlers won't happen correctly. * Use 'Process Explorer' from Sysinternals.com to kill the entire xorp_rtrmgr.exe process tree, to ensure XORP is fully terminated before running another test scenario. 6. Perform typical peer scenario tests. Scenario 1: Local RIP. Unusual behaviour to look for: Traffic not being sent or received. Routes not being plumbed into IP helper. * Before starting, ensure that the second interface is up and configured in Network Connections. * Start rtrmgr with RIP configured on two interfaces in config.boot. * Use xorpsh to check both interfaces are present and correctly addressed. xorpsh> show interfaces * Check routes are in FINAL table. xorpsh> show route table ipv4 unicast final * Check RIP is running. @empiric> show rip status all * RIP on bge0 bge0 192.168.123.199 Status: enabled * RIP on iwi0 iwi0 192.168.124.190 Status: enabled * Run ethereal and dump on both interfaces; check that you see RIP and IGMP membership traffic for 224.0.0.9. Restart XORP if necessary to capture the IGMP joins. * On a peer connected to the same network, tcpdump for RIP traffic: montagne:~ % s tcpdump -i em0 -n -vvvv -e udp port router 11:20:39.375098 00:10:c6:dd:06:f3 > 01:00:5e:00:00:09, ethertype IPv4 (0x0800), length 66: (tos 0x0, ttl 1, id 50313, offset 0, flags [none], proto: UDP (17), length: 52) 192.168.123.199.520 > 224.0.0.9.520: [udp sum ok] RIPv2, Request, length: 24 0x0000: 0102 0000 0000 0000 0000 0000 0000 0000 0x0010: 0000 0000 0000 0010 * Now configure RIP to redistribute static routes, and perform the above packet inspections again. * Check that static host route 1.2.3.4/32 is sent OK on secondary interface. Scenario 2: Local OSPF. Unusual behaviour to look for: Traffic not being sent or received when using raw socket IPv4 multicast. * Redistribute static routes into ospf. * Start XORP. * Check ospf is running on both primary and secondary interface. * Check IPv4 IGMP join to OSPF group on secondary interface. Locally; use Ethereal, look for IGMP on 224.0.0.6. * Check IGMP join and OSPF traffic seen by peer on primary subnetwork. On peer; use tcpdump or Ethereal, look for IGMP on 224.0.0.6 and proto ospf. * Start OSPF peer on primary subnetwork. * Leave protocol analyzers running and check for successful exchange of initial LSAs. * Verify peers see each other: xorp> show ospf4 neighbor * Verify LSAs are as you'd expect. @empiric> show ospf4 database OSPF link state database, Area 0.0.0.0 Type ID Adv Rtr Seq Age Opt Cksum Len Router *192.168.123.199 192.168.123.199 0x80000001 108 0x2 0x3c6b 48 ASExt-2 *0.0.0.0 192.168.123.199 0x80000001 739 0x2 0x55e9 36 ASExt-2 *1.2.3.4 192.168.123.199 0x80000001 739 0x2 0x2d03 36 Network *192.168.123.199 192.168.123.199 0x80000001 108 0x2 0x1950 32 Router 192.168.123.6 192.168.123.6 0x80000002 104 0x2 0x786c 36 ---- Scenario 3: Local BGP. Unusual behaviour to look for: Hold timer timing out, timer problems. * Configure both peers as before to enable BGP and redistribute static routes into BGP. * Bring up the peering on both sides by starting XORP. * Watch for the hold timer. @empiric> show bgp peers detail Peer 1: local 192.168.123.199/179 remote 192.168.123.6/179 Peer ID: 192.168.123.6 Peer State: ESTABLISHED Admin State: START Negotiated BGP Version: 4 Peer AS Number: 65024 Updates Received: 1, Updates Sent: 2 Messages Received: 3, Messages Sent: 4 Time since last received update: 12 seconds Number of transitions to ESTABLISHED: 1 Time since last entering ESTABLISHED state: 16 seconds Retry Interval: 120 seconds Hold Time: 90 seconds, Keep Alive Time: 30 seconds Configured Hold Time: 90 seconds, Configured Keep Alive Time: 30 seconds Minimum AS Origination Interval: 0 seconds Minimum Route Advertisement Interval: 0 seconds * Keep your eye on the keepalive timer. * Verify that the Windows peer learned a route from the test partner. @empiric> show route table ipv4 unicast ebgp detail Network 10.0.0.0/8 Nexthop := 192.168.123.1 Interface := bge0 Vif := bge0 Metric := 0 Protocol := ebgp Admin distance := 20 Once satisfied that everything is in order proceed to the next stage. 7. Cut the release. Use the Nullsoft Installer script in xorp/contrib/win32/installer. See README in this directory for more detailed instructions. You may need to edit the SRCDIR variable in the xorp.nsi file to point to where you've built XORP, as well as the DLLDIR variable if you've installed MSYS and MinGW separately or elsewhere. * makensis xorp.nsi This will take around 3 minutes depending on how busy your disk is -- everything gets compressed into one big SETUP.EXE. Note that the installer does *not* strip symbols from the binaries. * Test the installer by running it. * Test the uninstaller from Windows' 'Add/Remove Programs' Control Panel applet. xorp/devnotes/dot-emacs0000664000076400007640000000206611421137511015306 0ustar greearbgreearb;; ;; $XORP$ ;; ;; These settings can be added to your .emacs file to give standard ;; xorp developer emacs settings. Please update if there are features ;; others might find useful. ;; Customizations for all of c-mode, c++-mode, and objc-mode (defun my-c-mode-common-hook () ;; Delete all whitespaces; optional (c-toggle-hungry-state 1) ;; Use pretty colors (turn-on-font-lock) ) (defun my-c++-mode-hook () (c-set-style "gnu") ; optional (setq c-basic-offset 4) ) (add-hook 'c-mode-common-hook 'my-c-mode-common-hook) (add-hook 'c++-mode-hook 'my-c++-mode-hook) ;; ;; kdoc ;; (global-set-key "\C-x5h" 'kdoc-insert) (autoload 'kdoc-insert "kdoc" nil t) (global-set-key "\C-x6h" 'kdoc-h-insert) (autoload 'kdoc-h-insert "kdoc" nil t) (global-set-key "\C-x7h" 'kdoc-class-insert) (autoload 'kdoc-class-insert "kdoc" nil t) ;;;; Copy kdoc.el to ~/path/to/, and add that directory to your load path: ; (setq load-path (cons "~/path/to/" load-path)) ;;;; Another way to do it. ; (if (file-exists-p "~/path/to/kdoc.el") ; (load-library "~/path/to/kdoc.el")) xorp/devnotes/template_lgpl.c0000664000076400007640000000165411421137511016506 0ustar greearbgreearb/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- */ /* vim:set sts=4 ts=8: */ /* * Copyright (c) 2008-2009 XORP, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License, Version * 2.1, June 1999 as published by the Free Software Foundation. * Redistribution and/or modification of this program under the terms of * any other version of the GNU Lesser General Public License is not * permitted. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, * see the GNU Lesser General Public License, Version 2.1, a copy of * which can be found in the XORP LICENSE.lgpl file. * * XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; * http://xorp.net */ xorp/devnotes/mfea2fea_merge_plan.txt0000664000076400007640000001037311421137511020107 0ustar greearbgreearb# # $XORP: xorp/devnotes/mfea2fea_merge_plan.txt,v 1.1 2003/03/26 23:42:10 pavlin Exp $ # This file contains a strawman plan for merging the MFEA functionalities with the FEA. The plan is to perform the merging incrementally, without breaking existing code. Everyone should be free to edit this file and/or discuss the issues. Timescale: Start: March 26, 2003 Finish: ?? The FEA and the MFEA implement (with few exceptions) the following functionalities: * FEA: (a) Network interface manager (b) Forwarding table manager (c) Raw packets, TCP, UDP send/recv * MFEA: (a) Network interface observer (b) Forwarding table observer (c) Raw packets send/recv (d) Join/leave multicast groups (e) Multicast forwarding table handling (f) Multicast data notifications (and dataflow bandwidth monitoring) Some of the functionalities provided by the MFEA are a subset of the functionalities provided by the FEA (e.g., MFEA(a), MFEA(b) and MFEA(c) are subset of FEA(a), FEA(b), and FEA(c) respectively). Other functionalities are multicast-specific and are required only for multicast (e.g., MFEA(d), MFEA(e), MFEA(f)), though MFEA(d) for example might be needed by unicast routing protocols such as OSPF and RIP. The current plan for incremental merging is: 1. Make the FEA and MFEA run as same process on the same event loop. 2. Merge the FEA(a) and MFEA(a) network interface specific code such that the FEA supports Linux for example. 3. Modify the MFEA(a) network interface observer such that it receives the information about network interfaces from the FEA rather than the kernel. Internally, the MFEA may keep the current vif organization, and may continue to use the existing XRL interface to provide that information to PIM and IGMP for example. 4. Pull-out the MFEA(b) forwarding table observer and make it a separate box (process?). It will behave similar to a routing process, except that it will be started explicitly by the router manager in the only case when a subset of XORP is run as a multicast router only (e.g., if the unicast routes are not installed by the XORP router). 5. Move the MFEA(d) join/leave multicast groups to the FEA. Note that this task may have to happen earlier if RIP for example needs it before the merging is completed. 6. Move the MFEA(e) multicast forwarding table handling, and the MFEA(f) multicast data notification to the FEA. Note that those two are coupled (e.g., if a multicast forwarding entry is removed from the kernel, then the associated multicast dataflow state should be deleted as well), hence they should be moved together to the FEA. 7. Merge the FEA(c) and MFEA(c) raw packets send/recv code. In the process, the following things need to be added (subject to further discussion): * When the FEA creates internally the set of interfaces (and virtual interfaces) it knows about, it should assign an unique vif_index to each virtual interface. This vif_index has internal meaning only for each XORP process that obtains the set of interfaces from the FEA; i.e., the vif_index is not used in the XORP configuration. For example, when the FEA instructs PIM to add an interface with ifname="foo", vif_name="bar", vif_index=5, then if PIM sends an XRL to FEA with one of the fields in that XRL having vif_index=5, for the FEA it should mean same thing as (ifname="foo", vif_name="bar"). A side note: if the kernel supports unique vif_index (per vif), the FEA may choose to use those vif_index as assigned by the kernel; otherwise the FEA should assign them by itself. Open question: can vif_index be used by third party? E.g., if IGMP sends an XRL to PIM, can it use vif_index instead of (ifname="foo", vif_name="bar")? Clarification: most of XRLs will continue to have ifname and vif_name only when referring to an interface. The vif_index will be used _only_ in the few XRLs where it is really needed (e.g., the add_mfc() multicast-related XRLs). * When the FEA sends notifications about interface-related changes (flags, addresses, etc), each notification should carry a flag "last_notification" (or something like that) that is set to true only for the last notification. Thus, applying the notification events can be atomic at the receiving side. xorp/devnotes/kdoc-pointers0000664000076400007640000001076511421137511016220 0ustar greearbgreearbDocument Structure ------------------ o Introduction o Quick Start o Example o Tool Usage o References Introduction ------------ We have settled on KDOC as the documentation system for source code. KDOC is written for and used by the KDE project. It's syntax is derived from Javadoc. Quick Start ----------- kdoc comments are C comments that start with /** and end with */. When a kdoc comment appears in an interface it is assumed to refer to the code or declaration immediately following it. Thus a comment appearing before a class describes the class, a comment appearing before a method describes the method, and so forth. Within a kdoc comment, tags can be used to provide hints to the documentation system. Each tag, with the exception of @ref, should appear on a line of it's own. o Class Tags @short Provide a short description of a class. is limited to 1 line. @author Provide author details. is limited to 1 line. @version Provide version detail. is limited to 1 line. @see [one or more references to classes or methods ] Provide references to related documentation. When referring to a class just use the class name, when referring to a method use ClassName#MethodName, eg string#empty for string::empty(). o Method Tags @param Provide a description of a method parameter. The description may span multiple lines. @return Provide a description of the return value. @exception [ [ ...]] Provide a list of exceptions that may be thrown by method. @see [ [ ...]] See description in Class Tags. o Constants, Enums @see [ [ ...]] See description in Class Tags. o Bonus Magic @ref [ [ ...]] Like @see, but can appear anywhere in the documentation.
...code fragment... 
Provide a piece of pre-formatted text, such as a code fragment, that kdoc should not examine. o KDOC style In general, we try to follow the Javadoc style (see the URL in section "Web Links"). Some of the exceptions from those guidelines are: - If the @param description is longer than a line, you may add an empty "* " line before the next @param or @return if it helps to improve readability. - Always add a period (i.e., ".") after a @param or @return statement to indicate end-of-statement, even if the description is not a complete sentence. - The short version of a descriptor of a method or a class should always start with a capital letter. Note that this doesn't apply for the first letter in a @param or @return statement. - If the short version of a descriptor of a method or a class is a title-like, such as "Default constructor", don't add a period after it. If the short descriptor is like a statement then add a period after it: "Copy the raw address to an in6_addr structure." Example ------- /** * @short A class to format text messages. * * This class pulls text from an input stream and pushes it out on an * output stream. Each line pushed out may have optional decoration * characters prepended and appended. * * Example usage might be along the lines of: * *
 * Formatter f(cin, cout);
 * f.set_line_introduction("oh dear: ");
 * ...
 * 
*/ class Formatter { public: /** * Constructor * * @param istr input stream. * @param ostr output stream. */ Formatter(istream& istr, ostream& ostr); ~Formatter(); /** * Set the introductory text for each output by the formatter. * * @param msg the text to be prepended to each line. */ void set_line_introduction(const char* msg); /** * @return the text being prepended to each line. * @see Formatter#set_line_introduction */ const char* line_introduction() const; }; Tool Usage ---------- kdoc kdoc supports a range of command line arguments for customizing output directories, presentation, etc. The kdoc(1) manual page describes these in detail. Web Links --------- The links below should be helpful if you are just starting. If you find, or know of any other, links that would be appropriate please add them here. kdoc web page: http://www.ph.unimelb.edu.au/~ssk/kde/kdoc/ Javadoc HOWTO: http://java.sun.com/j2se/javadoc/writingdoccomments/index.html Javadoc comment specification: http://java.sun.com/docs/books/jls/first_edition/html/18.doc.html xorp/devnotes/cli-style.txt0000664000076400007640000001351611421137511016157 0ustar greearbgreearb# # $XORP: xorp/devnotes/cli-style.txt,v 1.4 2006/07/11 23:52:25 pavlin Exp $ # Provisional XORP CLI coding style This is a *provisional* attempt to lay down some rules that we can adhere to when it comes to adding new CLI configuration statements or commands (or modifying existing ones). Note that each configuration node can have a "short" and a "long" help string. In the text below we use the terms "short help string" and "long help string" to differenciate between them. * Node names should be all lower-case characters. They may include digits, but their usage should be limited, and should never be used as the first symbol. * Each configuration node should have a short help string. * A configuration node name SHOULD be less than 20 symbols long. The associated short help string SHOULD be less than 56 symbols long. The length of a configuration node name + its short help string MUST NOT exceed 76 symbols. This is to ensure that the output of possible completions would fit into 80 symbols wide terminal. In most of cases it's wrapping that makes the output look really bad. Both node names and short help strings should be concise. Node names are especially important. If it can be shortened below 20 symbols without loosing the point, it should be done. If it can't be done, OK, but these cases should be kept minimum. For example: restore-original-config-on-shutdown -> restore-system-conf enable-ip-router-alert-option-check -> router-alert-check * If a node name is composed of more than one word, use '-' to separate the words. For example: multicastcapable -> multicast-capable * Don't specify the units in the node names (e.g., sec or msec). This info should be in the short help string. For example: interpacket-delay-msecs Minimum delay between outbound RIPng packets. vs. interpacket-delay Minimum delay between outbound RIPng packets (msec) * Avoid "Set ...", "Edit ..." etc in the short help strings. A short help string should just describe the purpose of the particular variable or command. Using "Set" or "Edit" in the short help string can be logically wrong as well - variables and commands aren't always set, but sometimes deleted as well. For example: horizon Set the horizon type applied to routes announced on address. vs. horizon Horizon type applied to announced routes * Short help strings begin with capital letter and don't have a dot in the end. Just to make the look consistent. * Keep to minimum the number of parameters the user has to configure. For example, a timer with a jitter can be described as "some-timer-min" and "some-timer-max". If the user wants to change the timer value, he/she has to modify both parameters. Usually the user wants to modify only the average value of the timer. In this example, "some-timer" and "some-jitter" should be used instead, where "some-jitter" should have some reasonable default value. In most cases it is better to use percents rather than absolute value as jitter. Such decision should be done on case by case by taking into account the number of parameters that need to be modified under normal circumstances. * Commands should describe what they do, not what they can be used for. For example, the purpose of the following command is not very clear: accept-non-rip-requests Answer RIP requests made from non-RIP processes It is better use following command and help string to describe it: source-port-check Answer RIP requests made only from RIP port Additional details about the command can be provided in the user documentation. * The short help strings for each module (e.g., a protocol) should begin with " configuration". Example: set protocols ? rip RIP configuration * The short help strings should become more precise as additional parameters are specified, and should not provide more information than necessary. Example: set protocols rip interface eth0 vif eth0 address 1.2.3.4 authentication md5? md5 MD5 authentication key Bad example for a help string: md5 RIP MD5 authentication key on this interface/vif/address * Protocol acronyms in help strings should use common conventions, typically all upper case. Example: BGP, RIP, RIPng, VoIP * All timer-related configuration statements should follow common convention: - All configuration statements that represent periodic events should be named "foo-interval". - All configuration statements that represent expiration timeout events should be named "foo-timeout". - All configuration statements that represent lag/delay in events should be named "foo-delay". - All configuration statements that represent absolute time should be named "foo-time". In special cases a timer-related configuration statement might not follow the above convention, but there should be good justification for that (e.g., consistency with other router vendors, etc). * In general, the naming convention of other router vendors should be used as a guideline when choosing the name of a configuration statement, unless there are good reasons to choose a different name. * The name of an existing command should be changed only after careful considerations. The same applies for deleting a command or moving it to a different part of the configuration tree. * If a command is changed (renamed, deleted or moved), the original command should be marked as "deprecated" (i.e., by using the "%deprecated" keyword in the rtrmgr template) and kept in the configuration tree for at least three major releases or 18 months (whichever comes later). For example, if a command is deprecated in Release-1.5, the deprecated state about that command can be removed in Release-1.8 (assuming there have been at least 18 months between those two releases). xorp/devnotes/urib_metric_request.tex0000664000076400007640000000460311421137511020304 0ustar greearbgreearb\documentclass[11pt]{article} \usepackage{times} \usepackage{psfig} \usepackage{amsmath} %\usepackage{doublespace} \textwidth 6.5in \topmargin 0.0in \textheight 8.5in \headheight 0in \headsep 0in \oddsidemargin 0in %\date{} \title{Concept of XORP URIB Metric Interface} \author{Adam Greenhalgh} %\twocolumn \begin{document} \parsep 0ex \parskip 1.0ex \parindent 0em \noindent \maketitle \section{Introduction} This document describes the concept of a URIB metric interface. This interface to the URIB allows routing protocols to register an interest in an IGP metric associated with a particular route and to be informed if it changes. This interfaces is designed to allow protocols such as BGP which makes decisions based upon metrics to be able to reevaluate decisions if the data they made the decision on has changed. \section{Overview} The exact details of the interface need to be fleshed out but the interface will define two procedures in the URIB:- \newline \newline {\em uint32\_t reg\_interest\_in\_IGP\_metric(uint32\_t route)} \newline {\em void unreg\_interest\_in\_IGP\_metric(uint32\_t route) } \newline \newline The return value from register procedure is the current value of the metric which is associated with a route. An infinite metric is defined as 0xFFFFFFFF. \section{BGP and URIB interaction} BGP passes any routes it has learnt post any internal BGP decision process to the URIB to add to its RIB. The URIB can be configured to pass routing information learnt from other routing protocols or the routers configuration to BGP along with the associated metric. Changes in the routing table are communicated as deltas to BGP from the URIB, BGP is responsible for maintaining any required state. BGP may also , using the above interface, subscribe to receive updates of when a routing metric changes. In addition to receiving routes from its Peers and the URIB BGP is configured at initalisation time with a set of Routes that it is originating. \section{To do} The interface to which the URIB talks when informing the routing protocol of a change to the parameter it expressed an interest in needs to be defined. This definition will probably be expressed to some extent in terms of XRL's. Question : Does BGP receiving a routing update from the URIB with a metric attached mean that it automatically subscribes to here updates of that metric ? \section{Notes} \end{document} xorp/devnotes/code-sources.txt0000664000076400007640000000012411421137511016634 0ustar greearbgreearbSources of Open-Source code we can reuse: - COPS: http://www.intel.com/ial/cops/ xorp/devnotes/template_lgpl.hh0000664000076400007640000000211611421137511016655 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2008-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/devnotes/template_lgpl.hh,v 1.1 2008/10/02 21:56:43 bms Exp $ #ifndef __PATH_TO_TEMPLATE_HH__ #define __PATH_TO_TEMPLATE_HH__ #endif // __PATH_TO_TEMPLATE_HH__ xorp/postinst0000755000076400007640000000007111665020734013471 0ustar greearbgreearb#!/bin/bash echo "Xorp is installed in /usr/local/xorp." xorp/rip/0000775000076400007640000000000011631510076012452 5ustar greearbgreearbxorp/rip/port_manager.hh0000664000076400007640000000574711540224234015463 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/rip/port_manager.hh,v 1.15 2008/10/02 21:58:17 bms Exp $ #ifndef __RIP_PORT_MANAGER_HH__ #define __RIP_PORT_MANAGER_HH__ #include "libfeaclient/ifmgr_atoms.hh" template class Port; template class System; /** * @short Base for RIP Port container and factory classes. * * Classes derived from the PortManagerBase are expected to create * and manage RIP Port instances. The created Port instances should have * associated IO systems attached. */ template class PortManagerBase { public: typedef list*> PortList; typedef System
SystemType; public: PortManagerBase(SystemType& system, const IfMgrIfTree& iftree) : _system(system), _iftree(iftree) {} /** * Destructor * * It is important that all the routes stored in the associated * @ref System Route database and it's update queue are flushed * before destructor is invoked. */ virtual ~PortManagerBase(); /** * Get parent @ref System instance. */ SystemType& system() { return _system; } /** * Get parent @ref System instance. */ const SystemType& system() const { return _system; } /** * Get list of managed RIP Ports. */ const PortList& const_ports() const { return _ports; } /** * Get EventLoop. */ EventLoop& eventloop() { return _system.eventloop(); } /** * Get EventLoop. */ const EventLoop& eventloop() const { return _system.eventloop(); } /** * Get IfMgrIfTree. */ const IfMgrIfTree& iftree() const { return _iftree; } protected: /** * Get list of managed RIP Ports. */ PortList& ports() { return _ports; } /** * Get list of managed RIP Ports. */ const PortList& ports() const { return _ports; } protected: SystemType& _system; PortList _ports; const IfMgrIfTree& _iftree; }; // ---------------------------------------------------------------------------- // Inline PortManagerBase methods // template PortManagerBase::~PortManagerBase() { } #endif // __RIP_PORT_MANAGER_HH__ xorp/rip/route_db.hh0000664000076400007640000002367611540225534014615 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/rip/route_db.hh,v 1.31 2008/10/29 21:59:39 andrewma Exp $ #ifndef __RIP_ROUTE_DB_HH__ #define __RIP_ROUTE_DB_HH__ #include "libxorp/xorp.h" #include "libxorp/ref_ptr.hh" #include "policy/backend/policy_filters.hh" #include "route_entry.hh" #include "trace.hh" class EventLoop; template class UpdateQueue; template class PacketRouteEntry; template class RouteWalker; template class Peer; /** * @short A network comparitor class for the purposes of ordering * networks in sorted containers. */ template struct NetCmp { typedef IPNet Net; bool operator() (const Net& l, const Net& r) const; }; /** * @short Class that manages routes. * * The RouteDB class holds routes and manages their updates. * Successful updates are placed in the update queue contained within * the RouteDB instance. The UpdateQueue is used for generating * triggered update messages. * * The @ref RouteWalker class provides a way to walk the routes held. */ template class RouteDB : public NONCOPYABLE { public: typedef A Addr; typedef IPNet Net; typedef RouteEntry Route; typedef RouteEntryOrigin RouteOrigin; typedef RouteEntryRef DBRouteEntry; typedef RouteEntryRef ConstDBRouteEntry; typedef PacketRouteEntry PacketizedRoute; typedef map > RouteContainer; typedef map > RouteContainerNoRef; public: RouteDB(EventLoop& e, PolicyFilters& pfs); ~RouteDB(); /** * Insert a peer to the database. * * @param peer the peer to insert. * @return true if this is a new peer, otherwise false. */ bool insert_peer(Peer* peer); /** * Erase a peer from the database. * * @param peer the peer to erase. * @return true if this is an existing peer that was erased, otherwise * false. */ bool erase_peer(Peer* peer); /** * Update Route Entry in database for specified route. * * If the route does not exist or the values provided differ from the * existing route, then an update is placed in the update queue. * * @param net the network route being updated. * @param nexthop the corresponding nexthop address. * @param ifname the corresponding interface name toward the destination. * @param vifname the corresponding vif name toward the destination. * @param cost the corresponding metric value as received from the * route originator. * @param tag the corresponding route tag. * @param origin the route originator proposing update. * @param policytags the policytags of this route. * @param is_policy_push if true, this route update is triggered * by policy reconfiguration. * @return true if an update occurs, false otherwise. */ bool update_route(const Net& net, const Addr& nexthop, const string& ifname, const string& vifname, uint32_t cost, uint32_t tag, RouteOrigin* origin, const PolicyTags& policytags, bool is_policy_push); /** * A copy of RIB routes need to be kept, as they are not advertised * periodically. If a RIB route gets replaced with a better route from * another peer, it will be lost. By storing RIB routes, it is possible to * re-advertise RIB routes which have lost, but are now optimal. * * @param net network of the route being added. * @param nexthop the corresponding nexthop address. * @param ifname the corresponding interface name toward the destination. * @param vifname the corresponding vif name toward the destination. * @param cost the corresponding metric value. * @param the corresponding route tag. * @param origin the route originator [RIB in this case]. * @param policytags the policytags of this route. */ void add_rib_route(const Net& net, const Addr& nexthop, const string& ifname, const string& vifname, uint32_t cost, uint32_t tag, RouteOrigin* origin, const PolicyTags& policytags); /** * Permanently delete a RIB route. This occurs if redistribution of this * route ceased. * * @param net network of the route being deleted. */ void delete_rib_route(const Net& net); /** * Flatten route entry collection into a Vector. * * @param routes vector where routes are to be appended. */ void dump_routes(vector& routes); /** * Flush routes. */ void flush_routes(); /** * @return count of routes in database. */ uint32_t route_count() const; /** * @return pointer to route entry if it exists, 0 otherwise. */ const Route* find_route(const Net& n) const; /** * Accessor. * @return reference to UpdateQueue. */ UpdateQueue& update_queue(); /** * Accessor. * @return const reference to UpdateQueue. */ const UpdateQueue& update_queue() const; EventLoop& eventloop() { return _eventloop; } /** * Push routes through policy filters for re-filtering. */ void push_routes(); /** * Do policy filtering. * * @param r route to filter. * @return true if route was accepted, false otherwise. */ bool do_filtering(Route* r); Trace& trace() { return _trace; } protected: void expire_route(Route* r); void set_expiry_timer(Route* r); void delete_route(Route* r); void set_deletion_timer(Route* r); protected: RouteContainer& routes(); protected: EventLoop& _eventloop; RouteContainer _routes; UpdateQueue* _uq; PolicyFilters& _policy_filters; set* > _peers; // // RIB routes are not "readvertised", so consider if a rib route loses, // and then the winning route expires... we will have no route for that // destination... while we should. // // Also need to be able to re-filter original routes RouteContainerNoRef _rib_routes; RouteOrigin* _rib_origin; friend class RouteWalker; private: Trace _trace; // Trace variable }; /** * @short Asynchronous RouteDB walker. * * The RouteWalker class walks the routes in a RouteDB. It assumes * the walking is broken up into a number of shorter walks, and that * each short walk is triggered from a XorpTimer. The end of a short * walk causes state to saved and is signalled using the pause() * method. When the next short walk is ready to start, resume() * should be called. These calls save and resume state are relatively * expensive. */ template class RouteWalker : public NONCOPYABLE { public: typedef A Addr; typedef IPNet Net; typedef typename RouteDB::RouteContainer RouteContainer; typedef typename RouteDB::Route Route; enum State { STATE_RUNNING, STATE_PAUSED }; public: RouteWalker(RouteDB& route_db); ~RouteWalker(); /** * @return current state of instance. */ State state() const { return _state; } /** * Move iterator to next available route. * * @return true on success, false if route not available or instance is * not in the STATE_RUNNING state. */ const Route* next_route(); /** * Get current route. * * @return pointer to route if available, 0 if route not available or * not in STATE_RUNNING state. */ const Route* current_route(); /** * Pause route walking operation. The instance state is * transitioned from STATE_RUNNING to STATE_PAUSED on the assumption that * route walking will be resumed at some point in the future (@ref * resume). If the current route has a deletion timer associated * with it that would expire within pause_ms, the timer expiry is * pushed back so it will expire at a time after the expected * resume time. Thus in most cases a walk can safely resume from * where it was paused. * * @param pause_ms the expected time before resume is called. */ void pause(uint32_t pause_ms); /** * Resume route walking. The instance state is transitioned from * STATE_PAUSED to STATE_RUNNING. The internal iterator is checked for * validity and recovery steps taken should the route pointed to have * been deleted. */ void resume(); /** * Effect a reset. The internal iterator is moved to the first * stored route and the state is set to STATE_RUNNING. */ void reset(); private: static const Net NO_NET; private: RouteDB& _route_db; // RouteDB to be walked. State _state; // Current state (STATE_RUNNING/STATE_PAUSED). Net _last_visited; // Last route output before entering // STATE_PAUSED. // If set to RouteWalker::no_net there was // no valid route when paused. typename RouteContainer::iterator _pos; // Current route when the // state is STATE_RUNNING. }; #endif // __RIP_ROUTE_DB_HH__ xorp/rip/TODO0000664000076400007640000000236411421137511013143 0ustar greearbgreearb# # $XORP: xorp/rip/TODO,v 1.14 2004/10/15 17:40:48 bms Exp $ # // ---------------------------------------------------------------------------- // Pre 1.0 Release * Validate origin address in Port::port_io_receive If packet does not come from a host on the same network or from the end of a point to point link it should not be processed. * Add HopCount check on received RIPng packets. * Check and double check nexthop handling paying attention to incoming and outgoing interfaces and their properties. * Handle interfaces coming up and down and socket errors. * Inter-op testing. // ---------------------------------------------------------------------------- // Post 1.0 Release * MIB. * Clean up RIB interaction. Add flag to each router indicating that the RIB is aware it and nobble flag when route is updated. * Consider keeping losing routes, age them in the normal way, etc, and be prepared to fall back to them if/when winning routes expire. * Arrange to have triggered updates go out of all RIP Port instances at the same time. This is a little tricky as each RIP Port instance is autonomous. * Add horizon checks to tools/rip_announcer.cc and tools/ripng_announcer.cc for packets they receive. This is icing on the cake. xorp/rip/xrl_redist_manager.hh0000664000076400007640000000336111540224235016645 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/rip/xrl_redist_manager.hh,v 1.16 2008/10/02 21:58:19 bms Exp $ #ifndef __RIP_XRL_REDIST_MANAGER__ #define __RIP_XRL_REDIST_MANAGER__ #include "libxorp/xorp.h" #include "libxorp/ipnet.hh" #include "libxorp/service.hh" #include "redist.hh" template class System; /** * Xrl Route redistribution manager. */ template class XrlRedistManager : public ServiceBase { public: typedef A Addr; typedef IPNet Net; public: XrlRedistManager(System& system); ~XrlRedistManager(); int startup(); int shutdown(); void add_route(const Net& net, const Addr& nh, const string& ifname, const string& vifname, uint16_t cost, uint16_t tag, const PolicyTags& policytags); void delete_route(const Net& net); protected: RouteRedistributor _rr; }; #endif // __RIP_XRL_REDIST_MANAGER__ xorp/rip/output_table.cc0000664000076400007640000000737611540224234015502 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "libxorp/xorp.h" #include "output_table.hh" #include "packet_assembly.hh" #include "packet_queue.hh" #include "route_db.hh" template void OutputTable::output_packet() { if (_rw_valid == false) { _rw.reset(); _rw_valid = true; } _rw.resume(); ResponsePacketAssembler rpa(this->_port); RipPacket* pkt = new RipPacket(this->ip_addr(), this->ip_port()); rpa.packet_start(pkt); uint32_t done = 0; const RouteEntry* r = 0; for (r = _rw.current_route(); r != 0; r = _rw.next_route()) { // // We may either "drop the packet..." // or set cost to infinity... // or depending on poison-reverse / horizon settings // if (r->filtered()) { continue; } pair p = this->_port.route_policy(*r); if (p.second > RIP_INFINITY) { continue; } RouteEntryOrigin* origin = NULL; // XXX string ifname, vifname; // XXX: not set, because not needed RouteEntry* copy = new RouteEntry(r->net(), p.first, ifname, vifname, p.second, origin, r->tag(), r->policytags()); bool accepted = this->do_filtering(copy); if (!accepted) { delete copy; continue; } rpa.packet_add_route(copy->net(), copy->nexthop(), copy->cost(), copy->tag()); // XXX: packet_add_route stores args delete copy; done++; if (rpa.packet_full()) { _rw.next_route(); break; } } list*> auth_packets; if (done == 0 || rpa.packet_finish(auth_packets) == false) { // No routes added to packet or error finishing packet off. } else { typename list*>::iterator iter; for (iter = auth_packets.begin(); iter != auth_packets.end(); ++iter) { RipPacket* auth_pkt = *iter; this->_pkt_queue.enqueue_packet(auth_pkt); if (this->ip_port() == RIP_AF_CONSTANTS::IP_PORT) { this->_port.counters().incr_unsolicited_updates(); } else { this->_port.counters().incr_non_rip_updates_sent(); } this->incr_packets_sent(); } this->_port.push_packets(); } delete pkt; if (r == 0) { // Reached null route so note route walker is now invalid. _rw_valid = false; } else { // Not finished so set time to reschedule self and pause // route walker. this->_op_timer = this->_e.new_oneoff_after_ms(this->interpacket_gap_ms(), callback(this, &OutputTable::output_packet)); _rw.pause(this->interpacket_gap_ms()); } } template void OutputTable::start_output_processing() { output_packet(); // starts timer } template void OutputTable::stop_output_processing() { this->_op_timer.unschedule(); // stop timer } // ---------------------------------------------------------------------------- // Instantiations #ifdef INSTANTIATE_IPV4 template class OutputTable; #endif #ifdef INSTANTIATE_IPV6 template class OutputTable; #endif xorp/rip/peer.hh0000664000076400007640000001512211421137511013723 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/rip/peer.hh,v 1.15 2008/10/02 21:58:16 bms Exp $ #ifndef __RIP_PEER_HH__ #define __RIP_PEER_HH__ #include "libxorp/timeval.hh" #include "route_entry.hh" /** * @short Container of counters associated with a peer. */ struct PeerCounters { public: PeerCounters() : _packets_recv(0), _updates_recv(0), _requests_recv(0), _bad_routes(0), _bad_packets(0), _bad_auth_packets(0) {} /** * Get the total number of packets received. */ uint32_t packets_recv() const { return _packets_recv; } /** * Increment the total number of packets received. */ void incr_packets_recv() { _packets_recv++; } /** * Get the total number of update packets received. */ uint32_t update_packets_recv() const { return _updates_recv; } /** * Increment the total number of update packets received. */ void incr_update_packets_recv() { _updates_recv++; } /** * Get the total number of table request packets received. */ uint32_t table_requests_recv() const { return _requests_recv; } /** * Increment the total number of table request packets received. */ void incr_table_requests_recv() { _requests_recv++; } /** * Get the number of bad routes received (eg invalid metric, * invalid address family). */ uint32_t bad_routes() const { return _bad_routes; } /** * Increment the number of bad routes received. */ void incr_bad_routes() { _bad_routes++; } /** * Get the number of bad response packets received. */ uint32_t bad_packets() const { return _bad_packets; } /** * Increment the number of bad response packets received. */ void incr_bad_packets() { _bad_packets++; } /** * Get the number of bad authentication packets received. */ uint32_t bad_auth_packets() const { return _bad_auth_packets; } /** * Increment the number of bad authentication packets received. */ void incr_bad_auth_packets() { _bad_auth_packets++; } protected: uint32_t _packets_recv; uint32_t _updates_recv; uint32_t _requests_recv; uint32_t _bad_routes; uint32_t _bad_packets; uint32_t _bad_auth_packets; }; // Forward declaration of Peer class template class Peer; /** * @short RIP Peer Routes * * A class for storing the original routes received from a Peer host. * Those routes are used to push them whenever the routing policy is modified. */ template class PeerRoutes : public RouteEntryOrigin { public: PeerRoutes(Peer& peer) : RouteEntryOrigin(false), _peer(peer) {} private: uint32_t expiry_secs() const; uint32_t deletion_secs() const; Peer& _peer; // The corresponding peer }; // Forward declaration of Port class template class Port; /** * @short RIP Peer * * A RIP peer is a host that sent RIP packets, originating routes, to * the local RIP Port (@ref Port) that have originated routes. * Most of a Peer's work is conducted by the Port associated with * the Peer instance. The Peer class just acts as a container of * information about the Peer host, such as the number of packets sent, * the time of last update, etc. * It also contains the original routes as received from the Peer host. * Those routes are used to push them whenever the routing policy is modified. */ template class Peer : public RouteEntryOrigin { public: typedef A Addr; typedef Port RipPort; typedef RouteEntry Route; public: Peer(RipPort& p, const Addr& addr); ~Peer(); /** * Get address of Peer. */ const Addr& address() const { return _addr; } /** * Get counters associated with Peer. */ PeerCounters& counters() { return _counters; } /** * Get counters associated with Peer. */ const PeerCounters& counters() const { return _counters; } /** * Get port associated with Peer. */ RipPort& port() { return _port; } /** * Get port associated with Peer. */ const RipPort& port() const { return _port; } /** * Set last active time. */ void set_last_active(const TimeVal& t) { _last_active = t; } /** * Get last active time. */ const TimeVal& last_active() const { return _last_active; } /** * Update Route Entry in database for specified route. * * @param net the network route being updated. * @param nexthop the corresponding nexthop address. * @param cost the corresponding metric value as received from the * route originator. * @param tag the corresponding route tag. * @param policytags the policytags of this route. * @return true if an update occurs, false otherwise. */ bool update_route(const IPNet& net, const A& nexthop, uint32_t cost, uint32_t tag, const PolicyTags& policytags); /** * Push routes through the system. * * This is needed to apply the policy filters for re-filtering. */ void push_routes(); uint32_t expiry_secs() const; uint32_t deletion_secs() const; protected: void set_expiry_timer(Route* route); void expire_route(Route* route); RipPort& _port; Addr _addr; PeerCounters _counters; TimeVal _last_active; PeerRoutes _peer_routes; }; /** * Unary Function Predicate class for use with STL to determine if a * peer has an address. */ template struct peer_has_address { peer_has_address(const A& addr) : _a(addr) {} bool operator() (const Peer& p) const { return p.address() == _a; } bool operator() (const Peer* p) const { return p->address() == _a; } private: A _a; }; #endif // __RIP_PEER_HH__ xorp/rip/output_updates.hh0000664000076400007640000000442411421137511016060 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/rip/output_updates.hh,v 1.10 2008/10/02 21:58:16 bms Exp $ #ifndef __RIP_OUTPUT_UPDATES_HH__ #define __RIP_OUTPUT_UPDATES_HH__ #include "rip_module.h" #include "libxorp/xlog.h" #include "output.hh" #include "route_db.hh" #include "update_queue.hh" /** * @short Triggered Updates Output class. * * The OutputUpdate class produces an asynchronous sequence of * triggered update packets. * * Specialized implementations exist for IPv4 and IPv6. * Non-copyable due to inheritance from OutputBase. */ template class OutputUpdates : public OutputBase { public: OutputUpdates(EventLoop& e, Port& port, PacketQueue& pkt_queue, RouteDB& rdb, const A& ip_addr = RIP_AF_CONSTANTS::IP_GROUP(), uint16_t ip_port = RIP_AF_CONSTANTS::IP_PORT); ~OutputUpdates(); /** * Fast forward iterator doing triggered up reading. * * Triggered updates do not run during periodic route table dumps. This * method should be called immediately before halting for periodic update * as it will effectively stop the output of updates that are already * covered in table dump. */ void ffwd(); protected: void output_packet(); void start_output_processing(); void stop_output_processing(); private: UpdateQueue& _uq; typename UpdateQueue::ReadIterator _uq_iter; }; #endif // __RIP_OUTPUT_UPDATES_HH__ xorp/rip/xrl_config.cc0000664000076400007640000000227011421137511015110 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "xrl_config.hh" static const char* _rib_name = "rib"; static const char* _fea_name = "fea"; const char* xrl_rib_name() { return _rib_name; } const char* xrl_fea_name() { return _fea_name; } void set_xrl_rib_name(const char* n) { _rib_name = n; } void set_xrl_fea_name(const char* n) { _fea_name = n; } xorp/rip/xrl_target_ripng.cc0000664000076400007640000003361611540225534016345 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/status_codes.h" #include "libxipc/xrl_router.hh" #include "constants.hh" #include "system.hh" #include "xrl_process_spy.hh" #include "xrl_port_manager.hh" #include "xrl_redist_manager.hh" #include "xrl_target_ripng.hh" #include "xrl_target_common.hh" XrlRipngTarget::XrlRipngTarget(EventLoop& el, XrlRouter& xr, XrlProcessSpy& xps, XrlPortManager& xpm, XrlRedistManager& xrm, System& rip_system) : XrlRipngTargetBase(&xr), _e(el) { _ct = new XrlRipCommonTarget(xps, xpm, xrm, rip_system); } XrlRipngTarget::~XrlRipngTarget() { delete _ct; } XrlCmdError XrlRipngTarget::common_0_1_get_target_name(string& n) { n = get_name(); return XrlCmdError::OKAY(); } XrlCmdError XrlRipngTarget::common_0_1_get_version(string& v) { v = string(version()); return XrlCmdError::OKAY(); } void XrlRipngTarget::set_status(ProcessStatus status, const string& note) { _ct->set_status(status, note); } XrlCmdError XrlRipngTarget::common_0_1_get_status(uint32_t& status, string& reason) { return _ct->common_0_1_get_status(status, reason); } XrlCmdError XrlRipngTarget::common_0_1_shutdown() { return _ct->common_0_1_shutdown(); } XrlCmdError XrlRipngTarget::common_0_1_startup() { return _ct->common_0_1_startup(); } XrlCmdError XrlRipngTarget::finder_event_observer_0_1_xrl_target_birth(const string& cname, const string& iname) { return _ct->finder_event_observer_0_1_xrl_target_birth(cname, iname); } XrlCmdError XrlRipngTarget::finder_event_observer_0_1_xrl_target_death(const string& cname, const string& iname) { return _ct->finder_event_observer_0_1_xrl_target_death(cname, iname); } XrlCmdError XrlRipngTarget::ripng_0_1_add_rip_address(const string& ifn, const string& vifn, const IPv6& addr) { return _ct->ripx_0_1_add_rip_address(ifn, vifn, addr); } XrlCmdError XrlRipngTarget::ripng_0_1_remove_rip_address(const string& ifn, const string& vifn, const IPv6& addr) { return _ct->ripx_0_1_remove_rip_address(ifn, vifn, addr); } XrlCmdError XrlRipngTarget::ripng_0_1_set_rip_address_enabled(const string& ifn, const string& vifn, const IPv6& a, const bool& en) { return _ct->ripx_0_1_set_rip_address_enabled(ifn, vifn, a, en); } XrlCmdError XrlRipngTarget::ripng_0_1_rip_address_enabled(const string& ifn, const string& vifn, const IPv6& a, bool& en) { return _ct->ripx_0_1_rip_address_enabled(ifn, vifn, a, en); } XrlCmdError XrlRipngTarget::ripng_0_1_set_cost(const string& ifn, const string& vifn, const IPv6& a, const uint32_t& cost) { return _ct->ripx_0_1_set_cost(ifn, vifn, a, cost); } XrlCmdError XrlRipngTarget::ripng_0_1_cost(const string& ifn, const string& vifn, const IPv6& a, uint32_t& cost) { return _ct->ripx_0_1_cost(ifn, vifn, a, cost); } XrlCmdError XrlRipngTarget::ripng_0_1_set_horizon(const string& ifn, const string& vifn, const IPv6& a, const string& horizon) { return _ct->ripx_0_1_set_horizon(ifn, vifn, a, horizon); } XrlCmdError XrlRipngTarget::ripng_0_1_horizon(const string& ifn, const string& vifn, const IPv6& a, string& horizon) { return _ct->ripx_0_1_horizon(ifn, vifn, a, horizon); } XrlCmdError XrlRipngTarget::ripng_0_1_set_passive(const string& ifn, const string& vifn, const IPv6& a, const bool& passive) { return _ct->ripx_0_1_set_passive(ifn, vifn, a, passive); } XrlCmdError XrlRipngTarget::ripng_0_1_passive(const string& ifn, const string& vifn, const IPv6& a, bool& passive) { return _ct->ripx_0_1_passive(ifn, vifn, a, passive); } XrlCmdError XrlRipngTarget::ripng_0_1_set_accept_non_rip_requests(const string& ifn, const string& vifn, const IPv6& addr, const bool& accept) { return _ct->ripx_0_1_set_accept_non_rip_requests(ifn, vifn, addr, accept); } XrlCmdError XrlRipngTarget::ripng_0_1_accept_non_rip_requests(const string& ifn, const string& vifn, const IPv6& addr, bool& accept) { return _ct->ripx_0_1_accept_non_rip_requests(ifn, vifn, addr, accept); } XrlCmdError XrlRipngTarget::ripng_0_1_set_accept_default_route(const string& ifn, const string& vifn, const IPv6& addr, const bool& accept) { return _ct->ripx_0_1_set_accept_default_route(ifn, vifn, addr, accept); } XrlCmdError XrlRipngTarget::ripng_0_1_accept_default_route(const string& ifn, const string& vifn, const IPv6& addr, bool& accept) { return _ct->ripx_0_1_accept_default_route(ifn, vifn, addr, accept); } XrlCmdError XrlRipngTarget::ripng_0_1_set_advertise_default_route(const string& ifn, const string& vifn, const IPv6& addr, const bool& adv) { return _ct->ripx_0_1_set_advertise_default_route(ifn, vifn, addr, adv); } XrlCmdError XrlRipngTarget::ripng_0_1_advertise_default_route(const string& ifn, const string& vifn, const IPv6& addr, bool& adv) { return _ct->ripx_0_1_advertise_default_route(ifn, vifn, addr, adv); } XrlCmdError XrlRipngTarget::ripng_0_1_set_route_timeout(const string& ifn, const string& vifn, const IPv6& a, const uint32_t& t) { return _ct->ripx_0_1_set_route_timeout(ifn, vifn, a, t); } XrlCmdError XrlRipngTarget::ripng_0_1_route_timeout(const string& ifn, const string& vifn, const IPv6& a, uint32_t& t) { return _ct->ripx_0_1_route_timeout(ifn, vifn, a, t); } XrlCmdError XrlRipngTarget::ripng_0_1_set_deletion_delay(const string& ifn, const string& vifn, const IPv6& a, const uint32_t& t) { return _ct->ripx_0_1_set_deletion_delay(ifn, vifn, a, t); } XrlCmdError XrlRipngTarget::ripng_0_1_deletion_delay(const string& ifn, const string& vifn, const IPv6& a, uint32_t& t) { return _ct->ripx_0_1_deletion_delay(ifn, vifn, a, t); } XrlCmdError XrlRipngTarget::ripng_0_1_set_request_interval(const string& ifn, const string& vifn, const IPv6& a, const uint32_t& t) { return _ct->ripx_0_1_set_request_interval(ifn, vifn, a, t); } XrlCmdError XrlRipngTarget::ripng_0_1_request_interval(const string& ifn, const string& vifn, const IPv6& a, uint32_t& t) { return _ct->ripx_0_1_request_interval(ifn, vifn, a, t); } XrlCmdError XrlRipngTarget::ripng_0_1_set_update_interval(const string& ifn, const string& vifn, const IPv6& a, const uint32_t& t_secs) { return _ct->ripx_0_1_set_update_interval(ifn, vifn, a, t_secs); } XrlCmdError XrlRipngTarget::ripng_0_1_update_interval(const string& ifn, const string& vifn, const IPv6& a, uint32_t& t_secs) { return _ct->ripx_0_1_update_interval(ifn, vifn, a, t_secs); } XrlCmdError XrlRipngTarget::ripng_0_1_set_update_jitter(const string& ifn, const string& vifn, const IPv6& a, const uint32_t& t_jitter) { return _ct->ripx_0_1_set_update_jitter(ifn, vifn, a, t_jitter); } XrlCmdError XrlRipngTarget::ripng_0_1_update_jitter(const string& ifn, const string& vifn, const IPv6& a, uint32_t& t_jitter) { return _ct->ripx_0_1_update_jitter(ifn, vifn, a, t_jitter); } XrlCmdError XrlRipngTarget::ripng_0_1_set_triggered_update_delay(const string& ifn, const string& vifn, const IPv6& a, const uint32_t& t_secs) { return _ct->ripx_0_1_set_triggered_update_delay(ifn, vifn, a, t_secs); } XrlCmdError XrlRipngTarget::ripng_0_1_triggered_update_delay(const string& ifn, const string& vifn, const IPv6& a, uint32_t& t_secs) { return _ct->ripx_0_1_triggered_update_delay(ifn, vifn, a, t_secs); } XrlCmdError XrlRipngTarget::ripng_0_1_set_triggered_update_jitter(const string& ifn, const string& vifn, const IPv6& a, const uint32_t& t_jitter) { return _ct->ripx_0_1_set_triggered_update_jitter(ifn, vifn, a, t_jitter); } XrlCmdError XrlRipngTarget::ripng_0_1_triggered_update_jitter(const string& ifn, const string& vifn, const IPv6& a, uint32_t& t_jitter) { return _ct->ripx_0_1_triggered_update_jitter(ifn, vifn, a, t_jitter); } XrlCmdError XrlRipngTarget::ripng_0_1_set_interpacket_delay(const string& ifn, const string& vifn, const IPv6& a, const uint32_t& t_msecs) { return _ct->ripx_0_1_set_interpacket_delay(ifn, vifn, a, t_msecs); } XrlCmdError XrlRipngTarget::ripng_0_1_interpacket_delay(const string& ifn, const string& vifn, const IPv6& a, uint32_t& t_msecs) { return _ct->ripx_0_1_interpacket_delay(ifn, vifn, a, t_msecs); } XrlCmdError XrlRipngTarget::ripng_0_1_rip_address_status(const string& ifn, const string& vifn, const IPv6& a, string& status) { return _ct->ripx_0_1_rip_address_status(ifn, vifn, a, status); } XrlCmdError XrlRipngTarget::ripng_0_1_get_all_addresses(XrlAtomList& ifnames, XrlAtomList& vifnames, XrlAtomList& addrs) { return _ct->ripx_0_1_get_all_addresses(ifnames, vifnames, addrs); } XrlCmdError XrlRipngTarget::ripng_0_1_get_peers(const string& ifn, const string& vifn, const IPv6& a, XrlAtomList& peers) { return _ct->ripx_0_1_get_peers(ifn, vifn, a, peers); } XrlCmdError XrlRipngTarget::ripng_0_1_get_all_peers(XrlAtomList& peers, XrlAtomList& ifnames, XrlAtomList& vifnames, XrlAtomList& addrs) { return _ct->ripx_0_1_get_all_peers(peers, ifnames, vifnames, addrs); } XrlCmdError XrlRipngTarget::ripng_0_1_get_counters(const string& ifname, const string& vifname, const IPv6& addr, XrlAtomList& descs, XrlAtomList& values) { return _ct->ripx_0_1_get_counters(ifname, vifname, addr, descs, values); } XrlCmdError XrlRipngTarget::ripng_0_1_get_peer_counters(const string& ifn, const string& vifn, const IPv6& addr, const IPv6& peer, XrlAtomList& descs, XrlAtomList& vals, uint32_t& last_active) { return _ct->ripx_0_1_get_peer_counters(ifn, vifn, addr, peer, descs, vals, last_active); } XrlCmdError XrlRipngTarget::ripng_0_1_trace(const string& tvar, const bool& enable) { debug_msg("trace variable %s enable %s\n", tvar.c_str(), bool_c_str(enable)); if (tvar == "all") { _ct->trace(enable); } else { return XrlCmdError:: COMMAND_FAILED(c_format("Unknown variable %s", tvar.c_str())); } return XrlCmdError::OKAY(); } XrlCmdError XrlRipngTarget::socket6_user_0_1_recv_event( const string& sockid, const string& if_name, const string& vif_name, const IPv6& src_host, const uint32_t& src_port, const vector& pdata ) { return _ct->socketx_user_0_1_recv_event(sockid, if_name, vif_name, src_host, src_port, pdata); } XrlCmdError XrlRipngTarget::socket6_user_0_1_inbound_connect_event( const string& sockid, const IPv6& src_host, const uint32_t& src_port, const string& new_sockid, bool& accept) { return _ct->socketx_user_0_1_inbound_connect_event(sockid, src_host, src_port, new_sockid, accept); } XrlCmdError XrlRipngTarget::socket6_user_0_1_outgoing_connect_event( const string& sockid) { return _ct->socketx_user_0_1_outgoing_connect_event(sockid); } XrlCmdError XrlRipngTarget::socket6_user_0_1_error_event(const string& sockid, const string& reason, const bool& fatal) { return _ct->socketx_user_0_1_error_event(sockid, reason, fatal); } XrlCmdError XrlRipngTarget::socket6_user_0_1_disconnect_event(const string& sockid) { return _ct->socketx_user_0_1_disconnect_event(sockid); } XrlCmdError XrlRipngTarget::policy_backend_0_1_configure(const uint32_t& filter, const string& conf) { return _ct->policy_backend_0_1_configure(filter, conf); } XrlCmdError XrlRipngTarget::policy_backend_0_1_reset(const uint32_t& filter) { return _ct->policy_backend_0_1_reset(filter); } XrlCmdError XrlRipngTarget::policy_backend_0_1_push_routes() { return _ct->policy_backend_0_1_push_routes(); } XrlCmdError XrlRipngTarget::policy_redist6_0_1_add_route6(const IPv6Net& network, const bool& unicast, const bool& multicast, const IPv6& nexthop, const uint32_t& metric, const XrlAtomList& policytags) { return _ct->policy_redistx_0_1_add_routex(network, unicast, multicast, nexthop, metric, policytags); } XrlCmdError XrlRipngTarget::policy_redist6_0_1_delete_route6(const IPv6Net& network, const bool& unicast, const bool& multicast) { return _ct->policy_redistx_0_1_delete_routex(network, unicast, multicast); } xorp/rip/packet_queue.hh0000664000076400007640000000607411540224234015452 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/rip/packet_queue.hh,v 1.13 2008/10/02 21:58:16 bms Exp $ #ifndef __RIP_PACKET_QUEUE_HH__ #define __RIP_PACKET_QUEUE_HH__ #include "libxorp/xorp.h" #include "packets.hh" /** * @short Outbound packet queue. * * The queue is of fixed size and does FIFO. When the queue becomes * full the eldest packet behind the head is dropped, ie since the * head may be in transit. */ template class PacketQueue { public: typedef list*> QueueRep; public: PacketQueue(); ~PacketQueue(); /** * Place packet in ready to sent queue. The supplied packet is * expected to have been allocated with the standard new operator * and will be destructed by the packet queue when it is dropped * or popped from the queue. * * This may cause older packets in the queue to be dropped to make * sufficient space for new packet. */ void enqueue_packet(const RipPacket* pkt); /** * @return true if no packets are queued, false otherwise. */ bool empty() const; /** * Peek at head packet if it exists. * * @return pointer to head packet if it exists, 0 otherwise. */ const RipPacket* head() const; /** * Remove head packet. */ void pop_head(); /** * Discard packet behind head packet to make space for new packets. * * @return true if an old packet was dropped, false if no packets * dropped. */ bool drop_old(); /** * Flush queued packets. */ void flush_packets(); /** * Set the maximum amount of data to buffer. */ void set_max_buffered_bytes(uint32_t mb); /** * Get the maximum amount of buffered data. */ uint32_t max_buffered_bytes() const; /** * Get the current amount of buffered data. */ uint32_t buffered_bytes() const; /** * Get the number of packets dropped from queue. */ uint32_t drop_count() const; /** * Reset packet drop counter. */ void reset_drop_count(); protected: QueueRep _ready_packets; uint32_t _buffered_bytes; uint32_t _max_buffered_bytes; uint32_t _drops; }; #endif // __RIP_PACKET_QUEUE_HH__ xorp/rip/xrl_redist_manager.cc0000664000076400007640000000527011421137511016632 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING #include "rip_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipv4.hh" #include "libxorp/ipv6.hh" #include "system.hh" #include "xrl_redist_manager.hh" // ---------------------------------------------------------------------------- // XrlRedistManager Implementation template XrlRedistManager::XrlRedistManager(System& system) : _rr(system.route_db()) { } template XrlRedistManager::~XrlRedistManager() { } template int XrlRedistManager::startup() { if (status() == SERVICE_READY) { set_status(SERVICE_RUNNING); return (XORP_OK); } return (XORP_ERROR); } template int XrlRedistManager::shutdown() { if (status() != SERVICE_RUNNING) return (XORP_ERROR); _rr.withdraw_routes(); set_status(SERVICE_SHUTDOWN); return (XORP_OK); } template void XrlRedistManager::add_route(const Net& net, const Addr& nh, const string& ifname, const string& vifname, uint16_t cost, uint16_t tag, const PolicyTags& policytags) { debug_msg("got redist add_route for %s %s\n", net.str().c_str(), nh.str().c_str()); // XXX: Ignore link-local routes if (net.masked_addr().is_linklocal_unicast()) return; _rr.add_route(net, nh, ifname, vifname, cost, tag, policytags); } template void XrlRedistManager::delete_route(const Net& net) { // XXX: Ignore link-local routes if (net.masked_addr().is_linklocal_unicast()) return; _rr.expire_route(net); } #ifdef INSTANTIATE_IPV4 template class XrlRedistManager; #endif /* INSTANTIATE_IPV4 */ #ifdef INSTANTIATE_IPV6 template class XrlRedistManager; #endif /* INSTANTIATE_IPV6 */ xorp/rip/route_db.cc0000664000076400007640000004117611540224234014572 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #include "rip_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/eventloop.hh" #include "libxorp/ipv4.hh" #include "libxorp/ipv6.hh" #include "libxorp/xlog.h" #include "constants.hh" #include "route_db.hh" #include "update_queue.hh" #include "rip_varrw.hh" #include "peer.hh" // ---------------------------------------------------------------------------- // NetCmp template bool NetCmp::operator() (const IPNet& l, const IPNet& r) const { if (l.prefix_len() < r.prefix_len()) return true; if (l.prefix_len() > r.prefix_len()) return false; return l.masked_addr() < r.masked_addr(); } // ---------------------------------------------------------------------------- // RouteDB template RouteDB::RouteDB(EventLoop& e, PolicyFilters& pfs) : _eventloop(e), _policy_filters(pfs) { _uq = new UpdateQueue(); } template RouteDB::~RouteDB() { _routes.erase(_routes.begin(), _routes.end()); for (typename RouteContainerNoRef::iterator i = _rib_routes.begin(); i != _rib_routes.end(); ++i) { delete (*i).second; } delete _uq; } template bool RouteDB::insert_peer(Peer* peer) { typename set* >::iterator iter; iter = _peers.find(peer); if (iter != _peers.end()) return (false); _peers.insert(peer); return (true); } template bool RouteDB::erase_peer(Peer* peer) { typename set* >::iterator iter; iter = _peers.find(peer); if (iter == _peers.end()) return (false); _peers.erase(iter); return (true); } template void RouteDB::delete_route(Route* r) { typename RouteContainer::iterator i = _routes.find(r->net()); if (i == _routes.end()) { // Libxorp is bjorkfest if this happens... XLOG_ERROR("Route for %s missing when deletion came.", r->net().str().c_str()); return; } // // Check if we have rib routes to replace the delete route. // XXX: might be more correct to do all this in expire route. // typename RouteContainerNoRef::iterator iter = _rib_routes.find(r->net()); _routes.erase(i); // add possible rib route if (iter != _rib_routes.end()) { r = iter->second; XLOG_TRACE(trace()._routes, "Deleted route, but re-added from RIB routes: %s\n", r->net().str().c_str()); debug_msg("[RIP] Deleted route, but re-added from RIB routes: %s\n", r->net().str().c_str()); update_route(r->net(), r->nexthop(), r->ifname(), r->vifname(), r->cost(), r->tag(), _rib_origin, r->policytags(), false); } } template void RouteDB::set_deletion_timer(Route* r) { RouteOrigin* o = r->origin(); uint32_t deletion_ms = o->deletion_secs() * 1000; XorpTimer t = _eventloop.new_oneoff_after_ms(deletion_ms, callback(this, &RouteDB::delete_route, r)); r->set_timer(t); } template void RouteDB::expire_route(Route* r) { if (false == update_route(r->net(), r->nexthop(), r->ifname(), r->vifname(), RIP_INFINITY, r->tag(), r->origin(), r->policytags(), false)) { XLOG_ERROR("Expire route failed."); } } template void RouteDB::set_expiry_timer(Route* r) { XorpTimer t; RouteOrigin* o = r->origin(); uint32_t expiry_secs = o->expiry_secs(); if (expiry_secs) { t = _eventloop.new_oneoff_after_ms(expiry_secs * 1000, callback(this, &RouteDB::expire_route, r)); } r->set_timer(t); } template bool RouteDB::do_filtering(Route* r) { try { RIPVarRW varrw(*r); debug_msg("[RIP] Running import filter on route %s\n", r->net().str().c_str()); XLOG_TRACE(trace()._routes, "Running import filter on route %s\n", r->net().str().c_str()); bool accepted = _policy_filters.run_filter(filter::IMPORT, varrw); if (!accepted) return false; RIPVarRW varrw2(*r); debug_msg("[RIP] Running source match filter on route %s\n", r->net().str().c_str()); XLOG_TRACE(trace()._routes, "Running source match filter on route %s\n", r->net().str().c_str()); _policy_filters.run_filter(filter::EXPORT_SOURCEMATCH, varrw2); return true; } catch(const PolicyException& e) { XLOG_FATAL("PolicyException: %s", e.str().c_str()); XLOG_UNFINISHED(); } } template bool RouteDB::update_route(const Net& net, const Addr& nexthop, const string& ifname, const string& vifname, uint32_t cost, uint32_t tag, RouteOrigin* o, const PolicyTags& policytags, bool is_policy_push) { if (tag > 0xffff) { // Ingress sanity checks should take care of this XLOG_FATAL("Invalid tag (%u) when updating route.", XORP_UINT_CAST(tag)); return false; } if (cost > RIP_INFINITY) { cost = RIP_INFINITY; } // // Update steps, based on RFC2453 pp. 26-28 // bool updated = false; Route* r = 0; typename RouteContainer::iterator i = _routes.find(net); if (_routes.end() == i) { // Route does not appear in table so it needs to be // created if peer does not have an entry for it or // resurrected if it does. But first this... if (cost == RIP_INFINITY) { // Don't bother adding a route for unreachable net return false; } // Create route if necessary r = o->find_route(net); if (r == 0) { r = new Route(net, nexthop, ifname, vifname, cost, o, tag, policytags); set_expiry_timer(r); bool ok(_routes.insert(typename RouteContainer::value_type(net, r)).second); XLOG_ASSERT(ok); bool accepted = do_filtering(r); r->set_filtered(!accepted); if (!accepted) return false; _uq->push_back(r); return true; } // Resurrect route bool ok(_routes.insert(typename RouteContainer::value_type(net, r)).second); XLOG_ASSERT(ok); // XXX: this is wrong bool accepted = do_filtering(r); r->set_filtered(!accepted); if (accepted) updated = true; } else { r = i->second.get(); } RouteEntryOrigin* no_origin = NULL; RouteEntry* new_route = new RouteEntry(r->net(), nexthop, ifname, vifname, cost, no_origin, tag, policytags); // XXX: lost origin bool accepted = do_filtering(new_route); // XXX: this whole section of code is too entangled. if (r->origin() == o) { uint16_t orig_cost = r->cost(); updated |= r->set_nexthop(new_route->nexthop()); updated |= r->set_ifname(new_route->ifname()); updated |= r->set_vifname(new_route->vifname()); updated |= r->set_tag(new_route->tag()); updated |= r->set_cost(new_route->cost()); updated |= r->set_policytags(new_route->policytags()); delete new_route; if (cost == RIP_INFINITY) { if ((orig_cost == RIP_INFINITY) && r->timer().scheduled()) { // // XXX: The deletion process is started only when the // metric is set the first time to infinity. // } else { set_deletion_timer(r); } } else { if (is_policy_push && !updated) { // // XXX: The same route was pushed because of policy // reconfiguration, hence we don't need to update its // expiry timer. // } else { set_expiry_timer(r); } } bool was_filtered = r->filtered(); r->set_filtered(!accepted); debug_msg("[RIP] Was filtered: %d, Accepted: %d\n", was_filtered, accepted); XLOG_TRACE(trace()._routes, "Was filtered: %d, Accepted: %d\n", was_filtered, accepted); if (accepted) { if (was_filtered) { updated = true; } else { } } else { if (was_filtered) { return false; } else { if (cost != RIP_INFINITY) { // // XXX: Advertise the filtered route with INFINITY metric. // If the filtered route should not be advertised with // such metric, then remove the "set_cost()" statement // below, and add "return true;" at the end of this block. // r->set_cost(RIP_INFINITY); set_deletion_timer(r); updated = true; } // delete_route(r); } } } else { // route from other origin if (!accepted) { delete new_route; return false; } // this is "RIP's decision" -- where one route wins another. // Source-match filtering should go here. bool should_replace = false; do { if (r->cost() > new_route->cost()) { should_replace = true; break; } if (r->cost() < new_route->cost()) break; // XXX: the old route is better // // Same cost routes // if (new_route->cost() == RIP_INFINITY) { // // XXX: Don't update routes if both the old and the new // costs are infinity. // break; } // // If the existing route is showing signs of timing out, it // may be better to switch to an equally-good alternative route // immediately, rather than waiting for the timeout to happen. // The heuristic is: if the route is at least halfway to the // expiration point, switch to the new route // (see RFC 2453 Section 3.9.2 and RFC 2080 Section 2.4.2). // TimeVal expiry_timeval = TimeVal::ZERO(); if (r->origin() != NULL) expiry_timeval = TimeVal(r->origin()->expiry_secs(), 0); if (expiry_timeval == TimeVal::ZERO()) break; // XXX: the old route would never expire TimeVal remain; if (r->timer().time_remaining(remain) != true) break; // XXX: couldn't get the remaining time if (remain < (expiry_timeval / 2)) { should_replace = true; break; } break; } while (false); if (should_replace) { r->set_nexthop(new_route->nexthop()); r->set_ifname(new_route->ifname()); r->set_vifname(new_route->vifname()); r->set_tag(new_route->tag()); r->set_cost(new_route->cost()); r->set_policytags(new_route->policytags()); r->set_origin(o); set_expiry_timer(r); updated = true; } delete new_route; } if (updated) { _uq->push_back(r); } return updated; } template void RouteDB::dump_routes(vector& routes) { typename RouteContainer::iterator i = _routes.begin(); while (i != _routes.end()) { routes.push_back(i->second); ++i; } } template void RouteDB::flush_routes() { _uq->flush(); _routes.erase(_routes.begin(), _routes.end()); } template uint32_t RouteDB::route_count() const { return _routes.size(); } template const RouteEntry* RouteDB::find_route(const IPNet& net) const { typename RouteContainer::const_iterator ri = _routes.find(net); if (ri == _routes.end()) return 0; return ri->second.get(); } template UpdateQueue& RouteDB::update_queue() { return *_uq; } template const UpdateQueue& RouteDB::update_queue() const { return *_uq; } template typename RouteDB::RouteContainer& RouteDB::routes() { return _routes; } // ---------------------------------------------------------------------------- // RouteWalker template const typename RouteWalker::Net RouteWalker::NO_NET(~A::ZERO(), 0); template RouteWalker::RouteWalker(RouteDB& rdb) : _route_db(rdb), _state(STATE_RUNNING), _last_visited(NO_NET), _pos(rdb.routes().begin()) { } template RouteWalker::~RouteWalker() { } template const typename RouteWalker::Route* RouteWalker::next_route() { if (state() != STATE_RUNNING) { XLOG_ERROR("Calling RouteWalker::next_route() whilst not in " "STATE_RUNNING state."); return 0; } if (++_pos == _route_db.routes().end()) { return 0; } return _pos->second.get(); } template const typename RouteWalker::Route* RouteWalker::current_route() { if (state() != STATE_RUNNING) { XLOG_ERROR("Calling RouteWalker::next_route() whilst not in " "STATE_RUNNING state."); return 0; } if (_pos == _route_db.routes().end()) { return 0; } return _pos->second.get(); } template void RouteWalker::pause(uint32_t pause_ms) { if (state() == STATE_PAUSED) return; _state = STATE_PAUSED; if (_pos == _route_db.routes().end()) { _last_visited = NO_NET; return; } // Check if route has a deletion timer and if so push it's expiry time // back to maximize the chance of the route still being valid when // resume is called. Otherwise we have to do more work to find a good // point to resume from. We're advertising the route at infinity // so advertising it once past it's original expiry is no big deal XorpTimer t = _pos->second->timer(); if (t.scheduled() && _pos->second->cost() == RIP_INFINITY) { TimeVal next_run; _route_db.eventloop().current_time(next_run); next_run += TimeVal(0, 1000 * pause_ms * 2); // factor of 2 == slack if (t.expiry() <= next_run) { t.schedule_at(next_run); _pos->second->set_timer(t); } } _last_visited = _pos->second->net(); } template void RouteWalker::resume() { if (state() != STATE_PAUSED) return; _state = STATE_RUNNING; if (_last_visited == NO_NET) { _pos = _route_db.routes().end(); return; } _pos = _route_db.routes().find(_last_visited); if (_pos == _route_db.routes().end()) { // Node got deleted despite our pushing back it's timer (???) _pos = _route_db.routes().upper_bound(_last_visited); } } template void RouteWalker::reset() { _state = STATE_RUNNING; _pos = _route_db.routes().begin(); } template void RouteDB::push_routes() { debug_msg("[RIP] Push routes\n"); // // Push the original routes from all peers // for (typename set* >::iterator i = _peers.begin(); i != _peers.end(); ++i) { Peer* peer = *i; peer->push_routes(); } // XXX may have got RIB route adds because of delete_route // flush is probably not necessary... debug_msg("[RIP] Pushing the RIB routes we have\n"); for (typename RouteContainerNoRef::iterator i = _rib_routes.begin(); i != _rib_routes.end(); ++i) { Route* r = (*i).second; debug_msg("[RIP] Pushing RIB route %s\n", r->net().str().c_str()); XLOG_TRACE(trace()._routes, "Pushing RIB route %s\n", r->net().str().c_str()); update_route(r->net(), r->nexthop(), r->ifname(), r->vifname(), r->cost(), r->tag(), _rib_origin, r->policytags(), true); } } template void RouteDB::add_rib_route(const Net& net, const Addr& nexthop, const string& ifname, const string& vifname, uint32_t cost, uint32_t tag, RouteOrigin* origin, const PolicyTags& policytags) { debug_msg("[RIP] adding RIB route %s\n",net.str().c_str()); XLOG_TRACE(trace()._routes, "adding RIB route %s\n",net.str().c_str()); _rib_origin = origin; typename RouteContainerNoRef::iterator i = _rib_routes.find(net); if (i != _rib_routes.end()) { Route* prev = (*i).second; delete prev; } // // XXX: We are cheating here NULL origin so we don't get association. // RouteOrigin* no_origin = NULL; Route* r = new Route(net, nexthop, ifname, vifname, cost, no_origin, tag, policytags); _rib_routes[net] = r; } template void RouteDB::delete_rib_route(const Net& net) { debug_msg("[RIP] deleting RIB route %s\n",net.str().c_str()); XLOG_TRACE(trace()._routes, "deleting RIB route %s\n",net.str().c_str()); typename RouteContainerNoRef::iterator i = _rib_routes.find(net); if (i == _rib_routes.end()) return; // XXX: nothing to do Route* r = (*i).second; delete r; _rib_routes.erase(i); } // ---------------------------------------------------------------------------- // Instantiations #ifdef INSTANTIATE_IPV4 template class RouteDB; template class RouteWalker; #endif #ifdef INSTANTIATE_IPV6 template class RouteDB; template class RouteWalker; #endif xorp/rip/xrl_rib_notifier.hh0000664000076400007640000001071711540224235016337 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/rip/xrl_rib_notifier.hh,v 1.12 2008/10/02 21:58:19 bms Exp $ #ifndef __RIP_XRL_RIB_NOTIFIER_HH__ #define __RIP_XRL_RIB_NOTIFIER_HH__ #include "libxorp/ipnet.hh" #include "libxorp/service.hh" #include "rib_notifier_base.hh" class XrlSender; class XrlRouter; /** * @short Class to send RIP updates to RIB process. * * This class periodically checks the UpdateQueue for updates and * sends them to the RIB. Once an instance has been created, the * @ref startup() method should be called to register a routing table for * RIP and begin checking for updates. Before being destructed, * @ref shutdown() should be called to remove the RIP routing table from * the RIB. * * The XrlRibNotifier may be in one of several states enumerated by * @ref RunStatus. Before startup(), an instances state is SERVICE_READY. * Then when startup is called it transitions into * INSTALLING_RIP_TABLE and transitions into SERVICE_RUNNING. When in * SERVICE_RUNNING state updates are sent to the RIB. When shutdown() is * called it enters UNINSTALLING_RIP_TABLE before entering SERVICE_SHUTDOWN. * At any time it may fall into state SERVICE_FAILED if communication with * the RIB fails. */ template class XrlRibNotifier : public RibNotifierBase, public ServiceBase { public: typedef RibNotifierBase Super; static const uint32_t DEFAULT_INFLIGHT = 20; public: /** * Constructor. */ XrlRibNotifier(EventLoop& e, UpdateQueue& uq, XrlRouter& xr, uint32_t max_inflight = DEFAULT_INFLIGHT, uint32_t poll_ms = Super::DEFAULT_POLL_MS); /** * Constructor taking an XrlSender, a class name, and an instance * name as arguments. These arguments are broken out for * debugging instances, ie a fake XrlSender can be used to test * behaviour of this class. */ XrlRibNotifier(EventLoop& e, UpdateQueue& uq, XrlSender& xs, const string& class_name, const string& intance_name, uint32_t max_inflight = DEFAULT_INFLIGHT, uint32_t poll_ms = Super::DEFAULT_POLL_MS); ~XrlRibNotifier(); /** * Request RIB instantiates a RIP routing table and once instantiated * start passing route updates to RIB. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int startup(); /** * Stop forwarding route updates to RIB and request RIB * unregisters RIP routing table. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int shutdown(); /** * Accessor returning the current number of Xrls inflight. */ uint32_t xrls_inflight() const; /** * Accessor returning the maximum number of Xrls inflight. */ uint32_t max_xrls_inflight() const; protected: void updates_available(); private: void add_igp_cb(const XrlError& e); void delete_igp_cb(const XrlError& e); void send_add_route(const RouteEntry& re); void send_delete_route(const RouteEntry& re); void send_route_cb(const XrlError& e); void incr_inflight(); void decr_inflight(); protected: XrlSender& _xs; string _cname; string _iname; const uint32_t _max_inflight; uint32_t _inflight; set > _ribnets; // XXX hack }; // ---------------------------------------------------------------------------- // Public inline method definitions template inline uint32_t XrlRibNotifier::xrls_inflight() const { return _inflight; } template inline uint32_t XrlRibNotifier::max_xrls_inflight() const { return _max_inflight; } #endif // __RIP_XRL_RIB_NOTIFIER_HH__ xorp/rip/xrl_target_ripng.hh0000664000076400007640000002274511540225534016360 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __RIP_XRL_TARGET_RIPNG_HH__ #define __RIP_XRL_TARGET_RIPNG_HH__ #include "libxorp/status_codes.h" #include "xrl/targets/ripng_base.hh" class XrlRouter; class XrlProcessSpy; template class System; template class XrlPortManager; template class XrlRipCommonTarget; template class XrlRedistManager; class XrlRipngTarget : public XrlRipngTargetBase { public: XrlRipngTarget(EventLoop& e, XrlRouter& xr, XrlProcessSpy& xps, XrlPortManager& xpm, XrlRedistManager& xrm, System& rip_system); ~XrlRipngTarget(); void set_status(ProcessStatus ps, const string& annotation = ""); XrlCmdError common_0_1_get_target_name(string& name); XrlCmdError common_0_1_get_version(string& version); XrlCmdError common_0_1_get_status(uint32_t& status, string& reason); XrlCmdError common_0_1_shutdown(); XrlCmdError common_0_1_startup(); XrlCmdError finder_event_observer_0_1_xrl_target_birth(const string& class_name, const string& instance_name); XrlCmdError finder_event_observer_0_1_xrl_target_death(const string& class_name, const string& instance_name); XrlCmdError ripng_0_1_add_rip_address(const string& ifname, const string& vifname, const IPv6& addr); XrlCmdError ripng_0_1_remove_rip_address(const string& ifname, const string& vifname, const IPv6& addr); XrlCmdError ripng_0_1_set_rip_address_enabled(const string& ifname, const string& vifname, const IPv6& addr, const bool& enabled); XrlCmdError ripng_0_1_rip_address_enabled(const string& ifname, const string& vifname, const IPv6& addr, bool& enabled); XrlCmdError ripng_0_1_set_cost(const string& ifname, const string& vifname, const IPv6& addr, const uint32_t& cost); XrlCmdError ripng_0_1_cost(const string& ifname, const string& vifname, const IPv6& addr, uint32_t& cost); XrlCmdError ripng_0_1_set_horizon(const string& ifname, const string& vifname, const IPv6& addr, const string& horizon); XrlCmdError ripng_0_1_horizon(const string& ifname, const string& vifname, const IPv6& addr, string& horizon); XrlCmdError ripng_0_1_set_passive(const string& ifname, const string& vifname, const IPv6& addr, const bool& passive); XrlCmdError ripng_0_1_passive(const string& ifname, const string& vifname, const IPv6& addr, bool& passive); XrlCmdError ripng_0_1_set_accept_non_rip_requests(const string& ifname, const string& vifname, const IPv6& addr, const bool& accept); XrlCmdError ripng_0_1_accept_non_rip_requests(const string& ifname, const string& vifname, const IPv6& addr, bool& accept); XrlCmdError ripng_0_1_set_accept_default_route(const string& ifname, const string& vifname, const IPv6& addr, const bool& accept); XrlCmdError ripng_0_1_accept_default_route(const string& ifname, const string& vifname, const IPv6& addr, bool& accept); XrlCmdError ripng_0_1_set_advertise_default_route(const string& ifname, const string& vifname, const IPv6& addr, const bool& advertise); XrlCmdError ripng_0_1_advertise_default_route(const string& ifname, const string& vifname, const IPv6& addr, bool& advertise); XrlCmdError ripng_0_1_set_route_timeout(const string& ifname, const string& vifname, const IPv6& addr, const uint32_t& t_secs); XrlCmdError ripng_0_1_route_timeout(const string& ifname, const string& vifname, const IPv6& addr, uint32_t& t_secs); XrlCmdError ripng_0_1_set_deletion_delay(const string& ifname, const string& vifname, const IPv6& addr, const uint32_t& t_secs); XrlCmdError ripng_0_1_deletion_delay(const string& ifname, const string& vifname, const IPv6& addr, uint32_t& t_secs); XrlCmdError ripng_0_1_set_request_interval(const string& ifname, const string& vifname, const IPv6& addr, const uint32_t& t_secs); XrlCmdError ripng_0_1_request_interval(const string& ifname, const string& vifname, const IPv6& addr, uint32_t& t_secs); XrlCmdError ripng_0_1_set_update_interval(const string& ifname, const string& vifname, const IPv6& addr, const uint32_t& t_secs); XrlCmdError ripng_0_1_update_interval(const string& ifname, const string& vifname, const IPv6& addr, uint32_t& t_secs); XrlCmdError ripng_0_1_set_update_jitter(const string& ifname, const string& vifname, const IPv6& addr, const uint32_t& t_jitter); XrlCmdError ripng_0_1_update_jitter(const string& ifname, const string& vifname, const IPv6& addr, uint32_t& t_jitter); XrlCmdError ripng_0_1_set_triggered_update_delay(const string& ifname, const string& vifname, const IPv6& addr, const uint32_t& t_secs); XrlCmdError ripng_0_1_triggered_update_delay(const string& ifname, const string& vifname, const IPv6& addr, uint32_t& t_secs); XrlCmdError ripng_0_1_set_triggered_update_jitter(const string& ifname, const string& vifname, const IPv6& addr, const uint32_t& t_jitter); XrlCmdError ripng_0_1_triggered_update_jitter(const string& ifname, const string& vifname, const IPv6& addr, uint32_t& t_jitter); XrlCmdError ripng_0_1_set_interpacket_delay(const string& ifname, const string& vifname, const IPv6& addr, const uint32_t& t_msecs); XrlCmdError ripng_0_1_interpacket_delay(const string& ifname, const string& vifname, const IPv6& addr, uint32_t& t_msecs); XrlCmdError ripng_0_1_rip_address_status(const string& ifname, const string& vifname, const IPv6& addr, string& status); XrlCmdError ripng_0_1_get_all_addresses(XrlAtomList& ifnames, XrlAtomList& vifnames, XrlAtomList& addrs); XrlCmdError ripng_0_1_get_peers(const string& ifname, const string& vifname, const IPv6& addr, XrlAtomList& peers); XrlCmdError ripng_0_1_get_all_peers(XrlAtomList& peers, XrlAtomList& ifnames, XrlAtomList& vifnames, XrlAtomList& addrs); XrlCmdError ripng_0_1_get_counters(const string& ifname, const string& vifname, const IPv6& addr, XrlAtomList& descriptions, XrlAtomList& values); XrlCmdError ripng_0_1_get_peer_counters( const string& ifname, const string& vifname, const IPv6& addr, const IPv6& peer, XrlAtomList& descriptions, XrlAtomList& values, uint32_t& peer_last_active ); XrlCmdError ripng_0_1_trace(const string& tvar, const bool& enable); XrlCmdError socket6_user_0_1_recv_event(const string& sockid, const string& if_name, const string& vif_name, const IPv6& src_host, const uint32_t& src_port, const vector& pdata); XrlCmdError socket6_user_0_1_inbound_connect_event( const string& sockid, const IPv6& src_host, const uint32_t& src_port, const string& new_sockid, bool& accept); XrlCmdError socket6_user_0_1_outgoing_connect_event( const string& sockid); XrlCmdError socket6_user_0_1_error_event(const string& sockid, const string& reason, const bool& fatal); XrlCmdError socket6_user_0_1_disconnect_event(const string& sockid); XrlCmdError policy_backend_0_1_configure( // Input values, const uint32_t& filter, const string& conf); XrlCmdError policy_backend_0_1_reset( // Input values, const uint32_t& filter); XrlCmdError policy_backend_0_1_push_routes(); XrlCmdError policy_redist6_0_1_add_route6( // Input values, const IPv6Net& network, const bool& unicast, const bool& multicast, const IPv6& nexthop, const uint32_t& metric, const XrlAtomList& policytags); XrlCmdError policy_redist6_0_1_delete_route6( // Input values, const IPv6Net& network, const bool& unicast, const bool& multicast); protected: EventLoop& _e; XrlRipCommonTarget* _ct; }; #endif // __RIP_XRL_TARGET_RIPNG_HH__ xorp/rip/output.hh0000664000076400007640000001313211540225533014333 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/rip/output.hh,v 1.15 2008/10/02 21:58:16 bms Exp $ #ifndef __RIP_OUTPUT_HH__ #define __RIP_OUTPUT_HH__ // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/eventloop.hh" #include "policy/backend/policy_filters.hh" #include "port.hh" #include "system.hh" #include "rip_varrw.hh" template class PacketQueue; /** * @short Base class for RIP output processing components. * * RIP output processing components generate periodic trains of RIP * response packets. This class and it's children are intended to * generate the packet trains from RIP output processing. Each packet * within the train is temporally separated from it's neighbouring * packets. Timers elsewhere schedule the start of each packet train. */ template class OutputBase : public NONCOPYABLE { public: typedef A Addr; typedef IPNet Net; public: OutputBase(EventLoop& e, Port& port, PacketQueue& pkt_queue, const A& ip_addr, uint16_t ip_port); virtual ~OutputBase() {}; /** * Accessor for destination IP address applied to output packets. */ const A& ip_addr() const { return _ip_addr; } /** * Accessor for destination IP port applied to output packets. */ uint16_t ip_port() const { return _ip_port; } /** * @return true if output process is generating packets. */ bool running() const; /** * Start packet train if sufficient data is available. This instance * will remain in "running" so long as data is available and will * continue to generate packets until the data is exhausted. */ void start(); /** * Stop packet train. */ void stop(); /** * Get number of packets placed on packet queue for output. */ void packets_sent() const { return _pkts_out; } protected: /** * Accessor for the inter-packet gap the output process should when * generating packet trains. */ uint32_t interpacket_gap_ms() const; /** * Derived classes should implement this to start output processing. * It is invoked when start() is called. */ virtual void start_output_processing() = 0; /** * Derived classes should implement this to stop output processing. * It is invoked when stop() is called. */ virtual void stop_output_processing() = 0; /** * Output packet if suitable data is available, and place it in * the PacketQueue associated with this instance. Should data still be * available after packet is generated then implementations of this * method should reschedule a call to output_packet after * interpacket_gap_ms milliseconds. */ virtual void output_packet() = 0; void incr_packets_sent() { _pkts_out++; } /** * Policy filters the route. * * @param r route to filter. * @return true if the route was accepted, false otherwise. */ bool do_filtering(RouteEntry* r); protected: EventLoop& _e; Port& _port; // Port associated with output PacketQueue& _pkt_queue; // Place for generated packets to go const A _ip_addr; // IP address for output packets const uint16_t _ip_port; // IP port for output packets XorpTimer _op_timer; // Timer invoking output_packet() uint32_t _pkts_out; // Packets sent PolicyFilters& _policy_filters; // Global policy filters }; template OutputBase::OutputBase(EventLoop& e, Port& port, PacketQueue& pkt_queue, const A& ip_addr, uint16_t ip_port) : _e(e), _port(port), _pkt_queue(pkt_queue), _ip_addr(ip_addr), _ip_port(ip_port), _pkts_out(0), _policy_filters(port.port_manager().system().policy_filters()) { } template inline bool OutputBase::running() const { return _op_timer.scheduled(); } template inline void OutputBase::start() { if (running() == false) start_output_processing(); } template inline void OutputBase::stop() { stop_output_processing(); } template inline uint32_t OutputBase::interpacket_gap_ms() const { return _port.constants().interpacket_delay_ms(); } template bool OutputBase::do_filtering(RouteEntry* route) { try { RIPVarRW varrw(*route); debug_msg("[RIP] Running export filter on route: %s\n", route->net().str().c_str()); bool accepted = _policy_filters.run_filter(filter::EXPORT, varrw); return accepted; } catch(const PolicyException& e) { XLOG_FATAL("PolicyException: %s", e.str().c_str()); XLOG_UNFINISHED(); } } #endif // __RIP_OUTPUT_HH__ xorp/rip/xrl_target_common.hh0000664000076400007640000010543211540225534016524 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __RIP_XRL_TARGET_COMMON_HH__ #define __RIP_XRL_TARGET_COMMON_HH__ #include "libxorp/status_codes.h" #include "peer.hh" class XrlProcessSpy; template class XrlPortManager; template class System; template class XrlRedistManager; /** * @short Common handler for Xrl Requests. * * This class implements Xrl Target code that is common to both RIP * and RIP NG. */ template class XrlRipCommonTarget { public: XrlRipCommonTarget(XrlProcessSpy& xps, XrlPortManager& xpm, XrlRedistManager& xrm, System& rip_system); ~XrlRipCommonTarget(); void set_status(ProcessStatus ps, const string& annotation = ""); XrlCmdError common_0_1_get_status(uint32_t& status, string& reason); XrlCmdError common_0_1_shutdown(); XrlCmdError common_0_1_startup(); XrlCmdError finder_event_observer_0_1_xrl_target_birth(const string& class_name, const string& instance_name); XrlCmdError finder_event_observer_0_1_xrl_target_death(const string& class_name, const string& instance_name); XrlCmdError ripx_0_1_add_rip_address(const string& ifname, const string& vifname, const A& addr); XrlCmdError ripx_0_1_remove_rip_address(const string& ifname, const string& vifname, const A& addr); XrlCmdError ripx_0_1_set_rip_address_enabled(const string& ifname, const string& vifname, const A& addr, const bool& enabled); XrlCmdError ripx_0_1_rip_address_enabled(const string& ifname, const string& vifname, const A& addr, bool& enabled); XrlCmdError ripx_0_1_set_cost(const string& ifname, const string& vifname, const A& addr, const uint32_t& cost); XrlCmdError ripx_0_1_cost(const string& ifname, const string& vifname, const A& addr, uint32_t& cost); XrlCmdError ripx_0_1_set_horizon(const string& ifname, const string& vifname, const A& addr, const string& horizon); XrlCmdError ripx_0_1_horizon(const string& ifname, const string& vifname, const A& addr, string& horizon); XrlCmdError ripx_0_1_set_passive(const string& ifname, const string& vifname, const A& addr, const bool& passive); XrlCmdError ripx_0_1_passive(const string& ifname, const string& vifname, const A& addr, bool& passive); XrlCmdError ripx_0_1_set_accept_non_rip_requests(const string& ifname, const string& vifname, const A& addr, const bool& accept); XrlCmdError ripx_0_1_accept_non_rip_requests(const string& ifname, const string& vifname, const A& addr, bool& accept); XrlCmdError ripx_0_1_set_accept_default_route(const string& ifname, const string& vifname, const A& addr, const bool& accept); XrlCmdError ripx_0_1_accept_default_route(const string& ifname, const string& vifname, const A& addr, bool& accept); XrlCmdError ripx_0_1_set_advertise_default_route(const string& ifname, const string& vifname, const A& addr, const bool& advertise); XrlCmdError ripx_0_1_advertise_default_route(const string& ifname, const string& vifname, const A& addr, bool& advertise); XrlCmdError ripx_0_1_set_route_timeout(const string& ifname, const string& vifname, const A& addr, const uint32_t& t_secs); XrlCmdError ripx_0_1_route_timeout(const string& ifname, const string& vifname, const A& addr, uint32_t& t_secs); XrlCmdError ripx_0_1_set_deletion_delay(const string& ifname, const string& vifname, const A& addr, const uint32_t& t_secs); XrlCmdError ripx_0_1_deletion_delay(const string& ifname, const string& vifname, const A& addr, uint32_t& t_secs); XrlCmdError ripx_0_1_set_request_interval(const string& ifname, const string& vifname, const A& addr, const uint32_t& t_secs); XrlCmdError ripx_0_1_request_interval(const string& ifname, const string& vifname, const A& addr, uint32_t& t_secs); XrlCmdError ripx_0_1_set_update_interval(const string& ifname, const string& vifname, const A& addr, const uint32_t& t_secs); XrlCmdError ripx_0_1_update_interval(const string& ifname, const string& vifname, const A& addr, uint32_t& t_secs); XrlCmdError ripx_0_1_set_update_jitter(const string& ifname, const string& vifname, const A& addr, const uint32_t& t_jitter); XrlCmdError ripx_0_1_update_jitter(const string& ifname, const string& vifname, const A& addr, uint32_t& t_secs); XrlCmdError ripx_0_1_set_triggered_update_delay(const string& ifname, const string& vifname, const A& addr, const uint32_t& t_secs); XrlCmdError ripx_0_1_triggered_update_delay(const string& ifname, const string& vifname, const A& addr, uint32_t& t_secs); XrlCmdError ripx_0_1_set_triggered_update_jitter(const string& ifname, const string& vifname, const A& addr, const uint32_t& t_secs); XrlCmdError ripx_0_1_triggered_update_jitter(const string& ifname, const string& vifname, const A& addr, uint32_t& t_secs); XrlCmdError ripx_0_1_set_interpacket_delay(const string& ifname, const string& vifname, const A& addr, const uint32_t& t_msecs); XrlCmdError ripx_0_1_interpacket_delay(const string& ifname, const string& vifname, const A& addr, uint32_t& t_msecs); XrlCmdError ripx_0_1_rip_address_status(const string& ifname, const string& vifname, const A& addr, string& status); XrlCmdError ripx_0_1_get_all_addresses(XrlAtomList& ifnames, XrlAtomList& vifnames, XrlAtomList& addrs); XrlCmdError ripx_0_1_get_peers(const string& ifname, const string& vifname, const A& addr, XrlAtomList& peers); XrlCmdError ripx_0_1_get_all_peers(XrlAtomList& peers, XrlAtomList& ifnames, XrlAtomList& vifnames, XrlAtomList& addrs); XrlCmdError ripx_0_1_get_counters(const string& ifname, const string& vifname, const A& addr, XrlAtomList& descriptions, XrlAtomList& values); XrlCmdError ripx_0_1_get_peer_counters(const string& ifname, const string& vifname, const A& addr, const A& peer, XrlAtomList& descriptions, XrlAtomList& values, uint32_t& peer_last_pkt); XrlCmdError trace(bool enable); XrlCmdError socketx_user_0_1_recv_event(const string& sockid, const string& if_name, const string& vif_name, const A& src_host, const uint32_t& src_port, const vector& pdata); XrlCmdError socketx_user_0_1_inbound_connect_event( const string& sockid, const A& src_host, const uint32_t& src_port, const string& new_sockid, bool& accept); XrlCmdError socketx_user_0_1_outgoing_connect_event( const string& sockid); XrlCmdError socketx_user_0_1_error_event(const string& sockid, const string& reason, const bool& fatal); XrlCmdError socketx_user_0_1_disconnect_event(const string& sockid); XrlCmdError policy_backend_0_1_configure(const uint32_t& filter, const string& conf); XrlCmdError policy_backend_0_1_reset(const uint32_t& filter); XrlCmdError policy_backend_0_1_push_routes(); XrlCmdError policy_redistx_0_1_add_routex(const IPNet& net, const bool& unicast, const bool& multicast, const A& nexthop, const uint32_t& metric, const XrlAtomList& policytags); XrlCmdError policy_redistx_0_1_delete_routex(const IPNet& net, const bool& unicast, const bool& multicast); /** * Find Port associated with ifname, vifname, addr. * * @return on success the first item in the pair will be a * non-null pointer to the port and the second item with be * XrlCmdError::OKAY(). On failyre the first item in the pair * will be null and the XrlCmdError will signify the reason for * the failure. */ pair*,XrlCmdError> find_port(const string& ifname, const string& vifname, const A& addr); protected: XrlProcessSpy& _xps; XrlPortManager& _xpm; XrlRedistManager& _xrm; ProcessStatus _status; string _status_note; System& _rip_system; }; // ---------------------------------------------------------------------------- // Inline implementation of XrlRipCommonTarget template XrlRipCommonTarget::XrlRipCommonTarget(XrlProcessSpy& xps, XrlPortManager& xpm, XrlRedistManager& xrm, System& rip_system) : _xps(xps), _xpm(xpm), _xrm(xrm), _status(PROC_NULL), _status_note(""), _rip_system(rip_system) { } template XrlRipCommonTarget::~XrlRipCommonTarget() { } template void XrlRipCommonTarget::set_status(ProcessStatus status, const string& note) { debug_msg("Status Update %d -> %d: %s\n", _status, status, note.c_str()); _status = status; _status_note = note; } template XrlCmdError XrlRipCommonTarget::common_0_1_get_status(uint32_t& status, string& reason) { status = _status; reason = _status_note; return XrlCmdError::OKAY(); } template XrlCmdError XrlRipCommonTarget::common_0_1_shutdown() { debug_msg("Shutdown requested.\n"); xorp_do_run = 0; return XrlCmdError::OKAY(); } template XrlCmdError XrlRipCommonTarget::common_0_1_startup() { debug_msg("Startup requested.\n"); return XrlCmdError::OKAY(); } template XrlCmdError XrlRipCommonTarget::finder_event_observer_0_1_xrl_target_birth( const string& cname, const string& iname ) { _xps.birth_event(cname, iname); return XrlCmdError::OKAY(); } template XrlCmdError XrlRipCommonTarget::finder_event_observer_0_1_xrl_target_death( const string& cname, const string& iname ) { _xps.death_event(cname, iname); return XrlCmdError::OKAY(); } template XrlCmdError XrlRipCommonTarget::ripx_0_1_add_rip_address(const string& ifname, const string& vifname, const A& addr) { debug_msg("rip_x_1_add_rip_address %s/%s/%s\n", ifname.c_str(), vifname.c_str(), addr.str().c_str()); if (_xpm.add_rip_address(ifname, vifname, addr) == false) { return XrlCmdError::COMMAND_FAILED(); } return XrlCmdError::OKAY(); } template XrlCmdError XrlRipCommonTarget::ripx_0_1_remove_rip_address(const string& ifname, const string& vifname, const A& addr) { debug_msg("ripx_0_1_remove_rip_address %s/%s/%s\n", ifname.c_str(), vifname.c_str(), addr.str().c_str()); if (_xpm.remove_rip_address(ifname, vifname, addr) == false) { return XrlCmdError::COMMAND_FAILED(); } return XrlCmdError::OKAY(); } // ---------------------------------------------------------------------------- // Utility methods template pair*, XrlCmdError> XrlRipCommonTarget::find_port(const string& ifn, const string& vifn, const A& addr) { Port* p = _xpm.find_port(ifn, vifn, addr); if (p == 0) { string e = c_format("RIP not running on %s/%s/%s", ifn.c_str(), vifn.c_str(), addr.str().c_str()); return make_pair(p, XrlCmdError::COMMAND_FAILED(e)); } return make_pair(p, XrlCmdError::OKAY()); } // ---------------------------------------------------------------------------- // Port configuration accessors and modifiers template XrlCmdError XrlRipCommonTarget::ripx_0_1_set_rip_address_enabled(const string& ifn, const string& vifn, const A& addr, const bool& en) { pair*, XrlCmdError> pp = find_port(ifn, vifn, addr); if (pp.first == 0) return pp.second; Port* p = pp.first; p->set_enabled(en); return XrlCmdError::OKAY(); } template XrlCmdError XrlRipCommonTarget::ripx_0_1_rip_address_enabled(const string& ifn, const string& vifn, const A& addr, bool& en) { pair*, XrlCmdError> pp = find_port(ifn, vifn, addr); if (pp.first == 0) return pp.second; Port* p = pp.first; en = p->enabled(); return XrlCmdError::OKAY(); } template XrlCmdError XrlRipCommonTarget::ripx_0_1_set_cost(const string& ifname, const string& vifname, const A& addr, const uint32_t& cost) { pair*, XrlCmdError> pp = find_port(ifname, vifname, addr); if (pp.first == 0) return pp.second; Port* p = pp.first; if (cost > RIP_INFINITY) { string e = c_format("Cost must be less that RIP infinity (%u)", XORP_UINT_CAST(RIP_INFINITY)); return XrlCmdError::COMMAND_FAILED(e); } p->set_cost(cost); return XrlCmdError::OKAY(); } template XrlCmdError XrlRipCommonTarget::ripx_0_1_cost(const string& ifname, const string& vifname, const A& addr, uint32_t& cost) { pair*, XrlCmdError> pp = find_port(ifname, vifname, addr); if (pp.first == 0) return pp.second; Port* p = pp.first; cost = p->cost(); return XrlCmdError::OKAY(); } template XrlCmdError XrlRipCommonTarget::ripx_0_1_set_horizon(const string& ifname, const string& vifname, const A& addr, const string& horizon) { pair*, XrlCmdError> pp = find_port(ifname, vifname, addr); if (pp.first == 0) return pp.second; Port* p = pp.first; RipHorizon rh[3] = { NONE, SPLIT, SPLIT_POISON_REVERSE }; for (uint32_t i = 0; i < 3; ++i) { if (string(rip_horizon_name(rh[i])) == horizon) { p->set_horizon(rh[i]); return XrlCmdError::OKAY(); } } return XrlCmdError::COMMAND_FAILED( c_format("Horizon type \"%s\" not recognized.", horizon.c_str())); } template XrlCmdError XrlRipCommonTarget::ripx_0_1_horizon(const string& ifname, const string& vifname, const A& addr, string& horizon) { pair*, XrlCmdError> pp = find_port(ifname, vifname, addr); if (pp.first == 0) return pp.second; Port* p = pp.first; horizon = rip_horizon_name(p->horizon()); return XrlCmdError::OKAY(); } template XrlCmdError XrlRipCommonTarget::ripx_0_1_set_passive(const string& ifname, const string& vifname, const A& addr, const bool& passive) { pair*, XrlCmdError> pp = find_port(ifname, vifname, addr); if (pp.first == 0) return pp.second; Port* p = pp.first; p->set_passive(passive); return XrlCmdError::OKAY(); } template XrlCmdError XrlRipCommonTarget::ripx_0_1_passive(const string& ifname, const string& vifname, const A& addr, bool& passive) { pair*, XrlCmdError> pp = find_port(ifname, vifname, addr); if (pp.first == 0) return pp.second; Port* p = pp.first; passive = p->passive(); return XrlCmdError::OKAY(); } template XrlCmdError XrlRipCommonTarget::ripx_0_1_set_accept_non_rip_requests( const string& ifname, const string& vifname, const A& addr, const bool& accept ) { pair*, XrlCmdError> pp = find_port(ifname, vifname, addr); if (pp.first == 0) return pp.second; Port* p = pp.first; p->set_accept_non_rip_requests(accept); return XrlCmdError::OKAY(); } template XrlCmdError XrlRipCommonTarget::ripx_0_1_accept_non_rip_requests( const string& ifname, const string& vifname, const A& addr, bool& accept ) { pair*, XrlCmdError> pp = find_port(ifname, vifname, addr); if (pp.first == 0) return pp.second; Port* p = pp.first; accept = p->accept_non_rip_requests(); return XrlCmdError::OKAY(); } template XrlCmdError XrlRipCommonTarget::ripx_0_1_set_accept_default_route( const string& ifname, const string& vifname, const A& addr, const bool& accept ) { pair*, XrlCmdError> pp = find_port(ifname, vifname, addr); if (pp.first == 0) return pp.second; Port* p = pp.first; p->set_accept_default_route(accept); return XrlCmdError::OKAY(); } template XrlCmdError XrlRipCommonTarget::ripx_0_1_accept_default_route( const string& ifname, const string& vifname, const A& addr, bool& accept ) { pair*, XrlCmdError> pp = find_port(ifname, vifname, addr); if (pp.first == 0) return pp.second; Port* p = pp.first; accept = p->accept_default_route(); return XrlCmdError::OKAY(); } template XrlCmdError XrlRipCommonTarget::ripx_0_1_set_advertise_default_route( const string& ifname, const string& vifname, const A& addr, const bool& advertise ) { pair*, XrlCmdError> pp = find_port(ifname, vifname, addr); if (pp.first == 0) return pp.second; Port* p = pp.first; p->set_advertise_default_route(advertise); return XrlCmdError::OKAY(); } template XrlCmdError XrlRipCommonTarget::ripx_0_1_advertise_default_route( const string& ifname, const string& vifname, const A& addr, bool& advertise ) { pair*, XrlCmdError> pp = find_port(ifname, vifname, addr); if (pp.first == 0) return pp.second; Port* p = pp.first; advertise = p->advertise_default_route(); return XrlCmdError::OKAY(); } // ---------------------------------------------------------------------------- // The following pair of macros are used in setting timer constants on // RIP ports. #define PORT_TIMER_SET_HANDLER(field, t, min_val, max_val, method) \ do { \ pair*, XrlCmdError> pp = find_port(ifname, vifname, addr); \ if (pp.first == 0) \ return pp.second; \ Port* p = pp.first; \ \ if (static_cast(t) < min_val) \ return XrlCmdError::COMMAND_FAILED(c_format( \ "value supplied less than permitted minimum (%u < %u)", \ XORP_UINT_CAST(t), XORP_UINT_CAST(min_val))); \ if (t > max_val) \ return XrlCmdError::COMMAND_FAILED(c_format( \ "value supplied greater than permitted maximum (%u > %u)", \ XORP_UINT_CAST(t), XORP_UINT_CAST(max_val))); \ if (p->constants().set_##field (t) == false) \ return XrlCmdError::COMMAND_FAILED( \ "Failed to set value."); \ p->reschedule_##method (); \ return XrlCmdError::OKAY(); \ } while (0) #define PORT_TIMER_GET_HANDLER(field, t) \ do { \ pair*, XrlCmdError> pp = find_port(ifname, vifname, addr); \ if (pp.first == 0) \ return pp.second; \ Port* p = pp.first; \ t = p->constants(). field (); \ return XrlCmdError::OKAY(); \ } while (0) template XrlCmdError XrlRipCommonTarget::ripx_0_1_set_route_timeout( const string& ifname, const string& vifname, const A& addr, const uint32_t& t ) { PORT_TIMER_SET_HANDLER(expiry_secs, t, 10, 10000, dummy_timer); } template XrlCmdError XrlRipCommonTarget::ripx_0_1_route_timeout( const string& ifname, const string& vifname, const A& addr, uint32_t& t ) { PORT_TIMER_GET_HANDLER(expiry_secs, t); } template XrlCmdError XrlRipCommonTarget::ripx_0_1_set_deletion_delay( const string& ifname, const string& vifname, const A& addr, const uint32_t& t ) { PORT_TIMER_SET_HANDLER(deletion_secs, t, 10, 10000, dummy_timer); } template XrlCmdError XrlRipCommonTarget::ripx_0_1_deletion_delay( const string& ifname, const string& vifname, const A& addr, uint32_t& t ) { PORT_TIMER_GET_HANDLER(deletion_secs, t); } template XrlCmdError XrlRipCommonTarget::ripx_0_1_set_request_interval( const string& ifname, const string& vifname, const A& addr, const uint32_t& t ) { PORT_TIMER_SET_HANDLER(table_request_period_secs, t, 0, 10000, request_table_timer); } template XrlCmdError XrlRipCommonTarget::ripx_0_1_request_interval( const string& ifname, const string& vifname, const A& addr, uint32_t& t ) { PORT_TIMER_GET_HANDLER(table_request_period_secs, t); } template XrlCmdError XrlRipCommonTarget::ripx_0_1_set_update_interval( const string& ifname, const string& vifname, const A& addr, const uint32_t& t_secs ) { PORT_TIMER_SET_HANDLER(update_interval, t_secs, 1, 600, dummy_timer); } template XrlCmdError XrlRipCommonTarget::ripx_0_1_update_interval(const string& ifname, const string& vifname, const A& addr, uint32_t& t_secs) { PORT_TIMER_GET_HANDLER(update_interval, t_secs); } template XrlCmdError XrlRipCommonTarget::ripx_0_1_set_update_jitter(const string& ifname, const string& vifname, const A& addr, const uint32_t& t_jitter) { PORT_TIMER_SET_HANDLER(update_jitter, t_jitter, 0, 100, dummy_timer); } template XrlCmdError XrlRipCommonTarget::ripx_0_1_update_jitter(const string& ifname, const string& vifname, const A& addr, uint32_t& t_jitter) { PORT_TIMER_GET_HANDLER(update_jitter, t_jitter); } template XrlCmdError XrlRipCommonTarget::ripx_0_1_set_triggered_update_delay( const string& ifname, const string& vifname, const A& addr, const uint32_t& t_secs) { PORT_TIMER_SET_HANDLER(triggered_update_delay, t_secs, 1, 10000, dummy_timer); } template XrlCmdError XrlRipCommonTarget::ripx_0_1_triggered_update_delay( const string& ifname, const string& vifname, const A& addr, uint32_t& t_secs) { PORT_TIMER_GET_HANDLER(triggered_update_delay, t_secs); } template XrlCmdError XrlRipCommonTarget::ripx_0_1_set_triggered_update_jitter( const string& ifname, const string& vifname, const A& addr, const uint32_t& t_jitter) { PORT_TIMER_SET_HANDLER(triggered_update_jitter, t_jitter, 0, 100, dummy_timer); } template XrlCmdError XrlRipCommonTarget::ripx_0_1_triggered_update_jitter( const string& ifname, const string& vifname, const A& addr, uint32_t& t_jitter) { PORT_TIMER_GET_HANDLER(triggered_update_jitter, t_jitter); } template XrlCmdError XrlRipCommonTarget::ripx_0_1_set_interpacket_delay( const string& ifname, const string& vifname, const A& addr, const uint32_t& t_msecs ) { PORT_TIMER_SET_HANDLER(interpacket_delay_ms, t_msecs, 10, 10000, dummy_timer); } template XrlCmdError XrlRipCommonTarget::ripx_0_1_interpacket_delay( const string& ifname, const string& vifname, const A& addr, uint32_t& t_msecs ) { PORT_TIMER_GET_HANDLER(interpacket_delay_ms, t_msecs); } template XrlCmdError XrlRipCommonTarget::ripx_0_1_rip_address_status(const string& ifn, const string& vifn, const A& addr, string& status) { pair*, XrlCmdError> pp = find_port(ifn, vifn, addr); if (pp.first == 0) return pp.second; Port* p = pp.first; status = p->enabled() ? "enabled" : "disabled"; if (p->enabled() && _xpm.underlying_rip_address_up(ifn, vifn, addr) == false) { if (_xpm.underlying_rip_address_exists(ifn, vifn, addr)) { status += " [gated by disabled FEA interface/vif/address]"; } else { status += " [gated by non-existant FEA interface/vif/address]"; } } debug_msg("ripx_0_1_rip_address_status %s/%s/%s -> %s\n", ifn.c_str(), vifn.c_str(), addr.str().c_str(), status.c_str()); return XrlCmdError::OKAY(); } template XrlCmdError XrlRipCommonTarget::ripx_0_1_get_all_addresses(XrlAtomList& ifnames, XrlAtomList& vifnames, XrlAtomList& addrs) { const typename PortManagerBase::PortList & ports = _xpm.const_ports(); typename PortManagerBase::PortList::const_iterator pci; for (pci = ports.begin(); pci != ports.end(); ++pci) { const Port* port = *pci; const PortIOBase* pio = port->io_handler(); if (pio == 0) { continue; } ifnames.append ( XrlAtom(pio->ifname()) ); vifnames.append ( XrlAtom(pio->vifname()) ); addrs.append ( XrlAtom(pio->address()) ); } return XrlCmdError::OKAY(); } template XrlCmdError XrlRipCommonTarget::ripx_0_1_get_peers(const string& ifn, const string& vifn, const A& addr, XrlAtomList& peers) { pair*, XrlCmdError> pp = find_port(ifn, vifn, addr); if (pp.first == 0) return pp.second; Port* p = pp.first; typename Port::PeerList::const_iterator pi = p->peers().begin(); while (pi != p->peers().end()) { const Peer* peer = *pi; peers.append(XrlAtom(peer->address())); ++pi; } return XrlCmdError::OKAY(); } template XrlCmdError XrlRipCommonTarget::ripx_0_1_get_all_peers(XrlAtomList& peers, XrlAtomList& ifnames, XrlAtomList& vifnames, XrlAtomList& addrs) { const typename PortManagerBase::PortList & ports = _xpm.const_ports(); typename PortManagerBase::PortList::const_iterator pci; for (pci = ports.begin(); pci != ports.end(); ++pci) { const Port* port = *pci; const PortIOBase* pio = port->io_handler(); if (pio == 0) { continue; } typename Port::PeerList::const_iterator pi; for (pi = port->peers().begin(); pi != port->peers().end(); ++pi) { const Peer* peer = *pi; peers.append ( XrlAtom(peer->address()) ); ifnames.append ( XrlAtom(pio->ifname()) ); vifnames.append ( XrlAtom(pio->vifname()) ); addrs.append ( XrlAtom(pio->address()) ); } } return XrlCmdError::OKAY(); } template XrlCmdError XrlRipCommonTarget::ripx_0_1_get_counters(const string& ifn, const string& vifn, const A& addr, XrlAtomList& descriptions, XrlAtomList& values) { pair*, XrlCmdError> pp = find_port(ifn, vifn, addr); if (pp.first == 0) return pp.second; const Port* p = pp.first; descriptions.append(XrlAtom("", string("Requests Sent"))); values.append(XrlAtom(p->counters().table_requests_sent())); descriptions.append(XrlAtom("", string("Updates Sent"))); values.append(XrlAtom(p->counters().unsolicited_updates())); descriptions.append(XrlAtom("", string("Triggered Updates Sent"))); values.append(XrlAtom(p->counters().triggered_updates())); descriptions.append(XrlAtom("", string("Non-RIP Updates Sent"))); values.append(XrlAtom(p->counters().non_rip_updates_sent())); descriptions.append(XrlAtom("", string("Total Packets Received"))); values.append(XrlAtom(p->counters().packets_recv())); descriptions.append(XrlAtom("", string("Request Packets Received"))); values.append(XrlAtom(p->counters().table_requests_recv())); descriptions.append(XrlAtom("", string("Update Packets Received"))); values.append(XrlAtom(p->counters().update_packets_recv())); descriptions.append(XrlAtom("", string("Bad Packets Received"))); values.append(XrlAtom(p->counters().bad_packets())); if (A::ip_version() == 4) { descriptions.append(XrlAtom("", string("Authentication Failures"))); values.append(XrlAtom(p->counters().bad_auth_packets())); } descriptions.append(XrlAtom("", string("Bad Routes Received"))); values.append(XrlAtom(p->counters().bad_routes())); descriptions.append(XrlAtom("", string("Non-RIP Requests Received"))); values.append(XrlAtom(p->counters().non_rip_requests_recv())); return XrlCmdError::OKAY(); } template XrlCmdError XrlRipCommonTarget::ripx_0_1_get_peer_counters( const string& ifn, const string& vifn, const A& addr, const A& peer_addr, XrlAtomList& descriptions, XrlAtomList& values, uint32_t& peer_last_active) { pair*, XrlCmdError> pp = find_port(ifn, vifn, addr); if (pp.first == 0) return pp.second; const Port* port = pp.first; typename Port::PeerList::const_iterator pi; pi = find_if(port->peers().begin(), port->peers().end(), peer_has_address(peer_addr)); if (pi == port->peers().end()) { return XrlCmdError::COMMAND_FAILED( c_format("Peer %s not found on %s/%s/%s", peer_addr.str().c_str(), ifn.c_str(), vifn.c_str(), addr.str().c_str()) ); } const Peer* const peer = *pi; descriptions.append( XrlAtom("", string("Total Packets Received")) ); values.append( XrlAtom(peer->counters().packets_recv()) ); descriptions.append(XrlAtom("", string("Request Packets Received"))); values.append(XrlAtom(peer->counters().table_requests_recv())); descriptions.append(XrlAtom("", string("Update Packets Received"))); values.append(XrlAtom(peer->counters().update_packets_recv())); descriptions.append(XrlAtom("", string("Bad Packets Received"))); values.append(XrlAtom(peer->counters().bad_packets())); if (A::ip_version() == 4) { descriptions.append(XrlAtom("", string("Authentication Failures"))); values.append(XrlAtom(peer->counters().bad_auth_packets())); } descriptions.append(XrlAtom("", string("Bad Routes Received"))); values.append(XrlAtom(peer->counters().bad_routes())); descriptions.append(XrlAtom("", string("Routes Active"))); values.append(XrlAtom(peer->route_count())); peer_last_active = peer->last_active().sec(); return XrlCmdError::OKAY(); } template XrlCmdError XrlRipCommonTarget::trace(bool enable) { _rip_system.route_trace().all(enable); _xpm.packet_trace().all(enable); return XrlCmdError::OKAY(); } template XrlCmdError XrlRipCommonTarget::socketx_user_0_1_recv_event( const string& sockid, const string& if_name, const string& vif_name, const A& src_host, const uint32_t& src_port, const vector& pdata ) { _xpm.deliver_packet(sockid, if_name, vif_name, src_host, src_port, pdata); return XrlCmdError::OKAY(); } template XrlCmdError XrlRipCommonTarget::socketx_user_0_1_inbound_connect_event( const string& sockid, const A& src_host, const uint32_t& src_port, const string& new_sockid, bool& accept ) { debug_msg("socketx_user_0_1_inbound_connect_event %s %s/%u %s\n", sockid.c_str(), src_host.str().c_str(), XORP_UINT_CAST(src_port), new_sockid.c_str()); UNUSED(sockid); UNUSED(src_host); UNUSED(src_port); UNUSED(new_sockid); accept = false; return XrlCmdError::COMMAND_FAILED("Inbound connect not requested."); } template XrlCmdError XrlRipCommonTarget::socketx_user_0_1_outgoing_connect_event( const string& sockid ) { debug_msg("socketx_user_0_1_outgoing_connect_event %s\n", sockid.c_str()); UNUSED(sockid); return XrlCmdError::COMMAND_FAILED("Outgoing connect not requested."); } template XrlCmdError XrlRipCommonTarget::socketx_user_0_1_error_event( const string& sockid, const string& reason, const bool& fatal ) { debug_msg("socketx_user_0_1_error_event %s %s %s \n", sockid.c_str(), reason.c_str(), fatal ? "fatal" : "non-fatal"); UNUSED(sockid); UNUSED(reason); UNUSED(fatal); return XrlCmdError::OKAY(); } template XrlCmdError XrlRipCommonTarget::socketx_user_0_1_disconnect_event( const string& sockid ) { debug_msg("socketx_user_0_1_disconnect_event %s\n", sockid.c_str()); UNUSED(sockid); return XrlCmdError::OKAY(); } template XrlCmdError XrlRipCommonTarget::policy_backend_0_1_configure(const uint32_t& filter, const string& conf) { try { _rip_system.configure_filter(filter, conf); } catch(const PolicyException& e) { return XrlCmdError::COMMAND_FAILED("Filter configure failed: " + e.str()); } return XrlCmdError::OKAY(); } template XrlCmdError XrlRipCommonTarget::policy_backend_0_1_reset(const uint32_t& filter) { try { _rip_system.reset_filter(filter); } catch(const PolicyException& e) { return XrlCmdError::COMMAND_FAILED("Filter reset failed: " + e.str()); } return XrlCmdError::OKAY(); } template XrlCmdError XrlRipCommonTarget::policy_backend_0_1_push_routes() { _rip_system.push_routes(); return XrlCmdError::OKAY(); } template XrlCmdError XrlRipCommonTarget::policy_redistx_0_1_add_routex(const IPNet& net, const bool& unicast, const bool& multicast, const A& nexthop, const uint32_t& metric, const XrlAtomList& policytags) { string ifname, vifname; UNUSED(multicast); if (! unicast) return XrlCmdError::OKAY(); // // XXX: The interface and vif name are empty, because the policy // mechanism doesn't support setting them (yet). // _xrm.add_route(net, nexthop, ifname, vifname, metric, 0, policytags); return XrlCmdError::OKAY(); } template XrlCmdError XrlRipCommonTarget::policy_redistx_0_1_delete_routex(const IPNet& net, const bool& unicast, const bool& multicast) { UNUSED(multicast); if (! unicast) return XrlCmdError::OKAY(); _xrm.delete_route(net); return XrlCmdError::OKAY(); } #endif // __RIPX_XRL_TARGET_COMMON_HH__ xorp/rip/SConscript0000664000076400007640000001727111631510076014474 0ustar greearbgreearb# Copyright (c) 2009-2011 XORP, Inc and Others # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # $XORP$ import os Import('env') subdirs = [ 'tests', 'tools', ] SConscript(dirs = subdirs, exports='env') e = env.Clone() is_shared = e.has_key('SHAREDLIBS') # # NB: The same sources are used for RIP and RIPng. We use separate # scons environments with different CPPDEFINES and SHOBJPREFIXES to # support this. # # RIP/RIPng Library Sources. libxorp_rip_srcs = [ 'auth.cc', 'output_table.cc', 'output_updates.cc', 'packet_queue.cc', 'peer.cc', 'port.cc', 'redist.cc', 'rib_notifier_base.cc', 'rip_varrw.cc', 'route_db.cc', 'route_entry.cc', 'update_queue.cc' ] # RIP/RIPng XRL Library Sources. libxorp_rip_xrl_srcs = [ 'xrl_config.cc', 'xrl_port_manager.cc', 'xrl_port_io.cc', 'xrl_process_spy.cc', 'xrl_redist_manager.cc', 'xrl_rib_notifier.cc' ] # RIP Main/Driver Sources ripsrcs = [ 'xorp_rip_main.cc', 'xrl_target_rip.cc' ] # RIPng Main/Driver Sources ripngsrcs = [ 'xorp_rip_main.cc', 'xrl_target_ripng.cc' ] # # RIP # e['OBJPREFIX'] = 'rip-' e['SHOBJPREFIX'] = 'rip-' e.AppendUnique(CPPPATH = [ '#', '$BUILDDIR' ]) e.AppendUnique(CPPDEFINES = [ 'INSTANTIATE_IPV4' ]) e.PrependUnique(LIBPATH = [ '.', '$BUILDDIR/policy/backend', '$BUILDDIR/policy/common', '$BUILDDIR/xrl/interfaces', '$BUILDDIR/xrl/targets', '$BUILDDIR/libfeaclient', '$BUILDDIR/libproto', '$BUILDDIR/libxipc', '$BUILDDIR/libcomm', '$BUILDDIR/libxorp', ]) e.PrependUnique(LIBS = [ 'xorp_rip_xrl', 'xorp_rip', # Refers to library, not the executable. 'xst_rip', 'xif_rib', 'xif_finder_event_notifier', 'xorp_fea_client', 'xif_fea_ifmgr_mirror', 'xif_fea_ifmgr_replicator', 'xst_fea_ifmgr_mirror', 'xif_socket4', 'xorp_policy_backend', 'xorp_policy_common', 'xorp_proto', 'xorp_ipc', 'xorp_comm', 'xorp_core', ]) if not is_shared: e.AppendUnique(LIBS = [ "crypto", ]) if not (e.has_key('mingw') and e['mingw']): e.AppendUnique(LIBS = [ "rt", ]) if (e.has_key('mingw') and e['mingw']): e.AppendUnique(LIBS = [ 'iphlpapi', # 'mprapi', 'regex', ]) e.Append(LIBS = ['xorp_core', 'ws2_32', 'winmm']) e.Replace(RPATH = [ e.Literal(e['xorp_module_rpath']) ]) if is_shared: libxorp_rip = e.SharedLibrary(target = 'libxorp_rip', source = libxorp_rip_srcs, LIBS = '') if e['rtld_origin']: for obj in libxorp_rip: e.AddPostAction(libxorp_rip, e.Symlink(obj.abspath, os.path.join(env['xorp_alias_libdir'], str(obj)))) libxorp_rip_xrl = e.SharedLibrary(target = 'libxorp_rip_xrl', source = libxorp_rip_xrl_srcs, LIBS = '') if e['rtld_origin']: for obj in libxorp_rip_xrl: e.AddPostAction(libxorp_rip_xrl, e.Symlink(obj.abspath, os.path.join(env['xorp_alias_libdir'], str(obj)))) e.Alias('install', [ e.InstallLibrary(e['xorp_libdir'], libxorp_rip), e.InstallLibrary(e['xorp_libdir'], libxorp_rip_xrl) ]) else: libxorp_rip = e.StaticLibrary(target = 'libxorp_rip', source = libxorp_rip_srcs, LIBS = '') libxorp_rip_xrl = e.StaticLibrary(target = 'libxorp_rip_xrl', source = libxorp_rip_xrl_srcs, LIBS = '') rip = e.Program(target = 'xorp_rip', source = ripsrcs) e.Alias('install', e.InstallProgram(e['xorp_moduledir'], rip)) if not (env.has_key('disable_ipv6') and env['disable_ipv6']): # # RIPng # e = env.Clone() e['OBJPREFIX'] = 'ripng-' e['SHOBJPREFIX'] = 'ripng-' e.AppendUnique(CPPPATH = [ '#', '$BUILDDIR', ]) if not (env.has_key('disable_ipv6') and env['disable_ipv6']): e.AppendUnique(CPPDEFINES = [ 'INSTANTIATE_IPV6' ]) e.PrependUnique(LIBPATH = [ '$BUILDDIR/libfeaclient', '$BUILDDIR/policy/backend', '$BUILDDIR/policy/common', '$BUILDDIR/xrl/interfaces', '$BUILDDIR/xrl/targets', '.', '$BUILDDIR/libproto', '$BUILDDIR/libxipc', '$BUILDDIR/libcomm', '$BUILDDIR/libxorp', ]) e.PrependUnique(LIBS = [ 'xorp_ripng_xrl', 'xst_ripng', 'xif_rib', 'xif_finder_event_notifier', 'xorp_fea_client', 'xif_fea_ifmgr_mirror', 'xif_fea_ifmgr_replicator', 'xst_fea_ifmgr_mirror', 'xorp_policy_backend', 'xorp_policy_common', 'xorp_proto', 'xorp_ipc', 'xorp_comm', 'xorp_ripng', 'xif_socket6', 'xorp_core', ]) if not is_shared: e.AppendUnique(LIBS = [ "crypto", ]) if not (e.has_key('mingw') and e['mingw']): e.AppendUnique(LIBS = [ "rt", ]) if (e.has_key('mingw') and e['mingw']): e.AppendUnique(LIBS = [ 'iphlpapi', 'regex', ]) e.Append(LIBS = ['xorp_core', 'ws2_32', 'winmm']) e.Append(LIBS = [ 'xorp_policy_backend', ]) e.Replace(RPATH = [ e.Literal(e['xorp_module_rpath']) ]) if is_shared: libxorp_ripng = e.SharedLibrary(target = 'libxorp_ripng', source = libxorp_rip_srcs, LIBS = '') if e['rtld_origin']: for obj in libxorp_ripng: e.AddPostAction(libxorp_ripng, e.Symlink(obj.abspath, os.path.join(env['xorp_alias_libdir'], str(obj)))) libxorp_ripngxrl = e.SharedLibrary(target = 'libxorp_ripng_xrl', source = libxorp_rip_xrl_srcs, LIBS = '') if e['rtld_origin']: for obj in libxorp_ripngxrl: e.AddPostAction(libxorp_ripngxrl, e.Symlink(obj.abspath, os.path.join(env['xorp_alias_libdir'], str(obj)))) e.Alias('install', [ e.InstallLibrary(e['xorp_libdir'], libxorp_ripng), e.InstallLibrary(e['xorp_libdir'], libxorp_ripngxrl) ]) else: libxorp_ripng = e.StaticLibrary(target = 'libxorp_ripng', source = libxorp_rip_srcs, LIBS = '') libxorp_ripngxrl = e.StaticLibrary(target = 'libxorp_ripng_xrl', source = libxorp_rip_xrl_srcs, LIBS = '') ripng = e.Program(target = 'xorp_ripng', source = ripngsrcs) e.Alias('install', e.InstallProgram(e['xorp_moduledir'], ripng)) Default(rip, ripng) else: Default(rip) xorp/rip/rip_varrw.hh0000664000076400007640000000415411421137511015006 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/rip/rip_varrw.hh,v 1.13 2008/10/02 21:58:17 bms Exp $ #ifndef __RIP_RIP_VARRW_HH__ #define __RIP_RIP_VARRW_HH__ #include "policy/backend/single_varrw.hh" #include "policy/common/element_factory.hh" #include "route_entry.hh" /** * @short Enables reading and writing variables of RIP routes. */ template class RIPVarRW : public SingleVarRW { public: enum { VAR_NETWORK4 = VAR_PROTOCOL, VAR_NEXTHOP4, VAR_NETWORK6, VAR_NEXTHOP6, VAR_METRIC }; /** * @param route route to read/write values from. */ RIPVarRW(RouteEntry& route); // SingleVarRW interface void start_read(); Element* single_read(const Id& id); void single_write(const Id& id, const Element& e); private: /** * @param route route to read nexthop and network from. */ void read_route_nexthop(RouteEntry& route); /** * Specialized template method to write correct nexthop value based on * address family of route. * * @param id variable to write. * @param e value of variable. * @return true if write was performed, false otherwise. */ bool write_nexthop(const Id& id, const Element& e); RouteEntry& _route; }; #endif // __RIP_RIP_VARRW_HH__ xorp/rip/update_queue.cc0000664000076400007640000002367011540224235015455 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "rip_module.h" #include "libxorp/debug.h" #include "libxorp/ipv4.hh" #include "libxorp/ipv6.hh" #include "libxorp/xlog.h" #include "update_queue.hh" /** * @short Store for a fixed block of update. * * Stores a group of updates in a block. They are stored like this * for two reasons. Firstly, we can track readers entering and * leaving blocks which makes garbage collection decisions easy. * Secondly, it involves minimal copying of ref_ptrs (compared to say * a scrolling vector of ref_ptrs). */ template struct UpdateBlock { public: typedef typename UpdateQueue::RouteUpdate RouteUpdate; static const size_t MAX_UPDATES = 100; public: UpdateBlock() : _updates(MAX_UPDATES), _update_cnt(0), _refs(0) {} ~UpdateBlock() { XLOG_ASSERT(_refs == 0); } bool full() const { return _update_cnt == MAX_UPDATES; } bool empty() const { return _update_cnt == 0; } size_t count() const { return _update_cnt; } bool add_update(const RouteUpdate& u) { XLOG_ASSERT(u.get() != 0); if ( full() ) { XLOG_WARNING("Attempting to add update to full block"); return false; } _updates[_update_cnt++] = u; return true; } const RouteUpdate& get(uint32_t pos) const { XLOG_ASSERT(pos < MAX_UPDATES); return _updates[pos]; } void ref() { _refs++; } void unref() { XLOG_ASSERT( _refs > 0 ); _refs--; } uint32_t ref_cnt() const { return _refs; } private: vector _updates; size_t _update_cnt; uint32_t _refs; }; // ---------------------------------------------------------------------------- // UpdateQueueImpl /** * @short Internal implementation of UpdateQueue class. */ template class UpdateQueueImpl { private: typedef typename UpdateQueue::RouteUpdate RouteUpdate; typedef list > UpdateBlockList; /** * @short State associated with an UpdateQueue reader. */ struct ReaderPos { typename UpdateBlockList::iterator _bi; uint32_t _pos; ReaderPos(const typename UpdateBlockList::iterator& bi, uint32_t pos) : _bi(bi), _pos(pos) { _bi->ref(); } ~ReaderPos() { _bi->unref(); } typename UpdateBlockList::const_iterator block() const { return _bi; } uint32_t position() const { return _pos; } void advance_position() { if (_pos < _bi->count()) _pos++; } void advance_block() { _bi->unref(); _bi++; _bi->ref(); _pos = 0; } void move_to(const typename UpdateBlockList::iterator& bi, uint32_t pos) { _bi->unref(); _bi = bi; _bi->ref(); _pos = pos; } }; private: UpdateBlockList _update_blocks; vector _readers; uint32_t _num_readers; public: UpdateQueueImpl() : _num_readers(0) { _update_blocks.push_back(UpdateBlock()); } ~UpdateQueueImpl() { } /** * Create state for a new reader. * @return id for reader. */ uint32_t add_reader() { typename UpdateBlockList::iterator lb = --_update_blocks.end(); ReaderPos* new_reader = new ReaderPos(lb, lb->count()); _num_readers++; for (uint32_t i = 0; i < _readers.size(); ++i) { if (_readers[i] == 0) { _readers[i] = new_reader; return i; } } _readers.push_back(new_reader); return _readers.size() - 1; } /** * Destroy state for reader. * @param id unique id of reader. */ void remove_reader(uint32_t id) { if (id < _readers.size() && _readers[id] != 0) { delete _readers[id]; _readers[id] = 0; _num_readers--; if (_num_readers == 0 && _update_blocks.back().empty() == false) { _update_blocks.push_back(UpdateBlock()); } garbage_collect(); } } /** * Remove blocks at front of queue who have no referrers. */ void garbage_collect() { typename UpdateBlockList::iterator last = --_update_blocks.end(); while (_update_blocks.begin() != last && _update_blocks.front().ref_cnt() == 0) { _update_blocks.erase(_update_blocks.begin()); } } /** * Advance position of reader by 1 update. * @param id unique id of reader. * @return true if reader advanced, false if reader has reached end. */ bool advance_reader(uint32_t id) { XLOG_ASSERT(id < _readers.size()); XLOG_ASSERT(_readers[id] != 0); ReaderPos* rp = _readers[id]; if (rp->position() != rp->block()->count()) { debug_msg("Advancing position\n"); rp->advance_position(); } if (rp->position() == rp->block()->count() && rp->block()->count() != 0) { if (rp->block() == --_update_blocks.end()) { _update_blocks.push_back(UpdateBlock()); } debug_msg("Advancing block\n"); rp->advance_block(); garbage_collect(); } return true; } /** * Fast forward reader to end of updates. * @param id unique id of reader. */ void ffwd_reader(uint32_t id) { XLOG_ASSERT(id < _readers.size()); XLOG_ASSERT(_readers[id] != 0); typename UpdateBlockList::iterator bi = --_update_blocks.end(); _readers[id]->move_to(bi, bi->count()); advance_reader(id); garbage_collect(); } void rwd_reader(uint32_t id) { XLOG_ASSERT(id < _readers.size()); XLOG_ASSERT(_readers[id] != 0); _readers[id]->move_to(_update_blocks.begin(), 0); } /** * Fast forward all readers to end of updates. */ void flush() { _update_blocks.push_back(UpdateBlock()); for (size_t i = 0; i < _readers.size(); i++) { if (_readers[i] != 0) ffwd_reader(i); } } /** * Get data associated with reader. */ const RouteEntry* read(uint32_t id) { XLOG_ASSERT(id < _readers.size()); XLOG_ASSERT(_readers[id] != 0); ReaderPos* rp = _readers[id]; debug_msg("Reading from %u %u/%u\n", XORP_UINT_CAST(id), XORP_UINT_CAST(rp->position()), XORP_UINT_CAST(rp->block()->count())); // Reader may have reached end of last block and stopped, but // more data may now have been added. if (rp->position() == rp->block()->count()) { advance_reader(id); } if (rp->position() < rp->block()->count()) { const RouteUpdate& u = rp->block()->get(rp->position()); debug_msg("Got route\n"); return u.get(); } debug_msg("Nothing available\n"); return 0; } bool push_back(const RouteUpdate& u) { if (_num_readers == 0) { return true; } if (_update_blocks.back().full() == true) { _update_blocks.push_back(UpdateBlock()); } return _update_blocks.back().add_update(u); } uint32_t updates_queued() const { typename UpdateBlockList::const_iterator ci = _update_blocks.begin(); uint32_t total = 0; while (ci != _update_blocks.end()) { total += ci->count(); ++ci; } return total; } }; /* ------------------------------------------------------------------------- */ /* UpdateQueueReader methods */ template UpdateQueueReader::UpdateQueueReader(UpdateQueueImpl* impl) : _impl(impl) { _id = _impl->add_reader(); } template UpdateQueueReader::~UpdateQueueReader() { _impl->remove_reader(_id); } template inline uint32_t UpdateQueueReader::id() const { return _id; } template bool UpdateQueueReader::parent_is(const UpdateQueueImpl* o) const { return _impl == o; } /* ------------------------------------------------------------------------- */ /* UpdateQueue methods */ template UpdateQueue::UpdateQueue() { _impl = new UpdateQueueImpl(); } template UpdateQueue::~UpdateQueue() { delete _impl; } template typename UpdateQueue::ReadIterator UpdateQueue::create_reader() { Reader* r = new UpdateQueueReader(_impl); return ReadIterator(r); } template void UpdateQueue::destroy_reader(ReadIterator& r) { r = 0; } template bool UpdateQueue::reader_valid(const ReadIterator& r) { if (r.get() == 0) return false; return r->parent_is(this->_impl); } template const RouteEntry* UpdateQueue::get(ReadIterator& r) const { return _impl->read(r->id()); } template const RouteEntry* UpdateQueue::next(ReadIterator& r) { if (_impl->advance_reader(r->id())) { return get(r); } return 0; } template void UpdateQueue::ffwd(ReadIterator& r) { _impl->ffwd_reader(r->id()); } template void UpdateQueue::rwd(ReadIterator& r) { _impl->rwd_reader(r->id()); } template void UpdateQueue::flush() { _impl->flush(); } template void UpdateQueue::push_back(const RouteUpdate& u) { _impl->push_back(u); } template uint32_t UpdateQueue::updates_queued() const { return _impl->updates_queued(); } // ---------------------------------------------------------------------------- // Instantiations #ifdef INSTANTIATE_IPV4 template class UpdateQueue; template class UpdateQueueReader; #endif #ifdef INSTANTIATE_IPV6 template class UpdateQueue; template class UpdateQueueReader; #endif xorp/rip/rib_notifier_base.hh0000664000076400007640000000421111421137511016432 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/rip/rib_notifier_base.hh,v 1.8 2008/10/02 21:58:17 bms Exp $ #ifndef __RIP_RIB_NOTIFIER_BASE_HH__ #define __RIP_RIB_NOTIFIER_BASE_HH__ #include "libxorp/eventloop.hh" #include "update_queue.hh" class EventLoop; /** * @short Base class for RIB notificatiers. * * This class acts as a base class for RIB notifiers. RIB notifiers * are classes that convey RIP routes to the RIB. The base class * implements update queue polling. When polling is enabled, it polls * the UpdateQueue periodically for updates to be send to the RIB. If * updates are available available it calls @ref updates_available() * which derived classes implement according to their needs. */ template class RibNotifierBase { public: static const uint32_t DEFAULT_POLL_MS = 1000; public: RibNotifierBase(EventLoop& eventloop, UpdateQueue& update_queue, uint32_t poll_ms = DEFAULT_POLL_MS); virtual ~RibNotifierBase(); protected: virtual void updates_available() = 0; protected: void start_polling(); void stop_polling(); bool poll_updates(); protected: EventLoop& _e; UpdateQueue& _uq; typename UpdateQueue::ReadIterator _ri; uint32_t _poll_ms; XorpTimer _t; }; #endif // __RIP_RIB_NOTIFIER_BASE_HH__ xorp/rip/port.cc0000664000076400007640000007722411540225534013762 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "rip_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/random.h" #include "libxorp/eventloop.hh" #include "libxorp/ipv4.hh" #include "libxorp/ipv6.hh" #include "constants.hh" #include "packets.hh" #include "auth.hh" #include "peer.hh" #include "port.hh" #include "port_manager.hh" #include "packet_assembly.hh" #include "packet_queue.hh" #include "system.hh" #include "output_table.hh" #include "output_updates.hh" // ---------------------------------------------------------------------------- // Utilities inline static uint32_t range_random(uint32_t lo, uint32_t hi) { if (hi < lo) swap(hi, lo); return lo + ( xorp_random() % (hi - lo) ); } // ---------------------------------------------------------------------------- // Address Family specific Port methods #ifdef INSTANTIATE_IPV4 PortAFSpecState::PortAFSpecState() { set_auth_handler(new NullAuthHandler()); } PortAFSpecState::~PortAFSpecState() { delete auth_handler(); } AuthHandlerBase* PortAFSpecState::set_auth_handler(AuthHandlerBase* new_handler) { AuthHandlerBase* old_handler = _ah; _ah = new_handler; return old_handler; } const AuthHandlerBase* PortAFSpecState::auth_handler() const { return _ah; } AuthHandlerBase* PortAFSpecState::auth_handler() { return _ah; } #endif // INSTANTIATE_IPV4 // ---------------------------------------------------------------------------- // Generic Port Implementation template Port::Port(PortManagerBase& pm) : _pm(pm), _en(false), _cost(1), _horizon(SPLIT_POISON_REVERSE), _advertise(false), _adv_def_rt(true), _acc_def_rt(true), _passive(false), _acc_non_rip_reqs(true), _ur_out(0), _tu_out(0), _su_out(0) { _packet_queue = new PacketQueue(); } template Port::~Port() { stop_output_processing(); delete _ur_out; delete _su_out; delete _tu_out; while (_peers.empty() == false) { delete _peers.front(); _peers.pop_front(); } delete _packet_queue; } template Peer* Port::create_peer(const Addr& addr) { if (peer(addr) == 0) { Peer* peer = new Peer(*this, addr); _peers.push_back(peer); EventLoop& e = _pm.eventloop(); TimeVal now; e.current_time(now); peer->set_last_active(now); start_peer_gc_timer(); return peer; } return 0; } template Peer* Port::peer(const Addr& addr) { typename PeerList::iterator i = find_if(_peers.begin(), _peers.end(), peer_has_address(addr)); return (i == _peers.end()) ? 0 : *i; } template const Peer* Port::peer(const Addr& addr) const { typename PeerList::const_iterator i = find_if(_peers.begin(), _peers.end(), peer_has_address(addr)); return (i == _peers.end()) ? 0 : *i; } template void Port::unsolicited_response_timeout() { debug_msg("Unsolicited response timeout %p\n", this); // // Fast forward triggered updater because we're about to dump entire // table. // if (_tu_out->running()) { _tu_out->ffwd(); } // // Check if unsolicited response process already exists and kill // it if so. // if (_ur_out->running()) { XLOG_WARNING("Starting unsolicited response process while an " "existing one is already running.\n"); _ur_out->stop(); } // Start output process. _ur_out->start(); // // Reschedule this callback in next interval // TimeVal interval = TimeVal(constants().update_interval(), 0); double factor = constants().update_jitter() / 100.0; _ur_timer.reschedule_after(random_uniform(interval, factor)); } template void Port::triggered_update_timeout() { debug_msg("Triggered update timeout %p\n", this); { RouteDB& rdb = _pm.system().route_db(); UNUSED(rdb); debug_msg("- Route DB routes = %u\n", XORP_UINT_CAST(rdb.route_count())); } // Table dump is running, we should not be doing triggered updates. if (_ur_out->running()) goto reschedule; // // Push triggered updater along. It wont be running if we've just // instantiated it, or if it was running and ran out of updates to // announce. // if (_tu_out->running() == false) { _tu_out->start(); } reschedule: TimeVal delay = TimeVal(constants().triggered_update_delay(), 0); double factor = constants().triggered_update_jitter() / 100.0; _tu_timer.reschedule_after(random_uniform(delay, factor)); } template void Port::start_output_processing() { EventLoop& e = _pm.eventloop(); RouteDB& rdb = _pm.system().route_db(); // Create triggered update output process _tu_out = new OutputUpdates(e, *this, *_packet_queue, rdb); // Schedule triggered update process TimeVal interval = TimeVal(constants().update_interval(), 0); double factor = constants().update_jitter() / 100.0; _ur_timer = e.new_oneoff_after(random_uniform(interval, factor), callback(this, &Port::unsolicited_response_timeout)); // Create unsolicited response (table dump) output process _ur_out = new OutputTable(e, *this, *_packet_queue, rdb); // Schedule unsolicited output process TimeVal delay = TimeVal(constants().triggered_update_delay(), 0); factor = constants().triggered_update_jitter() / 100.0; _tu_timer = e.new_oneoff_after(random_uniform(delay, factor), callback(this, &Port::triggered_update_timeout)); } template void Port::stop_output_processing() { delete _ur_out; _ur_out = 0; delete _tu_out; _tu_out = 0; _ur_timer.unschedule(); _tu_timer.unschedule(); } template void Port::start_request_table_timer() { EventLoop& e = _pm.eventloop(); if (constants().table_request_period_secs() == 0) { // Don't start the timer, but cancel it instead _rt_timer.unschedule(); return; } _rt_timer = e.new_periodic_ms( constants().table_request_period_secs() * 1000, callback(this, &Port::request_table_timeout)); } template void Port::reschedule_request_table_timer() { if (! _rt_timer.scheduled()) return; start_request_table_timer(); } template void Port::stop_request_table_timer() { _rt_timer.unschedule(); } template bool Port::request_table() { RipPacket* pkt = new RipPacket(RIP_AF_CONSTANTS::IP_GROUP(), RIP_AF_CONSTANTS::IP_PORT); list*> auth_packets; RequestTablePacketAssembler rtpa(*this); if (rtpa.prepare(pkt, auth_packets) == true) { typename list*>::iterator iter; for (iter = auth_packets.begin(); iter != auth_packets.end(); ++iter) { RipPacket* auth_pkt = *iter; _packet_queue->enqueue_packet(auth_pkt); counters().incr_table_requests_sent(); } } else { XLOG_ERROR("Failed to assemble table request.\n"); } delete pkt; push_packets(); debug_msg("Sending Request.\n"); return true; } template bool Port::request_table_timeout() { if (_peers.empty() == false) return false; return (request_table()); } template void Port::start_peer_gc_timer() { XLOG_ASSERT(_peers.empty() == false); // Set peer garbage collection timeout to 180 seconds since for RIP // MIB we need to keep track of quiescent peers for this long. EventLoop& e = _pm.eventloop(); _gc_timer = e.new_periodic_ms(180 * 1000, callback(this, &Port::peer_gc_timeout)); } template bool Port::peer_gc_timeout() { typename PeerList::iterator i = _peers.begin(); while (i != _peers.end()) { Peer* pp = *i; if (pp->route_count() == 0) { delete pp; _peers.erase(i++); } else { ++i; } } if (_peers.empty()) { start_request_table_timer(); return false; } return true; } template void Port::record_packet(Peer* p) { counters().incr_packets_recv(); if (p) { EventLoop& e = _pm.eventloop(); TimeVal now; e.current_time(now); p->counters().incr_packets_recv(); p->set_last_active(now); } } template void Port::record_response_packet(Peer* p) { counters().incr_update_packets_recv(); if (p) { p->counters().incr_update_packets_recv(); } } template void Port::record_request_packet(Peer* p) { counters().incr_table_requests_recv(); if (p) { p->counters().incr_table_requests_recv(); } } template void Port::record_bad_packet(const string& why, const Addr& host, uint16_t port, Peer* p) { XLOG_INFO("RIP port %s/%s/%s received bad packet from %s:%u - %s\n", this->_pio->ifname().c_str(), this->_pio->vifname().c_str(), this->_pio->address().str().c_str(), host.str().c_str(), port, why.c_str()); UNUSED(why); // in case XLOG_INFO is compiled out. UNUSED(host); UNUSED(port); counters().incr_bad_packets(); if (p) { p->counters().incr_bad_packets(); } } template void Port::record_bad_auth_packet(const string& why, const Addr& host, uint16_t port, Peer* p) { XLOG_INFO("RIP port %s/%s/%s authentication failed %s:%u - %s\n", this->_pio->ifname().c_str(), this->_pio->vifname().c_str(), this->_pio->address().str().c_str(), host.str().c_str(), port, why.c_str()); UNUSED(why); // in case XLOG_INFO is compiled out. UNUSED(host); UNUSED(port); counters().incr_bad_auth_packets(); if (p) { p->counters().incr_bad_auth_packets(); } } template void Port::record_bad_route(const string& why, const Addr& host, uint16_t port, Peer* p) { XLOG_INFO("RIP port %s/%s/%s received bad route from %s:%u - %s\n", this->_pio->ifname().c_str(), this->_pio->vifname().c_str(), this->_pio->address().str().c_str(), host.str().c_str(), port, why.c_str()); UNUSED(why); // in case XLOG_INFO is compiled out. UNUSED(host); UNUSED(port); counters().incr_bad_routes(); if (p) { p->counters().incr_bad_routes(); } } static void noop() {} template void Port::block_queries() { EventLoop& e = _pm.eventloop(); _query_blocked_timer = e.new_oneoff_after_ms(constants().interquery_delay_ms(), callback(noop)); } template bool Port::queries_blocked() const { return _query_blocked_timer.scheduled(); } template void Port::push_packets() { if (this->io_handler()->pending()) return; const RipPacket* head = _packet_queue->head(); if (head == 0) return; if (this->io_handler()->send(head->address(), head->port(), head->data())) { return; } XLOG_WARNING("Send failed: discarding outbound packets."); _packet_queue->flush_packets(); } template pair Port::route_policy(const RouteEntry& r) const { if (r.net() == RIP_AF_CONSTANTS::DEFAULT_ROUTE() && advertise_default_route() == false) { return make_pair(A::ZERO(), RIP_MAX_COST); } uint16_t cost = r.cost(); const Peer* peer = dynamic_cast*>(r.origin()); if (peer == 0) { // Route did not come from a peer: it's a static route or a // redist route. No horizon checking necessary. return make_pair(A::ZERO(), cost); } const Port& peer_port = peer->port(); if (&peer_port != this) { // Route did not originate from this Port instance. No horizon // checking necessary. return make_pair(A::ZERO(), cost); } switch (horizon()) { case NONE: // No processing break; case SPLIT: // Don't advertise route back to source cost = RIP_MAX_COST; break; case SPLIT_POISON_REVERSE: // Advertise back at cost of infinity cost = RIP_INFINITY; break; } return make_pair(A::ZERO(), cost); } template void Port::port_io_send_completion(bool success) { if (success == false) { XLOG_ERROR("Send failed\n"); } const RipPacket* head = _packet_queue->head(); XLOG_ASSERT(head != 0); _packet_queue->pop_head(); push_packets(); } template void Port::port_io_enabled_change(bool en) { start_stop_output_processing(); if (en == false) kill_peer_routes(); } template void Port::start_stop_output_processing() { if (output_allowed()) { start_request_table_timer(); start_output_processing(); request_table(); } else { stop_request_table_timer(); stop_output_processing(); } } template void Port::kill_peer_routes() { #ifdef INSTANTIATE_IPV4 // Reset the authentication handler PortAFSpecState& pss = af_state(); if (pss.auth_handler() != NULL) pss.auth_handler()->reset(); #endif typename PeerList::iterator pli = _peers.begin(); while (pli != _peers.end()) { vector*> routes; Peer* p = *pli; p->dump_routes(routes); typename vector*>::const_iterator ri; for (ri = routes.begin(); ri != routes.end(); ++ri) { const RouteEntry* r = *ri; p->update_route(r->net(), r->nexthop(), RIP_INFINITY, r->tag(), r->policytags()); } pli++; } } template bool Port::output_allowed() const { return enabled() && this->port_io_enabled() && (passive() == false); } template void Port::set_enabled(bool en) { bool old_allowed = output_allowed(); _en = en; bool allowed = output_allowed(); if (allowed != old_allowed) { start_stop_output_processing(); } if (en == false) kill_peer_routes(); } template void Port::set_passive(bool p) { bool old_allowed = output_allowed(); _passive = p; bool allowed = output_allowed(); if (allowed != old_allowed) { start_stop_output_processing(); } } template void Port::set_advertise_default_route(bool en) { _adv_def_rt = en; } template void Port::set_accept_default_route(bool en) { _acc_def_rt = en; } template void Port::set_accept_non_rip_requests(bool en) { _acc_non_rip_reqs = en; } template void Port::parse_request(const Addr& src_addr, uint16_t src_port, const uint8_t* entries_ptr, uint32_t n_entries) { if (this->port_io_enabled() == false) { debug_msg("Discarding RIP request: port io system not enabled."); return; } const PacketRouteEntry pre(entries_ptr); if (n_entries == 1 && pre.is_table_request()) { if (src_port == RIP_AF_CONSTANTS::IP_PORT) { Peer* p = peer(src_addr); if (p == 0) { p = create_peer(src_addr); p->counters().incr_packets_recv(); p->counters().incr_table_requests_recv(); } // if already doing unsolicited dump, then ignore // set unsolicited timer timeout to zero to trigger port // route dump unsolicited_response_timeout(); } else { if (queries_blocked()) return; // if already doing a debug dump, then ignore // start debug route dump if (_su_out && _su_out->running()) return; // Delete existing solicited update output, which is just lying // around, and re-instantiate to reply to table dump request delete _su_out; EventLoop& e = _pm.eventloop(); RouteDB& rdb = _pm.system().route_db(); _su_out = new OutputTable(e, *this, *_packet_queue, rdb, src_addr, src_port); _su_out->start(); block_queries(); } return; } if (queries_blocked()) return; // // This is a query for a set of routes. Answer it. // uint32_t i = 0; ResponsePacketAssembler rpa(*this); RouteDB& rdb = _pm.system().route_db(); while (i != n_entries) { RipPacket* pkt = new RipPacket(src_addr, src_port); rpa.packet_start(pkt); while (rpa.packet_full() == false && i != n_entries) { const uint8_t* pre_ptr = entries_ptr + i * PacketRouteEntry::size(); const PacketRouteEntry pre(pre_ptr); if (pre.prefix_len() > A::ADDR_BITLEN) { // Route request has an address with a bad prefix length // Unfortunately it's non-trivial for us to propagate this // back to the offending enquirer so we just stop processing // the request. delete (pkt); break; } const RouteEntry* r = rdb.find_route(pre.net()); if (r) { rpa.packet_add_route(r->net(), r->nexthop(), r->cost(), r->tag()); } else { rpa.packet_add_route(pre.net(), A::ZERO(), RIP_INFINITY, 0); } i++; } list*> auth_packets; if (rpa.packet_finish(auth_packets) == true) { typename list*>::iterator iter; for (iter = auth_packets.begin(); iter != auth_packets.end(); ++iter) { RipPacket* auth_pkt = *iter; _packet_queue->enqueue_packet(auth_pkt); counters().incr_non_rip_updates_sent(); } delete pkt; } else { delete pkt; break; } } push_packets(); block_queries(); } template void Port::port_io_receive(const A& src_address, uint16_t src_port, const uint8_t* rip_packet, size_t rip_packet_bytes) { string why; x_static_assert(RipPacketHeader::SIZE == 4); x_static_assert(PacketRouteEntry::SIZE == 20); if (enabled() == false) { debug_msg("Discarding RIP packet: Port not enabled."); return; } Peer* p = 0; if (src_port == RIP_AF_CONSTANTS::IP_PORT) { p = peer(src_address); } else { if (accept_non_rip_requests() == false) { return; } XLOG_ASSERT(p == 0); } record_packet(p); if (rip_packet_bytes < RIPv2_MIN_PACKET_BYTES) { why = c_format("Packet size less than minimum (%u < %u)", XORP_UINT_CAST(rip_packet_bytes), XORP_UINT_CAST(RIPv2_MIN_PACKET_BYTES)); record_bad_packet(why, src_address, src_port, p); return; } const RipPacketHeader rph(rip_packet); // // Basic RIP packet header validity checks // if (rph.valid_command() == false) { why = c_format("Invalid command"); record_bad_packet(why, src_address, src_port, p); return; } else if (rph.valid_version(RIP_AF_CONSTANTS::PACKET_VERSION) == false) { why = c_format("Invalid version (%d)", rph.version()); record_bad_packet(why, src_address, src_port, p); return; } else if (rph.valid_padding() == false) { why = c_format("Invalid padding (%u,%u)", rph.unused0(), rph.unused1()); record_bad_packet(why,src_address, src_port, p); return; } // // Check this is not an attempt to inject routes from non-RIP port // if (rph.command() == RipPacketHeader::RESPONSE && src_port != RIP_AF_CONSTANTS::IP_PORT) { why = c_format("RIP response originating on wrong port (%d != %d)", src_port, RIP_AF_CONSTANTS::IP_PORT); record_bad_packet(why, src_address, src_port, p); return; } #if defined (INSTANTIATE_IPV4) const uint8_t* entries_ptr = NULL; uint32_t n_entries = 0; bool new_peer = (p == NULL); if ((p != NULL) && (p->route_count() == 0)) { // // XXX: If the peer hasn't been active for long enough, then // consider it a new peer for authentication purpose. // The reason we need this modification is because the idle // peer state may be kept for a little bit too long (e.g., 2*180 // seconds), and if the peer is restarted before that we won't // accept its initial packet with sequence number of zero. // With this modification we can accept the first authentication // packet with sequence number of zero immediately after // all routes have expired (e.g., after 300 seconds). // new_peer = true; } if (af_state().auth_handler()->authenticate_inbound(rip_packet, rip_packet_bytes, entries_ptr, n_entries, src_address, new_peer) == false) { why = c_format("packet failed authentication (%s): %s", af_state().auth_handler()->effective_name(), af_state().auth_handler()->error().c_str()); record_bad_auth_packet(why, src_address, src_port, p); return; } if (n_entries == 0) { // No entries in packet, nothing further to do. return; } #elif defined (INSTANTIATE_IPV6) const uint8_t* entries_ptr = rip_packet + RipPacketHeader::size(); uint32_t n_entries = (rip_packet_bytes - RipPacketHeader::size()) / PacketRouteEntry::size(); size_t calc_bytes = n_entries * PacketRouteEntry::size() + RipPacketHeader::size(); if (calc_bytes != rip_packet_bytes) { why = c_format("Packet did not contain an integral number of route " "entries"); record_bad_packet(why, src_address, src_port, p); } #endif if (src_port == RIP_AF_CONSTANTS::IP_PORT && rph.command() == RipPacketHeader::RESPONSE) { record_response_packet(p); parse_response(src_address, src_port, entries_ptr, n_entries); } else { XLOG_ASSERT(rph.command() == RipPacketHeader::REQUEST); if (src_port == RIP_AF_CONSTANTS::IP_PORT) { record_request_packet(p); } else { counters().incr_non_rip_requests_recv(); } parse_request(src_address, src_port, entries_ptr, n_entries); } } // ---------------------------------------------------------------------------- // Port Specialized methods // #ifdef INSTANTIATE_IPV4 template <> void Port::parse_response(const Addr& src_addr, uint16_t src_port, const uint8_t* entries_ptr, uint32_t n_entries) { static IPv4 net_filter("255.0.0.0"); static IPv4 class_b_net("128.0.0.0"); static IPv4 class_c_net("192.0.0.0"); static IPv4 class_d_net("224.0.0.0"); static IPv4 class_e_net("240.0.0.0"); string why; Peer* p = peer(src_addr); if (p == 0) { p = create_peer(src_addr); p->counters().incr_packets_recv(); p->counters().incr_update_packets_recv(); } for (uint32_t i = 0; i < n_entries; i++) { const uint8_t* pre_ptr = entries_ptr + i * PacketRouteEntry::size(); const PacketRouteEntry pre(pre_ptr); if (pre.addr_family() != AF_INET) { why = c_format("Bad address family (%u instead of %u)", pre.addr_family(), AF_INET); record_bad_route(why, src_addr, src_port, p); continue; } uint16_t metric = pre.metric(); if (metric > RIP_INFINITY) { why = c_format("Bad metric (%u > %u)", metric, RIP_INFINITY); record_bad_route(why, src_addr, src_port, p); continue; } uint32_t prefix_len = pre.prefix_len(); if (prefix_len > Addr::ADDR_BITLEN) { why = c_format("Bad prefix length (%u > %u)", prefix_len, Addr::ADDR_BITLEN); record_bad_packet(why, src_addr, src_port, p); continue; } IPv4Net net = pre.net(); IPv4 addr = pre.addr(); if (prefix_len == 0 && addr != IPv4::ZERO()) { // Subnet mask not specified, thus apply a clasfull mask if (addr < class_b_net) { prefix_len = 8; // Class A } else if (addr < class_c_net) { prefix_len = 16; // Class B } else if (addr < class_d_net) { prefix_len = 24; // Class C } else { prefix_len = 32; // XXX check RFC! } net = IPv4Net(IPv4(addr), prefix_len); } if (net == RIP_AF_CONSTANTS::DEFAULT_ROUTE() && accept_default_route() == false) { continue; } IPv4 masked_net = net.masked_addr() & net_filter; if (masked_net.is_multicast()) { why = c_format("Multicast route (%s)", masked_net.str().c_str()); record_bad_route(why, src_addr, src_port, p); continue; } if (masked_net.is_loopback()) { why = c_format("Loopback route (%s)", masked_net.str().c_str()); record_bad_route(why, src_addr, src_port, p); continue; } if (masked_net >= class_e_net) { why = c_format("Experimental route (%s)", masked_net.str().c_str()); record_bad_route(why, src_addr, src_port, p); continue; } if (masked_net == IPv4::ZERO()) { if (net.prefix_len() != 0) { why = c_format("Net 0"); record_bad_route(why, src_addr, src_port, p); continue; } else if (accept_default_route() == false) { why = c_format("Default route"); record_bad_route(why, src_addr, src_port, p); continue; } } if (prefix_len == Addr::ADDR_BITLEN) { // // Check if the route is for one of my own addresses or // for a directly connected broadcast address. // bool my_addr_found = false; bool bcast_addr_found = false; const IfMgrIfTree& iftree = _pm.iftree(); IfMgrIfTree::IfMap::const_iterator if_iter; for (if_iter = iftree.interfaces().begin(); if_iter != iftree.interfaces().end(); ++if_iter) { const IfMgrIfAtom& iface = if_iter->second; // Test if interface is enabled and the link state is up if ((! iface.enabled()) || iface.no_carrier()) continue; IfMgrIfAtom::VifMap::const_iterator vif_iter; for (vif_iter = iface.vifs().begin(); vif_iter != iface.vifs().end(); ++vif_iter) { const IfMgrVifAtom& vif = vif_iter->second; // Test if vif is enabled if (! vif.enabled()) continue; // // Test if there is a matching interface address // or a broadcast address. // IfMgrVifAtom::IPv4Map::const_iterator a4_iter; for (a4_iter = vif.ipv4addrs().begin(); a4_iter != vif.ipv4addrs().end(); ++a4_iter) { const IfMgrIPv4Atom& a4 = a4_iter->second; if (! a4.enabled()) continue; // Test if my own address if (a4.addr() == net.masked_addr()) { my_addr_found = true; break; } // Test if the broadcast address if (a4.has_broadcast() && (a4.broadcast_addr() == net.masked_addr())) { bcast_addr_found = true; break; } } } } if (my_addr_found) { why = c_format("My interface address (%s)", net.masked_addr().str().c_str()); record_bad_route(why, src_addr, src_port, p); continue; } if (bcast_addr_found) { why = c_format("My broadcast address (%s)", net.masked_addr().str().c_str()); record_bad_route(why, src_addr, src_port, p); continue; } } IPv4 nh = pre.nexthop(); if (nh == IPv4::ZERO()) { nh = src_addr; } else if (nh == _pio->address()) { // Nexthop points to us, ignore route (either poison-rev or bogus) continue; } else { // Test if nh is on the receiving subnet const IfMgrIfTree& iftree = _pm.iftree(); const IfMgrIPv4Atom* ifa = iftree.find_addr(_pio->ifname(), _pio->vifname(), _pio->address()); XLOG_ASSERT(ifa != NULL); if (IPv4Net(nh, ifa->prefix_len()) != IPv4Net(ifa->addr(), ifa->prefix_len())) { nh = src_addr; } } metric += cost(); if (metric > RIP_INFINITY) { metric = RIP_INFINITY; } // // XXX review // Want to do anything with tag? // uint16_t tag = pre.tag(); p->update_route(net, nh, metric, tag, PolicyTags()); } } #endif // INSTANTIATE_IPV4 // ---------------------------------------------------------------------------- // Port Specialized methods // #ifdef INSTANTIATE_IPV6 template <> void Port::parse_response(const Addr& src_addr, uint16_t src_port, const uint8_t* entries_ptr, uint32_t n_entries) { string why; Peer* p = peer(src_addr); if (p == 0) { p = create_peer(src_addr); p->counters().incr_packets_recv(); p->counters().incr_update_packets_recv(); } // ALL_ONES is used as a magic value to indicate no nexthop has been set. IPv6 nh = IPv6::ALL_ONES(); for (uint32_t i = 0; i < n_entries; i++) { const uint8_t* pre_ptr = entries_ptr + i * PacketRouteEntry::size(); const PacketRouteEntry pre(pre_ptr); if (pre.is_nexthop()) { nh = pre.nexthop(); if (! nh.is_linklocal_unicast()) nh = IPv6::ZERO(); if (nh == IPv6::ZERO()) { nh = src_addr; } continue; } else if (nh == IPv6::ALL_ONES()) { why = c_format("Route specified before nexthop"); record_bad_route(why, src_addr, src_port, p); continue; } uint16_t metric = pre.metric(); if (metric > RIP_INFINITY) { why = c_format("Bad metric (%u > %u)", metric, RIP_INFINITY); record_bad_route(why, src_addr, src_port, p); continue; } if (pre.prefix_len() > Addr::ADDR_BITLEN) { why = c_format("Bad prefix length (%u > %u)", pre.prefix_len(), Addr::ADDR_BITLEN); record_bad_packet(why, src_addr, src_port, p); continue; } IPv6Net net = pre.net(); IPv6 masked_net = net.masked_addr(); if (masked_net.is_multicast()) { why = c_format("Multicast route (%s)", masked_net.str().c_str()); record_bad_route(why, src_addr, src_port, p); continue; } if (masked_net.is_linklocal_unicast()) { why = c_format("Linklocal route (%s)", masked_net.str().c_str()); record_bad_route(why, src_addr, src_port, p); continue; } if (masked_net.is_loopback()) { why = c_format("Loopback route (%s)", masked_net.str().c_str()); record_bad_route(why, src_addr, src_port, p); continue; } if (masked_net == IPv6::ZERO()) { if (net.prefix_len() != 0) { why = c_format("Net 0"); record_bad_route(why, src_addr, src_port, p); continue; } else if (accept_default_route() == false) { why = c_format("Default route"); record_bad_route(why, src_addr, src_port, p); continue; } } if (pre.prefix_len() == Addr::ADDR_BITLEN) { // // Check if the route is for one of my own addresses. // bool my_addr_found = false; const IfMgrIfTree& iftree = _pm.iftree(); IfMgrIfTree::IfMap::const_iterator if_iter; for (if_iter = iftree.interfaces().begin(); if_iter != iftree.interfaces().end(); ++if_iter) { const IfMgrIfAtom& iface = if_iter->second; // Test if interface is enabled and the link state is up if ((! iface.enabled()) || iface.no_carrier()) continue; IfMgrIfAtom::VifMap::const_iterator vif_iter; for (vif_iter = iface.vifs().begin(); vif_iter != iface.vifs().end(); ++vif_iter) { const IfMgrVifAtom& vif = vif_iter->second; // Test if vif is enabled if (! vif.enabled()) continue; // // Test if there is a matching interface address // or a broadcast address. // IfMgrVifAtom::IPv6Map::const_iterator a6_iter; for (a6_iter = vif.ipv6addrs().begin(); a6_iter != vif.ipv6addrs().end(); ++a6_iter) { const IfMgrIPv6Atom& a6 = a6_iter->second; if (! a6.enabled()) continue; // Test if my own address if (a6.addr() == net.masked_addr()) { my_addr_found = true; break; } } } } if (my_addr_found) { why = c_format("My interface address (%s)", net.masked_addr().str().c_str()); record_bad_route(why, src_addr, src_port, p); continue; } } metric += metric + cost(); if (metric > RIP_INFINITY) { metric = RIP_INFINITY; } // // XXX review // Want to do anything with tag? // uint16_t tag = pre.tag(); p->update_route(net, nh, metric, tag, PolicyTags()); } } #endif // INSTANTIATE_IPV6 // ---------------------------------------------------------------------------- // Instantiations #ifdef INSTANTIATE_IPV4 template class Port; #endif #ifdef INSTANTIATE_IPV6 template class Port; #endif xorp/rip/packet_queue.cc0000664000076400007640000000644211421137511015436 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "rip_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/ipv4.hh" #include "libxorp/ipv6.hh" #include "packet_queue.hh" template PacketQueue::PacketQueue() : _buffered_bytes(0), _max_buffered_bytes(64000), _drops(0) { } template PacketQueue::~PacketQueue() { flush_packets(); } template void PacketQueue::enqueue_packet(const RipPacket* pkt) { while (_buffered_bytes + pkt->data_bytes() >= _max_buffered_bytes && drop_old() == true) { // XXX: Empty body } _buffered_bytes += pkt->data_bytes(); _ready_packets.push_back(pkt); } template bool PacketQueue::empty() const { return _ready_packets.empty(); } template const RipPacket* PacketQueue::head() const { if (_ready_packets.empty()) return 0; return _ready_packets.front(); } template void PacketQueue::pop_head() { if (_ready_packets.empty() == false) { _buffered_bytes -= _ready_packets.front()->data_bytes(); delete _ready_packets.front(); _ready_packets.pop_front(); } } template bool PacketQueue::drop_old() { if (_ready_packets.empty() == false) { typename QueueRep::iterator i = ++_ready_packets.begin(); if (i != _ready_packets.end()) { XLOG_INFO("Dropping outbound RIP packet"); delete *i; _ready_packets.erase(i); _drops++; return true; } } return false; } template uint32_t PacketQueue::drop_count() const { return _drops; } template void PacketQueue::reset_drop_count() { _drops = 0; } template void PacketQueue::flush_packets() { while (_ready_packets.empty() == false) { _buffered_bytes -= _ready_packets.front()->data_bytes(); delete _ready_packets.front(); _ready_packets.pop_front(); } XLOG_ASSERT(_buffered_bytes == 0); } template void PacketQueue::set_max_buffered_bytes(uint32_t mbb) { _max_buffered_bytes = mbb; } template uint32_t PacketQueue::max_buffered_bytes() const { return _max_buffered_bytes; } template uint32_t PacketQueue::buffered_bytes() const { return _buffered_bytes; } // ---------------------------------------------------------------------------- // Instantiations #ifdef INSTANTIATE_IPV4 template class PacketQueue; #endif #ifdef INSTANTIATE_IPV6 template class PacketQueue; #endif xorp/rip/port.hh0000664000076400007640000003541111540224234013760 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/rip/port.hh,v 1.33 2008/10/02 21:58:16 bms Exp $ #ifndef __RIP_PORT_HH__ #define __RIP_PORT_HH__ #include "libxorp/eventloop.hh" #include "constants.hh" #include "port_vars.hh" #include "port_io.hh" // // Forward declarations // class AuthHandlerBase; template class OutputTable; template class OutputUpdates; template class PacketRouteEntry; template class PacketQueue; template class Peer; template class PortManagerBase; template class RouteEntry; /** * @short Specializable Address Family state for Port classes. * * This class exists to be specialized with IPv4 and IPv6 state and * methods. */ template class PortAFSpecState {}; /** * @short IPv4 specialized Port state. * * This class holds authentication handler state which is IPv4 * specific data for a RIP Port. */ template <> class PortAFSpecState { private: AuthHandlerBase* _ah; public: /** * Constructor. * * Instantiates authentication handler as a @ref NullAuthHandler. */ PortAFSpecState(); /** * Destructor. * * Deletes authentication handler if non-null. */ ~PortAFSpecState(); /** * Set the authentication handler. * * @param ah authentication handler to be used. * @return pointer to former handler. */ AuthHandlerBase* set_auth_handler(AuthHandlerBase* ah); /** * Get authentication handler. */ const AuthHandlerBase* auth_handler() const; /** * Get authentication handler. */ AuthHandlerBase* auth_handler(); }; /** * @short IPv6 specialized Port state. */ template <> class PortAFSpecState { protected: uint32_t _mepp; // Max route entries per packet public: PortAFSpecState() : _mepp(50) {} /** * Get the maximum number of route entries placed in each RIPng response * packet. */ uint32_t max_entries_per_packet() const { return _mepp; } /** * Set the maximum number of route entries placed in each RIPng response * packet. */ void set_max_entries_per_packet(uint32_t n) { _mepp = n; } }; /** * @short RIP Port * * A RIP Port is an origin and sink of RIP packets. It is uniquely identified * by the tuplet of . The Port sends * and receives RIP packets via an attached Port IO object * (@ref PortIOBase). The Port contains a list of Peers (@ref Peer) * that it has received communication on and is responsible for updating * information sent by peers in the RIP route database (@ref RouteDB). */ template class Port : public PortIOUserBase { public: typedef A Addr; typedef list*> PeerList; public: Port(PortManagerBase& manager); ~Port(); PortManagerBase& port_manager() { return _pm; } /** * Set enabled state. When a port is enabled it can perform input * and output processing. When not enabled, input data is * discarded and output processing can not occur. */ void set_enabled(bool en); /** * Get enabled state. */ bool enabled() const { return _en; } /** * Get timer constants in use for routes received on this port. */ PortTimerConstants& constants() { return _constants; } /** * Get timer constants in use for routes received on this port. */ const PortTimerConstants& constants() const { return _constants; } /** * Get Address Family specific state associated with port. This is * state that only has meaning within the IP address family. */ PortAFSpecState& af_state() { return _af_state; } /** * Get Address Family specific state associated with port. This is * state that only has meaning within the IP address family. */ const PortAFSpecState& af_state() const { return _af_state; } /** * Get cost metric associated with Port. */ uint32_t cost() const { return _cost; } /** * Set cost metric associated with Port. */ void set_cost(uint32_t cost) { _cost = cost; } /** * Get horizon type associated with Port. */ const RipHorizon& horizon() const { return _horizon; } /** * Set horizon type associated with Port. * @param h horizon type. */ void set_horizon(const RipHorizon& h) { _horizon = h; } /** * Determine whether Port address should be advertised. * @return true if port should be advertised to other hosts, false * otherwise. */ bool advertise() const { return _advertise; } /** * Set Port advertisement status. * @param en true if port should be advertised, false otherwise. */ void set_advertise(bool en) { _advertise = en; } /** * Include default route in RIP response messages. * @return true if default route is advertised. */ bool advertise_default_route() const { return _adv_def_rt; } /** * Configure whether default route is advertised in RIP response * messages. * @param en true if default route should be advertised. */ void set_advertise_default_route(bool en); /** * Accept default route if found in RIP response messages. * @return true if default route should be accepted. */ bool accept_default_route() const { return _acc_def_rt; } /** * Accept default route if found in RIP response messages. * @param en true if default route should be accepted. */ void set_accept_default_route(bool en); /** * Accept routes, but do not advertise them. * @return true if this port is passive. */ bool passive() const { return _passive; } /** * Accept routes, but do not advertise them. * @param passive true if port should receive and not send packets. */ void set_passive(bool passive); /** * Accept and respond to non-RIP requests. Testing and Debugging * tools typically send requests from non-RIP IP ports. */ bool accept_non_rip_requests() const { return _acc_non_rip_reqs; } /** * Accept and respond to non-RIP requests. Testing and Debugging * tools typically send requests from non-RIP IP ports. */ void set_accept_non_rip_requests(bool en); /** * Get Peers associated with this Port. */ const PeerList& peers() const { return _peers; } /** * Get Peers associated with this Port. * * NB This method is a backdoor for testing purposes and should * not be relied upon to exist in future. */ PeerList& peers() { return _peers; } /** * Get counters associated with Port. */ const PortCounters& counters() const { return _counters; } /** * Get counters associated with Port. */ PortCounters& counters() { return _counters; } /** * Get Peer identified by address. * * @return pointer to Peer on success, 0 otherwise. */ const Peer* peer(const Addr& addr) const; /** * Set the maximum packet buffer size. */ void set_max_packet_buffer_bytes(uint32_t max_bytes); /** * Get the maximum packet buffer size. */ uint32_t set_max_packet_buffer_bytes() const; /** * Get the current number of bytes buffered in RIP packets. */ uint32_t packet_buffer_bytes() const; /** * A dummy method to reschedule a non-existing timer. */ void reschedule_dummy_timer() {} /** * Reschedule request table timer. If the timer was not running, * then don't schedule it. */ void reschedule_request_table_timer(); protected: /** * Start request table timer. When there are no peers, this * schedules the periodic transmission of request table packets. */ void start_request_table_timer(); /** * Stop request table timer. */ void stop_request_table_timer(); /** * Send request packet. * @return true if packet sent, false if no packet sent. */ bool request_table(); /** * Send request packet if there are no peers. * @return true if packet sent, false if no packet sent. */ bool request_table_timeout(); /** * Start periodic timer to garbage collect peers. Timer * deschedules itself when no peers exist. */ void start_peer_gc_timer(); /** * Poll peers and remove those with no routes. * return true if peers still exist, false otherwise. */ bool peer_gc_timeout(); /** * Kill peer routes. Change cost of routes learned from peers to * infinity. Typically called when port instance is disabled or * io system reports that it is not enabled. */ void kill_peer_routes(); protected: /** * Get Peer identified by address. * @return pointer to Peer on success, 0 otherwise. */ Peer* peer(const Addr& addr); /** * Create Peer. * @return pointer to Peer if created, 0 on failure or peer already exists. */ Peer* create_peer(const Addr& addr); /** * Record packet arrival. Updates port and peer counters. */ void record_packet(Peer* p); /** * Record response packet arrival. */ void record_response_packet(Peer* p); /** * Record request packet arrival. */ void record_request_packet(Peer* p); /** * Record bad packet. * * @param why reason packet marked */ void record_bad_packet(const string& why, const Addr& addr, uint16_t port, Peer* p); /** * Record bad authentication packet. */ void record_bad_auth_packet(const string& why, const Addr& addr, uint16_t port, Peer* p); /** * Record bad route. * * @param why reason packet marked */ void record_bad_route(const string& why, const Addr& src, uint16_t port, Peer* p); /** * Parse request message. */ void parse_request(const Addr& src_addr, uint16_t rc_port, const uint8_t* entries_ptr, uint32_t n_entries); /** * Parse response message. */ void parse_response(const Addr& src_addr, uint16_t src_port, const uint8_t* entries_ptr, uint32_t n_entries); /** * Block route queries for amount of time determined by * @ref PortTimerConstants::interquery_delay_ms(). */ void block_queries(); /** * Determine whether queries are currently blocked and should be * discarded. */ bool queries_blocked() const; /** * Unsolicited update output timer timeout. */ void unsolicited_response_timeout(); /** * Triggered update timeout. */ void triggered_update_timeout(); /** * Query output process policy. * * Check whether instance is enabled, whether it is active or * passive, and whether the associated I/O object is enabled. */ bool output_allowed() const; /** * Check whether output is allowed and start or stop output * processing accordingly. */ void start_stop_output_processing(); /** * Start output processing. * * Starts timers for unsolicited updates and triggered updates. */ void start_output_processing(); /** * Stop output processing. * * Stops timers for unsolicited updates and triggered updates. */ void stop_output_processing(); public: /** * If I/O handler is not already sending a packet, take a packet from * packet queue and send it. */ void push_packets(); /** * Check policy on route. * * @returns tuple (nexthop,cost). If route should not be * advertised the cost value will be greater than RIP_INFINITY. */ pair route_policy(const RouteEntry& re) const; /** * Send completion notification. Called by PortIO instance when a * send request is completed. * * @param success indication of whether send completed successfully. */ void port_io_send_completion(bool success); /** * Receive RIP packet. Called by PortIO instance when a RIP packet * arrives. * * @param addr source address of packet. * @param port source port of packet. * @param rip_packet pointer to RIP packet data. * @param rip_packet_bytes size of RIP packet data. */ void port_io_receive(const Addr& src_addr, uint16_t src_port, const uint8_t* rip_packet, const size_t rip_packet_bytes); /** * Notification that PortIO enabled state has changed. Called by * PortIO when it's enabled status changes. * * @param en the enabled status of the I/O system. */ void port_io_enabled_change(bool en); protected: PortManagerBase& _pm; PortAFSpecState _af_state; // Address family specific data PeerList _peers; // Peers on Port XorpTimer _rt_timer; // Request table timer XorpTimer _gc_timer; // Peer garbage collection XorpTimer _ur_timer; // Unsolicited response timer XorpTimer _tu_timer; // Triggered update timer XorpTimer _query_blocked_timer; // Rate limiting on queries bool _en; // enabled state uint32_t _cost; // Cost metric of port RipHorizon _horizon; // Port Horizon type bool _advertise; // Advertise IO port bool _adv_def_rt; // Advertise default route bool _acc_def_rt; // Accept default route bool _passive; // Passive (recv only port) bool _acc_non_rip_reqs; // Accept non-RIP requests PacketQueue* _packet_queue; // Outbound packet queue PortTimerConstants _constants; // Port related timer constants PortCounters _counters; // Packet counters OutputTable* _ur_out; // Unsolicited update output OutputUpdates* _tu_out; // Triggered update output OutputTable* _su_out; // Solicited update output }; #endif // __RIP_PORT_HH__ xorp/rip/redist.cc0000664000076400007640000001016211421137511014247 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "rip_module.h" #include "libxorp/xlog.h" #include "libxorp/eventloop.hh" #include "libxorp/ipv4.hh" #include "libxorp/ipv6.hh" #include "libxorp/ipv4net.hh" #include "libxorp/ipv6net.hh" #include "constants.hh" #include "route_db.hh" #include "route_entry.hh" #include "redist.hh" // ---------------------------------------------------------------------------- // RedistRouteOrigin template uint32_t RedistRouteOrigin::expiry_secs() const { return 0; } template uint32_t RedistRouteOrigin::deletion_secs() const { return DEFAULT_DELETION_SECS; } // ---------------------------------------------------------------------------- // RouteRedistributor template RouteRedistributor::RouteRedistributor(RouteDB& rdb) : _route_db(rdb), _wdrawer(0) { _rt_origin = new RedistRouteOrigin(); } template RouteRedistributor::~RouteRedistributor() { delete _rt_origin; delete _wdrawer; } template bool RouteRedistributor::add_route(const Net& net, const Addr& nexthop, const string& ifname, const string& vifname, uint16_t cost, uint16_t tag, const PolicyTags& policytags) { _route_db.add_rib_route(net, nexthop, ifname, vifname, cost, tag, _rt_origin, policytags); return _route_db.update_route(net, nexthop, ifname, vifname, cost, tag, _rt_origin, policytags, false); } template bool RouteRedistributor::expire_route(const Net& net) { string ifname, vifname; // XXX: not set, because not needed _route_db.delete_rib_route(net); return _route_db.update_route(net, A::ZERO(), ifname, vifname, RIP_INFINITY, 0, _rt_origin, PolicyTags(), false); } template uint32_t RouteRedistributor::route_count() const { return _rt_origin->route_count(); } template void RouteRedistributor::withdraw_routes() { if (_wtimer.scheduled() == false) { EventLoop& e = _route_db.eventloop(); _wtimer = e.new_periodic_ms(5, callback(this, &RouteRedistributor::withdraw_batch)); } } template bool RouteRedistributor::withdrawing_routes() const { return _wtimer.scheduled(); } template bool RouteRedistributor::withdraw_batch() { if (_wdrawer == 0) { _wdrawer = new RouteWalker(_route_db); _wdrawer->reset(); } XLOG_ASSERT(_wdrawer->state() == RouteWalker::STATE_RUNNING); uint32_t visited = 0; const RouteEntry* r = _wdrawer->current_route(); while (r != 0) { if (r->origin() == _rt_origin) { _route_db.update_route(r->net(), r->nexthop(), r->ifname(), r->vifname(), RIP_INFINITY, r->tag(), _rt_origin, r->policytags(), false); } r = _wdrawer->next_route(); if (++visited == 5) { return true; // we're not finished - reschedule timer } } delete _wdrawer; _wdrawer = 0; return false; // we're finished - cancel timer } // ---------------------------------------------------------------------------- // Instantiations #ifdef INSTANTIATE_IPV4 template class RedistRouteOrigin; template class RouteRedistributor; #endif #ifdef INSTANTIATE_IPV6 template class RouteRedistributor; template class RedistRouteOrigin; #endif xorp/rip/xrl_port_manager.hh0000664000076400007640000001443111540225534016342 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/rip/xrl_port_manager.hh,v 1.22 2008/10/30 00:30:54 pavlin Exp $ #ifndef __RIP_XRL_PORT_MANAGER_HH__ #define __RIP_XRL_PORT_MANAGER_HH__ #include "libxorp/service.hh" #include "libfeaclient/ifmgr_xrl_mirror.hh" #include "port_manager.hh" #include "port.hh" #include "trace.hh" class XrlRouter; /** * @short Class for managing RIP Ports and their transport systems. * * The XrlPortManager class instantiates RIP Port instances and their * transport system. The methods add_rip_address and * remove_rip_address cause @ref Port objects and @ref XrlPortIO * objects to be instantiated and destructed. * * The deliver_packet method forwards an arriving packet to the * appropriate @ref XrlPortIO object. */ template class XrlPortManager : public NONCOPYABLE, public PortManagerBase, public IfMgrHintObserver, public ServiceBase, public ServiceChangeObserverBase { public: XrlPortManager(System& system, XrlRouter& xr, IfMgrXrlMirror& ifm) : PortManagerBase(system, ifm.iftree()), ServiceBase("RIP Port Manager"), _xr(xr), _ifm(ifm) { _ifm.attach_hint_observer(this); } ~XrlPortManager(); /** * Request start up of instance. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int startup(); /** * Request shutdown of instance. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int shutdown(); /** * Request the addition of an address to run RIP on. If the * address is known to exist on the specified interface and vif, a * request is sent to the FEA to create an appropriately * bound RIP socket. * * @param interface to run RIP on. * @param vif virtual interface to run RIP on. * @param addr address to run RIP on. * * @return true on success or if address is already running RIP, * false on failure. */ bool add_rip_address(const string& ifname, const string& vifname, const A& addr); /** * Remove an address running RIP. * * @param interface to run RIP on. * @param vif virtual interface to run RIP on. * @param addr address to run RIP on. * * @return true on success or if address is not running RIP, false on * otherwise. */ bool remove_rip_address(const string& ifname, const string& vifname, const A& addr); /** * Deliver packet to RIP port associated with socket id that * received the packet. * * @param sockid unique socket identifier. * @param ifname the interface name the packet arrived on, if known. * If unknown, then it is an empty string. * @param vifname the vif name the packet arrived on, if known. * If unknown, then it is an empty string. * @param src_addr source address of packet. * @param src_port source port of packet. * @param pdata packet data. * * @return true if packet delivered, false if the owner of the * sockid can not be found. */ bool deliver_packet(const string& sockid, const string& ifname, const string& vifname, const A& src_addr, uint16_t src_port, const vector& pdata); /** * Find RIP port associated with interface, vif, address tuple. * * @return pointer to port on success, 0 if port could not be found. */ Port* find_port(const string& ifname, const string& vifname, const A& addr); /** * Find RIP port associated with interface, vif, address tuple. * * @return pointer to port on success, 0 if port could not be found. */ const Port* find_port(const string& ifname, const string& vifname, const A& addr) const; /** * Determine if rip address is up. The result is based on the current * state of information from the FEA. * * @return true if the address is up, false if the address is not up * or does not exist. */ bool underlying_rip_address_up(const string& ifname, const string& vifname, const A& addr) const; /** * Determine if rip address exists. The result is based on the * current state of information from the FEA. * * @return true if the address is up, false if the address is not up * or does not exist. */ bool underlying_rip_address_exists(const string& ifname, const string& vifname, const A& addr) const; Trace& packet_trace() { return _trace; } protected: // // IfMgrHintObserver methods // void tree_complete(); void updates_made(); // // ServiceChangeObserverBase methods // - used for observing status changes of XrlPortIO objects instantiated // by XrlPortManager instance. // void status_change(ServiceBase* service, ServiceStatus old_status, ServiceStatus new_status); // // Attempt to start next io handler. Walk list of ports, check // none are in state SERVICE_STARTING and call start on first found to be // in state SERVICE_READY. // // We start 1 at a time to avoid races trying to create // sockets with the fea. // void try_start_next_io_handler(); protected: XrlRouter& _xr; // XrlRouter IfMgrXrlMirror& _ifm; // Interface Mirror map*> _dead_ports; // Ports awaiting io shutdown private: Trace _trace; }; #endif // __RIP_XRL_PORT_MANAGER_HH__ xorp/rip/rib_notifier_base.cc0000664000076400007640000000371011421137511016423 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "rib_notifier_base.hh" // ---------------------------------------------------------------------------- // RibNotifierBase implementation template RibNotifierBase::RibNotifierBase(EventLoop& e, UpdateQueue& uq, uint32_t ms) : _e(e), _uq(uq), _poll_ms(ms) { _ri = _uq.create_reader(); } template RibNotifierBase::~RibNotifierBase() { _uq.destroy_reader(_ri); } template void RibNotifierBase::start_polling() { _t = _e.new_periodic_ms(_poll_ms, callback(this, &RibNotifierBase::poll_updates)); } template void RibNotifierBase::stop_polling() { _t.unschedule(); } template bool RibNotifierBase::poll_updates() { if (_uq.get(_ri)) { updates_available(); } return true; } // ---------------------------------------------------------------------------- // Instantiations #ifdef INSTANTIATE_IPV4 #include "libxorp/ipv4.hh" template class RibNotifierBase; #endif #ifdef INSTANTIATE_IPV6 #include "libxorp/ipv6.hh" template class RibNotifierBase; #endif xorp/rip/system.hh0000664000076400007640000000773611540225534014335 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/rip/system.hh,v 1.16 2008/10/29 21:59:39 andrewma Exp $ #ifndef __RIP_SYSTEM_HH__ #define __RIP_SYSTEM_HH__ #include "trace.hh" #include "route_db.hh" #include "port_manager.hh" #include "policy/backend/policy_filters.hh" /** * @short Top Level container for XORP RIP implementation. */ template class System : public NONCOPYABLE { public: typedef RouteDB RouteDatabase; typedef PortManagerBase PortManager; public: System(EventLoop& e) : _e(e), _rtdb(e,_policy_filters), _pm(0) {} ~System(); /** * Get @ref EventLoop instance that each object in system should * use. */ EventLoop& eventloop() { return _e; } /** * Get @ref EventLoop instance that each object in RIP system * should use. */ const EventLoop& eventloop() const { return _e; } /** * Get the Route Database that each object in the RIP system * should use. */ RouteDatabase& route_db() { return _rtdb; } /** * Get the Route Database that each object in the RIP system * should use. */ const RouteDatabase& route_db() const { return _rtdb; } /** * Set the port manager object associated with the system. * * @param pointer to PortManager to be used. * * @return true if port manager has not previously been set and * pointer is not null, false otherwise. */ bool set_port_manager(PortManager* pm); /** * Get pointer to PortManager that the RIP system is using. */ PortManager* port_manager() { return _pm; } /** * Get pointer PortManager that the RIP system is using. */ const PortManager* port_manager() const { return _pm; } /** * Configure a policy filter. * * @param filter id of filter to configure. * @param conf configuration of filter. */ void configure_filter(const uint32_t& filter, const string& conf) { _policy_filters.configure(filter,conf); } /** * Reset a policy filter. * * @param filter id of filter to reset. */ void reset_filter(const uint32_t& filter) { _policy_filters.reset(filter); } /** * Push routes through policy filters for re-filtering. */ void push_routes() { _rtdb.push_routes(); } /** * @return reference to global policy filters. */ PolicyFilters& policy_filters() { return _policy_filters; } Trace& route_trace() { return _rtdb.trace(); } protected: EventLoop& _e; // // There should be only one instatiation per process. // RIP uses separate processes for IPv4 and IPv6 so we are ok. // PolicyFilters _policy_filters; RouteDatabase _rtdb; PortManager* _pm; }; // ---------------------------------------------------------------------------- // Inline System methods // template bool System::set_port_manager(PortManager* pm) { if (pm != 0 && _pm == 0) { _pm = pm; return true; } return false; } template System::~System() { _rtdb.flush_routes(); } #endif // __RIP_SYSTEM_HH__ xorp/rip/xrl_port_io.hh0000664000076400007640000000713611540224235015340 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/rip/xrl_port_io.hh,v 1.17 2008/10/02 21:58:18 bms Exp $ #ifndef __XRL_PORT_IO_HH__ #define __XRL_PORT_IO_HH__ #include "libxorp/ipv4.hh" #include "libxorp/ipv6.hh" #include "libxorp/safe_callback_obj.hh" #include "libxorp/service.hh" #include "port_io.hh" class XrlError; class XrlRouter; template class XrlPortIO : public PortIOBase, public ServiceBase, public CallbackSafeObject { public: typedef A Addr; typedef PortIOUserBase PortIOUser; public: XrlPortIO(XrlRouter& xr, PortIOUser& port, const string& ifname, const string& vifname, const Addr& addr); /** * Startup. Sends request to FEA for socket server for address * and then attempts to instantiate socket with socket server. If * both operations are successful, instance status transitions to * SERVICE_RUNNING. Otherwise, it transitions to failed. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int startup(); /** * Shutdown. Sends request to close socket and transitions into * SERVICE_SHUTTING_DOWN state. When socket is closed transition to * SERVICE_SHUTDOWN occurs. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int shutdown(); /** * Send packet. Status of instance must be running. When packet is sent, * the @ref pending() method will return true until the Xrl sending the * packet has completed. * * @param dst_addr address to send packet. * @param dst_port port to send packet to. * @param rip_packet vector containing rip packet to be sent. * * @return false on immediately detectable failure, true otherwise. */ bool send(const Addr& dst_addr, uint16_t dst_port, const vector& rip_packet); /** * Return true if awaiting @ref send() completion, false otherwise. */ bool pending() const; /** * Get name of socket server used to instantiate socket. */ const string& socket_server() const { return _ss; } const string& socket_id() const { return _sid; } private: bool startup_socket(); bool request_open_bind_socket(); void open_bind_socket_cb(const XrlError& xe, const string* psid); bool request_ttl(); void ttl_cb(const XrlError& xe); bool request_no_loop(); void no_loop_cb(const XrlError& xe); bool request_socket_join(); void socket_join_cb(const XrlError& xe); bool request_socket_leave(); void socket_leave_cb(const XrlError& xe); void send_cb(const XrlError& xe); protected: XrlRouter& _xr; string _ss; // Socket Server Target Name string _sid; // Unicast Socket id bool _pending; }; #endif // __XRL_PORT_IO_HH__ xorp/rip/xorp_rip_main.cc0000664000076400007640000002156011540224235015631 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "rip_module.h" #include "libxorp/xlog.h" #include "libxorp/eventloop.hh" #include "libxorp/ipv4.hh" #include "libxorp/ipv6.hh" #include "libxipc/finder_constants.hh" #include "libxipc/xrl_std_router.hh" #include "libfeaclient/ifmgr_xrl_mirror.hh" #include "rip/constants.hh" #include "rip/system.hh" #include "rip/xrl_config.hh" #include "rip/xrl_target_rip.hh" #include "rip/xrl_port_manager.hh" #include "rip/xrl_process_spy.hh" #include "rip/xrl_redist_manager.hh" #include "rip/xrl_rib_notifier.hh" #include "rip/xrl_target_rip.hh" #include "rip/xrl_target_ripng.hh" #ifdef HAVE_GETOPT_H #include #endif /** * @short Class specialized to determine correct type of XrlTarget for * addresses of type A. */ template struct XrlTarget {}; template <> struct XrlTarget { typedef XrlRipTarget Type; static const char* name() { return "rip"; } }; template <> struct XrlTarget { typedef XrlRipngTarget Type; static const char* name() { return "ripng"; } }; /** * Unary function class to build string containing ServiceBase objects * in a particular state. */ class service_names_in { public: service_names_in(ServiceStatus in_state, string& out) : _st(in_state), _o(out) {} void operator() (const ServiceBase* sb) { if (sb->status() != _st) return; if (_o.empty() == false) _o += ", "; _o += sb->service_name(); } protected: ServiceStatus _st; string& _o; }; /** * Class to receive state changes of services used by RIP and update * status in the xrl interface accordingly. */ template class Service2XrlTargetStatus : public ServiceChangeObserverBase { protected: void status_change(ServiceBase*, ServiceStatus, ServiceStatus) { update_status(); } public: Service2XrlTargetStatus(typename XrlTarget::Type& t) : _t(t), _st(SERVICE_READY) {} ~Service2XrlTargetStatus() { while (_services.empty() == false) { _services.front()->unset_observer(this); _services.pop_front(); } } bool add_service(ServiceBase* sb) { if (sb->set_observer(this) == XORP_OK) { _services.push_back(sb); update_status(); return true; } return false; } void remove_service(ServiceBase* sb) { list::iterator i; i = find(_services.begin(), _services.end(), sb); if (i != _services.end()) { _services.erase(i); } sb->unset_observer(this); } void update_status() { // XXX This is particularly inefficient and not very good. // Clear existing state mask and then fill with current // service states. This is then propagated to the XrlTarget. _st = ServiceStatus(0u); list::const_iterator i; for (i = _services.begin(); i != _services.end(); ++i) { ServiceBase *sb = *i; _st = ServiceStatus(_st | sb->status()); } if (_st & SERVICE_FAILED) { string svcs; for_each(_services.begin(), _services.end(), service_names_in(SERVICE_FAILED, svcs)); _t.set_status(PROC_FAILED, "Failure(s): " + svcs); return; } if (_st & SERVICE_SHUTTING_DOWN) { string svcs; for_each(_services.begin(), _services.end(), service_names_in(SERVICE_SHUTTING_DOWN, svcs)); _t.set_status(PROC_SHUTDOWN, "Awaiting shutdown of: " + svcs); return; } if (_st & (SERVICE_STARTING | SERVICE_READY)) { string svcs; for_each(_services.begin(), _services.end(), service_names_in(SERVICE_STARTING, svcs)); _t.set_status(PROC_STARTUP, "Awaiting start up of: " + svcs); return; } XLOG_ASSERT(_st == SERVICE_RUNNING); _t.set_status(PROC_READY, ""); } bool have_status(ServiceStatus st) { return _st & st; } protected: typename XrlTarget::Type& _t; list _services; ServiceStatus _st; }; /** * Extract Host and port from either host string or host colon port string, eg * "host" or ":". * * @param host_colon_port string to be scanned [in]. * @param host host component of scanned string [out]. * @param port port component of scanned string [out]. * @return true on success, false if port value is invalid. */ static bool parse_finder_args(const string& host_colon_port, string& host, uint16_t& port) { string::size_type sp = host_colon_port.find(":"); if (sp == string::npos) { host = host_colon_port; // Do not set port, by design it has default finder port value. } else { host = string(host_colon_port, 0, sp); string s_port = string(host_colon_port, sp + 1, 14); uint32_t t_port = atoi(s_port.c_str()); if (t_port == 0 || t_port > 65535) { XLOG_ERROR("Finder port %u is not in range 1--65535.\n", XORP_UINT_CAST(t_port)); return false; } port = (uint16_t)t_port; } return true; } static void usage(const char* name) { fprintf(stderr, "Usage: %s [options]\n", name); fprintf(stderr, "Options:\n"); fprintf(stderr, " -F [:] Specify Finder host and port\n"); fprintf(stderr, " -h Display this information\n"); } /** * Class implementing RIP body. */ template class XorpRip { public: typedef typename XrlTarget::Type XrlTargetType; public: XorpRip(); int main(int argc, char* const argv[]); protected: void run(const string& finder_host, uint16_t finder_port); }; template XorpRip::XorpRip() { } template void XorpRip::run(const string& finder_host, uint16_t finder_port) { XorpUnexpectedHandler catch_all(xorp_unexpected_handler); try { EventLoop e; System rip_system(e); XrlStdRouter xsr(e, XrlTarget::name(), finder_host.c_str(), finder_port); XrlProcessSpy xps(xsr); IfMgrXrlMirror ixm(e, xrl_fea_name(), FinderConstants::FINDER_DEFAULT_HOST(), FinderConstants::FINDER_DEFAULT_PORT()); XrlPortManager xpm(rip_system, xsr, ixm); XrlRedistManager xrm(rip_system); XrlTargetType xrlt(e, xsr, xps, xpm, xrm, rip_system); wait_until_xrl_router_is_ready(e, xsr); Service2XrlTargetStatus smon(xrlt); // Start up process spy xps.startup(); smon.add_service(&xps); XrlRibNotifier xn(e, rip_system.route_db().update_queue(), xsr); xn.startup(); smon.add_service(&xn); // Start up interface mirror ixm.startup(); smon.add_service(&ixm); // Start up xrl port manager xpm.startup(); smon.add_service(&xpm); // Start up xrl redist manager xrm.startup(); smon.add_service(&xrm); while (xorp_do_run && (smon.have_status(SERVICE_FAILED) == false) && (xsr.failed() == false)) { e.run(); } xps.shutdown(); xn.shutdown(); ixm.shutdown(); xpm.shutdown(); xrm.shutdown(); bool flag(false); XorpTimer t = e.set_flag_after_ms(5000, &flag); while (flag == false && smon.have_status( ServiceStatus(~(SERVICE_FAILED | SERVICE_SHUTDOWN)))) { e.run(); } smon.remove_service(&xps); smon.remove_service(&xn); smon.remove_service(&ixm); smon.remove_service(&xpm); rip_system.route_db().flush_routes(); } catch (...) { xorp_catch_standard_exceptions(); } } template int XorpRip::main(int argc, char * const argv[]) { xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); setup_dflt_sighandlers(); string finder_host = FinderConstants::FINDER_DEFAULT_HOST().str(); uint16_t finder_port = FinderConstants::FINDER_DEFAULT_PORT(); bool do_run = true; int ch; while ((ch = getopt(argc, argv, "F:")) != -1) { switch (ch) { case 'F': do_run = parse_finder_args(optarg, finder_host, finder_port); break; default: usage(argv[0]); do_run = false; } } if (do_run) run(finder_host, finder_port); // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); return 0; } int main(int argc, char* const argv[]) { #if defined(INSTANTIATE_IPV4) XorpRip xorp_rip; #elif defined(INSTANTIATE_IPV6) XorpRip xorp_rip; #endif return xorp_rip.main(argc, argv); } xorp/rip/output_updates.cc0000664000076400007640000000754611540224234016057 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "output_updates.hh" #include "packet_assembly.hh" #include "packet_queue.hh" #include "route_db.hh" template OutputUpdates::OutputUpdates(EventLoop& e, Port& port, PacketQueue& pkt_queue, RouteDB& rdb, const A& dst_addr, uint16_t dst_port) : OutputBase(e, port, pkt_queue, dst_addr, dst_port), _uq(rdb.update_queue()) { } template OutputUpdates::~OutputUpdates() { stop_output_processing(); } template void OutputUpdates::ffwd() { _uq.ffwd(_uq_iter); } template void OutputUpdates::output_packet() { ResponsePacketAssembler rpa(this->_port); RipPacket* pkt = new RipPacket(this->ip_addr(), this->ip_port()); rpa.packet_start(pkt); set*> added_routes; uint32_t done = 0; const RouteEntry* r = 0; for (r = _uq.get(_uq_iter); r != 0; r = _uq.next(_uq_iter)) { if (added_routes.find(r) != added_routes.end()) continue; pair p = this->_port.route_policy(*r); if (p.second > RIP_INFINITY) continue; RouteEntryOrigin* origin = NULL; string ifname, vifname; // XXX: not set, because not needed RouteEntry* copy = new RouteEntry(r->net(), p.first, ifname, vifname, p.second, origin, r->tag(), r->policytags()); bool accepted = this->do_filtering(copy); if (!accepted) { delete copy; continue; } rpa.packet_add_route(copy->net(), copy->nexthop(), copy->cost(), r->tag()); added_routes.insert(r); delete copy; done++; if (rpa.packet_full()) { _uq.next(_uq_iter); break; } } list*> auth_packets; if (done == 0 || rpa.packet_finish(auth_packets) == false) { // No routes added to packet or error finishing packet off. } else { typename list*>::iterator iter; for (iter = auth_packets.begin(); iter != auth_packets.end(); ++iter) { RipPacket* auth_pkt = *iter; this->_pkt_queue.enqueue_packet(auth_pkt); this->_port.counters().incr_triggered_updates(); this->incr_packets_sent(); } this->_port.push_packets(); } delete pkt; if (r != 0) { // Not finished with updates so set time to reschedule self this->_op_timer = this->_e.new_oneoff_after_ms(this->interpacket_gap_ms(), callback(this, &OutputUpdates::output_packet)); } else { // Finished with updates for this run. Do not set timer. } } template void OutputUpdates::start_output_processing() { if (_uq.reader_valid(_uq_iter) == false) { _uq_iter = _uq.create_reader(); } output_packet(); } template void OutputUpdates::stop_output_processing() { _uq.destroy_reader(_uq_iter); this->_op_timer.unschedule(); } // ---------------------------------------------------------------------------- // Instantiations #ifdef INSTANTIATE_IPV4 template class OutputUpdates; #endif #ifdef INSTANTIATE_IPV6 template class OutputUpdates; #endif xorp/rip/tests/0000775000076400007640000000000011540224235013612 5ustar greearbgreearbxorp/rip/tests/test_packets.cc0000664000076400007640000002034011540224234016610 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "rip_module.h" #include "libxorp/xlog.h" #include "libxorp/c_format.hh" #include "libxorp/eventloop.hh" #include "packets.hh" #include "test_utils.hh" #ifdef HAVE_GETOPT_H #include #endif /////////////////////////////////////////////////////////////////////////////// // // Constants // static const char *program_name = "test_packets"; static const char *program_description = "Test RIP packet operations"; static const char *program_version_id = "0.1"; static const char *program_date = "April, 2003"; static const char *program_copyright = "See file LICENSE"; static const char *program_return_value = "0 on success, 1 if test error, 2 if internal error"; //---------------------------------------------------------------------------- // The test static int test_main() { // Static sizing tests x_static_assert(RipPacketHeader::SIZE == 4); x_static_assert(PacketRouteEntry::SIZE == 20); x_static_assert(RipPacketHeader::SIZE == RIPv2_MIN_PACKET_BYTES); x_static_assert(RipPacketHeader::SIZE + PacketRouteEntry::SIZE == RIPv2_MIN_AUTH_PACKET_BYTES); x_static_assert(PacketRouteEntry::SIZE == PlaintextPacketRouteEntry4::SIZE); x_static_assert(PacketRouteEntry::SIZE == MD5PacketRouteEntry4::SIZE); x_static_assert(MD5PacketTrailer::SIZE == 20); x_static_assert(PacketRouteEntry::SIZE == PacketRouteEntry::SIZE); // // Test packet header // { uint8_t h4[4] = { 1, 2, 0, 0 }; const RipPacketHeader rph(h4); if (rph.valid_command() == false) { verbose_log("Bad valid command check\n"); return 1; } h4[0] = 3; if (rph.valid_command() == true) { verbose_log("Bad valid command check\n"); return 1; } if (rph.valid_version(2) == false) { verbose_log("Bad version check\n"); return 1; } if (rph.valid_version(3) == true) { verbose_log("Bad version check\n"); return 1; } if (rph.valid_padding() == false) { verbose_log("Bad padding check\n"); return 1; } h4[3] = 1; if (rph.valid_padding() == true) { verbose_log("Bad padding check\n"); return 1; } h4[2] = 1; h4[3] = 0; if (rph.valid_padding() == true) { verbose_log("Bad padding check\n"); return 1; } } // // Test RIPv2 Route Entry // { uint16_t tag(1096); IPv4Net net(IPv4("10.0.10.0"), 24); IPv4 nh("10.0.10.1"); uint32_t metric(12); uint8_t r[20]; PacketRouteEntryWriter pre(r); pre.initialize(tag, net, nh, metric); uint8_t e[20] = { 0x00, 0x02, 0x04, 0x48, 0x0a, 0x00, 0x0a, 0x00, 0xff, 0xff, 0xff, 0x00, 0x0a, 0x00, 0x0a, 0x01, 0x00, 0x00, 0x00, 0x0c }; for (size_t i = 0; i < 20; i++) { if (e[i] != r[i]) { verbose_log("Expected packet data wrong at position %u\n", XORP_UINT_CAST(i)); return 1; } } if (pre.addr_family() != PacketRouteEntry::ADDR_FAMILY) { verbose_log("Bad address family accessor\n"); return 1; } else if (pre.tag() != tag) { verbose_log("Bad tag accessor\n"); return 1; } else if (pre.net() != net) { verbose_log("Bad net accessor\n"); return 1; } else if (pre.nexthop() != nh) { verbose_log("Bad nexthop accessor\n"); return 1; } else if (pre.metric() != metric) { verbose_log("Bad cost accessor\n"); return 1; } } // // Test Plaintext Password // { uint8_t r[20]; PlaintextPacketRouteEntry4Writer pre(r); pre.initialize("16 character password"); uint8_t e[20] = { 0xff, 0xff, 0x00, 0x02, '1', '6', ' ', 'c', 'h', 'a', 'r', 'a', 'c', 't', 'e', 'r', ' ', 'p', 'a', 's' }; for (size_t i = 0; i < 20; i++) { if (e[i] != r[i]) { verbose_log("Expected packet data wrong at position %u\n", XORP_UINT_CAST(i)); return 1; } } if (pre.password() != "16 character pas") { verbose_log("Password accessor wrong\n"); } pre.initialize("8 character"); uint8_t f[20] = { 0xff, 0xff, 0x00, 0x02, '8', ' ', 'c', 'h', 'a', 'r', 'a', 'c', 't', 'e', 'r', 0x0, 0x0, 0x0, 0x0, 0x0 }; for (size_t i = 0; i < 20; i++) { if (f[i] != r[i]) { verbose_log("Expected packet data wrong at position %u\n", XORP_UINT_CAST(i)); return 1; } } } // // Test MD5 Password // { uint8_t r[20]; MD5PacketRouteEntry4Writer pre(r); pre.initialize(0x7fee, 0xcc, 0x08, 0x12345678); uint8_t e[20] = { 0xff, 0xff, 0x00, 0x03, 0x7f, 0xee, 0xcc, 0x08, 0x12, 0x34, 0x56, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; for (size_t i = 0; i < 20; i++) { if (e[i] != r[i]) { verbose_log("Expected packet data wrong at position %u\n", XORP_UINT_CAST(i)); return 1; } } if (pre.addr_family() != MD5PacketRouteEntry4::ADDR_FAMILY) { verbose_log("bad address family accessor\n"); return 1; } else if (pre.auth_type() != MD5PacketRouteEntry4::AUTH_TYPE) { verbose_log("bad auth type accessor\n"); return 1; } else if (pre.auth_off() != 0x7fee) { verbose_log("bad packet bytes accessor\n"); return 1; } else if (pre.key_id() != 0xcc) { verbose_log("bad key id accessor\n"); return 1; } else if (pre.auth_bytes() != 0x08) { verbose_log("bad auth bytes accessor\n"); return 1; } else if (pre.seqno() != 0x12345678) { verbose_log("bad seqno accessor\n"); return 1; } } return 0; } /** * Print program info to output stream. * * @param stream the output stream the print the program info to. */ static void print_program_info(FILE *stream) { fprintf(stream, "Name: %s\n", program_name); fprintf(stream, "Description: %s\n", program_description); fprintf(stream, "Version: %s\n", program_version_id); fprintf(stream, "Date: %s\n", program_date); fprintf(stream, "Copyright: %s\n", program_copyright); fprintf(stream, "Return: %s\n", program_return_value); } /** * Print program usage information to the stderr. * * @param progname the name of the program. */ static void usage(const char* progname) { print_program_info(stderr); fprintf(stderr, "usage: %s [-v] [-h]\n", progname); fprintf(stderr, " -h : usage (this message)\n"); fprintf(stderr, " -v : verbose output\n"); } int main(int argc, char* const argv[]) { // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); int ch; while ((ch = getopt(argc, argv, "hv")) != -1) { switch (ch) { case 'v': set_verbose(true); break; case 'h': case '?': default: usage(argv[0]); xlog_stop(); xlog_exit(); if (ch == 'h') return (0); else return (1); } } argc -= optind; argv += optind; int rval = 0; XorpUnexpectedHandler x(xorp_unexpected_handler); try { rval = test_main(); } catch (...) { // Internal error xorp_print_standard_exceptions(); rval = 2; } // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); return rval; } xorp/rip/tests/test_route_walk.cc0000664000076400007640000002252411540224234017340 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "rip_module.h" #include "libxorp/xlog.h" #include "libxorp/c_format.hh" #include "libxorp/eventloop.hh" #include "libxorp/ipv4.hh" #include "libxorp/ipv4net.hh" #include "port.hh" #include "peer.hh" #include "route_db.hh" #include "system.hh" #include "test_utils.hh" #ifdef HAVE_GETOPT_H #include #endif /////////////////////////////////////////////////////////////////////////////// // // Constants // static const char *program_name = "test_route_walk"; static const char *program_description = "Test RIP route walking"; static const char *program_version_id = "0.1"; static const char *program_date = "July, 2003"; static const char *program_copyright = "See file LICENSE"; static const char *program_return_value = "0 on success, 1 if test error, 2 if internal error"; // ---------------------------------------------------------------------------- // Spoof Port that supports just a single Peer // template class SpoofPort : public Port { public: SpoofPort(PortManagerBase& pm, A addr) : Port(pm) { this->_peers.push_back(new Peer(*this, addr)); verbose_log("Constructing SpoofPort instance\n"); } ~SpoofPort() { verbose_log("Destructing SpoofPort instance\n"); while (this->_peers.empty() == false) { delete this->_peers.front(); this->_peers.pop_front(); } } }; // ---------------------------------------------------------------------------- // Type specific helpers template struct DefaultPeer { static A get(); }; template <> IPv4 DefaultPeer::get() { return IPv4("10.0.0.1"); } template <> IPv6 DefaultPeer::get() { return IPv6("10::1"); } // ---------------------------------------------------------------------------- // Spoof Port Manager instance support a single Spoof Port which in turn // contains a single Peer. // template class SpoofPortManager : public PortManagerBase { public: SpoofPortManager(System& s, const IfMgrIfTree& iftree) : PortManagerBase(s, iftree) { this->_ports.push_back(new SpoofPort(*this, DefaultPeer::get())); } ~SpoofPortManager() { while (!this->_ports.empty()) { delete this->_ports.front(); this->_ports.pop_front(); } } Port* the_port() { XLOG_ASSERT(this->_ports.size() == 1); return this->_ports.front(); } Peer* the_peer() { XLOG_ASSERT(this->_ports.size() == 1); XLOG_ASSERT(this->_ports.front()->peers().size() == 1); return this->_ports.front()->peers().front(); } }; /////////////////////////////////////////////////////////////////////////////// // // Walk tester class // static const IfMgrIfTree ift_dummy = IfMgrIfTree(); template class RouteWalkTester { public: RouteWalkTester() : _e(), _rip_system(_e), _pm(_rip_system, ift_dummy) { _pm.the_port()->constants().set_expiry_secs(3); _pm.the_port()->constants().set_deletion_secs(2); } ~RouteWalkTester() { RouteDB& rdb = _rip_system.route_db(); rdb.flush_routes(); } bool walk_routes(RouteWalker* rw, bool wait_on_expiring, uint32_t* done, uint32_t todo) { TimeVal now; _e.current_time(now); TimeVal ten_ms(0, 10000); verbose_log("walked routes = %u\n", XORP_UINT_CAST(*done)); rw->resume(); while (todo != 0) { if (rw->current_route() == 0) { verbose_log("Halting with %u routes done.\n", XORP_UINT_CAST(*done)); return false; } if (wait_on_expiring) { TimeVal delta = rw->current_route()->timer().expiry() - now; if (delta < ten_ms) { verbose_log("Pausing on route about to be expired " "or deleted (expiry in %d.%06d secs).\n", XORP_INT_CAST(delta.sec()), XORP_INT_CAST(delta.usec())); break; } } rw->next_route(); *done = *done + 1; todo--; } rw->pause(1); return true; } int run_test() { string ifname, vifname; // XXX: not set, because not needed const uint32_t n_routes = 20000; verbose_log("Creating routes for nets\n"); RouteDB& rdb = _rip_system.route_db(); set > nets; make_nets(nets, n_routes); TimeVal tv_add_start; _e.current_time(tv_add_start); for_each(nets.begin(), nets.end(), RouteInjector(rdb, A::ZERO(), ifname, vifname, 5, _pm.the_peer())); TimeVal tv_add_end; _e.current_time(tv_add_end); tv_add_end -= tv_add_start; verbose_log("Adding routes took %d.%06d secs\n", XORP_INT_CAST(tv_add_end.sec()), XORP_INT_CAST(tv_add_end.usec())); // Walk routes on 1ms timer // We make 2 passes over routes with 97 routes read per 1ms // Total time taken 2 * 20000 / 97 * 0.001 = 407ms uint32_t routes_done = 0; RouteWalker rw(rdb); XorpTimer t; for (int i = 0; i < 2; i++) { verbose_log("Starting full route walk %d\n", i); t = _e.new_periodic_ms(1, callback(this, &RouteWalkTester::walk_routes, &rw, false, &routes_done, static_cast(97u))); while (t.scheduled()) { _e.run(); if (routes_done > n_routes) { verbose_log("Walked more routes than exist!\n"); return 1; } } if (routes_done != n_routes) { verbose_log("Read %u routes, expected to read %u\n", XORP_UINT_CAST(routes_done), XORP_UINT_CAST(n_routes)); return 1; } routes_done = 0; rw.reset(); } // Routes start being deleted after 5 seconds so if we walk // slowly we have a reasonable chance of standing on a route as // it gets deleted. // // Read 10 routes every 30ms. // 20000 routes -> 20000 / 10 * 0.030 = 60 seconds // // Except it doesn't run that long as the routes are collapsing // verbose_log("Starting walk as routes are deleted.\n"); rw.reset(); routes_done = 0; t = _e.new_periodic_ms(30, callback(this, &RouteWalkTester::walk_routes, &rw, true, &routes_done, static_cast(10u))); while (t.scheduled()) { _e.run(); if (routes_done > n_routes) { verbose_log("Walked more routes than exist!\n"); return 1; } } verbose_log("Read %u routes, %u available at end.\n", XORP_UINT_CAST(routes_done), XORP_UINT_CAST(rdb.route_count())); rdb.flush_routes(); if (rdb.route_count() != 0) { verbose_log("Had routes left when none expected.\n"); return 1; } return 0; } protected: EventLoop _e; System _rip_system; SpoofPortManager _pm; }; /** * Print program info to output stream. * * @param stream the output stream the print the program info to. */ static void print_program_info(FILE *stream) { fprintf(stream, "Name: %s\n", program_name); fprintf(stream, "Description: %s\n", program_description); fprintf(stream, "Version: %s\n", program_version_id); fprintf(stream, "Date: %s\n", program_date); fprintf(stream, "Copyright: %s\n", program_copyright); fprintf(stream, "Return: %s\n", program_return_value); } /* * Print program usage information to the stderr. * * @param progname the name of the program. */ static void usage(const char* progname) { print_program_info(stderr); fprintf(stderr, "usage: %s [-v] [-h]\n", progname); fprintf(stderr, " -h : usage (this message)\n"); fprintf(stderr, " -v : verbose output\n"); } int main(int argc, char* const argv[]) { // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); int ch; while ((ch = getopt(argc, argv, "hv")) != -1) { switch (ch) { case 'v': set_verbose(true); break; case 'h': case '?': default: usage(argv[0]); xlog_stop(); xlog_exit(); if (ch == 'h') return (0); else return (1); } } argc -= optind; argv += optind; int rval = 0; XorpUnexpectedHandler x(xorp_unexpected_handler); try { { RouteWalkTester rwt4; rval = rwt4.run_test(); } { RouteWalkTester rwt6; rval |= rwt6.run_test(); } } catch (...) { // Internal error xorp_print_standard_exceptions(); rval = 2; } // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); return rval; } xorp/rip/tests/test_outputs.cc0000664000076400007640000004703111540224234016707 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "rip_module.h" #include "libxorp/xlog.h" #include "libxorp/c_format.hh" #include "libxorp/eventloop.hh" #include "libxorp/ipv4.hh" #include "libxorp/ipv6.hh" #include "libxorp/ipnet.hh" #include "auth.hh" #include "output_table.hh" #include "output_updates.hh" #include "port.hh" #include "peer.hh" #include "packet_queue.hh" #include "route_db.hh" #include "system.hh" #include "update_queue.hh" #include "test_utils.hh" #ifdef HAVE_GETOPT_H #include #endif /////////////////////////////////////////////////////////////////////////////// // // This test comprises of a RIP system with 2 ports injecting routes. One of // the ports has an associated Output class instance that generates packets // containing the routes. We test both the update packets and the unsolicted // response packets. // // +--- System ----+ // TestPort OtherPort // Routes ------> TestPeer OtherPeer <------ Routes // | // V // response packets (inspected) // // We look at the routes in the response packets and compare them against // what we'd expect to see against differing horizon policies. // /////////////////////////////////////////////////////////////////////////////// // // Generic Constants // static const char *program_name = "test_output_update"; static const char *program_description = "Test RIP Port processing"; static const char *program_version_id = "0.1"; static const char *program_date = "August, 2003"; static const char *program_copyright = "See file LICENSE"; static const char *program_return_value = "0 on success, 1 if test error, " "2 if internal error"; // ---------------------------------------------------------------------------- // Type specific helpers template struct DefaultPeer { static A get(); }; template struct OtherPeer { static A get(); }; template <> IPv4 DefaultPeer::get() { return IPv4("10.0.0.1"); } template <> IPv4 OtherPeer::get() { return IPv4("192.168.0.1"); } template <> IPv6 DefaultPeer::get() { return IPv6("10::1"); } template <> IPv6 OtherPeer::get() { return IPv6("1920:1680::1"); } // ---------------------------------------------------------------------------- // Spoof Port that supports just a single Peer // template class SpoofPort : public Port { public: SpoofPort(PortManagerBase& pm, A addr) : Port(pm) { this->_peers.push_back(new Peer(*this, addr)); // verbose_log("Constructing SpoofPort instance\n"); } ~SpoofPort() { // verbose_log("Destructing SpoofPort instance\n"); while (this->_peers.empty() == false) { delete this->_peers.front(); this->_peers.pop_front(); } } }; // ---------------------------------------------------------------------------- // BlockedPortIO // // This is a port IO class that reports it's always pending so packets // build up in the packet queue and never leave... template class BlockedPortIO : public PortIOBase { public: typedef A Addr; typedef PortIOUserBase PortIOUser; public: BlockedPortIO(PortIOUserBase& user) : PortIOBase(user, "if0", "vif0", A(DefaultPeer::get())) { } /** * Called by RIP Port instance. */ bool send(const Addr&, uint16_t, const vector&) { XLOG_FATAL("Called send inappropriately"); return true; } bool pending() const { return true; } private: }; // ---------------------------------------------------------------------------- // Spoof Port Manager instance support a single Spoof Port which in turn // contains a single Peer. // template class SpoofPortManager : public PortManagerBase { public: SpoofPortManager(System& s, const IfMgrIfTree& iftree) : PortManagerBase(s, iftree) { this->_ports.push_back(new SpoofPort(*this, DefaultPeer::get())); this->_ports.push_back(new SpoofPort(*this, OtherPeer::get())); } ~SpoofPortManager() { while (!this->_ports.empty()) { delete this->_ports.front(); this->_ports.pop_front(); } } Port* test_port() { return this->_ports.front(); } const Port* test_port() const { return this->_ports.front(); } Port* other_port() { return this->_ports.back(); } const Port* other_port() const { return this->_ports.back(); } Peer* test_peer() { return test_port()->peers().front(); } const Peer* test_peer() const { return test_port()->peers().front(); } Peer* other_peer() { return other_port()->peers().front(); } const Peer* other_peer() const { return other_port()->peers().front(); } }; // ---------------------------------------------------------------------------- // Response Reader - Really needed for IPv6, but IPv4 is trivial template struct ResponseReader { ResponseReader(const RipPacket* rp) : _rp(rp), _pe(0) { RipPacketHeader rph(_rp->header_ptr()); if (rph.command() != RipPacketHeader::RESPONSE) { verbose_log("Bad packet!\n"); _pe = ~0U; } } bool get(IPNet& n, A& nh, uint32_t& cost, uint32_t& tag); uint32_t packet_entry() const { return _pe; } protected: const RipPacket* _rp; uint32_t _pe; // Current packet entry A _nh6; // IPv6 only next hop }; template <> bool ResponseReader::get(IPNet& n, IPv4& nh, uint32_t& cost, uint32_t& tag) { const uint8_t* pre_ptr = _rp->route_entry_ptr(_pe); if (pre_ptr == NULL) return false; const PacketRouteEntry pre(pre_ptr); n = pre.net(); nh = pre.nexthop(); cost = pre.metric(); tag = pre.tag(); _pe++; return true; } template <> bool ResponseReader::get(IPNet& n, IPv6& nh, uint32_t& cost, uint32_t& tag) { for (;;) { const uint8_t* pre_ptr = _rp->route_entry_ptr(_pe); if (pre_ptr == NULL) return false; const PacketRouteEntry pre(pre_ptr); if (pre.is_nexthop()) { _nh6 = pre.nexthop(); _pe++; continue; } nh = _nh6; n = pre.net(); cost = pre.metric(); tag = pre.tag(); _pe++; return true; } } //----------------------------------------------------------------------------- // Horizon checkers template class HorizonValidatorBase { public: HorizonValidatorBase(const set >& tpn, const set >& opn) : _total_routes_seen(0), _test_peer_routes_seen(0), _tpn(tpn), _opn(opn) {} virtual ~HorizonValidatorBase() {} /* Check an individual response packet is valid */ virtual bool valid_response(const RipPacket* p) = 0; /* Final check responses valid */ virtual bool valid_in_sum() const = 0; uint32_t total_routes_seen() const { return _total_routes_seen; } uint32_t test_peer_routes_seen() const { return _test_peer_routes_seen; } protected: uint32_t _total_routes_seen; uint32_t _test_peer_routes_seen; const set >& _tpn; const set >& _opn; }; template class NoHorizonValidator : public HorizonValidatorBase { public: NoHorizonValidator(const set >& tpn, const set >& opn) : HorizonValidatorBase(tpn, opn) {} bool valid_response(const RipPacket* p) { IPNet n; A nh; uint32_t cost; uint32_t tag; ResponseReader rr(p); while (rr.get(n, nh, cost, tag) == true) { this->_total_routes_seen++; if (this->_tpn.find(n) != this->_tpn.end()) { this->_test_peer_routes_seen++; } else if (this->_opn.find(n) != this->_opn.end()) { // No-op } else { // Not a test peer net and not an other peer net // ==> it's bogus verbose_log("Failed Processing entry %u / %u %s cost %u\n", XORP_UINT_CAST(rr.packet_entry()), XORP_UINT_CAST(p->max_entries()), n.str().c_str(), XORP_UINT_CAST(cost)); return false; } } return true; } bool valid_in_sum() const { if (this->test_peer_routes_seen() != this->_tpn.size()) { verbose_log("Test routes seen (%u) does not match expected (%u)\n", XORP_UINT_CAST(this->test_peer_routes_seen()), XORP_UINT_CAST(this->_tpn.size())); return false; } verbose_log("total routes seen %u, test peer routes seen = %u\n", XORP_UINT_CAST(this->total_routes_seen()), XORP_UINT_CAST(this->test_peer_routes_seen())); return this->test_peer_routes_seen() * 2 == this->total_routes_seen(); } }; template class SplitHorizonValidator : public HorizonValidatorBase { public: SplitHorizonValidator(const set >& tpn, const set >& opn) : HorizonValidatorBase(tpn, opn) {} bool valid_response(const RipPacket* p) { IPNet n; A nh; uint32_t cost; uint32_t tag; ResponseReader rr(p); while (rr.get(n, nh, cost, tag) == true) { this->_total_routes_seen++; if (this->_opn.find(n) == this->_opn.end()) { verbose_log("Saw own or alien route with split horizon\n"); // ==> it's bogus verbose_log("Failed Processing entry %u / %u %s cost %u\n", XORP_UINT_CAST(rr.packet_entry()), XORP_UINT_CAST(p->max_entries()), n.str().c_str(), XORP_UINT_CAST(cost)); return false; } } return true; } bool valid_in_sum() const { if (this->test_peer_routes_seen() != 0) { verbose_log("Test peer routes seen (%u) does not match expected " "(%u)\n", XORP_UINT_CAST(this->test_peer_routes_seen()), XORP_UINT_CAST(0)); return false; } verbose_log("total routes seen %u, test peer routes seen = %u\n", XORP_UINT_CAST(this->total_routes_seen()), XORP_UINT_CAST(this->test_peer_routes_seen())); return this->total_routes_seen() == (uint32_t)this->_opn.size(); } }; template class PoisonReverseValidator : public HorizonValidatorBase { public: PoisonReverseValidator(const set >& tpn, const set >& opn) : HorizonValidatorBase(tpn, opn) {} bool valid_response(const RipPacket* p) { IPNet n; A nh; uint32_t cost; uint32_t tag; ResponseReader rr(p); while (rr.get(n, nh, cost, tag) == true) { this->_total_routes_seen++; if (this->_tpn.find(n) != this->_tpn.end() && cost == RIP_INFINITY) { this->_test_peer_routes_seen++; } else if (this->_opn.find(n) != this->_opn.end()) { // No-op } else { // Not a test peer net and not an other peer net // ==> it's bogus verbose_log("Failed Processing entry %u / %u %s cost %u\n", XORP_UINT_CAST(rr.packet_entry()), XORP_UINT_CAST(p->max_entries()), n.str().c_str(), XORP_UINT_CAST(cost)); return false; } } return true; } bool valid_in_sum() const { if (this->test_peer_routes_seen() != this->_tpn.size()) { verbose_log("Test routes seen (%u) does not match expected (%u)\n", XORP_UINT_CAST(this->test_peer_routes_seen()), XORP_UINT_CAST(this->_tpn.size())); return false; } verbose_log("total routes seen %u, test peer routes seen = %u\n", XORP_UINT_CAST(this->total_routes_seen()), XORP_UINT_CAST(this->test_peer_routes_seen())); return this->test_peer_routes_seen() * 2 == this->total_routes_seen(); } }; // ---------------------------------------------------------------------------- // OutputTester // // This is a bit nasty, the OutputClass is either OutputUpdates or OutputTable // class. These classes have the same methods and so it seems a waste to // write this code out twice. OutputClass is only referenced in one location // so it's not rocket science to comprehend this. // static const IfMgrIfTree ift_dummy = IfMgrIfTree(); template class OutputTester { public: OutputTester(const set >& test_peer_nets, const set >& other_peer_nets) : _e(), _rip_system(_e), _pm(_rip_system, ift_dummy), _tpn(test_peer_nets), _opn(other_peer_nets) { _pm.test_port()->constants().set_expiry_secs(10); _pm.test_port()->constants().set_deletion_secs(5); _pm.test_port()->set_advertise_default_route(false); _pm.other_port()->constants().set_expiry_secs(10); _pm.other_port()->constants().set_deletion_secs(5); _pm.test_port()->set_io_handler(new BlockedPortIO(*_pm.test_port()), true); _pm.other_port()->set_io_handler( new BlockedPortIO(*_pm.other_port()), true); } ~OutputTester() { RouteDB& rdb = _rip_system.route_db(); rdb.flush_routes(); } int run_test(RipHorizon horizon, HorizonValidatorBase& validator) { string ifname, vifname; // XXX: not set, because not needed _pm.test_port()->set_horizon(horizon); RouteDB& rdb = _rip_system.route_db(); PacketQueue op_out; // Output pkt qu. OutputClass ou(_e, *_pm.test_port(), op_out, rdb); // Output pkt gen ou.start(); verbose_log("Injecting routes from test peer.\n"); for (typename set >::const_iterator n = _tpn.begin(); n != _tpn.end(); n++) { RouteEntryOrigin* reo = _pm.test_peer(); if (rdb.update_route(*n, A::ZERO(), ifname, vifname, 5u, 0u, reo, PolicyTags(), false) == false) { verbose_log("Failed to add route for %s\n", n->str().c_str()); return 1; } } verbose_log("Injecting routes from other peer.\n"); for (typename set >::const_iterator n = this->_opn.begin(); n != this->_opn.end(); n++) { RouteEntryOrigin* reo = _pm.other_peer(); if (rdb.update_route(*n, A::ZERO(), ifname, vifname, 5u, 0u, reo, PolicyTags(), false) == false) { verbose_log("Failed to add route for %s\n", n->str().c_str()); return 1; } } bool timeout = false; XorpTimer tot = _e.set_flag_after_ms(10000, &timeout); ou.start(); while (ou.running() && timeout == false) { _e.run(); } verbose_log("%u bytes buffered in packet queue.\n", XORP_UINT_CAST(op_out.buffered_bytes())); if (timeout) { verbose_log("Timed out!\n"); return 1; } uint32_t cnt = 0; while (op_out.empty() == false) { if (validator.valid_response(op_out.head()) == false) { verbose_log("Failed on packet validation.\n"); return 1; } op_out.pop_head(); cnt++; } if (validator.valid_in_sum() == false) { verbose_log("Not valid in sum.\n"); return 1; } return 0; } protected: EventLoop _e; System _rip_system; SpoofPortManager _pm; const set >& _tpn; const set >& _opn; }; /** * Print program info to output stream. * * @param stream the output stream the print the program info to. */ static void print_program_info(FILE *stream) { fprintf(stream, "Name: %s\n", program_name); fprintf(stream, "Description: %s\n", program_description); fprintf(stream, "Version: %s\n", program_version_id); fprintf(stream, "Date: %s\n", program_date); fprintf(stream, "Copyright: %s\n", program_copyright); fprintf(stream, "Return: %s\n", program_return_value); } /* * Print program usage information to the stderr. * * @param progname the name of the program. */ static void usage(const char* progname) { print_program_info(stderr); fprintf(stderr, "usage: %s [-v] [-h]\n", progname); fprintf(stderr, " -h : usage (this message)\n"); fprintf(stderr, " -v : verbose output\n"); } // ---------------------------------------------------------------------------- // Injected Network state template int run_all_test_cases() { int rval = 0; static const uint32_t n_routes = 577; // Make one large collection of unique nets set > all_nets; make_nets(all_nets, 2 * n_routes); set > tpn; // networks associated with peer under test set > opn; // networks associated with other peer. // Split large collection into nets for tpn and opn for_each(all_nets.begin(), all_nets.end(), SplitNets(tpn, opn)); // // OutputUpdates class tests // { verbose_log("=== IPv%u No Horizon updates test ===\n", XORP_UINT_CAST(A::ip_version())); OutputTester > tester(tpn, opn); NoHorizonValidator nohv(tpn, opn); rval |= tester.run_test(NONE, nohv); if (rval) return rval; } { verbose_log("=== IPv%u Split Horizon updates test ===\n", XORP_UINT_CAST(A::ip_version())); OutputTester > tester(tpn, opn); SplitHorizonValidator shv(tpn, opn); rval |= tester.run_test(SPLIT, shv); if (rval) return rval; } { verbose_log("=== IPv%u Split Horizon Poison Reverse test ===\n", XORP_UINT_CAST(A::ip_version())); OutputTester > tester(tpn, opn); PoisonReverseValidator prv(tpn, opn); rval |= tester.run_test(SPLIT_POISON_REVERSE, prv); if (rval) return rval; } // // OutputTable class tests // { verbose_log("=== IPv%u No Horizon table test ===\n", XORP_UINT_CAST(A::ip_version())); OutputTester > tester(tpn, opn); NoHorizonValidator nohv(tpn, opn); rval |= tester.run_test(NONE, nohv); if (rval) return rval; } { verbose_log("=== IPv%u Split Horizon table test ===\n", XORP_UINT_CAST(A::ip_version())); OutputTester > tester(tpn, opn); SplitHorizonValidator shv(tpn, opn); rval |= tester.run_test(SPLIT, shv); if (rval) return rval; } { verbose_log("=== IPv%u Split Horizon Poison Reverse table test ===\n", XORP_UINT_CAST(A::ip_version())); OutputTester > tester(tpn, opn); PoisonReverseValidator prv(tpn, opn); rval |= tester.run_test(SPLIT_POISON_REVERSE, prv); } return rval; } int main(int argc, char* const argv[]) { // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); int ch; while ((ch = getopt(argc, argv, "hv")) != -1) { switch (ch) { case 'v': set_verbose(true); break; case 'h': case '?': default: usage(argv[0]); xlog_stop(); xlog_exit(); if (ch == 'h') return (0); else return (1); } } argc -= optind; argv += optind; int rval = 0; XorpUnexpectedHandler x(xorp_unexpected_handler); try { rval |= run_all_test_cases(); rval |= run_all_test_cases(); } catch (...) { // Internal error xorp_print_standard_exceptions(); rval = 2; } // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); return rval; } xorp/rip/tests/SConscript0000664000076400007640000000344711421137511015632 0ustar greearbgreearb# Copyright (c) 2009 XORP, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # # $XORP$ import os Import("env") env = env.Clone() env.AppendUnique(CPPPATH = [ '#', '$BUILDDIR', '$BUILDDIR/rip', '$BUILDDIR/rip/tests', ]) env.AppendUnique(LIBPATH = [ '$BUILDDIR/rip', '$BUILDDIR/libfeaclient', '$BUILDDIR/policy/backend', '$BUILDDIR/policy/common', '$BUILDDIR/xrl/interfaces', '$BUILDDIR/xrl/targets', '$BUILDDIR/libxipc', '$BUILDDIR/libproto', '$BUILDDIR/libxorp', '$BUILDDIR/libcomm', ]) env.AppendUnique(LIBS = [ 'xst_rip', 'xorp_rip', 'xorp_ripng', 'xif_rip', 'xif_finder_event_notifier', 'xorp_fea_client', 'xif_fea_ifmgr_mirror', 'xif_fea_ifmgr_replicator', 'xst_fea_ifmgr_mirror', 'xif_socket4', 'xorp_policy_backend', 'xorp_policy_common', 'xorp_ipc', 'xorp_core', 'xorp_comm', ]) simple_cpp_tests = [ 'auth', 'outputs', 'packets', 'request', 'route_walk', 'timers', 'update_queue', ] cpp_test_targets = [] for ct in simple_cpp_tests: cpp_test_targets.append(env.AutoTest(target = 'test_%s' % ct, source = 'test_%s.cc' % ct)) xorp/rip/tests/test_update_queue.cc0000664000076400007640000002343511540224235017655 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "rip_module.h" #include "libxorp/xlog.h" #include "libxorp/c_format.hh" #include "libxorp/eventloop.hh" #include "libxorp/ipv4.hh" #include "libxorp/ipv4net.hh" #include "port.hh" #include "peer.hh" #include "route_db.hh" #include "system.hh" #include "update_queue.hh" #include "test_utils.hh" #ifdef HAVE_GETOPT_H #include #endif /////////////////////////////////////////////////////////////////////////////// // // Constants // static const char *program_name = "test_updates"; static const char *program_description = "Test RIP update queue"; static const char *program_version_id = "0.1"; static const char *program_date = "July, 2003"; static const char *program_copyright = "See file LICENSE"; static const char *program_return_value = "0 on success, 1 if test error, 2 if internal error"; // ---------------------------------------------------------------------------- // Spoof Port that supports just a single Peer // template class SpoofPort : public Port { public: SpoofPort(PortManagerBase& pm, A addr) : Port(pm) { this->_peers.push_back(new Peer(*this, addr)); verbose_log("Constructing SpoofPort instance\n"); } ~SpoofPort() { verbose_log("Destructing SpoofPort instance\n"); while (this->_peers.empty() == false) { delete this->_peers.front(); this->_peers.pop_front(); } } }; // ---------------------------------------------------------------------------- // Type specific helpers template struct DefaultPeer { static A get(); }; template <> IPv4 DefaultPeer::get() { return IPv4("10.0.0.1"); } template <> IPv6 DefaultPeer::get() { return IPv6("10::1"); } // ---------------------------------------------------------------------------- // Spoof Port Manager instance support a single Spoof Port which in turn // contains a single Peer. // template class SpoofPortManager : public PortManagerBase { public: SpoofPortManager(System& s, const IfMgrIfTree& iftree) : PortManagerBase(s, iftree) { this->_ports.push_back(new SpoofPort(*this, DefaultPeer::get())); } ~SpoofPortManager() { while (!this->_ports.empty()) { delete this->_ports.front(); this->_ports.pop_front(); } } Port* the_port() { XLOG_ASSERT(this->_ports.size() == 1); return this->_ports.front(); } Peer* the_peer() { XLOG_ASSERT(this->_ports.size() == 1); XLOG_ASSERT(this->_ports.front()->peers().size() == 1); return this->_ports.front()->peers().front(); } }; static const IfMgrIfTree ift_dummy = IfMgrIfTree(); template class UpdateQueueTester { public: UpdateQueueTester(EventLoop& e) : _e(e), _rip_system(_e), _pm(_rip_system, ift_dummy) { _pm.the_port()->constants().set_expiry_secs(3); _pm.the_port()->constants().set_deletion_secs(2); } ~UpdateQueueTester() { RouteDB& rdb = _rip_system.route_db(); rdb.flush_routes(); UpdateQueue& uq = rdb.update_queue(); uq.flush(); } int run_test() { string ifname, vifname; // XXX: not set, because not needed const uint32_t n_routes = 20000; verbose_log("Running test for IPv%u\n", XORP_UINT_CAST(A::ip_version())); RouteDB& rdb = _rip_system.route_db(); UpdateQueue& uq = _rip_system.route_db().update_queue(); _fast_reader = uq.create_reader(); _slow_reader = uq.create_reader(); if (uq.updates_queued() != 0) { verbose_log("Have updates when none expected\n"); return 1; } verbose_log("Generating nets\n"); set > nets; make_nets(nets, n_routes); verbose_log("Creating routes for nets\n"); for_each(nets.begin(), nets.end(), RouteInjector(rdb, A::ZERO(), ifname, vifname, 5, _pm.the_peer())); if (uq.updates_queued() != n_routes) { verbose_log("%u updates queued, expected %u\n", XORP_UINT_CAST(uq.updates_queued()), XORP_UINT_CAST(n_routes)); return 1; } uint32_t n = 0; for (n = 0; n < n_routes; n++) { if (uq.get(_fast_reader) == 0) { verbose_log("Ran out of updates at %u.\n", XORP_UINT_CAST(n)); return 1; } uq.next(_fast_reader); } if (uq.get(_fast_reader) != 0) { verbose_log("Got an extra update.\n"); return 1; } bool expire = false; XorpTimer t = _e.set_flag_after_ms(6000, &expire); while (t.scheduled()) { _e.run(); } verbose_log("%u updates queued, expected %u\n", XORP_UINT_CAST(uq.updates_queued()), XORP_UINT_CAST(n_routes)); for (n = 0; n < n_routes; n++) { if (uq.get(_fast_reader) == 0) { verbose_log("Ran out of updates at %u.\n", XORP_UINT_CAST(n)); return 1; } uq.next(_fast_reader); } for (n = 0; n < 2 * n_routes; n++) { if (uq.get(_slow_reader) == 0) { verbose_log("Ran out of updates at %u.\n", XORP_UINT_CAST(n)); return 1; } uq.next(_slow_reader); } if (uq.get(_slow_reader) != 0) { verbose_log("Got an extra update.\n"); return 1; } uq.destroy_reader(_fast_reader); uq.destroy_reader(_slow_reader); if (uq.updates_queued() != 0) { verbose_log("Updates queued (%u) when no readers present\n", XORP_UINT_CAST(uq.updates_queued())); return 1; } _slow_reader = uq.create_reader(); _fast_reader = uq.create_reader(); verbose_log("Creating routes for nets (again)\n"); for (typename set >::const_iterator n = nets.begin(); n != nets.end(); ++n) { if (rdb.update_route(*n, A::ZERO(), ifname, vifname, 5, 0, _pm.the_peer(), PolicyTags(), false) == false) { verbose_log("Failed to add route for %s\n", n->str().c_str()); return 1; } } verbose_log("flushing and waiting for route updates\n"); uq.ffwd(_fast_reader); uq.flush(); if (uq.updates_queued() != 0) { verbose_log("Flushed and route count != 0\n"); return 1; } if (uq.get(_fast_reader) != 0) { verbose_log("Got an extra update.\n"); return 1; } if (uq.get(_slow_reader) != 0) { verbose_log("Got an extra update.\n"); return 1; } expire = false; t = _e.set_flag_after_ms(6000, &expire); while (t.scheduled()) { _e.run(); } for (n = 0; n < n_routes; n++) { if (uq.get(_slow_reader) == 0) { verbose_log("Ran out of updates at %u.\n", XORP_UINT_CAST(n)); return 1; } uq.next(_slow_reader); if (uq.get(_fast_reader) == 0) { verbose_log("Ran out of updates at %u.\n", XORP_UINT_CAST(n)); return 1; } uq.next(_fast_reader); } if (uq.get(_fast_reader) != 0) { verbose_log("Got an extra update.\n"); return 1; } if (uq.get(_slow_reader) != 0) { verbose_log("Got an extra update.\n"); return 1; } verbose_log("Success\n"); return 0; } protected: EventLoop& _e; System _rip_system; SpoofPortManager _pm; typename UpdateQueue::ReadIterator _fast_reader; typename UpdateQueue::ReadIterator _slow_reader; }; /** * Print program info to output stream. * * @param stream the output stream the print the program info to. */ static void print_program_info(FILE *stream) { fprintf(stream, "Name: %s\n", program_name); fprintf(stream, "Description: %s\n", program_description); fprintf(stream, "Version: %s\n", program_version_id); fprintf(stream, "Date: %s\n", program_date); fprintf(stream, "Copyright: %s\n", program_copyright); fprintf(stream, "Return: %s\n", program_return_value); } /* * Print program usage information to the stderr. * * @param progname the name of the program. */ static void usage(const char* progname) { print_program_info(stderr); fprintf(stderr, "usage: %s [-v] [-h]\n", progname); fprintf(stderr, " -h : usage (this message)\n"); fprintf(stderr, " -v : verbose output\n"); } int main(int argc, char* const argv[]) { // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); int ch; while ((ch = getopt(argc, argv, "hv")) != -1) { switch (ch) { case 'v': set_verbose(true); break; case 'h': case '?': default: usage(argv[0]); xlog_stop(); xlog_exit(); if (ch == 'h') return (0); else return (1); } } argc -= optind; argv += optind; int rval = 0; XorpUnexpectedHandler x(xorp_unexpected_handler); try { EventLoop e; UpdateQueueTester uqt4(e); rval |= uqt4.run_test(); UpdateQueueTester uqt6(e); rval |= uqt6.run_test(); } catch (...) { // Internal error xorp_print_standard_exceptions(); rval = 2; } // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); return rval; } xorp/rip/tests/test_timers.cc0000664000076400007640000002100711540224234016462 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "rip_module.h" #include "libxorp/xlog.h" #include "libxorp/c_format.hh" #include "libxorp/eventloop.hh" #include "libxorp/ipv4.hh" #include "libxorp/ipv4net.hh" #include "port.hh" #include "peer.hh" #include "route_db.hh" #include "system.hh" #include "test_utils.hh" #ifdef HAVE_GETOPT_H #include #endif /////////////////////////////////////////////////////////////////////////////// // // Constants // static const char *program_name = "test_timers"; static const char *program_description = "Test RIP timers and route scan"; static const char *program_version_id = "0.1"; static const char *program_date = "March, 2003"; static const char *program_copyright = "See file LICENSE"; static const char *program_return_value = "0 on success, 1 if test error, 2 if internal error"; static const uint32_t N_TEST_ROUTES = 32000; static bool print_twirl(void) { static const char t[] = { '\\', '|', '/', '-' }; static const size_t nt = sizeof(t) / sizeof(t[0]); static size_t n = 0; static char erase = '\0'; printf("%c%c", erase, t[n % nt]); fflush(stdout); n++; erase = '\b'; return true; } // ---------------------------------------------------------------------------- template struct Address { static A me(); static A peer(); }; template <> IPv4 Address::me() { return IPv4("10.0.0.1"); } template <> IPv4 Address::peer() { return IPv4("10.10.0.1"); } template <> IPv6 Address::me() { return IPv6("10::1"); } template <> IPv6 Address::peer() { return IPv6("1010::1"); } // ---------------------------------------------------------------------------- // Spoof Port that supports just a single Peer // template class SpoofPort : public Port { public: SpoofPort(PortManagerBase& pm, A addr) : Port(pm) { this->_peers.push_back(new Peer(*this, addr)); verbose_log("Constructing SpoofPort instance\n", XORP_UINT_CAST(A::ip_version())); } ~SpoofPort() { verbose_log("Destructing SpoofPort instance\n", XORP_UINT_CAST(A::ip_version())); while (this->_peers.empty() == false) { delete this->_peers.front(); this->_peers.pop_front(); } } }; // ---------------------------------------------------------------------------- // Spoof Port Manager instance support a single Spoof Port which in turn // contains a single Peer. // template class SpoofPortManager : public PortManagerBase { public: SpoofPortManager(System& s, const IfMgrIfTree& iftree) : PortManagerBase(s, iftree) { this->_ports.push_back(new SpoofPort(*this, Address::me())); } ~SpoofPortManager() { while (!this->_ports.empty()) { delete this->_ports.front(); this->_ports.pop_front(); } } Port* the_port() { XLOG_ASSERT(this->_ports.size() == 1); return this->_ports.front(); } Peer* the_peer() { XLOG_ASSERT(this->_ports.size() == 1); XLOG_ASSERT(this->_ports.front()->peers().size() == 1); return this->_ports.front()->peers().front(); } }; //---------------------------------------------------------------------------- // The test static const IfMgrIfTree ift_dummy = IfMgrIfTree(); template static int test_main() { string ifname, vifname; // XXX: not set, because not needed const uint32_t n_routes = N_TEST_ROUTES; set > nets; make_nets(nets, n_routes); EventLoop e; System rip_system(e); SpoofPortManager spm(rip_system, ift_dummy); RouteDB& rdb = rip_system.route_db(); Peer* peer = spm.the_peer(); // Fix up time constants for the impatient. spm.the_port()->constants().set_expiry_secs(3); spm.the_port()->constants().set_deletion_secs(2); for_each(nets.begin(), nets.end(), RouteInjector(rdb, Address::me(), ifname, vifname, 1, peer)); if (peer->route_count() != n_routes) { verbose_log("Routes lost (count %u != %u)\n", XORP_UINT_CAST(peer->route_count()), XORP_UINT_CAST(n_routes)); return 1; } e.timer_list().advance_time(); // XXX { // Quick route dump test TimeVal t1, t2; e.current_time(t1); vector::ConstDBRouteEntry> l; rdb.dump_routes(l); e.timer_list().advance_time(); // XXX e.current_time(t2); t2 -= t1; fprintf(stderr, "route db route dump took %d.%06d seconds\n", t2.sec(), t2.usec()); } { // Quick route dump test TimeVal t1, t2; e.current_time(t1); vector::Route*> l; peer->dump_routes(l); e.timer_list().advance_time(); // XXX e.current_time(t2); t2 -= t1; fprintf(stderr, "peer route dump took %d.%06d seconds\n", t2.sec(), t2.usec()); } { // Quick route dump test TimeVal t1, t2; e.current_time(t1); vector::ConstDBRouteEntry> l; rdb.dump_routes(l); e.timer_list().advance_time(); // XXX e.current_time(t2); t2 -= t1; fprintf(stderr, "route db route dump took %d.%06d seconds\n", t2.sec(), t2.usec()); } const PortTimerConstants& ptc = peer->port().constants(); uint32_t timeout_secs = ptc.expiry_secs() + ptc.deletion_secs() + 5; XorpTimer twirl; if (verbose()) twirl = e.new_periodic_ms(250, callback(print_twirl)); bool timeout = false; XorpTimer t = e.set_flag_after_ms(1000 * timeout_secs, &timeout); while (timeout == false) { size_t route_count = peer->route_count(); verbose_log("Route count = %u\n", XORP_UINT_CAST(route_count)); if (route_count == 0) break; e.run(); } if (peer->route_count()) { verbose_log("Routes did not clean up\n"); return 1; } return 0; } /** * Print program info to output stream. * * @param stream the output stream the print the program info to. */ static void print_program_info(FILE *stream) { fprintf(stream, "Name: %s\n", program_name); fprintf(stream, "Description: %s\n", program_description); fprintf(stream, "Version: %s\n", program_version_id); fprintf(stream, "Date: %s\n", program_date); fprintf(stream, "Copyright: %s\n", program_copyright); fprintf(stream, "Return: %s\n", program_return_value); } /** * Print program usage information to the stderr. * * @param progname the name of the program. */ static void usage(const char* progname) { print_program_info(stderr); fprintf(stderr, "usage: %s [-v] [-h]\n", progname); fprintf(stderr, " -h : usage (this message)\n"); fprintf(stderr, " -v : verbose output\n"); } int main(int argc, char* const argv[]) { // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); int ch; while ((ch = getopt(argc, argv, "hv")) != -1) { switch (ch) { case 'v': set_verbose(true); break; case 'h': case '?': default: usage(argv[0]); xlog_stop(); xlog_exit(); if (ch == 'h') return (0); else return (1); } } argc -= optind; argv += optind; int rval = 0; XorpUnexpectedHandler x(xorp_unexpected_handler); try { rval = test_main(); rval |= test_main(); } catch (...) { // Internal error xorp_print_standard_exceptions(); rval = 2; } // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); return rval; } xorp/rip/tests/test_auth.cc0000664000076400007640000003531611540224234016130 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "rip_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/c_format.hh" #include "libxorp/eventloop.hh" #include "libxorp/utils.hh" #include "auth.hh" #include "test_utils.hh" #ifdef HAVE_GETOPT_H #include #endif /////////////////////////////////////////////////////////////////////////////// // // Constants // static const char *program_name = "test_auth"; static const char *program_description = "Test RIP authentication operations"; static const char *program_version_id = "0.1"; static const char *program_date = "April, 2003"; static const char *program_copyright = "See file LICENSE"; static const char *program_return_value = "0 on success, 1 if test error, 2 if internal error"; // ---------------------------------------------------------------------------- // Utility functions /** * Build an authenticated RIP packet. * * @param pkt vector to hold raw packet. * @param ah authentication handler to be used. * @param n number of route entries to place in packet. * * @return 0 on success, 1 on failure. */ static int build_auth_packet(vector& pkt, AuthHandlerBase& ah, uint32_t n) { vector trailer; pkt.resize(RipPacketHeader::size() + (n + ah.head_entries()) * PacketRouteEntry::size()); RipPacketHeaderWriter rph(&pkt[0]); rph.initialize(RipPacketHeader::REQUEST, 2); for (uint32_t i = 0; i < n; i++) { uint32_t offset = RipPacketHeader::size() + (i + ah.head_entries()) * PacketRouteEntry::size(); PacketRouteEntryWriter p(&pkt[0] + offset); p.initialize(0, IPv4Net("10.0.0.0/8"), IPv4("172.11.100.1"), 3); } RipPacket rip_packet(IPv4::ZERO(), RIP_PORT, n + ah.head_entries()); rip_packet.data() = pkt; size_t n_routes = 0; list*> auth_packets; if ((ah.authenticate_outbound(rip_packet, auth_packets, n_routes) != true) || (n_routes != n)) { verbose_log("Unexpected outbound authentication failure: %s\n", ah.error().c_str()); return 1; } // // XXX: there should be only one copy of the authenticated packet, // and we don't care about it. // XLOG_ASSERT(auth_packets.size() == 1); delete_pointers_list(auth_packets); // Copy the modified data back to the original packet pkt = rip_packet.data(); return 0; } /** * Check an authenticated RIP packet. * * @param pkt raw packet to be checked. * @param ah authentication handler to be used to check packet. * @param n number of entries expected in packet. * @param expect_fail expect failure flag. * * @return 0 on success, 1 on failure. */ static int check_auth_packet(const vector& pkt, AuthHandlerBase& ah, uint32_t n, bool expect_fail = false) { const uint8_t* entries_ptr = NULL; uint32_t n_entries = 0; if (ah.authenticate_inbound(&pkt[0], pkt.size(), entries_ptr, n_entries, IPv4::ZERO(), false) == false) { if (expect_fail == false) { verbose_log("Unexpected failure (actual entries %u, " "expected %u) - %s\n", XORP_UINT_CAST(n_entries), XORP_UINT_CAST(n), ah.error().c_str()); return 1; } return 0; } if (n == 0) { if (entries_ptr != NULL) { verbose_log("Got an address for start of entries when no entries " "present in a packet.\n"); return 1; } return 0; } const uint8_t* exp0 = (&pkt[0] + RipPacketHeader::size()); exp0 += PacketRouteEntry::size() * ah.head_entries(); if (entries_ptr != exp0) { verbose_log("First entry in packet does not correspond with expected " "position\n"); return 1; } return 0; } /** * Dump a packet. */ void dump_binary_data(const vector& data) { static const uint32_t BPL = 8; // Bytes Per Line vector::size_type i = 0; while (i != data.size()) { fprintf(stdout, "%08x ", XORP_UINT_CAST(i)); string hex; string asc; int r = (data.size() - i) > BPL ? BPL : data.size() - i; while (r != 0) { if ((i & 7) == 4) hex += " "; hex += c_format("%02x", data[i]); asc += (xorp_isprint(data[i])) ? char(data[i]) : '.'; i++; r--; } hex += string(BPL, ' '); hex = string(hex, 0, 2 * BPL + 1); fprintf(stdout, "%s %s\n", hex.c_str(), asc.c_str()); } } /** * A MD5 rip packet captured on the wire. */ uint8_t rip_packet[] = { 0x2, 0x2, 0x0, 0x0, 0xff, 0xff, 0x0, 0x3, 0x0, 0xf4, 0x1, 0x14, 0x0, 0x0, 0x1, 0x13, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0xc0, 0x96, 0xba, 0x0, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0x0, 0x2, 0x0, 0x0, 0xc0, 0x96, 0xbb, 0xe0, 0xff, 0xff, 0xff, 0xfc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0x0, 0x2, 0x0, 0x0, 0xc0, 0x96, 0xbb, 0xf0, 0xff, 0xff, 0xff, 0xf8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0x0, 0x2, 0x0, 0x0, 0xc0, 0x96, 0xbb, 0xf8, 0xff, 0xff, 0xff, 0xf8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb, 0x0, 0x2, 0x0, 0x0, 0xc0, 0xa8, 0x3, 0x0, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0x0, 0x2, 0x0, 0x0, 0xc0, 0xa8, 0x4, 0x0, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0x0, 0x2, 0x0, 0x0, 0xc0, 0xa8, 0xfe, 0x1, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0x0, 0x2, 0x0, 0x0, 0xc0, 0xa8, 0xfe, 0x2, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0x0, 0x2, 0x0, 0x0, 0xc0, 0xa8, 0xfe, 0x3, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb, 0x0, 0x2, 0x0, 0x0, 0xc0, 0xa8, 0xfe, 0x4, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0x0, 0x2, 0x0, 0x0, 0xc0, 0xa8, 0xfe, 0x5, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xd, 0xff, 0xff, 0x0, 0x1, 0x2d, 0xaa, 0xa4, 0xba, 0x2e, 0xfd, 0x5c, 0xb, 0x25, 0x44, 0xb5, 0x98, 0xcd, 0x5f, 0x24, 0xab, }; /** * Check MD5 authentication against a saved packet. */ inline int check_saved_md5() { EventLoop e; MD5AuthHandler mah(e); string dummy_error_msg; mah.add_key(1, "bgp@icsi", TimeVal::ZERO(), TimeVal::MAXIMUM(), dummy_error_msg); vector pkt; for (uint32_t i = 0; i < sizeof(rip_packet); i++) pkt.push_back(rip_packet[i]); uint32_t n = 11; if (check_auth_packet(pkt, mah, n, false)) { verbose_log("Failed MD5 authentication with %u entries\n", XORP_UINT_CAST(n)); dump_binary_data(pkt); return 1; } return 0; } //---------------------------------------------------------------------------- // The test static int test_main() { string dummy_error_msg; // Static sizing tests x_static_assert(RipPacketHeader::SIZE == 4); x_static_assert(PacketRouteEntry::SIZE == 20); x_static_assert(RipPacketHeader::SIZE == RIPv2_MIN_PACKET_BYTES); x_static_assert(RipPacketHeader::SIZE + PacketRouteEntry::SIZE == RIPv2_MIN_AUTH_PACKET_BYTES); x_static_assert(PacketRouteEntry::SIZE == PlaintextPacketRouteEntry4::SIZE); x_static_assert(PacketRouteEntry::SIZE == MD5PacketRouteEntry4::SIZE); x_static_assert(MD5PacketTrailer::SIZE == 20); x_static_assert(PacketRouteEntry::SIZE == PacketRouteEntry::SIZE); vector pkt; // // Null Authentication Handler test // NullAuthHandler nah; for (uint32_t n = 0; n < nah.max_routing_entries(); n++) { if (build_auth_packet(pkt, nah, n)) { verbose_log("Failed to build null authentication scheme packet " "with %u entries.\n", XORP_UINT_CAST(n)); return 1; } if (check_auth_packet(pkt, nah, n, false)) { verbose_log("Failed null authentication with %u entries\n", XORP_UINT_CAST(n)); return 1; } // Add some extra data to break packet size. pkt.push_back(uint8_t(0)); if (check_auth_packet(pkt, nah, n, true)) { verbose_log("Null authentication passed broken packet " "with %u entries\n", XORP_UINT_CAST(n)); return 1; } } // // Plaintext Authentication Handler test // // Plaintext test three run throughs one without password, one // with password less than 16 characters, one with password > 16 // characters // PlaintextAuthHandler pah; for (uint32_t i = 0; i < 3; i++) { for (uint32_t n = 0; n < pah.max_routing_entries(); n++) { if (build_auth_packet(pkt, pah, n)) { verbose_log("Failed to build plaintext authentication scheme " "packet with %u entries.\n", XORP_UINT_CAST(n)); return 1; } if (check_auth_packet(pkt, pah, n, false)) { verbose_log("Failed plaintext authentication with " "%u entries\n", XORP_UINT_CAST(n)); return 1; } // Add some extra data to break packet size. pkt.push_back(uint8_t(0)); if (check_auth_packet(pkt, pah, n, true)) { verbose_log("Plaintext authentication passed broken packet " "with %u entries\n", XORP_UINT_CAST(n)); return 1; } if (n == 3 && verbose()) { verbose_log("Example Plaintext password packet.\n"); dump_binary_data(pkt); } } pah.set_key(pah.key() + string("A password")); } // xlog_enable(XLOG_LEVEL_INFO); // // MD5 Authentication Handler tests // if (0 != check_saved_md5()) return 1; EventLoop e; MD5AuthHandler mah(e); mah.add_key(1, "Hello World!", TimeVal::ZERO(), TimeVal::MAXIMUM(), dummy_error_msg); for (uint32_t n = 0; n < mah.max_routing_entries(); n++) { if (build_auth_packet(pkt, mah, n)) { verbose_log("Failed to build MD5 authentication scheme packet " "with %u entries.\n", XORP_UINT_CAST(n)); return 1; } if (check_auth_packet(pkt, mah, n, false)) { verbose_log("Failed MD5 authentication with %u entries\n", XORP_UINT_CAST(n)); dump_binary_data(pkt); return 1; } // Add some extra data to break packet size. pkt.push_back(uint8_t(0)); if (check_auth_packet(pkt, mah, n, true)) { verbose_log("MD5 authentication passed broken packet " "with %u entries\n", XORP_UINT_CAST(n)); dump_binary_data(pkt); return 1; } // Build other packets of same size and corrupt bytes in order // nb we have to build another packet otherwise we always fail // the sequence number test. for (vector::size_type c = 0; c != pkt.size(); c++) { if (build_auth_packet(pkt, mah, n)) { verbose_log("Failed to build MD5 authentication scheme packet " "with %u entries.\n", XORP_UINT_CAST(n)); dump_binary_data(pkt); return 1; } vector bad_pkt(pkt); bad_pkt[c] = bad_pkt[c] ^ 0x01; if (check_auth_packet(bad_pkt, mah, n, true)) { verbose_log("MD5 authentication passed corruption in byte " "%u with in packet %u entries\n", XORP_UINT_CAST(c), XORP_UINT_CAST(n)); dump_binary_data(bad_pkt); return 1; } } } // // Check removing the 1 MD5 key we have on ring works // mah.remove_key(1, dummy_error_msg); if (! mah.empty()) { verbose_log("Key removal failed\n"); return 1; } // // Add a selection of keys and check they timeout correctly // TimeVal now; e.current_time(now); uint32_t i; for (i = 0; i < 5; i++) { mah.add_key(i, "testing123", now, now + TimeVal(i, 0), dummy_error_msg); } mah.add_key(i, "testing123", now, TimeVal::MAXIMUM(), dummy_error_msg); bool stop_eventloop = false; XorpTimer to = e.set_flag_after(TimeVal(i, 0), &stop_eventloop); while (stop_eventloop == false) { e.run(); } if (mah.valid_key_chain().size() != 1) { verbose_log("Bogus key count: expected 1 key left, have %u\n", XORP_UINT_CAST(mah.valid_key_chain().size())); return 1; } return 0; } /** * Print program info to output stream. * * @param stream the output stream the print the program info to. */ static void print_program_info(FILE *stream) { fprintf(stream, "Name: %s\n", program_name); fprintf(stream, "Description: %s\n", program_description); fprintf(stream, "Version: %s\n", program_version_id); fprintf(stream, "Date: %s\n", program_date); fprintf(stream, "Copyright: %s\n", program_copyright); fprintf(stream, "Return: %s\n", program_return_value); } /** * Print program usage information to the stderr. * * @param progname the name of the program. */ static void usage(const char* progname) { print_program_info(stderr); fprintf(stderr, "usage: %s [-v] [-h]\n", progname); fprintf(stderr, " -h : usage (this message)\n"); fprintf(stderr, " -v : verbose output\n"); } int main(int argc, char* const argv[]) { // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); int ch; while ((ch = getopt(argc, argv, "hv")) != -1) { switch (ch) { case 'v': set_verbose(true); break; case 'h': case '?': default: usage(argv[0]); xlog_stop(); xlog_exit(); if (ch == 'h') return (0); else return (1); } } argc -= optind; argv += optind; int rval = 0; XorpUnexpectedHandler x(xorp_unexpected_handler); try { rval = test_main(); } catch (...) { // Internal error xorp_print_standard_exceptions(); rval = 2; } // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); verbose_log("Exit status %d\n", rval); return rval; } xorp/rip/tests/test_request.cc0000664000076400007640000002640311540224234016654 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "rip_module.h" #include "libxorp/xlog.h" #include "libxorp/c_format.hh" #include "libxorp/eventloop.hh" #include "libxorp/ipv4.hh" #include "libxorp/ipv4net.hh" #include "auth.hh" #include "port.hh" #include "peer.hh" #include "route_db.hh" #include "system.hh" #include "test_utils.hh" #ifdef HAVE_GETOPT_H #include #endif /////////////////////////////////////////////////////////////////////////////// // // Constants // static const char *program_name = "test_request"; static const char *program_description = "Test RIP handling route requests"; static const char *program_version_id = "0.1"; static const char *program_date = "July, 2003"; static const char *program_copyright = "See file LICENSE"; static const char *program_return_value = "0 on success, 1 if test error, 2 if internal error"; // ---------------------------------------------------------------------------- // Spoof Port that supports just a single Peer // template class SpoofPort : public Port { public: SpoofPort(PortManagerBase& pm, A addr) : Port(pm) { this->_peers.push_back(new Peer(*this, addr)); verbose_log("Constructing SpoofPort instance\n"); this->set_enabled(true); } ~SpoofPort() { verbose_log("Destructing SpoofPort instance\n"); while (this->_peers.empty() == false) { delete this->_peers.front(); this->_peers.pop_front(); } } }; // ---------------------------------------------------------------------------- // Type specific helpers template struct DefaultPeer { static A get(); }; template <> IPv4 DefaultPeer::get() { return IPv4("10.0.0.1"); } template <> IPv6 DefaultPeer::get() { return IPv6("10::1"); } // ---------------------------------------------------------------------------- // Spoof Port Manager instance support a single Spoof Port which in turn // contains a single Peer. // template class SpoofPortManager : public PortManagerBase { public: SpoofPortManager(System& s, const IfMgrIfTree& iftree) : PortManagerBase(s, iftree) { this->_ports.push_back(new SpoofPort(*this, DefaultPeer::get())); } ~SpoofPortManager() { while (!this->_ports.empty()) { delete this->_ports.front(); this->_ports.pop_front(); } } Port* the_port() { XLOG_ASSERT(this->_ports.size() == 1); return this->_ports.front(); } Peer* the_peer() { XLOG_ASSERT(this->_ports.size() == 1); XLOG_ASSERT(this->_ports.front()->peers().size() == 1); return this->_ports.front()->peers().front(); } }; // ---------------------------------------------------------------------------- // Test PortIO class template class SpoofPortIO : public PortIOBase { public: typedef A Addr; typedef PortIOUserBase PortIOUser; public: SpoofPortIO(PortIOUserBase& user) : PortIOBase(user, "if0", "vif0", IPv4("10.0.0.1")) { last_rip_send_flush(); } /** * Called by RIP Port instance. */ bool send(const Addr& addr, uint16_t port, const vector& rip_packet) { _lo_addr = addr; _lo_port = port; _lo_data = rip_packet; _pending = true; return true; } bool pending() const { return _lo_port != 0; } const Addr& last_rip_send_addr() const { return _lo_addr; } uint16_t last_rip_send_port() const { return _lo_port; } const vector& last_rip_send_data() const { return _lo_data; } void last_rip_send_flush() { _lo_addr = A::ZERO(); _lo_port = 0; _lo_data.resize(0); _pending = false; } private: vector _lo_data; Addr _lo_addr; uint16_t _lo_port; bool _pending; }; static const IPv4 REQUESTING_HOST = IPv4("10.0.100.1"); static const uint16_t REQUESTING_PORT = 1092; static const uint8_t ROUTE_COST = 5; static const IfMgrIfTree ift_dummy = IfMgrIfTree(); class RequestPacketTester { public: static const uint32_t INJECTED_ROUTES = 10; static const uint32_t REQUESTED_ROUTES = 25; public: RequestPacketTester() : _e(), _rip_system(_e), _pm(_rip_system, ift_dummy) { _portio = new SpoofPortIO(*_pm.the_port()); _pm.the_port()->set_io_handler(_portio, false); _portio->set_enabled(true); } ~RequestPacketTester() { _portio->set_enabled(false); delete _portio; RouteDB& rdb = _rip_system.route_db(); rdb.flush_routes(); } bool init_nets() { string ifname, vifname; // XXX: not set, because not needed RouteDB& rdb = _rip_system.route_db(); make_nets(_testnets, REQUESTED_ROUTES); set::const_iterator n = _testnets.begin(); for (uint32_t i = 0; i < INJECTED_ROUTES; i++) { if (rdb.update_route(*n, IPv4::ZERO(), ifname, vifname, ROUTE_COST, 0, _pm.the_peer(), PolicyTags(), false) == false) { verbose_log("Failed to add route for %s\n", n->str().c_str()); return false; } n++; } return true; } bool send_rip_route_queries() { vector buf; buf.resize(RipPacketHeader::SIZE + REQUESTED_ROUTES * PacketRouteEntry::size()); RipPacketHeaderWriter rph(&buf[0]); rph.initialize(RipPacketHeader::REQUEST, 2); set::const_iterator n = _testnets.begin(); for (uint32_t i = 0; i < REQUESTED_ROUTES; i++) { XLOG_ASSERT(n != _testnets.end()); uint32_t offset = RipPacketHeader::SIZE + i * PacketRouteEntry::size(); PacketRouteEntryWriter pre(&(buf[offset])); pre.initialize(0, *n, IPv4::ZERO(), 0); n++; } XLOG_ASSERT(_pm.the_port() != 0); _pm.the_port()->port_io_receive(REQUESTING_HOST, REQUESTING_PORT, &(buf[0]), buf.size()); return true; } bool check_response() { if (_portio->pending() == false) { verbose_log("No response packet sent by rip\n"); return false; } if (_portio->last_rip_send_addr() != REQUESTING_HOST) { verbose_log("Response was not sent to originator\n"); return false; } if (_portio->last_rip_send_port() != REQUESTING_PORT) { verbose_log("Response was not sent to originators port\n"); return false; } const vector buf = _portio->last_rip_send_data(); // Validate RIP packet header const RipPacketHeader rph(&buf[0]); if (rph.valid_command() == false) { verbose_log("Invalid command\n"); return false; } if (rph.valid_version(RipPacketHeader::IPv4_VERSION) == false) { verbose_log("Invalid version\n"); return false; } if (rph.valid_padding() == false) { verbose_log("Invalid padding\n"); return false; } if (rph.command() != RipPacketHeader::RESPONSE) { verbose_log("Not a response packet\n"); return false; } // Validate entries const uint8_t* pre_ptr = &buf[0] + RipPacketHeader::size(); uint32_t n_entries = buf.size() / PacketRouteEntry::size() - 1; if (n_entries > _testnets.size()) { verbose_log("Got more routes than requested (%u > %u).\n", XORP_UINT_CAST(n_entries), XORP_UINT_CAST(_testnets.size())); return false; } set::const_iterator ni = _testnets.begin(); for (uint32_t i = 0; i < n_entries; i++, pre_ptr += PacketRouteEntry::size()) { const PacketRouteEntry pre(pre_ptr); verbose_log("%s %s %u %u\n", pre.net().str().c_str(), pre.nexthop().str().c_str(), XORP_UINT_CAST(pre.metric()), pre.tag()); if (pre.addr_family() != PacketRouteEntry::ADDR_FAMILY) { verbose_log("Invalid address family in route entry %u\n", XORP_UINT_CAST(i)); return false; } if (*ni != pre.net()) { verbose_log("Mismatched net in route entry %u\n", XORP_UINT_CAST(i)); return false; } if (i < INJECTED_ROUTES) { if (pre.metric() != ROUTE_COST) { verbose_log("Metric changed.\n"); return false; } } else { if (pre.metric() != RIP_INFINITY) { verbose_log("Non-existant route with finite metric??\n"); } } ni++; } return true; } int run_test() { if (init_nets() == false) { return -1; } bool start_test = false; XorpTimer d = _e.set_flag_after_ms(1 * 1000, &start_test); while (start_test == false) { _e.run(); } if (send_rip_route_queries() == false) { return -1; } if (check_response() == false) { return -1; } return 0; } protected: EventLoop _e; System _rip_system; SpoofPortManager _pm; SpoofPortIO* _portio; set _testnets; }; /** * Print program info to output stream. * * @param stream the output stream the print the program info to. */ static void print_program_info(FILE *stream) { fprintf(stream, "Name: %s\n", program_name); fprintf(stream, "Description: %s\n", program_description); fprintf(stream, "Version: %s\n", program_version_id); fprintf(stream, "Date: %s\n", program_date); fprintf(stream, "Copyright: %s\n", program_copyright); fprintf(stream, "Return: %s\n", program_return_value); } /* * Print program usage information to the stderr. * * @param progname the name of the program. */ static void usage(const char* progname) { print_program_info(stderr); fprintf(stderr, "usage: %s [-v] [-h]\n", progname); fprintf(stderr, " -h : usage (this message)\n"); fprintf(stderr, " -v : verbose output\n"); } int main(int argc, char* const argv[]) { // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); int ch; while ((ch = getopt(argc, argv, "hv")) != -1) { switch (ch) { case 'v': set_verbose(true); break; case 'h': case '?': default: usage(argv[0]); xlog_stop(); xlog_exit(); if (ch == 'h') return (0); else return (1); } } argc -= optind; argv += optind; int rval = 0; XorpUnexpectedHandler x(xorp_unexpected_handler); try { RequestPacketTester rpt; rval = rpt.run_test(); } catch (...) { // Internal error xorp_print_standard_exceptions(); rval = 2; } // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); return rval; } xorp/rip/tests/test_utils.hh0000664000076400007640000000730211540224235016334 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/rip/test_utils.hh,v 1.16 2008/10/02 21:58:18 bms Exp $ #ifndef __RIP_TEST_UTILS_HH__ #define __RIP_TEST_UTILS_HH__ #include "libxorp/xorp.h" #include "libxorp/ipv4.hh" #include "libxorp/ipv6.hh" #include "libxorp/ipnet.hh" #include "peer.hh" #include "route_db.hh" template inline A random_addr(); /** * Generate a random IPv4 address. */ template <> IPv4 random_addr() { uint32_t h = htonl(xorp_random()); return IPv4(h); } /** * Generate a random IPv6 address. */ template <> IPv6 random_addr() { uint32_t x[4]; x[0] = htonl(xorp_random()); x[1] = htonl(xorp_random()); x[2] = htonl(xorp_random()); x[3] = htonl(xorp_random()); return IPv6(x); } /** * Generate a set of routes. */ template void make_nets(set >& nets, uint32_t n_nets) { // attempt at deterministic nets sequence while (nets.size() != n_nets) { A addr = random_addr(); IPNet net = IPNet(addr, 1 + xorp_random() % A::ADDR_BITLEN); nets.insert(net); } } /** * @short Unary function object to split routes evenly into 2 sets. */ template struct SplitNets : public unary_function { SplitNets(set >& a, set >& b) : _s1(a), _s2(b), _n(0) {} void operator() (const IPNet& net) { if (_n % 2) { _s1.insert(net); } else { _s2.insert(net); } _n++; } protected: set >& _s1; set >& _s2; uint32_t _n; }; /** * @short Unary function object to inject routes into a route database. */ template struct RouteInjector : public unary_function { RouteInjector(RouteDB& r, const A& my_addr, const string& ifname, const string& vifname, uint32_t cost, Peer* peer) : _r(r), _m(my_addr), _ifname(ifname), _vifname(vifname), _c(cost), _p(peer), _injected(0) {} void operator() (const IPNet& net) { if (_r.update_route(net, _m, _ifname, _vifname, _c, 0, _p, PolicyTags(), false) == true) { _injected++; } else { fprintf(stderr, "Failed to update %s\n", net.str().c_str()); } } uint32_t injected() const { return _injected; } protected: RouteDB& _r; A _m; string _ifname; string _vifname; uint32_t _c; Peer* _p; uint32_t _injected; }; /////////////////////////////////////////////////////////////////////////////// // // Verbosity level control // static bool s_verbose = false; bool verbose() { return s_verbose; } void set_verbose(bool v) { s_verbose = v; } #define verbose_log(x...) _verbose_log(__FILE__,__LINE__, x) #define _verbose_log(file, line, x...) \ do { \ if (verbose()) { \ printf("From %s:%d: ", file, line); \ printf(x); \ fflush(stdout); \ } \ } while(0) #endif // __RIP_TEST_UTILS_HH__ xorp/rip/xrl_rib_notifier.cc0000664000076400007640000002231311421137511016316 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING #include "rip_module.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/eventloop.hh" #include "libxipc/xrl_router.hh" #include "xrl/interfaces/rib_xif.hh" #include "constants.hh" #include "xrl_config.hh" #include "xrl_rib_notifier.hh" // ---------------------------------------------------------------------------- // Type specific function pointers. // // The following type specific declarations and instantiations hold // function pointers to methods in the XrlRibV0p1Client class. This // class contains similar methods for adding, updating, removing // routes for IPv4 and IPv6. The methods have the same forms but have // different names and take different address argument types. By // specializing just the function pointers, we avoid any // specialization of send related code in the XrlRibNotifier class // later on in this file. template struct Send { typedef bool (XrlRibV0p1Client::*AddIgpTable) (const char*, const string&, const string&, const string&, const bool&, const bool&, const XrlRibV0p1Client::AddIgpTable4CB&); typedef bool (XrlRibV0p1Client::*DeleteIgpTable) (const char*, const string&, const string&, const string&, const bool&, const bool&, const XrlRibV0p1Client::DeleteIgpTable4CB&); typedef bool (XrlRibV0p1Client::*AddRoute) (const char*, const string&, const bool&, const bool&, const IPNet&, const A&, const string&, const string&, const uint32_t&, const XrlAtomList&, const XrlRibV0p1Client::AddInterfaceRoute4CB&); typedef bool (XrlRibV0p1Client::*ReplaceRoute) (const char*, const string&, const bool&, const bool&, const IPNet&, const A&, const string&, const string&, const uint32_t&, const XrlAtomList&, const XrlRibV0p1Client::ReplaceInterfaceRoute4CB&); typedef bool (XrlRibV0p1Client::*DeleteRoute) (const char*, const string&, const bool&, const bool&, const IPNet&, const XrlRibV0p1Client::DeleteRoute4CB&); static AddIgpTable add_igp_table; static DeleteIgpTable delete_igp_table; static AddRoute add_route; static ReplaceRoute replace_route; static DeleteRoute delete_route; }; // // IPv4 Specialized Function pointers // #ifdef INSTANTIATE_IPV4 template <> Send::AddIgpTable Send::add_igp_table = &XrlRibV0p1Client::send_add_igp_table4; template <> Send::DeleteIgpTable Send::delete_igp_table = &XrlRibV0p1Client::send_delete_igp_table4; template <> Send::AddRoute Send::add_route = &XrlRibV0p1Client::send_add_interface_route4; template <> Send::ReplaceRoute Send::replace_route = &XrlRibV0p1Client::send_replace_interface_route4; template <> Send::DeleteRoute Send::delete_route = &XrlRibV0p1Client::send_delete_route4; #endif // INSTANTIATE_IPV4 // // IPv6 Specialized Function pointers // #ifdef INSTANTIATE_IPV6 template <> Send::AddIgpTable Send::add_igp_table = &XrlRibV0p1Client::send_add_igp_table6; template <> Send::DeleteIgpTable Send::delete_igp_table = &XrlRibV0p1Client::send_delete_igp_table6; template <> Send::AddRoute Send::add_route = &XrlRibV0p1Client::send_add_interface_route6; template <> Send::ReplaceRoute Send::replace_route = &XrlRibV0p1Client::send_replace_interface_route6; template <> Send::DeleteRoute Send::delete_route = &XrlRibV0p1Client::send_delete_route6; #endif // INSTANTIATE_IPV6 // ---------------------------------------------------------------------------- // XrlRibNotifier implementation template XrlRibNotifier::XrlRibNotifier(EventLoop& e, UpdateQueue& uq, XrlRouter& xr, uint32_t mf, uint32_t pms) : RibNotifierBase(e, uq, pms), ServiceBase("RIB Updater"), _xs(xr), _cname(xr.class_name()), _iname(xr.instance_name()), _max_inflight(mf), _inflight(0) { set_status(SERVICE_READY); } template XrlRibNotifier::XrlRibNotifier(EventLoop& e, UpdateQueue& uq, XrlSender& xs, const string& class_name, const string& instance_name, uint32_t mf, uint32_t pms) : RibNotifierBase(e, uq, pms), _xs(xs), _cname(class_name), _iname(instance_name), _max_inflight(mf), _inflight(0) { } template XrlRibNotifier::~XrlRibNotifier() { } template inline void XrlRibNotifier::incr_inflight() { _inflight++; XLOG_ASSERT(_inflight <= _max_inflight); } template inline void XrlRibNotifier::decr_inflight() { _inflight--; XLOG_ASSERT(_inflight <= _max_inflight); } template int XrlRibNotifier::startup() { XrlRibV0p1Client c(&_xs); bool ucast = true; bool mcast = false; if ((c.*Send::add_igp_table) (xrl_rib_name(), "rip", _cname, _iname, ucast, mcast, callback(this, &XrlRibNotifier::add_igp_cb)) == false) { XLOG_ERROR("Failed to send table creation request."); set_status(SERVICE_FAILED); return (XORP_ERROR); } set_status(SERVICE_STARTING); incr_inflight(); return (XORP_OK); } template void XrlRibNotifier::add_igp_cb(const XrlError& xe) { decr_inflight(); if (xe != XrlError::OKAY()) { XLOG_ERROR("add_igp failed: %s\n", xe.str().c_str()); set_status(SERVICE_FAILED); return; } this->start_polling(); set_status(SERVICE_RUNNING); } template int XrlRibNotifier::shutdown() { this->stop_polling(); set_status(SERVICE_SHUTTING_DOWN); XrlRibV0p1Client c(&_xs); bool ucast = true; bool mcast = false; if ((c.*Send::delete_igp_table) (xrl_rib_name(), "rip", _cname, _iname, ucast, mcast, callback(this, &XrlRibNotifier::delete_igp_cb)) == false) { XLOG_ERROR("Failed to send table creation request."); set_status(SERVICE_FAILED); return (XORP_ERROR); } incr_inflight(); return (XORP_OK); } template void XrlRibNotifier::delete_igp_cb(const XrlError& e) { decr_inflight(); if (e != XrlError::OKAY()) { set_status(SERVICE_FAILED); return; } set_status(SERVICE_SHUTDOWN); } template void XrlRibNotifier::send_add_route(const RouteEntry& re) { XrlRibV0p1Client c(&_xs); bool ok; if (_ribnets.find(re.net()) == _ribnets.end()) { _ribnets.insert(re.net()); ok = (c.*Send::add_route) (xrl_rib_name(), "rip", true, false, re.net(), re.nexthop(), re.ifname(), re.vifname(), re.cost(), re.policytags().xrl_atomlist(), callback(this, &XrlRibNotifier::send_route_cb)); } else { ok = (c.*Send::replace_route) (xrl_rib_name(), "rip", true, false, re.net(), re.nexthop(), re.ifname(), re.vifname(), re.cost(), re.policytags().xrl_atomlist(), callback(this, &XrlRibNotifier::send_route_cb)); } if (ok == false) { shutdown(); return; } incr_inflight(); } template void XrlRibNotifier::send_delete_route(const RouteEntry& re) { typename set >::iterator i = _ribnets.find(re.net()); if (i == _ribnets.end()) { debug_msg("Request to delete route to net %s that's not been passed" "to rib\n", re.net().str().c_str()); return; } else { _ribnets.erase(i); } XrlRibV0p1Client c(&_xs); if ((c.*Send::delete_route) (xrl_rib_name(), "rip", true, false, re.net(), callback(this, &XrlRibNotifier::send_route_cb)) == false) { shutdown(); return; } incr_inflight(); } template void XrlRibNotifier::send_route_cb(const XrlError& xe) { decr_inflight(); if (xe != XrlError::OKAY()) { XLOG_ERROR("Xrl error %s\n", xe.str().c_str()); } } template void XrlRibNotifier::updates_available() { XLOG_ASSERT(_inflight <= _max_inflight); for (const RouteEntry* r = this->_uq.get(this->_ri); r != 0; r = this->_uq.next(this->_ri)) { if (_inflight == _max_inflight) { break; } if (status() != SERVICE_RUNNING) { // If we're not running just skip any available updates. continue; } if ((r->origin() != NULL) && (r->origin()->is_rib_origin())) { // XXX: don't redistribute the RIB routes back to the RIB continue; } if (r->cost() < RIP_INFINITY) { send_add_route(*r); } else { send_delete_route(*r); } } } #ifdef INSTANTIATE_IPV4 template class XrlRibNotifier; #endif #ifdef INSTANTIATE_IPV6 template class XrlRibNotifier; #endif xorp/rip/tools/0000775000076400007640000000000011631507352013615 5ustar greearbgreearbxorp/rip/tools/common.cc0000664000076400007640000000756611540224235015425 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "rip/rip_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/eventloop.hh" #include "libxipc/xrl_std_router.hh" #include "common.hh" const char* default_xrl_target(uint32_t ip_version) { if (ip_version == 6) { return "ripng"; } else if (ip_version == 4) { return "rip"; } else { return 0; } } uint32_t rip_name_to_ip_version(const char* rip_name) { static const char m[] = "ripng"; size_t n_m = sizeof(m) / sizeof(m[0]); uint16_t i = 0; while (rip_name[i] == m[i]) { i++; if (i == n_m) break; } if (i == 3) return 4; if (i == n_m) return 6; return 0; } bool parse_finder_args(const string& host_colon_port, string& host, uint16_t& port) { string::size_type sp = host_colon_port.find(":"); if (sp == string::npos) { host = host_colon_port; // Do not set port, by design it has default finder port value. } else { host = string(host_colon_port, 0, sp); string s_port = string(host_colon_port, sp + 1, 14); uint32_t t_port = atoi(s_port.c_str()); if (t_port == 0 || t_port > 65535) { XLOG_ERROR("Finder port %u is not in range 1--65535.\n", XORP_UINT_CAST(t_port)); return false; } port = (uint16_t)t_port; } return true; } /** * Xrl Job Queue */ XrlJobQueue::XrlJobQueue(EventLoop& e, const string& finder_host, uint16_t finder_port, const string& tgtname) : _e(e), _fhost(finder_host), _fport(finder_port), _tgt(tgtname), _rtr(0), _rtr_poll_cnt(0) { set_status(SERVICE_READY); } XrlJobQueue::~XrlJobQueue() { delete _rtr; } int XrlJobQueue::startup() { string cls = c_format("%s-%u\n", xlog_process_name(), XORP_UINT_CAST(getpid())); _rtr = new XrlStdRouter(_e, cls.c_str(), _fhost.c_str(), _fport); _rtr->finalize(); set_status(SERVICE_STARTING); _rtr_poll = _e.new_periodic_ms(100, callback(this, &XrlJobQueue::xrl_router_ready_poll)); return (XORP_OK); } int XrlJobQueue::shutdown() { while (_jobs.empty() == false) { _jobs.pop_front(); } set_status(SERVICE_SHUTDOWN); return (XORP_OK); } void XrlJobQueue::dispatch_complete(const XrlError& xe, const XrlJobBase* cmd) { XLOG_ASSERT(_jobs.empty() == false); XLOG_ASSERT(_jobs.front().get() == cmd); if (xe != XrlError::OKAY()) { if (xe == XrlError::COMMAND_FAILED()) { cout << "Error: " << xe.note() << endl; } else { cout << xe.str() << endl; } shutdown(); return; } _jobs.pop_front(); if (_jobs.empty() == false) { process_next_job(); } else { shutdown(); } } bool XrlJobQueue::xrl_router_ready_poll() { if (_rtr->ready()) { process_next_job(); return false; } if (_rtr_poll_cnt++ > 50) { set_status(SERVICE_FAILED, "Could not contact XORP Finder process"); } return true; } void XrlJobQueue::process_next_job() { if (_jobs.front()->dispatch() == false) { set_status(SERVICE_FAILED, "Could not dispatch xrl"); } } xorp/rip/tools/show_peer_stats.cc0000664000076400007640000004147611421137511017342 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include #include "rip/rip_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/eventloop.hh" #include "libxorp/ipv4.hh" #include "libxorp/ipv6.hh" #include "libxorp/ref_ptr.hh" #include "libxorp/service.hh" #include "libxipc/xrl_std_router.hh" #include "xrl/interfaces/rip_xif.hh" #include "xrl/interfaces/ripng_xif.hh" #include "common.hh" #ifdef HAVE_GETOPT_H #include #endif static const char* NO_PEERS = "There are no known peers."; static const char* NO_PEERS_ON_ADDR = "There are no known peers on "; // ---------------------------------------------------------------------------- // Utility methods static void usage() { fprintf(stderr, "Usage: %s [options] (rip|ripng) [ []]\n", xlog_process_name()); fprintf(stderr, "Options:\n"); fprintf(stderr, "\t -1 " "Single line output (used by 'show rip peer')\n"); fprintf(stderr, "\t -F : " "Specify Finder host and port to use.\n"); fprintf(stderr, "\t -T " "Specify XrlTarget to query.\n\n"); exit(-1); } template static void print_peer_header(const A& peer, const string& ifn, const string& vifn, const A& addr) { cout << endl; cout << "* RIP statistics for peer " << peer.str() << " on " << ifn << " " << vifn << " " << addr.str() << endl << endl; } static void pretty_print_counters(const XrlAtomList& descriptions, const XrlAtomList& values, uint32_t last_active) { static const uint32_t COL1 = 32; static const uint32_t COL2 = 16; time_t when = static_cast(last_active); char whenbuf[12]; (void)strftime(&whenbuf[0], sizeof(whenbuf), "%H:%M:%S", gmtime(&when)); // Save flags ios::fmtflags fl = cout.flags(); cout << " "; cout << "Last Active at " << whenbuf << endl; cout.flags(ios::left); cout << " "; cout << setw(COL1) << "Counter" << setw(0) << " "; cout.flags(ios::right); cout << setw(COL2) << "Value" << endl; cout.flags(ios::left); cout << " "; cout << setw(COL1) << string(COL1, '-') << setw(0) << " "; cout.flags(ios::right); cout << setw(COL2) << string(COL2, '-') << endl; for (size_t i = 0; i != descriptions.size(); ++i) { const XrlAtom& d = descriptions.get(i); const XrlAtom& v = values.get(i); cout.flags(ios::left); cout << setw(0) << " "; // NB we use string.c_str() here as GCC's string ostream // renderer seems to ignore the field width. Formatting // ostreams is supremely fugly. cout << setw(COL1) << d.text().c_str(); cout << setw(0) << " "; cout.flags(ios::right); cout << setw(COL2) << v.uint32() << endl; } // Restore flags cout.flags(fl); } template static void pretty_print_counters_single_line(const XrlAtomList& descriptions, const XrlAtomList& values, uint32_t last_active, const A& peer, const string& ifn, const string& vifn) { time_t when = static_cast(last_active); char whenbuf[12]; (void)strftime(&whenbuf[0], sizeof(whenbuf), "%H:%M:%S", gmtime(&when)); // RIP sends the descriptions of each counter back, // we will need to scan the atom list for what we want. uint32_t rxcount = 0; for (size_t i = 0; i != descriptions.size(); ++i) { const XrlAtom& d = descriptions.get(i); const XrlAtom& v = values.get(i); if (0 == strcasecmp(d.text().c_str(), "Request Packets Received")) { rxcount = v.uint32(); break; } } ios::fmtflags fl = cout.flags(); cout.flags(ios::left); // XXX We are using C++ style formatting here. I would prefer to use // fprintf because you can just bang that out. This is somewhat tedious. cout << setw(17) << peer.str() << setw(4) << ifn << setw(1) << "/" << setw(12) << vifn << setw(8) << "Up" << setw(12) << rxcount << // receive count (not held) setw(12) << 0 << // transmit count XXX notyet setw(10) << (last_active == 0 ? "Never" : whenbuf) << endl; cout.flags(fl); } /** * Invoke Xrl to get peer stats on RIP address and pretty print result. */ class GetPeerStats4 : public XrlJobBase { public: GetPeerStats4(XrlJobQueue& jq, const string& ifname, const string& vifname, IPv4 addr, IPv4 peer, bool single_line = false, bool hide_errors = false) : XrlJobBase(jq), _ifn(ifname), _vifn(vifname), _a(addr), _p(peer), _single_line(single_line), _hide_errs(hide_errors) {} bool dispatch() { XrlRipV0p1Client cl(queue().sender()); return cl.send_get_peer_counters(queue().target().c_str(), _ifn, _vifn, _a, _p, callback(this, &GetPeerStats4::cmd_callback) ); } protected: void cmd_callback(const XrlError& xe, const XrlAtomList* descriptions, const XrlAtomList* values, const uint32_t* peer_last_active) { if (xe == XrlError::OKAY()) { if (_single_line) { pretty_print_counters_single_line(*descriptions, *values, *peer_last_active, _p, _ifn, _vifn); } else { print_peer_header(_p, _ifn, _vifn, _a); pretty_print_counters(*descriptions, *values, *peer_last_active); } queue().dispatch_complete(xe, this); } else if (_hide_errs) { // When invoked by GetAllPeerStats4 or GetPortPeerStats4 // we do not report an error. They queried RIP to get the // complete peers list and if we get here the specified // port has been garbage collected at the time it's stats // are queried. queue().dispatch_complete(XrlError::OKAY(), this); } else { queue().dispatch_complete(xe, this); } } protected: string _ifn; string _vifn; IPv4 _a; IPv4 _p; bool _single_line; bool _hide_errs; }; /** * Invoke Xrl to get all peers, which we then use to get the counters * for to pretty print result. */ class GetAllPeerStats4 : public XrlJobBase { public: GetAllPeerStats4(XrlJobQueue& jq, bool single_line = false) : XrlJobBase(jq), _single_line(single_line) { } bool dispatch() { XrlRipV0p1Client cl(queue().sender()); return cl.send_get_all_peers(queue().target().c_str(), callback(this, &GetAllPeerStats4::cmd_callback) ); } protected: void cmd_callback(const XrlError& xe, const XrlAtomList* peers, const XrlAtomList* ifnames, const XrlAtomList* vifnames, const XrlAtomList* addrs) { if (xe == XrlError::OKAY()) { if (peers->size() == 0) { cout << NO_PEERS << endl; } else { for (size_t i = 0; i < peers->size(); i++) { const IPv4& peer_addr = peers->get(i).ipv4(); const string& ifn = ifnames->get(i).text(); const string& vifn = vifnames->get(i).text(); const IPv4& addr = addrs->get(i).ipv4(); queue().enqueue( new GetPeerStats4(queue(), ifn, vifn, addr, peer_addr, _single_line, true) ); } } } else { cerr << xe.str() << endl; } queue().dispatch_complete(xe, this); } bool _single_line; }; /** * Invoke Xrl to get peers on if/vif/addr, which we then use to get * the counters for to pretty print result. */ class GetPortPeerStats4 : public XrlJobBase { public: GetPortPeerStats4(XrlJobQueue& jq, const string& ifname, const string& vifname, const IPv4& addr) : XrlJobBase(jq), _ifn(ifname), _vifn(vifname), _a(addr) {} bool dispatch() { XrlRipV0p1Client cl(queue().sender()); return cl.send_get_all_peers(queue().target().c_str(), callback(this, &GetPortPeerStats4::cmd_callback)); } protected: void cmd_callback(const XrlError& xe, const XrlAtomList* peers, const XrlAtomList* ifnames, const XrlAtomList* vifnames, const XrlAtomList* addrs) { if (xe == XrlError::OKAY()) { if (peers->size() == 0) { cout << NO_PEERS_ON_ADDR << _ifn << " " << _vifn << " " << _a.str() << endl; } else { for (size_t i = 0; i < peers->size(); i++) { const IPv4& peer_addr = peers->get(i).ipv4(); const string& ifn = ifnames->get(i).text(); const string& vifn = vifnames->get(i).text(); const IPv4& addr = addrs->get(i).ipv4(); if (ifn == _ifn && vifn == _vifn && _a == addr) { queue().enqueue( new GetPeerStats4(queue(), ifn, vifn, addr, peer_addr, false, true) ); } } } } queue().dispatch_complete(xe, this); } protected: string _ifn; string _vifn; IPv4 _a; }; /** * Invoke Xrl to get peer stats on RIP address and pretty print result. */ class GetPeerStats6 : public XrlJobBase { public: GetPeerStats6(XrlJobQueue& jq, const string& ifname, const string& vifname, IPv6 addr, IPv6 peer, bool single_line = false, bool hide_errors = false) : XrlJobBase(jq), _ifn(ifname), _vifn(vifname), _a(addr), _p(peer), _single_line(single_line), _hide_errs(hide_errors) {} bool dispatch() { XrlRipngV0p1Client cl(queue().sender()); return cl.send_get_peer_counters(queue().target().c_str(), _ifn, _vifn, _a, _p, callback(this, &GetPeerStats6::cmd_callback) ); } protected: void cmd_callback(const XrlError& xe, const XrlAtomList* descriptions, const XrlAtomList* values, const uint32_t* peer_last_active) { if (xe == XrlError::OKAY()) { if (_single_line) { pretty_print_counters_single_line(*descriptions, *values, *peer_last_active, _p, _ifn, _vifn); } else { print_peer_header(_p, _ifn, _vifn, _a); pretty_print_counters(*descriptions, *values, *peer_last_active); } queue().dispatch_complete(xe, this); } else if (_hide_errs) { // When invoked by GetAllPeerStats6 or GetPortPeerStats6 // we do not report an error. They queried RIP to get the // complete peers list and if we get here the specified // port has been garbage collected at the time it's stats // are queried. queue().dispatch_complete(XrlError::OKAY(), this); } else { queue().dispatch_complete(xe, this); } } protected: string _ifn; string _vifn; IPv6 _a; IPv6 _p; bool _single_line; bool _hide_errs; }; /** * Invoke Xrl to get all peers, which we then use to get the counters * for to pretty print result. */ class GetAllPeerStats6 : public XrlJobBase { public: GetAllPeerStats6(XrlJobQueue& jq, bool single_line = false) : XrlJobBase(jq), _single_line(single_line) {} bool dispatch() { XrlRipngV0p1Client cl(queue().sender()); return cl.send_get_all_peers(queue().target().c_str(), callback(this, &GetAllPeerStats6::cmd_callback) ); } protected: void cmd_callback(const XrlError& xe, const XrlAtomList* peers, const XrlAtomList* ifnames, const XrlAtomList* vifnames, const XrlAtomList* addrs) { if (xe == XrlError::OKAY()) { if (peers->size() == 0) { cout << NO_PEERS << endl; } else { for (size_t i = 0; i < peers->size(); i++) { const IPv6& peer_addr = peers->get(i).ipv6(); const string& ifn = ifnames->get(i).text(); const string& vifn = vifnames->get(i).text(); const IPv6& addr = addrs->get(i).ipv6(); queue().enqueue( new GetPeerStats6(queue(), ifn, vifn, addr, peer_addr, _single_line, true) ); } } } queue().dispatch_complete(xe, this); } bool _single_line; }; /** * Invoke Xrl to get peers on if/vif/addr, which we then use to get * the counters for to pretty print result. */ class GetPortPeerStats6 : public XrlJobBase { public: GetPortPeerStats6(XrlJobQueue& jq, const string& ifname, const string& vifname, const IPv6& addr) : XrlJobBase(jq), _ifn(ifname), _vifn(vifname), _a(addr) {} bool dispatch() { XrlRipngV0p1Client cl(queue().sender()); return cl.send_get_all_peers(queue().target().c_str(), callback(this, &GetPortPeerStats6::cmd_callback)); } protected: void cmd_callback(const XrlError& xe, const XrlAtomList* peers, const XrlAtomList* ifnames, const XrlAtomList* vifnames, const XrlAtomList* addrs) { if (xe == XrlError::OKAY()) { if (peers->size() == 0) { cout << NO_PEERS_ON_ADDR << _ifn << " " << _vifn << " " << _a.str() << endl; } else { for (size_t i = 0; i < peers->size(); i++) { const IPv6& peer_addr = peers->get(i).ipv6(); const string& ifn = ifnames->get(i).text(); const string& vifn = vifnames->get(i).text(); const IPv6& addr = addrs->get(i).ipv6(); if (ifn == _ifn && vifn == _vifn && _a == addr) { queue().enqueue( new GetPeerStats6(queue(), ifn, vifn, addr, peer_addr, false, true) ); } } } } queue().dispatch_complete(xe, this); } protected: string _ifn; string _vifn; IPv6 _a; }; // ---------------------------------------------------------------------------- // Main int main(int argc, char* const argv[]) { // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); try { bool do_single_line = false; bool do_run = true; string finder_host = FinderConstants::FINDER_DEFAULT_HOST().str(); uint16_t finder_port = FinderConstants::FINDER_DEFAULT_PORT(); string xrl_target; int ch; while ((ch = getopt(argc, argv, "1F:T:")) != -1) { switch (ch) { case '1': do_single_line = true; break; case 'F': do_run = parse_finder_args(optarg, finder_host, finder_port); break; case 'T': xrl_target = optarg; break; default: usage(); do_run = false; } } argc -= optind; argv += optind; if (argc == 0) { usage(); } uint32_t ip_version = rip_name_to_ip_version(argv[0]); if (ip_version == 0) { usage(); } argc -= 1; argv += 1; if (xrl_target.empty()) { const char* xt = default_xrl_target(ip_version); if (xt == 0) { usage(); } xrl_target = xt; } if (do_run) { EventLoop e; XrlJobQueue job_queue(e, finder_host, finder_port, xrl_target); if (do_single_line) { // Address Interface State Hello Rx Hello Tx Last Hello" << endl; ios::fmtflags fl = cout.flags(); cout.flags(ios::left); cout << setw(17) << " Address" << setw(17) << "Interface" << setw(8) << "State" << setw(12) << "Hello Rx" << setw(12) << "Hello Tx" << setw(10) << "Last Hello" << endl; cout.flags(fl); } if (argc == 4) { if (ip_version == 4) { job_queue.enqueue( new GetPeerStats4(job_queue, argv[0], argv[1], argv[2], argv[3], do_single_line) ); } else if (ip_version == 6) { job_queue.enqueue( new GetPeerStats6(job_queue, argv[0], argv[1], argv[2], argv[3], do_single_line) ); } } else if (argc == 3) { if (ip_version == 4) { job_queue.enqueue( new GetPortPeerStats4(job_queue, argv[0], argv[1], argv[2]) ); } else if (ip_version == 6) { job_queue.enqueue( new GetPortPeerStats6(job_queue, argv[0], argv[1], argv[2]) ); } } else if (argc == 0) { if (ip_version == 4) { job_queue.enqueue(new GetAllPeerStats4(job_queue, do_single_line)); } else if (ip_version == 6) { job_queue.enqueue(new GetAllPeerStats6(job_queue, do_single_line)); } } else { usage(); } job_queue.startup(); while (job_queue.status() != SERVICE_SHUTDOWN) { if (job_queue.status() == SERVICE_FAILED) { cerr << "Failed: " << job_queue.status_note() << endl; break; } e.run(); } } } catch (...) { xorp_print_standard_exceptions(); } // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); return 0; } xorp/rip/tools/ripng_announcer.cc0000664000076400007640000001523511540224235017314 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "rip/rip_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #ifdef HAVE_NET_IF_H #include #endif #include "libxorp/utils.hh" #include "libxorp/eventloop.hh" #include "libxorp/ipv4.hh" #include "libxorp/ipv6.hh" #include "libcomm/comm_api.h" #include "rip/auth.hh" #include "rip/packet_assembly.hh" #ifdef HAVE_GETOPT_H #include #endif template struct RipRoute { RipRoute() {} RipRoute(const IPNet& a_net, const A& a_nh, uint16_t a_cost, uint16_t a_tag) : net(a_net), nh(a_nh), cost(a_cost), tag(a_tag) {} IPNet net; A nh; uint16_t cost; uint16_t tag; }; static bool announce_routes(XorpFd fd, vector >* my_routes) { PacketAssemblerSpecState sp; ResponsePacketAssembler rpa(sp); vector >& rts = *my_routes; size_t n = my_routes->size(); size_t i = 0; while (i != n) { RipPacket pkt(IPv6::RIP2_ROUTERS(), 521); rpa.packet_start(&pkt); while (i != n && rpa.packet_full() == false) { rpa.packet_add_route(rts[i].net, rts[i].nh, rts[i].cost, rts[i].tag); i++; } list*> auth_packets; if (rpa.packet_finish(auth_packets) != true) break; if (pkt.data_bytes() == 0) break; list*>::iterator iter; for (iter = auth_packets.begin(); iter != auth_packets.end(); ++iter) { RipPacket* auth_pkt = *iter; sockaddr_in6 sai; auth_pkt->address().copy_out(sai); sai.sin6_port = htons(auth_pkt->port()); if (sendto(fd, XORP_BUF_CAST(auth_pkt->data_ptr()), auth_pkt->data_bytes(), 0, reinterpret_cast(&sai), sizeof(sai)) < 0) { cerr << "Write failed: " << strerror(errno) << endl; return false; } else { cout << "Packet sent" << endl; } } delete_pointers_list(auth_packets); } return true; } static void rip_announce(XorpFd fd, vector >& my_routes) { EventLoop e; XorpTimer t = e.new_periodic_ms(30 * 1000, callback(announce_routes, fd, &my_routes)); announce_routes(fd, &my_routes); while (t.scheduled()) { e.run(); } } static void originate_routes_from_file(const char* file, vector >& my_routes, IPv6 nh, uint16_t cost, uint16_t tag) { ifstream fin(file); if (!fin) { cerr << "Could not open nets file " << file << endl; return; } string l; while (fin >> l) { try { IPv6Net net(l.c_str()); my_routes.push_back(RipRoute(net, nh, cost, tag)); } catch (...) { } } fin.close(); } static XorpFd init_rip_socket(int if_num) { #ifdef HAVE_IPV6 in6_addr grp_addr; IPv6::RIP2_ROUTERS().copy_out(grp_addr); XorpFd fd = comm_bind_join_udp6(&grp_addr, if_num, htons(521), COMM_SOCK_ADDR_PORT_REUSE, COMM_SOCK_NONBLOCKING); if (!fd.is_valid()) { cerr << "comm_bind_join_udp6 failed" << endl; } if (comm_set_iface6(fd, if_num) != XORP_OK) { cerr << "comm_set_iface6 failed" << endl; comm_close(fd); fd.clear(); return fd; } if (comm_set_multicast_ttl(fd, 255) != XORP_OK) { cerr << "comm_set_multicast_ttl failed" << endl; comm_close(fd); fd.clear(); return fd; } if (comm_set_loopback(fd, 0) != XORP_OK) { cerr << "comm_set_loopback failed" << endl; comm_close(fd); fd.clear(); return fd; } return fd; #else cerr << "IPv6 support not found during build." << endl; XorpFd fd; return fd; UNUSED(if_num); #endif } static void short_usage() { cerr << "Use -h for more details. " << endl; } static void usage() { cerr << xlog_process_name() << " [options] -i -o " << endl; cerr << "Options:" << endl; cerr << " -c specify cost for nets in next ." << endl; cerr << " -i specify outbound interface." << endl; cerr << " -n specify nexthop for nets in next ." << endl; cerr << " -o specify file containing list of nets for announcement." << endl; cerr << " -t specify tag for nets in next ." << endl; cerr << " -h show this information." << endl; } int main(int argc, char* const argv[]) { // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); comm_init(); try { vector > my_routes; const char* if_name = ""; bool do_run = true; // Defaults uint16_t tag = 0; uint16_t cost = 1; IPv6 nh; int if_num = -1; int ch; while ((ch = getopt(argc, argv, "c:n:i:I:o:t:h")) != -1) { switch(ch) { case 'c': cost = atoi(optarg); break; case 'i': if_name = optarg; break; case 'I': if_num = atoi(optarg); break; case 'n': nh = IPv6(optarg); break; case 'o': originate_routes_from_file(optarg, my_routes, nh, cost, tag); break; case 't': tag = atoi(optarg); break; case 'h': default: usage(); do_run = false; } } if (do_run) { #ifdef HAVE_IF_NAMETOINDEX if_num = if_nametoindex(if_name); #endif if (if_num <= 0) { cerr << "Must specify a valid interface name with -i." << endl; short_usage(); } else if (my_routes.empty()) { cerr << "No routes to originate." << endl; short_usage(); } else { XorpFd fd = init_rip_socket(if_num); if (!fd.is_valid()) { rip_announce(fd, my_routes); comm_close(fd); } } } } catch (...) { xorp_print_standard_exceptions(); } comm_exit(); // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); return 0; } xorp/rip/tools/show_stats.cc0000664000076400007640000002722011421137511016316 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include #include "rip/rip_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/eventloop.hh" #include "libxorp/ipv4.hh" #include "libxorp/ipv6.hh" #include "libxorp/ref_ptr.hh" #include "libxorp/service.hh" #include "libxipc/xrl_std_router.hh" #include "xrl/interfaces/rip_xif.hh" #include "xrl/interfaces/ripng_xif.hh" #include "common.hh" #ifdef HAVE_GETOPT_H #include #endif // ---------------------------------------------------------------------------- // Forward declarations template static void enqueue_address_query(XrlJobQueue& jq, const string& ifname, const string& vifname, const A& address, bool brief); // ---------------------------------------------------------------------------- // Utility methods static void usage() { fprintf(stderr, "Usage: %s [options] (rip|ripng) [ ]\n", xlog_process_name()); fprintf(stderr, "Options:\n"); fprintf(stderr, "\t -F : " "Specify Finder host and port to use.\n"); fprintf(stderr, "\t -T " "Specify XrlTarget to query.\n\n"); fprintf(stderr, "\t -b " "Brief output."); exit(-1); } template static void print_header(const string& ifn, const string& vifn, const A& addr) { cout << endl; cout << "* RIP on " << ifn << " " << vifn << " " << addr.str() << endl; } static void pretty_print_status(const string& status) { cout << " Status: " << status << endl << endl; } static void pretty_print_counters(const XrlAtomList* descriptions, const XrlAtomList* values) { static const uint32_t COL1 = 32; static const uint32_t COL2 = 16; // Save flags ios::fmtflags fl = cout.flags(); cout.flags(ios::left); cout << " "; cout << setw(COL1) << "Counter" << setw(0) << " "; cout.flags(ios::right); cout << setw(COL2) << "Value" << endl; cout.flags(ios::left); cout << " "; cout << setw(COL1) << string(COL1, '-') << setw(0) << " "; cout.flags(ios::right); cout << setw(COL2) << string(COL2, '-') << endl; for (size_t i = 0; i != descriptions->size(); ++i) { const XrlAtom& d = descriptions->get(i); const XrlAtom& v = values->get(i); cout.flags(ios::left); cout << setw(0) << " "; // NB we use string.c_str() here as GCC's string ostream renderer // seems to ignore the field width. cout << setw(COL1) << d.text().c_str(); cout << setw(0) << " "; cout.flags(ios::right); cout << setw(COL2) << v.uint32() << endl; } // Restore flags cout.flags(fl); } // ---------------------------------------------------------------------------- // GetListAtom - extract argument of templatized type from XrlAtomList // [ This should probably be part of libxipc/xrl_atom_list.hh ] template struct GetListAtom { GetListAtom(const XrlAtomList& xal) : _xal(xal) {} GetListAtom(const XrlAtomList* pxal) : _xal(*pxal) {} const A& at(uint32_t index); private: const XrlAtomList& _xal; }; template <> inline const IPv4& GetListAtom::at(uint32_t index) { return _xal.get(index).ipv4(); } template <> const IPv6& GetListAtom::at(uint32_t index) { return _xal.get(index).ipv6(); } template <> const string& GetListAtom::at(uint32_t index) { return _xal.get(index).text(); } // ---------------------------------------------------------------------------- // Job classes and related template class XrlAddrJobBase : public XrlJobBase { public: XrlAddrJobBase(XrlJobQueue& jq, const string& ifname, const string& vifname, const A& addr) : XrlJobBase(jq), _ifname(ifname), _vifname(vifname), _addr(addr) {} const string& ifname() const { return _ifname; } const string& vifname()const { return _vifname; } const A& addr() const { return _addr; } protected: string _ifname; string _vifname; A _addr; }; /** * Class to print header. */ template class PrintAddress : public XrlAddrJobBase { public: PrintAddress(XrlJobQueue& jq, const string& ifname, const string& vifname, const A& addr) : XrlAddrJobBase(jq, ifname, vifname, addr) {} bool dispatch() { print_header(this->ifname(), this->vifname(), this->addr()); this->queue().dispatch_complete(XrlError::OKAY(), this); return true; } }; /** * Invoke Xrl to get address stats on RIP address and pretty print result. */ template class GetAddressStats : public XrlAddrJobBase { public: GetAddressStats(XrlJobQueue& jq, const string& ifname, const string& vifname, const A& addr) : XrlAddrJobBase(jq, ifname, vifname, addr) {} bool dispatch(); protected: void cmd_callback(const XrlError& xe, const XrlAtomList* descriptions, const XrlAtomList* values) { if (xe == XrlError::OKAY()) { pretty_print_counters(descriptions, values); } this->queue().dispatch_complete(xe, this); } }; template <> bool GetAddressStats::dispatch() { XrlRipV0p1Client cl(queue().sender()); return cl.send_get_counters(queue().target().c_str(), this->ifname(), this->vifname(), this->addr(), callback(this, &GetAddressStats::cmd_callback) ); } template <> bool GetAddressStats::dispatch() { XrlRipngV0p1Client cl(queue().sender()); return cl.send_get_counters(queue().target().c_str(), this->ifname(), this->vifname(), this->addr(), callback(this, &GetAddressStats::cmd_callback) ); } /** * Invoke Xrl to get address state on RIP address and pretty print result. */ template class GetAddressStatus : public XrlAddrJobBase { public: GetAddressStatus(XrlJobQueue& jq, const string& ifname, const string& vifname, const A& addr) : XrlAddrJobBase(jq, ifname, vifname, addr) {} bool dispatch(); protected: void cmd_callback(const XrlError& xe, const string* status) { if (xe == XrlError::OKAY()) { pretty_print_status(*status); } this->queue().dispatch_complete(xe, this); } }; template <> bool GetAddressStatus::dispatch() { XrlRipV0p1Client cl(queue().sender()); return cl.send_rip_address_status(queue().target().c_str(), this->ifname(), this->vifname(), this->addr(), callback(this, &GetAddressStatus::cmd_callback) ); } template <> bool GetAddressStatus::dispatch() { XrlRipngV0p1Client cl(queue().sender()); return cl.send_rip_address_status(queue().target().c_str(), this->ifname(), this->vifname(), this->addr(), callback(this, &GetAddressStatus::cmd_callback) ); } /** * Invoke Xrl to get all addresses, which we then use to get the counters * for to pretty print result. */ template class GetAllAddressStats : public XrlJobBase { public: GetAllAddressStats(XrlJobQueue& jq, bool brief) : XrlJobBase(jq), _brief(brief) {} bool dispatch(); protected: void cmd_callback(const XrlError& xe, const XrlAtomList* ifnames, const XrlAtomList* vifnames, const XrlAtomList* addrs) { if (xe == XrlError::OKAY()) { for (size_t i = 0; i < ifnames->size(); i++) { const string& ifn = ifnames->get(i).text(); const string& vifn = vifnames->get(i).text(); const A& addr = GetListAtom(addrs).at(i); enqueue_address_query(queue(), ifn, vifn, addr, _brief); } } queue().dispatch_complete(xe, this); } private: bool _brief; }; template <> bool GetAllAddressStats::dispatch() { XrlRipV0p1Client cl(queue().sender()); return cl.send_get_all_addresses(queue().target().c_str(), callback(this, &GetAllAddressStats::cmd_callback) ); } template <> bool GetAllAddressStats::dispatch() { XrlRipngV0p1Client cl(queue().sender()); return cl.send_get_all_addresses(queue().target().c_str(), callback(this, &GetAllAddressStats::cmd_callback) ); } template void enqueue_address_query(XrlJobQueue& jq, const string& ifname, const string& vifname, const A& addr, bool brief) { jq.enqueue(new PrintAddress(jq, ifname, vifname, addr)); jq.enqueue(new GetAddressStatus(jq, ifname, vifname, addr)); if (brief == false) jq.enqueue(new GetAddressStats(jq, ifname, vifname, addr)); } // ---------------------------------------------------------------------------- // Main int main(int argc, char* const argv[]) { // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); try { bool do_run = true; bool brief = false; string finder_host = FinderConstants::FINDER_DEFAULT_HOST().str(); uint16_t finder_port = FinderConstants::FINDER_DEFAULT_PORT(); string xrl_target; int ch; while ((ch = getopt(argc, argv, "bF:T:")) != -1) { switch (ch) { case 'b': brief = true; break; case 'F': do_run = parse_finder_args(optarg, finder_host, finder_port); break; case 'T': xrl_target = optarg; break; default: usage(); do_run = false; } } argc -= optind; argv += optind; if (argc == 0) { usage(); } uint32_t ip_version = rip_name_to_ip_version(argv[0]); if (ip_version == 0) { cerr << "Bad ip version (" << ip_version << ")" << endl; usage(); } argc -= 1; argv += 1; if (xrl_target.empty()) { const char* xt = default_xrl_target(ip_version); if (xt == 0) { cerr << "Bad xrl target (" << ip_version << ")" << endl; usage(); } xrl_target = xt; } if (do_run) { EventLoop e; XrlJobQueue job_queue(e, finder_host, finder_port, xrl_target); if (argc == 3) { if (ip_version == 4) { enqueue_address_query(job_queue, argv[0], argv[1], IPv4(argv[2]), brief); } else if (ip_version == 6) { enqueue_address_query(job_queue, argv[0], argv[1], IPv6(argv[2]), brief); } } else if (argc == 0) { if (ip_version == 4) { job_queue.enqueue(new GetAllAddressStats(job_queue, brief)); } else if (ip_version == 6) { job_queue.enqueue(new GetAllAddressStats(job_queue, brief)); } } else { cerr << "Expected (rip|ripng) (all|
)\n"; usage(); } job_queue.startup(); while (job_queue.status() != SERVICE_SHUTDOWN) { if (job_queue.status() == SERVICE_FAILED) { cerr << "Failed: " << job_queue.status_note() << endl; break; } e.run(); } } } catch (...) { xorp_print_standard_exceptions(); } // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); return 0; } xorp/rip/tools/SConscript0000664000076400007640000000365311631507352015636 0ustar greearbgreearb# Copyright (c) 2009-2011 XORP, Inc and Others # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # $XORP$ import os Import('env') env = env.Clone() env.AppendUnique(CPPPATH = [ '#', '$BUILDDIR', ]) env.AppendUnique(LIBPATH = [ '$BUILDDIR/libxorp', '$BUILDDIR/libcomm', '$BUILDDIR/libxipc', '$BUILDDIR/libproto', '$BUILDDIR/xrl/interfaces', '$BUILDDIR/xrl/targets', '.', ]) env.AppendUnique(LIBS = [ 'xif_rip', 'xif_ripng', 'xorp_ipc', 'xorp_core', 'xorp_proto', 'xorp_comm', ]) if (env.has_key('mingw') and env['mingw']): env.Append(LIBS = [ 'iphlpapi', # 'mprapi', 'regex', 'winmm', ]) env.Append(LIBS = ['xorp_core', 'crypto', 'ws2_32']) env.Replace(RPATH = [ env.Literal(env['xorp_tool_rpath']) ]) shpeersrcs = [ 'show_peer_stats.cc', 'common.cc' ] shstatssrcs = [ 'show_stats.cc', 'common.cc' ] shpeer = env.Program(target = 'rip_show_peer_stats', source = shpeersrcs) shstats = env.Program(target = 'rip_show_stats', source = shstatssrcs) env.Alias('install', env.InstallProgram(env['xorp_tooldir'], shpeer)) env.Alias('install', env.InstallProgram(env['xorp_tooldir'], shstats)) Default(shpeer, shstats) xorp/rip/tools/rip_announcer.cc0000664000076400007640000001503611540224235016766 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "rip/rip_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/utils.hh" #include "libxorp/eventloop.hh" #include "libxorp/ipv4.hh" #include "libxorp/ipv6.hh" #include "libcomm/comm_api.h" #include "rip/auth.hh" #include "rip/packet_assembly.hh" #ifdef HAVE_GETOPT_H #include #endif template struct RipRoute { RipRoute() {} RipRoute(const IPNet& a_net, const A& a_nh, uint16_t a_cost, uint16_t a_tag) : net(a_net), nh(a_nh), cost(a_cost), tag(a_tag) {} IPNet net; A nh; uint16_t cost; uint16_t tag; }; bool announce_routes(XorpFd fd, vector >* my_routes) { NullAuthHandler nah; PacketAssemblerSpecState sp(nah); ResponsePacketAssembler rpa(sp); vector >& rts = *my_routes; size_t n = my_routes->size(); size_t i = 0; while (i != n) { RipPacket pkt(IPv4::RIP2_ROUTERS(), 520); rpa.packet_start(&pkt); while (i != n && rpa.packet_full() == false) { rpa.packet_add_route(rts[i].net, rts[i].nh, rts[i].cost, rts[i].tag); i++; } list*> auth_packets; if (rpa.packet_finish(auth_packets) != true) break; if (pkt.data_bytes() == 0) break; list*>::iterator iter; for (iter = auth_packets.begin(); iter != auth_packets.end(); ++iter) { RipPacket* auth_pkt = *iter; sockaddr_in sai; auth_pkt->address().copy_out(sai); sai.sin_port = htons(auth_pkt->port()); if (sendto(fd, XORP_BUF_CAST(auth_pkt->data_ptr()), auth_pkt->data_bytes(), 0, reinterpret_cast(&sai), sizeof(sai)) < 0) { cerr << "Write failed: " << strerror(errno) << endl; return false; } else { cout << "Packet sent" << endl; } } delete_pointers_list(auth_packets); } return true; } static void fake_peer(XorpFd fd, uint32_t period, vector >& my_routes) { EventLoop e; XorpTimer t = e.new_periodic_ms(period * 1000, callback(announce_routes, fd, &my_routes)); announce_routes(fd, &my_routes); while (t.scheduled()) { e.run(); } } static void originate_routes_from_file(const char* file, vector >& my_routes, IPv4 nh, uint16_t cost, uint16_t tag) { ifstream fin(file); if (!fin) { cerr << "Could not open nets file " << file << endl; return; } string l; while (fin >> l) { try { IPv4Net net(l.c_str()); my_routes.push_back(RipRoute(net, nh, cost, tag)); } catch (...) { } } fin.close(); } static XorpFd init_rip_socket(IPv4 if_addr) { in_addr mcast_addr, join_if_addr; IPv4::RIP2_ROUTERS().copy_out(mcast_addr); if_addr.copy_out(join_if_addr); XorpFd fd = comm_bind_join_udp4(&mcast_addr, &join_if_addr, htons(520), COMM_SOCK_ADDR_PORT_DONTREUSE, COMM_SOCK_NONBLOCKING); if (!fd.is_valid()) { cerr << "Could not instantiate socket" << endl; } else if (comm_set_iface4(fd, &join_if_addr) != XORP_OK) { cerr << "comm_set_iface4 failed" << endl; comm_close(fd); fd.clear(); } else if (comm_set_multicast_ttl(fd, 1) != XORP_OK) { cerr << "comm_set_multicast_ttl failed" << endl; comm_close(fd); fd.clear(); } return fd; } static void short_usage() { cerr << "Use -h for more details. " << endl; } static void usage() { cerr << xlog_process_name() << " [options] -i -o " << endl; cerr << "Options:" << endl; cerr << " -c specify cost for nets in next ." << endl; cerr << " -i specify outbound interface." << endl; cerr << " -n specify nexthop for nets in next ." << endl; cerr << " -o specify file containing list of nets for announcement." << endl; cerr << " -p specify announcement period in seconds (default = 30)." << endl; cerr << " -t specify tag for nets in next ." << endl; cerr << " -h show this information." << endl; } int main(int argc, char* const argv[]) { // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); comm_init(); try { vector > my_routes; IPv4 if_addr; bool do_run = true; // Defaults uint16_t tag = 0; uint16_t cost = 1; uint32_t period = 30; IPv4 nh; int ch; while ((ch = getopt(argc, argv, "c:n:i:o:p:t:h")) != -1) { switch(ch) { case 'c': cost = atoi(optarg); break; case 'i': if_addr = IPv4(optarg); break; case 'n': nh = IPv4(optarg); break; case 'o': originate_routes_from_file(optarg, my_routes, nh, cost, tag); break; case 'p': period = strtoul(optarg, NULL, 10); if (period == 0) { period = 30; } break; case 't': tag = atoi(optarg); break; case 'h': default: usage(); do_run = false; } } if (do_run) { if (if_addr == IPv4::ZERO()) { cerr << "Must specify a valid interface address with -i." << endl; short_usage(); } else if (my_routes.empty()) { cerr << "No routes to originate." << endl; short_usage(); } else { XorpFd fd = init_rip_socket(if_addr); if (!fd.is_valid()) { fake_peer(fd, period, my_routes); comm_close(fd); fd.clear(); } } } } catch (...) { xorp_print_standard_exceptions(); } comm_exit(); // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); return 0; } xorp/rip/tools/xrl_rip_shell_funcs.sh0000664000076400007640000004324311421137511020214 0ustar greearbgreearbrip_common_get_target_name() { if [ $# -ne 0 ] ; then echo "Usage: rip_common_get_target_name" exit 1 fi XRL="finder://rip/common/0.1/get_target_name" call_xrl_wrapper -p all "${XRL}" } rip_common_get_version() { if [ $# -ne 0 ] ; then echo "Usage: rip_common_get_version" exit 1 fi XRL="finder://rip/common/0.1/get_version" call_xrl_wrapper -p all "${XRL}" } rip_common_get_status() { if [ $# -ne 0 ] ; then echo "Usage: rip_common_get_status" exit 1 fi XRL="finder://rip/common/0.1/get_status" call_xrl_wrapper -p all "${XRL}" } rip_common_shutdown() { if [ $# -ne 0 ] ; then echo "Usage: rip_common_shutdown" exit 1 fi XRL="finder://rip/common/0.1/shutdown" call_xrl_wrapper -p all "${XRL}" } rip_finder_event_observer_xrl_target_birth() { if [ $# -ne 2 ] ; then echo "Usage: rip_finder_event_observer_xrl_target_birth " exit 1 fi XRL="finder://rip/finder_event_observer/0.1/xrl_target_birth?target_class:txt=$1&target_instance:txt=$2" call_xrl_wrapper -p all "${XRL}" } rip_finder_event_observer_xrl_target_death() { if [ $# -ne 2 ] ; then echo "Usage: rip_finder_event_observer_xrl_target_death " exit 1 fi XRL="finder://rip/finder_event_observer/0.1/xrl_target_death?target_class:txt=$1&target_instance:txt=$2" call_xrl_wrapper -p all "${XRL}" } rip_policy_backend_configure() { if [ $# -ne 2 ] ; then echo "Usage: rip_policy_backend_configure " exit 1 fi XRL="finder://rip/policy_backend/0.1/configure?filter:u32=$1&conf:txt=$2" call_xrl_wrapper -p all "${XRL}" } rip_policy_backend_reset() { if [ $# -ne 1 ] ; then echo "Usage: rip_policy_backend_reset " exit 1 fi XRL="finder://rip/policy_backend/0.1/reset?filter:u32=$1" call_xrl_wrapper -p all "${XRL}" } rip_policy_backend_push_routes() { if [ $# -ne 0 ] ; then echo "Usage: rip_policy_backend_push_routes" exit 1 fi XRL="finder://rip/policy_backend/0.1/push_routes" call_xrl_wrapper -p all "${XRL}" } rip_policy_redist4_add_route4() { if [ $# -ne 6 ] ; then echo "Usage: rip_policy_redist4_add_route4 " exit 1 fi XRL="finder://rip/policy_redist4/0.1/add_route4?network:ipv4net=$1&unicast:bool=$2&multicast:bool=$3&nexthop:ipv4=$4&metric:u32=$5&policytags:list=$6" call_xrl_wrapper -p all "${XRL}" } rip_policy_redist4_delete_route4() { if [ $# -ne 3 ] ; then echo "Usage: rip_policy_redist4_delete_route4 " exit 1 fi XRL="finder://rip/policy_redist4/0.1/delete_route4?network:ipv4net=$1&unicast:bool=$2&multicast:bool=$3" call_xrl_wrapper -p all "${XRL}" } rip_rip_add_rip_address() { if [ $# -ne 3 ] ; then echo "Usage: rip_rip_add_rip_address " exit 1 fi XRL="finder://rip/rip/0.1/add_rip_address?ifname:txt=$1&vifname:txt=$2&addr:ipv4=$3" call_xrl_wrapper -p all "${XRL}" } rip_rip_remove_rip_address() { if [ $# -ne 3 ] ; then echo "Usage: rip_rip_remove_rip_address " exit 1 fi XRL="finder://rip/rip/0.1/remove_rip_address?ifname:txt=$1&vifname:txt=$2&addr:ipv4=$3" call_xrl_wrapper -p all "${XRL}" } rip_rip_set_rip_address_enabled() { if [ $# -ne 4 ] ; then echo "Usage: rip_rip_set_rip_address_enabled " exit 1 fi XRL="finder://rip/rip/0.1/set_rip_address_enabled?ifname:txt=$1&vifname:txt=$2&addr:ipv4=$3&enabled:bool=$4" call_xrl_wrapper -p all "${XRL}" } rip_rip_rip_address_enabled() { if [ $# -ne 3 ] ; then echo "Usage: rip_rip_rip_address_enabled " exit 1 fi XRL="finder://rip/rip/0.1/rip_address_enabled?ifname:txt=$1&vifname:txt=$2&addr:ipv4=$3" call_xrl_wrapper -p all "${XRL}" } rip_rip_set_cost() { if [ $# -ne 4 ] ; then echo "Usage: rip_rip_set_cost " exit 1 fi XRL="finder://rip/rip/0.1/set_cost?ifname:txt=$1&vifname:txt=$2&addr:ipv4=$3&cost:u32=$4" call_xrl_wrapper -p all "${XRL}" } rip_rip_cost() { if [ $# -ne 3 ] ; then echo "Usage: rip_rip_cost " exit 1 fi XRL="finder://rip/rip/0.1/cost?ifname:txt=$1&vifname:txt=$2&addr:ipv4=$3" call_xrl_wrapper -p all "${XRL}" } rip_rip_set_horizon() { if [ $# -ne 4 ] ; then echo "Usage: rip_rip_set_horizon " exit 1 fi XRL="finder://rip/rip/0.1/set_horizon?ifname:txt=$1&vifname:txt=$2&addr:ipv4=$3&horizon:txt=$4" call_xrl_wrapper -p all "${XRL}" } rip_rip_horizon() { if [ $# -ne 3 ] ; then echo "Usage: rip_rip_horizon " exit 1 fi XRL="finder://rip/rip/0.1/horizon?ifname:txt=$1&vifname:txt=$2&addr:ipv4=$3" call_xrl_wrapper -p all "${XRL}" } rip_rip_set_passive() { if [ $# -ne 4 ] ; then echo "Usage: rip_rip_set_passive " exit 1 fi XRL="finder://rip/rip/0.1/set_passive?ifname:txt=$1&vifname:txt=$2&addr:ipv4=$3&passive:bool=$4" call_xrl_wrapper -p all "${XRL}" } rip_rip_passive() { if [ $# -ne 3 ] ; then echo "Usage: rip_rip_passive " exit 1 fi XRL="finder://rip/rip/0.1/passive?ifname:txt=$1&vifname:txt=$2&addr:ipv4=$3" call_xrl_wrapper -p all "${XRL}" } rip_rip_set_accept_non_rip_requests() { if [ $# -ne 4 ] ; then echo "Usage: rip_rip_set_accept_non_rip_requests " exit 1 fi XRL="finder://rip/rip/0.1/set_accept_non_rip_requests?ifname:txt=$1&vifname:txt=$2&addr:ipv4=$3&accept:bool=$4" call_xrl_wrapper -p all "${XRL}" } rip_rip_accept_non_rip_requests() { if [ $# -ne 3 ] ; then echo "Usage: rip_rip_accept_non_rip_requests " exit 1 fi XRL="finder://rip/rip/0.1/accept_non_rip_requests?ifname:txt=$1&vifname:txt=$2&addr:ipv4=$3" call_xrl_wrapper -p all "${XRL}" } rip_rip_set_accept_default_route() { if [ $# -ne 4 ] ; then echo "Usage: rip_rip_set_accept_default_route " exit 1 fi XRL="finder://rip/rip/0.1/set_accept_default_route?ifname:txt=$1&vifname:txt=$2&addr:ipv4=$3&accept:bool=$4" call_xrl_wrapper -p all "${XRL}" } rip_rip_accept_default_route() { if [ $# -ne 3 ] ; then echo "Usage: rip_rip_accept_default_route " exit 1 fi XRL="finder://rip/rip/0.1/accept_default_route?ifname:txt=$1&vifname:txt=$2&addr:ipv4=$3" call_xrl_wrapper -p all "${XRL}" } rip_rip_set_advertise_default_route() { if [ $# -ne 4 ] ; then echo "Usage: rip_rip_set_advertise_default_route " exit 1 fi XRL="finder://rip/rip/0.1/set_advertise_default_route?ifname:txt=$1&vifname:txt=$2&addr:ipv4=$3&advertise:bool=$4" call_xrl_wrapper -p all "${XRL}" } rip_rip_advertise_default_route() { if [ $# -ne 3 ] ; then echo "Usage: rip_rip_advertise_default_route " exit 1 fi XRL="finder://rip/rip/0.1/advertise_default_route?ifname:txt=$1&vifname:txt=$2&addr:ipv4=$3" call_xrl_wrapper -p all "${XRL}" } rip_rip_set_route_timeout() { if [ $# -ne 4 ] ; then echo "Usage: rip_rip_set_route_timeout " exit 1 fi XRL="finder://rip/rip/0.1/set_route_timeout?ifname:txt=$1&vifname:txt=$2&addr:ipv4=$3&t_secs:u32=$4" call_xrl_wrapper -p all "${XRL}" } rip_rip_route_timeout() { if [ $# -ne 3 ] ; then echo "Usage: rip_rip_route_timeout " exit 1 fi XRL="finder://rip/rip/0.1/route_timeout?ifname:txt=$1&vifname:txt=$2&addr:ipv4=$3" call_xrl_wrapper -p all "${XRL}" } rip_rip_set_deletion_delay() { if [ $# -ne 4 ] ; then echo "Usage: rip_rip_set_deletion_delay " exit 1 fi XRL="finder://rip/rip/0.1/set_deletion_delay?ifname:txt=$1&vifname:txt=$2&addr:ipv4=$3&t_secs:u32=$4" call_xrl_wrapper -p all "${XRL}" } rip_rip_deletion_delay() { if [ $# -ne 3 ] ; then echo "Usage: rip_rip_deletion_delay " exit 1 fi XRL="finder://rip/rip/0.1/deletion_delay?ifname:txt=$1&vifname:txt=$2&addr:ipv4=$3" call_xrl_wrapper -p all "${XRL}" } rip_rip_set_request_interval() { if [ $# -ne 4 ] ; then echo "Usage: rip_rip_set_request_interval " exit 1 fi XRL="finder://rip/rip/0.1/set_request_interval?ifname:txt=$1&vifname:txt=$2&addr:ipv4=$3&t_secs:u32=$4" call_xrl_wrapper -p all "${XRL}" } rip_rip_request_interval() { if [ $# -ne 3 ] ; then echo "Usage: rip_rip_request_interval " exit 1 fi XRL="finder://rip/rip/0.1/request_interval?ifname:txt=$1&vifname:txt=$2&addr:ipv4=$3" call_xrl_wrapper -p all "${XRL}" } rip_rip_set_update_interval() { if [ $# -ne 4 ] ; then echo "Usage: rip_rip_set_update_interval " exit 1 fi XRL="finder://rip/rip/0.1/set_update_interval?ifname:txt=$1&vifname:txt=$2&addr:ipv4=$3&t_secs:u32=$4" call_xrl_wrapper -p all "${XRL}" } rip_rip_update_interval() { if [ $# -ne 3 ] ; then echo "Usage: rip_rip_update_interval " exit 1 fi XRL="finder://rip/rip/0.1/update_interval?ifname:txt=$1&vifname:txt=$2&addr:ipv4=$3" call_xrl_wrapper -p all "${XRL}" } rip_rip_set_update_jitter() { if [ $# -ne 4 ] ; then echo "Usage: rip_rip_set_update_jitter " exit 1 fi XRL="finder://rip/rip/0.1/set_update_jitter?ifname:txt=$1&vifname:txt=$2&addr:ipv4=$3&t_jitter:u32=$4" call_xrl_wrapper -p all "${XRL}" } rip_rip_update_jitter() { if [ $# -ne 3 ] ; then echo "Usage: rip_rip_update_jitter " exit 1 fi XRL="finder://rip/rip/0.1/update_jitter?ifname:txt=$1&vifname:txt=$2&addr:ipv4=$3" call_xrl_wrapper -p all "${XRL}" } rip_rip_set_triggered_update_delay() { if [ $# -ne 4 ] ; then echo "Usage: rip_rip_set_triggered_update_delay " exit 1 fi XRL="finder://rip/rip/0.1/set_triggered_update_delay?ifname:txt=$1&vifname:txt=$2&addr:ipv4=$3&t_secs:u32=$4" call_xrl_wrapper -p all "${XRL}" } rip_rip_triggered_update_delay() { if [ $# -ne 3 ] ; then echo "Usage: rip_rip_triggered_update_delay " exit 1 fi XRL="finder://rip/rip/0.1/triggered_update_delay?ifname:txt=$1&vifname:txt=$2&addr:ipv4=$3" call_xrl_wrapper -p all "${XRL}" } rip_rip_set_triggered_update_jitter() { if [ $# -ne 4 ] ; then echo "Usage: rip_rip_set_triggered_update_jitter " exit 1 fi XRL="finder://rip/rip/0.1/set_triggered_update_jitter?ifname:txt=$1&vifname:txt=$2&addr:ipv4=$3&t_jitter:u32=$4" call_xrl_wrapper -p all "${XRL}" } rip_rip_triggered_update_jitter() { if [ $# -ne 3 ] ; then echo "Usage: rip_rip_triggered_update_jitter " exit 1 fi XRL="finder://rip/rip/0.1/triggered_update_jitter?ifname:txt=$1&vifname:txt=$2&addr:ipv4=$3" call_xrl_wrapper -p all "${XRL}" } rip_rip_set_interpacket_delay() { if [ $# -ne 4 ] ; then echo "Usage: rip_rip_set_interpacket_delay " exit 1 fi XRL="finder://rip/rip/0.1/set_interpacket_delay?ifname:txt=$1&vifname:txt=$2&addr:ipv4=$3&t_msecs:u32=$4" call_xrl_wrapper -p all "${XRL}" } rip_rip_interpacket_delay() { if [ $# -ne 3 ] ; then echo "Usage: rip_rip_interpacket_delay " exit 1 fi XRL="finder://rip/rip/0.1/interpacket_delay?ifname:txt=$1&vifname:txt=$2&addr:ipv4=$3" call_xrl_wrapper -p all "${XRL}" } rip_rip_set_simple_authentication_key() { if [ $# -ne 4 ] ; then echo "Usage: rip_rip_set_simple_authentication_key " exit 1 fi XRL="finder://rip/rip/0.1/set_simple_authentication_key?ifname:txt=$1&vifname:txt=$2&addr:ipv4=$3&password:txt=$4" call_xrl_wrapper -p all "${XRL}" } rip_rip_delete_simple_authentication_key() { if [ $# -ne 3 ] ; then echo "Usage: rip_rip_delete_simple_authentication_key " exit 1 fi XRL="finder://rip/rip/0.1/delete_simple_authentication_key?ifname:txt=$1&vifname:txt=$2&addr:ipv4=$3" call_xrl_wrapper -p all "${XRL}" } rip_rip_set_md5_authentication_key() { if [ $# -ne 7 ] ; then echo "Usage: rip_rip_set_md5_authentication_key " exit 1 fi XRL="finder://rip/rip/0.1/set_md5_authentication_key?ifname:txt=$1&vifname:txt=$2&addr:ipv4=$3&key_id:u32=$4&password:txt=$5&start_time:txt=$6&end_time:txt=$7" call_xrl_wrapper -p all "${XRL}" } rip_rip_delete_md5_authentication_key() { if [ $# -ne 4 ] ; then echo "Usage: rip_rip_delete_md5_authentication_key " exit 1 fi XRL="finder://rip/rip/0.1/delete_md5_authentication_key?ifname:txt=$1&vifname:txt=$2&addr:ipv4=$3&key_id:u32=$4" call_xrl_wrapper -p all "${XRL}" } rip_rip_rip_address_status() { if [ $# -ne 3 ] ; then echo "Usage: rip_rip_rip_address_status " exit 1 fi XRL="finder://rip/rip/0.1/rip_address_status?ifname:txt=$1&vifname:txt=$2&addr:ipv4=$3" call_xrl_wrapper -p all "${XRL}" } rip_rip_get_all_addresses() { if [ $# -ne 0 ] ; then echo "Usage: rip_rip_get_all_addresses" exit 1 fi XRL="finder://rip/rip/0.1/get_all_addresses" call_xrl_wrapper -p all "${XRL}" } rip_rip_get_counters() { if [ $# -ne 3 ] ; then echo "Usage: rip_rip_get_counters " exit 1 fi XRL="finder://rip/rip/0.1/get_counters?ifname:txt=$1&vifname:txt=$2&addr:ipv4=$3" call_xrl_wrapper -p all "${XRL}" } rip_rip_get_peers() { if [ $# -ne 3 ] ; then echo "Usage: rip_rip_get_peers " exit 1 fi XRL="finder://rip/rip/0.1/get_peers?ifname:txt=$1&vifname:txt=$2&addr:ipv4=$3" call_xrl_wrapper -p all "${XRL}" } rip_rip_get_all_peers() { if [ $# -ne 0 ] ; then echo "Usage: rip_rip_get_all_peers" exit 1 fi XRL="finder://rip/rip/0.1/get_all_peers" call_xrl_wrapper -p all "${XRL}" } rip_rip_get_peer_counters() { if [ $# -ne 4 ] ; then echo "Usage: rip_rip_get_peer_counters " exit 1 fi XRL="finder://rip/rip/0.1/get_peer_counters?ifname:txt=$1&vifname:txt=$2&addr:ipv4=$3&peer:ipv4=$4" call_xrl_wrapper -p all "${XRL}" } rip_rip_trace() { if [ $# -ne 2 ] ; then echo "Usage: rip_rip_trace " exit 1 fi XRL="finder://rip/rip/0.1/trace?tvar:txt=$1&enable:bool=$2" call_xrl_wrapper -p all "${XRL}" } rip_socket4_user_recv_event() { if [ $# -ne 6 ] ; then echo "Usage: rip_socket4_user_recv_event " exit 1 fi XRL="finder://rip/socket4_user/0.1/recv_event?sockid:txt=$1&if_name:txt=$2&vif_name:txt=$3&src_host:ipv4=$4&src_port:u32=$5&data:binary=$6" call_xrl_wrapper -p all "${XRL}" } rip_socket4_user_inbound_connect_event() { if [ $# -ne 4 ] ; then echo "Usage: rip_socket4_user_inbound_connect_event " exit 1 fi XRL="finder://rip/socket4_user/0.1/inbound_connect_event?sockid:txt=$1&src_host:ipv4=$2&src_port:u32=$3&new_sockid:txt=$4" call_xrl_wrapper -p all "${XRL}" } rip_socket4_user_outgoing_connect_event() { if [ $# -ne 1 ] ; then echo "Usage: rip_socket4_user_outgoing_connect_event " exit 1 fi XRL="finder://rip/socket4_user/0.1/outgoing_connect_event?sockid:txt=$1" call_xrl_wrapper -p all "${XRL}" } rip_socket4_user_error_event() { if [ $# -ne 3 ] ; then echo "Usage: rip_socket4_user_error_event " exit 1 fi XRL="finder://rip/socket4_user/0.1/error_event?sockid:txt=$1&error:txt=$2&fatal:bool=$3" call_xrl_wrapper -p all "${XRL}" } rip_socket4_user_disconnect_event() { if [ $# -ne 1 ] ; then echo "Usage: rip_socket4_user_disconnect_event " exit 1 fi XRL="finder://rip/socket4_user/0.1/disconnect_event?sockid:txt=$1" call_xrl_wrapper -p all "${XRL}" } xorp/rip/tools/xrl_ripng_shell_funcs.sh0000664000076400007640000004135111421137511020537 0ustar greearbgreearbripng_common_get_target_name() { if [ $# -ne 0 ] ; then echo "Usage: ripng_common_get_target_name" exit 1 fi XRL="finder://ripng/common/0.1/get_target_name" call_xrl_wrapper -p all "${XRL}" } ripng_common_get_version() { if [ $# -ne 0 ] ; then echo "Usage: ripng_common_get_version" exit 1 fi XRL="finder://ripng/common/0.1/get_version" call_xrl_wrapper -p all "${XRL}" } ripng_common_get_status() { if [ $# -ne 0 ] ; then echo "Usage: ripng_common_get_status" exit 1 fi XRL="finder://ripng/common/0.1/get_status" call_xrl_wrapper -p all "${XRL}" } ripng_common_shutdown() { if [ $# -ne 0 ] ; then echo "Usage: ripng_common_shutdown" exit 1 fi XRL="finder://ripng/common/0.1/shutdown" call_xrl_wrapper -p all "${XRL}" } ripng_finder_event_observer_xrl_target_birth() { if [ $# -ne 2 ] ; then echo "Usage: ripng_finder_event_observer_xrl_target_birth " exit 1 fi XRL="finder://ripng/finder_event_observer/0.1/xrl_target_birth?target_class:txt=$1&target_instance:txt=$2" call_xrl_wrapper -p all "${XRL}" } ripng_finder_event_observer_xrl_target_death() { if [ $# -ne 2 ] ; then echo "Usage: ripng_finder_event_observer_xrl_target_death " exit 1 fi XRL="finder://ripng/finder_event_observer/0.1/xrl_target_death?target_class:txt=$1&target_instance:txt=$2" call_xrl_wrapper -p all "${XRL}" } ripng_policy_backend_configure() { if [ $# -ne 2 ] ; then echo "Usage: ripng_policy_backend_configure " exit 1 fi XRL="finder://ripng/policy_backend/0.1/configure?filter:u32=$1&conf:txt=$2" call_xrl_wrapper -p all "${XRL}" } ripng_policy_backend_reset() { if [ $# -ne 1 ] ; then echo "Usage: ripng_policy_backend_reset " exit 1 fi XRL="finder://ripng/policy_backend/0.1/reset?filter:u32=$1" call_xrl_wrapper -p all "${XRL}" } ripng_policy_backend_push_routes() { if [ $# -ne 0 ] ; then echo "Usage: ripng_policy_backend_push_routes" exit 1 fi XRL="finder://ripng/policy_backend/0.1/push_routes" call_xrl_wrapper -p all "${XRL}" } ripng_policy_redist6_add_route6() { if [ $# -ne 6 ] ; then echo "Usage: ripng_policy_redist6_add_route6 " exit 1 fi XRL="finder://ripng/policy_redist6/0.1/add_route6?network:ipv6net=$1&unicast:bool=$2&multicast:bool=$3&nexthop:ipv6=$4&metric:u32=$5&policytags:list=$6" call_xrl_wrapper -p all "${XRL}" } ripng_policy_redist6_delete_route6() { if [ $# -ne 3 ] ; then echo "Usage: ripng_policy_redist6_delete_route6 " exit 1 fi XRL="finder://ripng/policy_redist6/0.1/delete_route6?network:ipv6net=$1&unicast:bool=$2&multicast:bool=$3" call_xrl_wrapper -p all "${XRL}" } ripng_ripng_add_rip_address() { if [ $# -ne 3 ] ; then echo "Usage: ripng_ripng_add_rip_address " exit 1 fi XRL="finder://ripng/ripng/0.1/add_rip_address?ifname:txt=$1&vifname:txt=$2&addr:ipv6=$3" call_xrl_wrapper -p all "${XRL}" } ripng_ripng_remove_rip_address() { if [ $# -ne 3 ] ; then echo "Usage: ripng_ripng_remove_rip_address " exit 1 fi XRL="finder://ripng/ripng/0.1/remove_rip_address?ifname:txt=$1&vifname:txt=$2&addr:ipv6=$3" call_xrl_wrapper -p all "${XRL}" } ripng_ripng_set_rip_address_enabled() { if [ $# -ne 4 ] ; then echo "Usage: ripng_ripng_set_rip_address_enabled " exit 1 fi XRL="finder://ripng/ripng/0.1/set_rip_address_enabled?ifname:txt=$1&vifname:txt=$2&addr:ipv6=$3&enabled:bool=$4" call_xrl_wrapper -p all "${XRL}" } ripng_ripng_rip_address_enabled() { if [ $# -ne 3 ] ; then echo "Usage: ripng_ripng_rip_address_enabled " exit 1 fi XRL="finder://ripng/ripng/0.1/rip_address_enabled?ifname:txt=$1&vifname:txt=$2&addr:ipv6=$3" call_xrl_wrapper -p all "${XRL}" } ripng_ripng_set_cost() { if [ $# -ne 4 ] ; then echo "Usage: ripng_ripng_set_cost " exit 1 fi XRL="finder://ripng/ripng/0.1/set_cost?ifname:txt=$1&vifname:txt=$2&addr:ipv6=$3&cost:u32=$4" call_xrl_wrapper -p all "${XRL}" } ripng_ripng_cost() { if [ $# -ne 3 ] ; then echo "Usage: ripng_ripng_cost " exit 1 fi XRL="finder://ripng/ripng/0.1/cost?ifname:txt=$1&vifname:txt=$2&addr:ipv6=$3" call_xrl_wrapper -p all "${XRL}" } ripng_ripng_set_horizon() { if [ $# -ne 4 ] ; then echo "Usage: ripng_ripng_set_horizon " exit 1 fi XRL="finder://ripng/ripng/0.1/set_horizon?ifname:txt=$1&vifname:txt=$2&addr:ipv6=$3&horizon:txt=$4" call_xrl_wrapper -p all "${XRL}" } ripng_ripng_horizon() { if [ $# -ne 3 ] ; then echo "Usage: ripng_ripng_horizon " exit 1 fi XRL="finder://ripng/ripng/0.1/horizon?ifname:txt=$1&vifname:txt=$2&addr:ipv6=$3" call_xrl_wrapper -p all "${XRL}" } ripng_ripng_set_passive() { if [ $# -ne 4 ] ; then echo "Usage: ripng_ripng_set_passive " exit 1 fi XRL="finder://ripng/ripng/0.1/set_passive?ifname:txt=$1&vifname:txt=$2&addr:ipv6=$3&passive:bool=$4" call_xrl_wrapper -p all "${XRL}" } ripng_ripng_passive() { if [ $# -ne 3 ] ; then echo "Usage: ripng_ripng_passive " exit 1 fi XRL="finder://ripng/ripng/0.1/passive?ifname:txt=$1&vifname:txt=$2&addr:ipv6=$3" call_xrl_wrapper -p all "${XRL}" } ripng_ripng_set_accept_non_rip_requests() { if [ $# -ne 4 ] ; then echo "Usage: ripng_ripng_set_accept_non_rip_requests " exit 1 fi XRL="finder://ripng/ripng/0.1/set_accept_non_rip_requests?ifname:txt=$1&vifname:txt=$2&addr:ipv6=$3&accept:bool=$4" call_xrl_wrapper -p all "${XRL}" } ripng_ripng_accept_non_rip_requests() { if [ $# -ne 3 ] ; then echo "Usage: ripng_ripng_accept_non_rip_requests " exit 1 fi XRL="finder://ripng/ripng/0.1/accept_non_rip_requests?ifname:txt=$1&vifname:txt=$2&addr:ipv6=$3" call_xrl_wrapper -p all "${XRL}" } ripng_ripng_set_accept_default_route() { if [ $# -ne 4 ] ; then echo "Usage: ripng_ripng_set_accept_default_route " exit 1 fi XRL="finder://ripng/ripng/0.1/set_accept_default_route?ifname:txt=$1&vifname:txt=$2&addr:ipv6=$3&accept:bool=$4" call_xrl_wrapper -p all "${XRL}" } ripng_ripng_accept_default_route() { if [ $# -ne 3 ] ; then echo "Usage: ripng_ripng_accept_default_route " exit 1 fi XRL="finder://ripng/ripng/0.1/accept_default_route?ifname:txt=$1&vifname:txt=$2&addr:ipv6=$3" call_xrl_wrapper -p all "${XRL}" } ripng_ripng_set_advertise_default_route() { if [ $# -ne 4 ] ; then echo "Usage: ripng_ripng_set_advertise_default_route " exit 1 fi XRL="finder://ripng/ripng/0.1/set_advertise_default_route?ifname:txt=$1&vifname:txt=$2&addr:ipv6=$3&advertise:bool=$4" call_xrl_wrapper -p all "${XRL}" } ripng_ripng_advertise_default_route() { if [ $# -ne 3 ] ; then echo "Usage: ripng_ripng_advertise_default_route " exit 1 fi XRL="finder://ripng/ripng/0.1/advertise_default_route?ifname:txt=$1&vifname:txt=$2&addr:ipv6=$3" call_xrl_wrapper -p all "${XRL}" } ripng_ripng_set_route_timeout() { if [ $# -ne 4 ] ; then echo "Usage: ripng_ripng_set_route_timeout " exit 1 fi XRL="finder://ripng/ripng/0.1/set_route_timeout?ifname:txt=$1&vifname:txt=$2&addr:ipv6=$3&t_secs:u32=$4" call_xrl_wrapper -p all "${XRL}" } ripng_ripng_route_timeout() { if [ $# -ne 3 ] ; then echo "Usage: ripng_ripng_route_timeout " exit 1 fi XRL="finder://ripng/ripng/0.1/route_timeout?ifname:txt=$1&vifname:txt=$2&addr:ipv6=$3" call_xrl_wrapper -p all "${XRL}" } ripng_ripng_set_deletion_delay() { if [ $# -ne 4 ] ; then echo "Usage: ripng_ripng_set_deletion_delay " exit 1 fi XRL="finder://ripng/ripng/0.1/set_deletion_delay?ifname:txt=$1&vifname:txt=$2&addr:ipv6=$3&t_secs:u32=$4" call_xrl_wrapper -p all "${XRL}" } ripng_ripng_deletion_delay() { if [ $# -ne 3 ] ; then echo "Usage: ripng_ripng_deletion_delay " exit 1 fi XRL="finder://ripng/ripng/0.1/deletion_delay?ifname:txt=$1&vifname:txt=$2&addr:ipv6=$3" call_xrl_wrapper -p all "${XRL}" } ripng_ripng_set_request_interval() { if [ $# -ne 4 ] ; then echo "Usage: ripng_ripng_set_request_interval " exit 1 fi XRL="finder://ripng/ripng/0.1/set_request_interval?ifname:txt=$1&vifname:txt=$2&addr:ipv6=$3&t_secs:u32=$4" call_xrl_wrapper -p all "${XRL}" } ripng_ripng_request_interval() { if [ $# -ne 3 ] ; then echo "Usage: ripng_ripng_request_interval " exit 1 fi XRL="finder://ripng/ripng/0.1/request_interval?ifname:txt=$1&vifname:txt=$2&addr:ipv6=$3" call_xrl_wrapper -p all "${XRL}" } ripng_ripng_set_update_interval() { if [ $# -ne 4 ] ; then echo "Usage: ripng_ripng_set_update_interval " exit 1 fi XRL="finder://ripng/ripng/0.1/set_update_interval?ifname:txt=$1&vifname:txt=$2&addr:ipv6=$3&t_secs:u32=$4" call_xrl_wrapper -p all "${XRL}" } ripng_ripng_update_interval() { if [ $# -ne 3 ] ; then echo "Usage: ripng_ripng_update_interval " exit 1 fi XRL="finder://ripng/ripng/0.1/update_interval?ifname:txt=$1&vifname:txt=$2&addr:ipv6=$3" call_xrl_wrapper -p all "${XRL}" } ripng_ripng_set_update_jitter() { if [ $# -ne 4 ] ; then echo "Usage: ripng_ripng_set_update_jitter " exit 1 fi XRL="finder://ripng/ripng/0.1/set_update_jitter?ifname:txt=$1&vifname:txt=$2&addr:ipv6=$3&t_jitter:u32=$4" call_xrl_wrapper -p all "${XRL}" } ripng_ripng_update_jitter() { if [ $# -ne 3 ] ; then echo "Usage: ripng_ripng_update_jitter " exit 1 fi XRL="finder://ripng/ripng/0.1/update_jitter?ifname:txt=$1&vifname:txt=$2&addr:ipv6=$3" call_xrl_wrapper -p all "${XRL}" } ripng_ripng_set_triggered_update_delay() { if [ $# -ne 4 ] ; then echo "Usage: ripng_ripng_set_triggered_update_delay " exit 1 fi XRL="finder://ripng/ripng/0.1/set_triggered_update_delay?ifname:txt=$1&vifname:txt=$2&addr:ipv6=$3&t_secs:u32=$4" call_xrl_wrapper -p all "${XRL}" } ripng_ripng_triggered_update_delay() { if [ $# -ne 3 ] ; then echo "Usage: ripng_ripng_triggered_update_delay " exit 1 fi XRL="finder://ripng/ripng/0.1/triggered_update_delay?ifname:txt=$1&vifname:txt=$2&addr:ipv6=$3" call_xrl_wrapper -p all "${XRL}" } ripng_ripng_set_triggered_update_jitter() { if [ $# -ne 4 ] ; then echo "Usage: ripng_ripng_set_triggered_update_jitter " exit 1 fi XRL="finder://ripng/ripng/0.1/set_triggered_update_jitter?ifname:txt=$1&vifname:txt=$2&addr:ipv6=$3&t_jitter:u32=$4" call_xrl_wrapper -p all "${XRL}" } ripng_ripng_triggered_update_jitter() { if [ $# -ne 3 ] ; then echo "Usage: ripng_ripng_triggered_update_jitter " exit 1 fi XRL="finder://ripng/ripng/0.1/triggered_update_jitter?ifname:txt=$1&vifname:txt=$2&addr:ipv6=$3" call_xrl_wrapper -p all "${XRL}" } ripng_ripng_set_interpacket_delay() { if [ $# -ne 4 ] ; then echo "Usage: ripng_ripng_set_interpacket_delay " exit 1 fi XRL="finder://ripng/ripng/0.1/set_interpacket_delay?ifname:txt=$1&vifname:txt=$2&addr:ipv6=$3&t_msecs:u32=$4" call_xrl_wrapper -p all "${XRL}" } ripng_ripng_interpacket_delay() { if [ $# -ne 3 ] ; then echo "Usage: ripng_ripng_interpacket_delay " exit 1 fi XRL="finder://ripng/ripng/0.1/interpacket_delay?ifname:txt=$1&vifname:txt=$2&addr:ipv6=$3" call_xrl_wrapper -p all "${XRL}" } ripng_ripng_rip_address_status() { if [ $# -ne 3 ] ; then echo "Usage: ripng_ripng_rip_address_status " exit 1 fi XRL="finder://ripng/ripng/0.1/rip_address_status?ifname:txt=$1&vifname:txt=$2&addr:ipv6=$3" call_xrl_wrapper -p all "${XRL}" } ripng_ripng_get_all_addresses() { if [ $# -ne 0 ] ; then echo "Usage: ripng_ripng_get_all_addresses" exit 1 fi XRL="finder://ripng/ripng/0.1/get_all_addresses" call_xrl_wrapper -p all "${XRL}" } ripng_ripng_get_counters() { if [ $# -ne 3 ] ; then echo "Usage: ripng_ripng_get_counters " exit 1 fi XRL="finder://ripng/ripng/0.1/get_counters?ifname:txt=$1&vifname:txt=$2&addr:ipv6=$3" call_xrl_wrapper -p all "${XRL}" } ripng_ripng_get_peers() { if [ $# -ne 3 ] ; then echo "Usage: ripng_ripng_get_peers " exit 1 fi XRL="finder://ripng/ripng/0.1/get_peers?ifname:txt=$1&vifname:txt=$2&addr:ipv6=$3" call_xrl_wrapper -p all "${XRL}" } ripng_ripng_get_all_peers() { if [ $# -ne 0 ] ; then echo "Usage: ripng_ripng_get_all_peers" exit 1 fi XRL="finder://ripng/ripng/0.1/get_all_peers" call_xrl_wrapper -p all "${XRL}" } ripng_ripng_get_peer_counters() { if [ $# -ne 4 ] ; then echo "Usage: ripng_ripng_get_peer_counters " exit 1 fi XRL="finder://ripng/ripng/0.1/get_peer_counters?ifname:txt=$1&vifname:txt=$2&addr:ipv6=$3&peer:ipv6=$4" call_xrl_wrapper -p all "${XRL}" } ripng_ripng_trace() { if [ $# -ne 2 ] ; then echo "Usage: ripng_ripng_trace " exit 1 fi XRL="finder://ripng/ripng/0.1/trace?tvar:txt=$1&enable:bool=$2" call_xrl_wrapper -p all "${XRL}" } ripng_socket6_user_recv_event() { if [ $# -ne 6 ] ; then echo "Usage: ripng_socket6_user_recv_event " exit 1 fi XRL="finder://ripng/socket6_user/0.1/recv_event?sockid:txt=$1&if_name:txt=$2&vif_name:txt=$3&src_host:ipv6=$4&src_port:u32=$5&data:binary=$6" call_xrl_wrapper -p all "${XRL}" } ripng_socket6_user_inbound_connect_event() { if [ $# -ne 4 ] ; then echo "Usage: ripng_socket6_user_inbound_connect_event " exit 1 fi XRL="finder://ripng/socket6_user/0.1/inbound_connect_event?sockid:txt=$1&src_host:ipv6=$2&src_port:u32=$3&new_sockid:txt=$4" call_xrl_wrapper -p all "${XRL}" } ripng_socket6_user_outgoing_connect_event() { if [ $# -ne 1 ] ; then echo "Usage: ripng_socket6_user_outgoing_connect_event " exit 1 fi XRL="finder://ripng/socket6_user/0.1/outgoing_connect_event?sockid:txt=$1" call_xrl_wrapper -p all "${XRL}" } ripng_socket6_user_error_event() { if [ $# -ne 3 ] ; then echo "Usage: ripng_socket6_user_error_event " exit 1 fi XRL="finder://ripng/socket6_user/0.1/error_event?sockid:txt=$1&error:txt=$2&fatal:bool=$3" call_xrl_wrapper -p all "${XRL}" } ripng_socket6_user_disconnect_event() { if [ $# -ne 1 ] ; then echo "Usage: ripng_socket6_user_disconnect_event " exit 1 fi XRL="finder://ripng/socket6_user/0.1/disconnect_event?sockid:txt=$1" call_xrl_wrapper -p all "${XRL}" } xorp/rip/tools/common.hh0000664000076400007640000000734211540224235015427 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/rip/tools/common.hh,v 1.10 2008/10/02 21:58:21 bms Exp $ #ifndef __RIP_TOOLS_COMMON_HH__ #define __RIP_TOOLS_COMMON_HH__ #include "libxorp/xorp.h" #include "libxorp/ipv4.hh" #include "libxorp/ipv6.hh" #include "libxorp/service.hh" class EventLoop; class XrlJobBase; // ---------------------------------------------------------------------------- // Utility funtions /** * Utility function to return the default RIP XRL target according to * the IP version number. * * @param ip_version IP version number. * @return pointer to character string containing XRL target name on * success, NULL if an invalid IP version number. */ const char* default_xrl_target(uint32_t ip_version); /** * Utility function to return IP version from named rip description string. * * @param rip_name string that is expected to be "rip" or "ripng". * * @return on success either of 4 or 6, on failure 0. */ uint32_t rip_name_to_ip_version(const char* rip_name); /** * Utility function to break a colon separated host name and port into * separate variables. * * @param host_colon_port string containing colon separated host and port. * @param host string assigned with host name. * @param port unsigned integer assigned with port number. * @return true on success, false if host_colon_port is invalid. */ bool parse_finder_args(const string& host_colon_port, string& host, uint16_t& port); // ---------------------------------------------------------------------------- // Class declarations /** * Class for buffering and dispatching XRLs. */ class XrlJobQueue : public ServiceBase { public: typedef ref_ptr Job; public: XrlJobQueue(EventLoop& e, const string& finder_host, uint16_t finder_port, const string& tgtname); ~XrlJobQueue(); int startup(); int shutdown(); void dispatch_complete(const XrlError& xe, const XrlJobBase* cmd); XrlSender* sender() { return _rtr; } const string& target() const { return _tgt; } void enqueue(const Job& cmd) { _jobs.push_back(cmd); } protected: bool xrl_router_ready_poll(); void process_next_job(); protected: EventLoop& _e; string _fhost; // Finder host uint16_t _fport; // Finder port string _tgt; // Xrl target to for jobs list _jobs; XrlStdRouter* _rtr; XorpTimer _rtr_poll; // Timer used to poll XrlRouter::ready uint32_t _rtr_poll_cnt; // Number of timer XrlRouter polled. }; /** * Base class for Xrl Jobs that are invoked by classes derived * from XrlJobQueue. * Non-copyable due to inheriting from CallbackSafeObject. */ class XrlJobBase : public CallbackSafeObject { public: XrlJobBase(XrlJobQueue& q) : _q(q) {} virtual ~XrlJobBase() {} virtual bool dispatch() = 0; protected: XrlJobQueue& queue() { return _q; } private: XrlJobQueue& _q; }; #endif // __RIP_TOOLS_COMMON_HH__ xorp/rip/xrl_target_rip.cc0000664000076400007640000005235411540225534016020 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/status_codes.h" #include "libxipc/xrl_router.hh" #include "auth.hh" #include "system.hh" #include "xrl_process_spy.hh" #include "xrl_port_manager.hh" #include "xrl_redist_manager.hh" #include "xrl_target_rip.hh" #include "xrl_target_common.hh" static int decode_time_string(EventLoop& eventloop, const string& time_string, TimeVal& timeval) { const char* s; const char* format = "%Y-%m-%d.%H:%M"; struct tm tm; time_t result; if (time_string.empty()) { timeval = TimeVal::ZERO(); return (XORP_OK); } // // Initialize the parsed result with the current time, because // strptime(3) would not set/modify the unspecified members of the // time format (e.g, the timezone and the summer time flag). // TimeVal now; eventloop.current_time(now); time_t local_time = now.sec(); const struct tm* local_tm = localtime(&local_time); memcpy(&tm, local_tm, sizeof(tm)); s = xorp_strptime(time_string.c_str(), format, &tm); if ((s == NULL) || (*s != '\0')) { return (XORP_ERROR); } result = mktime(&tm); if (result == -1) return (XORP_ERROR); timeval = TimeVal(result, 0); return (XORP_OK); } XrlRipTarget::XrlRipTarget(EventLoop& el, XrlRouter& xr, XrlProcessSpy& xps, XrlPortManager& xpm, XrlRedistManager& xrm, System& rip_system) : XrlRipTargetBase(&xr), _e(el) { _ct = new XrlRipCommonTarget(xps, xpm, xrm, rip_system); } XrlRipTarget::~XrlRipTarget() { delete _ct; } XrlCmdError XrlRipTarget::common_0_1_get_target_name(string& n) { n = get_name(); return XrlCmdError::OKAY(); } XrlCmdError XrlRipTarget::common_0_1_get_version(string& v) { v = string(version()); return XrlCmdError::OKAY(); } void XrlRipTarget::set_status(ProcessStatus status, const string& note) { _ct->set_status(status, note); } XrlCmdError XrlRipTarget::common_0_1_get_status(uint32_t& status, string& reason) { return _ct->common_0_1_get_status(status, reason); } XrlCmdError XrlRipTarget::common_0_1_shutdown() { return _ct->common_0_1_shutdown(); } XrlCmdError XrlRipTarget::common_0_1_startup() { return _ct->common_0_1_startup(); } XrlCmdError XrlRipTarget::finder_event_observer_0_1_xrl_target_birth(const string& cname, const string& iname) { return _ct->finder_event_observer_0_1_xrl_target_birth(cname, iname); } XrlCmdError XrlRipTarget::finder_event_observer_0_1_xrl_target_death(const string& cname, const string& iname) { return _ct->finder_event_observer_0_1_xrl_target_death(cname, iname); } XrlCmdError XrlRipTarget::rip_0_1_add_rip_address(const string& ifn, const string& vifn, const IPv4& addr) { return _ct->ripx_0_1_add_rip_address(ifn, vifn, addr); } XrlCmdError XrlRipTarget::rip_0_1_remove_rip_address(const string& ifn, const string& vifn, const IPv4& addr) { return _ct->ripx_0_1_remove_rip_address(ifn, vifn, addr); } XrlCmdError XrlRipTarget::rip_0_1_set_rip_address_enabled(const string& ifn, const string& vifn, const IPv4& a, const bool& en) { return _ct->ripx_0_1_set_rip_address_enabled(ifn, vifn, a, en); } XrlCmdError XrlRipTarget::rip_0_1_rip_address_enabled(const string& ifn, const string& vifn, const IPv4& a, bool& en) { return _ct->ripx_0_1_rip_address_enabled(ifn, vifn, a, en); } XrlCmdError XrlRipTarget::rip_0_1_set_cost(const string& ifn, const string& vifn, const IPv4& a, const uint32_t& cost) { return _ct->ripx_0_1_set_cost(ifn, vifn, a, cost); } XrlCmdError XrlRipTarget::rip_0_1_cost(const string& ifn, const string& vifn, const IPv4& a, uint32_t& cost) { return _ct->ripx_0_1_cost(ifn, vifn, a, cost); } XrlCmdError XrlRipTarget::rip_0_1_set_horizon(const string& ifn, const string& vifn, const IPv4& a, const string& horizon) { return _ct->ripx_0_1_set_horizon(ifn, vifn, a, horizon); } XrlCmdError XrlRipTarget::rip_0_1_horizon(const string& ifn, const string& vifn, const IPv4& a, string& horizon) { return _ct->ripx_0_1_horizon(ifn, vifn, a, horizon); } XrlCmdError XrlRipTarget::rip_0_1_set_passive(const string& ifn, const string& vifn, const IPv4& a, const bool& passive) { return _ct->ripx_0_1_set_passive(ifn, vifn, a, passive); } XrlCmdError XrlRipTarget::rip_0_1_passive(const string& ifn, const string& vifn, const IPv4& a, bool& passive) { return _ct->ripx_0_1_passive(ifn, vifn, a, passive); } XrlCmdError XrlRipTarget::rip_0_1_set_accept_non_rip_requests(const string& ifn, const string& vifn, const IPv4& addr, const bool& accept) { return _ct->ripx_0_1_set_accept_non_rip_requests(ifn, vifn, addr, accept); } XrlCmdError XrlRipTarget::rip_0_1_accept_non_rip_requests(const string& ifn, const string& vifn, const IPv4& addr, bool& accept) { return _ct->ripx_0_1_accept_non_rip_requests(ifn, vifn, addr, accept); } XrlCmdError XrlRipTarget::rip_0_1_set_accept_default_route(const string& ifn, const string& vifn, const IPv4& addr, const bool& accept) { return _ct->ripx_0_1_set_accept_default_route(ifn, vifn, addr, accept); } XrlCmdError XrlRipTarget::rip_0_1_accept_default_route(const string& ifn, const string& vifn, const IPv4& addr, bool& accept) { return _ct->ripx_0_1_accept_default_route(ifn, vifn, addr, accept); } XrlCmdError XrlRipTarget::rip_0_1_set_advertise_default_route(const string& ifn, const string& vifn, const IPv4& addr, const bool& adv) { return _ct->ripx_0_1_set_advertise_default_route(ifn, vifn, addr, adv); } XrlCmdError XrlRipTarget::rip_0_1_advertise_default_route(const string& ifn, const string& vifn, const IPv4& addr, bool& adv) { return _ct->ripx_0_1_advertise_default_route(ifn, vifn, addr, adv); } XrlCmdError XrlRipTarget::rip_0_1_set_route_timeout(const string& ifn, const string& vifn, const IPv4& a, const uint32_t& t) { return _ct->ripx_0_1_set_route_timeout(ifn, vifn, a, t); } XrlCmdError XrlRipTarget::rip_0_1_route_timeout(const string& ifn, const string& vifn, const IPv4& a, uint32_t& t) { return _ct->ripx_0_1_route_timeout(ifn, vifn, a, t); } XrlCmdError XrlRipTarget::rip_0_1_set_deletion_delay(const string& ifn, const string& vifn, const IPv4& a, const uint32_t& t) { return _ct->ripx_0_1_set_deletion_delay(ifn, vifn, a, t); } XrlCmdError XrlRipTarget::rip_0_1_deletion_delay(const string& ifn, const string& vifn, const IPv4& a, uint32_t& t) { return _ct->ripx_0_1_deletion_delay(ifn, vifn, a, t); } XrlCmdError XrlRipTarget::rip_0_1_set_request_interval(const string& ifn, const string& vifn, const IPv4& a, const uint32_t& t) { return _ct->ripx_0_1_set_request_interval(ifn, vifn, a, t); } XrlCmdError XrlRipTarget::rip_0_1_request_interval(const string& ifn, const string& vifn, const IPv4& a, uint32_t& t) { return _ct->ripx_0_1_request_interval(ifn, vifn, a, t); } XrlCmdError XrlRipTarget::rip_0_1_set_update_interval(const string& ifn, const string& vifn, const IPv4& a, const uint32_t& t_secs) { return _ct->ripx_0_1_set_update_interval(ifn, vifn, a, t_secs); } XrlCmdError XrlRipTarget::rip_0_1_update_interval(const string& ifn, const string& vifn, const IPv4& a, uint32_t& t_secs) { return _ct->ripx_0_1_update_interval(ifn, vifn, a, t_secs); } XrlCmdError XrlRipTarget::rip_0_1_set_update_jitter(const string& ifn, const string& vifn, const IPv4& a, const uint32_t& t_jitter) { return _ct->ripx_0_1_set_update_jitter(ifn, vifn, a, t_jitter); } XrlCmdError XrlRipTarget::rip_0_1_update_jitter(const string& ifn, const string& vifn, const IPv4& a, uint32_t& t_jitter) { return _ct->ripx_0_1_update_jitter(ifn, vifn, a, t_jitter); } XrlCmdError XrlRipTarget::rip_0_1_set_triggered_update_delay(const string& ifn, const string& vifn, const IPv4& a, const uint32_t& t_secs) { return _ct->ripx_0_1_set_triggered_update_delay(ifn, vifn, a, t_secs); } XrlCmdError XrlRipTarget::rip_0_1_triggered_update_delay(const string& ifn, const string& vifn, const IPv4& a, uint32_t& t_secs) { return _ct->ripx_0_1_triggered_update_delay(ifn, vifn, a, t_secs); } XrlCmdError XrlRipTarget::rip_0_1_set_triggered_update_jitter(const string& ifn, const string& vifn, const IPv4& a, const uint32_t& t_jitter) { return _ct->ripx_0_1_set_triggered_update_jitter(ifn, vifn, a, t_jitter); } XrlCmdError XrlRipTarget::rip_0_1_triggered_update_jitter(const string& ifn, const string& vifn, const IPv4& a, uint32_t& t_jitter) { return _ct->ripx_0_1_triggered_update_jitter(ifn, vifn, a, t_jitter); } XrlCmdError XrlRipTarget::rip_0_1_set_interpacket_delay(const string& ifn, const string& vifn, const IPv4& a, const uint32_t& t_msecs) { return _ct->ripx_0_1_set_interpacket_delay(ifn, vifn, a, t_msecs); } XrlCmdError XrlRipTarget::rip_0_1_interpacket_delay(const string& ifn, const string& vifn, const IPv4& a, uint32_t& t_msecs) { return _ct->ripx_0_1_interpacket_delay(ifn, vifn, a, t_msecs); } XrlCmdError XrlRipTarget::rip_0_1_set_simple_authentication_key( // Input values, const string& ifname, const string& vifname, const IPv4& addr, const string& password) { AuthHandlerBase* current_ah = NULL; PlaintextAuthHandler* plaintext_ah = NULL; pair*, XrlCmdError> pp = _ct->find_port(ifname, vifname, addr); if (pp.first == 0) return pp.second; Port* p = pp.first; PortAFSpecState& pss = p->af_state(); // // Modify an existing simple authentication handler or create a new one // current_ah = pss.auth_handler(); XLOG_ASSERT(current_ah != NULL); plaintext_ah = dynamic_cast(current_ah); if (plaintext_ah != NULL) { plaintext_ah->set_key(password); } else { plaintext_ah = new PlaintextAuthHandler(); plaintext_ah->set_key(password); pss.set_auth_handler(plaintext_ah); delete current_ah; } return XrlCmdError::OKAY(); } XrlCmdError XrlRipTarget::rip_0_1_delete_simple_authentication_key( // Input values, const string& ifname, const string& vifname, const IPv4& addr) { AuthHandlerBase* current_ah = NULL; PlaintextAuthHandler* plaintext_ah = NULL; string error_msg; pair*, XrlCmdError> pp = _ct->find_port(ifname, vifname, addr); if (pp.first == 0) return pp.second; Port* p = pp.first; PortAFSpecState& pss = p->af_state(); // // Test whether the simple password handler is really configured // current_ah = pss.auth_handler(); XLOG_ASSERT(current_ah != NULL); plaintext_ah = dynamic_cast(current_ah); if (plaintext_ah != NULL) { // // XXX: Here we should return a mismatch error. // However, if we are adding both a simple password and MD5 handlers, // then the rtrmgr configuration won't match the protocol state. // Ideally, the rtrmgr and xorpsh shouldn't allow the user to add // both handlers. For the time being we need to live with this // limitation and therefore we shouldn't return an error here. // return XrlCmdError::OKAY(); #if 0 error_msg = c_format("Cannot delete simple password authentication: " "no such is configured"); return XrlCmdError::COMMAND_FAILED(error_msg); #endif } // // Install an empty handler and delete the simple authentication handler // NullAuthHandler* null_handler = new NullAuthHandler(); pss.set_auth_handler(null_handler); delete current_ah; return XrlCmdError::OKAY(); } XrlCmdError XrlRipTarget::rip_0_1_set_md5_authentication_key( // Input values, const string& ifname, const string& vifname, const IPv4& addr, const uint32_t& key_id, const string& password, const string& start_time, const string& end_time) { TimeVal start_timeval = TimeVal::ZERO(); TimeVal end_timeval = TimeVal::MAXIMUM(); AuthHandlerBase* current_ah = NULL; MD5AuthHandler* md5_ah = NULL; string error_msg; pair*, XrlCmdError> pp = _ct->find_port(ifname, vifname, addr); if (pp.first == 0) return pp.second; Port* p = pp.first; PortAFSpecState& pss = p->af_state(); // // Check the key ID // if (key_id > 255) { error_msg = c_format("Invalid key ID %u (valid range is [0, 255])", XORP_UINT_CAST(key_id)); return XrlCmdError::COMMAND_FAILED(error_msg); } // // Decode the start and end time // if (! start_time.empty()) { if (decode_time_string(_e, start_time, start_timeval) != XORP_OK) { error_msg = c_format("Invalid start time: %s", start_time.c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } } if (! end_time.empty()) { if (decode_time_string(_e, end_time, end_timeval) != XORP_OK) { error_msg = c_format("Invalid end time: %s", end_time.c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } } // // Modify an existing MD5 authentication handler or create a new one // current_ah = pss.auth_handler(); XLOG_ASSERT(current_ah != NULL); md5_ah = dynamic_cast(current_ah); if (md5_ah != NULL) { if (md5_ah->add_key(key_id, password, start_timeval, end_timeval, error_msg) != true) { error_msg = c_format("MD5 key add failed: %s", error_msg.c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } } else { md5_ah = new MD5AuthHandler(_e); if (md5_ah->add_key(key_id, password, start_timeval, end_timeval, error_msg) != true) { delete md5_ah; error_msg = c_format("MD5 key add failed: %s", error_msg.c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } pss.set_auth_handler(md5_ah); delete current_ah; } return XrlCmdError::OKAY(); } XrlCmdError XrlRipTarget::rip_0_1_delete_md5_authentication_key( // Input values, const string& ifname, const string& vifname, const IPv4& addr, const uint32_t& key_id) { AuthHandlerBase* current_ah = NULL; MD5AuthHandler* md5_ah = NULL; string error_msg; pair*, XrlCmdError> pp = _ct->find_port(ifname, vifname, addr); if (pp.first == 0) return pp.second; Port* p = pp.first; PortAFSpecState& pss = p->af_state(); // // Test whether the MD5 password handler is really configured // current_ah = pss.auth_handler(); XLOG_ASSERT(current_ah != NULL); md5_ah = dynamic_cast(current_ah); if (md5_ah != NULL) { // // XXX: Here we should return a mismatch error. // However, if we are adding both a simple password and MD5 handlers, // then the rtrmgr configuration won't match the protocol state. // Ideally, the rtrmgr and xorpsh shouldn't allow the user to add // both handlers. For the time being we need to live with this // limitation and therefore we shouldn't return an error here. // return XrlCmdError::OKAY(); #if 0 error_msg = c_format("Cannot delete MD5 password authentication: " "no such is configured"); return XrlCmdError::COMMAND_FAILED(error_msg); #endif } XLOG_ASSERT(md5_ah != NULL); // // Check the key ID // if (key_id > 255) { error_msg = c_format("Invalid key ID %u (valid range is [0, 255])", XORP_UINT_CAST(key_id)); return XrlCmdError::COMMAND_FAILED(error_msg); } // // Remove the key // if (md5_ah->remove_key(key_id, error_msg) != true) { error_msg = c_format("Invalid MD5 key ID %u: %s", XORP_UINT_CAST(key_id), error_msg.c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } // // If the last key, then install an empty handler and delete the MD5 // authentication handler. // if (md5_ah->empty()) { NullAuthHandler* null_handler = new NullAuthHandler(); pss.set_auth_handler(null_handler); delete current_ah; } return XrlCmdError::OKAY(); } XrlCmdError XrlRipTarget::rip_0_1_rip_address_status(const string& ifn, const string& vifn, const IPv4& a, string& status) { return _ct->ripx_0_1_rip_address_status(ifn, vifn, a, status); } XrlCmdError XrlRipTarget::rip_0_1_get_all_addresses(XrlAtomList& ifnames, XrlAtomList& vifnames, XrlAtomList& addrs) { return _ct->ripx_0_1_get_all_addresses(ifnames, vifnames, addrs); } XrlCmdError XrlRipTarget::rip_0_1_get_peers(const string& ifn, const string& vifn, const IPv4& a, XrlAtomList& peers) { return _ct->ripx_0_1_get_peers(ifn, vifn, a, peers); } XrlCmdError XrlRipTarget::rip_0_1_get_all_peers(XrlAtomList& peers, XrlAtomList& ifnames, XrlAtomList& vifnames, XrlAtomList& addrs) { return _ct->ripx_0_1_get_all_peers(peers, ifnames, vifnames, addrs); } XrlCmdError XrlRipTarget::rip_0_1_get_counters(const string& ifname, const string& vifname, const IPv4& addr, XrlAtomList& descs, XrlAtomList& values) { return _ct->ripx_0_1_get_counters(ifname, vifname, addr, descs, values); } XrlCmdError XrlRipTarget::rip_0_1_get_peer_counters(const string& ifn, const string& vifn, const IPv4& addr, const IPv4& peer, XrlAtomList& descs, XrlAtomList& vals, uint32_t& peer_last_active) { return _ct->ripx_0_1_get_peer_counters(ifn, vifn, addr, peer, descs, vals, peer_last_active); } XrlCmdError XrlRipTarget::rip_0_1_trace(const string& tvar, const bool& enable) { debug_msg("trace variable %s enable %s\n", tvar.c_str(), bool_c_str(enable)); if (tvar == "all") { _ct->trace(enable); } else { return XrlCmdError:: COMMAND_FAILED(c_format("Unknown variable %s", tvar.c_str())); } return XrlCmdError::OKAY(); } XrlCmdError XrlRipTarget::socket4_user_0_1_recv_event( const string& sockid, const string& if_name, const string& vif_name, const IPv4& src_host, const uint32_t& src_port, const vector& pdata ) { return _ct->socketx_user_0_1_recv_event(sockid, if_name, vif_name, src_host, src_port, pdata); } XrlCmdError XrlRipTarget::socket4_user_0_1_inbound_connect_event( const string& sockid, const IPv4& src_host, const uint32_t& src_port, const string& new_sockid, bool& accept) { return _ct->socketx_user_0_1_inbound_connect_event(sockid, src_host, src_port, new_sockid, accept); } XrlCmdError XrlRipTarget::socket4_user_0_1_outgoing_connect_event( const string& sockid) { return _ct->socketx_user_0_1_outgoing_connect_event(sockid); } XrlCmdError XrlRipTarget::socket4_user_0_1_error_event(const string& sockid, const string& reason, const bool& fatal) { return _ct->socketx_user_0_1_error_event(sockid, reason, fatal); } XrlCmdError XrlRipTarget::socket4_user_0_1_disconnect_event(const string& sockid) { return _ct->socketx_user_0_1_disconnect_event(sockid); } XrlCmdError XrlRipTarget::policy_backend_0_1_configure(const uint32_t& filter, const string& conf) { return _ct->policy_backend_0_1_configure(filter, conf); } XrlCmdError XrlRipTarget::policy_backend_0_1_reset(const uint32_t& filter) { return _ct->policy_backend_0_1_reset(filter); } XrlCmdError XrlRipTarget::policy_backend_0_1_push_routes() { return _ct->policy_backend_0_1_push_routes(); } XrlCmdError XrlRipTarget::policy_redist4_0_1_add_route4(const IPv4Net& network, const bool& unicast, const bool& multicast, const IPv4& nexthop, const uint32_t& metric, const XrlAtomList& policytags) { return _ct->policy_redistx_0_1_add_routex(network, unicast, multicast, nexthop, metric, policytags); } XrlCmdError XrlRipTarget::policy_redist4_0_1_delete_route4(const IPv4Net& network, const bool& unicast, const bool& multicast) { return _ct->policy_redistx_0_1_delete_routex(network, unicast, multicast); } xorp/rip/xrl_config.hh0000664000076400007640000000277311421137511015132 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/rip/xrl_config.hh,v 1.8 2008/10/02 21:58:18 bms Exp $ #ifndef __RIP_XRL_CONFIG_H__ #define __RIP_XRL_CONFIG_H__ /** * Get name to address RIB by in XRL calls. */ const char* xrl_rib_name(); /** * Get name to address FEA by in XRL calls. */ const char* xrl_fea_name(); /** * Set name to address RIB by in XRL calls. * * NB this call should be made before any calls to xrl_rib_name(). */ void set_xrl_rib_name(const char* rib_name); /** * Set name to address FEA by in XRL calls. * * NB this call should be made before any calls to xrl_rib_name(). */ void set_xrl_fea_name(const char* fea_name); #endif // __RIP_XRL_CONFIG_H__ xorp/rip/peer.cc0000664000076400007640000000752411421137511013720 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "peer.hh" #include "port.hh" #include "system.hh" template uint32_t PeerRoutes::expiry_secs() const { return _peer.expiry_secs(); } template uint32_t PeerRoutes::deletion_secs() const { return _peer.deletion_secs(); } template Peer::Peer(RipPort& p, const Addr& addr) : RouteEntryOrigin(false), _port(p), _addr(addr), _peer_routes(*this) { RouteDB& rdb = _port.port_manager().system().route_db(); rdb.insert_peer(this); } template Peer::~Peer() { RouteDB& rdb = _port.port_manager().system().route_db(); rdb.erase_peer(this); // // XXX: explicitly remove all peer routes, because we don't use them // as an indication whether the peer has expired. // _peer_routes.clear(); } template uint32_t Peer::expiry_secs() const { return port().constants().expiry_secs(); } template uint32_t Peer::deletion_secs() const { return port().constants().deletion_secs(); } template bool Peer::update_route(const IPNet& net, const A& nexthop, uint32_t cost, uint32_t tag, const PolicyTags& policytags) { string ifname, vifname; // Get the interface and vif name if (port().io_handler() != NULL) { ifname = port().io_handler()->ifname(); vifname = port().io_handler()->vifname(); } Route* route = _peer_routes.find_route(net); if (route == NULL) { RouteEntryOrigin* origin = &_peer_routes; route = new Route(net, nexthop, ifname, vifname, cost, origin, tag, policytags); } set_expiry_timer(route); RouteDB& rdb = _port.port_manager().system().route_db(); return (rdb.update_route(net, nexthop, ifname, vifname, cost, tag, this, policytags, false)); } template void Peer::push_routes() { RouteDB& rdb = _port.port_manager().system().route_db(); vector*> routes; if (! port().enabled()) return; // XXX: push routes only for enabled ports _peer_routes.dump_routes(routes); typename vector*>::const_iterator ri; for (ri = routes.begin(); ri != routes.end(); ++ri) { const RouteEntry* r = *ri; rdb.update_route(r->net(), r->nexthop(), r->ifname(), r->vifname(), r->cost(), r->tag(), this, r->policytags(), true); } } template void Peer::set_expiry_timer(Route* route) { XorpTimer t; uint32_t secs = expiry_secs(); EventLoop& eventloop = _port.port_manager().eventloop(); if (secs) { t = eventloop.new_oneoff_after_ms(secs * 1000, callback(this, &Peer::expire_route, route)); } route->set_timer(t); } template void Peer::expire_route(Route* route) { delete route; } // ---------------------------------------------------------------------------- // Instantiations #ifdef INSTANTIATE_IPV4 template class Peer; #endif #ifdef INSTANTIATE_IPV6 template class Peer; #endif xorp/rip/packets.hh0000664000076400007640000007377311540224234014443 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/rip/packets.hh,v 1.30 2008/10/02 21:58:16 bms Exp $ #ifndef __RIP_PACKET_ENTRIES_HH__ #define __RIP_PACKET_ENTRIES_HH__ #include "libxorp/ipv4.hh" #include "libxorp/ipv4net.hh" #include "libxorp/ipv6.hh" #include "libxorp/ipv6net.hh" #include "libxorp/utility.h" #include "libproto/packet.hh" #include "constants.hh" /** * @short Header appearing at the start of each RIP packet. * * The RIP packet header has the following content: * * command (1 byte): // The command: 1 - request, 2 - response * version (1 byte): // 1 - IPv4 RIPv1/IPv6 RIPng, 2 - IPv4 RIP v2 * unused0 (1 byte): // Unused * unused1 (1 byte): // Unused */ class RipPacketHeader { public: RipPacketHeader(const uint8_t* data) : _data(data), _command(_data + _command_offset), _version(_data + _version_offset), _unused0(_data + _unused0_offset), _unused1(_data + _unused1_offset) { x_static_assert(RipPacketHeader::SIZE == _command_sizeof + _version_sizeof + _unused0_sizeof + _unused1_sizeof); x_static_assert(RipPacketHeader::SIZE == _unused1_offset + _unused1_sizeof); } static const uint8_t REQUEST = 1; static const uint8_t RESPONSE = 2; static const uint8_t IPv4_VERSION = RIP_AF_CONSTANTS::PACKET_VERSION; static const uint8_t IPv6_VERSION = RIP_AF_CONSTANTS::PACKET_VERSION; static const size_t SIZE = 4; // The header size /** * Get the RIP packet header size. * * @return the RIP packet header size. */ static size_t size() { return RipPacketHeader::SIZE; } uint8_t command() const { return extract_8(_command); } uint8_t version() const { return extract_8(_version); } uint8_t unused0() const { return extract_8(_unused0); } uint8_t unused1() const { return extract_8(_unused1); } bool valid_command() const; bool valid_version(uint8_t v) const { return version() == v; } bool valid_padding() const; protected: // Sizes of the fields static const size_t _command_sizeof = 1; static const size_t _version_sizeof = 1; static const size_t _unused0_sizeof = 1; static const size_t _unused1_sizeof = 1; // Offsets for the fields static const size_t _command_offset = 0; static const size_t _version_offset = _command_offset + _command_sizeof; static const size_t _unused0_offset = _version_offset + _version_sizeof; static const size_t _unused1_offset = _unused0_offset + _unused0_sizeof; private: const uint8_t* _data; // The buffer data // Pointers to the fields const uint8_t* _command; // 1 - request, 2 - response const uint8_t* _version; // 1 - IPv4 RIPv1/IPv6 RIPng, 2 - IPv4 RIP v2 const uint8_t* _unused0; const uint8_t* _unused1; }; inline bool RipPacketHeader::valid_command() const { return command() == REQUEST || command() == RESPONSE; } inline bool RipPacketHeader::valid_padding() const { return (unused0() | unused1()) == 0; } /* * @short Class for writing data to RIP packet header. */ class RipPacketHeaderWriter : public RipPacketHeader { public: RipPacketHeaderWriter(uint8_t* data) : RipPacketHeader(data), _data(data), _command(_data + _command_offset), _version(_data + _version_offset), _unused0(_data + _unused0_offset), _unused1(_data + _unused1_offset) {} void initialize(uint8_t cmd, uint8_t vers); private: uint8_t* _data; // The buffer data // Pointers to the fields uint8_t* _command; // 1 - request, 2 - response uint8_t* _version; // 1 - IPv4 RIPv1/IPv6 RIPng, 2 - IPv4 RIP v2 uint8_t* _unused0; uint8_t* _unused1; }; inline void RipPacketHeaderWriter::initialize(uint8_t cmd, uint8_t vers) { embed_8(_command, cmd); embed_8(_version, vers); embed_8(_unused0, 0); embed_8(_unused1, 0); } /** * @short Route Entry as it appears in a RIP Packet. * * This structure is useful only it's specialized forms * @ref PacketRouteEntry and @ref PacketRouteEntry. */ template class PacketRouteEntry { }; /* * @short Class for writing data to RIP route entry. * * This structure is useful only it's specialized forms * @ref PacketRouteEntryWriter and @ref PacketRouteEntryWriter. */ template class PacketRouteEntryWriter { }; /** * Smallest RIPv2 packet size. */ static const size_t RIPv2_MIN_PACKET_BYTES = 4; static const size_t RIP_MIN_PACKET_BYTES = 4; /** * Smallest authenticated RIPv2 packet size. */ static const size_t RIPv2_MIN_AUTH_PACKET_BYTES = 4 + 20; /** * Largest RIPv2 packet size. */ static const size_t RIPv2_MAX_PACKET_BYTES = 4 + 20 * RIPv2_ROUTES_PER_PACKET; /** * @short Route Entry appearing in RIP packets on IPv4. * * This payload is carried in RIP packets on IPv4. The entry contains * all the fields for RIPv2. RIPv1 and RIPv2 use the same size * structure, except RIPv1 treats the route tag, subnet mask and * nexthop fields as Must-Be-Zero (MBZ) items. The interpretation of * the fields is described in RFC2453. * * All items in the route entry are stored in network order. The * accessor methods provide values in host order, and the modifiers * take arguments in host order. * * The route entry has the following content: * * af (2 bytes): // The address family * tag (2 bytes): // Tag * addr (4 bytes): // Address * mask (4 bytes): // Mask * nh (4 bytes): // Next hop * metric (4 bytes): // Metric */ template <> class PacketRouteEntry { public: PacketRouteEntry(const uint8_t* data) : _data(data), _af(_data + _af_offset), _tag(_data + _tag_offset), _addr(_data + _addr_offset), _mask(_data + _mask_offset), _nh(_data + _nh_offset), _metric(_data + _metric_offset) { x_static_assert(PacketRouteEntry::SIZE == _af_sizeof + _tag_sizeof + _addr_sizeof + _mask_sizeof + _nh_sizeof + _metric_sizeof); x_static_assert(PacketRouteEntry::SIZE == _metric_offset + _metric_sizeof); } static const size_t SIZE = 20; // The entry size /** * Get the RIP IPv4 route entry size. * * @return the RIP IPv4 route entry size. */ static size_t size() { return PacketRouteEntry::SIZE; } uint16_t addr_family() const { return extract_16(_af); } uint16_t tag() const { return extract_16(_tag); } IPv4 addr() const { return IPv4(_addr); } uint32_t prefix_len() const; IPv4Net net() const; IPv4 nexthop() const { return IPv4(_nh); } uint32_t metric() const { return extract_32(_metric); } /** * @return true if route entry has properties of a table request. */ bool is_table_request() const; /** * @return true if route entry has properties of an authentication entry. */ bool is_auth_entry() const; static const uint16_t ADDR_FAMILY = 2; static const uint16_t ADDR_FAMILY_DUMP = 0; static const uint16_t ADDR_FAMILY_AUTH = 0xffff; protected: // Sizes of the fields static const size_t _af_sizeof = 2; static const size_t _tag_sizeof = 2; static const size_t _addr_sizeof = 4; static const size_t _mask_sizeof = 4; static const size_t _nh_sizeof = 4; static const size_t _metric_sizeof = 4; // Offsets for the fields static const size_t _af_offset = 0; static const size_t _tag_offset = _af_offset + _af_sizeof; static const size_t _addr_offset = _tag_offset + _tag_sizeof; static const size_t _mask_offset = _addr_offset + _addr_sizeof; static const size_t _nh_offset = _mask_offset + _mask_sizeof; static const size_t _metric_offset = _nh_offset + _nh_sizeof; private: const uint8_t* _data; // The buffer data // Pointers to the fields const uint8_t* _af; const uint8_t* _tag; const uint8_t* _addr; const uint8_t* _mask; const uint8_t* _nh; const uint8_t* _metric; }; inline uint32_t PacketRouteEntry::prefix_len() const { return IPv4(_mask).mask_len(); } inline IPv4Net PacketRouteEntry::net() const { return IPv4Net(IPv4(_addr), IPv4(_mask).mask_len()); } inline bool PacketRouteEntry::is_table_request() const { return addr_family() == ADDR_FAMILY_DUMP && metric() == RIP_INFINITY; } inline bool PacketRouteEntry::is_auth_entry() const { return addr_family() == ADDR_FAMILY_AUTH; } /* * @short Class for writing data to RIP IPv4 route entry. */ template <> class PacketRouteEntryWriter : public PacketRouteEntry { public: PacketRouteEntryWriter(uint8_t* data) : PacketRouteEntry(data), _data(data), _af(_data + _af_offset), _tag(_data + _tag_offset), _addr(_data + _addr_offset), _mask(_data + _mask_offset), _nh(_data + _nh_offset), _metric(_data + _metric_offset) {} /** * Initialize fields as a regular routing entry. */ void initialize(uint16_t tag, const IPv4Net& net, IPv4 nh, uint32_t cost); /** * Initialize fields as a route table request. */ void initialize_table_request(); private: uint8_t* _data; // The buffer data // Pointers to the fields uint8_t* _af; uint8_t* _tag; uint8_t* _addr; uint8_t* _mask; uint8_t* _nh; uint8_t* _metric; }; inline void PacketRouteEntryWriter::initialize(uint16_t tag, const IPv4Net& net, IPv4 nh, uint32_t cost) { embed_16(_af, ADDR_FAMILY); embed_16(_tag, tag); net.masked_addr().copy_out(_addr); net.netmask().copy_out(_mask); nh.copy_out(_nh); embed_32(_metric, cost); } inline void PacketRouteEntryWriter::initialize_table_request() { embed_16(_af, ADDR_FAMILY_DUMP); embed_16(_tag, 0); IPv4::ZERO().copy_out(_addr); IPv4::ZERO().copy_out(_mask); IPv4::ZERO().copy_out(_nh); embed_32(_metric, RIP_INFINITY); } /** * @short Route Entry for Plaintext password. * * The PlaintextPacketRouteEntry4 may appear as the first route entry * in a RIPv2 packet. It has the same size as an @ref * PacketRouteEntry. The address family has the special value * 0xffff which implies authentication. The authentication type is * overlaid in the route tag field and takes the special value 2. * * All items in the route entry are stored in network order. The * accessor methods provide values in host order, and the modifiers * take arguments in host order. * * The plaintext authentication entry has the following content: * * af (2 bytes): // The address family * auth (2 bytes): // Authentication type * pw (16 bytes): // Password */ class PlaintextPacketRouteEntry4 { public: PlaintextPacketRouteEntry4(const uint8_t* data) : _data(data), _af(_data + _af_offset), _auth(_data + _auth_offset), _pw(_data + _pw_offset) { x_static_assert(PlaintextPacketRouteEntry4::SIZE == _af_sizeof + _auth_sizeof + _pw_sizeof); x_static_assert(PlaintextPacketRouteEntry4::SIZE == _pw_offset + _pw_sizeof); } static const size_t SIZE = 20; // The entry size /** * Get the RIP IPv4 plaintext password route entry size. * * @return the RIP IPv4 plaintext password route entry size. */ static size_t size() { return PlaintextPacketRouteEntry4::SIZE; } uint16_t addr_family() const { return extract_16(_af); } uint16_t auth_type() const { return extract_16(_auth); } string password() const; static const uint16_t ADDR_FAMILY = 0xffff; static const uint16_t AUTH_TYPE = 2; protected: // Sizes of the fields static const size_t _af_sizeof = 2; static const size_t _auth_sizeof = 2; static const size_t _pw_sizeof = 16; // Offsets for the fields static const size_t _af_offset = 0; static const size_t _auth_offset = _af_offset + _af_sizeof; static const size_t _pw_offset = _auth_offset + _auth_sizeof; private: const uint8_t* _data; // The buffer data // Pointers to the fields const uint8_t* _af; const uint8_t* _auth; const uint8_t* _pw; }; inline string PlaintextPacketRouteEntry4::password() const { return string(reinterpret_cast(_pw), 0, _pw_sizeof); } /** * @short Class for writing data to Plaintext password authentication entry. */ class PlaintextPacketRouteEntry4Writer : public PlaintextPacketRouteEntry4 { public: PlaintextPacketRouteEntry4Writer(uint8_t* data) : PlaintextPacketRouteEntry4(data), _data(data), _af(_data + _af_offset), _auth(_data + _auth_offset), _pw(_data + _pw_offset) {} /** * Initialize the entry. */ void initialize(const string& s); private: uint8_t* _data; // The buffer data // Pointers to the fields uint8_t* _af; uint8_t* _auth; uint8_t* _pw; }; inline void PlaintextPacketRouteEntry4Writer::initialize(const string& s) { embed_16(_af, ADDR_FAMILY); embed_16(_auth, AUTH_TYPE); memset(_pw, 0, _pw_sizeof); s.copy(reinterpret_cast(_pw), _pw_sizeof); } /** * @short Route Entry for MD5 data. * * The MD5PacketRouteEntry4 may appear as the first route entry * in a RIPv2 packet. It has the same size as an @ref * PacketRouteEntry. The address family has the special value * 0xffff which implies authentication. The authentication type is * overlaid in the route tag field and takes the special value 3. With * MD5 the authentication data appears after the remaining route entries. * Details are disclosed in RFC2082. * * All items in the route entry are stored in network order. The * accessor methods provide values in host order, and the modifiers * take arguments in host order. * * NB We describe the field labelled as "RIP-2 Packet Length" on page 5 of * RFC 2082 as the "auth_off". This matches the textual description in * the RFC. * * The MD5 authentication entry has the following content: * * af (2 bytes): // 0xffff - Authentication header * auth (2 bytes): // Authentication type * auth_off (2 bytes): // Offset of authentication data * key_id (1 byte): // Key number used * auth_bytes (1 byte): // Auth data length at end of packet * seqno (4 bytes): // Monotonically increasing seqno * mbz (8 bytes): // Must-be-zero */ class MD5PacketRouteEntry4 { public: MD5PacketRouteEntry4(const uint8_t* data) : _data(data), _af(_data + _af_offset), _auth(_data + _auth_offset), _auth_off(_data + _auth_off_offset), _key_id(_data + _key_id_offset), _auth_bytes(_data + _auth_bytes_offset), _seqno(_data + _seqno_offset), _mbz(_data + _mbz_offset) { x_static_assert(MD5PacketRouteEntry4::SIZE == _af_sizeof + _auth_sizeof + _auth_off_sizeof + _key_id_sizeof + _auth_bytes_sizeof + _seqno_sizeof + _mbz_sizeof); x_static_assert(MD5PacketRouteEntry4::SIZE == _mbz_offset + _mbz_sizeof); } static const size_t SIZE = 20; // The entry size /** * Get the RIP IPv4 MD5 authentication route entry size. * * @return the RIP IPv4 MD5 authentication route entry size. */ static size_t size() { return MD5PacketRouteEntry4::SIZE; } uint16_t addr_family() const { return extract_16(_af); } uint16_t auth_type() const { return extract_16(_auth); } uint16_t auth_off() const { return extract_16(_auth_off); } uint8_t key_id() const { return extract_8(_key_id); } uint8_t auth_bytes() const { return extract_8(_auth_bytes); } uint32_t seqno() const { return extract_32(_seqno); } static const uint16_t ADDR_FAMILY = 0xffff; static const uint16_t AUTH_TYPE = 3; protected: // Sizes of the fields static const size_t _af_sizeof = 2; static const size_t _auth_sizeof = 2; static const size_t _auth_off_sizeof = 2; static const size_t _key_id_sizeof = 1; static const size_t _auth_bytes_sizeof = 1; static const size_t _seqno_sizeof = 4; static const size_t _mbz_sizeof = 8; // Offsets for the fields static const size_t _af_offset = 0; static const size_t _auth_offset = _af_offset + _af_sizeof; static const size_t _auth_off_offset = _auth_offset + _auth_sizeof; static const size_t _key_id_offset = _auth_off_offset + _auth_off_sizeof; static const size_t _auth_bytes_offset = _key_id_offset + _key_id_sizeof; static const size_t _seqno_offset = _auth_bytes_offset + _auth_bytes_sizeof; static const size_t _mbz_offset = _seqno_offset + _seqno_sizeof; private: const uint8_t* _data; // The buffer data // Pointers to the fields const uint8_t* _af; // 0xffff - Authentication header const uint8_t* _auth; // Authentication type const uint8_t* _auth_off; // Offset of authentication data const uint8_t* _key_id; // Key number used const uint8_t* _auth_bytes; // Auth data length at end of packet const uint8_t* _seqno; // Monotonically increasing seqno const uint8_t* _mbz; // Must-be-zero }; /** * @short Class for writing data to MD5 authentication entry. */ class MD5PacketRouteEntry4Writer : public MD5PacketRouteEntry4 { public: MD5PacketRouteEntry4Writer(uint8_t* data) : MD5PacketRouteEntry4(data), _data(data), _af(_data + _af_offset), _auth(_data + _auth_offset), _auth_off(_data + _auth_off_offset), _key_id(_data + _key_id_offset), _auth_bytes(_data + _auth_bytes_offset), _seqno(_data + _seqno_offset), _mbz(_data + _mbz_offset) {} void set_auth_off(uint16_t b) { embed_16(_auth_off, b); } void set_key_id(uint8_t id) { embed_8(_key_id, id); } void set_auth_bytes(uint8_t b) { embed_8(_auth_bytes, b); } void set_seqno(uint32_t sno) { embed_32(_seqno, sno); } /** * Initialize the entry. */ void initialize(uint16_t pkt_bytes, uint8_t key_id, uint8_t auth_bytes, uint32_t seqno); private: uint8_t* _data; // The buffer data // Pointers to the fields uint8_t* _af; // 0xffff - Authentication header uint8_t* _auth; // Authentication type uint8_t* _auth_off; // Offset of authentication data uint8_t* _key_id; // Key number used uint8_t* _auth_bytes; // Auth data length at end of packet uint8_t* _seqno; // Monotonically increasing seqno uint8_t* _mbz; // Must-be-zero }; inline void MD5PacketRouteEntry4Writer::initialize(uint16_t auth_off, uint8_t key_id, uint8_t auth_bytes, uint32_t seqno) { embed_16(_af, ADDR_FAMILY); embed_16(_auth, AUTH_TYPE); set_auth_off(auth_off); set_key_id(key_id); set_auth_bytes(auth_bytes); set_seqno(seqno); memset(_mbz, 0, _mbz_sizeof); } /** * @short Container for MD5 trailer. * * THE MD5 authentication trailer has the following content: * * af (2 bytes): // 0xffff - Authentication header * one (2 bytes): // 0x01 - RFC2082 defined * auth_data (16 bytes); // 16 bytes of data */ class MD5PacketTrailer { public: MD5PacketTrailer(const uint8_t* data) : _data(data), _af(_data + _af_offset), _one(_data + _one_offset), _auth_data(_data + _auth_data_offset) { x_static_assert(MD5PacketTrailer::SIZE == _af_sizeof + _one_sizeof + _auth_data_sizeof); x_static_assert(MD5PacketTrailer::SIZE == _auth_data_offset + _auth_data_sizeof); } static const size_t SIZE = 20; // The trailer size /** * Get the RIP IPv4 MD5 authentication trailer size. * * @return the RIP IPv4 MD5 authentication trailer size. */ static size_t size() { return MD5PacketTrailer::SIZE; } uint16_t addr_family() const { return extract_16(_af); } const uint8_t* auth_data() const { return _auth_data; } uint32_t auth_data_bytes() const { return _auth_data_sizeof; } uint32_t auth_data_offset() const { return _auth_data_offset; } bool valid() const; protected: // Sizes of the fields static const size_t _af_sizeof = 2; static const size_t _one_sizeof = 2; static const size_t _auth_data_sizeof = 16; // Offsets for the fields static const size_t _af_offset = 0; static const size_t _one_offset = _af_offset + _af_sizeof; static const size_t _auth_data_offset = _one_offset + _one_sizeof; private: const uint8_t* _data; // The buffer data // Pointers to the fields const uint8_t* _af; // 0xffff - Authentication header const uint8_t* _one; // 0x01 - RFC2082 defined const uint8_t* _auth_data; // 16 bytes of data }; inline bool MD5PacketTrailer::valid() const { return (addr_family() == 0xffff) && (extract_16(_one) == 1); } /** * @short Class for writing data to MD5 authentication trailer. */ class MD5PacketTrailerWriter : public MD5PacketTrailer { public: MD5PacketTrailerWriter(uint8_t* data) : MD5PacketTrailer(data), _data(data), _af(_data + _af_offset), _one(_data + _one_offset), _auth_data(_data + _auth_data_offset) {} /** * Initialize the entry. */ void initialize(); uint8_t* auth_data() { return _auth_data; } private: uint8_t* _data; // The buffer data // Pointers to the fields uint8_t* _af; // 0xffff - Authentication header uint8_t* _one; // 0x01 - RFC2082 defined uint8_t* _auth_data; // 16 bytes of data }; inline void MD5PacketTrailerWriter::initialize() { embed_16(_af, 0xffff); embed_16(_one, 1); } /** * @short Route Entry appearing in RIP packets on IPv6. * * This payload is carried in RIP packets on IPv6. The interpretation * of the fields is defined in RFC2080. * * All fields in this structure are stored in network order. * * The route entry has the following content: * * prefix (16 bytes): // Prefix * tag (2 bytes): // Tag * prefix_len (1 byte): // Prefix length * metric (1 byte): // Metric */ template <> class PacketRouteEntry { public: PacketRouteEntry(const uint8_t* data) : _data(data), _prefix(_data + _prefix_offset), _tag(_data + _tag_offset), _prefix_len(_data + _prefix_len_offset), _metric(_data + _metric_offset) { x_static_assert(PacketRouteEntry::SIZE == _prefix_sizeof + _tag_sizeof + _prefix_len_sizeof + _metric_sizeof); x_static_assert(PacketRouteEntry::SIZE == _metric_offset + _metric_sizeof); } static const size_t SIZE = 20; // The entry size /** * Get the RIP IPv6 route entry size. * * @return the RIP IPv6 route entry size. */ static size_t size() { return PacketRouteEntry::SIZE; } bool is_nexthop() const; /** * @return true if route entry has properties of a table request. */ bool is_table_request() const; IPv6 nexthop() const; uint16_t tag() const; uint32_t prefix_len() const { return extract_8(_prefix_len); } IPv6Net net() const; uint8_t metric() const; static const uint8_t NEXTHOP_METRIC = 0xff; protected: // Sizes of the fields static const size_t _prefix_sizeof = 16; static const size_t _tag_sizeof = 2; static const size_t _prefix_len_sizeof = 1; static const size_t _metric_sizeof = 1; // Offsets for the fields static const size_t _prefix_offset = 0; static const size_t _tag_offset = _prefix_offset + _prefix_sizeof; static const size_t _prefix_len_offset = _tag_offset + _tag_sizeof; static const size_t _metric_offset = _prefix_len_offset + _prefix_len_sizeof; private: const uint8_t* _data; // The buffer data // Pointers to the fields const uint8_t* _prefix; const uint8_t* _tag; const uint8_t* _prefix_len; const uint8_t* _metric; }; inline bool PacketRouteEntry::is_nexthop() const { return metric() == NEXTHOP_METRIC; } inline IPv6 PacketRouteEntry::nexthop() const { return IPv6(_prefix); } inline uint16_t PacketRouteEntry::tag() const { return extract_16(_tag); } inline IPv6Net PacketRouteEntry::net() const { return IPv6Net(IPv6(_prefix), prefix_len()); } inline uint8_t PacketRouteEntry::metric() const { return extract_8(_metric); } inline bool PacketRouteEntry::is_table_request() const { if (metric() != RIP_INFINITY) { return false; } if (prefix_len() != 0) { return false; } IPv6 addr(_prefix); return (addr.is_zero()); } /* * @short Class for writing data to RIP IPv6 route entry. */ template <> class PacketRouteEntryWriter : public PacketRouteEntry { public: PacketRouteEntryWriter(uint8_t* data) : PacketRouteEntry(data), _data(data), _prefix(_data + _prefix_offset), _tag(_data + _tag_offset), _prefix_len(_data + _prefix_len_offset), _metric(_data + _metric_offset) {} /** * Initialize fields as a regular routing entry. */ void initialize_route(uint16_t route_tag, const IPv6Net& net, uint8_t cost); /** * Initialize fields as a nexthop entry. */ void initialize_nexthop(const IPv6& nexthop); /** * Initialize fields as a route table request. */ void initialize_table_request(); private: uint8_t* _data; // The buffer data // Pointers to the fields uint8_t* _prefix; uint8_t* _tag; uint8_t* _prefix_len; uint8_t* _metric; }; inline void PacketRouteEntryWriter::initialize_route(uint16_t tag, const IPv6Net& net, uint8_t cost) { net.masked_addr().copy_out(_prefix); embed_16(_tag, tag); embed_8(_prefix_len, net.prefix_len()); embed_8(_metric, cost); } inline void PacketRouteEntryWriter::initialize_nexthop(const IPv6& nexthop) { nexthop.copy_out(_prefix); embed_16(_tag, 0); embed_8(_prefix_len, 0); embed_8(_metric, NEXTHOP_METRIC); } inline void PacketRouteEntryWriter::initialize_table_request() { IPv6::ZERO().copy_out(_prefix); embed_16(_tag, 0); embed_8(_prefix_len, 0); embed_8(_metric, RIP_INFINITY); } /** * @short RIP Packet class. * * A container for RIP packet, provides easy to use accessors and modifiers. */ template class RipPacket { public: typedef A Addr; /** * @return destination address of packet. */ const Addr& address() const { return _addr; } /** * @return destination port of packet. */ uint16_t port() const { return _port; } /** * @return the maximum number of route entries packet may have. */ uint32_t max_entries() const { return _max_entries; } /** * Set the maximum number of route entries a packet may have. * This method should be called before using @ref append_data * methods as it resizes the internal storage and will cause * appended data to be lost. */ void set_max_entries(uint32_t max_entries); RipPacket(const Addr& addr, uint16_t port, uint32_t max_entries = RIPv2_ROUTES_PER_PACKET) : _addr(addr), _port(port), _max_entries(0) { set_max_entries(max_entries); } /** * @return const pointer to the beginning of the RIP packet header. */ const uint8_t* header_ptr() const; /** * @return pointer to the beginning of the RIP packet header. */ uint8_t* header_ptr(); /** * Route entry accessor. * * @param entry_no index of route entry to retrive. * @return const pointer to route entry, or 0 if entry_no is greater than * the maximum route entries associated with packet. */ const uint8_t* route_entry_ptr(uint32_t entry_no) const; /** * Route entry accessor. * * @param entry_no index of route entry to retrive. * @return pointer to route entry, or 0 if entry_no is greater than * the maximum route entries associated with packet. */ uint8_t* route_entry_ptr(uint32_t entry_no); void append_data(const uint8_t* data, uint32_t data_bytes); void append_data(const vector& data); vector& data() { return _data; } const vector& data() const { return _data; } uint32_t data_bytes() const { return _data.size(); } const uint8_t* data_ptr() const { return base_ptr(); } uint8_t* data_ptr() { return base_ptr(); } private: Addr _addr; // Src addr on inbound, dst address on outbound uint16_t _port; // Src port on inbound, dst port on outbound vector _data; // Data buffer uint32_t _max_entries; // Maximum number of route entries in packet const uint8_t* base_ptr() const { return &(_data[0]); } uint8_t* base_ptr() { return &(_data[0]); } }; template const uint8_t* RipPacket::header_ptr() const { return (base_ptr()); } template uint8_t* RipPacket::header_ptr() { return (base_ptr()); } template const uint8_t* RipPacket::route_entry_ptr(uint32_t entry_no) const { if (entry_no >= _max_entries) return NULL; const uint8_t* p = base_ptr() + RipPacketHeader::size() + entry_no * PacketRouteEntry::size(); return p; } template uint8_t* RipPacket::route_entry_ptr(uint32_t entry_no) { if (entry_no >= _max_entries) return NULL; uint8_t* p = base_ptr() + RipPacketHeader::size() + entry_no * PacketRouteEntry::size(); return p; } template void RipPacket::append_data(const uint8_t* data, uint32_t data_bytes) { _data.insert(_data.end(), data, data + data_bytes); } template void RipPacket::append_data(const vector& data) { _data.insert(_data.end(), data.begin(), data.end()); } template void RipPacket::set_max_entries(uint32_t max_entries) { if (max_entries != _max_entries) { _data.resize(PacketRouteEntry::size() * max_entries + RipPacketHeader::size()); _max_entries = max_entries; } } #endif // __RIP_PACKETS_HH__ xorp/rip/rip_module.h0000664000076400007640000000230011421137511014751 0ustar greearbgreearb/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- */ /* * Copyright (c) 2001-2009 XORP, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 2, June * 1991 as published by the Free Software Foundation. Redistribution * and/or modification of this program under the terms of any other * version of the GNU General Public License is not permitted. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, * see the GNU General Public License, Version 2, a copy of which can be * found in the XORP LICENSE.gpl file. * * XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; * http://xorp.net */ /* * $XORP: xorp/rip/rip_module.h,v 1.10 2008/10/02 21:58:17 bms Exp $ */ /* * Module definitions. */ #ifndef __RIP_RIP_MODULE_H__ #define __RIP_RIP_MODULE_H__ #ifndef XORP_MODULE_NAME #define XORP_MODULE_NAME "RIP" #endif #ifndef XORP_MODULE_VERSION #define XORP_MODULE_VERSION "0.1" #endif #endif /* __RIP_RIP_MODULE_H__ */ xorp/rip/route_entry.hh0000664000076400007640000002323711540225534015362 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/rip/route_entry.hh,v 1.23 2008/10/02 21:58:17 bms Exp $ #ifndef __RIP_ROUTE_ENTRY_HH__ #define __RIP_ROUTE_ENTRY_HH__ #include "libxorp/xorp.h" #include "libxorp/ipnet.hh" #include "libxorp/timer.hh" #include "policy/backend/policytags.hh" template class RouteEntryOrigin; template class RouteEntryRef; /** * RIP Route Entry Class. * * This class is used for storing RIPv2 and RIPng route entries. It is * a template class taking an address family type as a template argument. * Only IPv4 and IPv6 types may be supplied. */ template class RouteEntry : public NONCOPYABLE { public: typedef A Addr; typedef IPNet Net; typedef RouteEntryOrigin Origin; public: /** * Constructor. * * The constructor set the internal state according to the parameters and * if the Origin is non-null makes the appropriate call to tell the * Origin of it's existence. */ RouteEntry(const Net& n, const Addr& nh, const string& ifname, const string& vifname, uint16_t cost, Origin*& o, uint16_t tag); RouteEntry(const Net& n, const Addr& nh, const string& ifname, const string& vifname, uint16_t cost, Origin*& o, uint16_t tag, const PolicyTags& policytags); /** * Destructor. * * Cleans up state associated with RouteEntry. If the Origin associated * with the RouteEntry is not-null, the Origin is informed of the * destruction. */ ~RouteEntry(); /** * Get network. */ const IPNet& net() const { return _net; } /** * Set next hop. * * @param nh the new nexthop to be associated with Route Entry. * * @return true if the stored nexthop changed, false otherwise. */ bool set_nexthop(const A& nh); /** * Get next hop. * * @return nexthop address associated with the route entry. */ const A& nexthop() const { return _nh; } /** * Get the outgoing interface name. * * @return the outgoing interface name. */ const string& ifname() const { return _ifname; } /** * Set the outgoing interface name. * * @param ifname the outgoing interface name. * @return true if the stored interface name changed, false otherwise. */ bool set_ifname(const string& ifname); /** * Get the outgoing vif name. * * @return the outgoing vif name. */ const string& vifname() const { return _vifname; } /** * Set the outgoing vif name. * * @param vifname the outgoing vif name. * @return true if the stored vif name changed, false otherwise. */ bool set_vifname(const string& vifname); /** * Set the cost metric. * * @param cost the new cost to be associated with the Route Entry. * * @return true if stored cost changed, false otherwise. */ bool set_cost(uint16_t cost); /** * Get the cost metric. * * @return the cost associated with the route entry. */ uint16_t cost() const { return _cost; } /** * Set the origin. If the origin is different from the stored origin, * the RouteEntry dissociates itself from the current origin and * informs the new origin of it's existence. * * @param origin the new origin to be associated with the route entry. * * @return true if the stored origin changed, false otherwise. */ bool set_origin(Origin* origin); /** * Get the origin. * * @return a pointer to the origin associated with the route entry. */ const Origin* origin() const { return _origin; } /** * Get the origin. * * @return a pointer to the origin associated with the route entry. */ Origin* origin() { return _origin; } /** * Set the tag value. * * @param tag the tag value to be associated with the route entry. * * @return true if the stored tag changed, false otherwise. */ bool set_tag(uint16_t tag); /** * Get the tag. * * @return tag value associated with the route entry. */ uint16_t tag() const { return _tag; } /** * Set a Timer Event associated with this route. */ void set_timer(const XorpTimer& t) { _timer = t; } /** * Get Timer associated with route. */ const XorpTimer& timer() const { return _timer; } /** * @return policy-tags associated with route. */ const PolicyTags& policytags() const { return _policytags; } PolicyTags& policytags() { return _policytags; } /** * Replace policy-tags of route * @return true if tags were modified. * @param tags new policy-tags. */ bool set_policytags(const PolicyTags& tags); /** * @return true if route was rejected by policy filter. */ bool filtered() const { return _filtered; } /** * Set if route is accepted or rejected. * * @param v true if route is filtered */ void set_filtered(bool v) { _filtered = v; } private: friend class RouteEntryRef; void ref() { _ref_cnt++; } uint16_t unref() { return --_ref_cnt; } uint16_t ref_cnt() const { return _ref_cnt; } protected: void dissociate(); void associate(Origin* o); protected: Net _net; Addr _nh; string _ifname; string _vifname; uint16_t _cost; Origin* _origin; uint16_t _tag; uint16_t _ref_cnt; XorpTimer _timer; PolicyTags _policytags; bool _filtered; }; /** * @short RouteEntry reference class. * * A reference pointer that manipulates a counter embedded within * RouteEntry objects and deletes them when the counter goes to zero. Having * the counter embedded in the object makes it easier to reference count * than using ref_ptr. */ template class RouteEntryRef { private: RouteEntry* _rt; protected: void release() { if (_rt && _rt->unref() == 0) delete _rt; } public: RouteEntryRef(RouteEntry* r) : _rt(r) { _rt->ref(); } RouteEntryRef() : _rt(0) {} ~RouteEntryRef() { release(); } RouteEntryRef(const RouteEntryRef& o) : _rt(o._rt) { if (_rt) _rt->ref(); } RouteEntryRef& operator=(const RouteEntryRef& o) { release(); _rt = o._rt; if (_rt) _rt->ref(); return *this; } RouteEntry* get() const { return _rt; } RouteEntry* operator->() const { return _rt; } bool operator==(const RouteEntryRef& o) const { return _rt == o._rt; } }; /** * Base class for originators of RIP route entires. * * This class is used for storing RIPv2 and RIPng route entries. It is * a template class taking an address family type as a template argument. * Only IPv4 and IPv6 types may be supplied. */ template class RouteEntryOrigin : public NONCOPYABLE { public: typedef RouteEntry Route; typedef IPNet Net; struct RouteEntryStore; public: RouteEntryOrigin(bool is_rib_origin); virtual ~RouteEntryOrigin(); /** * Test if RIB is the originator. * * @return true if RIB is the originator, otherwise false. */ bool is_rib_origin() const { return _is_rib_origin; } /** * Associate route with this RouteEntryOrigin. * @param r route to be stored. * @return true on success, false if route is already associated. */ bool associate(Route* r); /** * Dissociate route from this RouteEntryOrigin. * @param r route to be dissociated. * @return true on success, false if route is not associated. */ bool dissociate(Route* r); /** * Find route if RouteOrigin has a route for given network. * @param n network. * @return true if entry exists in store, false otherwise. */ Route* find_route(const Net& n) const; /** * @return number of routes associated with this RouteEntryOrigin. */ uint32_t route_count() const; /** * Clear/remove all routes associated with this RouteEntryOrigin. */ void clear(); /** * Dump associated routes into a vector (debugging use only). */ void dump_routes(vector& routes) const; /** * Retrieve number of seconds before routes associated with this * RouteEntryOrigin should be marked as expired. A return value of 0 * indicates routes are of infinite duration, eg static routes. */ virtual uint32_t expiry_secs() const = 0; /** * Retrieve number of seconds before route should be deleted after * expiry. */ virtual uint32_t deletion_secs() const = 0; protected: struct RouteEntryStore* _rtstore; private: bool _is_rib_origin; // True if the origin is RIB }; #endif // __RIP_ROUTE_ENTRY_HH__ xorp/rip/xrl_port_manager.cc0000664000076400007640000003510211421137511016321 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING #include "rip_module.h" #include "libxorp/eventloop.hh" #include "libxorp/ipv4.hh" #include "libxorp/ipv6.hh" #include "libxorp/debug.h" #include "libxipc/xrl_router.hh" #include "libfeaclient/ifmgr_atoms.hh" #include "port.hh" #include "xrl_port_manager.hh" #include "xrl_port_io.hh" // ---------------------------------------------------------------------------- // Utility methods /** * Query whether an address exists on given interface and vif path and all * items on path are enabled. */ template static bool address_exists(const IfMgrIfTree& iftree, const string& ifname, const string& vifname, const A& addr) { debug_msg("Looking for %s/%s/%s\n", ifname.c_str(), vifname.c_str(), addr.str().c_str()); const IfMgrIfAtom* ia = iftree.find_interface(ifname); if (ia == NULL) return false; const IfMgrVifAtom* va = ia->find_vif(vifname); if (va == NULL) return false; const typename IfMgrIP::Atom* aa = va->find_addr(addr); if (aa == NULL) return false; return true; } /** * Query whether an address exists on given interface and vif path and all * items on path are enabled. */ template static bool address_enabled(const IfMgrIfTree& iftree, const string& ifname, const string& vifname, const A& addr) { debug_msg("Looking for %s/%s/%s\n", ifname.c_str(), vifname.c_str(), addr.str().c_str()); const IfMgrIfAtom* ia = iftree.find_interface(ifname); if (ia == 0 || ia->enabled() == false || ia->no_carrier()) { debug_msg("if %s exists ? %d ?\n", ifname.c_str(), (ia ? 1 : 0)); return false; } const IfMgrVifAtom* va = ia->find_vif(vifname); if (va == 0 || va->enabled() == false) { debug_msg("vif %s exists ? %d?\n", vifname.c_str(), (va ? 1: 0)); return false; } const typename IfMgrIP::Atom* aa = va->find_addr(addr); if (aa == 0 || aa->enabled() == false) { debug_msg("addr %s exists ? %d?\n", addr.str().c_str(), (aa ? 1: 0)); return false; } debug_msg("Found\n"); return true; } /** * Unary function object to test whether a particular address is * associated with a RIP port. */ template struct port_has_address { port_has_address(const A& addr) : _addr(addr) {} bool operator() (const Port* p) { const PortIOBase* io = p->io_handler(); return io && io->address() == _addr; } private: A _addr; }; /** * Unary function object to test whether a particular port's io * system is in a given state. */ template struct port_has_io_in_state { port_has_io_in_state(ServiceStatus st) : _st(st) {} bool operator() (const Port* p) const { const PortIOBase* io = p->io_handler(); const XrlPortIO* xio = dynamic_cast*>(io); if (xio == 0) return false; return xio->status() == _st; } protected: ServiceStatus _st; }; /** * Unary function object to test whether a particular RIP port is appropriate * for packet arriving on socket. * * NB At a future date we might want to track socket id to XrlPortIO * mappings. This would be more efficient. */ template struct is_port_for { is_port_for(const string* sockid, const string* ifname, const string* vifname, const A* addr, IfMgrXrlMirror* im) : _psid(sockid), _ifname(ifname), _vifname(vifname), _pa(addr), _pim(im) {} bool operator() (Port*& p); protected: bool link_addr_valid() const; private: const string* _psid; const string* _ifname; const string* _vifname; const A* _pa; IfMgrXrlMirror* _pim; }; template <> inline bool is_port_for::link_addr_valid() const { return true; } template <> inline bool is_port_for::link_addr_valid() const { return _pa->is_linklocal_unicast(); } template bool is_port_for::operator() (Port*& p) { // // Perform address family specific check for source address being // link-local. For IPv4 the concept does not exist, for IPv6 // check if origin is link local. // if (link_addr_valid() == false) { return false; } PortIOBase* io = p->io_handler(); XrlPortIO* xio = dynamic_cast*>(io); if (xio == 0) return false; // If another socket, ignore if (xio->socket_id() != *_psid) return false; // If our packet, ignore if (xio->address() == *_pa) return false; // Check the incoming interface and vif name (if known) if ((! _ifname->empty()) && (! _vifname->empty())) { if (xio->ifname() != *_ifname) return false; if (xio->vifname() != *_vifname) return false; } // // Packet has arrived on multicast socket and is not one of ours. // // Check source address to find originating neighbour on local nets // or p2p link. // const typename IfMgrIP::Atom* ifa; ifa = _pim->iftree().find_addr(xio->ifname(), xio->vifname(), xio->address()); if (ifa == 0) { return false; } if (ifa->has_endpoint()) { return ifa->endpoint_addr() == *_pa; } IPNet n(ifa->addr(), ifa->prefix_len()); return n.contains(*_pa); } // ---------------------------------------------------------------------------- // XrlPortManager template int XrlPortManager::startup() { set_status(SERVICE_STARTING); // Transition to SERVICE_RUNNING occurs when tree_complete() is called, ie // we have interface/vif/address state available. return (XORP_OK); } template int XrlPortManager::shutdown() { set_status(SERVICE_SHUTTING_DOWN); typename PortManagerBase::PortList& pl = this->ports(); typename PortManagerBase::PortList::iterator i = pl.begin(); // XXX Walk ports and shut them down. Only when they are all // shutdown should we consider ourselves shutdown. debug_msg("XXX XrlPortManager::shutdown (%p)\n", this); debug_msg("XXX n_ports = %u n_dead_ports %u\n", XORP_UINT_CAST(this->ports().size()), XORP_UINT_CAST(_dead_ports.size())); while (i != pl.end()) { Port* p = *i; XrlPortIO* xio = dynamic_cast*>(p->io_handler()); if (xio) { _dead_ports.insert(make_pair(xio, p)); xio->shutdown(); pl.erase(i++); debug_msg(" XXX killing port %p\n", p); } else { i++; debug_msg(" XXX skipping port %p\n", p); } } return (XORP_OK); } template void XrlPortManager::tree_complete() { debug_msg("XrlPortManager::tree_complete notification\n", XORP_UINT_CAST(A::ip_version())); set_status(SERVICE_RUNNING); } template void XrlPortManager::updates_made() { debug_msg("XrlPortManager::updates_made notification\n", XORP_UINT_CAST(A::ip_version())); // Scan ports and enable/disable underlying I/O handler // according to fea state typename PortManagerBase::PortList::iterator pi; for (pi = this->ports().begin(); pi != this->ports().end(); ++pi) { Port* p = *pi; XrlPortIO* xio = dynamic_cast*>(p->io_handler()); if (xio == 0) continue; bool fea_en = address_enabled(_ifm.iftree(), xio->ifname(), xio->vifname(), xio->address()); if (fea_en != xio->enabled()) { XLOG_INFO("Detected iftree change on %s %s %s setting transport enabled %s", xio->ifname().c_str(), xio->vifname().c_str(), xio->address().str().c_str(), bool_c_str(fea_en)); xio->set_enabled(fea_en); } } } template bool XrlPortManager::add_rip_address(const string& ifname, const string& vifname, const A& addr) { if (status() != SERVICE_RUNNING) { debug_msg("add_rip_address failed: not running.\n"); return false; } // Check whether address exists, fail if not. const IfMgrIfTree& iftree = _ifm.iftree(); if (address_exists(iftree, ifname, vifname, addr) == false) { debug_msg("add_rip_address failed: address does not exist.\n"); return false; } // Check if port already exists typename PortManagerBase::PortList::const_iterator pi; pi = find_if(this->ports().begin(), this->ports().end(), port_has_address(addr)); if (pi != this->ports().end()) return true; // Create port Port* p = new Port(*this); this->ports().push_back(p); // Create XrlPortIO object for port XrlPortIO* io = new XrlPortIO(_xr, *p, ifname, vifname, addr); // Bind io to port p->set_io_handler(io, false); // Add self to observers of io objects status io->set_observer(this); // Start port's I/O handler if no others are starting up already try_start_next_io_handler(); return true; } template bool XrlPortManager::remove_rip_address(const string& /* ifname */, const string& /* vifname */, const A& addr) { typename PortManagerBase::PortList& pl = this->ports(); typename PortManagerBase::PortList::iterator i; i = find_if(pl.begin(), pl.end(), port_has_address(addr)); if (i != pl.end()) { Port* p = *i; XrlPortIO* xio = dynamic_cast*>(p->io_handler()); if (xio) { _dead_ports.insert(make_pair(xio, p)); xio->shutdown(); } pl.erase(i); } return true; } template bool XrlPortManager::deliver_packet(const string& sockid, const string& ifname, const string& vifname, const A& src_addr, uint16_t src_port, const vector& pdata) { typename PortManagerBase::PortList& pl = this->ports(); typename PortManagerBase::PortList::iterator i; Port* p = NULL; XLOG_TRACE(packet_trace()._packets, "Packet on %s from interface %s vif %s %s/%u %u bytes\n", sockid.c_str(), ifname.c_str(), vifname.c_str(), src_addr.str().c_str(), src_port, XORP_UINT_CAST(pdata.size())); debug_msg("Packet on %s from interface %s vif %s %s/%u %u bytes\n", sockid.c_str(), ifname.c_str(), vifname.c_str(), src_addr.str().c_str(), src_port, XORP_UINT_CAST(pdata.size())); i = find_if(pl.begin(), pl.end(), is_port_for(&sockid, &ifname, &vifname, &src_addr, &_ifm)); if (i == this->ports().end()) { XLOG_TRACE(packet_trace()._packets, "Discarding packet %s/%u %u bytes\n", src_addr.str().c_str(), src_port, XORP_UINT_CAST(pdata.size())); debug_msg("Discarding packet %s/%u %u bytes\n", src_addr.str().c_str(), src_port, XORP_UINT_CAST(pdata.size())); return false; } p = *i; XLOG_ASSERT(find_if(++i, pl.end(), is_port_for(&sockid, &ifname, &vifname, &src_addr, &_ifm)) == pl.end()); p->port_io_receive(src_addr, src_port, &pdata[0], pdata.size()); return true; } template Port* XrlPortManager::find_port(const string& ifname, const string& vifname, const A& addr) { typename PortManagerBase::PortList::iterator pi; pi = find_if(this->ports().begin(), this->ports().end(), port_has_address(addr)); if (pi == this->ports().end()) { return 0; } Port* port = *pi; PortIOBase* port_io = port->io_handler(); if (port_io->ifname() != ifname || port_io->vifname() != vifname) { return 0; } return port; } template const Port* XrlPortManager::find_port(const string& ifname, const string& vifname, const A& addr) const { typename PortManagerBase::PortList::const_iterator pi; pi = find_if(this->ports().begin(), this->ports().end(), port_has_address(addr)); if (pi == this->ports().end()) { return 0; } const Port* port = *pi; const PortIOBase* port_io = port->io_handler(); if (port_io->ifname() != ifname || port_io->vifname() != vifname) { return 0; } return port; } template bool XrlPortManager::underlying_rip_address_up(const string& ifname, const string& vifname, const A& addr) const { return address_enabled(_ifm.iftree(), ifname, vifname, addr); } template bool XrlPortManager::underlying_rip_address_exists(const string& ifname, const string& vifname, const A& addr) const { return _ifm.iftree().find_addr(ifname, vifname, addr) != 0; } template void XrlPortManager::status_change(ServiceBase* service, ServiceStatus /* old_status */, ServiceStatus new_status) { debug_msg("XXX %p status -> %s\n", service, service_status_name(new_status)); try_start_next_io_handler(); if (new_status != SERVICE_SHUTDOWN) return; typename map*>::iterator i; i = _dead_ports.find(service); XLOG_ASSERT(i != _dead_ports.end()); // delete i->second; // _dead_ports.erase(i); } template XrlPortManager::~XrlPortManager() { _ifm.detach_hint_observer(this); while (_dead_ports.empty() == false) { Port* p = _dead_ports.begin()->second; PortIOBase* io = p->io_handler(); delete io; delete p; _dead_ports.erase(_dead_ports.begin()); } } template void XrlPortManager::try_start_next_io_handler() { typename PortManagerBase::PortList::const_iterator cpi; cpi = find_if(this->ports().begin(), this->ports().end(), port_has_io_in_state(SERVICE_STARTING)); if (cpi != this->ports().end()) { return; } typename PortManagerBase::PortList::iterator pi = this->ports().begin(); XrlPortIO* xio = 0; while (xio == 0) { pi = find_if(pi, this->ports().end(), port_has_io_in_state(SERVICE_READY)); if (pi == this->ports().end()) { return; } Port* p = (*pi); xio = dynamic_cast*>(p->io_handler()); pi++; } xio->startup(); } #ifdef INSTANTIATE_IPV4 template class XrlPortManager; #endif #ifdef INSTANTIATE_IPV6 template class XrlPortManager; #endif xorp/rip/auth.hh0000664000076400007640000004402511540224234013736 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/rip/auth.hh,v 1.26 2008/10/02 21:58:16 bms Exp $ #ifndef __RIP_AUTH_HH__ #define __RIP_AUTH_HH__ #include "packets.hh" class EventLoop; /** * @short Base clase for RIPv2 authentication mechanisms. * * The AuthHandlerBase class defines the interfaces for RIPv2 * authentication handlers. Handlers are responsible for * authenticating inbound datagrams and adding authentication data to * outbound datagrams. * * Error during authentication set an error buffer that clients may * query using the error() method. */ class AuthHandlerBase { public: /** * Virtual destructor. */ virtual ~AuthHandlerBase(); /** * Get the effective name of the authentication scheme. */ virtual const char* effective_name() const = 0; /** * Reset the authentication state. */ virtual void reset() = 0; /** * Get number of routing entries used by authentication scheme at the * head of the RIP packet. * * @return the number of routing entries used by the authentication scheme * at the head of the RIP packet: 0 for unauthenticated packets, 1 * otherwise. */ virtual uint32_t head_entries() const = 0; /** * Get maximum number of non-authentication scheme use routing entries * in a RIP packet. */ virtual uint32_t max_routing_entries() const = 0; /** * Inbound authentication method. * * @param packet pointer to first byte of RIP packet. * @param packet_bytes number of bytes in RIP packet. * @param entries_ptr output variable set to point to first * entry in packet. Set to NULL if there are no entries, or * on authentication failure. * @param n_entries number of entries in the packet. * @param src_addr the source address of the packet. * @param new_peer true if this is a new peer. * * @return true if packet passes authentication checks, false otherwise. */ virtual bool authenticate_inbound(const uint8_t* packet, size_t packet_bytes, const uint8_t*& entries_ptr, uint32_t& n_entries, const IPv4& src_addr, bool new_peer) = 0; /** * Outbound authentication method. * * Create a list of authenticated packets (one for each valid * authentication key). Note that the original packet is also modified * and authenticated with the first valid key. * * @param packet the RIP packet to authenticate. * @param auth_packets a return-by-reference list with the * authenticated RIP packets (one for each valid authentication key). * @param n_routes the return-by-reference number of routes in the packet. * @return true if packet was successfully authenticated, false when * no valid keys are present. */ virtual bool authenticate_outbound(RipPacket& packet, list *>& auth_packets, size_t& n_routes) = 0; /** * Get textual description of last error. */ const string& error() const; protected: /** * Reset textual description of last error. */ void reset_error(); /** * Set textual description of latest error. */ void set_error(const string& err); private: string _err; }; /** * @short RIPv2 Authentication handler when no authentication scheme is * employed. */ class NullAuthHandler : public AuthHandlerBase { public: /** * Get the effective name of the authentication scheme. */ const char* effective_name() const; /** * Get the method-specific name of the authentication scheme. * * @return the method-specific name of the authentication scheme. */ static const char* auth_type_name(); /** * Reset the authentication state. */ void reset(); /** * Get number of routing entries used by authentication scheme at the * head of the RIP packet. * * @return the number of routing entries used by the authentication scheme * at the head of the RIP packet: 0 for unauthenticated packets, 1 * otherwise. */ uint32_t head_entries() const; /** * Get maximum number of non-authentication scheme use routing entries * in a RIP packet. */ uint32_t max_routing_entries() const; /** * Inbound authentication method. * * @param packet pointer to first byte of RIP packet. * @param packet_bytes number of bytes in RIP packet. * @param entries_ptr output variable set to point to first * entry in packet. Set to NULL if there are no entries, or * on authentication failure. * @param n_entries number of entries in the packet. * @param src_addr the source address of the packet. * @param new_peer true if this is a new peer. * * @return true if packet passes authentication checks, false otherwise. */ bool authenticate_inbound(const uint8_t* packet, size_t packet_bytes, const uint8_t*& entries_ptr, uint32_t& n_entries, const IPv4& src_addr, bool new_peer); /** * Outbound authentication method. * * Create a list of authenticated packets (one for each valid * authentication key). Note that the original packet is also modified * and authenticated with the first valid key. * * @param packet the RIP packet to authenticate. * @param auth_packets a return-by-reference list with the * authenticated RIP packets (one for each valid authentication key). * @param n_routes the return-by-reference number of routes in the packet. * @return true if packet was successfully authenticated, false when * no valid keys are present. */ bool authenticate_outbound(RipPacket& packet, list *>& auth_packets, size_t& n_routes); }; /** * @short RIPv2 Authentication handler for plaintext scheme. */ class PlaintextAuthHandler : public AuthHandlerBase { public: /** * Get the effective name of the authentication scheme. */ const char* effective_name() const; /** * Get the method-specific name of the authentication scheme. * * @return the method-specific name of the authentication scheme. */ static const char* auth_type_name(); /** * Reset the authentication state. */ void reset(); /** * Get number of routing entries used by authentication scheme at the * head of the RIP packet. * * @return the number of routing entries used by the authentication scheme * at the head of the RIP packet: 0 for unauthenticated packets, 1 * otherwise. */ uint32_t head_entries() const; /** * Get maximum number of non-authentication scheme use routing entries * in a RIP packet. */ uint32_t max_routing_entries() const; /** * Inbound authentication method. * * @param packet pointer to first byte of RIP packet. * @param packet_bytes number of bytes in RIP packet. * @param entries_ptr output variable set to point to first * entry in packet. Set to NULL if there are no entries, or * on authentication failure. * @param n_entries number of entries in the packet. * @param src_addr the source address of the packet. * @param new_peer true if this is a new peer. * * @return true if packet passes authentication checks, false otherwise. */ bool authenticate_inbound(const uint8_t* packet, size_t packet_bytes, const uint8_t*& entries_ptr, uint32_t& n_entries, const IPv4& src_addr, bool new_peer); /** * Outbound authentication method. * * Create a list of authenticated packets (one for each valid * authentication key). Note that the original packet is also modified * and authenticated with the first valid key. * * @param packet the RIP packet to authenticate. * @param auth_packets a return-by-reference list with the * authenticated RIP packets (one for each valid authentication key). * @param n_routes the return-by-reference number of routes in the packet. * @return true if packet was successfully authenticated, false when * no valid keys are present. */ bool authenticate_outbound(RipPacket& packet, list *>& auth_packets, size_t& n_routes); /** * Get the authentication key. * * @return the authentication key. */ const string& key() const; /** * Set the authentication key. * * @param plaintext_key the plain-text key. */ void set_key(const string& plaintext_key); protected: string _key; }; /** * @short RIPv2 Authentication handler for MD5 scheme. * * Class to check inbound MD5 authenticated packets and add * authentication data to outbound RIP packets. The RIP MD5 * authentication scheme is described in RFC 2082. */ class MD5AuthHandler : public AuthHandlerBase { public: /** * Class to hold MD5 key information. */ class MD5Key { public: /** * Construct an MD5 Key. */ MD5Key(uint8_t key_id, const string& key, const TimeVal& start_timeval, const TimeVal& end_timeval, XorpTimer start_timer, XorpTimer end_timer); /** * Get the ID associated with the key. */ uint8_t id() const { return _id; } /** * Get pointer to key data. The data is of size KEY_BYTES. */ const char* key_data() const { return _key_data; } /** * Get the size of the key data in bytes. */ uint32_t key_data_bytes() const { return KEY_BYTES; } /** * Get key data as a string. */ string key() const; /** * Get the start time of the key. */ const TimeVal& start_timeval() const { return _start_timeval; } /** * Get the end time of the key. */ const TimeVal& end_timeval() const { return _end_timeval; } /** * Get indication of whether key is persistent. */ bool is_persistent() const { return _is_persistent; } /** * Set the flag whether the key is persistent. * * @param v if true the key is persistent. */ void set_persistent(bool v) { _is_persistent = v; } /** * Get whether ID matches a particular value (convenient for STL * algorithms). */ bool id_matches(uint8_t o) const { return _id == o; } /** * Get key validity status of key at a particular time. * * @param when the time to test whether the key is valid. */ bool valid_at(const TimeVal& when) const; /** * Reset the key for all sources. */ void reset(); /** * Reset the key for a particular source. * * @param src_addr the source address. */ void reset(const IPv4& src_addr); /** * Indicate whether valid packets have been received from a source * with this key ID. * * @param src_addr the source address. * @return true if a packet has been received from the source, * otherwise false. */ bool packets_received(const IPv4& src_addr) const; /** * Get last received sequence number from a source. * * @param src_addr the source address. * @return last sequence number seen from the source. Value may be * garbage if no packets have been received (check first with * @ref packets_received()). */ uint32_t last_seqno_recv(const IPv4& src_addr) const; /** * Set last sequence number received from a source. This method * implicitly set packets received to true. * * @param src_addr the source address. * @param seqno the last sequence number received from the source. */ void set_last_seqno_recv(const IPv4& src_addr, uint32_t seqno); /** * Get next sequence number for outbound packets. The counter * is automatically updated with each call of this method. */ uint32_t next_seqno_out() { return _o_seqno++; } protected: static const uint32_t KEY_BYTES = 16; uint8_t _id; // Key ID char _key_data[KEY_BYTES]; // Key data TimeVal _start_timeval; // Start time of the key TimeVal _end_timeval; // End time of the key bool _is_persistent; // True if key is persistent map _pkts_recv; // True if packets received map _lr_seqno; // Last received seqno uint32_t _o_seqno; // Next outbound sequence number XorpTimer _start_timer; // Key start timer XorpTimer _stop_timer; // Key stop timer friend class MD5AuthHandler; }; typedef list KeyChain; public: /** * Constructor * * @param eventloop the EventLoop instance to used for time reference. */ MD5AuthHandler(EventLoop& eventloop); /** * Get the effective name of the authentication scheme. */ const char* effective_name() const; /** * Get the method-specific name of the authentication scheme. * * @return the method-specific name of the authentication scheme. */ static const char* auth_type_name(); /** * Reset the authentication state. */ void reset(); /** * Get number of routing entries used by authentication scheme at the * head of the RIP packet. * * @return the number of routing entries used by the authentication scheme * at the head of the RIP packet: 0 for unauthenticated packets, 1 * otherwise. */ uint32_t head_entries() const; /** * Get maximum number of non-authentication scheme use routing entries * in a RIP packet. */ uint32_t max_routing_entries() const; /** * Inbound authentication method. * * @param packet pointer to first byte of RIP packet. * @param packet_bytes number of bytes in RIP packet. * @param entries_ptr output variable set to point to first * entry in packet. Set to NULL if there are no entries, or * on authentication failure. * @param n_entries number of entries in the packet. * @param src_addr the source address of the packet. * @param new_peer true if this is a new peer. * * @return true if packet passes authentication checks, false otherwise. */ bool authenticate_inbound(const uint8_t* packet, size_t packet_bytes, const uint8_t*& entries_ptr, uint32_t& n_entries, const IPv4& src_addr, bool new_peer); /** * Outbound authentication method. * * Create a list of authenticated packets (one for each valid * authentication key). Note that the original packet is also modified * and authenticated with the first valid key. * * @param packet the RIP packet to authenticate. * @param auth_packets a return-by-reference list with the * authenticated RIP packets (one for each valid authentication key). * @param n_routes the return-by-reference number of routes in the packet. * @return true if packet was successfully authenticated, false when * no valid keys are present. */ bool authenticate_outbound(RipPacket& packet, list *>& auth_packets, size_t& n_routes); /** * Add a key to the MD5 key chain. * * If the key already exists, it is updated with the new settings. * * @param key_id unique ID associated with key. * @param key phrase used for MD5 digest computation. * @param start_timeval start time when key becomes valid. * @param end_timeval end time when key becomes invalid. * @param error_msg the error message (if error). * @return true on success, false if end time is less than start time * or key has already expired. */ bool add_key(uint8_t key_id, const string& key, const TimeVal& start_timeval, const TimeVal& end_timeval, string& error_msg); /** * Remove a key from the MD5 key chain. * * @param key_id unique ID of key to be removed. * @param error_msg the error message (if error). * @return true if the key was found and removed, otherwise false. */ bool remove_key(uint8_t key_id, string& error_msg); /** * A callback that a key from the MD5 key chain has become valid. * * @param key_id unique ID of the key that has become valid. */ void key_start_cb(uint8_t key_id); /** * A callback that a key from the MD5 key chain has expired and is invalid. * * @param key_id unique ID of the key that has expired. */ void key_stop_cb(uint8_t key_id); /** * Reset the keys for all sources. */ void reset_keys(); /** * Get all valid keys managed by the MD5AuthHandler. * * @return list of all valid keys. */ const KeyChain& valid_key_chain() const { return _valid_key_chain; } /** * Get all invalid keys managed by the MD5AuthHandler. * * @return list of all invalid keys. */ const KeyChain& invalid_key_chain() const { return _invalid_key_chain; } /** * Test where the MD5AuthHandler contains any keys. * * @return if the MD5AuthHandler contains any keys, otherwise false. */ bool empty() const; protected: EventLoop& _eventloop; // The event loop KeyChain _valid_key_chain; // The set of all valid keys KeyChain _invalid_key_chain; // The set of all invalid keys NullAuthHandler _null_handler; // Null handler if no valid keys }; #endif // __RIP_AUTH_HH__ xorp/rip/redist.hh0000664000076400007640000001065011540225534014270 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/rip/redist.hh,v 1.14 2008/10/02 21:58:17 bms Exp $ #ifndef __RIP_ROUTE_REDIST_HH__ #define __RIP_ROUTE_REDIST_HH__ #include "route_entry.hh" template class RouteDB; template class RouteWalker; /** * @short Route Origination class for @ref RouteRedistributor instances. * * Route Origination class for @ref RouteRedistributor instances. * Overrides time constant accessors for @ref RouteEntryOrigin. By * default the time constants for route expiry and deletion is never. * If set_allow_timers() is called future updates will have timers * associated with them. set_allow_timers() is called when the * RedistTable is going to be withdrawn. This allows routes to be * advertised as unreachable via host before they are deleted. * * Non-copyable due to inheritance from RouteEntryOrigin. */ template class RedistRouteOrigin : public RouteEntryOrigin { public: RedistRouteOrigin() : RouteEntryOrigin(true) {} /** * Retrieve number of seconds before routes associated with this * RedistRouteOrigin expire. * * Always returns 0 to signify routes do not automatically expire. */ uint32_t expiry_secs() const; /** * Retrieve number of seconds before route should be deleted after * expiry. */ uint32_t deletion_secs() const; }; /** * @short Store for redistributed routes. */ template class RouteRedistributor : public NONCOPYABLE { public: typedef A Addr; typedef IPNet Net; public: /** * Constructor for RouteRedistributor * * @param route_db the route database to add and expire routes in. */ RouteRedistributor(RouteDB& route_db); ~RouteRedistributor(); /** * Add a route to be redistributed with specific cost and tag values. * * @param net network described by route. * @param nexthop router capable of forwarding route. * @param ifname the corresponding interface name toward the destination. * @param vifname the corresponding vif name toward the destination. * @param policytags policy-tags associated with route. * * @return true on success, false if route could not be added to * the RouteDatabase. Failure may occur if route already exists * or a lower cost route exists. */ bool add_route(const Net& net, const Addr& nexthop, const string& ifname, const string& vifname, uint16_t cost, uint16_t tag, const PolicyTags& policytags); /** * Trigger route expiry. * * @param network described by route. * * @return true on success, false if route did not come from this * RouteRedistributor instance. */ bool expire_route(const Net& net); /** * Accessor. * * @return number of routes */ uint32_t route_count() const; /** * Withdraw routes. Triggers a walking of associated routes and * their expiration from the RIP route database. */ void withdraw_routes(); /** * @return true if actively withdrawing routes, false otherwise. */ bool withdrawing_routes() const; private: /** * Periodic timer callback for withdrawing a batch of routes. The timer * is triggered by @ref withdraw_routes(). */ bool withdraw_batch(); protected: RouteDB& _route_db; RedistRouteOrigin* _rt_origin; RouteWalker* _wdrawer; // Route Withdrawl Walker XorpTimer _wtimer; // Clock for Withdrawls }; #endif // __RIP_REDIST_HH__ xorp/rip/output_table.hh0000664000076400007640000000375711421137511015512 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/rip/output_table.hh,v 1.10 2008/10/02 21:58:16 bms Exp $ #ifndef __RIP_OUTPUT_TABLE_HH__ #define __RIP_OUTPUT_TABLE_HH__ #include "rip_module.h" #include "libxorp/xlog.h" #include "output.hh" #include "route_db.hh" /** * @short Route Table Output class. * * The OutputTable class produces an asynchronous RIP table dump. It's * intended use is for solicited and unsolicited routing table. * * Specialized implementations exist for IPv4 and IPv6. * Non-copyable due to inheritance from OutputBase. */ template class OutputTable : public OutputBase { public: OutputTable(EventLoop& e, Port& port, PacketQueue& pkt_queue, RouteDB& rdb, const A& ip_addr = RIP_AF_CONSTANTS::IP_GROUP(), uint16_t ip_port = RIP_AF_CONSTANTS::IP_PORT) : OutputBase(e, port, pkt_queue, ip_addr, ip_port), _rw(rdb), _rw_valid(false) {} protected: void output_packet(); void start_output_processing(); void stop_output_processing(); private: RouteWalker _rw; // RouteWalker bool _rw_valid; // RouteWalker is valid (no reset req). }; #endif // __RIP_OUTPUT_TABLE_HH__ xorp/rip/trace.hh0000664000076400007640000000263611421137511014074 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/rip/trace.hh,v 1.1 2008/10/29 21:59:39 andrewma Exp $ #ifndef __RIP_TRACE_HH__ #define __RIP_TRACE_HH__ /** * All the trace variables in one place. */ struct Trace { Trace() : _routes(false), _packets(false) // Don't forget to add new variables to the all() method. {} /* * Set all flags */ void all(bool val) { _routes = _packets = val; } bool _routes; // add,replace,delete route. bool _packets; // Incoming and outgoing packets. }; #endif // __RIP_TRACE_HH__ xorp/rip/packet_assembly.hh0000664000076400007640000002650511421137511016145 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/rip/packet_assembly.hh,v 1.16 2008/10/02 21:58:16 bms Exp $ #ifndef __RIP_PACKET_ASSEMBLY_HH__ #define __RIP_PACKET_ASSEMBLY_HH__ #include "rip_module.h" #include "libxorp/xlog.h" #include "auth.hh" #include "packets.hh" #include "port.hh" /** * @short Internal specialized state for PacketAssembler classes. * * Completely specialized implementations exist for IPv4 and IPv6 template * arguments. */ template class PacketAssemblerSpecState {}; /** * @short IPv4 specialized PacketAssembler state. * * This class just holder the authentication handler that IPv4 packet * assembly requires. */ template <> class PacketAssemblerSpecState { private: AuthHandlerBase& _ah; public: /** * IPv4 Specific Constructor. */ PacketAssemblerSpecState(Port& port) : _ah(*(port.af_state().auth_handler())) {} /** * IPv4 Specific Constructor. */ PacketAssemblerSpecState(AuthHandlerBase& auth_handler) : _ah(auth_handler) {} /** * IPv4 Specific authentication handler accessor. */ AuthHandlerBase& ah() { return _ah; } /** * IPv4 Specific authentication handler accessor. */ const AuthHandlerBase& ah() const { return _ah; } }; /** * @short IPv6 specialized PacketAssembler state. * * This provides a means to query the RIP port and query the * configured maximum entries per packet. XXX At present it's a * placeholder and returns a fixed value. * * It also stores the last used nexthop value since nexthops are only * packed when they change. */ template <> class PacketAssemblerSpecState { private: uint32_t _max_entries; IPv6 _lnh; public: PacketAssemblerSpecState(Port& ) : _max_entries(25), _lnh(IPv6::ALL_ONES()) {} PacketAssemblerSpecState() : _max_entries(25), _lnh(IPv6::ALL_ONES()) {} uint32_t max_entries() const; void reset_last_nexthop(); void set_last_nexthop(const IPv6& ip6); const IPv6& last_nexthop() const; }; inline uint32_t PacketAssemblerSpecState::max_entries() const { return _max_entries; } inline void PacketAssemblerSpecState::reset_last_nexthop() { _lnh = IPv6::ALL_ONES(); } inline void PacketAssemblerSpecState::set_last_nexthop(const IPv6& ip6) { _lnh = ip6; } inline const IPv6& PacketAssemblerSpecState::last_nexthop() const { return _lnh; } /** * @short Class for RIP Response Packet Assemblers. * * Both RIPv2 and RIPng have some oddities in packing and this interface * provides a consistent interface for that packing. * * This class has specialized IPv4 and IPv6 implementations. */ template class ResponsePacketAssembler { public: typedef A Addr; typedef IPNet Net; typedef PacketAssemblerSpecState SpState; public: /** * Constructor. * * @param port Port to take configuration information from. */ ResponsePacketAssembler(Port& port); /** * Constructor. * * @param sp Specialized state. */ ResponsePacketAssembler(SpState& sp); /** * Destructor. */ ~ResponsePacketAssembler(); /** * Start assembling RIP response packet. */ void packet_start(RipPacket* pkt); /** * Add a route to RIP response packet. * * @return true if route was added, false if packet is full and would * have indicated this if only @ref packet_full was called. */ bool packet_add_route(const Net& net, const Addr& nexthop, uint16_t cost, uint16_t tag); /** * Ready-to-go accessor. * * @return true if packet has no more space for route entries. */ bool packet_full() const; /** * Finish packet. Some packet types require final stage processing * and this method gives that processing a chance to happen. Common * usage is RIPv2 authentication. * * @param auth_packets a return-by-reference list with the * authenticated RIP packets (one copy for each valid authentication key). * @return true on success, false if a failure is detected. */ bool packet_finish(list* >& auth_packets); private: /** * Copy Constructor (Disabled). */ ResponsePacketAssembler(const ResponsePacketAssembler&); /** * Assignment Operator (Disabled). */ ResponsePacketAssembler& operator=(const ResponsePacketAssembler&); protected: RipPacket* _pkt; uint32_t _pos; SpState _sp_state; }; /** * @short Class to configure a RIP packet to be a table request. * * This class has specialized IPv4 and IPv6 implementations to cater for * address family differences. */ template class RequestTablePacketAssembler { public: typedef A Addr; typedef IPNet Net; typedef PacketAssemblerSpecState SpState; public: RequestTablePacketAssembler(Port& port) : _sp_state(port) {} /** * Take RipPacket packet and make it into a table request packet. * * @param auth_packets a return-by-reference list with the * authenticated RIP packets (one copy for each valid authentication key). * @return true on success, false if an error is encountered. Should * an error be encountered the reason is written to the xlog facility. */ bool prepare(RipPacket* pkt, list* >& auth_packets); protected: SpState _sp_state; }; // ---------------------------------------------------------------------------- // ResponsePacketAssembler implementation template <> inline ResponsePacketAssembler::ResponsePacketAssembler(Port& port) : _pkt(0), _pos(0), _sp_state(port) { } template <> inline ResponsePacketAssembler::ResponsePacketAssembler(SpState& sp) : _pkt(0), _pos(0), _sp_state(sp) { } template <> inline ResponsePacketAssembler::~ResponsePacketAssembler() { } template <> inline void ResponsePacketAssembler::packet_start(RipPacket* pkt) { _pkt = pkt; const AuthHandlerBase& ah = _sp_state.ah(); _pos = ah.head_entries(); _pkt->set_max_entries(ah.head_entries() + ah.max_routing_entries()); RipPacketHeaderWriter rph(_pkt->header_ptr()); rph.initialize(RipPacketHeader::RESPONSE, RipPacketHeader::IPv4_VERSION); } template <> inline bool ResponsePacketAssembler::packet_full() const { const AuthHandlerBase& ah = _sp_state.ah(); return _pos == ah.max_routing_entries(); } template <> inline bool ResponsePacketAssembler::packet_add_route(const Net& net, const Addr& nexthop, uint16_t cost, uint16_t tag) { if (packet_full()) { return false; } uint8_t* pre_ptr = _pkt->route_entry_ptr(_pos); PacketRouteEntryWriter pre(pre_ptr); pre.initialize(tag, net, nexthop, cost); _pos++; return true; } template <> inline bool ResponsePacketAssembler::packet_finish( list* >& auth_packets) { AuthHandlerBase& ah = _sp_state.ah(); _pkt->set_max_entries(_pos); size_t n_routes = 0; if ((ah.authenticate_outbound(*_pkt, auth_packets, n_routes) != true) || (n_routes == 0)) { XLOG_ERROR("Outbound authentication error: %s\n", ah.error().c_str()); return false; } return true; } // ---------------------------------------------------------------------------- // ResponsePacketAssembler implementation template <> inline ResponsePacketAssembler::ResponsePacketAssembler(Port& port) : _pkt(0), _pos(0), _sp_state(port) { } template <> inline ResponsePacketAssembler::ResponsePacketAssembler(SpState& sp) : _pkt(0), _pos(0), _sp_state(sp) { } template <> inline ResponsePacketAssembler::~ResponsePacketAssembler() { } template <> inline void ResponsePacketAssembler::packet_start(RipPacket* pkt) { _pkt = pkt; _pos = 0; _sp_state.reset_last_nexthop(); RipPacketHeaderWriter rph(_pkt->header_ptr()); rph.initialize(RipPacketHeader::RESPONSE, RipPacketHeader::IPv6_VERSION); } template <> inline bool ResponsePacketAssembler::packet_full() const { return (_sp_state.max_entries() - _pos) <= 2; } template <> inline bool ResponsePacketAssembler::packet_add_route(const Net& net, const Addr& nexthop, uint16_t cost, uint16_t tag) { uint8_t* pre_ptr; if (packet_full()) { return false; } if (nexthop != _sp_state.last_nexthop()) { pre_ptr = _pkt->route_entry_ptr(_pos); PacketRouteEntryWriter pre(pre_ptr); pre.initialize_nexthop(nexthop); _pos++; _sp_state.set_last_nexthop(nexthop); } pre_ptr = _pkt->route_entry_ptr(_pos); PacketRouteEntryWriter pre(pre_ptr); pre.initialize_route(tag, net, cost); _pos++; return true; } template <> inline bool ResponsePacketAssembler::packet_finish( list* >& auth_packets) { _pkt->set_max_entries(_pos); RipPacket* packet = new RipPacket(*_pkt); auth_packets.push_back(packet); return true; } // ---------------------------------------------------------------------------- // RequestTablePacketAssembler implementation template<> inline bool RequestTablePacketAssembler::prepare(RipPacket* pkt, list* >& auth_packets) { RipPacketHeaderWriter rph(pkt->header_ptr()); rph.initialize(RipPacketHeader::REQUEST, RipPacketHeader::IPv4_VERSION); AuthHandlerBase& ah = _sp_state.ah(); pkt->set_max_entries(1 + ah.head_entries()); uint8_t* pre_ptr = pkt->route_entry_ptr(ah.head_entries()); PacketRouteEntryWriter pre(pre_ptr); pre.initialize_table_request(); size_t n_routes = 0; if ((ah.authenticate_outbound(*pkt, auth_packets, n_routes) != true) || (n_routes == 0)) { XLOG_ERROR("Outbound authentication error: %s\n", ah.error().c_str()); return false; } return true; } // ---------------------------------------------------------------------------- // RequestTablePacketAssembler implementation template<> inline bool RequestTablePacketAssembler::prepare(RipPacket* pkt, list* >& auth_packets) { RipPacketHeaderWriter rph(pkt->header_ptr()); rph.initialize(RipPacketHeader::REQUEST, RipPacketHeader::IPv6_VERSION); pkt->set_max_entries(1); uint8_t* pre_ptr = pkt->route_entry_ptr(0); PacketRouteEntryWriter pre(pre_ptr); pre.initialize_table_request(); RipPacket* packet = new RipPacket(*pkt); auth_packets.push_back(packet); return true; } #endif // __RIP_PACKET_ASSEMBLY_HH__ xorp/rip/auth.cc0000664000076400007640000005015511540224234013725 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "rip_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/eventloop.hh" #include #include "constants.hh" #include "auth.hh" // ---------------------------------------------------------------------------- // AuthHandlerBase implementation AuthHandlerBase::~AuthHandlerBase() { } const string& AuthHandlerBase::error() const { return _err; } inline void AuthHandlerBase::reset_error() { if (_err.empty() == false) _err.erase(); } inline void AuthHandlerBase::set_error(const string& err) { _err = err; } // ---------------------------------------------------------------------------- // NullAuthHandler implementation const char* NullAuthHandler::effective_name() const { return auth_type_name(); } const char* NullAuthHandler::auth_type_name() { return "none"; } void NullAuthHandler::reset() { } uint32_t NullAuthHandler::head_entries() const { return 0; } uint32_t NullAuthHandler::max_routing_entries() const { return RIPv2_ROUTES_PER_PACKET; } bool NullAuthHandler::authenticate_inbound(const uint8_t* packet, size_t packet_bytes, const uint8_t*& entries_ptr, uint32_t& n_entries, const IPv4&, bool) { entries_ptr = NULL; n_entries = 0; if (packet_bytes > RIPv2_MAX_PACKET_BYTES) { set_error(c_format("packet too large (%u bytes)", XORP_UINT_CAST(packet_bytes))); return false; } else if (packet_bytes < RIPv2_MIN_PACKET_BYTES) { set_error(c_format("packet too small (%u bytes)", XORP_UINT_CAST(packet_bytes))); return false; } size_t entry_bytes = packet_bytes - RipPacketHeader::size(); if (entry_bytes % PacketRouteEntry::size()) { set_error(c_format("non-integral route entries (%u bytes)", XORP_UINT_CAST(entry_bytes))); return false; } n_entries = entry_bytes / PacketRouteEntry::size(); if (n_entries == 0) { return true; } entries_ptr = packet + RipPacketHeader::size(); const PacketRouteEntry entry(entries_ptr); // Reject packet if first entry is authentication data if (entry.is_auth_entry()) { set_error(c_format("unexpected authentication data (type %d)", entry.tag())); entries_ptr = NULL; n_entries = 0; return false; } reset_error(); return true; } bool NullAuthHandler::authenticate_outbound(RipPacket& packet, list *>& auth_packets, size_t& n_routes) { // XXX: nothing to do so just create a single copy RipPacket* copy_packet = new RipPacket(packet); auth_packets.push_back(copy_packet); reset_error(); n_routes = packet.data_bytes() - RipPacketHeader::size(); n_routes /= PacketRouteEntry::size(); return (true); } // ---------------------------------------------------------------------------- // Plaintext handler implementation const char* PlaintextAuthHandler::effective_name() const { return auth_type_name(); } const char* PlaintextAuthHandler::auth_type_name() { return "simple"; } void PlaintextAuthHandler::reset() { } uint32_t PlaintextAuthHandler::head_entries() const { return 1; } uint32_t PlaintextAuthHandler::max_routing_entries() const { return RIPv2_ROUTES_PER_PACKET - 1; } bool PlaintextAuthHandler::authenticate_inbound(const uint8_t* packet, size_t packet_bytes, const uint8_t*& entries_ptr, uint32_t& n_entries, const IPv4&, bool) { entries_ptr = NULL; n_entries = 0; if (packet_bytes > RIPv2_MAX_PACKET_BYTES) { set_error(c_format("packet too large (%u bytes)", XORP_UINT_CAST(packet_bytes))); return false; } if (packet_bytes < RIPv2_MIN_AUTH_PACKET_BYTES) { set_error(c_format("packet too small (%u bytes)", XORP_UINT_CAST(packet_bytes))); return false; } size_t entry_bytes = packet_bytes - RipPacketHeader::size(); if (entry_bytes % PacketRouteEntry::size()) { set_error(c_format("non-integral route entries (%u bytes)", XORP_UINT_CAST(entry_bytes))); return false; } const PlaintextPacketRouteEntry4 ppr(packet + RipPacketHeader::size()); if (ppr.addr_family() != PlaintextPacketRouteEntry4::ADDR_FAMILY) { set_error("not an authenticated packet"); return false; } else if (ppr.auth_type() != PlaintextPacketRouteEntry4::AUTH_TYPE) { set_error("not a plaintext authenticated packet"); return false; } string passwd = ppr.password(); if (passwd != key()) { set_error(c_format("wrong password \"%s\"", passwd.c_str())); return false; } reset_error(); n_entries = entry_bytes / PacketRouteEntry::size() - 1; if (n_entries) { entries_ptr = (packet + RipPacketHeader::size() + PlaintextPacketRouteEntry4::size()); } return true; } bool PlaintextAuthHandler::authenticate_outbound(RipPacket& packet, list *>& auth_packets, size_t& n_routes) { uint8_t* first_entry_ptr = NULL; if (head_entries() > 0) first_entry_ptr = packet.route_entry_ptr(0); x_static_assert(PacketRouteEntry::SIZE == 20); x_static_assert(PlaintextPacketRouteEntry4::SIZE == 20); XLOG_ASSERT(packet.data_ptr() + RipPacketHeader::size() == first_entry_ptr); PlaintextPacketRouteEntry4Writer ppr(first_entry_ptr); ppr.initialize(key()); // XXX: just create a single copy RipPacket* copy_packet = new RipPacket(packet); auth_packets.push_back(copy_packet); reset_error(); n_routes = packet.data_bytes() - RipPacketHeader::size(); n_routes /= PacketRouteEntry::size(); n_routes--; // XXX: exclude the first (authentication) entry return (true); } const string& PlaintextAuthHandler::key() const { return _key; } void PlaintextAuthHandler::set_key(const string& plaintext_key) { _key = string(plaintext_key, 0, 16); } // ---------------------------------------------------------------------------- // MD5AuthHandler::MD5Key implementation MD5AuthHandler::MD5Key::MD5Key(uint8_t key_id, const string& key, const TimeVal& start_timeval, const TimeVal& end_timeval, XorpTimer start_timer, XorpTimer stop_timer) : _id(key_id), _start_timeval(start_timeval), _end_timeval(end_timeval), _is_persistent(false), _o_seqno(0), _start_timer(start_timer), _stop_timer(stop_timer) { string::size_type n = key.copy(_key_data, 16); if (n < KEY_BYTES) { memset(_key_data + n, 0, KEY_BYTES - n); } } string MD5AuthHandler::MD5Key::key() const { return string(_key_data, 0, 16); } bool MD5AuthHandler::MD5Key::valid_at(const TimeVal& when) const { if (is_persistent()) return true; return ((_start_timeval <= when) && (when <= _end_timeval)); } void MD5AuthHandler::MD5Key::reset() { // // Reset the seqno // _lr_seqno.clear(); // // Reset the flag that a packet has been received // _pkts_recv.clear(); } void MD5AuthHandler::MD5Key::reset(const IPv4& src_addr) { map::iterator seqno_iter; map::iterator recv_iter; // // Reset the seqno // seqno_iter = _lr_seqno.find(src_addr); if (seqno_iter != _lr_seqno.end()) _lr_seqno.erase(seqno_iter); // // Reset the flag that a packet has been received // recv_iter = _pkts_recv.find(src_addr); if (recv_iter != _pkts_recv.end()) _pkts_recv.erase(recv_iter); } bool MD5AuthHandler::MD5Key::packets_received(const IPv4& src_addr) const { map::const_iterator iter; iter = _pkts_recv.find(src_addr); if (iter == _pkts_recv.end()) return (false); return (iter->second); } uint32_t MD5AuthHandler::MD5Key::last_seqno_recv(const IPv4& src_addr) const { map::const_iterator iter; iter = _lr_seqno.find(src_addr); if (iter == _lr_seqno.end()) return (0); return (iter->second); } void MD5AuthHandler::MD5Key::set_last_seqno_recv(const IPv4& src_addr, uint32_t seqno) { map::iterator seqno_iter; map::iterator recv_iter; // // Set the seqno // seqno_iter = _lr_seqno.find(src_addr); if (seqno_iter == _lr_seqno.end()) _lr_seqno.insert(make_pair(src_addr, seqno)); else seqno_iter->second = seqno; // // Set the flag that a packet has been received // recv_iter = _pkts_recv.find(src_addr); if (recv_iter == _pkts_recv.end()) _pkts_recv.insert(make_pair(src_addr, true)); else recv_iter->second = true; } // ---------------------------------------------------------------------------- // MD5AuthHandler implementation MD5AuthHandler::MD5AuthHandler(EventLoop& eventloop) : _eventloop(eventloop) { } const char* MD5AuthHandler::effective_name() const { // // XXX: if no valid keys, then don't use any authentication // if (_valid_key_chain.empty()) { return (_null_handler.effective_name()); } return auth_type_name(); } const char* MD5AuthHandler::auth_type_name() { return "md5"; } void MD5AuthHandler::reset() { // // XXX: if no valid keys, then don't use any authentication // if (_valid_key_chain.empty()) { _null_handler.reset(); return; } reset_keys(); } uint32_t MD5AuthHandler::head_entries() const { // // XXX: if no valid keys, then don't use any authentication // if (_valid_key_chain.empty()) { return (_null_handler.head_entries()); } return 1; } uint32_t MD5AuthHandler::max_routing_entries() const { // // XXX: if no valid keys, then don't use any authentication // if (_valid_key_chain.empty()) { return (_null_handler.max_routing_entries()); } return RIPv2_ROUTES_PER_PACKET - 1; } bool MD5AuthHandler::authenticate_inbound(const uint8_t* packet, size_t packet_bytes, const uint8_t*& entries_ptr, uint32_t& n_entries, const IPv4& src_addr, bool new_peer) { x_static_assert(MD5PacketTrailer::SIZE == 20); // // XXX: if no valid keys, then don't use any authentication // if (_valid_key_chain.empty()) { if (_null_handler.authenticate_inbound(packet, packet_bytes, entries_ptr, n_entries, src_addr, new_peer) != true) { set_error(_null_handler.error()); return (false); } reset_error(); return (true); } entries_ptr = NULL; n_entries = 0; if (packet_bytes > RIPv2_MAX_PACKET_BYTES + MD5PacketTrailer::size()) { set_error(c_format("packet too large (%u bytes)", XORP_UINT_CAST(packet_bytes))); return false; } if (packet_bytes < RIPv2_MIN_AUTH_PACKET_BYTES) { set_error(c_format("packet too small (%u bytes)", XORP_UINT_CAST(packet_bytes))); return false; } const MD5PacketRouteEntry4 mpr(packet + RipPacketHeader::size()); if (mpr.addr_family() != MD5PacketRouteEntry4::ADDR_FAMILY) { set_error("not an authenticated packet"); return false; } if (mpr.auth_type() != MD5PacketRouteEntry4::AUTH_TYPE) { set_error("not an MD5 authenticated packet"); return false; } if (mpr.auth_bytes() != MD5PacketTrailer::size()) { set_error(c_format("wrong number of auth bytes (%d != %u)", mpr.auth_bytes(), XORP_UINT_CAST(MD5PacketTrailer::size()))); return false; } if (uint32_t(mpr.auth_off() + mpr.auth_bytes()) != packet_bytes) { set_error(c_format("Size of packet does not correspond with " "authentication data offset and size " "(%d + %d != %u).", mpr.auth_off(), mpr.auth_bytes(), XORP_UINT_CAST(packet_bytes))); return false; } KeyChain::iterator k = find_if(_valid_key_chain.begin(), _valid_key_chain.end(), bind2nd(mem_fun_ref(&MD5Key::id_matches), mpr.key_id())); if (k == _valid_key_chain.end()) { set_error(c_format("packet with key ID %d for which no key is " "configured", mpr.key_id())); return false; } MD5Key* key = &(*k); if (new_peer) key->reset(src_addr); uint32_t last_seqno_recv = key->last_seqno_recv(src_addr); if (key->packets_received(src_addr) && !(new_peer && mpr.seqno() == 0) && (mpr.seqno() - last_seqno_recv >= 0x7fffffff)) { set_error(c_format("bad sequence number 0x%08x < 0x%08x", XORP_UINT_CAST(mpr.seqno()), XORP_UINT_CAST(last_seqno_recv))); return false; } const MD5PacketTrailer mpt(packet + mpr.auth_off()); if (mpt.valid() == false) { set_error("invalid authentication trailer"); return false; } MD5_CTX ctx; uint8_t digest[16]; MD5_Init(&ctx); MD5_Update(&ctx, packet, mpr.auth_off() + mpt.auth_data_offset()); MD5_Update(&ctx, key->key_data(), key->key_data_bytes()); MD5_Final(digest, &ctx); if (memcmp(digest, mpt.auth_data(), mpt.auth_data_bytes()) != 0) { set_error(c_format("authentication digest doesn't match local key " "(key ID = %d)", key->id())); // #define DUMP_BAD_MD5 #ifdef DUMP_BAD_MD5 const char badmd5[] = "/tmp/rip_badmd5"; // If the file already exists don't dump anything. The file // should contain and only one packet. if (-1 == access(badmd5, R_OK)) { XLOG_INFO("Dumping bad MD5 to %s", badmd5); FILE *fp = fopen(badmd5, "w"); fwrite(packet, packet_bytes, 1 , fp); fclose(fp); } #endif return false; } // Update sequence number only after packet has passed digest check key->set_last_seqno_recv(src_addr, mpr.seqno()); reset_error(); n_entries = (mpr.auth_off() - RipPacketHeader::size()) / PacketRouteEntry::size() - 1; if (n_entries > 0) { entries_ptr = (packet + RipPacketHeader::size() + MD5PacketRouteEntry4::size()); } return true; } bool MD5AuthHandler::authenticate_outbound(RipPacket& packet, list *>& auth_packets, size_t& n_routes) { RipPacket first_packet(packet); vector first_trailer; KeyChain::iterator iter; x_static_assert(MD5PacketTrailer::SIZE == 20); // // XXX: if no valid keys, then don't use any authentication // if (_valid_key_chain.empty()) { if (_null_handler.authenticate_outbound(packet, auth_packets, n_routes) != true) { set_error(_null_handler.error()); return (false); } reset_error(); return (true); } // // Create an authenticated copy of the packet for each valid key // for (iter = _valid_key_chain.begin(); iter != _valid_key_chain.end(); ++iter) { MD5Key& key = *iter; RipPacket* copy_packet = new RipPacket(packet); auth_packets.push_back(copy_packet); uint8_t* first_entry_ptr = NULL; if (head_entries() > 0) first_entry_ptr = copy_packet->route_entry_ptr(0); MD5PacketRouteEntry4Writer mpr(first_entry_ptr); mpr.initialize(copy_packet->data_bytes(), key.id(), MD5PacketTrailer::size(), key.next_seqno_out()); vector trailer; trailer.resize(MD5PacketTrailer::size()); MD5PacketTrailerWriter mpt(&trailer[0]); mpt.initialize(); MD5_CTX ctx; MD5_Init(&ctx); MD5_Update(&ctx, copy_packet->data_ptr(), mpr.auth_off()); MD5_Update(&ctx, &trailer[0], mpt.auth_data_offset()); MD5_Update(&ctx, key.key_data(), key.key_data_bytes()); MD5_Final(mpt.auth_data(), &ctx); // // XXX: create a copy of the first packet without the trailer // and of the trailer itself. // if (iter == _valid_key_chain.begin()) { first_packet = *copy_packet; first_trailer = trailer; } copy_packet->append_data(trailer); } packet = first_packet; n_routes = packet.data_bytes() / MD5PacketRouteEntry4::size() - 1; packet.append_data(first_trailer); reset_error(); return (true); } bool MD5AuthHandler::add_key(uint8_t key_id, const string& key, const TimeVal& start_timeval, const TimeVal& end_timeval, string& error_msg) { TimeVal now; XorpTimer start_timer, end_timer; string dummy_error_msg; _eventloop.current_time(now); if (start_timeval > end_timeval) { error_msg = c_format("Start time is later than the end time"); return false; } if (end_timeval < now) { error_msg = c_format("End time is in the past"); return false; } if (start_timeval > now) { start_timer = _eventloop.new_oneoff_at( start_timeval, callback(this, &MD5AuthHandler::key_start_cb, key_id)); } if (end_timeval != TimeVal::MAXIMUM()) { end_timer = _eventloop.new_oneoff_at( end_timeval, callback(this, &MD5AuthHandler::key_stop_cb, key_id)); } // // XXX: If we are using the last authentication key that has expired, // move it to the list of invalid keys. // if (_valid_key_chain.size() == 1) { MD5Key& key = _valid_key_chain.front(); if (key.is_persistent()) { key.set_persistent(false); _invalid_key_chain.push_back(key); _valid_key_chain.pop_front(); } } // XXX: for simplicity just try to remove the key even if it doesn't exist remove_key(key_id, dummy_error_msg); // Add the new key to the appropriate chain MD5Key new_key = MD5Key(key_id, key, start_timeval, end_timeval, start_timer, end_timer); if (start_timer.scheduled()) _invalid_key_chain.push_back(new_key); else _valid_key_chain.push_back(new_key); return true; } bool MD5AuthHandler::remove_key(uint8_t key_id, string& error_msg) { KeyChain::iterator i; // Check among all valid keys i = find_if(_valid_key_chain.begin(), _valid_key_chain.end(), bind2nd(mem_fun_ref(&MD5Key::id_matches), key_id)); if (i != _valid_key_chain.end()) { _valid_key_chain.erase(i); return true; } // Check among all invalid keys i = find_if(_invalid_key_chain.begin(), _invalid_key_chain.end(), bind2nd(mem_fun_ref(&MD5Key::id_matches), key_id)); if (i != _invalid_key_chain.end()) { _invalid_key_chain.erase(i); return true; } error_msg = c_format("No such key"); return false; } void MD5AuthHandler::key_start_cb(uint8_t key_id) { KeyChain::iterator i; // Find the key among all invalid keys and move it to the valid keys i = find_if(_invalid_key_chain.begin(), _invalid_key_chain.end(), bind2nd(mem_fun_ref(&MD5Key::id_matches), key_id)); if (i != _invalid_key_chain.end()) { MD5Key& key = *i; _valid_key_chain.push_back(key); _invalid_key_chain.erase(i); } } void MD5AuthHandler::key_stop_cb(uint8_t key_id) { KeyChain::iterator i; // Find the key among all valid keys and move it to the invalid keys i = find_if(_valid_key_chain.begin(), _valid_key_chain.end(), bind2nd(mem_fun_ref(&MD5Key::id_matches), key_id)); if (i != _valid_key_chain.end()) { MD5Key& key = *i; // // XXX: If the last key expires then keep using it as per // RFC 2082 Section 4.3 until the lifetime is extended, the key // is deleted by network management, or a new key is configured. // if (_valid_key_chain.size() == 1) { XLOG_WARNING("Last authentication key (key ID = %u) has expired. " "Will keep using it until its lifetime is extended, " "the key is deleted, or a new key is configured.", key_id); key.set_persistent(true); return; } _invalid_key_chain.push_back(key); _valid_key_chain.erase(i); } } void MD5AuthHandler::reset_keys() { KeyChain::iterator iter; for (iter = _valid_key_chain.begin(); iter != _valid_key_chain.end(); ++iter) { iter->reset(); } } bool MD5AuthHandler::empty() const { return (_valid_key_chain.empty() && _invalid_key_chain.empty()); } xorp/rip/update_queue.hh0000664000076400007640000001007411540224235015461 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/rip/update_queue.hh,v 1.17 2008/10/02 21:58:18 bms Exp $ #ifndef __RIP_UPDATE_QUEUE__ #define __RIP_UPDATE_QUEUE__ #include "route_db.hh" template class UpdateQueueImpl; /** * @short Reader for @ref UpdateQueue class. * * Hooks and unhooks read iterators in update queue. The opaque * UpdateQueueReaderPool actually tracks the position of each iterator, * this class just maintains a token that the reader pool uses. */ template class UpdateQueueReader { public: UpdateQueueReader(UpdateQueueImpl* i); ~UpdateQueueReader(); uint32_t id() const; bool parent_is(const UpdateQueueImpl* o) const; private: UpdateQueueImpl* _impl; uint32_t _id; }; /** * @short Update Queue for RIP Route entries. * * The Update Queue has is conceptually a single writer multi-reader * queue. It is used to store state for triggered updates and may be * used unsolicited responses (routing table announcements). */ template class UpdateQueue { protected: typedef UpdateQueueReader Reader; public: typedef ref_ptr ReadIterator; typedef RouteEntryRef RouteUpdate; public: UpdateQueue(); ~UpdateQueue(); /** * Add update to back of queue. */ void push_back(const RouteUpdate& ru); /** * Remove all queued entries and reset all read iterators to the front * of the queue. */ void flush(); /** * Create a read iterator. These are reference counted entities that * need to be stored in order to operate. The newly created reader is * set to the end of the update queue. */ ReadIterator create_reader(); /** * Destroy read iterator. This method detaches the iterator from the * update queue. Use of the iterator after this call is unsafe. */ void destroy_reader(ReadIterator& r); /** * Check ReadIterator's validity. * @param r reader to be checked. * @return true if r is an active read iterator, false if iterator does * not belong to this instance or has been destroyed. */ bool reader_valid(const ReadIterator& r); /** * Increment iterator and return pointer to entry if available. * * @return A pointer to a RouteEntry if available, 0 otherwise. */ const RouteEntry* next(ReadIterator& r); /** * Get the RouteEntry associated with the read iterator. * * @return A pointer to a RouteEntry if available, 0 otherwise. */ const RouteEntry* get(ReadIterator& r) const; /** * Advance read iterator to end of update queue. Calls to * @ref next and @ref get will return 0 until further * updates occur. */ void ffwd(ReadIterator& r); /** * Move read iterator to first entry of update queue. */ void rwd(ReadIterator& r); /** * Return number of updates held. Note: this may be more than are * available for reading since there is internal buffering and * UpdateQueue iterators attach at the end of the UpdateQueue. * * @return number of updates queued. */ uint32_t updates_queued() const; protected: UpdateQueueImpl* _impl; }; #endif // __RIP_UPDATE_QUEUE__ xorp/rip/constants.hh0000664000076400007640000001005111421137511015000 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/rip/constants.hh,v 1.23 2008/10/02 21:58:16 bms Exp $ #ifndef __RIP_CONSTANTS_HH__ #define __RIP_CONSTANTS_HH__ #include "libxorp/ipv4.hh" #include "libxorp/ipv4net.hh" #include "libxorp/ipv6.hh" #include "libxorp/ipv6net.hh" static const uint32_t DEFAULT_UPDATE_INTERVAL = 30; static const uint32_t DEFAULT_UPDATE_JITTER = 16; static const uint32_t DEFAULT_EXPIRY_SECS = 180; static const uint32_t DEFAULT_DELETION_SECS = 120; static const uint32_t DEFAULT_TRIGGERED_UPDATE_DELAY = 3; static const uint32_t DEFAULT_TRIGGERED_UPDATE_JITTER = 66; static const uint32_t DEFAULT_TABLE_REQUEST_SECS = 1; static const uint32_t RIPv2_ROUTES_PER_PACKET = 25; /** * The default delay between back to back RIP packets when an update * is sent that spans more than 1 packet or there are multiple packets * to be sent. */ static const uint32_t DEFAULT_INTERPACKET_DELAY_MS = 50; /** * The maximum delay between back to back RIP packets when an update * is sent that spans more than 1 packet. */ static const uint32_t MAXIMUM_INTERPACKET_DELAY_MS = 250; /** * The default delay between accepting route request packets that * query specific routes. */ static const uint32_t DEFAULT_INTERQUERY_GAP_MS = 250; /** * Protocol specified metric corresponding to an unreachable (or expired) * host or network. */ static const uint32_t RIP_INFINITY = 16; /** * The maximum cost of a routing entry. * Note that it must be larger than the protocol defined RIP_INFINITY. */ static const uint32_t RIP_MAX_COST = 0xffff; /** * Time-To-Live value that should be used for multicast packets. */ static const uint32_t RIP_TTL = 1; /** * Hop-Count value that should be used for multicast RIPng packets. */ static const uint32_t RIP_NG_HOP_COUNT = 255; /** * RIP IPv4 protocol port */ static const uint16_t RIP_PORT = 520; /** * RIPng protocol port */ static const uint16_t RIP_NG_PORT = 521; static const IPv4Net IPv4_DEFAULT_ROUTE = IPv4Net(IPv4::ZERO(), 0); static const IPv6Net IPv6_DEFAULT_ROUTE = IPv6Net(IPv6::ZERO(), 0); /** * Basis of specialized classes containing RIP constants. */ template struct RIP_AF_CONSTANTS; template <> struct RIP_AF_CONSTANTS { static const IPv4 IP_GROUP() { return IPv4::RIP2_ROUTERS(); } static const uint16_t IP_PORT = RIP_PORT; static const IPv4Net& DEFAULT_ROUTE() { return IPv4_DEFAULT_ROUTE; } static const uint8_t PACKET_VERSION = 2; }; template <> struct RIP_AF_CONSTANTS { static const IPv6& IP_GROUP() { return IPv6::RIP2_ROUTERS(); } static const uint16_t IP_PORT = RIP_NG_PORT; static const IPv6Net& DEFAULT_ROUTE() { return IPv6_DEFAULT_ROUTE; } static const uint8_t PACKET_VERSION = 1; }; /** * Enumeration of RIP Horizon types. */ enum RipHorizon { // No filtering NONE = 0, // Don't a route origin its own routes. SPLIT = 1, // Show a route origin its own routes but with cost of infinity. SPLIT_POISON_REVERSE = 2 }; inline static const char* rip_horizon_name(RipHorizon h) { switch (h) { case SPLIT_POISON_REVERSE: return "split-horizon-poison-reverse"; case SPLIT: return "split-horizon"; case NONE: break; } return "none"; }; #endif // __RIP_CONSTANTS_HH__ xorp/rip/xrl_target_rip.hh0000664000076400007640000002404411540225534016025 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __RIP_XRL_TARGET_RIP_HH__ #define __RIP_XRL_TARGET_RIP_HH__ #include "libxorp/status_codes.h" #include "xrl/targets/rip_base.hh" class XrlRouter; class XrlProcessSpy; template class System; template class XrlPortManager; template class XrlRipCommonTarget; template class XrlRedistManager; class XrlRipTarget : public XrlRipTargetBase { public: XrlRipTarget(EventLoop& e, XrlRouter& xr, XrlProcessSpy& xps, XrlPortManager& xpm, XrlRedistManager& xrm, System& rip_system); ~XrlRipTarget(); void set_status(ProcessStatus ps, const string& annotation = ""); XrlCmdError common_0_1_get_target_name(string& name); XrlCmdError common_0_1_get_version(string& version); XrlCmdError common_0_1_get_status(uint32_t& status, string& reason); XrlCmdError common_0_1_shutdown(); XrlCmdError common_0_1_startup(); XrlCmdError finder_event_observer_0_1_xrl_target_birth(const string& class_name, const string& instance_name); XrlCmdError finder_event_observer_0_1_xrl_target_death(const string& class_name, const string& instance_name); XrlCmdError rip_0_1_add_rip_address(const string& ifname, const string& vifname, const IPv4& addr); XrlCmdError rip_0_1_remove_rip_address(const string& ifname, const string& vifname, const IPv4& addr); XrlCmdError rip_0_1_set_rip_address_enabled(const string& ifname, const string& vifname, const IPv4& addr, const bool& enabled); XrlCmdError rip_0_1_rip_address_enabled(const string& ifname, const string& vifname, const IPv4& addr, bool& enabled); XrlCmdError rip_0_1_set_cost(const string& ifname, const string& vifname, const IPv4& addr, const uint32_t& cost); XrlCmdError rip_0_1_cost(const string& ifname, const string& vifname, const IPv4& addr, uint32_t& cost); XrlCmdError rip_0_1_set_horizon(const string& ifname, const string& vifname, const IPv4& addr, const string& horizon); XrlCmdError rip_0_1_horizon(const string& ifname, const string& vifname, const IPv4& addr, string& horizon); XrlCmdError rip_0_1_set_passive(const string& ifname, const string& vifname, const IPv4& addr, const bool& passive); XrlCmdError rip_0_1_passive(const string& ifname, const string& vifname, const IPv4& addr, bool& passive); XrlCmdError rip_0_1_set_accept_non_rip_requests(const string& ifname, const string& vifname, const IPv4& addr, const bool& accept); XrlCmdError rip_0_1_accept_non_rip_requests(const string& ifname, const string& vifname, const IPv4& addr, bool& accept); XrlCmdError rip_0_1_set_accept_default_route(const string& ifname, const string& vifname, const IPv4& addr, const bool& accept); XrlCmdError rip_0_1_accept_default_route(const string& ifname, const string& vifname, const IPv4& addr, bool& accept); XrlCmdError rip_0_1_set_advertise_default_route(const string& ifname, const string& vifname, const IPv4& addr, const bool& advertise); XrlCmdError rip_0_1_advertise_default_route(const string& ifname, const string& vifname, const IPv4& addr, bool& advertise); XrlCmdError rip_0_1_set_route_timeout(const string& ifname, const string& vifname, const IPv4& addr, const uint32_t& t_secs); XrlCmdError rip_0_1_route_timeout(const string& ifname, const string& vifname, const IPv4& addr, uint32_t& t_secs); XrlCmdError rip_0_1_set_deletion_delay(const string& ifname, const string& vifname, const IPv4& addr, const uint32_t& t_secs); XrlCmdError rip_0_1_deletion_delay(const string& ifname, const string& vifname, const IPv4& addr, uint32_t& t_secs); XrlCmdError rip_0_1_set_request_interval(const string& ifname, const string& vifname, const IPv4& addr, const uint32_t& t_secs); XrlCmdError rip_0_1_request_interval(const string& ifname, const string& vifname, const IPv4& addr, uint32_t& t_secs); XrlCmdError rip_0_1_set_update_interval(const string& ifname, const string& vifname, const IPv4& addr, const uint32_t& t_secs); XrlCmdError rip_0_1_update_interval(const string& ifname, const string& vifname, const IPv4& addr, uint32_t& t_secs); XrlCmdError rip_0_1_set_update_jitter(const string& ifname, const string& vifname, const IPv4& addr, const uint32_t& t_jitter); XrlCmdError rip_0_1_update_jitter(const string& ifname, const string& vifname, const IPv4& addr, uint32_t& t_jitter); XrlCmdError rip_0_1_set_triggered_update_delay(const string& ifname, const string& vifname, const IPv4& addr, const uint32_t& t_secs); XrlCmdError rip_0_1_triggered_update_delay(const string& ifname, const string& vifname, const IPv4& addr, uint32_t& t_secs); XrlCmdError rip_0_1_set_triggered_update_jitter(const string& ifname, const string& vifname, const IPv4& addr, const uint32_t& t_jitter); XrlCmdError rip_0_1_triggered_update_jitter(const string& ifname, const string& vifname, const IPv4& addr, uint32_t& t_jitter); XrlCmdError rip_0_1_set_interpacket_delay(const string& ifname, const string& vifname, const IPv4& addr, const uint32_t& t_msecs); XrlCmdError rip_0_1_interpacket_delay(const string& ifname, const string& vifname, const IPv4& addr, uint32_t& t_msecs); XrlCmdError rip_0_1_set_simple_authentication_key( // Input values, const string& ifname, const string& vifname, const IPv4& addr, const string& password); XrlCmdError rip_0_1_delete_simple_authentication_key( // Input values, const string& ifname, const string& vifname, const IPv4& addr); XrlCmdError rip_0_1_set_md5_authentication_key( // Input values, const string& ifname, const string& vifname, const IPv4& addr, const uint32_t& key_id, const string& password, const string& start_time, const string& end_time); XrlCmdError rip_0_1_delete_md5_authentication_key( // Input values, const string& ifname, const string& vifname, const IPv4& addr, const uint32_t& key_id); XrlCmdError rip_0_1_rip_address_status(const string& ifname, const string& vifname, const IPv4& addr, string& status); XrlCmdError rip_0_1_get_all_addresses(XrlAtomList& ifnames, XrlAtomList& vifnames, XrlAtomList& addrs); XrlCmdError rip_0_1_get_peers(const string& ifname, const string& vifname, const IPv4& addr, XrlAtomList& peers); XrlCmdError rip_0_1_get_all_peers(XrlAtomList& peers, XrlAtomList& ifnames, XrlAtomList& vifnames, XrlAtomList& addrs); XrlCmdError rip_0_1_get_counters(const string& ifname, const string& vifname, const IPv4& addr, XrlAtomList& descriptions, XrlAtomList& values); XrlCmdError rip_0_1_get_peer_counters(const string& ifname, const string& vifname, const IPv4& addr, const IPv4& peer, XrlAtomList& descriptions, XrlAtomList& values, uint32_t& peer_last_active); XrlCmdError rip_0_1_trace(const string& tvar, const bool& enable); XrlCmdError socket4_user_0_1_recv_event(const string& sockid, const string& if_name, const string& vif_name, const IPv4& src_host, const uint32_t& src_port, const vector& pdata); XrlCmdError socket4_user_0_1_inbound_connect_event( const string& sockid, const IPv4& src_host, const uint32_t& src_port, const string& new_sockid, bool& accept); XrlCmdError socket4_user_0_1_outgoing_connect_event( const string& sockid); XrlCmdError socket4_user_0_1_error_event(const string& sockid, const string& reason, const bool& fatal); XrlCmdError socket4_user_0_1_disconnect_event(const string& sockid); XrlCmdError policy_backend_0_1_configure( // Input values, const uint32_t& filter, const string& conf); XrlCmdError policy_backend_0_1_reset( // Input values, const uint32_t& filter); XrlCmdError policy_backend_0_1_push_routes(); XrlCmdError policy_redist4_0_1_add_route4( // Input values, const IPv4Net& network, const bool& unicast, const bool& multicast, const IPv4& nexthop, const uint32_t& metric, const XrlAtomList& policytags); XrlCmdError policy_redist4_0_1_delete_route4( // Input values, const IPv4Net& network, const bool& unicast, const bool& multicast); protected: EventLoop& _e; XrlRipCommonTarget* _ct; }; #endif // __RIP_XRL_TARGET_RIP_HH__ xorp/rip/port_vars.hh0000664000076400007640000002715311421137511015016 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/rip/port_vars.hh,v 1.16 2008/10/02 21:58:17 bms Exp $ #ifndef __RIP_PORT_VARS_HH__ #define __RIP_PORT_VARS_HH__ #include "constants.hh" /** * @short Container of counters associated with a Port. */ struct PortCounters { public: PortCounters() : _packets_recv(0), _requests_recv(0), _updates_recv(0), _bad_routes(0), _bad_packets(0), _bad_auth_packets(0), _tr_sent(0), _tr_recv(), _triggered_updates(0), _unsol_updates(0), _nr_req_recv(0), _nr_updates(0) {} /** * Get the total number of packets received. */ uint32_t packets_recv() const { return _packets_recv; } /** * Increment the total number of packets received. */ void incr_packets_recv() { _packets_recv++; } /** * Get total number of update packets received. */ uint32_t update_packets_recv() const { return _updates_recv; } /** * Increment total number of update packets received. */ void incr_update_packets_recv() { _updates_recv++; } /** * Get the number of bad routes received (eg invalid metric, * invalid address family). */ uint32_t bad_routes() const { return _bad_routes; } /** * Increment the number of bad routes received. */ void incr_bad_routes() { _bad_routes++; } /** * Get the number of bad response packets received. */ uint32_t bad_packets() const { return _bad_packets; } /** * Increment the number of bad response packets received. */ void incr_bad_packets() { _bad_packets++; } /** * Get the number of authentication failing packets received. */ uint32_t bad_auth_packets() const { return _bad_auth_packets; } /** * Increment the number of bad authentication packets received. */ void incr_bad_auth_packets() { _bad_auth_packets++; } /** * Get the number of triggered updates sent. */ uint32_t triggered_updates() const { return _triggered_updates; } /** * Increment the number of triggered updates sent. */ void incr_triggered_updates() { _triggered_updates++; } /** * Get the number of unsolicited updates sent. */ uint32_t unsolicited_updates() const { return _unsol_updates; } /** * Increment the number of unsolicited updates sent. */ void incr_unsolicited_updates() { _unsol_updates++; } /** * Get the number of table requests sent. */ uint32_t table_requests_sent() const { return _tr_sent; } /** * Increment the number of table requests updates sent. */ void incr_table_requests_sent() { _tr_sent++; } /** * Get the number of table requests received. */ uint32_t table_requests_recv() const { return _tr_recv; } /** * Increment the number of table requests updates received. */ void incr_table_requests_recv() { _tr_recv++; } /** * Get number of non-RIP request packets received. */ uint32_t non_rip_requests_recv() const { return _nr_req_recv; } /** * Increment the number of non-RIP request packets received. */ void incr_non_rip_requests_recv() { _nr_req_recv++; } /** * Get number of non-RIP update packets sent. */ uint32_t non_rip_updates_sent() const { return _nr_updates; } /** * Increment the number of non-RIP request packets received. */ void incr_non_rip_updates_sent() { _nr_updates++; } protected: uint32_t _packets_recv; uint32_t _requests_recv; uint32_t _updates_recv; uint32_t _bad_routes; uint32_t _bad_packets; uint32_t _bad_auth_packets; uint32_t _tr_sent; uint32_t _tr_recv; uint32_t _triggered_updates; uint32_t _unsol_updates; uint32_t _nr_req_recv; uint32_t _nr_updates; }; /** * @short Container of timer constants associated with a RIP port. */ class PortTimerConstants { public: /** * Initialize contants with default values from RIPv2 spec. The values * are defined in constants.hh. */ PortTimerConstants(); /** * Set the route expiration time. * @param t the expiration time in seconds. * @return true on success. */ bool set_expiry_secs(uint32_t t); /** * Get the route route expiration time. * @return expiry time in seconds. */ uint32_t expiry_secs() const; /** * Set the route deletion time. * @param t the deletion time in seconds (must be >= 1). * @return true on success, false if t == 0. */ bool set_deletion_secs(uint32_t t); /** * Get the route deletion time. * @return deletion time in seconds. */ uint32_t deletion_secs() const; /** * Set request packet transmission period. Request packets are only * sent when there are no peers associated with a port. * @param t inter-packet interval in seconds. * @return true on success. */ bool set_table_request_period_secs(uint32_t t); /** * Set request packet transmission period. * @return inter-packet interval in seconds. */ uint32_t table_request_period_secs() const; /** * Set unsolicitied response time. * @param t_secs unsolicited response time in seconds. * @return true on success. */ bool set_update_interval(uint32_t t_secs); /** * Get unsolicitied response time. * @return unsolicited response time in seconds. */ uint32_t update_interval(); /** * Set unsolicitied response time jitter. * @param t_jitter unsolicited response time jitter * (in percents of the time period). * @return true on success. */ bool set_update_jitter(uint32_t t_jitter); /** * Get unsolicitied response time jitter. * @return unsolicited response time jitter * (in percents of the time period). */ uint32_t update_jitter(); /** * Set the triggered update delay. * @param t_secs the triggered update delay in seconds. * @return true on success. */ bool set_triggered_update_delay(uint32_t t_secs); /** * Get the triggered update delay. * @return the triggered update delay in seconds. */ uint32_t triggered_update_delay() const; /** * Set the triggered update jitter. * @param t_jitter the triggered update jitter * (in percents of the time delay). * @return true on success. */ bool set_triggered_update_jitter(uint32_t t_jitter); /** * Get the triggered update jitter. * @return the triggered update jitter (in percents of the time delay). */ uint32_t triggered_update_jitter() const; /** * Set the interpacket packet delay. * @param t the interpacket delay for back-to-back packets in * milliseconds. * @return true on success, false if t is greater than * MAXIMUM_INTERPACKET_DELAY_MS. */ bool set_interpacket_delay_ms(uint32_t t); /** * Get the interpacket packet delay in milliseconds. */ uint32_t interpacket_delay_ms() const; /** * Set the interquery gap. This is the minimum temporal gap between * route request packets that query specific routes. Queries arriving * at a faster rate are ignored. * @param t the interquery delay in milliseconds. * @return true on success. */ bool set_interquery_delay_ms(uint32_t t); /** * Get the interquery gap. This is the minimum temporal gap between * route request packets that query specific routes. Fast arriving * queries are ignored. * @return the interquery delay in milliseconds. */ uint32_t interquery_delay_ms() const; protected: uint32_t _expiry_secs; uint32_t _deletion_secs; uint32_t _table_request_secs; uint32_t _update_interval; uint32_t _update_jitter; uint32_t _triggered_update_delay; uint32_t _triggered_update_jitter; uint32_t _interpacket_msecs; uint32_t _interquery_msecs; }; // ---------------------------------------------------------------------------- // Inline PortTimerConstants accessor and modifiers. inline PortTimerConstants::PortTimerConstants() : _expiry_secs(DEFAULT_EXPIRY_SECS), _deletion_secs(DEFAULT_DELETION_SECS), _table_request_secs(DEFAULT_TABLE_REQUEST_SECS), _update_interval(DEFAULT_UPDATE_INTERVAL), _update_jitter(DEFAULT_UPDATE_JITTER), _triggered_update_delay(DEFAULT_TRIGGERED_UPDATE_DELAY), _triggered_update_jitter(DEFAULT_TRIGGERED_UPDATE_JITTER), _interpacket_msecs(DEFAULT_INTERPACKET_DELAY_MS), _interquery_msecs(DEFAULT_INTERQUERY_GAP_MS) { } inline bool PortTimerConstants::set_expiry_secs(uint32_t t) { if (t == 0) return false; _expiry_secs = t; return true; } inline uint32_t PortTimerConstants::expiry_secs() const { return _expiry_secs; } inline bool PortTimerConstants::set_deletion_secs(uint32_t t) { if (t == 0) return false; _deletion_secs = t; return true; } inline uint32_t PortTimerConstants::deletion_secs() const { return _deletion_secs; } inline bool PortTimerConstants::set_update_interval(uint32_t t_secs) { _update_interval = t_secs; return true; } inline uint32_t PortTimerConstants::update_interval() { return _update_interval; } inline bool PortTimerConstants::set_update_jitter(uint32_t t_jitter) { if (t_jitter > 100) return false; _update_jitter = t_jitter; return true; } inline uint32_t PortTimerConstants::update_jitter() { return _update_jitter; } inline bool PortTimerConstants::set_table_request_period_secs(uint32_t t) { // // XXX: value of 0 is accepted because it is used to disable the // periodic request messages. // _table_request_secs = t; return true; } inline uint32_t PortTimerConstants::table_request_period_secs() const { return _table_request_secs; } inline bool PortTimerConstants::set_triggered_update_delay(uint32_t t_secs) { _triggered_update_delay = t_secs; return true; } inline uint32_t PortTimerConstants::triggered_update_delay() const { return _triggered_update_delay; } inline bool PortTimerConstants::set_triggered_update_jitter(uint32_t t_jitter) { if (t_jitter > 100) return false; _triggered_update_jitter = t_jitter; return true; } inline uint32_t PortTimerConstants::triggered_update_jitter() const { return _triggered_update_jitter; } inline bool PortTimerConstants::set_interpacket_delay_ms(uint32_t t) { if (t > MAXIMUM_INTERPACKET_DELAY_MS) return false; _interpacket_msecs = t; return true; } inline uint32_t PortTimerConstants::interpacket_delay_ms() const { return _interpacket_msecs; } inline bool PortTimerConstants::set_interquery_delay_ms(uint32_t t) { _interquery_msecs = t; return true; } inline uint32_t PortTimerConstants::interquery_delay_ms() const { return _interquery_msecs; } #endif // __RIP_PORT_VARS_HH__ xorp/rip/xrl_port_io.cc0000664000076400007640000002623411540225534015331 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxipc/xrl_router.hh" #include "constants.hh" #include "xrl_config.hh" #include "xrl_port_io.hh" #include "rip_module.h" #include "libxorp/xlog.h" // ---------------------------------------------------------------------------- // Multicast socket code. We instantiate one multicast socket per // socket server. template class SocketManager { public: static const string no_entry; public: SocketManager() {} ~SocketManager() {} const string& sockid(const string& socket_server) const { map::const_iterator ci = _m.find(socket_server); if (ci == _m.end()) { return no_entry; } return ci->second; } void add_sockid(const string& socket_server, const string& sockid) { _m[socket_server] = sockid; } public: map _m; // [ : ] }; template const string SocketManager::no_entry; #if defined(INSTANTIATE_IPV4) static SocketManager socket_manager; #elif defined(INSTANTIATE_IPV6) static SocketManager socket_manager; #endif // ---------------------------------------------------------------------------- // IPv4 specialized XrlPortIO #ifdef INSTANTIATE_IPV4 #include "xrl/interfaces/socket4_xif.hh" template <> bool XrlPortIO::request_open_bind_socket() { XrlSocket4V0p1Client cl(&_xr); return cl.send_udp_open_and_bind( _ss.c_str(), _xr.instance_name(), IPv4::ANY(), RIP_AF_CONSTANTS::IP_PORT, vifname(), 1, callback(this, &XrlPortIO::open_bind_socket_cb) ); } template <> bool XrlPortIO::request_ttl() { XrlSocket4V0p1Client cl(&_xr); return cl.send_set_socket_option( _ss.c_str(), socket_id(), "multicast_ttl", RIP_TTL, callback(this, &XrlPortIO::ttl_cb)); } template <> bool XrlPortIO::request_no_loop() { XrlSocket4V0p1Client cl(&_xr); return cl.send_set_socket_option( _ss.c_str(), socket_id(), "multicast_loopback", 0, callback(this, &XrlPortIO::no_loop_cb)); } template <> bool XrlPortIO::request_socket_join() { XrlSocket4V0p1Client cl(&_xr); return cl.send_udp_join_group( _ss.c_str(), socket_id(), RIP_AF_CONSTANTS::IP_GROUP(), this->address(), callback(this, &XrlPortIO::socket_join_cb)); } template <> bool XrlPortIO::request_socket_leave() { XrlSocket4V0p1Client cl(&_xr); return cl.send_udp_leave_group( _ss.c_str(), socket_id(), RIP_AF_CONSTANTS::IP_GROUP(), this->address(), callback(this, &XrlPortIO::socket_leave_cb)); } template <> bool XrlPortIO::send(const IPv4& dst_addr, uint16_t dst_port, const vector& rip_packet) { if (_pending) { debug_msg("Send skipped (pending).\n"); return false; } XrlSocket4V0p1Client cl(&_xr); if (dst_addr.is_multicast()) { if (cl.send_send_from_multicast_if( _ss.c_str(), socket_id(), dst_addr, dst_port, this->address(), rip_packet, callback(this, &XrlPortIO::send_cb))) { debug_msg("Sent %u bytes to %s/%u from %s\n", XORP_UINT_CAST(rip_packet.size()), dst_addr.str().c_str(), dst_port, this->address().str().c_str()); _pending = true; return true; } } else { if (cl.send_send_to(_ss.c_str(), socket_id(), dst_addr, dst_port, rip_packet, callback(this, &XrlPortIO::send_cb))) { debug_msg("Sent %u bytes to %s/%u\n", XORP_UINT_CAST(rip_packet.size()), dst_addr.str().c_str(), dst_port); _pending = true; return true; } } return false; } #endif // INSTANTIATE_IPV4 // ---------------------------------------------------------------------------- // IPv6 specialized XrlPortIO #ifdef INSTANTIATE_IPV6 // TODO: Fix this up so none of this is compiled if IPv6 is disabled. #ifdef HAVE_IPV6 #include "xrl/interfaces/socket6_xif.hh" #endif template <> bool XrlPortIO::request_open_bind_socket() { #ifdef HAVE_IPV6 XrlSocket6V0p1Client cl(&_xr); return cl.send_udp_open_and_bind( _ss.c_str(), _xr.instance_name(), IPv6::ANY(), RIP_AF_CONSTANTS::IP_PORT, vifname(), 1, callback(this, &XrlPortIO::open_bind_socket_cb) ); #else return false; #endif } template <> bool XrlPortIO::request_ttl() { #ifdef HAVE_IPV6 XrlSocket6V0p1Client cl(&_xr); return cl.send_set_socket_option( _ss.c_str(), socket_id(), "multicast_ttl", RIP_NG_HOP_COUNT, callback(this, &XrlPortIO::ttl_cb)); #else return false; #endif } template <> bool XrlPortIO::request_no_loop() { #ifdef HAVE_IPV6 XrlSocket6V0p1Client cl(&_xr); return cl.send_set_socket_option( _ss.c_str(), socket_id(), "multicast_loopback", 0, callback(this, &XrlPortIO::no_loop_cb)); #else return false; #endif } template <> bool XrlPortIO::request_socket_join() { #ifdef HAVE_IPV6 XrlSocket6V0p1Client cl(&_xr); return cl.send_udp_join_group( _ss.c_str(), socket_id(), RIP_AF_CONSTANTS::IP_GROUP(), this->address(), callback(this, &XrlPortIO::socket_join_cb)); #else return false; #endif } template <> bool XrlPortIO::request_socket_leave() { #ifdef HAVE_IPV6 XrlSocket6V0p1Client cl(&_xr); return cl.send_udp_leave_group( _ss.c_str(), socket_id(), RIP_AF_CONSTANTS::IP_GROUP(), this->address(), callback(this, &XrlPortIO::socket_leave_cb)); #else return false; #endif } template <> bool XrlPortIO::send(const IPv6& dst_addr, uint16_t dst_port, const vector& rip_packet) { if (_pending) { debug_msg("Send skipped (pending).\n"); return false; } #ifdef HAVE_IPV6 XrlSocket6V0p1Client cl(&_xr); if (dst_addr.is_multicast()) { if (cl.send_send_from_multicast_if( _ss.c_str(), socket_id(), dst_addr, dst_port, this->address(), rip_packet, callback(this, &XrlPortIO::send_cb))) { debug_msg("Sent %u bytes to %s/%u from %s\n", XORP_UINT_CAST(rip_packet.size()), dst_addr.str().c_str(), dst_port, this->address().str().c_str()); _pending = true; return true; } } else { if (cl.send_send_to(_ss.c_str(), socket_id(), dst_addr, dst_port, rip_packet, callback(this, &XrlPortIO::send_cb))) { debug_msg("Sent %u bytes to %s/%u\n", XORP_UINT_CAST(rip_packet.size()), dst_addr.str().c_str(), dst_port); _pending = true; return true; } } #else UNUSED(dst_addr); UNUSED(dst_port); UNUSED(rip_packet); #endif return false; } #endif // INSTANTIATE_IPV6 // ---------------------------------------------------------------------------- // Non-specialized XrlPortIO methods. template XrlPortIO::XrlPortIO(XrlRouter& xr, PortIOUser& port, const string& ifname, const string& vifname, const Addr& addr) : PortIOBase(port, ifname, vifname, addr, false), ServiceBase("RIP I/O port"), _xr(xr), _pending(false) { debug_msg("Created RIP I/O Port on %s/%s/%s\n", ifname.c_str(), vifname.c_str(), addr.str().c_str()); } template bool XrlPortIO::pending() const { debug_msg("%s/%s/%s pending %d\n", this->ifname().c_str(), this->vifname().c_str(), this->address().str().c_str(), _pending); return _pending; } template int XrlPortIO::startup() { _pending = true; set_status(SERVICE_STARTING); if (startup_socket() == false) { set_status(SERVICE_FAILED, "Failed to find appropriate socket server."); return (XORP_ERROR); } return (XORP_OK); } template int XrlPortIO::shutdown() { _pending = true; this->set_enabled(false); set_status(SERVICE_SHUTTING_DOWN); if (request_socket_leave() == false) { set_status(SERVICE_SHUTDOWN); } return (XORP_OK); } template bool XrlPortIO::startup_socket() { _ss = xrl_fea_name(); if (_sid.size() == 0) { // Nobody has created the RIP socket yet do it! // If we succeed here the path is: // request_open_bind_socket() // -> open_bind_socket_cb() // -> request_ttl() // -> ttl_cb() // -> request_no_loop() // -> no_loop_cb() // ->request_socket_join() // if (request_open_bind_socket() == false) { set_status(SERVICE_FAILED, "Failed sending RIP socket open request."); return false; } } else { // RIP socket exists, join appropriate interface to multicast // group. if (request_socket_join() == false) { set_status(SERVICE_FAILED, "Failed sending multicast join request."); return false; } } return true; } template void XrlPortIO::open_bind_socket_cb(const XrlError& e, const string* psid) { if (e != XrlError::OKAY()) { set_status(SERVICE_FAILED, "Failed to instantiate RIP socket."); return; } _sid = *psid; socket_manager.add_sockid(_ss, _sid); if (request_ttl() == false) { set_status(SERVICE_FAILED, "Failed requesting ttl/hops."); } } template void XrlPortIO::ttl_cb(const XrlError& e) { if (e != XrlError::OKAY()) { XLOG_WARNING("Failed to set ttl/hops."); } if (request_no_loop() == false) { set_status(SERVICE_FAILED, "Failed requesting multicast loopback off."); } } template void XrlPortIO::no_loop_cb(const XrlError& e) { if (e != XrlError::OKAY()) { XLOG_WARNING("Failed to turn off multicast loopback."); } if (request_socket_join() == false) { set_status(SERVICE_FAILED, "Failed to send join request."); } } template void XrlPortIO::socket_join_cb(const XrlError& e) { if (e != XrlError::OKAY()) { set_status(SERVICE_FAILED, c_format("Failed to join group on %s/%s/%s.", this->ifname().c_str(), this->vifname().c_str(), this->address().str().c_str()) ); return; } _pending = false; set_status(SERVICE_RUNNING); this->set_enabled(true); } template void XrlPortIO::socket_leave_cb(const XrlError& /* e */) { set_status(SERVICE_SHUTDOWN); } template void XrlPortIO::send_cb(const XrlError& xe) { debug_msg("SendCB %s\n", xe.str().c_str()); _pending = false; this->_user.port_io_send_completion(xe == XrlError::OKAY()); } #ifdef INSTANTIATE_IPV4 template class XrlPortIO; #endif #ifdef INSTANTIATE_IPV6 template class XrlPortIO; #endif xorp/rip/xrl_process_spy.hh0000664000076400007640000000671411421137511016235 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/rip/xrl_process_spy.hh,v 1.12 2008/10/02 21:58:19 bms Exp $ #ifndef __RIP_XRL_PROCESS_SPY_HH__ #define __RIP_XRL_PROCESS_SPY_HH__ #include "libxorp/service.hh" class XrlRouter; /** * @short Class that watches remote FEA and RIB processes. * * This class registers interest with the Finder in the FEA and RIB * processes and reports whether the FEA and RIB are running to * interested parties. */ class XrlProcessSpy : public ServiceBase { public: XrlProcessSpy(XrlRouter& rtr); ~XrlProcessSpy(); /** * Register interest in FEA and RIB with Finder. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int startup(); /** * Deregister interest in FEA and RIB with Finder. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int shutdown(); /** * Get indication of whether FEA is present. * * @return true if FEA is present, false if FEA is not present or * @ref run_status() is not in SERVICE_RUNNING state. */ bool fea_present() const; /** * Get indication of whether RIB is present. * * @return true if RIB is present, false if RIB is not present or * @ref run_status() is not in SERVICE_RUNNING state. */ bool rib_present() const; /** * Inform instance about the birth of an Xrl Target instance * within a class. Typically called by associated Xrl Target of * running RIP. * * @param class_name class of new born Xrl Target. * @param instance_name instance name of new born Xrl Target. */ void birth_event(const string& class_name, const string& instance_name); /** * Inform instance about the death of a Xrl Target instance within a class. * Typically called by associated Xrl Target of running RIP. * * @param class_name class of recently deceased Xrl Target. * @param instance_name instance name of recently deceased Xrl Target. */ void death_event(const string& class_name, const string& instance_name); protected: void send_register(uint32_t idx); void register_cb(const XrlError& e, uint32_t idx); void schedule_register_retry(uint32_t idx); void send_deregister(uint32_t idx); void deregister_cb(const XrlError& e, uint32_t idx); void schedule_deregister_retry(uint32_t idx); protected: static const uint32_t FEA_IDX = 0; static const uint32_t RIB_IDX = 1; static const uint32_t END_IDX = 2; protected: XrlRouter& _rtr; string _cname[END_IDX]; string _iname[END_IDX]; XorpTimer _retry; }; #endif // __RIP_XRL_PROCESS_SPY_HH__ xorp/rip/port_io.hh0000664000076400007640000001415611421137511014451 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/rip/port_io.hh,v 1.16 2008/10/02 21:58:17 bms Exp $ #ifndef __RIP_PORT_IO_HH__ #define __RIP_PORT_IO_HH__ #include "constants.hh" #include "libxorp/ipv4.hh" #include "libxorp/ipv6.hh" template class PortIOUserBase; /** * Base class for RIP Port I/O classes. * * RIP Port I/O classes provide support for reading and writing to RIP * Ports. */ template class PortIOBase { public: typedef A Addr; typedef PortIOUserBase PortIOUser; public: /** * Constructor associating a Port I/O User object with I/O object. */ PortIOBase(PortIOUser& user, const string& ifname, const string& vifname, const Addr& address, bool enabled = true); virtual ~PortIOBase(); /** * Send RIP packet to Port. * * The invoker of this method is expected to be the associated * Port I/O User instance. The invoker is responsible for the * packet data in the send request and should not decommission the * data until it receives the @ref * PortIOUserBase::send_completion callback. If the request to * send fails immediately the send_completion callback is not * called for the associated data. * * @param dst_addr address to send packet. * @param dst_port port to send packet to. * @param rip_packet vector containing rip packet to be sent. * * @return false on immediately detectable failure, true otherwise. */ virtual bool send(const Addr& dst_addr, uint16_t dst_port, const vector& rip_packet) = 0; /** * Check if send request is pending. * @return true if a send request is pending, false otherwise. */ virtual bool pending() const = 0; /** * Get Interface name associated with I/O. */ const string& ifname() const { return _ifname; } /** * Get Virtual Interface name associated with I/O. */ const string& vifname() const { return _vifname; } /** * Get associated address. */ const Addr& address() const { return _addr; } /** * Get the maximum number of route entries in a packet. */ size_t max_route_entries_per_packet() const { return _max_rte_pp; } /** * Set the maximum number of route entries in a packet. * @return true on success, false if route entries per packet is fixed. */ bool set_max_route_entries_per_packet(size_t max_entries); /** * Get enabled status of I/O system. */ bool enabled() const { return _en; } /** * Set enabled status of I/O system. The user object associated with * the I/O system will be notified through * @ref PortIOBase::port_io_enabled_change() if the enabled state * changes. * * @param en new enable state. */ void set_enabled(bool en); protected: PortIOUser& _user; string _ifname; string _vifname; Addr _addr; size_t _max_rte_pp; bool _en; }; /** * @short Base class for users of Port I/O classes. */ template class PortIOUserBase { public: typedef PortIOBase PortIO; public: PortIOUserBase() : _pio(0) {} virtual ~PortIOUserBase(); virtual void port_io_send_completion(bool success) = 0; virtual void port_io_receive(const A& src_addr, uint16_t src_port, const uint8_t* rip_packet, size_t rip_packet_bytes) = 0; virtual void port_io_enabled_change(bool en) = 0; bool set_io_handler(PortIO* pio, bool set_owner); PortIO* io_handler(); const PortIO* io_handler() const; bool port_io_enabled() const; protected: PortIO* _pio; bool _pio_owner; }; // ---------------------------------------------------------------------------- // Inline PortIOBase Methods // template PortIOBase::PortIOBase(PortIOUser& user, const string& ifname, const string& vifname, const Addr& addr, bool en) : _user(user), _ifname(ifname), _vifname(vifname), _addr(addr), _max_rte_pp(RIPv2_ROUTES_PER_PACKET), _en(en) {} template PortIOBase::~PortIOBase() {} template <> inline bool PortIOBase::set_max_route_entries_per_packet(size_t) { return false; } template <> inline bool PortIOBase::set_max_route_entries_per_packet(size_t max_entries) { _max_rte_pp = max_entries; return true; } template inline void PortIOBase::set_enabled(bool en) { if (en != _en) { _en = en; _user.port_io_enabled_change(en); } } // ---------------------------------------------------------------------------- // Inline PortIOUserBase Methods // template PortIOUserBase::~PortIOUserBase() { if (_pio && _pio_owner) delete _pio; _pio = 0; } template inline bool PortIOUserBase::set_io_handler(PortIO* pio, bool set_owner) { if (_pio == 0) { _pio = pio; _pio_owner = set_owner; return true; } return false; } template inline PortIOBase* PortIOUserBase::io_handler() { return _pio; } template inline const PortIOBase* PortIOUserBase::io_handler() const { return _pio; } template inline bool PortIOUserBase::port_io_enabled() const { if (_pio) return _pio->enabled(); return false; } #endif // __RIP_PEER_IO_HH__ xorp/rip/rip_varrw.cc0000664000076400007640000000717611421137511015003 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "rip_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "policy/common/policy_utils.hh" #include "rip_varrw.hh" template RIPVarRW::RIPVarRW(RouteEntry& route) : _route(route) { } template void RIPVarRW::start_read() { initialize(VAR_POLICYTAGS, _route.policytags().element()); read_route_nexthop(_route); initialize(VAR_METRIC, new ElemU32(_route.cost())); // XXX which tag wins? Element* element = _route.policytags().element_tag(); ElemU32* e = dynamic_cast(element); if (e != NULL && e->val()) _route.set_tag(e->val()); delete element; initialize(VAR_TAG, new ElemU32(_route.tag())); } template Element* RIPVarRW::single_read(const Id& /* id */) { XLOG_UNREACHABLE(); return 0; } template void RIPVarRW::single_write(const Id& id, const Element& e) { if (id == VAR_POLICYTAGS) { _route.policytags().set_ptags(e); return; } if (write_nexthop(id, e)) return; const ElemU32* u32 = NULL; if (e.type() == ElemU32::id) { u32 = dynamic_cast(&e); XLOG_ASSERT(u32 != NULL); } if (id == VAR_METRIC) { XLOG_ASSERT(u32 != NULL); _route.set_cost(u32->val()); return; } if (id == VAR_TAG) { XLOG_ASSERT(u32 != NULL); _route.set_tag(u32->val()); _route.policytags().set_tag(e); return; } } #ifdef INSTANTIATE_IPV4 template <> bool RIPVarRW::write_nexthop(const Id& id, const Element& e) { if (id == VAR_NEXTHOP4 && e.type() == ElemIPv4NextHop::id) { const ElemIPv4NextHop* v4 = dynamic_cast(&e); XLOG_ASSERT(v4 != NULL); IPv4 nh(v4->val()); _route.set_nexthop(nh); return true; } return false; } template <> void RIPVarRW::read_route_nexthop(RouteEntry& route) { initialize(VAR_NETWORK4, new ElemIPv4Net(route.net())); initialize(VAR_NEXTHOP4, new ElemIPv4NextHop(route.nexthop())); initialize(VAR_NETWORK6, NULL); initialize(VAR_NEXTHOP6, NULL); } template class RIPVarRW; #endif // INSTANTIATE_IPV4 #ifdef INSTANTIATE_IPV6 template <> bool RIPVarRW::write_nexthop(const Id& id, const Element& e) { if (id == VAR_NEXTHOP6 && e.type() == ElemIPv6NextHop::id) { const ElemIPv6NextHop* v6 = dynamic_cast(&e); XLOG_ASSERT(v6 != NULL); IPv6 nh(v6->val()); _route.set_nexthop(nh); return true; } return false; } template <> void RIPVarRW::read_route_nexthop(RouteEntry& route) { initialize(VAR_NETWORK6, new ElemIPv6Net(route.net())); initialize(VAR_NEXTHOP6, new ElemIPv6NextHop(route.nexthop())); initialize(VAR_NETWORK4, NULL); initialize(VAR_NEXTHOP4, NULL); } template class RIPVarRW; #endif // INSTANTIATE_IPV6 xorp/rip/xrl_process_spy.cc0000664000076400007640000001173011421137511016215 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING #include "rip_module.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/xorp.h" #include "libxorp/eventloop.hh" #include "libxipc/xrl_router.hh" #include "xrl/interfaces/finder_event_notifier_xif.hh" #include "xrl_config.hh" #include "xrl_process_spy.hh" XrlProcessSpy::XrlProcessSpy(XrlRouter& rtr) : ServiceBase("FEA/RIB Process Watcher"), _rtr(rtr) { _cname[FEA_IDX] = xrl_fea_name(); _cname[RIB_IDX] = xrl_rib_name(); } XrlProcessSpy::~XrlProcessSpy() { } bool XrlProcessSpy::fea_present() const { if (status() == SERVICE_RUNNING) return _iname[FEA_IDX].empty() == false; debug_msg("XrlProcessSpy::fea_present() called when not " "SERVICE_RUNNING.\n"); return false; } bool XrlProcessSpy::rib_present() const { if (status() == SERVICE_RUNNING) return _iname[RIB_IDX].empty() == false; debug_msg("XrlProcessSpy::rib_present() called when not " "SERVICE_RUNNING.\n"); return false; } void XrlProcessSpy::birth_event(const string& class_name, const string& instance_name) { debug_msg("Birth event: class %s instance %s\n", class_name.c_str(), instance_name.c_str()); for (uint32_t i = 0; i < END_IDX; i++) { if (class_name != _cname[i]) { continue; } if (_iname[i].empty() == false) { XLOG_WARNING("Got "); } _iname[i] = instance_name; } } void XrlProcessSpy::death_event(const string& class_name, const string& instance_name) { debug_msg("Death event: class %s instance %s\n", class_name.c_str(), instance_name.c_str()); for (uint32_t i = 0; i < END_IDX; i++) { if (class_name != _cname[i]) { continue; } if (_iname[i] == instance_name) { _iname[i].erase(); return; } } } int XrlProcessSpy::startup() { if (status() == SERVICE_READY || status() == SERVICE_SHUTDOWN) { send_register(0); set_status(SERVICE_STARTING); } return (XORP_OK); } void XrlProcessSpy::schedule_register_retry(uint32_t idx) { EventLoop& e = _rtr.eventloop(); _retry = e.new_oneoff_after_ms(100, callback(this, &XrlProcessSpy::send_register, idx)); } void XrlProcessSpy::send_register(uint32_t idx) { XrlFinderEventNotifierV0p1Client x(&_rtr); if (x.send_register_class_event_interest("finder", _rtr.instance_name(), _cname[idx], callback(this, &XrlProcessSpy::register_cb, idx)) == false) { XLOG_ERROR("Failed to send interest registration for \"%s\"\n", _cname[idx].c_str()); schedule_register_retry(idx); } } void XrlProcessSpy::register_cb(const XrlError& xe, uint32_t idx) { if (XrlError::OKAY() != xe) { XLOG_ERROR("Failed to register interest in \"%s\": %s\n", _cname[idx].c_str(), xe.str().c_str()); schedule_register_retry(idx); return; } debug_msg("Registered interest in %s\n", _cname[idx].c_str()); idx++; if (idx < END_IDX) { send_register(idx); } else { set_status(SERVICE_RUNNING); } } int XrlProcessSpy::shutdown() { if (status() == SERVICE_RUNNING) { send_deregister(0); set_status(SERVICE_SHUTTING_DOWN); } return (XORP_OK); } void XrlProcessSpy::schedule_deregister_retry(uint32_t idx) { EventLoop& e = _rtr.eventloop(); _retry = e.new_oneoff_after_ms(100, callback(this, &XrlProcessSpy::send_deregister, idx)); } void XrlProcessSpy::send_deregister(uint32_t idx) { XrlFinderEventNotifierV0p1Client x(&_rtr); if (x.send_deregister_class_event_interest( "finder", _rtr.instance_name(), _cname[idx], callback(this, &XrlProcessSpy::deregister_cb, idx)) == false) { XLOG_ERROR("Failed to send interest registration for \"%s\"\n", _cname[idx].c_str()); schedule_deregister_retry(idx); } } void XrlProcessSpy::deregister_cb(const XrlError& xe, uint32_t idx) { if (XrlError::OKAY() != xe) { XLOG_ERROR("Failed to deregister interest in \"%s\": %s\n", _cname[idx].c_str(), xe.str().c_str()); schedule_deregister_retry(idx); return; } debug_msg("Deregistered interest in %s\n", _cname[idx].c_str()); idx++; if (idx < END_IDX) { send_deregister(idx); } else { set_status(SERVICE_SHUTDOWN); } } xorp/rip/route_entry.cc0000664000076400007640000001636111540224234015344 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "rip_module.h" #include "libxorp/ipv4.hh" #include "libxorp/ipv6.hh" #include "libxorp/xlog.h" #include "route_entry.hh" template inline void RouteEntry::dissociate() { RouteEntryOrigin* o = _origin; _origin = 0; if (o) { o->dissociate(this); } } template inline void RouteEntry::associate(Origin* o) { if (o) o->associate(this); _origin = o; } template RouteEntry::RouteEntry(const Net& n, const Addr& nh, const string& ifname, const string& vifname, uint16_t cost, Origin*& o, uint16_t tag) : _net(n), _nh(nh), _ifname(ifname), _vifname(vifname), _cost(cost), _tag(tag), _ref_cnt(0), _filtered(false) { associate(o); } template RouteEntry::RouteEntry(const Net& n, const Addr& nh, const string& ifname, const string& vifname, uint16_t cost, Origin*& o, uint16_t tag, const PolicyTags& policytags) : _net(n), _nh(nh), _ifname(ifname), _vifname(vifname), _cost(cost), _tag(tag), _ref_cnt(0), _policytags(policytags), _filtered(false) { associate(o); } template RouteEntry::~RouteEntry() { Origin* o = _origin; _origin = 0; if (o) { o->dissociate(this); } } template bool RouteEntry::set_nexthop(const A& nh) { if (nh != _nh) { _nh = nh; // // XXX: If the nexthop is not link-local or zero, then reset // the interface and vif name. // Ideally, we shouldn't do this if the policy mechanism has // support for setting the interface and vif name. // For the time being it is safer to reset them and let the RIB // find the interface and vif name based on the common subnet // address to the nexthop. // if (! (_nh.is_linklocal_unicast() || _nh.is_zero())) { set_ifname(""); set_vifname(""); } return true; } return false; } template bool RouteEntry::set_ifname(const string& ifname) { if (ifname != _ifname) { _ifname = ifname; return true; } return false; } template bool RouteEntry::set_vifname(const string& vifname) { if (vifname != _vifname) { _vifname = vifname; return true; } return false; } template bool RouteEntry::set_cost(uint16_t cost) { if (cost != _cost) { _cost = cost; return true; } return false; } template bool RouteEntry::set_origin(Origin* o) { if (o != _origin) { dissociate(); associate(o); return true; } return false; } template bool RouteEntry::set_tag(uint16_t tag) { if (tag != _tag) { _tag = tag; return true; } return false; } template bool RouteEntry::set_policytags(const PolicyTags& ptags) { if (ptags != _policytags) { _policytags = ptags; return true; } return false; } /** * A comparitor for the purposes of sorting containers of RouteEntry objects. * It examines the data rather than using the address of pointers. The * latter approach makes testing difficult on different platforms since the * tests may inadvertantly make assumptions about the memory layout. The * comparison is arbitrary, it just has to be consistent and reversible. * * IFF speed proves to be an issue, RouteEntry can be changed to be an element * in an intrusive linked list that has a sentinel embedded in the * RouteEntryOrigin. */ template struct NetCmp { bool operator() (const IPNet& l, const IPNet& r) const { if (l.prefix_len() < r.prefix_len()) return true; if (l.prefix_len() > r.prefix_len()) return false; return l.masked_addr() < r.masked_addr(); } }; // ---------------------------------------------------------------------------- // RouteEntryOrigin::RouteEntryStore and related // // RouteEntryStore is a private class that is used by Route Origin's to store // their associated routes. // template struct RouteEntryOrigin::RouteEntryStore { public: typedef map, RouteEntry*, NetCmp > Container; Container routes; }; // ---------------------------------------------------------------------------- // RouteEntryOrigin template RouteEntryOrigin::RouteEntryOrigin(bool is_rib_origin) : _is_rib_origin(is_rib_origin) { _rtstore = new RouteEntryStore(); } template RouteEntryOrigin::~RouteEntryOrigin() { // XXX store should be empty XLOG_ASSERT(_rtstore->routes.empty()); delete _rtstore; } template bool RouteEntryOrigin::associate(Route* r) { XLOG_ASSERT(r != 0); if (_rtstore->routes.find(r->net()) != _rtstore->routes.end()) { XLOG_FATAL("entry already exists"); return false; } _rtstore->routes.insert(typename RouteEntryStore::Container::value_type(r->net(), r)); return true; } template bool RouteEntryOrigin::dissociate(Route* r) { typename RouteEntryStore::Container::iterator i = _rtstore->routes.find(r->net()); if (i == _rtstore->routes.end()) { XLOG_FATAL("entry does not exist"); return false; } _rtstore->routes.erase(i); return true; } template uint32_t RouteEntryOrigin::route_count() const { return static_cast(_rtstore->routes.size()); } template void RouteEntryOrigin::clear() { typename RouteEntryStore::Container::iterator i = _rtstore->routes.begin(); while (i != _rtstore->routes.end()) { Route* r = i->second; delete r; i = _rtstore->routes.begin(); } } template void RouteEntryOrigin::dump_routes(vector& routes) const { typename RouteEntryStore::Container::const_iterator i = _rtstore->routes.begin(); typename RouteEntryStore::Container::const_iterator end = _rtstore->routes.end(); while (i != end) { routes.push_back(i->second); ++i; } } template RouteEntry* RouteEntryOrigin::find_route(const Net& n) const { typename RouteEntryStore::Container::const_iterator i = _rtstore->routes.find(n); if (i == _rtstore->routes.end()) return 0; return i->second; } // ---------------------------------------------------------------------------- // Instantiations #ifdef INSTANTIATE_IPV4 template class RouteEntryOrigin; template class RouteEntry; #endif #ifdef INSTANTIATE_IPV6 template class RouteEntryOrigin; template class RouteEntry; #endif xorp/package_files/0000775000076400007640000000000011703344234014436 5ustar greearbgreearbxorp/package_files/xorp.conf0000664000076400007640000000012011421137511016261 0ustar greearbgreearb/*XORP Configuration File, v1.0*/ rtrmgr { config-directory: "/etc/xorp" } xorp/package_files/xorp.sysconfig0000664000076400007640000000017711421137511017354 0ustar greearbgreearb# /etc/sysconfig/xorp # # Specify boot config # BOOT_CONF="/etc/xorp/xorp.conf" # # Add extra daemon options here # OPTIONS="" xorp/package_files/xorp.ct.spec0000664000076400007640000001036711703344234016716 0ustar greearbgreearb# configure options %define with_shared 1 %define with_optimize 1 %define with_strip 1 %define prefixdir /usr/local/xorp Summary: An eXtensible Open Router Platform (XORP) Name: xorp Version: 1.8.5 Release: 1%{?dist}%{?pext} License: GPL Group: System Environment/Daemons Source0: xorp-%{version}.tar.lzma Source1: xorp.redhat Source2: xorp.sysconfig Source3: xorp.logrotate Source4: xorp.conf URL: http://www.xorp.org Buildroot: %{_tmppath}/xorp-%{version}-%{release}-root-%(%{__id_u} -n) Vendor: The XORP Team Requires: traceroute Requires(pre): /usr/sbin/groupadd Requires(post): /sbin/chkconfig Requires(preun): /sbin/chkconfig /sbin/service Requires(postun): /sbin/service BuildRequires: libpcap-devel ncurses-devel BuildRequires: scons openssl-devel python %description XORP is an extensible open-source routing platform. Designed for extensibility from the start, XORP provides a fully featured platform that implements IPv4 and IPv6 routing protocols and a unified platform to configure them. XORP's modular architecture allows rapid introduction of new protocols, features and functionality, including support for custom hardware and software forwarding. %prep %setup -q -n xorp %build scons -j4 \ DESTDIR=${RPM_BUILD_ROOT} \ sbindir=%{_sbindir} \ prefix=%{prefixdir} \ libexecdir=%{_libexecdir} \ sysconfdir=%{_sysconfdir} \ xorp_confdir=%{_sysconfdir} \ localstatedir=%{_localstatedir} \ %if %with_shared shared=yes \ %endif %if %with_optimize optimize=yes \ %endif %if %with_strip strip=yes \ %endif #scons %{?_smp_mflags} #%check #scons check %install [ -n "${RPM_BUILD_ROOT}" -a "${RPM_BUILD_ROOT}" != "/" ] && %{__rm} -rf ${RPM_BUILD_ROOT} %{__mkdir_p} ${RPM_BUILD_ROOT}%{_initrddir} %{__mkdir_p} ${RPM_BUILD_ROOT}%{_sysconfdir}/{logrotate.d,sysconfig,xorp} %{__mkdir_p} ${RPM_BUILD_ROOT}%{_sbindir} %{__mkdir_p} ${RPM_BUILD_ROOT}%{_datadir}/xorp scons \ DESTDIR=${RPM_BUILD_ROOT} \ sbindir=%{_sbindir} \ prefix=%{prefixdir} \ libexecdir=%{_libexecdir} \ sysconfdir=%{_sysconfdir} \ xorp_confdir=%{_sysconfdir} \ localstatedir=%{_localstatedir} \ %if %with_shared shared=yes \ %endif %if %with_optimize optimize=yes \ %endif %if %with_strip strip=yes \ %endif install %{__install} -m 0755 %{SOURCE1} ${RPM_BUILD_ROOT}%{_initrddir}/xorp %{__install} -m 0644 %{SOURCE2} ${RPM_BUILD_ROOT}%{_sysconfdir}/sysconfig/xorp %{__install} -m 0644 %{SOURCE3} ${RPM_BUILD_ROOT}%{_sysconfdir}/logrotate.d/xorp %{__install} -m 0660 %{SOURCE4} ${RPM_BUILD_ROOT}%{_sysconfdir}/xorp cd %{_builddir}/xorp %pre if ! getent group xorp >/dev/null 2>&1; then /usr/sbin/groupadd -r xorp /usr/sbin/usermod -G xorp root fi exit 0 # Always pass %post /sbin/chkconfig --add xorp %preun if [ $1 -eq 0 ]; then # Remove /sbin/service xorp stop >/dev/null 2>&1 /sbin/chkconfig --del xorp fi %postun if [ $1 -ge 1 ]; then # Upgrade /sbin/service xorp condrestart >/dev/null 2>&1 || : fi %clean [ -n "${RPM_BUILD_ROOT}" -a "${RPM_BUILD_ROOT}" != "/" ] && %{__rm} -rf ${RPM_BUILD_ROOT} %files %defattr(-,root,root) %doc BUGS BUILD_NOTES ERRATA LICENSE* %doc README RELEASE_NOTES VERSION %{_initrddir}/xorp %config(noreplace) %{_sysconfdir}/logrotate.d/xorp %config(noreplace) %{_sysconfdir}/sysconfig/xorp %attr(770,root,xorp) %dir %{_sysconfdir}/xorp %attr(660,root,xorp) %config(noreplace) %verify(not md5 size mtime) %{_sysconfdir}/xorp/xorp.conf %{_sbindir} %{prefixdir} %{_datadir} %changelog * Wed Jan 11 2011 Ben Greear 1.8.5 * Mon Sep 23 2011 Ben Greear 1.8.5-WIP * Mon Sep 20 2011 Ben Greear 1.8.4 * Tue Mar 16 2011 Ben Greear 1.8.3 * Sun Mar 14 2010 Achmad Basuki - ct-1.7-WIP.1 - Initial build of xorp.ct xorp/package_files/xorp.logrotate0000664000076400007640000000012611421137511017342 0ustar greearbgreearb/var/log/xorp.log { copytruncate notifempty missingok delaycompress } xorp/package_files/xorp.redhat0000775000076400007640000000625111421137511016621 0ustar greearbgreearb#!/bin/bash # # xorp This shell script takes care of starting and stopping # the Open Router Platform XORP. # # chkconfig: - 84 16 # description: XORP is the eXtensible Open Router Platform (XORP). # processname: xorp # config: /etc/xorp/xorp.conf # pidfile: /var/run/xorp.pid # Source function library. . /etc/rc.d/init.d/functions # Source networking configuration. . /etc/sysconfig/network # Check that networking is up. [ ${NETWORKING} = "no" ] && exit 1 # Defines DESC="the Open Router Platform (XORP)" PROG="xorp" EXEC="/usr/sbin/${PROG}" LOCK="/var/lock/subsys/${PROG}" LOGF="/var/log/${PROG}.log" PIDF="/var/run/${PROG}.pid" CONF="/etc/${PROG}/${PROG}.conf" # Include config if [ -s /etc/sysconfig/${PROG} ]; then . /etc/sysconfig/${PROG} fi # Further defines XORP_CONF="${BOOT_CONF:-${CONF}}" XORP_LOGF="${XORP_LOGF:-${LOGF}}" XORP_TMPL="${XORP_TMPL:-/usr/local/xorp/share/${PROG}/templates}" XORP_SHUT="${XORP_SHUT:-100}" # Check for binaries and configs [ -x ${EXEC} ] || exit 5 [ -f ${CONF} ] || exit 6 start() { # Start daemons. echo -n $"Starting ${DESC}: " daemon "${EXEC} -d -l ${XORP_LOGF} -P ${PIDF} -b ${XORP_CONF} -t ${XORP_TMPL} ${OPTIONS} >/dev/null" RETVAL=${?} [ ${RETVAL} -eq 0 ] && touch ${LOCK} echo return ${RETVAL} } stop() { # Stop daemons. echo -n $"Shutting down ${DESC}: " PID=`pidof -o %PPID ${PROG}` RETVAL=${?} [ ${RETVAL} -eq 0 ] && { kill -TERM ${PID} TIMEOUT=0 while pidof -o %PPID ${PROG} >/dev/null; do if [ ${TIMEOUT} -ge ${XORP_SHUT} ]; then RETVAL=1 break else sleep 5 && echo -n "." TIMEOUT=$((TIMEOUT+5)) fi done [ ${RETVAL} -eq 0 ] && rm -f ${LOCK} ${PIDF} } [ ${RETVAL} -eq 0 ] && success $"${PROG} shutdown" || failure $"${PROG} shutdown" echo return ${RETVAL} } force_stop () { # Force Stopping daemons. echo -n $"Force-Shutting down ${DESC}: " PCPID=`pgrep ${PROG}_` PID=`pidof -o %PPID ${PROG}` RETVAL=${?} [ ${RETVAL} -eq 0 ] && { kill -TERM ${PID} TIMEOUT=0 kill -TERM ${PCPID} while pidof -o %PPID ${PROG} >/dev/null; do if [ ${TIMEOUT} -ge ${XORP_SHUT} ]; then sleep 5 && echo -n "." TIMEOUT=$((TIMEOUT+5)) else sleep 5 && echo -n "." TIMEOUT=$((TIMEOUT+5)) fi done [ ${RETVAL} -eq 0 ] && rm -f ${LOCK} ${PIDF} } [ ${RETVAL} -eq 0 ] && success $"${PROG} shutdown" || failure $"${PROG} shutdown" echo return ${RETVAL} } restart() { stop sleep 2 start } force-stop() { force_stop } rh_status() { status ${PROG} } rh_status_q() { rh_status >/dev/null 2>&1 } # See how we were called. case "${1}" in start) rh_status_q && exit 0 ${1} ;; stop) rh_status_q || exit 0 ${1} ;; restart) ${1} ;; force-stop) rh_status_q || exit 0 ${1} ;; status) rh_status ;; condrestart|try-restart) rh_status_q || exit 0 force-stop start ;; *) echo $"Usage: service ${PROG} {start|stop|status|restart|try-restart|force-stop}" exit 2 esac exit ${?} xorp/vrrp/0000775000076400007640000000000011631510164012647 5ustar greearbgreearbxorp/vrrp/arpd.cc0000664000076400007640000000461111540225535014112 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2008-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "vrrp_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "arpd.hh" #include "vrrp_vif.hh" ARPd::ARPd(VrrpInterface& vif) : _vif(vif), _running(false), _receiving(false) { } ARPd::~ARPd() { if (_running) stop(); } void ARPd::start() { XLOG_ASSERT(!_running); _running = true; ips_updated(); } void ARPd::stop() { XLOG_ASSERT(_running); stop_receiving(); _running = false; } void ARPd::clear() { _ips.clear(); } void ARPd::insert(const IPv4& ip) { XLOG_ASSERT(_ips.find(ip) == _ips.end()); _ips.insert(ip); } void ARPd::ips_updated() { if (_ips.empty()) stop_receiving(); else start_receiving(); } void ARPd::start_receiving() { if (!_running || _receiving) return; _vif.start_arps(); _receiving = true; } void ARPd::stop_receiving() { if (!_running || !_receiving) return; _vif.stop_arps(); _receiving = false; } void ARPd::set_mac(const Mac& mac) { _mac = mac; } void ARPd::recv(const Mac& src, const PAYLOAD& payload) { if (!_receiving) return; if (payload.size() > sizeof(ArpHeader)) { XLOG_ERROR("ERROR: payload_size: %i is > than ArpHeader size: %i\n", (int)(payload.size()), (int)(sizeof(ArpHeader))); return; } ArpHeader ah(payload); if (!ah.is_request()) return; IPv4 ip = ah.get_request(); if (_ips.find(ip) == _ips.end()) return; PAYLOAD reply; ah.make_reply(reply, _mac); _vif.send(_mac, src, ETHERTYPE_ARP, reply); } xorp/vrrp/vrrp_target.cc0000664000076400007640000004071411540225535015527 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2008-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "vrrp_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/status_codes.h" #include "vrrp_target.hh" #include "vrrp_exception.hh" const string VrrpTarget::vrrp_target_name = "vrrp"; const string VrrpTarget::fea_target_name = "fea"; namespace { string vrid_error(const string& msg, const string& ifn, const string& vifn, uint32_t id) { ostringstream oss; oss << msg << " (ifname " << ifn << " vifname " << vifn << " vrid " << id << ")" ; return oss.str(); } } // anonymous namespace VrrpTarget::VrrpTarget(XrlRouter& rtr) : XrlVrrpTargetBase(&rtr), _rtr(rtr), _running(true), _ifmgr(rtr.eventloop(), fea_target_name.c_str(), rtr.finder_address(), rtr.finder_port()), _ifmgr_setup(false), _rawlink(&rtr), _rawipv4(&rtr), _fea(&rtr), _xrls_pending(0) { _ifmgr.attach_hint_observer(this); // When changing MAC, Linux brings the interface down and up. This will // cause VRRP to stop and start, which causes changing MACs yet again. To // avoid this loop, we delay configuration changes so we don't see the // interface going down on a MAC change. _ifmgr.delay_updates(TimeVal(1, 0)); start(); } VrrpTarget::~VrrpTarget() { shutdown(); } EventLoop& VrrpTarget::eventloop() { return _rtr.eventloop(); } void VrrpTarget::start() { if (_ifmgr.startup() != XORP_OK) xorp_throw(VrrpException, "Can't startup Vrrp"); } bool VrrpTarget::running() const { return _running || _rtr.pending() || _xrls_pending > 0; } void VrrpTarget::shutdown() { if (_running) { _ifmgr.detach_hint_observer(this); if (_ifmgr.shutdown() != XORP_OK) xorp_throw(VrrpException, "Can't shutdown fea mirror"); } for (IFS::iterator i = _ifs.begin(); i != _ifs.end(); ++i) { VIFS* v = i->second; for (VIFS::iterator j = v->begin(); j != v->end(); ++j) delete j->second; delete v; } _ifs.clear(); _running = false; } Vrrp& VrrpTarget::find_vrid(const string& ifn, const string& vifn, uint32_t id) { Vrrp* v = find_vrid_ptr(ifn, vifn, id); if (!v) xorp_throw(VrrpException, vrid_error("Cannot find", ifn, vifn, id)); return *v; } Vrrp* VrrpTarget::find_vrid_ptr(const string& ifn, const string& vifn, uint32_t id) { VrrpVif* x = find_vif(ifn, vifn); if (!x) return NULL; return x->find_vrid(id); } void VrrpTarget::add_vrid(const string& ifn, const string& vifn, uint32_t id) { if (find_vrid_ptr(ifn, vifn, id)) xorp_throw(VrrpException, vrid_error("Already exists", ifn, vifn, id)); VrrpVif* x = find_vif(ifn, vifn, true); XLOG_ASSERT(x); x->add_vrid(id); } void VrrpTarget::delete_vrid(const string& ifn, const string& vifn, uint32_t id) { Vrrp* v = find_vrid_ptr(ifn, vifn, id); if (!v) xorp_throw(VrrpException, vrid_error("Cannot find", ifn, vifn, id)); VrrpVif* x = find_vif(ifn, vifn); XLOG_ASSERT(x); x->delete_vrid(id); } VrrpVif* VrrpTarget::find_vif(const string& ifn, const string& vifn, bool add) { VIFS* v = NULL; VrrpVif* vif = NULL; bool added = false; IFS::iterator i = _ifs.find(ifn); if (i == _ifs.end()) { if (!add) return NULL; v = new VIFS; _ifs[ifn] = v; added = true; } else v = i->second; VIFS::iterator j = v->find(vifn); if (j == v->end()) { if (!add) return NULL; vif = new VrrpVif(*this, ifn, vifn); v->insert(make_pair(vifn, vif)); added = true; } else vif = j->second; if (added) check_interfaces(); return vif; } void VrrpTarget::tree_complete() { _ifmgr_setup = true; check_interfaces(); } void VrrpTarget::updates_made() { check_interfaces(); } void VrrpTarget::check_interfaces() { XLOG_ASSERT(_ifmgr_setup); for (IFS::iterator i = _ifs.begin(); i != _ifs.end(); ++i) { VIFS* vifs = i->second; for (VIFS::iterator j = vifs->begin(); j != vifs->end(); ++j) { VrrpVif* vif = j->second; vif->configure(_ifmgr.iftree()); } } } void VrrpTarget::send(const string& ifname, const string& vifname, const Mac& src, const Mac& dest, uint32_t ether, const PAYLOAD& payload) { VrrpVif* vif = find_vif(ifname, vifname); XLOG_ASSERT(vif); bool rc = _rawlink.send_send(fea_target_name.c_str(), ifname, vifname, src, dest, ether, payload, callback(vif, &VrrpVif::xrl_cb)); if (!rc) XLOG_FATAL("Cannot send raw data"); } void VrrpTarget::join_mcast(const string& ifname, const string& vifname) { bool rc; XrlRawPacket4V0p1Client::RegisterReceiverCB cb = callback(this, &VrrpTarget::xrl_cb); uint32_t proto = IPPROTO_VRRP; const IPv4& ip = VrrpPacket::mcast_group; rc = _rawipv4.send_register_receiver(fea_target_name.c_str(), _rtr.instance_name(), ifname, vifname, proto, false, cb); if (!rc) { XLOG_FATAL("Cannot register receiver"); return; } _xrls_pending++; rc = _rawipv4.send_join_multicast_group(fea_target_name.c_str(), _rtr.instance_name(), ifname, vifname, proto, ip, cb); if (!rc) XLOG_FATAL("Cannot join mcast group"); _xrls_pending++; } void VrrpTarget::leave_mcast(const string& ifname, const string& vifname) { bool rc; XrlRawPacket4V0p1Client::RegisterReceiverCB cb = callback(this, &VrrpTarget::xrl_cb); uint32_t proto = IPPROTO_VRRP; const IPv4& ip = VrrpPacket::mcast_group; rc = _rawipv4.send_leave_multicast_group(fea_target_name.c_str(), _rtr.instance_name(), ifname, vifname, proto, ip, cb); if (!rc) { XLOG_FATAL("Cannot leave mcast group"); return; } _xrls_pending++; rc = _rawipv4.send_unregister_receiver(fea_target_name.c_str(), _rtr.instance_name(), ifname, vifname, proto, cb); if (!rc) XLOG_FATAL("Cannot unregister receiver"); _xrls_pending++; } void VrrpTarget::start_arps(const string& ifname, const string& vifname) { string filter; if (!_rawlink.send_register_receiver(fea_target_name.c_str(), _rtr.instance_name(), ifname, vifname, ETHERTYPE_ARP, filter, false, callback(this, &VrrpTarget::xrl_cb))) XLOG_FATAL("Cannot register arp receiver"); _xrls_pending++; } void VrrpTarget::stop_arps(const string& ifname, const string& vifname) { string filter; if (!_rawlink.send_unregister_receiver(fea_target_name.c_str(), _rtr.instance_name(), ifname, vifname, ETHERTYPE_ARP, filter, callback(this, &VrrpTarget::xrl_cb))) XLOG_FATAL("Cannot unregister arp receiver"); _xrls_pending++; } void VrrpTarget::add_mac(const string& ifname, const Mac& mac) { if (!_fea.send_create_mac(fea_target_name.c_str(), ifname, mac, callback(this, &VrrpTarget::xrl_cb))) XLOG_FATAL("Cannot add MAC"); _xrls_pending++; } void VrrpTarget::add_ip(const string& ifname, const IPv4& ip, const uint32_t prefix) { if (!_fea.send_create_address_atomic(fea_target_name.c_str(), ifname, ifname, ip, prefix, callback(this, &VrrpTarget::xrl_cb))) XLOG_FATAL("Cannot add IP"); _xrls_pending++; } void VrrpTarget::delete_mac(const string& ifname, const Mac& mac) { if (!_fea.send_delete_mac(fea_target_name.c_str(), ifname, mac, callback(this, &VrrpTarget::xrl_cb))) XLOG_FATAL("Cannot delete MAC"); _xrls_pending++; } void VrrpTarget::delete_ip(const string& ifname, const IPv4& ip) { if (!_fea.send_delete_address_atomic(fea_target_name.c_str(), ifname, ifname, ip, callback(this, &VrrpTarget::xrl_cb))) XLOG_FATAL("Cannot delete IP"); _xrls_pending++; } void VrrpTarget::xrl_cb(const XrlError& xrl_error) { _xrls_pending--; XLOG_ASSERT(_xrls_pending >= 0); if (xrl_error != XrlError::OKAY()) XLOG_FATAL("XRL error: %s", xrl_error.str().c_str()); } XrlCmdError VrrpTarget::common_0_1_get_target_name( // Output values, string& name) { name = vrrp_target_name; return XrlCmdError::OKAY(); } XrlCmdError VrrpTarget::common_0_1_get_version( // Output values, string& version) { version = XORP_MODULE_VERSION; return XrlCmdError::OKAY(); } XrlCmdError VrrpTarget::common_0_1_get_status( // Output values, uint32_t& status, string& reason) { if (_running) { status = PROC_READY; reason = "running"; } else { status = PROC_SHUTDOWN; reason = "dying"; } return XrlCmdError::OKAY(); } XrlCmdError VrrpTarget::common_0_1_shutdown() { shutdown(); return XrlCmdError::OKAY(); } XrlCmdError VrrpTarget::common_0_1_startup() { start(); return XrlCmdError::OKAY(); } XrlCmdError VrrpTarget::vrrp_0_1_add_vrid( // Input values, const string& ifname, const string& vifname, const uint32_t& vrid) { try { add_vrid(ifname, vifname, vrid); } catch(const VrrpException& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } return XrlCmdError::OKAY(); } XrlCmdError VrrpTarget::vrrp_0_1_delete_vrid( // Input values, const string& ifname, const string& vifname, const uint32_t& vrid) { try { delete_vrid(ifname, vifname, vrid); } catch(const VrrpException& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } return XrlCmdError::OKAY(); } XrlCmdError VrrpTarget::vrrp_0_1_set_priority( // Input values, const string& ifname, const string& vifname, const uint32_t& vrid, const uint32_t& priority) { try { Vrrp& v = find_vrid(ifname, vifname, vrid); v.set_priority(priority); } catch(const VrrpException& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } return XrlCmdError::OKAY(); } XrlCmdError VrrpTarget::vrrp_0_1_set_interval( // Input values, const string& ifname, const string& vifname, const uint32_t& vrid, const uint32_t& interval) { try { Vrrp& v = find_vrid(ifname, vifname, vrid); v.set_interval(interval); } catch(const VrrpException& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } return XrlCmdError::OKAY(); } XrlCmdError VrrpTarget::vrrp_0_1_set_preempt( // Input values, const string& ifname, const string& vifname, const uint32_t& vrid, const bool& preempt) { try { Vrrp& v = find_vrid(ifname, vifname, vrid); v.set_preempt(preempt); } catch(const VrrpException& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } return XrlCmdError::OKAY(); } XrlCmdError VrrpTarget::vrrp_0_1_set_disable( // Input values, const string& ifname, const string& vifname, const uint32_t& vrid, const bool& disable) { try { Vrrp& v = find_vrid(ifname, vifname, vrid); v.set_disable(disable); } catch(const VrrpException& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } return XrlCmdError::OKAY(); } XrlCmdError VrrpTarget::vrrp_0_1_add_ip( // Input values, const string& ifname, const string& vifname, const uint32_t& vrid, const IPv4& ip) { try { Vrrp& v = find_vrid(ifname, vifname, vrid); v.add_ip(ip); } catch(const VrrpException& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } return XrlCmdError::OKAY(); } XrlCmdError VrrpTarget::vrrp_0_1_set_prefix ( // Input values, const string& ifname, const string& vifname, const uint32_t& vrid, const IPv4& ip, const uint32_t& prefix_len) { try { Vrrp& v = find_vrid(ifname, vifname, vrid); v.set_prefix(ip, prefix_len); } catch(const VrrpException& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } return XrlCmdError::OKAY(); } XrlCmdError VrrpTarget::vrrp_0_1_delete_ip( // Input values, const string& ifname, const string& vifname, const uint32_t& vrid, const IPv4& ip) { try { Vrrp& v = find_vrid(ifname, vifname, vrid); v.delete_ip(ip); } catch(const VrrpException& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } return XrlCmdError::OKAY(); } XrlCmdError VrrpTarget::vrrp_0_1_get_vrid_info( // Input values, const string& ifname, const string& vifname, const uint32_t& vrid, // Output values, string& state, IPv4& master) { try { Vrrp& v = find_vrid(ifname, vifname, vrid); v.get_info(state, master); } catch(const VrrpException& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } return XrlCmdError::OKAY(); } XrlCmdError VrrpTarget::vrrp_0_1_get_vrids( // Input values, const string& ifname, const string& vifname, // Output values, XrlAtomList& vrids) { try { VrrpVif* vif = find_vif(ifname, vifname); if (!vif) xorp_throw(VrrpException, "unknown vif"); typedef VrrpVif::VRIDS VRIDS; VRIDS tmp; vif->get_vrids(tmp); for (VRIDS::iterator i = tmp.begin(); i != tmp.end(); ++i) { uint32_t vrid = *i; vrids.append(XrlAtom(vrid)); } } catch(const VrrpException& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } return XrlCmdError::OKAY(); } XrlCmdError VrrpTarget::vrrp_0_1_get_ifs( // Output values, XrlAtomList& ifs) { for (IFS::iterator i = _ifs.begin(); i != _ifs.end(); ++i) ifs.append(XrlAtom(i->first)); return XrlCmdError::OKAY(); } XrlCmdError VrrpTarget::vrrp_0_1_get_vifs( // Input values, const string& ifname, // Output values, XrlAtomList& vifs) { IFS::iterator i = _ifs.find(ifname); if (i == _ifs.end()) return XrlCmdError::COMMAND_FAILED("Can't find interface"); VIFS* v = i->second; for (VIFS::iterator i = v->begin(); i != v->end(); ++i) vifs.append(XrlAtom(i->first)); return XrlCmdError::OKAY(); } XrlCmdError VrrpTarget::raw_packet4_client_0_1_recv( // Input values, const string& if_name, const string& vif_name, const IPv4& src_address, const IPv4& dst_address, const uint32_t& ip_protocol, const int32_t& ip_ttl, const int32_t& ip_tos, const bool& ip_router_alert, const bool& ip_internet_control, const vector& payload) { UNUSED(ip_tos); UNUSED(ip_router_alert); UNUSED(ip_internet_control); VrrpVif* vif = find_vif(if_name, vif_name); if (!vif) { XLOG_WARNING("Cannot find IF %s VIF %s", if_name.c_str(), vif_name.c_str()); return XrlCmdError::OKAY(); } if (dst_address != VrrpPacket::mcast_group) { XLOG_WARNING("Received stuff for unknown IP %s", dst_address.str().c_str()); return XrlCmdError::OKAY(); } if (ip_protocol != IPPROTO_VRRP) { XLOG_WARNING("Unknown protocol %u", ip_protocol); return XrlCmdError::OKAY(); } if (ip_ttl != 255) { XLOG_WARNING("Bad TTL %d", ip_ttl); return XrlCmdError::OKAY(); } vif->recv(src_address, payload); return XrlCmdError::OKAY(); } XrlCmdError VrrpTarget::raw_link_client_0_1_recv( // Input values, const string& if_name, const string& vif_name, const Mac& src_address, const Mac& dst_address, const uint32_t& ether_type, const vector& payload) { VrrpVif* vif = find_vif(if_name, vif_name); if (!vif) { XLOG_WARNING("Can't find VIF %s", if_name.c_str()); return XrlCmdError::OKAY(); } if (ether_type != ETHERTYPE_ARP) { XLOG_WARNING("Unknown ethertype %u", ether_type); return XrlCmdError::OKAY(); } // only arp requests for now if (dst_address != Mac::BROADCAST()) return XrlCmdError::OKAY(); vif->recv_arp(src_address, payload); return XrlCmdError::OKAY(); } xorp/vrrp/vrrp_vif.cc0000664000076400007640000001513311540225535015022 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2008-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "vrrp_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxipc/xrl_error.hh" #include "vrrp_vif.hh" #include "vrrp.hh" #include "vrrp_target.hh" #include "vrrp_exception.hh" VrrpVif::VrrpVif(VrrpTarget& vt, const string& ifname, const string& vifname) : _vt(vt), _ifname(ifname), _vifname(vifname), _ready(false), _join(0), _arps(0) { } VrrpVif::~VrrpVif() { for (VRRPS::iterator i = _vrrps.begin(); i != _vrrps.end(); ++i) delete i->second; } bool VrrpVif::own(const IPv4& addr) { return _ips.find(addr) != _ips.end(); } Vrrp* VrrpVif::find_vrid(uint32_t vrid) { VRRPS::iterator i = _vrrps.find(vrid); if (i == _vrrps.end()) return NULL; return i->second; } void VrrpVif::add_vrid(uint32_t vrid) { XLOG_ASSERT(find_vrid(vrid) == NULL); _vrrps[vrid] = new Vrrp(*this, _vt.eventloop(), vrid); } void VrrpVif::delete_vrid(uint32_t vrid) { Vrrp* v = find_vrid(vrid); XLOG_ASSERT(v); _vrrps.erase(vrid); delete v; } bool VrrpVif::ready() const { return _ready; } void VrrpVif::configure(const IfMgrIfTree& conf) { // check interface const IfMgrIfAtom* ifa = conf.find_interface(_ifname); if (!is_enabled(ifa)) return; // check vif const IfMgrVifAtom* vifa = ifa->find_vif(_vifname); if (!is_enabled(vifa)) return; // check addresses _ips.clear(); const IfMgrVifAtom::IPv4Map& addrs = vifa->ipv4addrs(); for (IfMgrVifAtom::IPv4Map::const_iterator i = addrs.begin(); i != addrs.end(); ++i) { const IfMgrIPv4Atom& addr = i->second; if (addr.enabled()) { XLOG_WARNING("vif: %s/%s configured with IP: %s\n", _ifname.c_str(), _vifname.c_str(), addr.toString().c_str()); _ips.insert(addr.addr()); } } if (_ips.empty()) { set_ready(false); return; } set_ready(true); } template bool VrrpVif::is_enabled(const T* obj) { if (obj && obj->enabled()) return true; set_ready(false); return false; } void VrrpVif::set_ready(bool ready) { if (ready) _ready = ready; for (VRRPS::iterator i = _vrrps.begin(); i != _vrrps.end(); ++i) { Vrrp* v = i->second; if (ready) { //v->check_ownership(); v->start(); } else v->stop(); } _ready = ready; } const IPv4& VrrpVif::addr() const { XLOG_ASSERT(_ips.size()); // XXX we should use first configured address - not the lowest. return *(_ips.begin()); } void VrrpVif::send(const Mac& src, const Mac& dst, uint32_t ether, const PAYLOAD& payload) { XLOG_ASSERT(ready()); _vt.send(_ifname, _vifname, src, dst, ether, payload); } void VrrpVif::join_mcast() { _join++; XLOG_ASSERT(_join); if (_join == 1) _vt.join_mcast(_ifname, _vifname); } void VrrpVif::leave_mcast() { XLOG_ASSERT(_join); _join--; if (_join > 0) return; _vt.leave_mcast(_ifname, _vifname); // paranoia int cnt = 0; for (VRRPS::iterator i = _vrrps.begin(); i != _vrrps.end(); ++i) { Vrrp* v = i->second; if (v->running()) XLOG_ASSERT(++cnt == 1); } } void VrrpVif::start_arps() { _arps++; XLOG_ASSERT(_arps); if (_arps == 1) _vt.start_arps(_ifname, _vifname); } void VrrpVif::stop_arps() { XLOG_ASSERT(_arps); _arps--; if (_arps > 0) return; _vt.stop_arps(_ifname, _vifname); } void VrrpVif::recv(const IPv4& from, const PAYLOAD& payload) { try { const VrrpHeader& vh = VrrpHeader::assign(payload); Vrrp* v = find_vrid(vh.vh_vrid); if (!v) { // This is a normal and common occurance if there are // lots of different VRRP protocol groups running on // the LAN. Don't be noisy about it. //XLOG_WARNING("Cannot find VRID %d", vh.vh_vrid); return; } v->recv(from, vh); } catch (const VrrpException& e) { XLOG_WARNING("VRRP packet error: %s", e.str().c_str()); } } void VrrpVif::recv_arp(const Mac& from, const PAYLOAD& payload) { UNUSED(from); UNUSED(payload); #if 0 // XXX the arp object should be part of VrrpVif for (VRRPS::iterator i = _vrrps.begin(); i != _vrrps.end(); ++i) { ARPd& arp = i->second->arpd(); try { arp.recv(from, payload); } catch (const BadPacketException& e) { XLOG_WARNING("ARP packet error: %s", e.str().c_str()); break; } } #endif } void VrrpVif::add_mac(const Mac& mac) { // XXX mac operations are per physical interface. We musn't have multiple // vifs tweaking the mac of the same physical inteface. This is a // rudimentary (and very conservative) way to check this. Now all of VRRP // works on a "per vif" basis, but that may be wrong - it might need to work // on a "per if" basis. I still don't quite know the relationship, in XORP // terms, between if and vif. (If it's like Linux's virtual IPs and eth0:X // notation, then a vif isn't much more than an extra IP, and we should be // working on a "per if" basis.) -sorbo XLOG_ASSERT(_ifname == _vifname); _vt.add_mac(_ifname, mac); } void VrrpVif::add_ip(const IPv4& ip, uint32_t prefix) { XLOG_ASSERT(_ifname == _vifname); _vt.add_ip(_ifname, ip, prefix); } void VrrpVif::delete_mac(const Mac& mac) { // XXX see add mac XLOG_ASSERT(_ifname == _vifname); _vt.delete_mac(_ifname, mac); } void VrrpVif::delete_ip(const IPv4& ip) { // XXX see add mac XLOG_ASSERT(_ifname == _vifname); _vt.delete_ip(_ifname, ip); } void VrrpVif::get_vrids(VRIDS& vrids) { for (VRRPS::iterator i = _vrrps.begin(); i != _vrrps.end(); ++i) vrids.insert(i->first); } void VrrpVif::xrl_cb(const XrlError& xrl_error) { if (xrl_error == XrlError::OKAY()) return; XLOG_WARNING("Error on interface %s:%s - %s\n", _ifname.c_str(), _vifname.c_str(), xrl_error.str().c_str()); set_ready(false); } xorp/vrrp/vrrp_packet.hh0000664000076400007640000001227511540225535015523 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2008-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/vrrp/vrrp_packet.hh,v 1.5 2008/12/18 11:34:51 abittau Exp $ #ifndef __VRRP_VRRP_PACKET_HH__ #define __VRRP_VRRP_PACKET_HH__ #include "libxorp/ipv4.hh" #include "libproto/packet.hh" typedef vector PAYLOAD; /** * @short The VRRP header. */ struct VrrpHeader { enum Versions { VRRP_VERSION = 2 }; enum PktTypes { VRRP_TYPE_ADVERTISEMENT = 1 }; enum AuthTypes { VRRP_AUTH_NONE = 0 }; /** * Create a new VRRP packet. Caller must allocate memory and assert size * (VRRP_MAX_PACKET_SIZE). * * @return the VRRP header. * @param data pointer where packet should be stored. */ static VrrpHeader& assign(uint8_t* data); /** * Parse a VRRP packet. * * @return the VRRP header. * @param payload The VRRP packet starting with the VRRP header. */ static const VrrpHeader& assign(const PAYLOAD& payload); /** * Must be called when all fields have been manipulated. This will setup * the final bits of information (e.g., checksum) and the packet will become * ready to be sent. * * @return the length of the packet. */ uint32_t finalize(); /** * Add an IP address of the virtual router to the advertisement. * * @param ip IP address to add to the advertisement. */ void add_ip(const IPv4& ip); /** * Extract an IP address from the advertisement. * * @return the IP address at the specified index. * @param index the index of the IP (0..vh_ipcount). */ IPv4 ip(unsigned index) const; #if __BYTE_ORDER == __BIG_ENDIAN uint8_t vh_v:4; uint8_t vh_type:4; #elif __BYTE_ORDER == __LITTLE_ENDIAN uint8_t vh_type:4; uint8_t vh_v:4; #else #error "Unknown endianness" #endif uint8_t vh_vrid; uint8_t vh_priority; uint8_t vh_ipcount; uint8_t vh_auth; uint8_t vh_interval; uint16_t vh_sum; struct in_addr vh_addr[0]; }; /** * @short VRRP authentication data. Unused in RFC 3768. */ struct VrrpAuth { uint8_t va_data[8]; }; #define IP_HEADER_MIN_SIZE 20 #define VRRP_MAX_PACKET_SIZE (IP_HEADER_MIN_SIZE \ + sizeof(VrrpHeader) + sizeof(VrrpAuth) \ + sizeof(struct in_addr) * 255) /** * @short A VRRP packet including the IP header. */ class VrrpPacket { public: static const IPv4 mcast_group; VrrpPacket(); /** * Set the source IP address in the IP header. * * @param ip source IP address in IP header. */ void set_source(const IPv4& ip); /** * Set the virtual router ID in the VRRP header. * * @param vrid the virtual router ID in the VRRP header. */ void set_vrid(uint8_t vrid); /** * Set the priority in the VRRP header. * * @param priority the router priority in the VRRP header. */ void set_priority(uint8_t priority); /** * Set the advertisement interval in VRRP's header. * * @param interval the advertisement interval in VRRP's header. */ void set_interval(uint8_t interval); /** * Remove all IPs from the VRRP advertisement. */ void clear_ips(); /** * Add an IP to the VRRP header. * * @param ip IP to add to the virtual router in the VRRP header. */ void add_ip(const IPv4& ip); /** * Must be called when all fields are set. This method will finalize any * remaining fields such as checksums. */ void finalize(); /** * Get the packet data. * * @return the packet data (IP and VRRP). */ const PAYLOAD& data() const; /** * Get the packet size. * * @return packet size. */ uint32_t size() const; /** * Set the packet size. * * @param packet size. */ void set_size(uint32_t size); /** * Set multiple IPs from a container into the VRRP header. * * @param ips collection of IP addresses to add to the VRRP header. */ template void set_ips(const T& ips) { clear_ips(); for (typename T::const_iterator i = ips.begin(); i != ips.end(); ++i) add_ip(*i); } private: PAYLOAD _data; IpHeader4Writer _ip; VrrpHeader& _vrrp; }; #endif // __VRRP_VRRP_PACKET_HH__ xorp/vrrp/arpd.hh0000664000076400007640000000563511540224236014130 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2008-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/vrrp/arpd.hh,v 1.5 2008/12/18 11:34:51 abittau Exp $ #ifndef __VRRP_ARPD_HH__ #define __VRRP_ARPD_HH__ #include "libxorp/ipv4.hh" #include "vrrp_packet.hh" #include "vrrp_interface.hh" /** * @short an ARP daemon. * * This daemon can be configured to "own" several IPs for which it will send out * ARP replies when receiving ARP requests. */ class ARPd { public: /** * @param vif the VRRP interface on which the daemon runs. */ ARPd(VrrpInterface& vif); ~ARPd(); /** * Remove all configured IPs. */ void clear(); /** * Add an IP for which ARP replies should be sent. * * @param ip the IP to add. */ void insert(const IPv4& ip); /** * Stop the daemon. */ void stop(); /** * Start the daemon. */ void start(); /** * Using this method the caller notifies ARPd that it has finished * manipulating the IP addresses. This way one can clear and add IPs one by * one without causing ARPd to stop (if IPs are cleared) and resume if IPs * are added. */ void ips_updated(); /** * This method notifies the reception of an ARP packet. * * @param mac the source MAC address of the packet. * @param payload the ARP header and data. */ void recv(const Mac& src, const PAYLOAD& payload); /** * Sets the MAC address of the ARP daemon, used when generating replies. * * @param mac the MAC address. */ void set_mac(const Mac& mac); private: typedef set IPS; /** * Use this to notify the interface that we no longer need to receive * packets. This can be used for example when no IPs are configured or when * the ARPd has been stopped. */ void start_receiving(); /** * Notify the interface that we desire to receive ARP packets. */ void stop_receiving(); VrrpInterface& _vif; Mac _mac; IPS _ips; bool _running; bool _receiving; }; #endif // __VRRP_ARPD_HH__ xorp/vrrp/vrrp_interface.hh0000664000076400007640000000662711540225535016220 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2008-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/vrrp/vrrp_interface.hh,v 1.4 2008/12/18 11:34:51 abittau Exp $ #ifndef __VRRP_VRRP_INTERFACE_HH__ #define __VRRP_VRRP_INTERFACE_HH__ #include "libxorp/ipv4.hh" #include "libxorp/mac.hh" #include "vrrp_packet.hh" /** * @short A network interface on which VRRP runs. * * This interface provides the means via which VRRP receives and send packets. */ class VrrpInterface { public: virtual ~VrrpInterface() {} /** * Determines whether the router owns an IP address. If the router has this * IP address configured as the real IP address of one of its interfaces, * then it owns it, else it does not. * * @return whether the router owns the IP address. * @param addr the IP address to check for. */ virtual bool own(const IPv4& addr) = 0; /** * Check whether the network interface is up. * * @return whether the interface is up and ready. */ virtual bool ready() const = 0; /** * Obtain the primary IP address of the interface. This is the first one * configured. * * @return the primary IP address of the interface. The first configured. */ virtual const IPv4& addr() const = 0; /** * Transmits a L2 packet. * * @param src the source MAC address. * @param dst the destination MAC address. * @param ether the ethernet type. * @param payload the data following the MAC header. */ virtual void send(const Mac& src, const Mac& dst, uint32_t ether, const PAYLOAD& payload) = 0; /** * Join the VRRP multicast group on this interface. */ virtual void join_mcast() = 0; /** * Leave the VRRP multicast group on this interface. */ virtual void leave_mcast() = 0; /** * Add a MAC address to this interface. * * @param mac MAC address to add. */ virtual void add_mac(const Mac& mac) = 0; virtual void add_ip(const IPv4& ip) = 0; /** * Delete a MAC address from this interface. * * @param mac MAC address to delete from this interface. */ virtual void delete_mac(const Mac& mac) = 0; virtual void delete_ip(const IPv4& ip) = 0; // XXX these should be handled elsewhere, or differently. VRRP doesn't have // to "know" about ARPs. /** * Start the reception of ARP packets. */ virtual void start_arps() = 0; /** * Stop the reception of ARP packets. */ virtual void stop_arps() = 0; }; #endif // __VRRP_VRRP_INTERFACE_HH__ xorp/vrrp/SConscript0000664000076400007640000000541511631510164014666 0ustar greearbgreearb# Copyright (c) 2009-2011 XORP, Inc and Others # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # $XORP$ import os Import('env') # XXX Tests don't follow Autotools-style regression test format. # #subdirs = [ 'tests' ] # #SConscript(dirs = subdirs, exports='env') env = env.Clone() is_shared = env.has_key('SHAREDLIBS') env.AppendUnique(CPPPATH = [ '#', '$BUILDDIR', ]) env.PrependUnique(LIBPATH = [ '$BUILDDIR/libxorp', '$BUILDDIR/libcomm', '$BUILDDIR/libxipc', '$BUILDDIR/libproto', '$BUILDDIR/libfeaclient', '$BUILDDIR/xrl/interfaces', '$BUILDDIR/xrl/targets', '.' ]) env.AppendUnique(LIBS = [ 'xorp_vrrp', 'xst_vrrp', 'xorp_fea_client', 'xif_fea_ifmgr', 'xif_fea_ifmgr_mirror', 'xif_fea_ifmgr_replicator', 'xif_fea_rawlink', 'xif_fea_rawpkt4', 'xst_fea_ifmgr_mirror', 'xorp_ipc', 'xorp_core', 'xorp_proto', 'xorp_comm' ]) if (env.has_key('mingw') and env['mingw']): env.AppendUnique(LIBS = [ # 'mprapi', 'regex', ]) env.Append(LIBS = ['xorp_core', 'crypto', 'ws2_32', 'iphlpapi', 'winmm']) libxorp_vrrp_srcs = ['arpd.cc', 'vrrp.cc', 'vrrp_packet.cc', 'vrrp_target.cc', 'vrrp_vif.cc' ] env.Replace(RPATH = [ env.Literal(env['xorp_module_rpath']) ]) if is_shared: libxorp_vrrp = env.SharedLibrary(target = 'libxorp_vrrp', source = libxorp_vrrp_srcs, LIBS = '') if env['rtld_origin']: for obj in libxorp_vrrp: env.AddPostAction(libxorp_vrrp, env.Symlink(obj.abspath, os.path.join(env['xorp_alias_libdir'], str(obj)))) else: libxorp_vrrp = env.StaticLibrary(target = 'libxorp_vrrp', source = libxorp_vrrp_srcs, LIBS = '') vrrpsources = [ 'xorp_vrrp.cc' ] xorp_vrrp = env.Program(target = 'xorp_vrrp', source = vrrpsources) env.Alias('install', env.InstallProgram(env['xorp_moduledir'], xorp_vrrp)) if is_shared: env.Alias('install', env.InstallLibrary(env['xorp_libdir'], libxorp_vrrp)) Default(xorp_vrrp) xorp/vrrp/xorp_vrrp.cc0000664000076400007640000000326511421137511015223 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2008-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "vrrp_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/eventloop.hh" #include "libxipc/xrl_std_router.hh" #include "vrrp_target.hh" #include "vrrp_exception.hh" static void start() { EventLoop e; XrlStdRouter rtr(e, VrrpTarget::vrrp_target_name.c_str(), FinderConstants::FINDER_DEFAULT_HOST().str().c_str()); VrrpTarget vrrp(rtr); wait_until_xrl_router_is_ready(e, rtr); while (vrrp.running()) e.run(); } int main(int argc, char* argv[]) { UNUSED(argc); xlog_init(argv[0], 0); xlog_set_verbose(XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); try { start(); } catch(const VrrpException& e) { XLOG_FATAL("VrrpException: %s", e.str().c_str()); } xlog_stop(); xlog_exit(); exit(0); } xorp/vrrp/vrrp_vif.hh0000664000076400007640000001312311540225535015031 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2008-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/vrrp/vrrp_vif.hh,v 1.10 2008/12/18 11:34:52 abittau Exp $ #ifndef __VRRP_VRRP_VIF_HH__ #define __VRRP_VRRP_VIF_HH__ #include "libxorp/ipv4.hh" #include "libfeaclient/ifmgr_atoms.hh" #include "libxipc/xrl_error.hh" //#include "vrrp.hh" //#include "vrrp_interface.hh" class VrrpTarget; class Vrrp; /** * @short Implementation of a VRRP network interface. * * This class links the VRRP state machine to the actual network. */ class VrrpVif { public: typedef set VRIDS; /** * @param vt the VRRP target. * @param ifname the name of the physical interface. * @param vifname the name of the logical interface. */ VrrpVif(VrrpTarget& vt, const string& ifname, const string& vifname); ~VrrpVif(); VrrpTarget& get_vrrp_target() { return _vt; } /** * Check whether an IP address is configured on this interface. * * @return whether the given IP address is configured on the interface. * @param addr the IP address tocheck for. */ bool own(const IPv4& addr); /** * Look for a VRRP instance on this interface. * * @return a VRRP instance configured on this interface. * @param vrid the VRRP ID to look for. */ Vrrp* find_vrid(uint32_t vrid); /** * Add a VRRP instance on this interface. * * @param vrid the router ID of this VRRP instance. */ void add_vrid(uint32_t vrid); /** * Delete a VRRP instance from this interface. * * @param vrid the router ID of the instance. */ void delete_vrid(uint32_t vrid); /** * Check whether the interface is up. * * @return whether the interface is enabled. */ bool ready() const; /** * Change the interface's configuration. * * @param conf the new configuration of the interface. */ void configure(const IfMgrIfTree& conf); /** * Obtain the interface's primary IP address. * * @return the primary IP address. */ const IPv4& addr() const; /** * Send a L2 packet. * * @param src the source MAC address. * @param dst the destination MAC address. * @param ether the Ethernet type. * @param payload the data following the MAC header. */ void send(const Mac& src, const Mac& dst, uint32_t ether, const vector& payload); /** * Join the VRRP multicast group. */ void join_mcast(); /** * Leave the VRRP multicast group. */ void leave_mcast(); /** * Receive an IP packet. * * @param from the source IP address. * @param payload the IP payload. */ void recv(const IPv4& from, const vector& payload); /** * Add a MAC address to this interface. * * @param mac MAC address to add. */ void add_mac(const Mac& mac); void add_ip(const IPv4& ip, uint32_t prefix); /** * Delete a MAC address from this interface. * * @param mac MAC address to remove. */ void delete_mac(const Mac& mac); void delete_ip(const IPv4& ip); /** * Start the reception of ARP packets. */ void start_arps(); /** * Stop the reception of ARP packets. */ void stop_arps(); /** * Notify the reception of an ARP packet. * * @param src the source MAC address. * @param payload the ARP header and data. */ void recv_arp(const Mac& src, const vector& payload); /** * Obtain a list of VRRP instance configured on this interface. * * @param vrids the VRRP instances on this interface (output parameter). */ void get_vrids(VRIDS& vrids); /** * Callback on XRL error caused by this interface. * * @param xrl_error the error (if any). */ void xrl_cb(const XrlError& xrl_error); private: typedef set IPS; typedef map VRRPS; /** * Enable or disable the interface. This will start / stop all VRRP * instances running on this interface. * * @param ready if true, enable the interface, otherwise disable it. */ void set_ready(bool ready); /** * Check whether a rtrmgr configuration element (e.g., an interface) is * enabled. * * @return true if the element is enabled, false otherwise. * @param obj element to check. */ template bool is_enabled(const T* obj); VrrpTarget& _vt; string _ifname; string _vifname; bool _ready; // is it configured? IPS _ips; // IPs assigned to this vif VRRPS _vrrps; // VRRPs enabled on this vif uint32_t _join; uint32_t _arps; }; #endif // __VRRP_VRRP_VIF_HH__ xorp/vrrp/vrrp_packet.cc0000664000076400007640000001104411540225535015502 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2008-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "vrrp_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libproto/checksum.h" #include "vrrp_packet.hh" #include "vrrp_exception.hh" const IPv4 VrrpPacket::mcast_group = IPv4("224.0.0.18"); VrrpHeader& VrrpHeader::assign(uint8_t* data) { x_static_assert(sizeof(VrrpHeader) == 8); x_static_assert(sizeof(VrrpAuth) == 8); VrrpHeader* vh = reinterpret_cast(data); vh->vh_v = VRRP_VERSION; vh->vh_type = VRRP_TYPE_ADVERTISEMENT; vh->vh_vrid = 0; vh->vh_priority = 0; vh->vh_ipcount = 0; vh->vh_auth = VRRP_AUTH_NONE; vh->vh_interval = 0; vh->vh_sum = 0; return *vh; } const VrrpHeader& VrrpHeader::assign(const PAYLOAD& p) { const VrrpHeader* vh = reinterpret_cast(&p[0]); unsigned size = sizeof(*vh) + sizeof(VrrpAuth); if (p.size() < size) xorp_throw(VrrpException, "packet too small"); if (vh->vh_v != VRRP_VERSION) xorp_throw(VrrpException, "unknown version"); if (vh->vh_type != VRRP_TYPE_ADVERTISEMENT) xorp_throw(VrrpException, "unknown type"); size += sizeof(*vh->vh_addr) * vh->vh_ipcount; if (size != p.size()) xorp_throw(VrrpException, "bad size"); // checksum VrrpHeader* tmp = const_cast(vh); unsigned checksum = vh->vh_sum; unsigned sz2 = tmp->finalize(); // XXX will overwrite auth XLOG_ASSERT(size == sz2); if (checksum != vh->vh_sum) xorp_throw(VrrpException, "bad checksum"); return *vh; } void VrrpHeader::add_ip(const IPv4& ip) { XLOG_ASSERT(vh_ipcount < 255); ip.copy_out(vh_addr[vh_ipcount]); vh_ipcount++; } IPv4 VrrpHeader::ip(unsigned idx) const { XLOG_ASSERT(idx < vh_ipcount); IPv4 ip; ip.copy_in(vh_addr[idx]); return ip; } uint32_t VrrpHeader::finalize() { uint32_t len = sizeof(*this); len += sizeof(*vh_addr) * vh_ipcount; VrrpAuth* auth = reinterpret_cast((unsigned long) this + len); memset(auth, 0, sizeof(*auth)); len += sizeof(*auth); vh_sum = 0; vh_sum = inet_checksum(reinterpret_cast(this), len); return len; } VrrpPacket::VrrpPacket() : _data(VRRP_MAX_PACKET_SIZE, 0), _ip(&_data[0]), _vrrp(VrrpHeader::assign(&_data[IP_HEADER_MIN_SIZE])) { _data.resize(VRRP_MAX_PACKET_SIZE); _ip.set_ip_vhl(0); _ip.set_ip_version(4); _ip.set_ip_header_len(IP_HEADER_MIN_SIZE); _ip.set_ip_tos(0); _ip.set_ip_len(0); _ip.set_ip_id(0); _ip.set_ip_off(0); _ip.set_ip_ttl(255); _ip.set_ip_p(IPPROTO_VRRP); _ip.set_ip_sum(0); _ip.set_ip_dst(mcast_group); } void VrrpPacket::set_vrid(uint8_t vrid) { _vrrp.vh_vrid = vrid; } void VrrpPacket::set_priority(uint8_t priority) { _vrrp.vh_priority = priority; } void VrrpPacket::set_interval(uint8_t interval) { _vrrp.vh_interval = interval; } void VrrpPacket::finalize() { uint32_t size = _vrrp.finalize(); size += IP_HEADER_MIN_SIZE; _ip.set_ip_len(size); _ip.compute_checksum(); XLOG_ASSERT(size <= VRRP_MAX_PACKET_SIZE); // I don't see how this assert can possibly be correct! --Ben //XLOG_ASSERT(_data.size() == _data.capacity() // && _data.size() == VRRP_MAX_PACKET_SIZE); _data.resize(size); } uint32_t VrrpPacket::size() const { return _data.size(); } void VrrpPacket::set_size(uint32_t size) { _data.resize(size); } const PAYLOAD& VrrpPacket::data() const { return _data; } void VrrpPacket::clear_ips() { _vrrp.vh_ipcount = 0; } void VrrpPacket::add_ip(const IPv4& ip) { _data.resize(VRRP_MAX_PACKET_SIZE); _vrrp.add_ip(ip); } void VrrpPacket::set_source(const IPv4& ip) { _ip.set_ip_src(ip); } xorp/vrrp/vrrp.cc0000664000076400007640000002353011540225535014156 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2008-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "vrrp_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "vrrp.hh" #include "vrrp_exception.hh" #include "vrrp_vif.hh" #include "vrrp_target.hh" #include "vrrp_packet.hh" namespace { template void out_of_range(const string& msg, const T& x) { ostringstream oss; oss << msg << " (" << x << ")"; xorp_throw(VrrpException, oss.str()); } } // anonymous namespace // XXX init from VrrpPacket::mcast_group const Mac Vrrp::mcast_mac = Mac("01:00:5E:00:00:12"); Vrrp::Vrrp(VrrpVif& vif, EventLoop& e, uint32_t vrid) : _vif(vif), _vrid(vrid), _priority(100), _interval(1), _skew_time(0), _master_down_interval(0), _preempt(true), _state(INITIALIZE), //_own(false), _disable(true) //, _arpd(_vif) { if (_vrid < 1 || _vrid > 255) out_of_range("VRID out of range", _vrid); char tmp[sizeof "ff:ff:ff:ff:ff:ff"]; // This specific mac prefix is standard for VRRP, not just some // random value someone chose! snprintf(tmp, sizeof(tmp), "00:00:5E:00:01:%X", (uint8_t) vrid); _source_mac = Mac(tmp); //_arpd.set_mac(_source_mac); _master_down_timer = e.new_periodic_ms(0x29a, callback(this, &Vrrp::master_down_expiry)); _adver_timer = e.new_periodic_ms(0x29a, callback(this, &Vrrp::adver_expiry)); cancel_timers(); setup_intervals(); } Vrrp::~Vrrp() { stop(); } void Vrrp::set_priority(uint32_t priority) { if (priority == PRIORITY_LEAVE || priority >= PRIORITY_OWN) out_of_range("priority out of range", priority); _priority = priority; setup_intervals(); } void Vrrp::set_interval(uint32_t interval) { if (interval == 0) { XLOG_WARNING("Interval was configured for zero. Increasing to 1.\n"); interval = 1; } if (interval > 255) { XLOG_WARNING("Interval was > 255: %u. Setting to 255.\n", interval); interval = 255; } _interval = interval; setup_intervals(); } void Vrrp::set_preempt(bool preempt) { _preempt = preempt; } void Vrrp::set_disable(bool disable) { _disable = disable; if (_disable) stop(); else start(); } void Vrrp::add_ip(const IPv4& ip) { _ips.insert(ip); //check_ownership(); } void Vrrp::set_prefix(const IPv4& ip, uint32_t prefix) { _prefixes[ip.addr()] = prefix; set::iterator i = _ips.find(ip); if (i == _ips.end()) { add_ip(ip); } } void Vrrp::delete_ip(const IPv4& ip) { _ips.erase(ip); //check_ownership(); } void Vrrp::setup_intervals() { double skew_time = (256.0 - (double) priority()) / 256.0; double master_down_interval = 3.0 * (double) _interval + _skew_time; if (_skew_time != skew_time || _master_down_interval != master_down_interval) { _skew_time = skew_time; _master_down_interval = master_down_interval; setup_timers(); } } void Vrrp::setup_timers(bool skew) { if (!running()) return; cancel_timers(); switch (_state) { case INITIALIZE: XLOG_ASSERT(false); break; case MASTER: _adver_timer.schedule_after_ms(_interval * 1000); break; case BACKUP: _master_down_timer.schedule_after_ms( static_cast((skew ? _skew_time : _master_down_interval) * 1000.0)); break; } } #if 0 void Vrrp::check_ownership() { bool own = true; //_arpd.clear(); for (IPS::iterator i = _ips.begin(); i != _ips.end(); ++i) { own &= _vif.own(*i); //if (!own) // _arpd.insert(*i); } //_arpd.ips_updated(); // This is broken if IPs are split (we have two, own one, but not the other) _own = own; setup_intervals(); } #endif uint32_t Vrrp::priority() const { //if (_own) // return PRIORITY_OWN; return _priority; } void Vrrp::start() { if (running()) return; if (!_vif.ready()) return; _vif.join_mcast(); if (priority() == PRIORITY_OWN) become_master(); else become_backup(); } void Vrrp::become_master() { _state = MASTER; XLOG_WARNING("become master."); _vif.add_mac(_source_mac); for (set::iterator i = _ips.begin(); i != _ips.end(); ++i) { const IPv4& ip = *i; XLOG_WARNING("become_master, adding IP: %s\n", ip.str().c_str()); uint32_t pf = 24; if (_prefixes.find(ip.addr()) != _prefixes.end()) { pf = _prefixes[ip.addr()]; if (pf <= 0) pf = 24; if (pf > 32) pf = 32; } _vif.add_ip(ip, pf); } XLOG_WARNING("done adding IPs."); send_advertisement(); send_arps(); setup_timers(); //_arpd.start(); } void Vrrp::become_backup() { XLOG_WARNING("become backup."); if (_state == MASTER) { XLOG_WARNING("deleting mac."); _vif.delete_mac(_source_mac); //_arpd.stop(); for (IPS::iterator i = _ips.begin(); i != _ips.end(); ++i) { const IPv4& ip = *i; XLOG_WARNING("become_backup, deleting IP: %s\n", ip.str().c_str()); _vif.delete_ip(ip); } } XLOG_WARNING("done deleting things."); _state = BACKUP; setup_timers(); } void Vrrp::stop() { if (!running()) return; _vif.leave_mcast(); cancel_timers(); if (_state == MASTER) { send_advertisement(PRIORITY_LEAVE); _vif.delete_mac(_source_mac); //_arpd.stop(); for (IPS::iterator i = _ips.begin(); i != _ips.end(); ++i) { const IPv4& ip = *i; XLOG_WARNING("stopping, deleting IP: %s\n", ip.str().c_str()); _vif.delete_ip(ip); } } _state = INITIALIZE; } bool Vrrp::running() const { return _state != INITIALIZE; } void Vrrp::cancel_timers() { _master_down_timer.unschedule(); _adver_timer.unschedule(); } void Vrrp::send_advertisement() { send_advertisement(priority()); } void Vrrp::send_advertisement(uint32_t priority) { XLOG_ASSERT(priority <= PRIORITY_OWN); XLOG_ASSERT(_state == MASTER); // XXX prepare only on change prepare_advertisement(priority); _vif.send(_source_mac, mcast_mac, ETHERTYPE_IP, _adv_packet.data()); } void Vrrp::prepare_advertisement(uint32_t priority) { _adv_packet.set_size(VRRP_MAX_PACKET_SIZE); _adv_packet.set_source(_vif.addr()); _adv_packet.set_vrid(_vrid); _adv_packet.set_priority(priority); _adv_packet.set_interval(_interval); _adv_packet.set_ips(_ips); _adv_packet.finalize(); } void Vrrp::send_arps() { XLOG_ASSERT(_state == MASTER); for (IPS::iterator i = _ips.begin(); i != _ips.end(); ++i) send_arp(*i); } void Vrrp::send_arp(const IPv4& ip) { PAYLOAD data; ArpHeader::make_gratuitous(data, _source_mac, ip); _vif.send(_source_mac, Mac::BROADCAST(), ETHERTYPE_ARP, data); } bool Vrrp::master_down_expiry() { XLOG_ASSERT(_state == BACKUP); become_master(); return false; } bool Vrrp::adver_expiry() { XLOG_ASSERT(_state == MASTER); send_advertisement(); setup_timers(); return false; } void Vrrp::recv_advertisement(const IPv4& from, uint32_t priority) { XLOG_ASSERT(priority <= PRIORITY_OWN); switch (_state) { case INITIALIZE: XLOG_ASSERT(false); break; case BACKUP: _last_adv = from; recv_adver_backup(priority); break; case MASTER: recv_adver_master(from, priority); break; } } void Vrrp::recv_adver_backup(uint32_t pri) { if (pri == PRIORITY_LEAVE) setup_timers(true); else if (!_preempt || pri >= priority()) setup_timers(); } void Vrrp::recv_adver_master(const IPv4& from, uint32_t pri) { if (pri == PRIORITY_LEAVE) { send_advertisement(); setup_timers(); } else if (pri > priority() || (pri == priority() && from > _vif.addr())) become_backup(); } void Vrrp::recv(const IPv4& from, const VrrpHeader& vh) { XLOG_ASSERT(vh.vh_vrid == _vrid); if (!running()) xorp_throw(VrrpException, "VRRID not running"); //if (priority() == PRIORITY_OWN) // xorp_throw(VrrpException, "I own VRR-IP but got advertisement, priority: %i", // priority()); if (vh.vh_auth != VrrpHeader::VRRP_AUTH_NONE) xorp_throw(VrrpException, "Auth method not supported"); if (!check_ips(vh) && vh.vh_priority != PRIORITY_OWN) xorp_throw(VrrpException, "Bad IPs"); if (vh.vh_interval != _interval) xorp_throw(VrrpException, "Bad interval"); recv_advertisement(from, vh.vh_priority); } bool Vrrp::check_ips(const VrrpHeader& vh) { if (vh.vh_ipcount != _ips.size()) { XLOG_WARNING("Mismatch in configured IPs (got %u have %u)", vh.vh_ipcount, XORP_UINT_CAST(_ips.size())); return false; } for (unsigned i = 0; i < vh.vh_ipcount; i++) { IPv4 ip = vh.ip(i); if (_ips.find(ip) == _ips.end()) { XLOG_WARNING("He's got %s configured but I don't", ip.str().c_str()); return false; } } return true; } #if 0 ARPd& Vrrp::arpd() { return _arpd; } #endif void Vrrp::get_info(string& state, IPv4& master) const { typedef map STATES; static STATES states; if (states.empty()) { states[INITIALIZE] = "initialize"; states[MASTER] = "master"; states[BACKUP] = "backup"; } state = states.find(_state)->second; if (_state == MASTER) master = _vif.addr(); else master = _last_adv; } xorp/vrrp/test/0000775000076400007640000000000011421137511013624 5ustar greearbgreearbxorp/vrrp/test/vrrp_test.cc0000664000076400007640000002651011421137511016167 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2008-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "vrrp/vrrp_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "vrrp/vrrp_exception.hh" #include "vrrp/vrrp_interface.hh" #include "vrrp/vrrp.hh" namespace { #define TEST_ASSERT(x) \ if (!(x)) \ xorp_throw(VrrpException, "Test assertion failed") const string INITIALIZE = "initialize"; const string MASTER = "master"; const string BACKUP = "backup"; EventLoop _eventloop; // XXX split this into multiple files struct Packet { Packet(const Mac& s, const Mac& d, uint32_t e, const PAYLOAD& p) : src(s), dst(d), ether(e), payload(p) { } Mac src; Mac dst; uint32_t ether; PAYLOAD payload; }; typedef list PACKETS; typedef set IPS; class VrrpVifTest : public VrrpInterface { public: ~VrrpVifTest(); bool own(const IPv4& addr); bool ready() const; const IPv4& addr() const; void send(const Mac& src, const Mac& dst, uint32_t ether, const PAYLOAD& payload); void join_mcast(); void leave_mcast(); void add_mac(const Mac& mac); void delete_mac(const Mac& mac); void start_arps(); void stop_arps(); void add_ip(const IPv4& ip); PACKETS& packets(); private: IPS _ips; IPv4 _addr; PACKETS _packets; }; class VrrpInstance { public: VrrpInstance(uint32_t vrid); void from_initialize_to_master(); void from_initialize_to_backup(); void from_backup_to_initialize(); void from_backup_to_master(); void from_master_to_initialize(); void add_ip(const IPv4& ip); void check_gratitious_arp(const IPv4& ip); bool check_gratitious_arp(Packet& p, const IPv4& ip); void check_advertisement(); bool check_advertisement(Packet& p); void check_master_sent_packets(); void add_owned_ip(const IPv4& ip); string state(); private: uint32_t _vrid; VrrpVifTest _vif; Vrrp _vrrp; IPS _ips; Mac _source_mac; uint32_t _interval; uint32_t _priority; }; ///////////////////////// // // VrrpVifTest // ///////////////////////// VrrpVifTest::~VrrpVifTest() { for (PACKETS::iterator i = _packets.begin(); i != _packets.end(); ++i) delete *i; } bool VrrpVifTest::own(const IPv4& addr) { return _ips.find(addr) != _ips.end(); } bool VrrpVifTest::ready() const { return true; } const IPv4& VrrpVifTest::addr() const { return _addr; } void VrrpVifTest::send(const Mac& src, const Mac& dst, uint32_t ether, const PAYLOAD& payload) { _packets.push_back(new Packet(src, dst, ether, payload)); } void VrrpVifTest::join_mcast() { } void VrrpVifTest::leave_mcast() { } void VrrpVifTest::add_mac(const Mac& mac) { UNUSED(mac); } void VrrpVifTest::delete_mac(const Mac& mac) { UNUSED(mac); } void VrrpVifTest::start_arps() { } void VrrpVifTest::stop_arps() { } void VrrpVifTest::add_ip(const IPv4& ip) { _ips.insert(ip); } PACKETS& VrrpVifTest::packets() { return _packets; } ///////////////////////// // // VrrpInstance // ///////////////////////// VrrpInstance::VrrpInstance(uint32_t vrid) : _vrid(vrid), _vrrp(_vif, _eventloop, _vrid), _interval(1), _priority(100) { char tmp[64]; snprintf(tmp, sizeof(tmp), "00:00:5E:00:01:%X", (uint8_t) vrid); _source_mac = Mac(tmp); } void VrrpInstance::add_ip(const IPv4& ip) { _ips.insert(ip); _vrrp.add_ip(ip); } string VrrpInstance::state() { IPv4 ip; string state; _vrrp.get_info(state, ip); return state; } bool VrrpInstance::check_gratitious_arp(Packet& p, const IPv4& ip) { if (p.ether != ETHERTYPE_ARP) return false; TEST_ASSERT(p.src == _source_mac); TEST_ASSERT(p.dst == Mac::BROADCAST()); const ArpHeader& ah = ArpHeader::assign(p.payload); TEST_ASSERT(ah.is_request()); IPv4 tmp_ip = ah.get_request(); TEST_ASSERT(tmp_ip == ip); // check source fields Mac tmp_mac; tmp_mac.copy_in(ah.ah_data); TEST_ASSERT(tmp_mac == _source_mac); tmp_ip.copy_in(&ah.ah_data[ah.ah_hw_len]); TEST_ASSERT(tmp_ip == ip); return true; } void VrrpInstance::check_gratitious_arp(const IPv4& ip) { PACKETS& packets = _vif.packets(); for (PACKETS::iterator i = packets.begin(); i != packets.end(); ++i) { Packet* p = *i; if (check_gratitious_arp(*p, ip)) { delete p; packets.erase(i); return; } } TEST_ASSERT(false); } bool VrrpInstance::check_advertisement(Packet& p) { if (p.ether != ETHERTYPE_IP) return false; // check MAC addresses TEST_ASSERT(p.src == _source_mac); TEST_ASSERT(p.dst == Mac("01:00:5E:00:00:12")); // check IPs IPv4 tmp_ip; tmp_ip.copy_in(&p.payload[12]); TEST_ASSERT(tmp_ip == _vif.addr()); tmp_ip.copy_in(&p.payload[16]); TEST_ASSERT(tmp_ip == IPv4("224.0.0.18")); // check VRRP PAYLOAD vrrp(p.payload.begin() + 20, p.payload.end()); const VrrpHeader& vh = VrrpHeader::assign(vrrp); TEST_ASSERT(vh.vh_v == 2); TEST_ASSERT(vh.vh_type == 1); TEST_ASSERT(vh.vh_auth == 0); TEST_ASSERT(vh.vh_vrid == _vrid); TEST_ASSERT(vh.vh_priority == _priority); TEST_ASSERT(vh.vh_interval == _interval); TEST_ASSERT(vh.vh_ipcount == _ips.size()); for (unsigned i = 0; i < vh.vh_ipcount; ++i) { tmp_ip.copy_in((const uint8_t*) &vh.vh_addr[i]); TEST_ASSERT(_ips.find(tmp_ip) != _ips.end()); } return true; } void VrrpInstance::check_advertisement() { PACKETS& packets = _vif.packets(); for (PACKETS::iterator i = packets.begin(); i != packets.end(); ++i) { Packet* p = *i; if (check_advertisement(*p)) { delete p; packets.erase(i); return; } } TEST_ASSERT(false); } void VrrpInstance::check_master_sent_packets() { for (IPS::iterator i = _ips.begin(); i != _ips.end(); ++i) check_gratitious_arp(*i); check_advertisement(); TEST_ASSERT(_vif.packets().empty()); } void VrrpInstance::from_initialize_to_master() { // // initialize -> master // // - If the Priority = 255 (i.e., the router owns the IP address(es) // associated with the virtual router) // // o Send an ADVERTISEMENT // o Broadcast a gratuitous ARP request containing the virtual // router MAC address for each IP address associated with the // virtual router. // o Set the Adver_Timer to Advertisement_Interval // o Transition to the {Master} state // // check initial state TEST_ASSERT(state() == INITIALIZE); TEST_ASSERT(_vrrp.priority() == 255); _priority = 255; // start _vrrp.start(); // check sent packets check_master_sent_packets(); // check final state TEST_ASSERT(state() == MASTER); } void VrrpInstance::from_master_to_initialize() { // // - If a Shutdown event is received, then: // // o Cancel the Adver_Timer // o Send an ADVERTISEMENT with Priority = 0 // o Transition to the {Initialize} state // TEST_ASSERT(state() == MASTER); _vrrp.stop(); _priority = 0; check_advertisement(); TEST_ASSERT(_vif.packets().empty()); TEST_ASSERT(state() == INITIALIZE); } void VrrpInstance::add_owned_ip(const IPv4& ip) { _vif.add_ip(ip); add_ip(ip); } void VrrpInstance::from_initialize_to_backup() { // // initialize -> backup // // o Set the Master_Down_Timer to Master_Down_Interval // o Transition to the {Backup} state // // // check initial state TEST_ASSERT(state() == INITIALIZE); TEST_ASSERT(_vrrp.priority() == _priority); // start _vrrp.start(); // check sent packets TEST_ASSERT(_vif.packets().empty()); // check final state TEST_ASSERT(state() == BACKUP); } void VrrpInstance::from_backup_to_initialize() { // // - If a Shutdown event is received, then: // // o Cancel the Master_Down_Timer // o Transition to the {Initialize} state // // check initial state TEST_ASSERT(state() == BACKUP); // stop _vrrp.stop(); // check sent packets TEST_ASSERT(_vif.packets().empty()); // check final state TEST_ASSERT(state() == INITIALIZE); } void VrrpInstance::from_backup_to_master() { // // - If the Master_Down_Timer fires, then: // // o Send an ADVERTISEMENT // o Broadcast a gratuitous ARP request containing the virtual // router MAC address for each IP address associated with the // virtual router // o Set the Adver_Timer to Advertisement_Interval // o Transition to the {Master} state // TEST_ASSERT(state() == BACKUP); // XXX wait for Master_Down_Interval cout << "Waiting for Master_Down_Interval" << endl; while (state() != MASTER) _eventloop.run(); TEST_ASSERT(state() == MASTER); check_master_sent_packets(); } ///////////////////////// // // Tests // ///////////////////////// void from_initialize_to_master() { VrrpInstance vi(1); vi.add_owned_ip(IPv4("1.2.3.4")); vi.from_initialize_to_master(); } void from_master_to_initialize() { VrrpInstance vi(1); vi.add_owned_ip(IPv4("1.2.3.4")); vi.from_initialize_to_master(); vi.from_master_to_initialize(); } void from_initialize_to_backup() { VrrpInstance vi(1); vi.add_ip(IPv4("1.2.3.4")); vi.from_initialize_to_backup(); } void from_backup_to_initialize() { VrrpInstance vi(1); vi.add_ip(IPv4("1.2.3.4")); vi.from_initialize_to_backup(); vi.from_backup_to_initialize(); } void from_backup_to_master() { VrrpInstance vi(1); vi.add_ip(IPv4("1.2.3.4")); vi.from_initialize_to_backup(); vi.from_backup_to_master(); } void test_state_transitions() { // master from_initialize_to_master(); from_master_to_initialize(); // XXX test receiving advertisements // backup from_initialize_to_backup(); from_backup_to_initialize(); from_backup_to_master(); // XXX test receiving advertisements } void go() { test_state_transitions(); // XXX test sending advertisements // XXX test instances working in harmony // XXX test corner cases } } // anon namespace int main(int argc, char* argv[]) { UNUSED(argc); xlog_init(argv[0], 0); xlog_set_verbose(XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); try { go(); } catch(const VrrpException& e) { cout << "VrrpException: " << e.str() << endl; exit(1); } xlog_stop(); xlog_exit(); exit(0); } xorp/vrrp/vrrp.hh0000664000076400007640000001675311540225535014201 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2008-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/vrrp/vrrp.hh,v 1.10 2008/12/18 11:34:51 abittau Exp $ #ifndef __VRRP_VRRP_HH__ #define __VRRP_VRRP_HH__ #include "libxorp/ipv4.hh" #include "libxorp/timer.hh" #include "libxorp/mac.hh" #include "libxorp/eventloop.hh" #include "vrrp_packet.hh" #include "vrrp_vif.hh" //#include "arpd.hh" #define MAX_VRRP_SIZE (20 + VRRP_MAX_PACKET_SIZE) /** * @short VRRP protocol implementation. * * This class implements the state machine of the VRRP protocol. It receive * events and responds to them. */ class Vrrp { public: static const Mac mcast_mac; /** * @param vif the VRRP interface on which this instance is running. * @param e eventloop. * @param vrid the VRRP id of this instance. */ Vrrp(VrrpVif& vif, EventLoop& e, uint32_t vrid); ~Vrrp(); /** * Set the priority of the router. * * @param priority the priority of this router. */ void set_priority(uint32_t priority); /** * Set the advertisement interval. * * @param interval the interval for sending advertisements. */ void set_interval(uint32_t interval); /** * Set whether a high priority backup router should preempt backup routers * with lower priority acting as masters. * * @param preempt whether a high priority backup should preempt a low one. */ void set_preempt(bool preempt); /** * Stop or start this VRRP instance. * * @param disable stop this VRRP instance. */ void set_disable(bool disable); /** * Add an IP to the virtual router. * * @param ip IP address to add to the virtual router. */ void add_ip(const IPv4& ip); void set_prefix(const IPv4& ip, uint32_t prefix); /** * Delete an IP from the virtual router. * * @param ip IP address to remove from the virtual router. */ void delete_ip(const IPv4& ip); /** * Check whether this instance is running. * * @return whether this VRRP instance is running. */ bool running() const; /** * Obtain the priority of this router. * * @return priority of router. */ uint32_t priority() const; /** * Start VRRP processing. */ void start(); /** * Stop the protocol. */ void stop(); /** * Check whether the router owns all IPs configured for the virtual router. * This must be called after updating IP addresses. */ //void check_ownership(); /** * Must be called when a VRRP packet is received. * * @param from IP address sending the packet. * @param vh the VRRP packet received, starting from the VRRP header. */ void recv(const IPv4& from, const VrrpHeader& vh); /** * Obtain an instance to the ARP daemon. * * @return an instance of the ARP daemon. */ //ARPd& arpd(); /** * Returns information about the state of the protocol. * * @param state the state of the router (output parameter). * @param master the IP address of the master (output parameter). */ void get_info(string& state, IPv4& master) const; private: enum State { INITIALIZE = 0, MASTER, BACKUP }; enum { PRIORITY_LEAVE = 0, PRIORITY_OWN = 255 }; typedef set IPS; /** * Calculate advertisement intervals based on protocol parameters. This is * called when any of these parameters (e.g., whether the router owns the * IPs) change. */ void setup_intervals(); /** * Setup timers based on intervals. Must be called when intervals change. * * @param skew whether the skew time should be added when setting the timer. */ void setup_timers(bool skew = false); /** * Stop all timers. */ void cancel_timers(); /** * Send gratuitous ARPs for all the IP addresses of this virtual router. */ void send_arps(); /** * Send a gratitious ARP for a specific IP. * * @param ip the IP address to send the ARP for. */ void send_arp(const IPv4& ip); /** * Send an advertisement with given priority. * * @param priority of the advertisement. */ void send_advertisement(uint32_t priority); /** * Send a VRRP advertisement. The parameters are taken from member * variables. * */ void send_advertisement(); /** * Become the master router. */ void become_master(); /** * Become a backup router. */ void become_backup(); /** * This is called when this router thinks the master router is dead. That * is, when the timer waiting for an advertisement expires. * * @return whether to reschedule the timer. */ bool master_down_expiry(); /** * This is called when the next advertisement should be sent. * * @return whether to renew the timer. */ bool adver_expiry(); /** * Called when receiving an advertisement in the master state. * * @param from the IP that sent the advertisement. * @param priority the priority in the VRRP advertisement. */ void recv_adver_master(const IPv4& from, uint32_t priority); /** * Called when an advertisement is received in the backup state. * * @param priority the priority within the VRRP advertisement. */ void recv_adver_backup(uint32_t priority); /** * Create the advertisement packet. * * @param priority the priority to be announced in the advertisement. */ void prepare_advertisement(uint32_t priority); /** * Notification of advertisement reception. * * @param from IP address of sender. * @param priority the priority in the VRRP header. */ void recv_advertisement(const IPv4& from, uint32_t priority); /** * Check whether the IPs in the VRRP packet match those configured * * @return true on exact matches. * @param vh the VRRP header. */ bool check_ips(const VrrpHeader& vh); IPv4 _last_adv; VrrpVif& _vif; uint32_t _vrid; uint32_t _priority; uint32_t _interval; double _skew_time; double _master_down_interval; bool _preempt; set _ips; map _prefixes; // map IP addr to prefix value. State _state; XorpTimer _master_down_timer; XorpTimer _adver_timer; //bool _own; bool _disable; VrrpPacket _adv_packet; Mac _source_mac; //ARPd _arpd; // XXX use OS proxy arp mechanism? }; #endif // __VRRP_VRRP_HH__ xorp/vrrp/vrrp_exception.hh0000664000076400007640000000263111421137511016237 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2008-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/vrrp/vrrp_exception.hh,v 1.4 2008/12/18 11:34:51 abittau Exp $ #ifndef __VRRP_VRRP_EXCEPTION_HH__ #define __VRRP_VRRP_EXCEPTION_HH__ #include "libxorp/exceptions.hh" /** * @short Exceptions generated by the VRRP protocol. * * All VRRP errors are encapsulated with this class. */ class VrrpException : public XorpReasonedException { public: VrrpException(const char* file, size_t line, const string& why = "") : XorpReasonedException("VrrpException", file, line, why) {} }; #endif // __VRRP_VRRP_EXCEPTION_HH__ xorp/vrrp/vrrp_target.hh0000664000076400007640000002457611540225535015551 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2008-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/vrrp/vrrp_target.hh,v 1.11 2008/12/18 11:34:51 abittau Exp $ #ifndef __VRRP_VRRP_TARGET_HH__ #define __VRRP_VRRP_TARGET_HH__ #include "libxipc/xrl_router.hh" #include "libfeaclient/ifmgr_xrl_mirror.hh" #include "xrl/targets/vrrp_base.hh" #include "xrl/interfaces/fea_rawlink_xif.hh" #include "xrl/interfaces/fea_rawpkt4_xif.hh" #include "xrl/interfaces/fea_ifmgr_xif.hh" #include "vrrp.hh" #include "vrrp_vif.hh" /** * @short The VRRP XORP process. * * This class manages all VRRP instances and provides a link between XRLs and * the VRRP state machine. */ class VrrpTarget : public XrlVrrpTargetBase, public IfMgrHintObserver { public: static const string vrrp_target_name; static const string fea_target_name; /** * @param rtr the XRL router to use. */ VrrpTarget(XrlRouter& rtr); ~VrrpTarget(); /** * Check whether VRRP is running. * * @return whether the VRRP protocol is running. */ bool running() const; /** * Called when the rtrmgr configuration is first received. */ void tree_complete(); /** * Called when the rtrmgr configuration changed. */ void updates_made(); /** * Transmit a L2 packet. * * @param ifname the physical interface on which to transmit. * @param vifname the logical interface on which to transmit. * @param src the source MAC address. * @param dst the destination MAC address. * @param ether the 802.3 ethernet type. * @param payload the data to follow the MAC header. */ void send(const string& ifname, const string& vifname, const Mac& src, const Mac& dst, uint32_t ether, const PAYLOAD& payload); /** * Join the VRRP multicast group. * * @param ifname the interface on which to join. * @param vifname the vif on which to join. */ void join_mcast(const string& ifname, const string& vifname); /** * Leave the VRRP multicast group. * * @param ifname the interface on which to join. * @param vifname the vif on which to join. */ void leave_mcast(const string& ifname, const string& vifname); /** * Start the reception of ARP packets on an interface. * * @param ifname the interface on which to receive ARPs. * @param vifname the vif on which to get ARPs. */ void start_arps(const string& ifname, const string& vifname); /** * Stop the reception of ARPs. * * @param ifname the interface on which to stop reception. * @param vifname the vif on which to stop reception. */ void stop_arps(const string& ifname, const string& vifname); /** * Add a MAC address to the router. * * @param ifname the interface on which to add the MAC. * @param mac the MAC address. */ void add_mac(const string& ifname, const Mac& mac); /** * Delete MAC address from the router. * * @param ifname the interface on which the MAC should be deleted. * @param mac the MAC to remove. */ void delete_mac(const string& ifname, const Mac& mac); void add_ip(const string& ifname, const IPv4& ip, uint32_t prefix); void delete_ip(const string& ifname, const IPv4& ip); /** * @return an instance of the eventloop. */ EventLoop& eventloop(); protected: XrlCmdError common_0_1_get_target_name( // Output values, string& name); XrlCmdError common_0_1_get_version( // Output values, string& version); XrlCmdError common_0_1_get_status( // Output values, uint32_t& status, string& reason); XrlCmdError common_0_1_shutdown(); XrlCmdError common_0_1_startup(); XrlCmdError vrrp_0_1_add_vrid( // Input values, const string& ifname, const string& vifname, const uint32_t& vrid); XrlCmdError vrrp_0_1_delete_vrid( // Input values, const string& ifname, const string& vifname, const uint32_t& vrid); XrlCmdError vrrp_0_1_set_priority( // Input values, const string& ifname, const string& vifname, const uint32_t& vrid, const uint32_t& priority); XrlCmdError vrrp_0_1_set_interval( // Input values, const string& ifname, const string& vifname, const uint32_t& vrid, const uint32_t& interval); XrlCmdError vrrp_0_1_set_preempt( // Input values, const string& ifname, const string& vifname, const uint32_t& vrid, const bool& preempt); XrlCmdError vrrp_0_1_set_disable( // Input values, const string& ifname, const string& vifname, const uint32_t& vrid, const bool& disable); XrlCmdError vrrp_0_1_add_ip( // Input values, const string& ifname, const string& vifname, const uint32_t& vrid, const IPv4& ip); XrlCmdError vrrp_0_1_set_prefix ( // Input values, const string& ifname, const string& vifname, const uint32_t& vrid, const IPv4& ip, const uint32_t& prefix_len); XrlCmdError vrrp_0_1_delete_ip( // Input values, const string& ifname, const string& vifname, const uint32_t& vrid, const IPv4& ip); XrlCmdError vrrp_0_1_get_vrid_info( // Input values, const string& ifname, const string& vifname, const uint32_t& vrid, // Output values, string& state, IPv4& master); XrlCmdError vrrp_0_1_get_vrids( // Input values, const string& ifname, const string& vifname, // Output values, XrlAtomList& vrids); XrlCmdError vrrp_0_1_get_ifs( // Output values, XrlAtomList& ifs); XrlCmdError vrrp_0_1_get_vifs( // Input values, const string& ifname, // Output values, XrlAtomList& vifs); XrlCmdError raw_packet4_client_0_1_recv( // Input values, const string& if_name, const string& vif_name, const IPv4& src_address, const IPv4& dst_address, const uint32_t& ip_protocol, const int32_t& ip_ttl, const int32_t& ip_tos, const bool& ip_router_alert, const bool& ip_internet_control, const vector& payload); XrlCmdError raw_link_client_0_1_recv( // Input values, const string& if_name, const string& vif_name, const Mac& src_address, const Mac& dst_address, const uint32_t& ether_type, const vector& payload); private: typedef map VIFS; // vifname -> VrrpVif typedef map IFS; // ifname -> VIFS /** * Terminate the VRRP process. */ void shutdown(); /** * Start VRRP. */ void start(); /** * Check whether rtrmgr configuration changes affect VRRP. */ void check_interfaces(); /** * Check whether configuration changes affect a specific interface. * * @param vif the interface to check. */ void check_vif(VrrpVif& vif); /** * Add a VRRP instance. * * @param ifn interface to run on. * @param vifn vif to run on. * @param id the VRRP router id. */ void add_vrid(const string& ifn, const string& vifn, uint32_t id); /** * Delete a VRRP instance. * * @param ifn the interface on which the instance is running. * @param vifn the vif on which it's running. * @param id the VRRP router id. */ void delete_vrid(const string& ifn, const string& vifn, uint32_t id); /** * Get a reference to a VRRP instance. An exception is thrown if the * instance does not exist. * * @return the VRRP instance. * @param ifn the interface on which it is running. * @param vifn the vif on which it is running. * @param id the VRRP id. */ Vrrp& find_vrid(const string& ifn, const string& vifn, uint32_t id); /** * Get a pointer to a VRRP instance. Null is return if it does not exist. * * @return the VRRP instance or null if it is not present. * @param ifn the interface on which the instance is running. * @param vifn the vif on which it is running. * @param id the VRRP id. */ Vrrp* find_vrid_ptr(const string& ifn, const string& vifn, uint32_t id); /** * Get a pointer to a network interface. Null is returned if none is found * and add is false. If none is found and add is true, the interface is * created and a pointer to it is returned. * * @return the interface, or null if it is not found and add is false. * @param ifn the physical interface name. * @param vifn the logical interface name. * @param add whether the interface should be added if it is not found. */ VrrpVif* find_vif(const string& ifn, const string& vifn, bool add = false); /** * The callback for all XRL operations. * * @param xrl_error the error (if any) of the operation. */ void xrl_cb(const XrlError& xrl_error); XrlRouter& _rtr; bool _running; IFS _ifs; IfMgrXrlMirror _ifmgr; bool _ifmgr_setup; XrlRawLinkV0p1Client _rawlink; XrlRawPacket4V0p1Client _rawipv4; XrlIfmgrV0p1Client _fea; int _xrls_pending; }; #endif // __VRRP_VRRP_TARGET_HH__ xorp/vrrp/vrrp_module.h0000664000076400007640000000232311421137511015354 0ustar greearbgreearb/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- */ /* vim:set sts=4 ts=8: */ /* * Copyright (c) 2008-2009 XORP, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 2, June * 1991 as published by the Free Software Foundation. Redistribution * and/or modification of this program under the terms of any other * version of the GNU General Public License is not permitted. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, * see the GNU General Public License, Version 2, a copy of which can be * found in the XORP LICENSE.gpl file. * * XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; * http://xorp.net */ /* * $XORP: xorp/vrrp/vrrp_module.h,v 1.2 2008/10/09 18:03:49 abittau Exp $ */ #ifndef __VRRP_VRRP_MODULE_H__ #define __VRRP_VRRP_MODULE_H__ #ifndef XORP_MODULE_NAME #define XORP_MODULE_NAME "VRRP" #endif #ifndef XORP_MODULE_VERSION #define XORP_MODULE_VERSION "0.1" #endif #endif /* __VRRP_VRRP_MODULE_H__ */ xorp/contrib/0000775000076400007640000000000011421137511013314 5ustar greearbgreearbxorp/contrib/olsr/0000775000076400007640000000000011703345405014301 5ustar greearbgreearbxorp/contrib/olsr/neighbor.cc0000664000076400007640000002270411540225522016406 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8 sw=4: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "olsr_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/callback.hh" #include "libxorp/ipv4.hh" #include "libxorp/ipv4net.hh" #include "libxorp/status_codes.h" #include "libxorp/service.hh" #include "libxorp/eventloop.hh" #include "olsr.hh" #include "link.hh" #include "neighbor.hh" #include "neighborhood.hh" bool IsLinkSymmetricPred::operator()(OlsrTypes::LogicalLinkID linkid) { try { bool result = _nh->get_logical_link(linkid)->is_sym(); debug_msg("link %u is %s\n", XORP_UINT_CAST(linkid), bool_c_str(result)); return result; } catch (...) { return false; } } bool IsTwoHopLinkStrictPred::operator()(OlsrTypes::TwoHopLinkID tlid) { try { return _nh->get_twohop_link(tlid)->destination()->is_strict(); } catch (...) { return false; } } Neighbor::Neighbor(EventLoop& ev, Neighborhood* parent, const OlsrTypes::NeighborID nid, const IPv4& main_addr, const OlsrTypes::LogicalLinkID linkid) : _eventloop(ev), _parent(parent), _id(nid), _main_addr(main_addr), _is_mpr(false), _is_sym(false), _willingness(OlsrTypes::WILL_NEVER), _degree(0), _is_advertised(false) { // Invariant: A neighbor must be created with a newly created link. XLOG_ASSERT(OlsrTypes::UNUSED_NEIGHBOR_ID == _parent->get_logical_link(linkid)->neighbor_id()); update_link(linkid); // Ensure that the neighbor's initial MPR candidacy is updated. update_cand_mpr(false); } string Neighbor::toStringBrief() { ostringstream oss; oss << id() << "-(" << main_addr().str() << ")"; return oss.str(); } OlsrTypes::NeighborType Neighbor::neighbor_type() const { if (is_mpr()) return OlsrTypes::MPR_NEIGH; else if (is_sym()) return OlsrTypes::SYM_NEIGH; return OlsrTypes::NOT_NEIGH; } void Neighbor::recount_degree() { _degree = count_if(_twohop_links.begin(), _twohop_links.end(), IsTwoHopLinkStrictPred(_parent)); } bool Neighbor::is_cand_mpr() { #if 0 // A neighbor which will always forward MUST be an MPR, even // if it has no two-hop links. // XXX If this predicate always holds, we can't tell when a // change in degree would cause a persistent MPR to be considered // as an MPR candidate. if (_willingness == OlsrTypes::WILL_ALWAYS) return true; #endif // A neighbor which will never forward MUST NOT be an MPR. if (_willingness == OlsrTypes::WILL_NEVER) return false; // A neighbor with degree > 0 is an MPR. return degree() > 0; } bool Neighbor::update_cand_mpr(bool was_cand_mpr) { // If willingness is neither WILL_ALWAYS nor WILL_NEVER, // a recount of degree is necessary. // Degree is never used for MPR decisions when these conditions hold, // so update it lazily. if (_willingness != OlsrTypes::WILL_ALWAYS || _willingness != OlsrTypes::WILL_NEVER) recount_degree(); // Reconsider node's MPR candidacy. bool is_now_cand_mpr = is_cand_mpr(); // If there has been no change in MPR candidacy on a node // with normal willingness, there is no need to notify Neighborhood. if (!is_persistent_cand_mpr() && was_cand_mpr == is_now_cand_mpr) return is_now_cand_mpr; // // Trigger MPR recount in Neighborhood. // // 8.5: The MPR set MUST be recounted when a neighbor // appearance or loss is detected, or when a change in the 2-hop // neighborhood is detected. // // If a WILL_ALWAYS node is being updated we need to force an update // of this as it may have been left out during // if (is_persistent_cand_mpr() || (was_cand_mpr == false && is_cand_mpr() == true)) { // If a node is now a candidate MPR due to a change // in its state, it should be considered as an MPR // candidate for all interfaces from which it is reachable. _parent->add_cand_mpr(id()); } else { // If a node is no longer a candidate MPR due to a change // in its state, it should be withdrawn as an MPR // candidate for all interfaces from which it is reachable. _parent->withdraw_cand_mpr(id()); } return is_now_cand_mpr; } void Neighbor::set_willingness(const OlsrTypes::WillType value) { if (_willingness == value) return; // Consider node's MPR status before its change in willingness. bool was_cand_mpr = is_cand_mpr(); _willingness = value; // Reconsider node's MPR status after a state change. update_cand_mpr(was_cand_mpr); } void Neighbor::update_link(const OlsrTypes::LogicalLinkID linkid) { debug_msg("LinkID %u\n", XORP_UINT_CAST(linkid)); XLOG_ASSERT(OlsrTypes::UNUSED_LINK_ID != linkid); bool was_cand_mpr = is_cand_mpr(); if (_links.find(linkid) == _links.end()) { _links.insert(linkid); } // // 8.1 Neighbor Set Update: if any associated link tuple // indicates a symmetric link for the neighbor, set N_status to SYM. // // Update the link status regardless of whether this is a new link // or a link being touched as part of a HELLO message. Doing it from // Link itself may trigger an exception if the associated Neighbor // does not yet exist. // if (! is_sym()) set_is_sym(_parent->get_logical_link(linkid)->is_sym()); // We need to reconsider MPR candidacy as the neighbor // MAY now be reachable by another interface, or MAY now // provide a new path to two-hop neighbors which it did not before. update_cand_mpr(was_cand_mpr); } bool Neighbor::delete_link(const OlsrTypes::LogicalLinkID linkid) { debug_msg("LinkID %u\n", XORP_UINT_CAST(linkid)); // Invariant: Non-existent links should not be deleted. XLOG_ASSERT(_links.find(linkid) != _links.end()); // Consider node's MPR status before its change in link state. bool was_cand_mpr = is_cand_mpr(); _links.erase(linkid); bool is_empty = _links.empty(); // 8.1 Re-evaluate N_status. A neighbor with no links is no longer // a symmetric neighbor; otherwise, check the rest of the links. // XXX TODO Only 'good' links should be considered when ETX // is implemented, see Neighborhood::is_better_link(). set_is_sym(is_empty ? false : find_if(_links.begin(), _links.end(), IsLinkSymmetricPred(_parent)) != _links.end()); // Neighbor loses MPR status if we no longer have a symmetric link, or // indeed any links, to this neighbor. update_cand_mpr(was_cand_mpr); return is_empty; } bool Neighbor::is_mpr_selector() const { return _mpr_selector_timer.scheduled(); } void Neighbor::set_is_mpr_selector(bool value, const TimeVal& expiry_time) { if (_mpr_selector_timer.scheduled()) _mpr_selector_timer.clear(); if (value == true) { debug_msg("scheduling %u's MPR selector expiry for %s\n", XORP_UINT_CAST(id()), cstring(expiry_time)); _mpr_selector_timer = _eventloop.new_oneoff_after( expiry_time, callback(this, &Neighbor::event_mpr_selector_expired)); } } void Neighbor::add_twohop_link(const OlsrTypes::TwoHopLinkID tlid) { debug_msg("TwoHopLinkID %u\n", XORP_UINT_CAST(tlid)); // Invariant: Two-hop links should not be added more than once. XLOG_ASSERT(_twohop_links.find(tlid) == _twohop_links.end()); // Consider node's MPR status before its change in link state. bool was_cand_mpr = is_cand_mpr(); // Add the new two-hop link. _twohop_links.insert(tlid); // Reconsider node's MPR status after a state change. update_cand_mpr(was_cand_mpr); } bool Neighbor::delete_twohop_link(const OlsrTypes::TwoHopLinkID tlid) { debug_msg("TwoHopLinkID %u\n", XORP_UINT_CAST(tlid)); // Invariant: Non-existent two-hop links should not be deleted. XLOG_ASSERT(_twohop_links.find(tlid) != _twohop_links.end()); // Consider node's MPR status before its change in link state. bool was_cand_mpr = is_cand_mpr(); _twohop_links.erase(tlid); bool is_empty = _twohop_links.empty(); // Reconsider node's MPR status after a state change. update_cand_mpr(was_cand_mpr); return is_empty; } size_t Neighbor::delete_all_twohop_links() { size_t deleted_count = 0; // Consider node's MPR status before its change in link state. bool was_cand_mpr = is_cand_mpr(); set::iterator ii, jj; ii = _twohop_links.begin(); while (ii != _twohop_links.end()) { jj = ii++; _parent->delete_twohop_link((*jj)); // Don't erase the element; delete_twohop_link() will // call back to do this. ++deleted_count; } // Reconsider node's MPR status after a state change. update_cand_mpr(was_cand_mpr); return deleted_count; } void Neighbor::event_mpr_selector_expired() { _parent->event_mpr_selector_expired(id()); } xorp/contrib/olsr/test_faces.cc0000664000076400007640000003363211421137511016731 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8 sw=4: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "olsr_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/callback.hh" #include "libxorp/exceptions.hh" #include "libxorp/ipv4.hh" #include "libxorp/ipv4net.hh" #include "libxorp/status_codes.h" #include "libxorp/service.hh" #include "libxorp/eventloop.hh" #include "libxorp/tokenize.hh" #include "libxorp/test_main.hh" #include "olsr.hh" #include "debug_io.hh" #include "emulate_net.hh" // Reduce the hello interval from 10 to 1 second to speed up the test. uint16_t hello_interval = 1; uint16_t refresh_interval = 1; // Run on a different port. static const uint16_t MY_OLSR_PORT = 6698; // Do not stop a tests allow it to run forever to observe timers. bool forever = false; /** * Configure a single face. Nothing is really expected to go wrong * but the test is useful to verify the normal path through the code. */ bool single_face(TestInfo& info) { EventLoop eventloop; DebugIO io(info, eventloop); io.startup(); Olsr olsr(eventloop, &io); FaceManager& fm = olsr.face_manager(); // Create a Face. const string interface = "eth0"; const string vif = "vif0"; const uint16_t interface_cost = 10; OlsrTypes::FaceID faceid = fm.create_face(interface, vif); olsr.set_main_addr("192.0.2.1"); fm.set_local_addr(faceid, IPv4("192.0.2.1")); fm.set_local_port(faceid, MY_OLSR_PORT); fm.set_all_nodes_addr(faceid, IPv4("255.255.255.255")); fm.set_all_nodes_port(faceid, MY_OLSR_PORT); fm.activate_face(interface, vif); if (!olsr.set_hello_interval(TimeVal(hello_interval, 0))) { DOUT(info) << "Failed to set hello interval\n"; return false; } if (!olsr.set_refresh_interval(TimeVal(refresh_interval, 0))) { DOUT(info) << "Failed to set refresh interval\n"; return false; } if (!olsr.set_interface_cost(interface, vif, interface_cost)) { DOUT(info) << "Failed to set face cost\n"; return false; } // Bring the OLSR interface binding up. if (!fm.set_face_enabled(faceid, true)) { DOUT(info) << "Failed enable face\n"; return false; } if (forever) while (olsr.running()) eventloop.run(); bool timeout = false; XorpTimer t = eventloop.set_flag_after(TimeVal(4 * hello_interval ,0), &timeout); while (olsr.running() && !timeout) { eventloop.run(); if (2 == io.packets()) break; } if (timeout) { DOUT(info) << "No packets sent, test timed out\n"; return false; } // Take the OLSR interface binding down if (!fm.set_face_enabled(faceid, false)) { DOUT(info) << "Failed to disable face\n"; return false; } // Delete the face. if (!fm.delete_face(faceid)) { DOUT(info) << "Failed to delete face\n"; return false; } return true; } enum Stagger { NOSTAGGER, STAGGER1, STAGGER2}; string suppress; /** * Configure two faces. Nothing is really expected to go wrong * but the test is useful to verify the normal path through the code. */ bool two_faces(TestInfo& info, Stagger stagger) { EventLoop eventloop; bool verbose[2]; verbose[0] = info.verbose(); verbose[1] = info.verbose(); if (suppress == "") ; else if (suppress == "olsr1") verbose[0] = false; else if (suppress == "olsr2") verbose[1] = false; else { info.out() << "illegal value for suppress" << suppress << endl; return false; } TestInfo info1(info.test_name() + "(olsr1)" , verbose[0], info.verbose_level(), info.out()); TestInfo info2(info.test_name() + "(olsr2)" , verbose[1], info.verbose_level(), info.out()); DebugIO io_1(info1, eventloop); io_1.startup(); DebugIO io_2(info2, eventloop); io_2.startup(); Olsr olsr_1(eventloop, &io_1); Olsr olsr_2(eventloop, &io_2); olsr_1.set_main_addr("192.0.2.1"); olsr_2.set_main_addr("192.0.2.2"); FaceManager& fm_1 = olsr_1.face_manager(); FaceManager& fm_2 = olsr_2.face_manager(); const string interface_1 = "eth1"; const string interface_2 = "eth2"; const string vif_1 = "vif1"; const string vif_2 = "vif2"; IPv4 addr_1("192.0.2.1"); IPv4 addr_2("192.0.2.2"); const uint16_t interface_cost = 10; OlsrTypes::FaceID faceid_1 = fm_1. create_face(interface_1, vif_1); OlsrTypes::FaceID faceid_2 = fm_2. create_face(interface_2, vif_2); // TODO: push to DebugIo and use getaddresses() interface from // recompute_addresses to deal with the link's addresses. fm_1.set_local_addr(faceid_1, addr_1); fm_1.set_local_port(faceid_1, MY_OLSR_PORT); fm_1.set_all_nodes_addr(faceid_1, IPv4("255.255.255.255")); fm_1.set_all_nodes_port(faceid_1, MY_OLSR_PORT); fm_2.set_local_addr(faceid_2, addr_2); fm_2.set_local_port(faceid_2, MY_OLSR_PORT); fm_2.set_all_nodes_addr(faceid_2, IPv4("255.255.255.255")); fm_2.set_all_nodes_port(faceid_2, MY_OLSR_PORT); fm_1.activate_face(interface_1, vif_1); fm_2.activate_face(interface_2, vif_2); olsr_1.set_hello_interval(TimeVal(hello_interval, 0)); olsr_1.set_refresh_interval(TimeVal(refresh_interval, 0)); olsr_1.set_interface_cost(interface_1, vif_1, interface_cost); olsr_2.set_hello_interval(TimeVal(hello_interval, 0)); olsr_2.set_refresh_interval(TimeVal(refresh_interval, 0)); olsr_2.set_interface_cost(interface_2, vif_2, interface_cost); EmulateSubnet emu(info, eventloop); emu.bind_interface("olsr1", interface_1, vif_1, addr_1, MY_OLSR_PORT, io_1); emu.bind_interface("olsr2", interface_2, vif_2, addr_2, MY_OLSR_PORT, io_2); if (STAGGER1 != stagger) fm_1.set_face_enabled(faceid_1, true); if (STAGGER2 != stagger) fm_2.set_face_enabled(faceid_2, true); Neighborhood& nh_1 = olsr_1.neighborhood(); Neighborhood& nh_2 = olsr_2.neighborhood(); nh_1.set_willingness(OlsrTypes::WILL_HIGH); nh_2.set_willingness(OlsrTypes::WILL_HIGH); if (forever) while (olsr_1.running() && olsr_2.running()) eventloop.run(); bool timeout = false; XorpTimer t = eventloop.set_flag_after(TimeVal(20 * hello_interval, 0), &timeout); const int expected = 16; while (olsr_1.running() && olsr_2.running() && !timeout) { eventloop.run(); if (expected <= io_1.packets() + io_2.packets()) break; if (STAGGER1 == stagger && 1 == io_2.packets()) fm_1.set_face_enabled(faceid_1, true); if (STAGGER2 == stagger && 1 == io_1.packets()) fm_2.set_face_enabled(faceid_2, true); } if (timeout) { DOUT(info) << io_1.packets() << " packets sent, at most " << expected << " expected; test timed out\n"; return false; } // Assert that OLSR1 sees OLSR2 correctly. try { // Assert that OLSR2's interface on the emulated subnet is present in // OLSR1's link database, and that the link is symmetric. OlsrTypes::LogicalLinkID lid = nh_1.get_linkid(addr_2, addr_1); const LogicalLink* l = nh_1.get_logical_link(lid); if (l->is_sym() == false) { DOUT(info) << "Failed to establish a symmetric link between " << "remote interface " << addr_2.str() << "\n" << "and local interface " << addr_1.str() << ".\n"; return false; } // Assert that OLSR2 is present in OLSR1's neighbor database, // and that the neighbor is symmetric. OlsrTypes::NeighborID nid = nh_1.get_neighborid_by_main_addr(addr_2); const Neighbor* n = nh_1.get_neighbor(nid); if (n->is_sym() == false) { DOUT(info) << addr_1.str() << " failed to see " << addr_2.str() << " as a symmetric neighbor.\n"; return false; } XLOG_ASSERT(n->willingness() == OlsrTypes::WILL_HIGH); } catch (XorpException& e) { DOUT(info) << " threw exception " << cstring(e) << "\n"; return false; } // Assert that OLSR2 sees OLSR1 correctly. try { // Assert that OLSR1's interface on the emulated subnet is present in // OLSR2's link database, and that the link is symmetric. OlsrTypes::LogicalLinkID lid = nh_2.get_linkid(addr_1, addr_2); const LogicalLink* l = nh_2.get_logical_link(lid); if (l->is_sym() == false) { DOUT(info) << "Failed to establish a symmetric link between " << "remote interface " << addr_1.str() << "\n" << "and local interface " << addr_2.str() << ".\n"; return false; } // Assert that OLSR1 is present in OLSR2's neighbor database, // and that the neighbor is symmetric. OlsrTypes::NeighborID nid = nh_2.get_neighborid_by_main_addr(addr_1); const Neighbor* n = nh_2.get_neighbor(nid); if (n->is_sym() == false) { DOUT(info) << addr_2.str() << " failed to see " << addr_1.str() << " as a symmetric neighbor.\n"; return false; } XLOG_ASSERT(n->willingness() == OlsrTypes::WILL_HIGH); } catch (XorpException& e) { DOUT(info) << " threw exception " << cstring(e) << "\n"; return false; } // Simulate neighbor loss by alternately disconnecting both OLSR // instances from the EmulateSubnet. This is similar to pulling the // Ethernet jack out or turning the 802.11 radios off below MAC layer. // // Run the event loop until 3 * NEIGH_HOLD_TIME expires. // Verify that both neighbors expire their links. // // TODO: Simulate the separate use case of interface withdrawal, // i.e. OLSR is unconfigured from an interface *or* it is marked // administratively down for OLSR. int expected_1 = io_1.packets() + 1; int expected_2 = io_2.packets() + 1; bool pulled_1 = false; bool pulled_2 = false; if (NOSTAGGER == stagger) { debug_msg("Pulling both ports.\n"); emu.unbind_interface("olsr1", interface_1, vif_1, addr_1, MY_OLSR_PORT, io_1); emu.unbind_interface("olsr2", interface_2, vif_2, addr_2, MY_OLSR_PORT, io_2); pulled_1 = pulled_2 = true; } timeout = false; t.clear(); t = eventloop.set_flag_after(3 * nh_1.get_neighbor_hold_time(), &timeout); while (!timeout) { eventloop.run(); if (!pulled_1 && STAGGER1 == stagger && expected_1 == io_1.packets()) { debug_msg("Pulling OLSR1.\n"); emu.unbind_interface("olsr1", interface_1, vif_1, addr_1, MY_OLSR_PORT, io_1); pulled_1 = true; } if (!pulled_2 && STAGGER2 == stagger && expected_2 == io_2.packets()) { debug_msg("Pulling OLSR2.\n"); emu.unbind_interface("olsr2", interface_2, vif_2, addr_2, MY_OLSR_PORT, io_2); pulled_2 = true; } } if (io_1.packets() + io_2.packets() < expected_1 + expected_2) { DOUT(info) << io_1.packets() + io_2.packets() << " packets sent, at least " << expected_1 + expected_2 << " expected\n"; return false; } // Assert that both neighbors no longer contain each other // in their link databases. bool thrown; // Assert that OLSR1 no longer sees OLSR2 as a neighbor. try { thrown = false; OlsrTypes::NeighborID nid = nh_1.get_neighborid_by_main_addr(addr_2); UNUSED(nid); } catch (XorpException& e) { thrown = true; } XLOG_ASSERT(thrown == true); // Assert that OLSR1 no longer sees a link between OLSR2's and OLSR1's // interface addresses. try { thrown = false; OlsrTypes::LogicalLinkID lid = nh_1.get_linkid(addr_2, addr_1); UNUSED(lid); } catch (XorpException& e) { thrown = true; } XLOG_ASSERT(thrown == true); // Assert that OLSR2 no longer sees OLSR1 as a neighbor. try { thrown = false; OlsrTypes::NeighborID nid = nh_2.get_neighborid_by_main_addr(addr_1); UNUSED(nid); } catch (XorpException& e) { thrown = true; } XLOG_ASSERT(thrown == true); // Assert that OLSR2 no longer sees a link between OLSR1's and OLSR2's // interface addresses. try { thrown = false; OlsrTypes::LogicalLinkID lid = nh_2.get_linkid(addr_1, addr_2); UNUSED(lid); } catch (XorpException& e) { thrown = true; } XLOG_ASSERT(thrown == true); // Delete the faces. if (!fm_1.delete_face(faceid_1)) { DOUT(info) << "Failed to delete face\n"; return false; } if (!fm_2.delete_face(faceid_2)) { DOUT(info) << "Failed to delete face\n"; return false; } // TODO: Assert that no leftover state remains. return true; } int main(int argc, char **argv) { XorpUnexpectedHandler x(xorp_unexpected_handler); xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); TestMain t(argc, argv); string test = t.get_optional_args("-t", "--test", "run only the specified test"); t.complete_args_parsing(); typedef XorpCallback1::RefPtr TestCB; struct test { string test_name; TestCB cb; } tests[] = { {"single_face", callback(single_face)}, {"two_faces", callback(two_faces, NOSTAGGER)}, {"two_faces_s1", callback(two_faces, STAGGER1)}, {"two_faces_s2", callback(two_faces, STAGGER2)}, }; try { if (test.empty()) { for (size_t i = 0; i < sizeof(tests) / sizeof(struct test); i++) t.run(tests[i].test_name, tests[i].cb); } else { for (size_t i = 0; i < sizeof(tests) / sizeof(struct test); i++) if (test == tests[i].test_name) { t.run(tests[i].test_name, tests[i].cb); return t.exit(); } t.failed("No test with name " + test + " found\n"); } } catch(...) { xorp_catch_standard_exceptions(); } xlog_stop(); xlog_exit(); return t.exit(); } xorp/contrib/olsr/neighbor.hh0000664000076400007640000002377211540225522016426 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8 sw=4: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/contrib/olsr/neighbor.hh,v 1.4 2008/10/02 21:56:35 bms Exp $ #ifndef __OLSR_NEIGHBOR_HH__ #define __OLSR_NEIGHBOR_HH__ class Neighborhood; /** * @short Wrapper for LogicalLink::is_sym(). * * As LogicalLinkID is an indirect reference, it must be looked up * via Neighborhood. * Model of UnaryPredicate. */ struct IsLinkSymmetricPred { Neighborhood* _nh; IsLinkSymmetricPred(Neighborhood* nh) : _nh(nh) {} /** * @return true if the link identified by linkid is symmetric. */ bool operator()(OlsrTypes::LogicalLinkID linkid); }; /** * @short Wrapper for TwoHopNeighbor::is_strict(). * * As TwoHopLinkID is an indirect reference, it must be looked up * via Neighborhood. * Model of UnaryPredicate. */ struct IsTwoHopLinkStrictPred { Neighborhood* _nh; IsTwoHopLinkStrictPred(Neighborhood* nh) : _nh(nh) {} /** * @return true if the two-hop neighbor referenced by tlid is a * strict two-hop neighbor. */ bool operator()(OlsrTypes::TwoHopLinkID tlid); }; /** * @short A one-hop neighbor. */ class Neighbor { public: Neighbor(EventLoop& ev, Neighborhood* parent, const OlsrTypes::NeighborID nid, const IPv4& main_addr, const OlsrTypes::LogicalLinkID linkid); inline OlsrTypes::NeighborID id() const { return _id; } inline const IPv4 main_addr() const { return _main_addr; } string toStringBrief(); OlsrTypes::NeighborType neighbor_type() const; /** * Get the willingness-to-forward for this neighbor. * * @return the neighbor's willingness. */ inline uint8_t willingness() const { return _willingness; } /** * Set the willingness-to-forward for this neighbor. * * This may trigger a recount of the node's MPR status; a node * with willingness WILL_NEVER may never be an MPR. * * @param value the new value for this neighbor's willingness property. */ void set_willingness(OlsrTypes::WillType value); /** * Return the MPR selector state of this neighbor. * * @return true if this neighbor has previously chosen this node as an * MPR in a HELLO message, and the tuple is still valid. */ bool is_mpr_selector() const; /** * Set the MPR selector state of this neighbor. * * @param value true if this neighbor selects this OLSR node as an MPR. * @param expiry_time The expiry time for MPR selector status. Ignored * if value is false. */ void set_is_mpr_selector(bool value, const TimeVal& expiry_time); /** * Return the previously stored result of MPR selection. * This is computed by the deferred MPR selection task in Neighborhood. * * @return true if this neighbor has been selected as an MPR. */ inline bool is_mpr() const { return _is_mpr; } inline void set_is_mpr(bool value) { _is_mpr = value; } /** * @return true if this neighbor is a persistent MPR candidate. */ inline bool is_persistent_cand_mpr() const { return _willingness == OlsrTypes::WILL_ALWAYS; } /** * Return the MPR candidacy of this neighbor. * MPR candidacy is computed whenever Neighbor's link state changes. * * TODO: NOTE WELL: If a neighbor is not reachable due to no good ETX * links, it MUST NOT be considered as an MPR candidate. Currently * the code does not take account of this. * * @return true if this neighbor is a candidate MPR, that is, * there is at least one link to a strict two-hop neighbor, * and willingness is not WILL_NEVER. */ bool is_cand_mpr(); inline bool is_sym() const { return _is_sym; } inline void set_is_sym(bool value) { _is_sym = value; } inline const set& links() const { return _links; } inline const set& twohop_links() const { return _twohop_links; } /** * Associate a link tuple with a Neighbor or update an * existing one. N_status is updated. * * @param linkid the ID of the link to associate with this Neighbor. */ void update_link(const OlsrTypes::LogicalLinkID linkid); /** * Disassociate a link tuple from a Neighbor. * N_status is updated. * * @param linkid the ID of the link to disassociate from this Neighbor. * * @return true if there are no links to this Neighbor and it should * now be deleted. */ bool delete_link(const OlsrTypes::LogicalLinkID linkid); /** * Associate a two-hop link with a Neighbor. * MPR candidacy and degree are updated. * * @param tlid the ID of the two-hop link to associate with this Neighbor. */ void add_twohop_link(const OlsrTypes::TwoHopLinkID tlid); /** * Disassociate a two-hop link from a Neighbor. * MPR candidacy and degree are updated. * * @param tlid the ID of the two-hop link to disassociate from * this Neighbor. * @return true if this Neighbor has no more two-hop links. */ bool delete_twohop_link(const OlsrTypes::TwoHopLinkID tlid); /** * Disassociate all two-hop links from a Neighbor. * MPR candidacy and degree are updated. * * @return the number of links which have been deleted. */ size_t delete_all_twohop_links(); /** * Callback method to: process expiry of an MPR selector tuple. */ void event_mpr_selector_expired(); /** * Return the degree of this neighbor. * * @return The number of symmetric neighbors of this neighbor, * excluding this node, and other one-hop neighbors. */ inline uint32_t degree() const { return _degree; } /** * Return the previously computed reachability of this neighbor. * * @return The number of strict, uncovered, two-hop neighbors to which * this neighbor has a link. */ inline uint32_t reachability() const { return _reachability; } /** * Store the reachability of this neighbor. * * Typically called during MPR selection, as reachability can only * be computed during MPR selection. * * @param value The number of strict, uncovered, two-hop neighbors * to which this neighbor has a link. */ inline void set_reachability(uint32_t value) { _reachability = value; } /** * Return if this neighbor is advertised in TC messages. * * @return true if this neighbor is advertised in TC messages. */ inline bool is_advertised() const { return _is_advertised; } /** * Store the "is in advertised neighbor set" status. * * @param value true if this neighbor is advertised in TC. */ inline void set_is_advertised(bool value) { _is_advertised = value; } protected: /** * Re-count the degree of this strict one-hop Neighbor. * * Section 8.3.1 defines the degree of a one-hop neighbor as the number * of symmetric neighbors of the neighbor, excluding any members of the * one-hop neighborhood, and excluding this node. * * Triggered by a two-hop link state change. * TODO: Distribute the computation by pushing the responsibility for * signalling the change of state to TwoHopNeighbor, rather than * doing it here. */ void recount_degree(); /** * Update the MPR candidacy of this neighbor. * * Triggered by a one-hop or two-hop link state change. * The Neighborhood is notified of the change. * * @param was_cand_mpr true if the node was an MPR candidate before * any other criteria changed. * @return true if the node is now an MPR candidate. */ bool update_cand_mpr(bool was_cand_mpr); private: EventLoop& _eventloop; Neighborhood* _parent; OlsrTypes::NeighborID _id; /** * @short main address of this neighbor. */ IPv4 _main_addr; /** * @short true if neighbor is an MPR. * * Note: This may be out of sync with the actual MPR state; it * does not get updated until an MPR selection is performed * from Neighborhood (the computation is not fully distributed). */ bool _is_mpr; /** * @short true if neighbor is symmetric. */ bool _is_sym; /** * @short willingness to forward traffic. */ OlsrTypes::WillType _willingness; /** * @short the degree of this neighbor. */ uint32_t _degree; /** * @short the reachability of this neighbor. */ uint32_t _reachability; /** * @short true if neighbor is advertised in TC messages. */ bool _is_advertised; /** * @short A timer which is scheduled if and only if the neighbor * selects this node as an MPR. */ XorpTimer _mpr_selector_timer; /** * @short Links which reference this neighbor. */ set _links; /** * @short Links to all N2, strict and non-strict, reachable via this N. */ set _twohop_links; }; #endif // __OLSR_NEIGHBOR_HH__ xorp/contrib/olsr/xrl_port.hh0000664000076400007640000001277211565300035016500 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8 sw=4: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/contrib/olsr/xrl_port.hh,v 1.3 2008/10/02 21:56:37 bms Exp $ #ifndef __OLSR_XRL_PORT_HH__ #define __OLSR_XRL_PORT_HH__ #include "libxorp/ipv4.hh" #include "libxorp/safe_callback_obj.hh" #include "libxorp/service.hh" class XrlError; class XrlRouter; /** * @short Helper class which encapsulates XRL socket service * * Deals with setting the specific XRL socket options that OLSR * needs to send/receive broadcast/multicast control traffic to * the all-nodes address. */ class XrlPort : public ServiceBase, public CallbackSafeObject { public: /** * Begin creation of the broadcast/multicast socket. * * @param io pointer to parent object * @param eventloop process-wide event loop * @param xrl_router process-wide XRL router * @param ssname name of XRL target containing socket server; * usually this is the FEA. * @param ifname interface to listen on * @param vifname vif to listen on * @param local_addr address to listen on * @param local_port port to listen on * @param all_nodes_addr address to send to */ XrlPort(IO* io, EventLoop& eventloop, XrlRouter& xrl_router, const string& ssname, const string& ifname, const string& vifname, const IPv4& local_addr, const uint16_t local_port, const IPv4& all_nodes_addr); ~XrlPort(); /** * Start the port binding. * * Sends request to FEA for socket server for address * and then attempts to instantiate socket with socket server. If * both operations are successful, instance status transitions to * SERVICE_RUNNING. Otherwise, it transitions to failed. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int startup(); /** * Shutdown the port binding. * * Sends request to close socket and transitions into * SERVICE_SHUTTING_DOWN state. When socket is closed transition to * SERVICE_SHUTDOWN occurs. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int shutdown(); /** * Send packet. * * Status of instance must be running, and should not be pending. * * @param dst_addr address to send packet. * @param dst_port port to send packet to. * @param payload vector containing paylaod of packet to be sent. * * @return false on immediately detectable failure, true otherwise. */ bool send_to(const IPv4& dst_addr, const uint16_t dst_port, const vector& payload); /** * @return the name of the socket server in use. */ inline string socket_server() const { return _ss; } /** * @return the name of the interface to which this socket is bound. */ inline string ifname() const { return _ifname; } /** * @return the name of the vif to which this socket is bound. */ inline string vifname() const { return _vifname; } /** * @return the address to which this socket is bound. */ inline IPv4 local_address() const { return _local_addr; } /** * @return the port to which this socket is bound. */ inline uint16_t local_port() const { return _local_port; } /** * @return the socket server's socket identifier, * if service status is RUNNING; otherwise undefined. */ inline string sockid() const { return _sockid; } /** * @return the address to which this socket transmits. */ inline IPv4 all_nodes_address() const { return _all_nodes_addr; } private: bool startup_socket(); // // Socket open operations. // bool request_udp_open_bind_broadcast(); void udp_open_bind_broadcast_cb(const XrlError& e, const string* psid); bool request_tos(); void tos_cb(const XrlError& xrl_error); void socket_setup_complete(); // // BSD hack. // bool request_reuseport(); void reuseport_cb(const XrlError& e); bool request_onesbcast(const bool enabled); void onesbcast_cb(const XrlError& e); // // Socket close operations. // bool request_close(); void close_cb(const XrlError& xrl_error); // // Socket send operations. // void send_cb(const XrlError& xrl_error); private: IO* _io; EventLoop& _eventloop; XrlRouter& _xrl_router; string _ss; string _ifname; string _vifname; IPv4 _local_addr; uint16_t _local_port; IPv4 _all_nodes_addr; /* If true, system is initializing and cannot send normal packets with send_to */ bool _pending; string _sockid; bool _is_undirected_broadcast; }; #endif // __OLSR_XRL_PORT_HH__ xorp/contrib/olsr/olsr_types.hh0000664000076400007640000002360211421137511017022 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8 sw=4: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/contrib/olsr/olsr_types.hh,v 1.5 2008/10/02 21:56:35 bms Exp $ #ifndef __OLSR_OLSR_TYPES_HH__ #define __OLSR_OLSR_TYPES_HH__ /** * @short A scope to hold primitive types specific to OLSR. * * These are named exactly as they are in RFC 3626, to avoid * any confusion. */ struct OlsrTypes { /** * @short The default static cost of an OLSR interface. */ static const int DEFAULT_STATIC_FACE_COST = 0; /** * @short The maximum time-to-live of an OLSR message. */ static const int MAX_TTL = 255; /* * Constants from Section 18.2. */ /** * @short The default interval between HELLO advertisements, in seconds. */ static const int DEFAULT_HELLO_INTERVAL = 2; /** * @short The default interval between link advertisements, in seconds. * Used in situations where HELLO advertisements are segmented. */ static const int DEFAULT_REFRESH_INTERVAL = 2; /** * @short The default interval between TC broadcasts, in seconds. */ static const int DEFAULT_TC_INTERVAL = 5; /** * @short The default lifetime for each histogram-based duplicate * message detection entry. */ static const int DEFAULT_DUP_HOLD_TIME = 30; /** * @short The default interval between MID broadcasts, in seconds. */ static const int DEFAULT_MID_INTERVAL = DEFAULT_TC_INTERVAL; /** * @short The default interval between HNA broadcasts, in seconds. */ static const int DEFAULT_HNA_INTERVAL = DEFAULT_TC_INTERVAL; /** * @short The type of an OLSR external route ID. */ typedef uint32_t ExternalID; static const ExternalID UNUSED_EXTERNAL_ID = 0; /** * @short The type of an OLSR interface ID. */ typedef uint32_t FaceID; static const FaceID UNUSED_FACE_ID = 0; /* * Defaults controlling link hysteresis defined in link.cc. * Used to derive link quality when RFC-compliant * link hysteresis is enabled. */ /** * The default link hysteresis high threshold. */ static double DEFAULT_HYST_THRESHOLD_HIGH; /** * The default link hysteresis low threshold. */ static double DEFAULT_HYST_THRESHOLD_LOW; /** * The default link hysteresis scaling factor. */ static double DEFAULT_HYST_SCALING; /** * @short The type of an OLSR link, as found in a HELLO message. */ typedef uint8_t LinkType; enum LinkTypes { UNSPEC_LINK = 0, ASYM_LINK = 1, SYM_LINK = 2, LOST_LINK = 3, // May be used as a vector or array index. LINKTYPE_START = UNSPEC_LINK, LINKTYPE_END = LOST_LINK }; static const int NUM_LINKTYPE = LINKTYPE_END - LINKTYPE_START + 1; /** * @short The type of an OLSR link ID. */ typedef uint32_t LogicalLinkID; static const LogicalLinkID UNUSED_LINK_ID = 0; /** * @short The type of an OLSR protocol message. */ typedef uint8_t MessageType; enum MessageTypes { HELLO_MESSAGE = 1, TC_MESSAGE = 2, MID_MESSAGE = 3, HNA_MESSAGE = 4, LQ_HELLO_MESSAGE = 201, // HELLO with ETX measurements. LQ_TC_MESSAGE = 202 // TC with ETX measurements. }; /** * @short The type of a multiple interface descriptor (MID) ID. */ typedef uint32_t MidEntryID; static const MidEntryID UNUSED_MID_ID = 0; /** * @short The type of an OLSR neighbor ID. */ typedef uint32_t NeighborID; static const NeighborID UNUSED_NEIGHBOR_ID = 0; /** * @short The type of an OLSR neighbor, as found in a HELLO message. */ typedef uint8_t NeighborType; enum NeighborTypes { NOT_NEIGH = 0, SYM_NEIGH = 1, MPR_NEIGH = 2, // May be used as a vector or array index. NEIGHBORTYPE_START = NOT_NEIGH, NEIGHBORTYPE_END = MPR_NEIGH, }; static const int NUM_NEIGHBORTYPE = NEIGHBORTYPE_END - NEIGHBORTYPE_START + 1; /** * @short The default UDP port for the OLSR protocol. */ static const uint16_t DEFAULT_OLSR_PORT = 698; /** * @short Type representing a node's willingness to forward. */ typedef uint8_t WillType; enum Willingness { WILL_NEVER = 0, WILL_LOW = 1, WILL_DEFAULT = 3, WILL_HIGH = 6, WILL_ALWAYS = 7, // limits WILL_MIN = WILL_LOW, WILL_MAX = WILL_ALWAYS, // bounds WILL_START = WILL_NEVER, WILL_END = WILL_ALWAYS }; /** * @short Type representing a node's TC redundancy mode. * Section 15.1: TC_REDUNDANCY parameter. */ typedef uint8_t TcRedundancyType; enum TcRedundancyMode { TCR_MPRS_IN = 0, // MPR selectors only TCR_MPRS_INOUT = 1, // MPR selectors and MPRs TCR_ALL = 2, // The full neighbor set. // bounds TCR_START = TCR_MPRS_IN, TCR_END = TCR_ALL }; /** * @short The default TC_REDUNDANCY value. */ static const uint8_t DEFAULT_TC_REDUNDANCY = TCR_MPRS_IN; /** * @short The default number of MPRs which must cover a two-hop * neighbor when the OLSR MPR selection algorithm is enabled. */ static const uint8_t DEFAULT_MPR_COVERAGE = 1; /** * @short The type of an OLSR topology set entry ID. */ typedef uint32_t TopologyID; static const TopologyID UNUSED_TOPOLOGY_ID = 0; /** * @short The type of an OLSR two-hop node ID. */ typedef uint32_t TwoHopNodeID; static const TwoHopNodeID UNUSED_TWOHOP_NODE_ID = 0; /** * @short The type of an OLSR two-hop link ID. */ typedef uint32_t TwoHopLinkID; static const TwoHopLinkID UNUSED_TWOHOP_LINK_ID = 0; /** * @short Type representing the type of a Vertex in the * shortest-path-tree. * * SPT is a meta-class which embeds the Vertex as a POD type, * therefore, inheritance cannot be used to represent the vertex type. */ enum VertexType { VT_ORIGINATOR = 0, // iff origin() is true VT_UNKNOWN = 0, VT_NEIGHBOR = 1, // Nodes at radius 1. VT_TWOHOP = 2, // Nodes at radius 2. VT_TOPOLOGY = 3, VT_MID = 4, // Not a vertex type; used by routing table VT_HNA = 5, // do. // bounds VT_START = VT_ORIGINATOR, VT_END = VT_HNA }; /** * @short Type fields used when saving OLSR databases. */ enum TlvType { TLV_VERSION = 1, // Version number (u32) TLV_SYSTEM_INFO = 2, // Where created (string) TLV_OLSR_VERSION = 3, // Version of OLSR in use (u32) TLV_OLSR_FAMILY = 4, // Protocol family (u32) TLV_OLSR_N1 = 5, // One-hop neighbor TLV_OLSR_L1 = 6, // One-hop link TLV_OLSR_N2 = 7, // Two-hop neighbor TLV_OLSR_L2 = 8, // Two-hop link TLV_OLSR_MID = 9, // MID entry TLV_OLSR_TC = 10, // Topology Control entry TLV_OLSR_HNA = 11, // HNA entry }; }; /** * @return true if the given vertex type maps directly to a single * OLSR node. */ inline bool is_node_vertex(const OlsrTypes::VertexType vt) { if (vt == OlsrTypes::VT_NEIGHBOR || vt == OlsrTypes::VT_TWOHOP || vt == OlsrTypes::VT_TOPOLOGY || vt == OlsrTypes::VT_MID) return true; return false; } /** * @short Helper class to encode a TimeVal as an 8 bit binary * floating point value. */ class EightBitTime { public: /** * @short Helper function to convert from a TimeVal * to the eight-bit floating point format used by OLSR. */ static inline TimeVal to_timeval(const uint8_t byte) { unsigned int mant = byte >> 4; unsigned int exp = byte & 0x0F; double sec = ((16 + mant) << exp) * _scaling_factor / 16.0; return (TimeVal(sec)); } /** * @short Helper function to convert the eight-bit floating point * format used by OLSR to a TimeVal. */ static inline uint8_t from_timeval(const TimeVal& tv) { double sec = tv.get_double(); int isec = static_cast(sec / _scaling_factor); int mant = 0; int exp = 0; while (isec >= (1 << exp)) exp++; if (exp == 0) { mant = 1; } else { exp--; if (mant > (_mod - 1)) { // ceiling mant = exp = (_mod - 1); } else { mant = static_cast(_mod * sec / _scaling_factor / (1 << exp) - _mod); exp += mant >> 4; mant &= 0x0F; } } return (static_cast(mant << 4 | (exp & 0x0F))); } private: static const int _mod = 16; static const double _scaling_factor; }; /** * Helper function to compare two sequence numbers. * RFC 3626 Section 19: Sequence Numbers. * * @param seq1 An OLSR sequence number * @param seq2 An OLSR sequence number * @return true if seq1 is newer than seq2. */ inline bool is_seq_newer(const uint16_t seq1, const uint16_t seq2) { // UINT16_MAX is defined in C99 stdint.h, however this is not // part of the C++ Standard yet. static const uint16_t uint16_max = 65535; return (seq1 > seq2 && seq1 - seq2 <= uint16_max/2) || (seq2 > seq1 && seq2 - seq1 > uint16_max/2); } /** * @short Return a C string describing a topology control flooding mode. */ const char* tcr_to_str(const OlsrTypes::TcRedundancyType t); /** * @short Return a C string describing a willingness-to-forward mode. */ const char* will_to_str(const OlsrTypes::WillType t); /** * @return a C string describing a vertex type. */ const char* vt_to_str(const OlsrTypes::VertexType vt); #endif // __OLSR_OLSR_TYPES_HH__ xorp/contrib/olsr/TODO0000664000076400007640000001131211421137511014761 0ustar greearbgreearb# # $XORP: xorp/contrib/olsr/TODO,v 1.1 2008/04/24 15:19:51 bms Exp $ # TODO ~~~~ * Separate regression tests are needed to deal with the "MPR computation disabled" and "WILL_ALWAYS" scenarios, as these blow a lot of the assumptions in the current tests out of the water. * Teach XrlIO about socket IDs. Currently it relies on if_name and vif_name being set to sane values by the FEA, which is not guaranteed. * Teach XrlPort about link-scope multicast sockets. Production deployments of OLSR are potentially using 255.255.255.255 instead of a 224.0.0.0/24 MANET prefix. There are platform issues with undirected broadcasts; some implementations of the BSD socket API, which are strictly conformant to it, may not support IP broadcasts correctly. * Rewrite the code in XrlIO which tracks service startup to be more coherent; in particular we should track service dependencies in a more structured way, rather than relying on a simple integer count. * Fix issue whereby FEA will not pass if_name/vif_name correctly for interfaces added on-the-fly to the interfaces { } router manager configuration (this is a showstopper for hot swappable network interfaces). * Fix policy route filtering. Basic redistribution of everything OLSR learns into RIP is known to work, but finer tuning of policy redist needs wider exposure and testing. * ETX metric support. * Add jitter to message transmission. * Add support for segmenting, enqueuing and transmitting packets according to interface MTU. * Currently 1500 (Ethernet) is assumed in regression tests. Packets sent will be truncated to 1..N messages which will fit in an OLSR datagram which fits inside the interface's MTU, as discovered from the IfMgr tree. * Bring profiling back when it's needed. * IPv6 support (requires templatization). * Figure out what to do about the situation with link-local IPv4 and IPv6 where more than one interface may in fact have the same numeric address- even though the addresses are link scoped. This screws up the concept of an OLSR "main address" as a separate unique node ID is then needed. * Introduce jitter into outgoing messages/packets. * Fully document class Message and class Packet in doxygen. * Add a means of configuring the OLSR admin distance dynamically. * Add support for writing the database(s) out in tools/print_databases in Tlv format. * Whilst the RIB does not support equal cost paths: Tie break multiple routes before they get pushed to the RIB. Prefer the nearest routes ie the ones learned using the most cohesive topology mechanisms. MOVE TO GENERAL XORP TODO ~~~~~~~~~~~~~~~~~~~~~~~~~ * Fix valgrind memcheck hits in libxorp TaskList and TimerList. * Fix valgrind hit in Neighborhood::Neighborhood(): it is believed that the unschedule_node() call is potentially causing a minor leak. The only reason this call is there is to stop the MPR recount task being triggered prematurely. * The runtime buffers allocated by BufferedAsyncReader are relatively high (256KB). Look to reduce the memory footprint for embedded systems. FUTURE ~~~~~~ * Add a means of allowing external entities to register interest in OLSR protocol events using XRLs. With this you can build dynamic topology visualization tools. * Implement fisheye in TC. * Add sugar to source to enable OLSR to be compiled without regression testing support in production/embedded builds. * Add individual commands to: * clear links * clear neighbours * clear topology entries * Consider moving MPR selection to its own class. * Support multiple MPR selection algorithms. * Teach code to find unused link and neighbor IDs, and to cleanly recycle old ones, if we see cycling. * Note: The IDs which recycle most rapidly are the Topology Control entry IDs, however, they aren't likely to wrap unless you have a *very* dynamically varying topology. * Randomize packet and message sequence numbers on startup. * Add support for Secured OLSR messages, as documented in "Secure Extension to the OLSR Protocol", Hafslund et al, 2004. * Support MD5 and SHA-1 hashes using system's openssl library. * This changes the encoding slightly; handlers are needed for message types 11/12/13 for its per-hop challenge response. * Signatures are per hop only and per-packet -- see how OSPF's MD5 auth works - the hash is over adjusted packet header fields which is tricky for us, because it means the entire packet EXCEPT for the sig has to be copied out before hashing. * The signature is appended as the last message (type 10) in a Packet; as such, the size of the sig has to be taken into account in the packet if signing is active. * Support more than one protocol address per physical interface. xorp/contrib/olsr/face_manager.cc0000664000076400007640000010077511613327030017204 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8 sw=4: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "olsr_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/callback.hh" #include "libxorp/ipv4.hh" #include "libxorp/ipv4net.hh" #include "libxorp/status_codes.h" #include "libxorp/service.hh" #include "libxorp/eventloop.hh" #include "olsr.hh" #include "face_manager.hh" #include "face.hh" // #define DEBUG_LOGGING // #define DEBUG_FUNCTION_NAME // #define DETAILED_DEBUG FaceManager::FaceManager(Olsr& olsr, EventLoop& ev) : _olsr(olsr), _eventloop(ev), _nh(0), _next_faceid(1), _enabled_face_count(0), _next_msg_seqno(1), _hello_interval(TimeVal(OlsrTypes::DEFAULT_HELLO_INTERVAL, 0)), _mid_interval(TimeVal(OlsrTypes::DEFAULT_MID_INTERVAL, 0)), _dup_hold_time(TimeVal(OlsrTypes::DEFAULT_DUP_HOLD_TIME, 0)), _is_early_mid_enabled(false) { initialize_message_decoder(_md); add_message_cb(callback(this, &FaceManager::event_receive_unknown)); } FaceManager::~FaceManager() { // Timers may be scheduled even if we never got a slice. stop_all_timers(); clear_dupetuples(); clear_faces(); XLOG_ASSERT(_faces.empty()); XLOG_ASSERT(_duplicate_set.empty()); delete_message_cb(callback(this, &FaceManager::event_receive_unknown)); // TODO: Fix this assertion (minor leak) //XLOG_ASSERT(_handlers.empty()); } const Face* FaceManager::get_face_by_id(const OlsrTypes::FaceID faceid) const throw(BadFace) { map::const_iterator ii = _faces.find(faceid); if (ii == _faces.end()) { xorp_throw(BadFace, c_format("Mapping for %u does not exist", XORP_UINT_CAST(faceid))); } return (*ii).second; } void FaceManager::get_face_list(list& face_list) const { map::const_iterator ii; for (ii = _faces.begin(); ii != _faces.end(); ii++) face_list.push_back((*ii).first); } void FaceManager::set_hello_interval(const TimeVal& interval) { if (interval == _hello_interval) return; debug_msg("%s: setting HELLO_INTERVAL to %s\n", cstring(get_main_addr()), cstring(interval)); _hello_interval = interval; if (_hello_timer.scheduled()) { reschedule_hello_timer(); } } void FaceManager::set_mid_interval(const TimeVal& interval) { if (interval == _mid_interval) return; debug_msg("%s: setting MID_INTERVAL to %s\n", cstring(get_main_addr()), cstring(interval)); _mid_interval = interval; if (_mid_timer.scheduled()) { reschedule_mid_timer(); } } void FaceManager::clear_dupetuples() { DupeTupleMap::iterator ii, jj; for (ii = _duplicate_set.begin(); ii != _duplicate_set.end(); ) { jj = ii++; delete (*jj).second; _duplicate_set.erase(jj); } } void FaceManager::clear_faces() { map::iterator ii, jj; for (ii = _faces.begin(); ii != _faces.end(); ) { jj = ii++; delete (*jj).second; _faces.erase(jj); } } void FaceManager::receive(const string& interface, const string& vif, const IPv4 & dst, const uint16_t & dport, const IPv4 & src, const uint16_t & sport, uint8_t* data, const uint32_t & len) { debug_msg("If %s Vif %s Dst %s Dport %u Src %s Sport %u Len %u\n", interface.c_str(), vif.c_str(), cstring(dst), dport, cstring(src), sport, len); // We must find a link which matches, to accept the packet. OlsrTypes::FaceID faceid; try { faceid = get_faceid(interface, vif); } catch(...) { // No Face listening on the given link. return; } XLOG_ASSERT(_faces.find(faceid) != _faces.end()); Face* face = _faces[faceid]; if (! face->enabled()) { debug_msg("dropping %p(%u) as face %s/%s is disabled.\n", data, XORP_UINT_CAST(len), interface.c_str(), vif.c_str()); return; // not enabled, therefore not listening. } #if 0 // These checks are normally stubbed out, as it isn't possible // to determine the destination address or port using the socket4 xif. // Check for a port match, to accept this packet. // Strictly this is only needed if we're running in the simulator. if (dport != face->local_port()) return; // Check for a match with the configured broadcast address to // accept this packet. if (dst != face->all_nodes_addr()) return; #endif Packet* pkt = new Packet(_md, faceid); try { pkt->decode(data, len); } catch (InvalidPacket& e) { _faces[faceid]->counters().incr_bad_packets(); debug_msg("Packet from %s:%u could not be decoded.\n", cstring(src), XORP_UINT_CAST(sport)); XLOG_TRACE(_olsr.trace()._input_errors, "Packet from %s:%u could not be decoded.", cstring(src), XORP_UINT_CAST(sport)); return; } // An OLSR control packet may contain several control messages. // Demultiplex them and process them accordigly. const vector& messages = pkt->messages(); vector::const_iterator ii; for (ii = messages.begin(); ii != messages.end(); ii++) { Message* msg = (*ii); // 3.4, 2: Messages from myself must be silently dropped. if (msg->origin() == get_main_addr()) { _faces[faceid]->counters().incr_messages_from_self(); // XXX refcounting delete msg; continue; } // 3.4, 3.1: If the message has been recorded in the duplicate // set, it should not be processed. if (is_duplicate_message(msg)) { _faces[faceid]->counters().incr_duplicates(); // XXX refcounting delete msg; continue; } bool is_consumed = false; // Walk the list of message handler functions in reverse, // looking for one which is willing to consume this message. vector::reverse_iterator jj; for (jj = _handlers.rbegin(); jj != _handlers.rend(); jj++) { try { is_consumed = (*jj)->dispatch(msg, src, face->local_addr()); if (is_consumed) break; } catch (InvalidMessage& im) { _faces[faceid]->counters().incr_bad_messages(); XLOG_ERROR("%s", cstring(im)); } } // XXX This is here because we didn't implement // refcounting properly. We have to remember to delete the // message as we can't rely on Packet, or indeed forward_message(), // doing this for us. delete msg; // The message must be consumed at least by the UnknownMessage // handler, which floods messages according to the default // forwarding rules. if (! is_consumed) { XLOG_UNREACHABLE(); } } delete pkt; } bool FaceManager::transmit(const string& interface, const string& vif, const IPv4 & dst, const uint16_t & dport, const IPv4 & src, const uint16_t & sport, uint8_t* data, const uint32_t & len) { debug_msg("If %s Vif %s Dst %s Dport %u Src %s Sport %u Len %u\n", interface.c_str(), vif.c_str(), cstring(dst), dport, cstring(src), sport, len); return _olsr.transmit(interface, vif, dst, dport, src, sport, data, len); } OlsrTypes::FaceID FaceManager::create_face(const string& interface, const string& vif) throw(BadFace) { debug_msg("Interface %s Vif %s\n", interface.c_str(), vif.c_str()); string concat = interface + "/" + vif; if (_faceid_map.find(concat) != _faceid_map.end()) { xorp_throw(BadFace, c_format("Mapping for %s already exists", concat.c_str())); } OlsrTypes::FaceID faceid = _next_faceid++; _faceid_map[concat] = faceid; _faces[faceid] = new Face(_olsr, *this, _nh, _md, interface, vif, faceid); // // Register the interface/vif/address status callbacks. // // This only needs to be done once per invocation of xorp_olsr. // It is safe to do this once the interface manager service // has finished starting up in XrlIO. // // FaceManager::create_face() does not get called until this // happens, so OK to do this here. // _olsr.register_vif_status(callback(this, &FaceManager:: vif_status_change)); _olsr.register_address_status(callback(this, &FaceManager:: address_status_change)); return faceid; } bool FaceManager::activate_face(const string& interface, const string& vif) { debug_msg("Interface %s Vif %s\n", interface.c_str(), vif.c_str()); try { get_faceid(interface, vif); } catch(...) { return false; } return true; } bool FaceManager::delete_face(OlsrTypes::FaceID faceid) { debug_msg("FaceID %u\n", faceid); if (_faces.find(faceid) == _faces.end()) { XLOG_ERROR("Unknown FaceID %u", faceid); return false; } set_face_enabled(faceid, false); delete _faces[faceid]; _faces.erase(_faces.find(faceid)); map::iterator ii; for (ii = _faceid_map.begin(); ii != _faceid_map.end(); ii++) { if ((*ii).second == faceid) { _faceid_map.erase(ii); break; } } return true; } // TODO: Support advertising the other protocol addresses which // this OLSR node has been configured with on each link in MID. bool FaceManager::recompute_addresses_face(OlsrTypes::FaceID faceid) { #ifdef notyet debug_msg("FaceID %u\n", faceid); map::iterator ii = _faces.find(faceid); if (ii == _faces.end()) { XLOG_ERROR("Unable to find interface/vif associated with " "FaceID %u", XORP_UINT_CAST(faceid)); return false; } Face* face = (*ii).second; const string& interface = face->interface(); const string& vif = face->vif(); const IPv4& address = face->local_address(); // Before trying to get the addresses, verify that this // interface/vif exists in the FEA, and that the address we have // been configured with is usable. if (! _olsr.is_address_enabled(interface, vif, address)) return false; // Get all protocol addresses for this interface/vif. list addresses; if (! _olsr.get_addresses(interface, vif, addresses)) { XLOG_ERROR("Unable to find addresses on %s/%s ", interface.c_str(), vif.c_str()); return false; } list::iterator ii; for (ii = addresses.begin(); ii != addresses.end(); ii++) { // Skip linklocal addresses. if ((*i).is_linklocal_unicast()) continue; // Add the given address to MID. } // TODO: Force readvertisement of MID. return true; #else return true; UNUSED(faceid); #endif } void FaceManager::vif_status_change(const string& interface, const string& vif, bool state) { debug_msg("interface %s vif %s state %s\n", interface.c_str(), vif.c_str(), bool_c_str(state)); OlsrTypes::FaceID faceid; try { faceid = get_faceid(interface, vif); } catch(...) { return; } if (_faces.find(faceid) == _faces.end()) { XLOG_ERROR("Unknown FaceID %u", faceid); return; } // TODO: Process link status in Face. return; } void FaceManager::address_status_change(const string& interface, const string& vif, IPv4 addr, bool state) { debug_msg("interface %s vif %s addr %s state %s\n", interface.c_str(), vif.c_str(), cstring(addr), bool_c_str(state)); OlsrTypes::FaceID faceid; try { faceid = get_faceid(interface, vif); } catch(...) { return; } if (_faces.find(faceid) == _faces.end()) { XLOG_ERROR("Unknown FaceID %u", faceid); return; } // TODO: Process link status in Face. #ifdef notyet // XXX This may involve selecting a new main address for the OLSR node. if (false == recompute_addresses_face(faceid)) { XLOG_ERROR("Failed to recompute addresses for FaceID %u", faceid); } #endif return; } OlsrTypes::FaceID FaceManager::get_faceid(const string& interface, const string& vif) throw(BadFace) { debug_msg("Interface %s Vif %s\n", interface.c_str(), vif.c_str()); string concat = interface + "/" + vif; if (_faceid_map.find(concat) == _faceid_map.end()) { xorp_throw(BadFace, c_format("No mapping for %s exists", concat.c_str())); } return _faceid_map[concat]; } bool FaceManager::get_interface_vif_by_faceid(OlsrTypes::FaceID faceid, string & interface, string & vif) { debug_msg("FaceID %u\n", faceid); bool is_found = false; map::iterator ii; for (ii = _faceid_map.begin(); ii != _faceid_map.end(); ii++) { if ((*ii).second == faceid) { const string* concat = &((*ii).first); string::size_type n = concat->find_first_of("/"); interface = concat->substr(0, n); vif = concat->substr(n + 1); is_found = true; break; } } return is_found; } bool FaceManager::is_local_addr(const IPv4& addr) { bool is_found = false; map::const_iterator ii; for (ii = _faces.begin(); ii != _faces.end(); ii++) { Face* f = (*ii).second; if (f->local_addr() == addr) { is_found = true; break; } } return is_found; } bool FaceManager::get_face_stats(const string& ifname, const string& vifname, FaceCounters& stats) { try { OlsrTypes::FaceID faceid = get_faceid(ifname, vifname); const Face* face = get_face_by_id(faceid); stats = face->counters(); return true; } catch (...) {} return false; } bool FaceManager::get_interface_cost(OlsrTypes::FaceID faceid, int& cost) { debug_msg("FaceID %u\n", faceid); XLOG_ASSERT(_faces.find(faceid) != _faces.end()); Face* face = _faces[faceid]; bool is_found = true; cost = face->cost(); return is_found; } bool FaceManager::set_face_enabled(OlsrTypes::FaceID faceid, bool enabled) { debug_msg("FaceID %u\n", faceid); if (_faces.find(faceid) == _faces.end()) { XLOG_ERROR("Unknown FaceID %u", faceid); return false; } Face* face = _faces[faceid]; if (enabled == face->enabled()) return true; // // Check that the interface is capable of using the configured // means of addressing all OLSR nodes. // if (true == enabled) { bool try_multicast = face->all_nodes_addr().is_multicast(); bool has_capability = false; if (try_multicast) { has_capability =_olsr.is_vif_multicast_capable( face->interface(), face->vif()); } else { has_capability =_olsr.is_vif_broadcast_capable( face->interface(), face->vif()); } if (! has_capability) { XLOG_WARNING("%s/%s is not a %scast capable interface", face->interface().c_str(), face->vif().c_str(), try_multicast ? "multi" : "broad"); } } // // Now actually request the binding to the underlying address. // FaceManager has no direct knowledge of IO, therefore we // request this through Olsr's facade. // // TODO: Join/leave multicast groups if required. // if (true == enabled) { // // Learn interface MTU from IO. // TODO: Size outgoing message queue as appropriate. // uint32_t mtu = _olsr.get_mtu(face->interface()); face->set_mtu(mtu); bool success = _olsr.enable_address(face->interface(), face->vif(), face->local_addr(), face->local_port(), face->all_nodes_addr()); if (! success) { XLOG_ERROR("Failed to bring up I/O layer for %s/%s\n", face->interface().c_str(), face->vif().c_str()); return false; } } else { bool success = _olsr.disable_address(face->interface(), face->vif(), face->local_addr(), face->local_port()); if (! success) { XLOG_WARNING("Failed to shutdown I/O layer for %s/%s\n", face->interface().c_str(), face->vif().c_str()); } } // Mark the face status. face->set_enabled(enabled); XLOG_TRACE(_olsr.trace()._interface_events, "Interface %s/%s is now %s.", face->interface().c_str(), face->vif().c_str(), enabled ? "up" : "down"); if (enabled) { _enabled_face_count++; debug_msg("_enabled_face_count now %u\n", _enabled_face_count); if (_enabled_face_count == 1) { start_hello_timer(); } else if (_enabled_face_count > 1) { if (_enabled_face_count == 2) start_mid_timer(); // If configured to trigger a MID as soon as each new // interface is administratively up, do so. if (_is_early_mid_enabled) reschedule_immediate_mid_timer(); } } else { --_enabled_face_count; debug_msg("_enabled_face_count now %u\n", _enabled_face_count); if (_enabled_face_count == 1) { stop_mid_timer(); } else if (_enabled_face_count == 0) { stop_hello_timer(); } } return true; } bool FaceManager::get_face_enabled(OlsrTypes::FaceID faceid) { debug_msg("FaceID %u\n", faceid); if (_faces.find(faceid) == _faces.end()) { XLOG_ERROR("Unknown FaceID %u", faceid); return false; } return _faces[faceid]->enabled(); } bool FaceManager::set_interface_cost(OlsrTypes::FaceID faceid, int cost) { debug_msg("FaceID %u\n", faceid); if (_faces.find(faceid) == _faces.end()) { XLOG_ERROR("Unknown FaceID %u", faceid); return false; } _faces[faceid]->set_cost(cost); return true; } bool FaceManager::set_local_addr(OlsrTypes::FaceID faceid, const IPv4& local_addr) { debug_msg("FaceID %u\n", faceid); if (_faces.find(faceid) == _faces.end()) { XLOG_ERROR("Unknown FaceID %u", faceid); return false; } _faces[faceid]->set_local_addr(local_addr); return true; } bool FaceManager::get_local_addr(OlsrTypes::FaceID faceid, IPv4& local_addr) { debug_msg("FaceID %u\n", faceid); if (_faces.find(faceid) == _faces.end()) { XLOG_ERROR("Unknown FaceID %u", faceid); return false; } local_addr = _faces[faceid]->local_addr(); return true; } bool FaceManager::set_local_port(OlsrTypes::FaceID faceid, const uint16_t local_port) { debug_msg("FaceID %u\n", faceid); if (_faces.find(faceid) == _faces.end()) { XLOG_ERROR("Unknown FaceID %u", faceid); return false; } _faces[faceid]->set_local_port(local_port); return true; } bool FaceManager::get_local_port(OlsrTypes::FaceID faceid, uint16_t& local_port) { debug_msg("FaceID %u\n", faceid); if (_faces.find(faceid) == _faces.end()) { XLOG_ERROR("Unknown FaceID %u", faceid); return false; } local_port = _faces[faceid]->local_port(); return true; } bool FaceManager::set_all_nodes_addr(OlsrTypes::FaceID faceid, const IPv4& all_nodes_addr) { debug_msg("FaceID %u\n", faceid); if (_faces.find(faceid) == _faces.end()) { XLOG_ERROR("Unknown FaceID %u", faceid); return false; } Face* face = _faces[faceid]; // Check if nothing need be done. if (face->all_nodes_addr() == all_nodes_addr) return true; // TODO: If we are listening for OLSR traffic on a multicast group // on the interface, ask Olsr to ask IO to leave the group. if (face->all_nodes_addr().is_multicast()) { XLOG_UNFINISHED(); } const string& interface = face->interface(); const string& vif = face->vif(); const IPv4& local_addr = face->local_addr(); if (all_nodes_addr.is_multicast()) { // Multicast address. if (! all_nodes_addr.is_linklocal_multicast()) { XLOG_ERROR("Rejecting OLSR all-nodes address %s on %s/%s: " "not a link-local group", cstring(all_nodes_addr), interface.c_str(), vif.c_str()); return false; } //XLOG_UNFINISHED(); XLOG_ERROR("Rejecting OLSR all-nodes address %s on %s/%s: " "multicast groups are not yet supported", cstring(all_nodes_addr), interface.c_str(), vif.c_str()); return false; } else { // Broadcast address. 255.255.255.255 is always accepted. if (all_nodes_addr != IPv4::ALL_ONES()) { bool matched_bcast = false; do { // Attempt to get the configured broadcast address for // this interface, as seen by the interface manager. // If we cannot, or it does not match the all-nodes address // which the administrator has configured for this OLSR // binding, reject the address. IPv4 bcast_addr; if (! _olsr.get_broadcast_address(interface, vif, local_addr, bcast_addr)) { debug_msg("Could not obtain broadcast address for " "%s/%s%s\n", interface.c_str(), vif.c_str(), cstring(local_addr)); break; } debug_msg("broadcast address for %s is %s\n", cstring(local_addr), cstring(bcast_addr)); if (all_nodes_addr == bcast_addr) matched_bcast = true; } while (false); if (! matched_bcast) { XLOG_ERROR("Rejecting OLSR all-nodes address %s on %s/%s: " "not a configured broadcast address", cstring(all_nodes_addr), interface.c_str(), vif.c_str()); return false; } } } face->set_all_nodes_addr(all_nodes_addr); return true; } bool FaceManager::get_all_nodes_addr(OlsrTypes::FaceID faceid, IPv4& all_nodes_addr) { debug_msg("FaceID %u\n", faceid); if (_faces.find(faceid) == _faces.end()) { XLOG_ERROR("Unknown FaceID %u", faceid); return false; } all_nodes_addr = _faces[faceid]->all_nodes_addr(); return true; } bool FaceManager::set_all_nodes_port(OlsrTypes::FaceID faceid, const uint16_t all_nodes_port) { debug_msg("FaceID %u\n", faceid); if (_faces.find(faceid) == _faces.end()) { XLOG_ERROR("Unknown FaceID %u", faceid); return false; } Face* face = _faces[faceid]; // Check if nothing need be done. if (face->all_nodes_port() == all_nodes_port) return true; // This is mostly a no-op; the outgoing port generally requires // no special setup. face->set_all_nodes_port(all_nodes_port); return true; } bool FaceManager::get_all_nodes_port(OlsrTypes::FaceID faceid, uint16_t& all_nodes_port) { debug_msg("FaceID %u\n", faceid); if (_faces.find(faceid) == _faces.end()) { XLOG_ERROR("Unknown FaceID %u", faceid); return false; } all_nodes_port = _faces[faceid]->all_nodes_port(); return true; } bool FaceManager::flood_message(Message* message) { // TODO Use outgoing message queues per interface to // correctly deal with MTU and any fragmentation // which may be needed. // TODO Refcount messages as they flow up and down. map::iterator ii; for (ii = _faces.begin(); ii != _faces.end(); ii++) { Face* face = (*ii).second; // Skip transmission on disabled interfaces. if (! face->enabled()) continue; // XXX Deal with MTU by setting an upper limit on // the size of the encoded packet which is specific // to this interface; Packet will truncate to this size. Packet* pkt = new Packet(_md); pkt->set_mtu(face->mtu()); pkt->add_message(message); vector buf; bool result = pkt->encode(buf); if (result == false) { XLOG_WARNING("Outgoing packet on %s/%s truncated by MTU.", face->interface().c_str(), face->vif().c_str()); } pkt->set_seqno(face->get_pkt_seqno()); pkt->update_encoded_seqno(buf); face->transmit(&buf[0], buf.size()); // XXX message will be consumed by the deletion. delete pkt; } // consume message like we say we will in our API. // XXX We can't delete the message here as we have no way of // knowing if we were being called in response to forwarding // or originating a message. //delete message; // XXX we should do more meaningful error handling. return true; } void FaceManager::stop_all_timers() { stop_hello_timer(); stop_mid_timer(); } void FaceManager::start_mid_timer() { debug_msg("starting MID timer\n"); _mid_timer = _olsr.get_eventloop().new_periodic( get_mid_interval(), callback(this, &FaceManager::event_send_mid)); } void FaceManager::stop_mid_timer() { debug_msg("stopping MID timer\n"); _mid_timer.clear(); } void FaceManager::restart_mid_timer() { reschedule_mid_timer(); } void FaceManager::reschedule_mid_timer() { _mid_timer.reschedule_after(get_mid_interval()); } void FaceManager::reschedule_immediate_mid_timer() { _mid_timer.schedule_now(); } bool FaceManager::event_send_mid() { debug_msg("MyMainAddr %s event_send_mid\n", cstring(get_main_addr())); XLOG_ASSERT(_enabled_face_count > 1); MidMessage* mid = new MidMessage(); mid->set_expiry_time(get_mid_hold_time()); mid->set_origin(get_main_addr()); mid->set_ttl(OlsrTypes::MAX_TTL); mid->set_hop_count(0); mid->set_seqno(this->get_msg_seqno()); map::iterator ii; for (ii = _faces.begin(); ii != _faces.end(); ii++) { Face* face = (*ii).second; // Skip disabled interfaces, or interfaces with the main address, // which already appears in the message origin. if (false == face->enabled() || get_main_addr() == face->local_addr()) continue; mid->add_interface(face->local_addr()); } mid->set_valid(true); // XXX Return meaningful errors. flood_message(mid); // assumes that 'mid' is consumed // XXX no refcounting delete mid; return true; } void FaceManager::start_hello_timer() { _hello_timer = _olsr.get_eventloop().new_periodic( get_hello_interval(), callback(this, &FaceManager::event_send_hello)); } void FaceManager::stop_hello_timer() { _hello_timer.clear(); } void FaceManager::restart_hello_timer() { reschedule_hello_timer(); } void FaceManager::reschedule_hello_timer() { _hello_timer.reschedule_after(get_hello_interval()); } void FaceManager::reschedule_immediate_hello_timer() { _hello_timer.schedule_now(); } bool FaceManager::event_send_hello() { map::iterator ii; for (ii = _faces.begin(); ii != _faces.end(); ii++) { Face* face = (*ii).second; if (false == face->enabled()) continue; face->originate_hello(); } return true; } bool FaceManager::set_main_addr(const IPv4& addr) { bool is_allowed = false; if (get_enabled_face_count() == 0) { // If no interfaces are enabled, the main address may be changed // to any address during initial configuration. is_allowed = true; } else { // If any interfaces are enabled for OLSR, you may only // change the main address to one which is currently enabled. map::const_iterator ii; for (ii = _faces.begin(); ii != _faces.end(); ii++) { Face* face = (*ii).second; if (false == face->enabled()) continue; if (face->local_addr() == addr) { is_allowed = true; break; } } } // TODO: Propagate any protocol state changes required by the // change of main address. if (is_allowed) _main_addr = addr; return is_allowed; } void FaceManager::add_message_cb(MessageReceiveCB cb) { _handlers.push_back(cb); } bool FaceManager::delete_message_cb(MessageReceiveCB cb) { bool is_deleted = false; vector::iterator ii; for (ii = _handlers.begin(); ii != _handlers.end(); ii++) { #ifdef XORP_USE_USTL if (ii->get() == cb.get()) { #else if (*ii == cb) { #endif _handlers.erase(ii); is_deleted = true; break; } } return is_deleted; } bool FaceManager::event_receive_unknown( Message* msg, const IPv4& remote_addr, const IPv4& local_addr) { UnknownMessage* um = dynamic_cast(msg); if (um == 0) { debug_msg("Warning: couldn't cast to UnknownMessage; did you\n" "forget to register a receive callback in addition to\n" "registering a decoder?\n"); XLOG_UNREACHABLE(); } debug_msg("*** RECEIVED UNKNOWN MESSAGE %p ***\n", um); // Update statistics. _faces[msg->faceid()]->counters().incr_unknown_messages(); // Forward the message. forward_message(remote_addr, msg); return true; UNUSED(local_addr); } bool FaceManager::is_duplicate_message(const Message* msg) const { //debug_msg("Msg %p\n", msg); // 7.1.1: HELLO messages are excluded from duplicate detection. if (0 != dynamic_cast(msg)) return false; return (0 != get_dupetuple(msg->origin(), msg->seqno())); } bool FaceManager::forward_message(const IPv4& remote_addr, Message* msg) { debug_msg("MyMainAddr %s Msg %p\n", cstring(get_main_addr()), msg); // Invariant: HELLO messages should never be forwarded. XLOG_ASSERT(0 == dynamic_cast(msg)); if (is_forwarded_message(msg)) { debug_msg("%p already forwarded.\n", msg); return false; } // 3.4, 4: Check if *sender interface address* of the packet // which originally contained this message belongs to a neighbor // which is an MPR selector of this node. // If so, the message MUST be forwarded. bool will_forward = _nh->is_mpr_selector_addr(remote_addr) && msg->ttl() > 1; debug_msg("%sforwarding %p.\n", will_forward ? "" : "not ", msg); // 3.4, 5: Update the duplicate set tuple. update_dupetuple(msg, will_forward); if (will_forward) { // 3.4, 6: Increment hopcount. // 3.4, 7: Decrement ttl. // 3.4, 8: Forward message on all interfaces. msg->incr_hops(); msg->decr_ttl(); flood_message(msg); _faces[msg->faceid()]->counters().incr_forwarded(); } return will_forward; } bool FaceManager::is_forwarded_message(const Message* msg) const { //debug_msg("Msg %p\n", msg); // 3.4, 2: If a duplicate tuple does not exist, it is OK to // forward the message. DupeTuple* dt = get_dupetuple(msg->origin(), msg->seqno()); if (0 == dt) return false; // 3.4, 2: If a duplicate tuple exists, it is not OK to forward the // message if it has already been forwarded, OR it has already been // received on the interface where we originally received it. if (!dt->is_forwarded() && !dt->is_seen_by_face(msg->faceid())) return false; return true; } DupeTuple* FaceManager::get_dupetuple(const IPv4& origin_addr, const uint16_t seqno) const { //debug_msg("OriginAddr %s Seqno %u\n", cstring(origin_addr), // XORP_UINT_CAST(seqno)); if (_duplicate_set.empty()) return 0; // 3.4, 3.1: If address and sequence number are matched in // the duplicate set, the message is a duplicate. DupeTuple* found_dt = 0; pair rd = _duplicate_set.equal_range(origin_addr); for (DupeTupleMap::const_iterator ii = rd.first; ii != rd.second; ii++) { DupeTuple* dt = (*ii).second; if (dt->seqno() == seqno) { found_dt = dt; break; } } return found_dt; } void FaceManager::set_dup_hold_time(const TimeVal& dup_hold_time) { _dup_hold_time = dup_hold_time; } void FaceManager::update_dupetuple(const Message* msg, const bool is_forwarded) { debug_msg("MyMainAddr %s OriginAddr %s Seqno %u\n", cstring(get_main_addr()), cstring(msg->origin()), XORP_UINT_CAST(msg->seqno())); DupeTuple* dt = 0; // Check if we already have a duplicate set entry for the // sequence number and origin inside the message. pair rd = _duplicate_set.equal_range(msg->origin()); for (DupeTupleMap::iterator ii = rd.first; ii != rd.second; ii++) { DupeTuple* ndt = (*ii).second; if (ndt->seqno() == msg->seqno()) { dt = ndt; break; } } // If a duplicate set tuple was not found, record a new tuple with the // sequence number and origin inside the message. if (0 == dt) { dt = new DupeTuple(_eventloop, this, msg->origin(), msg->seqno(), get_dup_hold_time()); _duplicate_set.insert(make_pair(msg->origin(), dt)); } XLOG_ASSERT(dt != 0); // Update the message expiry time and the set of interfaces where it // was received. dt->update_timer(get_dup_hold_time()); dt->set_seen_by_face(msg->faceid()); dt->set_is_forwarded(is_forwarded); } void FaceManager::event_dupetuple_expired(const IPv4& origin, const uint16_t seqno) { bool is_found = false; pair rd = _duplicate_set.equal_range(origin); DupeTupleMap::iterator ii; for (ii = rd.first; ii != rd.second; ii++) { DupeTuple* dt = (*ii).second; if (dt->seqno() == seqno) { is_found = true; break; } } XLOG_ASSERT(is_found); delete (*ii).second; _duplicate_set.erase(ii); } void DupeTuple::update_timer(const TimeVal& vtime) { if (_expiry_timer.scheduled()) _expiry_timer.clear(); _expiry_timer = _ev.new_oneoff_after(vtime, callback(this, &DupeTuple::event_dead)); } void DupeTuple::event_dead() { _parent->event_dupetuple_expired(origin(), seqno()); } xorp/contrib/olsr/profile_vars.cc0000664000076400007640000000325711421137511017304 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8 sw=4: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "olsr_module.h" #include "libxorp/xorp.h" #include "libxorp/profile.hh" #include "profile_vars.hh" struct profile_vars { string var; string comment; } profile_vars[] = { { profile_message_in, "Messages entering OLSR" }, { profile_route_ribin, "Routes entering OLSR" }, { profile_route_rpc_in, "Routes being queued for the RIB" }, { profile_route_rpc_out, "Routes being sent to the RIB" }, { trace_message_in, "Trace Message entering OLSR" }, { trace_message_out, "Trace Message leaving OLSR" }, { trace_policy_configure, "Trace policy introduction" }, }; void initialize_profiling_variables(Profile& p) { for (size_t i = 0; i < sizeof(profile_vars) / sizeof(struct profile_vars); i++) p.create(profile_vars[i].var, profile_vars[i].comment); } xorp/contrib/olsr/neighborhood.cc0000664000076400007640000020735511540225522017267 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8 sw=4: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "olsr_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/callback.hh" #include "libxorp/ipv4.hh" #include "libxorp/ipv4net.hh" #include "libxorp/status_codes.h" #include "libxorp/service.hh" #include "libxorp/eventloop.hh" // #define DEBUG_LOGGING // #define DEBUG_FUNCTION_NAME // #define DETAILED_DEBUG #include "olsr.hh" #include "link.hh" #include "neighbor.hh" #include "neighborhood.hh" #include "topology.hh" bool CandMprOrderPred::operator()(const Neighbor* lhs, const Neighbor* rhs) const { if (lhs->willingness() == rhs->willingness()) { if (lhs->reachability() == rhs->reachability()) { return lhs->degree() > rhs->degree(); } return lhs->reachability() > rhs->reachability(); } return lhs->willingness() > rhs->willingness(); } bool LinkOrderPred::operator()(const OlsrTypes::LogicalLinkID lhid, const OlsrTypes::LogicalLinkID rhid) { try { const LogicalLink* lhs = _nh->get_logical_link(lhid); const LogicalLink* rhs = _nh->get_logical_link(rhid); if (lhs->is_sym() == rhs->is_sym()) { // TODO: ETX collation predicates go here. if (lhs->sym_time_remaining() == rhs->sym_time_remaining()) { // Final collation tiebreak: Return if the link has a higher ID. return lhs->id() > rhs->id(); } return lhs->sym_time_remaining() > rhs->sym_time_remaining(); } return lhs->is_sym() > rhs->is_sym(); } catch (...) {} return false; } bool TwoHopLinkOrderPred::operator()(const OlsrTypes::TwoHopLinkID lhid, const OlsrTypes::TwoHopLinkID rhid) { try { TwoHopLink* lhs = _nh->get_twohop_link(lhid); TwoHopLink* rhs = _nh->get_twohop_link(rhid); if (lhs->time_remaining() == rhs->time_remaining()) { // Final collation tiebreak: Return if the link has a higher ID. return lhs->id() > rhs->id(); } return lhs->time_remaining() > rhs->time_remaining(); } catch (...) {} return false; } Neighborhood::Neighborhood(Olsr& olsr, EventLoop& eventloop, FaceManager& fm) : _olsr(olsr), _eventloop(eventloop), _fm(fm), _tm(0), _link_order_pred(this), _twohop_link_order_pred(this), _next_linkid(1), _next_neighborid(1), _next_twohop_linkid(1), _next_twohop_nodeid(1), _enabled_face_count(0), _willingness(OlsrTypes::WILL_DEFAULT), _refresh_interval(TimeVal(OlsrTypes::DEFAULT_REFRESH_INTERVAL, 0)), _mpr_computation_enabled(true), _mpr_coverage(OlsrTypes::DEFAULT_MPR_COVERAGE), _tc_interval(TimeVal(OlsrTypes::DEFAULT_TC_INTERVAL, 0)), _tc_redundancy(OlsrTypes::DEFAULT_TC_REDUNDANCY), _tc_timer_state(TC_STOPPED), _tc_current_ansn(1), _tc_previous_ans_count(0), _loss_triggered_tc_enabled(true), _change_triggered_tc_enabled(false) { _fm.add_message_cb(callback(this, &Neighborhood::event_receive_hello)); // Create the MPR selection background task but make sure it is not // scheduled until we have neighbors to select as MPRs. // Weight this task to happen before route updates, as it SHOULD // happen before the state of our forwarding plane is changed. _mpr_recount_task = _eventloop.new_oneoff_task( callback(this, &Neighborhood::recount_mpr_set), XorpTask::PRIORITY_HIGH, XorpTask::WEIGHT_DEFAULT); // XXX does this cause a leak? _mpr_recount_task.unschedule(); } Neighborhood::~Neighborhood() { _mpr_recount_task.unschedule(); // Must stop TC timer even if in FINISHING state. stop_tc_timer(); _fm.delete_message_cb(callback(this, &Neighborhood::event_receive_hello)); clear_links(); XLOG_ASSERT(_twohop_links.empty()); XLOG_ASSERT(_twohop_nodes.empty()); XLOG_ASSERT(_links.empty()); XLOG_ASSERT(_neighbors.empty()); } /* * Protocol variable methods. */ bool Neighborhood::set_mpr_coverage(const uint32_t coverage) { if (_mpr_coverage != coverage) { _mpr_coverage = coverage; if (_enabled_face_count > 0) schedule_mpr_recount(); } return true; } void Neighborhood::set_tc_interval(const TimeVal& interval) { if (interval == _tc_interval) return; _tc_interval = interval; if (_tc_timer.scheduled()) { // Change period. reschedule_tc_timer(); // Fire one off now. debug_msg("%s: Scheduling an immediate TC due to a change in " "TC interval.\n", cstring(_fm.get_main_addr())); reschedule_immediate_tc_timer(); } } bool Neighborhood::set_tc_redundancy(const OlsrTypes::TcRedundancyType type) { debug_msg("TcRedundancy %u\n", XORP_UINT_CAST(type)); if (type == _tc_redundancy) { debug_msg("%s == %s, no-op.\n", tcr_to_str(type), tcr_to_str(_tc_redundancy)); return true; } if (type > OlsrTypes::TCR_END) { XLOG_ERROR("Topology control mode %u out of range.", XORP_UINT_CAST(type)); return false; } if (willingness() == OlsrTypes::WILL_NEVER && type != OlsrTypes::TCR_MPRS_IN) { XLOG_ERROR("Topology control mode %u invalid when configured not to " "forward OLSR traffic.", XORP_UINT_CAST(type)); return false; } _tc_redundancy = type; if (is_mpr()) { debug_msg("%s: Scheduling an immediate TC due to a change in " "TC redundancy.\n", cstring(_fm.get_main_addr())); reschedule_immediate_tc_timer(); } XLOG_INFO("TC redundancy mode is set to %s.\n", tcr_to_str(type)); return true; } void Neighborhood::set_willingness(const OlsrTypes::WillType willingness) { debug_msg("Willingness %u\n", XORP_UINT_CAST(willingness)); if (willingness == _willingness) return; // do nothing _willingness = willingness; XLOG_INFO("Willingness-to-forward is set to %s.\n", will_to_str(willingness)); // 15.1: A node with willingness equal to WILL_NEVER SHOULD have // TC_REDUNDANCY also equal to zero. if (willingness == OlsrTypes::WILL_NEVER) { debug_msg("Setting TC redundancy to %s to match willingness %s.\n", tcr_to_str(OlsrTypes::TCR_MPRS_IN), will_to_str(willingness)); set_tc_redundancy(OlsrTypes::TCR_MPRS_IN); } } /* * Methods which interact with other major classes. */ size_t Neighborhood::populate_hello(HelloMessage* hello) { #if 0 debug_msg("MyMainAddr %s HelloMessage* %p\n", cstring(_fm.get_main_addr()), hello); #endif size_t populated_count = 0; XLOG_ASSERT(0 != hello); XLOG_ASSERT(hello->faceid() != OlsrTypes::UNUSED_FACE_ID); XLOG_ASSERT(hello->links().empty() == true); // Set message attributes that are derived from Neighborhood. hello->set_expiry_time(get_neighbor_hold_time()); hello->set_willingness(willingness()); map::iterator ii; for (ii = _links.begin(); ii != _links.end(); ii++) { const LogicalLink* l = (*ii).second; const Neighbor* n = l->destination(); if (hello->faceid() == l->faceid()) { LinkCode lc(n->neighbor_type(), l->link_type()); // The link is local to this interface, therefore we // advertise it by its remote interface address. hello->add_link(lc, l->remote_addr()); populated_count++; } else { // We should not announce links for a disabled face; this // check is implemented further up in the call path. #if 0 debug_msg("hello->faceid() is %u, l->faceid() is %u\n", XORP_UINT_CAST(hello->faceid()), XORP_UINT_CAST(l->faceid())); #endif // Invariant: This path should only be reached if there is // more than one OLSR interface configured administratively // up in this process. XLOG_ASSERT(_enabled_face_count > 1); // The link is not local to this interface. // From the point of view of any neighbors which see // this HELLO message, the link we are advertising // is to a two-hop neighbor -- therefore we advertise // its main address. LinkCode lc(n->neighbor_type(), OlsrTypes::UNSPEC_LINK); hello->add_link(lc, n->main_addr()); populated_count++; } } return populated_count; } void Neighborhood::push_topology() { size_t n_pushed = 0; size_t n2_pushed = 0; map::iterator ii; for (ii = _neighbors.begin(); ii != _neighbors.end(); ii++) { if (true == push_neighbor((*ii).second)) { n_pushed++; } } debug_msg("Pushed %u neighbors to SPT.\n", XORP_UINT_CAST(n_pushed)); map::iterator jj; for (jj = _twohop_nodes.begin(); jj != _twohop_nodes.end(); jj++) { if (true == push_twohop_neighbor((*jj).second)) { n2_pushed++; } } debug_msg("Pushed %u two-hop neighbors to SPT.\n", XORP_UINT_CAST(n2_pushed)); } /* * Network interface methods. */ void Neighborhood::add_face(const OlsrTypes::FaceID faceid) { debug_msg("FaceID %u\n", XORP_UINT_CAST(faceid)); _enabled_face_count++; UNUSED(faceid); } void Neighborhood::delete_face(const OlsrTypes::FaceID faceid) { debug_msg("FaceID %u\n", XORP_UINT_CAST(faceid)); size_t deleted_link_count = 0; // If we are taking an interface administratively down, // pull all link-state information associated with the // address(es) configured on this interface. map::iterator ii, jj; for (ii = _links.begin(); ii != _links.end(); ) { jj = ii++; LogicalLink* l = (*jj).second; if (l->faceid() == faceid) { delete_link(l->id()); deleted_link_count++; } } --_enabled_face_count; if (_enabled_face_count == 0) { // If no interfaces remaining, ensure TC timer has stopped. stop_tc_timer(); } else { // If there were any links on the interface we have just // removed, we SHOULD schedule an MPR recount AND a route // computation. // [If we use per-interface granular MPR selection then we should // probably been notified of this already as part of the // delete_link() call graph above.] if (deleted_link_count > 0) { schedule_mpr_recount(); if (_rm) _rm->schedule_route_update(); } } } /* * One-hop link methods. */ OlsrTypes::LogicalLinkID Neighborhood::add_link(const TimeVal& vtime, const IPv4& remote_addr, const IPv4& local_addr) throw(BadLogicalLink) { debug_msg("MyMainAddr %s RemoteAddr %s LocalAddr %s\n", cstring(_fm.get_main_addr()), cstring(remote_addr), cstring(local_addr)); OlsrTypes::LogicalLinkID linkid = _next_linkid++; // Throw an exception if we overflowed the LogicalLinkID space. if (_links.find(linkid) != _links.end()) { xorp_throw(BadLogicalLink, c_format("Mapping for LogicalLinkID %u already exists", XORP_UINT_CAST(linkid))); } _links[linkid] = new LogicalLink(this, _eventloop, linkid, vtime, remote_addr, local_addr); _link_addr[make_pair(remote_addr, local_addr)] = linkid; debug_msg("New link: %s -> %s\n", cstring(local_addr), cstring(remote_addr)); XLOG_TRACE(_olsr.trace()._neighbor_events, "New link: %s -> %s\n", cstring(local_addr), cstring(remote_addr)); return linkid; } bool Neighborhood::delete_link(OlsrTypes::LogicalLinkID linkid) { debug_msg("MyMainAddr %s LinkID %u\n", cstring(_fm.get_main_addr()), XORP_UINT_CAST(linkid)); // Find link tuple by ID. map::iterator ii = _links.find(linkid); if (ii == _links.end()) return false; LogicalLink* l = (*ii).second; // Find address-to-link in map, erase entry if found. map, OlsrTypes::LogicalLinkID>::iterator jj = _link_addr.find(make_pair(l->remote_addr(), l->local_addr())); if (jj != _link_addr.end()) { _link_addr.erase(jj); } XLOG_ASSERT(l->faceid() != OlsrTypes::UNUSED_FACE_ID); // 8.1: Each time a link is deleted, the associated neighbor tuple // MUST be removed if it has no longer any associated link tuples. // // We must check if the neighbor ID is initialized as this method // may be called if we are unable to create the neighbor. if (l->neighbor_id() != OlsrTypes::UNUSED_NEIGHBOR_ID) { bool is_unlinked = l->destination()->delete_link(linkid); if (is_unlinked) { debug_msg("Deleting neighbor %u.\n", l->neighbor_id()); delete_neighbor(l->neighbor_id()); } } debug_msg("Delete link: %s -> %s\n", cstring(l->local_addr()), cstring(l->remote_addr())); XLOG_TRACE(_olsr.trace()._neighbor_events, "Delete link: %s -> %s\n", cstring(l->local_addr()), cstring(l->remote_addr())); // Destroy the link. _links.erase(ii); delete l; // Trigger a routing computation. if (_rm) _rm->schedule_route_update(); return true; } void Neighborhood::clear_links() { map::iterator ii; for (ii = _links.begin(); ii != _links.end(); ii++) { delete_link((*ii).first); } } const LogicalLink* Neighborhood::find_best_link(const Neighbor* n) throw(BadLinkCoverage) { const set& links = n->links(); if (links.empty()) { xorp_throw(BadLinkCoverage, c_format("No suitable links to Neighbor %u.", XORP_UINT_CAST(n->id()))); } set::const_iterator ii = min_element(links.begin(), links.end(), _link_order_pred); // Catchall: the link which we arrive at must be OK. const LogicalLink* l = _links[(*ii)]; if (! l->is_sym()) { xorp_throw(BadLinkCoverage, c_format("No suitable links to Neighbor %u.", XORP_UINT_CAST(n->id()))); } return l; } OlsrTypes::LogicalLinkID Neighborhood::get_linkid(const IPv4& remote_addr, const IPv4& local_addr) throw(BadLogicalLink) { //debug_msg("RemoteAddr %s LocalAddr %s\n", // cstring(remote_addr), cstring(local_addr)); map, OlsrTypes::LogicalLinkID>::iterator ii = _link_addr.find(make_pair(remote_addr, local_addr)); if (ii == _link_addr.end()) { xorp_throw(BadLogicalLink, c_format("No mapping for %s:%s exists", cstring(remote_addr), cstring(local_addr))); } return (*ii).second; } OlsrTypes::LogicalLinkID Neighborhood::update_link(const OlsrTypes::FaceID faceid, const IPv4& remote_addr, const IPv4& local_addr, const TimeVal& vtime, bool& is_created) throw(BadLogicalLink) { #if 0 debug_msg("MyMainAddr %s FaceID %u RemoteAddr %s LocalAddr %s Vtime %s\n", cstring(_fm.get_main_addr()), XORP_UINT_CAST(faceid), cstring(remote_addr), cstring(local_addr), cstring(vtime)); #endif OlsrTypes::LogicalLinkID linkid; try { linkid = get_linkid(remote_addr, local_addr); is_created = false; // Invariant: The Face which we see this link on MUST NOT // have changed. If an OLSR interface has been renumbered, // the face will have gone down and all its links with it. XLOG_ASSERT(faceid == _links[linkid]->faceid()); } catch(BadLogicalLink& bl) { debug_msg("allocating new link entry.\n"); // XXX: May throw an exception again, or even bad_alloc. linkid = add_link(vtime, remote_addr, local_addr); // Set back-reference to the Face where this link is terminated. // Inform FaceManager that the interface has a new link. _links[linkid]->set_faceid(faceid); is_created = true; } // A change in link state, or a link being added, means we // have to recompute the routing table. // TODO: Push this down further -- we should only trigger an // update when the link actually changed state. _rm->schedule_route_update(); return linkid; } const LogicalLink* Neighborhood::get_logical_link(const OlsrTypes::LogicalLinkID linkid) throw(BadLogicalLink) { //debug_msg("LinkID %u\n", XORP_UINT_CAST(linkid)); if (_links.find(linkid) == _links.end()) { xorp_throw(BadLogicalLink, c_format("No mapping for %u exists", XORP_UINT_CAST(linkid))); } return _links[linkid]; } void Neighborhood::get_logical_link_list(list& l1_list) const { map::const_iterator ii; for (ii = _links.begin(); ii != _links.end(); ii++) l1_list.push_back((*ii).first); } /* * One-hop neighbor methods. */ OlsrTypes::NeighborID Neighborhood::get_neighborid_by_main_addr(const IPv4& main_addr) throw(BadNeighbor) { //debug_msg("MainAddr %s\n", cstring(main_addr)); if (_neighbor_addr.find(main_addr) == _neighbor_addr.end()) { xorp_throw(BadNeighbor, c_format("No mapping for %s exists", cstring(main_addr))); } return _neighbor_addr[main_addr]; } OlsrTypes::NeighborID Neighborhood::get_neighborid_by_remote_addr(const IPv4& remote_addr) throw(BadNeighbor) { debug_msg("RemoteAddr %s\n", cstring(remote_addr)); // Check for a main address match first. if (_neighbor_addr.find(remote_addr) != _neighbor_addr.end()) return _neighbor_addr[remote_addr]; //debug_msg("neighbor not found by main address, checking link database\n"); OlsrTypes::NeighborID nid = OlsrTypes::UNUSED_NEIGHBOR_ID; bool is_found = false; // If not found by main address, we need to walk the one-hop link state. // We only have one end of the link so we must do a linear search. map::iterator ii; for (ii = _links.begin(); ii != _links.end(); ii++) { LogicalLink* l = (*ii).second; if (l->remote_addr() == remote_addr) { nid = l->neighbor_id(); is_found = true; break; } } if (is_found == false) { xorp_throw(BadNeighbor, c_format("No mapping for %s exists", cstring(remote_addr))); } // Invariant: If a link is found, its neighbor ID // MUST have been set when the association between // LogicalLink and Neighbor was made. XLOG_ASSERT(nid != OlsrTypes::UNUSED_NEIGHBOR_ID); return nid; } OlsrTypes::NeighborID Neighborhood::update_neighbor(const IPv4& main_addr, const OlsrTypes::LogicalLinkID linkid, const bool is_new_link, const OlsrTypes::WillType will, const bool is_mpr_selector, const TimeVal& mprs_expiry_time, bool& is_created) throw(BadNeighbor) { debug_msg("MyMainAddr %s MainAddr %s LinkID %u new %s will %u " "mprsel %s expiry %s\n", cstring(_fm.get_main_addr()), cstring(main_addr), XORP_UINT_CAST(linkid), bool_c_str(is_new_link), XORP_UINT_CAST(will), bool_c_str(is_mpr_selector), cstring(mprs_expiry_time)); OlsrTypes::NeighborID nid; Neighbor* n; try { nid = get_neighborid_by_main_addr(main_addr); n = _neighbors[nid]; is_created = false; // Update neighbor's associated link, creating the association // if it does not already exist. N_status will be updated. n->update_link(linkid); } catch(BadNeighbor& bn) { // Create a new neighbor tuple. // XXX May throw BadNeighbor if a neighbor ID cannot be allocated, // i.e. we ran out of neighbor ID space. nid = add_neighbor(main_addr, linkid); n = _neighbors[nid]; is_created = true; } if (is_new_link) { // Set back-reference from LogicalLink to Neighbor. Whilst a // Neighbor may have many LogicalLinks, a LogicalLink MUST be // associated with one and only one Neighbor. _links[linkid]->set_destination(n); _links[linkid]->set_neighbor_id(nid); } // Invariant: a neighbor cannot exist without at least one link. XLOG_ASSERT(! n->links().empty()); // Section 8.1.1: Update neighbor willingness. n->set_willingness(will); // 8.4.1: Update the MPR selector set, if the neighbor selects // me as an MPR. This *may* start the TC timer. if (is_mpr_selector) update_mpr_selector(nid, mprs_expiry_time); // Mark the neighbor as being advertised in TC broadcasts if // its advertisement state changed. schedule_ans_update(false); // Routing recomputation will be triggered by LogicalLink operations. return nid; } OlsrTypes::NeighborID Neighborhood::add_neighbor(const IPv4& main_addr, OlsrTypes::LogicalLinkID linkid) throw(BadNeighbor) { debug_msg("MyMainAddr %s MainAddr %s\n", cstring(_fm.get_main_addr()), cstring(main_addr)); OlsrTypes::NeighborID neighborid = _next_neighborid++; // Throw an exception if we overflow the neighbor ID space. if (_neighbors.find(neighborid) != _neighbors.end()) { xorp_throw(BadNeighbor, c_format("Mapping for NeighborID %u already exists", XORP_UINT_CAST(neighborid))); } // Section 8.1: Associated neighbor MUST be created with the link // if it does not already exist. // We enforce this at language level with the constructor signature. Neighbor* n = new Neighbor(_eventloop, this, neighborid, main_addr, linkid); _neighbors[neighborid] = n; XLOG_ASSERT(_neighbor_addr.find(main_addr) == _neighbor_addr.end()); _neighbor_addr[main_addr] = neighborid; // Check and record if the new one-hop neighbor is already known to // us as a two-hop neighbor. // XXX Should we only do this when the neighbor is first considered // symmetric? try { OlsrTypes::TwoHopNodeID tnid = get_twohop_nodeid_by_main_addr(main_addr); _twohop_nodes[tnid]->set_is_strict(false); } catch (...) {} debug_msg("New neighbor: %s\n", cstring(n->main_addr())); XLOG_TRACE(_olsr.trace()._neighbor_events, "New neighbor: %s\n", cstring(n->main_addr())); // We defer associating the Neighbor with its first LogicalLink // fully until both have been created. return neighborid; } bool Neighborhood::delete_neighbor(const OlsrTypes::NeighborID nid) { debug_msg("MyMainAddr %s NeighborID %u\n", cstring(_fm.get_main_addr()), XORP_UINT_CAST(nid)); XLOG_ASSERT(_neighbors.find(nid) != _neighbors.end()); map::iterator ii = _neighbors.find(nid); if (ii == _neighbors.end()) return false; Neighbor* n = (*ii).second; // Withdraw the neighbor from any pending TC broadcasts. // XXX NOTE WELL: This should only be done when the last good link // to a neighbor has been removed. Obviously ETX metrics are going // to mean this may be done earlier than neighbor deletion. schedule_ans_update(true); // Purge membership of MPR selector set. // 8.4.1: In case of neighbor loss, all MPR selector tuples with // MS_main_addr == Main Address of the neighbor MUST be deleted. // The timer will go out of scope when n is deleted. if (n->is_mpr_selector()) delete_mpr_selector(nid); // 8.5: In case of neighbor loss, all 2-hop tuples with // nexthop() of neighbor's main address MUST be deleted. n->delete_all_twohop_links(); // If the one-hop neighbor being lost is already known to us as a // two-hop neighbor, ensure that the 'N2 is not also an N' flag is cleared. try { OlsrTypes::TwoHopNodeID tnid = get_twohop_nodeid_by_main_addr(n->main_addr()); _twohop_nodes[tnid]->set_is_strict(true); } catch (...) {} // Purge membership of MPR sets, and trigger MPR set recount. withdraw_cand_mpr(nid); debug_msg("Delete neighbor: %s\n", cstring(n->main_addr())); XLOG_TRACE(_olsr.trace()._neighbor_events, "Delete neighbor: %s\n", cstring(n->main_addr())); map::iterator jj = _neighbor_addr.find(n->main_addr()); if (jj != _neighbor_addr.end()) { _neighbor_addr.erase(jj); } _neighbors.erase(ii); delete n; return true; } const Neighbor* Neighborhood::get_neighbor(const OlsrTypes::NeighborID nid) throw(BadNeighbor) { debug_msg("NeighborID %u\n", XORP_UINT_CAST(nid)); if (_neighbors.find(nid) == _neighbors.end()) { xorp_throw(BadNeighbor, c_format("No mapping for %u exists", XORP_UINT_CAST(nid))); } return _neighbors[nid]; } void Neighborhood::get_neighbor_list(list& n1_list) const { map::const_iterator ii; for (ii = _neighbors.begin(); ii != _neighbors.end(); ii++) n1_list.push_back((*ii).first); } bool Neighborhood::is_sym_neighbor_addr(const IPv4& remote_addr) { debug_msg("%s: RemoteAddr %s\n", cstring(_fm.get_main_addr()), cstring(remote_addr)); bool is_sym = false; try { OlsrTypes::NeighborID nid = get_neighborid_by_remote_addr(remote_addr); debug_msg("nid is %u\n", XORP_UINT_CAST(nid)); is_sym = _neighbors[nid]->is_sym(); } catch (BadNeighbor& bn) { debug_msg("threw exception: %s\n", cstring(bn)); } return is_sym; } void Neighborhood::schedule_ans_update(const bool is_deleted) { // Force an update of the ANSN if a neighbor is deleted, // but only if the timer is not in FINISHING state. if (is_deleted && _tc_timer_state != TC_FINISHING) ++_tc_current_ansn; // If we should be originating TC broadcasts, and we have neighbors // to advertise, restart the timer in RUNNING mode. if (! _mpr_selector_set.empty()) { if (_tc_timer_state != TC_RUNNING) { debug_msg("%s: Restarting timer due to non-empty ANS.\n", cstring(_fm.get_main_addr())); start_tc_timer(); } // If we are configured to send early TCs, and such a change // happened, and we are flooding non-empty TCs, schedule an early TC. if (_change_triggered_tc_enabled) { debug_msg("%s: Scheduling an immediate TC due to a change in " "the ANS.\n", cstring(_fm.get_main_addr())); reschedule_immediate_tc_timer(); } } } bool Neighborhood::push_neighbor(const Neighbor* n) { // Reject non-symmetric neighbors. if (! n->is_sym()) return false; // Choose the best link to this neighbor. const LogicalLink* l; try { l = find_best_link(n); } catch(BadLinkCoverage blc) { // Don't add this neighbor -- there are no good links to it. return false; } // We have the best link to N; populate the SPT. _rm->add_onehop_link(l, n); return true; } /* * Two-hop link methods. */ OlsrTypes::TwoHopLinkID Neighborhood::update_twohop_link(const LinkAddrInfo& node_info, Neighbor& nexthop, const OlsrTypes::FaceID faceid, const TimeVal& vtime) throw(BadTwoHopLink) { debug_msg("MyMainAddr %s NodeAddr %s Nexthop %s FaceID %u Vtime %s\n", cstring(_fm.get_main_addr()), cstring(node_info.remote_addr()), cstring(nexthop.main_addr()), XORP_UINT_CAST(faceid), cstring(vtime)); OlsrTypes::TwoHopLinkID tlid; bool is_new_l2 = false; // Check to see if the two-hop link exists. map, OlsrTypes::TwoHopLinkID>::iterator ii = _twohop_link_addrs.find(make_pair(nexthop.main_addr(), node_info.remote_addr())); if (ii == _twohop_link_addrs.end()) { // If the two-hop link does not exist, attempt to create it. // May throw BadTwoHopLink. debug_msg("adding new two-hop link\n"); tlid = add_twohop_link(&nexthop, node_info.remote_addr(), vtime); is_new_l2 = true; } else { // Update timer and status for existing two-hop link. debug_msg("updating two-hop link timer\n"); tlid = (*ii).second; _twohop_links[tlid]->update_timer(vtime); } // Track the interface where this two-hop link was last advertised. TwoHopLink* tl = _twohop_links[tlid]; tl->set_face_id(faceid); OlsrTypes::TwoHopNodeID tnid; bool is_new_n2 = false; try { // Update the associated TwoHopNeighbor, creating it if needed. tnid = update_twohop_node(node_info.remote_addr(), tlid, is_new_l2, is_new_n2); } catch (BadTwoHopNode btn) { // Re-throw exception with appropriate type for this method. xorp_throw(BadTwoHopLink, c_format("Could not create TwoHopNode with " "address %s", cstring(node_info.remote_addr()))); } // TODO: When ETX metrics are in use, we need only trigger a route // computation, or MPR computation, if the two-hop link is considered // good at this point. if (is_new_l2) { // Associate new TwoHopLink with new or existing TwoHopNeighbor. tl->set_destination(_twohop_nodes[tnid]); // Notify Neighbor that it has a two-hop link, now that both // TwoHopLink and TwoHopNeighbor exist and are associated together. // This must be done AFTER the two-hop neighborhood has been updated. // This MAY trigger an MPR computation. nexthop.add_twohop_link(tlid); } // Trigger a routing computation. _rm->schedule_route_update(); return tlid; } OlsrTypes::TwoHopLinkID Neighborhood::add_twohop_link(Neighbor* nexthop, const IPv4& remote_addr, const TimeVal& vtime) throw(BadTwoHopLink) { debug_msg("MyMainAddr %s Neighbor %u Vtime %s\n", cstring(_fm.get_main_addr()), XORP_UINT_CAST(nexthop->id()), cstring(vtime)); // Neighbor must exist. XLOG_ASSERT(0 != nexthop); // Two-hop links may only be added when the strong association is // with a symmetric one-hop neighbor. XLOG_ASSERT(nexthop->is_sym() == true); OlsrTypes::TwoHopLinkID tlid = _next_twohop_linkid++; // Throw an exception if we overflowed the TwoHopLinkID space. if (_twohop_links.find(tlid) != _twohop_links.end()) { xorp_throw(BadTwoHopLink, c_format("Mapping for TwoHopLinkID %u already exists", XORP_UINT_CAST(tlid))); } _twohop_links[tlid] = new TwoHopLink(_eventloop, this, tlid, nexthop, vtime); _twohop_link_addrs[make_pair(nexthop->main_addr(), remote_addr)] = tlid; // We defer notifying Neighbor until both TwoHopLink and // TwoHopNode exist; these objects are co-dependent, and Neighbor // needs to see the TwoHopNode for pre-computation of MPR state. // Routing table computation will be triggered by update_twohop_link(). return tlid; } bool Neighborhood::delete_twohop_link(const OlsrTypes::TwoHopLinkID tlid) { debug_msg("MyMainAddr %s TwoHopLinkID %u\n", cstring(_fm.get_main_addr()), XORP_UINT_CAST(tlid)); map::iterator ii = _twohop_links.find(tlid); if (ii == _twohop_links.end()) return false; TwoHopLink* tl = (*ii).second; Neighbor* n = tl->nexthop(); TwoHopNeighbor* n2 = tl->destination(); map, OlsrTypes::TwoHopLinkID>::iterator jj = _twohop_link_addrs.find(make_pair(n->main_addr(), n2->main_addr())); XLOG_ASSERT(jj != _twohop_link_addrs.end()); XLOG_ASSERT(tlid == (*jj).second); // Track two-hop link deletion in Neighbor. n->delete_twohop_link(tlid); // Track two-hop link deletion in TwoHopNeighbor. // If this is the last link to the TwoHopNeighbor, then we // will also delete the TwoHopNeighbor. // XXX: For LQ we must check if the two-hop is no longer 'reachable'. bool is_n2_unlinked = n2->delete_twohop_link(tlid); if (is_n2_unlinked) { debug_msg("Deleting two-hop neighbor %u.\n", XORP_UINT_CAST(n2->id())); delete_twohop_node(n2->id()); } _twohop_link_addrs.erase(jj); _twohop_links.erase(ii); delete tl; // Trigger a routing computation. if (_rm) _rm->schedule_route_update(); return is_n2_unlinked; } bool Neighborhood::delete_twohop_link_by_addrs(const IPv4& nexthop_addr, const IPv4& twohop_addr) { debug_msg("NexthopAddr %s TwohopAddr %s\n", cstring(nexthop_addr), cstring(twohop_addr)); map, OlsrTypes::TwoHopLinkID>::iterator ii = _twohop_link_addrs.find(make_pair(nexthop_addr, twohop_addr)); if (ii == _twohop_link_addrs.end()) return false; bool is_last = delete_twohop_link((*ii).second); return is_last; } TwoHopLink* Neighborhood::get_twohop_link(const OlsrTypes::TwoHopLinkID tlid) throw(BadTwoHopLink) { debug_msg("TwoHopLinkID %u\n", XORP_UINT_CAST(tlid)); if (_twohop_links.find(tlid) == _twohop_links.end()) { xorp_throw(BadTwoHopLink, c_format("No mapping for %u exists", XORP_UINT_CAST(tlid))); } return _twohop_links[tlid]; } void Neighborhood::get_twohop_link_list(list& l2_list) const { map::const_iterator ii; for (ii = _twohop_links.begin(); ii != _twohop_links.end(); ii++) l2_list.push_back((*ii).first); } const TwoHopLink* Neighborhood::find_best_twohop_link(const TwoHopNeighbor* n2) throw(BadTwoHopCoverage) { const set& twohop_links = n2->twohop_links(); if (twohop_links.empty()) { xorp_throw(BadTwoHopCoverage, c_format("No suitable links to TwoHopNeighbor %u.", XORP_UINT_CAST(n2->id()))); } set::const_iterator ii = min_element(twohop_links.begin(), twohop_links.end(), _twohop_link_order_pred); // Catchall: the two-hop link which we select must be OK. TwoHopLink* l2 = _twohop_links[(*ii)]; #if 0 // TODO: Catchall for ETX metrics. if (! l2->is_poor_etx()) { xorp_throw(BadTwoHopCoverage, c_format("No suitable links to TwoHopNeighbor %u.", XORP_UINT_CAST(n2->id()))); } #endif return l2; } /* * Two-hop neighbor methods. */ OlsrTypes::TwoHopNodeID Neighborhood::update_twohop_node(const IPv4& main_addr, const OlsrTypes::TwoHopLinkID tlid, const bool is_new_l2, bool& is_n2_created) throw(BadTwoHopNode) { debug_msg("MyMainAddr %s MainAddr %s TwoHopLinkID %u IsNewL2 %s\n", cstring(_fm.get_main_addr()), cstring(main_addr), XORP_UINT_CAST(tlid), bool_c_str(is_new_l2)); // Check if the TwoHopNeighbor with main_addr already exists. OlsrTypes::TwoHopNodeID tnid; map::iterator ii = _twohop_node_addrs.find(main_addr); if (ii == _twohop_node_addrs.end()) { // A new TwoHopNeighbor needs to be created. // May throw BadTwoHopNode. tnid = add_twohop_node(main_addr, tlid); is_n2_created = true; } else { // We are updating the existing TwoHopNeighbor. tnid = (*ii).second; // Associate existing TwoHopNeighbor with new TwoHopLink. if (is_new_l2) _twohop_nodes[tnid]->add_twohop_link(tlid); } // Check if the two-hop neighbor we are updating is already a // one-hop neighbor, and update the 'strict 2-hop' flag accordingly. try { OlsrTypes::NeighborID nid = get_neighborid_by_main_addr(main_addr); _twohop_nodes[tnid]->set_is_strict(false); UNUSED(nid); } catch (BadNeighbor bn) { _twohop_nodes[tnid]->set_is_strict(true); } return tnid; } OlsrTypes::TwoHopNodeID Neighborhood::add_twohop_node(const IPv4& main_addr, const OlsrTypes::TwoHopLinkID tlid) throw(BadTwoHopNode) { debug_msg("MyMainAddr %s MainAddr %s TwoHopLinkID %u\n", cstring(_fm.get_main_addr()), cstring(main_addr), XORP_UINT_CAST(tlid)); #ifdef DETAILED_DEBUG if (_twohop_node_addrs.find(main_addr) != _twohop_node_addrs.end()) { xorp_throw(BadTwoHopNode, c_format("Mapping for %s already exists", cstring(main_addr))); } #endif OlsrTypes::TwoHopNodeID tnid = _next_twohop_nodeid++; // Throw an exception if we overflowed the TwoHopNodeID space. if (_twohop_nodes.find(tnid) != _twohop_nodes.end()) { xorp_throw(BadTwoHopNode, c_format("Mapping for TwoHopNodeID %u already exists", XORP_UINT_CAST(tnid))); } _twohop_nodes[tnid] = new TwoHopNeighbor(_eventloop, this, tnid, main_addr, tlid); _twohop_node_addrs[main_addr] = tnid; return tnid; } bool Neighborhood::delete_twohop_node(const OlsrTypes::TwoHopNodeID tnid) { debug_msg("TwoHopNodeID %u\n", XORP_UINT_CAST(tnid)); map::iterator ii = _twohop_nodes.find(tnid); if (ii == _twohop_nodes.end()) return false; TwoHopNeighbor* n2 = (*ii).second; map::iterator jj = _twohop_node_addrs.find(n2->main_addr()); for (jj = _twohop_node_addrs.begin(); jj != _twohop_node_addrs.end(); jj++) { if ((*jj).second == tnid) { _twohop_node_addrs.erase(jj); break; } } // Delete all two-hop link entries which point to this two-hop neighbor. n2->delete_all_twohop_links(); delete n2; _twohop_nodes.erase(ii); // 8.5 Schedule an MPR recount as the two-hop neighborhood has changed. schedule_mpr_recount(); return true; } OlsrTypes::TwoHopNodeID Neighborhood::get_twohop_nodeid_by_main_addr(const IPv4& main_addr) throw(BadTwoHopNode) { debug_msg("MainAddr %s\n", cstring(main_addr)); map::const_iterator ii = _twohop_node_addrs.find(main_addr); if (ii == _twohop_node_addrs.end()) { xorp_throw(BadTwoHopNode, c_format("No mapping for %s exists", cstring(main_addr))); } return (*ii).second; } const TwoHopNeighbor* Neighborhood::get_twohop_neighbor(const OlsrTypes::TwoHopNodeID tnid) const throw(BadTwoHopNode) { map::const_iterator ii = _twohop_nodes.find(tnid); if (ii == _twohop_nodes.end()) { xorp_throw(BadTwoHopNode, c_format("No mapping for %u exists", XORP_UINT_CAST(tnid))); } return (*ii).second; } void Neighborhood::get_twohop_neighbor_list(list& n2_list) const { map::const_iterator ii; for (ii = _twohop_nodes.begin(); ii != _twohop_nodes.end(); ii++) n2_list.push_back((*ii).first); } bool Neighborhood::push_twohop_neighbor(TwoHopNeighbor* n2) { debug_msg("called\n"); // Reject two-hop neighbors which are also Neighbors, or have no links. if (! n2->is_strict()) { debug_msg("%s: rejected n2 %s due to !strict\n", cstring(_fm.get_main_addr()), cstring(n2->main_addr())); return false; } if (! n2->is_reachable()) { debug_msg("%s: rejected n2 %s due to !reachable\n", cstring(_fm.get_main_addr()), cstring(n2->main_addr())); return false; } const TwoHopLink* l2; try { l2 = find_best_twohop_link(n2); } catch (BadTwoHopCoverage btc) { debug_msg("Caught BadTwoHopCoverage.\n"); //xorp_print_standard_exceptions(); return false; } _rm->add_twohop_link(l2->nexthop(), l2, n2); return true; } /* * MPR selector methods. */ void Neighborhood::update_mpr_selector(const OlsrTypes::NeighborID nid, const TimeVal& vtime) { debug_msg("NeighborID %u Vtime %s\n", XORP_UINT_CAST(nid), cstring(vtime)); _neighbors[nid]->set_is_mpr_selector(true, vtime); debug_msg("Added MPR selector %s\n", cstring(_neighbors[nid]->main_addr())); XLOG_TRACE(_olsr.trace()._mpr_selection, "Added MPR selector %s\n", cstring(_neighbors[nid]->main_addr())); bool was_mpr = is_mpr(); bool is_created = false; if (_mpr_selector_set.find(nid) == _mpr_selector_set.end()) { _mpr_selector_set.insert(nid); is_created = true; } // 9.3 Start originating TC broadcasts, if we have now become an MPR. if (!was_mpr && ! _mpr_selector_set.empty()) { if (is_created) { debug_msg("%s now becoming an MPR node.\n", cstring(_fm.get_main_addr())); } start_tc_timer(); } XLOG_ASSERT(_mpr_selector_set.size() > 0); } void Neighborhood::delete_mpr_selector(const OlsrTypes::NeighborID nid) { debug_msg("NeighborID %u\n", XORP_UINT_CAST(nid)); XLOG_ASSERT(_mpr_selector_set.find(nid) != _mpr_selector_set.end()); _mpr_selector_set.erase(nid); _neighbors[nid]->set_is_mpr_selector(false, TimeVal()); debug_msg("Expired MPR selector %s\n", cstring(_neighbors[nid]->main_addr())); XLOG_TRACE(_olsr.trace()._mpr_selection, "Expired MPR selector %s\n", cstring(_neighbors[nid]->main_addr())); // 9.3 If our MPR selector set is now empty, // change the TC timer state to FINISHING. if (_mpr_selector_set.empty()) { debug_msg("%s now has no MPR selectors.\n", cstring(_fm.get_main_addr())); finish_tc_timer(); // 9.3 When a change to the MPR selector set is detected and // this change can be attributed to a link failure, an early // TC message SHOULD be transmitted. if (_loss_triggered_tc_enabled) { debug_msg("%s: Scheduling an immediate TC due to the loss of " "all MPR selectors.\n", cstring(_fm.get_main_addr())); reschedule_immediate_tc_timer(); } } } bool Neighborhood::is_mpr_selector_addr(const IPv4& remote_addr) { debug_msg("RemoteAddr %s\n", cstring(remote_addr)); bool is_mpr_selector = false; try { OlsrTypes::NeighborID nid = get_neighborid_by_remote_addr(remote_addr); is_mpr_selector = _neighbors[nid]->is_mpr_selector(); } catch (BadNeighbor& bn) { debug_msg("threw exception: %s\n", cstring(bn)); } return is_mpr_selector; } /* * MPR selection methods. */ void Neighborhood::recount_mpr_set() { //if (_neighbors.empty() // return; ostringstream dbg; // Clear all existing MPR state for Neighbors. reset_onehop_mpr_state(); // Clear all existing MPR state for TwoHopNeighbors. // Compute number of now uncovered reachable nodes at radius=2. const size_t reachable_n2_count = reset_twohop_mpr_state(dbg); size_t covered_n2_count = 0; set new_mpr_set; // For debugging. if (_mpr_computation_enabled) { // 8.3.1, 1: Start with an MPR set made of all members of N with // willingness equal to WILL_ALWAYS. covered_n2_count += consider_persistent_cand_mprs(dbg); // 8.3.1, 3: If N2 is still not fully covered, ensure that for // all uncovered strict N2 reachable only via 1 edge, their // neighbor N is selected as an MPR. if (covered_n2_count < reachable_n2_count) { covered_n2_count += consider_poorly_covered_twohops(dbg); } // 8.3.1, 4: If N2 is still not fully covered, consider // candidate MPRs in descending order of willingness, reachability // and degree. if (covered_n2_count < reachable_n2_count) { consider_remaining_cand_mprs(reachable_n2_count, covered_n2_count, dbg); } if (covered_n2_count < reachable_n2_count) { dbg << " covered_n2_count: " << covered_n2_count << " reachable_n2_count: " << reachable_n2_count << endl; XLOG_WARNING("%s", dbg.str().c_str()); // Invariant: All reachable N2 must now be covered by MPRs. XLOG_ASSERT(covered_n2_count >= reachable_n2_count); } size_t removed_mpr_count = minimize_mpr_set(new_mpr_set); debug_msg("MPRs removed: %u\n", XORP_UINT_CAST(removed_mpr_count)); // Invariant: All reachable N2 must remain covered by MPRs after // minimizing the set. XLOG_ASSERT(covered_n2_count >= reachable_n2_count); } else { // All one-hop neighbors with non-zero willingness are considered // MPRs when the MPR algorithm has been disabled. // Typically this is used when the topology is extremely dense. // XXX This path is UNTESTED! size_t added_mpr_count = mark_all_n1_as_mprs(new_mpr_set); UNUSED(added_mpr_count); } if (! (new_mpr_set == _mpr_set)) { // TODO: Print the changes to the MPR set just like olsrd does. debug_msg("%s: MPR set changed, now: \n", cstring(_fm.get_main_addr())); set::const_iterator ii; for (ii = new_mpr_set.begin(); ii != new_mpr_set.end(); ii++) { debug_msg("\t%s\n", cstring(get_neighbor((*ii))->main_addr())); } // TODO: Section 8.5: We MAY trigger an early HELLO when the MPR // set changes. The RFC does not specify if this early HELLO // should be sent on all interfaces, or only those from which the // newly selected MPRs are reachable. } // The protocol simulator needs to see the entire MPR set for // keeping invariants. _mpr_set = new_mpr_set; } size_t Neighborhood::mark_all_n1_as_mprs(set& final_mpr_set) { size_t mpr_count = 0; map::iterator ii; for (ii = _neighbors.begin(); ii != _neighbors.end(); ii++) { Neighbor* n =(*ii).second; if (n->willingness() == OlsrTypes::WILL_NEVER) continue; n->set_is_mpr(true); final_mpr_set.insert(n->id()); ++mpr_count; } return mpr_count; } size_t Neighborhood::minimize_mpr_set(set& final_mpr_set) throw(BadTwoHopCoverage) { size_t withdrawn_mpr_count = 0; //UNUSED(final_mpr_set); // 8.3.1, 5: Select all N such that it has been selected as an MPR // by the current MPR recount, in ascending order of willingness, and // consider if it is essential for coverage of its 2-hop neighbors. // If not, remove it from the MPR set. OlsrTypes::WillType will; map::iterator ii; #if 1 // XXX: Make a pass to ensure nodes with WILL_ALWAYS are // added to the final set. // Such nodes are ALWAYS MPRs even if redundant, and SHOULD NOT // be withdrawn by the loop below. // We need to do this because the protocol simulator currently relies // on being able to perform an exact comparison with our final // MPR set for protocol validation. // It should not be needed for production, as n->is_mpr() is only // examined when a TC message is being issued, or ANSN is being updated. for (ii = _neighbors.begin(); ii != _neighbors.end(); ii++) { const Neighbor* n =(*ii).second; if (n->willingness() == OlsrTypes::WILL_ALWAYS) final_mpr_set.insert(n->id()); } #endif for (will = OlsrTypes::WILL_LOW; will < OlsrTypes::WILL_ALWAYS; will++) { // For each previously selected MPR, // in reverse order of ID. XXX this may not be deterministic. for (ii = _neighbors.begin(); ii != _neighbors.end(); ii++) { Neighbor* n =(*ii).second; if (n->is_mpr() && n->willingness() == will) { // // If any N2 covered by N is poorly covered without N, // do not remove N from the MPR set. // if (! is_essential_mpr(n)) { // // N is non-essential. Withdraw N from each of its N2. // const set& n2_links = n->twohop_links(); // For each N2 of N. set::const_iterator jj; for (jj = n2_links.begin(); jj != n2_links.end(); jj++) { TwoHopLink* l2 = _twohop_links[(*jj)]; TwoHopNeighbor* n2 = l2->destination(); // Withdraw N from this N2. n2->withdraw_covering_mpr(n->id()); n->set_is_mpr(false); // Invariant: N2 must remain covered. if (! n2->is_covered()) { xorp_throw(BadTwoHopCoverage, c_format( "OLSR node %s has uncovered " "TwoHopNode %u (%sreachable " "%u two-hop links)", cstring(_fm.get_main_addr()), XORP_UINT_CAST(n2->id()), n2->is_reachable() ? "" : "un", XORP_UINT_CAST(n2->reachability()))); } } ++withdrawn_mpr_count; } else { // N remains an MPR. Add N to the produced MPR set. final_mpr_set.insert(n->id()); } } } } return withdrawn_mpr_count; } void Neighborhood::add_cand_mpr(const OlsrTypes::NeighborID nid) { schedule_mpr_recount(); // The nid parameter is not needed if MPR selection is being // performed for the entire N2 set, which is the default behaviour. UNUSED(nid); } void Neighborhood::withdraw_cand_mpr(const OlsrTypes::NeighborID nid) { schedule_mpr_recount(); // The nid parameter is not needed if MPR selection is being // performed for the entire N2 set, which is the default behaviour. UNUSED(nid); } bool Neighborhood::is_essential_mpr(const Neighbor* n) { bool is_essential = false; const set& n2_links = n->twohop_links(); // For each reachable strict N2 of N, consider if its MPR status // is essential to covering the strict N2. set::const_iterator ii; for (ii = n2_links.begin(); ii != n2_links.end(); ii++) { const TwoHopLink* l2 = _twohop_links[(*ii)]; const TwoHopNeighbor* n2 = l2->destination(); if (n2->is_strict() && n2->coverage() <= mpr_coverage()) { is_essential = true; break; } } return is_essential; } void Neighborhood::reset_onehop_mpr_state() { map::iterator ii; for (ii = _neighbors.begin(); ii != _neighbors.end(); ii++) { Neighbor* n = (*ii).second; n->set_is_mpr(false); } } size_t Neighborhood::reset_twohop_mpr_state(ostringstream& oss) { size_t n2_count = 0; // Clear all existing MPR coverage counts. map::iterator ii; for (ii = _twohop_nodes.begin(); ii != _twohop_nodes.end(); ii++) { TwoHopNeighbor* n2 = (*ii).second; // Reset N2's MPR coverage. n2->reset_covering_mprs(); // Amortize the runtime cost of tree walks by precomputing N2's // reachability. We can't distribute it, as it relies on // data known at this point in time. // [Computing the degree of each N is already distributed, and // therefore amortized to whenever the topology around N changes.] update_twohop_reachability(n2); // If N2 is strict and is reachable, count it as a member of N2. if (n2->is_strict() && n2->is_reachable()) { oss << "Counting 2-hop neighbor, is strict and reachable: " << n2->reachability() << ", n2: " << n2->toStringBrief() << endl; n2_count++; } } return n2_count; } void Neighborhood::update_onehop_reachability(Neighbor* n) { size_t reachability = 0; const set& n2_links = n->twohop_links(); // Consider each edge between this N and each N2. // If N2 is uncovered, count it. set::const_iterator ii; for (ii = n2_links.begin(); ii != n2_links.end(); ii++) { TwoHopLink* l2 = _twohop_links[(*ii)]; if (! l2->destination()->is_covered()) ++reachability; } n->set_reachability(reachability); } void Neighborhood::update_twohop_reachability(TwoHopNeighbor* n2) { size_t reachability = 0; const set& n_links = n2->twohop_links(); // Consider each edge between each N and this N2. // If the N is an MPR candidate, the edge is considered OK for // transit and is a link by which the N2 reachable, so count it. set::const_iterator ii; for (ii = n_links.begin(); ii != n_links.end(); ii++) { TwoHopLink* l2 = _twohop_links[(*ii)]; // Persistent MPR candidates take priority, hence short-circuit OR. // // XXX: Should check if l2 is a 'good' link for ETX // to be treated correctly during MPR computation. if (l2->nexthop()->is_persistent_cand_mpr() || l2->nexthop()->is_cand_mpr()) { debug_msg("%s: %s is reachable via cand-mpr %s\n", cstring(_fm.get_main_addr()), cstring(l2->destination()->main_addr()), cstring(l2->nexthop()->main_addr())); ++reachability; } } n2->set_reachability(reachability); } size_t Neighborhood::consider_persistent_cand_mprs(ostringstream& dbg) { size_t persistent_mpr_count = 0; // Mark each WILL_ALWAYS neighbor as an MPR in this pass. map::iterator ii; for (ii = _neighbors.begin(); ii != _neighbors.end(); ii++) { Neighbor* n = (*ii).second; if (n->willingness() != OlsrTypes::WILL_ALWAYS) continue; n->set_is_mpr(true); ++persistent_mpr_count; } debug_msg("%s: persistent_mpr_count is %u\n", cstring(_fm.get_main_addr()), XORP_UINT_CAST(persistent_mpr_count)); size_t covered_n2_count = 0; // Maintain the coverage count on each strict reachable two-hop // neighbor, by considering each two-hop link. // // XXX: To maintain the "poorly covered" invariant on each n2 in // minimize_mpr_set(), it's necessary to update the coverage count // whilst we are here. // // The invariant is there to ensure that the implementation of // MPR_COVERAGE is correct. It may be removed in future when the code // has been more rigorously tested, and then the need to mark each n2 // as covered by a WILL_ALWAYS node will go away. // map::iterator jj; for (jj = _twohop_links.begin(); jj != _twohop_links.end(); jj++) { TwoHopLink* l2 = (*jj).second; Neighbor* n = l2->nexthop(); TwoHopNeighbor* n2 = l2->destination(); // If N is WILL_ALWAYS, mark its destination in N2 as covered by // the persistent MPR N. // Reachability of N2 is implied by the existence of the link // and the willingness of N, so checking for it is redundant here. if (n2->is_strict() && n->willingness() == OlsrTypes::WILL_ALWAYS) { debug_msg("%s: adding WILL_ALWAYS MPR %s\n", cstring(_fm.get_main_addr()), cstring(n->main_addr())); XLOG_ASSERT(n->is_mpr()); n2->add_covering_mpr(n->id()); dbg << "Covered n2: " << n2->toStringBrief() << " in consider_persistent.\n"; covered_n2_count++; } else { dbg << "NOT covering n2: " << n2->toStringBrief() << " in consider_persistent, strict: " << n2->is_strict() << " n: " << n->toStringBrief() << " n->willingness: " << n->willingness() << endl; } } // The count may be 0 if the persistent MPRs have no 2-hop links. debug_msg("%s: covered_n2_count is %u\n", cstring(_fm.get_main_addr()), XORP_UINT_CAST(covered_n2_count)); return covered_n2_count; } size_t Neighborhood::consider_poorly_covered_twohops(ostringstream& dbg) { size_t covered_n2_count = 0; map::iterator ii; for (ii = _twohop_nodes.begin(); ii != _twohop_nodes.end(); ii++) { TwoHopNeighbor* n2 = (*ii).second; if (n2->is_strict() && n2->reachability() == 1 && !n2->is_covered()) { // // N2 is a strict, uncovered two-hop neighbor with reachability // of 1, after ETX is taken into account. For brevity, // re-use find_best_twohop_link(). // // TODO: XXX: Whilst this considers ETX, it does not // handle the case in which no link with favorable ETX // was found, in which case an uncaught exception // will be raised -- until this is implemented correctly // in update_twohop_reachability(). // const TwoHopLink* l2 = find_best_twohop_link(n2); Neighbor* n = l2->nexthop(); // // N2 is therefore covered by N. // Mark N2 as covered by N. Mark N as MPR. // n2->add_covering_mpr(n->id()); n->set_is_mpr(true); dbg << "Counting poorly_covered n2: " << n2->toStringBrief() << " n is set as mpr: " << n->toStringBrief() << endl; covered_n2_count++; } else { dbg << "NOT Counting poorly_covered n2: " << n2->toStringBrief() << " strict: " << n2->is_strict() << " reachability: " << n2->reachability() << " n2-covered: " << n2->is_covered() << endl; } } return covered_n2_count; } void Neighborhood::consider_remaining_cand_mprs(const size_t n2_count, size_t& covered_n2_count, ostringstream& dbg) { typedef set CandMprBag; UNUSED(n2_count); CandMprBag cand_mprs; // Construct the MPR candidate set. // Skip the ALWAYS neighbors, they have already been covered. map::iterator ii; for (ii = _neighbors.begin(); ii != _neighbors.end(); ii++) { Neighbor* n = (*ii).second; if (n->is_cand_mpr() && n->willingness() != OlsrTypes::WILL_ALWAYS) { // 8.4.1, 4.1: // Recompute n's reachability. We cannot amortize or // distribute this computation as it depends on the // state of the MPR set at this point in time. update_onehop_reachability(n); // Consider N as a candidate MPR if and only if it // covers any uncovered N2. n will point to a valid // object throughout this scope. if (n->reachability() > 0) cand_mprs.insert(n); // Insertion sort. } else { dbg << "Not using n: " << n->toStringBrief() << " as cand_mpr, willingness: " << n->willingness() << " is_cand_mpr: " << n->is_cand_mpr() << " is_mpr: " << n->is_mpr() << endl; } } // 8.3.1, 4.2: Select as an MPR the node with highest // willingness, reachability and degree. CandMprBag::iterator jj; for (jj = cand_mprs.begin(); jj != cand_mprs.end(); jj++) { Neighbor* n = (*jj); dbg << "Checking neighbour: " << n->toStringBrief() << " link count: " << n->twohop_links().size() << endl; // If all N2 are covered, we're done. // How do you know you haven't counted duplicates?? Comments indicate that redundant // MPRs will be cleaned up below.... --Ben //if (covered_n2_count >= n2_count) // break; // Mark each N2 reachable from this N as covered, // and mark this N as a candidate MPR. // Redundant MPRs will be later eliminated by minimize_mpr_set(). const set& links = n->twohop_links(); set::const_iterator kk; for (kk = links.begin(); kk != links.end(); kk++) { TwoHopLink* l2 = _twohop_links[(*kk)]; TwoHopNeighbor* n2 = l2->destination(); if (n2->is_strict()) { // Mark this strict N2 as covered by N. Mark N as MPR. dbg << "Adding covering_mpr: " << n->toStringBrief() << " to n2: " << n2->toStringBrief() << endl; n2->add_covering_mpr(n->id()); n->set_is_mpr(true); covered_n2_count++; } else { dbg << "n2: " << n2->toStringBrief() << " is strict, skipping.\n"; } } } } /* * Callback methods, for event processing. */ void Neighborhood::event_link_sym_timer(OlsrTypes::LogicalLinkID linkid) { XLOG_ASSERT(_links.find(linkid) != _links.end()); LogicalLink* l = _links[linkid]; // The timer may have fired at the same time as // the ASYM timer. if (l->link_type() != OlsrTypes::SYM_LINK) return; // Transition: SYM -> ASYM. debug_msg("LinkID %u: sym -> asym.\n", XORP_UINT_CAST(linkid)); XLOG_ASSERT(_neighbors.find(l->neighbor_id()) != _neighbors.end()); Neighbor* n = l->destination(); // Propagate status change to neighbor. // 8.5: This is "a change in the neighborhood" n->update_link(linkid); //_rm->schedule_route_update(); } void Neighborhood::event_link_asym_timer(OlsrTypes::LogicalLinkID linkid) { // Transition: ASYM -> LOST. debug_msg("LinkID %u: asym -> lost.\n", XORP_UINT_CAST(linkid)); XLOG_ASSERT(_links.find(linkid) != _links.end()); LogicalLink* l = _links[linkid]; // Propagate status change to neighbor. // 8.5: This is "a change in the neighborhood" // XXX: Should we delete the neighbor at this time? // Or withdraw it from interfaces? XLOG_ASSERT(_neighbors.find(l->neighbor_id()) != _neighbors.end()); Neighbor* n = l->destination(); n->update_link(linkid); // 8.5: When we lose a neighbor, we lose all links to the // two-hop neighbors which traverse that neighbor. // This will obviously trigger a route recomputation. n->delete_all_twohop_links(); //_rm->schedule_route_update(); } void Neighborhood::event_link_lost_timer(OlsrTypes::LogicalLinkID linkid) { XLOG_UNFINISHED(); // XXX TODO UNUSED(linkid); } void Neighborhood::event_link_dead_timer(OlsrTypes::LogicalLinkID linkid) { // Transition: * -> DEAD. debug_msg("LinkID %u expired, deleting it.\n", XORP_UINT_CAST(linkid)); delete_link(linkid); } void Neighborhood::event_mpr_selector_expired(const OlsrTypes::NeighborID nid) { // Transition: * -> DEAD. debug_msg("MPR selector for neighbor %u expired, deleting it.\n", XORP_UINT_CAST(nid)); // 8.4.1: Deletion of MPR selector tuples occurs in case of // expiration of the timer. delete_mpr_selector(nid); } void Neighborhood::event_twohop_link_dead_timer(const OlsrTypes::TwoHopLinkID tlid) { // Transition: * -> DEAD. debug_msg("TwoHopLinkID %u -> DEAD.\n", XORP_UINT_CAST(tlid)); // 4.3.2: N_time specifies the time at which the 2-hop tuple // expires and *MUST* be removed. // If this is the last link to the two-hop neighbor, the two-hop // neighbor will now be deleted. delete_twohop_link(tlid); } bool Neighborhood::event_receive_hello( Message* msg, const IPv4& remote_addr, const IPv4& local_addr) { HelloMessage* hello = dynamic_cast(msg); if (0 == hello) return false; // I did not consume this message. if (hello->ttl() != 1 || hello->hops() != 0) { debug_msg("Rejecting HELLO with ttl %u and hop-count %u\n", hello->ttl(), hello->hops()); XLOG_TRACE(_olsr.trace()._input_errors, "Rejecting HELLO with ttl %u and hop-count %u\n", hello->ttl(), hello->hops()); return true; // I consumed this message. } // We should never see HELLO messages originating from our own // main address, unless they are being looped back for some reason. if (_fm.get_main_addr() == hello->origin()) { debug_msg("Rejecting self-originated HELLO\n"); XLOG_TRACE(_olsr.trace()._input_errors, "Rejecting self-originated HELLO\n"); return true; } // Invariant: I should only receive HELLO messages from an // enabled interface. XLOG_ASSERT(true == _fm.get_face_enabled(hello->faceid())); bool i_was_mentioned = false; LinkCode mylc(OlsrTypes::UNSPEC_LINK, OlsrTypes::NOT_NEIGH); const HelloMessage::LinkBag& linkbag = hello->links(); // // Make a first pass through the link state information to see // if our own address is listed there, as this affects how the // link-state timers are updated, as well as telling us if the // neighbor selects us as an MPR. // // Section 7.1.1, 2.2: If the node finds the address of // the interface which received the HELLO message among // the addresses listed in the link message, then the // rules for updating the link tuple change. // if (! hello->links().empty()) { HelloMessage::LinkBag::const_iterator ii; for (ii = linkbag.begin(); ii != linkbag.end(); ii++) { const LinkAddrInfo& lai = (*ii).second; if (local_addr == lai.remote_addr()) { mylc = (*ii).first; i_was_mentioned = true; break; } } } // Update the link state tuple, creating it if needed. bool is_new_link = false; OlsrTypes::LogicalLinkID linkid; try { linkid = update_link(hello->faceid(), remote_addr, local_addr, hello->expiry_time(), is_new_link); // 7.1.1: Update the link state timers. _links[linkid]->update_timers(hello->expiry_time(), i_was_mentioned, mylc); } catch(BadLogicalLink& bl) { debug_msg("could not update link\n"); return true; // I consumed this message. } // Update the neighbor database tuple, creating it if needed. // 8.4.1: Update the MPR selector set, if the neighbor selects // me as an MPR. bool is_new_neighbor = false; bool is_mpr_selector = i_was_mentioned && mylc.is_mpr_neighbor(); OlsrTypes::NeighborID nid; try { nid = update_neighbor(hello->origin(), linkid, is_new_link, hello->willingness(), is_mpr_selector, hello->expiry_time(), is_new_neighbor); } catch(BadNeighbor& bn) { debug_msg("could not update neighbor\n"); if (is_new_link) delete_link(linkid); return true; // I consumed this message. } Neighbor* n = _neighbors[nid]; // Process two-hop neighbors, if link(s) with neighbor are now symmetric. if (n->is_sym()) { HelloMessage::LinkBag::const_iterator ii; for (ii = linkbag.begin(); ii != linkbag.end(); ii++) { const LinkAddrInfo& lai = (*ii).second; // 8.2.1, 1.1: A node cannot be its own two-hop neighbor. if (_fm.is_local_addr(lai.remote_addr())) continue; switch ((*ii).first.neighbortype()) { case OlsrTypes::MPR_NEIGH: case OlsrTypes::SYM_NEIGH: // 8.2.1, 1.2: Create or update two-hop neighbor tuple. // Interpreted as: add a link to a two-hop neighbor. // May throw BadTwoHopLink. update_twohop_link(lai, *n, hello->faceid(), hello->expiry_time()); break; case OlsrTypes::NOT_NEIGH: // 8.2.1, 2: Delete two-hop neighbor tuple. // Interpreted as: delete a neighbor's link to a // two-hop neighbor. delete_twohop_link_by_addrs(hello->origin(), lai.remote_addr()); break; default: break; } } } return true; // I consumed this message. } bool Neighborhood::event_send_tc() { debug_msg("%s: TC timer fired\n", cstring(_fm.get_main_addr())); #ifdef DETAILED_DEBUG // 9.3 TC timer should only fire if running or finishing. XLOG_ASSERT(_tc_timer_state == TC_RUNNING || _tc_timer_state == TC_FINISHING); #endif TcMessage* tc = new TcMessage(); tc->set_expiry_time(get_topology_hold_time()); tc->set_origin(_fm.get_main_addr()); tc->set_ttl(OlsrTypes::MAX_TTL); tc->set_hop_count(0); tc->set_seqno(_fm.get_msg_seqno()); if (_tc_timer_state == TC_RUNNING) { size_t curr_ans_count = 0; size_t curr_ans_changes = 0; // Compute the Advertised Neighbor Set, according to the // current TC_REDUNDANCY mode. map::const_iterator ii; for (ii = _neighbors.begin(); ii != _neighbors.end(); ii++) { Neighbor* n = (*ii).second; // Maintain a count of the number of Neighbors which have // changed state since the last TC broadcast. bool was_advertised = n->is_advertised(); bool is_advertised = is_tc_advertised_neighbor(n); if (was_advertised != is_advertised) ++curr_ans_changes; if (is_advertised) { ++curr_ans_count; tc->add_neighbor(n->main_addr()); } // Record the status of this neighbor in the current TC phase. n->set_is_advertised(is_advertised); } // Deal with transitions to an empty ANS. if (curr_ans_count == 0) { XLOG_ASSERT(tc->neighbors().empty()); if (_tc_previous_ans_count == 0) { // If the TC timer was started because we have a non-empty // MPR selector set, but the ANS set became empty before // the current timer phase, transition to STOPPED state. stop_tc_timer(); return false; } else { // If the ANS just computed was empty, transition to // FINISHING state to signal that we now have no neighbors // to advertise. finish_tc_timer(); } } // Deal with a non-empty ANS. // If there were any ANS changes, bump the ANSN for this TC. if (curr_ans_count > 0 && curr_ans_changes > 0) ++_tc_current_ansn; // Maintain a count of ANS between TC timer phases for comparison. _tc_previous_ans_count = curr_ans_count; } tc->set_ansn(_tc_current_ansn); bool is_flooded = _fm.flood_message(tc); // consumes tc UNUSED(is_flooded); // XXX no refcounting delete tc; // If TC timer is in 'finishing' state, and no further ticks are // pending, ensure that the TC timer is now unscheduled. if (_tc_timer_state == TC_FINISHING) { if (--_tc_timer_ticks_remaining == 0) { _tc_timer_state = TC_STOPPED; return false; } } return true; } /* * Timer manipulation methods. */ void Neighborhood::stop_all_timers() { stop_tc_timer(); } void Neighborhood::start_tc_timer() { debug_msg("%s -> TC_RUNNING\n", cstring(_fm.get_main_addr())); _tc_timer_state = TC_RUNNING; _tc_timer = _eventloop. new_periodic(get_tc_interval(), callback(this, &Neighborhood::event_send_tc)); } void Neighborhood::stop_tc_timer() { debug_msg("%s -> TC_STOPPED\n", cstring(_fm.get_main_addr())); _tc_timer.clear(); _tc_timer_state = TC_STOPPED; } void Neighborhood::finish_tc_timer() { XLOG_ASSERT(_tc_timer_state == TC_RUNNING || _tc_timer_state == TC_FINISHING); if (_tc_timer_state == TC_RUNNING) { debug_msg("%s -> TC_FINISHING\n", cstring(_fm.get_main_addr())); _tc_timer_state = TC_FINISHING; // Bump the TC timer to make sure everyone notices. ++_tc_current_ansn; // Run for at least another 3 ticks (TOP_HOLD_TIME / TOP_INTERVAL). _tc_timer_ticks_remaining = 3; } } void Neighborhood::reschedule_immediate_tc_timer() { XLOG_ASSERT(_tc_timer_state == TC_RUNNING || _tc_timer_state == TC_FINISHING); _tc_timer.schedule_now(); } void Neighborhood::restart_tc_timer() { reschedule_tc_timer(); } void Neighborhood::reschedule_tc_timer() { XLOG_ASSERT(_tc_timer_state == TC_RUNNING || _tc_timer_state == TC_FINISHING); _tc_timer.reschedule_after(get_tc_interval()); } xorp/contrib/olsr/profile_vars.hh0000664000076400007640000000325011421137511017307 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8 sw=4: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/contrib/olsr/profile_vars.hh,v 1.3 2008/10/02 21:56:35 bms Exp $ #ifndef __OLSR_PROFILE_VARS_HH__ #define __OLSR_PROFILE_VARS_HH__ /** * Profile variables * See: profile_vars.cc for definitions. */ const string profile_message_in = "message_in"; const string profile_route_ribin = "route_ribin"; const string profile_route_rpc_in = "route_rpc_in"; const string profile_route_rpc_out = "route_rpc_out"; const string trace_message_in = "trace_message_in"; const string trace_message_out = "trace_message_out"; const string trace_nexthop_resolution = "trace_nexthop_resolution"; const string trace_policy_configure = "trace_policy_configure"; const string trace_state_change = "trace_state_change"; void initialize_profiling_variables(Profile& p); #endif // __OLSR_PROFILE_VARS_HH__ xorp/contrib/olsr/olsr.cc0000664000076400007640000002214111421137511015561 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8 sw=4: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #include "olsr_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/callback.hh" #include "libxorp/ipv4.hh" #include "libxorp/ipv4net.hh" #include "libxorp/status_codes.h" #include "libxorp/service.hh" #include "libxorp/eventloop.hh" #include "olsr.hh" Olsr::Olsr(EventLoop& eventloop, IO* io) : _eventloop(eventloop), _io(io), _fm(*this, eventloop), _nh(*this, eventloop, _fm), _tm(*this, eventloop, _fm, _nh), _er(*this, eventloop, _fm, _nh), _rm(*this, eventloop, &_fm, &_nh, &_tm, &_er), _reason("Waiting for IO"), _process_status(PROC_STARTUP) { _nh.set_topology_manager(&_tm); _fm.set_neighborhood(&_nh); _nh.set_route_manager(&_rm); _tm.set_route_manager(&_rm); _er.set_route_manager(&_rm); _io->register_receive(callback(this, &Olsr::receive)); //initialize_profiling_variables(_profile); } ProcessStatus Olsr::status(string& reason) { if (PROC_STARTUP == _process_status) { if (SERVICE_RUNNING == _io->status()) { _process_status = PROC_READY; _reason = "Running"; } } reason = _reason; return _process_status; } void Olsr::shutdown() { _io->shutdown(); _reason = "shutting down"; _process_status = PROC_SHUTDOWN; } // Methods typically called by code in FaceManager (coming out of us). void Olsr::receive(const string& interface, const string& vif, IPv4 dst, uint16_t dport, IPv4 src, uint16_t sport, uint8_t* data, uint32_t len) { XLOG_TRACE(trace()._packets, "interface %s vif %s dst %s:%u src %s:%u data %p len %u\n", interface.c_str(), vif.c_str(), cstring(dst), XORP_UINT_CAST(dport), cstring(src), XORP_UINT_CAST(sport), data, len); debug_msg("interface %s vif %s dst %s:%u src %s:%u data %p len %u\n", interface.c_str(), vif.c_str(), cstring(dst), XORP_UINT_CAST(dport), cstring(src), XORP_UINT_CAST(sport), data, len); // TODO: Decode packet in order to pretty-print it. _fm.receive(interface, vif, dst, dport, src, sport, data, len); } bool Olsr::transmit(const string& interface, const string& vif, const IPv4& dst, const uint16_t& dport, const IPv4& src, const uint16_t& sport, uint8_t* data, const uint32_t& len) { XLOG_TRACE(trace()._packets, "interface %s vif %s dst %s:%u src %s:%u data %p len %u\n", interface.c_str(), vif.c_str(), cstring(dst), XORP_UINT_CAST(dport), cstring(src), XORP_UINT_CAST(sport), data, len); debug_msg("interface %s vif %s dst %s:%u src %s:%u data %p len %u\n", interface.c_str(), vif.c_str(), cstring(dst), XORP_UINT_CAST(dport), cstring(src), XORP_UINT_CAST(sport), data, len); // TODO: Decode packet in order to pretty-print it. return _io->send(interface, vif, src, sport, dst, dport, data, len); } uint32_t Olsr::get_mtu(const string& interface) { debug_msg("Interface %s\n", interface.c_str()); return _io->get_mtu(interface); } bool Olsr::get_broadcast_address(const string& interface, const string& vif, const IPv4& address, IPv4& bcast_address) { return _io->get_broadcast_address(interface, vif, address, bcast_address); } bool Olsr::add_route(IPv4Net net, IPv4 nexthop, uint32_t faceid, uint32_t metric, const PolicyTags& policytags) { debug_msg("Net %s Nexthop %s metric %d policy %s\n", cstring(net), cstring(nexthop), metric, cstring(policytags)); XLOG_TRACE(trace()._routes, "Add route " "Net %s Nexthop %s metric %d policy %s\n", cstring(net), cstring(nexthop), metric, cstring(policytags)); return _io->add_route(net, nexthop, faceid, metric, policytags); } bool Olsr::replace_route(IPv4Net net, IPv4 nexthop, uint32_t faceid, uint32_t metric, const PolicyTags& policytags) { debug_msg("Net %s Nexthop %s metric %d policy %s\n", cstring(net), cstring(nexthop), metric, cstring(policytags)); XLOG_TRACE(trace()._routes, "Replace route " "Net %s Nexthop %s metric %d policy %s\n", cstring(net), cstring(nexthop), metric, cstring(policytags)); return _io->replace_route(net, nexthop, faceid, metric, policytags); } bool Olsr::delete_route(IPv4Net net) { debug_msg("Net %s\n", cstring(net)); XLOG_TRACE(trace()._routes, "Delete route Net %s\n", cstring(net)); return _io->delete_route(net); } bool Olsr::is_vif_broadcast_capable(const string& interface, const string& vif) { return _io->is_vif_broadcast_capable(interface, vif); } bool Olsr::is_vif_multicast_capable(const string& interface, const string& vif) { return _io->is_vif_multicast_capable(interface, vif); } bool Olsr::enable_address(const string& interface, const string& vif, const IPv4& address, const uint16_t& port, const IPv4& all_nodes_address) { return _io->enable_address(interface, vif, address, port, all_nodes_address); } bool Olsr::disable_address(const string& interface, const string& vif, const IPv4& address, const uint16_t& port) { return _io->disable_address(interface, vif, address, port); } // Methods typically called by XRL target (going into us). bool Olsr::bind_address(const string& interface, const string& vif, const IPv4& local_addr, const uint32_t& local_port, const IPv4& all_nodes_addr, const uint32_t& all_nodes_port) { try { OlsrTypes::FaceID faceid = face_manager().create_face(interface, vif); face_manager().set_local_addr(faceid, local_addr); face_manager().set_local_port(faceid, local_port); face_manager().set_all_nodes_addr(faceid, all_nodes_addr); face_manager().set_all_nodes_port(faceid, all_nodes_port); // XXX Do we need to do this yet? We aren't using link-local // addresses yet, and we still need to explicitly specify // a local interface address in this interface. //face_manager().activate_face(interface, vif); return true; } catch (...) {} return false; } bool Olsr::unbind_address(const string& interface, const string& vif) { try { OlsrTypes::FaceID faceid = face_manager().get_faceid(interface, vif); // TODO: xrlio teardown. return face_manager().delete_face(faceid); } catch (...) {} return false; } bool Olsr::set_interface_enabled(const string& interface, const string& vif, const bool enabled) { try { OlsrTypes::FaceID faceid = face_manager().get_faceid(interface, vif); bool success = face_manager().set_face_enabled(faceid, enabled); debug_msg("%s/%s %senabled ok\n", interface.c_str(), vif.c_str(), success ? "" : "not "); return success; } catch (...) {} return false; } bool Olsr::get_interface_enabled(const string& interface, const string& vif, bool& enabled) { try { OlsrTypes::FaceID faceid = face_manager().get_faceid(interface, vif); enabled = face_manager().get_face_enabled(faceid); return true; } catch (...) {} return false; } bool Olsr::get_interface_stats(const string& interface, const string& vif, FaceCounters& stats) { return face_manager().get_face_stats(interface, vif, stats); } bool Olsr::clear_database() { // Clear links first. This will nuke all the neighbors. _nh.clear_links(); // Clear topology control and MID entries. _tm.clear_tc_entries(); _tm.clear_mid_entries(); // Clear learned external routes. // Do not clear routes redistributed from another protocol. _er.clear_hna_routes_in(); // Finally clear the duplicate message set. _fm.clear_dupetuples(); return true; } void Olsr::configure_filter(const uint32_t& filter, const string& conf) { _policy_filters.configure(filter, conf); } void Olsr::reset_filter(const uint32_t& filter) { _policy_filters.reset(filter); } void Olsr::push_routes() { _rm.push_routes(); } bool Olsr::originate_external_route(const IPv4Net& net, const IPv4& nexthop, const uint32_t& metric, const PolicyTags& policytags) { return _er.originate_hna_route_out(net); UNUSED(nexthop); UNUSED(metric); UNUSED(policytags); } bool Olsr::withdraw_external_route(const IPv4Net& net) { try { _er.withdraw_hna_route_out(net); return true; } catch (...) {} return false; } xorp/contrib/olsr/link.cc0000664000076400007640000001163111421137511015541 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8 sw=4: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "olsr_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/callback.hh" #include "libxorp/ipv4.hh" #include "libxorp/ipv4net.hh" #include "libxorp/status_codes.h" #include "libxorp/service.hh" #include "libxorp/eventloop.hh" #include "olsr.hh" #include "link.hh" #include "neighbor.hh" #include "topology.hh" // TODO: Implement link hysteresis Section 14.3. // TODO: Implement ETX histogram measurements. /* * Link hysteresis protocol constants. */ double OlsrTypes::DEFAULT_HYST_THRESHOLD_HIGH = 0.8f; double OlsrTypes::DEFAULT_HYST_THRESHOLD_LOW = 0.3f; double OlsrTypes::DEFAULT_HYST_SCALING = 0.3f; LogicalLink::LogicalLink(Neighborhood* nh, EventLoop& eventloop, const OlsrTypes::LogicalLinkID id, const TimeVal& vtime, const IPv4& remote_addr, const IPv4& local_addr) : _nh(nh), _eventloop(eventloop), _id(id), _faceid(OlsrTypes::UNUSED_FACE_ID), _neighborid(OlsrTypes::UNUSED_NEIGHBOR_ID), _destination(0), _remote_addr(remote_addr), _local_addr(local_addr), _is_pending(false) { // Section 7.1.1, 1: HELLO Message Processing. // // L_SYM_time is not yet scheduled, for the link is not yet symmetric. // L_ASYM_time is not yet scheduled; will be scheduled by the first // call to update_timers(). // L_LOST_time is only scheduled when link-layer loss is reported. // L_time is scheduled here for the first time, although it will // also be updated by update_timers(). // _dead_timer = _eventloop.new_oneoff_after( vtime, callback(this, &LogicalLink::event_dead_timer)); } void LogicalLink::update_timers(const TimeVal& vtime, bool saw_self, const LinkCode lc) { debug_msg("Vtime %s Saw_self %s LinkCode %s\n", cstring(vtime), bool_c_str(saw_self), cstring(lc)); // 2.1 Update L_ASYM_time. if (_asym_timer.scheduled()) _asym_timer.clear(); _asym_timer = _eventloop.new_oneoff_after( vtime, callback(this, &LogicalLink::event_asym_timer)); TimeVal dead_time = _dead_timer.expiry(); // If our own address appears in a local link tuple, // our link with the neighbor may now be symmetric. if (saw_self) { if (lc.is_lost_link()) { // 2.2.1 L_SYM_time = current time - 1 (i.e., expired) // (We can hear them, but they can't hear us.) if (_sym_timer.scheduled()) _sym_timer.clear(); } else if (lc.is_sym_link() || lc.is_asym_link()) { // 2.2.2 L_SYM_time = current time + validity time if (_sym_timer.scheduled()) _sym_timer.clear(); _sym_timer = _eventloop.new_oneoff_after(vtime, callback(this, &LogicalLink::event_sym_timer)); // 7.1.1, 2.2.2: L_time = L_SYM_time + NEIGHB_HOLD_TIME dead_time = _sym_timer.expiry() + _nh->get_neighbor_hold_time(); } } // 2.3 L_time = max(L_time,L_ASYM_time) // A link losing its symmetry should still be advertised for // at least 'vtime', even if the link has been lost, to allow // neighbors to detect link breakage. dead_time = max(_dead_timer.expiry(), _asym_timer.expiry()); if (_dead_timer.scheduled()) _dead_timer.clear(); _dead_timer = _eventloop.new_oneoff_at(dead_time, callback(this, &LogicalLink::event_dead_timer)); } OlsrTypes::LinkType LogicalLink::link_type() const { // Section 14.2, 1: If L_LOST_LINK_time is not expired, the // link is advertised with a link type of LOST_LINK. if (_lost_timer.scheduled()) return OlsrTypes::LOST_LINK; if (_sym_timer.scheduled()) // L_SYM_time not expired return OlsrTypes::SYM_LINK; else if (_asym_timer.scheduled()) // L_ASYM_time not expired return OlsrTypes::ASYM_LINK; return OlsrTypes::LOST_LINK; // Both timers expired } void LogicalLink::event_sym_timer() { _nh->event_link_sym_timer(id()); } void LogicalLink::event_asym_timer() { _nh->event_link_asym_timer(id()); } void LogicalLink::event_lost_timer() { _nh->event_link_lost_timer(id()); } void LogicalLink::event_dead_timer() { _nh->event_link_dead_timer(id()); } xorp/contrib/olsr/message.cc0000664000076400007640000005277511613326565016262 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8 sw=4: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "olsr_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/exceptions.hh" #include "libxorp/ipv4.hh" #include "libxorp/ipv4net.hh" #include "libxorp/ref_ptr.hh" #include "libxorp/timeval.hh" #include "libproto/packet.hh" #include "olsr_types.hh" #include "exceptions.hh" #include "message.hh" // XXX: These defines don't conceptually belong here. // We need to know the header sizes for working out how much // space OLSR has for messages in UDP datagrams. #define IPV4_HEADER_SIZE 20 // without any options #define UDP_HEADER_SIZE 8 // sizeof(udphdr) size_t HelloMessage::remove_link(const IPv4& remote_addr) { size_t removed_count = 0; for (LinkBag::iterator ii = _links.begin(); ii != _links.end(); ) { LinkAddrInfo& lai = (*ii).second; if (remote_addr == lai.remote_addr()) { _links.erase(ii++); removed_count++; } else { ii++; } } return removed_count; } size_t HelloMessage::get_links_length() const { if (_links.empty()) { return (0); } size_t byte_count = 0; LinkCode thislc; LinkBag::const_iterator ii; for (ii = _links.begin(); ii != _links.end(); ii++) { const LinkAddrInfo& lai = (*ii).second; if (ii == _links.begin() || (*ii).first != thislc) { thislc = (*ii).first; if (_links.count(thislc) == 0) continue; byte_count += link_tuple_header_length(); } byte_count += lai.size(); } return byte_count; } string HelloMessage::str() const { string str = this->common_str() + "HELLO "; str += "htime " + get_htime().str() + " "; str += "will " + c_format("%u", XORP_UINT_CAST(willingness())); LinkCode thislc; LinkBag::const_iterator ii; for (ii = _links.begin(); ii != _links.end(); ii++) { if (ii == _links.begin() || (*ii).first != thislc) { thislc = (*ii).first; if (_links.count(thislc) == 0) continue; str += " "; str += thislc.str(); } else { str += ","; } str += " "; str += (*ii).second.str(); } return (str += "\n"); } size_t HelloMessage::decode_link_tuple(uint8_t* buf, size_t& len, size_t& skiplen, bool haslq) throw(InvalidLinkTuple) { size_t offset = 0; // minimum size check skiplen = len; if (len < link_tuple_header_length()) { xorp_throw(InvalidLinkTuple, c_format("Runt link tuple, buffer size is %u", XORP_UINT_CAST(len))); } uint8_t code = extract_8(&buf[offset]); // Link Code offset += sizeof(uint8_t); // Link Code offset += sizeof(uint8_t); // Reserved (skip) size_t link_tuple_len = extract_16(&buf[offset]); // Link Message Size offset += sizeof(uint16_t); // We will consume the entire tuple (if possible) skiplen = link_tuple_len; bool is_bad_link_code = false; LinkCode linkcode; try { linkcode = code; } catch(BadLinkCode blc) { debug_msg("caught bad link code exception\n"); is_bad_link_code = true; } // Invalid link tuples must be discarded silently as per RFC. // Tell the caller to skip only the rest of the buffer, if the // size reported on the wire would overflow the buffer. if (is_bad_link_code || link_tuple_len > len) { if (link_tuple_len > len) skiplen = len; xorp_throw(InvalidLinkTuple, c_format("Invalid link tuple, advertised size is %u, " "buffer size is %u", XORP_UINT_CAST(link_tuple_len), XORP_UINT_CAST(len))); } size_t remaining = link_tuple_len - offset; while (remaining > 0) { LinkAddrInfo lai(haslq); if (remaining < lai.size()) break; size_t copied_in = lai.copy_in(&buf[offset]); offset += copied_in; remaining -= copied_in; add_link(linkcode, lai); // XXX no check for dupes } if (offset != link_tuple_len) { XLOG_WARNING("Link tuple has %u unparsed bytes", XORP_UINT_CAST(len - offset)); } skiplen = offset; return offset; } size_t LinkAddrInfo::copy_in(const uint8_t *from_uint8) { size_t offset = 0; const uint8_t* buf = from_uint8; offset += _remote_addr.copy_in(&buf[offset]); // Produce an encoding of the ETX measurement which is // compatible with olsrd. if (has_etx()) { _near_etx = extract_8(&buf[offset]); _near_etx /= 255; offset += sizeof(uint8_t); _far_etx = extract_8(&buf[offset]); _far_etx /= 255; offset += sizeof(uint8_t); } return offset; } size_t LinkAddrInfo::copy_out(uint8_t* to_uint8) const { size_t offset = 0; uint8_t* buf = to_uint8; offset += _remote_addr.copy_out(&buf[offset]); if (has_etx()) { embed_8(&buf[offset], static_cast(_near_etx * 255)); offset += sizeof(uint8_t); embed_8(&buf[offset], static_cast(_far_etx * 255)); offset += sizeof(uint8_t); } return offset; } Message* HelloMessage::decode(uint8_t* buf, size_t& len) throw(InvalidMessage) { if (len < min_length()) { xorp_throw(InvalidMessage, c_format("Runt HelloMessage, size is %u", XORP_UINT_CAST(len))); } HelloMessage* message = new HelloMessage(); size_t offset = message->decode_common_header(buf, len); offset += sizeof(uint16_t); // reserved (skip) message->set_htime(EightBitTime::to_timeval( extract_8(&buf[offset]))); offset += sizeof(uint8_t); // htime message->set_willingness(static_cast( extract_8(&buf[offset]))); offset += sizeof(uint8_t); // willingness size_t remaining = message->adv_message_length() - offset; while (remaining > 0) { size_t skiplen; try { message->decode_link_tuple(&buf[offset], remaining, skiplen); } catch(InvalidLinkTuple ilt) { debug_msg("%s\n", cstring(ilt)); XLOG_WARNING("Invalid link info tuple at offset %u", XORP_UINT_CAST(offset)); } offset += skiplen; remaining -= skiplen; } return (message); } bool HelloMessage::encode(uint8_t* buf, size_t& len) { if (len < length()) return false; if (!encode_common_header(buf, len)) return false; size_t offset = get_common_header_length(); embed_16(&buf[offset], 0); // reserved offset += sizeof(uint16_t); embed_8(&buf[offset], EightBitTime::from_timeval(get_htime())); offset += sizeof(uint8_t); embed_8(&buf[offset], willingness()); // willingness offset += sizeof(uint8_t); // link tuples LinkCode thislc; for (LinkBag::iterator ii = _links.begin(); ii != _links.end(); ii++) { LinkAddrInfo& lai = (*ii).second; // Is this a new link state tuple? if (ii == _links.begin() || (*ii).first != thislc) { thislc = (*ii).first; // The link code of the new tuple #ifdef DETAILED_DEBUG bool haslq = lai.has_etx(); // Invariant: ETX measurements MUST be present in // an LQ HELLO message, and MUST NOT be present in a // legacy HELLO message. if (0 != dynamic_cast(this)) { XLOG_ASSERT(haslq == true); } else { XLOG_ASSERT(haslq == false); } #endif // Don't put a link state tuple in the HELLO message if // there are no neighbors for this link code. size_t linksize = _links.count(thislc) * lai.size(); if (linksize == 0) continue; linksize += link_tuple_header_length(); // Prepend the link state tuple header. embed_8(&buf[offset], static_cast(thislc)); offset += sizeof(uint8_t); embed_8(&buf[offset], 0); // reserved offset += sizeof(uint8_t); embed_16(&buf[offset], linksize); // link message size offset += sizeof(uint16_t); } // Copy out this neighbor entry. offset += lai.copy_out(&buf[offset]); } return true; } Message* HnaMessage::decode(uint8_t* buf, size_t& len) throw(InvalidMessage) { HnaMessage* message = new HnaMessage(); size_t offset = message->decode_common_header(buf, len); size_t remaining = message->adv_message_length() - offset; while (remaining > 0) { if (remaining < (IPv4::addr_bytelen() * 2)) break; IPv4 addr(&buf[offset]); offset += addr.addr_bytelen(); remaining -= addr.addr_bytelen(); IPv4 mask(&buf[offset]); offset += mask.addr_bytelen(); remaining -= mask.addr_bytelen(); uint32_t prefix_len = mask.mask_len(); addr.mask_by_prefix_len(prefix_len); message->add_network(IPv4Net(addr, prefix_len)); } if (message->networks().empty()) { xorp_throw(InvalidMessage, c_format("Runt HnaMessage, size is %u", XORP_UINT_CAST(len))); } return (message); } bool HnaMessage::encode(uint8_t* buf, size_t& len) { if (len < length()) return false; if (!encode_common_header(buf, len)) return false; size_t offset = get_common_header_length(); vector::const_iterator ii; for (ii = _networks.begin(); ii != _networks.end(); ii++) { offset += (*ii).masked_addr().copy_out(&buf[offset]); offset += (*ii).netmask().copy_out(&buf[offset]); } return true; } string Message::common_str() const { string s = c_format( "msg: type %d vtime %s size %u origin %s ttl %u hops %u seq %u\n", XORP_UINT_CAST(type()), cstring(expiry_time()), XORP_UINT_CAST(length()), cstring(origin()), XORP_UINT_CAST(ttl()), XORP_UINT_CAST(hops()), XORP_UINT_CAST(seqno())); return (s); } size_t Message::decode_common_header(uint8_t* ptr, size_t& len) throw(InvalidMessage) { if (len < Message::get_common_header_length()) { xorp_throw(InvalidPacket, c_format("Message too short %u, must be at least %u", XORP_UINT_CAST(len), XORP_UINT_CAST(Message::get_common_header_length()))); } _adv_message_length = extract_16(&ptr[2]); if (_adv_message_length > len) { xorp_throw(InvalidMessage, c_format("Message too short %u, advertised size is %u", XORP_UINT_CAST(len), XORP_UINT_CAST(_adv_message_length))); } set_type(ptr[0]); _expiry_time = EightBitTime::to_timeval(ptr[1]); _msg.resize(extract_16(&ptr[2])); // XXX address family; we should use a pointer of our own for // dealing with IPv6 when refactoring. _origin.copy_in(&ptr[4]); // Do not perform the ttl check here. We do that elsewhere, i.e. // when sorting the messages into an input queue. set_ttl(ptr[8]); set_hop_count(ptr[9]); set_seqno(extract_16(&ptr[10])); // 3.4, 2: Drop messages with invalid TTL. if (ttl() <= 0) { xorp_throw(InvalidMessage, c_format("Invalid message TTL %u.", XORP_UINT_CAST(ttl()))); } // Store the entire message in the Message parent class _msg field. store(ptr, _adv_message_length); set_valid(true); // XXX should we do that here? return (Message::get_common_header_length()); } bool Message::encode_common_header(uint8_t* ptr, size_t& len) { if (len < Message::get_common_header_length()) { debug_msg("insufficient space for common message header"); return false; } embed_8(&ptr[0], type()); // message type uint8_t vtime = EightBitTime::from_timeval(expiry_time()); embed_8(&ptr[1], vtime); // valid/expiry time embed_16(&ptr[2], length()); // message length origin().copy_out(&ptr[4]); embed_8(&ptr[8], ttl()); // ttl embed_8(&ptr[9], hops()); // hop count embed_16(&ptr[10], seqno()); // message sequence number return true; } MessageDecoder::~MessageDecoder() { map::iterator i; for (i = _olsrv1.begin(); i != _olsrv1.end(); i++) delete i->second; } Message* MessageDecoder::decode(uint8_t* ptr, size_t len) throw(InvalidMessage) { // Message knows what the minimum size is -- but only for its // address family. if (len < Message::get_common_header_length()) { xorp_throw(InvalidMessage, c_format("Message too short %u, must be at least %u", XORP_UINT_CAST(len), XORP_UINT_CAST(Message::get_common_header_length()))); } // OLSRv1 type code offset; unchanged by address family. uint8_t type = ptr[0]; Message* decoder; map::iterator ii = _olsrv1.find(type); if (ii == _olsrv1.end()) { decoder = &_olsrv1_unknown; } else { decoder = (*ii).second; } return (decoder->decode(ptr, len)); } void MessageDecoder::register_decoder(Message* message) { XLOG_ASSERT(_olsrv1.find(message->type()) == _olsrv1.end()); XLOG_ASSERT(0 != message->type()); _olsrv1[message->type()] = message; } Message* MidMessage::decode(uint8_t* buf, size_t& len) throw(InvalidMessage) { MidMessage* message = new MidMessage(); size_t offset = message->decode_common_header(buf, len); size_t remaining = message->adv_message_length() - offset; while (remaining > 0) { if (remaining < IPv4::addr_bytelen()) break; message->add_interface(IPv4(&buf[offset])); offset += IPv4::addr_bytelen(); remaining -= IPv4::addr_bytelen(); } if (message->interfaces().empty()) { xorp_throw(InvalidMessage, c_format("Runt MidMessage, size is %u", XORP_UINT_CAST(len))); } return (message); } bool MidMessage::encode(uint8_t* buf, size_t& len) { if (len < length()) return false; if (!encode_common_header(buf, len)) return false; size_t offset = get_common_header_length(); vector::const_iterator ii; for (ii = _interfaces.begin(); ii != _interfaces.end(); ii++) offset += (*ii).copy_out(&buf[offset]); return true; } void Packet::decode(uint8_t* ptr, size_t len) throw(InvalidPacket) { size_t offset = decode_packet_header(ptr, len); size_t remaining = len - offset; size_t index = 0; while (remaining > 0) { Message* message; try { message = _message_decoder.decode(&ptr[offset], len - offset); message->set_is_first(0 == index++); message->set_faceid(faceid()); offset += message->length(); remaining -= message->length(); add_message(message); } catch (InvalidMessage& e) { debug_msg("%s\n", cstring(e)); break; } } // 3.4, 1: Packet Processing: A node must discard a message with // no messages. if (_messages.empty()) { xorp_throw(InvalidPacket, c_format("Packet contains no messages.")); } // Mark the last message in the packet for use with future S-OLSR // decoders as they need to track this information. (*_messages.rbegin())->set_is_last(true); if (remaining > 0) { debug_msg("Packet has %d bytes remaining\n", XORP_INT_CAST(remaining)); } } size_t Packet::decode_packet_header(uint8_t* ptr, size_t len) throw(InvalidPacket) { // 3.4, 1: Packet Processing: A node must discard a message with // no messages. if (len <= Packet::get_packet_header_length()) { xorp_throw(InvalidPacket, c_format("Packet too short %u, must be > %u", XORP_UINT_CAST(len), XORP_UINT_CAST(Packet::get_packet_header_length()))); } size_t packet_length = extract_16(&ptr[0]); if (len < packet_length) { xorp_throw(InvalidPacket, c_format("Packet too short %u, advertised size is %u", XORP_UINT_CAST(len), XORP_UINT_CAST(packet_length))); } store(ptr, packet_length); set_seqno(extract_16(&ptr[2])); return (get_packet_header_length()); } // without mtu size_t Packet::length() const { size_t len = get_packet_header_length(); vector::const_iterator ii; for (ii = _messages.begin(); ii != _messages.end(); ii++) len += (*ii)->length(); return len; } size_t Packet::mtu_bound() const { return mtu() - IPV4_HEADER_SIZE - UDP_HEADER_SIZE; }; // bound by mtu size_t Packet::bounded_length() const { if (mtu() == 0) return length(); // maximum OLSR packet length size_t free_bytes = mtu_bound(); size_t len = get_packet_header_length(); //debug_msg("mtu %u free_bytes %u\n", // XORP_UINT_CAST(mtu()), // XORP_UINT_CAST(free_bytes)); vector::const_iterator ii; for (ii = _messages.begin(); ii != _messages.end(); ii++) { //debug_msg("about to take that little step\n"); size_t msglen = (*ii)->length(); // Check if appending this message would overflow MTU. if (len + msglen > free_bytes) break; len += msglen; } return len; } bool Packet::encode(vector& pkt) { size_t bounded_len = bounded_length(); pkt.resize(bounded_len); uint8_t *ptr = &pkt[0]; memset(ptr, 0, bounded_len); embed_16(&ptr[0], bounded_len); embed_16(&ptr[2], seqno()); size_t offset = get_packet_header_length(); vector::const_iterator ii; for (ii = _messages.begin(); ii != _messages.end(); ii++) { size_t msglen = (*ii)->length(); // Check if appending this message would overflow MTU. if (offset + msglen > bounded_len) { debug_msg("mtu overflowed: %u + %u > %u", XORP_UINT_CAST(offset), XORP_UINT_CAST(msglen), XORP_UINT_CAST(bounded_len)); return false; } // Try to encode this message. if (false == (*ii)->encode(&pkt[offset], msglen)) return false; offset += msglen; } return true; } void Packet::update_encoded_seqno(vector& pkt) { embed_16(&pkt[2], seqno()); } string Packet::str() const { string s = c_format("OLSRv1: len %u seq %u\n", XORP_UINT_CAST(length()), XORP_UINT_CAST(seqno())); vector::const_iterator ii; for (ii = _messages.begin(); ii != _messages.end(); ii++) s += (*ii)->str(); return (s += '\n'); } void TcMessage::decode_tc_common(uint8_t* buf, size_t& len, bool haslq) throw(InvalidMessage) { size_t offset = decode_common_header(buf, len); size_t remaining = adv_message_length() - min_length(); set_ansn(extract_16(&buf[offset])); offset += sizeof(uint16_t); // ANSN offset += sizeof(uint16_t); // Reserved while (remaining > 0) { LinkAddrInfo lai(haslq); if (remaining < lai.size()) break; size_t copied_in = lai.copy_in(&buf[offset]); offset += copied_in; remaining -= copied_in; add_neighbor(lai); } } Message* TcMessage::decode(uint8_t* buf, size_t& len) throw(InvalidMessage) { if (len < min_length()) { xorp_throw(InvalidMessage, c_format("Runt TcMessage, size is %u", XORP_UINT_CAST(len))); } TcMessage* message = new TcMessage(); try { message->decode_tc_common(buf, len); } catch (...) { throw; } return (message); } Message* EtxTcMessage::decode(uint8_t* buf, size_t& len) throw(InvalidMessage) { if (len < min_length()) { xorp_throw(InvalidMessage, c_format("Runt EtxTcMessage, size is %u", XORP_UINT_CAST(len))); } EtxTcMessage* message = new EtxTcMessage(); try { message->decode_tc_common(buf, len, true); } catch (...) { throw; } return (message); } bool TcMessage::encode(uint8_t* buf, size_t& len) { if (len < length()) return false; if (!encode_common_header(buf, len)) return false; size_t offset = get_common_header_length(); embed_16(&buf[offset], ansn()); offset += sizeof(uint16_t); embed_16(&buf[offset], 0); offset += sizeof(uint16_t); vector::const_iterator ii; for (ii = _neighbors.begin(); ii != _neighbors.end(); ii++) { const LinkAddrInfo& lai = *ii; #ifdef DETAILED_DEBUG // Invariant: ETX information MUST be present in // an LQ TC message, and MUST NOT be present in a legacy TC message. if (0 != dynamic_cast(this)) { XLOG_ASSERT(lai.has_etx() == true); } else { XLOG_ASSERT(lai.has_etx() == false); } #endif offset += lai.copy_out(&buf[offset]); } return true; } Message* UnknownMessage::decode(uint8_t* ptr, size_t& len) throw(InvalidMessage) { UnknownMessage* message = new UnknownMessage(); size_t offset = message->decode_common_header(ptr, len); UNUSED(offset); return (message); } bool UnknownMessage::encode(uint8_t* ptr, size_t& len) { store(ptr, len); return true; } string MidMessage::str() const { string str = this->common_str() + "MID "; if (_interfaces.empty()) { str = ""; } else { vector::const_iterator ii; for (ii = _interfaces.begin(); ii != _interfaces.end(); ii++) str += ii->str() + " "; } return (str += "\n"); } #define NAME_CASE(x) case OlsrTypes:: x : return x##_NAME ; #define NUM_CASE(x) case x : return #x ; static const char* UNSPEC_LINK_NAME = "unspec"; static const char* ASYM_LINK_NAME = "asym"; static const char* SYM_LINK_NAME = "sym"; static const char* LOST_LINK_NAME = "lost"; const char* LinkCode::linktype_to_str(OlsrTypes::LinkType t) { switch (t) { NAME_CASE(UNSPEC_LINK); NAME_CASE(ASYM_LINK); NAME_CASE(SYM_LINK); NAME_CASE(LOST_LINK); } XLOG_UNREACHABLE(); return 0; } static const char* NOT_NEIGH_NAME = "not"; static const char* SYM_NEIGH_NAME = "sym"; static const char* MPR_NEIGH_NAME = "mpr"; const char* LinkCode::neighbortype_to_str(OlsrTypes::NeighborType t) { switch (t) { NAME_CASE(NOT_NEIGH); NAME_CASE(SYM_NEIGH); NAME_CASE(MPR_NEIGH); } XLOG_UNREACHABLE(); return 0; } #undef NUM_CASE #undef NAME_CASE xorp/contrib/olsr/twohop.hh0000664000076400007640000002217511540225522016145 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8 sw=4: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/contrib/olsr/twohop.hh,v 1.3 2008/10/02 21:56:36 bms Exp $ #ifndef __OLSR_TWOHOP_HH__ #define __OLSR_TWOHOP_HH__ class Neighbor; class Neighborhood; // From now on, N1 is shorthand for "one-hop neighbor", and // N2 is shorthand for "two-hop neighbor". /** * @short A two-hop neighbor. * * When the last link expires, the entire instance expires. */ class TwoHopNeighbor { public: TwoHopNeighbor(EventLoop& ev, Neighborhood* parent, const OlsrTypes::TwoHopNodeID id, const IPv4& main_addr, const OlsrTypes::TwoHopLinkID tlid); inline OlsrTypes::TwoHopNodeID id() const { return _id; } inline IPv4 main_addr() const { return _main_addr; } string toStringBrief(); /** * Associate this N2 with an N1, using an instance of * the association class TwoHopLink. * * @param tlid the ID of a two-hop link. */ void add_twohop_link(const OlsrTypes::TwoHopLinkID tlid); /** * Delete the given N1 from the set of Neighbors which may * be used to reach this N2. * * @param tlid the ID of a two-hop link. * @return true if the last link to the TwoHopNeighbor was removed. */ bool delete_twohop_link(const OlsrTypes::TwoHopLinkID tlid); size_t delete_all_twohop_links(); inline const set& twohop_links() const { return _twohop_links; } /** * Add the given neighbor as an MPR which covers this node. * * @param nid the N to count as one of this N2's covering nodes. */ void add_covering_mpr(const OlsrTypes::NeighborID nid); /** * Withdraw the given neighbor as an MPR which covers this node. * * @param nid the N to count as one of this N2's covering nodes. */ void withdraw_covering_mpr(const OlsrTypes::NeighborID nid); /** * Reset the MPR coverage state for this N2 node. */ void reset_covering_mprs(); /** * @return The number of MPRs which cover this node. */ inline uint32_t coverage() const { return _coverage; } /** * @return true if this N2 is covered by at least N1. */ inline bool is_covered() const { return _coverage > 0; } /** * @return The number of MPR candidates which cover this node. * This is not computed here but by Neighborhood, the parent. * Note: This is NOT the same as a neighbor's reachability. */ inline uint32_t reachability() const { return _reachability; } /** * Set this N2's reachability. */ inline void set_reachability(uint32_t value) { _reachability = value; } /** * @return true if this N2 is reachable. */ inline bool is_reachable() const { return _reachability > 0; } /** * @return true if this N2 is strict. * * An N2 is strict if it appears only in the * two-hop neighborhood, and not in the one-hop neighborhood. */ bool is_strict() const { return _is_strict; } /** * Set if this N2 is a strict two-hop neighbor. * * @param is_strict true if this N2 does NOT also appear as an N1. */ void set_is_strict(bool is_strict) { _is_strict = is_strict; } /** * @return the set of two-hop links pointing to this N2 node. */ const set& twohop_links() { return _twohop_links; } private: EventLoop& _ev; Neighborhood* _parent; /** * Unique ID of this N2 node. */ OlsrTypes::TwoHopNodeID _id; /** * N_2hop_addr protocol variable. * Main protocol address of this N2 node. */ IPv4 _main_addr; /** * true if this N2 is NOT also an N1 node. */ bool _is_strict; /** * The number of strict N1 which can * reach this strict N2, and which have been * selected as MPRs by the Neighborhood. */ uint32_t _coverage; /** * The number of strict N1 which can reach this strict N2. */ uint32_t _reachability; /** * Links by which this two-hop neighbor is reachable. */ set _twohop_links; }; /** * @short A link between a Neighbor and a TwoHopNeighbor. * * Association class between TwoHopNeighbor and Neighbor. * TwoHopNeighbor cannot exist without TwoHopLink. Like Link and Neighbor, * there is a co-dependent relationship. Both TwoHopNeighbor and TwoHopLink * cannot exist without Neighbor. * * TwoHopLinks are uniquely identified by the ID of the two-hop neighbor * at the far end, and the Neighbor at the near end. When the last * TwoHopLink associated with a TwoHopNeighbor is deleted, the * TwoHopNeighbor MUST be deleted, to conform to the relations in the RFC. */ class TwoHopLink { public: TwoHopLink(EventLoop& ev, Neighborhood* parent, OlsrTypes::TwoHopLinkID tlid, Neighbor* nexthop, const TimeVal& vtime); /** * @return A unique identifier for this link in the two-hop * neighborhood. */ inline OlsrTypes::TwoHopLinkID id() const { return _id; } /** * @return A pointer to the strict one-hop neighbor at the * near end of this link. */ inline Neighbor* nexthop() const { return _nexthop; } /** * @return A pointer to the strict two-hop neighbor at the * far end of this link. */ inline TwoHopNeighbor* destination() const { XLOG_ASSERT(0 != _destination); // Catch programming errors. return _destination; } /** * Set the cached destination pointer. * * @param destination the TwoHopNeighbor at the end of this link. */ inline void set_destination(TwoHopNeighbor* destination) { // Invariant: set_destination() should only be called once, // immediately after the TwoHopLink has been created, to // associate it with a TwoHopNeighbor. XLOG_ASSERT(0 == _destination); _destination = destination; } /** * @return The ID of the interface where the advertisement of this * TwoHopLink was last heard by a Neighbor. */ inline OlsrTypes::FaceID face_id() const { return _face_id; } /** * @return ETX measurement at near end of link. */ inline double near_etx() const { return _near_etx; } /** * @return ETX measurement at far end of link. */ inline double far_etx() const { return _far_etx; } /** * Set the ID of the interface where this link was last heard. * * @param faceid the ID of the interface. */ inline void set_face_id(const OlsrTypes::FaceID faceid) { _face_id = faceid; } /** * Set the ETX measurement at near end of link. * * @param near_etx near end ETX measurement. */ inline void set_near_etx(const double& near_etx) { _near_etx = near_etx; } /** * Set the ETX measurement at far end of link. * * @param far_etx far end ETX measurement. */ inline void set_far_etx(const double& far_etx) { _far_etx = far_etx; } /** * @return the amount of time remaining until this two-hop link expires. */ inline TimeVal time_remaining() const { TimeVal tv; _expiry_timer.time_remaining(tv); return tv; } /** * Update this link's expiry timer. * * @param vtime the amount of time from now til when the link expires. */ void update_timer(const TimeVal& vtime); /** * Callback method to: tell the parent that this two-hop link * has now expired. */ void event_dead(); private: EventLoop& _ev; Neighborhood* _parent; /** * A unique identifier for this object. */ OlsrTypes::TwoHopLinkID _id; /** * The strict one-hop neighbor used to reach the two-hop neighbor * referenced by _far_id. */ Neighbor* _nexthop; /** * The two-hop neighbor at the end of this link. */ TwoHopNeighbor* _destination; /** * The ID of the interface where the advertisement of this link * was last heard. */ OlsrTypes::FaceID _face_id; /** * The time at which this two-hop link expires. */ XorpTimer _expiry_timer; /** * The ETX as measured at the near end of this link. Optional. */ double _near_etx; /** * The ETX as measured at the far end of this link. Optional. */ double _far_etx; }; #endif // __OLSR_TWOHOP_HH__ xorp/contrib/olsr/message.hh0000664000076400007640000005334011703345405016253 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8 sw=4: // Copyright (c) 2001-2012 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/contrib/olsr/message.hh,v 1.4 2008/10/02 21:56:34 bms Exp $ #ifndef __OLSR_MESSAGE_HH__ #define __OLSR_MESSAGE_HH__ /** * @short An OLSR protocol message. */ class Message { public: virtual ~Message() {} inline TimeVal receive_time() const { return _receive_time; } inline TimeVal expiry_time() const { return _expiry_time; } inline bool valid() const { return _is_valid; } inline bool is_first() const { return _is_first; } inline bool is_last() const { return _is_last; } inline void set_is_first(bool arg) { _is_first = arg; } inline void set_is_last(bool arg) { _is_last = arg; } inline bool forwarded() const { return _is_forwarded; } inline OlsrTypes::FaceID faceid() const { return _faceid; } inline uint8_t hops() const { return _hops; } inline void incr_hops() { ++_hops; } inline uint16_t seqno() const { return _seqno; } inline uint8_t ttl() const { return _ttl; } inline void decr_ttl() { --_ttl; } inline IPv4 origin() const { return _origin; } inline OlsrTypes::MessageType type() const { return _type; } inline void set_hop_count(uint8_t hops) { _hops = hops; } inline void set_forwarded(bool is_forwarded) { _is_forwarded = is_forwarded; } inline void set_expiry_time(const TimeVal& expiry_time) { _expiry_time = expiry_time; } inline void set_receive_time(const TimeVal& receive_time) { _receive_time = receive_time; } inline void set_seqno(uint16_t seqno) { _seqno = seqno; } inline void set_ttl(uint8_t ttl) { _ttl = ttl; } inline void set_type(OlsrTypes::MessageType type) { _type = type; } inline void set_valid(bool is_valid) { _is_valid = is_valid; } inline void set_origin(IPv4 origin) { _origin = origin; } inline void set_faceid(OlsrTypes::FaceID faceid) { _faceid = faceid; } virtual Message* decode(uint8_t* buf, size_t& len) throw(InvalidMessage) = 0; virtual bool encode(uint8_t* buf, size_t& len) = 0; virtual size_t length() const = 0; virtual string str() const = 0; string common_str() const; /* * @return the length of the header common to all types of OLSR * protocol message. * As this varies according to the protocol family in use, it needs * to become templatized. */ static size_t get_common_header_length() { return sizeof(uint8_t) + // message type sizeof(uint8_t) + // validity time sizeof(uint16_t) + // message size (if not RA-OLSR) IPv4::addr_bytelen() + // IPv4 origin: family dependent sizeof(uint8_t) + // time-to-live sizeof(uint8_t) + // hop count sizeof(uint16_t); // message sequence number } uint16_t adv_message_length() const { return _adv_message_length; } protected: size_t decode_common_header(uint8_t* buf, size_t& len) throw(InvalidMessage); bool encode_common_header(uint8_t* buf, size_t& len); void store(uint8_t* ptr, size_t len) { _msg.resize(len); memcpy(&_msg[0], ptr, len); } protected: // common message data and fields TimeVal _receive_time; // time when this message was received TimeVal _expiry_time; // this message will self destruct in // C*(1+a/16)* 2^b seconds! bool _is_valid; // this message is valid bool _is_forwarded; // this message has already been forwarded bool _is_first; // this message is the first in the packet bool _is_last; // this message is the last in the packet OlsrTypes::FaceID _faceid; // interface where this was received IPv4 _origin; // who sent it uint8_t _type; // type of message uint8_t _ttl; // time-to-live; updated when forwarded uint8_t _hops; // hop count; updated when forwarded uint16_t _seqno; // message sequence number uint16_t _adv_message_length; // length field seen on wire; used by // derived class decoders. vector _msg; // on-wire format }; /** * @short Helper class which represents the link code used in a link tuple. */ class LinkCode { private: static const char* linktype_to_str(OlsrTypes::LinkType t); static const char* neighbortype_to_str(OlsrTypes::NeighborType t); public: LinkCode() : _linkcode(OlsrTypes::UNSPEC_LINK) { } LinkCode(OlsrTypes::NeighborType ntype, OlsrTypes::LinkType ltype) throw(BadLinkCode) { _linkcode = ((ntype << 2) & 0x0C) | (ltype & 0x03); throw_if_not_valid(); } LinkCode(uint8_t code) throw(BadLinkCode) : _linkcode(code) { throw_if_not_valid(); } LinkCode(const LinkCode& rhs) : _linkcode(rhs._linkcode) {} inline LinkCode& operator=(const uint8_t& rhs) throw(BadLinkCode) { _linkcode = rhs; throw_if_not_valid(); return (*this); } inline operator uint8_t() const { return _linkcode; } inline OlsrTypes::NeighborType neighbortype() const { return (_linkcode & 0x0C) >> 2; } inline OlsrTypes::LinkType linktype() const { return _linkcode & 0x03; } inline bool is_unspec_link() const { return linktype() == OlsrTypes::UNSPEC_LINK; } inline bool is_asym_link() const { return linktype() == OlsrTypes::ASYM_LINK; } inline bool is_sym_link() const { return linktype() == OlsrTypes::SYM_LINK; } inline bool is_lost_link() const { return linktype() == OlsrTypes::LOST_LINK; } inline bool is_mpr_neighbor() const { return neighbortype() == OlsrTypes::MPR_NEIGH; } inline bool is_sym_neighbor() const { return neighbortype() == OlsrTypes::SYM_NEIGH; } inline bool is_not_neighbor() const { return neighbortype() == OlsrTypes::NOT_NEIGH; } inline string str() const { return c_format("link %s neighbor %s", linktype_to_str(linktype()), neighbortype_to_str(neighbortype())); } private: inline void throw_if_not_valid() { if (!is_valid()) { xorp_throw(BadLinkCode, c_format("Bad link code: neighbor %u link %u", XORP_UINT_CAST(neighbortype()), XORP_UINT_CAST(linktype()))); } } inline bool is_valid() { if (linktype() > OlsrTypes::LINKTYPE_END || neighbortype() > OlsrTypes::NEIGHBORTYPE_END || (linktype() == OlsrTypes::SYM_LINK && neighbortype() == OlsrTypes::NOT_NEIGH)) { return false; } return true; } private: uint8_t _linkcode; }; /** * @short Wrapper for per-address information found in HELLO and TC. * * This is space-pessimized for the case where ETX measurements are in use. */ class LinkAddrInfo { public: explicit LinkAddrInfo(const bool has_lq) : _has_etx(has_lq) {} explicit LinkAddrInfo(const IPv4& addr) : _has_etx(false), _remote_addr(addr) {} explicit LinkAddrInfo(const IPv4& addr, const double& near_etx, const double& far_etx) : _has_etx(true), _remote_addr(addr), _near_etx(near_etx), _far_etx(far_etx) {} #ifdef XORP_USE_USTL LinkAddrInfo() { } #endif bool has_etx() const { return _has_etx; } IPv4 remote_addr() const { return _remote_addr; } double near_etx() const { return _near_etx; } double far_etx() const { return _far_etx; } inline size_t size() const { size_t byte_count = IPv4::addr_bytelen(); if (has_etx()) byte_count += (sizeof(uint8_t) * 2); return byte_count; } size_t copy_in(const uint8_t *from_uint8); size_t copy_out(uint8_t* to_uint8) const; inline string str() const { string str = _remote_addr.str(); if (has_etx()) { str += c_format("[nq %.2f, fq %.2f]", near_etx(), far_etx()); } return str; } private: bool _has_etx; IPv4 _remote_addr; double _near_etx; double _far_etx; }; /** * @short Representation of a HELLO protocol message. */ class HelloMessage : public Message { public: typedef multimap LinkBag; public: HelloMessage() { this->set_type(OlsrTypes::HELLO_MESSAGE); } ~HelloMessage() {} Message* decode(uint8_t* buf, size_t& len) throw(InvalidMessage); bool encode(uint8_t* buf, size_t& len); inline size_t min_length() const { return get_common_header_length() + sizeof(uint16_t) + // reserved sizeof(uint8_t) + // Htime sizeof(uint8_t); // Willingness } size_t length() const { size_t len = get_common_header_length() + sizeof(uint16_t) + // reserved sizeof(uint8_t) + // Htime sizeof(uint8_t) + // Willingness get_links_length(); return (len); } inline const TimeVal get_htime() const { return _htime; } inline void set_htime(const TimeVal& htime) { _htime = htime; } inline OlsrTypes::WillType willingness() const { return _willingness; } inline void set_willingness(OlsrTypes::WillType willingness) { _willingness = willingness; } inline void add_link(const LinkCode code, const IPv4& remote_addr) { add_link(code, LinkAddrInfo(remote_addr)); } /** * Remove a given neighbor interface address from ALL link tuples. */ size_t remove_link(const IPv4& remote_addr); inline void clear() { _htime = TimeVal::ZERO(); _willingness = OlsrTypes::WILL_DEFAULT; _links.clear(); } inline const LinkBag& links() const { return _links; } /** * Print a HelloMessage as a string. */ virtual string str() const; /** * Calculate the on-wire size of all link state tuples. */ virtual size_t get_links_length() const; protected: inline void add_link(const LinkCode code, const LinkAddrInfo& lai) { _links.insert(make_pair(code, lai)); } /** * Decode a single link tuple from the buffer into the HelloMessage. * * @param buf pointer to the buffer to decode. * @param len the number of bytes in the buffer. * @param skiplen the number of bytes consumed by this function. * @param has_lq true if this function is being called from derived * class EtxHelloMessage to process ETX information, * otherwise false. * @return the number of bytes consumed in the input stream to produce * a decoded link tuple. * @throw InvalidLinkTuple if an invalid link tuple was found * during message decoding. */ virtual size_t decode_link_tuple(uint8_t* buf, size_t& len, size_t& skiplen, bool has_lq = false) throw(InvalidLinkTuple); inline size_t link_tuple_header_length() const { return sizeof(uint8_t) + // link code sizeof(uint8_t) + // reserved sizeof(uint16_t); // link message size } TimeVal _htime; // hello interval OlsrTypes::WillType _willingness; // willingness-to-forward LinkBag _links; // link tuples }; /** * @short Specialization of a HELLO message with ETX measurements. */ class EtxHelloMessage : public HelloMessage { public: EtxHelloMessage() { this->set_type(OlsrTypes::LQ_HELLO_MESSAGE); } ~EtxHelloMessage() {} protected: size_t decode_link_tuple(uint8_t* buf, size_t& len, size_t& skiplen, bool has_lq = true) throw(InvalidLinkTuple) { // Overriding a virtual with default arguments means the signatures // have to match. We are invoked via a pointer. return HelloMessage::decode_link_tuple(buf, len, skiplen, has_lq); } inline void add_link(const LinkCode code, const IPv4& remote_addr, const double& near_etx, const double& far_etx) { HelloMessage::add_link(code, LinkAddrInfo(remote_addr, near_etx, far_etx)); } }; /** * @short Representation of a MID protocol message. */ class MidMessage : public Message { public: MidMessage() { this->set_type(OlsrTypes::MID_MESSAGE); } ~MidMessage() {} Message* decode(uint8_t* buf, size_t& len) throw(InvalidMessage); bool encode(uint8_t* buf, size_t& len); inline size_t length() const { return get_common_header_length() + (_interfaces.size() * IPv4::addr_bytelen()); } inline void add_interface(const IPv4& addr) { _interfaces.push_back(addr); } inline void clear() { _interfaces.clear(); } inline const vector& interfaces() const { return _interfaces; } string str() const; private: vector _interfaces; }; /** * @short Representation of a TC protocol message. */ class TcMessage : public Message { public: TcMessage() { this->set_type(OlsrTypes::TC_MESSAGE); } ~TcMessage() {} virtual Message* decode(uint8_t* buf, size_t& len) throw(InvalidMessage); bool encode(uint8_t* buf, size_t& len); inline size_t length() const { return get_common_header_length() + sizeof(uint16_t) + // ANSN sizeof(uint16_t) + // Reserved ( _neighbors.size() * sizeof(uint32_t)); // Neighbor } static inline size_t min_length() { return get_common_header_length() + sizeof(uint16_t) + // ANSN sizeof(uint16_t); // Reserved } inline uint16_t ansn() const { return _ansn; } inline const vector& neighbors() const { return _neighbors; } inline void add_neighbor(const IPv4& remote_addr) { add_neighbor(LinkAddrInfo(remote_addr)); } inline size_t remove_neighbor(const IPv4& remote_addr) { size_t removed_count = 0; vector::iterator ii = _neighbors.begin(); while (ii != _neighbors.end()) { if ((*ii).remote_addr() == remote_addr) { ii = _neighbors.erase(ii); ++removed_count; } else { ++ii; } } return removed_count; } inline void set_ansn(uint16_t ansn) { _ansn = ansn; } inline void clear() { _neighbors.clear(); _ansn = 0; } inline string str() const { string str = this->common_str(); str += c_format("TC ansn %u ", XORP_UINT_CAST(ansn())); if (!_neighbors.empty()) { vector::const_iterator ii; for (ii = _neighbors.begin(); ii != _neighbors.end(); ii++) str += (*ii).str() + " "; } return (str += '\n'); } protected: inline void add_neighbor(const LinkAddrInfo& lai) { _neighbors.push_back(lai); } void decode_tc_common(uint8_t* buf, size_t& len, bool has_lq = false) throw(InvalidMessage); private: uint16_t _ansn; // advertised neighbor sequence no. vector _neighbors; // advertised neighbor set. }; /** * @short Specialization of a TC message with ETX measurements. */ class EtxTcMessage : public TcMessage { public: EtxTcMessage() { this->set_type(OlsrTypes::LQ_TC_MESSAGE); } ~EtxTcMessage() {} inline void add_neighbor(const IPv4& remote_addr, const double& near_etx, const double& far_etx) { TcMessage::add_neighbor(LinkAddrInfo(remote_addr, near_etx, far_etx)); } Message* decode(uint8_t* buf, size_t& len) throw(InvalidMessage); }; /** * @short Representation of an HNA message, containing external routes. */ class HnaMessage : public Message { public: HnaMessage() { this->set_type(OlsrTypes::HNA_MESSAGE); } ~HnaMessage() {} Message* decode(uint8_t* buf, size_t& len) throw(InvalidMessage); bool encode(uint8_t* buf, size_t& len); inline size_t length() const { return get_common_header_length() + (_networks.size() * (sizeof(uint32_t) + // network address sizeof(uint32_t))); // network mask } inline const vector& networks() const { return _networks; } inline void add_network(const IPv4Net& network) { _networks.push_back(network); } inline size_t remove_network(const IPv4Net& network) { size_t removed_count = 0; vector::iterator ii = _networks.begin(); while (ii != _networks.end()) { if ((*ii) == network) { ii = _networks.erase(ii); ++removed_count; } else { ++ii; } } return removed_count; } inline void clear() { _networks.clear(); } inline string str() const { string str = this->common_str(); str += "HNA "; if (!_networks.empty()) { vector::const_iterator ii; for (ii = _networks.begin(); ii != _networks.end(); ii++) str += ii->str() + " "; } return (str += "\n"); } private: vector _networks; }; /** * @short Wrapper class for an unknown OLSR message type. * * Used for passing messages to entities which need to deal with them * in an opaque way, e.g. Secured OLSR. */ class UnknownMessage : public Message { public: Message* decode(uint8_t* buf, size_t& len) throw(InvalidMessage); bool encode(uint8_t* buf, size_t& len); inline size_t length() const { return _msg.size(); } inline vector opaque_data() { return this->_msg; } inline string str() const { string str = this->common_str() + "bytes "; vector::const_iterator ii; for (ii = _msg.begin(); ii != _msg.end(); ii++) str += c_format("0x%0x ", *ii); return (str += '\n'); } private: size_t _opaque_data_offset; }; /** * @short Decoder for OLSR protocol messages. * * Empty messages are registered with this class in a producer/consumer * pattern. When adding a new message class, make sure in its decode() * method that you are storing the message properties in the message * you've created -- not the fields of the empty message registered * with this class. */ class MessageDecoder { public: ~MessageDecoder(); Message* decode(uint8_t* ptr, size_t len) throw(InvalidMessage); void register_decoder(Message* message); private: map _olsrv1; UnknownMessage _olsrv1_unknown; }; /** * @short An OLSR packet containing Messages. * * Packets contain Messages. They are coalesced up up to the available * MTU size, to save power on devices where transmission may have a high * energy cost. */ class Packet { public: Packet(MessageDecoder& md, OlsrTypes::FaceID faceid = 0) : _message_decoder(md), _is_valid(false), _faceid(faceid), _seqno(0), _mtu(0) {} ~Packet() {} static size_t get_packet_header_length() { return sizeof(uint16_t) + // packet length sizeof(uint16_t); // packet sequence number } /** * @return the size of the packet payload. */ size_t length() const; /** * @return the amount of free space in this packet for an OLSR * packet payload, after MTU is taken into account. */ size_t mtu_bound() const; /** * @return the size of the packet payload which will fit inside * the MTU without splitting any messages. * If no MTU is set, returns length(). */ size_t bounded_length() const; void decode(uint8_t* ptr, size_t len) throw(InvalidPacket); /** * Decode an OLSR packet header. * * An OLSR packet is considered valid if its length is greater than * the size of a standard packet header. * The host IP stack takes care of UDP checksums, IP fragmentation and * reassembly, and MTU size checks for us. */ size_t decode_packet_header(uint8_t* ptr, size_t len) throw(InvalidPacket); /** * Encode a packet, including any nested messages. */ bool encode(vector& pkt); void update_encoded_seqno(vector& pkt); inline uint16_t seqno() const { return _seqno; } inline void set_seqno(uint16_t seqno) { _seqno = seqno; } inline OlsrTypes::FaceID faceid() const { return _faceid; } inline uint32_t mtu() const { return _mtu; } inline void set_mtu(const uint32_t mtu) { _mtu = mtu; } inline void set_faceid(OlsrTypes::FaceID faceid) { _faceid = faceid; } /** * @return a string representation of the entire packet. */ string str() const; inline void add_message(Message* m) { _messages.push_back(m); } inline void clear() { _seqno = 0; _mtu = 0; _messages.clear(); _is_valid = false; } inline const vector& messages() { return _messages; } /** * Get a non-const reference to a packet's messages. * For debugging use only, ie in simulation of multiple hops. * * @return reference to the messages contained within the packet. */ inline vector& get_messages() { return _messages; } bool valid() const { return _is_valid; } vector& get() { return _pkt; } void store(uint8_t* ptr, size_t len) { _pkt.resize(len); memcpy(&_pkt[0], ptr, len); } private: MessageDecoder& _message_decoder; bool _is_valid; OlsrTypes::FaceID _faceid; // interface where this was received uint16_t _seqno; uint32_t _mtu; // maximum transmission unit vector _messages; // messages this packet contains. vector _pkt; // on-wire packet data. }; inline void initialize_message_decoder(MessageDecoder& message_decoder) { // // NOTE: Do not register UnknownMessage explicitly -- it has no // type field to match. It is deliberately used from the message // parser so that we will forward opaque messages as per the OLSR RFC. // message_decoder.register_decoder(new HelloMessage()); message_decoder.register_decoder(new TcMessage()); message_decoder.register_decoder(new MidMessage()); message_decoder.register_decoder(new HnaMessage()); #ifdef notyet message_decoder.register_decoder(new EtxHelloMessage()); message_decoder.register_decoder(new EtxTcMessage()); #endif } #endif // __OLSR_MESSAGE_HH__ xorp/contrib/olsr/xrl_target.hh0000664000076400007640000006342011540225522016776 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8 sw=4: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/contrib/olsr/xrl_target.hh,v 1.3 2008/10/02 21:56:37 bms Exp $ #ifndef __OLSR_XRL_TARGET_HH__ #define __OLSR_XRL_TARGET_HH__ #include "xrl/targets/olsr4_base.hh" #include "olsr.hh" /** * @short Concrete implementation of OLSRv1/IPv4 XRL target. */ class XrlOlsr4Target : public XrlOlsr4TargetBase { public: /** * Constructor. */ XrlOlsr4Target(XrlRouter* r, Olsr& olsr, XrlIO& xrl_io); /** * Get name of Xrl Target */ XrlCmdError common_0_1_get_target_name( // Output values, string& name); /** * Get version string from Xrl Target */ XrlCmdError common_0_1_get_version( // Output values, string& version); /** * Get status of Xrl Target */ XrlCmdError common_0_1_get_status( // Output values, uint32_t& status, string& reason); /** * Request clean shutdown of Xrl Target */ XrlCmdError common_0_1_shutdown(); XrlCmdError common_0_1_startup() { return XrlCmdError::OKAY(); } /** * Announce target birth to observer. * * @param target_class the target class name. * @param target_instance the target instance name. */ XrlCmdError finder_event_observer_0_1_xrl_target_birth( // Input values, const string& target_class, const string& target_instance); /** * Announce target death to observer. * * @param target_class the target class name. * @param target_instance the target instance name. */ XrlCmdError finder_event_observer_0_1_xrl_target_death( // Input values, const string& target_class, const string& target_instance); /** * Method invoked by target implementing socket4/0.1 when a packet arrives * from an IPv4 source. * * @param sockid the identifier associated with socket where the event * occurred. * @param if_name the interface name the packet arrived on, if known. If * unknown, then it is an empty string. * @param vif_name the vif name the packet arrived on, if known. If * unknown, then it is an empty string. * @param src_host the originating host. * @param src_port the originating IP port. * @param data the data received. */ XrlCmdError socket4_user_0_1_recv_event( // Input values, const string& sockid, const string& if_name, const string& vif_name, const IPv4& src_host, const uint32_t& src_port, const vector& data); /** * Method invoked by target implementing socket4/0.1 when a connection * request is received from an IPv4 source. It applies only to TCP * sockets. * * @param sockid the identifier associated with socket where the event * occurred. * @param src_host the connecting host. * @param src_port the connecting IP port. * @param new_sockid the identifier associated with the new socket that * has been created to handle the new connection. * @param accept if true, the connection request has been accepted, * otherwise it has been rejected. */ XrlCmdError socket4_user_0_1_inbound_connect_event( // Input values, const string& sockid, const IPv4& src_host, const uint32_t& src_port, const string& new_sockid, // Output values, bool& accept); /** * Method invoked by target implementing socket4/0.1 when an outgoing * connection request originated by the local host is completed. It * applies only to TCP sockets. Note that if the connection failed, the * error_event will be dispatched instead. * * @param sockid the identifier associated with socket where the event * occurred. */ XrlCmdError socket4_user_0_1_outgoing_connect_event( // Input values, const string& sockid); /** * Method invoked by target implementing socket4/0.1 when an error occurs. * * @param sockid the identifier associated with socket where the event * occurred. * @param error a textual description of the error. * @param fatal indication of whether socket is shutdown because of error. */ XrlCmdError socket4_user_0_1_error_event( // Input values, const string& sockid, const string& error, const bool& fatal); /** * Method invoked by target implementing socket4/0.1 when the peer has * closed the connection. It applies only to TCP sockets. Note that the * socket itself is left open and must be explicitly closed. * * @param sockid the identifier associated with socket where the event * occurred. */ XrlCmdError socket4_user_0_1_disconnect_event( // Input values, const string& sockid); /** * Configure a policy filter. * * @param filter the identifier of the filter to configure. * @param conf the configuration of the filter. */ XrlCmdError policy_backend_0_1_configure( // Input values, const uint32_t& filter, const string& conf); /** * Reset a policy filter. * * @param filter the identifier of the filter to reset. */ XrlCmdError policy_backend_0_1_reset( // Input values, const uint32_t& filter); /** * Push all available routes through all filters for re-filtering. */ XrlCmdError policy_backend_0_1_push_routes(); /** * Start route redistribution for an IPv4 route. * * @param network the route to advertise. * @param unicast whether the route is unicast. * @param multicast whether the route is multicast. * @param nexthop the nexthop of the route. * @param metric the metric of the route. * @param policytags the set of policy-tags associated with the route. */ XrlCmdError policy_redist4_0_1_add_route4( // Input values, const IPv4Net& network, const bool& unicast, const bool& multicast, const IPv4& nexthop, const uint32_t& metric, const XrlAtomList& policytags); /** * Terminate route redistribution for an IPv4 route. * * @param network the route for which advertisements should cease. * @param unicast whether the route is unicast. * @param multicast whether the route is multicast. */ XrlCmdError policy_redist4_0_1_delete_route4( // Input values, const IPv4Net& network, const bool& unicast, const bool& multicast); /** * Enable profiling. * * @param pname profile variable */ XrlCmdError profile_0_1_enable( // Input values, const string& pname); /** * Disable profiling. * * @param pname profile variable */ XrlCmdError profile_0_1_disable( // Input values, const string& pname); /** * Get log entries. * * @param pname profile variable * @param instance_name to send the profiling info to. */ XrlCmdError profile_0_1_get_entries( // Input values, const string& pname, const string& instance_name); /** * Clear the profiling entries. * * @param pname profile variable */ XrlCmdError profile_0_1_clear( // Input values, const string& pname); /** * List all the profiling variables registered with this target. */ XrlCmdError profile_0_1_list( // Output values, string& info); /** * Enable/Disable tracing. * * @param tvar trace variable. * @param enable set to true to enable, false to disable. */ XrlCmdError olsr4_0_1_trace( // Input values, const string& tvar, const bool& enable); /** * Clear all OLSR protocol databases. */ XrlCmdError olsr4_0_1_clear_database(); /** * Set the willingness-to-forward. * * @param willingness the new willingness-to-forward. */ XrlCmdError olsr4_0_1_set_willingness( // Input values, const uint32_t& willingness); /** * Get the willingness-to-forward. */ XrlCmdError olsr4_0_1_get_willingness( // Output values, uint32_t& willingness); /** * Set the MPR_COVERAGE. * * @param coverage the new MPR_COVERAGE value. */ XrlCmdError olsr4_0_1_set_mpr_coverage( // Input values, const uint32_t& coverage); /** * Get the MPR_COVERAGE. */ XrlCmdError olsr4_0_1_get_mpr_coverage( // Output values, uint32_t& coverage); /** * Set the TC_REDUNDANCY. */ XrlCmdError olsr4_0_1_set_tc_redundancy( // Input values, const string& redundancy); /** * Get the TC_REDUNDANCY. */ XrlCmdError olsr4_0_1_get_tc_redundancy( // Output values, string& redundancy); /** * Enable/disable TC fisheye mode. * * @param enabled true to enable fisheye, false to disable it. */ XrlCmdError olsr4_0_1_set_tc_fisheye( // Input values, const bool& enabled); /** * Get the current TC fisheye mode. */ XrlCmdError olsr4_0_1_get_tc_fisheye( // Output values, bool& enabled); /** * Set the current HNA base cost metric. */ XrlCmdError olsr4_0_1_set_hna_base_cost( // Input values, const uint32_t& metric); /** * Get the current HNA base cost metric. */ XrlCmdError olsr4_0_1_get_hna_base_cost( // Output values, uint32_t& metric); /** * Set the HELLO_INTERVAL. * * @param interval the new HELLO_INTERVAL. */ XrlCmdError olsr4_0_1_set_hello_interval( // Input values, const uint32_t& interval); /** * Get the HELLO_INTERVAL. */ XrlCmdError olsr4_0_1_get_hello_interval( // Output values, uint32_t& interval); /** * Set the REFRESH_INTERVAL. * * @param interval the new REFRESH_INTERVAL. */ XrlCmdError olsr4_0_1_set_refresh_interval( // Input values, const uint32_t& interval); /** * Get the REFRESH_INTERVAL. */ XrlCmdError olsr4_0_1_get_refresh_interval( // Output values, uint32_t& interval); /** * Set the TC_INTERVAL. * * @param interval the new TC_INTERVAL. */ XrlCmdError olsr4_0_1_set_tc_interval( // Input values, const uint32_t& interval); /** * Get the TC_INTERVAL. */ XrlCmdError olsr4_0_1_get_tc_interval( // Output values, uint32_t& interval); /** * Set the MID_INTERVAL. * * @param interval the new MID_INTERVAL. */ XrlCmdError olsr4_0_1_set_mid_interval( // Input values, const uint32_t& interval); /** * Get the MID_INTERVAL. */ XrlCmdError olsr4_0_1_get_mid_interval( // Output values, uint32_t& interval); /** * Set the HNA_INTERVAL. * * @param interval the new HNA_INTERVAL. */ XrlCmdError olsr4_0_1_set_hna_interval( // Input values, const uint32_t& interval); /** * Get the HNA_INTERVAL. */ XrlCmdError olsr4_0_1_get_hna_interval( // Output values, uint32_t& interval); /** * Set the DUP_HOLD_TIME. * * @param dup_hold_time the new DUP_HOLD_TIME. */ XrlCmdError olsr4_0_1_set_dup_hold_time( // Input values, const uint32_t& dup_hold_time); /** * Get the DUP_HOLD_TIME. */ XrlCmdError olsr4_0_1_get_dup_hold_time( // Output values, uint32_t& dup_hold_time); /** * Set the main address. * * @param addr Our main IPv4 address which OLSR uses as a router ID. */ XrlCmdError olsr4_0_1_set_main_address( // Input values, const IPv4& addr); /** * Get the main address. */ XrlCmdError olsr4_0_1_get_main_address( // Output values, IPv4& addr); /** * Create an IPv4 address binding for OLSR. * * OLSR must be bound to a given protocol address on each interface, * which means interface bindings in OLSRv1 must be keyed by interface * name as well as protocol address. * * Only a single IPv4 address may be thus bound, and the address must be * supplied when the binding is created. This is to workaround the lack of * RFC 3927 link-scoped IPv4 capability in most IPv4 implementations. * * The addition of @param local_addr is not instantaneous. OLSR has to * instantiate state in the FEA to send and receive packets. Once * instantiated, the address must be explicitly enabled with the * set_binding_enabled XRL. * * @param ifname the interface that owns vif that has local_addr * @param vifname interface owning local_addr * @param local_addr the address to be added. * @param local_port the port to listen for control traffic on. * @param all_nodes_addr the address to use for transmission. * @param all_nodes_port the port to use for transmission. */ XrlCmdError olsr4_0_1_bind_address( // Input values, const string& ifname, const string& vifname, const IPv4& local_addr, const uint32_t& local_port, const IPv4& all_nodes_addr, const uint32_t& all_nodes_port); /** * Destroy an IPv4 address binding for OLSR. * * @param ifname the interface to unbind. * @param vifname the vif to unbind. */ XrlCmdError olsr4_0_1_unbind_address( // Input values, const string& ifname, const string& vifname); /** * Set the enabled state of an IPv4 address binding for OLSR. * * @param ifname the interface to set enabled state for. * @param vifname the vif to set enabled state for. * @param enabled true if OLSR is to be configured administratively up on * the interface, false if it is to be configured down. */ XrlCmdError olsr4_0_1_set_binding_enabled( // Input values, const string& ifname, const string& vifname, const bool& enabled); /** * Get the state of an IPv4 address binding for OLSR. * * @param ifname the interface to query. * @param vifname the vif to qurery * @param enabled true if OLSR is configured administratively up on the * given interface. */ XrlCmdError olsr4_0_1_get_binding_enabled( // Input values, const string& ifname, const string& vifname, // Output values, bool& enabled); /** * Change the UDP address and port where OLSR listens for control traffic * on this interface. In order to do this the process must tell the FEA to * tear down and re-bind the control traffic socket. * * @param ifname the name of the interface. * @param vifname the name of the vif. * @param local_addr the new local IPv4 address. * @param local_port the new local port number. */ XrlCmdError olsr4_0_1_change_local_addr_port( // Input values, const string& ifname, const string& vifname, const IPv4& local_addr, const uint32_t& local_port); /** * Change the address where OLSR sends control traffic on the given * interface. By default OLSR will attempt to use the all-ones broadcast * address. Currently multicast addresses are NOT supported. * * @param ifname the name of the interface. * @param vifname the name of the vif. * @param all_nodes_addr the address to use. * @param all_nodes_port the port to use. */ XrlCmdError olsr4_0_1_change_all_nodes_addr_port( // Input values, const string& ifname, const string& vifname, const IPv4& all_nodes_addr, const uint32_t& all_nodes_port); /** * Get the list of interfaces currently configured for OLSR. Return a list * of u32 type values. Each value is an internal ID that can be used with * the get_interface_info XRL. */ XrlCmdError olsr4_0_1_get_interface_list( // Output values, XrlAtomList& interfaces); /** * Get the per-interface information for the given interface. * * @param faceid interface ID returned by get_interface_list. * @param ifname the name of the interface. * @param vifname the name of the vif. * @param local_addr the IPv4 address where OLSR is listening. * @param local_port the UDP port where OLSR is listening. * @param all_nodes_addr the IPv4 address where OLSR sends packets. * @param all_nodes_port the UDP port where OLSR sends packets. */ XrlCmdError olsr4_0_1_get_interface_info( // Input values, const uint32_t& faceid, // Output values, string& ifname, string& vifname, IPv4& local_addr, uint32_t& local_port, IPv4& all_nodes_addr, uint32_t& all_nodes_port); /** * Set the edge cost of an interface/vif. * * @param ifname the name of the interface. * @param vifname the name of the vif. * @param cost the new edge cost of the interface. */ XrlCmdError olsr4_0_1_set_interface_cost( // Input values, const string& ifname, const string& vifname, const uint32_t& cost); /** * Get the per-interface statistics for the given interface. * * @param ifname the interface to query. * @param vifname the vif to qurery * @param bad_packets the number of bad packets received. * @param bad_messages the number of bad messages received. * @param messages_from_self the number of messages which appeared to be * from our own main address. * @param unknown_messages the number of messages which could not be * decoded. * @param duplicates the number of messages which appeared to be * duplicates, according to histogram based duplicate detection. * @param forwarded the number of messages which have been forwarded to * the rest of the OLSR topology on this interface. */ XrlCmdError olsr4_0_1_get_interface_stats( // Input values, const string& ifname, const string& vifname, // Output values, uint32_t& bad_packets, uint32_t& bad_messages, uint32_t& messages_from_self, uint32_t& unknown_messages, uint32_t& duplicates, uint32_t& forwarded); /** * Get the list of one-hop links. Return a list of u32 type values. Each * value is an internal ID that can be used with the get_link_info XRL. */ XrlCmdError olsr4_0_1_get_link_list( // Output values, XrlAtomList& links); /** * Get the information for a one-hop link. * * @param linkid Link entry ID returned by get_link_list. * @param local_addr the interface address of the local end of this link. * @param remote_addr the interface address of the remote end of this * link. * @param main_addr the main address of the neighbor at the remote end of * this link. * @param link_type the type of this link. * @param sym_time the time in seconds for which this link will be * considered symmetric. * @param asym_time the time in seconds for which this link will be * considered asymmetric. * @param hold_time the time in seconds until this link expires. */ XrlCmdError olsr4_0_1_get_link_info( // Input values, const uint32_t& linkid, // Output values, IPv4& local_addr, IPv4& remote_addr, IPv4& main_addr, uint32_t& link_type, uint32_t& sym_time, uint32_t& asym_time, uint32_t& hold_time); /** * Get the list of one-hop neighbors. Return a list of u32 type values. * Each value is an internal ID that can be used with the * get_neighbor_info XRL. */ XrlCmdError olsr4_0_1_get_neighbor_list( // Output values, XrlAtomList& neighbors); /** * Get the information for a one-hop neighbor. * * @param nid Neighbor entry ID returned by get_neighbor_list. * @param main_addr the main address of this neighbor. * @param willingness the willingness of this neighbor to forward. * @param degree the number of symmetric strict neighbors of this * neighbor, excluding one-hop neighbors and this node. * @param link_count the number of links to this neighbor. * @param twohop_link_count the number of two-hop links which transit this * neighbor as next-hop. * @param is_advertised true if this neighbor is in the Advertised * Neighbor Set of this node. * @param is_sym true if this neighbor is symmetric. * @param is_mpr true if this neighbor is selected as an MPR by this node. * @param is_mpr_selector true if this neighbor chooses this node as an * MPR. */ XrlCmdError olsr4_0_1_get_neighbor_info( // Input values, const uint32_t& nid, // Output values, IPv4& main_addr, uint32_t& willingness, uint32_t& degree, uint32_t& link_count, uint32_t& twohop_link_count, bool& is_advertised, bool& is_sym, bool& is_mpr, bool& is_mpr_selector); /** * Get the list of two-hop links. Return a list of u32 type values. Each * value is an internal ID that can be used with the get_twohop_link_info * XRL. */ XrlCmdError olsr4_0_1_get_twohop_link_list( // Output values, XrlAtomList& twohop_links); /** * Get the information for a two-hop link. * * @param tlid two-hop link ID returned by get_twohop_link_list. * @param last_face_id the internal interface ID where advertisement of * this two-hop link was last seen. * @param nexthop_addr the main address of the one-hop neighbor where this * two-hop link exists. * @param dest_addr the main address of the two-hop neighbor at the remote * end of this link. * @param hold_time the time in seconds until this two-hop link expires. */ XrlCmdError olsr4_0_1_get_twohop_link_info( // Input values, const uint32_t& tlid, // Output values, uint32_t& last_face_id, IPv4& nexthop_addr, IPv4& dest_addr, uint32_t& hold_time); /** * Get the list of two-hop neighbors. Return a list of u32 type values. * Each value is an internal ID that can be used with the * get_twohop_neighbor_info XRL. */ XrlCmdError olsr4_0_1_get_twohop_neighbor_list( // Output values, XrlAtomList& twohop_neighbors); /** * Get the information for a two-hop neighbor. * * @param tnid two-hop neighbor ID returned by get_twohop_neighbor_list. * @param main_addr the main address of this two-hop neighbor. * @param is_strict true if this two-hop neighbor is not also a two-hop * neighbor. * @param link_count the number of two-hop links that exist to this * two-hop neighbor. * @param reachability the number of MPR candidates which cover this * two-hop neighbor. * @param coverage the number of selected MPRs which cover this two-hop * neighbor. */ XrlCmdError olsr4_0_1_get_twohop_neighbor_info( // Input values, const uint32_t& tnid, // Output values, IPv4& main_addr, bool& is_strict, uint32_t& link_count, uint32_t& reachability, uint32_t& coverage); /** * Get the list of learned Multiple Interface Declaration (MID) entries. * Return a list of u32 type values. Each value is an internal ID that can * be used with the get_mid_entry XRL. */ XrlCmdError olsr4_0_1_get_mid_entry_list( // Output values, XrlAtomList& mid_entries); /** * Get the information contained in a MID entry. * * @param midid MID entry ID returned by get_mid_entry_list. * @param main_addr the main address of the OLSR node * @param iface_addr the interface address being advertised. * @param distance the distance measured between this node and the origin * of the MID packet containing this entry. * @param hold_time the time in seconds until this entry expires. */ XrlCmdError olsr4_0_1_get_mid_entry( // Input values, const uint32_t& midid, // Output values, IPv4& main_addr, IPv4& iface_addr, uint32_t& distance, uint32_t& hold_time); /** * Get the list of learned Topology Control (TC) entries. Return a list of * u32 type values. Each value is an internal ID that can be used with the * get_tc_entry XRL. */ XrlCmdError olsr4_0_1_get_tc_entry_list( // Output values, XrlAtomList& tc_entries); /** * Get the information contained in a TC entry. * * @param tcid TC entry ID returned by get_tc_entry_list. * @param destination the main address of the advertised destination. * @param lasthop the main address of the node advertising this entry. * @param distance the distance measured between this node and the origin * of the TC packet containing this entry. * @param seqno the advertised sequence number of this entry. * @param hold_time the time in seconds until this entry expires. */ XrlCmdError olsr4_0_1_get_tc_entry( // Input values, const uint32_t& tcid, // Output values, IPv4& destination, IPv4& lasthop, uint32_t& distance, uint32_t& seqno, uint32_t& hold_time); /** * Get the list of learned external route (HNA) entries. Return a list of * u32 type values. Each value is an internal ID that can be used with the * get_hna_entry XRL. */ XrlCmdError olsr4_0_1_get_hna_entry_list( // Output values, XrlAtomList& hna_entries); /** * Get the information contained in a HNA entry. * * @param hnaid HNA entry ID returned by get_hna_entry_list. * @param destination the main address of the advertised destination. * @param lasthop the main address of the node advertising this entry. * @param distance the distance measured between this node and the origin * of the TC packet containing this entry. * @param hold_time the time in seconds until this entry expires. */ XrlCmdError olsr4_0_1_get_hna_entry( // Input values, const uint32_t& hnaid, // Output values, IPv4Net& destination, IPv4& lasthop, uint32_t& distance, uint32_t& hold_time); private: Olsr& _olsr; XrlIO& _xrl_io; }; #endif // __OLSR_XRL_TARGET_HH__ xorp/contrib/olsr/face.hh0000664000076400007640000002272511421137511015522 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8 sw=4: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/contrib/olsr/face.hh,v 1.3 2008/10/02 21:56:34 bms Exp $ #ifndef __OLSR_FACE_HH__ #define __OLSR_FACE_HH__ class FaceManager; class Packet; class Link; class Message; class MessageDecoder; class Neighborhood; class Olsr; class XorpTimer; /** * @short Per-interface protocol statistics. */ class FaceCounters { public: FaceCounters() : _bad_packets(0), _bad_messages(0), _duplicates(0), _messages_from_self(0), _unknown_messages(0), _forwarded(0) {} inline size_t bad_packets() const { return _bad_packets; } inline void incr_bad_packets() { _bad_packets++; } inline size_t bad_messages() const { return _bad_messages; } inline void incr_bad_messages() { _bad_messages++; } inline size_t messages_from_self() const { return _messages_from_self; } inline void incr_messages_from_self() { _messages_from_self++; } inline size_t unknown_messages() const { return _unknown_messages; } inline void incr_unknown_messages() { _unknown_messages++; } inline size_t duplicates() const { return _duplicates; } inline void incr_duplicates() { _duplicates++; } inline size_t forwarded() const { return _forwarded; } inline void incr_forwarded() { _forwarded++; } private: /** * The number of bad packets received on this Face. */ uint32_t _bad_packets; /** * The number of bad messages received on this Face. */ uint32_t _bad_messages; /** * The number of messages received on this Face which were * already processed according to the duplicate set. */ uint32_t _duplicates; /** * The number of messages received on this Face which contained * our main address as the origin. */ uint32_t _messages_from_self; /** * The number of messages of unknown type received on this Face. */ uint32_t _unknown_messages; /** * The number of messages received on this interface which * have been forwarded to other nodes. */ uint32_t _forwarded; }; /** * @short An OLSR interface. * * There is one Face per physical if/vif which OLSR is * configured to run on. * * Whilst an IPv4 interface may have multiple addresses, * OLSR uses IPv4 addresses as primary keys in the * protocol. Therefore it is necessary to track the first * configured address on each vif, and use that as the * Face's address. This happens in the absence of any * IPv4 link-scope addresses as the system does not * have any concept of such things. This may be considered * the same limitation as IGMP has. * * The analogue in XORP's OSPF implementation is class Peer. * Unlike OSPF, we do not split Face in the same way as Peer/PeerOut * are split, as there is no concept of Areas in version 1 of OLSR. * * A face is associated with Links to each Neighbor. * An instance of Olsr must have at least one Face configured to run Olsr, * otherwise, it isn't an Olsr router. * If no Faces exist, OLSR is not active. * If only one Face exists, only a subset of the full OLSR protocol is used. */ class Face { public: Face(Olsr& olsr, FaceManager& fm, Neighborhood* nh, MessageDecoder& md, const string& interface, const string& vif, OlsrTypes::FaceID id); /** * @return the internal ID of this OLSR interface. */ inline OlsrTypes::FaceID id() const { return _id; } /** * @return the name of this OLSR interface as known to the FEA. */ string interface() const { return _interface; } /** * @return the name of this OLSR interface's vif as known to the FEA. */ string vif() const { return _vif; } /** * @return the protocol counters for this OLSR interface. */ FaceCounters& counters() { return _counters; } /** * @return the protocol counters for this OLSR interface. */ const FaceCounters& counters() const { return _counters; } /** * @return the MTU of this interface. */ inline uint32_t mtu() const { return _mtu; } /** * Set the MTU of this interface. */ inline void set_mtu(const uint32_t mtu) { _mtu = mtu; } /** * @return the next available sequence number for a Packet to be * sent from this OLSR interface. */ inline uint32_t get_pkt_seqno() { return _next_pkt_seqno++; } /** * @return true if this OLSR interface is administratively up. */ inline bool enabled() const { return _enabled; } /** * @return the "all nodes" address configured for this OLSR interface. */ inline IPv4 all_nodes_addr() const { return _all_nodes_addr; } /** * @return the "all nodes" UDP port configured for this OLSR interface. */ inline uint16_t all_nodes_port() const { return _all_nodes_port; } /** * @return the source address configured for this OLSR interface. */ inline IPv4 local_addr() const { return _local_addr; } /** * @return the UDP source port configured for this OLSR interface. */ inline uint16_t local_port() const { return _local_port; } /** * @return the default cost of transiting this OLSR interface. * Used by the shortest path algorithm. */ inline int cost() const { return _cost; } /** * Set the "administratively up" state of an OLSR interface. * * @param value true if this interface is enabled for OLSR, * otherwise false. */ void set_enabled(bool value); /** * Set the "all nodes" address for this OLSR interface. * * @param all_nodes_addr the "all nodes" address to set. */ inline void set_all_nodes_addr(const IPv4& all_nodes_addr) { _all_nodes_addr = all_nodes_addr; } /** * Set the "all nodes" UDP port for this OLSR interface. * * @param all_nodes_port the "all nodes" port to set. */ inline void set_all_nodes_port(const uint16_t all_nodes_port) { _all_nodes_port = all_nodes_port; } /** * Set the local address for this OLSR interface. * * @param local_addr the local address to set. */ inline void set_local_addr(const IPv4& local_addr) { _local_addr = local_addr; } /** * Set the UDP local port for this OLSR interface. * * @param local_port the local port to set. */ inline void set_local_port(const uint16_t local_port) { _local_port = local_port; } /** * Set the shortest path tree cost for this OLSR interface. * * @param cost the cost to set. */ inline void set_cost(int cost) { _cost = cost; } /** * Transmit a datagram on this Face. * * @param data the datagram to be sent. * @param len the length of the datagram to be sent. * @return true if the datagram was successfully transmitted. */ bool transmit(uint8_t* data, const uint32_t& len); /** * Originate a HELLO message on this interface. * * The message thus originated will be filled out appropriately, * containing the relevant OLSR protocol state as seen from this Face. * * TODO: Perform MTU-based message segmentation. */ void originate_hello(); private: Olsr& _olsr; FaceManager& _fm; Neighborhood* _nh; MessageDecoder& _md; /** * @short Counters for this Face. */ FaceCounters _counters; /** * @short A unique identifier for this Face. */ OlsrTypes::FaceID _id; /** * @short true if this Face is enabled for routing. */ bool _enabled; /** * @short The name of the interface with which this Face is associated. */ string _interface; /** * @short The name of the Vif with which this Face is associated. */ string _vif; /** * @short The MTU for this interface. */ uint32_t _mtu; /** * @short Local address to use for transmitting OLSR * protocol messages in UDP. */ IPv4 _local_addr; /** * @short Local port to use for transmitting * OLSR protocol messages. */ uint16_t _local_port; /** * @short The "all nodes" address to use on this Face * for sending protocol messages. */ IPv4 _all_nodes_addr; /** * @short The "all nodes" port to use on this Face * for sending protocol messages. */ uint16_t _all_nodes_port; /** * @short A fixed cost for the interface, used in route calculation. */ int _cost; /** * @short The sequence number of the next packet sent from this Face. */ uint32_t _next_pkt_seqno; }; #endif // __OLSR_FACE_HH__ xorp/contrib/olsr/debug_io.hh0000664000076400007640000001501611421137511016374 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8 sw=4: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/contrib/olsr/debug_io.hh,v 1.4 2008/10/02 21:56:33 bms Exp $ #ifndef __OLSR_DEBUG_IO_HH__ #define __OLSR_DEBUG_IO_HH__ /** * @short The DebugIO class realizes the interface IO. * * It is used for OLSR regression tests. */ class DebugIO : public IO { public: DebugIO(TestInfo& info, EventLoop& eventloop); virtual ~DebugIO(); /** * @short Pretty print frames. Specific to DebugIo. */ void pp(const string& which, int level, const string& interface, const string& vif, IPv4 dst, uint16_t dport, IPv4 src, uint16_t sport, uint8_t* data, uint32_t len); int startup(); int shutdown(); /** * @short Enable an IPv4 address and port * for OLSR datagram reception. */ bool enable_address(const string& interface, const string& vif, const IPv4& address, const uint16_t& port, const IPv4& all_nodes_address); /** * @short Disable an IPv4 address and port for * OLSR datagram reception. */ bool disable_address(const string& interface, const string& vif, const IPv4& address, const uint16_t& port); /** * Test whether this interface is enabled. * * @return true if it exists and is enabled, otherwise false. */ bool is_interface_enabled(const string& interface) const; /** * Test whether this interface/vif is enabled. * * @return true if it exists and is enabled, otherwise false. */ bool is_vif_enabled(const string & interface, const string & vif) const; /** * @short Return true if the given vif is broadcast capable. */ bool is_vif_broadcast_capable(const string& interface, const string& vif); /** * @short Return true if the given vif is multicast capable. */ bool is_vif_multicast_capable(const string& interface, const string& vif); /** * @short Return true if the given vif is a loopback vif. */ bool is_vif_loopback(const string& interface, const string& vif); /** * Test whether this interface/vif/address is enabled. * * @return true if it exists and is enabled, otherwise false. */ bool is_address_enabled(const string& interface, const string& vif, const IPv4& address) const; /** * Get all addresses associated with this interface/vif. * * @param interface the name of the interface * @param vif the name of the vif * @param addresses (out argument) list of associated addresses * * @return true if there are no errors. */ bool get_addresses(const string& interface, const string& vif, list& addresses) const; /** * Get the broadcast address associated with this IPv4 address. * * @param interface the name of the interface * @param vif the name of the vif * @param address the IPv4 interface address * @param bcast_address (out argument) the IPv4 broadcast address * * @return true if there are no errors. */ bool get_broadcast_address(const string& interface, const string& vif, const IPv4& address, IPv4& bcast_address) const; /** * @return the interface id for this interface, as seen by libfeaclient. */ bool get_interface_id(const string& interface, uint32_t& interface_id); /** * @return the mtu for this interface. */ uint32_t get_mtu(const string& interface); /** * @return the number of packets which have transited this interface. */ inline int packets() const { return _packets; } /** * Send a UDP datagram from src:sport to dst:dport, * preferably on the given link. */ bool send(const string& interface, const string& vif, const IPv4 & src, const uint16_t & sport, const IPv4 & dst, const uint16_t & dport, uint8_t* data, const uint32_t & len); /** * Receive frames. Specific to DebugIo. */ void receive(const string& interface, const string& vif, const IPv4 & dst, const uint16_t& dport, const IPv4 & src, const uint16_t& sport, uint8_t* data, const uint32_t & len); /** * Register where datagrams should be forwarded. Specific to DebugIo. */ bool register_forward(const string& interface, const string& vif, IO::ReceiveCallback cb); /** * Unregister an existing callback registered with register_forward(). * Specific to DebugIo. */ void unregister_forward(const string& interface, const string& vif); bool add_route(IPv4Net net, IPv4 nexthop, uint32_t nexthop_id, uint32_t metric, const PolicyTags& policytags); bool replace_route(IPv4Net net, IPv4 nexthop, uint32_t nexthop_id, uint32_t metric, const PolicyTags & policytags); bool delete_route(IPv4Net net); void routing_table_empty(); uint32_t routing_table_size(); /** * Verify that this route is in the routing table. */ bool routing_table_verify(IPv4Net net, IPv4 nexthop, uint32_t metric); /** * Dump the routing table contents to the given ostream (not necessarily * what's in the TestInfo). */ void routing_table_dump(ostream& o); private: TestInfo& _info; EventLoop& _eventloop; int _packets; uint32_t _next_interface_id; map _interface_ids; MessageDecoder _md; /** * * Forwarding callback, one per emulated interface, so * that interfaces may be bound to separate EmulateSubnet instances. */ map, IO::ReceiveCallback> _forward_cbs; struct DebugRouteEntry { IPv4 _nexthop; uint32_t _metric; PolicyTags _policytags; }; map _routing_table; }; #endif // __OLSR_DEBUG_IO_HH__ xorp/contrib/olsr/olsr_module.h0000664000076400007640000000211111421137511016763 0ustar greearbgreearb/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- */ /* vim:set sts=4 ts=8 sw=4: */ /* * Copyright (c) 2001-2009 XORP, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 2, June * 1991 as published by the Free Software Foundation. Redistribution * and/or modification of this program under the terms of any other * version of the GNU General Public License is not permitted. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, * see the GNU General Public License, Version 2, a copy of which can be * found in the XORP LICENSE.gpl file. * * XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; * http://xorp.net */ /* * $XORP: xorp/contrib/olsr/olsr_module.h,v 1.4 2008/10/02 21:56:35 bms Exp $ */ /* * Module definitions. */ #define XORP_MODULE_NAME "OLSR" #define XORP_MODULE_VERSION "0.1" xorp/contrib/olsr/twohop.cc0000664000076400007640000000744411540225522016135 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8 sw=4: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "olsr_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/callback.hh" #include "libxorp/ipv4.hh" #include "libxorp/ipv4net.hh" #include "libxorp/status_codes.h" #include "libxorp/service.hh" #include "libxorp/eventloop.hh" #include "olsr.hh" #include "link.hh" #include "neighbor.hh" #include "neighborhood.hh" #include "twohop.hh" TwoHopNeighbor::TwoHopNeighbor(EventLoop& ev, Neighborhood* parent, const OlsrTypes::TwoHopNodeID id, const IPv4& main_addr, const OlsrTypes::TwoHopLinkID tlid) : _ev(ev), _parent(parent), _id(id), _main_addr(main_addr), _is_strict(false), _coverage(0), _reachability(0) { add_twohop_link(tlid); } string TwoHopNeighbor::toStringBrief() { ostringstream oss; oss << id() << "-(" << main_addr().str() << ")"; return oss.str(); } void TwoHopNeighbor::add_twohop_link(const OlsrTypes::TwoHopLinkID tlid) { debug_msg("TwoHopLinkID %u\n", XORP_UINT_CAST(tlid)); // Invariant: Do not insert a link more than once. XLOG_ASSERT(0 == _twohop_links.count(tlid)); _twohop_links.insert(tlid); } bool TwoHopNeighbor::delete_twohop_link(const OlsrTypes::TwoHopLinkID tlid) { debug_msg("TwoHopLinkID %u\n", XORP_UINT_CAST(tlid)); // Invariant: Do not erase a non-existent link. XLOG_ASSERT(0 != _twohop_links.count(tlid)); _twohop_links.erase(tlid); return _twohop_links.empty(); } size_t TwoHopNeighbor::delete_all_twohop_links() { size_t deleted_count = 0; // Do not erase elements as Neighborhood will call back into // TwoHopNeighbor::delete_twohop_link(). Maintain a separate // iterator as jj will be invalidated by that method invocation. set::iterator ii, jj; for (ii = _twohop_links.begin(); ii != _twohop_links.end(); ) { jj = ii++; _parent->delete_twohop_link((*jj)); ++deleted_count; } return deleted_count; } void TwoHopNeighbor::add_covering_mpr(const OlsrTypes::NeighborID nid) { // We need only update a counter if MPR state is computed // for N2(all I), rather than for N2(specific I). _coverage++; UNUSED(nid); } void TwoHopNeighbor::withdraw_covering_mpr(const OlsrTypes::NeighborID nid) { --_coverage; UNUSED(nid); } void TwoHopNeighbor::reset_covering_mprs() { _coverage = 0; } TwoHopLink::TwoHopLink(EventLoop& ev, Neighborhood* parent, OlsrTypes::TwoHopLinkID tlid, Neighbor* nexthop, const TimeVal& vtime) : _ev(ev), _parent(parent), _id(tlid), _nexthop(nexthop), _destination(0) { update_timer(vtime); } void TwoHopLink::update_timer(const TimeVal& vtime) { if (_expiry_timer.scheduled()) _expiry_timer.clear(); _expiry_timer = _ev.new_oneoff_after(vtime, callback(this, &TwoHopLink::event_dead)); } void TwoHopLink::event_dead() { debug_msg("called\n"); _parent->event_twohop_link_dead_timer(id()); } xorp/contrib/olsr/face.cc0000664000076400007640000000644611540224221015507 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8 sw=4: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "olsr_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/callback.hh" #include "libxorp/ipv4.hh" #include "libxorp/ipv4net.hh" #include "libxorp/status_codes.h" #include "libxorp/service.hh" #include "libxorp/eventloop.hh" #include "olsr.hh" #include "face.hh" Face::Face(Olsr& olsr, FaceManager& fm, Neighborhood* nh, MessageDecoder& md, const string& interface, const string& vif, OlsrTypes::FaceID id) : _olsr(olsr), _fm(fm), _nh(nh), _md(md), _id(id), _enabled(false), _interface(interface), _vif(vif), _mtu(0), // XXX obtained later _local_addr(IPv4::ZERO()), // XXX obtained later _local_port(OlsrTypes::DEFAULT_OLSR_PORT), _all_nodes_addr(IPv4::ALL_ONES()), _all_nodes_port(OlsrTypes::DEFAULT_OLSR_PORT), _cost(OlsrTypes::DEFAULT_STATIC_FACE_COST), _next_pkt_seqno(1) { } void Face::set_enabled(bool value) { debug_msg("enable %s\n", bool_c_str(value)); if (value == _enabled) return; _enabled = value; XLOG_ASSERT(0 != _nh); if (_enabled == false) { _nh->delete_face(id()); } else { _nh->add_face(id()); } } bool Face::transmit(uint8_t* data, const uint32_t& len) { debug_msg("tx data %p len %u\n", data, len); return _fm.transmit(_interface, _vif, _all_nodes_addr, _all_nodes_port, _local_addr, _local_port, data, len); } void Face::originate_hello() { Packet* pkt = new Packet(_md, id()); HelloMessage* hello = new HelloMessage(); // Set message attributes which are local to Face and FaceManager. hello->set_origin(_fm.get_main_addr()); hello->set_ttl(1); hello->set_hop_count(0); hello->set_seqno(_fm.get_msg_seqno()); hello->set_htime(_fm.get_hello_interval()); // We must tell Neighborhood from which face this HELLO originates. hello->set_faceid(id()); // Push link state into the message. _nh->populate_hello(hello); // We would typically segment the message here. pkt->set_mtu(mtu()); pkt->add_message(hello); vector buf; bool result = pkt->encode(buf); if (result == false) { XLOG_WARNING("Outgoing packet on %s/%s truncated by MTU.", interface().c_str(), vif().c_str()); } pkt->set_seqno(get_pkt_seqno()); //pkt->update_encoded_seqno(buf); transmit(&buf[0], buf.size()); delete hello; // XXX no refcounting delete pkt; } xorp/contrib/olsr/xrl_target.cc0000664000076400007640000007434111421137511016766 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8 sw=4: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #include "olsr_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/status_codes.h" #include "libxorp/service.hh" #include "libxorp/eventloop.hh" #include "libxorp/ipv4.hh" #include "libxorp/ipv6.hh" #include "libxipc/xrl_std_router.hh" #include "olsr.hh" #include "xrl_io.hh" #include "xrl_target.hh" XrlOlsr4Target::XrlOlsr4Target(XrlRouter* r, Olsr& olsr, XrlIO& xrl_io) : XrlOlsr4TargetBase(r), _olsr(olsr), _xrl_io(xrl_io) { } /* * common/0.1 target interface. */ XrlCmdError XrlOlsr4Target::common_0_1_get_target_name(string& name) { name = "ospf4"; return XrlCmdError::OKAY(); } XrlCmdError XrlOlsr4Target::common_0_1_get_version(string& version) { version = "0.1"; return XrlCmdError::OKAY(); } XrlCmdError XrlOlsr4Target::common_0_1_get_status(uint32_t& status, string& reason) { status = _olsr.status(reason); return XrlCmdError::OKAY(); } XrlCmdError XrlOlsr4Target::common_0_1_shutdown() { debug_msg("common_0_1_shutdown\n"); _olsr.shutdown(); return XrlCmdError::OKAY(); } /* * socket4_user/0.1 target interface. */ XrlCmdError XrlOlsr4Target::finder_event_observer_0_1_xrl_target_birth( const string& target_class, const string& target_instance) { debug_msg("finder_event_observer_0_1_xrl_target_birth %s %s\n", target_class.c_str(), target_instance.c_str()); return XrlCmdError::OKAY(); UNUSED(target_class); UNUSED(target_instance); } XrlCmdError XrlOlsr4Target::finder_event_observer_0_1_xrl_target_death( const string& target_class, const string& target_instance) { debug_msg("finder_event_observer_0_1_xrl_target_death %s %s\n", target_class.c_str(), target_instance.c_str()); return XrlCmdError::OKAY(); UNUSED(target_class); UNUSED(target_instance); } /* * socket4_user/0.1 target interface. */ XrlCmdError XrlOlsr4Target::socket4_user_0_1_recv_event( const string& sockid, const string& if_name, const string& vif_name, const IPv4& src_host, const uint32_t& src_port, const vector& data) { debug_msg("socket4_user_0_1_recv_event %s %s/%s %s/%u %u\n", sockid.c_str(), if_name.c_str(), vif_name.c_str(), cstring(src_host), XORP_UINT_CAST(src_port), XORP_UINT_CAST(data.size())); if (if_name == "" || vif_name == "") { // Note: This can occur if an interface was added to the FEA interface { } // block at runtime and a binding later added without restarting the FEA. XLOG_FATAL("No FEA platform support for determining interface name, " "bailing. Please report this to the XORP/OLSR maintainer."); } // // Note: The socket4_user interface does not tell us the // destination address of the IPv4 datagram. // _xrl_io.receive(sockid, if_name, vif_name, src_host, src_port, data); return XrlCmdError::OKAY(); } XrlCmdError XrlOlsr4Target::socket4_user_0_1_inbound_connect_event( const string& sockid, const IPv4& src_host, const uint32_t& src_port, const string& new_sockid, bool& accept) { debug_msg("socket4_user_0_1_inbound_connect_event %s %s/%u %s\n", sockid.c_str(), cstring(src_host), XORP_UINT_CAST(src_port), new_sockid.c_str()); accept = false; return XrlCmdError::COMMAND_FAILED("Inbound connect not requested."); UNUSED(sockid); UNUSED(src_host); UNUSED(src_port); UNUSED(new_sockid); } XrlCmdError XrlOlsr4Target::socket4_user_0_1_outgoing_connect_event( const string& sockid) { debug_msg("socket4_user_0_1_outgoing_connect_event %s\n", sockid.c_str()); return XrlCmdError::COMMAND_FAILED("Outgoing connect not requested."); UNUSED(sockid); } XrlCmdError XrlOlsr4Target::socket4_user_0_1_error_event( const string& sockid, const string& error, const bool& fatal) { debug_msg("socket4_user_0_1_error_event %s %s %s\n", sockid.c_str(), error.c_str(), fatal ? "fatal" : "non-fatal"); return XrlCmdError::OKAY(); UNUSED(sockid); UNUSED(error); UNUSED(fatal); } XrlCmdError XrlOlsr4Target::socket4_user_0_1_disconnect_event(const string& sockid) { debug_msg("socket4_user_0_1_disconnect_event %s\n", sockid.c_str()); return XrlCmdError::OKAY(); UNUSED(sockid); } /* * policy_backend/0.1 target interface. */ XrlCmdError XrlOlsr4Target::policy_backend_0_1_configure(const uint32_t& filter, const string& conf) { debug_msg("policy_backend_0_1_configure %u %s\n", XORP_UINT_CAST(filter), conf.c_str()); try { #ifdef notyet XLOG_TRACE(_olsr.profile().enabled(trace_policy_configure), "policy filter: %d conf: %s\n", filter, conf.c_str()); #else debug_msg("policy filter: %d conf: %s\n", filter, conf.c_str()); #endif _olsr.configure_filter(filter, conf); } catch(const PolicyException& e) { return XrlCmdError::COMMAND_FAILED("Filter configure failed: " + e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlOlsr4Target::policy_backend_0_1_reset(const uint32_t& filter) { debug_msg("policy_backend_0_1_reset %u\n", XORP_UINT_CAST(filter)); try { #ifdef notyet XLOG_TRACE(_olsr.profile().enabled(trace_policy_configure), "policy filter: %d\n", filter); #else debug_msg("policy filter: %d\n", filter); #endif _olsr.reset_filter(filter); } catch(const PolicyException& e) { return XrlCmdError::COMMAND_FAILED("Filter reset failed: " + e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlOlsr4Target::policy_backend_0_1_push_routes() { debug_msg("policy_backend_0_1_push_routes\n"); _olsr.push_routes(); return XrlCmdError::OKAY(); } /* * policy_redist/0.1 target interface. */ XrlCmdError XrlOlsr4Target::policy_redist4_0_1_add_route4( const IPv4Net& network, const bool& unicast, const bool& multicast, const IPv4& nexthop, const uint32_t& metric, const XrlAtomList& policytags) { debug_msg("policy_redist4_0_1_add_route4 %s %s %s %s %u %u\n", cstring(network), bool_c_str(unicast), bool_c_str(multicast), cstring(nexthop), XORP_UINT_CAST(metric), XORP_UINT_CAST(policytags.size())); // OLSR does not [yet] support multicast routes. if (! unicast) return XrlCmdError::OKAY(); if (! _olsr.originate_external_route(network, nexthop, metric, policytags)) { return XrlCmdError::COMMAND_FAILED("Network: " + network.str()); } return XrlCmdError::OKAY(); UNUSED(multicast); } XrlCmdError XrlOlsr4Target::policy_redist4_0_1_delete_route4( const IPv4Net& network, const bool& unicast, const bool& multicast) { debug_msg("policy_redist4_0_1_delete_route4 %s %s %s\n", cstring(network), bool_c_str(unicast), bool_c_str(multicast)); if (! unicast) return XrlCmdError::OKAY(); if (! _olsr.withdraw_external_route(network)) return XrlCmdError::COMMAND_FAILED("Network: " + network.str()); return XrlCmdError::OKAY(); UNUSED(multicast); } /* * profile/0.1 target interface. */ XrlCmdError XrlOlsr4Target::profile_0_1_enable(const string& pname) { return XrlCmdError::COMMAND_FAILED("Profiling not yet implemented"); UNUSED(pname); } XrlCmdError XrlOlsr4Target::profile_0_1_disable(const string& pname) { return XrlCmdError::COMMAND_FAILED("Profiling not yet implemented"); UNUSED(pname); } XrlCmdError XrlOlsr4Target::profile_0_1_get_entries(const string& pname, const string& instance_name) { return XrlCmdError::COMMAND_FAILED("Profiling not yet implemented"); UNUSED(pname); UNUSED(instance_name); } XrlCmdError XrlOlsr4Target::profile_0_1_clear(const string& pname) { return XrlCmdError::COMMAND_FAILED("Profiling not yet implemented"); UNUSED(pname); } XrlCmdError XrlOlsr4Target::profile_0_1_list(string& info) { return XrlCmdError::COMMAND_FAILED("Profiling not yet implemented"); UNUSED(info); } /* * olsr4/0.1 target interface. */ XrlCmdError XrlOlsr4Target::olsr4_0_1_trace(const string& tvar, const bool& enable) { debug_msg("olsr4_0_1_trace %s %s\n", tvar.c_str(), bool_c_str(enable)); // TODO: Enable profiling points here. if (tvar == "all") { _olsr.trace().all(enable); } else { return XrlCmdError:: COMMAND_FAILED(c_format("Unknown variable %s", tvar.c_str())); } return XrlCmdError::OKAY(); } XrlCmdError XrlOlsr4Target::olsr4_0_1_clear_database() { debug_msg("olsr4_0_1_clear_database\n"); if (! _olsr.clear_database()) return XrlCmdError::COMMAND_FAILED("Unable to clear database"); return XrlCmdError::OKAY(); } XrlCmdError XrlOlsr4Target::olsr4_0_1_set_willingness(const uint32_t& willingness) { debug_msg("olsr4_0_1_set_willingness %u\n", XORP_UINT_CAST(willingness)); if (! _olsr.set_willingness(OlsrTypes::WillType(willingness))) return XrlCmdError::COMMAND_FAILED("Unable to set willingness"); return XrlCmdError::OKAY(); } XrlCmdError XrlOlsr4Target::olsr4_0_1_get_willingness(uint32_t& willingness) { debug_msg("olsr4_0_1_get_willingness\n"); willingness = _olsr.get_willingness(); return XrlCmdError::OKAY(); } XrlCmdError XrlOlsr4Target::olsr4_0_1_set_mpr_coverage(const uint32_t& coverage) { debug_msg("olsr4_0_1_set_mpr_coverage %u\n", XORP_UINT_CAST(coverage)); if (! _olsr.set_mpr_coverage(coverage)) return XrlCmdError::COMMAND_FAILED("Unable to set MPR_COVERAGE"); return XrlCmdError::OKAY(); } XrlCmdError XrlOlsr4Target::olsr4_0_1_get_mpr_coverage(uint32_t& coverage) { debug_msg("olsr4_0_1_get_mpr_coverage\n"); coverage = _olsr.get_mpr_coverage(); return XrlCmdError::OKAY(); } XrlCmdError XrlOlsr4Target::olsr4_0_1_set_tc_redundancy(const string& redundancy) { debug_msg("olsr4_0_1_set_tc_redundancy %s\n", redundancy.c_str()); OlsrTypes::TcRedundancyType type; do { if (strcasecmp(redundancy.c_str(), "mprs") == 0) { type = OlsrTypes::TCR_MPRS_IN; break; } if (strcasecmp(redundancy.c_str(), "mprs-and-selectors") == 0) { type = OlsrTypes::TCR_MPRS_INOUT; break; } if (strcasecmp(redundancy.c_str(), "all") == 0) { type = OlsrTypes::TCR_ALL; break; } return XrlCmdError::BAD_ARGS("Unknown TC_REDUNDANCY mode" + redundancy); } while (false); if (! _olsr.set_tc_redundancy(type)) return XrlCmdError::COMMAND_FAILED("Unable to set TC_REDUNDANCY"); return XrlCmdError::OKAY(); } XrlCmdError XrlOlsr4Target::olsr4_0_1_get_tc_redundancy(string& redundancy) { debug_msg("olsr4_0_1_get_tc_redundancy\n"); redundancy = _olsr.get_tc_redundancy(); return XrlCmdError::OKAY(); } XrlCmdError XrlOlsr4Target::olsr4_0_1_set_tc_fisheye(const bool& enabled) { debug_msg("olsr4_0_1_set_tc_fisheye %s\n", bool_c_str(enabled)); return XrlCmdError::COMMAND_FAILED("Unable to set TC fisheye; " "not yet implemented"); UNUSED(enabled); } XrlCmdError XrlOlsr4Target::olsr4_0_1_get_tc_fisheye(bool& enabled) { debug_msg("olsr4_0_1_get_tc_fisheye\n"); return XrlCmdError::COMMAND_FAILED("Unable to get TC fisheye; " "not yet implemented"); UNUSED(enabled); } XrlCmdError XrlOlsr4Target::olsr4_0_1_set_hna_base_cost(const uint32_t& metric) { debug_msg("olsr4_0_1_set_hna_base_cost %u\n", XORP_UINT_CAST(metric)); return XrlCmdError::COMMAND_FAILED("Unable to set HNA base cost; " "not yet implemented"); UNUSED(metric); } XrlCmdError XrlOlsr4Target::olsr4_0_1_get_hna_base_cost(uint32_t& metric) { debug_msg("olsr4_0_1_get_hna_base_cost\n"); return XrlCmdError::COMMAND_FAILED("Unable to get HNA base cost; " "not yet implemented"); UNUSED(metric); } XrlCmdError XrlOlsr4Target::olsr4_0_1_set_hello_interval(const uint32_t& interval) { debug_msg("olsr4_0_1_set_hello_interval %u\n", XORP_UINT_CAST(interval)); if (! _olsr.set_hello_interval(TimeVal(interval, 0))) return XrlCmdError::COMMAND_FAILED("Unable to set HELLO_INTERVAL"); return XrlCmdError::OKAY(); } XrlCmdError XrlOlsr4Target::olsr4_0_1_get_hello_interval(uint32_t& interval) { debug_msg("olsr4_0_1_get_hello_interval\n"); interval = _olsr.get_hello_interval().sec(); return XrlCmdError::OKAY(); } XrlCmdError XrlOlsr4Target::olsr4_0_1_set_refresh_interval(const uint32_t& interval) { debug_msg("olsr4_0_1_set_refresh_interval %u\n", XORP_UINT_CAST(interval)); if (! _olsr.set_refresh_interval(TimeVal(interval, 0))) return XrlCmdError::COMMAND_FAILED("Unable to set REFRESH_INTERVAL"); return XrlCmdError::OKAY(); } XrlCmdError XrlOlsr4Target::olsr4_0_1_get_refresh_interval(uint32_t& interval) { debug_msg("olsr4_0_1_get_refresh_interval\n"); interval = _olsr.get_refresh_interval().sec(); return XrlCmdError::OKAY(); } XrlCmdError XrlOlsr4Target::olsr4_0_1_set_tc_interval(const uint32_t& interval) { debug_msg("olsr4_0_1_set_tc_interval %u\n", XORP_UINT_CAST(interval)); if (! _olsr.set_tc_interval(TimeVal(interval, 0))) return XrlCmdError::COMMAND_FAILED("Unable to set TC_INTERVAL"); return XrlCmdError::OKAY(); } XrlCmdError XrlOlsr4Target::olsr4_0_1_get_tc_interval(uint32_t& interval) { debug_msg("olsr4_0_1_get_tc_interval\n"); interval = _olsr.get_tc_interval().sec(); return XrlCmdError::OKAY(); } XrlCmdError XrlOlsr4Target::olsr4_0_1_set_mid_interval(const uint32_t& interval) { debug_msg("olsr4_0_1_set_mid_interval %u\n", XORP_UINT_CAST(interval)); if (! _olsr.set_mid_interval(TimeVal(interval, 0))) return XrlCmdError::COMMAND_FAILED("Unable to set MID_INTERVAL"); return XrlCmdError::OKAY(); } XrlCmdError XrlOlsr4Target::olsr4_0_1_get_mid_interval(uint32_t& interval) { debug_msg("olsr4_0_1_get_mid_interval\n"); interval = _olsr.get_mid_interval().sec(); return XrlCmdError::OKAY(); } XrlCmdError XrlOlsr4Target::olsr4_0_1_set_hna_interval(const uint32_t& interval) { debug_msg("olsr4_0_1_set_hna_interval %u\n", XORP_UINT_CAST(interval)); if (! _olsr.set_hna_interval(TimeVal(interval, 0))) return XrlCmdError::COMMAND_FAILED("Unable to set HNA_INTERVAL"); return XrlCmdError::OKAY(); } XrlCmdError XrlOlsr4Target::olsr4_0_1_get_hna_interval(uint32_t& interval) { debug_msg("olsr4_0_1_get_hna_interval\n"); interval = _olsr.get_hna_interval().sec(); return XrlCmdError::OKAY(); } XrlCmdError XrlOlsr4Target::olsr4_0_1_set_dup_hold_time(const uint32_t& dup_hold_time) { debug_msg("olsr4_0_1_set_dup_hold_time %u\n", XORP_UINT_CAST(dup_hold_time)); if (! _olsr.set_dup_hold_time(TimeVal(dup_hold_time, 0))) return XrlCmdError::COMMAND_FAILED("Unable to set DUP_HOLD_TIME"); return XrlCmdError::OKAY(); } XrlCmdError XrlOlsr4Target::olsr4_0_1_get_dup_hold_time(uint32_t& dup_hold_time) { debug_msg("olsr4_0_1_set_dup_hold_time\n"); dup_hold_time = _olsr.get_dup_hold_time().sec(); return XrlCmdError::OKAY(); } XrlCmdError XrlOlsr4Target::olsr4_0_1_set_main_address(const IPv4& addr) { debug_msg("olsr4_0_1_set_main_address %s\n", cstring(addr)); if (! _olsr.set_main_addr(addr)) return XrlCmdError::COMMAND_FAILED("Unable to set main address"); return XrlCmdError::OKAY(); } XrlCmdError XrlOlsr4Target::olsr4_0_1_get_main_address(IPv4& addr) { debug_msg("olsr4_0_1_set_main_address\n"); addr = _olsr.get_main_addr(); return XrlCmdError::OKAY(); } XrlCmdError XrlOlsr4Target::olsr4_0_1_bind_address( const string& ifname, const string& vifname, const IPv4& local_addr, const uint32_t& local_port, const IPv4& all_nodes_addr, const uint32_t& all_nodes_port) { debug_msg("olsr4_0_1_bind_address %s/%s %s/%u %s/%u\n", ifname.c_str(), vifname.c_str(), cstring(local_addr), XORP_UINT_CAST(local_port), cstring(all_nodes_addr), XORP_UINT_CAST(all_nodes_port)); if (! _olsr.bind_address(ifname, vifname, local_addr, local_port, all_nodes_addr, all_nodes_port)) { return XrlCmdError::COMMAND_FAILED( c_format("Unable to bind to %s/%s %s/%u %s/%u\n", ifname.c_str(), vifname.c_str(), cstring(local_addr), XORP_UINT_CAST(local_port), cstring(all_nodes_addr), XORP_UINT_CAST(all_nodes_port))); } return XrlCmdError::OKAY(); } XrlCmdError XrlOlsr4Target::olsr4_0_1_unbind_address( const string& ifname, const string& vifname) { debug_msg("olsr4_0_1_unbind_address %s/%s\n", ifname.c_str(), vifname.c_str()); if (! _olsr.unbind_address(ifname, vifname)) { return XrlCmdError::COMMAND_FAILED( c_format("Unable to unbind from %s/%s", ifname.c_str(), vifname.c_str())); } return XrlCmdError::OKAY(); } XrlCmdError XrlOlsr4Target::olsr4_0_1_set_binding_enabled( const string& ifname, const string& vifname, const bool& enabled) { debug_msg("olsr4_0_1_set_binding_enabled %s/%s %s\n", ifname.c_str(), vifname.c_str(), bool_c_str(enabled)); if (! _olsr.set_interface_enabled(ifname, vifname, enabled)) { return XrlCmdError::COMMAND_FAILED( c_format("Unable to enable/disable binding on %s/%s", ifname.c_str(), vifname.c_str())); } return XrlCmdError::OKAY(); } XrlCmdError XrlOlsr4Target::olsr4_0_1_get_binding_enabled( const string& ifname, const string& vifname, bool& enabled) { debug_msg("olsr4_0_1_get_binding_enabled %s/%s\n", ifname.c_str(), vifname.c_str()); // XXX return XrlCmdError::OKAY(); UNUSED(ifname); UNUSED(vifname); UNUSED(enabled); } XrlCmdError XrlOlsr4Target::olsr4_0_1_change_local_addr_port( const string& ifname, const string& vifname, const IPv4& local_addr, const uint32_t& local_port) { debug_msg("olsr4_0_1_change_local_addr_port %s/%s %s/%u\n", ifname.c_str(), vifname.c_str(), cstring(local_addr), XORP_UINT_CAST(local_port)); // XXX XLOG_WARNING("OLSR does not yet support changing local address and " "port at runtime."); return XrlCmdError::OKAY(); UNUSED(ifname); UNUSED(vifname); UNUSED(local_addr); UNUSED(local_port); } XrlCmdError XrlOlsr4Target::olsr4_0_1_change_all_nodes_addr_port( const string& ifname, const string& vifname, const IPv4& all_nodes_addr, const uint32_t& all_nodes_port) { debug_msg("olsr4_0_1_change_all_nodes_addr_port %s/%s %s/%u\n", ifname.c_str(), vifname.c_str(), cstring(all_nodes_addr), XORP_UINT_CAST(all_nodes_port)); // XXX XLOG_WARNING("OLSR does not yet support changing remote address and " "port at runtime."); return XrlCmdError::OKAY(); UNUSED(ifname); UNUSED(vifname); UNUSED(all_nodes_addr); UNUSED(all_nodes_port); } XrlCmdError XrlOlsr4Target::olsr4_0_1_get_interface_list(XrlAtomList& interfaces) { debug_msg("olsr4_0_1_get_interface_list\n"); try { list face_list; _olsr.face_manager().get_face_list(face_list); list::const_iterator ii; for (ii = face_list.begin(); ii != face_list.end(); ii++) interfaces.append(XrlAtom(*ii)); return XrlCmdError::OKAY(); } catch (...) {} return XrlCmdError::COMMAND_FAILED("Unable to obtain interface list"); } XrlCmdError XrlOlsr4Target::olsr4_0_1_get_interface_info( const uint32_t& faceid, string& ifname, string& vifname, IPv4& local_addr, uint32_t& local_port, IPv4& all_nodes_addr, uint32_t& all_nodes_port) { debug_msg("olsr4_0_1_get_interface_info %u\n", XORP_UINT_CAST(faceid)); try { const Face* face = _olsr.face_manager().get_face_by_id(faceid); ifname = face->interface(); vifname = face->vif(); local_addr = face->local_addr(); local_port = face->local_port(); all_nodes_addr = face->all_nodes_addr(); all_nodes_port = face->all_nodes_port(); return XrlCmdError::OKAY(); } catch (...) {} return XrlCmdError::COMMAND_FAILED("Unable to get interface entry"); } XrlCmdError XrlOlsr4Target::olsr4_0_1_set_interface_cost( const string& ifname, const string& vifname, const uint32_t& cost) { debug_msg("olsr4_0_1_set_interface_cost %s/%s %u\n", ifname.c_str(), vifname.c_str(), XORP_UINT_CAST(cost)); if (! _olsr.set_interface_cost(ifname, vifname, cost)) return XrlCmdError::COMMAND_FAILED("Unable to set interface cost"); return XrlCmdError::OKAY(); } XrlCmdError XrlOlsr4Target::olsr4_0_1_get_interface_stats( const string& ifname, const string& vifname, uint32_t& bad_packets, uint32_t& bad_messages, uint32_t& messages_from_self, uint32_t& unknown_messages, uint32_t& duplicates, uint32_t& forwarded) { debug_msg("olsr4_0_1_get_interface_stats %s/%s\n", ifname.c_str(), vifname.c_str()); FaceCounters stats; if (! _olsr.get_interface_stats(ifname, vifname, stats)) { return XrlCmdError::COMMAND_FAILED( "Unable to get interface statistics"); } #define copy_stat(var) var = stats. var () copy_stat(bad_packets); copy_stat(bad_messages); copy_stat(messages_from_self); copy_stat(unknown_messages); copy_stat(duplicates); copy_stat(forwarded); #undef copy_stat return XrlCmdError::OKAY(); } XrlCmdError XrlOlsr4Target::olsr4_0_1_get_link_list(XrlAtomList& links) { debug_msg("olsr4_0_1_get_link_list\n"); try { list l1_list; _olsr.neighborhood().get_logical_link_list(l1_list); list::const_iterator ii; for (ii = l1_list.begin(); ii != l1_list.end(); ii++) links.append(XrlAtom(*ii)); return XrlCmdError::OKAY(); } catch (...) {} return XrlCmdError::COMMAND_FAILED("Unable to obtain link entry list"); } XrlCmdError XrlOlsr4Target::olsr4_0_1_get_link_info( const uint32_t& linkid, IPv4& local_addr, IPv4& remote_addr, IPv4& main_addr, uint32_t& link_type, uint32_t& sym_time, uint32_t& asym_time, uint32_t& hold_time) { debug_msg("olsr4_0_1_get_link_info %u\n", XORP_UINT_CAST(linkid)); try { const LogicalLink* l1 = _olsr.neighborhood().get_logical_link(linkid); local_addr = l1->local_addr(); remote_addr = l1->remote_addr(); main_addr = l1->destination()->main_addr(); link_type = l1->link_type(); sym_time = l1->sym_time_remaining().sec(); asym_time = l1->asym_time_remaining().sec(); hold_time = l1->time_remaining().sec(); return XrlCmdError::OKAY(); } catch (...) {} return XrlCmdError::COMMAND_FAILED("Unable to get link entry"); } XrlCmdError XrlOlsr4Target::olsr4_0_1_get_neighbor_list(XrlAtomList& neighbors) { debug_msg("olsr4_0_1_get_neighbor_list\n"); try { list n1_list; _olsr.neighborhood().get_neighbor_list(n1_list); list::const_iterator ii; for (ii = n1_list.begin(); ii != n1_list.end(); ii++) neighbors.append(XrlAtom(*ii)); return XrlCmdError::OKAY(); } catch (...) {} return XrlCmdError::COMMAND_FAILED("Unable to obtain neighbor entry list"); } XrlCmdError XrlOlsr4Target::olsr4_0_1_get_neighbor_info( const uint32_t& nid, IPv4& main_addr, uint32_t& willingness, uint32_t& degree, uint32_t& link_count, uint32_t& twohop_link_count, bool& is_advertised, bool& is_sym, bool& is_mpr, bool& is_mpr_selector) { debug_msg("olsr4_0_1_get_neighbor_info %u\n", XORP_UINT_CAST(nid)); try { const Neighbor* n1 = _olsr.neighborhood().get_neighbor(nid); main_addr = n1->main_addr(); willingness = n1->willingness(); degree = n1->degree(); link_count = n1->links().size(); twohop_link_count = n1->twohop_links().size(); is_advertised = n1->is_advertised(); is_sym = n1->is_sym(); is_mpr = n1->is_mpr(); is_mpr_selector = n1->is_mpr_selector(); return XrlCmdError::OKAY(); } catch (...) {} return XrlCmdError::COMMAND_FAILED("Unable to get neighbor entry"); } XrlCmdError XrlOlsr4Target::olsr4_0_1_get_twohop_link_list(XrlAtomList& twohop_links) { debug_msg("olsr4_0_1_get_twohop_link_list\n"); try { list l2_list; _olsr.neighborhood().get_twohop_link_list(l2_list); list::const_iterator ii; for (ii = l2_list.begin(); ii != l2_list.end(); ii++) twohop_links.append(XrlAtom(*ii)); return XrlCmdError::OKAY(); } catch (...) {} return XrlCmdError::COMMAND_FAILED("Unable to obtain two-hop link list"); } XrlCmdError XrlOlsr4Target::olsr4_0_1_get_twohop_link_info( const uint32_t& tlid, uint32_t& last_face_id, IPv4& nexthop_addr, IPv4& dest_addr, uint32_t& hold_time) { debug_msg("olsr4_0_1_get_twohop_link_info %u\n", XORP_UINT_CAST(tlid)); try { const TwoHopLink* l2 = _olsr.neighborhood().get_twohop_link(tlid); // We don't convert the face ID to a name in the // XRL, perhaps we should, this might change. last_face_id = l2->face_id(); nexthop_addr = l2->nexthop()->main_addr(); dest_addr = l2->destination()->main_addr(); hold_time = l2->time_remaining().sec(); return XrlCmdError::OKAY(); } catch (...) {} return XrlCmdError::COMMAND_FAILED("Unable to get two-hop link entry"); } XrlCmdError XrlOlsr4Target::olsr4_0_1_get_twohop_neighbor_list( XrlAtomList& twohop_neighbors) { debug_msg("olsr4_0_1_get_twohop_neighbor_list\n"); try { list n2_list; _olsr.neighborhood().get_twohop_link_list(n2_list); list::const_iterator ii; for (ii = n2_list.begin(); ii != n2_list.end(); ii++) twohop_neighbors.append(XrlAtom(*ii)); return XrlCmdError::OKAY(); } catch (...) {} return XrlCmdError:: COMMAND_FAILED("Unable to obtain two-hop neighbor list"); } XrlCmdError XrlOlsr4Target::olsr4_0_1_get_twohop_neighbor_info( const uint32_t& tnid, IPv4& main_addr, bool& is_strict, uint32_t& link_count, uint32_t& reachability, uint32_t& coverage) { debug_msg("olsr4_0_1_get_twohop_neighbor_info %u\n", XORP_UINT_CAST(tnid)); try { const TwoHopNeighbor* n2 = _olsr.neighborhood().get_twohop_neighbor(tnid); main_addr = n2->main_addr(); is_strict = n2->is_strict(); link_count = n2->twohop_links().size(); reachability = n2->reachability(); coverage = n2->coverage(); return XrlCmdError::OKAY(); } catch (...) {} return XrlCmdError::COMMAND_FAILED("Unable to get two-hop neighbor entry"); } XrlCmdError XrlOlsr4Target::olsr4_0_1_get_mid_entry_list(XrlAtomList& mid_entries) { debug_msg("olsr4_0_1_get_mid_entry_list\n"); try { list midlist; _olsr.topology_manager().get_mid_list(midlist); list::const_iterator ii; for (ii = midlist.begin(); ii != midlist.end(); ii++) mid_entries.append(XrlAtom(*ii)); return XrlCmdError::OKAY(); } catch (...) {} return XrlCmdError::COMMAND_FAILED("Unable to obtain MID entry list"); } XrlCmdError XrlOlsr4Target::olsr4_0_1_get_mid_entry( const uint32_t& midid, IPv4& main_addr, IPv4& iface_addr, uint32_t& distance, uint32_t& hold_time) { debug_msg("olsr4_0_1_get_mid_entry %u\n", XORP_UINT_CAST(midid)); try { const MidEntry* mid = _olsr.topology_manager().get_mid_entry_by_id(midid); main_addr = mid->main_addr(); iface_addr = mid->iface_addr(); distance = mid->distance(); hold_time = mid->time_remaining().sec(); return XrlCmdError::OKAY(); } catch (...) {} return XrlCmdError::COMMAND_FAILED("Unable to get MID entry"); } XrlCmdError XrlOlsr4Target::olsr4_0_1_get_tc_entry_list(XrlAtomList& tc_entries) { debug_msg("olsr4_0_1_get_tc_entry_list\n"); try { list tclist; _olsr.topology_manager().get_topology_list(tclist); list::const_iterator ii; for (ii = tclist.begin(); ii != tclist.end(); ii++) tc_entries.append(XrlAtom(*ii)); return XrlCmdError::OKAY(); } catch (...) {} return XrlCmdError::COMMAND_FAILED("Unable to obtain TC entry list"); } XrlCmdError XrlOlsr4Target::olsr4_0_1_get_tc_entry( const uint32_t& tcid, IPv4& destination, IPv4& lasthop, uint32_t& distance, uint32_t& seqno, uint32_t& hold_time) { debug_msg("olsr4_0_1_get_tc_entry %u\n", XORP_UINT_CAST(tcid)); try { const TopologyEntry* tc = _olsr.topology_manager().get_topology_entry_by_id(tcid); destination = tc->destination(); lasthop = tc->lasthop(); distance = tc->distance(); seqno = tc->seqno(); hold_time = tc->time_remaining().sec(); return XrlCmdError::OKAY(); } catch (...) {} return XrlCmdError::COMMAND_FAILED("Unable to get TC entry"); } XrlCmdError XrlOlsr4Target::olsr4_0_1_get_hna_entry_list(XrlAtomList& hna_entries) { debug_msg("olsr4_0_1_get_hna_entry_list\n"); try { list hnalist; _olsr.external_routes().get_hna_route_in_list(hnalist); list::const_iterator ii; for (ii = hnalist.begin(); ii != hnalist.end(); ii++) hna_entries.append(XrlAtom(*ii)); return XrlCmdError::OKAY(); } catch (...) {} return XrlCmdError::COMMAND_FAILED("Unable to obtain HNA entry list"); } XrlCmdError XrlOlsr4Target::olsr4_0_1_get_hna_entry( const uint32_t& hnaid, IPv4Net& destination, IPv4& lasthop, uint32_t& distance, uint32_t& hold_time) { debug_msg("olsr4_0_1_get_hna_entry %u\n", XORP_UINT_CAST(hnaid)); try { const ExternalRoute* er = _olsr.external_routes().get_hna_route_in_by_id(hnaid); destination = er->dest(); lasthop = er->lasthop(); distance = er->distance(); hold_time = er->time_remaining().sec(); return XrlCmdError::OKAY(); } catch (...) {} return XrlCmdError::COMMAND_FAILED("Unable to get HNA entry"); } xorp/contrib/olsr/vertex.hh0000664000076400007640000001701611421137511016136 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8 sw=4: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/contrib/olsr/vertex.hh,v 1.3 2008/10/02 21:56:36 bms Exp $ #ifndef __OLSR_VERTEX_HH__ #define __OLSR_VERTEX_HH__ /** * @short A vertex in the shortest path tree. */ class Vertex { public: Vertex(); explicit Vertex(const Neighbor& n); explicit Vertex(const TwoHopNeighbor& n2); explicit Vertex(const IPv4& main_addr); explicit Vertex(const TopologyEntry& tc); /** * Compare two Vertex instances for less-than ordering. * * Collation order for sort: * 1. Main address; always unique. * * XXX The following two can't be relied upon... because differentiating * them at all means we can't look up nodes purely by their address! * 2. Is origin vertex: true, false. * 3. Vertex type: origin, onehop, twohop, tc. * * @param other node to compare with. * @return true if this node comes before the other node. */ inline bool operator<(const Vertex& other) const { #if 0 if (_main_addr == other.main_addr()) { if (_is_origin == other.is_origin()) return _t < other.type(); return _is_origin > other.is_origin(); } #endif return _main_addr < other.main_addr(); } /** * Compare two Vertex instances for equality. * * Comparison is performed solely on the main address of the node, * which is always unique. * * @param other node to compare with. * @return true if this node and the other node are equal. */ inline bool operator==(const Vertex& other) const { return _main_addr == other.main_addr(); } inline void set_main_addr(const IPv4& main_addr) { _main_addr = main_addr; } inline IPv4 main_addr() const { return _main_addr; } void set_nodeid(uint32_t nid) { _nodeid = nid; } uint32_t nodeid() const { return _nodeid; } void set_is_origin(bool v) { _is_origin = v; } /** * @return true if this node represents this router. */ bool is_origin() const { return _is_origin; } void set_producer(const IPv4& producer) { _producer = producer; } IPv4 producer() const { return _producer; } void set_faceid(const OlsrTypes::FaceID fid) { _faceid = fid; } OlsrTypes::FaceID faceid() const { return _faceid; } /** * @return the LogicalLink associated with a one-hop neighbor. */ const LogicalLink* link() const { return _link; } void set_link(const LogicalLink* l) { XLOG_ASSERT(_t == OlsrTypes::VT_NEIGHBOR); _link = l; } /** * @return the TwoHopLink associated with a two-hop neighbor. */ const TwoHopLink* twohop_link() const { return _twohop_link; } void set_twohop_link(const TwoHopLink* l2) { XLOG_ASSERT(_t == OlsrTypes::VT_TWOHOP); _twohop_link = l2; } void set_type(const OlsrTypes::VertexType t) { _t = t; } OlsrTypes::VertexType type() const { return _t; } string str() const { string output = "OLSR"; output += c_format(" Node %s", cstring(_main_addr)); output += c_format(" Type %u", XORP_UINT_CAST(type())); output += c_format(" ID %u", XORP_UINT_CAST(nodeid())); return output; } private: /** * @short true if this Vertex represents this OLSR router. */ bool _is_origin; /** * The type of node in the OLSR link state databases from which * this Vertex is derived. */ OlsrTypes::VertexType _t; /** * ID of the Neighbor, TwoHopNeighbor or TopologyEntry responsible * for the creation of this vertex. */ uint32_t _nodeid; /** * The main protocol address of the OLSR node is used as the unique * node identifier in the shortest-path tree. */ IPv4 _main_addr; /** * The main protocol address of the OLSR node from where this vertex * was learned. In the routing CLI this is referred to as the origin; * it is NOT the origin of the SPT graph. */ IPv4 _producer; // XXX: union candidate. /** * If the OLSR node which this vertex represents is directly * connected to the origin, this is the FaceID which should be * used to reach the next hop. */ OlsrTypes::FaceID _faceid; /** * If this is a one-hop neighbor vertex, this is the link which * was chosen to reach the neighbor. */ const LogicalLink* _link; /** * If this is a two-hop neighbor vertex, this is the link which * was chosen to reach it. */ const TwoHopLink* _twohop_link; }; /** * Default constructor for Vertex. */ inline Vertex::Vertex() : _is_origin(false), _t(OlsrTypes::VT_UNKNOWN), _nodeid(OlsrTypes::UNUSED_NEIGHBOR_ID), _faceid(OlsrTypes::UNUSED_FACE_ID) { } // There are several explicit constructors for Vertex. These are used // to populate Vertex with the information which we will need when // plumbing routes to the policy engine for redistribution, after the // SPT computation. /** * Explicit constructor to: * Create a Vertex from a Neighbor. * * A neighbor vertex is learned from the neighbor itself, so mark it * as the producer. * * @param n the Neighbor represented by this Vertex. */ inline Vertex::Vertex(const Neighbor& n) : _is_origin(false), _t(OlsrTypes::VT_NEIGHBOR), _nodeid(n.id()), _main_addr(n.main_addr()), _producer(n.main_addr()), _faceid(OlsrTypes::UNUSED_FACE_ID) { } /** * Explicit constructor to: * Create a Vertex from a TwoHopNeighbor. * * The producer of a two-hop vertex is the one-hop neighbor which * advertised the link we choose to reach it, which we set after * creation using set_producer(). * * The face ID is not known until the SPT calculation has run and * paths have been chosen; it will be that of the path selected at * radius = 1. * * @param n2 the TwoHopNeighbor represented by this Vertex. */ inline Vertex::Vertex(const TwoHopNeighbor& n2) : _is_origin(false), _t(OlsrTypes::VT_TWOHOP), _nodeid(n2.id()), _main_addr(n2.main_addr()), _faceid(OlsrTypes::UNUSED_FACE_ID) { } /** * Explicit constructor to: * Create a Vertex from a protocol address. * * Typically used for sanity checking when populating the SPT from * OLSR's TC database. */ inline Vertex::Vertex(const IPv4& main_addr) : _is_origin(false), _t(OlsrTypes::VT_TOPOLOGY), _main_addr(main_addr) { } /** * Explicit constructor to: * Create a Vertex from a topology control tuple. * * The producer of a TC vertex is the OLSR node which advertised * the TC entry from which the vertex has been created. */ inline Vertex::Vertex(const TopologyEntry& tc) : _is_origin(false), _t(OlsrTypes::VT_TOPOLOGY), _nodeid(tc.id()), _main_addr(tc.destination()), _producer(tc.lasthop()) { } #endif // __OLSR_VERTEX_HH__ xorp/contrib/olsr/test_olsr_types.cc0000664000076400007640000001252311421137511020047 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8 sw=4: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "olsr_module.h" #include "libxorp/xorp.h" #include "libxorp/test_main.hh" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/exceptions.hh" #include "libxorp/timeval.hh" #include "libxorp/status_codes.h" #include "libxorp/service.hh" #include "libxorp/eventloop.hh" #include "libxorp/tokenize.hh" #include "olsr_types.hh" // // Test EightBitTime::to_timeval() against values specified in RFC. // bool eightbittime_totv_test(TestInfo& info) { struct dtuple { uint8_t htime; double dtime; }; struct dtuple dtuples[] = { { 0x05, 2.0 }, { 0x86, 6.0 }, { 0xe7, 15.0 }, { 0xe8, 30.0 } }; for (int i = 0; i < 4; i++) { DOUT_LEVEL(info, 2) << c_format("htime is %02x", (int)dtuples[i].htime) << endl; TimeVal tv1 = EightBitTime::to_timeval(dtuples[i].htime); DOUT_LEVEL(info, 2) << "tv1 is " << tv1.str() << endl; XLOG_ASSERT(tv1.get_double() == dtuples[i].dtime); } return true; } // // Test EightBitTime::from_timeval() against values specified in RFC. // bool eightbittime_fromtv_test(TestInfo& info) { struct dtuple { uint8_t htime; double dtime; }; struct dtuple dtuples[] = { { 0x05, 2.0 }, { 0x86, 6.0 }, { 0xe7, 15.0 }, { 0xe8, 30.0 } }; for (int i = 0; i < 4; i++) { DOUT_LEVEL(info, 2) << c_format("dtime is %.4f", dtuples[i].dtime) << endl; TimeVal tv1(dtuples[i].dtime); uint8_t et1 = EightBitTime::from_timeval(tv1); DOUT_LEVEL(info, 2) << c_format("htime is %02x", et1) << endl; XLOG_ASSERT(et1 == dtuples[i].htime); } return true; } // // Test EightBitTime::from_timeval() and EightBitTime::to_timeval() // together with an expected range of values. // bool eightbittime_roundtrip_test(TestInfo& info) { // // Test entire range of values which EightBitTime can represent; // that is, 4 bits of mantissa, and 4 bits of exponent. // const double min_time = 0.0625; const double max_time = 3840.0; const double increment = 0.0625f; // 1/16th of a second for (double dtime = 0.0f; dtime < max_time; dtime += increment) { TimeVal tv1(dtime); DOUT_LEVEL(info, 2) << c_format("dtime is %.4f", dtime) << endl; uint8_t et1 = EightBitTime::from_timeval(tv1); DOUT_LEVEL(info, 2) << c_format("et1 is %02x", (int)et1) << endl; TimeVal tv2 = EightBitTime::to_timeval(et1); DOUT_LEVEL(info, 2) << "tv2 is " << tv2.str() << endl; double finaldtime = tv2.get_double(); DOUT_LEVEL(info, 2) << c_format("finaldtime is %.4f", finaldtime) << endl; XLOG_ASSERT(finaldtime >= min_time); XLOG_ASSERT(finaldtime <= max_time); // integer parts must match up to 32, then they diverge if (dtime <= 32.0) XLOG_ASSERT((int)finaldtime == (int)dtime); } return true; } bool seqno_test(TestInfo& info) { struct seqno_tuple { uint16_t s1; uint16_t s2; bool expected_result; } sv[] = { { 65535, 2, false }, { 2, 65535, true }, { 32767, 65535, true }, { 65535, 32767, false }, { 33791, 1024, true }, { 33791, 1023, false } }; for (size_t i = 0; i < (sizeof(sv)/sizeof(struct seqno_tuple)); i++) { bool result = is_seq_newer(sv[i].s1, sv[i].s2); DOUT_LEVEL(info, 2) << c_format("is_seq_newer(%u, %u) is %s\n", sv[i].s1, sv[i].s2, bool_c_str(result)); XLOG_ASSERT(result == sv[i].expected_result); } return true; UNUSED(info); } int main(int argc, char **argv) { XorpUnexpectedHandler x(xorp_unexpected_handler); xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); TestMain t(argc, argv); string test = t.get_optional_args("-t", "--test", "run only the specified test"); t.complete_args_parsing(); struct test { string test_name; XorpCallback1::RefPtr cb; } tests[] = { {"eightbittime_totv", callback(eightbittime_totv_test)}, {"eightbittime_fromtv", callback(eightbittime_fromtv_test)}, {"eightbittime_roundtrip", callback(eightbittime_roundtrip_test)}, {"seqno", callback(seqno_test)} }; try { if (test.empty()) { for (size_t i = 0; i < sizeof(tests) / sizeof(struct test); i++) t.run(tests[i].test_name, tests[i].cb); } else { for (size_t i = 0; i < sizeof(tests) / sizeof(struct test); i++) if (test == tests[i].test_name) { t.run(tests[i].test_name, tests[i].cb); return t.exit(); } t.failed("No test with name " + test + " found\n"); } } catch(...) { xorp_catch_standard_exceptions(); } xlog_stop(); xlog_exit(); return t.exit(); } xorp/contrib/olsr/emulate_net.hh0000664000076400007640000001240711421137511017122 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8 sw=4: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/contrib/olsr/emulate_net.hh,v 1.3 2008/10/02 21:56:34 bms Exp $ #ifndef __OLSR_EMULATE_NET_HH__ #define __OLSR_EMULATE_NET_HH__ #include /** * Bind together a set of IO classes in order to form a virtual subnet * for testing, one instance per subnet. */ class EmulateSubnet { public: EmulateSubnet(TestInfo& info, EventLoop& eventloop); virtual ~EmulateSubnet(); /** * Receive frames * * All frames generated by an OLSR instance arrive here. Note * that a frame arriving from one OLSR instance is not sent * directly to another. The frames are queued and only when OLSR * instance gives back control to the eventloop are the frames * forwarded. This ensures that two OLSR instances are not in each * others call graphs, which can cause re-entrancy problems. */ void receive_frames(const string& interface, const string& vif, IPv4 dst, uint16_t dport, IPv4 src, uint16_t sport, uint8_t* data, uint32_t len, const string instance); /** * Bind an interface to the emulated subnet. */ virtual void bind_interface(const string& instance, const string& interface, const string& vif, const IPv4& listen_addr, const uint16_t listen_port, DebugIO& io); /** * Unbind an interface from the emulated subnet. */ void unbind_interface(const string& instance, const string& interface, const string& vif, const IPv4& listen_addr, const uint16_t listen_port, DebugIO& io); inline void set_all_nodes_addr(IPv4 all_nodes_addr) { _all_nodes_addr = all_nodes_addr; } protected: class Multiplex { public: Multiplex(const string& instance, const string& interface, const string& vif, IPv4 listen_addr, uint16_t listen_port); inline bool operator <(const Multiplex& him) const { return him._instance < _instance; } const string _instance; const string _interface; const string _vif; const IPv4 _listen_addr; const uint16_t _listen_port; }; class Frame { public: Frame(const string& interface, const string& vif, IPv4 dst, uint16_t dport, IPv4 src, uint16_t sport, uint8_t* data, uint32_t len, string instance); // for use by EmulateSubnetHops inline vector& get_buffer() { return _pkt; } string _interface; // uniquely identifies link string _vif; IPv4 _dst; // IP header fields uint16_t _dport; IPv4 _src; uint16_t _sport; vector _pkt; // payload string _instance; }; protected: void next(); virtual void forward(Frame frame); protected: TestInfo& _info; EventLoop& _eventloop; map _ios; XorpTimer _timer; deque _queue[2]; int _queue_add; int _queue_remove; IPv4 _all_nodes_addr; }; /** * Bind together a set of IO classes like EmulateSubnet, however, * in this case, any OLSR protocol messages which flow through this * entity have their hopcount and TTL decremented by a given integer. * * TODO: Simulate ETX-measurable loss on link. For this we need * only drop HELLO messages to create a situation where the measured ETX * for the link will increase. */ class EmulateSubnetHops : public EmulateSubnet { public: EmulateSubnetHops(TestInfo& info, EventLoop& eventloop, uint8_t hopdelta = 1, uint8_t maxlinks = 2); virtual ~EmulateSubnetHops(); inline uint8_t hopdelta() const { return _hopdelta; } inline size_t empty_pkt_drops() const { return _empty_pkt_drops; } inline size_t ttl_msg_drops() const { return _ttl_msg_drops; } /** * When used to simulate a cloud, enforce any limit on the number * of links connected to that cloud by using a run-time assertion. */ void bind_interface(const string& instance, const string& interface, const string& vif, const IPv4& listen_addr, const uint16_t listen_port, DebugIO& io); protected: /** * Forward the given OLSR frame, however, decrement the hop counts * in the Packet and in any contained Messages. * Note that duplicate packet detection is NOT performed; it is always * assumed that the given frame will be forwarded in either direction. */ void forward(Frame frame); private: MessageDecoder _md; uint8_t _hopdelta; uint8_t _maxlinks; size_t _empty_pkt_drops; size_t _ttl_msg_drops; }; #endif // __OLSR_EMULATE_NET_HH__ xorp/contrib/olsr/xrl_port.cc0000664000076400007640000001453311633743677016506 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8 sw=4: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #include "olsr_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/callback.hh" #include "libxorp/ipv4.hh" #include "libxorp/ipv4net.hh" #include "libxorp/status_codes.h" #include "libxorp/service.hh" #include "libxorp/eventloop.hh" #include "libxipc/xrl_router.hh" #include "xrl/interfaces/socket4_xif.hh" #include "olsr.hh" #include "io.hh" #include "xrl_port.hh" #include "xrl_io.hh" // TODO: Support IPv4 multicast for OLSR control traffic. // TODO: Support IPv6 multicast for OLSR control traffic. // TODO: Use definition from fea/ip.h which is spelled // differently but is always available ie IP_TOS_PREC_INTERNETCONTROL. #ifndef IPTOS_PREC_INTERNETCONTROL #define IPTOS_PREC_INTERNETCONTROL 0xc0 #endif /* !defined(IPTOS_PREC_INTERNETCONTROL) */ XrlPort::XrlPort( IO* io, EventLoop& eventloop, XrlRouter& xrl_router, const string& ssname, const string& ifname, const string& vifname, const IPv4& local_addr, const uint16_t local_port, const IPv4& remote_addr) : ServiceBase("OlsrXrlPort"), _io(io), _eventloop(eventloop), _xrl_router(xrl_router), _ss(ssname), _ifname(ifname), _vifname(vifname), _local_addr(local_addr), _local_port(local_port), _pending(false), _is_undirected_broadcast(false) { if (remote_addr == IPv4::ALL_ONES()) _is_undirected_broadcast = true; } XrlPort::~XrlPort() { } int XrlPort::startup() { debug_msg("%p: startup()\n", this); _pending = true; set_status(SERVICE_STARTING); if (startup_socket() == false) { set_status(SERVICE_FAILED, "Failed to find appropriate socket server."); return XORP_ERROR; } return XORP_OK; } int XrlPort::shutdown() { debug_msg("%p: shutdown()\n", this); _pending = true; set_status(SERVICE_SHUTTING_DOWN); if (request_close() == true) { set_status(SERVICE_SHUTDOWN); } return XORP_OK; } bool XrlPort::startup_socket() { if (! request_udp_open_bind_broadcast()) { set_status(SERVICE_FAILED, "Failed sending UDP broadcast socket open request."); return false; } return true; } bool XrlPort::request_udp_open_bind_broadcast() { XrlSocket4V0p1Client cl(&_xrl_router); // XXX Assumes that local and remote port are the same. We aren't // passed in a remote port because we currently assume we need to // use sendto() to transmit, although the special cases for // BSD undirected broadcast have now been moved into the FEA. debug_msg("%p: sending udp_open_bind_broadcast\n", this); return cl.send_udp_open_bind_broadcast(_ss.c_str(), _xrl_router.instance_name(), _ifname, _vifname, _local_port, _local_port, // XXX true, // reuse port _is_undirected_broadcast, false, // don't connect() callback(this, &XrlPort::udp_open_bind_broadcast_cb)); } void XrlPort::udp_open_bind_broadcast_cb(const XrlError& e, const string* psid) { if (e != XrlError::OKAY()) { set_status(SERVICE_FAILED, "Failed to open a UDP socket."); return; } _sockid = *psid; if (request_tos() == false) { set_status(SERVICE_FAILED, "Failed to set IP TOS bits."); } } bool XrlPort::request_tos() { XrlSocket4V0p1Client cl(&_xrl_router); debug_msg("%p: sending tos IPTOS_PREC_INTERNETCONTROL\n", this); return cl.send_set_socket_option( _ss.c_str(), _sockid, "tos", IPTOS_PREC_INTERNETCONTROL, callback(this, &XrlPort::tos_cb)); } void XrlPort::tos_cb(const XrlError& e) { if (e != XrlError::OKAY()) { XLOG_WARNING("Failed to set TOS."); return; } socket_setup_complete(); } void XrlPort::socket_setup_complete() { _pending = false; debug_msg("%p: socket setup complete\n", this); set_status(SERVICE_RUNNING); //this->set_enabled(true); } // // Close operations. // bool XrlPort::request_close() { XrlSocket4V0p1Client cl(&_xrl_router); debug_msg("%p: sending close\n", this); bool success = cl.send_close(_ss.c_str(), _sockid, callback(this, &XrlPort::close_cb)); if (success) _pending = true; return success; } void XrlPort::close_cb(const XrlError& e) { if (e != XrlError::OKAY()) { set_status(SERVICE_FAILED, "Failed to close UDP socket."); } _pending = false; debug_msg("%p: socket shutdown complete\n", this); set_status(SERVICE_SHUTDOWN); //this->set_enabled(false); } // // Send operations. // bool XrlPort::send_to(const IPv4& dst_addr, const uint16_t dst_port, const vector& payload) { if (_pending) { XLOG_WARNING("Port %p: send skipped (pending XRL)\n", this); return false; } XrlSocket4V0p1Client cl(&_xrl_router); bool success = cl.send_send_to(_ss.c_str(), _sockid, dst_addr, dst_port, payload, callback(this, &XrlPort::send_cb)); debug_msg("Sent %u bytes to %s:%u from %s:%u\n", XORP_UINT_CAST(payload.size()), cstring(dst_addr), XORP_UINT_CAST(dst_port), cstring(_local_addr), XORP_UINT_CAST(_local_port)); return success; } // TODO: Add an I/O completion notification. void XrlPort::send_cb(const XrlError& e) { debug_msg("SendCB %s\n", e.str().c_str()); if (e != XrlError::OKAY()) { XLOG_WARNING("Failed to send datagram."); } //this->_user.port_io_send_completion(xe == XrlError::OKAY()); } xorp/contrib/olsr/olsr.hh0000664000076400007640000004066211421137511015603 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8 sw=4: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/contrib/olsr/olsr.hh,v 1.3 2008/10/02 21:56:35 bms Exp $ #ifndef __OLSR_OLSR_HH__ #define __OLSR_OLSR_HH__ #include "olsr_types.hh" #include "exceptions.hh" #include "policy_varrw.hh" #include "io.hh" #include "message.hh" #include "link.hh" #include "neighbor.hh" #include "twohop.hh" #include "face.hh" #include "face_manager.hh" #include "neighborhood.hh" #include "topology.hh" #include "external.hh" #include "libxorp/trie.hh" #include "libproto/spt.hh" #include "vertex.hh" #include "route_manager.hh" #include "trace.hh" #include "libxorp/profile.hh" #include "profile_vars.hh" /** * @short An OLSR routing process. * * IO is performed by the associated class IO. */ class Olsr { public: Olsr(EventLoop& eventloop, IO* io); /** * @return true if this routing process is running. */ inline bool running() { return _io->status() != SERVICE_SHUTDOWN; } /** * Determine routing process status. * * @param reason a text description of the current process status. * @return status of the routing process. */ ProcessStatus status(string& reason); /** * Shut down this routing process. */ void shutdown(); /** * Callback method to: receive a datagram. * * All packets for OLSR are received through this interface. All good * packets are sent to the face manager which verifies that the packet * is expected, and is wholly responsible for further processing. */ void receive(const string& interface, const string& vif, IPv4 dst, uint16_t dport, IPv4 src, uint16_t sport, uint8_t* data, uint32_t len); /** * Transmit a datagram. */ bool transmit(const string& interface, const string& vif, const IPv4& dst, const uint16_t& dport, const IPv4& src, const uint16_t& sport, uint8_t* data, const uint32_t& len); /** * Register vif status callback with the FEA. * * @param cb the callback to register. */ inline void register_vif_status(IO::VifStatusCb cb) { _io->register_vif_status(cb); } /** * Register address status callback with the FEA. * * @param cb the callback to register. */ inline void register_address_status(IO::AddressStatusCb cb) { _io->register_address_status(cb); } inline EventLoop& get_eventloop() { return _eventloop; } inline FaceManager& face_manager() { return _fm; } inline Neighborhood& neighborhood() { return _nh; } inline TopologyManager& topology_manager() { return _tm; } inline ExternalRoutes& external_routes() { return _er; } inline RouteManager& route_manager() { return _rm; } /** * Set the main address for the node. * * @param addr the main address to set. * @return true if the main address was set successfully. */ inline bool set_main_addr(const IPv4& addr) { return face_manager().set_main_addr(addr); } /** * @return the main address. */ inline IPv4 get_main_addr() { return face_manager().get_main_addr(); } /** * Set the MPR_COVERAGE for the node. * * @param mpr_coverage the new value of MPR_COVERAGE. * @return true if MPR_COVERAGE was set successfully. */ inline bool set_mpr_coverage(const uint32_t mpr_coverage) { neighborhood().set_mpr_coverage(mpr_coverage); return true; } /** * @return the value of the MPR_COVERAGE variable. */ inline uint32_t get_mpr_coverage() { return neighborhood().mpr_coverage(); } /** * Set the TC_REDUNDANCY for the node. * * @param tc_redundancy the new value of TC_REDUNDANCY. * @return true if TC_REDUNDANCY was set successfully. */ inline bool set_tc_redundancy(const uint32_t tc_redundancy) { neighborhood().set_tc_redundancy(tc_redundancy); return true; } /** * @return the value of the TC_REDUNDANCY variable. */ inline uint32_t get_tc_redundancy() { return neighborhood().get_tc_redundancy(); } /** * Set the HELLO_INTERVAL. * * @param interval the new value of HELLO_INTERVAL. * @return true if HELLO_INTERVAL was set successfully. */ inline bool set_hello_interval(const TimeVal& interval) { face_manager().set_hello_interval(interval); return true; } /** * @return the value of the HELLO_INTERVAL variable. */ inline TimeVal get_hello_interval() { return face_manager().get_hello_interval(); } /** * Set the MID_INTERVAL. * * @param interval the new value of MID_INTERVAL. * @return true if MID_INTERVAL was set successfully. */ inline bool set_mid_interval(const TimeVal& interval) { face_manager().set_mid_interval(interval); return true; } /** * @return the value of the MID_INTERVAL variable. */ inline TimeVal get_mid_interval() { return face_manager().get_mid_interval(); } /** * Set the REFRESH_INTERVAL. * * @param interval the new refresh interval. * @return true if the refresh interval was set successfully. */ inline bool set_refresh_interval(const TimeVal& interval) { neighborhood().set_refresh_interval(interval); return true; } /** * @return the value of the REFRESH_INTERVAL protocol variable. */ inline TimeVal get_refresh_interval() { return neighborhood().get_refresh_interval(); } /** * Set the TC interval for the node. * * @param interval the new TC interval. * @return true if the TC interval was set successfully. */ inline bool set_tc_interval(TimeVal interval) { neighborhood().set_tc_interval(interval); return true; } /** * @return the value of the TC_INTERVAL protocol variable. */ inline TimeVal get_tc_interval() { return neighborhood().get_tc_interval(); } /** * Set the WILLINGNESS. * * @param willingness the new willingness-to-forward. * @return true if the willingness was set successfully. */ inline bool set_willingness(const OlsrTypes::WillType willingness) { neighborhood().set_willingness(willingness); return true; } /** * @return the WILLINGNESS protocol variable. */ inline OlsrTypes::WillType get_willingness() { return neighborhood().willingness(); } /** * Set the HNA interval. * * @param interval the new HNA interval. * @return true if the HNA interval was set successfully. */ inline bool set_hna_interval(const TimeVal& interval) { external_routes().set_hna_interval(interval); return true; } /** * @return the value of the HNA_INTERVAL protocol variable. */ inline TimeVal get_hna_interval() { return external_routes().get_hna_interval(); } /** * Set the DUP_HOLD_TIME. * * @param interval the new duplicate message hold time * @return true if the DUP_HOLD_TIME was set successfully. */ inline bool set_dup_hold_time(const TimeVal& interval) { face_manager().set_dup_hold_time(interval); return true; } /** * @return the value of the DUP_HOLD_TIME protocol variable. */ inline TimeVal get_dup_hold_time() { return face_manager().get_dup_hold_time(); } /** * Bind OLSR to an interface. * * @param interface the name of the interface to listen on. * @param vif the name of the vif to listen on. * @param local_addr the address to listen on. * @param local_port the address on the interface to listen on. * @param all_nodes_addr the address to transmit to. * @param all_nodes_port the port to transmit to. * @return true if the address was bound successfully. */ bool bind_address(const string& interface, const string& vif, const IPv4& local_addr, const uint32_t& local_port, const IPv4& all_nodes_addr, const uint32_t& all_nodes_port); /** * Unbind OLSR from an interface. * * OLSR may only be bound to a single address on an interface, * therefore there is no 'address' argument to this method. * * @param interface the name of the interface to unbind. * @param vif the name of the vif to unbind. * @return true if the interface was unbound successfully. */ bool unbind_address(const string& interface, const string& vif); /** * Enable I/O for OLSR on a given address. * * Proxy for class IO. * * @param interface the name of the interface to listen on. * @param vif the name of the vif to listen on. * @param address the name of the address to listen on. * @param port the name of the port to listen on. * @param all_nodes_address the name of the address to transmit to. * @return true if the address was enabled successfully. */ bool enable_address(const string& interface, const string& vif, const IPv4& address, const uint16_t& port, const IPv4& all_nodes_address); /** * Disable I/O for OLSR on a given address. * * Proxy for class IO. * * @param interface the name of the interface to listen on. * @param vif the name of the vif to listen on. * @param address the name of the address to listen on. * @param port the name of the port to listen on. * @return true if the address was enabled successfully. */ bool disable_address(const string& interface, const string& vif, const IPv4& address, const uint16_t& port); /** * Enable or disable an interface for OLSR. * * @param interface the name of the interface to enable/disable. * @param vif the name of the vif to enable/disable. * @param enabled the new enable state of the interface. * @return true if the interface state was set to @param enabled * successfully. */ bool set_interface_enabled(const string& interface, const string& vif, const bool enabled); /** * Determine if an interface is enabled for OLSR. * * @param interface the name of the interface to examine. * @param vif the name of the vif to examine. * @param enabled output variable will be set to the enable state * of the interface. * @return true if the enable state was retrieved successfully. */ bool get_interface_enabled(const string& interface, const string& vif, bool& enabled); /** * Get an interface's statistics. * * @param interface the name of the interface to examine. * @param vif the name of the vif to examine. * @param stats output variable will be set to the interface stats. * @return true if the stats werre retrieved successfully. */ bool get_interface_stats(const string& interface, const string& vif, FaceCounters& stats); /** * Set the cost on an interface. * * @param interface the name of the interface to set cost for. * @param vif the name of the vif to set cost for. * @param cost the new cost of the interface. * @return true if the interface cost was set successfully. */ inline bool set_interface_cost(const string& interface, const string& vif, int cost) { OlsrTypes::FaceID faceid = face_manager().get_faceid(interface, vif); return face_manager().set_interface_cost(faceid, cost); } /** * Get the MTU of an interface. * * @param interface the name of the interface to get MTU for. * @return the MTU of the interface. */ uint32_t get_mtu(const string& interface); /** * Get the primary IPv4 broadcast address configured for this * interface/vif/address. * * @param interface the interface to get broadcast address for. * @param vif the vif to get broadcast address for. * @param address the IPv4 interface address. * @param bcast_address (out argument) the IPv4 broadcast address. * @return true if the broadcast address was obtained successfully. */ bool get_broadcast_address(const string& interface, const string& vif, const IPv4& address, IPv4& bcast_address); /** * Test whether this interface/vif is broadcast capable. * * @param interface the interface to test. * @param vif the vif to test. * @return true if it is broadcast capable, otherwise false. */ bool is_vif_broadcast_capable(const string& interface, const string& vif); /** * Test whether this interface/vif is multicast capable. * * @param interface the interface to test. * @param vif the vif to test. * @return true if it is multicast capable, otherwise false. */ bool is_vif_multicast_capable(const string& interface, const string& vif); /** * Add route. * * @param net network * @param nexthop * @param nexthop_id interface ID towards the nexthop * @param metric to network * @param policytags policy info to the RIB. */ bool add_route(IPv4Net net, IPv4 nexthop, uint32_t nexthop_id, uint32_t metric, const PolicyTags& policytags); /** * Replace route. * * @param net network * @param nexthop * @param nexthop_id interface ID towards the nexthop * @param metric to network * @param policytags policy info to the RIB. */ bool replace_route(IPv4Net net, IPv4 nexthop, uint32_t nexthop_id, uint32_t metric, const PolicyTags& policytags); /** * Delete route. * * @param net network */ bool delete_route(IPv4Net net); /** * Configure a policy filter * * @param filter Id of filter to configure. * @param conf Configuration of filter. */ void configure_filter(const uint32_t& filter, const string& conf); /** * Reset a policy filter. * * @param filter Id of filter to reset. */ void reset_filter(const uint32_t& filter); /** * Push routes through policy filters for re-filtering. */ void push_routes(); /** * Originate an external route in HNA. * * @param net network prefix to originate. * @param nexthop the next hop. Ignored by HNA. * @param metric external metric for this route. * @param policytags the policy tags associated with this route. * @return true if the route was originated successfully. */ bool originate_external_route(const IPv4Net& net, const IPv4& nexthop, const uint32_t& metric, const PolicyTags& policytags); /** * Withdraw an external route from HNA. * * @param net network prefix to withdraw. * @return true if the route was withdrawn successfully. */ bool withdraw_external_route(const IPv4Net& net); /** * @return a reference to the policy filters */ PolicyFilters& get_policy_filters() { return _policy_filters; } /** * @return a reference to the tracing variables */ Trace& trace() { return _trace; } /** * @return a reference to the profiler */ //Profile& profile() { return _profile; } /** * Clear protocol databases. * * A full routing recomputation should be triggered by this operation. * This specifically clears links, MID, TC, learned HNA entries, * and the duplicate message set only. */ bool clear_database(); private: EventLoop& _eventloop; IO* _io; FaceManager _fm; Neighborhood _nh; TopologyManager _tm; ExternalRoutes _er; RouteManager _rm; string _reason; ProcessStatus _process_status; PolicyFilters _policy_filters; /** * Trace variables. */ Trace _trace; /** * Profiler instance. */ //Profile _profile; }; #endif // __OLSR_OLSR_HH__ xorp/contrib/olsr/test_routing1.py0000775000076400007640000014252011421137511017463 0ustar greearbgreearb#!/usr/bin/env python # vim:set sts=4 ts=8 sw=4: # Copyright (c) 2001-2009 XORP, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # $XORP: xorp/contrib/olsr/test_routing1.py,v 1.10 2008/10/02 21:56:36 bms Exp $ import getopt import sys import os import time from stat import * TESTS=[ # Fields: # 0: Test function # 1: True if this test works ['test_create', True], ['test_3line', True], ['test_3line_always', True], ['test_c4_partial', True], # currently broken, MPRs and thus TC are nondeterministic: #['test_c4_full', True], # No verification predicates yet other than N2: #['test_c5_partial', True], #['test_c5_full', True], #['test_c6_partial', True], #['test_c6_full', True], #['test_7up', True], ] ############################################################################# def start_routing_interactive(verbose, valgrind): """ Helper function to start the test_simulator program """ flags = "" if verbose: flags = flags + ' -v -l 2' command = os.path.abspath('test_simulator') + flags; # I should really import a which() here. if valgrind: valgrind_path = "/usr/local/bin/valgrind" valgrind_flags = "--tool=memcheck " \ "--leak-check=yes " \ "--leak-resolution=high " \ "--num-callers=10 " \ "--show-reachable=yes " \ "--suppressions=/home/bms/.valgrind/fbsd.supp " \ "--suppressions=/home/bms/.valgrind/xorp.supp " \ "--logfile=test_simulator.log " \ "--demangle=no " \ "--verbose " #"--undef-value-errors=no " # not in old valgrind command = valgrind_path + ' ' + valgrind_flags + command fp = os.popen(command, "w") return fp ############################################################################# def test_create(verbose, valgrind): """ Create an OLSR instance with two interfaces, and then destroy it. """ # Don't bother with valgrind for the simple creation test. fp = start_routing_interactive(verbose, False) command = """ create 192.168.0.1 192.168.0.2 destroy 192.168.0.1 """ print >>fp, command if not fp.close(): return True else: return False ############################################################################# def test_3line(verbose, valgrind): """ Simulate three OLSR nodes A-O-B and edges A-O, O-B, all connected in a straight line when embedded in the plane. * This test is designed to cover most of the core OLSR protocol functionality (HELLO, MID, HNA, TC). * TC_INTERVAL is set to 2s, therefore TOP_HOLD_TIME is 6s. * All other base timers default to 1s. * WILLINGNESS is set to 6, to simplify the metrics. * TC redundancy is set to the default -- it makes no difference in such a simple topology, as only 2 nodes will see the broadcasts. * We can't verify the internal routing entry fields from the simulator, just what the RIB would see. """ ############################################### # # A o olsr2 # | 192.168.1.2/32 eth0 # | # | # | 192.168.1.1/32 eth0 # O o olsr1 # | 192.168.2.1/32 eth1 # | # | olsr3 # | 192.168.2.2/32 eth0 # B o # ############################################### fp = start_routing_interactive(verbose, valgrind) # The order of creation and linkage here should make no difference. command = """ set_default_willingness 6 # TOP_HOLD_TIME is 2*3 = 6 set_default_tc_interval 2 create 192.168.1.1 192.168.2.1 create 192.168.1.2 create 192.168.2.2 # Event: O can now see A and B. add_link 192.168.1.1 192.168.1.2 add_link 192.168.2.1 192.168.2.2 # 6 seconds is enough time for N1/N2/MID to show up, # but not enough for TC to show up. echo waiting for N2 advertisements to propagate wait 6 # An extra second of slack is needed for the TC broadcasts # to propagate to A and B; the TC timer may be firing just out of phase. echo waiting for TC broadcast to propagate wait 1 select 192.168.1.1 verify_n1 192.168.1.2 true verify_n1 192.168.2.2 true # WILL(A, B, O) != WILL_ALWAYS: O should not select any MPRs. # O should become an MPR on behalf of both A and B regardless of the above. verify_mpr_set empty verify_mpr_selector_set 192.168.1.2 192.168.2.2 # O should only see 2 routes: an N1 route to A and an N1 route to B. # A selects O as MPR, thus O lowers its metric to prefer A as an MPR selector. # B selects O as MPR, thus O lowers its metric to prefer B as an MPR selector. verify_routing_table_size 2 verify_routing_entry 192.168.1.2/32 192.168.1.2 1 verify_routing_entry 192.168.2.2/32 192.168.2.2 1 verify_mid_node_count 0 verify_mid_node_addrs 192.168.1.1 empty verify_mid_node_addrs 192.168.2.2 empty # O should never see any TC entries from anybody. verify_tc_origin_count 0 # A should never become an MPR. # B should be an N2 of A via O, therefore O must be A's MPR. select 192.168.1.2 verify_n1 192.168.1.1 true verify_n2 192.168.2.2 true verify_mpr_set 192.168.1.1 verify_mpr_selector_set empty # A should see an N1 route for O and an N2 route for B via O. # A should also see a MID route for O's other address, which # of course have a next-hop of O's main address. # A's base cost for O will be 2 (1 hop away, 0 interface cost, and not # an MPR selector). B is a further hop away, thus its cost is 3. verify_routing_table_size 3 verify_routing_entry 192.168.1.1/32 192.168.1.1 2 verify_routing_entry 192.168.2.1/32 192.168.1.1 2 verify_routing_entry 192.168.2.2/32 192.168.1.1 3 # A should see a MID entry from O and always use it for routing, # as it cannot infer the existence of the far side. verify_mid_node_count 1 verify_mid_node_addrs 192.168.1.1 192.168.2.1 verify_mid_node_addrs 192.168.2.1 empty verify_mid_distance 192.168.1.1 192.168.2.1 1 # A should see a single TC entry from O which is redundant for routing # as it's within the 2 hop radius. verify_tc_origin_count 1 verify_tc_ans 192.168.1.1 2 192.168.1.2 192.168.2.2 verify_tc_destination 192.168.1.2 1 verify_tc_destination 192.168.2.2 1 verify_tc_distance 192.168.1.1 192.168.1.2 2 verify_tc_distance 192.168.1.1 192.168.2.2 2 # B should never become an MPR. # A should be an N2 of B via O, therefore O must be B's MPR. # Note: verifying n1 status with non-link address. select 192.168.2.2 verify_n1 192.168.1.1 true verify_n2 192.168.1.2 true verify_mpr_set 192.168.1.1 verify_mpr_selector_set empty # B should see an N2 route for A, an N1 route to O, and # a further route for O's main address (inferred because O's N1 link tuple # has a different address from the origin field; for B, the MID is redundant). # B's base cost for O will be 2 (1 hop away, 0 interface cost, and not # an MPR selector). A is a further hop away, thus its cost is 3. verify_routing_table_size 3 verify_routing_entry 192.168.1.1/32 192.168.2.1 2 verify_routing_entry 192.168.2.1/32 192.168.2.1 2 verify_routing_entry 192.168.1.2/32 192.168.2.1 3 verify_mid_node_count 1 verify_mid_node_addrs 192.168.1.1 192.168.2.1 verify_mid_node_addrs 192.168.2.1 empty verify_mid_distance 192.168.1.1 192.168.2.1 1 # B should see a single TC entry from O which is redundant for routing # as it's within the 2 hop radius. verify_tc_origin_count 1 verify_tc_ans 192.168.1.1 2 192.168.1.2 192.168.2.2 verify_tc_destination 192.168.1.2 1 verify_tc_destination 192.168.2.2 1 verify_tc_distance 192.168.1.1 192.168.1.2 2 verify_tc_distance 192.168.1.1 192.168.2.2 2 # Event: Start advertising two HNA prefixes from B. # Only O should see them. echo advertising two HNA prefixes from B. select 192.168.2.2 originate_hna 10.0.0.0/8 originate_hna 20.0.0.0/8 echo waiting 4 seconds for HNA to propagate. wait 4 # Verify that O can see the networks B advertised. select 192.168.1.1 verify_hna_entry_count 2 verify_hna_dest_count 2 verify_hna_origin_count 1 verify_hna_distance 10.0.0.0/8 192.168.2.2 1 verify_hna_distance 10.0.0.0/8 192.168.2.2 1 # O should see A,B as N1, *and* the two HNA routes B advertises, # making a total of 4 routes. # O's cost for both A and B is 1 as they are N1s. # The HNA cost of B's advertised prefixes is the same as the cost to reach B. verify_routing_table_size 4 verify_routing_entry 192.168.1.2/32 192.168.1.2 1 verify_routing_entry 192.168.2.2/32 192.168.2.2 1 verify_routing_entry 10.0.0.0/8 192.168.2.2 1 verify_routing_entry 20.0.0.0/8 192.168.2.2 1 # Verify that A can see B's prefixes; and that the next-hops resolved # to O, its only upstream, thus 2 hops away. select 192.168.1.2 verify_hna_entry_count 2 verify_hna_dest_count 2 verify_hna_origin_count 1 verify_hna_distance 10.0.0.0/8 192.168.2.2 2 verify_hna_distance 10.0.0.0/8 192.168.2.2 2 verify_routing_table_size 5 verify_routing_entry 192.168.1.1/32 192.168.1.1 2 verify_routing_entry 192.168.2.1/32 192.168.1.1 2 verify_routing_entry 192.168.2.2/32 192.168.1.1 3 verify_routing_entry 10.0.0.0/8 192.168.1.1 3 verify_routing_entry 20.0.0.0/8 192.168.1.1 3 # Verify that B can see no HNA whatsoever. # [It advertises HNA, but should not learn HNA.] # B should still see the N1 route for O and its MID entry, and the # N2 route to A. select 192.168.2.2 verify_hna_entry_count 0 verify_routing_table_size 3 echo withdrawing HNA prefixes at B. select 192.168.2.2 withdraw_hna 10.0.0.0/8 withdraw_hna 20.0.0.0/8 # Verify that all HNA entries expire correctly. echo waiting 4 seconds for HNA entries to time out. wait 4 select 192.168.1.1 verify_hna_entry_count 0 select 192.168.1.2 verify_hna_entry_count 0 select 192.168.2.2 verify_hna_entry_count 0 echo end of HNA sub-test. # Event: Take O's top interface down. # Should take 1 tick + NEIGH_HOLD_TIME (usually 3 * refresh == 1) for # the N2 deletion of A to propagate to B. # Should take another 3s for the MPR selector tuple at B to expire, # and for this to be seen by O, which causes O to stop being B's MPR. # During this window the TC ANSN at O will bump to 3, then 4 once # B's MPR selector tuple expires at O. face_down 192.168.1.1 wait 7 # B should no longer be able to reach A as a two-hop neighbor. # If B still thinks A is a two-hop neighbor, O will still # think it's an MPR. None of these predicates should now hold. # Note: verifying n1 status with non-link address. select 192.168.2.2 verify_n1 192.168.1.1 true verify_n2 192.168.1.2 false verify_mpr_set empty verify_mpr_selector_set empty # B should now see an N1 route for O, and an inferred address for O's # main address. MID should not influence B's routing table. # The MID entry for O will have expired after MID_HOLD_TIME which # in this case is (3 * 1) seconds. verify_routing_table_size 2 verify_mid_node_count 0 verify_mid_node_addrs 192.168.1.1 empty verify_mid_node_addrs 192.168.2.1 empty # Any TC entries originated by O should now have expired at B, # as O should no longer be an MPR, and O SHOULD have sent empty # TCs to withdraw its advertised topology by now. # [If this fails it is possible that TOP_HOLD_TIME is too high.] verify_tc_origin_count 0 verify_tc_ans 192.168.1.1 4 empty # A should no longer see any 2-hop or 1-hop neighbors. # If A still thinks B is a two-hop neighbor, O will still # think it's an MPR. None of these predicates should now hold. # A should no longer see any MID entries. select 192.168.1.2 verify_n1 192.168.1.1 false verify_n2 192.168.2.2 false verify_mpr_set empty verify_mpr_selector_set empty verify_routing_table_size 0 verify_mid_node_count 0 # Any TC entries originated by O should now have expired at A. verify_tc_origin_count 0 # O should not see A. # O should never see any MID entries. # O should never see any TC entries. select 192.168.1.1 verify_n1 192.168.1.2 false verify_n1 192.168.2.2 true # O should see only B now as an N1. # O is no longer selected as an MPR by B so its cost for B goes back up. verify_routing_table_size 1 # XXX Metric is not increasing correctly; this may need attention. # Commented out for now. -bms # XXX debug #dump_routing_table 192.168.1.1 #verify_routing_entry 192.168.2.2/32 192.168.2.2 2 verify_mid_node_count 0 verify_tc_origin_count 0 verify_tc_origin_seen 192.168.1.1 false verify_tc_origin_seen 192.168.1.2 false verify_tc_origin_seen 192.168.2.2 false # WILL(A, B, O) != WILL_ALWAYS: # O's MPR set should always be empty # O's MPR selector set should become empty. verify_mpr_set empty verify_mpr_selector_set empty # Event: Simulate physical removal of O's links to A and B. remove_link 192.168.1.1 192.168.1.2 remove_link 192.168.2.1 192.168.2.2 wait 5 # Verify that O can no longer see A or B, and has no routes. select 192.168.1.1 verify_n1 192.168.1.2 false verify_n1 192.168.2.2 false verify_mpr_set empty verify_mpr_selector_set empty verify_routing_table_size 0 verify_mid_node_count 0 verify_tc_origin_count 0 verify_hna_entry_count 0 # Routing table paranoia for A and B. # Any MID entries from O should now have expired. # Any TC entries from O should now have expired. select 192.168.1.2 verify_routing_table_size 0 verify_mid_node_count 0 verify_mid_node_addrs 192.168.1.1 empty verify_tc_origin_count 0 verify_hna_entry_count 0 select 192.168.2.2 verify_routing_table_size 0 verify_mid_node_count 0 verify_mid_node_addrs 192.168.1.1 empty verify_tc_origin_count 0 verify_hna_entry_count 0 """ print >>fp, command if not fp.close(): return True else: return False ############################################################################# def test_3line_always(verbose, valgrind): """ Simulate three OLSR nodes A-O-B and edges A-O, O-B, all connected in a straight line when embedded in the plane. All three nodes have willingness of WILL_ALWAYS. A slightly higher TC interval is needed to increase TOP_HOLD_TIME, as TCs are now forwarded between all nodes. """ ############################################### # # A o olsr2 # | 192.168.1.2/32 eth0 # | # | # | 192.168.1.1/32 eth0 # O o olsr1 # | 192.168.2.1/32 eth1 # | # | olsr3 # | 192.168.2.2/32 eth0 # B o # ############################################### fp = start_routing_interactive(verbose, valgrind) command = """ set_default_willingness 7 set_default_tc_interval 3 create 192.168.1.1 192.168.2.1 create 192.168.1.2 create 192.168.2.2 add_link 192.168.1.1 192.168.1.2 add_link 192.168.2.1 192.168.2.2 # 5 seconds is enough time for N1/N2/MID to show up, # but not enough for TC to show up. echo waiting for N2 advertisements to propagate wait 5 # Every direct neighbor is an MPR to every other direct neighbor. select 192.168.1.1 verify_n1 192.168.1.2 true verify_n1 192.168.2.2 true verify_mpr_set 192.168.1.2 192.168.2.2 verify_mpr_selector_set 192.168.1.2 192.168.2.2 select 192.168.1.2 verify_n1 192.168.1.1 true verify_n2 192.168.2.2 true verify_mpr_set 192.168.1.1 verify_mpr_selector_set 192.168.1.1 select 192.168.2.2 verify_n1 192.168.1.1 true verify_n2 192.168.1.2 true verify_mpr_set 192.168.1.1 verify_mpr_selector_set 192.168.1.1 echo waiting 3 seconds for TC broadcasts to propagate wait 3 select 192.168.1.1 verify_n1 192.168.1.2 true verify_n1 192.168.2.2 true verify_tc_origin_seen 192.168.1.1 false verify_tc_origin_seen 192.168.2.1 false verify_tc_origin_seen 192.168.1.2 true verify_tc_origin_seen 192.168.2.2 true verify_tc_ans 192.168.1.2 2 192.168.1.1 verify_tc_ans 192.168.2.2 2 192.168.1.1 verify_routing_table_size 2 verify_routing_entry 192.168.1.2/32 192.168.1.2 0 verify_routing_entry 192.168.2.2/32 192.168.2.2 0 select 192.168.1.2 verify_n1 192.168.1.1 true verify_n2 192.168.2.2 true verify_mpr_set 192.168.1.1 verify_tc_origin_seen 192.168.1.1 true verify_tc_origin_seen 192.168.2.1 false verify_tc_origin_seen 192.168.1.2 false verify_tc_origin_seen 192.168.2.2 true verify_tc_ans 192.168.1.1 2 192.168.1.2 192.168.2.2 verify_tc_ans 192.168.2.2 2 192.168.1.1 verify_routing_table_size 3 verify_routing_entry 192.168.1.1/32 192.168.1.1 0 verify_routing_entry 192.168.2.1/32 192.168.1.1 0 verify_routing_entry 192.168.2.2/32 192.168.1.1 1 select 192.168.2.2 verify_n1 192.168.1.1 true verify_n2 192.168.1.2 true verify_mpr_set 192.168.1.1 verify_tc_origin_seen 192.168.1.1 true verify_tc_origin_seen 192.168.1.2 true verify_tc_origin_seen 192.168.2.1 false verify_tc_origin_seen 192.168.2.2 false verify_tc_ans 192.168.1.1 2 192.168.1.2 192.168.2.2 verify_tc_ans 192.168.1.2 2 192.168.1.1 verify_routing_table_size 3 verify_routing_entry 192.168.1.1/32 192.168.2.1 0 verify_routing_entry 192.168.2.1/32 192.168.2.1 0 verify_routing_entry 192.168.1.2/32 192.168.2.1 1 # Set willingness of the stub nodes to 6 and # wait for the MPR selector tuples for a,b at O to expire. echo setting willingness of a,b to 6 and waiting for mpr selector expiry set_willingness 192.168.1.2 6 set_willingness 192.168.2.2 6 # MS_time is same as HELLO_INTERVAL at the other nodes; but we # may be out of phase so wait HELLO_INTERVAL * 2 + 1. # However this also means that the TC entries will have expired. wait 7 # verify that O is still MPR for A and B, but A and B # are no longer MPRs themselves. # also verify that the route metrics have correspondingly gone up # at O; they will stay the same at A and B because whilst O # no longer selects A, B as MPRs, O still appears to have # a willingness of 7 to them. select 192.168.1.1 verify_mpr_set empty verify_mpr_selector_set 192.168.1.2 192.168.2.2 verify_tc_origin_seen 192.168.1.1 false verify_tc_origin_seen 192.168.2.1 false verify_tc_origin_seen 192.168.1.2 true verify_tc_origin_seen 192.168.2.2 true verify_tc_ans 192.168.1.2 3 empty verify_tc_ans 192.168.2.2 3 empty verify_routing_table_size 2 verify_routing_entry 192.168.1.2/32 192.168.1.2 1 verify_routing_entry 192.168.2.2/32 192.168.2.2 1 select 192.168.1.2 verify_mpr_set 192.168.1.1 verify_mpr_selector_set empty verify_tc_origin_seen 192.168.1.1 true verify_tc_origin_seen 192.168.2.1 false verify_tc_origin_seen 192.168.1.2 false verify_tc_origin_seen 192.168.2.2 true verify_tc_ans 192.168.1.1 2 192.168.1.2 192.168.2.2 verify_tc_ans 192.168.2.2 3 empty verify_routing_table_size 3 verify_routing_entry 192.168.1.1/32 192.168.1.1 1 verify_routing_entry 192.168.2.1/32 192.168.1.1 1 verify_routing_entry 192.168.2.2/32 192.168.1.1 2 select 192.168.2.2 verify_mpr_set 192.168.1.1 verify_mpr_selector_set empty verify_tc_origin_seen 192.168.1.1 true verify_tc_origin_seen 192.168.1.2 true verify_tc_origin_seen 192.168.2.1 false verify_tc_origin_seen 192.168.2.2 false verify_tc_ans 192.168.1.1 2 192.168.1.2 192.168.2.2 verify_tc_ans 192.168.1.2 3 empty verify_routing_table_size 3 verify_routing_entry 192.168.1.1/32 192.168.2.1 1 verify_routing_entry 192.168.2.1/32 192.168.2.1 1 verify_routing_entry 192.168.1.2/32 192.168.2.1 2 echo removing O's links and waiting 5s remove_link 192.168.1.1 192.168.1.2 remove_link 192.168.2.1 192.168.2.2 wait 5 # At this point there is no way A, B or O can see any # of each other's withdrawn ANSNs. select 192.168.1.1 verify_n1 192.168.1.2 false verify_n1 192.168.2.2 false verify_mpr_set empty verify_mpr_selector_set empty verify_tc_origin_seen 192.168.1.1 false verify_tc_origin_seen 192.168.2.1 false verify_tc_origin_seen 192.168.1.2 true verify_tc_origin_seen 192.168.2.2 true select 192.168.1.2 verify_n1 192.168.1.1 false verify_n2 192.168.2.2 false verify_mpr_set empty verify_mpr_selector_set empty verify_tc_origin_seen 192.168.1.1 true verify_tc_origin_seen 192.168.2.1 false verify_tc_origin_seen 192.168.1.2 false verify_tc_origin_seen 192.168.2.2 true select 192.168.2.2 verify_n1 192.168.2.1 false verify_n2 192.168.1.2 false verify_mpr_set empty verify_mpr_selector_set empty verify_tc_origin_seen 192.168.1.1 true verify_tc_origin_seen 192.168.1.2 true verify_tc_origin_seen 192.168.2.1 false verify_tc_origin_seen 192.168.2.2 false """ print >>fp, command if not fp.close(): return True else: return False ############################################################################# def test_c4_partial(verbose, valgrind): """ Simulate four OLSR nodes A-O-B-C and edges A-O, O-B, B-C. Build the topology in counter-clockwise order, numbering nodes in the fourth octet and links in the third octet in ascending order. Verify n1, n2 status, MPR status. As soon as O sees B's link to C, it should select B as an MPR; this relationship should be reciprocal -- as O provides connectivity for B to reach A. """ ############################################### # # 192.168.2.4/32 192.168.2.3/32 # C o-----------o B # | 192.168.1.3/32 # | # | # | # | 192.168.1.2/32 # A o-----------o O # 192.168.0.1/32 192.168.0.2/32 # ############################################### fp = start_routing_interactive(verbose, valgrind) command = """ # simplify the link costing by adopting a willingness of 6 (HIGH). set_default_willingness 6 # only advertise MPR selectors in TC messages. set_default_tc_redundancy 0 # TOP_HOLD_TIME is 3*3 = 9 set_default_tc_interval 3 create 192.168.0.1 create 192.168.0.2 192.168.1.2 create 192.168.1.3 192.168.2.3 create 192.168.2.4 add_link 192.168.0.1 192.168.0.2 add_link 192.168.1.2 192.168.1.3 wait 3 select 192.168.0.2 verify_n1 192.168.0.1 true verify_n1 192.168.1.3 true add_link 192.168.2.3 192.168.2.4 wait 3 select 192.168.2.3 verify_n1 192.168.2.4 true wait 3 select 192.168.0.2 verify_n2 192.168.2.4 true wait 1 select 192.168.1.3 verify_n2 192.168.0.1 true ####################################################### # Wait for full link-state convergence (TC, N1, N2). # N1/N2/MPR OK. wait 5 # Wait at least 1 * TOP_HOLD_TIME. wait 9 # XXX needed with aggressive eventloop? #wait 1 ####################################################### # Verify that TCs are seen at either extremity. # Only O and B should originate TCs. # TC redundancy is set to 0 so only their MPR selectors # should be advertised. # Consider A. select 192.168.0.1 # Only O and B should originate TCs. verify_tc_origin_seen 192.168.0.1 false verify_tc_origin_seen 192.168.0.2 true verify_tc_origin_seen 192.168.1.3 true verify_tc_origin_seen 192.168.2.4 false # Should see O's advertisement of self as we select O as an MPR. verify_tc_destination 192.168.0.1 1 # Should see O's advertisement of B, as B selects O as an MPR. verify_tc_destination 192.168.1.3 1 # Should see B's advertisement of O, as O selects B as an MPR in # order to reach C. verify_tc_destination 192.168.0.2 1 # Should see B's advertisement of extremity C. verify_tc_destination 192.168.2.4 1 # Consider C. select 192.168.2.4 # Only O and B should originate TCs. verify_tc_origin_seen 192.168.0.1 false verify_tc_origin_seen 192.168.0.2 true verify_tc_origin_seen 192.168.1.3 true verify_tc_origin_seen 192.168.2.4 false # Should see B's advertisement of self, as we select O as an MPR. verify_tc_destination 192.168.2.4 1 # Should see B's advertisement of O, as O selects B as an MPR. verify_tc_destination 192.168.0.2 1 # Should see O's advertisement of B, as B selects O as an MPR in # order to reach A. verify_tc_destination 192.168.1.3 1 # Should see O's advertisement of extremity A. verify_tc_destination 192.168.0.1 1 ####################################################### # Verify everyone's MPR status when fully converged. # Consider A. select 192.168.0.1 # A must use O to reach everyone else. verify_mpr_set 192.168.0.2 # Nothing uses A as a next hop. verify_mpr_selector_set empty # Consider O. select 192.168.0.2 # O must use B to reach C. verify_mpr_set 192.168.1.3 # A must use O to reach everyone else. # B must use O to reach A. verify_mpr_selector_set 192.168.0.1 192.168.1.3 # Consider B. select 192.168.1.3 # B must use O to reach A. verify_mpr_set 192.168.0.2 # O must use B to reach C. # C must use B to reach everyone else. verify_mpr_selector_set 192.168.0.2 192.168.2.4 # Consider C. select 192.168.2.4 # C must use B to reach everyone else. verify_mpr_set 192.168.1.3 # Nothing uses C as a next hop. verify_mpr_selector_set empty ####################################################### # Verify everyone's routing tables when fully converged. # A's base cost for O will be 2 (1 hop away, 0 interface cost, and not # an MPR selector). The cost of an N2 link is 1; the cost of any MID # route is the same as the route to the main address upon which it # is based. # MPR selectors have their path cost decremented by 1 to prefer # them over other hops. # TC information comes into play as the extremities of this # graph are >= 3 hops away, those peers will only find routes to # each other in the TC information. The cost of a TC hop is 1 in # RFC compliant OLSR. select 192.168.0.1 # N1: O verify_routing_entry 192.168.0.2/32 192.168.0.2 2 # MID: O verify_routing_entry 192.168.1.2/32 192.168.0.2 2 # N2: B verify_routing_entry 192.168.1.3/32 192.168.0.2 3 # MID: B verify_routing_entry 192.168.2.3/32 192.168.0.2 3 # TC: C verify_routing_entry 192.168.2.4/32 192.168.0.2 4 select 192.168.0.2 # N1: A [MPR selector] verify_routing_entry 192.168.0.1/32 192.168.0.1 1 # N1: B [MPR selector] verify_routing_entry 192.168.1.3/32 192.168.1.3 1 # MID: B verify_routing_entry 192.168.2.3/32 192.168.1.3 1 # N2: C verify_routing_entry 192.168.2.4/32 192.168.1.3 2 select 192.168.1.3 # N1: C [MPR selector] verify_routing_entry 192.168.2.4/32 192.168.2.4 1 # N1: O [MPR selector] verify_routing_entry 192.168.1.2/32 192.168.1.2 1 # MID: O verify_routing_entry 192.168.0.2/32 192.168.1.2 1 # N2: A verify_routing_entry 192.168.0.1/32 192.168.1.2 2 select 192.168.2.4 # N1: B verify_routing_entry 192.168.2.3/32 192.168.2.3 2 # MID: B verify_routing_entry 192.168.1.3/32 192.168.2.3 2 # N2: O verify_routing_entry 192.168.1.2/32 192.168.2.3 3 # MID: O verify_routing_entry 192.168.0.2/32 192.168.2.3 3 # TC: A verify_routing_entry 192.168.0.1/32 192.168.2.3 4 ############################# # Disable edge B-C by taking down an interface, # and verify links go down. face_down 192.168.2.3 select 192.168.1.3 verify_n1 192.168.2.4 false # Verify N2 tuple at O times out: # vtime of N2(B) at O = 3 + # 2 * ( HELLO_INTERVAL(1) + REFRESH_INTERVAL(1) ) for propagation. wait 7 select 192.168.0.2 verify_n2 192.168.2.4 false # Verify everyone's MPR status. # B should be the only remaining MPR in the graph. select 192.168.0.1 verify_mpr_set 192.168.0.2 verify_mpr_selector_set empty select 192.168.0.2 verify_mpr_set empty verify_mpr_selector_set 192.168.0.1 192.168.1.3 select 192.168.1.3 verify_mpr_set 192.168.0.2 verify_mpr_selector_set empty select 192.168.2.4 verify_mpr_set empty verify_mpr_selector_set empty # Dismantle some of the other links. remove_link 192.168.1.2 192.168.1.3 remove_link 192.168.0.1 192.168.0.2 wait 5 select 192.168.0.2 verify_n1 192.168.0.1 false verify_n1 192.168.1.3 false """ print >>fp, command if not fp.close(): return True else: return False ############################################################################# def test_c4_full(verbose, valgrind): """ Simulate four OLSR nodes A-B-C-D in the cyclic graph C[4]. Each node has two interfaces. The main address of each node is configured to be the lowest interface address. In this topology every node is an MPR, as the node at the extremity is a two-hop neighbor with two links, and MPR coverage has been set to 2. Ties must be broken before routes are sent to a non-ECMP capable RIB. The tie SHOULD be broken by selecting the node with the lowest main address, and this is the main condition for this test. All nodes SHOULD originate TC messages once the network has fully converged. """ ############################################### # # 192.168.2.4/32 192.168.2.3/32 # D o-----------o C # 192.168.3.4/32 | | 192.168.1.3/32 # | | # | | # | | # 192.168.3.1/32 | | 192.168.1.2/32 # A o-----------o B # 192.168.0.1/32 192.168.0.2/32 # ############################################### fp = start_routing_interactive(verbose, valgrind) command = """ # simplify the link costing by adopting a willingness of 6 (HIGH). set_default_willingness 6 # only advertise MPR selectors in TC messages. set_default_tc_redundancy 0 # TOP_HOLD_TIME is 3*3 = 9 set_default_tc_interval 3 # MPR coverage is 2. #set_default_mpr_coverage 2 create 192.168.0.1 192.168.3.1 create 192.168.0.2 192.168.1.2 create 192.168.1.3 192.168.2.3 create 192.168.2.4 192.168.3.4 add_link 192.168.0.1 192.168.0.2 add_link 192.168.1.2 192.168.1.3 add_link 192.168.2.3 192.168.2.4 add_link 192.168.3.4 192.168.3.1 ####################################################### # Wait for partial link-state convergence (N1, N2). # * Every node SHOULD have two N1 and one N2. # * Remember: N1 and N2 are identified by main address. wait 5 select 192.168.0.1 verify_n1 192.168.0.2 true verify_n1 192.168.2.4 true verify_n2 192.168.1.3 true select 192.168.0.2 verify_n1 192.168.0.1 true verify_n1 192.168.1.3 true verify_n2 192.168.2.4 true select 192.168.1.3 verify_n1 192.168.0.2 true verify_n1 192.168.2.4 true verify_n2 192.168.0.1 true select 192.168.2.4 verify_n1 192.168.1.3 true verify_n1 192.168.0.1 true verify_n2 192.168.0.2 true ####################################################### # Wait for full link-state convergence (TC, MPR). # Wait at least 1 * TOP_HOLD_TIME. wait 9 ####################################################### # Verify everyone's MPR status when fully converged. # * MPR coverage is 2 for this test. # * NOTE WELL: THE MPR SELECTION IS CURRENTLY CHAOTIC IN THAT YOU MUST # KNOW THE INITIAL CONDITIONS FOR THE N1 TO BE SELECTED AS FINAL MPR. # * For each node: the N1 with highest main address should be selected # as MPR once the network has converged. select 192.168.0.1 #verify_mpr_set 192.168.0.2 192.168.2.4 #verify_mpr_selector_set 192.168.0.2 192.168.2.4 select 192.168.0.2 #verify_mpr_set 192.168.0.1 192.168.1.3 #verify_mpr_selector_set 192.168.0.1 192.168.1.3 select 192.168.1.3 #verify_mpr_set 192.168.0.2 192.168.2.4 #verify_mpr_selector_set 192.168.0.2 192.168.2.4 select 192.168.2.4 #verify_mpr_set 192.168.0.1 192.168.1.3 #verify_mpr_selector_set 192.168.0.1 192.168.1.3 ####################################################### # Verify that TCs are seen at either extremity. # # * All nodes SHOULD originate TCs. # * TC redundancy is set to 0 so only their MPR selectors # should be advertised, in this case both one-hop neighbors of each node. # * Nodes SHOULD NOT process their own TC messages. # * Each node should therefore see itself mentioned twice in TC by each # of its N1; # its N2 mentioned twice by each N1; # but both of its N1s should be mentioned once only by the node at # the opposite corner of the graph. # # XXX These predicates don't hold, why? select 192.168.0.1 #verify_tc_origin_count 3 #verify_tc_origin_seen 192.168.0.1 false #verify_tc_origin_seen 192.168.0.2 true #verify_tc_origin_seen 192.168.1.3 true #verify_tc_origin_seen 192.168.2.4 true #verify_tc_destination 192.168.0.1 2 #verify_tc_destination 192.168.0.2 1 #verify_tc_destination 192.168.1.3 2 #verify_tc_destination 192.168.2.4 1 select 192.168.0.2 #verify_tc_origin_count 3 #verify_tc_origin_seen 192.168.0.1 true #verify_tc_origin_seen 192.168.0.2 false #verify_tc_origin_seen 192.168.1.3 true #verify_tc_origin_seen 192.168.2.4 true #verify_tc_destination 192.168.0.1 1 #verify_tc_destination 192.168.0.2 2 #verify_tc_destination 192.168.1.3 1 #verify_tc_destination 192.168.2.4 2 select 192.168.1.3 #verify_tc_origin_count 3 #verify_tc_origin_seen 192.168.0.1 true #verify_tc_origin_seen 192.168.0.2 true #verify_tc_origin_seen 192.168.1.3 false #verify_tc_origin_seen 192.168.2.4 true #verify_tc_destination 192.168.0.1 2 #verify_tc_destination 192.168.0.2 1 #verify_tc_destination 192.168.1.3 2 #verify_tc_destination 192.168.2.4 1 select 192.168.2.4 #verify_tc_origin_count 3 #verify_tc_origin_seen 192.168.0.1 true #verify_tc_origin_seen 192.168.0.2 true #verify_tc_origin_seen 192.168.1.3 true #verify_tc_origin_seen 192.168.2.4 false #verify_tc_destination 192.168.0.1 1 #verify_tc_destination 192.168.0.2 2 #verify_tc_destination 192.168.1.3 1 #verify_tc_destination 192.168.2.4 2 ####################################################### # Verify everyone's routing tables when fully converged. # # * Each node's cost towards any N1 will be 1 (1 hop away, 0 interface cost, # and ALWAYS an MPR selector). # * Each node's cost towards any N2 will be 2 (1 + 1). # * The cost of any MID route is the same as the route to the main address # upon which it is based; MID entries SHOULD exist for each node in the # topology as they all have >1 interface. # * The link via the lowest main address MUST win in strict RFC 3626 OLSR. # However, the preferred next-hop address will be that of the interface # facing each node; it is easy to get confused below. # * No TC information should be used for routing as it is totally # redundant in this example. ALL routes must be derived from N1/N2/MID. select 192.168.0.1 verify_routing_table_size 6 verify_routing_entry 192.168.0.2/32 192.168.0.2 1 verify_routing_entry 192.168.1.2/32 192.168.0.2 1 verify_routing_entry 192.168.2.4/32 192.168.3.4 1 verify_routing_entry 192.168.3.4/32 192.168.3.4 1 verify_routing_entry 192.168.1.3/32 192.168.0.2 2 verify_routing_entry 192.168.2.3/32 192.168.0.2 2 select 192.168.0.2 verify_routing_table_size 6 verify_routing_entry 192.168.0.1/32 192.168.0.1 1 verify_routing_entry 192.168.3.1/32 192.168.0.1 1 verify_routing_entry 192.168.1.3/32 192.168.1.3 1 verify_routing_entry 192.168.2.3/32 192.168.1.3 1 verify_routing_entry 192.168.2.4/32 192.168.0.1 2 verify_routing_entry 192.168.3.4/32 192.168.0.1 2 select 192.168.1.3 verify_routing_table_size 6 verify_routing_entry 192.168.1.2/32 192.168.1.2 1 verify_routing_entry 192.168.0.2/32 192.168.1.2 1 verify_routing_entry 192.168.2.4/32 192.168.2.4 1 verify_routing_entry 192.168.3.4/32 192.168.2.4 1 verify_routing_entry 192.168.0.1/32 192.168.1.2 2 verify_routing_entry 192.168.3.1/32 192.168.1.2 2 select 192.168.2.4 verify_routing_table_size 6 verify_routing_entry 192.168.2.3/32 192.168.2.3 1 verify_routing_entry 192.168.1.3/32 192.168.2.3 1 verify_routing_entry 192.168.3.1/32 192.168.3.1 1 verify_routing_entry 192.168.0.1/32 192.168.3.1 1 verify_routing_entry 192.168.0.2/32 192.168.3.1 2 verify_routing_entry 192.168.1.2/32 192.168.3.1 2 ##################################################### # Take down the entire topology and validate that all state times out. remove_link 192.168.0.1 192.168.0.2 remove_link 192.168.1.2 192.168.1.3 remove_link 192.168.2.3 192.168.2.4 remove_link 192.168.3.4 192.168.3.1 #remove_all_links wait 15 #verify_all_link_state_empty select 192.168.0.1 verify_n1 192.168.0.2 false verify_n1 192.168.2.4 false verify_n2 192.168.1.3 false verify_mpr_set empty verify_mpr_selector_set empty verify_mid_node_count 0 verify_tc_origin_count 0 verify_routing_table_size 0 select 192.168.0.2 verify_n1 192.168.0.1 false verify_n1 192.168.1.3 false verify_n2 192.168.2.4 false verify_mpr_set empty verify_mpr_selector_set empty verify_mid_node_count 0 verify_tc_origin_count 0 verify_routing_table_size 0 select 192.168.1.3 verify_n1 192.168.0.2 false verify_n1 192.168.2.4 false verify_n2 192.168.0.1 false verify_mpr_set empty verify_mpr_selector_set empty verify_mid_node_count 0 verify_tc_origin_count 0 verify_routing_table_size 0 select 192.168.2.4 verify_n1 192.168.1.3 false verify_n1 192.168.0.1 false verify_n2 192.168.0.2 false verify_mpr_set empty verify_mpr_selector_set empty verify_mid_node_count 0 verify_tc_origin_count 0 verify_routing_table_size 0 """ print >>fp, command if not fp.close(): return True else: return False ############################################################################# def test_c5_partial(verbose, valgrind): """ Simulate five OLSR nodes A-B-C-D-E in a subgraph of C[5] which misses one link. Each node has two interfaces. The main address of each node is configured to be the lowest interface address. """ ############################################### # # 192.0.2.3 C 192.0.3.3 # o # / \ # / \ # 192.0.2.2 / \ 192.0.3.4 # B o o D # 192.0.1.2 | | 192.0.4.4 # | | # | | # 192.0.1.1 | | 192.0.4.5 # A o o E # n/c 192.0.5.1 192.0.5.5 n/c # ############################################### fp = start_routing_interactive(verbose, valgrind) command = """ # Simplify the link costing by adopting a willingness of 6 (HIGH). # Only advertise MPR selectors in TC messages. # TOP_HOLD_TIME is 3*4 = 12 set_default_willingness 6 set_default_tc_redundancy 0 set_default_tc_interval 3 create 192.0.1.1 192.0.5.1 create 192.0.1.2 192.0.2.2 create 192.0.2.3 192.0.3.3 create 192.0.3.4 192.0.4.4 create 192.0.4.5 192.0.5.5 add_link 192.0.1.1 192.0.1.2 add_link 192.0.2.2 192.0.2.3 add_link 192.0.3.3 192.0.3.4 add_link 192.0.4.4 192.0.4.5 wait 30 ####################################################### # N2 verification select 192.0.1.1 verify_n2 192.0.2.3 true verify_n2 192.0.3.4 false select 192.0.1.2 verify_n2 192.0.3.4 true verify_n2 192.0.4.5 false select 192.0.2.3 verify_n2 192.0.1.1 true verify_n2 192.0.4.5 true select 192.0.3.4 verify_n2 192.0.1.1 false verify_n2 192.0.1.2 true select 192.0.4.5 verify_n2 192.0.1.2 false verify_n2 192.0.2.3 true ####################################################### # Cleanup remove_all_links wait 15 verify_all_link_state_empty """ print >>fp, command if not fp.close(): return True else: return False ############################################################################# def test_c5_full(verbose, valgrind): """ Simulate five OLSR nodes A-B-C-D-E in the cyclic graph C[5]. Each node has two interfaces. The main address of each node is configured to be the lowest interface address. """ ############################################### # # 192.0.2.3 C 192.0.3.3 # o # / \ # / \ # 192.0.2.2 / \ 192.0.3.4 # B o o D # 192.0.1.2 | | 192.0.4.4 # | | # | | # 192.0.1.1 | | 192.0.4.5 # A o-------o E # 192.0.5.1 192.0.5.5 # ############################################### fp = start_routing_interactive(verbose, valgrind) command = """ # Simplify the link costing by adopting a willingness of 6 (HIGH). # Only advertise MPR selectors in TC messages. # TOP_HOLD_TIME is 3*4 = 12 set_default_willingness 6 set_default_tc_redundancy 0 set_default_tc_interval 3 create 192.0.1.1 192.0.5.1 create 192.0.1.2 192.0.2.2 create 192.0.2.3 192.0.3.3 create 192.0.3.4 192.0.4.4 create 192.0.4.5 192.0.5.5 add_link 192.0.1.1 192.0.1.2 add_link 192.0.2.2 192.0.2.3 add_link 192.0.3.3 192.0.3.4 add_link 192.0.4.4 192.0.4.5 add_link 192.0.5.5 192.0.5.1 wait 30 # N2 verification select 192.0.1.1 verify_n2 192.0.2.3 true verify_n2 192.0.3.4 true select 192.0.1.2 verify_n2 192.0.3.4 true verify_n2 192.0.4.5 true select 192.0.2.3 verify_n2 192.0.1.1 true verify_n2 192.0.4.5 true select 192.0.3.4 verify_n2 192.0.1.1 true verify_n2 192.0.1.2 true select 192.0.4.5 verify_n2 192.0.1.2 true verify_n2 192.0.2.3 true ####################################################### # Cleanup remove_all_links wait 15 verify_all_link_state_empty """ print >>fp, command if not fp.close(): return True else: return False ############################################################################# def test_c6_partial(verbose, valgrind): """ Simulate six OLSR nodes A-B-C-D-E-F in a subgraph of C[6] which misses one link. Each node has two interfaces. The main address of each node is configured to be the lowest interface address. """ ############################################### # # 192.0.6.1 n/c 192.0.6.6 # A F # 192.0.1.1 o o 192.0.5.6 # / \ # 192.0.1.2 / \ 192.0.5.5 # B o o E # 192.0.2.2 \ / 192.0.4.5 # \ / # 192.0.2.3 o-----------o 192.0.4.4 # C D # 192.0.3.3 192.0.3.4 # ############################################### fp = start_routing_interactive(verbose, valgrind) command = """ # Simplify the link costing by adopting a willingness of 6 (HIGH). # Only advertise MPR selectors in TC messages. # TOP_HOLD_TIME is 3*4 = 12 set_default_willingness 6 set_default_tc_redundancy 0 set_default_tc_interval 3 create 192.0.1.1 192.0.6.1 create 192.0.1.2 192.0.2.2 create 192.0.2.3 192.0.3.3 create 192.0.3.4 192.0.4.4 create 192.0.4.5 192.0.5.5 create 192.0.5.6 192.0.6.6 add_link 192.0.1.1 192.0.1.2 add_link 192.0.2.2 192.0.2.3 add_link 192.0.3.3 192.0.3.4 add_link 192.0.4.4 192.0.4.5 add_link 192.0.5.5 192.0.5.6 wait 30 ####################################################### # N2 verification select 192.0.1.1 verify_n2 192.0.2.3 true verify_n2 192.0.4.5 false select 192.0.1.2 verify_n2 192.0.3.4 true verify_n2 192.0.5.6 false select 192.0.2.3 verify_n2 192.0.1.1 true verify_n2 192.0.4.5 true select 192.0.3.4 verify_n2 192.0.1.2 true verify_n2 192.0.5.6 true select 192.0.4.5 verify_n2 192.0.1.1 false verify_n2 192.0.2.3 true select 192.0.5.6 verify_n2 192.0.1.2 false verify_n2 192.0.3.4 true ####################################################### # Cleanup remove_all_links wait 15 verify_all_link_state_empty """ print >>fp, command if not fp.close(): return True else: return False ############################################################################# def test_c6_full(verbose, valgrind): """ Simulate six OLSR nodes A-B-C-D-E-F in a subgraph of C[6] which misses one link. Each node has two interfaces. The main address of each node is configured to be the lowest interface address. """ ############################################### # # 192.0.6.1 192.0.6.6 # A F # 192.0.1.1 o-----------o 192.0.5.6 # / \ # 192.0.1.2 / \ 192.0.5.5 # B o o E # 192.0.2.2 \ / 192.0.4.5 # \ / # 192.0.2.3 o-----------o 192.0.4.4 # C D # 192.0.3.3 192.0.3.4 # ############################################### fp = start_routing_interactive(verbose, valgrind) command = """ # Simplify the link costing by adopting a willingness of 6 (HIGH). # Only advertise MPR selectors in TC messages. # TOP_HOLD_TIME is 3*4 = 12 set_default_willingness 6 set_default_tc_redundancy 0 set_default_tc_interval 3 create 192.0.1.1 192.0.6.1 create 192.0.1.2 192.0.2.2 create 192.0.2.3 192.0.3.3 create 192.0.3.4 192.0.4.4 create 192.0.4.5 192.0.5.5 create 192.0.5.6 192.0.6.6 add_link 192.0.1.1 192.0.1.2 add_link 192.0.2.2 192.0.2.3 add_link 192.0.3.3 192.0.3.4 add_link 192.0.4.4 192.0.4.5 add_link 192.0.5.5 192.0.5.6 add_link 192.0.6.6 192.0.6.1 wait 30 ####################################################### # N2 verification select 192.0.1.1 verify_n2 192.0.2.3 true verify_n2 192.0.4.5 false select 192.0.1.2 verify_n2 192.0.3.4 true verify_n2 192.0.5.6 false select 192.0.2.3 verify_n2 192.0.1.1 true verify_n2 192.0.4.5 true select 192.0.3.4 verify_n2 192.0.1.2 true verify_n2 192.0.5.6 true select 192.0.4.5 verify_n2 192.0.1.1 false verify_n2 192.0.2.3 true select 192.0.5.6 verify_n2 192.0.1.2 false verify_n2 192.0.3.4 true ####################################################### # Cleanup remove_all_links wait 15 verify_all_link_state_empty """ print >>fp, command if not fp.close(): return True else: return False ############################################################################# def test_7up(verbose, valgrind): """ Simulate six OLSR nodes A-B-C-D-E-F, and an intermittent visitor G. All nodes have 3 interfaces. The main address of each node is the lowest interface address. Nodes are numbered x:1-8. Links are numbered n:1-10 in 192.0.n.x. 9,10 are allocated to G. G has intermittent connectivity to F and is not present initially. A and D are connected to each other using their third interface; the others may be regarded as omnidirectional antennae which G uses to roam. """ ############################################### # 7up test. # # A G # o -o- # /|\ / # 1 / | \ 6 . 8 # x / | \ . # \ / | \ / # B o | o F # | | | # 2 | 7| | 5 # | | | # | | | # C o | o E # / \ | / \ # x \ | / x # 3 \ | / 4 # \|/ # o # D # # Key: o - node # /|\ - reliable link # x - unconnected link # . - intermittently connected link # ############################################### fp = start_routing_interactive(verbose, valgrind) command = """ # Simplify the link costing by adopting a willingness of 6 (HIGH). # Only advertise MPR selectors in TC messages. # TOP_HOLD_TIME is 3*4 = 12 set_default_willingness 6 set_default_tc_redundancy 0 set_default_tc_interval 3 # A create 192.0.1.1 192.0.6.1 192.0.7.1 create 192.0.1.2 192.0.2.2 192.0.8.2 create 192.0.2.3 192.0.3.3 192.0.8.3 # D create 192.0.3.4 192.0.4.4 192.0.7.4 create 192.0.4.5 192.0.5.5 192.0.8.5 # F create 192.0.5.6 192.0.6.6 192.0.8.6 # Visitor G. create 192.0.8.7 192.0.9.7 192.0.9.8 add_link 192.0.1.1 192.0.1.2 add_link 192.0.2.2 192.0.2.3 add_link 192.0.3.3 192.0.3.4 add_link 192.0.4.4 192.0.4.5 add_link 192.0.5.5 192.0.5.6 add_link 192.0.6.6 192.0.6.1 # A-D add_link 192.0.7.1 192.0.7.4 wait 30 ####################################################### # N2 verification; no major changes from C[6]. select 192.0.1.1 verify_n2 192.0.2.3 true verify_n2 192.0.4.5 true select 192.0.1.2 verify_n2 192.0.3.4 true verify_n2 192.0.5.6 true select 192.0.2.3 verify_n2 192.0.1.1 true verify_n2 192.0.4.5 true select 192.0.3.4 verify_n2 192.0.1.2 true verify_n2 192.0.5.6 true select 192.0.4.5 verify_n2 192.0.1.1 true verify_n2 192.0.2.3 true select 192.0.5.6 verify_n2 192.0.1.2 true verify_n2 192.0.3.4 true # TODO: Introduce G and make sure E, A learn about G via N2, # and that the others learn about G via TC. # TODO: Verify TC which gets really interesting here. ####################################################### # Cleanup remove_all_links wait 15 verify_all_link_state_empty """ print >>fp, command if not fp.close(): return True else: return False ############################################################################# def main(): def usage(): us = \ "usage: %s [-h|--help] [-g|--valgrind] [-v|--verbose] " \ "[-t|--test] [-b|--bad]" print us % sys.argv[0] try: opts, args = getopt.getopt(sys.argv[1:], "hgvt:b", \ ["help", \ "valgrind", \ "verbose", \ "test=", \ "bad", \ ]) except getopt.GetoptError: usage() sys.exit(1) bad = False tests = [] verbose = False valgrind = False for o, a in opts: if o in ("-h", "--help"): usage() sys.exit() if o in ("-v", "--verbose"): verbose = True if o in ("-g", "--valgrind"): valgrind = True if o in ("-t", "--test"): tests.append(a) if o in ("-b", "--bad"): bad = True if not tests: for i in TESTS: if bad != i[1]: tests.append(i[0]) print tests for i in tests: protocol = 'unknown' for j in TESTS: if j[0] == i: if len(j) > 2: protocol = j[2] test = i + '(verbose, valgrind)' print 'Running: ' + i, if not eval(test): print "FAILED" sys.exit(-1) else: print sys.exit(0) main() # Local Variables: # mode: python # py-indent-offset: 4 # End: xorp/contrib/olsr/SConscript0000664000076400007640000000672511631506571016330 0ustar greearbgreearb# Copyright (c) 2009-2011 XORP, Inc and Others # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # $XORP$ # FIXME: tests import os Import('env') env = env.Clone() is_shared = env.has_key('SHAREDLIBS') subdirs = ['tools'] SConscript(dirs = subdirs, exports = 'env') env.AppendUnique(CPPPATH = [ '#', '$BUILDDIR', ]) env.AppendUnique(LIBPATH = [ '$BUILDDIR/libxorp', '$BUILDDIR/libcomm', '$BUILDDIR/libxipc', '$BUILDDIR/libproto', '$BUILDDIR/libfeaclient', '$BUILDDIR/policy/backend', '$BUILDDIR/policy/common', '$BUILDDIR/xrl/interfaces', '$BUILDDIR/xrl/targets', '$BUILDDIR/mrt', '.' ]) env.AppendUnique(LIBS = [ 'olsr', 'olsrxrl', 'xst_olsr4', 'xorp_policy_backend', 'xorp_policy_common', 'xorp_fea_client', 'xif_rib', 'xif_socket4', 'xif_finder_event_notifier', 'xif_fea_ifmgr_mirror', 'xif_fea_ifmgr_replicator', 'xif_profile_client', 'xif_fea_rawpkt4', 'xst_fea_ifmgr_mirror', 'xorp_ipc', 'xorp_core', 'xorp_proto', 'xorp_comm', ]) if not is_shared: env.AppendUnique(LIBS = [ "crypto", ]) if not (env.has_key('mingw') and env['mingw']): env.AppendUnique(LIBS = [ "rt", ]) if (env.has_key('mingw') and env['mingw']): env.AppendUnique(LIBS = [ 'ws2_32', 'iphlpapi', # 'mprapi', 'regex', 'winmm', ]) env.Append(LIBS = ['xorp_core']) if not (env.has_key('disable_ipv6') and env['disable_ipv6']): env.AppendUnique(LIBS = [ 'xif_fea_rawpkt6', ]) env.Replace(RPATH = [ env.Literal(env['xorp_module_rpath']) ]) ### libolsr libolsrsrcs = [ 'external.cc', 'face.cc', 'face_manager.cc', 'link.cc', 'message.cc', 'neighbor.cc', 'neighborhood.cc', 'olsr.cc', 'olsr_types.cc', 'policy_varrw.cc', 'route_manager.cc', 'topology.cc', 'twohop.cc', ] if is_shared: libolsr = env.SharedLibrary(target = 'libolsr', source = libolsrsrcs, LIBS = '') env.Alias('install', env.InstallLibrary(env['xorp_libdir'], libolsr)) else: libolsr = env.StaticLibrary(target = 'libolsr', source = libolsrsrcs, LIBS = '') ### libolsrxrl libolsrxrlsrcs = [ 'xrl_io.cc', 'xrl_port.cc', 'xrl_queue.cc', 'xrl_target.cc', ] if is_shared: libolsrxrl = env.SharedLibrary(target = 'libolsrxrl', source = libolsrxrlsrcs, LIBS = '') env.Alias('install', env.InstallLibrary(env['xorp_libdir'], libolsrxrl)) else: libolsrxrl = env.StaticLibrary(target = 'libolsrxrl', source = libolsrxrlsrcs, LIBS = '') ### olsr4 olsr4srcs = [ 'xorp_olsr.cc', ] olsr4 = env.Program(target = 'xorp_olsr4', source = olsr4srcs) env.Alias('install', env.InstallProgram(env['xorp_moduledir'], olsr4)) Default(libolsr, libolsrxrl, olsr4) xorp/contrib/olsr/xorp_olsr4_wrapper.sh0000664000076400007640000000157011421137511020505 0ustar greearbgreearb#!/bin/sh # XXX assumes we are run from top level WRAPPED_CMD="olsr/xorp_olsr4" LOGFILE="/tmp/xorp_olsr4.log" UHOME="/home/bms" VALGRIND="$(which valgrind)" VALGRIND_OPTS=" \ --tool=memcheck \ --leak-check=yes \ --leak-resolution=high \ --num-callers=10 \ --show-reachable=yes \ --demangle=no \ --suppressions=$UHOME/.valgrind/xorp.supp \ --error-limit=no \ --verbose \ " #set -x UNAME=$(uname) if [ ${UNAME} = "FreeBSD" ]; then VALGRIND_OPTS="${VALGRIND_OPTS} \ --suppressions=$UHOME/.valgrind/fbsd.supp" fi VERS="$(${VALGRIND} --version)" if [ $? -ne 0 ]; then # old valgrind VALGRIND_OPTS="${VALGRIND_OPTS} --logfile=${LOGFILE}" else # new valgrind VALGRIND_OPTS="${VALGRIND_OPTS} --log-file=${LOGFILE}" fi if [ "${VALGRIND}" ]; then CMD="${VALGRIND} ${VALGRIND_OPTS} ${WRAPPED_CMD}" else CMD="${WRAPPED_CMD}" fi exec ${CMD} xorp/contrib/olsr/exceptions.hh0000664000076400007640000001247411421137511017005 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8 sw=4: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/contrib/olsr/exceptions.hh,v 1.3 2008/10/02 21:56:34 bms Exp $ #ifndef __OLSR_EXCEPTIONS_HH__ #define __OLSR_EXCEPTIONS_HH__ /* * @short The exception thrown when an operation on an external * route fails. */ class BadExternalRoute : public XorpReasonedException { public: BadExternalRoute(const char* file, size_t line, const string& init_why = "") : XorpReasonedException("OlsrBadExternalRoute", file, line, init_why) {} }; /** * @short The exception thrown when an operation on an OLSR * interface fails. */ class BadFace : public XorpReasonedException { public: BadFace(const char* file, size_t line, const string& init_why = "") : XorpReasonedException("OlsrBadFace", file, line, init_why) {} }; /** * @short The exception thrown when an operation on a link code fails. * * Usually this means the fields provided were invalid. */ class BadLinkCode : public XorpReasonedException { public: BadLinkCode(const char* file, size_t line, const string& init_why = "") : XorpReasonedException("OlsrBadLinkCode", file, line, init_why) {} }; /** * @short The exception thrown when an operation on a logical link fails. */ class BadLogicalLink : public XorpReasonedException { public: BadLogicalLink(const char* file, size_t line, const string& init_why = "") : XorpReasonedException("OlsrBadLogicalLink", file, line, init_why) {} }; /** * @short The exception thrown when no suitable link to a one-hop * neighbor exists. */ class BadLinkCoverage : public XorpReasonedException { public: BadLinkCoverage(const char* file, size_t line, const string& init_why = "") : XorpReasonedException("OlsrBadLinkCoverage", file, line, init_why) {} }; /** * @short The exception thrown when an operation on a MID entry fails. */ class BadMidEntry : public XorpReasonedException { public: BadMidEntry(const char* file, size_t line, const string& init_why = "") : XorpReasonedException("OlsrBadMidEntry", file, line, init_why) {} }; /** * @short The exception thrown when an operation on a one-hop * neighbor fails. */ class BadNeighbor : public XorpReasonedException { public: BadNeighbor(const char* file, size_t line, const string& init_why = "") : XorpReasonedException("OlsrBadNeighbor", file, line, init_why) {} }; /** * @short The exception thrown when an operation on a topology control * entry fails. */ class BadTopologyEntry : public XorpReasonedException { public: BadTopologyEntry(const char* file, size_t line, const string& init_why = "") : XorpReasonedException("OlsrBadTopologyEntry", file, line, init_why) {} }; /** * @short The exception thrown when no suitable link to a two-hop * neighbor exists. * * In particular it may be thrown during MPR calculation, if an * inconsistency is detected in the set of MPRs covering a two-hop neighbor * which was calculated to be reachable. */ class BadTwoHopCoverage : public XorpReasonedException { public: BadTwoHopCoverage(const char* file, size_t line, const string& init_why = "") : XorpReasonedException("OlsrBadTwoHopCoverage", file, line, init_why) {} }; /** * @short The exception thrown when an operation on a link in the * two-hop neighborhood fails. */ class BadTwoHopLink : public XorpReasonedException { public: BadTwoHopLink(const char* file, size_t line, const string& init_why = "") : XorpReasonedException("OlsrBadTwoHopLink", file, line, init_why) {} }; /** * @short The exception thrown when an operation on a two-hop * neighbor fails. */ class BadTwoHopNode : public XorpReasonedException { public: BadTwoHopNode(const char* file, size_t line, const string& init_why = "") : XorpReasonedException("OlsrBadTwoHopNode", file, line, init_why) {} }; /** * @short The exception thrown when the decoding or encoding of * a Message fails. */ class InvalidMessage : public XorpReasonedException { public: InvalidMessage(const char* file, size_t line, const string& init_why = "") : XorpReasonedException("OlsrInvalidMessage", file, line, init_why) {} }; /** * @short The exception thrown when the decoding or encoding of * a LinkTuple inside a HelloMessage fails. */ class InvalidLinkTuple : public XorpReasonedException { public: InvalidLinkTuple(const char* file, size_t line, const string& init_why = "") : XorpReasonedException("OlsrInvalidLinkTuple", file, line, init_why) {} }; #endif // __OLSR_EXCEPTIONS_HH__ xorp/contrib/olsr/policy_varrw.hh0000664000076400007640000000437611421137511017346 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8 sw=4: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/contrib/olsr/policy_varrw.hh,v 1.4 2008/10/02 21:56:35 bms Exp $ #ifndef __OLSR_POLICY_VARRRW_HH__ #define __OLSR_POLICY_VARRRW_HH__ #include "policy/backend/single_varrw.hh" #include "policy/common/element_factory.hh" #include "policy/backend/policy_filters.hh" #include "policy/backend/policytags.hh" class OlsrVarRW : public SingleVarRW { public: // Some of the following should be treated read-only when // importing routes into OLSR. enum { VAR_NETWORK = VAR_PROTOCOL, // Destination (RO) VAR_NEXTHOP, // Next-hop (RW) VAR_METRIC, // Metric (RW) VAR_VTYPE, // Which part of OLSR this route came from. (RO) VAR_ORIGINATOR, // OLSR main address of the advertising node. (RO) VAR_MAINADDR, // OLSR main address of the destination. (RO) VAR_OLSRMAX // must be last. }; OlsrVarRW(IPv4Net& network, IPv4& nexthop, uint32_t& metric, IPv4& originator, IPv4& main_addr, uint32_t vtype, PolicyTags& policytags); // SingleVarRW inteface: void start_read(); Element* single_read(const Id& id); void single_write(const Id& id, const Element& e); private: IPv4Net& _network; IPv4& _nexthop; uint32_t& _metric; IPv4& _originator; IPv4& _main_addr; uint32_t& _vtype; // actually OlsrTypes::VertexType. PolicyTags& _policytags; ElementFactory _ef; }; #endif // __OLSR_POLICY_VARRRW_HH__ xorp/contrib/olsr/README0000664000076400007640000000174711421137511015164 0ustar greearbgreearb# # $XORP: xorp/contrib/olsr/README,v 1.2 2008/05/09 16:09:38 bms Exp $ # Optimized Link State Routing (OLSR) Implementation ================================================== This directory contains the XORP implementation of the OLSR protocol, version 1, as specified in RFC 3626. This work was generously funded by CenGen, Inc. Documentation ============= Full user documentation is available in TeX format, in the xorp/docs/olsr directory. This may be used to produce PDF format files. Building ======== OLSR for XORP should now be built as part of a main XORP build. To enable the build and installation of OLSR, pass the "--with-olsr" option when running "configure". Testing ======= To build and run regression tests, use "gmake check". Status ====== September 2007: Initial development. December 2007: Interim code drop of RFC-compliant OLSR with no XRL support. January 2008: Preliminary release of RFC-compliant OLSR for IPv4. April 2008: Merge to XORP mainline CVS repository. xorp/contrib/olsr/topology.cc0000664000076400007640000007251611703345405016477 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8 sw=4: // Copyright (c) 2001-2012 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "olsr_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/callback.hh" #include "libxorp/ipv4.hh" #include "libxorp/ipv4net.hh" #include "libxorp/status_codes.h" #include "libxorp/service.hh" #include "libxorp/eventloop.hh" // #define DEBUG_LOGGING // #define DEBUG_FUNCTION_NAME // #define DETAILED_DEBUG #include "olsr.hh" #include "link.hh" #include "neighbor.hh" #include "neighborhood.hh" #include "topology.hh" TopologyManager::TopologyManager(Olsr& olsr, EventLoop& eventloop, FaceManager& fm, Neighborhood& nh) : _olsr(olsr), _eventloop(eventloop), _fm(fm), _nh(nh), _rm(0), _next_mid_id(1), _next_tcid(1) { _nh.set_topology_manager(this); _fm.add_message_cb(callback(this, &TopologyManager::event_receive_tc)); _fm.add_message_cb(callback(this, &TopologyManager::event_receive_mid)); } TopologyManager::~TopologyManager() { _fm.delete_message_cb(callback(this, &TopologyManager::event_receive_tc)); _fm.delete_message_cb(callback(this, &TopologyManager::event_receive_mid)); clear_tc_entries(); clear_mid_entries(); XLOG_ASSERT(_topology.empty()); XLOG_ASSERT(_mids.empty()); } /* * TC entries. */ void TopologyManager::update_tc_entry(const IPv4& dest_addr, const IPv4& origin_addr, const uint16_t distance, const uint16_t ansn, const TimeVal& vtime, bool& is_created) throw(BadTopologyEntry) { debug_msg("DestAddr %s OriginAddr %s Distance %u ANSN %u Expiry %s\n", cstring(dest_addr), cstring(origin_addr), XORP_UINT_CAST(distance), XORP_UINT_CAST(ansn), cstring(vtime)); OlsrTypes::TopologyID tcid = OlsrTypes::UNUSED_TOPOLOGY_ID; bool is_found = false; TcDestMap::iterator ii = _tc_destinations.find(dest_addr); for (; ii != _tc_destinations.end(); ii++) { tcid = (*ii).second; if (_topology[tcid]->destination() == dest_addr && _topology[tcid]->lasthop() == origin_addr) { is_found = true; break; } } TimeVal now; _eventloop.current_time(now); if (is_found) { TopologyEntry* tc = _topology[tcid]; // 9.5, 4.1: Update holding time of the existing tuple in the // topology set. tc->update_timer(vtime); // Update the distance of this TC entry, as the hop-count may // have changed due to other changes in topology. update_tc_distance(tc, distance); } else { // 9.5, 4.2: Record a new tuple in the topology set. // May throw BadTopologyEntry exception. tcid = add_tc_entry(dest_addr, origin_addr, distance, ansn, vtime); debug_msg("%s: Added TC entry %u.\n", cstring(_fm.get_main_addr()), XORP_UINT_CAST(tcid)); } is_created = !is_found; } OlsrTypes::TopologyID TopologyManager::add_tc_entry(const IPv4& dest_addr, const IPv4& origin_addr, const uint16_t distance, const uint16_t ansn, const TimeVal& expiry_time) throw(BadTopologyEntry) { debug_msg("DestAddr %s OriginAddr %s Distance %u ANSN %u Expiry %s\n", cstring(dest_addr), cstring(origin_addr), XORP_UINT_CAST(distance), XORP_UINT_CAST(ansn), cstring(expiry_time)); OlsrTypes::TopologyID tcid = _next_tcid++; // Throw an exception if we overflowed the TopologyID space. if (0 != _topology.count(tcid)) { xorp_throw(BadTopologyEntry, c_format("Mapping for TopologyID %u already exists", XORP_UINT_CAST(tcid))); } _topology[tcid] = new TopologyEntry(_eventloop, this, tcid, dest_addr, origin_addr, distance, ansn, expiry_time); // Add the new link to the multimaps. _tc_distances.insert(make_pair(distance, tcid)); _tc_destinations.insert(make_pair(dest_addr, tcid)); _tc_lasthops.insert(make_pair(origin_addr, tcid)); return tcid; } bool TopologyManager::delete_tc_entry(const OlsrTypes::TopologyID tcid) { debug_msg("Tcid %u\n", XORP_UINT_CAST(tcid)); map::iterator ii = _topology.find(tcid); if (ii == _topology.end()) return false; TopologyEntry* t = (*ii).second; // Remove from destination map. pair ra = _tc_destinations.equal_range(t->destination()); for (TcDestMap::iterator jj = ra.first; jj != ra.second; ) { if ((*jj).second == tcid) { _tc_destinations.erase(jj); break; } jj++; } // Remove from lasthop map. pair rb = _tc_lasthops.equal_range(t->lasthop()); for (TcLasthopMap::iterator kk = rb.first; kk != rb.second; ) { if ((*kk).second == tcid) { _tc_lasthops.erase(kk); break; } kk++; } // Remove from distance map. pair rc = _tc_distances.equal_range(t->distance()); for (TcDistanceMap::iterator ll = rc.first; ll != rc.second; ) { if ((*ll).second == t->id()) { _tc_distances.erase(ll); break; } ll++; } // Remove from ID map. _topology.erase(ii); delete t; // May be called from destructor. if (_rm) _rm->schedule_route_update(); return true; } // XXX: This is a definite candidate for a Boost multi_index container. void TopologyManager::clear_tc_entries() { while (! _topology.empty()) delete_tc_entry((*_topology.begin()).first); } bool TopologyManager::apply_tc_ansn(const uint16_t ansn, const IPv4& origin_addr) { debug_msg("ANSN %u Origin %s\n", XORP_UINT_CAST(ansn), cstring(origin_addr)); // The ANSN is regarded as valid until proven otherwise. bool is_valid = true; // Find the tuples, if any, recorded from the origin. TcLasthopMap::iterator ii, jj; ii = _tc_lasthops.find(origin_addr); while (is_valid && ii != _tc_lasthops.end()) { jj = ii++; OlsrTypes::TopologyID tcid = (*jj).second; if ( (*jj).first == origin_addr && _topology[tcid]->seqno() != ansn ) { // 9.5, 2: If any tuple in the topology set has T_seq > ANSN, // then the TC message was received out of order and MUST be // silently discarded. is_valid = false; } else { // 9.5, 3: All tuples in the topology set where T_seq < ANSN // MUST be removed from the topology set. delete_tc_entry(tcid); } } return is_valid; } OlsrTypes::TopologyID TopologyManager::get_topologyid(const IPv4& dest_addr, const IPv4& lasthop_addr) throw(BadTopologyEntry) { debug_msg("DestAddr %s LasthopAddr %s\n", cstring(dest_addr), cstring(lasthop_addr)); bool is_found = false; OlsrTypes::TopologyID tcid; multimap::const_iterator ii = _tc_destinations.find(dest_addr); while (ii != _tc_destinations.end()) { tcid = (*ii).second; if (_topology[tcid]->lasthop() == lasthop_addr) { is_found = true; break; } ii++; } if (!is_found) { xorp_throw(BadTopologyEntry, c_format("No mapping for %s exists", cstring(dest_addr))); } return tcid; } const TopologyEntry* TopologyManager::get_topology_entry_by_id(const OlsrTypes::TopologyID tcid) const throw(BadTopologyEntry) { TcIdMap::const_iterator ii = _topology.find(tcid); if (ii == _topology.end()) { xorp_throw(BadTopologyEntry, c_format("No mapping for %u exists", XORP_UINT_CAST(tcid))); } return (*ii).second; } void TopologyManager::get_topology_list(list& tclist) const { TcIdMap::const_iterator ii; for (ii = _topology.begin(); ii != _topology.end(); ii++) tclist.push_back((*ii).first); } vector TopologyManager::get_tc_neighbor_set(const IPv4& origin_addr, uint16_t& ansn) throw(BadTopologyEntry) { debug_msg("MyMainAddr %s Origin %s\n", cstring(_fm.get_main_addr()), cstring(origin_addr)); size_t found_tc_count = 0; vector addrs; pair rl = _tc_lasthops.equal_range(origin_addr); for (TcLasthopMap::iterator ii = rl.first; ii != rl.second; ii++) { TopologyEntry* t = _topology[(*ii).second]; XLOG_ASSERT(t != 0); // paranoia // If this is the first match, record the ANSN. if (ii == rl.first) ansn = t->seqno(); #ifdef DETAILED_DEBUG // Invariant: the ANSN of all entries for this peer must be the same. XLOG_ASSERT(t->seqno() == ansn); #endif addrs.push_back(t->destination()); ++found_tc_count; } if (found_tc_count == 0) { // Check to see if this origin was recorded as having an empty // neighbor set. // OLSR nodes originating TC broadcasts MUST send an // empty TC message with a new ANSN to invalidate any existing // topology information when they stop being selected as MPRs. TcFinalSeqMap::const_iterator ii = _tc_final_seqnos.find(origin_addr); if (ii != _tc_final_seqnos.end()) { ansn = (*ii).second; debug_msg("%s: found final ansn %u for %s\n", cstring(_fm.get_main_addr()), XORP_UINT_CAST(ansn), cstring(origin_addr)); return addrs; } // The origin is not known to TopologyManager. // This is a distinct condition from "there were no entries // for the given origin". xorp_throw(BadTopologyEntry, c_format("No mapping for %s exists", cstring(origin_addr))); } return addrs; } uint16_t TopologyManager::get_tc_distance(const IPv4& origin_addr, const IPv4& dest_addr) throw(BadTopologyEntry) { debug_msg("MyMainAddr %s Origin %s Dest %s\n", cstring(_fm.get_main_addr()), cstring(origin_addr), cstring(dest_addr)); bool is_found = false; uint16_t distance = 0; pair rl = _tc_lasthops.equal_range(origin_addr); for (TcLasthopMap::iterator ii = rl.first; ii != rl.second; ii++) { TopologyEntry* t = _topology[(*ii).second]; if (t->destination() == dest_addr) { is_found = true; distance = t->distance(); break; } } if (! is_found) { // No TC entry was found matching that origin and destination. xorp_throw(BadTopologyEntry, c_format("No mapping for (%s, %s) exists", cstring(origin_addr), cstring(dest_addr))); } return distance; } size_t TopologyManager::get_tc_lasthop_count_by_dest(const IPv4& dest_addr) { debug_msg("MyMainAddr %s Dest %s\n", cstring(_fm.get_main_addr()), cstring(dest_addr)); size_t lasthop_count = 0; pair rl = _tc_destinations.equal_range(dest_addr); for (TcDestMap::iterator ii = rl.first; ii != rl.second; ii++) { ++lasthop_count; } return lasthop_count; } size_t TopologyManager::tc_node_count() const { size_t unique_key_count = 0; // Count the number of unique keys (main addresses) in the lasthop map. TcLasthopMap::const_iterator ii; for (ii = _tc_lasthops.begin(); ii != _tc_lasthops.end(); ii = _tc_lasthops.upper_bound((*ii).first)) { unique_key_count++; } return unique_key_count; } void TopologyManager::update_tc_distance(TopologyEntry* tc, uint16_t distance) { if (tc->distance() == distance) return; // If the TC entry is already recorded for this distance, remove it. pair range = _tc_distances.equal_range(distance); for (TcDistanceMap::iterator ii = range.first; ii != range.second; ) { if ((*ii).second == tc->id()) { _tc_distances.erase(ii); break; } ii++; } // Update the TC's distance, and rewire it into the distance map. tc->set_distance(distance); _tc_distances.insert(make_pair(distance, tc->id())); #ifdef DETAILED_DEBUG // Invariant: the ID of the topology entry must appear once and // only once in the distances map. assert_tc_distance_is_unique(tc->id()); #endif } void TopologyManager::assert_tc_distance_is_unique(const OlsrTypes::TopologyID tcid) throw(BadTopologyEntry) { #ifdef DETAILED_DEBUG size_t id_seen_count = 0; multimap::const_iterator ii; for (ii = _tc_distances.begin(); ii != _tc_distances.end(); ii++) { if (tcid == (*ii).second) { id_seen_count++; } } if (id_seen_count != 1) { xorp_throw(BadTopologyEntry, c_format("Duplicate TopologyID %u in _tc_distances: " "appeared %u times.", XORP_UINT_CAST(tcid), XORP_UINT_CAST(id_seen_count))); } #else UNUSED(tcid); #endif // DETAILED_DEBUG } void TopologyManager::assert_tc_ansn_is_identical(const IPv4& origin_addr) throw(BadTopologyEntry) { #ifdef DETAILED_DEBUG bool is_origin_found = false; bool is_seq_match_failed = false; uint16_t first_ansn = 0; TopologyEntry* t = 0; TcIdMap::const_iterator ii; for (ii = _topology.begin(); ii != _topology.end(); ii++) { t = (*ii).second; if (t->lasthop() == origin_addr) { if (! is_origin_found) { // First match; record first sequence number seen // for this origin. first_ansn = t->seqno(); is_origin_found = true; } else { // We've seen at least one entry for this origin before. // Do the sequence numbers match, is the ANSN consistent? if (t->seqno() != first_ansn) { is_seq_match_failed = true; break; } } } } if (is_origin_found && is_seq_match_failed) { xorp_throw(BadTopologyEntry, c_format("Inconsistent ANSN (%s, %s, %u) in TopologyID %u", cstring(t->lasthop()), cstring(t->destination()), XORP_UINT_CAST(t->seqno()), XORP_UINT_CAST(t->id()))); } #else UNUSED(origin_addr); #endif } /* * MID entries. */ void TopologyManager::update_mid_entry(const IPv4& main_addr, const IPv4& iface_addr, const uint16_t distance, const TimeVal& vtime, bool& is_mid_created) throw(BadMidEntry) { debug_msg("MyMainAddr %s MainAddr %s IfaceAddr %s Distance %u Vtime %s\n", cstring(_fm.get_main_addr()), cstring(main_addr), cstring(iface_addr), XORP_UINT_CAST(distance), cstring(vtime)); is_mid_created = false; // Do not add redundant MID entries. if (main_addr == iface_addr) { debug_msg("Rejecting MID entry from %s for its main address.\n", cstring(main_addr)); XLOG_TRACE(_olsr.trace()._input_errors, "Rejecting MID entry from %s for its main address.", cstring(main_addr)); return; } bool is_found = false; MidEntry* mie = 0; // Look for MID entry's interface address in existing address map, // given origin address. pair range = _mid_addr.equal_range(main_addr); for (MidAddrMap::iterator ii = range.first; ii != range.second; ii++) { mie = _mids[(*ii).second]; if (mie->iface_addr() == iface_addr) { is_found = true; break; } } if (is_found) { // Section 5.4, 2.1: Update existing MID tuple. mie->update_timer(vtime); mie->set_distance(distance); } else { // Section 5.4, 2.2: Create new MID tuple. add_mid_entry(main_addr, iface_addr, distance, vtime); is_mid_created = true; } } void TopologyManager::add_mid_entry(const IPv4& main_addr, const IPv4& iface_addr, const uint16_t distance, const TimeVal& vtime) throw(BadMidEntry) { debug_msg("MyMainAddr %s MainAddr %s IfaceAddr %s Distance %u Vtime %s\n", cstring(_fm.get_main_addr()), cstring(main_addr), cstring(iface_addr), XORP_UINT_CAST(distance), cstring(vtime)); // Section 5.4, 2.2: Create new MID tuple. OlsrTypes::MidEntryID mid_id = _next_mid_id++; // Throw an exception if we overflow the MID ID space. if (0 != _mids.count(mid_id)) { xorp_throw(BadMidEntry, c_format("Mapping for %u already exists", XORP_UINT_CAST(mid_id))); } _mids[mid_id] = new MidEntry(_eventloop, this, mid_id, iface_addr, main_addr, distance, vtime); // Tot up another MID entry for this main address and MID entry combo. _mid_addr.insert(make_pair(main_addr, mid_id)); debug_msg("new MidEntryID %u\n", XORP_UINT_CAST(mid_id)); } bool TopologyManager::delete_mid_entry(const OlsrTypes::MidEntryID mid_id) { debug_msg("MyMainAddr %s MidEntryID %u\n", cstring(_fm.get_main_addr()), XORP_UINT_CAST(mid_id)); bool is_deleted = false; map::iterator ii = _mids.find(mid_id); if (ii != _mids.end()) { MidEntry* mie = (*ii).second; // Withdraw mid_id from multimap of MID IDs per primary address. pair range = _mid_addr.equal_range(mie->main_addr()); for (MidAddrMap::iterator jj = range.first; jj != range.second; jj++) { if ((*jj).second == mid_id) { // Only the first match by ID should be deleted. // Duplicates of the second key, mid_id, SHOULD NOT exist. _mid_addr.erase(jj); break; } } delete mie; _mids.erase(ii); is_deleted = true; debug_msg("MidEntryID %u was deleted.\n", XORP_UINT_CAST(mid_id)); } if (is_deleted) { // If any MID address entry was deleted, schedule a route // computation. // TODO: Find a way of optimizing the processing of MID entries // in this way, we should just be able to withdraw the MID-derived // routes for this entry in the routing table. if (_rm) _rm->schedule_route_update(); } return is_deleted; } void TopologyManager::clear_mid_entries() { map::iterator ii, jj; for (ii = _mids.begin(); ii != _mids.end(); ) { jj = ii++; delete (*jj).second; _mids.erase(jj); } } vector TopologyManager::get_mid_addresses(const IPv4& main_addr) { #if 0 debug_msg("%s MyMainAddr %s MainAddr %s\n", __func__, cstring(_fm.get_main_addr()) cstring(main_addr)); #endif vector addrs; pair range = _mid_addr.equal_range(main_addr); MidAddrMap::const_iterator ii; for (ii = range.first; ii != range.second; ii++) { MidEntry* mie = _mids[(*ii).second]; addrs.push_back(mie->iface_addr()); } return addrs; } // Only used by regression tests. uint16_t TopologyManager::get_mid_address_distance(const IPv4& main_addr, const IPv4& iface_addr) throw(BadMidEntry) { debug_msg("MyMainAddr %s MainAddr %s IfaceAddr %s\n", cstring(_fm.get_main_addr()), cstring(main_addr), cstring(iface_addr)); bool is_found = false; MidEntry* mie = 0; // Look up all matching interface addresses for main_addr, // and find the single matching MidEntry. pair range = _mid_addr.equal_range(main_addr); MidAddrMap::const_iterator ii; for (ii = range.first; ii != range.second; ii++) { mie = _mids[(*ii).second]; if (mie->iface_addr() == iface_addr) { is_found = true; break; } } if (! is_found) { xorp_throw(BadMidEntry, c_format("No mapping for (%s, %s) exists", cstring(main_addr), cstring(iface_addr))); } return mie->distance(); } IPv4 TopologyManager::get_main_addr_of_mid(const IPv4& mid_addr) throw(BadMidEntry) { MidEntry* mie = 0; bool is_found = false; map::const_iterator ii; for (ii = _mids.begin(); ii != _mids.end(); ii++) { mie = (*ii).second; if (mie->iface_addr() == mid_addr) { is_found = true; break; } } if (!is_found) { xorp_throw(BadMidEntry, c_format("No mapping for %s exists", cstring(mid_addr))); } return mie->main_addr(); } size_t TopologyManager::mid_node_count() const { size_t unique_key_count = 0; // Count the number of unique keys (main addresses) in the multimap. multimap::const_iterator ii; for (ii = _mid_addr.begin(); ii != _mid_addr.end(); ii = _mid_addr.upper_bound((*ii).first)) { unique_key_count++; } return unique_key_count; } const MidEntry* TopologyManager::get_mid_entry_by_id(const OlsrTypes::MidEntryID midid) const throw(BadTopologyEntry) { MidIdMap::const_iterator ii = _mids.find(midid); if (ii == _mids.end()) { xorp_throw(BadMidEntry, c_format("No mapping for %u exists", XORP_UINT_CAST(midid))); } return (*ii).second; } void TopologyManager::get_mid_list(list& midlist) const { MidIdMap::const_iterator ii; for (ii = _mids.begin(); ii != _mids.end(); ii++) midlist.push_back((*ii).first); } /* * RouteManager interaction. */ void TopologyManager::push_topology() { size_t tc_link_added_count = 0; XLOG_ASSERT(_rm != 0); // For all entries at each recorded radius in the topology set. TcDistanceMap::const_iterator ii, jj; for (ii = _tc_distances.begin(); ii != _tc_distances.end(); ) { // Look up the range of TC entries at the current radius. uint16_t hops = (*ii).first; // The following is equivalent to _tc_distances.upper_bound(hops); // equal_range() has a more efficient expansion. pair rd = _tc_distances.equal_range(hops); ii = rd.second; // Skip neighbors with redundant TC information; that is, those // which SHOULD have been covered by link state information in // the Neighborhood as they are less than 2 hops away. // We can't ignore TCs from two-hop neighbors because they may // know about other nodes which our neighbors don't. In any event // the SPF calculation will have screened those out. // Print detailed information about such hops if debugging. if (hops < 2) { debug_msg("%s: skipping %u redundant TC entries at distance %u\n", cstring(_fm.get_main_addr()), XORP_UINT_CAST(std::distance(rd.first, rd.second)), XORP_UINT_CAST(hops)); continue; } // 10, 3: The execution will stop if no new TC entry is recorded // in an iteration. if (0 == std::distance(rd.first, rd.second)) break; // For each link in the topology set at the current radius. for (jj = rd.first; jj != rd.second; jj++) { TopologyEntry* tc = _topology[(*jj).second]; // Notify RouteManager of the link's existence. // Duplicates covered by N1/N2 will return false. // If it cannot connect it with any node on the existing // boundary of the SPT topology, add_tc_link() will reject it // by returning false. if (true == _rm->add_tc_link(tc)) ++tc_link_added_count; } } debug_msg("%u TC entries pushed to SPT.\n", XORP_UINT_CAST(tc_link_added_count)); } /* * Event handlers. */ bool TopologyManager::event_receive_tc( Message* msg, const IPv4& remote_addr, const IPv4& local_addr) { TcMessage* tc = dynamic_cast(msg); if (0 == tc) return false; // not for me // Put this after the cast to skip false positives. debug_msg("[TC] MyMainAddr %s Src %s RecvIf %s\n", cstring(_fm.get_main_addr()), cstring(remote_addr), cstring(local_addr)); // 9.5.1 Sender of packet must be in symmetric 1-hop neighborhood. if (! _nh.is_sym_neighbor_addr(remote_addr)) { debug_msg("Rejecting TC message from %s via non-neighbor %s\n", cstring(msg->origin()), cstring(remote_addr)); XLOG_TRACE(_olsr.trace()._input_errors, "Rejecting TC message from %s via non-neighbor %s", cstring(msg->origin()), cstring(remote_addr)); return true; // consumed but invalid. } // Invariant: The above call should mean I reject TC messages // looped back to myself. XLOG_ASSERT(tc->origin() != _fm.get_main_addr()); // 9.5.1, 9.5.2 Evaluate ANSN. if (! apply_tc_ansn(tc->ansn(), tc->origin())) { debug_msg("Rejecting TC message from %s with old ANSN %u\n", cstring(msg->origin()), XORP_UINT_CAST(tc->ansn())); XLOG_TRACE(_olsr.trace()._input_errors, "Rejecting TC message from %s with old ANSN %u", cstring(msg->origin()), XORP_UINT_CAST(tc->ansn())); return true; } #ifdef DETAILED_DEBUG // Invariant: The ANSN of all TC entries from tc->origin() must // now be identical. assert_tc_ansn_is_identical(tc->origin()); #endif bool is_new_tc = false; // Determine distance of these TC entries. // TCs advertise neighbors which are 1 hop from the node which // originated the message. tc->hops() is not incremented until // forwarded, so add 1 here to take account of the actual distance. // However, the distance of the neighbors advertised in the TC is // one more than the distance of the neighbor itself, so add 2 hops. uint16_t distance = tc->hops() + 2; const vector& addrs = tc->neighbors(); vector::const_iterator ii; for (ii = addrs.begin(); ii != addrs.end(); ii++) { update_tc_entry((*ii).remote_addr(), tc->origin(), distance, tc->ansn(), tc->expiry_time(), is_new_tc); } // Maintain a list of the final sequence numbers seen from origins // with empty neighbor sets. TcFinalSeqMap::iterator jj = _tc_final_seqnos.find(tc->origin()); if (jj != _tc_final_seqnos.end()) _tc_final_seqnos.erase(jj); if (addrs.empty()) { _tc_final_seqnos.insert(make_pair(tc->origin(), tc->ansn())); } else { // Invariant: There must be no 'final' entry recorded if the // origin advertised any neighbors. XLOG_ASSERT(0 == _tc_final_seqnos.count(tc->origin())); } #ifdef DETAILED_DEBUG // Invariant: The ANSN of all TC entries from tc->origin() must // now be identical. assert_tc_ansn_is_identical(tc->origin()); #endif // A route computation will be necessary if apply_tc_ansn() // invalidated any entries or if any entries were added/updated. // It is possible to receive a TC w/o neighbors, so just assume // that the TC info changed if we get here. _rm->schedule_route_update(); _fm.forward_message(remote_addr, msg); return true; // consumed } void TopologyManager::event_tc_dead(const OlsrTypes::TopologyID tcid) { XLOG_ASSERT(0 != _topology.count(tcid)); delete_tc_entry(tcid); } bool TopologyManager::event_receive_mid( Message* msg, const IPv4& remote_addr, const IPv4& local_addr) { MidMessage* mid = dynamic_cast(msg); if (0 == mid) return false; // not for me // Put this after the cast to skip false positives. debug_msg("[MID] MyMainAddr %s Src %s RecvIf %s\n", cstring(_fm.get_main_addr()), cstring(remote_addr), cstring(local_addr)); // 5.4.1 Sender must be in symmetric 1-hop neighborhood. // This condition can be triggered during startup if the // protocol timers are set low or if we haven't seen a // HELLO from remote_addr yet. if (! _nh.is_sym_neighbor_addr(remote_addr)) { debug_msg("Rejecting MID message from %s via non-neighbor %s\n", cstring(msg->origin()), cstring(remote_addr)); XLOG_TRACE(_olsr.trace()._input_errors, "Rejecting MID message from %s via non-neighbor %s", cstring(msg->origin()), cstring(remote_addr)); return true; // consumed but invalid. } TimeVal now; _eventloop.current_time(now); // 5.4.2 Process each interface listed in MID message. size_t added_mid_count = 0; try { bool is_mid_created = false; // Message::hops() is not incremented for our last hop until // forwarded, so take account of this now when measuring distance. const vector& addrs = mid->interfaces(); const uint16_t distance = mid->hops() + 1; vector::const_iterator ii; for (ii = addrs.begin(); ii != addrs.end(); ii++) { update_mid_entry(mid->origin(), (*ii), distance, mid->expiry_time(), is_mid_created); if (is_mid_created) added_mid_count++; } } catch (...) { // If an exception is thrown, disregard the rest // of the MID entries in this message, as it is more than // likely we hit a hard limit. We can still forward the // message, we just can't process all the MID entries. } // Trigger a route computation if and only if we added new MID addresses. if (added_mid_count > 0) _rm->schedule_route_update(); _fm.forward_message(remote_addr, msg); return true; // consumed UNUSED(local_addr); } void TopologyManager::event_mid_dead(const OlsrTypes::MidEntryID mid_id) { XLOG_ASSERT(0 != _mids.count(mid_id)); delete_mid_entry(mid_id); } /* * Child classes. */ void TopologyEntry::update_timer(const TimeVal& vtime) { if (_expiry_timer.scheduled()) _expiry_timer.clear(); _expiry_timer = _ev.new_oneoff_after(vtime, callback(this, &TopologyEntry::event_dead)); } void TopologyEntry::event_dead() { _parent->event_tc_dead(id()); } void MidEntry::update_timer(const TimeVal& vtime) { if (_expiry_timer.scheduled()) _expiry_timer.clear(); _expiry_timer = _ev.new_oneoff_after(vtime, callback(this, &MidEntry::event_dead)); } void MidEntry::event_dead() { _parent->event_mid_dead(id()); } xorp/contrib/olsr/test_common.hh0000664000076400007640000000331511421137511017145 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8 sw=4: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/contrib/olsr/test_common.hh,v 1.3 2008/10/02 21:56:36 bms Exp $ #ifndef __OLSR_TEST_COMMON_HH__ #define __OLSR_TEST_COMMON_HH__ /** * The type fields when saving an OLSR instance's link state database. */ enum TLV { TLV_VERSION = 1, // The first entry in a file 4 byte version number. TLV_SYSTEM_INFO = 2, // A string defining the creation system. TLV_OLSR_VERSION = 3, // The OLSR version that the database came from. TLV_N1 = 4, // A one-hop neighbor entry. TLV_N2 = 5, // A two-hop neighbor entry. TLV_TC = 6, // A topology control entry. TLV_MID = 7, // A Multiple Interface entry. TLV_START = TLV_VERSION, // Lower bound. TLV_END = TLV_MID // Upper bound. }; const uint32_t TLV_CURRENT_VERSION = 1; // Current version number #endif // __OLSR_TEST_COMMON_HH__ xorp/contrib/olsr/xrl_io.cc0000664000076400007640000007145111540224221016103 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8 sw=4: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #include "olsr_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/callback.hh" #include "libxorp/ipv4.hh" #include "libxorp/ipv4net.hh" #include "libxorp/status_codes.h" #include "libxorp/service.hh" #include "libxorp/eventloop.hh" #include "libxipc/xrl_router.hh" #include "xrl/interfaces/rib_xif.hh" #include "xrl/interfaces/socket4_xif.hh" #include "libfeaclient/ifmgr_xrl_mirror.hh" #include "policy/backend/policytags.hh" #include "olsr.hh" #include "io.hh" #include "xrl_io.hh" #include "xrl_port.hh" #define OLSR_ADMIN_DISTANCE 230 // XXX hardcoded // -------------------------------------------------------------------------- // Utility methods /** * Query whether an address exists on given interface and vif path, * and that all items on path are enabled. */ static bool address_exists(const IfMgrIfTree& iftree, const string& ifname, const string& vifname, const IPv4& addr) { debug_msg("Looking for %s/%s/%s\n", ifname.c_str(), vifname.c_str(), addr.str().c_str()); const IfMgrIfAtom* ia = iftree.find_interface(ifname); if (ia == NULL) return false; const IfMgrVifAtom* va = ia->find_vif(vifname); if (va == NULL) return false; const IfMgrIPv4Atom* aa = va->find_addr(addr); if (aa == NULL) return false; return true; } /** * Query whether an address exists on given interface and vif path. * and that all items on path are enabled. */ static bool address_enabled(const IfMgrIfTree& iftree, const string& ifname, const string& vifname, const IPv4& addr) { debug_msg("Looking for %s/%s/%s\n", ifname.c_str(), vifname.c_str(), addr.str().c_str()); const IfMgrIfAtom* ia = iftree.find_interface(ifname); if (ia == 0 || ia->enabled() == false || ia->no_carrier()) { debug_msg("if %s exists ? %d ?\n", ifname.c_str(), (ia ? 1 : 0)); return false; } const IfMgrVifAtom* va = ia->find_vif(vifname); if (va == 0 || va->enabled() == false) { debug_msg("vif %s exists ? %d?\n", vifname.c_str(), (va ? 1: 0)); return false; } const IfMgrIPv4Atom* aa = va->find_addr(addr); if (aa == 0 || aa->enabled() == false) { debug_msg("addr %s exists ? %d?\n", addr.str().c_str(), (aa ? 1: 0)); return false; } debug_msg("Found\n"); return true; } // -------------------------------------------------------------------------- // Predicate functors. /** * @short Functor to test whether a particular local address is * associated with an XrlPort. */ struct port_has_local_address { port_has_local_address(const IPv4& addr) : _addr(addr) {} bool operator() (const XrlPort* xp) { return xp && xp->local_address() == _addr; } private: IPv4 _addr; }; /** * @short Functor to test whether a particular interface/vif is * associated with an XrlPort. */ struct port_has_interface_vif { port_has_interface_vif(const string& interface, const string& vif) : _interface(interface), _vif(vif) {} bool operator() (const XrlPort* xp) { return xp && xp->ifname() == _interface && xp->vifname() == _vif; } private: string _interface; string _vif; }; /** * @short Functor to test whether a particular XrlPort * is in a given service state. */ struct port_has_io_in_state { port_has_io_in_state(ServiceStatus st) : _st(st) {} bool operator() (const XrlPort* xp) const { return xp && xp->status() == _st; } protected: ServiceStatus _st; }; /** * @short Functor to test whether a particular OLSR port is appropriate * for packet arriving on socket. * * NB At a future date we might want to track socket id to XrlPortIO * mappings. This would be more efficient. */ struct is_port_for { is_port_for(const string* sockid, const string* ifname, const string* vifname, const IPv4* addr, IfMgrXrlMirror* im) : _psid(sockid), _ifname(ifname), _vifname(vifname), _pa(addr), _pim(im) {} bool operator() (XrlPort*& xp); protected: bool link_addr_valid() const; private: const string* _psid; const string* _ifname; const string* _vifname; const IPv4* _pa; IfMgrXrlMirror* _pim; }; inline bool is_port_for::link_addr_valid() const { return true; } bool is_port_for::operator() (XrlPort*& xp) { // // Perform address family specific check for source address being // link-local. For IPv4 the concept does not exist, for IPv6 // check if origin is link local. // if (link_addr_valid() == false) { return false; } if (xp == 0) return false; // If another socket, ignore if (xp->sockid() != *_psid) return false; // If our packet, ignore if (xp->local_address() == *_pa) return false; // Check the incoming interface and vif name (if known) if ((! _ifname->empty()) && (! _vifname->empty())) { if (xp->ifname() != *_ifname) return false; if (xp->vifname() != *_vifname) return false; } // // Packet has arrived on multicast socket and is not one of ours. // // Check source address to find originating neighbour on local nets // or p2p link. // const IfMgrIPv4Atom* ifa; ifa = _pim->iftree().find_addr(xp->ifname(), xp->vifname(), xp->local_address()); if (ifa == 0) { return false; } if (ifa->has_endpoint()) { return ifa->endpoint_addr() == *_pa; } IPv4Net n(ifa->addr(), ifa->prefix_len()); return n.contains(*_pa); } // -------------------------------------------------------------------------- // XrlIO XrlIO::XrlIO(EventLoop& eventloop, XrlRouter& xrl_router, const string& feaname, const string& ribname) : _eventloop(eventloop), _xrl_router(xrl_router), _feaname(feaname), _ribname(ribname), _component_count(0), _ifmgr(eventloop, feaname.c_str(), _xrl_router.finder_address(), _xrl_router.finder_port()), _rib_queue(eventloop, xrl_router) { _ifmgr.set_observer(this); _ifmgr.attach_hint_observer(this); _rib_queue.set_io(this); } XrlIO::~XrlIO() { _ifmgr.detach_hint_observer(this); _ifmgr.unset_observer(this); while (! _dead_ports.empty()) { XrlDeadPortMap::iterator ii = _dead_ports.begin(); XrlPort* xp = (*ii).second; delete xp; _dead_ports.erase(ii); } } int XrlIO::startup() { ServiceBase::set_status(SERVICE_STARTING); // // XXX: when the startup is completed, // IfMgrHintObserver::tree_complete() will be called. // if (_ifmgr.startup() != XORP_OK) { ServiceBase::set_status(SERVICE_FAILED); return (XORP_ERROR); } register_rib(); component_up("startup"); return (XORP_OK); } int XrlIO::shutdown() { // // XXX: when the shutdown is completed, XrlIO::status_change() // will be called. // ServiceBase::set_status(SERVICE_SHUTTING_DOWN); // // XXX Walk ports and shut them down. Only when they are all // shutdown should we consider ourselves shutdown. // debug_msg("XXX XrlIO::shutdown (%p)\n", this); debug_msg("XXX n_ports = %u n_dead_ports %u\n", XORP_UINT_CAST(_ports.size()), XORP_UINT_CAST(_dead_ports.size())); while (! _ports.empty()) { XrlPortList::iterator ii = _ports.begin(); XrlPort* xp = (*ii); debug_msg(" XXX killing port %p\n", xp); xp->shutdown(); _ports.erase(ii); _dead_ports.insert(make_pair(dynamic_cast(xp), xp)); } unregister_rib(); // XXX something is keeping the components up. component_down("shutdown"); return (_ifmgr.shutdown()); } void XrlIO::component_up(string name) { XLOG_ASSERT(name != "OlsrXrlPort"); debug_msg("component_up '%s'\n", name.c_str()); _component_count++; debug_msg("component_count %u\n", XORP_UINT_CAST(_component_count)); // XXX - Should really get every component to register at // initialisation time and track the individual // status. Simpler to uncomment the printfs and track the count. if (3 == _component_count) ServiceBase::set_status(SERVICE_RUNNING); UNUSED(name); } void XrlIO::component_down(string name) { XLOG_ASSERT(name != "OlsrXrlPort"); debug_msg("component_down '%s'\n", name.c_str()); _component_count--; debug_msg("component_count %u\n", XORP_UINT_CAST(_component_count)); if (0 == _component_count) ServiceBase::set_status(SERVICE_SHUTDOWN); else ServiceBase::set_status(SERVICE_SHUTTING_DOWN); UNUSED(name); } void XrlIO::status_change(ServiceBase* service, ServiceStatus old_status, ServiceStatus new_status) { if (service->service_name() == "OlsrXrlPort") { try_start_next_port(); if (new_status != SERVICE_SHUTDOWN) return; #if 0 // Resolve ServiceBase* to XrlPort*. // Note: this assertion can fail if we never detected the failure // of the socket to start up. XrlDeadPortMap::iterator ii = _dead_ports.find(service); XLOG_ASSERT(ii != _dead_ports.end()); #endif return; } if (old_status == new_status) return; if (SERVICE_RUNNING == new_status) component_up(service->service_name()); if (SERVICE_SHUTDOWN == new_status) component_down(service->service_name()); } // // This method is specific to XrlIO. // // Note: socket4_user.xif does not provide the destination address // or port at this time, therefore it is impossible to verify that // the traffic has been sent to a broadcast or multicast group. // void XrlIO::receive(const string& sockid, const string& interface, const string& vif, const IPv4& src, const uint16_t& sport, const vector& payload) { debug_msg("receive(%s, %s, %s, %s, %u, %u)\n", sockid.c_str(), interface.c_str(), vif.c_str(), cstring(src), XORP_UINT_CAST(sport), XORP_UINT_CAST(payload.size())); // Do I have an XrlPort on this interface where this packet // could actually have been received? // XXX: This can't be relied upon on all platforms, in particular // it may break on Windows; it is totally necessary for BSD derived // platforms, because if the socket(s) are bound to interface addresses, // they will not see any broadcast traffic ever. // TODO: Use sockid for a quicker lookup. XrlPortList& xpl = this->ports(); XrlPortList::iterator xpi; xpi = find_if(xpl.begin(), xpl.end(), port_has_interface_vif(interface, vif)); if (xpi == xpl.end()) { XLOG_ERROR("No socket exists for interface/vif %s/%s", interface.c_str(), vif.c_str()); return; } // Did the upper layer register a receive callback? if (IO::_receive_cb.is_empty()) return; // // XXX: create a copy of the payload, because the callback's argument // is not const-ified. // XXX: 'dst' and 'dport' cannot be learned from the socket4_user // XRL target, therefore empty addresses are passed to the upper layer. // vector payload_copy(payload); IO::_receive_cb->dispatch(interface, vif, IPv4::ZERO(), 0, src, sport, &payload_copy[0], payload_copy.size()); } bool XrlIO::send(const string& interface, const string& vif, const IPv4& src, const uint16_t& sport, const IPv4& dst, const uint16_t& dport, uint8_t* data, const uint32_t& len) { debug_msg("send(%s, %s, %s, %u, %s, %u, %p, %u)\n", interface.c_str(), vif.c_str(), cstring(src), XORP_UINT_CAST(dport), cstring(dst), XORP_UINT_CAST(sport), data, XORP_UINT_CAST(len)); // Do I have an XrlPort on this interface to send from? XrlPortList& xpl = this->ports(); XrlPortList::iterator xpi; xpi = find_if(xpl.begin(), xpl.end(), port_has_local_address(src)); if (xpi == xpl.end()) { XLOG_ERROR("No socket exists for address %s/%s/%s:%u", interface.c_str(), vif.c_str(), cstring(src), XORP_UINT_CAST(sport)); return false; } // Copy the payload, as the XRL send happens asynchronously. vector payload(len); memcpy(&payload[0], data, len); bool result = (*xpi)->send_to(dst, dport, payload); return result; } bool XrlIO::enable_address(const string& interface, const string& vif, const IPv4& address, const uint16_t& port, const IPv4& all_nodes_address) { debug_msg("Interface %s Vif %s Address %s Port %u AllNodesAddr %s\n", interface.c_str(), vif.c_str(), cstring(address), XORP_UINT_CAST(port), cstring(all_nodes_address)); if (! address_exists(_iftree, interface, vif, address)) { XLOG_WARNING("%s/%s/%s:%u does not exist", interface.c_str(), vif.c_str(), cstring(address), XORP_UINT_CAST(port)); return false; } // Check if port already exists. XrlPortList::const_iterator xpi; xpi = find_if(this->ports().begin(), this->ports().end(), port_has_local_address(address)); if (xpi != this->ports().end()) { XLOG_WARNING("Socket already exists for address %s/%s/%s:%u", interface.c_str(), vif.c_str(), cstring(address), XORP_UINT_CAST(port)); return true; } // Create XrlPort. XrlPort* xp = new XrlPort(this, _eventloop, _xrl_router, _feaname, interface, vif, address, port, all_nodes_address); this->ports().push_back(xp); // Add self to observers of the XrlPort's status. xp->set_observer(this); // Start next XrlPort if no others are starting up already. try_start_next_port(); return true; } bool XrlIO::disable_address(const string& interface, const string& vif, const IPv4& address, const uint16_t& port) { debug_msg("Interface %s Vif %s Address %s Port %u\n", interface.c_str(), vif.c_str(), cstring(address), XORP_UINT_CAST(port)); XrlPortList& xpl = this->ports(); XrlPortList::iterator xpi; xpi = find_if(xpl.begin(), xpl.end(), port_has_local_address(address)); if (xpi != xpl.end()) { XrlPort* xp = *xpi; if (xp) { _dead_ports.insert(make_pair(dynamic_cast(xp), xp)); xp->shutdown(); } xpl.erase(xpi); } return true; } bool XrlIO::is_vif_broadcast_capable(const string& interface, const string& vif) { debug_msg("Interface %s Vif %s\n", interface.c_str(), vif.c_str()); if (! is_interface_enabled(interface)) return false; const IfMgrVifAtom* fv = ifmgr_iftree().find_vif(interface, vif); if (fv == NULL) return false; return (fv->broadcast_capable()); } bool XrlIO::is_vif_multicast_capable(const string& interface, const string& vif) { debug_msg("Interface %s Vif %s\n", interface.c_str(), vif.c_str()); if (! is_interface_enabled(interface)) return false; const IfMgrVifAtom* fv = ifmgr_iftree().find_vif(interface, vif); if (fv == NULL) return false; return (fv->multicast_capable()); } bool XrlIO::is_vif_loopback(const string& interface, const string& vif) { debug_msg("Interface %s Vif %s\n", interface.c_str(), vif.c_str()); if (! is_interface_enabled(interface)) return false; const IfMgrVifAtom* fv = ifmgr_iftree().find_vif(interface, vif); if (fv == NULL) return false; return (fv->loopback()); } bool XrlIO::get_broadcast_address(const string& interface, const string& vif, const IPv4& address, IPv4& bcast_address) const { debug_msg("Interface %s Vif %s Address %s\n", interface.c_str(), vif.c_str(), cstring(address)); if (! is_vif_enabled(interface, vif)) return false; const IfMgrIPv4Atom* fa = ifmgr_iftree().find_addr(interface, vif, address); if (fa == NULL) return false; if (! fa->has_broadcast()) { debug_msg("%s/%s/%s doesn't have a broadcast address.\n", interface.c_str(), vif.c_str(), cstring(address)); return false; } bcast_address = fa->broadcast_addr(); return true; } bool XrlIO::is_interface_enabled(const string& interface) const { debug_msg("Interface %s\n", interface.c_str()); const IfMgrIfAtom* fi = ifmgr_iftree().find_interface(interface); if (fi == NULL) return false; return (fi->enabled() && (! fi->no_carrier())); } bool XrlIO::is_vif_enabled(const string& interface, const string& vif) const { debug_msg("Interface %s Vif %s\n", interface.c_str(), vif.c_str()); if (! is_interface_enabled(interface)) return false; const IfMgrVifAtom* fv = ifmgr_iftree().find_vif(interface, vif); if (fv == NULL) return false; return (fv->enabled()); } bool XrlIO::is_address_enabled(const string& interface, const string& vif, const IPv4& address) const { return address_enabled(ifmgr_iftree(), interface, vif, address); } bool XrlIO::get_addresses(const string& interface, const string& vif, list& addresses) const { debug_msg("Interface %s Vif %s\n", interface.c_str(), vif.c_str()); const IfMgrVifAtom* fv = ifmgr_iftree().find_vif(interface, vif); if (fv == NULL) return false; IfMgrVifAtom::IPv4Map::const_iterator i; for (i = fv->ipv4addrs().begin(); i != fv->ipv4addrs().end(); i++) addresses.push_back(i->second.addr()); return true; } bool XrlIO::get_interface_id(const string& interface, uint32_t& interface_id) { debug_msg("Interface %s\n", interface.c_str()); const IfMgrIfAtom* fi = ifmgr_iftree().find_interface(interface); if (fi == NULL) return false; interface_id = fi->pif_index(); return true; } uint32_t XrlIO::get_prefix_length(const string& interface, const string& vif, IPv4 address) { debug_msg("Interface %s Vif %s Address %s\n", interface.c_str(), vif.c_str(), cstring(address)); const IfMgrIPv4Atom* fa = ifmgr_iftree().find_addr(interface, vif, address); if (fa == NULL) return 0; return (fa->prefix_len()); } uint32_t XrlIO::get_mtu(const string& interface) { debug_msg("Interface %s\n", interface.c_str()); const IfMgrIfAtom* fi = ifmgr_iftree().find_interface(interface); if (fi == NULL) return 0; return (fi->mtu()); } void XrlIO::register_rib() { XrlRibV0p1Client rib(&_xrl_router); // Register the protocol admin distance as configured. // XXX: Don't forget to add the protocol admin distance to every // RIB for which we plan to provide an origin table. if (! rib.send_set_protocol_admin_distance( _ribname.c_str(), "olsr", // protocol true, // ipv4 false, // ipv6 true, // unicast false, // multicast OLSR_ADMIN_DISTANCE, // admin_distance callback(this, &XrlIO::rib_command_done, true, "set_protocol_admin_distance"))) { XLOG_WARNING("Failed to set OLSR admin distance in RIB"); } if (! rib.send_add_igp_table4( _ribname.c_str(), // RIB target name "olsr", // protocol name _xrl_router.class_name(), // our class _xrl_router.instance_name(), // our instance true, // unicast false, // multicast callback(this, &XrlIO::rib_command_done, true, "add_igp_table4"))) { XLOG_FATAL("Failed to add OLSR table(s) to IPv4 RIB"); } } void XrlIO::unregister_rib() { XrlRibV0p1Client rib(&_xrl_router); if (! rib.send_delete_igp_table4( _ribname.c_str(), // RIB target name "olsr", // protocol name _xrl_router.class_name(), // our class _xrl_router.instance_name(), // our instance true, // unicast false, // multicast callback(this, &XrlIO::rib_command_done, false, "delete_igp_table4"))) { XLOG_FATAL("Failed to delete OLSR table(s) from IPv4 RIB"); } } void XrlIO::rib_command_done(const XrlError& error, bool up, const char *comment) { debug_msg("callback %s %s\n", comment, cstring(error)); switch (error.error_code()) { case OKAY: break; case REPLY_TIMED_OUT: // We should really be using a reliable transport where // this error cannot happen. But it has so lets retry if we can. XLOG_ERROR("callback: %s %s", comment, cstring(error)); break; case RESOLVE_FAILED: case SEND_FAILED: case SEND_FAILED_TRANSIENT: case NO_SUCH_METHOD: XLOG_ERROR("callback: %s %s", comment, cstring(error)); break; case NO_FINDER: // XXX - Temporarily code dump if this condition occurs. XLOG_FATAL("NO FINDER"); break; case BAD_ARGS: case COMMAND_FAILED: case INTERNAL_ERROR: XLOG_FATAL("callback: %s %s", comment, cstring(error)); break; } if (0 == strcasecmp(comment, "set_protocol_admin_distance")) return; if (up) { component_up(c_format("rib %s", comment)); } else { component_down(c_format("rib %s", comment)); } } bool XrlIO::add_route(IPv4Net net, IPv4 nexthop, uint32_t nexthop_id, uint32_t metric, const PolicyTags& policytags) { debug_msg("Net %s Nexthop %s metric %d policy %s\n", cstring(net), cstring(nexthop), metric, cstring(policytags)); _rib_queue.queue_add_route(_ribname, net, nexthop, nexthop_id, metric, policytags); return true; } bool XrlIO::replace_route(IPv4Net net, IPv4 nexthop, uint32_t nexthop_id, uint32_t metric, const PolicyTags& policytags) { debug_msg("Net %s Nexthop %s metric %d policy %s\n", cstring(net), cstring(nexthop), metric, cstring(policytags)); _rib_queue.queue_delete_route(_ribname, net); _rib_queue.queue_add_route(_ribname, net, nexthop, nexthop_id, metric, policytags); return true; } bool XrlIO::delete_route(IPv4Net net) { debug_msg("Net %s\n", cstring(net)); _rib_queue.queue_delete_route(_ribname, net); return true; } void XrlIO::tree_complete() { // // XXX: we use same actions when the tree is completed or updates are made // updates_made(); } void XrlIO::updates_made() { IfMgrIfTree::IfMap::const_iterator ii; IfMgrIfAtom::VifMap::const_iterator vi; IfMgrVifAtom::IPv4Map::const_iterator ai; const IfMgrIfAtom* if_atom; const IfMgrIfAtom* other_if_atom; const IfMgrVifAtom* vif_atom; const IfMgrVifAtom* other_vif_atom; const IfMgrIPv4Atom* addr_atom; const IfMgrIPv4Atom* other_addr_atom; // // Check whether the old interfaces, vifs and addresses are still there // for (ii = _iftree.interfaces().begin(); ii != _iftree.interfaces().end(); ++ii) { bool is_old_interface_enabled = false; bool is_new_interface_enabled = false; bool is_old_vif_enabled = false; bool is_new_vif_enabled = false; bool is_old_address_enabled = false; bool is_new_address_enabled = false; if_atom = &ii->second; is_old_interface_enabled = if_atom->enabled(); is_old_interface_enabled &= (! if_atom->no_carrier()); // Check the interface other_if_atom = ifmgr_iftree().find_interface(if_atom->name()); if (other_if_atom == NULL) { // The interface has disappeared is_new_interface_enabled = false; } else { is_new_interface_enabled = other_if_atom->enabled(); is_new_interface_enabled &= (! other_if_atom->no_carrier()); } if ((is_old_interface_enabled != is_new_interface_enabled) && (! _interface_status_cb.is_empty())) { // The interface's enabled flag has changed _interface_status_cb->dispatch(if_atom->name(), is_new_interface_enabled); } for (vi = if_atom->vifs().begin(); vi != if_atom->vifs().end(); ++vi) { vif_atom = &vi->second; is_old_vif_enabled = vif_atom->enabled(); is_old_vif_enabled &= is_old_interface_enabled; // Check the vif other_vif_atom = ifmgr_iftree().find_vif(if_atom->name(), vif_atom->name()); if (other_vif_atom == NULL) { // The vif has disappeared is_new_vif_enabled = false; } else { is_new_vif_enabled = other_vif_atom->enabled(); } is_new_vif_enabled &= is_new_interface_enabled; if ((is_old_vif_enabled != is_new_vif_enabled) && (! _vif_status_cb.is_empty())) { // The vif's enabled flag has changed _vif_status_cb->dispatch(if_atom->name(), vif_atom->name(), is_new_vif_enabled); } for (ai = vif_atom->ipv4addrs().begin(); ai != vif_atom->ipv4addrs().end(); ++ai) { addr_atom = &ai->second; is_old_address_enabled = addr_atom->enabled(); is_old_address_enabled &= is_old_vif_enabled; // Check the address other_addr_atom = ifmgr_iftree().find_addr(if_atom->name(), vif_atom->name(), addr_atom->addr()); if (other_addr_atom == NULL) { // The address has disappeared is_new_address_enabled = false; } else { is_new_address_enabled = other_addr_atom->enabled(); } is_new_address_enabled &= is_new_vif_enabled; if ((is_old_address_enabled != is_new_address_enabled) && (! _address_status_cb.is_empty())) { // The address's enabled flag has changed _address_status_cb->dispatch(if_atom->name(), vif_atom->name(), addr_atom->addr(), is_new_address_enabled); } } } } // // Check for new interfaces, vifs and addresses // for (ii = ifmgr_iftree().interfaces().begin(); ii != ifmgr_iftree().interfaces().end(); ++ii) { if_atom = &ii->second; // Check the interface other_if_atom = _iftree.find_interface(if_atom->name()); if (other_if_atom == NULL) { // A new interface if (if_atom->enabled() && (! if_atom->no_carrier()) && (! _interface_status_cb.is_empty())) { _interface_status_cb->dispatch(if_atom->name(), true); } } for (vi = if_atom->vifs().begin(); vi != if_atom->vifs().end(); ++vi) { vif_atom = &vi->second; // Check the vif other_vif_atom = _iftree.find_vif(if_atom->name(), vif_atom->name()); if (other_vif_atom == NULL) { // A new vif if (if_atom->enabled() && (! if_atom->no_carrier()) && (vif_atom->enabled()) && (! _vif_status_cb.is_empty())) { _vif_status_cb->dispatch(if_atom->name(), vif_atom->name(), true); } } for (ai = vif_atom->ipv4addrs().begin(); ai != vif_atom->ipv4addrs().end(); ++ai) { addr_atom = &ai->second; // Check the address other_addr_atom = _iftree.find_addr(if_atom->name(), vif_atom->name(), addr_atom->addr()); if (other_addr_atom == NULL) { // A new address if (if_atom->enabled() && (! if_atom->no_carrier()) && (vif_atom->enabled()) && (addr_atom->enabled()) && (! _address_status_cb.is_empty())) { _address_status_cb->dispatch(if_atom->name(), vif_atom->name(), addr_atom->addr(), true); } } } } } // // Update the local copy of the interface tree // _iftree = ifmgr_iftree(); } // Gradually start each XrlPort to avoid races with the FEA. void XrlIO::try_start_next_port() { // If there are any ports currently starting up, // do not try to start another. XrlPortList::const_iterator cpi; cpi = find_if(_ports.begin(), _ports.end(), port_has_io_in_state(SERVICE_STARTING)); if (cpi != _ports.end()) { debug_msg("doing nothing (there is a port %p in SERVICE_STARTING " "state).\n", (*cpi)); return; } // Look for the first port that needs to be started up, and try // to start it up. XrlPortList::iterator xpi = _ports.begin(); XrlPort* xp = 0; while (xp == 0) { xpi = find_if(xpi, _ports.end(), port_has_io_in_state(SERVICE_READY)); if (xpi == _ports.end()) { debug_msg("reached end of ports in SERVICE_READY state\n"); return; } xp = (*xpi); // XXX xpi++; } debug_msg("starting up port %p\n", xp); xp->startup(); } XrlPort* XrlIO::find_port(const string& ifname, const string& vifname, const IPv4& addr) { XrlPortList::iterator xpi; xpi = find_if(this->ports().begin(), this->ports().end(), port_has_local_address(addr)); if (xpi == this->ports().end()) { return 0; } XrlPort* xp = (*xpi); if (xp->ifname() != ifname || xp->vifname() != vifname) { return 0; } return xp; } const XrlPort* XrlIO::find_port(const string& ifname, const string& vifname, const IPv4& addr) const { XrlPortList::const_iterator xpi; xpi = find_if(this->ports().begin(), this->ports().end(), port_has_local_address(addr)); if (xpi == this->ports().end()) { return 0; } const XrlPort* xp = (*xpi); if (xp->ifname() != ifname || xp->vifname() != vifname) { return 0; } return xp; } xorp/contrib/olsr/test_args.hh0000664000076400007640000001140311421137511016606 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8 sw=4: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/contrib/olsr/test_args.hh,v 1.3 2008/10/02 21:56:35 bms Exp $ #ifndef __OSPF_TEST_ARGS_HH__ #define __OSPF_TEST_ARGS_HH__ #include "libxorp/tokenize.hh" /** * Break a string into a sequence of space separated words. */ class Args { public: Args(string& line) : _line(line), _pos(0) { tokenize(line, _words); } /** * @param word if a word is available it is placed here. * @return true if a word has been returned. */ bool get_next(string& word) { if (_pos >= _words.size()) return false; word = _words[_pos++]; return true; } void push_back() { if (_pos > 1) _pos--; } const string& original_line() const { return _line; } private: const string _line; vector _words; size_t _pos; }; /** * Get a number in base 8,10 or 16. * * @param word the word to evaluate. * @return a number. * @throw InvalidString if invalid syntax. */ inline uint32_t get_number(const string& word) throw(InvalidString) { char *endptr; uint32_t number = strtoul(word.c_str(), &endptr, 0); if (0 != *endptr) xorp_throw(InvalidString, c_format("<%s> is not a number", word.c_str())); return number; } /** * Get an IPv4 address. * * @param word the word to evaluate. * @return an IPv4 host address. * @throw InvalidString if invalid syntax. */ inline IPv4 get_ipv4(const string& word) throw(InvalidString) { IPv4 addr; try { addr = IPv4(word.c_str()); } catch (...) { xorp_throw(InvalidString, c_format("<%s> is not an IPv4 address", word.c_str())); } return addr; } /** * Get an IPv4 network address. * * @param word the word to evaluate. * @return an IPv4 network address. * @throw InvalidString if invalid syntax. */ inline IPv4Net get_ipv4_net(const string& word) throw(InvalidString) { IPv4Net v4net; try { v4net = IPv4Net(word.c_str()); } catch (...) { xorp_throw(InvalidString, c_format("<%s> is not an IPv4 network address", word.c_str())); } return v4net; } /** * Get a boolean variable as a string or number. * * @param word the word to evaluate. * @return a boolean value. * @throw InvalidString if invalid syntax. */ inline bool get_bool(const string& word) throw(InvalidString) { bool value = false; try { int i_value = get_number(word); if (i_value == 1) { value = true; } else if (i_value == 1) { value = false; } } catch (InvalidString is) { if (0 == strcasecmp(word.c_str(), "true")) { value = true; } else if (0 == strcasecmp(word.c_str(), "false")) { value = false; } else { // re-throw exception with appropriate error message xorp_throw(InvalidString, c_format("<%s> is not a boolean", word.c_str())); } } return value; } /** * Get the next word throw an exception if its not present. * * @param args the argument structure to retreive the word from. * @param word the word to evaluate. * @return * @throw InvalidString if invalid syntax. */ inline string get_next_word(Args& args, const string& varname) throw(InvalidString) { string var; if (!args.get_next(var)) xorp_throw(InvalidString, c_format("No argument to %s. [%s]", varname.c_str(), args.original_line().c_str())); return var; } inline uint32_t get_next_number(Args& args, const string& varname) throw(InvalidString) { return get_number(get_next_word(args, varname)); } inline IPv4 get_next_ipv4(Args& args, const string& varname) throw(InvalidString) { return get_ipv4(get_next_word(args, varname)); } inline IPv4Net get_next_ipv4_net(Args& args, const string& varname) throw(InvalidString) { return get_ipv4_net(get_next_word(args, varname)); } inline bool get_next_bool(Args& args, const string& varname) throw(InvalidString) { return get_bool(get_next_word(args, varname)); } #endif // __OSPF_TEST_ARGS_HH__ xorp/contrib/olsr/tools/0000775000076400007640000000000011631506605015442 5ustar greearbgreearbxorp/contrib/olsr/tools/print_databases.cc0000664000076400007640000007627411540224221021122 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8 sw=4: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #include "olsr/olsr_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/ipv4.hh" #include "libxorp/service.hh" #include "libxorp/status_codes.h" #include "libxorp/eventloop.hh" #include "libxorp/tlv.hh" #include "libxipc/xrl_std_router.hh" #include "xrl/interfaces/olsr4_xif.hh" #include "olsr/olsr_types.hh" #include "olsr/olsr.hh" #include "olsr/test_common.hh" // Sorting will break the 1:1 mapping between id and Info // structures after retrieval via XRL. Bear this in mind if we need to // further extend this tool. #ifndef SORT_OUTPUT #define SORT_OUTPUT #endif /** * Humanize a bool. * * @param value the boolean value to express as a human readable string. * @param short_form true if a single character should be printed. * @return "yes"/"*" if value is true, otherwise "no"/" ". */ inline const char* humanize_bool(const bool value, const bool short_form = false) { if (value) { if (short_form) return ("*"); else return ("yes"); } else { if (short_form) return (" "); else return ("no"); } } // // OLSRv1/IPv4 operational mode command to get database status. // // Note: TLVs are in olsr/olsr_types.hh. // // // Base class. // class Getter { public: Getter(XrlRouter& xr, const string& tgt) : _xr(xr), _tgt(tgt), _fail(false), _done(false) {} virtual ~Getter() {} // parse command line arguments and fire off the XRLs // to get the particular state we want. virtual void get(int argc, char *argv[]) = 0; // true if the command failed virtual bool fail() { return _fail; } // print the appropriate output virtual void output() = 0; // TODO: save to a TLV file //virtual void save() = 0; // poll for completion; return true if we should keep // running the event loop. inline bool busy() { return ! _done; } protected: XrlRouter& _xr; string _tgt; bool _fail; bool _done; }; // // External route information. // struct ExternalInfo { IPv4Net _destination; IPv4 _lasthop; uint32_t _distance; uint32_t _hold_time; }; class GetExternals : public Getter { public: GetExternals(XrlRouter& xr, const string& tgt) : Getter(xr, tgt) {} ~GetExternals() {} void get(int argc, char *argv[]); // print the appropriate output void output(); private: void list_cb(const XrlError& e, const XrlAtomList *atoms); void external_cb(const XrlError& e, const IPv4Net* destination, const IPv4* lasthop, const uint32_t* distance, const uint32_t* hold_time); void fetch_next(); private: list _external_ids; list::iterator _index; list _infos; }; void GetExternals::get(int argc, char *argv[]) { XrlOlsr4V0p1Client cl(&this->_xr); bool success = cl.send_get_hna_entry_list(this->_tgt.c_str(), callback(this, &GetExternals::list_cb)); if (! success) XLOG_WARNING("Failed to get external route list."); UNUSED(argc); UNUSED(argv); } void GetExternals::list_cb(const XrlError& e, const XrlAtomList *atoms) { if (XrlError::OKAY() != e) { XLOG_WARNING("Failed to get external route list."); _done = true; _fail = true; return; } const size_t size = atoms->size(); for (size_t i = 0; i < size; i++) { _external_ids.push_back((atoms->get(i).uint32())); } _index = _external_ids.begin(); fetch_next(); } void GetExternals::fetch_next() { if (_external_ids.end() == _index) { _done = true; return; } XrlOlsr4V0p1Client cl(&this->_xr); bool success = cl.send_get_hna_entry(this->_tgt.c_str(), *_index, callback(this, &GetExternals::external_cb)); if (! success) XLOG_WARNING("Failed to get external route info."); } void GetExternals::external_cb(const XrlError& e, const IPv4Net* destination, const IPv4* lasthop, const uint32_t* distance, const uint32_t* hold_time) { if (XrlError::OKAY() != e) { XLOG_WARNING("Attempt to get external route info failed"); _done = true; _fail = true; return; } ExternalInfo info; #define copy_info(var) info._ ## var = *var copy_info(destination); copy_info(lasthop); copy_info(distance); copy_info(hold_time); #undef copy_info _infos.push_back(info); _index++; fetch_next(); } void GetExternals::output() { if (_infos.empty()) { printf("No external routes learned.\n"); return; } printf("%-18s %-15s %8s %5s\n", "Destination", "Lasthop", "Distance", "Hold"); list::iterator ii; for (ii = _infos.begin(); ii != _infos.end(); ii++) { const ExternalInfo& info = (*ii); printf("%-18s %-15s %8u %5u\n", cstring(info._destination), cstring(info._lasthop), XORP_UINT_CAST(info._distance), XORP_UINT_CAST(info._hold_time)); } } // // Interface information. // struct InterfaceInfo { string _ifname; string _vifname; IPv4 _local_addr; uint16_t _local_port; IPv4 _all_nodes_addr; uint16_t _all_nodes_port; }; class GetInterfaces : public Getter { public: GetInterfaces(XrlRouter& xr, const string& tgt) : Getter(xr, tgt) {} ~GetInterfaces() {} void get(int argc, char *argv[]); // print the appropriate output void output(); private: void list_cb(const XrlError& e, const XrlAtomList *atoms); void interface_cb(const XrlError& e, const string* ifname, const string* vifname, const IPv4* local_addr, const uint32_t* local_port, const IPv4* all_nodes_addr, const uint32_t* all_nodes_port); void fetch_next(); private: list _face_ids; list::iterator _index; list _infos; }; void GetInterfaces::get(int argc, char *argv[]) { XrlOlsr4V0p1Client cl(&this->_xr); bool success = cl.send_get_interface_list(this->_tgt.c_str(), callback(this, &GetInterfaces::list_cb)); if (! success) XLOG_WARNING("Failed to get interface list."); UNUSED(argc); UNUSED(argv); } void GetInterfaces::list_cb(const XrlError& e, const XrlAtomList *atoms) { if (XrlError::OKAY() != e) { XLOG_WARNING("Failed to get interface list."); _done = true; _fail = true; return; } const size_t size = atoms->size(); for (size_t i = 0; i < size; i++) { _face_ids.push_back((atoms->get(i).uint32())); } _index = _face_ids.begin(); fetch_next(); } void GetInterfaces::fetch_next() { if (_face_ids.end() == _index) { _done = true; return; } XrlOlsr4V0p1Client cl(&this->_xr); bool success = cl.send_get_interface_info(this->_tgt.c_str(), *_index, callback(this, &GetInterfaces::interface_cb)); if (! success) XLOG_WARNING("Failed to get interface info."); } void GetInterfaces::interface_cb( const XrlError& e, const string* ifname, const string* vifname, const IPv4* local_addr, const uint32_t* local_port, const IPv4* all_nodes_addr, const uint32_t* all_nodes_port) { if (XrlError::OKAY() != e) { XLOG_WARNING("Attempt to get interface info failed"); _done = true; _fail = true; return; } InterfaceInfo info; #define copy_info(var) info._ ## var = *var copy_info(ifname); copy_info(vifname); copy_info(local_addr); copy_info(local_port); copy_info(all_nodes_addr); copy_info(all_nodes_port); #undef copy_info _infos.push_back(info); _index++; fetch_next(); } void GetInterfaces::output() { if (_infos.empty()) { printf("No interfaces configured.\n"); return; } printf("%-10s %-20s %-20s\n", "Interface", "LocalAddr", "AllNodesAddr"); list::iterator ii; for (ii = _infos.begin(); ii != _infos.end(); ii++) { const InterfaceInfo& info = (*ii); printf("%-4s/%-4s %15s:%-5u %15s:%-5u\n", info._ifname.c_str(), info._vifname.c_str(), cstring(info._local_addr), XORP_UINT_CAST(info._local_port), cstring(info._all_nodes_addr), XORP_UINT_CAST(info._all_nodes_port)); } } struct InterfaceStatsInfo { uint32_t _bad_packets; uint32_t _bad_messages; uint32_t _messages_from_self; uint32_t _unknown_messages; uint32_t _duplicates; uint32_t _forwarded; }; // TODO: Interface statistics. // // Link information. // struct LinkInfo { IPv4 _local_addr; IPv4 _remote_addr; IPv4 _main_addr; uint32_t _link_type; uint32_t _sym_time; uint32_t _asym_time; uint32_t _hold_time; }; class GetLinks : public Getter { public: GetLinks(XrlRouter& xr, const string& tgt) : Getter(xr, tgt) {} ~GetLinks() {} void get(int argc, char *argv[]); // print the appropriate output void output(); private: void list_cb(const XrlError& e, const XrlAtomList *atoms); void link_cb(const XrlError& e, const IPv4* local_addr, const IPv4* remote_addr, const IPv4* main_addr, const uint32_t* link_type, const uint32_t* sym_time, const uint32_t* asym_time, const uint32_t* hold_time); void fetch_next(); private: list _link_ids; list::iterator _index; list _infos; }; void GetLinks::get(int argc, char *argv[]) { XrlOlsr4V0p1Client cl(&this->_xr); bool success = cl.send_get_link_list(this->_tgt.c_str(), callback(this, &GetLinks::list_cb)); if (! success) XLOG_WARNING("Failed to get link list."); UNUSED(argc); UNUSED(argv); } void GetLinks::list_cb(const XrlError& e, const XrlAtomList *atoms) { if (XrlError::OKAY() != e) { XLOG_WARNING("Failed to get link list."); _done = true; _fail = true; return; } const size_t size = atoms->size(); for (size_t i = 0; i < size; i++) { _link_ids.push_back((atoms->get(i).uint32())); } _index = _link_ids.begin(); fetch_next(); } void GetLinks::fetch_next() { if (_link_ids.end() == _index) { _done = true; return; } XrlOlsr4V0p1Client cl(&this->_xr); bool success = cl.send_get_link_info(this->_tgt.c_str(), *_index, callback(this, &GetLinks::link_cb)); if (! success) XLOG_WARNING("Failed to get link info."); } void GetLinks::link_cb(const XrlError& e, const IPv4* local_addr, const IPv4* remote_addr, const IPv4* main_addr, const uint32_t* link_type, const uint32_t* sym_time, const uint32_t* asym_time, const uint32_t* hold_time) { if (XrlError::OKAY() != e) { XLOG_WARNING("Failed to get link info."); _done = true; _fail = true; return; } LinkInfo info; #define copy_info(var) info._ ## var = *var copy_info(local_addr); copy_info(remote_addr); copy_info(main_addr); copy_info(link_type); copy_info(sym_time); copy_info(asym_time); copy_info(hold_time); #undef copy_info _infos.push_back(info); _index++; fetch_next(); } struct LinkSortOrderPred { bool operator()(const LinkInfo& lhs, const LinkInfo& rhs) const { return lhs._remote_addr < rhs._remote_addr; } }; void GetLinks::output() { if (_infos.empty()) { printf("No links learned.\n"); return; } //LocalAddr RemoteAddr Neighbor Type ASYM SYM Hold //%-15s %-15s %-15s %-4s %5u %5u %5u printf("%-15s %-15s %-15s %-4s %5s %5s %5s\n", "LocalAddr", "RemoteAddr", "Neighbor", "Type", "ASYM", "SYM", "Hold"); // TODO: Turn LinkType into a symbolic string. #ifdef SORT_OUTPUT #ifdef XORP_USE_USTL sort(_infos, LinkSortOrderPred()); #else _infos.sort(LinkSortOrderPred()); #endif #endif list::iterator ii; for (ii = _infos.begin(); ii != _infos.end(); ii++) { const LinkInfo& info = (*ii); printf("%-15s %-15s %-15s %-4u %5u %5u %5u\n", cstring(info._local_addr), cstring(info._remote_addr), cstring(info._main_addr), XORP_UINT_CAST(info._link_type), // XXX XORP_UINT_CAST(info._sym_time), XORP_UINT_CAST(info._asym_time), XORP_UINT_CAST(info._hold_time)); } } // // MID entries. // struct MidInfo { IPv4 _main_addr; IPv4 _iface_addr; uint32_t _distance; uint32_t _hold_time; }; class GetMids : public Getter { public: GetMids(XrlRouter& xr, const string& tgt) : Getter(xr, tgt) {} ~GetMids() {} void get(int argc, char *argv[]); // print the appropriate output void output(); private: void list_cb(const XrlError& e, const XrlAtomList *atoms); void mid_cb(const XrlError& e, const IPv4* main_addr, const IPv4* iface_addr, const uint32_t* distance, const uint32_t* hold_time); void fetch_next(); private: list _mid_ids; list::iterator _index; list _infos; }; void GetMids::get(int argc, char *argv[]) { XrlOlsr4V0p1Client cl(&this->_xr); bool success = cl.send_get_mid_entry_list(this->_tgt.c_str(), callback(this, &GetMids::list_cb)); if (! success) XLOG_WARNING("Failed to get MID list."); UNUSED(argc); UNUSED(argv); } void GetMids::list_cb(const XrlError& e, const XrlAtomList *atoms) { if (XrlError::OKAY() != e) { XLOG_WARNING("Failed to get MID list."); _done = true; _fail = true; return; } const size_t size = atoms->size(); for (size_t i = 0; i < size; i++) { _mid_ids.push_back((atoms->get(i).uint32())); } _index = _mid_ids.begin(); fetch_next(); } void GetMids::fetch_next() { if (_mid_ids.end() == _index) { _done = true; return; } XrlOlsr4V0p1Client cl(&this->_xr); bool success = cl.send_get_mid_entry(this->_tgt.c_str(), *_index, callback(this, &GetMids::mid_cb)); if (! success) XLOG_WARNING("Failed to get MID info."); } void GetMids::mid_cb(const XrlError& e, const IPv4* main_addr, const IPv4* iface_addr, const uint32_t* distance, const uint32_t* hold_time) { if (XrlError::OKAY() != e) { XLOG_WARNING("Failed to get MID info."); _done = true; _fail = true; return; } MidInfo info; #define copy_info(var) info._ ## var = *var copy_info(main_addr); copy_info(iface_addr); copy_info(distance); copy_info(hold_time); #undef copy_info _infos.push_back(info); _index++; fetch_next(); } void GetMids::output() { if (_infos.empty()) { printf("No MID entries learned.\n"); return; } printf("%-15s %-15s %9s %5s\n", "MainAddr", "RemoteAddr", "Distance", "Hold"); list::iterator ii; for (ii = _infos.begin(); ii != _infos.end(); ii++) { const MidInfo& info = (*ii); printf("%-15s %-15s %9u %5u\n", cstring(info._main_addr), cstring(info._iface_addr), XORP_UINT_CAST(info._distance), XORP_UINT_CAST(info._hold_time)); } } // // Neighbor information. // struct NeighborInfo { IPv4 _main_addr; uint32_t _willingness; uint32_t _degree; uint32_t _link_count; uint32_t _twohop_link_count; bool _is_advertised; bool _is_sym; bool _is_mpr; bool _is_mpr_selector; }; class GetNeighbors : public Getter { public: GetNeighbors(XrlRouter& xr, const string& tgt) : Getter(xr, tgt) {} ~GetNeighbors() {} void get(int argc, char *argv[]); // print the appropriate output void output(); private: void list_cb(const XrlError& e, const XrlAtomList *atoms); void neighbor_cb(const XrlError& e, const IPv4* _main_addr, const uint32_t* _willingness, const uint32_t* _degree, const uint32_t* _link_count, const uint32_t* _twohop_link_count, const bool* _is_advertised, const bool* _is_sym, const bool* _is_mpr, const bool* _is_mpr_selector); void fetch_next(); private: list _nids; list::iterator _index; list _infos; }; void GetNeighbors::get(int argc, char *argv[]) { XrlOlsr4V0p1Client cl(&this->_xr); bool success = cl.send_get_neighbor_list(this->_tgt.c_str(), callback(this, &GetNeighbors::list_cb)); if (! success) XLOG_WARNING("Failed to get neighbor list."); UNUSED(argc); UNUSED(argv); } void GetNeighbors::list_cb(const XrlError& e, const XrlAtomList *atoms) { if (XrlError::OKAY() != e) { XLOG_WARNING("Failed to get neighbor list."); _done = true; _fail = true; return; } const size_t size = atoms->size(); for (size_t i = 0; i < size; i++) { _nids.push_back((atoms->get(i).uint32())); } _index = _nids.begin(); fetch_next(); } void GetNeighbors::fetch_next() { if (_nids.end() == _index) { _done = true; return; } XrlOlsr4V0p1Client cl(&this->_xr); bool success = cl.send_get_neighbor_info(this->_tgt.c_str(), *_index, callback(this, &GetNeighbors::neighbor_cb)); if (! success) XLOG_WARNING("Failed to get neighbor info."); } void GetNeighbors::neighbor_cb(const XrlError& e, const IPv4* main_addr, const uint32_t* willingness, const uint32_t* degree, const uint32_t* link_count, const uint32_t* twohop_link_count, const bool* is_advertised, const bool* is_sym, const bool* is_mpr, const bool* is_mpr_selector) { if (XrlError::OKAY() != e) { XLOG_WARNING("Failed to get neighbor info."); _done = true; _fail = true; return; } NeighborInfo info; #define copy_info(var) info._ ## var = *var copy_info(main_addr); copy_info(willingness); copy_info(degree); copy_info(link_count); copy_info(twohop_link_count); copy_info(is_advertised); copy_info(is_sym); copy_info(is_mpr); copy_info(is_mpr_selector); #undef copy_info _infos.push_back(info); _index++; fetch_next(); } struct NeighborSortOrderPred { bool operator()(const NeighborInfo& lhs, const NeighborInfo& rhs) const { return lhs._main_addr < rhs._main_addr; } }; void GetNeighbors::output() { if (_infos.empty()) { printf("No neighbors learned.\n"); return; } printf("%-15s %4s %6s %5s %6s %3s %3s %3s %3s\n", "MainAddr", "Will", "Degree", "Links", "2links", "ADV", "SYM", "MPR", "MPRS"); #ifdef SORT_OUTPUT #ifdef XORP_USE_USTL sort(_infos, NeighborSortOrderPred()); #else _infos.sort(NeighborSortOrderPred()); #endif #endif list::iterator ii; for (ii = _infos.begin(); ii != _infos.end(); ii++) { const NeighborInfo& info = (*ii); printf("%-15s %4u %6u %5u %6u %3s %3s %3s %3s\n", cstring(info._main_addr), XORP_UINT_CAST(info._willingness), XORP_UINT_CAST(info._degree), XORP_UINT_CAST(info._link_count), XORP_UINT_CAST(info._twohop_link_count), humanize_bool(info._is_advertised, true), humanize_bool(info._is_sym, true), humanize_bool(info._is_mpr, true), humanize_bool(info._is_mpr_selector, true)); } } // // Topology information. // struct TopologyInfo { IPv4 _destination; IPv4 _lasthop; uint32_t _distance; uint32_t _seqno; uint32_t _hold_time; }; class GetTopology : public Getter { public: GetTopology(XrlRouter& xr, const string& tgt) : Getter(xr, tgt) {} ~GetTopology() {} void get(int argc, char *argv[]); // print the appropriate output void output(); private: void list_cb(const XrlError& e, const XrlAtomList *atoms); void topology_cb(const XrlError& e, const IPv4* destination, const IPv4* lasthop, const uint32_t* distance, const uint32_t* seqno, const uint32_t* hold_time); void fetch_next(); private: list _tc_ids; list::iterator _index; list _infos; }; void GetTopology::get(int argc, char *argv[]) { XrlOlsr4V0p1Client cl(&this->_xr); bool success = cl.send_get_tc_entry_list(this->_tgt.c_str(), callback(this, &GetTopology::list_cb)); if (! success) XLOG_WARNING("Failed to get topology list."); UNUSED(argc); UNUSED(argv); } void GetTopology::list_cb(const XrlError& e, const XrlAtomList *atoms) { if (XrlError::OKAY() != e) { XLOG_WARNING("Failed to get topology list."); _done = true; _fail = true; return; } const size_t size = atoms->size(); for (size_t i = 0; i < size; i++) { _tc_ids.push_back((atoms->get(i).uint32())); } _index = _tc_ids.begin(); fetch_next(); } void GetTopology::fetch_next() { if (_tc_ids.end() == _index) { _done = true; return; } XrlOlsr4V0p1Client cl(&this->_xr); bool success = cl.send_get_tc_entry(this->_tgt.c_str(), *_index, callback(this, &GetTopology::topology_cb)); if (! success) XLOG_WARNING("Failed to get topology info."); } void GetTopology::topology_cb(const XrlError& e, const IPv4* destination, const IPv4* lasthop, const uint32_t* distance, const uint32_t* seqno, const uint32_t* hold_time) { if (XrlError::OKAY() != e) { XLOG_WARNING("Failed to get topology info."); _done = true; _fail = true; return; } TopologyInfo info; #define copy_info(var) info._ ## var = *var copy_info(destination); copy_info(lasthop); copy_info(distance); copy_info(seqno); copy_info(hold_time); #undef copy_info _infos.push_back(info); _index++; fetch_next(); } void GetTopology::output() { if (_infos.empty()) { printf("No TC entries learned.\n"); return; } printf("%-15s %-15s %8s %5s %5s\n", "Destination", "Lasthop", "Distance", "SeqNo", "Hold"); list::iterator ii; for (ii = _infos.begin(); ii != _infos.end(); ii++) { const TopologyInfo& info = (*ii); printf("%-15s %-15s %8u %5u %5u\n", cstring(info._destination), cstring(info._lasthop), XORP_UINT_CAST(info._distance), XORP_UINT_CAST(info._seqno), XORP_UINT_CAST(info._hold_time)); } } // // Two-hop link information. // struct TwohopLinkInfo { uint32_t _last_face_id; IPv4 _nexthop_addr; IPv4 _dest_addr; uint32_t _hold_time; }; class GetTwohopLinks : public Getter { public: GetTwohopLinks(XrlRouter& xr, const string& tgt) : Getter(xr, tgt) {} ~GetTwohopLinks() {} void get(int argc, char *argv[]); // print the appropriate output void output(); private: void list_cb(const XrlError& e, const XrlAtomList *atoms); void twohop_link_cb(const XrlError& e, const uint32_t* _last_face_id, const IPv4* _nexthop_addr, const IPv4* _dest_addr, const uint32_t* _hold_time); void fetch_next(); private: list _tlids; list::iterator _index; list _infos; }; void GetTwohopLinks::get(int argc, char *argv[]) { XrlOlsr4V0p1Client cl(&this->_xr); bool success = cl.send_get_twohop_link_list(this->_tgt.c_str(), callback(this, &GetTwohopLinks::list_cb)); if (! success) XLOG_WARNING("Failed to get neighbor list."); UNUSED(argc); UNUSED(argv); } void GetTwohopLinks::list_cb(const XrlError& e, const XrlAtomList *atoms) { if (XrlError::OKAY() != e) { XLOG_WARNING("Failed to get neighbor list."); _done = true; _fail = true; return; } const size_t size = atoms->size(); for (size_t i = 0; i < size; i++) { _tlids.push_back((atoms->get(i).uint32())); } _index = _tlids.begin(); fetch_next(); } void GetTwohopLinks::fetch_next() { if (_tlids.end() == _index) { _done = true; return; } XrlOlsr4V0p1Client cl(&this->_xr); bool success = cl.send_get_twohop_link_info(this->_tgt.c_str(), *_index, callback(this, &GetTwohopLinks::twohop_link_cb)); if (! success) XLOG_WARNING("Failed to get two-hop link info."); } void GetTwohopLinks::twohop_link_cb(const XrlError& e, const uint32_t* last_face_id, const IPv4* nexthop_addr, const IPv4* dest_addr, const uint32_t* hold_time) { if (XrlError::OKAY() != e) { XLOG_WARNING("Failed to get two-hop link info."); _done = true; _fail = true; return; } TwohopLinkInfo info; #define copy_info(var) info._ ## var = *var copy_info(last_face_id); copy_info(nexthop_addr); copy_info(dest_addr); copy_info(hold_time); #undef copy_info _infos.push_back(info); _index++; fetch_next(); } void GetTwohopLinks::output() { if (_infos.empty()) { printf("No two-hop links learned.\n"); return; } printf("%-15s %-15s %5s\n", "Destination", "Nexthop", "Hold"); list::iterator ii; for (ii = _infos.begin(); ii != _infos.end(); ii++) { const TwohopLinkInfo& info = (*ii); printf( "%-15s %-15s %5u\n", cstring(info._dest_addr), cstring(info._nexthop_addr), XORP_UINT_CAST(info._hold_time)); } } // // Two-hop neighbor information. // struct TwohopNeighborInfo { IPv4 _main_addr; bool _is_strict; uint32_t _link_count; uint32_t _reachability; uint32_t _coverage; }; class GetTwohopNeighbors : public Getter { public: GetTwohopNeighbors(XrlRouter& xr, const string& tgt) : Getter(xr, tgt) {} ~GetTwohopNeighbors() {} void get(int argc, char *argv[]); // print the appropriate output void output(); private: void list_cb(const XrlError& e, const XrlAtomList *atoms); void twohop_neighbor_cb(const XrlError& e, const IPv4* main_addr, const bool* is_strict, const uint32_t* link_count, const uint32_t* reachability, const uint32_t* coverage); void fetch_next(); private: list _tnids; list::iterator _index; list _infos; }; void GetTwohopNeighbors::get(int argc, char *argv[]) { XrlOlsr4V0p1Client cl(&this->_xr); bool success = cl.send_get_twohop_neighbor_list( this->_tgt.c_str(), callback(this, &GetTwohopNeighbors::list_cb)); if (! success) XLOG_WARNING("Failed to get two-hop neighbor list."); UNUSED(argc); UNUSED(argv); } void GetTwohopNeighbors::list_cb(const XrlError& e, const XrlAtomList *atoms) { if (XrlError::OKAY() != e) { XLOG_WARNING("Failed to get two-hop neighbor list."); _done = true; _fail = true; return; } const size_t size = atoms->size(); for (size_t i = 0; i < size; i++) { _tnids.push_back((atoms->get(i).uint32())); } _index = _tnids.begin(); fetch_next(); } void GetTwohopNeighbors::fetch_next() { if (_tnids.end() == _index) { _done = true; return; } XrlOlsr4V0p1Client cl(&this->_xr); bool success = cl.send_get_twohop_neighbor_info( this->_tgt.c_str(), *_index, callback(this, &GetTwohopNeighbors::twohop_neighbor_cb)); if (! success) XLOG_WARNING("Failed to get two-hop neighbor info."); } void GetTwohopNeighbors::twohop_neighbor_cb(const XrlError& e, const IPv4* main_addr, const bool* is_strict, const uint32_t* link_count, const uint32_t* reachability, const uint32_t* coverage) { if (XrlError::OKAY() != e) { XLOG_WARNING("Failed to get two-hop neighbor info."); _done = true; _fail = true; return; } TwohopNeighborInfo info; #define copy_info(var) info._ ## var = *var copy_info(main_addr); copy_info(is_strict); copy_info(link_count); copy_info(reachability); copy_info(coverage); #undef copy_info _infos.push_back(info); _index++; fetch_next(); } void GetTwohopNeighbors::output() { if (_infos.empty()) { printf("No two-hop neighbors learned.\n"); return; } printf("%-15s %3s %8s %12s\n", "MainAddr", "N1", "Coverage", "Reachability"); list::iterator ii; for (ii = _infos.begin(); ii != _infos.end(); ii++) { const TwohopNeighborInfo& info = (*ii); printf( "%-15s %3s %8u %12u\n", cstring(info._main_addr), humanize_bool(! info._is_strict, true), XORP_UINT_CAST(info._link_count), XORP_UINT_CAST(info._coverage)); } } typedef map GetterMap; GetterMap _getters; void register_getter(const string& name, Getter* getter) { _getters[name] = getter; } void unregister_all_getters() { while (! _getters.empty()) { GetterMap::iterator ii = _getters.begin(); delete (*ii).second; _getters.erase(ii); } } void initialize_getters(XrlRouter& xr, const string& tgt) { register_getter("external", new GetExternals(xr, tgt)); register_getter("interface", new GetInterfaces(xr, tgt)); register_getter("link", new GetLinks(xr, tgt)); register_getter("mid", new GetMids(xr, tgt)); register_getter("neighbor", new GetNeighbors(xr, tgt)); register_getter("topology", new GetTopology(xr, tgt)); register_getter("twohop-link", new GetTwohopLinks(xr, tgt)); register_getter("twohop-neighbor", new GetTwohopNeighbors(xr, tgt)); } Getter* get_getter(const string& get_type) { GetterMap::iterator ii = _getters.find(get_type); if (ii == _getters.end()) return 0; return (*ii).second; } int usage(const char *myname) { fprintf(stderr, "usage: %s [external|interface|link|mid|neighbor|topology|\n" " twohop-link|twohop-neighbor]\n", myname); return -1; } int main(int argc, char **argv) { XorpUnexpectedHandler x(xorp_unexpected_handler); char* progname = argv[0]; // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); // TODO: getopt() here; use options for brief vs terse, ipv4 vs ipv6. if (argc < 2) { usage(progname); exit(1); } string olsr_name("olsr4"); argc--; ++argv; string getter_name(argv[0]); argc--; ++argv; try { EventLoop eventloop; XrlStdRouter xrl_router(eventloop, "print_databases"); //debug_msg("Waiting for router\n"); xrl_router.finalize(); wait_until_xrl_router_is_ready(eventloop, xrl_router); //debug_msg("\n"); initialize_getters(xrl_router, olsr_name); Getter* getter = get_getter(getter_name); if (getter == 0) { fprintf(stderr, "No such getter %s\n", getter_name.c_str()); unregister_all_getters(); exit(1); } getter->get(argc, argv); while (getter->busy()) eventloop.run(); if (! getter->fail()) { getter->output(); } } catch (...) { xorp_catch_standard_exceptions(); } unregister_all_getters(); xlog_stop(); xlog_exit(); return 0; } xorp/contrib/olsr/tools/SConscript0000664000076400007640000000424311631506605017457 0ustar greearbgreearb# Copyright (c) 2009-2011 XORP, Inc and Others # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # $XORP$ import os Import('env') env = env.Clone() is_shared = env.has_key('SHAREDLIBS') env.AppendUnique(CPPPATH = [ '#', '../..', '../../..', '$BUILDDIR', ]) env.AppendUnique(LIBPATH = [ '$BUILDDIR/libxorp', '$BUILDDIR/libcomm', '$BUILDDIR/libxipc', '$BUILDDIR/libproto', '$BUILDDIR/xrl/interfaces', '$BUILDDIR/xrl/targets', '$BUILDDIR/.', ]) env.AppendUnique(LIBS = [ 'xorp_ipc', 'xorp_proto', 'xorp_core', 'xorp_comm', 'xif_olsr4', ]) if not is_shared: env.AppendUnique(LIBS = [ "crypto", ]) if not (env.has_key('mingw') and env['mingw']): env.AppendUnique(LIBS = [ "rt", ]) if (env.has_key('mingw') and env['mingw']): env.AppendUnique(LIBS = [ 'ws2_32', 'iphlpapi', # 'mprapi', 'regex', 'winmm', ]) env.Append(LIBS = ['xorp_core']) cleardbsrcs = [ 'clear_database.cc' ] printdbsrcs = [ 'print_databases.cc', ] cleardb = env.Program(target = 'olsr_clear_database', source = cleardbsrcs) printdb = env.Program(target = 'olsr_print_databases', source = printdbsrcs) #olsrtoolpath = '$exec_prefix/contrib/olsr/tools' env.Alias('install', env.InstallProgram(env['xorp_tooldir'], cleardb)) env.Alias('install', env.InstallProgram(env['xorp_tooldir'], printdb)) Default(cleardb, printdb) Default() xorp/contrib/olsr/tools/clear_database.cc0000664000076400007640000000607511540224221020661 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8 sw=4: // Copyright (c) 2007-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #include "olsr/olsr_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/ipv4.hh" #include "libxorp/service.hh" #include "libxorp/status_codes.h" #include "libxorp/eventloop.hh" #include "libxorp/tlv.hh" #ifdef HAVE_GETOPT_H #include #endif #include "libxipc/xrl_std_router.hh" #include "xrl/interfaces/olsr4_xif.hh" #include "olsr/olsr.hh" #include "olsr/test_common.hh" class ClearDatabase { public: ClearDatabase(XrlStdRouter& xrl_router) : _xrl_router(xrl_router), _done(false), _fail(false) { } void start() { XrlOlsr4V0p1Client olsr4(&_xrl_router); olsr4.send_clear_database("olsr4", callback(this, &ClearDatabase::response)); } bool busy() { return !_done; } bool fail() { return _fail; } private: void response(const XrlError& error) { _done = true; if (XrlError::OKAY() != error) { XLOG_WARNING("Attempt to clear database"); _fail = true; return; } } private: XrlStdRouter &_xrl_router; bool _done; bool _fail; }; int usage(const char *myname) { fprintf(stderr, "usage: %s\n", myname); return -1; } int main(int argc, char **argv) { XorpUnexpectedHandler x(xorp_unexpected_handler); // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); try { EventLoop eventloop; XrlStdRouter xrl_router(eventloop, "clear_database"); debug_msg("Waiting for router\n"); xrl_router.finalize(); wait_until_xrl_router_is_ready(eventloop, xrl_router); debug_msg("\n"); ClearDatabase clear_database(xrl_router); clear_database.start(); while (clear_database.busy()) eventloop.run(); if (clear_database.fail()) { XLOG_ERROR("Failed to clear database"); return -1; } } catch (...) { xorp_catch_standard_exceptions(); } xlog_stop(); xlog_exit(); return 0; UNUSED(argc); } xorp/contrib/olsr/NOTES0000664000076400007640000003641011421137511015112 0ustar greearbgreearbNOTES ----- Process and XRL naming conventions ================================== The XORP OLSR v1 routing processes are named "olsr" and suffixed with the protocol version in use, e.g. "olsr4" and "olsr6", or "raolsr" for example. If OLSR v2 is implemented in future, it will be named "olsrv2" and suffixed differently, e.g. "olsrv2_4", "olsrv2_6". Whilst XORP's configuration template syntax supports the notion of the same protocol supporting multiple address families, OLSRv1 must be considered a separate protocol when run for each address family. Platform issues =============== * Broadcast-capable sockets are now created using a separate FEA command. All platform-specific issues are now dealt with by the FEA. * XORP/OLSR currently depends upon the FEA's ability to determine the name of the interface where a datagram was received on this socket. This capability is known to work under FreeBSD and Linux. It is not guaranteed to work on all Windows versions. Specifically, the 'sockid' field is ignored by low level I/O in the OLSR routing process -- it can be used to map an FEA socket to an OLSR interface, however this mapping is not currently implemented. Protocol Simulator ================== XORP/OLSR incorporates a command-driven OLSR network simulator. This is not intended to be a complete network simulator; it is intended for exercising the XORP implementation, and implementing regression tests for expected behaviour. As such, the functions it offers are geared towards verification and validation of the OLSR protocol. If a more complete network simulation is required, i.e. with flexible path characteristics, please consider using OpNet or NS-2. test_simulator makes use of XORP's event loop library to operate. Separate Olsr class instances are created for each node. A simple command language is used to configure the simulation, and it runs in the user process time domain; there are no soft/hard real-time capabilities. Normally the protocol simulator is invoked from a set of Python scripts, neither they nor the simulator are required for actual deployment of XORP/OLSR. Neighbor Lookups ================ Most components which process OLSR protocol messages have to check if the containing packet originated from a symmetric one-hop neighbor to comply with the RFC. There is scope for further optimization if needed -- the local link database contains a composite index of both addresses uniquely identifying a link pair. We use NeighborIDs as a unique key within the code, because whilst an OLSR neighbor is uniquely keyed by its protocol address, those addresses may be bigger than 32 bits when running OLSRv1-IPv6 or RA-OLSR in 802.11s, making key comparison more expensive. Incremental SPT =============== This is an optional future feature of XORP's Shortest Path Tree (SPT) implementation, intended to be used by link-state routing protocols such as OSPF and OLSR. It is not yet supported. Currently, a run of the Dijkstra algorithm over the entire OLSR link-state database is triggered every time an OLSR topology change happens. This is a deferred computation; the process schedules it to take place after any other pending protocol state changes have been processed. Other known implementations of OLSR do this, and this is keeping in common with XORP's implementation of OSPF, where one instance of Dijkstra is run for each configured OSPF area. However, this may need to be revisited in future for deployment of OLSR in environments where the topology is large and also highly dynamic; the incremental implementation of SPT may yield faster forwarding plane convergence and route propagation in such cases. MID-derived Route Computation ============================= In Section 10, step 4, the OLSR RFC specifies that additional host routes MUST be pushed to the local node's forwarding plane for each MID entry which corresponds to a remote node anywhere in the known OLSR topology. In the XORP implementation, MID-derived routes are created directly from the output of the Spt class. This simplifies the implementation, by completely avoiding the possibility of adding duplicate MID-derived routes; and should perform acceptably well for most general uses of OLSR. This simplicity, however, comes at the expense of triggering an SPT calculation whenever the contents of the MID database change, even if there was no change in the SPT. Additionally, OLSR protocol routes are tagged with VT_MID as they flow into the policy engine, allowing them to be manipulated by network engineers. The reasoning behind this approach is as follows: If the MID database has changed, the OLSR topology has probably changed too, as MIDs are not originated by a node until its OLSR configuration includes more than one interface. Furthermore, OLSR must add host routes to a node's main address, even if it has not yet received a MID message from the node. This is particularly important for TC entries where no link state is known for interfaces on the remote node which do not point towards the local node; that is, the TC and MID databases are out of phase. The RFC covers this specific case at the end of Section 10, Step 2. This implementation choice for MID needs to be revisited in future if Incremental SPT is implemented. In this case, there is scope for moving the MID processing into the RouteManager class, and triggering recomputation of MID-derived host routes separately from the SPT. MPR Selection ============= The reason for the existence in MPRs is clear, it is an attempt to optimize the flooding of messages whilst eliminating loops, in the absence of opportunities to build shortest-path trees where the topology of the net is changing rapidly. As such it is an essential part of the OLSR protocol, it is not optional. The definition of the MPR set in English is: select the minimal subset of N such that all N2 are covered, where N is the set of reachable forwarding one-hop neighbors, and where N2 are reachable strict two-hop neighbors. The RFC specifies a heuristic for MPR selection. This heuristic is specified to run per interface. However, this heuristic can be resource intensive to implement. It may only offer real performance benefits in situations where there is more than one OLSR interface, each using a directional or steerable antenna, with a non-overlapping two-hop neighborhood which changes rapidly. One approach towards implementing the above heuristic is to use a per-interface candidate MPR set. This however may only be worth the time/space tradeoff in the case where the two-hop nodes tend to be clustered around particular one-hop neighbors for large sizes of N. The implementation of MPR selection in xorp_olsr has more in common with the implementation in olsrd, in which the space of all reachable strict 2-hop neighbors is enumerated. This is not what is specified in the OLSR RFC however it is far simpler to implement. Whenever the neighborhood changes in a way which would affect MPR selection, the full MPR computation is rescheduled as all members of N and N2 must be considered together when computing the global MPR set. To avoid using secondary classes for storage, we keep state in the instances of Neighbor and TwoHopNeighbor which shouldn't really be exposed to other parts of the system other than MPR selection. In particular the coverage() count on a TwoHopNeighbor may be out of sync with the is_mpr() flag on individual Neighbor objects, as may the global set, until we atomically swap the new set with the old set at the end of this function. As such, the MPR computation state is a good candidate for a Memento pattern, particularly so if interested parties wish to implement the per-interface heuristic described in the RFC. Distributed MPR Selection ========================= As with other XORP routing processes, distributed computation of certain protocol variables is possible, but may not be desirable in all situations, therefore the implementation is aimed at the general case. To perform incremental, interface-based MPR set computation in xorp_olsr one would typically introduce a class into Neighborhood to represent the changes in the MPR candidacy, per interface. Class Neighbor then becomes responsible for triggering the deferred MPR recomputation task, by pushing its state change to this per-Face class (let's call it FaceMprState). In this way, the MPR computation may only be recomputed for the whole set of N2 reachable from I whenever the neighborhood reachable from I alone changes, as documented in the RFC. However as explained above this may only be useful for large sizes of N2(per I) where there is no significant overlap between instances of N2(per I) -- that is to say, the 2-hop neighborhood tends not to be reachable from more than one interface, as would be the case with an OLSR node with directional antennae and multiple radios. Multiple instances ================== Link state protocols, of any kind, have the advantage over distance vector protocols that they are quick to converge. However, they require the exchange of more information between peers. Despite this, all networks have the problem that eventually, the overhead of the low level reachability information will not scale. This can be seen in multicast networks (PIM-SM viz PIM-DM) and with IP over 802.3 Ethernet, where it becomes necessary to partition backbones using switches. OSPF has the concept of areas, which summarise routes automatically at area borders. OLSR might benefit from areas, although implemented somewhat differently -- and to truly implement roaming between OLSR areas, encapsulation may be desirable to overcome the same addressing problems seen with Mobile IP. XORP as a framework already has minimal support for the notion of multiple instances of the same routing protocol. The object model contained in xorp_olsr shall hopefully make this sort of work easier to implement. External Route Support (HNA) ============================ The mechanism for advertising, learning and redistributing external routes in OLSR is known as HNA (Host and Network Association), and is documented in Section 12 of RFC 3626. The RFC is is ambiguous about what the last hop for an HNA route actually is; it is sometimes referred to as the main address of a node, and sometimes referred to as the interface address of a node. XORP treats it as the main address, as there is no way, once an HNA message is forwarded, to determine the original sender interface address. HNA prefixes are advertised by an OLSR origin. If the network topology is changing rapidly, it is possible that OLSR at the local node will not have converged on a route to the advertised next-hop for the HNA prefix. Currently, XORP follows the RFC by adding routes, if and only if, the next-hop can be resolved within OLSR's internal routing trie. This behaviour is similar to that of an OSPF Type 1 AS-External-LSA, however, HNA, as specified in the RFC, contains no metric of its own. XORP's RIB has the capability to resolve the next-hop field for routes learned from a given protocol, using information from another protocol. This functionality is necessary to support BGP; OLSR does not currently use it, although it could do so optionally in future. In summary: The reachability of network prefixes, advertised using HNA in OLSR, is strictly affected by OLSR link state (including HELLO, MID and TC), and is not affected by other layer 3 routing protocols. In the future: As specified in the RFC, OLSR has no provision for originating external routes with metrics or tags. A future enhancement is planned whereby HNA may be extended to advertise routes with a metric, in a manner similar to that of both the OSPF Type 1 and Type 2 AS-External-LSA. Route redistribution ==================== The XORP implementation of OLSR exposes various OLSR protocol variables to the XORP redistribution process, xorp_policy. These variables are exposed via the OlsrVarRW structure in policy_varrw.hh. These are not fully documented herein as they are subject to change, however, the most interesting to developers and users will be the type and origin fields which allow for routes originating from different parts of the OLSR topology, or sub-protocols, to be selectively ignored or rewritten using XORP's policy syntax. RFC-compliant OLSR Route Metrics ================================ Currently the route metrics for XORP's implementation of OLSR use a simple scalar scheme. * Links to one-hop neighbors are assigned a cost based on the following, in decreasing order of precedence: * The interface cost is the base cost of the first hop. * If the next hop is not an MPR selector, add 1. * Subtract the neighbor's willingness from WILL_ALWAYS, and add to the cost of the first hop, therefore deriving the first hop's cost using the following formula: first-hop-cost = (interface-cost + !is_mpr_selector) * WILL_ALWAYS) / (WILL_ALWAYS - willingness) * Each subsequent link in the shortest-path tree has a cost of 1; this is because RFC-compliant TC contains no reachability information other than the presence or absence of a link. * Equal cost multi-path (ECMP) support is not yet implemented, pending changes in XORP's spanning tree implementation to support this. * Ties between equal-cost next-hops at each link are broken by selecting the node with the numerically lower main address; see class Vertex. * HNA routes get assigned the cost of the path to reach their origin. This is similar to the behaviour of the OLSR Type 1 AS-External-LSA metric, although no metrics are advertised in RFC compliant HNA. * The metrics thus assigned to OLSR routes are the same as their cost in the spanning tree after the Dijkstra computation. The metric scheme will be revised in future to incorporate ETX and advertised metrics for HNA routes. Composite OLSR Route Metrics ============================ [ PRELIMINARY AND SUBJECT TO CHANGE -- NOT YET IMPLEMENTED ] When using OLSR in conjunction with OSPF, or other routing protocols, it is necessary for redistributed OLSR routes to have a meaningful metric. This is particularly important if a second routing protocol is used to interconnect separate OLSR routing domains across a non-MANET LAN. The documents which specify OLSR do not specify a metric scheme at the time of writing, therefore it has been necessary to invent one. A functional syntax for setting the metric in XORP's policy statements MAY be added at a future date; currently, only simple assignment of a fixed value is possible. RFC 3626 compliant OLSR states in section 10 that next-hops should be preferred by their willingness and MPR selector status. Therefore, the criteria, in descending order of precedence, for an OLSR route metric are as follows: * The interface cost, if applicable. * MPR selector status of the immediate next-hop. * Willingness of the immediate next-hop. * EITHER: * the ETX path metric, OR * the hop count on the SPT path. * A load sharing metric, if applicable, based on a histogram of interface utilization and available link bandwidth. A composite metric scheme will be used which takes account of the above criteria. OSPF supports a 24-bit wide metric space for AS-External-LSAs. Recall that OSPF's Type 1 metric also adds the cost to the ASBR for the external path type. The metric scheme used for XORP/OLSR will in future allow for scaling or base costs to be added when redistributing the OLSR routes into OSPF. xorp/contrib/olsr/test_message.cc0000664000076400007640000004053711540224221017273 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8 sw=4: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "olsr_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/callback.hh" #include "libxorp/exceptions.hh" #include "libxorp/ipv4.hh" #include "libxorp/ipv4net.hh" #include "libxorp/status_codes.h" #include "libxorp/service.hh" #include "libxorp/eventloop.hh" #include "libxorp/tokenize.hh" #include "libxorp/test_main.hh" #include "olsr.hh" #include "message.hh" // Make sure that all tests free up any memory that they use. This will // allow us to use the leak checker program. /* * Compare two packets * * @return true when they are equal */ inline bool compare_packets(TestInfo& info, vector& pkt1, vector& pkt2) { if (pkt1.size() != pkt2.size()) { DOUT(info) << "Packet lengths don't match " << pkt1.size() << " " << pkt2.size() << endl; return false; } if (0 != memcmp(&pkt1[0], &pkt2[0], pkt1.size())) { for(size_t i = 0; i < pkt1.size(); i++) { if (pkt1[i] != pkt2[i]) { DOUT(info) << "mismatch at byte position " << i << endl; DOUT(info) << "bytes " << (int)pkt1[i] << " " << (int)pkt2[i] << endl; break; } } return false; } return true; } /* * Compare the string renditions of packets * * @return true when they are equal */ inline bool compare_packet_strings(TestInfo& info, string str1, string str2) { if (str1 != str2) { if (info.verbose()) { vector token1; vector token2; tokenize(str1, token1, "\n"); tokenize(str2, token2, "\n"); vector::iterator i1 = token1.begin(); vector::iterator i2 = token2.begin(); for(;;) { if (token1.end() == i1 || token2.end() == i2) { DOUT(info) << "Ran out of tokens\n"; break; } if (*i1 != *i2) { DOUT(info) << *i1 << " *** DOES NOT MATCH ***" << *i2 << endl; break; } i1++; i2++; } } return false; } return true; } inline void populate_mid(MidMessage* mid) { mid->set_hop_count(0); mid->set_ttl(OlsrTypes::MAX_TTL); mid->set_origin(IPv4("192.168.1.1")); mid->set_seqno(31337); TimeVal expiry(256,0); mid->set_expiry_time(expiry); TimeVal rx(1, 0); mid->set_receive_time(rx); mid->add_interface(IPv4("192.168.0.1")); mid->set_valid(true); } /** * Create a packet containing a MID message, and print it. */ bool mid_packet_print(TestInfo& info) { MessageDecoder md; Packet* pkt = new Packet(md); MidMessage* mid = new MidMessage(); populate_mid(mid); // The next line makes 'mid' invalid in this scope, // as it will be turned into a ref_ptr. pkt->add_message(mid); DOUT(info) << pkt->str() << endl; delete mid; // XXX refcounting was removed delete pkt; return true; } inline void populate_hna(HnaMessage* hna) { hna->set_hop_count(0); hna->set_ttl(OlsrTypes::MAX_TTL); hna->set_origin(IPv4("192.168.0.123")); hna->set_seqno(31337); TimeVal expiry(256,0); hna->set_expiry_time(expiry); TimeVal rx(1, 0); hna->set_receive_time(rx); hna->add_network(IPv4Net("192.168.2.0/24")); hna->set_valid(true); } /** * Create a packet containing an HNA message, and print it. */ bool hna_packet_print(TestInfo& info) { MessageDecoder md; Packet* pkt = new Packet(md); HnaMessage* hna = new HnaMessage(); populate_hna(hna); pkt->add_message(hna); DOUT(info) << pkt->str() << endl; delete hna; // XXX refcounting was removed delete pkt; return true; } inline void populate_tc(TcMessage* tc) { tc->set_hop_count(0); tc->set_ttl(OlsrTypes::MAX_TTL); tc->set_origin(IPv4("192.168.0.123")); tc->set_seqno(31337); TimeVal expiry(256,0); tc->set_expiry_time(expiry); TimeVal rx(1, 0); tc->set_receive_time(rx); tc->add_neighbor(IPv4("192.168.1.124")); tc->set_ansn(111); tc->set_valid(true); } /** * Create a packet containing a TC message, and print it. */ bool tc_packet_print(TestInfo& info) { MessageDecoder md; Packet* pkt = new Packet(md); TcMessage* tc = new TcMessage(); populate_tc(tc); pkt->add_message(tc); DOUT(info) << pkt->str() << endl; delete tc; // XXX refcounting was removed delete pkt; return true; } inline void populate_hello(HelloMessage* hello) { hello->set_hop_count(0); hello->set_ttl(OlsrTypes::MAX_TTL); hello->set_origin(IPv4("192.168.0.1")); hello->set_seqno(31338); TimeVal expiry(256,0); hello->set_expiry_time(expiry); TimeVal rx(1, 0); hello->set_receive_time(rx); TimeVal htime(5,0); hello->set_htime(htime); hello->set_willingness(OlsrTypes::WILL_ALWAYS); LinkCode lc(OlsrTypes::MPR_NEIGH, OlsrTypes::SYM_LINK); hello->add_link(lc, IPv4("192.168.0.2")); hello->set_valid(true); } /** * Create a packet containing a HELLO message, and print it. */ bool hello_packet_print(TestInfo& info) { MessageDecoder md; Packet* pkt = new Packet(md); HelloMessage* hello = new HelloMessage(); populate_hello(hello); pkt->add_message(hello); DOUT(info) << pkt->str() << endl; delete hello; // XXX refcounting was removed delete pkt; return true; } uint8_t multi_v4_packet_data[] = { #include "regression/multi_msg_ok_ipv4.data" }; /** * Decode a packet containing many messages, and print it. */ bool multi_packet_decode(TestInfo& info) { MessageDecoder md; initialize_message_decoder(md); Packet* pkt = new Packet(md); try { pkt->decode(&multi_v4_packet_data[0], sizeof(multi_v4_packet_data)); } catch (InvalidPacket& e) { DOUT(info) << cstring(e) << endl; return false; } DOUT(info) << pkt->str() << endl; #if 1 { // Check that the 'first' and 'last' packet flags are set as // S-OLSR needs to see them. const vector& msgs = pkt->messages(); size_t index; vector::const_iterator ii; for (index = 0, ii = msgs.begin(); ii != msgs.end(); ii++, index++) { if (0 == index) XLOG_ASSERT((*ii)->is_first() == true); if ((msgs.size() - 1) == index) XLOG_ASSERT((*ii)->is_last() == true); } } #endif #if 1 // XXX refcounting was removed for now { const vector& msgs = pkt->messages(); vector::const_iterator ii; for (ii = msgs.begin(); ii != msgs.end(); ii++) { delete (*ii); } } #endif delete pkt; return true; } uint8_t hna_v4_packet_data[] = { #include "regression/hna_ok_ipv4.data" }; /** * Decode a packet containing an HNA message, and print it. */ bool hna_packet_decode(TestInfo& info) { MessageDecoder md; initialize_message_decoder(md); Packet* pkt = new Packet(md); try { pkt->decode(&hna_v4_packet_data[0], sizeof(hna_v4_packet_data)); } catch (InvalidPacket& e) { DOUT(info) << cstring(e) << endl; return false; } DOUT(info) << pkt->str() << endl; #if 1 // XXX refcounting was removed for now { const vector& msgs = pkt->messages(); vector::const_iterator ii; for (ii = msgs.begin(); ii != msgs.end(); ii++) { delete (*ii); } } #endif delete pkt; return true; } uint8_t mid_v4_packet_data[] = { #include "regression/mid_ok_ipv4.data" }; /** * Decode a packet containing a MID message, and print it. */ bool mid_packet_decode(TestInfo& info) { MessageDecoder md; initialize_message_decoder(md); Packet* pkt = new Packet(md); try { pkt->decode(&mid_v4_packet_data[0], sizeof(mid_v4_packet_data)); } catch (InvalidPacket& e) { DOUT(info) << cstring(e) << endl; return false; } DOUT(info) << pkt->str() << endl; #if 1 // XXX refcounting was removed for now { const vector& msgs = pkt->messages(); vector::const_iterator ii; for (ii = msgs.begin(); ii != msgs.end(); ii++) { delete (*ii); } } #endif delete pkt; return true; } uint8_t tc_v4_packet_data[] = { #include "regression/tc_ok_ipv4.data" }; /** * Decode a packet containing a TC message, and print it. */ bool tc_packet_decode(TestInfo& info) { MessageDecoder md; initialize_message_decoder(md); Packet* pkt = new Packet(md); try { pkt->decode(&tc_v4_packet_data[0], sizeof(tc_v4_packet_data)); } catch (InvalidPacket& e) { DOUT(info) << cstring(e) << endl; return false; } DOUT(info) << pkt->str() << endl; #if 1 // XXX refcounting was removed for now { const vector& msgs = pkt->messages(); vector::const_iterator ii; for (ii = msgs.begin(); ii != msgs.end(); ii++) { delete (*ii); } } #endif delete pkt; return true; } uint8_t hello_multi_v4_packet_data[] = { #include "regression/hello_multi_ok_ipv4.data" }; /** * Decode a packet containing a HELLO message, and print it. */ bool hello_multi_link_packet_decode(TestInfo& info) { MessageDecoder md; initialize_message_decoder(md); Packet* pkt = new Packet(md); try { pkt->decode(&hello_multi_v4_packet_data[0], sizeof(hello_multi_v4_packet_data)); } catch (InvalidPacket& e) { DOUT(info) << cstring(e) << endl; return false; } DOUT(info) << pkt->str() << endl; #if 1 // XXX refcounting was removed for now { const vector& msgs = pkt->messages(); vector::const_iterator ii; for (ii = msgs.begin(); ii != msgs.end(); ii++) { delete (*ii); } } #endif delete pkt; return true; } inline void populate_hna_for_encode(HnaMessage *hna) { TimeVal expiry(256,0); hna->set_expiry_time(expiry); hna->set_origin(IPv4("192.168.124.1")); hna->set_ttl(OlsrTypes::MAX_TTL); hna->set_hop_count(0); hna->set_seqno(38018); hna->add_network(IPv4Net("192.168.123.0/24")); hna->set_valid(true); } /** * Create a packet containing an HNA message, intended to be * identical to a hard-coded test packet. */ bool hna_packet_encode(TestInfo& info) { MessageDecoder md; Packet* pkt = new Packet(md); HnaMessage* hna = new HnaMessage(); populate_hna_for_encode(hna); pkt->add_message(hna); pkt->set_seqno(58449); vector buf1; vector buf2; pkt->encode(buf1); std::copy(&hna_v4_packet_data[0], &hna_v4_packet_data[0] + sizeof(hna_v4_packet_data), std::back_inserter(buf2)); bool result = compare_packets(info, buf1, buf2); delete hna; // XXX refcounting was removed delete pkt; return result; } inline void populate_mid_for_encode(MidMessage *mid) { TimeVal expiry(256,0); mid->set_expiry_time(expiry); mid->set_origin(IPv4("192.168.124.1")); mid->set_ttl(OlsrTypes::MAX_TTL); mid->set_hop_count(0); mid->set_seqno(38019); mid->add_interface(IPv4("192.168.122.1")); mid->add_interface(IPv4("192.168.123.1")); mid->add_interface(IPv4("192.168.125.1")); mid->set_valid(true); } /** * Create a packet containing a MID message, intended to be * identical to a hard-coded test packet. */ bool mid_packet_encode(TestInfo& info) { MessageDecoder md; Packet* pkt = new Packet(md); MidMessage* mid = new MidMessage(); populate_mid_for_encode(mid); pkt->add_message(mid); pkt->set_seqno(58450); vector buf1; vector buf2; pkt->encode(buf1); std::copy(&mid_v4_packet_data[0], &mid_v4_packet_data[0] + sizeof(mid_v4_packet_data), std::back_inserter(buf2)); bool result = compare_packets(info, buf1, buf2); delete mid; // XXX refcounting was removed delete pkt; return result; } inline void populate_tc_for_encode(TcMessage *tc) { TimeVal expiry(256,0); tc->set_expiry_time(expiry); tc->set_origin(IPv4("192.168.124.2")); tc->set_ttl(OlsrTypes::MAX_TTL); tc->set_hop_count(0); tc->set_seqno(38017); tc->set_ansn(1); tc->add_neighbor(IPv4("192.168.124.17")); tc->set_valid(true); } /** * Create a packet containing a TC message, intended to be * identical to a hard-coded test packet. */ bool tc_packet_encode(TestInfo& info) { MessageDecoder md; Packet* pkt = new Packet(md); TcMessage* tc = new TcMessage(); populate_tc_for_encode(tc); pkt->add_message(tc); pkt->set_seqno(58448); vector buf1; vector buf2; pkt->encode(buf1); std::copy(&tc_v4_packet_data[0], &tc_v4_packet_data[0] + sizeof(tc_v4_packet_data), std::back_inserter(buf2)); bool result = compare_packets(info, buf1, buf2); delete tc; // XXX refcounting was removed delete pkt; return result; } inline void populate_hello_for_encode(HelloMessage *hello) { TimeVal expiry(256,0); hello->set_expiry_time(expiry); hello->set_origin(IPv4("192.168.124.1")); hello->set_ttl(OlsrTypes::MAX_TTL); hello->set_hop_count(0); hello->set_seqno(38038); hello->set_htime(TimeVal(6,0)); hello->set_willingness(OlsrTypes::WILL_LOW); LinkCode lc1(OlsrTypes::NOT_NEIGH, OlsrTypes::ASYM_LINK); hello->add_link(lc1, IPv4("192.168.122.22")); hello->add_link(lc1, IPv4("192.168.122.23")); LinkCode lc2(OlsrTypes::SYM_NEIGH, OlsrTypes::SYM_LINK); hello->add_link(lc2, IPv4("192.168.122.24")); hello->add_link(lc2, IPv4("192.168.122.25")); hello->set_valid(true); } /** * Create a packet containing a HELLO message, intended to be * identical to a hard-coded test packet. */ bool hello_packet_encode(TestInfo& info) { MessageDecoder md; Packet* pkt = new Packet(md); HelloMessage* hello = new HelloMessage(); populate_hello_for_encode(hello); pkt->add_message(hello); pkt->set_seqno(58445); vector buf1; vector buf2; pkt->encode(buf1); #if 0 for (size_t i = 0; i < buf1.size(); i++) { fprintf(stderr, "0x%02x ", XORP_UINT_CAST(buf1[i])); } #endif std::copy(&hello_multi_v4_packet_data[0], &hello_multi_v4_packet_data[0] + sizeof(hello_multi_v4_packet_data), std::back_inserter(buf2)); bool result = compare_packets(info, buf1, buf2); delete hello; // XXX refcounting was removed delete pkt; return result; } int main(int argc, char **argv) { XorpUnexpectedHandler x(xorp_unexpected_handler); xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); TestMain t(argc, argv); string test = t.get_optional_args("-t", "--test", "run only the specified test"); t.complete_args_parsing(); struct test { string test_name; XorpCallback1::RefPtr cb; } tests[] = { {"hello_print", callback(hello_packet_print)}, {"tc_print", callback(tc_packet_print)}, {"mid_print", callback(mid_packet_print)}, {"hna_print", callback(hna_packet_print)}, {"hello_multi_decode", callback(hello_multi_link_packet_decode)}, {"tc_decode", callback(tc_packet_decode)}, {"mid_decode", callback(mid_packet_decode)}, {"hna_decode", callback(hna_packet_decode)}, {"multi_decode", callback(multi_packet_decode)}, {"hello_encode", callback(hello_packet_encode)}, {"tc_encode", callback(tc_packet_encode)}, {"mid_encode", callback(mid_packet_encode)}, {"hna_encode", callback(hna_packet_encode)}, }; try { if (test.empty()) { for (size_t i = 0; i < sizeof(tests) / sizeof(struct test); i++) t.run(tests[i].test_name, tests[i].cb); } else { for (size_t i = 0; i < sizeof(tests) / sizeof(struct test); i++) if (test == tests[i].test_name) { t.run(tests[i].test_name, tests[i].cb); return t.exit(); } t.failed("No test with name " + test + " found\n"); } } catch(...) { xorp_catch_standard_exceptions(); } xlog_stop(); xlog_exit(); return t.exit(); } xorp/contrib/olsr/face_manager.hh0000664000076400007640000005557011540224221017215 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8 sw=4: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/contrib/olsr/face_manager.hh,v 1.3 2008/10/02 21:56:34 bms Exp $ #ifndef __OLSR_FACE_MANAGER_HH__ #define __OLSR_FACE_MANAGER_HH__ class Olsr; class Message; class Face; class FaceManager; class LogicalLink; class Neighbor; class Neighborhood; /** * @short A callback used to process a specific type of OLSR * protocol message. */ typedef XorpCallback3::RefPtr MessageReceiveCB; /** * @short A member of the duplicate set. * * This is contained within a map keyed by its origin address. */ class DupeTuple { public: DupeTuple(EventLoop& ev, FaceManager* parent, const IPv4& origin, const uint16_t seqno, const TimeVal& vtime) : _ev(ev), _parent(parent), _origin(origin), _seqno(seqno), _is_forwarded(false) { update_timer(vtime); } /** * @return the origin of this duplicate set tuple. */ inline IPv4 origin() const { return _origin; } /** * @return the sequence number of this duplicate set tuple. */ inline uint16_t seqno() const { return _seqno; } /** * @return true if the message has previously been forwarded. */ inline bool is_forwarded() const { return _is_forwarded; } /** * Set the forwarded flag for this message. * * @param is_forwarded the new value of _is_forwarded. */ inline void set_is_forwarded(const bool is_forwarded) { _is_forwarded = is_forwarded; } /** * Determine if an interface has already received this message. * * @param faceid the ID of the interface to check. * @return true if this message has previously been received by faceid. */ inline bool is_seen_by_face(const OlsrTypes::FaceID faceid) const { return (_iface_list.find(faceid) != _iface_list.end()); } /** * Add an interface to the list of interfaces which have already * received this message. * * @param faceid the ID of the interface to add. */ inline void set_seen_by_face(const OlsrTypes::FaceID faceid) { if (_iface_list.find(faceid) == _iface_list.end()) _iface_list.insert(faceid); } /** * Update the validity timer on this duplicate set entry. * * @param vtime relative validity time from now. */ void update_timer(const TimeVal& vtime); /** * Callback method to: remove a duplicate set entry when it expires. */ void event_dead(); private: EventLoop& _ev; FaceManager* _parent; set _iface_list; // D_iface_list IPv4 _origin; // D_addr uint16_t _seqno; // D_seq_num bool _is_forwarded; // D_retransmitted XorpTimer _expiry_timer; // D_time }; /** * @short Class which manages all interface bindings * in the OLSR routing process. */ class FaceManager { public: FaceManager(Olsr& olsr, EventLoop& ev); ~FaceManager(); MessageDecoder& message_decoder() { return _md; } Neighborhood* neighborhood() { return _nh; } void set_neighborhood(Neighborhood* nh) { _nh = nh; } /** * @return the number of interfaces known to FaceManager. */ uint32_t get_face_count() const { return _faces.size(); } /** * Get a pointer to a Face given its ID. * * @param faceid the face ID. * @return pointer to the face. * @throw BadFace if the face ID cannot be found. */ const Face* get_face_by_id(const OlsrTypes::FaceID faceid) const throw(BadFace); /** * Fill out a list of all face IDs. * * @param face_list the list to fill out. */ void get_face_list(list& face_list) const; /** * @return the OLSR main address of this node. */ IPv4 get_main_addr() const { return _main_addr; } /** * Attempt to set main address on node. * * TODO: Track address changes on interfaces. * TODO: Add a flag which does NOT allow the main address to * automatically change when interfaces change. * * This will fulfil the RFC requirement that the main address may never * change, but will effectively halt olsr when the main address is set * to an address we are not configured for. */ bool set_main_addr(const IPv4& addr); /** * @return the MID_INTERVAL protocol variable. */ TimeVal get_mid_interval() const { return _mid_interval; } /** * Set the MID_INTERVAL protocol variable. * The timer will be rescheduled if it is running. * * @param interval the new MID_INTERVAL. */ void set_mid_interval(const TimeVal& interval); /** * @return the MID_HOLD_TIME protocol variable. */ TimeVal get_mid_hold_time() const { return _mid_interval * 3; } /** * @return the DUP_HOLD_TIME protocol variable. */ TimeVal get_dup_hold_time() const { return _dup_hold_time; } /** * Set the hold time for duplicate messages. * * Note: This does not affect messages which are currently being tracked * in the duplicate set. * * @param dup_hold_time the new hold time for duplicate messages. */ void set_dup_hold_time(const TimeVal& dup_hold_time); /** * Process a received datagram. * * @param interface the interface where the datagram was received. * @param vif the vif where the datagram was received. * @param dst the IPv4 destination address of the datagram. * @param dport the UDP destination port of the datagram. * @param src the IPv4 source address of the datagram. * @param sport the UDP source port of the datagram. * @param data the received datagram. * @param len the length of the received datagram. */ void receive(const string& interface, const string& vif, const IPv4 & dst, const uint16_t & dport, const IPv4 & src, const uint16_t & sport, uint8_t* data, const uint32_t & len); /** * Transmit a datagram. * * @param interface the interface to transmit from. * @param vif the vif to transmit from. * @param dst the IPv4 destination address to send to. * @param dport the UDP destination port to send to. * @param src the IPv4 source address to transmit from. * @param sport the UDP source port to transmit from. * @param data the datagram to transmit. * @param len the length of the datagram to transmit. * @return true if the datagram was sent OK, otherwise false. */ bool transmit(const string& interface, const string& vif, const IPv4 & dst, const uint16_t & dport, const IPv4 & src, const uint16_t & sport, uint8_t* data, const uint32_t & len); /** * Add a new interface. * * @param interface the name of the interface as known to the FEA. * @param vif the name of the vif as known to the FEA. * @return the ID of the new interface. * @throw BadFace if the interface could not be added. */ OlsrTypes::FaceID create_face(const string& interface, const string& vif) throw(BadFace); /** * Clear the interface list. */ void clear_faces(); /** * Activate the OLSR binding to the given interface. * * This means recomputing address lists and choosing the primary * address on that interface; we do not yet support changing the * address used. * * We do not bring up any sockets until set_face_enabled(). */ bool activate_face(const string & interface, const string & vif); /** * Delete an interface. * * @param faceid the ID of the Face to delete. * @return true if the interface was deleted. */ bool delete_face(OlsrTypes::FaceID faceid); /** * Recompute the list of protocol addresses used * by OLSR to send and receive protocol control traffic * on this Face. * * @param faceid the ID of the Face to recompute addresses on. * @return true if the address lists were recomputed successfully. * * This method works significantly differently than in OSPF. * In OLSR, we need to recompute the list of addresses to which * OLSR control traffic may originate from or be received on * whenever the configured addresses on an interface change. * * This relates only to the MID message; there is no strict * parallel to the network LSA in OSPF. */ bool recompute_addresses_face(OlsrTypes::FaceID faceid); /** * Callback method to: Process a change of vif status from the FEA. * * @param interface the name of the affected interface. * @param vif the name of the affected vif. * @param state the new state of the vif. */ void vif_status_change(const string& interface, const string& vif, bool state); /** * Callback method to: Process a change of address status from the FEA. * * @param interface the name of the affected interface. * @param vif the name of the affected vif. * @param addr the affected IPv4 interface address. * @param state the new state of the address. */ void address_status_change(const string& interface, const string& vif, IPv4 addr, bool state); /** * Get the ID of an interface, given its names as known to the FEA. * * @param interface the name of the interface to look up. * @param vif the name of the vif to look up. * @return the ID of the face. * @throw BadFace if the interface was not found. */ OlsrTypes::FaceID get_faceid(const string& interface, const string& vif) throw(BadFace); /** * Get the FEA names of an interface, given its OLSR interface ID. * * @param faceid the ID of the interface to look up. * @param interface the name of the interface if found. * @param vif the name of the vif if found. * @return true if the interface was found, otherwise false. */ bool get_interface_vif_by_faceid(OlsrTypes::FaceID faceid, string & interface, string & vif); /** * Set the cost of an interface. * Used by shortest path calculation. * * @param faceid the ID of the interface to set cost for. * @param cost the interface cost to set. * @return true if the interface cost was set, otherwise false. */ bool get_interface_cost(OlsrTypes::FaceID faceid, int& cost); /** * Enable the OLSR binding on the given interface. * * This method is responsible for realizing the * socket needed by the Face. * If more than one interface is enabled, this method MAY * start the MID protocol t imer. */ bool set_face_enabled(OlsrTypes::FaceID faceid, bool enabled); /** * Get the "administratively up" status of an OLSR interface. * * @param faceid the ID of Face to query enabled state for. * @return true if faceid is enabled. */ bool get_face_enabled(OlsrTypes::FaceID faceid); /** * Set the cost of an OLSR interface. * * @param faceid the ID of Face to set cost for. * @param cost the cost to set. * @return true if the cost was set successfully, otherwise false. */ bool set_interface_cost(OlsrTypes::FaceID faceid, const int cost); /** * Set the IPv4 local address of an interface. * * @param faceid the ID of Face to set local address for. * @param local_addr the local address to set. * @return true if the local address was set successfully, * otherwise false. */ bool set_local_addr(OlsrTypes::FaceID faceid, const IPv4& local_addr); /** * Get the IPv4 local address of an interface. * * @param faceid the ID of Face to get local address for. * @param local_addr the local address, if interface was found. * @return true if the local address was retrieved successfully, * otherwise false. */ bool get_local_addr(OlsrTypes::FaceID faceid, IPv4& local_addr); /** * Set the UDP local port of an interface. * * @param faceid the ID of Face to set local port for. * @param local_port the local port to set. * @return true if the local port was set successfully, * otherwise false. */ bool set_local_port(OlsrTypes::FaceID faceid, const uint16_t local_port); /** * Get the UDP local port of an interface. * * Uses [] so can't be declared const. * * @param faceid the ID of Face to get local port for. * @param local_port the UDP local port, if interface was found. * @return true if the local port was retrieved successfully, * otherwise false. */ bool get_local_port(OlsrTypes::FaceID faceid, uint16_t& local_port); /** * Set the IPv4 all-nodes address of an interface. * * @param faceid the ID of Face to set all-nodes address for. * @param all_nodes_addr the all-nodes address to set. * @return true if the all-nodes address was set successfully, * otherwise false. */ bool set_all_nodes_addr(OlsrTypes::FaceID faceid, const IPv4& all_nodes_addr); /** * Get the IPv4 all-nodes address of an interface. * * Uses [] so can't be declared const. * * @param faceid the ID of Face to get all-nodes address for. * @param all_nodes_addr output variable which will contain the * all-nodes address. * @return true if the all-nodes address was retrieved successfully, * otherwise false. */ bool get_all_nodes_addr(OlsrTypes::FaceID faceid, IPv4& all_nodes_addr); /** * Set the UDP all-nodes port of an interface. * * @param faceid the ID of Face to set the all-nodes port for. * @param all_nodes_port the all-nodes port to set. * @return true if the all-nodes port was set successfully, * otherwise false. */ bool set_all_nodes_port(OlsrTypes::FaceID faceid, const uint16_t all_nodes_port); /** * Get the UDP all-nodes port of an interface. * * @param faceid the ID of Face to get the all-nodes port for. * @param all_nodes_port output variable which will contain the * all-nodes port. * @return true if the all-nodes port was retrieved successfully, * otherwise false. */ bool get_all_nodes_port(OlsrTypes::FaceID faceid, uint16_t& all_nodes_port); /** * Flood a message on all interfaces, performing appropriate * fragmentation for each interface. * * @param message The message to flood, will be deleted by this method. */ bool flood_message(Message* message); /** * @return the next sequence number to use for a transmitted Message. */ uint16_t get_msg_seqno() { return _next_msg_seqno++; } /** * Stop all timers (HELLO and MID). */ void stop_all_timers(); void start_hello_timer(); void stop_hello_timer(); void restart_hello_timer(); /** * Reschedule the HELLO protocol timer, if its interval changed. */ void reschedule_hello_timer(); /** * Reschedule the HELLO protocol timer to fire as soon as possible. */ void reschedule_immediate_hello_timer(); /** * Broadcast a HELLO message on each enabled interface. */ bool event_send_hello(); void start_mid_timer(); void stop_mid_timer(); void restart_mid_timer(); /** * Reschedule the MID protocol timer, if its interval changed. */ void reschedule_mid_timer(); /** * Reschedule the MID protocol timer to fire as soon as possible. */ void reschedule_immediate_mid_timer(); /** * Callback method to: Send a MID message on each enabled interface. */ bool event_send_mid(); /** * Set the HELLO_INTERVAL protocol variable. * * The HELLO timer will be rescheduled if running. */ void set_hello_interval(const TimeVal& interval); /** * @return the HELLO_INTERVAL protocol variable. */ TimeVal get_hello_interval() { return _hello_interval; } /** * @return the MAX_JITTER protocol variable. */ TimeVal get_max_jitter() { return _hello_interval / 4; } /** * @return the number of interfaces enabled for OLSR. */ inline uint32_t get_enabled_face_count() const { return _enabled_face_count; } /** * Register an OLSR protocol message handler. * * MessageReceiveCBs are invoked in reverse order to which they * have been registered. The FaceManager always registers a handler * for unknown message types first, so that such messages will be * forwarded, even if XORP has no handler for it. Each handler * thus registered is given an opportunity to claim the message * as its own, by returning true. * * C++ dynamic casts are used at runtime by each MessageReceiveCB * to determine if it should consume the message. * * @param cb the message receive callback function to register. */ void add_message_cb(MessageReceiveCB cb); /** * Deregister an OLSR protocol message handler. * * @param cb the message receive callback function to deregister. * @return true if cb was deregistered, otherwise false. */ bool delete_message_cb(MessageReceiveCB cb); /** * Callback method to: forward a Message of unknown type to * the rest of the OLSR topology. */ bool event_receive_unknown(Message* msg, const IPv4& remote_addr, const IPv4& local_addr); /** * Implement the Default Forwarding Algorithm (Section 3.4.1). * * @param remote_addr the interface address of the node which sent or * forwarded this message to us. * Note: This may not be the origin if the message * has previously been forwarded. * @param msg the message itself. * @return true if the message will be forwarded to other nodes. */ bool forward_message(const IPv4& remote_addr, Message* msg); /** * Check if a message is a previously seen duplicate. * * @return true if the message is a duplicate and should neither be * processed or forwarded. */ bool is_duplicate_message(const Message* msg) const; /** * Check if a message should be forwarded, according to the default * forwarding rules. * * @param msg the message to check. * @return true if the message SHOULD NOT be forwarded, false if it is * OK to forward the message. */ bool is_forwarded_message(const Message* msg) const; /** * Get a pointer to a tuple in the duplicate set for a message, given * its origin address and sequence number. * * Avoid throwing an exception as this path may be called * very frequently when forwarding or flooding messages. * This however means we return a pointer. * * @param origin_addr the protocol address of the message origin. * @param seqno the sequence number of the message. * @return the pointer to the duplicate set entry, or 0 if none exists. */ DupeTuple* get_dupetuple(const IPv4& origin_addr, const uint16_t seqno) const; /** * Update or create an entry in the duplicate message set. * * Given a message, update or create its duplicate set tuple in order * that we can detect if other nodes in the OLSR domain loop a message * back to us. * OLSR uses histogram based message loop detection, as the shortest * path tree may be out of phase with the real network topology. * * @param msg the message itself. * @param is_forwarded true if the message will be forwarded. */ void update_dupetuple(const Message* msg, const bool is_forwarded); /** * Delete a duplicate set entry when it expires. */ void event_dupetuple_expired(const IPv4& origin, const uint16_t seqno); /** * Clear the duplicate set. */ void clear_dupetuples(); /** * @return true if the given address belongs to any * locally configured interface. */ bool is_local_addr(const IPv4& addr); /** * Get the statistics for the given face. * * @param ifname the name of the interface to retrieve stats for. * @param vifname the name of the vif to retrieve stats for. * @param stats output; reference to an empty FaceCounters object. * @return true if the stats were retrieved, otherwise false. */ bool get_face_stats(const string& ifname, const string& vifname, FaceCounters& stats); private: Olsr& _olsr; EventLoop& _eventloop; MessageDecoder _md; Neighborhood* _nh; OlsrTypes::FaceID _next_faceid; uint32_t _enabled_face_count; /** * @short The next available message sequence number. */ uint16_t _next_msg_seqno; /** * @short primary protocol address of this node; * used as 'origin' for OLSR control messages. */ IPv4 _main_addr; /** * @short Vector of message handlers. */ vector _handlers; /** * @short Map interface/vif to OlsrTypes::FaceID. */ map _faceid_map; /** * @short Map FaceID to Face. */ map _faces; /** * Maintain state about messages we may have already processed * and/or forwarded. HELLO messages are specifically excluded. * Section 3.4: Duplicate Tuple. */ typedef multimap DupeTupleMap; DupeTupleMap _duplicate_set; /** * @short The HELLO_INTERVAL protocol control variable. * RFC 3626 Section 18.2. */ TimeVal _hello_interval; XorpTimer _hello_timer; /** * @short The MID_INTERVAL protocol control variable. * RFC 3626 Section 18.2. */ TimeVal _mid_interval; XorpTimer _mid_timer; /** * @short The DUP_HOLD_TIME protocol control variable. * Section 3.4.1: Default Forwarding Algorithm. */ TimeVal _dup_hold_time; /** * @short true if a MID message should be queued as soon as * the set of configured interfaces changes. */ bool _is_early_mid_enabled; }; #endif // __OLSR_FACE_MANAGER_HH__ xorp/contrib/olsr/route_manager.cc0000664000076400007640000004644411540225522017450 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8 sw=4: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "olsr_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/callback.hh" #include "libxorp/ipv4.hh" #include "libxorp/ipv4net.hh" #include "libxorp/status_codes.h" #include "libxorp/service.hh" #include "libxorp/eventloop.hh" // #define DEBUG_LOGGING // #define DEBUG_FUNCTION_NAME // #define DETAILED_DEBUG #include "libproto/spt.hh" #include "olsr.hh" #include "vertex.hh" #include "route_manager.hh" // TODO: Convert assertions to exception throws in vertex/link additions. // TODO: Support composite metrics. // At the moment we use a simple scalar scheme which conforms to the RFC. // TODO: Optionally withdraw all unfiltered routes from RIB on shutdown. // TODO: Support ETX metrics. // TODO: Support advertised metric scheme for HNA routes. // TODO: Support computation of external routes independently of SPT. // Currently an entire routing calculation is performed when external // routes change. // TODO: Support incremental SPT re-computation. string RouteEntry::str() { string output; output = c_format("RouteEntry: "); output += c_format("%s ", vt_to_str(destination_type())); output += c_format("%s", direct() ? "direct " : ""); if (is_node_vertex(_destination_type)) { output += c_format("mainaddr %s ", cstring(main_address())); } output += c_format("cost %d ", XORP_UINT_CAST(cost())); output += c_format("nexthop %s ", cstring(nexthop())); output += c_format("originator %s ", cstring(originator())); return output; } RouteManager::RouteManager(Olsr& olsr, EventLoop& eventloop, FaceManager* fm, Neighborhood* nh, TopologyManager* tm, ExternalRoutes* er) : _olsr(olsr), _eventloop(eventloop), _fm(fm), _nh(nh), _tm(tm), _er(er), _spt(Spt(_olsr.trace()._spt)), _in_transaction(false), _current(0), _previous(0) { // // Create the route computation background task but make sure it is not // scheduled until: // 1. we have either acquired artifacts which will form part of the // route table (if doing incremental calculation), or: // 2. we are asked to by our clients. // _route_update_task = _eventloop.new_oneoff_task( callback(this, &RouteManager::recompute_all_routes), XorpTask::PRIORITY_DEFAULT, XorpTask::WEIGHT_DEFAULT); _route_update_task.unschedule(); } RouteManager::~RouteManager() { _route_update_task.unschedule(); #if 1 // XXX Ensure that clients can't see us any more. if (_er) _er->set_route_manager(0); if (_tm) _tm->set_route_manager(0); if (_nh) _nh->set_route_manager(0); #endif // Clear internal routing tables. delete _previous; delete _current; } void RouteManager::schedule_route_update() { _route_update_task.reschedule(); } void RouteManager::schedule_external_route_update() { _route_update_task.reschedule(); } void RouteManager::begin() { debug_msg("\n"); XLOG_ASSERT(! _in_transaction); _in_transaction = true; delete _previous; _previous = _current; _current = new Trie; if (0 == _previous) return; // No checks for other sub-protocols should be needed. } void RouteManager::end() { debug_msg("\n"); XLOG_ASSERT(_in_transaction); _in_transaction = false; Trie::iterator tip; Trie::iterator tic; // If no previous routing table, just install current one. if (0 == _previous) { for (tic = _current->begin(); tic != _current->end(); tic++) { RouteEntry& rt = tic.payload(); if (! add_route(tic.key(), rt.nexthop(), rt.cost(), rt)) { XLOG_WARNING("Add of %s failed", cstring(tic.key())); } } return; } // Withdraw routes which no longer exist in new table. for (tip = _previous->begin(); tip != _previous->end(); tip++) { if (_current->end() == _current->lookup_node(tip.key())) { RouteEntry& rt = tip.payload(); if (! delete_route(tip.key(), rt)) { XLOG_WARNING("Delete of %s failed", cstring(tip.key())); } } } // Add or replace routes which exist in new table. for (tic = _current->begin(); tic != _current->end(); tic++) { RouteEntry& rt = tic.payload(); tip = _previous->lookup_node(tic.key()); if (_previous->end() == tip) { // The route is new and should be added. if (! add_route(tic.key(), rt.nexthop(), rt.cost(), rt)) { XLOG_WARNING("Add of %s failed", cstring(tic.key())); } } else { // The route already exists and may possibly be replaced. RouteEntry& rt_previous = tip.payload(); if (rt.nexthop() != rt_previous.nexthop() || rt.cost() != rt_previous.cost()) { // The cost or nexthop changed; replace the route. if (! replace_route(tip.key(), rt.nexthop(), rt.cost(), rt, rt_previous)) { XLOG_WARNING("Replace of %s failed", cstring(tip.key())); } } else { // No change; just update policy filtering state. rt.set_filtered(rt_previous.filtered()); } } } } void RouteManager::recompute_all_routes() { debug_msg("%s: recomputing all routes\n", cstring(_fm->get_main_addr())); // Clear the Spt. _spt.clear(); // Add the origin node. _origin = make_origin_vertex(); Node::NodeRef vtmp = _spt.find_node(_origin); if (vtmp.is_empty() || !vtmp->valid()) { _spt.add_node(_origin); } _spt.set_origin(_origin); // Ask the associated classes to push their topology to us. // They will call us back to populate the SPT. _nh->push_topology(); _tm->push_topology(); // Perform the SPT route computation. list > r; _spt.compute(r); // Begin a new routing trie transaction. begin(); // Produce route entries from SPT computation result. // This takes care of all steps of Section 10, except for // 3.2 next-hop selection which needs weighting in Vertex // for MPRs, which we'll do with the new composite metric scheme. // Assumes the non-incremental version of SPT. list >::const_iterator ri; for (ri = r.begin(); ri != r.end(); ri++) { const Vertex& node = ri->node(); const Vertex& nexthop = ri->nexthop(); RouteEntry rt; IPv4 addr; IPv4Net dest; #ifdef DETAILED_DEBUG // Invariant: Destination vertex must be derived from SPT. XLOG_ASSERT(node.type() == OlsrTypes::VT_NEIGHBOR || node.type() == OlsrTypes::VT_TWOHOP || node.type() == OlsrTypes::VT_TOPOLOGY); // Invariant: Next-hop thus chosen must be a one-hop neighbor. XLOG_ASSERT(nexthop.type() == OlsrTypes::VT_NEIGHBOR); #endif rt.set_destination_type(node.type()); // what produced the route rt.set_originator(node.producer()); // who produced the route rt.set_main_address(node.main_addr()); // where it goes rt.set_cost(ri->weight()); // how much the path costs // If the node *is* its own nexthop, it's directly connected. rt.set_direct(node == nexthop); // Links in the one-hop neighborhood must use the // address advertised in the HELLO link tuple as the next hop. if (node.type() == OlsrTypes::VT_NEIGHBOR) { addr = node.link()->remote_addr(); rt.set_nexthop(addr); rt.set_faceid(node.link()->faceid()); } else { // Links beyond the one-hop neighborhood must use the // link address of the first hop as the next-hop address, // as there might not be a RIB to catch our unresolved next-hops. // // The destination must be the main address, as we have no // link specific information at this point; MID aliases will // be added further below. addr = node.main_addr(); rt.set_nexthop(nexthop.link()->remote_addr()); rt.set_faceid(nexthop.link()->faceid()); } // OLSR routes are always host routes. dest = IPv4Net(addr, IPv4::ADDR_BITLEN); add_entry(dest, rt); // // If the route being added is to a one-hop neighbor, // and its link address differs from its main address, // make sure we also add a route to its main address, // as per RFC. This is separate from MID processing. // The nexthop should remain unchanged. // if (node.type() == OlsrTypes::VT_NEIGHBOR && node.main_addr() != node.link()->remote_addr()) { IPv4Net main_dest(node.main_addr(), IPv4::ADDR_BITLEN); add_entry(main_dest, rt); } // 10, 4: Add routes to each MID alias known for the node. vector aliases = _tm->get_mid_addresses(node.main_addr()); if (! aliases.empty()) { // Mark the routes learned from MID as such. rt.set_destination_type(OlsrTypes::VT_MID); vector::iterator ai; for (ai = aliases.begin(); ai != aliases.end(); ai++) { IPv4& alias_addr = (*ai); // If we just added a route to a one-hop neighbor, then // skip the link specific address or primary address as // we just dealt with those. if (node.type() == OlsrTypes::VT_NEIGHBOR && (alias_addr == node.link()->remote_addr() || alias_addr == node.main_addr())) continue; // Add a route to this MID alias for the node. IPv4Net mid_dest(alias_addr, IPv4::ADDR_BITLEN); add_entry(mid_dest, rt); } } } // Ask ExternalRoutes to push its state to us. _er->push_external_routes(); // Push the routes to the RIB, via policy. end(); } bool RouteManager::add_onehop_link(const LogicalLink* l, const Neighbor* n) { // Invariant: RouteManager should never see a Neighbor with WILL_NEVER. XLOG_ASSERT(n->willingness() != OlsrTypes::WILL_NEVER); // Create the neighbor vertex. Vertex v_n(*n); v_n.set_faceid(l->faceid()); // Asssociate l with v_n, for doing the MID routes correctly later on; // we must choose a single link for Dijkstra calculation, and there // is no way of associating attributes with edges just now. v_n.set_link(l); bool is_n_added = _spt.add_node(v_n); XLOG_ASSERT(true == is_n_added); // 10, 3.2: When h=1, nodes with highest willingness and MPR selectors // are preferred as next hop. Break ties using a simple scalar scheme. int cost; _fm->get_interface_cost(l->faceid(), cost); cost += OlsrTypes::WILL_MAX - n->willingness(); if (! n->is_mpr_selector()) cost++; // Add the link from myself to v_n. bool is_link_added = _spt.add_edge(_origin, cost, v_n); XLOG_ASSERT(true == is_link_added); return is_link_added; } bool RouteManager::add_twohop_link(const Neighbor* n, const TwoHopLink* l2, const TwoHopNeighbor* n2) { debug_msg("adding n2 %s via n1 %s\n", cstring(n2->main_addr()), cstring(n->main_addr())); // Assert that the Neighbor exists. // TODO: Throw an exception, we should never see this. Vertex v_n(*n); if (! _spt.exists_node(v_n)) { debug_msg("N1 %s does not exist.\n", cstring(n->main_addr())); return false; } // Make a Vertex from TwoHopNeighbor. Vertex v_n2(*n2); // We need to know the origin of the vertex. In the case // of a two-hop neighbor, this is the neighbor which advertises // the link to the two-hop neighbor, N. v_n2.set_producer(n->main_addr()); // Associate l2 with v_n, for doing the MID routes correctly later on; // we must choose a single link for Dijkstra calculation, and there // is no way of associating attributes with edges just now. v_n2.set_twohop_link(l2); bool is_n2_added = _spt.add_node(v_n2); XLOG_ASSERT(true == is_n2_added); // Add the link from v_n to v_n2. bool is_link_added = _spt.add_edge(v_n, 1, v_n2); XLOG_ASSERT(true == is_link_added); return is_link_added; UNUSED(l2); } bool RouteManager::add_tc_link(const TopologyEntry* tc) { // Check that the near endpoint (last hop) exists. // It should be a two-hop neighbor when tc->distance() is 2, // and a TC node in all other cases, however this is a pedantic invariant. // // It's problematic that we can't separate the semantics of search // in Spt from storage without considerably changing the class. // // Note: The TC input code can only measure the distance from the // node to the TC message's origin. Vertex v_lh(tc->lasthop()); if (! _spt.exists_node(v_lh)) { debug_msg("Ignoring TC %p as last-hop to %s not in SPT, v_lh:\n%s\n", tc, cstring(tc->destination()), cstring(v_lh)); return false; } // Add the vertex for the remote endpoint learned from TC. Vertex v_tc(*tc); Node::NodeRef vtmp = _spt.find_node(v_tc); // Don't try to add it if it already exists..spams the logs with spt.hh warnings. if (vtmp.is_empty() || !vtmp->valid()) { _spt.add_node(v_tc); } // Add the link from v_lh to v_tc. // For a non-LQ link the cost is always 1. // A trace statement may be printed if the link already exists. bool is_link_added = _spt.add_edge(v_lh, 1, v_tc); UNUSED(is_link_added); return true; } bool RouteManager::add_hna_route(const IPv4Net& dest, const IPv4& lasthop, const uint16_t distance) { // We need to look up the route to the node which advertises the // prefix 'dest' to know what the next-hop should be. Trie::iterator tic = _current->lookup_node(IPv4Net(lasthop, IPv4::ADDR_BITLEN)); if (tic == _current->end()) { debug_msg("%s: skipping HNA %s, no route to origin %s\n", cstring(_fm->get_main_addr()), cstring(dest), cstring(lasthop)); return false; } RouteEntry& ort = tic.payload(); // The route to the origin. RouteEntry rt; // The route we are about to add. rt.set_destination_type(OlsrTypes::VT_HNA); // what produced the route rt.set_originator(lasthop); // who produced the route rt.set_nexthop(ort.nexthop()); // the next hop // 12.6: The cost of an RFC compliant HNA route is that of // the path used to reach the OLSR node which advertises it. rt.set_cost(ort.cost()); add_entry(dest, rt); return true; // Currently the distance measured from the HNA messages is unused; // this parameter may change in future. UNUSED(distance); } bool RouteManager::add_entry(const IPv4Net& net, const RouteEntry& rt) { debug_msg("add %s %s\n", cstring(net), cstring(const_cast(rt))); XLOG_ASSERT(_in_transaction); XLOG_ASSERT(rt.direct() || rt.nexthop() != IPv4::ZERO()); bool result = true; Trie::iterator tic; tic = _current->lookup_node(net); if (_current->end() == tic) { _current->insert(net, rt); } return result; } bool RouteManager::delete_entry(const IPv4Net& net, const RouteEntry& rt) { debug_msg("delete %s %s\n", cstring(net), cstring(const_cast(rt))); Trie::iterator tic; tic = _current->lookup_node(net); if (_current->end() != tic) { _current->erase(tic); } return false; UNUSED(rt); } bool RouteManager::replace_entry(const IPv4Net& net, const RouteEntry& rt, const RouteEntry& previous_rt) { debug_msg("replace %s %s\n", cstring(net), cstring(const_cast(previous_rt))); delete_entry(net, previous_rt); bool result = add_entry(net, rt); return result; } bool RouteManager::add_route(IPv4Net net, IPv4 nexthop, uint32_t metric, RouteEntry& rt) { debug_msg("ADD ROUTE net %s nexthop %s metric %u\n", cstring(net), cstring(nexthop), metric); bool result = true; PolicyTags policytags; bool accepted = do_filtering(net, nexthop, metric, rt, policytags); rt.set_filtered(!accepted); if (accepted) { result = _olsr.add_route(net, nexthop, rt.faceid(), metric, policytags); } return result; } bool RouteManager::delete_route(const IPv4Net& net, const RouteEntry& rt) { debug_msg("DELETE ROUTE %s filtered %s\n", cstring(net), bool_c_str(rt.filtered())); bool result = false; if (! rt.filtered()) { result = _olsr.delete_route(net); } else { result = true; } return result; } bool RouteManager::replace_route(IPv4Net net, IPv4 nexthop, uint32_t metric, RouteEntry& rt, RouteEntry& previous_rt) { debug_msg("REPLACE ROUTE %s\n", cstring(net)); bool result = delete_route(net, previous_rt); if (!result) XLOG_WARNING("Failed to delete: %s", cstring(net)); result = add_route(net, nexthop, metric, rt); return result; } void RouteManager::push_routes() { Trie::iterator tic; if (0 == _current) return; for (tic = _current->begin(); tic != _current->end(); tic++) { RouteEntry& rt = tic.payload(); PolicyTags policytags; IPv4Net net = tic.key(); IPv4 nexthop = rt.nexthop(); uint32_t faceid = rt.faceid(); uint32_t metric = rt.cost(); bool accepted = do_filtering(net, nexthop, metric, rt, policytags); if (accepted) { if (! rt.filtered()) { _olsr.replace_route(net, nexthop, faceid, metric, policytags); } else { _olsr.add_route(net, nexthop, faceid, metric, policytags); } } else { if (! rt.filtered()) { _olsr.delete_route(net); } } rt.set_filtered(!accepted); } } bool RouteManager::do_filtering(IPv4Net& net, IPv4& nexthop, uint32_t& metric, RouteEntry& rt, PolicyTags& policytags) { #ifdef notyet // Unlike OSPF, OLSR is expected to send host routes to the // RIB which may be directly connected. if (rt.direct()) return false; #endif try { IPv4 originator = rt.originator(); IPv4 main_addr = rt.main_address(); uint32_t type = rt.destination_type(); // Import filtering. OlsrVarRW varrw(net, nexthop, metric, originator, main_addr, type, policytags); bool accepted = false; debug_msg("[OLSR] Running filter: %s on route: %s\n", filter::filter2str(filter::IMPORT), cstring(net)); XLOG_TRACE(_olsr.trace()._import_policy, "[OSPF] Running filter: %s on route: %s\n", filter::filter2str(filter::IMPORT), cstring(net)); accepted = _olsr.get_policy_filters(). run_filter(filter::IMPORT, varrw); if (!accepted) return accepted; // Export source-match filtering. OlsrVarRW varrw2(net, nexthop, metric, originator, main_addr, type, policytags); debug_msg("[OLSR] Running filter: %s on route: %s\n", filter::filter2str(filter::EXPORT_SOURCEMATCH), cstring(net)); XLOG_TRACE(_olsr.trace()._import_policy, "[OLSR] Running filter: %s on route: %s\n", filter::filter2str(filter::EXPORT_SOURCEMATCH), cstring(net)); _olsr.get_policy_filters(). run_filter(filter::EXPORT_SOURCEMATCH, varrw2); return accepted; } catch(const PolicyException& e) { XLOG_WARNING("PolicyException: %s", cstring(e)); return false; } return true; } xorp/contrib/olsr/Doxyfile0000664000076400007640000015417511421137511016016 0ustar greearbgreearb# Doxyfile 1.5.4 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project # # All text after a hash (#) is considered a comment and will be ignored # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" ") #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file that # follow. The default is UTF-8 which is also the encoding used for all text before # the first occurrence of this tag. Doxygen uses libiconv (or the iconv built into # libc) for the transcoding. See http://www.gnu.org/software/libiconv for the list of # possible encodings. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. PROJECT_NAME = "XORP/OLSR" # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, # Croatian, Czech, Danish, Dutch, Finnish, French, German, Greek, Hungarian, # Italian, Japanese, Japanese-en (Japanese with English messages), Korean, # Korean-en, Lithuanian, Norwegian, Polish, Portuguese, Romanian, Russian, # Serbian, Slovak, Slovene, Spanish, Swedish, and Ukrainian. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = YES # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. STRIP_FROM_PATH = . # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = . # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful is your file systems # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) JAVADOC_AUTOBRIEF = NO # If the QT_AUTOBRIEF tag is set to YES then Doxygen will # interpret the first line (until the first dot) of a Qt-style # comment as the brief description. If set to NO, the comments # will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the DETAILS_AT_TOP tag is set to YES then Doxygen # will output the detailed description near the top, like JavaDoc. # If set to NO, the detailed description appears after the member # documentation. DETAILS_AT_TOP = NO # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce # a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 8 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = libdoc=@mainpage \ sect=

\ reimplemented= \ "deprecated=This class or method is obsolete, it is provided for compatibility only." \ obsolete=@deprecated \ ref= # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for Java. # For instance, namespaces will be presented as packages, qualified scopes # will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want to # include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. # func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = YES # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. # Doxygen will parse them like normal C++ but will assume all classes use public # instead of private inheritance when no explicit protection keyword is present. SIP_SUPPORT = NO # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct (or union) is # documented as struct with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically # be useful for C code where the coding convention is that all structs are # typedef'ed and only the typedef is referenced never the struct's name. TYPEDEF_HIDES_STRUCT = NO #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = NO # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = NO # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be extracted # and appear in the documentation as a namespace called 'anonymous_namespace{file}', # where file will be replaced with the base name of the file that contains the anonymous # namespace. By default anonymous namespace are hidden. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = NO # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = NO # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = NO # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= NO # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or define consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and defines in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES # If the sources in your project are distributed over multiple directories # then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy # in the documentation. The default is NO. SHOW_DIRECTORIES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from the # version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = NO # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be abled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = . # This tag can be used to specify the character encoding of the source files that # doxygen parses. Internally doxygen uses the UTF-8 encoding, which is also the default # input encoding. Doxygen uses libiconv (or the iconv built into libc) for the transcoding. # See http://www.gnu.org/software/libiconv for the list of possible encodings. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx # *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 FILE_PATTERNS = *.c *.h *.cc *.hh # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. # XXX: these paths assume we are run from within ${SRCDIR}/olsr. EXCLUDE = ../config \ ../docs \ ../etc \ ../html \ ../tests \ ../utils \ ../libxorp/callback_nodebug.hh \ ../libxorp/callback_debug.hh \ ./tools # The EXCLUDE_SYMLINKS tag can be used select whether or not files or # directories that are symbolic links (a Unix filesystem feature) are excluded # from the input. EXCLUDE_SYMLINKS = YES # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = test* emulate_net.cc emulate_net.hh # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the output. # The symbol name can be a fully qualified name, a word, or if the wildcard * is used, # a substring. Examples: ANamespace, AClass, AClass::ANamespace, ANamespace::*Test EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. If FILTER_PATTERNS is specified, this tag will be # ignored. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the # filter if there is a match. The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER # is applied to all files. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. If you have enabled CALL_GRAPH or CALLER_GRAPH # then you must also enable this option. If you don't then doxygen will produce # a warning and turn it on anyway SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES (the default) # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = NO # If the REFERENCES_RELATION tag is set to YES (the default) # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will # link to the source code. Otherwise they will link to the documentstion. REFERENCES_LINK_SOURCE = NO # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = NO #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = NO # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. #HTML_HEADER = HTML_HEADER = doxy/doxyfiles-header.html # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. #HTML_FOOTER = HTML_FOOTER = doxy/doxyfiles-footer.html # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet. Note that doxygen will try to copy # the style sheet file to the HTML output directory, so don't put your own # stylesheet in the HTML output directory as well, or it will be erased! HTML_STYLESHEET = # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, # files or namespaces will be aligned in HTML using tables. If set to # NO a bullet list will be used. HTML_ALIGN_MEMBERS = YES # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compressed HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. For this to work a browser that supports # JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox # Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). HTML_DYNAMIC_SECTIONS = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # The DISABLE_INDEX tag can be used to turn on/off the condensed index at # top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. #DISABLE_INDEX = NO DISABLE_INDEX = YES # This tag can be used to set the number of enum values (range [1..20]) # that doxygen will group on one line in the generated HTML documentation. ENUM_VALUES_PER_LINE = 4 # If the GENERATE_TREEVIEW tag is set to YES, a side panel will be # generated containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, # Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are # probably better off using the HTML help feature. GENERATE_TREEVIEW = NO # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = NO # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, a4wide, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4wide # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = NO # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = NO # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load stylesheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = NO #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. This is useful # if you want to understand what is going on. On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = NO # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # in the INCLUDE_PATH (see below) will be search if a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = *.h *.hh # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all function-like macros that are alone # on a line, have an all uppercase name, and do not end with a semicolon. Such # function macros are typically used for boiler-plate code, and will confuse # the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. # Optionally an initial location of the external documentation # can be added for each tagfile. The format of a tag file without # this location is as follows: # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths or # URLs. If a location is present for each tag, the installdox tool # does not have to be run to correct the links. # Note that each tag file must have a unique name # (where the name does NOT include the path) # If a tag file is not located in the directory in which doxygen # is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option is superseded by the HAVE_DOT option below. This is only a # fallback. It is recommended to install and use dot, since it yields more # powerful graphs. CLASS_DIAGRAMS = NO # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see http://www.mcternan.me.uk/mscgen/) to # produce the chart and insert it in the documentation. The MSCGEN_PATH tag allows you to # specify the directory where the mscgen tool resides. If left empty the tool is assumed to # be found in the default search path. MSCGEN_PATH = # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = NO # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # the CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = NO # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = NO # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = NO # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = NO # If the CALL_GRAPH, SOURCE_BROWSER and HAVE_DOT tags are set to YES then doxygen will # generate a call dependency graph for every global function or class method. # Note that enabling this option will significantly increase the time of a run. # So in most cases it will be better to enable call graphs for selected # functions only using the \callgraph command. CALL_GRAPH = NO # If the CALLER_GRAPH, SOURCE_BROWSER and HAVE_DOT tags are set to YES then doxygen will # generate a caller dependency graph for every global function or class method. # Note that enabling this option will significantly increase the time of a run. # So in most cases it will be better to enable caller graphs for selected # functions only using the \callergraph command. CALLER_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = NO # If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = NO # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are png, jpg, or gif # If left blank png will be used. DOT_IMAGE_FORMAT = png # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The MAX_DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is # visualized by representing a node as a red box. Note that doxygen if the number # of direct children of the root node in a graph is already larger than # MAX_DOT_GRAPH_NOTES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 0 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, which results in a white background. # Warning: Depending on the platform used, enabling this option may lead to # badly anti-aliased labels on the edges of a graph (i.e. they become hard to # read). DOT_TRANSPARENT = YES # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES #--------------------------------------------------------------------------- # Configuration::additions related to the search engine #--------------------------------------------------------------------------- # The SEARCHENGINE tag specifies whether or not a search engine should be # used. If set to NO the values of all tags below this one will be ignored. SEARCHENGINE = NO xorp/contrib/olsr/trace.hh0000664000076400007640000000520511421137511015714 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8 sw=4: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/contrib/olsr/trace.hh,v 1.3 2008/10/02 21:56:36 bms Exp $ #ifndef __OLSR_TRACE_HH__ #define __OLSR_TRACE_HH__ /** * @short Trace control variables. */ struct Trace { /** * Construct a new Trace instance. * * Don't forget to add new variables to the all() method. */ inline Trace() : _input_errors(true), _packets(false), _mpr_selection(false), _interface_events(false), _neighbor_events(false), _spt(false), _routes(false), _import_policy(false), _export_policy(false) {} /** * Set all tracing options to @param value */ inline void all(bool value) { _input_errors = _packets = _mpr_selection = _interface_events = _neighbor_events = _spt = _routes = _import_policy = _export_policy = value; } /** * @short true if tracing is enabled for message input errors. */ bool _input_errors; /** * @short true if tracing is enabled for packet I/O. */ bool _packets; /** * @short true if tracing is enabled for MPR selection. */ bool _mpr_selection; /** * @short true if tracing is enabled for interface events. */ bool _interface_events; /** * @short true if tracing is enabled for neighbor events. */ bool _neighbor_events; /** * @short true if tracing is enabled for Shortest Path Tree operations. */ bool _spt; /** * @short true if tracing is enabled for route add/replace/delete * operations. */ bool _routes; /** * @short true if tracing is enabled for import policy filters. */ bool _import_policy; /** * @short true if tracing is enabled for export policy filters. */ bool _export_policy; }; #endif // __OLSR_TRACE_HH__ xorp/contrib/olsr/policy_varrw.cc0000664000076400007640000000642111421137511017325 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8 sw=4: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #include "olsr_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/callback.hh" #include "libxorp/ipv4.hh" #include "libxorp/ipv4net.hh" #include "libxorp/status_codes.h" #include "libxorp/service.hh" #include "libxorp/eventloop.hh" #include "olsr.hh" #include "policy_varrw.hh" OlsrVarRW::OlsrVarRW(IPv4Net& network, IPv4& nexthop, uint32_t& metric, IPv4& originator, IPv4& main_addr, uint32_t vtype, PolicyTags& policytags) : _network(network), _nexthop(nexthop), _metric(metric), _originator(originator), _main_addr(main_addr), _vtype(vtype), _policytags(policytags) { } void OlsrVarRW::start_read() { initialize(_policytags); initialize(VAR_NETWORK, _ef.create(ElemIPv4Net::id, cstring(_network))); initialize(VAR_NEXTHOP, _ef.create(ElemIPv4NextHop::id, cstring(_nexthop))); initialize(VAR_METRIC, _ef.create(ElemU32::id, c_format("%u", _metric).c_str())); initialize(VAR_ORIGINATOR, _ef.create(ElemIPv4::id, cstring(_originator))); initialize(VAR_MAINADDR, _ef.create(ElemIPv4::id, cstring(_main_addr))); initialize(VAR_VTYPE, _ef.create(ElemU32::id, c_format("%u", _vtype).c_str())); } Element* OlsrVarRW::single_read(const Id& /*id*/) { XLOG_UNREACHABLE(); return 0; } void OlsrVarRW::single_write(const Id& id, const Element& e) { switch(id) { case VAR_NETWORK: { const ElemIPv4Net* eip = dynamic_cast(&e); XLOG_ASSERT(eip != NULL); _network = IPv4Net(eip->val()); } break; case VAR_NEXTHOP: { const ElemIPv4NextHop* eip = dynamic_cast(&e); XLOG_ASSERT(eip != NULL); _nexthop = IPv4(eip->val()); } break; case VAR_METRIC: { const ElemU32& u32 = dynamic_cast(e); _metric = u32.val(); } break; #if 1 case VAR_VTYPE: { const ElemU32& u32 = dynamic_cast(e); _vtype = u32.val(); } break; #endif case VAR_ORIGINATOR: { const ElemIPv4* eip = dynamic_cast(&e); XLOG_ASSERT(eip != NULL); _originator = IPv4(eip->val()); } break; case VAR_MAINADDR: { const ElemIPv4* eip = dynamic_cast(&e); XLOG_ASSERT(eip != NULL); _main_addr = IPv4(eip->val()); } break; default: XLOG_WARNING("Unexpected Id %d %s", id, cstring(e)); } } xorp/contrib/olsr/neighborhood.hh0000664000076400007640000011755211540225522017300 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8 sw=4: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/contrib/olsr/neighborhood.hh,v 1.3 2008/10/02 21:56:35 bms Exp $ #ifndef __OLSR_NEIGHBORHOOD_HH__ #define __OLSR_NEIGHBORHOOD_HH__ class Olsr; class LogicalLink; class Neighbor; class TwoHopNeighbor; class TwoHopLink; class HelloMessage; class TopologyManager; class RouteManager; class Neighborhood; /** * @short Orders a sequence of Neighbor* in descending order of * MPR candidacy. * * Model of StrictWeakOrdering. */ struct CandMprOrderPred { /** * Compare two Neighbors based on their MPR candidacy. * It is assumed both pointers are valid for the scope of the functor. * * Collation order: willingness, reachability, degree. * * @return true if lhs comes before rhs. */ bool operator()(const Neighbor* lhs, const Neighbor* rhs) const; }; /** * @short Orders a sequence of OlsrTypes::LogicalLinkID in descending * order of link preference. * * Model of StrictWeakOrdering. */ struct LinkOrderPred { Neighborhood* _nh; inline LinkOrderPred(Neighborhood* nh) : _nh(nh) {} /** * Determine if the left-hand link comes before the the right-hand link. * * Collation order: symmetric, most recently updated, highest ID. * * TODO: This is a candidate for moving into class Link. * TODO: ETX metrics, collation order will be: * Anything !is_pending is above anything is_pending * Then order in ascending near_etx(). * Then order in ascending far_etx(). * A change in the best link means a change in routes, unless we * dampen flap somehow. * * @return true if link lhs is better than link rhs. */ bool operator()(const OlsrTypes::LogicalLinkID lhid, const OlsrTypes::LogicalLinkID rhid); }; /** * @short Orders a sequence of OlsrTypes::TwoHopLinkID in descending * order of link preference. * * Model of StrictWeakOrdering. */ struct TwoHopLinkOrderPred { Neighborhood* _nh; inline TwoHopLinkOrderPred(Neighborhood* nh) : _nh(nh) {} /** * Determine if a two-hop link is better than another two-hop link. * * Collation order: most recently updated, highest ID. * TODO: Use ETX measurements. * * @return true if link lhid is better than link rhid. */ bool operator()(const OlsrTypes::TwoHopLinkID lhid, const OlsrTypes::TwoHopLinkID rhid); }; /** * @short Representation of OLSR node's one-hop and two-hop neighborhood. * * Responsible for originating TC broadcasts when the node is selected * as an MPR by other nodes. */ class Neighborhood { public: Neighborhood(Olsr& olsr, EventLoop& eventloop, FaceManager& fm); ~Neighborhood(); // // Top level associations. // TopologyManager* topology_manager() { return _tm; } void set_topology_manager(TopologyManager* tm) { _tm = tm; } RouteManager* route_manager() { return _rm; } void set_route_manager(RouteManager* rm) { _rm = rm; } /** * Given an empty HELLO message, fill it out as appropriate for * its interface ID property. * If the message is not empty the results are undefined. * * TODO: Support ETX measurements. * * @param hello the HELLO message to be filled out, which MUST * contain the FaceID of where it is to be sent. * @return the number of link addresses, including neighbors * which are not local to this link, filled out in hello. */ size_t populate_hello(HelloMessage* hello); /** * Push the topology to the RouteManager for route computation. * * When we do incremental SPT this can partially go away. The SPT * can contain only one edge between each node -- the nested methods * here deal with this. */ void push_topology(); // // RFC 3626 Section 18.2: Protocol control variables. // /** * Attempt to set the redundancy of links advertised in TC broadcasts. * * @param type the new redundancy level to set. * @return true if the TC_REDUNDANCY was set to type. */ bool set_tc_redundancy(const OlsrTypes::TcRedundancyType type); /** * @return the value of the TC_REDUNDANCY protocol variable. */ OlsrTypes::TcRedundancyType get_tc_redundancy() const { return _tc_redundancy; } /** * @return the value of the MPR_COVERAGE protocol variable. */ inline uint32_t mpr_coverage() const { return _mpr_coverage; } /** * Attempt to set the required level of MPR coverage. * * If successful, and OLSR is running on any configured interface, * the MPR set will be recalculated. * * @param coverage the new value of the MPR_COVERAGE variable. * @return true if the MPR_COVERAGE was set to coverage. */ bool set_mpr_coverage(const uint32_t coverage); /** * @return the value of the REFRESH_INTERVAL protocol variable. */ TimeVal get_refresh_interval() const { return _refresh_interval; } /** * Set the value of the REFRESH_INTERVAL protocol variable. * * @param interval the new value of REFRESH_INTERVAL.. */ void set_refresh_interval(const TimeVal& interval) { _refresh_interval = interval; } /** * @return the value of the TC_INTERVAL protocol variable. */ TimeVal get_tc_interval() { return _tc_interval; } /** * Set the interval between TC broadcasts. * * The timer will only be restarted if previously scheduled. If the * period of the TC broadcasts is changed, a TC broadcast is scheduled * to take place immediately. * * @param interval the new value of TC_INTERVAL. */ void set_tc_interval(const TimeVal& interval); /** * Set the WILLINGNESS protocol variable. * * @param willingness the new value of the WILLINGNESS protocol variable. */ void set_willingness(const OlsrTypes::WillType willingness); /** * @return the current value of the WILLINGNESS protocol variable. */ OlsrTypes::WillType willingness() const { return _willingness; } /** * @return the current value of the NEIGHB_HOLD_TIME protocol variable. */ TimeVal get_neighbor_hold_time() { return _refresh_interval * 3; } /** * @return the current value of the TOP_HOLD_TIME protocol variable. */ TimeVal get_topology_hold_time() { return _tc_interval * 3; } // // Face interaction. // /** * Add an interface to the neighborhood. * Called whenever an instance of Face is configured administratively up. * * @param faceid The ID of the interface which has been configured * administratively up. */ void add_face(const OlsrTypes::FaceID faceid); /** * Delete an interface from the neighborhood. * Called whenever an instance of Face is configured administratively down. * * @param faceid The ID of the interface which has been configured * administratively down. */ void delete_face(const OlsrTypes::FaceID faceid); // // Link database. // /** * Update a LogicalLink. * * The link will be created if it does not exist. * Links are specified by their remote and local interface addresses. * * @param faceid the ID of the interface where this link appears. * @param remote_addr the protocol address of the remote interface * at the far end of the link. * @param local_addr the protocol address of the local interface * at the near end of the link. * @param vtime the validity time of the new link. * @param is_created will be set to true if the link did not * previously exist and was created by this method. * @return the ID of the link tuple. * @throw BadLogicalLink if the link could not be updated. */ OlsrTypes::LogicalLinkID update_link( const OlsrTypes::FaceID faceid, const IPv4& remote_addr, const IPv4& local_addr, const TimeVal& vtime, bool& is_created) throw(BadLogicalLink); /** * Add a link to the local link database. * * @param vtime the validity time of the new link. * @param remote_addr the protocol address of the remote interface * at the far end of the link. * @param local_addr the protocol address of the local interface * at the near end of the link. * @return the ID of the new link. * @throw BadLogicalLink if the link could not be created. */ OlsrTypes::LogicalLinkID add_link( const TimeVal& vtime, const IPv4& remote_addr, const IPv4& local_addr) throw(BadLogicalLink); /** * Delete the link tuple specified by the given link id. * * Note: The associated Neighbor may be deleted. State change is * also propagated to the FaceManager. * * @param linkid the identifier of the link tuple. * @return true if the link was deleted. */ bool delete_link(OlsrTypes::LogicalLinkID linkid); /** * Clear the neighborhood one-hop links. * * The neighbors and two-hop neighbors associated with these links * will be removed. Assertions for this are in the destructor. */ void clear_links(); /** * Look up a LogicalLink's pointer given its LogicalLinkID. * * @param linkid the ID of the link to look up. * @return a pointer to the logical link. * @throw BadLogicalLink if the link could not be found. */ const LogicalLink* get_logical_link(const OlsrTypes::LogicalLinkID linkid) throw(BadLogicalLink); /** * Fill out a list of all LogicalLinkIDs in the database. * * @param l1_list the list to fill out. */ void get_logical_link_list(list& l1_list) const; /** * Look up a LogicalLink's ID given its interface addresses. * * @param remote_addr the protocol address of the remote interface * at the far end of the link. * @param local_addr the protocol address of the local interface * at the near end of the link. * @return the ID of the LogicalLink. * @throw BadLogicalLink if the link could not be found. */ OlsrTypes::LogicalLinkID get_linkid(const IPv4& remote_addr, const IPv4& local_addr) throw(BadLogicalLink); //get_link_list // // Neighbor database. // /** * Update or create a Neighbor in the one-hop neighbor database. * * This method has the weak exception guarantee that BadNeighbor will * only be thrown before it is associated with LogicalLink. * * @param main_addr The main protocol address of the neighbor. * @param linkid The ID of the initially created link to the neighbor. * @param is_new_link true if the link the neighbor is being created * with has just been instantiated. * @param will The neighbor's advertised willingness-to-forward. * @param is_mpr_selector true if the neighbor selects us as an MPR. * @param mprs_expiry_time the expiry time for the MPR selector tuple. * @param is_created set to true if a new neighbor entry was created. * @return the ID of the updated or created neighbor tuple. * @throw BadNeighbor if the neighbor entry could not be updated. */ OlsrTypes::NeighborID update_neighbor(const IPv4& main_addr, const OlsrTypes::LogicalLinkID linkid, const bool is_new_link, const OlsrTypes::WillType will, const bool is_mpr_selector, const TimeVal& mprs_expiry_time, bool& is_created) throw(BadNeighbor); /** * Add a new Neighbor to the one-hop neighbor database. * A Neighbor must be created with at least one LogicalLink. * * @param main_addr The main address of the new neighbor. * @param linkid The ID of the Neighbor's first link. * @return the ID of the newly created neighbor. * @throw BadNeighbor if the neighbor entry could not be created. */ OlsrTypes::NeighborID add_neighbor(const IPv4& main_addr, OlsrTypes::LogicalLinkID linkid) throw(BadNeighbor); /** * Delete a neighbor from the neighbor database. * Called when the last link to the neighbor has been deleted. * * @param nid The ID of this neighbor. * @return true if the neighbor was removed, false if it cannot * be found. */ bool delete_neighbor(const OlsrTypes::NeighborID nid); /** * Find a Neighbor's pointer, given its NeighborID. * * @param nid the ID of the Neighbor. * @return the pointer to the Neighbor instance. * @throw BadNeighbor if the Neighbor does not exist. */ const Neighbor* get_neighbor(const OlsrTypes::NeighborID nid) throw(BadNeighbor); /** * Fill out a list of all NeighborIDs in the database. * * @param n1_list the list to fill out. */ void get_neighbor_list(list& n1_list) const; /** * Find a neighbor ID, given its main address. * * @param main_addr the main protocol address of the OLSR node. * @return the neighbor ID. * @throw BadNeighbor if the neighbor is not found. */ OlsrTypes::NeighborID get_neighborid_by_main_addr(const IPv4& main_addr) throw(BadNeighbor); /** * Find a neighbor ID, given the address of one of its interfaces. * * @param remote_addr the address of one of the interfaces of * an OLSR node. * @return the neighbor ID. * @throw BadNeighbor if the neighbor is not found. */ OlsrTypes::NeighborID get_neighborid_by_remote_addr(const IPv4& remote_addr) throw(BadNeighbor); /** * Check if a remote address belongs to a symmetric one-hop neighbor. * * Referenced from: * Section 5.4 point 1 MID Message Processing. * Section 9.5 point 1 TC Message Processing. * Section 12.5 point 1 HNA Message Processing. * * @param addr the interface address of a neighbor. * @return true if addr is an interface address of a symmetric one-hop * neighbor. */ bool is_sym_neighbor_addr(const IPv4& addr); // // Advertised neighbor set. // /** * @return true if the Neighbor n would be advertised in a TC * broadcast, given the current TC_REDUNDANCY. */ inline bool is_tc_advertised_neighbor(Neighbor* n) { if ((_tc_redundancy == OlsrTypes::TCR_ALL) || (_tc_redundancy == OlsrTypes::TCR_MPRS_INOUT && n->is_mpr()) || n->is_mpr_selector()) { return true; } return false; } /** * Schedule an update of the Advertised Neighbor Set. * * @param is_deleted true if the update is being scheduled in * response to the deletion of a Neighbor. */ void schedule_ans_update(const bool is_deleted); // // Two hop link database. // /** * Update the link state information for a two-hop neighbor. * * This method may create a TwoHopNeighbor and/or a TwoHopLink if they * do not already exist. * * @param node_info The address information for the two-hop neighbor; * contains ETX measurements, if applicable. * @param nexthop The main address of the immediate neighbor which * advertises this TwoHopLink. * @param faceid The interface where the advertisement was heard. * @param vtime The time for which the TwoHopLink remains valid. * @return the ID of the two-hop neighbor. */ OlsrTypes::TwoHopLinkID update_twohop_link(const LinkAddrInfo& node_info, Neighbor& nexthop, const OlsrTypes::FaceID faceid, const TimeVal& vtime) throw(BadTwoHopLink); /** * Add a TwoHopLink to the Neighborhood. * * The constructor signature forces us to associate the near * end of the TwoHopLink with a Neighbor. It MUST be associated * with a TwoHopNeighbor after construction to be considered valid. * * @param nexthop The strict one-hop neighbor at the near end of * the TwoHopLink being created. * @param remote_addr The two-hop neighbor at the far end of * the TwoHopLink being created. * @param vtime The time for which the TwoHopLink remains valid. * @return the ID of the newly created TwoHopLink. * @throw BadTwoHopLink if the TwoHopLink could not be created. */ OlsrTypes::TwoHopLinkID add_twohop_link(Neighbor* nexthop, const IPv4& remote_addr, const TimeVal& vtime) throw(BadTwoHopLink); /** * Delete the TwoHopLink to a two-hop neighbor. * * The deletion is propagated to the Neighbor and TwoHopNeighbor * instances on the near and far ends of the link respectively. * * @param tlid ID of the two-hop link which is to be deleted. * @return true if the link thus deleted was the last link to the * two-hop node it is used to reach, otherwise false. */ bool delete_twohop_link(OlsrTypes::TwoHopNodeID tlid); /** * Delete the TwoHopLink to a two-hop neighbor. * * The link is identified by the near and far end main addresses. * * @param nexthop_addr The address of the Neighbor used to reach the * two-hop neighbor given by twohop_addr. * @param twohop_addr The two-hop neighbor whose link has been lost. * @return true if this was the last link to the two-hop neighbor * and it was deleted as a result. */ bool delete_twohop_link_by_addrs(const IPv4& nexthop_addr, const IPv4& twohop_addr); /** * Given the ID of a TwoHopLink, return its instance pointer. * * @param tlid the ID of a TwoHopLink. * @return the pointer to the TwoHopLink instance. * @throw BadTwoHopLink if tlid does not exist. */ TwoHopLink* get_twohop_link(const OlsrTypes::TwoHopLinkID tlid) throw(BadTwoHopLink); /** * Fill out a list of all TwoHopLinkIDs in the database. * * @param l2_list the list to fill out. */ void get_twohop_link_list(list& l2_list) const; // // Two hop node database. // /** * Update a two-hop neighbor. * * If the TwoHopNeighbor does not exist it will be created. A valid * two-hop link must be provided; if the link is also newly created, * this method will create the back-reference. * * @param main_addr the main address of the two-hop neighbor. * @param tlid the ID of the two-hop link with which the two-hop * neighbor is initially associated. * @param is_new_l2 true if tlid refers to a newly created link. * @param is_n2_created set to true if a new TwoHopNeighbor was created. * @return the ID of the two-hop neighbor. * @throw BadTwoHopNode if the two-hop neighbor could not be updated. */ OlsrTypes::TwoHopNodeID update_twohop_node( const IPv4& main_addr, const OlsrTypes::TwoHopLinkID tlid, const bool is_new_l2, bool& is_n2_created) throw(BadTwoHopNode); /** * Add a two-hop neighbor to the two-hop neighborhood. * * @param main_addr the main address of the two-hop neighbor to create. * @param tlid the ID of the initial link to this two-hop neighbor. * @return the ID of the newly created two-hop neighbor. * @throw BadTwoHopNode if the two-hop neighbor could not be created. */ OlsrTypes::TwoHopNodeID add_twohop_node( const IPv4& main_addr, const OlsrTypes::TwoHopLinkID tlid) throw(BadTwoHopNode); /** * Delete an entry in the two-hop neighbor table. * * @param tnid the ID of a two-hop neighbor. * @return true if the neighbor was deleted, otherwise false. */ bool delete_twohop_node(OlsrTypes::TwoHopNodeID tnid); /** * Look up a two-hop neighbor by main address. * * @param main_addr the main address of a two-hop neighbor. * @return the ID of the two-hop neighbor. * @throw BadTwoHopNode if the two-hop neighbor could not be found. */ OlsrTypes::TwoHopNodeID get_twohop_nodeid_by_main_addr( const IPv4& main_addr) throw(BadTwoHopNode); /** * Given the ID of a TwoHopNeighbor, return its instance pointer. * * @param tnid the ID of a TwoHopNeighbor. * @return the pointer to the TwoHopNeighbor instance. * @throw BadTwoHopNode if tnid does not exist. */ const TwoHopNeighbor* get_twohop_neighbor( const OlsrTypes::TwoHopNodeID tnid) const throw(BadTwoHopNode); /** * Fill out a list of all TwoHopNodeIDs in the database. * * @param n2_list the list to fill out. */ void get_twohop_neighbor_list(list& n2_list) const; // // MPR selector set methods. // /** * @return true if this node has been selected as an MPR by any * one-hop neighbor, that is, the MPR selector set is non-empty. */ inline bool is_mpr() const { return !_mpr_selector_set.empty(); } /** * Update a Neighbor's status in the MPR selector set, * possibly adding it. * * If our node now has a non-empty MPR selector set, it * must now originate TC advertisements. * * @param nid the ID of the Neighbor to mark as an MPR selector. * @param vtime the duration of the MPR selector set membership. */ void update_mpr_selector(const OlsrTypes::NeighborID nid, const TimeVal& vtime); /** * Remove a neighbor from the MPR selector set by its ID. * * If the node no longer has any MPR selectors, it is no longer * considered an MPR, and it must continue to originate empty TCs * for TOP_HOLD_TIME, after which it shall stop. * * @param nid the ID of the Neighbor to add as an MPR selector. */ void delete_mpr_selector(const OlsrTypes::NeighborID nid); /** * Check if an address belongs to a one-hop neighbor which is * also an MPR selector. * * Referenced from: * Section 3.4.1 Default Forwarding Algorithm. * * @param remote_addr the IPv4 interface address to look up in * the neighbor database. * @return true if addr is an interface address of a symmetric * one-hop neighbor which selects this node as an MPR. */ bool is_mpr_selector_addr(const IPv4& remote_addr); /** * @return the MPR selector set. * * For use by simulation framework. */ set mpr_selector_set() const { return _mpr_selector_set; } // // MPR set methods. // /** * Trigger a recount of the MPR set. * * Invoked whenever there is a change of state which would cause the * MPR set to change. Calculating the MPR set is an expensive * operation, so it is scheduled as a one-off task in the event loop. */ inline void schedule_mpr_recount() { _mpr_recount_task.reschedule(); } /** * Add a neighbor to the set of MPR candidates for the * interfaces from which it is reachable. * * @param nid the ID of the neighbor to add. */ void add_cand_mpr(const OlsrTypes::NeighborID nid); /** * Remove a neighbor from the set of MPR candidates for all interfaces. * * @param nid the ID of the neighbor to remove. */ void withdraw_cand_mpr(const OlsrTypes::NeighborID nid); /** * Callback method to: recount the MPR set for all configured * OLSR interfaces. */ void recount_mpr_set(); /** * Clear all existing MPR state for Neighbors. */ void reset_onehop_mpr_state(); /** * Clear all existing MPR state for TwoHopNeighbors. * Compute number of now uncovered reachable nodes at radius=2. * * @return The number of reachable, strict two-hop neighbors * to be considered by MPR selection. */ size_t reset_twohop_mpr_state(ostringstream& dbg); /** * Compute one-hop neighbor reachability and update it in the * Neighbor to avoid repetitively computing it on every MPR recount. * * Coverage must be valid. If this method is called outside of an MPR * recount results are undefined. * * Reachability is defined as: the number of uncovered N2 nodes which * have edges to this N. We do this outside of Neighbor for code brevity. * * @param n Pointer to a Neighbor, which is normally an MPR candidate. */ void update_onehop_reachability(Neighbor* n); /** * Compute two-hop neighbor reachability. * * It will be updated it in the TwoHopNeighbor to avoid computing it * more than once during an MPR recount. * If an N2 is reachable via an N with WILL_ALWAYS this takes precedence. * * TODO: WHEN ETX IS IMPLEMENTED, A LINK WITH NO 'GOOD' LINKS * MUST BE CONSIDERED UNREACHABLE. * * Two-hop reachability is defined as: the number of MPR candidates with * edges linking them to N2. * Note: This is NOT THE SAME as a one-hop neighbor's reachability. * * We do this outside of TwoHopNeighbor to avoid playing too many tedious * C++ accessor games. MPR candidacy of linked neighbors must be valid. * If this method is called outside of an MPR recount, its results * are undefined. * * @param tn Pointer to a TwoHopNeighbor. */ void update_twohop_reachability(TwoHopNeighbor* tn); /** * Consider persistent MPR candidates for MPR selection. * * 8.3.1, 1: Start with an MPR set made of all members of N with * willingness equal to WILL_ALWAYS. * * This introduces the funky situation that a neighbor may be selected * as an MPR even if it has no two-hop links. Such neighbors are always * chosen as MPRs before other neighbors. * * @return The number of two-hop neighbors which have been covered * by considering the persistent MPR candidates. */ size_t consider_persistent_cand_mprs(ostringstream& dbg); /** * Consider MPR coverage of poorly covered two-hop neighbors. * * 8.3.1, 3: Ensure that for all uncovered strict N2 reachable * *only via 1 edge*, their neighbor N is selected as an MPR. * * TODO: Use ETX measurements. * * @return The number of two-hop neighbors which have been covered * by considering the persistent MPR candidates. */ size_t consider_poorly_covered_twohops(ostringstream& dbg); /** * Consider remaining MPR candidates for MPR selection. * * Candidates are considered in descending order of willingness, * reachability and degree. * * Note: As we only use the result of the insertion sort in this * scope, this block is a candidate for a Boost++ filter_iterator. * However a filter iterator might keep scanning the N space and * blowing the l2/l3 cache. * * @param n2_count The total number of N2 which must be reachable by * the MPR set, used as a recursion upper bound. * @param covered_n2_count A reference to the cumulative number of * N2 which are reachable by the MPR set, and * which this method will update. */ void consider_remaining_cand_mprs(const size_t n2_count, size_t& covered_n2_count, ostringstream& oss); /** * Mark all N1 neighbors was MPRs. * * Considers all reachable one-hop neighbors with willingness of * other than WILL_NEVER as MPRs. * This feature is typically used as a workaround in dense OLSR * topologies which are not sufficiently partitioned. * * @param final_mpr_set will have the result set of this method * merged with it. * @return the number of neighbors which have been selected as MPRs. */ size_t mark_all_n1_as_mprs(set& final_mpr_set); /** * Minimize the MPR set, based on the MPR coverage parameter. * * Produces the final MPR set in a std::set container, * for debugging purposes. * Section 8.3.1, 4. * * @param final_mpr_set reference to an empty MPR set that shall * contain the resultant MPR set after it * has been minimized. * @return the number of elements removed from the MPR set, as it * appears in the one-hop neighbor database. * @throw BadTwoHopCoverage if the MPR minimization algorithm * detects that a two-hop node is now uncovered by any MPRs. */ size_t minimize_mpr_set(set& final_mpr_set) throw(BadTwoHopCoverage); /** * Determine if an MPR is essential to covering the entire two-hop * neighborhood. * * @param n the Neighbor to evaluate. * @return true if any of N's links cover a poorly covered strict * TwoHopNeighbor. */ bool is_essential_mpr(const Neighbor* n); /** * @return the MPR selector set. * * For use by simulation framework. */ set mpr_set() const { return _mpr_set; } // // Event handlers. // /** * Callback method to: service a LogicalLink's SYM timer. * * Whilst the SYM interval timer is pending, the link is considered * symmetric. When the timer fires, the link is considered ASYM. * If both the ASYM and SYM timers fire in the same event loop quantum, * the ASYM timer is considered to have priority. * * @param linkid the ID of the link whose SYM timer has fired. */ void event_link_sym_timer(OlsrTypes::LogicalLinkID linkid); /** * Callback method to: service a LogicalLink's ASYM timer. * * Whilst the ASYM interval timer is pending, the link is considered * asymmetric. When the timer fires, the link is considered LOST. * * @param linkid the ID of the link whose ASYM timer has fired. */ void event_link_asym_timer(OlsrTypes::LogicalLinkID linkid); /** * Callback method to: service a LogicalLink's LOST timer. * * Section 13: Link Layer Notification. * * TODO: Not yet implemented, as it relies on link layer support from * the host platform which does not yet exist. In practice this * should not pose a problem, as 802.11 IBSS disassociation is often * unreliable anyway. * * @param linkid the ID of the link whose LOST timer has fired. */ void event_link_lost_timer(OlsrTypes::LogicalLinkID linkid); /** * Callback method to: service a LogicalLink's DEAD timer. * * @param linkid the ID of the link whose DEAD timer has fired. */ void event_link_dead_timer(OlsrTypes::LogicalLinkID linkid); /** * Callback method to: service a TwoHopLink's DEAD timer. * * @param tlid the ID of the two-hop link whose DEAD timer has fired. */ void event_twohop_link_dead_timer(const OlsrTypes::TwoHopLinkID tlid); /** * Callback method to: service an MPR selector's EXPIRY timer. * * @param nid the ID of the Neighbor whose MPR selector tuple * has expired. */ void event_mpr_selector_expired(const OlsrTypes::NeighborID nid); /** * Callback method to: process an incoming HELLO message. * Section 7.1.1: HELLO Message Processing. * * @param msg Pointer to a message which is derived from HelloMessage. * @param remote_addr The source address of the packet containing msg. * @param local_addr The address of the interface where this packet was * received. * @return true if this function consumed msg. */ bool event_receive_hello(Message* msg, const IPv4& remote_addr, const IPv4& local_addr); /** * Callback method to: service the TC transmission timer. * * Section 9.2: Advertised Neighbor Set. * * Flood a TC message to the rest of the OLSR domain * which contains our Advertised Neighbor Set (ANS). * This method should only be called if the TC timer is running * or finishing. The finishing state is entered when a node has * stopped being an MPR or when the ANS set becomes empty. * * TODO: Account for ETX metrics in selecting advertised neighbors. * TODO: Fish-eye TC emission optimization; transmit only. * * @return true if the callback should be rescheduled, otherwise false. */ bool event_send_tc(); // // Timer methods. // /** * Stop all timers in Neighborhood. */ void stop_all_timers(); /** * Start the TC transmission interval timer. */ void start_tc_timer(); /** * Stop the TC transmission interval timer. */ void stop_tc_timer(); /** * Restart the TC transmission interval timer. */ void restart_tc_timer(); protected: // // One-hop link selection. // /** * Find the best link to a neighbor N. * * @param n Pointer to a neighbor N. * @return Pointer to a LogicalLink l which is the best link to N. * @throw BadLinkCoverage if none of the links are reachable or * is of suitable ETX criteria. */ const LogicalLink* find_best_link(const Neighbor* n) throw(BadLinkCoverage); /** * Push a single Neighbor, and its links, to the RouteManager. * * The SPT structure can only hold a single edge between each * neighbor at the moment. We also need to select each node by * its ETX. In the absence of ETX measurements, we select * the most recently heard symmetric link which is up. * * @param n the neighbor to push. * @return true if the neighbor was pushed, false if it was not. */ bool push_neighbor(const Neighbor* n); // // Two-hop link selection. // /** * Find the best link to a two-hop neighbor N2. * * @param n2 Pointer to a neighbor N2. * @return Pointer to a TwoHopLink l2 which is the best link to N2. * @throw BadTwoHopCoverage if none of the links are reachable, * or of suitable ETX criteria. */ const TwoHopLink* find_best_twohop_link(const TwoHopNeighbor* n2) throw(BadTwoHopCoverage); /** * Push a single TwoHopNeigbor, and its links, to the RouteManager. * Here, we select the best link to this TwoHopNeighbor. * * @param n2 the two-hop neighbor to push. * @return true if the two-hop neighbor was pushed, false if it was not. */ bool push_twohop_neighbor(TwoHopNeighbor* n2); /** * Transition to the 'finish' state for the TC timer. */ void finish_tc_timer(); /** * Schedule the TC timer as soon as possible. */ void reschedule_immediate_tc_timer(); /** * Reschedule the TC timer if the TC interval changed. */ void reschedule_tc_timer(); enum TcTimerState { TC_STOPPED = 0, TC_RUNNING = 1, TC_FINISHING = 2 }; private: Olsr& _olsr; EventLoop& _eventloop; FaceManager& _fm; TopologyManager* _tm; RouteManager* _rm; LinkOrderPred _link_order_pred; TwoHopLinkOrderPred _twohop_link_order_pred; OlsrTypes::LogicalLinkID _next_linkid; OlsrTypes::NeighborID _next_neighborid; OlsrTypes::TwoHopLinkID _next_twohop_linkid; OlsrTypes::TwoHopNodeID _next_twohop_nodeid; /** * The count of administratively up and running OLSR interfaces * in this OLSR routing process. */ uint32_t _enabled_face_count; /** * Willingness of this node to forward packets for other nodes. */ OlsrTypes::WillType _willingness; /** * The REFRESH_INTERVAL protocol control variable. * RFC 3626 Section 18.2. */ TimeVal _refresh_interval; /* * MPR sets. */ /** * true if the MPR algorithm is enabled, false if all one-hop * neighbors willing to forward should always be considered as MPRs. */ bool _mpr_computation_enabled; /** * Section 16.1: MPR_COVERAGE Parameter. */ uint32_t _mpr_coverage; /** * A task which may be scheduled to recompute the global MPR set. */ XorpTask _mpr_recount_task; /** * The neighbors which select this node as an MPR. */ set _mpr_selector_set; /** * The global set of neighbors which this node has selected as MPRs. */ set _mpr_set; /* * Topology Control */ /** * The TC_INTERVAL protocol control variable. * RFC 3626 Section 18.2. */ TimeVal _tc_interval; /** * The TC_REDUNDANCY protocol control variable. * Section 15. */ OlsrTypes::TcRedundancyType _tc_redundancy; /** * The TC broadcast timer. */ XorpTimer _tc_timer; /** * The current state of the TC timer: STOPPED, RUNNING or FINISHING. */ TcTimerState _tc_timer_state; /** * The number of ticks remaining until the TC timer transitions from * FINISHING state to STOPPED state. */ uint32_t _tc_timer_ticks_remaining; /** * The current advertised neighbor sequence number. */ uint16_t _tc_current_ansn; /** * The previous advertised neighbor count. */ uint16_t _tc_previous_ans_count; /** * true if TC messages should be flooded immediately when an * MPR selector is deleted. */ bool _loss_triggered_tc_enabled; /** * true if TC messages should be flooded immediately when any * change in the ANSN is detected. */ bool _change_triggered_tc_enabled; /* * Link state databases. */ /** * This node's links to neighbors. * RFC 3626 Section 4.2.1 Local Link Information Base */ map _links; /** * A map providing lookup of Link ID based on * remote and local protocol addresses in that order. * Used for processing incoming HELLOs. */ map, OlsrTypes::LogicalLinkID> _link_addr; /** * This node's neighbors. * RFC 3626 Section 4.3 Neighborhood Information Base */ map _neighbors; /** * A map providing lookup of Neighbor ID based on * the node's main address. */ map _neighbor_addr; /** * The two-hop link table. * * This is not part of the RFC however we break the implementation * of two-hop neighbors into links and nodes to facilitate faster * convergence of MPR sets when links change in the vicinity. */ map _twohop_links; /** * A map providing lookup of two-hop link ID based on * the main addresses of the one-hop and two-hop neighbors, in * that order. */ map, OlsrTypes::TwoHopLinkID> _twohop_link_addrs; /** * The two-hop neighbor table. */ map _twohop_nodes; /** * A map providing lookup of two-hop neighbor ID based on * its main protocol address and the next-hop used to reach it. */ map _twohop_node_addrs; }; #endif // __OLSR_NEIGHBORHOOD_HH__ xorp/contrib/olsr/doxy/0000775000076400007640000000000011421137511015256 5ustar greearbgreearbxorp/contrib/olsr/doxy/doxyfiles-footer.html0000664000076400007640000000014611421137511021447 0ustar greearbgreearb


Generated at $datetime by Doxygen $doxygenversion
xorp/contrib/olsr/doxy/doxyfiles-header.html0000664000076400007640000000074211421137511021403 0ustar greearbgreearb XORP/OLSR Source Documentation
XORP Logo xorp/contrib/olsr/external.cc0000664000076400007640000004277611540224221016441 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8 sw=4: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "olsr_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/callback.hh" #include "libxorp/ipv4.hh" #include "libxorp/ipv4net.hh" #include "libxorp/status_codes.h" #include "libxorp/service.hh" #include "libxorp/eventloop.hh" // #define DEBUG_LOGGING // #define DEBUG_FUNCTION_NAME #include "olsr.hh" #include "external.hh" // NOTE: The OlsrTypes::ExternalID identifier space is currently shared // between learned routes and originated routes. // TODO: External route metrics. // TODO: Use metric information for collation in ExternalRouteOrderPred. bool ExternalRouteOrderPred::operator()(const OlsrTypes::ExternalID lhid, const OlsrTypes::ExternalID rhid) { try { // TODO Propagate exceptions if IDs cannot be retrieved, // rather than just catching them. const ExternalRoute* lhp = _ers.get_hna_route_in_by_id(lhid); const ExternalRoute* rhp = _ers.get_hna_route_in_by_id(rhid); // Invariant: Both ExternalRoutes SHOULD have the same origination // status, i.e. learned routes should only be compared with other // learned routes; originated routes with originated routes. XLOG_ASSERT(lhp->is_self_originated() == rhp->is_self_originated()); if (lhp->dest() == rhp->dest()) { // Invariant: Self originated routes should have a distance of 0. // Learned routes should have a non-zero distance. XLOG_ASSERT(lhp->is_self_originated() ? lhp->distance() == 0 && rhp->distance() == 0 : lhp->distance() != 0 && rhp->distance() != 0); return lhp->distance() < rhp->distance(); } return lhp->dest() < rhp->dest(); // Collation order on IPvXNet. } catch (...) {} return false; } /* * ExternalRoutes. */ ExternalRoutes::ExternalRoutes(Olsr& olsr, EventLoop& eventloop, FaceManager& fm, Neighborhood& nh) : _olsr(olsr), _eventloop(eventloop), _fm(fm), _nh(nh), _rm(0), _routes_in_order_pred(*this), _is_early_hna_enabled(false), _next_erid(1), _hna_interval(TimeVal(OlsrTypes::DEFAULT_HNA_INTERVAL, 0)) { _fm.add_message_cb(callback(this, &ExternalRoutes::event_receive_hna)); } ExternalRoutes::~ExternalRoutes() { _fm.delete_message_cb(callback(this, &ExternalRoutes::event_receive_hna)); clear_hna_routes_in(); clear_hna_routes_out(); } /* * Protocol variables. */ void ExternalRoutes::set_hna_interval(const TimeVal& hna_interval) { if (hna_interval == _hna_interval) return; debug_msg("%s setting HNA interval to %s.\n", cstring(_fm.get_main_addr()), cstring(hna_interval)); _hna_interval = hna_interval; if (_hna_send_timer.scheduled()) { // Change period. reschedule_hna_send_timer(); // Optionally, fire one off now. //reschedule_immediate_hna_send_timer(); } } /* * HNA Routes In. */ OlsrTypes::ExternalID ExternalRoutes::update_hna_route_in(const IPv4Net& dest, const IPv4& lasthop, const uint16_t distance, const TimeVal& expiry_time, bool& is_created) throw(BadExternalRoute) { debug_msg("Dest %s Lasthop %s Distance %u ExpiryTime %s\n", cstring(dest), cstring(lasthop), XORP_UINT_CAST(distance), cstring(expiry_time)); // We perform the multimap search inline, to avoid doing it more than // once; we may need to re-insert into the multimap if the HNA // distance changes. OlsrTypes::ExternalID erid; ExternalRoute* er = 0; bool is_found = false; pair rd = _routes_in_by_dest.equal_range(dest); ExternalDestInMap::iterator ii; for (ii = rd.first; ii != rd.second; ii++) { er = _routes_in[(*ii).second]; if (er->lasthop() == lasthop) { is_found = true; break; } } if (is_found) { erid = er->id(); if (er->distance() != distance) { // If the distance changed, update it. // ii already points to the entry, so need only // erase and re-insert; the use of ExternalRouteOrderPred // will cause this to be an insertion sort. _routes_in_by_dest.erase(ii); er->set_distance(distance); _routes_in_by_dest.insert(make_pair(dest, erid)); } er->update_timer(expiry_time); } else { // Create a new HNA entry. erid = add_hna_route_in(dest, lasthop, distance, expiry_time); } is_created = !is_found; return erid; } OlsrTypes::ExternalID ExternalRoutes::add_hna_route_in(const IPv4Net& dest, const IPv4& lasthop, const uint16_t distance, const TimeVal& expiry_time) throw(BadExternalRoute) { debug_msg("Dest %s Lasthop %s Distance %u ExpiryTime %s\n", cstring(dest), cstring(lasthop), XORP_UINT_CAST(distance), cstring(expiry_time)); OlsrTypes::ExternalID erid = _next_erid++; if (_routes_in.find(erid) != _routes_in.end()) { xorp_throw(BadExternalRoute, c_format("Mapping for ExternalID %u already exists", XORP_UINT_CAST(erid))); } _routes_in[erid] = new ExternalRoute(*this, _eventloop, erid, dest, lasthop, distance, expiry_time); _routes_in_by_dest.insert(make_pair(dest, erid)); // Route update will be scheduled further up in the call graph. return erid; } bool ExternalRoutes::delete_hna_route_in(OlsrTypes::ExternalID erid) { debug_msg("ExternalID %u\n", XORP_UINT_CAST(erid)); ExternalRouteMap::iterator ii = _routes_in.find(erid); if (ii == _routes_in.end()) return false; ExternalRoute* er = (*ii).second; // Prune from destination map. // Note: does not maintain invariant on ID in _routes_in_by_dest. pair rd = _routes_in_by_dest.equal_range(er->dest()); ExternalDestInMap::iterator jj; for (jj = rd.first; jj != rd.second; jj++) { if ((*jj).second == erid) { _routes_in_by_dest.erase(jj); // jj now invalidated; break. break; } } // Ensure routes are withdrawn. if (_rm) _rm->schedule_external_route_update(); _routes_in.erase(ii); delete er; return true; } void ExternalRoutes::clear_hna_routes_in() { _routes_in_by_dest.clear(); ExternalRouteMap::iterator ii, jj; for (ii = _routes_in.begin(); ii != _routes_in.end(); ) { jj = ii++; delete (*jj).second; _routes_in.erase(jj); } // Ensure routes are withdrawn. // Check that _rm is non-0 in case we are called from destructor. if (_rm) _rm->schedule_external_route_update(); } const ExternalRoute* ExternalRoutes::get_hna_route_in(const IPv4Net& dest, const IPv4& lasthop) throw(BadExternalRoute) { pair rd = _routes_in_by_dest.equal_range(dest); ExternalRoute* er = 0; bool is_found = false; ExternalDestInMap::const_iterator ii; for (ii = rd.first; ii != rd.second; ii++) { er = _routes_in[(*ii).second]; if (er->lasthop() == lasthop) { is_found = true; break; } } if (! is_found) { xorp_throw(BadExternalRoute, c_format("Mapping for %s:%s does not exist", cstring(dest), cstring(lasthop))); } return er; } OlsrTypes::ExternalID ExternalRoutes::get_hna_route_in_id(const IPv4Net& dest, const IPv4& lasthop) throw(BadExternalRoute) { const ExternalRoute* er = get_hna_route_in(dest, lasthop); return er->id(); } const ExternalRoute* ExternalRoutes::get_hna_route_in_by_id(const OlsrTypes::ExternalID erid) throw(BadExternalRoute) { ExternalRouteMap::iterator ii = _routes_in.find(erid); if (ii == _routes_in.end()) { xorp_throw(BadExternalRoute, c_format("Mapping for %u does not exist", XORP_UINT_CAST(erid))); } return (*ii).second; } size_t ExternalRoutes::hna_origin_count() const { set origins; // Count the number of unique origins in _routes_in. ExternalRouteMap::const_iterator ii; for (ii = _routes_in.begin(); ii != _routes_in.end(); ii++) { ExternalRoute* er = (*ii).second; XLOG_ASSERT(! er->is_self_originated()); // Requires that origins is a model of UniqueAssociativeContainer. origins.insert(er->lasthop()); } return origins.size(); } size_t ExternalRoutes::hna_dest_count() const { size_t unique_key_count = 0; ExternalDestInMap::const_iterator ii; for (ii = _routes_in_by_dest.begin(); ii != _routes_in_by_dest.end(); ii = _routes_in_by_dest.upper_bound((*ii).first)) { unique_key_count++; } return unique_key_count; } void ExternalRoutes::get_hna_route_in_list(list& hnalist) { ExternalRouteMap::const_iterator ii; for (ii = _routes_in.begin(); ii != _routes_in.end(); ii++) hnalist.push_back((*ii).first); } /* * HNA Routes Out [Redistribution]. */ bool ExternalRoutes::originate_hna_route_out(const IPv4Net& dest) throw(BadExternalRoute) { debug_msg("MyMainAddr %s Dest %s\n", cstring(_fm.get_main_addr()), cstring(dest)); bool is_first_route = _routes_out.empty(); if (_routes_out_by_dest.find(dest) != _routes_out_by_dest.end()) { debug_msg("Already originating %s\n", cstring(dest)); return false; } OlsrTypes::ExternalID erid = _next_erid++; if (_routes_out.find(erid) != _routes_out.end()) { xorp_throw(BadExternalRoute, c_format("Mapping for ExternalID %u already exists", XORP_UINT_CAST(erid))); } _routes_out[erid] = new ExternalRoute(*this, _eventloop, erid, dest); _routes_out_by_dest.insert(make_pair(dest, erid)); if (is_first_route) start_hna_send_timer(); // If configured to send HNA advertisements immediately when // routes are originated, do so. if (_is_early_hna_enabled) reschedule_immediate_hna_send_timer(); return true; } void ExternalRoutes::withdraw_hna_route_out(const IPv4Net& dest) throw(BadExternalRoute) { debug_msg("MyMainAddr %s Dest %s\n", cstring(_fm.get_main_addr()), cstring(dest)); ExternalDestOutMap::iterator ii = _routes_out_by_dest.find(dest); if (ii == _routes_out_by_dest.end()) { xorp_throw(BadExternalRoute, c_format("%s is not originated by this node", cstring(dest))); } ExternalRouteMap::iterator jj = _routes_out.find((*ii).second); if (jj == _routes_out.end()) { XLOG_UNREACHABLE(); xorp_throw(BadExternalRoute, c_format("Mapping for %s does not exist", cstring(dest))); } ExternalRoute* er = (*jj).second; XLOG_ASSERT(er != 0); if (! er->is_self_originated()) { XLOG_UNREACHABLE(); xorp_throw(BadExternalRoute, c_format("%s is not a self-originated prefix", cstring(dest))); } _routes_out.erase(jj); _routes_out_by_dest.erase(ii); delete er; // If the last originated route has been withdrawn, // stop the HNA transmission timer. // [No point in scheduling an early HNA broadcast, as // HNA routes are expired only when their life timer expires.] if (_routes_out.empty()) { debug_msg("%s: stopping HNA timer as last external route is now" "withdrawn.\n", cstring(_fm.get_main_addr())); stop_hna_send_timer(); } } void ExternalRoutes::clear_hna_routes_out() { ExternalRouteMap::iterator ii, jj; for (ii = _routes_out.begin(); ii != _routes_out.end(); ) { jj = ii++; delete (*jj).second; _routes_out.erase(jj); } } OlsrTypes::ExternalID ExternalRoutes::get_hna_route_out_id(const IPv4Net& dest) throw(BadExternalRoute) { ExternalDestOutMap::const_iterator ii = _routes_out_by_dest.find(dest); if (ii == _routes_out_by_dest.end()) { xorp_throw(BadExternalRoute, c_format("Mapping for %s does not exist", cstring(dest))); } return (*ii).second; } /* * RouteManager interaction. */ void ExternalRoutes::push_external_routes() { XLOG_ASSERT(_rm != 0); size_t pushed_route_count = 0; // For each destination (key) in the HNA "routes in" container, pick // the route with the shortest distance. // [An insertion sort was performed by update_hna_route_in(), so // the first route for each destination has the shortest distance.] // Recursive resolution of the next hop is the RIB's problem. for (ExternalDestInMap::iterator ii = _routes_in_by_dest.begin(); ii != _routes_in_by_dest.end(); ii = _routes_in_by_dest.upper_bound((*ii).first)) { ExternalRoute* er = _routes_in[(*ii).second]; bool is_route_added = _rm->add_hna_route(er->dest(), er->lasthop(), er->distance()); if (is_route_added) ++pushed_route_count; } debug_msg("%s: pushed %u HNA routes to RouteManager.\n", cstring(_fm.get_main_addr()), XORP_UINT_CAST(pushed_route_count)); } /* * Timer manipulation. */ void ExternalRoutes::start_hna_send_timer() { debug_msg("%s -> HNA_RUNNING\n", cstring(_fm.get_main_addr())); _hna_send_timer = _eventloop. new_periodic(get_hna_interval(), callback(this, &ExternalRoutes::event_send_hna)); } void ExternalRoutes::stop_hna_send_timer() { debug_msg("%s -> HNA_STOPPED\n", cstring(_fm.get_main_addr())); _hna_send_timer.clear(); } void ExternalRoutes::restart_hna_send_timer() { reschedule_hna_send_timer(); } void ExternalRoutes::reschedule_hna_send_timer() { _hna_send_timer.reschedule_after(get_hna_interval()); } void ExternalRoutes::reschedule_immediate_hna_send_timer() { _hna_send_timer.schedule_now(); } /* * Event handlers. */ bool ExternalRoutes::event_send_hna() { debug_msg("MyMainAddr %s event_send_hna \n", cstring(_fm.get_main_addr())); XLOG_ASSERT(! _routes_out.empty()); HnaMessage* hna = new HnaMessage(); // 12.2: TTL is set to max, expiry time is set to HNA_HOLD_TIME. hna->set_expiry_time(get_hna_hold_time()); hna->set_origin(_fm.get_main_addr()); hna->set_ttl(OlsrTypes::MAX_TTL); hna->set_hop_count(0); hna->set_seqno(_fm.get_msg_seqno()); // Populate the message with the routes which this node advertises. ExternalRouteMap::const_iterator ii; for (ii = _routes_out.begin(); ii != _routes_out.end(); ii++) { ExternalRoute* er = (*ii).second; hna->add_network(er->dest()); } // Flood the message to the rest of the OLSR topology. bool is_flooded = _fm.flood_message(hna); // consumes hna UNUSED(is_flooded); // XXX refcounting has been removed delete hna; // Reschedule the timer for next time. return true; } bool ExternalRoutes::event_receive_hna( Message* msg, const IPv4& remote_addr, const IPv4& local_addr) { HnaMessage* hna = dynamic_cast(msg); if (0 == hna) return false; // not for me // Put this after the cast to skip false positives. debug_msg("[HNA] MyMainAddr %s Src %s RecvIf %s\n", cstring(_fm.get_main_addr()), cstring(remote_addr), cstring(local_addr)); // 12.5, 1: Sender must be in symmetric 1-hop neighborhood. if (! _nh.is_sym_neighbor_addr(remote_addr)) { debug_msg("Rejecting HNA message from %s via non-neighbor %s\n", cstring(msg->origin()), cstring(remote_addr)); XLOG_TRACE(_olsr.trace()._input_errors, "Rejecting HNA message from %s via non-neighbor %s", cstring(msg->origin()), cstring(remote_addr)); return true; // consumed but invalid. } // Invariant: I should not see my own HNA route advertisements. XLOG_ASSERT(hna->origin() != _fm.get_main_addr()); TimeVal now; _eventloop.current_time(now); // 12.5, 2: For each address/mask pair in the message, // create or update an existing entry. size_t updated_hna_count = 0; try { bool is_hna_created = false; // Account for hop count not being incremented before forwarding. const vector& nets = hna->networks(); const uint16_t distance = hna->hops() + 1; vector::const_iterator ii; for (ii = nets.begin(); ii != nets.end(); ii++) { update_hna_route_in((*ii), hna->origin(), distance, hna->expiry_time() + now, is_hna_created); updated_hna_count++; UNUSED(is_hna_created); } } catch (...) { // If an exception is thrown whilst processing the HNA // message, disregard the rest of the message. } if (updated_hna_count > 0) _rm->schedule_external_route_update(); _fm.forward_message(remote_addr, msg); return true; // consumed UNUSED(local_addr); } void ExternalRoutes::event_hna_route_in_expired(const OlsrTypes::ExternalID erid) { delete_hna_route_in(erid); } void ExternalRoute::update_timer(const TimeVal& expiry_time) { XLOG_ASSERT(! _is_self_originated); if (_expiry_timer.scheduled()) _expiry_timer.clear(); _expiry_timer = _eventloop. new_oneoff_at(expiry_time, callback(this, &ExternalRoute::event_expired)); } void ExternalRoute::event_expired() { XLOG_ASSERT(! _is_self_originated); _parent.event_hna_route_in_expired(id()); } xorp/contrib/olsr/xrl_queue.cc0000664000076400007640000001305411540224221016613 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8 sw=4: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #include "olsr_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/callback.hh" #include "libxorp/ipv4.hh" #include "libxorp/ipv4net.hh" #include "libxorp/status_codes.h" #include "libxorp/service.hh" #include "libxorp/eventloop.hh" #include "olsr.hh" #include "xrl/interfaces/rib_xif.hh" //#include "xrl_io.hh" #include "libxipc/xrl_router.hh" #include "xrl_queue.hh" XrlQueue::XrlQueue(EventLoop& eventloop, XrlRouter& xrl_router) : _io(0), _eventloop(eventloop), _xrl_router(xrl_router), _flying(0) { } EventLoop& XrlQueue::eventloop() const { return _eventloop; } void XrlQueue::queue_add_route(string ribname, const IPv4Net& net, const IPv4& nexthop, uint32_t nexthop_id, uint32_t metric, const PolicyTags& policytags) { Queued q; q.add = true; q.ribname = ribname; q.net = net; q.nexthop = nexthop; q.nexthop_id = nexthop_id; q.metric = metric; q.comment = c_format("add_route: ribname %s net %s nexthop %s", ribname.c_str(), cstring(net), cstring(nexthop)); q.policytags = policytags; _xrl_queue.push_back(q); start(); } void XrlQueue::queue_delete_route(string ribname, const IPv4Net& net) { Queued q; q.add = false; q.ribname = ribname; q.net = net; q.comment = c_format("delete_route: ribname %s net %s", ribname.c_str(), cstring(net)); _xrl_queue.push_back(q); start(); } bool XrlQueue::busy() { return 0 != _flying; } void XrlQueue::start() { if (maximum_number_inflight()) return; // Now there are no outstanding XRLs try and send as many of the queued // route commands as possible as possible. for (;;) { debug_msg("queue length %u\n", XORP_UINT_CAST(_xrl_queue.size())); if (_xrl_queue.empty()) { debug_msg("Output no longer busy\n"); #if 0 _rib_ipc_handler.output_no_longer_busy(); #endif return; } deque::const_iterator qi; qi = _xrl_queue.begin(); XLOG_ASSERT(qi != _xrl_queue.end()); Queued q = *qi; const char *protocol = "olsr"; bool sent = sendit_spec(q, protocol); if (sent) { _flying++; _xrl_queue.pop_front(); if (maximum_number_inflight()) return; continue; } // We expect that the send may fail if the socket buffer is full. // It should therefore be the case that we have some route // adds/deletes in flight. If _flying is zero then something // unexpected has happended. We have no outstanding sends and // still its gone to poo. XLOG_ASSERT(0 != _flying); // We failed to send the last XRL. Don't attempt to send any more. return; } } bool XrlQueue::sendit_spec(Queued& q, const char* protocol) { bool sent; bool unicast = true; bool multicast = false; XrlRibV0p1Client rib(&_xrl_router); if (q.add) { debug_msg("adding route from %s peer to rib\n", protocol); sent = rib. send_add_route4(q.ribname.c_str(), protocol, unicast, multicast, q.net, q.nexthop, q.metric, q.policytags.xrl_atomlist(), callback(this, &XrlQueue::route_command_done, q.comment)); if (!sent) XLOG_WARNING("scheduling add route %s failed", cstring(q.net)); } else { debug_msg("deleting route from %s peer to rib\n", protocol); sent = rib. send_delete_route4(q.ribname.c_str(), protocol, unicast, multicast, q.net, callback(this, &XrlQueue::route_command_done, q.comment)); if (!sent) XLOG_WARNING("scheduling delete route %s failed", cstring(q.net)); } return sent; } void XrlQueue::route_command_done(const XrlError& error, const string comment) { _flying--; debug_msg("callback %s %s\n", comment.c_str(), cstring(error)); switch (error.error_code()) { case OKAY: break; case REPLY_TIMED_OUT: // We should really be using a reliable transport where // this error cannot happen. But it has so lets retry if we can. XLOG_WARNING("callback: %s %s", comment.c_str(), cstring(error)); break; case RESOLVE_FAILED: case SEND_FAILED: case SEND_FAILED_TRANSIENT: case NO_SUCH_METHOD: XLOG_ERROR("callback: %s %s", comment.c_str(), cstring(error)); break; case NO_FINDER: // XXX - Temporarily code dump if this condition occurs. XLOG_FATAL("NO FINDER"); // _olsr.finder_death(__FILE__, __LINE__); break; case BAD_ARGS: case COMMAND_FAILED: case INTERNAL_ERROR: // XXX - Make this XLOG_FATAL when this has been debugged. // TODO 40. XLOG_ERROR("callback: %s %s", comment.c_str(), cstring(error)); break; } // Fire off more requests. start(); } xorp/contrib/olsr/emulate_net.cc0000664000076400007640000001725211421137511017113 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8 sw=4: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "olsr_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/callback.hh" #include "libxorp/exceptions.hh" #include "libxorp/ipv4.hh" #include "libxorp/ipv4net.hh" #include "libxorp/status_codes.h" #include "libxorp/service.hh" #include "libxorp/eventloop.hh" #include "libxorp/tokenize.hh" #include "libxorp/test_main.hh" #include "olsr.hh" #include "debug_io.hh" #include "emulate_net.hh" #include EmulateSubnet::EmulateSubnet(TestInfo& info, EventLoop& eventloop) : _info(info), _eventloop(eventloop), _queue_add(1), _queue_remove(2), _all_nodes_addr(IPv4::ALL_ONES()) { } EmulateSubnet::~EmulateSubnet() { } void EmulateSubnet::receive_frames( const string& interface, const string& vif, IPv4 dst, uint16_t dport, IPv4 src, uint16_t sport, uint8_t* data, uint32_t len, const string instance) { DOUT(_info) << "receive(" << instance << "," << interface << "/" << vif << "/" << dst.str() << ":" << dport << "," << src.str() << ":" << sport << "," << len << "...)" << endl; _queue[_queue_add]. push_back(Frame(interface, vif, dst, dport, src, sport, data, len, instance)); if (_timer.scheduled()) return; XLOG_ASSERT(_queue[_queue_add].size() == 1); _timer = _eventloop. new_oneoff_after_ms(10, callback(this, &EmulateSubnet::next)); } void EmulateSubnet::bind_interface( const string& instance, const string& interface, const string& vif, const IPv4& listen_addr, const uint16_t listen_port, DebugIO& io) { DOUT(_info) << "bind " << instance << ": " << interface << "/" << vif << "/" << listen_addr.str() << ":" << listen_port << endl; io.register_forward(interface, vif, callback(this, &EmulateSubnet::receive_frames, instance)); _ios[Multiplex(instance, interface, vif, listen_addr, listen_port)] = &io; } void EmulateSubnet::unbind_interface( const string& instance, const string& interface, const string& vif, const IPv4& listen_addr, const uint16_t listen_port, DebugIO& io) { DOUT(_info) << "unbind " << instance << ": " << interface << "/" << vif << "/" << listen_addr.str() << ":" << listen_port << endl; io.unregister_forward(interface, vif); map::iterator ii = _ios.find(Multiplex(instance, interface, vif, listen_addr, listen_port)); XLOG_ASSERT(ii != _ios.end()); _ios.erase(ii); } EmulateSubnet::Multiplex::Multiplex( const string& instance, const string& interface, const string& vif, IPv4 listen_addr, uint16_t listen_port) : _instance(instance), _interface(interface), _vif(vif), _listen_addr(listen_addr), _listen_port(listen_port) { } EmulateSubnet::Frame::Frame( const string& interface, const string& vif, IPv4 dst, uint16_t dport, IPv4 src, uint16_t sport, uint8_t* data, uint32_t len, string instance) : _interface(interface), _vif(vif), _dst(dst), _dport(dport), _src(src), _sport(sport), _instance(instance) { _pkt.resize(len); memcpy(&_pkt[0], data, len); } void EmulateSubnet::next() { if (0 == _queue_add) { _queue_add = 1; _queue_remove = 0; } else { _queue_add = 0; _queue_remove = 1; } while (!_queue[_queue_remove].empty()) { Frame frame = _queue[_queue_remove].front(); _queue[_queue_remove].pop_front(); forward(frame); } } void EmulateSubnet::forward(Frame frame) { uint8_t* data = &frame._pkt[0]; uint32_t len = frame._pkt.size(); map::iterator i; for(i = _ios.begin(); i != _ios.end(); i++) { Multiplex m = (*i).first; // Prevent loopback. if (m._instance == frame._instance) continue; DOUT(_info) << "Send to: " << m._instance << ": " << m._listen_addr.str() << ":" << m._listen_port << " on " << m._interface << "/" << m._vif << " len " << len << " from " << frame._src.str() << ":" << frame._sport << endl; // Check if the packet was destined for us or one of the // configured broadcast addresses. if (frame._dport == m._listen_port && (frame._dst == _all_nodes_addr || frame._dst == m._listen_addr)) { (*i).second->receive(m._interface, m._vif, frame._dst, frame._dport, frame._src, frame._sport, data, len); } } } EmulateSubnetHops::EmulateSubnetHops(TestInfo& info, EventLoop& eventloop, uint8_t hopdelta, uint8_t maxlinks) : EmulateSubnet(info, eventloop), _hopdelta(hopdelta), _maxlinks(maxlinks), _empty_pkt_drops(0), _ttl_msg_drops(0) { // Only HELLO messages require special invariants when // traversing a simulation of multiple OLSR links. _md.register_decoder(new HelloMessage()); } EmulateSubnetHops::~EmulateSubnetHops() { } void EmulateSubnetHops::bind_interface(const string& instance, const string& interface, const string& vif, const IPv4& listen_addr, const uint16_t listen_port, DebugIO& io) { if (_ios.size() >= _maxlinks) XLOG_UNREACHABLE(); EmulateSubnet::bind_interface(instance, interface, vif, listen_addr, listen_port, io); } void EmulateSubnetHops::forward(Frame frame) { // Short-circuit; forward as per base class if hopcount is 0. if (hopdelta() == 0) { EmulateSubnet::forward(frame); return; } uint8_t* data = &frame._pkt[0]; uint32_t len = frame._pkt.size(); // decode packet Packet* pkt = new Packet(_md, OlsrTypes::UNUSED_FACE_ID); try { pkt->decode(data, len); } catch (InvalidPacket& e) { debug_msg("bad packet\n"); return; } vector& messages = pkt->get_messages(); vector::iterator ii, jj; for (ii = messages.begin(); ii != messages.end(); ) { jj = ii++; Message* msg = (*jj); int new_hops = msg->hops() + hopdelta(); int new_ttl = msg->ttl() - hopdelta(); // If ttl underflowed for this message, drop it. if (new_hops > OlsrTypes::MAX_TTL || new_ttl < 0) { //jj->release(); // if refcounting delete (*jj); // if not refcounting. messages.erase(jj); // Maintain a count of such drops. ++_ttl_msg_drops; continue; } // Invariant: OLSR HELLO messages should have been dropped // by hopcount simulation above. XLOG_ASSERT(0 == dynamic_cast(msg)); // Rewrite the hop counts in the contained message. msg->set_hop_count(new_hops); msg->set_ttl(new_ttl); } if (messages.empty()) { // If no messages, drop packet and maintain a count of such drops. ++_empty_pkt_drops; } else { // Encode rewritten packet into Frame's existing buffer. // The other properties of Frame are preserved. pkt->encode(frame.get_buffer()); // Forward as per base class. EmulateSubnet::forward(frame); } // XXX no refcounting #if 1 vector::iterator kk; for (kk = messages.begin(); kk != messages.end(); kk++) delete (*kk); #endif delete pkt; } xorp/contrib/olsr/olsr_types.cc0000664000076400007640000000527511421137511017016 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8 sw=4: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "olsr_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/exceptions.hh" #include "libxorp/ipv4.hh" #include "libxorp/ipv4net.hh" #include "libxorp/ref_ptr.hh" #include "libxorp/timeval.hh" #include "olsr_types.hh" #define NAME_CASE(x) case OlsrTypes:: x : return x##_NAME ; #define NUM_CASE(x) case x : return #x ; const double EightBitTime::_scaling_factor = 0.0625f; static const char* TCR_MPRS_IN_NAME = "mprs_in"; static const char* TCR_MPRS_INOUT_NAME = "mprs_inout"; static const char* TCR_ALL_NAME = "all"; const char* tcr_to_str(const OlsrTypes::TcRedundancyType t) { switch (t) { NAME_CASE(TCR_MPRS_IN); NAME_CASE(TCR_MPRS_INOUT); NAME_CASE(TCR_ALL); } XLOG_UNREACHABLE(); return 0; } static const char* WILL_NEVER_NAME = "never"; static const char* WILL_LOW_NAME = "low"; static const char* WILL_DEFAULT_NAME = "default"; static const char* WILL_HIGH_NAME = "high"; static const char* WILL_ALWAYS_NAME = "always"; const char* will_to_str(const OlsrTypes::WillType t) { switch (t) { NAME_CASE(WILL_NEVER); NUM_CASE(2); NAME_CASE(WILL_LOW); NAME_CASE(WILL_DEFAULT); NUM_CASE(4); NUM_CASE(5); NAME_CASE(WILL_HIGH); NAME_CASE(WILL_ALWAYS); } XLOG_UNREACHABLE(); return 0; } static const char* VT_UNKNOWN_NAME = "UNKNOWN"; static const char* VT_NEIGHBOR_NAME = "N"; static const char* VT_TWOHOP_NAME = "N2"; static const char* VT_TOPOLOGY_NAME = "TC"; static const char* VT_MID_NAME = "MID"; static const char* VT_HNA_NAME = "HNA"; const char* vt_to_str(const OlsrTypes::VertexType vt) { switch (vt) { NAME_CASE(VT_UNKNOWN); NAME_CASE(VT_NEIGHBOR); NAME_CASE(VT_TWOHOP); NAME_CASE(VT_TOPOLOGY); NAME_CASE(VT_MID); NAME_CASE(VT_HNA); } XLOG_UNREACHABLE(); return 0; } #undef NUM_CASE #undef NAME_CASE xorp/contrib/olsr/xrl_io.hh0000664000076400007640000003322511421137511016115 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8 sw=4: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/contrib/olsr/xrl_io.hh,v 1.3 2008/10/02 21:56:36 bms Exp $ #ifndef __OLSR_XRL_IO_HH__ #define __OLSR_XRL_IO_HH__ #include "libxipc/xrl_router.hh" #include "libfeaclient/ifmgr_xrl_mirror.hh" #include "policy/backend/policytags.hh" #include "io.hh" #include "xrl_queue.hh" class EventLoop; class XrlPort; typedef list XrlPortList; typedef map XrlDeadPortMap; /** * @short Concrete implementation of IO using XRLs */ class XrlIO : public IO, public IfMgrHintObserver, public ServiceChangeObserverBase { public: /** * Construct an XrlIO instance. * * @param eventloop the event loop for the OLSR process. * @param xrl_router the name of the XRL router instance. * @param feaname the name of the FEA XRL target. * @param ribname the name of the RIB XRL target. */ XrlIO(EventLoop& eventloop, XrlRouter& xrl_router, const string& feaname, const string& ribname); /** * Destroy an XrlIO instance. */ ~XrlIO(); /** * Startup operation. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int startup(); /** * Shutdown operation. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int shutdown(); /** * Called when internal subsystem comes up. * * @param name the name of the affected subsystem */ void component_up(string name); /** * Called when internal subsystem goes down. * * @param name the name of the affected subsystem */ void component_down(string name); /** * Receive UDP datagrams. Specific to XrlIO. * * @param sockid the id of the socket. * @param interface the interface where received. * @param vif the vif where received. * @param src the source port of the UDP datagram. * @param sport the source address of the UDP datagram. * @param payload the datagram payload. */ void receive(const string& sockid, const string& interface, const string& vif, const IPv4& src, const uint16_t& sport, const vector& payload); /** * Send a UDP datagram on a specific link. * * @param interface the interface to send from. * @param vif the vif to send from. * @param src the IPv4 address to send from. * @param sport the UDP port to send from. * @param dst the IPv4 address to send to. * @param dport the UDP port to send to. * @param data the datagram payload. * @param len the length of the buffer @param data * @return true if the datagram was queued successfully, * otherwise false. */ bool send(const string& interface, const string& vif, const IPv4& src, const uint16_t& sport, const IPv4& dst, const uint16_t& dport, uint8_t* data, const uint32_t& len); /** * Enable the interface/vif to receive frames. * XXX * * @param interface the name of the interface to enable. * @param vif the name of the vif to enable. * @return true if the interface/vif was enabled successfully, * otherwise false. */ bool enable_interface_vif(const string& interface, const string& vif); /** * Disable this interface/vif from receiving frames. * XXX * * @param interface the name of the interface to disable. * @param vif the name of the vif to disable. * @return true if the interface/vif was disabled successfully, * otherwise false. */ bool disable_interface_vif(const string& interface, const string& vif); /** * Enable an IPv4 address and port for OLSR datagram reception and * transmission. * * @param interface the interface to enable. * @param vif the vif to enable. * @param address the address to enable. * @param port the port to enable. * @param all_nodes_address the address to transmit to. * @return true if the address was enabled, otherwise false. */ bool enable_address(const string& interface, const string& vif, const IPv4& address, const uint16_t& port, const IPv4& all_nodes_address); /** * Disable an IPv4 address and port for OLSR datagram reception. * * @param interface the interface to disable. * @param vif the vif to disable. * @param address the address to disable. * @param port the port to disable. * @return true if the address was disabled, otherwise false. */ bool disable_address(const string& interface, const string& vif, const IPv4& address, const uint16_t& port); /** * Test whether an interface is enabled. * * @param interface the name of the interface to test. * @return true if it exists and is enabled, otherwise false. */ bool is_interface_enabled(const string& interface) const; /** * Test whether an interface/vif is enabled. * * @param interface the name of the interface to test. * @param vif the name of the vif to test. * @return true if it exists and is enabled, otherwise false. */ bool is_vif_enabled(const string& interface, const string& vif) const; /** * Test whether this interface/vif is broadcast capable. * * @param interface the interface to test. * @param vif the vif to test. * @return true if it is broadcast capable, otherwise false. */ bool is_vif_broadcast_capable(const string& interface, const string& vif); /** * Test whether this interface/vif is multicast capable. * * @param interface the interface to test. * @param vif the vif to test. * @return true if it is multicast capable, otherwise false. */ bool is_vif_multicast_capable(const string& interface, const string& vif); /** * @short Return true if the given vif is a loopback vif. */ bool is_vif_loopback(const string& interface, const string& vif); /** * Test whether an interface/vif/address is enabled. * * @param interface the name of the interface to test. * @param vif the name of the vif to test. * @param address the address to test. * @return true if it exists and is enabled, otherwise false. */ bool is_address_enabled(const string& interface, const string& vif, const IPv4& address) const; /** * Get all addresses associated with this interface/vif. * * @param interface the name of the interface * @param vif the name of the vif * @param addresses (out argument) list of associated addresses * * @return true if there are no errors. */ bool get_addresses(const string& interface, const string& vif, list& addresses) const; /** * Get the broadcast address associated with this vif. * * @param interface the name of the interface * @param vif the name of the vif * @param address (out argument) "all nodes" address * * @return true if there are no errors. */ bool get_broadcast_address(const string& interface, const string& vif, IPv4& address) const; /** * Get the broadcast address associated with this IPv4 address. * * @param interface the name of the interface * @param vif the name of the vif * @param address IPv4 binding address * @param bcast_address (out argument) primary broadcast address * @return true if there are no errors. */ bool get_broadcast_address(const string& interface, const string& vif, const IPv4& address, IPv4& bcast_address) const; /** * Get the interface ID. * * @param interface the name of the interface. * @param interface_id the value if found.. * @return true if the interface ID has been found.. */ bool get_interface_id(const string& interface, uint32_t& interface_id); /** * Obtain the subnet prefix length for an interface/vif/address. * * @param interface the name of the interface. * @param vif the name of the vif. * @param address the address. * @return the subnet prefix length for the address. */ uint32_t get_prefix_length(const string& interface, const string& vif, IPv4 address); /** * Obtain the MTU for an interface. * * @param interface the name of the interface. * @return the mtu for the interface. */ uint32_t get_mtu(const string& interface); /** * Register with the RIB. */ void register_rib(); /** * Remove registration from the RIB. */ void unregister_rib(); /** * Callback method to: signal that an XRL command which has been * sent to the RIB has returned. * * @param error the XRL command return code. * @param up indicates if the RIB component has been brought up or down. * @param comment text description of operation being performed. */ void rib_command_done(const XrlError& error, bool up, const char *comment); /** * Add route to RIB. * * @param net network * @param nexthop * @param nexthop_id interface ID towards the nexthop * @param metric to network * @param policytags policy info to the RIB. * @return true if the route add was queued successfully, * otherwise false. */ bool add_route(IPv4Net net, IPv4 nexthop, uint32_t nexthop_id, uint32_t metric, const PolicyTags& policytags); /** * Replace route in RIB. * * @param net network * @param nexthop * @param nexthop_id interface ID towards the nexthop * @param metric to network * @param policytags policy info to the RIB. * @return true if the route replacement was queued successfully, * otherwise false. */ bool replace_route(IPv4Net net, IPv4 nexthop, uint32_t nexthop_id, uint32_t metric, const PolicyTags& policytags); /** * Delete route from RIB. * * @param net the destination to delete route for. * @return true if the route delete was queued successfully, * otherwise false. */ bool delete_route(IPv4Net net); private: /** * A method invoked when the status of a service changes. * * @param service the service whose status has changed. * @param old_status the old status. * @param new_status the new status. */ void status_change(ServiceBase* service, ServiceStatus old_status, ServiceStatus new_status); /** * @return a pointer to the interface manager service base. */ const ServiceBase* ifmgr_mirror_service_base() const { return dynamic_cast(&_ifmgr); } /** * @return a reference to the interface manager's interface tree. */ const IfMgrIfTree& ifmgr_iftree() const { return _ifmgr.iftree(); } /** * An IfMgrHintObserver method invoked when the initial interface tree * information has been received. */ void tree_complete(); /** * An IfMgrHintObserver method invoked whenever the interface tree * information has been changed. */ void updates_made(); /** * Callback method to: signal that the XRL command to send * a UDP datagram has returned. * * @param xrl_error the XRL command return code. */ void send_cb(const XrlError& xrl_error); /** * Find OLSR port associated with interface, vif, address tuple. * * @return pointer to port on success, 0 if port could not be found. */ XrlPort* find_port(const string& ifname, const string& vifname, const IPv4& addr); /** * Find OLSR port associated with interface, vif, address tuple. * * @return pointer to port on success, 0 if port could not be found. */ const XrlPort* find_port(const string& ifname, const string& vifname, const IPv4& addr) const; /** * @return pointer to list of active XrlPorts. */ inline XrlPortList& ports() { return _ports; } /** * @return pointer to list of active XrlPorts. */ inline const XrlPortList& ports() const { return _ports; } /** * Gradually start each XrlPort to avoid races with the FEA. */ void try_start_next_port(); private: EventLoop& _eventloop; XrlRouter& _xrl_router; string _feaname; string _ribname; uint32_t _component_count; /** * @short libfeaclient wrapper. */ IfMgrXrlMirror _ifmgr; /** * @short local copy of interface state obtained from libfeaclient. */ IfMgrIfTree _iftree; /** * @short Queue of RIB add/delete XRL commands. */ XrlQueue _rib_queue; /** * @short List of active XrlPorts. */ XrlPortList _ports; /** * @short XrlPorts awaiting I/O shutdown. */ XrlDeadPortMap _dead_ports; }; #endif // __OLSR_XRL_IO_HH__ xorp/contrib/olsr/xrl_queue.hh0000664000076400007640000000650711421137511016635 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8 sw=4: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/contrib/olsr/xrl_queue.hh,v 1.3 2008/10/02 21:56:37 bms Exp $ #ifndef __OLSR_XRL_QUEUE_HH__ #define __OLSR_XRL_QUEUE_HH__ class XrlIO; /** * @short Helper class to queue route adds and deletes to the RIB. */ class XrlQueue { public: XrlQueue(EventLoop& eventloop, XrlRouter& xrl_router); void set_io(XrlIO* io) { _io = io; } /** * Queue a route add to the RIB. * * @param ribname the name of the RIB XRL target to send to. * @param net the destination. * @param nexthop the next hop. * @param nexthop_id the libfeaclient ID of the outward interface. * @param metric the route metric. * @param policytags The policy tags for the route. */ void queue_add_route(string ribname, const IPv4Net& net, const IPv4& nexthop, uint32_t nexthop_id, uint32_t metric, const PolicyTags& policytags); /** * Queue a route delete to the RIB. * * @param ribname the name of the RIB XRL target to send to. * @param net the destination. */ void queue_delete_route(string ribname, const IPv4Net& net); /** * @return true if RIB commands are currently in flight. */ bool busy(); private: struct Queued { bool add; string ribname; IPv4Net net; IPv4 nexthop; uint32_t nexthop_id; uint32_t metric; string comment; PolicyTags policytags; }; static const size_t WINDOW = 100; EventLoop& eventloop() const; /** * @return true if the maximum number of XRLs flight has been exceeded. */ inline bool maximum_number_inflight() const { return _flying >= WINDOW; } /** * Start the transmission of XRLs to tbe RIB. */ void start(); /** * The specialised method called by sendit to deal with IPv4/IPv6. * * @param q the queued command. * @param protocol "olsr" * @return True if the add/delete was queued. */ bool sendit_spec(Queued& q, const char *protocol); /** * Callback method to: signal completion of a RIB command. * * @param error reference to an XrlError containing command status. * @param comment a textual description of the error. */ void route_command_done(const XrlError& error, const string comment); private: XrlIO* _io; EventLoop& _eventloop; XrlRouter& _xrl_router; deque _xrl_queue; /** * Number of XRLs currently in flight. */ size_t _flying; }; #endif // __OLSR_XRL_QUEUE_HH__ xorp/contrib/olsr/io.hh0000664000076400007640000002602111540224221015221 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8 sw=4: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/contrib/olsr/io.hh,v 1.3 2008/10/02 21:56:34 bms Exp $ #ifndef __OLSR_IO_HH__ #define __OLSR_IO_HH__ /** * @short Abstract interface to low level IO operations. * * An abstract class that defines packet reception and * transmission. The details of how packets are received or transmitted * are therefore hidden from the internals of the OLSR code. */ class IO : public ServiceBase { public: IO() {} virtual ~IO() {} /** * Enable an IPv4 address and port for OLSR datagram reception and * transmission. * * @param interface the interface to enable. * @param vif the vif to enable. * @param address the address to enable. * @param port the port to enable. * @param all_nodes_address the all-nodes address to enable. * @return true if the address was enabled, otherwise false. */ virtual bool enable_address(const string& interface, const string& vif, const IPv4& address, const uint16_t& port, const IPv4& all_nodes_address) = 0; /** * Disable an IPv4 address and port for OLSR datagram reception. * * @param interface the interface to disable. * @param vif the vif to disable. * @param address the address to disable. * @param port the port to disable. * @return true if the address was disabled, otherwise false. */ virtual bool disable_address(const string& interface, const string& vif, const IPv4& address, const uint16_t& port) = 0; /** * Test whether this interface is enabled. * * @param interface the interface to test. * @return true if it exists and is enabled, otherwise false. */ virtual bool is_interface_enabled(const string& interface) const = 0; /** * Test whether this interface/vif is enabled. * * @param interface the interface to test. * @param vif the vif to test. * @return true if it exists and is enabled, otherwise false. */ virtual bool is_vif_enabled(const string& interface, const string& vif) const = 0; /** * Test whether this interface/vif is broadcast capable. * * @param interface the interface to test. * @param vif the vif to test. * @return true if it is broadcast capable, otherwise false. */ virtual bool is_vif_broadcast_capable(const string& interface, const string& vif) = 0; /** * Test whether this interface/vif is multicast capable. * * @param interface the interface to test. * @param vif the vif to test. * @return true if it is multicast capable, otherwise false. */ virtual bool is_vif_multicast_capable(const string& interface, const string& vif) = 0; /** * Test whether this interface/vif is a loopback interface. * * @param interface the interface to test. * @param vif the vif to test. * @return true if it is a loopback interface, otherwise false. */ virtual bool is_vif_loopback(const string& interface, const string& vif) = 0; /** * Test whether this interface/vif/address is enabled. * * @param interface the interface to test. * @param vif the vif to test. * @param address the address to test. * @return true if it exists and is enabled, otherwise false. */ virtual bool is_address_enabled(const string& interface, const string& vif, const IPv4& address) const = 0; /** * Callback for interface status from the FEA. */ typedef XorpCallback2::RefPtr InterfaceStatusCb; /** * Callback for vif status from the FEA. */ typedef XorpCallback3::RefPtr VifStatusCb; /** * Callback for address status from the FEA. */ typedef XorpCallback4::RefPtr AddressStatusCb; /** * Callback for packet reception from the FEA. */ typedef XorpCallback8::RefPtr ReceiveCallback; /** * Get all addresses associated with this interface/vif. * * @param interface the name of the interface * @param vif the name of the vif * @param addresses (out argument) list of associated addresses * @return true if there are no errors. */ virtual bool get_addresses(const string& interface, const string& vif, list& addresses) const = 0; /** * Get the broadcast address associated with this interface/vif/address. * * @param interface the name of the interface * @param vif the name of the vif * @param address IPv4 binding address * @param bcast_address (out argument) primary broadcast address * @return true if there are no errors. */ virtual bool get_broadcast_address(const string& interface, const string& vif, const IPv4& address, IPv4& bcast_address) const = 0; /** * Get the ID of the interface, as seen by libfeaclient. * * @param interface the name of the interface. * @param interface_id (out argument) interface ID. * @return the interface id for this interface. */ virtual bool get_interface_id(const string& interface, uint32_t& interface_id) = 0; /** * Get the MTU for an interface. * * @param interface the name of the interface. * @return the mtu for this interface. */ virtual uint32_t get_mtu(const string& interface) = 0; /** * Add a callback for tracking the interface status. * * The callback will be invoked whenever the status of the interface * is changed from disabled to enabled or vice-versa. * * @param cb the callback to register. */ inline void register_interface_status(InterfaceStatusCb cb) { _interface_status_cb = cb; }; /** * Add a callback for tracking the interface/vif status. * * The callback will be invoked whenever the status of the interface/vif * is changed from disabled to enabled or vice-versa. * * @param cb the callback to register. */ inline void register_vif_status(VifStatusCb cb) { _vif_status_cb = cb; }; /** * Add a callback for tracking the interface/vif/address status. * * The callback will be invoked whenever the status of the tuple * (interface, vif, address) is changed from disabled to enabled * or vice-versa. * * @param cb the callback to register. */ inline void register_address_status(AddressStatusCb cb) { _address_status_cb = cb; }; /** * Register for receiving datagrams. * * @param cb the callback to register. */ inline void register_receive(ReceiveCallback cb) { _receive_cb = cb; }; protected: ReceiveCallback _receive_cb; InterfaceStatusCb _interface_status_cb; VifStatusCb _vif_status_cb; AddressStatusCb _address_status_cb; public: struct interface_vif { string _interface_name; string _vif_name; }; /** * Send a UDP datagram from src:sport to dst:dport, on * the given interface, if possible. * * @param interface the interface to transmit from. * @param vif the vif to transmit from. * @param src the IPv4 source address to transmit from. * @param sport the UDP source port to transmit from. * @param dst the IPv4 destination address to send to. * @param dport the UDP destination port to send to. * @param data the datagram to transmit. * @param len the length of the datagram to transmit. * @return true if the datagram was sent OK, otherwise false. */ virtual bool send(const string& interface, const string& vif, const IPv4& src, const uint16_t& sport, const IPv4& dst, const uint16_t& dport, uint8_t* data, const uint32_t& len) = 0; /** * Add route. * * @param net network * @param nexthop * @param faceid interface ID towards the nexthop * @param metric to network * @param policytags policy info to the RIB. * @return true if the route was added OK, otherwise false. */ virtual bool add_route(IPv4Net net, IPv4 nexthop, uint32_t faceid, uint32_t metric, const PolicyTags& policytags) = 0; /** * Replace route. * * @param net network * @param nexthop * @param faceid interface ID towards the nexthop * @param metric to network * @param policytags policy info to the RIB. * @return true if the route was replaced OK, otherwise false. */ virtual bool replace_route(IPv4Net net, IPv4 nexthop, uint32_t faceid, uint32_t metric, const PolicyTags& policytags) = 0; /** * Delete route. * * @param net network * @return true if the route was deleted OK, otherwise false. */ virtual bool delete_route(IPv4Net net) = 0; /** * Store a mapping of the OLSR internal interface ID to * interface/vif. This will be required by when installing a route. * * @param interface_id the ID of the interface, as seen by libfeaclient. * @param interface the name of the interface mapped to interface_id. * @param vif the name of vif mapped to interface_id. */ inline void set_interface_mapping(uint32_t interface_id, const string& interface, const string& vif) { interface_vif iv; iv._interface_name = interface; iv._vif_name = vif; _interface_vif[interface_id] = iv; }; /** * Given an OLSR interface ID, return the interface/vif. * * @param interface_id the ID of the interface, as seen by libfeaclient. * @param interface the name of the interface mapped to interface_id. * @param vif the name of vif mapped to interface_id. * @return true if interface_id was found, otherwise false. */ inline bool get_interface_vif_by_interface_id(uint32_t interface_id, string& interface, string& vif) { if (_interface_vif.find(interface_id) == _interface_vif.end()) return false; interface_vif iv = _interface_vif[interface_id]; interface = iv._interface_name; vif = iv._vif_name; return true; }; protected: map _interface_vif; }; #endif // __OLSR_IO_HH__ xorp/contrib/olsr/route_manager.hh0000664000076400007640000003445211421137511017454 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8 sw=4: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/contrib/olsr/route_manager.hh,v 1.3 2008/10/02 21:56:35 bms Exp $ #ifndef __OLSR_ROUTE_MANAGER_HH__ #define __OLSR_ROUTE_MANAGER_HH__ class Olsr; class FaceManager; class Neighborhood; class TopologyManager; class ExternalRoutes; /** * @short An OLSR internal route entry. * * OLSRv1, unlike OSPF, does not implement areas, therefore there is no * need in the current design to have logical separation between the routes * which it processes internally and those which it exports to the RIB. * * It also has no concept of network LSAs [HNA routes are not used in * calculating reachability in the OLSR topology] therefore the address * property is not present here, nor is the 'discard' flag, nor is the * path type. * * TODO: Import this definition into UML. * TODO: Templatize to hold IPv6. */ class RouteEntry { public: RouteEntry() : _destination_type(OlsrTypes::VT_UNKNOWN), _direct(false), _nexthop(IPv4::ZERO()), _faceid(OlsrTypes::UNUSED_FACE_ID), _cost(0), _originator(IPv4::ZERO()), _main_address(IPv4::ZERO()), _filtered(false) {} /* * @return An integer which identifies the part of OLSR from which we * have learned this route: neighborhood, TC, HNA, MID. */ inline OlsrTypes::VertexType destination_type() const { return _destination_type; } /** * Set the type of the destination in this route entry. * The VertexType is re-used for this. * * @param vt destination type. */ inline void set_destination_type(const OlsrTypes::VertexType vt) { _destination_type = vt; } /** * @return true if the destination is directly connected. * * Strictly speaking, we should still add it to the RIB; the local * node MAY use ARP for L2 resolution, but we may still wish to * redistribute routes for these destinations, in order to see OLSR * protocol specific information for those destinations. */ inline bool direct() const { return _direct; } /** * Set if the destination is directly connected. * * @param is_direct true if destination is directly connected. */ inline void set_direct(bool is_direct) { _direct = is_direct; } /** * @return protocol address of next hop. */ inline IPv4 nexthop() const { return _nexthop; } /** * Set protocol address of next hop. * * @param nexthop address of next hop. */ inline void set_nexthop(const IPv4& nexthop) { _nexthop = nexthop; } /** * @return the ID of the interface used to reach the destination. */ inline OlsrTypes::FaceID faceid() { return _faceid; } /** * Set the ID of the interface used to reach the destination. * * @param faceid the ID of the outward interface. */ inline void set_faceid(const OlsrTypes::FaceID faceid) { _faceid = faceid; } /** * @return OLSR protocol cost of route. */ inline uint32_t cost() const { return _cost; } /** * Set OLSR protocol cost of route. * * @param cost OLSR protocol cost of route. */ inline void set_cost(const uint32_t cost) { _cost = cost; } /** * @return main address of node which originated this route. */ inline IPv4 originator() const { return _originator; } /** * Set the originator of this route. * * @param originator main address of node originating this route. */ inline void set_originator(const IPv4& originator) { _originator = originator; } /** * @return the OLSR main address of the destination. * May be unset. */ inline IPv4 main_address() const { return _main_address; } /** * Set the main address of the destination. * Applicable only if if this is a route which is tagged with such * information (N1, N2, TC, MID). * May be the same as the destination. * * @param main_addr the OLSR main address of the destination. */ inline void set_main_address(const IPv4& main_addr) { _main_address = main_addr; } /** * @return true if this route has been filtered by policy filters. */ inline bool filtered() const { return _filtered; } /** * Set if this route has been filtered by policy filters. * @param was_filtered true if the route has been filtered. */ inline void set_filtered(const bool was_filtered) { _filtered = was_filtered; } /** * @return a string representation of the route entry. */ string str(); private: OlsrTypes::VertexType _destination_type; // MID, TC, HNA. neighbor, etc. bool _direct; // destination is directly connected IPv4 _nexthop; OlsrTypes::FaceID _faceid; // face used to reach nexthop uint32_t _cost; // OLSR link cost. IPv4 _originator; // advertising router IPv4 _main_address; // If this is an N1, N2, TC or MID // route, this is the main address // of the destination; similar to // OSPF router ID. bool _filtered; // true if policy filtered this route. }; /** * @short Routing table manager. * * Performs incremental and/or deferred update of the OLSR routing table * for the domain which this routing process interacts with. * * Whilst incremental updates are possible, the code does not * currently do this because the incremental shortest-path-tree code * does not yet support incremental updates. The interface is however * written with this in mind as it may turn out to offer faster convergence * for link-state MANET protocols like OLSR. * * Classes which produce artifacts for RouteManager to turn into routes * therefore call schedule_route_update() whenever their state changes. * RouteManager will call them back. We do not use polymorphism for this * as the relationships do not significantly change. */ class RouteManager { public: RouteManager(Olsr& olsr, EventLoop& eventloop, FaceManager* fm, Neighborhood* nh, TopologyManager* tm, ExternalRoutes* er); ~RouteManager(); /** * Schedule a recalculation of the entire routing table. */ void schedule_route_update(); /** * Schedule a recalculation of external routes. */ void schedule_external_route_update(); /** * Add a link to a one-hop neighbor to the SPT. * * Given a link at radius 1 in the routing graph from this node, * add its endpoint node and the edge to the SPT graph. * * TODO: Bias for MPRs and willingness correctly. * * @param l pointer to a link between the origin and the Neighbor n. * @param n pointer to the Neighbor n. * @return true if the link was added OK. */ bool add_onehop_link(const LogicalLink* l, const Neighbor* n); /** * Add a two-hop link and neighbor to the SPT. * * Given a link at radius 2 in the routing graph from the origin, * add its endpoint nodes and the edge to the SPT graph. * * In the absence of ETX measurements, the only possible cost for a 2-hop * link which we can infer from link state is 1 (just the hop), and * this is what we get from Neighborhood. * * @param n pointer to the Neighbor n, which must already have been added * to the SPT graph. * @param l2 pointer to a link between Neighbor n and TwoHopNeighbor n2. * @param n2 pointer to a TwoHopNeighbor. * @return true if the two-hop link was added OK. */ bool add_twohop_link(const Neighbor* n, const TwoHopLink* l2, const TwoHopNeighbor* n2); /** * Add a TC-derived link and neighbor to the SPT. * * Given a link at radius > 2 in the routing graph from the origin, * add its far endpoint nodes and the edge to the SPT graph. * In the absence of ETX measurements, the only possible cost for a TC * link which we can infer from link state is 1 (just the hop), and * this is what we get from TopologyManager. * * @param tc The topology entry being considered for SPT add. * @return true if a link and vertex for the far endpoint was added * to the SPT, otherwise false. */ bool add_tc_link(const TopologyEntry* tc); /** * Add an external route, possibly HNA derived, to the current trie. * * Section 12: Non-OLSR Interfaces. * The metric of an HNA route in RFC-compliant OLSR is identical to * that of its last-hop. If the origin of the HNA route is not reachable * in the OLSR SPT, the HNA route will be rejected; see NOTES. * * @param dest the destination prefix. * @param lasthop the last hop advertising @param dest * Note: This is a main address, not an interface address. * @param distance the number of OLSR hops to @param lasthop * @return true if the route was added OK. */ bool add_hna_route(const IPv4Net& dest, const IPv4& lasthop, const uint16_t distance); /** * Backend method to: push all the routes through the policy filters * whenever they are updated. */ void push_routes(); protected: /** * Recompute the OLSR domain portion of the routing table. * * Until incremental computation is implemented, we perform the SPT * computation fully every time a recomputation is triggered. Producers * of routes call us back to populate the graph. */ void recompute_all_routes(); inline Vertex make_origin_vertex() { Vertex v; v.set_is_origin(true); v.set_main_addr(_fm->get_main_addr()); return v; } // // Route table transactions. // /** * Begin the route computation transaction. * * This must be called before any routes are recomputed, to take * snapshots of the state which is present in the RIB. */ void begin(); /** * End the route computation transaction. * * This must be called after all routes are recomputed in order for * them to propagate to the RIB. */ void end(); // // Route table manipulation. // /** * Internal method to: Add a route entry to the current * internal trie. * * @param net the destination prefix. * @param rt the entry to add. * @return true if the entry was added OK. */ bool add_entry(const IPv4Net& net, const RouteEntry& rt); /** * Internal method to: Delete a route entry from the current * internal trie. * * @param net the destination prefix. * @param rt the entry to delete. * @return true if the entry was deleted OK. */ bool delete_entry(const IPv4Net& net, const RouteEntry& rt); /** * Internal method to: Replace a route entry in the current * internal trie. * * @param net the destination prefix. * @param rt the new route entry which replaces @param previous_rt * @param previous_rt the entry to replace. * @return true if the entry was replaced OK. */ bool replace_entry(const IPv4Net& net, const RouteEntry& rt, const RouteEntry& previous_rt); // // RIB interaction. // /** * Backend method to: add a route to the RIB with policy filtering. * * @param net the destination prefix. * @param nexthop the next-hop. * @param metric the computed metric of the route. * @param rt the route entry to add, containing all other fields. * @return true if the route was added to the RIB OK, otherwise false. */ bool add_route(IPv4Net net, IPv4 nexthop, uint32_t metric, RouteEntry& rt); /** * Backend method to: withdraw a route from the RIB. * * @param net the destination prefix. * @param rt the route entry to delete, containing all other fields. * @return true if the route was withdrawn from the RIB OK, * otherwise false. */ bool delete_route(const IPv4Net& net, const RouteEntry& rt); /** * Backend method to: replace a route that has been sent to the RIB. * * @param net the destination prefix. * @param nexthop the new next-hop. * @param metric the new computed metric of the route. * @param rt the route entry to add, containing all other fields. * @param previous_rt the route entry to replace. * @return true if the route was replaced in the RIB OK, otherwise false. */ bool replace_route(IPv4Net net, IPv4 nexthop, uint32_t metric, RouteEntry& rt, RouteEntry& previous_rt); // // Policy interaction. // /** * Backend method to: perform policy filtering when a route may be * plumbed to the RIB. * May not be declared const; policy may modify fields. * * @param net the destination prefix. * @param nexthop the next-hop. * @param metric the metric. * @param rt the route entry to filter, containing all other fields. * @param policytags the tags presented to us by the policy engine. * @return true if the route was accepted by the policy engine, * otherwise false if any error occurred. */ bool do_filtering(IPv4Net& net, IPv4& nexthop, uint32_t& metric, RouteEntry& rt, PolicyTags& policytags); private: Olsr& _olsr; EventLoop& _eventloop; FaceManager* _fm; Neighborhood* _nh; TopologyManager* _tm; ExternalRoutes* _er; Spt _spt; Vertex _origin; bool _in_transaction; XorpTask _route_update_task; Trie* _current; Trie* _previous; }; #endif // __OLSR_ROUTE_MANAGER_HH__ xorp/contrib/olsr/test_simulator.cc0000664000076400007640000021276611421137511017676 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8 sw=4: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "olsr_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/callback.hh" #include "libxorp/exceptions.hh" #include "libxorp/ipv4.hh" #include "libxorp/ipv4net.hh" #include "libxorp/status_codes.h" #include "libxorp/service.hh" #include "libxorp/eventloop.hh" #include "libxorp/tokenize.hh" #include "libxorp/test_main.hh" #include "olsr.hh" #include "debug_io.hh" #include "emulate_net.hh" //#include "delay_queue.hh" //#include "vertex.hh" #include "test_args.hh" static const uint16_t MY_OLSR_PORT = 6698; // TODO: Simulation of ETX links, and verification thereof. // TODO: Support verification of HNA metrics and multiple matches. class NoSuchAddress : public XorpReasonedException { public: NoSuchAddress(const char* file, size_t line, const string& init_why = "") : XorpReasonedException("OlsrNoSuchAddress", file, line, init_why) {} }; class Simulator; class NodeTuple { public: NodeTuple() : _id(0), _olsr(0), _io(0) {} explicit NodeTuple(const int id, TestInfo* info, Olsr* olsr, DebugIO* io, const string& ifvifname, const string& instancename) : _id(id), _info(info), _olsr(olsr), _io(io), _ifvifname(ifvifname), _instancename(instancename) {} int id() const { return _id; } TestInfo* info() { return _info; } Olsr* olsr() { return _olsr; } DebugIO* io() { return _io; } string ifname() const { return _ifvifname; } string vifname() const { return _ifvifname; } string instancename() const { return _instancename; } private: int _id; TestInfo* _info; Olsr* _olsr; DebugIO* _io; string _ifvifname; string _instancename; }; class Nodes { public: Nodes(Simulator* r, EventLoop& ev); ~Nodes(); /** * Simulation graph creation. */ /** * Create an OLSR node in the simulator. * * @param addrs the interface addresses to create the node with; * the first address is always the main address. * @return true if ok, false if any error occurred. */ bool create_node(const vector& addrs); /** * Destroy an OLSR node in the simulator. * * @param main_addr the main address of the node to destroy. * @return true if ok, false if any error occurred. */ bool destroy_node(const IPv4& main_addr); /** * Given the main address of an OLSR node, destroy all links * referencing that node. * * @param main_addr The OLSR interface address of the node. * @return the number of links which were destroyed. * @throw NoSuchAddress if link_addr does not exist. */ size_t purge_links_by_node(const IPv4& main_addr) throw(NoSuchAddress); /** * Mark an OLSR node address as administratively up. * * @param iface_addr the interface address to configure up. * @return true if ok, false if any error occurred. */ bool configure_address(const IPv4& iface_addr); /** * Mark an OLSR node address as administratively down. * * @param iface_addr the interface address to configure down. * @return true if ok, false if any error occurred. */ bool unconfigure_address(const IPv4& iface_addr); /* * default protocol variable settings; * don't take effect until a new node is created. */ inline void set_default_hello_interval(const int value) { _default_hello_interval = value; } inline void set_default_mid_interval(const int value) { _default_mid_interval = value; } inline void set_default_mpr_coverage(const int value) { _default_mpr_coverage = value; } inline void set_default_refresh_interval(const int value) { _default_refresh_interval = value; } inline void set_default_tc_interval(const int value) { _default_tc_interval = value; } inline void set_default_hna_interval(const int value) { _default_hna_interval = value; } inline void set_default_dup_hold_time(const int value) { _default_dup_hold_time = value; } inline void set_default_willingness(const int value) { _default_willingness = value; } inline void set_default_tc_redundancy(const int value) { _default_tc_redundancy = value; } /* * per-node settings */ /** * Set HELLO_INTERVAL variable for the node given by main_addr. * * @param main_addr the address of the node to set the variable for. * @param value the new value of the HELLO_INTERVAL protocol variable. * @return true if ok, false if any error occurred. */ bool set_hello_interval(const IPv4& main_addr, const int value); /** * Set MID_INTERVAL variable for the node given by main_addr. * * @param main_addr the address of the node to set the variable for. * @param value the new value of the MID_INTERVAL protocol variable. * @return true if ok, false if any error occurred. */ bool set_mid_interval(const IPv4& main_addr, const int value); /** * Set MPR_COVERAGE variable for the node given by main_addr. * * @param main_addr the address of the node to set the variable for. * @param value the new value of the MPR_COVERAGE protocol variable. * @return true if ok, false if any error occurred. */ bool set_mpr_coverage(const IPv4& main_addr, const int value); /** * Set REFRESH_INTERVAL variable for the node given by main_addr. * * @param main_addr the address of the node to set the variable for. * @param value the new value of the REFRESH_INTERVAL protocol variable. * @return true if ok, false if any error occurred. */ bool set_refresh_interval(const IPv4& main_addr, const int value); /** * Set TC_INTERVAL variable for the node given by main_addr. * * @param main_addr the address of the node to set the variable for. * @param value the new value of the TC_INTERVAL protocol variable. * @return true if ok, false if any error occurred. */ bool set_tc_interval(const IPv4& main_addr, const int value); /** * Set TC_REDUNDANCY variable for the node given by main_addr. * * @param main_addr the address of the node to set the variable for. * @param value the new value of the TC_REDUNDANCY protocol variable. * @return true if ok, false if any error occurred. */ bool set_tc_redundancy(const IPv4& main_addr, const int value); /** * Set WILLINGNESS variable for the node given by main_addr. * * @param main_addr the address of the node to set the variable for. * @param value the new value of the WILLINGNESS protocol variable. * @return true if ok, false if any error occurred. */ bool set_willingness(const IPv4& main_addr, const int value); /** * Set HNA_INTERVAL variable for the node given by main_addr. * * @param main_addr the address of the node to set the variable for. * @param value the new value of the HNA_INTERVAL protocol variable. * @return true if ok, false if any error occurred. */ bool set_hna_interval(const IPv4& main_addr, const int value); /** * Set DUP_HOLD_TIME variable for the node given by main_addr. * * @param main_addr the address of the node to set the variable for. * @param value the new value of the DUP_HOLD_TIME protocol variable. * @return true if ok, false if any error occurred. */ bool set_dup_hold_time(const IPv4& main_addr, const int value); /** * Select the OLSR node with the given main address for the * verification commands which follow. * * @param main_addr the address of the node to select. * @return true if ok, false if any error occurred. */ bool select_node(const IPv4& main_addr); /** * Dump a node's routing table to cout. * * @param main_addr the address of the node to dump routes for. * @return true if ok, false if any error occurred. */ bool dump_routing_table(const IPv4& main_addr); /* * Neighborhood verification */ /** * Verify that the link state database at every node in the topology * is empty (N1/N2/TC/MPR/MID). * HNA state is NOT checked. * * @return true if ok, false if any error occurred. */ bool verify_all_link_state_empty(); /** * Verify that the link state database in the given node * is empty (N1/N2/TC/MPR/MID). * HNA state is NOT checked. * * @param nat the NodeTuple to verify. * @return true if ok, false if any error occurred. */ bool verify_node_link_state_empty(NodeTuple& nat); /** * Verify that the neighbor given by n1_addr is a one-hop neighbor * of the currently selected node. * * @param n1_addr the address of the one-hop neighbor to verify. * @param expected_is_n1 the expected status * @return true if ok, false if any error occurred. */ bool verify_n1(const IPv4& n1_addr, const bool expected_is_n1); /** * Verify that n2_addr is the address of a two-hop neighbor * of the currently selected node. * * @param n2_addr the address of the two-hop neighbor to verify. * @param expected_is_n2 the expected status * @return true if ok, false if any error occurred. */ bool verify_n2(const IPv4& n2_addr, const bool expected_is_n2); /* * MPR verification */ /** * Verify the MPR status of the selected node. * * @param expected_is_mpr the expected MPR status. * @return true if ok, false if any error occurred. */ bool verify_is_mpr(const bool expected_is_mpr); /** * Verify the MPR set of the selected node. * * @param mpr_addrs the expected contents of the MPR set. * @return true if ok, false if any error occurred. */ bool verify_mpr_set(const vector& mpr_addrs); /** * Verify the MPR selector set of the selected node. * * @param mprs_addrs the expected contents of the MPR selector set. * @return true if ok, false if any error occurred. */ bool verify_mpr_selector_set(const vector& mprs_addrs); /* * Topology verification */ /** * Verify the MID database of the selected node contains the * expected number of nodes * (Note: this is NOT the same as entries; rather, it's the * number of unique keys we have in the _mid_addr multimap.) * * @param expected_size the expected dize of the MID table. * @return true if ok, false if any error occurred. */ bool verify_mid_node_count(const size_t expected); /** * Verify the MID database of the selected node contains the given * entries for the given protocol address. * * @param origin the main address to look up in the MID database. * @param addrs the expected addresses for this node; may be empty. * @return true if ok, false if any error occurred. */ bool verify_mid_node_addrs(const IPv4& origin, const vector& addrs); /** * Verify the MID database of the selected node contains an entry * for the given main address and interface address, at the given * network distance measured in hops. * * @param main_addr the main address to look up in the MID database. * @param iface_addr the interface address for the MID entry. * @param expected_distance the expected distance in hops. * @return true if ok, false if any error occurred. */ bool verify_mid_address_distance(const IPv4& origin, const IPv4& iface_addr, const uint16_t expected_distance); /** * Verify the TC database of the selected node contains entries which * have originated from the given number of unique OLSR nodes. * * @param expected_count the expected number of TC origins. * @return true if ok, false if any error occurred. */ bool verify_tc_origin_count(const size_t expected_count); /** * Verify that the Advertised Neighbor Set of 'origin', as seen * at the currently selected node, has the expected ANSN and the * expected neighbor set contents. * * @param origin the origin to look up in the TC database. * @param expected_ansn the ANSN we expect to see. * @param expected_addrs the expected neighbor set contents; * may be empty to indicate set should be empty. * @return true if ok, false if any error occurred. */ bool verify_tc_ans(const IPv4& origin, const uint16_t expected_ansn, const vector& expected_addrs); /** * Verify that the selected node has seen TC broadcasts from 'origin' * at least once during the lifetime of the simulation. * * If it has never been seen then TopologyManager::get_tc_neighbor_set() * will throw an exception -- there is a check which takes place to * look for final recorded ANSN values. * * @param origin the origin to look up in the TC database. * @param expected_seen true if we expect to have seen the origin. * @return true if ok, false if any error occurred. */ bool verify_tc_origin_seen(const IPv4& origin, const bool expected_seen); /** * Verify that the given TC entry has the expected hop count, as observed * at the currently selected node's TC database. * * NOTE: The distance thus observed can never be less than 2, this * accounts for the hop used to reach us, and the hop from the TC's * origin to the advertised neighbor. This is used for the local TC * routing computation. * * @param origin the origin to look up in the TC database. * @param neighbor_addr the neighbor to look up for origin. * @param expected_distance the distance we expect to see recorded * for this TC entry. * @return true if ok, false if any error occurred. */ bool verify_tc_distance(const IPv4& origin, const IPv4& neighbor_addr, const uint16_t expected_distance); /** * Verify that the currently selected node has a given number of * TC entries pointing to the given destination. * * @param addr the destination to look up in the TC database. * @param expected_count the number of TC entries we expect to see. * @return true if ok, false if any error occurred. */ bool verify_tc_destination(const IPv4& dest_addr, const size_t expected_count); /* * External routing verification */ /** * Verify the HNA database of the selected node contains an entry * for the given destination from the given origin. * * @param dest the IPv4 destination to look up in HNA. * @param origin the expected OLSR main address of the origin. * @return true if ok, false if any error occurred. */ bool verify_hna_entry(const IPv4Net& dest, const IPv4& origin); /** * Verify the HNA database of the selected node contains an entry * for the given destination from the given origin, at * the given distance. * * @param dest the IPv4 destination to look up in HNA. * @param origin the expected OLSR main address of the origin. * @param distance the expected distance to the origin, as measured * from the HNA messages themselves. * @return true if ok, false if any error occurred. */ bool verify_hna_distance(const IPv4Net& dest, const IPv4& origin, const uint16_t distance); /** * Verify the HNA database of the selected node contains * exactly the given number of entries. * * @param expected_count the expected number of HNA entries. * @return true if ok, false if any error occurred. */ bool verify_hna_entry_count(const size_t expected_count); /** * Verify the HNA database of the selected node contains entries which * have originated from the given number of unique OLSR nodes. * * @param expected_count the expected number of HNA origins. * @return true if ok, false if any error occurred. */ bool verify_hna_origin_count(const size_t expected_count); /** * Verify the HNA database of the selected node contains the * given number of leanred network prefixes. * * @param expected_count the expected number of HNA prefixes. * @return true if ok, false if any error occurred. */ bool verify_hna_dest_count(const size_t expected_count); /** * Add a prefix to the HNA "routes out" database of the currently * selected node, and start advertising it. * * @param dest the network prefix to originate. * @return true if ok, false if any error occurred. */ bool originate_hna(const IPv4Net& dest); /** * Withdraw a prefix from the HNA "routes out" database of the currently * selected node, and stop advertising it. * * @param dest the network prefix to withdraw. * @return true if ok, false if any error occurred. */ bool withdraw_hna(const IPv4Net& dest); /* * General routing verification */ /** * Verify the routing table of the selected node contains exactly * the given number of entries. * * @param rtsize the expected number of routing table entries. * @return true if ok, false if any error occurred. */ bool verify_routing_table_size(const size_t rtsize); /** * Verify the routing table of the selected node contains the * given route entry. * TODO deal with multiple matches. * * @param dest the destination. * @param nexthop the expected next-hop of the entry. * @param metric the expected metric of the entry. * @return true if ok, false if any error occurred. */ bool verify_routing_entry(IPv4Net dest, IPv4 nexthop, uint32_t metric); /* * Utility methods. */ /** * Given an OLSR protocol address, return the corresponding * NodeTuple instance by value. * * @param addr the OLSR protocol address to look up in the simulator. * @return the NodeTuple corresponding to addr. * @throw NoSuchAddr if the address does not exist. */ NodeTuple& get_node_tuple_by_addr(const IPv4& addr) throw(NoSuchAddress); protected: inline bool node_is_selected() { return (_selected_node.olsr() != 0); } private: Simulator* _parent; EventLoop& _eventloop; /** * @short The next available instance ID for a pair of * OLSR/DebugIO instances. */ int _next_instance_id; /* * protocol defaults */ int _default_hello_interval; int _default_mid_interval; int _default_mpr_coverage; int _default_refresh_interval; int _default_tc_interval; int _default_hna_interval; int _default_dup_hold_time; int _default_willingness; int _default_tc_redundancy; /** * Database of current simulation instances, keyed by protocol * address; one entry for each OLSR address. * Multiple addresses may map to the same OLSR instance (see MID). */ map _nodes; /** * The currently selected node. */ NodeTuple _selected_node; }; class Links { public: /** * @short A structure describing an emulated link between two nodes. */ struct LinkTuple { public: EmulateSubnet* _es; // always a unique instance. NodeTuple _left; NodeTuple _right; explicit LinkTuple( EmulateSubnet* es, const NodeTuple& left, const NodeTuple& right) : _es(es), _left(left), _right(right) {} private: LinkTuple() {} }; public: Links(Simulator* r, EventLoop& ev, Nodes* n) : _parent(r), _eventloop(ev), _nodes(n) {} ~Links() {} /** * Add a link between two OLSR instances, given their addresses. * * If no previously created link exists, a new instance of * EmulateSubnet is created. The link local broadcast address * is always assumed to be 255.255.255.255 on the link. * * @param left_addr The OLSR interface address of the left hand node. * @param right_addr The OLSR interface address of the right hand node. * @return true if the link was successfully created, otherwise false. * @throw NoSuchAddress if neither address exists. */ bool add_link(const IPv4& left_addr, const IPv4& right_addr) throw(NoSuchAddress); /** * Remove a link between two OLSR instances, given their addresses. * * @param left_addr The OLSR interface address of the left hand node. * @param right_addr The OLSR interface address of the right hand node. * @return true if the link was successfully remove, otherwise false. * @throw NoSuchAddress if neither address exists. */ bool remove_link(const IPv4& left_addr, const IPv4& right_addr) throw(NoSuchAddress); /** * Remove all links in the topology, without removing the nodes. * * @return the number of links removed. */ size_t remove_all_links(); /** * Purge a link given a reference to it. * * @param lt reference to a LinkTuple. * @param left_addr The OLSR interface address of the left hand node. * @param right_addr The OLSR interface address of the right hand node. */ void purge_link_tuple(LinkTuple& lt, const IPv4& left_addr, const IPv4& right_addr); /** * Given the interface address of an OLSR node, remove all links * referencing that interface and destroy them. * * @param left_addr The OLSR interface address of the left hand node. * @return true if the link was successfully created, otherwise false. * @throw NoSuchAddress if link_addr does not exist. */ bool remove_all_links_for_addr(const IPv4& link_addr) throw(NoSuchAddress); Simulator* parent() { return _parent; } EventLoop& eventloop() { return _eventloop; } Nodes* nodes() { return _nodes; } private: Simulator* _parent; EventLoop& _eventloop; Nodes* _nodes; multimap, LinkTuple> _links; }; class Simulator { public: Simulator(bool verbose, int verbose_level); ~Simulator(); bool cmd(Args& args) throw(InvalidString); /** * @short Wait @param secs in EventLoop, whilst running other * callbacks. */ void wait(const int secs); inline EventLoop& eventloop() { return _eventloop; } inline TestInfo& info() { return _info; } inline Nodes& nodes() { return _nodes; } inline Links& links() { return _links; } private: // common eventloop must be used for serialization in time domain. EventLoop _eventloop; TestInfo _info; Nodes _nodes; Links _links; }; Simulator::Simulator(bool verbose, int verbose_level) : _info("simulator", verbose, verbose_level, cout), _nodes(this, _eventloop), _links(this, _eventloop, &_nodes) {} Simulator::~Simulator() {} void Simulator::wait(const int secs) { bool timeout = false; XorpTimer t = eventloop().set_flag_after(TimeVal(secs ,0), &timeout); while (! timeout) eventloop().run(); } Nodes::Nodes(Simulator* r, EventLoop& ev) : _parent(r), _eventloop(ev), _next_instance_id(1), _default_hello_interval(1), _default_mid_interval(1), _default_mpr_coverage(1), _default_refresh_interval(1), _default_tc_interval(1), _default_hna_interval(1), _default_dup_hold_time(4), _default_willingness(OlsrTypes::WILL_DEFAULT) { } Nodes::~Nodes() { } bool Nodes::create_node(const vector& addrs) { XLOG_ASSERT(! addrs.empty()); bool result = true; IPv4 main_addr; int instanceid = _next_instance_id++; string instancename = c_format("olsr%u", XORP_UINT_CAST(instanceid)); TestInfo* n_info = new TestInfo(instancename, _parent->info().verbose(), _parent->info().verbose_level(), _parent->info().out()); DebugIO* dio = new DebugIO(*n_info, _eventloop); dio->startup(); Olsr* olsr = new Olsr(_eventloop, dio); /* * apply simulation-wide defaults. */ olsr->set_hello_interval(TimeVal(_default_hello_interval, 0)); olsr->set_mid_interval(TimeVal(_default_mid_interval, 0)); olsr->set_mpr_coverage(_default_mpr_coverage); olsr->set_refresh_interval(TimeVal(_default_refresh_interval, 0)); olsr->set_tc_interval(TimeVal(_default_tc_interval, 0)); olsr->set_hna_interval(TimeVal(_default_hna_interval, 0)); olsr->set_dup_hold_time(TimeVal(_default_dup_hold_time, 0)); olsr->set_willingness(_default_willingness); olsr->set_tc_redundancy(_default_tc_redundancy); size_t face_index = 0; vector::const_iterator ii; for (ii = addrs.begin(); ii != addrs.end(); ii++) { if (ii == addrs.begin()) { main_addr = (*ii); } // create, but do not activate, the interface. string ifvifname = c_format("eth%u", XORP_UINT_CAST(face_index)); const string& ifname = ifvifname; const string& vifname = ifvifname; OlsrTypes::FaceID faceid = olsr->face_manager().create_face(ifname, vifname); olsr->face_manager().set_local_addr(faceid, (*ii)); olsr->face_manager().set_local_port(faceid, MY_OLSR_PORT); olsr->face_manager().set_all_nodes_addr(faceid, IPv4::ALL_ONES()); olsr->face_manager().set_all_nodes_port(faceid, MY_OLSR_PORT); //olsr->set_interface_cost(interface_1, vif_1, interface_cost); // establish a top level mapping for this node in the simulation. // XXX TODO use a list and a map... _nodes.insert(make_pair((*ii), NodeTuple(instanceid, n_info, olsr, dio, ifvifname, instancename))); // now and only now do we activate the interface. result = olsr->face_manager().activate_face(ifname, vifname); // enable the interface. result = olsr->face_manager().set_face_enabled(faceid, true); ++face_index; } olsr->set_main_addr(main_addr); //olsr->trace().all(verbose); //olsr->set_testing(true); return result; } bool Nodes::destroy_node(const IPv4& main_addr) { map::iterator ii = _nodes.find(main_addr); if (ii == _nodes.end()) { return false; } NodeTuple& nat = (*ii).second; delete nat.olsr(); delete nat.io(); delete nat.info(); _nodes.erase(ii); return true; } size_t Nodes::purge_links_by_node(const IPv4& main_addr) throw(NoSuchAddress) { size_t found_count = 0; map::iterator ii = _nodes.find(main_addr); if (ii == _nodes.end()) { return found_count; } // use olsr instance pointer as unique key; // find all NodeTuple such that olsr instance is same; // delete all links referencing each link address. Olsr* olsrp = (*ii).second.olsr(); for (ii = _nodes.begin(); ii != _nodes.end(); ii++) { if ((*ii).second.olsr() == olsrp) { _parent->links().remove_all_links_for_addr((*ii).first); ++found_count; } } return found_count; } bool Nodes::configure_address(const IPv4& iface_addr) { map::iterator ii = _nodes.find(iface_addr); if (ii == _nodes.end()) { return false; } NodeTuple& nat = (*ii).second; Olsr* olsr = nat.olsr(); OlsrTypes::FaceID faceid = olsr->face_manager(). get_faceid(nat.ifname(), nat.vifname()); bool result = olsr->face_manager().set_face_enabled(faceid, true); return result; } bool Nodes::unconfigure_address(const IPv4& iface_addr) { map::iterator ii = _nodes.find(iface_addr); if (ii == _nodes.end()) { return false; } NodeTuple& nat = (*ii).second; Olsr* olsr = nat.olsr(); OlsrTypes::FaceID faceid = olsr->face_manager(). get_faceid(nat.ifname(), nat.vifname()); bool result = olsr->face_manager().set_face_enabled(faceid, false); return result; } bool Nodes::set_hello_interval(const IPv4& main_addr, const int value) { map::iterator ii = _nodes.find(main_addr); if (ii == _nodes.end()) { return false; } NodeTuple& nat = (*ii).second; Olsr* olsr = nat.olsr(); bool result = olsr->set_hello_interval(TimeVal(value, 0)); return result; } bool Nodes::set_mid_interval(const IPv4& main_addr, const int value) { map::iterator ii = _nodes.find(main_addr); if (ii == _nodes.end()) { return false; } NodeTuple& nat = (*ii).second; Olsr* olsr = nat.olsr(); bool result = olsr->set_mid_interval(TimeVal(value, 0)); return result; } bool Nodes::set_mpr_coverage(const IPv4& main_addr, const int value) { map::iterator ii = _nodes.find(main_addr); if (ii == _nodes.end()) { return false; } NodeTuple& nat = (*ii).second; Olsr* olsr = nat.olsr(); bool result = olsr->set_mpr_coverage(value); return result; } bool Nodes::set_refresh_interval(const IPv4& main_addr, const int value) { map::iterator ii = _nodes.find(main_addr); if (ii == _nodes.end()) { return false; } NodeTuple& nat = (*ii).second; Olsr* olsr = nat.olsr(); bool result = olsr->set_refresh_interval(TimeVal(value, 0)); return result; } bool Nodes::set_tc_interval(const IPv4& main_addr, const int value) { map::iterator ii = _nodes.find(main_addr); if (ii == _nodes.end()) { return false; } NodeTuple& nat = (*ii).second; Olsr* olsr = nat.olsr(); bool result = olsr->set_tc_interval(TimeVal(value, 0)); return result; } bool Nodes::set_tc_redundancy(const IPv4& main_addr, const int value) { map::iterator ii = _nodes.find(main_addr); if (ii == _nodes.end()) { return false; } NodeTuple& nat = (*ii).second; Olsr* olsr = nat.olsr(); bool result = olsr->set_tc_redundancy(value); return result; } bool Nodes::set_willingness(const IPv4& main_addr, const int value) { map::iterator ii = _nodes.find(main_addr); if (ii == _nodes.end()) { return false; } NodeTuple& nat = (*ii).second; Olsr* olsr = nat.olsr(); bool result = olsr->set_willingness(value); return result; } bool Nodes::set_hna_interval(const IPv4& main_addr, const int value) { map::iterator ii = _nodes.find(main_addr); if (ii == _nodes.end()) { return false; } NodeTuple& nat = (*ii).second; Olsr* olsr = nat.olsr(); bool result = olsr->set_hna_interval(TimeVal(value, 0)); return result; } bool Nodes::set_dup_hold_time(const IPv4& main_addr, const int value) { map::iterator ii = _nodes.find(main_addr); if (ii == _nodes.end()) { return false; } NodeTuple& nat = (*ii).second; Olsr* olsr = nat.olsr(); bool result = olsr->set_dup_hold_time(TimeVal(value, 0)); return result; } bool Nodes::select_node(const IPv4& main_addr) { map::iterator ii = _nodes.find(main_addr); if (ii == _nodes.end()) { return false; } NodeTuple& nat = (*ii).second; _selected_node = nat; return true; } bool Nodes::dump_routing_table(const IPv4& main_addr) { map::iterator ii = _nodes.find(main_addr); if (ii == _nodes.end()) { return false; } NodeTuple& nat = (*ii).second; DebugIO* io = nat.io(); cout << "{{{ Routing table at " << main_addr.str() << ":" << endl; io->routing_table_dump(cout); cout << "}}}" << endl; return true; } bool Nodes::verify_all_link_state_empty() { bool all_empty = true; map::iterator ii; for (ii = _nodes.begin(); ii != _nodes.end(); ii++) { NodeTuple& nat = (*ii).second; if (! verify_node_link_state_empty(nat)) { all_empty = false; break; } } return all_empty; } bool Nodes::verify_node_link_state_empty(NodeTuple& nat) { Olsr* olsr = nat.olsr(); XLOG_ASSERT(olsr != 0); DebugIO* io = nat.io(); Neighborhood& nh = olsr->neighborhood(); TopologyManager& tm = olsr->topology_manager(); // XXX hodgepodge. NodeTuple save_selected = _selected_node; _selected_node = nat; bool is_link_state_empty = false; do { // N1 database must be empty. list n1_list; nh.get_neighbor_list(n1_list); if (! n1_list.empty()) break; // N2 database must be empty. list n2_list; nh.get_twohop_neighbor_list(n2_list); if (! n2_list.empty()) break; // MPR set must be empty. vector mpr_addrs; if (! verify_mpr_set(mpr_addrs)) { debug_msg("non-zero MPR set\n"); break; } // MPR selector set must be empty. vector mprs_addrs; if (! verify_mpr_selector_set(mpr_addrs)) { debug_msg("non-zero MPR selector set\n"); break; } // MID origin count must be 0. if (0 != tm.mid_node_count()) { debug_msg("non-zero MID origin count\n"); break; } // TC origin count must be 0. if (0 != tm.tc_node_count()) { debug_msg("non-zero TC origin count\n"); break; } // Routing table size must be 0. if (0 != io->routing_table_size()) { debug_msg("non-zero routing table size\n"); break; } is_link_state_empty = true; } while (0); _selected_node = save_selected; return is_link_state_empty; } bool Nodes::verify_n1(const IPv4& n1_addr, const bool expected_is_n1) { XLOG_ASSERT(node_is_selected()); NodeTuple& nat = _selected_node; Olsr* olsr = nat.olsr(); bool is_n1 = false; try { OlsrTypes::NeighborID nid = olsr->neighborhood(). get_neighborid_by_main_addr(n1_addr); // TODO: Should we verify if it's sym or not? UNUSED(nid); is_n1 = true; } catch (...) {} return is_n1 == expected_is_n1; } bool Nodes::verify_n2(const IPv4& n2_addr, const bool expected_is_n2) { XLOG_ASSERT(node_is_selected()); NodeTuple& nat = _selected_node; Olsr* olsr = nat.olsr(); bool is_n2 = false; try { OlsrTypes::TwoHopNodeID tnid = olsr->neighborhood(). get_twohop_nodeid_by_main_addr(n2_addr); // TODO: should we look at other properties of this n2? UNUSED(tnid); is_n2 = true; } catch (...) {} return is_n2 == expected_is_n2; } // shorthand for "verify that my mpr selector set is empty". bool Nodes::verify_is_mpr(const bool expected_is_mpr) { XLOG_ASSERT(node_is_selected()); NodeTuple& nat = _selected_node; Olsr* olsr = nat.olsr(); bool is_mpr = olsr->neighborhood().is_mpr(); return is_mpr == expected_is_mpr; } bool Nodes::verify_mpr_set(const vector& mpr_addrs) { XLOG_ASSERT(node_is_selected()); NodeTuple& nat = _selected_node; Olsr* olsr = nat.olsr(); // NOTE: If for any reason we race with MPR set computation // then this test is invalid. We shouldn't because MPR computation // takes place strictly within a XorpCallback which can't be // preempted by other tasks, and state is updated idempotently. // // Verify MPR set by direct set comparison. A true invariant. // bool result = false; set expected_mpr_set; // Look up runtime neighbor IDs in selected OLSR instance // for each provided MPR address and build comparison set. vector::const_iterator ii; for (ii = mpr_addrs.begin(); ii != mpr_addrs.end(); ii++) { try { OlsrTypes::NeighborID nid = olsr->neighborhood(). get_neighborid_by_main_addr((*ii)); expected_mpr_set.insert(nid); } catch (...) { throw; } } // Evaluate the invariant by testing for set equality. result = (olsr->neighborhood().mpr_set() == expected_mpr_set); return result; } bool Nodes::verify_mpr_selector_set(const vector& mprs_addrs) { XLOG_ASSERT(node_is_selected()); NodeTuple& nat = _selected_node; Olsr* olsr = nat.olsr(); // // Verify MPR selector set, the long way round: a full lookup // and comparison of each member (a true invariant). // [MPR selector state is always in phase with HELLO receive processing.] // bool result = false; set expected_mpr_selector_set; // Look up runtime neighbor IDs in selected OLSR instance // for each provided MPR selector address and build comparison set. vector::const_iterator ii; for (ii = mprs_addrs.begin(); ii != mprs_addrs.end(); ii++) { try { OlsrTypes::NeighborID nid = olsr->neighborhood(). get_neighborid_by_main_addr((*ii)); expected_mpr_selector_set.insert(nid); } catch (...) { throw; } } // Evaluate the invariant by testing for set equality. result = (olsr->neighborhood().mpr_selector_set() == expected_mpr_selector_set); return result; } bool Nodes::verify_mid_node_count(const size_t expected) { XLOG_ASSERT(node_is_selected()); NodeTuple& nat = _selected_node; Olsr* olsr = nat.olsr(); size_t actual = olsr->topology_manager().mid_node_count(); if (expected == actual) return true; debug_msg("expected %u, got %u\n", XORP_UINT_CAST(expected), XORP_UINT_CAST(actual)); return false; } bool Nodes::verify_mid_node_addrs(const IPv4& origin, const vector& addrs) { XLOG_ASSERT(node_is_selected()); NodeTuple& nat = _selected_node; Olsr* olsr = nat.olsr(); bool is_identical = false; try { vector actual_addrs = olsr->topology_manager(). get_mid_addresses(origin); is_identical = (addrs == actual_addrs); } catch(...) {} return is_identical; } bool Nodes::verify_mid_address_distance(const IPv4& origin, const IPv4& iface_addr, const uint16_t expected_distance) { XLOG_ASSERT(node_is_selected()); NodeTuple& nat = _selected_node; Olsr* olsr = nat.olsr(); bool is_identical = false; try { uint16_t actual_distance = olsr->topology_manager(). get_mid_address_distance(origin, iface_addr); is_identical = (expected_distance == actual_distance); if (! is_identical) { debug_msg("expected %u, got %u\n", XORP_UINT_CAST(expected_distance), XORP_UINT_CAST(actual_distance)); } } catch(...) {} return is_identical; } bool Nodes::verify_hna_entry(const IPv4Net& dest, const IPv4& origin) { XLOG_ASSERT(node_is_selected()); NodeTuple& nat = _selected_node; Olsr* olsr = nat.olsr(); ExternalRoutes& er = olsr->external_routes(); bool is_found = false; try { OlsrTypes::ExternalID erid = er.get_hna_route_in_id(dest, origin); is_found = true; UNUSED(erid); } catch (...) {} return is_found; } bool Nodes::verify_hna_distance(const IPv4Net& dest, const IPv4& origin, const uint16_t distance) { XLOG_ASSERT(node_is_selected()); NodeTuple& nat = _selected_node; Olsr* olsr = nat.olsr(); ExternalRoutes& er = olsr->external_routes(); bool is_as_expected = false; try { const ExternalRoute* e = er.get_hna_route_in(dest, origin); if (e->distance() == distance) is_as_expected = true; } catch (...) {} return is_as_expected; } bool Nodes::verify_hna_entry_count(const size_t expected_count) { XLOG_ASSERT(node_is_selected()); NodeTuple& nat = _selected_node; Olsr* olsr = nat.olsr(); ExternalRoutes& er = olsr->external_routes(); size_t actual_hna_entry_count = er.hna_entry_count(); if (expected_count == actual_hna_entry_count) return true; debug_msg("expected %u, got %u\n", XORP_UINT_CAST(expected_count), XORP_UINT_CAST(actual_hna_entry_count)); return false; } bool Nodes::verify_hna_origin_count(const size_t expected_count) { XLOG_ASSERT(node_is_selected()); NodeTuple& nat = _selected_node; Olsr* olsr = nat.olsr(); ExternalRoutes& er = olsr->external_routes(); size_t actual_hna_origin_count = er.hna_origin_count(); if (expected_count == actual_hna_origin_count) return true; debug_msg("expected %u, got %u\n", XORP_UINT_CAST(expected_count), XORP_UINT_CAST(actual_hna_origin_count)); return false; } bool Nodes::verify_hna_dest_count(const size_t expected_count) { XLOG_ASSERT(node_is_selected()); NodeTuple& nat = _selected_node; Olsr* olsr = nat.olsr(); ExternalRoutes& er = olsr->external_routes(); size_t actual_hna_dest_count = er.hna_dest_count(); if (expected_count == actual_hna_dest_count) return true; debug_msg("expected %u, got %u\n", XORP_UINT_CAST(expected_count), XORP_UINT_CAST(actual_hna_dest_count)); return false; } bool Nodes::originate_hna(const IPv4Net& dest) { XLOG_ASSERT(node_is_selected()); NodeTuple& nat = _selected_node; Olsr* olsr = nat.olsr(); ExternalRoutes& er = olsr->external_routes(); bool is_originated = false; try { is_originated = er.originate_hna_route_out(dest); } catch (...) {} return is_originated; } bool Nodes::withdraw_hna(const IPv4Net& dest) { XLOG_ASSERT(node_is_selected()); NodeTuple& nat = _selected_node; Olsr* olsr = nat.olsr(); ExternalRoutes& er = olsr->external_routes(); bool is_withdrawn = false; try { er.withdraw_hna_route_out(dest); is_withdrawn = true; } catch (...) {} return is_withdrawn; } bool Nodes::verify_tc_origin_count(const size_t expected_count) { XLOG_ASSERT(node_is_selected()); NodeTuple& nat = _selected_node; Olsr* olsr = nat.olsr(); TopologyManager& tm = olsr->topology_manager(); size_t actual_tc_node_count = tm.tc_node_count(); if (expected_count == actual_tc_node_count) return true; debug_msg("expected %u, got %u\n", XORP_UINT_CAST(expected_count), XORP_UINT_CAST(actual_tc_node_count)); return false; } // verify advertised neighbor set from given origin // has ANSN of 'expected_ansn' and matches contents of addrs. bool Nodes::verify_tc_ans(const IPv4& origin, const uint16_t expected_ansn, const vector& expected_addrs) { XLOG_ASSERT(node_is_selected()); NodeTuple& nat = _selected_node; Olsr* olsr = nat.olsr(); TopologyManager& tm = olsr->topology_manager(); uint16_t actual_ansn; vector actual_addrs = tm.get_tc_neighbor_set(origin, actual_ansn); if (expected_ansn == actual_ansn) { if (expected_addrs == actual_addrs) { return true; } else { debug_msg("address list mismatch\n"); } } else { debug_msg("ansn mismatch: expected %u, got %u\n", XORP_UINT_CAST(expected_ansn), XORP_UINT_CAST(actual_ansn)); } return false; } bool Nodes::verify_tc_origin_seen(const IPv4& origin, const bool expected_seen) { XLOG_ASSERT(node_is_selected()); NodeTuple& nat = _selected_node; Olsr* olsr = nat.olsr(); TopologyManager& tm = olsr->topology_manager(); // Try and look up the origin's ANS. If we can't find the // origin at all, then we know we either never saw it, // or it has completely timed out and ANSN was valid. bool actual_seen = false; try { uint16_t ansn; vector addrs = tm.get_tc_neighbor_set(origin, ansn); debug_msg("ansn %u with %sempty ans.\n", ansn, addrs.empty() ? "" : "non"); actual_seen = true; UNUSED(ansn); UNUSED(addrs); } catch (BadTopologyEntry bte) { debug_msg("BadTopologyEntry caught\n"); } if (expected_seen == actual_seen) return true; debug_msg("expected %s, actual %s\n", bool_c_str(expected_seen), bool_c_str(actual_seen)); return false; } bool Nodes::verify_tc_distance(const IPv4& origin, const IPv4& neighbor_addr, const uint16_t expected_distance) { XLOG_ASSERT(node_is_selected()); NodeTuple& nat = _selected_node; Olsr* olsr = nat.olsr(); TopologyManager& tm = olsr->topology_manager(); try { uint16_t actual_distance = tm.get_tc_distance(origin, neighbor_addr); if (expected_distance == actual_distance) return true; debug_msg("expected %u, actual %u\n", XORP_UINT_CAST(expected_distance), XORP_UINT_CAST(actual_distance)); } catch (...) {} return false; } bool Nodes::verify_tc_destination(const IPv4& dest_addr, const size_t expected_count) { XLOG_ASSERT(node_is_selected()); NodeTuple& nat = _selected_node; Olsr* olsr = nat.olsr(); TopologyManager& tm = olsr->topology_manager(); try { size_t actual_count = tm.get_tc_lasthop_count_by_dest(dest_addr); if (expected_count == actual_count) return true; } catch (...) {} return false; } bool Nodes::verify_routing_table_size(const size_t rtsize) { XLOG_ASSERT(node_is_selected()); NodeTuple& nat = _selected_node; DebugIO* io = nat.io(); size_t actual_rtsize = io->routing_table_size(); if (rtsize == actual_rtsize) return true; debug_msg("expected %u, got %u\n", XORP_UINT_CAST(rtsize), XORP_UINT_CAST(actual_rtsize)); return false; } bool Nodes::verify_routing_entry(IPv4Net net, IPv4 nexthop, uint32_t metric) { XLOG_ASSERT(node_is_selected()); NodeTuple& nat = _selected_node; DebugIO* io = nat.io(); bool result = io->routing_table_verify(net, nexthop, metric); return result; } NodeTuple& Nodes::get_node_tuple_by_addr(const IPv4& addr) throw(NoSuchAddress) { map::iterator ii = _nodes.find(addr); if (ii == _nodes.end()) { xorp_throw(NoSuchAddress, c_format("address %s not found in simulation", cstring(addr))); } NodeTuple& nat = (*ii).second; return nat; } bool Links::add_link(const IPv4& left_addr, const IPv4& right_addr) throw(NoSuchAddress) { // Check if mapping already exists for address pair. multimap, LinkTuple>::iterator ii = _links.find(make_pair(left_addr, right_addr)); if (ii != _links.end()) { xorp_throw(NoSuchAddress, c_format("Link between %s and %s already exists", cstring(left_addr), cstring(right_addr))); return false; } NodeTuple& left = _parent->nodes().get_node_tuple_by_addr(left_addr); NodeTuple& right = _parent->nodes().get_node_tuple_by_addr(right_addr); EmulateSubnet* es = new EmulateSubnet(_parent->info(), _eventloop); // TODO: Make the address/instance representation less wasteful... // and promote LinkTuple to a class like it should be. es->bind_interface(left.instancename(), left.ifname(), left.vifname(), left_addr, MY_OLSR_PORT, *left.io()); es->bind_interface(right.instancename(), right.ifname(), right.vifname(), right_addr, MY_OLSR_PORT, *right.io()); debug_msg("add_link %p %s %s\n", es, cstring(left_addr), cstring(right_addr)); _links.insert(make_pair(make_pair(left_addr, right_addr), LinkTuple(es, left, right))); return true; } void Links::purge_link_tuple(LinkTuple& lt, const IPv4& left_addr, const IPv4& right_addr) { EmulateSubnet* es = lt._es; NodeTuple& left = lt._left; es->unbind_interface(left.instancename(), left.ifname(), left.vifname(), left_addr, MY_OLSR_PORT, *left.io()); NodeTuple& right = lt._right; es->unbind_interface(right.instancename(), right.ifname(), right.vifname(), right_addr, MY_OLSR_PORT, *right.io()); delete es; } size_t Links::remove_all_links() { size_t nlinks = 0; multimap, LinkTuple>::iterator ii; while (! _links.empty()) { ii =_links.begin(); // A tuple of tuples ((l,r),lt) IPv4 left_addr = (*ii).first.first; IPv4 right_addr = (*ii).first.second; purge_link_tuple((*ii).second, left_addr, right_addr); _links.erase(ii); nlinks++; } return nlinks; } bool Links::remove_link(const IPv4& left_addr, const IPv4& right_addr) throw(NoSuchAddress) { // Check if mapping exists for address pair. multimap, LinkTuple>::iterator ii = _links.find(make_pair(left_addr, right_addr)); if (ii == _links.end()) { return false; // nothing to delete. } purge_link_tuple((*ii).second, left_addr, right_addr); _links.erase(ii); return true; } // remove all links which contain link_addr. bool Links::remove_all_links_for_addr(const IPv4& link_addr) throw(NoSuchAddress) { size_t found_count = 0; multimap, LinkTuple>::iterator ii; for (ii = _links.begin(); ii != _links.end(); ii++) { // If link appears in either link tuple for all links // in system, zap it. get the arguments the right way round. if (link_addr == (*ii).first.first) { ++found_count; purge_link_tuple((*ii).second, link_addr, (*ii).first.second); _links.erase(ii); } else if (link_addr == (*ii).first.second) { ++found_count; purge_link_tuple((*ii).second, (*ii).first.first, link_addr); _links.erase(ii); } } if (found_count == 0) { xorp_throw(NoSuchAddress, c_format("address %s not found in simulated links" , cstring(link_addr))); } return (found_count != 0); } bool Simulator::cmd(Args& args) throw(InvalidString) { string word; while (args.get_next(word)) { if (_info.verbose()) { cout << "[" << word << "]" << endl; cout << args.original_line() << endl; } if ("#" == word.substr(0,1)) { // CMD: # return true; } else if ("echo" == word) { // CMD: echo if (! _info.verbose()) { cout << args.original_line() << endl; } return true; } else if ("exit" == word) { // CMD: exit return false; } else if ("dump_routing_table" == word) { // CMD: dump_routing_table IPv4 main_addr = get_next_ipv4(args, "dump_routing_table"); if (! nodes().dump_routing_table(main_addr)) { xorp_throw(InvalidString, c_format("Failed to dump table for node <%s> [%s]", cstring(main_addr), args.original_line().c_str())); } } else if ("add_link" == word) { // CMD: add_link IPv4 left_addr = get_next_ipv4(args, "add_link"); IPv4 right_addr = get_next_ipv4(args, "add_link"); if (! links().add_link(left_addr, right_addr)) { xorp_throw(InvalidString, c_format("Failed to create link %s %s [%s]", cstring(left_addr), cstring(right_addr), args.original_line().c_str())); } } else if ("remove_all_links" == word) { // CMD: remove_all_links size_t nlinks = links().remove_all_links(); debug_msg("Removed %u links.\n", XORP_UINT_CAST(nlinks)); } else if ("remove_link" == word) { // CMD: remove_link IPv4 left_addr = get_next_ipv4(args, "remove_link"); IPv4 right_addr = get_next_ipv4(args, "remove_link"); if (! links().remove_link(left_addr, right_addr)) { xorp_throw(InvalidString, c_format("Failed to remove link %s %s [%s]", cstring(left_addr), cstring(right_addr), args.original_line().c_str())); } } else if ("create" == word) { // CMD: create [link-address-1] .. // all interfaces are bound and configured up by default. vector addrs; IPv4 main_addr = get_next_ipv4(args, "create"); addrs.push_back(main_addr); do { IPv4 addr; try { addrs.push_back(get_next_ipv4(args, "create")); } catch (InvalidString is) { break; } } while (true); if (! nodes().create_node(addrs)) { xorp_throw(InvalidString, c_format("Failed to create node <%s> [%s]", cstring(addrs[0]), args.original_line().c_str())); } } else if ("destroy" == word) { // CMD: destroy IPv4 main_addr = get_next_ipv4(args, "destroy"); if (! nodes().destroy_node(main_addr)) { xorp_throw(InvalidString, c_format("Failed to destroy node <%s> [%s]", cstring(main_addr), args.original_line().c_str())); } } else if ("face_up" == word) { // CMD: face_up IPv4 link_addr = get_next_ipv4(args, "face_up"); if (! nodes().configure_address(link_addr)) { xorp_throw(InvalidString, c_format("Failed to configure address <%s> [%s]", cstring(link_addr), args.original_line().c_str())); } } else if ("face_down" == word) { // CMD: face_down IPv4 link_addr = get_next_ipv4(args, "face_down"); if (! nodes().unconfigure_address(link_addr)) { xorp_throw(InvalidString, c_format("Failed to unconfigure address <%s> [%s]", cstring(link_addr), args.original_line().c_str())); } } else if ("set_default_hello_interval" == word) { // CMD: set_default_hello_interval int value = get_next_number(args, "set_default_hello_interval"); nodes().set_default_hello_interval(value); } else if ("set_default_mid_interval" == word) { // CMD: set_default_mid_interval int value = get_next_number(args, "set_default_mid_interval"); nodes().set_default_mid_interval(value); } else if ("set_default_mpr_coverage" == word) { // CMD: set_default_mpr_coverage int value = get_next_number(args, "set_default_mpr_coverage"); nodes().set_default_mpr_coverage(value); } else if ("set_default_refresh_interval" == word) { // CMD: set_default_refresh_interval int value = get_next_number(args, "set_default_refresh_interval"); nodes().set_default_refresh_interval(value); } else if ("set_default_tc_interval" == word) { // CMD: set_default_tc_interval int value = get_next_number(args, "set_default_tc_interval"); nodes().set_default_tc_interval(value); } else if ("set_default_willingness" == word) { // CMD: set_default_willingness int value = get_next_number(args, "set_default_willingness"); nodes().set_default_willingness(value); } else if ("set_default_tc_redundancy" == word) { // CMD: set_default_tc_redundancy int value = get_next_number(args, "set_default_tc_redundancy"); nodes().set_default_tc_redundancy(value); } else if ("set_hello_interval" == word) { // CMD: set_hello_interval IPv4 main_addr = get_next_ipv4(args, "set_hello_interval"); int value = get_next_number(args, "set_hello_interval"); if (! nodes().set_hello_interval(main_addr, value)) { xorp_throw(InvalidString, c_format("Failed to set hello interval [%s]", args.original_line().c_str())); } } else if ("set_mpr_coverage" == word) { // CMD: set_mpr_coverage IPv4 main_addr = get_next_ipv4(args, "set_mpr_coverage"); int value = get_next_number(args, "set_mpr_coverage"); if (! nodes().set_mpr_coverage(main_addr, value)) { xorp_throw(InvalidString, c_format("Failed to set coverage <%s> <%d> [%s]", cstring(main_addr), value, args.original_line().c_str())); } } else if ("set_mid_interval" == word) { // CMD: set_mid_interval IPv4 main_addr = get_next_ipv4(args, "set_mid_interval"); int value = get_next_number(args, "set_mid_interval"); if (! nodes().set_mid_interval(main_addr, value)) { xorp_throw(InvalidString, c_format("Failed to set mid interval [%s]", args.original_line().c_str())); } } else if ("set_refresh_interval" == word) { // CMD: set_refresh_interval IPv4 main_addr = get_next_ipv4(args, "set_refresh_interval"); int value = get_next_number(args, "set_refresh_interval"); if (! nodes().set_refresh_interval(main_addr, value)) { xorp_throw(InvalidString, c_format("Failed to set refresh interval [%s]", args.original_line().c_str())); } } else if ("set_tc_interval" == word) { // CMD: set_tc_interval IPv4 main_addr = get_next_ipv4(args, "set_tc_interval"); int value = get_next_number(args, "set_tc_interval"); if (! nodes().set_tc_interval(main_addr, value)) { xorp_throw(InvalidString, c_format("Failed to set TC interval [%s]", args.original_line().c_str())); } } else if ("set_willingness" == word) { // CMD: set_willingness IPv4 main_addr = get_next_ipv4(args, "set_willingness"); int value = get_next_number(args, "set_willingness"); if (! nodes().set_willingness(main_addr, value)) { xorp_throw(InvalidString, c_format("Failed to set willingness <%s> <%d> [%s]", cstring(main_addr), value, args.original_line().c_str())); } } else if ("select" == word) { // CMD: select IPv4 main_addr = get_next_ipv4(args, "select"); if (! nodes().select_node(main_addr)) { xorp_throw(InvalidString, c_format("Failed to select node <%s> [%s]", cstring(main_addr), args.original_line().c_str())); } } else if ("verify_all_link_state_empty" == word) { // CMD: verify_all_link_state_empty if (! nodes().verify_all_link_state_empty()) { xorp_throw(InvalidString, "Failed to verify that link-state " "databases are empty at every node."); } } else if ("verify_n1" == word) { // CMD: verify_n1 IPv4 n1_addr = get_next_ipv4(args, "verify_n1"); bool value = get_next_bool(args, "verify_n1"); if (! nodes().verify_n1(n1_addr, value)) { xorp_throw(InvalidString, c_format("Failed to verify neighbor <%s> [%s]", cstring(n1_addr), args.original_line().c_str())); } } else if ("verify_n2" == word) { // CMD: verify_n2 IPv4 n2_addr = get_next_ipv4(args, "verify_n2"); bool value = get_next_bool(args, "verify_n1"); if (! nodes().verify_n2(n2_addr, value)) { xorp_throw(InvalidString, c_format("Failed to verify two-hop <%s> [%s]", cstring(n2_addr), args.original_line().c_str())); } } else if ("verify_is_mpr" == word) { // CMD: verify_is_mpr bool expected_mpr_state = get_next_bool(args, "verify_is_mpr"); if (! nodes().verify_is_mpr(expected_mpr_state)) { xorp_throw(InvalidString, c_format("Failed to verify MPR status [%s]", args.original_line().c_str())); } } else if ("verify_mpr_set" == word) { // CMD: verify_mpr_set empty // | verify_mpr_set ... bool has_addrs = false; // true if second form of command. vector mpr_addrs; do { try { IPv4 addr = get_next_ipv4(args, "verify_mpr_set"); mpr_addrs.push_back(addr); has_addrs = true; } catch (InvalidString is) { // If we already saw addresses, OK to stop parsing. if (has_addrs) break; // Re-throw exception if no string present, or if // first word is not "empty". args.push_back(); string emptyword = get_next_word(args, "verify_mpr_set"); if ("empty" == emptyword) break; else throw; } } while (true); // has_addrs is false if and only if "empty" was specified. if (has_addrs && mpr_addrs.empty()) { xorp_throw(InvalidString, c_format("No valid addresses [%s]", args.original_line().c_str())); } if (! nodes().verify_mpr_set(mpr_addrs)) { xorp_throw(InvalidString, c_format("Failed to verify MPR set [%s]", args.original_line().c_str())); } } else if ("verify_mpr_selector_set" == word) { // CMD: verify_mpr_selector_set empty // | verify_mpr_selector_set ... bool has_addrs = false; // true if second form of command. vector mprs_addrs; do { try { IPv4 addr = get_next_ipv4(args, "verify_mpr_selector_set"); mprs_addrs.push_back(addr); has_addrs = true; } catch (InvalidString is) { // If we already saw addresses, OK to stop parsing. if (has_addrs) break; // Re-throw exception if no string present, or if // first word is not "empty". args.push_back(); string emptyword = get_next_word(args, "verify_mpr_selector_set"); if ("empty" == emptyword) break; else throw; } } while (true); // has_addrs is false if and only if "empty" was specified. if (has_addrs && mprs_addrs.empty()) { xorp_throw(InvalidString, c_format("No valid addresses [%s]", args.original_line().c_str())); } if (! nodes().verify_mpr_selector_set(mprs_addrs)) { xorp_throw(InvalidString, c_format("Failed to verify MPR selector set [%s]", args.original_line().c_str())); } } else if ("verify_routing_table_size" == word) { // CMD: verify_routing_table_size int expected = get_next_number(args, "verify_routing_table_size"); if (! nodes().verify_routing_table_size(expected)) { xorp_throw(InvalidString, c_format("Failed to verify table size <%d> [%s]", expected, args.original_line().c_str())); } } else if ("verify_routing_entry" == word) { // CMD: verify_routing_entry IPv4Net dest = get_next_ipv4_net(args, "verify_routing_entry"); IPv4 nexthop = get_next_ipv4(args, "verify_routing_entry"); int metric = get_next_number(args, "verify_routing_entry"); if (! nodes().verify_routing_entry(dest, nexthop, metric)) { xorp_throw(InvalidString, c_format("Failed to verify table entry [%s]", args.original_line().c_str())); } } else if ("verify_mid_node_count" == word) { // CMD: verify_mid_node_count int expected = get_next_number(args, "verify_mid_node_count"); if (! nodes().verify_mid_node_count(expected)) { xorp_throw(InvalidString, c_format("Failed to verify MID count <%d> [%s]", expected, args.original_line().c_str())); } } else if ("verify_mid_node_addrs" == word) { // CMD: verify_mid_node_addrs empty // | verify_mid_node_addrs ... IPv4 mid_origin = get_next_ipv4(args, "verify_mid_node_addrs"); bool has_addrs = false; // true if second form of command. vector mid_addrs; do { try { IPv4 addr = get_next_ipv4(args, "verify_mid_node_addrs"); mid_addrs.push_back(addr); has_addrs = true; } catch (InvalidString is) { // If we already saw addresses, OK to stop parsing. if (has_addrs) break; // Re-throw exception if no string present, or if // first word is not "empty". args.push_back(); string emptyword = get_next_word(args, "verify_mid_node_addrs"); if ("empty" == emptyword) break; else throw; } } while (true); // has_addrs is false if and only if "empty" was specified. if (has_addrs && mid_addrs.empty()) { xorp_throw(InvalidString, c_format("No valid addresses [%s]", args.original_line().c_str())); } if (! nodes().verify_mid_node_addrs(mid_origin, mid_addrs)) { xorp_throw(InvalidString, c_format("Failed to verify MID node [%s]", args.original_line().c_str())); } } else if ("verify_mid_distance" == word) { // CMD: verify_mid_distance IPv4 origin = get_next_ipv4(args, "verify_mid_distance"); IPv4 iface_addr = get_next_ipv4(args, "verify_mid_distance"); int distance = get_next_number(args, "verify_mid_distance"); if (! nodes().verify_mid_address_distance(origin, iface_addr, distance)) { xorp_throw(InvalidString, c_format("Failed to verify MID distance [%s]", args.original_line().c_str())); } } else if ("verify_tc_origin_count" == word) { // CMD: verify_tc_origin_count int count = get_next_number(args, "verify_tc_origin_count"); if (! nodes().verify_tc_origin_count(count)) { xorp_throw(InvalidString, c_format("Failed to verify TC origin count [%s]", args.original_line().c_str())); } } else if ("verify_tc_ans" == word) { // CMD: verify_tc_ans empty // | verify_tc_ans ... IPv4 origin = get_next_ipv4(args, "verify_tc_ans"); uint16_t ansn = get_next_number(args, "verify_tc_ans"); vector addrs; bool has_addrs = false; // true if second form of command. do { try { IPv4 addr = get_next_ipv4(args, "verify_tc_ans"); addrs.push_back(addr); has_addrs = true; } catch (InvalidString is) { // If we already saw addresses, OK to stop parsing. if (has_addrs) break; // Re-throw exception if no string present, or if // first word is not "empty". args.push_back(); string emptyword = get_next_word(args, "verify_tc_ans"); if ("empty" == emptyword) break; else throw; } } while (true); if (! nodes().verify_tc_ans(origin, ansn, addrs)) { xorp_throw(InvalidString, c_format("Failed to verify TC advertised set [%s]", args.original_line().c_str())); } } else if ("verify_tc_origin_seen" == word) { // CMD: verify_tc_origin_seen IPv4 origin = get_next_ipv4(args, "verify_tc_origin_seen"); bool value = get_next_bool(args, "verify_tc_origin_seen"); if (! nodes().verify_tc_origin_seen(origin, value)) { xorp_throw(InvalidString, c_format("Failed to verify TC origin seen [%s]", args.original_line().c_str())); } } else if ("verify_tc_distance" == word) { // CMD: verify_tc_distance IPv4 origin = get_next_ipv4(args, "verify_tc_distance"); IPv4 addr = get_next_ipv4(args, "verify_tc_distance"); uint16_t distance = get_next_number(args, "verify_tc_distance"); if (! nodes().verify_tc_distance(origin, addr, distance)) { xorp_throw(InvalidString, c_format("Failed to verify TC distance [%s]", args.original_line().c_str())); } } else if ("verify_tc_destination" == word) { // CMD: verify_tc_destination IPv4 addr = get_next_ipv4(args, "verify_tc_destination"); size_t count = get_next_number(args, "verify_tc_destination"); if (! nodes().verify_tc_destination(addr, count)) { xorp_throw(InvalidString, c_format("Failed to verify TC destination [%s]", args.original_line().c_str())); } } else if ("verify_hna_entry" == word) { // CMD: verify_hna_entry IPv4Net dest = get_next_ipv4_net(args, "verify_hna_entry"); IPv4 origin = get_next_ipv4(args, "verify_hna_entry"); if (! nodes().verify_hna_entry(dest, origin)) { xorp_throw(InvalidString, c_format("Failed to verify HNA entry [%s]", args.original_line().c_str())); } } else if ("verify_hna_distance" == word) { // CMD: verify_hna_distance IPv4Net dest = get_next_ipv4_net(args, "verify_hna_distance"); IPv4 origin = get_next_ipv4(args, "verify_hna_distance"); uint16_t distance = get_next_number(args, "verify_hna_distance"); if (! nodes().verify_hna_distance(dest, origin, distance)) { xorp_throw(InvalidString, c_format("Failed to verify HNA distance [%s]", args.original_line().c_str())); } } else if ("verify_hna_entry_count" == word) { // CMD: verify_hna_entry_count int count = get_next_number(args, "verify_hna_entry_count"); if (! nodes().verify_hna_entry_count(count)) { xorp_throw(InvalidString, c_format("Failed to verify HNA entry count [%s]", args.original_line().c_str())); } } else if ("verify_hna_origin_count" == word) { // CMD: verify_hna_origin_count int count = get_next_number(args, "verify_hna_origin_count"); if (! nodes().verify_hna_origin_count(count)) { xorp_throw(InvalidString, c_format("Failed to verify HNA origin count [%s]", args.original_line().c_str())); } } else if ("verify_hna_dest_count" == word) { // CMD: verify_hna_dest_count int count = get_next_number(args, "verify_hna_dest_count"); if (! nodes().verify_hna_dest_count(count)) { xorp_throw(InvalidString, c_format("Failed to verify HNA prefix count [%s]", args.original_line().c_str())); } } else if ("originate_hna" == word) { // CMD: originate_hna IPv4Net dest = get_next_ipv4_net(args, "originate_hna"); if (! nodes().originate_hna(dest)) { xorp_throw(InvalidString, c_format("Failed to originate HNA [%s]", args.original_line().c_str())); } } else if ("withdraw_hna" == word) { // CMD: withdraw_hna IPv4Net dest = get_next_ipv4_net(args, "withdraw_hna"); if (! nodes().withdraw_hna(dest)) { xorp_throw(InvalidString, c_format("Failed to withdraw HNA [%s]", args.original_line().c_str())); } } else if ("wait" == word) { // CMD: wait int secs = get_next_number(args, "wait"); wait(secs); } else { xorp_throw(InvalidString, c_format("Unknown command <%s>. [%s]", word.c_str(), args.original_line().c_str())) } } return true; } int go(bool verbose, int verbose_level) { Simulator simulator(verbose, verbose_level); try { for(;;) { string line; if (!getline(cin, line)) return 0; Args args(line); if (!simulator.cmd(args)) return -1; } } catch(...) { xorp_print_standard_exceptions(); return -1; } } int main(int argc, char **argv) { XorpUnexpectedHandler x(xorp_unexpected_handler); xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); TestMain t(argc, argv); t.complete_args_parsing(); if (0 != t.exit()) return t.exit(); int retval = go(t.get_verbose(), t.get_verbose_level()); xlog_stop(); xlog_exit(); return retval; } xorp/contrib/olsr/link.hh0000664000076400007640000001566011421137511015561 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8 sw=4: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/contrib/olsr/link.hh,v 1.3 2008/10/02 21:56:34 bms Exp $ #ifndef __OLSR_LINK_HH__ #define __OLSR_LINK_HH__ class Neighbor; class Neighborhood; /** * @short Logical link used to reach a directly reachable Neighbor. * * LogicalLink is so named because it is mostly independent of the * underlying link layer. OLSR uses link-layer broadcasts or multicasts * for the purposes of neighbor discovery, and links are created through * the exchange of HELLO messages. LogicalLink may be associated with * link-layer specific state, e.g. that of an 802.11s mesh portal. */ class LogicalLink { public: LogicalLink(Neighborhood* tm, EventLoop& eventloop, const OlsrTypes::LogicalLinkID id, const TimeVal& vtime, const IPv4& remote_addr, const IPv4& local_addr); /** * @return the ID of this link. */ inline OlsrTypes::LogicalLinkID id() const { return _id; } /** * @return the ID of the neighbor at the far end of this link. */ inline OlsrTypes::NeighborID neighbor_id() const { return _neighborid; } /** * Set the ID of the neighbor at the far end of this link. * * @param neighborid the ID of the neighbor. */ inline void set_neighbor_id(OlsrTypes::NeighborID neighborid) { _neighborid = neighborid; } /** * @return the cached Neighbor pointer. */ inline Neighbor* destination() const { XLOG_ASSERT(0 != _destination); // Pilot error if we get here. return _destination; } /** * Set the cached neighbor pointer. * * @param n the Neighbor pointer. */ inline void set_destination(Neighbor* n) { _destination = n; } /** * @return the ID of the local interface where this link resides. */ inline OlsrTypes::FaceID faceid() const { return _faceid; } /** * Set the ID of the local interface where this link resides. * * @param faceid the ID of the local interface. */ inline void set_faceid(OlsrTypes::FaceID faceid) { _faceid = faceid; } /** * @return the protocol address of the local interface. */ inline IPv4 local_addr() const { return _local_addr; } /** * @return the protocol address of the neighbor's interface. */ inline IPv4 remote_addr() const { return _remote_addr; } /** * @return the near end ETX measurement. */ inline double near_etx() const { return _near_etx; } /** * @return the far end ETX measurement. */ inline double far_etx() const { return _far_etx; } /** * Update the link timers, based on the information present * in a HELLO message. * Section 7.1.1, 2: HELLO Message Processing. * * @param vtime The validity time present in the HELLO message. * @param saw_self true if the neighbor advertised our own address. * @param lc the link code with which our address was advertised. */ void update_timers(const TimeVal& vtime, bool saw_self = false, const LinkCode lc = LinkCode()); /** * Determine the current state of the link. * Section 6.2, 1: HELLO Message Generation, Link Type. * * @return The link type code representing this link's state. */ OlsrTypes::LinkType link_type() const; /** * @return true if this link is symmetric. */ inline bool is_sym() const { return link_type() == OlsrTypes::SYM_LINK; } /** * @return the amount of time remaining until the SYM timer fires. */ inline TimeVal sym_time_remaining() const { TimeVal tv; _sym_timer.time_remaining(tv); return tv; } /** * @return the amount of time remaining until the ASYM timer fires. */ inline TimeVal asym_time_remaining() const { TimeVal tv; _asym_timer.time_remaining(tv); return tv; } /** * @return the amount of time remaining until the DEAD timer fires. */ inline TimeVal time_remaining() const { TimeVal tv; _dead_timer.time_remaining(tv); return tv; } /** * Determine if a link is pending (i.e. not yet established). * * @return true if the link is pending. */ inline bool pending() const { return _is_pending; } /** * Callback method to: service a LogicalLink's SYM timer. */ void event_sym_timer(); /** * Callback method to: service a LogicalLink's ASYM timer. */ void event_asym_timer(); /** * Callback method to: service a LogicalLink's LOST timer. */ void event_lost_timer(); /** * Callback method to: service a LogicalLink's DEAD timer. * This is immediately passed to the parent Neighborhood as * the link may be deleted. */ void event_dead_timer(); private: Neighborhood* _nh; EventLoop& _eventloop; /** * Unique identifier of link. */ OlsrTypes::LogicalLinkID _id; /** * ID of interface where this link resides. */ OlsrTypes::FaceID _faceid; /** * ID of neighbor at other end of link. */ OlsrTypes::NeighborID _neighborid; /** * Cached pointer to Neighbor. */ Neighbor* _destination; /** * L_neighbor_iface_addr protocol variable. * The protocol address of the neighbor's interface. */ IPv4 _remote_addr; /** * L_local_iface_addr protocol variable. * The protocol address of our local interface. */ IPv4 _local_addr; // // Link state timers. // /** * L_SYM_time protocol variable. */ XorpTimer _sym_timer; /** * L_ASYM_time protocol variable. */ XorpTimer _asym_timer; /** * L_LOST_LINK_time protocol variable. */ XorpTimer _lost_timer; /** * L_time protocol variable. */ XorpTimer _dead_timer; // // Link etx fields. // /** * L_link_pending protocol variable. */ bool _is_pending; /** * L_link_etx protocol variable. */ double _near_etx; /** * ETX far-end measurement. */ double _far_etx; }; #endif // __OLSR_LINK_HH__ xorp/contrib/olsr/debug_io.cc0000664000076400007640000002213111421137511016356 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8 sw=4: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "olsr_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/callback.hh" #include "libxorp/exceptions.hh" #include "libxorp/ipv4.hh" #include "libxorp/ipv4net.hh" #include "libxorp/status_codes.h" #include "libxorp/service.hh" #include "libxorp/eventloop.hh" #include "libxorp/tokenize.hh" #include "libxorp/test_main.hh" #include "olsr.hh" #include "debug_io.hh" DebugIO::DebugIO(TestInfo& info, EventLoop& eventloop) : _info(info), _eventloop(eventloop), _packets(0), _next_interface_id(1) { initialize_message_decoder(_md); } DebugIO::~DebugIO() { } void DebugIO::pp(const string& which, int level, const string& interface, const string& vif, IPv4 dst, uint16_t dport, IPv4 src, uint16_t sport, uint8_t* data, uint32_t len) { TimeVal now; _eventloop.current_time(now); // 'now' is relative, so don't use pretty_print(). DOUT_LEVEL(_info, level) << now.str() << endl; DOUT_LEVEL(_info, level) << which << "(" << interface << "/" << vif << "/" << dst.str() << ":" << dport << "," << src.str() << ":" << sport << ")" << endl; // Try to decode the packet to print it. try { Packet* pkt = new Packet(_md); pkt->decode(data, len); DOUT_LEVEL(_info, level) << pkt->str() << endl; #if 1 // XXX no refcounting! const vector& msgs = pkt->messages(); vector::const_iterator ii; for (ii = msgs.begin(); ii != msgs.end(); ii++) delete (*ii); #endif delete pkt; } catch (InvalidPacket& e) { DOUT_LEVEL(_info, level) << "Bad packet: " << cstring(e) << endl; } } int DebugIO::startup() { ServiceBase::set_status(SERVICE_READY); return (XORP_OK); } int DebugIO::shutdown() { ServiceBase::set_status(SERVICE_SHUTDOWN); return (XORP_OK); } bool DebugIO::enable_address(const string& interface, const string& vif, const IPv4& address, const uint16_t& port, const IPv4& all_nodes_address) { DOUT(_info) << "enable_address(" << interface << "/" << vif << "/" << address.str() << ":" << port << ")" << all_nodes_address.str() << endl; return true; } bool DebugIO::disable_address(const string& interface, const string& vif, const IPv4& address, const uint16_t& port) { DOUT(_info) << "disable_address(" << interface << "/" << vif << "/" << address.str() << ":" << port << ")" << endl; return true; } bool DebugIO::is_interface_enabled(const string& interface) const { DOUT(_info) << "enabled(" << interface << ")\n"; return true; } bool DebugIO::is_vif_enabled(const string & interface, const string & vif) const { DOUT(_info) << "enabled(" << interface << "," << vif << ")" << endl; return true; } bool DebugIO::is_vif_broadcast_capable(const string& interface, const string& vif) { DOUT(_info) << "broadcast_capable(" << interface << "," << vif << ")" << endl; return true; } bool DebugIO::is_vif_multicast_capable(const string& interface, const string& vif) { DOUT(_info) << "multicast_capable(" << interface << "," << vif << ")" << endl; return true; } bool DebugIO::is_vif_loopback(const string& interface, const string& vif) { DOUT(_info) << "loopback(" << interface << "," << vif << ")" << endl; return false; } bool DebugIO:: is_address_enabled(const string& interface, const string& vif, const IPv4& address) const { DOUT(_info) << "address_enabled(" << interface << "," << vif << "," << address.str() << ")" << endl; return false; } bool DebugIO::get_addresses(const string& interface, const string& vif, list& addresses) const { DOUT(_info) << "get_addresses(" << interface << "," << vif << ")" << endl; return false; UNUSED(addresses); } bool DebugIO::get_broadcast_address(const string& interface, const string& vif, const IPv4& address, IPv4& bcast_address) const { bcast_address = IPv4::ALL_ONES(); UNUSED(interface); UNUSED(vif); UNUSED(address); return true; } bool DebugIO::get_interface_id(const string& interface, uint32_t& interface_id) { DOUT(_info) << "get_interface_id(" << interface << ")\n"; if (0 == _interface_ids.count(interface)) { interface_id = _next_interface_id++; _interface_ids[interface] = interface_id; } else { interface_id = _interface_ids[interface]; } return true; } uint32_t DebugIO::get_mtu(const string& interface) { DOUT(_info) << "get_mtu(" << interface << ")\n"; return 1500; } bool DebugIO::send(const string& interface, const string& vif, const IPv4 & src, const uint16_t & sport, const IPv4 & dst, const uint16_t & dport, uint8_t* data, const uint32_t & len) { pp("SEND", 0, interface, vif, dst, dport, src, sport, data, len); _packets++; DOUT(_info) << "packets sent " << _packets << endl; map, IO::ReceiveCallback>::iterator ii = _forward_cbs.find(make_pair(interface, vif)); if (ii != _forward_cbs.end()) { ((*ii).second)->dispatch(interface, vif, dst, dport, src, sport, data, len); } return true; } void DebugIO::receive(const string& interface, const string& vif, const IPv4 & dst, const uint16_t& dport, const IPv4 & src, const uint16_t& sport, uint8_t* data, const uint32_t & len) { pp("RECEIVE", 1, interface, vif, dst, dport, src, sport, data, len); if (! IO::_receive_cb.is_empty()) { IO::_receive_cb->dispatch(interface, vif, dst, dport, src, sport, data, len); } } bool DebugIO::register_forward(const string& interface, const string& vif, IO::ReceiveCallback cb) { XLOG_ASSERT(0 == _forward_cbs.count(make_pair(interface, vif))); _forward_cbs[make_pair(interface, vif)] = cb; return true; } void DebugIO::unregister_forward(const string& interface, const string& vif) { map, IO::ReceiveCallback>::iterator ii = _forward_cbs.find(make_pair(interface, vif)); if (ii != _forward_cbs.end()) { (*ii).second.release(); _forward_cbs.erase(ii); } } bool DebugIO::add_route(IPv4Net net, IPv4 nexthop, uint32_t nexthop_id, uint32_t metric, const PolicyTags& policytags) { DOUT(_info) << "Net: " << net.str() << " nexthop: " << nexthop.str() << " nexthop_id: " << nexthop_id << " metric: " << metric << " policy: " << policytags.str() << endl; XLOG_ASSERT(0 == _routing_table.count(net)); DebugRouteEntry dre; dre._nexthop = nexthop; dre._metric = metric; dre._policytags = policytags; _routing_table[net] = dre; return true; } bool DebugIO::replace_route(IPv4Net net, IPv4 nexthop, uint32_t nexthop_id, uint32_t metric, const PolicyTags & policytags) { DOUT(_info) << "Net: " << net.str() << " nexthop: " << nexthop.str() << " nexthop_id: " << nexthop_id << " metric: " << metric << " policy: " << policytags.str() << endl; if (!delete_route(net)) return false; return add_route(net, nexthop, nexthop_id, metric, policytags); } bool DebugIO::delete_route(IPv4Net net) { DOUT(_info) << "Net: " << net.str() << endl; XLOG_ASSERT(1 == _routing_table.count(net)); _routing_table.erase(_routing_table.find(net)); return true; } void DebugIO::routing_table_empty() { _routing_table.clear(); } uint32_t DebugIO::routing_table_size() { return _routing_table.size(); } bool DebugIO::routing_table_verify(IPv4Net net, IPv4 nexthop, uint32_t metric) { DOUT(_info) << "Net: " << net.str() << " nexthop: " << nexthop.str() << " metric: " << metric << endl; if (0 == _routing_table.count(net)) { DOUT(_info) << "Net: " << net.str() << " not in table\n"; return false; } DebugRouteEntry dre = _routing_table[net]; if (dre._nexthop != nexthop) { DOUT(_info) << "Nexthop mismatch: " << nexthop.str() << " " << dre._nexthop.str() << endl; return false; } if (dre._metric != metric) { DOUT(_info) << "Metric mismatch: " << metric << " " << dre._metric << endl; return false; } return true; } void DebugIO::routing_table_dump(ostream& o) { map::iterator ii; for (ii = _routing_table.begin(); ii != _routing_table.end(); ++ii) { DebugRouteEntry& dre = (*ii).second; o << (*ii).first.str() << ": " << dre._nexthop.str() << " " << dre._metric << endl; } } xorp/contrib/olsr/xorp_olsr.cc0000664000076400007640000000510311421137511016630 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8 sw=4: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "olsr_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/ipv4.hh" #include "libxorp/service.hh" #include "libxorp/status_codes.h" #include "libxorp/eventloop.hh" #include "libxipc/xrl_std_router.hh" #include "olsr.hh" #include "xrl_io.hh" #include "xrl_target.hh" static const char TARGET_OLSR[] = "olsr4"; // XRL target name. int main(int /*argc*/, char **argv) { XorpUnexpectedHandler x(xorp_unexpected_handler); // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporarily increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_level_set_verbose(XLOG_LEVEL_WARNING, XLOG_VERBOSE_HIGH); xlog_level_set_verbose(XLOG_LEVEL_INFO, XLOG_VERBOSE_HIGH); // xlog_level_set_verbose(XLOG_LEVEL_TRACE, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); // XXX TODO: implement a command line option which periodically // prints OLSR protocol status in a manner near identical to olsrd. try { EventLoop eventloop; string feaname = "fea"; string ribname = "rib"; XrlStdRouter xrl_router(eventloop, TARGET_OLSR); XrlIO io(eventloop, xrl_router, feaname, ribname); Olsr olsr(eventloop, &io); XrlOlsr4Target target(&xrl_router, olsr, io); wait_until_xrl_router_is_ready(eventloop, xrl_router); io.startup(); while (olsr.running()) eventloop.run(); } catch(...) { xorp_catch_standard_exceptions(); } // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); debug_msg("Bye!\n"); return 0; } xorp/contrib/olsr/regression/0000775000076400007640000000000011421137511016453 5ustar greearbgreearbxorp/contrib/olsr/regression/mid_ok_ipv4.data0000664000076400007640000000135111421137511021512 0ustar greearbgreearb// $XORP$ // Well-formed OLSR packet with a single MID message and three // interface addresses. /* Packet Header */ /* Packet Length (28 bytes) */ 0x00, 0x1c, /* Packet Sequence Number (58450) */ 0xe4, 0x52, /* Message Header */ /* Message Type (MID) */ 0x03, /* Validity Time (256s) */ 0x0c, /* Message Size (24 bytes) */ 0x00, 0x18, /* Originator Address (192.168.124.1) */ 0xc0, 0xa8, 0x7c, 0x01, /* Time to Live (255) */ 0xff, /* Hop Count (0) */ 0x00, /* Message Sequence Number (38019) */ 0x94, 0x83, /* MID Message Body */ /* Interface Address (192.168.122.1) */ 0xc0, 0xa8, 0x7a, 0x01, /* Interface Address (192.168.123.1) */ 0xc0, 0xa8, 0x7b, 0x01, /* Interface Address (192.168.125.1) */ 0xc0, 0xa8, 0x7d, 0x01, xorp/contrib/olsr/regression/tc_ok_ipv4.data0000664000076400007640000000123411421137511021347 0ustar greearbgreearb// $XORP$ // Well-formed OLSR packet with a single TC message. /* Packet Header */ /* Packet Length (24 bytes) */ 0x00, 0x18, /* Packet Sequence Number (58448) */ 0xe4, 0x50, /* Message Header */ /* Message Type (TC) */ 0x02, /* Validity Time (256s) */ 0x0c, /* Message Size (20 bytes) */ 0x00, 0x14, /* Originator Address (192.168.124.2) */ 0xc0, 0xa8, 0x7c, 0x02, /* Time to Live (255) */ 0xff, /* Hop Count (0) */ 0x00, /* Message Sequence Number (38017) */ 0x94, 0x81, /* TC Message Body */ /* Advertised Neighbor Sequence Number (1) */ 0x00, 0x01, /* Reserved */ 0x00, 0x00, /* Neighbor Address (192.168.124.17) */ 0xc0, 0xa8, 0x7c, 0x11 xorp/contrib/olsr/regression/hello_empty_ok_ipv4.data0000664000076400007640000000123311421137511023261 0ustar greearbgreearb// $XORP$ // Well-formed OLSR packet with a single HELLO message, with // no neighbor or link information. /* Packet Header */ /* Packet Length (24 bytes) */ 0x00, 0x18, /* Packet Sequence Number (58443) */ 0xe4, 0x4b, /* Message Header */ /* Message Type (HELLO) */ 0x01, /* Validity Time (256s) */ 0x0c, /* Message Size (20 bytes) */ 0x00, 0x14, /* Originator Address (192.168.124.1) */ 0xc0, 0xa8, 0x7c, 0x01, /* Time to Live (255) */ 0xff, /* Hop Count (0) */ 0x00, /* Message Sequence Number (38015) */ 0x94, 0x7f, /* HELLO Message Body */ /* Reserved */ 0x00, 0x00, /* Emission Interval (5s) */ 0x46, /* Willingness (Default) */ 0x03 xorp/contrib/olsr/regression/multi_msg_ok_ipv4.data0000664000076400007640000000223211421137511022740 0ustar greearbgreearb// $XORP$ // Well-formed OLSR packet with two messages: a MID message with // two interface addresses, and an HNA with a single network. /* Packet Header */ /* Packet Length (44 bytes) */ 0x00, 0x2c, /* Packet Sequence Number (58455) */ 0xe4, 0x57, /* Message Header */ /* Message Type (MID) */ 0x03, /* Validity Time (256s) */ 0x0c, /* Message Size (20 bytes) */ 0x00, 0x14, /* Originator Address (192.168.122.1) */ 0xc0, 0xa8, 0x7a, 0x01, /* Time to Live (255) */ 0xff, /* Hop Count (0) */ 0x00, /* Message Sequence Number (38025) */ 0x94, 0x89, /* MID Message Body */ /* Interface Address (192.168.125.1) */ 0xc0, 0xa8, 0x7d, 0x01, /* Interface Address (192.168.126.1) */ 0xc0, 0xa8, 0x7e, 0x01, /* Message Header */ /* Message Type (HNA) */ 0x04, /* Validity Time (256s) */ 0x0c, /* Message Size (20 bytes) */ 0x00, 0x14, /* Originator Address (192.168.122.1) */ 0xc0, 0xa8, 0x7a, 0x01, /* Time to Live (255) */ 0xff, /* Hop Count (0) */ 0x00, /* Message Sequence Number (38026) */ 0x94, 0x8a, /* HNA Message Body */ /* Network Address (192.168.123.0) */ 0xc0, 0xa8, 0x7b, 0x00, /* Network Mask (255.255.255.0) */ 0xff, 0xff, 0xff, 0x00 xorp/contrib/olsr/regression/hna_ok_ipv4.data0000664000076400007640000000125611421137511021513 0ustar greearbgreearb// $XORP$ // Well-formed OLSR packet with a single HNA message and a single // IPv4 network address/mask pair. /* Packet Header */ /* Packet Length (24 bytes) */ 0x00, 0x18, /* Packet Sequence Number (58449) */ 0xe4, 0x51, /* Message Header */ /* Message Type (HNA) */ 0x04, /* Validity Time (256s) */ 0x0c, /* Message Size (20 bytes) */ 0x00, 0x14, /* Originator Address (192.168.124.1) */ 0xc0, 0xa8, 0x7c, 0x01, /* Time to Live (255) */ 0xff, /* Hop Count (0) */ 0x00, /* Message Sequence Number (38018) */ 0x94, 0x82, /* HNA Message Body */ /* Network Address (192.168.123.0) */ 0xc0, 0xa8, 0x7b, 0x00, /* Network Mask (255.255.255.0) */ 0xff, 0xff, 0xff, 0x00 xorp/contrib/olsr/regression/hello_single_ok_ipv4.data0000664000076400007640000000157711421137511023417 0ustar greearbgreearb// $XORP$ // Well-formed OLSR packet with a single HELLO message, with // a single link tuple containing a single neighbor. /* Packet Header */ /* Packet Length (32 bytes) */ 0x00, 0x20, /* Packet Sequence Number (58443) */ 0xe4, 0x4b, /* Message Header */ /* Message Type (HELLO) */ 0x01, /* Validity Time (256s) */ 0x0c, /* Message Size (28 bytes) */ 0x00, 0x1c, /* Originator Address (192.168.124.1) */ 0xc0, 0xa8, 0x7c, 0x01, /* Time to Live (255) */ 0xff, /* Hop Count (0) */ 0x00, /* Message Sequence Number (38015) */ 0x94, 0x7f, /* HELLO Message Body */ /* Reserved */ 0x00, 0x00, /* Emission Interval (5s) */ 0x46, /* Willingness (Default) */ 0x03, /* Link Information Tuple */ /* Link Code (SYM_NEIGH|SYM_LINK) */ 0x06, /* Reserved */ 0x00, /* Link Message Size (8 bytes) */ 0x00, 0x08, /* Neighbor Address (192.168.122.25) */ 0xc0, 0xa8, 0x7a, 0x19 xorp/contrib/olsr/regression/hello_multi_ok_ipv4.data0000664000076400007640000000232111421137511023254 0ustar greearbgreearb// $XORP$ // Well-formed OLSR packet with a single HELLO message, with // two link tuples containing two neighbors each. /* Packet Header */ /* Packet Length (44 bytes) */ 0x00, 0x2c, /* Packet Sequence Number (58445) */ 0xe4, 0x4d, /* Message Header */ /* Message Type (HELLO) */ 0x01, /* Validity Time (256s) */ 0x0c, /* Message Size (40 bytes) */ 0x00, 0x28, /* Originator Address (192.168.124.1) */ 0xc0, 0xa8, 0x7c, 0x01, /* Time to Live (255) */ 0xff, /* Hop Count (0) */ 0x00, /* Message Sequence Number (38038) */ 0x94, 0x96, /* HELLO Message Body */ /* Reserved */ 0x00, 0x00, /* Emission Interval (6s) */ 0x86, /* Willingness (Low) */ 0x01, /* Link Information Tuple */ /* Link Code (NOT_NEIGH|ASYM_LINK) */ 0x01, /* Reserved */ 0x00, /* Link Message Size (12 bytes) */ 0x00, 0x0c, /* Neighbor Address (192.168.122.22) */ 0xc0, 0xa8, 0x7a, 0x16, /* Neighbor Address (192.168.122.23) */ 0xc0, 0xa8, 0x7a, 0x17, /* Link Information Tuple */ /* Link Code (SYM_NEIGH|SYM_LINK) */ 0x06, /* Reserved */ 0x00, /* Link Message Size (12 bytes) */ 0x00, 0x0c, /* Neighbor Address (192.168.122.24) */ 0xc0, 0xa8, 0x7a, 0x18, /* Neighbor Address (192.168.122.25) */ 0xc0, 0xa8, 0x7a, 0x19 xorp/contrib/olsr/topology.hh0000664000076400007640000004726111421137511016502 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8 sw=4: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/contrib/olsr/topology.hh,v 1.3 2008/10/02 21:56:36 bms Exp $ #ifndef __OLSR_TOPOLOGY_HH__ #define __OLSR_TOPOLOGY_HH__ class TopologyManager; /** * @short A multiple interface record (MID). * * There is one MidEntry per interface address advertised by an OLSR peer. * As such, a primary address may be associated with multiple MID entries. */ class MidEntry { public: MidEntry(EventLoop& ev, TopologyManager* parent, const OlsrTypes::MidEntryID id, const IPv4& iface_addr, const IPv4& main_addr, const uint16_t distance, const TimeVal& vtime) : _ev(ev), _parent(parent), _id(id), _iface_addr(iface_addr), _main_addr(main_addr), _distance(distance) { update_timer(vtime); } inline OlsrTypes::MidEntryID id() const { return _id; } inline IPv4 iface_addr() const { return _iface_addr; } inline IPv4 main_addr() const { return _main_addr; } inline uint16_t distance() const { return _distance; } inline void set_distance(const uint16_t distance) { _distance = distance; } /** * @return the amount of time remaining until this * MID entry expires. */ inline TimeVal time_remaining() const { TimeVal tv; _expiry_timer.time_remaining(tv); return tv; } /** * Update the MidEntry's expiry timer. * * @param vtime the new value of the expiry timer. */ void update_timer(const TimeVal& vtime); /** * Callback method to: delete a dying MidEntry. */ void event_dead(); private: EventLoop& _ev; TopologyManager* _parent; OlsrTypes::MidEntryID _id; IPv4 _iface_addr; IPv4 _main_addr; uint16_t _distance; XorpTimer _expiry_timer; }; /** * @short A topology control record (TC). */ class TopologyEntry { public: TopologyEntry(EventLoop& ev, TopologyManager* parent, OlsrTypes::TopologyID id, const IPv4& dest, const IPv4& lasthop, const uint16_t distance, const uint16_t seqno, const TimeVal& vtime) : _ev(ev), _parent(parent), _id(id), _destination(dest), _lasthop(lasthop), _distance(distance), _seqno(seqno) { update_timer(vtime); } inline OlsrTypes::TopologyID id() const { return _id; } inline IPv4 destination() const { return _destination; } inline IPv4 lasthop() const { return _lasthop; } inline uint16_t distance() const { return _distance; } inline uint16_t seqno() const { return _seqno; } inline void set_distance(const uint16_t d) { _distance = d; } /** * @return the amount of time remaining until this * TC entry expires. */ inline TimeVal time_remaining() const { TimeVal tv; _expiry_timer.time_remaining(tv); return tv; } /** * Update the TopologyEntry's expiry timer. * * @param vtime the new value of the expiry timer. */ void update_timer(const TimeVal& vtime); /** * Callback method to: delete a dying TopologyEntry. */ void event_dead(); private: EventLoop& _ev; TopologyManager* _parent; /** * @short unique identifier. */ OlsrTypes::TopologyID _id; /** * @short destination address. */ IPv4 _destination; // T_dest_addr /** * @short The last hop to reach _dest. */ IPv4 _lasthop; // T_last_addr /** * @short The number of hops from the origin to the neighbor * advertised in this TC entry. */ uint16_t _distance; // Not named -- Section 10, 3.1 /** * @short The ANSN of this entry. */ uint16_t _seqno; // T_seq /** * @short The time at which this entry will expire. */ XorpTimer _expiry_timer; // T_time }; /** * @short Class which manages topology outside of the one-hop and * two-hop neighborhood in the OLSR domain. */ class TopologyManager { public: TopologyManager(Olsr& olsr, EventLoop& eventloop, FaceManager& fm, Neighborhood& nh); ~TopologyManager(); inline RouteManager* route_manager() { return _rm; } void set_route_manager(RouteManager* rm) { _rm = rm; } // // TC entries. // /** * Update or create a topology entry in the TC database. * * @param dest_addr the destination of the topology entry. * @param origin_addr the origin of the topology entry. * @param distance the distance in hops from this node to the origin, * calculated from the message hop count. * @param ansn the advertised sequence number of the topology entry. * @param vtime the time for which this topology entry * remains valid. * @param is_created a boolean which is set to true if a new entry * has been created. * @throw BadTopologyEntry if the entry could not be created. */ void update_tc_entry(const IPv4& dest_addr, const IPv4& origin_addr, const uint16_t distance, const uint16_t ansn, const TimeVal& vtime, bool& is_created) throw(BadTopologyEntry); /** * Add a topology entry to the database. * * @param dest_addr the destination of the new topology entry. * @param origin_addr the origin of the new topology entry. * @param distance the distance in hops from this node to the origin, * calculated from the message hop count. * @param ansn the advertised sequence number of the topology entry. * @param expiry_time the time for which this topology entry * remains valid. */ OlsrTypes::TopologyID add_tc_entry(const IPv4& dest_addr, const IPv4& origin_addr, const uint16_t distance, const uint16_t ansn, const TimeVal& expiry_time) throw(BadTopologyEntry); /** * Delete a topology entry by ID. * It must be removed from last-hop and destination maps. * * @param tcid the ID of the toplogy entry to delete. * @return true if the topology entry was deleted, false if it could * not be found. */ bool delete_tc_entry(const OlsrTypes::TopologyID tcid); /** * Clear the TC database. */ void clear_tc_entries(); /** * Apply the Advertised Neighbor Sequence Number in the given TC * message to the Topology Set. * Section 9.5: TC Message Processing, 2-3. * * @param ansn the ANSN to apply. * @param origin_addr the origin of the TC message containing @param ansn * @return true if the provided ANSN is valid and has been applied, * otherwise false if the message is stale and should be * rejected. */ bool apply_tc_ansn(const uint16_t ansn, const IPv4& origin_addr); /** * Return a topology entry ID given its destination and origin. * * Note: This is not declared 'const' due to the undeclared * mutability of map::operator[]. * * @param dest_addr the destination of the TC entry to look up. * @param lasthop_addr the origin of the TC entry to look up. * @return the topology ID. * @throw BadTopologyEntry if the entry could not be found. */ OlsrTypes::TopologyID get_topologyid(const IPv4& dest_addr, const IPv4& lasthop_addr) throw(BadTopologyEntry); /** * Get a pointer to a topology entry given its ID. * * Used by the XRL layer. * * @param tcid the ID of the TC entry to look up. * @return the MID pointer. * @throw BadTopologyEntry if the entry could not be found. */ const TopologyEntry* get_topology_entry_by_id( const OlsrTypes::TopologyID tcid) const throw(BadTopologyEntry); /** * Fill out a list of all topology entry IDs. * * Used by the XRL layer. * * @param tclist the list to fill out. */ void get_topology_list(list& tclist) const; /** * Retrieve the Advertised Neighbor Set (ANS) for a given OLSR peer. * Typically used by protocol simulator. * * Given the address of a node in the topology, retrieve the addresses * from all TC entries originated by that node. * Assumes that the "all entries for origin have same ANSN" invariant holds. * * TODO: Also return the per-link ETX information. * * @param origin_addr the originating node to look up in the TC database. * @param ansn the sequence number of origin_addr's neighbor set. * @throw BadTopologyEntry if origin_addr was not found. */ vector get_tc_neighbor_set(const IPv4& origin_addr, uint16_t& ansn) throw(BadTopologyEntry); /* * Look up the distance of a TC entry. * Used by protocol simulator. * * @param origin_addr the address of the node originating this TC entry. * @param neighbor_addr the address of the destination node. * @return the number of hops to reach neighbor_addr via origin_addr. * @throw BadTopologyEntry if the entry does not exist. */ uint16_t get_tc_distance(const IPv4& origin_addr, const IPv4& dest_addr) throw(BadTopologyEntry); /** * Count the number of TC entries which point to a given destination. * Used by protocol simulator. * * @param dest_addr the TC destination. * @return The number of TC entries pointing to dest_addr. */ size_t get_tc_lasthop_count_by_dest(const IPv4& dest_addr); /** * Calculate the number of unique OLSR nodes with TC entries in this * node's TC database. * * @return the number of unique main addresses in the TC lasthop map. */ size_t tc_node_count() const; // // MID entries. // /** * Update a Multiple Interface Declaration (MID) entry. * * The entry will be created if it does not exist. * * @param main_addr the main address of the node originating * the MID entry. * @param iface_addr the interface address of the MID entry. * @param distance the distance in hops of the origin of the * MIS message being processed. * @param vtime the time for which the MID entry remains valid. * @param is_mid_created set to true if a new MID entry was created. * @throw BadMidEntry if the entry could not be created or updated. */ void update_mid_entry(const IPv4& main_addr, const IPv4& iface_addr, const uint16_t distance, const TimeVal& vtime, bool& is_mid_created) throw(BadMidEntry); /** * Create a new entry in the MID database. * * TODO Find the next available ID if already taken, as the range may * recycle quickly in large, dynamically changing topologies. * * @param main_addr the main address of the node originating * the MID entry. * @param iface_addr the interface address of the MID entry. * @param distance the distance in hops of the origin of the * MIS message being processed. * @param vtime the time for which the MID entry remains valid. * @throw BadMidEntry if the entry could not be created or updated. */ void add_mid_entry(const IPv4& main_addr, const IPv4& iface_addr, const uint16_t distance, const TimeVal& vtime) throw(BadMidEntry); /** * Delete a MID entry by ID. * * @param mid_id the ID of the MID entry to delete. * @return true if the MID entry was deleted. */ bool delete_mid_entry(const OlsrTypes::MidEntryID mid_id); /** * Clear MID entries. */ void clear_mid_entries(); /** * Given the main address of an OLSR node, return a vector containing * all other interface addresses for it, as learned via the MID part * of the OLSR protocol. * * @param main_addr the main address to look up * @return a vector of protocol addresses, which may be empty. */ vector get_mid_addresses(const IPv4& main_addr); /** * Look up the most recently seen distance of a MID entry, given its * origin and interface address. * * Internal method. Stubbed out if DETAILED_DEBUG is not defined. * * @param main_addr the main address of the OLSR node to look up. * @param iface_addr the interface address of the OLSR node to look up. * @throw BadMidEntry if the entry could not be found. */ uint16_t get_mid_address_distance(const IPv4& main_addr, const IPv4& iface_addr) throw(BadMidEntry); /** * Given an address possibly corresponding to a MID entry, return * the main address to which it would map. * * Used by the protocol simulator. Requires a linear search of MID space, * returns first match, there should be no other matches; no invariant. * * @param mid_addr the interface address to look up. * @return the main address of the node with the given interface address. * @throw BadMidEntry if mid_addr was not found. */ IPv4 get_main_addr_of_mid(const IPv4& mid_addr) throw(BadMidEntry); /** * Count the number of unique OLSR main addresses in this node's MID * database. * * @return the number of OLSR main addresses in _mid_addr. */ size_t mid_node_count() const; /** * Get a pointer to a MID entry given its ID. * * Used by the XRL layer. * * @param midid the ID of the MID entry to look up. * @return the MID entry pointer. * @throw BadTopologyEntry if the entry could not be found. */ const MidEntry* get_mid_entry_by_id( const OlsrTypes::MidEntryID midid) const throw(BadTopologyEntry); /** * Fill out a list of all MID entry IDs. * * Used by the XRL layer. * * @param midlist the list to fill out. */ void get_mid_list(list& midlist) const; // // RouteManager interaction. // /** * Push topology set to the RouteManager for SPT computation. * Section 10: Route computation. * * In ascending order, we push the rest of the known network topology, * starting with the TC entries which would have been originated by * two-hop neighbors. * If we encounter incomplete TC information for the network topology, * that is, there are no known nodes at a particular distance, we stop * pushing topology to RouteManager. * * TODO: Use ETX measurements for edge choice. */ void push_topology(); // // Event handlers. // /** * Callback method to: process an incoming TC message. * Section 9.5: TC Message Processing. * * @param msg Pointer to a message which is derived from TcMessage. * @param remote_addr The source address of the packet containing msg. * @param local_addr The address of the interface where this packet was * received. * @return true if this function consumed msg. */ bool event_receive_tc(Message* msg, const IPv4& remote_addr, const IPv4& local_addr); /** * Callback method to: delete an expiring TopologyEntry. * * @param tcid the ID of the expiring TopologyEntry. */ void event_tc_dead(OlsrTypes::TopologyID tcid); /** * Callback method to: process an incoming MID message. * Section 5.4: MID Message Processing. * * @param msg the message to process. * @param remote_addr the source address of the Packet containing msg * @param local_addr the address of the interface where @param msg was * received. * @return true if this method consumed @param msg */ bool event_receive_mid(Message* msg, const IPv4& remote_addr, const IPv4& local_addr); /** * Callback method to: delete a dying MidEntry. */ void event_mid_dead(const OlsrTypes::MidEntryID mid_id); protected: /** * Internal method to: update a TC entry's distance. * * It is necessary to maintain an insertion sort collation order in the * TcDistanceMap as it is used for route calculation. * * @param tc pointer to the TopologyEntry to update. * @param distance the new distance of the TopologyEntry. */ void update_tc_distance(TopologyEntry* tc, uint16_t distance); /** * Internal method to: assert that a TC's distance is unique. * * Verify that the given TopologyID appears once, and only once, in * the _tc_distances array used for route computation. * Stubbed out if DETAILED_DEBUG is not defined. * * @param tcid the topology entry ID to verify. * @throw BadTopologyEntry if tcid appears more than once in _tc_distances. */ void assert_tc_distance_is_unique(const OlsrTypes::TopologyID tcid) throw(BadTopologyEntry); /** * Internal method to: assert that the ANSNs for all TC entries * originated by the node origin_addr are identical. * * A full linear search of the TC record space is performed. * Stubbed out if DETAILED_DEBUG is not defined. * * TODO: Eliminate this function by refactoring the data structures; * we SHOULD be able to check if the last ANSN from origin was * empty, currently we don't do that. * * @param origin_addr the node for which to verify the ANSNs. * @throw BadTopologyEntry if the ANSNs for origin_addr are not identical. */ void assert_tc_ansn_is_identical(const IPv4& origin_addr) throw(BadTopologyEntry); private: Olsr& _olsr; EventLoop& _eventloop; FaceManager& _fm; Neighborhood& _nh; RouteManager* _rm; OlsrTypes::MidEntryID _next_mid_id; OlsrTypes::TopologyID _next_tcid; /** * Multiple Interface Association Database. */ typedef map MidIdMap; MidIdMap _mids; /** * A map providing lookup of the MidEntries corresponding to the * main protocol address of an OLSR node. */ typedef multimap MidAddrMap; MidAddrMap _mid_addr; /** * Topology Information Base. */ typedef map TcIdMap; TcIdMap _topology; /** * A multimap providing lookup of topology entries by * their distance. * Used for routing computation, the most common case. */ typedef multimap TcDistanceMap; TcDistanceMap _tc_distances; /** * A multimap providing lookup of topology entries by destination. * Used by TC updates. */ typedef multimap TcDestMap; TcDestMap _tc_destinations; /** * A multimap providing lookup of topology entries by last hop. * Used by ANSN processing. */ typedef multimap TcLasthopMap; TcLasthopMap _tc_lasthops; /** * A map of final ANSN numbers for each origin from which we have seen * TC messages with empty neighbor sets. * Used by the simulator for verification. */ typedef map TcFinalSeqMap; TcFinalSeqMap _tc_final_seqnos; }; #endif // __OLSR_TOPOLOGY_HH__ xorp/contrib/olsr/external.hh0000664000076400007640000003614511421137511016447 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8 sw=4: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/contrib/olsr/external.hh,v 1.3 2008/10/02 21:56:34 bms Exp $ #ifndef __OLSR_EXTERNAL_HH__ #define __OLSR_EXTERNAL_HH__ class ExternalRoute; class ExternalRoutes; class RouteManager; /** * @short Orders a sequence of OlsrTypes::ExternalID in descending * order of preference for route selection. * * Model of StrictWeakOrdering. */ struct ExternalRouteOrderPred { ExternalRoutes& _ers; inline ExternalRouteOrderPred(ExternalRoutes& ers) : _ers(ers) {} /** * Functor to: implement collation order on external route maps. * * 12.6: The distance to the last-hop, as measured from the HNA * message, is used as the current discriminator; this is what's * specified in the RFC. * * Because identifiers for these structures are normally passed around * rather than the pointers themselves, it is necessary to use a functor. * * Collation order: * 1. Network destination, as per IPvXNet. * 2. If not self originated, distance in ascending order. * * @param lhid the ID of the external route on the left-hand side. * @param rhid the ID of the external route on the right-hand side. * @return true if lhid comes before rhid. */ bool operator()(const OlsrTypes::ExternalID lhid, const OlsrTypes::ExternalID rhid); }; /** * @short Class which manages distribution of external routes * throughout the OLSR domain. */ class ExternalRoutes { public: ExternalRoutes(Olsr& olsr, EventLoop& eventloop, FaceManager& fm, Neighborhood& nh); ~ExternalRoutes(); // // Accessors/mutators. // FaceManager& face_manager() { return _fm; } inline RouteManager* route_manager() { return _rm; } inline void set_route_manager(RouteManager* rm) { _rm = rm; } // // HNA protocol variables. // inline TimeVal get_hna_interval() const { return _hna_interval; } /** * Set the HNA send timer. * * @param hna_interval the interval between HNA advertisements. * * The timer will only be restarted if previously scheduled. If the * period of the HNA broadcasts is changed, a HNA broadcast MAY be * scheduled to take place immediately. */ void set_hna_interval(const TimeVal& hna_interval); inline TimeVal get_hna_hold_time() const { return 3 * get_hna_interval(); } // // HNA routes in. // /** * Update or create a route entry learned from HNA. * * If the origin of the HNA route is not reachable in the OLSR SPT, * the route will be rejected; see NOTES. * TODO: In future distance may also be treated as an advertised metric. * * @param dest The destination being updated. * @param lasthop The last-hop to reach the destination. * @param distance the number of hops to reach lasthop. * @param expiry_time The time at which the entry will expire. * @param is_created reference to a boolean which is set to true * iff a new entry was created by this method. * * @return The ID of the created or updated route entry. * @throw BadExternalRoute if the route could not be created. */ OlsrTypes::ExternalID update_hna_route_in(const IPv4Net& dest, const IPv4& lasthop, const uint16_t distance, const TimeVal& expiry_time, bool& is_created) throw(BadExternalRoute); /** * Create a route entry learned from HNA. * * @param dest The destination being updated. * @param lasthop The last-hop to reach the destination; * usually this is the origin of the route. * @param distance the number of hops to reach lasthop. * @param expiry_time The time at which the entry will expire. * * @return The ID of the created route entry. * @throw BadExternalRoute if the route could not be created. */ OlsrTypes::ExternalID add_hna_route_in(const IPv4Net& dest, const IPv4& lasthop, const uint16_t distance, const TimeVal& expiry_time) throw(BadExternalRoute); /** * Delete an HNA learned route entry given its ID. * * @param erid The ID of the external route to delete. * @return true if the entry was found and deleted. */ bool delete_hna_route_in(OlsrTypes::ExternalID erid); /** * Clear the learned HNA routes. */ void clear_hna_routes_in(); /** * Look up an ExternalRoute learned from HNA, given its origin and * destination network prefix. * * @param dest the destination network prefix. * @param lasthop the origin. * @return pointer to the ExternalRoute. * @throw BadExternalRoute if the route could not be found. */ const ExternalRoute* get_hna_route_in(const IPv4Net& dest, const IPv4& lasthop) throw(BadExternalRoute); /** * Look up the ID of a learned HNA entry. * Both the destination and the OLSR last-hop must be specified to match. * * @param dest The destination address. * @param lasthop The last-hop advertised for the destination. * * @return the ID of the learned external route. * @throw BadExternalRoute if the route could not be found. */ OlsrTypes::ExternalID get_hna_route_in_id(const IPv4Net& dest, const IPv4& lasthop) throw(BadExternalRoute); /** * Look up a learned HNA route entry by its ID. * * @param erid the ID of the learned ExternalRoute. * @return the pointer to the learned ExternalRoute. * @throw BadExternalRoute if the route could not be found. */ const ExternalRoute* get_hna_route_in_by_id( const OlsrTypes::ExternalID erid) throw(BadExternalRoute); /** * Calculate the number of unique OLSR nodes with HNA entries in this * node's HNA learned route database. * * Used only by the protocol simulator. * * As we don't currently maintain a list of origins for HNA, this is * a more computationally expensive invariant than for TC or MID. * * @return the number of unique origins in the HNA route-in map. */ size_t hna_origin_count() const; /** * Calculate the number of unique HNA prefixes which have been learned. * * Used only by the protocol simulator. * * @return the number of unique destinations in the HNA route-in map. */ size_t hna_dest_count() const; /** * @return the number of entries in the learned HNA route database. */ inline size_t hna_entry_count() const { return _routes_in.size(); } /** * Fill out a list of all the external learned route IDs. * * @param hnalist the list to fill out. */ void get_hna_route_in_list(list& hnalist); // // HNA routes out. // /** * Originate an HNA route. * * @param dest the network for which to originate HNA broadcasts. * @return true if the route was originated successfully. * @throw BadExternalRoute if the route could not be originated. */ bool originate_hna_route_out(const IPv4Net& dest) throw(BadExternalRoute); /** * Withdraw an HNA route. * * @param dest the network to withdraw from HNA broadcasts. * @throw BadExternalRoute if no route to the given destination * could be found. */ void withdraw_hna_route_out(const IPv4Net& dest) throw(BadExternalRoute); /** * Clear the advertised HNA routes. */ void clear_hna_routes_out(); /** * Look up the ID of an originated HNA entry. * Both the destination and the OLSR last-hop must be specified to match. * * @param dest The destination address. * @return the ID of the originated external route. * @throw BadExternalRoute if the route could not be found. */ OlsrTypes::ExternalID get_hna_route_out_id(const IPv4Net& dest) throw(BadExternalRoute); // // RouteManager interaction. // /** * Push candidate HNA routes to the RouteManager. * * 12.6: The distance to the last-hop, as measured from the HNA * message, is used as the current discriminator; this is what's * specified in the RFC. * * TODO: Deal with the metric here rather than in the RouteManager. * For now, RouteManager will invent a metric before plumbing to the RIB. */ void push_external_routes(); // // HNA transmission timer. // void start_hna_send_timer(); void stop_hna_send_timer(); void restart_hna_send_timer(); /** * Reschedule the HNA send timer (if the HNA interval has changed). */ void reschedule_hna_send_timer(); /** * Schedule the HNA send timer to fire as soon as possible. */ void reschedule_immediate_hna_send_timer(); // // Event handlers. // /** * Callback method to: service the HNA transmission timer. * Section 12: Non-OLSR Interfaces. * * Flood a HNA message to the rest of the OLSR domain * which contains this node's Host and Network Associations. * * @return true if the callback should be rescheduled, otherwise false. */ bool event_send_hna(); /** * Process incoming HNA message. * Section 12.5: HNA Message Processing. * * @param msg the message to process. * @param remote_addr the source address of the Packet containing msg. * @param local_addr the address of the OLSR interface where the * Packet containing msg was received. * @return true if msg was consumed by this method, otherwise false. */ bool event_receive_hna(Message* msg, const IPv4& remote_addr, const IPv4& local_addr); /** * Callback method to: delete a learned route entry when it expires. * * @param erid The ID of the external route to delete. */ void event_hna_route_in_expired(const OlsrTypes::ExternalID erid); private: Olsr& _olsr; EventLoop& _eventloop; FaceManager& _fm; Neighborhood& _nh; RouteManager* _rm; ExternalRouteOrderPred _routes_in_order_pred; bool _is_early_hna_enabled; OlsrTypes::ExternalID _next_erid; /** * @short the interval at which HNA broadcasts are flooded * throughout the OLSR domain by this node, if routes are originated. */ TimeVal _hna_interval; XorpTimer _hna_send_timer; /** * @short Map of learned routes by destination to route ID * covering that destination. */ typedef multimap ExternalDestInMap; ExternalDestInMap _routes_in_by_dest; /** * @short HNA entries learned by this node. */ typedef map ExternalRouteMap; ExternalRouteMap _routes_in; /** * @short Map of originated route IDs by destination. */ typedef map ExternalDestOutMap; ExternalDestOutMap _routes_out_by_dest; /** * @short HNA entries originated by this node. */ ExternalRouteMap _routes_out; }; /** * @short Class representing an external route entry, * either learned or originated in the OLSRv1 HNA sub-protocol. */ class ExternalRoute { public: /** * Constructor for a external route learned from another OLSR peer. */ explicit ExternalRoute(ExternalRoutes& parent, EventLoop& ev, const OlsrTypes::ExternalID erid, const IPv4Net& dest, const IPv4& lasthop, const uint16_t distance, const TimeVal& expiry_time) : _parent(parent), _eventloop(ev), _id(erid), _is_self_originated(false), _dest(dest), _lasthop(lasthop), _distance(distance) { update_timer(expiry_time); } /** * Constructor for a external route originated by this node. */ explicit ExternalRoute(ExternalRoutes& parent, EventLoop& ev, const OlsrTypes::ExternalID erid, const IPv4Net& dest) : _parent(parent), _eventloop(ev), _id(erid), _is_self_originated(true), _dest(dest), _distance(0) { } virtual ~ExternalRoute() {} /** * @return the ID of the external route. */ inline OlsrTypes::ExternalID id() const { return _id; } /** * @return true if we are the node which originated this route. */ inline bool is_self_originated() const { return _is_self_originated; } /** * @return the destination of this route. */ inline IPv4Net dest() const { return _dest; } /** * @return the node in the OLSR topology which advertised the * destination which this route points to. */ inline IPv4 lasthop() const { return _lasthop; } /** * @return the number of hops between this node and the last hop. */ inline uint16_t distance() const { return _distance; } /** * Update the distance in hops between this node and the last hop. */ inline void set_distance(const uint16_t distance) { _distance = distance; } /** * @return the amount of time remaining until this HNA entry expires. */ inline TimeVal time_remaining() const { TimeVal tv; _expiry_timer.time_remaining(tv); return tv; } /** * Schedule or re-schedule the expiry timer on this route entry. * * @param expiry_time The new expiry time of the route entry. */ void update_timer(const TimeVal& expiry_time); /** * Callback method to: request that the parent delete this HNA * derived external route, as it has now expired. */ void event_expired(); private: ExternalRoutes& _parent; EventLoop& _eventloop; OlsrTypes::ExternalID _id; /** * true if this ExternalRoute is being originated by this * node and was not learned from another node, otherwise false. */ bool _is_self_originated; /** * The A_network_addr/A_netmask protocol variable. */ IPv4Net _dest; /** * The A_gateway_addr protocol variable. */ IPv4 _lasthop; /** * The A_time protocol variable. */ XorpTimer _expiry_timer; /** * The number of hops recorded between this node and the lasthop, * as seen in the most recent HNA message containing this prefix. */ uint16_t _distance; }; #endif // __OLSR_EXTERNAL_HH__ xorp/contrib/packages/0000775000076400007640000000000011421137511015072 5ustar greearbgreearbxorp/contrib/packages/debian/0000775000076400007640000000000011421137511016314 5ustar greearbgreearbxorp/contrib/packages/debian/xorp.README.Debian0000664000076400007640000000050511421137511021344 0ustar greearbgreearbXORP for Debian --------------- This package of XORP has been built with PREFIX=/usr/lib/xorp. This is where XORP will store all of its files. To be more FHS compliant, we store stuff in their normal place, and then symlink them to /usr/lib/xorp -- Benjamin Sonntag , Sun, 4 Jun 2006 21:02:31 +0200 xorp/contrib/packages/debian/preinst.ex0000775000076400007640000000126111421137511020341 0ustar greearbgreearb#!/bin/sh # preinst script for xorp # # see: dh_installdeb(1) set -e # summary of how this script can be called: # * `install' # * `install' # * `upgrade' # * `abort-upgrade' # # for details, see http://www.debian.org/doc/debian-policy/ or # the debian-policy package case "$1" in install|upgrade) ;; abort-upgrade) ;; *) echo "preinst called with unknown argument \`$1'" >&2 exit 1 ;; esac # dh_installdeb will replace this with shell code automatically # generated by other debhelper scripts. #DEBHELPER# exit 0 xorp/contrib/packages/debian/conffiles.ex0000664000076400007640000000032211421137511020617 0ustar greearbgreearb# # If you want to use this conffile, remove all comments and list the files # that you want dpkg to process here using their absolute pathnames. # See the policy manual. # # For example: # /etc/xorp/xorp.conf xorp/contrib/packages/debian/xorp-doc.dirs0000664000076400007640000000003011421137511020723 0ustar greearbgreearbusr/share/doc/xorp/doc/ xorp/contrib/packages/debian/cron.d.ex0000664000076400007640000000011511421137511020032 0ustar greearbgreearb# # Regular cron jobs for the xorp package # 0 4 * * * root xorp_maintenance xorp/contrib/packages/debian/compat0000664000076400007640000000000211421137511017512 0ustar greearbgreearb4 xorp/contrib/packages/debian/xorp-doc.docs0000664000076400007640000000010011421137511020710 0ustar greearbgreearbBUGS README TODO LICENSE LICENSE.gpl LICENSE.lgpl LICENSE.other xorp/contrib/packages/debian/prerm.ex0000775000076400007640000000156011421137511020004 0ustar greearbgreearb#!/bin/sh # prerm script for xorp # # see: dh_installdeb(1) set -e # summary of how this script can be called: # * `remove' # * `upgrade' # * `failed-upgrade' # * `remove' `in-favour' # * `deconfigure' `in-favour' # `removing' # # for details, see http://www.debian.org/doc/debian-policy/ or # the debian-policy package case "$1" in remove|upgrade|deconfigure) ;; failed-upgrade) ;; *) echo "prerm called with unknown argument \`$1'" >&2 exit 1 ;; esac # dh_installdeb will replace this with shell code automatically # generated by other debhelper scripts. #DEBHELPER# exit 0 xorp/contrib/packages/debian/xorp.dirs0000664000076400007640000000006611421137511020171 0ustar greearbgreearbusr/sbin usr/lib/xorp etc/xorp etc/default etc/init.d xorp/contrib/packages/debian/postinst0000775000076400007640000000166511421137511020135 0ustar greearbgreearb#!/bin/sh # postinst script for xorp # # see: dh_installdeb(1) set -e # summary of how this script can be called: # * `configure' # * `abort-upgrade' # * `abort-remove' `in-favour' # # * `abort-deconfigure' `in-favour' # `removing' # # for details, see http://www.debian.org/doc/debian-policy/ or # the debian-policy package # case "$1" in configure) update-rc.d xorp defaults ;; abort-upgrade|abort-remove|abort-deconfigure) ;; *) echo "postinst called with unknown argument \`$1'" >&2 exit 1 ;; esac # dh_installdeb will replace this with shell code automatically # generated by other debhelper scripts. #DEBHELPER# exit 0 xorp/contrib/packages/debian/xorp.docs0000664000076400007640000000002111421137511020147 0ustar greearbgreearbBUGS README TODO xorp/contrib/packages/debian/watch0000664000076400007640000000032611421137511017346 0ustar greearbgreearb# you can run the "uscan" command # to check for upstream updates and more. # See uscan(1) for format # Compulsory line, this is a version 2 file version=2 http://www.xorp.org/releases/current/xorp-(.*)\.tar\.gz xorp/contrib/packages/debian/postrm0000775000076400007640000000166311421137511017574 0ustar greearbgreearb#!/bin/sh # postrm script for xorp # # see: dh_installdeb(1) set -e # summary of how this script can be called: # * `remove' # * `purge' # * `upgrade' # * `failed-upgrade' # * `abort-install' # * `abort-install' # * `abort-upgrade' # * `disappear' overwrit>r> # for details, see http://www.debian.org/doc/debian-policy/ or # the debian-policy package case "$1" in purge|remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear) update-rc.d -f xorp remove ;; *) echo "postrm called with unknown argument \`$1'" >&2 exit 1 esac # dh_installdeb will replace this with shell code automatically # generated by other debhelper scripts. #DEBHELPER# exit 0 xorp/contrib/packages/debian/xorp.default0000664000076400007640000000102711421137511020652 0ustar greearbgreearb# Defaults for xorp initscript # sourced by /etc/init.d/xorp # installed at /etc/default/xorp by the maintainer scripts # # This is a POSIX shell fragment # # Master system-wide xorp switch. The initscript # will not run if it is not set to yes. RUN="no" # Additional options that are passed to the rtrmgr Daemon. # e.g. : # -a Host allowed by the finder # -n Subnet allowed by the finder # -v Print verbose information # -b Specify boot file DAEMON_OPTS=" -b /etc/xorp/config.boot " xorp/contrib/packages/debian/rules0000775000076400007640000001003411421137511017372 0ustar greearbgreearb#!/usr/bin/make -f # -*- makefile -*- # Sample debian/rules that uses debhelper. # # This file was originally written by Joey Hess and Craig Small. # As a special exception, when this file is copied by dh-make into a # dh-make output file, you may use that output file without restriction. # This special exception was added by Craig Small in version 0.37 of dh-make. # # Modified to make a template file for a multi-binary package with separated # build-arch and build-indep targets by Bill Allombert 2001 # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 # This has to be exported to make some magic below work. export DH_OPTIONS # These are used for cross-compiling and for saving the configure script # from having to guess our platform (since we know it already) DEB_HOST_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE) DEB_BUILD_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE) # oula, ils sont gentils debian par defaut hein, mais on va eviter la ... #CFLAGS = -Wall -g # This does not work for now with current gcc version #ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) # CFLAGS += -O0 #else # CFLAGS += -O2 #endif config.status: configure dh_testdir # Add here commands to configure the package. ./configure --prefix=/usr/lib/xorp --with-openssl=/usr #Architecture build: build-arch build-indep build-arch: build-arch-stamp build-arch-stamp: config.status # Add here commands to compile the arch part of the package. $(MAKE) touch build-arch-stamp build-indep: build-indep-stamp build-indep-stamp: config.status # Add here commands to compile the indep part of the package. pushd docs && $(MAKE) && popd touch build-indep-stamp clean: dh_testdir dh_testroot rm -f build-arch-stamp build-indep-stamp #CONFIGURE-STAMP# # Add here commands to clean up after the build process. $(MAKE) distclean || true pushd docs ; $(MAKE) distclean || true ; popd ifneq "$(wildcard /usr/share/misc/config.sub)" "" cp -f /usr/share/misc/config.sub config.sub endif ifneq "$(wildcard /usr/share/misc/config.guess)" "" cp -f /usr/share/misc/config.guess config.guess endif dh_clean install: install-indep install-arch install-indep: dh_testdir dh_testroot dh_clean -k -i dh_installdirs -i # Add here commands to install the indep part of the package into # debian/-doc. #INSTALLDOC# find docs/ -name "*.pdf" -exec cp {} debian/xorp-doc/usr/share/doc/xorp/doc/ \; dh_install -i install-arch: dh_testdir dh_testroot dh_clean -k -s dh_installdirs -s # Add here commands to install the arch part of the package into # debian/tmp. $(MAKE) install DESTDIR=$(CURDIR)/debian/xorp # After the install, we move the files and create the symlinks : # Binaries : find debian/xorp/usr/lib/xorp/bin/ -type f -exec mv -v {} debian/xorp/usr/sbin/ \; pushd debian/xorp/usr/sbin/ && find -type f -exec ln -vsf /usr/sbin/{} ../lib/xorp/bin/ \; && popd # Config files : cp rtrmgr/config.boot.sample debian/xorp/etc/xorp/config.boot cp debian/xorp.default debian/xorp/etc/default/xorp cp debian/xorp.init.d debian/xorp/etc/init.d/xorp dh_install -s # Must not depend on anything. This is to be called by # binary-arch/binary-indep # in another 'make' thread. binary-common: dh_testdir dh_testroot dh_installchangelogs dh_installdocs dh_installexamples # dh_installmenu # dh_installdebconf # dh_installlogrotate # dh_installemacsen # dh_installpam # dh_installmime # dh_installinit # dh_installcron # dh_installinfo dh_installman dh_link dh_strip dh_compress dh_fixperms # dh_perl # dh_python dh_makeshlibs dh_installdeb dh_shlibdeps dh_gencontrol dh_md5sums dh_builddeb # Build architecture independant packages using the common target. binary-indep: build-indep install-indep $(MAKE) -f debian/rules DH_OPTIONS=-i binary-common # Build architecture dependant packages using the common target. binary-arch: build-arch install-arch $(MAKE) -f debian/rules DH_OPTIONS=-a binary-common binary: binary-arch binary-indep .PHONY: build clean binary-indep binary-arch binary install install-indep install-arch xorp/contrib/packages/debian/control0000664000076400007640000000147311421137511017724 0ustar greearbgreearbSource: xorp Section: net Priority: optional Maintainer: Benjamin Sonntag Build-Depends: debhelper (>= 4.0.0), autotools-dev, libssl-dev, tetex-bin, tetex-extra Standards-Version: 3.7.2.0 Package: xorp Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends} Description: eXtensible Open Router Platform XORP is the eXtensible Open Router Platform. It implements a number of network protocols such as BGP, OSPF, RIP/RIPng, IGMP/MLD and PIM-SM. Package: xorp-doc Architecture: all Description: The documentation for XORP router platform XORP is the eXtensible Open Router Platform. It implements a number of network protocols such as BGP, OSPF, RIP/RIPng, IGMP/MLD and PIM-SM. This is the documentation for XORP in PDF format. It is also available from project's WEB site: http//www.xorp.org/ xorp/contrib/packages/debian/xorp.init.d0000775000076400007640000000375311421137511020426 0ustar greearbgreearb#!/bin/sh # # skeleton example file to build /etc/init.d/ scripts. # This file should be used to construct scripts for /etc/init.d. # # Written by Miquel van Smoorenburg . # Modified for Debian # by Ian Murdock . # # Version: @(#)skeleton 1.9 26-Feb-2001 miquels@cistron.nl # PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin DAEMON=/usr/sbin/xorp_rtrmgr NAME=xorp DESC=xorp test -x $DAEMON || exit 0 RUN="no" DAEMON_OPTS="" # Include xorp defaults if available if [ -f /etc/default/xorp ] ; then . /etc/default/xorp fi if [ "x$RUN" != "xyes" ] ; then echo "XORP disabled, change /etc/default/xorp to enable the router manager daemon !" exit 0 fi set -e case "$1" in start) echo -n "Starting $DESC: " start-stop-daemon --start --quiet --pidfile /var/run/$NAME.pid \ --exec $DAEMON -- $DAEMON_OPTS echo "$NAME." ;; stop) echo -n "Stopping $DESC: " start-stop-daemon --stop --quiet --pidfile /var/run/$NAME.pid \ --exec $DAEMON echo "$NAME." ;; #reload) # # If the daemon can reload its config files on the fly # for example by sending it SIGHUP, do it here. # # If the daemon responds to changes in its config file # directly anyway, make this a do-nothing entry. # # echo "Reloading $DESC configuration files." # start-stop-daemon --stop --signal 1 --quiet --pidfile \ # /var/run/$NAME.pid --exec $DAEMON #;; restart|force-reload) # # If the "reload" option is implemented, move the "force-reload" # option to the "reload" entry above. If not, "force-reload" is # just the same as "restart". # echo -n "Restarting $DESC: " start-stop-daemon --stop --quiet --pidfile \ /var/run/$NAME.pid --exec $DAEMON sleep 1 start-stop-daemon --start --quiet --pidfile \ /var/run/$NAME.pid --exec $DAEMON -- $DAEMON_OPTS echo "$NAME." ;; *) N=/etc/init.d/$NAME # echo "Usage: $N {start|stop|restart|reload|force-reload}" >&2 echo "Usage: $N {start|stop|restart|force-reload}" >&2 exit 1 ;; esac exit 0 xorp/contrib/packages/debian/copyright0000664000076400007640000000132011421137511020243 0ustar greearbgreearb# # $XORP: xorp/contrib/packages/debian/copyright,v 1.6 2008/10/02 21:56:39 bms Exp $ # With the exception of code derived from other sources, all XORP software is copyrighted by XORP, Inc. [Copyright (c) 2001-2009 XORP, Inc.]. Files containing derived software are listed in the "LICENSE.other" file together with their corresponding copyrights and original licenses. All XORP software is licensed under the GNU General Public License, Version 2, June 1991 contained in the "LICENSE.gpl" file unless otherwise indicated in the source file. Software in source files that refer to the "LICENSE.lgpl" file is licensed under the GNU Lesser General Public License, Version 2.1, February 1999 contained in "LICENSE.lgpl". xorp/contrib/packages/debian/changelog0000664000076400007640000000026211421137511020166 0ustar greearbgreearbxorp (1.2-1) stable; urgency=low * Initial Release. -- Benjamin Sonntag Sun, 4 Jun 2006 21:03:51 +0200 Local variables: mode: debian-changelog End: xorp/contrib/packages/README0000664000076400007640000000033611421137511015754 0ustar greearbgreearb# # $XORP: xorp/contrib/packages/README,v 1.1 2006/06/23 22:48:39 pavlin Exp $ # This directory contains XORP-related package information for various OS distributions: * debian: Debian GNU/Linux (http://www.debian.org/) xorp/contrib/init_scripts/0000775000076400007640000000000011421137511016026 5ustar greearbgreearbxorp/contrib/init_scripts/debian/0000775000076400007640000000000011542207017017253 5ustar greearbgreearbxorp/contrib/init_scripts/debian/xorp0000775000076400007640000001131711542207017020174 0ustar greearbgreearb#!/bin/sh # ### BEGIN INIT INFO # Provides: xorp # Required-Start: $network $local_fs # Required-Stop: # Should-Start: $named # Should-Stop: # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: eXtensible Open Router Platform # Description: XORP is the eXtensible Open Router Platform. It # implements a number of network protocols such as BGP, # OSPF, RIP/RIPng, IGMP/MLD and PIM-SM. ### END INIT INFO PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/xorp/sbin DAEMON=/usr/local/xorp/sbin/xorp_rtrmgr DAEMON_WRAPPER=/usr/bin/daemon NAME=xorp PIDFILE=/var/run/$NAME.pid DESC="eXtensible Open Router Platform" test -x $DAEMON || exit 0 if [ ! -x $DAEMON_WRAPPER ] then echo "ERROR: daemon program not installed: $DAEMON_WRAPPER" exit 1 fi . /lib/lsb/init-functions RUN="no" DAEMON_OPTS="-b /etc/xorp.conf" # time to wait for daemons death, in seconds # don't set it too low or you might not let xorp die gracefully DIETIME=10 LOGFILE=/var/log/xorp/router.log LOGFILE_ERR=/var/log/xorp/router.err.log # Include xorp defaults if available if [ -f /etc/default/xorp ] ; then . /etc/default/xorp fi set -e running_pid() { # Check if a given process pid's cmdline matches a given name pid=$1 name=$2 [ -z "$pid" ] && return 1 [ ! -d /proc/$pid ] && return 1 cmd=`cat /proc/$pid/cmdline | tr "\000" "\n"|head -n 1 |cut -d : -f 1` # Is this the expected child? [ "$cmd" != "$name" ] && return 1 return 0 } running_proc() { # Check if the process is running looking at /proc # (works for all users) # No pidfile, probably no daemon present [ ! -f "$PIDFILE" ] && return 1 pid=`cat $PIDFILE` running_pid $pid $DAEMON_WRAPPER || return 1 # The daemon exists, check also that the parent process (the wrapper) has a # child which is $DAEMON pidchild=`ps -eo ppid,pid |grep ^$pid | awk '{print $2}'| head -1` running_pid $pidchild $DAEMON || return 1 return 0 } running_daemon() { # Check if the process is running using 'daemon' # (only works for root) $DAEMON_WRAPPER --running --pidfile $PIDFILE \ --name=$NAME return $? } running() { # Check if the process is running # Use one function or other depending if we are root or not if [ "`id -u`" != "0" ] ; then running_proc return $? fi running_daemon return $? } start_xorp() { # Start the process using the wrapper $DAEMON_WRAPPER --pidfile $PIDFILE \ --name=$NAME \ --stdout=$LOGFILE \ --stderr=$LOGFILE_ERR \ -- $DAEMON $DAEMON_OPTS errcode=$? return $errcode } stop_xorp () { # Stop the process using the wrapper $DAEMON_WRAPPER --stop --pidfile $PIDFILE \ --name=$NAME \ errcode=$ return $errcode } force_stop() { # Force the process to die killing it manually [ ! -e "$PIDFILE" ] && return if running ; then kill -15 $pid # Is it really dead? sleep "$DIETIME"s if running ; then kill -9 $pid sleep "$DIETIME"s if running ; then echo "Cannot kill $NAME (pid=$pid)!" exit 1 fi fi fi rm -f $PIDFILE } case "$1" in start) log_daemon_msg "Starting $DESC " "$NAME" if running ; then log_progress_msg "apparently already running" log_end_msg 0 exit 0 fi if start_xorp && running ; then log_end_msg 0 else log_end_msg 1 fi ;; stop) log_daemon_msg "Stopping $DESC" "$NAME" if running ; then stop_xorp log_end_msg $? else log_progress_msg "apparently not running" log_end_msg 0 exit 0 fi ;; reload) log_warning_msg "Reloading $NAME daemon: not implemented, as the daemon" log_warning_msg "cannot re-read the config file (use restart)." ;; force-stop) # First try to stop gracefully $0 stop if running; then log_daemon_msg "Stopping (force) $DESC" "$NAME" force_stop log_end_msg $? fi ;; restart|force-reload) log_daemon_msg "Restarting $DESC" "$NAME" stop_xorp sleep $DIETIME start_xorp running log_end_msg $? ;; status) log_daemon_msg "Checking status of $DESC" "$NAME" if running ; then log_progress_msg "running" log_end_msg 0 else log_progress_msg "apparently not running" log_end_msg 1 exit 1 fi ;; *) N=/etc/init.d/$NAME echo "Usage: $N {start|stop|restart|force-reload|status}" >&2 exit 1 ;; esac exit 0 xorp/contrib/init_scripts/README0000664000076400007640000000144611421137511016713 0ustar greearbgreearb# # $XORP$ # This directory contains XORP-related startup information for various OS distributions: * debian: Debian GNU/Linux (http://www.debian.org/): - The debian/xorp script should be added to the /etc/init.d/ directory. However, future distributions of Debian will include that script, therefore the script needs to be added only if it is not there. - The script uses the "daemon" program (http://www.libslack.org/daemon/), which can be used to send stdout/stderr to a logfile or syslog. It also takes care of running the process in background, generate its pidfile, chroot() the daemon, close file descriptors, set umask, respawn if it dies, etc. The "daemon" program is fairly portable, therefore a similar solution can be used for other OS distributions. xorp/contrib/mld6igmp_lite/0000775000076400007640000000000011540224221016045 5ustar greearbgreearbxorp/contrib/mld6igmp_lite/mld6igmp_node_cli.cc0000664000076400007640000003013711421137511021736 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // The Lightweight IGMP/MLD modifications to this file are copyrighted by: // // Copyright (c) 2008 Huawei Technologies Co. Ltd // // // MLD6IGMP protocol CLI implementation // #include "mld6igmp_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvx.hh" #include "mld6igmp_group_record.hh" #include "mld6igmp_node.hh" #include "mld6igmp_node_cli.hh" #include "mld6igmp_vif.hh" // // Exported variables // // // Local constants definitions // // // Local structures/classes, typedefs and macros // // // Local variables // // // Local functions prototypes // /** * Mld6igmpNodeCli::Mld6igmpNodeCli: * @mld6igmp_node: The MLD6IGMP node to use. * * Mld6igmpNodeCli constructor. **/ Mld6igmpNodeCli::Mld6igmpNodeCli(Mld6igmpNode& mld6igmp_node) : ProtoNodeCli(mld6igmp_node.family(), mld6igmp_node.module_id()), _mld6igmp_node(mld6igmp_node) { } Mld6igmpNodeCli::~Mld6igmpNodeCli() { stop(); } int Mld6igmpNodeCli::start() { if (! is_enabled()) return (XORP_OK); if (is_up() || is_pending_up()) return (XORP_OK); if (ProtoUnit::start() != XORP_OK) return (XORP_ERROR); if (add_all_cli_commands() != XORP_OK) return (XORP_ERROR); XLOG_INFO("CLI started"); return (XORP_OK); } int Mld6igmpNodeCli::stop() { int ret_code = XORP_OK; if (is_down()) return (XORP_OK); if (delete_all_cli_commands() != XORP_OK) ret_code = XORP_ERROR; XLOG_INFO("CLI stopped"); return (ret_code); } /** * Enable the node operation. * * If an unit is not enabled, it cannot be start, or pending-start. */ void Mld6igmpNodeCli::enable() { ProtoUnit::enable(); XLOG_INFO("CLI 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 Mld6igmpNodeCli::disable() { stop(); ProtoUnit::disable(); XLOG_INFO("CLI disabled"); } int Mld6igmpNodeCli::add_all_cli_commands() { // XXX: command "show" must have been installed by the CLI itself. if (mld6igmp_node().proto_is_igmp()) { add_cli_dir_command("show igmp", "Display information about IGMP"); add_cli_command("show igmp group", "Display information about IGMP group membership", callback(this, &Mld6igmpNodeCli::cli_show_mld6igmp_group)); add_cli_command("show igmp interface", "Display information about IGMP interfaces", callback(this, &Mld6igmpNodeCli::cli_show_mld6igmp_interface)); add_cli_command("show igmp interface address", "Display information about addresses of IGMP interfaces", callback(this, &Mld6igmpNodeCli::cli_show_mld6igmp_interface_address)); } if (mld6igmp_node().proto_is_mld6()) { add_cli_dir_command("show mld", "Display information about MLD"); add_cli_command("show mld group", "Display information about MLD group membership", callback(this, &Mld6igmpNodeCli::cli_show_mld6igmp_group)); add_cli_command("show mld interface", "Display information about MLD interfaces", callback(this, &Mld6igmpNodeCli::cli_show_mld6igmp_interface)); add_cli_command("show mld interface address", "Display information about addresses of MLD interfaces", callback(this, &Mld6igmpNodeCli::cli_show_mld6igmp_interface_address)); } return (XORP_OK); } // // CLI COMMAND: "show mld interface [interface-name]" // CLI COMMAND: "show igmp interface [interface-name]" // // Display information about the interfaces on which MLD/IGMP is configured. // int Mld6igmpNodeCli::cli_show_mld6igmp_interface(const vector& argv) { string interface_name; // Check the optional argument if (argv.size()) { interface_name = argv[0]; if (mld6igmp_node().vif_find_by_name(interface_name.c_str()) == NULL) { cli_print(c_format("ERROR: Invalid interface name: %s\n", interface_name.c_str())); return (XORP_ERROR); } } cli_print(c_format("%-12s %-8s %-15s %7s %7s %6s\n", "Interface", "State", "Querier", "Timeout", "Version", "Groups")); for (uint32_t i = 0; i < mld6igmp_node().maxvifs(); i++) { Mld6igmpVif *mld6igmp_vif = mld6igmp_node().vif_find_by_vif_index(i); if (mld6igmp_vif == NULL) continue; // Test if we should print this entry bool do_print = true; if (interface_name.size()) { do_print = false; if (mld6igmp_vif->name() == interface_name) { do_print = true; } } if (! do_print) continue; string querier_timeout_sec_string; if (mld6igmp_vif->const_other_querier_timer().scheduled()) { TimeVal tv; mld6igmp_vif->const_other_querier_timer().time_remaining(tv); querier_timeout_sec_string = c_format("%d", XORP_INT_CAST(tv.sec())); } else { querier_timeout_sec_string = "None"; } cli_print(c_format("%-12s %-8s %-15s %7s %7d %6u\n", mld6igmp_vif->name().c_str(), mld6igmp_vif->state_str().c_str(), cstring(mld6igmp_vif->querier_addr()), querier_timeout_sec_string.c_str(), mld6igmp_vif->proto_version(), XORP_UINT_CAST(mld6igmp_vif->group_records().size()))); } return (XORP_OK); } // // CLI COMMAND: "show mld interface address [interface-name]" // CLI COMMAND: "show igmp interface address [interface-name]" // // Display information about the addresses of MLD/IGMP interfaces // int Mld6igmpNodeCli::cli_show_mld6igmp_interface_address(const vector& argv) { string interface_name; // Check the optional argument if (argv.size()) { interface_name = argv[0]; if (mld6igmp_node().vif_find_by_name(interface_name) == NULL) { cli_print(c_format("ERROR: Invalid interface name: %s\n", interface_name.c_str())); return (XORP_ERROR); } } cli_print(c_format("%-12s %-15s %-15s\n", "Interface", "PrimaryAddr", "SecondaryAddr")); for (uint32_t i = 0; i < mld6igmp_node().maxvifs(); i++) { Mld6igmpVif *mld6igmp_vif = mld6igmp_node().vif_find_by_vif_index(i); if (mld6igmp_vif == NULL) continue; // Test if we should print this entry bool do_print = true; if (interface_name.size()) { do_print = false; if (mld6igmp_vif->name() == interface_name) { do_print = true; } } if (! do_print) continue; // // Create a list with all secondary addresses // list secondary_addr_list; list::const_iterator vif_addr_iter; for (vif_addr_iter = mld6igmp_vif->addr_list().begin(); vif_addr_iter != mld6igmp_vif->addr_list().end(); ++vif_addr_iter) { const VifAddr& vif_addr = *vif_addr_iter; if (vif_addr.addr() == mld6igmp_vif->primary_addr()) continue; secondary_addr_list.push_back(vif_addr.addr()); } cli_print(c_format("%-12s %-15s %-15s\n", mld6igmp_vif->name().c_str(), cstring(mld6igmp_vif->primary_addr()), (secondary_addr_list.size())? cstring(secondary_addr_list.front()): "")); // Pop the first secondary address if (secondary_addr_list.size()) secondary_addr_list.pop_front(); // // Print the rest of the secondary addresses // list::iterator secondary_addr_iter; for (secondary_addr_iter = secondary_addr_list.begin(); secondary_addr_iter != secondary_addr_list.end(); ++secondary_addr_iter) { IPvX& secondary_addr = *secondary_addr_iter; cli_print(c_format("%-12s %-15s %-15s\n", " ", " ", cstring(secondary_addr))); } } return (XORP_OK); } // // CLI COMMAND: "show mld group [group-name [...]]" // CLI COMMAND: "show igmp group [group-name [...]]" // // Display information about MLD/IGMP group membership. // int Mld6igmpNodeCli::cli_show_mld6igmp_group(const vector& argv) { vector groups; // Check the (optional) arguments, and create an array of groups to test for (size_t i = 0; i < argv.size(); i++) { try { IPvX g(argv[i].c_str()); if (g.af() != family()) { cli_print(c_format("ERROR: Address with invalid address family: %s\n", argv[i].c_str())); return (XORP_ERROR); } if (! g.is_multicast()) { cli_print(c_format("ERROR: Not a multicast address: %s\n", argv[i].c_str())); return (XORP_ERROR); } groups.push_back(g); } catch (InvalidString) { cli_print(c_format("ERROR: Invalid IP address: %s\n", argv[i].c_str())); return (XORP_ERROR); } } cli_print(c_format("%-12s %-15s %-15s %-12s %7s %1s %5s\n", "Interface", "Group", "Source", "LastReported", "Timeout", "V", "State")); for (uint32_t i = 0; i < mld6igmp_node().maxvifs(); i++) { const Mld6igmpVif *mld6igmp_vif = mld6igmp_node().vif_find_by_vif_index(i); if (mld6igmp_vif == NULL) continue; Mld6igmpGroupSet::const_iterator group_iter; for (group_iter = mld6igmp_vif->group_records().begin(); group_iter != mld6igmp_vif->group_records().end(); ++group_iter) { const Mld6igmpGroupRecord *group_record = group_iter->second; Mld6igmpSourceSet::const_iterator source_iter; int version = 0; string state; // Test if we should print this entry bool do_print = true; if (groups.size()) { do_print = false; for (size_t j = 0; j < groups.size(); j++) { if (groups[j] == group_record->group()) { do_print = true; break; } } } if (! do_print) continue; // Calcuate the group entry version do { version = 0; if (mld6igmp_vif->is_igmpv1_mode(group_record)) { version = 1; break; } if (mld6igmp_vif->is_igmpv2_mode(group_record)) { version = 2; break; } if (mld6igmp_vif->is_igmpv3_mode(group_record)) { version = 3; break; } if (mld6igmp_vif->is_mldv1_mode(group_record)) { version = 1; break; } if (mld6igmp_vif->is_mldv2_mode(group_record)) { version = 2; break; } break; } while (false); XLOG_ASSERT(version > 0); // // The state: // - "I" = INCLUDE (for group entry) // - "E" = EXCLUDE (for group entry) // - "F" = Forward (for source entry) // - "D" = Don't forward (for source entry) // // The group state if (group_record->is_include_mode()) state = "I"; if (group_record->is_asm_mode()) state = "E"; // Print the group-specific output cli_print(c_format("%-6s %-15s %-15s %-12s %7d %1d %2s %5d %5d\n", mld6igmp_vif->name().c_str(), cstring(group_record->group()), cstring(IPvX::ZERO(family())), cstring(group_record->last_reported_host()), XORP_INT_CAST(group_record->timeout_sec()), version, state.c_str(), XORP_INT_CAST(group_record->igmpv1_host_present_timer_timeout_sec()), XORP_INT_CAST(group_record->igmpv2_mldv1_host_present_timer_timeout_sec()) )); // Print the sources to forward state = "F"; for (source_iter = group_record->do_forward_sources().begin(); source_iter != group_record->do_forward_sources().end(); ++source_iter) { const Mld6igmpSourceRecord *source_record = source_iter->second; cli_print(c_format("%-12s %-15s %-15s %-12s %7d %1d %5s\n", mld6igmp_vif->name().c_str(), cstring(group_record->group()), cstring(source_record->source()), cstring(group_record->last_reported_host()), XORP_INT_CAST(source_record->timeout_sec()), version, state.c_str())); } } } return (XORP_OK); } xorp/contrib/mld6igmp_lite/mld6igmp_proto.cc0000664000076400007640000007143111540224221021324 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // Internet Group Management Protocol implementation. // IGMPv1, IGMPv2 (RFC 2236), and IGMPv3 (RFC 3376). // // AND // // Multicast Listener Discovery protocol implementation. // MLDv1 (RFC 2710) and MLDv2 (RFC 3810). // #include "mld6igmp_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvx.hh" #include "mld6igmp_vif.hh" // // Exported variables // // // Local constants definitions // // // Local structures/classes, typedefs and macros // // // Local variables // // // Local functions prototypes // /** * Mld6igmpVif::mld6igmp_membership_query_recv: * @src: The message source address. * @dst: The message destination address. * @message_type: The message type. * @max_resp_code: The Maximum Response Code from the MLD/IGMP header. * @group_address: The Group Address from the MLD/IGMP message. * @buffer: The buffer with the rest of the message. * * Receive and process IGMP_MEMBERSHIP_QUERY/MLD_LISTENER_QUERY message * from another router. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int Mld6igmpVif::mld6igmp_membership_query_recv(const IPvX& src, const IPvX& dst, uint8_t message_type, uint16_t max_resp_code, const IPvX& group_address, buffer_t *buffer) { int message_version = 0; // Ignore my own queries if (mld6igmp_node().is_my_addr(src)) return (XORP_ERROR); // // Determine the protocol version of the Query message // if (proto_is_igmp()) { // // The IGMP version of a Membership Query message is: // - IGMPv1 Query: length = 8 AND Max Resp Code field is zero // - IGMPv2 Query: length = 8 AND Max Resp Code field is non-zero // - IGMPv3 Query: length >= 12 // do { // // Note that the Query processing so far has decoded the message // up to the group address included (i.e., 8 octets), hence // we add-back the size of the decoded portion. // size_t data_size = BUFFER_DATA_SIZE(buffer) + IGMP_MINLEN; if ((data_size == IGMP_MINLEN) && (max_resp_code == 0)) { message_version = IGMP_V1; break; } if ((data_size == IGMP_MINLEN) && (max_resp_code != 0)) { message_version = IGMP_V2; break; } if (data_size >= IGMP_V3_QUERY_MINLEN) { message_version = IGMP_V3; break; } // // Silently ignore all other Query messages that don't match // any of the above conditions. // return (XORP_ERROR); } while (false); XLOG_ASSERT(message_version > 0); // // Query version consistency check. // If there is mismatch, then drop the message. // See RFC 3376 Section 7.3.1, and RFC 3810 Section 8.3.1. // if (mld6igmp_query_version_consistency_check(src, dst, message_type, message_version) != XORP_OK) { return (XORP_ERROR); } } if (proto_is_mld6()) { // // The MLD version of a Membership Query message is: // - MLDv1 Query: length = 24 // - MLDv2 Query: length >= 28 // do { // // Note that the Query processing so far has decoded the message // up to the group address included (i.e., 24 octets), hence // we add-back the size of the decoded portion. // size_t data_size = BUFFER_DATA_SIZE(buffer) + MLD_MINLEN; if (data_size == MLD_MINLEN) { message_version = MLD_V1; break; } if (data_size >= MLD_V2_QUERY_MINLEN) { message_version = MLD_V2; break; } // // Silently ignore all other Query messages that don't match // any of the above conditions. // return (XORP_ERROR); } while (false); XLOG_ASSERT(message_version > 0); // // Query version consistency check. // If there is mismatch, then drop the message. // See RFC 3376 Section 7.3.1, and RFC 3810 Section 8.3.1. // if (mld6igmp_query_version_consistency_check(src, dst, message_type, message_version) != XORP_OK) { return (XORP_ERROR); } } XLOG_ASSERT(message_version > 0); // // Compare this querier address with my address. // XXX: Here we should compare the old and new querier // addresses, but we don't really care. // XLOG_ASSERT(primary_addr() != IPvX::ZERO(family())); if (src < primary_addr()) { // Eventually a new querier _query_timer.unschedule(); set_querier_addr(src); set_i_am_querier(false); TimeVal other_querier_present_interval = effective_query_interval() * effective_robustness_variable() + query_response_interval().get() / 2; _other_querier_timer = mld6igmp_node().eventloop().new_oneoff_after( other_querier_present_interval, callback(this, &Mld6igmpVif::other_querier_timer_timeout)); } // // XXX: if this is IGMPv3 or MLDv2 Query, then process separately // the rest the message. // if ((proto_is_igmp() && (message_version >= IGMP_V3)) || (proto_is_mld6() && (message_version >= MLD_V2))) { mld6igmp_ssm_membership_query_recv(src, dst, message_type, max_resp_code, group_address, buffer); return (XORP_OK); } // // From RFC 2236: // "When a non-Querier receives a Group-Specific Query message, if its // existing group membership timer is greater than [Last Member Query // Count] times the Max Response Time specified in the message, it sets // its group membership timer to that value." // // // From RFC 2710: // "When a router in Non-Querier state receives a Multicast-Address- // Specific Query, if its timer value for the identified multicast // address is greater than [Last Listener Query Count] times the Maximum // Response Delay specified in the message, it sets the address's timer // to that latter value." // if ( (! group_address.is_zero()) && (max_resp_code != 0) && (! i_am_querier())) { uint32_t timer_scale = mld6igmp_constant_timer_scale(); TimeVal received_resp_tv; // "Last Member Query Count" / "Last Listener Query Count" received_resp_tv = TimeVal(effective_robustness_variable() * max_resp_code, 0); received_resp_tv = received_resp_tv / timer_scale; _group_records.lower_group_timer(group_address, received_resp_tv); } return (XORP_OK); } /** * Mld6igmpVif::mld6igmp_ssm_membership_query_recv: * @src: The message source address. * @dst: The message destination address. * @message_type: The message type. * @max_resp_code: The Maximum Response Code from the MLD/IGMP header. * @group_address: The Group Address from the MLD/IGMP message. * @buffer: The buffer with the rest of the message. * * Receive and process IGMPv3/MLDv2 IGMP_MEMBERSHIP_QUERY/MLD_LISTENER_QUERY * message from another router. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int Mld6igmpVif::mld6igmp_ssm_membership_query_recv(const IPvX& src, const IPvX& dst, uint8_t message_type, uint16_t max_resp_code, const IPvX& group_address, buffer_t *buffer) { bool s_flag = false; uint8_t qrv = 0; uint8_t qqic = 0; uint16_t sources_n = 0; TimeVal max_resp_time, qqi; set sources; string error_msg; // // Decode the Max Resp Code // if (proto_is_igmp()) { decode_exp_time_code8(max_resp_code, max_resp_time, mld6igmp_constant_timer_scale()); } if (proto_is_mld6()) { decode_exp_time_code16(max_resp_code, max_resp_time, mld6igmp_constant_timer_scale()); } // // Decode the rest of the message header // BUFFER_GET_OCTET(qrv, buffer); BUFFER_GET_OCTET(qqic, buffer); BUFFER_GET_HOST_16(sources_n, buffer); if (proto_is_igmp()) { s_flag = IGMP_SFLAG(qrv); qrv = IGMP_QRV(qrv); } if (proto_is_mld6()) { s_flag = MLD_SFLAG(qrv); qrv = MLD_QRV(qrv); } decode_exp_time_code8(qqic, qqi, 1); // // Check the remaining size of the message // if (BUFFER_DATA_SIZE(buffer) < sources_n * IPvX::addr_bytelen(family())) { error_msg = c_format("RX %s from %s to %s on vif %s: " "source addresses array size too short" "(received %u expected at least %u)", proto_message_type2ascii(message_type), cstring(src), cstring(dst), name().c_str(), XORP_UINT_CAST(BUFFER_DATA_SIZE(buffer)), XORP_UINT_CAST(sources_n * IPvX::addr_bytelen(family()))); XLOG_WARNING("%s", error_msg.c_str()); return (XORP_ERROR); } // // Decode the array of source addresses // while (sources_n != 0) { IPvX ipvx(family()); BUFFER_GET_IPVX(family(), ipvx, buffer); sources.insert(ipvx); sources_n--; } // // Adopt the Querier's Robustness Variable and Query Interval // if (! i_am_querier()) { if (qrv != 0) { set_effective_robustness_variable(qrv); } else { set_effective_robustness_variable(configured_robust_count().get()); } if (qqi != TimeVal::ZERO()) { set_effective_query_interval(qqi); } else { set_effective_query_interval(configured_query_interval().get()); } } // // Lower the group and source timers // if (! s_flag) { if (sources.empty()) { // XXX: Q(G) Query _group_records.lower_group_timer(group_address, last_member_query_time()); } else { // XXX: Q(G, A) Query _group_records.lower_source_timer(group_address, sources, last_member_query_time()); } } return (XORP_OK); rcvlen_error: XLOG_UNREACHABLE(); error_msg = c_format("RX %s from %s to %s on vif %s: " "some fields are too short", proto_message_type2ascii(message_type), cstring(src), cstring(dst), name().c_str()); XLOG_WARNING("%s", error_msg.c_str()); return (XORP_ERROR); } /** * Mld6igmpVif::mld6igmp_membership_report_recv: * @src: The message source address. * @dst: The message destination address. * @message_type: The message type. * @max_resp_code: The Maximum Response Code from the MLD/IGMP header. * @group_address: The Group Address from the MLD/IGMP message. * @buffer: The buffer with the rest of the message. * * Receive and process * (IGMP_V1_MEMBERSHIP_REPORT or IGMP_V2_MEMBERSHIP_REPORT)/MLD_LISTENER_REPORT * message from a host. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int Mld6igmpVif::mld6igmp_membership_report_recv(const IPvX& src, const IPvX& dst, uint8_t message_type, uint16_t max_resp_code, const IPvX& group_address, buffer_t *buffer) { Mld6igmpGroupRecord *group_record = NULL; // The group address must be a valid multicast address if (! group_address.is_multicast()) { XLOG_WARNING("RX %s from %s to %s on vif %s: " "the group address %s is not " "valid multicast address", proto_message_type2ascii(message_type), cstring(src), cstring(dst), name().c_str(), cstring(group_address)); return (XORP_ERROR); } set no_sources; // XXX: empty set group_records().process_mode_is_exclude(group_address, no_sources, src); // // Check whether an older Membership report has been received // int message_version = 0; if (proto_is_igmp()) { switch (message_type) { case IGMP_V1_MEMBERSHIP_REPORT: message_version = IGMP_V1; break; case IGMP_V2_MEMBERSHIP_REPORT: message_version = IGMP_V2; break; case IGMP_V3_MEMBERSHIP_REPORT: message_version = IGMP_V3; break; default: message_version = IGMP_V2; break; } } if (proto_is_mld6()) { switch (message_type) { case MLD_LISTENER_REPORT: message_version = MLD_V1; break; case MLDV2_LISTENER_REPORT: message_version = MLD_V2; break; default: message_version = MLD_V1; break; } } XLOG_ASSERT(message_version > 0); group_record = _group_records.find_group_record(group_address); XLOG_ASSERT(group_record != NULL); group_record->received_older_membership_report(message_version); UNUSED(max_resp_code); UNUSED(buffer); return (XORP_OK); } /** * Mld6igmpVif::mld6igmp_leave_group_recv: * @src: The message source address. * @dst: The message destination address. * @message_type: The message type. * @max_resp_code: The Maximum Response Code from the MLD/IGMP header. * @group_address: The Group Address from the MLD/IGMP message. * @buffer: The buffer with the rest of the message. * * Receive and process IGMP_V2_LEAVE_GROUP/MLD_LISTENER_DONE message * from a host. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int Mld6igmpVif::mld6igmp_leave_group_recv(const IPvX& src, const IPvX& dst, uint8_t message_type, uint16_t max_resp_code, const IPvX& group_address, buffer_t *buffer) { Mld6igmpGroupRecord *group_record = NULL; string dummy_error_msg; // The group address must be a valid multicast address if (! group_address.is_multicast()) { XLOG_WARNING("RX %s from %s to %s on vif %s: " "the group address %s is not " "valid multicast address", proto_message_type2ascii(message_type), cstring(src), cstring(dst), name().c_str(), cstring(group_address)); return (XORP_ERROR); } // // Find if we already have an entry for this group // group_record = _group_records.find_group_record(group_address); if (group_record == NULL) { // Nothing found. Ignore. return (XORP_OK); } // // Group found // if (is_igmpv1_mode(group_record)) { // // Ignore this 'Leave Group' message because this // group has IGMPv1 hosts members. // return (XORP_OK); } set no_sources; // XXX: empty set group_records().process_change_to_include_mode(group_address, no_sources, src); return (XORP_OK); UNUSED(max_resp_code); UNUSED(buffer); return (XORP_OK); } /** * Mld6igmpVif::mld6igmp_ssm_membership_report_recv: * @src: The message source address. * @dst: The message destination address. * @message_type: The message type. * @buffer: The buffer with the rest of the message. * * Receive and process IGMP_V3_MEMBERSHIP_REPORT/MLDV2_LISTENER_REPORT * message from a host. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int Mld6igmpVif::mld6igmp_ssm_membership_report_recv(const IPvX& src, const IPvX& dst, uint8_t message_type, buffer_t *buffer) { uint16_t group_records_n = 0; string error_msg; typedef pair > gs_record; // XXX: a handy typedef list mode_is_include_groups; list mode_is_exclude_groups; list change_to_include_mode_groups; list change_to_exclude_mode_groups; list allow_new_sources_groups; list block_old_sources_groups; list::iterator gs_iter; // // Decode the rest of the message header // BUFFER_GET_SKIP(2, buffer); // The 'Reserved' field BUFFER_GET_HOST_16(group_records_n, buffer); // // Decode the array of group records // while (group_records_n != 0) { uint8_t record_type; uint8_t aux_data_len; uint16_t sources_n; IPvX group_address(family()); set source_addresses; list* gs_record_ptr = NULL; BUFFER_GET_OCTET(record_type, buffer); BUFFER_GET_OCTET(aux_data_len, buffer); BUFFER_GET_HOST_16(sources_n, buffer); BUFFER_GET_IPVX(family(), group_address, buffer); // Decode the array of source addresses while (sources_n != 0) { IPvX ipvx(family()); BUFFER_GET_IPVX(family(), ipvx, buffer); source_addresses.insert(ipvx); sources_n--; } // XXX: Skip the 'Auxiliary Data', because we don't use it BUFFER_GET_SKIP(aux_data_len, buffer); // // Select the appropriate set, and add the group and the sources to it // if (proto_is_igmp()) { switch (record_type) { case IGMP_MODE_IS_INCLUDE: gs_record_ptr = &mode_is_include_groups; break; case IGMP_MODE_IS_EXCLUDE: gs_record_ptr = &mode_is_exclude_groups; break; case IGMP_CHANGE_TO_INCLUDE_MODE: gs_record_ptr = &change_to_include_mode_groups; break; case IGMP_CHANGE_TO_EXCLUDE_MODE: gs_record_ptr = &change_to_exclude_mode_groups; break; case IGMP_ALLOW_NEW_SOURCES: gs_record_ptr = &allow_new_sources_groups; break; case IGMP_BLOCK_OLD_SOURCES: gs_record_ptr = &block_old_sources_groups; break; default: break; } } if (proto_is_mld6()) { switch (record_type) { case MLD_MODE_IS_INCLUDE: gs_record_ptr = &mode_is_include_groups; break; case MLD_MODE_IS_EXCLUDE: gs_record_ptr = &mode_is_exclude_groups; break; case MLD_CHANGE_TO_INCLUDE_MODE: gs_record_ptr = &change_to_include_mode_groups; break; case MLD_CHANGE_TO_EXCLUDE_MODE: gs_record_ptr = &change_to_exclude_mode_groups; break; case MLD_ALLOW_NEW_SOURCES: gs_record_ptr = &allow_new_sources_groups; break; case MLD_BLOCK_OLD_SOURCES: gs_record_ptr = &block_old_sources_groups; break; default: break; } } if (gs_record_ptr != NULL) { gs_record_ptr->push_back(make_pair(group_address, source_addresses)); } else { error_msg = c_format("RX %s from %s to %s on vif %s: " "unrecognized record type %d (ignored)", proto_message_type2ascii(message_type), cstring(src), cstring(dst), name().c_str(), record_type); XLOG_WARNING("%s", error_msg.c_str()); } group_records_n--; } // // Process the records // for (gs_iter = mode_is_include_groups.begin(); gs_iter != mode_is_include_groups.end(); ++gs_iter) { group_records().process_mode_is_include(gs_iter->first, gs_iter->second, src); } for (gs_iter = mode_is_exclude_groups.begin(); gs_iter != mode_is_exclude_groups.end(); ++gs_iter) { group_records().process_mode_is_exclude(gs_iter->first, gs_iter->second, src); } for (gs_iter = change_to_include_mode_groups.begin(); gs_iter != change_to_include_mode_groups.end(); ++gs_iter) { group_records().process_change_to_include_mode(gs_iter->first, gs_iter->second, src); } for (gs_iter = change_to_exclude_mode_groups.begin(); gs_iter != change_to_exclude_mode_groups.end(); ++gs_iter) { group_records().process_change_to_exclude_mode(gs_iter->first, gs_iter->second, src); } for (gs_iter = allow_new_sources_groups.begin(); gs_iter != allow_new_sources_groups.end(); ++gs_iter) { group_records().process_allow_new_sources(gs_iter->first, gs_iter->second, src); } for (gs_iter = block_old_sources_groups.begin(); gs_iter != block_old_sources_groups.end(); ++gs_iter) { group_records().process_block_old_sources(gs_iter->first, gs_iter->second, src); } return (XORP_OK); rcvlen_error: error_msg = c_format("RX %s from %s to %s on vif %s: " "some fields are too short", proto_message_type2ascii(message_type), cstring(src), cstring(dst), name().c_str()); XLOG_WARNING("%s", error_msg.c_str()); return (XORP_ERROR); } /** * Mld6igmpVif::other_querier_timer_timeout: * * Timeout: the previous querier has expired. I will become the querier. **/ void Mld6igmpVif::other_querier_timer_timeout() { string dummy_error_msg; if (primary_addr() == IPvX::ZERO(family())) { // XXX: the vif address is unknown; this cannot happen if the // vif status is UP. XLOG_ASSERT(! is_up()); return; } set_querier_addr(primary_addr()); set_i_am_querier(true); // // Now I am the querier. Send a general membership query. // TimeVal max_resp_time = query_response_interval().get(); set no_sources; // XXX: empty set mld6igmp_query_send(primary_addr(), IPvX::MULTICAST_ALL_SYSTEMS(family()), max_resp_time, IPvX::ZERO(family()), // XXX: ANY no_sources, false, dummy_error_msg); _startup_query_count = 0; // XXX: not a startup case _query_timer = mld6igmp_node().eventloop().new_oneoff_after( effective_query_interval(), callback(this, &Mld6igmpVif::query_timer_timeout)); } /** * Mld6igmpVif::query_timer_timeout: * * Timeout: time to send a membership query. **/ void Mld6igmpVif::query_timer_timeout() { TimeVal interval; string dummy_error_msg; if (! i_am_querier()) return; // I am not the querier anymore. Ignore. // // Send a general membership query // TimeVal max_resp_time = query_response_interval().get(); set no_sources; // XXX: empty set mld6igmp_query_send(primary_addr(), IPvX::MULTICAST_ALL_SYSTEMS(family()), max_resp_time, IPvX::ZERO(family()), // XXX: ANY no_sources, false, dummy_error_msg); if (_startup_query_count > 0) _startup_query_count--; if (_startup_query_count > 0) { // "Startup Query Interval" interval = effective_query_interval() / 4; } else { interval = effective_query_interval(); } _query_timer = mld6igmp_node().eventloop().new_oneoff_after( interval, callback(this, &Mld6igmpVif::query_timer_timeout)); } /** * mld6igmp_query_version_consistency_check: * @src: The message source address. * @dst: The message destination address. * @message_type: The type of the MLD/IGMP message. * @message_version: The protocol version of the received Query message: * (IGMP_V1, IGMP_V2, IGMP_V3 for IGMP) or (MLD_V1, MLD_V2 for MLD). * * Check for MLD/IGMP protocol version interface configuration consistency. * For example, if the received Query message was IGMPv1, a correctly * configured local interface must be operating in IGMPv1 mode. * Similarly, if the local interface is operating in IGMPv1 mode, * all other neighbor routers (for that interface) must be * operating in IGMPv1 as well. * * Return value: %XORP_OK if consistency, otherwise %XORP_ERROR. **/ int Mld6igmpVif::mld6igmp_query_version_consistency_check(const IPvX& src, const IPvX& dst, uint8_t message_type, int message_version) { string proto_name, mode_config, mode_received; if (message_version == proto_version()) return (XORP_OK); if (proto_is_igmp()) proto_name = "IGMP"; if (proto_is_mld6()) proto_name = "MLD"; mode_config = c_format("%sv%u", proto_name.c_str(), proto_version()); mode_received = c_format("%sv%u", proto_name.c_str(), message_version); // TODO: rate-limit the warning XLOG_WARNING("RX %s from %s to %s on vif %s: " "this interface is in %s mode, but received %s message", proto_message_type2ascii(message_type), cstring(src), cstring(dst), name().c_str(), mode_config.c_str(), mode_received.c_str()); XLOG_WARNING("Please configure properly all routers on " "that subnet to use same %s version", proto_name.c_str()); return (XORP_ERROR); } void Mld6igmpVif::set_configured_query_interval_cb(TimeVal v) { set_effective_query_interval(v); } void Mld6igmpVif::set_effective_query_interval(const TimeVal& v) { _effective_query_interval = v; recalculate_effective_query_interval(); } void Mld6igmpVif::recalculate_effective_query_interval() { recalculate_group_membership_interval(); recalculate_older_version_host_present_interval(); } void Mld6igmpVif::set_query_last_member_interval_cb(TimeVal v) { UNUSED(v); recalculate_last_member_query_time(); } void Mld6igmpVif::set_query_response_interval_cb(TimeVal v) { UNUSED(v); recalculate_group_membership_interval(); recalculate_older_version_host_present_interval(); } void Mld6igmpVif::set_configured_robust_count_cb(uint32_t v) { set_effective_robustness_variable(v); } void Mld6igmpVif::set_effective_robustness_variable(uint32_t v) { _effective_robustness_variable = v; recalculate_effective_robustness_variable(); } void Mld6igmpVif::recalculate_effective_robustness_variable() { recalculate_group_membership_interval(); recalculate_last_member_query_count(); recalculate_older_version_host_present_interval(); } void Mld6igmpVif::recalculate_last_member_query_count() { _last_member_query_count = effective_robustness_variable(); recalculate_last_member_query_time(); } void Mld6igmpVif::recalculate_group_membership_interval() { _group_membership_interval = effective_query_interval() * effective_robustness_variable() + query_response_interval().get(); } void Mld6igmpVif::recalculate_last_member_query_time() { _last_member_query_time = query_last_member_interval().get() * last_member_query_count(); } void Mld6igmpVif::recalculate_older_version_host_present_interval() { _older_version_host_present_interval = effective_query_interval() * effective_robustness_variable() + query_response_interval().get(); } void Mld6igmpVif::restore_effective_variables() { // Restore the default Query Interval and Robustness Variable set_effective_robustness_variable(configured_robust_count().get()); set_effective_query_interval(configured_query_interval().get()); } void Mld6igmpVif::decode_exp_time_code8(uint8_t code, TimeVal& timeval, uint32_t timer_scale) { uint32_t decoded_time = 0; // // From RFC 3376 Section 4.1.1, and RFC 3810 Section 5.1.9: // // If Code < 128, Time = Code // // If Code >= 128, Code represents a floating-point value as follows: // // 0 1 2 3 4 5 6 7 // +-+-+-+-+-+-+-+-+ // |1| exp | mant | // +-+-+-+-+-+-+-+-+ // // Time = (mant | 0x10) << (exp + 3) // if (code < 128) { decoded_time = code; } else { uint8_t mant = code & 0xf; uint8_t exp = (code >> 4) & 0x7; decoded_time = (mant | 0x10) << (exp + 3); } timeval = TimeVal(decoded_time, 0); timeval = timeval / timer_scale; } void Mld6igmpVif::decode_exp_time_code16(uint16_t code, TimeVal& timeval, uint32_t timer_scale) { uint32_t decoded_time = 0; // // From RFC 3810 Section 5.1.9: // // If Code < 32768, Time = Code // // If Code >= 32768, Code represents a floating-point value as follows: // // 0 1 2 3 4 5 6 7 8 9 A B C D E F // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // |1| exp | mant | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // // Time = (mant | 0x1000) << (exp + 3) // if (code < 32768) { decoded_time = code; } else { uint8_t mant = code & 0xfff; uint8_t exp = (code >> 12) & 0x7; decoded_time = (mant | 0x1000) << (exp + 3); } timeval = TimeVal(decoded_time, 0); timeval = timeval / timer_scale; } void Mld6igmpVif::encode_exp_time_code8(const TimeVal& timeval, uint8_t& code, uint32_t timer_scale) { TimeVal scaled_max_resp_time = timeval * timer_scale; uint32_t decoded_time = scaled_max_resp_time.sec(); code = 0; // // From RFC 3376 Section 4.1.1, and RFC 3810 Section 5.1.9: // // If Code < 128, Time = Code // // If Code >= 128, Code represents a floating-point value as follows: // // 0 1 2 3 4 5 6 7 // +-+-+-+-+-+-+-+-+ // |1| exp | mant | // +-+-+-+-+-+-+-+-+ // // Time = (mant | 0x10) << (exp + 3) // if (decoded_time < 128) { code = decoded_time; } else { uint8_t mant = 0; uint8_t exp = 0; // Calculate the "mant" and the "exp" while ((decoded_time >> (exp + 3)) > 0x1f) { exp++; } mant = (decoded_time >> (exp + 3)) & 0xf; code = 0x80 | (exp << 4) | mant; } } void Mld6igmpVif::encode_exp_time_code16(const TimeVal& timeval, uint16_t& code, uint32_t timer_scale) { TimeVal scaled_max_resp_time = timeval * timer_scale; uint32_t decoded_time = scaled_max_resp_time.sec(); code = 0; // // From RFC 3810 Section 5.1.9: // // If Code < 32768, Time = Code // // If Code >= 32768, Code represents a floating-point value as follows: // // 0 1 2 3 4 5 6 7 8 9 A B C D E F // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // |1| exp | mant | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // // Time = (mant | 0x1000) << (exp + 3) // if (decoded_time < 32768) { code = decoded_time; } else { uint8_t mant = 0; uint8_t exp = 0; // Calculate the "mant" and the "exp" while ((decoded_time >> (exp + 3)) > 0x1fff) { exp++; } mant = (decoded_time >> (exp + 3)) & 0xfff; code = 0x8000 | (exp << 12) | mant; } } xorp/contrib/mld6igmp_lite/TODO0000664000076400007640000000362011421137511016541 0ustar greearbgreearb# # $XORP: xorp/contrib/mld6igmp_lite/TODO,v 1.21 2007/03/22 22:35:26 pavlin Exp $ # * Implement RFC 3590 "Source Address Selection for the Multicast Listener Discovery (MLD) Protocol". * Implement graceful stopping of node and/or a vif. I.e., perform protocol-related operations to inform other nodes that this node/vif is stopping operations (if there are such operations). * Add vif manager config similar to PIM. * Add Mld6igmpConfig class to Mld6igmpNode. * Add Mld6igmpNode::pending_down() and Mld6igmpVif::pending_down() similar to PIM. * When a protocol registers with MLD6IGMP, that protocol should try periodically the registration if MLD6IGMP has not been started yet (applies to PIM as well). Similarly, protocols that register with MFEA should retry the registration if the MFEA is not UP yet (applies to MLD6IGMP, PIM) * When adding a protocol (for membership info), the return messages should use vif_name instead of vif_index. Also, check that in add/delete_protocol, the vif_name and vif_index match. Similar match check should be applied for all XRLs in MFEA, MLD6IGMP and PIM * The "State" in "show igmp interface" should be "Up/Down" instead of "UP/DOWN" ?? (for consistency with Juniper??) * Change all (Foo *)casting to static_cast<> * Replace all proto_family with family ? (in all mcast-related directories) * The return XrlCmdError values in mld6igmp_proto_access.hh are probably bogus. * Initialize/setup mld6igmp_vif::vif_index() and vif_name(). * Instead of using Join/Prune in the context of IGMP/MLD6, use Join/Leave instead? * The IGMP spec must say that the Group Address in Membership Report and Leave Group messages must be a valid multicast address. * In mld6igmp_recv(), if the mld6igmp_vif interface is NULL, e.g. the interface is not configured, then print a warning or at least a message?? Currently, the program will crash (XLOG_UNREACHABLE()). xorp/contrib/mld6igmp_lite/mld6igmp_group_record.hh0000664000076400007640000003521011540224220022657 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // The Lightweight IGMP/MLD modifications to this file are copyrighted by: // // Copyright (c) 2008 Huawei Technologies Co. Ltd // // $XORP: xorp/contrib/mld6igmp_lite/mld6igmp_group_record.hh,v 1.5 2008/10/02 21:56:32 bms Exp $ #ifndef __MLD6IGMP_MLD6IGMP_GROUP_RECORD_HH__ #define __MLD6IGMP_MLD6IGMP_GROUP_RECORD_HH__ // // IGMP and MLD group record. // #include "libxorp/ipvx.hh" #include "libxorp/timer.hh" #include "mld6igmp_source_record.hh" // // Constants definitions // // // Structures/classes, typedefs and macros // class Mld6igmpVif; /** * @short A class to store information about multicast group membership. */ class Mld6igmpGroupRecord { public: /** * Constructor for a given vif and group address. * * @param mld6igmp_vif the interface this entry belongs to. * @param group the multicast group address. */ Mld6igmpGroupRecord(Mld6igmpVif& mld6igmp_vif, const IPvX& group); /** * Destructor. */ ~Mld6igmpGroupRecord(); /** * Get the vif this entry belongs to. * * @return a reference to the vif this entry belongs to. */ Mld6igmpVif& mld6igmp_vif() const { return (_mld6igmp_vif); } /** * Get the multicast group address. * * @return the multicast group address. */ const IPvX& group() const { return (_group); } /** * Get the corresponding event loop. * * @return the corresponding event loop. */ EventLoop& eventloop(); /** * Test whether the filter mode is INCLUDE. * * @return true if the filter mode is INCLUDE. */ bool is_include_mode() const { return (this->timeout_sec() == 0); } /** * Test whether the filter mode is EXCLUDE. * * @return true if the filter mode is EXCLUDE. */ bool is_asm_mode() const { return (! (this->timeout_sec() == 0)); } /** * Set the filter mode to INCLUDE. */ void set_include_mode() { } /** * Set the filter mode to asm. */ void set_exclude_mode() { } /** * Test whether the entry is unused. * * @return true if the entry is unused, otherwise false. */ bool is_unused() const; /** * Find a source that should be forwarded. * * @param source the source address. * @return the corresponding source record (@ref Mld6igmpSourceRecord) * if found, otherwise NULL. */ Mld6igmpSourceRecord* find_do_forward_source(const IPvX& source); /** * Find a source that should not be forwarded. * * @param source the source address. * @return the corresponding source record (@ref Mld6igmpSourceRecord) * if found, otherwise NULL. */ Mld6igmpSourceRecord* find_dont_forward_source(const IPvX& source); /** * Get a reference to the set of sources to forward. * * @return a reference to the set of sources to forward. */ const Mld6igmpSourceSet& do_forward_sources() const { return (_do_forward_sources); } /** * Get a reference to the set of sources not to forward. * * @return a reference to the set of sources not to forward. */ /** * Process MODE_IS_INCLUDE report. * * @param sources the source addresses. * @param last_reported_host the address of the host that last reported * as member. */ void process_mode_is_include(const set& sources, const IPvX& last_reported_host); /** * Process MODE_IS_EXCLUDE report. * * @param sources the source addresses. * @param last_reported_host the address of the host that last reported * as member. */ void process_mode_is_exclude(const set& sources, const IPvX& last_reported_host); /** * Process CHANGE_TO_INCLUDE_MODE report. * * @param sources the source addresses. * @param last_reported_host the address of the host that last reported * as member. */ void process_change_to_include_mode(const set& sources, const IPvX& last_reported_host); /** * Process CHANGE_TO_EXCLUDE_MODE report. * * @param sources the source addresses. * @param last_reported_host the address of the host that last reported * as member. */ void process_change_to_exclude_mode(const set& sources, const IPvX& last_reported_host); /** * Process ALLOW_NEW_SOURCES report. * * @param sources the source addresses. * @param last_reported_host the address of the host that last reported * as member. */ void process_allow_new_sources(const set& sources, const IPvX& last_reported_host); /** * Process BLOCK_OLD_SOURCES report. * * @param sources the source addresses. * @param last_reported_host the address of the host that last reported * as member. */ void process_block_old_sources(const set& sources, const IPvX& last_reported_host); /** * Lower the group timer. * * @param timeval the timeout interval the timer should be lowered to. */ void lower_group_timer(const TimeVal& timeval); /** * Lower the source timer for a set of sources. * * @param sources the source addresses. * @param timeval the timeout interval the timer should be lowered to. */ void lower_source_timer(const set& sources, const TimeVal& timeval); /** * Take the appropriate actions for a source that has expired. * * @param source_record the source record that has expired. */ void source_expired(Mld6igmpSourceRecord* source_record); /** * Get the number of seconds until the group timer expires. * * @return the number of seconds until the group timer expires. */ uint32_t timeout_sec() const; /** * Get the number of seconds until the IGMPv1 host present timer expires. * * @return the number of seconds until the IGMPv1 host present timer * expires. */ uint32_t igmpv1_host_present_timer_timeout_sec() const; /** * Get the number of seconds until the IGMPv2/MLDv1 host present timer * expires. * * @return the number of seconds until the IGMPv2/MLDv1 host present timer * expires. */ uint32_t igmpv2_mldv1_host_present_timer_timeout_sec() const; /** * Get the address of the host that last reported as member. * * @return the address of the host that last reported as member. */ const IPvX& last_reported_host() const { return (_last_reported_host); } /** * Get a refererence to the group timer. * * @return a reference to the group timer. */ XorpTimer& group_timer() { return _group_timer; } /** * Schedule periodic Group-Specific and Group-and-Source-Specific Query * retransmission. * * If the sources list is empty, we schedule Group-Specific Query, * otherwise we schedule Group-and-Source-Specific Query. * * @param sources the source addresses. */ void schedule_periodic_group_query(const set& sources); /** * Record that an older Membership report message has been received. * * @param message_version the corresponding protocol version of the * received message. */ void received_older_membership_report(int message_version); /** * Test if the group is running in IGMPv1 mode. * * @return true if the group is running in IGMPv1 mode, otherwise false. */ bool is_igmpv1_mode() const; /** * Test if the group is running in IGMPv2 mode. * * @return true if the group is running in IGMPv2 mode, otherwise false. */ bool is_igmpv2_mode() const; /** * Test if the group is running in IGMPv3 mode. * * @return true if the group is running in IGMPv3 mode, otherwise false. */ bool is_igmpv3_mode() const; /** * Test if the group is running in MLDv1 mode. * * @return true if the group is running in MLDv1 mode, otherwise false. */ bool is_mldv1_mode() const; /** * Test if the group is running in MLDv2 mode. * * @return true if the group is running in MLDv2 mode, otherwise false. */ bool is_mldv2_mode() const; /** * Get the address family. * * @return the address family. */ int family() const { return _group.af(); } private: /** * Calculate the forwarding changes and notify the interested parties. * * @param old_is_include mode if true, the old filter mode was INCLUDE, * otherwise was EXCLUDE. * @param old_do_forward_sources the old set of sources to forward. * @param old_dont_forward_sources the old set of sources not to forward. */ void calculate_forwarding_changes(bool old_is_include_mode, const set& old_do_forward_sources) const; /** * Timeout: one of the older version host present timers has expired. */ void older_version_host_present_timer_timeout(); /** * Timeout: the group timer has expired. */ void group_timer_timeout(); /** * Periodic timeout: time to send the next Group-Specific and * Group-and-Source-Specific Queries. * * @return true if the timer should be scheduled again, otherwise false. */ bool group_query_periodic_timeout(); /** * Set the address of the host that last reported as member. * * @param v the address of the host that last reported as member. */ void set_last_reported_host(const IPvX& v) { _last_reported_host = v; } Mld6igmpVif& _mld6igmp_vif; // The interface this entry belongs to IPvX _group; // The multicast group address Mld6igmpSourceSet _do_forward_sources; // Sources to forward IPvX _last_reported_host; // The host that last reported as member // Timers indicating that hosts running older protocol version are present XorpTimer _igmpv1_host_present_timer; XorpTimer _igmpv2_mldv1_host_present_timer; XorpTimer _group_timer; // Group timer for filter mode switch XorpTimer _group_query_timer; // Timer for periodic Queries size_t _query_retransmission_count; // Count for periodic Queries }; /** * @short A class to store information about a set of multicast groups. */ class Mld6igmpGroupSet : public map { public: /** * Constructor for a given vif. * * @param mld6igmp_vif the interface this set belongs to. */ Mld6igmpGroupSet(Mld6igmpVif& mld6igmp_vif); /** * Destructor. */ ~Mld6igmpGroupSet(); /** * Find a group record. * * @param group the group address. * @return the corresponding group record (@ref Mld6igmpGroupRecord) * if found, otherwise NULL. */ Mld6igmpGroupRecord* find_group_record(const IPvX& group); /** * Delete the payload of the set, and clear the set itself. */ void delete_payload_and_clear(); /** * Process MODE_IS_INCLUDE report. * * @param group the group address. * @param sources the source addresses. * @param last_reported_host the address of the host that last reported * as member. */ void process_mode_is_include(const IPvX& group, const set& sources, const IPvX& last_reported_host); /** * Process MODE_IS_EXCLUDE report. * * @param group the group address. * @param sources the source addresses. * @param last_reported_host the address of the host that last reported * as member. */ void process_mode_is_exclude(const IPvX& group, const set& sources, const IPvX& last_reported_host); /** * Process CHANGE_TO_INCLUDE_MODE report. * * @param group the group address. * @param sources the source addresses. * @param last_reported_host the address of the host that last reported * as member. */ void process_change_to_include_mode(const IPvX& group, const set& sources, const IPvX& last_reported_host); /** * Process CHANGE_TO_EXCLUDE_MODE report. * * @param group the group address. * @param sources the source addresses. * @param last_reported_host the address of the host that last reported * as member. */ void process_change_to_exclude_mode(const IPvX& group, const set& sources, const IPvX& last_reported_host); /** * Process ALLOW_NEW_SOURCES report. * * @param group the group address. * @param sources the source addresses. * @param last_reported_host the address of the host that last reported * as member. */ void process_allow_new_sources(const IPvX& group, const set& sources, const IPvX& last_reported_host); /** * Process BLOCK_OLD_SOURCES report. * * @param group the group address. * @param sources the source addresses. * @param last_reported_host the address of the host that last reported * as member. */ void process_block_old_sources(const IPvX& group, const set& sources, const IPvX& last_reported_host); /** * Lower the group timer. * * @param group the group address. * @param timeval the timeout interval the timer should be lowered to. */ void lower_group_timer(const IPvX& group, const TimeVal& timeval); /** * Lower the source timer for a set of sources. * * @param group the group address. * @param sources the source addresses. * @param timeval the timeout interval the timer should be lowered to. */ void lower_source_timer(const IPvX& group, const set& sources, const TimeVal& timeval); private: Mld6igmpVif& _mld6igmp_vif; // The interface this set belongs to }; // // Global variables // // // Global functions prototypes // #endif // __MLD6IGMP_MLD6IGMP_GROUP_RECORD_HH__ xorp/contrib/mld6igmp_lite/xrl_mld6igmp_shell_funcs.sh0000664000076400007640000002673711421137511023414 0ustar greearbgreearb#!/bin/sh # # $XORP: xorp/contrib/mld6igmp_lite/xrl_mld6igmp_shell_funcs.sh,v 1.16 2007/04/20 00:14:52 pavlin Exp $ # # # Library of functions to sent XRLs to a running MLD6IGMP process. # # Conditionally set ${srcdir} if it wasn't assigned (e.g., by `gmake check`) if [ "X${srcdir}" = "X" ] ; then srcdir=`dirname $0` ; fi . ${srcdir}/../utils/xrl_shell_lib.sh # # Conditionally set the target name # IP_VERSION=${IP_VERSION:?"IP_VERSION undefined. Must be defined to either IPV4 or IPV6"} case "${IP_VERSION}" in IPV4) MLD6IGMP_TARGET=${MLD6IGMP_TARGET:="IGMP"} ;; IPV6) MLD6IGMP_TARGET=${MLD6IGMP_TARGET:="MLD"} ;; *) echo "Error: invalid IP_VERSION = ${IP_VERSION}. Must be either IPV4 or IPV6" exit 1 ;; esac mld6igmp_enable_vif() { if [ $# -lt 2 ] ; then echo "Usage: mld6igmp_enable_vif " exit 1 fi vif_name=$1 enable=$2 echo "mld6igmp_enable_vif" $* XRL="finder://$MLD6IGMP_TARGET/mld6igmp/0.1/enable_vif" XRL_ARGS="?vif_name:txt=$vif_name&enable:bool=$enable" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } mld6igmp_start_vif() { if [ $# -lt 1 ] ; then echo "Usage: mld6igmp_start_vif " exit 1 fi vif_name=$1 echo "mld6igmp_start_vif" $* XRL="finder://$MLD6IGMP_TARGET/mld6igmp/0.1/start_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } mld6igmp_stop_vif() { if [ $# -lt 1 ] ; then echo "Usage: mld6igmp_stop_vif " exit 1 fi vif_name=$1 echo "mld6igmp_stop_vif" $* XRL="finder://$MLD6IGMP_TARGET/mld6igmp/0.1/stop_vif" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } mld6igmp_enable_all_vifs() { if [ $# -lt 1 ] ; then echo "Usage: mld6igmp_enable_all_vifs " exit 1 fi enable=$1 echo "mld6igmp_enable_all_vifs" $* XRL="finder://$MLD6IGMP_TARGET/mld6igmp/0.1/enable_all_vifs" XRL_ARGS="?enable:bool=$enable" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } mld6igmp_start_all_vifs() { echo "mld6igmp_start_all_vifs" $* XRL="finder://$MLD6IGMP_TARGET/mld6igmp/0.1/start_all_vifs" XRL_ARGS="" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } mld6igmp_stop_all_vifs() { echo "mld6igmp_stop_all_vifs" $* XRL="finder://$MLD6IGMP_TARGET/mld6igmp/0.1/stop_all_vifs" XRL_ARGS="" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } mld6igmp_enable_mld6igmp() { if [ $# -lt 1 ] ; then echo "Usage: mld6igmp_enable_mld6igmp " exit 1 fi enable=$1 echo "mld6igmp_enable_mld6igmp" $* XRL="finder://$MLD6IGMP_TARGET/mld6igmp/0.1/enable_mld6igmp" XRL_ARGS="?enable:bool=$enable" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } mld6igmp_start_mld6igmp() { echo "mld6igmp_start_mld6igmp" $* XRL="finder://$MLD6IGMP_TARGET/mld6igmp/0.1/start_mld6igmp" XRL_ARGS="" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } mld6igmp_stop_mld6igmp() { echo "mld6igmp_stop_mld6igmp" $* XRL="finder://$MLD6IGMP_TARGET/mld6igmp/0.1/stop_mld6igmp" XRL_ARGS="" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } mld6igmp_enable_cli() { if [ $# -lt 1 ] ; then echo "Usage: mld6igmp_enable_cli " exit 1 fi enable=$1 echo "mld6igmp_enable_cli" $* XRL="finder://$MLD6IGMP_TARGET/mld6igmp/0.1/enable_cli" XRL_ARGS="?enable:bool=$enable" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } mld6igmp_start_cli() { echo "mld6igmp_start_cli" $* XRL="finder://$MLD6IGMP_TARGET/mld6igmp/0.1/start_cli" XRL_ARGS="" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } mld6igmp_stop_cli() { echo "mld6igmp_stop_cli" $* XRL="finder://$MLD6IGMP_TARGET/mld6igmp/0.1/stop_cli" XRL_ARGS="" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } # # Configure MLD6IGMP interface-related metrics. # mld6igmp_get_vif_proto_version() { if [ $# -lt 1 ] ; then echo "Usage: mld6igmp_get_vif_proto_version " exit 1 fi vif_name=$1 echo "mld6igmp_get_vif_proto_version" $* XRL="finder://$MLD6IGMP_TARGET/mld6igmp/0.1/get_vif_proto_version" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p proto_version:u32 $XRL$XRL_ARGS } mld6igmp_set_vif_proto_version() { if [ $# -lt 2 ] ; then echo "Usage: mld6igmp_set_vif_proto_version " exit 1 fi vif_name=$1 proto_version=$2 echo "mld6igmp_set_vif_proto_version" $* XRL="finder://$MLD6IGMP_TARGET/mld6igmp/0.1/set_vif_proto_version" XRL_ARGS="?vif_name:txt=$vif_name&proto_version:u32=$proto_version" call_xrl_wrapper $XRL$XRL_ARGS } mld6igmp_reset_vif_proto_version() { if [ $# -lt 1 ] ; then echo "Usage: mld6igmp_reset_vif_proto_version " exit 1 fi vif_name=$1 echo "mld6igmp_reset_vif_proto_version" $* XRL="finder://$MLD6IGMP_TARGET/mld6igmp/0.1/reset_vif_proto_version" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper $XRL$XRL_ARGS } mld6igmp_get_vif_ip_router_alert_option_check() { if [ $# -lt 1 ] ; then echo "Usage: mld6igmp_get_vif_ip_router_alert_option_check " exit 1 fi vif_name=$1 echo "mld6igmp_get_vif_ip_router_alert_option_check" $* XRL="finder://$MLD6IGMP_TARGET/mld6igmp/0.1/get_vif_ip_router_alert_option_check" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p enabled:bool $XRL$XRL_ARGS } mld6igmp_set_vif_ip_router_alert_option_check() { if [ $# -lt 2 ] ; then echo "Usage: mld6igmp_set_vif_ip_router_alert_option_check " exit 1 fi vif_name=$1 enable=$2 echo "mld6igmp_set_vif_ip_router_alert_option_check" $* XRL="finder://$MLD6IGMP_TARGET/mld6igmp/0.1/set_vif_ip_router_alert_option_check" XRL_ARGS="?vif_name:txt=$vif_name&enable:bool=$enable" call_xrl_wrapper $XRL$XRL_ARGS } mld6igmp_reset_vif_ip_router_alert_option_check() { if [ $# -lt 1 ] ; then echo "Usage: mld6igmp_reset_vif_ip_router_alert_option_check " exit 1 fi vif_name=$1 echo "mld6igmp_reset_vif_ip_router_alert_option_check" $* XRL="finder://$MLD6IGMP_TARGET/mld6igmp/0.1/reset_vif_ip_router_alert_option_check" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper $XRL$XRL_ARGS } mld6igmp_get_vif_query_interval() { if [ $# -lt 1 ] ; then echo "Usage: mld6igmp_get_vif_query_interval " exit 1 fi vif_name=$1 echo "mld6igmp_get_vif_query_interval" $* XRL="finder://$MLD6IGMP_TARGET/mld6igmp/0.1/get_vif_query_interval" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p interval_sec:u32 -p interval_usec:u32 $XRL$XRL_ARGS } mld6igmp_set_vif_query_interval() { if [ $# -lt 3 ] ; then echo "Usage: mld6igmp_set_vif_query_interval " exit 1 fi vif_name=$1 interval_sec=$2 interval_usec=$3 echo "mld6igmp_set_vif_query_interval" $* XRL="finder://$MLD6IGMP_TARGET/mld6igmp/0.1/set_vif_query_interval" XRL_ARGS="?vif_name:txt=$vif_name&interval_sec:u32=$interval_sec&interval_usec:u32=$interval_usec" call_xrl_wrapper $XRL$XRL_ARGS } mld6igmp_reset_vif_query_interval() { if [ $# -lt 1 ] ; then echo "Usage: mld6igmp_reset_vif_query_interval " exit 1 fi vif_name=$1 echo "mld6igmp_reset_vif_query_interval" $* XRL="finder://$MLD6IGMP_TARGET/mld6igmp/0.1/reset_vif_query_interval" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper $XRL$XRL_ARGS } mld6igmp_get_vif_query_last_member_interval() { if [ $# -lt 1 ] ; then echo "Usage: mld6igmp_get_vif_query_last_member_interval " exit 1 fi vif_name=$1 echo "mld6igmp_get_vif_query_last_member_interval" $* XRL="finder://$MLD6IGMP_TARGET/mld6igmp/0.1/get_vif_query_last_member_interval" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p interval_sec:u32 -p interval_usec:u32 $XRL$XRL_ARGS } mld6igmp_set_vif_query_last_member_interval() { if [ $# -lt 3 ] ; then echo "Usage: mld6igmp_set_vif_query_last_member_interval " exit 1 fi vif_name=$1 interval_sec=$2 interval_usec=$3 echo "mld6igmp_set_vif_query_last_member_interval" $* XRL="finder://$MLD6IGMP_TARGET/mld6igmp/0.1/set_vif_query_last_member_interval" XRL_ARGS="?vif_name:txt=$vif_name&interval_sec:u32=$interval_sec&interval_usec:u32=$interval_usec" call_xrl_wrapper $XRL$XRL_ARGS } mld6igmp_reset_vif_query_last_member_interval() { if [ $# -lt 1 ] ; then echo "Usage: mld6igmp_reset_vif_query_last_member_interval " exit 1 fi vif_name=$1 echo "mld6igmp_reset_vif_query_last_member_interval" $* XRL="finder://$MLD6IGMP_TARGET/mld6igmp/0.1/reset_vif_query_last_member_interval" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper $XRL$XRL_ARGS } mld6igmp_get_vif_query_response_interval() { if [ $# -lt 1 ] ; then echo "Usage: mld6igmp_get_vif_query_response_interval " exit 1 fi vif_name=$1 echo "mld6igmp_get_vif_query_response_interval" $* XRL="finder://$MLD6IGMP_TARGET/mld6igmp/0.1/get_vif_query_response_interval" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p interval_sec:u32 -p interval_usec:u32 $XRL$XRL_ARGS } mld6igmp_set_vif_query_response_interval() { if [ $# -lt 3 ] ; then echo "Usage: mld6igmp_set_vif_query_response_interval " exit 1 fi vif_name=$1 interval_sec=$2 interval_usec=$3 echo "mld6igmp_set_vif_query_response_interval" $* XRL="finder://$MLD6IGMP_TARGET/mld6igmp/0.1/set_vif_query_response_interval" XRL_ARGS="?vif_name:txt=$vif_name&interval_sec:u32=$interval_sec&interval_usec:u32=$interval_usec" call_xrl_wrapper $XRL$XRL_ARGS } mld6igmp_reset_vif_query_response_interval() { if [ $# -lt 1 ] ; then echo "Usage: mld6igmp_reset_vif_query_response_interval " exit 1 fi vif_name=$1 echo "mld6igmp_reset_vif_query_response_interval" $* XRL="finder://$MLD6IGMP_TARGET/mld6igmp/0.1/reset_vif_query_response_interval" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper $XRL$XRL_ARGS } mld6igmp_get_vif_robust_count() { if [ $# -lt 1 ] ; then echo "Usage: mld6igmp_get_vif_robust_count " exit 1 fi vif_name=$1 echo "mld6igmp_get_vif_robust_count" $* XRL="finder://$MLD6IGMP_TARGET/mld6igmp/0.1/get_vif_robust_count" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper -p robust_count:u32 $XRL$XRL_ARGS } mld6igmp_set_vif_robust_count() { if [ $# -lt 2 ] ; then echo "Usage: mld6igmp_set_vif_robust_count " exit 1 fi vif_name=$1 robust_count=$2 echo "mld6igmp_set_vif_robust_count" $* XRL="finder://$MLD6IGMP_TARGET/mld6igmp/0.1/set_vif_robust_count" XRL_ARGS="?vif_name:txt=$vif_name&robust_count:u32=$robust_count" call_xrl_wrapper $XRL$XRL_ARGS } mld6igmp_reset_vif_robust_count() { if [ $# -lt 1 ] ; then echo "Usage: mld6igmp_reset_vif_robust_count " exit 1 fi vif_name=$1 echo "mld6igmp_reset_vif_robust_count" $* XRL="finder://$MLD6IGMP_TARGET/mld6igmp/0.1/reset_vif_robust_count" XRL_ARGS="?vif_name:txt=$vif_name" call_xrl_wrapper $XRL$XRL_ARGS } mld6igmp_log_trace_all() { if [ $# -lt 1 ] ; then echo "Usage: mld6igmp_log_trace_all " exit 1 fi enable=$1 echo "mld6igmp_log_trace_all" $* XRL="finder://$MLD6IGMP_TARGET/mld6igmp/0.1/log_trace_all" XRL_ARGS="?enable:bool=$enable" call_xrl_wrapper -r 0 $XRL$XRL_ARGS } xorp/contrib/mld6igmp_lite/SConscript0000664000076400007640000000536411421137511020072 0ustar greearbgreearb# Copyright (c) 2009 XORP, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # $XORP$ import os Import('env') env = env.Clone() is_shared = env.has_key('SHAREDLIBS') env.AppendUnique(CPPPATH = [ '#', '$BUILDDIR', ]) env.AppendUnique(LIBPATH = [ '$BUILDDIR/libxorp', '$BUILDDIR/libcomm', '$BUILDDIR/libxipc', '$BUILDDIR/libproto', '$BUILDDIR/libfeaclient', '$BUILDDIR/policy/backend', '$BUILDDIR/policy/common', '$BUILDDIR/xrl/interfaces', '$BUILDDIR/xrl/targets', '$BUILDDIR/mrt', '.' ]) env.AppendUnique(LIBS = [ 'xorp_mld6igmp', 'xorp_fea_client', 'xif_mld6igmp_client', 'xif_fea_rawpkt4', 'xif_fea_rawpkt6', 'xif_fea_ifmgr_mirror', 'xif_fea_ifmgr_replicator', 'xif_cli_manager', 'xif_finder_event_notifier', 'xst_fea_ifmgr_mirror', 'xst_mld6igmp', 'xorp_mrt', 'xorp_ipc', 'xorp_core', 'xorp_proto', 'xorp_comm' ]) env.Replace(RPATH = [ env.Literal(env['xorp_module_rpath']) ]) libmld6igmplitesrcs = [ 'mld6igmp_config.cc', 'mld6igmp_group_record.cc', 'mld6igmp_node.cc', 'mld6igmp_node_cli.cc', 'mld6igmp_proto.cc', 'mld6igmp_source_record.cc', 'mld6igmp_vif.cc', 'xrl_mld6igmp_node.cc', ] # XXX needs fixing for new world order if is_shared: libmld6igmplite = env.SharedLibrary(target = 'libmld6igmplite', source = libmld6igmplitesrcs, LIBS = '') env.Alias('install', env.InstallLibrary(env['xorp_libdir'], libmld6igmplite)) else: libmld6igmplite = env.StaticLibrary(target = 'libmld6igmplite', source = libmld6igmplitesrcs, LIBS = '') igmplitesrcs = [ 'xorp_igmp.cc', ] igmplite = env.Program(target = 'xorp_igmp_lite', source = igmplitesrcs) mldlitesrcs = [ 'xorp_mld.cc', ] mldlite = env.Program(target = 'xorp_mld_lite', source = mldlitesrcs) env.Alias('install', env.InstallProgram(env['xorp_moduledir'], igmplite)) env.Alias('install', env.InstallProgram(env['xorp_moduledir'], mldlite)) Default(libmld6igmplite, igmplite, mldlite) xorp/contrib/mld6igmp_lite/command_mld6igmp0000664000076400007640000000217411421137511021214 0ustar greearbgreearb#!/bin/sh # # $XORP: xorp/contrib/mld6igmp_lite/command_mld6igmp,v 1.5 2003/10/15 19:07:35 pavlin Exp $ # # # Send commands to a running PIM process. # MODULE_NAME="IGMP" HOSTNAME=`hostname` echo "Sending command to $MODULE_NAME on $HOSTNAME..." # Conditionally set ${srcdir} if it wasn't assigned (e.g., by `gmake check`) if [ "X${srcdir}" = "X" ] ; then srcdir=`dirname $0` ; fi # # The optional first argument should be either -4 or -6 to specify # IPv4 or IPv6 command # # The next argument should be the command to call. # The rest of the arguments should be the arguments to the command to call. # usage() { echo "Usage: $0 [-4 | -6 ] [xrl_command_arguments ... ]" } if [ $# -lt 1 ] ; then usage; exit 1 fi case "${1}" in -4) if [ $# -lt 2 ] ; then usage; exit 1 fi IP_VERSION=IPV4 shift ;; -6) if [ $# -lt 2 ] ; then usage; exit 1 fi IP_VERSION=IPV6 shift ;; *) # XXX: IP version defaults to IPv4 IP_VERSION=IPV4 ;; esac . ${srcdir}/../cli/xrl_cli_shell_funcs.sh . ${srcdir}/../fea/xrl_mfea_shell_funcs.sh . ${srcdir}/../mld6igmp/xrl_mld6igmp_shell_funcs.sh $* exit $? xorp/contrib/mld6igmp_lite/mld6igmp_source_record.hh0000664000076400007640000002003111540224221023017 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/contrib/mld6igmp_lite/mld6igmp_source_record.hh,v 1.3 2008/10/02 21:56:32 bms Exp $ #ifndef __MLD6IGMP_MLD6IGMP_SOURCE_RECORD_HH__ #define __MLD6IGMP_MLD6IGMP_SOURCE_RECORD_HH__ // // IGMP and MLD source record. // #include "libxorp/ipvx.hh" #include "libxorp/timer.hh" // // Constants definitions // // // Structures/classes, typedefs and macros // class EventLoop; class Mld6igmpGroupRecord; /** * @short A class to store information about a source (within a given * multicast group). */ class Mld6igmpSourceRecord { public: /** * Constructor for a given group record and source address. * * @param group_record the group record this entry belongs to. * @param source the source address. */ Mld6igmpSourceRecord(Mld6igmpGroupRecord& group_record, const IPvX& source); /** * Destructor. */ ~Mld6igmpSourceRecord(); /** * Get the group record this entry belongs to. * * @return a reference to the group record this entry belongs to. */ Mld6igmpGroupRecord& group_record() const { return (_group_record); } /** * Get the source address. * * @return the source address. */ const IPvX& source() const { return (_source); } /** * Get the address family. * * @return the address family. */ int family() const { return _source.af(); } /** * Set the source timer. * * @param timeval the timeout interval of the source timer. */ void set_source_timer(const TimeVal& timeval); /** * Cancel the source timer. */ void cancel_source_timer(); /** * Lower the source timer. * * @param timeval the timeout interval the source timer should be * lowered to. */ void lower_source_timer(const TimeVal& timeval); /** * Get a reference to the source timer. * * @return a reference to the source timer. */ XorpTimer& source_timer() { return _source_timer; } /** * Get the number of seconds until the source timer expires. * * @return the number of seconds until the source timer expires. */ uint32_t timeout_sec() const; /** * Get the Query retransmission count. * * @return the Query retransmission count. */ size_t query_retransmission_count() const { return _query_retransmission_count; } /** * Set the Query retransmission count. * * @param v the value to set. */ void set_query_retransmission_count(size_t v) { _query_retransmission_count = v; } private: /** * Timeout: the source timer has expired. */ void source_timer_timeout(); Mld6igmpGroupRecord& _group_record; // The group record we belong to IPvX _source; // The source address XorpTimer _source_timer; // The source timer size_t _query_retransmission_count; // Count for periodic Queries }; /** * @short A class to store information about a set of sources. */ class Mld6igmpSourceSet : public map { public: /** * Constructor for a given group record. * * @param group_record the group record this set belongs to. */ Mld6igmpSourceSet(Mld6igmpGroupRecord& group_record); /** * Destructor. */ ~Mld6igmpSourceSet(); /** * Find a source record. * * @param source the source address. * @return the corresponding source record (@ref Mld6igmpSourceRecord) * if found, otherwise NULL. */ Mld6igmpSourceRecord* find_source_record(const IPvX& source); /** * Delete the payload of the set, and clear the set itself. */ void delete_payload_and_clear(); /** * Assignment operator for sets. * * @param other the right-hand operand. * @return the assigned set. */ Mld6igmpSourceSet& operator=(const Mld6igmpSourceSet& other); /** * UNION operator for sets. * * @param other the right-hand operand. * @return the union of two sets. Note that if an element is in * both sets, we use the value from the first set. */ Mld6igmpSourceSet operator+(const Mld6igmpSourceSet& other); /** * UNION operator for sets when the second operand is a set of IPvX * addresses. * * @param other the right-hand operand. * @return the union of two sets. Note that if an element is not in the * first set, then it is created (see @ref Mld6igmpSourceRecord). */ Mld6igmpSourceSet operator+(const set& other); /** * INTERSECTION operator for sets. * * @param other the right-hand operand. * @return the intersection of two sets. Note that we use the values * from the first set. */ Mld6igmpSourceSet operator*(const Mld6igmpSourceSet& other); /** * INTERSECTION operator for sets when the second operand is a set of IPvX * addresses. * * @param other the right-hand operand. * @return the intersection of two sets. Note that we use the values * from the first set. */ Mld6igmpSourceSet operator*(const set& other); /** * REMOVAL operator for sets. * * @param other the right-hand operand. * @return the elements from the first set (after the elements from * the right-hand set have been removed). */ Mld6igmpSourceSet operator-(const Mld6igmpSourceSet& other); /** * REMOVAL operator for sets when the second operand is a set of IPvX * addresses. * * @param other the right-hand operand. * @return the elements from the first set (after the elements from * the right-hand set have been removed). */ Mld6igmpSourceSet operator-(const set& other); /** * Set the source timer for a set of source addresses. * * @param sources the set of source addresses whose source timer will * be set. * @param timeval the timeout interval of the source timer. */ void set_source_timer(const set& sources, const TimeVal& timeval); /** * Set the source timer for all source addresses. * * @param timeval the timeout interval of the source timer. */ void set_source_timer(const TimeVal& timeval); /** * Cancel the source timer for a set of source addresses. * * @param sources the set of source addresses whose source timer will * be canceled. */ void cancel_source_timer(const set& sources); /** * Cancel the source timer for all source addresses. */ void cancel_source_timer(); /** * Lower the source timer for a set of sources. * * @param sources the source addresses. * @param timeval the timeout interval the source timer should be * lowered to. */ void lower_source_timer(const set& sources, const TimeVal& timeval); /** * Extract the set of source addresses. * * @return the set with the source addresses. */ set extract_source_addresses() const; private: Mld6igmpGroupRecord& _group_record; // The group record this set belongs to }; // // Global variables // // // Global functions prototypes // #endif // __MLD6IGMP_MLD6IGMP_SOURCE_RECORD_HH__ xorp/contrib/mld6igmp_lite/README0000664000076400007640000000433511421137511016735 0ustar greearbgreearb# # $XORP: xorp/contrib/mld6igmp_lite/README,v 1.2 2008/07/10 11:14:34 pavlin Exp $ # LightWeight Multicast Listener Discovery/Internet Group Management Protocol (LW-MLD/IGMP) Implementation =============================================================== This directory contains the XORP implementation of the LightWeight MLD and IGMP protocols. Configuration ============= LightWeight MLD/IGMP like most XORP processes does not take its configuration parameters from the command line. Its parameters are provided via XRLs. At the very least a LW-MLD/IGMP process must be provided with the set of network interfaces to enable for multicast. Startup ======= LW-MLD/IGMP is disabled by default. It can be used instead of the existing MLD/IGMP vanilla implementation by using the following command before compiling XORP: ./configure --with-mld6igmp_lite In normal operation, LW-MLD/IGMP would be started by the XORP router manager process, not directly from the command line. For information how to configure LW-MLD/IGMP see http://www.xorp.org/getting_started.html or http://www.xorp.org/releases/current/docs/user_manual/user_manual.pdf Currently, the LW-MLD/IGMP XORP configuration is exactly same as the configuration for the existing MLD/IGMP vanilla implementation. Documentation ============= The LW-MLD/IGMP design architecture and code structure are described in: ${XORP}/docs/mld6igmp/ The programming documentation is in: ${XORP}/docs/kdoc/html/mld6igmp/ Testing ======= Currently, the LW-MLD/IGMP testing is performed manually, by starting LW-MLD/IGMP on a number of testbed machines. In the future, automated testing will be added, similar to the BGP testing framework. Status ====== The LW-MLD/IGMP implementation is based on the specification in the following documents: * RFC 2236: Internet Group Management Protocol, Version 2 * RFC 3376: Internet Group Management Protocol, Version 3 * RFC 2710: Multicast Listener Discovery for IPv6, Version 1 * RFC 3810: Multicast Listener Discovery Version 2 (MLDv2) for IPv6 * draft-ietf-mboned-lightweight-igmpv3-mldv2-02 Contact ======= For any question or suggestion about the LightWeight IGMPv3/MLDv2 implementation please contact the current maintainer: libin020989 AT gmail.com xorp/contrib/mld6igmp_lite/mld6igmp_source_record.cc0000664000076400007640000003036211421137511023020 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // Multicast source record information used by IGMPv3 (RFC 3376) and // MLDv2 (RFC 3810). // #include "mld6igmp_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvx.hh" #include "mld6igmp_source_record.hh" #include "mld6igmp_group_record.hh" #include "mld6igmp_vif.hh" // // Exported variables // // // Local constants definitions // // // Local structures/classes, typedefs and macros // // // Local variables // // // Local functions prototypes // /** * Mld6igmpSourceRecord::Mld6igmpSourceRecord: * @group_record: The group record this entry belongs to. * @source: The entry source address. * * Return value: **/ Mld6igmpSourceRecord::Mld6igmpSourceRecord(Mld6igmpGroupRecord& group_record, const IPvX& source) : _group_record(group_record), _source(source), _query_retransmission_count(0) { } /** * Mld6igmpSourceRecord::~Mld6igmpSourceRecord: * @: * * Mld6igmpSourceRecord destructor. **/ Mld6igmpSourceRecord::~Mld6igmpSourceRecord() { } /** * Set the source timer. * * @param timeval the timeout interval of the source timer. */ void Mld6igmpSourceRecord::set_source_timer(const TimeVal& timeval) { EventLoop& eventloop = _group_record.eventloop(); _source_timer = eventloop.new_oneoff_after( timeval, callback(this, &Mld6igmpSourceRecord::source_timer_timeout)); } /** * Cancel the source timer. */ void Mld6igmpSourceRecord::cancel_source_timer() { _source_timer.unschedule(); } /** * Lower the source timer. * * @param timeval the timeout interval the source timer should be * lowered to. */ void Mld6igmpSourceRecord::lower_source_timer(const TimeVal& timeval) { EventLoop& eventloop = _group_record.eventloop(); TimeVal timeval_remaining; // // Lower the source timer // _source_timer.time_remaining(timeval_remaining); if (timeval < timeval_remaining) { _source_timer = eventloop.new_oneoff_after( timeval, callback(this, &Mld6igmpSourceRecord::source_timer_timeout)); } } /** * Timeout: the source timer has expired. */ void Mld6igmpSourceRecord::source_timer_timeout() { _group_record.source_expired(this); } /** * Get the number of seconds until the source timer expires. * * @return the number of seconds until the source timer expires. */ uint32_t Mld6igmpSourceRecord::timeout_sec() const { TimeVal tv; _source_timer.time_remaining(tv); return (tv.sec()); } /** * Constructor for a given group record. * * @param group_record the group record this set belongs to. */ Mld6igmpSourceSet::Mld6igmpSourceSet(Mld6igmpGroupRecord& group_record) : _group_record(group_record) { } /** * Destructor. */ Mld6igmpSourceSet::~Mld6igmpSourceSet() { // XXX: don't delete the payload, because it might be used elsewhere } /** * Find a source record. * * @param source the source address. * @return the corresponding source record (@ref Mld6igmpSourceRecord) * if found, otherwise NULL. */ Mld6igmpSourceRecord* Mld6igmpSourceSet::find_source_record(const IPvX& source) { Mld6igmpSourceSet::iterator iter = this->find(source); if (iter != this->end()) return (iter->second); return (NULL); } /** * Delete the payload of the set, and clear the set itself. */ void Mld6igmpSourceSet::delete_payload_and_clear() { Mld6igmpSourceSet::iterator iter; // // Delete the payload of the set // for (iter = this->begin(); iter != this->end(); ++iter) { Mld6igmpSourceRecord* source_record = iter->second; delete source_record; } // // Clear the set itself // this->clear(); } /** * Assignment operator for sets. * * @param other the right-hand operand. * @return the assigned set. */ Mld6igmpSourceSet& Mld6igmpSourceSet::operator=(const Mld6igmpSourceSet& other) { Mld6igmpSourceSet::const_iterator iter; XLOG_ASSERT(&_group_record == &(other._group_record)); this->clear(); // // Copy the payload of the set // for (iter = other.begin(); iter != other.end(); ++iter) { insert(make_pair(iter->first, iter->second)); } return (*this); } /** * UNION operator for sets. * * @param other the right-hand operand. * @return the union of two sets. Note that if an element is in * both sets, we use the value from the first set. */ Mld6igmpSourceSet Mld6igmpSourceSet::operator+(const Mld6igmpSourceSet& other) { Mld6igmpSourceSet result(*this); // XXX: all elements from the first set Mld6igmpSourceSet::const_iterator iter; // // Insert all elements from the second set that are not in the first set // for (iter = other.begin(); iter != other.end(); ++iter) { const IPvX& ipvx = iter->first; if (result.find(ipvx) == result.end()) result.insert(make_pair(iter->first, iter->second)); } return (result); } /** * UNION operator for sets when the second operand is a set of IPvX * addresses. * * @param other the right-hand operand. * @return the union of two sets. Note that if an element is not in the * first set, then it is created (see @ref Mld6igmpSourceRecord). */ Mld6igmpSourceSet Mld6igmpSourceSet::operator+(const set& other) { Mld6igmpSourceSet result(*this); // XXX: all elements from the first set set::const_iterator iter; Mld6igmpSourceRecord* source_record; // // Insert all elements from the second set that are not in the first set // for (iter = other.begin(); iter != other.end(); ++iter) { const IPvX& ipvx = *iter; if (result.find(ipvx) == result.end()) { source_record = new Mld6igmpSourceRecord(_group_record, ipvx); result.insert(make_pair(ipvx, source_record)); } } return (result); } /** * INTERSECTION operator for sets. * * @param other the right-hand operand. * @return the intersection of two sets. Note that we use the values * from the first set. */ Mld6igmpSourceSet Mld6igmpSourceSet::operator*(const Mld6igmpSourceSet& other) { Mld6igmpSourceSet result(_group_record); Mld6igmpSourceSet::const_iterator iter; // // Insert all elements from the first set that are also in the second set // for (iter = this->begin(); iter != this->end(); ++iter) { const IPvX& ipvx = iter->first; if (other.find(ipvx) != other.end()) result.insert(make_pair(iter->first, iter->second)); } return (result); } /** * INTERSECTION operator for sets when the second operand is a set of IPvX * addresses. * * @param other the right-hand operand. * @return the intersection of two sets. Note that we use the values * from the first set. */ Mld6igmpSourceSet Mld6igmpSourceSet::operator*(const set& other) { Mld6igmpSourceSet result(_group_record); Mld6igmpSourceSet::const_iterator iter; // // Insert all elements from the first set that are also in the second set // for (iter = this->begin(); iter != this->end(); ++iter) { const IPvX& ipvx = iter->first; if (other.find(ipvx) != other.end()) result.insert(make_pair(iter->first, iter->second)); } return (result); } /** * REMOVAL operator for sets. * * @param other the right-hand operand. * @return the elements from the first set (after the elements from * the right-hand set have been removed). */ Mld6igmpSourceSet Mld6igmpSourceSet::operator-(const Mld6igmpSourceSet& other) { Mld6igmpSourceSet result(_group_record); Mld6igmpSourceSet::const_iterator iter; // // Insert all elements from the first set that are not in the second set // for (iter = this->begin(); iter != this->end(); ++iter) { const IPvX& ipvx = iter->first; if (other.find(ipvx) == other.end()) result.insert(make_pair(iter->first, iter->second)); } return (result); } /** * REMOVAL operator for sets when the second operand is a set of IPvX * addresses. * * @param other the right-hand operand. * @return the elements from the first set (after the elements from * the right-hand set have been removed). */ Mld6igmpSourceSet Mld6igmpSourceSet::operator-(const set& other) { Mld6igmpSourceSet result(_group_record); Mld6igmpSourceSet::const_iterator iter; // // Insert all elements from the first set that are not in the second set // for (iter = this->begin(); iter != this->end(); ++iter) { const IPvX& ipvx = iter->first; if (other.find(ipvx) == other.end()) result.insert(make_pair(iter->first, iter->second)); } return (result); } /** * Set the source timer for a set of source addresses. * * @param sources the set of source addresses whose source timer will * be set. * @param timeval the timeout interval of the source timer. */ void Mld6igmpSourceSet::set_source_timer(const set& sources, const TimeVal& timeval) { set::const_iterator iter; Mld6igmpSourceSet::iterator record_iter; for (iter = sources.begin(); iter != sources.end(); ++iter) { const IPvX& ipvx = *iter; record_iter = this->find(ipvx); if (record_iter != this->end()) { Mld6igmpSourceRecord* source_record = record_iter->second; source_record->set_source_timer(timeval); } } } /** * Set the source timer for all source addresses. * * @param timeval the timeout interval of the source timer. */ void Mld6igmpSourceSet::set_source_timer(const TimeVal& timeval) { Mld6igmpSourceSet::iterator iter; for (iter = this->begin(); iter != this->end(); ++iter) { Mld6igmpSourceRecord* source_record = iter->second; source_record->set_source_timer(timeval); } } /** * Cancel the source timer for a set of source addresses. * * @param sources the set of source addresses whose source timer will * be canceled. */ void Mld6igmpSourceSet::cancel_source_timer(const set& sources) { set::const_iterator iter; Mld6igmpSourceSet::iterator record_iter; for (iter = sources.begin(); iter != sources.end(); ++iter) { const IPvX& ipvx = *iter; record_iter = this->find(ipvx); if (record_iter != this->end()) { Mld6igmpSourceRecord* source_record = record_iter->second; source_record->cancel_source_timer(); } } } /** * Cancel the source timer for all source addresses. */ void Mld6igmpSourceSet::cancel_source_timer() { Mld6igmpSourceSet::iterator iter; for (iter = this->begin(); iter != this->end(); ++iter) { Mld6igmpSourceRecord* source_record = iter->second; source_record->cancel_source_timer(); } } /** * Lower the source timer for a set of sources. * * @param sources the source addresses. * @param timeval the timeout interval the source timer should be * lowered to. */ void Mld6igmpSourceSet::lower_source_timer(const set& sources, const TimeVal& timeval) { set::const_iterator iter; Mld6igmpSourceSet::iterator record_iter; for (iter = sources.begin(); iter != sources.end(); ++iter) { const IPvX& ipvx = *iter; record_iter = this->find(ipvx); if (record_iter != this->end()) { Mld6igmpSourceRecord* source_record = record_iter->second; source_record->lower_source_timer(timeval); } } } /** * Extract the set of source addresses. * * @return the set with the source addresses. */ set Mld6igmpSourceSet::extract_source_addresses() const { set sources; Mld6igmpSourceSet::const_iterator record_iter; for (record_iter = this->begin(); record_iter != this->end(); ++record_iter) { const Mld6igmpSourceRecord* source_record = record_iter->second; const IPvX& ipvx = source_record->source(); sources.insert(ipvx); } return (sources); } xorp/contrib/mld6igmp_lite/xrl_mld6igmp_node.cc0000664000076400007640000017426111421137511022003 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "mld6igmp_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvx.hh" #include "libxorp/status_codes.h" #include "libxorp/utils.hh" #include "mld6igmp_node.hh" #include "mld6igmp_node_cli.hh" #include "mld6igmp_vif.hh" #include "xrl_mld6igmp_node.hh" const TimeVal XrlMld6igmpNode::RETRY_TIMEVAL = TimeVal(1, 0); // // XrlMld6igmpNode front-end interface // XrlMld6igmpNode::XrlMld6igmpNode(int family, xorp_module_id module_id, EventLoop& eventloop, const string& class_name, const string& finder_hostname, uint16_t finder_port, const string& finder_target, const string& fea_target, const string& mfea_target) : Mld6igmpNode(family, module_id, eventloop), XrlStdRouter(eventloop, class_name.c_str(), finder_hostname.c_str(), finder_port), XrlMld6igmpTargetBase(&xrl_router()), Mld6igmpNodeCli(*static_cast(this)), _eventloop(eventloop), _finder_target(finder_target), _fea_target(fea_target), _mfea_target(mfea_target), _ifmgr(eventloop, mfea_target.c_str(), xrl_router().finder_address(), xrl_router().finder_port()), _xrl_fea_client4(&xrl_router()), _xrl_fea_client6(&xrl_router()), _xrl_mld6igmp_client_client(&xrl_router()), _xrl_cli_manager_client(&xrl_router()), _xrl_finder_client(&xrl_router()), _is_finder_alive(false), _is_fea_alive(false), _is_fea_registered(false), _is_mfea_alive(false), _is_mfea_registered(false) { _ifmgr.set_observer(dynamic_cast(this)); _ifmgr.attach_hint_observer(dynamic_cast(this)); } XrlMld6igmpNode::~XrlMld6igmpNode() { shutdown(); _ifmgr.detach_hint_observer(dynamic_cast(this)); _ifmgr.unset_observer(dynamic_cast(this)); delete_pointers_list(_xrl_tasks_queue); } int XrlMld6igmpNode::startup() { if (start_mld6igmp() != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int XrlMld6igmpNode::shutdown() { int ret_value = XORP_OK; if (stop_cli() != XORP_OK) ret_value = XORP_ERROR; if (stop_mld6igmp() != XORP_OK) ret_value = XORP_ERROR; return (ret_value); } int XrlMld6igmpNode::enable_cli() { Mld6igmpNodeCli::enable(); return (XORP_OK); } int XrlMld6igmpNode::disable_cli() { Mld6igmpNodeCli::disable(); return (XORP_OK); } int XrlMld6igmpNode::start_cli() { if (Mld6igmpNodeCli::start() != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int XrlMld6igmpNode::stop_cli() { if (Mld6igmpNodeCli::stop() != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int XrlMld6igmpNode::enable_mld6igmp() { Mld6igmpNode::enable(); return (XORP_OK); } int XrlMld6igmpNode::disable_mld6igmp() { Mld6igmpNode::disable(); return (XORP_OK); } int XrlMld6igmpNode::start_mld6igmp() { if (Mld6igmpNode::start() != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int XrlMld6igmpNode::stop_mld6igmp() { if (Mld6igmpNode::stop() != XORP_OK) return (XORP_ERROR); return (XORP_OK); } // // Finder-related events // /** * Called when Finder connection is established. * * Note that this method overwrites an XrlRouter virtual method. */ void XrlMld6igmpNode::finder_connect_event() { _is_finder_alive = true; } /** * Called when Finder disconnect occurs. * * Note that this method overwrites an XrlRouter virtual method. */ void XrlMld6igmpNode::finder_disconnect_event() { XLOG_ERROR("Finder disconnect event. Exiting immediately..."); _is_finder_alive = false; stop_mld6igmp(); } // // Task-related methods // void XrlMld6igmpNode::add_task(XrlTaskBase* xrl_task) { _xrl_tasks_queue.push_back(xrl_task); // If the queue was empty before, start sending the changes if (_xrl_tasks_queue.size() == 1) send_xrl_task(); } void XrlMld6igmpNode::send_xrl_task() { if (_xrl_tasks_queue.empty()) return; XrlTaskBase* xrl_task_base = _xrl_tasks_queue.front(); XLOG_ASSERT(xrl_task_base != NULL); xrl_task_base->dispatch(); } void XrlMld6igmpNode::pop_xrl_task() { XLOG_ASSERT(! _xrl_tasks_queue.empty()); XrlTaskBase* xrl_task_base = _xrl_tasks_queue.front(); XLOG_ASSERT(xrl_task_base != NULL); delete xrl_task_base; _xrl_tasks_queue.pop_front(); } void XrlMld6igmpNode::retry_xrl_task() { if (_xrl_tasks_queue_timer.scheduled()) return; // XXX: already scheduled _xrl_tasks_queue_timer = _eventloop.new_oneoff_after( RETRY_TIMEVAL, callback(this, &XrlMld6igmpNode::send_xrl_task)); } // // Register with the FEA // void XrlMld6igmpNode::fea_register_startup() { if (! _is_finder_alive) return; // The Finder is dead if (_is_fea_registered) return; // Already registered Mld6igmpNode::incr_startup_requests_n(); // XXX: for FEA registration Mld6igmpNode::incr_startup_requests_n(); // XXX: for FEA birth // // Register interest in the FEA with the Finder // add_task(new RegisterUnregisterInterest(*this, _fea_target, true)); } // // Register with the MFEA // void XrlMld6igmpNode::mfea_register_startup() { if (! _is_finder_alive) return; // The Finder is dead if (_is_mfea_registered) return; // Already registered Mld6igmpNode::incr_startup_requests_n(); // XXX: for MFEA registration Mld6igmpNode::incr_startup_requests_n(); // XXX: for MFEA birth Mld6igmpNode::incr_startup_requests_n(); // XXX: for the ifmgr // // Register interest in the FEA with the Finder // add_task(new RegisterUnregisterInterest(*this, _mfea_target, true)); } // // De-register with the FEA // void XrlMld6igmpNode::fea_register_shutdown() { if (! _is_finder_alive) return; // The Finder is dead if (! _is_fea_alive) return; // The FEA is not there anymore if (! _is_fea_registered) return; // Not registered Mld6igmpNode::incr_shutdown_requests_n(); // XXX: for FEA deregistration // // De-register interest in the FEA with the Finder // add_task(new RegisterUnregisterInterest(*this, _fea_target, false)); } // // De-register with the MFEA // void XrlMld6igmpNode::mfea_register_shutdown() { if (! _is_finder_alive) return; // The Finder is dead if (! _is_mfea_alive) return; // The MFEA is not there anymore if (! _is_mfea_registered) return; // Not registered Mld6igmpNode::incr_shutdown_requests_n(); // XXX: for MFEA deregistration Mld6igmpNode::incr_shutdown_requests_n(); // XXX: for the ifmgr // // De-register interest in the MFEA with the Finder // add_task(new RegisterUnregisterInterest(*this, _mfea_target, false)); // // XXX: when the shutdown is completed, Mld6igmpNode::status_change() // will be called. // _ifmgr.shutdown(); } void XrlMld6igmpNode::send_register_unregister_interest() { bool success = true; if (! _is_finder_alive) return; // The Finder is dead XLOG_ASSERT(! _xrl_tasks_queue.empty()); XrlTaskBase* xrl_task_base = _xrl_tasks_queue.front(); RegisterUnregisterInterest* entry; entry = dynamic_cast(xrl_task_base); XLOG_ASSERT(entry != NULL); if (entry->is_register()) { // Register interest success = _xrl_finder_client.send_register_class_event_interest( _finder_target.c_str(), xrl_router().instance_name(), entry->target_name(), callback(this, &XrlMld6igmpNode::finder_send_register_unregister_interest_cb)); } else { // Unregister interest success = _xrl_finder_client.send_deregister_class_event_interest( _finder_target.c_str(), xrl_router().instance_name(), entry->target_name(), callback(this, &XrlMld6igmpNode::finder_send_register_unregister_interest_cb)); } if (! success) { // // If an error, then try again // XLOG_ERROR("Failed to %s register interest in %s with the Finder. " "Will try again.", entry->operation_name(), entry->target_name().c_str()); retry_xrl_task(); return; } } void XrlMld6igmpNode::finder_send_register_unregister_interest_cb(const XrlError& xrl_error) { XLOG_ASSERT(! _xrl_tasks_queue.empty()); XrlTaskBase* xrl_task_base = _xrl_tasks_queue.front(); RegisterUnregisterInterest* entry; entry = dynamic_cast(xrl_task_base); XLOG_ASSERT(entry != NULL); switch (xrl_error.error_code()) { case OKAY: // // If success, then schedule the next task // if (entry->is_register()) { // // Register interest // if (entry->target_name() == _fea_target) { // // If success, then the FEA birth event will startup the FEA // registration. // _is_fea_registered = true; Mld6igmpNode::decr_startup_requests_n(); // XXX: for FEA registration } if (entry->target_name() == _mfea_target) { // // If success, then the MFEA birth event will startup the MFEA // registration and the ifmgr. // _is_mfea_registered = true; Mld6igmpNode::decr_startup_requests_n(); // XXX: for MFEA registration } } else { // // Unregister interest // if (entry->target_name() == _fea_target) { _is_fea_registered = false; Mld6igmpNode::decr_shutdown_requests_n(); // XXX: for the FEA } if (entry->target_name() == _mfea_target) { _is_mfea_registered = false; Mld6igmpNode::decr_shutdown_requests_n(); // XXX: for the MFEA } } pop_xrl_task(); send_xrl_task(); break; case COMMAND_FAILED: // // If a command failed because the other side rejected it, this is // fatal. // XLOG_FATAL("Cannot %s interest in Finder events: %s", entry->operation_name(), xrl_error.str().c_str()); break; case NO_FINDER: case RESOLVE_FAILED: case SEND_FAILED: // // A communication error that should have been caught elsewhere // (e.g., by tracking the status of the Finder and the other targets). // Probably we caught it here because of event reordering. // In some cases we print an error. In other cases our job is done. // if (entry->is_register()) { XLOG_ERROR("XRL communication error: %s", xrl_error.str().c_str()); } else { if (entry->target_name() == _fea_target) { _is_fea_registered = false; } if (entry->target_name() == _mfea_target) { _is_mfea_registered = false; } pop_xrl_task(); send_xrl_task(); } break; case BAD_ARGS: case NO_SUCH_METHOD: case INTERNAL_ERROR: // // An error that should happen only if there is something unusual: // e.g., there is XRL mismatch, no enough internal resources, etc. // We don't try to recover from such errors, hence this is fatal. // XLOG_FATAL("Fatal XRL error: %s", xrl_error.str().c_str()); break; case REPLY_TIMED_OUT: case SEND_FAILED_TRANSIENT: // // If a transient error, then try again // XLOG_ERROR("Failed to %s interest in Finder envents: %s. " "Will try again.", entry->operation_name(), xrl_error.str().c_str()); retry_xrl_task(); break; } } int XrlMld6igmpNode::register_receiver(const string& if_name, const string& vif_name, uint8_t ip_protocol, bool enable_multicast_loopback) { Mld6igmpNode::incr_startup_requests_n(); // XXX: for FEA-receiver add_task(new RegisterUnregisterReceiver(*this, if_name, vif_name, ip_protocol, enable_multicast_loopback, true)); return (XORP_OK); } int XrlMld6igmpNode::unregister_receiver(const string& if_name, const string& vif_name, uint8_t ip_protocol) { Mld6igmpNode::incr_shutdown_requests_n(); // XXX: for FEA-non-receiver add_task(new RegisterUnregisterReceiver(*this, if_name, vif_name, ip_protocol, false, // XXX: ignored false)); return (XORP_OK); } void XrlMld6igmpNode::send_register_unregister_receiver() { bool success = true; if (! _is_finder_alive) return; // The Finder is dead XLOG_ASSERT(! _xrl_tasks_queue.empty()); XrlTaskBase* xrl_task_base = _xrl_tasks_queue.front(); RegisterUnregisterReceiver* entry; entry = dynamic_cast(xrl_task_base); XLOG_ASSERT(entry != NULL); // // Check whether we have already registered with the FEA // if (! _is_fea_registered) { retry_xrl_task(); return; } if (entry->is_register()) { // Register a receiver with the FEA if (Mld6igmpNode::is_ipv4()) { success = _xrl_fea_client4.send_register_receiver( _fea_target.c_str(), xrl_router().instance_name(), entry->if_name(), entry->vif_name(), entry->ip_protocol(), entry->enable_multicast_loopback(), callback(this, &XrlMld6igmpNode::fea_client_send_register_unregister_receiver_cb)); if (success) return; } if (Mld6igmpNode::is_ipv6()) { success = _xrl_fea_client6.send_register_receiver( _fea_target.c_str(), xrl_router().instance_name(), entry->if_name(), entry->vif_name(), entry->ip_protocol(), entry->enable_multicast_loopback(), callback(this, &XrlMld6igmpNode::fea_client_send_register_unregister_receiver_cb)); if (success) return; } } else { // Unregister a receiver with the FEA if (Mld6igmpNode::is_ipv4()) { success = _xrl_fea_client4.send_unregister_receiver( _fea_target.c_str(), xrl_router().instance_name(), entry->if_name(), entry->vif_name(), entry->ip_protocol(), callback(this, &XrlMld6igmpNode::fea_client_send_register_unregister_receiver_cb)); if (success) return; } if (Mld6igmpNode::is_ipv6()) { success = _xrl_fea_client6.send_unregister_receiver( _fea_target.c_str(), xrl_router().instance_name(), entry->if_name(), entry->vif_name(), entry->ip_protocol(), callback(this, &XrlMld6igmpNode::fea_client_send_register_unregister_receiver_cb)); if (success) return; } } if (! success) { // // If an error, then try again // XLOG_ERROR("Failed to %s register receiver on interface %s vif %s " "IP protocol %u with the FEA. " "Will try again.", entry->operation_name(), entry->if_name().c_str(), entry->vif_name().c_str(), entry->ip_protocol()); retry_xrl_task(); return; } } void XrlMld6igmpNode::fea_client_send_register_unregister_receiver_cb( const XrlError& xrl_error) { XLOG_ASSERT(! _xrl_tasks_queue.empty()); XrlTaskBase* xrl_task_base = _xrl_tasks_queue.front(); RegisterUnregisterReceiver* entry; entry = dynamic_cast(xrl_task_base); XLOG_ASSERT(entry != NULL); switch (xrl_error.error_code()) { case OKAY: // // If success, then schedule the next task // if (entry->is_register()) Mld6igmpNode::decr_startup_requests_n(); // XXX: for FEA-receiver else Mld6igmpNode::decr_shutdown_requests_n(); // XXX: for FEA-non-receiver pop_xrl_task(); send_xrl_task(); break; case COMMAND_FAILED: // // If a command failed because the other side rejected it, this is // fatal. // XLOG_FATAL("Cannot %s receiver with the FEA: %s", entry->operation_name(), xrl_error.str().c_str()); break; case NO_FINDER: case RESOLVE_FAILED: case SEND_FAILED: // // A communication error that should have been caught elsewhere // (e.g., by tracking the status of the Finder and the other targets). // Probably we caught it here because of event reordering. // In some cases we print an error. In other cases our job is done. // if (entry->is_register()) { XLOG_ERROR("XRL communication error: %s", xrl_error.str().c_str()); } else { Mld6igmpNode::decr_shutdown_requests_n(); // XXX: for FEA-non-receiver pop_xrl_task(); send_xrl_task(); } break; case BAD_ARGS: case NO_SUCH_METHOD: case INTERNAL_ERROR: // // An error that should happen only if there is something unusual: // e.g., there is XRL mismatch, no enough internal resources, etc. // We don't try to recover from such errors, hence this is fatal. // XLOG_FATAL("Fatal XRL error: %s", xrl_error.str().c_str()); break; case REPLY_TIMED_OUT: case SEND_FAILED_TRANSIENT: // // If a transient error, then try again // XLOG_ERROR("Failed to %s receiver with the FEA: %s. " "Will try again.", entry->operation_name(), xrl_error.str().c_str()); retry_xrl_task(); break; } } int XrlMld6igmpNode::join_multicast_group(const string& if_name, const string& vif_name, uint8_t ip_protocol, const IPvX& group_address) { Mld6igmpNode::incr_startup_requests_n(); // XXX: for FEA-join add_task(new JoinLeaveMulticastGroup(*this, if_name, vif_name, ip_protocol, group_address, true)); return (XORP_OK); } int XrlMld6igmpNode::leave_multicast_group(const string& if_name, const string& vif_name, uint8_t ip_protocol, const IPvX& group_address) { Mld6igmpNode::incr_shutdown_requests_n(); // XXX: for FEA-leave add_task(new JoinLeaveMulticastGroup(*this, if_name, vif_name, ip_protocol, group_address, false)); return (XORP_OK); } void XrlMld6igmpNode::send_join_leave_multicast_group() { bool success = true; if (! _is_finder_alive) return; // The Finder is dead XLOG_ASSERT(! _xrl_tasks_queue.empty()); XrlTaskBase* xrl_task_base = _xrl_tasks_queue.front(); JoinLeaveMulticastGroup* entry; entry = dynamic_cast(xrl_task_base); XLOG_ASSERT(entry != NULL); // // Check whether we have already registered with the FEA // if (! _is_fea_registered) { retry_xrl_task(); return; } if (entry->is_join()) { // Join a multicast group with the FEA if (Mld6igmpNode::is_ipv4()) { success = _xrl_fea_client4.send_join_multicast_group( _fea_target.c_str(), xrl_router().instance_name(), entry->if_name(), entry->vif_name(), entry->ip_protocol(), entry->group_address().get_ipv4(), callback(this, &XrlMld6igmpNode::fea_client_send_join_leave_multicast_group_cb)); if (success) return; } if (Mld6igmpNode::is_ipv6()) { success = _xrl_fea_client6.send_join_multicast_group( _fea_target.c_str(), xrl_router().instance_name(), entry->if_name(), entry->vif_name(), entry->ip_protocol(), entry->group_address().get_ipv6(), callback(this, &XrlMld6igmpNode::fea_client_send_join_leave_multicast_group_cb)); if (success) return; } } else { // Leave a multicast group with the FEA if (Mld6igmpNode::is_ipv4()) { success = _xrl_fea_client4.send_leave_multicast_group( _fea_target.c_str(), xrl_router().instance_name(), entry->if_name(), entry->vif_name(), entry->ip_protocol(), entry->group_address().get_ipv4(), callback(this, &XrlMld6igmpNode::fea_client_send_join_leave_multicast_group_cb)); if (success) return; } if (Mld6igmpNode::is_ipv6()) { success = _xrl_fea_client6.send_leave_multicast_group( _fea_target.c_str(), xrl_router().instance_name(), entry->if_name(), entry->vif_name(), entry->ip_protocol(), entry->group_address().get_ipv6(), callback(this, &XrlMld6igmpNode::fea_client_send_join_leave_multicast_group_cb)); if (success) return; } } if (! success) { // // If an error, then try again // XLOG_ERROR("Failed to %s group %s on interface/vif %s/%s with the FEA. " "Will try again.", entry->operation_name(), entry->group_address().str().c_str(), entry->if_name().c_str(), entry->vif_name().c_str()); retry_xrl_task(); return; } } void XrlMld6igmpNode::fea_client_send_join_leave_multicast_group_cb( const XrlError& xrl_error) { XLOG_ASSERT(! _xrl_tasks_queue.empty()); XrlTaskBase* xrl_task_base = _xrl_tasks_queue.front(); JoinLeaveMulticastGroup* entry; entry = dynamic_cast(xrl_task_base); XLOG_ASSERT(entry != NULL); switch (xrl_error.error_code()) { case OKAY: // // If success, then schedule the next task // if (entry->is_join()) Mld6igmpNode::decr_startup_requests_n(); // XXX: for FEA-join else Mld6igmpNode::decr_shutdown_requests_n(); // XXX: for FEA-leave pop_xrl_task(); send_xrl_task(); break; case COMMAND_FAILED: // // If a command failed because the other side rejected it, this is // fatal. // XLOG_FATAL("Cannot %s a multicast group with the FEA: %s", entry->operation_name(), xrl_error.str().c_str()); break; case NO_FINDER: case RESOLVE_FAILED: case SEND_FAILED: // // A communication error that should have been caught elsewhere // (e.g., by tracking the status of the Finder and the other targets). // Probably we caught it here because of event reordering. // In some cases we print an error. In other cases our job is done. // if (entry->is_join()) { XLOG_ERROR("XRL communication error: %s", xrl_error.str().c_str()); } else { Mld6igmpNode::decr_shutdown_requests_n(); // XXX: for FEA-leave pop_xrl_task(); send_xrl_task(); } break; case BAD_ARGS: case NO_SUCH_METHOD: case INTERNAL_ERROR: // // An error that should happen only if there is something unusual: // e.g., there is XRL mismatch, no enough internal resources, etc. // We don't try to recover from such errors, hence this is fatal. // XLOG_FATAL("Fatal XRL error: %s", xrl_error.str().c_str()); break; case REPLY_TIMED_OUT: case SEND_FAILED_TRANSIENT: // // If a transient error, then try again // XLOG_ERROR("Failed to %s group %s on interface/vif %s/%s " "with the FEA: %s. " "Will try again.", entry->operation_name(), entry->group_address().str().c_str(), entry->if_name().c_str(), entry->vif_name().c_str(), xrl_error.str().c_str()); retry_xrl_task(); break; } } int XrlMld6igmpNode::send_add_membership(const string& dst_module_instance_name, xorp_module_id dst_module_id, uint32_t vif_index, const IPvX& source, const IPvX& group) { Mld6igmpVif *mld6igmp_vif = Mld6igmpNode::vif_find_by_vif_index(vif_index); if (mld6igmp_vif == NULL) { XLOG_ERROR("Cannot send add_membership to %s for (%s, %s) on vif " "with vif_index %d: no such vif", dst_module_instance_name.c_str(), cstring(source), cstring(group), vif_index); return (XORP_ERROR); } _send_add_delete_membership_queue.push_back(SendAddDeleteMembership( dst_module_instance_name, dst_module_id, vif_index, source, group, true)); // If the queue was empty before, start sending the changes if (_send_add_delete_membership_queue.size() == 1) { send_add_delete_membership(); } return (XORP_OK); } int XrlMld6igmpNode::send_delete_membership(const string& dst_module_instance_name, xorp_module_id dst_module_id, uint32_t vif_index, const IPvX& source, const IPvX& group) { Mld6igmpVif *mld6igmp_vif = Mld6igmpNode::vif_find_by_vif_index(vif_index); if (mld6igmp_vif == NULL) { XLOG_ERROR("Cannot send delete_membership to %s for (%s, %s) on vif " "with vif_index %d: no such vif", dst_module_instance_name.c_str(), cstring(source), cstring(group), vif_index); return (XORP_ERROR); } _send_add_delete_membership_queue.push_back(SendAddDeleteMembership( dst_module_instance_name, dst_module_id, vif_index, source, group, false)); // If the queue was empty before, start sending the changes if (_send_add_delete_membership_queue.size() == 1) { send_add_delete_membership(); } return (XORP_OK); } void XrlMld6igmpNode::send_add_delete_membership() { bool success = true; Mld6igmpVif *mld6igmp_vif = NULL; if (! _is_finder_alive) return; // The Finder is dead if (_send_add_delete_membership_queue.empty()) return; // No more changes const SendAddDeleteMembership& membership = _send_add_delete_membership_queue.front(); mld6igmp_vif = Mld6igmpNode::vif_find_by_vif_index(membership.vif_index()); if (mld6igmp_vif == NULL) { XLOG_ERROR("Cannot send %s for (%s, %s) on vif " "with vif_index %d to %s: no such vif", membership.operation_name(), cstring(membership.source()), cstring(membership.group()), membership.vif_index(), membership.dst_module_instance_name().c_str()); _send_add_delete_membership_queue.pop_front(); goto start_timer_label; } if (membership.is_add()) { // Send add_membership to the client protocol if (Mld6igmpNode::is_ipv4()) { success = _xrl_mld6igmp_client_client.send_add_membership4( membership.dst_module_instance_name().c_str(), xrl_router().class_name(), mld6igmp_vif->name(), membership.vif_index(), membership.source().get_ipv4(), membership.group().get_ipv4(), callback(this, &XrlMld6igmpNode::mld6igmp_client_send_add_delete_membership_cb)); if (success) return; } if (Mld6igmpNode::is_ipv6()) { success = _xrl_mld6igmp_client_client.send_add_membership6( membership.dst_module_instance_name().c_str(), xrl_router().class_name(), mld6igmp_vif->name(), membership.vif_index(), membership.source().get_ipv6(), membership.group().get_ipv6(), callback(this, &XrlMld6igmpNode::mld6igmp_client_send_add_delete_membership_cb)); if (success) return; } } else { // Send delete_membership to the client protocol if (Mld6igmpNode::is_ipv4()) { success = _xrl_mld6igmp_client_client.send_delete_membership4( membership.dst_module_instance_name().c_str(), xrl_router().class_name(), mld6igmp_vif->name(), membership.vif_index(), membership.source().get_ipv4(), membership.group().get_ipv4(), callback(this, &XrlMld6igmpNode::mld6igmp_client_send_add_delete_membership_cb)); if (success) return; } if (Mld6igmpNode::is_ipv6()) { success = _xrl_mld6igmp_client_client.send_delete_membership6( membership.dst_module_instance_name().c_str(), xrl_router().class_name(), mld6igmp_vif->name(), membership.vif_index(), membership.source().get_ipv6(), membership.group().get_ipv6(), callback(this, &XrlMld6igmpNode::mld6igmp_client_send_add_delete_membership_cb)); if (success) return; } } if (! success) { // // If an error, then try again // XLOG_ERROR("Failed to send %s for (%s, %s) on vif %s to %s. " "Will try again.", membership.operation_name(), cstring(membership.source()), cstring(membership.group()), mld6igmp_vif->name().c_str(), membership.dst_module_instance_name().c_str()); start_timer_label: _send_add_delete_membership_queue_timer = _eventloop.new_oneoff_after( RETRY_TIMEVAL, callback(this, &XrlMld6igmpNode::send_add_delete_membership)); } } void XrlMld6igmpNode::mld6igmp_client_send_add_delete_membership_cb( const XrlError& xrl_error) { const SendAddDeleteMembership& membership = _send_add_delete_membership_queue.front(); switch (xrl_error.error_code()) { case OKAY: // // If success, then schedule the next task // _send_add_delete_membership_queue.pop_front(); send_add_delete_membership(); break; case COMMAND_FAILED: // // If a command failed because the other side rejected it, // then print an error and send the next one. // XLOG_ERROR("Cannot %s for a multicast group with a client: %s", membership.operation_name(), xrl_error.str().c_str()); _send_add_delete_membership_queue.pop_front(); send_add_delete_membership(); break; case NO_FINDER: case RESOLVE_FAILED: case SEND_FAILED: // // A communication error that should have been caught elsewhere // (e.g., by tracking the status of the Finder and the other targets). // Probably we caught it here because of event reordering. // In some cases we print an error. In other cases our job is done. // XLOG_ERROR("XRL communication error: %s", xrl_error.str().c_str()); break; case BAD_ARGS: case NO_SUCH_METHOD: case INTERNAL_ERROR: // // An error that should happen only if there is something unusual: // e.g., there is XRL mismatch, no enough internal resources, etc. // We don't try to recover from such errors, hence this is fatal. // XLOG_FATAL("Fatal XRL error: %s", xrl_error.str().c_str()); break; case REPLY_TIMED_OUT: case SEND_FAILED_TRANSIENT: // // If a transient error, then try again // if (! _send_add_delete_membership_queue_timer.scheduled()) { XLOG_ERROR("Failed to %s for a multicast group with a client: %s. " "Will try again.", membership.operation_name(), xrl_error.str().c_str()); _send_add_delete_membership_queue_timer = _eventloop.new_oneoff_after( RETRY_TIMEVAL, callback(this, &XrlMld6igmpNode::send_add_delete_membership)); } break; } } // // Protocol node methods // int XrlMld6igmpNode::proto_send(const string& if_name, const string& vif_name, const IPvX& src_address, const IPvX& dst_address, uint8_t ip_protocol, int32_t ip_ttl, int32_t ip_tos, bool ip_router_alert, bool ip_internet_control, const uint8_t* sndbuf, size_t sndlen, string& error_msg) { add_task(new SendProtocolMessage(*this, if_name, vif_name, src_address, dst_address, ip_protocol, ip_ttl, ip_tos, ip_router_alert, ip_internet_control, sndbuf, sndlen)); error_msg = ""; return (XORP_OK); } /** * Send a protocol message through the FEA. **/ void XrlMld6igmpNode::send_protocol_message() { bool success = true; if (! _is_finder_alive) return; // The Finder is dead XLOG_ASSERT(! _xrl_tasks_queue.empty()); XrlTaskBase* xrl_task_base = _xrl_tasks_queue.front(); SendProtocolMessage* entry; entry = dynamic_cast(xrl_task_base); XLOG_ASSERT(entry != NULL); // // Check whether we have already registered with the FEA // if (! _is_fea_registered) { retry_xrl_task(); return; } // // Send the protocol message // do { if (Mld6igmpNode::is_ipv4()) { success = _xrl_fea_client4.send_send( _fea_target.c_str(), entry->if_name(), entry->vif_name(), entry->src_address().get_ipv4(), entry->dst_address().get_ipv4(), entry->ip_protocol(), entry->ip_ttl(), entry->ip_tos(), entry->ip_router_alert(), entry->ip_internet_control(), entry->payload(), callback(this, &XrlMld6igmpNode::fea_client_send_protocol_message_cb)); if (success) return; break; } if (Mld6igmpNode::is_ipv6()) { // XXX: no Extention headers XrlAtomList ext_headers_type; XrlAtomList ext_headers_payload; success = _xrl_fea_client6.send_send( _fea_target.c_str(), entry->if_name(), entry->vif_name(), entry->src_address().get_ipv6(), entry->dst_address().get_ipv6(), entry->ip_protocol(), entry->ip_ttl(), entry->ip_tos(), entry->ip_router_alert(), entry->ip_internet_control(), ext_headers_type, ext_headers_payload, entry->payload(), callback(this, &XrlMld6igmpNode::fea_client_send_protocol_message_cb)); if (success) return; break; } XLOG_UNREACHABLE(); break; } while (false); if (! success) { // // If an error, then try again // XLOG_ERROR("Failed to send a protocol message on interface/vif %s/%s. " "Will try again.", entry->if_name().c_str(), entry->vif_name().c_str()); retry_xrl_task(); return; } } void XrlMld6igmpNode::fea_client_send_protocol_message_cb(const XrlError& xrl_error) { XLOG_ASSERT(! _xrl_tasks_queue.empty()); XrlTaskBase* xrl_task_base = _xrl_tasks_queue.front(); SendProtocolMessage* entry; entry = dynamic_cast(xrl_task_base); XLOG_ASSERT(entry != NULL); switch (xrl_error.error_code()) { case OKAY: // // If success, then schedule the next task // pop_xrl_task(); send_xrl_task(); break; case COMMAND_FAILED: // // If a command failed because the other side rejected it, // then print an error and send the next one. // // XXX: The FEA may fail to send a protocol message, therefore // we don't call XLOG_FATAL() here. For example, the transimssion // by the FEA it may fail if there is no buffer space or if an // unicast destination is not reachable. // Furthermore, all protocol messages are soft-state (i.e., they are // retransmitted periodically by the protocol), // hence we don't retransmit them here if there was an error. // XLOG_ERROR("Cannot send a protocol message: %s", xrl_error.str().c_str()); pop_xrl_task(); send_xrl_task(); break; case NO_FINDER: case RESOLVE_FAILED: case SEND_FAILED: // // A communication error that should have been caught elsewhere // (e.g., by tracking the status of the Finder and the other targets). // Probably we caught it here because of event reordering. // In some cases we print an error. In other cases our job is done. // XLOG_ERROR("Cannot send a protocol message: %s", xrl_error.str().c_str()); pop_xrl_task(); send_xrl_task(); break; case BAD_ARGS: case NO_SUCH_METHOD: case INTERNAL_ERROR: // // An error that should happen only if there is something unusual: // e.g., there is XRL mismatch, no enough internal resources, etc. // We don't try to recover from such errors, hence this is fatal. // XLOG_FATAL("Fatal XRL error: %s", xrl_error.str().c_str()); break; case REPLY_TIMED_OUT: case SEND_FAILED_TRANSIENT: // // XXX: if a transient error, then don't try again. // All protocol messages are soft-state (i.e., they are // retransmitted periodically by the protocol), // hence we don't retransmit them here if there was an error. // XLOG_ERROR("Failed to send a protocol message: %s", xrl_error.str().c_str()); pop_xrl_task(); send_xrl_task(); break; } } // // Protocol node CLI methods // int XrlMld6igmpNode::add_cli_command_to_cli_manager(const char *command_name, const char *command_help, bool is_command_cd, const char *command_cd_prompt, bool is_command_processor ) { bool success = false; if (! _is_finder_alive) return (XORP_ERROR); // The Finder is dead success = _xrl_cli_manager_client.send_add_cli_command( xorp_module_name(family(), XORP_MODULE_CLI), xrl_router().class_name(), string(command_name), string(command_help), is_command_cd, string(command_cd_prompt), is_command_processor, callback(this, &XrlMld6igmpNode::cli_manager_client_send_add_cli_command_cb)); if (! success) { XLOG_ERROR("Failed to add CLI command '%s' to the CLI manager", command_name); return (XORP_ERROR); } return (XORP_OK); } void XrlMld6igmpNode::cli_manager_client_send_add_cli_command_cb( const XrlError& xrl_error) { switch (xrl_error.error_code()) { case OKAY: // // If success, then we are done // break; case COMMAND_FAILED: // // If a command failed because the other side rejected it, this is // fatal. // XLOG_FATAL("Cannot add a command to CLI manager: %s", xrl_error.str().c_str()); break; case NO_FINDER: case RESOLVE_FAILED: case SEND_FAILED: // // A communication error that should have been caught elsewhere // (e.g., by tracking the status of the Finder and the other targets). // Probably we caught it here because of event reordering. // In some cases we print an error. In other cases our job is done. // XLOG_ERROR("Cannot add a command to CLI manager: %s", xrl_error.str().c_str()); break; case BAD_ARGS: case NO_SUCH_METHOD: case INTERNAL_ERROR: // // An error that should happen only if there is something unusual: // e.g., there is XRL mismatch, no enough internal resources, etc. // We don't try to recover from such errors, hence this is fatal. // XLOG_FATAL("Fatal XRL error: %s", xrl_error.str().c_str()); break; case REPLY_TIMED_OUT: case SEND_FAILED_TRANSIENT: // // If a transient error, then try again // // // TODO: if the command failed, then we should retransmit it // XLOG_ERROR("Failed to add a command to CLI manager: %s", xrl_error.str().c_str()); break; } } int XrlMld6igmpNode::delete_cli_command_from_cli_manager(const char *command_name) { bool success = true; if (! _is_finder_alive) return (XORP_ERROR); // The Finder is dead success = _xrl_cli_manager_client.send_delete_cli_command( xorp_module_name(family(), XORP_MODULE_CLI), xrl_router().class_name(), string(command_name), callback(this, &XrlMld6igmpNode::cli_manager_client_send_delete_cli_command_cb)); if (! success) { XLOG_ERROR("Failed to delete CLI command '%s' with the CLI manager", command_name); return (XORP_ERROR); } return (XORP_OK); } void XrlMld6igmpNode::cli_manager_client_send_delete_cli_command_cb( const XrlError& xrl_error) { switch (xrl_error.error_code()) { case OKAY: // // If success, then we are done // break; case COMMAND_FAILED: // // If a command failed because the other side rejected it, this is // fatal. // XLOG_FATAL("Cannot delete a command from CLI manager: %s", xrl_error.str().c_str()); break; case NO_FINDER: case RESOLVE_FAILED: case SEND_FAILED: // // A communication error that should have been caught elsewhere // (e.g., by tracking the status of the Finder and the other targets). // Probably we caught it here because of event reordering. // In some cases we print an error. In other cases our job is done. // XLOG_ERROR("Cannot delete a command from CLI manager: %s", xrl_error.str().c_str()); break; case BAD_ARGS: case NO_SUCH_METHOD: case INTERNAL_ERROR: // // An error that should happen only if there is something unusual: // e.g., there is XRL mismatch, no enough internal resources, etc. // We don't try to recover from such errors, hence this is fatal. // XLOG_FATAL("Fatal XRL error: %s", xrl_error.str().c_str()); break; case REPLY_TIMED_OUT: case SEND_FAILED_TRANSIENT: // // If a transient error, then try again // // // TODO: if the command failed, then we should retransmit it // XLOG_ERROR("Failed to delete a command from CLI manager: %s", xrl_error.str().c_str()); break; } } // // XRL target methods // XrlCmdError XrlMld6igmpNode::common_0_1_get_target_name( // Output values, string& name) { name = xrl_router().class_name(); return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::common_0_1_get_version( // Output values, string& version) { version = XORP_MODULE_VERSION; return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::common_0_1_get_status(// Output values, uint32_t& status, string& reason) { status = Mld6igmpNode::node_status(reason); return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::common_0_1_shutdown() { bool is_error = false; string error_msg; if (shutdown() != XORP_OK) { if (! is_error) error_msg = c_format("Failed to shutdown %s", Mld6igmpNode::proto_is_igmp() ? "IGMP" : "MLD"); is_error = true; } if (is_error) return XrlCmdError::COMMAND_FAILED(error_msg); return XrlCmdError::OKAY(); } /** * Announce target birth to observer. * * @param target_class the target class name. * * @param target_instance the target instance name. */ XrlCmdError XrlMld6igmpNode::finder_event_observer_0_1_xrl_target_birth( // Input values, const string& target_class, const string& target_instance) { if (target_class == _fea_target) { _is_fea_alive = true; Mld6igmpNode::decr_startup_requests_n(); // XXX: for FEA birth } if (target_class == _mfea_target) { _is_mfea_alive = true; Mld6igmpNode::decr_startup_requests_n(); // XXX: for MFEA birth // // XXX: when the startup is completed, // IfMgrHintObserver::tree_complete() will be called. // if (_ifmgr.startup() != XORP_OK) { Mld6igmpNode::set_status(SERVICE_FAILED); Mld6igmpNode::update_status(); } } return XrlCmdError::OKAY(); UNUSED(target_instance); } /** * Announce target death to observer. * * @param target_class the target class name. * * @param target_instance the target instance name. */ XrlCmdError XrlMld6igmpNode::finder_event_observer_0_1_xrl_target_death( // Input values, const string& target_class, const string& target_instance) { bool do_shutdown = false; if (target_class == _fea_target) { XLOG_ERROR("FEA (instance %s) has died, shutting down.", target_instance.c_str()); _is_fea_alive = false; do_shutdown = true; } if (target_class == _mfea_target) { XLOG_ERROR("MFEA (instance %s) has died, shutting down.", target_instance.c_str()); _is_mfea_alive = false; do_shutdown = true; } if (do_shutdown) stop_mld6igmp(); return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::cli_processor_0_1_process_command( // Input values, const string& processor_name, const string& cli_term_name, const uint32_t& cli_session_id, const string& command_name, const string& command_args, // Output values, string& ret_processor_name, string& ret_cli_term_name, uint32_t& ret_cli_session_id, string& ret_command_output) { Mld6igmpNodeCli::cli_process_command(processor_name, cli_term_name, cli_session_id, command_name, command_args, ret_processor_name, ret_cli_term_name, ret_cli_session_id, ret_command_output); return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::raw_packet4_client_0_1_recv( // Input values, const string& if_name, const string& vif_name, const IPv4& src_address, const IPv4& dst_address, const uint32_t& ip_protocol, const int32_t& ip_ttl, const int32_t& ip_tos, const bool& ip_router_alert, const bool& ip_internet_control, const vector& payload) { string error_msg; // // Verify the address family // if (! Mld6igmpNode::is_ipv4()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv4"); return XrlCmdError::COMMAND_FAILED(error_msg); } // // Receive the message // Mld6igmpNode::proto_recv(if_name, vif_name, IPvX(src_address), IPvX(dst_address), ip_protocol, ip_ttl, ip_tos, ip_router_alert, ip_internet_control, payload, error_msg); // XXX: no error returned, because if there is any, it is at the // protocol level, and the FEA shoudn't care about it. // // Success // return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::raw_packet6_client_0_1_recv( // Input values, const string& if_name, const string& vif_name, const IPv6& src_address, const IPv6& dst_address, const uint32_t& ip_protocol, const int32_t& ip_ttl, const int32_t& ip_tos, const bool& ip_router_alert, const bool& ip_internet_control, const XrlAtomList& ext_headers_type, const XrlAtomList& ext_headers_payload, const vector& payload) { string error_msg; UNUSED(ext_headers_type); UNUSED(ext_headers_payload); // // Verify the address family // if (! Mld6igmpNode::is_ipv6()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv6"); return XrlCmdError::COMMAND_FAILED(error_msg); } // // Receive the message // Mld6igmpNode::proto_recv(if_name, vif_name, IPvX(src_address), IPvX(dst_address), ip_protocol, ip_ttl, ip_tos, ip_router_alert, ip_internet_control, payload, error_msg); // XXX: no error returned, because if there is any, it is at the // protocol level, and the FEA shoudn't care about it. // // Success // return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_enable_vif( // Input values, const string& vif_name, const bool& enable) { string error_msg; int ret_value; if (enable) ret_value = Mld6igmpNode::enable_vif(vif_name, error_msg); else ret_value = Mld6igmpNode::disable_vif(vif_name, error_msg); if (ret_value != XORP_OK) return XrlCmdError::COMMAND_FAILED(error_msg); return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_start_vif( // Input values, const string& vif_name) { string error_msg; if (Mld6igmpNode::start_vif(vif_name, error_msg) != XORP_OK) return XrlCmdError::COMMAND_FAILED(error_msg); return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_stop_vif( // Input values, const string& vif_name) { string error_msg; if (Mld6igmpNode::stop_vif(vif_name, error_msg) != XORP_OK) return XrlCmdError::COMMAND_FAILED(error_msg); return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_enable_all_vifs( // Input values, const bool& enable) { string error_msg; int ret_value; if (enable) ret_value = Mld6igmpNode::enable_all_vifs(); else ret_value = Mld6igmpNode::enable_all_vifs(); if (ret_value != XORP_OK) { if (enable) error_msg = c_format("Failed to enable all vifs"); else error_msg = c_format("Failed to disable all vifs"); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_start_all_vifs() { string error_msg; if (Mld6igmpNode::start_all_vifs() != XORP_OK) { error_msg = c_format("Failed to start all vifs"); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_stop_all_vifs() { string error_msg; if (Mld6igmpNode::stop_all_vifs() != XORP_OK) { error_msg = c_format("Failed to stop all vifs"); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_enable_mld6igmp( // Input values, const bool& enable) { string error_msg; int ret_value; if (enable) ret_value = enable_mld6igmp(); else ret_value = disable_mld6igmp(); if (ret_value != XORP_OK) { if (enable) error_msg = c_format("Failed to enable MLD6IGMP"); else error_msg = c_format("Failed to disable MLD6IGMP"); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_start_mld6igmp() { string error_msg; if (start_mld6igmp() != XORP_OK) { error_msg = c_format("Failed to start MLD6IMGP"); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_stop_mld6igmp() { string error_msg; if (stop_mld6igmp() != XORP_OK) { error_msg = c_format("Failed to stop MLD6IMGP"); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_enable_cli( // Input values, const bool& enable) { string error_msg; int ret_value; if (enable) ret_value = enable_cli(); else ret_value = disable_cli(); if (ret_value != XORP_OK) { if (enable) error_msg = c_format("Failed to enable MLD6IGMP CLI"); else error_msg = c_format("Failed to disable MLD6IGMP CLI"); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_start_cli() { string error_msg; if (start_cli() != XORP_OK) { error_msg = c_format("Failed to start MLD6IMGP CLI"); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_stop_cli() { string error_msg; if (stop_cli() != XORP_OK) { error_msg = c_format("Failed to stop MLD6IMGP CLI"); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_get_vif_proto_version( // Input values, const string& vif_name, // Output values, uint32_t& proto_version) { string error_msg; int v; if (Mld6igmpNode::get_vif_proto_version(vif_name, v, error_msg) != XORP_OK) return XrlCmdError::COMMAND_FAILED(error_msg); proto_version = v; return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_set_vif_proto_version( // Input values, const string& vif_name, const uint32_t& proto_version) { string error_msg; if (Mld6igmpNode::set_vif_proto_version(vif_name, proto_version, error_msg) != XORP_OK) return XrlCmdError::COMMAND_FAILED(error_msg); return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_reset_vif_proto_version( // Input values, const string& vif_name) { string error_msg; if (Mld6igmpNode::reset_vif_proto_version(vif_name, error_msg) != XORP_OK) return XrlCmdError::COMMAND_FAILED(error_msg); return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_get_vif_ip_router_alert_option_check( // Input values, const string& vif_name, // Output values, bool& enabled) { string error_msg; bool v; if (Mld6igmpNode::get_vif_ip_router_alert_option_check(vif_name, v, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } enabled = v; return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_set_vif_ip_router_alert_option_check( // Input values, const string& vif_name, const bool& enable) { string error_msg; if (Mld6igmpNode::set_vif_ip_router_alert_option_check(vif_name, enable, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_reset_vif_ip_router_alert_option_check( // Input values, const string& vif_name) { string error_msg; if (Mld6igmpNode::reset_vif_ip_router_alert_option_check(vif_name, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_get_vif_query_interval( // Input values, const string& vif_name, // Output values, uint32_t& interval_sec, uint32_t& interval_usec) { string error_msg; TimeVal v; if (Mld6igmpNode::get_vif_query_interval(vif_name, v, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } interval_sec = v.sec(); interval_usec = v.usec(); return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_set_vif_query_interval( // Input values, const string& vif_name, const uint32_t& interval_sec, const uint32_t& interval_usec) { string error_msg; TimeVal interval(interval_sec, interval_usec); if (Mld6igmpNode::set_vif_query_interval(vif_name, interval, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_reset_vif_query_interval( // Input values, const string& vif_name) { string error_msg; if (Mld6igmpNode::reset_vif_query_interval(vif_name, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_get_vif_query_last_member_interval( // Input values, const string& vif_name, // Output values, uint32_t& interval_sec, uint32_t& interval_usec) { string error_msg; TimeVal v; if (Mld6igmpNode::get_vif_query_last_member_interval(vif_name, v, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } interval_sec = v.sec(); interval_usec = v.usec(); return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_set_vif_query_last_member_interval( // Input values, const string& vif_name, const uint32_t& interval_sec, const uint32_t& interval_usec) { string error_msg; TimeVal interval(interval_sec, interval_usec); if (Mld6igmpNode::set_vif_query_last_member_interval(vif_name, interval, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_reset_vif_query_last_member_interval( // Input values, const string& vif_name) { string error_msg; if (Mld6igmpNode::reset_vif_query_last_member_interval(vif_name, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_get_vif_query_response_interval( // Input values, const string& vif_name, // Output values, uint32_t& interval_sec, uint32_t& interval_usec) { string error_msg; TimeVal v; if (Mld6igmpNode::get_vif_query_response_interval(vif_name, v, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } interval_sec = v.sec(); interval_usec = v.usec(); return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_set_vif_query_response_interval( // Input values, const string& vif_name, const uint32_t& interval_sec, const uint32_t& interval_usec) { string error_msg; TimeVal interval(interval_sec, interval_usec); if (Mld6igmpNode::set_vif_query_response_interval(vif_name, interval, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_reset_vif_query_response_interval( // Input values, const string& vif_name) { string error_msg; if (Mld6igmpNode::reset_vif_query_response_interval(vif_name, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_get_vif_robust_count( // Input values, const string& vif_name, // Output values, uint32_t& robust_count) { string error_msg; uint32_t v; if (Mld6igmpNode::get_vif_robust_count(vif_name, v, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } robust_count = v; return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_set_vif_robust_count( // Input values, const string& vif_name, const uint32_t& robust_count) { string error_msg; if (Mld6igmpNode::set_vif_robust_count(vif_name, robust_count, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_reset_vif_robust_count( // Input values, const string& vif_name) { string error_msg; if (Mld6igmpNode::reset_vif_robust_count(vif_name, error_msg) != XORP_OK) { return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_log_trace_all( // Input values, const bool& enable) { Mld6igmpNode::set_log_trace(enable); return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_add_protocol4( // Input values, const string& xrl_sender_name, const string& , // protocol_name, const uint32_t& protocol_id, const string& vif_name, const uint32_t& vif_index) { string error_msg; // // Verify the address family // if (! Mld6igmpNode::is_ipv4()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv4"); return XrlCmdError::COMMAND_FAILED(error_msg); } // // Verify the module ID // xorp_module_id src_module_id = static_cast(protocol_id); if (! is_valid_module_id(src_module_id)) { error_msg = c_format("Invalid module ID = %d", XORP_INT_CAST(protocol_id)); return XrlCmdError::COMMAND_FAILED(error_msg); } if (Mld6igmpNode::add_protocol(xrl_sender_name, src_module_id, vif_index) != XORP_OK) { // TODO: must find-out and return the reason for failure error_msg = c_format("Cannot add protocol instance '%s' " "on vif %s with vif_index %d", xrl_sender_name.c_str(), vif_name.c_str(), XORP_INT_CAST(vif_index)); return XrlCmdError::COMMAND_FAILED(error_msg); } // // Send info about all existing membership on the particular vif. // Mld6igmpVif *mld6igmp_vif = Mld6igmpNode::vif_find_by_vif_index(vif_index); if (mld6igmp_vif == NULL) { Mld6igmpNode::delete_protocol(xrl_sender_name, src_module_id, vif_index); error_msg = c_format("Cannot add protocol instance '%s' " "on vif %s with vif_index %d: " "no such vif", xrl_sender_name.c_str(), vif_name.c_str(), XORP_INT_CAST(vif_index)); return XrlCmdError::COMMAND_FAILED(error_msg); } Mld6igmpGroupSet::const_iterator iter; for (iter = mld6igmp_vif->group_records().begin(); iter != mld6igmp_vif->group_records().end(); ++iter) { const Mld6igmpGroupRecord *group_record = iter->second; send_add_membership(xrl_sender_name.c_str(), src_module_id, mld6igmp_vif->vif_index(), IPvX::ZERO(family()), group_record->group()); } // // Success // return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_add_protocol6( // Input values, const string& xrl_sender_name, const string& , // protocol_name, const uint32_t& protocol_id, const string& vif_name, const uint32_t& vif_index) { string error_msg; // // Verify the address family // if (! Mld6igmpNode::is_ipv6()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv6"); return XrlCmdError::COMMAND_FAILED(error_msg); } // // Verify the module ID // xorp_module_id src_module_id = static_cast(protocol_id); if (! is_valid_module_id(src_module_id)) { error_msg = c_format("Invalid module ID = %d", XORP_INT_CAST(protocol_id)); return XrlCmdError::COMMAND_FAILED(error_msg); } if (Mld6igmpNode::add_protocol(xrl_sender_name, src_module_id, vif_index) != XORP_OK) { // TODO: must find-out and return the reason for failure error_msg = c_format("Cannot add protocol instance '%s' " "on vif %s with vif_index %d", xrl_sender_name.c_str(), vif_name.c_str(), XORP_INT_CAST(vif_index)); return XrlCmdError::COMMAND_FAILED(error_msg); } // // Send info about all existing membership on the particular vif. // Mld6igmpVif *mld6igmp_vif = Mld6igmpNode::vif_find_by_vif_index(vif_index); if (mld6igmp_vif == NULL) { Mld6igmpNode::delete_protocol(xrl_sender_name, src_module_id, vif_index); error_msg = c_format("Cannot add protocol instance '%s' " "on vif %s with vif_index %d: " "no such vif", xrl_sender_name.c_str(), vif_name.c_str(), XORP_INT_CAST(vif_index)); return XrlCmdError::COMMAND_FAILED(error_msg); } Mld6igmpGroupSet::const_iterator iter; for (iter = mld6igmp_vif->group_records().begin(); iter != mld6igmp_vif->group_records().end(); ++iter) { const Mld6igmpGroupRecord *group_record = iter->second; send_add_membership(xrl_sender_name.c_str(), src_module_id, mld6igmp_vif->vif_index(), IPvX::ZERO(family()), group_record->group()); } // // Success // return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_delete_protocol4( // Input values, const string& xrl_sender_name, const string& , // protocol_name, const uint32_t& protocol_id, const string& vif_name, const uint32_t& vif_index) { string error_msg; // // Verify the address family // if (! Mld6igmpNode::is_ipv4()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv4"); return XrlCmdError::COMMAND_FAILED(error_msg); } // // Verify the module ID // xorp_module_id src_module_id = static_cast(protocol_id); if (! is_valid_module_id(src_module_id)) { error_msg = c_format("Invalid module ID = %d", XORP_INT_CAST(protocol_id)); return XrlCmdError::COMMAND_FAILED(error_msg); } if (Mld6igmpNode::delete_protocol(xrl_sender_name, src_module_id, vif_index) != XORP_OK) { // TODO: must find-out and return the reason for failure error_msg = c_format("Cannot delete protocol instance '%s' " "on vif %s with vif_index %d", xrl_sender_name.c_str(), vif_name.c_str(), XORP_INT_CAST(vif_index)); return XrlCmdError::COMMAND_FAILED(error_msg); } // // Success // return XrlCmdError::OKAY(); } XrlCmdError XrlMld6igmpNode::mld6igmp_0_1_delete_protocol6( // Input values, const string& xrl_sender_name, const string& , // protocol_name, const uint32_t& protocol_id, const string& vif_name, const uint32_t& vif_index) { string error_msg; // // Verify the address family // if (! Mld6igmpNode::is_ipv6()) { error_msg = c_format("Received protocol message with " "invalid address family: IPv6"); return XrlCmdError::COMMAND_FAILED(error_msg); } // // Verify the module ID // xorp_module_id src_module_id = static_cast(protocol_id); if (! is_valid_module_id(src_module_id)) { error_msg = c_format("Invalid module ID = %d", XORP_INT_CAST(protocol_id)); return XrlCmdError::COMMAND_FAILED(error_msg); } if (Mld6igmpNode::delete_protocol(xrl_sender_name, src_module_id, vif_index) != XORP_OK) { // TODO: must find-out and return the reason for failure error_msg = c_format("Cannot delete protocol instance '%s' " "on vif %s with vif_index %d", xrl_sender_name.c_str(), vif_name.c_str(), XORP_INT_CAST(vif_index)); return XrlCmdError::COMMAND_FAILED(error_msg); } // // Success // return XrlCmdError::OKAY(); } xorp/contrib/mld6igmp_lite/mld6igmp_node_cli.hh0000664000076400007640000000560111540224221021743 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/contrib/mld6igmp_lite/mld6igmp_node_cli.hh,v 1.3 2008/10/02 21:56:32 bms Exp $ #ifndef __MLD6IGMP_MLD6IGMP_NODE_CLI_HH__ #define __MLD6IGMP_MLD6IGMP_NODE_CLI_HH__ // // MLD6IGMP protocol CLI // #include "libproto/proto_node_cli.hh" // // Constants definitions // // // Structures/classes, typedefs and macros // // MLD6IGMP protocol CLI class class Mld6igmpNode; /** * @short The class for @ref Mld6igmpNode CLI access. */ class Mld6igmpNodeCli : public ProtoNodeCli { public: /** * Constructor for a given MLD6IGMP node. * * @param mld6igmp_node the @ref Mld6igmpNode this node belongs to. */ Mld6igmpNodeCli(Mld6igmpNode& mld6igmp_node); /** * Destructor */ virtual ~Mld6igmpNodeCli(); /** * Start the CLI operation. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int start(); /** * Stop the CLI operation. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int stop(); /** * Enable node operation. * * If an unit is not enabled, it cannot be start, or pending-start. */ void enable(); /** * Disable 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 disable(); /** * Install all MLD6IGMP-related CLI commands to the CLI. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_all_cli_commands(); private: Mld6igmpNode& mld6igmp_node() const { return (_mld6igmp_node); } Mld6igmpNode& _mld6igmp_node; // // MLD6IGMP CLI commands // int cli_show_mld6igmp_interface(const vector& argv); int cli_show_mld6igmp_interface_address(const vector& argv); int cli_show_mld6igmp_group(const vector& argv); }; // // Global variables // // // Global functions prototypes // #endif // __MLD6IGMP_MLD6IGMP_NODE_CLI_HH__ xorp/contrib/mld6igmp_lite/mld6igmp_config.cc0000664000076400007640000003520711421137511021432 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // TODO: a temporary solution for various MLD6IGMP configuration // #include "mld6igmp_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvx.hh" #include "mld6igmp_node.hh" #include "mld6igmp_vif.hh" int Mld6igmpNode::set_config_all_vifs_done(string& error_msg) { map::iterator vif_iter; map& configured_vifs = ProtoNode::configured_vifs(); string dummy_error_msg; // // Add new vifs and update existing ones // for (vif_iter = configured_vifs.begin(); vif_iter != configured_vifs.end(); ++vif_iter) { Vif* vif = &vif_iter->second; Vif* node_vif = vif_find_by_name(vif->name()); if (vif->is_pim_register()) continue; // XXX: don't add the PIM Register vifs // // Add a new vif // if (node_vif == NULL) { add_vif(*vif, dummy_error_msg); continue; } // // Update the vif flags // set_vif_flags(vif->name(), vif->is_pim_register(), vif->is_p2p(), vif->is_loopback(), vif->is_multicast_capable(), vif->is_broadcast_capable(), vif->is_underlying_vif_up(), vif->mtu(), dummy_error_msg); } // // Add new vif addresses, update existing ones, and remove old addresses // for (vif_iter = configured_vifs.begin(); vif_iter != configured_vifs.end(); ++vif_iter) { Vif* vif = &vif_iter->second; Vif* node_vif = vif_find_by_name(vif->name()); list::const_iterator vif_addr_iter; if (vif->is_pim_register()) continue; // XXX: don't add the PIM Register vifs if (node_vif == NULL) continue; for (vif_addr_iter = vif->addr_list().begin(); vif_addr_iter != vif->addr_list().end(); ++vif_addr_iter) { const VifAddr& vif_addr = *vif_addr_iter; add_vif_addr(vif->name(), vif_addr.addr(), vif_addr.subnet_addr(), vif_addr.broadcast_addr(), vif_addr.peer_addr(), dummy_error_msg); } // // Delete vif addresses that don't exist anymore // { list delete_addresses_list; 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->find_address(vif_addr.addr()) == 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; delete_vif_addr(vif->name(), ipvx, dummy_error_msg); } } } // // Remove vifs that don't exist anymore // for (uint32_t i = 0; i < maxvifs(); i++) { Vif* node_vif = vif_find_by_vif_index(i); if (node_vif == NULL) continue; if (configured_vifs.find(node_vif->name()) == configured_vifs.end()) { // Delete the interface string vif_name = node_vif->name(); delete_vif(vif_name, dummy_error_msg); continue; } } if (end_config(error_msg) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int Mld6igmpNode::get_vif_proto_version(const string& vif_name, int& proto_version, string& error_msg) { Mld6igmpVif *mld6igmp_vif = vif_find_by_name(vif_name); if (mld6igmp_vif == NULL) { error_msg = c_format("Cannot get protocol version for vif %s: " "no such vif", vif_name.c_str()); return (XORP_ERROR); } proto_version = mld6igmp_vif->proto_version(); return (XORP_OK); } int Mld6igmpNode::set_vif_proto_version(const string& vif_name, int proto_version, string& error_msg) { Mld6igmpVif *mld6igmp_vif = vif_find_by_name(vif_name); if (start_config(error_msg) != XORP_OK) return (XORP_ERROR); if (mld6igmp_vif == NULL) { end_config(error_msg); error_msg = c_format("Cannot set protocol version for vif %s: " "no such vif", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } if (mld6igmp_vif->set_proto_version(proto_version) != XORP_OK) { end_config(error_msg); error_msg = c_format("Cannot set protocol version for vif %s: " "invalid protocol version %d", vif_name.c_str(), proto_version); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } if (end_config(error_msg) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int Mld6igmpNode::reset_vif_proto_version(const string& vif_name, string& error_msg) { Mld6igmpVif *mld6igmp_vif = vif_find_by_name(vif_name); if (start_config(error_msg) != XORP_OK) return (XORP_ERROR); if (mld6igmp_vif == NULL) { end_config(error_msg); error_msg = c_format("Cannot reset protocol version for vif %s: " "no such vif", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } mld6igmp_vif->set_proto_version(mld6igmp_vif->proto_version_default()); if (end_config(error_msg) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int Mld6igmpNode::get_vif_ip_router_alert_option_check(const string& vif_name, bool& enabled, string& error_msg) { Mld6igmpVif *mld6igmp_vif = vif_find_by_name(vif_name); if (mld6igmp_vif == NULL) { error_msg = c_format("Cannot get 'IP Router Alert option check' " "flag for vif %s: " "no such vif", vif_name.c_str()); return (XORP_ERROR); } enabled = mld6igmp_vif->ip_router_alert_option_check().get(); return (XORP_OK); } int Mld6igmpNode::set_vif_ip_router_alert_option_check(const string& vif_name, bool enable, string& error_msg) { Mld6igmpVif *mld6igmp_vif = vif_find_by_name(vif_name); if (start_config(error_msg) != XORP_OK) return (XORP_ERROR); if (mld6igmp_vif == NULL) { end_config(error_msg); error_msg = c_format("Cannot set 'IP Router Alert option check' " "flag for vif %s: " "no such vif", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } mld6igmp_vif->ip_router_alert_option_check().set(enable); if (end_config(error_msg) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int Mld6igmpNode::reset_vif_ip_router_alert_option_check(const string& vif_name, string& error_msg) { Mld6igmpVif *mld6igmp_vif = vif_find_by_name(vif_name); if (start_config(error_msg) != XORP_OK) return (XORP_ERROR); if (mld6igmp_vif == NULL) { end_config(error_msg); error_msg = c_format("Cannot reset 'IP Router Alert option check' " "flag for vif %s: " "no such vif", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } mld6igmp_vif->ip_router_alert_option_check().reset(); if (end_config(error_msg) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int Mld6igmpNode::get_vif_query_interval(const string& vif_name, TimeVal& interval, string& error_msg) { Mld6igmpVif *mld6igmp_vif = vif_find_by_name(vif_name); if (mld6igmp_vif == NULL) { error_msg = c_format("Cannot get Query Interval for vif %s: " "no such vif", vif_name.c_str()); return (XORP_ERROR); } interval = mld6igmp_vif->configured_query_interval().get(); return (XORP_OK); } int Mld6igmpNode::set_vif_query_interval(const string& vif_name, const TimeVal& interval, string& error_msg) { Mld6igmpVif *mld6igmp_vif = vif_find_by_name(vif_name); if (start_config(error_msg) != XORP_OK) return (XORP_ERROR); if (mld6igmp_vif == NULL) { end_config(error_msg); error_msg = c_format("Cannot set Query Interval for vif %s: " "no such vif", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } mld6igmp_vif->configured_query_interval().set(interval); if (end_config(error_msg) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int Mld6igmpNode::reset_vif_query_interval(const string& vif_name, string& error_msg) { Mld6igmpVif *mld6igmp_vif = vif_find_by_name(vif_name); if (start_config(error_msg) != XORP_OK) return (XORP_ERROR); if (mld6igmp_vif == NULL) { end_config(error_msg); error_msg = c_format("Cannot reset Query Interval for vif %s: " "no such vif", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } mld6igmp_vif->configured_query_interval().reset(); if (end_config(error_msg) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int Mld6igmpNode::get_vif_query_last_member_interval(const string& vif_name, TimeVal& interval, string& error_msg) { Mld6igmpVif *mld6igmp_vif = vif_find_by_name(vif_name); if (mld6igmp_vif == NULL) { error_msg = c_format("Cannot get Last Member Query Interval for vif %s: " "no such vif", vif_name.c_str()); return (XORP_ERROR); } interval = mld6igmp_vif->query_last_member_interval().get(); return (XORP_OK); } int Mld6igmpNode::set_vif_query_last_member_interval(const string& vif_name, const TimeVal& interval, string& error_msg) { Mld6igmpVif *mld6igmp_vif = vif_find_by_name(vif_name); if (start_config(error_msg) != XORP_OK) return (XORP_ERROR); if (mld6igmp_vif == NULL) { end_config(error_msg); error_msg = c_format("Cannot set Last Member Query Interval for vif %s: " "no such vif", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } mld6igmp_vif->query_last_member_interval().set(interval); if (end_config(error_msg) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int Mld6igmpNode::reset_vif_query_last_member_interval(const string& vif_name, string& error_msg) { Mld6igmpVif *mld6igmp_vif = vif_find_by_name(vif_name); if (start_config(error_msg) != XORP_OK) return (XORP_ERROR); if (mld6igmp_vif == NULL) { end_config(error_msg); error_msg = c_format("Cannot reset Last Member Query Interval for vif %s: " "no such vif", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } mld6igmp_vif->query_last_member_interval().reset(); if (end_config(error_msg) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int Mld6igmpNode::get_vif_query_response_interval(const string& vif_name, TimeVal& interval, string& error_msg) { Mld6igmpVif *mld6igmp_vif = vif_find_by_name(vif_name); if (mld6igmp_vif == NULL) { error_msg = c_format("Cannot get Query Response Interval for vif %s: " "no such vif", vif_name.c_str()); return (XORP_ERROR); } interval = mld6igmp_vif->query_response_interval().get(); return (XORP_OK); } int Mld6igmpNode::set_vif_query_response_interval(const string& vif_name, const TimeVal& interval, string& error_msg) { Mld6igmpVif *mld6igmp_vif = vif_find_by_name(vif_name); if (start_config(error_msg) != XORP_OK) return (XORP_ERROR); if (mld6igmp_vif == NULL) { end_config(error_msg); error_msg = c_format("Cannot set Query Response Interval for vif %s: " "no such vif", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } mld6igmp_vif->query_response_interval().set(interval); if (end_config(error_msg) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int Mld6igmpNode::reset_vif_query_response_interval(const string& vif_name, string& error_msg) { Mld6igmpVif *mld6igmp_vif = vif_find_by_name(vif_name); if (start_config(error_msg) != XORP_OK) return (XORP_ERROR); if (mld6igmp_vif == NULL) { end_config(error_msg); error_msg = c_format("Cannot reset Query Response Interval for vif %s: " "no such vif", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } mld6igmp_vif->query_response_interval().reset(); if (end_config(error_msg) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int Mld6igmpNode::get_vif_robust_count(const string& vif_name, uint32_t& robust_count, string& error_msg) { Mld6igmpVif *mld6igmp_vif = vif_find_by_name(vif_name); if (mld6igmp_vif == NULL) { error_msg = c_format("Cannot get Robustness Variable count for vif %s: " "no such vif", vif_name.c_str()); return (XORP_ERROR); } robust_count = mld6igmp_vif->configured_robust_count().get(); return (XORP_OK); } int Mld6igmpNode::set_vif_robust_count(const string& vif_name, uint32_t robust_count, string& error_msg) { Mld6igmpVif *mld6igmp_vif = vif_find_by_name(vif_name); if (start_config(error_msg) != XORP_OK) return (XORP_ERROR); if (mld6igmp_vif == NULL) { end_config(error_msg); error_msg = c_format("Cannot set Robustness Variable count for vif %s: " "no such vif", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } mld6igmp_vif->configured_robust_count().set(robust_count); if (end_config(error_msg) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int Mld6igmpNode::reset_vif_robust_count(const string& vif_name, string& error_msg) { Mld6igmpVif *mld6igmp_vif = vif_find_by_name(vif_name); if (start_config(error_msg) != XORP_OK) return (XORP_ERROR); if (mld6igmp_vif == NULL) { end_config(error_msg); error_msg = c_format("Cannot reset Robustness Variable count for vif %s: " "no such vif", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } mld6igmp_vif->configured_robust_count().reset(); if (end_config(error_msg) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } xorp/contrib/mld6igmp_lite/mld.tp0000664000076400007640000001551411421137511017177 0ustar greearbgreearb/* $XORP: xorp/etc/templates/mld.tp,v 1.21 2006/12/20 08:30:15 pavlin Exp $ */ protocols { mld { targetname: txt = "MLD"; disable: toggle = false; enabled: bool; /* %deprecated */ interface @: txt { vif @: txt { disable: toggle = false; enabled: bool; /* %deprecated */ version: u32 = 1; /* Default to MLDv1 */ enable-ip-router-alert-option-check: bool = false; query-interval: u32 = 125; query-last-member-interval: u32 = 1; query-response-interval: u32 = 10; robust-count: u32 = 2; } } traceoptions { flag { all { disable: toggle = false; enabled: bool; /* %deprecated */ } } } } } protocols { mld { %help: short "Configure the MLD protocol"; %modinfo: provides mld; %modinfo: depends mfea6; %modinfo: path "contrib/mld6igmp_lite/xorp_mld"; %modinfo: default_targetname "mld6igmp"; %modinfo: status_method xrl "$(mld.targetname)/common/0.1/get_status->status:u32&reason:txt"; %modinfo: startup_method xrl "$(mld.targetname)/mld6igmp/0.1/start_mld6igmp"; %modinfo: shutdown_method xrl "$(mld.targetname)/common/0.1/shutdown"; %mandatory: $(@.targetname); targetname { %user-hidden: "XRL target name"; %help: short "XRL target name"; %set:; } disable { %help: short "Disable the MLD protocol"; %create:; %set: xrl "$(mld.targetname)/mld6igmp/0.1/enable_mld6igmp?enable:bool=`~$(@)`"; %delete: xrl "$(mld.targetname)/mld6igmp/0.1/enable_mld6igmp?enable:bool=`~$(DEFAULT)`"; } enabled { %deprecated: "Statement 'enabled: true/false' is replaced with 'disable: false/true'"; %help: short "Enable the MLD protocol"; %create:; %set: xrl "$(mld.targetname)/mld6igmp/0.1/enable_mld6igmp?enable:bool=$(@)"; } interface @ { %help: short "Configure MLD on a network interface"; %delete: xrl "$(mld.targetname)/mld6igmp/0.1/stop_vif?vif_name:txt=$(interface.@)"; vif @ { %help: short "Configure MLD on a virtual interface"; %activate: xrl "$(mld.targetname)/mld6igmp/0.1/start_vif?vif_name:txt=$(vif.@)"; %update: xrl "$(mld.targetname)/mld6igmp/0.1/start_vif?vif_name:txt=$(vif.@)"; %delete: xrl "$(mld.targetname)/mld6igmp/0.1/stop_vif?vif_name:txt=$(vif.@)"; disable { %help: short "Disable MLD on an interface"; %set: xrl "$(mld.targetname)/mld6igmp/0.1/enable_vif?vif_name:txt=$(vif.@)&enable:bool=`~$(@)`"; %delete: xrl "$(mld.targetname)/mld6igmp/0.1/enable_vif?vif_name:txt=$(vif.@)&enable:bool=`~$(DEFAULT)`"; } enabled { %deprecated: "Statement 'enabled: true/false' is replaced with 'disable: false/true'"; %help: short "Enable MLD on an interface"; %set: xrl "$(mld.targetname)/mld6igmp/0.1/enable_vif?vif_name:txt=$(vif.@)&enable:bool=$(@)"; %delete: xrl "$(mld.targetname)/mld6igmp/0.1/enable_vif?vif_name:txt=$(vif.@)&enable:bool=`~$(DEFAULT)`"; } version { %help: short "Set the MLD protocol version"; %allow-range: $(@) "1" "2" %help: "The MLD protocol version"; %create: xrl "$(mld.targetname)/mld6igmp/0.1/set_vif_proto_version?vif_name:txt=$(vif.@)&proto_version:u32=$(@)"; %set: xrl "$(mld.targetname)/mld6igmp/0.1/set_vif_proto_version?vif_name:txt=$(vif.@)&proto_version:u32=$(@)"; %delete: xrl "$(mld.targetname)/mld6igmp/0.1/set_vif_proto_version?vif_name:txt=$(vif.@)&proto_version:u32=$(DEFAULT)"; } enable-ip-router-alert-option-check { %help: short "Enable the IP Router Alert option check"; %create: xrl "$(mld.targetname)/mld6igmp/0.1/set_vif_ip_router_alert_option_check?vif_name:txt=$(vif.@)&enable:bool=$(@)"; %set: xrl "$(mld.targetname)/mld6igmp/0.1/set_vif_ip_router_alert_option_check?vif_name:txt=$(vif.@)&enable:bool=$(@)"; %delete: xrl "$(mld.targetname)/mld6igmp/0.1/set_vif_ip_router_alert_option_check?vif_name:txt=$(vif.@)&enable:bool=$(DEFAULT)"; } query-interval { %help: short "Set the query interval (in seconds)"; %allow-range: $(@) "1" "1024" %help: "The query interval (in seconds)"; %create: xrl "$(mld.targetname)/mld6igmp/0.1/set_vif_query_interval?vif_name:txt=$(vif.@)&interval_sec:u32=$(@)&interval_usec:u32=0"; %set: xrl "$(mld.targetname)/mld6igmp/0.1/set_vif_query_interval?vif_name:txt=$(vif.@)&interval_sec:u32=$(@)&interval_usec:u32=0"; %delete: xrl "$(mld.targetname)/mld6igmp/0.1/set_vif_query_interval?vif_name:txt=$(vif.@)&interval_sec:u32=$(DEFAULT)&interval_usec:u32=0"; } query-last-member-interval { %help: short "Set the last member query interval (in seconds)"; %allow-range: $(@) "1" "1024" %help: "The last member query interval (in seconds)"; %create: xrl "$(mld.targetname)/mld6igmp/0.1/set_vif_query_last_member_interval?vif_name:txt=$(vif.@)&interval_sec:u32=$(@)&interval_usec:u32=0"; %set: xrl "$(mld.targetname)/mld6igmp/0.1/set_vif_query_last_member_interval?vif_name:txt=$(vif.@)&interval_sec:u32=$(@)&interval_usec:u32=0"; %delete: xrl "$(mld.targetname)/mld6igmp/0.1/set_vif_query_last_member_interval?vif_name:txt=$(vif.@)&interval_sec:u32=$(DEFAULT)&interval_usec:u32=0"; } query-response-interval { %help: short "Set the query response interval (in seconds)"; %allow-range: $(@) "1" "1024" %help: "The query response interval (in seconds)"; %create: xrl "$(mld.targetname)/mld6igmp/0.1/set_vif_query_response_interval?vif_name:txt=$(vif.@)&interval_sec:u32=$(@)&interval_usec:u32=0"; %set: xrl "$(mld.targetname)/mld6igmp/0.1/set_vif_query_response_interval?vif_name:txt=$(vif.@)&interval_sec:u32=$(@)&interval_usec:u32=0"; %delete: xrl "$(mld.targetname)/mld6igmp/0.1/set_vif_query_response_interval?vif_name:txt=$(vif.@)&interval_sec:u32=$(DEFAULT)&interval_usec:u32=0"; } robust-count { %help: short "Set the robustness variable count"; %allow-range: $(@) "2" "10" %help: "The robustness variable count"; %create: xrl "$(mld.targetname)/mld6igmp/0.1/set_vif_robust_count?vif_name:txt=$(vif.@)&robust_count:u32=$(@)"; %set: xrl "$(mld.targetname)/mld6igmp/0.1/set_vif_robust_count?vif_name:txt=$(vif.@)&robust_count:u32=$(@)"; %delete: xrl "$(mld.targetname)/mld6igmp/0.1/set_vif_robust_count?vif_name:txt=$(vif.@)&robust_count:u32=$(DEFAULT)"; } } } traceoptions { %help: short "Configure the tracing options"; flag { %help: short "Configure the tracing operation to perform"; all { %help: short "Configure all tracing operations"; disable { %help: short "Enable all tracing operations"; %set: xrl "$(mld.targetname)/mld6igmp/0.1/log_trace_all?enable:bool=`~$(@)`"; %delete: xrl "$(mld.targetname)/mld6igmp/0.1/log_trace_all?enable:bool=$(DEFAULT)"; } enabled { %deprecated: "Statement 'enabled: true/false' is replaced with 'disable: false/true'"; %help: short "Enable all tracing operations"; %set: xrl "$(mld.targetname)/mld6igmp/0.1/log_trace_all?enable:bool=$(@)"; } } } } } } xorp/contrib/mld6igmp_lite/mld6igmp_node.cc0000664000076400007640000011724311421137511021113 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // Multicast Listener Discovery and Internet Group Management Protocol // node implementation (common part). // IGMPv1 and IGMPv2 (RFC 2236), IGMPv3 (RFC 3376), // MLDv1 (RFC 2710), and MLDv2 (RFC 3810). // #include "mld6igmp_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvx.hh" #include "mld6igmp_node.hh" #include "mld6igmp_vif.hh" // // Exported variables // // // Local constants definitions // // // Local structures/classes, typedefs and macros // // // Local variables // // // Local functions prototypes // /** * Mld6igmpNode::Mld6igmpNode: * @family: The address family (%AF_INET or %AF_INET6 * for IPv4 and IPv6 respectively). * @module_id: The module ID (must be %XORP_MODULE_MLD6IGMP). * @eventloop: The event loop. * * MLD6IGMP node constructor. **/ Mld6igmpNode::Mld6igmpNode(int family, xorp_module_id module_id, EventLoop& eventloop) : ProtoNode(family, module_id, eventloop), _is_log_trace(false) { XLOG_ASSERT(module_id == XORP_MODULE_MLD6IGMP); if (module_id != XORP_MODULE_MLD6IGMP) { XLOG_FATAL("Invalid module ID = %d (must be 'XORP_MODULE_MLD6IGMP' = %d)", module_id, XORP_MODULE_MLD6IGMP); } _buffer_recv = BUFFER_MALLOC(BUF_SIZE_DEFAULT); // // Set the node status // ProtoNode::set_node_status(PROC_STARTUP); // // Set myself as an observer when the node status changes // set_observer(this); } /** * Mld6igmpNode::~Mld6igmpNode: * @: * * MLD6IGMP node destructor. * **/ Mld6igmpNode::~Mld6igmpNode() { // // Unset myself as an observer when the node status changes // unset_observer(this); stop(); ProtoNode::set_node_status(PROC_NULL); delete_all_vifs(); BUFFER_FREE(_buffer_recv); } /** * Mld6igmpNode::start: * @: * * Start the MLD or IGMP protocol. * TODO: This function should not start the protocol operation on the * interfaces. The interfaces must be activated separately. * After the startup operations are completed, * Mld6igmpNode::final_start() is called to complete the job. * * Return value: %XORP_OK on success, otherwize %XORP_ERROR. **/ int Mld6igmpNode::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() != XORP_OK) return (XORP_ERROR); // // Register with the FEA and MFEA // fea_register_startup(); mfea_register_startup(); // // Set the node status // ProtoNode::set_node_status(PROC_STARTUP); // // Update the node status // update_status(); return (XORP_OK); } /** * Mld6igmpNode::final_start: * @: * * Complete the start-up of the MLD/IGMP protocol. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int Mld6igmpNode::final_start() { #if 0 // TODO: XXX: PAVPAVPAV if (! is_pending_up()) return (XORP_ERROR); #endif if (ProtoNode::start() != XORP_OK) { ProtoNode::stop(); return (XORP_ERROR); } // Start the mld6igmp_vifs start_all_vifs(); XLOG_INFO("Protocol started"); return (XORP_OK); } /** * Mld6igmpNode::stop: * @: * * Gracefully stop the MLD or IGMP protocol. * XXX: After the cleanup is completed, * Mld6igmpNode::final_stop() is called to complete the job. * XXX: This function, unlike start(), will stop the protocol * operation on all interfaces. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int Mld6igmpNode::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() != XORP_OK) return (XORP_ERROR); // // Perform misc. MLD6IGMP-specific stop operations // // XXX: nothing to do // Stop the vifs stop_all_vifs(); // // Set the node status // ProtoNode::set_node_status(PROC_SHUTDOWN); // // Update the node status // update_status(); return (XORP_OK); } /** * Mld6igmpNode::final_stop: * @: * * Completely stop the MLD/IGMP protocol. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int Mld6igmpNode::final_stop() { if (! (is_up() || is_pending_up() || is_pending_down())) return (XORP_ERROR); if (ProtoNode::stop() != XORP_OK) return (XORP_ERROR); XLOG_INFO("Protocol stopped"); return (XORP_OK); } /** * Enable the node operation. * * If an unit is not enabled, it cannot be start, or pending-start. */ void Mld6igmpNode::enable() { ProtoUnit::enable(); XLOG_INFO("Protocol 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 Mld6igmpNode::disable() { stop(); ProtoUnit::disable(); XLOG_INFO("Protocol disabled"); } /** * Get the IP protocol number. * * @return the IP protocol number. */ uint8_t Mld6igmpNode::ip_protocol_number() const { if (proto_is_igmp()) return (IPPROTO_IGMP); if (proto_is_mld6()) return (IPPROTO_ICMPV6); XLOG_UNREACHABLE(); return (0); } void Mld6igmpNode::status_change(ServiceBase* service, ServiceStatus old_status, ServiceStatus new_status) { if (service == this) { if ((old_status == SERVICE_STARTING) && (new_status == SERVICE_RUNNING)) { // The startup process has completed if (final_start() != XORP_OK) { 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)) { decr_shutdown_requests_n(); // XXX: for the ifmgr } } } void Mld6igmpNode::tree_complete() { decr_startup_requests_n(); // XXX: for the ifmgr // // XXX: we use same actions when the tree is completed or updates are made // updates_made(); } void Mld6igmpNode::updates_made() { map::iterator mld6igmp_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.interfaces().begin(); ifmgr_iface_iter != _iftree.interfaces().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; mld6igmp_vif_iter = configured_vifs().find(ifmgr_vif_name); if (mld6igmp_vif_iter != configured_vifs().end()) { node_vif = &(mld6igmp_vif_iter->second); } // // Add a new vif // if (node_vif == NULL) { uint32_t vif_index = ifmgr_vif.vif_index(); XLOG_ASSERT(vif_index != Vif::VIF_INDEX_INVALID); if (add_config_vif(ifmgr_vif_name, vif_index, error_msg) != XORP_OK) { XLOG_ERROR("Cannot add vif %s to the set of configured " "vifs: %s", ifmgr_vif_name.c_str(), error_msg.c_str()); continue; } mld6igmp_vif_iter = configured_vifs().find(ifmgr_vif_name); XLOG_ASSERT(mld6igmp_vif_iter != configured_vifs().end()); node_vif = &(mld6igmp_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, ifmgr_vif.pim_register(), ifmgr_vif.p2p_capable(), ifmgr_vif.loopback(), ifmgr_vif.multicast_capable(), ifmgr_vif.broadcast_capable(), is_up, ifmgr_iface.mtu(), error_msg); } } // // Add new vif addresses, update existing ones, and remove old addresses // for (ifmgr_iface_iter = _iftree.interfaces().begin(); ifmgr_iface_iter != _iftree.interfaces().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 // mld6igmp_vif_iter = configured_vifs().find(ifmgr_vif_name); if (mld6igmp_vif_iter != configured_vifs().end()) { node_vif = &(mld6igmp_vif_iter->second); } if (is_ipv4()) { IfMgrVifAtom::IPv4Map::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) != XORP_OK) { 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) != XORP_OK) { 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) != XORP_OK) { 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::IPv6Map::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) != XORP_OK) { 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) != XORP_OK) { 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) != XORP_OK) { 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) != XORP_OK) { 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 (mld6igmp_vif_iter = configured_vifs().begin(); mld6igmp_vif_iter != configured_vifs().end(); ++mld6igmp_vif_iter) { Vif* node_vif = &mld6igmp_vif_iter->second; #if 0 if (node_vif->is_pim_register()) continue; // XXX: don't delete the PIM Register vif #endif 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) != XORP_OK) { 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); } /** * Mld6igmpNode::add_vif: * @vif: Information about the new Mld6igmpVif to install. * @error_msg: The error message (if error). * * Install a new MLD/IGMP vif. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int Mld6igmpNode::add_vif(const Vif& vif, string& error_msg) { // // Create a new Mld6igmpVif // Mld6igmpVif *mld6igmp_vif = new Mld6igmpVif(*this, vif); if (ProtoNode::add_vif(mld6igmp_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 mld6igmp_vif; return (XORP_ERROR); } // // Update and check the primary address // do { if (mld6igmp_vif->update_primary_address(error_msg) == XORP_OK) break; if (mld6igmp_vif->addr_ptr() == NULL) { // XXX: don't print an error if the vif has no addresses break; } if (mld6igmp_vif->is_loopback() || mld6igmp_vif->is_pim_register()) { // XXX: don't print an error if this is a loopback or register_vif break; } XLOG_ERROR("Error updating primary address for vif %s: %s", mld6igmp_vif->name().c_str(), error_msg.c_str()); return (XORP_ERROR); } while (false); XLOG_INFO("Interface added: %s", mld6igmp_vif->str().c_str()); return (XORP_OK); } /** * Mld6igmpNode::add_vif: * @vif_name: The name of the new vif. * @vif_index: The vif index of the new vif. * @error_msg: The error message (if error). * * Install a new MLD/IGMP vif. If the vif exists, nothing is installed. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int Mld6igmpNode::add_vif(const string& vif_name, uint32_t vif_index, string& error_msg) { Mld6igmpVif *mld6igmp_vif = vif_find_by_vif_index(vif_index); if ((mld6igmp_vif != NULL) && (mld6igmp_vif->name() == vif_name)) { return (XORP_OK); // Already have this vif } // // Create a new Vif // Vif vif(vif_name); vif.set_vif_index(vif_index); if (add_vif(vif, error_msg) != XORP_OK) { return (XORP_ERROR); } return (XORP_OK); } /** * Mld6igmpNode::delete_vif: * @vif_name: The name of the vif to delete. * @error_msg: The error message (if error). * * Delete an existing MLD/IGMP vif. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int Mld6igmpNode::delete_vif(const string& vif_name, string& error_msg) { Mld6igmpVif *mld6igmp_vif = vif_find_by_name(vif_name); if (mld6igmp_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(mld6igmp_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 mld6igmp_vif; return (XORP_ERROR); } delete mld6igmp_vif; XLOG_INFO("Interface deleted: %s", vif_name.c_str()); return (XORP_OK); } int Mld6igmpNode::set_vif_flags(const string& vif_name, bool is_pim_register, bool is_p2p, bool is_loopback, bool is_multicast, bool is_broadcast, bool is_up, uint32_t mtu, string& error_msg) { bool is_changed = false; Mld6igmpVif *mld6igmp_vif = vif_find_by_name(vif_name); if (mld6igmp_vif == NULL) { error_msg = c_format("Cannot set flags vif %s: no such vif", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } if (mld6igmp_vif->is_pim_register() != is_pim_register) { mld6igmp_vif->set_pim_register(is_pim_register); is_changed = true; } if (mld6igmp_vif->is_p2p() != is_p2p) { mld6igmp_vif->set_p2p(is_p2p); is_changed = true; } if (mld6igmp_vif->is_loopback() != is_loopback) { mld6igmp_vif->set_loopback(is_loopback); is_changed = true; } if (mld6igmp_vif->is_multicast_capable() != is_multicast) { mld6igmp_vif->set_multicast_capable(is_multicast); is_changed = true; } if (mld6igmp_vif->is_broadcast_capable() != is_broadcast) { mld6igmp_vif->set_broadcast_capable(is_broadcast); is_changed = true; } if (mld6igmp_vif->is_underlying_vif_up() != is_up) { mld6igmp_vif->set_underlying_vif_up(is_up); is_changed = true; } if (mld6igmp_vif->mtu() != mtu) { mld6igmp_vif->set_mtu(mtu); is_changed = true; } if (is_changed) XLOG_INFO("Interface flags changed: %s", mld6igmp_vif->str().c_str()); return (XORP_OK); } int Mld6igmpNode::add_vif_addr(const string& vif_name, const IPvX& addr, const IPvXNet& subnet_addr, const IPvX& broadcast_addr, const IPvX& peer_addr, string& error_msg) { Mld6igmpVif *mld6igmp_vif = vif_find_by_name(vif_name); if (mld6igmp_vif == NULL) { error_msg = c_format("Cannot add address on vif %s: no such vif", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } const VifAddr vif_addr(addr, subnet_addr, broadcast_addr, peer_addr); // // Check the arguments // if (! addr.is_unicast()) { error_msg = c_format("Cannot add address on vif %s: " "invalid unicast address: %s", vif_name.c_str(), addr.str().c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } if ((addr.af() != family()) || (subnet_addr.af() != family()) || (broadcast_addr.af() != family()) || (peer_addr.af() != family())) { error_msg = c_format("Cannot add address on vif %s: " "invalid address family: %s ", vif_name.c_str(), vif_addr.str().c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } VifAddr* node_vif_addr = mld6igmp_vif->find_address(addr); if ((node_vif_addr != NULL) && (*node_vif_addr == vif_addr)) return (XORP_OK); // Already have this address // // TODO: If an interface changes its primary IP address, then // we should do something about it. // // However, by adding or updating an existing address we cannot // change a valid primary address, hence we do nothing here. // if (node_vif_addr != NULL) { // Update the address XLOG_INFO("Updated existing address on vif %s: old is %s new is %s", mld6igmp_vif->name().c_str(), node_vif_addr->str().c_str(), vif_addr.str().c_str()); *node_vif_addr = vif_addr; } else { // Add a new address mld6igmp_vif->add_address(vif_addr); XLOG_INFO("Added new address to vif %s: %s", mld6igmp_vif->name().c_str(), vif_addr.str().c_str()); } // // Update and check the primary address // do { if (mld6igmp_vif->update_primary_address(error_msg) == XORP_OK) break; if (! (mld6igmp_vif->is_up() || mld6igmp_vif->is_pending_up())) { // XXX: print an error only if the interface is UP or PENDING_UP break; } if (mld6igmp_vif->is_loopback() || mld6igmp_vif->is_pim_register()) { // XXX: don't print an error if this is a loopback or register_vif break; } XLOG_ERROR("Error updating primary address for vif %s: %s", mld6igmp_vif->name().c_str(), error_msg.c_str()); return (XORP_ERROR); } while (false); return (XORP_OK); } int Mld6igmpNode::delete_vif_addr(const string& vif_name, const IPvX& addr, string& error_msg) { Mld6igmpVif *mld6igmp_vif = vif_find_by_name(vif_name); if (mld6igmp_vif == NULL) { error_msg = c_format("Cannot delete address on vif %s: no such vif", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } const VifAddr *tmp_vif_addr = mld6igmp_vif->find_address(addr); if (tmp_vif_addr == NULL) { error_msg = c_format("Cannot delete address on vif %s: " "invalid address %s", vif_name.c_str(), addr.str().c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } VifAddr vif_addr = *tmp_vif_addr; // Get a copy // // Get the vif's old primary address and whether the vif is UP // bool old_vif_is_up = mld6igmp_vif->is_up() || mld6igmp_vif->is_pending_up(); IPvX old_primary_addr = mld6igmp_vif->primary_addr(); // // If an interface's primary address is deleted, first stop the vif. // if (old_vif_is_up) { if (mld6igmp_vif->primary_addr() == addr) { string dummy_error_msg; mld6igmp_vif->stop(dummy_error_msg); } } if (mld6igmp_vif->delete_address(addr) != XORP_OK) { XLOG_UNREACHABLE(); return (XORP_ERROR); } XLOG_INFO("Deleted address on interface %s: %s", mld6igmp_vif->name().c_str(), vif_addr.str().c_str()); // // Update and check the primary address. // If the vif has no primary address, then stop it. // If the vif's primary address was changed, then restart the vif. // do { string dummy_error_msg; if (mld6igmp_vif->update_primary_address(error_msg) != XORP_OK) { XLOG_ERROR("Error updating primary address for vif %s: %s", mld6igmp_vif->name().c_str(), error_msg.c_str()); } if (mld6igmp_vif->primary_addr().is_zero()) { mld6igmp_vif->stop(dummy_error_msg); break; } if (old_primary_addr == mld6igmp_vif->primary_addr()) break; // Nothing changed // Conditionally restart the interface mld6igmp_vif->stop(dummy_error_msg); if (old_vif_is_up) mld6igmp_vif->start(dummy_error_msg); break; } while (false); return (XORP_OK); } /** * Mld6igmpNode::enable_vif: * @vif_name: The name of the vif to enable. * @error_msg: The error message (if error). * * Enable an existing MLD6IGMP vif. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int Mld6igmpNode::enable_vif(const string& vif_name, string& error_msg) { Mld6igmpVif *mld6igmp_vif = vif_find_by_name(vif_name); if (mld6igmp_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); } mld6igmp_vif->enable(); return (XORP_OK); } /** * Mld6igmpNode::disable_vif: * @vif_name: The name of the vif to disable. * @error_msg: The error message (if error). * * Disable an existing MLD6IGMP vif. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int Mld6igmpNode::disable_vif(const string& vif_name, string& error_msg) { Mld6igmpVif *mld6igmp_vif = vif_find_by_name(vif_name); if (mld6igmp_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); } mld6igmp_vif->disable(); return (XORP_OK); } /** * Mld6igmpNode::start_vif: * @vif_name: The name of the vif to start. * @error_msg: The error message (if error). * * Start an existing MLD6IGMP vif. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int Mld6igmpNode::start_vif(const string& vif_name, string& error_msg) { Mld6igmpVif *mld6igmp_vif = vif_find_by_name(vif_name); if (mld6igmp_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 (mld6igmp_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); } return (XORP_OK); } /** * Mld6igmpNode::stop_vif: * @vif_name: The name of the vif to stop. * @error_msg: The error message (if error). * * Stop an existing MLD6IGMP vif. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int Mld6igmpNode::stop_vif(const string& vif_name, string& error_msg) { Mld6igmpVif *mld6igmp_vif = vif_find_by_name(vif_name); if (mld6igmp_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 (mld6igmp_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); } /** * Mld6igmpNode::start_all_vifs: * @: * * Start MLD/IGMP on all enabled interfaces. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int Mld6igmpNode::start_all_vifs() { vector::iterator iter; string error_msg; int ret_value = XORP_OK; for (iter = proto_vifs().begin(); iter != proto_vifs().end(); ++iter) { Mld6igmpVif *mld6igmp_vif = (*iter); if (mld6igmp_vif == NULL) continue; if (start_vif(mld6igmp_vif->name(), error_msg) != XORP_OK) ret_value = XORP_ERROR; } return (ret_value); } /** * Mld6igmpNode::stop_all_vifs: * @: * * Stop MLD/IGMP on all interfaces it was running on. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int Mld6igmpNode::stop_all_vifs() { vector::iterator iter; string error_msg; int ret_value = XORP_OK; for (iter = proto_vifs().begin(); iter != proto_vifs().end(); ++iter) { Mld6igmpVif *mld6igmp_vif = (*iter); if (mld6igmp_vif == NULL) continue; if (stop_vif(mld6igmp_vif->name(), error_msg) != XORP_OK) ret_value = XORP_ERROR; } return (ret_value); } /** * Mld6igmpNode::enable_all_vifs: * @: * * Enable MLD/IGMP on all interfaces. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int Mld6igmpNode::enable_all_vifs() { vector::iterator iter; string error_msg; int ret_value = XORP_OK; for (iter = proto_vifs().begin(); iter != proto_vifs().end(); ++iter) { Mld6igmpVif *mld6igmp_vif = (*iter); if (mld6igmp_vif == NULL) continue; if (enable_vif(mld6igmp_vif->name(), error_msg) != XORP_OK) ret_value = XORP_ERROR; } return (ret_value); } /** * Mld6igmpNode::disable_all_vifs: * @: * * Disable MLD/IGMP on all interfaces. All running interfaces are stopped * first. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int Mld6igmpNode::disable_all_vifs() { vector::iterator iter; string error_msg; int ret_value = XORP_OK; for (iter = proto_vifs().begin(); iter != proto_vifs().end(); ++iter) { Mld6igmpVif *mld6igmp_vif = (*iter); if (mld6igmp_vif == NULL) continue; if (disable_vif(mld6igmp_vif->name(), error_msg) != XORP_OK) ret_value = XORP_ERROR; } return (ret_value); } /** * Mld6igmpNode::delete_all_vifs: * @: * * Delete all MLD/IGMP vifs. **/ void Mld6igmpNode::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) { Mld6igmpVif *mld6igmp_vif = (*iter); if (mld6igmp_vif != NULL) { string vif_name = mld6igmp_vif->name(); vif_names.push_back(mld6igmp_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 Mld6igmpNode::vif_shutdown_completed(const string& vif_name) { vector::iterator iter; // // If all vifs have completed the shutdown, then de-register with // the MFEA. // for (iter = proto_vifs().begin(); iter != proto_vifs().end(); ++iter) { Mld6igmpVif *mld6igmp_vif = *iter; if (mld6igmp_vif == NULL) continue; if (! mld6igmp_vif->is_down()) return; } // // De-register with the FEA and MFEA // if (ServiceBase::status() == SERVICE_SHUTTING_DOWN) { mfea_register_shutdown(); fea_register_shutdown(); } UNUSED(vif_name); } int Mld6igmpNode::proto_recv(const string& if_name, const string& vif_name, const IPvX& src_address, const IPvX& dst_address, uint8_t ip_protocol, int32_t ip_ttl, int32_t ip_tos, bool ip_router_alert, bool ip_internet_control, const vector& payload, string& error_msg) { Mld6igmpVif *mld6igmp_vif = NULL; int ret_value = XORP_ERROR; debug_msg("Received message on %s/%s from %s to %s: " "ip_ttl = %d ip_tos = %#x ip_router_alert = %d " "ip_internet_control = %d rcvlen = %u\n", if_name.c_str(), vif_name.c_str(), cstring(src_address), cstring(dst_address), ip_ttl, ip_tos, ip_router_alert, ip_internet_control, XORP_UINT_CAST(payload.size())); UNUSED(if_name); // // XXX: We registered to receive only one protocol, hence we ignore // the ip_protocol value. // UNUSED(ip_protocol); // // Check whether the node is up. // if (! is_up()) { error_msg = c_format("MLD/IGMP node is not UP"); return (XORP_ERROR); } // // Find the vif for that packet // mld6igmp_vif = vif_find_by_name(vif_name); if (mld6igmp_vif == NULL) { error_msg = c_format("Cannot find vif with vif_name = %s", vif_name.c_str()); return (XORP_ERROR); } // Copy the data to the receiving #buffer_t BUFFER_RESET(_buffer_recv); BUFFER_PUT_DATA(&payload[0], _buffer_recv, payload.size()); // Process the data by the vif ret_value = mld6igmp_vif->mld6igmp_recv(src_address, dst_address, ip_ttl, ip_tos, ip_router_alert, ip_internet_control, _buffer_recv, error_msg); return (ret_value); buflen_error: XLOG_UNREACHABLE(); return (XORP_ERROR); } int Mld6igmpNode::mld6igmp_send(const string& if_name, const string& vif_name, const IPvX& src_address, const IPvX& dst_address, uint8_t ip_protocol, int32_t ip_ttl, int32_t ip_tos, bool ip_router_alert, bool ip_internet_control, buffer_t *buffer, string& error_msg) { if (! is_up()) { error_msg = c_format("MLD/IGMP node is not UP"); return (XORP_ERROR); } if (proto_send(if_name, vif_name, src_address, dst_address, ip_protocol, ip_ttl, ip_tos, ip_router_alert, ip_internet_control, BUFFER_DATA_HEAD(buffer), BUFFER_DATA_SIZE(buffer), error_msg) != XORP_OK) { return (XORP_ERROR); } return (XORP_OK); } /** * Mld6igmpNode::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. * @vif_index: The vif index of the interface to add the protocol to. * * Add a protocol to the list of entries that would be notified if there * is membership change on a particular interface. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int Mld6igmpNode::add_protocol(const string& module_instance_name, xorp_module_id module_id, uint32_t vif_index) { Mld6igmpVif *mld6igmp_vif = vif_find_by_vif_index(vif_index); if (mld6igmp_vif == NULL) { XLOG_ERROR("Cannot add protocol instance %s on vif_index %d: " "no such vif", module_instance_name.c_str(), vif_index); return (XORP_ERROR); } if (mld6igmp_vif->add_protocol(module_id, module_instance_name) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } /** * Mld6igmpNode::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. * @vif_index: The vif index of the interface to delete the protocol from. * * Delete a protocol from the list of entries that would be notified if there * is membership change on a particular interface. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int Mld6igmpNode::delete_protocol(const string& module_instance_name, xorp_module_id module_id, uint32_t vif_index) { Mld6igmpVif *mld6igmp_vif = vif_find_by_vif_index(vif_index); if (mld6igmp_vif == NULL) { XLOG_ERROR("Cannot delete protocol instance %s on vif_index %d: " "no such vif", module_instance_name.c_str(), vif_index); return (XORP_ERROR); } if (mld6igmp_vif->delete_protocol(module_id, module_instance_name) != XORP_OK) { return (XORP_ERROR); } return (XORP_OK); } /** * Mld6igmpNode::join_prune_notify_routing: * @module_instance_name: The module instance name of the protocol to notify. * @module_id: The #xorp_module_id of the protocol to notify. * @vif_index: The vif index of the interface with membership change. * @source: The source address of the (S,G) or (*,G) entry that has changed. * In case of group-specific membership, it is IPvX::ZERO(). * @group: The group address of the (S,G) or (*,G) entry that has changed. * @action_jp: The membership change type #action_jp_t: * either %ACTION_JOIN or %ACTION_PRUNE. * * Notify the protocol instance with name @module_instance_name that there is * multicast membership change on interface with vif index of @vif_index. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int Mld6igmpNode::join_prune_notify_routing(const string& module_instance_name, xorp_module_id module_id, uint32_t vif_index, const IPvX& source, const IPvX& group, action_jp_t action_jp) { // // Send add/delete membership to the registered protocol instance. // switch (action_jp) { case ACTION_JOIN: send_add_membership(module_instance_name, module_id, vif_index, source, group); break; case ACTION_PRUNE: send_delete_membership(module_instance_name, module_id, vif_index, source, group); break; default: XLOG_UNREACHABLE(); break; } return (XORP_OK); } /** * Mld6igmpNode::is_directly_connected: * @mld6igmp_vif: The virtual interface to test against. * @ipaddr_test: The address to test. * * Note that the virtual interface the address is directly connected to * must be UP. * * Return value: True if @ipaddr_test is directly connected to @mld6igmp_vif, * otherwise false. **/ bool Mld6igmpNode::is_directly_connected(const Mld6igmpVif& mld6igmp_vif, const IPvX& ipaddr_test) const { if (! mld6igmp_vif.is_up()) return (false); #if 0 // TODO: not implemented yet // // Test the alternative subnets // list::const_iterator iter; for (iter = mld6igmp_vif.alternative_subnet_list().begin(); iter != mld6igmp_vif.alternative_subnet_list().end(); ++iter) { const IPvXNet& ipvxnet = *iter; if (ipvxnet.contains(ipaddr_test)) return true; } #endif // // Test the same subnet addresses, or the P2P addresses // return (mld6igmp_vif.is_same_subnet(ipaddr_test) || mld6igmp_vif.is_same_p2p(ipaddr_test)); } xorp/contrib/mld6igmp_lite/mld6igmp_module.h0000664000076400007640000000237111421137511021310 0ustar greearbgreearb/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- */ /* * Copyright (c) 2001-2009 XORP, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 2, June * 1991 as published by the Free Software Foundation. Redistribution * and/or modification of this program under the terms of any other * version of the GNU General Public License is not permitted. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, * see the GNU General Public License, Version 2, a copy of which can be * found in the XORP LICENSE.gpl file. * * XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; * http://xorp.net */ /* * $XORP: xorp/contrib/mld6igmp_lite/mld6igmp_module.h,v 1.3 2008/10/02 21:56:32 bms Exp $ */ /* * Module definitions. */ #ifndef __MLD6IGMP_MLD6IGMP_MODULE_H__ #define __MLD6IGMP_MLD6IGMP_MODULE_H__ #ifndef XORP_MODULE_NAME #define XORP_MODULE_NAME "MLD6IGMP" #endif #ifndef XORP_MODULE_VERSION #define XORP_MODULE_VERSION "0.1" #endif #endif /* __MLD6IGMP_MLD6IGMP_MODULE_H__ */ xorp/contrib/mld6igmp_lite/mld6_proto.h0000664000076400007640000001340211421137511020306 0ustar greearbgreearb/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- */ /* * Copyright (c) 2001-2009 XORP, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 2, June * 1991 as published by the Free Software Foundation. Redistribution * and/or modification of this program under the terms of any other * version of the GNU General Public License is not permitted. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, * see the GNU General Public License, Version 2, a copy of which can be * found in the XORP LICENSE.gpl file. * * XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; * http://xorp.net */ /* * $XORP: xorp/contrib/mld6igmp_lite/mld6_proto.h,v 1.3 2008/10/02 21:56:31 bms Exp $ */ #ifndef __MLD6IGMP_MLD6_PROTO_H__ #define __MLD6IGMP_MLD6_PROTO_H__ /* * Multicast Listener Discovery protocol-specific definitions. * MLDv1 (RFC 2710), and MLDv2 (RFC 3810). */ #ifdef HAVE_NETINET_IN_H #include #endif #ifdef HAVE_NETINET_ICMP6_H #include #endif /* * Constants definitions */ #ifndef IPPROTO_ICMPV6 #define IPPROTO_ICMPV6 58 #endif /* MLD versions definition */ #define MLD_V1 1 #define MLD_V2 2 #define MLD_VERSION_MIN MLD_V1 #define MLD_VERSION_MAX MLD_V2 #define MLD_VERSION_DEFAULT MLD_V1 /* * Constans for Multicast Listener Discovery protocol for IPv6. * All intervals are in seconds. * XXX: Several of these, especially the robustness variable, should be * variables and not constants. */ #define MLD_ROBUSTNESS_VARIABLE 2 #define MLD_QUERY_INTERVAL 125 #define MLD_QUERY_RESPONSE_INTERVAL 10 #define MLD_MULTICAST_LISTENER_INTERVAL (MLD_ROBUSTNESS_VARIABLE \ * MLD_QUERY_INTERVAL \ + MLD_QUERY_RESPONSE_INTERVAL) #define MLD_OTHER_QUERIER_PRESENT_INTERVAL (MLD_ROBUSTNESS_VARIABLE \ * MLD_QUERY_INTERVAL \ + MLD_QUERY_RESPONSE_INTERVAL / 2) #define MLD_STARTUP_QUERY_INTERVAL (MLD_QUERY_INTERVAL / 4) #define MLD_STARTUP_QUERY_COUNT MLD_ROBUSTNESS_VARIABLE #define MLD_LAST_LISTENER_QUERY_INTERVAL 1 #define MLD_LAST_LISTENER_QUERY_COUNT MLD_ROBUSTNESS_VARIABLE #define MLD_OLDER_VERSION_HOST_PRESENT_INTERVAL (MLD_ROBUSTNESS_VARIABLE \ * MLD_QUERY_INTERVAL \ + MLD_QUERY_RESPONSE_INTERVAL) #ifndef MLD_TIMER_SCALE /* the MLD max. response delay is in 1000th of seconds */ #define MLD_TIMER_SCALE 1000 #endif /* * MLDv1-related missing definitions * * Note that on newer systems all MLD-related definitions use * mld_xxx and MLD_XXX instead of mld6_xxx and MLD6_XXX. */ #ifndef MLD_LISTENER_QUERY # ifdef MLD6_LISTENER_QUERY # define MLD_LISTENER_QUERY MLD6_LISTENER_QUERY # else # define MLD_LISTENER_QUERY 130 # endif #endif #ifndef MLD_LISTENER_REPORT # ifdef MLD6_LISTENER_REPORT # define MLD_LISTENER_REPORT MLD6_LISTENER_REPORT # else # define MLD_LISTENER_REPORT 131 # endif #endif #ifndef MLD_LISTENER_DONE # ifdef MLD6_LISTENER_DONE # define MLD_LISTENER_DONE MLD6_LISTENER_DONE # else # define MLD_LISTENER_DONE 132 # endif #endif #ifndef MLD_MTRACE_RESP # ifdef MLD6_MTRACE_RESP # define MLD_MTRACE_RESP MLD6_MTRACE_RESP # else # define MLD_MTRACE_RESP 200 # endif #endif #ifndef MLD_MTRACE # ifdef MLD6_MTRACE # define MLD_MTRACE MLD6_MTRACE # else # define MLD_MTRACE 201 # endif #endif #ifndef MLD_MINLEN # ifdef HAVE_STRUCT_MLD_HDR # define MLD_MINLEN (sizeof(struct mld_hdr)) # else # define MLD_MINLEN 24 # endif #endif /* * MLDv2-related missing definitions */ #ifndef MLDV2_LISTENER_REPORT # ifdef MLD6V2_LISTENER_REPORT # define MLDV2_LISTENER_REPORT MLD6V2_LISTENER_REPORT # else # define MLDV2_LISTENER_REPORT 143 # endif #endif #ifndef MLD_MODE_IS_INCLUDE # define MLD_MODE_IS_INCLUDE 1 #endif #ifndef MLD_MODE_IS_EXCLUDE # define MLD_MODE_IS_EXCLUDE 2 #endif #ifndef MLD_CHANGE_TO_INCLUDE_MODE # define MLD_CHANGE_TO_INCLUDE_MODE 3 #endif #ifndef MLD_CHANGE_TO_EXCLUDE_MODE # define MLD_CHANGE_TO_EXCLUDE_MODE 4 #endif #ifndef MLD_ALLOW_NEW_SOURCES # define MLD_ALLOW_NEW_SOURCES 5 #endif #ifndef MLD_BLOCK_OLD_SOURCES # define MLD_BLOCK_OLD_SOURCES 6 #endif #ifndef MLD_V2_QUERY_MINLEN # define MLD_V2_QUERY_MINLEN 28 #endif #ifndef MLD_HOST_MRC_EXP # define MLD_HOST_MRC_EXP(x) (((x) >> 12) & 0x0007) #endif #ifndef MLD_HOST_MRC_MANT # define MLD_HOST_MRC_MANT(x) ((x) & 0x0fff) #endif #ifndef MLD_QQIC_EXP # define MLD_QQIC_EXP(x) (((x) >> 4) & 0x07) #endif #ifndef MLD_QQIC_MANT # define MLD_QQIC_MANT(x) ((x) & 0x0f) #endif #ifndef MLD_QRESV # define MLD_QRESV(x) (((x) >> 4) & 0x0f) #endif #ifndef MLD_SFLAG # define MLD_SFLAG(x) (((x) >> 3) & 0x01) #endif #ifndef MLD_QRV # define MLD_QRV(x) ((x) & 0x07) #endif /* * Structures, typedefs and macros */ /* * The ASCII names of the MLD protocol control messages */ #ifdef MLDV2_LISTENER_REPORT #define MLDV2TYPE2ASCII(t) \ (((t) == MLDV2_LISTENER_REPORT) ? \ "MLDV2_LISTENER_REPORT" \ : "MLD_type_unknown") #else #define MLDV2TYPE2ASCII(t) "MLD_type_unknown" #endif #define MLDTYPE2ASCII(t) \ (((t) == MLD_LISTENER_QUERY) ? \ "MLD_LISTENER_QUERY" \ : ((t) == MLD_LISTENER_REPORT) ? \ "MLD_LISTENER_REPORT" \ : ((t) == MLD_LISTENER_DONE) ? \ "MLD_LISTENER_DONE" \ : ((t) == MLD_MTRACE_RESP) ? \ "MLD_MTRACE_RESP" \ : ((t) == MLD_MTRACE) ? \ "MLD_MTRACE" \ : MLDV2TYPE2ASCII(t)) /* * Global variables */ /* * Global functions prototypes */ __BEGIN_DECLS __END_DECLS #endif /* __MPD6IGMP_MLD6_PROTO_H__ */ xorp/contrib/mld6igmp_lite/mld6igmp_node.hh0000664000076400007640000007574711540224221021136 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/contrib/mld6igmp_lite/mld6igmp_node.hh,v 1.3 2008/10/02 21:56:32 bms Exp $ #ifndef __MLD6IGMP_MLD6IGMP_NODE_HH__ #define __MLD6IGMP_MLD6IGMP_NODE_HH__ // // IGMP and MLD node definition. // #include "libxorp/vif.hh" #include "libproto/proto_node.hh" #include "libfeaclient/ifmgr_xrl_mirror.hh" #include "mrt/buffer.h" #include "mrt/multicast_defs.h" // // Constants definitions // // // Structures/classes, typedefs and macros // class EventLoop; class IPvX; class IPvXNet; class Mld6igmpVif; /** * @short The MLD/IGMP node class. * * There should be one node per MLD or IGMP instance. There should be * one instance per address family. */ class Mld6igmpNode : public ProtoNode, public IfMgrHintObserver, public ServiceChangeObserverBase { public: /** * Constructor for a given address family, module ID, and event loop. * * @param family the address family (AF_INET or AF_INET6 for * IPv4 and IPv6 respectively). * @param module_id the module ID (@ref xorp_module_id). Should be * equal to XORP_MODULE_MLD6IGMP. * @param eventloop the event loop to use. */ Mld6igmpNode(int family, xorp_module_id module_id, EventLoop& eventloop); /** * Destructor */ virtual ~Mld6igmpNode(); /** * Start the node operation. * * Start the MLD or IGMP protocol. * After the startup operations are completed, * @ref Mld6igmpNode::final_start() is called internally * to complete the job. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int start(); /** * Stop the node operation. * * Gracefully stop the MLD or IGMP protocol. * After the shutdown operations are completed, * @ref Mld6igmpNode::final_stop() is called internally * to complete the job. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int stop(); /** * Completely start the node operation. * * This method should be called internally after @ref Mld6igmpNode::start() * to complete the job. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int final_start(); /** * Completely stop the node operation. * * This method should be called internally after @ref Mld6igmpNode::stop() * to complete the job. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int final_stop(); /** * Enable node operation. * * If an unit is not enabled, it cannot be start, or pending-start. */ void enable(); /** * Disable 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 disable(); /** * Get the IP protocol number. * * @return the IP protocol number. */ uint8_t ip_protocol_number() const; /** * Install a new MLD/IGMP vif. * * @param vif vif information about the new Mld6igmpVif to install. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_vif(const Vif& vif, string& error_msg); /** * Install a new MLD/IGMP vif. * * @param vif_name the name of the new vif. * @param vif_index the vif index of the new vif. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_vif(const string& vif_name, uint32_t vif_index, string& error_msg); /** * Delete an existing MLD/IGMP vif. * * @param vif_name the name of the vif to delete. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int delete_vif(const string& vif_name, string& error_msg); /** * Set flags to a vif. * * @param vif_name the name of the vif. * @param is_pim_register true if this is a PIM Register vif. * @param is_p2p true if this is a point-to-point vif. * @param is_loopback true if this is a loopback interface. * @param is_multicast true if the vif is multicast-capable. * @param is_broadcast true if the vif is broadcast-capable. * @param is_up true if the vif is UP and running. * @param mtu the MTU of the vif. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_vif_flags(const string& vif_name, bool is_pim_register, bool is_p2p, bool is_loopback, bool is_multicast, bool is_broadcast, bool is_up, uint32_t mtu, string& error_msg); /** * Add an address to a vif. * * @param vif_name the name of the vif. * @param addr the unicast address to add. * @param subnet_addr the subnet address to add. * @param broadcast_addr the broadcast address (when applicable). * @param peer_addr the peer address (when applicable). * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_vif_addr(const string& vif_name, const IPvX& addr, const IPvXNet& subnet_addr, const IPvX& broadcast_addr, const IPvX& peer_addr, string& error_msg); /** * Delete an address from a vif. * * @param vif_name the name of the vif. * @param addr the unicast address to delete. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int delete_vif_addr(const string& vif_name, const IPvX& addr, string& error_msg); /** * Enable an existing MLD6IGMP vif. * * @param vif_name the name of the vif to enable. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int enable_vif(const string& vif_name, string& error_msg); /** * Disable an existing MLD6IGMP vif. * * @param vif_name the name of the vif to disable. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int disable_vif(const string& vif_name, string& error_msg); /** * Start an existing MLD6IGMP vif. * * @param vif_name the name of the vif to start. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int start_vif(const string& vif_name, string& error_msg); /** * Stop an existing MLD6IGMP vif. * * @param vif_name the name of the vif to start. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int stop_vif(const string& vif_name, string& error_msg); /** * Start MLD/IGMP on all enabled interfaces. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int start_all_vifs(); /** * Stop MLD/IGMP on all interfaces it was running on. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int stop_all_vifs(); /** * Enable MLD/IGMP on all interfaces. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int enable_all_vifs(); /** * Disable MLD/IGMP on all interfaces. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int disable_all_vifs(); /** * Delete all MLD/IGMP vifs. */ void delete_all_vifs(); /** * A method called when a vif has completed its shutdown. * * @param vif_name the name of the vif that has completed its shutdown. */ void vif_shutdown_completed(const string& vif_name); /** * Receive a protocol packet. * * @param if_name the interface name the packet arrived on. * @param vif_name the vif name the packet arrived on. * @param src_address the IP source address. * @param dst_address the IP destination address. * @param ip_protocol the IP protocol number. * @param ip_ttl the IP TTL (hop-limit). If it has a negative value, then * the received value is unknown. * @param ip_tos the Type of Service (Diffserv/ECN bits for IPv4). If it * has a negative value, then the received value is unknown. * @param ip_router_alert if true, the IP Router Alert option was included * in the IP packet. * @param ip_internet_control if true, then this is IP control traffic. * @param payload the payload, everything after the IP header and options. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int proto_recv(const string& if_name, const string& vif_name, const IPvX& src_address, const IPvX& dst_address, uint8_t ip_protocol, int32_t ip_ttl, int32_t ip_tos, bool ip_router_alert, bool ip_internet_control, const vector& payload, string& error_msg); /** * Send a protocol packet. * * @param if_name the interface to send the packet on. It is essential for * multicast. In the unicast case this field may be empty. * @param vif_name the vif to send the packet on. It is essential for * multicast. In the unicast case this field may be empty. * @param src_address the IP source address. * @param dst_address the IP destination address. * @param ip_protocol the IP protocol number. It must be between 1 and * 255. * @param ip_ttl the IP TTL (hop-limit). If it has a negative value, the * TTL will be set internally before transmission. * @param ip_tos the Type Of Service (Diffserv/ECN bits for IPv4). If it * has a negative value, the TOS will be set internally before * transmission. * @param ip_router_alert if true, then add the IP Router Alert option to * the IP packet. * @param ip_internet_control if true, then this is IP control traffic. * @param buffer the data buffer with the packet to send. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int mld6igmp_send(const string& if_name, const string& vif_name, const IPvX& src_address, const IPvX& dst_address, uint8_t ip_protocol, int32_t ip_ttl, int32_t ip_tos, bool ip_router_alert, bool ip_internet_control, buffer_t *buffer, string& error_msg); /** * Receive signal message: not used by MLD/IGMP. */ int signal_message_recv(const string& , // src_module_instance_name, int , // message_type, uint32_t , // vif_index, const IPvX& , // src, const IPvX& , // dst, const uint8_t * , // rcvbuf, size_t // rcvlen ) { XLOG_UNREACHABLE(); return (XORP_ERROR); } /** * Send signal message: not used by MLD/IGMP. */ int signal_message_send(const string& , // dst_module_instance_name, int , // message_type, uint32_t , // vif_index, const IPvX& , // src, const IPvX& , // dst, const uint8_t * , // sndbuf, size_t // sndlen ) { XLOG_UNREACHABLE(); return (XORP_ERROR); } /** * Register as a receiver to receive packets. * * This is a pure virtual function, and it must be implemented * by the communication-wrapper class that inherits this base class. * * @param if_name the interface through which packets should be accepted. * @param vif_name the vif through which packets should be accepted. * @param ip_protocol the IP protocol number that the receiver is * interested in. It must be between 0 and 255. A protocol number of 0 is * used to specify all protocols. * @param enable_multicast_loopback if true then enable delivering * of multicast datagrams back to this host (assuming the host is * a member of the same multicast group). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int register_receiver(const string& if_name, const string& vif_name, uint8_t ip_protocol, bool enable_multicast_loopback) = 0; /** * Unregister as a receiver to receive packets. * * This is a pure virtual function, and it must be implemented * by the communication-wrapper class that inherits this base class. * * @param if_name the interface through which packets should not be * accepted. * @param vif_name the vif through which packets should not be accepted. * @param ip_protocol the IP Protocol number that the receiver is * not interested in anymore. It must be between 0 and 255. A protocol * number of 0 is used to specify all protocols. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int unregister_receiver(const string& if_name, const string& vif_name, uint8_t ip_protocol) = 0; /** * Join a multicast group on an interface. * * This is a pure virtual function, and it must be implemented * by the communication-wrapper class that inherits this base class. * * TODO: add a source address as well!! * * @param if_name the interface name to join. * @param vif_name the vif name to join. * @param ip_protocol the IP protocol number that the receiver is * interested in. * @param group_address the multicast group address to join. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int join_multicast_group(const string& if_name, const string& vif_name, uint8_t ip_protocol, const IPvX& group_address) = 0; /** * Leave a multicast group on an interface. * * This is a pure virtual function, and it must be implemented * by the communication-wrapper class that inherits this base class. * * TODO: add a source address as well!! * * @param if_name the interface name to leave. * @param vif_name the vif name to leave. * @param ip_protocol the IP protocol number that the receiver is * not interested in anymore. * @param group_address the multicast group address to leave. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int leave_multicast_group(const string& if_name, const string& vif_name, uint8_t ip_protocol, const IPvX& group_address) = 0; /** * Add a protocol that needs to be notified about multicast membership * changes. * * Add a protocol to the list of entries that would be notified * if there is membership change on a particular interface. * * @param module_instance_name the module instance name of the * protocol to add. * @param module_id the module ID (@ref xorp_module_id) of the * protocol to add. * @param vif_index the vif index of the interface to add the protocol to. * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_protocol(const string& module_instance_name, xorp_module_id module_id, uint32_t vif_index); /** * Delete a protocol that needs to be notified about multicast membership * changes. * * Delete a protocol from the list of entries that would be notified * if there is membership change on a particular interface. * * @param module_instance_name the module instance name of the * protocol to delete. * @param module_id the module ID (@ref xorp_module_id) of the * protocol to delete. * @param vif_index the vif index of the interface to delete the * protocol from. * @return XORP_OK on success, otherwise XORP_ERROR. */ int delete_protocol(const string& module_instance_name, xorp_module_id module_id, uint32_t vif_index); /** * Send "add membership" to a protocol that needs to be notified * about multicast membership changes. * * This is a pure virtual function, and it must be implemented * by the communication-wrapper class that inherits this base class. * * @param dst_module_instance_name the module instance name of the * protocol to notify. * @param dst_module_id the module ID (@ref xorp_module_id) of the * protocol to notify. * @param vif_index the vif index of the interface with membership change. * @param source the source address of the (S,G) or (*,G) entry that has * changed membership. In case of Any-Source Multicast, it is IPvX::ZERO(). * @param group the group address. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int send_add_membership(const string& dst_module_instance_name, xorp_module_id dst_module_id, uint32_t vif_index, const IPvX& source, const IPvX& group) = 0; /** * Send "delete membership" to a protocol that needs to be notified * about multicast membership changes. * * This is a pure virtual function, and it must be implemented * by the communication-wrapper class that inherits this base class. * * @param dst_module_instance_name the module instance name of the * protocol to notify. * @param dst_module_id the module ID (@ref xorp_module_id) of the * protocol to notify. * @param vif_index the vif index of the interface with membership change. * @param source the source address of the (S,G) or (*,G) entry that has * changed membership. In case of Any-Source Multicast, it is IPvX::ZERO(). * @param group the group address of the (S,G) or (*,G) entry that has * changed. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int send_delete_membership(const string& dst_module_instance_name, xorp_module_id dst_module_id, uint32_t vif_index, const IPvX& source, const IPvX& group) = 0; /** * Notify a protocol about multicast membership change. * * @param module_instance_name the module instance name of the * protocol to notify. * @param module_id the module ID (@ref xorp_module_id) of the * protocol to notify. * @param vif_index the vif index of the interface with membership change. * @param source the source address of the (S,G) or (*,G) entry that has * changed. In case of group-specific multicast, it is IPvX::ZERO(). * @param group the group address of the (S,G) or (*,G) entry that has * changed. * @param action_jp the membership change type (@ref action_jp_t): * either ACTION_JOIN or ACTION_PRUNE. * @return XORP_OK on success, otherwise XORP_ERROR. */ int join_prune_notify_routing(const string& module_instance_name, xorp_module_id module_id, uint32_t vif_index, const IPvX& source, const IPvX& group, action_jp_t action_jp); /** * Test if an address is directly connected to a specified virtual * interface. * * Note that the virtual interface the address is directly connected to * must be UP. * * @param mld6igmp_vif the virtual interface to test against. * @param ipaddr_test the address to test. * @return true if @ref ipaddr_test is directly connected to @ref vif, * otherwise false. */ bool is_directly_connected(const Mld6igmpVif& mld6igmp_vif, const IPvX& ipaddr_test) const; // // Configuration methods // /** * Complete the set of vif configuration changes. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_config_all_vifs_done(string& error_msg); /** * Get the protocol version on an interface. * * @param vif_name the name of the vif to get the protocol version of. * @param proto_version the return-by-reference protocol version. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int get_vif_proto_version(const string& vif_name, int& proto_version, string& error_msg); /** * Set the protocol version on an interface. * * @param vif_name the name of the vif to set the protocol version of. * @param proto_version the new protocol version. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_vif_proto_version(const string& vif_name, int proto_version, string& error_msg); /** * Reset the protocol version on an interface to its default value. * * @param vif_name the name of the vif to reset the protocol version of * to its default value. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int reset_vif_proto_version(const string& vif_name, string& error_msg); /** * Get the value of the flag that enables/disables the IP Router Alert * option check per interface for received packets. * * @param vif_name the name of the vif to apply to. * @param enabled the return-by-reference flag value. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int get_vif_ip_router_alert_option_check(const string& vif_name, bool& enabled, string& error_msg); /** * Enable/disable the IP Router Alert option check per interface for * received packets. * * @param vif_name the name of the vif to apply to. * @param enable if true, then enable the IP Router Alert option check, * otherwise disable it. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_vif_ip_router_alert_option_check(const string& vif_name, bool enable, string& error_msg); /** * Reset the value of the flag that enables/disables the IP Router Alert * option check per interface for received packets to its default value. * * @param vif_name the name of the vif to apply to. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int reset_vif_ip_router_alert_option_check(const string& vif_name, string& error_msg); /** * Get the Query Interval per interface. * * @param vif_name the name of the vif to apply to. * @param interval the return-by-reference interval. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int get_vif_query_interval(const string& vif_name, TimeVal& interval, string& error_msg); /** * Set the Query Interval per interface. * * @param vif_name the name of the vif to apply to. * @param interval the interval. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_vif_query_interval(const string& vif_name, const TimeVal& interval, string& error_msg); /** * Reset the Query Interval per interface. * * @param vif_name the name of the vif to apply to. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int reset_vif_query_interval(const string& vif_name, string& error_msg); /** * Get the Last Member Query Interval per interface. * * @param vif_name the name of the vif to apply to. * @param interval the return-by-reference interval. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int get_vif_query_last_member_interval(const string& vif_name, TimeVal& interval, string& error_msg); /** * Set the Last Member Query Interval per interface. * * @param vif_name the name of the vif to apply to. * @param interval the interval. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_vif_query_last_member_interval(const string& vif_name, const TimeVal& interval, string& error_msg); /** * Reset the Last Member Query Interval per interface. * * @param vif_name the name of the vif to apply to. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int reset_vif_query_last_member_interval(const string& vif_name, string& error_msg); /** * Get the Query Response Interval per interface. * * @param vif_name the name of the vif to apply to. * @param interval the return-by-reference interval. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int get_vif_query_response_interval(const string& vif_name, TimeVal& interval, string& error_msg); /** * Set the Query Response Interval per interface. * * @param vif_name the name of the vif to apply to. * @param interval the interval. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_vif_query_response_interval(const string& vif_name, const TimeVal& interval, string& error_msg); /** * Reset the Query Response Interval per interface. * * @param vif_name the name of the vif to apply to. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int reset_vif_query_response_interval(const string& vif_name, string& error_msg); /** * Get the Robustness Variable count per interface. * * @param vif_name the name of the vif to apply to. * @param robust_count the return-by-reference count value. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int get_vif_robust_count(const string& vif_name, uint32_t& robust_count, string& error_msg); /** * Set the Robustness Variable count per interface. * * @param vif_name the name of the vif to apply to. * @param robust_count the count value. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_vif_robust_count(const string& vif_name, uint32_t robust_count, string& error_msg); /** * Reset the Robustness Variable count per interface. * * @param vif_name the name of the vif to apply to. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int reset_vif_robust_count(const string& vif_name, string& error_msg); // // Debug-related methods // /** * Test if trace log is enabled. * * This method is used to test whether to output trace log debug messges. * * @return true if trace log is enabled, otherwise false. */ bool is_log_trace() const { return (_is_log_trace); } /** * Enable/disable trace log. * * This method is used to enable/disable trace log debug messages output. * * @param is_enabled if true, trace log is enabled, otherwise is disabled. */ void set_log_trace(bool is_enabled) { _is_log_trace = is_enabled; } protected: // // IfMgrHintObserver methods // void tree_complete(); void updates_made(); private: /** * A method invoked when the status of a service changes. * * @param service the service whose status has changed. * @param old_status the old status. * @param new_status the new status. */ void status_change(ServiceBase* service, ServiceStatus old_status, ServiceStatus new_status); /** * Get a reference to the service base of the interface manager. * * This is a pure virtual function, and it must be implemented * by the communication-wrapper class that inherits this base class. * * @return a reference to the service base of the interface manager. */ virtual const ServiceBase* ifmgr_mirror_service_base() const = 0; /** * Get a reference to the interface manager tree. * * This is a pure virtual function, and it must be implemented * by the communication-wrapper class that inherits this base class. * * @return a reference to the interface manager tree. */ virtual const IfMgrIfTree& ifmgr_iftree() const = 0; /** * Initiate registration with the FEA. * * This is a pure virtual function, and it must be implemented * by the communication-wrapper class that inherits this base class. */ virtual void fea_register_startup() = 0; /** * Initiate registration with the MFEA. * * This is a pure virtual function, and it must be implemented * by the communication-wrapper class that inherits this base class. */ virtual void mfea_register_startup() = 0; /** * Initiate de-registration with the FEA. * * This is a pure virtual function, and it must be implemented * by the communication-wrapper class that inherits this base class. */ virtual void fea_register_shutdown() = 0; /** * Initiate de-registration with the MFEA. * * This is a pure virtual function, and it must be implemented * by the communication-wrapper class that inherits this base class. */ virtual void mfea_register_shutdown() = 0; buffer_t *_buffer_recv; // Buffer for receiving messages // // Status-related state // size_t _waiting_for_mfea_startup_events; // // A local copy with the interface state information // IfMgrIfTree _iftree; // // Debug and test-related state // bool _is_log_trace; // If true, enable XLOG_TRACE() }; // // Global variables // // // Global functions prototypes // #endif // __MLD6IGMP_MLD6IGMP_NODE_HH__ xorp/contrib/mld6igmp_lite/igmp_proto.h0000664000076400007640000001724011421137511020404 0ustar greearbgreearb/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- */ /* * Copyright (c) 2001-2009 XORP, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 2, June * 1991 as published by the Free Software Foundation. Redistribution * and/or modification of this program under the terms of any other * version of the GNU General Public License is not permitted. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, * see the GNU General Public License, Version 2, a copy of which can be * found in the XORP LICENSE.gpl file. * * XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; * http://xorp.net */ /* * $XORP: xorp/contrib/mld6igmp_lite/igmp_proto.h,v 1.3 2008/10/02 21:56:31 bms Exp $ */ #ifndef __MLD6IGMP_IGMP_PROTO_H__ #define __MLD6IGMP_IGMP_PROTO_H__ /* * Internet Group Management Protocol protocol-specific definitions: * IGMPv1 and IGMPv2 (RFC 2236), and IGMPv3 (RFC 3376). */ #ifdef HAVE_NETINET_IN_H #include #endif #ifdef HAVE_NETINET_IGMP_H #include #endif /* * Constants definitions */ #ifndef IPPROTO_IGMP #define IPPROTO_IGMP 2 #endif /* IGMP versions definition */ #define IGMP_V1 1 #define IGMP_V2 2 #define IGMP_V3 3 #define IGMP_VERSION_MIN IGMP_V1 #define IGMP_VERSION_MAX IGMP_V3 #define IGMP_VERSION_DEFAULT IGMP_V2 /* * Constants for IGMP Version 2. * All intervals are in seconds. * XXX: several of these, especially the robustness variable, should be * variables and not constants. * XXX: some of the definitions are copied from mrouted code. */ #define IGMP_ROBUSTNESS_VARIABLE 2 #define IGMP_QUERY_INTERVAL 125 #define IGMP_QUERY_RESPONSE_INTERVAL 10 #define IGMP_GROUP_MEMBERSHIP_INTERVAL (IGMP_ROBUSTNESS_VARIABLE \ * IGMP_QUERY_INTERVAL \ + IGMP_QUERY_RESPONSE_INTERVAL) #define IGMP_OTHER_QUERIER_PRESENT_INTERVAL (IGMP_ROBUSTNESS_VARIABLE \ * IGMP_QUERY_INTERVAL \ + IGMP_QUERY_RESPONSE_INTERVAL / 2) #define IGMP_STARTUP_QUERY_INTERVAL (IGMP_QUERY_INTERVAL / 4) #define IGMP_STARTUP_QUERY_COUNT IGMP_ROBUSTNESS_VARIABLE #define IGMP_LAST_MEMBER_QUERY_INTERVAL 1 #define IGMP_LAST_MEMBER_QUERY_COUNT IGMP_ROBUSTNESS_VARIABLE #define IGMP_VERSION1_ROUTER_PRESENT_TIMEOUT 400 #define IGMP_OLDER_VERSION_HOST_PRESENT_INTERVAL (IGMP_ROBUSTNESS_VARIABLE \ * IGMP_QUERY_INTERVAL \ + IGMP_QUERY_RESPONSE_INTERVAL) #ifndef IGMP_TIMER_SCALE /* the igmp code field is in 10th of seconds */ #define IGMP_TIMER_SCALE 10 #endif /* * DVMRP message types from mrouted/mrinfo * (carried in the "code" field of an IGMP header) */ /* TODO: remove the things we don't need */ #define DVMRP_PROBE 1 /* for finding neighbors */ #define DVMRP_REPORT 2 /* for reporting some or all routes */ #define DVMRP_ASK_NEIGHBORS 3 /* sent by mapper, asking for a list */ /* of this router's neighbors. */ #define DVMRP_NEIGHBORS 4 /* response to such a request */ #define DVMRP_ASK_NEIGHBORS2 5 /* as above, want new format reply */ #define DVMRP_NEIGHBORS2 6 #define DVMRP_PRUNE 7 /* prune message */ #define DVMRP_GRAFT 8 /* graft message */ #define DVMRP_GRAFT_ACK 9 /* graft acknowledgement */ #define DVMRP_INFO_REQUEST 10 /* information request */ #define DVMRP_INFO_REPLY 11 /* information reply */ /* * 'flags' byte values in DVMRP_NEIGHBORS2 reply. */ #define DVMRP_NF_TUNNEL 0x01 /* neighbors reached via tunnel */ #define DVMRP_NF_SRCRT 0x02 /* tunnel uses IP source routing */ #define DVMRP_NF_PIM 0x04 /* neighbor is a PIM neighbor */ #define DVMRP_NF_DOWN 0x10 /* kernel state of interface */ #define DVMRP_NF_DISABLED 0x20 /* administratively disabled */ #define DVMRP_NF_QUERIER 0x40 /* I am the subnet's querier */ #define DVMRP_NF_LEAF 0x80 /* Neighbor reports that it is a leaf*/ /* * Request/reply types for info queries/replies */ #define DVMRP_INFO_VERSION 1 /* version string */ #define DVMRP_INFO_NEIGHBORS 2 /* neighbors2 data */ /* * IGMPv1,v2-related missing definitions */ #ifndef IGMP_MEMBERSHIP_QUERY # ifdef IGMP_HOST_MEMBERSHIP_QUERY # define IGMP_MEMBERSHIP_QUERY IGMP_HOST_MEMBERSHIP_QUERY # else # define IGMP_MEMBERSHIP_QUERY 0x11 # endif #endif #ifndef IGMP_V1_MEMBERSHIP_REPORT # ifdef IGMP_v1_HOST_MEMBERSHIP_REPORT # define IGMP_V1_MEMBERSHIP_REPORT IGMP_v1_HOST_MEMBERSHIP_REPORT # else # define IGMP_V1_MEMBERSHIP_REPORT 0x12 # endif #endif #ifndef IGMP_V2_MEMBERSHIP_REPORT # ifdef IGMP_v2_HOST_MEMBERSHIP_REPORT # define IGMP_V2_MEMBERSHIP_REPORT IGMP_v2_HOST_MEMBERSHIP_REPORT # else # define IGMP_V2_MEMBERSHIP_REPORT 0x16 # endif #endif #ifndef IGMP_V2_LEAVE_GROUP # ifdef IGMP_HOST_LEAVE_MESSAGE # define IGMP_V2_LEAVE_GROUP IGMP_HOST_LEAVE_MESSAGE # else # define IGMP_V2_LEAVE_GROUP 0x17 # endif #endif #ifndef IGMP_DVMRP # define IGMP_DVMRP 0x13 #endif #ifndef IGMP_PIM # define IGMP_PIM 0x14 #endif #ifndef IGMP_MTRACE_RESP # ifdef IGMP_MTRACE_REPLY # define IGMP_MTRACE_RESP IGMP_MTRACE_REPLY # else # define IGMP_MTRACE_RESP 0x1e # endif #endif #ifndef IGMP_MTRACE # ifdef IGMP_MTRACE_QUERY # define IGMP_MTRACE IGMP_MTRACE_QUERY # else # define IGMP_MTRACE 0x1f # endif #endif #ifndef IGMP_MINLEN # define IGMP_MINLEN 8 #endif /* * IGMPv3-related missing definitions */ #ifndef IGMP_V3_MEMBERSHIP_REPORT # ifdef IGMP_v3_HOST_MEMBERSHIP_REPORT # define IGMP_V3_MEMBERSHIP_REPORT IGMP_v3_HOST_MEMBERSHIP_REPORT # else # define IGMP_V3_MEMBERSHIP_REPORT 0x22 # endif #endif #ifndef IGMP_MODE_IS_INCLUDE # define IGMP_MODE_IS_INCLUDE 1 #endif #ifndef IGMP_MODE_IS_EXCLUDE # define IGMP_MODE_IS_EXCLUDE 2 #endif #ifndef IGMP_CHANGE_TO_INCLUDE_MODE # define IGMP_CHANGE_TO_INCLUDE_MODE 3 #endif #ifndef IGMP_CHANGE_TO_EXCLUDE_MODE # define IGMP_CHANGE_TO_EXCLUDE_MODE 4 #endif #ifndef IGMP_ALLOW_NEW_SOURCES # define IGMP_ALLOW_NEW_SOURCES 5 #endif #ifndef IGMP_BLOCK_OLD_SOURCES # define IGMP_BLOCK_OLD_SOURCES 6 #endif #ifndef IGMP_V3_QUERY_MINLEN # define IGMP_V3_QUERY_MINLEN 12 #endif #ifndef IGMP_EXP # define IGMP_EXP(x) (((x) >> 4) & 0x07) #endif #ifndef IGMP_MANT # define IGMP_MANT(x) ((x) & 0x0f) #endif #ifndef IGMP_QRESV # define GMP_QRESV(x) (((x) >> 4) & 0x0f) #endif #ifndef IGMP_SFLAG # define IGMP_SFLAG(x) (((x) >> 3) & 0x01) #endif #ifndef IGMP_QRV # define IGMP_QRV(x) ((x) & 0x07) #endif /* * Structures, typedefs and macros */ /* * The ASCII names of the IGMP protocol control messages */ #ifdef IGMP_V3_MEMBERSHIP_REPORT #define IGMPV3TYPE2ASCII(t) \ (((t) == IGMP_V3_MEMBERSHIP_REPORT) ? \ "IGMP_V3_MEMBERSHIP_REPORT" \ : "IGMP_type_unknown") #else #define IGMPV3TYPE2ASCII(t) "IGMP_type_unknown" #endif #define IGMPTYPE2ASCII(t) \ (((t) == IGMP_MEMBERSHIP_QUERY) ? \ "IGMP_MEMBERSHIP_QUERY" \ : ((t) == IGMP_V1_MEMBERSHIP_REPORT) ? \ "IGMP_V1_MEMBERSHIP_REPORT" \ : ((t) == IGMP_V2_MEMBERSHIP_REPORT) ? \ "IGMP_V2_MEMBERSHIP_REPORT" \ : ((t) == IGMP_V2_LEAVE_GROUP) ? \ "IGMP_V2_LEAVE_GROUP" \ : ((t) == IGMP_DVMRP) ? \ "IGMP_DVMRP" \ : ((t) == IGMP_PIM) ? \ "IGMP_PIM" \ : ((t) == IGMP_MTRACE_RESP) ? \ "IGMP_MTRACE_RESP" \ : ((t) == IGMP_MTRACE) ? \ "IGMP_MTRACE" \ : IGMPV3TYPE2ASCII(t)) /* * Global variables */ /* * Global functions prototypes */ __BEGIN_DECLS __END_DECLS #endif /* __MLD6IGMP_IGMP_PROTO_H__ */ xorp/contrib/mld6igmp_lite/mld6igmp_vif.hh0000664000076400007640000006140011540224221020752 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/contrib/mld6igmp_lite/mld6igmp_vif.hh,v 1.3 2008/10/02 21:56:32 bms Exp $ #ifndef __MLD6IGMP_MLD6IGMP_VIF_HH__ #define __MLD6IGMP_MLD6IGMP_VIF_HH__ // // IGMP and MLD virtual interface definition. // #include "libxorp/config_param.hh" #include "libxorp/timer.hh" #include "libxorp/vif.hh" #include "libproto/proto_unit.hh" #include "mrt/buffer.h" #include "mrt/multicast_defs.h" #include "igmp_proto.h" #include "mld6_proto.h" #include "mld6igmp_node.hh" #include "mld6igmp_group_record.hh" // // Constants definitions // // // Structures/classes, typedefs and macros // /** * @short A class for MLD/IGMP-specific virtual interface. */ class Mld6igmpVif : public ProtoUnit, public Vif { public: /** * Constructor for a given MLD/IGMP node and a generic virtual interface. * * @param mld6igmp_node the @ref Mld6igmpNode this interface belongs to. * @param vif the generic Vif interface that contains various information. */ Mld6igmpVif(Mld6igmpNode& mld6igmp_node, const Vif& vif); /** * Destructor */ virtual ~Mld6igmpVif(); /** * Set the current protocol version. * * The protocol version must be in the interval * [IGMP_VERSION_MIN, IGMP_VERSION_MAX] * or [MLD_VERSION_MIN, MLD_VERSION_MAX] * * @param proto_version the protocol version to set. * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_proto_version(int proto_version); /** * Start MLD/IGMP on a single virtual interface. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int start(string& error_msg); /** * Stop MLD/IGMP on a single virtual interface. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int stop(string& error_msg); /** * Enable MLD/IGMP on a single virtual interface. * * If an unit is not enabled, it cannot be start, or pending-start. */ void enable(); /** * Disable MLD/IGMP 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 disable(); /** * Receive a protocol message. * * @param src the source address of the message. * @param dst the destination address of the message. * @param ip_ttl the IP TTL of the message. If it has a negative value * it should be ignored. * @param ip_ttl the IP TOS of the message. If it has a negative value, * it should be ignored. * @param ip_router_alert if true, the IP Router Alert option in the IP * packet was set (when applicable). * @param ip_internet_control if true, then this is IP control traffic. * @param buffer the data buffer with the received message. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int mld6igmp_recv(const IPvX& src, const IPvX& dst, int ip_ttl, int ip_tos, bool ip_router_alert, bool ip_internet_control, buffer_t *buffer, string& error_msg); /** * Get the string with the flags about the vif status. * * TODO: temporary here. Should go to the Vif class after the Vif * class starts using the Proto class. * * @return the C++ style string with the flags about the vif status * (e.g., UP/DOWN/DISABLED, etc). */ string flags_string() const; /** * Get the MLD6IGMP node (@ref Mld6igmpNode). * * @return a reference to the MLD6IGMP node (@ref Mld6igmpNode). */ Mld6igmpNode& mld6igmp_node() const { return (_mld6igmp_node); } /** * Get my primary address on this interface. * * @return my primary address on this interface. */ const IPvX& primary_addr() const { return (_primary_addr); } /** * Set my primary address on this interface. * * @param v the value of the primary address. */ void set_primary_addr(const IPvX& v) { _primary_addr = v; } /** * Update the primary address. * * The primary address should be a link-local unicast address, and * is used for transmitting the multicast control packets on the LAN. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int update_primary_address(string& error_msg); /** * Get the MLD/IGMP querier address. * * @return the MLD/IGMP querier address. */ const IPvX& querier_addr() const { return (_querier_addr); } /** * Set the MLD6/IGMP querier address. * * @param v the value of the MLD/IGMP querier address. */ void set_querier_addr(const IPvX& v) { _querier_addr = v; } /** * Get the set with the multicast group records information * (@ref Mld6igmpGroupSet). * * @return the set with the multicast group records information * (@ref Mld6igmpGroupSet). */ Mld6igmpGroupSet& group_records() { return (_group_records); } /** * Get the const set with the multicast group records information * (@ref Mld6igmpGroupSet). * * @return the const set with the multicast group records information * (@ref Mld6igmpGroupSet). */ const Mld6igmpGroupSet& group_records() const { return (_group_records); } /** * Test if the protocol is Source-Specific Multicast (e.g., IGMPv3 * or MLDv2). * * @return true if the protocol is Source-Specific Multicast (e.g., IGMPv3 * or MLDv2). */ bool proto_is_ssm() const; /** * Get the timer to timeout the (other) MLD/IGMP querier. * * @return a reference to the timer to timeout the (other) * MLD/IGMP querier. * */ const XorpTimer& const_other_querier_timer() const { return (_other_querier_timer); } /** * Optain a reference to the "IP Router Alert option check" flag. * * @return a reference to the "IP Router Alert option check" flag. */ ConfigParam& ip_router_alert_option_check() { return (_ip_router_alert_option_check); } /** * Optain a reference to the configured Query Interval. * * @return a reference to the configured Query Interval. */ ConfigParam& configured_query_interval() { return (_configured_query_interval); } /** * Get the effective Query Interval value. * * Note that this value may be modified by reconfiguring the router, * or by the Query message from the current Querier. * * @return the value of the effective Query Interval. */ const TimeVal& effective_query_interval() const { return (_effective_query_interval); } /** * Set the effective Query Interval. * * Note that this value may be modified by reconfiguring the router, * or by the Query message from the current Querier. * * @param v the value of the effective Query Interval. */ void set_effective_query_interval(const TimeVal& v); /** * Optain a reference to the Last Member Query Interval. * * @return a reference to the Last Member Query Interval. */ ConfigParam& query_last_member_interval() { return (_query_last_member_interval); } /** * Optain a reference to the Query Response Interval. * * @return a reference to the Query Response Interval. */ ConfigParam& query_response_interval() { return (_query_response_interval); } /** * Optain a reference to the configured Robustness Variable count. * * @return a reference to the configured Robustness Variable count. */ ConfigParam& configured_robust_count() { return (_configured_robust_count); } /** * Get the effective Robustness Variable value. * * Note that this value may be modified by reconfiguring the router, * or by the Query messages from the current Querier. * * @return the value of the effective Robustness Variable. */ uint32_t effective_robustness_variable() const { return (_effective_robustness_variable); } /** * Set the effective Robustness Variable. * * Note that this value may be modified by reconfiguring the router, * or by the Query messages from the current Querier. * * @param v the value of the effective Robustness Variable. */ void set_effective_robustness_variable(uint32_t v); /** * Get the Last Member Query Count value. * * Note: According to the IGMP/MLD spec, the default value for the * Last Member Query Count is the Robustness Variable. * Hence, the Last Member Query Count itself should be configurable. * For simplicity (and for consistency with other router vendors), it * is always same as the Robustness Variable. * * @return the value of the Last Member Query Count. */ uint32_t last_member_query_count() const { return (_last_member_query_count); } /** * Obtain a reference to the Group Membership Interval. * * Note that it is not directly configurable, but may be tuned by * changing the values of the parameters it depends on. * * @return a reference to the Group Membership Interval. */ const TimeVal& group_membership_interval() const { return (_group_membership_interval); } /** * Obtain a reference to the Last Member Query Time. * * Note that it is not directly configurable, but may be tuned by * changing the values of the parameters it depends on. * * @return a reference to the Last Member Query Time. */ const TimeVal& last_member_query_time() const { return (_last_member_query_time); } /** * Obtain a reference to the Older Version Host Present Interval. * * Note that it is not directly configurable, but may be tuned by * changing the values of the parameters it depends on. * * @return a reference to the Older Version Host Present Interval. */ const TimeVal& older_version_host_present_interval() const { return (_older_version_host_present_interval); } // // Add/delete routing protocols that need to be notified for membership // changes. // /** * Add a protocol that needs to be notified about multicast membership * changes. * * Add a protocol to the list of entries that would be notified * if there is membership change on a particular interface. * * @param module_instance_name the module instance name of the * protocol to add. * @param module_id the module ID (@ref xorp_module_id) of the * protocol to add. * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_protocol(xorp_module_id module_id, const string& module_instance_name); /** * Delete a protocol that needs to be notified about multicast membership * changes. * * Delete a protocol from the list of entries that would be notified * if there is membership change on a particular interface. * * @param module_instance_name the module instance name of the * protocol to delete. * @param module_id the module ID (@ref xorp_module_id) of the * protocol to delete. * @return XORP_OK on success, otherwise XORP_ERROR. */ int delete_protocol(xorp_module_id module_id, const string& module_instance_name); /** * Notify the interested parties that there is membership change among * the local members. * * @param source the source address of the (S,G) entry that has changed. * In case of group-specific membership, it could be IPvX::ZERO(). * @param group the group address of the (S,G) entry that has changed. * @param action_jp the membership change: @ref ACTION_JOIN * or @ref ACTION_PRUNE. * @return XORP_OK on success, otherwise XORP_ERROR. */ int join_prune_notify_routing(const IPvX& source, const IPvX& group, action_jp_t action_jp) const; // // Functions for sending protocol messages // /** * Send MLD or IGMP message. * * @param src the message source address. * @param dst the message destination address. * @param message_type the MLD or IGMP type of the message. * @param max_resp_code the "Maximum Response Code" or "Max Resp Code" * field in the MLD or IGMP headers respectively (in the particular * protocol resolution). * @param group_address the "Multicast Address" or "Group Address" field * in the MLD or IGMP headers respectively. * @param buffer the buffer with the rest of the message. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. **/ int mld6igmp_send(const IPvX& src, const IPvX& dst, uint8_t message_type, uint16_t max_resp_code, const IPvX& group_address, buffer_t *buffer, string& error_msg); /** * Send Group-Specific Query message. * * @param group_address the "Multicast Address" or "Group Address" field * in the MLD or IGMP headers respectively. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. **/ int mld6igmp_group_query_send(const IPvX& group_address, string& error_msg); /** * Send MLDv2 or IGMPv3 Group-and-Source-Specific Query message. * * @param group_address the "Multicast Address" or "Group Address" field * in the MLD or IGMP headers respectively. * @param sources the set of source addresses. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. **/ int mld6igmp_group_source_query_send(const IPvX& group_address, const set& sources, string& error_msg); /** * Send MLD or IGMP Query message. * * @param src the message source address. * @param dst the message destination address. * @param max_resp_time the maximum response time. * @param group_address the "Multicast Address" or "Group Address" field * in the MLD or IGMP headers respectively. * @param sources the set of source addresses (for IGMPv3 or MLDv2 only). * @param s_flag the "Suppress Router-Side Processing" bit (for IGMPv3 * or MLDv2 only; in all other cases it should be set to false). * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. **/ int mld6igmp_query_send(const IPvX& src, const IPvX& dst, const TimeVal& max_resp_time, const IPvX& group_address, const set& sources, bool s_flag, string& error_msg); /** * Test if the interface is running in IGMPv1 mode. * * @return true if the interface is running in IGMPv1 mode, * otherwise false. */ bool is_igmpv1_mode() const; /** * Test if the interface is running in IGMPv2 mode. * * @return true if the interface is running in IGMPv2 mode, * otherwise false. */ bool is_igmpv2_mode() const; /** * Test if the interface is running in IGMPv3 mode. * * @return true if the interface is running in IGMPv3 mode, * otherwise false. */ bool is_igmpv3_mode() const; /** * Test if the interface is running in MLDv1 mode. * * @return true if the interface is running in MLDv1 mode, * otherwise false. */ bool is_mldv1_mode() const; /** * Test if the interface is running in MLDv2 mode. * * @return true if the interface is running in MLDv2 mode, * otherwise false. */ bool is_mldv2_mode() const; /** * Test if a group is running in IGMPv1 mode. * * Note that if @ref group_record is NULL, then we test whether the * interface itself is running in IGMPv1 mode. * @param group_record the group record to test. * @return true if the group is running in IGMPv1 mode, * otherwise false. */ bool is_igmpv1_mode(const Mld6igmpGroupRecord* group_record) const; /** * Test if a group is running in IGMPv2 mode. * * Note that if @ref group_record is NULL, then we test whether the * interface itself is running in IGMPv2 mode. * @param group_record the group record to test. * @return true if the group is running in IGMPv2 mode, * otherwise false. */ bool is_igmpv2_mode(const Mld6igmpGroupRecord* group_record) const; /** * Test if a group is running in IGMPv3 mode. * * Note that if @ref group_record is NULL, then we test whether the * interface itself is running in IGMPv3 mode. * @param group_record the group record to test. * @return true if the group is running in IGMPv3 mode, * otherwise false. */ bool is_igmpv3_mode(const Mld6igmpGroupRecord* group_record) const; /** * Test if a group is running in MLDv1 mode. * * Note that if @ref group_record is NULL, then we test whether the * interface itself is running in MLDv1 mode. * @param group_record the group record to test. * @return true if the group is running in MLDv1 mode, * otherwise false. */ bool is_mldv1_mode(const Mld6igmpGroupRecord* group_record) const; /** * Test if a group is running in MLDv2 mode. * * Note that if @ref group_record is NULL, then we test whether the * interface itself is running in MLDv2 mode. * @param group_record the group record to test. * @return true if the group is running in MLDv2 mode, * otherwise false. */ bool is_mldv2_mode(const Mld6igmpGroupRecord* group_record) const; private: // // Private functions // /** * Return the ASCII text description of the protocol message. * * @param message_type the protocol message type. * @return the ASCII text descrpition of the protocol message. */ const char *proto_message_type2ascii(uint8_t message_type) const; /** * Reset and prepare the buffer for sending data. * * @return the prepared buffer. */ buffer_t *buffer_send_prepare(); /** * Calculate the checksum of an IPv6 "pseudo-header" as described * in RFC 2460. * * @param src the source address of the pseudo-header. * @param dst the destination address of the pseudo-header. * @param len the upper-layer packet length of the pseudo-header * (in host-order). * @param protocol the upper-layer protocol number. * @return the checksum of the IPv6 "pseudo-header". */ uint16_t calculate_ipv6_pseudo_header_checksum(const IPvX& src, const IPvX& dst, size_t len, uint8_t protocol); /** * Test whether I am the querier for this vif. * * @return true if I am the querier for this vif, otherwise false. */ bool i_am_querier() const; /** * Set the state whether I am the querier for this vif. * * @param v if true, then I am the querier for this vif. */ void set_i_am_querier(bool v); // // Callbacks for configuration and non-configurable parameters // void set_configured_query_interval_cb(TimeVal v); void set_query_last_member_interval_cb(TimeVal v); void set_query_response_interval_cb(TimeVal v); void set_configured_robust_count_cb(uint32_t v); void recalculate_effective_query_interval(); void recalculate_effective_robustness_variable(); void recalculate_last_member_query_count(); void recalculate_group_membership_interval(); void recalculate_last_member_query_time(); void recalculate_older_version_host_present_interval(); void restore_effective_variables(); // // Private state // Mld6igmpNode& _mld6igmp_node; // The MLD6IGMP node I belong to buffer_t *_buffer_send; // Buffer for sending messages enum { MLD6IGMP_VIF_QUERIER = 1 << 0 // I am the querier }; uint32_t _proto_flags; // Various flags (MLD6IGMP_VIF_*) IPvX _primary_addr; // The primary address on this vif IPvX _querier_addr; // IP address of the current querier XorpTimer _other_querier_timer; // To timeout the (other) 'querier' XorpTimer _query_timer; // Timer to send queries uint8_t _startup_query_count; // Number of queries to send quickly // during startup Mld6igmpGroupSet _group_records; // The group records // // Misc configuration parameters // ConfigParam _ip_router_alert_option_check; // The IP Router Alert option check flag ConfigParam _configured_query_interval; // The configured Query Interval TimeVal _effective_query_interval; // The effective Query Interval ConfigParam _query_last_member_interval; // The Last Member Query Interval ConfigParam _query_response_interval; // The Query Response Interval ConfigParam _configured_robust_count; // The configured Robustness Variable count uint32_t _effective_robustness_variable; // The effective Robustness Variable // // Other parameters that are not directly configurable // uint32_t _last_member_query_count; // The Last Member Query Count TimeVal _group_membership_interval; // The Group Membership Interval TimeVal _last_member_query_time; // The Last Member Query Time TimeVal _older_version_host_present_interval; // The Older Version Host Present Interval // // Misc. other state // // Registered protocols to notify for membership change. vector > _notify_routing_protocols; bool _dummy_flag; // Dummy flag // // Not-so handy private functions that should go somewhere else // // MLD/IGMP control messages recv functions int mld6igmp_membership_query_recv(const IPvX& src, const IPvX& dst, uint8_t message_type, uint16_t max_resp_code, const IPvX& group_address, buffer_t *buffer); int mld6igmp_ssm_membership_query_recv(const IPvX& src, const IPvX& dst, uint8_t message_type, uint16_t max_resp_code, const IPvX& group_address, buffer_t *buffer); int mld6igmp_membership_report_recv(const IPvX& src, const IPvX& dst, uint8_t message_type, uint16_t max_resp_code, const IPvX& group_address, buffer_t *buffer); int mld6igmp_leave_group_recv(const IPvX& src, const IPvX& dst, uint8_t message_type, uint16_t max_resp_code, const IPvX& group_address, buffer_t *buffer); int mld6igmp_ssm_membership_report_recv(const IPvX& src, const IPvX& dst, uint8_t message_type, buffer_t *buffer); int mld6igmp_query_version_consistency_check(const IPvX& src, const IPvX& dst, uint8_t message_type, int message_version); // MLD/IGMP control messages process functions int mld6igmp_process(const IPvX& src, const IPvX& dst, int ip_ttl, int ip_tos, bool ip_router_alert, bool ip_internet_control, buffer_t *buffer, string& error_msg); // MLD/IGMP uniform interface for protocol-related constants size_t mld6igmp_constant_minlen() const; uint32_t mld6igmp_constant_timer_scale() const; uint8_t mld6igmp_constant_membership_query() const; void other_querier_timer_timeout(); void query_timer_timeout(); void decode_exp_time_code8(uint8_t code, TimeVal& timeval, uint32_t timer_scale); void decode_exp_time_code16(uint16_t code, TimeVal& timeval, uint32_t timer_scale); void encode_exp_time_code8(const TimeVal& timeval, uint8_t& code, uint32_t timer_scale); void encode_exp_time_code16(const TimeVal& timeval, uint16_t& code, uint32_t timer_scale); }; // // Global variables // // // Global functions prototypes // #endif // __MLD6IGMP_MLD6IGMP_VIF_HH__ xorp/contrib/mld6igmp_lite/mld6igmp_vif.cc0000664000076400007640000014604411421137511020753 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // The Lightweight IGMP/MLD modifications to this file are copyrighted by: // // Copyright (c) 2008 Huawei Technologies Co. Ltd // // // MLD6IGMP virtual interfaces implementation. // #include "mld6igmp_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvx.hh" #include "libproto/checksum.h" #include "mld6igmp_vif.hh" // // Exported variables // // // Local constants definitions // // // Local structures/classes, typedefs and macros // // // Local variables // // // Local functions prototypes // /** * Mld6igmpVif::Mld6igmpVif: * @mld6igmp_node: The MLD6IGMP node this interface belongs to. * @vif: The generic Vif interface that contains various information. * * MLD6IGMP protocol vif constructor. **/ Mld6igmpVif::Mld6igmpVif(Mld6igmpNode& mld6igmp_node, const Vif& vif) : ProtoUnit(mld6igmp_node.family(), mld6igmp_node.module_id()), Vif(vif), _mld6igmp_node(mld6igmp_node), _proto_flags(0), _primary_addr(IPvX::ZERO(mld6igmp_node.family())), _querier_addr(IPvX::ZERO(mld6igmp_node.family())), _startup_query_count(0), _group_records(*this), _ip_router_alert_option_check(false), _configured_query_interval( TimeVal(0, 0), callback(this, &Mld6igmpVif::set_configured_query_interval_cb)), _effective_query_interval(TimeVal(0, 0)), _query_last_member_interval( TimeVal(0, 0), callback(this, &Mld6igmpVif::set_query_last_member_interval_cb)), _query_response_interval( TimeVal(0, 0), callback(this, &Mld6igmpVif::set_query_response_interval_cb)), _configured_robust_count( 0, callback(this, &Mld6igmpVif::set_configured_robust_count_cb)), _effective_robustness_variable(0), _last_member_query_count(0), _group_membership_interval(TimeVal(0, 0)), _last_member_query_time(TimeVal(0, 0)), _older_version_host_present_interval(TimeVal(0, 0)), _dummy_flag(false) { XLOG_ASSERT(proto_is_igmp() || proto_is_mld6()); // // TODO: when more things become classes, most of this init should go away // _buffer_send = BUFFER_MALLOC(BUF_SIZE_DEFAULT); // // Set the protocol version // if (proto_is_igmp()) { set_proto_version_default(IGMP_VERSION_DEFAULT); _configured_query_interval.set(TimeVal(IGMP_QUERY_INTERVAL, 0)); _query_last_member_interval.set( TimeVal(IGMP_LAST_MEMBER_QUERY_INTERVAL, 0)); _query_response_interval.set(TimeVal(IGMP_QUERY_RESPONSE_INTERVAL, 0)); _configured_robust_count.set(IGMP_ROBUSTNESS_VARIABLE); } if (proto_is_mld6()) { set_proto_version_default(MLD_VERSION_DEFAULT); _configured_query_interval.set(TimeVal(MLD_QUERY_INTERVAL, 0)); _query_last_member_interval.set( TimeVal(MLD_LAST_LISTENER_QUERY_INTERVAL, 0)); _query_response_interval.set(TimeVal(MLD_QUERY_RESPONSE_INTERVAL, 0)); _configured_robust_count.set(MLD_ROBUSTNESS_VARIABLE); } set_proto_version(proto_version_default()); } /** * Mld6igmpVif::~Mld6igmpVif: * @: * * MLD6IGMP protocol vif destructor. * **/ Mld6igmpVif::~Mld6igmpVif() { string error_msg; stop(error_msg); _group_records.delete_payload_and_clear(); BUFFER_FREE(_buffer_send); } /** * Mld6igmpVif::set_proto_version: * @proto_version: The protocol version to set. * * Set protocol version. * * Return value: %XORP_OK is @proto_version is valid, otherwise %XORP_ERROR. **/ int Mld6igmpVif::set_proto_version(int proto_version) { if (proto_is_igmp()) { if ((proto_version < IGMP_VERSION_MIN) || (proto_version > IGMP_VERSION_MAX)) { return (XORP_ERROR); } if (proto_version < IGMP_V3) { // // XXX: Restore the variables that might have been adopted from // the Querier. // restore_effective_variables(); } } if (proto_is_mld6()) { if ((proto_version < MLD_VERSION_MIN) || (proto_version > MLD_VERSION_MAX)) { return (XORP_ERROR); } if (proto_version < IGMP_V3) { // // XXX: Restore the variables that might have been adopted from // the Querier. // restore_effective_variables(); } } ProtoUnit::set_proto_version(proto_version); return (XORP_OK); } /** * Mld6igmpVif::proto_is_ssm: * @: * * Test if the interface is running a source-specific multicast capable * protocol version (e.g. IGMPv3 or MLDv2). * * Return value: @true if the protocol version is source-specific multicast * capable, otherwise @fa.se **/ bool Mld6igmpVif::proto_is_ssm() const { if (proto_is_igmp()) return (proto_version() >= IGMP_V3); if (proto_is_mld6()) return (proto_version() >= MLD_V2); return (false); } /** * Mld6igmpVif::start: * @error_msg: The error message (if error). * * Start MLD or IGMP on a single virtual interface. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int Mld6igmpVif::start(string& error_msg) { string dummy_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); } // // Start the vif only if it is of the appropriate type: // multicast-capable (loopback excluded). // if (! (is_multicast_capable() && (! is_loopback()))) { error_msg = "the interface is not multicast capable"; return (XORP_ERROR); } if (update_primary_address(error_msg) != XORP_OK) return (XORP_ERROR); if (ProtoUnit::start() != XORP_OK) { error_msg = "internal error"; return (XORP_ERROR); } // On startup, assume I am the MLD6IGMP Querier set_querier_addr(primary_addr()); set_i_am_querier(true); // // Register as a receiver with the kernel // if (mld6igmp_node().register_receiver(name(), name(), mld6igmp_node().ip_protocol_number(), true) != XORP_OK) { error_msg = c_format("cannot register as a receiver on vif %s " "with the kernel", name().c_str()); return (XORP_ERROR); } // // Join the appropriate multicast groups: ALL-SYSTEMS, ALL-ROUTERS, // and SSM-ROUTERS. // list groups; list::iterator groups_iter; groups.push_back(IPvX::MULTICAST_ALL_SYSTEMS(family())); groups.push_back(IPvX::MULTICAST_ALL_ROUTERS(family())); groups.push_back(IPvX::SSM_ROUTERS(family())); for (groups_iter = groups.begin(); groups_iter != groups.end(); ++groups_iter) { const IPvX& group = *groups_iter; if (mld6igmp_node().join_multicast_group(name(), name(), mld6igmp_node().ip_protocol_number(), group) != XORP_OK) { error_msg = c_format("cannot join group %s on vif %s", cstring(group), name().c_str()); return (XORP_ERROR); } } // // Query all members on startup // TimeVal max_resp_time = query_response_interval().get(); set no_sources; // XXX: empty set mld6igmp_query_send(primary_addr(), IPvX::MULTICAST_ALL_SYSTEMS(family()), max_resp_time, IPvX::ZERO(family()), no_sources, false, dummy_error_msg); _startup_query_count = effective_robustness_variable(); if (_startup_query_count > 0) _startup_query_count--; TimeVal startup_query_interval = effective_query_interval() / 4; _query_timer = mld6igmp_node().eventloop().new_oneoff_after( startup_query_interval, callback(this, &Mld6igmpVif::query_timer_timeout)); XLOG_INFO("Interface started: %s%s", this->str().c_str(), flags_string().c_str()); return (XORP_OK); } /** * Mld6igmpVif::stop: * @error_msg: The error message (if error). * * Stop MLD or IGMP on a single virtual interface. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int Mld6igmpVif::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() != XORP_OK) { error_msg = "internal error"; ret_value = XORP_ERROR; } // // XXX: we don't have to explicitly leave the multicast groups // we have joined on that interface, because this will happen // automatically when we stop the vif through the MFEA. // if (ProtoUnit::stop() != XORP_OK) { error_msg = "internal error"; ret_value = XORP_ERROR; } set_i_am_querier(false); set_querier_addr(IPvX::ZERO(family())); // XXX: ANY _other_querier_timer.unschedule(); _query_timer.unschedule(); _startup_query_count = 0; // Notify routing and remove all group records Mld6igmpGroupSet::const_iterator group_iter; for (group_iter = _group_records.begin(); group_iter != _group_records.end(); ++group_iter) { const Mld6igmpGroupRecord *group_record = group_iter->second; Mld6igmpSourceSet::const_iterator source_iter; // Clear the state for all included sources for (source_iter = group_record->do_forward_sources().begin(); source_iter != group_record->do_forward_sources().end(); ++source_iter) { const Mld6igmpSourceRecord *source_record = source_iter->second; join_prune_notify_routing(source_record->source(), group_record->group(), ACTION_PRUNE); } // Clear the state for all excluded sources if (group_record->is_asm_mode()) { join_prune_notify_routing(IPvX::ZERO(family()), group_record->group(), ACTION_PRUNE); } } _group_records.delete_payload_and_clear(); // // Unregister as a receiver with the kernel // if (mld6igmp_node().unregister_receiver(name(), name(), mld6igmp_node().ip_protocol_number()) != XORP_OK) { XLOG_ERROR("Cannot unregister as a receiver on 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 // mld6igmp_node().vif_shutdown_completed(name()); return (ret_value); } /** * Enable MLD/IGMP on a single virtual interface. * * If an unit is not enabled, it cannot be start, or pending-start. */ void Mld6igmpVif::enable() { ProtoUnit::enable(); XLOG_INFO("Interface enabled: %s%s", this->str().c_str(), flags_string().c_str()); } /** * Disable MLD/IGMP 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 Mld6igmpVif::disable() { string error_msg; stop(error_msg); ProtoUnit::disable(); XLOG_INFO("Interface disabled: %s%s", this->str().c_str(), flags_string().c_str()); } /** * Mld6igmpVif::mld6igmp_send: * @src: The message source address. * @dst: The message destination address. * @message_type: The MLD or IGMP type of the message. * @max_resp_code: The "Maximum Response Code" or "Max Resp Code" * field in the MLD or IGMP headers respectively (in the particular * protocol resolution). * @group_address: The "Multicast Address" or "Group Address" field * in the MLD or IGMP headers respectively. * @buffer: The buffer with the rest of the message. * @error_msg: The error message (if error). * * Send MLD or IGMP message. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int Mld6igmpVif::mld6igmp_send(const IPvX& src, const IPvX& dst, uint8_t message_type, uint16_t max_resp_code, const IPvX& group_address, buffer_t *buffer, string& error_msg) { uint16_t cksum; int ret_value; size_t datalen; bool ip_router_alert = true; // XXX: always use Router Alert option bool ip_internet_control = true; // XXX: always true if (! (is_up() || is_pending_down())) { error_msg = c_format("vif %s is not UP", name().c_str()); return (XORP_ERROR); } XLOG_ASSERT(src != IPvX::ZERO(family())); // // Prepare the MLD or IGMP header. // // Point the buffer to the protocol header datalen = BUFFER_DATA_SIZE(buffer); if (datalen > 0) { BUFFER_RESET_TAIL(buffer); } if (proto_is_igmp()) { BUFFER_PUT_OCTET(message_type, buffer); // The message type BUFFER_PUT_OCTET(max_resp_code, buffer); BUFFER_PUT_HOST_16(0, buffer); // Zero the checksum field BUFFER_PUT_IPVX(group_address, buffer); } if (proto_is_mld6()) { BUFFER_PUT_OCTET(message_type, buffer); // The message type BUFFER_PUT_OCTET(0, buffer); // XXX: Always 0 for MLD BUFFER_PUT_HOST_16(0, buffer); // Zero the checksum field BUFFER_PUT_HOST_16(max_resp_code, buffer); BUFFER_PUT_HOST_16(0, buffer); // Reserved BUFFER_PUT_IPVX(group_address, buffer); } // Restore the buffer to include the data if (datalen > 0) { BUFFER_RESET_TAIL(buffer); BUFFER_PUT_SKIP(datalen, buffer); } // // Compute the checksum // cksum = inet_checksum(BUFFER_DATA_HEAD(buffer), BUFFER_DATA_SIZE(buffer)); #ifdef HAVE_IPV6 // Add the checksum for the IPv6 pseudo-header if (proto_is_mld6()) { uint16_t cksum2; size_t ph_len = BUFFER_DATA_SIZE(buffer); cksum2 = calculate_ipv6_pseudo_header_checksum(src, dst, ph_len, IPPROTO_ICMPV6); cksum = inet_checksum_add(cksum, cksum2); } #endif // HAVE_IPV6 BUFFER_COPYPUT_INET_CKSUM(cksum, buffer, 2); // XXX: the checksum XLOG_TRACE(mld6igmp_node().is_log_trace(), "TX %s from %s to %s", proto_message_type2ascii(message_type), cstring(src), cstring(dst)); // // Send the message // ret_value = mld6igmp_node().mld6igmp_send(name(), name(), src, dst, mld6igmp_node().ip_protocol_number(), MINTTL, -1, ip_router_alert, ip_internet_control, buffer, error_msg); return (ret_value); buflen_error: XLOG_UNREACHABLE(); error_msg = c_format("TX %s from %s to %s: " "packet cannot fit into sending buffer", proto_message_type2ascii(message_type), cstring(src), cstring(dst)); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } /** * Send Group-Specific Query message. * * @param group_address the "Multicast Address" or "Group Address" field * in the MLD or IGMP headers respectively. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. **/ int Mld6igmpVif::mld6igmp_group_query_send(const IPvX& group_address, string& error_msg) { const IPvX& src = primary_addr(); const IPvX& dst = group_address; const TimeVal& max_resp_time = query_last_member_interval().get(); Mld6igmpGroupRecord* group_record = NULL; set no_sources; // XXX: empty set int ret_value; // // Only the Querier should originate Query messages // if (! i_am_querier()) return (XORP_OK); // Find the group record group_record = _group_records.find_group_record(group_address); if (group_record == NULL) return (XORP_ERROR); // No such group // // Lower the group timer // _group_records.lower_group_timer(group_address, last_member_query_time()); // // Send the message // ret_value = mld6igmp_query_send(src, dst, max_resp_time, group_address, no_sources, false, // XXX: reset the s_flag error_msg); // // Schedule the periodic Group-Specific Query // if (ret_value == XORP_OK) group_record->schedule_periodic_group_query(no_sources); // // Print the error message if there was an error // if (ret_value != XORP_OK) { XLOG_ERROR("Error sending Group-Specific query for %s: %s", cstring(group_address), error_msg.c_str()); } return (ret_value); } /** * Send MLDv2 or IGMPv3 Group-and-Source-Specific Query message. * * @param group_address the "Multicast Address" or "Group Address" field * in the MLD or IGMP headers respectively. * @param sources the set of source addresses. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. **/ int Mld6igmpVif::mld6igmp_group_source_query_send(const IPvX& group_address, const set& sources, string& error_msg) { const IPvX& src = primary_addr(); const IPvX& dst = group_address; const TimeVal& max_resp_time = query_last_member_interval().get(); Mld6igmpGroupRecord* group_record = NULL; set selected_sources; set::const_iterator source_iter; int ret_value; // // Only the Querier should originate Query messages // if (! i_am_querier()) return (XORP_OK); if (sources.empty()) return (XORP_OK); // No sources to query // Find the group record group_record = _group_records.find_group_record(group_address); if (group_record == NULL) return (XORP_ERROR); // No such group // // Select only the sources with source timer larger than the // Last Member Query Time. // for (source_iter = sources.begin(); source_iter != sources.end(); ++source_iter) { const IPvX& ipvx = *source_iter; Mld6igmpSourceRecord* source_record; source_record = group_record->find_do_forward_source(ipvx); if (source_record == NULL) continue; TimeVal timeval_remaining; source_record->source_timer().time_remaining(timeval_remaining); if (timeval_remaining <= last_member_query_time()) continue; selected_sources.insert(ipvx); } if (selected_sources.empty()) return (XORP_OK); // No selected sources to query // // Lower the source timer // group_record->lower_source_timer(selected_sources, last_member_query_time()); // // Send the message // ret_value = mld6igmp_query_send(src, dst, max_resp_time, group_address, selected_sources, false, // XXX: reset the s_flag error_msg); // // Schedule the periodic Group-and-Source-Specific Query // if (ret_value == XORP_OK) group_record->schedule_periodic_group_query(selected_sources); // // Print the error message if there was an error // if (ret_value != XORP_OK) { XLOG_ERROR("Error sending Group-and-Source-Specific query for %s: %s", cstring(group_address), error_msg.c_str()); } return (ret_value); } /** * Send MLD or IGMP Query message. * * @param src the message source address. * @param dst the message destination address. * @param max_resp_time the maximum response time. * @param group_address the "Multicast Address" or "Group Address" field * in the MLD or IGMP headers respectively. * @param sources the set of source addresses (for IGMPv3 or MLDv2 only). * @param s_flag the "Suppress Router-Side Processing" bit (for IGMPv3 * or MLDv2 only; in all other cases it should be set to false). * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. **/ int Mld6igmpVif::mld6igmp_query_send(const IPvX& src, const IPvX& dst, const TimeVal& max_resp_time, const IPvX& group_address, const set& sources, bool s_flag, string& error_msg) { buffer_t *buffer; uint32_t timer_scale = mld6igmp_constant_timer_scale(); TimeVal scaled_max_resp_time = max_resp_time * timer_scale; set::const_iterator source_iter; uint8_t qrv, qqic; size_t max_sources_n; size_t max_payload = 0; Mld6igmpGroupRecord* group_record = NULL; // // Only the Querier should originate Query messages // if (! i_am_querier()) return (XORP_OK); // Find the group record group_record = _group_records.find_group_record(group_address); // // Check protocol version and Query message matching // do { if (sources.empty()) break; // IGMPv3/MLDv2 Query(G, A) message if (is_igmpv3_mode(group_record) || is_mldv2_mode(group_record)) break; // XXX: Query(G, A) messages are not allowed in this mode return (XORP_ERROR); } while (false); // // Lower the group and source timers (if necessary) // if (! s_flag) { if (sources.empty()) { // XXX: Q(G) Query _group_records.lower_group_timer(group_address, last_member_query_time()); } else { // XXX: Q(G, A) Query _group_records.lower_source_timer(group_address, sources, last_member_query_time()); } } // // Prepare the data after the Query header // // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | Resv |S| QRV | QQIC | Number of Sources (N) | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // qrv = 0; if (effective_robustness_variable() <= 0x7) qrv = effective_robustness_variable(); if (s_flag) qrv |= 0x8; qqic = 0; encode_exp_time_code8(effective_query_interval(), qqic, 1); // // Calculate the maximum number of sources // max_sources_n = sources.size(); if (proto_is_igmp()) { max_payload = mtu() // The MTU of the vif - (0xf << 2) // IPv4 max header size - 4 // IPv4 Router Alert option - IGMP_V3_QUERY_MINLEN; // IGMPv3 Query pre-source fields } if (proto_is_mld6()) { max_payload = mtu() // The MTU of the vif - 8 // IPv6 Hop-by-hop Ext. Header with Router Alert option - MLD_V2_QUERY_MINLEN; // MLDv2 Query pre-source fields } max_sources_n = min(max_sources_n, max_payload / IPvX::addr_bytelen(family())); // // XXX: According to RFC 3810 (MLDv2), Section 8.3.2, the Querier // continues to send MLDv2 queries, regardless of its Multicast Address // Compatibility Mode. // // Interestingly, RFC 3376 (IGMPv3) does not include this statement. // According to the following email, the need for this statement has been // discovered too late to be included in RFC 3376: // http://www1.ietf.org/mail-archive/web/ssm/current/msg00084.html // // // Prepare the buffer // buffer = buffer_send_prepare(); BUFFER_PUT_SKIP(mld6igmp_constant_minlen(), buffer); // // Insert the data (for IGMPv3 and MLDv2 only) // if (is_igmpv3_mode() || is_mldv2_mode()) { // // XXX: Note that we consider only the interface mode, but ignore // the Multicast Address Compatibility Mode. // BUFFER_PUT_OCTET(qrv, buffer); BUFFER_PUT_OCTET(qqic, buffer); BUFFER_PUT_HOST_16(max_sources_n, buffer); source_iter = sources.begin(); while (max_sources_n > 0) { const IPvX& ipvx = *source_iter; BUFFER_PUT_IPVX(ipvx, buffer); ++source_iter; max_sources_n--; } } else { // // If IGMPv1 Multicast Address Compatibility Mode, then set the // Max Response Time to zero. // if (is_igmpv1_mode(group_record)) scaled_max_resp_time = TimeVal::ZERO(); } // // Send the message // return (mld6igmp_send(src, dst, mld6igmp_constant_membership_query(), scaled_max_resp_time.sec(), group_address, buffer, error_msg)); buflen_error: XLOG_UNREACHABLE(); XLOG_ERROR("INTERNAL mld6igmp_query_send() ERROR: " "buffer size too small"); return (XORP_ERROR); } /** * Mld6igmpVif::mld6igmp_recv: * @src: The message source address. * @dst: The message destination address. * @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. * @ip_router_alert: True if the received IP packet had the Router Alert * IP option set. * @ip_internet_control: If true, then this is IP control traffic. * @buffer: The buffer with the received message. * @error_msg: The error message (if error). * * Receive MLD or IGMP message and pass it for processing. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int Mld6igmpVif::mld6igmp_recv(const IPvX& src, const IPvX& dst, int ip_ttl, int ip_tos, bool ip_router_alert, bool ip_internet_control, buffer_t *buffer, string& error_msg) { int ret_value = XORP_ERROR; if (! is_up()) { error_msg = c_format("vif %s is not UP", name().c_str()); return (XORP_ERROR); } ret_value = mld6igmp_process(src, dst, ip_ttl, ip_tos, ip_router_alert, ip_internet_control, buffer, error_msg); return (ret_value); } /** * Mld6igmpVif::mld6igmp_process: * @src: The message source address. * @dst: The message destination address. * @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. * @ip_router_alert: True if the received IP packet had the Router Alert * IP option set. * @ip_internet_control: If true, then this is IP control traffic. * @buffer: The buffer with the message. * @error_msg: The error message (if error). * * Process MLD or IGMP message and pass the control to the type-specific * functions. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int Mld6igmpVif::mld6igmp_process(const IPvX& src, const IPvX& dst, int ip_ttl, int ip_tos, bool ip_router_alert, bool ip_internet_control, buffer_t *buffer, string& error_msg) { uint8_t message_type = 0; uint16_t max_resp_code = 0; IPvX group_address(family()); uint16_t cksum; bool check_router_alert_option = false; bool check_src_linklocal_unicast = false; bool allow_src_zero_address = false; bool check_dst_multicast = false; bool check_group_interfacelocal_multicast = false; bool decode_extra_fields = false; // // Message length check. // if (BUFFER_DATA_SIZE(buffer) < mld6igmp_constant_minlen()) { error_msg = c_format("RX packet from %s to %s on vif %s: " "too short data field (%u octets)", cstring(src), cstring(dst), name().c_str(), XORP_UINT_CAST(BUFFER_DATA_SIZE(buffer))); XLOG_WARNING("%s", error_msg.c_str()); return (XORP_ERROR); } // // Checksum verification. // cksum = inet_checksum(BUFFER_DATA_HEAD(buffer), BUFFER_DATA_SIZE(buffer)); #ifdef HAVE_IPV6 // Add the checksum for the IPv6 pseudo-header if (proto_is_mld6()) { uint16_t cksum2; size_t ph_len = BUFFER_DATA_SIZE(buffer); cksum2 = calculate_ipv6_pseudo_header_checksum(src, dst, ph_len, IPPROTO_ICMPV6); cksum = inet_checksum_add(cksum, cksum2); } #endif // HAVE_IPV6 if (cksum) { error_msg = c_format("RX packet from %s to %s on vif %s: " "checksum error", cstring(src), cstring(dst), name().c_str()); XLOG_WARNING("%s", error_msg.c_str()); return (XORP_ERROR); } // // Protocol version check. // // XXX: MLD and IGMP messages do not have an explicit field for protocol // version. Protocol version check is performed later, per (some) message // type. // // // Get the message type and the max. resp. time (in case of IGMP). // // Note that in case of IGMP the max. resp. time is the `igmp_code' field // in `struct igmp'. // if (proto_is_igmp()) { BUFFER_GET_OCTET(message_type, buffer); BUFFER_GET_OCTET(max_resp_code, buffer); BUFFER_GET_SKIP(2, buffer); // The checksum } if (proto_is_mld6()) { BUFFER_GET_OCTET(message_type, buffer); BUFFER_GET_SKIP(1, buffer); // The `Code' field: unused BUFFER_GET_SKIP(2, buffer); // The `Checksum' field } XLOG_TRACE(mld6igmp_node().is_log_trace(), "RX %s from %s to %s on vif %s", proto_message_type2ascii(message_type), cstring(src), cstring(dst), name().c_str()); // // Ignore messages that are not recognized by older protocol version. // // XXX: Unrecognized message types MUST be silently ignored. // if (proto_is_igmp()) { switch (message_type) { case IGMP_MEMBERSHIP_QUERY: // Recognized by IGMPv1, IGMPv2, IGMPv3 break; case IGMP_V1_MEMBERSHIP_REPORT: // Recognized by IGMPv1, IGMPv2, IGMPv3 break; case IGMP_V2_MEMBERSHIP_REPORT: // Recognized by IGMPv2, IGMPv3 if (is_igmpv1_mode()) return (XORP_ERROR); break; case IGMP_V2_LEAVE_GROUP: // Recognized by IGMPv2, IGMPv3 if (is_igmpv1_mode()) return (XORP_ERROR); break; case IGMP_V3_MEMBERSHIP_REPORT: // Recognized by IGMPv3 if (is_igmpv1_mode() || is_igmpv2_mode()) return (XORP_ERROR); break; case IGMP_DVMRP: case IGMP_MTRACE: break; default: // Unrecognized message return (XORP_ERROR); } } if (proto_is_mld6()) { switch (message_type) { case MLD_LISTENER_QUERY: // Recognized by MLDv1, MLDv2 break; case MLD_LISTENER_REPORT: // Recognized by MLDv1, MLDv2 break; case MLD_LISTENER_DONE: // Recognized by MLDv1, MLDv2 break; case MLDV2_LISTENER_REPORT: // Recognized by MLDv2 if (is_mldv1_mode()) return (XORP_ERROR); break; case MLD_MTRACE: break; default: // Unrecognized message return (XORP_ERROR); } } // // Assign various flags what needs to be checked, based on the // message type: // - check_router_alert_option // - check_src_linklocal_unicast // - allow_src_zero_address // - check_dst_multicast // - check_group_interfacelocal_multicast // - decode_extra_fields // if (proto_is_igmp()) { switch (message_type) { case IGMP_MEMBERSHIP_QUERY: case IGMP_V1_MEMBERSHIP_REPORT: case IGMP_V2_MEMBERSHIP_REPORT: case IGMP_V2_LEAVE_GROUP: case IGMP_V3_MEMBERSHIP_REPORT: if (_ip_router_alert_option_check.get()) check_router_alert_option = true; check_src_linklocal_unicast = false; // Not needed for IPv4 if (is_igmpv3_mode()) { if ((message_type == IGMP_V1_MEMBERSHIP_REPORT) || (message_type == IGMP_V2_MEMBERSHIP_REPORT) || (message_type == IGMP_V3_MEMBERSHIP_REPORT)) { allow_src_zero_address = true; // True only for IGMPv3 } } check_dst_multicast = true; if (is_igmpv3_mode()) check_dst_multicast = false; // XXX: disable check_group_interfacelocal_multicast = false;// Not needed for IPv4 decode_extra_fields = true; if (message_type == IGMP_V3_MEMBERSHIP_REPORT) decode_extra_fields = false; break; case IGMP_DVMRP: case IGMP_MTRACE: // TODO: Assign the flags as appropriate break; default: break; } } if (proto_is_mld6()) { switch (message_type) { case MLD_LISTENER_QUERY: case MLD_LISTENER_REPORT: case MLD_LISTENER_DONE: case MLDV2_LISTENER_REPORT: check_router_alert_option = true; check_src_linklocal_unicast = true; allow_src_zero_address = false; // Always false for MLD check_dst_multicast = true; if (is_mldv2_mode()) check_dst_multicast = false; // XXX: disable check_group_interfacelocal_multicast = true; decode_extra_fields = true; if (message_type == MLDV2_LISTENER_REPORT) decode_extra_fields = false; break; case MLD_MTRACE: // TODO: Assign the flags as appropriate break; default: break; } } // // Decode the extra fields: the max. resp. time (in case of MLD), // and the group address. // if (decode_extra_fields) { if (proto_is_igmp()) { BUFFER_GET_IPVX(family(), group_address, buffer); } if (proto_is_mld6()) { BUFFER_GET_HOST_16(max_resp_code, buffer); BUFFER_GET_SKIP(2, buffer); // The `Reserved' field BUFFER_GET_IPVX(family(), group_address, buffer); } } // // IP Router Alert option check. // if (check_router_alert_option && (! ip_router_alert)) { error_msg = c_format("RX %s from %s to %s on vif %s: " "missing IP Router Alert option", proto_message_type2ascii(message_type), cstring(src), cstring(dst), name().c_str()); XLOG_WARNING("%s", error_msg.c_str()); return (XORP_ERROR); } // // TODO: check the TTL, TOS and ip_internet_control flag if we are // running in secure mode. // UNUSED(ip_ttl); UNUSED(ip_tos); UNUSED(ip_internet_control); #if 0 if (ip_ttl != MINTTL) { error_msg = c_format("RX %s from %s to %s on vif %s: " "ip_ttl = %d instead of %d", proto_message_type2ascii(message_type), cstring(src), cstring(dst), name().c_str(), ip_ttl, MINTTL); XLOG_WARNING("%s", error_msg.c_str()); return (XORP_ERROR); } #endif // 0 // // Source address check. // if (! (src.is_unicast() || (allow_src_zero_address && src.is_zero()))) { // // Source address must always be unicast. // The kernel should have checked that, but just in case... // error_msg = c_format("RX %s from %s to %s on vif %s: " "source must be unicast", proto_message_type2ascii(message_type), cstring(src), cstring(dst), name().c_str()); XLOG_WARNING("%s", error_msg.c_str()); return (XORP_ERROR); } if (src.af() != family()) { // Invalid source address family XLOG_WARNING("RX %s from %s to %s on vif %s: " "invalid source address family " "(received %d expected %d)", proto_message_type2ascii(message_type), cstring(src), cstring(dst), name().c_str(), src.af(), family()); } // Source address must be directly connected if (! (mld6igmp_node().is_directly_connected(*this, src) || (allow_src_zero_address && src.is_zero()))) { error_msg = c_format("RX %s from %s to %s on vif %s: " "source must be directly connected", proto_message_type2ascii(message_type), cstring(src), cstring(dst), name().c_str()); XLOG_WARNING("%s", error_msg.c_str()); return (XORP_ERROR); } if (check_src_linklocal_unicast) { if (src.is_linklocal_unicast() || (allow_src_zero_address && src.is_zero())) { // The source address is link-local or (allowed) zero address } else { // The source address is not link-local error_msg = c_format("RX %s from %s to %s on vif %s: " "source is not a link-local address", proto_message_type2ascii(message_type), cstring(src), cstring(dst), name().c_str()); XLOG_WARNING("%s", error_msg.c_str()); return (XORP_ERROR); } } // // Destination address check. // if (dst.af() != family()) { // Invalid destination address family XLOG_WARNING("RX %s from %s to %s on vif %s: " "invalid destination address family " "(received %d expected %d)", proto_message_type2ascii(message_type), cstring(src), cstring(dst), name().c_str(), dst.af(), family()); } if (check_dst_multicast && (! dst.is_multicast())) { // The destination address is not multicast error_msg = c_format("RX %s from %s to %s on vif %s: " "destination must be multicast. " "Packet ignored.", proto_message_type2ascii(message_type), cstring(src), cstring(dst), name().c_str()); XLOG_WARNING("%s", error_msg.c_str()); return (XORP_ERROR); } // // Inner multicast address scope check. // if (check_group_interfacelocal_multicast && group_address.is_interfacelocal_multicast()) { error_msg = c_format("RX %s from %s to %s on vif %s: " "invalid interface-local scope of inner " "multicast address: %s", proto_message_type2ascii(message_type), cstring(src), cstring(dst), name().c_str(), cstring(group_address)); XLOG_WARNING("%s", error_msg.c_str()); return (XORP_ERROR); } // // Origin router neighbor check. // // XXX: in IGMP and MLD we don't need such check // // Process each message, based on its type. // if (proto_is_igmp()) { switch (message_type) { case IGMP_MEMBERSHIP_QUERY: mld6igmp_membership_query_recv(src, dst, message_type, max_resp_code, group_address, buffer); break; case IGMP_V1_MEMBERSHIP_REPORT: case IGMP_V2_MEMBERSHIP_REPORT: mld6igmp_membership_report_recv(src, dst, message_type, max_resp_code, group_address, buffer); break; case IGMP_V2_LEAVE_GROUP: mld6igmp_leave_group_recv(src, dst, message_type, max_resp_code, group_address, buffer); break; case IGMP_V3_MEMBERSHIP_REPORT: mld6igmp_ssm_membership_report_recv(src, dst, message_type, buffer); break; case IGMP_DVMRP: { // // XXX: We care only about the DVMRP messages that are used // by mrinfo. // // XXX: the older purpose of the 'igmp_code' field uint16_t igmp_code = max_resp_code; switch (igmp_code) { case DVMRP_ASK_NEIGHBORS: // Some old DVMRP messages from mrinfo(?). // TODO: not implemented yet. // TODO: do we really need this message implemented? break; case DVMRP_ASK_NEIGHBORS2: // Used for mrinfo support. // XXX: not implemented yet. break; case DVMRP_INFO_REQUEST: // Information request (TODO: used by mrinfo?) // TODO: not implemented yet. break; default: // XXX: We don't care about the rest of the DVMRP_* messages break; } } case IGMP_MTRACE: // TODO: is this the new message sent by 'mtrace'? // TODO: not implemented yet. break; default: // XXX: Unrecognized message types MUST be silently ignored. break; } } if (proto_is_mld6()) { switch (message_type) { case MLD_LISTENER_QUERY: mld6igmp_membership_query_recv(src, dst, message_type, max_resp_code, group_address, buffer); break; case MLD_LISTENER_REPORT: mld6igmp_membership_report_recv(src, dst, message_type, max_resp_code, group_address, buffer); break; case MLD_LISTENER_DONE: mld6igmp_leave_group_recv(src, dst, message_type, max_resp_code, group_address, buffer); break; case MLDV2_LISTENER_REPORT: mld6igmp_ssm_membership_report_recv(src, dst, message_type, buffer); break; case MLD_MTRACE: // TODO: is this the new message sent by 'mtrace'? // TODO: not implemented yet. break; default: // XXX: Unrecognized message types MUST be silently ignored. break; } } return (XORP_OK); rcvlen_error: XLOG_UNREACHABLE(); error_msg = c_format("RX packet from %s to %s on vif %s: " "some fields are too short", cstring(src), cstring(dst), name().c_str()); XLOG_WARNING("%s", error_msg.c_str()); return (XORP_ERROR); } /** * Mld6igmpVif::update_primary_address: * @error_msg: The error message (if error). * * Update the primary address. * * The primary address should be a link-local unicast address, and * is used for transmitting the multicast control packets on the LAN. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int Mld6igmpVif::update_primary_address(string& error_msg) { bool i_was_querier = false; IPvX primary_a(IPvX::ZERO(family())); IPvX domain_wide_a(IPvX::ZERO(family())); // // Reset the primary address if it is not valid anymore. // if (Vif::find_address(primary_addr()) == NULL) { if (primary_addr() == querier_addr()) { // Reset the querier address set_querier_addr(IPvX::ZERO(family())); set_i_am_querier(false); i_was_querier = true; } set_primary_addr(IPvX::ZERO(family())); } list::const_iterator iter; for (iter = addr_list().begin(); iter != addr_list().end(); ++iter) { const VifAddr& vif_addr = *iter; const IPvX& addr = vif_addr.addr(); if (! addr.is_unicast()) continue; if (addr.is_linklocal_unicast()) { if (primary_a.is_zero()) primary_a = addr; continue; } // // XXX: assume that everything else can be a domain-wide reachable // address. if (domain_wide_a.is_zero()) domain_wide_a = addr; } // // XXX: In case of IPv6 if there is no link-local address we may try // to use the the domain-wide address as a primary address, // but the MLD spec is clear that the MLD messages are to be originated // from a link-local address. // Hence, only in case of IPv4 we assign the domain-wide address // to the primary address. // if (is_ipv4()) { if (primary_a.is_zero()) primary_a = domain_wide_a; } // // Check that the interface has a primary address. // if (primary_addr().is_zero() && primary_a.is_zero()) { error_msg = "invalid primary address"; return (XORP_ERROR); } if (primary_addr().is_zero()) set_primary_addr(primary_a); if (i_was_querier) { // Assume again that I am the MLD6IGMP Querier set_querier_addr(primary_addr()); set_i_am_querier(true); } return (XORP_OK); } /** * Mld6igmpVif::add_protocol: * @module_id: The #xorp_module_id of the protocol to add. * @module_instance_name: The module instance name of the protocol to add. * * Add a protocol to the list of entries that would be notified if there * is membership change on this interface. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int Mld6igmpVif::add_protocol(xorp_module_id module_id, const string& module_instance_name) { if (find(_notify_routing_protocols.begin(), _notify_routing_protocols.end(), pair(module_id, module_instance_name)) != _notify_routing_protocols.end()) { return (XORP_ERROR); // Already added } _notify_routing_protocols.push_back( pair(module_id, module_instance_name)); return (XORP_OK); } /** * Mld6igmpVif::delete_protocol: * @module_id: The #xorp_module_id of the protocol to delete. * @module_instance_name: The module instance name of the protocol to delete. * * Delete a protocol from the list of entries that would be notified if there * is membership change on this interface. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int Mld6igmpVif::delete_protocol(xorp_module_id module_id, const string& module_instance_name) { vector >::iterator iter; iter = find(_notify_routing_protocols.begin(), _notify_routing_protocols.end(), pair(module_id, module_instance_name)); if (iter == _notify_routing_protocols.end()) return (XORP_ERROR); // Not on the list _notify_routing_protocols.erase(iter); return (XORP_OK); } /** * Test if the interface is running in IGMPv1 mode. * * @return true if the interface is running in IGMPv1 mode, otherwise false. */ bool Mld6igmpVif::is_igmpv1_mode() const { return (proto_is_igmp() && (proto_version() == IGMP_V1)); } /** * Test if the interface is running in IGMPv2 mode. * * @return true if the interface is running in IGMPv2 mode, otherwise false. */ bool Mld6igmpVif::is_igmpv2_mode() const { return (proto_is_igmp() && (proto_version() == IGMP_V2)); } /** * Test if the interface is running in IGMPv3 mode. * * @return true if the interface is running in IGMPv3 mode, otherwise false. */ bool Mld6igmpVif::is_igmpv3_mode() const { return (proto_is_igmp() && (proto_version() == IGMP_V3)); } /** * Test if the interface is running in MLDv1 mode. * * @return true if the interface is running in MLDv1 mode, otherwise false. */ bool Mld6igmpVif::is_mldv1_mode() const { return (proto_is_mld6() && (proto_version() == MLD_V1)); } /** * Test if the interface is running in MLDv2 mode. * * @return true if the interface is running in MLDv2 mode, otherwise false. */ bool Mld6igmpVif::is_mldv2_mode() const { return (proto_is_mld6() && (proto_version() == MLD_V2)); } /** * Test if a group is running in IGMPv1 mode. * * Note that if @ref group_record is NULL, then we test whether the * interface itself is running in IGMPv1 mode. * @param group_record the group record to test. * @return true if the group is running in IGMPv1 mode, * otherwise false. */ bool Mld6igmpVif::is_igmpv1_mode(const Mld6igmpGroupRecord* group_record) const { if (group_record != NULL) return (group_record->is_igmpv1_mode()); return (is_igmpv1_mode()); } /** * Test if a group is running in IGMPv2 mode. * * Note that if @ref group_record is NULL, then we test whether the * interface itself is running in IGMPv2 mode. * @param group_record the group record to test. * @return true if the group is running in IGMPv2 mode, * otherwise false. */ bool Mld6igmpVif::is_igmpv2_mode(const Mld6igmpGroupRecord* group_record) const { if (group_record != NULL) return (group_record->is_igmpv2_mode()); return (is_igmpv2_mode()); } /** * Test if a group is running in IGMPv3 mode. * * Note that if @ref group_record is NULL, then we test whether the * interface itself is running in IGMPv3 mode. * @param group_record the group record to test. * @return true if the group is running in IGMPv3 mode, * otherwise false. */ bool Mld6igmpVif::is_igmpv3_mode(const Mld6igmpGroupRecord* group_record) const { if (group_record != NULL) return (group_record->is_igmpv3_mode()); return (is_igmpv3_mode()); } /** * Test if a group is running in MLDv1 mode. * * Note that if @ref group_record is NULL, then we test whether the * interface itself is running in MLDv1 mode. * @param group_record the group record to test. * @return true if the group is running in MLDv1 mode, * otherwise false. */ bool Mld6igmpVif::is_mldv1_mode(const Mld6igmpGroupRecord* group_record) const { if (group_record != NULL) return (group_record->is_mldv1_mode()); return (is_mldv1_mode()); } /** * Test if a group is running in MLDv2 mode. * * Note that if @ref group_record is NULL, then we test whether the * interface itself is running in MLDv2 mode. * @param group_record the group record to test. * @return true if the group is running in MLDv2 mode, * otherwise false. */ bool Mld6igmpVif::is_mldv2_mode(const Mld6igmpGroupRecord* group_record) const { if (group_record != NULL) return (group_record->is_mldv2_mode()); return (is_mldv2_mode()); } /** * Return the ASCII text description of the protocol message. * * @param message_type the protocol message type. * @return the ASCII text descrpition of the protocol message. */ const char * Mld6igmpVif::proto_message_type2ascii(uint8_t message_type) const { if (proto_is_igmp()) return (IGMPTYPE2ASCII(message_type)); if (proto_is_mld6()) return (MLDTYPE2ASCII(message_type)); return ("Unknown protocol message"); } /** * Reset and prepare the buffer for sending data. * * @return the prepared buffer. */ buffer_t * Mld6igmpVif::buffer_send_prepare() { BUFFER_RESET(_buffer_send); return (_buffer_send); } /** * Calculate the checksum of an IPv6 "pseudo-header" as described * in RFC 2460. * * @param src the source address of the pseudo-header. * @param dst the destination address of the pseudo-header. * @param len the upper-layer packet length of the pseudo-header * (in host-order). * @param protocol the upper-layer protocol number. * @return the checksum of the IPv6 "pseudo-header". */ uint16_t Mld6igmpVif::calculate_ipv6_pseudo_header_checksum(const IPvX& src, const IPvX& dst, size_t len, uint8_t protocol) { struct ip6_pseudo_hdr { struct in6_addr ip6_src; // Source address struct in6_addr ip6_dst; // Destination address uint32_t ph_len; // Upper-layer packet length uint8_t ph_zero[3]; // Zero uint8_t ph_next; // Upper-layer protocol number } ip6_pseudo_header; // TODO: may need __attribute__((__packed__)) src.copy_out(ip6_pseudo_header.ip6_src); dst.copy_out(ip6_pseudo_header.ip6_dst); ip6_pseudo_header.ph_len = htonl(len); ip6_pseudo_header.ph_zero[0] = 0; ip6_pseudo_header.ph_zero[1] = 0; ip6_pseudo_header.ph_zero[2] = 0; ip6_pseudo_header.ph_next = protocol; uint16_t cksum = inet_checksum( reinterpret_cast(&ip6_pseudo_header), sizeof(ip6_pseudo_header)); return (cksum); } /** * Notify the interested parties that there is membership change among * the local members. * * @param source the source address of the (S,G) entry that has changed. * In case of group-specific membership, it could be IPvX::ZERO(). * @param group the group address of the (S,G) entry that has changed. * @param action_jp the membership change: @ref ACTION_JOIN * or @ref ACTION_PRUNE. * @return XORP_OK on success, otherwise XORP_ERROR. */ int Mld6igmpVif::join_prune_notify_routing(const IPvX& source, const IPvX& group, action_jp_t action_jp) const { XLOG_TRACE(mld6igmp_node().is_log_trace(), "Notify routing %s membership for (%s, %s) on vif %s", (action_jp == ACTION_JOIN)? "add" : "delete", cstring(source), cstring(group), name().c_str()); vector >::const_iterator iter; for (iter = _notify_routing_protocols.begin(); iter != _notify_routing_protocols.end(); ++iter) { pair my_pair = *iter; xorp_module_id module_id = my_pair.first; string module_instance_name = my_pair.second; if (mld6igmp_node().join_prune_notify_routing(module_instance_name, module_id, vif_index(), source, group, action_jp) != XORP_OK) { // // TODO: remove ?? // } } return (XORP_OK); } bool Mld6igmpVif::i_am_querier() const { if (_proto_flags & MLD6IGMP_VIF_QUERIER) return (true); else return (false); } void Mld6igmpVif::set_i_am_querier(bool v) { if (v) { _proto_flags |= MLD6IGMP_VIF_QUERIER; // // XXX: Restore the variables that might have been adopted from // the Querier. // restore_effective_variables(); } else { _proto_flags &= ~MLD6IGMP_VIF_QUERIER; } } size_t Mld6igmpVif::mld6igmp_constant_minlen() const { if (proto_is_igmp()) return (IGMP_MINLEN); if (proto_is_mld6()) return (MLD_MINLEN); XLOG_UNREACHABLE(); return (0); } uint32_t Mld6igmpVif::mld6igmp_constant_timer_scale() const { if (proto_is_igmp()) return (IGMP_TIMER_SCALE); if (proto_is_mld6()) return (MLD_TIMER_SCALE); XLOG_UNREACHABLE(); return (0); } uint8_t Mld6igmpVif::mld6igmp_constant_membership_query() const { if (proto_is_igmp()) return (IGMP_MEMBERSHIP_QUERY); if (proto_is_mld6()) return (MLD_LISTENER_QUERY); XLOG_UNREACHABLE(); return (0); } // TODO: temporary here. Should go to the Vif class after the Vif // class starts using the Proto class string Mld6igmpVif::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); } xorp/contrib/mld6igmp_lite/xrl_mld6igmp_node.hh0000664000076400007640000007064111421137511022012 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/contrib/mld6igmp_lite/xrl_mld6igmp_node.hh,v 1.3 2008/10/02 21:56:33 bms Exp $ #ifndef __MLD6IGMP_XRL_MLD6IGMP_NODE_HH__ #define __MLD6IGMP_XRL_MLD6IGMP_NODE_HH__ // // MLD6IGMP XRL-aware node definition. // #include "libxipc/xrl_std_router.hh" #include "libfeaclient/ifmgr_xrl_mirror.hh" #include "xrl/interfaces/finder_event_notifier_xif.hh" #include "xrl/interfaces/fea_rawpkt4_xif.hh" #include "xrl/interfaces/fea_rawpkt6_xif.hh" #include "xrl/interfaces/cli_manager_xif.hh" #include "xrl/interfaces/mld6igmp_client_xif.hh" #include "xrl/targets/mld6igmp_base.hh" #include "mld6igmp_node.hh" #include "mld6igmp_node_cli.hh" // // The top-level class that wraps-up everything together under one roof // class XrlMld6igmpNode : public Mld6igmpNode, public XrlStdRouter, public XrlMld6igmpTargetBase, public Mld6igmpNodeCli { public: XrlMld6igmpNode(int family, xorp_module_id module_id, EventLoop& eventloop, const string& class_name, const string& finder_hostname, uint16_t finder_port, const string& finder_target, const string& fea_target, const string& mfea_target); virtual ~XrlMld6igmpNode(); /** * Startup the node operation. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int startup(); /** * Shutdown the node operation. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int shutdown(); /** * Get a reference to the XrlRouter instance. * * @return a reference to the XrlRouter (@ref XrlRouter) instance. */ XrlRouter& xrl_router() { return *this; } /** * Get a const reference to the XrlRouter instance. * * @return a const reference to the XrlRouter (@ref XrlRouter) instance. */ const XrlRouter& xrl_router() const { return *this; } // // XrlMld6igmpNode front-end interface // int enable_cli(); int disable_cli(); int start_cli(); int stop_cli(); int enable_mld6igmp(); int disable_mld6igmp(); int start_mld6igmp(); int stop_mld6igmp(); // XrlTask relatedMethods that need to be public void send_register_unregister_interest(); void send_register_unregister_receiver(); void send_join_leave_multicast_group(); void send_protocol_message(); protected: // // XRL target methods // /** * Get name of Xrl Target */ XrlCmdError common_0_1_get_target_name( // Output values, string& name); /** * Get version string from Xrl Target */ XrlCmdError common_0_1_get_version( // Output values, string& version); /** * Get status from Xrl Target */ XrlCmdError common_0_1_get_status(// Output values, uint32_t& status, string& reason); /** * Shutdown cleanly */ XrlCmdError common_0_1_shutdown(); /** * Announce target birth to observer. * * @param target_class the target class name. * * @param target_instance the target instance name. */ XrlCmdError finder_event_observer_0_1_xrl_target_birth( // Input values, const string& target_class, const string& target_instance); /** * Announce target death to observer. * * @param target_class the target class name. * * @param target_instance the target instance name. */ XrlCmdError finder_event_observer_0_1_xrl_target_death( // Input values, const string& target_class, const string& target_instance); /** * Process a CLI command. * * @param processor_name the processor name for this command. * * @param cli_term_name the terminal name the command was entered from. * * @param cli_session_id the CLI session ID the command was entered from. * * @param command_name the command name to process. * * @param command_args the command arguments to process. * * @param ret_processor_name the processor name to return back to the CLI. * * @param ret_cli_term_name the terminal name to return back. * * @param ret_cli_session_id the CLI session ID to return back. * * @param ret_command_output the command output to return back. */ XrlCmdError cli_processor_0_1_process_command( // Input values, const string& processor_name, const string& cli_term_name, const uint32_t& cli_session_id, const string& command_name, const string& command_args, // Output values, string& ret_processor_name, string& ret_cli_term_name, uint32_t& ret_cli_session_id, string& ret_command_output); /** * Receive an IPv4 packet from a raw socket. * * @param if_name the interface name the packet arrived on. * * @param vif_name the vif name the packet arrived on. * * @param src_address the IP source address. * * @param dst_address the IP destination address. * * @param ip_protocol the IP protocol number. * * @param ip_ttl the IP TTL (hop-limit). If it has a negative value, then * the received value is unknown. * * @param ip_tos the Type of Service (Diffserv/ECN bits for IPv4). If it * has a negative value, then the received value is unknown. * * @param ip_router_alert if true, the IP Router Alert option was included * in the IP packet. * * @param ip_internet_control if true, then this is IP control traffic. */ XrlCmdError raw_packet4_client_0_1_recv( // Input values, const string& if_name, const string& vif_name, const IPv4& src_address, const IPv4& dst_address, const uint32_t& ip_protocol, const int32_t& ip_ttl, const int32_t& ip_tos, const bool& ip_router_alert, const bool& ip_internet_control, const vector& payload); /** * Receive an IPv6 packet from a raw socket. * * @param if_name the interface name the packet arrived on. * * @param vif_name the vif name the packet arrived on. * * @param src_address the IP source address. * * @param dst_address the IP destination address. * * @param ip_protocol the IP protocol number. * * @param ip_ttl the IP TTL (hop-limit). If it has a negative value, then * the received value is unknown. * * @param ip_tos the Type Of Service (IP traffic class for IPv4). If it * has a negative value, then the received value is unknown. * * @param ip_router_alert if true, the IP Router Alert option was included * in the IP packet. * * @param ip_internet_control if true, then this is IP control traffic. * * @param ext_headers_type a list of u32 integers with the types of the * optional extention headers. * * @param ext_headers_payload a list of payload data, one for each * optional extention header. The number of entries must match * ext_headers_type. */ XrlCmdError raw_packet6_client_0_1_recv( // Input values, const string& if_name, const string& vif_name, const IPv6& src_address, const IPv6& dst_address, const uint32_t& ip_protocol, const int32_t& ip_ttl, const int32_t& ip_tos, const bool& ip_router_alert, const bool& ip_internet_control, const XrlAtomList& ext_headers_type, const XrlAtomList& ext_headers_payload, const vector& payload); /** * Enable/disable/start/stop a MLD6IGMP vif interface. * * @param vif_name the name of the vif to enable/disable/start/stop. * * @param enable if true, then enable the vif, otherwise disable it. */ XrlCmdError mld6igmp_0_1_enable_vif( // Input values, const string& vif_name, const bool& enable); XrlCmdError mld6igmp_0_1_start_vif( // Input values, const string& vif_name); XrlCmdError mld6igmp_0_1_stop_vif( // Input values, const string& vif_name); /** * Enable/disable/start/stop all MLD6IGMP vif interfaces. * * @param enable if true, then enable the vifs, otherwise disable them. */ XrlCmdError mld6igmp_0_1_enable_all_vifs( // Input values, const bool& enable); XrlCmdError mld6igmp_0_1_start_all_vifs(); XrlCmdError mld6igmp_0_1_stop_all_vifs(); /** * Enable/disable/start/stop the MLD6IGMP protocol. * * @param enable if true, then enable the MLD6IGMP protocol, otherwise * disable it. */ XrlCmdError mld6igmp_0_1_enable_mld6igmp( // Input values, const bool& enable); XrlCmdError mld6igmp_0_1_start_mld6igmp(); XrlCmdError mld6igmp_0_1_stop_mld6igmp(); /** * Enable/disable/start/stop the MLD6IGMP CLI access. * * @param enable if true, then enable the MLD6IGMP CLI access, otherwise * disable it. */ XrlCmdError mld6igmp_0_1_enable_cli( // Input values, const bool& enable); XrlCmdError mld6igmp_0_1_start_cli(); XrlCmdError mld6igmp_0_1_stop_cli(); /** * Get the configured protocol version per interface. * * @param vif_name the name of the vif to apply to. * * @param proto_version the protocol version. */ XrlCmdError mld6igmp_0_1_get_vif_proto_version( // Input values, const string& vif_name, // Output values, uint32_t& proto_version); /** * Set the protocol version per interface. * * @param vif_name the name of the vif to apply to. * * @param proto_version the protocol version. */ XrlCmdError mld6igmp_0_1_set_vif_proto_version( // Input values, const string& vif_name, const uint32_t& proto_version); /** * Reset the protocol version per interface to its default value. * * @param vif_name the name of the vif to apply to. */ XrlCmdError mld6igmp_0_1_reset_vif_proto_version( // Input values, const string& vif_name); /** * Get the IP Router Alert option check per interface for received * packets. * * @param vif_name the name of the vif to apply to. * * @param enabled if true, then the IP Router Alert option check was * enabled, otherwise it was disabled. */ XrlCmdError mld6igmp_0_1_get_vif_ip_router_alert_option_check( // Input values, const string& vif_name, // Output values, bool& enabled); /** * Set the IP Router Alert option check per interface for received * packets. * * @param vif_name the name of the vif to apply to. * * @param enable if true, then enable the IP Router Alert option check, * otherwise disable it. */ XrlCmdError mld6igmp_0_1_set_vif_ip_router_alert_option_check( // Input values, const string& vif_name, const bool& enable); /** * Reset the IP Router Alert option check per interface for received * packets to its default value. * * @param vif_name the name of the vif to apply to. */ XrlCmdError mld6igmp_0_1_reset_vif_ip_router_alert_option_check( // Input values, const string& vif_name); /** * Get the Query Interval per interface. * * @param vif_name the name of the vif to apply to. * * @param interval_sec the number of seconds in the interval. * * @param interval_usec the number of microseconds (in addition to * interval_sec) in the interval. */ XrlCmdError mld6igmp_0_1_get_vif_query_interval( // Input values, const string& vif_name, // Output values, uint32_t& interval_sec, uint32_t& interval_usec); /** * Set the Query Interval per interface. * * @param vif_name the name of the vif to apply to. * * @param interval_sec the number of seconds in the interval. * * @param interval_usec the number of microseconds (in addition to * interval_sec) in the interval. */ XrlCmdError mld6igmp_0_1_set_vif_query_interval( // Input values, const string& vif_name, const uint32_t& interval_sec, const uint32_t& interval_usec); /** * Reset the Query Interval per interface to its default value. * * @param vif_name the name of the vif to apply to. */ XrlCmdError mld6igmp_0_1_reset_vif_query_interval( // Input values, const string& vif_name); /** * Get the Last Member Query Interval per interface. * * @param vif_name the name of the vif to apply to. * * @param interval_sec the number of seconds in the interval. * * @param interval_usec the number of microseconds (in addition to * interval_sec) in the interval. */ XrlCmdError mld6igmp_0_1_get_vif_query_last_member_interval( // Input values, const string& vif_name, // Output values, uint32_t& interval_sec, uint32_t& interval_usec); /** * Set the Last Member Query Interval per interface. * * @param vif_name the name of the vif to apply to. * * @param interval_sec the number of seconds in the interval. * * @param interval_usec the number of microseconds (in addition to * interval_sec) in the interval. */ XrlCmdError mld6igmp_0_1_set_vif_query_last_member_interval( // Input values, const string& vif_name, const uint32_t& interval_sec, const uint32_t& interval_usec); /** * Reset the Last Member Query Interval per interface to its default * value. * * @param vif_name the name of the vif to apply to. */ XrlCmdError mld6igmp_0_1_reset_vif_query_last_member_interval( // Input values, const string& vif_name); /** * Get the Query Response Interval per interface. * * @param vif_name the name of the vif to apply to. * * @param interval_sec the number of seconds in the interval. * * @param interval_usec the number of microseconds (in addition to * interval_sec) in the interval. */ XrlCmdError mld6igmp_0_1_get_vif_query_response_interval( // Input values, const string& vif_name, // Output values, uint32_t& interval_sec, uint32_t& interval_usec); /** * Set the Query Response Interval per interface. * * @param vif_name the name of the vif to apply to. * * @param interval_sec the number of seconds in the interval. * * @param interval_usec the number of microseconds (in addition to * interval_sec) in the interval. */ XrlCmdError mld6igmp_0_1_set_vif_query_response_interval( // Input values, const string& vif_name, const uint32_t& interval_sec, const uint32_t& interval_usec); /** * Reset the Query Response Interval per interface to its default value. * * @param vif_name the name of the vif to apply to. */ XrlCmdError mld6igmp_0_1_reset_vif_query_response_interval( // Input values, const string& vif_name); /** * Get the Robustness Variable count per interface. * * @param vif_name the name of the vif to apply to. * * @param robust_count the count value. */ XrlCmdError mld6igmp_0_1_get_vif_robust_count( // Input values, const string& vif_name, // Output values, uint32_t& robust_count); /** * Set the Robustness Variable count per interface. * * @param vif_name the name of the vif to apply to. * * @param robust_count the count value. */ XrlCmdError mld6igmp_0_1_set_vif_robust_count( // Input values, const string& vif_name, const uint32_t& robust_count); /** * Reset the Robustness Variable count per interface to its default value. * * @param vif_name the name of the vif to apply to. */ XrlCmdError mld6igmp_0_1_reset_vif_robust_count( // Input values, const string& vif_name); /** * Enable/disable the MLD6IGMP trace log for all operations. * * @param enable if true, then enable the trace log, otherwise disable it. */ XrlCmdError mld6igmp_0_1_log_trace_all( // Input values, const bool& enable); /** * Add/delete a client protocol in the MLD/IGMP protocol. * * @param xrl_sender_name the XRL name of the originator of this XRL. * * @param protocol_name the name of the protocol to add/delete. * * @param protocol_id the ID of the protocol to add/delete (both sides * must agree on the particular values). * * @param vif_name the name of the vif the protocol add/delete to apply * to. * * @param vif_index the index of the vif the protocol add/delete to apply * to. The added protocol will receive Join/Leave membership information * about same-LAN members for the particular vif. */ XrlCmdError mld6igmp_0_1_add_protocol4( // Input values, const string& xrl_sender_name, const string& protocol_name, const uint32_t& protocol_id, const string& vif_name, const uint32_t& vif_index); XrlCmdError mld6igmp_0_1_add_protocol6( // Input values, const string& xrl_sender_name, const string& protocol_name, const uint32_t& protocol_id, const string& vif_name, const uint32_t& vif_index); XrlCmdError mld6igmp_0_1_delete_protocol4( // Input values, const string& xrl_sender_name, const string& protocol_name, const uint32_t& protocol_id, const string& vif_name, const uint32_t& vif_index); XrlCmdError mld6igmp_0_1_delete_protocol6( // Input values, const string& xrl_sender_name, const string& protocol_name, const uint32_t& protocol_id, const string& vif_name, const uint32_t& vif_index); private: class XrlTaskBase; const ServiceBase* ifmgr_mirror_service_base() const { return dynamic_cast(&_ifmgr); } const IfMgrIfTree& ifmgr_iftree() const { return _ifmgr.iftree(); } /** * Called when Finder connection is established. * * Note that this method overwrites an XrlRouter virtual method. */ virtual void finder_connect_event(); /** * Called when Finder disconnect occurs. * * Note that this method overwrites an XrlRouter virtual method. */ virtual void finder_disconnect_event(); // // Methods to handle the XRL tasks // void add_task(XrlTaskBase* xrl_task); void send_xrl_task(); void pop_xrl_task(); void retry_xrl_task(); void fea_register_startup(); void mfea_register_startup(); void fea_register_shutdown(); void mfea_register_shutdown(); void finder_send_register_unregister_interest_cb(const XrlError& xrl_error); // // Protocol node methods // int register_receiver(const string& if_name, const string& vif_name, uint8_t ip_protocol, bool enable_multicast_loopback); int unregister_receiver(const string& if_name, const string& vif_name, uint8_t ip_protocol); void fea_client_send_register_unregister_receiver_cb(const XrlError& xrl_error); int join_multicast_group(const string& if_name, const string& vif_name, uint8_t ip_protocol, const IPvX& group_address); int leave_multicast_group(const string& if_name, const string& vif_name, uint8_t ip_protocol, const IPvX& group_address); void fea_client_send_join_leave_multicast_group_cb(const XrlError& xrl_error); int proto_send(const string& if_name, const string& vif_name, const IPvX& src_address, const IPvX& dst_address, uint8_t ip_protocol, int32_t ip_ttl, int32_t ip_tos, bool ip_router_alert, bool ip_internet_control, const uint8_t* sndbuf, size_t sndlen, string& error_msg); void fea_client_send_protocol_message_cb(const XrlError& xrl_error); int send_add_membership(const string& dst_module_instance_name, xorp_module_id dst_module_id, uint32_t vif_index, const IPvX& source, const IPvX& group); int send_delete_membership(const string& dst_module_instance_name, xorp_module_id dst_module_id, uint32_t vif_index, const IPvX& source, const IPvX& group); void send_add_delete_membership(); void mld6igmp_client_send_add_delete_membership_cb(const XrlError& xrl_error); // // Protocol node CLI methods // int add_cli_command_to_cli_manager(const char *command_name, const char *command_help, bool is_command_cd, const char *command_cd_prompt, bool is_command_processor); void cli_manager_client_send_add_cli_command_cb(const XrlError& xrl_error); int delete_cli_command_from_cli_manager(const char *command_name); void cli_manager_client_send_delete_cli_command_cb(const XrlError& xrl_error); int family() const { return (Mld6igmpNode::family()); } /** * A base class for handling tasks for sending XRL requests. */ class XrlTaskBase { public: XrlTaskBase(XrlMld6igmpNode& xrl_mld6igmp_node) : _xrl_mld6igmp_node(xrl_mld6igmp_node) {} virtual ~XrlTaskBase() {} virtual void dispatch() = 0; virtual const char* operation_name() const = 0; protected: XrlMld6igmpNode& _xrl_mld6igmp_node; private: }; /** * Class for handling the task to register/unregister interest * in the FEA or MFEA with the Finder. */ class RegisterUnregisterInterest : public XrlTaskBase { public: RegisterUnregisterInterest(XrlMld6igmpNode& xrl_mld6igmp_node, const string& target_name, bool is_register) : XrlTaskBase(xrl_mld6igmp_node), _target_name(target_name), _is_register(is_register) {} void dispatch() { _xrl_mld6igmp_node.send_register_unregister_interest(); } const char* operation_name() const { return ((_is_register)? "register" : "unregister"); } const string& target_name() const { return _target_name; } bool is_register() const { return _is_register; } private: string _target_name; bool _is_register; }; /** * Class for handling the task to register/unregister with the FEA * as a receiver on an interface. */ class RegisterUnregisterReceiver : public XrlTaskBase { public: RegisterUnregisterReceiver(XrlMld6igmpNode& xrl_mld6igmp_node, const string& if_name, const string& vif_name, uint8_t ip_protocol, bool enable_multicast_loopback, bool is_register) : XrlTaskBase(xrl_mld6igmp_node), _if_name(if_name), _vif_name(vif_name), _ip_protocol(ip_protocol), _enable_multicast_loopback(enable_multicast_loopback), _is_register(is_register) {} void dispatch() { _xrl_mld6igmp_node.send_register_unregister_receiver(); } const char* operation_name() const { return ((_is_register)? "register" : "unregister"); } const string& if_name() const { return _if_name; } const string& vif_name() const { return _vif_name; } uint8_t ip_protocol() const { return _ip_protocol; } bool enable_multicast_loopback() const { return _enable_multicast_loopback; } bool is_register() const { return _is_register; } private: string _if_name; string _vif_name; uint8_t _ip_protocol; bool _enable_multicast_loopback; bool _is_register; }; /** * Class for handling the task of join/leave multicast group requests */ class JoinLeaveMulticastGroup : public XrlTaskBase { public: JoinLeaveMulticastGroup(XrlMld6igmpNode& xrl_mld6igmp_node, const string& if_name, const string& vif_name, uint8_t ip_protocol, const IPvX& group_address, bool is_join) : XrlTaskBase(xrl_mld6igmp_node), _if_name(if_name), _vif_name(vif_name), _ip_protocol(ip_protocol), _group_address(group_address), _is_join(is_join) {} void dispatch() { _xrl_mld6igmp_node.send_join_leave_multicast_group(); } const char* operation_name() const { return ((_is_join)? "join" : "leave"); } const string& if_name() const { return _if_name; } const string& vif_name() const { return _vif_name; } uint8_t ip_protocol() const { return _ip_protocol; } const IPvX& group_address() const { return _group_address; } bool is_join() const { return _is_join; } private: string _if_name; string _vif_name; uint8_t _ip_protocol; IPvX _group_address; bool _is_join; }; /** * Class for handling the task of sending protocol messages */ class SendProtocolMessage : public XrlTaskBase { public: SendProtocolMessage(XrlMld6igmpNode& xrl_mld6igmp_node, const string& if_name, const string& vif_name, const IPvX& src_address, const IPvX& dst_address, uint8_t ip_protocol, int32_t ip_ttl, int32_t ip_tos, bool ip_router_alert, bool ip_internet_control, const uint8_t* sndbuf, size_t sndlen) : XrlTaskBase(xrl_mld6igmp_node), _if_name(if_name), _vif_name(vif_name), _src_address(src_address), _dst_address(dst_address), _ip_protocol(ip_protocol), _ip_ttl(ip_ttl), _ip_tos(ip_tos), _ip_router_alert(ip_router_alert), _ip_internet_control(ip_internet_control) { _payload.resize(sndlen); for (size_t i = 0; i < sndlen; i++) _payload[i] = sndbuf[i]; } void dispatch() { _xrl_mld6igmp_node.send_protocol_message(); } const char* operation_name() const { return ("send"); } const string& if_name() const { return _if_name; } const string& vif_name() const { return _vif_name; } const IPvX& src_address() const { return _src_address; } const IPvX& dst_address() const { return _dst_address; } uint8_t ip_protocol() const { return _ip_protocol; } int32_t ip_ttl() const { return _ip_ttl; } int32_t ip_tos() const { return _ip_tos; } bool ip_router_alert() const { return _ip_router_alert; } bool ip_internet_control() const { return _ip_internet_control; } const vector& payload() const { return _payload; } private: string _if_name; string _vif_name; IPvX _src_address; IPvX _dst_address; uint8_t _ip_protocol; int32_t _ip_ttl; int32_t _ip_tos; bool _ip_router_alert; bool _ip_internet_control; vector _payload; }; /** * Class for handling the queue of sending Add/Delete membership requests */ class SendAddDeleteMembership { public: SendAddDeleteMembership(const string& dst_module_instance_name, xorp_module_id dst_module_id, uint32_t vif_index, const IPvX& source, const IPvX& group, bool is_add) : _dst_module_instance_name(dst_module_instance_name), _dst_module_id(dst_module_id), _vif_index(vif_index), _source(source), _group(group), _is_add(is_add) {} const char* operation_name() const { return ((_is_add)? "add membership" : "delete membership"); } const string& dst_module_instance_name() const { return _dst_module_instance_name; } xorp_module_id dst_module_id() const { return _dst_module_id; } uint32_t vif_index() const { return _vif_index; } const IPvX& source() const { return _source; } const IPvX& group() const { return _group; } bool is_add() const { return _is_add; } private: string _dst_module_instance_name; xorp_module_id _dst_module_id; uint32_t _vif_index; IPvX _source; IPvX _group; bool _is_add; }; EventLoop& _eventloop; const string _finder_target; const string _fea_target; const string _mfea_target; IfMgrXrlMirror _ifmgr; XrlRawPacket4V0p1Client _xrl_fea_client4; XrlRawPacket6V0p1Client _xrl_fea_client6; XrlMld6igmpClientV0p1Client _xrl_mld6igmp_client_client; XrlCliManagerV0p1Client _xrl_cli_manager_client; XrlFinderEventNotifierV0p1Client _xrl_finder_client; static const TimeVal RETRY_TIMEVAL; bool _is_finder_alive; bool _is_fea_alive; bool _is_fea_registered; bool _is_mfea_alive; bool _is_mfea_registered; list _xrl_tasks_queue; XorpTimer _xrl_tasks_queue_timer; list _send_add_delete_membership_queue; XorpTimer _send_add_delete_membership_queue_timer; }; #endif // __MLD6IGMP_XRL_MLD6IGMP_NODE_HH__ xorp/contrib/mld6igmp_lite/xorp_mld.cc0000664000076400007640000001235111421137511020205 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // XORP MLD/IGMP module implementation. // #include "mld6igmp_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/callback.hh" #include "libxorp/eventloop.hh" #include "libxorp/exceptions.hh" #include "xrl_mld6igmp_node.hh" #ifdef HAVE_GETOPT_H #include #endif // // Local functions prototypes // static void usage(const char *argv0, int exit_value); /** * usage: * @argv0: Argument 0 when the program was called (the program name itself). * @exit_value: The exit value of the program. * * Print the program usage. * If @exit_value is 0, the usage will be printed to the standart output, * otherwise to the standart error. **/ static void usage(const char *argv0, int exit_value) { FILE *output; const char *progname = strrchr(argv0, '/'); if (progname != NULL) progname++; // Skip the last '/' if (progname == NULL) progname = argv0; // // If the usage is printed because of error, output to stderr, otherwise // output to stdout. // if (exit_value == 0) output = stdout; else output = stderr; fprintf(output, "Usage: %s [-F [:]]\n", progname); fprintf(output, " -F [:] : finder hostname and port\n"); fprintf(output, " -h : usage (this message)\n"); fprintf(output, "\n"); fprintf(output, "Program name: %s\n", progname); fprintf(output, "Module name: %s\n", XORP_MODULE_NAME); fprintf(output, "Module version: %s\n", XORP_MODULE_VERSION); exit (exit_value); // NOTREACHED } static void mld6igmp_main(const string& finder_hostname, uint16_t finder_port) { #ifdef HAVE_IPV6 // // Init stuff // EventLoop eventloop; // // MLD6IGMP node // XrlMld6igmpNode xrl_mld6igmp_node6(AF_INET6, XORP_MODULE_MLD6IGMP, eventloop, xorp_module_name(AF_INET6, XORP_MODULE_MLD6IGMP), finder_hostname, finder_port, "finder", xorp_module_name(AF_INET6, XORP_MODULE_FEA), xorp_module_name(AF_INET6, XORP_MODULE_MFEA)); wait_until_xrl_router_is_ready(eventloop, xrl_mld6igmp_node6.xrl_router()); // // Startup // #ifdef HAVE_IPV6_MULTICAST xrl_mld6igmp_node6.enable_mld6igmp(); // xrl_mld6igmp_node6.startup(); xrl_mld6igmp_node6.enable_cli(); xrl_mld6igmp_node6.start_cli(); #endif // // Main loop // #ifdef HAVE_IPV6_MULTICAST while (! xrl_mld6igmp_node6.is_done()) { eventloop.run(); } while (xrl_mld6igmp_node6.xrl_router().pending()) { eventloop.run(); } #endif // HAVE_IPV6_MULTICAST #endif // HAVE_IPV6 UNUSED(finder_hostname); UNUSED(finder_port); } int main(int argc, char *argv[]) { int ch; string::size_type idx; const char *argv0 = argv[0]; string finder_hostname = FinderConstants::FINDER_DEFAULT_HOST().str(); uint16_t finder_port = FinderConstants::FINDER_DEFAULT_PORT(); // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); // // Get the program options // while ((ch = getopt(argc, argv, "F:h")) != -1) { switch (ch) { case 'F': // Finder hostname and port finder_hostname = optarg; idx = finder_hostname.find(':'); if (idx != string::npos) { if (idx + 1 >= finder_hostname.length()) { // No port number usage(argv0, 1); // NOTREACHED } char* p = &finder_hostname[idx + 1]; finder_port = static_cast(atoi(p)); finder_hostname = finder_hostname.substr(0, idx); } break; case 'h': case '?': // Help usage(argv0, 0); // NOTREACHED break; default: usage(argv0, 1); // NOTREACHED break; } } argc -= optind; argv += optind; if (argc != 0) { usage(argv0, 1); // NOTREACHED } // // Run everything // try { mld6igmp_main(finder_hostname, finder_port); } catch(...) { xorp_catch_standard_exceptions(); } // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); exit (0); } xorp/contrib/mld6igmp_lite/mld6igmp_group_record.cc0000664000076400007640000011465211421137511022661 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // The Lightweight IGMP/MLD modifications to this file are copyrighted by: // // Copyright (c) 2008 Huawei Technologies Co. Ltd // // // Multicast group record information used by // IGMPv1 and IGMPv2 (RFC 2236), IGMPv3 (RFC 3376), // MLDv1 (RFC 2710), and MLDv2 (RFC 3810). // #include "mld6igmp_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvx.hh" #include "mld6igmp_group_record.hh" #include "mld6igmp_node.hh" #include "mld6igmp_vif.hh" // // Exported variables // // // Local constants definitions // // // Local structures/classes, typedefs and macros // // // Local variables // // // Local functions prototypes // /** * Mld6igmpGroupRecord::Mld6igmpGroupRecord: * @mld6igmp_vif: The vif interface this entry belongs to. * @group: The entry group address. * * Return value: **/ Mld6igmpGroupRecord::Mld6igmpGroupRecord(Mld6igmpVif& mld6igmp_vif, const IPvX& group) : _mld6igmp_vif(mld6igmp_vif), _group(group), _do_forward_sources(*this), _last_reported_host(IPvX::ZERO(family())), _query_retransmission_count(0) { } /** * Mld6igmpGroupRecord::~Mld6igmpGroupRecord: * @: * * Mld6igmpGroupRecord destructor. **/ Mld6igmpGroupRecord::~Mld6igmpGroupRecord() { _do_forward_sources.delete_payload_and_clear(); } /** * Get the corresponding event loop. * * @return the corresponding event loop. */ EventLoop& Mld6igmpGroupRecord::eventloop() { return (_mld6igmp_vif.mld6igmp_node().eventloop()); } /** * Find a source that should be forwarded. * * @param source the source address. * @return the corresponding source record (@ref Mld6igmpSourceRecord) * if found, otherwise NULL. */ Mld6igmpSourceRecord* Mld6igmpGroupRecord::find_do_forward_source(const IPvX& source) { return (_do_forward_sources.find_source_record(source)); } /** * Test whether the entry is unused. * * @return true if the entry is unused, otherwise false. */ bool Mld6igmpGroupRecord::is_unused() const { if (is_include_mode()) { if (_do_forward_sources.empty()) { return (true); } return (false); } if (is_asm_mode()) { // // XXX: the group timer must be running in EXCLUDE mode, // otherwise there must have been transition to INCLUDE mode. // if (_group_timer.scheduled()) return (false); XLOG_ASSERT(_do_forward_sources.empty()); return (true); } XLOG_UNREACHABLE(); return (true); } /** * Process MODE_IS_INCLUDE report. * * @param sources the source addresses. * @param last_reported_host the address of the host that last reported * as member. */ void Mld6igmpGroupRecord::process_mode_is_include(const set& sources, const IPvX& last_reported_host) { bool old_is_include_mode = is_include_mode(); set old_do_forward_sources = _do_forward_sources.extract_source_addresses(); set_last_reported_host(last_reported_host); if (is_include_mode()) { // // Router State: INCLUDE (A) // Report Received: IS_IN (B) // New Router State: INCLUDE (A + B) // Actions: (B) = GMI // Mld6igmpSourceSet& a = _do_forward_sources; const set& b = sources; TimeVal gmi = _mld6igmp_vif.group_membership_interval(); _do_forward_sources = a + b; // (A + B) _do_forward_sources.set_source_timer(b, gmi); // (B) = GMI calculate_forwarding_changes(old_is_include_mode, old_do_forward_sources); return; } if (is_asm_mode()) { // // Router State: EXCLUDE (X, Y) // Report Received: IS_IN (A) // New Router State: EXCLUDE (X + A, Y - A) // Actions: (A) = GMI // Mld6igmpSourceSet& x = _do_forward_sources; const set& a = sources; TimeVal gmi = _mld6igmp_vif.group_membership_interval(); // XXX: first transfer (Y * A) from (Y) to (X) _do_forward_sources = x + a; // (X + A) _do_forward_sources.set_source_timer(a, gmi); // (A) = GMI calculate_forwarding_changes(old_is_include_mode, old_do_forward_sources); return; } } /** * Process MODE_IS_EXCLUDE report. * * @param sources the source addresses. * @param last_reported_host the address of the host that last reported * as member. */ void Mld6igmpGroupRecord::process_mode_is_exclude(const set& sources, const IPvX& last_reported_host) { bool old_is_include_mode = is_include_mode(); set old_do_forward_sources = _do_forward_sources.extract_source_addresses(); set_last_reported_host(last_reported_host); if (! sources.empty()) { XLOG_WARNING("Non-empty list of sources when processing mode is EXCLUDE"); } if (is_include_mode()) { // // Router State: INCLUDE (A) // Report Received: IS_EX (B) // New Router State: EXCLUDE (A * B, B - A) // Actions: (B - A) = 0 // Delete (A - B) // Group Timer = GMI // TimeVal gmi = _mld6igmp_vif.group_membership_interval(); _group_timer = eventloop().new_oneoff_after( gmi, callback(this, &Mld6igmpGroupRecord::group_timer_timeout)); calculate_forwarding_changes(old_is_include_mode, old_do_forward_sources); return; } if (is_asm_mode()) { // // Router State: EXCLUDE (X, Y) // Report Received: IS_EX (A) // New Router State: EXCLUDE (A - Y, Y * A) // Actions: (A - X - Y) = GMI // Delete (X - A) // Delete (Y - A) // Group Timer = GMI // TimeVal gmi = _mld6igmp_vif.group_membership_interval(); _group_timer = eventloop().new_oneoff_after( gmi, callback(this, &Mld6igmpGroupRecord::group_timer_timeout)); calculate_forwarding_changes(old_is_include_mode, old_do_forward_sources); return; } } /** * Process CHANGE_TO_INCLUDE_MODE report. * * @param sources the source addresses. * @param last_reported_host the address of the host that last reported * as member. */ void Mld6igmpGroupRecord::process_change_to_include_mode(const set& sources, const IPvX& last_reported_host) { bool old_is_include_mode = is_include_mode(); set old_do_forward_sources = _do_forward_sources.extract_source_addresses(); string dummy_error_msg; set_last_reported_host(last_reported_host); if (is_include_mode()) { // // Router State: INCLUDE (A) // Report Received: TO_IN (B) // New Router State: INCLUDE (A + B) // Actions: (B) = GMI // Send Q(G, A - B) // Mld6igmpSourceSet& a = _do_forward_sources; const set& b = sources; TimeVal gmi = _mld6igmp_vif.group_membership_interval(); Mld6igmpSourceSet a_minus_b = a - b; // A - B _do_forward_sources = a + b; // A + B _do_forward_sources.set_source_timer(b, gmi); // (B) = GMI // Send Q(G, A - B) with a_minus_b _mld6igmp_vif.mld6igmp_group_source_query_send( group(), a_minus_b.extract_source_addresses(), dummy_error_msg); calculate_forwarding_changes(old_is_include_mode, old_do_forward_sources); return; } if (is_asm_mode()) { // // Router State: EXCLUDE (X, Y) // Report Received: TO_IN (A) // New Router State: EXCLUDE (X + A, Y - A) // Actions: (A) = GMI // Send Q(G, X - A) // Send Q(G) // Mld6igmpSourceSet& x = _do_forward_sources; const set& a = sources; TimeVal gmi = _mld6igmp_vif.group_membership_interval(); Mld6igmpSourceSet x_minus_a = x - a; // X - A // XXX: first transfer (Y * A) from (Y) to (X) _do_forward_sources = x + a; // X + A _do_forward_sources.set_source_timer(a, gmi); // (A) = GMI // Send Q(G, X - A) with x_minus_a _mld6igmp_vif.mld6igmp_group_source_query_send( group(), x_minus_a.extract_source_addresses(), dummy_error_msg); // Send Q(G) _mld6igmp_vif.mld6igmp_group_query_send(group(), dummy_error_msg); calculate_forwarding_changes(old_is_include_mode, old_do_forward_sources); return; } } /** * Process CHANGE_TO_EXCLUDE_MODE report. * * @param sources the source addresses. * @param last_reported_host the address of the host that last reported * as member. */ void Mld6igmpGroupRecord::process_change_to_exclude_mode(const set& sources, const IPvX& last_reported_host) { bool old_is_include_mode = is_include_mode(); set old_do_forward_sources = _do_forward_sources.extract_source_addresses(); string dummy_error_msg; if (! sources.empty()) { XLOG_WARNING("Non-empty list of sources when processing change to EXCLUDE"); } set_last_reported_host(last_reported_host); if (is_include_mode()) { // // Router State: INCLUDE (A) // Report Received: TO_EX (B) // New Router State: EXCLUDE (A * B, B - A) // Actions: (B - A) = 0 // Delete (A - B) // Send Q(G, A * B) // Group Timer = GMI // TimeVal gmi = _mld6igmp_vif.group_membership_interval(); _group_timer = eventloop().new_oneoff_after( gmi, callback(this, &Mld6igmpGroupRecord::group_timer_timeout)); // Send Q(G, A * B) with _do_forward_sources _mld6igmp_vif.mld6igmp_group_source_query_send( group(), _do_forward_sources.extract_source_addresses(), dummy_error_msg); calculate_forwarding_changes(old_is_include_mode, old_do_forward_sources); return; } if (is_asm_mode()) { // // Router State: EXCLUDE (X, Y) // Report Received: TO_EX (A) // New Router State: EXCLUDE (A - Y, Y * A) // Actions: (A - X - Y) = Group Timer // Delete (X - A) // Delete (Y - A) // Send Q(G, A - Y) // Group Timer = GMI // TimeVal gmi = _mld6igmp_vif.group_membership_interval(); TimeVal gt; _group_timer.time_remaining(gt); _group_timer = eventloop().new_oneoff_after( gmi, callback(this, &Mld6igmpGroupRecord::group_timer_timeout)); calculate_forwarding_changes(old_is_include_mode, old_do_forward_sources); return; } } /** * Process ALLOW_NEW_SOURCES report. * * @param sources the source addresses. * @param last_reported_host the address of the host that last reported * as member. */ void Mld6igmpGroupRecord::process_allow_new_sources(const set& sources, const IPvX& last_reported_host) { bool old_is_include_mode = is_include_mode(); set old_do_forward_sources = _do_forward_sources.extract_source_addresses(); set_last_reported_host(last_reported_host); if (is_include_mode()) { // // Router State: INCLUDE (A) // Report Received: ALLOW (B) // New Router State: INCLUDE (A + B) // Actions: (B) = GMI // Mld6igmpSourceSet& a = _do_forward_sources; const set& b = sources; TimeVal gmi = _mld6igmp_vif.group_membership_interval(); _do_forward_sources = a + b; // A + B _do_forward_sources.set_source_timer(b, gmi); // (B) = GMI calculate_forwarding_changes(old_is_include_mode, old_do_forward_sources); return; } if (is_asm_mode()) { // // Router State: EXCLUDE (X, Y) // Report Received: ALLOW (A) // New Router State: EXCLUDE (X + A, Y - A) // Actions: (A) = GMI // Mld6igmpSourceSet& x = _do_forward_sources; const set& a = sources; TimeVal gmi = _mld6igmp_vif.group_membership_interval(); // XXX: first transfer (Y * A) from (Y) to (X) _do_forward_sources = x + a; // X + A _do_forward_sources.set_source_timer(a, gmi); // (A) = GMI calculate_forwarding_changes(old_is_include_mode, old_do_forward_sources); return; } } /** * Process BLOCK_OLD_SOURCES report. * * @param sources the source addresses. * @param last_reported_host the address of the host that last reported * as member. */ void Mld6igmpGroupRecord::process_block_old_sources(const set& sources, const IPvX& last_reported_host) { bool old_is_include_mode = is_include_mode(); set old_do_forward_sources = _do_forward_sources.extract_source_addresses(); string dummy_error_msg; set_last_reported_host(last_reported_host); if (is_include_mode()) { // // Router State: INCLUDE (A) // Report Received: BLOCK (B) // New Router State: INCLUDE (A) // Actions: Send Q(G, A * B) // Mld6igmpSourceSet& a = _do_forward_sources; const set& b = sources; Mld6igmpSourceSet a_and_b = a * b; // Send Q(G, A * B) with a_and_b _mld6igmp_vif.mld6igmp_group_source_query_send( group(), a_and_b.extract_source_addresses(), dummy_error_msg); calculate_forwarding_changes(old_is_include_mode, old_do_forward_sources); return; } if (is_asm_mode()) { // // Router State: EXCLUDE (X, Y) // Report Received: BLOCK (A) // New Router State: EXCLUDE (X + (A - Y), Y) // Actions: (A - X - Y) = Group Timer // Send Q(G, A - Y) // Mld6igmpSourceSet& a = _do_forward_sources; TimeVal gt; _group_timer.time_remaining(gt); const set& b = sources; Mld6igmpSourceSet a_and_b= a * b; // Send Q(G, A - Y) with a_minus_y _mld6igmp_vif.mld6igmp_group_source_query_send( group(), a_and_b.extract_source_addresses(), dummy_error_msg); calculate_forwarding_changes(old_is_include_mode, old_do_forward_sources); return; } } /** * Lower the group timer. * * @param timeval the timeout interval the timer should be lowered to. */ void Mld6igmpGroupRecord::lower_group_timer(const TimeVal& timeval) { TimeVal timeval_remaining; // // Lower the group timer // _group_timer.time_remaining(timeval_remaining); if (timeval < timeval_remaining) { _group_timer = eventloop().new_oneoff_after( timeval, callback(this, &Mld6igmpGroupRecord::group_timer_timeout)); } } /** * Lower the source timer for a set of sources. * * @param sources the source addresses. * @param timeval the timeout interval the timer should be lowered to. */ void Mld6igmpGroupRecord::lower_source_timer(const set& sources, const TimeVal& timeval) { // // Lower the source timer // _do_forward_sources.lower_source_timer(sources, timeval); } /** * Take the appropriate actions for a source that has expired. * * @param source_record the source record that has expired. */ void Mld6igmpGroupRecord::source_expired(Mld6igmpSourceRecord* source_record) { Mld6igmpSourceSet::iterator iter; // Erase the source record from the appropriate source set iter = _do_forward_sources.find(source_record->source()); XLOG_ASSERT(iter != _do_forward_sources.end()); _do_forward_sources.erase(iter); if (is_include_mode()) { // notify routing (-) mld6igmp_vif().join_prune_notify_routing(source_record->source(), group(), ACTION_PRUNE); // Delete the source record delete source_record; // If no more source records, then delete the group record if (_do_forward_sources.empty()) { mld6igmp_vif().group_records().erase(group()); delete this; } return; } if (is_asm_mode()) { // notify routing (-) // // XXX: Note that we send a PRUNE twice: the first one to remove the // original JOIN for the source, and the second one to create // PRUNE state for the source. // mld6igmp_vif().join_prune_notify_routing(source_record->source(), group(), ACTION_PRUNE); delete source_record; return; } } /** * Get the number of seconds until the group timer expires. * * @return the number of seconds until the group timer expires. */ uint32_t Mld6igmpGroupRecord::timeout_sec() const { TimeVal tv; _group_timer.time_remaining(tv); return (tv.sec()); } /** * Timeout: the group timer has expired. */ void Mld6igmpGroupRecord::group_timer_timeout() { if (is_include_mode()) { // XXX: Nothing to do when in INCLUDE mode. if (_do_forward_sources.empty()) { mld6igmp_vif().join_prune_notify_routing(IPvX::ZERO(family()), group(), ACTION_PRUNE); mld6igmp_vif().group_records().erase(group()); delete this; } else { mld6igmp_vif().join_prune_notify_routing(IPvX::ZERO(family()), group(), ACTION_PRUNE); } return; } if (is_asm_mode()) { // notify routing (-) mld6igmp_vif().join_prune_notify_routing(IPvX::ZERO(family()), group(), ACTION_PRUNE); if (! _do_forward_sources.empty()) { // Transition to INCLUDE mode return; } // // No sources with running source timers. // Delete the group record and return immediately. // mld6igmp_vif().group_records().erase(group()); delete this; return; } } /** * Schedule periodic Group-Specific and Group-and-Source-Specific Query * retransmission. * * If the sources list is empty, we schedule Group-Specific Query, * otherwise we schedule Group-and-Source-Specific Query. * * @param sources the source addresses. */ void Mld6igmpGroupRecord::schedule_periodic_group_query(const set& sources) { Mld6igmpSourceSet::iterator source_iter; size_t count = _mld6igmp_vif.last_member_query_count() - 1; if (_mld6igmp_vif.last_member_query_count() == 0) return; if (_mld6igmp_vif.query_last_member_interval().get() == TimeVal::ZERO()) return; // // Set the count for query retransmissions // if (sources.empty()) { // // Set the count for Group-Specific Query retransmission // _query_retransmission_count = count; } else { // // Set the count for Group-and-Source-Specific Query retransmission // set::const_iterator ipvx_iter; for (ipvx_iter = sources.begin(); ipvx_iter != sources.end(); ++ipvx_iter) { const IPvX& ipvx = *ipvx_iter; Mld6igmpSourceRecord* source_record = find_do_forward_source(ipvx); if (source_record == NULL) continue; source_record->set_query_retransmission_count(count); } } // // Set the periodic timer for SSM Group-Specific and // Group-and-Source-Specific Queries. // // Note that we set the timer only if it wasn't running already. // if (! _group_query_timer.scheduled()) { _group_query_timer = eventloop().new_periodic( _mld6igmp_vif.query_last_member_interval().get(), callback(this, &Mld6igmpGroupRecord::group_query_periodic_timeout)); } } /** * Periodic timeout: time to send the next Group-Specific and * Group-and-Source-Specific Queries. * * @return true if the timer should be scheduled again, otherwise false. */ bool Mld6igmpGroupRecord::group_query_periodic_timeout() { string dummy_error_msg; bool s_flag = false; set no_sources; // XXX: empty set set sources_with_s_flag; set sources_without_s_flag; Mld6igmpSourceSet::iterator source_iter; TimeVal max_resp_time = mld6igmp_vif().query_last_member_interval().get(); bool do_send_group_query = true; // // XXX: Don't send Group-Specific or Group-and-Source-Specific Queries // for entries that are in IGMPv1 mode. // if (is_igmpv1_mode()) return (false); // // XXX: The IGMPv3/MLDv2 spec doesn't say what to do here if we changed // from a Querier to a non-Querier. // However, the IGMPv2 spec says that Querier to non-Querier transitions // are to be ignored (see the bottom part of Section 3 of RFC 2236). // Hence, for this reason and for robustness purpose we send the Query // messages without taking into account any Querier to non-Querier // transitions. // // // Send the Group-Specific Query message // if (_query_retransmission_count == 0) { do_send_group_query = false; // No more queries to send } else { _query_retransmission_count--; // // Calculate the group-specific "Suppress Router-Side Processing" bit // TimeVal timeval_remaining; group_timer().time_remaining(timeval_remaining); if (timeval_remaining > _mld6igmp_vif.last_member_query_time()) s_flag = true; _mld6igmp_vif.mld6igmp_query_send(mld6igmp_vif().primary_addr(), group(), max_resp_time, group(), no_sources, s_flag, dummy_error_msg); } // // Select all the sources that should be queried, and add them to // the appropriate set. // for (source_iter = _do_forward_sources.begin(); source_iter != _do_forward_sources.end(); ++source_iter) { Mld6igmpSourceRecord* source_record = source_iter->second; size_t count = source_record->query_retransmission_count(); bool s_flag = false; if (count == 0) continue; source_record->set_query_retransmission_count(count - 1); // // Calculate the "Suppress Router-Side Processing" bit // TimeVal timeval_remaining; source_record->source_timer().time_remaining(timeval_remaining); if (timeval_remaining > _mld6igmp_vif.last_member_query_time()) s_flag = true; if (s_flag) sources_with_s_flag.insert(source_record->source()); else sources_without_s_flag.insert(source_record->source()); } // // Send the Group-and-Source-Specific Query messages // if ((! sources_with_s_flag.empty()) && (! do_send_group_query)) { // // According to RFC 3376, Section 6.6.3.2: // "If a group specific query is scheduled to be transmitted at the // same time as a group and source specific query for the same group, // then transmission of the group and source specific message with the // "Suppress Router-Side Processing" bit set may be suppressed." // // The corresponding text from RFC 3810, Section 7.6.3.2 is similar. // _mld6igmp_vif.mld6igmp_query_send(mld6igmp_vif().primary_addr(), group(), max_resp_time, group(), sources_with_s_flag, true, // XXX: set the s_flag dummy_error_msg); } if (! sources_without_s_flag.empty()) { _mld6igmp_vif.mld6igmp_query_send(mld6igmp_vif().primary_addr(), group(), max_resp_time, group(), sources_without_s_flag, false, // XXX: reset the s_flag dummy_error_msg); } if (sources_with_s_flag.empty() && sources_without_s_flag.empty() && (! do_send_group_query)) { return (false); // No more queries to send } return (true); // Schedule the next timeout } /** * Record that an older Membership report message has been received. * * @param message_version the corresponding protocol version of the * received message. */ void Mld6igmpGroupRecord::received_older_membership_report(int message_version) { TimeVal timeval = _mld6igmp_vif.older_version_host_present_interval(); if (_mld6igmp_vif.proto_is_igmp()) { switch (message_version) { case IGMP_V1: if (_mld6igmp_vif.is_igmpv2_mode()) { // // XXX: The value specified in RFC 2236 is different from // the value specified in RFC 3376. // timeval = _mld6igmp_vif.group_membership_interval(); } _igmpv1_host_present_timer = eventloop().new_oneoff_after( timeval, callback(this, &Mld6igmpGroupRecord::older_version_host_present_timer_timeout)); break; case IGMP_V2: _igmpv2_mldv1_host_present_timer = eventloop().new_oneoff_after( timeval, callback(this, &Mld6igmpGroupRecord::older_version_host_present_timer_timeout)); break; default: break; } } if (_mld6igmp_vif.proto_is_mld6()) { switch (message_version) { case MLD_V1: _igmpv2_mldv1_host_present_timer = eventloop().new_oneoff_after( timeval, callback(this, &Mld6igmpGroupRecord::older_version_host_present_timer_timeout)); break; default: break; } } } void Mld6igmpGroupRecord::older_version_host_present_timer_timeout() { // XXX: nothing to do } /** * Test if the group is running in IGMPv1 mode. * * @return true if the group is running in IGMPv1 mode, otherwise false. */ bool Mld6igmpGroupRecord::is_igmpv1_mode() const { if (! _mld6igmp_vif.proto_is_igmp()) return (false); if (_mld6igmp_vif.is_igmpv1_mode()) return (true); // XXX: explicitly configured in IGMPv1 mode return (_igmpv1_host_present_timer.scheduled()); } /** * Test if the group is running in IGMPv2 mode. * * @return true if the group is running in IGMPv2 mode, otherwise false. */ bool Mld6igmpGroupRecord::is_igmpv2_mode() const { if (! _mld6igmp_vif.proto_is_igmp()) return (false); if (is_igmpv1_mode()) return (false); return (_igmpv2_mldv1_host_present_timer.scheduled()); } /** * Test if the group is running in IGMPv3 mode. * * @return true if the group is running in IGMPv3 mode, otherwise false. */ bool Mld6igmpGroupRecord::is_igmpv3_mode() const { if (! _mld6igmp_vif.proto_is_igmp()) return (false); if (is_igmpv1_mode() || is_igmpv2_mode()) return (false); return (true); } /** * Test if the group is running in MLDv1 mode. * * @return true if the group is running in MLDv1 mode, otherwise false. */ bool Mld6igmpGroupRecord::is_mldv1_mode() const { if (! _mld6igmp_vif.proto_is_mld6()) return (false); if (_mld6igmp_vif.is_mldv1_mode()) return (true); // XXX: explicitly configured in MLDv1 mode return (_igmpv2_mldv1_host_present_timer.scheduled()); } /** * Test if the group is running in MLDv2 mode. * * @return true if the group is running in MLDv2 mode, otherwise false. */ bool Mld6igmpGroupRecord::is_mldv2_mode() const { if (! _mld6igmp_vif.proto_is_mld6()) return (false); if (is_mldv1_mode()) return (false); return (true); } /** * Calculate the forwarding changes and notify the interested parties. * * @param old_is_include mode if true, the old filter mode was INCLUDE, * otherwise was EXCLUDE. * @param old_do_forward_sources the old set of sources to forward. * @param old_dont_forward_sources the old set of sources not to forward. */ void Mld6igmpGroupRecord::calculate_forwarding_changes( bool old_is_include_mode, const set& old_do_forward_sources) const { bool new_is_include_mode = is_include_mode(); set new_do_forward_sources = _do_forward_sources.extract_source_addresses(); set::const_iterator iter; if (old_is_include_mode) { if (new_is_include_mode) { // INCLUDE -> INCLUDE // Join all new sources that are to be forwarded for (iter = new_do_forward_sources.begin(); iter != new_do_forward_sources.end(); ++iter) { const IPvX& ipvx = *iter; if (old_do_forward_sources.find(ipvx) == old_do_forward_sources.end()) { mld6igmp_vif().join_prune_notify_routing(ipvx, group(), ACTION_JOIN); } } // Prune all old sources that were forwarded for (iter = old_do_forward_sources.begin(); iter != old_do_forward_sources.end(); ++iter) { const IPvX& ipvx = *iter; if (new_do_forward_sources.find(ipvx) == new_do_forward_sources.end()) { mld6igmp_vif().join_prune_notify_routing(ipvx, group(), ACTION_PRUNE); } } } if (! new_is_include_mode) { // INCLUDE -> EXCLUDE // Prune the old sources that were forwarded for (iter = old_do_forward_sources.begin(); iter != old_do_forward_sources.end(); ++iter) { const IPvX& ipvx = *iter; if (new_do_forward_sources.find(ipvx) == new_do_forward_sources.end()) { mld6igmp_vif().join_prune_notify_routing(ipvx, group(), ACTION_PRUNE); } } // Join the group itself mld6igmp_vif().join_prune_notify_routing(IPvX::ZERO(family()), group(), ACTION_JOIN); // Join all new sources that are to be forwarded for (iter = new_do_forward_sources.begin(); iter != new_do_forward_sources.end(); ++iter) { const IPvX& ipvx = *iter; if (old_do_forward_sources.find(ipvx) == old_do_forward_sources.end()) { mld6igmp_vif().join_prune_notify_routing(ipvx, group(), ACTION_JOIN); } } } } if (! old_is_include_mode) { if (new_is_include_mode) { // EXCLUDE -> INCLUDE // Prune the group itself mld6igmp_vif().join_prune_notify_routing(IPvX::ZERO(family()), group(), ACTION_PRUNE); // Join all new sources that are to be forwarded for (iter = new_do_forward_sources.begin(); iter != new_do_forward_sources.end(); ++iter) { const IPvX& ipvx = *iter; if (old_do_forward_sources.find(ipvx) == old_do_forward_sources.end()) { mld6igmp_vif().join_prune_notify_routing(ipvx, group(), ACTION_JOIN); } } } if (! new_is_include_mode) { // EXCLUDE -> EXCLUDE // Join all new sources that are to be forwarded for (iter = new_do_forward_sources.begin(); iter != new_do_forward_sources.end(); ++iter) { const IPvX& ipvx = *iter; if (old_do_forward_sources.find(ipvx) == old_do_forward_sources.end()) { mld6igmp_vif().join_prune_notify_routing(ipvx, group(), ACTION_JOIN); } } // Prune all old sources that were forwarded for (iter = old_do_forward_sources.begin(); iter != old_do_forward_sources.end(); ++iter) { const IPvX& ipvx = *iter; if (new_do_forward_sources.find(ipvx) == new_do_forward_sources.end()) { mld6igmp_vif().join_prune_notify_routing(ipvx, group(), ACTION_PRUNE); } } } } } /** * Constructor for a given vif. * * @param mld6igmp_vif the interface this set belongs to. */ Mld6igmpGroupSet::Mld6igmpGroupSet(Mld6igmpVif& mld6igmp_vif) : _mld6igmp_vif(mld6igmp_vif) { } /** * Destructor. */ Mld6igmpGroupSet::~Mld6igmpGroupSet() { // XXX: don't delete the payload, because it might be used elsewhere } /** * Find a group record. * * @param group the group address. * @return the corresponding group record (@ref Mld6igmpGroupRecord) * if found, otherwise NULL. */ Mld6igmpGroupRecord* Mld6igmpGroupSet::find_group_record(const IPvX& group) { Mld6igmpGroupSet::iterator iter = this->find(group); if (iter != this->end()) return (iter->second); return (NULL); } /** * Delete the payload of the set, and clear the set itself. */ void Mld6igmpGroupSet::delete_payload_and_clear() { Mld6igmpGroupSet::iterator iter; // // Delete the payload of the set // for (iter = this->begin(); iter != this->end(); ++iter) { Mld6igmpGroupRecord* group_record = iter->second; delete group_record; } // // Clear the set itself // this->clear(); } /** * Process MODE_IS_INCLUDE report. * * @param group the group address. * @param sources the source addresses. * @param last_reported_host the address of the host that last reported * as member. */ void Mld6igmpGroupSet::process_mode_is_include(const IPvX& group, const set& sources, const IPvX& last_reported_host) { Mld6igmpGroupSet::iterator iter; Mld6igmpGroupRecord* group_record = NULL; iter = this->find(group); if (iter != this->end()) { group_record = iter->second; } else { group_record = new Mld6igmpGroupRecord(_mld6igmp_vif, group); this->insert(make_pair(group, group_record)); } XLOG_ASSERT(group_record != NULL); group_record->process_mode_is_include(sources, last_reported_host); // // If the group record is not used anymore, then delete it // if (group_record->is_unused()) { this->erase(group); delete group_record; } } /** * Process MODE_IS_EXCLUDE report. * * @param group the group address. * @param sources the source addresses. * @param last_reported_host the address of the host that last reported * as member. */ void Mld6igmpGroupSet::process_mode_is_exclude(const IPvX& group, const set& sources, const IPvX& last_reported_host) { Mld6igmpGroupSet::iterator iter; Mld6igmpGroupRecord* group_record = NULL; set nosources; UNUSED(sources); iter = this->find(group); if (iter != this->end()) { group_record = iter->second; } else { group_record = new Mld6igmpGroupRecord(_mld6igmp_vif, group); this->insert(make_pair(group, group_record)); } XLOG_ASSERT(group_record != NULL); group_record->process_mode_is_exclude(nosources, last_reported_host); // // If the group record is not used anymore, then delete it // if (group_record->is_unused()) { this->erase(group); delete group_record; } } /** * Process CHANGE_TO_INCLUDE_MODE report. * * @param group the group address. * @param sources the source addresses. * @param last_reported_host the address of the host that last reported * as member. */ void Mld6igmpGroupSet::process_change_to_include_mode(const IPvX& group, const set& sources, const IPvX& last_reported_host) { Mld6igmpGroupSet::iterator iter; Mld6igmpGroupRecord* group_record = NULL; iter = this->find(group); if (iter != this->end()) { group_record = iter->second; } else { group_record = new Mld6igmpGroupRecord(_mld6igmp_vif, group); this->insert(make_pair(group, group_record)); } XLOG_ASSERT(group_record != NULL); if (_mld6igmp_vif.is_igmpv1_mode(group_record)) { // // XXX: Ignore CHANGE_TO_INCLUDE_MODE messages when in // IGMPv1 mode. // } else { group_record->process_change_to_include_mode(sources, last_reported_host); } // // If the group record is not used anymore, then delete it // if (group_record->is_unused()) { this->erase(group); delete group_record; } } /** * Process CHANGE_TO_EXCLUDE_MODE report. * * @param group the group address. * @param sources the source addresses. * @param last_reported_host the address of the host that last reported * as member. */ void Mld6igmpGroupSet::process_change_to_exclude_mode(const IPvX& group, const set& sources, const IPvX& last_reported_host) { Mld6igmpGroupSet::iterator iter; Mld6igmpGroupRecord* group_record = NULL; UNUSED(sources); iter = this->find(group); if (iter != this->end()) { group_record = iter->second; } else { group_record = new Mld6igmpGroupRecord(_mld6igmp_vif, group); this->insert(make_pair(group, group_record)); } XLOG_ASSERT(group_record != NULL); if (_mld6igmp_vif.is_igmpv1_mode(group_record) || _mld6igmp_vif.is_igmpv2_mode(group_record) || _mld6igmp_vif.is_mldv1_mode(group_record)) { // // XXX: Ignore the source list in the CHANGE_TO_EXCLUDE_MODE // messages when in IGMPv1, IGMPv2, or MLDv1 mode. // set no_sources; // XXX: empty set group_record->process_change_to_exclude_mode(no_sources, last_reported_host); } else { set no_sources; group_record->process_change_to_exclude_mode(no_sources, last_reported_host); } // // If the group record is not used anymore, then delete it // if (group_record->is_unused()) { this->erase(group); delete group_record; } } /** * Process ALLOW_NEW_SOURCES report. * * @param group the group address. * @param sources the source addresses. * @param last_reported_host the address of the host that last reported * as member. */ void Mld6igmpGroupSet::process_allow_new_sources(const IPvX& group, const set& sources, const IPvX& last_reported_host) { Mld6igmpGroupSet::iterator iter; Mld6igmpGroupRecord* group_record = NULL; iter = this->find(group); if (iter != this->end()) { group_record = iter->second; } else { group_record = new Mld6igmpGroupRecord(_mld6igmp_vif, group); this->insert(make_pair(group, group_record)); } XLOG_ASSERT(group_record != NULL); group_record->process_allow_new_sources(sources, last_reported_host); // // If the group record is not used anymore, then delete it // if (group_record->is_unused()) { this->erase(group); delete group_record; } } /** * Process BLOCK_OLD_SOURCES report. * * @param group the group address. * @param sources the source addresses. * @param last_reported_host the address of the host that last reported * as member. */ void Mld6igmpGroupSet::process_block_old_sources(const IPvX& group, const set& sources, const IPvX& last_reported_host) { Mld6igmpGroupSet::iterator iter; Mld6igmpGroupRecord* group_record = NULL; iter = this->find(group); if (iter != this->end()) { group_record = iter->second; } else { group_record = new Mld6igmpGroupRecord(_mld6igmp_vif, group); this->insert(make_pair(group, group_record)); } XLOG_ASSERT(group_record != NULL); if (_mld6igmp_vif.is_igmpv1_mode(group_record) || _mld6igmp_vif.is_igmpv2_mode(group_record) || _mld6igmp_vif.is_mldv1_mode(group_record)) { // // XXX: Ignore BLOCK_OLD_SOURCES messages when in // IGMPv1, IGMPv2, or MLDv1 mode. // } else { group_record->process_block_old_sources(sources, last_reported_host); } // // If the group record is not used anymore, then delete it // if (group_record->is_unused()) { this->erase(group); delete group_record; } } /** * Lower the group timer. * * @param group the group address. * @param timeval the timeout interval the timer should be lowered to. */ void Mld6igmpGroupSet::lower_group_timer(const IPvX& group, const TimeVal& timeval) { Mld6igmpGroupSet::iterator iter; iter = this->find(group); if (iter != this->end()) { Mld6igmpGroupRecord* group_record = iter->second; group_record->lower_group_timer(timeval); } } /** * Lower the source timer for a set of sources. * * @param group the group address. * @param sources the source addresses. * @param timeval the timeout interval the timer should be lowered to. */ void Mld6igmpGroupSet::lower_source_timer(const IPvX& group, const set& sources, const TimeVal& timeval) { Mld6igmpGroupSet::iterator iter; iter = this->find(group); if (iter != this->end()) { Mld6igmpGroupRecord* group_record = iter->second; group_record->lower_source_timer(sources, timeval); } } /** * Get the number of seconds until the IGMPv1 host present timer expires. * * @return the number of seconds until the IGMPv1 host present timer * expires. */ uint32_t Mld6igmpGroupRecord::igmpv1_host_present_timer_timeout_sec() const { TimeVal tv; _igmpv1_host_present_timer.time_remaining(tv); return (tv.sec()); } /** * Get the number of seconds until the IGMPv2/MLDv1 host present timer * expires. * * @return the number of seconds until the IGMPv2/MLDv1 host present timer * expires. */ uint32_t Mld6igmpGroupRecord::igmpv2_mldv1_host_present_timer_timeout_sec() const { TimeVal tv; _igmpv2_mldv1_host_present_timer.time_remaining(tv); return (tv.sec()); } xorp/contrib/mld6igmp_lite/igmp.tp0000664000076400007640000001560011421137511017353 0ustar greearbgreearb/* $XORP: xorp/etc/templates/igmp.tp,v 1.21 2006/12/20 08:30:15 pavlin Exp $ */ protocols { igmp { targetname: txt = "IGMP"; disable: toggle = false; enabled: bool; /* %deprecated */ interface @: txt { vif @: txt { disable: toggle = false; enabled: bool; /* %deprecated */ version: u32 = 2; /* Default to IGMPv2 */ enable-ip-router-alert-option-check: bool = false; query-interval: u32 = 125; query-last-member-interval: u32 = 1; query-response-interval: u32 = 10; robust-count: u32 = 2; } } traceoptions { flag { all { disable: toggle = false; enabled: bool; /* %deprecated */ } } } } } protocols { igmp { %help: short "Configure the IGMP protocol"; %modinfo: provides igmp; %modinfo: depends mfea4; %modinfo: path "contrib/mld6igmp_lite/xorp_igmp"; %modinfo: default_targetname "mld6igmp"; %modinfo: status_method xrl "$(igmp.targetname)/common/0.1/get_status->status:u32&reason:txt"; %modinfo: startup_method xrl "$(igmp.targetname)/mld6igmp/0.1/start_mld6igmp"; %modinfo: shutdown_method xrl "$(igmp.targetname)/common/0.1/shutdown"; %mandatory: $(@.targetname); targetname { %user-hidden: "XRL target name"; %help: short "XRL target name"; %set:; } disable { %help: short "Disable the IGMP protocol"; %create:; %set: xrl "$(igmp.targetname)/mld6igmp/0.1/enable_mld6igmp?enable:bool=`~$(@)`"; %delete: xrl "$(igmp.targetname)/mld6igmp/0.1/enable_mld6igmp?enable:bool=`~$(DEFAULT)`"; } enabled { %deprecated: "Statement 'enabled: true/false' is replaced with 'disable: false/true'"; %help: short "Enable the IGMP protocol"; %create:; %set: xrl "$(igmp.targetname)/mld6igmp/0.1/enable_mld6igmp?enable:bool=$(@)"; } interface @ { %help: short "Configure IGMP on a network interface"; %delete: xrl "$(igmp.targetname)/mld6igmp/0.1/stop_vif?vif_name:txt=$(interface.@)"; vif @ { %help: short "Configure IGMP on a virtual interface"; %activate: xrl "$(igmp.targetname)/mld6igmp/0.1/start_vif?vif_name:txt=$(vif.@)"; %update: xrl "$(igmp.targetname)/mld6igmp/0.1/start_vif?vif_name:txt=$(vif.@)"; %delete: xrl "$(igmp.targetname)/mld6igmp/0.1/stop_vif?vif_name:txt=$(vif.@)"; disable { %help: short "Disable IGMP on an interface"; %set: xrl "$(igmp.targetname)/mld6igmp/0.1/enable_vif?vif_name:txt=$(vif.@)&enable:bool=`~$(@)`"; %delete: xrl "$(igmp.targetname)/mld6igmp/0.1/enable_vif?vif_name:txt=$(vif.@)&enable:bool=`~$(DEFAULT)`"; } enabled { %deprecated: "Statement 'enabled: true/false' is replaced with 'disable: false/true'"; %help: short "Enable IGMP on an interface"; %set: xrl "$(igmp.targetname)/mld6igmp/0.1/enable_vif?vif_name:txt=$(vif.@)&enable:bool=$(@)"; %delete: xrl "$(igmp.targetname)/mld6igmp/0.1/enable_vif?vif_name:txt=$(vif.@)&enable:bool=`~$(DEFAULT)`"; } version { %help: short "Set the IGMP protocol version"; %allow-range: $(@) "1" "3" %help: "The IGMP protocol version"; %create: xrl "$(igmp.targetname)/mld6igmp/0.1/set_vif_proto_version?vif_name:txt=$(vif.@)&proto_version:u32=$(@)"; %set: xrl "$(igmp.targetname)/mld6igmp/0.1/set_vif_proto_version?vif_name:txt=$(vif.@)&proto_version:u32=$(@)"; %delete: xrl "$(igmp.targetname)/mld6igmp/0.1/set_vif_proto_version?vif_name:txt=$(vif.@)&proto_version:u32=$(DEFAULT)"; } enable-ip-router-alert-option-check { %help: short "Enable the IP Router Alert option check"; %create: xrl "$(igmp.targetname)/mld6igmp/0.1/set_vif_ip_router_alert_option_check?vif_name:txt=$(vif.@)&enable:bool=$(@)"; %set: xrl "$(igmp.targetname)/mld6igmp/0.1/set_vif_ip_router_alert_option_check?vif_name:txt=$(vif.@)&enable:bool=$(@)"; %delete: xrl "$(igmp.targetname)/mld6igmp/0.1/set_vif_ip_router_alert_option_check?vif_name:txt=$(vif.@)&enable:bool=$(DEFAULT)"; } query-interval { %help: short "Set the query interval (in seconds)"; %allow-range: $(@) "1" "1024" %help: "The query interval (in seconds)"; %create: xrl "$(igmp.targetname)/mld6igmp/0.1/set_vif_query_interval?vif_name:txt=$(vif.@)&interval_sec:u32=$(@)&interval_usec:u32=0"; %set: xrl "$(igmp.targetname)/mld6igmp/0.1/set_vif_query_interval?vif_name:txt=$(vif.@)&interval_sec:u32=$(@)&interval_usec:u32=0"; %delete: xrl "$(igmp.targetname)/mld6igmp/0.1/set_vif_query_interval?vif_name:txt=$(vif.@)&interval_sec:u32=$(DEFAULT)&interval_usec:u32=0"; } query-last-member-interval { %help: short "Set the last member query interval (in seconds)"; %allow-range: $(@) "1" "1024" %help: "The last member query interval (in seconds)"; %create: xrl "$(igmp.targetname)/mld6igmp/0.1/set_vif_query_last_member_interval?vif_name:txt=$(vif.@)&interval_sec:u32=$(@)&interval_usec:u32=0"; %set: xrl "$(igmp.targetname)/mld6igmp/0.1/set_vif_query_last_member_interval?vif_name:txt=$(vif.@)&interval_sec:u32=$(@)&interval_usec:u32=0"; %delete: xrl "$(igmp.targetname)/mld6igmp/0.1/set_vif_query_last_member_interval?vif_name:txt=$(vif.@)&interval_sec:u32=$(DEFAULT)&interval_usec:u32=0"; } query-response-interval { %help: short "Set the query response interval (in seconds)"; %allow-range: $(@) "1" "1024" %help: "The query response interval (in seconds)"; %create: xrl "$(igmp.targetname)/mld6igmp/0.1/set_vif_query_response_interval?vif_name:txt=$(vif.@)&interval_sec:u32=$(@)&interval_usec:u32=0"; %set: xrl "$(igmp.targetname)/mld6igmp/0.1/set_vif_query_response_interval?vif_name:txt=$(vif.@)&interval_sec:u32=$(@)&interval_usec:u32=0"; %delete: xrl "$(igmp.targetname)/mld6igmp/0.1/set_vif_query_response_interval?vif_name:txt=$(vif.@)&interval_sec:u32=$(DEFAULT)&interval_usec:u32=0"; } robust-count { %help: short "Set the robustness variable count"; %allow-range: $(@) "2" "10" %help: "The robustness variable count"; %create: xrl "$(igmp.targetname)/mld6igmp/0.1/set_vif_robust_count?vif_name:txt=$(vif.@)&robust_count:u32=$(@)"; %set: xrl "$(igmp.targetname)/mld6igmp/0.1/set_vif_robust_count?vif_name:txt=$(vif.@)&robust_count:u32=$(@)"; %delete: xrl "$(igmp.targetname)/mld6igmp/0.1/set_vif_robust_count?vif_name:txt=$(vif.@)&robust_count:u32=$(DEFAULT)"; } } } traceoptions { %help: short "Configure the tracing options"; flag { %help: short "Configure the tracing operation to perform"; all { %help: short "Configure all tracing operations"; disable { %help: short "Disable all tracing operations"; %set: xrl "$(igmp.targetname)/mld6igmp/0.1/log_trace_all?enable:bool=`~$(@)`"; %delete: xrl "$(igmp.targetname)/mld6igmp/0.1/log_trace_all?enable:bool=$(DEFAULT)"; } enabled { %deprecated: "Statement 'enabled: true/false' is replaced with 'disable: false/true'"; %help: short "Enable all tracing operations"; %set: xrl "$(igmp.targetname)/mld6igmp/0.1/log_trace_all?enable:bool=$(@)"; } } } } } } xorp/contrib/mld6igmp_lite/xorp_igmp.cc0000664000076400007640000001205211421137511020363 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // XORP MLD/IGMP module implementation. // #include "mld6igmp_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/callback.hh" #include "libxorp/eventloop.hh" #include "libxorp/exceptions.hh" #include "xrl_mld6igmp_node.hh" #ifdef HAVE_GETOPT_H #include #endif // // Local functions prototypes // static void usage(const char *argv0, int exit_value); /** * usage: * @argv0: Argument 0 when the program was called (the program name itself). * @exit_value: The exit value of the program. * * Print the program usage. * If @exit_value is 0, the usage will be printed to the standart output, * otherwise to the standart error. **/ static void usage(const char *argv0, int exit_value) { FILE *output; const char *progname = strrchr(argv0, '/'); if (progname != NULL) progname++; // Skip the last '/' if (progname == NULL) progname = argv0; // // If the usage is printed because of error, output to stderr, otherwise // output to stdout. // if (exit_value == 0) output = stdout; else output = stderr; fprintf(output, "Usage: %s [-F [:]]\n", progname); fprintf(output, " -F [:] : finder hostname and port\n"); fprintf(output, " -h : usage (this message)\n"); fprintf(output, "\n"); fprintf(output, "Program name: %s\n", progname); fprintf(output, "Module name: %s\n", XORP_MODULE_NAME); fprintf(output, "Module version: %s\n", XORP_MODULE_VERSION); exit (exit_value); // NOTREACHED } static void mld6igmp_main(const string& finder_hostname, uint16_t finder_port) { // // Init stuff // EventLoop eventloop; // // MLD6IGMP node // XrlMld6igmpNode xrl_mld6igmp_node4(AF_INET, XORP_MODULE_MLD6IGMP, eventloop, xorp_module_name(AF_INET, XORP_MODULE_MLD6IGMP), finder_hostname, finder_port, "finder", xorp_module_name(AF_INET, XORP_MODULE_FEA), xorp_module_name(AF_INET, XORP_MODULE_MFEA)); wait_until_xrl_router_is_ready(eventloop, xrl_mld6igmp_node4.xrl_router()); // // Startup // xrl_mld6igmp_node4.enable_mld6igmp(); // xrl_mld6igmp_node4.startup(); xrl_mld6igmp_node4.enable_cli(); xrl_mld6igmp_node4.start_cli(); // // Main loop // while (! xrl_mld6igmp_node4.is_done()) { eventloop.run(); } while (xrl_mld6igmp_node4.xrl_router().pending()) { eventloop.run(); } } int main(int argc, char *argv[]) { int ch; string::size_type idx; const char *argv0 = argv[0]; string finder_hostname = FinderConstants::FINDER_DEFAULT_HOST().str(); uint16_t finder_port = FinderConstants::FINDER_DEFAULT_PORT(); // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); // // Get the program options // while ((ch = getopt(argc, argv, "F:h")) != -1) { switch (ch) { case 'F': // Finder hostname and port finder_hostname = optarg; idx = finder_hostname.find(':'); if (idx != string::npos) { if (idx + 1 >= finder_hostname.length()) { // No port number usage(argv0, 1); // NOTREACHED } char* p = &finder_hostname[idx + 1]; finder_port = static_cast(atoi(p)); finder_hostname = finder_hostname.substr(0, idx); } break; case 'h': case '?': // Help usage(argv0, 0); // NOTREACHED break; default: usage(argv0, 1); // NOTREACHED break; } } argc -= optind; argv += optind; if (argc != 0) { usage(argv0, 1); // NOTREACHED } // // Run everything // try { mld6igmp_main(finder_hostname, finder_port); } catch(...) { xorp_catch_standard_exceptions(); } // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); exit (0); } xorp/SConscript0000664000076400007640000000270411540225520013671 0ustar greearbgreearb# Copyright (c) 2009-2011 XORP, Inc and Others # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # $ID$ Import('env') subdirs = [ 'cli', 'libcomm', 'libxorp', 'libxipc', 'libproto', 'libfeaclient', 'xrl/targets', 'xrl/interfaces', #'xrl/tests', # XXX not in use. 'etc/templates', 'fea', 'fib2mrib', 'mld6igmp', 'mrt', 'pim', 'policy', 'rib', 'rtrmgr', 'static_routes', 'utils', ] if env['enable_olsr']: subdirs.append('contrib/olsr') if env['enable_bgp']: subdirs.append('bgp') if env['enable_ospf']: subdirs.append('ospf') if env['enable_rip']: subdirs.append('rip') if env['enable_vrrp']: subdirs.append('vrrp') SConscript(dirs = subdirs, exports='env') xorp/ERRATA0000664000076400007640000003473611421137511012572 0ustar greearbgreearb XORP ERRATA See: https://sourceforge.net/apps/trac/xorp/report for current bug list. ALL: - The following compiler is known to be buggy, and should not be used to compile XORP: gcc34 (GCC) 3.4.0 20040310 (prerelease) [FreeBSD] A newer compiler such as the following should be used instead: gcc34 (GCC) 3.4.2 20040827 (prerelease) [FreeBSD] - If you run BGP, RIB, FIB2MRIB, and PIM-SM at the same time, the propagation latency for the BGP routes to reach the kernel is increased. We are investigating the problem. LIBXORP: - No known issues. LIBXIPC: - No known issues. LIBFEACLIENT: - No known issues. XRL: - No known issues. RTRMGR: - There are several known issues, but none of them is considered critical. - Using the rtrmgr "-r" command-line option to restart processes that have failed does not work if a process fails while being reconfigured via xorpsh. If that happens, the rtrmgr itself may coredump. Therefore, using the "-r" command-line option is not recommended! Also, note that a process that has been killed by SIGTERM or SIGKILL will not be restarted (this is a feature rather than a bug). Ideally, we want to monitor the processes status using the finder rather than the forked children process status, therefore in the future when we have a more robust implementation the "-r" switch will be removed and will be enabled by default. XORPSH: - There are several known issues, but none of them is considered critical. FEA/MFEA: - On Linux with kernels prior to 2.6.24 and IPv6 support, interfaces fail to be reconfigured properly if brought down and up again. This occurs because Linux's IPv6 subsystem (incorrectly) sends and RTM_DELLINK message when an interface is brought down, and XORP accordingly deletes the interface. When the interface comes back up, a pristine interface is created within XORP with no configuration. Hence any IP addresses associated to the interface are lost. To check for the bug, launch XORP, bring an interface down and up again, and from xorpsh, do a show interfaces and check whether you see DISABLED next to the IP addresses of the interface. If so, you're hit by the bug. On Linux, a typical scenario where an interface is brought down and up again is when changing its MAC address. This means that XORP's MAC configuration directive may not work correctly on affected systems. - On Linux with kernel 2.6 (e.g., RedHat FC2 with kernel 2.6.5-1.358), some of the tests may fail (with or without an error message), but no coredump image. Some of those failures can be contributed to a kernel problem. E.g., running "dmesg" can show kernel "Oops" messages like: Unable to handle kernel NULL pointer dereference at virtual address 00000000 printing eip: ... This appears to be a kernel bug triggered by ioctl(SIOCGIFNAME) which itself is called by if_indextoname(3). Currently, there is no known solution, but it appears the problem may have been fixed for more recent Linux kernel versions: https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=121697 - On Linux with kernel older than linux-2.6.15-rc7 there is a kernel bug that prevents the FEA to receive netlink(7) notifications about added/deleted IPv6 network addresses and routes: http://www.uwsg.indiana.edu/hypermail/linux/kernel/0512.2/2121.html Typically, this could be an issue only if someone is running IPv6 PIM-SM on Linux, and only if the unicast routes may be modified while XORP is running. In that case the fix would be to replace "RTMGRP_IPV6_IFADDR" with "(RTMGRP_IPV6_IFADDR >> 1)" inside fea/data_plane/ifconfig/ifconfig_observer_netlink_socket.cc, and to replace "RTMGRP_IPV6_ROUTE" with "(RTMGRP_IPV6_ROUTE >> 1)" inside fea/data_plane/fibconfig/fibconfig_entry_observer_netlink_socket.cc and fea/data_plane/fibconfig/fibconfig_table_observer_netlink_socket.cc - On Linux, adding and deleting multiple IPv4 addresses per interface may trigger an error: typically, if the primary IPv4 address is deleted, the kernel automatically deletes all secondary IPv4 addresses on that interface. In Linux kernel 2.6.12 and later, enabling the new sysctl net.ipv4.conf.all.promote_secondaries (or one of the interface specific variants) can be used to automatically promote one of the secondary addresses to become the new primary address. - On Linux with kernel older than linux-2.6.17.11 there is a kernel bug related to modifying MAC addresses using the RTM_SETLINK netlink mechanism: http://www.spinics.net/lists/kernel/msg498873.html If it is not possible to update the kernel, the simplest work-around would be to replace "#ifdef RTM_SETLINK" with "#if 0" inside file fea/data_plane/ifconfig/ifconfig_set_netlink_socket.cc, method IfConfigSetNetlinkSocket::set_interface_mac_address(). - The mechanism for tracking the network interface link status may not work for the following OS-es because the kernel for those systems does not provide a mechanism for asynchronous notification of userland programs when the link status changes: FreeBSD-5.2 and earlier and MacOS X. - On Linux with kernel version linux-2.6.17 and older there is a kernel bug which prevents adding an IPv6 address to an interface if the interface is DOWN: # ip link set eth1 down # ip addr add 3ffe:c00:0:0:1111:1111:1111:1111/64 dev eth1 RTNETLINK answers: Network is down Exit 2 As a result of that, XORP cannot be started with explicitly configured IPv6 addresses on an interface if that interface is DOWN. The problem has been fixed between linux-2.6.17 and linux-2.6.20. If updating to a newer Linux kernel is not feasible, a possible work-around is to explicitly set the interface status to UP before adding/configuring IPv6 addresses to it. - On Linux systems up to year 2008 or so the following header files are not C++ friendly: This prevents the compilation of the Linux Netfilter-based firewall support. See the following Bugzilla entry for details: http://bugzilla.netfilter.org/show_bug.cgi?id=536 To enable the Linux firewall compilation the following patches need should be applied to the above two files. Note that the patches are for Ubuntu Server-7.10, so they might need slight adjustment for other Linux distributions. After applying the patch rerun "./configure; gmake". --- /usr/include/linux/netfilter_ipv4/ip_tables.h.org 2008-02-12 02:49:51.000000000 -0800 +++ /usr/include/linux/netfilter_ipv4/ip_tables.h 2008-04-19 22:09:22.000000000 -0700 @@ -217,7 +217,7 @@ static __inline__ struct ipt_entry_target * ipt_get_target(struct ipt_entry *e) { - return (void *)e + e->target_offset; + return (struct ipt_entry_target *)((char *)e + e->target_offset); } /* fn returns 0 to continue iteration */ --- /usr/include/linux/netfilter_ipv6/ip6_tables.h.org 2008-02-12 02:49:51.000000000 -0800 +++ /usr/include/linux/netfilter_ipv6/ip6_tables.h 2008-04-25 16:43:05.000000000 -0700 @@ -271,7 +271,7 @@ static __inline__ struct ip6t_entry_target * ip6t_get_target(struct ip6t_entry *e) { - return (void *)e + e->target_offset; + return (struct ip6t_entry_target *)((char *)e + e->target_offset); } /* fn returns 0 to continue iteration */ - On Linux systems up to year 2008 or so a VLAN can be configured on a network interface only if the interface us UP. Eventually this limitation would be fixed in Linux kernel 2.6.28. RIB: - In some rare cases, the RIB may fail to delete an existing route. We are aware of the issue and will attempt to fix it in the future. RIP: - No known issues. OSPF: - Just before the XORP-1.4 release it was noticed that in OSPFv3 LSAs with link-local scope could be flooded to links where they shouldn't be sent. We believe this shouldn't affect the routing computation, but it does introduce more LSAs than necessary. If multiple interfaces on a host have the either the same IP address or have addresses in the same subnet then OSPF will not function correctly. Error messages of this form have been seen when this misconfiguration occurs: [ 2008/12/18 15:42:11 WARNING xorp_fea FEA ] proto_socket_read() failed: RX packet from 10.100.10.2 to 224.0.0.5 pif_index 2: no vif found - There are several other known issues, but none of them is considered critical. The list of known issues is available from: BGP: - The BGP configuration mandates that an IPv4 nexthop must be supplied. Unfortunately it is necessary to provide an IPv4 nexthop even for an IPv6 only peering. Even more unfortunately it is not possible to force the IPv6 nexthop. - It is *essential* for an IPv6 peering that an IPv6 nexthop is provided. Unfortunately the configuration does not enforce this requrement. This will be fixed in the future. STATIC_ROUTES: - No known issues. MLD/IGMP: - If MLD/IGMP is started on Linux with a relatively large number of interfaces (e.g., on the order of 10), then it may fail with the following error: [ 2004/06/14 12:58:56 ERROR test_pim:16548 MFEA +666 mfea_proto_comm.cc join_multicast_group ] Cannot join group 224.0.0.2 on vif eth8: No buffer space available The solution is to increase the multicast group membership limit. E.g., to increase the value from 20 (the default) to 200, run as a root: sysctl -w net.ipv4.igmp_max_memberships=200 or echo 200 > /proc/sys/net/ipv4/igmp_max_memberships In addition, if the number of interfaces is notably larger (e.g., on the order of 150), the maximum amount of socket option memory buffer should be increased as well. E.g., to increase the value from 10240 (the default) to 200000, run as a root: sysctl -w net.core.optmem_max=200000 or echo 200000 > /proc/sys/net/core/optmem_max Also, see the PIM-SM instructions for increasing other system limits when the number of interfaces is relatively large. PIM-SM: - If the kernel does not support PIM-SM, or if PIM-SM is not enabled in the kernel, then running PIM-SM will fail with the following error message: [ 2004/06/12 10:26:41 ERROR xorp_fea:444 MFEA +529 mfea_mrouter.cc start_mrt ] setsockopt(MRT_INIT, 1) failed: Operation not supported - On Linux, if the unicast Reverse Path Forwarding information is different from the multicast Reverse Path Forwarding information, the Reverse Path Filtering should be disabled. E.g., as root: echo 0 > /proc/sys/net/ipv4/conf/all/rp_filter OR echo 0 > /proc/sys/net/ipv4/conf/eth0/rp_filter echo 0 > /proc/sys/net/ipv4/conf/eth1/rp_filter ... Otherwise, the router will ignore packets if they don't arrive on the reverse-path interface. For more information about Reverse Path Filtering see: http://www.tldp.org/HOWTO/Adv-Routing-HOWTO/lartc.kernel.rpf.html - Currently, the PIM-SM implementation does not support unnumbered point-to-point links. Furthermore, even on numbered point-to-point links the next-hop information in the routing entries should use an IP address instead of an interface name. For example, if there is a GRE tunnel on Linux, then use the following command to add a route that uses that tunnel: route add -net gw instead of: route add -net - If PIM-SM is configured to run over a large number of interfaces (e.g., more than 31 VLANs), it might fail with the following error: [ 2006/07/04 11:56:23 ERROR xorp_fea:28353 MFEA +967 mfea_mrouter.cc add_multicast_vif ] setsockopt(MRT_ADD_VIF, vif eth0.4) failed: Too many open files in system The reason for that error is that by default majority of the UNIX kernels cannot support more than 32 interfaces enabled for multicast forwarding (one interface is always used as the internal PIM Register virtual interface). The solution is to increase the MAXVIFS limit in the kernel (typically defined in the "netinet/ip_mroute.h" (BSD) or the "include/linux/mroute.h" (Linux) kernel file), and recompile the kernel. It should be increased also in the corresponding system header file as well: or . After that XORP should be recompiled to take into account the MAXVIFS increase. If modifying the system header files is not acceptable, then the following should be added toward the end of file "xorp/mrt/max_vifs.h" before recompiling XORP: #undef MAX_VIFS #define MAX_VIFS 50 Also, see the MLD/IGMP instructions for increasing other system limits when the number of interfaces is relatively large. FIB2MRIB: - No known issues. VRRP: - Cannot run more than one VRRP instance on a physical interface. Each instance requires a different MAC address, and only one MAC address can be set per interface. - Incorrect behavior when running more than one VRRP instance on the same LAN and host. VRRP uses raw sockets to listen for advertisements and locally generated packets are not sent to them. So if two VRRP instances on the same host share the same LAN, they will not see each other's advertisements and may result in both becoming masters. - Need to manually (or via XORP configuration) reset an interface's MAC address if VRRP crashes. VRRP may alter the MAC address of an interface and restore it only upon exit. If VRRP crashes, the MAC address may remain altered and will need to be fixed manually before restarting VRRP as the daemon will otherwise complain. - On some combinations of Linux kernels and network cards, if the link beat goes down, when it comes back up, packets are no longer received by the OS, causing VRRP to fail. CLI: - No known issues. xorp/VERSION0000664000076400007640000000000611703343713012726 0ustar greearbgreearb1.8.5 xorp/policy/0000775000076400007640000000000011631505660013162 5ustar greearbgreearbxorp/policy/process_watch.cc0000664000076400007640000000531411421137511016331 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #include "policy_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "process_watch.hh" ProcessWatch::ProcessWatch(XrlStdRouter& rtr, ProtocolMap& pmap) : _pmap(pmap), _finder(&rtr), _instance_name(rtr.instance_name()), _notifier(NULL), _finder_name("finder") // FIXME: hardcoded value { } void ProcessWatch::register_cb(const XrlError& err) { string error_msg; if (err != XrlError::OKAY()) { error_msg = c_format("XRL register_cb() error: %s", err.str().c_str()); XLOG_ERROR("%s", error_msg.c_str()); // xorp_throw(PWException, error_msg); } } void ProcessWatch::add_interest(const string& proc) { // check if we already added interested, if so do nothing if (_watching.find(proc) != _watching.end()) return; _watching.insert(proc); debug_msg("[POLICY] ProcessWatch Add interest in process: %s\n", proc.c_str()); // add interested in process _finder.send_register_class_event_interest(_finder_name.c_str(), _instance_name, _pmap.xrl_target(proc), callback(this,&ProcessWatch::register_cb)); } void ProcessWatch::birth(const string& proto) { const string& p = _pmap.protocol(proto); _alive.insert(p); // inform any hooked notifier if (_notifier) _notifier->birth(p); } void ProcessWatch::death(const string& proto) { const string& p = _pmap.protocol(proto); _alive.erase(p); if (_notifier) _notifier->death(p); } bool ProcessWatch::alive(const string& proto) { if (_watching.find(proto) == _watching.end()) xorp_throw(PWException, "Not watching protocol: " + proto); return _alive.find(proto) != _alive.end(); } void ProcessWatch::set_notifier(PWNotifier& notifier) { // old notifier is lost... it is a feature, not a bug. _notifier = ¬ifier; } xorp/policy/visitor.hh0000664000076400007640000000415211540224233015175 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/policy/visitor.hh,v 1.12 2008/10/02 21:58:01 bms Exp $ #ifndef __POLICY_VISITOR_HH__ #define __POLICY_VISITOR_HH__ #include "policy/common/element_base.hh" template class NodeAny; typedef NodeAny NodeVar; class NodeElem; class NodeBin; class NodeUn; class NodeSet; class NodeAssign; class NodeAccept; class NodeReject; class NodeProto; class Term; class PolicyStatement; class NodeNext; class NodeSubr; /** * @short Visitor pattern interface. * * Inspired by Alexandrescu. */ class Visitor { public: virtual ~Visitor() {} virtual const Element* visit(NodeUn&) = 0; virtual const Element* visit(NodeBin&) = 0; virtual const Element* visit(NodeVar&) = 0; virtual const Element* visit(NodeAssign&) = 0; virtual const Element* visit(NodeSet&) = 0; virtual const Element* visit(NodeAccept&) = 0; virtual const Element* visit(NodeReject&) = 0; virtual const Element* visit(Term&) = 0; virtual const Element* visit(PolicyStatement&) = 0; virtual const Element* visit(NodeElem&) = 0; virtual const Element* visit(NodeProto&) = 0; virtual const Element* visit(NodeNext&) = 0; virtual const Element* visit(NodeSubr&) = 0; }; #endif // __POLICY_VISITOR_HH__ xorp/policy/export_code_generator.cc0000664000076400007640000000612011540224233020042 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "policy_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "export_code_generator.hh" ExportCodeGenerator::ExportCodeGenerator( const string& proto, const SourceMatchCodeGenerator::Tags& t, const VarMap& varmap, PolicyMap& pmap) : CodeGenerator(proto, filter::EXPORT, varmap, pmap), _tags(t) { _tags_iter = _tags.begin(); } const Element* ExportCodeGenerator::visit_term(Term& term) { XLOG_ASSERT(_tags_iter != _tags.end()); // ignore source [done by source match] // XXX but there could be a from policy subroutine that has a dest block. // Currently ignored. -sorbo Term::Nodes& dest = term.dest_nodes(); Term::Nodes& actions = term.action_nodes(); Term::Nodes::iterator i; _os << "TERM_START " << term.name() << endl ; // make sure source block was not empty: // tags are linear.. for each term, match the tag in the source block. const SourceMatchCodeGenerator::Taginfo& ti = *_tags_iter; if (ti.first) { _os << "LOAD " << (int)(VarRW::VAR_POLICYTAGS) << "\n"; _os << "PUSH u32 " << (ti.second) << endl; _os << "<=\n"; _os << "ONFALSE_EXIT" << endl; bool is_redist_tag = true; if (term.from_protocol() == protocol()) { // // XXX: If we have an export policy that exports routes // from a protocol to itself, then don't tag those routes // for redistribution from the RIB back to the protocol. // is_redist_tag = false; } // update tags used by the code _code.add_tag(ti.second, is_redist_tag); } // do dest block for(i = dest.begin(); i != dest.end(); ++i) { (i->second)->accept(*this); _os << "ONFALSE_EXIT" << endl; } // // Do the action block. // XXX: We generate last the code for the "accept" or "reject" statements. // for(i = actions.begin(); i != actions.end(); ++i) { if ((i->second)->is_accept_or_reject()) continue; (i->second)->accept(*this); } for(i = actions.begin(); i != actions.end(); ++i) { if ((i->second)->is_accept_or_reject()) (i->second)->accept(*this); } _os << "TERM_END\n"; // go to next tag information ++_tags_iter; return NULL; } xorp/policy/policy_target.hh0000664000076400007640000001751011540224233016345 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/policy/policy_target.hh,v 1.19 2008/10/02 21:57:59 bms Exp $ #ifndef __POLICY_POLICY_TARGET_HH__ #define __POLICY_POLICY_TARGET_HH__ #include "libxipc/xrl_std_router.hh" #include "process_watch.hh" #include "configuration.hh" #include "filter_manager.hh" #include "policy/common/varrw.hh" /** * @short The XORP Policy target. * * This is the class that will be called to perform operation from the xrl * target. */ class PolicyTarget { public: static string policy_target_name; /** * @param rtr Xrl router used by this XORP process. */ PolicyTarget(XrlStdRouter& rtr); /** * @return true if process is running. */ bool running(); /** * Shutdown the process. */ void shutdown(); /** * Attempts to create a term. * Terms are appended in existing policies [currently no way of inserting a * term in a specific position]. * * Exception is thrown on error. * * @param policy policy in which term should be created. * @param order node ID with position of term. * @param term name of term to create. */ void create_term(const string& policy, const ConfigNodeId& order, const string& term); /** * Attempts to delete a term. * * Exception is thrown on error. * * @param policy policy in which term should be deleted. * @param term name of the term. */ void delete_term(const string& policy, const string& term); /** * Update the source/dest/action block of a term in a policy. * * Exception is thrown on error * * @param policy the name of the policy. * @param term the name of the term. * @param block the block to update (0:source, 1:dest, 2:action). * @param order node ID with position of term. * @param statement the statement to insert. */ void update_term_block(const string& policy, const string& term, const uint32_t& block, const ConfigNodeId& order, const string& statement); /** * Attempts to create a policy. * * Exception is thrown on error. * * @param policy name of policy to create. */ void create_policy(const string& policy); /** * Attempts to delete a policy. * * Exception is thrown on error. * * @param policy name of policy to delete. */ void delete_policy(const string& policy); /** * Attempts to create a policy. * * Exception is thrown on error. * * @param name name of set to create. */ void create_set(const string& name); /** * Attempts to update set elements. * * Exception is thrown on error. * * @param type the type of the set. * @param name name of set to update. * @param elements the elements of a set comma separated. */ void update_set(const string& type, const string& name, const string& elements); /** * Attempts to delete a set. * * Exception is thrown on error. * * @param name name of set to create. */ void delete_set(const string& name); /** * Add an element to a set. * * Exception is thrown on error. * * @param type the type of the set. * @param name name of the set. * @param element the element to add. */ void add_to_set(const string& type, const string& name, const string& element); /** * Delete an element from a set. * * Exception is thrown on error. * * @param type the type of the set. * @param name name of the set. * @param element the element to delete. */ void delete_from_set(const string& type, const string& name, const string& element); /** * Updates the import policy list for a protocol and triggers a delayed * commit. * * @param protocol protocol for which to update imports. * @param policies comma separated policy list. */ void update_import(const string& protocol, const string& policies, const string& modifier); /** * Updates the export policy list for a protocol and triggers a delayed * commit. * * @param protocol protocol for which to update imports. * @param policies comma separated policy list. */ void update_export(const string& protocol, const string& policies, const string& modifier); /* * Configure the variable map used for semantic checking. * This should be initialized only once at startup. * * Dynamic configuration may easily be implemented by invalidiating policies * in the configuration class. * * Dynamic addition of variables should be safe. It is the removal and * update which needs to trigger a policy to be flagged as modified. * * @param protocol the protocol for which the variable is available. * @param variable the name of the variable. * @param type the type of the variable. * @param access the permissions on the variable (r/rw). * @param id the varrw interface id. */ void add_varmap(const string& protocol, const string& variable, const string& type, const string& access, const VarRW::Id& id); /** * Commit all configuration changes, but trigger a delayed update to the * actual policy filters. * * @param msec milliseconds after which policy filters should be updated. */ void commit(uint32_t msec); /** * Dump internal state. Use only for debugging. * * @param id which part of the state to dump. * @return string representation of internal state. */ string dump_state(uint32_t id); /** * Announce birth of a XORP process. * * @param tclass target class. * @param tinstance target instance of class. */ void birth(const string& tclass, const string& tinstance); /** * Announce death of a XORP process. * * @param tclass target class. * @param tinstance target instance of class. */ void death(const string& tclass, const string& tinstance); /** * Update the protocol -> XRL target map. * * @param protocol the protocol. * @param target the XRL target. */ void set_proto_target(const string& protocol, const string& target); string cli_command(const string& command); string test_policy(const string& arg); string show(const string& arg); void show(const string& type, const string& name, RESOURCES& res); bool test_policy(const string& policy, const string& prefix, const string& attributes, string& mods); bool test_policy(const string& policy, const RATTR& attrs, RATTR& mods); private: void parse_attributes(const string& attr, RATTR& out); bool _running; uint32_t _commit_delay; ProtocolMap _pmap; ProcessWatch _process_watch; Configuration _conf; FilterManager _filter_manager; }; #endif // __POLICY_POLICY_TARGET_HH__ xorp/policy/protocol_map.hh0000664000076400007640000000432711540225532016203 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/policy/protocol_map.hh,v 1.6 2008/10/02 21:58:00 bms Exp $ #ifndef __POLICY_PROTOCOL_MAP_HH__ #define __POLICY_PROTOCOL_MAP_HH__ /** * @short Maps protocols to the XORP process name. * * By default the mapping is the protocol name itself unless an entry has been * explicitly added. This class is used to map user configuration directives * regarding protocols to the actual name of the XORP process for that protocol. */ class ProtocolMap : public NONCOPYABLE { public: ProtocolMap(); /** * Determine the XRL target for a protocol. * * @return the XRL target for the protocol. * @param protocol the protocol for which the XRL target is wanted. */ const string& xrl_target(const string& protocol); /** * Set the XRL target for a protocol. * * @param protocol the protocol for which the XRL target needs to be set. * @param target the XRL target for the protocol. */ void set_xrl_target(const string& protocol, const string& target); /** * Return internal protocol name based on XRL target. * * @return protocol name. * @param target the XRL target for the protocol. */ const string& protocol(const string& target); private: typedef map Map; Map _map; }; #endif // __POLICY_PROTOCOL_MAP_HH__ xorp/policy/lex.yy_policy_parser.cc0000664000076400007640000057731711421137511017671 0ustar greearbgreearb#line 2 "lex.yy_policy_parser.cc" #line 4 "lex.yy_policy_parser.cc" #define YY_INT_ALIGNED short int /* A lexical scanner generated by flex */ #define FLEX_SCANNER #define YY_FLEX_MAJOR_VERSION 2 #define YY_FLEX_MINOR_VERSION 5 #define YY_FLEX_SUBMINOR_VERSION 33 #if YY_FLEX_SUBMINOR_VERSION > 0 #define FLEX_BETA #endif /* First, we deal with platform-specific or compiler-specific issues. */ /* begin standard C headers. */ #include #include #include #include /* end standard C headers. */ /* flex integer type definitions */ #ifndef FLEXINT_H #define FLEXINT_H /* C99 systems have . Non-C99 systems may or may not. */ #if __STDC_VERSION__ >= 199901L /* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, * if you want the limit (max/min) macros for int types. */ #ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS 1 #endif #include typedef int8_t flex_int8_t; typedef uint8_t flex_uint8_t; typedef int16_t flex_int16_t; typedef uint16_t flex_uint16_t; typedef int32_t flex_int32_t; typedef uint32_t flex_uint32_t; #else typedef signed char flex_int8_t; typedef short int flex_int16_t; typedef int flex_int32_t; typedef unsigned char flex_uint8_t; typedef unsigned short int flex_uint16_t; typedef unsigned int flex_uint32_t; #endif /* ! C99 */ /* Limits of integral types. */ #ifndef INT8_MIN #define INT8_MIN (-128) #endif #ifndef INT16_MIN #define INT16_MIN (-32767-1) #endif #ifndef INT32_MIN #define INT32_MIN (-2147483647-1) #endif #ifndef INT8_MAX #define INT8_MAX (127) #endif #ifndef INT16_MAX #define INT16_MAX (32767) #endif #ifndef INT32_MAX #define INT32_MAX (2147483647) #endif #ifndef UINT8_MAX #define UINT8_MAX (255U) #endif #ifndef UINT16_MAX #define UINT16_MAX (65535U) #endif #ifndef UINT32_MAX #define UINT32_MAX (4294967295U) #endif #endif /* ! FLEXINT_H */ #ifdef __cplusplus /* The "const" storage-class-modifier is valid. */ #define YY_USE_CONST #else /* ! __cplusplus */ #if __STDC__ #define YY_USE_CONST #endif /* __STDC__ */ #endif /* ! __cplusplus */ #ifdef YY_USE_CONST #define yyconst const #else #define yyconst #endif /* Returned upon end-of-file. */ #define YY_NULL 0 /* Promotes a possibly negative, possibly signed char to an unsigned * integer for use as an array index. If the signed char is negative, * we want to instead treat it as an 8-bit unsigned char, hence the * double cast. */ #define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) /* Enter a start condition. This macro really ought to take a parameter, * but we do it the disgusting crufty way forced on us by the ()-less * definition of BEGIN. */ #define BEGIN (yy_start) = 1 + 2 * /* Translate the current start state into a value that can be later handed * to BEGIN to return to the state. The YYSTATE alias is for lex * compatibility. */ #define YY_START (((yy_start) - 1) / 2) #define YYSTATE YY_START /* Action number for EOF rule of a given start state. */ #define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) /* Special action meaning "start processing a new file". */ #define YY_NEW_FILE yy_policy_parserrestart(yy_policy_parserin ) #define YY_END_OF_BUFFER_CHAR 0 /* Size of default input buffer. */ #ifndef YY_BUF_SIZE #define YY_BUF_SIZE 16384 #endif /* The state buf must be large enough to hold one state per character in the main buffer. */ #define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) #ifndef YY_TYPEDEF_YY_BUFFER_STATE #define YY_TYPEDEF_YY_BUFFER_STATE typedef struct yy_buffer_state *YY_BUFFER_STATE; #endif extern int yy_policy_parserleng; extern FILE *yy_policy_parserin, *yy_policy_parserout; #define EOB_ACT_CONTINUE_SCAN 0 #define EOB_ACT_END_OF_FILE 1 #define EOB_ACT_LAST_MATCH 2 #define YY_LESS_LINENO(n) /* Return all but the first "n" matched characters back to the input stream. */ #define yyless(n) \ do \ { \ /* Undo effects of setting up yy_policy_parsertext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ *yy_cp = (yy_hold_char); \ YY_RESTORE_YY_MORE_OFFSET \ (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ YY_DO_BEFORE_ACTION; /* set up yy_policy_parsertext again */ \ } \ while ( 0 ) #define unput(c) yyunput( c, (yytext_ptr) ) /* The following is because we cannot portably get our hands on size_t * (without autoconf's help, which isn't available because we want * flex-generated scanners to compile on their own). */ #ifndef YY_TYPEDEF_YY_SIZE_T #define YY_TYPEDEF_YY_SIZE_T typedef unsigned int yy_size_t; #endif #ifndef YY_STRUCT_YY_BUFFER_STATE #define YY_STRUCT_YY_BUFFER_STATE struct yy_buffer_state { FILE *yy_input_file; char *yy_ch_buf; /* input buffer */ char *yy_buf_pos; /* current position in input buffer */ /* Size of input buffer in bytes, not including room for EOB * characters. */ yy_size_t yy_buf_size; /* Number of characters read into yy_ch_buf, not including EOB * characters. */ int yy_n_chars; /* Whether we "own" the buffer - i.e., we know we created it, * and can realloc() it to grow it, and should free() it to * delete it. */ int yy_is_our_buffer; /* Whether this is an "interactive" input source; if so, and * if we're using stdio for input, then we want to use getc() * instead of fread(), to make sure we stop fetching input after * each newline. */ int yy_is_interactive; /* Whether we're considered to be at the beginning of a line. * If so, '^' rules will be active on the next match, otherwise * not. */ int yy_at_bol; int yy_bs_lineno; /**< The line count. */ int yy_bs_column; /**< The column count. */ /* Whether to try to fill the input buffer when we reach the * end of it. */ int yy_fill_buffer; int yy_buffer_status; #define YY_BUFFER_NEW 0 #define YY_BUFFER_NORMAL 1 /* When an EOF's been seen but there's still some text to process * then we mark the buffer as YY_EOF_PENDING, to indicate that we * shouldn't try reading from the input source any more. We might * still have a bunch of tokens to match, though, because of * possible backing-up. * * When we actually see the EOF, we change the status to "new" * (via yy_policy_parserrestart()), so that the user can continue scanning by * just pointing yy_policy_parserin at a new input file. */ #define YY_BUFFER_EOF_PENDING 2 }; #endif /* !YY_STRUCT_YY_BUFFER_STATE */ /* Stack of input buffers. */ static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */ static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */ static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */ /* We provide macros for accessing buffer states in case in the * future we want to put the buffer states in a more general * "scanner state". * * Returns the top of the stack, or NULL. */ #define YY_CURRENT_BUFFER ( (yy_buffer_stack) \ ? (yy_buffer_stack)[(yy_buffer_stack_top)] \ : NULL) /* Same as previous macro, but useful when we know that the buffer stack is not * NULL or when we need an lvalue. For internal use only. */ #define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)] /* yy_hold_char holds the character lost when yy_policy_parsertext is formed. */ static char yy_hold_char; static int yy_n_chars; /* number of characters read into yy_ch_buf */ int yy_policy_parserleng; /* Points to current character in buffer. */ static char *yy_c_buf_p = (char *) 0; static int yy_init = 0; /* whether we need to initialize */ static int yy_start = 0; /* start state number */ /* Flag which is used to allow yy_policy_parserwrap()'s to do buffer switches * instead of setting up a fresh yy_policy_parserin. A bit of a hack ... */ static int yy_did_buffer_switch_on_eof; void yy_policy_parserrestart (FILE *input_file ); void yy_policy_parser_switch_to_buffer (YY_BUFFER_STATE new_buffer ); YY_BUFFER_STATE yy_policy_parser_create_buffer (FILE *file,int size ); void yy_policy_parser_delete_buffer (YY_BUFFER_STATE b ); void yy_policy_parser_flush_buffer (YY_BUFFER_STATE b ); void yy_policy_parserpush_buffer_state (YY_BUFFER_STATE new_buffer ); void yy_policy_parserpop_buffer_state (void ); static void yy_policy_parserensure_buffer_stack (void ); static void yy_policy_parser_load_buffer_state (void ); static void yy_policy_parser_init_buffer (YY_BUFFER_STATE b,FILE *file ); #define YY_FLUSH_BUFFER yy_policy_parser_flush_buffer(YY_CURRENT_BUFFER ) YY_BUFFER_STATE yy_policy_parser_scan_buffer (char *base,yy_size_t size ); YY_BUFFER_STATE yy_policy_parser_scan_string (yyconst char *yy_str ); YY_BUFFER_STATE yy_policy_parser_scan_bytes (yyconst char *bytes,int len ); void *yy_policy_parseralloc (yy_size_t ); void *yy_policy_parserrealloc (void *,yy_size_t ); void yy_policy_parserfree (void * ); #define yy_new_buffer yy_policy_parser_create_buffer #define yy_set_interactive(is_interactive) \ { \ if ( ! YY_CURRENT_BUFFER ){ \ yy_policy_parserensure_buffer_stack (); \ YY_CURRENT_BUFFER_LVALUE = \ yy_policy_parser_create_buffer(yy_policy_parserin,YY_BUF_SIZE ); \ } \ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ } #define yy_set_bol(at_bol) \ { \ if ( ! YY_CURRENT_BUFFER ){\ yy_policy_parserensure_buffer_stack (); \ YY_CURRENT_BUFFER_LVALUE = \ yy_policy_parser_create_buffer(yy_policy_parserin,YY_BUF_SIZE ); \ } \ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ } #define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) /* Begin user sect3 */ #define yy_policy_parserwrap(n) 1 #define YY_SKIP_YYWRAP typedef unsigned char YY_CHAR; FILE *yy_policy_parserin = (FILE *) 0, *yy_policy_parserout = (FILE *) 0; typedef int yy_state_type; extern int yy_policy_parserlineno; int yy_policy_parserlineno = 1; extern char *yy_policy_parsertext; #define yytext_ptr yy_policy_parsertext static yy_state_type yy_get_previous_state (void ); static yy_state_type yy_try_NUL_trans (yy_state_type current_state ); static int yy_get_next_buffer (void ); static void yy_fatal_error (yyconst char msg[] ); /* Done after the current pattern has been matched and before the * corresponding action - sets up yy_policy_parsertext. */ #define YY_DO_BEFORE_ACTION \ (yytext_ptr) = yy_bp; \ yy_policy_parserleng = (size_t) (yy_cp - yy_bp); \ (yy_hold_char) = *yy_cp; \ *yy_cp = '\0'; \ (yy_c_buf_p) = yy_cp; #define YY_NUM_RULES 60 #define YY_END_OF_BUFFER 61 /* This struct is not used in this scanner, but its presence is necessary. */ struct yy_trans_info { flex_int32_t yy_verify; flex_int32_t yy_nxt; }; static yyconst flex_int16_t yy_accept[1371] = { 0, 0, 0, 0, 0, 61, 59, 57, 58, 32, 6, 59, 16, 17, 25, 24, 26, 2, 2, 2, 15, 56, 22, 27, 23, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 59, 8, 7, 57, 19, 31, 28, 3, 29, 0, 2, 0, 0, 2, 2, 2, 13, 20, 18, 21, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 39, 55, 55, 55, 55, 55, 55, 55, 55, 30, 8, 0, 0, 0, 0, 2, 0, 0, 13, 2, 2, 0, 0, 13, 13, 13, 13, 55, 55, 49, 55, 42, 38, 45, 55, 55, 55, 55, 55, 55, 41, 55, 55, 55, 55, 55, 55, 43, 55, 55, 40, 1, 0, 0, 0, 0, 0, 2, 0, 0, 0, 13, 13, 13, 13, 0, 0, 14, 14, 0, 13, 0, 13, 13, 13, 13, 55, 55, 55, 55, 55, 44, 55, 55, 52, 55, 55, 55, 55, 55, 55, 54, 4, 0, 0, 0, 0, 2, 0, 0, 13, 13, 0, 13, 13, 13, 13, 0, 0, 14, 14, 14, 0, 0, 0, 13, 13, 13, 13, 13, 13, 13, 50, 55, 33, 5, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 0, 0, 0, 0, 0, 0, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 0, 0, 12, 14, 0, 0, 0, 0, 0, 13, 0, 13, 0, 13, 13, 13, 13, 47, 34, 46, 55, 55, 53, 55, 48, 55, 10, 10, 10, 0, 0, 0, 13, 13, 0, 13, 13, 13, 13, 13, 13, 0, 13, 13, 13, 13, 0, 0, 12, 12, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 13, 13, 13, 13, 13, 13, 13, 55, 55, 55, 35, 0, 0, 10, 10, 10, 10, 0, 0, 0, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 0, 0, 0, 12, 12, 12, 12, 0, 12, 0, 12, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 0, 13, 0, 13, 13, 13, 13, 36, 55, 51, 0, 11, 11, 11, 10, 0, 0, 13, 13, 0, 13, 13, 13, 13, 13, 13, 0, 13, 13, 13, 13, 13, 13, 0, 13, 13, 13, 13, 0, 0, 12, 12, 0, 12, 12, 12, 12, 0, 0, 0, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 0, 0, 0, 0, 0, 0, 0, 0, 13, 13, 13, 13, 13, 13, 13, 37, 0, 0, 0, 0, 0, 0, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 0, 0, 0, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 0, 0, 0, 0, 0, 12, 0, 12, 0, 12, 12, 12, 12, 13, 13, 13, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 0, 13, 0, 13, 13, 13, 13, 0, 0, 0, 0, 0, 0, 0, 13, 13, 0, 13, 13, 13, 13, 13, 13, 0, 13, 13, 13, 13, 13, 13, 0, 13, 13, 13, 13, 13, 13, 0, 13, 13, 13, 13, 0, 0, 12, 12, 0, 12, 12, 12, 12, 12, 12, 0, 12, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 0, 0, 0, 0, 0, 0, 0, 0, 13, 13, 13, 13, 13, 13, 13, 0, 0, 0, 0, 0, 0, 0, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 0, 0, 0, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 12, 0, 12, 12, 12, 12, 13, 13, 13, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 0, 13, 0, 13, 13, 13, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 0, 13, 0, 13, 13, 13, 13, 13, 13, 0, 13, 13, 13, 13, 13, 13, 0, 13, 13, 13, 13, 13, 13, 0, 13, 13, 13, 13, 13, 13, 0, 13, 13, 13, 13, 0, 0, 12, 12, 0, 12, 12, 12, 12, 12, 12, 0, 12, 12, 12, 12, 12, 12, 0, 12, 12, 12, 12, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 0, 0, 0, 0, 0, 0, 0, 0, 13, 13, 13, 13, 13, 13, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 0, 0, 0, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 12, 0, 12, 12, 12, 12, 13, 13, 13, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 0, 13, 0, 13, 13, 13, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 13, 0, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 0, 0, 12, 12, 0, 12, 12, 12, 12, 12, 12, 0, 12, 12, 12, 12, 12, 12, 0, 12, 12, 12, 12, 12, 12, 0, 12, 12, 12, 12, 12, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 0, 0, 0, 0, 0, 0, 0, 0, 13, 13, 13, 13, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 13, 13, 13, 13, 13, 13, 13, 0, 0, 0, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 12, 0, 12, 12, 12, 12, 13, 13, 13, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 13, 9, 9, 9, 9, 0, 0, 0, 0, 13, 13, 13, 13, 13, 13, 13, 0, 0, 0, 0, 12, 0, 12, 0, 12, 12, 12, 12, 12, 12, 0, 12, 12, 12, 12, 12, 12, 0, 12, 12, 12, 12, 12, 12, 0, 12, 12, 12, 12, 12, 12, 0, 12, 12, 12, 12, 12, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 0, 0, 0, 0, 0, 13, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 12, 0, 12, 12, 12, 12, 13, 13, 13, 13, 0, 0, 0, 0, 0, 13, 13, 13, 13, 0, 0, 0, 0, 0, 0, 12, 12, 0, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 12, 12, 13, 13, 13, 13, 0, 13, 13, 13, 13, 0, 0, 0, 0, 0, 0, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 13, 13, 13, 13, 13, 0, 0, 0, 0, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 0, 0, 0, 0, 0, 12, 13, 0, 0, 0, 0, 0, 12, 12, 12, 12, 0, 0, 0, 0, 0, 12, 12, 12, 12, 0, 12, 12, 12, 12, 0, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 0 } ; static yyconst flex_int32_t yy_ec[256] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 4, 5, 1, 1, 1, 6, 7, 8, 9, 10, 11, 1, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 21, 21, 22, 23, 24, 25, 26, 27, 1, 1, 28, 28, 28, 28, 29, 28, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 33, 34, 31, 31, 31, 35, 31, 31, 1, 1, 1, 1, 36, 1, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 31, 47, 48, 49, 50, 51, 31, 52, 53, 54, 55, 31, 31, 56, 57, 31, 1, 58, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 } ; static yyconst flex_int32_t yy_meta[59] = { 0, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 3, 4, 5, 6, 6, 6, 6, 6, 6, 6, 6, 7, 1, 1, 8, 1, 9, 9, 3, 3, 3, 3, 3, 3, 3, 9, 9, 9, 9, 9, 9, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1 } ; static yyconst flex_int16_t yy_base[1727] = { 0, 0, 0, 54, 55, 5173, 8703, 5170, 8703, 5145, 8703, 5164, 8703, 8703, 8703, 5143, 5142, 50, 78, 65, 5142, 8703, 5138, 5137, 5136, 89, 132, 5123, 5107, 37, 5081, 5078, 5096, 5091, 5081, 115, 5078, 63, 5086, 20, 62, 5076, 5067, 0, 8703, 5122, 8703, 8703, 8703, 160, 8703, 170, 180, 5091, 5075, 208, 136, 216, 226, 8703, 8703, 8703, 0, 254, 5067, 5062, 5056, 5054, 5053, 5040, 5052, 5041, 5050, 5037, 128, 5022, 115, 5013, 5009, 5012, 5007, 5018, 5003, 4999, 4999, 8703, 0, 282, 5037, 292, 5036, 302, 5025, 5015, 332, 0, 5022, 5021, 360, 370, 398, 195, 408, 416, 4998, 0, 4970, 0, 0, 0, 4971, 4956, 4968, 4964, 4961, 4951, 0, 4952, 4957, 4955, 4945, 4948, 4919, 0, 4913, 4904, 0, 444, 452, 4931, 4930, 462, 4929, 181, 4918, 4917, 4916, 470, 498, 241, 508, 4923, 4912, 516, 524, 534, 544, 572, 602, 610, 620, 4921, 4910, 4878, 51, 4849, 4836, 0, 4835, 4826, 0, 4825, 4823, 4833, 4821, 4829, 4813, 0, 0, 4853, 628, 4852, 4842, 193, 4822, 4812, 638, 668, 696, 726, 734, 744, 4795, 4784, 4777, 8703, 750, 269, 4771, 760, 4770, 239, 313, 770, 798, 347, 808, 315, 0, 4728, 0, 0, 4729, 4726, 4736, 4726, 4718, 4735, 4719, 4731, 816, 4758, 4743, 826, 4734, 4723, 4722, 4721, 834, 862, 385, 872, 345, 383, 882, 910, 431, 920, 429, 4711, 4708, 928, 8703, 956, 4717, 4710, 966, 4694, 481, 974, 984, 1012, 1042, 1050, 1060, 4685, 0, 0, 0, 4656, 4642, 0, 4645, 0, 4642, 153, 1068, 236, 4666, 4647, 4646, 1078, 1108, 1136, 1166, 1174, 1184, 4655, 483, 1192, 1220, 1250, 1258, 1268, 4654, 4634, 4631, 1274, 1304, 1332, 559, 4630, 4633, 1342, 4617, 4608, 4607, 1352, 4606, 557, 582, 1362, 1390, 653, 1400, 584, 4566, 4576, 4555, 0, 4587, 1408, 264, 266, 1418, 319, 4576, 4575, 4574, 1426, 1454, 683, 1464, 651, 681, 1474, 1502, 711, 1512, 709, 781, 783, 1522, 1550, 849, 1560, 847, 4573, 4570, 4569, 1570, 1598, 897, 4545, 1606, 1616, 1644, 4544, 1672, 1682, 293, 1691, 4553, 4550, 1701, 4531, 1707, 4513, 4496, 1717, 4494, 895, 1725, 1735, 1763, 1793, 1801, 1811, 4493, 0, 4436, 0, 1817, 1825, 340, 8703, 321, 4447, 4436, 1835, 1865, 1893, 1923, 1931, 1941, 4445, 938, 1949, 1977, 2007, 2015, 2025, 4444, 940, 2033, 2061, 2091, 2099, 2109, 4443, 4430, 4409, 2115, 2145, 2173, 4391, 2201, 2211, 593, 4390, 2222, 4389, 995, 996, 2232, 2260, 1027, 4378, 997, 381, 2270, 426, 4387, 4384, 2280, 4363, 4345, 4334, 2290, 4333, 1025, 1088, 2300, 2328, 1123, 2338, 1090, 0, 4332, 2348, 4331, 4318, 4297, 4279, 2358, 2386, 1151, 2396, 1121, 1149, 2406, 2434, 1207, 2444, 1205, 1230, 1232, 2454, 2482, 1289, 2492, 1287, 1315, 1317, 2502, 2530, 1377, 2540, 1375, 4268, 4267, 4266, 2550, 2578, 1441, 4265, 1241, 1343, 2588, 2616, 1489, 4262, 1439, 2624, 4234, 4232, 2634, 4213, 4187, 2640, 2650, 2678, 4169, 2706, 2716, 1487, 487, 489, 2727, 555, 2733, 4167, 4166, 2743, 4165, 2749, 4162, 4140, 2759, 4139, 1533, 2767, 2777, 2805, 2835, 2843, 2853, 4138, 2859, 4137, 4134, 2869, 4111, 4093, 4077, 2877, 2907, 2935, 2965, 2973, 2983, 4068, 1535, 2991, 3019, 3049, 3057, 3067, 4067, 1581, 3075, 3103, 3133, 3141, 3151, 4060, 1583, 3159, 3187, 3217, 3225, 3235, 4035, 4023, 4022, 3241, 3271, 3299, 4021, 3327, 3337, 1561, 3997, 3348, 3376, 3995, 3404, 3414, 1617, 3978, 3425, 3962, 3927, 3891, 3435, 3889, 1628, 1629, 3445, 3473, 1659, 3843, 1657, 589, 648, 3483, 679, 3851, 3850, 3493, 3805, 3803, 3802, 3503, 3782, 1746, 1748, 3513, 3541, 1778, 3551, 1776, 3763, 3561, 3762, 3761, 3750, 3747, 3569, 3599, 3627, 1850, 3637, 1848, 1876, 3647, 3675, 1908, 3685, 1878, 1906, 1960, 3695, 3723, 1992, 3733, 1962, 1990, 2044, 3743, 3771, 2076, 3781, 2046, 2074, 2125, 3791, 3819, 2160, 3829, 2127, 3724, 3699, 3697, 3839, 3867, 2188, 3696, 2136, 2158, 3877, 3905, 2247, 3676, 2186, 2223, 2245, 3915, 3943, 2315, 3651, 2281, 3951, 3659, 3658, 3961, 3638, 3967, 3621, 3619, 3977, 3618, 3607, 3983, 3993, 4021, 3606, 4049, 4059, 2313, 706, 787, 4070, 789, 4076, 3613, 3581, 4086, 3580, 4092, 3533, 3532, 4102, 3531, 2369, 4110, 4120, 4148, 4178, 4186, 4196, 3530, 4202, 3466, 3465, 4212, 3464, 3453, 4220, 4248, 2421, 845, 3452, 4258, 0, 4288, 4296, 4306, 3459, 2371, 4314, 0, 4344, 4352, 4362, 3423, 2419, 4370, 0, 4400, 4408, 4418, 3422, 2465, 4426, 0, 4456, 4464, 4474, 3415, 2467, 4482, 0, 4512, 4520, 4530, 3399, 3387, 3385, 4536, 4566, 4594, 3384, 4622, 4632, 2339, 3383, 4643, 4671, 3380, 4699, 4709, 2513, 3360, 4720, 4748, 3358, 4776, 4786, 2514, 4795, 4803, 4811, 3367, 3366, 4821, 3365, 3349, 3346, 4831, 3345, 2515, 2541, 4841, 4869, 2565, 3328, 2563, 893, 945, 4879, 1001, 3322, 3318, 4889, 3317, 3316, 3313, 4899, 3293, 2599, 2601, 4909, 4937, 2665, 4947, 2663, 3291, 4957, 3290, 3289, 3278, 4965, 4975, 3275, 3262, 5003, 5013, 2651, 1003, 2688, 2690, 1022, 2788, 2790, 2815, 1095, 2817, 2887, 2889, 1119, 2918, 2920, 2945, 1146, 2947, 3002, 3004, 1203, 3029, 3245, 3243, 3242, 5024, 5052, 3090, 3241, 2826, 2956, 5062, 5090, 3118, 3226, 3031, 3032, 3040, 5100, 5128, 3174, 3208, 3088, 3116, 3172, 5138, 5166, 3202, 3191, 3200, 5174, 5182, 3256, 8703, 5190, 3199, 3198, 5200, 3197, 5206, 3173, 3171, 5216, 3170, 3142, 5222, 5232, 5260, 3124, 5288, 5298, 3254, 1237, 1284, 5309, 1321, 5315, 3117, 3115, 5325, 3114, 5331, 3113, 3089, 5341, 3087, 3282, 5349, 5359, 0, 5389, 5397, 5407, 3086, 5413, 3068, 3038, 5423, 3037, 3036, 5431, 3033, 3284, 3309, 1323, 1373, 3310, 1437, 3311, 1485, 3359, 1539, 3361, 1541, 3386, 1587, 3388, 1589, 3003, 3001, 5439, 5469, 5497, 3000, 5525, 5535, 3426, 2974, 5546, 5574, 2948, 5602, 5612, 3446, 2944, 5623, 5651, 2939, 5679, 5689, 3457, 2919, 5700, 5728, 2890, 5756, 5766, 3458, 8703, 5775, 5783, 5791, 2898, 2896, 5801, 2891, 2870, 2854, 5811, 2828, 3494, 3524, 5821, 5849, 3584, 2814, 3525, 1633, 1635, 5859, 1654, 2819, 2799, 5869, 2768, 2760, 2744, 5879, 2717, 3526, 3582, 1690, 3610, 5887, 5895, 5903, 2701, 5911, 2699, 2697, 5921, 2692, 2652, 1752, 1754, 1773, 1845, 1882, 1884, 1903, 2625, 2600, 5927, 5957, 5985, 3662, 2579, 3552, 3612, 5995, 6023, 3710, 2554, 3660, 3708, 3754, 6033, 6061, 3806, 2506, 3755, 3756, 3804, 6071, 6099, 3854, 2483, 3830, 3852, 3878, 6109, 6137, 3892, 2466, 3890, 6145, 6153, 3930, 8703, 6161, 2445, 2397, 6171, 2380, 6177, 2314, 2246, 6187, 2212, 2177, 6193, 6203, 6231, 2175, 6259, 6269, 3916, 1966, 1968, 6280, 1987, 6286, 2159, 2138, 6296, 2134, 6302, 2129, 2110, 6312, 2092, 3928, 2050, 6318, 6326, 4008, 8703, 2073, 6336, 2055, 2026, 2052, 2071, 2132, 2156, 2183, 2219, 2243, 1998, 6346, 6374, 4036, 0, 1979, 6384, 0, 1961, 6412, 6422, 3994, 1932, 6433, 0, 1914, 6461, 6471, 4006, 1895, 6482, 0, 1877, 6510, 6520, 4034, 1837, 6531, 0, 1802, 6559, 6569, 4131, 1784, 6580, 0, 1765, 6608, 6618, 4132, 8703, 6627, 6635, 6643, 1757, 1726, 6653, 1718, 1702, 1683, 6663, 1658, 4133, 4158, 6673, 6701, 4235, 1646, 4159, 2311, 2375, 6711, 2377, 1637, 1630, 6721, 1592, 1544, 2417, 8703, 6729, 1513, 1488, 6739, 1465, 1430, 6745, 6755, 1391, 1316, 6783, 6793, 4160, 0, 4161, 4221, 0, 4233, 4269, 4270, 0, 4271, 4325, 4326, 0, 4327, 4381, 4382, 0, 4383, 4437, 4438, 0, 4439, 6802, 6810, 4497, 8703, 6818, 1305, 1286, 6828, 1269, 6834, 1243, 1239, 6844, 1234, 1175, 6850, 6860, 0, 1157, 6888, 6898, 4495, 2471, 2473, 6909, 2519, 6915, 1148, 1101, 6925, 1099, 2521, 2561, 6933, 2605, 1097, 1092, 6943, 1061, 4546, 4547, 0, 8703, 4548, 0, 1033, 0, 1014, 0, 988, 0, 965, 0, 957, 0, 8703, 6951, 6959, 6967, 951, 949, 6977, 947, 942, 921, 6987, 873, 4549, 4567, 0, 4578, 2607, 2695, 6997, 2724, 827, 2794, 2796, 7007, 2822, 7013, 809, 792, 7023, 761, 735, 0, 0, 0, 0, 0, 0, 0, 7029, 7037, 4609, 8703, 7045, 727, 708, 7055, 650, 7061, 629, 621, 7071, 595, 581, 0, 2894, 2924, 7079, 2926, 2952, 586, 7087, 535, 509, 0, 8703, 8703, 8703, 8703, 8703, 8703, 8703, 7095, 7103, 7111, 492, 463, 7121, 428, 409, 0, 3008, 7129, 324, 268, 7139, 238, 7145, 7153, 4658, 8703, 7161, 194, 192, 7171, 150, 8703, 7177, 7185, 7193, 146, 8703, 7201, 7209, 7217, 103, 7225, 7233, 4686, 8703, 7241, 7249, 4735, 8703, 8703, 8703, 8703, 7271, 7275, 7283, 7287, 7291, 7297, 7300, 7301, 7305, 7311, 7314, 7316, 7320, 7324, 7330, 7333, 7334, 7338, 7342, 7348, 7351, 7354, 7360, 7361, 7365, 7371, 7375, 7379, 7385, 7388, 7391, 7392, 7396, 7400, 7404, 7408, 7414, 7417, 7420, 7423, 7426, 7433, 7439, 7440, 7444, 7450, 7456, 7460, 7464, 7468, 7472, 7478, 7481, 7484, 7487, 7491, 7495, 7499, 7503, 7507, 7511, 7517, 7520, 7523, 7527, 7533, 7536, 7539, 7542, 7545, 7548, 7551, 7558, 7564, 7567, 7568, 7572, 7578, 7584, 7590, 7594, 7598, 7602, 7608, 7612, 7618, 7621, 7622, 7628, 7631, 7634, 7637, 7640, 7643, 7646, 7649, 7650, 7654, 7658, 7662, 7666, 7670, 7676, 7679, 7682, 7685, 7688, 7692, 7698, 7701, 7704, 7707, 7710, 7713, 7716, 7722, 7725, 7726, 7732, 7738, 7744, 7750, 7754, 7758, 7762, 7768, 7772, 7778, 7782, 7788, 7791, 7794, 7797, 7798, 7804, 7807, 7810, 7813, 7816, 7819, 7822, 7825, 7826, 7832, 7836, 7840, 7844, 7848, 7852, 7856, 7860, 7864, 7868, 7872, 7876, 7882, 7885, 7888, 7891, 7894, 7898, 7904, 7907, 7910, 7913, 7916, 7919, 7922, 7928, 7931, 7932, 7936, 7942, 7948, 7954, 7960, 7966, 7972, 7978, 7984, 7990, 7996, 8002, 8006, 8010, 8014, 8020, 8024, 8030, 8034, 8040, 8044, 8050, 8053, 8056, 8059, 8060, 8066, 8069, 8072, 8075, 8078, 8081, 8082, 8088, 8091, 8094, 8097, 8100, 8106, 8112, 8118, 8124, 8130, 8136, 8142, 8146, 8150, 8154, 8158, 8162, 8166, 8172, 8175, 8178, 8181, 8184, 8188, 8194, 8197, 8200, 8203, 8206, 8209, 8212, 8218, 8224, 8227, 8230, 8236, 8242, 8248, 8254, 8260, 8266, 8270, 8274, 8280, 8284, 8290, 8294, 8300, 8304, 8310, 8314, 8320, 8323, 8326, 8329, 8330, 8336, 8339, 8342, 8345, 8348, 8351, 8354, 8360, 8363, 8366, 8370, 8374, 8378, 8382, 8386, 8390, 8394, 8398, 8402, 8406, 8410, 8414, 8418, 8424, 8427, 8430, 8433, 8436, 8440, 8446, 8449, 8452, 8455, 8458, 8464, 8467, 8468, 8472, 8476, 8482, 8486, 8492, 8496, 8502, 8506, 8512, 8516, 8522, 8526, 8532, 8535, 8536, 8540, 8546, 8549, 8552, 8555, 8558, 8561, 8564, 8568, 8572, 8576, 8580, 8584, 8588, 8592, 8598, 8601, 8604, 8607, 8610, 8614, 8620, 8623, 8626, 8629, 8630, 8634, 8638, 8642, 8646, 8650, 8654, 8660, 8663, 8664, 8670, 8673, 8676, 8677, 8683, 8686, 8687, 8693, 8696 } ; static yyconst flex_int16_t yy_def[1727] = { 0, 1370, 1, 1371, 1371, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1372, 1370, 17, 18, 1370, 1370, 1370, 1370, 1370, 1370, 25, 26, 26, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 1370, 1373, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1374, 1375, 52, 55, 55, 1370, 1370, 1370, 1370, 26, 25, 26, 26, 63, 63, 26, 26, 26, 63, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 1370, 1373, 1370, 1376, 1370, 1377, 1370, 1378, 1379, 1370, 91, 91, 1370, 1370, 1370, 99, 100, 100, 63, 26, 26, 103, 103, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 1370, 1370, 1377, 1380, 1370, 1370, 1381, 1370, 1382, 1383, 1370, 137, 138, 138, 1370, 1384, 1370, 1370, 1370, 1370, 1370, 146, 146, 148, 148, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 1385, 1370, 1386, 1370, 1381, 1387, 1388, 1370, 1370, 1370, 177, 177, 179, 179, 1389, 1370, 1370, 1370, 1370, 1390, 1370, 1391, 1392, 1392, 1370, 193, 194, 194, 1392, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 1370, 1386, 1393, 1370, 1370, 1370, 1394, 1395, 1370, 218, 219, 219, 1396, 1396, 1370, 224, 225, 225, 1396, 1397, 1398, 1370, 1370, 1370, 1391, 1399, 1370, 1370, 1370, 1370, 1370, 1370, 240, 240, 242, 242, 26, 26, 26, 26, 26, 26, 26, 26, 26, 1400, 1370, 1401, 1370, 1402, 1403, 1370, 1370, 1370, 262, 262, 264, 264, 1370, 1370, 1370, 269, 269, 271, 271, 1404, 1405, 1370, 1370, 278, 279, 1406, 1407, 1370, 1408, 1370, 1409, 1370, 1410, 1411, 1411, 1370, 291, 292, 292, 1411, 1412, 1412, 1412, 1412, 1370, 1370, 1401, 1413, 1370, 1370, 1370, 1414, 1415, 1370, 309, 310, 310, 1416, 1416, 1370, 315, 316, 316, 1416, 1417, 1417, 1370, 322, 323, 323, 1417, 1370, 1418, 1419, 1370, 330, 331, 1420, 1370, 1370, 1370, 1421, 335, 1421, 1421, 1370, 1422, 1423, 1370, 1370, 1370, 1424, 1425, 1370, 1370, 1370, 1370, 1370, 1370, 353, 353, 355, 355, 1426, 1426, 1426, 1370, 1370, 1370, 1370, 1370, 1427, 1428, 1370, 1370, 1370, 370, 370, 372, 372, 1370, 1370, 1370, 377, 377, 379, 379, 1370, 1370, 1370, 384, 384, 386, 386, 1429, 1430, 1370, 1370, 1370, 1431, 393, 1431, 1431, 1432, 1370, 1433, 1434, 1434, 1370, 404, 405, 1435, 1434, 1436, 1370, 1437, 1370, 1438, 1370, 1439, 1370, 1440, 1370, 1441, 1442, 1442, 1370, 422, 423, 423, 1442, 1443, 1444, 1370, 1445, 1370, 1446, 1447, 1370, 434, 435, 435, 1448, 1448, 1370, 440, 441, 441, 1448, 1449, 1449, 1370, 447, 448, 448, 1449, 1450, 1450, 1370, 454, 455, 455, 1450, 1370, 1451, 1452, 1370, 462, 463, 1453, 1454, 1454, 1370, 468, 469, 1455, 1454, 1370, 1456, 1457, 1370, 1370, 1370, 1370, 1370, 1370, 1458, 480, 1458, 1458, 1459, 1460, 1370, 1370, 1370, 1461, 1462, 1370, 1370, 1370, 1463, 1464, 1370, 1370, 1370, 1370, 1370, 1370, 502, 502, 504, 504, 1370, 1465, 1466, 1370, 1370, 1467, 1468, 1370, 1370, 1370, 516, 516, 518, 518, 1370, 1370, 1370, 523, 523, 525, 525, 1370, 1370, 1370, 530, 530, 532, 532, 1370, 1370, 1370, 537, 537, 539, 539, 1469, 1470, 1370, 1370, 1370, 1471, 546, 1471, 1471, 1370, 1370, 1370, 1472, 553, 1472, 1472, 1473, 1370, 1474, 1370, 1475, 1370, 1476, 1477, 1477, 1370, 568, 569, 1478, 1477, 1370, 1479, 1370, 1480, 1370, 1481, 1370, 1482, 1370, 1483, 1370, 1484, 1485, 1485, 1370, 587, 588, 588, 1485, 1486, 1370, 1487, 1370, 1370, 1488, 1370, 1370, 599, 600, 600, 1489, 1489, 1370, 605, 606, 606, 1489, 1490, 1490, 1370, 612, 613, 613, 1490, 1491, 1491, 1370, 619, 620, 620, 1491, 1492, 1492, 1370, 626, 627, 627, 1492, 1370, 1493, 1494, 1370, 634, 635, 1495, 1496, 1496, 1370, 640, 641, 1497, 1496, 1498, 1498, 1370, 647, 648, 1499, 1498, 1370, 1500, 1501, 1370, 1370, 1370, 1502, 1503, 1370, 1370, 1370, 1370, 1370, 1370, 1504, 664, 1504, 1504, 1505, 1506, 1370, 1370, 1370, 1507, 1508, 1370, 1370, 1370, 1509, 1510, 1370, 1370, 1370, 1370, 1370, 1370, 686, 686, 688, 688, 1370, 1511, 1512, 1370, 1370, 1513, 1370, 698, 699, 1514, 1515, 1370, 1516, 703, 703, 705, 705, 1370, 1370, 1517, 710, 710, 712, 712, 1370, 1370, 1518, 717, 717, 719, 719, 1370, 1370, 1519, 724, 724, 726, 726, 1370, 1370, 1520, 731, 731, 733, 733, 1521, 1522, 1370, 1370, 1370, 1523, 740, 1523, 1523, 1370, 1370, 1370, 1524, 747, 1524, 1524, 1370, 1370, 1370, 1525, 754, 1525, 1525, 1370, 1370, 1370, 1370, 1526, 1370, 1527, 1370, 1528, 1370, 1529, 1530, 1530, 1370, 773, 774, 1531, 1530, 1370, 1532, 1370, 1533, 1370, 1534, 1370, 1535, 1370, 1536, 1370, 1537, 1538, 1538, 1370, 792, 793, 793, 1538, 1539, 1370, 1540, 1370, 1370, 1370, 1370, 1541, 1542, 803, 1542, 1542, 1543, 1544, 1544, 1545, 1544, 1546, 1546, 1547, 1546, 1548, 1548, 1549, 1548, 1550, 1550, 1551, 1550, 1552, 1552, 1553, 1552, 1370, 1554, 1555, 1370, 833, 834, 1556, 1557, 1557, 1370, 839, 840, 1558, 1557, 1559, 1559, 1370, 846, 847, 1560, 1559, 1561, 1561, 1370, 853, 854, 1562, 1561, 1370, 1370, 1370, 1370, 1370, 1563, 1564, 1370, 1370, 1370, 1565, 1566, 1370, 1370, 1370, 1370, 1370, 1370, 1567, 874, 1567, 1567, 1568, 1569, 1370, 1370, 1370, 1570, 1571, 1370, 1370, 1370, 1572, 1573, 1370, 1370, 1370, 1370, 1370, 1574, 896, 896, 898, 898, 1370, 1575, 1576, 1370, 1370, 1577, 1370, 1578, 1579, 1579, 1580, 1370, 1579, 1581, 1370, 1582, 1370, 1583, 1370, 1584, 1370, 1585, 1370, 1586, 1587, 1588, 1370, 1370, 1370, 1589, 929, 1589, 1589, 1370, 1370, 1370, 1590, 936, 1590, 1590, 1370, 1370, 1370, 1591, 943, 1591, 1591, 1370, 1370, 1370, 1592, 950, 1592, 1592, 1370, 1370, 1370, 1370, 1370, 1593, 1370, 1594, 1370, 1595, 1370, 1596, 1597, 1597, 1370, 970, 971, 1598, 1597, 1370, 1599, 1370, 1600, 1370, 1601, 1370, 1602, 1370, 1603, 1370, 1604, 1605, 1605, 1606, 1605, 1370, 1370, 1370, 1370, 1370, 1607, 1608, 1370, 1370, 1370, 1609, 1610, 1611, 1612, 1613, 1614, 1615, 1370, 1616, 1370, 1370, 1011, 1012, 1617, 1618, 1618, 1370, 1017, 1018, 1619, 1618, 1620, 1620, 1370, 1024, 1025, 1621, 1620, 1622, 1622, 1370, 1031, 1032, 1623, 1622, 1624, 1624, 1370, 1038, 1039, 1625, 1624, 1370, 1370, 1370, 1370, 1370, 1626, 1627, 1370, 1370, 1370, 1628, 1629, 1370, 1370, 1370, 1370, 1370, 1370, 1630, 1059, 1630, 1630, 1631, 1632, 1370, 1370, 1370, 1633, 1634, 1370, 1370, 1370, 1635, 1636, 1370, 1370, 1370, 1637, 1370, 1370, 1370, 1370, 1638, 1370, 1639, 1370, 1640, 1370, 1370, 1370, 1370, 1370, 1370, 1641, 1370, 1097, 1098, 1642, 1643, 1370, 1644, 1645, 1102, 1645, 1645, 1370, 1370, 1646, 1647, 1109, 1647, 1647, 1370, 1370, 1648, 1649, 1116, 1649, 1649, 1370, 1370, 1650, 1651, 1123, 1651, 1651, 1370, 1370, 1652, 1653, 1130, 1653, 1653, 1370, 1370, 1370, 1370, 1370, 1654, 1370, 1655, 1370, 1656, 1370, 1657, 1658, 1658, 1370, 1150, 1151, 1659, 1658, 1370, 1660, 1370, 1661, 1370, 1662, 1370, 1663, 1370, 1664, 1370, 1370, 1665, 1666, 1370, 1370, 1370, 1370, 1370, 1667, 1668, 1173, 1668, 1668, 1669, 1670, 1670, 1671, 1670, 1672, 1672, 1673, 1672, 1674, 1674, 1675, 1674, 1676, 1676, 1677, 1676, 1678, 1678, 1679, 1678, 1370, 1370, 1370, 1370, 1370, 1655, 1680, 1370, 1370, 1370, 1657, 1681, 1370, 1370, 1370, 1370, 1370, 1682, 1683, 1216, 1683, 1683, 1661, 1684, 1370, 1370, 1370, 1663, 1685, 1370, 1370, 1370, 1686, 1370, 1687, 1370, 1688, 1370, 1689, 1690, 1690, 1691, 1370, 1690, 1692, 1370, 1693, 1370, 1694, 1370, 1695, 1370, 1696, 1370, 1697, 1370, 1370, 1370, 1370, 1370, 1698, 1370, 1699, 1370, 1700, 1370, 1701, 1702, 1702, 1703, 1702, 1370, 1704, 1370, 1705, 1370, 1687, 1706, 1370, 1370, 1370, 1689, 1707, 1370, 1370, 1370, 1708, 1709, 1710, 1711, 1712, 1713, 1714, 1370, 1370, 1370, 1370, 1370, 1699, 1715, 1370, 1370, 1370, 1701, 1716, 1370, 1370, 1370, 1717, 1705, 1718, 1370, 1370, 1370, 1719, 1370, 1720, 1370, 1721, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1722, 1370, 1723, 1370, 1724, 1370, 1370, 1720, 1725, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1723, 1726, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 0, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370 } ; static yyconst flex_int16_t yy_nxt[8762] = { 0, 6, 7, 8, 9, 10, 11, 10, 12, 13, 14, 15, 16, 6, 6, 17, 17, 18, 19, 19, 19, 19, 19, 20, 21, 22, 23, 24, 25, 25, 26, 26, 27, 28, 26, 26, 6, 29, 25, 30, 25, 31, 32, 26, 33, 26, 26, 34, 26, 35, 36, 37, 38, 39, 40, 26, 41, 26, 42, 44, 44, 44, 44, 51, 80, 52, 52, 52, 52, 52, 52, 52, 52, 53, 53, 81, 66, 67, 54, 54, 57, 57, 57, 57, 57, 57, 68, 54, 54, 54, 54, 54, 54, 55, 55, 55, 55, 55, 56, 57, 57, 62, 199, 82, 63, 63, 63, 63, 63, 63, 63, 63, 53, 77, 83, 78, 1345, 63, 63, 62, 62, 62, 62, 62, 62, 62, 63, 63, 63, 63, 63, 63, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 1370, 74, 96, 96, 1336, 62, 62, 117, 1345, 114, 75, 300, 301, 118, 62, 62, 62, 62, 62, 62, 49, 49, 49, 49, 49, 49, 49, 49, 87, 115, 88, 88, 89, 90, 90, 90, 90, 90, 51, 141, 91, 91, 91, 91, 91, 91, 91, 91, 53, 53, 1345, 141, 1345, 92, 92, 151, 151, 151, 151, 151, 151, 1370, 92, 92, 92, 92, 92, 92, 95, 95, 95, 95, 95, 95, 95, 95, 96, 96, 96, 96, 96, 96, 96, 96, 97, 98, 99, 99, 100, 101, 101, 101, 101, 101, 300, 301, 1336, 145, 98, 102, 102, 182, 182, 182, 182, 182, 182, 147, 102, 102, 102, 102, 102, 102, 103, 103, 103, 103, 103, 103, 103, 103, 300, 301, 300, 301, 1336, 103, 103, 232, 232, 232, 232, 232, 232, 232, 103, 103, 103, 103, 103, 103, 127, 127, 127, 127, 127, 127, 127, 127, 128, 334, 130, 130, 130, 130, 130, 131, 132, 132, 51, 336, 133, 133, 133, 133, 133, 133, 133, 133, 53, 97, 98, 145, 98, 134, 134, 300, 301, 300, 301, 147, 1336, 147, 134, 134, 134, 134, 134, 134, 97, 98, 137, 137, 138, 139, 139, 139, 139, 139, 365, 365, 365, 145, 98, 140, 140, 245, 245, 245, 245, 245, 245, 178, 140, 140, 140, 140, 140, 140, 143, 144, 143, 143, 143, 143, 143, 143, 145, 98, 146, 146, 146, 146, 146, 146, 146, 146, 147, 97, 98, 97, 98, 148, 148, 267, 267, 267, 267, 267, 267, 178, 148, 148, 148, 148, 148, 148, 149, 149, 149, 149, 149, 150, 151, 151, 97, 1302, 148, 148, 148, 148, 148, 148, 148, 148, 152, 152, 152, 152, 152, 152, 152, 152, 97, 98, 1345, 145, 98, 152, 152, 274, 274, 274, 274, 274, 274, 178, 152, 152, 152, 152, 152, 152, 127, 127, 127, 127, 127, 127, 127, 127, 169, 169, 170, 171, 171, 171, 171, 171, 128, 1345, 172, 172, 172, 172, 172, 172, 145, 98, 177, 177, 177, 177, 177, 177, 177, 177, 178, 97, 98, 97, 98, 179, 179, 97, 98, 97, 98, 147, 1297, 178, 179, 179, 179, 179, 179, 179, 180, 180, 180, 180, 180, 181, 182, 182, 97, 1280, 179, 179, 179, 179, 179, 179, 179, 179, 185, 185, 185, 185, 185, 185, 185, 185, 186, 186, 187, 185, 185, 185, 185, 185, 142, 1336, 188, 188, 189, 190, 190, 190, 190, 190, 145, 98, 191, 191, 191, 191, 191, 191, 191, 191, 147, 97, 98, 239, 98, 192, 192, 340, 340, 340, 340, 340, 340, 241, 192, 192, 192, 192, 192, 192, 193, 193, 194, 195, 195, 195, 195, 195, 97, 98, 239, 98, 1336, 196, 196, 97, 98, 1217, 241, 334, 241, 1302, 196, 196, 196, 196, 196, 196, 97, 394, 192, 192, 192, 192, 192, 192, 192, 192, 197, 197, 197, 197, 197, 197, 197, 197, 145, 1302, 197, 197, 197, 197, 197, 197, 210, 1302, 212, 212, 212, 212, 212, 213, 214, 214, 97, 98, 218, 218, 219, 220, 220, 220, 220, 220, 97, 98, 1297, 145, 98, 221, 221, 358, 358, 358, 358, 358, 358, 263, 221, 221, 221, 221, 221, 221, 145, 98, 222, 222, 222, 222, 222, 222, 222, 222, 178, 97, 98, 97, 98, 223, 223, 375, 375, 375, 375, 375, 375, 263, 223, 223, 223, 223, 223, 223, 224, 224, 225, 226, 226, 226, 226, 226, 97, 98, 1297, 145, 98, 227, 227, 382, 382, 382, 382, 382, 382, 263, 227, 227, 227, 227, 227, 227, 97, 1297, 223, 223, 223, 223, 223, 223, 223, 223, 228, 228, 228, 228, 228, 228, 228, 228, 145, 1174, 228, 228, 228, 228, 228, 228, 232, 232, 232, 232, 232, 232, 232, 232, 233, 1280, 235, 235, 235, 235, 235, 236, 237, 237, 239, 98, 240, 240, 240, 240, 240, 240, 240, 240, 241, 239, 98, 97, 98, 242, 242, 97, 98, 97, 98, 270, 1280, 270, 242, 242, 242, 242, 242, 242, 243, 243, 243, 243, 243, 244, 245, 245, 97, 1280, 242, 242, 242, 242, 242, 242, 242, 242, 255, 255, 256, 257, 257, 257, 257, 257, 210, 1226, 258, 258, 258, 258, 258, 258, 145, 98, 262, 262, 262, 262, 262, 262, 262, 262, 263, 97, 98, 239, 98, 264, 264, 389, 389, 389, 389, 389, 389, 270, 264, 264, 264, 264, 264, 264, 265, 265, 265, 265, 265, 266, 267, 267, 97, 1302, 264, 264, 264, 264, 264, 264, 264, 264, 239, 98, 269, 269, 269, 269, 269, 269, 269, 269, 270, 97, 98, 97, 98, 271, 271, 398, 398, 398, 398, 398, 398, 241, 271, 271, 271, 271, 271, 271, 272, 272, 272, 272, 272, 273, 274, 274, 97, 1302, 271, 271, 271, 271, 271, 271, 271, 271, 278, 278, 279, 280, 280, 280, 280, 280, 97, 98, 97, 98, 1209, 281, 281, 97, 98, 1297, 263, 1297, 270, 1204, 281, 281, 281, 281, 281, 281, 282, 282, 283, 284, 284, 284, 284, 284, 233, 1131, 285, 285, 285, 285, 285, 285, 142, 1124, 286, 286, 287, 288, 288, 288, 288, 288, 239, 98, 289, 289, 289, 289, 289, 289, 289, 289, 241, 334, 1370, 334, 1117, 290, 290, 97, 98, 97, 98, 336, 336, 336, 290, 290, 290, 290, 290, 290, 291, 291, 292, 293, 293, 293, 293, 293, 97, 98, 1110, 352, 98, 294, 294, 485, 485, 485, 485, 485, 485, 354, 294, 294, 294, 294, 294, 294, 97, 1103, 290, 290, 290, 290, 290, 290, 290, 290, 295, 295, 295, 295, 295, 295, 295, 295, 239, 1280, 295, 295, 295, 295, 295, 295, 300, 301, 303, 303, 303, 303, 303, 304, 305, 305, 97, 98, 309, 309, 310, 311, 311, 311, 311, 311, 97, 98, 352, 98, 1280, 312, 312, 97, 98, 1166, 354, 1226, 354, 1226, 312, 312, 312, 312, 312, 312, 145, 98, 313, 313, 313, 313, 313, 313, 313, 313, 263, 97, 98, 145, 98, 314, 314, 507, 507, 507, 507, 507, 507, 371, 314, 314, 314, 314, 314, 314, 315, 315, 316, 317, 317, 317, 317, 317, 97, 98, 1226, 97, 98, 318, 318, 521, 521, 521, 521, 521, 521, 371, 318, 318, 318, 318, 318, 318, 97, 1217, 314, 314, 314, 314, 314, 314, 314, 314, 319, 319, 319, 319, 319, 319, 319, 319, 145, 1060, 319, 319, 319, 319, 319, 319, 239, 98, 320, 320, 320, 320, 320, 320, 320, 320, 270, 97, 98, 145, 98, 321, 321, 528, 528, 528, 528, 528, 528, 371, 321, 321, 321, 321, 321, 321, 322, 322, 323, 324, 324, 324, 324, 324, 239, 98, 97, 98, 1209, 325, 325, 97, 98, 1209, 378, 334, 378, 1209, 325, 325, 325, 325, 325, 325, 97, 394, 321, 321, 321, 321, 321, 321, 321, 321, 326, 326, 326, 326, 326, 326, 326, 326, 239, 1204, 326, 326, 326, 326, 326, 326, 330, 330, 331, 332, 332, 332, 332, 332, 97, 98, 1204, 239, 98, 333, 333, 535, 535, 535, 535, 535, 535, 378, 333, 333, 333, 333, 333, 333, 334, 1204, 335, 335, 335, 335, 335, 335, 335, 335, 336, 352, 98, 97, 98, 337, 337, 97, 98, 97, 98, 385, 1174, 385, 337, 337, 337, 337, 337, 337, 338, 338, 338, 338, 338, 339, 340, 340, 341, 1370, 343, 343, 343, 343, 343, 344, 345, 345, 346, 394, 348, 348, 348, 348, 348, 349, 350, 350, 352, 98, 353, 353, 353, 353, 353, 353, 353, 353, 354, 97, 98, 352, 98, 355, 355, 542, 542, 542, 542, 542, 542, 385, 355, 355, 355, 355, 355, 355, 356, 356, 356, 356, 356, 357, 358, 358, 97, 1242, 355, 355, 355, 355, 355, 355, 355, 355, 363, 363, 363, 364, 365, 365, 365, 365, 300, 301, 366, 366, 366, 366, 366, 366, 145, 98, 370, 370, 370, 370, 370, 370, 370, 370, 371, 97, 98, 334, 1010, 372, 372, 551, 551, 551, 551, 551, 551, 394, 372, 372, 372, 372, 372, 372, 373, 373, 373, 373, 373, 374, 375, 375, 97, 1166, 372, 372, 372, 372, 372, 372, 372, 372, 239, 98, 377, 377, 377, 377, 377, 377, 377, 377, 378, 97, 98, 479, 1166, 379, 379, 558, 558, 558, 558, 558, 558, 481, 379, 379, 379, 379, 379, 379, 380, 380, 380, 380, 380, 381, 382, 382, 97, 1166, 379, 379, 379, 379, 379, 379, 379, 379, 352, 98, 384, 384, 384, 384, 384, 384, 384, 384, 385, 97, 98, 97, 98, 386, 386, 97, 98, 97, 98, 354, 1074, 371, 386, 386, 386, 386, 386, 386, 387, 387, 387, 387, 387, 388, 389, 389, 97, 334, 386, 386, 386, 386, 386, 386, 386, 386, 334, 547, 393, 393, 393, 393, 393, 393, 393, 393, 394, 97, 98, 97, 98, 395, 395, 97, 98, 97, 98, 378, 1226, 385, 395, 395, 395, 395, 395, 395, 396, 396, 396, 396, 396, 397, 398, 398, 399, 399, 400, 401, 401, 401, 401, 401, 334, 479, 402, 402, 402, 402, 402, 402, 402, 402, 336, 554, 479, 1370, 1226, 403, 403, 97, 98, 97, 98, 1069, 481, 481, 403, 403, 403, 403, 403, 403, 404, 404, 405, 406, 406, 406, 406, 406, 97, 98, 1217, 479, 1209, 407, 407, 669, 669, 669, 669, 669, 669, 481, 407, 407, 407, 407, 407, 407, 408, 408, 408, 408, 408, 408, 408, 408, 334, 1209, 408, 408, 408, 408, 408, 408, 97, 98, 336, 409, 409, 410, 411, 411, 411, 411, 411, 341, 1052, 412, 412, 412, 412, 412, 412, 413, 413, 414, 415, 415, 415, 415, 415, 346, 1204, 416, 416, 416, 416, 416, 416, 142, 1204, 417, 417, 418, 419, 419, 419, 419, 419, 352, 98, 420, 420, 420, 420, 420, 420, 420, 420, 354, 501, 98, 97, 98, 421, 421, 97, 98, 97, 98, 503, 1047, 503, 421, 421, 421, 421, 421, 421, 422, 422, 423, 424, 424, 424, 424, 424, 97, 98, 1131, 501, 98, 425, 425, 691, 691, 691, 691, 691, 691, 503, 425, 425, 425, 425, 425, 425, 97, 951, 421, 421, 421, 421, 421, 421, 421, 421, 426, 426, 426, 426, 426, 426, 426, 426, 352, 1124, 426, 426, 426, 426, 426, 426, 428, 428, 429, 430, 430, 430, 430, 430, 365, 365, 365, 365, 365, 365, 365, 365, 97, 98, 434, 434, 435, 436, 436, 436, 436, 436, 97, 98, 944, 145, 98, 437, 437, 708, 708, 708, 708, 708, 708, 517, 437, 437, 437, 437, 437, 437, 145, 98, 438, 438, 438, 438, 438, 438, 438, 438, 371, 97, 98, 145, 98, 439, 439, 97, 98, 97, 98, 517, 1117, 517, 439, 439, 439, 439, 439, 439, 440, 440, 441, 442, 442, 442, 442, 442, 97, 98, 937, 239, 98, 443, 443, 715, 715, 715, 715, 715, 715, 524, 443, 443, 443, 443, 443, 443, 97, 1110, 439, 439, 439, 439, 439, 439, 439, 439, 444, 444, 444, 444, 444, 444, 444, 444, 145, 930, 444, 444, 444, 444, 444, 444, 239, 98, 445, 445, 445, 445, 445, 445, 445, 445, 378, 97, 98, 239, 98, 446, 446, 97, 98, 97, 98, 524, 1103, 524, 446, 446, 446, 446, 446, 446, 447, 447, 448, 449, 449, 449, 449, 449, 97, 98, 1174, 352, 98, 450, 450, 722, 722, 722, 722, 722, 722, 531, 450, 450, 450, 450, 450, 450, 97, 1010, 446, 446, 446, 446, 446, 446, 446, 446, 451, 451, 451, 451, 451, 451, 451, 451, 239, 995, 451, 451, 451, 451, 451, 451, 352, 98, 452, 452, 452, 452, 452, 452, 452, 452, 385, 97, 98, 352, 98, 453, 453, 97, 98, 97, 98, 531, 1166, 531, 453, 453, 453, 453, 453, 453, 454, 454, 455, 456, 456, 456, 456, 456, 97, 98, 1166, 501, 98, 457, 457, 729, 729, 729, 729, 729, 729, 538, 457, 457, 457, 457, 457, 457, 97, 1074, 453, 453, 453, 453, 453, 453, 453, 453, 458, 458, 458, 458, 458, 458, 458, 458, 352, 1074, 458, 458, 458, 458, 458, 458, 462, 462, 463, 464, 464, 464, 464, 464, 97, 98, 501, 98, 1074, 465, 465, 97, 98, 1069, 538, 334, 538, 1069, 465, 465, 465, 465, 465, 465, 334, 547, 466, 466, 466, 466, 466, 466, 466, 466, 394, 97, 98, 1370, 1069, 467, 467, 736, 736, 736, 736, 736, 736, 547, 467, 467, 467, 467, 467, 467, 468, 468, 469, 470, 470, 470, 470, 470, 97, 98, 1060, 334, 875, 471, 471, 745, 745, 745, 745, 745, 745, 547, 471, 471, 471, 471, 471, 471, 472, 472, 472, 472, 472, 472, 472, 472, 334, 1052, 472, 472, 472, 472, 472, 472, 97, 98, 394, 473, 479, 475, 475, 475, 475, 475, 476, 477, 477, 479, 554, 480, 480, 480, 480, 480, 480, 480, 480, 481, 97, 98, 1370, 1052, 482, 482, 752, 752, 752, 752, 752, 752, 554, 482, 482, 482, 482, 482, 482, 483, 483, 483, 483, 483, 484, 485, 485, 97, 98, 487, 487, 487, 487, 487, 488, 489, 489, 490, 479, 492, 492, 492, 492, 492, 493, 494, 494, 495, 554, 497, 497, 497, 497, 497, 498, 499, 499, 501, 98, 502, 502, 502, 502, 502, 502, 502, 502, 503, 97, 98, 663, 1052, 504, 504, 759, 759, 759, 759, 759, 759, 665, 504, 504, 504, 504, 504, 504, 505, 505, 505, 505, 505, 506, 507, 507, 97, 334, 504, 504, 504, 504, 504, 504, 504, 504, 508, 741, 510, 510, 510, 510, 510, 511, 512, 512, 145, 98, 516, 516, 516, 516, 516, 516, 516, 516, 517, 97, 98, 97, 98, 518, 518, 97, 98, 97, 98, 503, 1047, 517, 518, 518, 518, 518, 518, 518, 519, 519, 519, 519, 519, 520, 521, 521, 97, 1047, 518, 518, 518, 518, 518, 518, 518, 518, 239, 98, 523, 523, 523, 523, 523, 523, 523, 523, 524, 97, 98, 97, 98, 525, 525, 808, 808, 808, 808, 808, 808, 524, 525, 525, 525, 525, 525, 525, 526, 526, 526, 526, 526, 527, 528, 528, 97, 1047, 525, 525, 525, 525, 525, 525, 525, 525, 352, 98, 530, 530, 530, 530, 530, 530, 530, 530, 531, 97, 98, 97, 98, 532, 532, 97, 98, 97, 98, 531, 1131, 538, 532, 532, 532, 532, 532, 532, 533, 533, 533, 533, 533, 534, 535, 535, 97, 1124, 532, 532, 532, 532, 532, 532, 532, 532, 501, 98, 537, 537, 537, 537, 537, 537, 537, 537, 538, 479, 663, 663, 1117, 539, 539, 97, 98, 97, 98, 748, 755, 665, 539, 539, 539, 539, 539, 539, 540, 540, 540, 540, 540, 541, 542, 542, 97, 1370, 539, 539, 539, 539, 539, 539, 539, 539, 334, 665, 546, 546, 546, 546, 546, 546, 546, 546, 547, 97, 98, 663, 1110, 548, 548, 879, 879, 879, 879, 879, 879, 665, 548, 548, 548, 548, 548, 548, 549, 549, 549, 549, 549, 550, 551, 551, 479, 1103, 553, 553, 553, 553, 553, 553, 553, 553, 554, 685, 98, 97, 98, 555, 555, 97, 98, 97, 98, 687, 1010, 687, 555, 555, 555, 555, 555, 555, 556, 556, 556, 556, 556, 557, 558, 558, 559, 559, 560, 561, 561, 561, 561, 561, 473, 832, 562, 562, 562, 562, 562, 562, 563, 563, 564, 565, 565, 565, 565, 565, 479, 802, 566, 566, 566, 566, 566, 566, 566, 566, 481, 804, 804, 685, 98, 567, 567, 901, 901, 901, 901, 901, 901, 687, 567, 567, 567, 567, 567, 567, 568, 568, 569, 570, 570, 570, 570, 570, 145, 98, 97, 98, 995, 571, 571, 97, 98, 995, 704, 995, 704, 902, 571, 571, 571, 571, 571, 571, 572, 572, 572, 572, 572, 572, 572, 572, 479, 1074, 572, 572, 572, 572, 572, 572, 97, 98, 481, 97, 98, 573, 573, 573, 573, 573, 573, 574, 574, 575, 576, 576, 576, 576, 576, 490, 1074, 577, 577, 577, 577, 577, 577, 578, 578, 579, 580, 580, 580, 580, 580, 495, 889, 581, 581, 581, 581, 581, 581, 142, 1069, 582, 582, 583, 584, 584, 584, 584, 584, 501, 98, 585, 585, 585, 585, 585, 585, 585, 585, 503, 145, 98, 239, 98, 586, 586, 97, 98, 97, 98, 704, 1069, 711, 586, 586, 586, 586, 586, 586, 587, 587, 588, 589, 589, 589, 589, 589, 97, 98, 239, 98, 884, 590, 590, 97, 98, 1060, 711, 334, 711, 1052, 590, 590, 590, 590, 590, 590, 97, 741, 586, 586, 586, 586, 586, 586, 586, 586, 591, 591, 591, 591, 591, 591, 591, 591, 501, 1052, 591, 591, 591, 591, 591, 591, 592, 592, 593, 594, 594, 594, 594, 594, 508, 867, 595, 595, 595, 595, 595, 595, 97, 98, 599, 599, 600, 601, 601, 601, 601, 601, 352, 98, 97, 98, 1047, 602, 602, 97, 98, 1047, 718, 862, 718, 951, 602, 602, 602, 602, 602, 602, 145, 98, 603, 603, 603, 603, 603, 603, 603, 603, 517, 352, 98, 501, 98, 604, 604, 97, 98, 97, 98, 718, 755, 725, 604, 604, 604, 604, 604, 604, 605, 605, 606, 607, 607, 607, 607, 607, 97, 98, 501, 98, 944, 608, 608, 97, 98, 748, 725, 1370, 725, 937, 608, 608, 608, 608, 608, 608, 97, 741, 604, 604, 604, 604, 604, 604, 604, 604, 609, 609, 609, 609, 609, 609, 609, 609, 145, 741, 609, 609, 609, 609, 609, 609, 239, 98, 610, 610, 610, 610, 610, 610, 610, 610, 524, 685, 98, 97, 98, 611, 611, 97, 98, 930, 1010, 732, 832, 732, 611, 611, 611, 611, 611, 611, 612, 612, 613, 614, 614, 614, 614, 614, 685, 98, 334, 479, 995, 615, 615, 995, 902, 902, 732, 1370, 741, 748, 615, 615, 615, 615, 615, 615, 97, 748, 611, 611, 611, 611, 611, 611, 611, 611, 616, 616, 616, 616, 616, 616, 616, 616, 239, 902, 616, 616, 616, 616, 616, 616, 352, 98, 617, 617, 617, 617, 617, 617, 617, 617, 531, 895, 889, 479, 889, 618, 618, 934, 934, 934, 934, 934, 934, 748, 618, 618, 618, 618, 618, 618, 619, 619, 620, 621, 621, 621, 621, 621, 889, 884, 884, 663, 884, 622, 622, 941, 941, 941, 941, 941, 941, 755, 622, 622, 622, 622, 622, 622, 97, 875, 618, 618, 618, 618, 618, 618, 618, 618, 623, 623, 623, 623, 623, 623, 623, 623, 352, 665, 623, 623, 623, 623, 623, 623, 501, 98, 624, 624, 624, 624, 624, 624, 624, 624, 538, 867, 867, 1370, 867, 625, 625, 948, 948, 948, 948, 948, 948, 755, 625, 625, 625, 625, 625, 625, 626, 626, 627, 628, 628, 628, 628, 628, 862, 862, 862, 663, 951, 629, 629, 955, 955, 955, 955, 955, 955, 755, 629, 629, 629, 629, 629, 629, 97, 944, 625, 625, 625, 625, 625, 625, 625, 625, 630, 630, 630, 630, 630, 630, 630, 630, 501, 937, 630, 630, 630, 630, 630, 630, 634, 634, 635, 636, 636, 636, 636, 636, 930, 928, 832, 873, 633, 637, 637, 956, 956, 956, 956, 956, 956, 875, 637, 637, 637, 637, 637, 637, 334, 804, 638, 638, 638, 638, 638, 638, 638, 638, 547, 97, 98, 802, 913, 639, 639, 598, 692, 902, 902, 687, 889, 804, 639, 639, 639, 639, 639, 639, 640, 640, 641, 642, 642, 642, 642, 642, 1370, 802, 97, 98, 889, 643, 643, 679, 884, 884, 804, 804, 704, 674, 643, 643, 643, 643, 643, 643, 644, 644, 644, 644, 644, 644, 644, 644, 334, 875, 644, 644, 644, 644, 644, 644, 867, 867, 547, 479, 657, 645, 645, 645, 645, 645, 645, 645, 645, 554, 97, 98, 97, 98, 646, 646, 862, 862, 652, 755, 711, 554, 718, 646, 646, 646, 646, 646, 646, 647, 647, 648, 649, 649, 649, 649, 649, 97, 98, 97, 98, 748, 650, 650, 547, 741, 832, 725, 633, 732, 685, 650, 650, 650, 650, 650, 650, 651, 651, 651, 651, 651, 651, 651, 651, 479, 501, 651, 651, 651, 651, 651, 651, 352, 239, 554, 652, 334, 654, 654, 654, 654, 654, 655, 656, 656, 657, 930, 659, 659, 659, 659, 659, 660, 661, 661, 663, 479, 664, 664, 664, 664, 664, 664, 664, 664, 665, 937, 663, 873, 145, 666, 666, 804, 598, 692, 692, 692, 944, 951, 666, 666, 666, 666, 666, 666, 667, 667, 667, 667, 667, 668, 669, 669, 97, 98, 671, 671, 671, 671, 671, 672, 673, 673, 674, 873, 676, 676, 676, 676, 676, 677, 678, 678, 679, 875, 681, 681, 681, 681, 681, 682, 683, 683, 685, 98, 686, 686, 686, 686, 686, 686, 686, 686, 687, 1370, 873, 895, 98, 688, 688, 685, 679, 679, 679, 875, 875, 897, 688, 688, 688, 688, 688, 688, 689, 689, 689, 689, 689, 690, 691, 691, 97, 334, 688, 688, 688, 688, 688, 688, 688, 688, 692, 930, 694, 694, 694, 694, 694, 695, 696, 696, 698, 698, 699, 700, 700, 700, 700, 700, 701, 674, 674, 97, 98, 702, 702, 1064, 1064, 1064, 1064, 1064, 1064, 897, 702, 702, 702, 702, 702, 702, 145, 98, 703, 703, 703, 703, 703, 703, 703, 703, 704, 895, 98, 1370, 674, 705, 705, 665, 481, 657, 657, 897, 657, 930, 705, 705, 705, 705, 705, 705, 706, 706, 706, 706, 706, 707, 708, 708, 97, 652, 705, 705, 705, 705, 705, 705, 705, 705, 239, 98, 710, 710, 710, 710, 710, 710, 710, 710, 711, 652, 652, 334, 755, 712, 712, 1107, 1107, 1107, 1107, 1107, 1107, 930, 712, 712, 712, 712, 712, 712, 713, 713, 713, 713, 713, 714, 715, 715, 97, 748, 712, 712, 712, 712, 712, 712, 712, 712, 352, 98, 717, 717, 717, 717, 717, 717, 717, 717, 718, 741, 739, 479, 633, 719, 719, 1114, 1114, 1114, 1114, 1114, 1114, 937, 719, 719, 719, 719, 719, 719, 720, 720, 720, 720, 720, 721, 722, 722, 97, 461, 719, 719, 719, 719, 719, 719, 719, 719, 501, 98, 724, 724, 724, 724, 724, 724, 724, 724, 725, 1370, 479, 663, 598, 726, 726, 433, 508, 692, 692, 937, 937, 944, 726, 726, 726, 726, 726, 726, 727, 727, 727, 727, 727, 728, 729, 729, 97, 679, 726, 726, 726, 726, 726, 726, 726, 726, 685, 98, 731, 731, 731, 731, 731, 731, 731, 731, 732, 679, 495, 1370, 674, 733, 733, 1121, 1121, 1121, 1121, 1121, 1121, 944, 733, 733, 733, 733, 733, 733, 734, 734, 734, 734, 734, 735, 736, 736, 97, 663, 733, 733, 733, 733, 733, 733, 733, 733, 334, 944, 740, 740, 740, 740, 740, 740, 740, 740, 741, 674, 490, 873, 665, 742, 742, 1128, 1128, 1128, 1128, 1128, 1128, 951, 742, 742, 742, 742, 742, 742, 743, 743, 743, 743, 743, 744, 745, 745, 479, 1370, 747, 747, 747, 747, 747, 747, 747, 747, 748, 951, 657, 873, 657, 749, 749, 1135, 1135, 1135, 1135, 1135, 1135, 951, 749, 749, 749, 749, 749, 749, 750, 750, 750, 750, 750, 751, 752, 752, 663, 1058, 754, 754, 754, 754, 754, 754, 754, 754, 755, 1060, 473, 97, 98, 756, 756, 1136, 1136, 1136, 1136, 1136, 1136, 897, 756, 756, 756, 756, 756, 756, 757, 757, 757, 757, 757, 758, 759, 759, 760, 760, 761, 762, 762, 762, 762, 762, 652, 652, 763, 763, 763, 763, 763, 763, 764, 764, 765, 766, 766, 766, 766, 766, 657, 652, 767, 767, 767, 767, 767, 767, 768, 768, 769, 770, 770, 770, 770, 770, 663, 334, 771, 771, 771, 771, 771, 771, 771, 771, 665, 1103, 554, 479, 394, 772, 772, 1165, 1165, 1165, 1165, 1165, 1165, 1110, 772, 772, 772, 772, 772, 772, 773, 773, 774, 775, 775, 775, 775, 775, 547, 633, 461, 663, 501, 776, 776, 1178, 1178, 1178, 1178, 1178, 1178, 1117, 776, 776, 776, 776, 776, 776, 777, 777, 777, 777, 777, 777, 777, 777, 663, 352, 777, 777, 777, 777, 777, 777, 239, 145, 665, 97, 98, 778, 778, 778, 778, 778, 778, 779, 779, 780, 781, 781, 781, 781, 781, 674, 598, 782, 782, 782, 782, 782, 782, 783, 783, 784, 785, 785, 785, 785, 785, 679, 433, 786, 786, 786, 786, 786, 786, 142, 508, 787, 787, 788, 789, 789, 789, 789, 789, 685, 98, 790, 790, 790, 790, 790, 790, 790, 790, 687, 873, 1058, 1058, 508, 791, 791, 508, 501, 495, 495, 1124, 1131, 1060, 791, 791, 791, 791, 791, 791, 792, 792, 793, 794, 794, 794, 794, 794, 1370, 1058, 1172, 334, 495, 795, 795, 490, 490, 490, 1060, 1060, 1174, 1103, 795, 795, 795, 795, 795, 795, 97, 481, 791, 791, 791, 791, 791, 791, 791, 791, 796, 796, 796, 796, 796, 796, 796, 796, 685, 336, 796, 796, 796, 796, 796, 796, 797, 797, 798, 799, 799, 799, 799, 799, 692, 473, 800, 800, 800, 800, 800, 800, 802, 1370, 803, 803, 803, 803, 803, 803, 803, 803, 804, 1103, 473, 334, 473, 805, 805, 1221, 1221, 1221, 1221, 1221, 1221, 1103, 805, 805, 805, 805, 805, 805, 806, 806, 806, 806, 806, 807, 808, 808, 145, 98, 810, 810, 810, 810, 810, 810, 810, 810, 704, 479, 1370, 479, 554, 811, 811, 547, 545, 461, 329, 1110, 1110, 1110, 811, 811, 811, 811, 811, 811, 97, 515, 811, 811, 811, 811, 811, 811, 811, 811, 813, 813, 813, 813, 813, 813, 813, 813, 145, 433, 813, 813, 813, 813, 813, 813, 239, 98, 814, 814, 814, 814, 814, 814, 814, 814, 711, 663, 1370, 663, 308, 815, 815, 508, 508, 495, 495, 1117, 1117, 1117, 815, 815, 815, 815, 815, 815, 97, 346, 815, 815, 815, 815, 815, 815, 815, 815, 817, 817, 817, 817, 817, 817, 817, 817, 239, 490, 817, 817, 817, 817, 817, 817, 352, 98, 818, 818, 818, 818, 818, 818, 818, 818, 718, 873, 1370, 873, 490, 819, 819, 341, 481, 473, 473, 1124, 1124, 1124, 819, 819, 819, 819, 819, 819, 97, 394, 819, 819, 819, 819, 819, 819, 819, 819, 821, 821, 821, 821, 821, 821, 821, 821, 352, 461, 821, 821, 821, 821, 821, 821, 501, 98, 822, 822, 822, 822, 822, 822, 822, 822, 725, 1058, 1370, 1058, 329, 823, 823, 352, 239, 145, 433, 1131, 1131, 1131, 823, 823, 823, 823, 823, 823, 97, 308, 823, 823, 823, 823, 823, 823, 823, 823, 825, 825, 825, 825, 825, 825, 825, 825, 501, 427, 825, 825, 825, 825, 825, 825, 685, 98, 826, 826, 826, 826, 826, 826, 826, 826, 732, 352, 346, 1215, 346, 827, 827, 1255, 1255, 1255, 1255, 1255, 1255, 1217, 827, 827, 827, 827, 827, 827, 97, 346, 827, 827, 827, 827, 827, 827, 827, 827, 829, 829, 829, 829, 829, 829, 829, 829, 685, 341, 829, 829, 829, 829, 829, 829, 833, 833, 834, 835, 835, 835, 835, 835, 1172, 1370, 1172, 1215, 341, 836, 836, 341, 336, 394, 1174, 1174, 1174, 1217, 836, 836, 836, 836, 836, 836, 334, 1370, 837, 837, 837, 837, 837, 837, 837, 837, 741, 1217, 1215, 392, 329, 838, 838, 230, 369, 308, 217, 362, 1217, 361, 838, 838, 838, 838, 838, 838, 839, 839, 840, 841, 841, 841, 841, 841, 360, 359, 346, 346, 233, 842, 842, 1325, 1325, 1325, 1325, 1325, 1325, 341, 842, 842, 842, 842, 842, 842, 843, 843, 843, 843, 843, 843, 843, 843, 334, 341, 843, 843, 843, 843, 843, 843, 336, 329, 741, 479, 230, 844, 844, 844, 844, 844, 844, 844, 844, 748, 239, 145, 308, 217, 845, 845, 1355, 1355, 1355, 1355, 1355, 1355, 210, 845, 845, 845, 845, 845, 845, 846, 846, 847, 848, 848, 848, 848, 848, 299, 298, 297, 296, 239, 849, 849, 1368, 1368, 1368, 1368, 1368, 1368, 233, 849, 849, 849, 849, 849, 849, 850, 850, 850, 850, 850, 850, 850, 850, 479, 233, 850, 850, 850, 850, 850, 850, 233, 277, 748, 663, 230, 851, 851, 851, 851, 851, 851, 851, 851, 755, 261, 217, 136, 210, 852, 852, 1369, 1369, 1369, 1369, 1369, 1369, 210, 852, 852, 852, 852, 852, 852, 853, 853, 854, 855, 855, 855, 855, 855, 210, 254, 253, 252, 251, 856, 856, 250, 249, 248, 247, 246, 233, 233, 856, 856, 856, 856, 856, 856, 857, 857, 857, 857, 857, 857, 857, 857, 663, 231, 857, 857, 857, 857, 857, 857, 230, 145, 755, 858, 858, 858, 858, 858, 858, 858, 858, 859, 859, 859, 859, 859, 860, 861, 861, 861, 861, 861, 861, 861, 861, 861, 861, 862, 217, 864, 864, 864, 864, 864, 865, 866, 866, 867, 136, 869, 869, 869, 869, 869, 870, 871, 871, 873, 128, 874, 874, 874, 874, 874, 874, 874, 874, 875, 210, 210, 209, 208, 876, 876, 207, 206, 205, 204, 203, 202, 201, 876, 876, 876, 876, 876, 876, 877, 877, 877, 877, 877, 878, 879, 879, 97, 98, 881, 881, 881, 881, 881, 882, 883, 883, 884, 200, 886, 886, 886, 886, 886, 887, 888, 888, 889, 198, 891, 891, 891, 891, 891, 892, 893, 893, 895, 98, 896, 896, 896, 896, 896, 896, 896, 896, 897, 53, 145, 184, 87, 898, 898, 176, 136, 53, 128, 128, 128, 168, 898, 898, 898, 898, 898, 898, 899, 899, 899, 899, 899, 900, 901, 901, 97, 167, 898, 898, 898, 898, 898, 898, 898, 898, 902, 166, 904, 904, 904, 904, 904, 905, 906, 906, 907, 907, 908, 909, 909, 909, 909, 909, 802, 165, 910, 910, 910, 910, 910, 910, 910, 910, 804, 164, 163, 162, 161, 911, 911, 160, 159, 158, 157, 156, 155, 154, 911, 911, 911, 911, 911, 911, 914, 914, 914, 914, 914, 914, 914, 914, 802, 153, 914, 914, 914, 914, 914, 914, 142, 141, 804, 334, 136, 929, 929, 929, 929, 929, 929, 929, 929, 930, 53, 128, 128, 126, 931, 931, 125, 124, 123, 122, 121, 120, 119, 931, 931, 931, 931, 931, 931, 932, 932, 932, 932, 932, 933, 934, 934, 479, 116, 936, 936, 936, 936, 936, 936, 936, 936, 937, 113, 112, 111, 110, 938, 938, 109, 108, 107, 106, 105, 104, 53, 938, 938, 938, 938, 938, 938, 939, 939, 939, 939, 939, 940, 941, 941, 663, 94, 943, 943, 943, 943, 943, 943, 943, 943, 944, 45, 85, 84, 79, 945, 945, 76, 73, 72, 71, 70, 69, 65, 945, 945, 945, 945, 945, 945, 946, 946, 946, 946, 946, 947, 948, 948, 873, 64, 950, 950, 950, 950, 950, 950, 950, 950, 951, 61, 60, 59, 58, 952, 952, 50, 48, 47, 46, 45, 1370, 1370, 952, 952, 952, 952, 952, 952, 953, 953, 953, 953, 953, 954, 955, 955, 861, 861, 861, 861, 861, 861, 861, 861, 956, 956, 956, 956, 956, 956, 956, 956, 957, 957, 958, 959, 959, 959, 959, 959, 862, 1370, 960, 960, 960, 960, 960, 960, 961, 961, 962, 963, 963, 963, 963, 963, 867, 1370, 964, 964, 964, 964, 964, 964, 965, 965, 966, 967, 967, 967, 967, 967, 873, 1370, 968, 968, 968, 968, 968, 968, 968, 968, 875, 1370, 1370, 1370, 1370, 969, 969, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 969, 969, 969, 969, 969, 969, 970, 970, 971, 972, 972, 972, 972, 972, 1370, 1370, 1370, 1370, 1370, 973, 973, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 973, 973, 973, 973, 973, 973, 974, 974, 974, 974, 974, 974, 974, 974, 873, 1370, 974, 974, 974, 974, 974, 974, 1370, 1370, 875, 97, 98, 975, 975, 975, 975, 975, 975, 976, 976, 977, 978, 978, 978, 978, 978, 884, 1370, 979, 979, 979, 979, 979, 979, 980, 980, 981, 982, 982, 982, 982, 982, 889, 1370, 983, 983, 983, 983, 983, 983, 142, 1370, 984, 984, 985, 986, 986, 986, 986, 986, 895, 98, 987, 987, 987, 987, 987, 987, 987, 987, 897, 1370, 1370, 1370, 1370, 988, 988, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 988, 988, 988, 988, 988, 988, 97, 1370, 988, 988, 988, 988, 988, 988, 988, 988, 990, 990, 990, 990, 990, 990, 990, 990, 895, 1370, 990, 990, 990, 990, 990, 990, 991, 991, 992, 993, 993, 993, 993, 993, 902, 1370, 994, 994, 994, 994, 994, 994, 995, 1370, 997, 997, 997, 997, 997, 998, 999, 999, 1011, 1011, 1012, 1013, 1013, 1013, 1013, 1013, 1370, 1370, 1370, 1370, 1370, 1014, 1014, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1014, 1014, 1014, 1014, 1014, 1014, 334, 1370, 1015, 1015, 1015, 1015, 1015, 1015, 1015, 1015, 930, 1370, 1370, 1370, 1370, 1016, 1016, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1016, 1016, 1016, 1016, 1016, 1016, 1017, 1017, 1018, 1019, 1019, 1019, 1019, 1019, 1370, 1370, 1370, 1370, 1370, 1020, 1020, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1020, 1020, 1020, 1020, 1020, 1020, 1021, 1021, 1021, 1021, 1021, 1021, 1021, 1021, 334, 1370, 1021, 1021, 1021, 1021, 1021, 1021, 1370, 1370, 930, 479, 1370, 1022, 1022, 1022, 1022, 1022, 1022, 1022, 1022, 937, 1370, 1370, 1370, 1370, 1023, 1023, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1023, 1023, 1023, 1023, 1023, 1023, 1024, 1024, 1025, 1026, 1026, 1026, 1026, 1026, 1370, 1370, 1370, 1370, 1370, 1027, 1027, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1027, 1027, 1027, 1027, 1027, 1027, 1028, 1028, 1028, 1028, 1028, 1028, 1028, 1028, 479, 1370, 1028, 1028, 1028, 1028, 1028, 1028, 1370, 1370, 937, 663, 1370, 1029, 1029, 1029, 1029, 1029, 1029, 1029, 1029, 944, 1370, 1370, 1370, 1370, 1030, 1030, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1030, 1030, 1030, 1030, 1030, 1030, 1031, 1031, 1032, 1033, 1033, 1033, 1033, 1033, 1370, 1370, 1370, 1370, 1370, 1034, 1034, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1034, 1034, 1034, 1034, 1034, 1034, 1035, 1035, 1035, 1035, 1035, 1035, 1035, 1035, 663, 1370, 1035, 1035, 1035, 1035, 1035, 1035, 1370, 1370, 944, 873, 1370, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 951, 1370, 1370, 1370, 1370, 1037, 1037, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1037, 1037, 1037, 1037, 1037, 1037, 1038, 1038, 1039, 1040, 1040, 1040, 1040, 1040, 1370, 1370, 1370, 1370, 1370, 1041, 1041, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1041, 1041, 1041, 1041, 1041, 1041, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 873, 1370, 1042, 1042, 1042, 1042, 1042, 1042, 1370, 1370, 951, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1044, 1044, 1044, 1044, 1044, 1045, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1047, 1370, 1049, 1049, 1049, 1049, 1049, 1050, 1051, 1051, 1052, 1370, 1054, 1054, 1054, 1054, 1054, 1055, 1056, 1056, 1058, 1370, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1060, 1370, 1370, 1370, 1370, 1061, 1061, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1061, 1061, 1061, 1061, 1061, 1061, 1062, 1062, 1062, 1062, 1062, 1063, 1064, 1064, 97, 98, 1066, 1066, 1066, 1066, 1066, 1067, 1068, 1068, 1069, 1370, 1071, 1071, 1071, 1071, 1071, 1072, 1073, 1073, 1074, 1370, 1076, 1076, 1076, 1076, 1076, 1077, 1078, 1078, 1081, 1081, 1081, 1081, 1081, 1081, 1081, 1081, 1082, 1082, 1082, 1082, 1082, 1083, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1085, 1085, 1086, 1087, 1087, 1087, 1087, 1087, 995, 1370, 1088, 1088, 1088, 1088, 1088, 1088, 1097, 1097, 1098, 1099, 1099, 1099, 1099, 1099, 1100, 1370, 1370, 1370, 1370, 1101, 1101, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1101, 1101, 1101, 1101, 1101, 1101, 334, 1370, 1102, 1102, 1102, 1102, 1102, 1102, 1102, 1102, 1103, 1370, 1370, 1370, 1370, 1104, 1104, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1104, 1104, 1104, 1104, 1104, 1104, 1105, 1105, 1105, 1105, 1105, 1106, 1107, 1107, 479, 1370, 1109, 1109, 1109, 1109, 1109, 1109, 1109, 1109, 1110, 1370, 1370, 1370, 1370, 1111, 1111, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1111, 1111, 1111, 1111, 1111, 1111, 1112, 1112, 1112, 1112, 1112, 1113, 1114, 1114, 663, 1370, 1116, 1116, 1116, 1116, 1116, 1116, 1116, 1116, 1117, 1370, 1370, 1370, 1370, 1118, 1118, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1118, 1118, 1118, 1118, 1118, 1118, 1119, 1119, 1119, 1119, 1119, 1120, 1121, 1121, 873, 1370, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1124, 1370, 1370, 1370, 1370, 1125, 1125, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1125, 1125, 1125, 1125, 1125, 1125, 1126, 1126, 1126, 1126, 1126, 1127, 1128, 1128, 1058, 1370, 1130, 1130, 1130, 1130, 1130, 1130, 1130, 1130, 1131, 1370, 1370, 1370, 1370, 1132, 1132, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1132, 1132, 1132, 1132, 1132, 1132, 1133, 1133, 1133, 1133, 1133, 1134, 1135, 1135, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1136, 1136, 1136, 1136, 1136, 1136, 1136, 1136, 1137, 1137, 1138, 1139, 1139, 1139, 1139, 1139, 1047, 1370, 1140, 1140, 1140, 1140, 1140, 1140, 1141, 1141, 1142, 1143, 1143, 1143, 1143, 1143, 1052, 1370, 1144, 1144, 1144, 1144, 1144, 1144, 1145, 1145, 1146, 1147, 1147, 1147, 1147, 1147, 1058, 1370, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1060, 1370, 1370, 1370, 1370, 1149, 1149, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1149, 1149, 1149, 1149, 1149, 1149, 1150, 1150, 1151, 1152, 1152, 1152, 1152, 1152, 1370, 1370, 1370, 1370, 1370, 1153, 1153, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1153, 1153, 1153, 1153, 1153, 1153, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1058, 1370, 1154, 1154, 1154, 1154, 1154, 1154, 1370, 1370, 1060, 97, 98, 1155, 1155, 1155, 1155, 1155, 1155, 1156, 1156, 1157, 1158, 1158, 1158, 1158, 1158, 1069, 1370, 1159, 1159, 1159, 1159, 1159, 1159, 1160, 1160, 1161, 1162, 1162, 1162, 1162, 1162, 1074, 1370, 1163, 1163, 1163, 1163, 1163, 1163, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1165, 1165, 1165, 1165, 1165, 1165, 1165, 1165, 1166, 1370, 1168, 1168, 1168, 1168, 1168, 1169, 1170, 1170, 1172, 1370, 1173, 1173, 1173, 1173, 1173, 1173, 1173, 1173, 1174, 1370, 1370, 1370, 1370, 1175, 1175, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1175, 1175, 1175, 1175, 1175, 1175, 1176, 1176, 1176, 1176, 1176, 1177, 1178, 1178, 334, 1370, 1180, 1180, 1180, 1180, 1180, 1180, 1180, 1180, 1103, 1370, 1370, 1370, 1370, 1181, 1181, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1181, 1181, 1181, 1181, 1181, 1181, 1183, 1183, 1183, 1183, 1183, 1183, 1183, 1183, 334, 1370, 1183, 1183, 1183, 1183, 1183, 1183, 1370, 1370, 1103, 479, 1370, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1110, 1370, 1370, 1370, 1370, 1185, 1185, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1185, 1185, 1185, 1185, 1185, 1185, 1187, 1187, 1187, 1187, 1187, 1187, 1187, 1187, 479, 1370, 1187, 1187, 1187, 1187, 1187, 1187, 1370, 1370, 1110, 663, 1370, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1117, 1370, 1370, 1370, 1370, 1189, 1189, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1189, 1189, 1189, 1189, 1189, 1189, 1191, 1191, 1191, 1191, 1191, 1191, 1191, 1191, 663, 1370, 1191, 1191, 1191, 1191, 1191, 1191, 1370, 1370, 1117, 873, 1370, 1192, 1192, 1192, 1192, 1192, 1192, 1192, 1192, 1124, 1370, 1370, 1370, 1370, 1193, 1193, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1193, 1193, 1193, 1193, 1193, 1193, 1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195, 873, 1370, 1195, 1195, 1195, 1195, 1195, 1195, 1370, 1370, 1124, 1058, 1370, 1196, 1196, 1196, 1196, 1196, 1196, 1196, 1196, 1131, 1370, 1370, 1370, 1370, 1197, 1197, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1197, 1197, 1197, 1197, 1197, 1197, 1199, 1199, 1199, 1199, 1199, 1199, 1199, 1199, 1058, 1370, 1199, 1199, 1199, 1199, 1199, 1199, 1370, 1370, 1131, 1200, 1200, 1200, 1200, 1200, 1200, 1200, 1200, 1201, 1201, 1201, 1201, 1201, 1202, 1203, 1203, 1203, 1203, 1203, 1203, 1203, 1203, 1203, 1203, 1204, 1370, 1206, 1206, 1206, 1206, 1206, 1207, 1208, 1208, 1209, 1370, 1211, 1211, 1211, 1211, 1211, 1212, 1213, 1213, 1215, 1370, 1216, 1216, 1216, 1216, 1216, 1216, 1216, 1216, 1217, 1370, 1370, 1370, 1370, 1218, 1218, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1218, 1218, 1218, 1218, 1218, 1218, 1219, 1219, 1219, 1219, 1219, 1220, 1221, 1221, 97, 98, 1223, 1223, 1223, 1223, 1223, 1224, 1225, 1225, 1226, 1370, 1228, 1228, 1228, 1228, 1228, 1229, 1230, 1230, 1232, 1232, 1233, 1234, 1234, 1234, 1234, 1234, 1166, 1370, 1235, 1235, 1235, 1235, 1235, 1235, 1236, 1236, 1237, 1238, 1238, 1238, 1238, 1238, 1172, 1370, 1239, 1239, 1239, 1239, 1239, 1239, 1239, 1239, 1174, 1370, 1370, 1370, 1370, 1240, 1240, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1240, 1240, 1240, 1240, 1240, 1240, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1172, 1370, 1243, 1243, 1243, 1243, 1243, 1243, 1370, 1370, 1174, 1203, 1203, 1203, 1203, 1203, 1203, 1203, 1203, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1256, 1256, 1257, 1258, 1258, 1258, 1258, 1258, 1204, 1370, 1259, 1259, 1259, 1259, 1259, 1259, 1260, 1260, 1261, 1262, 1262, 1262, 1262, 1262, 1209, 1370, 1263, 1263, 1263, 1263, 1263, 1263, 1264, 1264, 1265, 1266, 1266, 1266, 1266, 1266, 1215, 1370, 1267, 1267, 1267, 1267, 1267, 1267, 1267, 1267, 1217, 1370, 1370, 1370, 1370, 1268, 1268, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1268, 1268, 1268, 1268, 1268, 1268, 1270, 1270, 1270, 1270, 1270, 1270, 1270, 1270, 1215, 1370, 1270, 1270, 1270, 1270, 1270, 1270, 1370, 1370, 1217, 97, 98, 1271, 1271, 1271, 1271, 1271, 1271, 1272, 1272, 1273, 1274, 1274, 1274, 1274, 1274, 1226, 1370, 1275, 1275, 1275, 1275, 1275, 1275, 97, 98, 1277, 1277, 1277, 1277, 1277, 1278, 1279, 1279, 1280, 1370, 1282, 1282, 1282, 1282, 1282, 1283, 1284, 1284, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1294, 1294, 1294, 1294, 1294, 1295, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1297, 1370, 1299, 1299, 1299, 1299, 1299, 1300, 1301, 1301, 1302, 1370, 1304, 1304, 1304, 1304, 1304, 1305, 1306, 1306, 97, 98, 1310, 1310, 1310, 1310, 1310, 1311, 1312, 1312, 97, 98, 1313, 1313, 1313, 1313, 1313, 1313, 1314, 1314, 1315, 1316, 1316, 1316, 1316, 1316, 1280, 1370, 1317, 1317, 1317, 1317, 1317, 1317, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1326, 1326, 1327, 1328, 1328, 1328, 1328, 1328, 1297, 1370, 1329, 1329, 1329, 1329, 1329, 1329, 1330, 1330, 1331, 1332, 1332, 1332, 1332, 1332, 1302, 1370, 1333, 1333, 1333, 1333, 1333, 1333, 97, 98, 1335, 1335, 1335, 1335, 1335, 1335, 1336, 1370, 1338, 1338, 1338, 1338, 1338, 1339, 1340, 1340, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1342, 1342, 1342, 1342, 1342, 1343, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1345, 1370, 1347, 1347, 1347, 1347, 1347, 1348, 1349, 1349, 1351, 1351, 1352, 1353, 1353, 1353, 1353, 1353, 1336, 1370, 1354, 1354, 1354, 1354, 1354, 1354, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1355, 1355, 1355, 1355, 1355, 1355, 1355, 1355, 1356, 1356, 1357, 1358, 1358, 1358, 1358, 1358, 1345, 1370, 1359, 1359, 1359, 1359, 1359, 1359, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1361, 1361, 1361, 1361, 1361, 1362, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1365, 1365, 1365, 1365, 1365, 1366, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 43, 43, 43, 43, 43, 43, 43, 43, 43, 49, 1370, 49, 86, 1370, 86, 86, 86, 86, 86, 86, 86, 93, 93, 1370, 93, 92, 92, 1370, 92, 129, 1370, 129, 132, 1370, 132, 134, 134, 1370, 134, 135, 135, 1370, 135, 172, 1370, 172, 173, 1370, 173, 173, 174, 174, 1370, 174, 175, 175, 1370, 175, 183, 183, 1370, 183, 211, 1370, 211, 214, 1370, 214, 215, 215, 1370, 215, 216, 216, 1370, 216, 229, 229, 1370, 229, 234, 1370, 234, 237, 1370, 237, 238, 238, 238, 238, 1370, 238, 258, 1370, 258, 259, 259, 1370, 259, 260, 260, 1370, 260, 268, 268, 268, 268, 1370, 268, 275, 275, 1370, 275, 276, 276, 1370, 276, 285, 1370, 285, 302, 302, 302, 305, 305, 305, 306, 306, 1370, 306, 307, 307, 1370, 307, 327, 327, 1370, 327, 328, 328, 1370, 328, 337, 337, 1370, 337, 342, 1370, 342, 345, 1370, 345, 347, 1370, 347, 350, 1370, 350, 351, 351, 351, 351, 1370, 351, 62, 1370, 1370, 62, 1370, 1370, 62, 366, 366, 366, 367, 367, 1370, 367, 368, 368, 1370, 368, 376, 376, 376, 376, 1370, 376, 383, 383, 383, 383, 1370, 383, 390, 390, 1370, 390, 391, 391, 1370, 391, 395, 395, 1370, 395, 403, 403, 1370, 403, 345, 1370, 345, 412, 1370, 412, 350, 1370, 350, 416, 1370, 416, 62, 1370, 1370, 62, 1370, 1370, 62, 431, 431, 1370, 431, 432, 432, 1370, 432, 459, 459, 1370, 459, 460, 460, 1370, 460, 467, 467, 1370, 467, 474, 1370, 474, 477, 1370, 477, 478, 1370, 478, 478, 1370, 478, 482, 482, 1370, 482, 486, 486, 486, 489, 489, 489, 491, 1370, 491, 494, 1370, 494, 496, 1370, 496, 499, 1370, 499, 500, 500, 500, 500, 1370, 500, 62, 1370, 1370, 62, 1370, 1370, 62, 509, 1370, 509, 512, 1370, 512, 513, 513, 1370, 513, 514, 514, 1370, 514, 522, 522, 522, 522, 1370, 522, 529, 529, 529, 529, 1370, 529, 536, 536, 536, 536, 1370, 536, 543, 543, 1370, 543, 544, 544, 1370, 544, 548, 548, 1370, 548, 552, 1370, 552, 552, 1370, 552, 555, 555, 1370, 555, 477, 1370, 477, 562, 1370, 562, 567, 567, 1370, 567, 489, 489, 489, 573, 573, 573, 494, 1370, 494, 577, 1370, 577, 499, 1370, 499, 581, 1370, 581, 512, 1370, 512, 595, 1370, 595, 596, 596, 1370, 596, 597, 597, 1370, 597, 631, 631, 1370, 631, 632, 632, 1370, 632, 639, 639, 1370, 639, 646, 646, 1370, 646, 653, 1370, 653, 656, 1370, 656, 658, 1370, 658, 661, 1370, 661, 662, 1370, 662, 662, 1370, 662, 666, 666, 1370, 666, 670, 670, 670, 673, 673, 673, 675, 1370, 675, 678, 1370, 678, 680, 1370, 680, 683, 1370, 683, 684, 684, 684, 684, 1370, 684, 693, 1370, 693, 696, 1370, 696, 697, 697, 1370, 697, 709, 709, 709, 709, 1370, 709, 716, 716, 716, 716, 1370, 716, 723, 723, 723, 723, 1370, 723, 730, 730, 730, 730, 1370, 730, 737, 737, 1370, 737, 738, 738, 1370, 738, 742, 742, 1370, 742, 746, 1370, 746, 746, 1370, 746, 749, 749, 1370, 749, 753, 1370, 753, 753, 1370, 753, 756, 756, 1370, 756, 656, 1370, 656, 763, 1370, 763, 661, 1370, 661, 767, 1370, 767, 772, 772, 1370, 772, 673, 673, 673, 778, 778, 778, 678, 1370, 678, 782, 1370, 782, 683, 1370, 683, 786, 1370, 786, 696, 1370, 696, 800, 1370, 800, 801, 801, 1370, 801, 809, 809, 809, 1370, 1370, 809, 805, 805, 1370, 805, 812, 1370, 1370, 812, 816, 1370, 1370, 816, 820, 1370, 1370, 820, 824, 1370, 1370, 824, 828, 1370, 1370, 828, 830, 830, 1370, 830, 831, 831, 1370, 831, 838, 838, 1370, 838, 845, 845, 1370, 845, 852, 852, 1370, 852, 863, 1370, 863, 866, 1370, 866, 868, 1370, 868, 871, 1370, 871, 872, 1370, 872, 872, 1370, 872, 876, 876, 1370, 876, 880, 880, 880, 883, 883, 883, 885, 1370, 885, 888, 1370, 888, 890, 1370, 890, 893, 1370, 893, 894, 894, 894, 894, 1370, 894, 903, 1370, 903, 906, 1370, 906, 912, 912, 1370, 912, 911, 911, 1370, 911, 915, 915, 915, 1370, 1370, 915, 916, 916, 916, 916, 1370, 916, 917, 917, 917, 1370, 1370, 917, 918, 918, 918, 918, 1370, 918, 919, 919, 919, 1370, 1370, 919, 920, 920, 920, 920, 1370, 920, 921, 921, 921, 1370, 1370, 921, 922, 922, 922, 922, 1370, 922, 923, 923, 923, 1370, 1370, 923, 924, 924, 924, 924, 1370, 924, 925, 925, 925, 1370, 1370, 925, 926, 926, 1370, 926, 927, 927, 1370, 927, 931, 931, 1370, 931, 935, 1370, 935, 935, 1370, 935, 938, 938, 1370, 938, 942, 1370, 942, 942, 1370, 942, 945, 945, 1370, 945, 949, 1370, 949, 949, 1370, 949, 952, 952, 1370, 952, 866, 1370, 866, 960, 1370, 960, 871, 1370, 871, 964, 1370, 964, 969, 969, 1370, 969, 883, 883, 883, 975, 975, 975, 888, 1370, 888, 979, 1370, 979, 893, 1370, 893, 983, 1370, 983, 989, 1370, 1370, 989, 906, 1370, 906, 994, 1370, 994, 996, 1370, 996, 999, 1370, 999, 1000, 1370, 1000, 1000, 1370, 1000, 1001, 1001, 1001, 1370, 1370, 1001, 1002, 1002, 1002, 1370, 1370, 1002, 1003, 1003, 1003, 1370, 1370, 1003, 1004, 1004, 1004, 1370, 1370, 1004, 1005, 1005, 1005, 1370, 1370, 1005, 1006, 1006, 1006, 1370, 1370, 1006, 1007, 1007, 1007, 1370, 1370, 1007, 1008, 1008, 1370, 1008, 1009, 1009, 1370, 1009, 1016, 1016, 1370, 1016, 1023, 1023, 1370, 1023, 1030, 1030, 1370, 1030, 1037, 1037, 1370, 1037, 1048, 1370, 1048, 1051, 1370, 1051, 1053, 1370, 1053, 1056, 1370, 1056, 1057, 1370, 1057, 1057, 1370, 1057, 1061, 1061, 1370, 1061, 1065, 1065, 1065, 1068, 1068, 1068, 1070, 1370, 1070, 1073, 1370, 1073, 1075, 1370, 1075, 1078, 1370, 1078, 1079, 1079, 1079, 1079, 1370, 1079, 1080, 1080, 1080, 1370, 1370, 1080, 999, 1370, 999, 1088, 1370, 1088, 1089, 1089, 1089, 1370, 1370, 1089, 1090, 1090, 1090, 1370, 1370, 1090, 1091, 1091, 1091, 1370, 1370, 1091, 1092, 1092, 1092, 1370, 1370, 1092, 1093, 1093, 1093, 1370, 1370, 1093, 1094, 1094, 1094, 1370, 1370, 1094, 1095, 1095, 1095, 1370, 1370, 1095, 1096, 1096, 1370, 1096, 1104, 1104, 1370, 1104, 1108, 1370, 1108, 1108, 1370, 1108, 1111, 1111, 1370, 1111, 1115, 1370, 1115, 1115, 1370, 1115, 1118, 1118, 1370, 1118, 1122, 1370, 1122, 1122, 1370, 1122, 1125, 1125, 1370, 1125, 1129, 1370, 1129, 1129, 1370, 1129, 1132, 1132, 1370, 1132, 1051, 1370, 1051, 1140, 1370, 1140, 1056, 1370, 1056, 1144, 1370, 1144, 1149, 1149, 1370, 1149, 1068, 1068, 1068, 1155, 1155, 1155, 1073, 1370, 1073, 1159, 1370, 1159, 1078, 1370, 1078, 1163, 1370, 1163, 1164, 1164, 1164, 1370, 1370, 1164, 1167, 1370, 1167, 1170, 1370, 1170, 913, 913, 913, 1370, 1370, 913, 1171, 1171, 1370, 1171, 1179, 1370, 1370, 1179, 1175, 1175, 1370, 1175, 1182, 1370, 1370, 1182, 1181, 1181, 1370, 1181, 1186, 1370, 1370, 1186, 1185, 1185, 1370, 1185, 1190, 1370, 1370, 1190, 1189, 1189, 1370, 1189, 1194, 1370, 1370, 1194, 1193, 1193, 1370, 1193, 1198, 1370, 1370, 1198, 1197, 1197, 1370, 1197, 1205, 1370, 1205, 1208, 1370, 1208, 1210, 1370, 1210, 1213, 1370, 1213, 1214, 1370, 1214, 1214, 1370, 1214, 1218, 1218, 1370, 1218, 1222, 1222, 1222, 1225, 1225, 1225, 1227, 1370, 1227, 1230, 1370, 1230, 1231, 1231, 1231, 1370, 1370, 1231, 1170, 1370, 1170, 1235, 1370, 1235, 1241, 1241, 1370, 1241, 1240, 1240, 1370, 1240, 1244, 1370, 1370, 1244, 1245, 1370, 1245, 1245, 1370, 1245, 1246, 1370, 1370, 1246, 1247, 1370, 1247, 1247, 1370, 1247, 1248, 1370, 1370, 1248, 1249, 1370, 1249, 1249, 1370, 1249, 1250, 1370, 1370, 1250, 1251, 1370, 1251, 1251, 1370, 1251, 1252, 1370, 1370, 1252, 1253, 1370, 1253, 1253, 1370, 1253, 1254, 1370, 1370, 1254, 1259, 1370, 1259, 1263, 1370, 1263, 1269, 1370, 1370, 1269, 1268, 1268, 1370, 1268, 1271, 1271, 1271, 1275, 1370, 1275, 1276, 1276, 1276, 1279, 1279, 1279, 1281, 1370, 1281, 1284, 1370, 1284, 1285, 1370, 1285, 1285, 1370, 1285, 1286, 1370, 1370, 1286, 1287, 1370, 1370, 1287, 1288, 1370, 1370, 1288, 1289, 1370, 1370, 1289, 1290, 1370, 1370, 1290, 1291, 1370, 1370, 1291, 1292, 1370, 1370, 1292, 1298, 1370, 1298, 1301, 1370, 1301, 1303, 1370, 1303, 1306, 1370, 1306, 1307, 1370, 1307, 1307, 1370, 1307, 1308, 1370, 1370, 1308, 1309, 1309, 1309, 1312, 1312, 1312, 1313, 1313, 1313, 1317, 1370, 1317, 1318, 1370, 1370, 1318, 1319, 1370, 1370, 1319, 1320, 1370, 1370, 1320, 1321, 1370, 1370, 1321, 1322, 1370, 1370, 1322, 1323, 1370, 1370, 1323, 1324, 1370, 1370, 1324, 1329, 1370, 1329, 1333, 1370, 1333, 1334, 1370, 1370, 1334, 1335, 1335, 1335, 1337, 1370, 1337, 1340, 1370, 1340, 1242, 1370, 1370, 1242, 1346, 1370, 1346, 1349, 1370, 1349, 1350, 1370, 1370, 1350, 1354, 1370, 1354, 1359, 1370, 1359, 5, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370 } ; static yyconst flex_int16_t yy_chk[8762] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 4, 3, 4, 17, 39, 17, 17, 17, 17, 17, 17, 17, 17, 17, 154, 39, 29, 29, 17, 17, 19, 19, 19, 19, 19, 19, 29, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 25, 154, 40, 25, 25, 25, 25, 25, 25, 25, 25, 25, 37, 40, 37, 1359, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 35, 56, 56, 1354, 26, 26, 76, 1349, 74, 35, 255, 255, 76, 26, 26, 26, 26, 26, 26, 49, 49, 49, 49, 49, 49, 49, 49, 51, 74, 51, 51, 51, 51, 51, 51, 51, 51, 52, 133, 52, 52, 52, 52, 52, 52, 52, 52, 52, 133, 1347, 173, 1346, 52, 52, 101, 101, 101, 101, 101, 101, 173, 52, 52, 52, 52, 52, 52, 55, 55, 55, 55, 55, 55, 55, 55, 57, 57, 57, 57, 57, 57, 57, 57, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 257, 257, 1340, 191, 191, 58, 58, 139, 139, 139, 139, 139, 139, 191, 58, 58, 58, 58, 58, 58, 63, 63, 63, 63, 63, 63, 63, 63, 302, 302, 303, 303, 1338, 63, 63, 187, 187, 187, 187, 187, 187, 187, 63, 63, 63, 63, 63, 63, 87, 87, 87, 87, 87, 87, 87, 87, 89, 340, 89, 89, 89, 89, 89, 89, 89, 89, 91, 340, 91, 91, 91, 91, 91, 91, 91, 91, 91, 192, 192, 197, 197, 91, 91, 305, 305, 366, 366, 192, 1337, 197, 91, 91, 91, 91, 91, 91, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 364, 364, 364, 222, 222, 94, 94, 195, 195, 195, 195, 195, 195, 222, 94, 94, 94, 94, 94, 94, 98, 98, 98, 98, 98, 98, 98, 98, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 409, 409, 223, 223, 99, 99, 220, 220, 220, 220, 220, 220, 223, 99, 99, 99, 99, 99, 99, 100, 100, 100, 100, 100, 100, 100, 100, 102, 1333, 102, 102, 102, 102, 102, 102, 102, 102, 103, 103, 103, 103, 103, 103, 103, 103, 411, 411, 1332, 228, 228, 103, 103, 226, 226, 226, 226, 226, 226, 228, 103, 103, 103, 103, 103, 103, 127, 127, 127, 127, 127, 127, 127, 127, 128, 128, 128, 128, 128, 128, 128, 128, 131, 1330, 131, 131, 131, 131, 131, 131, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 238, 238, 268, 268, 137, 137, 486, 486, 487, 487, 238, 1329, 268, 137, 137, 137, 137, 137, 137, 138, 138, 138, 138, 138, 138, 138, 138, 140, 1317, 140, 140, 140, 140, 140, 140, 140, 140, 143, 143, 143, 143, 143, 143, 143, 143, 144, 144, 144, 144, 144, 144, 144, 144, 145, 1316, 145, 145, 145, 145, 145, 145, 145, 145, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 489, 489, 289, 289, 146, 146, 280, 280, 280, 280, 280, 280, 289, 146, 146, 146, 146, 146, 146, 147, 147, 147, 147, 147, 147, 147, 147, 290, 290, 295, 295, 1314, 147, 147, 573, 573, 1307, 290, 398, 295, 1306, 147, 147, 147, 147, 147, 147, 148, 398, 148, 148, 148, 148, 148, 148, 148, 148, 149, 149, 149, 149, 149, 149, 149, 149, 150, 1304, 150, 150, 150, 150, 150, 150, 170, 1303, 170, 170, 170, 170, 170, 170, 170, 170, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 574, 574, 1301, 313, 313, 176, 176, 293, 293, 293, 293, 293, 293, 313, 176, 176, 176, 176, 176, 176, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 576, 576, 314, 314, 177, 177, 311, 311, 311, 311, 311, 311, 314, 177, 177, 177, 177, 177, 177, 178, 178, 178, 178, 178, 178, 178, 178, 670, 670, 1299, 319, 319, 178, 178, 317, 317, 317, 317, 317, 317, 319, 178, 178, 178, 178, 178, 178, 179, 1298, 179, 179, 179, 179, 179, 179, 179, 179, 180, 180, 180, 180, 180, 180, 180, 180, 181, 1285, 181, 181, 181, 181, 181, 181, 186, 186, 186, 186, 186, 186, 186, 186, 189, 1284, 189, 189, 189, 189, 189, 189, 189, 189, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 320, 320, 321, 321, 193, 193, 671, 671, 673, 673, 320, 1282, 321, 193, 193, 193, 193, 193, 193, 194, 194, 194, 194, 194, 194, 194, 194, 196, 1281, 196, 196, 196, 196, 196, 196, 196, 196, 210, 210, 210, 210, 210, 210, 210, 210, 213, 1275, 213, 213, 213, 213, 213, 213, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 701, 701, 326, 326, 218, 218, 324, 324, 324, 324, 324, 324, 326, 218, 218, 218, 218, 218, 218, 219, 219, 219, 219, 219, 219, 219, 219, 221, 1266, 221, 221, 221, 221, 221, 221, 221, 221, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 778, 778, 351, 351, 224, 224, 332, 332, 332, 332, 332, 332, 351, 224, 224, 224, 224, 224, 224, 225, 225, 225, 225, 225, 225, 225, 225, 227, 1264, 227, 227, 227, 227, 227, 227, 227, 227, 231, 231, 231, 231, 231, 231, 231, 231, 376, 376, 383, 383, 1263, 231, 231, 779, 779, 1262, 376, 1260, 383, 1259, 231, 231, 231, 231, 231, 231, 233, 233, 233, 233, 233, 233, 233, 233, 236, 1253, 236, 236, 236, 236, 236, 236, 239, 1251, 239, 239, 239, 239, 239, 239, 239, 239, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 402, 403, 408, 1249, 240, 240, 781, 781, 809, 809, 402, 403, 408, 240, 240, 240, 240, 240, 240, 241, 241, 241, 241, 241, 241, 241, 241, 812, 812, 1247, 420, 420, 241, 241, 406, 406, 406, 406, 406, 406, 420, 241, 241, 241, 241, 241, 241, 242, 1245, 242, 242, 242, 242, 242, 242, 242, 242, 243, 243, 243, 243, 243, 243, 243, 243, 244, 1238, 244, 244, 244, 244, 244, 244, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 261, 261, 261, 261, 261, 261, 261, 261, 261, 261, 421, 421, 426, 426, 1236, 261, 261, 816, 816, 1235, 421, 1230, 426, 1228, 261, 261, 261, 261, 261, 261, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 820, 820, 438, 438, 262, 262, 424, 424, 424, 424, 424, 424, 438, 262, 262, 262, 262, 262, 262, 263, 263, 263, 263, 263, 263, 263, 263, 824, 824, 1227, 439, 439, 263, 263, 436, 436, 436, 436, 436, 436, 439, 263, 263, 263, 263, 263, 263, 264, 1218, 264, 264, 264, 264, 264, 264, 264, 264, 265, 265, 265, 265, 265, 265, 265, 265, 266, 1214, 266, 266, 266, 266, 266, 266, 269, 269, 269, 269, 269, 269, 269, 269, 269, 269, 269, 828, 828, 444, 444, 269, 269, 442, 442, 442, 442, 442, 442, 444, 269, 269, 269, 269, 269, 269, 270, 270, 270, 270, 270, 270, 270, 270, 445, 445, 446, 446, 1213, 270, 270, 880, 880, 1211, 445, 466, 446, 1210, 270, 270, 270, 270, 270, 270, 271, 466, 271, 271, 271, 271, 271, 271, 271, 271, 272, 272, 272, 272, 272, 272, 272, 272, 273, 1208, 273, 273, 273, 273, 273, 273, 277, 277, 277, 277, 277, 277, 277, 277, 881, 881, 1206, 451, 451, 277, 277, 449, 449, 449, 449, 449, 449, 451, 277, 277, 277, 277, 277, 277, 278, 1205, 278, 278, 278, 278, 278, 278, 278, 278, 278, 452, 452, 453, 453, 278, 278, 883, 883, 912, 912, 452, 1175, 453, 278, 278, 278, 278, 278, 278, 279, 279, 279, 279, 279, 279, 279, 279, 283, 467, 283, 283, 283, 283, 283, 283, 283, 283, 287, 467, 287, 287, 287, 287, 287, 287, 287, 287, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 913, 913, 458, 458, 291, 291, 456, 456, 456, 456, 456, 456, 458, 291, 291, 291, 291, 291, 291, 292, 292, 292, 292, 292, 292, 292, 292, 294, 1174, 294, 294, 294, 294, 294, 294, 294, 294, 301, 301, 301, 301, 301, 301, 301, 301, 304, 304, 304, 304, 304, 304, 304, 304, 309, 309, 309, 309, 309, 309, 309, 309, 309, 309, 309, 915, 915, 472, 1171, 309, 309, 464, 464, 464, 464, 464, 464, 472, 309, 309, 309, 309, 309, 309, 310, 310, 310, 310, 310, 310, 310, 310, 312, 1170, 312, 312, 312, 312, 312, 312, 312, 312, 315, 315, 315, 315, 315, 315, 315, 315, 315, 315, 315, 917, 917, 485, 1168, 315, 315, 470, 470, 470, 470, 470, 470, 485, 315, 315, 315, 315, 315, 315, 316, 316, 316, 316, 316, 316, 316, 316, 318, 1167, 318, 318, 318, 318, 318, 318, 318, 318, 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, 500, 500, 522, 522, 322, 322, 919, 919, 921, 921, 500, 1163, 522, 322, 322, 322, 322, 322, 322, 323, 323, 323, 323, 323, 323, 323, 323, 325, 551, 325, 325, 325, 325, 325, 325, 325, 325, 330, 551, 330, 330, 330, 330, 330, 330, 330, 330, 330, 529, 529, 536, 536, 330, 330, 923, 923, 925, 925, 529, 1162, 536, 330, 330, 330, 330, 330, 330, 331, 331, 331, 331, 331, 331, 331, 331, 334, 334, 334, 334, 334, 334, 334, 334, 335, 558, 335, 335, 335, 335, 335, 335, 335, 335, 335, 558, 566, 567, 1160, 335, 335, 975, 975, 976, 976, 1159, 566, 567, 335, 335, 335, 335, 335, 335, 336, 336, 336, 336, 336, 336, 336, 336, 978, 978, 1153, 572, 1147, 336, 336, 570, 570, 570, 570, 570, 570, 572, 336, 336, 336, 336, 336, 336, 338, 338, 338, 338, 338, 338, 338, 338, 339, 1145, 339, 339, 339, 339, 339, 339, 989, 989, 339, 341, 341, 341, 341, 341, 341, 341, 341, 344, 1144, 344, 344, 344, 344, 344, 344, 346, 346, 346, 346, 346, 346, 346, 346, 349, 1143, 349, 349, 349, 349, 349, 349, 352, 1141, 352, 352, 352, 352, 352, 352, 352, 352, 353, 353, 353, 353, 353, 353, 353, 353, 353, 353, 353, 585, 585, 586, 586, 353, 353, 1001, 1001, 1002, 1002, 585, 1140, 586, 353, 353, 353, 353, 353, 353, 354, 354, 354, 354, 354, 354, 354, 354, 1003, 1003, 1132, 591, 591, 354, 354, 589, 589, 589, 589, 589, 589, 591, 354, 354, 354, 354, 354, 354, 355, 1129, 355, 355, 355, 355, 355, 355, 355, 355, 356, 356, 356, 356, 356, 356, 356, 356, 357, 1125, 357, 357, 357, 357, 357, 357, 362, 362, 362, 362, 362, 362, 362, 362, 363, 363, 363, 363, 363, 363, 363, 363, 369, 369, 369, 369, 369, 369, 369, 369, 369, 369, 1004, 1004, 1122, 603, 603, 369, 369, 601, 601, 601, 601, 601, 601, 603, 369, 369, 369, 369, 369, 369, 370, 370, 370, 370, 370, 370, 370, 370, 370, 370, 370, 604, 604, 609, 609, 370, 370, 1005, 1005, 1006, 1006, 604, 1118, 609, 370, 370, 370, 370, 370, 370, 371, 371, 371, 371, 371, 371, 371, 371, 1007, 1007, 1115, 610, 610, 371, 371, 607, 607, 607, 607, 607, 607, 610, 371, 371, 371, 371, 371, 371, 372, 1111, 372, 372, 372, 372, 372, 372, 372, 372, 373, 373, 373, 373, 373, 373, 373, 373, 374, 1108, 374, 374, 374, 374, 374, 374, 377, 377, 377, 377, 377, 377, 377, 377, 377, 377, 377, 611, 611, 616, 616, 377, 377, 1065, 1065, 1066, 1066, 611, 1104, 616, 377, 377, 377, 377, 377, 377, 378, 378, 378, 378, 378, 378, 378, 378, 1068, 1068, 1101, 617, 617, 378, 378, 614, 614, 614, 614, 614, 614, 617, 378, 378, 378, 378, 378, 378, 379, 1096, 379, 379, 379, 379, 379, 379, 379, 379, 380, 380, 380, 380, 380, 380, 380, 380, 381, 1088, 381, 381, 381, 381, 381, 381, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 618, 618, 623, 623, 384, 384, 1080, 1080, 1089, 1089, 618, 1087, 623, 384, 384, 384, 384, 384, 384, 385, 385, 385, 385, 385, 385, 385, 385, 1090, 1090, 1085, 624, 624, 385, 385, 621, 621, 621, 621, 621, 621, 624, 385, 385, 385, 385, 385, 385, 386, 1078, 386, 386, 386, 386, 386, 386, 386, 386, 387, 387, 387, 387, 387, 387, 387, 387, 388, 1076, 388, 388, 388, 388, 388, 388, 392, 392, 392, 392, 392, 392, 392, 392, 625, 625, 630, 630, 1075, 392, 392, 1091, 1091, 1073, 625, 638, 630, 1071, 392, 392, 392, 392, 392, 392, 393, 638, 393, 393, 393, 393, 393, 393, 393, 393, 393, 1092, 1092, 639, 1070, 393, 393, 628, 628, 628, 628, 628, 628, 639, 393, 393, 393, 393, 393, 393, 394, 394, 394, 394, 394, 394, 394, 394, 1093, 1093, 1061, 644, 1057, 394, 394, 636, 636, 636, 636, 636, 636, 644, 394, 394, 394, 394, 394, 394, 396, 396, 396, 396, 396, 396, 396, 396, 397, 1056, 397, 397, 397, 397, 397, 397, 1094, 1094, 397, 400, 645, 400, 400, 400, 400, 400, 400, 400, 400, 404, 645, 404, 404, 404, 404, 404, 404, 404, 404, 404, 1095, 1095, 646, 1054, 404, 404, 642, 642, 642, 642, 642, 642, 646, 404, 404, 404, 404, 404, 404, 405, 405, 405, 405, 405, 405, 405, 405, 410, 410, 410, 410, 410, 410, 410, 410, 410, 410, 414, 651, 414, 414, 414, 414, 414, 414, 414, 414, 418, 651, 418, 418, 418, 418, 418, 418, 418, 418, 422, 422, 422, 422, 422, 422, 422, 422, 422, 422, 422, 1155, 1155, 669, 1053, 422, 422, 649, 649, 649, 649, 649, 649, 669, 422, 422, 422, 422, 422, 422, 423, 423, 423, 423, 423, 423, 423, 423, 425, 745, 425, 425, 425, 425, 425, 425, 425, 425, 429, 745, 429, 429, 429, 429, 429, 429, 429, 429, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 684, 684, 709, 709, 434, 434, 1156, 1156, 1158, 1158, 684, 1051, 709, 434, 434, 434, 434, 434, 434, 435, 435, 435, 435, 435, 435, 435, 435, 437, 1049, 437, 437, 437, 437, 437, 437, 437, 437, 440, 440, 440, 440, 440, 440, 440, 440, 440, 440, 440, 1164, 1164, 716, 716, 440, 440, 700, 700, 700, 700, 700, 700, 716, 440, 440, 440, 440, 440, 440, 441, 441, 441, 441, 441, 441, 441, 441, 443, 1048, 443, 443, 443, 443, 443, 443, 443, 443, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 723, 723, 730, 730, 447, 447, 1222, 1222, 1223, 1223, 723, 1041, 730, 447, 447, 447, 447, 447, 447, 448, 448, 448, 448, 448, 448, 448, 448, 450, 1034, 450, 450, 450, 450, 450, 450, 450, 450, 454, 454, 454, 454, 454, 454, 454, 454, 454, 454, 454, 752, 759, 771, 1027, 454, 454, 1225, 1225, 1231, 1231, 752, 759, 771, 454, 454, 454, 454, 454, 454, 455, 455, 455, 455, 455, 455, 455, 455, 457, 772, 457, 457, 457, 457, 457, 457, 457, 457, 462, 772, 462, 462, 462, 462, 462, 462, 462, 462, 462, 1232, 1232, 777, 1020, 462, 462, 775, 775, 775, 775, 775, 775, 777, 462, 462, 462, 462, 462, 462, 463, 463, 463, 463, 463, 463, 463, 463, 468, 1014, 468, 468, 468, 468, 468, 468, 468, 468, 468, 790, 790, 791, 791, 468, 468, 1234, 1234, 1271, 1271, 790, 1009, 791, 468, 468, 468, 468, 468, 468, 469, 469, 469, 469, 469, 469, 469, 469, 473, 473, 473, 473, 473, 473, 473, 473, 476, 1008, 476, 476, 476, 476, 476, 476, 479, 479, 479, 479, 479, 479, 479, 479, 480, 808, 480, 480, 480, 480, 480, 480, 480, 480, 480, 808, 1000, 796, 796, 480, 480, 794, 794, 794, 794, 794, 794, 796, 480, 480, 480, 480, 480, 480, 481, 481, 481, 481, 481, 481, 481, 481, 810, 810, 811, 811, 999, 481, 481, 1272, 1272, 997, 810, 996, 811, 994, 481, 481, 481, 481, 481, 481, 483, 483, 483, 483, 483, 483, 483, 483, 484, 986, 484, 484, 484, 484, 484, 484, 1274, 1274, 484, 488, 488, 488, 488, 488, 488, 488, 488, 490, 490, 490, 490, 490, 490, 490, 490, 493, 984, 493, 493, 493, 493, 493, 493, 495, 495, 495, 495, 495, 495, 495, 495, 498, 983, 498, 498, 498, 498, 498, 498, 501, 982, 501, 501, 501, 501, 501, 501, 501, 501, 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, 813, 813, 814, 814, 502, 502, 1276, 1276, 1277, 1277, 813, 980, 814, 502, 502, 502, 502, 502, 502, 503, 503, 503, 503, 503, 503, 503, 503, 815, 815, 817, 817, 979, 503, 503, 1279, 1279, 973, 815, 837, 817, 967, 503, 503, 503, 503, 503, 503, 504, 837, 504, 504, 504, 504, 504, 504, 504, 504, 505, 505, 505, 505, 505, 505, 505, 505, 506, 965, 506, 506, 506, 506, 506, 506, 508, 508, 508, 508, 508, 508, 508, 508, 511, 964, 511, 511, 511, 511, 511, 511, 515, 515, 515, 515, 515, 515, 515, 515, 515, 515, 818, 818, 819, 819, 963, 515, 515, 1309, 1309, 961, 818, 960, 819, 952, 515, 515, 515, 515, 515, 515, 516, 516, 516, 516, 516, 516, 516, 516, 516, 516, 516, 821, 821, 822, 822, 516, 516, 1310, 1310, 1312, 1312, 821, 949, 822, 516, 516, 516, 516, 516, 516, 517, 517, 517, 517, 517, 517, 517, 517, 823, 823, 825, 825, 945, 517, 517, 1313, 1313, 942, 823, 838, 825, 938, 517, 517, 517, 517, 517, 517, 518, 838, 518, 518, 518, 518, 518, 518, 518, 518, 519, 519, 519, 519, 519, 519, 519, 519, 520, 935, 520, 520, 520, 520, 520, 520, 523, 523, 523, 523, 523, 523, 523, 523, 523, 523, 523, 826, 826, 827, 827, 523, 523, 1335, 1335, 931, 927, 826, 926, 827, 523, 523, 523, 523, 523, 523, 524, 524, 524, 524, 524, 524, 524, 524, 829, 829, 843, 844, 909, 524, 524, 907, 906, 904, 829, 845, 843, 844, 524, 524, 524, 524, 524, 524, 525, 845, 525, 525, 525, 525, 525, 525, 525, 525, 526, 526, 526, 526, 526, 526, 526, 526, 527, 903, 527, 527, 527, 527, 527, 527, 530, 530, 530, 530, 530, 530, 530, 530, 530, 530, 530, 901, 893, 850, 891, 530, 530, 835, 835, 835, 835, 835, 835, 850, 530, 530, 530, 530, 530, 530, 531, 531, 531, 531, 531, 531, 531, 531, 890, 888, 886, 851, 885, 531, 531, 841, 841, 841, 841, 841, 841, 851, 531, 531, 531, 531, 531, 531, 532, 876, 532, 532, 532, 532, 532, 532, 532, 532, 533, 533, 533, 533, 533, 533, 533, 533, 534, 872, 534, 534, 534, 534, 534, 534, 537, 537, 537, 537, 537, 537, 537, 537, 537, 537, 537, 871, 869, 852, 868, 537, 537, 848, 848, 848, 848, 848, 848, 852, 537, 537, 537, 537, 537, 537, 538, 538, 538, 538, 538, 538, 538, 538, 866, 864, 863, 857, 856, 538, 538, 855, 855, 855, 855, 855, 855, 857, 538, 538, 538, 538, 538, 538, 539, 849, 539, 539, 539, 539, 539, 539, 539, 539, 540, 540, 540, 540, 540, 540, 540, 540, 541, 842, 541, 541, 541, 541, 541, 541, 545, 545, 545, 545, 545, 545, 545, 545, 836, 832, 831, 879, 830, 545, 545, 860, 860, 860, 860, 860, 860, 879, 545, 545, 545, 545, 545, 545, 546, 805, 546, 546, 546, 546, 546, 546, 546, 546, 546, 894, 894, 910, 804, 546, 546, 801, 800, 799, 797, 894, 789, 910, 546, 546, 546, 546, 546, 546, 547, 547, 547, 547, 547, 547, 547, 547, 911, 914, 916, 916, 787, 547, 547, 786, 785, 783, 911, 914, 916, 782, 547, 547, 547, 547, 547, 547, 549, 549, 549, 549, 549, 549, 549, 549, 550, 776, 550, 550, 550, 550, 550, 550, 770, 768, 550, 553, 767, 553, 553, 553, 553, 553, 553, 553, 553, 553, 918, 918, 920, 920, 553, 553, 766, 764, 763, 756, 918, 753, 920, 553, 553, 553, 553, 553, 553, 554, 554, 554, 554, 554, 554, 554, 554, 922, 922, 924, 924, 749, 554, 554, 746, 742, 738, 922, 737, 924, 736, 554, 554, 554, 554, 554, 554, 556, 556, 556, 556, 556, 556, 556, 556, 557, 729, 557, 557, 557, 557, 557, 557, 722, 715, 557, 560, 934, 560, 560, 560, 560, 560, 560, 560, 560, 564, 934, 564, 564, 564, 564, 564, 564, 564, 564, 568, 941, 568, 568, 568, 568, 568, 568, 568, 568, 568, 941, 948, 955, 708, 568, 568, 702, 697, 696, 694, 693, 948, 955, 568, 568, 568, 568, 568, 568, 569, 569, 569, 569, 569, 569, 569, 569, 575, 575, 575, 575, 575, 575, 575, 575, 575, 575, 579, 968, 579, 579, 579, 579, 579, 579, 579, 579, 583, 968, 583, 583, 583, 583, 583, 583, 583, 583, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 969, 974, 987, 987, 587, 587, 691, 683, 681, 680, 969, 974, 987, 587, 587, 587, 587, 587, 587, 588, 588, 588, 588, 588, 588, 588, 588, 590, 1015, 590, 590, 590, 590, 590, 590, 590, 590, 593, 1015, 593, 593, 593, 593, 593, 593, 593, 593, 598, 598, 598, 598, 598, 598, 598, 598, 598, 678, 676, 988, 988, 598, 598, 972, 972, 972, 972, 972, 972, 988, 598, 598, 598, 598, 598, 598, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 990, 990, 1016, 675, 599, 599, 666, 662, 661, 659, 990, 658, 1016, 599, 599, 599, 599, 599, 599, 600, 600, 600, 600, 600, 600, 600, 600, 602, 656, 602, 602, 602, 602, 602, 602, 602, 602, 605, 605, 605, 605, 605, 605, 605, 605, 605, 605, 605, 654, 653, 1021, 650, 605, 605, 1013, 1013, 1013, 1013, 1013, 1013, 1021, 605, 605, 605, 605, 605, 605, 606, 606, 606, 606, 606, 606, 606, 606, 608, 643, 608, 608, 608, 608, 608, 608, 608, 608, 612, 612, 612, 612, 612, 612, 612, 612, 612, 612, 612, 637, 633, 1022, 632, 612, 612, 1019, 1019, 1019, 1019, 1019, 1019, 1022, 612, 612, 612, 612, 612, 612, 613, 613, 613, 613, 613, 613, 613, 613, 615, 631, 615, 615, 615, 615, 615, 615, 615, 615, 619, 619, 619, 619, 619, 619, 619, 619, 619, 619, 619, 1023, 1028, 1029, 597, 619, 619, 596, 595, 594, 592, 1023, 1028, 1029, 619, 619, 619, 619, 619, 619, 620, 620, 620, 620, 620, 620, 620, 620, 622, 584, 622, 622, 622, 622, 622, 622, 622, 622, 626, 626, 626, 626, 626, 626, 626, 626, 626, 626, 626, 582, 581, 1030, 580, 626, 626, 1026, 1026, 1026, 1026, 1026, 1026, 1030, 626, 626, 626, 626, 626, 626, 627, 627, 627, 627, 627, 627, 627, 627, 629, 1035, 629, 629, 629, 629, 629, 629, 629, 629, 634, 1035, 634, 634, 634, 634, 634, 634, 634, 634, 634, 578, 577, 1036, 571, 634, 634, 1033, 1033, 1033, 1033, 1033, 1033, 1036, 634, 634, 634, 634, 634, 634, 635, 635, 635, 635, 635, 635, 635, 635, 640, 1037, 640, 640, 640, 640, 640, 640, 640, 640, 640, 1037, 565, 1042, 563, 640, 640, 1040, 1040, 1040, 1040, 1040, 1040, 1042, 640, 640, 640, 640, 640, 640, 641, 641, 641, 641, 641, 641, 641, 641, 647, 1064, 647, 647, 647, 647, 647, 647, 647, 647, 647, 1064, 562, 1079, 1079, 647, 647, 1045, 1045, 1045, 1045, 1045, 1045, 1079, 647, 647, 647, 647, 647, 647, 648, 648, 648, 648, 648, 648, 648, 648, 652, 652, 652, 652, 652, 652, 652, 652, 655, 561, 655, 655, 655, 655, 655, 655, 657, 657, 657, 657, 657, 657, 657, 657, 660, 559, 660, 660, 660, 660, 660, 660, 663, 663, 663, 663, 663, 663, 663, 663, 664, 1107, 664, 664, 664, 664, 664, 664, 664, 664, 664, 1107, 555, 1114, 552, 664, 664, 1083, 1083, 1083, 1083, 1083, 1083, 1114, 664, 664, 664, 664, 664, 664, 665, 665, 665, 665, 665, 665, 665, 665, 548, 544, 543, 1121, 542, 665, 665, 1099, 1099, 1099, 1099, 1099, 1099, 1121, 665, 665, 665, 665, 665, 665, 667, 667, 667, 667, 667, 667, 667, 667, 668, 535, 668, 668, 668, 668, 668, 668, 528, 521, 668, 672, 672, 672, 672, 672, 672, 672, 672, 674, 674, 674, 674, 674, 674, 674, 674, 677, 514, 677, 677, 677, 677, 677, 677, 679, 679, 679, 679, 679, 679, 679, 679, 682, 513, 682, 682, 682, 682, 682, 682, 685, 512, 685, 685, 685, 685, 685, 685, 685, 685, 686, 686, 686, 686, 686, 686, 686, 686, 686, 686, 686, 1128, 1135, 1148, 510, 686, 686, 509, 507, 499, 497, 1128, 1135, 1148, 686, 686, 686, 686, 686, 686, 687, 687, 687, 687, 687, 687, 687, 687, 1149, 1154, 1178, 1180, 496, 687, 687, 494, 492, 491, 1149, 1154, 1178, 1180, 687, 687, 687, 687, 687, 687, 688, 482, 688, 688, 688, 688, 688, 688, 688, 688, 689, 689, 689, 689, 689, 689, 689, 689, 690, 478, 690, 690, 690, 690, 690, 690, 692, 692, 692, 692, 692, 692, 692, 692, 695, 477, 695, 695, 695, 695, 695, 695, 698, 1181, 698, 698, 698, 698, 698, 698, 698, 698, 698, 1181, 475, 1183, 474, 698, 698, 1152, 1152, 1152, 1152, 1152, 1152, 1183, 698, 698, 698, 698, 698, 698, 699, 699, 699, 699, 699, 699, 699, 699, 703, 703, 703, 703, 703, 703, 703, 703, 703, 703, 703, 1184, 1185, 1187, 471, 703, 703, 465, 461, 460, 459, 1184, 1185, 1187, 703, 703, 703, 703, 703, 703, 705, 433, 705, 705, 705, 705, 705, 705, 705, 705, 706, 706, 706, 706, 706, 706, 706, 706, 707, 432, 707, 707, 707, 707, 707, 707, 710, 710, 710, 710, 710, 710, 710, 710, 710, 710, 710, 1188, 1189, 1191, 431, 710, 710, 430, 428, 419, 417, 1188, 1189, 1191, 710, 710, 710, 710, 710, 710, 712, 416, 712, 712, 712, 712, 712, 712, 712, 712, 713, 713, 713, 713, 713, 713, 713, 713, 714, 415, 714, 714, 714, 714, 714, 714, 717, 717, 717, 717, 717, 717, 717, 717, 717, 717, 717, 1192, 1193, 1195, 413, 717, 717, 412, 407, 401, 399, 1192, 1193, 1195, 717, 717, 717, 717, 717, 717, 719, 395, 719, 719, 719, 719, 719, 719, 719, 719, 720, 720, 720, 720, 720, 720, 720, 720, 721, 391, 721, 721, 721, 721, 721, 721, 724, 724, 724, 724, 724, 724, 724, 724, 724, 724, 724, 1196, 1197, 1199, 390, 724, 724, 389, 382, 375, 368, 1196, 1197, 1199, 724, 724, 724, 724, 724, 724, 726, 367, 726, 726, 726, 726, 726, 726, 726, 726, 727, 727, 727, 727, 727, 727, 727, 727, 728, 360, 728, 728, 728, 728, 728, 728, 731, 731, 731, 731, 731, 731, 731, 731, 731, 731, 731, 358, 350, 1221, 348, 731, 731, 1202, 1202, 1202, 1202, 1202, 1202, 1221, 731, 731, 731, 731, 731, 731, 733, 347, 733, 733, 733, 733, 733, 733, 733, 733, 734, 734, 734, 734, 734, 734, 734, 734, 735, 345, 735, 735, 735, 735, 735, 735, 739, 739, 739, 739, 739, 739, 739, 739, 1239, 1240, 1243, 1267, 343, 739, 739, 342, 337, 333, 1239, 1240, 1243, 1267, 739, 739, 739, 739, 739, 739, 740, 1268, 740, 740, 740, 740, 740, 740, 740, 740, 740, 1268, 1270, 329, 328, 740, 740, 327, 308, 307, 306, 300, 1270, 298, 740, 740, 740, 740, 740, 740, 741, 741, 741, 741, 741, 741, 741, 741, 297, 296, 288, 286, 285, 741, 741, 1295, 1295, 1295, 1295, 1295, 1295, 284, 741, 741, 741, 741, 741, 741, 743, 743, 743, 743, 743, 743, 743, 743, 744, 282, 744, 744, 744, 744, 744, 744, 281, 276, 744, 747, 275, 747, 747, 747, 747, 747, 747, 747, 747, 747, 274, 267, 260, 259, 747, 747, 1343, 1343, 1343, 1343, 1343, 1343, 258, 747, 747, 747, 747, 747, 747, 748, 748, 748, 748, 748, 748, 748, 748, 254, 252, 250, 249, 245, 748, 748, 1362, 1362, 1362, 1362, 1362, 1362, 237, 748, 748, 748, 748, 748, 748, 750, 750, 750, 750, 750, 750, 750, 750, 751, 235, 751, 751, 751, 751, 751, 751, 234, 230, 751, 754, 229, 754, 754, 754, 754, 754, 754, 754, 754, 754, 217, 216, 215, 214, 754, 754, 1366, 1366, 1366, 1366, 1366, 1366, 212, 754, 754, 754, 754, 754, 754, 755, 755, 755, 755, 755, 755, 755, 755, 211, 209, 208, 207, 206, 755, 755, 205, 204, 203, 202, 199, 190, 188, 755, 755, 755, 755, 755, 755, 757, 757, 757, 757, 757, 757, 757, 757, 758, 184, 758, 758, 758, 758, 758, 758, 183, 182, 758, 760, 760, 760, 760, 760, 760, 760, 760, 761, 761, 761, 761, 761, 761, 761, 761, 762, 762, 762, 762, 762, 762, 762, 762, 765, 175, 765, 765, 765, 765, 765, 765, 765, 765, 769, 174, 769, 769, 769, 769, 769, 769, 769, 769, 773, 172, 773, 773, 773, 773, 773, 773, 773, 773, 773, 171, 169, 166, 165, 773, 773, 164, 163, 162, 161, 159, 158, 156, 773, 773, 773, 773, 773, 773, 774, 774, 774, 774, 774, 774, 774, 774, 780, 780, 780, 780, 780, 780, 780, 780, 780, 780, 784, 155, 784, 784, 784, 784, 784, 784, 784, 784, 788, 153, 788, 788, 788, 788, 788, 788, 788, 788, 792, 792, 792, 792, 792, 792, 792, 792, 792, 792, 792, 152, 151, 142, 141, 792, 792, 136, 135, 134, 132, 130, 129, 125, 792, 792, 792, 792, 792, 792, 793, 793, 793, 793, 793, 793, 793, 793, 795, 124, 795, 795, 795, 795, 795, 795, 795, 795, 798, 122, 798, 798, 798, 798, 798, 798, 798, 798, 802, 802, 802, 802, 802, 802, 802, 802, 803, 121, 803, 803, 803, 803, 803, 803, 803, 803, 803, 120, 119, 118, 117, 803, 803, 115, 114, 113, 112, 111, 110, 106, 803, 803, 803, 803, 803, 803, 806, 806, 806, 806, 806, 806, 806, 806, 807, 104, 807, 807, 807, 807, 807, 807, 97, 96, 807, 833, 93, 833, 833, 833, 833, 833, 833, 833, 833, 833, 92, 90, 88, 84, 833, 833, 83, 82, 81, 80, 79, 78, 77, 833, 833, 833, 833, 833, 833, 834, 834, 834, 834, 834, 834, 834, 834, 839, 75, 839, 839, 839, 839, 839, 839, 839, 839, 839, 73, 72, 71, 70, 839, 839, 69, 68, 67, 66, 65, 64, 54, 839, 839, 839, 839, 839, 839, 840, 840, 840, 840, 840, 840, 840, 840, 846, 53, 846, 846, 846, 846, 846, 846, 846, 846, 846, 45, 42, 41, 38, 846, 846, 36, 34, 33, 32, 31, 30, 28, 846, 846, 846, 846, 846, 846, 847, 847, 847, 847, 847, 847, 847, 847, 853, 27, 853, 853, 853, 853, 853, 853, 853, 853, 853, 24, 23, 22, 20, 853, 853, 16, 15, 11, 9, 7, 5, 0, 853, 853, 853, 853, 853, 853, 854, 854, 854, 854, 854, 854, 854, 854, 858, 858, 858, 858, 858, 858, 858, 858, 859, 859, 859, 859, 859, 859, 859, 859, 862, 862, 862, 862, 862, 862, 862, 862, 865, 0, 865, 865, 865, 865, 865, 865, 867, 867, 867, 867, 867, 867, 867, 867, 870, 0, 870, 870, 870, 870, 870, 870, 873, 873, 873, 873, 873, 873, 873, 873, 874, 0, 874, 874, 874, 874, 874, 874, 874, 874, 874, 0, 0, 0, 0, 874, 874, 0, 0, 0, 0, 0, 0, 0, 874, 874, 874, 874, 874, 874, 875, 875, 875, 875, 875, 875, 875, 875, 0, 0, 0, 0, 0, 875, 875, 0, 0, 0, 0, 0, 0, 0, 875, 875, 875, 875, 875, 875, 877, 877, 877, 877, 877, 877, 877, 877, 878, 0, 878, 878, 878, 878, 878, 878, 0, 0, 878, 882, 882, 882, 882, 882, 882, 882, 882, 884, 884, 884, 884, 884, 884, 884, 884, 887, 0, 887, 887, 887, 887, 887, 887, 889, 889, 889, 889, 889, 889, 889, 889, 892, 0, 892, 892, 892, 892, 892, 892, 895, 0, 895, 895, 895, 895, 895, 895, 895, 895, 896, 896, 896, 896, 896, 896, 896, 896, 896, 896, 896, 0, 0, 0, 0, 896, 896, 0, 0, 0, 0, 0, 0, 0, 896, 896, 896, 896, 896, 896, 898, 0, 898, 898, 898, 898, 898, 898, 898, 898, 899, 899, 899, 899, 899, 899, 899, 899, 900, 0, 900, 900, 900, 900, 900, 900, 902, 902, 902, 902, 902, 902, 902, 902, 905, 0, 905, 905, 905, 905, 905, 905, 908, 0, 908, 908, 908, 908, 908, 908, 908, 908, 928, 928, 928, 928, 928, 928, 928, 928, 0, 0, 0, 0, 0, 928, 928, 0, 0, 0, 0, 0, 0, 0, 928, 928, 928, 928, 928, 928, 929, 0, 929, 929, 929, 929, 929, 929, 929, 929, 929, 0, 0, 0, 0, 929, 929, 0, 0, 0, 0, 0, 0, 0, 929, 929, 929, 929, 929, 929, 930, 930, 930, 930, 930, 930, 930, 930, 0, 0, 0, 0, 0, 930, 930, 0, 0, 0, 0, 0, 0, 0, 930, 930, 930, 930, 930, 930, 932, 932, 932, 932, 932, 932, 932, 932, 933, 0, 933, 933, 933, 933, 933, 933, 0, 0, 933, 936, 0, 936, 936, 936, 936, 936, 936, 936, 936, 936, 0, 0, 0, 0, 936, 936, 0, 0, 0, 0, 0, 0, 0, 936, 936, 936, 936, 936, 936, 937, 937, 937, 937, 937, 937, 937, 937, 0, 0, 0, 0, 0, 937, 937, 0, 0, 0, 0, 0, 0, 0, 937, 937, 937, 937, 937, 937, 939, 939, 939, 939, 939, 939, 939, 939, 940, 0, 940, 940, 940, 940, 940, 940, 0, 0, 940, 943, 0, 943, 943, 943, 943, 943, 943, 943, 943, 943, 0, 0, 0, 0, 943, 943, 0, 0, 0, 0, 0, 0, 0, 943, 943, 943, 943, 943, 943, 944, 944, 944, 944, 944, 944, 944, 944, 0, 0, 0, 0, 0, 944, 944, 0, 0, 0, 0, 0, 0, 0, 944, 944, 944, 944, 944, 944, 946, 946, 946, 946, 946, 946, 946, 946, 947, 0, 947, 947, 947, 947, 947, 947, 0, 0, 947, 950, 0, 950, 950, 950, 950, 950, 950, 950, 950, 950, 0, 0, 0, 0, 950, 950, 0, 0, 0, 0, 0, 0, 0, 950, 950, 950, 950, 950, 950, 951, 951, 951, 951, 951, 951, 951, 951, 0, 0, 0, 0, 0, 951, 951, 0, 0, 0, 0, 0, 0, 0, 951, 951, 951, 951, 951, 951, 953, 953, 953, 953, 953, 953, 953, 953, 954, 0, 954, 954, 954, 954, 954, 954, 0, 0, 954, 957, 957, 957, 957, 957, 957, 957, 957, 958, 958, 958, 958, 958, 958, 958, 958, 959, 959, 959, 959, 959, 959, 959, 959, 962, 0, 962, 962, 962, 962, 962, 962, 962, 962, 966, 0, 966, 966, 966, 966, 966, 966, 966, 966, 970, 0, 970, 970, 970, 970, 970, 970, 970, 970, 970, 0, 0, 0, 0, 970, 970, 0, 0, 0, 0, 0, 0, 0, 970, 970, 970, 970, 970, 970, 971, 971, 971, 971, 971, 971, 971, 971, 977, 977, 977, 977, 977, 977, 977, 977, 977, 977, 981, 0, 981, 981, 981, 981, 981, 981, 981, 981, 985, 0, 985, 985, 985, 985, 985, 985, 985, 985, 991, 991, 991, 991, 991, 991, 991, 991, 992, 992, 992, 992, 992, 992, 992, 992, 993, 993, 993, 993, 993, 993, 993, 993, 995, 995, 995, 995, 995, 995, 995, 995, 998, 0, 998, 998, 998, 998, 998, 998, 1010, 1010, 1010, 1010, 1010, 1010, 1010, 1010, 1010, 0, 0, 0, 0, 1010, 1010, 0, 0, 0, 0, 0, 0, 0, 1010, 1010, 1010, 1010, 1010, 1010, 1011, 0, 1011, 1011, 1011, 1011, 1011, 1011, 1011, 1011, 1011, 0, 0, 0, 0, 1011, 1011, 0, 0, 0, 0, 0, 0, 0, 1011, 1011, 1011, 1011, 1011, 1011, 1012, 1012, 1012, 1012, 1012, 1012, 1012, 1012, 1017, 0, 1017, 1017, 1017, 1017, 1017, 1017, 1017, 1017, 1017, 0, 0, 0, 0, 1017, 1017, 0, 0, 0, 0, 0, 0, 0, 1017, 1017, 1017, 1017, 1017, 1017, 1018, 1018, 1018, 1018, 1018, 1018, 1018, 1018, 1024, 0, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 0, 0, 0, 0, 1024, 1024, 0, 0, 0, 0, 0, 0, 0, 1024, 1024, 1024, 1024, 1024, 1024, 1025, 1025, 1025, 1025, 1025, 1025, 1025, 1025, 1031, 0, 1031, 1031, 1031, 1031, 1031, 1031, 1031, 1031, 1031, 0, 0, 0, 0, 1031, 1031, 0, 0, 0, 0, 0, 0, 0, 1031, 1031, 1031, 1031, 1031, 1031, 1032, 1032, 1032, 1032, 1032, 1032, 1032, 1032, 1038, 0, 1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, 0, 0, 0, 0, 1038, 1038, 0, 0, 0, 0, 0, 0, 0, 1038, 1038, 1038, 1038, 1038, 1038, 1039, 1039, 1039, 1039, 1039, 1039, 1039, 1039, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, 1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, 1050, 0, 1050, 1050, 1050, 1050, 1050, 1050, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1055, 0, 1055, 1055, 1055, 1055, 1055, 1055, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1059, 0, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 0, 0, 0, 0, 1059, 1059, 0, 0, 0, 0, 0, 0, 0, 1059, 1059, 1059, 1059, 1059, 1059, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 0, 0, 0, 0, 0, 1060, 1060, 0, 0, 0, 0, 0, 0, 0, 1060, 1060, 1060, 1060, 1060, 1060, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1063, 0, 1063, 1063, 1063, 1063, 1063, 1063, 0, 0, 1063, 1067, 1067, 1067, 1067, 1067, 1067, 1067, 1067, 1069, 1069, 1069, 1069, 1069, 1069, 1069, 1069, 1072, 0, 1072, 1072, 1072, 1072, 1072, 1072, 1074, 1074, 1074, 1074, 1074, 1074, 1074, 1074, 1077, 0, 1077, 1077, 1077, 1077, 1077, 1077, 1081, 1081, 1081, 1081, 1081, 1081, 1081, 1081, 1082, 1082, 1082, 1082, 1082, 1082, 1082, 1082, 1086, 0, 1086, 1086, 1086, 1086, 1086, 1086, 1086, 1086, 1097, 0, 1097, 1097, 1097, 1097, 1097, 1097, 1097, 1097, 1097, 0, 0, 0, 0, 1097, 1097, 0, 0, 0, 0, 0, 0, 0, 1097, 1097, 1097, 1097, 1097, 1097, 1098, 1098, 1098, 1098, 1098, 1098, 1098, 1098, 1102, 0, 1102, 1102, 1102, 1102, 1102, 1102, 1102, 1102, 1102, 0, 0, 0, 0, 1102, 1102, 0, 0, 0, 0, 0, 0, 0, 1102, 1102, 1102, 1102, 1102, 1102, 1105, 1105, 1105, 1105, 1105, 1105, 1105, 1105, 1106, 0, 1106, 1106, 1106, 1106, 1106, 1106, 0, 0, 1106, 1109, 0, 1109, 1109, 1109, 1109, 1109, 1109, 1109, 1109, 1109, 0, 0, 0, 0, 1109, 1109, 0, 0, 0, 0, 0, 0, 0, 1109, 1109, 1109, 1109, 1109, 1109, 1112, 1112, 1112, 1112, 1112, 1112, 1112, 1112, 1113, 0, 1113, 1113, 1113, 1113, 1113, 1113, 0, 0, 1113, 1116, 0, 1116, 1116, 1116, 1116, 1116, 1116, 1116, 1116, 1116, 0, 0, 0, 0, 1116, 1116, 0, 0, 0, 0, 0, 0, 0, 1116, 1116, 1116, 1116, 1116, 1116, 1119, 1119, 1119, 1119, 1119, 1119, 1119, 1119, 1120, 0, 1120, 1120, 1120, 1120, 1120, 1120, 0, 0, 1120, 1123, 0, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 0, 0, 0, 0, 1123, 1123, 0, 0, 0, 0, 0, 0, 0, 1123, 1123, 1123, 1123, 1123, 1123, 1126, 1126, 1126, 1126, 1126, 1126, 1126, 1126, 1127, 0, 1127, 1127, 1127, 1127, 1127, 1127, 0, 0, 1127, 1130, 0, 1130, 1130, 1130, 1130, 1130, 1130, 1130, 1130, 1130, 0, 0, 0, 0, 1130, 1130, 0, 0, 0, 0, 0, 0, 0, 1130, 1130, 1130, 1130, 1130, 1130, 1133, 1133, 1133, 1133, 1133, 1133, 1133, 1133, 1134, 0, 1134, 1134, 1134, 1134, 1134, 1134, 0, 0, 1134, 1137, 1137, 1137, 1137, 1137, 1137, 1137, 1137, 1138, 1138, 1138, 1138, 1138, 1138, 1138, 1138, 1139, 1139, 1139, 1139, 1139, 1139, 1139, 1139, 1142, 0, 1142, 1142, 1142, 1142, 1142, 1142, 1142, 1142, 1146, 0, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1150, 0, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 0, 0, 0, 0, 1150, 1150, 0, 0, 0, 0, 0, 0, 0, 1150, 1150, 1150, 1150, 1150, 1150, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1161, 0, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1166, 1166, 1166, 1166, 1166, 1166, 1166, 1166, 1169, 0, 1169, 1169, 1169, 1169, 1169, 1169, 1172, 1172, 1172, 1172, 1172, 1172, 1172, 1172, 1173, 0, 1173, 1173, 1173, 1173, 1173, 1173, 1173, 1173, 1173, 0, 0, 0, 0, 1173, 1173, 0, 0, 0, 0, 0, 0, 0, 1173, 1173, 1173, 1173, 1173, 1173, 1176, 1176, 1176, 1176, 1176, 1176, 1176, 1176, 1177, 0, 1177, 1177, 1177, 1177, 1177, 1177, 0, 0, 1177, 1200, 1200, 1200, 1200, 1200, 1200, 1200, 1200, 1201, 1201, 1201, 1201, 1201, 1201, 1201, 1201, 1204, 1204, 1204, 1204, 1204, 1204, 1204, 1204, 1207, 0, 1207, 1207, 1207, 1207, 1207, 1207, 1209, 1209, 1209, 1209, 1209, 1209, 1209, 1209, 1212, 0, 1212, 1212, 1212, 1212, 1212, 1212, 1215, 1215, 1215, 1215, 1215, 1215, 1215, 1215, 1216, 0, 1216, 1216, 1216, 1216, 1216, 1216, 1216, 1216, 1216, 0, 0, 0, 0, 1216, 1216, 0, 0, 0, 0, 0, 0, 0, 1216, 1216, 1216, 1216, 1216, 1216, 1219, 1219, 1219, 1219, 1219, 1219, 1219, 1219, 1220, 0, 1220, 1220, 1220, 1220, 1220, 1220, 0, 0, 1220, 1224, 1224, 1224, 1224, 1224, 1224, 1224, 1224, 1226, 1226, 1226, 1226, 1226, 1226, 1226, 1226, 1229, 0, 1229, 1229, 1229, 1229, 1229, 1229, 1233, 1233, 1233, 1233, 1233, 1233, 1233, 1233, 1233, 1233, 1237, 0, 1237, 1237, 1237, 1237, 1237, 1237, 1237, 1237, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1261, 0, 1261, 1261, 1261, 1261, 1261, 1261, 1261, 1261, 1265, 0, 1265, 1265, 1265, 1265, 1265, 1265, 1265, 1265, 1273, 1273, 1273, 1273, 1273, 1273, 1273, 1273, 1273, 1273, 1278, 1278, 1278, 1278, 1278, 1278, 1278, 1278, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1283, 0, 1283, 1283, 1283, 1283, 1283, 1283, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1300, 0, 1300, 1300, 1300, 1300, 1300, 1300, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1305, 0, 1305, 1305, 1305, 1305, 1305, 1305, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1315, 0, 1315, 1315, 1315, 1315, 1315, 1315, 1315, 1315, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1331, 0, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1339, 0, 1339, 1339, 1339, 1339, 1339, 1339, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1348, 0, 1348, 1348, 1348, 1348, 1348, 1348, 1351, 1351, 1351, 1351, 1351, 1351, 1351, 1351, 1352, 1352, 1352, 1352, 1352, 1352, 1352, 1352, 1353, 1353, 1353, 1353, 1353, 1353, 1353, 1353, 1356, 1356, 1356, 1356, 1356, 1356, 1356, 1356, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1372, 0, 1372, 1373, 0, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1374, 1374, 0, 1374, 1375, 1375, 0, 1375, 1376, 0, 1376, 1377, 0, 1377, 1378, 1378, 0, 1378, 1379, 1379, 0, 1379, 1380, 0, 1380, 1381, 0, 1381, 1381, 1382, 1382, 0, 1382, 1383, 1383, 0, 1383, 1384, 1384, 0, 1384, 1385, 0, 1385, 1386, 0, 1386, 1387, 1387, 0, 1387, 1388, 1388, 0, 1388, 1389, 1389, 0, 1389, 1390, 0, 1390, 1391, 0, 1391, 1392, 1392, 1392, 1392, 0, 1392, 1393, 0, 1393, 1394, 1394, 0, 1394, 1395, 1395, 0, 1395, 1396, 1396, 1396, 1396, 0, 1396, 1397, 1397, 0, 1397, 1398, 1398, 0, 1398, 1399, 0, 1399, 1400, 1400, 1400, 1401, 1401, 1401, 1402, 1402, 0, 1402, 1403, 1403, 0, 1403, 1404, 1404, 0, 1404, 1405, 1405, 0, 1405, 1406, 1406, 0, 1406, 1407, 0, 1407, 1408, 0, 1408, 1409, 0, 1409, 1410, 0, 1410, 1411, 1411, 1411, 1411, 0, 1411, 1412, 0, 0, 1412, 0, 0, 1412, 1413, 1413, 1413, 1414, 1414, 0, 1414, 1415, 1415, 0, 1415, 1416, 1416, 1416, 1416, 0, 1416, 1417, 1417, 1417, 1417, 0, 1417, 1418, 1418, 0, 1418, 1419, 1419, 0, 1419, 1420, 1420, 0, 1420, 1421, 1421, 0, 1421, 1422, 0, 1422, 1423, 0, 1423, 1424, 0, 1424, 1425, 0, 1425, 1426, 0, 0, 1426, 0, 0, 1426, 1427, 1427, 0, 1427, 1428, 1428, 0, 1428, 1429, 1429, 0, 1429, 1430, 1430, 0, 1430, 1431, 1431, 0, 1431, 1432, 0, 1432, 1433, 0, 1433, 1434, 0, 1434, 1434, 0, 1434, 1435, 1435, 0, 1435, 1436, 1436, 1436, 1437, 1437, 1437, 1438, 0, 1438, 1439, 0, 1439, 1440, 0, 1440, 1441, 0, 1441, 1442, 1442, 1442, 1442, 0, 1442, 1443, 0, 0, 1443, 0, 0, 1443, 1444, 0, 1444, 1445, 0, 1445, 1446, 1446, 0, 1446, 1447, 1447, 0, 1447, 1448, 1448, 1448, 1448, 0, 1448, 1449, 1449, 1449, 1449, 0, 1449, 1450, 1450, 1450, 1450, 0, 1450, 1451, 1451, 0, 1451, 1452, 1452, 0, 1452, 1453, 1453, 0, 1453, 1454, 0, 1454, 1454, 0, 1454, 1455, 1455, 0, 1455, 1456, 0, 1456, 1457, 0, 1457, 1458, 1458, 0, 1458, 1459, 1459, 1459, 1460, 1460, 1460, 1461, 0, 1461, 1462, 0, 1462, 1463, 0, 1463, 1464, 0, 1464, 1465, 0, 1465, 1466, 0, 1466, 1467, 1467, 0, 1467, 1468, 1468, 0, 1468, 1469, 1469, 0, 1469, 1470, 1470, 0, 1470, 1471, 1471, 0, 1471, 1472, 1472, 0, 1472, 1473, 0, 1473, 1474, 0, 1474, 1475, 0, 1475, 1476, 0, 1476, 1477, 0, 1477, 1477, 0, 1477, 1478, 1478, 0, 1478, 1479, 1479, 1479, 1480, 1480, 1480, 1481, 0, 1481, 1482, 0, 1482, 1483, 0, 1483, 1484, 0, 1484, 1485, 1485, 1485, 1485, 0, 1485, 1486, 0, 1486, 1487, 0, 1487, 1488, 1488, 0, 1488, 1489, 1489, 1489, 1489, 0, 1489, 1490, 1490, 1490, 1490, 0, 1490, 1491, 1491, 1491, 1491, 0, 1491, 1492, 1492, 1492, 1492, 0, 1492, 1493, 1493, 0, 1493, 1494, 1494, 0, 1494, 1495, 1495, 0, 1495, 1496, 0, 1496, 1496, 0, 1496, 1497, 1497, 0, 1497, 1498, 0, 1498, 1498, 0, 1498, 1499, 1499, 0, 1499, 1500, 0, 1500, 1501, 0, 1501, 1502, 0, 1502, 1503, 0, 1503, 1504, 1504, 0, 1504, 1505, 1505, 1505, 1506, 1506, 1506, 1507, 0, 1507, 1508, 0, 1508, 1509, 0, 1509, 1510, 0, 1510, 1511, 0, 1511, 1512, 0, 1512, 1513, 1513, 0, 1513, 1514, 1514, 1514, 0, 0, 1514, 1515, 1515, 0, 1515, 1516, 0, 0, 1516, 1517, 0, 0, 1517, 1518, 0, 0, 1518, 1519, 0, 0, 1519, 1520, 0, 0, 1520, 1521, 1521, 0, 1521, 1522, 1522, 0, 1522, 1523, 1523, 0, 1523, 1524, 1524, 0, 1524, 1525, 1525, 0, 1525, 1526, 0, 1526, 1527, 0, 1527, 1528, 0, 1528, 1529, 0, 1529, 1530, 0, 1530, 1530, 0, 1530, 1531, 1531, 0, 1531, 1532, 1532, 1532, 1533, 1533, 1533, 1534, 0, 1534, 1535, 0, 1535, 1536, 0, 1536, 1537, 0, 1537, 1538, 1538, 1538, 1538, 0, 1538, 1539, 0, 1539, 1540, 0, 1540, 1541, 1541, 0, 1541, 1542, 1542, 0, 1542, 1543, 1543, 1543, 0, 0, 1543, 1544, 1544, 1544, 1544, 0, 1544, 1545, 1545, 1545, 0, 0, 1545, 1546, 1546, 1546, 1546, 0, 1546, 1547, 1547, 1547, 0, 0, 1547, 1548, 1548, 1548, 1548, 0, 1548, 1549, 1549, 1549, 0, 0, 1549, 1550, 1550, 1550, 1550, 0, 1550, 1551, 1551, 1551, 0, 0, 1551, 1552, 1552, 1552, 1552, 0, 1552, 1553, 1553, 1553, 0, 0, 1553, 1554, 1554, 0, 1554, 1555, 1555, 0, 1555, 1556, 1556, 0, 1556, 1557, 0, 1557, 1557, 0, 1557, 1558, 1558, 0, 1558, 1559, 0, 1559, 1559, 0, 1559, 1560, 1560, 0, 1560, 1561, 0, 1561, 1561, 0, 1561, 1562, 1562, 0, 1562, 1563, 0, 1563, 1564, 0, 1564, 1565, 0, 1565, 1566, 0, 1566, 1567, 1567, 0, 1567, 1568, 1568, 1568, 1569, 1569, 1569, 1570, 0, 1570, 1571, 0, 1571, 1572, 0, 1572, 1573, 0, 1573, 1574, 0, 0, 1574, 1575, 0, 1575, 1576, 0, 1576, 1577, 0, 1577, 1578, 0, 1578, 1579, 0, 1579, 1579, 0, 1579, 1580, 1580, 1580, 0, 0, 1580, 1581, 1581, 1581, 0, 0, 1581, 1582, 1582, 1582, 0, 0, 1582, 1583, 1583, 1583, 0, 0, 1583, 1584, 1584, 1584, 0, 0, 1584, 1585, 1585, 1585, 0, 0, 1585, 1586, 1586, 1586, 0, 0, 1586, 1587, 1587, 0, 1587, 1588, 1588, 0, 1588, 1589, 1589, 0, 1589, 1590, 1590, 0, 1590, 1591, 1591, 0, 1591, 1592, 1592, 0, 1592, 1593, 0, 1593, 1594, 0, 1594, 1595, 0, 1595, 1596, 0, 1596, 1597, 0, 1597, 1597, 0, 1597, 1598, 1598, 0, 1598, 1599, 1599, 1599, 1600, 1600, 1600, 1601, 0, 1601, 1602, 0, 1602, 1603, 0, 1603, 1604, 0, 1604, 1605, 1605, 1605, 1605, 0, 1605, 1606, 1606, 1606, 0, 0, 1606, 1607, 0, 1607, 1608, 0, 1608, 1609, 1609, 1609, 0, 0, 1609, 1610, 1610, 1610, 0, 0, 1610, 1611, 1611, 1611, 0, 0, 1611, 1612, 1612, 1612, 0, 0, 1612, 1613, 1613, 1613, 0, 0, 1613, 1614, 1614, 1614, 0, 0, 1614, 1615, 1615, 1615, 0, 0, 1615, 1616, 1616, 0, 1616, 1617, 1617, 0, 1617, 1618, 0, 1618, 1618, 0, 1618, 1619, 1619, 0, 1619, 1620, 0, 1620, 1620, 0, 1620, 1621, 1621, 0, 1621, 1622, 0, 1622, 1622, 0, 1622, 1623, 1623, 0, 1623, 1624, 0, 1624, 1624, 0, 1624, 1625, 1625, 0, 1625, 1626, 0, 1626, 1627, 0, 1627, 1628, 0, 1628, 1629, 0, 1629, 1630, 1630, 0, 1630, 1631, 1631, 1631, 1632, 1632, 1632, 1633, 0, 1633, 1634, 0, 1634, 1635, 0, 1635, 1636, 0, 1636, 1637, 1637, 1637, 0, 0, 1637, 1638, 0, 1638, 1639, 0, 1639, 1640, 1640, 1640, 0, 0, 1640, 1641, 1641, 0, 1641, 1642, 0, 0, 1642, 1643, 1643, 0, 1643, 1644, 0, 0, 1644, 1645, 1645, 0, 1645, 1646, 0, 0, 1646, 1647, 1647, 0, 1647, 1648, 0, 0, 1648, 1649, 1649, 0, 1649, 1650, 0, 0, 1650, 1651, 1651, 0, 1651, 1652, 0, 0, 1652, 1653, 1653, 0, 1653, 1654, 0, 1654, 1655, 0, 1655, 1656, 0, 1656, 1657, 0, 1657, 1658, 0, 1658, 1658, 0, 1658, 1659, 1659, 0, 1659, 1660, 1660, 1660, 1661, 1661, 1661, 1662, 0, 1662, 1663, 0, 1663, 1664, 1664, 1664, 0, 0, 1664, 1665, 0, 1665, 1666, 0, 1666, 1667, 1667, 0, 1667, 1668, 1668, 0, 1668, 1669, 0, 0, 1669, 1670, 0, 1670, 1670, 0, 1670, 1671, 0, 0, 1671, 1672, 0, 1672, 1672, 0, 1672, 1673, 0, 0, 1673, 1674, 0, 1674, 1674, 0, 1674, 1675, 0, 0, 1675, 1676, 0, 1676, 1676, 0, 1676, 1677, 0, 0, 1677, 1678, 0, 1678, 1678, 0, 1678, 1679, 0, 0, 1679, 1680, 0, 1680, 1681, 0, 1681, 1682, 0, 0, 1682, 1683, 1683, 0, 1683, 1684, 1684, 1684, 1685, 0, 1685, 1686, 1686, 1686, 1687, 1687, 1687, 1688, 0, 1688, 1689, 0, 1689, 1690, 0, 1690, 1690, 0, 1690, 1691, 0, 0, 1691, 1692, 0, 0, 1692, 1693, 0, 0, 1693, 1694, 0, 0, 1694, 1695, 0, 0, 1695, 1696, 0, 0, 1696, 1697, 0, 0, 1697, 1698, 0, 1698, 1699, 0, 1699, 1700, 0, 1700, 1701, 0, 1701, 1702, 0, 1702, 1702, 0, 1702, 1703, 0, 0, 1703, 1704, 1704, 1704, 1705, 1705, 1705, 1706, 1706, 1706, 1707, 0, 1707, 1708, 0, 0, 1708, 1709, 0, 0, 1709, 1710, 0, 0, 1710, 1711, 0, 0, 1711, 1712, 0, 0, 1712, 1713, 0, 0, 1713, 1714, 0, 0, 1714, 1715, 0, 1715, 1716, 0, 1716, 1717, 0, 0, 1717, 1718, 1718, 1718, 1719, 0, 1719, 1720, 0, 1720, 1721, 0, 0, 1721, 1722, 0, 1722, 1723, 0, 1723, 1724, 0, 0, 1724, 1725, 0, 1725, 1726, 0, 1726, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370 } ; static yy_state_type yy_last_accepting_state; static char *yy_last_accepting_cpos; extern int yy_policy_parser_flex_debug; int yy_policy_parser_flex_debug = 0; /* The intent behind this definition is that it'll catch * any uses of REJECT which flex missed. */ #define REJECT reject_used_but_not_detected #define yymore() yymore_used_but_not_detected #define YY_MORE_ADJ 0 #define YY_RESTORE_YY_MORE_OFFSET char *yy_policy_parsertext; #line 1 "policy.l" #line 2 "policy.l" #include "policy_module.h" #include "libxorp/xorp.h" #include "policy/common/policy_utils.hh" #include "policy_parser.hh" #include "yacc.yy_policy_parser.cc.h" #define yylval yy_policy_parserlval #define yyerror yy_policy_parsererror #define yyparse yy_policy_parserparse void yyerror(const char *m); extern int yyparse(void); using namespace policy_parser; // instantiate the globals here. vector* policy_parser::_parser_nodes; unsigned policy_parser::_parser_lineno; // try not to pollute namespace { string _last_error; Term::BLOCKS _block; } #line 2947 "lex.yy_policy_parser.cc" #define INITIAL 0 #define STR 1 #ifndef YY_NO_UNISTD_H /* Special case for "unistd.h", since it is non-ANSI. We include it way * down here because we want the user's section 1 to have been scanned first. * The user has a chance to override it with an option. */ #include #endif #ifndef YY_EXTRA_TYPE #define YY_EXTRA_TYPE void * #endif static int yy_init_globals (void ); /* Macros after this point can all be overridden by user definitions in * section 1. */ #ifndef YY_SKIP_YYWRAP #ifdef __cplusplus extern "C" int yy_policy_parserwrap (void ); #else extern int yy_policy_parserwrap (void ); #endif #endif #ifndef yytext_ptr static void yy_flex_strncpy (char *,yyconst char *,int ); #endif #ifdef YY_NEED_STRLEN static int yy_flex_strlen (yyconst char * ); #endif #ifndef YY_NO_INPUT #ifdef __cplusplus static int yyinput (void ); #else static int input (void ); #endif #endif /* Amount of stuff to slurp up with each read. */ #ifndef YY_READ_BUF_SIZE #define YY_READ_BUF_SIZE 8192 #endif /* Copy whatever the last rule matched to the standard output. */ #ifndef ECHO /* This used to be an fputs(), but since the string might contain NUL's, * we now use fwrite(). */ #define ECHO (void) fwrite( yy_policy_parsertext, yy_policy_parserleng, 1, yy_policy_parserout ) #endif /* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, * is returned in "result". */ #ifndef YY_INPUT #define YY_INPUT(buf,result,max_size) \ if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ { \ int c = '*'; \ int n; \ for ( n = 0; n < max_size && \ (c = getc( yy_policy_parserin )) != EOF && c != '\n'; ++n ) \ buf[n] = (char) c; \ if ( c == '\n' ) \ buf[n++] = (char) c; \ if ( c == EOF && ferror( yy_policy_parserin ) ) \ YY_FATAL_ERROR( "input in flex scanner failed" ); \ result = n; \ } \ else \ { \ errno=0; \ while ( (result = fread(buf, 1, max_size, yy_policy_parserin))==0 && ferror(yy_policy_parserin)) \ { \ if( errno != EINTR) \ { \ YY_FATAL_ERROR( "input in flex scanner failed" ); \ break; \ } \ errno=0; \ clearerr(yy_policy_parserin); \ } \ }\ \ #endif /* No semi-colon after return; correct usage is to write "yyterminate();" - * we don't want an extra ';' after the "return" because that will cause * some compilers to complain about unreachable statements. */ #ifndef yyterminate #define yyterminate() return YY_NULL #endif /* Number of entries by which start-condition stack grows. */ #ifndef YY_START_STACK_INCR #define YY_START_STACK_INCR 25 #endif /* Report a fatal error. */ #ifndef YY_FATAL_ERROR #define YY_FATAL_ERROR(msg) yy_fatal_error( msg ) #endif /* end tables serialization structures and prototypes */ /* Default declaration of generated scanner - a define so the user can * easily add parameters. */ #ifndef YY_DECL #define YY_DECL_IS_OURS 1 extern int yy_policy_parserlex (void); #define YY_DECL int yy_policy_parserlex (void) #endif /* !YY_DECL */ /* Code executed at the beginning of each rule, after yy_policy_parsertext and yy_policy_parserleng * have been set up. */ #ifndef YY_USER_ACTION #define YY_USER_ACTION #endif /* Code executed at the end of each rule. */ #ifndef YY_BREAK #define YY_BREAK break; #endif #define YY_RULE_SETUP \ YY_USER_ACTION /** The main scanner function which does all the work. */ YY_DECL { register yy_state_type yy_current_state; register char *yy_cp, *yy_bp; register int yy_act; #line 62 "policy.l" #line 3102 "lex.yy_policy_parser.cc" if ( !(yy_init) ) { (yy_init) = 1; #ifdef YY_USER_INIT YY_USER_INIT; #endif if ( ! (yy_start) ) (yy_start) = 1; /* first start state */ if ( ! yy_policy_parserin ) yy_policy_parserin = stdin; if ( ! yy_policy_parserout ) yy_policy_parserout = stdout; if ( ! YY_CURRENT_BUFFER ) { yy_policy_parserensure_buffer_stack (); YY_CURRENT_BUFFER_LVALUE = yy_policy_parser_create_buffer(yy_policy_parserin,YY_BUF_SIZE ); } yy_policy_parser_load_buffer_state( ); } while ( 1 ) /* loops until end-of-file is reached */ { yy_cp = (yy_c_buf_p); /* Support of yy_policy_parsertext. */ *yy_cp = (yy_hold_char); /* yy_bp points to the position in yy_ch_buf of the start of * the current run. */ yy_bp = yy_cp; yy_current_state = (yy_start); yy_match: do { register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)]; if ( yy_accept[yy_current_state] ) { (yy_last_accepting_state) = yy_current_state; (yy_last_accepting_cpos) = yy_cp; } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 1371 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; ++yy_cp; } while ( yy_current_state != 1370 ); yy_cp = (yy_last_accepting_cpos); yy_current_state = (yy_last_accepting_state); yy_find_action: yy_act = yy_accept[yy_current_state]; YY_DO_BEFORE_ACTION; do_action: /* This label is used only to access EOF actions. */ switch ( yy_act ) { /* beginning of action switch */ case 0: /* must back up */ /* undo the effects of YY_DO_BEFORE_ACTION */ *yy_cp = (yy_hold_char); yy_cp = (yy_last_accepting_cpos); yy_current_state = (yy_last_accepting_state); goto yy_find_action; case 1: YY_RULE_SETUP #line 64 "policy.l" { yylval.c_str = strdup(yy_policy_parsertext); return YY_UINTRANGE; } YY_BREAK case 2: YY_RULE_SETUP #line 68 "policy.l" { yylval.c_str = strdup(yy_policy_parsertext); return YY_UINT; } YY_BREAK case 3: YY_RULE_SETUP #line 72 "policy.l" { yylval.c_str = strdup(yy_policy_parsertext); return YY_INT; } YY_BREAK case 4: YY_RULE_SETUP #line 76 "policy.l" { yylval.c_str = strdup(yy_policy_parsertext); return YY_BOOL; } YY_BREAK case 5: YY_RULE_SETUP #line 80 "policy.l" { yylval.c_str = strdup(yy_policy_parsertext); return YY_BOOL; } YY_BREAK case 6: YY_RULE_SETUP #line 84 "policy.l" BEGIN(STR); YY_BREAK case 7: YY_RULE_SETUP #line 86 "policy.l" BEGIN(INITIAL); YY_BREAK case 8: /* rule 8 can match eol */ YY_RULE_SETUP #line 88 "policy.l" { yylval.c_str = strdup(yy_policy_parsertext); _parser_lineno += policy_utils::count_nl(yy_policy_parsertext); /* XXX: a string can be started with " but terminated with ' * and vice versa... */ return YY_STR; } YY_BREAK case 9: YY_RULE_SETUP #line 96 "policy.l" { yylval.c_str = strdup(yy_policy_parsertext); return YY_IPV4RANGE; } YY_BREAK case 10: YY_RULE_SETUP #line 101 "policy.l" { yylval.c_str = strdup(yy_policy_parsertext); return YY_IPV4; } YY_BREAK case 11: YY_RULE_SETUP #line 106 "policy.l" { yylval.c_str = strdup(yy_policy_parsertext); return YY_IPV4NET; } YY_BREAK case 12: YY_RULE_SETUP #line 112 "policy.l" { yylval.c_str = strdup(yy_policy_parsertext); return YY_IPV6RANGE; } YY_BREAK case 13: YY_RULE_SETUP #line 117 "policy.l" { yylval.c_str = strdup(yy_policy_parsertext); return YY_IPV6; } YY_BREAK case 14: YY_RULE_SETUP #line 122 "policy.l" { yylval.c_str = strdup(yy_policy_parsertext); return YY_IPV6NET; } YY_BREAK case 15: YY_RULE_SETUP #line 127 "policy.l" { // the colon is an alias for asignment in action and equality // in the source / dest blocks. if (_block == Term::ACTION) return YY_ASSIGN; else return YY_EQ; } YY_BREAK case 16: YY_RULE_SETUP #line 136 "policy.l" return YY_LPAR; YY_BREAK case 17: YY_RULE_SETUP #line 137 "policy.l" return YY_RPAR; YY_BREAK case 18: YY_RULE_SETUP #line 138 "policy.l" return YY_EQ; YY_BREAK case 19: YY_RULE_SETUP #line 139 "policy.l" return YY_NE; YY_BREAK case 20: YY_RULE_SETUP #line 140 "policy.l" return YY_LE; YY_BREAK case 21: YY_RULE_SETUP #line 141 "policy.l" return YY_GE; YY_BREAK case 22: YY_RULE_SETUP #line 142 "policy.l" return YY_LT; YY_BREAK case 23: YY_RULE_SETUP #line 143 "policy.l" return YY_GT; YY_BREAK case 24: YY_RULE_SETUP #line 144 "policy.l" return YY_ADD; YY_BREAK case 25: YY_RULE_SETUP #line 145 "policy.l" return YY_MUL; YY_BREAK case 26: YY_RULE_SETUP #line 146 "policy.l" return YY_SUB; YY_BREAK case 27: YY_RULE_SETUP #line 147 "policy.l" return YY_ASSIGN; YY_BREAK case 28: YY_RULE_SETUP #line 148 "policy.l" return YY_PLUS_EQUALS; YY_BREAK case 29: YY_RULE_SETUP #line 149 "policy.l" return YY_MINUS_EQUALS; YY_BREAK case 30: YY_RULE_SETUP #line 150 "policy.l" return YY_OR; YY_BREAK case 31: YY_RULE_SETUP #line 151 "policy.l" return YY_AND; YY_BREAK case 32: YY_RULE_SETUP #line 152 "policy.l" return YY_NOT; YY_BREAK case 33: YY_RULE_SETUP #line 154 "policy.l" return YY_IPNET_EQ; YY_BREAK case 34: YY_RULE_SETUP #line 155 "policy.l" return YY_IPNET_LT; YY_BREAK case 35: YY_RULE_SETUP #line 156 "policy.l" return YY_IPNET_GT; YY_BREAK case 36: YY_RULE_SETUP #line 157 "policy.l" return YY_IPNET_LE; YY_BREAK case 37: YY_RULE_SETUP #line 158 "policy.l" return YY_IPNET_GE; YY_BREAK case 38: YY_RULE_SETUP #line 159 "policy.l" return YY_AND; YY_BREAK case 39: YY_RULE_SETUP #line 160 "policy.l" return YY_OR; YY_BREAK case 40: YY_RULE_SETUP #line 161 "policy.l" return YY_XOR; YY_BREAK case 41: YY_RULE_SETUP #line 162 "policy.l" return YY_NOT; YY_BREAK case 42: YY_RULE_SETUP #line 163 "policy.l" return YY_PLUS_EQUALS; YY_BREAK case 43: YY_RULE_SETUP #line 164 "policy.l" return YY_MINUS_EQUALS; YY_BREAK case 44: YY_RULE_SETUP #line 165 "policy.l" return YY_HEAD; YY_BREAK case 45: YY_RULE_SETUP #line 166 "policy.l" return YY_CTR; YY_BREAK case 46: YY_RULE_SETUP #line 167 "policy.l" return YY_NE_INT; YY_BREAK case 47: YY_RULE_SETUP #line 168 "policy.l" return YY_ACCEPT; YY_BREAK case 48: YY_RULE_SETUP #line 169 "policy.l" return YY_REJECT; YY_BREAK case 49: YY_RULE_SETUP #line 170 "policy.l" return YY_SET; YY_BREAK case 50: YY_RULE_SETUP #line 171 "policy.l" return YY_REGEX; YY_BREAK case 51: YY_RULE_SETUP #line 172 "policy.l" return YY_PROTOCOL; YY_BREAK case 52: YY_RULE_SETUP #line 173 "policy.l" return YY_NEXT; YY_BREAK case 53: YY_RULE_SETUP #line 174 "policy.l" return YY_POLICY; YY_BREAK case 54: YY_RULE_SETUP #line 175 "policy.l" return YY_TERM; YY_BREAK case 55: YY_RULE_SETUP #line 177 "policy.l" { yylval.c_str = strdup(yy_policy_parsertext); return YY_ID; } YY_BREAK case 56: YY_RULE_SETUP #line 181 "policy.l" return YY_SEMICOLON; YY_BREAK case 57: YY_RULE_SETUP #line 183 "policy.l" /* eat blanks */ YY_BREAK case 58: /* rule 58 can match eol */ YY_RULE_SETUP #line 185 "policy.l" _parser_lineno++; YY_BREAK case 59: YY_RULE_SETUP #line 187 "policy.l" { yyerror("Unknown character"); } YY_BREAK case 60: YY_RULE_SETUP #line 189 "policy.l" ECHO; YY_BREAK #line 3526 "lex.yy_policy_parser.cc" case YY_STATE_EOF(INITIAL): case YY_STATE_EOF(STR): yyterminate(); case YY_END_OF_BUFFER: { /* Amount of text matched not including the EOB char. */ int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1; /* Undo the effects of YY_DO_BEFORE_ACTION. */ *yy_cp = (yy_hold_char); YY_RESTORE_YY_MORE_OFFSET if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) { /* We're scanning a new file or input source. It's * possible that this happened because the user * just pointed yy_policy_parserin at a new source and called * yy_policy_parserlex(). If so, then we have to assure * consistency between YY_CURRENT_BUFFER and our * globals. Here is the right place to do so, because * this is the first action (other than possibly a * back-up) that will match for the new input source. */ (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; YY_CURRENT_BUFFER_LVALUE->yy_input_file = yy_policy_parserin; YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; } /* Note that here we test for yy_c_buf_p "<=" to the position * of the first EOB in the buffer, since yy_c_buf_p will * already have been incremented past the NUL character * (since all states make transitions on EOB to the * end-of-buffer state). Contrast this with the test * in input(). */ if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) { /* This was really a NUL. */ yy_state_type yy_next_state; (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state( ); /* Okay, we're now positioned to make the NUL * transition. We couldn't have * yy_get_previous_state() go ahead and do it * for us because it doesn't know how to deal * with the possibility of jamming (and we don't * want to build jamming into it because then it * will run more slowly). */ yy_next_state = yy_try_NUL_trans( yy_current_state ); yy_bp = (yytext_ptr) + YY_MORE_ADJ; if ( yy_next_state ) { /* Consume the NUL. */ yy_cp = ++(yy_c_buf_p); yy_current_state = yy_next_state; goto yy_match; } else { yy_cp = (yy_last_accepting_cpos); yy_current_state = (yy_last_accepting_state); goto yy_find_action; } } else switch ( yy_get_next_buffer( ) ) { case EOB_ACT_END_OF_FILE: { (yy_did_buffer_switch_on_eof) = 0; if ( yy_policy_parserwrap( ) ) { /* Note: because we've taken care in * yy_get_next_buffer() to have set up * yy_policy_parsertext, we can now set up * yy_c_buf_p so that if some total * hoser (like flex itself) wants to * call the scanner after we return the * YY_NULL, it'll still work - another * YY_NULL will get returned. */ (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ; yy_act = YY_STATE_EOF(YY_START); goto do_action; } else { if ( ! (yy_did_buffer_switch_on_eof) ) YY_NEW_FILE; } break; } case EOB_ACT_CONTINUE_SCAN: (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state( ); yy_cp = (yy_c_buf_p); yy_bp = (yytext_ptr) + YY_MORE_ADJ; goto yy_match; case EOB_ACT_LAST_MATCH: (yy_c_buf_p) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)]; yy_current_state = yy_get_previous_state( ); yy_cp = (yy_c_buf_p); yy_bp = (yytext_ptr) + YY_MORE_ADJ; goto yy_find_action; } break; } default: YY_FATAL_ERROR( "fatal flex scanner internal error--no action found" ); } /* end of action switch */ } /* end of scanning one token */ } /* end of yy_policy_parserlex */ /* yy_get_next_buffer - try to read in a new buffer * * Returns a code representing an action: * EOB_ACT_LAST_MATCH - * EOB_ACT_CONTINUE_SCAN - continue scanning from current position * EOB_ACT_END_OF_FILE - end of file */ static int yy_get_next_buffer (void) { register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; register char *source = (yytext_ptr); register int number_to_move, i; int ret_val; if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] ) YY_FATAL_ERROR( "fatal flex scanner internal error--end of buffer missed" ); if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) { /* Don't try to fill the buffer, so this is an EOF. */ if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 ) { /* We matched a single character, the EOB, so * treat this as a final EOF. */ return EOB_ACT_END_OF_FILE; } else { /* We matched some text prior to the EOB, first * process it. */ return EOB_ACT_LAST_MATCH; } } /* Try to read more data. */ /* First move last chars to start of buffer. */ number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1; for ( i = 0; i < number_to_move; ++i ) *(dest++) = *(source++); if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) /* don't do the read, it's not guaranteed to return an EOF, * just force an EOF */ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0; else { int num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; while ( num_to_read <= 0 ) { /* Not enough room in the buffer - grow it. */ /* just a shorter name for the current buffer */ YY_BUFFER_STATE b = YY_CURRENT_BUFFER; int yy_c_buf_p_offset = (int) ((yy_c_buf_p) - b->yy_ch_buf); if ( b->yy_is_our_buffer ) { int new_size = b->yy_buf_size * 2; if ( new_size <= 0 ) b->yy_buf_size += b->yy_buf_size / 8; else b->yy_buf_size *= 2; b->yy_ch_buf = (char *) /* Include room in for 2 EOB chars. */ yy_policy_parserrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 ); } else /* Can't grow it, we don't own it. */ b->yy_ch_buf = 0; if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "fatal error - scanner input buffer overflow" ); (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset]; num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; } if ( num_to_read > YY_READ_BUF_SIZE ) num_to_read = YY_READ_BUF_SIZE; /* Read in more data. */ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), (yy_n_chars), num_to_read ); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); } if ( (yy_n_chars) == 0 ) { if ( number_to_move == YY_MORE_ADJ ) { ret_val = EOB_ACT_END_OF_FILE; yy_policy_parserrestart(yy_policy_parserin ); } else { ret_val = EOB_ACT_LAST_MATCH; YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_EOF_PENDING; } } else ret_val = EOB_ACT_CONTINUE_SCAN; (yy_n_chars) += number_to_move; YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR; YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR; (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; return ret_val; } /* yy_get_previous_state - get the state just before the EOB char was reached */ static yy_state_type yy_get_previous_state (void) { register yy_state_type yy_current_state; register char *yy_cp; yy_current_state = (yy_start); for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp ) { register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); if ( yy_accept[yy_current_state] ) { (yy_last_accepting_state) = yy_current_state; (yy_last_accepting_cpos) = yy_cp; } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 1371 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; } return yy_current_state; } /* yy_try_NUL_trans - try to make a transition on the NUL character * * synopsis * next_state = yy_try_NUL_trans( current_state ); */ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state ) { register int yy_is_jam; register char *yy_cp = (yy_c_buf_p); register YY_CHAR yy_c = 1; if ( yy_accept[yy_current_state] ) { (yy_last_accepting_state) = yy_current_state; (yy_last_accepting_cpos) = yy_cp; } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 1371 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; yy_is_jam = (yy_current_state == 1370); return yy_is_jam ? 0 : yy_current_state; } #ifndef YY_NO_INPUT #ifdef __cplusplus static int yyinput (void) #else static int input (void) #endif { int c; *(yy_c_buf_p) = (yy_hold_char); if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR ) { /* yy_c_buf_p now points to the character we want to return. * If this occurs *before* the EOB characters, then it's a * valid NUL; if not, then we've hit the end of the buffer. */ if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) /* This was really a NUL. */ *(yy_c_buf_p) = '\0'; else { /* need more input */ int offset = (yy_c_buf_p) - (yytext_ptr); ++(yy_c_buf_p); switch ( yy_get_next_buffer( ) ) { case EOB_ACT_LAST_MATCH: /* This happens because yy_g_n_b() * sees that we've accumulated a * token and flags that we need to * try matching the token before * proceeding. But for input(), * there's no matching to consider. * So convert the EOB_ACT_LAST_MATCH * to EOB_ACT_END_OF_FILE. */ /* Reset buffer status. */ yy_policy_parserrestart(yy_policy_parserin ); /*FALLTHROUGH*/ case EOB_ACT_END_OF_FILE: { if ( yy_policy_parserwrap( ) ) return EOF; if ( ! (yy_did_buffer_switch_on_eof) ) YY_NEW_FILE; #ifdef __cplusplus return yyinput(); #else return input(); #endif } case EOB_ACT_CONTINUE_SCAN: (yy_c_buf_p) = (yytext_ptr) + offset; break; } } } c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */ *(yy_c_buf_p) = '\0'; /* preserve yy_policy_parsertext */ (yy_hold_char) = *++(yy_c_buf_p); return c; } #endif /* ifndef YY_NO_INPUT */ /** Immediately switch to a different input stream. * @param input_file A readable stream. * * @note This function does not reset the start condition to @c INITIAL . */ void yy_policy_parserrestart (FILE * input_file ) { if ( ! YY_CURRENT_BUFFER ){ yy_policy_parserensure_buffer_stack (); YY_CURRENT_BUFFER_LVALUE = yy_policy_parser_create_buffer(yy_policy_parserin,YY_BUF_SIZE ); } yy_policy_parser_init_buffer(YY_CURRENT_BUFFER,input_file ); yy_policy_parser_load_buffer_state( ); } /** Switch to a different input buffer. * @param new_buffer The new input buffer. * */ void yy_policy_parser_switch_to_buffer (YY_BUFFER_STATE new_buffer ) { /* TODO. We should be able to replace this entire function body * with * yy_policy_parserpop_buffer_state(); * yy_policy_parserpush_buffer_state(new_buffer); */ yy_policy_parserensure_buffer_stack (); if ( YY_CURRENT_BUFFER == new_buffer ) return; if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ *(yy_c_buf_p) = (yy_hold_char); YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); } YY_CURRENT_BUFFER_LVALUE = new_buffer; yy_policy_parser_load_buffer_state( ); /* We don't actually know whether we did this switch during * EOF (yy_policy_parserwrap()) processing, but the only time this flag * is looked at is after yy_policy_parserwrap() is called, so it's safe * to go ahead and always set it. */ (yy_did_buffer_switch_on_eof) = 1; } static void yy_policy_parser_load_buffer_state (void) { (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; yy_policy_parserin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; (yy_hold_char) = *(yy_c_buf_p); } /** Allocate and initialize an input buffer state. * @param file A readable stream. * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. * * @return the allocated buffer state. */ YY_BUFFER_STATE yy_policy_parser_create_buffer (FILE * file, int size ) { YY_BUFFER_STATE b; b = (YY_BUFFER_STATE) yy_policy_parseralloc(sizeof( struct yy_buffer_state ) ); if ( ! b ) YY_FATAL_ERROR( "out of dynamic memory in yy_policy_parser_create_buffer()" ); b->yy_buf_size = size; /* yy_ch_buf has to be 2 characters longer than the size given because * we need to put in 2 end-of-buffer characters. */ b->yy_ch_buf = (char *) yy_policy_parseralloc(b->yy_buf_size + 2 ); if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "out of dynamic memory in yy_policy_parser_create_buffer()" ); b->yy_is_our_buffer = 1; yy_policy_parser_init_buffer(b,file ); return b; } /** Destroy the buffer. * @param b a buffer created with yy_policy_parser_create_buffer() * */ void yy_policy_parser_delete_buffer (YY_BUFFER_STATE b ) { if ( ! b ) return; if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; if ( b->yy_is_our_buffer ) yy_policy_parserfree((void *) b->yy_ch_buf ); yy_policy_parserfree((void *) b ); } /* Initializes or reinitializes a buffer. * This function is sometimes called more than once on the same buffer, * such as during a yy_policy_parserrestart() or at EOF. */ static void yy_policy_parser_init_buffer (YY_BUFFER_STATE b, FILE * file ) { int oerrno = errno; yy_policy_parser_flush_buffer(b ); b->yy_input_file = file; b->yy_fill_buffer = 1; /* If b is the current buffer, then yy_policy_parser_init_buffer was _probably_ * called from yy_policy_parserrestart() or through yy_get_next_buffer. * In that case, we don't want to reset the lineno or column. */ if (b != YY_CURRENT_BUFFER){ b->yy_bs_lineno = 1; b->yy_bs_column = 0; } b->yy_is_interactive = 0; errno = oerrno; } /** Discard all buffered characters. On the next scan, YY_INPUT will be called. * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. * */ void yy_policy_parser_flush_buffer (YY_BUFFER_STATE b ) { if ( ! b ) return; b->yy_n_chars = 0; /* We always need two end-of-buffer characters. The first causes * a transition to the end-of-buffer state. The second causes * a jam in that state. */ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; b->yy_buf_pos = &b->yy_ch_buf[0]; b->yy_at_bol = 1; b->yy_buffer_status = YY_BUFFER_NEW; if ( b == YY_CURRENT_BUFFER ) yy_policy_parser_load_buffer_state( ); } /** Pushes the new state onto the stack. The new state becomes * the current state. This function will allocate the stack * if necessary. * @param new_buffer The new state. * */ void yy_policy_parserpush_buffer_state (YY_BUFFER_STATE new_buffer ) { if (new_buffer == NULL) return; yy_policy_parserensure_buffer_stack(); /* This block is copied from yy_policy_parser_switch_to_buffer. */ if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ *(yy_c_buf_p) = (yy_hold_char); YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); } /* Only push if top exists. Otherwise, replace top. */ if (YY_CURRENT_BUFFER) (yy_buffer_stack_top)++; YY_CURRENT_BUFFER_LVALUE = new_buffer; /* copied from yy_policy_parser_switch_to_buffer. */ yy_policy_parser_load_buffer_state( ); (yy_did_buffer_switch_on_eof) = 1; } /** Removes and deletes the top of the stack, if present. * The next element becomes the new top. * */ void yy_policy_parserpop_buffer_state (void) { if (!YY_CURRENT_BUFFER) return; yy_policy_parser_delete_buffer(YY_CURRENT_BUFFER ); YY_CURRENT_BUFFER_LVALUE = NULL; if ((yy_buffer_stack_top) > 0) --(yy_buffer_stack_top); if (YY_CURRENT_BUFFER) { yy_policy_parser_load_buffer_state( ); (yy_did_buffer_switch_on_eof) = 1; } } /* Allocates the stack if it does not exist. * Guarantees space for at least one push. */ static void yy_policy_parserensure_buffer_stack (void) { int num_to_alloc; if (!(yy_buffer_stack)) { /* First allocation is just for 2 elements, since we don't know if this * scanner will even need a stack. We use 2 instead of 1 to avoid an * immediate realloc on the next call. */ num_to_alloc = 1; (yy_buffer_stack) = (struct yy_buffer_state**)yy_policy_parseralloc (num_to_alloc * sizeof(struct yy_buffer_state*) ); memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*)); (yy_buffer_stack_max) = num_to_alloc; (yy_buffer_stack_top) = 0; return; } if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){ /* Increase the buffer to prepare for a possible push. */ int grow_size = 8 /* arbitrary grow size */; num_to_alloc = (yy_buffer_stack_max) + grow_size; (yy_buffer_stack) = (struct yy_buffer_state**)yy_policy_parserrealloc ((yy_buffer_stack), num_to_alloc * sizeof(struct yy_buffer_state*) ); /* zero only the new slots.*/ memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*)); (yy_buffer_stack_max) = num_to_alloc; } } /** Setup the input buffer state to scan directly from a user-specified character buffer. * @param base the character buffer * @param size the size in bytes of the character buffer * * @return the newly allocated buffer state object. */ YY_BUFFER_STATE yy_policy_parser_scan_buffer (char * base, yy_size_t size ) { YY_BUFFER_STATE b; if ( size < 2 || base[size-2] != YY_END_OF_BUFFER_CHAR || base[size-1] != YY_END_OF_BUFFER_CHAR ) /* They forgot to leave room for the EOB's. */ return 0; b = (YY_BUFFER_STATE) yy_policy_parseralloc(sizeof( struct yy_buffer_state ) ); if ( ! b ) YY_FATAL_ERROR( "out of dynamic memory in yy_policy_parser_scan_buffer()" ); b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ b->yy_buf_pos = b->yy_ch_buf = base; b->yy_is_our_buffer = 0; b->yy_input_file = 0; b->yy_n_chars = b->yy_buf_size; b->yy_is_interactive = 0; b->yy_at_bol = 1; b->yy_fill_buffer = 0; b->yy_buffer_status = YY_BUFFER_NEW; yy_policy_parser_switch_to_buffer(b ); return b; } /** Setup the input buffer state to scan a string. The next call to yy_policy_parserlex() will * scan from a @e copy of @a str. * @param str a NUL-terminated string to scan * * @return the newly allocated buffer state object. * @note If you want to scan bytes that may contain NUL values, then use * yy_policy_parser_scan_bytes() instead. */ YY_BUFFER_STATE yy_policy_parser_scan_string (yyconst char * yystr ) { return yy_policy_parser_scan_bytes(yystr,strlen(yystr) ); } /** Setup the input buffer state to scan the given bytes. The next call to yy_policy_parserlex() will * scan from a @e copy of @a bytes. * @param bytes the byte buffer to scan * @param len the number of bytes in the buffer pointed to by @a bytes. * * @return the newly allocated buffer state object. */ YY_BUFFER_STATE yy_policy_parser_scan_bytes (yyconst char * yybytes, int _yybytes_len ) { YY_BUFFER_STATE b; char *buf; yy_size_t n; int i; /* Get memory for full buffer, including space for trailing EOB's. */ n = _yybytes_len + 2; buf = (char *) yy_policy_parseralloc(n ); if ( ! buf ) YY_FATAL_ERROR( "out of dynamic memory in yy_policy_parser_scan_bytes()" ); for ( i = 0; i < _yybytes_len; ++i ) buf[i] = yybytes[i]; buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; b = yy_policy_parser_scan_buffer(buf,n ); if ( ! b ) YY_FATAL_ERROR( "bad buffer in yy_policy_parser_scan_bytes()" ); /* It's okay to grow etc. this buffer, and we should throw it * away when we're done. */ b->yy_is_our_buffer = 1; return b; } #ifndef YY_EXIT_FAILURE #define YY_EXIT_FAILURE 2 #endif static void yy_fatal_error (yyconst char* msg ) { (void) fprintf( stderr, "%s\n", msg ); exit( YY_EXIT_FAILURE ); } /* Redefine yyless() so it works in section 3 code. */ #undef yyless #define yyless(n) \ do \ { \ /* Undo effects of setting up yy_policy_parsertext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ yy_policy_parsertext[yy_policy_parserleng] = (yy_hold_char); \ (yy_c_buf_p) = yy_policy_parsertext + yyless_macro_arg; \ (yy_hold_char) = *(yy_c_buf_p); \ *(yy_c_buf_p) = '\0'; \ yy_policy_parserleng = yyless_macro_arg; \ } \ while ( 0 ) /* Accessor methods (get/set functions) to struct members. */ /** Get the current line number. * */ int yy_policy_parserget_lineno (void) { return yy_policy_parserlineno; } /** Get the input stream. * */ FILE *yy_policy_parserget_in (void) { return yy_policy_parserin; } /** Get the output stream. * */ FILE *yy_policy_parserget_out (void) { return yy_policy_parserout; } /** Get the length of the current token. * */ int yy_policy_parserget_leng (void) { return yy_policy_parserleng; } /** Get the current token. * */ char *yy_policy_parserget_text (void) { return yy_policy_parsertext; } /** Set the current line number. * @param line_number * */ void yy_policy_parserset_lineno (int line_number ) { yy_policy_parserlineno = line_number; } /** Set the input stream. This does not discard the current * input buffer. * @param in_str A readable stream. * * @see yy_policy_parser_switch_to_buffer */ void yy_policy_parserset_in (FILE * in_str ) { yy_policy_parserin = in_str ; } void yy_policy_parserset_out (FILE * out_str ) { yy_policy_parserout = out_str ; } int yy_policy_parserget_debug (void) { return yy_policy_parser_flex_debug; } void yy_policy_parserset_debug (int bdebug ) { yy_policy_parser_flex_debug = bdebug ; } static int yy_init_globals (void) { /* Initialization is the same as for the non-reentrant scanner. * This function is called from yy_policy_parserlex_destroy(), so don't allocate here. */ (yy_buffer_stack) = 0; (yy_buffer_stack_top) = 0; (yy_buffer_stack_max) = 0; (yy_c_buf_p) = (char *) 0; (yy_init) = 0; (yy_start) = 0; /* Defined in main.c */ #ifdef YY_STDINIT yy_policy_parserin = stdin; yy_policy_parserout = stdout; #else yy_policy_parserin = (FILE *) 0; yy_policy_parserout = (FILE *) 0; #endif /* For future reference: Set errno on error, since we are called by * yy_policy_parserlex_init() */ return 0; } /* yy_policy_parserlex_destroy is for both reentrant and non-reentrant scanners. */ int yy_policy_parserlex_destroy (void) { /* Pop the buffer stack, destroying each element. */ while(YY_CURRENT_BUFFER){ yy_policy_parser_delete_buffer(YY_CURRENT_BUFFER ); YY_CURRENT_BUFFER_LVALUE = NULL; yy_policy_parserpop_buffer_state(); } /* Destroy the stack itself. */ yy_policy_parserfree((yy_buffer_stack) ); (yy_buffer_stack) = NULL; /* Reset the globals. This is important in a non-reentrant scanner so the next time * yy_policy_parserlex() is called, initialization will occur. */ yy_init_globals( ); return 0; } /* * Internal utility routines. */ #ifndef yytext_ptr static void yy_flex_strncpy (char* s1, yyconst char * s2, int n ) { register int i; for ( i = 0; i < n; ++i ) s1[i] = s2[i]; } #endif #ifdef YY_NEED_STRLEN static int yy_flex_strlen (yyconst char * s ) { register int n; for ( n = 0; s[n]; ++n ) ; return n; } #endif void *yy_policy_parseralloc (yy_size_t size ) { return (void *) malloc( size ); } void *yy_policy_parserrealloc (void * ptr, yy_size_t size ) { /* The cast to (char *) in the following accommodates both * implementations that use char* generic pointers, and those * that use void* generic pointers. It works with the latter * because both ANSI C and C++ allow castless assignment from * any pointer type to void*, and deal with argument conversions * as though doing an assignment. */ return (void *) realloc( (char *) ptr, size ); } void yy_policy_parserfree (void * ptr ) { free( (char *) ptr ); /* see yy_policy_parserrealloc() for (char *) cast */ } #define YYTABLES_NAME "yytables" #line 189 "policy.l" void yyerror(const char *m) { ostringstream oss; oss << "Error on line " << _parser_lineno << " near ("; for(int i = 0; i < yy_policy_parserleng; i++) oss << yy_policy_parsertext[i]; oss << "): " << m; _last_error = oss.str(); } // Everything is put in the lexer because of YY_BUFFER_STATE... int policy_parser::policy_parse(vector& outnodes, const Term::BLOCKS& block, const string& conf, string& outerr) { YY_BUFFER_STATE yybuffstate = yy_policy_parser_scan_string(conf.c_str()); _last_error = "No error"; _parser_nodes = &outnodes; _parser_lineno = 1; _block = block; int res = yyparse(); yy_policy_parser_delete_buffer(yybuffstate); outerr = _last_error; return res; } xorp/policy/policy_target.cc0000664000076400007640000001766311540224233016344 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "policy_module.h" #include "libxorp/xorp.h" #include "policy/common/policy_utils.hh" #include "policy/common/varrw.hh" #include "policy_target.hh" // static members string PolicyTarget::policy_target_name = "policy"; PolicyTarget::PolicyTarget(XrlStdRouter& rtr) : _running(true), _commit_delay(2000), _process_watch(rtr, _pmap), _conf(_process_watch), _filter_manager(_conf.import_filters(), _conf.sourcematch_filters(), _conf.export_filters(), _conf.sets(), _conf.tagmap(), rtr, _process_watch, _pmap) { _conf.set_filter_manager(_filter_manager); _process_watch.set_notifier(_filter_manager); } bool PolicyTarget::running() { return _running; } void PolicyTarget::shutdown() { _running = false; } void PolicyTarget::create_term(const string& policy, const ConfigNodeId& order, const string& term) { _conf.create_term(policy, order, term); } void PolicyTarget::delete_term(const string& policy, const string& term) { _conf.delete_term(policy,term); } void PolicyTarget::update_term_block(const string& policy, const string& term, const uint32_t& block, const ConfigNodeId& order, const string& statement) { _conf.update_term_block(policy, term, block, order, statement); } void PolicyTarget::create_policy(const string& policy) { _conf.create_policy(policy); } void PolicyTarget::delete_policy(const string& policy) { _conf.delete_policy(policy); } void PolicyTarget::create_set(const string& name) { _conf.create_set(name); } void PolicyTarget::update_set(const string& type, const string& name, const string& element) { _conf.update_set(type, name, element); } void PolicyTarget::delete_set(const string& name) { _conf.delete_set(name); } void PolicyTarget::add_to_set(const string& type, const string& name, const string& element) { _conf.add_to_set(type, name, element); } void PolicyTarget::delete_from_set(const string& type, const string& name, const string& element) { _conf.delete_from_set(type, name, element); } void PolicyTarget::update_import(const string& protocol, const string& policies, const string& mod) { POLICIES p; policy_utils::str_to_list(policies, p); _conf.update_imports(protocol, p, mod); // commit after a bit, as we may get conf changes... especially on "global // conf change" or at startup _conf.commit(_commit_delay); } void PolicyTarget::update_export(const string& protocol, const string& policies, const string& mod) { POLICIES p; policy_utils::str_to_list(policies, p); _conf.update_exports(protocol, p, mod); // try to aggregate commits by delaying them _conf.commit(_commit_delay); } void PolicyTarget::add_varmap(const string& protocol, const string& variable, const string& type, const string& access, const VarRW::Id& id) { _conf.add_varmap(protocol, variable, type, access, id); } void PolicyTarget::commit(uint32_t msec) { _conf.commit(msec); } string PolicyTarget::dump_state(uint32_t id) { return _conf.dump_state(id); } void PolicyTarget::birth(const string& tclass, const string& /* tinstance */) { _process_watch.birth(tclass); } void PolicyTarget::death(const string& tclass, const string& /* tinstance */) { // Remove the "import" and "export" dependencies for the protocol string protocol = _pmap.protocol(tclass); _conf.clear_imports(protocol); _conf.clear_exports(protocol); _process_watch.death(tclass); } void PolicyTarget::set_proto_target(const string& protocol, const string& target) { _pmap.set_xrl_target(protocol, target); } string PolicyTarget::test_policy(const string& args) { string policy; string prefix; string attributes; // We receive the following string: // policyname prefix [route attributes] // parse policy string::size_type i = args.find(' ', 0); if (i == string::npos) xorp_throw(PolicyException, "No policy specified"); policy = args.substr(0, i); // parse prefix i++; string::size_type j = args.find(' ', i); if (j == string::npos) prefix = args.substr(i); else { prefix = args.substr(i, j - i); j += 1; // strip quotes if present if (args.find('"') == j) { string tmp("\""); string::size_type k = args.find_last_of(tmp); if (j == k || k != (args.length() - 1)) xorp_throw(PolicyException, "Missing last quote"); j++; attributes = args.substr(j, k - j); } else attributes = args.substr(j); } string route; bool accepted = test_policy(policy, prefix, attributes, route); ostringstream oss; oss << "Policy decision: " << (accepted ? "accepted" : "rejected") << endl; if (!route.empty()) oss << "Route modifications:" << endl << route; return oss.str(); } bool PolicyTarget::test_policy(const string& policy, const string& prefix, const string& attributes, string& mods) { RATTR attrs; RATTR mod; // XXX lame IPv6 detection if (prefix.find(':') != string::npos) attrs["network6"] = prefix; else attrs["network4"] = prefix; parse_attributes(attributes, attrs); bool res = test_policy(policy, attrs, mod); for (RATTR::iterator i = mod.begin(); i != mod.end(); ++i) { mods += i->first; mods += "\t"; mods += i->second; mods += "\n"; } return res; } bool PolicyTarget::test_policy(const string& policy, const RATTR& attrs, RATTR& mods) { return _conf.test_policy(policy, attrs, mods); } void PolicyTarget::parse_attributes(const string& attr, RATTR& out) { // format: --attributename=value string::size_type i = 0; string::size_type j = 0; while ((j = attr.find("--", i)) != string::npos) { j += 2; // name i = attr.find('=', j); if (i == string::npos) xorp_throw(PolicyException, "Need a value in attribute list"); string name = attr.substr(j, i - j); // value string value; i++; j = attr.find(" --", i); if (j == string::npos) value = attr.substr(i); else value = attr.substr(i, j - i); out[name] = value; } } string PolicyTarget::cli_command(const string& cmd) { string command; string arg; string::size_type i = cmd.find(' '); if (i == string::npos) command = cmd; else { command = cmd.substr(0, i); arg = cmd.substr(i + 1); } if (command.compare("test") == 0) return test_policy(arg); else if (command.compare("show") == 0) return show(arg); else xorp_throw(PolicyException, "Unknown command"); } string PolicyTarget::show(const string& arg) { string type; string name; string::size_type i = arg.find(' '); if (i == string::npos) type = arg; else { type = arg.substr(0, i); name = arg.substr(i + 1); } RESOURCES res; show(type, name, res); ostringstream oss; for (RESOURCES::iterator i = res.begin(); i != res.end(); ++i) { if (name.empty()) oss << i->first << "\t"; oss << i->second << endl; } return oss.str(); } void PolicyTarget::show(const string& type, const string& name, RESOURCES& res) { _conf.show(type, name, res); } xorp/policy/xorp_policy.cc0000664000076400007640000000342011540224234016031 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "policy_module.h" #include "libxorp/xorp.h" #include "policy/common/policy_exception.hh" #include "policy/common/policy_utils.hh" #include "xrl_target.hh" void go() { setup_dflt_sighandlers(); EventLoop e; XrlStdRouter rtr(e,PolicyTarget::policy_target_name.c_str(), FinderConstants::FINDER_DEFAULT_HOST().str().c_str()); PolicyTarget policy_target(rtr); XrlPolicyTarget xrl_policy_target(&rtr,policy_target); while (xorp_do_run && !rtr.ready()) e.run(); while (xorp_do_run && policy_target.running()) e.run(); } int main(int /* argc */, char* argv[]) { xlog_init(argv[0], 0); xlog_set_verbose(XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); try { go(); } catch (const PolicyException& e) { XLOG_FATAL("PolicyException: %s",e.str().c_str()); } xlog_stop(); xlog_exit(); exit(0); } xorp/policy/source_match_code_generator.hh0000664000076400007640000000740511540225533021222 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/policy/source_match_code_generator.hh,v 1.14 2008/10/02 21:58:00 bms Exp $ #ifndef __POLICY_SOURCE_MATCH_CODE_GENERATOR_HH__ #define __POLICY_SOURCE_MATCH_CODE_GENERATOR_HH__ #include "policy/common/policy_exception.hh" #include "code_generator.hh" /** * @short Code generator for source match filters. * * This is a specialized version of the import filter CodeGenerator. * * It skips dest and action blocks in policies. * The action block is replaced with the actual policy tagging. */ class SourceMatchCodeGenerator : public NONCOPYABLE, public CodeGenerator { public: // bool == tag used // uint32_t actual tag typedef pair Taginfo; typedef vector Tags; /** * @short Exception thrown if no protocol was specified in source block. */ class NoProtoSpec : public PolicyException { public: NoProtoSpec(const char* file, size_t line, const string& init_why = "") : PolicyException("NoProtoSpec", file, line, init_why) {} }; /** * @short Exception thrown if protocol was re-defined in source block. */ class ProtoRedefined : public PolicyException { public: ProtoRedefined(const char* file, size_t line, const string& init_why = "") : PolicyException("ProtoRedefined", file, line, init_why) {} }; /** * @param tagstart the first policy tag available. * @param varmap the varmap. */ SourceMatchCodeGenerator(uint32_t tagstart, const VarMap& varmap, PolicyMap& pmap, map >& ptags); const Element* visit_policy(PolicyStatement& policy); const Element* visit_term(Term& term); const Element* visit_proto(NodeProto& node); /** * The targets of source match code may be multiple as different protocols * may refer to different source terms. Thus many different code fragments * may be generated. * * @return seturn all the code fragments generated. */ vector& codes(); /** * The source match code generator will map source blocks to tags. If a * source block is empty, a tag will not be used. * * @return information about tags used. */ const Tags& tags() const; /** * @return The next available policy tag. * */ uint32_t next_tag() const; protected: const string& protocol(); private: typedef map CodeMap; void do_term(Term& term); /** * Adds the the code of the current term being analyzed. */ void addTerm(); uint32_t _currtag; string _protocol; CodeMap _codes; // FIXME: who deletes these on exception ? vector _codes_vect; Tags _tags; map >& _protocol_tags; bool _protocol_statement; string _policy; }; #endif // __POLICY_SOURCE_MATCH_CODE_GENERATOR_HH__ xorp/policy/visitor_semantic.cc0000664000076400007640000001764711540224234017064 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #include "policy_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "policy/common/elem_null.hh" #include "visitor_semantic.hh" #include "policy_map.hh" VisitorSemantic::VisitorSemantic(SemanticVarRW& varrw, VarMap& varmap, SetMap& setmap, PolicyMap& pmap, const string& protocol, PolicyType ptype) : _varrw(varrw), _varmap(varmap), _setmap(setmap), _pmap(pmap), _protocol(protocol), _ptype(ptype) { } const Element* VisitorSemantic::visit(PolicyStatement& policy) { do_policy_statement(policy); // helps for garbage gollection in varrw _varrw.sync(); return NULL; } void VisitorSemantic::do_policy_statement(PolicyStatement& policy) { PolicyStatement::TermContainer& terms = policy.terms(); PolicyStatement::TermContainer::iterator i; _reject = false; // go through all terms for (i = terms.begin(); i != terms.end(); ++i) (i->second)->accept(*this); } const Element* VisitorSemantic::visit(Term& term) { Term::Nodes& source = term.source_nodes(); Term::Nodes& dest = term.dest_nodes(); Term::Nodes& actions = term.action_nodes(); Term::Nodes::iterator i; _current_protocol = ""; // assume import policy, so set protocol to whatever protocol instantiated // the policy change_protocol(_protocol); // go through the source block bool empty_source = true; debug_msg("[POLICY] source size: %u\n", XORP_UINT_CAST(source.size())); for (i = source.begin(); i != source.end(); ++i) { (i->second)->accept(*this); empty_source = false; } // if it was an export policy maybe varrw was switched to some other // protocol during source match, so replace it with original protocol. change_protocol(_protocol); // if it is an export policy, a source protocol must be specified [XXX: need // to fix this] if (_ptype == EXPORT && _current_protocol == "") { // Currently, allow empty source blocks... which means: // if something manages to get to the export filter, then match it. if (!empty_source) { string err = "No protocol specified in source match of export policy"; err += " in term: " + term.name(); xorp_throw(sem_error, err); } } // import policies should not have dest blocks if (_ptype == IMPORT && !(dest.empty())) { xorp_throw(sem_error, "Invalid use of dest in import policy in term " + term.name()); } // check dest block for (i = dest.begin(); i != dest.end(); ++i) { (i->second)->accept(*this); } // check actions for (i = actions.begin(); i != actions.end(); ++i) { (i->second)->accept(*this); } return NULL; } const Element* VisitorSemantic::visit(NodeUn& node) { // check argument const Element* arg = node.node().accept(*this); Element* res; // see if we may execute unary operation try { res = _disp.run(node.op(),*arg); if (res->refcount() == 1) _trash.insert(res); return res; } // we can't catch (const PolicyException& e) { ostringstream error; error << "Invalid unop " << e.str() << " at line " << node.line(); xorp_throw(sem_error, error.str()); } } const Element* VisitorSemantic::visit(NodeBin& node) { // check arguments const Element* left = node.left().accept(*this); const Element* right = node.right().accept(*this); return do_bin(*left, *right, node.op(), node); } const Element* VisitorSemantic::do_bin(const Element& left, const Element& right, const BinOper& op, const Node& node) { // see if we may execute bin operation. try { Element* res = _disp.run(op, left, right); if (res->refcount() == 1) _trash.insert(res); return res; } // nope catch (const PolicyException& e) { ostringstream error; error << "Invalid binop " << e.str() << " at line " << node.line(); xorp_throw(sem_error, error.str()); } } const Element* VisitorSemantic::visit(NodeAssign& node) { // check argument const Element* rvalue = node.rvalue().accept(*this); // try assignment try { VarRW::Id id = _varmap.var2id(semantic_protocol(), node.varid()); // see if there's a modifier to the assignment if (node.mod()) { const Element* left = &_varrw.read(id); rvalue = do_bin(*left, *rvalue, *node.mod(), node); } _varrw.write(id, *rvalue); } catch (SemanticVarRW::var_error e) { ostringstream error; error << e.str() << " at line " << node.line(); xorp_throw(sem_error, error.str()); } return NULL; } const Element* VisitorSemantic::visit(NodeVar& node) { // try reading a variable try { VarRW::Id id = _varmap.var2id(semantic_protocol(), node.val()); return &_varrw.read(id); } catch(SemanticVarRW::var_error e) { ostringstream error; error << e.str() << " at line " << node.line(); xorp_throw(sem_error, error.str()); } } const Element* VisitorSemantic::visit(NodeSet& node) { // try getting a set [setdep should have caught there errors] try { const Element& e = _setmap.getSet(node.setid()); _sets.insert(node.setid()); return &e; } catch(const PolicyException& e) { ostringstream error; error << "Set not found: " << node.setid() << " at line " << node.line(); xorp_throw(sem_error, error.str()); } } const Element* VisitorSemantic::visit(NodeElem& node) { return &node.val(); } const Element* VisitorSemantic::visit(NodeAccept& /* node */) { return NULL; } const Element* VisitorSemantic::visit(NodeReject& /* node */) { _reject = true; return NULL; } const Element* VisitorSemantic::visit(NodeProto& node) { ostringstream err; // import policies may not use protocol directive if(_ptype == IMPORT) { err << "May not define protocol for import policy at line " << node.line(); xorp_throw(sem_error, err.str()); } string proto = node.proto(); // check for redifinition in same term. if(_current_protocol != "") { err << "Redifinition of protocol from " << _current_protocol << " to " << proto << " at line " << node.line(); xorp_throw(sem_error, err.str()); } // do the switch _current_protocol = proto; // make the varrw emulate the new protocol change_protocol(_current_protocol); return NULL; } void VisitorSemantic::change_protocol(const string& proto) { _semantic_protocol = proto; _varrw.set_protocol(_semantic_protocol); } const string& VisitorSemantic::semantic_protocol() { return _semantic_protocol; } const Element* VisitorSemantic::visit(NodeNext& /* node */) { return NULL; } const Element* VisitorSemantic::visit(NodeSubr& node) { // XXX check for recursion. PolicyStatement& policy = _pmap.find(node.policy()); string proto = _protocol; bool reject = _reject; do_policy_statement(policy); Element* e = new ElemBool(!_reject); _trash.insert(e); change_protocol(proto); _reject = reject; return e; } xorp/policy/dependency.hh0000664000076400007640000001451611540225532015624 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/policy/dependency.hh,v 1.4 2008/10/02 21:57:58 bms Exp $ #ifndef __POLICY_DEPENDENCY_HH__ #define __POLICY_DEPENDENCY_HH__ #include "policy/common/policy_exception.hh" /** * @short A class which relates objects and handles dependencies between them. * * This class is a container of objects [pointers]. It relates string object * names to the actual objects. Also, it has the ability to add and remove * dependencies to that objects. A dependency is some entity which is using a * specific object in the Dependency container. This entity is string * represented. * * For example, if a policy x uses set y, Set y will have x added in its * dependencies. This means that x depends on y. * * Having a consistent dependency list allows objects to be deleted correctly. */ template class Dependency : public NONCOPYABLE { public: // things that depend on object typedef list DependencyList; // object dependency pair. typedef pair Pair; // object of this name has these dependencies typedef map Map; typedef set KEYS; struct ObjPair { const string& name; const T& object; ObjPair(const string& n, const T& o) : name(n), object(o) {} }; /** * @short Exception thrown if an illegal action is requested. * * Such as deleting an object which has a non empty dependency list. */ class DependencyError : public PolicyException { public: DependencyError(const char* file, size_t line, const string& init_why = "") : PolicyException("DependencyError", file, line, init_why) {} }; Dependency(); ~Dependency(); void clear(); /** * Checks if an object is present in the container. * * @return true if object is contained. False otherwise. * @param objectname name of the object. */ bool exists(const string& objectname) const; /** * Attempts to create an object. If creation is successfull, the object * ownership is transfered to this container. The caller should not modify / * delete the object any more. * * If object exists, creation fails. * * @return true if creation was successful. False otherwise. * @param objectname name of the object. * @param object the actual object. */ bool create(const string& objectname, T* object); /** * Tries to remove and delete an object. Checks if object is in use [non * empty dependency list]. * * Throws an exception on failure. * * @param objectname object to remove and delete. */ void remove(const string& objectname); /** * Adds dependencies to this object. A dependency is another object which * uses this object. * * Throws an exception if object does not exist. * * @param objectname name of object to which dependency should be added. * @param dep name of object which depends on objectname. */ void add_dependency(const string& objectname, const string& dep); /** * Deletes a dependency on an object. * * Throws an exception if object does not exist. * * @param objectname name of object to which dependency should be removed. * @param dep name of dependency to remove. */ void del_dependency(const string& objectname, const string& dep); /** * Returns the object being searched for. * * @param objectname name of object to return. * @return object requested. */ T& find(const string& objectname) const; /** * Returns a pointer the object being searched for. * * @param objectname name of object to return. * @return a pointer to the object requested if found, otherwise NULL. */ T* find_ptr(const string& objectname) const; /** * Obtains the dependency list for an object. * * Duplicates are removed, as it is a set. * * @param objectname name of object for which dependency list is requested. * @param deps set of strings filled with dependency list. */ void get_deps(const string& objectname, set& deps) const; /** * Replaces an object. The previous one is deleted. * Caller does not own object. Should not modify or delete it. * * Throws an exception if object does not exist. * * @param objectname name of object to replace. * @param obj the new object. */ void update_object(const string& objectname,T* obj); // XXX: this interface has to be re-done... /** * Obtain an iterator for this container. * * @return iterator for Dependency container. */ typename Map::const_iterator get_iterator() const; /** * Checks if more objects are available with this iterator. * * @return true if more objects are available. False otherwise. * @param i iterator to use. */ bool has_next(const typename Map::const_iterator& i) const; /** * Returns the next object pair and increments the iterator. * * An object pair consists of the object name, and the actual object. * * @return the object pair associated with the iterator. * @param i iterator that points to object. Iterator is then incremented. */ ObjPair next(typename Map::const_iterator& i) const; void keys(KEYS& out) const; private: Map _map; Pair* findDepend(const string& objectname) const; }; #endif // __POLICY_DEPENDENCY_HH__ xorp/policy/policy_statement.hh0000664000076400007640000001110011540225532017053 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/policy/policy_statement.hh,v 1.16 2008/10/02 21:57:59 bms Exp $ #ifndef __POLICY_POLICY_STATEMENT_HH__ #define __POLICY_POLICY_STATEMENT_HH__ #include "libproto/config_node_id.hh" #include "policy/common/policy_exception.hh" #include "set_map.hh" #include "term.hh" class PolicyMap; typedef set DEPS; /** * @short A policy statement is a collection of terms. */ class PolicyStatement : public NONCOPYABLE { public: /** * @short Exception thrown on error such as when no term is found. */ class PolicyStatementErr : public PolicyException { public: PolicyStatementErr(const char* file, size_t line, const string& init_why = "") : PolicyException("PolicyStatementErr", file, line, init_why) {} }; typedef ConfigNodeIdMap TermContainer; /** * @param name the name of the policy. * @param smap the SetMap. Used for dependency tracking. * @param pmap the PolicyMap. Used for dependency tracking. */ PolicyStatement(const string& name, SetMap& smap, PolicyMap& pmap); ~PolicyStatement(); /** * Append a term at the end of the policy. * * Caller must not delete / modify pointer. * * @param order node ID with position of term. * @param term term to append to policy. */ void add_term(const ConfigNodeId& order, Term* term); /** * Throws exception if no term is found. * * @return term requested. * @param name name of term to find. */ Term& find_term(const string& name) const; /** * Checks if a term already exists. * * @return true if term exists, false otherwise. * @param name term name. */ bool term_exists(const string& name) const; /** * Attempts to delete a term. * * @return true on successful delete, false otherwise. * @param name name of term to delete. */ bool delete_term(const string& name); /** * Perform operations at the end of the policy. */ void set_policy_end(); /** * @return name of policy. */ const string& name() const; /** * Visitor implementation. * * @param v visitor to visit policy. */ bool accept(Visitor& v); /** * @return terms of this policy */ TermContainer& terms(); /** * Replace the set dependencies. * * @param sets the new sets this policy is dependent on. */ void set_dependency(const DEPS& sets, const DEPS& policies); private: typedef list > OOL; /** * Delete all set dependencies of this policy. */ void del_dependencies(); /** * Get the iterator for a specific term. * * @return iterator for term. * @param name name of the term. */ TermContainer::iterator get_term_iter(const string& name); TermContainer::const_iterator get_term_iter(const string& name) const; /** * Get the iterator for a term that is out of order. * * @param order the order for the term. * @return iterator for term. */ OOL::iterator find_out_of_order_term(const ConfigNodeId& order); /** * Get the iterator for a term that is out of order. * * @param name the name for the term. * @return iterator for term. */ OOL::iterator find_out_of_order_term(const string& name); OOL::const_iterator find_out_of_order_term(const string& name) const; string _name; TermContainer _terms; list > _out_of_order_terms; DEPS _sets; DEPS _policies; SetMap& _smap; PolicyMap& _pmap; }; #endif // __POLICY_POLICY_STATEMENT_HH__ xorp/policy/code_list.hh0000664000076400007640000000545011540225532015450 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/policy/code_list.hh,v 1.11 2008/10/02 21:57:57 bms Exp $ #ifndef __POLICY_CODE_LIST_HH__ #define __POLICY_CODE_LIST_HH__ #include "code.hh" /** * @short A collection of code fragments. */ class CodeList : public NONCOPYABLE { public: /** * Initialize codelist. * * @param policy the policy name. */ CodeList(const string& policy); ~CodeList(); /** * Append code to the list. * Code is now owned by the code list. * * @param c code to append. Caller must not delete code. */ void push_back(Code* c); /** * Obtain string representation of the code list. * * @return string representation of the code list. */ string str() const; /** * Links all code in the code list to c. * The code is basically added to c. * * @param c code to link current code list to. */ void link_code(Code& c) const; /** * Obtain the set of targets the code list has. * * @param targets argument is filled with targets the code list has. */ void get_targets(Code::TargetSet& targets) const; /** * Obtain the set of targets of particular filter type the code list has. * * @param targets argument is filled with targets the code list has. * @param filter the filter type. */ void get_targets(Code::TargetSet& targets, const filter::Filter& filter) const; /** * Return the tags used by a certain protocol for route redistribution, * in the code list. * * @param protocol protocol caller wants tags of. * @param tagset filled with policytags used by protocol for route * redistribution. */ void get_redist_tags(const string& protocol, Code::TagSet& tagset) const; private: string _policy; // The policy name typedef list ListCode; ListCode _codes; }; #endif // __POLICY_CODE_LIST_HH__ xorp/policy/parser.hh0000664000076400007640000000367411540224233015002 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/policy/parser.hh,v 1.8 2008/10/02 21:57:59 bms Exp $ #ifndef __POLICY_PARSER_HH__ #define __POLICY_PARSER_HH__ #include "node_base.hh" #include "term.hh" /** * @short A lex/yacc wrapper which parses a configuration and returns nodes. * * This class parses a raw user configuration and returns a vector of nodes. * * Each node will normally relate to a single statement. The vector of nodes * reflects all the statements present. */ class Parser { public: typedef vector Nodes; /** * @param block the term block which is being parsed [action/src/dest]. * @param text Configuration to parse. * @return the parse-tree of the configuration. Null on error. */ Nodes* parse(const Term::BLOCKS& block, const string& text); /** * This should be called if parse returns null. * * If parse is successful, the value of last_error is undefined. * * @return the last error of the parse. */ string last_error(); private: string _last_error; }; #endif // __POLICY_PARSER_HH__ xorp/policy/semantic_varrw.hh0000664000076400007640000000602311540225533016525 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/policy/semantic_varrw.hh,v 1.11 2008/10/02 21:58:00 bms Exp $ #ifndef __POLICY_SEMANTIC_VARRW_HH__ #define __POLICY_SEMANTIC_VARRW_HH__ #include "policy/common/varrw.hh" #include "policy/common/element_base.hh" #include "policy/common/element_factory.hh" #include "policy/common/policy_exception.hh" #include "var_map.hh" /** * @short A VarRW used for semantic checking. * * This VarRW checks if elements may be read/written to and does typechecking * according to the VarMap. * * The user may set which protocols should be "simulated" by the VarRW. * * The SemanticVarRW will create dummy elements which are initialized to a * default value. This may not be optimal for semantic checking. */ class SemanticVarRW : public NONCOPYABLE, public VarRW { public: /** * @short Exception thrown on illegal variable use. */ class var_error : public PolicyException { public: var_error(const char* file, size_t line, const string& init_why = "") : PolicyException("var_error", file, line, init_why) {} }; /** * @param vars the VarMap to use. */ SemanticVarRW(VarMap& vars); ~SemanticVarRW(); /** * VarRW read interface. * * Checks if a variable may be read. * * Throws exception on error. * * @return dummy element initialized to a default value. * @param id name of variable. */ const Element& read(const Id& id); /** * VarRW write interface. * * Checks if a variable may be written to, and if the type is correct. * * Throws exception on error. * * @param id name of variable. * @param elem value of variable. */ void write(const Id& id, const Element& elem); /** * VarRW sync interface. * * Does garbage collection. */ void sync(); /** * Change the protocol being simulated. * * @param proto protocol to simulate. */ void set_protocol(const string& proto); private: string _protocol; VarMap& _vars; ElementFactory _ef; set _trash; }; #endif // __POLICY_SEMANTIC_VARRW_HH__ xorp/policy/visitor_test.cc0000664000076400007640000001624211421137511016225 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "policy_module.h" #include "libxorp/xorp.h" #include "visitor_test.hh" #include "test_varrw.hh" // XXX duplication with semantic visitor. Factor out common functionality and // inherit. VisitorTest::VisitorTest(SetMap& sm, PolicyMap& pm, VarMap& vm, const RATTR& attr, RATTR& mod) : _sm(sm), _pm(pm), _vm(vm), _finished(false), _varrw(NULL), _mod(mod) { TestVarRW* varrw = new TestVarRW(); _varrw = varrw; RATTR::const_iterator i = attr.find("protocol"); if (i != attr.end()) _protocol = i->second; change_protocol(_protocol); // init varrw with route attributes for (i = attr.begin(); i != attr.end(); ++i) { string name = i->first; if (name.compare("protocol") == 0) continue; const VarMap::Variable& v = var2variable(name); Element* e = _ef.create(v.type, (i->second).c_str()); trash_add(e); varrw->write(v.id, *e); } } VisitorTest::~VisitorTest() { delete _varrw; for (TRASH::iterator i = _trash.begin(); i != _trash.end(); ++i) delete (*i); _trash.clear(); } const Element* VisitorTest::visit(PolicyStatement& ps) { do_policy_statement(ps); return NULL; } const Element* VisitorTest::do_policy_statement(PolicyStatement& ps) { PolicyStatement::TermContainer& terms = ps.terms(); _outcome = DEFAULT; // go throgh all terms for (PolicyStatement::TermContainer::iterator i = terms.begin(); i != terms.end(); ++i) { (i->second)->accept(*this); if (_outcome != DEFAULT) break; if (_finished) { switch (_flow) { case NodeNext::POLICY: return NULL; case NodeNext::TERM: continue; } } } return NULL; } const Element* VisitorTest::visit(Term& term) { Term::Nodes& source = term.source_nodes(); Term::Nodes& dest = term.dest_nodes(); Term::Nodes& actions = term.action_nodes(); Term::Nodes::iterator i; _finished = false; _flow = NodeNext::TERM; change_protocol(_protocol); // do source block for (i = source.begin(); i != source.end(); ++i) { const Element* e = (i->second)->accept(*this); if (_finished) return NULL; if (!match(e)) return NULL; } change_protocol(_protocol); // do dest block for (i = dest.begin(); i != dest.end(); ++i) { const Element* e = (i->second)->accept(*this); if (_finished) return NULL; if (!match(e)) return NULL; } // do action block for (i = actions.begin(); i != actions.end(); ++i) { (i->second)->accept(*this); if (_finished) return NULL; } return NULL; } const Element* VisitorTest::visit(NodeUn& node) { const Element* arg = node.node().accept(*this); Element* res = _disp.run(node.op(), *arg); trash_add(res); return res; } const Element* VisitorTest::visit(NodeBin& node) { const Element* left = node.left().accept(*this); const Element* right = node.right().accept(*this); return do_bin(*left, *right, node.op()); } const Element* VisitorTest::do_bin(const Element& left, const Element& right, const BinOper& op) { Element* res = _disp.run(op, left, right); trash_add(res); return res; } const Element* VisitorTest::visit(NodeAssign& node) { const Element* rvalue = node.rvalue().accept(*this); if (node.mod()) { const Element& left = read(node.varid()); rvalue = do_bin(left, *rvalue, *node.mod()); } write(node.varid(), *rvalue); return NULL; } const Element* VisitorTest::visit(NodeVar& node) { const Element& e = read(node.val()); return &e; } const Element* VisitorTest::visit(NodeSet& node) { const Element& e = _sm.getSet(node.setid()); return &e; } const Element* VisitorTest::visit(NodeElem& node) { const Element& e = node.val(); return &e; } const Element* VisitorTest::visit(NodeAccept& /* node */) { _outcome = ACCEPT; _finished = true; return NULL; } const Element* VisitorTest::visit(NodeReject& /*node */) { _outcome = REJECT; _finished = true; return NULL; } const Element* VisitorTest::visit(NodeProto& node) { change_protocol(node.proto()); return NULL; } const Element* VisitorTest::visit(NodeNext& node) { _flow = node.flow(); _finished = true; return NULL; } const Element* VisitorTest::visit(NodeSubr& node) { PolicyStatement& policy = _pm.find(node.policy()); bool finished = _finished; Outcome outcome = _outcome; Flow flow = _flow; do_policy_statement(policy); Element* e = new ElemBool(_outcome == REJECT ? false : true); _finished = finished; _outcome = outcome; _flow = flow; return e; } void VisitorTest::trash_add(Element* e) { if (e->refcount() == 1) _trash.insert(e); } bool VisitorTest::accepted() { return _outcome != REJECT; } void VisitorTest::change_protocol(const string& protocol) { _current_protocol = protocol; } const Element& VisitorTest::read(const string& id) { try { Id i = var2id(id); const Element& e = _varrw->read(i); return e; } catch (const PolicyException& e) { ostringstream oss; oss << "Can't read uninitialized attribute " << id; xorp_throw(PolicyException, oss.str()); } } void VisitorTest::write(const string& id, const Element& e) { const Variable& v = var2variable(id); // XXX perhaps we should do a semantic check before a test run... if (!v.writable()) xorp_throw(PolicyException, "writing a read-only variable"); if (v.type != e.type()) xorp_throw(PolicyException, "type mismatch on write"); _varrw->write(v.id, e); _mod[id] = e.str(); } VisitorTest::Id VisitorTest::var2id(const string& var) { const Variable& v = var2variable(var); return v.id; } const VisitorTest::Variable& VisitorTest::var2variable(const string& var) { string protocol = _current_protocol; // Always allow reading prefix. // XXX we could code this better... if (protocol.empty()) { if (var.compare("network4") == 0 || var.compare("network6") == 0) protocol = "bgp"; } if (protocol.empty()) xorp_throw(PolicyException, "Provide a protocol name"); Id id = _vm.var2id(protocol, var); return _vm.variable(protocol, id); } bool VisitorTest::match(const Element* e) { if (!e) return true; const ElemBool* b = dynamic_cast(e); XLOG_ASSERT(b); return b->val(); } xorp/policy/policy_list.cc0000664000076400007640000002503411540225532016023 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "policy_module.h" #include "libxorp/xorp.h" #include "policy_list.hh" #include "visitor_semantic.hh" #include "export_code_generator.hh" #include "source_match_code_generator.hh" #include "visitor_dep.hh" uint32_t PolicyList::_pe = 0; PolicyList::PolicyList(const string& p, PolicyType pt, PolicyMap& pmap, SetMap& smap, VarMap& vmap, string mod) : _protocol(p), _type(pt), _pmap(pmap), _smap(smap), _varmap(vmap), _mod(mod), _mod_term(NULL), _mod_term_import(NULL), _mod_term_export(NULL) { if (!_mod.empty()) { _mod_term_import = create_mod(Term::SOURCE); _mod_term_export = create_mod(Term::DEST); } } Term* PolicyList::create_mod(Term::BLOCKS block) { // We add the modifier term at the beginning of each policy. If it matches, // we continue executing the policy, else we go to the next one. Term* t = new Term("__mod"); // XXX leak if exception is thrown below ConfigNodeId nid(0, 0); string statement = "not " + _mod; t->set_block(block, nid, statement); t->set_block_end(block); statement = "next policy;"; t->set_block(Term::ACTION, nid, statement); t->set_block_end(Term::ACTION); return t; } PolicyList::~PolicyList() { for (PolicyCodeList::iterator i = _policies.begin(); i != _policies.end(); ++i) { PolicyCode& pc = *i; _pmap.del_dependency(pc.first,_protocol); delete (*i).second; } for (POLICIES::iterator i = _pe_policies.begin(); i != _pe_policies.end(); ++i) _pmap.delete_policy(*i); delete _mod_term_import; delete _mod_term_export; } void PolicyList::push_back(const string& policyname) { if (!policyname.empty() && policyname.at(0) == '(') { add_policy_expression(policyname); return; } _policies.push_back(PolicyCode(policyname, NULL)); _pmap.add_dependency(policyname, _protocol); } void PolicyList::add_policy_expression(const string& exp) { // We create an internal policy based on the expression, and execute that // policy. ostringstream oss; oss << "PE_" << _pe++; string name = oss.str(); _pmap.create(name, _smap); _pe_policies.insert(name); PolicyStatement& ps = _pmap.find(name); // replace "string" into "policy string". That is, execute policies as // subroutines. oss.str(""); int state = 0; for (string::const_iterator i = exp.begin(); i != exp.end(); ++i) { char x = *i; if (isalnum(x)) { if (state == 0) { oss << "policy "; state = 1; } } else state = 0; oss << x; } string conf = oss.str(); ConfigNodeId order(1, 0); // XXX how should this function with export policies? Term* t = new Term("match"); t->set_block(_type == IMPORT ? Term::SOURCE : Term::DEST, order, conf); t->set_block(Term::ACTION, order, "accept;"); ps.add_term(order, t); // XXX handle next-policy too - how should it work? t = new Term("nomatch"); t->set_block(Term::ACTION, order, "reject;"); ps.add_term(ConfigNodeId(2, 1), t); ps.set_policy_end(); // update dependencies. // XXX we shouldn't be doing this here. We should have an encapsulated // mechanism for adding "internal" policies and dealing with them correctly. // It seems like a useful feature. VisitorDep dep(_smap, _pmap); ps.accept(dep); push_back(name); } void PolicyList::compile_policy(PolicyStatement& ps,Code::TargetSet& mod, uint32_t& tagstart, map >& ptags) { // go throw all the policies present in this list for(PolicyCodeList::iterator i = _policies.begin(); i != _policies.end(); ++i) { // if the policy is present, then compile it if(ps.name() == (*i).first) { switch(_type) { case IMPORT: compile_import(i,ps,mod); break; case EXPORT: compile_export(i,ps,mod,tagstart, ptags); break; } } } } void PolicyList::compile(Code::TargetSet& mod, uint32_t& tagstart, map >& ptags) { // go throw all policies in the list for (PolicyCodeList::iterator i = _policies.begin(); i != _policies.end(); ++i) { PolicyCode& pc = *i; // deal only with non compiled policies [i.e. policies without // associated code]. if (pc.second) continue; // find the policy statement and compile it. PolicyStatement& ps = _pmap.find(pc.first); switch(_type) { case IMPORT: compile_import(i, ps, mod); break; case EXPORT: compile_export(i, ps, mod, tagstart, ptags); break; } } } string PolicyList::str() { string ret = "Policy Type: "; switch(_type) { case IMPORT: ret += "import"; break; case EXPORT: ret += "export"; break; } ret += "\n"; ret += "Protocol: " + _protocol + "\n"; for(PolicyCodeList::iterator i = _policies.begin(); i != _policies.end(); ++i) { ret += "PolicyName: " + (*i).first + "\n"; ret += "Code:\n"; CodeList* cl = (*i).second; if(cl) ret += cl->str(); else ret += "NOT COMPILED\n"; } return ret; } void PolicyList::link_code(Code& ret) { // go through all the policies, and link the code for (PolicyCodeList::iterator i = _policies.begin(); i != _policies.end(); ++i) { CodeList* cl = (*i).second; // because of target set in ret, only relevant code will be linked. cl->link_code(ret); } } void PolicyList::get_targets(Code::TargetSet& targets) { // go through all the policies in the list, and return the targets affected // by the code. for(PolicyCodeList::iterator i = _policies.begin(); i != _policies.end(); ++i) { CodeList* cl = (*i).second; // get all the targets in this code list [a single policy may have more // than one targets -- such as source match filters]. cl->get_targets(targets); } } void PolicyList::get_redist_tags(const string& protocol, Code::TagSet& ts) { // go through all policies and return tags associated with the requested // protocol. for(PolicyCodeList::iterator i = _policies.begin(); i != _policies.end(); ++i) { CodeList* cl = (*i).second; cl->get_redist_tags(protocol,ts); } } void PolicyList::semantic_check(PolicyStatement& ps, VisitorSemantic::PolicyType type) { // check if policy makes sense with this instantiation // [i.e. protocol and import/export pair]. SemanticVarRW varrw(_varmap); VisitorSemantic sem_check(varrw, _varmap, _smap, _pmap, _protocol, type); // exception will be thrown if all goes wrong. // check modifier [a bit of a hack] if (_mod_term) _mod_term->accept(sem_check); ps.accept(sem_check); } void PolicyList::compile_import(PolicyCodeList::iterator& iter, PolicyStatement& ps, Code::TargetSet& modified_targets) { _mod_term = _mod_term_import; // check the policy semantic_check(ps, VisitorSemantic::IMPORT); // generate the code CodeGenerator cg(_protocol, _varmap, _pmap); // check modifier [a bit of a hack] if (_mod_term) _mod_term->accept(cg); ps.accept(cg); // make a copy of the code Code* code = new Code(cg.code()); // in this case, since it is import, there is only onle code fragment in the // list. CodeList* cl = new CodeList(ps.name()); cl->push_back(code); // if any code was previously stored, delete it if((*iter).second) { delete (*iter).second; } // replace code (*iter).second = cl; // target was modified modified_targets.insert(code->target()); } void PolicyList::compile_export(PolicyCodeList::iterator& iter, PolicyStatement& ps, Code::TargetSet& modified_targets, uint32_t& tagstart, map >& ptags) { _mod_term = _mod_term_export; // make sure policy makes sense semantic_check(ps, VisitorSemantic::EXPORT); // generate source match code SourceMatchCodeGenerator smcg(tagstart, _varmap, _pmap, ptags); // check modifier [a bit of a hack] if (_mod_term) _mod_term->accept(smcg); ps.accept(smcg); // generate Export code ExportCodeGenerator ecg(_protocol, smcg.tags(), _varmap, _pmap); // check modifier [a bit of a hack] if (_mod_term) _mod_term->accept(ecg); ps.accept(ecg); // update the global tag start tagstart = smcg.next_tag(); // get the export code and add it to the new codelist. Code* code = new Code(ecg.code()); CodeList* cl = new CodeList(ps.name()); cl->push_back(code); // // If we had a codelist, then add the set of EXPORT_SOURCEMATCH // targets to the set of modified targets. // // This is needed to cover the case when we delete all policy terms // that export from a particular protocol. // if ((*iter).second) { CodeList* old_cl = (*iter).second; Code::TargetSet old_targets; old_cl->get_targets(old_targets, filter::EXPORT_SOURCEMATCH); Code::TargetSet::iterator i; for (i = old_targets.begin(); i != old_targets.end(); ++i) { modified_targets.insert(*i); } } // if we had a codelist get rid of it if ((*iter).second) { delete (*iter).second; } // store new code list (*iter).second = cl; // export target modified modified_targets.insert(code->target()); // we may get a lot of code fragments here: // consider a policy where each term has a different source protocol... vector& codes = smcg.codes(); // add the fragments to the code list for (vector::iterator i = codes.begin(); i != codes.end(); ++i) { Code* c = *i; cl->push_back(c); modified_targets.insert(c->target()); // keep track of source protocols in export policy code. code->add_source_protocol(c->target().protocol()); } } xorp/policy/xrl_target.hh0000664000076400007640000001155511540225533015662 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/policy/xrl_target.hh,v 1.16 2008/10/02 21:58:02 bms Exp $ #ifndef __POLICY_XRL_TARGET_HH__ #define __POLICY_XRL_TARGET_HH__ #include "libxipc/xrl_std_router.hh" #include "xrl/targets/policy_base.hh" #include "policy_target.hh" /** * @short The XORP Xrl target. * * This class simply forwards calls to the PolicyTarget. */ class XrlPolicyTarget : public XrlPolicyTargetBase { public: /** * @param r XrlRouter to use. * @param ptarget the main PolicyTarget. */ XrlPolicyTarget(XrlStdRouter* r, PolicyTarget& ptarget); XrlCmdError common_0_1_get_target_name( // Output values, string& name); XrlCmdError common_0_1_get_version( // Output values, string& version); XrlCmdError common_0_1_get_status( // Output values, uint32_t& status, string& reason); XrlCmdError common_0_1_shutdown(); virtual XrlCmdError common_0_1_startup() { return XrlCmdError::OKAY(); } XrlCmdError policy_0_1_create_term( // Input values, const string& policy, const string& order, const string& term); XrlCmdError policy_0_1_delete_term( // Input values, const string& policy, const string& term); XrlCmdError policy_0_1_update_term_block( // Input values, const string& policy, const string& term, const uint32_t& block, const string& order, const string& statement); XrlCmdError policy_0_1_create_policy( // Input values, const string& policy); XrlCmdError policy_0_1_delete_policy( // Input values, const string& policy); XrlCmdError policy_0_1_create_set( // Input values, const string& set); XrlCmdError policy_0_1_update_set( // Input values, const string& type, const string& set, const string& elements); XrlCmdError policy_0_1_delete_set( // Input values, const string& set); XrlCmdError policy_0_1_add_to_set( // Input values, const string& type, const string& set, const string& element); XrlCmdError policy_0_1_delete_from_set( // Input values, const string& type, const string& set, const string& element); XrlCmdError policy_0_1_done_global_policy_conf(); XrlCmdError policy_0_1_import( // Input values, const string& protocol, const string& policies, const string& modifier); XrlCmdError policy_0_1_export( // Input values, const string& protocol, const string& policies, const string& modifier); XrlCmdError policy_0_1_add_varmap( // Input values, const string& protocol, const string& variable, const string& type, const string& access, const uint32_t& id); XrlCmdError policy_0_1_dump_state( // Input values, const uint32_t& id, // Output values, string& state); XrlCmdError policy_0_1_set_proto_target( // Input values, const string& protocol, const string& target); XrlCmdError finder_event_observer_0_1_xrl_target_birth( // Input values, const string& target_class, const string& target_instance); XrlCmdError finder_event_observer_0_1_xrl_target_death( // Input values, const string& target_class, const string& target_instance); XrlCmdError cli_processor_0_1_process_command( // Input values, const string& processor_name, const string& cli_term_name, const uint32_t& cli_session_id, const string& command_name, const string& command_args, // Output values, string& ret_processor_name, string& ret_cli_term_name, uint32_t& ret_cli_session_id, string& ret_command_output); private: PolicyTarget& _policy_target; }; #endif // __POLICY_XRL_TARGET_HH__ xorp/policy/visitor_printer.hh0000664000076400007640000000377311421137511016750 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/policy/visitor_printer.hh,v 1.9 2008/10/02 21:58:01 bms Exp $ #ifndef __POLICY_VISITOR_PRINTER_HH__ #define __POLICY_VISITOR_PRINTER_HH__ #include "visitor.hh" #include "policy_statement.hh" #include "node.hh" /** * @short This visitor will produce a human readable text stream from a policy. * * Useful for debugging and checking what the policy manager thinks polcies look * like. */ class VisitorPrinter : public Visitor { public: /** * @param out stream which receives the text representation of policy. */ VisitorPrinter(ostream& out); const Element* visit(NodeUn&); const Element* visit(NodeBin&); const Element* visit(NodeVar&); const Element* visit(NodeAssign&); const Element* visit(NodeSet&); const Element* visit(NodeAccept&); const Element* visit(NodeReject&); const Element* visit(Term&); const Element* visit(PolicyStatement&); const Element* visit(NodeElem&); const Element* visit(NodeProto&); const Element* visit(NodeNext&); const Element* visit(NodeSubr& node); private: ostream& _out; }; #endif // __POLICY_VISITOR_PRINTER_HH__ xorp/policy/test_varrw.hh0000664000076400007640000000245611540224233015703 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/policy/test_varrw.hh,v 1.3 2008/10/02 21:58:01 bms Exp $ #ifndef __POLICY_TEST_VARRW_HH__ #define __POLICY_TEST_VARRW_HH__ #include "policy/common/varrw.hh" class TestVarRW : public VarRW { public: const Element& read(const Id& id); void write(const Id& id, const Element& elem); private: typedef map ELEM; ELEM _elem; }; #endif // __POLICY_TEST_VARRW_HH__ xorp/policy/code_list.cc0000664000076400007640000000547611421137511015443 0ustar greearbgreearb// vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "policy_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "code_list.hh" #include "policy/common/policy_utils.hh" CodeList::CodeList(const string& policy) : _policy(policy) { } CodeList::~CodeList() { policy_utils::clear_container(_codes); } void CodeList::push_back(Code* c) { _codes.push_back(c); } string CodeList::str() const { string ret = "Policy: " + _policy + "\n"; for (ListCode::const_iterator i = _codes.begin(); i != _codes.end(); ++i) { ret += (*i)->str(); } return ret; } void CodeList::link_code(Code& c) const { // go through all the code we have, and link it to c. for (ListCode::const_iterator i = _codes.begin(); i != _codes.end(); ++i) { const Code* tmp = *i; // the += operator will check for target correctness. c += *tmp; } } void CodeList::get_targets(Code::TargetSet& targets) const { // go through all our code and see what targets the code is for for (ListCode::const_iterator i = _codes.begin(); i != _codes.end(); ++i) { const Code* c = *i; targets.insert(c->target()); } } void CodeList::get_targets(Code::TargetSet& targets, const filter::Filter& filter) const { // // Go through all our code and see what targets the code. // Insert only targets that match the given filter. // for (ListCode::const_iterator i = _codes.begin(); i != _codes.end(); ++i) { const Code* c = *i; if (c->target().filter() == filter) targets.insert(c->target()); } } void CodeList::get_redist_tags(const string& protocol, Code::TagSet& tagset) const { // go through all the code we have. for (ListCode::const_iterator i = _codes.begin(); i != _codes.end(); ++i) { const Code* c = *i; // we only want tags for specific protocols. if (c->target().protocol() != protocol) continue; const Code::TagSet& ts = c->redist_tags(); // insert the tags for this protocol. for (Code::TagSet::const_iterator j = ts.begin(); j != ts.end(); ++j) { tagset.insert(*j); } } } xorp/policy/yacc.yy_policy_parser.cc0000664000076400007640000006745511421137511020015 0ustar greearbgreearb#include #ifndef lint #endif #define YYBYACC 1 #define YYMAJOR 1 #define YYMINOR 9 #define YYLEX yylex() #define YYEMPTY -1 #define yyclearin (yychar=(YYEMPTY)) #define yyerrok (yyerrflag=0) #define YYRECOVERING() (yyerrflag!=0) #if defined(__cplusplus) || __STDC__ static int yygrowstack(void); #else static int yygrowstack(); #endif #define yyparse yy_policy_parserparse #define yylex yy_policy_parserlex #define yyerror yy_policy_parsererror #define yychar yy_policy_parserchar #define yyval yy_policy_parserval #define yylval yy_policy_parserlval #define yydebug yy_policy_parserdebug #define yynerrs yy_policy_parsernerrs #define yyerrflag yy_policy_parsererrflag #define yyss yy_policy_parserss #define yyssp yy_policy_parserssp #define yyvs yy_policy_parservs #define yyvsp yy_policy_parservsp #define yylhs yy_policy_parserlhs #define yylen yy_policy_parserlen #define yydefred yy_policy_parserdefred #define yydgoto yy_policy_parserdgoto #define yysindex yy_policy_parsersindex #define yyrindex yy_policy_parserrindex #define yygindex yy_policy_parsergindex #define yytable yy_policy_parsertable #define yycheck yy_policy_parsercheck #define yyname yy_policy_parsername #define yyrule yy_policy_parserrule #define yysslim yy_policy_parsersslim #define yystacksize yy_policy_parserstacksize #define YYPREFIX "yy_policy_parser" #line 2 "policy.y" /* * Grammar may be simplified, by allowing "any structure", semantic checking is * done at run time anyway... * By any structure i mean that you may add / multiple boolean expressions for * example. This will give more run time flexibility * * yacc -d -p yy_policy_parser -o yacc.yy_policy_parser.cc policy.y */ #include "policy_module.h" #include "libxorp/xorp.h" #include "policy/common/element.hh" #include "policy/common/element_factory.hh" #include "policy/common/operator.hh" #include "policy_parser.hh" extern int yylex(void); extern void yyerror(const char *m); using namespace policy_parser; static ElementFactory _ef; #line 29 "policy.y" typedef union { char* c_str; Node* node; BinOper* op; } YYSTYPE; #line 82 "yacc.yy_policy_parser.cc" #define YYERRCODE 256 #define YY_BOOL 257 #define YY_INT 258 #define YY_UINT 259 #define YY_UINTRANGE 260 #define YY_STR 261 #define YY_ID 262 #define YY_IPV4 263 #define YY_IPV4RANGE 264 #define YY_IPV4NET 265 #define YY_IPV6 266 #define YY_IPV6RANGE 267 #define YY_IPV6NET 268 #define YY_SEMICOLON 269 #define YY_LPAR 270 #define YY_RPAR 271 #define YY_ASSIGN 272 #define YY_SET 273 #define YY_REGEX 274 #define YY_ACCEPT 275 #define YY_REJECT 276 #define YY_PROTOCOL 277 #define YY_NEXT 278 #define YY_POLICY 279 #define YY_PLUS_EQUALS 280 #define YY_MINUS_EQUALS 281 #define YY_TERM 282 #define YY_NOT 283 #define YY_AND 284 #define YY_XOR 285 #define YY_OR 286 #define YY_HEAD 287 #define YY_CTR 288 #define YY_NE_INT 289 #define YY_EQ 290 #define YY_NE 291 #define YY_LE 292 #define YY_GT 293 #define YY_LT 294 #define YY_GE 295 #define YY_IPNET_EQ 296 #define YY_IPNET_LE 297 #define YY_IPNET_GT 298 #define YY_IPNET_LT 299 #define YY_IPNET_GE 300 #define YY_ADD 301 #define YY_SUB 302 #define YY_MUL 303 const short yy_policy_parserlhs[] = { -1, 0, 0, 0, 1, 2, 2, 2, 2, 2, 6, 7, 7, 7, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, }; const short yy_policy_parserlen[] = { 2, 2, 2, 0, 2, 1, 1, 1, 2, 2, 3, 1, 1, 1, 2, 3, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, }; const short yy_policy_parserdefred[] = { 3, 0, 46, 45, 43, 44, 42, 0, 49, 50, 53, 51, 52, 54, 0, 0, 6, 7, 0, 0, 0, 0, 0, 0, 1, 0, 2, 0, 0, 5, 11, 12, 13, 0, 47, 0, 0, 48, 0, 8, 9, 17, 16, 0, 0, 0, 4, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 41, 15, 0, 0, 18, 19, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, }; const short yy_policy_parserdgoto[] = { 1, 24, 25, 26, 27, 28, 29, 33, }; const short yy_policy_parsersindex[] = { 0, 57, 0, 0, 0, 0, 0, -266, 0, 0, 0, 0, 0, 0, 89, -258, 0, 0, -279, -213, -249, 89, 121, 121, 0, -246, 0, 226, 122, 0, 0, 0, 0, 121, 0, 11, 10, 0, -238, 0, 0, 0, 0, 121, -178, -203, 0, 0, 89, 89, 89, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, -178, 0, 0, 0, -269, -178, 0, 0, 0, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -275, -275, 0, }; const short yy_policy_parserrindex[] = { 0, 0, 0, 0, 0, 0, 0, 152, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -239, 0, 0, 0, 0, -20, 0, 0, 0, -268, -264, -259, -197, -194, -190, 187, 190, 193, 196, 199, 217, 220, 223, -156, -110, 0, }; const short yy_policy_parsergindex[] = { 0, 0, 0, 0, 72, -14, 0, 0, }; #define YYTABLESIZE 512 const short yy_policy_parsertable[] = { 36, 35, 70, 35, 37, 28, 30, 28, 44, 45, 33, 38, 33, 41, 31, 32, 35, 35, 35, 68, 28, 28, 28, 46, 71, 33, 33, 33, 67, 72, 10, 73, 65, 66, 67, 0, 0, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 2, 3, 4, 5, 6, 34, 8, 9, 10, 11, 12, 13, 39, 43, 0, 40, 15, 0, 21, 0, 21, 22, 0, 22, 0, 25, 0, 25, 0, 0, 22, 23, 35, 21, 21, 21, 22, 22, 22, 42, 25, 25, 25, 0, 65, 66, 67, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 0, 36, 36, 0, 74, 75, 76, 65, 66, 67, 0, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 0, 37, 37, 0, 0, 0, 0, 0, 0, 0, 0, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 0, 39, 39, 0, 0, 0, 0, 0, 0, 0, 0, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 0, 40, 40, 0, 0, 0, 0, 0, 0, 0, 0, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 70, 69, 0, 51, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 48, 49, 50, 0, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 0, 14, 0, 0, 15, 0, 16, 17, 18, 19, 20, 0, 0, 0, 21, 0, 0, 0, 22, 23, 2, 3, 4, 5, 6, 34, 8, 9, 10, 11, 12, 13, 0, 14, 0, 0, 15, 0, 0, 0, 18, 0, 20, 0, 0, 0, 21, 0, 0, 0, 22, 23, 2, 3, 4, 5, 6, 34, 8, 9, 10, 11, 12, 13, 0, 43, 0, 0, 15, 0, 51, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 22, 23, 0, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 47, 0, 0, 0, 0, 0, 0, 0, 0, 47, 0, 0, 0, 0, 0, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 24, 0, 24, 23, 0, 23, 26, 0, 26, 27, 0, 27, 31, 0, 31, 24, 24, 24, 23, 23, 23, 26, 26, 26, 27, 27, 27, 31, 31, 31, 30, 0, 30, 29, 0, 29, 32, 0, 32, 47, 0, 0, 0, 0, 0, 30, 30, 30, 29, 29, 29, 32, 32, 32, 48, 49, 50, }; const short yy_policy_parsercheck[] = { 14, 269, 271, 271, 262, 269, 272, 271, 22, 23, 269, 290, 271, 262, 280, 281, 284, 285, 286, 33, 284, 285, 286, 269, 262, 284, 285, 286, 303, 43, 269, 45, 301, 302, 303, -1, -1, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 279, 270, -1, 282, 273, -1, 269, -1, 271, 269, -1, 271, -1, 269, -1, 271, -1, -1, 287, 288, 14, 284, 285, 286, 284, 285, 286, 21, 284, 285, 286, -1, 301, 302, 303, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, -1, 273, 274, -1, 48, 49, 50, 301, 302, 303, -1, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, -1, 273, 274, -1, -1, -1, -1, -1, -1, -1, -1, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, -1, 273, 274, -1, -1, -1, -1, -1, -1, -1, -1, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, -1, 273, 274, -1, -1, -1, -1, -1, -1, -1, -1, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 271, 271, -1, 274, -1, -1, -1, -1, -1, -1, -1, -1, 283, -1, 284, 285, 286, -1, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, -1, 270, -1, -1, 273, -1, 275, 276, 277, 278, 279, -1, -1, -1, 283, -1, -1, -1, 287, 288, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, -1, 270, -1, -1, 273, -1, -1, -1, 277, -1, 279, -1, -1, -1, 283, -1, -1, -1, 287, 288, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, -1, 270, -1, -1, 273, -1, 274, -1, -1, -1, -1, -1, -1, -1, -1, 283, -1, -1, 287, 288, -1, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 274, -1, -1, -1, -1, -1, -1, -1, -1, 283, -1, -1, -1, -1, -1, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 269, -1, 271, 269, -1, 271, 269, -1, 271, 269, -1, 271, 269, -1, 271, 284, 285, 286, 284, 285, 286, 284, 285, 286, 284, 285, 286, 284, 285, 286, 269, -1, 271, 269, -1, 271, 269, -1, 271, 269, -1, -1, -1, -1, -1, 284, 285, 286, 284, 285, 286, 284, 285, 286, 284, 285, 286, }; #define YYFINAL 1 #ifndef YYDEBUG #define YYDEBUG 0 #endif #define YYMAXTOKEN 303 #if YYDEBUG const char * const yy_policy_parsername[] = { "end-of-file",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,"YY_BOOL","YY_INT","YY_UINT", "YY_UINTRANGE","YY_STR","YY_ID","YY_IPV4","YY_IPV4RANGE","YY_IPV4NET","YY_IPV6", "YY_IPV6RANGE","YY_IPV6NET","YY_SEMICOLON","YY_LPAR","YY_RPAR","YY_ASSIGN", "YY_SET","YY_REGEX","YY_ACCEPT","YY_REJECT","YY_PROTOCOL","YY_NEXT","YY_POLICY", "YY_PLUS_EQUALS","YY_MINUS_EQUALS","YY_TERM","YY_NOT","YY_AND","YY_XOR","YY_OR", "YY_HEAD","YY_CTR","YY_NE_INT","YY_EQ","YY_NE","YY_LE","YY_GT","YY_LT","YY_GE", "YY_IPNET_EQ","YY_IPNET_LE","YY_IPNET_GT","YY_IPNET_LT","YY_IPNET_GE","YY_ADD", "YY_SUB","YY_MUL", }; const char * const yy_policy_parserrule[] = { "$accept : statement", "statement : statement actionstatement", "statement : statement boolstatement", "statement :", "actionstatement : action YY_SEMICOLON", "action : assignexpr", "action : YY_ACCEPT", "action : YY_REJECT", "action : YY_NEXT YY_POLICY", "action : YY_NEXT YY_TERM", "assignexpr : YY_ID assignop expr", "assignop : YY_ASSIGN", "assignop : YY_PLUS_EQUALS", "assignop : YY_MINUS_EQUALS", "boolstatement : boolexpr YY_SEMICOLON", "boolexpr : YY_PROTOCOL YY_EQ YY_ID", "boolexpr : YY_NOT boolexpr", "boolexpr : YY_POLICY YY_ID", "boolexpr : boolexpr YY_AND boolexpr", "boolexpr : boolexpr YY_XOR boolexpr", "boolexpr : boolexpr YY_OR boolexpr", "boolexpr : expr YY_EQ expr", "boolexpr : expr YY_NE expr", "boolexpr : expr YY_LT expr", "boolexpr : expr YY_GT expr", "boolexpr : expr YY_LE expr", "boolexpr : expr YY_GE expr", "boolexpr : expr YY_IPNET_EQ expr", "boolexpr : expr YY_NOT expr", "boolexpr : expr YY_IPNET_LT expr", "boolexpr : expr YY_IPNET_GT expr", "boolexpr : expr YY_IPNET_LE expr", "boolexpr : expr YY_IPNET_GE expr", "boolexpr : expr YY_NE_INT expr", "boolexpr : YY_LPAR boolexpr YY_RPAR", "boolexpr : expr YY_REGEX expr", "expr : expr YY_ADD expr", "expr : expr YY_SUB expr", "expr : expr YY_MUL expr", "expr : YY_HEAD expr", "expr : YY_CTR expr expr", "expr : YY_LPAR expr YY_RPAR", "expr : YY_STR", "expr : YY_UINT", "expr : YY_UINTRANGE", "expr : YY_INT", "expr : YY_BOOL", "expr : YY_ID", "expr : YY_SET YY_ID", "expr : YY_IPV4", "expr : YY_IPV4RANGE", "expr : YY_IPV6", "expr : YY_IPV6RANGE", "expr : YY_IPV4NET", "expr : YY_IPV6NET", }; #endif #if YYDEBUG #include #endif #ifdef YYSTACKSIZE #undef YYMAXDEPTH #define YYMAXDEPTH YYSTACKSIZE #else #ifdef YYMAXDEPTH #define YYSTACKSIZE YYMAXDEPTH #else #define YYSTACKSIZE 10000 #define YYMAXDEPTH 10000 #endif #endif #define YYINITSTACKSIZE 200 int yydebug; int yynerrs; int yyerrflag; int yychar; short *yyssp; YYSTYPE *yyvsp; YYSTYPE yyval; YYSTYPE yylval; short *yyss; short *yysslim; YYSTYPE *yyvs; int yystacksize; /* allocate initial stack or double stack size, up to YYMAXDEPTH */ static int yygrowstack() { int newsize, i; short *newss; YYSTYPE *newvs; if ((newsize = yystacksize) == 0) newsize = YYINITSTACKSIZE; else if (newsize >= YYMAXDEPTH) return -1; else if ((newsize *= 2) > YYMAXDEPTH) newsize = YYMAXDEPTH; i = yyssp - yyss; newss = yyss ? (short *)realloc(yyss, newsize * sizeof *newss) : (short *)malloc(newsize * sizeof *newss); if (newss == NULL) return -1; yyss = newss; yyssp = newss + i; newvs = yyvs ? (YYSTYPE *)realloc(yyvs, newsize * sizeof *newvs) : (YYSTYPE *)malloc(newsize * sizeof *newvs); if (newvs == NULL) return -1; yyvs = newvs; yyvsp = newvs + i; yystacksize = newsize; yysslim = yyss + newsize - 1; return 0; } #define YYABORT goto yyabort #define YYREJECT goto yyabort #define YYACCEPT goto yyaccept #define YYERROR goto yyerrlab #ifndef YYPARSE_PARAM #if defined(__cplusplus) || __STDC__ #define YYPARSE_PARAM_ARG void #define YYPARSE_PARAM_DECL #else /* ! ANSI-C/C++ */ #define YYPARSE_PARAM_ARG #define YYPARSE_PARAM_DECL #endif /* ANSI-C/C++ */ #else /* YYPARSE_PARAM */ #ifndef YYPARSE_PARAM_TYPE #define YYPARSE_PARAM_TYPE void * #endif #if defined(__cplusplus) || __STDC__ #define YYPARSE_PARAM_ARG YYPARSE_PARAM_TYPE YYPARSE_PARAM #define YYPARSE_PARAM_DECL #else /* ! ANSI-C/C++ */ #define YYPARSE_PARAM_ARG YYPARSE_PARAM #define YYPARSE_PARAM_DECL YYPARSE_PARAM_TYPE YYPARSE_PARAM; #endif /* ANSI-C/C++ */ #endif /* ! YYPARSE_PARAM */ int yyparse (YYPARSE_PARAM_ARG) YYPARSE_PARAM_DECL { int yym, yyn, yystate; #if YYDEBUG const char *yys; if ((yys = getenv("YYDEBUG"))) { yyn = *yys; if (yyn >= '0' && yyn <= '9') yydebug = yyn - '0'; } #endif yynerrs = 0; yyerrflag = 0; yychar = (-1); if (yyss == NULL && yygrowstack()) goto yyoverflow; yyssp = yyss; yyvsp = yyvs; *yyssp = yystate = 0; yyloop: if ((yyn = yydefred[yystate])) goto yyreduce; if (yychar < 0) { if ((yychar = yylex()) < 0) yychar = 0; #if YYDEBUG if (yydebug) { yys = 0; if (yychar <= YYMAXTOKEN) yys = yyname[yychar]; if (!yys) yys = "illegal-symbol"; printf("%sdebug: state %d, reading %d (%s)\n", YYPREFIX, yystate, yychar, yys); } #endif } if ((yyn = yysindex[yystate]) && (yyn += yychar) >= 0 && yyn <= YYTABLESIZE && yycheck[yyn] == yychar) { #if YYDEBUG if (yydebug) printf("%sdebug: state %d, shifting to state %d\n", YYPREFIX, yystate, yytable[yyn]); #endif if (yyssp >= yysslim && yygrowstack()) { goto yyoverflow; } *++yyssp = yystate = yytable[yyn]; *++yyvsp = yylval; yychar = (-1); if (yyerrflag > 0) --yyerrflag; goto yyloop; } if ((yyn = yyrindex[yystate]) && (yyn += yychar) >= 0 && yyn <= YYTABLESIZE && yycheck[yyn] == yychar) { yyn = yytable[yyn]; goto yyreduce; } if (yyerrflag) goto yyinrecovery; #if defined(lint) || defined(__GNUC__) goto yynewerror; #endif yynewerror: yyerror("syntax error"); #if defined(lint) || defined(__GNUC__) goto yyerrlab; #endif yyerrlab: ++yynerrs; yyinrecovery: if (yyerrflag < 3) { yyerrflag = 3; for (;;) { if ((yyn = yysindex[*yyssp]) && (yyn += YYERRCODE) >= 0 && yyn <= YYTABLESIZE && yycheck[yyn] == YYERRCODE) { #if YYDEBUG if (yydebug) printf("%sdebug: state %d, error recovery shifting\ to state %d\n", YYPREFIX, *yyssp, yytable[yyn]); #endif if (yyssp >= yysslim && yygrowstack()) { goto yyoverflow; } *++yyssp = yystate = yytable[yyn]; *++yyvsp = yylval; goto yyloop; } else { #if YYDEBUG if (yydebug) printf("%sdebug: error recovery discarding state %d\n", YYPREFIX, *yyssp); #endif if (yyssp <= yyss) goto yyabort; --yyssp; --yyvsp; } } } else { if (yychar == 0) goto yyabort; #if YYDEBUG if (yydebug) { yys = 0; if (yychar <= YYMAXTOKEN) yys = yyname[yychar]; if (!yys) yys = "illegal-symbol"; printf("%sdebug: state %d, error recovery discards token %d (%s)\n", YYPREFIX, yystate, yychar, yys); } #endif yychar = (-1); goto yyloop; } yyreduce: #if YYDEBUG if (yydebug) printf("%sdebug: state %d, reducing by rule %d (%s)\n", YYPREFIX, yystate, yyn, yyrule[yyn]); #endif yym = yylen[yyn]; yyval = yyvsp[1-yym]; switch (yyn) { case 1: #line 52 "policy.y" { _parser_nodes->push_back(yyvsp[0].node); } break; case 2: #line 53 "policy.y" { _parser_nodes->push_back(yyvsp[0].node); } break; case 4: #line 58 "policy.y" { yyval.node = yyvsp[-1].node; } break; case 6: #line 63 "policy.y" { yyval.node = new NodeAccept(_parser_lineno); } break; case 7: #line 64 "policy.y" { yyval.node = new NodeReject(_parser_lineno); } break; case 8: #line 66 "policy.y" { yyval.node = new NodeNext(_parser_lineno, NodeNext::POLICY); } break; case 9: #line 68 "policy.y" { yyval.node = new NodeNext(_parser_lineno, NodeNext::TERM); } break; case 10: #line 73 "policy.y" { yyval.node = new NodeAssign(yyvsp[-2].c_str, yyvsp[-1].op, yyvsp[0].node, _parser_lineno); free(yyvsp[-2].c_str); } break; case 11: #line 77 "policy.y" { yyval.op = NULL; } break; case 12: #line 78 "policy.y" { yyval.op = new OpAdd; } break; case 13: #line 79 "policy.y" { yyval.op = new OpSub; } break; case 14: #line 83 "policy.y" { yyval.node = yyvsp[-1].node; } break; case 15: #line 87 "policy.y" { yyval.node = new NodeProto(yyvsp[0].c_str,_parser_lineno); free(yyvsp[0].c_str); } break; case 16: #line 88 "policy.y" { yyval.node = new NodeUn(new OpNot,yyvsp[0].node,_parser_lineno); } break; case 17: #line 89 "policy.y" { yyval.node = new NodeSubr(_parser_lineno, yyvsp[0].c_str); free(yyvsp[0].c_str); } break; case 18: #line 90 "policy.y" { yyval.node = new NodeBin(new OpAnd,yyvsp[-2].node,yyvsp[0].node,_parser_lineno); } break; case 19: #line 91 "policy.y" { yyval.node = new NodeBin(new OpXor,yyvsp[-2].node,yyvsp[0].node,_parser_lineno); } break; case 20: #line 92 "policy.y" { yyval.node = new NodeBin(new OpOr,yyvsp[-2].node,yyvsp[0].node,_parser_lineno); } break; case 21: #line 94 "policy.y" { yyval.node = new NodeBin(new OpEq,yyvsp[-2].node,yyvsp[0].node,_parser_lineno); } break; case 22: #line 95 "policy.y" { yyval.node = new NodeBin(new OpNe,yyvsp[-2].node,yyvsp[0].node,_parser_lineno); } break; case 23: #line 97 "policy.y" { yyval.node = new NodeBin(new OpLt,yyvsp[-2].node,yyvsp[0].node,_parser_lineno); } break; case 24: #line 98 "policy.y" { yyval.node = new NodeBin(new OpGt,yyvsp[-2].node,yyvsp[0].node,_parser_lineno); } break; case 25: #line 99 "policy.y" { yyval.node = new NodeBin(new OpLe,yyvsp[-2].node,yyvsp[0].node,_parser_lineno); } break; case 26: #line 100 "policy.y" { yyval.node = new NodeBin(new OpGe,yyvsp[-2].node,yyvsp[0].node,_parser_lineno); } break; case 27: #line 102 "policy.y" { yyval.node = new NodeBin(new OpEq,yyvsp[-2].node,yyvsp[0].node,_parser_lineno); } break; case 28: #line 103 "policy.y" { yyval.node = new NodeBin(new OpNe,yyvsp[-2].node,yyvsp[0].node,_parser_lineno); } break; case 29: #line 104 "policy.y" { yyval.node = new NodeBin(new OpLt,yyvsp[-2].node,yyvsp[0].node,_parser_lineno); } break; case 30: #line 105 "policy.y" { yyval.node = new NodeBin(new OpGt,yyvsp[-2].node,yyvsp[0].node,_parser_lineno); } break; case 31: #line 106 "policy.y" { yyval.node = new NodeBin(new OpLe,yyvsp[-2].node,yyvsp[0].node,_parser_lineno); } break; case 32: #line 107 "policy.y" { yyval.node = new NodeBin(new OpGe,yyvsp[-2].node,yyvsp[0].node,_parser_lineno); } break; case 33: #line 109 "policy.y" { yyval.node = new NodeBin(new OpNEInt, yyvsp[-2].node, yyvsp[0].node, _parser_lineno); } break; case 34: #line 111 "policy.y" { yyval.node = yyvsp[-1].node; } break; case 35: #line 113 "policy.y" { yyval.node = new NodeBin(new OpRegex, yyvsp[-2].node, yyvsp[0].node, _parser_lineno); } break; case 36: #line 117 "policy.y" { yyval.node = new NodeBin(new OpAdd,yyvsp[-2].node,yyvsp[0].node,_parser_lineno); } break; case 37: #line 118 "policy.y" { yyval.node = new NodeBin(new OpSub,yyvsp[-2].node,yyvsp[0].node,_parser_lineno); } break; case 38: #line 119 "policy.y" { yyval.node = new NodeBin(new OpMul,yyvsp[-2].node,yyvsp[0].node,_parser_lineno); } break; case 39: #line 121 "policy.y" { yyval.node = new NodeUn(new OpHead, yyvsp[0].node, _parser_lineno); } break; case 40: #line 122 "policy.y" { yyval.node = new NodeBin(new OpCtr, yyvsp[-1].node, yyvsp[0].node, _parser_lineno); } break; case 41: #line 124 "policy.y" { yyval.node = yyvsp[-1].node; } break; case 42: #line 126 "policy.y" { yyval.node = new NodeElem(_ef.create(ElemStr::id,yyvsp[0].c_str),_parser_lineno); free(yyvsp[0].c_str); } break; case 43: #line 127 "policy.y" { yyval.node = new NodeElem(_ef.create(ElemU32::id,yyvsp[0].c_str),_parser_lineno); free(yyvsp[0].c_str);} break; case 44: #line 128 "policy.y" { yyval.node = new NodeElem(_ef.create(ElemU32Range::id,yyvsp[0].c_str),_parser_lineno); free(yyvsp[0].c_str);} break; case 45: #line 129 "policy.y" { yyval.node = new NodeElem(_ef.create(ElemInt32::id,yyvsp[0].c_str),_parser_lineno); free(yyvsp[0].c_str);} break; case 46: #line 130 "policy.y" { yyval.node = new NodeElem(_ef.create(ElemBool::id,yyvsp[0].c_str),_parser_lineno); free(yyvsp[0].c_str);} break; case 47: #line 131 "policy.y" { yyval.node = new NodeVar(yyvsp[0].c_str,_parser_lineno); free(yyvsp[0].c_str); } break; case 48: #line 132 "policy.y" { yyval.node = new NodeSet(yyvsp[0].c_str,_parser_lineno); free(yyvsp[0].c_str); } break; case 49: #line 133 "policy.y" { yyval.node = new NodeElem(_ef.create(ElemIPv4::id,yyvsp[0].c_str),_parser_lineno); free(yyvsp[0].c_str); } break; case 50: #line 134 "policy.y" { yyval.node = new NodeElem(_ef.create(ElemIPv4Range::id,yyvsp[0].c_str),_parser_lineno); free(yyvsp[0].c_str); } break; case 51: #line 135 "policy.y" { yyval.node = new NodeElem(_ef.create(ElemIPv6::id,yyvsp[0].c_str),_parser_lineno); free(yyvsp[0].c_str); } break; case 52: #line 136 "policy.y" { yyval.node = new NodeElem(_ef.create(ElemIPv6Range::id,yyvsp[0].c_str),_parser_lineno); free(yyvsp[0].c_str); } break; case 53: #line 137 "policy.y" { yyval.node = new NodeElem(_ef.create(ElemIPv4Net::id,yyvsp[0].c_str),_parser_lineno); free(yyvsp[0].c_str); } break; case 54: #line 138 "policy.y" { yyval.node = new NodeElem(_ef.create(ElemIPv6Net::id,yyvsp[0].c_str),_parser_lineno); free(yyvsp[0].c_str); } break; #line 807 "yacc.yy_policy_parser.cc" } yyssp -= yym; yystate = *yyssp; yyvsp -= yym; yym = yylhs[yyn]; if (yystate == 0 && yym == 0) { #if YYDEBUG if (yydebug) printf("%sdebug: after reduction, shifting from state 0 to\ state %d\n", YYPREFIX, YYFINAL); #endif yystate = YYFINAL; *++yyssp = YYFINAL; *++yyvsp = yyval; if (yychar < 0) { if ((yychar = yylex()) < 0) yychar = 0; #if YYDEBUG if (yydebug) { yys = 0; if (yychar <= YYMAXTOKEN) yys = yyname[yychar]; if (!yys) yys = "illegal-symbol"; printf("%sdebug: state %d, reading %d (%s)\n", YYPREFIX, YYFINAL, yychar, yys); } #endif } if (yychar == 0) goto yyaccept; goto yyloop; } if ((yyn = yygindex[yym]) && (yyn += yystate) >= 0 && yyn <= YYTABLESIZE && yycheck[yyn] == yystate) yystate = yytable[yyn]; else yystate = yydgoto[yym]; #if YYDEBUG if (yydebug) printf("%sdebug: after reduction, shifting from state %d \ to state %d\n", YYPREFIX, *yyssp, yystate); #endif if (yyssp >= yysslim && yygrowstack()) { goto yyoverflow; } *++yyssp = yystate; *++yyvsp = yyval; goto yyloop; yyoverflow: yyerror("yacc stack overflow"); yyabort: return (1); yyaccept: return (0); } xorp/policy/var_map.hh0000664000076400007640000001210711540225533015126 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/policy/var_map.hh,v 1.12 2008/10/02 21:58:01 bms Exp $ #ifndef __POLICY_VAR_MAP_HH__ #define __POLICY_VAR_MAP_HH__ #include "policy/common/policy_exception.hh" #include "policy/common/varrw.hh" #include "process_watch.hh" /** * @short A VarMap contains all information for legal protocol variables. * * It contains all the possible protocols. * All the possible variables those protocols support and whether the variables * are read-only or read-write. * * This is crutial for semantic checking. */ class VarMap : public NONCOPYABLE { public: /** * @short Exception thrown on VarMap errors such as on unknown variables. */ class VarMapErr : public PolicyException { public: VarMapErr(const char* file, size_t line, const string& init_why = "") : PolicyException("VarMapErr", file, line, init_why) {} }; /** * A variable may be READ [readonly] or READ_WRITE [read/write]. */ enum Access { READ, READ_WRITE, WRITE }; /** * A variable has Access control, it has a name, and a type. */ struct Variable { Access access; string name; string type; VarRW::Id id; Variable(const string& n, const string& t, Access a, VarRW::Id i) : access(a), name(n), type(t), id(i) { } Variable(const Variable& v) { access = v.access; name = v.name; type = v.type; id = v.id; } bool writable() const { return access == READ_WRITE || access == WRITE; } bool operator==(const Variable& other) const { return ((access == other.access) && (name == other.name) && (type == other.type) && (id == other.id)); } }; typedef map VariableMap; typedef map ProtoMap; /** * Return Variable information for a variable of a specific protocol. * * Throws an exception if no information is found. * * Caller must not delete the map. * * @return access and type information for the requested variable. * @param protocol protocol of variable interested in. * @param varname name of variable interested in. */ const Variable& variable(const string& protocol, const VarRW::Id& varname) const; VarRW::Id var2id(const string& protocol, const string& varname) const; /** * As the VarMap learns about new protocols, it will register interest with * the process watcher for that protocol to monitor the status of the * protocol process. * * @param pw processWatch to use. */ VarMap(ProcessWatchBase& pw); ~VarMap(); /** * @return True if the protocol is known to the VarMap, false otherwise. * @param protocol protocol caller wish to knows existance of. */ bool protocol_known(const string& protocol); /** * Add a variable to a protocol. * * @param protocol protocol for which variable should be added. * @param var the variable to add. Do not delete. */ void add_protocol_variable(const string& protocol, Variable* var); /** * String representation of varmap. Use only for debugging. * * @return string representation of varmap. */ string str(); private: /** * Use this if you want a variable to be present for all protocols. * * @param var the variable to add. Watch out for clashes and don't delete. */ void add_metavariable(Variable *var); /** * Add a variable to a specific protocol. * * @param vm VariableMap where variable should be added. * @param var the variable to add. Do not delete. */ void add_variable(VariableMap& vm, Variable* var); /** * A VariableMap relates a variable name to its Variable information [access * and type]. * * Throws an exception if no map is found. * * @return variable map for requested protocol. * @param protocol protocol name for which variable map is requested. */ const VariableMap& variablemap(const string& protocol) const; ProtoMap _protocols; ProcessWatchBase& _process_watch; typedef VariableMap MetaVarContainer; MetaVarContainer _metavars; }; #endif // __POLICY_VAR_MAP_HH__ xorp/policy/set_map.hh0000664000076400007640000001061711540224233015131 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/policy/set_map.hh,v 1.12 2008/10/02 21:58:00 bms Exp $ #ifndef __POLICY_SET_MAP_HH__ #define __POLICY_SET_MAP_HH__ #include "policy/common/element_factory.hh" #include "policy/common/policy_exception.hh" #include "dependency.hh" typedef vector SETS; /** * @short Container of all sets. * * The SetMap owns all sets in the policy configuration. It also tracks * dependencies between sets and policies. */ class SetMap { public: /** * @short Exception thrown on error, such as deleting a set in use. */ class SetMapError : public PolicyException { public: SetMapError(const char* file, size_t line, const string& init_why = "") : PolicyException("SetMapError", file, line, init_why) {} }; /** * Throws exception if set is not found. * * @return set requested. * @param name set name requested. */ const Element& getSet(const string& name) const; /** * Create a new set. * * Throws exception if set exists. * * @param name name of the set. */ void create(const string& name); /** * Replace the elements of a set. * * Throws an expcetion if set does not exist. * * @param type type of the set. * @param name name of the set. * @param elements the new elements comma separated. * @param modified set filled with policies which are now modified. */ void update_set(const string& type, const string& name, const string& elements, set& modified); /** * Attempts to delete a set. * * Throws an exception if set is in use. * * @param name name of the set. */ void delete_set(const string& name); /** * Add an element to a set. * * Throws an expcetion if set does not exist. * * @param type type of the set. * @param name name of the set. * @param element the element to add. * @param modified set filled with policies which are now modified. */ void add_to_set(const string& type, const string& name, const string& element, set& modified); /** * Delete an element from a set. * * Throws an expcetion if set does not exist. * * @param type type of the set. * @param name name of the set. * @param element the element to delete. * @param modified set filled with policies which are now modified. */ void delete_from_set(const string& type, const string& name, const string& element, set& modified); /** * Add a dependency of a policy using a set. * * Throws an exception if set is not found. * * @param setname name of set in which dependency should be added. * @param policyname name of policy which uses the set. */ void add_dependency(const string& setname, const string& policyname); /** * Delete a dependency of a policy using a set. * * Throws an exception if set or policy is not found. * * @param setname name of set in which dependency should be removed. * @param policyname name of policy which no longer uses the set. */ void del_dependency(const string& setname, const string& policyname); /** * @return string representation of all sets. */ string str() const; void sets_by_type(SETS& s, const string& type) const; private: typedef Dependency Dep; Dep _deps; ElementFactory _ef; }; #endif // __POLICY_SET_MAP_HH__ xorp/policy/visitor_dep.hh0000664000076400007640000000505211540224233016025 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/policy/visitor_dep.hh,v 1.3 2008/10/02 21:58:01 bms Exp $ #ifndef __POLICY_VISITOR_DEP_HH__ #define __POLICY_VISITOR_DEP_HH__ #include "policy/common/policy_exception.hh" #include "visitor.hh" #include "set_map.hh" #include "policy_map.hh" #include "policy_statement.hh" #include "node.hh" /** * @short This visitor is used to check what sets a policy uses. * * This is useful for set dependancy tracking. */ class VisitorDep : public Visitor { public: /** * @short Semantic error thrown if set is not found. */ class sem_error : public PolicyException { public: sem_error(const char* file, size_t line, const string& init_why = "") : PolicyException("sem_error", file, line, init_why) {} }; /** * @param setmap The setmap used. */ VisitorDep(SetMap& setmap, PolicyMap& pmap); const Element* visit(PolicyStatement& policy); const Element* visit(Term& term); const Element* visit(NodeUn& node); const Element* visit(NodeBin& node); const Element* visit(NodeAssign& node); const Element* visit(NodeVar& node); const Element* visit(NodeSet& node); const Element* visit(NodeElem& node); const Element* visit(NodeAccept& node); const Element* visit(NodeReject& node); const Element* visit(NodeProto& node); const Element* visit(NodeNext& node); const Element* visit(NodeSubr& node); /** * @return the sets used by the policy. */ const DEPS& sets() const; private: void commit_deps(PolicyStatement& policy); SetMap& _setmap; PolicyMap& _pmap; DEPS _sets; DEPS _policies; }; #endif // __POLICY_VISITOR_DEP_HH__ xorp/policy/backend/0000775000076400007640000000000011540225532014545 5ustar greearbgreearbxorp/policy/backend/single_varrw.hh0000664000076400007640000001127611540225532017577 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/policy/backend/single_varrw.hh,v 1.15 2008/10/02 21:58:05 bms Exp $ #ifndef __POLICY_BACKEND_SINGLE_VARRW_HH__ #define __POLICY_BACKEND_SINGLE_VARRW_HH__ #include "policy/common/varrw.hh" #include "policy/common/policy_utils.hh" #include "policy/common/element_base.hh" #include "policytags.hh" /** * @short An interface to VarRW which deals with memory management. * * Read and writes are cached, so they are done only once on the route, not * matter how many time the filter requests or writes the variable. * * Because of this caching, the SingleVarRW is usuable only once. After it has * done its work once, it has to be re-created. */ class SingleVarRW : public NONCOPYABLE, public VarRW { public: /** * @short Exception thrown on error, such as reading unsupported variable. */ class SingleVarRWErr : public PolicyException { public: SingleVarRWErr(const char* file, size_t line, const string& init_why = "") : PolicyException("SingleVarRWErr", file, line, init_why) {} }; SingleVarRW(); virtual ~SingleVarRW(); /** * Implementation of VarRW read. * * @return variable requested. * @param id identifier of variable to be read. */ const Element& read(const Id& id); /** * Implementation of VarRW write. * * @param id identifier of variable to be written to. * @param e value of variable to be written to. */ void write(const Id& id, const Element& e); /** * Implementation of VarRW sync. * * Writes are performed now, as cached Element* pointers may become invalid * afterwards.a * * trash is also emptied upon completion. */ void sync(); // XXX: be smart: register callback for element writing /** * Register a variable for read access with SingleVarRW. * SingleVarRW owns the element, so derived classes do not need to worry * about deleting objects. * * All supported variables must be registered, even the ones not present in * the current route. For example v6 nexthops must be set to ElemNull on v4 * routes. [assuming the protocol itself supports v6]. * * @param id identifier of variable that may be read. * @param e value of variable. */ void initialize(const Id& id, Element* e); void initialize(PolicyTags& pt); /** * If any reads are performed, this is a marker which informs the derived * class that reads will now start. */ virtual void start_read() {} /** * If any writes were performed, this is a marker which informs the derived * class that writes will start. */ virtual void start_write() {} /** * Write of a variable. The write MUST be performed now, as the element * pointer may become invalid after this call. Also, a single write will be * called for each modified element. * * @param id identifier of variable to be written to. * @param e value of variable. */ virtual void single_write(const Id& id, const Element& e) = 0; /** * Read of a variable. The VarRW needs to read a particular element. This * may return NULL indicating ElemNull---i.e. variable not present in THIS * route. * * @return variable requested. * @param id the id of the variable. */ virtual Element* single_read(const Id& id) = 0; /** * Marks the end of writes in case there were any modified fields. */ virtual void end_write() {} private: Element* _trash[16]; unsigned _trashc; const Element* _elems[VAR_MAX]; // Map that caches element read/writes bool _modified[VAR_MAX]; // variable id's that changed bool _did_first_read; PolicyTags* _pt; }; #endif // __POLICY_BACKEND_SINGLE_VARRW_HH__ xorp/policy/backend/instruction.hh0000664000076400007640000001247111540225532017454 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/policy/backend/instruction.hh,v 1.14 2008/10/02 21:58:03 bms Exp $ #ifndef __POLICY_BACKEND_INSTRUCTION_HH__ #define __POLICY_BACKEND_INSTRUCTION_HH__ #include "libxorp/xorp.h" #ifdef HAVE_REGEX_H # include #else // ! HAVE_REGEX_H # ifdef HAVE_PCRE_H # include # endif # ifdef HAVE_PCREPOSIX_H # include # endif #endif // ! HAVE_REGEX_H #include "policy/common/element_base.hh" #include "policy/common/operator_base.hh" #include "policy/common/policy_exception.hh" #include "policy/common/varrw.hh" #include "instr_visitor.hh" #include "instruction_base.hh" /** * @short Push operation. Pushes a single element on the stack. * * The Push instruction owns the element. */ class Push : public NONCOPYABLE, public Instruction { public: /** * Element is owned by Push. * Caller must not delete element. * * @param e element associated with push. */ Push(Element* e) : _elem(e) {} ~Push() { delete _elem; } // semicolon for kdoc INSTR_VISITABLE(); /** * @return element associated with push. */ const Element& elem() const { return *_elem; } private: Element* _elem; }; /** * @short Push a set on the stack. * * The difference with a normal push, is that the operation does does not own * the set element, but only contains the label of the set. The SetManager will * match the set label with the actual element. [It is much like containing a * pointer, and the SetManager deals with dereference]. * * SetManager acts similar to VarRW [almost like a symbol-table]. */ class PushSet : public Instruction { public: /** * @param setid name of the set. */ PushSet(const string& setid) : _setid(setid) {} INSTR_VISITABLE(); /** * @return name of the set. */ const string& setid() const { return _setid; } private: string _setid; }; /** * @short Instruction that exits the current term if top of stack is false. * * This instruction checks if the top of the stack contains an ElemBool. If it * is false, the current term is exited [execution continues at next term]. If * not, normal execution continues. */ class OnFalseExit : public Instruction { public: INSTR_VISITABLE(); }; /** * @short Instruction to read a variable via VarRW interface. */ class Load : public Instruction { public: /** * @param var identifier of variable to load. */ Load(const VarRW::Id& var) : _var(var) {} INSTR_VISITABLE(); /** * @return identifier of variable to read. */ const VarRW::Id& var() const { return _var; } private: VarRW::Id _var; }; /** * @short Instruction to write a variable via VarRW interface. * * Argument is top most element on stack. * 1 element popped from stack. */ class Store : public Instruction { public: /** * @param var identifier of variable to store. */ Store(const VarRW::Id& var) : _var(var) {} INSTR_VISITABLE(); /** * @return identifier of variable to write. */ const VarRW::Id& var() const { return _var; } private: VarRW::Id _var; }; /** * @short Instruction to accept a route. */ class Accept : public Instruction { public: INSTR_VISITABLE(); }; /** * @short Instruction to reject a route. */ class Reject : public Instruction { public: INSTR_VISITABLE(); }; class Next : public Instruction { public: enum Flow { TERM, POLICY }; Next(Flow f) : _flow(f) {} Flow flow() { return _flow; } INSTR_VISITABLE(); private: Flow _flow; }; class Subr : public Instruction { public: Subr(string target) : _target(target) {} string target() { return _target; } INSTR_VISITABLE(); private: string _target; }; /** * @short An N-ary operation. * * Arguments are the N top-most elements of the stack. * Top-most argument is first. Thus elements have to be pushed on stack in * reverse order. * * Operation will pop N elements from the stack. */ class NaryInstr : public NONCOPYABLE, public Instruction { public: /** * Caller must not delete / modify operation. * * @param op operation of this instruction */ NaryInstr(Oper* op) : _op(op) {} ~NaryInstr() { delete _op; } INSTR_VISITABLE(); /** * @return operation associated with this instruction. */ const Oper& op() const { return *_op; } private: Oper* _op; }; #endif // __POLICY_BACKEND_INSTRUCTION_HH__ xorp/policy/backend/policy_filter.hh0000664000076400007640000000537511540225532017744 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/policy/backend/policy_filter.hh,v 1.13 2008/10/02 21:58:04 bms Exp $ #ifndef __POLICY_BACKEND_POLICY_FILTER_HH__ #define __POLICY_BACKEND_POLICY_FILTER_HH__ #include "policy/common/varrw.hh" #include "policy/common/policy_exception.hh" #include "policy_instr.hh" #include "set_manager.hh" #include "filter_base.hh" #include "iv_exec.hh" #include "libxorp/ref_ptr.hh" /** * @short A generic policy filter. * * It may accept/reject/modify any route which supports VarRW. */ class PolicyFilter : public NONCOPYABLE, public FilterBase { public: /** * @short Exception thrown on configuration error. */ class ConfError : public PolicyException { public: ConfError(const char* file, size_t line, const string& init_why = "") : PolicyException("ConfError", file, line, init_why) {} }; PolicyFilter(); ~PolicyFilter(); /** * Configure the filter * * @param str filter configuration. */ void configure(const string& str); /** * Reset the filter. * * Filter becomes a NO-operation -- default action should * be returned everytime an acceptRoute is called. */ void reset(); /** * See if a route is accepted by the filter. * The route may be modified by the filter [through VarRW]. * * @return true if the route is accepted, false otherwise. * @param varrw the VarRW associated with the route being filtered. */ bool acceptRoute(VarRW& varrw); #ifndef XORP_DISABLE_PROFILE void set_profiler_exec(PolicyProfiler* profiler); #endif private: vector* _policies; SetManager _sman; IvExec _exec; #ifndef XORP_DISABLE_PROFILE PolicyProfiler* _profiler_exec; #endif SUBR* _subr; }; typedef ref_ptr RefPf; #endif // __POLICY_BACKEND_POLICY_FILTER_HH__ xorp/policy/backend/instruction_base.hh0000664000076400007640000000324511421137511020442 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/policy/backend/instruction_base.hh,v 1.8 2008/10/02 21:58:03 bms Exp $ #ifndef __POLICY_BACKEND_INSTRUCTION_BASE_HH__ #define __POLICY_BACKEND_INSTRUCTION_BASE_HH__ #include "instr_visitor.hh" /** * @short Base class for an instruction. * * An instruction is an operation a policy filter may execute. Such as pushing * an element on the stack. */ class Instruction { public: virtual ~Instruction() {} /** * Pass the current instruction to the visitor. * * @param v visitor to use on instruction. */ virtual void accept(InstrVisitor& v) = 0; }; // macro ugliness to make instruction visitable [usable by visitor]. #define INSTR_VISITABLE() \ void accept(InstrVisitor& v) { \ v.visit(*this); \ } #endif // __POLICY_BACKEND_INSTRUCTION_BASE_HH__ xorp/policy/backend/term_instr.hh0000664000076400007640000000435711540225532017265 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/policy/backend/term_instr.hh,v 1.10 2008/10/02 21:58:05 bms Exp $ #ifndef __POLICY_BACKEND_TERM_INSTR_HH__ #define __POLICY_BACKEND_TERM_INSTR_HH__ #include "instruction.hh" #include "policy/common/policy_utils.hh" /** * @short Container of instructions. * * A term is an atomic policy unit which may be executed. */ class TermInstr : public NONCOPYABLE { public: /** * @param name term name. * @param instr list of instructions of this term. Caller must not delete. */ TermInstr(const string& name, vector* instr) : _name(name) { _instrc = instr->size(); _instructions = new Instruction*[_instrc]; vector::iterator iter; int i = 0; for (iter = instr->begin(); iter != instr->end(); iter++) { _instructions[i] = *iter; i++; } delete instr; } ~TermInstr() { for (int i = 0; i < _instrc; i++) delete _instructions[i]; delete [] _instructions; } /** * @return the instructions of this term. Caller must not delete. */ Instruction** instructions() { return _instructions; } /** * @return name of the term */ const string& name() { return _name; } int instrc() { return _instrc; } private: string _name; Instruction** _instructions; int _instrc; }; #endif // __POLICY_BACKEND_TERM_INSTR_HH__ xorp/policy/backend/version_filter.cc0000664000076400007640000000516711540225532020117 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "policy/policy_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #ifndef XORP_USE_USTL #include #endif #include "policy/common/elem_filter.hh" #include "version_filter.hh" VersionFilter::VersionFilter(const VarRW::Id& fname) : _filter(new PolicyFilter), _fname(fname) { } VersionFilter::~VersionFilter() { } void VersionFilter::configure(const string& conf) { PolicyFilter* pf = new PolicyFilter(); try { pf->configure(conf); // XXX: programming question: // Since i'm deleting pf... do i need to copy the exception [i.e. not ref to // exception?] } catch(PolicyException e) { delete pf; throw e; } _filter = RefPf(pf); } void VersionFilter::reset() { PolicyFilter* pf = new PolicyFilter(); pf->reset(); _filter = RefPf(pf); } bool VersionFilter::acceptRoute(VarRW& varrw) { // get the associated filter RefPf filter; try { const ElemFilter& ef = dynamic_cast(varrw.read(_fname)); filter = ef.val(); } catch(const bad_cast& exp) { const Element& e = varrw.read(_fname); UNUSED(e); // in case XLOG_FATAL is compiled out. XLOG_FATAL("Reading %d but didn't get ElemFilter! Got %s: (%s)", _fname, e.type(), e.str().c_str()); xorp_throw(PolicyException, "Reading filter but didn't get ElemFilter!"); } // filter exists... run it if(!filter.is_empty()) return filter->acceptRoute(varrw); // assign it latest filter ElemFilter cur(_filter); // XXX for some reason varrw.write(_fname, ElemFilter(_filter)) won't // work... i thought it would create a tmp var on the stack... varrw.write(_fname, cur); XLOG_ASSERT(!_filter.is_empty()); return _filter->acceptRoute(varrw); } xorp/policy/backend/single_varrw.cc0000664000076400007640000001007411421137511017555 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "policy/policy_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "policy/common/elem_null.hh" #include "single_varrw.hh" SingleVarRW::SingleVarRW() : _trashc(0), _did_first_read(false), _pt(NULL) { memset(&_elems, 0, sizeof(_elems)); memset(&_modified, 0, sizeof(_modified)); } SingleVarRW::~SingleVarRW() { for (unsigned i = 0; i < _trashc; i++) delete _trash[i]; } const Element& SingleVarRW::read(const Id& id) { // Maybe there was a write before a read for this variable, if so, just // return the value... no need to bother the client. const Element* e = _elems[id]; // nope... no value found. if(!e) { // if it's the first read, inform the client. if(!_did_first_read) { start_read(); _did_first_read = true; // try again, old clients initialize on start_read() e = _elems[id]; // no luck... need to explicitly read... if (!e) initialize(id, single_read(id)); } // client already had chance to initialize... but apparently didn't... else initialize(id, single_read(id)); // the client may have initialized the variables after the start_read // marker, so try reading again... e = _elems[id]; // out of luck... if(!e) xorp_throw(SingleVarRWErr, "Unable to read variable " + id); } return *e; } void SingleVarRW::write(const Id& id, const Element& e) { // XXX no paranoid checks on what we write _elems[id] = &e; _modified[id] = true; } void SingleVarRW::sync() { bool first = true; // it's faster doing it this way rather than STL set if VAR_MAX is small... for (unsigned i = 0; i < VAR_MAX; i++) { if (!_modified[i]) continue; const Element* e = _elems[i]; XLOG_ASSERT(e); _modified[i] = false; if (first) { // alert derived class we are committing start_write(); first = false; } if (_pt) { switch (i) { case VAR_POLICYTAGS: _pt->set_ptags(*e); continue; case VAR_TAG: _pt->set_tag(*e); continue; } } single_write(i, *e); } // done commiting [so the derived class may sync] end_write(); // clear cache memset(&_elems, 0, sizeof(_elems)); // delete all garbage for (unsigned i = 0; i < _trashc; i++) delete _trash[i]; _trashc = 0; } void SingleVarRW::initialize(const Id& id, Element* e) { // check if we already have a value for a variable. // if so, do nothing. // // Consider clients initializing variables on start_read. // Consider a variable being written to before any reads. In such a case, the // SingleVarRW will already have the correct value for that variable, so we // need to ignore any initialize() called for that variable. if(_elems[id]) { if(e) delete e; return; } // special case nulls [for supported variables, but not present in this // particular case]. if(!e) e = new ElemNull(); _elems[id] = e; // we own the pointers. XLOG_ASSERT(_trashc < sizeof(_trash)/sizeof(Element*)); _trash[_trashc] = e; _trashc++; } void SingleVarRW::initialize(PolicyTags& pt) { _pt = &pt; initialize(VAR_POLICYTAGS, _pt->element()); initialize(VAR_TAG, _pt->element_tag()); } xorp/policy/backend/policy_filters.cc0000664000076400007640000000407311421137511020104 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "libxorp/xorp.h" #include "policy_filters.hh" PolicyFilters::PolicyFilters() { _import_filter = new PolicyFilter(); _export_sm_filter = new PolicyFilter(); _export_filter = new PolicyFilter(); } PolicyFilters::PolicyFilters(FilterBase* im, FilterBase* sm, FilterBase* ex) : _import_filter(im), _export_sm_filter(sm), _export_filter(ex) { } PolicyFilters::~PolicyFilters() { delete _import_filter; delete _export_sm_filter; delete _export_filter; } bool PolicyFilters::run_filter(const uint32_t& ftype, VarRW& varrw) { FilterBase& pf = whichFilter(ftype); return pf.acceptRoute(varrw); } void PolicyFilters::configure(const uint32_t& ftype, const string& conf) { FilterBase& pf = whichFilter(ftype); pf.configure(conf); } void PolicyFilters::reset(const uint32_t& ftype) { FilterBase& pf = whichFilter(ftype); pf.reset(); } FilterBase& PolicyFilters::whichFilter(const uint32_t& ftype) { switch(ftype) { case 1: return *_import_filter; case 2: return *_export_sm_filter; case 4: return *_export_filter; } xorp_throw(PolicyFiltersErr, "Unknown filter: " + policy_utils::to_str(ftype)); } xorp/policy/backend/policy_redist_map.cc0000664000076400007640000000402011421137511020553 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "libxorp/xorp.h" #include "policy_redist_map.hh" #include "policy/common/policy_utils.hh" PolicyRedistMap::PolicyRedistMap() { } PolicyRedistMap::~PolicyRedistMap() { reset(); } void PolicyRedistMap::insert(const string& protocol, const PolicyTags& tags) { PolicyTags* ptags; Map::iterator i = _map.find(protocol); // create new policytags [first time we insert] if(i == _map.end()) { ptags = new PolicyTags(tags); _map[protocol] = ptags; return; } ptags = (*i).second; // just append the tags ptags->insert(tags); } void PolicyRedistMap::reset() { // clear it ALL policy_utils::clear_map(_map); } void PolicyRedistMap::get_protocols(set& out, const PolicyTags& tags) { // XXX: maybe caller would like to control this out.clear(); // go through all our tags. for(Map::iterator i = _map.begin(); i != _map.end(); ++i) { PolicyTags* ptags = (*i).second; // if atleast one tag in the taglist for this protocol is present in the // tags supplied by the user, then the protocol applies. if(ptags->contains_atleast_one(tags)) out.insert((*i).first); } } xorp/policy/backend/policy_filters.hh0000664000076400007640000000613111540225532020116 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/policy/backend/policy_filters.hh,v 1.11 2008/10/02 21:58:04 bms Exp $ #ifndef __POLICY_BACKEND_POLICY_FILTERS_HH__ #define __POLICY_BACKEND_POLICY_FILTERS_HH__ #include "policy_filter.hh" #include "policy/common/filter.hh" #include "policy/common/varrw.hh" #include "policy/common/policy_exception.hh" /** * @short A container for all policy filters a protocol should support. * * Filters which are not used should just not be configured / executed. In the * future an option to disable a filter should be added. Although, not running a * filter is harmless for now [if configured however, state consumes memory]. */ class PolicyFilters : public NONCOPYABLE { public: class PolicyFiltersErr : public PolicyException { public: PolicyFiltersErr(const char* file, size_t line, const string& init_why = "") : PolicyException("PolicyFiltersErr", file, line, init_why) {} }; PolicyFilters(); PolicyFilters(FilterBase* im, FilterBase* sm, FilterBase* ex); virtual ~PolicyFilters(); /** * Run a filter and decide whether route should be accepted. * * May throw an exception on run-time errors. * * @return true if route is accepted, false otherwise. * @param type which filter should be executed. * @param varrw the VarRW associated with the route to be filtered. */ bool run_filter(const uint32_t& type, VarRW& varrw); /** * Configure a filter. * * Throws an exception on error. * * @param type the filter to configure. * @param conf the configuration of the filter. */ void configure(const uint32_t& type, const string& conf); /** * Reset a filter. * * @param type the filter to reset. */ void reset(const uint32_t& type); private: /** * Decide which filter to run based on its type. * * Throws exception if ftype is invalid. * * @return filter to execute. * @param ftype integral filter identifier. */ FilterBase& whichFilter(const uint32_t& ftype); private: FilterBase* _import_filter; FilterBase* _export_sm_filter; FilterBase* _export_filter; }; #endif // __POLICY_BACKEND_POLICY_FILTERS_HH__ xorp/policy/backend/filter_base.hh0000664000076400007640000000361111540224232017342 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/policy/backend/filter_base.hh,v 1.6 2008/10/02 21:58:03 bms Exp $ #ifndef __POLICY_BACKEND_FILTER_BASE_HH__ #define __POLICY_BACKEND_FILTER_BASE_HH__ #include "policy/common/varrw.hh" /** * @short Base class for all policy filters. */ class FilterBase { public: virtual ~FilterBase() {} /** * Configure the filter * * @param str filter configuration. */ virtual void configure(const string& str) = 0; /** * Reset the filter. * * Filter becomes a NO-operation -- default action should * be returned everytime an acceptRoute is called. */ virtual void reset() = 0; /** * See if a route is accepted by the filter. * The route may be modified by the filter [through VarRW]. * * @return true if the route is accepted, false otherwise. * @param varrw the VarRW associated with the route being filtered. */ virtual bool acceptRoute(VarRW& varrw) = 0; }; #endif // __POLICY_BACKEND_FILTER_BASE_HH__ xorp/policy/backend/policy_instr.hh0000664000076400007640000000443711540225532017614 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/policy/backend/policy_instr.hh,v 1.11 2008/10/02 21:58:04 bms Exp $ #ifndef __POLICY_BACKEND_POLICY_INSTR_HH__ #define __POLICY_BACKEND_POLICY_INSTR_HH__ #include "term_instr.hh" /** * @short Container for terms instructions. * * A policy instruction is a list of term instructions. */ class PolicyInstr : public NONCOPYABLE { public: /** * @param name name of the policy. * @param terms terms of the policy. Caller must not delete terms. */ PolicyInstr(const string& name, vector* terms) : _name(name), _trace(false) { int i = 0; vector::iterator iter; _termc = terms->size(); _terms = new TermInstr*[_termc]; for (iter = terms->begin(); iter != terms->end(); ++iter) { _terms[i] = *iter; i++; } delete terms; } ~PolicyInstr() { for (int i = 0; i < _termc; i++) delete _terms[i]; delete [] _terms; } /** * @return terms of this policy. Caller must not delete terms. */ TermInstr** terms() { return _terms; } /** * @return name of the policy. */ const string& name() { return _name; } int termc() const { return _termc; } void set_trace(bool trace) { _trace = trace; } bool trace() const { return _trace; } private: string _name; TermInstr** _terms; int _termc; bool _trace; }; #endif // __POLICY_BACKEND_POLICY_INSTR_HH__ xorp/policy/backend/iv_exec.cc0000664000076400007640000002404411540225532016502 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "policy/policy_module.h" #include "libxorp/xorp.h" #include "policy/common/policy_utils.hh" #include "policy/common/elem_null.hh" #include "policy/common/element.hh" #include "iv_exec.hh" IvExec::IvExec() : _policies(NULL), _policy_count(0), _stack_bottom(NULL), _sman(NULL), _varrw(NULL), _finished(false), _fa(DEFAULT), _trash(NULL), _trashc(0), _trashs(2000) #ifndef XORP_DISABLE_PROFILE , _profiler(NULL) #endif { unsigned ss = 128; _trash = new Element*[_trashs]; _stack_bottom = _stack = new const Element*[ss]; _stackptr = &_stack[0]; _stackptr--; _stackend = &_stack[ss]; } IvExec::~IvExec() { delete [] _policies; clear_trash(); delete [] _trash; delete [] _stack_bottom; } IvExec::FlowAction IvExec::run(VarRW* varrw) { _varrw = varrw; _did_trace = false; _os.clear(); XLOG_ASSERT(_policies); XLOG_ASSERT(_sman); XLOG_ASSERT(_varrw); FlowAction ret = DEFAULT; // clear stack _stackptr = _stack = _stack_bottom; _stackptr--; // execute all policies for (int i = _policy_count-1; i>= 0; --i) { FlowAction fa = runPolicy(*_policies[i]); // if a policy rejected/accepted a route then terminate. if (fa != DEFAULT) { ret = fa; break; } } if (_did_trace) _os << "Outcome of whole filter: " << fa2str(ret) << endl; // important because varrw may hold pointers to trash elements _varrw->sync(); clear_trash(); return ret; } IvExec::FlowAction IvExec::runPolicy(PolicyInstr& pi) { TermInstr** terms = pi.terms(); int termc = pi.termc(); FlowAction outcome = DEFAULT; // create a "stack frame". We do this just so we can "clear" the stack // frame when running terms and keep the asserts. In reality if we get rid // of asserts and clearing of stack, we're fine, but we gotta be bug free. // -sorbo const Element** stack_bottom = _stack; const Element** stack_ptr = _stackptr; _stack = _stackptr + 1; XLOG_ASSERT(_stack < _stackend && _stack >= _stack_bottom); _do_trace = pi.trace(); _varrw->enable_trace(_do_trace); if (_do_trace) _did_trace = true; if (_do_trace) _os << "Running policy: " << pi.name() << endl; // execute terms sequentially _ctr_flow = Next::TERM; // run all terms for (int i = 0; i < termc ; ++i) { FlowAction fa = runTerm(*terms[i]); // if term accepted/rejected route, then terminate. if (fa != DEFAULT) { outcome = fa; break; } if (_ctr_flow == Next::POLICY) break; } if (_do_trace) _os << "Outcome of policy: " << fa2str(outcome) << endl; // restore stack frame _stack = stack_bottom; _stackptr = stack_ptr; return outcome; } IvExec::FlowAction IvExec::runTerm(TermInstr& ti) { // we just started _finished = false; _fa = DEFAULT; // clear stack _stackptr = _stack; _stackptr--; int instrc = ti.instrc(); Instruction** instr = ti.instructions(); if (_do_trace) _os << "Running term: " << ti.name() << endl; // run all instructions for (int i = 0; i < instrc; ++i) { #ifndef XORP_DISABLE_PROFILE if (_profiler) _profiler->start(); #endif (instr[i])->accept(*this); #ifndef XORP_DISABLE_PROFILE if (_profiler) _profiler->stop(); #endif // a flow action occured [accept/reject/default -- exit] if (_finished) break; } if (_do_trace) _os << "Outcome of term: " << fa2str(_fa) << endl; return _fa; } void IvExec::visit(Push& p) { const Element& e = p.elem(); // node owns element [no need to trash] _stackptr++; XLOG_ASSERT(_stackptr < _stackend); *_stackptr = &e; if(_do_trace) _os << "PUSH " << e.type() << " " << e.str() << endl; } void IvExec::visit(PushSet& ps) { string name = ps.setid(); const Element& s = _sman->getSet(name); // set manager owns set [no need to trash] _stackptr++; XLOG_ASSERT(_stackptr < _stackend); *_stackptr = &s; if(_do_trace) _os << "PUSH_SET " << s.type() << " " << name << ": " << s.str() << endl; } void IvExec::visit(OnFalseExit& /* x */) { if (_stackptr < _stack) xorp_throw(RuntimeError, "Got empty stack on ON_FALSE_EXIT"); // we expect a bool at the top. const ElemBool* t = dynamic_cast(*_stackptr); if(!t) { // but maybe it is a ElemNull... in which case its a NOP const Element* e = *_stackptr; if(e->hash() == ElemNull::_hash) { if(_do_trace) _os << "GOT NULL ON TOP OF STACK, GOING TO NEXT TERM" << endl; _finished = true; return; } // if it is anything else, its an error else { xorp_throw(RuntimeError, "Expected bool on top of stack instead: "); } } // we do not pop the element!!! // The reason is, that maybe we want to stick ONFALSE_EXIT's here and there // for optimizations to peek on the stack. Consider a giant AND, we may // stick an ON_FALSEEXIT after earch clause of the and. In that case we do // not wish to pop the element from the stack, as if it is true, we want to // continue computing the AND. // it is false, so lets go to next term if(!t->val()) _finished = true; if(_do_trace) _os << "ONFALSE_EXIT: " << t->str() << endl; } void IvExec::visit(Load& l) { const Element& x = _varrw->read_trace(l.var()); if (_do_trace) _os << "LOAD " << l.var() << ": " << x.str() << endl; // varrw owns element [do not trash] _stackptr++; XLOG_ASSERT(_stackptr < _stackend); *_stackptr = &x; } void IvExec::visit(Store& s) { if (_stackptr < _stack) xorp_throw(RuntimeError, "Stack empty on assign of " + s.var()); const Element* arg = *_stackptr; _stackptr--; XLOG_ASSERT(_stackptr >= (_stack-1)); if (arg->hash() == ElemNull::_hash) { if (_do_trace) _os << "STORE NULL [treated as NOP]" << endl; return; } // we still own the element. // if it had to be trashed, it would have been trashed on creation, so do // NOT trash now. And yes, it likely is an element we do not have to // trash anyway. _varrw->write_trace(s.var(), *arg); if (_do_trace) _os << "STORE " << s.var() << ": " << arg->str() << endl; } void IvExec::visit(Accept& /* a */) { // ok we like the route, so exit all execution _finished = true; _fa = ACCEPT; if(_do_trace) _os << "ACCEPT" << endl; } void IvExec::visit(Next& next) { _finished = true; _ctr_flow = next.flow(); if (_do_trace) { _os << "NEXT "; switch (_ctr_flow) { case Next::TERM: _os << "TERM"; break; case Next::POLICY: _os << "POLICY"; break; } } } void IvExec::visit(Reject& /* r */) { // we don't like it, get out of here. _finished = true; _fa = REJ; if(_do_trace) _os << "REJECT" << endl; } void IvExec::visit(NaryInstr& nary) { unsigned arity = nary.op().arity(); XLOG_ASSERT((_stackptr - arity + 1) >= _stack); // execute the operation Element* r = _disp.run(nary.op(), arity, _stackptr - arity + 1); if (arity) _stackptr -= arity -1; else _stackptr++; // trash the result. // XXX only if it's a new element. if (r->refcount() == 1) { _trash[_trashc] = r; _trashc++; XLOG_ASSERT(_trashc < _trashs); } // store result on stack XLOG_ASSERT(_stackptr < _stackend && _stackptr >= _stack); *_stackptr = r; // output trace if (_do_trace) _os << nary.op().str() << endl; } void IvExec::clear_trash() { for (unsigned i = 0; i< _trashc; i++) delete _trash[i]; _trashc = 0; } string IvExec::fa2str(const FlowAction& fa) { switch(fa) { case ACCEPT: return "Accept"; case REJ: return "Reject"; case DEFAULT: return "Default action"; } return "Unknown"; } void IvExec::set_policies(vector* policies) { if (_policies) { delete [] _policies; _policies = NULL; } // resetting... if (!policies) { _policy_count = 0; return; } _policy_count = policies->size(); _policies = new PolicyInstr*[_policy_count]; vector::iterator iter; unsigned i = 0; for (iter = policies->begin(); iter != policies->end(); ++iter) { _policies[i] = *iter; i++; } } void IvExec::set_set_manager(SetManager* sman) { _sman = sman; } #ifndef XORP_DISABLE_PROFILE void IvExec::set_profiler(PolicyProfiler* pp) { _profiler = pp; } #endif string IvExec::tracelog() { return _os.str(); } void IvExec::visit(Subr& sub) { SUBR::iterator i = _subr->find(sub.target()); XLOG_ASSERT(i != _subr->end()); PolicyInstr* policy = i->second; if (_do_trace) _os << "POLICY " << policy->name() << endl; FlowAction old_fa = _fa; bool old_finished = _finished; FlowAction fa = runPolicy(*policy); _fa = old_fa; _finished = old_finished; bool result = true; switch (fa) { case DEFAULT: case ACCEPT: result = true; break; case REJ: result = false; break; } Element* e = new ElemBool(result); _stackptr++; XLOG_ASSERT(_stackptr < _stackend); *_stackptr = e; _trash[_trashc] = e; _trashc++; XLOG_ASSERT(_trashc < _trashs); } void IvExec::set_subr(SUBR* subr) { _subr = subr; } xorp/policy/backend/policy_profiler.hh0000664000076400007640000000266511421137511020275 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2008-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/policy/backend/policy_profiler.hh,v 1.6 2008/10/02 21:58:04 bms Exp $ #ifndef __POLICY_BACKEND_POLICY_PROFILER_HH__ #define __POLICY_BACKEND_POLICY_PROFILER_HH__ class PolicyProfiler { public: typedef uint64_t TU; static const unsigned int MAX_SAMPLES = 128; PolicyProfiler(); void clear(); void start(); void stop(); unsigned count(); TU sample(unsigned idx); private: TU _samples[MAX_SAMPLES]; unsigned _samplec; bool _stopped; }; #endif // __POLICY_BACKEND_POLICY_PROFILER_HH__ xorp/policy/backend/yacc.yy_policy_backend_parser.cc0000664000076400007640000004666511421137511023053 0ustar greearbgreearb#include #ifndef lint #endif #define YYBYACC 1 #define YYMAJOR 1 #define YYMINOR 9 #define YYLEX yylex() #define YYEMPTY -1 #define yyclearin (yychar=(YYEMPTY)) #define yyerrok (yyerrflag=0) #define YYRECOVERING() (yyerrflag!=0) #if defined(__cplusplus) || __STDC__ static int yygrowstack(void); #else static int yygrowstack(); #endif #define yyparse yy_policy_backend_parserparse #define yylex yy_policy_backend_parserlex #define yyerror yy_policy_backend_parsererror #define yychar yy_policy_backend_parserchar #define yyval yy_policy_backend_parserval #define yylval yy_policy_backend_parserlval #define yydebug yy_policy_backend_parserdebug #define yynerrs yy_policy_backend_parsernerrs #define yyerrflag yy_policy_backend_parsererrflag #define yyss yy_policy_backend_parserss #define yyssp yy_policy_backend_parserssp #define yyvs yy_policy_backend_parservs #define yyvsp yy_policy_backend_parservsp #define yylhs yy_policy_backend_parserlhs #define yylen yy_policy_backend_parserlen #define yydefred yy_policy_backend_parserdefred #define yydgoto yy_policy_backend_parserdgoto #define yysindex yy_policy_backend_parsersindex #define yyrindex yy_policy_backend_parserrindex #define yygindex yy_policy_backend_parsergindex #define yytable yy_policy_backend_parsertable #define yycheck yy_policy_backend_parsercheck #define yyname yy_policy_backend_parsername #define yyrule yy_policy_backend_parserrule #define yysslim yy_policy_backend_parsersslim #define yystacksize yy_policy_backend_parserstacksize #define YYPREFIX "yy_policy_backend_parser" #line 2 "backend.y" /* * yacc -d -p yy_policy_backend_parser -o yacc.yy_policy_backend_parser.cc backend.y */ #include "libxorp/xorp.h" #include "policy/common/varrw.hh" #include "policy/common/element_factory.hh" #include "policy/common/operator.hh" #include "policy_backend_parser.hh" #include "instruction.hh" #include "term_instr.hh" #include "policy_instr.hh" extern int yylex(void); extern void yyerror(const char*); using namespace policy_backend_parser; static ElementFactory _ef; #line 26 "backend.y" typedef union { char* c_str; PolicyInstr* c_pi; } YYSTYPE; #line 78 "yacc.yy_policy_backend_parser.cc" #define YYERRCODE 256 #define YY_ARG 257 #define YY_NEWLINE 258 #define YY_BLANK 259 #define YY_POLICY_START 260 #define YY_POLICY_END 261 #define YY_TERM_START 262 #define YY_TERM_END 263 #define YY_PUSH 264 #define YY_PUSH_SET 265 #define YY_EQ 266 #define YY_NE 267 #define YY_LT 268 #define YY_GT 269 #define YY_LE 270 #define YY_GE 271 #define YY_NOT 272 #define YY_AND 273 #define YY_OR 274 #define YY_XOR 275 #define YY_HEAD 276 #define YY_CTR 277 #define YY_NE_INT 278 #define YY_ADD 279 #define YY_SUB 280 #define YY_MUL 281 #define YY_ONFALSE_EXIT 282 #define YY_REGEX 283 #define YY_LOAD 284 #define YY_STORE 285 #define YY_ACCEPT 286 #define YY_REJECT 287 #define YY_SET 288 #define YY_NEXT 289 #define YY_POLICY 290 #define YY_SUBR_START 291 #define YY_SUBR_END 292 #define YY_TERM 293 const short yy_policy_backend_parserlhs[] = { -1, 0, 0, 0, 0, 3, 2, 4, 4, 1, 5, 5, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, }; const short yy_policy_backend_parserlen[] = { 2, 2, 2, 2, 0, 5, 5, 2, 0, 6, 7, 0, 3, 0, 3, 2, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, }; const short yy_policy_backend_parserdefred[] = { 4, 0, 0, 0, 0, 1, 2, 3, 0, 0, 8, 11, 0, 0, 0, 0, 0, 7, 0, 0, 5, 6, 9, 0, 13, 0, 0, 0, 0, 21, 22, 23, 24, 25, 26, 27, 28, 30, 29, 34, 35, 36, 31, 32, 33, 16, 37, 0, 0, 19, 20, 0, 0, 0, 10, 0, 15, 17, 18, 38, 39, 40, 12, 14, }; const short yy_policy_backend_parserdgoto[] = { 1, 5, 6, 7, 13, 14, 25, 53, }; const short yy_policy_backend_parsersindex[] = { 0, -258, -254, -250, -249, 0, 0, 0, -248, -246, 0, 0, -245, -260, -256, -244, -243, 0, -242, -240, 0, 0, 0, -239, 0, -229, -238, -236, -235, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -234, -233, 0, 0, -289, -232, -231, 0, -228, 0, 0, 0, 0, 0, 0, 0, 0, }; const short yy_policy_backend_parserrindex[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; const short yy_policy_backend_parsergindex[] = { 0, -5, 0, 0, 0, 0, 0, 0, }; #define YYTABLESIZE 61 const short yy_policy_backend_parsertable[] = { 2, 59, 2, 8, 60, 18, 19, 9, 17, 10, 11, 12, 15, 0, 20, 21, 22, 23, 0, 24, 54, 55, 56, 57, 58, 61, 0, 62, 0, 63, 3, 0, 16, 4, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 0, 51, 52, }; const short yy_policy_backend_parsercheck[] = { 260, 290, 260, 257, 293, 261, 262, 257, 13, 258, 258, 257, 257, -1, 258, 258, 258, 257, -1, 258, 258, 257, 257, 257, 257, 257, -1, 258, -1, 257, 288, -1, 292, 291, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, -1, 289, 290, }; #define YYFINAL 1 #ifndef YYDEBUG #define YYDEBUG 0 #endif #define YYMAXTOKEN 293 #if YYDEBUG const char * const yy_policy_backend_parsername[] = { "end-of-file",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,"YY_ARG","YY_NEWLINE","YY_BLANK", "YY_POLICY_START","YY_POLICY_END","YY_TERM_START","YY_TERM_END","YY_PUSH", "YY_PUSH_SET","YY_EQ","YY_NE","YY_LT","YY_GT","YY_LE","YY_GE","YY_NOT","YY_AND", "YY_OR","YY_XOR","YY_HEAD","YY_CTR","YY_NE_INT","YY_ADD","YY_SUB","YY_MUL", "YY_ONFALSE_EXIT","YY_REGEX","YY_LOAD","YY_STORE","YY_ACCEPT","YY_REJECT", "YY_SET","YY_NEXT","YY_POLICY","YY_SUBR_START","YY_SUBR_END","YY_TERM", }; const char * const yy_policy_backend_parserrule[] = { "$accept : program", "program : program policy", "program : program subroutine", "program : program set", "program :", "set : YY_SET YY_ARG YY_ARG YY_ARG YY_NEWLINE", "subroutine : YY_SUBR_START YY_NEWLINE policies YY_SUBR_END YY_NEWLINE", "policies : policies policy", "policies :", "policy : YY_POLICY_START YY_ARG YY_NEWLINE terms YY_POLICY_END YY_NEWLINE", "terms : terms YY_TERM_START YY_ARG YY_NEWLINE statements YY_TERM_END YY_NEWLINE", "terms :", "statements : statements statement YY_NEWLINE", "statements :", "statement : YY_PUSH YY_ARG YY_ARG", "statement : YY_PUSH_SET YY_ARG", "statement : YY_ONFALSE_EXIT", "statement : YY_LOAD YY_ARG", "statement : YY_STORE YY_ARG", "statement : YY_ACCEPT", "statement : YY_REJECT", "statement : YY_EQ", "statement : YY_NE", "statement : YY_LT", "statement : YY_GT", "statement : YY_LE", "statement : YY_GE", "statement : YY_NOT", "statement : YY_AND", "statement : YY_XOR", "statement : YY_OR", "statement : YY_ADD", "statement : YY_SUB", "statement : YY_MUL", "statement : YY_HEAD", "statement : YY_CTR", "statement : YY_NE_INT", "statement : YY_REGEX", "statement : YY_NEXT YY_POLICY", "statement : YY_NEXT YY_TERM", "statement : YY_POLICY YY_ARG", }; #endif #if YYDEBUG #include #endif #ifdef YYSTACKSIZE #undef YYMAXDEPTH #define YYMAXDEPTH YYSTACKSIZE #else #ifdef YYMAXDEPTH #define YYSTACKSIZE YYMAXDEPTH #else #define YYSTACKSIZE 10000 #define YYMAXDEPTH 10000 #endif #endif #define YYINITSTACKSIZE 200 int yydebug; int yynerrs; int yyerrflag; int yychar; short *yyssp; YYSTYPE *yyvsp; YYSTYPE yyval; YYSTYPE yylval; short *yyss; short *yysslim; YYSTYPE *yyvs; int yystacksize; /* allocate initial stack or double stack size, up to YYMAXDEPTH */ static int yygrowstack() { int newsize, i; short *newss; YYSTYPE *newvs; if ((newsize = yystacksize) == 0) newsize = YYINITSTACKSIZE; else if (newsize >= YYMAXDEPTH) return -1; else if ((newsize *= 2) > YYMAXDEPTH) newsize = YYMAXDEPTH; i = yyssp - yyss; newss = yyss ? (short *)realloc(yyss, newsize * sizeof *newss) : (short *)malloc(newsize * sizeof *newss); if (newss == NULL) return -1; yyss = newss; yyssp = newss + i; newvs = yyvs ? (YYSTYPE *)realloc(yyvs, newsize * sizeof *newvs) : (YYSTYPE *)malloc(newsize * sizeof *newvs); if (newvs == NULL) return -1; yyvs = newvs; yyvsp = newvs + i; yystacksize = newsize; yysslim = yyss + newsize - 1; return 0; } #define YYABORT goto yyabort #define YYREJECT goto yyabort #define YYACCEPT goto yyaccept #define YYERROR goto yyerrlab #ifndef YYPARSE_PARAM #if defined(__cplusplus) || __STDC__ #define YYPARSE_PARAM_ARG void #define YYPARSE_PARAM_DECL #else /* ! ANSI-C/C++ */ #define YYPARSE_PARAM_ARG #define YYPARSE_PARAM_DECL #endif /* ANSI-C/C++ */ #else /* YYPARSE_PARAM */ #ifndef YYPARSE_PARAM_TYPE #define YYPARSE_PARAM_TYPE void * #endif #if defined(__cplusplus) || __STDC__ #define YYPARSE_PARAM_ARG YYPARSE_PARAM_TYPE YYPARSE_PARAM #define YYPARSE_PARAM_DECL #else /* ! ANSI-C/C++ */ #define YYPARSE_PARAM_ARG YYPARSE_PARAM #define YYPARSE_PARAM_DECL YYPARSE_PARAM_TYPE YYPARSE_PARAM; #endif /* ANSI-C/C++ */ #endif /* ! YYPARSE_PARAM */ int yyparse (YYPARSE_PARAM_ARG) YYPARSE_PARAM_DECL { int yym, yyn, yystate; #if YYDEBUG const char *yys; if ((yys = getenv("YYDEBUG"))) { yyn = *yys; if (yyn >= '0' && yyn <= '9') yydebug = yyn - '0'; } #endif yynerrs = 0; yyerrflag = 0; yychar = (-1); if (yyss == NULL && yygrowstack()) goto yyoverflow; yyssp = yyss; yyvsp = yyvs; *yyssp = yystate = 0; yyloop: if ((yyn = yydefred[yystate])) goto yyreduce; if (yychar < 0) { if ((yychar = yylex()) < 0) yychar = 0; #if YYDEBUG if (yydebug) { yys = 0; if (yychar <= YYMAXTOKEN) yys = yyname[yychar]; if (!yys) yys = "illegal-symbol"; printf("%sdebug: state %d, reading %d (%s)\n", YYPREFIX, yystate, yychar, yys); } #endif } if ((yyn = yysindex[yystate]) && (yyn += yychar) >= 0 && yyn <= YYTABLESIZE && yycheck[yyn] == yychar) { #if YYDEBUG if (yydebug) printf("%sdebug: state %d, shifting to state %d\n", YYPREFIX, yystate, yytable[yyn]); #endif if (yyssp >= yysslim && yygrowstack()) { goto yyoverflow; } *++yyssp = yystate = yytable[yyn]; *++yyvsp = yylval; yychar = (-1); if (yyerrflag > 0) --yyerrflag; goto yyloop; } if ((yyn = yyrindex[yystate]) && (yyn += yychar) >= 0 && yyn <= YYTABLESIZE && yycheck[yyn] == yychar) { yyn = yytable[yyn]; goto yyreduce; } if (yyerrflag) goto yyinrecovery; #if defined(lint) || defined(__GNUC__) goto yynewerror; #endif yynewerror: yyerror("syntax error"); #if defined(lint) || defined(__GNUC__) goto yyerrlab; #endif yyerrlab: ++yynerrs; yyinrecovery: if (yyerrflag < 3) { yyerrflag = 3; for (;;) { if ((yyn = yysindex[*yyssp]) && (yyn += YYERRCODE) >= 0 && yyn <= YYTABLESIZE && yycheck[yyn] == YYERRCODE) { #if YYDEBUG if (yydebug) printf("%sdebug: state %d, error recovery shifting\ to state %d\n", YYPREFIX, *yyssp, yytable[yyn]); #endif if (yyssp >= yysslim && yygrowstack()) { goto yyoverflow; } *++yyssp = yystate = yytable[yyn]; *++yyvsp = yylval; goto yyloop; } else { #if YYDEBUG if (yydebug) printf("%sdebug: error recovery discarding state %d\n", YYPREFIX, *yyssp); #endif if (yyssp <= yyss) goto yyabort; --yyssp; --yyvsp; } } } else { if (yychar == 0) goto yyabort; #if YYDEBUG if (yydebug) { yys = 0; if (yychar <= YYMAXTOKEN) yys = yyname[yychar]; if (!yys) yys = "illegal-symbol"; printf("%sdebug: state %d, error recovery discards token %d (%s)\n", YYPREFIX, yystate, yychar, yys); } #endif yychar = (-1); goto yyloop; } yyreduce: #if YYDEBUG if (yydebug) printf("%sdebug: state %d, reducing by rule %d (%s)\n", YYPREFIX, yystate, yyn, yyrule[yyn]); #endif yym = yylen[yyn]; yyval = yyvsp[1-yym]; switch (yyn) { case 1: #line 48 "backend.y" { _yy_policies->push_back(yyvsp[0].c_pi); } break; case 5: #line 56 "backend.y" { /* XXX: doesn't delete old*/ (*_yy_sets)[yyvsp[-2].c_str] = _ef.create(yyvsp[-3].c_str, yyvsp[-1].c_str); free(yyvsp[-3].c_str); free(yyvsp[-2].c_str); free(yyvsp[-1].c_str); } break; case 7: #line 68 "backend.y" { (*_yy_subr)[yyvsp[0].c_pi->name()] = yyvsp[0].c_pi; } break; case 9: #line 73 "backend.y" { PolicyInstr* pi = new PolicyInstr(yyvsp[-4].c_str,_yy_terms); pi->set_trace(_yy_trace); _yy_trace = false; _yy_terms = new vector(); free(yyvsp[-4].c_str); yyval.c_pi = pi; } break; case 10: #line 88 "backend.y" { TermInstr* ti = new TermInstr(yyvsp[-4].c_str,_yy_instructions); _yy_instructions = new vector(); _yy_terms->push_back(ti); free(yyvsp[-4].c_str); } break; case 14: #line 105 "backend.y" { Instruction* i = new Push(_ef.create(yyvsp[-1].c_str,yyvsp[0].c_str)); _yy_instructions->push_back(i); free(yyvsp[-1].c_str); free(yyvsp[0].c_str); } break; case 15: #line 110 "backend.y" { _yy_instructions->push_back(new PushSet(yyvsp[0].c_str)); free(yyvsp[0].c_str); } break; case 16: #line 115 "backend.y" { _yy_instructions->push_back(new OnFalseExit()); } break; case 17: #line 119 "backend.y" { char* err = NULL; bool is_error = false; VarRW::Id id = strtoul(yyvsp[0].c_str, &err, 10); if ((err != NULL) && (*err != '\0')) is_error = true; free(yyvsp[0].c_str); if (is_error) { yyerror("Need numeric var ID"); YYERROR; } _yy_instructions->push_back(new Load(id)); } break; case 18: #line 133 "backend.y" { char* err = NULL; bool is_error = false; VarRW::Id id = strtoul(yyvsp[0].c_str, &err, 10); if ((err != NULL) && (*err != '\0')) is_error = true; free(yyvsp[0].c_str); if (is_error) { yyerror("Need numeric var ID"); YYERROR; } if (id == VarRW::VAR_TRACE) _yy_trace = true; _yy_instructions->push_back(new Store(id)); } break; case 19: #line 154 "backend.y" { _yy_instructions->push_back(new Accept()); } break; case 20: #line 155 "backend.y" { _yy_instructions->push_back(new Reject()); } break; case 21: #line 157 "backend.y" { _yy_instructions->push_back(new NaryInstr(new OpEq)); } break; case 22: #line 158 "backend.y" { _yy_instructions->push_back(new NaryInstr(new OpNe)); } break; case 23: #line 159 "backend.y" { _yy_instructions->push_back(new NaryInstr(new OpLt)); } break; case 24: #line 160 "backend.y" { _yy_instructions->push_back(new NaryInstr(new OpGt)); } break; case 25: #line 161 "backend.y" { _yy_instructions->push_back(new NaryInstr(new OpLe)); } break; case 26: #line 162 "backend.y" { _yy_instructions->push_back(new NaryInstr(new OpGe)); } break; case 27: #line 164 "backend.y" { _yy_instructions->push_back(new NaryInstr(new OpNot)); } break; case 28: #line 165 "backend.y" { _yy_instructions->push_back(new NaryInstr(new OpAnd)); } break; case 29: #line 166 "backend.y" { _yy_instructions->push_back(new NaryInstr(new OpXor)); } break; case 30: #line 167 "backend.y" { _yy_instructions->push_back(new NaryInstr(new OpOr)); } break; case 31: #line 169 "backend.y" { _yy_instructions->push_back(new NaryInstr(new OpAdd)); } break; case 32: #line 170 "backend.y" { _yy_instructions->push_back(new NaryInstr(new OpSub)); } break; case 33: #line 171 "backend.y" { _yy_instructions->push_back(new NaryInstr(new OpMul)); } break; case 34: #line 172 "backend.y" { _yy_instructions->push_back(new NaryInstr(new OpHead));} break; case 35: #line 173 "backend.y" { _yy_instructions->push_back(new NaryInstr(new OpCtr));} break; case 36: #line 174 "backend.y" { _yy_instructions->push_back(new NaryInstr(new OpNEInt));} break; case 37: #line 175 "backend.y" { _yy_instructions->push_back(new NaryInstr(new OpRegex));} break; case 38: #line 177 "backend.y" { _yy_instructions->push_back(new Next(Next::POLICY)); } break; case 39: #line 179 "backend.y" { _yy_instructions->push_back(new Next(Next::TERM)); } break; case 40: #line 181 "backend.y" { _yy_instructions->push_back(new Subr(yyvsp[0].c_str)); free(yyvsp[0].c_str); } break; #line 655 "yacc.yy_policy_backend_parser.cc" } yyssp -= yym; yystate = *yyssp; yyvsp -= yym; yym = yylhs[yyn]; if (yystate == 0 && yym == 0) { #if YYDEBUG if (yydebug) printf("%sdebug: after reduction, shifting from state 0 to\ state %d\n", YYPREFIX, YYFINAL); #endif yystate = YYFINAL; *++yyssp = YYFINAL; *++yyvsp = yyval; if (yychar < 0) { if ((yychar = yylex()) < 0) yychar = 0; #if YYDEBUG if (yydebug) { yys = 0; if (yychar <= YYMAXTOKEN) yys = yyname[yychar]; if (!yys) yys = "illegal-symbol"; printf("%sdebug: state %d, reading %d (%s)\n", YYPREFIX, YYFINAL, yychar, yys); } #endif } if (yychar == 0) goto yyaccept; goto yyloop; } if ((yyn = yygindex[yym]) && (yyn += yystate) >= 0 && yyn <= YYTABLESIZE && yycheck[yyn] == yystate) yystate = yytable[yyn]; else yystate = yydgoto[yym]; #if YYDEBUG if (yydebug) printf("%sdebug: after reduction, shifting from state %d \ to state %d\n", YYPREFIX, *yyssp, yystate); #endif if (yyssp >= yysslim && yygrowstack()) { goto yyoverflow; } *++yyssp = yystate; *++yyvsp = yyval; goto yyloop; yyoverflow: yyerror("yacc stack overflow"); yyabort: return (1); yyaccept: return (0); } xorp/policy/backend/version_filters.cc0000664000076400007640000000224311421137511020267 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "libxorp/xorp.h" #include "version_filters.hh" #include "version_filter.hh" VersionFilters::VersionFilters() : PolicyFilters( new VersionFilter(VarRW::VAR_FILTER_IM), new VersionFilter(VarRW::VAR_FILTER_SM), new VersionFilter(VarRW::VAR_FILTER_EX)) { } xorp/policy/backend/policy_redist_map.hh0000664000076400007640000000451711540225532020603 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/policy/backend/policy_redist_map.hh,v 1.8 2008/10/02 21:58:04 bms Exp $ #ifndef __POLICY_BACKEND_POLICY_REDIST_MAP_HH__ #define __POLICY_BACKEND_POLICY_REDIST_MAP_HH__ #include "policytags.hh" /** * @short A Map between policytags and where the route should be redistributed. * * This map normally resides in the RIB. As routes pass through the rib, their * policytags need to be analyzed. According to these tags, the route must be * sent to various routing protocols to enable export policies. */ class PolicyRedistMap : public NONCOPYABLE { public: PolicyRedistMap(); ~PolicyRedistMap(); /** * Configure redistribution to a protcol for these tags. * * @param protocol destination protocol for these tags. * @param tags policytags which need to be redistributed to the protocol. */ void insert(const string& protocol, const PolicyTags& tags); /** * Reset the redistribution map */ void reset(); /** * Obtain which protocols the route containing these tags should be sent to. * * @param out will be filled with protocols route should be sent to. * @param tags policytags that need to be resolved. */ void get_protocols(set& out, const PolicyTags& tags); private: // XXX: this should be the other way around for faster lookups typedef map Map; Map _map; }; #endif // __POLICY_BACKEND_POLICY_REDIST_MAP_HH__ xorp/policy/backend/SConscript0000664000076400007640000000352711540225532016566 0ustar greearbgreearb# Copyright (c) 2009-2011 XORP, Inc and Others # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # $XORP$ import os Import('env') env = env.Clone() is_shared = env.has_key('SHAREDLIBS') env.AppendUnique(CPPPATH = [ '#' ]) libpbesrcs = [ 'iv_exec.cc', 'policy_filter.cc', 'policy_filters.cc', 'policy_redist_map.cc', 'policytags.cc', 'set_manager.cc', 'single_varrw.cc', 'version_filter.cc', 'version_filters.cc', 'lex.yy_policy_backend_parser.cc', 'yacc.yy_policy_backend_parser.cc' ] if not (env.has_key('disable_profile') and env['disable_profile']): libpbesrcs.append('policy_profiler.cc') if is_shared: libpbe = env.SharedLibrary(target = 'libxorp_policy_backend', source = libpbesrcs) if env['rtld_origin']: for obj in libpbe: env.AddPostAction(libpbe, env.Symlink(obj.abspath, os.path.join(env['xorp_alias_libdir'], str(obj)))) env.Alias('install', env.InstallLibrary(env['xorp_libdir'], libpbe)) else: libpbe = env.StaticLibrary(target = 'libxorp_policy_backend', source = libpbesrcs) Default(libpbe) xorp/policy/backend/set_manager.cc0000664000076400007640000000306211421137511017337 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "libxorp/xorp.h" #include "set_manager.hh" #include "policy/common/policy_utils.hh" SetManager::SetManager() : _sets(NULL) { } SetManager::~SetManager() { clear(); } const Element& SetManager::getSet(const string& setid) const { if(!_sets) xorp_throw(SetNotFound, "No sets initialized"); SetMap::iterator i = _sets->find(setid); if(i == _sets->end()) xorp_throw(SetNotFound, "Set not found: " + setid); Element* e = (*i).second; return *e; } void SetManager::replace_sets(SetMap* sets) { clear(); _sets = sets; } void SetManager::clear() { if(_sets) { policy_utils::clear_map(*_sets); delete _sets; _sets = NULL; } } xorp/policy/backend/lex.yy_policy_backend_parser.cc0000664000076400007640000015610011421137511022706 0ustar greearbgreearb#line 2 "lex.yy_policy_backend_parser.cc" #line 4 "lex.yy_policy_backend_parser.cc" #define YY_INT_ALIGNED short int /* A lexical scanner generated by flex */ #define FLEX_SCANNER #define YY_FLEX_MAJOR_VERSION 2 #define YY_FLEX_MINOR_VERSION 5 #define YY_FLEX_SUBMINOR_VERSION 33 #if YY_FLEX_SUBMINOR_VERSION > 0 #define FLEX_BETA #endif /* First, we deal with platform-specific or compiler-specific issues. */ /* begin standard C headers. */ #include #include #include #include /* end standard C headers. */ /* flex integer type definitions */ #ifndef FLEXINT_H #define FLEXINT_H /* C99 systems have . Non-C99 systems may or may not. */ #if __STDC_VERSION__ >= 199901L /* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, * if you want the limit (max/min) macros for int types. */ #ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS 1 #endif #include typedef int8_t flex_int8_t; typedef uint8_t flex_uint8_t; typedef int16_t flex_int16_t; typedef uint16_t flex_uint16_t; typedef int32_t flex_int32_t; typedef uint32_t flex_uint32_t; #else typedef signed char flex_int8_t; typedef short int flex_int16_t; typedef int flex_int32_t; typedef unsigned char flex_uint8_t; typedef unsigned short int flex_uint16_t; typedef unsigned int flex_uint32_t; #endif /* ! C99 */ /* Limits of integral types. */ #ifndef INT8_MIN #define INT8_MIN (-128) #endif #ifndef INT16_MIN #define INT16_MIN (-32767-1) #endif #ifndef INT32_MIN #define INT32_MIN (-2147483647-1) #endif #ifndef INT8_MAX #define INT8_MAX (127) #endif #ifndef INT16_MAX #define INT16_MAX (32767) #endif #ifndef INT32_MAX #define INT32_MAX (2147483647) #endif #ifndef UINT8_MAX #define UINT8_MAX (255U) #endif #ifndef UINT16_MAX #define UINT16_MAX (65535U) #endif #ifndef UINT32_MAX #define UINT32_MAX (4294967295U) #endif #endif /* ! FLEXINT_H */ #ifdef __cplusplus /* The "const" storage-class-modifier is valid. */ #define YY_USE_CONST #else /* ! __cplusplus */ #if __STDC__ #define YY_USE_CONST #endif /* __STDC__ */ #endif /* ! __cplusplus */ #ifdef YY_USE_CONST #define yyconst const #else #define yyconst #endif /* Returned upon end-of-file. */ #define YY_NULL 0 /* Promotes a possibly negative, possibly signed char to an unsigned * integer for use as an array index. If the signed char is negative, * we want to instead treat it as an 8-bit unsigned char, hence the * double cast. */ #define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) /* Enter a start condition. This macro really ought to take a parameter, * but we do it the disgusting crufty way forced on us by the ()-less * definition of BEGIN. */ #define BEGIN (yy_start) = 1 + 2 * /* Translate the current start state into a value that can be later handed * to BEGIN to return to the state. The YYSTATE alias is for lex * compatibility. */ #define YY_START (((yy_start) - 1) / 2) #define YYSTATE YY_START /* Action number for EOF rule of a given start state. */ #define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) /* Special action meaning "start processing a new file". */ #define YY_NEW_FILE yy_policy_backend_parserrestart(yy_policy_backend_parserin ) #define YY_END_OF_BUFFER_CHAR 0 /* Size of default input buffer. */ #ifndef YY_BUF_SIZE #define YY_BUF_SIZE 16384 #endif /* The state buf must be large enough to hold one state per character in the main buffer. */ #define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) #ifndef YY_TYPEDEF_YY_BUFFER_STATE #define YY_TYPEDEF_YY_BUFFER_STATE typedef struct yy_buffer_state *YY_BUFFER_STATE; #endif extern int yy_policy_backend_parserleng; extern FILE *yy_policy_backend_parserin, *yy_policy_backend_parserout; #define EOB_ACT_CONTINUE_SCAN 0 #define EOB_ACT_END_OF_FILE 1 #define EOB_ACT_LAST_MATCH 2 #define YY_LESS_LINENO(n) /* Return all but the first "n" matched characters back to the input stream. */ #define yyless(n) \ do \ { \ /* Undo effects of setting up yy_policy_backend_parsertext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ *yy_cp = (yy_hold_char); \ YY_RESTORE_YY_MORE_OFFSET \ (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ YY_DO_BEFORE_ACTION; /* set up yy_policy_backend_parsertext again */ \ } \ while ( 0 ) #define unput(c) yyunput( c, (yytext_ptr) ) /* The following is because we cannot portably get our hands on size_t * (without autoconf's help, which isn't available because we want * flex-generated scanners to compile on their own). */ #ifndef YY_TYPEDEF_YY_SIZE_T #define YY_TYPEDEF_YY_SIZE_T typedef unsigned int yy_size_t; #endif #ifndef YY_STRUCT_YY_BUFFER_STATE #define YY_STRUCT_YY_BUFFER_STATE struct yy_buffer_state { FILE *yy_input_file; char *yy_ch_buf; /* input buffer */ char *yy_buf_pos; /* current position in input buffer */ /* Size of input buffer in bytes, not including room for EOB * characters. */ yy_size_t yy_buf_size; /* Number of characters read into yy_ch_buf, not including EOB * characters. */ int yy_n_chars; /* Whether we "own" the buffer - i.e., we know we created it, * and can realloc() it to grow it, and should free() it to * delete it. */ int yy_is_our_buffer; /* Whether this is an "interactive" input source; if so, and * if we're using stdio for input, then we want to use getc() * instead of fread(), to make sure we stop fetching input after * each newline. */ int yy_is_interactive; /* Whether we're considered to be at the beginning of a line. * If so, '^' rules will be active on the next match, otherwise * not. */ int yy_at_bol; int yy_bs_lineno; /**< The line count. */ int yy_bs_column; /**< The column count. */ /* Whether to try to fill the input buffer when we reach the * end of it. */ int yy_fill_buffer; int yy_buffer_status; #define YY_BUFFER_NEW 0 #define YY_BUFFER_NORMAL 1 /* When an EOF's been seen but there's still some text to process * then we mark the buffer as YY_EOF_PENDING, to indicate that we * shouldn't try reading from the input source any more. We might * still have a bunch of tokens to match, though, because of * possible backing-up. * * When we actually see the EOF, we change the status to "new" * (via yy_policy_backend_parserrestart()), so that the user can continue scanning by * just pointing yy_policy_backend_parserin at a new input file. */ #define YY_BUFFER_EOF_PENDING 2 }; #endif /* !YY_STRUCT_YY_BUFFER_STATE */ /* Stack of input buffers. */ static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */ static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */ static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */ /* We provide macros for accessing buffer states in case in the * future we want to put the buffer states in a more general * "scanner state". * * Returns the top of the stack, or NULL. */ #define YY_CURRENT_BUFFER ( (yy_buffer_stack) \ ? (yy_buffer_stack)[(yy_buffer_stack_top)] \ : NULL) /* Same as previous macro, but useful when we know that the buffer stack is not * NULL or when we need an lvalue. For internal use only. */ #define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)] /* yy_hold_char holds the character lost when yy_policy_backend_parsertext is formed. */ static char yy_hold_char; static int yy_n_chars; /* number of characters read into yy_ch_buf */ int yy_policy_backend_parserleng; /* Points to current character in buffer. */ static char *yy_c_buf_p = (char *) 0; static int yy_init = 0; /* whether we need to initialize */ static int yy_start = 0; /* start state number */ /* Flag which is used to allow yy_policy_backend_parserwrap()'s to do buffer switches * instead of setting up a fresh yy_policy_backend_parserin. A bit of a hack ... */ static int yy_did_buffer_switch_on_eof; void yy_policy_backend_parserrestart (FILE *input_file ); void yy_policy_backend_parser_switch_to_buffer (YY_BUFFER_STATE new_buffer ); YY_BUFFER_STATE yy_policy_backend_parser_create_buffer (FILE *file,int size ); void yy_policy_backend_parser_delete_buffer (YY_BUFFER_STATE b ); void yy_policy_backend_parser_flush_buffer (YY_BUFFER_STATE b ); void yy_policy_backend_parserpush_buffer_state (YY_BUFFER_STATE new_buffer ); void yy_policy_backend_parserpop_buffer_state (void ); static void yy_policy_backend_parserensure_buffer_stack (void ); static void yy_policy_backend_parser_load_buffer_state (void ); static void yy_policy_backend_parser_init_buffer (YY_BUFFER_STATE b,FILE *file ); #define YY_FLUSH_BUFFER yy_policy_backend_parser_flush_buffer(YY_CURRENT_BUFFER ) YY_BUFFER_STATE yy_policy_backend_parser_scan_buffer (char *base,yy_size_t size ); YY_BUFFER_STATE yy_policy_backend_parser_scan_string (yyconst char *yy_str ); YY_BUFFER_STATE yy_policy_backend_parser_scan_bytes (yyconst char *bytes,int len ); void *yy_policy_backend_parseralloc (yy_size_t ); void *yy_policy_backend_parserrealloc (void *,yy_size_t ); void yy_policy_backend_parserfree (void * ); #define yy_new_buffer yy_policy_backend_parser_create_buffer #define yy_set_interactive(is_interactive) \ { \ if ( ! YY_CURRENT_BUFFER ){ \ yy_policy_backend_parserensure_buffer_stack (); \ YY_CURRENT_BUFFER_LVALUE = \ yy_policy_backend_parser_create_buffer(yy_policy_backend_parserin,YY_BUF_SIZE ); \ } \ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ } #define yy_set_bol(at_bol) \ { \ if ( ! YY_CURRENT_BUFFER ){\ yy_policy_backend_parserensure_buffer_stack (); \ YY_CURRENT_BUFFER_LVALUE = \ yy_policy_backend_parser_create_buffer(yy_policy_backend_parserin,YY_BUF_SIZE ); \ } \ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ } #define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) /* Begin user sect3 */ #define yy_policy_backend_parserwrap(n) 1 #define YY_SKIP_YYWRAP typedef unsigned char YY_CHAR; FILE *yy_policy_backend_parserin = (FILE *) 0, *yy_policy_backend_parserout = (FILE *) 0; typedef int yy_state_type; extern int yy_policy_backend_parserlineno; int yy_policy_backend_parserlineno = 1; extern char *yy_policy_backend_parsertext; #define yytext_ptr yy_policy_backend_parsertext static yy_state_type yy_get_previous_state (void ); static yy_state_type yy_try_NUL_trans (yy_state_type current_state ); static int yy_get_next_buffer (void ); static void yy_fatal_error (yyconst char msg[] ); /* Done after the current pattern has been matched and before the * corresponding action - sets up yy_policy_backend_parsertext. */ #define YY_DO_BEFORE_ACTION \ (yytext_ptr) = yy_bp; \ yy_policy_backend_parserleng = (size_t) (yy_cp - yy_bp); \ (yy_hold_char) = *yy_cp; \ *yy_cp = '\0'; \ (yy_c_buf_p) = yy_cp; #define YY_NUM_RULES 41 #define YY_END_OF_BUFFER 42 /* This struct is not used in this scanner, but its presence is necessary. */ struct yy_trans_info { flex_int32_t yy_verify; flex_int32_t yy_nxt; }; static yyconst flex_int16_t yy_accept[151] = { 0, 0, 0, 0, 0, 42, 37, 36, 35, 37, 38, 34, 32, 33, 28, 37, 29, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 40, 39, 37, 36, 27, 30, 26, 31, 37, 37, 37, 37, 37, 37, 37, 37, 16, 37, 37, 37, 37, 37, 37, 37, 37, 40, 37, 14, 18, 37, 37, 37, 37, 13, 37, 37, 37, 37, 37, 5, 37, 37, 37, 15, 37, 17, 9, 21, 37, 37, 37, 6, 37, 37, 37, 37, 23, 37, 37, 37, 37, 37, 8, 37, 10, 37, 37, 11, 37, 37, 22, 37, 12, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 7, 25, 37, 4, 37, 37, 37, 37, 37, 37, 37, 37, 37, 2, 37, 24, 3, 37, 37, 37, 37, 19, 1, 37, 37, 37, 37, 37, 37, 37, 37, 37, 20, 0 } ; static yyconst flex_int32_t yy_ec[256] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 4, 5, 1, 1, 1, 1, 1, 1, 1, 6, 7, 1, 8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 10, 11, 1, 1, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 1, 22, 23, 24, 25, 26, 1, 27, 28, 29, 30, 1, 1, 31, 32, 1, 1, 1, 1, 1, 33, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 } ; static yyconst flex_int32_t yy_meta[34] = { 0, 1, 2, 2, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 } ; static yyconst flex_int16_t yy_base[154] = { 0, 0, 0, 156, 155, 159, 0, 156, 162, 147, 162, 0, 0, 0, 146, 145, 144, 20, 124, 136, 126, 20, 11, 12, 134, 23, 133, 123, 0, 162, 0, 145, 0, 0, 0, 0, 132, 130, 117, 131, 130, 110, 17, 123, 0, 117, 110, 22, 108, 111, 122, 107, 106, 0, 116, 0, 0, 116, 115, 100, 95, 0, 115, 106, 106, 108, 107, 0, 95, 94, 97, 0, 93, 0, 0, 0, 102, 95, 102, 82, 83, 99, 96, 78, 77, 80, 85, 79, 74, 77, 0, 75, 0, 31, 32, 0, 77, 86, 68, 84, 0, 75, 69, 73, 67, 66, 61, 33, 64, 77, 79, 75, 77, 56, 71, 62, 56, 0, 0, 57, 0, 56, 49, 50, 65, 67, 49, 48, 56, 55, 0, 47, 0, 0, 49, 43, 42, 41, 0, 0, 53, 41, 39, 42, 43, 27, 35, 29, 27, 0, 162, 61, 49, 64 } ; static yyconst flex_int16_t yy_def[154] = { 0, 150, 1, 151, 151, 150, 152, 150, 150, 152, 150, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 153, 150, 152, 150, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 153, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 0, 150, 150, 150 } ; static yyconst flex_int16_t yy_nxt[196] = { 0, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 6, 18, 6, 6, 6, 6, 19, 6, 6, 20, 6, 21, 22, 23, 24, 25, 26, 6, 27, 6, 6, 36, 43, 41, 45, 44, 48, 65, 60, 46, 66, 37, 42, 61, 101, 103, 115, 30, 149, 49, 50, 148, 147, 146, 145, 144, 102, 104, 116, 28, 28, 28, 53, 53, 143, 142, 141, 140, 139, 138, 137, 136, 135, 134, 133, 132, 131, 130, 129, 128, 127, 126, 125, 124, 123, 122, 121, 120, 119, 118, 117, 114, 113, 112, 111, 110, 109, 108, 107, 106, 105, 100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87, 86, 85, 84, 83, 82, 81, 80, 79, 78, 77, 76, 75, 74, 73, 72, 71, 70, 69, 68, 67, 64, 63, 62, 59, 58, 57, 56, 55, 54, 31, 52, 51, 47, 40, 39, 38, 35, 34, 33, 32, 31, 150, 29, 29, 5, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150 } ; static yyconst flex_int16_t yy_chk[196] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 17, 22, 21, 23, 22, 25, 47, 42, 23, 47, 17, 21, 42, 93, 94, 107, 152, 148, 25, 25, 147, 146, 145, 144, 143, 93, 94, 107, 151, 151, 151, 153, 153, 142, 141, 140, 137, 136, 135, 134, 131, 129, 128, 127, 126, 125, 124, 123, 122, 121, 119, 116, 115, 114, 113, 112, 111, 110, 109, 108, 106, 105, 104, 103, 102, 101, 99, 98, 97, 96, 91, 89, 88, 87, 86, 85, 84, 83, 82, 81, 80, 79, 78, 77, 76, 72, 70, 69, 68, 66, 65, 64, 63, 62, 60, 59, 58, 57, 54, 52, 51, 50, 49, 48, 46, 45, 43, 41, 40, 39, 38, 37, 36, 31, 27, 26, 24, 20, 19, 18, 16, 15, 14, 9, 7, 5, 4, 3, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150 } ; static yy_state_type yy_last_accepting_state; static char *yy_last_accepting_cpos; extern int yy_policy_backend_parser_flex_debug; int yy_policy_backend_parser_flex_debug = 0; /* The intent behind this definition is that it'll catch * any uses of REJECT which flex missed. */ #define REJECT reject_used_but_not_detected #define yymore() yymore_used_but_not_detected #define YY_MORE_ADJ 0 #define YY_RESTORE_YY_MORE_OFFSET char *yy_policy_backend_parsertext; #line 1 "backend.l" #line 2 "backend.l" #include #include "libxorp/xorp.h" #include "policy/common/policy_utils.hh" #include "policy_backend_parser.hh" #include "yacc.yy_policy_backend_parser.cc.h" #define yyparse yy_policy_backend_parserparse #define yyerror yy_policy_backend_parsererror #define yylval yy_policy_backend_parserlval using namespace policy_utils; using namespace policy_backend_parser; void yyerror(const char*); int yyparse(void); vector* policy_backend_parser::_yy_policies; map* policy_backend_parser::_yy_sets; vector* policy_backend_parser::_yy_terms; vector* policy_backend_parser::_yy_instructions; bool policy_backend_parser::_yy_trace; SUBR* policy_backend_parser::_yy_subr; namespace { string _last_error; unsigned _parser_lineno; } #line 582 "lex.yy_policy_backend_parser.cc" #define INITIAL 0 #define STR 1 #ifndef YY_NO_UNISTD_H /* Special case for "unistd.h", since it is non-ANSI. We include it way * down here because we want the user's section 1 to have been scanned first. * The user has a chance to override it with an option. */ #include #endif #ifndef YY_EXTRA_TYPE #define YY_EXTRA_TYPE void * #endif static int yy_init_globals (void ); /* Macros after this point can all be overridden by user definitions in * section 1. */ #ifndef YY_SKIP_YYWRAP #ifdef __cplusplus extern "C" int yy_policy_backend_parserwrap (void ); #else extern int yy_policy_backend_parserwrap (void ); #endif #endif #ifndef yytext_ptr static void yy_flex_strncpy (char *,yyconst char *,int ); #endif #ifdef YY_NEED_STRLEN static int yy_flex_strlen (yyconst char * ); #endif #ifndef YY_NO_INPUT #ifdef __cplusplus static int yyinput (void ); #else static int input (void ); #endif #endif /* Amount of stuff to slurp up with each read. */ #ifndef YY_READ_BUF_SIZE #define YY_READ_BUF_SIZE 8192 #endif /* Copy whatever the last rule matched to the standard output. */ #ifndef ECHO /* This used to be an fputs(), but since the string might contain NUL's, * we now use fwrite(). */ #define ECHO (void) fwrite( yy_policy_backend_parsertext, yy_policy_backend_parserleng, 1, yy_policy_backend_parserout ) #endif /* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, * is returned in "result". */ #ifndef YY_INPUT #define YY_INPUT(buf,result,max_size) \ if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ { \ int c = '*'; \ int n; \ for ( n = 0; n < max_size && \ (c = getc( yy_policy_backend_parserin )) != EOF && c != '\n'; ++n ) \ buf[n] = (char) c; \ if ( c == '\n' ) \ buf[n++] = (char) c; \ if ( c == EOF && ferror( yy_policy_backend_parserin ) ) \ YY_FATAL_ERROR( "input in flex scanner failed" ); \ result = n; \ } \ else \ { \ errno=0; \ while ( (result = fread(buf, 1, max_size, yy_policy_backend_parserin))==0 && ferror(yy_policy_backend_parserin)) \ { \ if( errno != EINTR) \ { \ YY_FATAL_ERROR( "input in flex scanner failed" ); \ break; \ } \ errno=0; \ clearerr(yy_policy_backend_parserin); \ } \ }\ \ #endif /* No semi-colon after return; correct usage is to write "yyterminate();" - * we don't want an extra ';' after the "return" because that will cause * some compilers to complain about unreachable statements. */ #ifndef yyterminate #define yyterminate() return YY_NULL #endif /* Number of entries by which start-condition stack grows. */ #ifndef YY_START_STACK_INCR #define YY_START_STACK_INCR 25 #endif /* Report a fatal error. */ #ifndef YY_FATAL_ERROR #define YY_FATAL_ERROR(msg) yy_fatal_error( msg ) #endif /* end tables serialization structures and prototypes */ /* Default declaration of generated scanner - a define so the user can * easily add parameters. */ #ifndef YY_DECL #define YY_DECL_IS_OURS 1 extern int yy_policy_backend_parserlex (void); #define YY_DECL int yy_policy_backend_parserlex (void) #endif /* !YY_DECL */ /* Code executed at the beginning of each rule, after yy_policy_backend_parsertext and yy_policy_backend_parserleng * have been set up. */ #ifndef YY_USER_ACTION #define YY_USER_ACTION #endif /* Code executed at the end of each rule. */ #ifndef YY_BREAK #define YY_BREAK break; #endif #define YY_RULE_SETUP \ YY_USER_ACTION /** The main scanner function which does all the work. */ YY_DECL { register yy_state_type yy_current_state; register char *yy_cp, *yy_bp; register int yy_act; #line 40 "backend.l" #line 737 "lex.yy_policy_backend_parser.cc" if ( !(yy_init) ) { (yy_init) = 1; #ifdef YY_USER_INIT YY_USER_INIT; #endif if ( ! (yy_start) ) (yy_start) = 1; /* first start state */ if ( ! yy_policy_backend_parserin ) yy_policy_backend_parserin = stdin; if ( ! yy_policy_backend_parserout ) yy_policy_backend_parserout = stdout; if ( ! YY_CURRENT_BUFFER ) { yy_policy_backend_parserensure_buffer_stack (); YY_CURRENT_BUFFER_LVALUE = yy_policy_backend_parser_create_buffer(yy_policy_backend_parserin,YY_BUF_SIZE ); } yy_policy_backend_parser_load_buffer_state( ); } while ( 1 ) /* loops until end-of-file is reached */ { yy_cp = (yy_c_buf_p); /* Support of yy_policy_backend_parsertext. */ *yy_cp = (yy_hold_char); /* yy_bp points to the position in yy_ch_buf of the start of * the current run. */ yy_bp = yy_cp; yy_current_state = (yy_start); yy_match: do { register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)]; if ( yy_accept[yy_current_state] ) { (yy_last_accepting_state) = yy_current_state; (yy_last_accepting_cpos) = yy_cp; } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 151 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; ++yy_cp; } while ( yy_current_state != 150 ); yy_cp = (yy_last_accepting_cpos); yy_current_state = (yy_last_accepting_state); yy_find_action: yy_act = yy_accept[yy_current_state]; YY_DO_BEFORE_ACTION; do_action: /* This label is used only to access EOF actions. */ switch ( yy_act ) { /* beginning of action switch */ case 0: /* must back up */ /* undo the effects of YY_DO_BEFORE_ACTION */ *yy_cp = (yy_hold_char); yy_cp = (yy_last_accepting_cpos); yy_current_state = (yy_last_accepting_state); goto yy_find_action; case 1: YY_RULE_SETUP #line 42 "backend.l" { return YY_POLICY_START; } YY_BREAK case 2: YY_RULE_SETUP #line 43 "backend.l" { return YY_POLICY_END; } YY_BREAK case 3: YY_RULE_SETUP #line 44 "backend.l" { return YY_TERM_START; } YY_BREAK case 4: YY_RULE_SETUP #line 45 "backend.l" { return YY_TERM_END; } YY_BREAK case 5: YY_RULE_SETUP #line 46 "backend.l" { return YY_SET; } YY_BREAK case 6: YY_RULE_SETUP #line 47 "backend.l" { return YY_PUSH; } YY_BREAK case 7: YY_RULE_SETUP #line 48 "backend.l" { return YY_PUSH_SET; } YY_BREAK case 8: YY_RULE_SETUP #line 49 "backend.l" { return YY_REGEX; } YY_BREAK case 9: YY_RULE_SETUP #line 50 "backend.l" { return YY_LOAD; } YY_BREAK case 10: YY_RULE_SETUP #line 51 "backend.l" { return YY_STORE; } YY_BREAK case 11: YY_RULE_SETUP #line 52 "backend.l" { return YY_ACCEPT; } YY_BREAK case 12: YY_RULE_SETUP #line 53 "backend.l" { return YY_REJECT; } YY_BREAK case 13: YY_RULE_SETUP #line 54 "backend.l" { return YY_NOT; } YY_BREAK case 14: YY_RULE_SETUP #line 55 "backend.l" { return YY_AND; } YY_BREAK case 15: YY_RULE_SETUP #line 56 "backend.l" { return YY_XOR; } YY_BREAK case 16: YY_RULE_SETUP #line 57 "backend.l" { return YY_OR; } YY_BREAK case 17: YY_RULE_SETUP #line 58 "backend.l" { return YY_HEAD; } YY_BREAK case 18: YY_RULE_SETUP #line 59 "backend.l" { return YY_CTR; } YY_BREAK case 19: YY_RULE_SETUP #line 60 "backend.l" { return YY_ONFALSE_EXIT; } YY_BREAK case 20: YY_RULE_SETUP #line 61 "backend.l" { return YY_NE_INT; } YY_BREAK case 21: YY_RULE_SETUP #line 62 "backend.l" { return YY_NEXT; } YY_BREAK case 22: YY_RULE_SETUP #line 63 "backend.l" { return YY_POLICY; } YY_BREAK case 23: YY_RULE_SETUP #line 64 "backend.l" { return YY_TERM; } YY_BREAK case 24: YY_RULE_SETUP #line 65 "backend.l" { return YY_SUBR_START; } YY_BREAK case 25: YY_RULE_SETUP #line 66 "backend.l" { return YY_SUBR_END; } YY_BREAK case 26: YY_RULE_SETUP #line 68 "backend.l" { return YY_EQ; } YY_BREAK case 27: YY_RULE_SETUP #line 69 "backend.l" { return YY_NE; } YY_BREAK case 28: YY_RULE_SETUP #line 70 "backend.l" { return YY_LT; } YY_BREAK case 29: YY_RULE_SETUP #line 71 "backend.l" { return YY_GT; } YY_BREAK case 30: YY_RULE_SETUP #line 72 "backend.l" { return YY_LE; } YY_BREAK case 31: YY_RULE_SETUP #line 73 "backend.l" { return YY_GE; } YY_BREAK case 32: YY_RULE_SETUP #line 74 "backend.l" { return YY_ADD; } YY_BREAK case 33: YY_RULE_SETUP #line 75 "backend.l" { return YY_SUB; } YY_BREAK case 34: YY_RULE_SETUP #line 76 "backend.l" { return YY_MUL; } YY_BREAK case 35: /* rule 35 can match eol */ YY_RULE_SETUP #line 78 "backend.l" { _parser_lineno++; return YY_NEWLINE; } YY_BREAK case 36: YY_RULE_SETUP #line 80 "backend.l" /* eat blanks */ YY_BREAK case 37: YY_RULE_SETUP #line 82 "backend.l" { yylval.c_str = strdup(yy_policy_backend_parsertext); return YY_ARG; } YY_BREAK case 38: YY_RULE_SETUP #line 87 "backend.l" BEGIN(STR); YY_BREAK case 39: YY_RULE_SETUP #line 89 "backend.l" BEGIN(INITIAL); YY_BREAK case 40: /* rule 40 can match eol */ YY_RULE_SETUP #line 91 "backend.l" { yylval.c_str = strdup(yy_policy_backend_parsertext); _parser_lineno += count_nl(yy_policy_backend_parsertext); return YY_ARG; } YY_BREAK case 41: YY_RULE_SETUP #line 96 "backend.l" ECHO; YY_BREAK #line 1029 "lex.yy_policy_backend_parser.cc" case YY_STATE_EOF(INITIAL): case YY_STATE_EOF(STR): yyterminate(); case YY_END_OF_BUFFER: { /* Amount of text matched not including the EOB char. */ int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1; /* Undo the effects of YY_DO_BEFORE_ACTION. */ *yy_cp = (yy_hold_char); YY_RESTORE_YY_MORE_OFFSET if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) { /* We're scanning a new file or input source. It's * possible that this happened because the user * just pointed yy_policy_backend_parserin at a new source and called * yy_policy_backend_parserlex(). If so, then we have to assure * consistency between YY_CURRENT_BUFFER and our * globals. Here is the right place to do so, because * this is the first action (other than possibly a * back-up) that will match for the new input source. */ (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; YY_CURRENT_BUFFER_LVALUE->yy_input_file = yy_policy_backend_parserin; YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; } /* Note that here we test for yy_c_buf_p "<=" to the position * of the first EOB in the buffer, since yy_c_buf_p will * already have been incremented past the NUL character * (since all states make transitions on EOB to the * end-of-buffer state). Contrast this with the test * in input(). */ if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) { /* This was really a NUL. */ yy_state_type yy_next_state; (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state( ); /* Okay, we're now positioned to make the NUL * transition. We couldn't have * yy_get_previous_state() go ahead and do it * for us because it doesn't know how to deal * with the possibility of jamming (and we don't * want to build jamming into it because then it * will run more slowly). */ yy_next_state = yy_try_NUL_trans( yy_current_state ); yy_bp = (yytext_ptr) + YY_MORE_ADJ; if ( yy_next_state ) { /* Consume the NUL. */ yy_cp = ++(yy_c_buf_p); yy_current_state = yy_next_state; goto yy_match; } else { yy_cp = (yy_last_accepting_cpos); yy_current_state = (yy_last_accepting_state); goto yy_find_action; } } else switch ( yy_get_next_buffer( ) ) { case EOB_ACT_END_OF_FILE: { (yy_did_buffer_switch_on_eof) = 0; if ( yy_policy_backend_parserwrap( ) ) { /* Note: because we've taken care in * yy_get_next_buffer() to have set up * yy_policy_backend_parsertext, we can now set up * yy_c_buf_p so that if some total * hoser (like flex itself) wants to * call the scanner after we return the * YY_NULL, it'll still work - another * YY_NULL will get returned. */ (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ; yy_act = YY_STATE_EOF(YY_START); goto do_action; } else { if ( ! (yy_did_buffer_switch_on_eof) ) YY_NEW_FILE; } break; } case EOB_ACT_CONTINUE_SCAN: (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state( ); yy_cp = (yy_c_buf_p); yy_bp = (yytext_ptr) + YY_MORE_ADJ; goto yy_match; case EOB_ACT_LAST_MATCH: (yy_c_buf_p) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)]; yy_current_state = yy_get_previous_state( ); yy_cp = (yy_c_buf_p); yy_bp = (yytext_ptr) + YY_MORE_ADJ; goto yy_find_action; } break; } default: YY_FATAL_ERROR( "fatal flex scanner internal error--no action found" ); } /* end of action switch */ } /* end of scanning one token */ } /* end of yy_policy_backend_parserlex */ /* yy_get_next_buffer - try to read in a new buffer * * Returns a code representing an action: * EOB_ACT_LAST_MATCH - * EOB_ACT_CONTINUE_SCAN - continue scanning from current position * EOB_ACT_END_OF_FILE - end of file */ static int yy_get_next_buffer (void) { register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; register char *source = (yytext_ptr); register int number_to_move, i; int ret_val; if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] ) YY_FATAL_ERROR( "fatal flex scanner internal error--end of buffer missed" ); if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) { /* Don't try to fill the buffer, so this is an EOF. */ if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 ) { /* We matched a single character, the EOB, so * treat this as a final EOF. */ return EOB_ACT_END_OF_FILE; } else { /* We matched some text prior to the EOB, first * process it. */ return EOB_ACT_LAST_MATCH; } } /* Try to read more data. */ /* First move last chars to start of buffer. */ number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1; for ( i = 0; i < number_to_move; ++i ) *(dest++) = *(source++); if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) /* don't do the read, it's not guaranteed to return an EOF, * just force an EOF */ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0; else { int num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; while ( num_to_read <= 0 ) { /* Not enough room in the buffer - grow it. */ /* just a shorter name for the current buffer */ YY_BUFFER_STATE b = YY_CURRENT_BUFFER; int yy_c_buf_p_offset = (int) ((yy_c_buf_p) - b->yy_ch_buf); if ( b->yy_is_our_buffer ) { int new_size = b->yy_buf_size * 2; if ( new_size <= 0 ) b->yy_buf_size += b->yy_buf_size / 8; else b->yy_buf_size *= 2; b->yy_ch_buf = (char *) /* Include room in for 2 EOB chars. */ yy_policy_backend_parserrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 ); } else /* Can't grow it, we don't own it. */ b->yy_ch_buf = 0; if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "fatal error - scanner input buffer overflow" ); (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset]; num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; } if ( num_to_read > YY_READ_BUF_SIZE ) num_to_read = YY_READ_BUF_SIZE; /* Read in more data. */ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), (yy_n_chars), num_to_read ); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); } if ( (yy_n_chars) == 0 ) { if ( number_to_move == YY_MORE_ADJ ) { ret_val = EOB_ACT_END_OF_FILE; yy_policy_backend_parserrestart(yy_policy_backend_parserin ); } else { ret_val = EOB_ACT_LAST_MATCH; YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_EOF_PENDING; } } else ret_val = EOB_ACT_CONTINUE_SCAN; (yy_n_chars) += number_to_move; YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR; YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR; (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; return ret_val; } /* yy_get_previous_state - get the state just before the EOB char was reached */ static yy_state_type yy_get_previous_state (void) { register yy_state_type yy_current_state; register char *yy_cp; yy_current_state = (yy_start); for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp ) { register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); if ( yy_accept[yy_current_state] ) { (yy_last_accepting_state) = yy_current_state; (yy_last_accepting_cpos) = yy_cp; } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 151 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; } return yy_current_state; } /* yy_try_NUL_trans - try to make a transition on the NUL character * * synopsis * next_state = yy_try_NUL_trans( current_state ); */ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state ) { register int yy_is_jam; register char *yy_cp = (yy_c_buf_p); register YY_CHAR yy_c = 1; if ( yy_accept[yy_current_state] ) { (yy_last_accepting_state) = yy_current_state; (yy_last_accepting_cpos) = yy_cp; } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 151 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; yy_is_jam = (yy_current_state == 150); return yy_is_jam ? 0 : yy_current_state; } #ifndef YY_NO_INPUT #ifdef __cplusplus static int yyinput (void) #else static int input (void) #endif { int c; *(yy_c_buf_p) = (yy_hold_char); if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR ) { /* yy_c_buf_p now points to the character we want to return. * If this occurs *before* the EOB characters, then it's a * valid NUL; if not, then we've hit the end of the buffer. */ if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) /* This was really a NUL. */ *(yy_c_buf_p) = '\0'; else { /* need more input */ int offset = (yy_c_buf_p) - (yytext_ptr); ++(yy_c_buf_p); switch ( yy_get_next_buffer( ) ) { case EOB_ACT_LAST_MATCH: /* This happens because yy_g_n_b() * sees that we've accumulated a * token and flags that we need to * try matching the token before * proceeding. But for input(), * there's no matching to consider. * So convert the EOB_ACT_LAST_MATCH * to EOB_ACT_END_OF_FILE. */ /* Reset buffer status. */ yy_policy_backend_parserrestart(yy_policy_backend_parserin ); /*FALLTHROUGH*/ case EOB_ACT_END_OF_FILE: { if ( yy_policy_backend_parserwrap( ) ) return EOF; if ( ! (yy_did_buffer_switch_on_eof) ) YY_NEW_FILE; #ifdef __cplusplus return yyinput(); #else return input(); #endif } case EOB_ACT_CONTINUE_SCAN: (yy_c_buf_p) = (yytext_ptr) + offset; break; } } } c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */ *(yy_c_buf_p) = '\0'; /* preserve yy_policy_backend_parsertext */ (yy_hold_char) = *++(yy_c_buf_p); return c; } #endif /* ifndef YY_NO_INPUT */ /** Immediately switch to a different input stream. * @param input_file A readable stream. * * @note This function does not reset the start condition to @c INITIAL . */ void yy_policy_backend_parserrestart (FILE * input_file ) { if ( ! YY_CURRENT_BUFFER ){ yy_policy_backend_parserensure_buffer_stack (); YY_CURRENT_BUFFER_LVALUE = yy_policy_backend_parser_create_buffer(yy_policy_backend_parserin,YY_BUF_SIZE ); } yy_policy_backend_parser_init_buffer(YY_CURRENT_BUFFER,input_file ); yy_policy_backend_parser_load_buffer_state( ); } /** Switch to a different input buffer. * @param new_buffer The new input buffer. * */ void yy_policy_backend_parser_switch_to_buffer (YY_BUFFER_STATE new_buffer ) { /* TODO. We should be able to replace this entire function body * with * yy_policy_backend_parserpop_buffer_state(); * yy_policy_backend_parserpush_buffer_state(new_buffer); */ yy_policy_backend_parserensure_buffer_stack (); if ( YY_CURRENT_BUFFER == new_buffer ) return; if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ *(yy_c_buf_p) = (yy_hold_char); YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); } YY_CURRENT_BUFFER_LVALUE = new_buffer; yy_policy_backend_parser_load_buffer_state( ); /* We don't actually know whether we did this switch during * EOF (yy_policy_backend_parserwrap()) processing, but the only time this flag * is looked at is after yy_policy_backend_parserwrap() is called, so it's safe * to go ahead and always set it. */ (yy_did_buffer_switch_on_eof) = 1; } static void yy_policy_backend_parser_load_buffer_state (void) { (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; yy_policy_backend_parserin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; (yy_hold_char) = *(yy_c_buf_p); } /** Allocate and initialize an input buffer state. * @param file A readable stream. * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. * * @return the allocated buffer state. */ YY_BUFFER_STATE yy_policy_backend_parser_create_buffer (FILE * file, int size ) { YY_BUFFER_STATE b; b = (YY_BUFFER_STATE) yy_policy_backend_parseralloc(sizeof( struct yy_buffer_state ) ); if ( ! b ) YY_FATAL_ERROR( "out of dynamic memory in yy_policy_backend_parser_create_buffer()" ); b->yy_buf_size = size; /* yy_ch_buf has to be 2 characters longer than the size given because * we need to put in 2 end-of-buffer characters. */ b->yy_ch_buf = (char *) yy_policy_backend_parseralloc(b->yy_buf_size + 2 ); if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "out of dynamic memory in yy_policy_backend_parser_create_buffer()" ); b->yy_is_our_buffer = 1; yy_policy_backend_parser_init_buffer(b,file ); return b; } /** Destroy the buffer. * @param b a buffer created with yy_policy_backend_parser_create_buffer() * */ void yy_policy_backend_parser_delete_buffer (YY_BUFFER_STATE b ) { if ( ! b ) return; if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; if ( b->yy_is_our_buffer ) yy_policy_backend_parserfree((void *) b->yy_ch_buf ); yy_policy_backend_parserfree((void *) b ); } /* Initializes or reinitializes a buffer. * This function is sometimes called more than once on the same buffer, * such as during a yy_policy_backend_parserrestart() or at EOF. */ static void yy_policy_backend_parser_init_buffer (YY_BUFFER_STATE b, FILE * file ) { int oerrno = errno; yy_policy_backend_parser_flush_buffer(b ); b->yy_input_file = file; b->yy_fill_buffer = 1; /* If b is the current buffer, then yy_policy_backend_parser_init_buffer was _probably_ * called from yy_policy_backend_parserrestart() or through yy_get_next_buffer. * In that case, we don't want to reset the lineno or column. */ if (b != YY_CURRENT_BUFFER){ b->yy_bs_lineno = 1; b->yy_bs_column = 0; } b->yy_is_interactive = 0; errno = oerrno; } /** Discard all buffered characters. On the next scan, YY_INPUT will be called. * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. * */ void yy_policy_backend_parser_flush_buffer (YY_BUFFER_STATE b ) { if ( ! b ) return; b->yy_n_chars = 0; /* We always need two end-of-buffer characters. The first causes * a transition to the end-of-buffer state. The second causes * a jam in that state. */ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; b->yy_buf_pos = &b->yy_ch_buf[0]; b->yy_at_bol = 1; b->yy_buffer_status = YY_BUFFER_NEW; if ( b == YY_CURRENT_BUFFER ) yy_policy_backend_parser_load_buffer_state( ); } /** Pushes the new state onto the stack. The new state becomes * the current state. This function will allocate the stack * if necessary. * @param new_buffer The new state. * */ void yy_policy_backend_parserpush_buffer_state (YY_BUFFER_STATE new_buffer ) { if (new_buffer == NULL) return; yy_policy_backend_parserensure_buffer_stack(); /* This block is copied from yy_policy_backend_parser_switch_to_buffer. */ if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ *(yy_c_buf_p) = (yy_hold_char); YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); } /* Only push if top exists. Otherwise, replace top. */ if (YY_CURRENT_BUFFER) (yy_buffer_stack_top)++; YY_CURRENT_BUFFER_LVALUE = new_buffer; /* copied from yy_policy_backend_parser_switch_to_buffer. */ yy_policy_backend_parser_load_buffer_state( ); (yy_did_buffer_switch_on_eof) = 1; } /** Removes and deletes the top of the stack, if present. * The next element becomes the new top. * */ void yy_policy_backend_parserpop_buffer_state (void) { if (!YY_CURRENT_BUFFER) return; yy_policy_backend_parser_delete_buffer(YY_CURRENT_BUFFER ); YY_CURRENT_BUFFER_LVALUE = NULL; if ((yy_buffer_stack_top) > 0) --(yy_buffer_stack_top); if (YY_CURRENT_BUFFER) { yy_policy_backend_parser_load_buffer_state( ); (yy_did_buffer_switch_on_eof) = 1; } } /* Allocates the stack if it does not exist. * Guarantees space for at least one push. */ static void yy_policy_backend_parserensure_buffer_stack (void) { int num_to_alloc; if (!(yy_buffer_stack)) { /* First allocation is just for 2 elements, since we don't know if this * scanner will even need a stack. We use 2 instead of 1 to avoid an * immediate realloc on the next call. */ num_to_alloc = 1; (yy_buffer_stack) = (struct yy_buffer_state**)yy_policy_backend_parseralloc (num_to_alloc * sizeof(struct yy_buffer_state*) ); memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*)); (yy_buffer_stack_max) = num_to_alloc; (yy_buffer_stack_top) = 0; return; } if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){ /* Increase the buffer to prepare for a possible push. */ int grow_size = 8 /* arbitrary grow size */; num_to_alloc = (yy_buffer_stack_max) + grow_size; (yy_buffer_stack) = (struct yy_buffer_state**)yy_policy_backend_parserrealloc ((yy_buffer_stack), num_to_alloc * sizeof(struct yy_buffer_state*) ); /* zero only the new slots.*/ memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*)); (yy_buffer_stack_max) = num_to_alloc; } } /** Setup the input buffer state to scan directly from a user-specified character buffer. * @param base the character buffer * @param size the size in bytes of the character buffer * * @return the newly allocated buffer state object. */ YY_BUFFER_STATE yy_policy_backend_parser_scan_buffer (char * base, yy_size_t size ) { YY_BUFFER_STATE b; if ( size < 2 || base[size-2] != YY_END_OF_BUFFER_CHAR || base[size-1] != YY_END_OF_BUFFER_CHAR ) /* They forgot to leave room for the EOB's. */ return 0; b = (YY_BUFFER_STATE) yy_policy_backend_parseralloc(sizeof( struct yy_buffer_state ) ); if ( ! b ) YY_FATAL_ERROR( "out of dynamic memory in yy_policy_backend_parser_scan_buffer()" ); b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ b->yy_buf_pos = b->yy_ch_buf = base; b->yy_is_our_buffer = 0; b->yy_input_file = 0; b->yy_n_chars = b->yy_buf_size; b->yy_is_interactive = 0; b->yy_at_bol = 1; b->yy_fill_buffer = 0; b->yy_buffer_status = YY_BUFFER_NEW; yy_policy_backend_parser_switch_to_buffer(b ); return b; } /** Setup the input buffer state to scan a string. The next call to yy_policy_backend_parserlex() will * scan from a @e copy of @a str. * @param str a NUL-terminated string to scan * * @return the newly allocated buffer state object. * @note If you want to scan bytes that may contain NUL values, then use * yy_policy_backend_parser_scan_bytes() instead. */ YY_BUFFER_STATE yy_policy_backend_parser_scan_string (yyconst char * yystr ) { return yy_policy_backend_parser_scan_bytes(yystr,strlen(yystr) ); } /** Setup the input buffer state to scan the given bytes. The next call to yy_policy_backend_parserlex() will * scan from a @e copy of @a bytes. * @param bytes the byte buffer to scan * @param len the number of bytes in the buffer pointed to by @a bytes. * * @return the newly allocated buffer state object. */ YY_BUFFER_STATE yy_policy_backend_parser_scan_bytes (yyconst char * yybytes, int _yybytes_len ) { YY_BUFFER_STATE b; char *buf; yy_size_t n; int i; /* Get memory for full buffer, including space for trailing EOB's. */ n = _yybytes_len + 2; buf = (char *) yy_policy_backend_parseralloc(n ); if ( ! buf ) YY_FATAL_ERROR( "out of dynamic memory in yy_policy_backend_parser_scan_bytes()" ); for ( i = 0; i < _yybytes_len; ++i ) buf[i] = yybytes[i]; buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; b = yy_policy_backend_parser_scan_buffer(buf,n ); if ( ! b ) YY_FATAL_ERROR( "bad buffer in yy_policy_backend_parser_scan_bytes()" ); /* It's okay to grow etc. this buffer, and we should throw it * away when we're done. */ b->yy_is_our_buffer = 1; return b; } #ifndef YY_EXIT_FAILURE #define YY_EXIT_FAILURE 2 #endif static void yy_fatal_error (yyconst char* msg ) { (void) fprintf( stderr, "%s\n", msg ); exit( YY_EXIT_FAILURE ); } /* Redefine yyless() so it works in section 3 code. */ #undef yyless #define yyless(n) \ do \ { \ /* Undo effects of setting up yy_policy_backend_parsertext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ yy_policy_backend_parsertext[yy_policy_backend_parserleng] = (yy_hold_char); \ (yy_c_buf_p) = yy_policy_backend_parsertext + yyless_macro_arg; \ (yy_hold_char) = *(yy_c_buf_p); \ *(yy_c_buf_p) = '\0'; \ yy_policy_backend_parserleng = yyless_macro_arg; \ } \ while ( 0 ) /* Accessor methods (get/set functions) to struct members. */ /** Get the current line number. * */ int yy_policy_backend_parserget_lineno (void) { return yy_policy_backend_parserlineno; } /** Get the input stream. * */ FILE *yy_policy_backend_parserget_in (void) { return yy_policy_backend_parserin; } /** Get the output stream. * */ FILE *yy_policy_backend_parserget_out (void) { return yy_policy_backend_parserout; } /** Get the length of the current token. * */ int yy_policy_backend_parserget_leng (void) { return yy_policy_backend_parserleng; } /** Get the current token. * */ char *yy_policy_backend_parserget_text (void) { return yy_policy_backend_parsertext; } /** Set the current line number. * @param line_number * */ void yy_policy_backend_parserset_lineno (int line_number ) { yy_policy_backend_parserlineno = line_number; } /** Set the input stream. This does not discard the current * input buffer. * @param in_str A readable stream. * * @see yy_policy_backend_parser_switch_to_buffer */ void yy_policy_backend_parserset_in (FILE * in_str ) { yy_policy_backend_parserin = in_str ; } void yy_policy_backend_parserset_out (FILE * out_str ) { yy_policy_backend_parserout = out_str ; } int yy_policy_backend_parserget_debug (void) { return yy_policy_backend_parser_flex_debug; } void yy_policy_backend_parserset_debug (int bdebug ) { yy_policy_backend_parser_flex_debug = bdebug ; } static int yy_init_globals (void) { /* Initialization is the same as for the non-reentrant scanner. * This function is called from yy_policy_backend_parserlex_destroy(), so don't allocate here. */ (yy_buffer_stack) = 0; (yy_buffer_stack_top) = 0; (yy_buffer_stack_max) = 0; (yy_c_buf_p) = (char *) 0; (yy_init) = 0; (yy_start) = 0; /* Defined in main.c */ #ifdef YY_STDINIT yy_policy_backend_parserin = stdin; yy_policy_backend_parserout = stdout; #else yy_policy_backend_parserin = (FILE *) 0; yy_policy_backend_parserout = (FILE *) 0; #endif /* For future reference: Set errno on error, since we are called by * yy_policy_backend_parserlex_init() */ return 0; } /* yy_policy_backend_parserlex_destroy is for both reentrant and non-reentrant scanners. */ int yy_policy_backend_parserlex_destroy (void) { /* Pop the buffer stack, destroying each element. */ while(YY_CURRENT_BUFFER){ yy_policy_backend_parser_delete_buffer(YY_CURRENT_BUFFER ); YY_CURRENT_BUFFER_LVALUE = NULL; yy_policy_backend_parserpop_buffer_state(); } /* Destroy the stack itself. */ yy_policy_backend_parserfree((yy_buffer_stack) ); (yy_buffer_stack) = NULL; /* Reset the globals. This is important in a non-reentrant scanner so the next time * yy_policy_backend_parserlex() is called, initialization will occur. */ yy_init_globals( ); return 0; } /* * Internal utility routines. */ #ifndef yytext_ptr static void yy_flex_strncpy (char* s1, yyconst char * s2, int n ) { register int i; for ( i = 0; i < n; ++i ) s1[i] = s2[i]; } #endif #ifdef YY_NEED_STRLEN static int yy_flex_strlen (yyconst char * s ) { register int n; for ( n = 0; s[n]; ++n ) ; return n; } #endif void *yy_policy_backend_parseralloc (yy_size_t size ) { return (void *) malloc( size ); } void *yy_policy_backend_parserrealloc (void * ptr, yy_size_t size ) { /* The cast to (char *) in the following accommodates both * implementations that use char* generic pointers, and those * that use void* generic pointers. It works with the latter * because both ANSI C and C++ allow castless assignment from * any pointer type to void*, and deal with argument conversions * as though doing an assignment. */ return (void *) realloc( (char *) ptr, size ); } void yy_policy_backend_parserfree (void * ptr ) { free( (char *) ptr ); /* see yy_policy_backend_parserrealloc() for (char *) cast */ } #define YYTABLES_NAME "yytables" #line 96 "backend.l" void yyerror(const char *m) { ostringstream oss; oss << "Error on line " << _parser_lineno << " near ("; for(int i = 0; i < yy_policy_backend_parserleng; i++) oss << yy_policy_backend_parsertext[i]; oss << "): " << m; _last_error = oss.str(); } int policy_backend_parser::policy_backend_parse(vector& outpolicies, map& outsets, SUBR& subr, const string& conf, string& outerr) { YY_BUFFER_STATE yybuffstate = yy_policy_backend_parser_scan_string(conf.c_str()); _last_error = "No error"; _parser_lineno = 1; _yy_policies = &outpolicies; _yy_sets = &outsets; _yy_subr = &subr; _yy_terms = new vector(); _yy_instructions = new vector(); _yy_trace = false; int res = yyparse(); yy_policy_backend_parser_delete_buffer(yybuffstate); outerr = _last_error; // parse error if (res) { // get rid of temporary parse object not yet bound to policies delete_vector(_yy_terms); delete_vector(_yy_instructions); } // good parse else { // all terms should be bound to policies assert(_yy_terms->empty()); delete _yy_terms; // all instructions should be bound to terms assert(_yy_instructions->empty()); delete _yy_instructions; } return res; } xorp/policy/backend/yacc.yy_policy_backend_parser.cc.h0000664000076400007640000000164411421137511023265 0ustar greearbgreearb#ifndef YYERRCODE #define YYERRCODE 256 #endif #define YY_ARG 257 #define YY_NEWLINE 258 #define YY_BLANK 259 #define YY_POLICY_START 260 #define YY_POLICY_END 261 #define YY_TERM_START 262 #define YY_TERM_END 263 #define YY_PUSH 264 #define YY_PUSH_SET 265 #define YY_EQ 266 #define YY_NE 267 #define YY_LT 268 #define YY_GT 269 #define YY_LE 270 #define YY_GE 271 #define YY_NOT 272 #define YY_AND 273 #define YY_OR 274 #define YY_XOR 275 #define YY_HEAD 276 #define YY_CTR 277 #define YY_NE_INT 278 #define YY_ADD 279 #define YY_SUB 280 #define YY_MUL 281 #define YY_ONFALSE_EXIT 282 #define YY_REGEX 283 #define YY_LOAD 284 #define YY_STORE 285 #define YY_ACCEPT 286 #define YY_REJECT 287 #define YY_SET 288 #define YY_NEXT 289 #define YY_POLICY 290 #define YY_SUBR_START 291 #define YY_SUBR_END 292 #define YY_TERM 293 typedef union { char* c_str; PolicyInstr* c_pi; } YYSTYPE; extern YYSTYPE yy_policy_backend_parserlval; xorp/policy/backend/backend.l0000664000076400007640000000711411421137511016311 0ustar greearbgreearb%{ #include #include #include "libxorp/xorp.h" #include "policy/common/policy_utils.hh" #include "policy_backend_parser.hh" #include "yacc.yy_policy_backend_parser.cc.h" #define yyparse yy_policy_backend_parserparse #define yyerror yy_policy_backend_parsererror #define yylval yy_policy_backend_parserlval using namespace policy_utils; using namespace policy_backend_parser; void yyerror(const char*); int yyparse(void); vector* policy_backend_parser::_yy_policies; map* policy_backend_parser::_yy_sets; vector* policy_backend_parser::_yy_terms; vector* policy_backend_parser::_yy_instructions; bool policy_backend_parser::_yy_trace; SUBR* policy_backend_parser::_yy_subr; namespace { string _last_error; unsigned _parser_lineno; } %} %option noyywrap %option nounput %option prefix="yy_policy_backend_parser" %option outfile="lex.yy_policy_backend_parser.cc" %option never-interactive %x STR %% "POLICY_START" { return YY_POLICY_START; } "POLICY_END" { return YY_POLICY_END; } "TERM_START" { return YY_TERM_START; } "TERM_END" { return YY_TERM_END; } "SET" { return YY_SET; } "PUSH" { return YY_PUSH; } "PUSH_SET" { return YY_PUSH_SET; } "REGEX" { return YY_REGEX; } "LOAD" { return YY_LOAD; } "STORE" { return YY_STORE; } "ACCEPT" { return YY_ACCEPT; } "REJECT" { return YY_REJECT; } "NOT" { return YY_NOT; } "AND" { return YY_AND; } "XOR" { return YY_XOR; } "OR" { return YY_OR; } "HEAD" { return YY_HEAD; } "CTR" { return YY_CTR; } "ONFALSE_EXIT" { return YY_ONFALSE_EXIT; } "NON_EMPTY_INTERSECTION" { return YY_NE_INT; } "NEXT" { return YY_NEXT; } "POLICY" { return YY_POLICY; } "TERM" { return YY_TERM; } "SUBR_START" { return YY_SUBR_START; } "SUBR_END" { return YY_SUBR_END; } "==" { return YY_EQ; } "!=" { return YY_NE; } "<" { return YY_LT; } ">" { return YY_GT; } "<=" { return YY_LE; } ">=" { return YY_GE; } "+" { return YY_ADD; } "\-" { return YY_SUB; } "*" { return YY_MUL; } "\n" { _parser_lineno++; return YY_NEWLINE; } [[:blank:]]+ /* eat blanks */ [^\"[:blank:]\n]+ { yylval.c_str = strdup(yytext); return YY_ARG; } \" BEGIN(STR); \" BEGIN(INITIAL); [^\"]+ { yylval.c_str = strdup(yytext); _parser_lineno += count_nl(yytext); return YY_ARG; } %% void yyerror(const char *m) { ostringstream oss; oss << "Error on line " << _parser_lineno << " near ("; for(int i = 0; i < yyleng; i++) oss << yytext[i]; oss << "): " << m; _last_error = oss.str(); } int policy_backend_parser::policy_backend_parse(vector& outpolicies, map& outsets, SUBR& subr, const string& conf, string& outerr) { YY_BUFFER_STATE yybuffstate = yy_scan_string(conf.c_str()); _last_error = "No error"; _parser_lineno = 1; _yy_policies = &outpolicies; _yy_sets = &outsets; _yy_subr = &subr; _yy_terms = new vector(); _yy_instructions = new vector(); _yy_trace = false; int res = yyparse(); yy_delete_buffer(yybuffstate); outerr = _last_error; // parse error if (res) { // get rid of temporary parse object not yet bound to policies delete_vector(_yy_terms); delete_vector(_yy_instructions); } // good parse else { // all terms should be bound to policies assert(_yy_terms->empty()); delete _yy_terms; // all instructions should be bound to terms assert(_yy_instructions->empty()); delete _yy_instructions; } return res; } xorp/policy/backend/instr_visitor.hh0000664000076400007640000000352711421137511020010 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/policy/backend/instr_visitor.hh,v 1.10 2008/10/02 21:58:03 bms Exp $ #ifndef __POLICY_BACKEND_INSTR_VISITOR_HH__ #define __POLICY_BACKEND_INSTR_VISITOR_HH__ // XXX: not acyclic! [but better for compiletime "safety"]. class Push; class PushSet; class OnFalseExit; class Load; class Store; class Accept; class Reject; class NaryInstr; class Next; class Subr; /** * @short Visitor pattern to traverse a structure of instructions. * * Inspired by Alexandrescu [Modern C++ Design]. */ class InstrVisitor { public: virtual ~InstrVisitor() {} virtual void visit(Push&) = 0; virtual void visit(PushSet&) = 0; virtual void visit(OnFalseExit&) = 0; virtual void visit(Load&) = 0; virtual void visit(Store&) = 0; virtual void visit(Accept&) = 0; virtual void visit(Reject&) = 0; virtual void visit(NaryInstr&) = 0; virtual void visit(Next&) = 0; virtual void visit(Subr&) = 0; }; #endif // __POLICY_BACKEND_INSTR_VISITOR_HH__ xorp/policy/backend/policytags.cc0000664000076400007640000000744311540224232017236 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "libxorp/xorp.h" #include "policytags.hh" #include "policy/common/elem_set.hh" #include "libxipc/xrl_atom.hh" PolicyTags::PolicyTags() : _tag(0) { } PolicyTags::PolicyTags(const XrlAtomList& alist) : _tag(0) { // first is always tag XLOG_ASSERT(alist.size() > 0); // go through all the atoms in the list for (unsigned i = 0; i < alist.size(); ++i) { const XrlAtom& atom = alist.get(i); // only support u32's if (atom.type() != xrlatom_uint32) xorp_throw(PolicyTagsError, "XrlAtomList does not contain uint32's"); uint32_t val = atom.uint32(); if (i == 0) { _tag = val; continue; } // it's good, insert it _tags.insert(val); } } void PolicyTags::set_ptags(const Element& element) { // we only support set elements const ElemSetU32* es = dynamic_cast(&element); if (!es) xorp_throw(PolicyTagsError, string("Element is not a set: ") + element.type()); _tags.clear(); // go through all the set elements. for (ElemSetU32::const_iterator i = es->begin(); i != es->end(); ++i) { const ElemU32& x = *i; // all ElemSet elements are represented as string, so convert and // insert. _tags.insert(x.val()); } } string PolicyTags::str() const { ostringstream oss; for(Set::const_iterator i = _tags.begin(); i != _tags.end(); ++i) oss << *i << ", "; string res = oss.str(); unsigned len = res.length(); if(len < 2) return res; // kill last ", " res.erase(res.length()-2); return res; } bool PolicyTags::operator==(const PolicyTags& rhs) const { return (_tags == rhs._tags) && (_tag == rhs._tag); } XrlAtomList PolicyTags::xrl_atomlist() const { XrlAtomList alist; alist.append(XrlAtom(_tag)); for(Set::const_iterator i = _tags.begin(); i != _tags.end(); ++i) { uint32_t tag = *i; alist.append(XrlAtom(tag)); } return alist; } Element* PolicyTags::element() const { ElemSetU32* s = new ElemSetU32; for (Set::const_iterator i = _tags.begin(); i != _tags.end(); ++i) { ElemU32 e(*i); s->insert(e); } return s; } Element* PolicyTags::element_tag() const { return new ElemU32(_tag); } void PolicyTags::set_tag(const Element& e) { uint32_t val = dynamic_cast(e).val(); _tag = val; } void PolicyTags::insert(const PolicyTags& ptags) { // go through all the elements in ptags and insert them. for(Set::const_iterator i = ptags._tags.begin(); i != ptags._tags.end(); ++i) _tags.insert(*i); } bool PolicyTags::contains_atleast_one(const PolicyTags& tags) const { Set output; // The two sets must not be dis-joint. // The intersection must contain atleast one element. set_intersection(tags._tags.begin(), tags._tags.end(), _tags.begin(), _tags.end(), insert_iterator(output,output.begin())); return !output.empty(); } void PolicyTags::insert(uint32_t tag) { _tags.insert(tag); } xorp/policy/backend/policy_filter.cc0000664000076400007640000001000311540225532017712 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "policy/policy_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "policy/common/policy_utils.hh" #include "policy_filter.hh" #include "policy_backend_parser.hh" #include "set_manager.hh" #include "iv_exec.hh" using namespace policy_utils; using policy_backend_parser::policy_backend_parse; PolicyFilter::PolicyFilter() : _policies(NULL), #ifndef XORP_DISABLE_PROFILE _profiler_exec(NULL), #endif _subr(NULL) { _exec.set_set_manager(&_sman); } void PolicyFilter::configure(const string& str) { vector* policies = new vector(); map* sets = new map(); SUBR* subr = new SUBR; string err; // do the actual parsing if (policy_backend_parse(*policies, *sets, *subr, str, err)) { // get rid of temporary parse junk. delete_vector(policies); clear_map(*sets); clear_map(*subr); delete sets; delete subr; xorp_throw(ConfError, err); } // properly erase old conf reset(); // replace with new conf _policies = policies; _subr = subr; _sman.replace_sets(sets); _exec.set_policies(_policies); _exec.set_subr(_subr); } PolicyFilter::~PolicyFilter() { reset(); } void PolicyFilter::reset() { if (_policies) { delete_vector(_policies); _policies = NULL; _exec.set_policies(NULL); } if (_subr) { clear_map(*_subr); delete _subr; _subr = NULL; } _sman.clear(); } bool PolicyFilter::acceptRoute(VarRW& varrw) { bool default_action = true; // no configuration done yet. if (!_policies) { // need to sync. Consider case where the parent [such as version policy // filter] performed a write for some reason. If we return without // syncing, it might be a problem [i.e. when using singlevarrw which // will perform the write only on sync!] varrw.sync(); return default_action; } #ifndef XORP_DISABLE_PROFILE // setup profiling _exec.set_profiler(_profiler_exec); #endif // run policies IvExec::FlowAction fa = _exec.run(&varrw); // print any trace data... uint32_t level = varrw.trace(); if (level) { string trace = ""; // basic, one line [hopefully!] info... if (level > 0) { trace += varrw.more_tracelog(); switch (fa) { case IvExec::REJ: trace += ": rejected"; break; case IvExec::DEFAULT: trace += ": default action"; break; case IvExec::ACCEPT: trace += ": accepted"; break; } } if (level > 1) { trace += "\nBasic VarRW trace:\n"; trace += varrw.tracelog(); } if (level > 2) { trace += "Execution trace:\n"; trace += _exec.tracelog(); trace += "End of trace\n"; } XLOG_TRACE(true, "Policy filter result: %s", trace.c_str()); } // decide what to do switch (fa) { case IvExec::REJ: return false; case IvExec::DEFAULT: return default_action; case IvExec::ACCEPT: return true; } // unreach [hopefully] return default_action; } #ifndef XORP_DISABLE_PROFILE void PolicyFilter::set_profiler_exec(PolicyProfiler* profiler) { _profiler_exec = profiler; } #endif xorp/policy/backend/version_filter.hh0000664000076400007640000000534611540225532020130 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __POLICY_BACKEND_VERSION_FILTER_HH__ #define __POLICY_BACKEND_VERSION_FILTER_HH__ #include "policy/common/varrw.hh" #include "filter_base.hh" #include "policy_filter.hh" /** * @short Policy filters which support versioning [i.e. keep old version]. * * The idea is to create a new policy filter on each configuration. Whenever a * route is being processed, you read which filter to run. If this filter is 0, * [null pointer] then give it the last configuration. Else just run whatever * filter is returned. * * Filters should be referenced counted by routes. When reference count reaches * 0, it should be deleted. * * Why not keep filters internally here and read a filter id from route? Well * because we cannot assume when to increment and decrement the reference count. * Say it's a normal route lookup and we do the filtering, and it results to * "accepted". It doesn't imply we need to +1 the reference count. */ class VersionFilter : public FilterBase { public: /** * @param fname the variable to read/write in order to access filter. */ VersionFilter(const VarRW::Id& fname); ~VersionFilter(); /** * Configure the filter * * @param str filter configuration. */ void configure(const string& str); /** * Reset the filter. * * Filter becomes a NO-operation -- default action should * be returned everytime an acceptRoute is called. */ void reset(); /** * See if a route is accepted by the filter. * The route may be modified by the filter [through VarRW]. * * @return true if the route is accepted, false otherwise. * @param varrw the VarRW associated with the route being filtered. */ bool acceptRoute(VarRW& varrw); private: RefPf _filter; VarRW::Id _fname; }; #endif // __POLICY_BACKEND_VERSION_FILTER_HH__ xorp/policy/backend/version_filters.hh0000664000076400007640000000360711540225532020311 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __POLICY_BACKEND_VERSION_FILTERS_HH__ #define __POLICY_BACKEND_VERSION_FILTERS_HH__ #include "policy_filters.hh" /** * @short Policy filters which support versioning [i.e. keep old version]. * * The idea is to create a new policy filter on each configuration. Whenever a * route is being processed, you read which filter to run. If this filter is 0, * [null pointer] then give it the last configuration. Else just run whatever * filter is returned. * * Filters should be referenced counted by routes. When reference count reaches * 0, it should be deleted. * * Why not keep filters internally here and read a filter id from route? Well * because we cannot assume when to increment and decrement the reference count. * Say it's a normal route lookup and we do the filtering, and it results to * "accepted". It doesn't imply we need to +1 the reference count. */ class VersionFilters : public PolicyFilters { public: VersionFilters(); }; #endif // __POLICY_BACKEND_VERSION_FILTERS_HH__ xorp/policy/backend/policy_backend_parser.hh0000664000076400007640000000471711540224232021415 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/policy/backend/policy_backend_parser.hh,v 1.9 2008/10/02 21:58:04 bms Exp $ #ifndef __POLICY_BACKEND_POLICY_BACKEND_PARSER_HH__ #define __POLICY_BACKEND_POLICY_BACKEND_PARSER_HH__ #include "policy/common/element_base.hh" #include "policy_instr.hh" #include "term_instr.hh" #include "instruction_base.hh" typedef map SUBR; /** * @short Minimises global namespace pollution of yacc/lex variables. * * The nature of lex and yacc causes global variables / functions to be present. * Here such methods and functions are grouped under one namespace. */ namespace policy_backend_parser { typedef vector POLICIES; /** * Parses a backend policy configuration. * * Caller is responsible for deleting partially parsed policies and sets. * * @return 0 on success. Otherwise, outerr is filled with error message. * @param outpolicies the parse tree of all policies. * @param outsets the pair of set-name / content. * @param conf the configuration to parse. * @param outerr string filled with parse error message, on error. */ int policy_backend_parse(vector& outpolicies, map& outsets, SUBR& outsubr, const string& conf, string& outerr); extern vector* _yy_policies; extern map* _yy_sets; extern vector* _yy_terms; extern vector* _yy_instructions; extern bool _yy_trace; extern SUBR* _yy_subr; } // namespace #endif // __POLICY_BACKEND_POLICY_BACKEND_PARSER_HH__ xorp/policy/backend/iv_exec.hh0000664000076400007640000001057211540225532016515 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/policy/backend/iv_exec.hh,v 1.19 2008/10/02 21:58:04 bms Exp $ #ifndef __POLICY_BACKEND_IV_EXEC_HH__ #define __POLICY_BACKEND_IV_EXEC_HH__ #include "libxorp/xorp.h" #include "policy/common/dispatcher.hh" #include "policy/common/varrw.hh" #include "policy/common/policy_exception.hh" #ifndef XORP_DISABLE_PROFILE #include "policy_profiler.hh" #endif #include "instruction.hh" #include "set_manager.hh" #include "term_instr.hh" #include "policy_instr.hh" #include "policy_backend_parser.hh" /** * @short Visitor that executes instructions * * The execution process may be optimized by not using visitors. Having * instructions implement a method that returns a flow action directly. */ class IvExec : public NONCOPYABLE, public InstrVisitor { public: /** * A FlowAction is what has to be done with the route. DEFAULT is the * default action which is normally "go to the next term", or if the last * term, ACCEPT. */ enum FlowAction { ACCEPT, REJ, DEFAULT }; /** * @short Run time errors, such as doing unsupported operations. * * The semantic check should get rid of these. */ class RuntimeError : public PolicyException { public: RuntimeError(const char* file, size_t line, const string& init_why = "") : PolicyException("RuntimeError", file, line, init_why) {} }; IvExec(); ~IvExec(); void set_policies(vector* policies); void set_set_manager(SetManager* sman); /** * Execute the policies. */ FlowAction run(VarRW* varrw); /** * Execute a policy. * * @param pi policy to execute */ FlowAction runPolicy(PolicyInstr& pi); /** * Execute a term. * * @param ti term to execute. */ FlowAction runTerm(TermInstr& ti); /** * @param p push to execute. */ void visit(Push& p); /** * @param ps push of a set to execute. */ void visit(PushSet& ps); /** * @param x OnFalseExit to execute. */ void visit(OnFalseExit& x); /** * @param l Load to execute. */ void visit(Load& l); /** * @param s Store to execute. */ void visit(Store& s); /** * @param a accept the route. */ void visit(Accept& a); /** * @param r reject the route. */ void visit(Reject& r); /** * @param nary N-ary instruction to execute. */ void visit(NaryInstr& nary); void visit(Next& next); void visit(Subr& sub); /** * @return String representation of flow action. * @param fa Flow action to convert. */ static string fa2str(const FlowAction& fa); #ifndef XORP_DISABLE_PROFILE void set_profiler(PolicyProfiler*); #endif string tracelog(); void set_subr(SUBR* subr); private: /** * Do garbage collection. */ void clear_trash(); PolicyInstr** _policies; unsigned _policy_count; const Element** _stack_bottom; const Element** _stack; const Element** _stackend; const Element** _stackptr; SetManager* _sman; VarRW* _varrw; bool _finished; Dispatcher _disp; FlowAction _fa; Element** _trash; unsigned _trashc; unsigned _trashs; ostringstream _os; #ifndef XORP_DISABLE_PROFILE PolicyProfiler* _profiler; #endif bool _do_trace; bool _did_trace; Next::Flow _ctr_flow; SUBR* _subr; }; #endif // __POLICY_BACKEND_IV_EXEC_HH__ xorp/policy/backend/policy_profiler.cc0000664000076400007640000000333011421137511020251 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "policy/policy_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/profile.hh" #include "policy_profiler.hh" PolicyProfiler::PolicyProfiler() : _samplec(0), _stopped(true) { } void PolicyProfiler::start() { XLOG_ASSERT(_stopped); XLOG_ASSERT(_samplec < MAX_SAMPLES); _samples[_samplec] = SP::sample(); _stopped = false; } void PolicyProfiler::stop() { TU now = SP::sample(); XLOG_ASSERT(!_stopped); XLOG_ASSERT(now >= _samples[_samplec]); _samples[_samplec] = now - _samples[_samplec]; _stopped = true; _samplec++; } unsigned PolicyProfiler::count() { return _samplec; } PolicyProfiler::TU PolicyProfiler::sample(unsigned idx) { XLOG_ASSERT(idx < _samplec); return _samples[idx]; } void PolicyProfiler::clear() { _samplec = 0; _stopped = true; } xorp/policy/backend/policytags.hh0000664000076400007640000000665611540224232017255 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/policy/backend/policytags.hh,v 1.9 2008/10/02 21:58:04 bms Exp $ #ifndef __POLICY_BACKEND_POLICYTAGS_HH__ #define __POLICY_BACKEND_POLICYTAGS_HH__ #include "policy/common/policy_exception.hh" #include "policy/common/element_base.hh" #include "libxipc/xrl_atom_list.hh" /** * @short A set of policy tags. A policytag is a marker for a route. * * A policytag marks a route. This is needed to match the source part of an * export policy. Export filters are in the destination protocol, so no * information about the origin of the route is available then. Thus, routes * need to be tagged in the source protocol, so the export filter may match * against tags. */ class PolicyTags { public: /** * @short Exception thrown on failed initialization of tags. */ class PolicyTagsError : public PolicyException { public: PolicyTagsError(const char* file, size_t line, const string& init_why = "") : PolicyException("PolicyTagsError", file, line, init_why) {} }; /** * Empty policytags may be safely created. No exception thrown */ PolicyTags(); /** * Attempt to create policy tags from an XrlAtomList. * It must contain unsigned 32bit integer atoms. * * @param xrlatoms list of xrlatom_uint32 atoms to initialize from. */ PolicyTags(const XrlAtomList& xrlatoms); /** * @return string representation of policytags. */ string str() const; /** * @return true if set is equal. * @param rhs PolicyTags to compare with. */ bool operator==(const PolicyTags& rhs) const; /** * Convert to an ElemSet. * * @return ElemSet representation. Caller is responsible for delete. */ Element* element() const; Element* element_tag() const; void set_tag(const Element& e); void set_ptags(const Element& e); /** * Convert to XrlAtomList of xrlatom_uint32's * * @return XrlAtomList representation. */ XrlAtomList xrl_atomlist() const; /** * Insert policy tags from another PolicyTags. * * @param pt PolicyTags to insert. */ void insert(const PolicyTags& pt); void insert(uint32_t tag); /** * Check if intersection is not empty. * * @return true if atleast one tag is contained in the other tags. * @param tags tags to check with. */ bool contains_atleast_one(const PolicyTags& tags) const; private: typedef set Set; Set _tags; uint32_t _tag; }; #endif // __POLICY_BACKEND_POLICYTAGS_HH__ xorp/policy/backend/backend.y0000664000076400007640000001077111421137511016331 0ustar greearbgreearb%{ /* * yacc -d -p yy_policy_backend_parser -o yacc.yy_policy_backend_parser.cc backend.y */ #include #include "libxorp/xorp.h" #include "policy/common/varrw.hh" #include "policy/common/element_factory.hh" #include "policy/common/operator.hh" #include "policy_backend_parser.hh" #include "instruction.hh" #include "term_instr.hh" #include "policy_instr.hh" extern int yylex(void); extern void yyerror(const char*); using namespace policy_backend_parser; static ElementFactory _ef; %} %union { char* c_str; PolicyInstr* c_pi; }; %token YY_ARG %token YY_NEWLINE YY_BLANK %token YY_POLICY_START YY_POLICY_END YY_TERM_START YY_TERM_END %token YY_PUSH YY_PUSH_SET %token YY_EQ YY_NE YY_LT YY_GT YY_LE YY_GE %token YY_NOT YY_AND YY_OR YY_XOR YY_HEAD YY_CTR YY_NE_INT %token YY_ADD YY_SUB YY_MUL %token YY_ONFALSE_EXIT %token YY_REGEX %token YY_LOAD YY_STORE %token YY_ACCEPT YY_REJECT %token YY_SET YY_NEXT YY_POLICY YY_SUBR_START YY_SUBR_END YY_TERM %type policy %% program: program policy { _yy_policies->push_back($2); } | program subroutine | program set | /* empty */ ; set: YY_SET YY_ARG YY_ARG YY_ARG YY_NEWLINE { // XXX: doesn't delete old (*_yy_sets)[$3] = _ef.create($2, $4); free($2); free($3); free($4); } ; subroutine: YY_SUBR_START YY_NEWLINE policies YY_SUBR_END YY_NEWLINE ; policies: policies policy { (*_yy_subr)[$2->name()] = $2; } | /* empty */ ; policy: YY_POLICY_START YY_ARG YY_NEWLINE terms YY_POLICY_END YY_NEWLINE { PolicyInstr* pi = new PolicyInstr($2,_yy_terms); pi->set_trace(_yy_trace); _yy_trace = false; _yy_terms = new vector(); free($2); $$ = pi; } ; terms: terms YY_TERM_START YY_ARG YY_NEWLINE statements YY_TERM_END YY_NEWLINE { TermInstr* ti = new TermInstr($3,_yy_instructions); _yy_instructions = new vector(); _yy_terms->push_back(ti); free($3); } | /* empty */ ; statements: statements statement YY_NEWLINE | /* empty */ ; statement: YY_PUSH YY_ARG YY_ARG { Instruction* i = new Push(_ef.create($2,$3)); _yy_instructions->push_back(i); free($2); free($3); } | YY_PUSH_SET YY_ARG { _yy_instructions->push_back(new PushSet($2)); free($2); } | YY_ONFALSE_EXIT { _yy_instructions->push_back(new OnFalseExit()); } | YY_LOAD YY_ARG { char* err = NULL; bool is_error = false; VarRW::Id id = strtoul($2, &err, 10); if ((err != NULL) && (*err != '\0')) is_error = true; free($2); if (is_error) { yyerror("Need numeric var ID"); YYERROR; } _yy_instructions->push_back(new Load(id)); } | YY_STORE YY_ARG { char* err = NULL; bool is_error = false; VarRW::Id id = strtoul($2, &err, 10); if ((err != NULL) && (*err != '\0')) is_error = true; free($2); if (is_error) { yyerror("Need numeric var ID"); YYERROR; } if (id == VarRW::VAR_TRACE) _yy_trace = true; _yy_instructions->push_back(new Store(id)); } | YY_ACCEPT { _yy_instructions->push_back(new Accept()); } | YY_REJECT { _yy_instructions->push_back(new Reject()); } | YY_EQ { _yy_instructions->push_back(new NaryInstr(new OpEq)); } | YY_NE { _yy_instructions->push_back(new NaryInstr(new OpNe)); } | YY_LT { _yy_instructions->push_back(new NaryInstr(new OpLt)); } | YY_GT { _yy_instructions->push_back(new NaryInstr(new OpGt)); } | YY_LE { _yy_instructions->push_back(new NaryInstr(new OpLe)); } | YY_GE { _yy_instructions->push_back(new NaryInstr(new OpGe)); } | YY_NOT { _yy_instructions->push_back(new NaryInstr(new OpNot)); } | YY_AND { _yy_instructions->push_back(new NaryInstr(new OpAnd)); } | YY_XOR { _yy_instructions->push_back(new NaryInstr(new OpXor)); } | YY_OR { _yy_instructions->push_back(new NaryInstr(new OpOr)); } | YY_ADD { _yy_instructions->push_back(new NaryInstr(new OpAdd)); } | YY_SUB { _yy_instructions->push_back(new NaryInstr(new OpSub)); } | YY_MUL { _yy_instructions->push_back(new NaryInstr(new OpMul)); } | YY_HEAD { _yy_instructions->push_back(new NaryInstr(new OpHead));} | YY_CTR { _yy_instructions->push_back(new NaryInstr(new OpCtr));} | YY_NE_INT { _yy_instructions->push_back(new NaryInstr(new OpNEInt));} | YY_REGEX { _yy_instructions->push_back(new NaryInstr(new OpRegex));} | YY_NEXT YY_POLICY { _yy_instructions->push_back(new Next(Next::POLICY)); } | YY_NEXT YY_TERM { _yy_instructions->push_back(new Next(Next::TERM)); } | YY_POLICY YY_ARG { _yy_instructions->push_back(new Subr($2)); free($2); } ; %% xorp/policy/backend/set_manager.hh0000664000076400007640000000457211540225532017363 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/policy/backend/set_manager.hh,v 1.9 2008/10/02 21:58:04 bms Exp $ #ifndef __POLICY_BACKEND_SET_MANAGER_HH__ #define __POLICY_BACKEND_SET_MANAGER_HH__ #include "policy/common/element_base.hh" #include "policy/common/policy_exception.hh" /** * @short Class that owns all sets. It resolves set names to ElemSet's. * * Ideally, if the contents of a set changes, a filter should not be * reconfigured, but only the sets. This is currently not the case, but there is * enough structure to allow it. */ class SetManager : public NONCOPYABLE { public: typedef map SetMap; /** * @short Exception thrown when a set with an unknown name is requested. */ class SetNotFound : public PolicyException { public: SetNotFound(const char* file, size_t line, const string& init_why = "") : PolicyException("SetNotFound", file, line, init_why) {} }; SetManager(); ~SetManager(); /** * Return the corresponding ElemSet for the requested set name. * * @return the ElemSet requested. * @param setid name of set wanted. */ const Element& getSet(const string& setid) const; /** * Resplace all sets with the given ones. * Caller must not delete them. * * @param sets the new sets that should be used. */ void replace_sets(SetMap* sets); /** * Zap all sets. */ void clear(); private: SetMap* _sets; }; #endif // __POLICY_BACKEND_SET_MANAGER_HH__ xorp/policy/code.hh0000664000076400007640000001535511540224233014417 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/policy/code.hh,v 1.13 2008/10/02 21:57:57 bms Exp $ #ifndef __POLICY_CODE_HH__ #define __POLICY_CODE_HH__ #include "policy/common/filter.hh" typedef map SUBR; // XXX string* would be better... /** * @short This class represents the intermediate language code. * * It contains the actual code for the policy, its target, and names of the * sets referenced. It also contains the policytags referenced. */ class Code { public: /** * @short A target represents where the code should be placed. * * A target consists of a protocol and a filter type. It identifies exactly * which filter of which protocol has to be configured with this code. */ class Target { public: /** * Default constructor. */ Target() {} /** * Construct a target [protocol/filter pair]. * * @param p target protocol. * @param f target filter. */ Target(const string& p, filter::Filter f) : _protocol(p), _filter(f) {} /** * Operator to compare Targets. Needed for STL set storage. * * @return true if target is less than argument * @param rhs target to compare with */ bool operator<(const Target& rhs) const; bool operator==(const Target& rhs) const; bool operator!=(const Target& rhs) const; /** * Get the protocol. * * @return the protocol. */ const string protocol() const { return _protocol; } /** * Set the protocol. * * @param protocol the protocol name. */ void set_protocol(const string& protocol) { _protocol = protocol; } /** * Get the filter type. * * @return the filter type. */ filter::Filter filter() const { return _filter; } /** * Set the filter type. * * @param filter the filter type. */ void set_filter(const filter::Filter& filter) { _filter = filter; } /** * @return string representation of target. */ string str() const; private: string _protocol; filter::Filter _filter; }; typedef set TargetSet; // if it is export filter code, these are the tags used by the code. typedef set TagSet; /** * Get the target. * * @return a reference to the target. */ const Code::Target& target() const { return _target; } /** * Set the target. * * @param target the target. */ void set_target(const Code::Target target) { _target = target; } /** * Set the target protocol. * * @param protocol the target protocol name. */ void set_target_protocol(const string& protocol); /** * Set the target filter type. * * @param filter the target filter type. */ void set_target_filter(const filter::Filter& filter); /** * Get the actual code. * * @return a reference to the actual code. */ const string& code() const { return _code; } /** * Set the actual code. * * @param code the actual code. */ void set_code(const string& code) { _code = code; } /** * Add to the actual code. * * @param code the code to add. */ void add_code(const string& code) { _code += code; } /** * Get the names of the sets referenced by this code. * * @return a reference to the names of the sets referenced by this code. */ const set referenced_set_names() const { return _referenced_set_names; } /** * Set the names of the sets referenced by this code. * * @param set_names the names of the sets referenced by this code. */ void set_referenced_set_names(const set& set_names) { _referenced_set_names = set_names; } /** * Add the name of a set referenced by this code. * * @param set_name the name of the set referenced by this code. */ void add_referenced_set_name(const string& set_name) { _referenced_set_names.insert(set_name); } /** * Remove the names of all sets referenced by this code. */ void clear_referenced_set_names() { _referenced_set_names.clear(); } /* * Get the set of source protocols. * * @return a reference to the set of source protocols. */ const set& source_protocols() const { return _source_protocols; } /** * Add a source protocol. * * @param protocol the protocol to add. */ void add_source_protocol(const string& protocol) { _source_protocols.insert(protocol); } /** * Get the set with all tags. * * @return a reference to the set with all tags. */ const Code::TagSet& all_tags() const { return _all_tags; } /** * Get the set with the tags used for route redistribution to other * protocols. * * @return a reference to the set with tags used for route redistribution. */ const Code::TagSet& redist_tags() const { return _redist_tags; } /** * Add a tag. * * @param tag the tag to add. * @param is_redist_tag if true, the tag is used for route redistribution * to other protocols. */ void add_tag(uint32_t tag, bool is_redist_tag) { _all_tags.insert(tag); if (is_redist_tag) _redist_tags.insert(tag); } void add_subr(const string& policy, const string& code); const SUBR& subr() const; /** * @return string representation of code. */ string str(); /** * Appends code to current code. It enables for chunks of code to be * linked. * * @return reference to the updated code. * @param rhs code to link. */ Code& operator +=(const Code& rhs); private: Target _target; string _code; set _referenced_set_names; // if it is an export filter code, these are source protocols which will // send routes to the target set _source_protocols; TagSet _all_tags; TagSet _redist_tags; // The tags used for route redistribution SUBR _subr; }; #endif // __POLICY_CODE_HH__ xorp/policy/xrl_target.cc0000664000076400007640000002314111421137511015636 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "policy_module.h" #include "libxorp/xorp.h" #include "libxorp/status_codes.h" #include "policy/common/policy_utils.hh" #include "xrl_target.hh" using namespace policy_utils; XrlPolicyTarget::XrlPolicyTarget(XrlStdRouter* r, PolicyTarget& ptarget) : XrlPolicyTargetBase(r), _policy_target(ptarget) { } XrlCmdError XrlPolicyTarget::common_0_1_get_target_name( // Output values, string& name) { name = PolicyTarget::policy_target_name; return XrlCmdError::OKAY(); } XrlCmdError XrlPolicyTarget::common_0_1_get_version( // Output values, string& version) { version = "0.1"; return XrlCmdError::OKAY(); } XrlCmdError XrlPolicyTarget::common_0_1_get_status( // Output values, uint32_t& status, string& reason) { if(_policy_target.running()) { status = PROC_READY; reason = "running"; } else { status = PROC_SHUTDOWN; reason = "dying"; } return XrlCmdError::OKAY(); } XrlCmdError XrlPolicyTarget::common_0_1_shutdown() { _policy_target.shutdown(); return XrlCmdError::OKAY(); } XrlCmdError XrlPolicyTarget::policy_0_1_create_term(const string& policy, const string& order, const string& term) { ConfigNodeId config_node_id(ConfigNodeId::ZERO()); try { config_node_id.copy_in(order); } catch (const InvalidString& e) { return XrlCmdError::COMMAND_FAILED("Create of policy " + policy + " term " + term + " failed " + "because of invalid node ID " + "\"" + order + "\" : " + e.str()); } try { _policy_target.create_term(policy, config_node_id, term); } catch(const PolicyException& e) { return XrlCmdError::COMMAND_FAILED("create_term failed: " + e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlPolicyTarget::policy_0_1_delete_term(const string& policy, const string& term) { try { _policy_target.delete_term(policy,term); } catch(const PolicyException& e) { return XrlCmdError::COMMAND_FAILED("delete_term failed: " + e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlPolicyTarget::policy_0_1_update_term_block(const string& policy, const string& term, const uint32_t& block, const string& order, const string& statement) { ConfigNodeId config_node_id(ConfigNodeId::ZERO()); try { config_node_id.copy_in(order); } catch (const InvalidString& e) { return XrlCmdError::COMMAND_FAILED("Update of policy " + policy + " term " + term + " failed " + "because of invalid node ID " + "\"" + order + "\" : " + e.str()); } try { _policy_target.update_term_block(policy, term, block, config_node_id, statement); } catch(const PolicyException& e) { return XrlCmdError::COMMAND_FAILED("Update of policy " + policy + " term " + term + " failed: " + e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlPolicyTarget::policy_0_1_create_policy(const string& policy) { try { _policy_target.create_policy(policy); } catch(const PolicyException& e) { return XrlCmdError::COMMAND_FAILED("create_policy: " + e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlPolicyTarget::policy_0_1_delete_policy(const string& policy) { try { _policy_target.delete_policy(policy); } catch(const PolicyException& e) { return XrlCmdError::COMMAND_FAILED("delete_policy: " + e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlPolicyTarget::policy_0_1_create_set(const string& set) { try { _policy_target.create_set(set); } catch(const PolicyException& e) { return XrlCmdError::COMMAND_FAILED("create_set: " + e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlPolicyTarget::policy_0_1_update_set(const string& type, const string& set, const string& elements) { try { _policy_target.update_set(type, set, elements); } catch(const PolicyException& e) { return XrlCmdError::COMMAND_FAILED("update_set: " + e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlPolicyTarget::policy_0_1_delete_set(const string& set) { try { _policy_target.delete_set(set); } catch(const PolicyException& e ) { return XrlCmdError::COMMAND_FAILED("delete_set: " + e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlPolicyTarget::policy_0_1_add_to_set( // Input values, const string& type, const string& set, const string& element) { try { _policy_target.add_to_set(type, set, element); } catch(const PolicyException& e) { return XrlCmdError::COMMAND_FAILED("add_to_set: " + e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlPolicyTarget::policy_0_1_delete_from_set( // Input values, const string& type, const string& set, const string& element) { try { _policy_target.delete_from_set(type, set, element); } catch(const PolicyException& e) { return XrlCmdError::COMMAND_FAILED("delete_from_set: " + e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlPolicyTarget::policy_0_1_done_global_policy_conf() { try { _policy_target.commit(0); } catch(const PolicyException& e) { return XrlCmdError::COMMAND_FAILED("Policy configuration failed: " + e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlPolicyTarget::policy_0_1_import(const string& protocol, const string& policies, const string& modifier) { try { _policy_target.update_import(protocol, policies, modifier); } catch (const PolicyException& e) { return XrlCmdError::COMMAND_FAILED("Import of " + protocol + " failed: " + e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlPolicyTarget::policy_0_1_export(const string& protocol, const string& policies, const string& modifier) { try { _policy_target.update_export(protocol, policies, modifier); } catch (const PolicyException& e) { return XrlCmdError::COMMAND_FAILED("Export of " + protocol + " failed: " + e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlPolicyTarget::policy_0_1_add_varmap(const string& protocol, const string& variable, const string& type, const string& access, const uint32_t& id) { try { _policy_target.add_varmap(protocol, variable, type, access, id); } catch(const PolicyException& e) { return XrlCmdError::COMMAND_FAILED("Adding varmap failed for protocol: " + protocol + " var: " + variable + " :" + e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlPolicyTarget::policy_0_1_dump_state(const uint32_t& id, string& state) { try { state = _policy_target.dump_state(id); } catch(const PolicyException& e) { return XrlCmdError::COMMAND_FAILED("Unable to dump state, id: " + to_str(id)); } return XrlCmdError::OKAY(); } XrlCmdError XrlPolicyTarget::policy_0_1_set_proto_target(const string& protocol, const string& target) { _policy_target.set_proto_target(protocol, target); return XrlCmdError::OKAY(); } XrlCmdError XrlPolicyTarget::finder_event_observer_0_1_xrl_target_birth( const string& target_class, const string& target_instance) { try { _policy_target.birth(target_class,target_instance); } catch(const PolicyException& e) { return XrlCmdError::COMMAND_FAILED("Birth announce of " + target_class + " failed: " + e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlPolicyTarget::finder_event_observer_0_1_xrl_target_death( const string& target_class, const string& target_instance) { try { _policy_target.death(target_class,target_instance); } catch(const PolicyException& e) { return XrlCmdError::COMMAND_FAILED("Death announce of " + target_class + " failed: " + e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlPolicyTarget::cli_processor_0_1_process_command( // Input values, const string& processor_name, const string& cli_term_name, const uint32_t& cli_session_id, const string& command_name, const string& /* command_args */, // Output values, string& ret_processor_name, string& ret_cli_term_name, uint32_t& ret_cli_session_id, string& ret_command_output) { try { ret_processor_name = processor_name; ret_cli_term_name = cli_term_name; ret_cli_session_id = cli_session_id; ret_command_output = _policy_target.cli_command(command_name); } catch (const PolicyException& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } return XrlCmdError::OKAY(); } xorp/policy/configuration.hh0000664000076400007640000003136011540225532016351 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/policy/configuration.hh,v 1.20 2008/10/02 21:57:58 bms Exp $ #ifndef __POLICY_CONFIGURATION_HH__ #define __POLICY_CONFIGURATION_HH__ #include "policy/common/policy_exception.hh" #include "process_watch_base.hh" #include "set_map.hh" #include "policy_map.hh" #include "policy_list.hh" #include "filter_manager_base.hh" #include "var_map.hh" typedef list POLICIES; typedef Code::TargetSet TARGETSET; typedef uint32_t tag_t; typedef set TagSet; typedef map RATTR; typedef map RESOURCES; // XXX we go reverse in order to make peer specific policies override global // ones. Global is "" so it's always smallest (first). #define FOR_ALL_POLICIES(n) \ for (PROTOCOL::reverse_iterator i = _protocols.rbegin(); \ i != _protocols.rend(); ++i) \ for (POLICY::reverse_iterator n = i->second->rbegin(); \ n != i->second->rend(); ++n) class IEMap { public: typedef map POLICY; // modifier -> policy list typedef map PROTOCOL; // protocol -> modifiers IEMap(); ~IEMap(); PolicyList* find(const string& proto, const string& mod); void insert(const string& proto, const string& mod, PolicyList* pl); void clear(); void clear(TARGETSET& ts); void get_targets(const string& proto, const string& mod, TARGETSET& targets); void compile(PolicyStatement& ps, TARGETSET& targets, tag_t& tag, map >& ptags); void compile(TARGETSET& targets, tag_t& tag, map >& ptags); void link_code(Code& code); void link_code(const string& proto, Code& code); void get_redist_tags(const string& proto, TagSet& ts); private: POLICY* find_policy(const string& proto); void clear(POLICY* p); PROTOCOL _protocols; }; /** * @short Class that contains all configuration and generated code state. * * This class contains all user policy configuration. It updates the relevant * configuration portions based on user changes. Also, it does some sanity * checking by (dis)allowing the user to do certain actions [such as delete sets * which are referenced in policies]. */ class Configuration : public NONCOPYABLE { public: typedef map CodeMap; typedef map TagMap; /** * @short Exception thrown on configuration error */ class ConfError : public PolicyException { public: ConfError(const char* file, size_t line, const string& init_why = "") : PolicyException("ConfError", file, line, init_why) {} }; /** * @param a process watcher used to initialize the VarMap. */ Configuration(ProcessWatchBase& pw); ~Configuration(); /** * Throws an exception on failure. * Checks for non-existant policy/term conditions. * * @param policy policy in which term should be deleted. * @param term term to delete. */ void delete_term(const string& policy, const string& term); /** * Update the source/dest/action block of a term. * * Throws an exception on failure. * Checks for non-existent policy/term conditions. Also tries to parse the * configuration. No compilation / semantic check is performed now. * * @param policy the name of the policy. * @param term the name of the term. * @param block the block to update (0:source, 1:dest, 2:action). * @param order node ID with position of term. * @param statement the statement to insert. */ void update_term_block(const string& policy, const string& term, const uint32_t& block, const ConfigNodeId& order, const string& statement); /** * Append a term to a policy. * * Throws an exception on failure. * Checks if term already exists. * * @param policy policy in which term should be created. * @param order node ID with position of term. * @param term term name which should be created. */ void create_term(const string& policy, const ConfigNodeId& order, const string& term); /** * Throws an exception on failure. * Checks if policy already exists. * * @param policy policy which should be created. */ void create_policy(const string& policy); /** * Throws an exception on failure. * Checks if policy is in use [instantiated by an export/import directive.] * * @param policy policy which should be deleted. */ void delete_policy(const string& policy); /** * Throws an exception on failure. * Checks if set already exists. * * @param set name of the set to be created. */ void create_set(const string& set); /** * Throws an exception on failure. * Checks if set exists. * * @param type the type of the set. * @param set name of the set to be updated. * @param elements comma separated elements to be replaced in set. */ void update_set(const string& type, const string& set, const string& elements); /** * Throws an exception on failure. * Checks if set is in use. * * @param set name of set to delete. */ void delete_set(const string& set); /** * Add an element to a set. * * Throws an exception on failure. * Checks if set exists. * * @param type the type of the set. * @param name name of the set. * @param element the element to add. */ void add_to_set(const string& type, const string& name, const string& element); /** * Delete an element from a set. * * Throws an exception on failure. * Checks if set exists. * * @param type the type of the set. * @param name name of the set. * @param element the element to delete. */ void delete_from_set(const string& type, const string& name, const string& element); /** * Throws an exception on failure. * Checks if policies exist. * * @param protocol name of protocol which should have imports updated. * @param imports list of policy-names. */ void update_imports(const string& protocol, const POLICIES& imports, const string& mod); /** * Throws an exception on failure. * Checks if policies exist. * * @param protocol name of protocol which should have exports updated. * @param exports list of policy-names. */ void update_exports(const string& protocol, const POLICIES& exports, const string& mod); /** * @return string representation of configuration */ string str(); /** * Commit all configuration changes. * This will compile all needed policies and link them. * It will then commit changes to the actual policy filters. * Commits are optionally delayed in order to aggregate configuration * changes. For example, at boot-up many small changes are done in small * time intervals. It would be more efficient to configure the filters only * after all changes have been made. Thus delaying a commit will help. * * The delay will only be imposed on sending the configuration to the * filters -- all semantic checks and compile is done immediately. * * @param msec milliseconds after which code should be sent to filters. */ void commit(uint32_t msec); /** * Add a variable to the VarMap, needed for semantic checking. * * @param protocol the protocol this variable is available to. * @param variable name of the variable. * @param type the type of the variable. * @param access the permissions on the variable (r/rw). * @param id the id used for VarRW interaction. */ void add_varmap(const string& protocol, const string& name, const string& type, const string& access, const VarRW::Id& id); /** * This method should be called once at initialization to set the * FilterManager. * It should not be deleted by the Configuration class -- it does not own * it. */ void set_filter_manager(FilterManagerBase&); /** * A CodeMap is a map relating protocols to code. All the code for a * protocol will be found in its entry. The code however will normally be * for a specific filter. * * @return the CodeMap for import filters. */ CodeMap& import_filters() { return _import_filters; } /** * @return the CodeMap for source match filters. */ CodeMap& sourcematch_filters() { return _sourcematch_filters; } /** * @return the CodeMap for export filters. */ CodeMap& export_filters() { return _export_filters; } /** * @return the SetMap relating set-name to the actual set. */ SetMap& sets() { return _sets; } /** * @return the policy tag map relating policytags to destination protocols. */ TagMap& tagmap() { return _tagmap; } /** * Dump internal state. Debugging only. * * @param id specifies which aspect of state to dump. * @return human readable state information. */ string dump_state(uint32_t id); void clear_imports(const string& protocol); void clear_exports(const string& protocol); bool test_policy(const string& policy, const RATTR& attrs, RATTR& mods); void show(const string& type, const string& name, RESOURCES& res); void show_sets(const string& type, const string& name, RESOURCES& res); void show_policies(const string& name, RESOURCES& res); private: /** * Throws an exception if no term is found. * * @return term being searched for. * @param policy policy name term should be found in. * @param term term being searched for. */ Term& find_term(const string& policy, const string& term); /** * Scans policy and checks which sets it uses. It also binds the policy to * those sets, so sets may not be deleted. * * @param policy policy which should have set dependencies updated. */ void update_dependencies(PolicyStatement& policy); /** * Generate code for a policy. * Throws an exception on failure. * * @param name name of policy to be compiled. */ void compile_policy(const string& name); /** * Compile all modified and non previously compiled policies. * Throws an exception on failure. */ void compile_policies(); /** * Links all source match filter code for a specific target. * Code is internally kept fragmented [so deleting one policy will not * involve recompiling the whole policy list for a target, for example]. * * @param target target for which code should be linked. */ void link_sourcematch_code(const Code::Target& target); /** * Update the policytags used by a protocol. * * @param protocol protocol for which to update policytags. */ void update_tagmap(const string& protocol); /** * Link code for updated targets. */ void link_code(); void update_ie(const string& protocol, const POLICIES& policies, IEMap& iemap, PolicyList::PolicyType pt, const string& mod); void link_code(const Code::Target& target, IEMap& iemap, CodeMap& codemap); string codemap_str(CodeMap& cm); void policy_modified(const string& policy); typedef set PolicySet; PolicyMap _policies; IEMap _imports; IEMap _exports; SetMap _sets; PolicySet _modified_policies; TARGETSET _modified_targets; ElementFactory _ef; CodeMap _import_filters; CodeMap _sourcematch_filters; CodeMap _export_filters; tag_t _currtag; map > _protocol_tags; TagMap _tagmap; VarMap _varmap; FilterManagerBase* _filter_manager; // do not delete }; #endif // __POLICY_CONFIGURATION_HH__ xorp/policy/process_watch.hh0000664000076400007640000000702711540224233016346 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/policy/process_watch.hh,v 1.9 2008/10/02 21:58:00 bms Exp $ #ifndef __POLICY_PROCESS_WATCH_HH__ #define __POLICY_PROCESS_WATCH_HH__ #include "policy/common/policy_exception.hh" #include "libxipc/xrl_std_router.hh" #include "xrl/interfaces/finder_event_notifier_xif.hh" #include "process_watch_base.hh" #include "pw_notifier.hh" #include "protocol_map.hh" /** * @short Keeps track of which XORP processes of interest are alive. * * The VarMap will register interest in protocols for known protocols with the * ProcessWatch. The ProcessWatch will then register this interest with the * finder. * * Very similar / identical to BGP's process watch. */ class ProcessWatch : public ProcessWatchBase { public: /** * @short Exception thrown on error, such as Xrl failure. */ class PWException : public PolicyException { public: PWException(const char* file, size_t line, const string& init_why = "") : PolicyException("PWException", file, line, init_why) {} }; /** * @param rtr Xrl router to use. * @param pmap protocol map. */ ProcessWatch(XrlStdRouter& rtr, ProtocolMap& pmap); /** * Callback for all Xrl calls. * * @param err possible Xrl error. */ void register_cb(const XrlError& err); /** * Add an interest in a protocol. * * @param proc process of the protocol to add interest for. */ void add_interest(const string& proc); /** * Announce birth of a protocol [process]. * * @param proto protocol that came to life. */ void birth(const string& proto); /** * Announce death of a protocol. * * @param proto protocol that died. */ void death(const string& proto); /** * An exception is thrown if the process watch is not watching the requested * protocol. * * @return true if protocol is alive, false otherwise. * @param proto protocol for which status is requested. */ bool alive(const string& proto); /** * Set an object which will receive birth/death notifications. * * If a previous object was "registered", it will be removed. Only one * object may receive notifications. * * @param notifier object where notifications should be sent. */ void set_notifier(PWNotifier& notifier); private: ProtocolMap& _pmap; set _watching; set _alive; XrlFinderEventNotifierV0p1Client _finder; string _instance_name; // do not delete, we do not own PWNotifier* _notifier; string _finder_name; }; #endif // __POLICY_PROCESS_WATCH_HH__ xorp/policy/configuration.cc0000664000076400007640000004314211540225532016340 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "libxorp/xorp.h" #include "policy_module.h" #include "configuration.hh" #include "visitor_dep.hh" #include "policy/common/policy_utils.hh" #include "visitor_test.hh" #include "visitor_printer.hh" using namespace policy_utils; Configuration::Configuration(ProcessWatchBase& pw) : _currtag(0), _varmap(pw), _filter_manager(NULL) { } Configuration::~Configuration() { _imports.clear(); _exports.clear(); clear_map(_import_filters); clear_map(_sourcematch_filters); clear_map(_export_filters); clear_map(_tagmap); // // XXX: Clear the _policies before the _sets goes out of scope. // Otherwise, the _policies destructor might try to use a reference // to _sets after _sets has been destroyed. // _policies.clear(); } Term& Configuration::find_term(const string& policy, const string& term) { const PolicyStatement& ps = _policies.find(policy); return ps.find_term(term); } void Configuration::delete_term(const string& policy, const string& term) { PolicyStatement& ps = _policies.find(policy); if (ps.delete_term(term)) { // policy needs to be re-compiled [will do so on commit] policy_modified(policy); return; } xorp_throw(ConfError, "TERM NOT FOUND " + policy + " " + term); } void Configuration::update_term_block(const string& policy, const string& term, const uint32_t& block, const ConfigNodeId& order, const string& statement) { Term& t = find_term(policy,term); try { t.set_block(block, order, statement); policy_modified(policy); } catch (const Term::term_syntax_error& e) { string err = "In policy " + policy + ": " + e.why(); xorp_throw(ConfError, err); } } void Configuration::create_term(const string& policy, const ConfigNodeId& order, const string& term) { PolicyStatement& ps = _policies.find(policy); if (ps.term_exists(term)) { xorp_throw(ConfError, "Term " + term + " exists already in policy " + policy); } Term* t = new Term(term); ps.add_term(order, t); policy_modified(policy); } void Configuration::create_policy(const string& policy) { _policies.create(policy,_sets); _modified_policies.insert(policy); } void Configuration::delete_policy(const string& policy) { _policies.delete_policy(policy); // if we manage to delete a policy, it means it is not in use... so we do // not need to send updates to filters. _modified_policies.erase(policy); } void Configuration::create_set(const string& set) { _sets.create(set); } void Configuration::update_set(const string& type, const string& set, const string& elements) { // policies affected will be marked as modified. _sets.update_set(type, set, elements, _modified_policies); } void Configuration::delete_set(const string& set) { // if we manage to delete a set, it is not in use, so no updates are // necessary to filters / configuration. _sets.delete_set(set); } void Configuration::add_to_set(const string& type, const string& set, const string& element) { // policies affected will be marked as modified. _sets.add_to_set(type, set, element, _modified_policies); } void Configuration::delete_from_set(const string& type, const string& set, const string& element) { // policies affected will be marked as modified. _sets.delete_from_set(type, set, element, _modified_policies); } void Configuration::update_imports(const string& protocol, const POLICIES& imports, const string& mod) { // check if protocol exists if (!_varmap.protocol_known(protocol)) xorp_throw(ConfError, "imports: Protocol " + protocol + " unknown"); update_ie(protocol, imports, _imports, PolicyList::IMPORT, mod); _modified_targets.insert(Code::Target(protocol, filter::IMPORT)); } void Configuration::update_exports(const string& protocol, const POLICIES& exports, const string& mod) { // check if protocol exists if(!_varmap.protocol_known(protocol)) xorp_throw(ConfError, "exports: Protocol " + protocol + " unknown"); // XXX: if conf fails we lost tagmap TagMap::iterator i = _tagmap.find(protocol); if(i != _tagmap.end()) { TagSet* ts = (*i).second; delete ts; _tagmap.erase(i); } update_ie(protocol, exports, _exports, PolicyList::EXPORT, mod); // other modified targets [such as sourcematch] will be added as compilation // proceeds. _modified_targets.insert(Code::Target(protocol,filter::EXPORT)); } void Configuration::clear_imports(const string& protocol) { // check if protocol exists if (!_varmap.protocol_known(protocol)) xorp_throw(ConfError, "imports: Protocol " + protocol + " unknown"); _imports.clear(_modified_targets); _modified_targets.insert(Code::Target(protocol, filter::IMPORT)); } void Configuration::clear_exports(const string& protocol) { // check if protocol exists if (!_varmap.protocol_known(protocol)) xorp_throw(ConfError, "imports: Protocol " + protocol + " unknown"); _exports.clear(_modified_targets); _modified_targets.insert(Code::Target(protocol, filter::EXPORT)); } string Configuration::str() { ostringstream conf; /* for(PolicyMap::iterator i = _policies.begin(); i != _policies.end(); ++i) { conf += ((*i).second)->str(); } for(SetMap::iterator i = _sets.begin(); i != _sets.end(); ++i) { conf += "set " + (*i).first + " {\n"; conf += ((*i).second)->str(); conf += "\n}\n"; } for(IEMap::iterator i = _imports.begin(); i != _imports.end(); ++i) { conf += "import " + (*i).first; conf += "\n"; } return conf; */ conf << "IMPORTS:\n"; conf << codemap_str(_import_filters); conf << "SOURCE MATCH:\n"; conf << codemap_str(_sourcematch_filters); conf << "EXPORTS:\n"; conf << codemap_str(_export_filters); conf << "TAGS:\n"; for(TagMap::iterator i = _tagmap.begin(); i != _tagmap.end(); ++i) { const string& protocol = (*i).first; const TagSet& tagset = *((*i).second); conf << protocol << ":"; for(TagSet::const_iterator j = tagset.begin(); j != tagset.end(); ++j) { conf << " " << *j; } conf << "\n"; } conf << "CURRTAG: " << _currtag << endl; return conf.str(); } void Configuration::update_dependencies(PolicyStatement& policy) { // check if used sets & policies exist, and mark dependencies. VisitorDep dep(_sets, _policies); policy.accept(dep); } void Configuration::compile_policy(const string& name) { PolicyStatement& policy = _policies.find(name); // Mark the end of the policy policy.set_policy_end(); // probably is a fresh / modified policy, so update dependencies with sets. update_dependencies(policy); // save old tag to check for integer overflow tag_t old_currtag = _currtag; // go through all the import statements _imports.compile(policy, _modified_targets, _currtag, _protocol_tags); // go through all export statements _exports.compile(policy, _modified_targets, _currtag, _protocol_tags); // integer overflow if (_currtag < old_currtag) // FIXME XLOG_FATAL("The un-avoidable occurred: We ran out of policy tags"); } void Configuration::compile_policies() { // integer overflow check tag_t old_currtag = _currtag; // compile all modified policies for (PolicySet::iterator i = _modified_policies.begin(); i != _modified_policies.end(); ++i) { compile_policy(*i); } _modified_policies.clear(); // compile any import policies that have not yet been compiled. // This is a case if a policy is not modified, but just added to a policy // list. _imports.compile(_modified_targets, _currtag, _protocol_tags); // same for exports. _exports.compile(_modified_targets, _currtag, _protocol_tags); // integer overflow. if (_currtag < old_currtag) { // FIXME XLOG_FATAL("The un-avoidable occurred: We ran out of policy tags"); abort(); } } void Configuration::link_sourcematch_code(const Code::Target& target) { // create empty code but only with target set. // This will allow the += operator of Code to behave properly. [i.e. link // only stuff we really are interested in]. Code* code = new Code(); code->set_target(target); // only export statements have source match code. // go through all of them and link. _exports.link_code(*code); // kill previous CodeMap::iterator i = _sourcematch_filters.find(target.protocol()); if(i != _sourcematch_filters.end()) { delete (*i).second; _sourcematch_filters.erase(i); } // if there is nothing, keep it deleted and empty. if(code->code() == "") delete code; else { _sourcematch_filters[target.protocol()] = code; } } void Configuration::update_tagmap(const string& protocol) { // delete previous tags if present TagMap::iterator tmi = _tagmap.find(protocol); if (tmi != _tagmap.end()) { delete (*tmi).second; _tagmap.erase(tmi); } // Get the redist policytags for the protocol TagSet* tagset = new TagSet(); _exports.get_redist_tags(protocol, *tagset); if (tagset->size()) _tagmap[protocol] = tagset; // if empty, just don't keep anything [no entry at all]. else delete tagset; } void Configuration::link_code() { // go through all modified targets and relink them. for(Code::TargetSet::iterator i = _modified_targets.begin(); i != _modified_targets.end(); ++i) { const Code::Target& t = *i; switch(t.filter()) { case filter::IMPORT: link_code(t,_imports,_import_filters); break; case filter::EXPORT_SOURCEMATCH: link_sourcematch_code(t); break; case filter::EXPORT: link_code(t,_exports,_export_filters); // export policies produce tags, update them. update_tagmap(t.protocol()); break; } // we need a filter manager, and need to inform it modified targets // [which reflect policy filters in protocols]. XLOG_ASSERT(_filter_manager); _filter_manager->update_filter(t); } _modified_targets.clear(); } void Configuration::commit(uint32_t msec) { // recompile and link compile_policies(); link_code(); XLOG_ASSERT(_filter_manager); // flush changes after the delay. [usful when receiving a lot of small // changes... such as boot-up]. _filter_manager->flush_updates(msec); } void Configuration::add_varmap(const string& protocol, const string& variable, const string& type, const string& access, const VarRW::Id& id) { // figure out access... VarMap::Access acc = VarMap::READ; if (access == "rw") acc = VarMap::READ_WRITE; else if (access == "r") acc = VarMap::READ; else if (access == "w") acc = VarMap::WRITE; else xorp_throw(PolicyException, "Unknown access (" + access + ") for protocol: " + protocol + " variable: " + variable); _varmap.add_protocol_variable(protocol, new VarMap::Variable(variable, type, acc, id)); } void Configuration::set_filter_manager(FilterManagerBase& fm) { // cannot reassign XLOG_ASSERT(!_filter_manager); _filter_manager = &fm; } void Configuration::update_ie(const string& protocol, const POLICIES& policies, IEMap& iemap, PolicyList::PolicyType pt, const string& mod) { // create a new policy list PolicyList* pl = new PolicyList(protocol, pt, _policies, _sets, _varmap, mod); // add the policy names to the policy list for (POLICIES::const_iterator i = policies.begin(); i != policies.end(); ++i) { pl->push_back(*i); } // if there were policies, get their targets [no longer have policies] iemap.get_targets(protocol, mod, _modified_targets); // replace policy list iemap.insert(protocol, mod, pl); } void Configuration::link_code(const Code::Target& target, IEMap& iemap, CodeMap& codemap) { // create new code and set target, so code may be linked properly Code* code = new Code(); code->set_target(target); // link the code iemap.link_code(target.protocol(), *code); // erase previous code CodeMap::iterator iter = codemap.find(target.protocol()); if(iter != codemap.end()) { delete (*iter).second; codemap.erase(iter); } // if code is empty [no-op filter] just erase it, and keep no entry. if(code->code() == "") delete code; else codemap[target.protocol()] = code; } string Configuration::codemap_str(CodeMap& cm) { string ret = ""; for(CodeMap::iterator i = cm.begin(); i != cm.end(); ++i) { Code* c= (*i).second; ret += "PROTO: " + (*i).first + "\n"; ret += "CODE: " + c->str() + "\n"; } return ret; } string Configuration::dump_state(uint32_t id) { switch(id) { // dump policies case 0: return _policies.str(); break; // dump varmap case 1: return _varmap.str(); break; // dump sets case 2: return _sets.str(); break; default: xorp_throw(PolicyException, "Unknown state id: " + to_str(id)); } } void Configuration::policy_modified(const string& policy) { _modified_policies.insert(policy); _policies.policy_deps(policy, _modified_policies); } bool Configuration::test_policy(const string& policy, const RATTR& attr, RATTR& mods) { PolicyStatement& ps = _policies.find(policy); VisitorTest test(_sets, _policies, _varmap, attr, mods); ps.accept(test); return test.accepted(); } void Configuration::show(const string& type, const string& name, RESOURCES& res) { if (type == "policy-statement") show_policies(name, res); else show_sets(type, name, res); } void Configuration::show_policies(const string& name, RESOURCES& res) { PolicyMap::KEYS p; _policies.policies(p); for (PolicyMap::KEYS::iterator i = p.begin(); i != p.end(); ++i) { string n = *i; if (!name.empty() && name.compare(n) != 0) continue; PolicyStatement& ps = _policies.find(n); ostringstream oss; VisitorPrinter printer(oss); ps.accept(printer); res[n] = oss.str(); } } void Configuration::show_sets(const string& type, const string& name, RESOURCES& res) { SETS s; _sets.sets_by_type(s, type); for (SETS::iterator i = s.begin(); i != s.end(); ++i) { string n = *i; if (!name.empty() && name.compare(n) != 0) continue; const Element& e = _sets.getSet(n); res[n] = e.str(); } } IEMap::IEMap() { } IEMap::~IEMap() { clear(); } IEMap::POLICY* IEMap::find_policy(const string& protocol) { PROTOCOL::iterator i = _protocols.find(protocol); if (i == _protocols.end()) return NULL; return i->second; } PolicyList* IEMap::find(const string& protocol, const string& mod) { POLICY* p = find_policy(protocol); if (!p) return NULL; POLICY::iterator i = p->find(mod); if (i == p->end()) return NULL; return i->second; } void IEMap::insert(const string& protocol, const string& mod, PolicyList* pl) { POLICY* p = find_policy(protocol); if (!p) { p = new POLICY; _protocols[protocol] = p; } // delete old if there PolicyList* pol = find(protocol, mod); if (pol) delete pol; (*p)[mod] = pl; } void IEMap::clear() { for (PROTOCOL::iterator i = _protocols.begin(); i != _protocols.end(); ++i) { POLICY* p = i->second; clear(p); delete p; } _protocols.clear(); } void IEMap::clear(POLICY* p) { for (POLICY::iterator i = p->begin(); i != p->end(); ++i) delete i->second; p->clear(); } void IEMap::get_targets(const string& proto, const string& mod, TARGETSET& ts) { PolicyList* pl = find(proto, mod); if (!pl) return; pl->get_targets(ts); } void IEMap::compile(PolicyStatement& ps, TARGETSET& ts, tag_t& tag, map >& ptags) { FOR_ALL_POLICIES(j) { PolicyList* p = j->second; p->compile_policy(ps, ts, tag, ptags); } } void IEMap::compile(TARGETSET& ts, tag_t& tag, map >& ptags) { FOR_ALL_POLICIES(j) { PolicyList* p = j->second; p->compile(ts, tag, ptags); } } void IEMap::link_code(Code& code) { FOR_ALL_POLICIES(j) { PolicyList* p = j->second; p->link_code(code); } } void IEMap::link_code(const string& proto, Code& code) { POLICY* p = find_policy(proto); XLOG_ASSERT(p); for (POLICY::reverse_iterator i = p->rbegin(); i != p->rend(); ++i) { PolicyList* pl = i->second; pl->link_code(code); } } void IEMap::get_redist_tags(const string& proto, TagSet& ts) { POLICY* p = find_policy(proto); if (!p) return; for (POLICY::iterator i = p->begin(); i != p->end(); i++) { PolicyList* pl = i->second; pl->get_redist_tags(proto, ts); } } void IEMap::clear(TARGETSET& ts) { FOR_ALL_POLICIES(j) { PolicyList* p = j->second; p->get_targets(ts); } clear(); } xorp/policy/pw_notifier.hh0000664000076400007640000000334611540224233016027 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/policy/pw_notifier.hh,v 1.8 2008/10/02 21:58:00 bms Exp $ #ifndef __POLICY_PW_NOTIFIER_HH__ #define __POLICY_PW_NOTIFIER_HH__ /** * @short Interface which receives notification events from ProcessWatch. * * An object may register to receive notification events with a process watch. * This will enable the object to receive announcements for the death and birth * of a XORP process. */ class PWNotifier { public: virtual ~PWNotifier() {} /** * Method called when a XORP process comes to life. * * @param process process name which was born. */ virtual void birth(const string& process) = 0; /** * Method called when a XORP process dies. * * @param process process name which died. */ virtual void death(const string& process) = 0; }; #endif // __POLICY_PW_NOTIFIER_HH__ xorp/policy/visitor_printer.cc0000664000076400007640000001103311540224234016723 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "policy_module.h" #include "libxorp/xorp.h" #include "visitor_printer.hh" VisitorPrinter::VisitorPrinter(ostream& out) : _out(out) { } const Element* VisitorPrinter::visit(PolicyStatement& ps) { PolicyStatement::TermContainer& terms = ps.terms(); PolicyStatement::TermContainer::iterator i; // Work around bugs in uSTL const char* pss = "policy-statement "; const char* op = " {"; const char* cp = "}"; _out << pss << ps.name() << op << endl; // go throgh all terms for(i = terms.begin(); i != terms.end(); ++i) { (i->second)->accept(*this); } _out << cp << endl; return NULL; } const Element* VisitorPrinter::visit(Term& term) { Term::Nodes& source = term.source_nodes(); Term::Nodes& dest = term.dest_nodes(); Term::Nodes& actions = term.action_nodes(); Term::Nodes::iterator i; // NOTE: const char* casts are to work around bugs in uSTL. _out << (const char*)("\tterm ") << term.name() << (const char*)(" {") << endl; _out << (const char*)("\t\tfrom {") << endl; // do source block for (i = source.begin(); i != source.end(); ++i) { _out << (const char*)("\t\t\t"); (i->second)->accept(*this); _out << (const char*)(";") << endl; } _out << (const char*)("\t\t}") << endl; _out << (const char*)("\t\tto {") << endl; // do dest block for (i = dest.begin(); i != dest.end(); ++i) { _out << (const char*)("\t\t\t"); (i->second)->accept(*this); _out << (const char*)(";") << endl; } _out << (const char*)("\t\t}") << endl; _out << (const char*)("\t\tthen {") << endl; // do action block for (i = actions.begin(); i != actions.end(); ++i) { _out << (const char*)("\t\t\t"); (i->second)->accept(*this); _out << (const char*)(";") << endl; } _out << (const char*)("\t\t}") << endl; _out << (const char*)("\t}") << endl; return NULL; } const Element* VisitorPrinter::visit(NodeUn& node) { // const char* cast works around uSTL bug. _out << node.op().str() << (const char*)(" "); node.node().accept(*this); return NULL; } const Element* VisitorPrinter::visit(NodeBin& node) { node.left().accept(*this); // const char* cast works around uSTL bug. _out << (const char*)(" ") << node.op().str() << (const char*)(" "); node.right().accept(*this); return NULL; } const Element* VisitorPrinter::visit(NodeAssign& node) { _out << node.varid() << (const char*)(" "); if (node.mod()) _out << node.mod()->str(); _out << (const char*)("= "); node.rvalue().accept(*this); return NULL; } const Element* VisitorPrinter::visit(NodeVar& node) { _out << node.val(); return NULL; } const Element* VisitorPrinter::visit(NodeSet& node) { _out << node.setid(); return NULL; } const Element* VisitorPrinter::visit(NodeElem& node) { _out << node.val().str(); return NULL; } const Element* VisitorPrinter::visit(NodeAccept& /* node */) { _out << (const char*)("accept"); return NULL; } const Element* VisitorPrinter::visit(NodeReject& /*node */) { _out << (const char*)("reject"); return NULL; } const Element* VisitorPrinter::visit(NodeProto& node) { _out << (const char*)("protocol ") << node.proto(); return NULL; } const Element* VisitorPrinter::visit(NodeNext& node) { _out << (const char*)("next "); switch (node.flow()) { case NodeNext::POLICY: _out << (const char*)("policy "); break; case NodeNext::TERM: _out << (const char*)("term "); break; } return NULL; } const Element* VisitorPrinter::visit(NodeSubr& node) { _out << (const char*)("policy ") << node.policy(); return NULL; } xorp/policy/SConscript0000664000076400007640000000704411631505660015201 0ustar greearbgreearb# Copyright (c) 2009-2011 XORP, Inc and Others # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # $XORP$ import os Import('env') # XXX Not sure how to proceed here. #subdirs = [ 'tests' ] SConscript([ 'backend/SConscript', 'common/SConscript' ], exports='env') env = env.Clone() is_shared = env.has_key('SHAREDLIBS') env.AppendUnique(CPPPATH = [ '#', '$BUILDDIR', ]) env.PrependUnique(LIBPATH = [ '$BUILDDIR/libfeaclient', '$BUILDDIR/policy/backend', '$BUILDDIR/policy/common', '$BUILDDIR/xrl/interfaces', '$BUILDDIR/xrl/targets', '$BUILDDIR/libproto', '$BUILDDIR/libxipc', '$BUILDDIR/libxorp', '$BUILDDIR/libcomm', '.', ]) env.AppendUnique(LIBS = [ 'xorp_policy', 'xorp_policy_backend', 'xorp_policy_common', 'xst_policy', 'xif_policy_backend', 'xif_finder_event_notifier', 'xif_rib', 'xorp_ipc', 'xorp_core', 'xorp_proto', 'xorp_comm' ]) if not is_shared: env.AppendUnique(LIBS = [ "crypto", ]) if not (env.has_key('mingw') and env['mingw']): env.AppendUnique(LIBS = [ "rt", ]) if (env.has_key('mingw') and env['mingw']): env.AppendUnique(LIBS = [ 'ws2_32', 'iphlpapi', 'regex', 'winmm', ]) env.Append(LIBS = ['xorp_core']) env.Replace(RPATH = [ env.Literal(env['xorp_module_rpath']) ]) libxorp_policy_srcs = [ 'code.cc', 'code_generator.cc', 'code_list.cc', 'configuration.cc', 'dependency.cc', 'export_code_generator.cc', 'filter_manager.cc', 'parser.cc', 'policy_list.cc', 'policy_map.cc', 'policy_statement.cc', 'policy_target.cc', 'process_watch.cc', 'protocol_map.cc', 'semantic_varrw.cc', 'set_map.cc', 'source_match_code_generator.cc', 'term.cc', 'test_varrw.cc', 'var_map.cc', 'visitor_dep.cc', 'visitor_printer.cc', 'visitor_semantic.cc', 'visitor_test.cc', 'xrl_target.cc', 'yacc.yy_policy_parser.cc', 'lex.yy_policy_parser.cc' ] ###### if is_shared: libxorp_policy = env.SharedLibrary(target = 'libxorp_policy', source = libxorp_policy_srcs, LIBS = '') if env['rtld_origin']: for obj in libxorp_policy: env.AddPostAction(libxorp_policy, env.Symlink(obj.abspath, os.path.join(env['xorp_alias_libdir'], str(obj)))) env.Alias('install', env.InstallLibrary(env['xorp_libdir'], libxorp_policy)) else: libxorp_policy = env.StaticLibrary(target = 'libxorp_policy', source = libxorp_policy_srcs, LIBS = '') ###### policysrcs = [ 'xorp_policy.cc', ] policy = env.Program(target = 'xorp_policy', source = policysrcs) env.Alias('install', env.InstallProgram(env['xorp_moduledir'], policy)) Default(libxorp_policy, policy) xorp/policy/visitor_dep.cc0000664000076400007640000000731011540224233016012 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "policy_module.h" #include "libxorp/xorp.h" #include "visitor_dep.hh" VisitorDep::VisitorDep(SetMap& setmap, PolicyMap& pmap) : _setmap(setmap), _pmap(pmap) { } const Element* VisitorDep::visit(PolicyStatement& policy) { PolicyStatement::TermContainer& terms = policy.terms(); PolicyStatement::TermContainer::iterator i; // go throgh all terms for (i = terms.begin(); i != terms.end(); ++i) { (i->second)->accept(*this); } commit_deps(policy); return NULL; } const Element* VisitorDep::visit(Term& term) { Term::Nodes& source = term.source_nodes(); Term::Nodes& dest = term.dest_nodes(); Term::Nodes& actions = term.action_nodes(); Term::Nodes::iterator i; // do source block for(i = source.begin(); i != source.end(); ++i) { (i->second)->accept(*this); } // do dest block for(i = dest.begin(); i != dest.end(); ++i) { (i->second)->accept(*this); } // do action block for(i = actions.begin(); i != actions.end(); ++i) { (i->second)->accept(*this); } return NULL; } const Element* VisitorDep::visit(NodeSet& node) { // see if set exists try { _setmap.getSet(node.setid()); // track sets this policy uses _sets.insert(node.setid()); } // it doesn't catch(const PolicyException& e) { ostringstream error; error << "Set not found: " << node.setid() << " at line " << node.line(); xorp_throw(sem_error, error.str()); } return NULL; } const Element* VisitorDep::visit(NodeSubr& node) { string policy = node.policy(); if (!_pmap.exists(policy)) { ostringstream err; err << "Policy not found: " << policy << " at line " << node.line(); xorp_throw(sem_error, err.str()); } _policies.insert(policy); return NULL; } void VisitorDep::commit_deps(PolicyStatement& policy) { policy.set_dependency(_sets, _policies); } const Element* VisitorDep::visit(NodeUn& node) { // check arg node.node().accept(*this); return NULL; } const Element* VisitorDep::visit(NodeBin& node) { // check args node.left().accept(*this); node.right().accept(*this); return NULL; } const Element* VisitorDep::visit(NodeAssign& node) { // check arg node.rvalue().accept(*this); return NULL; } const Element* VisitorDep::visit(NodeVar& /* node */) { return NULL; } const Element* VisitorDep::visit(NodeElem& /* node */) { return NULL; } const Element* VisitorDep::visit(NodeAccept& /* node */) { return NULL; } const Element* VisitorDep::visit(NodeReject& /* node */) { return NULL; } const Element* VisitorDep::visit(NodeProto& /* node */) { return NULL; } const Element* VisitorDep::visit(NodeNext& /* node */) { return NULL; } const set& VisitorDep::sets() const { return _sets; } xorp/policy/semantic_varrw.cc0000664000076400007640000000415211540224233016510 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "policy_module.h" #include "libxorp/xorp.h" #include "policy/common/policy_utils.hh" #include "semantic_varrw.hh" SemanticVarRW::SemanticVarRW(VarMap& vars) : _vars(vars) { } SemanticVarRW::~SemanticVarRW() { policy_utils::clear_container(_trash); } const Element& SemanticVarRW::read(const Id& id) { const VarMap::Variable& var = _vars.variable(_protocol,id); Element* e = _ef.create(var.type,NULL); _trash.insert(e); return *e; } void SemanticVarRW::write(const Id& id, const Element& elem) { // this will throw exception on unknown variable const VarMap::Variable& var = _vars.variable(_protocol,id); // check the rw access if(!var.writable()) { ostringstream err; err <<"Trying to write on read-only variable " << (int)(id); xorp_throw(var_error, err.str()); } // type checking if(var.type != elem.type()) { ostringstream err; err << "Trying to assign value of type " << elem.type() << " to " << var.type << " variable " << id; xorp_throw(var_error, err.str()); } } void SemanticVarRW::set_protocol(const string& proto) { _protocol = proto; } void SemanticVarRW::sync() { policy_utils::clear_container(_trash); } xorp/policy/parser.cc0000664000076400007640000000263011421137511014757 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "policy_module.h" #include "libxorp/xorp.h" #include "policy/common/policy_utils.hh" #include "parser.hh" #include "policy_parser.hh" Parser::Nodes* Parser::parse(const Term::BLOCKS& block, const string& text) { Nodes* nodes = new Nodes(); // there was an error if(policy_parser::policy_parse(*nodes, block, text, _last_error)) { // delete semi-parsed tree policy_utils::delete_vector(nodes); return NULL; } return nodes; } string Parser::last_error() { return _last_error; } xorp/policy/common/0000775000076400007640000000000011540225532014446 5ustar greearbgreearbxorp/policy/common/element_base.cc0000664000076400007640000000324411421137511017400 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "policy/policy_module.h" #include "libxorp/xorp.h" #include "element_base.hh" #include "libxorp/xlog.h" #include "policy_exception.hh" #include "element.hh" Element::~Element() { } Element::Element(Hash hash) : _refcount(1), _hash(hash) { if (_hash >= HASH_ELEM_MAX) xorp_throw(PolicyException, "Too many elems for dispatcher---find a better hashing mechanism\n"); } // TODO do a proper refcount implementation, factory, object reuse, etc. void Element::ref() const { _refcount++; XLOG_ASSERT(_refcount); } void Element::unref() { XLOG_ASSERT(_refcount > 0); _refcount--; if (_refcount == 0) delete this; } uint32_t Element::refcount() const { return _refcount; } Element::Hash Element::hash() const { return _hash; } xorp/policy/common/register_elements.hh0000664000076400007640000000300711421137511020504 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/policy/common/register_elements.hh,v 1.8 2008/10/02 21:58:07 bms Exp $ #ifndef __POLICY_COMMON_REGISTER_ELEMENTS_HH__ #define __POLICY_COMMON_REGISTER_ELEMENTS_HH__ /** * @short Does the initial registration of ElementFactory callbacks. * * This class only has a constructor which registers callbacks. It is used to * ensure that callbacks are registered once, and at the correct time. */ class RegisterElements { public: /** * Contains callback registration routines. */ RegisterElements(); private: template void register_element(); }; #endif // __POLICY_COMMON_REGISTER_ELEMENTS_HH__ xorp/policy/common/register_elements.cc0000664000076400007640000000410411421137511020471 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "libxorp/xorp.h" #include "register_elements.hh" #include "element_factory.hh" #include "element.hh" #include "elem_set.hh" #include "elem_null.hh" #include "elem_bgp.hh" RegisterElements::RegisterElements() { register_element(); register_element(); register_element(); register_element(); register_element(); register_element(); register_element(); register_element(); register_element(); register_element(); register_element(); register_element(); register_element(); register_element(); register_element(); register_element(); register_element(); register_element(); register_element(); register_element(); } // I love templates =D [and C++] template void RegisterElements::register_element() { static ElementFactory ef; struct Local { static Element* create(const char* x) { return new T(x); } }; ef.add(T::id, &Local::create); } xorp/policy/common/varrw.hh0000664000076400007640000001053611540224233016132 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/policy/common/varrw.hh,v 1.19 2008/10/02 21:58:07 bms Exp $ #ifndef __POLICY_BACKEND_VARRW_HH__ #define __POLICY_BACKEND_VARRW_HH__ #include "element_base.hh" /** * @short Interface used by policy filters to execute a policy on a route. * * It deals with reading and writing field/variables/attributes of a route [such * as nexthop, metric and so on]. * * A routing protocol must implement this interface in order to support policy * filtering. * */ class VarRW { public: typedef int Id; enum { VAR_TRACE = 0, VAR_POLICYTAGS, VAR_FILTER_IM, VAR_FILTER_SM, VAR_FILTER_EX, VAR_TAG = 5, VAR_PROTOCOL = 10, // protocol specific vars start here VAR_MAX = 32 // must be last }; VarRW(); virtual ~VarRW(); /** * Read a variable from a route [such as nexthop]. * * If the protocol doesn't support the requested variable, and exception * should be thrown. * * If the variable is not present in the current route, then an ElemNull * should be returned [for example if ipv6 is requested on a v4 route]. * * VarRW is responsible for deleting the object read [it owns it]. However * care must be taken not to delete objects that were obtained by write() * even though we pass them to read() later. * * @return Element requested, or ElemNull of element is not available. * @param id The variable that is being requested [such as metric]. * */ virtual const Element& read(const Id& id) = 0; /** * Write a variable to a route. * * VarRW does not own Element, so it must not delete it. * * @param id Identifier of variable that must be written to. * @param e Value that must be written to the variable. * */ virtual void write(const Id& id, const Element& e) = 0; /** * VarRW must perform all pending writes to the route now. * * This is usefull in scenarios where VarRW decides to cache read and writes * and perform the actual writes at the end [i.e. it stores pointers to * elements]. * * All pointers to elements [by write] may become invalid after a sync. * */ virtual void sync(); /** * Enable/disable generating trace strings / output. * */ void enable_trace(bool on); /** * Support for tracing reads. Executor will call this. * This call will then call read() * * @param id variable to read. * @return variable desired. */ const Element& read_trace(const Id& id); /** * Support for tracing writes. Executor will call this. * This will then call write() * * @ param id variable to write to. * @param e value to write. */ void write_trace(const Id& id, const Element& e); /** * Obtain the final trace value. Should be called after executing the * policy in case it changes. * * @return trace value. * */ uint32_t trace(); /** * Obtain the actual trace from the varrw. * * @return string representation of what was read and written. */ string tracelog(); /** * Obtain any VarRW specific traces. * * @return string representation of specific VarRW traces. */ virtual string more_tracelog(); void reset_trace(); private: bool _do_trace; uint32_t _trace; ostringstream _tracelog; }; #endif // __POLICY_BACKEND_VARRW_HH__ xorp/policy/common/elem_set.cc0000664000076400007640000001401411540224233016547 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "libxorp/xorp.h" #include "elem_set.hh" #include "policy_utils.hh" template ElemSetAny::ElemSetAny(const Set& val) : ElemSet(_hash), _val(val) { } template ElemSetAny::ElemSetAny(const char* c_str) : ElemSet(_hash) { if (!c_str) return; // create each element in the list set s; policy_utils::str_to_set(c_str, s); for (set::iterator i = s.begin(); i != s.end(); ++i) { const char* str = (*i).c_str(); _val.insert(T(str)); } } template ElemSetAny::ElemSetAny() : ElemSet(_hash) { } template string ElemSetAny::str() const { string s = ""; if (!_val.size()) return s; for (typename Set::const_iterator i = _val.begin(); i != _val.end(); ++i) { s += (*i).str(); s += ","; } // remove last comma s.erase(s.length()-1); return s; } template void ElemSetAny::insert(const T& s) { _val.insert(s); } template void ElemSetAny::insert(const ElemSetAny& s) { _val.insert(s._val.begin(), s._val.end()); } template bool ElemSetAny::operator==(const ElemSetAny& rhs) const { return _val == rhs._val; } template bool ElemSetAny::operator!=(const ElemSetAny& rhs) const { return !(*this == rhs); } template bool ElemSetAny::operator<(const ElemSetAny& rhs) const { const Set& rset = rhs._val; // left has to be smaller if (_val.size() >= rset.size()) return false; // for all elements on left to match, the intersection must be equal to // the left set. Set tmp; set_intersection(_val.begin(), _val.end(), rset.begin(), rset.end(), insert_iterator(tmp, tmp.begin())); return tmp == _val; } template bool ElemSetAny::operator>(const ElemSetAny& rhs) const { return (rhs < *this); } template bool ElemSetAny::operator<=(const ElemSetAny& rhs) const { return (*this < rhs || *this == rhs); } template bool ElemSetAny::operator>=(const ElemSetAny& rhs) const { return (*this > rhs || *this == rhs); } template bool ElemSetAny::operator<(const T& /* rhs */) const { return _val.empty(); } template bool ElemSetAny::operator>(const T& rhs) const { typename Set::const_iterator i = _val.find(rhs); if (i == _val.end()) return false; // left has to have at least 2 elements // [so it has more than 1 elements -- size of rhs]. if (_val.size() < 2) return false; return true; } template bool ElemSetAny::operator==(const T& rhs) const { if (_val.size() != 1) return false; if (_val.find(rhs) == _val.end()) return false; return true; } template bool ElemSetAny::operator!=(const T& rhs) const { if (_val.find(rhs) == _val.end()) return true; return false; } template bool ElemSetAny::operator<=(const T& rhs) const { return (*this < rhs || *this == rhs); } template bool ElemSetAny::operator>=(const T& rhs) const { return (*this > rhs || *this == rhs); } template bool ElemSetAny::nonempty_intersection(const ElemSetAny& rhs) const { Set tmp; set_intersection(_val.begin(), _val.end(), rhs._val.begin(), rhs._val.end(), insert_iterator(tmp,tmp.begin())); return tmp.size(); } template void ElemSetAny::erase(const ElemSetAny& rhs) { // go through all elements and delete ones present for (typename Set::const_iterator i = rhs._val.begin(); i != rhs._val.end(); ++i) { typename Set::iterator j = _val.find(*i); if (j != _val.end()) _val.erase(j); } } template void ElemSetAny::erase(const ElemSet& rhs) { erase(dynamic_cast&>(rhs)); } template typename ElemSetAny::iterator ElemSetAny::begin() { return _val.begin(); } template typename ElemSetAny::iterator ElemSetAny::end() { return _val.end(); } template typename ElemSetAny::const_iterator ElemSetAny::begin() const { return _val.begin(); } template typename ElemSetAny::const_iterator ElemSetAny::end() const { return _val.end(); } template const char* ElemSetAny::type() const { return id; } // define the various sets template <> const char* ElemSetU32::id = "set_u32"; template <> Element::Hash ElemSetU32::_hash = HASH_ELEM_SET_U32; template class ElemSetAny; template <> const char* ElemSetCom32::id = "set_com32"; template <> Element::Hash ElemSetCom32::_hash = HASH_ELEM_SET_COM32; template class ElemSetAny; template <> const char* ElemSetIPv4Net::id = "set_ipv4net"; template <> Element::Hash ElemSetIPv4Net::_hash = HASH_ELEM_SET_IPV4NET; template class ElemSetAny; template <> const char* ElemSetIPv6Net::id = "set_ipv6net"; template <> Element::Hash ElemSetIPv6Net::_hash = HASH_ELEM_SET_IPV6NET; template class ElemSetAny; template <> const char* ElemSetStr::id = "set_str"; template <> Element::Hash ElemSetStr::_hash = HASH_ELEM_SET_STR; template class ElemSetAny; xorp/policy/common/element_base.hh0000664000076400007640000000371511540224233017415 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/policy/common/element_base.hh,v 1.10 2008/10/02 21:58:06 bms Exp $ #ifndef __POLICY_COMMON_ELEMENT_BASE_HH__ #define __POLICY_COMMON_ELEMENT_BASE_HH__ /** * @short Basic object type used by policy engine. * * This element hierarchy is similar to XrlAtom's but exclusive to policy * components. */ class Element { public: typedef unsigned char Hash; Element(Hash hash); virtual ~Element(); /** * Every element must be representable by a string. This is a requirement * to enable the policy manager to send elements to the backend filters via * XRL calls for example. * * @return string representation of the element. */ virtual string str() const = 0; /** * @return string representation of element type. */ virtual const char* type() const = 0; Hash hash() const; // XXX don't use for now... not implemented void ref() const; void unref(); uint32_t refcount() const; private: mutable uint32_t _refcount; Hash _hash; }; #endif // __POLICY_COMMON_ELEMENT_BASE_HH__ xorp/policy/common/policy_utils.cc0000664000076400007640000000675411421137511017505 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "libxorp/xorp.h" #ifdef HAVE_REGEX_H # include #else // ! HAVE_REGEX_H # ifdef HAVE_PCRE_H # include # endif # ifdef HAVE_PCREPOSIX_H # include # endif #endif // ! HAVE_REGEX_H #include "policy_utils.hh" namespace policy_utils { void str_to_list(const string& in, list& out) { string::size_type pos1 = 0; // beginning of token string::size_type pos2 = 0; // end of token string::size_type len = in.length(); string token; while(pos1 < len) { // find delimiter pos2 = in.find(",",pos1); // none found, so treat end of string as delim if(pos2 == string::npos) { token = in.substr(pos1,len-pos1); out.push_back(token); return; } // grab token [delimiter found]. token = in.substr(pos1,pos2-pos1); out.push_back(token); pos1 = pos2+1; } } void str_to_set(const string& in, set& out) { list tmp; // XXX: slow str_to_list(in,tmp); for(list::iterator i = tmp.begin(); i != tmp.end(); ++i) // since it is a set, dups will be removed. out.insert(*i); } void read_file(const string& fname, string& out) { char buff[4096]; int rd; string err; // open file FILE* f = fopen(fname.c_str(),"r"); if(!f) { err += "Unable to open file " + fname + ": "; err += strerror(errno); xorp_throw(PolicyUtilsErr, err); } buff[0] = 0; // read file while(!feof(f)) { rd = fread(buff,1,sizeof(buff)-1,f); if(rd == 0) break; if(rd < 0) { err += "Unable to read file " + fname + ": "; err += strerror(errno); fclose(f); xorp_throw(PolicyUtilsErr, err); } // append to content of file to out buff[rd] = 0; out += buff; } fclose(f); return; } unsigned count_nl(const char* x) { const char* end = &x[strlen(x)]; unsigned nl = 0; for(const char* ptr = x; ptr < end; ptr++) if(*ptr == '\n') nl++; return nl; } bool regex(const string& str, const string& reg) { // compile the regex regex_t re; int res = regcomp(&re, reg.c_str(), REG_EXTENDED); if (res) { char tmp[128]; string err; regerror(res, &re, tmp, sizeof(tmp)); regfree(&re); err = "Unable to compile regex (" + reg; err += "): "; err += tmp; xorp_throw(PolicyUtilsErr, err); } // execute the regex [XXX: check for errors!!] bool result = !regexec(&re, str.c_str(), 0, 0, 0); regfree(&re); return result; } } // namespace xorp/policy/common/element.hh0000664000076400007640000003035511540224233016423 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/policy/common/element.hh,v 1.17 2008/10/02 21:58:06 bms Exp $ #ifndef __POLICY_COMMON_ELEMENT_HH__ #define __POLICY_COMMON_ELEMENT_HH__ #include "libxorp/ipv4.hh" #include "libxorp/ipv6.hh" #include "libxorp/ipv4net.hh" #include "libxorp/ipv6net.hh" #include "element_base.hh" #include "policy_exception.hh" #include "policy_utils.hh" #include "policy/policy_module.h" #include "operator_base.hh" enum { HASH_ELEM_INT32 = 1, HASH_ELEM_U32, HASH_ELEM_COM32, HASH_ELEM_STR, HASH_ELEM_BOOL, // 5 HASH_ELEM_IPV4, HASH_ELEM_IPV6, HASH_ELEM_IPV4RANGE, HASH_ELEM_IPV6RANGE, HASH_ELEM_IPV4NET, // 10 HASH_ELEM_IPV6NET, HASH_ELEM_U32RANGE, HASH_ELEM_SET_U32, HASH_ELEM_SET_COM32, HASH_ELEM_SET_IPV4NET, // 15 HASH_ELEM_SET_IPV6NET, HASH_ELEM_SET_STR, HASH_ELEM_NULL, HASH_ELEM_FILTER, HASH_ELEM_ASPATH, // 20 HASH_ELEM_IPV4NEXTHOP, HASH_ELEM_IPV6NEXTHOP, HASH_ELEM_MAX = 32 // must be last }; /** * @short 32bit signed integer. */ class ElemInt32 : public Element { public: /** * The identifier [type] of the element. */ static const char* id; static Hash _hash; ElemInt32() : Element(_hash) {} /** * Construct via c-style string. * * This is necessary in order to create elements via the ElementFactory. * If c_str is null, then the element is assigned a default value. Null * c_str is used by the semantic checker, to obtain "dummy" elements for * validity checks. * * @param c_str initialize via string, or assign default value if null. */ ElemInt32(const char* c_str) : Element(_hash) { if (c_str) _val = strtol(c_str,NULL,10); else _val = 0; } ElemInt32(const int32_t val) : Element(_hash), _val(val) {} /** * @return string representation of integer */ string str() const { return policy_utils::to_str(_val); } /** * @return value of the element. */ int32_t val() const { return _val; } const char* type() const { return id; } private: int32_t _val; }; /** * @short 32bit unsigned integer. */ class ElemU32 : public Element { public: static const char* id; static Hash _hash; ElemU32() : Element(_hash) {} ElemU32(const char* c_str) : Element(_hash) { if (c_str) _val = strtoul(c_str,NULL,10); else _val = 0; } ElemU32(const uint32_t val) : Element(_hash), _val(val) {} string str() const { return policy_utils::to_str(_val); } uint32_t val() const { return _val; } const char* type() const { return id; } bool operator==(const ElemU32& rhs) const { return _val == rhs._val; } bool operator<(const ElemU32& rhs) const { return _val < rhs._val; } private: uint32_t _val; }; /** * @short 32bit unsigned integer with BGP communities friendly syntax. * "X" -> (uint32_t) X * ":X" -> (uint16_t) X * "X:" -> ((uint16_t) X) << 16 * "X:Y" -> (((uint16_t) X) << 16) + (uint16_t) Y */ class ElemCom32 : public Element { public: static const char* id; static Hash _hash; ElemCom32() : Element(_hash) {} ElemCom32(const char*); // in element.cc ElemCom32(const uint32_t val) : Element(_hash), _val(val) {} string str() const; // in element.cc uint32_t val() const { return _val; } const char* type() const { return id; } bool operator==(const ElemCom32& rhs) const { return _val == rhs._val; } bool operator<(const ElemCom32& rhs) const { return _val < rhs._val; } private: uint32_t _val; }; /** * @short string element. */ class ElemStr : public Element { public: static const char* id; static Hash _hash; ElemStr() : Element(_hash) {} ElemStr(const char* val) : Element(_hash) { if (val) _val = val; else _val = ""; } ElemStr(const string& str) : Element(_hash), _val(str) {} string str() const { return _val; } string val() const { return _val; } const char* type() const { return id; } bool operator==(const ElemStr& rhs) const { return _val == rhs._val; } bool operator<(const ElemStr& rhs) const { return _val < rhs._val; } private: string _val; }; /** * @short boolean element. */ class ElemBool : public Element { public: static const char* id; static Hash _hash; ElemBool() : Element(_hash) {} ElemBool(const char* c_str) : Element(_hash) { if (c_str && (strcmp(c_str,"true") == 0) ) _val = true; else _val = false; } ElemBool(const bool val) : Element(_hash), _val(val) {} string str() const { if (_val) return "true"; else return "false"; } bool val() const { return _val; } const char* type() const { return id; } bool operator==(const ElemBool& rhs) const { return _val == rhs._val; } private: bool _val; }; /** * @short Generic Element wrapper for existing classes. * * Classes being wrapped need to provide a str() method and must have a c-style * string constructor. * They also must support various relational operators such as ==. */ template class ElemAny : public Element { public: /** * @short exception thrown if c-stype string initialization fails. */ class ElemInitError : public PolicyException { public: ElemInitError(const char* file, size_t line, const string& init_why = "") : PolicyException("ElemInitError", file, line, init_why) {} }; static const char* id; static Hash _hash; ElemAny() : Element(_hash), _val() {} ElemAny(const T& val) : Element(_hash), _val(val) {} /** * If the c-style constructor of the wrapped class throws and exception, * it is caught and an ElemInitError exception is thrown. The original * exception is lost. */ ElemAny(const char* c_str) : Element(_hash), _val() { if (c_str) { try { _val = T(c_str); } catch (...) { string err = "Unable to initialize element of type "; err += id; err += " with "; err += c_str; xorp_throw(ElemInitError, err); } } // else leave it to the default value } /** * Invokes the == operator on the actual classes being wrapped. * * @return whether the two values are equal. * @param rhs element to compare with. */ bool operator==(const ElemAny& rhs) const { return _val == rhs._val; } /** * Invoke the < operator in the wrapped class. * * @return whether this is less than argument. * @param rhs element to compare with. */ bool operator<(const ElemAny& rhs) const { return _val < rhs._val; } /** * Invokes the str() method of the actual class being wrapped. * * @return string representation of element. */ string str() const { return _val.str(); } /** * @return the actual object of the class being wrapped. */ const T& val() const { return _val; } const char* type() const { return id; } private: T _val; }; template class ElemRefAny : public Element { public: /** * @short exception thrown if c-stype string initialization fails. */ class ElemInitError : public PolicyException { public: ElemInitError(const char* file, size_t line, const string& init_why = "") : PolicyException("ElemInitError", file, line, init_why) {} }; static const char* id; static Hash _hash; ElemRefAny() : Element(_hash), _val(new T()), _free(true) {} ElemRefAny(const T& val) : Element(_hash), _val(&val), _free(false) {} ElemRefAny(const T& val, bool f) : Element(_hash), _val(&val), _free(f) {} ~ElemRefAny() { if (_free) delete _val; } /** * If the c-style constructor of the wrapped class throws and exception, * it is caught and an ElemInitError exception is thrown. The original * exception is lost. */ ElemRefAny(const char* c_str) : Element(_hash), _val(NULL), _free(false) { if (c_str) { try { _val = new T(c_str); _free = true; } catch(...) { string err = "Unable to initialize element of type "; err += id; err += " with "; err += c_str; xorp_throw(ElemInitError, err); } } // else leave it to the default value else { _val = new T(); _free = true; } } /** * Invokes the == operator on the actual classes being wrapped. * * @return whether the two values are equal. * @param rhs element to compare with. */ bool operator==(const ElemRefAny& rhs) const { return (*_val) == rhs.val(); } /** * Invoke the < operator in the wrapped class. * * @return whether this is less than argument. * @param rhs element to compare with. */ bool operator<(const ElemRefAny& rhs) const { return (*_val) < rhs.val(); } /** * Invokes the str() method of the actual class being wrapped. * * @return string representation of element. */ string str() const { return _val->str(); } /** * @return the actual object of the class being wrapped. */ const T& val() const { return *_val; } const char* type() const { return id; } ElemRefAny(const ElemRefAny& copy) : Element(_hash) { _val = copy._val; _free = copy._free; copy._free = false; } private: ElemRefAny& operator=(const ElemRefAny&); const T* _val; mutable bool _free; }; template class ElemNet : public Element { public: enum Mod { MOD_NONE, MOD_EXACT, MOD_SHORTER, MOD_ORSHORTER, MOD_LONGER, MOD_ORLONGER, MOD_NOT }; static const char* id; static Hash _hash; ElemNet(); ElemNet(const char*); ElemNet(const A&); ElemNet(const ElemNet&); // copyable ~ElemNet(); string str() const; const char* type() const; const A& val() const; static Mod str_to_mod(const char* p); static string mod_to_str(Mod mod); BinOper& op() const; bool operator<(const ElemNet& rhs) const; bool operator==(const ElemNet& rhs) const; #ifdef XORP_USE_USTL ElemNet& operator=(const ElemNet& rhs) { if (this != &rhs) { if (_net) { delete _net; } _net = new A(*rhs._net); _mod = rhs._mod; _op = rhs._op; } return *this; } #else private: ElemNet& operator=(const ElemNet&); // not assignable #endif const A* _net; Mod _mod; mutable BinOper* _op; }; template class ElemNextHop : public Element { public: enum Var { VAR_NONE, VAR_DISCARD, VAR_NEXT_TABLE, VAR_PEER_ADDRESS, VAR_REJECT, VAR_SELF }; static const char* id; static Hash _hash; ElemNextHop(const char*); ElemNextHop(); ElemNextHop(const A& nh); string str() const; const char* type() const; Var var() const; const A& addr() const; const A& val() const; // for relop compatibility private: Var _var; A _addr; }; // User defined types typedef ElemRefAny ElemIPv4; typedef ElemAny ElemIPv6; typedef ElemAny ElemIPv4Range; typedef ElemAny ElemIPv6Range; typedef ElemNet ElemIPv4Net; typedef ElemNet ElemIPv6Net; typedef ElemAny ElemU32Range; typedef ElemNextHop ElemIPv4NextHop; typedef ElemNextHop ElemIPv6NextHop; #endif // __POLICY_COMMON_ELEMENT_HH__ xorp/policy/common/elem_bgp.hh0000664000076400007640000000213011540225532016535 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __POLICY_COMMON_ELEM_BGP_HH__ #define __POLICY_COMMON_ELEM_BGP_HH__ #include "element.hh" #include "bgp/aspath.hh" typedef ElemRefAny ElemASPath; #endif // __POLICY_COMMON_ELEM_BGP_HH__ xorp/policy/common/operator.cc0000664000076400007640000000325711421137511016614 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "libxorp/xorp.h" #include "operator.hh" // Initialization of static members. Oper::Hash OpAnd::_hash = HASH_OP_AND; Oper::Hash OpOr::_hash = HASH_OP_OR; Oper::Hash OpXor::_hash = HASH_OP_XOR; Oper::Hash OpNot::_hash = HASH_OP_NOT; Oper::Hash OpEq::_hash = HASH_OP_EQ; Oper::Hash OpNe::_hash = HASH_OP_NE; Oper::Hash OpLt::_hash = HASH_OP_LT; Oper::Hash OpGt::_hash = HASH_OP_GT; Oper::Hash OpLe::_hash = HASH_OP_LE; Oper::Hash OpGe::_hash = HASH_OP_GE; Oper::Hash OpAdd::_hash = HASH_OP_ADD; Oper::Hash OpSub::_hash = HASH_OP_SUB; Oper::Hash OpMul::_hash = HASH_OP_MUL; Oper::Hash OpRegex::_hash = HASH_OP_REGEX; Oper::Hash OpCtr::_hash = HASH_OP_CTR; Oper::Hash OpNEInt::_hash = HASH_OP_NEINT; Oper::Hash OpHead::_hash = HASH_OP_HEAD; xorp/policy/common/SConscript0000664000076400007640000000362011540225532016461 0ustar greearbgreearb# Copyright (c) 2009-2011 XORP, Inc and Others # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # $XORP$ import os Import('env') env = env.Clone() env.AppendUnique(CPPPATH = [ '#' ]) libpcmsrcs = [ 'dispatcher.cc', 'elem_set.cc', 'element.cc', 'element_base.cc', 'element_factory.cc', 'filter.cc', 'operator.cc', 'policy_utils.cc', 'register_elements.cc', 'register_operations.cc', 'varrw.cc' ] is_shared = env.has_key('SHAREDLIBS') # deal with bgp aspath shared code if is_shared: libpcmsrcs += [ '../../bgp/aspath.cc' ] libpcm = env.SharedLibrary(target = 'libxorp_policy_common', source = libpcmsrcs) if env['rtld_origin']: for obj in libpcm: env.AddPostAction(libpcm, env.Symlink(obj.abspath, os.path.join(env['xorp_alias_libdir'], str(obj)))) env.Alias('install', env.InstallLibrary(env['xorp_libdir'], libpcm)) else: obj_aspath = env.StaticObject(target = 'aspath', source = '../../bgp/aspath.cc') libpcmsrcs += [ obj_aspath ] libpcm = env.StaticLibrary(target = 'libxorp_policy_common', source = libpcmsrcs) Default(libpcm) xorp/policy/common/filter.hh0000664000076400007640000000325311540224233016254 0ustar greearbgreearb// vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/policy/common/filter.hh,v 1.8 2008/10/02 21:58:07 bms Exp $ #ifndef __POLICY_COMMON_FILTER_HH__ #define __POLICY_COMMON_FILTER_HH__ namespace filter { /** * There are three type of filters: * * IMPORT: deals with import filtering. Incoming routes from other routers and * possibly the rib. * * EXPORT_SOURCEMATCH: a filter which tags routes that need to be * redistributed. This filter only modifies policytags. * * EXPORT: Filters outgoing routes from the routing protocols to other routers * and possibly the rib itself. */ enum Filter { IMPORT = 1, EXPORT_SOURCEMATCH = 2, EXPORT = 4 }; /** * @param f filter type to convert to human readable string. * @return string representation of filter name. */ const char* filter2str(const Filter& f); } // namespace #endif // __POLICY_COMMON_FILTER_HH__ xorp/policy/common/policy_exception.hh0000664000076400007640000000345411540224233020347 0ustar greearbgreearb// vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/policy/common/policy_exception.hh,v 1.8 2008/10/02 21:58:07 bms Exp $ #ifndef __POLICY_COMMON_POLICY_EXCEPTION_HH__ #define __POLICY_COMMON_POLICY_EXCEPTION_HH__ #include "libxorp/exceptions.hh" /** * @short Base class for all policy exceptions. * * All policy exceptions have a string representing the error. */ /** * @short Base class for all policy exceptions. * * All policy exceptions have a string representing the error. */ class PolicyException : public XorpReasonedException { public: /** * @param reason the error message */ PolicyException(const char* file, size_t line, const string& init_why = "") : XorpReasonedException("PolicyException", file, line, init_why) {} PolicyException(const char* type, const char* file, size_t line, const string& init_why = "") : XorpReasonedException(type, file, line, init_why) {} virtual ~PolicyException() {} }; #endif // __POLICY_COMMON_POLICY_EXCEPTION_HH__ xorp/policy/common/policy_utils.hh0000664000076400007640000001030511540224233017502 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/policy/common/policy_utils.hh,v 1.11 2008/10/02 21:58:07 bms Exp $ #ifndef __POLICY_COMMON_POLICY_UTILS_HH__ #define __POLICY_COMMON_POLICY_UTILS_HH__ #include "policy_exception.hh" /** * Some generic utility functions used by different policy components. */ namespace policy_utils { /** * @short Generic exception for errors */ class PolicyUtilsErr : public PolicyException { public: PolicyUtilsErr(const char* file, size_t line, const string& init_why = "") : PolicyException("PolicyUtilsErr", file, line, init_why) {} }; /** * Deletes a vector by deleting objects and deleting the vector itself. * It checks if objects are null and skips them if so. Also checks if vector * itself is null. * * @param v vector to delete */ template void delete_vector(vector* v) { if(!v) return; for(typename vector::iterator i = v->begin(); i != v->end(); ++i) { if(*i) delete *i; } delete v; } /** * Deletes objects in container. * Checks if objects are null and skips them if so. * * The container is cleared at the end. * * @param l container to be deleted and cleared. */ template void clear_container(T& l) { for(typename T::iterator i = l.begin(); i != l.end(); ++i) { if(*i) delete *i; } l.clear(); } /** * Delets objects of a map and clears the map at the end. * Checks for null objects. * * @param m map to be deleted and cleared. */ template void clear_map(map& m) { for(typename map::iterator i = m.begin(); i != m.end(); ++i) { if((*i).second) delete (*i).second; } m.clear(); } /** * Delets objects of a map-like container and clears the map at the end. * Checks for null objects. * * @param m map-like container to be deleted and cleared. */ template void clear_map_container(T& m) { for(typename T::iterator i = m.begin(); i != m.end(); ++i) { if((*i).second) delete (*i).second; } m.clear(); } /** * Converts a string in the form "1,2,...,n" to a list of strings. * * @param in input string. * @param out output list. */ void str_to_list(const string& in, list& out); /** * Converts a string in the form "1,2,...,n" to a set of strings. * * @param in input string. * @param out output set. */ void str_to_set(const string& in, set& out); /** * Converts an object to a string via an ostringstream. * * @return string representation of object. * @param x object to convert to string. */ template string to_str(T x) { ostringstream oss; oss << x; return oss.str(); } /** * Reads a file into a string. * An exception is thrown on error. * * @param fname filename to read. * @param out output string which will be filled with file content. */ void read_file(const string& fname, string& out); /** * Count the occurences of newlines in the c-style string. * * @return the number of new lines in the string. * @param x the 0 terminated c-style string to count new lines in. */ unsigned count_nl(const char* x); /** * Match a regex. * * @param str input string to check. * @param reg regular expression used for matching. * @return true if string matches regular expression */ bool regex(const string& str, const string& reg); }; #endif // __POLICY_COMMON_POLICY_UTILS_HH__ xorp/policy/common/elem_filter.hh0000664000076400007640000000273711540225532017267 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __POLICY_COMMON_ELEM_FILTER_HH__ #define __POLICY_COMMON_ELEM_FILTER_HH__ #include "policy/backend/policy_filter.hh" #include "element_base.hh" #include "libxorp/ref_ptr.hh" /** * @short a filter element. Used when versioning. */ class ElemFilter : public Element { public: static Hash _hash; ElemFilter(const RefPf& pf) : Element(_hash), _pf(pf) {} string str() const { return "policy filter"; } const RefPf& val() const { return _pf; } const char* type() const { return "filter"; } private: RefPf _pf; }; #endif // __POLICY_COMMON_ELEM_FILTER_HH__ xorp/policy/common/dispatcher.cc0000664000076400007640000000560611540224233017107 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "libxorp/xorp.h" #ifndef XORP_USE_USTL #include #endif #include "dispatcher.hh" #include "elem_null.hh" #include "policy_utils.hh" #include "operator.hh" #include "element.hh" #include "register_operations.hh" // init static members Dispatcher::Value Dispatcher::_map[32768]; Dispatcher::Dispatcher() { } Element* Dispatcher::run(const Oper& op, unsigned argc, const Element** argv) const { XLOG_ASSERT(op.arity() == argc); unsigned key = 0; key |= op.hash(); XLOG_ASSERT(key); // check for null arguments and special case them: return null for (unsigned i = 0; i < argc; i++) { const Element* arg = argv[i]; unsigned char h = arg->hash(); XLOG_ASSERT(h); if(h == ElemNull::_hash) return new ElemNull(); key |= h << (5*(argc-i)); } // check for constructor if (argc == 2 && typeid(op) == typeid(OpCtr)) { string arg1type = argv[1]->type(); if (arg1type != ElemStr::id) xorp_throw(OpNotFound, "First argument of ctr must be txt type, but is: " + arg1type); const ElemStr& es = dynamic_cast(*argv[1]); return operations::ctr(es, *(argv[0])); } // find function Value funct = _map[key]; // expand args and execute function switch(argc) { case 1: XLOG_ASSERT(funct.un); return funct.un(*(argv[0])); case 2: XLOG_ASSERT(funct.bin); return funct.bin(*(argv[1]),*(argv[0])); // the infrastructure is ready however. default: xorp_throw(OpNotFound, "Operations of arity: " + policy_utils::to_str(argc) + " not supported"); } // unreach } Element* Dispatcher::run(const UnOper& op, const Element& arg) const { static const Element* argv[1]; argv[0] = &arg; // execute generic run return run(op, 1, argv); } Element* Dispatcher::run(const BinOper& op, const Element& left, const Element& right) const { static const Element* argv[2]; argv[0] = &right; argv[1] = &left; return run(op, 2, argv); } xorp/policy/common/element_factory.hh0000664000076400007640000000714711540224233020155 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/policy/common/element_factory.hh,v 1.9 2008/10/02 21:58:06 bms Exp $ #ifndef __POLICY_COMMON_ELEMENT_FACTORY_HH__ #define __POLICY_COMMON_ELEMENT_FACTORY_HH__ #include "register_elements.hh" #include "element_base.hh" #include "elem_set.hh" #include "policy_exception.hh" /** * @short A factory for creating elements based on their type. * * Elements are created via their string represented type. They are initialized * via a c-style string. If this string is null, then a default value is * assigned to the element. * * Functions that perform the creation are registered with the factory at * run-time. * * An exception is throw on element creationg failure. * * Similar to Dispatcher. */ class ElementFactory { public: // Function called to create element typedef Element* (*Callback)(const char*); // Container which maps a key to a callback. May consider using a hash table. typedef map Map; ElementFactory(); /** * @short Exception thrown if an Unknown element is being created. * * When creating an element of a type which has no registered creation * callback with the factory. */ class UnknownElement : public PolicyException { public: UnknownElement(const char* file, size_t line, const string& init_why = "") : PolicyException("UnknownElement", file, line, "ElementFactory: unable to create unknown element: " + init_why) {} }; /** * Register a callback with the factory. * * @param key the element id/type. Must be unique. * @param cb function to be called when the element must be created. */ void add(const string& key, Callback cb); /** * Create an element. * * @return the requested element. Caller is responsible for deletion. * @param key the type of element that needs to be created. * @param arg initial value of element. If null, a default is assigned. */ Element* create(const string& key, const char* arg); /** * Checks whether a type exists. * * @param key the element to check for. * @return true if the element can be create via the factory. */ static bool can_create(const string& key); private: /** * There is only one factory map. * * Creating additional factory objects is therefore safe. No need to pass a * global factory around in the various classes. */ static Map _map; /** * A class which registers defined callbacks upon creation. * * Callbacks are thus registered once [static] and before the factory is * actually used. */ static RegisterElements _regelems; }; #endif // __POLICY_COMMON_ELEMENT_FACTORY_HH__ xorp/policy/common/elem_null.hh0000664000076400007640000000332611540224233016744 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/policy/common/elem_null.hh,v 1.10 2008/10/02 21:58:06 bms Exp $ #ifndef __POLICY_COMMON_ELEM_NULL_HH__ #define __POLICY_COMMON_ELEM_NULL_HH__ #include "element_base.hh" /** * @short An element representing nothing. Null. * * This is used by VarRW when an element being read is not available. For * example, if the route is IPv4, but the IPv6 representation is being asked * for. * * The dispatcher also treats Null arguments as a special case by returning a * Null. */ class ElemNull : public Element { public: static const char* id; static Hash _hash; ElemNull() : Element(_hash) {} ElemNull(const char* /* c_str */) : Element(_hash) {} string str() const { return "null"; } const char* type() const { return id; } }; #endif // __POLICY_COMMON_ELEM_NULL_HH__ xorp/policy/common/operator.hh0000664000076400007640000000411711421137511016622 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/policy/common/operator.hh,v 1.11 2008/10/02 21:58:07 bms Exp $ #ifndef __POLICY_COMMON_OPERATOR_HH__ #define __POLICY_COMMON_OPERATOR_HH__ #include "operator_base.hh" #define DEFINE_OPER(name,human,parent) \ class name : public parent { \ public: \ static Hash _hash; \ name() : parent(_hash) {} \ ~name() {} \ string str() const { return #human; } \ Hash hash() const { return _hash; } \ void set_hash(const Hash& x) const { _hash = x; } \ }; #define DEFINE_UNOPER(name,human) \ DEFINE_OPER(name,human,UnOper) #define DEFINE_BINOPER(name,human) \ DEFINE_OPER(name,human,BinOper) // Logical operators DEFINE_BINOPER(OpAnd,AND) DEFINE_BINOPER(OpOr,OR) DEFINE_BINOPER(OpXor,XOR) // Relational operators DEFINE_BINOPER(OpEq,==) DEFINE_BINOPER(OpNe,!=) DEFINE_BINOPER(OpLt,<) DEFINE_BINOPER(OpGt,>) DEFINE_BINOPER(OpLe,<=) DEFINE_BINOPER(OpGe,>=) // Math operators DEFINE_BINOPER(OpAdd,+) DEFINE_BINOPER(OpSub,-) DEFINE_BINOPER(OpMul,*) // Regular expression operator DEFINE_BINOPER(OpRegex,REGEX) DEFINE_BINOPER(OpCtr,CTR) DEFINE_BINOPER(OpNEInt,NON_EMPTY_INTERSECTION) // Unary operators DEFINE_UNOPER(OpNot,NOT) DEFINE_UNOPER(OpHead,HEAD) #endif // __POLICY_COMMON_OPERATOR_HH__ xorp/policy/common/element.cc0000664000076400007640000002234111421137511016405 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "element.hh" #include "elem_null.hh" #include "elem_filter.hh" #include "elem_bgp.hh" #include "policy_exception.hh" #include "operator.hh" // Initialization of static members. // Remember to be unique in id's. const char* ElemInt32::id = "i32"; const char* ElemU32::id = "u32"; const char* ElemCom32::id = "com32"; const char* ElemStr::id = "txt"; const char* ElemBool::id = "bool"; const char* ElemNull::id = "null"; template<> const char* ElemIPv4::id = "ipv4"; template<> const char* ElemIPv4Range::id = "ipv4range"; template<> const char* ElemIPv6::id = "ipv6"; template<> const char* ElemIPv6Range::id = "ipv6range"; template<> const char* ElemIPv4Net::id = "ipv4net"; template<> const char* ElemIPv6Net::id = "ipv6net"; template<> const char* ElemU32Range::id = "u32range"; template<> const char* ElemASPath::id = "aspath"; template<> const char* ElemIPv4NextHop::id = "ipv4nexthop"; template<> const char* ElemIPv6NextHop::id = "ipv6nexthop"; Element::Hash ElemU32::_hash = HASH_ELEM_U32; Element::Hash ElemInt32::_hash = HASH_ELEM_INT32; Element::Hash ElemCom32::_hash = HASH_ELEM_COM32; Element::Hash ElemStr::_hash = HASH_ELEM_STR; Element::Hash ElemBool::_hash = HASH_ELEM_BOOL; Element::Hash ElemNull::_hash = HASH_ELEM_NULL; Element::Hash ElemFilter::_hash = HASH_ELEM_FILTER; template<> Element::Hash ElemIPv4::_hash = HASH_ELEM_IPV4; template<> Element::Hash ElemIPv4Range::_hash = HASH_ELEM_IPV4RANGE; template<> Element::Hash ElemIPv6::_hash = HASH_ELEM_IPV6; template<> Element::Hash ElemIPv6Range::_hash = HASH_ELEM_IPV6RANGE; template<> Element::Hash ElemIPv4Net::_hash = HASH_ELEM_IPV4NET; template<> Element::Hash ElemIPv6Net::_hash = HASH_ELEM_IPV6NET; template<> Element::Hash ElemU32Range::_hash = HASH_ELEM_U32RANGE; template<> Element::Hash ElemASPath::_hash = HASH_ELEM_ASPATH; template<> Element::Hash ElemIPv4NextHop::_hash = HASH_ELEM_IPV4NEXTHOP; template<> Element::Hash ElemIPv6NextHop::_hash = HASH_ELEM_IPV6NEXTHOP; /** * @short Well-known communities per RFC1997 */ static struct { string text; uint32_t value; } com_aliases[] = { { "NO_EXPORT", 0xFFFFFF01 }, { "NO_ADVERTISE", 0xFFFFFF02 }, { "NO_EXPORT_SUBCONFED", 0xFFFFFF03 }, { "", 0 } }; /** * @short Element constructor with a parser for a BGP community syntax. * "N" -> (uint32_t) N * ":N" -> (uint16_t) N * "N:" -> ((uint16_t) N) << 16 * "N:M" -> (((uint16_t) N) << 16) + (uint16_t) M */ ElemCom32::ElemCom32(const char* c_str) : Element(_hash) { // Semantic checker needs this if(c_str == NULL) { _val = 0; return; } int len = strlen(c_str); char *colon = strstr(const_cast(c_str), ":"); if(len > 0 && colon != NULL) { uint32_t msw, lsw; msw = strtoul(c_str, NULL, 0); lsw = strtoul(++colon, NULL, 0); if (msw > 0xffff || lsw > 0xffff) xorp_throw(PolicyException, "uint16_t overflow for community " + string(c_str)); _val = (msw << 16) + lsw; } else { string x = string(c_str); _val = strtoul(c_str, NULL, 0); for(int i = 0; com_aliases[i].text.length(); i++) if (com_aliases[i].text == x) { _val = com_aliases[i].value; break; } } } string ElemCom32::str() const { for(int i = 0; com_aliases[i].text.length(); i++) if (com_aliases[i].value == _val) return com_aliases[i].text; ostringstream oss; oss << (_val >> 16) << ":" << (_val & 0xffff); return (oss.str()); } template ElemNet::ElemNet() : Element(_hash), _net(NULL), _mod(MOD_NONE), _op(NULL) { _net = new A(); } template ElemNet::ElemNet(const char* str) : Element(_hash), _net(NULL), _mod(MOD_NONE), _op(NULL) { if (!str) { _net = new A(); return; } // parse modifier string in = str; const char* p = strchr(str, ' '); if (p) { in = in.substr(0, p - str); _mod = str_to_mod(++p); } // parse net try { _net = new A(in.c_str()); } catch(...) { ostringstream oss; oss << "Can't init " << id << " using " << in; xorp_throw(PolicyException, oss.str()); } } template ElemNet::ElemNet(const A& net) : Element(_hash), _net(NULL), _mod(MOD_NONE), _op(NULL) { _net = new A(net); } template ElemNet::ElemNet(const ElemNet& net) : Element(_hash), _net(net._net), _mod(net._mod), _op(NULL) { if (_net) _net = new A(*_net); } template ElemNet::~ElemNet() { delete _net; } template string ElemNet::str() const { string str = _net->str(); if (_mod != MOD_NONE) { str += " "; str += mod_to_str(_mod); } return str; } template const char* ElemNet::type() const { return id; } template const A& ElemNet::val() const { return *_net; } template bool ElemNet::operator<(const ElemNet& rhs) const { return *_net < *rhs._net; } template bool ElemNet::operator==(const ElemNet& rhs) const { return *_net == *rhs._net; } template typename ElemNet::Mod ElemNet::str_to_mod(const char* p) { string in = p; if (!in.compare("<=") || !in.compare("orlonger")) { return MOD_ORLONGER; } else if (!in.compare("<") || !in.compare("longer")) { return MOD_LONGER; } else if (!in.compare(">") || !in.compare("shorter")) { return MOD_SHORTER; } else if (!in.compare(">=") || !in.compare("orshorter")) { return MOD_ORSHORTER; } else if (!in.compare("!=") || !in.compare("not")) { return MOD_NOT; } else if (!in.compare("==") || !in.compare(":") || !in.compare("exact")) { return MOD_EXACT; } else { string err = "Can't parse modifier: " + in; xorp_throw(PolicyException, err); } // unreach abort(); } template string ElemNet::mod_to_str(Mod mod) { switch (mod) { case MOD_NONE: return ""; case MOD_EXACT: return "=="; case MOD_SHORTER: return ">"; case MOD_ORSHORTER: return ">="; case MOD_LONGER: return "<"; case MOD_ORLONGER: return "<="; case MOD_NOT: return "!="; } // unreach abort(); } template BinOper& ElemNet::op() const { static OpEq EQ; static OpNe NE; static OpLt LT; static OpLe LE; static OpGt GT; static OpGe GE; if (_op) return *_op; switch (_mod) { case MOD_NONE: case MOD_EXACT: _op = &EQ; break; case MOD_NOT: _op = &NE; break; case MOD_SHORTER: _op = > break; case MOD_ORSHORTER: _op = &GE; break; case MOD_LONGER: _op = < break; case MOD_ORLONGER: _op = &LE; break; } XLOG_ASSERT(_op); return op(); } template ElemNextHop::ElemNextHop() : Element(_hash), _var(VAR_NONE) { } template ElemNextHop::ElemNextHop(const A& nh) : Element(_hash), _var(VAR_NONE), _addr(nh) { } template ElemNextHop::ElemNextHop(const char* in) : Element(_hash), _var(VAR_NONE) { if (!in) return; string s = in; if (s.compare("discard") == 0) _var = VAR_DISCARD; else if (s.compare("next-table") == 0) _var = VAR_NEXT_TABLE; else if (s.compare("peer-address") == 0) _var = VAR_PEER_ADDRESS; else if (s.compare("reject") == 0) _var = VAR_REJECT; else if (s.compare("self") == 0) _var = VAR_SELF; else { _var = VAR_NONE; _addr = A(in); } } template string ElemNextHop::str() const { switch (_var) { case VAR_NONE: return _addr.str(); case VAR_DISCARD: return "discard"; case VAR_NEXT_TABLE: return "next-table"; case VAR_PEER_ADDRESS: return "peer-address"; case VAR_REJECT: return "reject"; case VAR_SELF: return "self"; } // unreach XLOG_ASSERT(false); abort(); } template const char* ElemNextHop::type() const { return id; } template typename ElemNextHop::Var ElemNextHop::var() const { return _var; } template const A& ElemNextHop::addr() const { XLOG_ASSERT(_var == VAR_NONE); return _addr; } template const A& ElemNextHop::val() const { return addr(); } // instantiate template class ElemNet; template class ElemNet; template class ElemNextHop; template class ElemNextHop; xorp/policy/common/varrw.cc0000664000076400007640000000364311421137511016121 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "policy/policy_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "varrw.hh" #include "element.hh" VarRW::VarRW() : _do_trace(false), _trace(0) { } VarRW::~VarRW() { } const Element& VarRW::read_trace(const Id& id) { const Element& e = read(id); if (_do_trace) _tracelog << "Read " << id << ": " << e.str() << endl; return e; } void VarRW::write_trace(const Id& id, const Element& e) { if (_do_trace) _tracelog << "Write " << id << ": " << e.str() << endl; // trace is a special variable, not to be implemented by upper layers... if (id == VAR_TRACE) { XLOG_ASSERT(e.type() == ElemU32::id); const ElemU32& u32 = dynamic_cast(e); _trace = u32.val(); return; } write(id, e); } uint32_t VarRW::trace() { return _trace; } string VarRW::tracelog() { return _tracelog.str(); } string VarRW::more_tracelog() { return ""; } void VarRW::reset_trace() { _trace = 0; } void VarRW::enable_trace(bool on) { _do_trace = on; } void VarRW::sync() { } xorp/policy/common/elem_set.hh0000664000076400007640000001307011540224233016562 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/policy/common/elem_set.hh,v 1.14 2008/10/02 21:58:06 bms Exp $ #ifndef __POLICY_COMMON_ELEM_SET_HH__ #define __POLICY_COMMON_ELEM_SET_HH__ #include "element_base.hh" #include "element.hh" class ElemSet : public Element { public: ElemSet(Hash hash) : Element(hash) {} virtual ~ElemSet() {} virtual void erase(const ElemSet&) = 0; }; /** * @short A set of elements. * * All sets hold a string representation of the elements. All type information * will be lost, as elements will all be promoted to strings. */ template class ElemSetAny : public ElemSet { public: typedef set Set; typedef typename Set::iterator iterator; typedef typename Set::const_iterator const_iterator; static const char* id; static Hash _hash; ElemSetAny(const Set& val); /** * @param c_str initialize from string in the form element1,element2,... */ ElemSetAny(const char* c_str); ElemSetAny(); /** * @return string representation of set. */ string str() const; /** * @param s element to insert. */ void insert(const T& s); /** * Insert all elements of other set. * * @param s set to insert. */ void insert(const ElemSetAny& s); /** * Left and right sets are identical [same elements and size]. * * @param rhs set to compare with */ bool operator==(const ElemSetAny& rhs) const; /** * Left and right are not identical * * @param rhs set to compare with */ bool operator!=(const ElemSetAny& rhs) const; /** * All elements on left match, but right has more elments. * * @param rhs set to compare with */ bool operator<(const ElemSetAny& rhs) const; /** * All elements on right match, but left has more elements. * * @param rhs set to compare with */ bool operator>(const ElemSetAny& rhs) const; /** * Left is a subset of right. * * @param rhs set to compare with */ bool operator<=(const ElemSetAny& rhs) const; /** * Right is a subset of left. * * @param rhs set to compare with */ bool operator>=(const ElemSetAny& rhs) const; /** * All elements on left match, but right has more. * * May only be true if left is an empty set. * * @param rhs element to compare with. */ bool operator<(const T& rhs) const; /** * All elements on on right match, but left has more. * * Will be true if the element is present in the set, and the set contains * at least one more element. * * @param rhs element to compare with. */ bool operator>(const T& rhs) const; /** * Left and right are identical. * * Will be true in a single element set which contains the rhs element. * * @param rhs element to compare with. */ bool operator==(const T& rhs) const; /** * Disjoint sets. * * Will be true if element is not contained in set. * * @param rhs element to compare with. */ bool operator!=(const T& rhs) const; /** * Left is a subset of right. * * Will be true if set is empty or contains rhs. * * @param rhs element to compare with. */ bool operator<=(const T& rhs) const; /** * Right is a subset of left. * * Will be true if element is contained in set. * * @param rhs element to compare with. */ bool operator>=(const T& rhs) const; /** * @return true if intersection is not empty */ bool nonempty_intersection(const ElemSetAny& rhs) const; /** * Removes elements in set. * * @param s elements to remove. */ void erase(const ElemSetAny& rhs); void erase(const ElemSet& rhs); /** * Obtain iterator for set. * * @return iterator for the set. */ iterator begin(); /** * Obtain an iterator for the end. * * @return iterator for the end of the set. */ iterator end(); /** * Obtain const iterator for set. * * @return const iterator for the set. */ const_iterator begin() const; /** * Obtain an const iterator for the end. * * @return const iterator for the end of the set. */ const_iterator end() const; const char* type() const; private: Set _val; }; // define set types typedef ElemSetAny ElemSetU32; typedef ElemSetAny ElemSetCom32; typedef ElemSetAny ElemSetIPv4Net; typedef ElemSetAny ElemSetIPv6Net; typedef ElemSetAny ElemSetStr; #endif // __POLICY_COMMON_ELEM_SET_HH__ xorp/policy/common/operator_base.hh0000664000076400007640000000504511540224233017615 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/policy/common/operator_base.hh,v 1.9 2008/10/02 21:58:07 bms Exp $ #ifndef __POLICY_COMMON_OPERATOR_BASE_HH__ #define __POLICY_COMMON_OPERATOR_BASE_HH__ #include "policy/policy_module.h" #include "libxorp/xlog.h" enum { HASH_OP_AND = 1, HASH_OP_OR, HASH_OP_XOR, HASH_OP_NOT, HASH_OP_EQ, HASH_OP_NE, HASH_OP_LT, HASH_OP_GT, HASH_OP_LE, HASH_OP_GE, HASH_OP_ADD, HASH_OP_SUB, HASH_OP_MUL, HASH_OP_REGEX, HASH_OP_CTR, HASH_OP_NEINT, HASH_OP_HEAD, HASH_OP_MAX = 32 // last }; /** * @short Base class for operations. * * An operation is simply an operation that may be done upon elements, such as * addition and comparison. */ class Oper { public: typedef unsigned char Hash; Oper(Hash hash, unsigned arity) : _hash(hash), _arity(arity) { XLOG_ASSERT(_hash < HASH_OP_MAX); } virtual ~Oper() {}; /** * @return number of arguments operation takes */ unsigned arity() const { return _arity; } /** * Must be unique. * * @return string representation of operation. */ virtual string str() const = 0; Hash hash() const { return _hash; } private: Hash _hash; unsigned _arity; }; /** * @short Base class for unary operations. */ class UnOper : public Oper { public: UnOper(Hash hash) : Oper(hash, 1) {} virtual ~UnOper() {}; virtual string str() const = 0; }; /** * @short Base class for binary operations. */ class BinOper : public Oper { public: BinOper(Hash hash) : Oper(hash, 2) {} virtual ~BinOper() {}; virtual string str() const = 0; }; #endif // __POLICY_COMMON_OPERATOR_BASE_HH__ xorp/policy/common/register_operations.cc0000664000076400007640000003713011540224233021045 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "libxorp/xorp.h" #include "register_operations.hh" #include "dispatcher.hh" #include "element.hh" #include "elem_set.hh" #include "elem_null.hh" #include "elem_bgp.hh" #include "operator.hh" #include "element_factory.hh" #include "policy_utils.hh" namespace operations { // Unary operations Element* op_not(const ElemBool& x) { return new ElemBool(!x.val()); } Element* op_head(const ElemStr& x) { const string& str = x.val(); string::size_type pos = str.find(',', 0); // try again if (pos == string::npos) pos = str.find(' ', 0); // return whole thing... [if space and comma not found] return new ElemStr(str.substr(0, pos)); } // operations directly on element value #define DEFINE_BINOP(name,op) \ template \ Element* name(const Left& x, const Right& y) \ { \ return new Result(x.val() op y.val()); \ } // We'd like partial template specialization for functions, but it's not // standard. We special case when a bool is returned and optimize returning // true / false. We return one of these global objects rather than creating a // new one each time. ElemBool _true(true); ElemBool _false(false); Element* return_bool(bool x) { Element* r = x ? &_true : &_false; XLOG_ASSERT(r->refcount() > 1); return r; } #define DEFINE_BINOP_BOOL(name, op) \ template \ Element* name(const Left& x, const Right& y) \ { \ bool val = x.val() op y.val(); \ \ return return_bool(val); \ } DEFINE_BINOP_BOOL(op_and, &&) DEFINE_BINOP_BOOL(op_or, ||) DEFINE_BINOP_BOOL(op_xor, ^) DEFINE_BINOP_BOOL(op_eq, ==) DEFINE_BINOP_BOOL(op_ne, !=) DEFINE_BINOP_BOOL(op_lt, <) DEFINE_BINOP_BOOL(op_gt, >) DEFINE_BINOP_BOOL(op_le, <=) DEFINE_BINOP_BOOL(op_ge, >=) DEFINE_BINOP(op_add,+) DEFINE_BINOP(op_sub,-) DEFINE_BINOP(op_mul,*) // Operations for which .val() is not needed. [operation performed on element // itself]. #define DEFINE_BINOP_NOVAL(name,op) \ template \ Element* \ name(const Left& x, const Right& y) { \ return new Result(x op y); \ } DEFINE_BINOP_NOVAL(op_eq_nv,==) DEFINE_BINOP_NOVAL(op_ne_nv,!=) DEFINE_BINOP_NOVAL(op_lt_nv,<) DEFINE_BINOP_NOVAL(op_gt_nv,>) DEFINE_BINOP_NOVAL(op_le_nv,<=) DEFINE_BINOP_NOVAL(op_ge_nv,>=) // useful for equivalences where we want one class to deal with the operation. // For example we want sets to know about elements, but not elements to know // about sets. // so Element < Set will normally be interpreted as Element::operator< but we do // not want that... so we switch the parameters and obtain // Set::operator>(Element) #define DEFINE_BINOP_SWITCHPARAMS(name,op) \ template \ Element* \ name(const Left& x, const Right& y) \ { \ return new Result(y op x); \ } DEFINE_BINOP_SWITCHPARAMS(op_eq_sw,==) DEFINE_BINOP_SWITCHPARAMS(op_ne_sw,!=) DEFINE_BINOP_SWITCHPARAMS(op_lt_sw,>) DEFINE_BINOP_SWITCHPARAMS(op_gt_sw,<) DEFINE_BINOP_SWITCHPARAMS(op_le_sw,>=) DEFINE_BINOP_SWITCHPARAMS(op_ge_sw,<=) // // Network related operators // template Element* op_lt_net(const Left& x, const Right& y) { bool result; result = (y.val().contains(x.val()) && (y.val() != x.val())); return return_bool(result); } template Element* op_gt_net(const Left& x, const Right& y) { bool result; result = (x.val().contains(y.val()) && (x.val() != y.val())); return return_bool(result); } template Element* op_le_net(const Left& x, const Right& y) { bool result; result = y.val().contains(x.val()); return return_bool(result); } template Element* op_ge_net(const Left& x, const Right& y) { bool result; result = x.val().contains(y.val()); return return_bool(result); } // 2 template parameters because U can be T or ElemSetAny template Element* set_add(const ElemSetAny& s, const U& e) { ElemSetAny* es = new ElemSetAny(); es->insert(s); es->insert(e); return es; } template Element* set_del(const ElemSetAny& s, const ElemSetAny& r) { ElemSetAny* es = new ElemSetAny(); es->insert(s); es->erase(r); return es; } template Element* set_ne_int(const ElemSetAny& l, const ElemSetAny& r) { return new ElemBool(l.nonempty_intersection(r)); } Element* str_add(const ElemStr& left, const ElemStr& right) { return new ElemStr(left.val() + right.val()); } Element* str_mul(const ElemStr& left, const ElemU32& right) { string str = left.val(); string res = ""; uint32_t times = right.val(); for (uint32_t i = 0; i < times; ++i) res.append(str); return new ElemStr(res); } Element* ctr_base(const ElemStr& type, const string& arg_str) { ElementFactory ef; return ef.create(type.val(), arg_str.c_str()); } Element* ctr(const ElemStr& type, const Element& arg) { return ctr_base(type, arg.str()); } template Element* ctr(const ElemStr& type, const T& arg) { return ctr_base(type, arg.str()); } Element* str_regex(const ElemStr& left, const ElemStr& right) { return new ElemBool(policy_utils::regex(left.val(), right.val())); } Element* str_setregex(const ElemStr& left, const ElemSetStr& right) { string str = left.val(); // go through all regexps... // only 1 needs to match... for (ElemSetStr::const_iterator i = right.begin(); i != right.end(); ++i) { const ElemStr& re = *i; if (policy_utils::regex(str, re.val())) return new ElemBool(true); } return new ElemBool(false); } Element* aspath_prepend(const ElemU32& left, const ElemASPath& right) { ASPath* path = new ASPath(right.val()); path->prepend_as(AsNum(left.val())); ElemASPath* ret = new ElemASPath(*path, true); return ret; } Element* aspath_expand(const ElemU32& left, const ElemASPath& right) { ASPath* path = new ASPath(right.val()); if (path->path_length()) { const AsNum& head = path->first_asnum(); unsigned times = left.val(); for (unsigned i = 0; i < times; ++i) path->prepend_as(head); } ElemASPath* ret = new ElemASPath(*path, true); return ret; } Element* aspath_contains(const ElemASPath& left, const ElemU32& right) { return new ElemBool(left.val().contains(AsNum(right.val()))); } Element* aspath_regex(const ElemASPath& left, const ElemStr& right) { return new ElemBool(policy_utils::regex(left.val().short_str(), right.val())); } Element* aspath_regex(const ElemASPath& left, const ElemSetStr& right) { string str = left.val().short_str(); // go through all regexps... // only 1 needs to match... for (ElemSetStr::const_iterator i = right.begin(); i != right.end(); ++i) { const ElemStr& re = *i; if (policy_utils::regex(str, re.val())) return new ElemBool(true); } return new ElemBool(false); } template bool net_match(const ElemNet& left, const ElemNet& right) { static Dispatcher d; Element* r; r = d.run(right.op(), left, right); if (r == &_true) return true; else if (r == &_false) return false; else abort(); } template Element* net_set_match(const ElemNet& left, const ElemSetAny >& right) { bool ret = false; for (typename ElemSetAny >::const_iterator i = right.begin(); i != right.end(); ++i) { const ElemNet& r = *i; if (net_match(left, r)) { ret = true; break; } } return return_bool(ret); } // register callbacks RegisterOperations _regops; } // namespace // XXX: hack to compile on 2.95.x [may not use &operation::op... with templates] using namespace operations; // FIXME: no comment =D // macro ugliness to specify possible operations RegisterOperations::RegisterOperations() { Dispatcher disp; // prevent these from being deleted _true.ref(); _false.ref(); #define ADD_BINOP(result,left,right,funct,oper) \ do { \ disp.add >(Op##oper()); \ } while (0) // EQUAL AND NOT EQUAL #define ADD_EQOP(arg) \ do { \ ADD_BINOP(ElemBool,arg,arg,op_eq,Eq); \ ADD_BINOP(ElemBool,arg,arg,op_ne,Ne); \ } while (0) #define ADD_EQOP2(argl,argr) \ do { \ ADD_BINOP(ElemBool,argl,argr,op_eq,Eq); \ ADD_BINOP(ElemBool,argl,argr,op_ne,Ne); \ } while (0) // RELATIONAL OPERATORS #define ADD_RELOP(arg) \ do { \ ADD_BINOP(ElemBool,arg,arg,op_lt,Lt); \ ADD_BINOP(ElemBool,arg,arg,op_gt,Gt); \ ADD_BINOP(ElemBool,arg,arg,op_le,Le); \ ADD_BINOP(ElemBool,arg,arg,op_ge,Ge); \ } while (0) #define ADD_RELOP_SPECIALIZED(arg, suffix) \ do { \ ADD_BINOP(ElemBool,arg,arg,op_lt_##suffix,Lt); \ ADD_BINOP(ElemBool,arg,arg,op_gt_##suffix,Gt); \ ADD_BINOP(ElemBool,arg,arg,op_le_##suffix,Le); \ ADD_BINOP(ElemBool,arg,arg,op_ge_##suffix,Ge); \ } while (0) #define ADD_RELOP2(argl,argr) \ do { \ ADD_BINOP(ElemBool,argl,argr,op_lt,Lt); \ ADD_BINOP(ElemBool,argl,argr,op_gt,Gt); \ ADD_BINOP(ElemBool,argl,argr,op_le,Le); \ ADD_BINOP(ElemBool,argl,argr,op_ge,Ge); \ } while (0) #define ADD_ALL_RELOP2(argl, argr) \ do { \ ADD_RELOP2(argl, argr); \ ADD_EQOP2(argl, argr); \ } while (0) // MATH OPERATORS #define ADD_MATHOP(arg) \ do { \ ADD_BINOP(arg,arg,arg,op_add,Add); \ ADD_BINOP(arg,arg,arg,op_sub,Sub); \ ADD_BINOP(arg,arg,arg,op_mul,Mul); \ } while (0) disp.add(OpNot()); disp.add(OpHead()); // boolean logic ADD_BINOP(ElemBool,ElemBool,ElemBool,op_and,And); ADD_BINOP(ElemBool,ElemBool,ElemBool,op_or,Or); ADD_BINOP(ElemBool,ElemBool,ElemBool,op_xor,Xor); ADD_EQOP(ElemBool); // SET ADDITION [used for policy tags] -- insert an element in the set. disp.add(OpAdd()); disp.add(OpAdd()); // SET operations [used for communities in BGP for example]. disp.add(OpNEInt()); disp.add(OpAdd()); disp.add(OpSub()); disp.add(OpNEInt()); disp.add(OpAdd()); disp.add(OpSub()); // // The "ctr" operator. // It takes 2 arguments. A string representing the type to be // constructed, and any type of element to construct it from. // Think of it as a cast---powerful but dangerous. // disp.add(OpCtr()); disp.add(OpCtr()); disp.add(OpCtr()); disp.add(OpCtr()); disp.add(OpCtr()); disp.add(OpCtr()); disp.add(OpCtr()); disp.add(OpCtr()); disp.add(OpCtr()); disp.add(OpCtr()); disp.add(OpCtr()); disp.add(OpCtr()); // ASPATH operations disp.add(OpAdd()); disp.add(OpMul()); disp.add(OpNEInt()); disp.add(OpRegex()); disp.add(OpRegex()); #define ADD_LSETBINOP(set, arg) \ do { \ ADD_BINOP(ElemBool,set,arg,op_eq_nv,Eq); \ ADD_BINOP(ElemBool,set,arg,op_ne_nv,Ne); \ ADD_BINOP(ElemBool,set,arg,op_lt_nv,Lt); \ ADD_BINOP(ElemBool,set,arg,op_gt_nv,Gt); \ ADD_BINOP(ElemBool,set,arg,op_le_nv,Le); \ ADD_BINOP(ElemBool,set,arg,op_ge_nv,Ge); \ } while(0) ADD_LSETBINOP(ElemSetU32, ElemSetU32); ADD_LSETBINOP(ElemSetCom32, ElemSetCom32); #define ADD_SETBINOP(set, arg) \ do { \ ADD_LSETBINOP(set, arg); \ ADD_BINOP(ElemBool,arg,set,op_eq_sw,Eq); \ ADD_BINOP(ElemBool,arg,set,op_ne_sw,Ne); \ ADD_BINOP(ElemBool,arg,set,op_lt_sw,Lt); \ ADD_BINOP(ElemBool,arg,set,op_gt_sw,Gt); \ ADD_BINOP(ElemBool,arg,set,op_le_sw,Le); \ ADD_BINOP(ElemBool,arg,set,op_ge_sw,Ge); \ } while (0) // i32 ADD_EQOP(ElemInt32); ADD_RELOP(ElemInt32); ADD_MATHOP(ElemInt32); // ADD_SETBINOP(ElemInt32); // u32 ADD_EQOP(ElemU32); ADD_RELOP(ElemU32); ADD_MATHOP(ElemU32); ADD_SETBINOP(ElemSetU32, ElemU32); // u32range ADD_EQOP2(ElemU32,ElemU32Range); ADD_RELOP2(ElemU32,ElemU32Range); // com32 ADD_EQOP(ElemCom32); // ADD_RELOP(ElemCom32); // ADD_MATHOP(ElemCom32); ADD_SETBINOP(ElemSetCom32, ElemCom32); // strings ADD_EQOP(ElemStr); // ADD_SETBINOP(ElemStr); disp.add(OpAdd()); disp.add(OpMul()); disp.add(OpRegex()); disp.add(OpRegex()); // IPV4 ADD_EQOP(ElemIPv4); ADD_EQOP2(ElemIPv4, ElemIPv4Range); ADD_RELOP(ElemIPv4); ADD_RELOP2(ElemIPv4, ElemIPv4Range); // ADD_SETBINOP(ElemIPv4); ADD_ALL_RELOP2(ElemIPv4NextHop, ElemIPv4Range); // IPV4NET ADD_EQOP(ElemIPv4Net); ADD_EQOP2(ElemIPv4Net, ElemU32Range); ADD_RELOP_SPECIALIZED(ElemIPv4Net, net); ADD_RELOP2(ElemIPv4Net, ElemU32Range); ADD_SETBINOP(ElemSetIPv4Net, ElemIPv4Net); // Special case set operation to make use of modifiers disp.add(OpLe()); // IPV6 ADD_EQOP(ElemIPv6); ADD_EQOP2(ElemIPv6,ElemIPv6Range); ADD_RELOP(ElemIPv6); ADD_RELOP2(ElemIPv6,ElemIPv6Range); // ADD_SETBINOP(ElemIPv6); // IPV6NET ADD_EQOP(ElemIPv6Net); ADD_EQOP2(ElemIPv6Net,ElemU32Range); ADD_RELOP_SPECIALIZED(ElemIPv6Net, net); ADD_RELOP2(ElemIPv6Net,ElemU32Range); ADD_SETBINOP(ElemSetIPv6Net, ElemIPv6Net); // Special case set operation to make use of modifiers disp.add(OpLe()); } xorp/policy/common/dispatcher.hh0000664000076400007640000001436711540224233017125 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/policy/common/dispatcher.hh,v 1.15 2008/10/02 21:58:06 bms Exp $ #ifndef __POLICY_COMMON_DISPATCHER_HH__ #define __POLICY_COMMON_DISPATCHER_HH__ #include "policy/policy_module.h" #include "libxorp/xlog.h" #include "element_base.hh" #include "operator_base.hh" #include "register_operations.hh" #include "policy_exception.hh" /** * @short Link between elements and operations. Executes operations on elments. * * Implementation of multimethods. * Insipred/copied from Alexandrescu [Modern C++ Design]. * * By taking base element arguments and an operation, it will execute the * correct operation based on the concrete type of the arguments. * * Similar to an ElementFactory. */ class Dispatcher { public: typedef vector ArgList; Dispatcher(); /** * @short Exception thrown if no operation is found for given arguments. * * If there is no combination for the given operation and element types. */ class OpNotFound : public PolicyException { public: OpNotFound(const char* file, size_t line, const string& init_why = "") : PolicyException("OpNotFound", file, line, init_why) {} }; /** * Method to register a binary operation callback with dispatcher. * * @param L concrete class of first argument * @param R concrete class of second argument * @param funct function to be called to perform operation. * @param op binary operation to be registered. */ template void add(const BinOper& op) { // XXX: do it in a better way L arg1; R arg2; const Element* args[] = { &arg1, &arg2 }; Key key = makeKey(op, 2, args); struct Local { static Element* Trampoline(const Element& left, const Element& right) { return funct(static_cast(left), static_cast(right)); } }; _map[key].bin = &Local::Trampoline; } /** * Method to register a unary operation callback with dispatcher. * * @param T concrete class of argument * @param funct function to be called to perform operation. * @param op unary operation to be registered. */ template void add(const UnOper& op) { // XXX: ugly T arg; const Element* args[] = { &arg }; Key key = makeKey(op, 1, args); struct Local { static Element* Trampoline(const Element& arg) { return funct(static_cast(arg)); } }; _map[key].un = &Local::Trampoline; } /** * Execute an n-ary operation. * * Throws an exception on failure. * * @return result of operation. * @param op operation to dispatch. * @param args arguments of operation. */ Element* run(const Oper& op, unsigned argc, const Element** argv) const; /** * Execute an unary operation. * * @return Result of operation. Caller is responsible for delete. * @param op Operation to perform. * @param arg Argument of operation. */ Element* run(const UnOper& op, const Element& arg) const; /** * Execute a binary operation. * * @return result of operation. Caller is responsible for delete. * @param op Operation to perform. * @param left first argument. * @param right second argument. */ Element* run(const BinOper& op, const Element& left, const Element& right) const; private: // Callback for binary operation typedef Element* (*CB_bin)(const Element&, const Element&); // Callback for unary operation typedef Element* (*CB_un)(const Element&); // Key which relates to a callback typedef unsigned Key; // A key relates to either a binary (x)or unary operation. typedef union { CB_un un; CB_bin bin; } Value; // Hashtable would be better typedef map Map; /** * Create a key for the callback table based on operation and arguments. * * @return key used for callback lookup. * @param op requested operation. * @param args the arguments for the operation. */ Key makeKey(const Oper& op, unsigned argc, const Element** argv) const { XLOG_ASSERT(op.arity() == argc); XLOG_ASSERT(argc <= 2); unsigned key = 0; key |= op.hash(); XLOG_ASSERT(key); for (unsigned i = 0; i < argc; i++) { const Element* arg = argv[i]; unsigned eh = arg->hash(); XLOG_ASSERT(eh); key |= eh << (5*(i+1)); } return key; } // XXX: definition moved in header file to allow compilation on 2.95.x /** * Lookup a callback for the requested operation and elements. * Throws exception if none is found. * * @return callback which will perform requested operation. * @param op operation to perform. * @param args the arguments of the operation. */ Value lookup(const Oper& op, unsigned argc, const Element** argv) const { XLOG_ASSERT(op.arity() == argc); // find callback Key key = makeKey(op, argc, argv); return _map[key]; } // Only one global map. Creating multiple dispatcher is thus harmless. // However, we may not have different dispatchers. static Value _map[32768]; }; #endif // __POLICY_COMMON_DISPATCHER_HH__ xorp/policy/common/element_factory.cc0000664000076400007640000000317411421137511020137 0ustar greearbgreearb// vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "policy/policy_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "element_factory.hh" // static members ElementFactory::Map ElementFactory::_map; RegisterElements ElementFactory::_regelems; ElementFactory::ElementFactory() { } void ElementFactory::add(const string& key, Callback cb) { // it is safe to blindly replace callbacks, but probably an error ;D XLOG_ASSERT(_map.find(key) == _map.end()); _map[key] = cb; } Element* ElementFactory::create(const string& key, const char* arg) { Map::iterator i = _map.find(key); // No way of creating element if(i == _map.end()) xorp_throw(UnknownElement, key); // execute the callback return (i->second)(arg); } bool ElementFactory::can_create(const string& key) { return _map.find(key) != _map.end(); } xorp/policy/common/register_operations.hh0000664000076400007640000000352011421137511021053 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/policy/common/register_operations.hh,v 1.8 2008/10/02 21:58:07 bms Exp $ #ifndef __POLICY_COMMON_REGISTER_OPERATIONS_HH__ #define __POLICY_COMMON_REGISTER_OPERATIONS_HH__ #include "element.hh" /** * @short Do initial registration of dispatcher callbacks. * * The sole purpose of this class is to register the callbacks before the * dispatcher is actually used, and register them only once at startup. */ class RegisterOperations { public: /** * Constructor which performs registrations * * In essence, this is where the grammar lives. */ RegisterOperations(); }; namespace operations { /** * Maybe be used to construct elements. Also for casting! * * @param type the string representation of typename. * @param arg the string representation of value. * @return element of wanted type representing arg. */ Element* ctr(const ElemStr& type, const Element& arg); } #endif // __POLICY_COMMON_REGISTER_OPERATIONS_HH__ xorp/policy/common/filter.cc0000664000076400007640000000215511540224233016242 0ustar greearbgreearb// vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "libxorp/xorp.h" #include "filter.hh" const char* filter::filter2str(const filter::Filter& f) { switch(f) { case IMPORT: return "Import"; case EXPORT_SOURCEMATCH: return "Export-SourceMatch"; case EXPORT: return "Export"; } return "Unknown"; } xorp/policy/export_code_generator.hh0000664000076400007640000000365011421137511020061 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/policy/export_code_generator.hh,v 1.10 2008/10/02 21:57:58 bms Exp $ #ifndef __POLICY_EXPORT_CODE_GENERATOR_HH__ #define __POLICY_EXPORT_CODE_GENERATOR_HH__ #include "code_generator.hh" #include "source_match_code_generator.hh" /** * @short Generates export filter code from a node structure. * * This is a specialized version of the CodeGenerator used for import filters. * It skips the source section of terms and replaces it with tag matching * instead. * */ class ExportCodeGenerator : public CodeGenerator { public: /** * @param proto Protocol for which code will be generated. * @param t information on which tags should be used. * @param varmap varmap. */ ExportCodeGenerator(const string& proto, const SourceMatchCodeGenerator::Tags& t, const VarMap& varmap, PolicyMap& pmap); const Element* visit_term(Term& term); private: const SourceMatchCodeGenerator::Tags& _tags; SourceMatchCodeGenerator::Tags::const_iterator _tags_iter; }; #endif // __POLICY_EXPORT_CODE_GENERATOR_HH__ xorp/policy/policy_map.hh0000664000076400007640000000661411540224233015637 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/policy/policy_map.hh,v 1.14 2008/10/02 21:57:59 bms Exp $ #ifndef __POLICY_POLICY_MAP_HH__ #define __POLICY_POLICY_MAP_HH__ #include "policy/common/policy_exception.hh" #include "policy_statement.hh" #include "dependency.hh" /** * @short Container of all policies. * * It relates policy names with actual policies and deals with dependencies. */ class PolicyMap { public: typedef Dependency::KEYS KEYS; /** * @short Exception thrown on errors such as when a policy is not found. */ class PolicyMapError : public PolicyException { public: PolicyMapError(const char* file, size_t line, const string& init_why = "") : PolicyException("PolicyMapError", file, line, init_why) {} }; /** * Find a policy. * * Throws an exception if not found. * * @return policy requested. * @param name name of policy wanted. */ PolicyStatement& find(const string& name) const; /** * Checks if a policy exists. * * @return true if policy exists. * @param name name of policy. */ bool exists(const string& name); /** * Attempts to create a policy and update depndencies. * * Throws an exception on error. * * @param name name of policy. * @param smap SetMap used for updating dependencies. */ void create(const string& name, SetMap& smap); /** * Attempts to delete a policy. * * Throws an exception on error. * * @param name policy name. */ void delete_policy(const string& name); /** * Indicates the use of a policy by a protocol. * * @param policyname policy name. * @param protocol name of protocol which uses policy. */ void add_dependency(const string& policyname, const string& protocol); /** * Remove the use of a policy by a protocol. * * @param policyname policy name. * @param protocol name of protocol which no longer uses policy. */ void del_dependency(const string& policyname, const string& protocol); /** * Dumps all policies in human readable format. * * @return string representation of all policies. */ string str(); void clear() { _deps.clear(); } void policy_deps(const string& policy, DEPS& deps); void policies(KEYS& out); private: // internally, policystatements are held as pointers. typedef Dependency Dep; Dep _deps; }; #endif // __POLICY_POLICY_MAP_HH__ xorp/policy/policy_list.hh0000664000076400007640000001307311540225532016035 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/policy/policy_list.hh,v 1.10 2008/10/02 21:57:59 bms Exp $ #ifndef __POLICY_POLICY_LIST_HH__ #define __POLICY_POLICY_LIST_HH__ #include "code_list.hh" #include "set_map.hh" #include "var_map.hh" #include "policy_map.hh" #include "visitor_semantic.hh" /** * @short The list of policies associated with a protocol. * * This relates to the import/export directives of protocols. * * Depending on what protocols support, they will normally have a single * import/export policy list associated with them. * * Each policy list is an instantiation of a policy, and thus it hold the * specific code for this instantiation. */ class PolicyList : public NONCOPYABLE { public: typedef set TagSet; typedef map TagMap; enum PolicyType { IMPORT, EXPORT }; /** * @param p protocol for which this list applies. * @param pt the type of policy list [import/export]. * @param pmap the container of all policies. * @param smap the container of all sets. * @param vmap the VarMap. */ PolicyList(const string& p, PolicyType pt, PolicyMap& pmap, SetMap& smap, VarMap& vmap, string mod); ~PolicyList(); /** * Append a policy to the list. * * @param policyname the name of the policy */ void push_back(const string& policyname); /** * Compiles a specific policy. * * Throws an exception on semantic / compile errors. * * @param ps policy to compile. * @param mod set filled with targets which are modified by compilation. * @param tagstart first policy tag available. */ void compile_policy(PolicyStatement& ps, Code::TargetSet& mod, uint32_t& tagstart, map >& ptags); /** * Compile all policies which were not previously compiled. * * Throws an exception on semantic / compile errors. * * @param mod set filled with targets which are modified by compilation. * @param tagstart first policy tag available. */ void compile(Code::TargetSet& mod, uint32_t& tagstart, map >& ptags); /** * @return string representation of list */ string str(); /** * Link the all the code avialable in this policy list with code supplied. * * The code supplied will normally contain a target, so only relevant code * is linked. * * @param ret code to link with. */ void link_code(Code& ret); /** * Return all targets in this policy list. * * @param targets set filled with all targets in this policy instantiation. */ void get_targets(Code::TargetSet& targets); /** * Return the policy tags used by a specific protocol for route * redistribution. * * @param protocol protocol for which tags are requested. * @param ts set filled with policy-tags used by the protocol. */ void get_redist_tags(const string& protocol, Code::TagSet& ts); private: typedef pair PolicyCode; typedef list PolicyCodeList; typedef set POLICIES; /** * Semantically check the policy for this instantiation. * * Throws an exception on error. * * @param ps policy to check. * @param type type of policy [import/export]. */ void semantic_check(PolicyStatement& ps, VisitorSemantic::PolicyType type); /** * Compile an import policy. * * @param iter position in code list which needs to be replaced with code. * @param ps policy to compile. * @param modified_targets set filled with targets modified by compilation. */ void compile_import(PolicyCodeList::iterator& iter, PolicyStatement& ps, Code::TargetSet& modified_targets); /** * Compile an export policy. * * @param iter position in code list which needs to be replaced with code. * @param ps policy to compile. * @param modified_targets set filled with targets modified by compilation. * @param tagstart first policy tag available. */ void compile_export(PolicyCodeList::iterator& iter, PolicyStatement& ps, Code::TargetSet& modified_targets, uint32_t& tagstart, map > & ptags); Term* create_mod(Term::BLOCKS block); void add_policy_expression(const string& exp); string _protocol; PolicyType _type; PolicyCodeList _policies; PolicyMap& _pmap; SetMap& _smap; VarMap& _varmap; string _mod; Term* _mod_term; Term* _mod_term_import; Term* _mod_term_export; static uint32_t _pe; POLICIES _pe_policies; }; #endif // __POLICY_POLICY_LIST_HH__ xorp/policy/node_base.hh0000664000076400007640000000470611421137511015422 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/policy/node_base.hh,v 1.10 2008/10/02 21:57:58 bms Exp $ #ifndef __POLICY_NODE_BASE_HH__ #define __POLICY_NODE_BASE_HH__ #include "visitor.hh" /** * @short The base class of a node for building a hierarchy. * * Each node has a line number to associate it with a line in the configuration * file. This is useful for reporting error location in semantic checks. * * Nodes implement the visitor pattern [Inspired by Alexandrescu]. */ class Node { public: /** * @param line the configuration line number where this node was created. */ Node(unsigned line) : _line(line) {} virtual ~Node() {} /** * @return line number of configuration where node was created. */ unsigned line() const { return _line; } /** * Implementation of visitor. * * @param v visit node with this pattern. * @return element at the end of node evaluation. */ virtual const Element* accept(Visitor& v) =0; /** * Test whether this is a "protocol" statement. * * @return true if this is a "protocol" statement. */ virtual bool is_protocol_statement() const { return (false); } /** * Test whether this is "accept" or "reject" statement. * * @return true if this is "accept" or "reject" statement. */ virtual bool is_accept_or_reject() const { return (false); } private: unsigned _line; }; // macro ugliness for visitor implemntation. #define DEFINE_VISITABLE() \ const Element* accept(Visitor& visitor) { \ return visitor.visit(*this); \ } #endif // __POLICY_NODE_BASE_HH__ xorp/policy/policy.y0000664000076400007640000001222611421137511014647 0ustar greearbgreearb%{ /* * Grammar may be simplified, by allowing "any structure", semantic checking is * done at run time anyway... * By any structure i mean that you may add / multiple boolean expressions for * example. This will give more run time flexibility * * yacc -d -p yy_policy_parser -o yacc.yy_policy_parser.cc policy.y */ #include #include "policy_module.h" #include "libxorp/xorp.h" #include "policy/common/element.hh" #include "policy/common/element_factory.hh" #include "policy/common/operator.hh" #include "policy_parser.hh" extern int yylex(void); extern void yyerror(const char *m); using namespace policy_parser; static ElementFactory _ef; %} %union { char* c_str; Node* node; BinOper* op; }; %token YY_BOOL YY_INT YY_UINT YY_UINTRANGE YY_STR YY_ID %token YY_IPV4 YY_IPV4RANGE YY_IPV4NET YY_IPV6 YY_IPV6RANGE YY_IPV6NET %token YY_SEMICOLON YY_LPAR YY_RPAR YY_ASSIGN YY_SET YY_REGEX %token YY_ACCEPT YY_REJECT YY_PROTOCOL YY_NEXT YY_POLICY YY_PLUS_EQUALS %token YY_MINUS_EQUALS YY_TERM %left YY_NOT YY_AND YY_XOR YY_OR YY_HEAD YY_CTR YY_NE_INT %left YY_EQ YY_NE YY_LE YY_GT YY_LT YY_GE %left YY_IPNET_EQ YY_IPNET_LE YY_IPNET_GT YY_IPNET_LT YY_IPNET_GE %left YY_ADD YY_SUB %left YY_MUL %type actionstatement action boolstatement boolexpr expr assignexpr %type assignop %% statement: statement actionstatement { _parser_nodes->push_back($2); } | statement boolstatement { _parser_nodes->push_back($2); } | /* empty */ ; actionstatement: action YY_SEMICOLON { $$ = $1; } ; action: assignexpr | YY_ACCEPT { $$ = new NodeAccept(_parser_lineno); } | YY_REJECT { $$ = new NodeReject(_parser_lineno); } | YY_NEXT YY_POLICY { $$ = new NodeNext(_parser_lineno, NodeNext::POLICY); } | YY_NEXT YY_TERM { $$ = new NodeNext(_parser_lineno, NodeNext::TERM); } ; assignexpr: YY_ID assignop expr { $$ = new NodeAssign($1, $2, $3, _parser_lineno); free($1); } ; assignop: YY_ASSIGN { $$ = NULL; } | YY_PLUS_EQUALS { $$ = new OpAdd; } | YY_MINUS_EQUALS { $$ = new OpSub; } ; boolstatement: boolexpr YY_SEMICOLON { $$ = $1; } ; boolexpr: YY_PROTOCOL YY_EQ YY_ID { $$ = new NodeProto($3,_parser_lineno); free($3); } | YY_NOT boolexpr { $$ = new NodeUn(new OpNot,$2,_parser_lineno); } | YY_POLICY YY_ID { $$ = new NodeSubr(_parser_lineno, $2); free($2); } | boolexpr YY_AND boolexpr { $$ = new NodeBin(new OpAnd,$1,$3,_parser_lineno); } | boolexpr YY_XOR boolexpr { $$ = new NodeBin(new OpXor,$1,$3,_parser_lineno); } | boolexpr YY_OR boolexpr { $$ = new NodeBin(new OpOr,$1,$3,_parser_lineno); } | expr YY_EQ expr { $$ = new NodeBin(new OpEq,$1,$3,_parser_lineno); } | expr YY_NE expr { $$ = new NodeBin(new OpNe,$1,$3,_parser_lineno); } | expr YY_LT expr { $$ = new NodeBin(new OpLt,$1,$3,_parser_lineno); } | expr YY_GT expr { $$ = new NodeBin(new OpGt,$1,$3,_parser_lineno); } | expr YY_LE expr { $$ = new NodeBin(new OpLe,$1,$3,_parser_lineno); } | expr YY_GE expr { $$ = new NodeBin(new OpGe,$1,$3,_parser_lineno); } | expr YY_IPNET_EQ expr { $$ = new NodeBin(new OpEq,$1,$3,_parser_lineno); } | expr YY_NOT expr { $$ = new NodeBin(new OpNe,$1,$3,_parser_lineno); } | expr YY_IPNET_LT expr { $$ = new NodeBin(new OpLt,$1,$3,_parser_lineno); } | expr YY_IPNET_GT expr { $$ = new NodeBin(new OpGt,$1,$3,_parser_lineno); } | expr YY_IPNET_LE expr { $$ = new NodeBin(new OpLe,$1,$3,_parser_lineno); } | expr YY_IPNET_GE expr { $$ = new NodeBin(new OpGe,$1,$3,_parser_lineno); } | expr YY_NE_INT expr { $$ = new NodeBin(new OpNEInt, $1, $3, _parser_lineno); } | YY_LPAR boolexpr YY_RPAR { $$ = $2; } | expr YY_REGEX expr { $$ = new NodeBin(new OpRegex, $1, $3, _parser_lineno); } ; expr: expr YY_ADD expr { $$ = new NodeBin(new OpAdd,$1,$3,_parser_lineno); } | expr YY_SUB expr { $$ = new NodeBin(new OpSub,$1,$3,_parser_lineno); } | expr YY_MUL expr { $$ = new NodeBin(new OpMul,$1,$3,_parser_lineno); } | YY_HEAD expr { $$ = new NodeUn(new OpHead, $2, _parser_lineno); } | YY_CTR expr expr { $$ = new NodeBin(new OpCtr, $2, $3, _parser_lineno); } | YY_LPAR expr YY_RPAR { $$ = $2; } | YY_STR { $$ = new NodeElem(_ef.create(ElemStr::id,$1),_parser_lineno); free($1); } | YY_UINT { $$ = new NodeElem(_ef.create(ElemU32::id,$1),_parser_lineno); free($1);} | YY_UINTRANGE { $$ = new NodeElem(_ef.create(ElemU32Range::id,$1),_parser_lineno); free($1);} | YY_INT { $$ = new NodeElem(_ef.create(ElemInt32::id,$1),_parser_lineno); free($1);} | YY_BOOL { $$ = new NodeElem(_ef.create(ElemBool::id,$1),_parser_lineno); free($1);} | YY_ID { $$ = new NodeVar($1,_parser_lineno); free($1); } | YY_SET YY_ID { $$ = new NodeSet($2,_parser_lineno); free($2); } | YY_IPV4 { $$ = new NodeElem(_ef.create(ElemIPv4::id,$1),_parser_lineno); free($1); } | YY_IPV4RANGE { $$ = new NodeElem(_ef.create(ElemIPv4Range::id,$1),_parser_lineno); free($1); } | YY_IPV6 { $$ = new NodeElem(_ef.create(ElemIPv6::id,$1),_parser_lineno); free($1); } | YY_IPV6RANGE { $$ = new NodeElem(_ef.create(ElemIPv6Range::id,$1),_parser_lineno); free($1); } | YY_IPV4NET { $$ = new NodeElem(_ef.create(ElemIPv4Net::id,$1),_parser_lineno); free($1); } | YY_IPV6NET { $$ = new NodeElem(_ef.create(ElemIPv6Net::id,$1),_parser_lineno); free($1); } ; %% xorp/policy/filter_manager.cc0000664000076400007640000002520011540224233016440 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #include "policy_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "backend/policytags.hh" #include "filter_manager.hh" FilterManager::FilterManager(const CodeMap& imp, const CodeMap& sm, const CodeMap& exp, const SetMap& sets, const TagMap& tagmap, XrlStdRouter& rtr, ProcessWatch& pw, ProtocolMap& pmap) : _import(imp), _sourcematch(sm), _export(exp), _sets(sets), _tagmap(tagmap), _eventloop(rtr.eventloop()), _push_timeout(2000), _process_watch(pw), _policy_backend(&rtr), _rib(&rtr), _rib_name("rib"), // FIXME: rib name hardcoded _pmap(pmap) { } void FilterManager::update_filter(const Code::Target& t) { switch (t.filter()) { case filter::IMPORT: update_import_filter(t.protocol()); break; case filter::EXPORT_SOURCEMATCH: update_sourcematch_filter(t.protocol()); break; case filter::EXPORT: update_export_filter(t.protocol()); break; } } void FilterManager::update_import_filter(const string& protocol) { update_queue(protocol,_import,_import_queue); } void FilterManager::update_sourcematch_filter(const string& protocol) { update_queue(protocol,_sourcematch,_sourcematch_queue); } void FilterManager::update_export_filter(const string& protocol) { update_queue(protocol,_export,_export_queue); } void FilterManager::update_tagmap(const string& protocol) { TagMap::const_iterator i = _tagmap.find(protocol); // no tags for this protocol, no update needed. if(i == _tagmap.end()) return; const TagSet* ts = (*i).second; // convert tags to PolicyTags PolicyTags pt; for (TagSet::const_iterator iter = ts->begin(); iter != ts->end(); ++iter) pt.insert(*iter); XrlAtomList al = pt.xrl_atomlist(); debug_msg("[POLICY] Updating tagmap proto: %s, tags: %s\n", protocol.c_str(),al.str().c_str()); // send out update _rib.send_insert_policy_redist_tags(_rib_name.c_str(), _pmap.xrl_target(protocol),al, callback(this,&FilterManager::policy_backend_cb)); } void FilterManager::policy_backend_cb(const XrlError& e) { string error_msg; if(e != XrlError::OKAY()) { debug_msg("[POLICY] XRL exception: %s\n", e.str().c_str()); error_msg = c_format("XRL policy_backend_cb() error: %s", e.str().c_str()); XLOG_ERROR("%s", error_msg.c_str()); // xorp_throw(FMException, error_msg); // XXX: what else can we do ? } } void FilterManager::flush_export_queue() { debug_msg("[POLICY] Flushing export filter queue...\n"); // commit all updates on export queue for(ConfQueue::iterator i = _export_queue.begin(); i != _export_queue.end(); ++i) { const string& protocol = (*i).first; const string& conf = (*i).second; debug_msg("[POLICY] Protocol: %s, Conf:\n%s\nEnd...\n", protocol.c_str(),conf.c_str()); // if configuration is empty, reset the filter if(!conf.length()) { _policy_backend.send_reset(_pmap.xrl_target(protocol).c_str(), filter::EXPORT,callback(this,&FilterManager::policy_backend_cb)); } // else configure it else { _policy_backend.send_configure(_pmap.xrl_target(protocol).c_str(), filter::EXPORT, conf, callback(this, &FilterManager::policy_backend_cb)); } // export filters may change tagmap update_tagmap(protocol); // we need to push routes for this protocol [export filter changed]. _push_queue.insert(protocol); } _export_queue.clear(); } void FilterManager::flush_queue(ConfQueue& queue, filter::Filter f) { debug_msg("[POLICY] Flushing %s queue...\n", filter::filter2str(f)); // flush all updates on queue for(ConfQueue::iterator i = queue.begin(); i != queue.end(); ++i) { const string& protocol = (*i).first; const string& conf = (*i).second; debug_msg("[POLICY] Protocol: %s, Conf:\n%s\nEnd...\n", protocol.c_str(),conf.c_str()); // if conf is empty, reset filter. if(!conf.length()) { _policy_backend.send_reset(_pmap.xrl_target(protocol).c_str(), f, callback(this,&FilterManager::policy_backend_cb)); } // else configure filter normally. else { _policy_backend.send_configure(_pmap.xrl_target(protocol).c_str(), f, conf, callback(this,&FilterManager::policy_backend_cb)); } // need to push routes for protocol [filters changed]. _push_queue.insert(protocol); // tagmap is updated on export filter flush [even if we may deal with // source match filters here... there is no need to worry about tags]. } queue.clear(); } void FilterManager::push_routes_now() { for(set::iterator i = _push_queue.begin(); i != _push_queue.end(); ++i) { const string& proto = *i; debug_msg("[POLICY] Pushing routes for %s\n", proto.c_str()); _policy_backend.send_push_routes(_pmap.xrl_target(proto).c_str(), callback(this,&FilterManager::policy_backend_cb)); } _push_queue.clear(); } void FilterManager::flush_updates_now() { // flush all queues flush_export_queue(); flush_queue(_sourcematch_queue,filter::EXPORT_SOURCEMATCH); flush_queue(_import_queue,filter::IMPORT); // push routes [may get overwritten, its ok for now]. _push_timer = _eventloop.new_oneoff_after_ms(_push_timeout, callback(this,&FilterManager::push_routes_now)); } void FilterManager::flush_updates(uint32_t msec) { // delayed flush _flush_timer = _eventloop.new_oneoff_after_ms(msec, callback(this,&FilterManager::flush_updates_now)); } void FilterManager::birth(const string& protocol) { debug_msg("[POLICY] Protocol born: %s\n",protocol.c_str()); // resend configuration to new born process. update_export_filter(protocol); update_sourcematch_filter(protocol); update_import_filter(protocol); // FIXME: need a mechanism to make routes from RIB reach the new born // process. Consider if source match filter was setup before the export // filter was alive... the routes will be sitting in the RIB, and never go // to the process. // This is a HACK [as this problem was discovered quite late]. So it looks // ugly on purpose. CodeMap::const_iterator cmi = _export.find(protocol); if(cmi != _export.end()) { const Code* export_code = (*cmi).second; for(set::const_iterator i = export_code->source_protocols().begin(); i != export_code->source_protocols().end(); ++i) { const string& push_proto = *i; if(push_proto == protocol) continue; if(!_process_watch.alive(push_proto)) continue; // LUCKY!!!! if(_push_queue.find(protocol) != _push_queue.end()) continue; XLOG_WARNING("XXX HACK: PUSHING ROUTES OF %s FOR %s", push_proto.c_str(),protocol.c_str()); _push_queue.insert(push_proto); } } // EOH [end of hack] // perhaps we can delay the flush. Consider boot-time. A lot of processes // are coming up, so we will always be flushing. At boot, the commit is // delayed by ~2 seconds I think, so if we delay the flush ~2 seconds here // it might be better... flush_updates_now(); // We still push routes even though this protocol was just brought up. // The reason being that some routes could have already been processed by // the time the protocol was born and the filters configured. Some // protocols (like BGP) avoid unnecessary flapping by waiting until this // push is received. Perhaps there should be an explicit mechanism for the // policy manager to signal protocols that it's done with configuration // rather than overloading the meaning of route push. } void FilterManager::death(const string& protocol) { // do not send any updates to dead process. delete_queue_protocol(_export_queue,protocol); delete_queue_protocol(_sourcematch_queue,protocol); delete_queue_protocol(_import_queue,protocol); _push_queue.erase(protocol); // XXX: might want to delete policytags in rib... but the tagmap in rib is // quite unflexible now. debug_msg("[POLICY] Protocol death: %s\n",protocol.c_str()); } void FilterManager::delete_queue_protocol(ConfQueue& queue, const string& protocol) { ConfQueue::iterator i = queue.find(protocol); if(i == queue.end()) return; queue.erase(i); } void FilterManager::update_queue(const string& protocol, const CodeMap& cm, ConfQueue& queue) { // if a process is dead, erase it from the queue if it is there, and then do // nothing. bool alive = _process_watch.alive(protocol); if (!alive) { debug_msg("[POLICY] clearing update queue for dead protocol: %s\n", protocol.c_str()); ConfQueue::iterator i = queue.find(protocol); if (i != queue.end()) queue.erase(i); return; } // check if there is any code present for this protocol, if not reset // filter. CodeMap::const_iterator i = cm.find(protocol); if (i == cm.end()) { // reset filter queue[protocol] = ""; return; } // get the code Code* code = (*i).second; string conf = code->code(); const set& set_names = code->referenced_set_names(); // expand the set names for (set::const_iterator iter = set_names.begin(); iter != set_names.end(); ++iter) { const string& name = *iter; const Element& e = _sets.getSet(*iter); conf += string("SET ") + e.type() + " " + name + " \""; conf += e.str(); conf += "\"\n"; } // add subroutines const SUBR& subr = code->subr(); if (!subr.empty()) conf += "SUBR_START\n"; for (SUBR::const_iterator i = subr.begin(); i != subr.end(); ++i) conf += i->second; if (!subr.empty()) conf += "SUBR_END\n"; // send it the complete configuration [code + sets] queue[protocol] = conf; } xorp/policy/tests/0000775000076400007640000000000011540225533014321 5ustar greearbgreearbxorp/policy/tests/filter_manager_fake.hh0000664000076400007640000000223411540225533020610 0ustar greearbgreearb// vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __POLICY_TEST_FILTER_MANAGER_FAKE_HH__ #define __POLICY_TEST_FILTER_MANAGER_FAKE_HH__ #include "policy/filter_manager_base.hh" class FilterManagerFake : public FilterManagerBase { public: void update_filter(const Code::Target& t); void flush_updates(uint32_t msec); }; #endif // __POLICY_TEST_FILTER_MANAGER_FAKE_HH__ xorp/policy/tests/compilepolicy.hh0000664000076400007640000000213011540225533017506 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __POLICY_TEST_COMPILEPOLICY_HH__ #define __POLICY_TEST_COMPILEPOLICY_HH__ typedef string yy_statement; typedef vector yy_statements; #endif // __POLICY_TEST_COMPILEPOLICY_HH__ xorp/policy/tests/policyvarmap.conf0000664000076400007640000000155011421137511017673 0ustar greearbgreearbbgp network4 ipv4net r 10 bgp nexthop4 ipv4 rw 11 bgp network6 ipv6net r 12 bgp nexthop6 ipv6 rw 13 bgp aspath aspath rw 14 bgp origin u32 r 15 bgp neighbor ipv4 r 16 bgp localpref u32 rw 17 bgp community set_com32 rw 18 bgp med u32 rw 19 bgp med_remove bool rw 20 bgp aggregate_prefix_len u32 w 21 bgp aggregate_brief_mode bool w 22 bgp was_aggregated u32 r 23 ripng network6 ipv6net r 20 ripng nexthop6 ipv6 rw 21 ripng metric u32 rw 22 ripng tag u32 rw 23 rip network4 ipv4net r 24 rip nexthop4 ipv4 rw 25 rip metric u32 rw 26 rip tag u32 rw 27 static_routes network6 ipv6net r 28 static_routes network4 ipv4net r 29 static_routes nexthop6 ipv6 r 30 static_routes nexthop4 ipv4 r 31 static_routes metric u32 r 32 rib network6 ipv6net r 33 rib network4 ipv4net r 34 rib nexthop6 ipv6 r 35 rib nexthop4 ipv4 r 36 rib metric u32 r 37 xorp/policy/tests/policy2.var0000664000076400007640000000004711421137511016411 0ustar greearbgreearb1 set_u32 0 14 aspath 7865 32 u32 1 xorp/policy/tests/process_watch_fake.cc0000664000076400007640000000200311540224233020451 0ustar greearbgreearb// vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "libxorp/xorp.h" #include "process_watch_fake.hh" void ProcessWatchFake::add_interest(const string& id) { cout << "ProcessWatchFake add_interest: " << id << endl; } xorp/policy/tests/policy1.var0000664000076400007640000000003311421137511016403 0ustar greearbgreearb1 set_u32 0 14 aspath 7865 xorp/policy/tests/file_varrw.cc0000664000076400007640000000644311421137511016773 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "policy/policy_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "policy/common/policy_utils.hh" #include "file_varrw.hh" FileVarRW::FileVarRW() : _trashc(0), _verbose(true) { memset(_map, 0, sizeof(_map)); } void FileVarRW::load(const string& fname) { FILE* f = fopen(fname.c_str(), "r"); if (!f) { string err = "Can't open file " + fname; err += ": "; err += strerror(errno); xorp_throw(Error, err); } char buff[1024]; int lineno = 1; while (fgets(buff, sizeof(buff)-1, f)) { if (doLine(buff)) continue; // parse error fclose(f); ostringstream oss; oss << "Parse error at line: " << lineno; xorp_throw(Error, oss.str()); } fclose(f); } FileVarRW::~FileVarRW() { clear_trash(); } bool FileVarRW::doLine(const string& str) { istringstream iss(str); string varname; string type; string value; if (iss.eof()) return false; iss >> varname; if (iss.eof()) return false; iss >> type; if(iss.eof()) return false; iss >> value; while (! iss.eof()) { string v; iss >> v; if (v.size()) value += " " + v; } Element* e = _ef.create(type, value.c_str()); XLOG_ASSERT(_trashc < MAX_TRASH); _trash[_trashc++] = e; if (_verbose) cout << "FileVarRW adding variable " << varname << " of type " << type << ": " << e->str() << endl; char* err = 0; Id id = strtol(varname.c_str(), &err, 10); if (*err) { xorp_throw(Error, string("Varname must be ID [numeric]: ") + err); } _map[id] = e; return true; } const Element& FileVarRW::read(const Id& id) { const Element* e = _map[id]; if (!e) { ostringstream oss; oss << "Cannot read variable: " << id; xorp_throw(Error, oss.str()); } if (_verbose) cout << "FileVarRW READ " << id << ": " << e->str() << endl; return *e; } void FileVarRW::write(const Id& id, const Element& e) { if (_verbose) cout << "FileVarRW WRITE " << id << ": " << e.str() << endl; _map[id] = &e; } void FileVarRW::sync() { if (_verbose) { cout << "FileVarRW SYNC" << endl; for (unsigned i = 0; i < VAR_MAX; i++) { const Element* e = _map[i]; if (e) cout << i << ": " << e->str() << endl; } } } void FileVarRW::clear_trash() { while (_trashc--) delete _trash[_trashc]; _trashc = 0; } void FileVarRW::set_verbose(bool verb) { _verbose = verb; } xorp/policy/tests/yacc.yy_compile_policy.cc.h0000664000076400007640000000121111421137511021513 0ustar greearbgreearb#ifndef YYERRCODE #define YYERRCODE 256 #endif #define YY_INT 257 #define YY_STR 258 #define YY_ID 259 #define YY_STATEMENT 260 #define YY_SOURCEBLOCK 261 #define YY_DESTBLOCK 262 #define YY_ACTIONBLOCK 263 #define YY_IPV4 264 #define YY_IPV4NET 265 #define YY_IPV6 266 #define YY_IPV6NET 267 #define YY_SEMICOLON 268 #define YY_LBRACE 269 #define YY_RBRACE 270 #define YY_POLICY_STATEMENT 271 #define YY_TERM 272 #define YY_SOURCE 273 #define YY_DEST 274 #define YY_ACTION 275 #define YY_SET 276 #define YY_EXPORT 277 #define YY_IMPORT 278 typedef union { char *c_str; yy_statements* statements; } YYSTYPE; extern YYSTYPE yy_compile_policylval; xorp/policy/tests/execpolicy.cc0000664000076400007640000000517111540224233016774 0ustar greearbgreearb// vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net /* * EXIT CODES: * * 0: filter rejected route * 1: filter accepted route * 2: Policy exception * 3: error * */ #include "policy/policy_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/timeval.hh" #include "libxorp/clock.hh" #include "libxorp/timer.hh" #include "policy/common/policy_utils.hh" #include "policy/backend/policy_filter.hh" #include "file_varrw.hh" using namespace policy_utils; int main(int argc, char *argv[]) { if(argc < 3) { cout << "Usage: " << argv[0] << " " << endl; exit(3); } string conf = ""; bool accepted = true; SystemClock sc; TimerList timerlist(&sc); TimeVal start; TimerList::system_gettimeofday(&start); xlog_init(argv[0], 0); xlog_set_verbose(XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); try { read_file(argv[1],conf); } catch(const PolicyException& e) { cout << "May not read file: " << e.str() << endl; exit(3); } try { cout << "Configuring varrw..." << endl; FileVarRW varrw; varrw.load(argv[2]); PolicyFilter filter; cout << "Configuring filter..." << endl; filter.configure(conf); cout << "Running filter..." << endl; accepted = filter.acceptRoute(varrw); cout << "Filter " << ( accepted ? "accepted" : "rejected") << " route" << endl; } catch(const FileVarRW::Error& e) { cout << "May not configure filevarrw " << e.str() << endl; exit(3); } catch(const PolicyException& e) { cout << "PolicyException: " << e.str() << endl; exit(2); } TimeVal elapsed; TimerList::system_gettimeofday(&elapsed); elapsed -= start; printf("Execution successful in %d milliseconds\n", elapsed.to_ms()); xlog_stop(); xlog_exit(); if(accepted) exit(1); exit(0); } xorp/policy/tests/compilepolicy.y0000664000076400007640000001041211421137511017355 0ustar greearbgreearb%{ /* * yacc -d -p yy_compile_policy -o yacc.yy_compile_policy.cc compilepolicy.y */ #include "policy/policy_module.h" #include "libxorp/xorp.h" #include #include #include "libproto/config_node_id.hh" #include "policy/configuration.hh" #include "policy/common/policy_utils.hh" #include "policy/test/compilepolicy.hh" extern int yylex(void); extern void yyerror(const char *); extern Configuration _yy_configuration; struct yy_tb { string name; yy_statements* block[3]; }; static vector _yy_terms; static yy_statements* _yy_statements = NULL; // add blocks to configuration, and delete stuff from memory static void add_blocks(const string& pname, const string& tname, yy_tb& term) { // source, action, dest for(int i = 0; i < 3; i++) { yy_statements* statements = term.block[i]; // empty blocks! if(statements == 0) continue; ConfigNodeId order_generator(0, i); ConfigNodeId prev_order(ConfigNodeId::ZERO()); ConfigNodeId order(ConfigNodeId::ZERO()); for(yy_statements::iterator j = statements->begin(); j != statements->end(); ++j) { yy_statement* statement = *j; order = order_generator.generate_unique_node_id(); order.set_position(prev_order.unique_node_id()); prev_order = order; _yy_configuration.update_term_block(pname, tname, i, order, *statement); delete statement; } delete statements; } } %} %union { char *c_str; yy_statements* statements; }; %token YY_INT YY_STR YY_ID YY_STATEMENT %token YY_SOURCEBLOCK YY_DESTBLOCK YY_ACTIONBLOCK %token YY_IPV4 YY_IPV4NET YY_IPV6 YY_IPV6NET %token YY_SEMICOLON YY_LBRACE YY_RBRACE %token YY_POLICY_STATEMENT YY_TERM %token YY_SOURCE YY_DEST YY_ACTION %token YY_SET %token YY_EXPORT YY_IMPORT %type source dest action %% configuration: configuration policy_statement | configuration set | configuration YY_EXPORT YY_ID YY_STR YY_SEMICOLON { list tmp; string proto = $3; string pols = $4; free($3); free($4); policy_utils::str_to_list(pols,tmp); _yy_configuration.update_exports(proto, tmp, ""); } | configuration YY_IMPORT YY_ID YY_STR YY_SEMICOLON { list tmp; string proto = $3; string pols = $4; free($3); free($4); policy_utils::str_to_list(pols,tmp); _yy_configuration.update_imports(proto, tmp, ""); } | /* empty */ ; set: YY_SET YY_STR YY_ID YY_STR YY_SEMICOLON { string type = $2; string id = $3; string sets = $4; free($2); free($3); free($4); _yy_configuration.create_set(id); _yy_configuration.update_set(type, id, sets); } ; policy_statement: YY_POLICY_STATEMENT YY_ID YY_LBRACE terms YY_RBRACE { string pname = $2; free($2); _yy_configuration.create_policy(pname); ConfigNodeId order_generator(ConfigNodeId::ZERO()); ConfigNodeId prev_order(ConfigNodeId::ZERO()); ConfigNodeId order(ConfigNodeId::ZERO()); for(vector::iterator i = _yy_terms.begin(); i != _yy_terms.end(); ++i) { yy_tb* term = *i; string& tname = term->name; order = order_generator.generate_unique_node_id(); order.set_position(prev_order.unique_node_id()); prev_order = order; _yy_configuration.create_term(pname, order, tname); add_blocks(pname, tname, *term); delete term; } _yy_terms.clear(); } ; terms: terms YY_TERM YY_ID YY_LBRACE source dest action YY_RBRACE { yy_tb* tb = new yy_tb; tb->name = $3; tb->block[0] = $5; tb->block[1] = $6; tb->block[2] = $7; free($3); _yy_terms.push_back(tb); } | /* exmpty */ ; source: YY_SOURCE YY_LBRACE statements YY_RBRACE { yy_statements* tmp = _yy_statements; _yy_statements = NULL; $$ = tmp; } ; dest: YY_DEST YY_LBRACE statements YY_RBRACE { yy_statements* tmp = _yy_statements; _yy_statements = NULL; $$ = tmp; } ; action: YY_ACTION YY_LBRACE statements YY_RBRACE { yy_statements* tmp = _yy_statements; _yy_statements = NULL; $$ = tmp; } ; statements: statements YY_STATEMENT YY_SEMICOLON { if (_yy_statements == NULL) { _yy_statements = new yy_statements; } yy_statement* statement = new yy_statement($2); statement->append(";"); free($2); _yy_statements->push_back(statement); } | /* empty */ ; %% xorp/policy/tests/compilepolicy.l0000664000076400007640000000710411421137511017344 0ustar greearbgreearb%{ // XXX: this whole parsing is becoming a mess... Once we finalize how to test // policy and what it should look like, it needs a re-write. #include "libxorp/xorp.h" #include "policy/test/compilepolicy.hh" #include "yacc.yy_compile_policy.cc.h" #include "policy/common/policy_utils.hh" #include #define yylval yy_compile_policylval #define yyparse yy_compile_policyparse #define yyerror yy_compile_policyerror void yyerror(const char *); int yyparse(void); namespace { unsigned _yy_lineno; string _yy_last_err; } %} %option prefix="yy_compile_policy" %option outfile="lex.yy_compile_policy.cc" %option noyywrap %option nounput %option never-interactive %x STR BLOCK RE_IPV4_BYTE 25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]? RE_IPV4 {RE_IPV4_BYTE}\.{RE_IPV4_BYTE}\.{RE_IPV4_BYTE}\.{RE_IPV4_BYTE} RE_IPV4_PREFIXLEN 3[0-2]|[0-2]?[0-9] RE_IPV4NET {RE_IPV4}\/{RE_IPV4_PREFIXLEN} RE_H4 [a-fA-F0-9]{1,4} RE_H4_COLON {RE_H4}: RE_LS32 (({RE_H4}:{RE_H4})|{RE_IPV4}) RE_IPV6_P1 {RE_H4_COLON}{6}{RE_LS32} RE_IPV6_P2 ::{RE_H4_COLON}{5}{RE_LS32} RE_IPV6_P3 ({RE_H4})?::{RE_H4_COLON}{4}{RE_LS32} RE_IPV6_P4 ({RE_H4_COLON}{0,1}{RE_H4})?::{RE_H4_COLON}{3}{RE_LS32} RE_IPV6_P5 ({RE_H4_COLON}{0,2}{RE_H4})?::{RE_H4_COLON}{2}{RE_LS32} RE_IPV6_P6 ({RE_H4_COLON}{0,3}{RE_H4})?::{RE_H4_COLON}{1}{RE_LS32} RE_IPV6_P7 ({RE_H4_COLON}{0,4}{RE_H4})?::{RE_LS32} RE_IPV6_P8 ({RE_H4_COLON}{0,5}{RE_H4})?::{RE_H4} RE_IPV6_P9 ({RE_H4_COLON}{0,6}{RE_H4})?:: RE_IPV6 {RE_IPV6_P1}|{RE_IPV6_P2}|{RE_IPV6_P3}|{RE_IPV6_P4}|{RE_IPV6_P5}|{RE_IPV6_P6}|{RE_IPV6_P7}|{RE_IPV6_P8}|{RE_IPV6_P9} RE_IPV6_PREFIXLEN 12[0-8]|1[01][0-9]|[0-9][0-9]? RE_IPV6NET {RE_IPV6}\/{RE_IPV6_PREFIXLEN} %% \" BEGIN(STR); "{" return YY_LBRACE; "}" return YY_RBRACE; "source" BEGIN(BLOCK); return YY_SOURCE; "dest" BEGIN(BLOCK); return YY_DEST; "action" BEGIN(BLOCK); return YY_ACTION; ";" { return YY_SEMICOLON; } [[:blank:]\n]*"{"[[:blank:]\n]* { _yy_lineno += policy_utils::count_nl(yytext); return YY_LBRACE; } [[:blank:]\n]*"}"[[:blank:]\n]* { BEGIN(INITIAL); _yy_lineno += policy_utils::count_nl(yytext); return YY_RBRACE; } [^;{}]* { yylval.c_str = strdup(yytext); _yy_lineno += policy_utils::count_nl(yytext); return YY_STATEMENT; } \" BEGIN(INITIAL); [^\"]+ { yylval.c_str = strdup(yytext); _yy_lineno += policy_utils::count_nl(yytext); return YY_STR; } {RE_IPV4} { yylval.c_str = strdup(yytext); return YY_IPV4; } {RE_IPV4NET} { yylval.c_str = strdup(yytext); return YY_IPV4NET; } {RE_IPV6} { yylval.c_str = strdup(yytext); return YY_IPV6; } {RE_IPV6NET} { yylval.c_str = strdup(yytext); return YY_IPV6NET; } "term" return YY_TERM; "policy\-statement" return YY_POLICY_STATEMENT; "SET" return YY_SET; "import" return YY_IMPORT; "export" return YY_EXPORT; [[:alpha:]][[:alnum:]_]* { yylval.c_str = strdup(yytext); return YY_ID; } ; return YY_SEMICOLON; "\n" _yy_lineno++; [[:blank:]]+ /* eat blanks */ . { yyerror("Unknown character"); } %% void yyerror(const char *m) { ostringstream oss; oss << "Error on line " << _yy_lineno << ": " << m; _yy_last_err = oss.str(); } // XXX: no memory management [a lot of leaks... for simplicy] int do_parsing(const string& conf, string& outerr) { YY_BUFFER_STATE yybuffstate = yy_scan_string(conf.c_str()); _yy_last_err = "No error"; _yy_lineno =1; int res = yyparse(); yy_delete_buffer(yybuffstate); outerr = _yy_last_err; return res; } xorp/policy/tests/dummy_test.sh0000775000076400007640000000101311421137511017041 0ustar greearbgreearb#!/bin/sh # # $XORP: xorp/policy/test/dummy_test.sh,v 1.3 2004/10/05 22:42:27 pavlin Exp $ # if [ "X${srcdir}" = "X" ] ; then srcdir=`dirname $0` ; fi test_policy() { ${srcdir}/test_policy.sh $1 $2 $3 $4 if [ $? -ne 0 ] then exit 1 fi } test_accept() { test_policy $1 $2 $3 1 } test_reject() { test_policy $1 $2 $3 0 } test_accept "${srcdir}/policy1.src" "${srcdir}/policyvarmap.conf" "${srcdir}/policy1.var" test_reject "${srcdir}/policy2.src" "${srcdir}/policyvarmap.conf" "${srcdir}/policy2.var" exit 0 xorp/policy/tests/yacc.yy_compile_policy.cc0000664000076400007640000004124011421137511021273 0ustar greearbgreearb#include #ifndef lint #endif #define YYBYACC 1 #define YYMAJOR 1 #define YYMINOR 9 #define YYLEX yylex() #define YYEMPTY -1 #define yyclearin (yychar=(YYEMPTY)) #define yyerrok (yyerrflag=0) #define YYRECOVERING() (yyerrflag!=0) #if defined(__cplusplus) || __STDC__ static int yygrowstack(void); #else static int yygrowstack(); #endif #define yyparse yy_compile_policyparse #define yylex yy_compile_policylex #define yyerror yy_compile_policyerror #define yychar yy_compile_policychar #define yyval yy_compile_policyval #define yylval yy_compile_policylval #define yydebug yy_compile_policydebug #define yynerrs yy_compile_policynerrs #define yyerrflag yy_compile_policyerrflag #define yyss yy_compile_policyss #define yyssp yy_compile_policyssp #define yyvs yy_compile_policyvs #define yyvsp yy_compile_policyvsp #define yylhs yy_compile_policylhs #define yylen yy_compile_policylen #define yydefred yy_compile_policydefred #define yydgoto yy_compile_policydgoto #define yysindex yy_compile_policysindex #define yyrindex yy_compile_policyrindex #define yygindex yy_compile_policygindex #define yytable yy_compile_policytable #define yycheck yy_compile_policycheck #define yyname yy_compile_policyname #define yyrule yy_compile_policyrule #define yysslim yy_compile_policysslim #define yystacksize yy_compile_policystacksize #define YYPREFIX "yy_compile_policy" #line 2 "compilepolicy.y" /* * yacc -d -p yy_compile_policy -o yacc.yy_compile_policy.cc compilepolicy.y */ #include "policy/policy_module.h" #include "libxorp/xorp.h" #include "libproto/config_node_id.hh" #include "policy/configuration.hh" #include "policy/common/policy_utils.hh" #include "policy/test/compilepolicy.hh" extern int yylex(void); extern void yyerror(const char *); extern Configuration _yy_configuration; struct yy_tb { string name; yy_statements* block[3]; }; static vector _yy_terms; static yy_statements* _yy_statements = NULL; /* add blocks to configuration, and delete stuff from memory*/ static void add_blocks(const string& pname, const string& tname, yy_tb& term) { /* source, action, dest*/ for(int i = 0; i < 3; i++) { yy_statements* statements = term.block[i]; /* empty blocks!*/ if(statements == 0) continue; ConfigNodeId order_generator(0, i); ConfigNodeId prev_order(ConfigNodeId::ZERO()); ConfigNodeId order(ConfigNodeId::ZERO()); for(yy_statements::iterator j = statements->begin(); j != statements->end(); ++j) { yy_statement* statement = *j; order = order_generator.generate_unique_node_id(); order.set_position(prev_order.unique_node_id()); prev_order = order; _yy_configuration.update_term_block(pname, tname, i, order, *statement); delete statement; } delete statements; } } #line 67 "compilepolicy.y" typedef union { char *c_str; yy_statements* statements; } YYSTYPE; #line 119 "yacc.yy_compile_policy.cc" #define YYERRCODE 256 #define YY_INT 257 #define YY_STR 258 #define YY_ID 259 #define YY_STATEMENT 260 #define YY_SOURCEBLOCK 261 #define YY_DESTBLOCK 262 #define YY_ACTIONBLOCK 263 #define YY_IPV4 264 #define YY_IPV4NET 265 #define YY_IPV6 266 #define YY_IPV6NET 267 #define YY_SEMICOLON 268 #define YY_LBRACE 269 #define YY_RBRACE 270 #define YY_POLICY_STATEMENT 271 #define YY_TERM 272 #define YY_SOURCE 273 #define YY_DEST 274 #define YY_ACTION 275 #define YY_SET 276 #define YY_EXPORT 277 #define YY_IMPORT 278 const short yy_compile_policylhs[] = { -1, 0, 0, 0, 0, 0, 5, 4, 6, 6, 1, 2, 3, 7, 7, }; const short yy_compile_policylen[] = { 2, 2, 2, 5, 5, 0, 5, 5, 8, 0, 4, 4, 4, 3, 0, }; const short yy_compile_policydefred[] = { 5, 0, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 3, 4, 7, 0, 6, 0, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 10, 0, 14, 8, 13, 11, 0, 12, }; const short yy_compile_policydgoto[] = { 1, 26, 29, 33, 6, 7, 16, 30, }; const short yy_compile_policysindex[] = { 0, -263, -254, -251, -243, -242, 0, 0, -250, -241, -238, -237, 0, -236, -245, -244, -266, -240, 0, 0, 0, -234, 0, -239, -247, -235, -233, 0, -232, -248, -260, 0, -231, -230, -229, 0, -259, 0, 0, 0, 0, -258, 0, }; const short yy_compile_policyrindex[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; const short yy_compile_policygindex[] = { 0, 0, 0, 0, 0, 0, 0, -28, }; #define YYTABLESIZE 41 const short yy_compile_policytable[] = { 34, 34, 34, 36, 20, 8, 21, 9, 2, 41, 35, 40, 42, 3, 4, 5, 10, 11, 13, 12, 14, 15, 17, 18, 19, 23, 25, 32, 22, 0, 24, 0, 0, 0, 27, 0, 0, 31, 37, 39, 38, 28, }; const short yy_compile_policycheck[] = { 260, 260, 260, 31, 270, 259, 272, 258, 271, 37, 270, 270, 270, 276, 277, 278, 259, 259, 259, 269, 258, 258, 258, 268, 268, 259, 273, 275, 268, -1, 269, -1, -1, -1, 269, -1, -1, 269, 269, 268, 270, 274, }; #define YYFINAL 1 #ifndef YYDEBUG #define YYDEBUG 0 #endif #define YYMAXTOKEN 278 #if YYDEBUG const char * const yy_compile_policyname[] = { "end-of-file",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,"YY_INT","YY_STR","YY_ID", "YY_STATEMENT","YY_SOURCEBLOCK","YY_DESTBLOCK","YY_ACTIONBLOCK","YY_IPV4", "YY_IPV4NET","YY_IPV6","YY_IPV6NET","YY_SEMICOLON","YY_LBRACE","YY_RBRACE", "YY_POLICY_STATEMENT","YY_TERM","YY_SOURCE","YY_DEST","YY_ACTION","YY_SET", "YY_EXPORT","YY_IMPORT", }; const char * const yy_compile_policyrule[] = { "$accept : configuration", "configuration : configuration policy_statement", "configuration : configuration set", "configuration : configuration YY_EXPORT YY_ID YY_STR YY_SEMICOLON", "configuration : configuration YY_IMPORT YY_ID YY_STR YY_SEMICOLON", "configuration :", "set : YY_SET YY_STR YY_ID YY_STR YY_SEMICOLON", "policy_statement : YY_POLICY_STATEMENT YY_ID YY_LBRACE terms YY_RBRACE", "terms : terms YY_TERM YY_ID YY_LBRACE source dest action YY_RBRACE", "terms :", "source : YY_SOURCE YY_LBRACE statements YY_RBRACE", "dest : YY_DEST YY_LBRACE statements YY_RBRACE", "action : YY_ACTION YY_LBRACE statements YY_RBRACE", "statements : statements YY_STATEMENT YY_SEMICOLON", "statements :", }; #endif #if YYDEBUG #include #endif #ifdef YYSTACKSIZE #undef YYMAXDEPTH #define YYMAXDEPTH YYSTACKSIZE #else #ifdef YYMAXDEPTH #define YYSTACKSIZE YYMAXDEPTH #else #define YYSTACKSIZE 10000 #define YYMAXDEPTH 10000 #endif #endif #define YYINITSTACKSIZE 200 int yydebug; int yynerrs; int yyerrflag; int yychar; short *yyssp; YYSTYPE *yyvsp; YYSTYPE yyval; YYSTYPE yylval; short *yyss; short *yysslim; YYSTYPE *yyvs; int yystacksize; /* allocate initial stack or double stack size, up to YYMAXDEPTH */ static int yygrowstack() { int newsize, i; short *newss; YYSTYPE *newvs; if ((newsize = yystacksize) == 0) newsize = YYINITSTACKSIZE; else if (newsize >= YYMAXDEPTH) return -1; else if ((newsize *= 2) > YYMAXDEPTH) newsize = YYMAXDEPTH; i = yyssp - yyss; newss = yyss ? (short *)realloc(yyss, newsize * sizeof *newss) : (short *)malloc(newsize * sizeof *newss); if (newss == NULL) return -1; yyss = newss; yyssp = newss + i; newvs = yyvs ? (YYSTYPE *)realloc(yyvs, newsize * sizeof *newvs) : (YYSTYPE *)malloc(newsize * sizeof *newvs); if (newvs == NULL) return -1; yyvs = newvs; yyvsp = newvs + i; yystacksize = newsize; yysslim = yyss + newsize - 1; return 0; } #define YYABORT goto yyabort #define YYREJECT goto yyabort #define YYACCEPT goto yyaccept #define YYERROR goto yyerrlab #ifndef YYPARSE_PARAM #if defined(__cplusplus) || __STDC__ #define YYPARSE_PARAM_ARG void #define YYPARSE_PARAM_DECL #else /* ! ANSI-C/C++ */ #define YYPARSE_PARAM_ARG #define YYPARSE_PARAM_DECL #endif /* ANSI-C/C++ */ #else /* YYPARSE_PARAM */ #ifndef YYPARSE_PARAM_TYPE #define YYPARSE_PARAM_TYPE void * #endif #if defined(__cplusplus) || __STDC__ #define YYPARSE_PARAM_ARG YYPARSE_PARAM_TYPE YYPARSE_PARAM #define YYPARSE_PARAM_DECL #else /* ! ANSI-C/C++ */ #define YYPARSE_PARAM_ARG YYPARSE_PARAM #define YYPARSE_PARAM_DECL YYPARSE_PARAM_TYPE YYPARSE_PARAM; #endif /* ANSI-C/C++ */ #endif /* ! YYPARSE_PARAM */ int yyparse (YYPARSE_PARAM_ARG) YYPARSE_PARAM_DECL { int yym, yyn, yystate; #if YYDEBUG const char *yys; if ((yys = getenv("YYDEBUG"))) { yyn = *yys; if (yyn >= '0' && yyn <= '9') yydebug = yyn - '0'; } #endif yynerrs = 0; yyerrflag = 0; yychar = (-1); if (yyss == NULL && yygrowstack()) goto yyoverflow; yyssp = yyss; yyvsp = yyvs; *yyssp = yystate = 0; yyloop: if ((yyn = yydefred[yystate])) goto yyreduce; if (yychar < 0) { if ((yychar = yylex()) < 0) yychar = 0; #if YYDEBUG if (yydebug) { yys = 0; if (yychar <= YYMAXTOKEN) yys = yyname[yychar]; if (!yys) yys = "illegal-symbol"; printf("%sdebug: state %d, reading %d (%s)\n", YYPREFIX, yystate, yychar, yys); } #endif } if ((yyn = yysindex[yystate]) && (yyn += yychar) >= 0 && yyn <= YYTABLESIZE && yycheck[yyn] == yychar) { #if YYDEBUG if (yydebug) printf("%sdebug: state %d, shifting to state %d\n", YYPREFIX, yystate, yytable[yyn]); #endif if (yyssp >= yysslim && yygrowstack()) { goto yyoverflow; } *++yyssp = yystate = yytable[yyn]; *++yyvsp = yylval; yychar = (-1); if (yyerrflag > 0) --yyerrflag; goto yyloop; } if ((yyn = yyrindex[yystate]) && (yyn += yychar) >= 0 && yyn <= YYTABLESIZE && yycheck[yyn] == yychar) { yyn = yytable[yyn]; goto yyreduce; } if (yyerrflag) goto yyinrecovery; #if defined(lint) || defined(__GNUC__) goto yynewerror; #endif yynewerror: yyerror("syntax error"); #if defined(lint) || defined(__GNUC__) goto yyerrlab; #endif yyerrlab: ++yynerrs; yyinrecovery: if (yyerrflag < 3) { yyerrflag = 3; for (;;) { if ((yyn = yysindex[*yyssp]) && (yyn += YYERRCODE) >= 0 && yyn <= YYTABLESIZE && yycheck[yyn] == YYERRCODE) { #if YYDEBUG if (yydebug) printf("%sdebug: state %d, error recovery shifting\ to state %d\n", YYPREFIX, *yyssp, yytable[yyn]); #endif if (yyssp >= yysslim && yygrowstack()) { goto yyoverflow; } *++yyssp = yystate = yytable[yyn]; *++yyvsp = yylval; goto yyloop; } else { #if YYDEBUG if (yydebug) printf("%sdebug: error recovery discarding state %d\n", YYPREFIX, *yyssp); #endif if (yyssp <= yyss) goto yyabort; --yyssp; --yyvsp; } } } else { if (yychar == 0) goto yyabort; #if YYDEBUG if (yydebug) { yys = 0; if (yychar <= YYMAXTOKEN) yys = yyname[yychar]; if (!yys) yys = "illegal-symbol"; printf("%sdebug: state %d, error recovery discards token %d (%s)\n", YYPREFIX, yystate, yychar, yys); } #endif yychar = (-1); goto yyloop; } yyreduce: #if YYDEBUG if (yydebug) printf("%sdebug: state %d, reducing by rule %d (%s)\n", YYPREFIX, yystate, yyn, yyrule[yyn]); #endif yym = yylen[yyn]; yyval = yyvsp[1-yym]; switch (yyn) { case 3: #line 90 "compilepolicy.y" { list tmp; string proto = yyvsp[-2].c_str; string pols = yyvsp[-1].c_str; free(yyvsp[-2].c_str); free(yyvsp[-1].c_str); policy_utils::str_to_list(pols,tmp); _yy_configuration.update_exports(proto, tmp, ""); } break; case 4: #line 105 "compilepolicy.y" { list tmp; string proto = yyvsp[-2].c_str; string pols = yyvsp[-1].c_str; free(yyvsp[-2].c_str); free(yyvsp[-1].c_str); policy_utils::str_to_list(pols,tmp); _yy_configuration.update_imports(proto, tmp, ""); } break; case 6: #line 122 "compilepolicy.y" { string type = yyvsp[-3].c_str; string id = yyvsp[-2].c_str; string sets = yyvsp[-1].c_str; free(yyvsp[-3].c_str); free(yyvsp[-2].c_str); free(yyvsp[-1].c_str); _yy_configuration.create_set(id); _yy_configuration.update_set(type, id, sets); } break; case 7: #line 136 "compilepolicy.y" { string pname = yyvsp[-3].c_str; free(yyvsp[-3].c_str); _yy_configuration.create_policy(pname); ConfigNodeId order_generator(ConfigNodeId::ZERO()); ConfigNodeId prev_order(ConfigNodeId::ZERO()); ConfigNodeId order(ConfigNodeId::ZERO()); for(vector::iterator i = _yy_terms.begin(); i != _yy_terms.end(); ++i) { yy_tb* term = *i; string& tname = term->name; order = order_generator.generate_unique_node_id(); order.set_position(prev_order.unique_node_id()); prev_order = order; _yy_configuration.create_term(pname, order, tname); add_blocks(pname, tname, *term); delete term; } _yy_terms.clear(); } break; case 8: #line 168 "compilepolicy.y" { yy_tb* tb = new yy_tb; tb->name = yyvsp[-5].c_str; tb->block[0] = yyvsp[-3].statements; tb->block[1] = yyvsp[-2].statements; tb->block[2] = yyvsp[-1].statements; free(yyvsp[-5].c_str); _yy_terms.push_back(tb); } break; case 10: #line 184 "compilepolicy.y" { yy_statements* tmp = _yy_statements; _yy_statements = NULL; yyval.statements = tmp; } break; case 11: #line 193 "compilepolicy.y" { yy_statements* tmp = _yy_statements; _yy_statements = NULL; yyval.statements = tmp; } break; case 12: #line 202 "compilepolicy.y" { yy_statements* tmp = _yy_statements; _yy_statements = NULL; yyval.statements = tmp; } break; case 13: #line 210 "compilepolicy.y" { if (_yy_statements == NULL) { _yy_statements = new yy_statements; } yy_statement* statement = new yy_statement(yyvsp[-1].c_str); statement->append(";"); free(yyvsp[-1].c_str); _yy_statements->push_back(statement); } break; #line 580 "yacc.yy_compile_policy.cc" } yyssp -= yym; yystate = *yyssp; yyvsp -= yym; yym = yylhs[yyn]; if (yystate == 0 && yym == 0) { #if YYDEBUG if (yydebug) printf("%sdebug: after reduction, shifting from state 0 to\ state %d\n", YYPREFIX, YYFINAL); #endif yystate = YYFINAL; *++yyssp = YYFINAL; *++yyvsp = yyval; if (yychar < 0) { if ((yychar = yylex()) < 0) yychar = 0; #if YYDEBUG if (yydebug) { yys = 0; if (yychar <= YYMAXTOKEN) yys = yyname[yychar]; if (!yys) yys = "illegal-symbol"; printf("%sdebug: state %d, reading %d (%s)\n", YYPREFIX, YYFINAL, yychar, yys); } #endif } if (yychar == 0) goto yyaccept; goto yyloop; } if ((yyn = yygindex[yym]) && (yyn += yystate) >= 0 && yyn <= YYTABLESIZE && yycheck[yyn] == yystate) yystate = yytable[yyn]; else yystate = yydgoto[yym]; #if YYDEBUG if (yydebug) printf("%sdebug: after reduction, shifting from state %d \ to state %d\n", YYPREFIX, *yyssp, yystate); #endif if (yyssp >= yysslim && yygrowstack()) { goto yyoverflow; } *++yyssp = yystate; *++yyvsp = yyval; goto yyloop; yyoverflow: yyerror("yacc stack overflow"); yyabort: return (1); yyaccept: return (0); } xorp/policy/tests/file_varrw.hh0000664000076400007640000000366311540224233017006 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/policy/test/file_varrw.hh,v 1.14 2008/10/02 21:58:08 bms Exp $ #ifndef __POLICY_TEST_FILE_VARRW_HH__ #define __POLICY_TEST_FILE_VARRW_HH__ #include "policy/common/varrw.hh" #include "policy/common/policy_exception.hh" #include "policy/common/element_factory.hh" class FileVarRW : public VarRW { public: class Error : public PolicyException { public: Error(const char* file, size_t line, const string& init_why = "") : PolicyException("Error", file, line, init_why) {} }; FileVarRW(); ~FileVarRW(); const Element& read(const Id&); void write(const Id&, const Element&); void sync(); void printVars(); void load(const string& fname); void set_verbose(bool); private: static const int MAX_TRASH = 666; bool doLine(const string& line); void clear_trash(); const Element* _map[VAR_MAX]; ElementFactory _ef; Element* _trash[MAX_TRASH]; int _trashc; bool _verbose; }; #endif // __POLICY_TEST_FILE_VARRW_HH__ xorp/policy/tests/process_watch_fake.hh0000664000076400007640000000215411540225533020476 0ustar greearbgreearb// vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __POLICY_TEST_PROCESS_WATCH_FAKE_HH__ #define __POLICY_TEST_PROCESS_WATCH_FAKE_HH__ #include "policy/process_watch_base.hh" class ProcessWatchFake : public ProcessWatchBase { public: void add_interest(const string& proto); }; #endif // __POLICY_TEST_PROCESS_WATCH_FAKE_HH__ xorp/policy/tests/compilepolicy.cc0000664000076400007640000001715411540224233017504 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net /* * EXIT CODES: * * 4 -- other errors. * * 3 -- bad arguments to program * * 2 -- parse error * 1 -- policy exception * * 0 -- no error * */ #include "policy/policy_module.h" #include "libxorp/xorp.h" #ifdef HAVE_GETOPT_H #include #endif #include "policy/configuration.hh" #include "policy/common/policy_utils.hh" #include "process_watch_fake.hh" #include "filter_manager_fake.hh" using namespace policy_utils; ProcessWatchFake pw; FilterManagerFake fm; Configuration _yy_configuration(pw); typedef Configuration::CodeMap CodeMap; typedef Configuration::TagMap TagMap; ofstream* code_out = NULL; int do_parsing(const string& conf, string& outerr); void print_code(Code& c) { ostream* os = &cout; if(code_out) os = code_out; *os << c.code(); const set& set_names = c.referenced_set_names(); for(set::const_iterator i = set_names.begin(); i != set_names.end(); ++i) { *os << "SET " << *i << " \""; const Element& s = _yy_configuration.sets().getSet(*i); *os << s.str() << "\"\n"; } } void print_codemap(CodeMap& cmap) { for(CodeMap::iterator i = cmap.begin(); i != cmap.end(); ++i) { Code* c = (*i).second; cout << "Printing code for " << (*i).first << endl; print_code(*c); } } void print_sets() { cout << _yy_configuration.sets().str(); } void print_tagmap() { TagMap& tm = _yy_configuration.tagmap(); for(TagMap::iterator i = tm.begin(); i != tm.end(); ++i) { TagSet* ts = (*i).second; cout << "Protocol " << (*i).first << ":"; for(TagSet::iterator iter = ts->begin(); iter != ts->end(); ++iter) cout << " " << *iter; cout << endl; } } void print_target(int,const string&); void print_target(const string& protocol) { print_target(1,protocol); print_target(2,protocol); print_target(4,protocol); } CodeMap& get_codemap(int filterid) { switch(filterid) { case 1: return _yy_configuration.import_filters(); case 2: return _yy_configuration.sourcematch_filters(); case 4: return _yy_configuration.export_filters(); default: cout << "Filterid " << filterid << " Unknown\n"; abort(); } } void print_target(int filterid) { CodeMap& cm = get_codemap(filterid); for(CodeMap::iterator i = cm.begin(); i != cm.end(); ++i) { Code* c = (*i).second; print_code(*c); } } void print_target(int filterid, const string& protocol) { if(filterid == -1) { print_target(protocol); return; } if(protocol.empty()) { print_target(filterid); return; } CodeMap& cm = get_codemap(filterid); CodeMap::iterator i = cm.find(protocol); if(i == cm.end()) { cout << "No code for protocol: " << protocol << " filterid: " << filterid << endl; return; } Code* c = (*i).second; print_code(*c); } void configure_varmap(const string& conf) { istringstream iss(conf); unsigned state = 0; // protocol, variable, type, access, id string tokens[5]; while(!iss.eof()) { string token; // lex =D iss >> token; if(!token.length()) continue; tokens[state] = token; state++; // yacc =D if(state == 5) { _yy_configuration.add_varmap(tokens[0], tokens[1], tokens[2], tokens[3], atoi(tokens[4].c_str())); state = 0; } } } void go(const string& fsrc, const string& fvarmap, int filterid, const string& protocol) { string src; string varmapconf; try { read_file(fsrc,src); read_file(fvarmap,varmapconf); } catch(const PolicyException& e) { cout << "Unable to read file: " << e.str() << endl; exit(4); } _yy_configuration.set_filter_manager(fm); configure_varmap(varmapconf); string err; if(do_parsing(src,err)) { cout << err << endl; exit(2); } _yy_configuration.commit(0); if(filterid != -1 || !protocol.empty()) { print_target(filterid,protocol); return; } // else print it all cout << "Printing import filters..." << endl; print_codemap(_yy_configuration.import_filters()); cout << "Printing source_match filters..." << endl; print_codemap(_yy_configuration.sourcematch_filters()); cout << "Printing export filters..." << endl; print_codemap(_yy_configuration.export_filters()); cout << "Printing sets..." << endl; print_sets(); cout << "Printing tagmap..." << endl; print_tagmap(); } void usage(const char* x) { cout << "Usage: " << x << " \n"; cout << "-h\t\tthis help message\n"; cout << "-s \tsource file of policy\n"; cout << "-m \tfile with policy variables mapping\n"; cout << "-f \ttarget filter to produce code for\n"; cout << "-p \ttarget protocol to produce code for\n"; cout << "-o \tfile where generated code should be stored\n"; exit(3); } int main(int argc, char *argv[]) { int filterid = -1; string protocol(""); string source_file(""); string policy_var_map_file(""); if(argc < 2) usage(argv[0]); int ch; while( (ch = getopt(argc,argv,"hs:m:p:f:o:")) != -1) { switch(ch) { case 's': source_file = optarg; break; case 'm': policy_var_map_file = optarg; break; case 'p': protocol = optarg; break; case 'f': filterid = atoi(optarg); switch(filterid) { case 1: case 2: case 4: break; default: cout << "Invalid filter id: " << filterid << endl; usage(argv[0]); } break; // yea its a hack... case 'o': if(code_out) { cout << "out file already specified\n"; exit(4); } code_out = new ofstream(optarg); if(!code_out->is_open()) { cout << "Unable to open file for writing: " << optarg << endl; exit(4); } break; case 'h': usage(argv[0]); default: usage(argv[0]); } } xlog_init(argv[0], 0); xlog_set_verbose(XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); if(source_file.empty()) { cout << "No source file specified\n\n"; usage(argv[0]); } if (policy_var_map_file.empty()) { cout << "No source file specified for mapping of policy variables\n\n"; usage(argv[0]); } TimeVal start; SystemClock sc; TimerList timerlist(&sc); TimerList::system_gettimeofday(&start); try { go(source_file,policy_var_map_file,filterid,protocol); } catch(const PolicyException& e) { cout << "Compile FAILED" << endl; cout << "PolicyException: " << e.str() << endl; exit(1); } TimeVal elapsed; TimerList::system_gettimeofday(&elapsed); elapsed -= start; if(code_out) { code_out->close(); delete code_out; } xlog_stop(); xlog_exit(); printf("Compile successful in %d milliseconds\n", elapsed.to_ms()); exit(0); } xorp/policy/tests/lex.yy_compile_policy.cc0000664000076400007640000034161411421137511021154 0ustar greearbgreearb#line 2 "lex.yy_compile_policy.cc" #line 4 "lex.yy_compile_policy.cc" #define YY_INT_ALIGNED short int /* A lexical scanner generated by flex */ #define FLEX_SCANNER #define YY_FLEX_MAJOR_VERSION 2 #define YY_FLEX_MINOR_VERSION 5 #define YY_FLEX_SUBMINOR_VERSION 33 #if YY_FLEX_SUBMINOR_VERSION > 0 #define FLEX_BETA #endif /* First, we deal with platform-specific or compiler-specific issues. */ /* begin standard C headers. */ #include #include #include #include /* end standard C headers. */ /* flex integer type definitions */ #ifndef FLEXINT_H #define FLEXINT_H /* C99 systems have . Non-C99 systems may or may not. */ #if __STDC_VERSION__ >= 199901L /* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, * if you want the limit (max/min) macros for int types. */ #ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS 1 #endif #include typedef int8_t flex_int8_t; typedef uint8_t flex_uint8_t; typedef int16_t flex_int16_t; typedef uint16_t flex_uint16_t; typedef int32_t flex_int32_t; typedef uint32_t flex_uint32_t; #else typedef signed char flex_int8_t; typedef short int flex_int16_t; typedef int flex_int32_t; typedef unsigned char flex_uint8_t; typedef unsigned short int flex_uint16_t; typedef unsigned int flex_uint32_t; #endif /* ! C99 */ /* Limits of integral types. */ #ifndef INT8_MIN #define INT8_MIN (-128) #endif #ifndef INT16_MIN #define INT16_MIN (-32767-1) #endif #ifndef INT32_MIN #define INT32_MIN (-2147483647-1) #endif #ifndef INT8_MAX #define INT8_MAX (127) #endif #ifndef INT16_MAX #define INT16_MAX (32767) #endif #ifndef INT32_MAX #define INT32_MAX (2147483647) #endif #ifndef UINT8_MAX #define UINT8_MAX (255U) #endif #ifndef UINT16_MAX #define UINT16_MAX (65535U) #endif #ifndef UINT32_MAX #define UINT32_MAX (4294967295U) #endif #endif /* ! FLEXINT_H */ #ifdef __cplusplus /* The "const" storage-class-modifier is valid. */ #define YY_USE_CONST #else /* ! __cplusplus */ #if __STDC__ #define YY_USE_CONST #endif /* __STDC__ */ #endif /* ! __cplusplus */ #ifdef YY_USE_CONST #define yyconst const #else #define yyconst #endif /* Returned upon end-of-file. */ #define YY_NULL 0 /* Promotes a possibly negative, possibly signed char to an unsigned * integer for use as an array index. If the signed char is negative, * we want to instead treat it as an 8-bit unsigned char, hence the * double cast. */ #define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) /* Enter a start condition. This macro really ought to take a parameter, * but we do it the disgusting crufty way forced on us by the ()-less * definition of BEGIN. */ #define BEGIN (yy_start) = 1 + 2 * /* Translate the current start state into a value that can be later handed * to BEGIN to return to the state. The YYSTATE alias is for lex * compatibility. */ #define YY_START (((yy_start) - 1) / 2) #define YYSTATE YY_START /* Action number for EOF rule of a given start state. */ #define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) /* Special action meaning "start processing a new file". */ #define YY_NEW_FILE yy_compile_policyrestart(yy_compile_policyin ) #define YY_END_OF_BUFFER_CHAR 0 /* Size of default input buffer. */ #ifndef YY_BUF_SIZE #define YY_BUF_SIZE 16384 #endif /* The state buf must be large enough to hold one state per character in the main buffer. */ #define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) #ifndef YY_TYPEDEF_YY_BUFFER_STATE #define YY_TYPEDEF_YY_BUFFER_STATE typedef struct yy_buffer_state *YY_BUFFER_STATE; #endif extern int yy_compile_policyleng; extern FILE *yy_compile_policyin, *yy_compile_policyout; #define EOB_ACT_CONTINUE_SCAN 0 #define EOB_ACT_END_OF_FILE 1 #define EOB_ACT_LAST_MATCH 2 #define YY_LESS_LINENO(n) /* Return all but the first "n" matched characters back to the input stream. */ #define yyless(n) \ do \ { \ /* Undo effects of setting up yy_compile_policytext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ *yy_cp = (yy_hold_char); \ YY_RESTORE_YY_MORE_OFFSET \ (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ YY_DO_BEFORE_ACTION; /* set up yy_compile_policytext again */ \ } \ while ( 0 ) #define unput(c) yyunput( c, (yytext_ptr) ) /* The following is because we cannot portably get our hands on size_t * (without autoconf's help, which isn't available because we want * flex-generated scanners to compile on their own). */ #ifndef YY_TYPEDEF_YY_SIZE_T #define YY_TYPEDEF_YY_SIZE_T typedef unsigned int yy_size_t; #endif #ifndef YY_STRUCT_YY_BUFFER_STATE #define YY_STRUCT_YY_BUFFER_STATE struct yy_buffer_state { FILE *yy_input_file; char *yy_ch_buf; /* input buffer */ char *yy_buf_pos; /* current position in input buffer */ /* Size of input buffer in bytes, not including room for EOB * characters. */ yy_size_t yy_buf_size; /* Number of characters read into yy_ch_buf, not including EOB * characters. */ int yy_n_chars; /* Whether we "own" the buffer - i.e., we know we created it, * and can realloc() it to grow it, and should free() it to * delete it. */ int yy_is_our_buffer; /* Whether this is an "interactive" input source; if so, and * if we're using stdio for input, then we want to use getc() * instead of fread(), to make sure we stop fetching input after * each newline. */ int yy_is_interactive; /* Whether we're considered to be at the beginning of a line. * If so, '^' rules will be active on the next match, otherwise * not. */ int yy_at_bol; int yy_bs_lineno; /**< The line count. */ int yy_bs_column; /**< The column count. */ /* Whether to try to fill the input buffer when we reach the * end of it. */ int yy_fill_buffer; int yy_buffer_status; #define YY_BUFFER_NEW 0 #define YY_BUFFER_NORMAL 1 /* When an EOF's been seen but there's still some text to process * then we mark the buffer as YY_EOF_PENDING, to indicate that we * shouldn't try reading from the input source any more. We might * still have a bunch of tokens to match, though, because of * possible backing-up. * * When we actually see the EOF, we change the status to "new" * (via yy_compile_policyrestart()), so that the user can continue scanning by * just pointing yy_compile_policyin at a new input file. */ #define YY_BUFFER_EOF_PENDING 2 }; #endif /* !YY_STRUCT_YY_BUFFER_STATE */ /* Stack of input buffers. */ static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */ static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */ static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */ /* We provide macros for accessing buffer states in case in the * future we want to put the buffer states in a more general * "scanner state". * * Returns the top of the stack, or NULL. */ #define YY_CURRENT_BUFFER ( (yy_buffer_stack) \ ? (yy_buffer_stack)[(yy_buffer_stack_top)] \ : NULL) /* Same as previous macro, but useful when we know that the buffer stack is not * NULL or when we need an lvalue. For internal use only. */ #define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)] /* yy_hold_char holds the character lost when yy_compile_policytext is formed. */ static char yy_hold_char; static int yy_n_chars; /* number of characters read into yy_ch_buf */ int yy_compile_policyleng; /* Points to current character in buffer. */ static char *yy_c_buf_p = (char *) 0; static int yy_init = 0; /* whether we need to initialize */ static int yy_start = 0; /* start state number */ /* Flag which is used to allow yy_compile_policywrap()'s to do buffer switches * instead of setting up a fresh yy_compile_policyin. A bit of a hack ... */ static int yy_did_buffer_switch_on_eof; void yy_compile_policyrestart (FILE *input_file ); void yy_compile_policy_switch_to_buffer (YY_BUFFER_STATE new_buffer ); YY_BUFFER_STATE yy_compile_policy_create_buffer (FILE *file,int size ); void yy_compile_policy_delete_buffer (YY_BUFFER_STATE b ); void yy_compile_policy_flush_buffer (YY_BUFFER_STATE b ); void yy_compile_policypush_buffer_state (YY_BUFFER_STATE new_buffer ); void yy_compile_policypop_buffer_state (void ); static void yy_compile_policyensure_buffer_stack (void ); static void yy_compile_policy_load_buffer_state (void ); static void yy_compile_policy_init_buffer (YY_BUFFER_STATE b,FILE *file ); #define YY_FLUSH_BUFFER yy_compile_policy_flush_buffer(YY_CURRENT_BUFFER ) YY_BUFFER_STATE yy_compile_policy_scan_buffer (char *base,yy_size_t size ); YY_BUFFER_STATE yy_compile_policy_scan_string (yyconst char *yy_str ); YY_BUFFER_STATE yy_compile_policy_scan_bytes (yyconst char *bytes,int len ); void *yy_compile_policyalloc (yy_size_t ); void *yy_compile_policyrealloc (void *,yy_size_t ); void yy_compile_policyfree (void * ); #define yy_new_buffer yy_compile_policy_create_buffer #define yy_set_interactive(is_interactive) \ { \ if ( ! YY_CURRENT_BUFFER ){ \ yy_compile_policyensure_buffer_stack (); \ YY_CURRENT_BUFFER_LVALUE = \ yy_compile_policy_create_buffer(yy_compile_policyin,YY_BUF_SIZE ); \ } \ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ } #define yy_set_bol(at_bol) \ { \ if ( ! YY_CURRENT_BUFFER ){\ yy_compile_policyensure_buffer_stack (); \ YY_CURRENT_BUFFER_LVALUE = \ yy_compile_policy_create_buffer(yy_compile_policyin,YY_BUF_SIZE ); \ } \ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ } #define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) /* Begin user sect3 */ #define yy_compile_policywrap(n) 1 #define YY_SKIP_YYWRAP typedef unsigned char YY_CHAR; FILE *yy_compile_policyin = (FILE *) 0, *yy_compile_policyout = (FILE *) 0; typedef int yy_state_type; extern int yy_compile_policylineno; int yy_compile_policylineno = 1; extern char *yy_compile_policytext; #define yytext_ptr yy_compile_policytext static yy_state_type yy_get_previous_state (void ); static yy_state_type yy_try_NUL_trans (yy_state_type current_state ); static int yy_get_next_buffer (void ); static void yy_fatal_error (yyconst char msg[] ); /* Done after the current pattern has been matched and before the * corresponding action - sets up yy_compile_policytext. */ #define YY_DO_BEFORE_ACTION \ (yytext_ptr) = yy_bp; \ yy_compile_policyleng = (size_t) (yy_cp - yy_bp); \ (yy_hold_char) = *yy_cp; \ *yy_cp = '\0'; \ (yy_c_buf_p) = yy_cp; #define YY_NUM_RULES 27 #define YY_END_OF_BUFFER 28 /* This struct is not used in this scanner, but its presence is necessary. */ struct yy_trans_info { flex_int32_t yy_verify; flex_int32_t yy_nxt; }; static yyconst flex_int16_t yy_accept[701] = { 0, 0, 0, 0, 0, 10, 10, 28, 26, 25, 24, 1, 26, 26, 26, 26, 23, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 2, 3, 12, 11, 10, 10, 7, 8, 9, 25, 0, 0, 0, 0, 0, 0, 0, 15, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 12, 10, 10, 8, 9, 8, 9, 0, 0, 0, 0, 0, 0, 15, 0, 0, 15, 15, 15, 15, 22, 19, 22, 22, 22, 22, 22, 22, 22, 0, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15, 15, 16, 16, 0, 15, 0, 15, 15, 15, 15, 22, 22, 5, 22, 22, 22, 22, 17, 0, 0, 0, 0, 0, 0, 15, 15, 0, 15, 15, 15, 15, 16, 16, 16, 0, 0, 0, 15, 15, 15, 15, 15, 15, 15, 22, 22, 22, 22, 22, 0, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 16, 0, 0, 0, 0, 0, 15, 0, 15, 0, 15, 15, 15, 15, 6, 21, 20, 22, 4, 13, 13, 13, 0, 0, 0, 15, 15, 0, 15, 15, 15, 15, 15, 15, 0, 15, 15, 15, 15, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15, 15, 15, 15, 15, 0, 0, 13, 13, 13, 13, 0, 0, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 15, 0, 15, 15, 15, 15, 0, 14, 14, 14, 13, 0, 0, 15, 15, 0, 15, 15, 15, 15, 15, 15, 0, 15, 15, 15, 15, 15, 15, 0, 15, 15, 15, 15, 15, 15, 15, 0, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15, 15, 15, 15, 15, 0, 0, 0, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 15, 0, 15, 15, 15, 15, 0, 0, 0, 15, 15, 0, 15, 15, 15, 15, 15, 15, 0, 15, 15, 15, 15, 15, 15, 0, 15, 15, 15, 15, 15, 15, 0, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15, 15, 15, 15, 15, 0, 0, 0, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 15, 0, 15, 15, 15, 15, 0, 0, 0, 0, 0, 15, 0, 15, 0, 15, 15, 15, 15, 15, 15, 0, 15, 15, 15, 15, 15, 15, 0, 15, 15, 15, 15, 15, 15, 0, 15, 15, 15, 15, 15, 15, 0, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15, 15, 15, 15, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 15, 0, 15, 15, 15, 15, 0, 0, 0, 0, 0, 0, 15, 15, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15, 15, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 15, 18, 0, 0, 0, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, 15, 15, 15, 15, 0, 0, 0, 0, 0, 15, 15, 15, 15, 0, 15, 15, 15, 15, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0 } ; static yyconst flex_int32_t yy_ec[256] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 14, 14, 15, 16, 17, 1, 1, 1, 1, 1, 18, 18, 18, 18, 19, 18, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21, 22, 20, 20, 20, 20, 20, 20, 1, 1, 1, 1, 23, 1, 24, 18, 25, 26, 27, 18, 20, 20, 28, 20, 20, 29, 30, 31, 32, 33, 20, 34, 35, 36, 37, 20, 20, 38, 39, 20, 40, 1, 41, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 } ; static yyconst flex_int32_t yy_meta[42] = { 0, 1, 1, 1, 2, 1, 3, 4, 5, 5, 5, 5, 5, 5, 5, 5, 6, 7, 8, 8, 1, 1, 1, 1, 8, 8, 8, 8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 7, 7 } ; static yyconst flex_int16_t yy_base[924] = { 0, 0, 0, 2177, 2176, 40, 42, 2177, 3856, 2162, 3856, 3856, 78, 98, 38, 2146, 3856, 106, 138, 2141, 2134, 2131, 2119, 2109, 2106, 2105, 2109, 3856, 3856, 0, 3856, 0, 58, 3856, 50, 52, 2131, 54, 160, 2101, 2100, 180, 51, 52, 189, 209, 0, 2093, 2078, 2076, 2067, 2065, 2068, 2059, 2061, 0, 0, 203, 67, 69, 71, 73, 2088, 239, 2087, 61, 79, 2061, 248, 161, 2067, 270, 290, 229, 299, 308, 0, 2047, 2038, 2039, 2026, 2028, 2021, 2024, 150, 2047, 2046, 322, 2045, 2000, 1999, 1998, 332, 352, 299, 361, 0, 172, 201, 372, 392, 405, 414, 64, 424, 1997, 1980, 0, 1977, 1976, 1984, 1972, 0, 1986, 435, 1985, 1984, 1970, 1969, 444, 466, 486, 426, 506, 86, 516, 3856, 0, 1957, 1965, 527, 1964, 518, 537, 548, 568, 361, 577, 579, 1938, 1930, 1916, 1894, 1901, 221, 1921, 1920, 582, 1916, 1905, 1892, 1891, 592, 612, 426, 621, 623, 625, 636, 656, 518, 665, 667, 3856, 260, 1900, 1899, 670, 1896, 670, 282, 681, 701, 714, 723, 211, 733, 0, 0, 0, 1883, 0, 1862, 743, 1857, 1857, 1846, 1842, 752, 774, 794, 807, 816, 250, 826, 827, 838, 858, 871, 880, 299, 890, 1851, 901, 1838, 1837, 1836, 911, 1835, 892, 921, 932, 952, 537, 961, 963, 1803, 972, 1817, 1798, 981, 1793, 1783, 1782, 1778, 991, 1011, 733, 1020, 1022, 1024, 1035, 1055, 827, 1064, 1066, 1068, 1070, 1081, 1101, 892, 1110, 1112, 321, 1787, 1774, 1115, 1773, 344, 1772, 1771, 1117, 1768, 1117, 384, 1128, 1148, 1161, 1170, 310, 1180, 1722, 456, 405, 3856, 1750, 1740, 1739, 1190, 1212, 1232, 1245, 1254, 361, 1264, 1265, 1276, 1296, 1309, 1318, 394, 1328, 1329, 1340, 1360, 1373, 1382, 446, 1392, 1745, 1402, 1744, 1734, 1733, 1412, 1715, 1710, 1709, 1422, 1708, 1432, 1434, 1445, 1465, 921, 1474, 1476, 1686, 1693, 1680, 1679, 1487, 1507, 963, 1516, 1518, 1520, 1531, 1551, 981, 1560, 1562, 1564, 1566, 1577, 1597, 1180, 1606, 1608, 1610, 1612, 1623, 1643, 1265, 1652, 1654, 1687, 1686, 1657, 1683, 478, 1671, 1660, 1659, 1657, 498, 1656, 1640, 1661, 1639, 1662, 560, 1673, 1693, 1706, 1715, 488, 1725, 1608, 1627, 1624, 1735, 1757, 1777, 1790, 1799, 537, 1809, 1810, 1821, 1841, 1854, 1863, 575, 1873, 1874, 1885, 1905, 1918, 1927, 577, 1937, 1938, 1949, 1969, 1982, 1991, 598, 2001, 1620, 1618, 2011, 1616, 1615, 1614, 2021, 1594, 1593, 1592, 2031, 1591, 2041, 2043, 2054, 2074, 1329, 2083, 2085, 1567, 1565, 1563, 2094, 2116, 2136, 1392, 2145, 2147, 2149, 2160, 2180, 1434, 2189, 2191, 2193, 2195, 2206, 2226, 1476, 2235, 2237, 2239, 2241, 2252, 2272, 1725, 2281, 2283, 2285, 2287, 2298, 2318, 1810, 2327, 2329, 1570, 1568, 2332, 1567, 625, 1548, 1547, 2334, 1546, 648, 1545, 1542, 2336, 1529, 2337, 670, 2348, 2368, 2381, 2390, 600, 2400, 1503, 1515, 2411, 2431, 1874, 1523, 1513, 2441, 0, 2402, 2461, 687, 2471, 2472, 2483, 0, 2496, 2505, 689, 2515, 2516, 2527, 0, 2540, 2549, 703, 2559, 2560, 2571, 0, 2584, 2593, 708, 2603, 2604, 2615, 0, 2628, 2637, 733, 2647, 1521, 1503, 2657, 1502, 1502, 1501, 2667, 1498, 1485, 1462, 2677, 1461, 2687, 2689, 2700, 2720, 1938, 2729, 2731, 1439, 1449, 764, 2742, 1446, 1433, 2762, 754, 1413, 1400, 2733, 2772, 1399, 2774, 2776, 2778, 1381, 2780, 2791, 2793, 1376, 2795, 2797, 2799, 1375, 2810, 2812, 2814, 1374, 2816, 1370, 1369, 2820, 1356, 786, 1356, 1355, 2823, 1354, 807, 1351, 1337, 2825, 1318, 2817, 850, 2835, 0, 2848, 2857, 796, 2867, 1288, 1312, 2878, 1311, 2022, 2668, 1306, 1305, 2869, 1292, 2887, 1291, 2888, 1290, 2889, 1289, 2890, 1286, 2891, 1272, 1253, 1248, 2901, 1247, 1247, 1243, 2911, 1242, 1229, 1228, 2921, 1227, 2931, 2933, 1225, 2935, 1193, 871, 1207, 1206, 2938, 1205, 1194, 1200, 1199, 1188, 1187, 1169, 1164, 1163, 1162, 1158, 2941, 1157, 944, 1145, 1144, 2944, 1143, 1003, 1142, 1139, 2948, 1121, 2950, 1118, 3856, 1116, 2961, 1114, 1098, 1096, 1095, 1094, 1091, 1078, 1076, 1074, 1072, 1071, 2970, 1051, 1046, 1033, 2980, 1031, 1029, 1007, 1024, 1002, 972, 2990, 949, 942, 929, 2992, 928, 1047, 912, 880, 2994, 869, 867, 854, 3003, 848, 835, 815, 790, 3012, 784, 769, 722, 717, 3021, 691, 675, 668, 3023, 652, 646, 633, 3856, 3038, 3046, 3054, 3062, 633, 3066, 3070, 3076, 3079, 3082, 3086, 631, 3091, 604, 3097, 3098, 3102, 3107, 589, 582, 3112, 3118, 3121, 3122, 3126, 3131, 566, 3137, 3140, 3143, 3148, 560, 3154, 3155, 3159, 3164, 3170, 3175, 504, 3181, 484, 3183, 3188, 3190, 3191, 3195, 3200, 3205, 3211, 3214, 3217, 3220, 3223, 3228, 3233, 3234, 3238, 3243, 3249, 3254, 3260, 3265, 478, 3271, 427, 3274, 415, 3276, 3280, 3284, 3289, 3294, 3299, 3304, 3306, 3309, 3312, 3315, 3318, 3321, 3326, 3330, 3334, 3339, 3345, 3350, 3356, 3361, 3367, 3372, 3377, 390, 3380, 384, 3383, 350, 3385, 3389, 3393, 3398, 3403, 3408, 3413, 3418, 3420, 3423, 3426, 3429, 3432, 3435, 3440, 3444, 3449, 3455, 3460, 3466, 3471, 3477, 3482, 3488, 3493, 3498, 344, 3501, 288, 3504, 282, 3506, 3510, 3515, 3519, 3523, 3528, 3532, 3537, 3541, 3546, 3550, 3555, 3559, 3564, 3569, 3571, 3574, 3577, 3580, 3583, 3586, 3591, 266, 3595, 3599, 3604, 3610, 3615, 3621, 3626, 3632, 3637, 3643, 3648, 3654, 3659, 3664, 241, 3667, 227, 3670, 207, 3671, 3676, 3682, 3685, 3688, 3693, 3698, 3703, 3708, 3713, 3718, 3723, 3728, 3730, 3733, 3736, 3739, 3742, 3745, 3750, 178, 3756, 3758, 3763, 3768, 3773, 3778, 3783, 3788, 3793, 156, 3796, 150, 3799, 3801, 3807, 3810, 3812, 3817, 3819, 3822, 3825, 3827, 118, 3833, 3835, 51, 3838, 3840, 3842, 3844, 3846, 3848, 3850 } ; static yyconst flex_int16_t yy_def[924] = { 0, 700, 1, 701, 701, 702, 702, 700, 700, 700, 700, 700, 700, 12, 13, 700, 700, 700, 17, 18, 17, 17, 17, 18, 18, 18, 18, 700, 700, 703, 700, 704, 704, 700, 700, 700, 700, 705, 700, 706, 707, 38, 41, 707, 700, 17, 18, 18, 45, 45, 18, 18, 18, 18, 18, 703, 704, 704, 700, 700, 700, 700, 708, 700, 709, 710, 710, 711, 700, 710, 712, 700, 71, 72, 713, 45, 18, 18, 18, 18, 18, 18, 18, 18, 714, 709, 715, 715, 700, 700, 716, 717, 700, 92, 93, 718, 719, 719, 720, 700, 700, 721, 99, 102, 721, 18, 18, 18, 18, 18, 18, 18, 18, 722, 700, 723, 700, 724, 725, 700, 700, 700, 726, 120, 123, 726, 700, 727, 727, 728, 700, 729, 730, 730, 700, 134, 135, 731, 730, 18, 18, 18, 18, 18, 732, 723, 733, 733, 700, 700, 734, 735, 700, 152, 153, 736, 737, 737, 700, 158, 159, 738, 737, 700, 739, 729, 740, 740, 700, 700, 741, 700, 700, 742, 171, 174, 742, 18, 18, 18, 18, 18, 743, 700, 744, 700, 745, 746, 700, 700, 700, 747, 189, 192, 747, 700, 700, 700, 748, 196, 199, 748, 749, 700, 750, 700, 751, 700, 752, 753, 753, 700, 211, 212, 754, 753, 700, 700, 744, 755, 755, 700, 700, 756, 757, 700, 225, 226, 758, 759, 759, 700, 231, 232, 760, 759, 761, 761, 700, 238, 239, 762, 761, 763, 750, 764, 764, 700, 765, 752, 766, 766, 700, 700, 767, 700, 700, 768, 255, 258, 768, 700, 217, 700, 700, 700, 769, 770, 700, 700, 700, 771, 269, 272, 771, 700, 700, 700, 772, 276, 279, 772, 700, 700, 700, 773, 283, 286, 773, 774, 700, 775, 700, 776, 700, 777, 700, 778, 700, 779, 780, 780, 700, 302, 303, 781, 780, 700, 700, 782, 783, 700, 311, 312, 784, 785, 785, 700, 317, 318, 786, 785, 787, 787, 700, 324, 325, 788, 787, 789, 789, 700, 331, 332, 790, 789, 775, 791, 791, 700, 792, 777, 793, 793, 700, 794, 779, 795, 795, 700, 700, 796, 700, 700, 797, 352, 355, 797, 700, 798, 799, 700, 700, 700, 800, 362, 365, 800, 700, 700, 700, 801, 369, 372, 801, 700, 700, 700, 802, 376, 379, 802, 700, 700, 700, 803, 383, 386, 803, 700, 804, 700, 805, 700, 806, 700, 807, 700, 808, 700, 809, 810, 810, 700, 403, 404, 811, 810, 700, 700, 812, 700, 700, 412, 413, 813, 814, 814, 700, 418, 419, 815, 814, 816, 816, 700, 425, 426, 817, 816, 818, 818, 700, 432, 433, 819, 818, 820, 820, 700, 439, 440, 821, 820, 805, 822, 822, 700, 823, 807, 824, 824, 700, 825, 809, 826, 826, 700, 700, 827, 700, 700, 828, 460, 463, 828, 700, 829, 700, 468, 469, 830, 831, 700, 832, 833, 473, 476, 833, 700, 700, 834, 835, 480, 483, 835, 700, 700, 836, 837, 487, 490, 837, 700, 700, 838, 839, 494, 497, 839, 700, 700, 840, 841, 501, 504, 841, 700, 842, 700, 843, 700, 844, 700, 845, 700, 846, 700, 847, 848, 848, 700, 521, 522, 849, 848, 700, 700, 850, 700, 851, 852, 529, 532, 852, 853, 854, 854, 855, 854, 856, 856, 857, 856, 858, 858, 859, 858, 860, 860, 861, 860, 862, 862, 863, 862, 843, 864, 864, 700, 865, 845, 866, 866, 700, 867, 847, 868, 868, 700, 700, 869, 700, 870, 871, 572, 575, 871, 700, 872, 700, 873, 874, 874, 875, 700, 874, 876, 700, 877, 700, 878, 700, 879, 700, 880, 700, 881, 700, 882, 700, 883, 700, 884, 700, 885, 700, 886, 700, 887, 888, 888, 889, 888, 700, 890, 873, 891, 891, 700, 700, 892, 893, 894, 895, 896, 897, 898, 883, 899, 899, 700, 900, 885, 901, 901, 700, 902, 887, 903, 903, 700, 700, 904, 700, 905, 700, 906, 700, 907, 700, 700, 700, 700, 700, 700, 700, 908, 700, 909, 700, 910, 700, 911, 700, 912, 913, 906, 914, 914, 700, 909, 915, 915, 700, 916, 911, 917, 917, 700, 700, 918, 700, 919, 700, 700, 920, 700, 921, 700, 919, 922, 922, 700, 921, 923, 923, 700, 700, 700, 0, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700 } ; static yyconst flex_int16_t yy_nxt[3898] = { 0, 8, 9, 10, 11, 8, 8, 8, 12, 12, 13, 14, 14, 14, 14, 14, 15, 16, 17, 17, 18, 19, 18, 8, 20, 17, 21, 22, 23, 18, 18, 18, 18, 24, 18, 25, 26, 18, 18, 18, 27, 28, 32, 32, 32, 32, 43, 43, 43, 43, 43, 43, 60, 60, 61, 61, 688, 33, 37, 33, 57, 57, 62, 62, 63, 66, 66, 37, 39, 60, 60, 61, 61, 60, 60, 61, 61, 39, 133, 133, 34, 35, 34, 35, 37, 700, 38, 38, 38, 38, 38, 38, 38, 38, 39, 39, 40, 40, 58, 59, 157, 157, 40, 40, 40, 40, 41, 41, 41, 41, 41, 42, 43, 43, 45, 45, 45, 45, 45, 45, 45, 45, 39, 683, 45, 45, 46, 46, 46, 46, 45, 45, 45, 45, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 700, 663, 46, 46, 113, 113, 114, 659, 46, 46, 46, 46, 37, 37, 65, 65, 65, 65, 65, 65, 65, 65, 39, 39, 66, 66, 127, 127, 128, 647, 66, 66, 66, 66, 69, 69, 69, 69, 69, 69, 69, 69, 70, 71, 71, 72, 73, 73, 73, 73, 73, 57, 57, 74, 74, 129, 129, 130, 609, 74, 74, 74, 74, 75, 75, 75, 75, 75, 75, 75, 75, 210, 210, 75, 75, 182, 182, 183, 605, 75, 75, 75, 75, 104, 104, 104, 104, 104, 104, 58, 59, 84, 601, 86, 86, 86, 86, 86, 87, 88, 88, 70, 92, 92, 93, 94, 94, 94, 94, 94, 230, 230, 95, 95, 202, 202, 203, 581, 95, 95, 95, 95, 98, 70, 99, 99, 99, 99, 99, 99, 99, 99, 100, 518, 101, 101, 206, 206, 207, 514, 101, 101, 101, 101, 102, 102, 102, 102, 102, 103, 104, 104, 70, 125, 125, 125, 125, 125, 125, 237, 237, 100, 105, 105, 105, 105, 105, 105, 105, 105, 301, 301, 105, 105, 84, 289, 289, 290, 105, 105, 105, 105, 700, 700, 98, 70, 120, 120, 120, 120, 120, 120, 120, 120, 121, 510, 122, 122, 293, 293, 294, 400, 122, 122, 122, 122, 123, 123, 123, 123, 123, 124, 125, 125, 70, 176, 176, 176, 176, 176, 176, 316, 316, 121, 98, 70, 132, 132, 132, 132, 132, 132, 132, 132, 100, 396, 133, 133, 297, 297, 298, 392, 133, 133, 133, 133, 134, 134, 135, 136, 136, 136, 136, 136, 323, 323, 137, 137, 70, 264, 264, 264, 137, 137, 137, 137, 299, 100, 138, 138, 138, 138, 138, 138, 138, 138, 98, 70, 295, 70, 194, 194, 194, 194, 194, 194, 100, 144, 121, 146, 146, 146, 146, 146, 147, 148, 148, 70, 152, 152, 153, 154, 154, 154, 154, 154, 330, 330, 155, 155, 264, 264, 264, 264, 155, 155, 155, 155, 98, 70, 156, 156, 156, 156, 156, 156, 156, 156, 121, 291, 157, 157, 390, 390, 391, 208, 157, 157, 157, 157, 158, 158, 159, 160, 160, 160, 160, 160, 402, 402, 161, 161, 394, 394, 395, 204, 161, 161, 161, 161, 162, 162, 162, 162, 162, 162, 162, 162, 98, 70, 98, 70, 201, 201, 201, 201, 201, 201, 121, 164, 100, 166, 166, 166, 166, 166, 167, 168, 168, 700, 70, 260, 260, 260, 260, 260, 260, 417, 417, 100, 170, 70, 171, 171, 171, 171, 171, 171, 171, 171, 172, 184, 173, 173, 398, 398, 399, 163, 173, 173, 173, 173, 174, 174, 174, 174, 174, 175, 176, 176, 70, 98, 70, 131, 144, 424, 424, 431, 431, 172, 126, 100, 700, 700, 98, 70, 189, 189, 189, 189, 189, 189, 189, 189, 190, 115, 191, 191, 438, 438, 520, 520, 191, 191, 191, 191, 192, 192, 192, 192, 192, 193, 194, 194, 70, 98, 70, 700, 70, 508, 508, 509, 96, 190, 64, 121, 70, 121, 170, 70, 196, 196, 196, 196, 196, 196, 196, 196, 197, 70, 198, 198, 512, 512, 513, 70, 198, 198, 198, 198, 199, 199, 199, 199, 199, 200, 201, 201, 70, 98, 70, 70, 164, 70, 516, 516, 517, 197, 70, 121, 700, 700, 100, 170, 70, 209, 209, 209, 209, 209, 209, 209, 209, 172, 70, 210, 210, 537, 537, 541, 541, 210, 210, 210, 210, 211, 211, 212, 213, 213, 213, 213, 213, 545, 545, 214, 214, 70, 549, 549, 70, 214, 214, 214, 214, 70, 172, 215, 215, 215, 215, 215, 215, 215, 215, 170, 70, 274, 274, 274, 274, 274, 274, 553, 553, 172, 217, 219, 219, 219, 219, 219, 220, 221, 221, 70, 225, 225, 226, 227, 227, 227, 227, 227, 583, 583, 228, 228, 579, 579, 580, 675, 228, 228, 228, 228, 98, 70, 229, 229, 229, 229, 229, 229, 229, 229, 190, 70, 230, 230, 599, 599, 600, 70, 230, 230, 230, 230, 231, 231, 232, 233, 233, 233, 233, 233, 611, 611, 234, 234, 70, 603, 603, 604, 234, 234, 234, 234, 70, 190, 235, 235, 235, 235, 235, 235, 235, 235, 98, 70, 70, 281, 281, 281, 281, 281, 281, 666, 190, 121, 170, 70, 236, 236, 236, 236, 236, 236, 236, 236, 197, 70, 237, 237, 607, 607, 608, 70, 237, 237, 237, 237, 238, 238, 239, 240, 240, 240, 240, 240, 70, 675, 241, 241, 70, 645, 645, 646, 241, 241, 241, 241, 675, 197, 242, 242, 242, 242, 242, 242, 242, 242, 170, 70, 170, 70, 288, 288, 288, 288, 288, 288, 197, 243, 172, 245, 245, 245, 245, 245, 246, 247, 247, 248, 675, 250, 250, 250, 250, 250, 251, 252, 252, 700, 70, 357, 357, 357, 357, 357, 357, 70, 70, 172, 254, 70, 255, 255, 255, 255, 255, 255, 255, 255, 256, 70, 257, 257, 657, 657, 658, 666, 257, 257, 257, 257, 258, 258, 258, 258, 258, 259, 260, 260, 70, 170, 70, 367, 367, 367, 367, 367, 367, 256, 666, 172, 262, 262, 262, 263, 264, 264, 264, 264, 217, 374, 374, 374, 374, 374, 374, 700, 700, 98, 70, 269, 269, 269, 269, 269, 269, 269, 269, 270, 666, 271, 271, 661, 661, 662, 70, 271, 271, 271, 271, 272, 272, 272, 272, 272, 273, 274, 274, 70, 98, 70, 700, 70, 681, 681, 682, 637, 270, 675, 190, 675, 190, 170, 70, 276, 276, 276, 276, 276, 276, 276, 276, 277, 632, 278, 278, 686, 686, 687, 70, 278, 278, 278, 278, 279, 279, 279, 279, 279, 280, 281, 281, 70, 98, 70, 170, 70, 700, 70, 70, 70, 277, 70, 190, 70, 197, 70, 197, 254, 70, 283, 283, 283, 283, 283, 283, 283, 283, 284, 70, 285, 285, 70, 70, 70, 615, 285, 285, 285, 285, 286, 286, 286, 286, 286, 287, 288, 288, 70, 170, 70, 666, 243, 666, 248, 70, 70, 284, 637, 197, 700, 700, 700, 700, 172, 254, 70, 300, 300, 300, 300, 300, 300, 300, 300, 256, 637, 301, 301, 637, 632, 632, 632, 301, 301, 301, 301, 302, 302, 303, 304, 304, 304, 304, 304, 70, 70, 305, 305, 70, 70, 70, 70, 305, 305, 305, 305, 70, 256, 306, 306, 306, 306, 306, 306, 306, 306, 254, 70, 381, 381, 381, 381, 381, 381, 70, 70, 256, 70, 311, 311, 312, 313, 313, 313, 313, 313, 70, 70, 314, 314, 530, 615, 615, 615, 314, 314, 314, 314, 98, 70, 315, 315, 315, 315, 315, 315, 315, 315, 270, 644, 316, 316, 70, 637, 637, 565, 316, 316, 316, 316, 317, 317, 318, 319, 319, 319, 319, 319, 632, 632, 320, 320, 70, 560, 70, 70, 320, 320, 320, 320, 70, 270, 321, 321, 321, 321, 321, 321, 321, 321, 98, 70, 70, 388, 388, 388, 388, 388, 388, 70, 270, 190, 170, 70, 322, 322, 322, 322, 322, 322, 322, 322, 277, 70, 323, 323, 70, 70, 70, 70, 323, 323, 323, 323, 324, 324, 325, 326, 326, 326, 326, 326, 70, 70, 327, 327, 70, 615, 615, 614, 327, 327, 327, 327, 565, 277, 328, 328, 328, 328, 328, 328, 328, 328, 170, 70, 70, 465, 465, 465, 465, 465, 465, 565, 277, 197, 254, 70, 329, 329, 329, 329, 329, 329, 329, 329, 284, 565, 330, 330, 560, 560, 560, 70, 330, 330, 330, 330, 331, 331, 332, 333, 333, 333, 333, 333, 70, 70, 334, 334, 70, 70, 70, 70, 334, 334, 334, 334, 70, 284, 335, 335, 335, 335, 335, 335, 335, 335, 254, 70, 478, 478, 478, 478, 478, 478, 70, 70, 284, 70, 337, 337, 337, 337, 337, 338, 339, 339, 340, 528, 342, 342, 342, 342, 342, 343, 344, 344, 345, 530, 347, 347, 347, 347, 347, 348, 349, 349, 254, 70, 700, 70, 485, 485, 485, 485, 485, 485, 256, 530, 256, 351, 70, 352, 352, 352, 352, 352, 352, 352, 352, 353, 585, 354, 354, 411, 578, 565, 565, 354, 354, 354, 354, 355, 355, 355, 355, 355, 356, 357, 357, 70, 254, 70, 492, 492, 492, 492, 492, 492, 353, 453, 256, 98, 70, 362, 362, 362, 362, 362, 362, 362, 362, 363, 560, 364, 364, 560, 448, 70, 70, 364, 364, 364, 364, 365, 365, 365, 365, 365, 366, 367, 367, 70, 98, 70, 700, 70, 70, 530, 70, 411, 363, 526, 270, 453, 270, 170, 70, 369, 369, 369, 369, 369, 369, 369, 369, 370, 453, 371, 371, 453, 448, 448, 448, 371, 371, 371, 371, 372, 372, 372, 372, 372, 373, 374, 374, 70, 98, 70, 170, 70, 700, 70, 70, 70, 370, 70, 270, 411, 277, 310, 277, 254, 70, 376, 376, 376, 376, 376, 376, 376, 376, 377, 466, 378, 378, 453, 453, 345, 448, 378, 378, 378, 378, 379, 379, 379, 379, 379, 380, 381, 381, 70, 170, 70, 254, 70, 700, 70, 448, 340, 377, 70, 277, 70, 284, 70, 284, 351, 70, 383, 383, 383, 383, 383, 383, 383, 383, 384, 411, 385, 385, 310, 408, 345, 345, 385, 385, 385, 385, 386, 386, 386, 386, 386, 387, 388, 388, 70, 254, 70, 345, 340, 70, 340, 340, 345, 384, 70, 284, 700, 700, 700, 700, 700, 700, 340, 256, 351, 70, 401, 401, 401, 401, 401, 401, 401, 401, 353, 70, 402, 402, 70, 70, 361, 310, 402, 402, 402, 402, 403, 403, 404, 405, 405, 405, 405, 405, 224, 358, 406, 406, 70, 345, 345, 248, 406, 406, 406, 406, 340, 353, 407, 407, 407, 407, 407, 407, 407, 407, 351, 70, 499, 499, 499, 499, 499, 499, 340, 243, 353, 70, 412, 412, 413, 414, 414, 414, 414, 414, 70, 70, 415, 415, 310, 224, 217, 307, 415, 415, 415, 415, 98, 70, 416, 416, 416, 416, 416, 416, 416, 416, 363, 248, 417, 417, 248, 248, 243, 243, 417, 417, 417, 417, 418, 418, 419, 420, 420, 420, 420, 420, 243, 268, 421, 421, 70, 224, 151, 217, 421, 421, 421, 421, 217, 363, 422, 422, 422, 422, 422, 422, 422, 422, 98, 70, 70, 506, 506, 506, 506, 506, 506, 217, 363, 270, 170, 70, 423, 423, 423, 423, 423, 423, 423, 423, 370, 261, 424, 424, 248, 248, 164, 243, 424, 424, 424, 424, 425, 425, 426, 427, 427, 427, 427, 427, 243, 224, 428, 428, 70, 151, 144, 217, 428, 428, 428, 428, 217, 370, 429, 429, 429, 429, 429, 429, 429, 429, 170, 70, 70, 534, 534, 534, 534, 534, 534, 216, 370, 277, 254, 70, 430, 430, 430, 430, 430, 430, 430, 430, 377, 164, 431, 431, 164, 164, 188, 151, 431, 431, 431, 431, 432, 432, 433, 434, 434, 434, 434, 434, 91, 144, 435, 435, 70, 144, 144, 181, 435, 435, 435, 435, 180, 377, 436, 436, 436, 436, 436, 436, 436, 436, 254, 70, 70, 577, 577, 577, 577, 577, 577, 179, 377, 284, 351, 70, 437, 437, 437, 437, 437, 437, 437, 437, 384, 178, 438, 438, 177, 164, 164, 700, 438, 438, 438, 438, 439, 439, 440, 441, 441, 441, 441, 441, 151, 91, 442, 442, 70, 84, 144, 144, 442, 442, 442, 442, 143, 384, 443, 443, 443, 443, 443, 443, 443, 443, 351, 70, 142, 141, 140, 139, 39, 119, 91, 39, 384, 70, 445, 445, 445, 445, 445, 446, 447, 447, 448, 528, 450, 450, 450, 450, 450, 451, 452, 452, 453, 530, 455, 455, 455, 455, 455, 456, 457, 457, 351, 70, 700, 70, 84, 84, 84, 112, 111, 110, 353, 109, 353, 459, 70, 460, 460, 460, 460, 460, 460, 460, 460, 461, 108, 462, 462, 107, 106, 97, 91, 462, 462, 462, 462, 463, 463, 463, 463, 463, 464, 465, 465, 70, 351, 70, 84, 84, 83, 82, 81, 80, 461, 79, 353, 468, 468, 469, 470, 470, 470, 470, 470, 471, 78, 472, 472, 77, 76, 39, 68, 472, 472, 472, 472, 98, 70, 473, 473, 473, 473, 473, 473, 473, 473, 474, 36, 475, 475, 54, 53, 52, 51, 475, 475, 475, 475, 476, 476, 476, 476, 476, 477, 478, 478, 70, 98, 70, 700, 70, 50, 49, 48, 47, 474, 44, 363, 36, 363, 170, 70, 480, 480, 480, 480, 480, 480, 480, 480, 481, 700, 482, 482, 30, 30, 700, 700, 482, 482, 482, 482, 483, 483, 483, 483, 483, 484, 485, 485, 70, 98, 70, 170, 70, 700, 70, 700, 700, 481, 700, 363, 700, 370, 700, 370, 254, 70, 487, 487, 487, 487, 487, 487, 487, 487, 488, 700, 489, 489, 700, 700, 700, 700, 489, 489, 489, 489, 490, 490, 490, 490, 490, 491, 492, 492, 70, 170, 70, 254, 70, 700, 70, 700, 700, 488, 700, 370, 700, 377, 700, 377, 351, 70, 494, 494, 494, 494, 494, 494, 494, 494, 495, 700, 496, 496, 700, 700, 700, 700, 496, 496, 496, 496, 497, 497, 497, 497, 497, 498, 499, 499, 70, 254, 70, 351, 70, 700, 70, 700, 700, 495, 700, 377, 700, 384, 700, 384, 459, 70, 501, 501, 501, 501, 501, 501, 501, 501, 502, 700, 503, 503, 700, 700, 700, 700, 503, 503, 503, 503, 504, 504, 504, 504, 504, 505, 506, 506, 70, 351, 70, 700, 700, 70, 448, 700, 453, 502, 70, 384, 700, 700, 700, 700, 700, 700, 700, 353, 459, 70, 519, 519, 519, 519, 519, 519, 519, 519, 461, 700, 520, 520, 700, 700, 700, 700, 520, 520, 520, 520, 521, 521, 522, 523, 523, 523, 523, 523, 700, 700, 524, 524, 70, 700, 700, 700, 524, 524, 524, 524, 700, 461, 525, 525, 525, 525, 525, 525, 525, 525, 459, 70, 700, 70, 700, 700, 700, 700, 700, 700, 461, 528, 474, 529, 529, 529, 529, 529, 529, 529, 529, 530, 700, 531, 531, 700, 700, 700, 700, 531, 531, 531, 531, 532, 532, 532, 532, 532, 533, 534, 534, 98, 70, 536, 536, 536, 536, 536, 536, 536, 536, 474, 700, 537, 537, 700, 700, 700, 700, 537, 537, 537, 537, 539, 539, 539, 539, 539, 539, 539, 539, 98, 70, 70, 700, 700, 700, 700, 700, 700, 700, 474, 363, 170, 70, 540, 540, 540, 540, 540, 540, 540, 540, 481, 700, 541, 541, 70, 700, 700, 700, 541, 541, 541, 541, 700, 481, 543, 543, 543, 543, 543, 543, 543, 543, 170, 70, 70, 700, 700, 700, 700, 700, 700, 700, 481, 370, 254, 70, 544, 544, 544, 544, 544, 544, 544, 544, 488, 700, 545, 545, 70, 700, 700, 700, 545, 545, 545, 545, 700, 488, 547, 547, 547, 547, 547, 547, 547, 547, 254, 70, 70, 700, 700, 700, 700, 700, 700, 700, 488, 377, 351, 70, 548, 548, 548, 548, 548, 548, 548, 548, 495, 700, 549, 549, 70, 700, 700, 700, 549, 549, 549, 549, 700, 495, 551, 551, 551, 551, 551, 551, 551, 551, 351, 70, 70, 700, 700, 700, 700, 700, 700, 700, 495, 384, 459, 70, 552, 552, 552, 552, 552, 552, 552, 552, 502, 700, 553, 553, 70, 700, 700, 700, 553, 553, 553, 553, 700, 502, 555, 555, 555, 555, 555, 555, 555, 555, 459, 70, 700, 700, 700, 700, 700, 700, 700, 700, 502, 70, 557, 557, 557, 557, 557, 558, 559, 559, 560, 700, 562, 562, 562, 562, 562, 563, 564, 564, 565, 530, 567, 567, 567, 567, 567, 568, 569, 569, 459, 70, 700, 70, 700, 700, 700, 700, 700, 700, 461, 700, 461, 571, 70, 572, 572, 572, 572, 572, 572, 572, 572, 573, 700, 574, 574, 700, 700, 700, 700, 574, 574, 574, 574, 575, 575, 575, 575, 575, 576, 577, 577, 70, 459, 70, 98, 70, 700, 700, 700, 700, 573, 700, 461, 528, 474, 582, 582, 582, 582, 582, 582, 582, 582, 530, 700, 583, 583, 700, 700, 700, 700, 583, 583, 583, 583, 586, 586, 586, 586, 586, 586, 586, 586, 700, 70, 98, 70, 170, 70, 700, 70, 170, 70, 474, 700, 474, 700, 481, 700, 481, 700, 481, 254, 70, 700, 70, 254, 70, 351, 70, 700, 70, 488, 700, 488, 700, 488, 700, 495, 700, 495, 351, 70, 459, 70, 700, 70, 459, 70, 70, 700, 495, 70, 502, 560, 502, 565, 502, 461, 700, 700, 700, 700, 700, 700, 700, 571, 70, 610, 610, 610, 610, 610, 610, 610, 610, 573, 700, 611, 611, 70, 700, 700, 700, 611, 611, 611, 611, 700, 573, 613, 613, 613, 613, 613, 613, 613, 613, 571, 70, 528, 700, 700, 700, 700, 700, 700, 700, 573, 615, 530, 617, 617, 617, 617, 617, 618, 619, 619, 70, 70, 70, 70, 70, 700, 700, 700, 700, 474, 481, 488, 495, 502, 70, 629, 629, 629, 629, 629, 630, 631, 631, 632, 700, 634, 634, 634, 634, 634, 635, 636, 636, 637, 700, 639, 639, 639, 639, 639, 640, 641, 641, 571, 70, 700, 70, 571, 70, 700, 615, 700, 700, 573, 70, 573, 632, 573, 700, 700, 637, 700, 700, 70, 700, 700, 700, 700, 700, 700, 700, 700, 573, 666, 700, 668, 668, 668, 668, 668, 669, 670, 670, 70, 672, 672, 672, 672, 672, 673, 674, 674, 675, 700, 677, 677, 677, 677, 677, 678, 679, 679, 666, 700, 700, 70, 675, 700, 700, 700, 700, 700, 700, 700, 700, 700, 70, 691, 691, 691, 691, 691, 692, 693, 693, 70, 695, 695, 695, 695, 695, 696, 697, 697, 70, 700, 70, 700, 700, 700, 700, 700, 700, 700, 700, 29, 29, 29, 29, 29, 29, 29, 29, 31, 31, 31, 31, 31, 31, 31, 31, 55, 700, 55, 55, 55, 55, 55, 55, 56, 56, 56, 56, 56, 56, 700, 56, 67, 67, 700, 67, 66, 66, 700, 66, 85, 700, 85, 88, 700, 88, 89, 700, 89, 89, 700, 89, 90, 90, 700, 90, 101, 101, 101, 700, 101, 116, 700, 116, 117, 117, 700, 117, 118, 118, 700, 118, 122, 122, 122, 700, 122, 133, 133, 133, 700, 133, 145, 700, 145, 148, 700, 148, 149, 149, 700, 149, 150, 150, 700, 150, 157, 157, 157, 700, 157, 165, 700, 165, 168, 700, 168, 169, 169, 169, 169, 700, 169, 173, 173, 173, 700, 173, 185, 700, 185, 186, 186, 700, 186, 187, 187, 700, 187, 191, 191, 191, 700, 191, 195, 195, 195, 195, 700, 195, 198, 198, 198, 700, 198, 205, 700, 205, 210, 210, 210, 700, 210, 218, 218, 221, 221, 222, 222, 700, 222, 223, 223, 700, 223, 230, 230, 230, 700, 230, 237, 237, 237, 700, 237, 244, 700, 244, 247, 700, 247, 249, 700, 249, 252, 700, 252, 253, 253, 253, 253, 700, 253, 257, 257, 257, 700, 257, 265, 265, 266, 266, 700, 266, 267, 267, 700, 267, 271, 271, 271, 700, 271, 275, 275, 275, 275, 700, 275, 278, 278, 278, 700, 278, 282, 282, 282, 282, 700, 282, 285, 285, 285, 700, 285, 292, 700, 292, 296, 700, 296, 301, 301, 301, 700, 301, 308, 308, 700, 308, 309, 309, 700, 309, 316, 316, 316, 700, 316, 323, 323, 323, 700, 323, 330, 330, 330, 700, 330, 336, 336, 339, 339, 341, 700, 341, 344, 700, 344, 346, 700, 346, 349, 700, 349, 350, 350, 350, 350, 700, 350, 354, 354, 354, 700, 354, 359, 359, 700, 359, 360, 360, 700, 360, 364, 364, 364, 700, 364, 368, 368, 368, 368, 700, 368, 371, 371, 371, 700, 371, 375, 375, 375, 375, 700, 375, 378, 378, 378, 700, 378, 382, 382, 382, 382, 700, 382, 385, 385, 385, 700, 385, 389, 389, 393, 700, 393, 397, 700, 397, 402, 402, 402, 700, 402, 409, 409, 700, 409, 410, 410, 700, 410, 417, 417, 417, 700, 417, 424, 424, 424, 700, 424, 431, 431, 431, 700, 431, 438, 438, 438, 700, 438, 444, 444, 447, 447, 449, 700, 449, 452, 700, 452, 454, 700, 454, 457, 700, 457, 458, 458, 458, 458, 700, 458, 462, 462, 462, 700, 462, 467, 467, 700, 467, 475, 475, 475, 700, 475, 479, 479, 479, 479, 700, 479, 482, 482, 482, 700, 482, 486, 486, 486, 486, 700, 486, 489, 489, 489, 700, 489, 493, 493, 493, 493, 700, 493, 496, 496, 496, 700, 496, 500, 500, 500, 500, 700, 500, 503, 503, 503, 700, 503, 507, 507, 511, 700, 511, 515, 700, 515, 520, 520, 520, 700, 520, 527, 527, 700, 527, 535, 535, 700, 700, 535, 531, 531, 700, 531, 538, 700, 700, 538, 537, 537, 537, 700, 537, 542, 700, 700, 542, 541, 541, 541, 700, 541, 546, 700, 700, 546, 545, 545, 545, 700, 545, 550, 700, 700, 550, 549, 549, 549, 700, 549, 554, 700, 700, 554, 553, 553, 553, 700, 553, 556, 556, 559, 559, 561, 700, 561, 564, 700, 564, 566, 700, 566, 569, 700, 569, 570, 570, 570, 570, 700, 570, 574, 574, 574, 700, 574, 584, 584, 700, 584, 583, 583, 700, 583, 587, 587, 700, 700, 587, 588, 588, 588, 588, 700, 588, 589, 589, 700, 700, 589, 590, 590, 590, 590, 700, 590, 591, 591, 700, 700, 591, 592, 592, 592, 592, 700, 592, 593, 593, 700, 700, 593, 594, 594, 594, 594, 700, 594, 595, 595, 700, 700, 595, 596, 596, 596, 596, 700, 596, 597, 597, 700, 700, 597, 598, 598, 602, 700, 602, 606, 700, 606, 612, 700, 700, 612, 611, 611, 611, 700, 611, 616, 700, 616, 619, 700, 619, 620, 700, 620, 620, 700, 620, 621, 621, 700, 700, 621, 622, 622, 700, 700, 622, 623, 623, 700, 700, 623, 624, 624, 700, 700, 624, 625, 625, 700, 700, 625, 626, 626, 700, 700, 626, 627, 627, 700, 700, 627, 628, 628, 631, 631, 633, 700, 633, 636, 700, 636, 638, 700, 638, 641, 700, 641, 642, 642, 642, 642, 700, 642, 643, 643, 700, 700, 643, 648, 700, 648, 649, 649, 700, 700, 649, 650, 650, 700, 700, 650, 651, 651, 700, 700, 651, 652, 652, 700, 700, 652, 653, 653, 700, 700, 653, 654, 654, 700, 700, 654, 655, 655, 700, 700, 655, 656, 656, 660, 700, 660, 664, 700, 664, 665, 665, 700, 700, 665, 667, 700, 667, 670, 700, 670, 585, 585, 700, 700, 585, 671, 671, 674, 674, 676, 700, 676, 679, 700, 679, 680, 680, 700, 700, 680, 684, 700, 684, 685, 685, 689, 700, 689, 690, 690, 693, 693, 694, 694, 697, 697, 698, 698, 699, 699, 7, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700 } ; static yyconst flex_int16_t yy_chk[3898] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 5, 6, 6, 14, 14, 14, 14, 14, 14, 34, 34, 35, 35, 916, 5, 43, 6, 32, 32, 37, 37, 37, 42, 42, 65, 43, 58, 58, 59, 59, 60, 60, 61, 61, 65, 103, 103, 5, 5, 6, 6, 12, 66, 12, 12, 12, 12, 12, 12, 12, 12, 12, 66, 12, 12, 32, 32, 124, 124, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 17, 17, 17, 17, 17, 17, 17, 17, 17, 913, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 902, 18, 18, 84, 84, 84, 900, 18, 18, 18, 18, 38, 69, 38, 38, 38, 38, 38, 38, 38, 38, 38, 69, 38, 38, 97, 97, 97, 890, 38, 38, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 44, 44, 44, 44, 44, 44, 44, 44, 44, 57, 57, 44, 44, 98, 98, 98, 869, 44, 44, 44, 44, 45, 45, 45, 45, 45, 45, 45, 45, 175, 175, 45, 45, 144, 144, 144, 867, 45, 45, 45, 45, 73, 73, 73, 73, 73, 73, 57, 57, 63, 865, 63, 63, 63, 63, 63, 63, 63, 63, 68, 68, 68, 68, 68, 68, 68, 68, 68, 193, 193, 68, 68, 164, 164, 164, 850, 68, 68, 68, 68, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 827, 71, 71, 170, 170, 170, 825, 71, 71, 71, 71, 72, 72, 72, 72, 72, 72, 72, 72, 74, 94, 94, 94, 94, 94, 94, 200, 200, 74, 75, 75, 75, 75, 75, 75, 75, 75, 259, 259, 75, 75, 87, 243, 243, 243, 75, 75, 75, 75, 87, 87, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 823, 92, 92, 248, 248, 248, 796, 92, 92, 92, 92, 93, 93, 93, 93, 93, 93, 93, 93, 95, 136, 136, 136, 136, 136, 136, 273, 273, 95, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 794, 99, 99, 254, 254, 254, 792, 99, 99, 99, 99, 100, 100, 100, 100, 100, 100, 100, 100, 280, 280, 100, 100, 101, 263, 263, 263, 100, 100, 100, 100, 767, 101, 102, 102, 102, 102, 102, 102, 102, 102, 104, 104, 765, 122, 154, 154, 154, 154, 154, 154, 104, 114, 122, 114, 114, 114, 114, 114, 114, 114, 114, 119, 119, 119, 119, 119, 119, 119, 119, 119, 287, 287, 119, 119, 262, 262, 262, 262, 119, 119, 119, 119, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 763, 120, 120, 340, 340, 340, 741, 120, 120, 120, 120, 121, 121, 121, 121, 121, 121, 121, 121, 356, 356, 121, 121, 345, 345, 345, 739, 121, 121, 121, 121, 123, 123, 123, 123, 123, 123, 123, 123, 125, 125, 132, 132, 160, 160, 160, 160, 160, 160, 125, 130, 132, 130, 130, 130, 130, 130, 130, 130, 130, 133, 133, 213, 213, 213, 213, 213, 213, 366, 366, 133, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 732, 134, 134, 351, 351, 351, 727, 134, 134, 134, 134, 135, 135, 135, 135, 135, 135, 135, 135, 137, 138, 138, 720, 147, 373, 373, 380, 380, 137, 719, 138, 147, 147, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 714, 152, 152, 387, 387, 464, 464, 152, 152, 152, 152, 153, 153, 153, 153, 153, 153, 153, 153, 155, 156, 156, 157, 157, 448, 448, 448, 712, 155, 705, 156, 699, 157, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 698, 158, 158, 453, 453, 453, 697, 158, 158, 158, 158, 159, 159, 159, 159, 159, 159, 159, 159, 161, 162, 162, 695, 167, 169, 459, 459, 459, 161, 694, 162, 167, 167, 169, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 693, 171, 171, 477, 477, 484, 484, 171, 171, 171, 171, 172, 172, 172, 172, 172, 172, 172, 172, 491, 491, 172, 172, 173, 498, 498, 691, 172, 172, 172, 172, 690, 173, 174, 174, 174, 174, 174, 174, 174, 174, 176, 176, 227, 227, 227, 227, 227, 227, 505, 505, 176, 183, 183, 183, 183, 183, 183, 183, 183, 183, 188, 188, 188, 188, 188, 188, 188, 188, 188, 533, 533, 188, 188, 528, 528, 528, 689, 188, 188, 188, 188, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 688, 189, 189, 560, 560, 560, 686, 189, 189, 189, 189, 190, 190, 190, 190, 190, 190, 190, 190, 576, 576, 190, 190, 191, 565, 565, 565, 190, 190, 190, 190, 685, 191, 192, 192, 192, 192, 192, 192, 192, 192, 194, 194, 195, 233, 233, 233, 233, 233, 233, 684, 194, 195, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 683, 196, 196, 571, 571, 571, 681, 196, 196, 196, 196, 197, 197, 197, 197, 197, 197, 197, 197, 680, 679, 197, 197, 198, 615, 615, 615, 197, 197, 197, 197, 677, 198, 199, 199, 199, 199, 199, 199, 199, 199, 201, 201, 209, 209, 240, 240, 240, 240, 240, 240, 201, 203, 209, 203, 203, 203, 203, 203, 203, 203, 203, 207, 676, 207, 207, 207, 207, 207, 207, 207, 207, 210, 210, 304, 304, 304, 304, 304, 304, 674, 672, 210, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 671, 211, 211, 632, 632, 632, 670, 211, 211, 211, 211, 212, 212, 212, 212, 212, 212, 212, 212, 214, 215, 215, 313, 313, 313, 313, 313, 313, 214, 668, 215, 217, 217, 217, 217, 217, 217, 217, 217, 220, 319, 319, 319, 319, 319, 319, 220, 220, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 667, 225, 225, 637, 637, 637, 665, 225, 225, 225, 225, 226, 226, 226, 226, 226, 226, 226, 226, 228, 229, 229, 230, 230, 666, 666, 666, 664, 228, 663, 229, 661, 230, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 660, 231, 231, 675, 675, 675, 659, 231, 231, 231, 231, 232, 232, 232, 232, 232, 232, 232, 232, 234, 235, 235, 236, 236, 237, 237, 657, 656, 234, 655, 235, 654, 236, 653, 237, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 652, 238, 238, 651, 650, 649, 648, 238, 238, 238, 238, 239, 239, 239, 239, 239, 239, 239, 239, 241, 242, 242, 647, 246, 645, 251, 253, 643, 241, 641, 242, 246, 246, 251, 251, 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 639, 255, 255, 638, 636, 634, 633, 255, 255, 255, 255, 256, 256, 256, 256, 256, 256, 256, 256, 631, 629, 256, 256, 257, 628, 627, 626, 256, 256, 256, 256, 625, 257, 258, 258, 258, 258, 258, 258, 258, 258, 260, 260, 326, 326, 326, 326, 326, 326, 624, 623, 260, 268, 268, 268, 268, 268, 268, 268, 268, 268, 622, 621, 268, 268, 620, 619, 617, 616, 268, 268, 268, 268, 269, 269, 269, 269, 269, 269, 269, 269, 269, 269, 269, 614, 269, 269, 612, 609, 607, 606, 269, 269, 269, 269, 270, 270, 270, 270, 270, 270, 270, 270, 605, 603, 270, 270, 271, 602, 601, 599, 270, 270, 270, 270, 598, 271, 272, 272, 272, 272, 272, 272, 272, 272, 274, 274, 275, 333, 333, 333, 333, 333, 333, 597, 274, 275, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 595, 276, 276, 593, 591, 589, 587, 276, 276, 276, 276, 277, 277, 277, 277, 277, 277, 277, 277, 585, 584, 277, 277, 278, 581, 579, 578, 277, 277, 277, 277, 569, 278, 279, 279, 279, 279, 279, 279, 279, 279, 281, 281, 282, 405, 405, 405, 405, 405, 405, 567, 281, 282, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 566, 283, 283, 564, 562, 561, 559, 283, 283, 283, 283, 284, 284, 284, 284, 284, 284, 284, 284, 557, 556, 284, 284, 285, 554, 550, 546, 284, 284, 284, 284, 542, 285, 286, 286, 286, 286, 286, 286, 286, 286, 288, 288, 414, 414, 414, 414, 414, 414, 538, 535, 288, 290, 290, 290, 290, 290, 290, 290, 290, 290, 294, 534, 294, 294, 294, 294, 294, 294, 294, 294, 298, 534, 298, 298, 298, 298, 298, 298, 298, 298, 300, 300, 301, 301, 420, 420, 420, 420, 420, 420, 300, 531, 301, 302, 302, 302, 302, 302, 302, 302, 302, 302, 302, 302, 530, 302, 302, 527, 526, 518, 516, 302, 302, 302, 302, 303, 303, 303, 303, 303, 303, 303, 303, 305, 306, 306, 427, 427, 427, 427, 427, 427, 305, 515, 306, 311, 311, 311, 311, 311, 311, 311, 311, 311, 311, 311, 514, 311, 311, 512, 511, 510, 508, 311, 311, 311, 311, 312, 312, 312, 312, 312, 312, 312, 312, 314, 315, 315, 316, 316, 507, 472, 471, 467, 314, 466, 315, 457, 316, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 455, 317, 317, 454, 452, 450, 449, 317, 317, 317, 317, 318, 318, 318, 318, 318, 318, 318, 318, 320, 321, 321, 322, 322, 323, 323, 447, 445, 320, 444, 321, 410, 322, 409, 323, 324, 324, 324, 324, 324, 324, 324, 324, 324, 324, 324, 408, 324, 324, 400, 398, 397, 396, 324, 324, 324, 324, 325, 325, 325, 325, 325, 325, 325, 325, 327, 328, 328, 329, 329, 330, 330, 394, 393, 327, 392, 328, 390, 329, 389, 330, 331, 331, 331, 331, 331, 331, 331, 331, 331, 331, 331, 360, 331, 331, 359, 358, 349, 347, 331, 331, 331, 331, 332, 332, 332, 332, 332, 332, 332, 332, 334, 335, 335, 346, 344, 338, 343, 342, 348, 334, 350, 335, 338, 338, 343, 343, 348, 348, 341, 350, 352, 352, 352, 352, 352, 352, 352, 352, 352, 352, 352, 339, 352, 352, 337, 336, 310, 309, 352, 352, 352, 352, 353, 353, 353, 353, 353, 353, 353, 353, 308, 307, 353, 353, 354, 299, 297, 296, 353, 353, 353, 353, 295, 354, 355, 355, 355, 355, 355, 355, 355, 355, 357, 357, 434, 434, 434, 434, 434, 434, 293, 292, 357, 361, 361, 361, 361, 361, 361, 361, 361, 361, 291, 289, 361, 361, 267, 266, 265, 261, 361, 361, 361, 361, 362, 362, 362, 362, 362, 362, 362, 362, 362, 362, 362, 252, 362, 362, 250, 249, 247, 245, 362, 362, 362, 362, 363, 363, 363, 363, 363, 363, 363, 363, 244, 224, 363, 363, 364, 223, 222, 221, 363, 363, 363, 363, 219, 364, 365, 365, 365, 365, 365, 365, 365, 365, 367, 367, 368, 441, 441, 441, 441, 441, 441, 218, 367, 368, 369, 369, 369, 369, 369, 369, 369, 369, 369, 369, 369, 216, 369, 369, 208, 206, 205, 204, 369, 369, 369, 369, 370, 370, 370, 370, 370, 370, 370, 370, 202, 187, 370, 370, 371, 186, 185, 184, 370, 370, 370, 370, 182, 371, 372, 372, 372, 372, 372, 372, 372, 372, 374, 374, 375, 470, 470, 470, 470, 470, 470, 180, 374, 375, 376, 376, 376, 376, 376, 376, 376, 376, 376, 376, 376, 168, 376, 376, 166, 165, 151, 150, 376, 376, 376, 376, 377, 377, 377, 377, 377, 377, 377, 377, 149, 148, 377, 377, 378, 146, 145, 143, 377, 377, 377, 377, 142, 378, 379, 379, 379, 379, 379, 379, 379, 379, 381, 381, 382, 523, 523, 523, 523, 523, 523, 141, 381, 382, 383, 383, 383, 383, 383, 383, 383, 383, 383, 383, 383, 140, 383, 383, 139, 131, 129, 128, 383, 383, 383, 383, 384, 384, 384, 384, 384, 384, 384, 384, 118, 117, 384, 384, 385, 116, 115, 113, 384, 384, 384, 384, 111, 385, 386, 386, 386, 386, 386, 386, 386, 386, 388, 388, 110, 109, 108, 106, 105, 91, 90, 89, 388, 391, 391, 391, 391, 391, 391, 391, 391, 391, 395, 582, 395, 395, 395, 395, 395, 395, 395, 395, 399, 582, 399, 399, 399, 399, 399, 399, 399, 399, 401, 401, 402, 402, 88, 86, 85, 83, 82, 81, 401, 80, 402, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 79, 403, 403, 78, 77, 70, 67, 403, 403, 403, 403, 404, 404, 404, 404, 404, 404, 404, 404, 406, 407, 407, 64, 62, 54, 53, 52, 51, 406, 50, 407, 411, 411, 411, 411, 411, 411, 411, 411, 411, 49, 411, 411, 48, 47, 40, 39, 411, 411, 411, 411, 412, 412, 412, 412, 412, 412, 412, 412, 412, 412, 412, 36, 412, 412, 26, 25, 24, 23, 412, 412, 412, 412, 413, 413, 413, 413, 413, 413, 413, 413, 415, 416, 416, 417, 417, 22, 21, 20, 19, 415, 15, 416, 9, 417, 418, 418, 418, 418, 418, 418, 418, 418, 418, 418, 418, 7, 418, 418, 4, 3, 0, 0, 418, 418, 418, 418, 419, 419, 419, 419, 419, 419, 419, 419, 421, 422, 422, 423, 423, 424, 424, 0, 0, 421, 0, 422, 0, 423, 0, 424, 425, 425, 425, 425, 425, 425, 425, 425, 425, 425, 425, 0, 425, 425, 0, 0, 0, 0, 425, 425, 425, 425, 426, 426, 426, 426, 426, 426, 426, 426, 428, 429, 429, 430, 430, 431, 431, 0, 0, 428, 0, 429, 0, 430, 0, 431, 432, 432, 432, 432, 432, 432, 432, 432, 432, 432, 432, 0, 432, 432, 0, 0, 0, 0, 432, 432, 432, 432, 433, 433, 433, 433, 433, 433, 433, 433, 435, 436, 436, 437, 437, 438, 438, 0, 0, 435, 0, 436, 0, 437, 0, 438, 439, 439, 439, 439, 439, 439, 439, 439, 439, 439, 439, 0, 439, 439, 0, 0, 0, 0, 439, 439, 439, 439, 440, 440, 440, 440, 440, 440, 440, 440, 442, 443, 443, 0, 0, 446, 451, 0, 456, 442, 458, 443, 446, 446, 451, 451, 456, 456, 0, 458, 460, 460, 460, 460, 460, 460, 460, 460, 460, 460, 460, 0, 460, 460, 0, 0, 0, 0, 460, 460, 460, 460, 461, 461, 461, 461, 461, 461, 461, 461, 0, 0, 461, 461, 462, 0, 0, 0, 461, 461, 461, 461, 0, 462, 463, 463, 463, 463, 463, 463, 463, 463, 465, 465, 0, 475, 0, 0, 0, 0, 0, 0, 465, 468, 475, 468, 468, 468, 468, 468, 468, 468, 468, 468, 0, 468, 468, 0, 0, 0, 0, 468, 468, 468, 468, 469, 469, 469, 469, 469, 469, 469, 469, 473, 473, 473, 473, 473, 473, 473, 473, 473, 473, 473, 0, 473, 473, 0, 0, 0, 0, 473, 473, 473, 473, 476, 476, 476, 476, 476, 476, 476, 476, 478, 478, 479, 0, 0, 0, 0, 0, 0, 0, 478, 479, 480, 480, 480, 480, 480, 480, 480, 480, 480, 480, 480, 0, 480, 480, 482, 0, 0, 0, 480, 480, 480, 480, 0, 482, 483, 483, 483, 483, 483, 483, 483, 483, 485, 485, 486, 0, 0, 0, 0, 0, 0, 0, 485, 486, 487, 487, 487, 487, 487, 487, 487, 487, 487, 487, 487, 0, 487, 487, 489, 0, 0, 0, 487, 487, 487, 487, 0, 489, 490, 490, 490, 490, 490, 490, 490, 490, 492, 492, 493, 0, 0, 0, 0, 0, 0, 0, 492, 493, 494, 494, 494, 494, 494, 494, 494, 494, 494, 494, 494, 0, 494, 494, 496, 0, 0, 0, 494, 494, 494, 494, 0, 496, 497, 497, 497, 497, 497, 497, 497, 497, 499, 499, 500, 0, 0, 0, 0, 0, 0, 0, 499, 500, 501, 501, 501, 501, 501, 501, 501, 501, 501, 501, 501, 0, 501, 501, 503, 0, 0, 0, 501, 501, 501, 501, 0, 503, 504, 504, 504, 504, 504, 504, 504, 504, 506, 506, 0, 0, 0, 0, 0, 0, 0, 0, 506, 509, 509, 509, 509, 509, 509, 509, 509, 509, 513, 583, 513, 513, 513, 513, 513, 513, 513, 513, 517, 583, 517, 517, 517, 517, 517, 517, 517, 517, 519, 519, 520, 520, 0, 0, 0, 0, 0, 0, 519, 0, 520, 521, 521, 521, 521, 521, 521, 521, 521, 521, 521, 521, 0, 521, 521, 0, 0, 0, 0, 521, 521, 521, 521, 522, 522, 522, 522, 522, 522, 522, 522, 524, 525, 525, 536, 536, 0, 0, 0, 0, 524, 0, 525, 529, 536, 529, 529, 529, 529, 529, 529, 529, 529, 529, 0, 529, 529, 0, 0, 0, 0, 529, 529, 529, 529, 532, 532, 532, 532, 532, 532, 532, 532, 537, 537, 539, 539, 540, 540, 541, 541, 543, 543, 537, 0, 539, 0, 540, 0, 541, 0, 543, 544, 544, 545, 545, 547, 547, 548, 548, 549, 549, 544, 0, 545, 0, 547, 0, 548, 0, 549, 551, 551, 552, 552, 553, 553, 555, 555, 570, 0, 551, 558, 552, 563, 553, 568, 555, 570, 558, 558, 0, 563, 563, 568, 568, 572, 572, 572, 572, 572, 572, 572, 572, 572, 572, 572, 0, 572, 572, 574, 0, 0, 0, 572, 572, 572, 572, 0, 574, 575, 575, 575, 575, 575, 575, 575, 575, 577, 577, 586, 0, 0, 0, 0, 0, 0, 0, 577, 580, 586, 580, 580, 580, 580, 580, 580, 580, 580, 588, 590, 592, 594, 596, 0, 0, 0, 0, 588, 590, 592, 594, 596, 600, 600, 600, 600, 600, 600, 600, 600, 600, 604, 0, 604, 604, 604, 604, 604, 604, 604, 604, 608, 0, 608, 608, 608, 608, 608, 608, 608, 608, 610, 610, 611, 611, 613, 613, 0, 618, 0, 0, 610, 630, 611, 635, 613, 618, 618, 640, 630, 630, 642, 635, 635, 0, 0, 640, 640, 0, 0, 642, 646, 0, 646, 646, 646, 646, 646, 646, 646, 646, 658, 658, 658, 658, 658, 658, 658, 658, 658, 662, 0, 662, 662, 662, 662, 662, 662, 662, 662, 669, 0, 0, 673, 678, 0, 0, 0, 669, 669, 673, 673, 678, 678, 682, 682, 682, 682, 682, 682, 682, 682, 682, 687, 687, 687, 687, 687, 687, 687, 687, 687, 692, 0, 696, 0, 0, 0, 0, 692, 692, 696, 696, 701, 701, 701, 701, 701, 701, 701, 701, 702, 702, 702, 702, 702, 702, 702, 702, 703, 0, 703, 703, 703, 703, 703, 703, 704, 704, 704, 704, 704, 704, 0, 704, 706, 706, 0, 706, 707, 707, 0, 707, 708, 0, 708, 709, 0, 709, 710, 0, 710, 710, 0, 710, 711, 711, 0, 711, 713, 713, 713, 0, 713, 715, 0, 715, 716, 716, 0, 716, 717, 717, 0, 717, 718, 718, 718, 0, 718, 721, 721, 721, 0, 721, 722, 0, 722, 723, 0, 723, 724, 724, 0, 724, 725, 725, 0, 725, 726, 726, 726, 0, 726, 728, 0, 728, 729, 0, 729, 730, 730, 730, 730, 0, 730, 731, 731, 731, 0, 731, 733, 0, 733, 734, 734, 0, 734, 735, 735, 0, 735, 736, 736, 736, 0, 736, 737, 737, 737, 737, 0, 737, 738, 738, 738, 0, 738, 740, 0, 740, 742, 742, 742, 0, 742, 743, 743, 744, 744, 745, 745, 0, 745, 746, 746, 0, 746, 747, 747, 747, 0, 747, 748, 748, 748, 0, 748, 749, 0, 749, 750, 0, 750, 751, 0, 751, 752, 0, 752, 753, 753, 753, 753, 0, 753, 754, 754, 754, 0, 754, 755, 755, 756, 756, 0, 756, 757, 757, 0, 757, 758, 758, 758, 0, 758, 759, 759, 759, 759, 0, 759, 760, 760, 760, 0, 760, 761, 761, 761, 761, 0, 761, 762, 762, 762, 0, 762, 764, 0, 764, 766, 0, 766, 768, 768, 768, 0, 768, 769, 769, 0, 769, 770, 770, 0, 770, 771, 771, 771, 0, 771, 772, 772, 772, 0, 772, 773, 773, 773, 0, 773, 774, 774, 775, 775, 776, 0, 776, 777, 0, 777, 778, 0, 778, 779, 0, 779, 780, 780, 780, 780, 0, 780, 781, 781, 781, 0, 781, 782, 782, 0, 782, 783, 783, 0, 783, 784, 784, 784, 0, 784, 785, 785, 785, 785, 0, 785, 786, 786, 786, 0, 786, 787, 787, 787, 787, 0, 787, 788, 788, 788, 0, 788, 789, 789, 789, 789, 0, 789, 790, 790, 790, 0, 790, 791, 791, 793, 0, 793, 795, 0, 795, 797, 797, 797, 0, 797, 798, 798, 0, 798, 799, 799, 0, 799, 800, 800, 800, 0, 800, 801, 801, 801, 0, 801, 802, 802, 802, 0, 802, 803, 803, 803, 0, 803, 804, 804, 805, 805, 806, 0, 806, 807, 0, 807, 808, 0, 808, 809, 0, 809, 810, 810, 810, 810, 0, 810, 811, 811, 811, 0, 811, 812, 812, 0, 812, 813, 813, 813, 0, 813, 814, 814, 814, 814, 0, 814, 815, 815, 815, 0, 815, 816, 816, 816, 816, 0, 816, 817, 817, 817, 0, 817, 818, 818, 818, 818, 0, 818, 819, 819, 819, 0, 819, 820, 820, 820, 820, 0, 820, 821, 821, 821, 0, 821, 822, 822, 824, 0, 824, 826, 0, 826, 828, 828, 828, 0, 828, 829, 829, 0, 829, 830, 830, 0, 0, 830, 831, 831, 0, 831, 832, 0, 0, 832, 833, 833, 833, 0, 833, 834, 0, 0, 834, 835, 835, 835, 0, 835, 836, 0, 0, 836, 837, 837, 837, 0, 837, 838, 0, 0, 838, 839, 839, 839, 0, 839, 840, 0, 0, 840, 841, 841, 841, 0, 841, 842, 842, 843, 843, 844, 0, 844, 845, 0, 845, 846, 0, 846, 847, 0, 847, 848, 848, 848, 848, 0, 848, 849, 849, 849, 0, 849, 851, 851, 0, 851, 852, 852, 0, 852, 853, 853, 0, 0, 853, 854, 854, 854, 854, 0, 854, 855, 855, 0, 0, 855, 856, 856, 856, 856, 0, 856, 857, 857, 0, 0, 857, 858, 858, 858, 858, 0, 858, 859, 859, 0, 0, 859, 860, 860, 860, 860, 0, 860, 861, 861, 0, 0, 861, 862, 862, 862, 862, 0, 862, 863, 863, 0, 0, 863, 864, 864, 866, 0, 866, 868, 0, 868, 870, 0, 0, 870, 871, 871, 871, 0, 871, 872, 0, 872, 873, 0, 873, 874, 0, 874, 874, 0, 874, 875, 875, 0, 0, 875, 876, 876, 0, 0, 876, 877, 877, 0, 0, 877, 878, 878, 0, 0, 878, 879, 879, 0, 0, 879, 880, 880, 0, 0, 880, 881, 881, 0, 0, 881, 882, 882, 883, 883, 884, 0, 884, 885, 0, 885, 886, 0, 886, 887, 0, 887, 888, 888, 888, 888, 0, 888, 889, 889, 0, 0, 889, 891, 0, 891, 892, 892, 0, 0, 892, 893, 893, 0, 0, 893, 894, 894, 0, 0, 894, 895, 895, 0, 0, 895, 896, 896, 0, 0, 896, 897, 897, 0, 0, 897, 898, 898, 0, 0, 898, 899, 899, 901, 0, 901, 903, 0, 903, 904, 904, 0, 0, 904, 905, 0, 905, 906, 0, 906, 907, 907, 0, 0, 907, 908, 908, 909, 909, 910, 0, 910, 911, 0, 911, 912, 912, 0, 0, 912, 914, 0, 914, 915, 915, 917, 0, 917, 918, 918, 919, 919, 920, 920, 921, 921, 922, 922, 923, 923, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700 } ; static yy_state_type yy_last_accepting_state; static char *yy_last_accepting_cpos; extern int yy_compile_policy_flex_debug; int yy_compile_policy_flex_debug = 0; /* The intent behind this definition is that it'll catch * any uses of REJECT which flex missed. */ #define REJECT reject_used_but_not_detected #define yymore() yymore_used_but_not_detected #define YY_MORE_ADJ 0 #define YY_RESTORE_YY_MORE_OFFSET char *yy_compile_policytext; #line 1 "compilepolicy.l" #line 2 "compilepolicy.l" // XXX: this whole parsing is becoming a mess... Once we finalize how to test // policy and what it should look like, it needs a re-write. #include "libxorp/xorp.h" #include "policy/test/compilepolicy.hh" #include "yacc.yy_compile_policy.cc.h" #include "policy/common/policy_utils.hh" #define yylval yy_compile_policylval #define yyparse yy_compile_policyparse #define yyerror yy_compile_policyerror void yyerror(const char *); int yyparse(void); namespace { unsigned _yy_lineno; string _yy_last_err; } #line 1619 "lex.yy_compile_policy.cc" #define INITIAL 0 #define STR 1 #define BLOCK 2 #ifndef YY_NO_UNISTD_H /* Special case for "unistd.h", since it is non-ANSI. We include it way * down here because we want the user's section 1 to have been scanned first. * The user has a chance to override it with an option. */ #include #endif #ifndef YY_EXTRA_TYPE #define YY_EXTRA_TYPE void * #endif static int yy_init_globals (void ); /* Macros after this point can all be overridden by user definitions in * section 1. */ #ifndef YY_SKIP_YYWRAP #ifdef __cplusplus extern "C" int yy_compile_policywrap (void ); #else extern int yy_compile_policywrap (void ); #endif #endif #ifndef yytext_ptr static void yy_flex_strncpy (char *,yyconst char *,int ); #endif #ifdef YY_NEED_STRLEN static int yy_flex_strlen (yyconst char * ); #endif #ifndef YY_NO_INPUT #ifdef __cplusplus static int yyinput (void ); #else static int input (void ); #endif #endif /* Amount of stuff to slurp up with each read. */ #ifndef YY_READ_BUF_SIZE #define YY_READ_BUF_SIZE 8192 #endif /* Copy whatever the last rule matched to the standard output. */ #ifndef ECHO /* This used to be an fputs(), but since the string might contain NUL's, * we now use fwrite(). */ #define ECHO (void) fwrite( yy_compile_policytext, yy_compile_policyleng, 1, yy_compile_policyout ) #endif /* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, * is returned in "result". */ #ifndef YY_INPUT #define YY_INPUT(buf,result,max_size) \ if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ { \ int c = '*'; \ int n; \ for ( n = 0; n < max_size && \ (c = getc( yy_compile_policyin )) != EOF && c != '\n'; ++n ) \ buf[n] = (char) c; \ if ( c == '\n' ) \ buf[n++] = (char) c; \ if ( c == EOF && ferror( yy_compile_policyin ) ) \ YY_FATAL_ERROR( "input in flex scanner failed" ); \ result = n; \ } \ else \ { \ errno=0; \ while ( (result = fread(buf, 1, max_size, yy_compile_policyin))==0 && ferror(yy_compile_policyin)) \ { \ if( errno != EINTR) \ { \ YY_FATAL_ERROR( "input in flex scanner failed" ); \ break; \ } \ errno=0; \ clearerr(yy_compile_policyin); \ } \ }\ \ #endif /* No semi-colon after return; correct usage is to write "yyterminate();" - * we don't want an extra ';' after the "return" because that will cause * some compilers to complain about unreachable statements. */ #ifndef yyterminate #define yyterminate() return YY_NULL #endif /* Number of entries by which start-condition stack grows. */ #ifndef YY_START_STACK_INCR #define YY_START_STACK_INCR 25 #endif /* Report a fatal error. */ #ifndef YY_FATAL_ERROR #define YY_FATAL_ERROR(msg) yy_fatal_error( msg ) #endif /* end tables serialization structures and prototypes */ /* Default declaration of generated scanner - a define so the user can * easily add parameters. */ #ifndef YY_DECL #define YY_DECL_IS_OURS 1 extern int yy_compile_policylex (void); #define YY_DECL int yy_compile_policylex (void) #endif /* !YY_DECL */ /* Code executed at the beginning of each rule, after yy_compile_policytext and yy_compile_policyleng * have been set up. */ #ifndef YY_USER_ACTION #define YY_USER_ACTION #endif /* Code executed at the end of each rule. */ #ifndef YY_BREAK #define YY_BREAK break; #endif #define YY_RULE_SETUP \ YY_USER_ACTION /** The main scanner function which does all the work. */ YY_DECL { register yy_state_type yy_current_state; register char *yy_cp, *yy_bp; register int yy_act; #line 54 "compilepolicy.l" #line 1775 "lex.yy_compile_policy.cc" if ( !(yy_init) ) { (yy_init) = 1; #ifdef YY_USER_INIT YY_USER_INIT; #endif if ( ! (yy_start) ) (yy_start) = 1; /* first start state */ if ( ! yy_compile_policyin ) yy_compile_policyin = stdin; if ( ! yy_compile_policyout ) yy_compile_policyout = stdout; if ( ! YY_CURRENT_BUFFER ) { yy_compile_policyensure_buffer_stack (); YY_CURRENT_BUFFER_LVALUE = yy_compile_policy_create_buffer(yy_compile_policyin,YY_BUF_SIZE ); } yy_compile_policy_load_buffer_state( ); } while ( 1 ) /* loops until end-of-file is reached */ { yy_cp = (yy_c_buf_p); /* Support of yy_compile_policytext. */ *yy_cp = (yy_hold_char); /* yy_bp points to the position in yy_ch_buf of the start of * the current run. */ yy_bp = yy_cp; yy_current_state = (yy_start); yy_match: do { register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)]; if ( yy_accept[yy_current_state] ) { (yy_last_accepting_state) = yy_current_state; (yy_last_accepting_cpos) = yy_cp; } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 701 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; ++yy_cp; } while ( yy_current_state != 700 ); yy_cp = (yy_last_accepting_cpos); yy_current_state = (yy_last_accepting_state); yy_find_action: yy_act = yy_accept[yy_current_state]; YY_DO_BEFORE_ACTION; do_action: /* This label is used only to access EOF actions. */ switch ( yy_act ) { /* beginning of action switch */ case 0: /* must back up */ /* undo the effects of YY_DO_BEFORE_ACTION */ *yy_cp = (yy_hold_char); yy_cp = (yy_last_accepting_cpos); yy_current_state = (yy_last_accepting_state); goto yy_find_action; case 1: YY_RULE_SETUP #line 56 "compilepolicy.l" BEGIN(STR); YY_BREAK case 2: YY_RULE_SETUP #line 58 "compilepolicy.l" return YY_LBRACE; YY_BREAK case 3: YY_RULE_SETUP #line 60 "compilepolicy.l" return YY_RBRACE; YY_BREAK case 4: YY_RULE_SETUP #line 62 "compilepolicy.l" BEGIN(BLOCK); return YY_SOURCE; YY_BREAK case 5: YY_RULE_SETUP #line 63 "compilepolicy.l" BEGIN(BLOCK); return YY_DEST; YY_BREAK case 6: YY_RULE_SETUP #line 64 "compilepolicy.l" BEGIN(BLOCK); return YY_ACTION; YY_BREAK case 7: YY_RULE_SETUP #line 66 "compilepolicy.l" { return YY_SEMICOLON; } YY_BREAK case 8: /* rule 8 can match eol */ YY_RULE_SETUP #line 67 "compilepolicy.l" { _yy_lineno += policy_utils::count_nl(yy_compile_policytext); return YY_LBRACE; } YY_BREAK case 9: /* rule 9 can match eol */ YY_RULE_SETUP #line 71 "compilepolicy.l" { BEGIN(INITIAL); _yy_lineno += policy_utils::count_nl(yy_compile_policytext); return YY_RBRACE; } YY_BREAK case 10: /* rule 10 can match eol */ YY_RULE_SETUP #line 77 "compilepolicy.l" { yylval.c_str = strdup(yy_compile_policytext); _yy_lineno += policy_utils::count_nl(yy_compile_policytext); return YY_STATEMENT; } YY_BREAK case 11: YY_RULE_SETUP #line 82 "compilepolicy.l" BEGIN(INITIAL); YY_BREAK case 12: /* rule 12 can match eol */ YY_RULE_SETUP #line 83 "compilepolicy.l" { yylval.c_str = strdup(yy_compile_policytext); _yy_lineno += policy_utils::count_nl(yy_compile_policytext); return YY_STR; } YY_BREAK case 13: YY_RULE_SETUP #line 88 "compilepolicy.l" { yylval.c_str = strdup(yy_compile_policytext); return YY_IPV4; } YY_BREAK case 14: YY_RULE_SETUP #line 93 "compilepolicy.l" { yylval.c_str = strdup(yy_compile_policytext); return YY_IPV4NET; } YY_BREAK case 15: YY_RULE_SETUP #line 98 "compilepolicy.l" { yylval.c_str = strdup(yy_compile_policytext); return YY_IPV6; } YY_BREAK case 16: YY_RULE_SETUP #line 103 "compilepolicy.l" { yylval.c_str = strdup(yy_compile_policytext); return YY_IPV6NET; } YY_BREAK case 17: YY_RULE_SETUP #line 108 "compilepolicy.l" return YY_TERM; YY_BREAK case 18: YY_RULE_SETUP #line 110 "compilepolicy.l" return YY_POLICY_STATEMENT; YY_BREAK case 19: YY_RULE_SETUP #line 112 "compilepolicy.l" return YY_SET; YY_BREAK case 20: YY_RULE_SETUP #line 114 "compilepolicy.l" return YY_IMPORT; YY_BREAK case 21: YY_RULE_SETUP #line 115 "compilepolicy.l" return YY_EXPORT; YY_BREAK case 22: YY_RULE_SETUP #line 117 "compilepolicy.l" { yylval.c_str = strdup(yy_compile_policytext); return YY_ID; } YY_BREAK case 23: YY_RULE_SETUP #line 121 "compilepolicy.l" return YY_SEMICOLON; YY_BREAK case 24: /* rule 24 can match eol */ YY_RULE_SETUP #line 123 "compilepolicy.l" _yy_lineno++; YY_BREAK case 25: YY_RULE_SETUP #line 125 "compilepolicy.l" /* eat blanks */ YY_BREAK case 26: YY_RULE_SETUP #line 127 "compilepolicy.l" { yyerror("Unknown character"); } YY_BREAK case 27: YY_RULE_SETUP #line 129 "compilepolicy.l" ECHO; YY_BREAK #line 2021 "lex.yy_compile_policy.cc" case YY_STATE_EOF(INITIAL): case YY_STATE_EOF(STR): case YY_STATE_EOF(BLOCK): yyterminate(); case YY_END_OF_BUFFER: { /* Amount of text matched not including the EOB char. */ int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1; /* Undo the effects of YY_DO_BEFORE_ACTION. */ *yy_cp = (yy_hold_char); YY_RESTORE_YY_MORE_OFFSET if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) { /* We're scanning a new file or input source. It's * possible that this happened because the user * just pointed yy_compile_policyin at a new source and called * yy_compile_policylex(). If so, then we have to assure * consistency between YY_CURRENT_BUFFER and our * globals. Here is the right place to do so, because * this is the first action (other than possibly a * back-up) that will match for the new input source. */ (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; YY_CURRENT_BUFFER_LVALUE->yy_input_file = yy_compile_policyin; YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; } /* Note that here we test for yy_c_buf_p "<=" to the position * of the first EOB in the buffer, since yy_c_buf_p will * already have been incremented past the NUL character * (since all states make transitions on EOB to the * end-of-buffer state). Contrast this with the test * in input(). */ if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) { /* This was really a NUL. */ yy_state_type yy_next_state; (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state( ); /* Okay, we're now positioned to make the NUL * transition. We couldn't have * yy_get_previous_state() go ahead and do it * for us because it doesn't know how to deal * with the possibility of jamming (and we don't * want to build jamming into it because then it * will run more slowly). */ yy_next_state = yy_try_NUL_trans( yy_current_state ); yy_bp = (yytext_ptr) + YY_MORE_ADJ; if ( yy_next_state ) { /* Consume the NUL. */ yy_cp = ++(yy_c_buf_p); yy_current_state = yy_next_state; goto yy_match; } else { yy_cp = (yy_last_accepting_cpos); yy_current_state = (yy_last_accepting_state); goto yy_find_action; } } else switch ( yy_get_next_buffer( ) ) { case EOB_ACT_END_OF_FILE: { (yy_did_buffer_switch_on_eof) = 0; if ( yy_compile_policywrap( ) ) { /* Note: because we've taken care in * yy_get_next_buffer() to have set up * yy_compile_policytext, we can now set up * yy_c_buf_p so that if some total * hoser (like flex itself) wants to * call the scanner after we return the * YY_NULL, it'll still work - another * YY_NULL will get returned. */ (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ; yy_act = YY_STATE_EOF(YY_START); goto do_action; } else { if ( ! (yy_did_buffer_switch_on_eof) ) YY_NEW_FILE; } break; } case EOB_ACT_CONTINUE_SCAN: (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state( ); yy_cp = (yy_c_buf_p); yy_bp = (yytext_ptr) + YY_MORE_ADJ; goto yy_match; case EOB_ACT_LAST_MATCH: (yy_c_buf_p) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)]; yy_current_state = yy_get_previous_state( ); yy_cp = (yy_c_buf_p); yy_bp = (yytext_ptr) + YY_MORE_ADJ; goto yy_find_action; } break; } default: YY_FATAL_ERROR( "fatal flex scanner internal error--no action found" ); } /* end of action switch */ } /* end of scanning one token */ } /* end of yy_compile_policylex */ /* yy_get_next_buffer - try to read in a new buffer * * Returns a code representing an action: * EOB_ACT_LAST_MATCH - * EOB_ACT_CONTINUE_SCAN - continue scanning from current position * EOB_ACT_END_OF_FILE - end of file */ static int yy_get_next_buffer (void) { register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; register char *source = (yytext_ptr); register int number_to_move, i; int ret_val; if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] ) YY_FATAL_ERROR( "fatal flex scanner internal error--end of buffer missed" ); if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) { /* Don't try to fill the buffer, so this is an EOF. */ if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 ) { /* We matched a single character, the EOB, so * treat this as a final EOF. */ return EOB_ACT_END_OF_FILE; } else { /* We matched some text prior to the EOB, first * process it. */ return EOB_ACT_LAST_MATCH; } } /* Try to read more data. */ /* First move last chars to start of buffer. */ number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1; for ( i = 0; i < number_to_move; ++i ) *(dest++) = *(source++); if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) /* don't do the read, it's not guaranteed to return an EOF, * just force an EOF */ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0; else { int num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; while ( num_to_read <= 0 ) { /* Not enough room in the buffer - grow it. */ /* just a shorter name for the current buffer */ YY_BUFFER_STATE b = YY_CURRENT_BUFFER; int yy_c_buf_p_offset = (int) ((yy_c_buf_p) - b->yy_ch_buf); if ( b->yy_is_our_buffer ) { int new_size = b->yy_buf_size * 2; if ( new_size <= 0 ) b->yy_buf_size += b->yy_buf_size / 8; else b->yy_buf_size *= 2; b->yy_ch_buf = (char *) /* Include room in for 2 EOB chars. */ yy_compile_policyrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 ); } else /* Can't grow it, we don't own it. */ b->yy_ch_buf = 0; if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "fatal error - scanner input buffer overflow" ); (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset]; num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; } if ( num_to_read > YY_READ_BUF_SIZE ) num_to_read = YY_READ_BUF_SIZE; /* Read in more data. */ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), (yy_n_chars), num_to_read ); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); } if ( (yy_n_chars) == 0 ) { if ( number_to_move == YY_MORE_ADJ ) { ret_val = EOB_ACT_END_OF_FILE; yy_compile_policyrestart(yy_compile_policyin ); } else { ret_val = EOB_ACT_LAST_MATCH; YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_EOF_PENDING; } } else ret_val = EOB_ACT_CONTINUE_SCAN; (yy_n_chars) += number_to_move; YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR; YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR; (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; return ret_val; } /* yy_get_previous_state - get the state just before the EOB char was reached */ static yy_state_type yy_get_previous_state (void) { register yy_state_type yy_current_state; register char *yy_cp; yy_current_state = (yy_start); for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp ) { register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); if ( yy_accept[yy_current_state] ) { (yy_last_accepting_state) = yy_current_state; (yy_last_accepting_cpos) = yy_cp; } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 701 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; } return yy_current_state; } /* yy_try_NUL_trans - try to make a transition on the NUL character * * synopsis * next_state = yy_try_NUL_trans( current_state ); */ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state ) { register int yy_is_jam; register char *yy_cp = (yy_c_buf_p); register YY_CHAR yy_c = 1; if ( yy_accept[yy_current_state] ) { (yy_last_accepting_state) = yy_current_state; (yy_last_accepting_cpos) = yy_cp; } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 701 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; yy_is_jam = (yy_current_state == 700); return yy_is_jam ? 0 : yy_current_state; } #ifndef YY_NO_INPUT #ifdef __cplusplus static int yyinput (void) #else static int input (void) #endif { int c; *(yy_c_buf_p) = (yy_hold_char); if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR ) { /* yy_c_buf_p now points to the character we want to return. * If this occurs *before* the EOB characters, then it's a * valid NUL; if not, then we've hit the end of the buffer. */ if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) /* This was really a NUL. */ *(yy_c_buf_p) = '\0'; else { /* need more input */ int offset = (yy_c_buf_p) - (yytext_ptr); ++(yy_c_buf_p); switch ( yy_get_next_buffer( ) ) { case EOB_ACT_LAST_MATCH: /* This happens because yy_g_n_b() * sees that we've accumulated a * token and flags that we need to * try matching the token before * proceeding. But for input(), * there's no matching to consider. * So convert the EOB_ACT_LAST_MATCH * to EOB_ACT_END_OF_FILE. */ /* Reset buffer status. */ yy_compile_policyrestart(yy_compile_policyin ); /*FALLTHROUGH*/ case EOB_ACT_END_OF_FILE: { if ( yy_compile_policywrap( ) ) return EOF; if ( ! (yy_did_buffer_switch_on_eof) ) YY_NEW_FILE; #ifdef __cplusplus return yyinput(); #else return input(); #endif } case EOB_ACT_CONTINUE_SCAN: (yy_c_buf_p) = (yytext_ptr) + offset; break; } } } c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */ *(yy_c_buf_p) = '\0'; /* preserve yy_compile_policytext */ (yy_hold_char) = *++(yy_c_buf_p); return c; } #endif /* ifndef YY_NO_INPUT */ /** Immediately switch to a different input stream. * @param input_file A readable stream. * * @note This function does not reset the start condition to @c INITIAL . */ void yy_compile_policyrestart (FILE * input_file ) { if ( ! YY_CURRENT_BUFFER ){ yy_compile_policyensure_buffer_stack (); YY_CURRENT_BUFFER_LVALUE = yy_compile_policy_create_buffer(yy_compile_policyin,YY_BUF_SIZE ); } yy_compile_policy_init_buffer(YY_CURRENT_BUFFER,input_file ); yy_compile_policy_load_buffer_state( ); } /** Switch to a different input buffer. * @param new_buffer The new input buffer. * */ void yy_compile_policy_switch_to_buffer (YY_BUFFER_STATE new_buffer ) { /* TODO. We should be able to replace this entire function body * with * yy_compile_policypop_buffer_state(); * yy_compile_policypush_buffer_state(new_buffer); */ yy_compile_policyensure_buffer_stack (); if ( YY_CURRENT_BUFFER == new_buffer ) return; if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ *(yy_c_buf_p) = (yy_hold_char); YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); } YY_CURRENT_BUFFER_LVALUE = new_buffer; yy_compile_policy_load_buffer_state( ); /* We don't actually know whether we did this switch during * EOF (yy_compile_policywrap()) processing, but the only time this flag * is looked at is after yy_compile_policywrap() is called, so it's safe * to go ahead and always set it. */ (yy_did_buffer_switch_on_eof) = 1; } static void yy_compile_policy_load_buffer_state (void) { (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; yy_compile_policyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; (yy_hold_char) = *(yy_c_buf_p); } /** Allocate and initialize an input buffer state. * @param file A readable stream. * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. * * @return the allocated buffer state. */ YY_BUFFER_STATE yy_compile_policy_create_buffer (FILE * file, int size ) { YY_BUFFER_STATE b; b = (YY_BUFFER_STATE) yy_compile_policyalloc(sizeof( struct yy_buffer_state ) ); if ( ! b ) YY_FATAL_ERROR( "out of dynamic memory in yy_compile_policy_create_buffer()" ); b->yy_buf_size = size; /* yy_ch_buf has to be 2 characters longer than the size given because * we need to put in 2 end-of-buffer characters. */ b->yy_ch_buf = (char *) yy_compile_policyalloc(b->yy_buf_size + 2 ); if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "out of dynamic memory in yy_compile_policy_create_buffer()" ); b->yy_is_our_buffer = 1; yy_compile_policy_init_buffer(b,file ); return b; } /** Destroy the buffer. * @param b a buffer created with yy_compile_policy_create_buffer() * */ void yy_compile_policy_delete_buffer (YY_BUFFER_STATE b ) { if ( ! b ) return; if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; if ( b->yy_is_our_buffer ) yy_compile_policyfree((void *) b->yy_ch_buf ); yy_compile_policyfree((void *) b ); } /* Initializes or reinitializes a buffer. * This function is sometimes called more than once on the same buffer, * such as during a yy_compile_policyrestart() or at EOF. */ static void yy_compile_policy_init_buffer (YY_BUFFER_STATE b, FILE * file ) { int oerrno = errno; yy_compile_policy_flush_buffer(b ); b->yy_input_file = file; b->yy_fill_buffer = 1; /* If b is the current buffer, then yy_compile_policy_init_buffer was _probably_ * called from yy_compile_policyrestart() or through yy_get_next_buffer. * In that case, we don't want to reset the lineno or column. */ if (b != YY_CURRENT_BUFFER){ b->yy_bs_lineno = 1; b->yy_bs_column = 0; } b->yy_is_interactive = 0; errno = oerrno; } /** Discard all buffered characters. On the next scan, YY_INPUT will be called. * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. * */ void yy_compile_policy_flush_buffer (YY_BUFFER_STATE b ) { if ( ! b ) return; b->yy_n_chars = 0; /* We always need two end-of-buffer characters. The first causes * a transition to the end-of-buffer state. The second causes * a jam in that state. */ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; b->yy_buf_pos = &b->yy_ch_buf[0]; b->yy_at_bol = 1; b->yy_buffer_status = YY_BUFFER_NEW; if ( b == YY_CURRENT_BUFFER ) yy_compile_policy_load_buffer_state( ); } /** Pushes the new state onto the stack. The new state becomes * the current state. This function will allocate the stack * if necessary. * @param new_buffer The new state. * */ void yy_compile_policypush_buffer_state (YY_BUFFER_STATE new_buffer ) { if (new_buffer == NULL) return; yy_compile_policyensure_buffer_stack(); /* This block is copied from yy_compile_policy_switch_to_buffer. */ if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ *(yy_c_buf_p) = (yy_hold_char); YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); } /* Only push if top exists. Otherwise, replace top. */ if (YY_CURRENT_BUFFER) (yy_buffer_stack_top)++; YY_CURRENT_BUFFER_LVALUE = new_buffer; /* copied from yy_compile_policy_switch_to_buffer. */ yy_compile_policy_load_buffer_state( ); (yy_did_buffer_switch_on_eof) = 1; } /** Removes and deletes the top of the stack, if present. * The next element becomes the new top. * */ void yy_compile_policypop_buffer_state (void) { if (!YY_CURRENT_BUFFER) return; yy_compile_policy_delete_buffer(YY_CURRENT_BUFFER ); YY_CURRENT_BUFFER_LVALUE = NULL; if ((yy_buffer_stack_top) > 0) --(yy_buffer_stack_top); if (YY_CURRENT_BUFFER) { yy_compile_policy_load_buffer_state( ); (yy_did_buffer_switch_on_eof) = 1; } } /* Allocates the stack if it does not exist. * Guarantees space for at least one push. */ static void yy_compile_policyensure_buffer_stack (void) { int num_to_alloc; if (!(yy_buffer_stack)) { /* First allocation is just for 2 elements, since we don't know if this * scanner will even need a stack. We use 2 instead of 1 to avoid an * immediate realloc on the next call. */ num_to_alloc = 1; (yy_buffer_stack) = (struct yy_buffer_state**)yy_compile_policyalloc (num_to_alloc * sizeof(struct yy_buffer_state*) ); memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*)); (yy_buffer_stack_max) = num_to_alloc; (yy_buffer_stack_top) = 0; return; } if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){ /* Increase the buffer to prepare for a possible push. */ int grow_size = 8 /* arbitrary grow size */; num_to_alloc = (yy_buffer_stack_max) + grow_size; (yy_buffer_stack) = (struct yy_buffer_state**)yy_compile_policyrealloc ((yy_buffer_stack), num_to_alloc * sizeof(struct yy_buffer_state*) ); /* zero only the new slots.*/ memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*)); (yy_buffer_stack_max) = num_to_alloc; } } /** Setup the input buffer state to scan directly from a user-specified character buffer. * @param base the character buffer * @param size the size in bytes of the character buffer * * @return the newly allocated buffer state object. */ YY_BUFFER_STATE yy_compile_policy_scan_buffer (char * base, yy_size_t size ) { YY_BUFFER_STATE b; if ( size < 2 || base[size-2] != YY_END_OF_BUFFER_CHAR || base[size-1] != YY_END_OF_BUFFER_CHAR ) /* They forgot to leave room for the EOB's. */ return 0; b = (YY_BUFFER_STATE) yy_compile_policyalloc(sizeof( struct yy_buffer_state ) ); if ( ! b ) YY_FATAL_ERROR( "out of dynamic memory in yy_compile_policy_scan_buffer()" ); b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ b->yy_buf_pos = b->yy_ch_buf = base; b->yy_is_our_buffer = 0; b->yy_input_file = 0; b->yy_n_chars = b->yy_buf_size; b->yy_is_interactive = 0; b->yy_at_bol = 1; b->yy_fill_buffer = 0; b->yy_buffer_status = YY_BUFFER_NEW; yy_compile_policy_switch_to_buffer(b ); return b; } /** Setup the input buffer state to scan a string. The next call to yy_compile_policylex() will * scan from a @e copy of @a str. * @param str a NUL-terminated string to scan * * @return the newly allocated buffer state object. * @note If you want to scan bytes that may contain NUL values, then use * yy_compile_policy_scan_bytes() instead. */ YY_BUFFER_STATE yy_compile_policy_scan_string (yyconst char * yystr ) { return yy_compile_policy_scan_bytes(yystr,strlen(yystr) ); } /** Setup the input buffer state to scan the given bytes. The next call to yy_compile_policylex() will * scan from a @e copy of @a bytes. * @param bytes the byte buffer to scan * @param len the number of bytes in the buffer pointed to by @a bytes. * * @return the newly allocated buffer state object. */ YY_BUFFER_STATE yy_compile_policy_scan_bytes (yyconst char * yybytes, int _yybytes_len ) { YY_BUFFER_STATE b; char *buf; yy_size_t n; int i; /* Get memory for full buffer, including space for trailing EOB's. */ n = _yybytes_len + 2; buf = (char *) yy_compile_policyalloc(n ); if ( ! buf ) YY_FATAL_ERROR( "out of dynamic memory in yy_compile_policy_scan_bytes()" ); for ( i = 0; i < _yybytes_len; ++i ) buf[i] = yybytes[i]; buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; b = yy_compile_policy_scan_buffer(buf,n ); if ( ! b ) YY_FATAL_ERROR( "bad buffer in yy_compile_policy_scan_bytes()" ); /* It's okay to grow etc. this buffer, and we should throw it * away when we're done. */ b->yy_is_our_buffer = 1; return b; } #ifndef YY_EXIT_FAILURE #define YY_EXIT_FAILURE 2 #endif static void yy_fatal_error (yyconst char* msg ) { (void) fprintf( stderr, "%s\n", msg ); exit( YY_EXIT_FAILURE ); } /* Redefine yyless() so it works in section 3 code. */ #undef yyless #define yyless(n) \ do \ { \ /* Undo effects of setting up yy_compile_policytext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ yy_compile_policytext[yy_compile_policyleng] = (yy_hold_char); \ (yy_c_buf_p) = yy_compile_policytext + yyless_macro_arg; \ (yy_hold_char) = *(yy_c_buf_p); \ *(yy_c_buf_p) = '\0'; \ yy_compile_policyleng = yyless_macro_arg; \ } \ while ( 0 ) /* Accessor methods (get/set functions) to struct members. */ /** Get the current line number. * */ int yy_compile_policyget_lineno (void) { return yy_compile_policylineno; } /** Get the input stream. * */ FILE *yy_compile_policyget_in (void) { return yy_compile_policyin; } /** Get the output stream. * */ FILE *yy_compile_policyget_out (void) { return yy_compile_policyout; } /** Get the length of the current token. * */ int yy_compile_policyget_leng (void) { return yy_compile_policyleng; } /** Get the current token. * */ char *yy_compile_policyget_text (void) { return yy_compile_policytext; } /** Set the current line number. * @param line_number * */ void yy_compile_policyset_lineno (int line_number ) { yy_compile_policylineno = line_number; } /** Set the input stream. This does not discard the current * input buffer. * @param in_str A readable stream. * * @see yy_compile_policy_switch_to_buffer */ void yy_compile_policyset_in (FILE * in_str ) { yy_compile_policyin = in_str ; } void yy_compile_policyset_out (FILE * out_str ) { yy_compile_policyout = out_str ; } int yy_compile_policyget_debug (void) { return yy_compile_policy_flex_debug; } void yy_compile_policyset_debug (int bdebug ) { yy_compile_policy_flex_debug = bdebug ; } static int yy_init_globals (void) { /* Initialization is the same as for the non-reentrant scanner. * This function is called from yy_compile_policylex_destroy(), so don't allocate here. */ (yy_buffer_stack) = 0; (yy_buffer_stack_top) = 0; (yy_buffer_stack_max) = 0; (yy_c_buf_p) = (char *) 0; (yy_init) = 0; (yy_start) = 0; /* Defined in main.c */ #ifdef YY_STDINIT yy_compile_policyin = stdin; yy_compile_policyout = stdout; #else yy_compile_policyin = (FILE *) 0; yy_compile_policyout = (FILE *) 0; #endif /* For future reference: Set errno on error, since we are called by * yy_compile_policylex_init() */ return 0; } /* yy_compile_policylex_destroy is for both reentrant and non-reentrant scanners. */ int yy_compile_policylex_destroy (void) { /* Pop the buffer stack, destroying each element. */ while(YY_CURRENT_BUFFER){ yy_compile_policy_delete_buffer(YY_CURRENT_BUFFER ); YY_CURRENT_BUFFER_LVALUE = NULL; yy_compile_policypop_buffer_state(); } /* Destroy the stack itself. */ yy_compile_policyfree((yy_buffer_stack) ); (yy_buffer_stack) = NULL; /* Reset the globals. This is important in a non-reentrant scanner so the next time * yy_compile_policylex() is called, initialization will occur. */ yy_init_globals( ); return 0; } /* * Internal utility routines. */ #ifndef yytext_ptr static void yy_flex_strncpy (char* s1, yyconst char * s2, int n ) { register int i; for ( i = 0; i < n; ++i ) s1[i] = s2[i]; } #endif #ifdef YY_NEED_STRLEN static int yy_flex_strlen (yyconst char * s ) { register int n; for ( n = 0; s[n]; ++n ) ; return n; } #endif void *yy_compile_policyalloc (yy_size_t size ) { return (void *) malloc( size ); } void *yy_compile_policyrealloc (void * ptr, yy_size_t size ) { /* The cast to (char *) in the following accommodates both * implementations that use char* generic pointers, and those * that use void* generic pointers. It works with the latter * because both ANSI C and C++ allow castless assignment from * any pointer type to void*, and deal with argument conversions * as though doing an assignment. */ return (void *) realloc( (char *) ptr, size ); } void yy_compile_policyfree (void * ptr ) { free( (char *) ptr ); /* see yy_compile_policyrealloc() for (char *) cast */ } #define YYTABLES_NAME "yytables" #line 129 "compilepolicy.l" void yyerror(const char *m) { ostringstream oss; oss << "Error on line " << _yy_lineno << ": " << m; _yy_last_err = oss.str(); } // XXX: no memory management [a lot of leaks... for simplicy] int do_parsing(const string& conf, string& outerr) { YY_BUFFER_STATE yybuffstate = yy_compile_policy_scan_string(conf.c_str()); _yy_last_err = "No error"; _yy_lineno =1; int res = yyparse(); yy_compile_policy_delete_buffer(yybuffstate); outerr = _yy_last_err; return res; } xorp/policy/tests/policy2.src0000664000076400007640000000032611421137511016410 0ustar greearbgreearbpolicy-statement rejected { term a { source { protocol == static_routes; metric == 1 + 1 - 1 + 1 - 1; } dest { } action { aspath = 6234 + aspath; reject; } } } export bgp "rejected"; xorp/policy/tests/policy1.src0000664000076400007640000000026311421137511016407 0ustar greearbgreearbpolicy-statement accepted { term a { source { protocol : static_routes; } dest { } action { trace: 1; aspath = 6234 + aspath; } } } export bgp "accepted"; xorp/policy/tests/filter_manager_fake.cc0000664000076400007640000000222111540224233020566 0ustar greearbgreearb// vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "libxorp/xorp.h" #include "filter_manager_fake.hh" void FilterManagerFake::update_filter(const Code::Target& t) { cout << "FilterManagerFake update_filter: " << t.str() << endl; } void FilterManagerFake::flush_updates(uint32_t msec) { cout << "FilterManagerFake flush_updates msec: " << msec << endl; } xorp/policy/tests/policybench.cc0000664000076400007640000001517011421137511017127 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "policy/policy_module.h" #include "policy/common/policy_utils.hh" #include "policy/common/filter.hh" #include "policy/backend/policy_filter.hh" #include "libxorp/xorp.h" #include "libxorp/timer.hh" #include "file_varrw.hh" #include "bgp/bgp_varrw.hh" #ifdef HAVE_GETOPT_H #include #endif using namespace policy_utils; namespace { enum { BTYPE_FILE = 0, BTYPE_BGP, }; template struct bgp_routes { bgp_routes(); InternalMessage* bgp_message; SubnetRoute* bgp_route; PAListRef bgp_attributes; }; template bgp_routes::bgp_routes() { IPNet net("192.168.0.0/24"); A nexthop("192.168.0.1"); ASPath path("7865"); OriginType origin = EGP; FPAList4Ref fpalist1 = new FastPathAttributeList(nexthop, path, origin); bgp_attributes = new PathAttributeList(fpalist1); bgp_route = new SubnetRoute(net, bgp_attributes, NULL); bgp_message = new InternalMessage(bgp_route, NULL, 1); } #define AF IPv4 typedef BGPVarRW BGP_VARRW; typedef bgp_routes BGP_ROUTES; struct conf { conf() : c_bgp_varrw(NULL), c_bgp_routes(NULL) { memset(&c_exec_total, 0, sizeof(c_exec_total)); } // conf string c_policy_file; string c_varrw_file; unsigned c_iterations; int c_type; int c_profiler; // stats TimeVal c_start; TimeVal c_end; PolicyProfiler c_exec; double c_exec_total[PolicyProfiler::MAX_SAMPLES]; // BGP stuff BGP_VARRW* c_bgp_varrw; BGP_ROUTES** c_bgp_routes; } _conf; void usage(const string& progname) { cout << "Usage: " << progname << " " << endl << "-p\t" << endl << "-v\t" << endl << "-i\t" << endl << "-t\t" << endl << "-n\tdisable profiler" << endl << "-h\thelp" << endl << endl << "Supported benchmark types:" << endl << "0\tFileVarRW (test policy filter)" << endl << "1\tBGPVarRW" << endl ; exit(1); } void die(const string& err) { cout << "Death: " << err << endl; exit(1); } void get_time(TimeVal& tv) { TimerList::system_gettimeofday(&tv); } void stats() { double elapsed = (_conf.c_end - _conf.c_start).to_ms(); double iter = _conf.c_iterations; double speed = iter / elapsed * 1000.0; printf("Total time %d (ms) iterations %d\n" "Iterations/s %.3f\n" , (int) elapsed, (int) iter, speed ); double total = 0.0; for (unsigned i = 0; i < _conf.c_exec.count(); i++) { _conf.c_exec_total[i] /= iter; total += _conf.c_exec_total[i]; } if (total == 0.0) return; for (unsigned i = 0; i < _conf.c_exec.count(); i++) { double tm = _conf.c_exec_total[i]; printf("Instr %2d Avg Time %10.3f\t(%5.2f%%)\n" , i+1, tm, tm/total * 100.0); } } void do_iter_bgp(PolicyFilter& filter, VarRW& varrw, int i) { BGP_VARRW& v = static_cast(varrw); v.attach_route(*_conf.c_bgp_routes[i]->bgp_message, false); filter.acceptRoute(v); if (v.modified()) { v.filtered_message(); // XXX leak } } void do_iter(PolicyFilter& filter, VarRW& varrw, int i) { _conf.c_exec.clear(); switch (_conf.c_type) { case BTYPE_BGP: do_iter_bgp(filter, varrw, i); break; case BTYPE_FILE: filter.acceptRoute(varrw); break; } for (unsigned i = 0; i < _conf.c_exec.count(); i++) _conf.c_exec_total[i] += _conf.c_exec.sample(i); } VarRW* create_varrw_file() { FileVarRW* f = new FileVarRW(); f->set_verbose(false); if (!_conf.c_varrw_file.empty()) f->load(_conf.c_varrw_file); return f; } VarRW* create_varrw_bgp(int num) { if (!_conf.c_bgp_varrw) _conf.c_bgp_varrw = new BGP_VARRW(filter::filter2str(filter::IMPORT)); if (!_conf.c_bgp_routes) _conf.c_bgp_routes = new BGP_ROUTES* [_conf.c_iterations]; _conf.c_bgp_routes[num] = new BGP_ROUTES; return _conf.c_bgp_varrw; } VarRW* create_varrw(int i) { switch (_conf.c_type) { case BTYPE_FILE: return create_varrw_file(); case BTYPE_BGP: return create_varrw_bgp(i); } XLOG_ASSERT(false); return NULL; } void benchmark_run(void) { string policy; VarRW** varrws; unsigned iters = _conf.c_iterations; PolicyFilter filter; cout << "Loading..." << endl; read_file(_conf.c_policy_file, policy); filter.configure(policy); filter.set_profiler_exec(&_conf.c_exec); varrws = new VarRW* [iters]; for (unsigned i = 0; i < iters; i++) varrws[i] = create_varrw(i); cout << "Benchmarking. Iterations: " << iters << endl; get_time(_conf.c_start); for (unsigned i = 0; i < iters; i++) do_iter(filter, *varrws[i], i); get_time(_conf.c_end); cout << "Stats:" << endl; stats(); } void own(void) { benchmark_run(); } } // namespace int main(int argc, char* argv[]) { int opt; _conf.c_iterations = 100000; _conf.c_type = 0; _conf.c_profiler = 1; while ((opt = getopt(argc, argv, "hp:v:i:t:n")) != -1) { switch (opt) { case 'n': _conf.c_profiler = 0; break; case 't': _conf.c_type = atoi(optarg); break; case 'p': _conf.c_policy_file = optarg; break; case 'v': _conf.c_varrw_file = optarg; break; case 'i': _conf.c_iterations = atoi(optarg); break; case 'h': // fall-through default: usage(argv[0]); break; } } if (_conf.c_policy_file.empty()) usage(argv[0]); xlog_init(argv[0], 0); xlog_set_verbose(XLOG_VERBOSE_HIGH); xlog_disable(XLOG_LEVEL_TRACE); xlog_add_default_output(); xlog_start(); try { own(); } catch (const XorpReasonedException& e) { die(e.str()); } catch (const exception& e) { die(e.what()); } xlog_stop(); xlog_exit(); exit(0); } xorp/policy/tests/test_policy.sh0000775000076400007640000000120511421137511017210 0ustar greearbgreearb#!/bin/sh # # $XORP: xorp/policy/test/test_policy.sh,v 1.2 2004/10/05 22:42:27 pavlin Exp $ # TMPFILE=/tmp/xorp_policy_test.txt cleanup() { rm -f ${TMPFILE} } if [ $# -ne "4" ] then echo "Usage: `basename $0` policyfile policy_var_map_file varfile exitcode" exit 1 fi echo Will run policy $1 with policy_var_map_file $2 with variables $3 checking for exit code $4 ./compilepolicy -s $1 -m $2 -o ${TMPFILE} if [ $? -ne 0 ] ; then exit 1 fi cat ${TMPFILE} ./execpolicy ${TMPFILE} $3 EXITCODE=$? if [ ${EXITCODE} -ne $4 ] then cleanup echo Test FAILED exit code ${EXITCODE} exit 1 fi cleanup echo Test was SUCCESSFULL exit 0 xorp/policy/dependency.cc0000664000076400007640000001162011421137511015600 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "policy_module.h" #include "libxorp/xorp.h" #include "dependency.hh" #include "common/element_base.hh" #include "policy_statement.hh" template Dependency::Dependency() { } template Dependency::~Dependency() { clear(); } template void Dependency::clear() { for (typename Map::iterator i = _map.begin(); i != _map.end(); ++i) { Pair* p = (*i).second; if (p->first != NULL) delete p->first; delete p; } _map.clear(); } template bool Dependency::exists(const string& objectname) const { return _map.find(objectname) != _map.end(); } template bool Dependency::create(const string& objectname, T* object) { if (exists(objectname)) return false; Pair* p = new Pair(object, DependencyList()); _map[objectname] = p; return true; } template void Dependency::remove(const string& objectname) { typename Map::iterator i = _map.find(objectname); if (i == _map.end()) xorp_throw(DependencyError, "Dependency remove: Cannot find object " + objectname); Pair* p = (*i).second; DependencyList& s = (*p).second; // check if object is in use if (!s.empty()) { ostringstream oss; oss << "Dependency remove: Object " << objectname << " in use by: "; for (DependencyList::iterator j = s.begin(); j != s.end(); ++j) oss << *j << " "; xorp_throw(DependencyError, oss.str()); } // delete object if (p->first) delete p->first; delete p; _map.erase(i); } template void Dependency::add_dependency(const string& objectname, const string& dep) { Pair* p = findDepend(objectname); DependencyList& s = (*p).second; s.push_back(dep); } template void Dependency::del_dependency(const string& objectname, const string& dep) { Pair* p = findDepend(objectname); DependencyList& s = (*p).second; s.remove(dep); } template T& Dependency::find(const string& objectname) const { Pair* p = findDepend(objectname); T* x = (*p).first; return *x; } template T* Dependency::find_ptr(const string& objectname) const { typename Map::const_iterator i = _map.find(objectname); if (i == _map.end()) return (NULL); Pair* p = i->second; T* x = (*p).first; return x; } template void Dependency::get_deps(const string& objectname, set& deps) const { Pair* p = findDepend(objectname); DependencyList& s = (*p).second; for (typename DependencyList::iterator i = s.begin(); i != s.end(); ++i) deps.insert(*i); // duplicates are removed [set] } template void Dependency::update_object(const string& objectname,T* obj) { Pair* p = findDepend(objectname); // delete old if (p->first) delete p->first; // replace [dependencies are maintained] p->first = obj; } template typename Dependency::Map::const_iterator Dependency::get_iterator() const { return _map.begin(); } template bool Dependency::has_next(const typename Map::const_iterator& i) const { return i != _map.end(); } template typename Dependency::ObjPair Dependency::next(typename Map::const_iterator& i) const { if (i == _map.end()) xorp_throw(DependencyError, "No more objects"); Pair* p = (*i).second; const T* obj = p->first; ObjPair ret((*i).first,*obj); i++; return ret; } template void Dependency::keys(KEYS& out) const { typename Map::const_iterator i = get_iterator(); while (has_next(i)) { ObjPair op(next(i)); out.insert(op.name); } } template typename Dependency::Pair* Dependency::findDepend(const string& objectname) const { typename Map::const_iterator i = _map.find(objectname); if (i == _map.end()) xorp_throw(DependencyError, "Dependency: Cannot find object of name " + objectname); return (*i).second; } template class Dependency; template class Dependency; xorp/policy/policy_statement.cc0000664000076400007640000002061111421137511017045 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "policy_module.h" #include "libxorp/xorp.h" #include "policy/common/policy_utils.hh" #include "policy_statement.hh" #include "policy_map.hh" using namespace policy_utils; PolicyStatement::PolicyStatement(const string& name, SetMap& smap, PolicyMap& pmap) : _name(name), _smap(smap), _pmap(pmap) { } PolicyStatement::~PolicyStatement() { del_dependencies(); policy_utils::clear_map_container(_terms); list >::iterator iter; for (iter = _out_of_order_terms.begin(); iter != _out_of_order_terms.end(); ++iter) { delete iter->second; } } void PolicyStatement::add_term(const ConfigNodeId& order, Term* term) { if ((_terms.find(order) != _terms.end()) || (find_out_of_order_term(order) != _out_of_order_terms.end())) { xorp_throw(PolicyException, "Term already present in position: " + order.str()); } pair res; res = _terms.insert(order, term); if (res.second != true) { // // Failed to add the entry, probably because it was received out of // order. Add it to the list of entries that need to be added later. // _out_of_order_terms.push_back(make_pair(order, term)); return; } // // Try to add any entries that are out of order. // Note that we need to keep trying traversing the list until // no entry is added. // while (true) { bool entry_added = false; list >::iterator iter; for (iter = _out_of_order_terms.begin(); iter != _out_of_order_terms.end(); ++iter) { res = _terms.insert(iter->first, iter->second); if (res.second == true) { // Entry added successfully entry_added = true; _out_of_order_terms.erase(iter); break; } } if (! entry_added) break; } } PolicyStatement::TermContainer::iterator PolicyStatement::get_term_iter(const string& name) { TermContainer::iterator i; for(i = _terms.begin(); i != _terms.end(); ++i) { if( (i->second)->name() == name) { return i; } } return i; } PolicyStatement::TermContainer::const_iterator PolicyStatement::get_term_iter(const string& name) const { TermContainer::const_iterator i; for(i = _terms.begin(); i != _terms.end(); ++i) { if( (i->second)->name() == name) { return i; } } return i; } Term& PolicyStatement::find_term(const string& name) const { TermContainer::const_iterator i = get_term_iter(name); if(i == _terms.end()) { list >::const_iterator list_iter; list_iter = find_out_of_order_term(name); if (list_iter != _out_of_order_terms.end()) { Term* t = list_iter->second; return *t; } xorp_throw(PolicyStatementErr, "Term " + name + " not found in policy " + _name); } Term* t = i->second; return *t; } bool PolicyStatement::delete_term(const string& name) { TermContainer::iterator i = get_term_iter(name); if(i == _terms.end()) { list >::iterator list_iter; list_iter = find_out_of_order_term(name); if (list_iter != _out_of_order_terms.end()) { Term* t = list_iter->second; _out_of_order_terms.erase(list_iter); delete t; return true; } return false; } Term* t = i->second; _terms.erase(i); delete t; return true; } void PolicyStatement::set_policy_end() { // The final action lives in an internally created term named __final. // This is a single node (i.e., unlike terms, there can be only one // instance) so the ordering is messed up and we need to fix it. This call // though should be received after all terms have been added so it is safe // to assume that we simply add the term at the end of the list. for (OOL::iterator i = _out_of_order_terms.begin(); i != _out_of_order_terms.end(); ++i) { Term* t = i->second; if (t->name().compare("__final") != 0) continue; // find last position and compute next one XLOG_ASSERT(!_terms.empty()); TermContainer::iterator j = _terms.end(); j--; ConfigNodeId order = j->first; ConfigNodeId::UniqueNodeId nid = order.unique_node_id(); // XXX we really need operator++ in ConfigNodeId order = ConfigNodeId(nid+1, nid); // insert bool res = _terms.insert(order, t).second; XLOG_ASSERT(res); _out_of_order_terms.erase(i); break; } TermContainer::iterator i; for (i = _terms.begin(); i != _terms.end(); ++i) { Term* term = i->second; term->set_term_end(); } // // XXX: The multi-value term nodes should not have holes, hence // print a warning if there are remaining out of order terms. // if (! _out_of_order_terms.empty()) { // Create a list with the term names string term_names; list >::iterator list_iter; for (list_iter = _out_of_order_terms.begin(); list_iter != _out_of_order_terms.end(); ++list_iter) { Term* term = (*list_iter).second; if (list_iter != _out_of_order_terms.begin()) term_names += ", "; term_names += term->name(); } XLOG_ERROR("Found out-of-order term(s) inside policy %s: %s. " "The term(s) will be excluded!", name().c_str(), term_names.c_str()); } } const string& PolicyStatement::name() const { return _name; } bool PolicyStatement::accept(Visitor& v) { return v.visit(*this); } PolicyStatement::TermContainer& PolicyStatement::terms() { return _terms; } void PolicyStatement::set_dependency(const DEPS& sets, const DEPS& policies) { // delete dependencies del_dependencies(); // replace them _sets = sets; _policies = policies; // re-insert dependencies for (DEPS::iterator i = _sets.begin(); i != _sets.end(); ++i) _smap.add_dependency(*i, _name); for (DEPS::iterator i = _policies.begin(); i != _policies.end(); ++i) _pmap.add_dependency(*i, _name); } void PolicyStatement::del_dependencies() { // remove all dependencies for (DEPS::iterator i = _sets.begin(); i != _sets.end(); ++i) _smap.del_dependency(*i, _name); for (DEPS::iterator i = _policies.begin(); i != _policies.end(); ++i) _pmap.del_dependency(*i, _name); _sets.clear(); } bool PolicyStatement::term_exists(const string& name) const { if((get_term_iter(name) == _terms.end()) && (find_out_of_order_term(name) == _out_of_order_terms.end())) { return false; } return true; } list >::iterator PolicyStatement::find_out_of_order_term(const ConfigNodeId& order) { list >::iterator iter; for (iter = _out_of_order_terms.begin(); iter != _out_of_order_terms.end(); ++iter) { const ConfigNodeId& list_order = iter->first; if (list_order.unique_node_id() == order.unique_node_id()) return (iter); } return (_out_of_order_terms.end()); } list >::iterator PolicyStatement::find_out_of_order_term(const string& name) { list >::iterator iter; for (iter = _out_of_order_terms.begin(); iter != _out_of_order_terms.end(); ++iter) { const Term* term = iter->second; if (term->name() == name) return (iter); } return (_out_of_order_terms.end()); } list >::const_iterator PolicyStatement::find_out_of_order_term(const string& name) const { list >::const_iterator iter; for (iter = _out_of_order_terms.begin(); iter != _out_of_order_terms.end(); ++iter) { const Term* term = iter->second; if (term->name() == name) return (iter); } return (_out_of_order_terms.end()); } xorp/policy/term.cc0000664000076400007640000002231611421137511014435 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #include "policy_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "policy/common/policy_utils.hh" #include "term.hh" #include "parser.hh" using namespace policy_utils; Term::Term(const string& name) : _name(name), _source_nodes(_block_nodes[SOURCE]), _dest_nodes(_block_nodes[DEST]), _action_nodes(_block_nodes[ACTION]) { for(unsigned int i = 0; i < LAST_BLOCK; i++) _block_nodes[i] = new Nodes; } Term::~Term() { for(unsigned int i = 0; i < LAST_BLOCK; i++) { clear_map_container(*_block_nodes[i]); delete _block_nodes[i]; list >::iterator iter; for (iter = _out_of_order_nodes[i].begin(); iter != _out_of_order_nodes[i].end(); ++iter) { delete iter->second; } } } void Term::set_term_end() { uint32_t i; for (i = 0; i < LAST_BLOCK; i++) { set_block_end(i); } } void Term::set_block(const uint32_t& block, const ConfigNodeId& order, const string& statement) { if (block >= LAST_BLOCK) { xorp_throw(term_syntax_error, "Unknown block: " + to_str(block)); } // check if we want to delete if (statement.empty()) { del_block(block, order); return; } // check that position is empty... // if it's not delete and then add... This usually occurs when "replacing" // an existing statement. like: // localpref: 101 // setting localpref: 102 in same node will cause this... Nodes& conf_block = *_block_nodes[block]; if ((conf_block.find(order) != conf_block.end()) || (find_out_of_order_node(block, order) != _out_of_order_nodes[block].end())) { debug_msg("[POLICY] Deleting previous statement...\n"); del_block(block, order); #if 0 // // XXX: don't throw an error if a previous statement is in this // position. Given that currently, say, same "nexthop4" // rtrmgr policy template node exists in more than one template // (bgp.tp, ospfv2.tp, rip.tp), we could receive three times same // XRL to set same block. The reason for this is because each // *.tp "nexthop4" entry incremantally adds same "%set xrl" action. // The alternative solution would be to avoid node/action duplication // in the rtrmgr templates. // xorp_throw(term_syntax_error, "A statement is already present in position: " + to_str(order)); #endif // 0 } debug_msg("[POLICY] Statement=(%s)\n", statement.c_str()); // parse and check syntax error Parser parser; // cast should be safe... because of check earlier in method. Parser::Nodes* nodes = parser.parse(static_cast(block), statement); if (!nodes) { string err = parser.last_error(); // XXX convert block from int to string... [human readable] xorp_throw(term_syntax_error, "Syntax error in term " + _name + " block " + block2str(block) + " statement=(" + statement + "): " + err); } XLOG_ASSERT(nodes->size() == 1); // XXX a single statement! pair res; res = conf_block.insert(order, nodes->front()); if (res.second != true) { // // Failed to add the entry, probably because it was received out of // order. Add it to the list of entries that need to be added later. // _out_of_order_nodes[block].push_back(make_pair(order, nodes->front())); return; } // // Try to add any entries that are out of order. // Note that we need to keep trying traversing the list until // no entry is added. // while (true) { bool entry_added = false; list >::iterator iter; for (iter = _out_of_order_nodes[block].begin(); iter != _out_of_order_nodes[block].end(); ++iter) { res = conf_block.insert(iter->first, iter->second); if (res.second == true) { // Entry added successfully entry_added = true; _out_of_order_nodes[block].erase(iter); break; } } if (! entry_added) break; } } void Term::del_block(const uint32_t& block, const ConfigNodeId& order) { XLOG_ASSERT (block < LAST_BLOCK); Nodes& conf_block = *_block_nodes[block]; Nodes::iterator i = conf_block.find(order); if (i != conf_block.end()) { conf_block.erase(i); return; } // Try to delete from the list of out-of-order nodes list >::iterator iter; iter = find_out_of_order_node(block, order); if (iter != _out_of_order_nodes[block].end()) { _out_of_order_nodes[block].erase(iter); return; } #if 0 // // XXX: don't throw an error if no previous statement is in this // position. Given that currently, say, same "nexthop4" // rtrmgr policy template node exists in more than one template // (bgp.tp, ospfv2.tp, rip.tp), we could receive three times same // XRL to delete same block. The reason for this is because each // *.tp "nexthop4" entry incremantally adds same "%delete xrl" action. // The alternative solution would be to avoid node/action duplication // in the rtrmgr templates. // xorp_throw(term_syntax_error, "Want to delete an empty position: " + order.str()); #endif // 0 } void Term::set_block_end(uint32_t block) { if (block >= LAST_BLOCK) { xorp_throw(term_syntax_error, "Unknown block: " + to_str(block)); } Nodes& conf_block = *_block_nodes[block]; // // Add all remaining entries that are out of order // while (! _out_of_order_nodes[block].empty()) { list >::iterator iter; pair res; // // Try to add as much as possible out-of-order entries // while (true) { bool entry_added = false; for (iter = _out_of_order_nodes[block].begin(); iter != _out_of_order_nodes[block].end(); ++iter) { res = conf_block.insert(iter->first, iter->second); if (res.second == true) { // Entry added successfully entry_added = true; _out_of_order_nodes[block].erase(iter); break; } } if (! entry_added) break; } // // If there are any remaining entries, then add the first at the // end of the block. After that try to add the remaining entries // in the proper order. // // XXX: Note that we need this mechanism in case there were some // holes in the order of received entries. E.g., if the rtrmgr // template file contained leaf nodes without XRLs, those nodes // will have a node ID that may place them in the middle of the // ordered set of leaf nodes. Such nodes won't be received, hence // we need this mechanism to add the nodes after the hole. // However, this mechanism may still add some of the nodes in // the wrong order. For example, lets say the original configuration // contained the following nodes, and that (H) was a leaf node // without an XRL: // A B C (H) E F G // We will correctly order the nodes as "A B C E F G". // However, lets say a new node D is added to the configuration: // A B C (H) D E F G // When node D is received, it will look like an out-of-order node, // and this algorithm will add it to the end. I.e., the result // will be "A B C E F G D". // Fortunately, this appears to be an issue only for leaf nodes // (to be more specific, only for nodes that are not multi-value // nodes), which, arguably, should not need node IDs. // At least, in case of the policy manager, the order of the // statements inside a policy block shouldn't matter. // if (! _out_of_order_nodes[block].empty()) { iter = _out_of_order_nodes[block].begin(); // XXX: we ignore the return result, because we should fail // to insert only if the node was added already. conf_block.insert_out_of_order(iter->first, iter->second); _out_of_order_nodes[block].erase(iter); break; } } } list >::iterator Term::find_out_of_order_node(const uint32_t& block, const ConfigNodeId& order) { list >::iterator iter; XLOG_ASSERT (block < LAST_BLOCK); for (iter = _out_of_order_nodes[block].begin(); iter != _out_of_order_nodes[block].end(); ++iter) { const ConfigNodeId& list_order = iter->first; if (list_order.unique_node_id() == order.unique_node_id()) return (iter); } return (_out_of_order_nodes[block].end()); } string Term::block2str(uint32_t block) { switch (block) { case SOURCE: return "source"; case DEST: return "dest"; case ACTION: return "action"; default: return "UNKNOWN"; } } xorp/policy/yacc.yy_policy_parser.cc.h0000664000076400007640000000217111421137511020223 0ustar greearbgreearb#ifndef YYERRCODE #define YYERRCODE 256 #endif #define YY_BOOL 257 #define YY_INT 258 #define YY_UINT 259 #define YY_UINTRANGE 260 #define YY_STR 261 #define YY_ID 262 #define YY_IPV4 263 #define YY_IPV4RANGE 264 #define YY_IPV4NET 265 #define YY_IPV6 266 #define YY_IPV6RANGE 267 #define YY_IPV6NET 268 #define YY_SEMICOLON 269 #define YY_LPAR 270 #define YY_RPAR 271 #define YY_ASSIGN 272 #define YY_SET 273 #define YY_REGEX 274 #define YY_ACCEPT 275 #define YY_REJECT 276 #define YY_PROTOCOL 277 #define YY_NEXT 278 #define YY_POLICY 279 #define YY_PLUS_EQUALS 280 #define YY_MINUS_EQUALS 281 #define YY_TERM 282 #define YY_NOT 283 #define YY_AND 284 #define YY_XOR 285 #define YY_OR 286 #define YY_HEAD 287 #define YY_CTR 288 #define YY_NE_INT 289 #define YY_EQ 290 #define YY_NE 291 #define YY_LE 292 #define YY_GT 293 #define YY_LT 294 #define YY_GE 295 #define YY_IPNET_EQ 296 #define YY_IPNET_LE 297 #define YY_IPNET_GT 298 #define YY_IPNET_LT 299 #define YY_IPNET_GE 300 #define YY_ADD 301 #define YY_SUB 302 #define YY_MUL 303 typedef union { char* c_str; Node* node; BinOper* op; } YYSTYPE; extern YYSTYPE yy_policy_parserlval; xorp/policy/visitor_test.hh0000664000076400007640000000525311421137511016237 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/policy/visitor_test.hh,v 1.4 2008/10/02 21:58:02 bms Exp $ #ifndef __POLICY_VISITOR_TEST_HH__ #define __POLICY_VISITOR_TEST_HH__ #include "configuration.hh" class VisitorTest : public Visitor { public: VisitorTest(SetMap& sm, PolicyMap& pm, VarMap& vm, const RATTR& attr, RATTR& mods); ~VisitorTest(); bool accepted(); const Element* visit(NodeUn&); const Element* visit(NodeBin&); const Element* visit(NodeVar&); const Element* visit(NodeAssign&); const Element* visit(NodeSet&); const Element* visit(NodeAccept&); const Element* visit(NodeReject&); const Element* visit(Term&); const Element* visit(PolicyStatement&); const Element* visit(NodeElem&); const Element* visit(NodeProto&); const Element* visit(NodeNext&); const Element* visit(NodeSubr& node); private: typedef set TRASH; typedef NodeNext::Flow Flow; typedef VarRW::Id Id; typedef VarMap::Variable Variable; enum Outcome { DEFAULT, ACCEPT, REJECT }; void trash_add(Element*); const Element* do_policy_statement(PolicyStatement& ps); const Element* do_bin(const Element& left, const Element& right, const BinOper& op); const Element& read(const string& id); void write(const string& id, const Element& e); void change_protocol(const string& protocol); Id var2id(const string& var); const Variable& var2variable(const string& var); bool match(const Element* e); SetMap& _sm; PolicyMap& _pm; VarMap& _vm; bool _finished; VarRW* _varrw; Dispatcher _disp; TRASH _trash; Outcome _outcome; Flow _flow; string _protocol; string _current_protocol; RATTR& _mod; ElementFactory _ef; }; #endif // __POLICY_VISITOR_TEST_HH__ xorp/policy/source_match_code_generator.cc0000664000076400007640000001636011540225533021210 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "policy_module.h" #include "libxorp/xorp.h" #include "source_match_code_generator.hh" SourceMatchCodeGenerator::SourceMatchCodeGenerator(uint32_t tagstart, const VarMap& varmap, PolicyMap& pmap, map > & ptags) : CodeGenerator(varmap, pmap), _currtag(tagstart), _protocol_tags(ptags) { } const Element* SourceMatchCodeGenerator::visit_policy(PolicyStatement& policy) { PolicyStatement::TermContainer& terms = policy.terms(); _policy = policy.name(); // go through all the terms for (PolicyStatement::TermContainer::iterator i = terms.begin(); i != terms.end(); ++i) { Term* term = i->second; term->accept(*this); } // Maybe we got called from subr. If so, we're not a protocol statement // (maybe one of our internal statements was though). Reset it. _protocol_statement = false; if (_subr) return NULL; // mark the end for all policies for (CodeMap::iterator i = _codes.begin(); i != _codes.end(); ++i) { Code* c = (*i).second; c->add_code("POLICY_END\n"); // for all subroutines too for (SUBR::const_iterator j = c->subr().begin(); j != c->subr().end();) { string x = j->second; x += "POLICY_END\n"; string p = j->first; j++; c->add_subr(p, x); } _codes_vect.push_back(c); } return NULL; } void SourceMatchCodeGenerator::addTerm() { // copy the code for the term Code* term = new Code(); term->set_target_protocol(_protocol); term->set_target_filter(filter::EXPORT_SOURCEMATCH); term->set_referenced_set_names(_code.referenced_set_names()); // see if we have code for this target already CodeMap::iterator i = _codes.find(_protocol); // if so link it if (i != _codes.end()) { Code* existing = (*i).second; // link "raw" code string s = _os.str(); if (_subr) { SUBR::const_iterator j = existing->subr().find(_policy); XLOG_ASSERT(j != existing->subr().end()); term->add_subr(_policy, (j->second) + s); } else term->set_code(s); *existing += *term; delete term; return; } XLOG_ASSERT(!_policy.empty()); // code for a new target, need to create policy start header. string s = "POLICY_START " + _policy + "\n" + _os.str(); if (_subr) term->add_subr(_policy, s); else term->set_code(s); _codes[_protocol] = term; } const Element* SourceMatchCodeGenerator::visit_term(Term& term) { // reset code and sets _os.str(""); _code.clear_referenced_set_names(); // make sure the source of the term has something [non empty source] if (term.source_nodes().size()) { do_term(term); // term may be for a new target, so deal with that. addTerm(); } else _tags.push_back(Taginfo(false, _currtag)); return NULL; } void SourceMatchCodeGenerator::do_term(Term& term) { Term::Nodes& source = term.source_nodes(); Term::Nodes::iterator i; _os << "TERM_START " << term.name() << endl ; _protocol = ""; // // XXX: Generate first the source block for the protocol statement, // because the protocol value is needed by the processing of other // statements. // for(i = source.begin(); i != source.end(); ++i) { if ((i->second)->is_protocol_statement()) { (i->second)->accept(*this); term.set_from_protocol(_protocol); } } // // Generate the remaining code for the source block // for(i = source.begin(); i != source.end(); ++i) { if ((i->second)->is_protocol_statement()) { // XXX: the protocol statement was processes above continue; } _protocol_statement = false; (i->second)->accept(*this); // if it was a protocol statement, no need for "ONFALSE_EXIT", if its // any other statement, then yes. The protocol is not read as a variable // by the backend filters... it is only used by the policy manager. if(!_protocol_statement) _os << "ONFALSE_EXIT" << endl; } // XXX: we can assume _protocol = PROTOCOL IN EXPORT STATEMENT if(_protocol == "") xorp_throw(NoProtoSpec, "No protocol specified in term " + term.name() + " in export policy source match"); // ignore any destination block [that is dealt with in the export code // generator] // If subroutine, do actions. Tags are set by caller. if (_subr) { for (Term::Nodes::iterator i = term.action_nodes().begin(); i != term.action_nodes().end(); ++i) { Node* n = i->second; n->accept(*this); } return; } // // As an action, store policy tags... // XXX: Note that we store the policy tags only if the route // doesn't carry some other protocol's tags. // _tags.push_back(Taginfo(true, _currtag)); _protocol_tags[_protocol].insert(_currtag); // Create the set of the tags known (so far) to belong to this protocol ElemSetU32 element_set; const set& protocol_tags = _protocol_tags[_protocol]; for (set::const_iterator iter = protocol_tags.begin(); iter != protocol_tags.end(); ++iter) { ElemU32 e(*iter); element_set.insert(e); } // Check that the route's tags are subset of this protocol's tags _os << "PUSH set_u32 " << element_set.str() << endl; _os << "LOAD " << (int)(VarRW::VAR_POLICYTAGS) << "\n"; _os << "<=\n"; _os << "ONFALSE_EXIT" << endl; // Add another tag to the route _os << "PUSH u32 " << _currtag << endl; _os << "LOAD " << (int)(VarRW::VAR_POLICYTAGS) << "\n"; _os << "+\n"; _os << "STORE " << (int)(VarRW::VAR_POLICYTAGS) << "\n"; _os << "TERM_END\n"; // FIXME: integer overflow _currtag++; } const Element* SourceMatchCodeGenerator::visit_proto(NodeProto& node) { // check for protocol redifinition if(_protocol != "") { ostringstream err; err << "PROTOCOL REDEFINED FROM " << _protocol << " TO " << node.proto() << " AT LINE " << node.line(); xorp_throw(ProtoRedefined, err.str()); } // define protocol _protocol = node.proto(); _protocol_statement = true; return NULL; } vector& SourceMatchCodeGenerator::codes() { return _codes_vect; } const SourceMatchCodeGenerator::Tags& SourceMatchCodeGenerator::tags() const { return _tags; } uint32_t SourceMatchCodeGenerator::next_tag() const { return _currtag; } const string& SourceMatchCodeGenerator::protocol() { return _protocol; } xorp/policy/node.hh0000664000076400007640000001724111540225532014431 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/policy/node.hh,v 1.17 2008/10/02 21:57:58 bms Exp $ #ifndef __POLICY_NODE_HH__ #define __POLICY_NODE_HH__ #include "policy/common/operator_base.hh" #include "policy/common/varrw.hh" #include "node_base.hh" #include "visitor.hh" /** * @short A generic node wrapper. */ template class NodeAny : public Node { public: /** * @param val the value of the node. * @param line the line of the configuration where the node was created. */ NodeAny(const T& val, unsigned line) : Node(line), _val(val) {} // semicolon for kdoc DEFINE_VISITABLE(); /** * @return the value of the node. */ const T& val() const { return _val; } private: T _val; }; /** * A node that holds a variable [such as aspath]. */ typedef NodeAny NodeVar; /** * @short A node which holds an element. * * Such as an IP address. */ class NodeElem : public NONCOPYABLE, public Node { public: /** * The node owns the element. Caller must not modify / delete. * * @param elem the element to hold. * @param line line of configuration where node was created. */ NodeElem(Element* elem, unsigned line) : Node(line), _elem(elem) {} ~NodeElem() { delete _elem; } DEFINE_VISITABLE(); /** * @return the element in this node. */ const Element& val() const { return *_elem; } private: Element* _elem; }; /** * @short A node which holds a set. * * The set name is only stored, as the SetMap is used for dereferencing. */ class NodeSet : public Node { public: /** * @param c_str the name of the set. * @param line line of configuration where node was created. */ NodeSet(const char* c_str, unsigned line) : Node(line), _setid(c_str) {} DEFINE_VISITABLE(); /** * @return the name of the set. */ const string& setid() const { return _setid; } private: string _setid; }; /** * @short A node for a binary operation. * * The node will thus have two children. It owns both of them. * */ class NodeBin : public NONCOPYABLE, public Node { public: /** * Caller must not delete / modify pointers. * * @param op binary operation of node. * @param left first argument of operation. * @param right second argument of operation. * @param line line where node was created. */ NodeBin(BinOper* op, Node* left, Node* right, unsigned line) : Node(line), _op(op), _left(left), _right(right) {} ~NodeBin() { delete _op; delete _left; delete _right; } DEFINE_VISITABLE(); /** * @return operation associated with node. */ const BinOper& op() const { return *_op; } /** * @return first argument of operation. */ Node& left() const { return *_left; } /** * @return second argument of operation. */ Node& right() const { return *_right; } private: BinOper* _op; Node *_left; Node *_right; }; /** * @short Unary operation. * * The node will have one child. It owns it. */ class NodeUn : public NONCOPYABLE, public Node { public: /** * Caller must not delete / modify pointers. * * @param op unary operation associated with node. * @param node child of node -- argument of operation. * @param line line of configuration where node was created. */ NodeUn(UnOper* op, Node* node, unsigned line) : Node(line), _op(op), _node(node) {} ~NodeUn() { delete _op; delete _node; } DEFINE_VISITABLE(); /** * @return unary operation associated with node. */ const UnOper& op() const { return *_op; } /** * @return argument of unary operation. */ Node& node() const { return *_node; } private: UnOper* _op; Node* _node; }; /** * @short An assignment operation. */ class NodeAssign : public NONCOPYABLE, public Node { public: /** * Caller must not delete / modify pointer. * * @param varid the name of the variable being assigned to. * @param mod the modifier (e.g., += has a modifier OpAdd). * @param rvalue the expression being assigned to the variable. * @param line line of configuration where node was created. */ NodeAssign(const string& varid, BinOper* mod, Node* rvalue, unsigned line) : Node(line), _varid(varid), _mod(mod), _rvalue(rvalue) {} ~NodeAssign() { delete _rvalue; delete _mod; } DEFINE_VISITABLE(); /** * @return name of variable being assigned to. */ const string& varid() const { return _varid; } /** * @return argument of assignment. */ Node& rvalue() const { return *_rvalue; } BinOper* mod() const { return _mod; } private: string _varid; BinOper* _mod; Node* _rvalue; }; /** * @short Node representing an accept statement. */ class NodeAccept : public Node { public: /** * @param line line of configuration where node was created. */ NodeAccept(unsigned line) : Node(line) {} DEFINE_VISITABLE(); /** * Test whether this is "accept" or "reject" statement. * * @return true if this is "accept" or "reject" statement. */ virtual bool is_accept_or_reject() const { return (true); } }; /** * @short Node representing a reject statement. */ class NodeReject : public Node { public: /** * @param line line of configuration where node was created. */ NodeReject(unsigned line) : Node(line) {} DEFINE_VISITABLE(); /** * Test whether this is "accept" or "reject" statement. * * @return true if this is "accept" or "reject" statement. */ virtual bool is_accept_or_reject() const { return (true); } }; /** * @short Node representing a protocol statement. */ class NodeProto : public Node { public: /** * @param protocol the protocol of the statement. * @param line line of configuration where node was created. */ NodeProto(const string& proto, unsigned line) : Node(line), _proto(proto) {} DEFINE_VISITABLE(); /** * Test whether this is a "protocol" statement. * * @return true if this is a "protocol" statement. */ virtual bool is_protocol_statement() const { return (true); } /** * @return the protocol being referenced. */ const string& proto() const { return _proto; } private: string _proto; }; class NodeNext : public Node { public: enum Flow { POLICY = 0, TERM }; NodeNext(unsigned line, Flow f) : Node(line), _flow(f) {} DEFINE_VISITABLE(); Flow flow() const { return _flow; } private: Flow _flow; }; class NodeSubr : public Node { public: NodeSubr(unsigned line, string policy) : Node(line), _policy(policy) {} DEFINE_VISITABLE(); string policy() const { return _policy; } private: string _policy; }; #endif // __POLICY_NODE_HH__ xorp/policy/process_watch_base.hh0000664000076400007640000000273611540224233017342 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/policy/process_watch_base.hh,v 1.8 2008/10/02 21:58:00 bms Exp $ #ifndef __POLICY_PROCESS_WATCH_BASE_HH__ #define __POLICY_PROCESS_WATCH_BASE_HH__ /** * @short Base class for a process watcher. * * The VarMap registers interest in known protocols. Finally, the filter manager * may be informed about which processes are alive. */ class ProcessWatchBase { public: virtual ~ProcessWatchBase() {} /** * @param proto protocol to register interest in. */ virtual void add_interest(const string& proto) = 0; }; #endif // __POLICY_PROCESS_WATCH_BASE_HH__ xorp/policy/filter_manager_base.hh0000664000076400007640000000344711421137511017455 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/policy/filter_manager_base.hh,v 1.8 2008/10/02 21:57:58 bms Exp $ #ifndef __POLICY_FILTER_MANAGER_BASE_HH__ #define __POLICY_FILTER_MANAGER_BASE_HH__ #include "code.hh" /** * @short Base class for a FilterManager. * * A filter manager is the entitiy which interacts with the actual policy * filters in protocols and the rib. * * It is used by the configuration class to trigger updates * */ class FilterManagerBase { public: virtual ~FilterManagerBase() {} /** * Update a specific policy filter. * * @param t The target to update [protocol/filter pair]. * */ virtual void update_filter(const Code::Target& t) = 0; /** * Commit all updates after msec milliseconds. * * @param msec Milliseconds after which all updates should be commited. * */ virtual void flush_updates(uint32_t msec) = 0; }; #endif // __POLICY_FILTER_MANAGER_BASE_HH__ xorp/policy/policy.l0000664000076400007640000001165611421137511014640 0ustar greearbgreearb%{ #include #include #include #include "policy_module.h" #include "libxorp/xorp.h" #include "policy/common/policy_utils.hh" #include "policy_parser.hh" #include "yacc.yy_policy_parser.cc.h" #define yylval yy_policy_parserlval #define yyerror yy_policy_parsererror #define yyparse yy_policy_parserparse void yyerror(const char *m); extern int yyparse(void); using namespace policy_parser; // instantiate the globals here. vector* policy_parser::_parser_nodes; unsigned policy_parser::_parser_lineno; // try not to pollute namespace { string _last_error; Term::BLOCKS _block; } %} %option prefix="yy_policy_parser" %option outfile="lex.yy_policy_parser.cc" %option noyywrap %option nounput %option never-interactive %x STR RE_IPV4_BYTE 25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]? RE_IPV4 {RE_IPV4_BYTE}\.{RE_IPV4_BYTE}\.{RE_IPV4_BYTE}\.{RE_IPV4_BYTE} RE_IPV4_PREFIXLEN 3[0-2]|[0-2]?[0-9] RE_IPV4NET {RE_IPV4}\/{RE_IPV4_PREFIXLEN} RE_H4 [a-fA-F0-9]{1,4} RE_H4_COLON {RE_H4}: RE_LS32 (({RE_H4}:{RE_H4})|{RE_IPV4}) RE_IPV6_P1 {RE_H4_COLON}{6}{RE_LS32} RE_IPV6_P2 ::{RE_H4_COLON}{5}{RE_LS32} RE_IPV6_P3 ({RE_H4})?::{RE_H4_COLON}{4}{RE_LS32} RE_IPV6_P4 ({RE_H4_COLON}{0,1}{RE_H4})?::{RE_H4_COLON}{3}{RE_LS32} RE_IPV6_P5 ({RE_H4_COLON}{0,2}{RE_H4})?::{RE_H4_COLON}{2}{RE_LS32} RE_IPV6_P6 ({RE_H4_COLON}{0,3}{RE_H4})?::{RE_H4_COLON}{1}{RE_LS32} RE_IPV6_P7 ({RE_H4_COLON}{0,4}{RE_H4})?::{RE_LS32} RE_IPV6_P8 ({RE_H4_COLON}{0,5}{RE_H4})?::{RE_H4} RE_IPV6_P9 ({RE_H4_COLON}{0,6}{RE_H4})?:: RE_IPV6 {RE_IPV6_P1}|{RE_IPV6_P2}|{RE_IPV6_P3}|{RE_IPV6_P4}|{RE_IPV6_P5}|{RE_IPV6_P6}|{RE_IPV6_P7}|{RE_IPV6_P8}|{RE_IPV6_P9} RE_IPV6_PREFIXLEN 12[0-8]|1[01][0-9]|[0-9][0-9]? RE_IPV6NET {RE_IPV6}\/{RE_IPV6_PREFIXLEN} %% [[:digit:]]+".."[[:digit:]]+ { yylval.c_str = strdup(yytext); return YY_UINTRANGE; } [[:digit:]]+ { yylval.c_str = strdup(yytext); return YY_UINT; } -[[:digit:]]+ { yylval.c_str = strdup(yytext); return YY_INT; } "true" { yylval.c_str = strdup(yytext); return YY_BOOL; } "false" { yylval.c_str = strdup(yytext); return YY_BOOL; } \"|\' BEGIN(STR); \"|\' BEGIN(INITIAL); [^\"\']+ { yylval.c_str = strdup(yytext); _parser_lineno += policy_utils::count_nl(yytext); /* XXX: a string can be started with " but terminated with ' * and vice versa... */ return YY_STR; } {RE_IPV4}".."{RE_IPV4} { yylval.c_str = strdup(yytext); return YY_IPV4RANGE; } {RE_IPV4} { yylval.c_str = strdup(yytext); return YY_IPV4; } {RE_IPV4NET} { yylval.c_str = strdup(yytext); return YY_IPV4NET; } {RE_IPV6}".."{RE_IPV6} { yylval.c_str = strdup(yytext); return YY_IPV6RANGE; } {RE_IPV6} { yylval.c_str = strdup(yytext); return YY_IPV6; } {RE_IPV6NET} { yylval.c_str = strdup(yytext); return YY_IPV6NET; } ":" { // the colon is an alias for asignment in action and equality // in the source / dest blocks. if (_block == Term::ACTION) return YY_ASSIGN; else return YY_EQ; } "(" return YY_LPAR; ")" return YY_RPAR; "==" return YY_EQ; "!=" return YY_NE; "<=" return YY_LE; ">=" return YY_GE; "<" return YY_LT; ">" return YY_GT; "+" return YY_ADD; "*" return YY_MUL; "\-" return YY_SUB; "=" return YY_ASSIGN; "+=" return YY_PLUS_EQUALS; "-=" return YY_MINUS_EQUALS; "||" return YY_OR; "&&" return YY_AND; "!" return YY_NOT; "exact" return YY_IPNET_EQ; "longer" return YY_IPNET_LT; "shorter" return YY_IPNET_GT; "orlonger" return YY_IPNET_LE; "orshorter" return YY_IPNET_GE; "and" return YY_AND; "or" return YY_OR; "xor" return YY_XOR; "not" return YY_NOT; "add" return YY_PLUS_EQUALS; "sub" return YY_MINUS_EQUALS; "head" return YY_HEAD; "ctr" return YY_CTR; "ne_int" return YY_NE_INT; "accept" return YY_ACCEPT; "reject" return YY_REJECT; "SET" return YY_SET; "REGEX" return YY_REGEX; "protocol" return YY_PROTOCOL; "next" return YY_NEXT; "policy" return YY_POLICY; "term" return YY_TERM; [[:alpha:]][[:alnum:]_-]* { yylval.c_str = strdup(yytext); return YY_ID; } ; return YY_SEMICOLON; [[:blank:]]+ /* eat blanks */ "\n" _parser_lineno++; . { yyerror("Unknown character"); } %% void yyerror(const char *m) { ostringstream oss; oss << "Error on line " << _parser_lineno << " near ("; for(int i = 0; i < yyleng; i++) oss << yytext[i]; oss << "): " << m; _last_error = oss.str(); } // Everything is put in the lexer because of YY_BUFFER_STATE... int policy_parser::policy_parse(vector& outnodes, const Term::BLOCKS& block, const string& conf, string& outerr) { YY_BUFFER_STATE yybuffstate = yy_scan_string(conf.c_str()); _last_error = "No error"; _parser_nodes = &outnodes; _parser_lineno = 1; _block = block; int res = yyparse(); yy_delete_buffer(yybuffstate); outerr = _last_error; return res; } xorp/policy/term.hh0000664000076400007640000001106711540225533014454 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/policy/term.hh,v 1.18 2008/10/02 21:58:01 bms Exp $ #ifndef __POLICY_TERM_HH__ #define __POLICY_TERM_HH__ #include "libproto/config_node_id.hh" #include "policy/common/policy_exception.hh" #include "node_base.hh" /** * @short A term is an atomic policy unit. * * It is a complete specification of how a route needs to be matched, and what * actions must be taken. */ class Term : public NONCOPYABLE { public: enum BLOCKS { SOURCE = 0, DEST, ACTION, // keep this last LAST_BLOCK }; // the integer is the "line number", the node is the parsed structure [AST] // of the statement(s) in that line. typedef ConfigNodeIdMap Nodes; /** * @short Exception thrown on a syntax error while parsing configuration. */ class term_syntax_error : public PolicyException { public: term_syntax_error(const char* file, size_t line, const string& init_why = "") : PolicyException("term_syntax_error", file, line, init_why) {} }; /** * @param name term name. */ Term(const string& name); ~Term(); /** * @return name of the term. */ const string& name() const { return _name; } /** * Perform operations at the end of the term. */ void set_term_end(); /** * Updates the source/dest/action block of a term. * * @param block the block to update (0:source, 1:dest, 2:action). * @param order node ID with position of term. * @param statement the statement to insert. */ void set_block(const uint32_t& block, const ConfigNodeId& order, const string& statement); /** * Deletes statements in the location specified by order and block. * * @param block the block to update (0:source, 1:dest, 2:action). * @param order node ID with position of term. */ void del_block(const uint32_t& block, const ConfigNodeId& order); /** * Perform operations at the end of the block. * * @param block the block to perform operations on * (0:source, 1:dest, 2:action). */ void set_block_end(uint32_t block); /** * Visitor implementation. * * @param v visitor used to visit this term. */ const Element* accept(Visitor& v) { return v.visit(*this); } /** * @return parse tree of source block. */ Nodes& source_nodes() { return *_source_nodes; } /** * @return parse tree of dest block. */ Nodes& dest_nodes() { return *_dest_nodes; } /** * @return parse tree of action block. */ Nodes& action_nodes() { return *_action_nodes; } /** * Convert block number to human readable form. * * @param num the block number. * @return human readable representation of block name. */ static string block2str(uint32_t num); /** * Get the protocol name (in the "from" block). * * @return the protocol name (in the "from" block) if set, otherwise * an empty string. */ const string& from_protocol() const { return (_from_protocol); } /** * Set the protocol name (in the "from" block). * * @param v the protocol name (in the "from" block). */ void set_from_protocol(const string& v) { _from_protocol = v; } private: list >::iterator find_out_of_order_node( const uint32_t& block, const ConfigNodeId& order); string _name; Nodes* _block_nodes[3]; list > _out_of_order_nodes[3]; Nodes*& _source_nodes; Nodes*& _dest_nodes; Nodes*& _action_nodes; string _from_protocol; // The protocol (in the "from" block) }; #endif // __POLICY_TERM_HH__ xorp/policy/set_map.cc0000664000076400007640000001026311421137511015114 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "policy_module.h" #include "libxorp/xorp.h" #include "set_map.hh" const Element& SetMap::getSet(const string& name) const { return _deps.find(name); } void SetMap::create(const string& name) { // initially, set is empty [null object is fine]. Element* e = NULL; if(!_deps.create(name,e)) xorp_throw(SetMapError, "Can't create set " + name + " : exists"); } void SetMap::update_set(const string& type, const string& name, const string& elements, set& modified) { // create the object, _deps will own it... Element* e = _ef.create(type, elements.c_str()); // see affected policies _deps.get_deps(name, modified); // replace with new set _deps.update_object(name, e); } void SetMap::delete_set(const string& name) { _deps.remove(name); } void SetMap::add_to_set(const string& type, const string& name, const string& element, set& modified) { Element* e = _deps.find_ptr(name); // Find the element if (e == NULL) { // First element to the set update_set(type, name, element, modified); return; } // Check the element type if (type != string(e->type())) { string error_msg = c_format("Can't add to set %s: type mismatch " "(received %s expected %s)", name.c_str(), type.c_str(), e->type()); xorp_throw(SetMapError, error_msg); } // Get a string with the existing elements and add the new element string elements = e->str(); if (! elements.empty()) elements += ","; elements += element; update_set(type, name, elements, modified); } void SetMap::delete_from_set(const string& type, const string& name, const string& element, set& modified) { Element* e = _deps.find_ptr(name); // Find the element if (e == NULL) { string error_msg = c_format("Can't delete from set %s: not found", name.c_str()); xorp_throw(SetMapError, error_msg); return; } // Check the element type if (type != string(e->type())) { string error_msg = c_format("Can't delete from set %s: type mismatch " "(received %s expected %s)", name.c_str(), type.c_str(), e->type()); xorp_throw(SetMapError, error_msg); return; } // Delete element Element* base = _ef.create(type, element.c_str()); ElemSet* del = dynamic_cast(base); ElemSet* eset = dynamic_cast(e); if (eset != NULL && del != NULL) { eset->erase(*del); } delete base; // sort out dependencies _deps.get_deps(name, modified); } void SetMap::add_dependency(const string& setname, const string& policyname) { _deps.add_dependency(setname,policyname); } void SetMap::del_dependency(const string& setname, const string& policyname) { _deps.del_dependency(setname,policyname); } string SetMap::str() const { Dep::Map::const_iterator i = _deps.get_iterator(); string ret; while (_deps.has_next(i)) { Dep::ObjPair op(_deps.next(i)); ret += op.name + ": "; ret += op.object.str(); ret += "\n"; } return ret; } void SetMap::sets_by_type(SETS& s, const string& type) const { Dep::Map::const_iterator i = _deps.get_iterator(); while (_deps.has_next(i)) { Dep::ObjPair op(_deps.next(i)); const Element* e = &op.object; if (type.compare(e->type()) == 0) s.push_back(op.name); } } xorp/policy/policy_map.cc0000664000076400007640000000500511421137511015616 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "policy_module.h" #include "libxorp/xorp.h" #include "visitor_printer.hh" #include "policy_map.hh" PolicyStatement& PolicyMap::find(const string& name) const { return _deps.find(name); } bool PolicyMap::exists(const string& name) { return _deps.exists(name); } void PolicyMap::create(const string& name,SetMap& smap) { PolicyStatement* ps = new PolicyStatement(name, smap, *this); if (!_deps.create(name,ps)) { delete ps; xorp_throw(PolicyMapError, "Can't create policy " + name + " : already exists"); } } void PolicyMap::delete_policy(const string& name) { _deps.remove(name); } void PolicyMap::add_dependency(const string& policyname, const string& protocol) { _deps.add_dependency(policyname,protocol); } void PolicyMap::del_dependency(const string& policyname, const string& protocol) { _deps.del_dependency(policyname,protocol); } string PolicyMap::str() { ostringstream out; VisitorPrinter printer(out); // go through all policies and print them Dep::Map::const_iterator i = _deps.get_iterator(); while (_deps.has_next(i)) { Dep::ObjPair p = _deps.next(i); // XXX hack! lame! [anyway this is only for debug] string policyname = p.name; printer.visit(find(policyname)); } return out.str(); } void PolicyMap::policy_deps(const string& policy, DEPS& deps) { // XXX we mix protocol names and policy names =( DEPS tmp; _deps.get_deps(policy, tmp); for (DEPS::iterator i = tmp.begin(); i != tmp.end(); ++i) { const string& name = *i; if (exists(name)) deps.insert(name); } } void PolicyMap::policies(KEYS& out) { _deps.keys(out); } xorp/policy/protocol_map.cc0000664000076400007640000000374011421137511016164 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "policy_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "protocol_map.hh" ProtocolMap::ProtocolMap() { } const string& ProtocolMap::xrl_target(const string& protocol) { Map::iterator i = _map.find(protocol); // by default, the protocol has the same XRL target name. if (i == _map.end()) { set_xrl_target(protocol, protocol); i = _map.find(protocol); XLOG_ASSERT(i != _map.end()); } return i->second; } void ProtocolMap::set_xrl_target(const string& protocol, const string& target) { _map[protocol] = target; } const string& ProtocolMap::protocol(const string& target) { // XXX lame for (Map::iterator i = _map.begin(); i != _map.end(); ++i) { string& t = i->second; if (target == t) return i->first; } // by default protocol = target // The case in which a protocol called target exists is probably bad... XLOG_ASSERT(_map.find(target) == _map.end()); set_xrl_target(target, target); return protocol(target); // an assert that item was added would be good, in // order to avoid infinite recursion... } xorp/policy/BUGS0000664000076400007640000000073311421137511013641 0ustar greearbgreearb* How do we ad the varmap ? * What happens if Source match is up, but export is not!!!!!! * rip v6/v4 are different processes... rib will send route to any process... dunno * Sets: escape comma escape quote in instructions etc. * bgp check if route going through pipeline before doing a policy push * push route races... e.g stuff going from X->Y will cause X and Y to push, so same route may be "added" from X and repushed from Y inside of Y. May cause problems. xorp/policy/code_generator.cc0000664000076400007640000001415511421137511016450 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "policy_module.h" #include "libxorp/xorp.h" #include "code_generator.hh" #include "policy_map.hh" CodeGenerator::CodeGenerator(const VarMap& varmap, PolicyMap& pmap) : _varmap(varmap), _pmap(pmap), _subr(false) { } CodeGenerator::CodeGenerator(const string& proto, const filter::Filter& filter, const VarMap& varmap, PolicyMap& pmap) : _varmap(varmap), _pmap(pmap), _subr(false) { _protocol = proto; _code.set_target_protocol(proto); _code.set_target_filter(filter); } // constructor for import policies CodeGenerator::CodeGenerator(const string& proto, const VarMap& varmap, PolicyMap& pmap) : _varmap(varmap), _pmap(pmap), _subr(false) { _protocol = proto; _code.set_target_protocol(proto); _code.set_target_filter(filter::IMPORT); } CodeGenerator::~CodeGenerator() { } const Element* CodeGenerator::visit_policy(PolicyStatement& policy) { PolicyStatement::TermContainer& terms = policy.terms(); // go through all the terms for (PolicyStatement::TermContainer::iterator i = terms.begin(); i != terms.end(); ++i) { (i->second)->accept(*this); } ostringstream oss; oss << "POLICY_START " << policy.name() << endl; oss << _os.str(); oss << "POLICY_END" << endl; _code.set_code(oss.str()); return NULL; } const Element* CodeGenerator::visit_term(Term& term) { Term::Nodes& source = term.source_nodes(); Term::Nodes& dest = term.dest_nodes(); Term::Nodes& actions = term.action_nodes(); Term::Nodes::iterator i; _os << "TERM_START " << term.name() << endl ; // do the source block for(i = source.begin(); i != source.end(); ++i) { (i->second)->accept(*this); _os << "ONFALSE_EXIT" << endl; } // Import policies should not have a dest block if(!dest.empty()) { xorp_throw(CodeGeneratorErr, "Term " + term.name() + " has a dest part!"); } // // Do the action block. // XXX: We generate last the code for the "accept" or "reject" statements. // for(i = actions.begin(); i != actions.end(); ++i) { if ((i->second)->is_accept_or_reject()) continue; (i->second)->accept(*this); } for(i = actions.begin(); i != actions.end(); ++i) { if ((i->second)->is_accept_or_reject()) (i->second)->accept(*this); } _os << "TERM_END\n"; return NULL; } const Element* CodeGenerator::visit(NodeUn& node) { node.node().accept(*this); _os << node.op().str() << endl; return NULL; } const Element* CodeGenerator::visit(NodeBin& node) { // reverse order, so they can be popped in correct order node.right().accept(*this); node.left().accept(*this); _os << node.op().str() << endl; return NULL; } const Element* CodeGenerator::visit(NodeAssign& node) { node.rvalue().accept(*this); VarRW::Id id = _varmap.var2id(protocol(), node.varid()); // XXX backend should have specialized operators for performance reasons. // For now we just expand expressions such as "a += b" into "a = a + b" in // the frontend. // -sorbo if (node.mod()) { _os << "LOAD " << id << endl; _os << node.mod()->str() << endl; } _os << "STORE " << id << endl; return NULL; } const Element* CodeGenerator::visit(NodeElem& node) { _os << "PUSH " << node.val().type() << " " << "\"" << node.val().str() << "\"" << endl; return NULL; } const Element* CodeGenerator::visit(NodeVar& node) { VarRW::Id id = _varmap.var2id(protocol(), node.val()); _os << "LOAD " << id << endl; return NULL; } const Element* CodeGenerator::visit(NodeSet& node) { _os << "PUSH_SET " << node.setid() << endl; _code.add_referenced_set_name(node.setid()); return NULL; } const Element* CodeGenerator::visit(NodeAccept& /* node */) { _os << "ACCEPT" << endl; return NULL; } const Element* CodeGenerator::visit(NodeReject& /* node */) { _os << "REJECT" << endl; return NULL; } const Element* CodeGenerator::visit_proto(NodeProto& node) { ostringstream err; // import policies may not have protocol set. err << "INVALID protocol statement in line " << node.line() << endl; xorp_throw(CodeGeneratorErr, err.str()); } const Code& CodeGenerator::code() { return _code; } const Element* CodeGenerator::visit(PolicyStatement& ps) { return visit_policy(ps); } const Element* CodeGenerator::visit(Term& term) { return visit_term(term); } const Element* CodeGenerator::visit(NodeProto& proto) { return visit_proto(proto); } const string& CodeGenerator::protocol() { return _protocol; } const Element* CodeGenerator::visit(NodeNext& next) { _os << "NEXT "; switch (next.flow()) { case NodeNext::POLICY: _os << "POLICY"; break; case NodeNext::TERM: _os << "TERM"; break; } _os << endl; return NULL; } const Element* CodeGenerator::visit(NodeSubr& node) { string policy = node.policy(); PolicyStatement& ps = _pmap.find(policy); string tmp = _os.str(); _os.clear(); _os.str(""); bool subr = _subr; _subr = true; visit(ps); _subr = subr; string code = _code.code(); _code.add_subr(policy, code); _os.clear(); _os.str(""); _os << tmp; _os << "POLICY " << policy << endl; return NULL; } xorp/policy/code_generator.hh0000664000076400007640000000742511540224233016464 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/policy/code_generator.hh,v 1.12 2008/10/02 21:57:57 bms Exp $ #ifndef __POLICY_CODE_GENERATOR_HH__ #define __POLICY_CODE_GENERATOR_HH__ #include "policy/common/varrw.hh" #include "policy/common/policy_exception.hh" #include "var_map.hh" #include "visitor.hh" #include "code.hh" #include "policy_statement.hh" #include "node.hh" /** * @short Generic code generator. It is suitable for import filters. * * This class visits a structure of Nodes and generates appropriate code. */ class CodeGenerator : public Visitor { public: /** * @short Exception thrown if code generation fails. * * This may occur for example, if an import policy has a dest part. The * semantic check should however get rid of all errors. */ class CodeGeneratorErr : public PolicyException { public: CodeGeneratorErr(const char* file, size_t line, const string& init_why = "") : PolicyException("CodeGeneratorErr", file, line, init_why) {} }; // used by source match code generator. CodeGenerator(const VarMap& varmap, PolicyMap& pmap); /** * Generate code for a specific protocol and filter [target] * * This construct is mainly used by derived classes to set the code target. * * @param proto target protocol. * @param filter target filter type. * @param varmap varmap. */ CodeGenerator(const string& proto, const filter::Filter& filter, const VarMap& varmap, PolicyMap& pmap); /** * Initialize code generation for an import of a specific protocol. * * @param proto target protocol. * @param varmap varmap. */ CodeGenerator(const string& proto, const VarMap& varmap, PolicyMap& pmap); virtual ~CodeGenerator(); const Element* visit(NodeUn& node); const Element* visit(NodeBin& node); const Element* visit(NodeAssign& node); const Element* visit(NodeElem& node); const Element* visit(NodeVar& node); const Element* visit(NodeSet& node); const Element* visit(NodeAccept& node); const Element* visit(NodeReject& node); const Element* visit(PolicyStatement& policy); const Element* visit(Term& policy); const Element* visit(NodeProto& policy); const Element* visit(NodeNext& node); const Element* visit(NodeSubr& node); /** * @return code generated. */ const Code& code(); protected: // may not overload virtual functions =( // yes it is a triple dispatch... but we will get there eventually =D virtual const Element* visit_policy(PolicyStatement& policy); virtual const Element* visit_term(Term& term); virtual const Element* visit_proto(NodeProto& node); virtual const string& protocol(); Code _code; ostringstream _os; const VarMap& _varmap; PolicyMap& _pmap; bool _subr; private: string _protocol; }; #endif // __POLICY_CODE_GENERATOR_HH__ xorp/policy/filter_manager.hh0000664000076400007640000001442411540224233016460 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/policy/filter_manager.hh,v 1.10 2008/10/02 21:57:58 bms Exp $ #ifndef __POLICY_FILTER_MANAGER_HH__ #define __POLICY_FILTER_MANAGER_HH__ #include "policy/common/policy_exception.hh" #include "policy/common/filter.hh" #include "xrl/interfaces/policy_backend_xif.hh" #include "xrl/interfaces/rib_xif.hh" #include "libxorp/eventloop.hh" #include "code.hh" #include "set_map.hh" #include "process_watch.hh" #include "filter_manager_base.hh" #include "pw_notifier.hh" #include "protocol_map.hh" /** * @short Deals with sending code to policy filters. * * It manages sending the code and sets to the filters in the protocols. It also * updates the policy-tag-map in the RIB. * * It also keeps track if protocols die / come back to life, throw the process * watch in order to re-send code to filters which were dead and are now back. */ class FilterManager : public FilterManagerBase, public PWNotifier { public: // XXX: pull this out ? typedef map CodeMap; typedef map ConfQueue; typedef set TagSet; typedef map TagMap; /** * @short Exception thrown on error. Such as xrl failure. */ class FMException : public PolicyException { public: FMException(const char* file, size_t line, const string& init_why = "") : PolicyException("FMException", file, line, init_why) {} }; /** * The FilterManager closely works with the Configuration class. Maybe in * the future holding a reference the Configuration class, instead of the * internal components may be a better solution. * * @param imp import filter CodeMap to use. * @param sm source match filter CodeMap to use. * @param exp export filter CodeMap to use. * @param sets SetMap to use. * @param tagmap TagMap to use. * @param rtr the XRL router used by the policy process. * @param pw the process watcher. * @param pmap the protocol map. */ FilterManager(const CodeMap& imp, const CodeMap& sm, const CodeMap& exp, const SetMap& sets, const TagMap& tagmap, XrlStdRouter& rtr, ProcessWatch& pw, ProtocolMap& pmap); /** * Update the filter for a specific target. This will normally queue a * filter configuration request. * * @param t target which should be updated. */ void update_filter(const Code::Target& t); /** * Xrl callback for all XRL requests. * * @param e possible XRL error. */ void policy_backend_cb(const XrlError& e); /** * Flushes the route pushing queue. */ void push_routes_now(); /** * Flush all queues now */ void flush_updates_now(); /** * Flush all updates after msec milliseconds. * * If a new update comes in before msec expires, only the new update will be * performed. * * @param msec milliseconds after which all queues should be flushed. */ void flush_updates(uint32_t msec); // PWNotifier interface: /** * A protocol just came back to life. * * @param protocol name of protocol which is alive. */ void birth(const string& protocol); /** * A protocol just died. * * @param protocol name of protocol which died. */ void death(const string& protocol); private: /** * Update the import filter for a specific protocol. * * @param protocol protocol of which the import filter must be updated. */ void update_import_filter(const string& protocol); /** * Update the source-match filter for a specific protocol. * * @param protocol protocol of which the sourcematch filter must be updated. */ void update_sourcematch_filter(const string& protocol); /** * Update the export filter for a specific protocol. * * @param protocol protocol of which the export filter must be updated. */ void update_export_filter(const string& protocol); /** * Update the policy-tag map in the RIB for a specific protocol. * * @param protocol protocol for which tags should be updated. */ void update_tagmap(const string& protocol); /** * Flush all updates for export filters. Also keep track which protocols * must have their routes pushed. */ void flush_export_queue(); /** * Flush all updates for a specific filter-type queue. Record which * protocols need to be pushed. * * @param queue queue for which updates need to be flushed. * @param f filter for which updates should be flushed. */ void flush_queue(ConfQueue& queue, filter::Filter f); void delete_queue_protocol(ConfQueue& queue, const string& protocol); void update_queue(const string& protocol, const CodeMap& cm, ConfQueue& queue); const CodeMap& _import; const CodeMap& _sourcematch; const CodeMap& _export; const SetMap& _sets; const TagMap& _tagmap; ConfQueue _import_queue; ConfQueue _sourcematch_queue; ConfQueue _export_queue; set _push_queue; EventLoop& _eventloop; // we should have a timer per protocol. XorpTimer _flush_timer; XorpTimer _push_timer; unsigned _push_timeout; ProcessWatch& _process_watch; XrlPolicyBackendV0p1Client _policy_backend; XrlRibV0p1Client _rib; string _rib_name; ProtocolMap& _pmap; }; #endif // __POLICY_FILTER_MANAGER_HH__ xorp/policy/policy_module.h0000664000076400007640000000234211421137511016171 0ustar greearbgreearb/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- */ /* vim:set sts=4 ts=8: */ /* * Copyright (c) 2001-2009 XORP, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 2, June * 1991 as published by the Free Software Foundation. Redistribution * and/or modification of this program under the terms of any other * version of the GNU General Public License is not permitted. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, * see the GNU General Public License, Version 2, a copy of which can be * found in the XORP LICENSE.gpl file. * * XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; * http://xorp.net */ /* * $XORP: xorp/policy/policy_module.h,v 1.12 2008/10/02 21:57:59 bms Exp $ */ #ifndef __POLICY_POLICY_MODULE_H__ #define __POLICY_POLICY_MODULE_H__ #ifndef XORP_MODULE_NAME #define XORP_MODULE_NAME "POLICY" #endif #ifndef XORP_MODULE_VERSION #define XORP_MODULE_VERSION "0.1" #endif #endif /* __POLICY_MODULE_MODULE_H__ */ xorp/policy/test_varrw.cc0000664000076400007640000000246511421137511015671 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "policy_module.h" #include "libxorp/xorp.h" #include "test_varrw.hh" #include "common/policy_exception.hh" const Element& TestVarRW::read(const Id& id) { ELEM::iterator i = _elem.find(id); if (i == _elem.end()) xorp_throw(PolicyException, "Reading uninitialized attribute"); const Element* e = i->second; return *e; } void TestVarRW::write(const Id& id, const Element& elem) { _elem[id] = &elem; } xorp/policy/visitor_semantic.hh0000664000076400007640000000726511540225533017074 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/policy/visitor_semantic.hh,v 1.17 2008/10/02 21:58:02 bms Exp $ #ifndef __POLICY_VISITOR_SEMANTIC_HH__ #define __POLICY_VISITOR_SEMANTIC_HH__ #include "libxorp/xorp.h" #include "policy/common/varrw.hh" #include "policy/common/dispatcher.hh" #include "visitor.hh" #include "semantic_varrw.hh" #include "set_map.hh" #include "policy_statement.hh" #include "node.hh" /** * @short A policy semantic checker. * * A policy is instantiated by a protocol and policytype. Thus, semantic * checking must be performed realtive to the instantiation. [Generic semantic * checking may be accomplished too, but it is not done.] */ class VisitorSemantic : public NONCOPYABLE, public Visitor { public: enum PolicyType { IMPORT, EXPORT }; /** * @short Exception thrown on a semantic error */ class sem_error : public PolicyException { public: sem_error(const char* file, size_t line, const string& init_why = "") : PolicyException("sem_error", file, line, init_why) {} }; /** * @param varrw semantic VarRW used to simulate a protocol. * @param varmap the varmap. * @param setmap the SetMap to check if sets exist. * @param pmap the policy map to check subroutines. * @param protocol the protocol which instantiates the policy. * @param ptype the type of policy [import/export]. */ VisitorSemantic(SemanticVarRW& varrw, VarMap& varmap, SetMap& setmap, PolicyMap& pmap, const string& protocol, PolicyType ptype); const Element* visit(PolicyStatement& policy); const Element* visit(Term& term); const Element* visit(NodeUn& node); const Element* visit(NodeBin& node); const Element* visit(NodeAssign& node); const Element* visit(NodeVar& node); const Element* visit(NodeSet& node); const Element* visit(NodeElem& node); const Element* visit(NodeAccept& node); const Element* visit(NodeReject& node); const Element* visit(NodeProto& node); const Element* visit(NodeNext& node); const Element* visit(NodeSubr& node); /** * @return sets used by the policy. * */ const set& sets() const { return _sets; } private: void change_protocol(const string& proto); const string& semantic_protocol(); const Element* do_bin(const Element& left, const Element& right, const BinOper& op, const Node& from); void do_policy_statement(PolicyStatement& ps); SemanticVarRW& _varrw; VarMap& _varmap; SetMap& _setmap; PolicyMap& _pmap; Dispatcher _disp; set _sets; string _protocol; string _current_protocol; string _semantic_protocol; PolicyType _ptype; set _trash; bool _reject; }; #endif // __POLICY_VISITOR_SEMANTIC_HH__ xorp/policy/var_map.cc0000664000076400007640000001233111421137511015107 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #include "policy_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "var_map.hh" #include "policy/common/policy_utils.hh" #include "policy/common/element_factory.hh" using namespace policy_utils; const VarMap::VariableMap& VarMap::variablemap(const string& protocol) const { ProtoMap::const_iterator i = _protocols.find(protocol); if(i == _protocols.end()) xorp_throw(VarMapErr, "Unknown protocol: " + protocol); const VariableMap* vm = (*i).second; return *vm; } const VarMap::Variable& VarMap::variable(const string& protocol, const VarRW::Id& varname) const { const VariableMap& vmap = variablemap(protocol); VariableMap::const_iterator i = vmap.find(varname); if(i == vmap.end()) { ostringstream oss; oss << "Unknown variable: " << varname << " in protocol " << protocol; xorp_throw(VarMapErr, oss.str()); } const Variable* v = (*i).second; return *v; } VarMap::VarMap(ProcessWatchBase& pw) : _process_watch(pw) { add_metavariable(new Variable("trace", "u32", WRITE, VarRW::VAR_TRACE)); add_metavariable(new Variable("tag", "u32", READ_WRITE, VarRW::VAR_TAG)); } VarMap::~VarMap() { for(ProtoMap::iterator i = _protocols.begin(); i != _protocols.end(); ++i) { VariableMap* vm = (*i).second; clear_map(*vm); } clear_map(_protocols); clear_map(_metavars); } bool VarMap::protocol_known(const string& protocol) { return _protocols.find(protocol) != _protocols.end(); } void VarMap::add_variable(VariableMap& vm, Variable* var) { VariableMap::iterator i = vm.find(var->id); if(i != vm.end()) { // XXX: if the same variable already exists, then return silently Variable* old_var = i->second; if (*old_var == *var) return; ostringstream oss; oss << "Variable " << var->id << " exists already"; delete var; xorp_throw(VarMapErr, oss.str()); } vm[var->id] = var; } void VarMap::add_protocol_variable(const string& protocol, Variable* var) { debug_msg("[POLICY] VarMap adding proto: %s, var: %s, type: %s, R/W: %d, ID: %d\n", protocol.c_str(), var->name.c_str(), var->type.c_str(), var->access, var->id); if (!ElementFactory::can_create(var->type)) { ostringstream oss; oss << "Unable to create element of type: " << var->type << " in proto: " << protocol << " varname: " << var->name; delete var; xorp_throw(VarMapErr, oss.str()); } ProtoMap::iterator iter = _protocols.find(protocol); VariableMap* vm; // if no variablemap exists for the protocol exists, create one if(iter == _protocols.end()) { vm = new VariableMap(); _protocols[protocol] = vm; _process_watch.add_interest(protocol); // add the metavars for (MetaVarContainer::iterator i = _metavars.begin(); i != _metavars.end(); ++i) { Variable* v = i->second; add_variable(*vm, new Variable(*v)); } } // or else just update existing one else vm = (*iter).second; add_variable(*vm, var); } void VarMap::add_metavariable(Variable* v) { if (_metavars.find(v->id) != _metavars.end()) { ostringstream oss; oss << "Metavar: " << v->id << " exists already" << endl; delete v; xorp_throw(VarMapErr, oss.str()); } _metavars[v->id] = v; } string VarMap::str() { ostringstream out; // go through protocols for (ProtoMap::iterator i = _protocols.begin(); i != _protocols.end(); ++i) { const string& proto = i->first; VariableMap* vm = i->second; for(VariableMap::iterator j = vm->begin(); j != vm->end(); ++j) { Variable* v = j->second; out << proto << " " << v->name << " " << v->type << " "; if(v->access == READ) out << "r"; else out << "rw"; out << endl; } } return out.str(); } VarRW::Id VarMap::var2id(const string& protocol, const string& varname) const { ProtoMap::const_iterator i = _protocols.find(protocol); if (i == _protocols.end()) xorp_throw(VarMapErr, "Unknown protocol: " + protocol); const VariableMap* vm = i->second; // XXX slow & lame. Semantic checking / compilation will be slow. for (VariableMap::const_iterator j = vm->begin(); j != vm->end(); ++j) { const Variable* v = j->second; if (v->name == varname) return v->id; } xorp_throw(VarMapErr, "Unknown variable: " + varname); } xorp/policy/code.cc0000664000076400007640000000652311540224232014401 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "policy_module.h" #include "libxorp/xorp.h" #include "policy/common/policy_utils.hh" #include "code.hh" bool Code::Target::operator<(const Target& rhs) const { // XXX: I think XOR will do the trick [filter number / protocol] string left, right; left = _protocol + policy_utils::to_str((int)(_filter)); right = rhs._protocol + policy_utils::to_str((int)(rhs._filter)); return left < right; } bool Code::Target::operator==(const Target& rhs) const { if (_protocol != rhs._protocol) return false; if (_filter != rhs._filter) return false; return true; } bool Code::Target::operator!=(const Target& rhs) const { return !(*this == rhs); } string Code::Target::str() const { string ret = "Protocol: "; ret += _protocol; ret += ", Filter: "; ret += filter2str(_filter); return ret; } void Code::set_target_protocol(const string& protocol) { _target.set_protocol(protocol); } void Code::set_target_filter(const filter::Filter& filter) { _target.set_filter(filter); } string Code::str() { string ret = "TARGET proto: " + _target.protocol(); ret += " FILTER: "; ret += filter2str(_target.filter()); ret += "\nCODE:\n"; ret += _code; ret += "SETS:"; for (set::iterator i = _referenced_set_names.begin(); i != _referenced_set_names.end(); ++i) { ret += " " + *i; } ret += "\n"; return ret; } Code& Code::operator+=(const Code& rhs) { // may only add for same target if (_target != rhs._target) return *this; // do nothing // add the code [link it] _code += rhs._code; // add any new sets. for (set::const_iterator i = rhs._referenced_set_names.begin(); i != rhs._referenced_set_names.end(); ++i) { _referenced_set_names.insert(*i); } // add tags for (TagSet::const_iterator i = rhs._all_tags.begin(); i != rhs._all_tags.end(); ++i) { _all_tags.insert(*i); } for (TagSet::const_iterator i = rhs._redist_tags.begin(); i != rhs._redist_tags.end(); ++i) { _redist_tags.insert(*i); } // add protos for (set::const_iterator i = rhs._source_protocols.begin(); i != rhs._source_protocols.end(); ++i) { _source_protocols.insert(*i); } // add subroutines _subr.insert(rhs._subr.begin(), rhs._subr.end()); return *this; } void Code::add_subr(const string& policy, const string& code) { _subr[policy] = code; } const SUBR& Code::subr() const { return _subr; } xorp/policy/policy_parser.hh0000664000076400007640000000374711540224233016362 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/policy/policy_parser.hh,v 1.8 2008/10/02 21:57:59 bms Exp $ #ifndef __POLICY_POLICY_PARSER_HH__ #define __POLICY_POLICY_PARSER_HH__ #include "node.hh" #include "term.hh" /** * @short Minimises global namespace pollution of yacc/lex variables. * * The nature of lex and yacc causes global variables / functions to be present. * Here such methods and functions are grouped under one namespace. */ namespace policy_parser { /** * Parser a policy. * * Caller is responsible for deleting nodes created from a partial parse due to * errors. * * @return 0 on success. * @param outnodes where parse tree will be stored. * @param block the policy block [source, action, dest] which is being parsed. * @param conf configuration to parse. * @param outerr on error, this buffer will be filled with an error message. */ int policy_parse(vector& outnodes, const Term::BLOCKS& block, const string& conf, string& outerr); // THESE SHOULD NOT BE TOUCHED! extern vector* _parser_nodes; extern unsigned _parser_lineno; } // namespace #endif // __POLICY_POLICY_PARSER_HH__ xorp/cleanup_xrp.bash0000775000076400007640000000037611425061724015052 0ustar greearbgreearb#!/bin/bash # Try to initialize the system before we start doing xorp buildbot # builds and tests. # Kill all existing xorp processes killall -9 -r xorp # Remove tmp files rm -fr /var/tmp/xrl.* || exit 1 # Remove build directory rm -fr obj exit 0 xorp/site_scons/0000775000076400007640000000000011421137511014025 5ustar greearbgreearbxorp/site_scons/site_tools/0000775000076400007640000000000011421141062016205 5ustar greearbgreearbxorp/site_scons/site_tools/autotest.pyc0000664000076400007640000000703611421141062020600 0ustar greearbgreearbÑò I¿DLc@s€ddkTddklZddkZdeiifd„ƒYZeiieƒdd„Z gd„Z d„Z d „Z dS( iÿÿÿÿ(t*(tBuilderNtToolAutoTestWarningcBseZRS((t__name__t __module__(((sG/home/greearb/git/xorp.ct.github/xorp/site_scons/site_tools/autotest.pyR&scCs2x+|D]}|i|ƒo ||SqW|SdS(sË Takes a list of dictionaries as its 1st argument. Checks if the key exists in each one and returns the 1st one it finds. If the key is found in no dictionaries, the default is returned. N(thas_key(tdictlisttkeytdefaulttdict((sG/home/greearb/git/xorp.ct.github/xorp/site_scons/site_tools/autotest.pytmultiget+s  cKs­tt||gdƒƒ}tt||gdƒƒ}tt||gdƒƒ}tt||gdƒƒ}tt||gdƒƒ}|} |idƒoHt|dƒi} | g} |D]} | | id| ƒq¾~ 7} n||d<||d<||d<| |d<||d<|i|d ||} t||gd tƒo#|i| d id d „ƒ}n|i| d iƒ}|i |d| |ƒ|i |dƒ| S(si Prepares the Program call arguments, calls Program and adds the result to the check target. tCCFLAGStCXXFLAGStLIBPATHtRPATHt LINKFLAGSt SHAREDLIBStBUILDDIRs $BUILDDIRtsourcetAUTOTEST_SKIP_ERRORSit exitstatfunccSsdS(i((tx((sG/home/greearb/git/xorp.ct.github/xorp/site_scons/site_tools/autotest.pytYstAUTOTEST_TARGET( tSplitR RtDirtabspathtreplacetProgramtFalsetActiontAliast AlwaysBuild(tenvttargetRtkwargstccflagstcxxflagstlibpathtrpatht linkflagstmyrpatht baserpatht_[1]Rttesttrunner((sG/home/greearb/git/xorp.ct.github/xorp/site_scons/site_tools/autotest.pyt _UnitTest7s*5     #cKsZ|iddƒ|idtƒt|id|ƒdd„}|idh|d6ƒdS( s8 Accepted keyword arguments: AUTOTEST_TARGET - the target to append the tests to. Default: check AUTOTEST_SKIP_ERRORS - set to True to continue running the next test if one test fails. Default: False ... and all others that Program() accepts, like CPPPATH etc. RtcheckRc[st|d|d||S(sWUsage: The function is modelled to be called as the Program() call is. R"R(R.(R!R"RR#((sG/home/greearb/git/xorp.ct.github/xorp/site_scons/site_tools/autotest.pytAutoTesttstBUILDERSR0N((t SetDefaultRtapplytReplacetNonetAppend(R!R#R0((sG/home/greearb/git/xorp.ct.github/xorp/site_scons/site_tools/autotest.pytgenerateas  cCstS(N(tTrue(R!((sG/home/greearb/git/xorp.ct.github/xorp/site_scons/site_tools/autotest.pytexists|s(t SCons.Scriptt SCons.BuilderRtostSConstWarningstWarningRtenableWarningClassR5R R.R7R9(((sG/home/greearb/git/xorp.ct.github/xorp/site_scons/site_tools/autotest.pyt!s   * xorp/site_scons/site_tools/tgtgen.pyc0000664000076400007640000000343411421141062020216 0ustar greearbgreearbÑò I¿DLc @sddkZddkZddkZeiidƒZd„ZeiidddgddƒZ ei i d ed dd ed e ƒZ d „Z d„Z dS(iÿÿÿÿNs>$TGTGEN $_TGTGEN_INCFLAGS --output-dir ${TARGET.dir} ${SOURCE}c Csptiit|dƒƒ\}}||idƒd}|d}|d}|d}|||g}||fS(Nit/is_base.ccs_base.hhs.xrls(tSConstUtiltsplitexttstrtrfind( ttargettsourcetenvtbasetexttccthhtxrlstt((sE/home/greearb/git/xorp.ct.github/xorp/site_scons/site_tools/tgtgen.pyttgtgen_emitters"   t TGTGENScans.tgts*.xiftTGTGEN_CPPPATHs5^[ ]*#[ ]*(?:include|import)[ ]*(<|")([^>"]+)(>|")tactiont src_suffixtemittertsource_scannercCs0t|dds         xorp/site_scons/site_tools/autotest.py0000664000076400007640000001047311421137511020440 0ustar greearbgreearb#!/usr/bin/env python # vim:set sts=4 ts=4 sw=4: # coding=UTF-8 # # Copyright (c) 2009 XORP, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # # $XORP$ # # Wrapper for legacy Automake-style regression tests. # # Loosely based on the CxxTest builder for SCons. # Usage is identical to the Program() builder. # # This script should be placed in a file called autotest.py, somewhere in the # scons toolpath. (default: #/site_scons/site_tools/) # from SCons.Script import * from SCons.Builder import Builder import os # A warning class to notify users of problems class ToolAutoTestWarning(SCons.Warnings.Warning): pass SCons.Warnings.enableWarningClass(ToolAutoTestWarning) def multiget(dictlist, key, default = None): """ Takes a list of dictionaries as its 1st argument. Checks if the key exists in each one and returns the 1st one it finds. If the key is found in no dictionaries, the default is returned. """ for dict in dictlist: if dict.has_key(key): return dict[key] else: return default def _UnitTest(env, target, source = [], **kwargs): """ Prepares the Program call arguments, calls Program and adds the result to the check target. """ # get the c and cxx flags to process. ccflags = Split( multiget([kwargs, env], 'CCFLAGS' )) cxxflags = Split( multiget([kwargs, env], 'CXXFLAGS')) libpath = Split( multiget([kwargs, env], 'LIBPATH')) rpath = Split( multiget([kwargs, env], 'RPATH')) linkflags = Split( multiget([kwargs, env], 'LINKFLAGS')) # For a test, take the our passed in LIBPATH, expand it, and prepend # to our passed in RPATH, so that tests can build using shared # libraries, even though they are not installed. # Tests are not intended to be installed, so we don't do any # further RPATH magic here. myrpath = rpath if env.has_key('SHAREDLIBS'): baserpath = Dir(env['BUILDDIR']).abspath myrpath += [ x.replace('$BUILDDIR', baserpath) for x in libpath ] # fill the flags into kwargs kwargs["CXXFLAGS"] = cxxflags kwargs["CCFLAGS"] = ccflags kwargs["LIBPATH"] = libpath kwargs["RPATH"] = myrpath kwargs["LINKFLAGS"] = linkflags # build the test program test = env.Program(target, source = source, **kwargs) # FIXME: Skip this step if we are cross-compiling (don't run the runner). if multiget([kwargs, env], 'AUTOTEST_SKIP_ERRORS', False): runner = env.Action(test[0].abspath, exitstatfunc=lambda x:0) else: runner = env.Action(test[0].abspath) env.Alias(env['AUTOTEST_TARGET'], test, runner) env.AlwaysBuild(env['AUTOTEST_TARGET']) return test def generate(env, **kwargs): """ Accepted keyword arguments: AUTOTEST_TARGET - the target to append the tests to. Default: check AUTOTEST_SKIP_ERRORS - set to True to continue running the next test if one test fails. Default: False ... and all others that Program() accepts, like CPPPATH etc. """ # # Expected behaviour: keyword arguments override environment variables; # environment variables override default settings. # env.SetDefault( AUTOTEST_TARGET = 'check' ) env.SetDefault( AUTOTEST_SKIP_ERRORS = False ) #Here's where keyword arguments are applied apply(env.Replace, (), kwargs) def AutoTest(env, target, source = None, **kwargs): """Usage: The function is modelled to be called as the Program() call is. """ return _UnitTest(env, target = target, source = source, **kwargs) env.Append( BUILDERS = { "AutoTest" : AutoTest } ) def exists(env): return True xorp/site_scons/site_tools/tgtgen.py0000664000076400007640000000230011421137511020046 0ustar greearbgreearbimport SCons.Action import SCons.Builder import SCons.Scanner tgtgen_action = SCons.Action.Action("$TGTGEN $_TGTGEN_INCFLAGS --output-dir ${TARGET.dir} ${SOURCE}") def tgtgen_emitter(target, source, env): base,ext = SCons.Util.splitext(str(source[0])) base = base[base.rfind("/") + 1:] cc = base + "_base.cc" hh = base + "_base.hh" xrls = base + ".xrls" t = [cc, hh, xrls] return (t, source) tgtgen_scanner = SCons.Scanner.ClassicCPP("TGTGENScan", [ ".tgt", "*.xif" ], "TGTGEN_CPPPATH", '^[ \t]*#[ \t]*(?:include|import)[ \t]*(<|")([^>"]+)(>|")') tgtgen_builder = SCons.Builder.Builder( action = tgtgen_action, src_suffix = ".tgt", emitter = tgtgen_emitter, source_scanner = tgtgen_scanner) def generate(env): """Add Builder for XRL targets to the Environment ...""" env['BUILDERS']['TGTGEN'] = tgtgen_builder env['TGTGEN'] = "$xorp_sourcedir/xrl/scripts/tgt-gen" env['TGTGEN_CPPPATH'] = [] env['_TGTGEN_INCFLAGS'] = '$( ${_concat(INCPREFIX, TGTGEN_CPPPATH, INCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)' def exists(env): return True xorp/site_scons/site_tools/clntgen.pyc0000664000076400007640000000260411421141062020356 0ustar greearbgreearbÑò I¿DLc @sjddkZddkZeiidƒZd„ZeiidedddeƒZd„Zd „Z dS( iÿÿÿÿNsC$CLNTGEN -I${TARGET.dir}/../.. --output-dir ${TARGET.dir} ${SOURCE}cCsctiit|dƒƒ\}}||idƒd}|d}|d}||g}||fS(Nit/is_xif.ccs_xif.hh(tSConstUtiltsplitexttstrtrfind(ttargettsourcetenvtbasetexttccthhtt((sF/home/greearb/git/xorp.ct.github/xorp/site_scons/site_tools/clntgen.pytclntgen_emitters "   tactiont src_suffixs.xiftemittercCst|dds      xorp/site_scons/site_tools/TOOL_SUBST.py0000664000076400007640000000564711421137511020334 0ustar greearbgreearbimport re from SCons.Script import * # the usual scons stuff you get in a SConscript def generate(env): """Adds SubstInFile builder, which substitutes the keys->values of SUBST_DICT from the source to the target. The values of SUBST_DICT first have any construction variables expanded (its keys are not expanded). If a value of SUBST_DICT is a python callable function, it is called and the result is expanded as the value. If there's more than one source and more than one target, each target gets substituted from the corresponding source. """ env.Append(TOOLS = 'SUBST') def do_subst_in_file(targetfile, sourcefile, dict): """Replace all instances of the keys of dict with their values. For example, if dict is {'%VERSION%': '1.2345', '%BASE%': 'MyProg'}, then all instances of %VERSION% in the file will be replaced with 1.2345 etc. """ try: f = open(sourcefile, 'rb') contents = f.read() f.close() except: raise SCons.Errors.UserError, "Can't read source file %s"%sourcefile for (k,v) in dict.items(): contents = re.sub(k, v, contents) try: f = open(targetfile, 'wb') f.write(contents) f.close() except: raise SCons.Errors.UserError, "Can't write target file %s"%targetfile return 0 # success def subst_in_file(target, source, env): if not env.has_key('SUBST_DICT'): raise SCons.Errors.UserError, "SubstInFile requires SUBST_DICT to be set." d = dict(env['SUBST_DICT']) # copy it for (k,v) in d.items(): if callable(v): d[k] = env.subst(v()) elif SCons.Util.is_String(v): d[k]=env.subst(v) else: raise SCons.Errors.UserError, "SubstInFile: key %s: %s must be a string or callable"%(k, repr(v)) for (t,s) in zip(target, source): return do_subst_in_file(str(t), str(s), d) def subst_in_file_string(target, source, env): """This is what gets printed on the console.""" return '\n'.join(['Substituting vars from %s into %s'%(str(s), str(t)) for (t,s) in zip(target, source)]) def subst_emitter(target, source, env): """Add dependency from substituted SUBST_DICT to target. Returns original target, source tuple unchanged. """ d = env['SUBST_DICT'].copy() # copy it for (k,v) in d.items(): if callable(v): d[k] = env.subst(v()) elif SCons.Util.is_String(v): d[k]=env.subst(v) Depends(target, SCons.Node.Python.Value(d)) return target, source subst_action=SCons.Action.Action(subst_in_file, subst_in_file_string) env['BUILDERS']['SubstInFile'] = Builder(action=subst_action, emitter=subst_emitter) def exists(env): return True xorp/site_scons/site_tools/TOOL_SUBST.pyc0000664000076400007640000000743111421141062020464 0ustar greearbgreearbÑò I¿DLc@s,ddkZddkTd„Zd„ZdS(iÿÿÿÿN(t*csp|iddƒd„‰‡fd†}d„}d„}tii||ƒ}td|d|ƒ|d d values of SUBST_DICT from the source to the target. The values of SUBST_DICT first have any construction variables expanded (its keys are not expanded). If a value of SUBST_DICT is a python callable function, it is called and the result is expanded as the value. If there's more than one source and more than one target, each target gets substituted from the corresponding source. tTOOLStSUBSTc SsÃy)t|dƒ}|iƒ}|iƒWntiid|‚nXx/|iƒD]!\}}ti|||ƒ}qSWy*t|dƒ}|i |ƒ|iƒWntiid|‚nXdS(sèReplace all instances of the keys of dict with their values. For example, if dict is {'%VERSION%': '1.2345', '%BASE%': 'MyProg'}, then all instances of %VERSION% in the file will be replaced with 1.2345 etc. trbsCan't read source file %stwbsCan't write target file %si( topentreadtclosetSConstErrorst UserErrortitemstretsubtwrite(t targetfilet sourcefiletdicttftcontentstktv((sI/home/greearb/git/xorp.ct.github/xorp/site_scons/site_tools/TOOL_SUBST.pytdo_subst_in_files    csý|idƒptiid‚nt|dƒ}xŠ|iƒD]|\}}t|ƒo|i|ƒƒ||s   Cxorp/site_scons/site_tools/clntgen.py0000664000076400007640000000133111421137511020213 0ustar greearbgreearbimport SCons.Action import SCons.Builder clntgen_action = SCons.Action.Action("$CLNTGEN -I${TARGET.dir}/../.. --output-dir ${TARGET.dir} ${SOURCE}") def clntgen_emitter(target, source, env): base,ext = SCons.Util.splitext(str(source[0])) base = base[base.rfind("/") + 1:] cc = base + "_xif.cc" hh = base + "_xif.hh" t = [cc, hh] return (t, source) clntgen_builder = SCons.Builder.Builder( action = clntgen_action, src_suffix = ".xif", emitter = clntgen_emitter) def generate(env): """Add Builder for XRL interfaces to the Environment ...""" env['BUILDERS']['CLNTGEN'] = clntgen_builder env['CLNTGEN'] = "$xorp_sourcedir/xrl/scripts/clnt-gen" def exists(env): return True xorp/site_scons/config/0000775000076400007640000000000011703351433015276 5ustar greearbgreearbxorp/site_scons/config/allconfig.py0000664000076400007640000011721611703345406017621 0ustar greearbgreearb# Copyright (c) 2009-2012 XORP, Inc and Others # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # $Id$ import sys import os import string from SCons.Script.SConscript import SConsEnvironment # TODO SCons support for headerfilename needs to be fixed at source-- # that would let us use confdefs.h for the include file header # conditionals, instead of having everything in one file like this. # # TODO Some of these checks should be fatal. # TODO Split this up into separate files. # Hint: Some tests depend on others. def DoAllConfig(env, conf, host_os): ########## # endian has_endian_h = conf.CheckHeader('endian.h'); if not has_endian_h: conf.CheckEndianness() # Bleh, FC8 era scons doesn't have this check. try: if not conf.CheckCC: print "\nERROR: Cannot find functional cc compiler." print " On Fedora/RedHat: yum install gcc" sys.exit(1); print "OK: c compiler appears functional."; if not conf.CheckCXX: print "\nERROR: Cannot find functional c++ compiler." print " On Fedora/RedHat: yum install gcc-g++" sys.exit(1); print "OK: C++ compiler appears functional."; except: print "NOTE: This version of scons cannot check for" print " existence of gcc and g++ compilers." print " Will assume the exist and function properly...\n" # Mingw/windows stuff has_iphlpapi_h = conf.CheckHeader(['winsock2.h', 'iphlpapi.h']) has_routprot_h = conf.CheckHeader('routprot.h') ########## # c99 has_stdint_h = conf.CheckHeader('stdint.h') has_inttypes_h = conf.CheckHeader('inttypes.h') for type in [ 'int8_t', 'uint8_t', 'int16_t', 'uint16_t', 'int32_t', 'uint32_t', 'int64_t', 'uint64_t' ]: includes = "" if has_inttypes_h: includes += '#include \n' if has_stdint_h: includes += '#include \n' conf.CheckType(type, includes) ########## # stdc has_stddef_h = conf.CheckHeader('stddef.h') has_stdarg_h = conf.CheckHeader('stdarg.h') has_stdlib_h = conf.CheckHeader('stdlib.h') has_strings_h = conf.CheckHeader('strings.h') has_string_h = conf.CheckHeader('string.h') has_signal_h = conf.CheckHeader('signal.h') has_math_h = conf.CheckHeader('math.h') # SUNWlibm has_memory_h = conf.CheckHeader('memory.h') # c90, libc: functions has_strftime = conf.CheckFunc('strftime') has_strlcpy = conf.CheckFunc('strlcpy') # not in glibc! has_strlcat = conf.CheckFunc('strlcat') # not in glibc! has_va_copy = conf.CheckDeclaration('va_copy', '#include ') if has_va_copy: conf.Define('HAVE_VA_COPY') # autoconf compat ########## # posix has_sys_types_h = conf.CheckHeader('sys/types.h') has_fcntl_h = conf.CheckHeader('fcntl.h') has_getopt_h = conf.CheckHeader('getopt.h') has_glob_h = conf.CheckHeader('glob.h') has_grp_h = conf.CheckHeader('grp.h') has_pthread_h = conf.CheckHeader('pthread.h') has_pwd_h = conf.CheckHeader('pwd.h') has_mqueue_h = conf.CheckHeader('mqueue.h') prereq_regex_h = [] if has_sys_types_h: prereq_regex_h.append('sys/types.h') has_regex_h = conf.CheckHeader(prereq_regex_h + ['regex.h']) has_syslog_h = conf.CheckHeader('syslog.h') has_termios_h = conf.CheckHeader('termios.h') has_time_h = conf.CheckHeader('time.h') has_unistd_h = conf.CheckHeader('unistd.h') has_vfork_h = conf.CheckHeader('vfork.h') # posix: function tests has_readv = conf.CheckFunc('readv') has_strerror = conf.CheckFunc('strerror') has_syslog = conf.CheckFunc('syslog') has_uname = conf.CheckFunc('uname') has_writev = conf.CheckFunc('writev') # may be in -lxnet on opensolaris has_libxnet = conf.CheckLib('xnet') has_recvmsg = conf.CheckFunc('recvmsg') has_sendmsg = conf.CheckFunc('sendmsg') # may be in -lrt has_librt = conf.CheckLib('rt') has_clock_gettime = conf.CheckFunc('clock_gettime') has_clock_monotonic = conf.CheckDeclaration('CLOCK_MONOTONIC', '#include ') if has_clock_monotonic: conf.Define('HAVE_CLOCK_MONOTONIC') # autoconf compat # BSD extension has_clock_monotonic_fast = conf.CheckDeclaration( 'CLOCK_MONOTONIC_FAST', '#include ') if has_clock_monotonic_fast: conf.Define('HAVE_CLOCK_MONOTONIC_FAST') has_struct_timespec = conf.CheckType('struct timespec', includes='#include ') ########## # bsd: headers has_paths_h = conf.CheckHeader('paths.h') has_sysexits_h = conf.CheckHeader('sysexits.h') # bsd: functions has_realpath = conf.CheckFunc('realpath') has_strptime = conf.CheckFunc('strptime') has_sysctl = conf.CheckFunc('sysctl') # bsd-style resolver: functions has_netdb_h = conf.CheckHeader('netdb.h') has_libresolv = conf.CheckLib('resolv') # opensolaris needs it has_hstrerror = conf.CheckFunc('hstrerror') ########## # unix: system headers has_sys_cdefs_h = conf.CheckHeader('sys/cdefs.h') has_sys_param_h = conf.CheckHeader('sys/param.h') has_sys_utsname_h = conf.CheckHeader('sys/utsname.h') has_sys_errno_h = conf.CheckHeader('sys/errno.h') has_sys_wait_h = conf.CheckHeader('sys/wait.h') has_sys_signal_h = conf.CheckHeader('sys/signal.h') has_sys_time_h = conf.CheckHeader('sys/time.h') has_sys_uio_h = conf.CheckHeader('sys/uio.h') has_sys_ioctl_h = conf.CheckHeader('sys/ioctl.h') has_sys_select_h = conf.CheckHeader('sys/select.h') has_sys_socket_h = conf.CheckHeader('sys/socket.h') has_sys_sockio_h = conf.CheckHeader('sys/sockio.h') has_sys_un_h = conf.CheckHeader('sys/un.h') prereq_sys_mount_h = [] if has_sys_types_h: prereq_sys_mount_h.append('sys/types.h') if has_sys_param_h: prereq_sys_mount_h.append('sys/param.h') has_sys_mount_h = conf.CheckHeader(prereq_sys_mount_h + ['sys/mount.h']) has_sys_resource_h = conf.CheckHeader('sys/resource.h') has_sys_stat_h = conf.CheckHeader('sys/stat.h') has_sys_syslog_h = conf.CheckHeader('sys/syslog.h') # bsd has_sys_linker_h = conf.CheckHeader(['sys/param.h', 'sys/linker.h']) has_sys_sysctl_h = conf.CheckHeader(['sys/param.h', 'sys/sysctl.h']) # linux has_linux_types_h = conf.CheckHeader('linux/types.h') has_linux_sockios_h = conf.CheckHeader('linux/sockios.h') # XXX needs header conditionals has_struct_iovec = conf.CheckType('struct iovec', includes='#include ') has_struct_msghdr = conf.CheckType('struct msghdr', includes='#include ') has_struct_cmsghdr = conf.CheckType('struct cmsghdr', includes='#include ') # Check for boost noncopyable include file. # This doesn't work..not too sure why. --Ben #has_boost_noncopyable_hpp = conf.CheckHeader('boost/noncopyable.hpp'); #if has_boost_noncopyable_hpp: # conf.Define('HAS_BOOST_NONCOPYABLE_INC') ########## # Socket support checks if (env.has_key('mingw') and env['mingw']): prereq_af_inet_includes = [ 'winsock2.h' ] else: prereq_af_inet_includes = [ 'sys/types.h', 'sys/socket.h' ] af_inet_includes = [] for s in prereq_af_inet_includes: af_inet_includes.append("#include <%s>\n" % s) af_inet_includes = string.join(af_inet_includes, '') has_af_inet = conf.CheckDeclaration('AF_INET', af_inet_includes) has_af_inet6 = conf.CheckDeclaration('AF_INET6', af_inet_includes) has_sock_stream = conf.CheckDeclaration('SOCK_STREAM', af_inet_includes) has_sock_dgram = conf.CheckDeclaration('SOCK_DGRAM', af_inet_includes) has_sock_raw = conf.CheckDeclaration('SOCK_RAW', af_inet_includes) if has_af_inet and has_sock_stream and has_sock_dgram: conf.Define('HAVE_TCPUDP_UNIX_SOCKETS') if has_af_inet and has_sock_raw: conf.Define('HAVE_IP_RAW_SOCKETS') # TODO: This needs to be properly detected. # TODO: This used to check for bsd and linux in an error prone # way. Now, do negative checks, but this could break Solaris # (or not..no idea if it supports raw or not). if not (env.has_key('mingw') and env['mingw']): conf.Define('IPV4_RAW_OUTPUT_IS_RAW') conf.Define('IPV4_RAW_INPUT_IS_RAW') if has_struct_msghdr: has_struct_msghdr_msg_control = conf.CheckTypeMember('struct msghdr', 'msg_control', includes='#include ') has_struct_msghdr_msg_iov = conf.CheckTypeMember('struct msghdr', 'msg_iov', includes='#include ') has_struct_msghdr_msg_name = conf.CheckTypeMember('struct msghdr', 'msg_name', includes='#include ') has_struct_msghdr_msg_namelen = conf.CheckTypeMember('struct msghdr', 'msg_namelen', includes='#include ') has_struct_sockaddr_sa_len = conf.CheckTypeMember('struct sockaddr', 'sa_len', includes='#include ') has_struct_sockaddr_storage_ss_len = conf.CheckTypeMember('struct sockaddr_storage', 'ss_len', includes='#include ') has_struct_sockaddr_un_sun_len = conf.CheckTypeMember('struct sockaddr_un', 'sun_len', includes='#include \n#include ') ########## # net stack has_net_ethernet_h = conf.CheckHeader(['sys/types.h', 'net/ethernet.h']) has_sys_ethernet_h = conf.CheckHeader('sys/ethernet.h') has_net_if_h = conf.CheckHeader(['sys/types.h', 'sys/ioctl.h', 'sys/socket.h', 'net/if.h']) has_net_if_arp_h = conf.CheckHeader(['sys/types.h', 'sys/ioctl.h', 'sys/socket.h', 'net/if.h', 'net/if_arp.h']) has_net_if_dl_h = conf.CheckHeader(['sys/types.h', 'net/if_dl.h']) has_net_if_ether_h = conf.CheckHeader(['sys/types.h', 'net/if.h', 'net/if_ether.h']) has_net_if_media_h = conf.CheckHeader(['sys/types.h', 'net/if_media.h']) has_net_if_var_h = conf.CheckHeader(['sys/types.h', 'sys/ioctl.h', 'sys/socket.h', 'net/if.h', 'net/if_var.h']) has_net_if_types_h = conf.CheckHeader('net/if_types.h') has_net_route_h = conf.CheckHeader(['sys/types.h', 'sys/ioctl.h', 'sys/socket.h', 'net/if.h', 'net/route.h']) has_ifaddrs_h = conf.CheckHeader(['sys/types.h', 'sys/socket.h', 'ifaddrs.h']) has_stropts_h = conf.CheckHeader('stropts.h') # Header file might need , , # and/or prereq_linux_ethtool_h = [] if has_inttypes_h: prereq_linux_ethtool_h.append('inttypes.h') if has_stdint_h: prereq_linux_ethtool_h.append('stdint.h') if has_linux_types_h: prereq_linux_ethtool_h.append('linux/types.h') has_linux_ethtool_h = conf.CheckHeader(prereq_linux_ethtool_h + ['linux/ethtool.h']) has_linux_if_tun_h = conf.CheckHeader('linux/if_tun.h') # Header file might need , , # and/or prereq_linux_netlink_h = [] if has_sys_types_h: prereq_linux_netlink_h.append('sys/types.h') if has_sys_socket_h: prereq_linux_netlink_h.append('sys/socket.h') if has_linux_types_h: prereq_linux_netlink_h.append('linux/types.h') has_linux_netlink_h = conf.CheckHeader(prereq_linux_netlink_h + ['linux/netlink.h']) # Header file might need , , # and/or prereq_linux_rtnetlink_h = [] if has_sys_types_h: prereq_linux_rtnetlink_h.append('sys/types.h') if has_sys_socket_h: prereq_linux_rtnetlink_h.append('sys/socket.h') if has_linux_types_h: prereq_linux_rtnetlink_h.append('linux/types.h') has_linux_rtnetlink_h = conf.CheckHeader(prereq_linux_rtnetlink_h + ['linux/rtnetlink.h']) if has_linux_netlink_h: conf.Define('HAVE_NETLINK_SOCKETS') elif has_net_route_h and host_os != 'linux-gnu': conf.Define('HAVE_ROUTING_SOCKETS') if has_linux_netlink_h: rta_nl_includes = [] for s in prereq_linux_rtnetlink_h: rta_nl_includes.append("#include <%s>\n" % s) rta_nl_includes.append("#include \n"); rta_nl_includes = string.join(rta_nl_includes, '') has_netlink_rta_table = conf.CheckDeclaration('RTA_TABLE', rta_nl_includes) if has_netlink_rta_table: conf.Define('HAVE_NETLINK_SOCKET_ATTRIBUTE_RTA_TABLE') # net stack: struct members # XXX header conditionals for linux/bsd variants needed. has_struct_sockaddr_dl_sdl_len = conf.CheckTypeMember('struct sockaddr_dl', 'sdl_len', includes='#include \n#include ') has_struct_ifreq_ifr_hwaddr = conf.CheckTypeMember('struct ifreq', 'ifr_hwaddr', includes='#include \n#include ') has_struct_ifreq_ifr_ifindex = conf.CheckTypeMember('struct ifreq', 'ifr_ifindex', includes='#include \n#include ') # net stack: functions # XXX some may be in libc or libnsl has_ether_aton = conf.CheckFunc('ether_aton') has_ether_aton_r = conf.CheckFunc('ether_aton_r') has_ether_ntoa = conf.CheckFunc('ether_ntoa') has_ether_ntoa_r = conf.CheckFunc('ether_ntoa_r') has_getaddrinfo = conf.CheckFunc('getaddrinfo') has_getifaddrs = conf.CheckFunc('getifaddrs') has_getnameinfo = conf.CheckFunc('getnameinfo') has_if_indextoname = conf.CheckFunc('if_indextoname') has_if_nametoindex = conf.CheckFunc('if_nametoindex') has_inet_ntop = conf.CheckFunc('inet_ntop') has_inet_pton = conf.CheckFunc('inet_pton') # net stack: types # XXX header conditionals for linux/bsd variants needed. prereq_ether_includes = [ 'sys/types.h', 'sys/socket.h' ] if has_net_ethernet_h: prereq_ether_includes.append('net/ethernet.h') if has_net_if_h: prereq_ether_includes.append('net/if.h') if has_net_if_ether_h: prereq_ether_includes.append('net/if_ether.h') ether_includes = [] for s in prereq_ether_includes: ether_includes.append("#include <%s>\n" % s) ether_includes = string.join(ether_includes, '') has_struct_ether_addr = conf.CheckType('struct ether_addr', includes=ether_includes) # net stack: sysctl (bsd) conf.CheckSysctl('NET_RT_DUMP', oid='CTL_NET, AF_ROUTE, 0, AF_INET, NET_RT_DUMP, 0', includes='#include ') conf.CheckSysctl('NET_RT_IFLIST', oid='CTL_NET, AF_ROUTE, 0, AF_INET, NET_RT_IFLIST, 0', includes='#include ') # XXX test for SIOCGIFCONF. Very gnarly. siocgifconf_includes = [ 'stdlib.h', 'errno.h' ] if has_sys_types_h: siocgifconf_includes.append('sys/types.h') if has_sys_socket_h: siocgifconf_includes.append('sys/socket.h') if has_sys_sockio_h: siocgifconf_includes.append('sys/sockio.h') if has_sys_ioctl_h: siocgifconf_includes.append('sys/ioctl.h') if has_net_if_h: siocgifconf_includes.append('net/if.h') si = [] for s in siocgifconf_includes: si.append("#include <%s>\n" % s) si = string.join(si, '') has_siocgifconf = conf.CheckDeclaration('SIOCGIFCONF', si) if has_siocgifconf: conf.Define('HAVE_IOCTL_SIOCGIFCONF') # autoconf compat ########## # v4 stack has_netinet_in_h = conf.CheckHeader('netinet/in.h') has_netinet_in_systm_h = conf.CheckHeader(['sys/types.h', 'netinet/in_systm.h']) prereq_netinet_in_var_h = [] if has_sys_types_h: prereq_netinet_in_var_h.append('sys/types.h') if has_sys_socket_h: prereq_netinet_in_var_h.append('sys/socket.h') if has_net_if_h: prereq_netinet_in_var_h.append('net/if.h') if has_net_if_var_h: prereq_netinet_in_var_h.append('net/if_var.h') if has_netinet_in_h: prereq_netinet_in_var_h.append('netinet/in.h') has_netinet_in_var_h = conf.CheckHeader(prereq_netinet_in_var_h + ['netinet/in_var.h']) # Header file might need , , # and/or prereq_netinet_ip_h = [] if has_sys_types_h: prereq_netinet_ip_h.append('sys/types.h') if has_netinet_in_h: prereq_netinet_ip_h.append('netinet/in.h') if has_netinet_in_systm_h: prereq_netinet_ip_h.append('netinet/in_systm.h') has_netinet_ip_h = conf.CheckHeader(prereq_netinet_ip_h + ['netinet/ip.h']) has_netinet_tcp_h = conf.CheckHeader(['sys/param.h', 'sys/socket.h', 'netinet/in.h', 'netinet/in_systm.h', 'netinet/ip.h', 'netinet/tcp.h']) has_netinet_igmp_h = conf.CheckHeader(['sys/types.h', 'netinet/in.h', 'netinet/igmp.h']) has_netinet_ether_h = conf.CheckHeader('netinet/ether.h') # Header file might need , # , , and/or prereq_netinet_if_ether_h = [] if has_sys_types_h: prereq_netinet_if_ether_h.append('sys/types.h') if has_sys_socket_h: prereq_netinet_if_ether_h.append('sys/socket.h') if has_net_if_h: prereq_netinet_if_ether_h.append('net/if.h') if has_netinet_in_h: prereq_netinet_if_ether_h.append('netinet/in.h') has_netinet_if_ether_h = conf.CheckHeader(prereq_netinet_if_ether_h + ['netinet/if_ether.h']) # opensolaris has_inet_nd_h = conf.CheckHeader('inet/nd.h') has_inet_ip_h = conf.CheckHeader('inet/ip.h') # name lookup, telnet has_arpa_inet_h = conf.CheckHeader('arpa/inet.h') has_arpa_telnet_h = conf.CheckHeader('arpa/telnet.h') has_struct_sockaddr_in_sin_len = conf.CheckTypeMember('struct sockaddr_in', 'sin_len', includes='#include \n#include ') # check for v4 multicast capability # XXX conditional on these headers please prereq_v4mcast = [ 'sys/types.h', 'sys/socket.h', 'netinet/in.h' ] v4mcast_symbols = [ 'IP_MULTICAST_IF', 'IP_MULTICAST_TTL', 'IP_MULTICAST_LOOP', 'IP_ADD_MEMBERSHIP', 'IP_DROP_MEMBERSHIP' ] # munge header list v4mcast_includes = [] for s in prereq_v4mcast: v4mcast_includes.append("#include <%s>\n" % s) v4mcast_includes = string.join(v4mcast_includes, '') # check for each symbol gotv4sym = True for s in v4mcast_symbols: gotv4sym = gotv4sym and conf.CheckDeclaration(s, v4mcast_includes) has_v4_mcast = gotv4sym # test result if has_v4_mcast: conf.Define('HAVE_IPV4_MULTICAST') if host_os == 'linux-gnu': print "Enabling MULT_MCAST_TABLES logic since we are compiling for Linux.\n" conf.Define('USE_MULT_MCAST_TABLES') # v4 stack: sysctl (bsd) conf.CheckSysctl('IPCTL_FORWARDING', oid='CTL_NET, AF_INET, IPPROTO_IP, IPCTL_FORWARDING', includes='#include \n#include ') ########## # logs if not (env.has_key('disable_warninglogs') and env['disable_warninglogs']): conf.Define('L_WARNING') if not (env.has_key('disable_infologs') and env['disable_infologs']): conf.Define('L_INFO') if not (env.has_key('disable_errorlogs') and env['disable_errorlogs']): conf.Define('L_ERROR') if not (env.has_key('disable_tracelogs') and env['disable_tracelogs']): conf.Define('L_TRACE') if not (env.has_key('disable_assertlogs') and env['disable_assertlogs']): conf.Define('L_ASSERT') if not (env.has_key('disable_otherlogs') and env['disable_otherlogs']): conf.Define('L_OTHER') if not (env.has_key('disable_fatallogs') and env['disable_fatallogs']): conf.Define('L_FATAL') ########## # v6 stack if has_af_inet6 and has_sock_stream: if not (env.has_key('disable_ipv6') and env['disable_ipv6']): conf.Define('HAVE_IPV6') prereq_rfc3542 = ['stdlib.h', 'sys/types.h', 'netinet/in.h'] rfc3542_includes = [] for s in prereq_rfc3542: # XXX: __USE_GNU must be defined for RFC3542 defines under Linux. if host_os == 'linux-gnu' and s == 'netinet/in.h': rfc3542_includes.append("#define __USE_GNU\n") rfc3542_includes.append("#include <%s>\n" % s) rfc3542_includes = string.join(rfc3542_includes, '') has___kame__ = conf.CheckDeclaration('__KAME__', rfc3542_includes) # CheckFunc() too tight. has_inet6_opt_init = conf.CheckDeclaration('inet6_opt_init', rfc3542_includes) if has___kame__: conf.Define('IPV6_STACK_KAME') if has_inet6_opt_init: conf.Define('HAVE_RFC3542') has_struct_sockaddr_in6_sin6_len = conf.CheckTypeMember('struct sockaddr_in6', 'sin6_len', includes='#include \n#include ') has_struct_sockaddr_in6_sin6_scope_id = conf.CheckTypeMember('struct sockaddr_in6', 'sin6_scope_id', includes='#include \n#include ') has_netinet_ip6_h = conf.CheckHeader(['sys/types.h', 'netinet/in.h', 'netinet/ip6.h']) prereq_netinet_icmp6_h = ['sys/types.h', 'sys/socket.h', 'netinet/in.h', 'netinet/ip6.h'] netinet_icmp6_h = 'netinet/icmp6.h' has_netinet_icmp6_h = conf.CheckHeader(prereq_netinet_icmp6_h + [ netinet_icmp6_h ]) # struct mld_hdr normally defined in mld_hdr_includes = [] for s in prereq_netinet_icmp6_h + [ netinet_icmp6_h ]: mld_hdr_includes.append("#include <%s>\n" % s) mld_hdr_includes = string.join(mld_hdr_includes, '') has_struct_mld_hdr = conf.CheckType('struct mld_hdr', includes=mld_hdr_includes) # Header file might need , , # , , and/or . prereq_netinet6_in6_var_h = [] if has_sys_types_h: prereq_netinet6_in6_var_h.append('sys/types.h') if has_sys_socket_h: prereq_netinet6_in6_var_h.append('sys/socket.h') if has_net_if_h: prereq_netinet6_in6_var_h.append('net/if.h') if has_net_if_var_h: prereq_netinet6_in6_var_h.append('net/if_var.h') if has_netinet_in_h: prereq_netinet6_in6_var_h.append('netinet/in.h') has_netinet6_in6_var_h = conf.CheckHeader(prereq_netinet6_in6_var_h + ['netinet6/in6_var.h']) # Header file might need , , # , , , and/or prereq_netinet6_nd6_h = [] if has_sys_types_h: prereq_netinet6_nd6_h.append('sys/types.h') if has_sys_socket_h: prereq_netinet6_nd6_h.append('sys/socket.h') if has_net_if_h: prereq_netinet6_nd6_h.append('net/if.h') if has_net_if_var_h: prereq_netinet6_nd6_h.append('net/if_var.h') if has_netinet_in_h: prereq_netinet6_nd6_h.append('netinet/in.h') if has_netinet6_in6_var_h: prereq_netinet6_nd6_h.append('netinet6/in6_var.h') netinet6_nd6_h = 'netinet6/nd6.h' has_netinet6_nd6_h = conf.CheckHeader(prereq_netinet6_nd6_h + [ netinet6_nd6_h ]) has_cxx_netinet6_nd6_h = conf.CheckHeader(prereq_netinet6_nd6_h + [ netinet6_nd6_h ], language='C++') if has_netinet6_nd6_h and not has_cxx_netinet6_nd6_h: conf.Define('HAVE_BROKEN_CXX_NETINET6_ND6_H') # v6 stack: sysctl (bsd) conf.CheckSysctl('IPV6CTL_FORWARDING', oid='CTL_NET, AF_INET6, IPPROTO_IPV6, IPV6CTL_FORWARDING', includes='#include \n#include ') conf.CheckSysctl('IPV6CTL_ACCEPT_RTADV', oid='CTL_NET, AF_INET6, IPPROTO_IPV6, IPV6CTL_ACCEPT_RTADV', includes='#include \n#include ') # check for v6 multicast capability # XXX conditional on these headers please prereq_v6mcast = [ 'sys/types.h', 'sys/socket.h', 'netinet/in.h' ] v6mcast_symbols = [ 'IPV6_MULTICAST_IF', 'IPV6_MULTICAST_LOOP' ] # munge header list v6mcast_includes = [] for s in prereq_v6mcast: v6mcast_includes.append("#include <%s>\n" % s) v6mcast_includes = string.join(v6mcast_includes, '') # check for each symbol gotv6sym = True for s in v6mcast_symbols: gotv6sym = gotv6sym and conf.CheckDeclaration(s, v6mcast_includes) has_v6_mcast = gotv6sym # test result if has_v6_mcast: if not (env.has_key('disable_ipv6') and env['disable_ipv6']): conf.Define('HAVE_IPV6_MULTICAST') # See if we need -std=gnu99 for fpclassify (math.h) prereq_fpclassify = [ 'math.h' ] fpclassify_includes = [] for s in prereq_fpclassify: fpclassify_includes.append("#include <%s>\n" % s) fpclassify_includes = string.join(fpclassify_includes, '') has_fpclassify = conf.CheckDeclaration('fpclassify', fpclassify_includes) if not has_fpclassify: env.AppendUnique(CFLAGS = '-std=gnu99') has_fpclassify = conf.CheckDeclaration('fpclassify', fpclassify_includes) if not has_fpclassify: print "\nERROR: Cannot find fpclassify, tried -std=gnu99 as well." sys.exit(1) else: print "\nNOTE: Using -std=gnu99 for fpclassify (math.h)\n" ########## # v4 mforwarding # this platform's multicast forwarding header(s) prereq_mroute_h = [] mroute_h = None if host_os == 'sunos': prereq_netinet_ip_mroute_h = ['sys/types.h', 'sys/ioctl.h', 'sys/socket.h', 'sys/time.h', 'inet/ip.h', 'netinet/in.h'] else: prereq_netinet_ip_mroute_h = ['sys/types.h', 'sys/ioctl.h', 'sys/socket.h', 'sys/time.h', 'net/if.h', 'net/route.h', 'netinet/in.h'] if has_net_if_var_h: prereq_netinet_ip_mroute_h.append('net/if_var.h') netinet_ip_mroute_h = 'netinet/ip_mroute.h' has_netinet_ip_mroute_h = conf.CheckHeader(prereq_netinet_ip_mroute_h + [ netinet_ip_mroute_h ]) if has_netinet_ip_mroute_h: prereq_mroute_h = prereq_netinet_ip_mroute_h mroute_h = netinet_ip_mroute_h prereq_net_ip_mroute_ip_mroute_h = ['sys/types.h', 'sys/ioctl.h', 'sys/socket.h', 'sys/time.h', 'net/if.h', 'net/route.h', 'netinet/in.h'] if has_net_if_var_h: prereq_net_ip_mroute_ip_mroute_h.append('net/if_var.h') net_ip_mroute_ip_mroute_h = 'net/ip_mroute/ip_mroute.h' has_net_ip_mroute_ip_mroute_h = conf.CheckHeader(prereq_net_ip_mroute_ip_mroute_h + [ net_ip_mroute_ip_mroute_h ]) if has_net_ip_mroute_ip_mroute_h: prereq_mroute_h = prereq_net_ip_mroute_ip_mroute_h mroute_h = net_ip_mroute_ip_mroute_h # Header file might need , , # , and/or # # TODO: The autoconf feature test for this contained a hack to exclude # that might be included by , because # might conflict with that was included # earlier. This is currently difficult to replicate in SCons, as # you can't pass arbitrary code that is prepended to the test. prereq_linux_mroute_h = [] if has_sys_types_h: prereq_linux_mroute_h.append('sys/types.h') if has_sys_socket_h: prereq_linux_mroute_h.append('sys/socket.h') if has_netinet_in_h: prereq_linux_mroute_h.append('netinet/in.h') if has_linux_types_h: prereq_linux_mroute_h.append('linux/types.h') linux_mroute_h = 'linux/mroute.h' has_linux_mroute_h = conf.CheckHeader(prereq_linux_mroute_h + [ linux_mroute_h ]) if has_linux_mroute_h: prereq_mroute_h = prereq_linux_mroute_h mroute_h = linux_mroute_h else: # Try without netinet/in.h, older releases (CentOS 5, for instance) doesn't need it # and break with it. prereq_linux_mroute_h = [] if has_sys_types_h: prereq_linux_mroute_h.append('sys/types.h') if has_sys_socket_h: prereq_linux_mroute_h.append('sys/socket.h') if has_linux_types_h: prereq_linux_mroute_h.append('linux/types.h') linux_mroute_h = 'linux/mroute.h' has_linux_mroute_h = conf.CheckHeader(prereq_linux_mroute_h + [ linux_mroute_h ]) if has_linux_mroute_h: prereq_mroute_h = prereq_linux_mroute_h mroute_h = linux_mroute_h mfcctl2_includes = [] for s in prereq_mroute_h + [ mroute_h ]: mfcctl2_includes.append("#include <%s>\n" % s) mfcctl2_includes = string.join(mfcctl2_includes, '') # common structs has_struct_mfcctl2 = conf.CheckType('struct mfcctl2', includes=mfcctl2_includes) has_struct_mfcctl2_mfcc_flags = conf.CheckTypeMember('struct mfcctl2', 'mfcc_flags', includes=mfcctl2_includes) has_struct_mfcctl2_mfcc_rp = conf.CheckTypeMember('struct mfcctl2', 'mfcc_rp', includes=mfcctl2_includes) # pim has_netinet_pim_h = conf.CheckHeader('netinet/pim.h') has_struct_pim = conf.CheckType('struct pim', includes='#include ') has_struct_pim_pim_vt = conf.CheckTypeMember('struct pim', 'pim_vt', includes='#include ') if has_netinet_ip_mroute_h or has_net_ip_mroute_ip_mroute_h or has_linux_mroute_h: conf.Define('HAVE_IPV4_MULTICAST_ROUTING') ########## # v6 mforwarding # this platform's v6 multicast forwarding header(s) prereq_mroute6_h = [] mroute6_h = None # bsd prereq_netinet6_ip6_mroute_h = ['sys/param.h', 'sys/socket.h', 'sys/time.h', 'net/if.h', 'net/route.h', 'netinet/in.h'] if has_net_if_var_h: prereq_netinet6_ip6_mroute_h.append('net/if_var.h') netinet6_ip6_mroute_h = 'netinet6/ip6_mroute.h' has_netinet6_ip6_mroute_h = conf.CheckHeader(prereq_netinet6_ip6_mroute_h + [ netinet6_ip6_mroute_h ]) if has_netinet6_ip6_mroute_h: prereq_mroute6_h = prereq_netinet6_ip6_mroute_h mroute6_h = netinet6_ip6_mroute_h # linux prereq_linux_mroute6_h = ['sys/types.h', 'sys/socket.h', 'netinet/in.h', 'linux/types.h'] linux_mroute6_h = 'linux/mroute6.h' has_linux_mroute6_h = conf.CheckHeader(prereq_linux_mroute6_h + [ linux_mroute6_h ]) if has_linux_mroute6_h: prereq_mroute6_h = prereq_linux_mroute6_h mroute6_h = linux_mroute6_h i6o_includes = [] for s in prereq_linux_mroute6_h: i6o_includes.append("#include <%s>\n" % s) i6o_includes.append("#include <%s>\n" % linux_mroute6_h); i6o_includes = string.join(i6o_includes, '') has_inet6_option_space = conf.CheckDeclaration('inet6_option_space', i6o_includes); # common structs mf6cctl2_includes = [] for s in prereq_mroute6_h + [ mroute6_h ]: mf6cctl2_includes.append("#include <%s>\n" % s) mf6cctl2_includes = string.join(mf6cctl2_includes, '') has_struct_mf6cctl2 = conf.CheckType('struct mf6cctl2', includes=mf6cctl2_includes) has_struct_mfcctl2_mfcc_flags = conf.CheckTypeMember('struct mf6cctl2', 'mf6cc_flags', includes=mf6cctl2_includes) has_struct_mfcctl2_mfcc_rp = conf.CheckTypeMember('struct mf6cctl2', 'mf6cc_rp', includes=mf6cctl2_includes) # XXX: linux marked inet6_option_space() and friends as deprecated; # either rework mfea code or do this. if has_netinet6_ip6_mroute_h or has_linux_mroute6_h: if not (env.has_key('disable_ipv6') and env['disable_ipv6']): conf.Define('HAVE_IPV6_MULTICAST_ROUTING') if has_inet6_option_space: conf.Define('HAVE_IPV6_OPTION_SPACE') else: if not has_inet6_opt_init: print "\nWARNING: inet6_option_* and inet6_opt_* are not supported on this system." print " this might cause some problems with IPv6 multicast routing.\n" has_struct_mif6ctl_vifc_threshold = conf.CheckTypeMember('struct mif6ctl', 'vifc_threshold', includes=mf6cctl2_includes) ########## # packet filters has_netinet_ip_compat_h = conf.CheckHeader(['sys/types.h', 'netinet/ip_compat.h']) has_netinet_ip_fil_h = conf.CheckHeader(['sys/types.h', 'sys/ioctl.h', 'sys/socket.h', 'netinet/in.h', 'netinet/in_systm.h', 'netinet/ip.h', 'netinet/ip_compat.h', 'netinet/ip_fil.h']) prereq_ip_fw_h = ['sys/types.h', 'sys/ioctl.h', 'sys/socket.h', 'net/if.h', 'netinet/in.h'] if has_net_if_var_h: prereq_ip_fw_h.append('net/if_var.h') has_netinet_ip_fw_h = conf.CheckHeader(prereq_ip_fw_h + ['netinet/ip_fw.h']) prereq_net_pfvar_h = ['sys/param.h', 'sys/file.h', 'sys/ioctl.h', 'sys/socket.h', 'net/if.h', 'netinet/in.h'] if has_net_if_var_h: prereq_net_pfvar_h.append('net/if_var.h') has_net_pfvar_h = conf.CheckHeader(prereq_net_pfvar_h + ['net/pfvar.h']) # Older linux kernel headers for netfilter wouldn't compile with C++ w/out hacking on the headers themselves. has_linux_netfilter_ipv4_ip_tables_h = conf.CheckHeader(['sys/param.h', 'net/if.h', 'netinet/in.h', 'linux/netfilter_ipv4/ip_tables.h'], language = "C++") has_linux_netfilter_ipv6_ip6_tables_h = conf.CheckHeader(['sys/param.h', 'net/if.h', 'netinet/in.h', 'linux/netfilter_ipv6/ip6_tables.h'], language = "C++") if has_linux_netfilter_ipv4_ip_tables_h or has_linux_netfilter_ipv6_ip6_tables_h: if not (env.has_key('disable_fw') and env['disable_fw']): conf.Define('HAVE_FIREWALL_NETFILTER') ########## # vlan # Header files , , and # might need a list of other files. prereq_vlan = [] if has_sys_types_h: prereq_vlan.append('sys/types.h') if has_sys_socket_h: prereq_vlan.append('sys/socket.h') if has_net_if_h: prereq_vlan.append('net/if.h') if has_net_ethernet_h: prereq_vlan.append('net/ethernet.h') if has_net_if_ether_h: prereq_vlan.append('net/if_ether.h') if has_netinet_in_h: prereq_vlan.append('netinet/in.h') if has_netinet_if_ether_h: prereq_vlan.append('netinet/if_ether.h') has_net_if_vlanvar_h = conf.CheckHeader(prereq_vlan + ['net/if_vlanvar.h']) has_net_if_vlan_var_h = conf.CheckHeader(prereq_vlan + ['net/if_vlan_var.h']) has_net_vlan_if_vlan_var_h = conf.CheckHeader(prereq_vlan + ['net/vlan/if_vlan_var.h']) has_linux_if_vlan_h = conf.CheckHeader('linux/if_vlan.h') if has_linux_if_vlan_h: conf.Define('HAVE_VLAN_LINUX') # Check for really old if_vlan.h that doesn't have GET_VLAN_REALDEV_NAME_CMD enum # Vlans might still not work if the target kernel doesn't support these options, # but at least it will compile and has the potential to work. if not conf.CheckDeclaration('GET_VLAN_REALDEV_NAME_CMD', '#include '): conf.Define('GET_VLAN_REALDEV_NAME_CMD', '8') if not conf.CheckDeclaration('GET_VLAN_VID_CMD', '#include '): conf.Define('GET_VLAN_VID_CMD', '9') else: if has_net_if_vlanvar_h or has_net_if_vlan_var_h: conf.Define('HAVE_VLAN_BSD') ########## # pcre posix regexp emulation # used by policy for regexps. has_pcre_h = conf.CheckHeader('pcre.h') has_pcreposix_h = conf.CheckHeader('pcreposix.h') has_libpcre = conf.CheckLib('pcre') has_libpcreposix = conf.CheckLib('pcreposix') ########## # openssl for md5 # XXX Check for MD5_Init() prereq_md5 = [] if has_sys_types_h: prereq_md5.append('sys/types.h') has_openssl_md5_h = conf.CheckHeader(prereq_md5 + ['openssl/md5.h']) if not has_openssl_md5_h: print "\nERROR: Cannot find required openssl/md5.h." print " On Fedora/RedHat: yum install openssl-devel" print " On Ubuntu: apt-get install libssl-dev" print " After install, rm -fr xorp/obj build directory to" print " clear the configure cache before re-building." sys.exit(1) has_libcrypto = conf.CheckLib('crypto') if not has_libcrypto: print "\nERROR: Cannot find required crypto library." print " clear the configure cache before re-building." sys.exit(1) has_md5_init = conf.CheckFunc('MD5_Init') ########## # x/open dynamic linker # XXX Check for dlopen() for fea use. has_dlfcn_h = conf.CheckHeader('dlfcn.h') has_libdl = conf.CheckLib('dl') has_dl_open = conf.CheckFunc('dlopen') ########## # pcap for l2 comms has_pcap_h = conf.CheckHeader('pcap.h') has_libpcap = conf.CheckLib('pcap') env['has_libpcap'] = has_libpcap has_pcap_sendpacket = conf.CheckFunc('pcap_sendpacket') if not has_libpcap: print "\nWARNING: Libpcap was not detected.\n VRRP and other protocols may have issues." print " On Fedora/RedHat: yum install libpcap-devel" print " On Ubuntu: apt-get install libpcap-dev" print " After install, rm -fr xorp/obj build directory to" print " clear the configure cache before re-building.\n" # pcap filtering can be used to cut down on un-needed netlink packets. # This is a performance gain only, can function fine without it. prereq_pcap_bpf = [] if has_sys_types_h: prereq_pcap_bpf.append('sys/types.h') has_pcap_bpf_h = conf.CheckHeader(prereq_pcap_bpf + ['pcap-bpf.h']) if not has_pcap_bpf_h: print "\nWARNING: PCAP-BPF is not supported on this system," print " socket filtering will not work." print " This is not a real problem, just a small performance" print " loss when using multiple virtual routers on the same system." print " On Debian: apt-get install libpcap-dev" print " On Older Ubuntu: apt-get install pcap-dev\n" print " On Newer Ubuntu: apt-get install libpcap-dev\n" if not (has_linux_netfilter_ipv4_ip_tables_h or has_linux_netfilter_ipv6_ip6_tables_h): if not (env.has_key('disable_fw') and env['disable_fw']): if has_linux_mroute_h: # We are Linux...should warn users about how to make netfiltering work since # it appears their headers are busted. print "\nWARNING: Netfilter include files are broken or do not exist." print " This means the Linux firewall support will not be compiled in." print " To fix, you may edit: /usr/include/linux/netfilter_ipv4/ip_tables.h" print " line 222 or so, to look like this:" print " /* Helper functions */" print " static __inline__ struct ipt_entry_target *" print " ipt_get_target(struct ipt_entry *e)" print "{" print " /* BEN: Was void* */" print " return (struct ipt_entry_target *)((char*)e + e->target_offset);" print "}" print "\nYou will also want to edit similar code around line 282 of:" print "/usr/include/linux/netfilter_ipv6/ip6_tables.h" print "NOTE: Recent kernels use struct xt_entry_target for the argument" print " for these methods, so use that instead of ipt_entry_target if that" print " is the case for your system." ########## # curses for cli/libtecla has_libcurses = conf.CheckLib('curses') has_libpdcurses = conf.CheckLib('pdcurses') has_libncurses = conf.CheckLib('ncurses') env['has_libcurses'] = has_libcurses env['has_libpdcurses'] = has_libpdcurses env['has_libncurses'] = has_libncurses if not has_libcurses and not has_libncurses and not has_libpdcurses: print "\nERROR: Cannot find required (n)curses or pdcurses library." print " On Fedora/RedHat: yum install ncurses-devel" print " On Debian/Ubuntu: apt-get install ncurses-dev" print " After install, rm -fr xorp/obj build directory to" print " clear the configure cache before re-building." sys.exit(1) xorp/site_scons/config/allconfig.pyc0000664000076400007640000005663611703351434017772 0ustar greearbgreearbÑò Ë Oc@sAddkZddkZddkZddklZd„ZdS(iÿÿÿÿN(tSConsEnvironmentcù*Csò!|idƒ}|p|iƒnyX|ipdGHdGHtidƒndGH|ipdGHdGHtidƒndGHWnd GHd GHd GHnX|id d gƒ}|idƒ}|idƒ}|idƒ}xfddddddddgD]F}d} |o| d7} n|o| d7} n|i|| ƒqöW|idƒ} |idƒ} |idƒ} |idƒ} |id ƒ}|id!ƒ}|id"ƒ}|id#ƒ}|id$ƒ}|id%ƒ}|id&ƒ}|id'd(ƒ}|o|i d)ƒn|id*ƒ}|id+ƒ}|id,ƒ}|id-ƒ}|id.ƒ}|id/ƒ}|id0ƒ}|id1ƒ}g}|o|i d*ƒn|i|d2gƒ}|id3ƒ} |id4ƒ}!|id5ƒ}"|id6ƒ}#|id7ƒ}$|id8ƒ}%|id9ƒ}&|id:ƒ}'|id;ƒ}(|id<ƒ})|i d=ƒ}*|id>ƒ}+|id?ƒ},|i d@ƒ}-|idAƒ}.|idBdCƒ}/|/o|i dDƒn|idEdCƒ}0|0o|i dFƒn|idGdHdCƒ}1|idIƒ}2|idJƒ}3|idKƒ}4|idLƒ}5|idMƒ}6|idNƒ}7|i dOƒ}8|idPƒ}9|idQƒ}:|idRƒ};|idSƒ}<|idTƒ}=|idUƒ}>|idVƒ}?|idWƒ}@|idXƒ}A|idYƒ}B|idZƒ}C|id[ƒ}D|id\ƒ}E|id]ƒ}Fg}G|o|Gi d*ƒn|;o|Gi dRƒn|i|Gd^gƒ}H|id_ƒ}I|id`ƒ}J|idaƒ}K|idRdbgƒ}L|idRdcgƒ}M|iddƒ}N|ideƒ}O|idfdHdgƒ}P|idhdHdiƒ}Q|idjdHdiƒ}R|i dkƒo|dko d g}Sn d*d[g}Sg}Tx|SD]}U|Ti dl|UƒqWt i|Tdƒ}T|idm|Tƒ}V|idn|Tƒ}W|ido|Tƒ}X|idp|Tƒ}Y|idq|Tƒ}Z|Vo|Xo|Yo|i drƒn|VoQ|ZoJ|i dsƒ|i dkƒo|dkp|i dtƒ|i duƒq†n|Qod|idhdvdHdiƒ}[|idhdwdHdiƒ}\|idhdxdHdiƒ}]|idhdydHdiƒ}^n|idzd{dHdiƒ}_|id|d}dHdiƒ}`|id~ddHd€ƒ}a|id*dgƒ}b|id‚ƒ}c|id*dYd[dƒgƒ}d|id*dYd[dƒd„gƒ}e|id*d…gƒ}f|id*dƒd†gƒ}g|id*d‡gƒ}h|id*dYd[dƒdˆgƒ}i|id‰ƒ}j|id*dYd[dƒdŠgƒ}k|id*d[d‹gƒ}l|idŒƒ}mg}n|o|ni dƒn|o|ni dƒn|No|ni ddƒn|i|ndgƒ}o|idŽƒ}pg}q|o|qi d*ƒn|Do|qi d[ƒn|No|qi ddƒn|i|qdgƒ}rg}s|o|si d*ƒn|Do|si d[ƒn|No|si ddƒn|i|sdgƒ}t|ro|i d‘ƒn&|ko|d’jo|i d“ƒn|roug}ux|sD]}U|ui dl|UƒqÖ W|ui d”ƒt i|udƒ}u|id•|uƒ}v|vo|i d–ƒq> n|id—d˜dHd™ƒ}w|idšd›dHdœƒ}x|idšddHdœƒ}y|idžƒ}z|idŸƒ}{|id ƒ}||id¡ƒ}}|id¢ƒ}~|id£ƒ}|id¤ƒ}€|id¥ƒ}|id¦ƒ}‚|id§ƒ}ƒ|id¨ƒ}„d*d[g}…|bo|…i dƒn|do|…i dƒƒn|go|…i d†ƒng}†x|…D]}U|†i dl|UƒqŒ Wt i|†dƒ}†|id©dH|†ƒ}‡|idªd«d¬dHdiƒ|id­d«d®dHdiƒdd¯g}ˆ|o|ˆi d*ƒn|Do|ˆi d[ƒn|Eo|ˆi d\ƒn|Bo|ˆi dYƒn|do|ˆi dƒƒng}‰x|ˆD]}U|‰i dl|Uƒq‘ Wt i|‰dƒ}‰|id°|‰ƒ}Š|Šo|i d±ƒn|id²ƒ}‹|id*d³gƒ}Œg}|o|i d*ƒn|Do|i d[ƒn|do|i dƒƒn|io|i dˆƒn|‹o|i d²ƒn|i|d´gƒ}Žg}|o|i d*ƒn|‹o|i d²ƒn|Œo|i d³ƒn|i|dµgƒ}|idRd[d²d³dµd¶gƒ}‘|id*d²d·gƒ}’|id¸ƒ}“g}”|o|”i d*ƒn|Do|”i d[ƒn|do|”i dƒƒn|‹o|”i d²ƒn|i|”d¹gƒ}•|idºƒ}–|id»ƒ}—|id¼ƒ}˜|id½ƒ}™|id¾d¿dHdÀƒ}šd*d[d²g}›dÁdÂdÃdÄdÅg}œg}x|›D]}U|i dl|UƒqMWt i|dƒ}t}žx'|œD]}U|žo|i|U|ƒ}žq‡W|ž}Ÿ|Ÿo4|i dƃ|d’jodÇGH|i dȃqën|idÉd«dÊdHdÀƒ|i d˃o|dËp|i d̃n|i d̓o|dÍp|i d΃n|i dσo|dÏp|i dЃn|i dуo|dÑp|i dÒƒn|i dÓƒo|dÓp|i dÔƒn|i dÕƒo|dÕp|i dÖƒn|i d׃o|d×p|i d؃n|Wo7|Xo0|i dÙƒo|dÙp|i dÚƒqvndd*d²g} g}¡xJ| D]B}U|d’jo|Ud²jo|¡i dÛƒn|¡i dl|Uƒq’Wt i|¡dƒ}¡|idÜ|¡ƒ}¢|idÝ|¡ƒ}£|¢o|i dÞƒn|£o|i d߃n|idàdádHdÀƒ}¤|idàdâdHdÀƒ}¥|id*d²dãgƒ}¦d*d[d²dãg}§dä}¨|i|§|¨gƒ}©g}ªx&|§|¨gD]}U|ªi dl|UƒqÈWt i|ªdƒ}ª|idådH|ªƒ}«g}¬|o|¬i d*ƒn|Do|¬i d[ƒn|do|¬i dƒƒn|io|¬i dˆƒn|‹o|¬i d²ƒn|i|¬dægƒ}­g}®|o|®i d*ƒn|Do|®i d[ƒn|do|®i dƒƒn|io|®i dˆƒn|‹o|®i d²ƒn|­o|®i dæƒndç}¯|i|®|¯gƒ}°|i|®|¯gdèdéƒ}±|°o|± o|i dêƒn|idëd«dìdHdÀƒ|idíd«dîdHdÀƒd*d[d²g}²dïdðg}³g}´x|²D]}U|´i dl|UƒqæWt i|´dƒ}´t}µx'|³D]}U|µo|i|U|´ƒ}µq W|µ}¶|¶o0|i dÙƒo|dÙp|i dñƒq€nd"g}·g}¸x|·D]}U|¸i dl|Uƒq–Wt i|¸dƒ}¸|idò|¸ƒ}¹|¹pH|idódôƒ|idò|¸ƒ}¹|¹pdõGHtidƒq$döGHng}ºd}»|d÷jod*dYd[dWd»d²g}¼n4d*dYd[dWdƒdŠd²g}¼|io|¼i dˆƒndø}½|i|¼|½gƒ}¾|¾o|¼}º|½}»nd*dYd[dWdƒdŠd²g}¿|io|¿i dˆƒndù}À|i|¿|Àgƒ}Á|Áo|¿}º|À}»ng}Â|o|Âi d*ƒn|Do|Âi d[ƒn|‹o|Âi d²ƒn|No|Âi ddƒndú}Ã|i|Â|Ãgƒ}Ä|Äo|Â}º|Ã}»n‚g}Â|o|Âi d*ƒn|Do|Âi d[ƒn|No|Âi ddƒndú}Ã|i|Â|Ãgƒ}Ä|Äo|Â}º|Ã}»ng}Åx&|º|»gD]}U|Åi dl|UƒqSWt i|Ådƒ}Å|idûdH|Ń}Æ|idûdüdH|Ń}Ç|idûdýdH|Ń}È|idþƒ}É|idÿdHdƒ}Ê|idÿddHdƒ}Ë|¾p|Áp|Äo|i dƒng}Ìd}ÍdRd[dWdƒdŠd²g}Î|io|Îi dˆƒnd}Ï|i|Î|Ïgƒ}Ð|Ðo|Î}Ì|Ï}Índ*d[d²ddg}Ñd}Ò|i|Ñ|Ògƒ}Ó|Óo|Ñ}Ì|Ò}Íng}Ôx|ÑD]}U|Ôi dl|UƒqèW|Ôi dl|Òƒt i|Ôdƒ}Ô|id|Ôƒ}Õg}Öx&|Ì|ÍgD]}U|Öi dl|UƒqLWt i|Ödƒ}Ö|iddH|Öƒ}×|idddH|Öƒ}Ç|idddH|Öƒ}È|Ðp|Óo]|i dÙƒo|dÙp>|i d ƒ|Õo|i d ƒq%|£pd GHd GHq%q)n|id ddH|Öƒ}Ø|id*dgƒ}Ù|id*dYd[d²d³dµddgƒ}Úd*dYd[dƒd²g}Û|io|Ûi dˆƒn|i|Ûdgƒ}ÜdRddYd[dƒd²g}Ý|io|Ýi dˆƒn|i|Ýdgƒ}Þ|idRdƒd²dgdèdéƒ}ß|idRdƒd²dgdèdéƒ}à|ßp|ào0|i dƒo|dp|i dƒq†ng}á|o|ái d*ƒn|Do|ái d[ƒn|do|ái dƒƒn|bo|ái dƒn|go|ái d†ƒn|‹o|ái d²ƒn|•o|ái d¹ƒn|i|ádgƒ}â|i|ádgƒ}ã|i|ádgƒ}ä|idƒ}å|åo_|i dƒ|iddƒp|i ddƒn|id dƒp|i d d!ƒq n |âp|ão|i d"ƒn|id#ƒ}æ|id$ƒ}ç|i d%ƒ}è|i d&ƒ}ég}ê|o|êi d*ƒn|i|êd'gƒ}ë|ëp*d(GHd)GHd*GHd+GHd,GHtidƒn|i d-ƒ}ì|ìpd.GHd,GHtidƒn|id/ƒ}í|id0ƒ}î|i d1ƒ}ï|id2ƒ}ð|id3ƒ}ñ|i d4ƒ}ò|ò|d5<|id6ƒ}ó|òpd7GHd8GHd9GHd+GHd:GHng}ô|o|ôi d*ƒn|i|ôd;gƒ}õ|õp'd<GHd=GHd>GHd?GHd@GHdAGHdBGHn|ßp|àp~|i dƒo|dp_|ÄoTdCGHdDGHdEGHdFGHdGGHdHGHdIGHdJGHdKGHdLGHdMGHdNGHdOGHdPGHdQGHdRGHq]!qa!n|i dSƒ}ö|i dTƒ}÷|i dUƒ}ø|ö|dV<|÷|dW<|ø|dX<|ö o:|ø o2|÷ o*dYGHdZGHd[GHd+GHd,GHtidƒndS(\Nsendian.hs, ERROR: Cannot find functional cc compiler.s# On Fedora/RedHat: yum install gccis#OK: c compiler appears functional.s- ERROR: Cannot find functional c++ compiler.s' On Fedora/RedHat: yum install gcc-g++s%OK: C++ compiler appears functional.s-NOTE: This version of scons cannot check fors% existence of gcc and g++ compilers.s1 Will assume the exist and function properly... s winsock2.hs iphlpapi.hs routprot.hsstdint.hs inttypes.htint8_ttuint8_ttint16_ttuint16_ttint32_ttuint32_ttint64_ttuint64_tts#include s#include sstddef.hsstdarg.hsstdlib.hs strings.hsstring.hssignal.hsmath.hsmemory.htstrftimetstrlcpytstrlcattva_copys#include t HAVE_VA_COPYs sys/types.hsfcntl.hsgetopt.hsglob.hsgrp.hs pthread.hspwd.hsmqueue.hsregex.hssyslog.hs termios.hstime.hsunistd.hsvfork.htreadvtstrerrortsyslogtunametwritevtxnettrecvmsgtsendmsgtrtt clock_gettimetCLOCK_MONOTONICs#include tHAVE_CLOCK_MONOTONICtCLOCK_MONOTONIC_FASTtHAVE_CLOCK_MONOTONIC_FASTsstruct timespectincludesspaths.hs sysexits.htrealpathtstrptimetsysctlsnetdb.htresolvt hstrerrors sys/cdefs.hs sys/param.hs sys/utsname.hs sys/errno.hs sys/wait.hs sys/signal.hs sys/time.hs sys/uio.hs sys/ioctl.hs sys/select.hs sys/socket.hs sys/sockio.hssys/un.hs sys/mount.hssys/resource.hs sys/stat.hs sys/syslog.hs sys/linker.hs sys/sysctl.hs linux/types.hslinux/sockios.hs struct iovecs#include s struct msghdrs#include sstruct cmsghdrtmingws#include <%s> tAF_INETtAF_INET6t SOCK_STREAMt SOCK_DGRAMtSOCK_RAWtHAVE_TCPUDP_UNIX_SOCKETStHAVE_IP_RAW_SOCKETStIPV4_RAW_OUTPUT_IS_RAWtIPV4_RAW_INPUT_IS_RAWt msg_controltmsg_iovtmsg_namet msg_namelensstruct sockaddrtsa_lensstruct sockaddr_storagetss_lensstruct sockaddr_untsun_lens+#include #include snet/ethernet.hssys/ethernet.hsnet/if.hs net/if_arp.hs net/if_dl.hsnet/if_ether.hsnet/if_media.hs net/if_var.hsnet/if_types.hs net/route.hs ifaddrs.hs stropts.hslinux/ethtool.hslinux/if_tun.hslinux/netlink.hslinux/rtnetlink.htHAVE_NETLINK_SOCKETSs linux-gnutHAVE_ROUTING_SOCKETSs#include t RTA_TABLEt'HAVE_NETLINK_SOCKET_ATTRIBUTE_RTA_TABLEsstruct sockaddr_dltsdl_lens-#include #include s struct ifreqt ifr_hwaddrs*#include #include t ifr_ifindext ether_atont ether_aton_rt ether_ntoat ether_ntoa_rt getaddrinfot getifaddrst getnameinfotif_indextonametif_nametoindext inet_ntopt inet_ptonsstruct ether_addrt NET_RT_DUMPtoids-CTL_NET, AF_ROUTE, 0, AF_INET, NET_RT_DUMP, 0t NET_RT_IFLISTs/CTL_NET, AF_ROUTE, 0, AF_INET, NET_RT_IFLIST, 0serrno.ht SIOCGIFCONFtHAVE_IOCTL_SIOCGIFCONFs netinet/in.hsnetinet/in_systm.hsnetinet/in_var.hs netinet/ip.hs netinet/tcp.hsnetinet/igmp.hsnetinet/ether.hsnetinet/if_ether.hs inet/nd.hs inet/ip.hs arpa/inet.hs arpa/telnet.hsstruct sockaddr_intsin_lens/#include #include tIP_MULTICAST_IFtIP_MULTICAST_TTLtIP_MULTICAST_LOOPtIP_ADD_MEMBERSHIPtIP_DROP_MEMBERSHIPtHAVE_IPV4_MULTICASTsCEnabling MULT_MCAST_TABLES logic since we are compiling for Linux. tUSE_MULT_MCAST_TABLEStIPCTL_FORWARDINGs.CTL_NET, AF_INET, IPPROTO_IP, IPCTL_FORWARDINGtdisable_warninglogst L_WARNINGtdisable_infologstL_INFOtdisable_errorlogstL_ERRORtdisable_tracelogstL_TRACEtdisable_assertlogstL_ASSERTtdisable_otherlogstL_OTHERtdisable_fatallogstL_FATALt disable_ipv6t HAVE_IPV6s#define __USE_GNU t__KAME__tinet6_opt_inittIPV6_STACK_KAMEt HAVE_RFC3542sstruct sockaddr_in6tsin6_lent sin6_scope_ids netinet/ip6.hsnetinet/icmp6.hsstruct mld_hdrsnetinet6/in6_var.hsnetinet6/nd6.htlanguagesC++tHAVE_BROKEN_CXX_NETINET6_ND6_HtIPV6CTL_FORWARDINGs3CTL_NET, AF_INET6, IPPROTO_IPV6, IPV6CTL_FORWARDINGtIPV6CTL_ACCEPT_RTADVs5CTL_NET, AF_INET6, IPPROTO_IPV6, IPV6CTL_ACCEPT_RTADVtIPV6_MULTICAST_IFtIPV6_MULTICAST_LOOPtHAVE_IPV6_MULTICASTt fpclassifytCFLAGSs -std=gnu99s: ERROR: Cannot find fpclassify, tried -std=gnu99 as well.s1 NOTE: Using -std=gnu99 for fpclassify (math.h) tsunossnetinet/ip_mroute.hsnet/ip_mroute/ip_mroute.hslinux/mroute.hsstruct mfcctl2t mfcc_flagstmfcc_rps netinet/pim.hs struct pims#include tpim_vttHAVE_IPV4_MULTICAST_ROUTINGsnetinet6/ip6_mroute.hslinux/mroute6.htinet6_option_spacesstruct mf6cctl2t mf6cc_flagstmf6cc_rptHAVE_IPV6_MULTICAST_ROUTINGtHAVE_IPV6_OPTION_SPACEsK WARNING: inet6_option_* and inet6_opt_* are not supported on this system.s> this might cause some problems with IPv6 multicast routing. sstruct mif6ctltvifc_thresholdsnetinet/ip_compat.hsnetinet/ip_fil.hsnetinet/ip_fw.hs sys/file.hs net/pfvar.hs linux/netfilter_ipv4/ip_tables.hs!linux/netfilter_ipv6/ip6_tables.ht disable_fwtHAVE_FIREWALL_NETFILTERsnet/if_vlanvar.hsnet/if_vlan_var.hsnet/vlan/if_vlan_var.hslinux/if_vlan.htHAVE_VLAN_LINUXtGET_VLAN_REALDEV_NAME_CMDs#include t8tGET_VLAN_VID_CMDt9t HAVE_VLAN_BSDspcre.hs pcreposix.htpcret pcreposixs openssl/md5.hs, ERROR: Cannot find required openssl/md5.h.s. On Fedora/RedHat: yum install openssl-devels( On Ubuntu: apt-get install libssl-devs3 After install, rm -fr xorp/obj build directory tos/ clear the configure cache before re-building.tcryptos- ERROR: Cannot find required crypto library.tMD5_Initsdlfcn.htdltdlopenspcap.htpcapt has_libpcaptpcap_sendpacketsP WARNING: Libpcap was not detected. VRRP and other protocols may have issues.s. On Fedora/RedHat: yum install libpcap-devels) On Ubuntu: apt-get install libpcap-devs0 clear the configure cache before re-building. s pcap-bpf.hs3 WARNING: PCAP-BPF is not supported on this system,s! socket filtering will not work.s6 This is not a real problem, just a small performances> loss when using multiple virtual routers on the same system.s) On Debian: apt-get install libpcap-devs- On Older Ubuntu: apt-get install pcap-dev s0 On Newer Ubuntu: apt-get install libpcap-dev s= WARNING: Netfilter include files are broken or do not exist.s@ This means the Linux firewall support will not be compiled in.sE To fix, you may edit: /usr/include/linux/netfilter_ipv4/ip_tables.hs$ line 222 or so, to look like this:s /* Helper functions */s- static __inline__ struct ipt_entry_target *s% ipt_get_target(struct ipt_entry *e)t{s /* BEN: Was void* */sH return (struct ipt_entry_target *)((char*)e + e->target_offset);t}s< You will also want to edit similar code around line 282 of:s./usr/include/linux/netfilter_ipv6/ip6_tables.hsANOTE: Recent kernels use struct xt_entry_target for the argumentsE for these methods, so use that instead of ipt_entry_target if thats is the case for your system.tcursestpdcursestncursest has_libcursesthas_libpdcursesthas_libncursess< ERROR: Cannot find required (n)curses or pdcurses library.s. On Fedora/RedHat: yum install ncurses-devels0 On Debian/Ubuntu: apt-get install ncurses-dev(t CheckHeadertCheckEndiannesstCheckCCtsystexittCheckCXXt CheckTypet CheckFunctCheckDeclarationtDefinetappendtCheckLibthas_keytstringtjointCheckTypeMembert CheckSysctltTruet AppendUniquetNone(ùtenvtconfthost_ost has_endian_hthas_iphlpapi_hthas_routprot_ht has_stdint_hthas_inttypes_httypeRt has_stddef_ht has_stdarg_ht has_stdlib_ht has_strings_ht has_string_ht has_signal_ht has_math_ht has_memory_ht has_strftimet has_strlcpyt has_strlcatt has_va_copythas_sys_types_ht has_fcntl_ht has_getopt_ht has_glob_ht has_grp_ht has_pthread_ht has_pwd_ht has_mqueue_htprereq_regex_ht has_regex_ht has_syslog_ht has_termios_ht has_time_ht has_unistd_ht has_vfork_ht has_readvt has_strerrort has_syslogt has_unamet has_writevt has_libxnett has_recvmsgt has_sendmsgt has_librtthas_clock_gettimethas_clock_monotonicthas_clock_monotonic_fastthas_struct_timespect has_paths_hthas_sysexits_ht has_realpatht has_strptimet has_sysctlt has_netdb_ht has_libresolvt has_hstrerrorthas_sys_cdefs_hthas_sys_param_hthas_sys_utsname_hthas_sys_errno_hthas_sys_wait_hthas_sys_signal_hthas_sys_time_ht has_sys_uio_hthas_sys_ioctl_hthas_sys_select_hthas_sys_socket_hthas_sys_sockio_ht has_sys_un_htprereq_sys_mount_hthas_sys_mount_hthas_sys_resource_hthas_sys_stat_hthas_sys_syslog_hthas_sys_linker_hthas_sys_sysctl_hthas_linux_types_hthas_linux_sockios_hthas_struct_iovecthas_struct_msghdrthas_struct_cmsghdrtprereq_af_inet_includestaf_inet_includestst has_af_inett has_af_inet6thas_sock_streamthas_sock_dgramt has_sock_rawthas_struct_msghdr_msg_controlthas_struct_msghdr_msg_iovthas_struct_msghdr_msg_namethas_struct_msghdr_msg_namelenthas_struct_sockaddr_sa_lent"has_struct_sockaddr_storage_ss_lenthas_struct_sockaddr_un_sun_lenthas_net_ethernet_hthas_sys_ethernet_ht has_net_if_hthas_net_if_arp_hthas_net_if_dl_hthas_net_if_ether_hthas_net_if_media_hthas_net_if_var_hthas_net_if_types_hthas_net_route_ht has_ifaddrs_ht has_stropts_htprereq_linux_ethtool_hthas_linux_ethtool_hthas_linux_if_tun_htprereq_linux_netlink_hthas_linux_netlink_htprereq_linux_rtnetlink_hthas_linux_rtnetlink_htrta_nl_includesthas_netlink_rta_tablethas_struct_sockaddr_dl_sdl_lenthas_struct_ifreq_ifr_hwaddrthas_struct_ifreq_ifr_ifindexthas_ether_atonthas_ether_aton_rthas_ether_ntoathas_ether_ntoa_rthas_getaddrinfothas_getifaddrsthas_getnameinfothas_if_indextonamethas_if_nametoindext has_inet_ntopt has_inet_ptontprereq_ether_includestether_includesthas_struct_ether_addrtsiocgifconf_includestsithas_siocgifconfthas_netinet_in_hthas_netinet_in_systm_htprereq_netinet_in_var_hthas_netinet_in_var_htprereq_netinet_ip_hthas_netinet_ip_hthas_netinet_tcp_hthas_netinet_igmp_hthas_netinet_ether_htprereq_netinet_if_ether_hthas_netinet_if_ether_ht has_inet_nd_ht has_inet_ip_hthas_arpa_inet_hthas_arpa_telnet_hthas_struct_sockaddr_in_sin_lentprereq_v4mcasttv4mcast_symbolstv4mcast_includestgotv4symt has_v4_mcasttprereq_rfc3542trfc3542_includest has___kame__thas_inet6_opt_initt has_struct_sockaddr_in6_sin6_lent%has_struct_sockaddr_in6_sin6_scope_idthas_netinet_ip6_htprereq_netinet_icmp6_htnetinet_icmp6_hthas_netinet_icmp6_htmld_hdr_includesthas_struct_mld_hdrtprereq_netinet6_in6_var_hthas_netinet6_in6_var_htprereq_netinet6_nd6_htnetinet6_nd6_hthas_netinet6_nd6_hthas_cxx_netinet6_nd6_htprereq_v6mcasttv6mcast_symbolstv6mcast_includestgotv6symt has_v6_mcasttprereq_fpclassifytfpclassify_includesthas_fpclassifytprereq_mroute_htmroute_htprereq_netinet_ip_mroute_htnetinet_ip_mroute_hthas_netinet_ip_mroute_ht prereq_net_ip_mroute_ip_mroute_htnet_ip_mroute_ip_mroute_hthas_net_ip_mroute_ip_mroute_htprereq_linux_mroute_htlinux_mroute_hthas_linux_mroute_htmfcctl2_includesthas_struct_mfcctl2thas_struct_mfcctl2_mfcc_flagsthas_struct_mfcctl2_mfcc_rpthas_netinet_pim_hthas_struct_pimthas_struct_pim_pim_vttprereq_mroute6_ht mroute6_htprereq_netinet6_ip6_mroute_htnetinet6_ip6_mroute_hthas_netinet6_ip6_mroute_htprereq_linux_mroute6_htlinux_mroute6_hthas_linux_mroute6_ht i6o_includesthas_inet6_option_spacetmf6cctl2_includesthas_struct_mf6cctl2t!has_struct_mif6ctl_vifc_thresholdthas_netinet_ip_compat_hthas_netinet_ip_fil_htprereq_ip_fw_hthas_netinet_ip_fw_htprereq_net_pfvar_hthas_net_pfvar_ht$has_linux_netfilter_ipv4_ip_tables_ht%has_linux_netfilter_ipv6_ip6_tables_ht prereq_vlanthas_net_if_vlanvar_hthas_net_if_vlan_var_hthas_net_vlan_if_vlan_var_hthas_linux_if_vlan_ht has_pcre_hthas_pcreposix_ht has_libpcrethas_libpcreposixt prereq_md5thas_openssl_md5_ht has_libcryptot has_md5_initt has_dlfcn_ht has_libdlt has_dl_opent has_pcap_hRthas_pcap_sendpackettprereq_pcap_bpfthas_pcap_bpf_hR”R•R–((sD/home/greearb/git/xorp.ct.github/xorp/site_scons/config/allconfig.pyt DoAllConfig!sæ               !             '!!       (RštosR¤tSCons.Script.SConscriptRRŸ(((sD/home/greearb/git/xorp.ct.github/xorp/site_scons/config/allconfig.pyts    xorp/site_scons/config/sysctl.pyc0000664000076400007640000001174411421141062017333 0ustar greearbgreearbÑò I¿DLc@srddkZddkZddklZddklZlZddd„Zdd„Z dd„Z d„Z dS(iÿÿÿÿN(tIntType(t LogInputFilestLogErrorMessagesc Csç|iod|i}nd}|p d}n|dj o/|djo"d}|id||fƒ|Sd}d}dh|d6|d 6|d 6} |id |ƒ|i| |ƒ} t|| d || d |ƒd|_| S(s' Configure check for a BSD-style sysctl with oid "oid", which must be quoted text. This is purely a compile-time check, not a run-time check. Optional "includes" can be defined to include a header file. "language" should be "C" or None. Default is "C". Sets HAVE_type_name in context.havedict according to the result. Note that this uses the current value of compiler and linker flags, make sure $CFLAGS, $CPPFLAGS and $LIBS are set correctly. Returns an empty string for success, an error message for failure. s #include "%s"ttCsUnsupported languagesCannot check for sysctl %s: %s s.cs %(include)s #include #include #include #include %(header)s int main() { size_t buflen; int mib1[] = { %(oid)s }; if (sysctl(mib1, sizeof(mib1)/sizeof(mib1[0]), NULL, &buflen, NULL, 0) < 0) return (1); return (0); } tincludetheadertoids)Checking whether system has sysctl %s... t HAVE_SYSCTL_s*Define to 1 if target has the `%s' sysctl.iN(theaderfilenametNonetDisplayt BuildProgt _YesNoResulttdid_show_result( tcontextt sysctl_nameRtincludestlanguaget includetexttmsgtlangtsuffixtsrctret((sA/home/greearb/git/xorp.ct.github/xorp/site_scons/config/sysctl.pyt CheckSysctls(      cCsX|ot||| |ƒn|o!|idƒt|||ƒn|idƒdS(s¡ Handle the result of a test with a "yes" or "no" result. "ret" is the return value: empty if OK, error message when not. "key" is the name of the symbol to be defined (HAVE_foo). "text" is the source code of the program used for testing. "comment" is the C comment to add above the line defining the symbol (the comment is automatically put inside a /* */). If None, no comment is added. sno syes N(t_HaveR t _LogFailed(RRtkeyttexttcomment((sA/home/greearb/git/xorp.ct.github/xorp/site_scons/config/sysctl.pyR Ys  cCs,ti|ƒ}tidd|ƒ}||i|<|djod|}nY|djod|}n>t|ƒtjod||f}nd|t|ƒf}|d j od ||}n d |}|i o-t |i d ƒ}|i |ƒ|i ƒn%t |d ƒo|i||_nd S(s& Store result of a test in context.havedict and context.headerfilename. "key" is a "HAVE_abc" name. It is turned into all CAPITALS and non- alphanumerics are replaced by an underscore. The value of "have" can be: 1 - Feature is defined, add "#define key". 0 - Feature is not defined, add "/* #undef key */". Adding "undef" is what autoconf does. Not useful for the compiler, but it shows that the test was done. number - Feature is defined to this number "#define key have". Doesn't work for 0 or 1, use a string then. string - Feature is defined to this string "#define key have". Give "have" as is should appear in the header file, include quotes when desired and escape special characters! s [^A-Z0-9_]t_is #define %s 1 is/* #undef %s */ s#define %s %d s#define %s %s s /* %s */ s tatconfig_hN(tstringtuppertretsubthavedictttypeRtstrR R topentwritetclosethasattrR!(RRthaveRtkey_uptlinetlinestf((sA/home/greearb/git/xorp.ct.github/xorp/site_scons/config/sysctl.pyRks&       cCs®to‡|idƒti|dƒ}t|ƒo|ddjo|d }nd}x3|D]'}|id||fƒ|d}q_Wnto|id|ƒndS( sr Write to the log about a failed program. Add line numbers, so that error messages can be understood. sFailed program was: s iÿÿÿÿRis%d: %s sError message: %s N(RtLogR"tsplittlenR(RRRR0tnR/((sA/home/greearb/git/xorp.ct.github/xorp/site_scons/config/sysctl.pyR”s ( R$R"ttypesRtSCons.ConftestRRR RR RR(((sA/home/greearb/git/xorp.ct.github/xorp/site_scons/config/sysctl.pyts  ?  )xorp/site_scons/config/member.py0000664000076400007640000001257411421137511017124 0ustar greearbgreearb# Copyright (c) 2009 XORP, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # $Id$ import re import string from types import IntType from SCons.Conftest import LogInputFiles, LogErrorMessages # TODO: Support C++. def CheckTypeMember(context, type_name, member_name, includes = None, language = None): """ Configure check for a C or C++ type "type_name" with "member_name". Optional "includes" can be defined to include a header file. "language" should be "C" or None. Default is "C". Sets HAVE_type_name in context.havedict according to the result. Note that this uses the current value of compiler and linker flags, make sure $CFLAGS, $CPPFLAGS and $LIBS are set correctly. Returns an empty string for success, an error message for failure. """ # Include "confdefs.h" first, so that the header can use HAVE_HEADER_H. if context.headerfilename: includetext = '#include "%s"' % context.headerfilename else: includetext = '' if not includes: includes = "" if language is not None and language != 'C': msg = 'Unsupported language' context.Display("Cannot check for %s type: %s\n" % (type_name, msg)) return msg lang = 'C' suffix = '.c' src = includetext + includes src = src + """ int main() { static %(type_name)s scons_aggr; if (sizeof (scons_aggr.%(member_name)s)) return 0; return 0; } """ % { 'type_name': type_name, 'member_name': member_name } context.Display("Checking whether %s type %s has member %s... " % (lang, type_name, member_name)) ret = context.BuildProg(src, suffix) _YesNoResult(context, ret, "HAVE_" + type_name + '_' + member_name, src, "Define to 1 if `%s' is member of `%s'." % (member_name, type_name)) # XXX context.did_show_result = 1 return ret # # END OF PUBLIC FUNCTIONS # Following cloned from Conftest.py. # def _YesNoResult(context, ret, key, text, comment = None): """ Handle the result of a test with a "yes" or "no" result. "ret" is the return value: empty if OK, error message when not. "key" is the name of the symbol to be defined (HAVE_foo). "text" is the source code of the program used for testing. "comment" is the C comment to add above the line defining the symbol (the comment is automatically put inside a /* */). If None, no comment is added. """ if key: _Have(context, key, not ret, comment) if ret: context.Display("no\n") _LogFailed(context, text, ret) else: context.Display("yes\n") def _Have(context, key, have, comment = None): """ Store result of a test in context.havedict and context.headerfilename. "key" is a "HAVE_abc" name. It is turned into all CAPITALS and non- alphanumerics are replaced by an underscore. The value of "have" can be: 1 - Feature is defined, add "#define key". 0 - Feature is not defined, add "/* #undef key */". Adding "undef" is what autoconf does. Not useful for the compiler, but it shows that the test was done. number - Feature is defined to this number "#define key have". Doesn't work for 0 or 1, use a string then. string - Feature is defined to this string "#define key have". Give "have" as is should appear in the header file, include quotes when desired and escape special characters! """ key_up = string.upper(key) key_up = re.sub('[^A-Z0-9_]', '_', key_up) context.havedict[key_up] = have if have == 1: line = "#define %s 1\n" % key_up elif have == 0: line = "/* #undef %s */\n" % key_up elif type(have) == IntType: line = "#define %s %d\n" % (key_up, have) else: line = "#define %s %s\n" % (key_up, str(have)) if comment is not None: lines = "\n/* %s */\n" % comment + line else: lines = "\n" + line if context.headerfilename: f = open(context.headerfilename, "a") f.write(lines) f.close() elif hasattr(context,'config_h'): context.config_h = context.config_h + lines def _LogFailed(context, text, msg): """ Write to the log about a failed program. Add line numbers, so that error messages can be understood. """ if LogInputFiles: context.Log("Failed program was:\n") lines = string.split(text, '\n') if len(lines) and lines[-1] == '': lines = lines[:-1] # remove trailing empty line n = 1 for line in lines: context.Log("%d: %s\n" % (n, line)) n = n + 1 if LogErrorMessages: context.Log("Error message: %s\n" % msg) xorp/site_scons/config/__init__.py0000664000076400007640000000000011421137511017371 0ustar greearbgreearbxorp/site_scons/config/boost.pyc0000664000076400007640000000543711421141062017142 0ustar greearbgreearbÑò I¿DLc@s.ddkZd„Zd„Zdd„ZdS(iÿÿÿÿNcCsº|i}hdd6dd6dd6dd6d d 6d d 6d d6dd6}|i||dƒ}d||iddƒ}|id|gƒd|}|d7}|i|dƒotStS(Ns date_time.hppt date_timesfilesystem.hppt filesystemsiostreams/constants.hppt iostreamssprogram_options.hpptprogram_optionssregex/config.hpptregexs signal.hpptsignalsssystem/config.hpptsystems thread.hpptthreads.hpptboost_t boost_suffixttLIBSs& #include s1 int main() { } s.cpp(tenvtgett PrependUniquetTryLinktTruetFalse(tcontextt boost_libR t boost_headerst header_nametlibnamet test_program((s@/home/greearb/git/xorp.ct.github/xorp/site_scons/config/boost.pyt_CheckBoostLibs&   cCs…|id|ƒt||ƒ}| o4|iidƒ o d|id= %s... sChecking for Boost... s#include t.iiisq #if BOOST_VERSION < %d #error Boost version is too old! #endif i †ids0 int main() { } s.cppRR( RRtsplittintt ValueErrort IndexErrort TryCompileRR(Rtrequire_versionRRtversiontmajortminort sub_minor((s@/home/greearb/git/xorp.ct.github/xorp/site_scons/config/boost.pyt CheckBoostLs0      (tsysRRtNoneR*(((s@/home/greearb/git/xorp.ct.github/xorp/site_scons/config/boost.pyts  ! xorp/site_scons/config/member.pyc0000664000076400007640000001141011421141062017247 0ustar greearbgreearbÑò I¿DLc@srddkZddkZddklZddklZlZddd„Zdd„Z dd„Z d„Z dS(iÿÿÿÿN(tIntType(t LogInputFilestLogErrorMessagesc Cs|iod|i}nd}|p d}n|dj o/|djo"d}|id||fƒ|Sd}d}||} | dh|d6|d 6} |id |||fƒ|i| |ƒ} t|| d |d || d ||fƒd|_| S(sÖ Configure check for a C or C++ type "type_name" with "member_name". Optional "includes" can be defined to include a header file. "language" should be "C" or None. Default is "C". Sets HAVE_type_name in context.havedict according to the result. Note that this uses the current value of compiler and linker flags, make sure $CFLAGS, $CPPFLAGS and $LIBS are set correctly. Returns an empty string for success, an error message for failure. s #include "%s"ttCsUnsupported languagesCannot check for %s type: %s s.csx int main() { static %(type_name)s scons_aggr; if (sizeof (scons_aggr.%(member_name)s)) return 0; return 0; } t type_namet member_names-Checking whether %s type %s has member %s... tHAVE_t_s&Define to 1 if `%s' is member of `%s'.iN(theaderfilenametNonetDisplayt BuildProgt _YesNoResulttdid_show_result( tcontextRRtincludestlanguaget includetexttmsgtlangtsuffixtsrctret((sA/home/greearb/git/xorp.ct.github/xorp/site_scons/config/member.pytCheckTypeMembers.       cCsX|ot||| |ƒn|o!|idƒt|||ƒn|idƒdS(s¡ Handle the result of a test with a "yes" or "no" result. "ret" is the return value: empty if OK, error message when not. "key" is the name of the symbol to be defined (HAVE_foo). "text" is the source code of the program used for testing. "comment" is the C comment to add above the line defining the symbol (the comment is automatically put inside a /* */). If None, no comment is added. sno syes N(t_HaveR t _LogFailed(RRtkeyttexttcomment((sA/home/greearb/git/xorp.ct.github/xorp/site_scons/config/member.pyR Qs  cCs,ti|ƒ}tidd|ƒ}||i|<|djod|}nY|djod|}n>t|ƒtjod||f}nd|t|ƒf}|d j od ||}n d |}|i o-t |i d ƒ}|i |ƒ|i ƒn%t |d ƒo|i||_nd S(s& Store result of a test in context.havedict and context.headerfilename. "key" is a "HAVE_abc" name. It is turned into all CAPITALS and non- alphanumerics are replaced by an underscore. The value of "have" can be: 1 - Feature is defined, add "#define key". 0 - Feature is not defined, add "/* #undef key */". Adding "undef" is what autoconf does. Not useful for the compiler, but it shows that the test was done. number - Feature is defined to this number "#define key have". Doesn't work for 0 or 1, use a string then. string - Feature is defined to this string "#define key have". Give "have" as is should appear in the header file, include quotes when desired and escape special characters! s [^A-Z0-9_]Ris #define %s 1 is/* #undef %s */ s#define %s %d s#define %s %s s /* %s */ s tatconfig_hN(tstringtuppertretsubthavedictttypeRtstrR R topentwritetclosethasattrR(RRthaveRtkey_uptlinetlinestf((sA/home/greearb/git/xorp.ct.github/xorp/site_scons/config/member.pyRcs&       cCs®to‡|idƒti|dƒ}t|ƒo|ddjo|d }nd}x3|D]'}|id||fƒ|d}q_Wnto|id|ƒndS( sr Write to the log about a failed program. Add line numbers, so that error messages can be understood. sFailed program was: s iÿÿÿÿRis%d: %s sError message: %s N(RtLogR tsplittlenR(RRRR.tnR-((sA/home/greearb/git/xorp.ct.github/xorp/site_scons/config/member.pyRŒs ( R"R ttypesRtSCons.ConftestRRR RR RR(((sA/home/greearb/git/xorp.ct.github/xorp/site_scons/config/member.pyts   6  )xorp/site_scons/config/__init__.pyc0000664000076400007640000000023211421141062017537 0ustar greearbgreearbÑò I¿DLc@sdS(N((((sC/home/greearb/git/xorp.ct.github/xorp/site_scons/config/__init__.pytsxorp/site_scons/config/sysctl.py0000664000076400007640000001306111421137511017166 0ustar greearbgreearb# Copyright (c) 2009 XORP, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # $Id$ import re import string from types import IntType from SCons.Conftest import LogInputFiles, LogErrorMessages # TODO: Support C++. def CheckSysctl(context, sysctl_name, oid, includes = None, language = None): """ Configure check for a BSD-style sysctl with oid "oid", which must be quoted text. This is purely a compile-time check, not a run-time check. Optional "includes" can be defined to include a header file. "language" should be "C" or None. Default is "C". Sets HAVE_type_name in context.havedict according to the result. Note that this uses the current value of compiler and linker flags, make sure $CFLAGS, $CPPFLAGS and $LIBS are set correctly. Returns an empty string for success, an error message for failure. """ # Include "confdefs.h" first, so that the header can use HAVE_HEADER_H. if context.headerfilename: includetext = '#include "%s"' % context.headerfilename else: includetext = '' if not includes: includes = "" if language is not None and language != 'C': msg = 'Unsupported language' context.Display("Cannot check for sysctl %s: %s\n" % (sysctl_name, msg)) return msg lang = 'C' suffix = '.c' # XXX confdefs not picked up? how does it get specified? src = """ %(include)s #include #include #include #include %(header)s int main() { size_t buflen; int mib1[] = { %(oid)s }; if (sysctl(mib1, sizeof(mib1)/sizeof(mib1[0]), NULL, &buflen, NULL, 0) < 0) return (1); return (0); } """ % { 'include': includetext, 'header': includes, 'oid': oid } context.Display("Checking whether system has sysctl %s... " % sysctl_name) ret = context.BuildProg(src, suffix) _YesNoResult(context, ret, "HAVE_SYSCTL_" + sysctl_name, src, "Define to 1 if target has the `%s' sysctl." % sysctl_name) context.did_show_result = 1 # XXX return ret # # END OF PUBLIC FUNCTIONS # Following cloned from Conftest.py. # def _YesNoResult(context, ret, key, text, comment = None): """ Handle the result of a test with a "yes" or "no" result. "ret" is the return value: empty if OK, error message when not. "key" is the name of the symbol to be defined (HAVE_foo). "text" is the source code of the program used for testing. "comment" is the C comment to add above the line defining the symbol (the comment is automatically put inside a /* */). If None, no comment is added. """ if key: _Have(context, key, not ret, comment) if ret: context.Display("no\n") _LogFailed(context, text, ret) else: context.Display("yes\n") def _Have(context, key, have, comment = None): """ Store result of a test in context.havedict and context.headerfilename. "key" is a "HAVE_abc" name. It is turned into all CAPITALS and non- alphanumerics are replaced by an underscore. The value of "have" can be: 1 - Feature is defined, add "#define key". 0 - Feature is not defined, add "/* #undef key */". Adding "undef" is what autoconf does. Not useful for the compiler, but it shows that the test was done. number - Feature is defined to this number "#define key have". Doesn't work for 0 or 1, use a string then. string - Feature is defined to this string "#define key have". Give "have" as is should appear in the header file, include quotes when desired and escape special characters! """ key_up = string.upper(key) key_up = re.sub('[^A-Z0-9_]', '_', key_up) context.havedict[key_up] = have if have == 1: line = "#define %s 1\n" % key_up elif have == 0: line = "/* #undef %s */\n" % key_up elif type(have) == IntType: line = "#define %s %d\n" % (key_up, have) else: line = "#define %s %s\n" % (key_up, str(have)) if comment is not None: lines = "\n/* %s */\n" % comment + line else: lines = "\n" + line if context.headerfilename: f = open(context.headerfilename, "a") f.write(lines) f.close() elif hasattr(context,'config_h'): context.config_h = context.config_h + lines def _LogFailed(context, text, msg): """ Write to the log about a failed program. Add line numbers, so that error messages can be understood. """ if LogInputFiles: context.Log("Failed program was:\n") lines = string.split(text, '\n') if len(lines) and lines[-1] == '': lines = lines[:-1] # remove trailing empty line n = 1 for line in lines: context.Log("%d: %s\n" % (n, line)) n = n + 1 if LogErrorMessages: context.Log("Error message: %s\n" % msg) xorp/site_scons/config/endian.pyc0000664000076400007640000000311511421141062017241 0ustar greearbgreearbÑò I¿DLc@s(ddkZdZd„Zd„ZdS(iÿÿÿÿNs{Define to 1 if your processor stores words with the %s significant byte first (%s Motorola and SPARC, %s Intel and VAX).cCsédhdt6dt6|}thdt6dt6|hdt6dt6|hdt6dt6|f}d|i|s  xorp/site_scons/config/boost.py0000664000076400007640000000721211421137511016774 0ustar greearbgreearb#!/usr/bin/env python # # vim:set sts=4 ts=8 syntax=python: # # Copyright (c) 2009 XORP, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # $Id$ # Boost configure checks derived from Mapnik/Wesnoth (LGPL/GPL2) import sys def _CheckBoostLib(context, boost_lib): env = context.env boost_headers = { # library basename : include file "date_time" : "date_time.hpp", "filesystem" : "filesystem.hpp", "iostreams" : "iostreams/constants.hpp", "program_options" : "program_options.hpp", "regex" : "regex/config.hpp", "signals" : "signal.hpp", "system" : "system/config.hpp", "thread" : "thread.hpp", } header_name = boost_headers.get(boost_lib, boost_lib + ".hpp") libname = "boost_" + boost_lib + env.get("boost_suffix", "") # XXX prepending to global environment LIBS env.PrependUnique(LIBS = [libname]) test_program = """ #include \n""" % header_name test_program += """ int main() { } \n""" if context.TryLink(test_program, ".cpp"): return True return False def CheckBoostLibrary(context, boost_lib): """Check for a Boost library. XXX Assumes CPPPATH and LIBPATH contain boost path somewhere.""" context.Message("Checking for Boost %s library... " % boost_lib) check_result = _CheckBoostLib(context, boost_lib) # Try again with mt variant if no boost_suffix specified if not check_result and not context.env.get("boost_suffix"): context.env["boost_suffix"] = "-mt" check_result = _CheckBoostLib(context, boost_lib) if check_result: context.Result("yes") else: context.Result("no") return check_result def CheckBoost(context, require_version = None): """ Check for Boost itself, by checking for version.hpp. A version may or may not be specified. XXX Assumes CPPPATH and LIBPATH contain boost path somewhere.""" check_result = False if require_version: context.Message("Checking for Boost version >= %s... " % \ require_version) else: context.Message("Checking for Boost... ") test_program = "#include \n" if require_version: version = require_version.split(".", 2) major = int(version[0]) minor = int(version[1]) try: sub_minor = int(version[2]) except (ValueError, IndexError): sub_minor = 0 test_program += """ #if BOOST_VERSION < %d #error Boost version is too old! #endif """ % (major * 100000 + minor * 100 + sub_minor) test_program += """ int main() { } """ if context.TryCompile(test_program, ".cpp"): check_result = True if check_result: context.Result("yes") else: context.Result("no") return check_result xorp/site_scons/config/endian.py0000664000076400007640000000363011421137511017104 0ustar greearbgreearb# Copyright (c) 2009 XORP, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # $Id$ import sys _comment = """Define to 1 if your processor stores words with the %s significant byte first (%s Motorola and SPARC, %s Intel and VAX).""" def _WordsEndian(context, big): """ Store result of the autotools-style endian test in context.havedict and context.headerfilename. "big" is a Boolean which specifies if the target endianness is big. """ key = "WORDS_%sENDIAN" % {True:"BIG", False:"SMALL"}[big] comment = _comment % ({True:"most", False:"least"}[big], {True:"like", False:"unlike"}[big], {True:"unlike", False:"like"}[big]) context.havedict[key] = '' lines = "\n/* %s */\n" % comment lines += "#define %s\n" % key if context.headerfilename: f = open(context.headerfilename, "a") f.write(lines) f.close() elif hasattr(context,'config_h'): context.config_h = context.config_h + lines # FIXME not for cross-compiles. def CheckEndianness(context): context.Message("Checking whether byte ordering is bigendian... ") ret = (sys.byteorder != "little") context.Result({True:"yes", False:"no"}[ret]) _WordsEndian(context, ret) return ret xorp/tests/0000775000076400007640000000000011540225535013024 5ustar greearbgreearbxorp/tests/bgp/0000775000076400007640000000000011421137511013566 5ustar greearbgreearbxorp/tests/bgp/test_bgp_policy1.py0000775000076400007640000001204711421137511017416 0ustar greearbgreearb#!/usr/bin/env python # Copyright (c) 2001-2009 XORP, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # $XORP: xorp/tests/bgp/test_bgp_policy1.py,v 1.5 2008/10/02 21:58:31 bms Exp $ # # Test bgp interactions with policy # import sys sys.path.append("..") from test_main import test_main from test_main import coord from test_main import delay from test_builddir import builddir import test_bgp_config as config TESTS=[ # Fields: # 0: Symbolic name for test # 1: Actual test function # 2: True if this test works. # 3: Optional Configuration String # 4: Optional Configuration Functions # NOTE: One of field 3 or 4 must be set and the other must be empty. # ['test_import_med1', 'test_policy_med1', False, '', # ['conf_RUT_as2_TR1_as1_TR2_as2_TR3_as3', 'conf_interfaces', # 'conf_import_med_change']], ['test_export_med1', 'test_policy_med1', True, '', ['conf_RUT_as2_TR1_as1_TR2_as1_TR3_as3', 'conf_interfaces', 'conf_export_med_change']], ['test_import_origin1', 'test_policy_origin1', True, '', ['conf_RUT_as2_TR1_as1_TR2_as1_TR3_as3', 'conf_interfaces', 'conf_import_origin_change']], ['test_export_origin1', 'test_policy_origin1', True, '', ['conf_RUT_as2_TR1_as1_TR2_as1_TR3_as3', 'conf_interfaces', 'conf_export_origin_change']], ] def test_policy_med1(): """ Introduce a med of 0 and expect a med of 2 at the peers. Allows the testing of import and export policies to change the med. """ coord("reset") coord("target 127.0.0.1 10001") coord("initialise attach peer1") coord("target 127.0.0.1 10002") coord("initialise attach peer2") coord("target 127.0.0.1 10003") coord("initialise attach peer3") coord("peer1 establish AS 1 holdtime 0 id 10.0.0.1 keepalive false") coord("peer2 establish AS 1 holdtime 0 id 10.0.0.2 keepalive false") coord("peer3 establish AS 3 holdtime 0 id 10.0.0.3 keepalive false") delay(2) coord("peer1 assert established"); coord("peer2 assert established"); coord("peer3 assert established"); delay(2) packet = "packet update \ nexthop %s \ origin 0 \ aspath %s \ med %s \ nlri 192.1.0.0/16" spacket = packet % ("127.0.0.2", "1", "0") # The nexthop is not re-written as it is on a common subnet. epacket = packet % ("127.0.0.2", "2,1", "42") coord("peer1 expect %s" % epacket) coord("peer2 expect %s" % epacket) coord("peer3 expect %s" % epacket) delay(2) coord("peer2 send %s" % spacket) delay(10) coord("peer1 assert established") coord("peer2 assert established") coord("peer3 assert established") coord("peer1 assert queue 1") coord("peer2 assert queue 1") coord("peer3 assert queue 0") return True def test_policy_origin1(): """ Introduce an origin of 0 and expect and origin of 2 at the peers. Allows the testing of import and export policies to change the origin. """ coord("reset") coord("target 127.0.0.1 10001") coord("initialise attach peer1") coord("target 127.0.0.1 10002") coord("initialise attach peer2") coord("target 127.0.0.1 10003") coord("initialise attach peer3") coord("peer1 establish AS 1 holdtime 0 id 10.0.0.1 keepalive false") coord("peer2 establish AS 1 holdtime 0 id 10.0.0.2 keepalive false") coord("peer3 establish AS 3 holdtime 0 id 10.0.0.3 keepalive false") delay(2) coord("peer1 assert established"); coord("peer2 assert established"); coord("peer3 assert established"); delay(2) packet = "packet update \ nexthop %s \ origin %s \ aspath %s \ med 0 \ nlri 192.1.0.0/16" spacket = packet % ("127.0.0.2", "0", "1") # The nexthop is not re-written as it is on a common subnet. epacket = packet % ("127.0.0.2", "2", "2,1") coord("peer1 expect %s" % epacket) coord("peer2 expect %s" % epacket) coord("peer3 expect %s" % epacket) delay(2) coord("peer2 send %s" % spacket) delay(10) coord("peer1 assert established") coord("peer2 assert established") coord("peer3 assert established") coord("peer1 assert queue 1") coord("peer2 assert queue 1") coord("peer3 assert queue 0") return True test_main(TESTS, 'test_bgp_config', 'test_bgp_policy1') # Local Variables: # mode: python # py-indent-offset: 4 # End: xorp/tests/bgp/test_bgp_dump1.py0000775000076400007640000000563411421137511017070 0ustar greearbgreearb#!/usr/bin/env python # Copyright (c) 2001-2009 XORP, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # $XORP: xorp/tests/bgp/test_bgp_dump1.py,v 1.4 2008/10/02 21:58:31 bms Exp $ # Test to see if BGP is correctly dumping routes when new sessions are formed. import sys import os import time sys.path.append("..") from test_main import test_main from test_main import coord,pending,status from test_main import delay from test_builddir import builddir import test_bgp_config as config TESTS=[ # Fields: # 0: Symbolic name for test # 1: Actual test function # 2: True if this test works. # 3: Optional Configuration String # 4: Optional Configuration Functions # NOTE: One of field 3 or 4 must be set and the other must be empty. ['test_dump_1', 'test_dump_1', True, '', ['conf_test_dump_1', 'conf_interfaces']], ] def bgp_peer_unchanged(peer): """ Wait for this peer to become quiet """ while True: s1 = status(peer) print s1, time.sleep(10) s2 = status(peer) print s2, if s1 == s2: break def test_dump_1(): """ Test that new sessions correctly receive all the routes that they should. Peer1 populated with routes. Peer2 repeatedly connects to verify that the BGP dump code is working correctly """ coord("reset") coord("target 127.0.0.1 10001") coord("initialise attach peer1") coord("peer1 establish AS 65000 holdtime 0 id 75.75.75.75 keepalive false") delay(2) coord("peer1 assert established") delay(2) coord("peer1 send dump mrtd update ../../data/bgp/icsi1.mrtd", \ noblock=True) bgp_peer_unchanged("peer1") coord("target 127.0.0.1 10002") coord("initialise attach peer2") for i in range(10): os.system("date") print "Iteration: ", i coord("peer2 establish AS 65001 holdtime 0 id 1.1.1.1 keepalive false",noblock=True) delay(2) bgp_peer_unchanged("peer2") coord("peer2 assert established") coord("peer2 disconnect") return True test_main(TESTS, 'test_bgp_config', 'test_bgp_dump1') # Local Variables: # mode: python # py-indent-offset: 4 # End: xorp/tests/bgp/test_bgp_reports1.py0000775000076400007640000002077411421137511017623 0ustar greearbgreearb#!/usr/bin/env python # Copyright (c) 2001-2009 XORP, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # $XORP: xorp/tests/bgp/test_bgp_reports1.py,v 1.11 2008/10/02 21:58:31 bms Exp $ # Tests used to investigate bug reports. import sys sys.path.append("..") from test_main import test_main from test_main import coord from test_main import delay from test_builddir import builddir import test_bgp_config as config TESTS=[ # Fields: # 0: Symbolic name for test # 1: Actual test function # 2: True if this test works. # 3: Optional Configuration String # 4: Optional Configuration Functions # NOTE: One of field 3 or 4 must be set and the other must be empty. ['test_bug_360', 'test_bug_360', True, '', ['conf_bug_360', 'conf_interfaces', 'conf_redist_static']], ['test_bug_639', 'test_bug_639', True, '', ['conf_EBGP', 'conf_interfaces', 'conf_create_protocol_static']], ['test_bug_649', 'test_bug_649', True, '', ['conf_EBGP', 'conf_interfaces', 'conf_create_protocol_static']], ['test_bug_740', 'test_bug_740', True, '', ['conf_bug_740', 'conf_interfaces']], ] def test_bug_360(): """ http://www.xorp.org/bugzilla/show_bug.cgi?id=360 The bug report stated that the output from the bgp show route command can vary. This test populates the routing table with similar values and repeats the show command a number of times. NOTE: No problem was ever found. """ coord("reset") coord("target 127.0.0.1 10001") coord("initialise attach peer1") coord("peer1 establish AS 75 holdtime 0 id 75.75.75.75 keepalive false") delay(2) coord("peer1 assert established"); delay(2) packet = "packet update \ nexthop 127.0.0.2 \ origin %s \ aspath %s \ nlri %s" incomplete = "2" coord("peer1 send %s" % (packet % (incomplete, "75,50,25", "25.25.25.0/24"))) coord("peer1 send %s" % (packet % (incomplete, "75,50,25", "25.25.0.0/16"))) coord("peer1 send %s" % (packet % (incomplete, "75,50,25", "25.0.0.0/8"))) coord("peer1 send %s" % (packet % (incomplete, "75", "75.75.75.0/24"))) coord("peer1 send %s" % (packet % (incomplete, "75", "75.75.0.0/16"))) coord("peer1 send %s" % (packet % (incomplete, "75", "75.70.0.0/8"))) if not config.conf_add_static_route4(builddir(1), "100.100.100.0/24"): return False if not config.conf_add_static_route4(builddir(1), "100.100.0.0/16"): return False if not config.conf_add_static_route4(builddir(1), "100.0.0.0/8"): return False reference_output = \ """*> 100.100.100.0/24 127.0.0.1 0.0.0.0 i *> 100.100.0.0/16 127.0.0.1 0.0.0.0 i *> 100.0.0.0/8 127.0.0.1 0.0.0.0 i *> 25.25.25.0/24 127.0.0.2 75.75.75.75 75 50 25 ? *> 25.25.0.0/16 127.0.0.2 75.75.75.75 75 50 25 ? *> 25.0.0.0/8 127.0.0.2 75.75.75.75 75 50 25 ? *> 75.75.75.0/24 127.0.0.2 75.75.75.75 75 ? *> 75.75.0.0/16 127.0.0.2 75.75.75.75 75 ? *> 75.0.0.0/8 127.0.0.2 75.75.75.75 75 ? """ for l in range(100): result, output = config.show_bgp_routes(builddir(1)) import string # Looks like the xorpsh inserts carriage returns. output = output.replace('\r', '') # XXX # This might be a problem on windows, in later installations of python # we can use splitlines. lines = string.split(output, '\n') output = "" for i in lines: if i and i[0] == '*': output += i + '\n' if reference_output != output: reference = '/tmp/reference' actual = '/tmp/actual' print 'Output did now match check %s and %s' % (reference, actual) file = open(reference, 'w') file.write(reference_output) file.close() file = open(actual, 'w') file.write(output) file.close() print '<@' + reference_output + '@>' print "===" print '<@' + output + '@>' return False return True def test_bug_639(): """ http://www.xorp.org/bugzilla/show_bug.cgi?id=639 BGP and STATIC install the same route, this route also resolved the nexthop. The introduction of the policy to redistribute static triggers the problem in the RIB. This emulates the order of events when the router is being configured and a peering comes up. """ coord("reset") coord("target 127.0.0.1 10001") coord("initialise attach peer1") coord("peer1 establish AS 65001 holdtime 0 id 1.2.3.4 keepalive false") delay(2) coord("peer1 assert established"); incomplete = "2" packet = "packet update \ nexthop %s \ origin " + incomplete + "\ aspath 65001 \ med 0 \ nlri %s" if not config.conf_add_static_route4(builddir(1), "192.168.0.0/16"): return False delay(2) coord("peer1 send %s" % (packet % ("192.168.0.1", "192.168.0.0/16"))) if not config.conf_redist_static(builddir(1), False): return False delay(5) coord("peer1 assert established"); return True def test_bug_649(): """ http://www.xorp.org/bugzilla/show_bug.cgi?id=649 Trigger a problem caused by BGP receiving the "route_info_changed" XRL. 1) A default static route is required with a metric of 1 2) Add a route that does not cover the nexthop the route should reduce the coverage of the default route. Causing the RIB to send an "route_info_invalid" XRL to sent to BGP. 3) Install a route that exactly matches the range covered by the RIB causing a "route_info_changed" XRL to be sent to BGP. Note that at the time of writing BGP installed all routes with a metric of 0 and the static route has a metric of 1. Only if the routes match and the metric changes should there be an upcall. """ coord("reset") coord("target 127.0.0.1 10001") coord("initialise attach peer1") coord("peer1 establish AS 65001 holdtime 0 id 1.2.3.4 keepalive false") delay(2) coord("peer1 assert established"); # Install the default route. if not config.conf_add_static_route4(builddir(1), "0.0.0.0/0"): return False delay(2) packet = "packet update \ nexthop 10.0.0.1\ origin 0 \ aspath 65001 \ nlri %s" # Send in a route that does not cover the nexthop. coord("peer1 send %s" % (packet % ("10.1.0.0/20"))) # Send in a route that covers the nexthop and matches the next hop resolver # value. coord("peer1 send %s" % (packet % ("10.0.0.0/16"))) coord("peer1 assert established"); return True def test_bug_740(): """ http://www.xorp.org/bugzilla/show_bug.cgi?id=740 This test triggers a simple problem when there is both an import and an export policy. If the import policy uses the neighbor statement then BGP fails. The connection is established to verify that BGP is still alive. """ # Make a connection just to see if BGP is still alive coord("reset") coord("target 127.0.0.1 10001") coord("initialise attach peer1") # Put an extra delay here so the TCP connection doesn't occur at the same # time as the peer is bounced due to the interface address changing. delay(2) coord("peer1 establish AS 75 holdtime 0 id 75.75.75.75 keepalive false") delay(2) coord("peer1 assert established"); return True test_main(TESTS, 'test_bgp_config', 'test_bgp_reports1') # Local Variables: # mode: python # py-indent-offset: 4 # End: xorp/tests/bgp/test_unh1.py0000775000076400007640000005364311421137511016070 0ustar greearbgreearb#!/usr/bin/env python # Copyright (c) 2001-2009 XORP, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # $XORP: xorp/tests/bgp/test_unh1.py,v 1.26 2008/10/02 21:58:31 bms Exp $ # # The tests in this file are based on the: # University of New Hampshire # InterOperability Laboratory # IPv4 CONSORTIUM # Border Gateway Protocol 4 (BGP) Operations Test Suite # Technical Document Revision 3.3 # import sys sys.path.append("..") from test_main import test_main from test_main import coord from test_main import delay from test_builddir import builddir import test_bgp_config as config TESTS=[ # Fields: # 0: Symbolic name for test # 1: Actual test function # 2: True if this test works. # 3: Optional Configuration String # 4: Optional Configuration Functions # NOTE: One of field 3 or 4 must be set and the other must be empty. ['1.1A', 'test1_1_A', True, '', ['conf_IBGP']], ['1.1B', 'test1_1_B', True, '', ['conf_EBGP']], ['1.4C', 'test1_4_C', True, '', ['conf_EBGP', 'conf_interfaces']], ['1.6B', 'test1_6_B', True, '', ['conf_EBGP', 'conf_interfaces']], ['1.10C', 'test1_10_C', True, '', ['conf_EBGP_IBGP_IBGP', 'conf_interfaces', 'conf_redist_static_incomplete']], ['1.12AB', 'test1_12_AB', True, '', ['conf_RUT_as2_TR1_as1_TR2_as2', 'conf_interfaces', 'conf_redist_static']], ['1.13A', 'test1_13_A', True, '', ['conf_RUT_as2_TR1_as1_TR2_as2', 'conf_interfaces', 'conf_redist_static_med']], ['1.13B', 'test1_13_B', True, '', ['conf_RUT_as2_TR1_as1_TR2_as2', 'conf_interfaces']], ['1.13C', 'test1_13_C', True, '', ['conf_RUT_as2_TR1_as1_TR2_as2', 'conf_interfaces']], ['1.15A', 'test1_15_A', True, '', ['conf_RUT_as3_TR1_as1_TR2_as2_TR3_as4', 'conf_interfaces']], ['1.15B', 'test1_15_B', True, '', ['conf_RUT_as3_TR1_as1_TR2_as2_TR3_as4', 'conf_interfaces', 'conf_aggregate_brief']], ['3.8A', 'test3_8_A', True, '', ['conf_EBGP']], ['4.6A', 'test4_6_A', True, '', ['conf_EBGP_EBGP', 'conf_interfaces', 'conf_redist_static_no_export']], ['4.8A', 'test4_8_A', True, '', ['conf_RUT_as2_TR1_as1', 'conf_interfaces', 'conf_redist_static', 'conf_multiprotocol']], ['4.11A', 'test4_11_A', True, '', ['conf_RUT_as2_TR1_as1_TR2_as1_TR3_as3', 'conf_interfaces', 'conf_damping']], ['4.11BCD', 'test4_11_BCD', True, '', ['conf_RUT_as2_TR1_as1_TR2_as1_TR3_as3', 'conf_interfaces', 'conf_preference_TR1', 'conf_damping']], ['4.12', 'test4_12', True, '', ['conf_RUT_as2_TR1_as1_TR2_as3', 'conf_interfaces', 'conf_damping']], ] def test1_1_A(): """ Direct connection Basic IBGP """ # configure the test peering. coord("reset") coord("target 127.0.0.1 10001") coord("initialise attach peer1") coord("peer1 establish AS 65000 holdtime 0 id 1.2.3.4 keepalive false") delay(2) coord("peer1 assert established"); return True def test1_1_B(): """ Direct connection Basic EBGP """ # configure the test peering. coord("reset") coord("target 127.0.0.1 10001") coord("initialise attach peer1") coord("peer1 establish AS 65001 holdtime 0 id 1.2.3.4 keepalive false") delay(2) coord("peer1 assert established"); return True def test1_4_C(): """ Set the holdtime to an illegal value of 2 seconds """ # Try and set the holdtime to 2 seconds which is illegal. try: if not config.conf_set_holdtime(builddir(1), "127.0.0.1", 2): return True except Exception, (ErrorMessage): print ErrorMessage return True return False def test1_6_B(): """ Cease notification message """ # Set the prefix limit to 3 config.conf_set_prefix_limit(builddir(1), "peer1", 3) coord("reset") coord("target 127.0.0.1 10001") coord("initialise attach peer1") coord("peer1 establish AS 65001 holdtime 0 id 1.2.3.4 keepalive false") delay(2) coord("peer1 assert established"); packet = "packet update \ nexthop 127.0.0.2 \ origin 0 \ aspath 65001 \ med 0\ nlri %s" cease = 6 coord("peer1 expect packet notify %s" % cease) # Send four NRLIs to trigger a cease coord("peer1 send %s" % (packet % "172.16.0.0/16")) coord("peer1 send %s" % (packet % "172.16.0.0/17")) coord("peer1 send %s" % (packet % "172.16.0.0/18")) coord("peer1 send %s" % (packet % "172.16.0.0/19")) delay(2) coord("peer1 assert idle") coord("peer1 assert queue 0") return True def test1_10_C(): """ To verify that a BGP router properly generates the ORIGIN attribute """ coord("reset") coord("target 127.0.0.1 10001") coord("initialise attach peer1") coord("target 127.0.0.1 10002") coord("initialise attach peer2") coord("target 127.0.0.1 10003") coord("initialise attach peer3") coord("peer1 establish AS 65001 holdtime 0 id 10.0.0.1 keepalive false") coord("peer2 establish AS 65000 holdtime 0 id 10.0.0.2 keepalive false") coord("peer3 establish AS 65000 holdtime 0 id 10.0.0.3 keepalive false") delay(2) coord("peer1 assert established"); coord("peer2 assert established"); coord("peer3 assert established"); delay(2) incomplete = 2 coord("peer1 expect packet update \ nexthop 127.0.0.1 \ origin %s \ aspath 65000 \ med 0 nlri \ 172.16.0.0/16" % incomplete) if not config.conf_add_static_route4(builddir(1), "172.16.0.0/16"): return False delay(10) coord("peer1 assert established") coord("peer2 assert established") coord("peer3 assert established") coord("peer1 assert queue 0") return True def test1_12_AB(): """ Check the rewriting of the NEXT_HOP attribute. """ coord("reset") coord("target 127.0.0.1 10001") coord("initialise attach peer1") coord("target 127.0.0.1 10002") coord("initialise attach peer2") coord("peer1 establish AS 1 holdtime 0 id 10.0.0.1 keepalive false") coord("peer2 establish AS 2 holdtime 0 id 10.0.0.2 keepalive false") delay(2) coord("peer1 assert established"); coord("peer2 assert established"); delay(2) packet = "packet update \ nexthop 127.0.0.2 \ origin 0 \ nlri 172.16.0.0/16 %s" coord("peer1 expect %s" % (packet % "aspath 2 med 0")) coord("peer2 expect %s" % (packet % "aspath empty localpref 100")) if not config.conf_add_static_route4(builddir(1), "172.16.0.0/16", "127.0.0.2"): return False delay(10) coord("peer1 assert established") coord("peer2 assert established") coord("peer1 assert queue 0") coord("peer2 assert queue 0") return True def test1_13_A(): """ MULTI_EXIT_DISC Attribute """ coord("reset") coord("target 127.0.0.1 10001") coord("initialise attach peer1") coord("target 127.0.0.1 10002") coord("initialise attach peer2") coord("peer1 establish AS 1 holdtime 0 id 10.0.0.1 keepalive false") coord("peer2 establish AS 2 holdtime 0 id 10.0.0.2 keepalive false") packet1 = "packet update \ nexthop 127.0.0.1 \ origin 0 \ aspath 2 \ med 42 \ nlri 172.16.0.0/16" packet2 = "packet update \ nexthop 127.0.0.1 \ origin 0 \ aspath empty \ localpref 100 \ nlri 172.16.0.0/16" coord("peer1 expect %s" % packet1) coord("peer2 expect %s" % packet2) if not config.conf_add_static_route4(builddir(1), "172.16.0.0/16"): return False delay(10) coord("peer1 assert established") coord("peer2 assert established") coord("peer1 assert queue 0") coord("peer2 assert queue 0") return True def test1_13_B(): """ MULTI_EXIT_DISC Attribute """ coord("reset") coord("target 127.0.0.1 10001") coord("initialise attach peer1") coord("target 127.0.0.1 10002") coord("initialise attach peer2") coord("peer1 establish AS 1 holdtime 0 id 10.0.0.1 keepalive false") coord("peer2 establish AS 2 holdtime 0 id 10.0.0.2 keepalive false") packet = "packet update \ nexthop 127.0.0.2 \ origin 0 \ aspath 1 \ %s \ nlri 172.16.0.0/16" spacket = packet % "med 42" epacket = packet % "localpref 100" coord("peer1 expect %s" % epacket) coord("peer2 expect %s" % epacket) coord("peer1 send %s" % spacket) delay(10) coord("peer1 assert established") coord("peer2 assert established") coord("peer1 assert queue 1") coord("peer2 assert queue 0") return True def test1_13_C(): """ MULTI_EXIT_DISC Attribute """ coord("reset") coord("target 127.0.0.1 10001") coord("initialise attach peer1") coord("target 127.0.0.1 10002") coord("initialise attach peer2") coord("peer1 establish AS 1 holdtime 0 id 10.0.0.1 keepalive false") coord("peer2 establish AS 2 holdtime 0 id 10.0.0.2 keepalive false") packet = "packet update \ origin 0 \ %s \ nlri 172.16.0.0/16" spacket = packet % "nexthop 127.0.0.2 aspath empty med 42" # The nexthop is not re-written as it is on a common subnet. epacket = packet % "nexthop 127.0.0.2 aspath 2 med 0" coord("peer1 expect %s" % epacket) coord("peer2 expect %s" % epacket) coord("peer2 send %s" % spacket) delay(10) coord("peer1 assert established") coord("peer2 assert established") coord("peer1 assert queue 0") coord("peer2 assert queue 1") return True def test1_15_A(): """ ATOMIC_AGGREGATE Attribute """ coord("reset") coord("target 127.0.0.1 10001") coord("initialise attach peer1") coord("target 127.0.0.1 10002") coord("initialise attach peer2") coord("target 127.0.0.1 10003") coord("initialise attach peer3") delay(2) coord("peer1 establish AS 1 holdtime 0 id 10.0.0.1 keepalive false") coord("peer2 establish AS 2 holdtime 0 id 10.0.0.2 keepalive false") coord("peer3 establish AS 4 holdtime 0 id 10.0.0.3 keepalive false") packet1 = "packet update \ nexthop 127.0.0.2 \ origin 0 \ aspath %s \ med 0 \ nlri 192.0.0.0/8" packet2 = "packet update \ nexthop 127.0.0.2 \ origin 0 \ aspath %s \ med 0 \ nlri 192.1.0.0/16" spacket1 = packet1 % "1" spacket2 = packet2 % "2" epacket1 = packet1 % "3,1" epacket2 = packet2 % "3,2" coord("peer1 expect %s" % epacket2) coord("peer2 expect %s" % epacket1) coord("peer3 expect %s" % epacket1) coord("peer3 expect %s" % epacket2) coord("peer1 send %s" % spacket1) coord("peer2 send %s" % spacket2) delay(10) coord("peer1 assert queue 0") coord("peer2 assert queue 0") coord("peer3 assert queue 0") coord("peer1 assert established") coord("peer2 assert established") coord("peer3 assert established") return True def test1_15_B(): """ ATOMIC_AGGREGATE Attribute """ coord("reset") coord("target 127.0.0.1 10001") coord("initialise attach peer1") coord("target 127.0.0.1 10002") coord("initialise attach peer2") coord("target 127.0.0.1 10003") coord("initialise attach peer3") delay(2) coord("peer1 establish AS 1 holdtime 0 id 10.0.0.1 keepalive false") coord("peer2 establish AS 2 holdtime 0 id 10.0.0.2 keepalive false") coord("peer3 establish AS 4 holdtime 0 id 10.0.0.3 keepalive false") packet1 = "packet update \ nexthop 127.0.0.2 \ origin 0 \ aspath %s \ med 0 \ nlri 192.0.0.0/8" packet2 = "packet update \ nexthop 127.0.0.2 \ origin 0 \ aspath %s \ med 0 \ nlri 192.1.0.0/16" spacket1 = packet1 % "1" spacket2 = packet2 % "2" # # XXX # This test is not complete the expect packets should check for: # Atomic Aggregate Attribute and Aggregator Attribute AS/3 10.0.0.1 # # epacket1 = packet1 % "3,1" # epacket2 = packet2 % "3,2" # coord("peer1 expect %s" % epacket2) # coord("peer2 expect %s" % epacket1) # coord("peer3 expect %s" % epacket1) # coord("peer3 expect %s" % epacket2) coord("peer1 send %s" % spacket1) coord("peer2 send %s" % spacket2) delay(10) coord("peer1 trie recv lookup 192.1.0.0/8 aspath 3") coord("peer1 trie recv lookup 192.1.0.0/16 not") coord("peer2 trie recv lookup 192.1.0.0/8 aspath 3") coord("peer2 trie recv lookup 192.1.0.0/16 not") coord("peer3 trie recv lookup 192.1.0.0/8 aspath 3") coord("peer3 trie recv lookup 192.1.0.0/16 not") # coord("peer1 assert queue 0") # coord("peer2 assert queue 0") # coord("peer3 assert queue 0") coord("peer1 assert established") coord("peer2 assert established") coord("peer3 assert established") return True def test3_8_A(): """ To verify that correct handling of NLRI field errors in UPDATE messages """ print "Manual checks" print "1) Verify that the multicast NLRI generated an error message" print "2) Verify the route has not been installed" # configure the test peering. coord("reset") coord("target 127.0.0.1 10001") coord("initialise attach peer1") coord("peer1 establish AS 65001 holdtime 0 id 1.2.3.4 keepalive false") delay(2) coord("peer1 assert established"); bad_packet = "packet update \ origin 0 \ aspath 65001 \ nexthop 127.0.0.1 \ nlri 172.16.0.0/16 \ nlri 224.0.0.5/16" coord("peer1 send %s" % bad_packet) delay(10) coord("peer1 assert established") return True def test4_6_A(): """ Verify that the NO_EXPORT community can be set on a redistributed route """ coord("reset") coord("target 127.0.0.1 10001") coord("initialise attach peer1") coord("target 127.0.0.1 10002") coord("initialise attach peer2") coord("peer1 establish AS 65001 holdtime 0 id 10.0.0.1 keepalive false") coord("peer2 establish AS 65002 holdtime 0 id 10.0.0.2 keepalive false") delay(2) coord("peer1 assert established"); coord("peer2 assert established"); delay(2) packet = "packet update \ nexthop 127.0.0.1 \ origin 0 \ aspath 65000 \ med 0 \ nlri 172.16.0.0/16 \ community NO_EXPORT" coord("peer1 expect %s" % packet) coord("peer2 expect %s" % packet) if not config.conf_add_static_route4(builddir(1), "172.16.0.0/16"): return False delay(10) coord("peer1 assert established") coord("peer2 assert established") coord("peer1 assert queue 0") coord("peer2 assert queue 0") return True def test4_8_A(): """ Verify correct operation of the multiprotocol extension """ coord("reset") coord("target 127.0.0.1 10001") coord("initialise attach peer1") coord("peer1 establish AS 1 holdtime 0 id 10.0.0.1 keepalive false") delay(2) coord("peer1 assert established"); delay(2) # packet = "packet update \ # nexthop 127.0.0.1 \ # origin 0 \ # aspath 65000 \ # med 0 \ # nlri 172.16.0.0/16 \ # community NO_EXPORT" # coord("peer1 expect %s" % packet) if not config.conf_add_static_route4(builddir(1), "16.0.0.0/4"): return False delay(10) coord("peer1 assert established") coord("peer1 assert queue 0") return True def test4_11_A(): """ Test route flap damping """ coord("reset") coord("target 127.0.0.1 10001") coord("initialise attach peer1") coord("target 127.0.0.1 10002") coord("initialise attach peer2") coord("target 127.0.0.1 10003") coord("initialise attach peer3") coord("peer1 establish AS 1 holdtime 0 id 10.0.0.1 keepalive false") coord("peer2 establish AS 1 holdtime 0 id 10.0.0.2 keepalive false") coord("peer3 establish AS 3 holdtime 0 id 10.0.0.3 keepalive false") delay(2) coord("peer1 assert established"); coord("peer2 assert established"); coord("peer3 assert established"); delay(2) packet = "packet update \ nexthop %s \ origin 0 \ aspath %s \ med 0 \ nlri 192.1.0.0/16" spacket = packet % ("127.0.0.2", "1") # The nexthop is not re-written as it is on a common subnet. epacket = packet % ("127.0.0.2", "2,1") coord("peer1 expect %s" % epacket) coord("peer2 expect %s" % epacket) coord("peer3 expect %s" % epacket) delay(2) coord("peer1 send %s" % spacket) delay(10) coord("peer1 assert established") coord("peer2 assert established") coord("peer3 assert established") coord("peer1 assert queue 1") coord("peer2 assert queue 1") coord("peer3 assert queue 0") return True def test4_11_BCD(): """ Test route flap damping """ coord("reset") coord("target 127.0.0.1 10001") coord("initialise attach peer1") coord("target 127.0.0.1 10002") coord("initialise attach peer2") coord("target 127.0.0.1 10003") coord("initialise attach peer3") coord("peer1 establish AS 1 holdtime 0 id 10.0.0.1 keepalive false") coord("peer2 establish AS 1 holdtime 0 id 10.0.0.2 keepalive false") coord("peer3 establish AS 3 holdtime 0 id 10.0.0.3 keepalive false") delay(2) coord("peer1 assert established"); coord("peer2 assert established"); coord("peer3 assert established"); delay(2) packet = "packet update \ nexthop %s \ origin 0 \ aspath %s \ med 0 \ nlri 192.1.0.0/16" spacket = packet % ("127.0.0.3", "1") # The nexthop is not re-written as it is on a common subnet. epacket2 = packet % ("127.0.0.2", "2,1") epacket3 = packet % ("127.0.0.3", "2,1") coord("peer1 expect %s" % epacket3) coord("peer2 expect %s" % epacket3) coord("peer3 expect %s" % epacket3) delay(2) # 9. TR2 Sends an update message with a route of 192.1.0.0/16 ... coord("peer2 send %s" % spacket) coord("peer1 assert established") coord("peer2 assert established") coord("peer3 assert established") coord("peer1 assert queue 1") coord("peer2 assert queue 1") coord("peer3 assert queue 0") spacket = packet % ("127.0.0.2", "1") coord("peer3 expect %s" % epacket2) coord("peer3 expect %s" % epacket3) coord("peer3 expect %s" % epacket2) coord("peer3 expect %s" % epacket3) delay(2) # Flap the route from peer1 spacket = packet % ("127.0.0.2", "1") coord("peer1 send %s" % spacket) coord("peer1 send packet update withdraw 192.1.0.0/16") coord("peer1 send %s" % spacket) coord("peer1 send packet update withdraw 192.1.0.0/16") delay(2) coord("peer1 assert established") coord("peer2 assert established") coord("peer3 assert established") coord("peer1 assert queue 1") coord("peer2 assert queue 1") coord("peer3 assert queue 0") # Part C coord("peer1 send %s" % spacket) coord("peer1 send packet update withdraw 192.1.0.0/16") coord("peer1 send %s" % spacket) # coord("peer1 send packet update withdraw 192.1.0.0/16") # Part D delay(10) # The release of the damped packet. coord("peer3 expect %s" % epacket2) sleep = 5 * 60 print 'Sleeping for %d seconds, waiting for damped route' % sleep delay(sleep) # Make sure that at the end of the test all the connections still exist. coord("peer1 assert established") coord("peer2 assert established") coord("peer3 assert established") coord("peer1 assert queue 1") coord("peer2 assert queue 1") coord("peer3 assert queue 0") return True def test4_12(): """ Test route flap damping """ coord("reset") coord("target 127.0.0.1 10001") coord("initialise attach peer1") coord("target 127.0.0.1 10002") coord("initialise attach peer2") coord("peer1 establish AS 1 holdtime 0 id 10.0.0.1 keepalive false") coord("peer2 establish AS 3 holdtime 0 id 10.0.0.2 keepalive false") delay(2) coord("peer1 assert established"); coord("peer2 assert established"); packet = "packet update \ nexthop %s \ origin 0 \ aspath %s \ med 0 \ nlri 192.1.0.0/16" spacket1 = packet % ("127.0.0.3", "1,12") spacket2 = packet % ("127.0.0.3", "1,14") # The nexthop is not re-written as it is on a common subnet. epacket1 = packet % ("127.0.0.3", "2,1,12") epacket2 = packet % ("127.0.0.3", "2,1,14") # This is a hack to test that no packets arrive on this peer. coord("peer1 expect %s" % spacket1) coord("peer2 expect %s" % epacket1) coord("peer2 expect %s" % epacket2) coord("peer2 send packet update withdraw 192.1.0.0/16") for i in range(5): coord("peer1 send %s" % spacket1) coord("peer1 send %s" % spacket2) delay(10) coord("peer1 assert established") coord("peer2 assert established") coord("peer1 assert queue 1") coord("peer2 assert queue 0") # The release of the damped packet. coord("peer2 expect %s" % epacket2) sleep = 5 * 60 print 'Sleeping for %d seconds, waiting for damped route' % sleep delay(sleep) # Make sure that at the end of the test all the connections still exist. coord("peer1 assert established") coord("peer2 assert established") coord("peer1 assert queue 1") coord("peer2 assert queue 0") return True test_main(TESTS, 'test_bgp_config', 'test_unh1') # Local Variables: # mode: python # py-indent-offset: 4 # End: xorp/tests/bgp/test_bgp_config.py0000664000076400007640000004134511421137511017303 0ustar greearbgreearb#!/usr/bin/env python # Copyright (c) 2001-2009 XORP, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # $XORP: xorp/tests/bgp/test_bgp_config.py,v 1.24 2008/10/02 21:58:31 bms Exp $ import sys sys.path.append("..") from test_xorpsh import xorpsh LOCALHOST="127.0.0.1" AS=65000 # IBGP AS_LOCAL_1=AS AS_PEER_1=AS PORT_LOCAL_1=10001 PORT_PEER_1=20001 def conf_interfaces(builddir): """ Configure an interface """ # Configure the xorpsh xorpsh_commands = \ """ configure create interfaces edit interfaces create interface fxp0 edit interface fxp0 create vif fxp0 edit vif fxp0 create address 127.0.0.1 edit address 127.0.0.1 set prefix-length 16 commit """ if not xorpsh(builddir, xorpsh_commands): return False return True def conf_tracing_state(builddir): """ Enable the tracing of state changes, assume that BGP is already configured. """ # Configure the xorpsh xorpsh_commands = \ """ configure edit protocols bgp create traceoptions edit traceoptions create flag edit flag create state-change commit """ if not xorpsh(builddir, xorpsh_commands): return False return True def conf_set_holdtime(builddir, peer, holdtime): """ Set the holdtime on the specified peer. """ # Configure the xorpsh xorpsh_commands = \ """ configure set protocols bgp peer %s holdtime %s commit """ % (peer, holdtime) if not xorpsh(builddir, xorpsh_commands): return False return True def conf_set_prefix_limit(builddir, peer, maximum): """ Set the prefix limit on a peer """ # Configure the xorpsh xorpsh_commands = \ """ configure create protocols bgp peer %s prefix-limit edit protocols bgp peer %s prefix-limit set maximum %s commit """ % (peer, peer, maximum) if not xorpsh(builddir, xorpsh_commands): return False return True def conf_IBGP(builddir): """ Configure ONE IBGP peering """ # Configure the xorpsh xorpsh_commands = \ """configure load empty.boot create protocol bgp edit protocol bgp set bgp-id 1.2.3.4 set local-as 65000 create peer peer1 edit peer peer1 set local-port 10001 set peer-port 20001 set next-hop 127.0.0.1 set local-ip 127.0.0.1 set as 65000 commit """ if not xorpsh(builddir, xorpsh_commands): return False return True def conf_EBGP(builddir): """ Configure ONE EBGP peering """ # Configure the xorpsh xorpsh_commands = \ """configure load empty.boot create protocol bgp edit protocol bgp set bgp-id 1.2.3.4 set local-as 65000 create peer peer1 edit peer peer1 set local-port 10001 set peer-port 20001 set next-hop 127.0.0.1 set local-ip 127.0.0.1 set as 65001 commit """ if not xorpsh(builddir, xorpsh_commands): return False return True def conf_bug_360(builddir): """ Configure ONE EBGP peering """ # Configure the xorpsh xorpsh_commands = \ """configure load empty.boot create protocol bgp edit protocol bgp set bgp-id 1.2.3.4 set local-as 65001 create peer peer1 edit peer peer1 set local-port 10001 set peer-port 20001 set next-hop 127.0.0.1 set local-ip 127.0.0.1 set as 75 commit """ if not xorpsh(builddir, xorpsh_commands): return False return True def conf_bug_740(builddir): """ Configure an import and an export policy """ # Configure the xorpsh xorpsh_commands = \ """configure load empty.boot create policy policy-statement out term a from protocol connected create policy policy-statement in term a from neighbor 10.1.2.18..10.1.2.18 create protocol bgp edit protocol bgp set bgp-id 1.2.3.4 set local-as 65001 set export "out" set import "in" create peer peer1 edit peer peer1 set local-port 10001 set peer-port 20001 set next-hop 127.0.0.1 set local-ip 127.0.0.1 set as 75 commit """ if not xorpsh(builddir, xorpsh_commands): return False return True def conf_EBGP_EBGP(builddir): """ Configure two EBGP peerings """ # Configure the xorpsh xorpsh_commands = \ """configure load empty.boot create protocol bgp edit protocol bgp set bgp-id 1.2.3.4 set local-as 65000 create peer peer1 edit peer peer1 set local-port 10001 set peer-port 20001 set next-hop 127.0.0.1 set local-ip 127.0.0.1 set as 65001 up create peer peer2 edit peer peer2 set local-port 10002 set peer-port 20002 set next-hop 127.0.0.1 set local-ip 127.0.0.1 set as 65002 commit """ if not xorpsh(builddir, xorpsh_commands): return False return True def conf_test_dump_1(builddir): """ Configure two EBGP peerings """ # Configure the xorpsh xorpsh_commands = \ """configure load empty.boot create protocol bgp edit protocol bgp set bgp-id 1.2.3.4 set local-as 65010 create peer peer1 edit peer peer1 set local-port 10001 set peer-port 20001 set next-hop 127.0.0.1 set local-ip 127.0.0.1 set as 65000 up create peer peer2 edit peer peer2 set local-port 10002 set peer-port 20002 set next-hop 127.0.0.1 set local-ip 127.0.0.1 set as 65001 up up up create protocols static edit protocols static create route 192.150.187.0/24 edit route 192.150.187.0/24 set next-hop 127.0.0.1 up commit """ if not xorpsh(builddir, xorpsh_commands): return False return True def conf_EBGP_IBGP_IBGP(builddir): """ Configure One EBGP peering and two IBGP peerings """ # Configure the xorpsh xorpsh_commands = \ """configure load empty.boot create protocol bgp edit protocol bgp set bgp-id 1.2.3.4 set local-as 65000 create peer peer1 edit peer peer1 set local-port 10001 set peer-port 20001 set next-hop 127.0.0.1 set local-ip 127.0.0.1 set as 65001 up create peer peer2 edit peer peer2 set local-port 10002 set peer-port 20002 set next-hop 127.0.0.1 set local-ip 127.0.0.1 set as 65000 up create peer peer3 edit peer peer3 set local-port 10003 set peer-port 20003 set next-hop 127.0.0.1 set local-ip 127.0.0.1 set as 65000 up commit """ if not xorpsh(builddir, xorpsh_commands): return False return True def conf_RUT_as2_TR1_as1(builddir): """ Configure One EBGP peering """ # Configure the xorpsh xorpsh_commands = \ """configure load empty.boot create protocol bgp edit protocol bgp set bgp-id 1.2.3.4 set local-as 2 create peer peer1 edit peer peer1 set local-port 10001 set peer-port 20001 set next-hop 127.0.0.1 set local-ip 127.0.0.1 set as 1 up commit """ if not xorpsh(builddir, xorpsh_commands): return False return True def conf_RUT_as2_TR1_as1_TR2_as2(builddir): """ Configure One EBGP peering and one IBGP peerings """ # Configure the xorpsh xorpsh_commands = \ """configure load empty.boot create protocol bgp edit protocol bgp set bgp-id 1.2.3.4 set local-as 2 create peer peer1 edit peer peer1 set local-port 10001 set peer-port 20001 set next-hop 127.0.0.1 set local-ip 127.0.0.1 set as 1 up create peer peer2 edit peer peer2 set local-port 10002 set peer-port 20002 set next-hop 127.0.0.1 set local-ip 127.0.0.1 set as 2 up commit """ if not xorpsh(builddir, xorpsh_commands): return False return True def conf_RUT_as2_TR1_as1_TR2_as1_TR3_as3(builddir): """ Configure One EBGP peering and two IBGP peerings """ # Configure the xorpsh xorpsh_commands = \ """configure load empty.boot create protocol bgp edit protocol bgp set bgp-id 1.2.3.4 set local-as 2 create peer peer1 edit peer peer1 set local-port 10001 set peer-port 20001 set next-hop 127.0.0.1 set local-ip 127.0.0.1 set as 1 up create peer peer2 edit peer peer2 set local-port 10002 set peer-port 20002 set next-hop 127.0.0.1 set local-ip 127.0.0.1 set as 1 up create peer peer3 edit peer peer3 set local-port 10003 set peer-port 20003 set next-hop 127.0.0.1 set local-ip 127.0.0.1 set as 3 up commit """ if not xorpsh(builddir, xorpsh_commands): return False return True def conf_RUT_as2_TR1_as1_TR2_as3(builddir): """ Configure One EBGP peering and two IBGP peerings """ # Configure the xorpsh xorpsh_commands = \ """configure load empty.boot create protocol bgp edit protocol bgp set bgp-id 1.2.3.4 set local-as 2 create peer peer1 edit peer peer1 set local-port 10001 set peer-port 20001 set next-hop 127.0.0.1 set local-ip 127.0.0.1 set as 1 up create peer peer2 edit peer peer2 set local-port 10002 set peer-port 20002 set next-hop 127.0.0.1 set local-ip 127.0.0.1 set as 3 commit """ if not xorpsh(builddir, xorpsh_commands): return False return True def conf_RUT_as3_TR1_as1_TR2_as2_TR3_as4(builddir): """ Configure three EBGP peerings """ # Configure the xorpsh xorpsh_commands = \ """configure load empty.boot create protocol bgp edit protocol bgp set bgp-id 1.2.3.4 set local-as 3 create peer peer1 edit peer peer1 set local-port 10001 set peer-port 20001 set next-hop 127.0.0.1 set local-ip 127.0.0.1 set as 1 up create peer peer2 edit peer peer2 set local-port 10002 set peer-port 20002 set next-hop 127.0.0.1 set local-ip 127.0.0.1 set as 2 up create peer peer3 edit peer peer3 set local-port 10003 set peer-port 20003 set next-hop 127.0.0.1 set local-ip 127.0.0.1 set as 4 commit """ if not xorpsh(builddir, xorpsh_commands): return False return True def conf_redist_static(builddir, create_static = True): """ Redistribute static into BGP """ if create_static: create_static_command = \ """ create protocols static edit protocols static top """ else: create_static_command = '' # Configure the xorpsh xorpsh_commands = \ """ configure %s create policy edit policy create policy-statement static edit policy-statement static create term 1 edit term 1 create from edit from set protocol static top edit protocols bgp set export static commit """ % (create_static_command) if not xorpsh(builddir, xorpsh_commands): return False return True def conf_redist_static_incomplete(builddir): """ Redistribute static into BGP and set the origin to INCOMPLETE """ conf_redist_static(builddir) # Configure the xorpsh xorpsh_commands = \ """ configure edit policy policy-statement static term 1 create then edit then set origin 2 commit """ if not xorpsh(builddir, xorpsh_commands): return False return True def conf_redist_static_no_export(builddir): """ Redistribute static into BGP and set NO_EXPORT """ conf_redist_static(builddir) # Configure the xorpsh xorpsh_commands = \ """ configure edit policy policy-statement static term 1 create then edit then set community NO_EXPORT commit """ if not xorpsh(builddir, xorpsh_commands): return False return True def conf_redist_static_med(builddir): """ Redistribute static into BGP and set the MED to 42 """ conf_redist_static(builddir) # Configure the xorpsh xorpsh_commands = \ """ configure edit policy policy-statement static term 1 create then edit then set med 42 up up create term 2 edit term 2 create to edit to set as-path ^$ up create then edit then set med-remove true commit """ if not xorpsh(builddir, xorpsh_commands): return False return True def conf_preference_TR1(builddir): """ Configure TR1 to have a higher preference than TR2 """ # Configure the xorpsh xorpsh_commands = \ """ configure create policy policy-statement preference term 1 edit policy policy-statement preference term 1 create from edit from set nexthop4 127.0.0.2..127.0.0.2 up create then edit then set localpref 200 top edit protocols bgp set import preference commit """ if not xorpsh(builddir, xorpsh_commands): return False return True def conf_import_med_change(builddir): """ Set the med of all incoming packets to 42 """ # Configure the xorpsh xorpsh_commands = \ """ configure create policy policy-statement preference term 1 edit policy policy-statement preference term 1 create then edit then set med 42 top edit protocols bgp set import preference commit """ if not xorpsh(builddir, xorpsh_commands): return False return True def conf_export_med_change(builddir): """ Set the med of all outgoing packets to 42 """ # Configure the xorpsh xorpsh_commands = \ """ configure create policy policy-statement preference term 1 edit policy policy-statement preference term 1 create then edit then set med 42 top edit protocols bgp set export preference commit """ if not xorpsh(builddir, xorpsh_commands): return False return True def conf_import_origin_change(builddir): """ Set the origin of all incoming packets to incomplete """ # Configure the xorpsh xorpsh_commands = \ """ configure create policy policy-statement preference term 1 edit policy policy-statement preference term 1 create then edit then set origin 2 top edit protocols bgp set import preference commit """ if not xorpsh(builddir, xorpsh_commands): return False return True def conf_export_origin_change(builddir): """ Set the origin of all outgoing packets to incomplete """ # Configure the xorpsh xorpsh_commands = \ """ configure create policy policy-statement preference term 1 edit policy policy-statement preference term 1 create then edit then set origin 2 top edit protocols bgp set export preference commit """ if not xorpsh(builddir, xorpsh_commands): return False return True def conf_damping(builddir): """ Configure damping """ # Configure the xorpsh xorpsh_commands = \ """ configure edit protocols bgp create damping edit damping set suppress 2000 set reuse 800 set half-life 3 set max-suppress 5 commit """ if not xorpsh(builddir, xorpsh_commands): return False return True def conf_aggregate_brief(builddir): if not conf_aggregate(builddir, "true"): return False return True def conf_aggregate_asset(builddir): if not conf_aggregate(builddir, "false"): return False return True def conf_aggregate(builddir, brief_mode): """ Configure aggregate """ # Configure the xorpsh xorpsh_commands = \ """ configure create policy edit policy create policy-statement aggregate edit policy-statement aggregate create term 1 edit term 1 create from edit from set network4 <= 192.0.0.0/8 up create then edit then set aggregate-prefix-len 8 set aggregate-brief-mode %s top edit policy create policy-statement drop-component edit policy-statement drop-component create term 1 edit term 1 create to edit to set was-aggregated true up create then edit then set reject top edit protocol bgp set import aggregate set export drop-component commit """ % (brief_mode) if not xorpsh(builddir, xorpsh_commands): return False return True def conf_create_protocol_static(builddir): """ Create the static protocol """ # Configure the xorpsh xorpsh_commands = \ """ configure create protocols static commit """ if not xorpsh(builddir, xorpsh_commands): return False return True def conf_add_static_route4(builddir, net, next_hop = "127.0.0.1"): """ Add a static route """ # Configure the xorpsh xorpsh_commands = \ """ configure edit protocols static create route %s edit route %s set next-hop %s commit """ % (net, net, next_hop) if not xorpsh(builddir, xorpsh_commands): return False return True def conf_delete_static_route4(builddir, net, next_hop = "127.0.0.1"): """ Delete a static route """ # Configure the xorpsh xorpsh_commands = \ """ configure delete protocols static route %s commit """ % (net) if not xorpsh(builddir, xorpsh_commands): return False return True def conf_multiprotocol(builddir): """ Configure multiprotocol """ return True def show_bgp_routes(builddir): """ Return the output of the show bgp routes command. """ xorpsh_commands = \ """ show bgp routes """ result, output = xorpsh(builddir, xorpsh_commands) if not result: return False, output return True, output # Local Variables: # mode: python # py-indent-offset: 4 # End: xorp/tests/test_process.py0000775000076400007640000000422111421137511016107 0ustar greearbgreearb#!/usr/bin/env python # Copyright (c) 2001-2009 XORP, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # $XORP: xorp/tests/test_process.py,v 1.6 2008/10/02 21:58:30 bms Exp $ import thread,threading,time,sys,os,popen2 class Process(threading.Thread): """ Start a process in a separate thread """ def __init__(self, command=""): threading.Thread.__init__(self) self._status = "INIT" self._command = command self.lock = thread.allocate_lock() def run(self): self.lock.acquire() print "command:", self._command self.process = popen2.Popen4("exec " + self._command) print "PID:", self.process.pid self._status = "RUNNING" while 1: o = self.process.fromchild.read(1) if not o: break os.write(1, o) self._status = "TERMINATED" print "exiting:", self._command self.lock.release() def status(self): return self._status def command(self): return self._command def terminate(self): """ Terminate this process """ print "sending kill to", self._command, self.process.pid if self._status == "RUNNING": os.kill(self.process.pid, 9) self.lock.acquire() self.lock.release() else: print self._command, "not running" # Local Variables: # mode: python # py-indent-offset: 4 # End: xorp/tests/test_call_xrl.py0000664000076400007640000000251711421137511016234 0ustar greearbgreearb#!/usr/bin/env python # Copyright (c) 2001-2009 XORP, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # $XORP: xorp/tests/test_call_xrl.py,v 1.5 2008/10/02 21:58:30 bms Exp $ import popen2 def call_xrl(builddir, command): """ Call an XRL """ #print command call_xrl_path=builddir + "libxipc/call_xrl" process = popen2.Popen4(call_xrl_path + " " + "\"" + command + "\"") out="" while 1: lines = process.fromchild.readline() if not lines: break out += lines status = process.wait() return status, out # Local Variables: # mode: python # py-indent-offset: 4 # End: xorp/tests/test_xorpsh.py0000664000076400007640000000334211421137511015754 0ustar greearbgreearb#!/usr/bin/env python # Copyright (c) 2001-2009 XORP, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # $XORP: xorp/tests/test_xorpsh.py,v 1.8 2008/10/02 21:58:30 bms Exp $ import popen2 def xorpsh(builddir, command, templates = '../templates'): """ Send commands via the xorpsh """ xorpsh_path = builddir + "rtrmgr/xorpsh -t %s" % templates process = popen2.Popen4(xorpsh_path) process.tochild.write(command) process.tochild.close() # XXX - This is not really a satisfactory way of determining if an # error has occurred error_responses = ["ERROR", "unknown command", "syntax error", "Commit Failed"] output = "" while 1: line = process.fromchild.readline() if not line: break for i in error_responses: if line.startswith(i): raise Exception, line print line, output += line status = process.wait() if 0 == status: return True, output else: return False, output xorp/tests/install_templates.sh0000775000076400007640000000332411421137511017103 0ustar greearbgreearb#!/bin/sh # Copyright (c) 2001-2009 XORP, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # $XORP: xorp/tests/install_templates.sh,v 1.5 2008/10/02 21:58:30 bms Exp $ # Take a local copy of the template files and modify them for use with # the tests. # Create an empty boot file. touch empty.boot TEMPLATES=templates if [ ! -d $TEMPLATES ] then mkdir $TEMPLATES fi cd $TEMPLATES cp ../../etc/templates/* . for i in * do if grep xorp_fea $i then ed $i <<\EOF 1,$s/xorp_fea/xorp_fea_dummy/g wq EOF fi done # Remove the setting of local-ip and then add back the setting of # local-ip, peer-port and local-port as noops. ed bgp.tp <> sys.stderr, "Still pending" def pending(): """ Check the previous command has completed """ status, message = call_xrl(builddir(1), "finder://coord/coord/0.1/pending") if message == "pending:bool=false\n": return False else: return True def status(peer): """ Get the status of a test peer. """ status, message = call_xrl(builddir(1), "finder://coord/coord/0.1/status?peer:txt=" + peer) message = re.sub('^status:txt=', '', message) message = re.sub('\+', ' ', message) return message def run_test(test, single, configure, TESTS, config_module, test_module): """ Run the provided test """ bdir = builddir(1) # First find the test if it exists test_func = '' conf_funcs = [] for i in TESTS: if test == i[0]: test_func = i[1] if i[3] != '' and i[4] != '': print "Both fields should not be set" return False if i[3] != '': conf_funcs.append("UNKNOWN") test_func += '(bdir,conf)' if i[4] != '': print "debug", i[4] for f in i[4]: conf_funcs.append(f + '(bdir)') test_func += '()' if not single: print "------ START PROGRAMS ------" conf_mod = __import__(config_module) test_mod = __import__(test_module) print conf_funcs try: if configure: for i in conf_funcs: if not eval('conf_mod' + '.' + i): print i, "FAILED" return False if not eval('test_mod' + '.' + test_func): print test, "FAILED" return False else: print test, "SUCCEEDED" except Exception, (ErrorMessage): print ErrorMessage print test, "FAILED" return False return True def test_main(TESTS, config_module, test_module): def usage(): us = \ "usage: %s [-h|--help] [-t|--test] [-b|--bad] [-s|--single] [-c|--configure]" print us % sys.argv[0] try: opts, args = getopt.getopt(sys.argv[1:], "h:t:bsc", \ ["help", \ "test=", \ "bad", \ "single", \ "configure", \ ]) except getopt.GetoptError: usage() sys.exit(1) bad = False single = False configure = True tests = [] for o, a in opts: if o in ("-h", "--help"): usage() sys.exit() if o in ("-t", "--test"): tests.append(a) if o in ("-b", "--bad"): bad = True if o in ("-s", "--single"): single = True configure = False if o in ("-c", "--configure"): configure = True if not tests: for i in TESTS: if bad != i[2]: tests.append(i[0]) print tests for i in tests: if not run_test(i,single,configure,TESTS,config_module,test_module): print "Test: " + i + " FAILED" sys.exit(-1) sys.exit(0) # Local Variables: # mode: python # py-indent-offset: 4 # End: xorp/tests/add_routes.pl0000775000076400007640000000100211421137511015500 0ustar greearbgreearb#!/usr/bin/perl # Add a bunch of routes to the specified routing table # Used to test fib2mrib propagation. use strict; my $amt = 2000; my $a = 1; my $b = 1; my $c = 10; my $d = 0; my $sigb = 24; my $dev = "rddVR0"; my $table = 10001; my $gw = "1.1.1.2"; my $i = 0; for ($i = 0; $i<$amt; $i++) { my $cmd = "ip route add $a.$b.$c.$d/$sigb via $gw dev $dev table $table"; print "$cmd\n"; `$cmd`; $c++; if ($c >= 255) { $b++; $c = 1; if ($b >= 255) { $a++; $b = 1; } } } xorp/tests/test_start.py0000775000076400007640000000733311421137511015575 0ustar greearbgreearb#!/usr/bin/env python # Copyright (c) 2001-2009 XORP, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # $XORP: xorp/tests/test_start.py,v 1.8 2008/10/02 21:58:30 bms Exp $ import getopt,threading,time,sys from test_process import Process from test_builddir import builddir class Start: """ Start the router manager and the test harness processes. """ def __init__(self, builddir="..", verbose = False): self.builddir = builddir self.plist = [] self.verbose = verbose def start(self): """ Start all the processes """ rtrmgr = self.builddir + "rtrmgr/xorp_rtrmgr -t templates -b empty.boot" self.__start_process(rtrmgr) time.sleep(5) coord = self.builddir + "bgp/harness/coord" self.__start_process(coord) if self.verbose: peer = self.builddir + "bgp/harness/test_peer -t -v" else: peer = self.builddir + "bgp/harness/test_peer" for i in ["peer1", "peer2", "peer3"]: self.__start_process(peer + " -s " + i) def __start_process(self, process): """ Start a single process and add it to the list """ p = Process(command=process) p.start(); self.plist.append(p) def check(self): """ Make sure all the processes are still running """ for i in self.plist: print "Testing: ", i.command() status = i.status() if "TERMINATED" == status: return False if "RUNNING" == status: continue if "INIT" == status: for a in range(5): time.sleep(1) if "RUNNING" == i.status(): break print "Not running", i.command(), i.status() return True def terminate(self): """ Stop all the processes """ for i in self.plist: i.terminate() if __name__ == '__main__': def usage(): us = "usage: %s [-h|--help] [-v|--verbose]" print us % sys.argv[0] try: opts, args = getopt.getopt(sys.argv[1:], "hv", \ ["help", \ "verbose", \ ]) except getopt.GetoptError: usage() sys.exit(-1) verbose = False for o, a in opts: if o in ("-h", "--help"): usage() sys.exit() if o in ("-v", "--verbose"): verbose = True s = Start(builddir=builddir(), verbose=verbose) s.start() if not s.check(): print "Processes did not start" s.terminate() sys.exit(-1) print "Hit return to kill processes" sys.stdin.readline() print "About to terminate processes" if not s.check(): print "Processes no longer running" s.terminate() sys.exit(-1) s.terminate() sys.exit(0) # Local Variables: # mode: python # py-indent-offset: 4 # End: xorp/tests/test_builddir.py0000664000076400007640000000221411421137511016224 0ustar greearbgreearb#!/usr/bin/env python # Copyright (c) 2001-2009 XORP, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # $XORP: xorp/tests/test_builddir.py,v 1.8 2008/10/02 21:58:30 bms Exp $ BUILDDIR='../' def builddir(depth = 0): """ Return the top of the build directory """ global BUILDDIR bdir = '' for i in range(depth): bdir += '../' return bdir + BUILDDIR # Local Variables: # mode: python # py-indent-offset: 4 # End: xorp/libproto/0000775000076400007640000000000011540225526013514 5ustar greearbgreearbxorp/libproto/proto_state.hh0000664000076400007640000001222511540224225016375 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __LIBPROTO_PROTO_STATE_HH__ #define __LIBPROTO_PROTO_STATE_HH__ #include "libxorp/xorp.h" #include "libxorp/service.hh" // // Protocol state generic functionality // /** * @short Base class for keeping state for each protocol unit (node, vif, etc). */ class ProtoState : public ServiceBase { public: /** * Default Constructor. */ ProtoState(); /** * Destructor */ virtual ~ProtoState(); /** * Start the unit. * * This operation will fail if the unit is disabled, or is already up. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int start(); /** * Stop the unit. * * This operation will fail if the unit was down already. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int stop(); /** * Pending-start the unit. * * The pending-start state is an intermediate state between down and up. * In this state only some operations are allowed (the allowed operations * are unit-specific). * This operation will fail if the unit is disabled, is up, or is * pending-up already. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int pending_start(); /** * Pending-stop the unit. * * The pending-stop state is an intermediate state between up and down. * In this state only some operations are allowed (the allowed operations * are unit-specific). * This operation will fail if the unit is not up. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int pending_stop(); /** * Enable the unit. * * If an unit is not enabled, it cannot be start, or pending-start. */ void enable() { _flags |= XORP_ENABLED; } /** * Disable the unit. * * If an unit is disabled, it cannot be start or pending-start. * If the unit was runnning, it will be stop first. */ void disable(); /** * Test if the unit state is UP. * * @return true if the unit state is UP. */ bool is_up() const; /** * Test if the unit state is DOWN. * * @return true if the unit state is DOWN. */ bool is_down() const; /** * Test if the unit state is PENDING-UP. * * @return true if the unit state is PENDING-UP. */ bool is_pending_up() const; /** * Test if the unit state is PENDING-DOWN. * * @return true if the unit state is PENDING-DOWN. */ bool is_pending_down() const; /** * Test if the unit is enabled. * * @return true if the unit is enabled. */ bool is_enabled() const { return (_flags & XORP_ENABLED); } /** * Test if the unit is disabled. * * @return true if the unit is disabled. */ bool is_disabled() const { return (! is_enabled()); } /** * Test if debug mode is enabled. * * @return true if debug mode is enabled. */ bool is_debug() const { return (_debug_flag); } /** * Set/reset debug mode. * * @param v if true, set debug mode, otherwise reset it. */ void set_debug(bool v) { _debug_flag = v; } /** * Get a string with the state of the unit. * * The state string is one of the following: * "DISABLED", "DOWN", "UP", "PENDING_UP", "PENDING_DOWN", "UNKNOWN" * * @return string with the state of the unit. */ string state_str() const; private: /** * Startup operation. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int startup(); /** * Shutdown operation. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int shutdown(); /** * Reset service to SERVICE_READY from whichever state it is in. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int reset(); enum { XORP_ENABLED = 1 << 0 // Entity is enabled. }; uint32_t _flags; // Misc. flags: XORP_ENABLED, etc // (see above). bool _debug_flag; // Enable/Disable debug messages. }; // // Global variables // // // Global functions prototypes // #endif // __LIBPROTO_PROTO_STATE_HH__ xorp/libproto/packet.cc0000664000076400007640000002302411540225526015273 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2006-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // Packet related manipulation tools // #include "libproto_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "checksum.h" #include "packet.hh" int IpHeader4::fragment(size_t mtu, list >& fragments, bool do_checksum, string& error_msg) const { const IpHeader4& orig_ip4 = *this; size_t datalen = orig_ip4.ip_len(); // // If the data packet is small enough, then don't fragment it // if (datalen <= mtu) { return (XORP_OK); } // // Fragment the inner packet, then encapsulate and send each fragment // vector frag_buf(datalen); // The buffer for the fragments IpHeader4Writer frag_ip4(&frag_buf[0]); size_t frag_optlen = 0; // The optlen to copy into fragments size_t frag_ip_hl; if (orig_ip4.ip_off() & IpHeader4::FRAGMENT_FLAGS_IP_DF) { // Fragmentation is forbidded error_msg = c_format("Cannot fragment encapsulated IP packet " "from %s to %s: " "fragmentation not allowed", cstring(orig_ip4.ip_src()), cstring(orig_ip4.ip_dst())); XLOG_WARNING("%s", error_msg.c_str()); return (XORP_ERROR); } if (((mtu - (orig_ip4.ip_header_len())) & ~7) < 8) { // // Fragmentation is possible only if we can put at least // 8 octets per fragment (except the last one). // error_msg = c_format("Cannot fragment encapsulated IP packet " "from %s to %s: " "cannot send fragment with size less than 8 octets", cstring(orig_ip4.ip_src()), cstring(orig_ip4.ip_dst())); XLOG_WARNING("%s", error_msg.c_str()); return (XORP_ERROR); } // // Copy the IP header and the options that should be copied during // fragmentation. // XXX: the code below is taken from FreeBSD's ip_optcopy() // in netinet/ip_output.c // memcpy(&frag_buf[0], _data, IpHeader4::SIZE); { register const u_char *cp; register u_char *dp; int opt, optlen, cnt; cp = (const u_char *)(orig_ip4.data() + orig_ip4.size()); dp = (u_char *)(frag_ip4.data() + frag_ip4.size()); cnt = orig_ip4.ip_header_len() - IpHeader4::SIZE; for (; cnt > 0; cnt -= optlen, cp += optlen) { opt = cp[0]; if (opt == IpHeader4::OPTIONS_IPOPT_EOL) break; if (opt == IpHeader4::OPTIONS_IPOPT_NOP) { // Preserve for IP mcast tunnel's LSRR alignment. *dp++ = IpHeader4::OPTIONS_IPOPT_NOP; optlen = 1; continue; } // // Check for bogus lengths // if ((size_t)cnt < IpHeader4::OPTIONS_IPOPT_OLEN + sizeof(*cp)) { error_msg = c_format("Cannot fragment encapsulated IP " "packet from %s to %s: " "malformed IPv4 option", cstring(orig_ip4.ip_src()), cstring(orig_ip4.ip_dst())); XLOG_WARNING("%s", error_msg.c_str()); return (XORP_ERROR); } optlen = cp[IpHeader4::OPTIONS_IPOPT_OLEN]; if (((size_t)optlen < IpHeader4::OPTIONS_IPOPT_OLEN + sizeof(*cp)) || (optlen > cnt)) { error_msg = c_format("Cannot fragment encapsulated IP " "packet from %s to %s: " "malformed IPv4 option", cstring(orig_ip4.ip_src()), cstring(orig_ip4.ip_dst())); XLOG_WARNING("%s", error_msg.c_str()); return (XORP_ERROR); } if (optlen > cnt) optlen = cnt; if (IpHeader4::OPTIONS_COPIED_FLAG & opt) { memcpy(dp, cp, optlen); dp += optlen; } } for (optlen = dp - (u_char *)(frag_ip4.data() + frag_ip4.size()); optlen & 0x3; optlen++) { *dp++ = IpHeader4::OPTIONS_IPOPT_EOL; } frag_optlen = optlen; // return (optlen); } // The header length with the remaining IP options frag_ip_hl = IpHeader4::SIZE + frag_optlen; frag_ip4.set_ip_header_len(frag_ip_hl); // // Do the fragmentation // size_t data_start = 0; size_t data_end = datalen; // Send the first segment with all the options { vector first_frag(mtu); IpHeader4Writer first_ip(&first_frag[0]); size_t first_ip_hl = orig_ip4.ip_header_len(); size_t nfb = (mtu - first_ip_hl) / 8; size_t first_frag_len = first_ip_hl + nfb*8; first_frag.resize(first_frag_len); memcpy(&first_frag[0], _data, first_frag_len); // Correct the IP header first_ip.set_ip_off(first_ip.ip_off() | IpHeader4::FRAGMENT_FLAGS_IP_MF); first_ip.set_ip_len(first_frag_len); first_ip.set_ip_sum(0); if (do_checksum) first_ip.compute_checksum(); // Add the first fragment fragments.push_back(first_frag); data_start += first_frag_len; } // // Create the remaining of the fragments // while (data_start < data_end) { size_t nfb = (mtu - frag_ip_hl) / 8; size_t frag_len = frag_ip_hl + nfb*8; size_t frag_data_len = nfb*8; bool is_last_fragment = false; // Compute the fragment length if (data_end - data_start <= frag_data_len) { frag_data_len = data_end - data_start; frag_len = frag_ip_hl + frag_data_len; is_last_fragment = true; } // Copy the data frag_buf.resize(frag_len); memcpy(&frag_buf[0] + frag_ip_hl, _data + data_start, frag_data_len); // The IP packet total length frag_ip4.set_ip_len(frag_len); // The IP fragment flags and offset { unsigned short ip_off_field = orig_ip4.ip_off(); unsigned short frag_ip_off_flags = ip_off_field & ~IpHeader4::FRAGMENT_OFFSET_MASK; unsigned short frag_ip_off = (ip_off_field & IpHeader4::FRAGMENT_OFFSET_MASK); if (! is_last_fragment) frag_ip_off_flags |= IpHeader4::FRAGMENT_FLAGS_IP_MF; frag_ip_off += (data_start - orig_ip4.ip_header_len()) / 8; // XXX frag_ip4.set_ip_off(frag_ip_off_flags | frag_ip_off); } frag_ip4.set_ip_sum(0); if (do_checksum) frag_ip4.compute_checksum(); // Add the fragment fragments.push_back(frag_buf); data_start += frag_data_len; } return (XORP_OK); } void IpHeader4Writer::compute_checksum() { set_ip_sum(0); set_ip_sum(ntohs(inet_checksum(data(), ip_header_len()))); } ArpHeader::ArpHeader() { memset(this, 0, sizeof(this)); ah_hw_len = 6; ah_proto_len = 4; } ArpHeader::ArpHeader(const vector& pkt) { XLOG_ASSERT(pkt.size() <= sizeof(*this)); memcpy(this, &pkt[0], pkt.size()); if (ah_hw_len != 6) { XLOG_WARNING("Bad arp header len: %i\n", (int)(ah_hw_len)); ah_hw_len = 6; } if (ah_proto_len != 4) { XLOG_WARNING("Bad arp proto len: %i\n", (int)(ah_proto_len)); ah_proto_len = 4; } } bool ArpHeader::is_request() const { return ntohs(ah_op) == ARP_REQUEST; } IPv4 ArpHeader::get_request() const { if (!is_request()) xorp_throw(BadPacketException, "Not an ARP request"); if (ntohs(ah_proto_fmt) != ETHERTYPE_IP) xorp_throw(BadPacketException, "Not an IPv4 ARP"); IPv4 ip; ip.copy_in(&ah_data_store[ah_hw_len * 2 + ah_proto_len]); return ip; } void ArpHeader::make_reply(vector& out, const Mac& mac) const { // sanity checks if (!is_request()) xorp_throw(BadPacketException, "Not an ARP request"); if (ntohs(ah_hw_fmt) != HW_ETHER) xorp_throw(BadPacketException, "Not an ethernet ARP"); // allocate size int sz = size(); out.reserve(sz); out.resize(sz); ArpHeader reply; // copy request (this) into reply memcpy(&reply, this, sz); // make it a reply reply.ah_op = htons(ARP_REPLY); // set the destination sz = ah_hw_len + ah_proto_len; memcpy(&reply.ah_data_store[sz], ah_data_store, sz); // set the source mac.copy_out(reply.ah_data_store); sz += ah_hw_len; memcpy(&reply.ah_data_store[ah_hw_len], &ah_data_store[sz], ah_proto_len); // Copy reply into 'out' vector. memcpy(&out[0], &reply, reply.size()); } void ArpHeader::set_sender(const Mac& mac, const IPv4& ip) { ah_hw_fmt = htons(HW_ETHER); ah_hw_len = mac.copy_out(ah_data_store); ah_proto_fmt = htons(ETHERTYPE_IP); ah_proto_len = ip.copy_out(&ah_data_store[ah_hw_len]); } void ArpHeader::set_request(const IPv4& ip) { XLOG_ASSERT(ah_proto_fmt == htons(ETHERTYPE_IP)); ah_op = htons(ARP_REQUEST); ip.copy_out(&ah_data_store[ah_hw_len * 2 + ah_proto_len]); } void ArpHeader::set_reply(const Mac& mac, const IPv4& ip) { XLOG_ASSERT(ah_hw_fmt == htons(HW_ETHER)); XLOG_ASSERT(ah_proto_fmt == htons(ETHERTYPE_IP)); set_request(ip); ah_op = htons(ARP_REPLY); mac.copy_out(&ah_data_store[ah_hw_len + ah_proto_len]); } uint32_t ArpHeader::size() const { uint32_t rv = 8 + ah_hw_len * 2 + ah_proto_len * 2; XLOG_ASSERT(rv <= sizeof(*this)); return rv; } void ArpHeader::make_gratuitous(vector& data, const Mac& mac, const IPv4& ip) { ArpHeader arp; uint32_t sz = arp.size(); data.resize(sz, 0); arp.set_sender(mac, ip); arp.set_request(ip); XLOG_ASSERT(arp.size() <= data.capacity()); memcpy(&data[0], &arp, sz); } xorp/libproto/proto_unit.cc0000664000076400007640000001037111421137511016221 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // Protocol unit generic functionality implementation // #include "libproto_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "proto_unit.hh" // // Exported variables // // // Local constants definitions // // XXX: must be consistent with xorp_module_id definition // (TODO: a temp. solution) // TODO: the _4/6 suffix is a temp. solution static const char *_xorp_module_name[][2] = { { "XORP_MODULE_NULL", "XORP_MODULE_NULL" }, // 0 { "fea", "fea" }, // 1 { "MFEA_4", "MFEA_6" }, // 2 { "IGMP", "MLD" }, // 3 { "PIMSM_4", "PIMSM_6" }, // 4 { "PIMDM_4", "PIMDM_6" }, // 5 { "BGMP_4", "BGMP_6" }, // 6 { "BGP_4", "BGP_6" }, // 7 { "OSPF_4", "OSPF_6" }, // 8 { "RIP_4", "RIP_6" }, // 9 { "CLI", "CLI" }, // 10 { "rib", "rib" }, // 11 { "RTRMGR", "RTRMGR" }, // 12 { "static_routes", "static_routes" }, // 13 { "fib2mrib", "fib2mrib" }, // 14 { "XORP_MODULE_UNKNOWN", "XORP_MODULE_UNKNOWN" } }; // // Local structures/classes, typedefs and macros // // // Local variables // // // Local functions prototypes // /** * xorp_module_name: * @family: The address family. * @module_id: The #xorp_module_id module ID to search for. * * Return the pre-defined module name for a given address family and module ID. * * Return value: C string with the module name. **/ const char * xorp_module_name(int family, xorp_module_id module_id) { if (! is_valid_module_id(module_id)) { XLOG_ERROR("Invalid module_id = %d", module_id); return (NULL); } if (family == AF_INET) return (_xorp_module_name[module_id][0]); #ifdef HAVE_IPV6 if (family == AF_INET6) return (_xorp_module_name[module_id][1]); #endif // HAVE_IPV6 XLOG_ERROR("Invalid address family = %d", family); return (NULL); } /** * xorp_module_name2id: * @module_name: The module name to searh for. * * Return the pre-defined module ID for a given module name. * * Return value: The module ID if @module_name is a valid module name, * otherwise %XORP_MODULE_NULL. **/ xorp_module_id xorp_module_name2id(const char *module_name) { for (int i = XORP_MODULE_MIN; i < XORP_MODULE_MAX; i++) { if ((strcmp(module_name, _xorp_module_name[i][0]) == 0) || (strcmp(module_name, _xorp_module_name[i][1]) == 0)) return (static_cast(i)); } return (XORP_MODULE_NULL); } /** * is_valid_module_id: * @module_id: The module ID to test. * * Test if a module ID is valid. * * Return value: true if @module_id is valid, otherwise false. **/ bool is_valid_module_id(xorp_module_id module_id) { if ((XORP_MODULE_MIN <= module_id) && (module_id < XORP_MODULE_MAX)) return (true); return (false); } /** * ProtoUnit::ProtoUnit: * @init_family: The address family (%AF_INET or %AF_INET6 * for IPv4 and IPv6 respectively). * @init_module_id: The module ID (XORP_MODULE_*). * * Proto unit constructor. **/ ProtoUnit::ProtoUnit(int init_family, xorp_module_id init_module_id) : _family(init_family), _module_id(init_module_id) { if (! is_valid_module_id(init_module_id)) { XLOG_FATAL("Invalid module_id = %d", init_module_id); } _comm_handler = -1; _proto_version = 0; _proto_version_default = 0; _module_name = xorp_module_name(init_family, init_module_id); } ProtoUnit::~ProtoUnit() { } xorp/libproto/TODO0000664000076400007640000000027711421137511014204 0ustar greearbgreearb# # $XORP: xorp/libproto/TODO,v 1.2 2004/06/22 01:38:40 pavlin Exp $ # * Rename ProtoNode::proto_send() to send_protocol_message() Do similar renaming for other methods in ProtoNode. (??) xorp/libproto/spt.cc0000664000076400007640000003540111421137511014626 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // XXX // THIS FILE IS NOW OBSOLETE // XXX // #define INCREMENTAL_SPT // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #if 0 #include "libxorp/xorp.h" #include "libproto_module.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipv4.hh" #include "libxorp/ipv4net.hh" #include "libxorp/ipv6.hh" #include "libxorp/ipv6net.hh" #include "ospf/vertex.hh" #include "spt.hh" template Spt::~Spt() { for (typename Nodes::iterator i = _nodes.begin(); i != _nodes.end(); i++) i->second->clear(); } template bool Spt::set_origin(A node) { // Lookup this node. It must exist. typename Node::NodeRef srcnode = find_node(node); if (srcnode.is_empty()) { XLOG_WARNING("Node does not exist %s", Node(node).str().c_str()); return false; } _origin = srcnode; return true; } template bool Spt::add_node(A node) { // If a valid node already exists return false typename Node::NodeRef srcnode = find_node(node); if (!srcnode.is_empty()) { if (srcnode->valid()) { XLOG_WARNING("Node already exists %s", Node(node).str().c_str()); return false; } else { // We are going to revive this node so dump its adjacency // info. srcnode->drop_adjacencies(); srcnode->set_valid(true); return true; } } Node *n = new Node(node); _nodes[node] = typename Node::NodeRef(n); return true; } template bool Spt::remove_node(A node) { // If a valid node doesn't exist return false typename Node::NodeRef srcnode = find_node(node); if (srcnode.is_empty()) { XLOG_WARNING("Request to delete non-existant node %s", Node(node).str().c_str()); return false; } if (!srcnode->valid()) { XLOG_WARNING("Node already removed %s", Node(node).str().c_str()); return false; } srcnode->set_valid(false); return true; } template bool Spt::exists_node(A node) { return _nodes.count(node); } template bool Spt::add_edge(A src, int weight, A dst) { // Find the src node it must exist. typename Node::NodeRef srcnode = find_node(src); if (srcnode.is_empty()) { XLOG_WARNING("Node: %s not found", Node(src).str().c_str()); return false; } // The dst node doesn't have to exist. If it doesn't exist create it. typename Node::NodeRef dstnode = find_node(dst); if (dstnode.is_empty()) { if (!add_node(dst)) { XLOG_WARNING("Add node %s failed", Node(dst).str().c_str()); return false; } } dstnode = find_node(dst); if (dstnode.is_empty()) { XLOG_WARNING("Node: %s not found", Node(dst).str().c_str()); return false; } srcnode->add_edge(dstnode, weight); return true; } template bool Spt::update_edge_weight(A src, int weight, A dst) { typename Node::NodeRef srcnode = find_node(src); if (srcnode.is_empty()) { debug_msg("Node: %s not found\n", Node(src).str().c_str()); return false; } typename Node::NodeRef dstnode = find_node(dst); if (dstnode.is_empty()) { debug_msg("Node: %s not found\n", Node(dst).str().c_str()); return false; } return srcnode->update_edge_weight(dstnode, weight); } template bool Spt::get_edge_weight(A src, int& weight, A dst) { typename Node::NodeRef srcnode = find_node(src); if (srcnode.is_empty()) { debug_msg("Node: %s not found\n", Node(src).str().c_str()); return false; } typename Node::NodeRef dstnode = find_node(dst); if (dstnode.is_empty()) { debug_msg("Node: %s not found\n", Node(dst).str().c_str()); return false; } return srcnode->get_edge_weight(dstnode, weight); } template bool Spt::remove_edge(A src, A dst) { typename Node::NodeRef srcnode = find_node(src); if (srcnode.is_empty()) { debug_msg("Node: %s not found\n", Node(src).str().c_str()); return false; } typename Node::NodeRef dstnode = find_node(dst); if (dstnode.is_empty()) { debug_msg("Node: %s not found\n", Node(dst).str().c_str()); return false; } return srcnode->remove_edge(dstnode); } template bool Spt::compute(list >& routes) { #ifdef INCREMENTAL_SPT if (!incremental_spt()) return false; #else if (!dijkstra()) return false; #endif for(typename Nodes::const_iterator ni = _nodes.begin(); ni != _nodes.end(); ni++) { // We don't need to know how to reach ourselves. if (ni->second == _origin) continue; RouteCmd rcmd; if (ni->second->delta(rcmd)) routes.push_back(rcmd); } // Remove all the deleted nodes. garbage_collect(); return true; } template string Spt::str() const { string pres; if (_origin.is_empty()) { pres = "No origin\n"; } else pres = "Origin: " + _origin->str() + "\n"; for(typename Nodes::const_iterator ni = _nodes.begin(); ni != _nodes.end(); ni++) { pres += ni->second->pp() + "\n"; } return pres; } template inline void init_dijkstra(const pair::NodeRef >& p) { p.second->set_tentative(true); p.second->invalidate_weights(); } template bool Spt::dijkstra() { if (_origin.is_empty()) { XLOG_WARNING("No origin"); return false; } for_each(_nodes.begin(), _nodes.end(), ptr_fun(init_dijkstra)); typename Node::NodeRef current = _origin; _origin->set_tentative(false); int weight = 0; // Map of tentative nodes. PriorityQueue tentative; for(;;) { // Set the weight on all the nodes that are adjacent to this one. current->set_adjacent_weights(current, weight, tentative); if (tentative.empty()) break; current = tentative.pop(); XLOG_ASSERT(!current.is_empty()); // Get the weight of this node. weight = current->get_local_weight(); // Make the node permanent. current->set_tentative(false); // Compute the next hop to get to this node. typename Node::NodeRef prev = current->get_last_hop(); if (prev == _origin) current->set_first_hop(current); else current->set_first_hop(prev->get_first_hop()); debug_msg("Previous: %s\n", prev->str().c_str()); debug_msg("Permanent: %s distance %d next hop %s\n", current->str().c_str(), weight, current->get_first_hop()->str().c_str()); } return true; } template bool Spt::incremental_spt() { XLOG_UNFINISHED(); return true; } template typename Node::NodeRef Spt::find_node(A node) { typename map::NodeRef>::iterator i = _nodes.find(node); if (i != _nodes.end()) { // debug_msg("Node %s found\n", Node(node).str().c_str()); return (*i).second; } // debug_msg("Node %s not found\n", Node(node).str().c_str()); // Node *n = 0; return typename Node::NodeRef(/*n*/); } template Node::Node(A nodename) : _valid(true), _nodename(nodename) { } template A Node::nodename() { return _nodename; } template bool Node::add_edge(NodeRef dst, int weight) { // See if this edge already exists. typename adjacency::iterator i = _adjacencies.find(dst->nodename()); // If this edge already exists consider this an error. if (i != _adjacencies.end()) { debug_msg("Edge from %s to %s exists\n", str().c_str(), dst->str().c_str()); return false; } _adjacencies[dst->nodename()] = Edge(dst, weight); return true; } template bool Node::update_edge_weight(NodeRef dst, int weight) { typename adjacency::iterator i = _adjacencies.find(dst->nodename()); // This edge should exist. if (i == _adjacencies.end()) { debug_msg("Edge from %s to %s doesn't exists\n", str().c_str(), dst->str().c_str()); return false; } Edge edge = i->second; edge._weight = weight; i->second = edge; return true; } template bool Node::get_edge_weight(NodeRef dst, int& weight) { typename adjacency::iterator i = _adjacencies.find(dst->nodename()); // This edge should exist. if (i == _adjacencies.end()) { debug_msg("Edge from %s to %s doesn't exists\n", str().c_str(), dst->str().c_str()); return false; } Edge edge = i->second; weight = edge._weight; return true; } template bool Node::remove_edge(NodeRef dst) { typename adjacency::iterator i = _adjacencies.find(dst->nodename()); if (i == _adjacencies.end()) { XLOG_WARNING("Edge from %s to %s doesn't exists", str().c_str(), dst->str().c_str()); return false; } _adjacencies.erase(i); return true; } template void Node::drop_adjacencies() { _adjacencies.clear(); } template void Node::garbage_collect() { typename adjacency::iterator ni; for(ni = _adjacencies.begin(); ni != _adjacencies.end();) { NodeRef node = ni->second._dst; if (!node->valid()) { // Clear any references that this node may have to itself. node->clear(); _adjacencies.erase(ni++); } else { ni++; } } } template void Node::set_adjacent_weights(NodeRef me, int delta_weight, PriorityQueue& tentative) { typename adjacency::iterator i; for(i = _adjacencies.begin(); i != _adjacencies.end(); i++) { NodeRef n = i->second._dst; debug_msg("Node: %s\n", n->str().c_str()); if (n->valid() && n->tentative()) { // It is critial that the weight of a node is not changed // while it is in the PriorityQueue. if (tentative.add(n, delta_weight + i->second._weight)) n->set_last_hop(me); } } } template bool Node::set_local_weight(int weight) { // If this node is no longer tentative we shouldn't be changing // its value. XLOG_ASSERT(_tentative); bool accepted = false; // If no valid state exists just set the weight otherwise make // sure it's less than the value already present. if (!_current._valid) { _current._path_length = weight; _current._valid = true; accepted = true; } else { if (_current._path_length > weight) { _current._path_length = weight; accepted = true; } } return accepted; } template int Node::get_local_weight() { // debug_msg("Node: %s\n", str().c_str()); // This node must be valid, tentative and its value must be valid. XLOG_ASSERT(_valid); XLOG_ASSERT(_tentative); XLOG_ASSERT(_current._valid); return _current._path_length; } template bool Node::delta(RouteCmd& rcmd) { // Has this node been deleted? if (!valid()) { rcmd = RouteCmd(RouteCmd::DELETE, nodename(), nodename()); return true; } path p,c; c = _current; p = _previous; _previous = _current; // It is possible that this node is not reachable. if (!c._valid) { XLOG_WARNING("Node: %s not reachable", str().c_str()); if (p._valid) { rcmd = RouteCmd(RouteCmd::DELETE, nodename(), nodename()); return true; } return false; } // If the previous result is invalid this is a new route. if (!p._valid) { XLOG_ASSERT(_current._valid); rcmd = RouteCmd(RouteCmd::ADD, nodename(), _current._first_hop->nodename()); return true; } XLOG_ASSERT(c._valid); XLOG_ASSERT(p._valid); // If nothing has changed, nothing to report. if (c._first_hop == p._first_hop) return false; rcmd = RouteCmd(RouteCmd::REPLACE, nodename(), _current._first_hop->nodename()); return true; } template class Pa: public unary_function >, void> { public: void operator()(const pair >& p) { Edge e = p.second; _result += e.str(); } string result() const { return _result; } private: string _result; }; template string Node::pp() const { string result = str() + ":: "; result += for_each(_adjacencies.begin(),_adjacencies.end(), Pa()).result(); return result; } template <> string Node::str() const { return _nodename; } template string Node::str() const { return _nodename.str(); } template inline void gc(const pair::NodeRef >& p) { p.second->garbage_collect(); } template void Spt::garbage_collect() { // Remove all the invalid nodes. for(typename Nodes::iterator ni = _nodes.begin(); ni != _nodes.end();) { typename Node::NodeRef node = ni->second; if (!node->valid()) { _nodes.erase(ni++); } else { ni++; } } // Garbage collect all the edges that point at deleted nodes. for_each(_nodes.begin(), _nodes.end(), ptr_fun(gc)); } template bool PriorityQueue::add(typename Node::NodeRef n, int weight) { // Find this node if its already in set and remove it. if (n->valid_weight()) { typename Tent::iterator i = _tentative.find(n); for(; i != _tentative.end(); i++) { if ((*i) == n) { // debug_msg("Erase %s\n", (*i)->str().c_str()); _tentative.erase(i); break; } } } bool accepted = n->set_local_weight(weight); _tentative.insert(n); return accepted; // debug_msg("Insert %s\n", n->str().c_str()); } template typename Node::NodeRef PriorityQueue::pop() { // Find this node if its already in set and remove it. typename Tent::iterator i = _tentative.begin(); if (i == _tentative.end()) return typename Node::NodeRef(); typename Node::NodeRef n = *i; _tentative.erase(i); // debug_msg("Pop %s\n", n->str().c_str()); return n; } template class Node; template class Spt; template class Node; template class Spt; template class Node; template class Spt; template class Node; template class Spt; template class Node; template class Spt; template class Node; template class Spt; #endif xorp/libproto/checksum.h0000664000076400007640000000367711421137511015476 0ustar greearbgreearb/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- */ /* vim:set sts=4 ts=8: */ /* * Copyright (c) 2001-2009 XORP, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License, Version * 2.1, June 1999 as published by the Free Software Foundation. * Redistribution and/or modification of this program under the terms of * any other version of the GNU Lesser General Public License is not * permitted. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, * see the GNU Lesser General Public License, Version 2.1, a copy of * which can be found in the XORP LICENSE.lgpl file. * * XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; * http://xorp.net */ /* * $XORP: xorp/libproto/checksum.h,v 1.6 2008/10/02 21:57:17 bms Exp $ */ #ifndef __LIBPROTO_CHECKSUM_H__ #define __LIBPROTO_CHECKSUM_H__ /* * Header file for checksum computations. */ # ifdef __cplusplus extern "C" { # endif /** * Checksum computation for Internet Protocol family headers. * * @param addr the address with the data. * @param len the length of the data. * @return the calculated checksum (in network order). */ extern uint16_t inet_checksum(const uint8_t *addr, size_t len); /** * Add two previously computed checksums for Internet Protocol family header. * * Note that if both checksums to add are in host order, the result is also in * host order. Similarly, if both checksums to add are in network order, the * result is also in network order. * * @param sum1 the first sum to add. * @param sum2 the second sum to add. * @return the sum of the two checksums. */ extern uint16_t inet_checksum_add(uint16_t sum1, uint16_t sum2); # ifdef __cplusplus } # endif #endif /* __LIBPROTO_CHECKSUM_H__ */ xorp/libproto/checksum.c0000664000076400007640000000615611421137511015464 0ustar greearbgreearb/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- */ /* vim:set sts=4 ts=8: */ /* * Copyright (c) 2001-2009 XORP, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License, Version * 2.1, June 1999 as published by the Free Software Foundation. * Redistribution and/or modification of this program under the terms of * any other version of the GNU Lesser General Public License is not * permitted. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, * see the GNU Lesser General Public License, Version 2.1, a copy of * which can be found in the XORP LICENSE.lgpl file. * * XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; * http://xorp.net */ /* * Checksum computations. */ #include "libproto_module.h" #include "libxorp/xorp.h" #include "checksum.h" /* * inet_checksum extracted from: * P I N G . C * * Author - * Mike Muuss * U. S. Army Ballistic Research Laboratory * December, 1983 * Modified at Uc Berkeley * * (ping.c) Status - * Public Domain. Distribution Unlimited. * * I N _ C K S U M * * Checksum routine for Internet Protocol family headers (C Version) * */ uint16_t inet_checksum(const uint8_t *addr, size_t len) { register size_t nleft = len; register const uint8_t *w = addr; uint16_t answer = 0; register uint32_t sum = 0; /* * Our algorithm is simple, using a 32 bit accumulator (sum), * we add sequential 16 bit words to it, and at the end, fold * back all the carry bits from the top 16 bits into the lower * 16 bits. */ while (nleft > 1) { sum += ((w[0] << 8) | w[1]); w += 2; nleft -= 2; } /* mop up an odd byte, if necessary */ if (nleft == 1) { /* * XXX: If the number of bytes is odd, we assume a padding * with a zero byte, hence we just "<< 8" the remaining * odd byte. */ sum += (w[0] << 8); } /* * add back carry outs from top 16 bits to low 16 bits */ sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ sum += (sum >> 16); /* add carry */ answer = ~sum; /* truncate to 16 bits */ /* * XXX: We need to swap the bytes, because we implicitly performed * network-to-host order swapping when we accessed the data. */ answer = htons(answer); /* swap the bytes */ return (answer); } /* * inet_checksum_add based on inet_cksum extracted from: * P I N G . C * * Status - * Public Domain. Distribution Unlimited. * * I N _ C K S U M _ A D D * * Checksum routine for Internet Protocol family headers (C Version): * adds two previously computed checksums. * */ uint16_t inet_checksum_add(uint16_t sum1, uint16_t sum2) { register uint32_t sum = (uint16_t)~sum1 + (uint16_t)~sum2; uint16_t answer; /* * add back carry outs from top 16 bits to low 16 bits */ sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ sum += (sum >> 16); /* add carry */ answer = ~sum; /* truncate to 16 bits */ return (answer); } xorp/libproto/proto_node_cli.hh0000664000076400007640000001677011540224225017042 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/libproto/proto_node_cli.hh,v 1.11 2008/10/02 21:57:18 bms Exp $ #ifndef __LIBPROTO_PROTO_NODE_CLI_HH__ #define __LIBPROTO_PROTO_NODE_CLI_HH__ #include "libxorp/xorp.h" #include "libxorp/callback.hh" #include "proto_unit.hh" // // Protocol generic CLI access // // // Constants definitions // // // Structures/classes, typedefs and macros // typedef XorpCallback1& >::RefPtr CLIProcessCallback; // /** * @short Base class for Protocol node CLI access. */ class ProtoNodeCli : public ProtoUnit { public: /** * Constructor for a given address family and module ID. * * @param init_family the address family (AF_INET or AF_INET6 * for IPv4 and IPv6 respectively). * @param init_module_id the module ID XORP_MODULE_* (@ref xorp_module_id). */ ProtoNodeCli(int init_family, xorp_module_id init_module_id); /** * Destructor */ virtual ~ProtoNodeCli(); /** * Add a CLI directory level that does not allow user to move to it. * * The added directory level does not allow an user to move to it * (i.e., user cannot "cd" to that directory level). * * @param dir_command_name the directory name to add. * @param dir_command_help the help message for the directory. * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_cli_dir_command(const char *dir_command_name, const char *dir_command_help); /** * Add a CLI directory level that may allow user to move to it. * * The added directory level may allow an user to move to it * (i.e., user can type the directory name to "cd" to that directory * level). * * @param dir_command_name the directory name to add. * @param dir_command_help the help message for the directory. * @param is_allow_cd if true, allow user to "cd" to that directory. * @param dir_cd_prompt if user can "cd" to that directory, the prompt * to appear after the "cd". * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_cli_dir_command(const char *dir_command_name, const char *dir_command_help, bool is_allow_cd, const char *dir_cd_prompt); /** * Add a CLI command. * * @param command_name the command name to add. * @param command_help the command help. * @param cli_process_callback the callback function that will be called * when this command is executed. * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_cli_command(const char *command_name, const char *command_help, const CLIProcessCallback& cli_process_callback); /** * Delete a CLI command. * * @param command_name the command name to delete. * @return XORP_OK on success, otherwise XORP_ERROR. */ int delete_cli_command(const char *command_name); /** * Delete all CLI commands that were added by this node. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int delete_all_cli_commands(); /** * Process a CLI command. * * This method is invoked when the CLI has detected a valid command * has been entered, and that command has been installed by this node. * * @param processor_name the processor name for this command. * @param cli_term_name the terminal name the command was entered from. * @param cli_session_id the CLI session ID the command was entered from. * @param command_name the command name to process. * @param command_args the command arguments to process. * @param ret_processor_name the processor name to return back to the CLI. * @param ret_cli_term_name the terminal name to return back. * @param ret_cli_session_id the CLI session ID to return back. * @param ret_command_output the command output to return back. * @return XORP_OK on success, otherwise XORP_ERROR. */ int cli_process_command(const string& processor_name, const string& cli_term_name, const uint32_t& cli_session_id, const string& command_name, const string& command_args, string& ret_processor_name, string& ret_cli_term_name, uint32_t& ret_cli_session_id, string& ret_command_output); /** * Print a message to the CLI interface. * * @param msg the message string to display. * @return the number of characters printed (not including * the trailing '\0'). */ int cli_print(const string& msg); /** * Add a CLI command to the CLI manager. * * This is a pure virtual function, and it must be implemented * by the particular protocol node class that inherits this base class. * * @param command_name the command name to add. * @param command_help the help message for the command. * @param is_command_cd if true, this is a directory level that allows * user to "cd" to that directory. * @param command_cd_prompt if this is a directory user can "cd" to it, * the prompt to appear after the "cd". * @param is_command_processor if true, this is an oridinary command * that can be invoked for processing rather than a directory level. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int add_cli_command_to_cli_manager(const char *command_name, const char *command_help, bool is_command_cd, const char *command_cd_prompt, bool is_command_processor) = 0; /** * Delete a CLI command from the CLI manager. * * This is a pure virtual function, and it must be implemented * by the particular protocol node class that inherits this base class. * * @param command_name the command name to delete. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int delete_cli_command_from_cli_manager(const char *command_name) = 0; private: int add_cli_command_entry(const char *command_name, const char *command_help, bool is_command_cd, const char *command_cd_prompt, bool is_command_processor, const CLIProcessCallback& cli_process_callback); int cli_process_dummy(const vector& /* argv */) { return (XORP_OK); } string _cli_result_string; // The string with the CLI result map _cli_callback_map; // Map with commands vector _cli_callback_vector; // Keep commands in order of adding }; // // Global variables // // // Global functions prototypes // #endif // __LIBPROTO_PROTO_NODE_CLI_HH__ xorp/libproto/spt.hh0000664000076400007640000006614211540225526014654 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8 sw=4: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/libproto/spt.hh,v 1.23 2008/10/02 21:57:18 bms Exp $ #ifndef __LIBPROTO_SPT_HH__ #define __LIBPROTO_SPT_HH__ // #define INCREMENTAL_SPT //#define DEBUG_LOGGING #define DEBUG_PRINT_FUNCTION_NAME #include "libxorp/ref_ptr.hh" #include "libxorp/c_format.hh" template class Spt; template class Edge; template class Node; template class PriorityQueue; template class RouteCmd; /** * Shortest Path Tree * * Compute shortest path tree's * */ template class Spt { public: // typedef Node::NodeRef NodeRef; typedef map::NodeRef> Nodes; Spt(bool trace = true) : _trace(trace) {} ~Spt(); /** * Clear all state from this Spt instance. */ void clear(); /** * Set the origin node. * * @return false if the node doesn't exist, otherwise true. */ bool set_origin(const A& node); /** * Add node * * @return false if the node already exists, otherwise true. */ bool add_node(const A& node); /** * Update node * * @return false if the node doesn't exist, otherwise true. */ bool update_node(const A& node); /** * Remove node * * @return false if the node doesn't exist or has already been * removed, otherwise true. */ bool remove_node(const A& node); /** * Does this node exist? * * @return true if the node exists. */ bool exists_node(const A& node); /** * Add a new edge. * * @param src source node must exist. * @param weight edge weight. * @param dst destination node, created if necessary. * @return true on success. */ bool add_edge(const A& src, int weight, const A& dst); /** * Update existing edge weight. * * @param src source node must exist. * @param weight new edge weight. * @param dst destination node must exist * @return true on success. */ bool update_edge_weight(const A& src, int weight, const A& dst); /** * Get edge weight. * * @param src source node must exist. * @param weight of this edge returned. * @param dst destination node must exist * @return true on success. */ bool get_edge_weight(const A& src, int& weight, const A& dst); /** * Remove an edge * * @param src source node must exist. * @param dst destination node must exist * @return true on success. */ bool remove_edge(const A& src, const A& dst); /** * Compute the tree. * * @param routes a list of route adds, deletes and replaces that must be * performed. * @return true on success */ bool compute(list >& routes); /** * Convert this graph to presentation format. * * @return C++ string with the human-readable ASCII representation * of the graph. */ string str() const; /** * Find this node. */ typename Node::NodeRef find_node(const A& node); private: bool _trace; // True of tracing is enabled. /** * Dijkstra * * @return true on success. */ bool dijkstra(); /** * Incremental SPT. * * @return true on success. */ bool incremental_spt(); /** * Remove all the nodes that have been marked for deletion. */ void garbage_collect(); typename Node::NodeRef _origin; // Origin node Nodes _nodes; // Nodes }; template class Node { public: typedef map > adjacency; // Only one edge allowed // between nodes. typedef ref_ptr > NodeRef; Node(A a, bool trace = false); ~Node(); /** * @return nodename */ A nodename(); /** * Set nodename * DONT' USE THIS METHOD. * Changing the nodename could alter the result of the comparison function, * causing confusion in the spt map. * * @return true on success. */ bool set_nodename(A nodename); /** * Add a new edge. * * @return true on success. false if edge already exists. */ bool add_edge(NodeRef dst, int weight); /** * Update edge weight. * * @return true on success, false if the edge doesn't exist. */ bool update_edge_weight(NodeRef dst, int weight); /** * Get edge weight. * * @return true on success, false if the edge doesn't exist. */ bool get_edge_weight(NodeRef dst, int& weight); /** * Remove an edge */ bool remove_edge(NodeRef dst); /** * Drop all adjacencies. * Used to revive invalid nodes. */ void drop_adjacencies(); /** * Remove all edges that point at invalid nodes. */ void garbage_collect(); /** * Set the valid state. */ void set_valid(bool p) { _valid = p; } /** * @return true if this node is not marked for deletion. */ bool valid() { return _valid;} /** * Set the tentative state. */ void set_tentative(bool p) { _tentative = p; } /** * Get the tentative state. */ bool tentative() { return _tentative; } /** * Invalidate the weights. */ void invalidate_weights() { _current._valid = false; } /** * Is the current entry valid. */ bool valid_weight() { return _current._valid; } /** * Set weights. * Visit all neighbours that are tentative and add this weight. * * @param delta_weight to add to this node. * @param tentative add all updated adjacent nodes to the * tentative set. */ void set_adjacent_weights(NodeRef me, int delta_weight, PriorityQueue& tentative); /** * Set local weight. * Set the weight on this node if its tentative and less than the * previous value. * * @return true if its accepted. */ bool set_local_weight(int weight); /** * get local weight. * */ int get_local_weight(); /** * The first hop to this node. */ void set_first_hop(NodeRef n) { _current._first_hop = n; } /** * The first hop to this node. */ NodeRef get_first_hop() { XLOG_ASSERT(_current._valid); return _current._first_hop; } /** * The node before this. */ void set_last_hop(NodeRef n) { _current._last_hop = n; } /** * The node before this. */ NodeRef get_last_hop() { XLOG_ASSERT(_current._valid); return _current._last_hop; } /** * Return the difference between this computation and the last. * * @param rcmd the new route to this node if it has changed. * @return true if the node has changed. */ bool delta(RouteCmd& rcmd); /** * Clear all the references to other nodes as well as possible * references to ourselves. */ void clear() { _current.clear(); _previous.clear(); _adjacencies.clear(); } /** * Pretty print this node with its adjacencies */ string pp() const; /** * @return C++ string with the human-readable ASCII representation * of the node. */ string str() const; private: bool _valid; // True if node is not marked for deletion. A _nodename; // Node name, external name of this node. adjacency _adjacencies; // Adjacency list bool _trace; // True of tracing is enabled. // private: // friend class Spt; bool _tentative; // Intermediate state for Dijkstra. struct path { path() : _valid(false) {} bool _valid; // Are these entries valid. NodeRef _first_hop; // On the path to this node, the // neighbour of the origin. NodeRef _last_hop; // On the path to this node the // previous node. int _path_length; // The sum of all the edge weights. void clear() { _first_hop = _last_hop = typename Node::NodeRef(); } }; path _current; // Current computation. path _previous; // Previous computation. }; template class Edge { public: Edge() {} Edge(typename Node::NodeRef dst, int weight) : _dst(dst), _weight(weight) {} string str() const { return c_format("%s(%d) ", _dst->str().c_str(), _weight); } typename Node::NodeRef _dst; int _weight; }; /** * Tentative nodes in a priority queue. */ template class PriorityQueue { public: /** * Add or Update the weight of a node. * @return true if the weight was used. */ bool add(typename Node::NodeRef n, int weight); /** * Pop the node with lowest weight. */ typename Node::NodeRef pop(); bool empty() { return _tentative.empty(); } private: #ifndef XORP_USE_USTL template struct lweight { bool operator ()(const typename Node::NodeRef& a, const typename Node::NodeRef& b) const { int aw = a->get_local_weight(); int bw = b->get_local_weight(); // If the weights match then sort on node names which must // be unique. if (aw == bw) return a.get() < b.get(); return aw < bw; } }; #endif #ifdef XORP_USE_USTL // TODO: I tried making ref-ptr compare work..but don't know if // it is actually functional. Need to fix up uSTL probably. typedef set::NodeRef> Tent; #else typedef set::NodeRef, lweight > Tent; #endif Tent _tentative; }; /** * The idealised command to execute. */ template class RouteCmd { public: enum Cmd {ADD, DELETE, REPLACE}; RouteCmd() {} RouteCmd(Cmd cmd, A node, A nexthop, A prevhop, int weight = 0, bool next_hop_changed = false, bool weight_changed = false) : _cmd(cmd), _node(node), _nexthop(nexthop), _prevhop(prevhop), _weight(weight),_next_hop_changed(next_hop_changed), _weight_changed(weight_changed) {} Cmd cmd() const { return _cmd; } const A& node() const { return _node; } const A& nexthop() const { return _nexthop; } const A& prevhop() const { return _prevhop; } int weight() const { return _weight; } bool next_hop_changed() const { return _next_hop_changed; } bool weight_changed() const { return _weight_changed; } bool operator==(const RouteCmd& lhs) { return _cmd == lhs._cmd && _node == lhs._node && _nexthop == lhs._nexthop && _prevhop == lhs._prevhop && _weight == lhs._weight && _next_hop_changed == lhs._next_hop_changed && _weight_changed == lhs._weight_changed; } string c() const { string cmd; switch(_cmd) { case ADD: cmd = "ADD"; break; case DELETE: cmd = "DELETE"; break; case REPLACE: cmd = "REPLACE"; break; } return cmd; } string str() const { return c() + " node: " + _node.str() + " nexthop: " + _nexthop.str() + " prevhop: " + _prevhop.str() + " weight: " + c_format("%d", _weight) + " next hop changed: " + bool_c_str(_next_hop_changed) + " weight changed: " + bool_c_str(_weight_changed); } private: Cmd _cmd; A _node; A _nexthop; A _prevhop; int _weight; bool _next_hop_changed; bool _weight_changed; }; template Spt::~Spt() { clear(); } template void Spt::clear() { //XLOG_TRACE(_trace, "Clearing spt %p.", this); // Release the origin node by assigning an empty value to its ref_ptr. _origin = typename Node::NodeRef(); // Free all node state in the Spt. // A depth first traversal might be more efficient, but we just want // to free memory here. Container Nodes knows nothing about the // degree of each Node. // Because the last node reference is held in the container we must be // careful not to introduce another one in this scope by using // a reference to a Node ref_ptr. while (! _nodes.empty()) { typename Nodes::iterator ii; for (ii = _nodes.begin(); ii != _nodes.end(); ) { typename Node::NodeRef& rnr = (*ii).second; //XLOG_ASSERT(! rnr.is_empty()); rnr->clear(); if (rnr.is_only()) { _nodes.erase(ii++); } else { ii++; } } } } template bool Spt::set_origin(const A& node) { // Lookup this node. It must exist. typename Node::NodeRef srcnode = find_node(node); if (srcnode.is_empty()) { XLOG_WARNING("Node does not exist %s", Node(node).str().c_str()); return false; } _origin = srcnode; return true; } template bool Spt::add_node(const A& node) { // If a valid node already exists return false typename Node::NodeRef srcnode = find_node(node); if (!srcnode.is_empty()) { if (srcnode->valid()) { XLOG_WARNING("Node already exists %s", Node(node).str().c_str()); return false; } else { // We are going to revive this node so dump its adjacency // info. srcnode->drop_adjacencies(); srcnode->set_valid(true); return true; } } Node *n = new Node(node, _trace); _nodes[node] = typename Node::NodeRef(n); //debug_msg("added node %p\n", n); return true; } template bool Spt::update_node(const A& node) { // If a valid node doesn't exist return false typename Node::NodeRef srcnode = find_node(node); if (srcnode.is_empty()) { XLOG_WARNING("Request to update non-existant node %s", Node(node).str().c_str()); return false; } if (!srcnode->valid()) { XLOG_WARNING("Node is not valid %s", Node(node).str().c_str()); return false; } srcnode->set_nodename(node); return true; } template bool Spt::remove_node(const A& node) { // If a valid node doesn't exist return false typename Node::NodeRef srcnode = find_node(node); if (srcnode.is_empty()) { XLOG_WARNING("Request to delete non-existant node %s", Node(node).str().c_str()); return false; } if (!srcnode->valid()) { XLOG_WARNING("Node already removed %s", Node(node).str().c_str()); return false; } srcnode->set_valid(false); return true; } template bool Spt::exists_node(const A& node) { return _nodes.count(node); } template bool Spt::add_edge(const A& src, int weight, const A& dst) { // Find the src node it must exist. typename Node::NodeRef srcnode = find_node(src); if (srcnode.is_empty()) { XLOG_WARNING("Node: %s not found", Node(src).str().c_str()); return false; } // The dst node doesn't have to exist. If it doesn't exist create it. typename Node::NodeRef dstnode = find_node(dst); if (dstnode.is_empty()) { if (!add_node(dst)) { XLOG_WARNING("Add node %s failed", Node(dst).str().c_str()); return false; } } dstnode = find_node(dst); if (dstnode.is_empty()) { XLOG_WARNING("Node: %s not found", Node(dst).str().c_str()); return false; } return srcnode->add_edge(dstnode, weight); } template bool Spt::update_edge_weight(const A& src, int weight, const A& dst) { typename Node::NodeRef srcnode = find_node(src); if (srcnode.is_empty()) { debug_msg("Node: %s not found\n", Node(src).str().c_str()); return false; } typename Node::NodeRef dstnode = find_node(dst); if (dstnode.is_empty()) { debug_msg("Node: %s not found\n", Node(dst).str().c_str()); return false; } return srcnode->update_edge_weight(dstnode, weight); } template bool Spt::get_edge_weight(const A& src, int& weight, const A& dst) { typename Node::NodeRef srcnode = find_node(src); if (srcnode.is_empty()) { debug_msg("Node: %s not found\n", Node(src).str().c_str()); return false; } typename Node::NodeRef dstnode = find_node(dst); if (dstnode.is_empty()) { debug_msg("Node: %s not found\n", Node(dst).str().c_str()); return false; } return srcnode->get_edge_weight(dstnode, weight); } template bool Spt::remove_edge(const A& src, const A& dst) { typename Node::NodeRef srcnode = find_node(src); if (srcnode.is_empty()) { debug_msg("Node: %s not found\n", Node(src).str().c_str()); return false; } typename Node::NodeRef dstnode = find_node(dst); if (dstnode.is_empty()) { debug_msg("Node: %s not found\n", Node(dst).str().c_str()); return false; } return srcnode->remove_edge(dstnode); } template bool Spt::compute(list >& routes) { #ifdef INCREMENTAL_SPT if (!incremental_spt()) return false; #else if (!dijkstra()) return false; #endif for(typename Nodes::const_iterator ni = _nodes.begin(); ni != _nodes.end(); ni++) { // We don't need to know how to reach ourselves. if (ni->second == _origin) continue; RouteCmd rcmd; if (ni->second->delta(rcmd)) routes.push_back(rcmd); } // Remove all the deleted nodes. garbage_collect(); return true; } template string Spt::str() const { string pres; if (_origin.is_empty()) { pres = "No origin\n"; } else pres = "Origin: " + _origin->str() + "\n"; for(typename Nodes::const_iterator ni = _nodes.begin(); ni != _nodes.end(); ni++) { pres += ni->second->pp() + "\n"; } return pres; } template inline void init_dijkstra(const pair::NodeRef >& p) { p.second->set_tentative(true); p.second->invalidate_weights(); } template bool Spt::dijkstra() { if (_origin.is_empty()) { XLOG_WARNING("No origin"); return false; } for_each(_nodes.begin(), _nodes.end(), init_dijkstra); typename Node::NodeRef current = _origin; _origin->set_tentative(false); int weight = 0; // Map of tentative nodes. PriorityQueue tentative; for(;;) { // Set the weight on all the nodes that are adjacent to this one. current->set_adjacent_weights(current, weight, tentative); if (tentative.empty()) break; current = tentative.pop(); XLOG_ASSERT(!current.is_empty()); // Get the weight of this node. weight = current->get_local_weight(); // Make the node permanent. current->set_tentative(false); // Compute the next hop to get to this node. typename Node::NodeRef prev = current->get_last_hop(); if (prev == _origin) current->set_first_hop(current); else current->set_first_hop(prev->get_first_hop()); debug_msg("Previous: %s\n", prev->str().c_str()); debug_msg("Permanent: %s distance %d next hop %s\n", current->str().c_str(), weight, current->get_first_hop()->str().c_str()); } return true; } template bool Spt::incremental_spt() { XLOG_UNFINISHED(); return true; } template typename Node::NodeRef Spt::find_node(const A& node) { typename map::NodeRef>::iterator i = _nodes.find(node); if (i != _nodes.end()) { // debug_msg("Node %s found\n", Node(node).str().c_str()); return (*i).second; } // debug_msg("Node %s not found\n", Node(node).str().c_str()); // Node *n = 0; return typename Node::NodeRef(/*n*/); } template Node::Node(A nodename, bool trace) : _valid(true), _nodename(nodename), _trace(trace) { } template Node::~Node() { clear(); } template A Node::nodename() { return _nodename; } template bool Node::set_nodename(A nodename) { _nodename = nodename; return true; } template bool Node::add_edge(NodeRef dst, int weight) { // See if this edge already exists. typename adjacency::iterator i = _adjacencies.find(dst->nodename()); // If this edge already exists consider this an error. if (i != _adjacencies.end()) { debug_msg("Edge from %s to %s exists\n", str().c_str(), dst->str().c_str()); return false; } _adjacencies.insert(make_pair(dst->nodename(), Edge(dst, weight))); return true; } template bool Node::update_edge_weight(NodeRef dst, int weight) { typename adjacency::iterator i = _adjacencies.find(dst->nodename()); // This edge should exist. if (i == _adjacencies.end()) { debug_msg("Edge from %s to %s doesn't exists\n", str().c_str(), dst->str().c_str()); return false; } Edge edge = i->second; edge._weight = weight; i->second = edge; return true; } template bool Node::get_edge_weight(NodeRef dst, int& weight) { typename adjacency::iterator i = _adjacencies.find(dst->nodename()); // This edge should exist. if (i == _adjacencies.end()) { debug_msg("Edge from %s to %s doesn't exists\n", str().c_str(), dst->str().c_str()); return false; } Edge edge = i->second; weight = edge._weight; return true; } template bool Node::remove_edge(NodeRef dst) { typename adjacency::iterator i = _adjacencies.find(dst->nodename()); if (i == _adjacencies.end()) { XLOG_WARNING("Edge from %s to %s doesn't exists", str().c_str(), dst->str().c_str()); return false; } _adjacencies.erase(i); return true; } template void Node::drop_adjacencies() { _adjacencies.clear(); } template void Node::garbage_collect() { typename adjacency::iterator ni; for(ni = _adjacencies.begin(); ni != _adjacencies.end();) { NodeRef node = ni->second._dst; if (!node->valid()) { // Clear any references that this node may have to itself. node->clear(); _adjacencies.erase(ni++); } else { ni++; } } } template void Node::set_adjacent_weights(NodeRef me, int delta_weight, PriorityQueue& tentative) { typename adjacency::iterator i; for(i = _adjacencies.begin(); i != _adjacencies.end(); i++) { NodeRef n = i->second._dst; debug_msg("Node: %s\n", n->str().c_str()); if (n->valid() && n->tentative()) { // It is critial that the weight of a node is not changed // while it is in the PriorityQueue. if (tentative.add(n, delta_weight + i->second._weight)) n->set_last_hop(me); } } } template bool Node::set_local_weight(int weight) { // If this node is no longer tentative we shouldn't be changing // its value. XLOG_ASSERT(_tentative); bool accepted = false; // If no valid state exists just set the weight otherwise make // sure it's less than the value already present. if (!_current._valid) { _current._path_length = weight; _current._valid = true; accepted = true; } else { if (_current._path_length > weight) { _current._path_length = weight; accepted = true; } } return accepted; } template int Node::get_local_weight() { // debug_msg("Node: %s\n", str().c_str()); // This node must be valid, tentative and its value must be valid. XLOG_ASSERT(_valid); XLOG_ASSERT(_tentative); XLOG_ASSERT(_current._valid); return _current._path_length; } template bool Node::delta(RouteCmd& rcmd) { // Has this node been deleted? if (!valid()) { rcmd = RouteCmd(RouteCmd::DELETE, nodename(), nodename(), nodename()); return true; } path p,c; c = _current; p = _previous; _previous = _current; // It is possible that this node is not reachable. if (!c._valid) { XLOG_TRACE(_trace, "Node: %s not reachable", str().c_str()); if (p._valid) { rcmd = RouteCmd(RouteCmd::DELETE, nodename(), nodename(), nodename()); return true; } return false; } // If the previous result is invalid this is a new route. if (!p._valid) { XLOG_ASSERT(_current._valid); rcmd = RouteCmd(RouteCmd::ADD, nodename(), _current._first_hop->nodename(), _current._last_hop->nodename(), _current._path_length); return true; } XLOG_ASSERT(c._valid); XLOG_ASSERT(p._valid); // If nothing has changed, nothing to report. if (c._first_hop == p._first_hop && c._path_length == p._path_length) return false; rcmd = RouteCmd(RouteCmd::REPLACE, nodename(), _current._first_hop->nodename(), _current._last_hop->nodename(), _current._path_length, c._first_hop != p._first_hop, c._path_length != p._path_length); return true; } template class Pa: public unary_function >, void> { public: void operator()(const pair >& p) { Edge e = p.second; _result += e.str(); } string result() const { return _result; } private: string _result; }; template string Node::pp() const { string result = str() + ":: "; result += for_each(_adjacencies.begin(),_adjacencies.end(), Pa()).result(); return result; } template string Node::str() const { return _nodename.str(); } #if 0 template <> string Node::str() const { return _nodename; } #endif template inline void gc(const pair::NodeRef >& p) { p.second->garbage_collect(); } template void Spt::garbage_collect() { // Remove all the invalid nodes. // Use a reference, no need to bump the ref_ptr in this scope. for(typename Nodes::iterator ni = _nodes.begin(); ni != _nodes.end();) { typename Node::NodeRef& node = ni->second; if (!node->valid()) { _nodes.erase(ni++); } else { ni++; } } // Garbage collect all the edges that point at deleted nodes. for_each(_nodes.begin(), _nodes.end(), gc); } template bool PriorityQueue::add(typename Node::NodeRef n, int weight) { // Find this node if its already in set and remove it. if (n->valid_weight()) { typename Tent::iterator i = _tentative.find(n); for(; i != _tentative.end(); i++) { if ((*i) == n) { // debug_msg("Erase %s\n", (*i)->str().c_str()); _tentative.erase(i); break; } } } bool accepted = n->set_local_weight(weight); _tentative.insert(n); return accepted; // debug_msg("Insert %s\n", n->str().c_str()); } template typename Node::NodeRef PriorityQueue::pop() { // Find this node if its already in set and remove it. typename Tent::iterator i = _tentative.begin(); if (i == _tentative.end()) return typename Node::NodeRef(); typename Node::NodeRef n = *i; _tentative.erase(i); // debug_msg("Pop %s\n", n->str().c_str()); return n; } #endif // __LIBPROTO_SPT_HH__ xorp/libproto/SConscript0000664000076400007640000000332611421137511015524 0ustar greearbgreearb# Copyright (c) 2009 XORP, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # $XORP$ import os Import("env") subdirs = [ 'tests' ] SConscript(dirs = subdirs, exports='env') env = env.Clone() is_shared = env.has_key('SHAREDLIBS') env.AppendUnique(CPPPATH = [ "#" ]) env.PrependUnique(LIBPATH = [ '$BUILDDIR/libxorp'] ) env.AppendUnique(LIBS = [ 'xorp_core' ]) sources = [ 'checksum.c', 'packet.cc', 'proto_node_cli.cc', 'proto_state.cc', 'proto_unit.cc', 'spt.cc', ] if is_shared: libxorp_proto = env.SharedLibrary(target = 'libxorp_proto', source = sources) if env['rtld_origin']: for obj in libxorp_proto: env.AddPostAction(libxorp_proto, env.Symlink(obj.abspath, os.path.join(env['xorp_alias_libdir'], str(obj)))) else: libxorp_proto = env.StaticLibrary(target = 'libxorp_proto', source = sources) if is_shared: env.Alias("install", env.InstallLibrary(env['xorp_libdir'], libxorp_proto)) Default(libxorp_proto) xorp/libproto/proto_state.cc0000664000076400007640000001122111421137511016355 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // Protocol unit generic functionality implementation // #include "libproto_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "proto_state.hh" // // Exported variables // // // Local constants definitions // // // Local structures/classes, typedefs and macros // // // Local variables // // // Local functions prototypes // /** * ProtoState::ProtoState: * * Proto state default constructor. **/ ProtoState::ProtoState() { _flags = 0; _debug_flag = false; _flags &= ~XORP_ENABLED; // XXX: default is to disable. } ProtoState::~ProtoState() { } int ProtoState::start() { if (is_disabled()) return (XORP_ERROR); if (is_up()) return (XORP_OK); // Already running ProtoState::reset(); if (ProtoState::startup() != XORP_OK) return (XORP_ERROR); ServiceBase::set_status(SERVICE_RUNNING); return (XORP_OK); } int ProtoState::stop() { if (is_down()) return (XORP_OK); // Already down if (ProtoState::shutdown() != XORP_OK) return (XORP_ERROR); ServiceBase::set_status(SERVICE_SHUTDOWN); return (XORP_OK); } int ProtoState::pending_start() { if (is_disabled()) return (XORP_ERROR); if (is_up()) return (XORP_OK); // Already running if (is_pending_up()) return (XORP_OK); // Already pending UP ServiceBase::set_status(SERVICE_STARTING); return (XORP_OK); } int ProtoState::pending_stop() { if (is_down()) return (XORP_OK); // Already down if (is_pending_down()) return (XORP_OK); // Already pending DOWN ServiceBase::set_status(SERVICE_SHUTTING_DOWN); return (XORP_OK); } int ProtoState::startup() { // // Test the service status // if ((ServiceBase::status() == SERVICE_STARTING) || (ServiceBase::status() == SERVICE_RUNNING)) return (XORP_OK); if (ServiceBase::status() != SERVICE_READY) return (XORP_ERROR); return (XORP_OK); } int ProtoState::reset() { if (ServiceBase::status() != SERVICE_READY) ServiceBase::set_status(SERVICE_READY); return (XORP_OK); } int ProtoState::shutdown() { // // 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); } return (XORP_OK); } void ProtoState::disable() { (void)ProtoState::shutdown(); _flags &= ~XORP_ENABLED; } string ProtoState::state_str() const { if (is_disabled()) return ("DISABLED"); if (is_down()) return ("DOWN"); if (is_up()) return ("UP"); if (is_pending_up()) return ("PENDING_UP"); if (is_pending_down()) return ("PENDING_DOWN"); return ("UNKNOWN"); } /** * Test if the unit state is UP. * * @return true if the unit state is UP. */ bool ProtoState::is_up() const { return (ServiceBase::status() == SERVICE_RUNNING); } /** * Test if the unit state is DOWN. * * @return true if the unit state is DOWN. */ bool ProtoState::is_down() const { return ((ServiceBase::status() == SERVICE_READY) || (ServiceBase::status() == SERVICE_SHUTDOWN)); } /** * Test if the unit state is PENDING-UP. * * @return true if the unit state is PENDING-UP. */ bool ProtoState::is_pending_up() const { return (ServiceBase::status() == SERVICE_STARTING); } /** * Test if the unit state is PENDING-DOWN. * * @return true if the unit state is PENDING-DOWN. */ bool ProtoState::is_pending_down() const { return (ServiceBase::status() == SERVICE_SHUTTING_DOWN); } xorp/libproto/packet.hh0000664000076400007640000006600411540225526015312 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/libproto/packet.hh,v 1.22 2008/12/18 11:13:59 abittau Exp $ #ifndef __LIBPROTO_PACKET_HH__ #define __LIBPROTO_PACKET_HH__ #include "libxorp/xorp.h" #include "libxorp/ipv4.hh" #include "libxorp/ipv6.hh" #include "libxorp/mac.hh" // // Network protocols related constants // #ifndef ETHERTYPE_IP #define ETHERTYPE_IP 0x0800 // IP protocol #endif #ifndef ETHERTYPE_ARP #define ETHERTYPE_ARP 0x0806 // Address Resolution Protocol #endif #ifndef IPPROTO_VRRP #define IPPROTO_VRRP 112 // Virtual Router Redundancy Protocol #endif /** * Extract an 8-bit number from the beginning of a buffer. * * Note that the integers in the buffer are stored in network order. * * @param ptr the buffer with the data. * @return an 8-bit number from the beginning of a buffer. */ inline uint8_t extract_8(const uint8_t *ptr) { uint8_t val; val = ptr[0]; return val; } /** * Embed an 8-bit number into the beginning of a buffer. * * Note that the integers in the buffer are stored in network order. * * @param ptr the buffer to store the data. * @param val the 8-bit value to embed into the beginning of the buffer. */ inline void embed_8(uint8_t *ptr, uint8_t val) { ptr[0] = val; } /** * Extract a 16-bit number from the beginning of a buffer. * * Note that the integers in the buffer are stored in network order. * * @param ptr the buffer with the data. * @return a 16-bit number from the beginning of a buffer. */ inline uint16_t extract_16(const uint8_t *ptr) { uint16_t val; val = ptr[0]; val <<= 8; val |= ptr[1]; return val; } /** * Embed a 16-bit number into the beginning of a buffer. * * Note that the integers in the buffer are stored in network order. * * @param ptr the buffer to store the data. * @param val the 16-bit value to embed into the beginning of the buffer. */ inline void embed_16(uint8_t *ptr, uint16_t val) { ptr[0] = (val >> 8) & 0xff; ptr[1] = val & 0xff; } /** * Extract a 24-bit number from the beginning of a buffer. * * Note that the integers in the buffer are stored in network order. * * @param ptr the buffer with the data. * @return a 24-bit number from the beginning of a buffer. */ inline uint32_t extract_24(const uint8_t *ptr) { uint32_t val; val = ptr[0]; val <<= 8; val |= ptr[1]; val <<= 8; val |= ptr[2]; return val; } /** * Embed a 24-bit number into the beginning of a buffer. * * Note that the integers in the buffer are stored in network order. * * @param ptr the buffer to store the data. * @param val the 24-bit value to embed into the beginning of the buffer. */ inline void embed_24(uint8_t *ptr, uint32_t val) { ptr[0] = (val >> 16) & 0xff; ptr[1] = (val >> 8) & 0xff; ptr[2] = val & 0xff; } /** * Extract a 32-bit number from the beginning of a buffer. * * Note that the integers in the buffer are stored in network order. * * @param ptr the buffer with the data. * @return a 32-bit number from the beginning of a buffer. */ inline uint32_t extract_32(const uint8_t *ptr) { uint32_t val; val = ptr[0]; val <<= 8; val |= ptr[1]; val <<= 8; val |= ptr[2]; val <<= 8; val |= ptr[3]; return val; } /** * Embed a 32-bit number into the beginning of a buffer. * * Note that the integers in the buffer are stored in network order. * * @param ptr the buffer to store the data. * @param val the 32-bit value to embed into the beginning of the buffer. */ inline void embed_32(uint8_t *ptr, uint32_t val) { ptr[0] = (val >> 24) & 0xff; ptr[1] = (val >> 16) & 0xff; ptr[2] = (val >> 8) & 0xff; ptr[3] = val & 0xff; } /** * Extract an 8-bit number from the beginning of a buffer. * * Note that the integers in the buffer are stored in host order. * * @param ptr the buffer with the data. * @return an 8-bit number from the beginning of a buffer. */ inline uint8_t extract_host_8(const uint8_t *ptr) { uint8_t val; val = ptr[0]; return val; } /** * Embed an 8-bit number into the beginning of a buffer. * * Note that the integers in the buffer are stored in host order. * * @param ptr the buffer to store the data. * @param val the 8-bit value to embed into the beginning of the buffer. */ inline void embed_host_8(uint8_t *ptr, uint8_t val) { ptr[0] = val; } /** * Extract a 16-bit number from the beginning of a buffer. * * Note that the integers in the buffer are stored in host order. * * @param ptr the buffer with the data. * @return a 16-bit number from the beginning of a buffer. */ inline uint16_t extract_host_16(const uint8_t *ptr) { union { uint16_t val; uint8_t c[sizeof(uint16_t)]; } u; u.c[0] = ptr[0]; u.c[1] = ptr[1]; return u.val; } /** * Embed a 16-bit number into the beginning of a buffer. * * Note that the integers in the buffer are stored in host order. * * @param ptr the buffer to store the data. * @param val the 16-bit value to embed into the beginning of the buffer. */ inline void embed_host_16(uint8_t *ptr, uint16_t val) { union { uint16_t val; uint8_t c[sizeof(uint16_t)]; } u; u.val = val; ptr[0] = u.c[0]; ptr[1] = u.c[1]; } /* * XXX: Note that we don't define extract_host_24() and embed_host_24(), * because 3 octets of data might occupy either 3 or 4 octets (depending * on the byte ordering). Hence, extract_host_32() and embed_host_32() * should be used instead. */ /** * Extract a 32-bit number from the beginning of a buffer. * * Note that the integers in the buffer are stored in host order. * * @param ptr the buffer with the data. * @return a 32-bit number from the beginning of a buffer. */ inline uint32_t extract_host_32(const uint8_t *ptr) { union { uint32_t val; uint8_t c[sizeof(uint32_t)]; } u; u.c[0] = ptr[0]; u.c[1] = ptr[1]; u.c[2] = ptr[2]; u.c[3] = ptr[3]; return u.val; } /** * Embed a 32-bit number into the beginning of a buffer. * * Note that the integers in the buffer are stored in host order. * * @param ptr the buffer to store the data. * @param val the 32-bit value to embed into the beginning of the buffer. */ inline void embed_host_32(uint8_t *ptr, uint32_t val) { union { uint32_t val; uint8_t c[sizeof(uint32_t)]; } u; u.val = val; ptr[0] = u.c[0]; ptr[1] = u.c[1]; ptr[2] = u.c[2]; ptr[3] = u.c[3]; } /** * Extract an integer number from the beginning of a buffer. * * Note that the integers in the buffer are stored in host order. * * @param ptr the buffer with the data. * @return an integer number from the beginning of a buffer. */ inline int extract_host_int(const uint8_t *ptr) { union { int val; uint8_t c[sizeof(int)]; } u; for (size_t i = 0; i < sizeof(int); i++) { u.c[i] = ptr[i]; } return u.val; } /** * Embed an integer number into the beginning of a buffer. * * Note that the integers in the buffer are stored in host order. * * @param ptr the buffer to store the data. * @param val the integer value to embed into the beginning of the buffer. */ inline void embed_host_int(uint8_t *ptr, int val) { union { int val; uint8_t c[sizeof(int)]; } u; u.val = val; for (size_t i = 0; i < sizeof(int); i++) { ptr[i] = u.c[i]; } } /** * @short IPv4 packet header. * * The IPv4 packet header has the following content: * * ip_vhl (1 byte): // IP ver/hdrlen (version << 4 | header length >> 2) * ip_tos (1 byte): // Type of service * ip_len (2 bytes): // Total length * ip_id (2 bytes): // Identification * ip_off (2 bytes): // Fragment offset field (least-significant 13 bits) * ip_ttl (1 byte): // Time to live * ip_p (1 byte): // Protocol * ip_sum (2 bytes): // Checksum * ip_src (4 bytes): // Source address * ip_dst (4 bytes): // Destination address */ class IpHeader4 { public: IpHeader4(const uint8_t* data) : _data(data), _ip_vhl(_data + _ip_vhl_offset), _ip_tos(_data + _ip_tos_offset), _ip_len(_data + _ip_len_offset), _ip_id(_data + _ip_id_offset), _ip_off(_data + _ip_off_offset), _ip_ttl(_data + _ip_ttl_offset), _ip_p(_data + _ip_p_offset), _ip_sum(_data + _ip_sum_offset), _ip_src(_data + _ip_src_offset), _ip_dst(_data + _ip_dst_offset) { x_static_assert(IpHeader4::SIZE == _ip_vhl_sizeof + _ip_tos_sizeof + _ip_len_sizeof + _ip_id_sizeof + _ip_off_sizeof + _ip_ttl_sizeof + _ip_p_sizeof + _ip_sum_sizeof + _ip_src_sizeof + _ip_dst_sizeof); x_static_assert(IpHeader4::SIZE == _ip_dst_offset + _ip_dst_sizeof); } static const size_t SIZE = 20; // The header size static const uint8_t IP_VERSION = 4; // IPv4 version /** * Get the IPv4 packet header size. * * Note that this is the header size only without any header options. * * @return the IPv4 packet header size. */ static size_t size() { return IpHeader4::SIZE; } /** * Get the buffer data. * * @return the buffer data. */ const uint8_t* data() const { return (_data); } /** * Methods to get various IP header fields. */ uint8_t ip_vhl() const { return extract_8(_ip_vhl); } uint8_t ip_tos() const { return extract_8(_ip_tos); } uint16_t ip_len() const { return extract_16(_ip_len); } uint16_t ip_id() const { return extract_16(_ip_id); } uint16_t ip_off() const { return extract_16(_ip_off); } uint8_t ip_ttl() const { return extract_8(_ip_ttl); } uint8_t ip_p() const { return extract_8(_ip_p); } uint16_t ip_sum() const { return extract_16(_ip_sum); } IPv4 ip_src() const { return IPv4(_ip_src); } IPv4 ip_dst() const { return IPv4(_ip_dst); } /* * A method to extract the ip_len value that is presumably stored * in host order. * * @return the ip_len value that is presumably stored in host order. */ uint16_t ip_len_host() const{ return extract_host_16(_ip_len); } /** * Get the IP protocol version of the header. * * @return the IP protocol version of the header. */ uint8_t ip_version() const { uint8_t v = ip_vhl(); return ((v >> 4) & 0x0f); } /** * Get the IPv4 packet header size (including any header options). * * @return the IPv4 packet header size (including any header options). */ uint8_t ip_header_len() const { return ((ip_vhl() & 0x0f) << 2); } /** * Get the IPv4 fragment offset (excluding the fragment flags). * * @return the IPv4 fragment offset (excluding the fragment flags). */ uint16_t ip_fragment_offset() const { return (ip_off() & IpHeader4::FRAGMENT_OFFSET_MASK); } /** * Get the IPv4 fragment flags. * * @return the IPv4 fragment flags. */ uint16_t ip_fragment_flags() const { return (ip_off() & IpHeader4::FRAGMENT_FLAGS_MASK); } /** * Test whether the IP header version is valid. * * @return true if the IP header version is valid, otherwise false. */ bool is_valid_version() const { return (ip_version() == IpHeader4::IP_VERSION); } /** * Fragment an IPv4 packet. * * Note: If the original packet is not larger than the MTU, then the packet * is not fragmented (i.e., @ref fragments is empty), and the return * value is XORP_OK. * * @param mtu the MTU for fragmenting the packet. * @param fragments the return-by-reference fragments of the IPv4 packet. * @param do_checksum if true, compute and set the checksum in the IPv4 * header, otherwise reset it to zero. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int fragment(size_t mtu, list >& fragments, bool do_checksum, string& error_msg) const; protected: // IPv4 header related constants static const uint16_t FRAGMENT_OFFSET_MASK = 0x1fff; static const uint16_t FRAGMENT_FLAGS_MASK = 0xe000; static const uint16_t FRAGMENT_FLAGS_IP_DF = 0x4000; // Don't fragment static const uint16_t FRAGMENT_FLAGS_IP_MF = 0x2000; // More fragments static const uint8_t OPTIONS_IPOPT_EOL = 0; // End of option list static const uint8_t OPTIONS_IPOPT_NOP = 1; // No operation static const size_t OPTIONS_IPOPT_OLEN = 1; // Option length offset static const uint8_t OPTIONS_COPIED_FLAG = 0x80; // Option copied flag // Sizes of the fields static const size_t _ip_vhl_sizeof = 1; static const size_t _ip_tos_sizeof = 1; static const size_t _ip_len_sizeof = 2; static const size_t _ip_id_sizeof = 2; static const size_t _ip_off_sizeof = 2; static const size_t _ip_ttl_sizeof = 1; static const size_t _ip_p_sizeof = 1; static const size_t _ip_sum_sizeof = 2; static const size_t _ip_src_sizeof = 4; static const size_t _ip_dst_sizeof = 4; // Offsets for the fields static const size_t _ip_vhl_offset = 0; static const size_t _ip_tos_offset = _ip_vhl_offset + _ip_vhl_sizeof; static const size_t _ip_len_offset = _ip_tos_offset + _ip_tos_sizeof; static const size_t _ip_id_offset = _ip_len_offset + _ip_len_sizeof; static const size_t _ip_off_offset = _ip_id_offset + _ip_id_sizeof; static const size_t _ip_ttl_offset = _ip_off_offset + _ip_off_sizeof; static const size_t _ip_p_offset = _ip_ttl_offset + _ip_ttl_sizeof; static const size_t _ip_sum_offset = _ip_p_offset + _ip_p_sizeof; static const size_t _ip_src_offset = _ip_sum_offset + _ip_sum_sizeof; static const size_t _ip_dst_offset = _ip_src_offset + _ip_src_sizeof; private: const uint8_t* _data; // The buffer data // Pointers to the fields const uint8_t* _ip_vhl; // IP version and header length const uint8_t* _ip_tos; // Type of service const uint8_t* _ip_len; // Total length const uint8_t* _ip_id; // Identification const uint8_t* _ip_off; // Fragment offset field const uint8_t* _ip_ttl; // Time to live const uint8_t* _ip_p; // Protocol const uint8_t* _ip_sum; // Checksum const uint8_t* _ip_src; // Source address const uint8_t* _ip_dst; // Destination address }; /** * @short Class for writing data to IPv4 packet header. */ class IpHeader4Writer : public IpHeader4 { public: IpHeader4Writer(uint8_t* data) : IpHeader4(data), _data(data), _ip_vhl(_data + _ip_vhl_offset), _ip_tos(_data + _ip_tos_offset), _ip_len(_data + _ip_len_offset), _ip_id(_data + _ip_id_offset), _ip_off(_data + _ip_off_offset), _ip_ttl(_data + _ip_ttl_offset), _ip_p(_data + _ip_p_offset), _ip_sum(_data + _ip_sum_offset), _ip_src(_data + _ip_src_offset), _ip_dst(_data + _ip_dst_offset) {} /** * Get the buffer data. * * @return the buffer data. */ uint8_t* data() { return (_data); } /** * Methods to set various IP header fields. */ void set_ip_vhl(uint8_t v) { embed_8(_ip_vhl, v); } void set_ip_tos(uint8_t v) { embed_8(_ip_tos, v); } void set_ip_len(uint16_t v) { embed_16(_ip_len, v); } void set_ip_id(uint16_t v) { embed_16(_ip_id, v); } void set_ip_off(uint16_t v) { embed_16(_ip_off, v); } void set_ip_ttl(uint8_t v) { embed_8(_ip_ttl, v); } void set_ip_p(uint8_t v) { embed_8(_ip_p, v); } void set_ip_sum(uint16_t v) { embed_16(_ip_sum, v); } void set_ip_src(const IPv4& v) { v.copy_out(_ip_src); } void set_ip_dst(const IPv4& v) { v.copy_out(_ip_dst); } /** * A method to compute and set the IP checksum. */ void compute_checksum(); /* * A method to embed the ip_len value by storing it in host order. * * @param v the ip_len value that will be stored in host order. */ void set_ip_len_host(uint16_t v) { embed_host_16(_ip_len, v); } /** * Set the IP protocol version of the header. * * @param v the IP protocol version of the header. */ void set_ip_version(uint8_t v) { uint8_t vhl = ((v << 4) | (ip_header_len() >> 2)); set_ip_vhl(vhl); } /** * Set the IPv4 packet header size (including any header options). * * @param v the IPv4 packet header size (including any header options). */ void set_ip_header_len(uint8_t v) { uint8_t vhl = ((ip_version() << 4) | (v >> 2)); set_ip_vhl(vhl); } /** * Set the IPv4 fragment offset (excluding the fragment flags). * * @param v the IPv4 fragment offset (excluding the fragment flags). */ void set_ip_fragment_offset(uint16_t v) { uint16_t off = v & IpHeader4::FRAGMENT_OFFSET_MASK; off |= ip_fragment_flags(); set_ip_off(off); } /** * Set the IPv4 fragment flags. * * @param v the IPv4 fragment flags. */ void set_ip_fragment_flags(uint16_t v) { uint16_t off = v & IpHeader4::FRAGMENT_FLAGS_MASK; off |= ip_fragment_offset(); set_ip_off(off); } private: uint8_t* _data; // The buffer data // Pointers to the fields uint8_t* _ip_vhl; // IP version and header length uint8_t* _ip_tos; // Type of service uint8_t* _ip_len; // Total length uint8_t* _ip_id; // Identification uint8_t* _ip_off; // Fragment offset field uint8_t* _ip_ttl; // Time to live uint8_t* _ip_p; // Protocol uint8_t* _ip_sum; // Checksum uint8_t* _ip_src; // Source address uint8_t* _ip_dst; // Destination address }; /** * @short IPv6 packet header. * * The IPv6 packet header has the following content: * * ip_vtc_flow (4 bytes): // 4 bits vers., 8 bits traf. class, 20 bits flow-ID * ip_plen (2 bytes): // Payload length * ip_nxt (1 byte): // Next header * ip_hlim (1 byte): // Hop limit * ip_src (16 bytes): // Source address * ip_dst (16 bytes): // Destination address */ class IpHeader6 { public: IpHeader6(const uint8_t* data) : _data(data), _ip_vtc_flow(_data + _ip_vtc_flow_offset), _ip_plen(_data + _ip_plen_offset), _ip_nxt(_data + _ip_nxt_offset), _ip_hlim(_data + _ip_hlim_offset), _ip_src(_data + _ip_src_offset), _ip_dst(_data + _ip_dst_offset) { x_static_assert(IpHeader6::SIZE == _ip_vtc_flow_sizeof + _ip_plen_sizeof + _ip_nxt_sizeof + _ip_hlim_sizeof + _ip_src_sizeof + _ip_dst_sizeof); x_static_assert(IpHeader6::SIZE == _ip_dst_offset + _ip_dst_sizeof); } static const size_t SIZE = 40; // The header size static const uint8_t IP_VERSION = 6; // IPv6 version /** * Get the IPv6 packet header size. * * Note that this is the header size only without any extention headers. * * @return the IPv6 packet header size. */ static size_t size() { return IpHeader6::SIZE; } /** * Get the buffer data. * * @return the buffer data. */ const uint8_t* data() const { return (_data); } /** * Methods to get various IP header fields. */ uint32_t ip_vtc_flow() const { return extract_32(_ip_vtc_flow); } uint16_t ip_plen() const { return extract_16(_ip_plen); } uint8_t ip_nxt() const { return extract_8(_ip_nxt); } uint8_t ip_hlim() const { return extract_8(_ip_hlim); } IPv6 ip_src() const { return IPv6(_ip_src); } IPv6 ip_dst() const { return IPv6(_ip_dst); } /** * Get the IP protocol version of the header. * * @return the IP protocol version of the header. */ uint8_t ip_version() const { uint32_t v = ip_vtc_flow() & IpHeader6::VERSION_MASK; return ((v >> IpHeader6::VERSION_SHIFT) & 0x0f); } /** * Get the IPv6 traffic class. * * @return the IPv6 traffic class. */ uint8_t ip_traffic_class() const { uint32_t tc = ip_vtc_flow() & IpHeader6::TRAFFIC_CLASS_MASK; return ((tc >> IpHeader6::TRAFFIC_CLASS_SHIFT) & 0xff); } /** * Get the IPv6 flow label. * * @return the IPv6 flow label. */ uint32_t ip_flow_label() const { uint32_t flow = ip_vtc_flow() & IpHeader6::FLOW_LABEL_MASK; return (flow >> IpHeader6::FLOW_LABEL_SHIFT); } /** * Test whether the IP header version is valid. * * @return true if the IP header version is valid, otherwise false. */ bool is_valid_version() const { return (ip_version() == IpHeader6::IP_VERSION); } protected: static const uint32_t VERSION_MASK = 0xf0000000; static const uint32_t TRAFFIC_CLASS_MASK = 0x0ff00000; static const uint32_t FLOW_LABEL_MASK = 0x000fffff; static const size_t VERSION_SHIFT = 28; static const size_t TRAFFIC_CLASS_SHIFT = 20; static const size_t FLOW_LABEL_SHIFT = 0; // Sizes of the fields static const size_t _ip_vtc_flow_sizeof = 4; static const size_t _ip_plen_sizeof = 2; static const size_t _ip_nxt_sizeof = 1; static const size_t _ip_hlim_sizeof = 1; static const size_t _ip_src_sizeof = 16; static const size_t _ip_dst_sizeof = 16; // Offsets for the fields static const size_t _ip_vtc_flow_offset = 0; static const size_t _ip_plen_offset = _ip_vtc_flow_offset + _ip_vtc_flow_sizeof; static const size_t _ip_nxt_offset = _ip_plen_offset + _ip_plen_sizeof; static const size_t _ip_hlim_offset = _ip_nxt_offset + _ip_nxt_sizeof; static const size_t _ip_src_offset = _ip_hlim_offset + _ip_hlim_sizeof; static const size_t _ip_dst_offset = _ip_src_offset + _ip_src_sizeof; private: const uint8_t* _data; // The buffer data // Pointers to the fields const uint8_t* _ip_vtc_flow; // IP version, traffic class and flow label const uint8_t* _ip_plen; // Payload length const uint8_t* _ip_nxt; // Next header const uint8_t* _ip_hlim; // Hop limit const uint8_t* _ip_src; // Source address const uint8_t* _ip_dst; // Destination address }; /** * @short Class for writing data to IPv6 packet header. */ class IpHeader6Writer : public IpHeader6 { public: IpHeader6Writer(uint8_t* data) : IpHeader6(data), _data(data), _ip_vtc_flow(_data + _ip_vtc_flow_offset), _ip_plen(_data + _ip_plen_offset), _ip_nxt(_data + _ip_nxt_offset), _ip_hlim(_data + _ip_hlim_offset), _ip_src(_data + _ip_src_offset), _ip_dst(_data + _ip_dst_offset) {} /** * Get the buffer data. * * @return the buffer data. */ uint8_t* data() { return (_data); } /** * Methods to set various IP header fields. */ void set_ip_vtc_flow(uint32_t v) { embed_32(_ip_vtc_flow, v); } void set_ip_plen(uint16_t v) { embed_16(_ip_plen, v); } void set_ip_nxt(uint8_t v) { embed_8(_ip_nxt, v); } void set_ip_hlim(uint8_t v) { embed_8(_ip_hlim, v); } void set_ip_src(const IPv6& v) { v.copy_out(_ip_src); } void set_ip_dst(const IPv6& v) { v.copy_out(_ip_dst); } /** * Set the IP protocol version of the header. * * @param v the IP protocol version of the header. */ void set_ip_version(uint8_t v) { uint32_t vtc_flow = ip_vtc_flow() & ~IpHeader6::VERSION_MASK; uint32_t shifted_v = v; shifted_v <<= IpHeader6::VERSION_SHIFT; vtc_flow |= shifted_v; set_ip_vtc_flow(vtc_flow); } /** * Set the IPv6 traffic class. * * @param v the IPv6 traffic class. */ void set_ip_traffic_class(uint8_t v) { uint32_t vtc_flow = ip_vtc_flow() & ~IpHeader6::TRAFFIC_CLASS_MASK; uint32_t shifted_v = v; shifted_v <<= IpHeader6::TRAFFIC_CLASS_SHIFT; vtc_flow |= shifted_v; set_ip_vtc_flow(vtc_flow); } /** * Set the IPv6 flow label. * * @param v the IPv6 flow label. */ void set_ip_flow_label(uint32_t v) { uint32_t vtc_flow = ip_vtc_flow() & ~IpHeader6::FLOW_LABEL_MASK; uint32_t shifted_v = v; shifted_v <<= IpHeader6::FLOW_LABEL_SHIFT; vtc_flow |= shifted_v; set_ip_vtc_flow(vtc_flow); } private: uint8_t* _data; // The buffer data // Pointers to the fields uint8_t* _ip_vtc_flow; // IP version, traffic class and flow label uint8_t* _ip_plen; // Payload length uint8_t* _ip_nxt; // Next header uint8_t* _ip_hlim; // Hop limit uint8_t* _ip_src; // Source address uint8_t* _ip_dst; // Destination address }; /** * @short Exception thrown when parsing malformed packets. */ class BadPacketException : public XorpReasonedException { public: BadPacketException(const char* file, size_t line, const string& why = "") : XorpReasonedException("BadPacketException", file, line, why) {} }; /** * @short an ARP packet. */ class ArpHeader { public: enum Op { ARP_REQUEST = 1, ARP_REPLY }; enum HwFmt { HW_ETHER = 1 }; ArpHeader(); ArpHeader(const vector& pkt); /** * Create a gratuitous ARP. I.e., an ARP request for my own IP address - * the one used in the source section of the ARP packet. * * @param output data (output argument). * @param MAC address of IP. * @param ip IP address to create request for. */ static void make_gratuitous(vector& payload, const Mac& mac, const IPv4& ip); /** * Set the sender information in the ARP packet. * * @param mac source MAC address. * @param ip source IP address. */ void set_sender(const Mac& mac, const IPv4& ip); /** * Create an ARP request for an IP address. * * @param ip IP address to ask request for. */ void set_request(const IPv4& ip); /** * Create an ARP reply. * * @param mac MAC address of requested IP address. * @param ip IP address requested in the ARP request. */ void set_reply(const Mac& mac, const IPv4& ip); /** * The size of the ARP packet (ARP header + data). * * @return the size of the ARP packet. */ uint32_t size() const; /** * Determine whether it is an ARP request. This (usually) implies whether * or not it is an ARP reply. * * @return true if it is an ARP request. */ bool is_request() const; /** * If an ARP request, return the IP address that is being asked for. * * @return the IP address being asked for. */ IPv4 get_request() const; /** * If this is an ARP request, create an ARP reply with the give MAC address. * * @param out the ARP reply data (output parameter). * @param mac the MAC address of the requested IP address. */ void make_reply(vector& out, const Mac& mac) const; private: uint16_t ah_hw_fmt; uint16_t ah_proto_fmt; uint8_t ah_hw_len; uint8_t ah_proto_len; uint16_t ah_op; uint8_t ah_data_store[6 * 2 + 4 * 2]; } __attribute__((__packed__)); #endif // __LIBPROTO_PACKET_HH__ xorp/libproto/proto_node_cli.cc0000664000076400007640000001653511421137511017026 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // Protocol generic CLI access implementation // #include "libproto_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/c_format.hh" #include "libxorp/token.hh" #include "proto_node_cli.hh" #include "proto_unit.hh" // // Exported variables // // // Local constants definitions // // // Local structures/classes, typedefs and macros // // // Local variables // // // Local functions prototypes // /** * ProtoNodeCli::ProtoNodeCli: * @init_family: The address family (%AF_INET or %AF_INET6 * for IPv4 and IPv6 respectively). * @init_module_id: The module ID (XORP_MODULE_*). * * ProtoNodeCli node constructor. **/ ProtoNodeCli::ProtoNodeCli(int init_family, xorp_module_id init_module_id) : ProtoUnit(init_family, init_module_id) { } ProtoNodeCli::~ProtoNodeCli() { // TODO: implement it delete_all_cli_commands(); } int ProtoNodeCli::add_cli_dir_command(const char *dir_command_name, const char *dir_command_help) { return (add_cli_command_entry(dir_command_name, dir_command_help, false, "", false, callback(this, &ProtoNodeCli::cli_process_dummy))); } int ProtoNodeCli::add_cli_dir_command(const char *dir_command_name, const char *dir_command_help, bool is_allow_cd, const char *dir_cd_prompt) { return (add_cli_command_entry(dir_command_name, dir_command_help, is_allow_cd, dir_cd_prompt, false, callback(this, &ProtoNodeCli::cli_process_dummy))); } int ProtoNodeCli::add_cli_command(const char *command_name, const char *command_help, const CLIProcessCallback& cli_process_callback) { return (add_cli_command_entry(command_name, command_help, false, "", true, cli_process_callback)); } int ProtoNodeCli::add_cli_command_entry(const char *command_name, const char *command_help, bool is_command_cd, const char *command_cd_prompt, bool is_command_processor, const CLIProcessCallback& cli_process_callback) { // // XXX: the command name and the command help are mandatory // if (command_name == NULL) { XLOG_ERROR("Cannot add CLI command: invalid command name: NULL"); return (XORP_ERROR); } if (command_help == NULL) { XLOG_ERROR("Cannot add CLI command '%s': invalid command help: NULL", command_name); return (XORP_ERROR); } // // Insert the command into the local map with the handler for it. // _cli_callback_map.insert(pair(string(command_name), cli_process_callback)); _cli_callback_vector.push_back(string(command_name)); // // Call the virtual function to add the command to the CLI manager. // if (add_cli_command_to_cli_manager(command_name, command_help, is_command_cd, command_cd_prompt, is_command_processor) != XORP_OK) { return (XORP_ERROR); } return (XORP_OK); } int ProtoNodeCli::delete_cli_command(const char *command_name) { // // XXX: the command name is mandatory // if (command_name == NULL) { XLOG_ERROR("Cannot delete CLI command: invalid command name: NULL"); return (XORP_ERROR); } // // XXX: Use a local copy of the command name string, because the original // string may be deleted by some of the erase operations below. // string command_name_str(command_name); // // Delete the command from the local vector of commands, // and the callback map // vector::iterator iter; for (iter = _cli_callback_vector.begin(); iter != _cli_callback_vector.end(); ++iter) { if (*iter == command_name_str) { _cli_callback_vector.erase(iter); break; } } map::iterator pos; pos = _cli_callback_map.find(command_name_str); if (pos == _cli_callback_map.end()) { XLOG_ERROR("Cannot delete CLI command '%s': not in the local map", command_name_str.c_str()); return (XORP_ERROR); } _cli_callback_map.erase(pos); // // Call the virtual function to delete the command from the CLI manager. // if (delete_cli_command_from_cli_manager(command_name_str.c_str()) != XORP_OK) { return (XORP_ERROR); } return (XORP_OK); } // // Delete all CLI commands // int ProtoNodeCli::delete_all_cli_commands() { int ret_code = XORP_OK; // // Delete all commands one after another in the reverse order they // were added. // while (_cli_callback_vector.size() > 0) { size_t i = _cli_callback_vector.size() - 1; if (delete_cli_command(_cli_callback_vector[i].c_str()) != XORP_OK) ret_code = XORP_ERROR; } return (ret_code); } int ProtoNodeCli::cli_process_command(const string& processor_name, const string& cli_term_name, const uint32_t& cli_session_id, const string& command_name, const string& command_args, string& ret_processor_name, string& ret_cli_term_name, uint32_t& ret_cli_session_id, string& ret_command_output) { // // Copy some of the return argument // ret_processor_name = processor_name; ret_cli_term_name = cli_term_name; ret_cli_session_id = cli_session_id, ret_command_output = ""; // // Check the request // if (command_name.empty()) return (XORP_ERROR); // // Lookup the command // map::iterator pos; pos = _cli_callback_map.find(command_name); if (pos == _cli_callback_map.end()) return (XORP_ERROR); CLIProcessCallback& cli_process_callback = pos->second; // // Create a vector of the arguments // vector argv; string token, token_line(command_args); do { token = pop_token(token_line); if (token.empty()) break; argv.push_back(token); } while (true); _cli_result_string = ""; // XX: reset the result buffer cli_process_callback->dispatch(argv); ret_command_output = _cli_result_string; _cli_result_string = ""; // Clean-up the result buffer return (XORP_OK); } /** * ProtoNodeCli::cli_print: * @msg: the message string to display. * * Print a message to the CLI interface. * * Return value: The number of characters printed (not including * the trailing `\0'). **/ int ProtoNodeCli::cli_print(const string& msg) { int old_size = _cli_result_string.size(); _cli_result_string += msg; return (_cli_result_string.size() - old_size); } xorp/libproto/tests/0000775000076400007640000000000011540224225014651 5ustar greearbgreearbxorp/libproto/tests/test_spt.cc0000664000076400007640000003713211540224225017033 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8 sw=4: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "libproto_module.h" #include "libxorp/xorp.h" #ifndef DEBUG_LOGGING #define DEBUG_LOGGING #endif /* DEBUG_LOGGING */ #ifndef DEBUG_PRINT_FUNCTION_NAME #define DEBUG_PRINT_FUNCTION_NAME #endif /* DEBUG_PRINT_FUNCTION_NAME */ #include "libxorp/test_main.hh" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/exceptions.hh" #include "libxorp/tokenize.hh" #include "spt.hh" template <> string Node::str() const { return _nodename; } template <> string RouteCmd::str() const { return c() + " node: " + _node + " nexthop: " + _nexthop + " prevhop: " + _prevhop + " weight: " + c_format("%d", _weight) + " next hop changed: " + bool_c_str(_next_hop_changed) + " weight changed: " + bool_c_str(_weight_changed); } /** * Utility routine to be used by tests to read in graphs from files. */ bool populate_graph(TestInfo& info, Spt *spt, string& fname) { // Lines starting with '#' are considered to be comments. // Blank lines are ignored. // A standard line is in the form of node weight node. // The first node that is encountered is considered to be the // origin. std::ifstream graph(fname.c_str()); if (!graph) { XLOG_WARNING("Couldn't open %s", fname.c_str()); return false; } string line; string origin; while (graph) { getline(graph, line); DOUT(info) << '<' << line << '>' << endl; // Split this line into words vector words; tokenize(line, words); // If this is an empty line ignore it. if (words.empty()) continue; // Is this a comment if (line[0] == '#') continue; if (words.size() != 3) { XLOG_WARNING("Bad input %s", line.c_str()); continue; } if (origin.empty()) origin = words[0]; if (!spt->exists_node(words[0])) spt->add_node(words[0]); if (!spt->exists_node(words[2])) spt->add_node(words[2]); spt->add_edge(words[0], atoi(words[1].c_str()), words[2]); } spt->set_origin(origin); graph.close(); return true; } /** * Print the route command. */ template class Pr: public unary_function, void> { public: Pr(TestInfo& info) : _info(info) {} void operator()(const RouteCmd& rcmd) { DOUT(_info) << rcmd.str() << endl; } private: TestInfo& _info; }; /** * Verify that the route computation gave the correct result by * comparing the expected and received routes. */ bool verify(TestInfo& info, list > received, list > expected) { // remove all the matching routes. list >::iterator ri, ei; for(ei = expected.begin(); ei != expected.end();) { bool found = false; for(ri = received.begin(); ri != received.end(); ri++) { if ((*ei) == (*ri)) { found = true; received.erase(ri); expected.erase(ei++); break; } } if (!found) { DOUT(info) << ei->str() << " Not received" << endl; return false; } } // There should be no expected routes left. if (!expected.empty()) { DOUT(info) << "Routes not received:\n"; for_each(expected.begin(), expected.end(), Pr(info)); return false; } // There should be no extra routes. if (!received.empty()) { DOUT(info) << "Additional Routes received:\n"; for_each(received.begin(), received.end(), Pr(info)); return false; } return true; } /** * Test adding and changing edge weights. */ bool test1(TestInfo& info) { Node a("A", info.verbose()); DOUT(info) << "Nodename: " << a.nodename() << endl; Node *b = new Node("B", info.verbose()); Node::NodeRef bref(b); if (!a.add_edge(bref, 10)) { DOUT(info) << "Adding an edge failed!!!\n"; return false; } // Add the same edge again to force an error. if (a.add_edge(bref, 10)) { DOUT(info) << "Adding the same edge twice succeeded!!!\n"; return false; } int weight; // Check the edge weight. if (!a.get_edge_weight(bref, weight)) { DOUT(info) << "Failed to get edge!!!\n"; return false; } if (weight != 10) { DOUT(info) << "Expected weight of 10 got " << weight << endl; return false; } // Change the weight to 20 and check if (!a.update_edge_weight(bref, 20)) { DOUT(info) << "Failed to update edge!!!\n"; return false; } if (!a.get_edge_weight(bref, weight)) { DOUT(info) << "Failed to get edge!!!\n"; return false; } if (weight != 20) { DOUT(info) << "Expected weight of 20 got " << weight << endl; return false; } return true; } bool test2(TestInfo& info) { Spt *spt = new Spt(info.verbose() /* enable tracing */); spt->add_node("A"); spt->add_node("B"); spt->add_edge("A", 10, "B"); if (spt->add_edge("A", 10, "B")) { DOUT(info) << "Adding the same edge twice succeeded!!!\n"; return false; } if (!spt->update_node("A")) { DOUT(info) << "Unable to update node A\n"; return false; } spt->set_origin("A"); int weight; if (!spt->get_edge_weight("A", weight, "B")) { DOUT(info) << "Failed to get weight from A to B\n"; return false; } DOUT(info) << "Edge weight from A to B is " << weight << endl; if (weight != 10) { DOUT(info) << "Edge weight from A to B should be 10 not " << weight << endl; return false; } spt->update_edge_weight("A", 5, "B"); if (!spt->get_edge_weight("A", weight, "B")) { DOUT(info) << "Failed to get weight from A to B\n"; return false; } DOUT(info) << "Edge weight from A to B is " << weight << endl; if (weight != 5) { DOUT(info) << "Edge weight from A to B should be 5 not " << weight << endl; return false; } if (spt->get_edge_weight("B", weight, "A")) { DOUT(info) << "Got edge weight from B to A should not be possible on unidirectional link\n"; return false; } delete spt; return true; } bool test3(TestInfo& info) { Spt *spt = new Spt(info.verbose() /* enable tracing */); spt->add_node("A"); for(int i = 0; i < 1000000; i++) spt->set_origin("A"); for(int i = 0; i < 1000000; i++) { spt->add_node("B"); spt->remove_node("B"); } delete spt; return true; } bool test4(TestInfo& info) { Spt spt(info.verbose() /* enable tracing */); list > routes; // This should fail as the origin has not yet been configured. if (spt.compute(routes)) return false; return true; } bool test5(TestInfo& info, string fname) { Spt spt(info.verbose() /* enable tracing */); if (!populate_graph(info, &spt, fname)) return false; DOUT(info) << spt.str(); list > routes; if (!spt.compute(routes)) return false; for_each(routes.begin(), routes.end(), Pr(info)); return true; } /** * Manipulate the database and verify the route commands. */ bool test6(TestInfo& info) { Spt spt(info.verbose() /* enable tracing */); spt.add_node("s"); spt.add_node("t"); spt.add_node("x"); spt.add_node("y"); spt.add_node("z"); spt.set_origin("s"); spt.add_edge("s", 10, "t"); spt.add_edge("s", 5, "y"); spt.add_edge("t", 1, "x"); spt.add_edge("t", 2, "y"); spt.add_edge("y", 3, "t"); spt.add_edge("y", 9, "x"); spt.add_edge("y", 2, "z"); spt.add_edge("x", 4, "z"); spt.add_edge("z", 6, "x"); DOUT(info) << spt.str(); list > routes, expected_routes; /* =========================================== */ routes.clear(); expected_routes.clear(); if (!spt.compute(routes)) return false; for_each(routes.begin(), routes.end(), Pr(info)); expected_routes. push_back(RouteCmd(RouteCmd::ADD, "t", "y", "y", 8)); expected_routes. push_back(RouteCmd(RouteCmd::ADD, "x", "y", "t", 9)); expected_routes. push_back(RouteCmd(RouteCmd::ADD, "y", "y", "s", 5)); expected_routes. push_back(RouteCmd(RouteCmd::ADD, "z", "y", "y", 7)); if (!verify(info, routes, expected_routes)) return false; /* =========================================== */ DOUT(info) << "remove edge s y" << endl; spt.remove_edge("s", "y"); routes.clear(); expected_routes.clear(); if (!spt.compute(routes)) return false; for_each(routes.begin(), routes.end(), Pr(info)); expected_routes. push_back(RouteCmd(RouteCmd::REPLACE, "t", "t", "s", 10, true, true)); expected_routes. push_back(RouteCmd(RouteCmd::REPLACE, "x", "t", "t", 11, true, true)); expected_routes. push_back(RouteCmd(RouteCmd::REPLACE, "y", "t", "t", 12, true, true)); expected_routes. push_back(RouteCmd(RouteCmd::REPLACE, "z", "t", "y", 14, true, true)); if (!verify(info, routes, expected_routes)) return false; /* =========================================== */ DOUT(info) << "add edge s 5 y" << endl; DOUT(info) << "update edge s 1 t" << endl; spt.add_edge("s", 5, "y"); spt.update_edge_weight("s", 1, "t"); routes.clear(); expected_routes.clear(); if (!spt.compute(routes)) return false; for_each(routes.begin(), routes.end(), Pr(info)); expected_routes. push_back(RouteCmd(RouteCmd::REPLACE, "t", "t", "s", 1, false, true)); expected_routes. push_back(RouteCmd(RouteCmd::REPLACE, "x", "t", "t", 2, false, true)); expected_routes. push_back(RouteCmd(RouteCmd::REPLACE, "y", "t", "t", 3, false, true)); expected_routes. push_back(RouteCmd(RouteCmd::REPLACE, "z", "t", "y", 5, false, true)); if (!verify(info, routes, expected_routes)) return false; /* =========================================== */ DOUT(info) << "update edge s 10 t" << endl; DOUT(info) << "remove node z" << endl; spt.update_edge_weight("s", 10, "t"); spt.remove_node("z"); routes.clear(); expected_routes.clear(); if (!spt.compute(routes)) return false; for_each(routes.begin(), routes.end(), Pr(info)); expected_routes. push_back(RouteCmd(RouteCmd::REPLACE, "t", "y", "y", 8, true, true)); expected_routes. push_back(RouteCmd(RouteCmd::REPLACE, "x", "y", "t", 9, true, true)); expected_routes. push_back(RouteCmd(RouteCmd::REPLACE, "y", "y", "s", 5, true, true)); expected_routes. push_back(RouteCmd(RouteCmd::DELETE, "z", "z", "z")); if (!verify(info, routes, expected_routes)) return false; /* =========================================== */ DOUT(info) << "add node z" << endl; spt.add_node("z"); routes.clear(); expected_routes.clear(); if (!spt.compute(routes)) return false; for_each(routes.begin(), routes.end(), Pr(info)); if (!verify(info, routes, expected_routes)) return false; /* =========================================== */ DOUT(info) << "No change" << endl; routes.clear(); expected_routes.clear(); if (!spt.compute(routes)) return false; for_each(routes.begin(), routes.end(), Pr(info)); if (!verify(info, routes, expected_routes)) return false; /* =========================================== */ DOUT(info) << "add edge x 4 z" << endl; DOUT(info) << "add edge y 2 z" << endl; spt.add_edge("x", 4, "z"); spt.add_edge("y", 2, "z"); routes.clear(); expected_routes.clear(); if (!spt.compute(routes)) return false; for_each(routes.begin(), routes.end(), Pr(info)); expected_routes. push_back(RouteCmd(RouteCmd::ADD, "z", "y", "y", 7)); if (!verify(info, routes, expected_routes)) return false; return true; } /** * Simple test used to look for memory leaks. */ bool test7(TestInfo& info) { Spt spt(info.verbose() /* enable tracing */); spt.add_node("s"); spt.add_node("t"); spt.set_origin("s"); spt.add_edge("s", 10, "t"); list > routes; if (!spt.compute(routes)) return false; return true; } /** * o ---10--> a * | ^ * | | * 3 12 * | | * V | * b ----3--> c * * Shortest path o->a is direct (nexthop: a) * However, nexthop of b is currently returned * * Provided by: Adam Barr * Used to trigger a problem in the nexthop code. */ bool test8(TestInfo& info) { Spt spt(info.verbose() /* enable tracing */); spt.add_node("o"); spt.set_origin("o"); spt.add_node("a"); spt.add_node("b"); spt.add_node("c"); spt.add_edge("o", 10, "a"); spt.add_edge("o", 3, "b"); spt.add_edge("b", 3, "c"); spt.add_edge("c", 12, "a"); list > routes, expected_routes; if (!spt.compute(routes)) { DOUT(info) << "spt compute failed" << endl; return false; } for_each(routes.begin(), routes.end(), Pr(info)); expected_routes. push_back(RouteCmd(RouteCmd::ADD, "a", "a", "o", 10)); expected_routes. push_back(RouteCmd(RouteCmd::ADD, "b", "b", "o", 3)); expected_routes. push_back(RouteCmd(RouteCmd::ADD, "c", "b", "b", 6)); if (!verify(info, routes, expected_routes)) { DOUT(info) << "verify failed" << endl; return false; } return true; } int main(int argc, char **argv) { XorpUnexpectedHandler x(xorp_unexpected_handler); xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); TestMain t(argc, argv); string test = t.get_optional_args("-t", "--test", "run only the specified test"); string fname = t.get_optional_args("-f", "--filename", "test data"); t.complete_args_parsing(); if (fname.empty()) { char *src = getenv("srcdir"); fname = "spt_graph1"; if (src) fname = string(src) + "/" + fname; } struct test { string test_name; XorpCallback1::RefPtr cb; } tests[] = { {"test1", callback(test1)}, {"test2", callback(test2)}, {"test3", callback(test3)}, {"test4", callback(test4)}, {"test5", callback(test5, fname)}, {"test6", callback(test6)}, {"test7", callback(test7)}, {"test8", callback(test8)}, }; try { if (test.empty()) { for (size_t i = 0; i < sizeof(tests) / sizeof(struct test); i++) t.run(tests[i].test_name, tests[i].cb); } else { for (size_t i = 0; i < sizeof(tests) / sizeof(struct test); i++) if (test == tests[i].test_name) { t.run(tests[i].test_name, tests[i].cb); return t.exit(); } t.failed("No test with name " + test + " found\n"); } } catch(...) { xorp_catch_standard_exceptions(); } xlog_stop(); xlog_exit(); return t.exit(); } xorp/libproto/tests/test_packet.cc0000664000076400007640000002540511421137511017473 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2006-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "libproto_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/exceptions.hh" #include "libxorp/test_main.hh" #include "libproto/packet.hh" #ifdef HAVE_GETOPT_H #include #endif // // TODO: XXX: remove after the switch to the TestMain facility is completed // #if 0 // // XXX: MODIFY FOR YOUR TEST PROGRAM // static const char *program_name = "test_packet"; static const char *program_description = "Test packet header manipulation"; static const char *program_version_id = "0.1"; static const char *program_date = "August 21, 2006"; static const char *program_copyright = "See file LICENSE"; static const char *program_return_value = "0 on success, 1 if test error, 2 if internal error"; #endif // 0 static bool s_verbose = false; bool verbose() { return s_verbose; } void set_verbose(bool v) { s_verbose = v; } static int s_failures = 0; bool failures() { return (s_failures)? (true) : (false); } void incr_failures() { s_failures++; } void reset_failures() { s_failures = 0; } // // printf(3)-like facility to conditionally print a message if verbosity // is enabled. // #define verbose_log(x...) _verbose_log(__FILE__,__LINE__, x) #define _verbose_log(file, line, x...) \ do { \ if (verbose()) { \ printf("From %s:%d: ", file, line); \ printf(x); \ } \ } while(0) // // Test and print a message whether two strings are lexicographically same. // The strings can be either C or C++ style. // #define verbose_match(s1, s2) \ _verbose_match(__FILE__, __LINE__, s1, s2) bool _verbose_match(const char* file, int line, const string& s1, const string& s2) { bool match = s1 == s2; _verbose_log(file, line, "Comparing %s == %s : %s\n", s1.c_str(), s2.c_str(), match ? "OK" : "FAIL"); if (match == false) incr_failures(); return match; } // // Test and print a message whether a condition is true. // // The first argument is the condition to test. // The second argument is a string with a brief description of the tested // condition. // #define verbose_assert(cond, desc) \ _verbose_assert(__FILE__, __LINE__, cond, desc) bool _verbose_assert(const char* file, int line, bool cond, const string& desc) { _verbose_log(file, line, "Testing %s : %s\n", desc.c_str(), cond ? "OK" : "FAIL"); if (cond == false) incr_failures(); return cond; } // // TODO: XXX: remove after the switch to the TestMain facility is completed // #if 0 /** * Print program info to output stream. * * @param stream the output stream the print the program info to. */ static void print_program_info(FILE *stream) { fprintf(stream, "Name: %s\n", program_name); fprintf(stream, "Description: %s\n", program_description); fprintf(stream, "Version: %s\n", program_version_id); fprintf(stream, "Date: %s\n", program_date); fprintf(stream, "Copyright: %s\n", program_copyright); fprintf(stream, "Return: %s\n", program_return_value); } /** * Print program usage information to the stderr. * * @param progname the name of the program. */ static void usage(const char* progname) { print_program_info(stderr); fprintf(stderr, "usage: %s [-v] [-h]\n", progname); fprintf(stderr, " -h : usage (this message)\n"); fprintf(stderr, " -v : verbose output\n"); } #endif // 0 /** * Test IPv4 packet header manipulation. */ bool test_ipv4_header(TestInfo& test_info) { UNUSED(test_info); uint8_t data[IpHeader4::SIZE]; // Test values uint8_t ip_tos = 1; uint16_t ip_len = 1024; uint16_t ip_id = 1000; uint16_t ip_fragment_offset = 2000; uint16_t ip_fragment_flags = 0xe000; uint8_t ip_ttl = 64; uint8_t ip_p = 100; uint16_t ip_sum = 0x7acd; IPv4 ip_src("1.2.3.4"); IPv4 ip_dst("5.6.7.8"); memset(data, 0, sizeof(data)); IpHeader4 iph(data); IpHeader4Writer iphw(data); // // Test whether the packet version is valid // verbose_assert(iph.is_valid_version() == false, "IPv4 invalid header version"); // // Set all fields // iphw.set_ip_version(IpHeader4::IP_VERSION); iphw.set_ip_header_len(IpHeader4::SIZE); iphw.set_ip_tos(ip_tos); iphw.set_ip_len(ip_len); iphw.set_ip_id(ip_id); iphw.set_ip_fragment_offset(ip_fragment_offset); iphw.set_ip_fragment_flags(ip_fragment_flags); iphw.set_ip_ttl(ip_ttl); iphw.set_ip_p(ip_p); iphw.set_ip_sum(ip_sum); iphw.set_ip_src(ip_src); iphw.set_ip_dst(ip_dst); // // Test the IPv4 version // verbose_assert(iph.is_valid_version() == true, "IPv4 valid header version"); verbose_assert(iph.ip_version() == IpHeader4::IP_VERSION, "IPv4 version"); // // Test the IPv4 packet header size // verbose_assert(iph.ip_header_len() == IpHeader4::SIZE, "IPv4 header length"); // // Test the IPv4 TOS field // verbose_assert(iph.ip_tos() == ip_tos, "IPv4 TOS field"); // // Test the total IPv4 packet length // verbose_assert(iph.ip_len() == ip_len, "IPv4 total packet length"); // // Test the IPv4 ID field // verbose_assert(iph.ip_id() == ip_id, "IPv4 identification field"); // // Test the IPv4 fragment offset // verbose_assert(iph.ip_fragment_offset() == ip_fragment_offset, "IPv4 fragment offset"); // // Test the IPv4 fragment flags // verbose_assert(iph.ip_fragment_flags() == ip_fragment_flags, "IPv4 fragment flags"); // // Test the IPv4 TTL // verbose_assert(iph.ip_ttl() == ip_ttl, "IPv4 TTL"); // // Test the IPv4 protocol // verbose_assert(iph.ip_p() == ip_p, "IPv4 protocol field"); // // Test the IPv4 packet header checksum // verbose_assert(iph.ip_sum() == ip_sum, "IPv4 checksum"); // Set the checksum to garbage and compute it again iphw.set_ip_sum(0xffff); iphw.compute_checksum(); verbose_assert(iph.ip_sum() == ip_sum, "IPv4 checksum"); // // Test the IPv4 source and destination address // verbose_assert(iph.ip_src() == ip_src, "IPv4 source address"); verbose_assert(iph.ip_dst() == ip_dst, "IPv4 destination address"); return (! failures()); } /** * Test IPv6 packet header manipulation. */ bool test_ipv6_header(TestInfo& test_info) { UNUSED(test_info); uint8_t data[IpHeader6::SIZE]; // Test values uint8_t ip_traffic_class = 10; uint32_t ip_flow_label = 2000; uint16_t ip_plen = 3000; uint8_t ip_nxt = 123; uint8_t ip_hlim = 64; IPv6 ip_src("1:2:3:4::"); IPv6 ip_dst("5:6:7:8::"); memset(data, 0, sizeof(data)); IpHeader6 iph(data); IpHeader6Writer iphw(data); // // Test whether the packet version is valid // verbose_assert(iph.is_valid_version() == false, "IPv6 invalid header version"); // // Set all fields // iphw.set_ip_version(IpHeader6::IP_VERSION); iphw.set_ip_traffic_class(ip_traffic_class); iphw.set_ip_flow_label(ip_flow_label); iphw.set_ip_plen(ip_plen); iphw.set_ip_nxt(ip_nxt); iphw.set_ip_hlim(ip_hlim); iphw.set_ip_src(ip_src); iphw.set_ip_dst(ip_dst); // // Test the IPv6 version // verbose_assert(iph.is_valid_version() == true, "IPv6 valid header version"); verbose_assert(iph.ip_version() == IpHeader6::IP_VERSION, "IPv6 version"); // // Test the IPv6 traffic class // verbose_assert(iph.ip_traffic_class() == ip_traffic_class, "IPv6 traffic class"); // // Test the IPv6 flow label // verbose_assert(iph.ip_flow_label() == ip_flow_label, "IPv6 flow label"); // // Test the IPv6 payload length // verbose_assert(iph.ip_plen() == ip_plen, "IPv6 payload length"); // // Test the IPv6 next header // verbose_assert(iph.ip_nxt() == ip_nxt, "IPv6 next header"); // // Test the IPv6 hop limit // verbose_assert(iph.ip_hlim() == ip_hlim, "IPv6 hop limit"); // // Test the IPv6 source and destination address // verbose_assert(iph.ip_src() == ip_src, "IPv6 source address"); verbose_assert(iph.ip_dst() == ip_dst, "IPv6 destination address"); return (! failures()); } int main(int argc, char * const argv[]) { XorpUnexpectedHandler x(xorp_unexpected_handler); // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); TestMain test_main(argc, argv); string test = test_main.get_optional_args("-t", "--test", "run only the specified test"); test_main.complete_args_parsing(); // // TODO: XXX: a temporary glue until we complete the switch to the // TestMain facility. // if (test_main.get_verbose()) set_verbose(true); struct test { string test_name; XorpCallback1::RefPtr cb; bool run_by_default; } tests[] = { { "test_ipv4_header", callback(test_ipv4_header), true }, { "test_ipv6_header", callback(test_ipv6_header), true } }; try { if (test.empty()) { for (size_t i = 0; i < sizeof(tests) / sizeof(struct test); i++) { if (! tests[i].run_by_default) continue; reset_failures(); test_main.run(tests[i].test_name, tests[i].cb); } } else { for (size_t i = 0; i < sizeof(tests) / sizeof(struct test); i++) { if (test == tests[i].test_name) { reset_failures(); test_main.run(tests[i].test_name, tests[i].cb); return test_main.exit(); } } test_main.failed("No test with name " + test + " found\n"); } } catch (...) { xorp_print_standard_exceptions(); } // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); return test_main.exit(); } xorp/libproto/tests/test_config_node_id.cc0000664000076400007640000003545411421137511021157 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "libproto_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/exceptions.hh" #ifdef HAVE_GETOPT_H #include #endif #include "libproto/config_node_id.hh" // // XXX: MODIFY FOR YOUR TEST PROGRAM // static const char *program_name = "test_config_node_id"; static const char *program_description = "Test ConfigNodeId class"; static const char *program_version_id = "0.1"; static const char *program_date = "September 23, 2005"; static const char *program_copyright = "See file LICENSE"; static const char *program_return_value = "0 on success, 1 if test error, 2 if internal error"; static bool s_verbose = false; bool verbose() { return s_verbose; } void set_verbose(bool v) { s_verbose = v; } static int s_failures = 0; bool failures() { return s_failures; } void incr_failures() { s_failures++; } // // printf(3)-like facility to conditionally print a message if verbosity // is enabled. // #define verbose_log(x...) _verbose_log(__FILE__,__LINE__, x) #define _verbose_log(file, line, x...) \ do { \ if (verbose()) { \ printf("From %s:%d: ", file, line); \ printf(x); \ } \ } while(0) // // Test and print a message whether two strings are lexicographically same. // The strings can be either C or C++ style. // #define verbose_match(s1, s2) \ _verbose_match(__FILE__, __LINE__, s1, s2) bool _verbose_match(const char* file, int line, const string& s1, const string& s2) { bool match = s1 == s2; _verbose_log(file, line, "Comparing %s == %s : %s\n", s1.c_str(), s2.c_str(), match ? "OK" : "FAIL"); if (match == false) incr_failures(); return match; } // // Test and print a message whether a condition is true. // // The first argument is the condition to test. // The second argument is a string with a brief description of the tested // condition. // #define verbose_assert(cond, desc) \ _verbose_assert(__FILE__, __LINE__, cond, desc) bool _verbose_assert(const char* file, int line, bool cond, const string& desc) { _verbose_log(file, line, "Testing %s : %s\n", desc.c_str(), cond ? "OK" : "FAIL"); if (cond == false) incr_failures(); return cond; } /** * Print program info to output stream. * * @param stream the output stream the print the program info to. */ static void print_program_info(FILE *stream) { fprintf(stream, "Name: %s\n", program_name); fprintf(stream, "Description: %s\n", program_description); fprintf(stream, "Version: %s\n", program_version_id); fprintf(stream, "Date: %s\n", program_date); fprintf(stream, "Copyright: %s\n", program_copyright); fprintf(stream, "Return: %s\n", program_return_value); } /** * Print program usage information to the stderr. * * @param progname the name of the program. */ static void usage(const char* progname) { print_program_info(stderr); fprintf(stderr, "usage: %s [-v] [-h]\n", progname); fprintf(stderr, " -h : usage (this message)\n"); fprintf(stderr, " -v : verbose output\n"); } /** * Test ConfigNodeId valid constructors. */ void test_config_node_id_valid_constructors() { // Test values for node ID and node position: "1" and "2" const string config_node_id_string = "1 2"; const string config_node_id_empty_string = ""; const ConfigNodeId::UniqueNodeId unique_node_id = 1; const ConfigNodeId::Position position = 2; // // Constructor from a string. // ConfigNodeId config_node_id1(config_node_id_string); verbose_match(config_node_id1.str(), config_node_id_string); // // Constructor from an empty string. // ConfigNodeId config_node_id1_1(config_node_id_empty_string); verbose_match(config_node_id1_1.str(), string("0 0")); verbose_assert(config_node_id1_1.is_empty(), "is_empty()"); // // Constructor from another ConfigNodeId. // ConfigNodeId config_node_id2(config_node_id1); verbose_match(config_node_id2.str(), config_node_id_string); // // Constructor from integer values. // ConfigNodeId config_node_id3(unique_node_id, position); verbose_match(config_node_id3.str(), config_node_id_string); verbose_assert(config_node_id3.unique_node_id() == unique_node_id, "compare unique_node_id()"); verbose_assert(config_node_id3.position() == position, "compare position()"); } /** * Test ConfigNodeId invalid constructors. */ void test_config_node_id_invalid_constructors() { // Invalid test values for node ID and node position: "A" and "B" const string invalid_config_node_id_string = "A B"; // // Constructor from an invalid init string. // try { // Invalid init string ConfigNodeId config_node_id(invalid_config_node_id_string); verbose_log("Cannot catch invalid ConfigNodeId string \"A B\" : FAIL\n"); incr_failures(); UNUSED(config_node_id); } catch (const InvalidString& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } } /** * Test ConfigNodeId valid copy in/out methods. */ void test_config_node_id_valid_copy_in_out() { // Test values for node ID and node position: "1" and "2" const string config_node_id_string = "1 2"; // // Copy a node ID from a string into ConfigNodeId structure. // ConfigNodeId config_node_id1(0, 0); verbose_assert(config_node_id1.copy_in(config_node_id_string) == 3, "copy_in(string&) for ConfigNodeId"); verbose_match(config_node_id1.str(), config_node_id_string); } /** * Test ConfigNodeId invalid copy in/out methods. */ void test_config_node_id_invalid_copy_in_out() { // Invalid test values for node ID and node position: "A" and "B" const string invalid_config_node_id_string = "A B"; // // Constructor from an invalid init string. // try { // Invalid init string ConfigNodeId config_node_id(0, 0); config_node_id.copy_in(invalid_config_node_id_string); verbose_log("Cannot catch invalid ConfigNodeId string \"A B\" : FAIL\n"); incr_failures(); UNUSED(config_node_id); } catch (const InvalidString& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } } /** * Test ConfigNodeId operators. */ void test_config_node_id_operators() { ConfigNodeId config_node_id_a("1 2"); ConfigNodeId config_node_id_b("1 3"); ConfigNodeId config_node_id_c("2 3"); // // Equality Operator // verbose_assert(config_node_id_a == config_node_id_a, "operator=="); verbose_assert(!(config_node_id_a == config_node_id_b), "operator=="); verbose_assert(!(config_node_id_a == config_node_id_c), "operator=="); // // Not-Equal Operator // verbose_assert(!(config_node_id_a != config_node_id_a), "operator!="); verbose_assert(config_node_id_a != config_node_id_b, "operator!="); verbose_assert(config_node_id_a != config_node_id_c, "operator!="); } /** * Test ConfigNodeId constant values. */ void test_config_node_id_const() { // // Test pre-defined constant values // verbose_assert(ConfigNodeId::ZERO() == ConfigNodeId("0 0"), "ZERO()"); } /** * Test ConfigNodeId miscellaneous methods. */ void test_config_node_id_misc() { ConfigNodeId::UniqueNodeId unique_node_id = 1; const ConfigNodeId::Position position = 2; const ConfigNodeId::InstanceId instance_id = 3; // // Test ConfigNodeId::set_instance_id() // ConfigNodeId config_node_id1(unique_node_id, position); verbose_match(config_node_id1.str(), "1 2"); config_node_id1.set_instance_id(instance_id); verbose_match(config_node_id1.str(), "12884901889 2"); // // Test ConfigNodeId::generate_unique_node_id() // ConfigNodeId config_node_id2(unique_node_id, position); config_node_id2.set_instance_id(instance_id); ConfigNodeId config_node_id3 = config_node_id2.generate_unique_node_id(); verbose_match(config_node_id3.str(), config_node_id2.str()); verbose_match(config_node_id3.str(), "12884901890 2"); } /** * Test ConfigNodeIdMap class. */ void test_config_node_id_map() { ConfigNodeId config_node_id1("3 0"); ConfigNodeId config_node_id2("2 3"); ConfigNodeId config_node_id3("1 2"); ConfigNodeId config_node_id_out_of_order("50 60"); ConfigNodeId config_node_id_unknown("10 20"); ConfigNodeIdMap node_id_map; ConfigNodeIdMap::iterator iter; string test_string; uint32_t foo = 1; // // Insert the elements // verbose_assert(node_id_map.insert(config_node_id1, foo++).second == true, "insert(config_node_id1)"); verbose_assert(node_id_map.insert(config_node_id2, foo++).second == true, "insert(config_node_id2)"); verbose_assert(node_id_map.insert(config_node_id3, foo++).second == true, "insert(config_node_id3)"); verbose_assert(node_id_map.insert_out_of_order(config_node_id_out_of_order, foo++).second == true, "insert(config_node_id_out_of_order)"); test_string = config_node_id1.str() + ", " + config_node_id2.str() + ", " + config_node_id3.str() + ", " + config_node_id_out_of_order.str(); verbose_match(node_id_map.str(), test_string); // // Try to reinsert an element with the same node ID. This should fail. // verbose_assert(node_id_map.insert(config_node_id3, foo++).second == false, "insert(config_node_id3)"); // Test for an unknown element verbose_assert(node_id_map.find(config_node_id_unknown) == node_id_map.end(), "find(config_node_id_unknown)"); // Test the elements verbose_assert(node_id_map.size() == 4, "size(4)"); verbose_assert(node_id_map.empty() == false, "empty()"); iter = node_id_map.begin(); verbose_match(iter->first.str(), config_node_id1.str()); verbose_assert(node_id_map.find(config_node_id1) == iter, "find(config_node_id1)"); ++iter; verbose_match(iter->first.str(), config_node_id2.str()); verbose_assert(node_id_map.find(config_node_id2) == iter, "find(config_node_id2)"); ++iter; verbose_match(iter->first.str(), config_node_id3.str()); verbose_assert(node_id_map.find(config_node_id3) == iter, "find(config_node_id3)"); ++iter; verbose_match(iter->first.str(), config_node_id_out_of_order.str()); verbose_assert(node_id_map.find(config_node_id_out_of_order) == iter, "find(config_node_id_out_of_order)"); // Erase the first element by using an interator iter = node_id_map.begin(); node_id_map.erase(iter); // Test the remaining elements verbose_assert(node_id_map.size() == 3, "size(3)"); verbose_assert(node_id_map.empty() == false, "empty()"); verbose_assert(node_id_map.find(config_node_id1) == node_id_map.end(), "find(config_node_id1)"); test_string = config_node_id2.str() + ", " + config_node_id3.str() + ", " + config_node_id_out_of_order.str(); verbose_match(node_id_map.str(), test_string); iter = node_id_map.begin(); verbose_match(iter->first.str(), config_node_id2.str()); verbose_assert(node_id_map.find(config_node_id2) == iter, "find(config_node_id2)"); ++iter; verbose_match(iter->first.str(), config_node_id3.str()); verbose_assert(node_id_map.find(config_node_id3) == iter, "find(config_node_id3)"); ++iter; verbose_match(iter->first.str(), config_node_id_out_of_order.str()); verbose_assert(node_id_map.find(config_node_id_out_of_order) == iter, "find(config_node_id_out_of_order)"); // Erase the new first element by using a node ID iter = node_id_map.begin(); node_id_map.erase(iter->first); // Test the remaining elements verbose_assert(node_id_map.size() == 2, "size(2)"); verbose_assert(node_id_map.empty() == false, "empty()"); verbose_assert(node_id_map.find(config_node_id2) == node_id_map.end(), "find(config_node_id2)"); test_string = config_node_id3.str() + ", " + config_node_id_out_of_order.str(); verbose_match(node_id_map.str(), test_string); iter = node_id_map.begin(); verbose_match(iter->first.str(), config_node_id3.str()); verbose_assert(node_id_map.find(config_node_id3) == iter, "find(config_node_id3)"); ++iter; verbose_match(iter->first.str(), config_node_id_out_of_order.str()); verbose_assert(node_id_map.find(config_node_id_out_of_order) == iter, "find(config_node_id_out_of_order)"); // Remove all elements node_id_map.clear(); verbose_assert(node_id_map.size() == 0, "size(0)"); verbose_assert(node_id_map.empty() == true, "empty()"); verbose_assert(node_id_map.find(config_node_id3) == node_id_map.end(), "find(config_node_id3)"); verbose_assert(node_id_map.find(config_node_id_out_of_order) == node_id_map.end(), "find(config_node_id_out_of_order)"); } int main(int argc, char * const argv[]) { int ret_value = 0; // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); int ch; while ((ch = getopt(argc, argv, "hv")) != -1) { switch (ch) { case 'v': set_verbose(true); break; case 'h': case '?': default: usage(argv[0]); xlog_stop(); xlog_exit(); if (ch == 'h') return (0); else return (1); } } argc -= optind; argv += optind; XorpUnexpectedHandler x(xorp_unexpected_handler); try { test_config_node_id_valid_constructors(); test_config_node_id_invalid_constructors(); test_config_node_id_valid_copy_in_out(); test_config_node_id_invalid_copy_in_out(); test_config_node_id_operators(); test_config_node_id_const(); test_config_node_id_misc(); test_config_node_id_map(); ret_value = failures() ? 1 : 0; } catch (...) { // Internal error xorp_print_standard_exceptions(); ret_value = 2; } // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); return (ret_value); } xorp/libproto/tests/SConscript0000664000076400007640000000303511421137511016663 0ustar greearbgreearb# Copyright (c) 2009 XORP, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # $XORP$ import os Import("env") env = env.Clone() env.AppendUnique(CPPPATH = [ '#', '$BUILDDIR', '$BUILDDIR/libproto', ]) env.AppendUnique(LIBPATH = [ '$BUILDDIR/libproto', '$BUILDDIR/xrl/interfaces', '$BUILDDIR/xrl/targets', '$BUILDDIR/libxipc', '$BUILDDIR/libxorp', '$BUILDDIR/libcomm', ]) env.AppendUnique(LIBS = [ 'xorp_proto', 'xorp_core', 'xorp_comm', ]) tests = [ 'checksum', 'config_node_id', 'packet', #'spt', # XXX notyet ] # XXX UNIX environment needs 'srcdir' set to point to source so # SPT test can find spt_graph1 file. # XXX No direct dependency on libproto in graph -- fix. test_targets = [] for t in tests: test_targets.append(env.AutoTest(target = 'test_%s' % t, source = 'test_%s.cc' % t)) xorp/libproto/tests/spt_graph10000664000076400007640000000011311421137511016636 0ustar greearbgreearb# Simple graph s 10 t s 5 y t 1 x t 2 y y 3 t y 9 x y 2 z x 4 z z 6 x xorp/libproto/tests/test_checksum.cc0000664000076400007640000002445611421137511020033 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2006-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "libproto_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/exceptions.hh" #include "libxorp/test_main.hh" #include "libproto/checksum.h" #ifdef HAVE_GETOPT_H #include #endif // // TODO: XXX: remove after the switch to the TestMain facility is completed // #if 0 // // XXX: MODIFY FOR YOUR TEST PROGRAM // static const char *program_name = "test_checksum"; static const char *program_description = "Test checksum calculation"; static const char *program_version_id = "0.1"; static const char *program_date = "August 11, 2006"; static const char *program_copyright = "See file LICENSE"; static const char *program_return_value = "0 on success, 1 if test error, 2 if internal error"; #endif // 0 static bool s_verbose = false; bool verbose() { return s_verbose; } void set_verbose(bool v) { s_verbose = v; } static int s_failures = 0; bool failures() { return (s_failures)? (true) : (false); } void incr_failures() { s_failures++; } void reset_failures() { s_failures = 0; } // // printf(3)-like facility to conditionally print a message if verbosity // is enabled. // #define verbose_log(x...) _verbose_log(__FILE__,__LINE__, x) #define _verbose_log(file, line, x...) \ do { \ if (verbose()) { \ printf("From %s:%d: ", file, line); \ printf(x); \ } \ } while(0) // // Test and print a message whether two strings are lexicographically same. // The strings can be either C or C++ style. // #define verbose_match(s1, s2) \ _verbose_match(__FILE__, __LINE__, s1, s2) bool _verbose_match(const char* file, int line, const string& s1, const string& s2) { bool match = s1 == s2; _verbose_log(file, line, "Comparing %s == %s : %s\n", s1.c_str(), s2.c_str(), match ? "OK" : "FAIL"); if (match == false) incr_failures(); return match; } // // Test and print a message whether a condition is true. // // The first argument is the condition to test. // The second argument is a string with a brief description of the tested // condition. // #define verbose_assert(cond, desc) \ _verbose_assert(__FILE__, __LINE__, cond, desc) bool _verbose_assert(const char* file, int line, bool cond, const string& desc) { _verbose_log(file, line, "Testing %s : %s\n", desc.c_str(), cond ? "OK" : "FAIL"); if (cond == false) incr_failures(); return cond; } // // TODO: XXX: remove after the switch to the TestMain facility is completed // #if 0 /** * Print program info to output stream. * * @param stream the output stream the print the program info to. */ static void print_program_info(FILE *stream) { fprintf(stream, "Name: %s\n", program_name); fprintf(stream, "Description: %s\n", program_description); fprintf(stream, "Version: %s\n", program_version_id); fprintf(stream, "Date: %s\n", program_date); fprintf(stream, "Copyright: %s\n", program_copyright); fprintf(stream, "Return: %s\n", program_return_value); } /** * Print program usage information to the stderr. * * @param progname the name of the program. */ static void usage(const char* progname) { print_program_info(stderr); fprintf(stderr, "usage: %s [-v] [-h]\n", progname); fprintf(stderr, " -h : usage (this message)\n"); fprintf(stderr, " -v : verbose output\n"); } #endif // 0 /** * Test checksum computation for Internet Protocol family headers. */ bool test_inet_checksum(TestInfo& test_info) { UNUSED(test_info); uint16_t checksum; uint16_t checksum1, checksum2; // Test values for packets of different length const uint8_t packet_length_0[] = { }; const uint8_t packet_length_1[] = { 0x1 }; const uint8_t packet_length_2[] = { 0x1, 0x2 }; const uint8_t packet_length_3[] = { 0x1, 0x2, 0x3 }; const uint8_t packet_length_4[] = { 0x1, 0x2, 0x3, 0x4 }; const uint8_t packet_length_16[] = { 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0x10 }; const uint8_t packet_all_zeros[] = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }; const uint8_t packet_all_ones[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; uint8_t packet_length_long1[0x10000]; uint8_t packet_length_long2[0x10000]; uint8_t packet_length_long3[0x10000]; // // Initialize some of the packets // for (size_t i = 0; i < sizeof(packet_length_long1); i++) { packet_length_long1[i] = i; } for (size_t i = 0; i < sizeof(packet_length_long2); i++) { packet_length_long2[i] = 0x0; } for (size_t i = 0; i < sizeof(packet_length_long3); i++) { packet_length_long3[i] = 0xff; } // // Test the checksum of a packet with zero length. // checksum = inet_checksum(packet_length_0, sizeof(packet_length_0)); verbose_assert(checksum == htons(0xffff), "Internet checksum for a packet with length 0 bytes"); // // Test the checksum for packets with length between 1 and 16 // checksum = inet_checksum(packet_length_1, sizeof(packet_length_1)); verbose_assert(checksum == htons(0xfeff), "Internet checksum for a packet with length 1 bytes"); checksum = inet_checksum(packet_length_2, sizeof(packet_length_2)); verbose_assert(checksum == htons(0xfefd), "Internet checksum for a packet with length 2 bytes"); checksum = inet_checksum(packet_length_3, sizeof(packet_length_3)); verbose_assert(checksum == htons(0xfbfd), "Internet checksum for a packet with length 3 bytes"); checksum = inet_checksum(packet_length_4, sizeof(packet_length_4)); verbose_assert(checksum == htons(0xfbf9), "Internet checksum for a packet with length 4 bytes"); checksum = inet_checksum(packet_length_16, sizeof(packet_length_16)); verbose_assert(checksum == htons(0xbfb7), "Internet checksum for a packet with length 16 bytes"); // // Test the checksum for packets initialized with all zero and all one // checksum = inet_checksum(packet_all_zeros, sizeof(packet_all_zeros)); verbose_assert(checksum == htons(0xffff), "Internet checksum for a packet initialized with all zeros"); checksum = inet_checksum(packet_all_ones, sizeof(packet_all_ones)); verbose_assert(checksum == htons(0x0), "Internet checksum for a packet initialized with all ones"); // // Test the checksum for long packets // checksum = inet_checksum(packet_length_long1, sizeof(packet_length_long1)); verbose_assert(checksum == htons(0xc03f), "Internet checksum for a long packet"); checksum = inet_checksum(packet_length_long2, sizeof(packet_length_long2)); verbose_assert(checksum == htons(0xffff), "Internet checksum for a long packet"); checksum = inet_checksum(packet_length_long3, sizeof(packet_length_long3)); verbose_assert(checksum == htons(0x0), "Internet checksum for a long packet"); // // Test the addition of two checksums // checksum1 = htons(0x1); checksum2 = htons(0x2); checksum = inet_checksum_add(checksum1, checksum2); verbose_assert(checksum == htons(0x3), "Addition of two checksums"); // // Test the addition of two zero checksums // checksum1 = htons(0x0); checksum2 = htons(0x0); checksum = inet_checksum_add(checksum1, checksum2); verbose_assert(checksum == htons(0x0), "Addition of two zero checksums"); // // Test the addition of two all-ones checksums // checksum1 = htons(0xff); checksum2 = htons(0xff); checksum = inet_checksum_add(checksum1, checksum2); verbose_assert(checksum == htons(0x1fe), "Addition of two all-ones checksums"); return (! failures()); } int main(int argc, char * const argv[]) { XorpUnexpectedHandler x(xorp_unexpected_handler); // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); TestMain test_main(argc, argv); string test = test_main.get_optional_args("-t", "--test", "run only the specified test"); test_main.complete_args_parsing(); // // TODO: XXX: a temporary glue until we complete the switch to the // TestMain facility. // if (test_main.get_verbose()) set_verbose(true); struct test { string test_name; XorpCallback1::RefPtr cb; bool run_by_default; } tests[] = { { "test_inet_checksum", callback(test_inet_checksum), true } }; try { if (test.empty()) { for (size_t i = 0; i < sizeof(tests) / sizeof(struct test); i++) { if (! tests[i].run_by_default) continue; reset_failures(); test_main.run(tests[i].test_name, tests[i].cb); } } else { for (size_t i = 0; i < sizeof(tests) / sizeof(struct test); i++) { if (test == tests[i].test_name) { reset_failures(); test_main.run(tests[i].test_name, tests[i].cb); return test_main.exit(); } } test_main.failed("No test with name " + test + " found\n"); } } catch (...) { xorp_print_standard_exceptions(); } // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); return test_main.exit(); } xorp/libproto/proto_node.hh0000664000076400007640000010535011540224225016204 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __LIBPROTO_PROTO_NODE_HH__ #define __LIBPROTO_PROTO_NODE_HH__ #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/callback.hh" #include "libxorp/eventloop.hh" #include "libxorp/status_codes.h" #include "libxorp/vif.hh" #include "proto_unit.hh" // // Protocol node generic functionality // class EventLoop; class IPvX; class IPvXNet; /** * @short Base class for a protocol node. */ template class ProtoNode : public ProtoUnit { public: /** * Constructor for a given address family, module ID, and event loop. * * @param init_family the address family (AF_INET or AF_INET6 for * IPv4 and IPv6 respectively). * @param init_module_id the module ID XORP_MODULE_* (@ref xorp_module_id). * @param init_eventloop the event loop to use. */ ProtoNode(int init_family, xorp_module_id init_module_id, EventLoop& init_eventloop) : ProtoUnit(init_family, init_module_id), _eventloop(init_eventloop), _node_status(PROC_NULL), _startup_requests_n(0), _shutdown_requests_n(0) {} /** * Destructor */ virtual ~ProtoNode() { // TODO: free vifs (after they are added, etc. } /** * Map a vif name to a vif index. * * @param vif_name the vif name to map to a vif index. * @return the virtual interface index for vif name @ref vif_name. */ uint32_t vif_name2vif_index(const string& vif_name) const; /** * Find an unused vif index. * * @return the smallest unused vif index if there is one available, * otherwise return @ref Vif::VIF_INDEX_INVALID. */ uint32_t find_unused_vif_index() const; /** * Find a virtual interface for a given name. * * @param name the name to search for. * @return the virtual interface with name @ref name if found, * otherwise NULL. */ V *vif_find_by_name(const string& name) const; /** * Find a virtual interface for a given address. * * Note that the PIM Register virtual interfaces are excluded, because * they are special, and because they may share the same address as some * of the other virtual interfaces. * * @param ipaddr_test the address to search for. * @return the virtual interface with address @ref ipaddr_test if found, * otherwise NULL. */ V *vif_find_by_addr(const IPvX& ipaddr_test) const; /** * Find a virtual interface for a given physical interface index. * * @param pif_index the physical interface index to search for. * @return the virtual interface with physical interface index @ref * pif_index if found, otherwise NULL. */ V *vif_find_by_pif_index(uint32_t pif_index) const; /** * Find a virtual interface for a given virtual interface index. * * @param vif_index the virtual interface index to search for. * @return the vvirtual interface with virtual interface index @ref * vif_index if found, otherwise NULL. */ V *vif_find_by_vif_index(uint32_t vif_index) const; /** * Find a virtual interface that belongs to the same subnet * or point-to-point link as a given address. * * Note that the PIM Register virtual interfaces are excluded, because * they are special, and because they may share the same address as some * of the other virtual interfaces. * * @param ipaddr_test the address to search by. * @return the virtual interface that belongs to the same subnet * or point-to-point link as address @ref ipaddr_test if found, * otherwise NULL. */ V *vif_find_same_subnet_or_p2p(const IPvX& ipaddr_test) const; /** * Test if an address belongs to one of my virtual interfaces. * * Note that the PIM Register virtual interfaces are excluded, because * they are special, and because they may share the same address as some * of the other virtual interfaces. * * @param ipaddr_test the address to test. * @return true if @ref ipaddr_test belongs to one of my virtual * interfaces, otherwise false. */ bool is_my_addr(const IPvX& ipaddr_test) const { return (vif_find_by_addr(ipaddr_test) != NULL); } /** * Add a virtual interface. * * @param vif a pointer to the virtual interface to add. * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_vif(V *vif); /** * Delete a virtual interface. * * Note: the @ref vif itself is not deleted, only its place in the * array of protocol vifs. * * @param vif a pointer to the virtual interface to delete. * @return XORP_OK on success, otherwise XORP_ERROR. */ int delete_vif(const V *vif); /** * Get the array of pointers to the virtual interfaces. * * @return the array of pointers to the virtual interfaces. */ vector& proto_vifs() { return (_proto_vifs); } /** * Get the array of pointers to the virtual interfaces. * * @return the array of pointers to the virtual interfaces. */ const vector& const_proto_vifs() const { return (_proto_vifs); } /** * Get the maximum number of vifs. * * Note: the interfaces that are not configured or are down are * also included. * * @return the maximum number of vifs we can have. */ uint32_t maxvifs() const { return (_proto_vifs.size()); } /** * Get the event loop this node is added to. * * @return the event loop this node is added to. */ EventLoop& eventloop() { return (_eventloop); } /** * Receive a protocol packet. * * This is a pure virtual function, and it must be implemented * by the particular protocol node class that inherits this base class. * * @param if_name the interface name the packet arrived on. * @param vif_name the vif name the packet arrived on. * @param src_address the IP source address. * @param dst_address the IP destination address. * @param ip_protocol the IP protocol number. * @param ip_ttl the IP TTL (hop-limit). If it has a negative value, then * the received value is unknown. * @param ip_tos the Type of Service (Diffserv/ECN bits for IPv4). If it * has a negative value, then the received value is unknown. * @param ip_router_alert if true, the IP Router Alert option was included * in the IP packet. * @param ip_internet_control if true, then this is IP control traffic. * @param payload the payload, everything after the IP header and options. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int proto_recv(const string& if_name, const string& vif_name, const IPvX& src_address, const IPvX& dst_address, uint8_t ip_protocol, int32_t ip_ttl, int32_t ip_tos, bool ip_router_alert, bool ip_internet_control, const vector& payload, string& error_msg) = 0; /** * Send a protocol packet. * * This is a pure virtual function, and it must be implemented * by the particular protocol node class that inherits this base class. * * @param if_name the interface to send the packet on. It is essential for * multicast. In the unicast case this field may be empty. * @param vif_name the vif to send the packet on. It is essential for * multicast. In the unicast case this field may be empty. * @param src_address the IP source address. * @param dst_address the IP destination address. * @param ip_protocol the IP protocol number. It must be between 1 and * 255. * @param ip_ttl the IP TTL (hop-limit). If it has a negative value, the * TTL will be set internally before transmission. * @param ip_tos the Type Of Service (Diffserv/ECN bits for IPv4). If it * has a negative value, the TOS will be set internally before * transmission. * @param ip_router_alert if true, then add the IP Router Alert option to * the IP packet. * @param ip_internet_control if true, then this is IP control traffic. * @param sndbuf the data buffer with the outgoing packet. * @param sndlen the data length in @ref sndbuf. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int proto_send(const string& if_name, const string& vif_name, const IPvX& src_address, const IPvX& dst_address, uint8_t ip_protocol, int32_t ip_ttl, int32_t ip_tos, bool ip_router_alert, bool ip_internet_control, const uint8_t* sndbuf, size_t sndlen, string& error_msg) = 0; /** * Receive a signal message. * * This is a pure virtual function, and it must be implemented * by the particular protocol node class that inherits this base class. * * @param src_module_instance_name the module instance name of the * module-origin of the message. * * @param message_type the message type. The particular values are * specific for the origin and recepient of this signal message. * * @param vif_index the vif index of the related interface * (message-specific relation). * * @param src the source address of the message. The exact meaning of * this address is message-specific. * * @param dst the destination address of the message. The exact meaning of * this address is message-specific. * * @param rcvbuf the data buffer with the additional information in * the message. * @param rcvlen the data length in @ref rcvbuf. * * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int signal_message_recv(const string& src_module_instance_name, int message_type, uint32_t vif_index, const IPvX& src, const IPvX& dst, const uint8_t *rcvbuf, size_t rcvlen) = 0; /** * Send a signal message. * * This is a pure virtual function, and it must be implemented * by the particular protocol node class that inherits this base class. * * @param dst_module_instance_name the module instance name of the * module-recepient of the message. * * @param message_type the message type. The particular values are * specific for the origin and recepient of this signal message. * * @param vif_index the vif index of the related interface * (message-specific relation). * * @param src the source address of the message. The exact meaning of * this address is message-specific. * * @param dst the destination address of the message. The exact meaning of * this address is message-specific. * * @param sndbuf the data buffer with the outgoing message. * * @param sndlen the data length in @ref sndbuf. * * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int signal_message_send(const string& dst_module_instance_name, int message_type, uint32_t vif_index, const IPvX& src, const IPvX& dst, const uint8_t *sndbuf, size_t sndlen) = 0; /** * Test if the node processing is done. * * @return true if the node processing is done, otherwise false. */ bool is_done() const { return (_node_status == PROC_DONE); } /** * Get the node status (see @ref ProcessStatus). * * @return the node status (see @ref ProcessStatus). */ ProcessStatus node_status() const { return (_node_status); } /** * Set the node status (see @ref ProcessStatus). * * @param v the value to set the node status to. */ void set_node_status(ProcessStatus v) { _node_status = v; } /** * Start a set of configuration changes. * * Note that it may change the node status. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int start_config(string& error_msg); /** * End a set of configuration changes. * * Note that it may change the node status. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int end_config(string& error_msg); /** * Find an unused vif index from the set of configured vifs. * * @return the smallest unused vif index from the set of configured vifs * if there is one available, otherwise return @ref Vif::VIF_INDEX_INVALID. */ uint32_t find_unused_config_vif_index() const; /** * Add a configured vif. * * @param vif the vif with the information to add. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_config_vif(const Vif& vif, string& error_msg); /** * Add a configured vif. * * @param vif_name the name of the vif to add. * @param vif_index the vif index of the vif to add. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_config_vif(const string& vif_name, uint32_t vif_index, string& error_msg); /** * Delete a configured vif. * * @param vif_name the name of the vif to delete. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int delete_config_vif(const string& vif_name, string& error_msg); /** * Add an address to a configured vif. * * @param vif_name the name of the vif. * @param addr the address to add. * @param subnet the subnet address to add. * @param broadcast the broadcast address to add. * @param peer the peer address to add. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_config_vif_addr(const string& vif_name, const IPvX& addr, const IPvXNet& subnet, const IPvX& broadcast, const IPvX& peer, string& error_msg); /** * Delete an address from a configured vif. * * @param vif_name the name of the vif. * @param addr the address to delete. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int delete_config_vif_addr(const string& vif_name, const IPvX& addr, string& error_msg); /** * Set the pif_index of a configured vif. * * @param vif_name the name of the vif. * @param pif_index the physical interface index. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_config_pif_index(const string& vif_name, uint32_t pif_index, string& error_msg); /** * Set the vif flags of a configured vif. * * @param vif_name the name of the vif. * @param is_pim_register true if the vif is a PIM Register interface. * @param is_p2p true if the vif is point-to-point interface. * @param is_loopback true if the vif is a loopback interface. * @param is_multicast true if the vif is multicast capable. * @param is_broadcast true if the vif is broadcast capable. * @param is_up true if the underlying vif is UP. * @param mtu the MTU of the vif. * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_config_vif_flags(const string& vif_name, bool is_pim_register, bool is_p2p, bool is_loopback, bool is_multicast, bool is_broadcast, bool is_up, uint32_t mtu, string& error_msg); /** * Get the map with configured vifs. * * @return a reference for the map with configured vifs. */ map& configured_vifs() { return (_configured_vifs); } /** * Find a configured virtual interface for a given name. * * @param name the name to search for. * @return the virtual interface with name @ref name if found, * otherwise NULL. */ const Vif *configured_vif_find_by_name(const string& name) const; /** * Get the node status (see @ref ProcessStatus). * * @param reason_msg return-by-reference string that contains * human-readable information about the status. * @return the node status (see @ref ProcessStatus). */ ProcessStatus node_status(string& reason_msg); /** * Increment the number of startup requests. */ void incr_startup_requests_n(); /** * Decrement the number of startup requests. */ void decr_startup_requests_n(); /** * Increment the number of shutdown requests. */ void incr_shutdown_requests_n(); /** * Decrement the number of shutdown requests. */ void decr_shutdown_requests_n(); protected: void update_status(); private: // TODO: add vifs, etc vector _proto_vifs; // The array with all protocol vifs EventLoop& _eventloop; // The event loop to use map _vif_name2vif_index_map; ProcessStatus _node_status; // The node status // // Status-related state // size_t _startup_requests_n; size_t _shutdown_requests_n; // // Config-related state // map _configured_vifs; // Configured vifs }; // // Deferred definitions // template ProcessStatus ProtoNode::node_status(string& reason_msg) { ProcessStatus status = _node_status; // Set the return message with the reason reason_msg = ""; switch (status) { case PROC_NULL: // Can't be running and in this state XLOG_UNREACHABLE(); break; case PROC_STARTUP: // Get the message about the startup progress reason_msg = c_format("Waiting for %u startup events", XORP_UINT_CAST(_startup_requests_n)); break; case PROC_NOT_READY: reason_msg = c_format("Waiting for configuration completion"); break; case PROC_READY: reason_msg = c_format("Node is READY"); break; case PROC_SHUTDOWN: // Get the message about the shutdown progress reason_msg = c_format("Waiting for %u shutdown events", XORP_UINT_CAST(_shutdown_requests_n)); break; case PROC_FAILED: reason_msg = c_format("Node is PROC_FAILED"); break; case PROC_DONE: // Process has completed operation break; default: // Unknown status XLOG_UNREACHABLE(); break; } return (status); } template inline void ProtoNode::incr_startup_requests_n() { _startup_requests_n++; XLOG_ASSERT(_startup_requests_n > 0); } template inline void ProtoNode::decr_startup_requests_n() { XLOG_ASSERT(_startup_requests_n > 0); _startup_requests_n--; update_status(); } template inline void ProtoNode::incr_shutdown_requests_n() { _shutdown_requests_n++; XLOG_ASSERT(_shutdown_requests_n > 0); } template inline void ProtoNode::decr_shutdown_requests_n() { XLOG_ASSERT(_shutdown_requests_n > 0); _shutdown_requests_n--; update_status(); } template inline void ProtoNode::update_status() { // // Test if the startup process has completed // if (ServiceBase::status() == SERVICE_STARTING) { if (_startup_requests_n > 0) return; // The startup process has completed ServiceBase::set_status(SERVICE_RUNNING); set_node_status(PROC_READY); return; } // // Test if the shutdown process has completed // if (ServiceBase::status() == SERVICE_SHUTTING_DOWN) { if (_shutdown_requests_n > 0) return; // The shutdown process has completed ServiceBase::set_status(SERVICE_SHUTDOWN); set_node_status(PROC_DONE); return; } // // Test if we have failed // if (ServiceBase::status() == SERVICE_FAILED) { set_node_status(PROC_DONE); return; } } template inline uint32_t ProtoNode::vif_name2vif_index(const string& vif_name) const { map::const_iterator iter; iter = _vif_name2vif_index_map.find(vif_name); if (iter != _vif_name2vif_index_map.end()) return (iter->second); return (Vif::VIF_INDEX_INVALID); } template inline uint32_t ProtoNode::find_unused_vif_index() const { for (uint32_t i = 0; i < _proto_vifs.size(); i++) { if (_proto_vifs[i] == NULL) return (i); } if (maxvifs() + 1 >= Vif::VIF_INDEX_MAX) return (Vif::VIF_INDEX_INVALID); return (maxvifs()); } template inline V * ProtoNode::vif_find_by_name(const string& name) const { typename vector::const_iterator iter; for (iter = _proto_vifs.begin(); iter != _proto_vifs.end(); ++iter) { V *vif = *iter; if (vif == NULL) continue; if (vif->name() == name) return (vif); } return (NULL); } template inline V * ProtoNode::vif_find_by_addr(const IPvX& ipaddr_test) const { typename vector::const_iterator iter; for (iter = _proto_vifs.begin(); iter != _proto_vifs.end(); ++iter) { V *vif = *iter; if (vif == NULL) continue; // // XXX: exclude the PIM Register vifs, because they are special // if (vif->is_pim_register()) continue; if (vif->is_my_addr(ipaddr_test)) return (vif); } return (NULL); } template inline V * ProtoNode::vif_find_by_pif_index(uint32_t pif_index) const { typename vector::const_iterator iter; for (iter = _proto_vifs.begin(); iter != _proto_vifs.end(); ++iter) { V *vif = *iter; if (vif == NULL) continue; if (vif->pif_index() == pif_index) return (vif); } return (NULL); } template inline V * ProtoNode::vif_find_by_vif_index(uint32_t vif_index) const { if (vif_index < _proto_vifs.size()) { // XXX: if vif_index becomes signed, we must check (vif_index >= 0) return (_proto_vifs[vif_index]); } return (NULL); } template inline V * ProtoNode::vif_find_same_subnet_or_p2p(const IPvX& ipaddr_test) const { typename vector::const_iterator iter; for (iter = _proto_vifs.begin(); iter != _proto_vifs.end(); ++iter) { V *vif = *iter; if (vif == NULL) continue; // // XXX: exclude the PIM Register vifs, because they are special // if (vif->is_pim_register()) continue; if (vif->is_same_subnet(ipaddr_test) || vif->is_same_p2p(ipaddr_test)) return (vif); } return (NULL); } template inline int ProtoNode::add_vif(V *vif) { if (vif == NULL) { XLOG_ERROR("Cannot add NULL vif"); return (XORP_ERROR); } if (vif_find_by_name(vif->name()) != NULL) { XLOG_ERROR("Cannot add vif %s: already exist", vif->name().c_str()); return (XORP_ERROR); } if (vif_find_by_vif_index(vif->vif_index()) != NULL) { XLOG_ERROR("Cannot add vif %s with vif_index = %d: " "already exist vif with such vif_index", vif->name().c_str(), vif->vif_index()); return (XORP_ERROR); } // XXX: we should check for the pif_index as well, but on older // systems the kernel doesn't assign pif_index to the interfaces // // Add enough empty entries for the new vif // while (vif->vif_index() >= maxvifs()) { _proto_vifs.push_back(NULL); } XLOG_ASSERT(_proto_vifs[vif->vif_index()] == NULL); // // Add the new vif // _proto_vifs[vif->vif_index()] = vif; // Add the entry to the vif_name2vif_index map _vif_name2vif_index_map.insert( pair(vif->name(), vif->vif_index())); return (XORP_OK); } template inline int ProtoNode::delete_vif(const V *vif) { if (vif == NULL) { XLOG_ERROR("Cannot delete NULL vif"); return (XORP_ERROR); } if (vif_find_by_name(vif->name()) != vif) { XLOG_ERROR("Cannot delete vif %s: inconsistent data pointers", vif->name().c_str()); return (XORP_ERROR); } if (vif_find_by_vif_index(vif->vif_index()) != vif) { XLOG_ERROR("Cannot delete vif %s with vif_index = %d: " "inconsistent data pointers", vif->name().c_str(), vif->vif_index()); return (XORP_ERROR); } XLOG_ASSERT(vif->vif_index() < maxvifs()); XLOG_ASSERT(_proto_vifs[vif->vif_index()] == vif); _proto_vifs[vif->vif_index()] = NULL; // // Remove unused vif pointers from the back of the vif array // while (_proto_vifs.size()) { size_t i = _proto_vifs.size() - 1; if (_proto_vifs[i] != NULL) break; _proto_vifs.pop_back(); } // Remove the entry from the vif_name2vif_index map map::iterator iter; iter = _vif_name2vif_index_map.find(vif->name()); XLOG_ASSERT(iter != _vif_name2vif_index_map.end()); _vif_name2vif_index_map.erase(iter); return (XORP_OK); } template inline int ProtoNode::start_config(string& error_msg) { switch (node_status()) { case PROC_NOT_READY: break; // OK, probably the first set of configuration changes, // or a batch of configuration changes that call end_config() // at the end. case PROC_READY: set_node_status(PROC_NOT_READY); break; // OK, start a set of configuration changes case PROC_STARTUP: break; // OK, we are still in the startup state case PROC_SHUTDOWN: error_msg = "invalid start config in PROC_SHUTDOWN state"; return (XORP_ERROR); case PROC_FAILED: error_msg = "invalid start config in PROC_FAILED state"; return (XORP_ERROR); case PROC_DONE: error_msg = "invalid start config in PROC_DONE state"; return (XORP_ERROR); case PROC_NULL: // FALLTHROUGH default: XLOG_UNREACHABLE(); return (XORP_ERROR); } return (XORP_OK); } template inline int ProtoNode::end_config(string& error_msg) { switch (node_status()) { case PROC_NOT_READY: set_node_status(PROC_READY); break; // OK, end a set of configuration changes case PROC_READY: break; // OK, maybe we got into PROC_READY directly from PROC_STARTUP case PROC_STARTUP: break; // OK, we are still in the startup state case PROC_SHUTDOWN: error_msg = "invalid end config in PROC_SHUTDOWN state"; return (XORP_ERROR); case PROC_FAILED: error_msg = "invalid end config in PROC_FAILED state"; return (XORP_ERROR); case PROC_DONE: error_msg = "invalid end config in PROC_DONE state"; return (XORP_ERROR); case PROC_NULL: // FALLTHROUGH default: XLOG_UNREACHABLE(); return (XORP_ERROR); } return (XORP_OK); } template inline uint32_t ProtoNode::find_unused_config_vif_index() const { map::const_iterator iter; for (uint32_t i = 0; i < Vif::VIF_INDEX_INVALID; i++) { bool is_avail = true; // Check if this vif index is in use for (iter = _configured_vifs.begin(); iter != _configured_vifs.end(); ++iter) { const Vif& vif = iter->second; if (vif.vif_index() == i) { is_avail = false; break; } } if (is_avail) return (i); } return (Vif::VIF_INDEX_INVALID); } template inline int ProtoNode::add_config_vif(const Vif& vif, string& error_msg) { if (start_config(error_msg) != XORP_OK) return (XORP_ERROR); if (add_config_vif(vif.name(), vif.vif_index(), error_msg) != XORP_OK) return (XORP_ERROR); list::const_iterator vif_addr_iter; for (vif_addr_iter = vif.addr_list().begin(); vif_addr_iter != vif.addr_list().end(); ++vif_addr_iter) { const VifAddr& vif_addr = *vif_addr_iter; if (add_config_vif_addr(vif.name(), vif_addr.addr(), vif_addr.subnet_addr(), vif_addr.broadcast_addr(), vif_addr.peer_addr(), error_msg) != XORP_OK) { string dummy_error_msg; delete_config_vif(vif.name(), dummy_error_msg); return (XORP_ERROR); } } if (set_config_pif_index(vif.name(), vif.pif_index(), error_msg) != XORP_OK) { string dummy_error_msg; delete_config_vif(vif.name(), dummy_error_msg); return (XORP_ERROR); } if (set_config_vif_flags(vif.name(), vif.is_pim_register(), vif.is_p2p(), vif.is_loopback(), vif.is_multicast_capable(), vif.is_broadcast_capable(), vif.is_underlying_vif_up(), vif.mtu(), error_msg) != XORP_OK) { string dummy_error_msg; delete_config_vif(vif.name(), dummy_error_msg); return (XORP_ERROR); } return (XORP_OK); } template inline int ProtoNode::add_config_vif(const string& vif_name, uint32_t vif_index, string& error_msg) { map::iterator iter; if (start_config(error_msg) != XORP_OK) return (XORP_ERROR); // Check whether we have vif with same name iter = _configured_vifs.find(vif_name); if (iter != _configured_vifs.end()) { error_msg = c_format("Cannot add vif %s: already have such vif", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } // Check whether we have vif with same vif_index for (iter = _configured_vifs.begin(); iter != _configured_vifs.end(); ++iter) { Vif* tmp_vif = &iter->second; if (tmp_vif->vif_index() == vif_index) { error_msg = c_format("Cannot add vif %s with vif_index %d: " "already have vif %s with same vif_index", vif_name.c_str(), vif_index, tmp_vif->name().c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } } // Insert the new vif Vif vif(vif_name); vif.set_vif_index(vif_index); _configured_vifs.insert(make_pair(vif_name, vif)); return (XORP_OK); } template inline int ProtoNode::delete_config_vif(const string& vif_name, string& error_msg) { map::iterator iter; if (start_config(error_msg) != XORP_OK) return (XORP_ERROR); // Find the vif iter = _configured_vifs.find(vif_name); if (iter == _configured_vifs.end()) { 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); } // Delete the vif _configured_vifs.erase(iter); return (XORP_OK); } template inline int ProtoNode::add_config_vif_addr(const string& vif_name, const IPvX& addr, const IPvXNet& subnet, const IPvX& broadcast, const IPvX& peer, string& error_msg) { map::iterator iter; if (start_config(error_msg) != XORP_OK) return (XORP_ERROR); // Find the vif iter = _configured_vifs.find(vif_name); if (iter == _configured_vifs.end()) { error_msg = c_format("Cannot add address to vif %s: no such vif", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } Vif* vif = &iter->second; // Test if we have same address if (vif->find_address(addr) != NULL) { error_msg = c_format("Cannot add address %s to vif %s: " "already have such address", cstring(addr), vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } // Add the address vif->add_address(addr, subnet, broadcast, peer); return (XORP_OK); } template inline int ProtoNode::delete_config_vif_addr(const string& vif_name, const IPvX& addr, string& error_msg) { map::iterator iter; if (start_config(error_msg) != XORP_OK) return (XORP_ERROR); // Find the vif iter = _configured_vifs.find(vif_name); if (iter == _configured_vifs.end()) { error_msg = c_format("Cannot delete address from vif %s: no such vif", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } Vif* vif = &iter->second; // Test if we have this address if (vif->find_address(addr) == NULL) { error_msg = c_format("Cannot delete address %s from vif %s: " "no such address", cstring(addr), vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); } // Delete the address vif->delete_address(addr); return (XORP_OK); } template inline int ProtoNode::set_config_pif_index(const string& vif_name, uint32_t pif_index, string& error_msg) { map::iterator iter; if (start_config(error_msg) != XORP_OK) return (XORP_ERROR); // Find the vif iter = _configured_vifs.find(vif_name); if (iter == _configured_vifs.end()) { error_msg = c_format("Cannot set pif_index for vif %s: no such vif", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } Vif* vif = &iter->second; vif->set_pif_index(pif_index); return (XORP_OK); } template inline int ProtoNode::set_config_vif_flags(const string& vif_name, bool is_pim_register, bool is_p2p, bool is_loopback, bool is_multicast, bool is_broadcast, bool is_up, uint32_t mtu, string& error_msg) { map::iterator iter; if (start_config(error_msg) != XORP_OK) return (XORP_ERROR); // Find the vif iter = _configured_vifs.find(vif_name); if (iter == _configured_vifs.end()) { error_msg = c_format("Cannot set flags for vif %s: no such vif", vif_name.c_str()); XLOG_ERROR("%s", error_msg.c_str()); return (XORP_ERROR); } Vif* vif = &iter->second; vif->set_pim_register(is_pim_register); vif->set_p2p(is_p2p); vif->set_loopback(is_loopback); vif->set_multicast_capable(is_multicast); vif->set_broadcast_capable(is_broadcast); vif->set_underlying_vif_up(is_up); vif->set_mtu(mtu); return (XORP_OK); } template inline const Vif * ProtoNode::configured_vif_find_by_name(const string& name) const { map::const_iterator iter; iter = _configured_vifs.find(name); if (iter != _configured_vifs.end()) return (&iter->second); return (NULL); } // // Global variables // // // Global functions prototypes // #endif // __LIBPROTO_PROTO_NODE_HH__ xorp/libproto/config_node_id.hh0000664000076400007640000004132711540224225016765 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/libproto/config_node_id.hh,v 1.12 2008/10/02 21:57:17 bms Exp $ #ifndef __LIBPROTO_CONFIG_NODE_ID_HH__ #define __LIBPROTO_CONFIG_NODE_ID_HH__ #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/exceptions.hh" // // Configuration Node ID support // /** * @short Class for encoding and decoding configuration-related node IDs. * * The node ID is composed of two uint64_t integers. * The first integer is the unique node ID. The second integer is * the node positiion and is set to the unique node ID of the previous * node. If it is the first node, then the position is set to 0. * The upper half of the unique node ID (the most significant 32 bits) * is the instance ID. The lower half of the unique node ID (the least * significant 32 bits) is the unique part of the node ID for that instance. */ class ConfigNodeId { public: typedef uint64_t UniqueNodeId; typedef uint64_t Position; typedef uint32_t InstanceId; /** * Constructor from a string. * * The string format is two uint64_t integers separated by space. * The first integer is the unique node ID. The second integer is * the node positiion and is set to the unique node ID of the previous * node. If it is the first node, then the position is set to 0. * * @param s the initialization string. */ explicit ConfigNodeId(const string& s) throw (InvalidString) { copy_in(s); } #ifdef XORP_USE_USTL ConfigNodeId() { } #endif /** * Constructor for a given unique node ID and position. * * @param unique_node_id the unique node ID. * @param position the position of the node. */ ConfigNodeId(const UniqueNodeId& unique_node_id, const Position& position) : _unique_node_id(unique_node_id), _position(position) {} /** * Destructor */ virtual ~ConfigNodeId() {} /** * Copy an unique node ID from a string into ConfigNodeId structure. * * @param from_string the string to copy the node ID from. * @return the number of copied octets. */ size_t copy_in(const string& from_string) throw (InvalidString); /** * Equality Operator * * @param other the right-hand operand to compare against. * @return true if the left-hand operand is numerically same as the * right-hand operand. */ bool operator==(const ConfigNodeId& other) const; /** * Return the unique node ID. * * @return the unique node ID. */ const UniqueNodeId& unique_node_id() const { return (_unique_node_id); } /** * Return the position of the node. * * @return the position of the node. */ const Position& position() const { return (_position); } /** * Set the instance ID. * * @param v the instance ID. */ void set_instance_id(const InstanceId& v); /** * Set the node position. * * The node position is equal to the unique node ID of the previous node. * * @param v the node position. */ void set_position(const Position& v) { _position = v; } /** * Generate a new unique node ID. * * Note that this instance is modified to the value of the new * unique node ID. * * @return a new unique node ID. */ const ConfigNodeId& generate_unique_node_id(); /** * Convert this node ID from binary form to presentation format. * * @return C++ string with the human-readable ASCII representation * of the node ID. */ string str() const { ostringstream ost; // // XXX: We use ostringstream instead of the c_format() facility // because the c_format() facility doesn't work well for uint64_t // integers: on 32-bit architectures the coversion specifier // has to be %llu, while on 64-bit architectures it should be %lu. // Note that this is c_format() specific problem and it doesn't // exist for the system printf(). // ost << _unique_node_id << " " << _position; return ost.str(); } /** * Test if the node ID is empty. * * @return true if the node ID is empty, otherwise false. */ bool is_empty() const { return (_unique_node_id == 0); } /** * Pre-defined constants. */ static ConfigNodeId ZERO() { return ConfigNodeId(0, 0); } private: UniqueNodeId _unique_node_id; // The unique node ID Position _position; // The position of the node }; /** * @short Class for storing the mapping between a @ref ConfigNodeId node * and the corresponding value. * * Internally the class is implemented as a mapped linked list: * - A linked list of pairs contains * the ConfigNodeId nodes and the corresponding values. * - An STL stores the mapping between unique node IDs and the * corresponding iterators in the above list. * * The advantage of such implementation is that the time to insert and * remove entries to/from the list is similar to the time to perform * those operations on an STL map container. */ template class ConfigNodeIdMap { public: typedef list > ValuesList; typedef typename ValuesList::iterator iterator; typedef typename ValuesList::const_iterator const_iterator; /** * Default constructor */ ConfigNodeIdMap() {} /** * Destructor */ virtual ~ConfigNodeIdMap() {} /** * Get the iterator to the first element. * * @return the iterator to the first element. */ typename ConfigNodeIdMap::iterator begin() { return (_values_list.begin()); } /** * Get the const iterator to the first element. * * @return the const iterator to the first element. */ typename ConfigNodeIdMap::const_iterator begin() const { return _values_list.begin(); } /** * Get the iterator to the last element. * * @return the iterator to the last element. */ typename ConfigNodeIdMap::iterator end() { return (_values_list.end()); } /** * Get the const iterator to the last element. * * @return the const iterator to the last element. */ typename ConfigNodeIdMap::const_iterator end() const { return _values_list.end(); } /** * Find an element for a given node ID. * * @param node_id the node ID to search for. * @return the iterator to the element. */ typename ConfigNodeIdMap::iterator find(const ConfigNodeId& node_id); /** * Find an element for a given node ID. * * @param node_id the node ID to search for. * @return the const iterator to the element. */ typename ConfigNodeIdMap::const_iterator find(const ConfigNodeId& node_id) const; /** * Insert a new element. * * @param node_id the node ID of the element to insert. * @param v the value of the element to insert. * @return true a pair of two values: iterator and a boolean flag. * If the boolean flag is true, the element was inserted successfully, * and the iterator points to the new element. If the boolean flag is * false, then either there is an element with the same node ID, and the * iterator points to that element, or the element could not be inserted * because of invalid node ID and the iterator points to @ref end() * of the container. */ pair insert(const ConfigNodeId& node_id, const V& v) { return (insert_impl(node_id, v, false)); } /** * Insert a new element that might be out-of-order. * * @param node_id the node ID of the element to insert. * @param v the value of the element to insert. * @return true a pair of two values: iterator and a boolean flag. * If the boolean flag is true, the element was inserted successfully, * and the iterator points to the new element. If the boolean flag is * false, then there is an element with the same node ID, and the * iterator points to that element. */ pair insert_out_of_order( const ConfigNodeId& node_id, const V& v) { return (insert_impl(node_id, v, true)); } /** * Remove an existing element. * * @param node_id the node ID of the element to remove. * @return the number of removed elements. */ size_t erase(const ConfigNodeId& node_id); /** * Remove an existing element. * * @param iter the iterator to the element to remove. */ void erase(ConfigNodeIdMap::iterator iter); /** * Remove all elements. */ void clear(); /** * Convert this object from binary form to presentation format. * * @return C++ string with the human-readable ASCII representation * of the object. */ string str() const; /** * Get the number of elements in the storage. * * @return the number of elements in the storage. */ size_t size() const { return (_node_id2iter.size()); } /** * Test if the container is empty. * * @return true if the container is empty, otherwise false. */ bool empty() const { return (size() == 0); } private: /** * Insert a new element. * * @param node_id the node ID of the element to insert. * @param v the value of the element to insert. * @param ignore_missing_previous_element if true, and the * previous element is not found, then insert the element at the * end of the container. If this flag is false and the previous element * is not found, then don't insert the element, but return an error. * @return true a pair of two values: iterator and a boolean flag. * If the boolean flag is true, the element was inserted successfully, * and the iterator points to the new element. If the boolean flag is * false, then either there is an element with the same node ID, and the * iterator points to that element, or the element could not be inserted * because of invalid node ID and the iterator points to @ref end() * of the container. */ pair insert_impl(const ConfigNodeId& node_id, const V& v, bool ignore_missing_previous_element); typedef map NodeId2IterMap; NodeId2IterMap _node_id2iter; // The node ID to iterator map ValuesList _values_list; // The list with the values }; inline size_t ConfigNodeId::copy_in(const string& from_string) throw (InvalidString) { string::size_type space, ix; string s = from_string; if (s.empty()) { _unique_node_id = 0; _position = 0; return (from_string.size()); } space = s.find(' '); if ((space == string::npos) || (space == 0) || (space >= s.size() - 1)) { xorp_throw(InvalidString, c_format("Bad ConfigNodeId \"%s\"", s.c_str())); } // // Check that everything from the beginning to "space" is digits, // and that everything after "space" to the end is also digits. // for (ix = 0; ix < space; ix++) { if (! xorp_isdigit(s[ix])) { xorp_throw(InvalidString, c_format("Bad ConfigNodeId \"%s\"", s.c_str())); } } for (ix = space + 1; ix < s.size(); ix++) { if (! xorp_isdigit(s[ix])) { xorp_throw(InvalidString, c_format("Bad ConfigNodeId \"%s\"", s.c_str())); } } // // Extract the unique node ID and the position. // string tmp_str = s.substr(0, space); _unique_node_id = strtoll(tmp_str.c_str(), (char **)NULL, 10); tmp_str = s.substr(space + 1); _position = strtoll(tmp_str.c_str(), (char **)NULL, 10); return (from_string.size()); } inline bool ConfigNodeId::operator==(const ConfigNodeId& other) const { return ((_unique_node_id == other._unique_node_id) && (_position == other._position)); } inline void ConfigNodeId::set_instance_id(const InstanceId& v) { // Set the upper 32 bits to the instance ID uint64_t h = (0xffffffffU & v); h = h << 32; _unique_node_id &= 0xffffffffU; _unique_node_id |= h; } inline const ConfigNodeId& ConfigNodeId::generate_unique_node_id() { // Check that the lower 32 bits of the unique node ID won't overflow XLOG_ASSERT((0xffffffffU & _unique_node_id) + 1 != 0); _unique_node_id++; return (*this); } template inline typename ConfigNodeIdMap::iterator ConfigNodeIdMap::find(const ConfigNodeId& node_id) { typename NodeId2IterMap::iterator node_id_iter; node_id_iter = _node_id2iter.find(node_id.unique_node_id()); if (node_id_iter == _node_id2iter.end()) return (_values_list.end()); return (node_id_iter->second); } template inline typename ConfigNodeIdMap::const_iterator ConfigNodeIdMap::find(const ConfigNodeId& node_id) const { typename NodeId2IterMap::const_iterator node_id_iter; node_id_iter = _node_id2iter.find(node_id.unique_node_id()); if (node_id_iter == _node_id2iter.end()) return (_values_list.end()); return (node_id_iter->second); } template inline pair::iterator, bool> ConfigNodeIdMap::insert_impl(const ConfigNodeId& node_id, const V& v, bool ignore_missing_previous_element) { typename NodeId2IterMap::iterator node_id_iter; typename ValuesList::iterator values_iter; node_id_iter = _node_id2iter.find(node_id.unique_node_id()); if (node_id_iter != _node_id2iter.end()) { values_iter = node_id_iter->second; XLOG_ASSERT(values_iter != _values_list.end()); return (make_pair(values_iter, false)); // Node already exists } // Find the iterator to the previous element values_iter = _values_list.begin(); do { if (node_id.position() == 0) { // The first element values_iter = _values_list.begin(); break; } if (_values_list.size() == 0) { if (! ignore_missing_previous_element) { // Error: no other elements found return (make_pair(_values_list.end(), false)); } values_iter = _values_list.end(); break; } // Find the iterator to the previous element node_id_iter = _node_id2iter.find(node_id.position()); if (node_id_iter == _node_id2iter.end()) { if (! ignore_missing_previous_element) { // Error: the previous element is not found return (make_pair(_values_list.end(), false)); } values_iter = _values_list.end(); break; } values_iter = node_id_iter->second; // XXX: increment the iterator to point to the insert position ++values_iter; break; } while (false); // Insert the new element values_iter = _values_list.insert(values_iter, make_pair(node_id, v)); XLOG_ASSERT(values_iter != _values_list.end()); pair res = _node_id2iter.insert( make_pair(node_id.unique_node_id(), values_iter)); XLOG_ASSERT(res.second == true); return (make_pair(values_iter, true)); } template inline size_t ConfigNodeIdMap::erase(const ConfigNodeId& node_id) { typename NodeId2IterMap::iterator node_id_iter; typename ValuesList::iterator values_iter; node_id_iter = _node_id2iter.find(node_id.unique_node_id()); if (node_id_iter == _node_id2iter.end()) return (0); // No element found to erase values_iter = node_id_iter->second; _node_id2iter.erase(node_id_iter); _values_list.erase(values_iter); return (1); // One element erased } template inline void ConfigNodeIdMap::erase(ConfigNodeIdMap::iterator iter) { if (iter == _values_list.end()) return; const ConfigNodeId& node_id = iter->first; erase(node_id); } template inline void ConfigNodeIdMap::clear() { _node_id2iter.clear(); _values_list.clear(); } template inline string ConfigNodeIdMap::str() const { typename ConfigNodeIdMap::const_iterator iter; string res; for (iter = _values_list.begin(); iter != _values_list.end(); ++iter) { if (iter != _values_list.begin()) res += ", "; res += iter->first.str(); } return (res); } #endif // __LIBPROTO_CONFIG_NODE_ID_HH__ xorp/libproto/proto_unit.hh0000664000076400007640000002004511540224225016233 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __LIBPROTO_PROTO_UNIT_HH__ #define __LIBPROTO_PROTO_UNIT_HH__ #include "libxorp/xorp.h" #include "proto_state.hh" /** * @short The unique module IDs (one per protocol or module). * * Note: the module IDs must be consistent with the _xorp_module_name * definition in file proto_unit.cc (TODO: a temporary solution). */ enum xorp_module_id { XORP_MODULE_MIN = 0, XORP_MODULE_NULL = 0, XORP_MODULE_FEA = 1, XORP_MODULE_MFEA = 2, XORP_MODULE_MLD6IGMP = 3, XORP_MODULE_PIMSM = 4, XORP_MODULE_PIMDM = 5, XORP_MODULE_BGMP = 6, XORP_MODULE_BGP = 7, XORP_MODULE_OSPF = 8, XORP_MODULE_RIP = 9, XORP_MODULE_CLI = 10, XORP_MODULE_RIB = 11, XORP_MODULE_RTRMGR = 12, XORP_MODULE_STATIC_ROUTES = 13, XORP_MODULE_FIB2MRIB = 14, XORP_MODULE_MAX }; /** * @short Base class for each protocol unit (node, vif, etc). */ class ProtoUnit : public ProtoState { public: /** * Constructor for a given address family and module ID. * * @param init_family the address family. * @param init_module_id the module ID (@ref xorp_module_id). */ ProtoUnit(int init_family, xorp_module_id init_module_id); /** * Destructor */ virtual ~ProtoUnit(); /** * Get the address family. * * @return the address family (e.g., AF_INET or AF_INET6 for IPv4 and * IPv6 respectively). */ int family() const { return (_family); } /** * Get the module ID. * * @return the module ID (@ref xorp_module_id). */ xorp_module_id module_id() const { return (_module_id); } /** * Get the current protocol version. * * @return the current protocol version. */ int proto_version() const { return (_proto_version); } /** * Set the current protocol version. * * @param v the protocol version. */ void set_proto_version(int v) { _proto_version = v; } /** * Get the default protocol version. * * @return the default protocol version. */ int proto_version_default() const { return (_proto_version_default); } /** * Set the default protocol version. * * @param v the default protocol version. */ void set_proto_version_default(int v) { _proto_version_default = v;} /** * Test if the address family of the unit is IPv4. * * @return true if the address family of the unit is IPv4. */ bool is_ipv4() const { return (_family == AF_INET); } /** * Test if the address family of the unit is IPv6. * * @return true if the address family of the unit is IPv6. */ bool is_ipv6() const { return (_family == AF_INET6); } /** * Get the module name. * * TODO: temporary, all names are listed in "_xorp_module_name[][]" * in proto_unit.cc. * * @return C-style string with the module name. */ const char *module_name() const { return (_module_name.c_str()); } /** * Get the communication handler for this unit. * * Note: currently, the purpose of the communication handler is undefined. * * @return the communication handler for this unit. */ int comm_handler() const { return (_comm_handler); } /** * Set the communication handler for this unit. * * @param v the communication handler to set for this unit. */ void set_comm_handler(int v) { _comm_handler = v; } // // Protocol tests // /** * Test if the protocol is MLD6 or IGMP. * * @return true if the protocol is MLD6 or IGMP. */ bool proto_is_mld6igmp() const { return (_module_id == XORP_MODULE_MLD6IGMP); } /** * Test if the protocol is IGMP. * * @return true if the protocol is IGMP. */ bool proto_is_igmp() const { return (proto_is_mld6igmp() && is_ipv4()); } /** * Test if the protocol is MLD6. * * @return true if the protocol is MLD6. */ bool proto_is_mld6() const { return (proto_is_mld6igmp() && is_ipv6()); } /** * Test if the protocol is PIM-SM. * * @return true if the protocol is PIM-SM. */ bool proto_is_pimsm() const { return (_module_id == XORP_MODULE_PIMSM); } /** * Test if the protocol is PIM-DM. * * @return true if the protocol is PIM-DM. */ bool proto_is_pimdm() const { return (_module_id == XORP_MODULE_PIMDM); } /** * Test if the protocol is BGMP. * * @return true if the protocol is BGMP. */ bool proto_is_bgmp() const { return (_module_id == XORP_MODULE_BGMP); } /** * Test if the protocol is BGP. * * @return true if the protocol is BGP. */ bool proto_is_bgp() const { return (_module_id == XORP_MODULE_BGP); } /** * Test if the protocol is OSPF. * * @return true if the protocol is OSPF. */ bool proto_is_ospf() const { return (_module_id == XORP_MODULE_OSPF); } /** * Test if the protocol is RIP. * * @return true if the protocol is RIP. */ bool proto_is_rip() const { return (_module_id == XORP_MODULE_RIP); } /** * Test if the protocol is StaticRoutes. * * @return true if the protocol is StaticRoutes */ bool proto_is_static_routes() const { return (_module_id == XORP_MODULE_STATIC_ROUTES); } /** * Test if the protocol is Fib2mrib. * * @return true if the protocol is Fib2mrib */ bool proto_is_fib2mrib() const { return (_module_id == XORP_MODULE_FIB2MRIB); } private: int _family; // The address family. xorp_module_id _module_id; // The module ID (XORP_MODULE_*). int _comm_handler; // The communication handler. int _proto_version; // Protocol version (proto. specific). int _proto_version_default; // Default protocol version. string _module_name; // The module name. }; // // Global variables // // // Global functions prototypes // /** * Get the module name for a given address family and module ID. * * TODO: temporary, all names are listed in "_xorp_module_name[][]" * in proto_unit.cc. * * @param family the address family (e.g., AF_INET or AF_INET6 for * IPv4 and IPv6 respectively). * @param module_id the module ID (@ref xorp_module_id). * @return C-style string with the module name. */ const char *xorp_module_name(int family, xorp_module_id module_id); /** * Convert from module name to module ID. * * The module name must be a valid name returned by @ref xorp_module_name(). * * @param module_name the module name. * @return the module ID (@ref xorp_module_id) if @ref module_name is valid, * otherwise @ref XORP_MODULE_NULL. */ xorp_module_id xorp_module_name2id(const char *module_name); /** * Test if a module ID is valid. * * A valid module ID is defined as valid if it is in the interval * [@ref XORP_MODULE_MIN, @ref XORP_MODULE_MAX). * * @param module_id the module ID to test (@ref xorp_module_id). * @return true if @ref module_id is valid, otherwise false. */ bool is_valid_module_id(xorp_module_id module_id); #endif // __LIBPROTO_PROTO_UNIT_HH__ xorp/libproto/libproto_module.h0000664000076400007640000000244511421137511017063 0ustar greearbgreearb/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- */ /* vim:set sts=4 ts=8: */ /* * Copyright (c) 2001-2009 XORP, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License, Version * 2.1, June 1999 as published by the Free Software Foundation. * Redistribution and/or modification of this program under the terms of * any other version of the GNU Lesser General Public License is not * permitted. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, * see the GNU Lesser General Public License, Version 2.1, a copy of * which can be found in the XORP LICENSE.lgpl file. * * XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; * http://xorp.net */ /* * $XORP: xorp/libproto/libproto_module.h,v 1.11 2008/10/02 21:57:17 bms Exp $ */ /* * Module definitions. */ #ifndef __LIBPROTO_LIBPROTO_MODULE_H__ #define __LIBPROTO_LIBPROTO_MODULE_H__ #ifndef XORP_MODULE_NAME #define XORP_MODULE_NAME "LIBPROTO" #endif #ifndef XORP_MODULE_VERSION #define XORP_MODULE_VERSION "0.1" #endif #endif /* __LIBPROTO_LIBPROTO_MODULE_H__ */ xorp/ospf/0000775000076400007640000000000011703345405012631 5ustar greearbgreearbxorp/ospf/packet.cc0000664000076400007640000007536311540224231014415 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME // #define DEBUG_RAW_PACKETS #include "ospf_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/callback.hh" #include "libxorp/ipv4.hh" #include "libxorp/ipv6.hh" #include "libxorp/ipnet.hh" #include "libxorp/status_codes.h" #include "libxorp/service.hh" #include "libxorp/eventloop.hh" #include "libproto/checksum.h" #include "libproto/packet.hh" #include "ospf.hh" #include "packet.hh" /** * Return the IP checksum in host order. */ inline uint16_t checksum(uint8_t *ptr, size_t len) { return ntohs(inet_checksum(ptr, len)); } /** * Return the IPv6 pseudo header checksum in host order. */ inline uint16_t ipv6_pseudo_header_checksum(const IPv6& src, const IPv6& dst, size_t len, uint8_t protocol) { uint8_t pseudo_header[16 /* Source address */ + 16 /* Destination address */ + 4 /* Upper-layer packet length */ + 3 /* Zero */ + 1 /* Upper-layer protocol number */ ]; src.copy_out(&pseudo_header[0]); dst.copy_out(&pseudo_header[16]); embed_32(&pseudo_header[16 + 16], len); embed_24(&pseudo_header[16 + 16 + 4], 0); pseudo_header[16 + 16 + 4 + 3] = protocol; return checksum(&pseudo_header[0], sizeof(pseudo_header)); } template <> void ipv6_checksum_verify(const IPv6& src, const IPv6& dst, const uint8_t *data, size_t len, size_t checksum_offset, uint8_t protocol) throw(InvalidPacket) { debug_msg("src %s dst data %p %s len %u chsum offset %u protocol %u\n", cstring(src), cstring(dst), data, XORP_UINT_CAST(len), XORP_UINT_CAST(checksum_offset), protocol); if (len < checksum_offset) xorp_throw(InvalidPacket, c_format("Checksum offset %u greater than packet length %u", XORP_UINT_CAST(checksum_offset), XORP_UINT_CAST(len))); if (0 == inet_checksum_add(ipv6_pseudo_header_checksum(src, dst, len, protocol), checksum(const_cast(data), len))) return; // If we get here there is a problem with the checksum. Compute // the expected checksum to aid in debugging. uint8_t *temp = new uint8_t[len]; memcpy(&temp[0], &data[0], len); uint16_t checksum_inpacket = extract_16(&temp[checksum_offset]); embed_16(&temp[checksum_offset], 0); uint16_t checksum_computed = inet_checksum_add(ipv6_pseudo_header_checksum(src, dst, len, protocol), checksum(temp, len)); delete []temp; ; if (checksum_inpacket != checksum_computed) xorp_throw(InvalidPacket, c_format("Checksum mismatch expected %#x received %#x", checksum_computed, checksum_inpacket)); } template <> void ipv6_checksum_verify(const IPv4& src, const IPv4& dst, const uint8_t *data, size_t len, size_t checksum_offset, uint8_t protocol) throw(InvalidPacket) { debug_msg("src %s dst data %p %s len %u chsum offset %u protocol %u\n", cstring(src), cstring(dst), data, XORP_UINT_CAST(len), XORP_UINT_CAST(checksum_offset), protocol); } template <> void ipv6_checksum_apply(const IPv6& src, const IPv6& dst, uint8_t *data, size_t len, size_t checksum_offset, uint8_t protocol) throw(InvalidPacket) { debug_msg("src %s dst data %p %s len %u chsum offset %u protocol %u\n", cstring(src), cstring(dst), data, XORP_UINT_CAST(len), XORP_UINT_CAST(checksum_offset), protocol); if (len < checksum_offset) xorp_throw(InvalidPacket, c_format("Checksum offset %u greater than packet length %u", XORP_UINT_CAST(checksum_offset), XORP_UINT_CAST(len))); embed_16(&data[checksum_offset], inet_checksum_add(ipv6_pseudo_header_checksum(src, dst, len, protocol), checksum(data, len))); } template <> void ipv6_checksum_apply(const IPv4& src, const IPv4& dst, uint8_t *data, size_t len, size_t checksum_offset, uint8_t protocol) throw(InvalidPacket) { debug_msg("src %s dst data %p %s len %u chsum offset %u protocol %u\n", cstring(src), cstring(dst), data, XORP_UINT_CAST(len), XORP_UINT_CAST(checksum_offset), protocol); } #ifdef DEBUG_RAW_PACKETS inline string dump_packet(uint8_t *ptr, size_t len) { string output; for(size_t i = 0; i < len; i++) { output += c_format("%#4x ", ptr[i]); if (!((i + 1) % 4)) output += "\n"; } return output; } #endif /* Packet */ size_t Packet::decode_standard_header(uint8_t *ptr, size_t& len) throw(InvalidPacket) { debug_msg("ptr %p len %u\n", ptr, XORP_UINT_CAST(len)); #ifdef DEBUG_RAW_PACKETS debug_msg("\n%s", dump_packet(ptr, len).c_str()); #endif // Store a copy of the raw packet data for possible later use for // authentication. store(ptr, len); // Make sure that at least two bytes have been extracted: // Version and Type fields. if (len < 2) xorp_throw(InvalidPacket, c_format("Packet too short %u, must be at least %u", XORP_UINT_CAST(len), XORP_UINT_CAST(2))); OspfTypes::Version version; switch(ptr[0]) { case 2: version = OspfTypes::V2; break; case 3: version = OspfTypes::V3; break; default: xorp_throw(InvalidPacket, c_format("Version mismatch expected %u received %u", get_version(), ptr[Packet::VERSION_OFFSET] & 0xff)); break; } if (ptr[1] != get_type()) xorp_throw(InvalidPacket, c_format("Type mismatch expected %u received %u", get_type(), ptr[Packet::TYPE_OFFSET])); // Make sure that at least the standard header length is present. switch(version) { case OspfTypes::V2: if (len < STANDARD_HEADER_V2) xorp_throw(InvalidPacket, c_format("Packet too short %u, must be at least %u", XORP_UINT_CAST(len), XORP_UINT_CAST(STANDARD_HEADER_V2))); case OspfTypes::V3: if (len < STANDARD_HEADER_V3) xorp_throw(InvalidPacket, c_format("Packet too short %u, must be at least %u", XORP_UINT_CAST(len), XORP_UINT_CAST(STANDARD_HEADER_V3))); } // Verify that the length in the packet and the length of received // data match. uint32_t packet_length = extract_16(&ptr[Packet::LEN_OFFSET]); if (packet_length != len) { // If the frame is too small complain. if (len < packet_length) xorp_throw(InvalidPacket, c_format("Packet length expected %u received %u", packet_length, XORP_UINT_CAST(len))); // "Be liberal in what you accept, and conservative in what you send." // -- Jon Postel len = packet_length; // Drop the length and continue. } set_router_id(extract_32(&ptr[Packet::ROUTER_ID_OFFSET])); set_area_id(extract_32(&ptr[Packet::AREA_ID_OFFSET])); // In OSPFv2 there is authentication info in the standard header. switch(version) { case OspfTypes::V2: // Verify the auth structure is the correct size. x_static_assert(sizeof(_auth) == (64 / 8)); set_auth_type(extract_16(&ptr[Packet::AUTH_TYPE_OFFSET])); memcpy(&_auth[0], &ptr[Packet::AUTH_PAYLOAD_OFFSET], sizeof(_auth)); // The authentication field is expected to be zero for the // checksumming. memset(&ptr[Packet::AUTH_PAYLOAD_OFFSET], 0, sizeof(_auth)); break; case OspfTypes::V3: set_instance_id(ptr[Packet::INSTANCE_ID_OFFSET]); // For OSPFv3 the checksum has already been verified. return get_standard_header_length(); break; } // Extract the checksum and check the packet. uint16_t checksum_inpacket = extract_16(&ptr[Packet::CHECKSUM_OFFSET]); // Zero the checksum location. embed_16(&ptr[Packet::CHECKSUM_OFFSET], 0); uint16_t checksum_actual = checksum(ptr, len); // Restore the zero'd fields. switch(version) { case OspfTypes::V2: memcpy(&ptr[Packet::AUTH_PAYLOAD_OFFSET], &_auth[0], sizeof(_auth)); break; case OspfTypes::V3: break; } embed_16(&ptr[Packet::CHECKSUM_OFFSET], checksum_inpacket); if (0 == checksum_inpacket && OspfTypes::CRYPTOGRAPHIC_AUTHENTICATION == get_auth_type()) return get_standard_header_length(); if (checksum_inpacket != checksum_actual) xorp_throw(InvalidPacket, c_format("Checksum mismatch expected %#x received %#x", checksum_actual, checksum_inpacket)); // Return the offset at which continued processing can take place. return get_standard_header_length(); } size_t Packet::encode_standard_header(uint8_t *ptr, size_t len) { debug_msg("ptr %p len %u\n", ptr, XORP_UINT_CAST(len)); if (len < get_standard_header_length()) { XLOG_ERROR("Request to put a header of size %u in space %u", XORP_UINT_CAST(get_standard_header_length()), XORP_UINT_CAST(len)); return 0; } // Zero the space memset(ptr, 0, get_standard_header_length()); OspfTypes::Version version = get_version(); ptr[Packet::VERSION_OFFSET] = version; ptr[Packet::TYPE_OFFSET] = get_type(); embed_16(&ptr[Packet::LEN_OFFSET], len); embed_32(&ptr[Packet::ROUTER_ID_OFFSET], get_router_id()); embed_32(&ptr[Packet::AREA_ID_OFFSET], get_area_id()); switch(version) { case OspfTypes::V2: embed_16(&ptr[Packet::AUTH_TYPE_OFFSET], get_auth_type()); break; case OspfTypes::V3: ptr[Packet::INSTANCE_ID_OFFSET] = get_instance_id(); // For OSPFv3 the checksum will be written later. return get_standard_header_length(); break; } embed_16(&ptr[Packet::CHECKSUM_OFFSET], checksum(ptr, len)); // OSPFv2 only copy the authentication data out. switch(version) { case OspfTypes::V2: memcpy(&ptr[Packet::AUTH_PAYLOAD_OFFSET], &_auth[0], sizeof(_auth)); break; case OspfTypes::V3: break; } #ifdef DEBUG_RAW_PACKETS debug_msg("\n%s", dump_packet(ptr, len).c_str()); #endif return get_standard_header_length(); } string Packet::standard() const { string output; output = c_format("\tVersion %u\n", get_version()); output += c_format("\tType %u\n", get_type()); output += "\tRouter ID " + pr_id(get_router_id()) + "\n"; output += "\tArea ID " + pr_id(get_area_id()) + "\n"; switch(get_version()) { case OspfTypes::V2: output += c_format("\tAuth Type %u", get_auth_type()); break; case OspfTypes::V3: output += c_format("\tInstance ID %u", get_instance_id()); break; } return output; } /* PacketDecoder */ PacketDecoder::~PacketDecoder() { // Free all the stored decoder packets. map::iterator i; for(i = _ospfv2.begin(); i != _ospfv2.end(); i++) delete i->second; for(i = _ospfv3.begin(); i != _ospfv3.end(); i++) delete i->second; } #if 0 void PacketDecoder::register_decoder(Packet *packet, OspfTypes::Version version, OspfTypes::Type type) { switch(version) { case OspfTypes::V2: _ospfv2[type] = packet; break; case OspfTypes::V3: _ospfv3[type] = packet; break; } } #endif void PacketDecoder::register_decoder(Packet *packet) { switch(packet->get_version()) { case OspfTypes::V2: // Don't allow a registration to be overwritten. XLOG_ASSERT(0 == _ospfv2.count(packet->get_type())); _ospfv2[packet->get_type()] = packet; break; case OspfTypes::V3: // Don't allow a registration to be overwritten. XLOG_ASSERT(0 == _ospfv3.count(packet->get_type())); _ospfv3[packet->get_type()] = packet; break; } } Packet * PacketDecoder::decode(uint8_t *ptr, size_t len) throw(InvalidPacket) { // Make sure that at least two bytes have been extracted: // Version and Type fields. if (len < 2) xorp_throw(InvalidPacket, c_format("Packet too short %u, must be at least %u", XORP_UINT_CAST(len), XORP_UINT_CAST(Packet::TYPE_OFFSET))); OspfTypes::Version version; switch(ptr[Packet::VERSION_OFFSET]) { case 2: version = OspfTypes::V2; break; case 3: version = OspfTypes::V3; break; default: xorp_throw(InvalidPacket, c_format("Unknown OSPF Version %u", ptr[Packet::VERSION_OFFSET] & 0xff)); break; } map::iterator i; uint8_t type = ptr[Packet::TYPE_OFFSET]; Packet *packet = NULL; switch(version) { case OspfTypes::V2: i = _ospfv2.find(type); if (i != _ospfv2.end()) packet = i->second; break; case OspfTypes::V3: i = _ospfv3.find(type); if (i != _ospfv3.end()) packet = i->second; break; } if (packet == NULL) xorp_throw(InvalidPacket, c_format("OSPF Version %u Unknown Type %u", version, type)); return packet->decode(ptr, len); } /* HelloPacket */ Packet * HelloPacket::decode(uint8_t *ptr, size_t len) const throw(InvalidPacket) { OspfTypes::Version version = get_version(); HelloPacket *packet = new HelloPacket(version); size_t offset = packet->decode_standard_header(ptr, len); // Verify that this packet is large enough, up to but not including // any neighbours. if ((len - offset) < MINIMUM_LENGTH) xorp_throw(InvalidPacket, c_format("Packet too short %u, must be at least %u", XORP_UINT_CAST(len), XORP_UINT_CAST(offset + MINIMUM_LENGTH))); #ifdef DEBUG_RAW_PACKETS debug_msg("\n%s", dump_packet(ptr, len).c_str()); #endif switch(version) { case OspfTypes::V2: packet->set_network_mask(extract_32(&ptr[offset + HelloPacket::NETWORK_MASK_OFFSET])); packet->set_hello_interval(extract_16(&ptr[offset + HelloPacket::HELLO_INTERVAL_V2_OFFSET])); packet->set_options(ptr[offset + HelloPacket::OPTIONS_V2_OFFSET]); packet->set_router_priority(ptr[offset + HelloPacket::ROUTER_PRIORITY_V2_OFFSET]); packet->set_router_dead_interval(extract_32(&ptr[offset + HelloPacket::ROUTER_DEAD_INTERVAL_V2_OFFSET])); break; case OspfTypes::V3: packet->set_interface_id(extract_32(&ptr[offset + HelloPacket::INTERFACE_ID_OFFSET])); packet->set_router_priority(ptr[offset + HelloPacket::ROUTER_PRIORITY_V3_OFFSET]); packet->set_options(extract_32(&ptr[offset + HelloPacket::OPTIONS_V3_OFFSET]) & 0xffffff); packet->set_hello_interval(extract_16(&ptr[offset + HelloPacket::HELLO_INTERVAL_V3_OFFSET])); packet->set_router_dead_interval(extract_16(&ptr[offset + HelloPacket::ROUTER_DEAD_INTERVAL_V3_OFFSET])); break; } packet->set_designated_router(extract_32(&ptr[offset + HelloPacket::DESIGNATED_ROUTER_OFFSET])); packet->set_backup_designated_router(extract_32(&ptr[offset + HelloPacket::BACKUP_DESIGNATED_ROUTER_OFFSET])); // If there is any more space in the packet extract the neighbours. int neighbours = (len - (offset + MINIMUM_LENGTH)) / 4; // XXX - Should we be checking for multiples of 4 here? for(int i = 0; i < neighbours; i++) packet->get_neighbours(). push_back(extract_32(&ptr[offset + MINIMUM_LENGTH + i*4])); return packet; } bool HelloPacket::encode(vector& pkt) { size_t offset = get_standard_header_length(); size_t len = offset + MINIMUM_LENGTH + get_neighbours().size() * 4; pkt.resize(len); uint8_t *ptr = &pkt[0]; // uint8_t *ptr = new uint8_t[len]; memset(ptr, 0, len); // Put the specific Hello Packet information first as the standard // header code will also add the checksum. This must be done last. /**************************************/ OspfTypes::Version version = get_version(); switch(version) { case OspfTypes::V2: embed_32(&ptr[offset + HelloPacket::NETWORK_MASK_OFFSET], get_network_mask()); embed_16(&ptr[offset + HelloPacket::HELLO_INTERVAL_V2_OFFSET], get_hello_interval()); ptr[offset + HelloPacket::OPTIONS_V2_OFFSET] = get_options(); ptr[offset + HelloPacket::ROUTER_PRIORITY_V2_OFFSET] = get_router_priority(); embed_32(&ptr[offset + HelloPacket::ROUTER_DEAD_INTERVAL_V2_OFFSET], get_router_dead_interval()); break; case OspfTypes::V3: embed_32(&ptr[offset + HelloPacket::INTERFACE_ID_OFFSET], get_interface_id()); // Careful Options occupy 3 bytes, four bytes are written out // and the top byte is overwritten by the router priority. embed_32(&ptr[offset + HelloPacket::OPTIONS_V3_OFFSET], get_options()); ptr[offset + HelloPacket::ROUTER_PRIORITY_V3_OFFSET] = get_router_priority(); embed_16(&ptr[offset + HelloPacket::HELLO_INTERVAL_V3_OFFSET], get_hello_interval()); embed_16(&ptr[offset + HelloPacket::ROUTER_DEAD_INTERVAL_V3_OFFSET], get_router_dead_interval()); break; } embed_32(&ptr[offset + HelloPacket::DESIGNATED_ROUTER_OFFSET], get_designated_router()); embed_32(&ptr[offset + HelloPacket::BACKUP_DESIGNATED_ROUTER_OFFSET], get_backup_designated_router()); list &li = get_neighbours(); list::iterator i = li.begin(); for(size_t index = 0; i != li.end(); i++, index += 4) { embed_32(&ptr[offset + 20 + index], *i); } if (offset != encode_standard_header(ptr, len)) { XLOG_ERROR("Encode of %s failed", str().c_str()); return false; } return true; } string HelloPacket::str() const { string output; output = "Hello Packet:\n"; // Standard Header output += standard() + "\n"; // Hello Packet Specifics switch(get_version()) { case OspfTypes::V2: output += c_format("\tNetwork Mask %#x\n", get_network_mask()); break; case OspfTypes::V3: output += c_format("\tInterface ID %u\n", get_interface_id()); break; } output += c_format("\tHello Interval %u\n", get_hello_interval()); output += c_format("\tOptions %#x %s\n", get_options(), cstring(Options(get_version(), get_options()))); output += c_format("\tRouter Priority %u\n", get_router_priority()); output += c_format("\tRouter Dead Interval %u\n", get_router_dead_interval()); output += "\tDesignated Router " + pr_id(get_designated_router()) + "\n"; output += "\tBackup Designated Router " + pr_id(get_backup_designated_router()); list li = _neighbours; list::iterator i = li.begin(); for(; i != li.end(); i++) { output += "\n\tNeighbour: " + pr_id(*i); } return output; } /* Database Description packet */ Packet * DataDescriptionPacket::decode(uint8_t *ptr, size_t len) const throw(InvalidPacket) { OspfTypes::Version version = get_version(); DataDescriptionPacket *packet = new DataDescriptionPacket(version); size_t offset = packet->decode_standard_header(ptr, len); // Verify that this packet is large enough, up to but not including // any neighbours. if ((len - offset) < minimum_length()) xorp_throw(InvalidPacket, c_format("Packet too short %u, must be at least %u", XORP_UINT_CAST(len), XORP_UINT_CAST(offset + minimum_length()))); #ifdef DEBUG_RAW_PACKETS debug_msg("\n%s", dump_packet(ptr, len).c_str()); #endif size_t bias = 0; switch(version) { case OspfTypes::V2: packet->set_interface_mtu(extract_16(&ptr[offset])); packet->set_options(ptr[offset + 2]); bias = 0; break; case OspfTypes::V3: packet->set_options(extract_32(&ptr[offset]) & 0xffffff); packet->set_interface_mtu(extract_16(&ptr[offset + 4])); bias = 4; break; } uint8_t flag = ptr[offset + bias + 3]; if (flag & 0x4) packet->set_i_bit(true); else packet->set_i_bit(false); if (flag & 0x2) packet->set_m_bit(true); else packet->set_m_bit(false); if (flag & 0x1) packet->set_ms_bit(true); else packet->set_ms_bit(false); packet->set_dd_seqno(extract_32(&ptr[offset + bias + 4])); size_t lsa_offset = offset + 8 + bias; Lsa_header lsa_header(version); // If there is any more space in the packet extract the lsas. int lsas = (len - lsa_offset) / lsa_header.length(); // XXX - Should we be checking for multiples of 20 here? for(int i = 0; i < lsas; i++) { packet->get_lsa_headers(). push_back(lsa_header.decode(&ptr[lsa_offset + i*lsa_header.length()])); } return packet; } bool DataDescriptionPacket::encode(vector& pkt) { size_t offset = get_standard_header_length(); size_t len = offset + minimum_length() + get_lsa_headers().size() * Lsa_header::length(); pkt.resize(len); uint8_t *ptr = &pkt[0]; // uint8_t *ptr = new uint8_t[len]; memset(ptr, 0, len); // Put the specific Data Description Packet information first as // the standard header code will also add the checksum. This must // be done last. /**************************************/ OspfTypes::Version version = get_version(); size_t bias = 0; switch(version) { case OspfTypes::V2: embed_16(&ptr[offset], get_interface_mtu()); ptr[offset + 2] = get_options(); bias = 0; break; case OspfTypes::V3: // Careful Options occupy 3 bytes, four bytes are written out. embed_32(&ptr[offset], get_options()); embed_16(&ptr[offset + 4], get_interface_mtu()); bias = 4; break; } uint8_t flag = 0; if (get_i_bit()) flag |= 0x4; if (get_m_bit()) flag |= 0x2; if (get_ms_bit()) flag |= 0x1; ptr[offset + bias + 3] = flag; embed_32(&ptr[offset + bias + 4], get_dd_seqno()); size_t lsa_offset = offset + 8 + bias; list &li = get_lsa_headers(); list::iterator i = li.begin(); for(size_t index = 0; i != li.end(); i++, index += Lsa_header::length()) { (*i).copy_out(&ptr[lsa_offset + index]); } if (offset != encode_standard_header(ptr, len)) { XLOG_ERROR("Encode of %s failed", str().c_str()); return false; } return true; } string DataDescriptionPacket::str() const { string output; output = "Data Description Packet:\n"; // Standard Header output += standard() + "\n"; // Data Description Packet Specifics output += c_format("\tInterface MTU %u\n", get_interface_mtu()); output += c_format("\tOptions %#x %s\n", get_options(), cstring(Options(get_version(), get_options()))); output += c_format("\tI-bit %s\n", bool_c_str(get_i_bit())); output += c_format("\tM-bit %s\n", bool_c_str(get_m_bit())); output += c_format("\tMS-bit %s\n", bool_c_str(get_ms_bit())); output += c_format("\tDD sequence number %u", get_dd_seqno()); list li = _lsa_headers; list::iterator i = li.begin(); for (; i != li.end(); i++) { output += "\n\t" + (*i).str(); } return output; } /* Link State Request Packet */ Packet * LinkStateRequestPacket::decode(uint8_t *ptr, size_t len) const throw(InvalidPacket) { OspfTypes::Version version = get_version(); LinkStateRequestPacket *packet = new LinkStateRequestPacket(version); size_t offset = packet->decode_standard_header(ptr, len); Ls_request ls(version); // Verify that this packet is large enough, a standard header plus // at least one request if ((len - offset) < ls.length()) xorp_throw(InvalidPacket, c_format("Packet too short %u, must be at least %u", XORP_UINT_CAST(len), XORP_UINT_CAST(offset + ls.length()))); #ifdef DEBUG_RAW_PACKETS debug_msg("\n%s", dump_packet(ptr, len).c_str()); #endif // How many request are there? int requests = (len - offset) / ls.length(); // XXX - Should we be checking for multiples of 12 here? for(int i = 0; i < requests; i++) { packet->get_ls_request(). push_back(ls.decode(&ptr[offset + i * ls.length()])); } return packet; } bool LinkStateRequestPacket::encode(vector& pkt) { OspfTypes::Version version = get_version(); Ls_request ls(version); size_t offset = get_standard_header_length(); size_t len = offset + get_ls_request().size() * ls.length(); pkt.resize(len); uint8_t *ptr = &pkt[0]; // uint8_t *ptr = new uint8_t[len]; memset(ptr, 0, len); // Put the specific Link State Request Packet information first as // the standard header code will also add the checksum. This must // be done last. /**************************************/ list &li = get_ls_request(); list::iterator i = li.begin(); for(size_t index = 0; i != li.end(); i++, index += ls.length()) { (*i).copy_out(&ptr[offset + index]); } if (offset != encode_standard_header(ptr, len)) { XLOG_ERROR("Encode of %s failed", str().c_str()); return false; } return true; } string LinkStateRequestPacket::str() const { string output; output = "Link State Request Packet:\n"; // Standard Header output += standard(); // Link State Request Packet Specifics list li = _ls_request; list::iterator i = li.begin(); for (; i != li.end(); i++) { output += "\n\t" + (*i).str(); } return output; } /* Link State Update Packet */ Packet * LinkStateUpdatePacket::decode(uint8_t *ptr, size_t len) const throw(InvalidPacket) { OspfTypes::Version version = get_version(); LinkStateUpdatePacket *packet = new LinkStateUpdatePacket(version, _lsa_decoder); size_t offset = packet->decode_standard_header(ptr, len); // Verify that this packet is large enough to hold the smallest // LSA that we are aware of. size_t min_length = _lsa_decoder.min_length(); if ((len - offset) < min_length) xorp_throw(InvalidPacket, c_format("Packet too short %u, must be at least %u", XORP_UINT_CAST(len), XORP_UINT_CAST(offset + min_length))); #ifdef DEBUG_RAW_PACKETS debug_msg("\n%s", dump_packet(ptr, len).c_str()); #endif // How many LSAs are there? size_t n_lsas = extract_32(&ptr[offset]); // Step over # LSAs offset += 4; size_t lsa_length; // If anything goes wrong the decoder will throw an exception. for(size_t i = 0; i < n_lsas; i++) { lsa_length = len - offset; packet->get_lsas(). push_back(_lsa_decoder.decode(&ptr[offset], lsa_length)); offset += lsa_length; } return packet; } bool LinkStateUpdatePacket::encode(vector& pkt) { return encode(pkt, 0 /* inftransdelay */); } bool LinkStateUpdatePacket::encode(vector& pkt, uint16_t inftransdelay) { size_t header_offset = get_standard_header_length(); size_t offset = header_offset; // Make a pass over all the LSAs to compute the total length of // the packet. size_t n_lsas = 0; size_t len = offset + 4; // 4 == # LSAs list &lsas = get_lsas(); list::iterator i = lsas.begin(); for(; i != lsas.end(); i++, n_lsas++) { // Don't encode the LSA it should already be encoded. // If this is a self originating LSA then we will have encoded // the LSA. If we received it from a neighbour then we are not // supposed to mess with it apart from updating the age field. // (*i)->encode(); size_t lsa_len; (*i)->lsa(lsa_len); len += lsa_len; } pkt.resize(len); uint8_t *ptr = &pkt[0]; // uint8_t *ptr = new uint8_t[len]; memset(ptr, 0, len); // Put the specific Link State Update Packet information first as // the standard header code will also add the checksum. This must // be done last. /**************************************/ embed_32(&ptr[offset], n_lsas); offset += 4; for(i = lsas.begin() ; i != lsas.end(); i++) { size_t lsa_len; uint8_t *lsa_ptr; lsa_ptr = (*i)->lsa(lsa_len); memcpy(&ptr[offset], lsa_ptr, lsa_len); Lsa::update_age_inftransdelay(&ptr[offset], inftransdelay); offset += lsa_len; } if (header_offset != encode_standard_header(ptr, len)) { XLOG_ERROR("Encode of %s failed", str().c_str()); return false; } return true; } string LinkStateUpdatePacket::str() const { string output; output = "Link State Update Packet:\n"; // Standard Header output += standard() + "\n"; // Link State Packet Specifics list li = _lsas; list::iterator i = li.begin(); for (; i != li.end(); i++) { output += "\n\t" + (*i)->str(); } return output; } /* Link State Acknowledgement Packet */ Packet * LinkStateAcknowledgementPacket::decode(uint8_t *ptr, size_t len) const throw(InvalidPacket) { OspfTypes::Version version = get_version(); LinkStateAcknowledgementPacket *packet = new LinkStateAcknowledgementPacket(version); size_t offset = packet->decode_standard_header(ptr, len); // Verify that this packet is large enough to hold the at least // one LSA header. if ((len - offset) < Lsa_header::length()) xorp_throw(InvalidPacket, c_format("Packet too short %u, must be at least %u", XORP_UINT_CAST(len), XORP_UINT_CAST(offset + Lsa_header::length()))); #ifdef DEBUG_RAW_PACKETS debug_msg("\n%s", dump_packet(ptr, len).c_str()); #endif Lsa_header lsa_header(version); // How many LSA header are there, there should be at least 1. int lsas = (len - offset) / lsa_header.length(); // XXX - Should we be checking for multiples of 20 here? for(int i = 0; i < lsas; i++) { packet->get_lsa_headers(). push_back(lsa_header.decode(&ptr[offset + i*lsa_header.length()])); } return packet; } bool LinkStateAcknowledgementPacket::encode(vector& pkt) { size_t offset = get_standard_header_length(); size_t len = offset + get_lsa_headers().size() * Lsa_header::length(); pkt.resize(len); uint8_t *ptr = &pkt[0]; // uint8_t *ptr = new uint8_t[len]; memset(ptr, 0, len); // Put the specific Link State Acknowledgement Packet information first as // the standard header code will also add the checksum. This must // be done last. /**************************************/ size_t lsa_offset = offset; list &li = get_lsa_headers(); list::iterator i = li.begin(); for(size_t index = 0; i != li.end(); i++, index += Lsa_header::length()) { (*i).copy_out(&ptr[lsa_offset + index]); } if (offset != encode_standard_header(ptr, len)) { XLOG_ERROR("Encode of %s failed", str().c_str()); return false; } return true; } string LinkStateAcknowledgementPacket::str() const { string output; output = "Link State Acknowledgement Packet:\n"; // Standard Header output += standard() + "\n"; // Link State Acknowledgement Packet Specifics list li = _lsa_headers; list::iterator i = li.begin(); for (; i != li.end(); i++) { output += "\n\t" + (*i).str(); } return output; } xorp/ospf/xrl_target3.hh0000664000076400007640000003777411540225531015426 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/ospf/xrl_target.hh,v 1.46 2008/10/02 21:57:50 bms Exp $ #ifndef __OSPF_XRL_TARGET3_HH__ #define __OSPF_XRL_TARGET3_HH__ #ifdef HAVE_IPV6 #include "xrl/targets/ospfv3_base.hh" #include "ospf.hh" class XrlOspfV3Target : XrlOspfv3TargetBase { public: XrlOspfV3Target(XrlRouter *r, /* Ospf& ospf_ipv4, */ Ospf& ospf_ipv6, /* XrlIO& io_ipv4, */ XrlIO& io_ipv6); /** * Get name of Xrl Target */ XrlCmdError common_0_1_get_target_name( // Output values, string& name); /** * Get version string from Xrl Target */ XrlCmdError common_0_1_get_version( // Output values, string& version); /** * Get status of Xrl Target */ XrlCmdError common_0_1_get_status( // Output values, uint32_t& status, string& reason); /** * Request clean shutdown of Xrl Target */ XrlCmdError common_0_1_shutdown(); XrlCmdError common_0_1_startup(); /** * Receive an IPv4 packet from a raw socket. * * @param if_name the interface name the packet arrived on. * * @param vif_name the vif name the packet arrived on. * * @param src_address the IP source address. * * @param dst_address the IP destination address. * * @param ip_protocol the IP protocol number. * * @param ip_ttl the IP TTL (hop-limit). If it has a negative value, then * the received value is unknown. * * @param ip_tos the Type of Service (Diffserv/ECN bits for IPv4). If it * has a negative value, then the received value is unknown. * * @param ip_router_alert if true, the IP Router Alert option was included * in the IP packet. * * @param ip_internet_control if true, then this is IP control traffic. */ XrlCmdError raw_packet4_client_0_1_recv( // Input values, const string& if_name, const string& vif_name, const IPv4& src_address, const IPv4& dst_address, const uint32_t& ip_protocol, const int32_t& ip_ttl, const int32_t& ip_tos, const bool& ip_router_alert, const bool& ip_internet_control, const vector& payload); /** * Receive an IPv6 packet from a raw socket. * * @param if_name the interface name the packet arrived on. * * @param vif_name the vif name the packet arrived on. * * @param src_address the IP source address. * * @param dst_address the IP destination address. * * @param ip_protocol the IP protocol number. * * @param ip_ttl the IP TTL (hop-limit). If it has a negative value, then * the received value is unknown. * * @param ip_tos the Type Of Service (IP traffic class for IPv4). If it * has a negative value, then the received value is unknown. * * @param ip_router_alert if true, the IP Router Alert option was included * in the IP packet. * * @param ip_internet_control if true, then this is IP control traffic. * * @param ext_headers_type a list of u32 integers with the types of the * optional extention headers. * * @param ext_headers_payload a list of payload data, one for each * optional extention header. The number of entries must match * ext_headers_type. */ XrlCmdError raw_packet6_client_0_1_recv( // Input values, const string& if_name, const string& vif_name, const IPv6& src_address, const IPv6& dst_address, const uint32_t& ip_protocol, const int32_t& ip_ttl, const int32_t& ip_tos, const bool& ip_router_alert, const bool& ip_internet_control, const XrlAtomList& ext_headers_type, const XrlAtomList& ext_headers_payload, const vector& payload); /** * Pure-virtual function that needs to be implemented to: * * Configure a policy filter. * * @param filter the identifier of the filter to configure. * * @param conf the configuration of the filter. */ XrlCmdError policy_backend_0_1_configure( // Input values, const uint32_t& filter, const string& conf); /** * @param filter the identifier of the filter to reset. */ XrlCmdError policy_backend_0_1_reset( // Input values, const uint32_t& filter); /** * Push all available routes through all filters for re-filtering. */ XrlCmdError policy_backend_0_1_push_routes(); /** * Start route redistribution for an IPv6 route. * * @param network the route to advertise. * * @param unicast whether the route is unicast. * * @param multicast whether the route is multicast. * * @param nexthop the nexthop of the route. * * @param metric the metric of the route. * * @param policytags the set of policy-tags associated with the route. */ XrlCmdError policy_redist6_0_1_add_route6( // Input values, const IPv6Net& network, const bool& unicast, const bool& multicast, const IPv6& nexthop, const uint32_t& metric, const XrlAtomList& policytags); /** * Terminate route redistribution for an IPv6 route. * * @param network the route for which advertisements should cease. * * @param unicast whether the route is unicast. * * @param multicast whether the route is multicast. */ XrlCmdError policy_redist6_0_1_delete_route6( // Input values, const IPv6Net& network, const bool& unicast, const bool& multicast); /** * Set instance id */ XrlCmdError ospfv3_0_1_set_instance_id( // Input values, const uint32_t& id); /** * Set router id */ XrlCmdError ospfv3_0_1_set_router_id( // Input values, const IPv4& id); /** * Set the router alert in the IP options. */ XrlCmdError ospfv3_0_1_set_ip_router_alert( // Input values, const bool& ip_router_alert); /** * @param type of area "normal", "stub", "nssa" */ XrlCmdError ospfv3_0_1_create_area_router( // Input values, const IPv4& area, const string& type); /** * Change area type. * * @param area id of the area * * @param type of area "border", "stub", "nssa" */ XrlCmdError ospfv3_0_1_change_area_router_type( // Input values, const IPv4& area, const string& type); /** * Destroy area. */ XrlCmdError ospfv3_0_1_destroy_area_router( // Input values, const IPv4& area); /** * Create a binding to an interface. * * @param ifname the interface. * * @param vifname virtual interface. * * @param type of link "p2p", "broadcast", "nbma", "p2m", "vlink" */ XrlCmdError ospfv3_0_1_create_peer( // Input values, const string& ifname, const string& vifname, const string& type, const IPv4& area); /** * Delete peer. */ XrlCmdError ospfv3_0_1_delete_peer( // Input values, const string& ifname, const string& vifname); /** * Set the peer state up or down. */ XrlCmdError ospfv3_0_1_set_peer_state( // Input values, const string& ifname, const string& vifname, const bool& enable); /** * Add an address to the peer. */ XrlCmdError ospfv3_0_1_add_address_peer( // Input values, const string& ifname, const string& vifname, const IPv4& area, const IPv6& addr); /** * Remove an address from the peer. */ XrlCmdError ospfv3_0_1_remove_address_peer( // Input values, const string& ifname, const string& vifname, const IPv4& area, const IPv6& addr); /** * Set the address state up or down. */ XrlCmdError ospfv3_0_1_set_address_state_peer( // Input values, const string& ifname, const string& vifname, const IPv4& area, const IPv6& addr, const bool& enable); /** * Activate peer. Called once the peer and child nodes have been * configured. */ XrlCmdError ospfv3_0_1_activate_peer( // Input values, const string& ifname, const string& vifname, const IPv4& area); /** * Update peer. Called if the peer and child nodes are modified. */ XrlCmdError ospfv3_0_1_update_peer( // Input values, const string& ifname, const string& vifname, const IPv4& area); /** * Add a neighbour to the peer. */ XrlCmdError ospfv3_0_1_add_neighbour( // Input values, const string& ifname, const string& vifname, const IPv4& area, const IPv6& neighbour_address, const IPv4& neighbour_id); /** * Remove a neighbour from the peer. */ XrlCmdError ospfv3_0_1_remove_neighbour( // Input values, const string& ifname, const string& vifname, const IPv4& area, const IPv6& neighbour_address, const IPv4& neighbour_id); /** * Create a virtual link. * * @param neighbour_id the router ID of the other end of the link. * * @param area in which an attempt has been made to configure a virtual * link it has to be the backbone. Its just being passed in so it can be * checked by the protocol. */ XrlCmdError ospfv3_0_1_create_virtual_link( // Input values, const IPv4& neighbour_id, const IPv4& area); /** * Delete virtual link * * @param neighbour_id the router ID of the other end of the link. */ XrlCmdError ospfv3_0_1_delete_virtual_link( // Input values, const IPv4& neighbour_id); /** * The area through which the virtual link transits. * * @param neighbour_id the router ID of the other end of the link. * * @param transit_area that the virtual link transits. */ XrlCmdError ospfv3_0_1_transit_area_virtual_link( // Input values, const IPv4& neighbour_id, const IPv4& transit_area); /** * The edge cost of this interface. */ XrlCmdError ospfv3_0_1_set_interface_cost( // Input values, const string& ifname, const string& vifname, const IPv4& area, const uint32_t& cost); /** * The RxmtInterval. */ XrlCmdError ospfv3_0_1_set_retransmit_interval( // Input values, const string& ifname, const string& vifname, const IPv4& area, const uint32_t& interval); /** * Update packet will have their age incremented by this amount before * transmission. This value should take into account transmission and * propagation delays; it must be greater than zero. */ XrlCmdError ospfv3_0_1_set_inftransdelay( // Input values, const string& ifname, const string& vifname, const IPv4& area, const uint32_t& delay); /** * Used in the designated router election. */ XrlCmdError ospfv3_0_1_set_router_priority( // Input values, const string& ifname, const string& vifname, const IPv4& area, const uint32_t& interval); /** * The interval between hello messages. */ XrlCmdError ospfv3_0_1_set_hello_interval( // Input values, const string& ifname, const string& vifname, const IPv4& area, const uint32_t& interval); /** * The period to wait before considering a router dead. */ XrlCmdError ospfv3_0_1_set_router_dead_interval( // Input values, const string& ifname, const string& vifname, const IPv4& area, const uint32_t& interval); /** * Toggle the passive status of an interface. */ XrlCmdError ospfv3_0_1_set_passive( // Input values, const string& ifname, const string& vifname, const IPv4& area, const bool& passive); /** * If this is a "stub" or "nssa" area toggle the sending of a default * route. */ XrlCmdError ospfv3_0_1_originate_default_route( // Input values, const IPv4& area, const bool& enable); /** * Set the StubDefaultCost, the default cost sent in a default route in a * "stub" or "nssa" area. */ XrlCmdError ospfv3_0_1_stub_default_cost( // Input values, const IPv4& area, const uint32_t& cost); /** * Toggle the sending of summaries into "stub" or "nssa" areas. */ XrlCmdError ospfv3_0_1_summaries( // Input values, const IPv4& area, const bool& enable); /** * Add area range. */ XrlCmdError ospfv3_0_1_area_range_add( // Input values, const IPv4& area, const IPv6Net& net, const bool& advertise); /** * Delete area range. */ XrlCmdError ospfv3_0_1_area_range_delete( // Input values, const IPv4& area, const IPv6Net& net); /** * Change the advertised state of this area. */ XrlCmdError ospfv3_0_1_area_range_change_state( // Input values, const IPv4& area, const IPv6Net& net, const bool& advertise); /** * Enable/Disable tracing. * * @param tvar trace variable. * * @param enable set to true to enable false to disable. */ XrlCmdError ospfv3_0_1_trace( // Input values, const string& tvar, const bool& enable); /** * Get a single lsa from an area. A stateless mechanism to get LSAs. The * client of this interface should start from zero and continue to request * LSAs (incrementing index) until toohigh becomes true. * * @param area database that is being searched. * * @param index into database starting from 0. * * @param valid true if a LSA has been returned. Some index values do not * contain LSAs. This should not be considered an error. * * @param toohigh true if no more LSA exist after this index. * * @param self if true this LSA was originated by this router. * * @param lsa if valid is true the LSA at index. */ XrlCmdError ospfv3_0_1_get_lsa( // Input values, const IPv4& area, const uint32_t& index, // Output values, bool& valid, bool& toohigh, bool& self, vector& lsa); /** * Get a list of all the configured areas. */ XrlCmdError ospfv3_0_1_get_area_list(XrlAtomList& areas); /** * Get the list of neighbours. */ XrlCmdError ospfv3_0_1_get_neighbour_list( // Output values, XrlAtomList& areas); /** * Get information on a neighbour. * * @param nid neighbour ID returned by the get_neighbour_list. * * @param valid true if valid information has been returned. * * @param address of neighbour in txt to allow IPv4 and IPv6. * * @param interface with which the neighbour forms the adjacency. * * @param state of the adjacency. * * @param rid router ID of the neighbour. * * @param priority of the neighbour (used for DR election). * * @param area the neighbour is in. * * @param opt value in the neighbours hello packet. * * @param dr designated router. * * @param bdr backup designated router. * * @param up time in seconds that the neigbour has been up. * * @param adjacent time in seconds that there has been an adjacency. */ XrlCmdError ospfv3_0_1_get_neighbour_info( // Input values, const uint32_t& nid, // Output values, string& address, string& interface, string& state, IPv4& rid, uint32_t& priority, uint32_t& deadtime, IPv4& area, uint32_t& opt, IPv4& dr, IPv4& bdr, uint32_t& up, uint32_t& adjacent); /** * Clear the OSPF database. */ XrlCmdError ospfv3_0_1_clear_database(); private: // Ospf& _ospf_ipv4; Ospf& _ospf_ipv6; // XrlIO& _xrl_io_ipv4; XrlIO& _xrl_io_ipv6; }; #endif // ipv6 #endif // target3 xorp/ospf/fletcher_checksum.hh0000664000076400007640000000264411421137511016630 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/ospf/fletcher_checksum.hh,v 1.6 2008/10/02 21:57:47 bms Exp $ #ifndef __OSPF_FLETCHER_CHECKSUM_HH__ #define __OSPF_FLETCHER_CHECKSUM_HH__ /** * Compute a fletcher checksum * * @param bufp pointer to start of the buffer. * @param length of the buffer. * @param off Offset into the buffer where the checksum is placed. * @param x output value checksum * @param y output value checksum */ void fletcher_checksum(uint8_t *bufp, size_t len, size_t off, int32_t& x, int32_t& y); #endif // __OSPF_FLETCHER_CHECKSUM_HH__ xorp/ospf/vlink.hh0000664000076400007640000001063611421137511014275 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/ospf/vlink.hh,v 1.14 2008/10/02 21:57:50 bms Exp $ #ifndef __OSPF_VLINK_HH__ #define __OSPF_VLINK_HH__ /** * Manage all the state for virtual links. */ template class Vlink { public: /** * Add a virtual link router ID to the database. */ bool create_vlink(OspfTypes::RouterID rid); /** * Remove this router ID from the database. */ bool delete_vlink(OspfTypes::RouterID rid); /** * Set the transmit area through which the virtual link is formed. */ bool set_transit_area(OspfTypes::RouterID rid, OspfTypes::AreaID transit_area); /** * Get the transmit area information. */ bool get_transit_area(OspfTypes::RouterID rid, OspfTypes::AreaID& transit_area) const; /** * Set state to know if the area has been notified about this * virtual link. */ bool set_transit_area_notified(OspfTypes::RouterID rid, bool state); /** * Has the area been notified? */ bool get_transit_area_notified(OspfTypes::RouterID rid) const; /** * Associate the endpoint addresses with this virtual link. */ bool add_address(OspfTypes::RouterID rid, A source, A destination); /** * Get the endpoint addresses of this virtual link. */ bool get_address(OspfTypes::RouterID rid, A& source, A& destination); /** * Provide an interface and vif for this router ID. Must not be * called before the address information has been provided. */ bool get_interface_vif(OspfTypes::RouterID rid, string& interface, string& vif) const; /** * Save the peerid that has been allocted to this virtual link. */ bool add_peerid(OspfTypes::RouterID rid, OspfTypes::PeerID peerid); /** * Get the associated peerid. */ OspfTypes::PeerID get_peerid(OspfTypes::RouterID rid) const; /** * The phyical interface and vif that should be used for transmission. */ bool set_physical_interface_vif(OspfTypes::RouterID rid, string& interface, string& vif); /** * Given the source and destination addresses return the interface * and vif. */ bool get_physical_interface_vif(A source, A destination, string& interface, string& vif) const; /** * Given the source and destination address find the PeerID of the * relevant virtual link. */ OspfTypes::PeerID get_peerid(A source, A destination) const; /** * Get the list of virtual links (router ids) that flow through * this area. */ void get_router_ids(OspfTypes::AreaID transit_area, list& rids) const; /** * This area has been removed mark all the notified for this area * to false. Allowing an area to be removed and then brought back. */ void area_removed(OspfTypes::AreaID area); private: /** * State about each virtual link. */ struct Vstate { Vstate() : _peerid(OspfTypes::ALLPEERS), // Illegal value for a PeerID. _transit_area(OspfTypes::BACKBONE), // Again an illegal value. _notified(false) {} OspfTypes::PeerID _peerid; // PeerID of virtual link OspfTypes::AreaID _transit_area; // Transit area for the link // True if the transit area has been notified. bool _notified; A _source; // Source address A _destination; // Destination address // Required for transmission. string _physical_interface; // Actual interface string _physical_vif; // Actual vif }; map _vlinks; }; #endif // __OSPF_VLINK_HH__ xorp/ospf/TODO0000664000076400007640000001732111421137511013317 0ustar greearbgreearb TODO ~~~~ 1) None of the packet decode routines check the type field in the header against their own type field. Could probably put the approriate check in the standard header decode code. 2) I don't think that the LSA routines check their own type field either. 17) When decoding a link state update packet if an LSA is not decodable either due to being an unknown type or some other problem the whole packet is discarded. Only the bad LSA should be dropped. See section 13 of the RFC. 19) The delayed ACKs are not delayed. The peer should probably hold a delayed ack list that is transmitted ocassionally. 28) An LSA can be looked up by type,id and advertising router (find_lsa()). In an exception to this Network-LSAs can be looked up by id only (find_network_lsa()). The two lookups are currently linear scans of the database. Add a map for each lookup type to speed up lookup. 29) In xrl_target.cc calls are made directly to the peer_manager, equivalent calls exist in the ospf class and should be used in preference. 30) It is a requirement that LSAs should not be originated more frequently than MinLSInterval (5 seconds). It is also a requirement that an LSA is not accepted if it arrives more frequently than MinLSArrival (1 second). Generated LSAs are added to the delay queue in the area_router. If an LSA arrives more frequently than 1 second it is ignored. When as part of the database exchange process an LSA is requested it is returned immediately (not delayed by 5 seconds). As soon as routers become adjacent a new Router-LSA is generated. This Router-LSA is ignored because in the regression tests it typically arrives before 1 second. The retransmission of the Router-LSA eventually fixes the problem. We should probably delay the sending of the Router-LSA somehow after an adjacency is formed. Adding a timestamp to LSAs could be used to stop this problem or the delay queue could be prodded when a self originated LSA is requested. We could also perhaps be a little more permissive about the first LSA that violates the 1 second rule. This problem has been partially fixes by firing the delay timer when a self originating LSA is requested in a link state request packet. 36) There is a queue to which route adds and deletes are added. The queue does not handle replace or discard routes. Plus policy tags should be passed in somehow. 37) The original design did not leave MaxAge LSAs in the database. When an LSA reached MaxAge it was removed from the database and existed only in retransmission lists. If an LSA was received which seemed to be from a previous incarnation of OSPF it had its age set to MaxAge and was fired out, also not being added to the database. If while a MaxAge LSA is on the retransmission only, either a new LSA such as a Network-LSA is generated or an updated LSA arrives a second LSA can be created with the same tuple. Two LSAs can exist on the retransmission list. Leaving the a MaxAge LSA in the database solves both problems but doesn't deal with when to purge the LSA. The indicator that an LSA can be purged is when its NACK list becomes empty. Currently the MaxAge LSAs are purged in valid_entry_database() this method is called to build the list of LSAs during database exchange. Remove the purge code from valid_entry_database() make it and subsequent() const again. When a MaxAge LSA is removed from the retransmission list and the NACK list becomes empty have it callback to the area code from the peer code to remove the LSA from the database. One possible implementation is to actually put a callback in the LSA itself that the peer code invokes. If MAX_AGE_IN_DATABASE in database is not defined then a check is now made for the duplicate LSA in the retransmission list. It might be appropriate to leave MaxAge LSAs on the retransmission list. If this fix works then do away with MAX_AGE_IN_DATABASE. 40) When the adding of routes is more robust change the XLOG_ERROR to an XLOG_FATAL in route_command_done(). 44) The packet header encode and decode routines should use AUTH_* consts not hard coded numbers. 47) During shutdown the method PeerManager::virtual_link_endpoint gets called. The vlink database has knowledge of a peer that has been removed and a warning is printed. Modify the code to remove peerids from the vlink database when a peer is removed. 48) If the user disables an address that OSPF is using via the xorpsh then the interface/peer is taken down. Part of the interface down process involves leaving multicast groups. When leaving a multicast group in IPv4 the interface address is used as the key to the kernel. Disabling an address via the xorpsh removes the address from the kernel the subsequent attempt to leave the multicast group fails generating an error. OSPF in the IPv4 case should check if the address (key) is still enabled before attempting to leave a group. 49) When a policy is modified OSPF receives a push_routes call. All routes that are sent to the RIB are re-filtered, the import policies. The routes that are introduced to OSPF via an export policy are currently not re-filtered. A method External::push_routes() exists but currently does nothing. If the "then" part of an export policy is changed such as setting the nexthop, or the ebit or the tag. The route is currently pushed through the system again so the filter can be re-run. Currently no functionality seems to be lost by not implementing this method, as the routes are always pushed through the system anyway. For completeness the method should be implemented for the day that a policy change does not force all routes back through the system. In order to implement the push_routes the network, nexthop, metric and possibly policytags must be saved in a separate list, Or the AS-External-LSA could be augmented to hold the original values. 52) When implementing OSPFv3 the Vertex class was modified to hold a list of all the Router-LSAs that a router had generated. Currently only one entry is ever placed on the list. In order to support updating the list the SPT code will have to be modified to return a node. The list of Router-LSAs is actually held in LsaTempStore. Either the list should be updated in the Vertex class or it should be reverted to holding a single entry. My preference is reverting to holding a single entry. 53) The program in test_packet.cc contains a number of asserts of the form: XLOG_ASSERT(rlsa->known()); XLOG_ASSERT(!rlsa->link_local_scope()); XLOG_ASSERT(rlsa->area_scope()); XLOG_ASSERT(!rlsa->as_scope()); One for each LSA type, replace these asserts with a proper test for known and scopting, note this is OSPFv3 only. Possible issues ~~~~~~~~~~~~~~~ 1) Received hello packets are demultiplex on the source address of the packet or on the router ID, depending on the linktype that the packet arrived on. If a packet is being demultipled on the source address and the router ID changes is this likely to be a problem? 2) In the OSPFv2 case when generating router links the state backup is treated the same as DR_other, which generates links. Previously the backup state generated no links. In the OSPFv3 case in state backup we generate no links. Its not clear what the correct behaviour should be. 3) All LSAs carried in Link State Update Packets now have InfTransDelay added to them. InfTransDelay is being added to self originated LSAs as well is this correct? 4) The Network-LSAs are not explicitly stored in the area router. It *may* improve performance to store a reference to any Network-LSAs in the PeerState data structure. 5) The set_maxage() method on an LSA sets the age to MaxAge. It does not however manipulate the origination time. Therefore any subsequent erroneous calls to update_age() may revive an LSA. It may be safer to hold a separate flag in an LSA to stop the age being updated once it has been set to MaxAge. xorp/ospf/iso512.data0000664000076400007640000000674111421137511014510 0ustar greearbgreearb0x055, 0x0ab, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x010, 0x011, 0x012, 0x013, 0x014, 0x015, 0x016, 0x017, 0x018, 0x019, 0x01a, 0x01b, 0x01c, 0x01d, 0x01e, 0x01f, 0x020, 0x021, 0x022, 0x023, 0x024, 0x025, 0x026, 0x027, 0x028, 0x029, 0x02a, 0x02b, 0x02c, 0x02d, 0x02e, 0x02f, 0x030, 0x031, 0x032, 0x033, 0x034, 0x035, 0x036, 0x037, 0x038, 0x039, 0x03a, 0x03b, 0x03c, 0x03d, 0x03e, 0x03f, 0x040, 0x041, 0x042, 0x043, 0x044, 0x045, 0x046, 0x047, 0x048, 0x049, 0x04a, 0x04b, 0x04c, 0x04d, 0x04e, 0x04f, 0x050, 0x051, 0x052, 0x053, 0x054, 0x055, 0x056, 0x057, 0x058, 0x059, 0x05a, 0x05b, 0x05c, 0x05d, 0x05e, 0x05f, 0x060, 0x061, 0x062, 0x063, 0x064, 0x065, 0x066, 0x067, 0x068, 0x069, 0x06a, 0x06b, 0x06c, 0x06d, 0x06e, 0x06f, 0x070, 0x071, 0x072, 0x073, 0x074, 0x075, 0x076, 0x077, 0x078, 0x079, 0x07a, 0x07b, 0x07c, 0x07d, 0x07e, 0x07f, 0x080, 0x081, 0x082, 0x083, 0x084, 0x085, 0x086, 0x087, 0x088, 0x089, 0x08a, 0x08b, 0x08c, 0x08d, 0x08e, 0x08f, 0x090, 0x091, 0x092, 0x093, 0x094, 0x095, 0x096, 0x097, 0x098, 0x099, 0x09a, 0x09b, 0x09c, 0x09d, 0x09e, 0x09f, 0x0a0, 0x0a1, 0x0a2, 0x0a3, 0x0a4, 0x0a5, 0x0a6, 0x0a7, 0x0a8, 0x0a9, 0x0aa, 0x0ab, 0x0ac, 0x0ad, 0x0ae, 0x0af, 0x0b0, 0x0b1, 0x0b2, 0x0b3, 0x0b4, 0x0b5, 0x0b6, 0x0b7, 0x0b8, 0x0b9, 0x0ba, 0x0bb, 0x0bc, 0x0bd, 0x0be, 0x0bf, 0x0c0, 0x0c1, 0x0c2, 0x0c3, 0x0c4, 0x0c5, 0x0c6, 0x0c7, 0x0c8, 0x0c9, 0x0ca, 0x0cb, 0x0cc, 0x0cd, 0x0ce, 0x0cf, 0x0d0, 0x0d1, 0x0d2, 0x0d3, 0x0d4, 0x0d5, 0x0d6, 0x0d7, 0x0d8, 0x0d9, 0x0da, 0x0db, 0x0dc, 0x0dd, 0x0de, 0x0df, 0x0e0, 0x0e1, 0x0e2, 0x0e3, 0x0e4, 0x0e5, 0x0e6, 0x0e7, 0x0e8, 0x0e9, 0x0ea, 0x0eb, 0x0ec, 0x0ed, 0x0ee, 0x0ef, 0x0f0, 0x0f1, 0x0f2, 0x0f3, 0x0f4, 0x0f5, 0x0f6, 0x0f7, 0x0f8, 0x0f9, 0x0fa, 0x0fb, 0x0fc, 0x0fd, 0x0fe, 0x0ff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x010, 0x011, 0x012, 0x013, 0x014, 0x015, 0x016, 0x017, 0x018, 0x019, 0x01a, 0x01b, 0x01c, 0x01d, 0x01e, 0x01f, 0x020, 0x021, 0x022, 0x023, 0x024, 0x025, 0x026, 0x027, 0x028, 0x029, 0x02a, 0x02b, 0x02c, 0x02d, 0x02e, 0x02f, 0x030, 0x031, 0x032, 0x033, 0x034, 0x035, 0x036, 0x037, 0x038, 0x039, 0x03a, 0x03b, 0x03c, 0x03d, 0x03e, 0x03f, 0x040, 0x041, 0x042, 0x043, 0x044, 0x045, 0x046, 0x047, 0x048, 0x049, 0x04a, 0x04b, 0x04c, 0x04d, 0x04e, 0x04f, 0x050, 0x051, 0x052, 0x053, 0x054, 0x055, 0x056, 0x057, 0x058, 0x059, 0x05a, 0x05b, 0x05c, 0x05d, 0x05e, 0x05f, 0x060, 0x061, 0x062, 0x063, 0x064, 0x065, 0x066, 0x067, 0x068, 0x069, 0x06a, 0x06b, 0x06c, 0x06d, 0x06e, 0x06f, 0x070, 0x071, 0x072, 0x073, 0x074, 0x075, 0x076, 0x077, 0x078, 0x079, 0x07a, 0x07b, 0x07c, 0x07d, 0x07e, 0x07f, 0x080, 0x081, 0x082, 0x083, 0x084, 0x085, 0x086, 0x087, 0x088, 0x089, 0x08a, 0x08b, 0x08c, 0x08d, 0x08e, 0x08f, 0x090, 0x091, 0x092, 0x093, 0x094, 0x095, 0x096, 0x097, 0x098, 0x099, 0x09a, 0x09b, 0x09c, 0x09d, 0x09e, 0x09f, 0x0a0, 0x0a1, 0x0a2, 0x0a3, 0x0a4, 0x0a5, 0x0a6, 0x0a7, 0x0a8, 0x0a9, 0x0aa, 0x0ab, 0x0ac, 0x0ad, 0x0ae, 0x0af, 0x0b0, 0x0b1, 0x0b2, 0x0b3, 0x0b4, 0x0b5, 0x0b6, 0x0b7, 0x0b8, 0x0b9, 0x0ba, 0x0bb, 0x0bc, 0x0bd, 0x0be, 0x0bf, 0x0c0, 0x0c1, 0x0c2, 0x0c3, 0x0c4, 0x0c5, 0x0c6, 0x0c7, 0x0c8, 0x0c9, 0x0ca, 0x0cb, 0x0cc, 0x0cd, 0x0ce, 0x0cf, 0x0d0, 0x0d1, 0x0d2, 0x0d3, 0x0d4, 0x0d5, 0x0d6, 0x0d7, 0x0d8, 0x0d9, 0x0da, 0x0db, 0x0dc, 0x0dd, 0x0de, 0x0df, 0x0e0, 0x0e1, 0x0e2, 0x0e3, 0x0e4, 0x0e5, 0x0e6, 0x0e7, 0x0e8, 0x0e9, 0x0ea, 0x0eb, 0x0ec, 0x0ed, 0x0ee, 0x0ef, 0x0f0, 0x0f1, 0x0f2, 0x0f3, 0x0f4, 0x0f5, 0x0f6, 0x0f7, 0x0f8, 0x0f9, 0x0fa, 0x0fb, 0x0fc, 0x0fd, 0x0fe, 0x0ff xorp/ospf/xorp_ospfv2.cc0000664000076400007640000000475611703345405015443 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2012 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "ospf_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/ipv4.hh" #include "libxorp/ipv6.hh" #include "libxorp/service.hh" #include "libxorp/status_codes.h" #include "libxorp/eventloop.hh" #include "libxipc/xrl_std_router.hh" #include "ospf.hh" #include "io.hh" #include "xrl_io.hh" #include "xrl_target.hh" int main(int /*argc*/, char **argv) { XorpUnexpectedHandler x(xorp_unexpected_handler); // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_level_set_verbose(XLOG_LEVEL_WARNING, XLOG_VERBOSE_HIGH); xlog_level_set_verbose(XLOG_LEVEL_INFO, XLOG_VERBOSE_HIGH); xlog_level_set_verbose(XLOG_LEVEL_TRACE, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); try { EventLoop eventloop; string feaname = "fea"; string ribname = "rib"; XrlStdRouter xrl_router(eventloop, TARGET_OSPFv2); XrlIO io(eventloop, xrl_router, feaname, ribname); Ospf ospf(OspfTypes::V2, eventloop, &io); XrlOspfV2Target v2target(&xrl_router, ospf, io); wait_until_xrl_router_is_ready(eventloop, xrl_router); io.startup(); setup_dflt_sighandlers(); while (xorp_do_run && ospf.running()) eventloop.run(); } catch(...) { xorp_catch_standard_exceptions(); } // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); debug_msg("Bye!\n"); return 0; } xorp/ospf/peer.hh0000664000076400007640000014062411703345405014114 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2012 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __OSPF_PEER_HH__ #define __OSPF_PEER_HH__ template class Ospf; template class Peer; #include "auth.hh" /** * In OSPF terms this class represents an interface/link; interface is * too overloaded a term. The Peer class is also associated with an * area. In OSPFv2 there is a one-to-one correspondence. In OSPFv3 an * interface/link can belong to multiple areas. * * Responsibilities: * 1) Packet transmission; the outgoing queue lives here. * 2) Exchange Hello packets. * 3) Bring up adjacency, synchronize databases. * 4) Elect designated and backup router. */ template class PeerOut { public: PeerOut(Ospf& ospf, const string interface, const string vif, OspfTypes::PeerID peerid, const A source, OspfTypes::LinkType linktype, OspfTypes::AreaID area, OspfTypes::AreaType area_type); ~PeerOut(); /** * For debugging only printable rendition of this interface/vif. */ string get_if_name() const { return _interface + "/" + _vif; } /** * If the source address matches the interface address return the * interface and vif. */ bool match(A source, string& interface, string& vif); /** * Get Peer ID. */ OspfTypes::PeerID get_peerid() const { return _peerid; } /** * Set the address of this interface/vif */ bool set_interface_address(A interface_address) { _interface_address = interface_address; return true; } /** * Address of this interface/vif. * * @return interface/vif address. */ A get_interface_address() const { return _interface_address; } /** * @return prefix length of this interface. */ uint16_t get_interface_prefix_length() const; /** * @return mtu of this interface. */ uint16_t get_interface_mtu() const { XLOG_ASSERT(0 != _interface_mtu); return _interface_mtu; } /** * The maximum size of an OSPF frame, the MTU minus the IP header. * * @return maximum frame size. */ uint16_t get_frame_size() const; /** * Join multicast group on this interface/vif. */ void join_multicast_group(A address) { _ospf.join_multicast_group(_interface, _vif, address); } /** * Leave multicast group on this interface/vif. */ void leave_multicast_group(A address) { _ospf.leave_multicast_group(_interface, _vif, address); } /** * @return cost of this interface. */ uint16_t get_interface_cost() const { return _interface_cost; } /** * @return InfTransDelay */ uint16_t get_inftransdelay() const { return _inftransdelay; } /** * Get the list of areas associated with this peer. */ bool get_areas(list& areas) const; /** * Add another Area for this peer to be in, should only be allowed * for OSPFv3. */ bool add_area(OspfTypes::AreaID area, OspfTypes::AreaType area_type); /** * Change the type of this area. */ bool change_area_router_type(OspfTypes::AreaID area, OspfTypes::AreaType area_type); /** * This area is being removed. * * @return true if this peer is no longer associated with any * areas. Allowing the caller to delete this peer. */ bool remove_area(OspfTypes::AreaID area); /** * Add a neighbour to the peer. */ bool add_neighbour(OspfTypes::AreaID area, A neighbour_address, OspfTypes::RouterID); /** * Remove a neighbour from the peer. */ bool remove_neighbour(OspfTypes::AreaID area, A neighbour_address, OspfTypes::RouterID rid); /** * Set the state of this peer. */ void set_state(bool state); /** * Get the state of this peer. */ bool get_state() const { return _running; } /** * Set the link status. This is not only the link status but is * the interface/vif/address configured up. */ void set_link_status(bool status, const char* dbg); /** * Dependent on the configured peer status and the link status * decide if the peer should be brought up or taken down. */ void peer_change(); /** * Used by external and internal entities to transmit packets. */ bool transmit(typename Transmit::TransmitRef tr); /** * Packets for this peer are received here. */ bool receive(A dst, A src, Packet *packet) throw(BadPeer); /** * Send this LSA directly to the neighbour. Do not place on * retransmission list. * * @param area * @param nid * @param lsar * * @return true on success */ bool send_lsa(OspfTypes::AreaID area, const OspfTypes::NeighbourID nid, Lsa::LsaRef lsar); /** * Queue an LSA for transmission. * * @param peer the LSA arrived on. * @param nid the LSA arrived on. * @param lsar the lsa * @param multicast_on_peer Did this LSA get multicast on this peer. * * @return true on success. */ bool queue_lsa(OspfTypes::PeerID peerid, OspfTypes::NeighbourID nid, Lsa::LsaRef lsar, bool &multicast_on_peer) const; /** * Send (push) any queued LSAs. */ bool push_lsas(const char* message); /** * Are any of the neighbours of this peer in the state exchange or * loading. * * @param area * * @return true if any of the neighbours are in state exchange or loading. */ bool neighbours_exchange_or_loading(OspfTypes::AreaID area); /** * Is the state of the neighbour with the specified Router ID at * least 2-Way. * * @param area * @param rid Router ID * @param twoway if the neighbour is found true means the * neighbour is at least twoway. * * @return true if the neighbour is found. */ bool neighbour_at_least_two_way(OspfTypes::AreaID area, OspfTypes::RouterID rid, bool& twoway); /** * Neighbour's source address. * * @param area * @param rid Router ID * @param interface_id Interface ID. * @param neighbour_address set if neighbour is found. * * @return true if the neighbour is found. */ bool get_neighbour_address(OspfTypes::AreaID area, OspfTypes::RouterID rid, uint32_t interface_id, A& neighbour_address); /** * Is this LSA on this neighbours link state request list. * @param nid * * @return true if it is. */ bool on_link_state_request_list(OspfTypes::AreaID area, const OspfTypes::NeighbourID nid, Lsa::LsaRef lsar); /** * Generate a BadLSReq event. * * @param area * @param nid * */ bool event_bad_link_state_request(OspfTypes::AreaID area, const OspfTypes::NeighbourID nid); /** * Are any of neighbours of this peer a virtual link endpoint. * * @return true if any are. */ bool virtual_link_endpoint(OspfTypes::AreaID area); /** * @return the link type. */ OspfTypes::LinkType get_linktype() const { return _linktype; } // Configure the peering. /** * The router ID is about to change. */ void router_id_changing(); /** * Set the interface ID OSPFv3 only. */ bool set_interface_id(uint32_t interface_id); /** * Get the interface ID OSPFv3 only. */ uint32_t get_interface_id() const { return _interface_id; } /** * Return a list of the fully adjacent routers. */ bool get_attached_routers(OspfTypes::AreaID area, list& routes); /** * Set a network to advertise OSPFv3 only. */ bool add_advertise_net(OspfTypes::AreaID area, A addr, uint32_t prefix); /** * Remove all the networks that are being advertised OSPFv3 only. */ bool remove_all_nets(OspfTypes::AreaID area); /** * Calls to add_advertise_net() and remove_all_nets() must be * followed by a call to update nets to force a new Link-LSA to be * sent out OSPFv3 only. */ bool update_nets(OspfTypes::AreaID area); /** * Set the hello interval in seconds. */ bool set_hello_interval(OspfTypes::AreaID area, uint16_t hello_interval); /** * Set options. */ bool set_options(OspfTypes::AreaID area, uint32_t options); /** * Set router priority. */ bool set_router_priority(OspfTypes::AreaID area, uint8_t priority); /** * Set the router dead interval in seconds. */ bool set_router_dead_interval(OspfTypes::AreaID area, uint32_t router_dead_interval); /** * Set a simple password authentication key. * * Note that the current authentication handler is replaced with * a simple password authentication handler. * * @param area the area ID. * @param password the password to set. * @param the error message (if error). * @return true on success, otherwise false. */ bool set_simple_authentication_key(OspfTypes::AreaID area, const string& password, string& error_msg); /** * Delete a simple password authentication key. * * Note that after the deletion the simple password authentication * handler is replaced with a Null authentication handler. * * @param area the area ID. * @param the error message (if error). * @return true on success, otherwise false. */ bool delete_simple_authentication_key(OspfTypes::AreaID area, string& error_msg); /** * Set an MD5 authentication key. * * Note that the current authentication handler is replaced with * an MD5 authentication handler. * * @param area the area ID. * @param key_id unique ID associated with key. * @param password phrase used for MD5 digest computation. * @param start_timeval start time when key becomes valid. * @param end_timeval end time when key becomes invalid. * @param max_time_drift the maximum time drift among all routers. * @param the error message (if error). * @return true on success, otherwise false. */ bool set_md5_authentication_key(OspfTypes::AreaID area, uint8_t key_id, const string& password, const TimeVal& start_timeval, const TimeVal& end_timeval, const TimeVal& max_time_drift, string& error_msg); /** * Delete an MD5 authentication key. * * Note that after the deletion if there are no more valid MD5 keys, * the MD5 authentication handler is replaced with a Null authentication * handler. * * @param area the area ID. * @param key_id the ID of the key to delete. * @param the error message (if error). * @return true on success, otherwise false. */ bool delete_md5_authentication_key(OspfTypes::AreaID area, uint8_t key_id, string& error_msg); /** * Toggle the passive status of an interface. */ bool set_passive(OspfTypes::AreaID area, bool passive, bool host); /** * Set the interface cost. */ bool set_interface_cost(uint16_t interface_cost); /** * Set RxmtInterval. */ bool set_retransmit_interval(OspfTypes::AreaID area, uint16_t retransmit_interval); /** * Set InfTransDelay. */ bool set_inftransdelay(uint16_t inftransdelay) { _inftransdelay = inftransdelay; return true; } /** * Get a list of all the neighbours. */ bool get_neighbour_list(list& neighbours) const; /** * Get state information about this neighbour. * * @param nid neighbour information is being request about. * @param ninfo if neighbour is found its information. * */ bool get_neighbour_info(OspfTypes::NeighbourID nid, NeighbourInfo& ninfo) const; /** * Get the set of addresses that should be advertised OSPFv3 only. */ set >& get_address_info(OspfTypes::AreaID area); /** * Enable receiving packets on this interface. */ void start_receiving_packets(); /** * Disable receiving packets on this interface. */ void stop_receiving_packets(); private: Ospf& _ospf; // Reference to the controlling class. const string _interface; // The interface and vif this peer is const string _vif; // responsible for. const OspfTypes::PeerID _peerid; // The peers ID. uint32_t _interface_id; // Inferface ID OSPFv3 only. A _interface_address; // Interface address. uint16_t _interface_prefix_length; // Interface prefix length uint16_t _interface_mtu; // MTU of this interface. uint16_t _interface_cost; // Cost of this interface. uint16_t _inftransdelay; // InfTransDelay. const OspfTypes::LinkType _linktype; // Type of this link. // Areas being served. map *> _areas; // In order for the peer to be up and running the peer has to be // configured up and the link also has to be up. bool _running; // True if the peer is up and running bool _link_status; // True if the link is up, // cable connected and // interface/vif/address // configured up. bool _status; // True if the peer has been // configured up. set > _dummy; // If get_address_info fails // return a reference to this // dummy entry bool _receiving; // Are we currently enabled // for receiving packets. /** * If this IPv4 then set the mask in the hello packet. */ void set_mask(Peer *peer); // In order to maintain the requirement for an interpacket gap, // all outgoing packets are appended to this queue. Then they are // read off the queue and transmitted at the interpacket gap rate. queue::TransmitRef> _transmit_queue; bool bring_up_peering(); void take_down_peering(); /** * Are all the peers in a passive state. */ bool get_passive(); }; template class Neighbour; /** * A peer represents a single area and is bound to a PeerOut. */ template class Peer { public: /** * Interface as defined by OSPF not XORP. */ enum InterfaceState { Down, Loopback, Waiting, Point2Point, DR_other, Backup, DR, }; Peer(Ospf& ospf, PeerOut& peerout, OspfTypes::AreaID area_id, OspfTypes::AreaType area_type) : _ospf(ospf), _peerout(peerout), _area_id(area_id), _area_type(area_type), _go_called(false), _enabled(false), _passive(false), _passive_host(false), _auth_handler(_ospf.get_eventloop()), _interface_state(Down), _hello_packet(ospf.get_version()) { _hello_packet.set_area_id(area_id); // Some defaults taken from the Juniper manual. These values // should be overriden by the values in the templates files. // For testing set some useful values _hello_packet.set_hello_interval(10); _hello_packet.set_router_priority(128); // RFC 2328 Appendix C.3 Router Interface Parameters _hello_packet. set_router_dead_interval(4 * _hello_packet.get_hello_interval()); _rxmt_interval = 5; init(); } ~Peer() { typename list *>::iterator n; for (n = _neighbours.begin(); n != _neighbours.end(); n++) delete (*n); _neighbours.clear(); shutdown(); } /** * Should be invoked just once. */ bool init() { bool status = true; switch(_ospf.get_version()) { case OspfTypes::V2: break; case OspfTypes::V3: status = initV3(); break; } return status; } /** * Will only execute if go() has been called. Can be called multiple * times paired with go(). */ bool shutdown() { bool status = true; if (!_go_called) return status; else _go_called = false; switch(_ospf.get_version()) { case OspfTypes::V2: break; case OspfTypes::V3: status = shutdownV3(); break; } return status; } /** * Called once the peer is configured and enabled. Can be called * multiple times paired with shutdown(). */ bool go() { bool status = true; XLOG_ASSERT(!_go_called); _go_called = true; switch(_ospf.get_version()) { case OspfTypes::V2: break; case OspfTypes::V3: status = goV3(); break; } return status; } /** * OSPFv3 specific initialisation. */ bool initV3(); /** * OSPFv3 specific go */ bool goV3(); /** * OSPFv3 specific shutdown */ bool shutdownV3(); /* * OSPFv3 set all the fields for the peers Link-LSA. */ void populate_link_lsa(); /** * If the source address matches a global address return true. */ bool match(IPv6 source) const; /** * For debugging only printable rendition of this interface/vif. */ string get_if_name() const { return _peerout.get_if_name(); } /** * Get Peer ID. * */ OspfTypes::PeerID get_peerid() const { return _peerout.get_peerid(); } /** * Address of this interface/vif. * * @return interface/vif address. */ A get_interface_address() const { return _peerout.get_interface_address(); } /** * @return prefix length of this interface. */ uint16_t get_interface_prefix_length() const { return _peerout.get_interface_prefix_length(); } #if 0 /** * Address of the p2p neighbour. * * @return p2p neighbour address. */ A get_p2p_neighbour_address() const { XLOG_ASSERT(OspfTypes::PointToPoint == get_linktype()); XLOG_ASSERT(1 == _neighbours.size()); // When an P2P interface is configured a single neighbour will // exist. Fetch the address from the neighbour structure. typename list *>::const_iterator ni = _neighbours.begin(); XLOG_ASSERT(ni != _neighbours.end()); return (*ni)->get_neighbour_address(); } #endif /** * @return mtu of this interface. */ uint16_t get_interface_mtu() const { return _peerout.get_interface_mtu(); } /** * The maximum size of an OSPF frame, the MTU minus the IP * header. Also include any bytes that the authentication scheme * may use. * * @return maximum frame size. */ uint16_t get_frame_size() const { return _peerout.get_frame_size() - _auth_handler.additional_payload(); } /** * @return InfTransDelay */ uint16_t get_inftransdelay() const { return _peerout.get_inftransdelay(); } /** * Used by external and internal entities to transmit packets. */ bool transmit(typename Transmit::TransmitRef tr) { return _peerout.transmit(tr); } /** * Add neighbour */ bool add_neighbour(A neighbour_address, OspfTypes::RouterID rid); /** * Remove neighbour */ bool remove_neighbour(A neighbour_address, OspfTypes::RouterID rid); /** * Address belongs to this router used for destination address validation. */ bool belongs(A addr) const; /** * Packets for this peer are received here. */ bool receive(A dst, A src, Packet *packet); /** * Used to test if an lsa should be accepted for this * peer/neighbour. Specifically to deal with the case that * AS-External-LSAs should not be sent on virtual links. */ bool accept_lsa(Lsa::LsaRef lsar) const; /** * Send this LSA directly to the neighbour. Do not place on * retransmission list. * * @param nid * @param lsar * * @return true on success */ bool send_lsa(const OspfTypes::NeighbourID nid, Lsa::LsaRef lsar) const; /** * Queue an LSA for transmission. * * @param peer the LSA arrived on. * @param nid the LSA arrived on. * @param lsar the lsa * @param multicast_on_peer Did this LSA get multicast on this peer. * * @return true on success. */ bool queue_lsa(OspfTypes::PeerID peerid, OspfTypes::NeighbourID nid, Lsa::LsaRef lsar, bool& multicast_on_peer) const; /** * Send (push) any queued LSAs. */ bool push_lsas(const char* message); /* * Should we be computing the DR and BDR on this peer? * Another way of phrasing this is, is the linktype BROADCAST or NBMA? */ bool do_dr_or_bdr() const; /** * @return true if this router is the DR. */ bool is_DR() const; /** * @return true if this router is the BDR. */ bool is_BDR() const; /** * @return true if this router is the DR or BDR. */ bool is_DR_or_BDR() const; /** * Are any of the neighbours of this peer in the state exchange or * loading. * * @return true if any of the neighbours are in state exchange or loading. */ bool neighbours_exchange_or_loading() const; /** * Get the state of the neighbour with the specified router ID. * * @param rid Router ID * @param twoway if the neighbour is found true means the * neighbour is at least twoway. * * @return true if the neighbour is found. */ bool neighbour_at_least_two_way(OspfTypes::RouterID rid, bool& twoway); /** * Neighbour's source address. * * @param rid Router ID * @param interface_id Interface ID. * @param neighbour_address set if neighbour is found. * * @return true if the neighbour is found. */ bool get_neighbour_address(OspfTypes::RouterID rid, uint32_t interface_id, A& neighbour_address); /** * Is this LSA on this neighbours link state request list. * @param nid * * @return true if it is. */ bool on_link_state_request_list(const OspfTypes::NeighbourID nid, Lsa::LsaRef lsar) const; /** * Generate a BadLSReq event. * * @param nid * */ bool event_bad_link_state_request(const OspfTypes::NeighbourID nid) const; /** * Are any of neighbours of this peer a virtual link endpoint. * * @return true if any are. */ bool virtual_link_endpoint() const; /** * Send direct ACKs * * @param nid the neighbour that the LSAs that are being acked * arrived on. * @param ack list of acks to send. */ void send_direct_acks(OspfTypes::NeighbourID nid, list& ack); /** * Send delayed ACKs * * @param nid the neighbour that the LSAs that are being acked * arrived on. * @param ack list of acks to send. */ void send_delayed_acks(OspfTypes::NeighbourID nid, list& ack); /* * Find neighbour that this address or router ID is associated * with. If the linktype is Virtual Link or PointToPoint the * router ID is used otherwise the src address is used. * * @param src address of neighbour. * @param rid router ID of neighbour * * @return neighbour or 0 if no match. */ Neighbour *find_neighbour(A src, OspfTypes::RouterID rid); /** * @return true if this routers neighbour is the DR or BDR. */ bool is_neighbour_DR_or_BDR(OspfTypes::NeighbourID nid) const; /** * Process a hello packet. */ bool process_hello_packet(A dst, A src, HelloPacket *hello); /** * Process a data description packet. */ bool process_data_description_packet(A dst, A src, DataDescriptionPacket *dd); /** * Process a link state request packet. */ bool process_link_state_request_packet(A dst, A src, LinkStateRequestPacket *lsrp); /** * Process a link state update packet. */ bool process_link_state_update_packet(A dst, A src, LinkStateUpdatePacket *lsup); /** * Process a link state acknowledgement packet. */ bool process_link_state_acknowledgement_packet(A dst, A src, LinkStateAcknowledgementPacket *lsap); /** * Start the protocol machinery running */ void start(); /** * Stop the protocol machinery running */ void stop(); /** * Change the type of this area. */ void change_area_router_type(OspfTypes::AreaType area_type); /** * Event: InterfaceUP */ void event_interface_up(); /** * Event: WaitTimer */ void event_wait_timer(); /** * Event: BackupSeen */ void event_backup_seen(); /** * Event: NeighborChange */ void event_neighbour_change(); /** * Event: LoopInd */ void event_loop_ind(); /** * Event: UnLoopInd */ void event_unloop_ind(); /** * Event: InterfaceDown */ void event_interface_down(); /** * Schedule an event, used by the neighbours to schedule an * interface event. */ void schedule_event(const char *); /** * Run all the deferred events, callback method. */ void process_scheduled_events(); /** * Get the area router. */ AreaRouter *get_area_router() { AreaRouter *area_router = _ospf.get_peer_manager().get_area_router(get_area_id()); XLOG_ASSERT(area_router); return area_router; } /** * @return the value that should be used for DR or BDR. * In OSPFv2 its the source address of the interface. * In OSPFv3 its the router ID. */ static OspfTypes::RouterID get_candidate_id(A, OspfTypes::RouterID); /** * @return the value that should be used for DR or BDR for this router * In OSPFv2 its the source address of the interface. * In OSPFv3 its the router ID. * A dummy argument is used to force an IPv4 and an IPv6 instance * of this method to be generated. Isn't C++ cool? */ OspfTypes::RouterID get_candidate_id(A = A::ZERO()) const; InterfaceState get_state() const { return _interface_state; } /** * @return the link type. */ OspfTypes::LinkType get_linktype() const { return _peerout.get_linktype(); } /** * Return the authentication handler. */ Auth& get_auth_handler() { return _auth_handler; } #if 0 /** * @return the options field that is placed in some of outgoing * packets. */ uint32_t send_options(); #endif /** * Fill in the common header parts of the packet. */ void populate_common_header(Packet& packet); /** * Pretty print the interface state. */ static string pp_interface_state(InterfaceState is); /** * @return the Area ID. */ OspfTypes::AreaID get_area_id() const { return _area_id; } /** * @return the Area Type. */ OspfTypes::AreaType get_area_type() const { return _area_type; } /** * @return the Area Type. */ void set_area_type(OspfTypes::AreaType area_type) { _area_type = area_type; } /** * The router ID is about to change. */ void router_id_changing(); /** * Set the network mask OSPFv2 only. */ bool set_network_mask(uint32_t network_mask); /** * Set the network mask OSPFv2 only. */ uint32_t get_network_mask() const; /** * Set the interface ID OSPFv3 only. */ bool set_interface_id(uint32_t interface_id); /** * Get the interface ID OSPFv3 only. */ uint32_t get_interface_id() const; /** * Set a network to advertise OSPFv3 only. */ bool add_advertise_net(A addr, uint32_t prefix); /** * Remove all the networks that are being advertised OSPFv3 only. */ bool remove_all_nets(); /** * Calls to add_advertise_net() and remove_all_nets() must be * followed by a call to update nets to force a new Link-LSA to be * sent out OSPFv3 only. */ bool update_nets(); /** * Set the hello interval in seconds. */ bool set_hello_interval(uint16_t hello_interval); /** * Set options. */ bool set_options(uint32_t options); /** * Get options. */ uint32_t get_options() const; /** * Set router priority. */ bool set_router_priority(uint8_t priority); /** * Set the router dead interval in seconds. */ bool set_router_dead_interval(uint32_t router_dead_interval); /** * Get the router dead interval in seconds. */ uint32_t get_router_dead_interval() const; /** * Set a simple password authentication key. * * Note that the current authentication handler is replaced with * a simple password authentication handler. * * @param password the password to set. * @param the error message (if error). * @return true on success, otherwise false. */ bool set_simple_authentication_key(const string& password, string& error_msg); /** * Delete a simple password authentication key. * * Note that after the deletion the simple password authentication * handler is replaced with a Null authentication handler. * * @param the error message (if error). * @return true on success, otherwise false. */ bool delete_simple_authentication_key(string& error_msg); /** * Set an MD5 authentication key. * * Note that the current authentication handler is replaced with * an MD5 authentication handler. * * @param key_id unique ID associated with key. * @param password phrase used for MD5 digest computation. * @param start_timeval start time when key becomes valid. * @param end_timeval end time when key becomes invalid. * @param max_time_drift the maximum time drift among all routers. * @param the error message (if error). * @return true on success, otherwise false. */ bool set_md5_authentication_key(uint8_t key_id, const string& password, const TimeVal& start_timeval, const TimeVal& end_timeval, const TimeVal& max_time_drift, string& error_msg); /** * Delete an MD5 authentication key. * * Note that after the deletion if there are no more valid MD5 keys, * the MD5 authentication handler is replaced with a Null authentication * handler. * * @param key_id the ID of the key to delete. * @param the error message (if error). * @return true on success, otherwise false. */ bool delete_md5_authentication_key(uint8_t key_id, string& error_msg); /** * Toggle the passive status of an interface. */ bool set_passive(bool passive, bool host); /** * If all peers are in state passive then return passive. */ bool get_passive() const; /** * Set RxmtInterval. */ bool set_rxmt_interval(uint32_t rxmt_interval); /** * Get RxmtInterval. */ uint32_t get_rxmt_interval(); /** * Get the designated router. */ OspfTypes::RouterID get_designated_router() const; /** * Get the backup designated router. */ OspfTypes::RouterID get_backup_designated_router() const; /** * Get the interface ID of the designated router. * OSPFv3 only. */ uint32_t get_designated_router_interface_id(A = A::ZERO()) const; /** * Compute the current router link. * * Typically called after a state transition. */ void update_router_links(); /** * Used to notify the peer that a neighbour has become fully * adjacent or a neighbour is no longer fully adjacent. Used to * trigger the generation or withdrawal of a network-LSA. Should * only be called if the interface is in state DR. * * @param up true if the adjacency became full, false otherwise. */ void adjacency_change(bool up); /** * Get a list of all the neighbours. */ bool get_neighbour_list(list& neighbours) const; /** * Get state information about this neighbour. * * @param nid neighbour information is being request about. * @param ninfo if neighbour is found its information. * */ bool get_neighbour_info(OspfTypes::NeighbourID nid, NeighbourInfo& ninfo) const; /** * Get this list of addresses that should be advertised OSPFv3 only. */ set >& get_address_info(); /** * Return a list of the fully adjacent routers. */ bool get_attached_routers(list& routers); private: Ospf& _ospf; // Reference to the controlling class. PeerOut& _peerout; // Reference to PeerOut class. const OspfTypes::AreaID _area_id; // Area that is being represented. OspfTypes::AreaType _area_type; // NORMAL or STUB or NSSA. bool _go_called; // True if go() has been called. bool _enabled; // True if the interface is enabled. bool _passive; // True if the interface is in loopback bool _passive_host; // True for host, False for // network when in loopback i.e. // (_passive == True). Auth _auth_handler; // The authentication handler. XorpTimer _hello_timer; // Timer used to fire hello messages. XorpTimer _wait_timer; // Wait to discover other DRs. XorpTimer _event_timer; // Defer event timer. uint32_t _rxmt_interval; // The number of seconds // between transmission for: // LSAs, DDs and LSRPs. InterfaceState _interface_state; list *> _neighbours; // List of discovered neighbours. HelloPacket _hello_packet; // Packet that is sent by this peer. Lsa::LsaRef _link_lsa; // This interfaces OSPFv3 Link-LSA. list _router_links; // Router links for this peer /** * Possible DR or BDR candidates. */ struct Candidate { Candidate(OspfTypes::RouterID candidate_id, OspfTypes::RouterID router_id, OspfTypes::RouterID dr, OspfTypes::RouterID bdr, uint8_t router_priority) : _candidate_id(candidate_id), _router_id(router_id), _dr(dr), _bdr(bdr), _router_priority(router_priority) {} // OSPFv2 the candidate ID is the interface address. // OSPFv3 the candidate ID is the Router ID. OspfTypes::RouterID _candidate_id;// Candidate's ID OspfTypes::RouterID _router_id; // Router ID OspfTypes::RouterID _dr; // Designated router. OspfTypes::RouterID _bdr; // Backup Designated router. uint8_t _router_priority; // Router Priority. string str() const { return c_format("CID %s RID %s DR %s BDR %s PRI %d", pr_id(_candidate_id).c_str(), pr_id(_router_id).c_str(), pr_id(_dr).c_str(), pr_id(_bdr).c_str(), _router_priority); } }; list _scheduled_events; // List of deferred events. set > _address_info; // Set of addresses that have // been configured with this // peer. Only the peer manager // should access this data // structure. /** * Change state, use this not set_state when changing states. */ void change_state(InterfaceState state); /** * Set the state of this peer. */ void set_state(InterfaceState state) {_interface_state = state; } /** * Set the designated router. */ bool set_designated_router(OspfTypes::RouterID dr); /** * Set the backup designated router. */ bool set_backup_designated_router(OspfTypes::RouterID dr); /** * Called when this peer becomes the designated router or * this peer was the designated router. * * @param yes true if the peer became the DR false if it is no * longer the DR. */ void designated_router_changed(bool yes); /** * Unconditionally start the hello timer running. */ void start_hello_timer(); /** * Unconditionally stop the hello timer running. */ void stop_hello_timer(); /** * If the hello timer is already running, then stop and start the * timer. Required when the timer value is changed interactively. */ void restart_hello_timer(); void start_wait_timer(); void stop_wait_timer(); bool send_hello_packet(); OspfTypes::RouterID backup_designated_router(list& candidates) const; OspfTypes::RouterID designated_router(list& candidates, OspfTypes::RouterID backup_designated_router) const; void compute_designated_router_and_backup_designated_router(); /** * Compute the current router link for OSPFv2 * * Typically called after a state transition. */ void update_router_linksV2(list& router_links); /** * Compute the current router link for OSPFv3 * * Typically called after a state transition. */ void update_router_linksV3(list& router_links); /** * Remove neighbour state. */ void remove_neighbour_state(); /** * Stop all timers. */ void tear_down_state(); }; class RxmtWrapper; /** * Neighbour specific information. */ template class Neighbour { public: /** * The ordering is important (used in the DR and BDR election). */ enum State { Down = 1, Attempt = 2, Init = 3, TwoWay = 4, ExStart = 5, Exchange = 6, Loading = 7, Full = 8 }; static const uint32_t TIMERS = 2; // Number of timers /** * Index for timers. */ enum Timers { INITIAL = 0, // Database exchanges. FULL = 1, // LSA requests and retransmissions. }; typedef XorpCallback0::RefPtr RxmtCallback; /** * We start in Init not Down state as typically this class is * created on demand when a hello packet arrives. */ Neighbour(Ospf& ospf, Peer& peer, OspfTypes::RouterID router_id, A neighbour_address, OspfTypes::NeighbourID neighbourid, OspfTypes::LinkType linktype, State state = Init) : _ospf(ospf), _peer(peer), _router_id(router_id), _neighbour_address(neighbour_address), _neighbourid(neighbourid), _linktype(linktype), _state(state), _hello_packet(0), _last_dd(ospf.get_version()), _data_description_packet(ospf.get_version()) { // No neighbour should ever have this ID. XLOG_ASSERT(OspfTypes::ALLNEIGHBOURS != neighbourid); for (uint32_t i = 0; i < TIMERS; i++) _rxmt_wrapper[i] = 0; TimeVal t; _ospf.get_eventloop().current_time(t); // If we are debugging numbers starting from 0 are easier to // deal with. #ifdef DEBUG_LOGGING _data_description_packet.set_dd_seqno(0); #else _data_description_packet.set_dd_seqno(t.sec()); #endif _creation_time = t; } ~Neighbour() { delete _hello_packet; for (uint32_t i = 0; i < TIMERS; i++) delete _rxmt_wrapper[i]; } /** * Get neighbour ID our internal ID for each neighbour. */ OspfTypes::NeighbourID get_neighbour_id() const { return _neighbourid; } /** * Neighbours router ID. */ OspfTypes::RouterID get_router_id() const { if (_hello_packet) return _hello_packet->get_router_id(); return _router_id; } /** * Neighbour's source address. */ A get_neighbour_address() const { return _neighbour_address; } /** * @return the value that should be used for DR or BDR for this neighbour * In OSPFv2 its the source address of the interface. * In OSPFv3 its the router ID. */ OspfTypes::RouterID get_candidate_id() const { return Peer::get_candidate_id(_neighbour_address, get_router_id()); } /** * Get the state of this neighbour. */ State get_state() const { return _state; } /** * Return the authentication handler. */ Auth& get_auth_handler() { return _peer.get_auth_handler(); } /** * @return true if this routers neighbour is the DR. */ bool is_neighbour_DR() const; /** * @return true if this routers neighbour is the DR or BDR. */ bool is_neighbour_DR_or_BDR() const; /** * Should this neighbour be announced in hello packet. * * @return true if it should. */ bool announce_in_hello_packet() const { return _hello_packet; } /** * Get a copy of the last hello packet that was received. */ HelloPacket *get_hello_packet() { return _hello_packet; } /** * Get a copy of the last hello packet that was received. */ HelloPacket *get_hello_packet() const { return _hello_packet; } void event_hello_received(HelloPacket *hello); void data_description_received(DataDescriptionPacket *dd); void link_state_request_received(LinkStateRequestPacket *lsrp); void link_state_update_received(LinkStateUpdatePacket *lsup); void link_state_acknowledgement_received(LinkStateAcknowledgementPacket *lsap); /** * Send this LSA directly to the neighbour. Do not place on * retransmission list. * * @param lsar * * @return true on success */ bool send_lsa(Lsa::LsaRef lsar); /** * Queue an LSA for transmission. * * @param peer the LSA arrived on. * @param nid the LSA arrived on. * @param lsar the lsa * @param multicast_on_peer Did this LSA get multicast on this peer. * * @return true on success. */ bool queue_lsa(OspfTypes::PeerID peerid, OspfTypes::NeighbourID nid, Lsa::LsaRef lsar, bool& multicast_on_peer); /** * Send (push) any queued LSAs. */ bool push_lsas(const char* message); /** * Is this LSA on this neighbours link state request list. * * @return true if it is. */ bool on_link_state_request_list(Lsa::LsaRef lsar) const; /** * @return the link type. */ OspfTypes::LinkType get_linktype() const { return _linktype; } /** * Send acknowledgement. * * @param ack list of acknowledgements. * @param direct if true send directly to the neighbour. * @param multicast_on_peer set to true if the ack was * multicast. Only if direct is false is it possible for the * packet to be multicast. * * @return true if an acknowledgement is sent. */ bool send_ack(list& ack, bool direct, bool& multicast_on_peer); void event_kill_neighbour(); void event_adj_ok(); void event_bad_link_state_request(); /** * Pretty print the neighbour state. */ static const char* pp_state(State is); /** * Get state information about this neighbour. * * @param ninfo if neighbour is found its information. * */ bool get_neighbour_info(NeighbourInfo& ninfo) const; string str() { return "Address: " + _neighbour_address.str() + "RouterID: " + pr_id(get_router_id()); } static OspfTypes::NeighbourID _ticket; // Allocator for NeighbourID's private: Ospf& _ospf; // Reference to the controlling class. Peer& _peer; // Reference to Peer class. const OspfTypes::RouterID _router_id;// Neighbour's RouterID. const A _neighbour_address; // Neighbour's address. const OspfTypes::NeighbourID _neighbourid; // The neighbours ID. const OspfTypes::LinkType _linktype; // Type of this link. State _state; // State of this neighbour. HelloPacket *_hello_packet; // Last hello packet received // from this neighbour. DataDescriptionPacket _last_dd; // Saved state from Last DDP received. // The DDP this neighbour sends. DataDescriptionPacket _data_description_packet; bool _all_headers_sent; // Tracking database transmssion XorpTimer _rxmt_timer[TIMERS]; // Retransmit timers. RxmtWrapper *_rxmt_wrapper[TIMERS]; // Wrappers to retransmiter. DataBaseHandle _database_handle; // Handle to the Link State Database. list _ls_request_list; // Link state request list. list _lsa_queue; // Queue of LSAs waiting to be sent. list _lsa_rxmt; // Unacknowledged LSAs // awaiting retransmission. XorpTimer _inactivity_timer; // Inactivity timer. TimeVal _creation_time; // Creation time. TimeVal _adjacency_time; // Adjacency time. /** * Get the area router. */ AreaRouter *get_area_router() {return _peer.get_area_router(); } /** * Change state, use this not set_state when changing states. */ void change_state(State state); /** * Set the state of this neighbour. */ void set_state(State state) {_state = state; } /** * @return true if an adjacency should be established with this neighbour */ bool establish_adjacency_p() const; /** * @return true if this router is the DR. */ bool is_DR() const; /** * @return true if this router is the BDR. */ bool is_BDR() const; /** * @return true if this router is the DR or BDR. */ bool is_DR_or_BDR() const; /** * Start the inactivity timer. * Used to track Hello packets from the neighbour. */ void start_inactivity_timer(); /** * Stop the inactivity timer. * Used to track Hello packets from the neighbour. */ void stop_inactivity_timer(); /** * Start the retransmit timer. * * @param index defining which timer to use. * @param RxmtCallback method to be called ever retransmit interval. * @param immediate don't wait for the retransmit interval send * one now. * @param comment to track the callbacks */ void start_rxmt_timer(uint32_t index, RxmtCallback, bool immediate, const char *comment); /** * Stop the retransmit timer. * @param index defining which timer to use. * */ void stop_rxmt_timer(uint32_t index, const char *comment); /** * Ensure that the LSA retransmitter is running. * If there is an existing timer, make no changes, otherwise * start a new timer running. */ void ensure_retransmitter_running(const char* comment); /** * Retransmit link state request and link state update packets. * * @return true if there are more retransmissions to perform. */ bool retransmitter(); /** * Build database description packet. */ void build_data_description_packet(); /** * Send database description packet. */ bool send_data_description_packet(); /** * Start sending data description packets. * Should only be called in state ExStart. * * @param event_name for debugging. * @param immediate if true send the packet immediately, if false * wait the retransmit interval. */ void start_sending_data_description_packets(const char *event_name, bool immediate = true); /** * Extract the list of LSA headers for future requests from the * neighbour. * * @return false if an unknown LS type is encountered or if an * AS-External-LSA appears in a non-normal area, otherwise true. */ bool extract_lsa_headers(DataDescriptionPacket *dd); /** * Send link state request packet. */ bool send_link_state_request_packet(LinkStateRequestPacket& lsrp); /** * Send link state update packet. * @param direct if true send directly to the neighbour. */ bool send_link_state_update_packet(LinkStateUpdatePacket& lsup, bool direct = false); /** * Send link state ack packet. * @param direct if true send directly to the neighbour. * @param multicast_on_peer set to true if the packet is multicast * false otherwise. */ bool send_link_state_ack_packet(LinkStateAcknowledgementPacket& lsap, bool direct, bool& multicast_on_peer); /** * The state has just dropped so pull out any state associated * with a higher state. * * @param previous_state */ void tear_down_state(State previous_state); void event_1_way_received(); void event_2_way_received(); void event_negotiation_done(); void event_sequence_number_mismatch(); void event_exchange_done(); void event_loading_done(); void event_inactivity_timer(); /** * Common code for: * Sequence Number Mismatch and Bad Link State Request. */ void event_SequenceNumberMismatch_or_BadLSReq(const char *event_name); }; #endif // __OSPF_PEER_HH__ xorp/ospf/area_router.hh0000664000076400007640000013014111421137511015454 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/ospf/area_router.hh,v 1.141 2008/10/02 21:57:47 bms Exp $ #ifndef __OSPF_AREA_ROUTER_HH__ #define __OSPF_AREA_ROUTER_HH__ class DataBaseHandle; class LsaTempStore; /** * Area Router * */ template class AreaRouter : public ServiceBase { public: AreaRouter(Ospf& ospf, OspfTypes::AreaID area, OspfTypes::AreaType area_type); /** * Required by the class Subsystem. * Called on startup. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int startup(); /** * Required by the class Subsystem. * Called on shutdown. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int shutdown(); /** * Add peer */ void add_peer(OspfTypes::PeerID peer); /** * Delete peer */ void delete_peer(OspfTypes::PeerID peer); /** * Peer came up */ bool peer_up(OspfTypes::PeerID peer); /** * Peer went down */ bool peer_down(OspfTypes::PeerID peer); /** * Track border router transitions. * * @param up true of the router just became an area border router, * false if the router was an area border router and is no longer. */ void area_border_router_transition(bool up); /** * Change the type of this area. */ void change_area_router_type(OspfTypes::AreaType area_type); /** * Given an advertising router and type find a global address if * present in its associated Intra-Area-Prefix-LSA if present, * OSPFv3 only. * * @param adv advertising router. * @param type of Intra-Area-Prefix-LSA (Router-LSA or * Network-LSA), Router-LSA expected. * @param lsa_temp_store store of all possible Router-LSAs. * @param global_address (out) argument. * * @return true if global address found. */ bool find_global_address(uint32_t adv, uint16_t type, LsaTempStore& lsa_temp_store, A& global_address) const; /** * @return true if any virtual links are configured through this area. */ bool configured_virtual_link() const; /** * Add a virtual link endpoint. */ bool add_virtual_link(OspfTypes::RouterID rid); /** * Remove a virtual link endpoint. */ bool remove_virtual_link(OspfTypes::RouterID rid); /** * Start looking through the list of routers for a virtual link endpoint. */ void start_virtual_link(); /** * Check this node to see if its a virtual link endpoint. * * @param rc node under consideration. * @param router this router's Router-LSA. */ void check_for_virtual_linkV2(const RouteCmd& rc, Lsa::LsaRef lsar); /** * Check this node to see if its a virtual link endpoint. * * @param rc node under consideration. * @param lsar RouterLSA belonging to the router under consideration. * @param lsa_temp_store store of all possible Router-LSAs. */ void check_for_virtual_linkV3(const RouteCmd& rc, Lsa::LsaRef lsar, LsaTempStore& lsa_temp_store); /** * End looking through the list of routers for a virtual link endpoint. */ void end_virtual_link(); /** * Given two LSAs find the interface address of the destination * LSA. The source LSA can be a Router-LSA or a Network-LSA the * destination LSA must be a Router-LSA. */ bool find_interface_address(Lsa::LsaRef src, Lsa::LsaRef dst, A& interface)const; /** * OSPFv3 only. * Given a Router ID and interface ID find the associated Link-LSA * if present and return the Link-local Interface Address. */ bool find_interface_address(OspfTypes::RouterID rid, uint32_t interface_id, A& interface); /** * Add area range. */ bool area_range_add(IPNet net, bool advertise); /** * Delete area range. */ bool area_range_delete(IPNet net); /** * Change the advertised state of this area. */ bool area_range_change_state(IPNet net, bool advertise); /** * Is network covered by an area range and if it is should it be * advertised. */ bool area_range_covered(IPNet net, bool& advertise); /** * This network falls in a covered area range, return the covering * range. */ bool area_range_covering(IPNet net, IPNet& sumnet); /** * Does this area have any area ranges configured. */ bool area_range_configured(); /** * If this is a "stub" or "nssa" area toggle the sending of a default * route. */ bool originate_default_route(bool enable); /** * Set the StubDefaultCost, the default cost sent in a default route in a * "stub" or "nssa" area. */ bool stub_default_cost(uint32_t cost); /** * Toggle the sending of summaries into "stub" or "nssa" areas. */ bool summaries(bool enable); /** * get lsa at index if it exists. */ bool get_lsa(const uint32_t index, bool& valid, bool& toohigh, bool& self, vector& lsa); /** * A new set of router links. */ bool new_router_links(OspfTypes::PeerID peer, const list& router_link); /** * Refresh Router-LSA. * * Cause the generation of a new Router-LSA if necessary. * * @param timer true if called by the timer. */ void refresh_router_lsa(bool timer = false); /** * For OSPFv3 only there are no router links describing stub * networks, Intra-Area-Prefix-LSAs that reference the Router-LSA * have to be generated. */ void stub_networksV3(bool timer); /** * A new route has been added to the routing table it is being * presented to this area for possible Summary-LSA generation. * * @param area the route came from * @param net * @param rt routing entry. * @param push true if the routes are arriving as a consquence of * calling summary_push() */ void summary_announce(OspfTypes::AreaID area, IPNet net, RouteEntry& rt, bool push); /** * A route has been deleted from the routing table. It may * previously have caused a Summary-LSA which now needs to be * withdrawn. */ void summary_withdraw(OspfTypes::AreaID area, IPNet net, RouteEntry& rt); /** * A route has been replaced in the routing table. A previously * generated Summary-LSA may need to be withdrawn or replaced. */ void summary_replace(OspfTypes::AreaID area, IPNet net, RouteEntry& rt, RouteEntry& previous_rt, OspfTypes::AreaID previous_area); /** * @return true if this area should accept an AS-External-LSA or * a Type-7-LSA. */ bool external_area_type() const; /** * Copy the net and nexthop information from one AS-External-LSA to * another. * * The first dummy argument A is to allow template specialisation * by address family. */ void external_copy_net_nexthop(A, ASExternalLsa *dst, ASExternalLsa *src); /** * Given an AS-External-LSA generate a Type-7 LSA. * * @param indb if true the Type-7-LSA is already in the database. */ Lsa::LsaRef external_generate_type7(Lsa::LsaRef lsar, bool& indb); /** * Given a Type-7 LSA generate an AS-External-LSA. */ Lsa::LsaRef external_generate_external(Lsa::LsaRef lsar); /** * An AS-External-LSA being announced either from another area or * from the RIB as a redist. * * The LSAs should not be scheduled for transmission until the * external_announce_complete() is seen. In many cases a number of * LSAs may arrive in a single packet, waiting for the * external_announce_complete() offers an opportunity for * aggregation. * * @param lsar the AS-External-LSA * @param push set to true if the push is a result of an external_push(). * @param redist true if this LSA was locally generated due to a * redistribution. */ void external_announce(Lsa::LsaRef lsar, bool push, bool redist); /** * Called to complete a series of calls to external_announce(). */ void external_announce_complete(); /** * Refresh this LSA either because a timer has expired or because * a newer LSA has arrived from another area. In either cause the * LSA should already be in this area's database. */ void external_refresh(Lsa::LsaRef lsar); /** * An AS-External-LSA being withdrawn either from another area or * from the RIB as a redist. * * @param lsar the AS-External-LSA */ void external_withdraw(Lsa::LsaRef lsar); /** * Add a Link-LSA for this peer. */ bool add_link_lsa(OspfTypes::PeerID peerid, Lsa::LsaRef lsar); /** * Update the Link-LSA for this peer. */ bool update_link_lsa(OspfTypes::PeerID peerid, Lsa::LsaRef lsar); /** * Withdraw the Link-LSA for this peer. */ bool withdraw_link_lsa(OspfTypes::PeerID peerid, Lsa::LsaRef lsar); /** * Refresh the Link-LSA for this peer. */ void refresh_link_lsa(OspfTypes::PeerID peerid, Lsa::LsaRef lsar); /** * Generate a Network-LSA for this peer. */ bool generate_network_lsa(OspfTypes::PeerID peer, OspfTypes::RouterID link_state_id, list& attached_routers, uint32_t network_mask); /** * Update the Network-LSA for this peer. */ bool update_network_lsa(OspfTypes::PeerID peer, OspfTypes::RouterID link_state_id, list& attached_routers, uint32_t network_mask); /** * Withdraw the Network-LSA for this peer by prematurely aging. */ bool withdraw_network_lsa(OspfTypes::PeerID peer, OspfTypes::RouterID link_state_id); /** * Refresh the Network-LSAs. * * @param peerid the peer that needs its Network-LSA refreshed. * @param lsar the Network-LSA itself. * @param timer is the Network-LSA being refreshed due to the * timer firing? */ void refresh_network_lsa(OspfTypes::PeerID peerid, Lsa::LsaRef lsar, bool timer = false); /** * OSPFv3 only. * A new Link-LSA has arrived if this router is the designated * router then it may be necessary to generate a new * Intra-Area-Prefix-LSA. * * @return true if a new Intra-Area-Prefix-LSA needs to be generated. */ bool check_link_lsa(LinkLsa *nllsa, LinkLsa *ollsa); /** * OSPFv3 only. * This method is paired with check_link_lsa() if a new Link-LSA * has arrived that requires a new Intra-Area-Prefix-LSA then this * method should be called. */ void update_intra_area_prefix_lsa(OspfTypes::PeerID peerid); /** * Generate a Intra-Area-Prefix-LSA for this peer OSPFv3 only and * add it to the database. * * @param peerid the peer that the generated Intra-Area-Prefix-LSA * belongs to. * @param lsar the LSA that is referenced by the * Intra-Area-Prefix-LSA. * @param interface_id that the generated Intra-Area-Prefix-LSA * belongs to. */ bool generate_intra_area_prefix_lsa(OspfTypes::PeerID peerid, Lsa::LsaRef lsar, uint32_t interface_id); /** * OSPFv3 only. * Find the Link-LSA (if it exists) specified by the the tuple LS * type, Link State ID and Router ID, and add to the prefix * list. If the prefix is already on the list just or in the * options field. * * @return the options associated with this Link-LSA if present * otherwise zero. */ uint32_t populate_prefix(OspfTypes::PeerID peeridid, uint32_t interface_id, OspfTypes::RouterID router_id, list& prefixes); /** * Update the Intra-Area-Prefix-LSA for this peer OSPFv3 only. * * @param peerid the peer that needs its Intra-Area-Prefix-LSA refreshed. * @param referenced_ls_type * @param interface_id that the generated Intra-Area-Prefix-LSA * belongs to. * @param attached_routers list of fully attached routers. * * @return the options fields from all the Link-LSAs or'd together. */ uint32_t update_intra_area_prefix_lsa(OspfTypes::PeerID peer, uint16_t referenced_ls_type, OspfTypes::RouterID interface_id, const list& attached_routers); /** * Withdraw the Intra-Area-Prefix-LSA for this peer by prematurely * aging OSPFv3 only. * * @param peerid the peer that needs its Intra-Area-Prefix-LSA refreshed. * @param referenced_ls_type * @param interface_id that the generated Intra-Area-Prefix-LSA * belongs to. */ bool withdraw_intra_area_prefix_lsa(OspfTypes::PeerID peer, uint16_t referenced_ls_type, uint32_t interface_id); /** * Refresh the Intra-Area-Prefix-LSA OSPFv3 only. * * NOT IMPLEMENTED. * * @param peerid the peer that needs its Intra-Area-Prefix-LSA refreshed. * @param referenced_ls_type * @param interface_id that the generated Intra-Area-Prefix-LSA * belongs to. */ void refresh_intra_area_prefix_lsa(OspfTypes::PeerID peerid, uint16_t referenced_ls_type, uint32_t interface_id); /** * Create an LSA that will be used to announce the default route * into "stub" and "nssa" areas. */ void generate_default_route(); /** * Find the default route LSA in the database if it exists. */ bool find_default_route(size_t& index); // Temporary storage used by save_default_route() and // restore_default_route(). Lsa::LsaRef _saved_default_route; /** * If the default route LSA is in the database remove it. * Typically to stop it being purged when the area type changes or * summarisation is disable. */ void save_default_route(); /** * If the default route LSA should be in the database put it * back. Either from the previously saved or generate a new one if * necessary. Typically paired with save_default_route(). */ void restore_default_route(); /** * Withdraw the default route LSA if it exists. * Set the LSA to MaxAge and floods. */ void withdraw_default_route(); /** * Refresh the default route LSA. * Increments the sequence and floods updates the cost if it has * changed. */ void refresh_default_route(); /** * Add a network to be announced. */ // void add_network(OspfTypes::PeerID peer, IPNet network); /** * Remove a network to be announced. */ // void remove_network(OspfTypes::PeerID peer, IPNet network); /** * @return the type of this area. */ OspfTypes::AreaType get_area_type() const { return _area_type; } /** * Get the options that are sent in hello packets, data * description packets, LSA headers (OSPFv2), Router-LSAs * (OSPFv3) and Network-LSAs (OSPFv3). */ uint32_t get_options() { return _ospf.get_peer_manager().compute_options(get_area_type()); } /** * Receive LSAs * * @param peerid that the LSAs arrived on. * @param nid neighbourID that the LSAs arrived on. * @param lsas list of recived lsas. * @param direct_ack list of direct acks to send in response to the LSA * @param delayed_ack list of delayed acks to send in response to the LSA * @param is_router_dr true if the router is the designated router. * @param is_router_bdr true if the receiving interface was in * state backup. * @param is_neighbour_dr true if the LSA was received from the * designated router. */ void receive_lsas(OspfTypes::PeerID peerid, OspfTypes::NeighbourID nid, list& lsas, list& direct_ack, list& delayed_ack, bool is_router_dr, bool is_router_bdr, bool is_neighbour_dr); /** * Returned by compare_lsa. */ enum LsaSearch { NOMATCH, // No matching LSA was found. EQUIVALENT, // The two LSAs are considered equivalent. NEWER, // The offered LSA is newer than the database copy. OLDER, // The offered LSA is older than the database copy. }; /** * Compare two LSAs. * * @param candidate offered LSA * @param current equivalent to the database copy. * * @return LsaSearch that describes the type of match. */ LsaSearch compare_lsa(const Lsa_header& candidate, const Lsa_header& current) const; /** * Compare this LSA to * * @param Lsa_header that is being sought. * * @return LsaSearch that describes the type of match. */ LsaSearch compare_lsa(const Lsa_header&) const; /** * @return true if this is a newer LSA than we already have. */ bool newer_lsa(const Lsa_header&) const; /** * Fetch a list of lsas given a list of requests. * * The age fields of the returned LSAs will be correctly set. * * @param requests list of requests * @param lsas list of LSAs * * @return True if *all* the requests have been satisfied. If an LSA * can not be found False is returned and the state of the lsas * list is undefined; hence should not be used. * */ bool get_lsas(const list& requests, list& lsas); /** * Open database * * Used only by the peer to generate the database description packets. * * @param peerid * @param empty true if the database is empty. * @return Database Handle */ DataBaseHandle open_database(OspfTypes::PeerID peerid, bool& empty); /** * Is this a valid entry to be returned by the database. * * This method is for internal use and its use is not recommended. * * @return true if this entry is valid. */ bool valid_entry_database(OspfTypes::PeerID peerid, size_t index); /** * Is there another database entry following this one. * * This method is for internal use and its use is not recommended. * * @return true if there is a subsequent entry. */ bool subsequent(DataBaseHandle& dbh); /** * Next database entry * * @param last true if this is the last entry. * * @return The next LSA in the database. */ Lsa::LsaRef get_entry_database(DataBaseHandle& dbh, bool& last); /** * Close the database * * @param dbd Database descriptor */ void close_database(DataBaseHandle& dbh); /** * Clear the database. * * @param preserve_link_lsas if true when clearing the database * don't remove Link-LSAs that were generated by this * router. Relevant to OSPFv3 only. */ void clear_database(bool preserve_link_lsas = false); /** * All self originated LSAs of this type MaxAge them. */ void maxage_type_database(uint16_t type); /** * Is this the backbone area? */ bool backbone() const { return OspfTypes::BACKBONE == _area; } bool backbone(OspfTypes::AreaID area) const { return OspfTypes::BACKBONE == area; } #ifndef UNFINISHED_INCREMENTAL_UPDATE bool get_transit_capability() const { return _TransitCapability; } #endif /** * Totally recompute the routing table from the LSA database. */ void routing_total_recompute(); /** * Testing entry point to force a total routing computation. */ void testing_routing_total_recompute() { routing_total_recompute(); } /** * Print link state database. */ void testing_print_link_state_database() const; /** * Testing entry point to add this router Router-LSA to the * database replacing the one that is already there. */ bool testing_replace_router_lsa(Lsa::LsaRef lsar) { RouterLsa *rlsa = dynamic_cast(lsar.get()); XLOG_ASSERT(rlsa); XLOG_ASSERT(rlsa->get_self_originating()); switch (_ospf.get_version()) { case OspfTypes::V2: XLOG_ASSERT(_ospf.get_router_id() == rlsa->get_header().get_link_state_id()); break; case OspfTypes::V3: break; } XLOG_ASSERT(_ospf.get_router_id() == rlsa->get_header().get_advertising_router()); size_t index; if (find_lsa(_router_lsa, index)) { delete_lsa(_router_lsa, index, true); } _router_lsa = lsar; add_lsa(_router_lsa); return true; } /** * Testing entry point to add an LSA to the database. */ bool testing_add_lsa(Lsa::LsaRef lsar) { return add_lsa(lsar); } /** * Testing entry point to delete an LSA from the database. */ bool testing_delete_lsa(Lsa::LsaRef lsar) { size_t index; if (find_lsa(lsar, index)) { delete_lsa(lsar, index, true); return true; } XLOG_FATAL("Attempt to delete LSA that is not in database \n%s", cstring(*lsar)); return false; } string str() { return "Area " + pr_id(_area); } private: Ospf& _ospf; // Reference to the controlling class. OspfTypes::AreaID _area; // Area: That is represented. OspfTypes::AreaType _area_type; // Type of this area. map _vlinks; // Virtual link endpoints. set _tmp; // temporary storage for // virtual links. bool _summaries; // True if summaries should be // generated into a stub area. bool _stub_default_announce; // Announce a default route into // stub or nssa uint32_t _stub_default_cost; // The cost of the default // route that is injected into // a stub area. bool _external_flooding; // True if AS-External-LSAs // are being flooded. #ifdef UNFINISHED_INCREMENTAL_UPDATE Spt _spt; // SPT computation unit. #endif Lsa::LsaRef _invalid_lsa; // An invalid LSA to overwrite slots Lsa::LsaRef _router_lsa; // This routers router LSA. vector _db; // Database of LSAs. deque _empty_slots; // Available slots in the Database. uint32_t _last_entry; // One past last entry in // database. A value of 0 is // an empty database. uint32_t _allocated_entries; // Number of allocated entries. uint32_t _readers; // Number of database readers. DelayQueue _queue; // Router LSA queue. XorpTimer _reincarnate_timer; // Wait for the LSAs to be purged. list _reincarnate; // Self-originated LSAs where // the sequence number has // reached MaxSequenceNumber. uint32_t _lsid; // OSPFv3 only next Link State ID. map, uint32_t> _lsmap; // OSPFv3 only #ifdef UNFINISHED_INCREMENTAL_UPDATE uint32_t _TransitCapability; // Used by the spt computation. // XXX - This needs a better name. struct Bucket { Bucket(size_t index, bool known) : _index(index), _known(known) {} size_t _index; bool _known; }; list _new_lsas; // Indexes of new LSAs that // have arrived recently. #else bool _TransitCapability; // Does this area support // transit traffic? void set_transit_capability(bool t) { _TransitCapability = t; } #endif /** * Internal state that is required about each peer. */ struct PeerState { PeerState() : _up(false) {} bool _up; // True if peer is enabled. list _router_links; // Router links for this peer }; typedef ref_ptr PeerStateRef; typedef map PeerMap; PeerMap _peers; // Peers of this area. uint32_t _routing_recompute_delay; // How many seconds to wait // before recompting. XorpTimer _routing_recompute_timer; // Timer to cause recompute. // How to handle Type-7 LSAs at the border. OspfTypes::NSSATranslatorRole _translator_role; OspfTypes::NSSATranslatorState _translator_state; bool _type7_propagate; // How to set the propagate bit. /** * Range to be summarised or suppressed from other areas. */ struct Range { bool _advertise; // Should this range be advertised. }; Trie _area_range; // Area range for summary generation. /** * Networks with same network number but different prefix lengths * can generate the same link state ID. When generating a new LSA * if a collision occurs use: * RFC 2328 Appendix E. An algorithm for assigning Link State IDs * to resolve the clash. Summary-LSAs only. */ void unique_link_state_id(Lsa::LsaRef lsar); /** * Networks with same network number but different prefix lengths * can generate the same link state ID. When looking for an LSA * make sure that there the lsar that matches the net is * found. Summary-LSAs only. */ bool unique_find_lsa(Lsa::LsaRef lsar, const IPNet& net, size_t& index); /** * Set network and link state ID in a Summary-LSA/Inter-Area-Prefix-LSA. */ void summary_network_lsa_set_net_lsid(SummaryNetworkLsa *snlsa, IPNet net); Lsa::LsaRef summary_network_lsa(IPNet net, RouteEntry& rt); /** * Generate a Summary-LSA for an intra area path taking into * account area ranges. An announcement may not generate an LSA * and a withdraw may not cause an LSA to be removed. */ Lsa::LsaRef summary_network_lsa_intra_area(OspfTypes::AreaID area, IPNet net, RouteEntry& rt, bool& announce); /** * Construct a summary LSA if appropriate. * @param announce true if this in an announce false if its a withdraw. * @return the LSA can be empty. */ Lsa::LsaRef summary_build(OspfTypes::AreaID area, IPNet net, RouteEntry& rt, bool& announce); /** * Announce this Summary-LSA to all neighbours and refresh it as * appropriate. */ void refresh_summary_lsa(Lsa::LsaRef lsar); /** * Start aging LSA. * * All non self originated LSAs must be aged and when they reach * MAXAGE flooded and flushed. */ bool age_lsa(Lsa::LsaRef lsar); /** * This LSA has reached MAXAGE so flood it to all the peers of * this area. If its an external LSA flood it to all areas. */ void maxage_reached(Lsa::LsaRef lsar, size_t index); /** * Prematurely age self originated LSAs, remove them from the * database flood with age set to MAXAGE. */ void premature_aging(Lsa::LsaRef lsar, size_t index); /** * Increment the sequence number of of this LSA, most importantly * handle the sequence number reaching MaxSequenceNumber. */ void increment_sequence_number(Lsa::LsaRef lsar); /** * Update the age and increment the sequence number of of this * LSA, most importantly handle the sequence number reaching * MaxSequenceNumber. */ void update_age_and_seqno(Lsa::LsaRef lsar, const TimeVal& now); /** * Process an LSA where the sequence number has reached MaxSequenceNumber. */ void max_sequence_number_reached(Lsa::LsaRef lsar); /** * Reincarnate LSAs that have gone through the MaxSequenceNumber * transition. Called on a periodic timer. */ bool reincarnate(); /** * Add this LSA to the database. * * The LSA must have an updated age field. * * @param lsar LSA to add. * @return true on success */ bool add_lsa(Lsa::LsaRef lsar); /** * Delete this LSA from the database. * * @param lsar LSA to delete. * @param index into database. * @param invalidate if true as well as removing from the database * mark the LSA as invalid. * @return true on success */ bool delete_lsa(Lsa::LsaRef lsar, size_t index, bool invalidate); /** * Update this LSA in the database. * * @param lsar LSA to update. * @param index into database. * @return true on success */ bool update_lsa(Lsa::LsaRef lsar, size_t index); /** * Find LSA matching this request. * * @param lsr that is being sought. * @param index into LSA database if search succeeded. * * @return true if an LSA was found. */ bool find_lsa(const Ls_request& lsr, size_t& index) const; /** * Find LSA matching this request. * * @param lsar that is being sought. * @param index into LSA database if search succeeded. * * @return true if an LSA was found. */ bool find_lsa(Lsa::LsaRef lsar, size_t& index) const; /** * Find Network-LSA. * * @param link_state_id * @param index into LSA database if search succeeded. * * @return true if an LSA was found. */ bool find_network_lsa(uint32_t link_state_id, size_t& index) const; /** * Find Router-LSA. OSPFv3 only * * In OSPFv3 a router may generate more that one Router-LSA. In * order to find all the Router-LSAs that a router may have * generated the index must be seeded. * * @param advertising_router * @param index into LSA database if search succeeded. On input * the index should be set to one past the last search result. * * @return true if an LSA was found. */ bool find_router_lsa(uint32_t advertising_router, size_t& index) const; /** * Compare this LSA to * * @param Lsa_header that is being sought. * @param index into LSA database if search succeeded. * * @return LsaSearch that describes the type of match. */ LsaSearch compare_lsa(const Lsa_header&, size_t& index) const; /** * Update router links. * * A peer has just changed state, or the refresh timer has fired, * so update the router lsa and publish. * * @return true if something changed. */ bool update_router_links(); /* * Send this LSA to all our peers. * * @param peerid The peer this LSA arrived on. * @param nid The neighbour this LSA arrived on so don't reflect. * @param lsar The LSA to publish * @param multicast_on_peer Did this LSA get multicast on this peer. */ void publish(const OspfTypes::PeerID peerid, const OspfTypes::NeighbourID nid, Lsa::LsaRef lsar, bool& multicast_on_peer) const; /* * Send this LSA to all our peers. * * @param lsar The LSA to publish */ void publish_all(Lsa::LsaRef lsar); /** * Send (push) any queued LSAs. */ void push_lsas(const char* msg); /** * Return the setting of the propagate bit in a Type-7-LSA. */ bool external_propagate_bit(Lsa::LsaRef lsar) const { XLOG_ASSERT(lsar->type7()); return Options(_ospf.get_version(),lsar->get_header().get_options()) .get_p_bit(); } /** * Take a Type-7-LSA that has arrived on the wire and translate if * required. */ void external_type7_translate(Lsa::LsaRef lsar); /** * Send this LSA to all area's. * * This is an AS-External-LSA being sent to other areas. * * @param lsar The LSA to publish */ void external_flood_all_areas(Lsa::LsaRef lsar); /** * Notify all areas this is the last of the AS-External-LSAs. */ void external_push_all_areas(); /** * @return true if any of the neigbours are in state Exchange or Loading. */ bool neighbours_exchange_or_loading() const; /** * @param rid Router ID of neighbouring router. * * @return true if this router is at least twoway with the * specified router. */ bool neighbour_at_least_two_way(OspfTypes::RouterID rid) const; /** * OSPFv3 Only * Neighbour's source address. * * @param rid Router ID * @param interface_id Interface ID. * @param neighbour_address set if neighbour is found. * * @return true if the neighbour is found. */ bool get_neighbour_address(OspfTypes::RouterID rid, uint32_t interface_id, A& neighbour_address) const; /** * Is this LSA on this neighbours link state request list. * @param peerid * @param nid * * @return true if it is. */ bool on_link_state_request_list(const OspfTypes::PeerID peerid, const OspfTypes::NeighbourID nid, Lsa::LsaRef lsar) const; /** * Generate a BadLSReq event. * * @param peerid * @param nid */ bool event_bad_link_state_request(const OspfTypes::PeerID peerid, const OspfTypes::NeighbourID nid) const; /** * Send this LSA directly to the neighbour. Do not place on * retransmission list. * * @param peerid * @param nid * @param lsar * * @return true on success */ bool send_lsa(const OspfTypes::PeerID peerid, const OspfTypes::NeighbourID nid, Lsa::LsaRef lsar) const; /** * Check this LSA to see if its link state id matched any of the * routers interfaces. * * @param lsar LSA to check. * A dummy argument is used to force an IPv4 and an IPv6 instance * of this method to be generated. Isn't C++ cool? */ bool self_originated_by_interface(Lsa::LsaRef lsar, A = A::ZERO()) const; /** * If this is a self-originated LSA do the appropriate processing. * RFC 2328 Section 13.4. Receiving self-originated LSAs * * @param lsar received LSA. * @param match if true this LSA has matched a self-originated LSA * already in the database. * @param index if match is true the index into the database for * the matching LSA. * * @return true if this is a self-orignated LSA. */ bool self_originated(Lsa::LsaRef lsar, bool match, size_t index); /** * Create a vertex for this router. */ void RouterVertex(Vertex& v); /** * Prepare for routing changes. */ void routing_begin(); /** * Add this LSA to the routing computation. * * The calls to this method are bracketed between a call to * routing begin and routing end. * * @param lsar LSA to be added to the database. * @param known true if this LSA is already in the database. */ void routing_add(Lsa::LsaRef lsar, bool known); /** * Remove this LSA from the routing computation. * * The calls to this method are *NOT* bracketed between a call to * routing begin and routing end. */ void routing_delete(Lsa::LsaRef lsar); /** * Routing changes are completed. * 1) Update the routing table if necessary. * 2) Possibly generate new summary LSAs. */ void routing_end(); /** * Schedule recomputing the whole table. */ void routing_schedule_total_recompute(); /** * Callback routine that causes route recomputation. */ void routing_timer(); /** * Totally recompute the routing table from the LSA database. */ void routing_total_recomputeV2(); void routing_total_recomputeV3(); /** * Add an entry to the routing table making sure that an entry * doesn't already exist. */ void routing_table_add_entry(RoutingTable& routing_table, IPNet net, RouteEntry& route_entry, const char* msg); /** * Compute the discard routes related to area ranges. */ void routing_area_ranges(list >& r); void routing_area_rangesV2(const list >& r); void routing_area_rangesV3(const list >& r, LsaTempStore& lsa_temp_store); /** * Compute the inter-area routes. */ void routing_inter_area(); void routing_inter_areaV2(); void routing_inter_areaV3(); /** * Compute the transit area routes. */ void routing_transit_area(); void routing_transit_areaV2(); void routing_transit_areaV3(); /** * Compute the AS external routes. */ void routing_as_external(); void routing_as_externalV2(); void routing_as_externalV3(); #if 0 /** * Given a Router-LSA and the list of Intra-Area-Prefix-LSAs * generated by the same router, return the list of prefixes that * are associated with the Router-LSA. * * @param rlsa Router-LSA * @param iapl List of Intra-Area-Prefix-LSAs same router as the Router-LSA * @param prefixes (out argument) List if prefixes associated with * the Router-LSA. */ bool associated_prefixesV3(const RouterLsa *rlsa, const list& iapl, list& prefixes); /** * Given a Network-LSA and the list of Intra-Area-Prefix-LSAs * generated by the same router, return the list of prefixes that * are associated with the Network-LSA. * * @param nlsa Network-LSA * @param iapl List of Intra-Area-Prefix-LSAs same router as the * Network-LSA * @param prefixes (out argument) List if prefixes associated with * the Network-LSA. */ bool associated_prefixesV3(NetworkLsa *nlsa, const list& iapl, list& prefixes); #endif /** * Given a LS type and a referenced link state ID and the list of * Intra-Area-Prefix-LSAs generated by the same router, return the * list of prefixes that are associated with the LSA. * * @param ls_type From Router-LSA or Network-LSA * @param referenced_link_state_id zero for a Router-LSA and the * link state ID from a Network-LSA (in fact its interface ID). * @param iapl List of Intra-Area-Prefix-LSAs same router as the Router-LSA * @param prefixes (out argument) List if prefixes associated with LSA */ bool associated_prefixesV3(uint16_t ls_type, uint32_t referenced_link_state_id, const list& lsai, list& prefixes) const; /** * RFC 3101 Section 2.5. (6) (e) Calculating Type-7 AS external routes. * * Return true if the current LSA should be replaced by the * candidate LSA. */ bool routing_compare_externals(Lsa::LsaRef current, Lsa::LsaRef candidate) const; /** * Does this Router-LSA point back to the router link that points * at it. * * @param rl_type type of link p2p or vlink * @param link_state_id from RouterLSA that points at rlsa. * @param rl link from RouterLSA that points at rlsa. * @param rlsa that is pointed at by previous two arguments. * @param metric (out argument) from rlsa back to link_state_id if the back * pointer exists. * @param interface_address (out argument) if the back pointer exists. * * @return true if the back pointer exists also fill in the metric * and interface address. * value. */ bool bidirectionalV2(RouterLink::Type rl_type, const uint32_t link_state_id, const RouterLink& rl, RouterLsa *rlsa, uint16_t& metric, uint32_t& interface_address); /** * Does this Network-LSA point back to the router link that points * at it. * * @param link_state_id_or_adv OSPFv2 Link State ID, OSPFv3 * Advertising Router. * */ bool bidirectional(const uint32_t link_state_id_or_adv, const RouterLink& rl, NetworkLsa *nlsa) const; /** * Does the Router-LSA point at the Network-LSA that points at it. * * @param interface_address (out argument) if the back pointer exists. * * @return true if Router-LSA points at the Network-LSA. */ bool bidirectionalV2(RouterLsa *rlsa, NetworkLsa *nlsa, uint32_t& interface_address); /** * Does the Router-LSA point at the Network-LSA that points at it, * OSPFv3. * * @param interface_id (out argument) interface ID if * the Router-LSA points at the Network-LSA. * * @return true if Router-LSA points at the Network-LSA. */ bool bidirectionalV3(RouterLsa *rlsa, NetworkLsa *nlsa, uint32_t& interface_id); /** * Does this Router-LSA point back to the router link that points * at it, OSPFv3. * * @param rl_type type of link p2p or vlink * @param advertising_router * @param rlsa that is searched for a router link pointing at the * advertising router. * @param metric (out argument) from rlsa back to advertising * router if the back pointer exists. * * @return true if the Router-LSA points to the advertising router. */ bool bidirectionalV3(RouterLink::Type rl_type, uint32_t advertising_router, RouterLsa *rlsa, uint16_t& metric); /** * Add this newly arrived or changed Router-LSA to the SPT. */ void routing_router_lsaV2(Spt& spt, const Vertex& src, RouterLsa *rlsa); void routing_router_link_p2p_vlinkV2(Spt& spt, const Vertex& src, RouterLsa *rlsa, RouterLink rl); void routing_router_link_transitV2(Spt& spt, const Vertex& src, RouterLsa *rlsa, RouterLink rl); void routing_router_link_stubV2(Spt& spt, const Vertex& src, RouterLsa *rlsa, RouterLink rl); /** * Add this newly arrived or changed Router-LSA to the SPT. */ void routing_router_lsaV3(Spt& spt, const Vertex& src, RouterLsa *rlsa); void routing_router_link_p2p_vlinkV3(Spt& spt, const Vertex& src, RouterLsa *rlsa, RouterLink rl); void routing_router_link_transitV3(Spt& spt, const Vertex& src, RouterLsa *rlsa, RouterLink rl); }; /** * DataBase Handle * * Holds state regarding position in the database */ class DataBaseHandle { public: DataBaseHandle() : _position(0), _last_entry(0), _valid(false), _peerid(OspfTypes::ALLPEERS) {} DataBaseHandle(bool v, uint32_t last_entry, OspfTypes::PeerID peerid) : _position(0), _last_entry(last_entry), _valid(v), _peerid(peerid) {} uint32_t position() const { XLOG_ASSERT(valid()); return _position; } uint32_t last() const { XLOG_ASSERT(valid()); return _last_entry; } void advance(bool& last) { XLOG_ASSERT(valid()); XLOG_ASSERT(_last_entry != _position); _position++; last = _last_entry == _position; } bool valid() const { return _valid; } void invalidate() { _valid = false; } OspfTypes::PeerID get_peerid() const { return _peerid; } private: uint32_t _position; // Position in database. uint32_t _last_entry; // One past last entry, for an empty // database value would be 0. bool _valid; // True if this handle is valid. OspfTypes::PeerID _peerid; // The Peer ID that opened the database. }; /** * Router-LSA and Intra-Area-Prefix-LSA store. * * In OSPFv3 a router can generate multiple Router-LSAs and * Intra-Area-Prefix-LSAs, hold a store of the LSAs indexed by router * ID. * NOTE: LsaTempStore should only be used as temporary storage during * the routing computation. */ class LsaTempStore { public: void add_router_lsa(Lsa::LsaRef lsar) { _router_lsas[lsar->get_header().get_advertising_router()]. push_back(lsar); } list& get_router_lsas(OspfTypes::RouterID rid) { return _router_lsas[rid]; } void add_intra_area_prefix_lsa(IntraAreaPrefixLsa *iaplsa) { XLOG_ASSERT(iaplsa); _intra_area_prefix_lsas[iaplsa->get_header().get_advertising_router()]. push_back(iaplsa); } list& get_intra_area_prefix_lsas(OspfTypes::RouterID rid) { return _intra_area_prefix_lsas[rid]; } private: map > _router_lsas; map > _intra_area_prefix_lsas; }; #endif // __OSPF_AREA_ROUTER_HH__ xorp/ospf/routing_table.hh0000664000076400007640000003663511703345405016025 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2012 XORP, Inc and Others-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/ospf/routing_table.hh,v 1.49 2008/11/21 00:32:11 atanu Exp $ #ifndef __OSPF_ROUTING_TABLE_HH__ #define __OSPF_ROUTING_TABLE_HH__ #include /** * External view of a routing entry. */ template class RouteEntry { public: /** * The ordering is important used to select the best route. */ enum PathType { intra_area = 1, inter_area = 2, type1 = 3, type2 = 4 }; RouteEntry() : _destination_type(OspfTypes::Router), _discard(false), _direct(false), _address(0), _id(0), _area_border_router(false), _as_boundary_router(false), _area(0), _path_type(intra_area), _cost(0), _type_2_cost(0), _nexthop(A::ZERO()), _nexthop_id(OspfTypes::UNUSED_INTERFACE_ID), _advertising_router(0), _filtered(false) {} void set_destination_type(OspfTypes::VertexType destination_type) { _destination_type = destination_type; } OspfTypes::VertexType get_destination_type() const { return _destination_type; } void set_discard(bool discard) { _discard = discard; } bool get_discard() const { return _discard; } void set_directly_connected(bool direct) { _direct = direct; } bool get_directly_connected() const { return _direct; } void set_address(uint32_t address) { XLOG_ASSERT(OspfTypes::Network == _destination_type); _address = address; } uint32_t get_address() const { XLOG_ASSERT(OspfTypes::Network == _destination_type); return _address; } void set_router_id(OspfTypes::RouterID id) { XLOG_ASSERT(OspfTypes::Router == _destination_type); _id = id; } OspfTypes::RouterID get_router_id() const { XLOG_ASSERT(OspfTypes::Router == _destination_type); return _id; } void set_area_border_router(bool area_border_router) { XLOG_ASSERT(OspfTypes::Router == _destination_type); _area_border_router = area_border_router; } bool get_area_border_router() const { XLOG_ASSERT(OspfTypes::Router == _destination_type); return _area_border_router; } void set_as_boundary_router(bool as_boundary_router) { XLOG_ASSERT(OspfTypes::Router == _destination_type); _as_boundary_router = as_boundary_router; } bool get_as_boundary_router() const { XLOG_ASSERT(OspfTypes::Router == _destination_type); return _as_boundary_router; } void set_area(OspfTypes::AreaID area) { _area = area; } OspfTypes::AreaID get_area() const { return _area; } void set_path_type(PathType path_type) { _path_type = path_type; } PathType get_path_type() const { return _path_type; } void set_cost(uint32_t cost) { _cost = cost; } uint32_t get_cost() const { return _cost; } void set_type_2_cost(uint32_t type_2_cost) { _type_2_cost = type_2_cost; } uint32_t get_type_2_cost() const { return _type_2_cost; } void set_nexthop(A nexthop) { _nexthop = nexthop; } A get_nexthop() const { return _nexthop; } void set_nexthop_id(uint32_t nexthop_id) { _nexthop_id = nexthop_id; } uint32_t get_nexthop_id() { return _nexthop_id; } void set_advertising_router(uint32_t advertising_router) { _advertising_router = advertising_router; } uint32_t get_advertising_router() const { return _advertising_router; } void set_lsa(Lsa::LsaRef lsar) { _lsar = lsar; } Lsa::LsaRef get_lsa() const { return _lsar; } void set_filtered(bool filtered) { _filtered = filtered; } bool get_filtered() const { return _filtered; } string str() { string output; output = c_format("RouteEntry: "); output += c_format("%s ", OspfTypes::Router == _destination_type ? "Router" : "Network"); output += c_format("%s", _discard ? "discard " : ""); output += c_format("%s", _direct ? "direct " : ""); if (OspfTypes::Network == _destination_type) output += c_format("\nAddress %s ", pr_id(_address).c_str()); if (OspfTypes::Router == _destination_type) { output += c_format("\nRouter ID %s ", pr_id(_id).c_str()); if (_area_border_router) output += c_format("ABR "); if (_as_boundary_router) output += c_format("ASBR "); } output += c_format("\nArea %s ", pr_id(_area).c_str()); switch(_path_type) { case intra_area: output += c_format("\nintra area cost %d ", _cost); break; case inter_area: output += c_format("\ninter area %d ", _cost); break; case type1: output += c_format("\ntype1 %d ", _cost); break; case type2: output += c_format("\ntype2 %d ", _type_2_cost); break; } output += c_format("\nnexthop %s ", cstring(_nexthop)); output += c_format("\nadvertising router %s ", pr_id(_advertising_router).c_str()); output += c_format("\n%s ", cstring(*_lsar)); return output; } private: OspfTypes::VertexType _destination_type; bool _discard; // True if this is a discard route. bool _direct; // True if directly connected, // this route should not be // sent to the RIB. uint32_t _address; // If dest type is Network OspfTypes:: RouterID _id; // If dest type is Router // The router that is referred too. // For a Router-LSA this is // the LSA itself. For Summary // LSA that refers to a router // (Type 4) its the AS // boundary router. bool _area_border_router; // Only valid if dest type is router bool _as_boundary_router; // Only valid if dest type is router OspfTypes::AreaID _area; // Associated area. PathType _path_type; uint32_t _cost; uint32_t _type_2_cost; A _nexthop; uint32_t _nexthop_id; uint32_t _advertising_router; // The router ID from the LSA // that generated this route. Lsa::LsaRef _lsar; // LSA that contributed to this route. // Currently only used for debugging. bool _filtered; // True if this route has been // filtered by policy. }; /** * Internal routing entry, potentially one per area. */ template class InternalRouteEntry { public: InternalRouteEntry() : _winner(0) {} InternalRouteEntry(const InternalRouteEntry& rhs) { copy(rhs); } InternalRouteEntry& operator=(const InternalRouteEntry& rhs) { if(&rhs == this) return *this; copy(rhs); return *this; } void copy(const InternalRouteEntry& rhs) { _entries = rhs._entries; reset_winner(); } /** * Add entry indexed by area. * @return true on sucess. */ bool add_entry(OspfTypes::AreaID area, const RouteEntry& rt); bool replace_entry(OspfTypes::AreaID area, const RouteEntry& rt); /** * Delete entry. Perfectly safe to attempt to delete an entry for * an area even if no entry exists. * * @param area area ID. * @param winner_changed out parameter set to true if this entry * was the winner. * @return true if an entry was deleted. */ bool delete_entry(OspfTypes::AreaID, bool& winner_changed); /** * Get the winning entry. */ RouteEntry& get_entry() const; /** * Look from an entry for this specific area. * * @param area area ID. * @param rt if a match is found this is filled in with the route. * * @return true if an entry for this area was found. */ bool get_entry(OspfTypes::AreaID area, RouteEntry& rt) const; /** * Are there any entries? */ bool empty() const { return 0 == _winner; } string str(); private: RouteEntry *_winner; // Winning route. map > _entries; // Routes by area. bool reset_winner(); }; /** * Storage for routing table entries indexed by advertising router. */ template class Adv { public: /** * Clear all entries for this area. */ void clear_area(OspfTypes::AreaID area); /** * Add an entry for this routing entry keyed on advertising router * * @param area to add entry. * @param adv advertising router. * @param rt associated routing entry. */ bool add_entry(OspfTypes::AreaID area, uint32_t adv, const RouteEntry& rt, const char* dbg); /** * Replace entry with new entry keyed on advertising router. * * @param area to add entry. * @param adv advertising router. * @param rt associated routing entry. */ bool replace_entry(OspfTypes::AreaID area, uint32_t adv, const RouteEntry& rt, const char* dbg); /** * Lookup an entry by advertising router. * * @param area to look in. * @param adv router to look for. * @param rt routing entry returned if entry is found. * * @return true if entry is found */ bool lookup_entry(OspfTypes::AreaID area, uint32_t adv, RouteEntry& rt) const; private: typedef map > AREA; typedef map ADV; ADV _adv; }; template class RoutingTable { public: RoutingTable(Ospf &ospf) : _ospf(ospf), _in_transaction(false), _current(0), _previous(0) {} ~RoutingTable() { delete _current; delete _previous; _current = _previous = 0; } /** * Before [add|replace|delete]_entry can be called, this method * must be called to start the transaction. */ void begin(OspfTypes::AreaID area); bool add_entry(OspfTypes::AreaID area, IPNet net, const RouteEntry& rt, const char* message); bool replace_entry(OspfTypes::AreaID area, IPNet net, const RouteEntry& rt); /** * Delete an entry from the current table. */ bool delete_entry(OspfTypes::AreaID area, IPNet net); /** * For the [add|replace|delete]_entry calls to take effect this * method must be called. */ void end(); /** * Lookup address A in the routing table exact match. * * @param router address being looked up. * @param rt if a match is found fill this in. * * @return true if an entry is found. */ bool lookup_entry(A router, RouteEntry& rt); /** * Lookup address A in the routing table exact match in specified area. * * @param area area ID. * @param router address being looked up. * @param rt if a match is found fill this in. * * @return true if an entry is found. */ bool lookup_entry(OspfTypes::AreaID area, A router, RouteEntry& rt); /** * Lookup network in the routing table exact match. * * @param network address being looked up. * @param rt if a match is found fill this in. * * @return true if an entry is found. */ bool lookup_entry(IPNet net, RouteEntry& rt); /** * Lookup network in the routing table exact match. * * @param area area ID. * @param network address being looked up. * @param rt if a match is found fill this in. * * @return true if an entry is found. */ bool lookup_entry(OspfTypes::AreaID area, IPNet net, RouteEntry& rt); /** * Lookup advertising router in specific area. * * @param area area ID. * @param adv advertsing router * @param rt if a match is found fill this in. * * @return true if an entry is found. */ bool lookup_entry_by_advertising_router(OspfTypes::AreaID area, uint32_t adv, RouteEntry& rt); /** * Lookup address A in the routing table longest match. * * @param nexthop address to lookup. * @param rt if a match is found fill this in. * * @return true if an entry is found. */ bool longest_match_entry(A router, RouteEntry& rt); /** * This call notifies the routing table that this area no longer * exists, therefore all routes that came from this area should be * removed. All other areas also need to be notified so that any * summarisation information can be removed. */ void remove_area(OspfTypes::AreaID area); /** * Re-run the policy filters on all routes. */ void push_routes(); private: Ospf& _ospf; // Reference to the controlling class. bool _in_transaction; // Flag to verify that the // routing table is only // manipulated during a transaction. Adv _adv; // Routing entries indexed by // advertising router. Trie > *_current; Trie > *_previous; // Yes the RouteEntry contains the area, nexthop and metric but they // are functionally distinct. bool add_route(OspfTypes::AreaID area, IPNet net, A nexthop, uint32_t metric, RouteEntry& rt, bool summaries); bool delete_route(OspfTypes::AreaID area, IPNet net, RouteEntry& rt, bool summaries); bool replace_route(OspfTypes::AreaID area, IPNet net, A nexthop, uint32_t metric, RouteEntry& rt, RouteEntry& previous_rt, OspfTypes::AreaID previous_area); /** * @return true if the route should be accepted. */ bool do_filtering(IPNet& net, A& nexthop, uint32_t& metric, RouteEntry& rt, PolicyTags& policytags); }; #if 0 template class RouteEntry { public: list _nexthops; // Possible nexthops for this route bool _discard; // True if this is a discard route. uint32_t _users; // Only valid if this is a discard // route. The number of routes below // the discard route. OspfTypes::AreaID _area; // Area the LSA came from. list lsars; // LSAs the routes came from. bool _modified; // True if this route has been // modified in the last sweep. }; template class RoutingTable { public: RoutingTable(Ospf &ospf) : _ospf(ospf) {} void begin(); /** * Add route * * @param net network * @param nexthop * @param metric to network * @param equal true if this in another route to the same destination. */ bool add_route(IPNet net, A nexthop, uint32_t metric, bool equal); /** * Replace route * * @param net network * @param nexthop * @param metric to network * @param equal true if this in another route to the same destination. */ bool replace_route(IPNet net, A nexthop, uint32_t metric, bool equal); /** * Delete route */ bool delete_route(IPNet net); void end(); private: Ospf& _ospf; // Reference to the controlling class. Trie > _trie; }; #endif #endif // __OSPF_ROUTING_TABLE_HH__ xorp/ospf/xrl_target.hh0000664000076400007640000003673311540225531015335 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/ospf/xrl_target.hh,v 1.46 2008/10/02 21:57:50 bms Exp $ #ifndef __OSPF_XRL_TARGET_HH__ #define __OSPF_XRL_TARGET_HH__ #include "xrl/targets/ospfv2_base.hh" #include "ospf.hh" class XrlOspfV2Target : XrlOspfv2TargetBase { public: XrlOspfV2Target(XrlRouter *r, Ospf& ospf, XrlIO& io); /** * Get name of Xrl Target */ XrlCmdError common_0_1_get_target_name( // Output values, string& name); /** * Get version string from Xrl Target */ XrlCmdError common_0_1_get_version( // Output values, string& version); /** * Get status of Xrl Target */ XrlCmdError common_0_1_get_status( // Output values, uint32_t& status, string& reason); /** * Request clean shutdown of Xrl Target */ XrlCmdError common_0_1_shutdown(); XrlCmdError common_0_1_startup(); /** * Receive an IPv4 packet from a raw socket. * * @param if_name the interface name the packet arrived on. * * @param vif_name the vif name the packet arrived on. * * @param src_address the IP source address. * * @param dst_address the IP destination address. * * @param ip_protocol the IP protocol number. * * @param ip_ttl the IP TTL (hop-limit). If it has a negative value, then * the received value is unknown. * * @param ip_tos the Type of Service (Diffserv/ECN bits for IPv4). If it * has a negative value, then the received value is unknown. * * @param ip_router_alert if true, the IP Router Alert option was included * in the IP packet. * * @param ip_internet_control if true, then this is IP control traffic. */ XrlCmdError raw_packet4_client_0_1_recv( // Input values, const string& if_name, const string& vif_name, const IPv4& src_address, const IPv4& dst_address, const uint32_t& ip_protocol, const int32_t& ip_ttl, const int32_t& ip_tos, const bool& ip_router_alert, const bool& ip_internet_control, const vector& payload); /** * Configure a policy filter. * * @param filter the identifier of the filter to configure. * * @param conf the configuration of the filter. */ XrlCmdError policy_backend_0_1_configure( // Input values, const uint32_t& filter, const string& conf); /** * Reset a policy filter. * * @param filter the identifier of the filter to reset. */ XrlCmdError policy_backend_0_1_reset( // Input values, const uint32_t& filter); /** * Push all available routes through all filters for re-filtering. */ XrlCmdError policy_backend_0_1_push_routes(); /** * Start route redistribution for an IPv4 route. * * @param network the route to advertise. * * @param unicast whether the route is unicast. * * @param multicast whether the route is multicast. * * @param nexthop the nexthop of the route. * * @param metric the metric of the route. * * @param policytags the set of policy-tags associated with the route. */ XrlCmdError policy_redist4_0_1_add_route4( // Input values, const IPv4Net& network, const bool& unicast, const bool& multicast, const IPv4& nexthop, const uint32_t& metric, const XrlAtomList& policytags); /** * Terminate route redistribution for an IPv4 route. * * @param network the route for which advertisements should cease. * * @param unicast whether the route is unicast. * * @param multicast whether the route is multicast. */ XrlCmdError policy_redist4_0_1_delete_route4( // Input values, const IPv4Net& network, const bool& unicast, const bool& multicast); /** * Set router id */ XrlCmdError ospfv2_0_1_set_router_id( // Input values, const IPv4& id); /** * Set RFC 1583 compatibility. */ XrlCmdError ospfv2_0_1_set_rfc1583_compatibility( // Input values, const bool& compatibility); /** * Set the router alert in the IP options. */ XrlCmdError ospfv2_0_1_set_ip_router_alert( // Input values, const bool& ip_router_alert); /** * @param type of area "normal", "stub", "nssa" */ XrlCmdError ospfv2_0_1_create_area_router( // Input values, const IPv4& area, const string& type); /** * Change area type. * * @param area id of the area * * @param type of area "border", "stub", "nssa" */ XrlCmdError ospfv2_0_1_change_area_router_type( // Input values, const IPv4& area, const string& type); /** * Destroy area. */ XrlCmdError ospfv2_0_1_destroy_area_router( // Input values, const IPv4& area); /** * Create a binding to an interface. * * @param ifname the interface that owns vif that has address. * * @param vifname virtual interface owning address. * * @param addr the address to be added. * * @param type of link "p2p", "broadcast", "nbma", "p2m", "vlink" */ XrlCmdError ospfv2_0_1_create_peer( // Input values, const string& ifname, const string& vifname, const IPv4& addr, const string& type, const IPv4& area); /** * Delete peer. */ XrlCmdError ospfv2_0_1_delete_peer( // Input values, const string& ifname, const string& vifname); /** * Set the peer state up or down. */ XrlCmdError ospfv2_0_1_set_peer_state( // Input values, const string& ifname, const string& vifname, const bool& enable); /** * Add a neighbour to the peer. */ XrlCmdError ospfv2_0_1_add_neighbour( // Input values, const string& ifname, const string& vifname, const IPv4& area, const IPv4& neighbour_address, const IPv4& neighbour_id); /** * Remove a neighbour from the peer. */ XrlCmdError ospfv2_0_1_remove_neighbour( // Input values, const string& ifname, const string& vifname, const IPv4& area, const IPv4& neighbour_address, const IPv4& neighbour_id); /** * Create a virtual link. * * @param neighbour_id the router ID of the other end of the link. * * @param area in which an attempt has been made to configure a virtual * link it has to be the backbone. Its just being passed in so it can be * checked by the protocol. */ XrlCmdError ospfv2_0_1_create_virtual_link( // Input values, const IPv4& neighbour_id, const IPv4& area); /** * Delete virtual link * * @param neighbour_id the router ID of the other end of the link. */ XrlCmdError ospfv2_0_1_delete_virtual_link( // Input values, const IPv4& neighbour_id); /** * The area through which the virtual link transits. * * @param neighbour_id the router ID of the other end of the link. * * @param transit_area that the virtual link transits. */ XrlCmdError ospfv2_0_1_transit_area_virtual_link( // Input values, const IPv4& neighbour_id, const IPv4& transit_area); /** * The edge cost of this interface. */ XrlCmdError ospfv2_0_1_set_interface_cost( // Input values, const string& ifname, const string& vifname, const IPv4& area, const uint32_t& cost); /** * The RxmtInterval. */ XrlCmdError ospfv2_0_1_set_retransmit_interval( // Input values, const string& ifname, const string& vifname, const IPv4& area, const uint32_t& interval); /** * Update packet will have their age incremented by this amount before * transmission. This value should take into account transmission and * propagation delays; it must be greater than zero. */ XrlCmdError ospfv2_0_1_set_inftransdelay( // Input values, const string& ifname, const string& vifname, const IPv4& area, const uint32_t& delay); /** * Used in the designated router election. */ XrlCmdError ospfv2_0_1_set_router_priority( // Input values, const string& ifname, const string& vifname, const IPv4& area, const uint32_t& interval); /** * The interval between hello messages. */ XrlCmdError ospfv2_0_1_set_hello_interval( // Input values, const string& ifname, const string& vifname, const IPv4& area, const uint32_t& interval); /** * The period to wait before considering a router dead. */ XrlCmdError ospfv2_0_1_set_router_dead_interval( // Input values, const string& ifname, const string& vifname, const IPv4& area, const uint32_t& interval); /** * Set simple password authentication key. * * @param ifname the interface name. * * @param vifname the vif name. * * @param area the area ID. * * @param password the authentication password. */ XrlCmdError ospfv2_0_1_set_simple_authentication_key( // Input values, const string& ifname, const string& vifname, const IPv4& area, const string& password); /** * Delete simple password authentication key. * * @param ifname the interface name. * * @param vifname the vif name. * * @param area the area ID. */ XrlCmdError ospfv2_0_1_delete_simple_authentication_key( // Input values, const string& ifname, const string& vifname, const IPv4& area); /** * Set MD5 authentication key. * * @param ifname the interface name. * * @param vifname the vif name. * * @param area the area ID. * * @param key_id the key ID (must be an integer in the interval [0, 255]). * * @param password the authentication password. * * @param start_time the authentication start time (YYYY-MM-DD.HH:MM). * * @param end_time the authentication end time (YYYY-MM-DD.HH:MM). * * @param max_time_drift the maximum time drift (in seconds) among * all routers. Allowed values are [0--65534] seconds or 65535 for * unlimited time drift. */ XrlCmdError ospfv2_0_1_set_md5_authentication_key( // Input values, const string& ifname, const string& vifname, const IPv4& area, const uint32_t& key_id, const string& password, const string& start_time, const string& end_time, const uint32_t& max_time_drift); /** * Delete MD5 authentication key. * * @param ifname the interface name. * * @param vifname the vif name. * * @param area the area ID. * * @param key_id the key ID (must be an integer in the interval [0, 255]). */ XrlCmdError ospfv2_0_1_delete_md5_authentication_key( // Input values, const string& ifname, const string& vifname, const IPv4& area, const uint32_t& key_id); /** * Toggle the passive status of an interface. */ XrlCmdError ospfv2_0_1_set_passive( // Input values, const string& ifname, const string& vifname, const IPv4& area, const bool& passive, const bool& host); /** * If this is a "stub" or "nssa" area toggle the sending of a default * route. */ XrlCmdError ospfv2_0_1_originate_default_route( // Input values, const IPv4& area, const bool& enable); /** * Set the StubDefaultCost, the default cost sent in a default route in a * "stub" or "nssa" area. */ XrlCmdError ospfv2_0_1_stub_default_cost( // Input values, const IPv4& area, const uint32_t& cost); /** * Toggle the sending of summaries into "stub" or "nssa" areas. */ XrlCmdError ospfv2_0_1_summaries( // Input values, const IPv4& area, const bool& enable); /** * Add area range. */ XrlCmdError ospfv2_0_1_area_range_add( // Input values, const IPv4& area, const IPv4Net& net, const bool& advertise); /** * Delete area range. */ XrlCmdError ospfv2_0_1_area_range_delete( // Input values, const IPv4& area, const IPv4Net& net); /** * Change the advertised state of this area. */ XrlCmdError ospfv2_0_1_area_range_change_state( // Input values, const IPv4& area, const IPv4Net& net, const bool& advertise); /** * Enable/Disable tracing. * * @param tvar trace variable. * * @param enable set to true to enable false to disable. */ XrlCmdError ospfv2_0_1_trace( // Input values, const string& tvar, const bool& enable); /** * Get a single lsa from an area. A stateless mechanism to get LSAs. The * client of this interface should start from zero and continue to request * LSAs (incrementing index) until toohigh becomes true. * * @param area database that is being searched. * * @param index into database starting from 0. * * @param valid true if a LSA has been returned. Some index values do not * contain LSAs. This should not be considered an error. * * @param toohigh true if no more LSA exist after this index. * * @param self if true this LSA was originated by this router. * * @param lsa if valid is true the LSA at index. */ XrlCmdError ospfv2_0_1_get_lsa( // Input values, const IPv4& area, const uint32_t& index, // Output values, bool& valid, bool& toohigh, bool& self, vector& lsa); /** * Get a list of all the configured areas. */ XrlCmdError ospfv2_0_1_get_area_list(XrlAtomList& areas); /** * Get the list of neighbours. */ XrlCmdError ospfv2_0_1_get_neighbour_list( // Output values, XrlAtomList& areas); /** * Get information on a neighbour. * * @param nid neighbour ID returned by the get_neighbour_list. * * @param valid true if valid information has been returned. * * @param address of neighbour in txt to allow IPv4 and IPv6. * * @param interface with which the neighbour forms the adjacency. * * @param state of the adjacency. * * @param rid router ID of the neighbour. * * @param priority of the neighbour (used for DR election). * * @param area the neighbour is in. * * @param opt value in the neighbours hello packet. * * @param dr designated router. * * @param bdr backup designated router. * * @param up time in seconds that the neigbour has been up. * * @param adjacent time in seconds that there has been an adjacency. */ XrlCmdError ospfv2_0_1_get_neighbour_info( // Input values, const uint32_t& nid, // Output values, string& address, string& interface, string& state, IPv4& rid, uint32_t& priority, uint32_t& deadtime, IPv4& area, uint32_t& opt, IPv4& dr, IPv4& bdr, uint32_t& up, uint32_t& adjacent); /** * Clear the OSPF database. */ XrlCmdError ospfv2_0_1_clear_database(); private: Ospf& _ospf; XrlIO& _xrl_io; }; #endif // __OSPF_XRL_TARGET_HH__ xorp/ospf/debug_io.hh0000664000076400007640000002523711421137511014732 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/ospf/debug_io.hh,v 1.30 2008/11/14 12:44:19 bms Exp $ #ifndef __OSPF_DEBUG_IO_HH__ #define __OSPF_DEBUG_IO_HH__ /** * Debugging implementation of IO for use by test programs. */ template class DebugIO : public IO { public: DebugIO(TestInfo& info, OspfTypes::Version version, EventLoop& eventloop) : _info(info), _eventloop(eventloop), _packets(0), _lsa_decoder(version), _next_interface_id(1) { initialise_lsa_decoder(version, _lsa_decoder); initialise_packet_decoder(version, _dec, _lsa_decoder); } /** * Pretty print frames. Specific to DebugIO. */ void pp(const string& which, int level, const string& interface, const string& vif, A dst, A src, uint8_t* data, uint32_t len) { TimeVal now; _eventloop.current_time(now); DOUT_LEVEL(_info, level) << now.pretty_print() << endl; DOUT_LEVEL(_info, level) << which << "(" << interface << "," << vif << "," << dst.str() << "," << src.str() << "...)" << endl; try { // Decode the packet in order to pretty print it. Packet *packet = _dec.decode(data, len); DOUT_LEVEL(_info, level) << packet->str() << endl; delete packet; } catch(InvalidPacket& e) { DOUT_LEVEL(_info, level) << "Probably no decoder provided: " << e.str() << endl; } } int startup() { ServiceBase::set_status(SERVICE_READY); return (XORP_OK); } int shutdown() { ServiceBase::set_status(SERVICE_SHUTDOWN); return (XORP_OK); } /** * Send Raw frames. */ bool send(const string& interface, const string& vif, A dst, A src, int ttl, uint8_t* data, uint32_t len) { pp("SEND", 0, interface, vif, dst, src, data, len); _packets++; DOUT(_info) << "packets sent " << _packets << endl; if (!_forward_cb.is_empty()) _forward_cb->dispatch(interface, vif, dst, src, data, len); return true; UNUSED(ttl); } /** * Register where frames should be forwarded. Specific to DebugIO. */ bool register_forward(typename IO::ReceiveCallback cb) { _forward_cb = cb; return true; } /** * Receive frames. Specific to DebugIO. */ void receive(const string& interface, const string& vif, A dst, A src, uint8_t* data, uint32_t len) { pp("RECEIVE", 1, interface, vif, dst, src, data, len); if (! IO::_receive_cb.is_empty()) IO::_receive_cb->dispatch(interface, vif, dst, src, data, len); } /** * Enable the interface/vif to receive frames. */ bool enable_interface_vif(const string& interface, const string& vif) { DOUT(_info) << "enable_interface_vif(" << interface << "," << vif << "...)" << endl; return true; } /** * Disable this interface/vif from receiving frames. */ bool disable_interface_vif(const string& interface, const string& vif) { DOUT(_info) << "disable_interface_vif(" << interface << "," << vif << "...)" << endl; return true; } /** * Test whether this interface is enabled. * * @return true if it exists and is enabled, otherwise false. */ bool is_interface_enabled(const string& interface) const { DOUT(_info) << "enabled(" << interface << ")\n"; return true; } /** * Test whether this interface/vif is enabled. * * @return true if it exists and is enabled, otherwise false. */ bool is_vif_enabled(const string& interface, const string& vif) const { DOUT(_info) << "enabled(" << interface << "," << vif << ")\n"; return true; } /** * Test whether this interface/vif/address is enabled. * * @return true if it exists and is enabled, otherwise false. */ bool is_address_enabled(const string& interface, const string& vif, const A& address) const { DOUT(_info) << "enabled(" << interface << "," << vif << "," << cstring(address) << ")\n"; return true; } bool get_addresses(const string& interface, const string& vif, list& /*addresses*/) const { DOUT(_info) << "get_addresses(" << interface << "," << vif << "," << ")\n"; return true; } // It should be safe to return the same address for each interface // as it is link local. bool get_link_local_address(const string& interface, const string& vif, IPv4& address) { DOUT(_info) << "link_local(" << interface << "," << vif << ")\n"; // RFC 3330 address = IPv4("169.254.0.1"); return true; } bool get_link_local_address(const string& interface, const string& vif, IPv6& address) { DOUT(_info) << "link_local(" << interface << "," << vif << ")\n"; // XXX // Nasty hack to generate a different link local address for // each vif. Assumes that the last character of the vif name // is a number, solves a problem with test_peering. Should // really hash the whole interface and vid and use the name // from the info structure. string addr = "fe80::"; addr.push_back(vif[vif.size() -1]); DOUT(_info) << "address = " << addr << ")\n"; address = IPv6(addr.c_str()); return true; } bool get_interface_id(const string& interface, uint32_t& interface_id) { DOUT(_info) << "get_interface_id(" << interface << ")\n"; if (0 == _interface_ids.count(interface)) { interface_id = _next_interface_id++; _interface_ids[interface] = interface_id; } else { interface_id = _interface_ids[interface]; } return true; } uint32_t get_prefix_length(const string& interface, const string& vif, A address) { DOUT(_info) << "get_prefix_length(" << interface << "," << vif << "," << cstring(address) << ")\n"; return 16; } uint32_t get_mtu(const string& interface) { DOUT(_info) << "get_mtu(" << interface << ")\n"; return 1500; } /** * On the interface/vif join this multicast group. */ bool join_multicast_group(const string& interface, const string& vif, A mcast) { DOUT(_info) << "join_multicast_group(" << interface << "," << vif << "," << mcast.str() << ")" << endl; return true; } /** * On the interface/vif leave this multicast group. */ bool leave_multicast_group(const string& interface, const string& vif, A mcast) { DOUT(_info) << "leave_multicast_group(" << interface << "," << vif << "," << mcast.str() << ")" << endl; return true; } /** * Add route to RIB. */ bool add_route(IPNet net, A nexthop, uint32_t nexthop_id, uint32_t metric, bool equal, bool discard, const PolicyTags& policytags) { DOUT(_info) << "Net: " << net.str() << " nexthop: " << nexthop.str() << " nexthop_id: " << nexthop_id << " metric: " << metric << " equal: " << bool_c_str(equal) << " discard: " << bool_c_str(discard) << " policy: " << policytags.str() << endl; XLOG_ASSERT(0 == _routing_table.count(net)); DebugRouteEntry dre; dre._nexthop = nexthop; dre._metric = metric; dre._equal = equal; dre._discard = discard; dre._policytags = policytags; _routing_table[net] = dre; return true; } /** * Replace route in RIB. */ bool replace_route(IPNet net, A nexthop, uint32_t nexthop_id, uint32_t metric, bool equal, bool discard, const PolicyTags& policytags) { DOUT(_info) << "Net: " << net.str() << " nexthop: " << nexthop.str() << " nexthop_id: " << nexthop_id << " metric: " << metric << " equal: " << bool_c_str(equal) << " discard: " << bool_c_str(discard) << " policy: " << policytags.str() << endl; if (!delete_route(net)) return false; return add_route(net, nexthop, nexthop_id, metric, equal, discard, policytags); } /** * Delete route from RIB */ bool delete_route(IPNet net) { DOUT(_info) << "Net: " << net.str() << endl; XLOG_ASSERT(1 == _routing_table.count(net)); _routing_table.erase(_routing_table.find(net)); return true; } /** * A debugging entry point. * Empty the routing table. */ void routing_table_empty() { _routing_table.clear(); } uint32_t routing_table_size() { return _routing_table.size(); } /** * Verify that this route is in the routing table. */ bool routing_table_verify(IPNet net, A nexthop, uint32_t metric, bool equal, bool discard) { DOUT(_info) << "Net: " << net.str() << " nexthop: " << nexthop.str() << " metric: " << metric << " equal: " << bool_c_str(equal) << " discard: " << bool_c_str(discard) << endl; if (0 == _routing_table.count(net)) { DOUT(_info) << "Net: " << net.str() << " not in table\n"; return false; } DebugRouteEntry dre = _routing_table[net]; if (dre._nexthop != nexthop) { DOUT(_info) << "Nexthop mismatch: " << nexthop.str() << " " << dre._nexthop.str() << endl; return false; } if (dre._metric != metric) { DOUT(_info) << "Metric mismatch: " << metric << " " << dre._metric << endl; return false; } if (dre._equal != equal) { DOUT(_info) << "Equal mismatch: " << bool_c_str(equal) << " " << bool_c_str(dre._equal) << endl; return false; } if (dre._discard != discard) { DOUT(_info) << "Discard mismatch: " << bool_c_str(discard) << " " << bool_c_str(dre._discard) << endl; return false; } return true; } /** * Return the number of packets that have seen so far. */ int packets() { return _packets; } private: TestInfo& _info; EventLoop& _eventloop; PacketDecoder _dec; int _packets; LsaDecoder _lsa_decoder; typename IO::ReceiveCallback _forward_cb; uint32_t _next_interface_id; map _interface_ids; struct DebugRouteEntry { A _nexthop; uint32_t _metric; bool _equal; bool _discard; PolicyTags _policytags; }; map, DebugRouteEntry> _routing_table; }; #endif // __OSPF_DEBUG_IO_HH__ xorp/ospf/area_router.cc0000664000076400007640000052721611540224231015455 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #include "ospf_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/callback.hh" #include "libxorp/ipv4.hh" #include "libxorp/ipv6.hh" #include "libxorp/ipnet.hh" #include "libxorp/status_codes.h" #include "libxorp/service.hh" #include "libxorp/eventloop.hh" #include #include "libproto/spt.hh" #include "ospf.hh" #include "delay_queue.hh" #include "vertex.hh" #include "area_router.hh" template AreaRouter::AreaRouter(Ospf& ospf, OspfTypes::AreaID area, OspfTypes::AreaType area_type) : _ospf(ospf), _area(area), _area_type(area_type), _summaries(true), _stub_default_announce(false), _stub_default_cost(0), _external_flooding(false), _last_entry(0), _allocated_entries(0), _readers(0), _queue(ospf.get_eventloop(), OspfTypes::MinLSInterval, callback(this, &AreaRouter::publish_all)), _lsid(1), #ifdef UNFINISHED_INCREMENTAL_UPDATE _TransitCapability(0), #else _TransitCapability(false), #endif _routing_recompute_delay(1), // In seconds. _translator_role(OspfTypes::CANDIDATE), _translator_state(OspfTypes::DISABLED), _type7_propagate(false) // Default from RFC 3210 Appendix A { // Never need to delete this as the ref_ptr will tidy up. // An entry to be placed in invalid slots. // When a LSA that was not self originated reaches MAXAGE it must // be flooded but we can't mark it as invalid. In all other cases // just marking an entry as invalid is good enough. _invalid_lsa = Lsa::LsaRef(new RouterLsa(_ospf.get_version())); _invalid_lsa->invalidate(); // Never need to delete this as the ref_ptr will tidy up. RouterLsa *rlsa = new RouterLsa(_ospf.get_version()); rlsa->set_self_originating(true); TimeVal now; _ospf.get_eventloop().current_time(now); rlsa->record_creation_time(now); Lsa_header& header = rlsa->get_header(); switch(_ospf.get_version()) { case OspfTypes::V2: // This is a router LSA so the link state ID is the Router ID. header.set_link_state_id(_ospf.get_router_id()); break; case OspfTypes::V3: header.set_link_state_id(0); break; } header.set_advertising_router(_ospf.get_router_id()); _router_lsa = Lsa::LsaRef(rlsa); add_lsa(_router_lsa); // _db.push_back(_router_lsa); // _last_entry = 1; #ifdef UNFINISHED_INCREMENTAL_UPDATE // Add this router to the SPT table. Vertex v; RouterVertex(v); _spt.add_node(v); _spt.set_origin(v); #endif } template int AreaRouter::startup() { generate_default_route(); // Request the peer manager to send routes that are candidates // from summarisation. Also request an AS-External-LSAs that // should be announced. These requests are only relevant when // there are multiple areas involved although the requests are // made unconditionally. // Perform these actions last as they will invoke a callback into // this class. PeerManager& pm = _ospf.get_peer_manager(); pm.summary_push(_area); if (external_area_type()) pm.external_push(_area); return (XORP_OK); } template int AreaRouter::shutdown() { _ospf.get_routing_table().remove_area(_area); clear_database(); return (XORP_OK); } template void AreaRouter::add_peer(OspfTypes::PeerID peerid) { debug_msg("PeerID %u\n", peerid); // The peer starts in the down state. _peers[peerid] = PeerStateRef(new PeerState); } template void AreaRouter::delete_peer(OspfTypes::PeerID peerid) { debug_msg("PeerID %u\n", peerid); // The peer manager does not keep track of which peers belong to // which areas. So when a peer is deleted all areas are notified. if (0 == _peers.count(peerid)) return; _peers.erase(_peers.find(peerid)); } template bool AreaRouter::peer_up(OspfTypes::PeerID peerid) { debug_msg("PeerID %u\n", peerid); if (0 == _peers.count(peerid)) { XLOG_WARNING("Peer not found %u", peerid); return false; } // Mark the peer as UP typename PeerMap::iterator i = _peers.find(peerid); PeerStateRef psr = i->second; psr->_up = true; refresh_router_lsa(); return true; } template bool AreaRouter::peer_down(OspfTypes::PeerID peerid) { debug_msg("PeerID %u\n", peerid); if (0 == _peers.count(peerid)) { XLOG_WARNING("Peer not found %u", peerid); return false; } // Mark the peer as DOWN typename PeerMap::iterator i = _peers.find(peerid); PeerStateRef psr = i->second; psr->_up = false; refresh_router_lsa(); return true; } template void AreaRouter::area_border_router_transition(bool up) { if (up) { generate_default_route(); } else { withdraw_default_route(); } } template void AreaRouter::change_area_router_type(OspfTypes::AreaType area_type) { debug_msg("Type %s\n", pp_area_type(area_type).c_str()); _area_type = area_type; // Remove this routers Router-LSA from the database. size_t index; if (!find_lsa(_router_lsa, index)) XLOG_FATAL("Couldn't find this router's Router-LSA in database %s\n", cstring(*_router_lsa)); delete_lsa(_router_lsa, index, false /* Don't invalidate */); save_default_route(); clear_database(true /* Preserve Link-LSAs OSPFv3 */); // Put the Router-LSA back. add_lsa(_router_lsa); restore_default_route(); // Put the Summary-LSAs and (AS-External-LSAs or Type7-LSAs) back startup(); } template <> bool AreaRouter::find_global_address(uint32_t, uint16_t, LsaTempStore&, IPv4&) const { XLOG_FATAL("Only IPv4 not IPv6"); return true; } template <> bool AreaRouter::find_global_address(uint32_t adv, uint16_t type, LsaTempStore& lsa_temp_store, IPv6& global_address) const { // Find the global interface address of the neighbour that should be used. const list& nlsai = lsa_temp_store.get_intra_area_prefix_lsas(adv); list nprefixes; associated_prefixesV3(type, 0, nlsai, nprefixes); list::const_iterator ni; for (ni = nprefixes.begin(); ni != nprefixes.end(); ni++) { if (ni->get_la_bit() && ni->get_network().prefix_len() == IPv6::ADDR_BITLEN) { IPv6 addr = ni->get_network().masked_addr(); if (!addr.is_linklocal_unicast() && !addr.is_zero()) { global_address = addr; return true; } } } return false; } template bool AreaRouter::configured_virtual_link() const { return !_vlinks.empty(); } template bool AreaRouter::add_virtual_link(OspfTypes::RouterID rid) { debug_msg("Router ID %s\n", pr_id(rid).c_str()); XLOG_TRACE(_ospf.trace()._virtual_link, "Add virtual link rid %s\n", pr_id(rid).c_str()); switch(_area_type) { case OspfTypes::NORMAL: break; case OspfTypes::STUB: case OspfTypes::NSSA: XLOG_WARNING("Can't configure a virtual link through a %s area", pp_area_type(_area_type).c_str()); return false; break; } XLOG_ASSERT(0 == _vlinks.count(rid)); _vlinks[rid] = false; routing_schedule_total_recompute(); return true; } template bool AreaRouter::remove_virtual_link(OspfTypes::RouterID rid) { debug_msg("Router ID %s\n", pr_id(rid).c_str()); XLOG_TRACE(_ospf.trace()._virtual_link, "Remove virtual link rid %s\n", pr_id(rid).c_str()); switch(_area_type) { case OspfTypes::NORMAL: break; case OspfTypes::STUB: case OspfTypes::NSSA: XLOG_WARNING("Can't configure a virtual link through a %s area", pp_area_type(_area_type).c_str()); return false; break; } XLOG_ASSERT(0 != _vlinks.count(rid)); _vlinks.erase(_vlinks.find(rid)); // Note this call is async if it was sync it would cause a delete // link upcall to the peer_manager, possibly surprising it. routing_schedule_total_recompute(); return true; } template void AreaRouter::start_virtual_link() { // Create a set of virtual links that were up. At the end of this // process all entries that are currently on the list should be // taken down as they are no longer up or they would have been // removed from the set. _tmp.clear(); map::iterator i; for(i = _vlinks.begin(); i != _vlinks.end(); i++) if (i->second) _tmp.insert(i->first); } template void AreaRouter::check_for_virtual_linkV2(const RouteCmd& rc, Lsa::LsaRef r) { Vertex node = rc.node(); Lsa::LsaRef lsar = node.get_lsa(); RouterLsa *rlsa = dynamic_cast(lsar.get()); XLOG_ASSERT(rlsa); OspfTypes::RouterID rid = rlsa->get_header().get_link_state_id(); // If this router ID is in the tmp set then it is already up, just // remove it from the set and return; set::iterator i = _tmp.find(rid); if (i != _tmp.end()) { _tmp.erase(i); return; } XLOG_TRACE(_ospf.trace()._virtual_link, "Checking for virtual links V2, count(rid): %i %s\n", (int)(_vlinks.count(rid)), cstring(*rlsa)); if (0 == _vlinks.count(rid)) return; // Not a candidate endpoint. XLOG_TRACE(_ospf.trace()._virtual_link, "Found virtual link endpoint %s\n", pr_id(rid).c_str()); // Find the interface address of the neighbour that should be used. A neighbour_interface_address; if (!find_interface_address(rc.prevhop().get_lsa(), lsar, neighbour_interface_address)) return; // Find this routers own interface address. A routers_interface_address; if (!find_interface_address(rc.nexthop().get_lsa(), r, routers_interface_address)) return; // Now that everything has succeeded mark the virtual link as up. XLOG_ASSERT(0 != _vlinks.count(rid)); _vlinks[rid] = true; _ospf.get_peer_manager().up_virtual_link(rid, routers_interface_address, rc.weight(), neighbour_interface_address); } template <> void AreaRouter::check_for_virtual_linkV3(const RouteCmd&, Lsa::LsaRef, LsaTempStore&) { } template <> void AreaRouter::check_for_virtual_linkV3(const RouteCmd& rc, Lsa::LsaRef r, LsaTempStore& lsa_temp_store) { Vertex node = rc.node(); list& lsars = node.get_lsas(); list::iterator l = lsars.begin(); XLOG_ASSERT(l != lsars.end()); Lsa::LsaRef lsar = *l++; RouterLsa *rlsa = dynamic_cast(lsar.get()); XLOG_ASSERT(rlsa); OspfTypes::RouterID rid = rlsa->get_header().get_advertising_router(); // If this router ID is in the tmp set then it is already up, just // remove it from the set and return; set::iterator i = _tmp.find(rid); if (i != _tmp.end()) { _tmp.erase(i); return; } XLOG_TRACE(_ospf.trace()._virtual_link, "Checking for virtual links %s\n", cstring(*rlsa)); if (0 == _vlinks.count(rid)) return; // Not a candidate endpoint. XLOG_TRACE(_ospf.trace()._virtual_link, "Found virtual link endpoint %s\n", pr_id(rid).c_str()); // Find the global interface address of the neighbour that should be used. IPv6 neighbour_interface_address; if (!find_global_address(rid, rlsa->get_ls_type(), lsa_temp_store, neighbour_interface_address)) { XLOG_TRACE(_ospf.trace()._virtual_link, "No global address for virtual link endpoint %s\n", pr_id(rid).c_str()); return; } // Find this routers own interface address, use the LSA database // because if the global address is not being advertised there is // no point in trying to bring up the virtual link. IPv6 routers_interface_address; if (!find_global_address(r->get_header().get_advertising_router(), rlsa->get_ls_type(), lsa_temp_store, routers_interface_address)) { XLOG_TRACE(_ospf.trace()._virtual_link, "No global address for this router\n"); return; } // Now that everything has succeeded mark the virtual link as up. XLOG_ASSERT(0 != _vlinks.count(rid)); _vlinks[rid] = true; _ospf.get_peer_manager().up_virtual_link(rid, routers_interface_address, rc.weight(), neighbour_interface_address); } template void AreaRouter::end_virtual_link() { set::iterator i; for(i = _tmp.begin(); i != _tmp.end(); i++) { OspfTypes::RouterID rid = *i; XLOG_ASSERT(0 != _vlinks.count(rid)); _vlinks[rid] = false; _ospf.get_peer_manager().down_virtual_link(rid); } } template <> bool AreaRouter::find_interface_address(Lsa::LsaRef src, Lsa::LsaRef dst, IPv4& interface) const { XLOG_TRACE(_ospf.trace()._find_interface_address, "Find interface address \nsrc:\n%s\ndst:\n%s\n", cstring(*src), cstring(*dst)); RouterLsa *rlsa = dynamic_cast(src.get()); NetworkLsa *nlsa = dynamic_cast(src.get()); if(0 == rlsa && 0 == nlsa) { XLOG_WARNING( "Expecting the source to be a " "Router-Lsa or a Network-LSA not %s", cstring(*src)); return false; } RouterLsa *dst_rlsa = dynamic_cast(dst.get()); if(0 == dst_rlsa) { XLOG_WARNING("Expecting the source to be a Router-Lsa not %s", cstring(*src)); return false; } OspfTypes::RouterID srid = src->get_header().get_link_state_id(); // Look for the corresponding link. It is not necessary to check // for bidirectional connectivity as this check has already been made. const list &rlinks = dst_rlsa->get_router_links(); list::const_iterator l = rlinks.begin(); for(; l != rlinks.end(); l++) { debug_msg("Does %s == %s\n", pr_id(l->get_link_id()).c_str(), pr_id(srid).c_str()); if (l->get_link_id() == srid) { if (rlsa) { if (RouterLink::p2p == l->get_type() || RouterLink::vlink == l->get_type()) { interface = IPv4(htonl(l->get_link_data())); return true; } } if (nlsa) { if (RouterLink::transit == l->get_type()) { interface = IPv4(htonl(l->get_link_data())); return true; } } } } if (nlsa) return false; // There is a special case to deal with which is not part of the // normal adjacency check. Under normal circumstances two // Router-LSAs can be checked for adjacency by checking for p2p or // vlink. If the router link type is transit then the adjacency // should be Network-LSA to Router-LSA. However when introducing // Router-LSAs into the SPT the Router-LSA <-> Router-LSA wins // over Network-LSA <-> Router-LSA. If both of the LSAs are // Router-LSAs then check the transit links to find a common // router interface address. const list &src_links = rlsa->get_router_links(); const list &dst_links = dst_rlsa->get_router_links(); list::const_iterator si = src_links.begin(); for (; si != src_links.end(); si++) { list::const_iterator di = dst_links.begin(); for (; di != dst_links.end(); di++) { if (si->get_type() == RouterLink::transit && di->get_type() == RouterLink::transit) { if (si->get_link_id() == di->get_link_id()) { interface = IPv4(htonl(di->get_link_data())); return true; } } } } return false; } template <> bool AreaRouter::find_interface_address(Lsa::LsaRef, Lsa::LsaRef, IPv6&) const { XLOG_FATAL("Only IPv4 not IPv6"); return false; } template <> bool AreaRouter::find_interface_address(OspfTypes::RouterID, uint32_t, IPv4&) { XLOG_FATAL("Only IPv6 not IPv4"); return false; } template <> bool AreaRouter::find_interface_address(OspfTypes::RouterID rid, uint32_t interface_id, IPv6& interface) { XLOG_ASSERT(OspfTypes::V3 == _ospf.get_version()); // Try and find the router link that this one points at. Ls_request lsr(_ospf.get_version(), LinkLsa(_ospf.get_version()).get_header().get_ls_type(), interface_id, rid); size_t index; if (find_lsa(lsr, index)) { Lsa::LsaRef lsa = _db[index]; // This can probably never happen if (lsa->maxage()) { XLOG_WARNING("LSA in database MaxAge\n%s", cstring(*lsa)); return false; } LinkLsa *llsa = dynamic_cast(lsa.get()); XLOG_ASSERT(llsa); interface = llsa->get_link_local_address(); return true; } if (get_neighbour_address(rid, interface_id, interface)) return true; return false; } template bool AreaRouter::area_range_add(IPNet net, bool advertise) { debug_msg("Net %s advertise %s\n", cstring(net), bool_c_str(advertise)); Range r; r._advertise = advertise; _area_range.insert(net, r); routing_schedule_total_recompute(); return true; } template bool AreaRouter::area_range_delete(IPNet net) { debug_msg("Net %s\n", cstring(net)); _area_range.erase(net); routing_schedule_total_recompute(); return true; } template bool AreaRouter::area_range_change_state(IPNet net, bool advertise) { debug_msg("Net %s advertise %s\n", cstring(net), bool_c_str(advertise)); typename Trie::iterator i = _area_range.lookup_node(net); if (_area_range.end() == i) { XLOG_WARNING("Area range %s not found", cstring(net)); return false; } Range& r = i.payload(); if (r._advertise == advertise) return true; r._advertise = advertise; routing_schedule_total_recompute(); return true; } template bool AreaRouter::area_range_covered(IPNet net, bool& advertise) { debug_msg("Net %s advertise %s\n", cstring(net), bool_c_str(advertise)); typename Trie::iterator i = _area_range.find(net); if (_area_range.end() == i) return false; advertise = i.payload()._advertise; return true; } template bool AreaRouter::area_range_covering(IPNet net, IPNet& sumnet) { debug_msg("Net %s\n", cstring(net)); typename Trie::iterator i = _area_range.find(net); if (_area_range.end() == i) { XLOG_WARNING("Net %s not covered", cstring(net)); return false; } sumnet = i.key(); return true; } template bool AreaRouter::area_range_configured() { return 0 != _area_range.route_count(); } template bool AreaRouter::originate_default_route(bool enable) { if (_stub_default_announce == enable) return true; _stub_default_announce = enable; switch(_area_type) { case OspfTypes::NORMAL: return true; break; case OspfTypes::STUB: case OspfTypes::NSSA: break; } if (_stub_default_announce) generate_default_route(); else withdraw_default_route(); return true; } template bool AreaRouter::stub_default_cost(uint32_t cost) { if (_stub_default_cost == cost) return true; _stub_default_cost = cost; switch(_area_type) { case OspfTypes::NORMAL: return true; break; case OspfTypes::STUB: case OspfTypes::NSSA: break; } if (_stub_default_announce) refresh_default_route(); return true; } template bool AreaRouter::summaries(bool enable) { if (_summaries == enable) return true; _summaries = enable; switch(_area_type) { case OspfTypes::NORMAL: return true; break; case OspfTypes::STUB: case OspfTypes::NSSA: break; } if (_summaries) { _ospf.get_peer_manager().summary_push(_area); return true; } save_default_route(); // Remove all the Summary-LSAs from the database by MaxAging them. OspfTypes::Version version = _ospf.get_version(); maxage_type_database(SummaryNetworkLsa(version).get_ls_type()); maxage_type_database(SummaryRouterLsa(version).get_ls_type()); restore_default_route(); return true; } template bool AreaRouter::get_lsa(const uint32_t index, bool& valid, bool& toohigh, bool& self, vector& lsa) { debug_msg("Index %u\n", index); if (index >= _last_entry) { valid = false; toohigh = true; return true; } else { toohigh = false; } Lsa::LsaRef lsar = _db[index]; if (!lsar->valid()) { valid = false; return true; } if (!lsar->available()) { valid = false; return true; } TimeVal now; _ospf.get_eventloop().current_time(now); if (!lsar->maxage()) lsar->update_age(now); self = lsar->get_self_originating(); size_t len; uint8_t *ptr = lsar->lsa(len); lsa.resize(len); memcpy(&lsa[0], ptr, len); valid = true; return true; } template <> void AreaRouter::unique_link_state_id(Lsa::LsaRef lsar) { SummaryNetworkLsa *snlsa = dynamic_cast(lsar.get()); if (0 == snlsa) // Must be a type 4 lsa. return; size_t index; if (!find_lsa(lsar, index)) return; Lsa::LsaRef lsar_in_db = _db[index]; XLOG_ASSERT(lsar_in_db->get_self_originating()); SummaryNetworkLsa *snlsa_in_db = dynamic_cast(lsar_in_db.get()); if (0 == snlsa) return; if (snlsa->get_network_mask() == snlsa_in_db->get_network_mask()) return; IPv4 mask = IPv4(htonl(snlsa->get_network_mask())); IPv4 mask_in_db = IPv4(htonl(snlsa_in_db->get_network_mask())); XLOG_ASSERT(mask != mask_in_db); // The simple case the new LSA is more specific so its host bits // can be set and we are done. if (mask_in_db.mask_len() < mask.mask_len()) { Lsa_header& header = lsar->get_header(); header.set_link_state_id(set_host_bits(header.get_link_state_id(), ntohl(mask.addr()))); lsar->encode(); return; } // The harder case, the LSA already in the database needs to be // changed. Its not strictly necessary to remove the LSA from the // database but if we ever use maps to speed up access to the // database the deletion will be required as the link state ID // will be one of the keys. delete_lsa(lsar_in_db, index, false /* invalidate */); Lsa_header& header = lsar_in_db->get_header(); header.set_link_state_id(set_host_bits(header.get_link_state_id(), ntohl(mask_in_db.addr()))); lsar_in_db->encode(); add_lsa(lsar_in_db); refresh_summary_lsa(lsar_in_db); } template <> void AreaRouter::unique_link_state_id(Lsa::LsaRef /*lsar*/) { } template <> bool AreaRouter::unique_find_lsa(Lsa::LsaRef lsar, const IPNet& net, size_t& index) { if (!find_lsa(lsar, index)) return false; Lsa::LsaRef lsar_in_db = _db[index]; XLOG_ASSERT(lsar_in_db->get_self_originating()); SummaryNetworkLsa *snlsa_in_db = dynamic_cast(lsar_in_db.get()); if (0 == snlsa_in_db) return true; IPv4 mask_in_db = IPv4(htonl(snlsa_in_db->get_network_mask())); if (mask_in_db.mask_len() == net.prefix_len()) return true; // The incoming LSA can't be modified so create a new LSA to // continue the search. // Don't worry about the memory it will be freed when the LSA goes // out of scope. SummaryNetworkLsa *search = new SummaryNetworkLsa(_ospf.version()); Lsa::LsaRef searchlsar(search); // Copy across the header fields. searchlsar->get_header() = lsar->get_header(); Lsa_header& header = searchlsar->get_header(); // Set the host bits and try to find it again. header.set_link_state_id(set_host_bits(header.get_link_state_id(), ntohl(net.netmask().addr()))); // Recursive return unique_find_lsa(searchlsar, net, index); } template <> bool AreaRouter::unique_find_lsa(Lsa::LsaRef lsar, const IPNet& /*net*/, size_t& index) { return find_lsa(lsar, index); } template <> void AreaRouter::summary_network_lsa_set_net_lsid(SummaryNetworkLsa *snlsa, IPNet net) { snlsa->set_network_mask(ntohl(net.netmask().addr())); Lsa_header& header = snlsa->get_header(); header.set_link_state_id(ntohl(net.masked_addr().addr())); } template <> void AreaRouter::summary_network_lsa_set_net_lsid(SummaryNetworkLsa *snlsa, IPNet net) { IPv6Prefix prefix(_ospf.get_version()); prefix.set_network(net); snlsa->set_ipv6prefix(prefix); // Note entries in the _lsmap are never removed, this guarantees // for the life of OSPF that the same network to link state ID // mapping exists. If this is a problem on a withdraw remove the // entry, will need to add another argument. uint32_t lsid = 0; if (0 == _lsmap.count(net)) { lsid = _lsid++; _lsmap[net] = lsid; } else { lsid = _lsmap[net]; } Lsa_header& header = snlsa->get_header(); header.set_link_state_id(lsid); } template Lsa::LsaRef AreaRouter::summary_network_lsa(IPNet net, RouteEntry& rt) { OspfTypes::Version version = _ospf.get_version(); SummaryNetworkLsa *snlsa = new SummaryNetworkLsa(version); Lsa_header& header = snlsa->get_header(); summary_network_lsa_set_net_lsid(snlsa, net); snlsa->set_metric(rt.get_cost()); switch (version) { case OspfTypes::V2: header.set_options(get_options()); break; case OspfTypes::V3: if (net.masked_addr().is_linklocal_unicast()) XLOG_WARNING("Advertising a Link-local address in %s", cstring(*snlsa)); break; } Lsa::LsaRef summary_lsa = snlsa; return summary_lsa; } template Lsa::LsaRef AreaRouter::summary_network_lsa_intra_area(OspfTypes::AreaID area, IPNet net, RouteEntry& rt, bool& announce) { debug_msg("Area %s net %s rentry %s\n", pr_id(area).c_str(), cstring(net), cstring(rt)); XLOG_ASSERT(rt.get_path_type() == RouteEntry::intra_area); XLOG_ASSERT(rt.get_destination_type() == OspfTypes::Network); announce = true; // Unconditionally construct the Summary-LSA so it can be used to // remove old entries. Lsa::LsaRef summary_lsa = summary_network_lsa(net, rt); // Is this net covered by the originating areas area // ranges. Interestingly it doesn't matter if it should be // advertised or not. In both cases we shouldn't announce the LSA. bool advertise; if (!rt.get_discard() && _ospf.get_peer_manager().area_range_covered(area, net, advertise)) announce = false; // If this route came from the backbone and this is a transit area // then no summarisation should take place. if (backbone(area) && get_transit_capability()) { if (rt.get_discard()) announce = false; else announce = true; } return summary_lsa; } template Lsa::LsaRef AreaRouter::summary_build(OspfTypes::AreaID area, IPNet net, RouteEntry& rt, bool& announce) { debug_msg("Area %s net %s rentry %s\n", pr_id(area).c_str(), cstring(net), cstring(rt)); announce = true; Lsa::LsaRef summary_lsa; // Only intra-area routes are advertised into the backbone switch (rt.get_path_type()) { case RouteEntry::intra_area: break; case RouteEntry::inter_area: if (backbone()) return summary_lsa; break; case RouteEntry::type1: case RouteEntry::type2: // The peer manager should already have filtered out these two // types of routes. XLOG_UNREACHABLE(); break; } switch(_area_type) { case OspfTypes::NORMAL: break; case OspfTypes::STUB: case OspfTypes::NSSA: if (!_summaries) return summary_lsa; // RFC 2328 Section 12.4.3.1 Originating summary-LSAs into stub areas // Type 4 summary-LSAs (ASBR-summary-LSAs) are never // originated into stub areas. if (OspfTypes::Router == rt.get_destination_type() && rt.get_as_boundary_router()) return summary_lsa; break; } // RFC 2328 Section 12.4.3. Summary-LSAs // All the sanity checks upto "is this our own area" have already // been performed? RoutingTable& routing_table = _ospf.get_routing_table(); // If the nexthop falls into this area don't generate a summary. RouteEntry nexthop_rt; if (routing_table.longest_match_entry(rt.get_nexthop(), nexthop_rt)) { if (nexthop_rt.get_area() == _area) return summary_lsa; } // If the routing table cost equals or exceeds the value // LSInfinity, a summary-LSA cannot be generated for this route. if (rt.get_cost() >= OspfTypes::LSInfinity) return summary_lsa; OspfTypes::Version version = _ospf.get_version(); switch (rt.get_destination_type()) { case OspfTypes::Router: { XLOG_ASSERT(rt.get_as_boundary_router()); SummaryRouterLsa *srlsa = new SummaryRouterLsa(version); Lsa_header& header = srlsa->get_header(); // AS boundary router's Router ID, for OSPFv3 we can choose // any unique value so lets leave it as the the Router ID. header.set_link_state_id(rt.get_router_id()); // header.set_advertising_router(_ospf.get_router_id()); switch (version) { case OspfTypes::V2: srlsa->set_network_mask(0); header.set_options(get_options()); break; case OspfTypes::V3: srlsa->set_destination_id(rt.get_router_id()); RouterLsa *rlsa = dynamic_cast(rt.get_lsa().get()); SummaryRouterLsa *sr = dynamic_cast(rt.get_lsa().get()); uint32_t options = 0; if (rlsa) { options = rlsa->get_options(); } else if(sr) { options = sr->get_options(); } else { XLOG_WARNING("Unexpected LSA can't get options %s", cstring(rt)); } // In OSPFv3 the options field is set to the options from // the original Router-LSA in OSPFv2 as far as I can tell // it should be this routers options. srlsa->set_options(options); break; } srlsa->set_metric(rt.get_cost()); summary_lsa = Lsa::LsaRef(srlsa); } break; case OspfTypes::Network: { switch (rt.get_path_type()) { case RouteEntry::intra_area: return summary_network_lsa_intra_area(area, net, rt, announce); break; case RouteEntry::inter_area: return summary_network_lsa(net, rt); break; case RouteEntry::type1: case RouteEntry::type2: // The peer manager should already have filtered out these two // types of routes. XLOG_UNREACHABLE(); break; } } break; } return summary_lsa; } template void AreaRouter::summary_announce(OspfTypes::AreaID area, IPNet net, RouteEntry& rt, bool push) { debug_msg("Area %s net %s rentry %s push %s\n", pr_id(area).c_str(), cstring(net), cstring(rt), bool_c_str(push)); XLOG_ASSERT(area != _area); XLOG_ASSERT(area == rt.get_area()); // If this is a discard route generated by an area range then // request a push of all the routes. We are seeing the discard // route either due to a reconfiguration or due to the first route // falling into an area range. Its unfortunate that the first // route to fall into a range will cause a recompute. if (!push && rt.get_discard()) { PeerManager& pm = _ospf.get_peer_manager(); pm.summary_push(_area); // return immediately the push will cause all the routes to be // pushed through again anyway. return; } bool announce; Lsa::LsaRef lsar = summary_build(area, net, rt, announce); if (0 == lsar.get()) return; // Set the general fields. lsar->get_header().set_advertising_router(_ospf.get_router_id()); lsar->set_self_originating(true); TimeVal now; _ospf.get_eventloop().current_time(now); lsar->record_creation_time(now); lsar->encode(); if (push) { // See if its already being announced. size_t index; if (unique_find_lsa(lsar, net, index)) { // Remove it if it should no longer be announced. if(!announce) { lsar = _db[index]; premature_aging(lsar, index); } // It is already being announced so out of here. return; } } // Check to see if its already being announced, another LSA may // already have caused this summary to have been announced. Not // absolutely clear how. size_t index; if (unique_find_lsa(lsar, net, index)) { XLOG_WARNING("LSA already being announced \n%s", cstring(*_db[index])); return; } if (!announce) { return; } unique_link_state_id(lsar); add_lsa(lsar); refresh_summary_lsa(lsar); } template void AreaRouter::refresh_summary_lsa(Lsa::LsaRef lsar) { TimeVal now; _ospf.get_eventloop().current_time(now); update_age_and_seqno(lsar, now); lsar->get_timer() = _ospf.get_eventloop(). new_oneoff_after(TimeVal(OspfTypes::LSRefreshTime, 0), callback(this, &AreaRouter::refresh_summary_lsa, lsar)); // Announce this LSA to all neighbours. publish_all(lsar); } template void AreaRouter::summary_withdraw(OspfTypes::AreaID area, IPNet net, RouteEntry& rt) { debug_msg("Area %s net %s\n", pr_id(area).c_str(), cstring(net)); XLOG_ASSERT(area != _area); XLOG_ASSERT(area == rt.get_area()); bool announce; Lsa::LsaRef lsar = summary_build(area, net, rt, announce); if (0 == lsar.get()) return; // Set the advertising router otherwise the lookup will fail. lsar->get_header().set_advertising_router(_ospf.get_router_id()); // It would be useful to rely on the announce variable to // determine if an LSA was previously being announced and should // now be withdrawn. Under normal circumstances it works fine, // however a reconfiguration of area ranges may solicit the // messages. // Withdraw the LSA. size_t index; if (unique_find_lsa(lsar, net, index)) { if (!announce) XLOG_WARNING("LSA probably should not have been announced! " "Area range change?\n%s", cstring(*lsar)); // Remove it if it should no longer be announced. lsar = _db[index]; premature_aging(lsar, index); } else { if (announce) XLOG_WARNING("LSA not being announced! Area range change?\n%s", cstring(*lsar)); } } template void AreaRouter::summary_replace(OspfTypes::AreaID area, IPNet net, RouteEntry& rt, RouteEntry& previous_rt, OspfTypes::AreaID previous_area) { debug_msg("Area %s net %s rentry %s\n", pr_id(area).c_str(), cstring(net), cstring(rt)); XLOG_ASSERT(area != _area); XLOG_ASSERT(area == rt.get_area()); bool announce; Lsa::LsaRef olsar = summary_build(previous_area, net, previous_rt, announce); if (0 == olsar.get()) { // No previous LSA just try and announce the new one. summary_announce(area, net, rt, false); return; } // Set the advertising router otherwise the lookup will fail. olsar->get_header().set_advertising_router(_ospf.get_router_id()); bool found = false; size_t index; if (unique_find_lsa(olsar, net, index)) { if (!announce) XLOG_WARNING("LSA probably should not have been announced! " "Area range change?\n%s", cstring(*olsar)); olsar = _db[index]; found = true; } else { if (announce) XLOG_WARNING("LSA not being announced! Area range change?\n%s", cstring(*olsar)); } // No previous LSA was being announced, nothing to replace, so just // call announce. if (!found) { summary_announce(area, net, rt, false); return; } Lsa::LsaRef nlsar = summary_build(area, net, rt, announce); if (0 == nlsar.get()) { premature_aging(olsar, index); return; } // Set the general fields. nlsar->get_header().set_advertising_router(_ospf.get_router_id()); nlsar->set_self_originating(true); TimeVal now; _ospf.get_eventloop().current_time(now); nlsar->record_creation_time(now); nlsar->encode(); if (!announce) { premature_aging(olsar, index); return; } unique_link_state_id(nlsar); nlsar->set_ls_sequence_number(olsar->get_ls_sequence_number()); increment_sequence_number(nlsar); delete_lsa(olsar, index, true); add_lsa(nlsar); refresh_summary_lsa(nlsar); } template bool AreaRouter::external_area_type() const { bool accept = true; switch(_area_type) { case OspfTypes::NORMAL: accept = true; break; case OspfTypes::STUB: accept = false; break; case OspfTypes::NSSA: accept = true; break; } return accept; } template void AreaRouter::external_copy_net_nexthop(A, ASExternalLsa *dst, ASExternalLsa *src) { dst->set_network(src->get_network(A::ZERO())); switch(_ospf.get_version()) { case OspfTypes::V2: dst->set_forwarding_address(src->get_forwarding_address(A::ZERO())); break; case OspfTypes::V3: if (src->get_f_bit()) dst-> set_forwarding_address(src->get_forwarding_address(A::ZERO())); break; } } template Lsa::LsaRef AreaRouter::external_generate_type7(Lsa::LsaRef lsar, bool& indb) { ASExternalLsa *aselsa = dynamic_cast(lsar.get()); XLOG_ASSERT(aselsa); OspfTypes::Version version = _ospf.get_version(); Type7Lsa *type7= new Type7Lsa(version); Lsa::LsaRef t7(type7); Lsa_header& header = type7->get_header(); switch(version) { case OspfTypes::V2: { Options options(version, aselsa->get_header().get_options()); bool pbit = false; if (_type7_propagate && !_ospf.get_peer_manager().area_border_router_p()) pbit = true; options.set_p_bit(pbit); header.set_options(options.get_options()); type7->set_external_route_tag(aselsa->get_external_route_tag()); } break; case OspfTypes::V3: type7->set_f_bit(aselsa->get_f_bit()); if (type7->get_f_bit()) type7->set_forwarding_address_ipv6(aselsa-> get_forwarding_address_ipv6()); type7->set_t_bit(aselsa->get_t_bit()); if (type7->get_t_bit()) type7->set_external_route_tag(aselsa->get_external_route_tag()); break; } external_copy_net_nexthop(A::ZERO(), type7, aselsa); header. set_advertising_router(aselsa->get_header().get_advertising_router()); type7->set_e_bit(aselsa->get_e_bit()); type7->set_metric(aselsa->get_metric()); type7->set_self_originating(true); TimeVal now; _ospf.get_eventloop().current_time(now); type7->record_creation_time(now); type7->encode(); indb = true; // If this LSA already exists in the database just return it. size_t index; if (find_lsa(t7, index)) { return _db[index]; } indb = false; return t7; } template Lsa::LsaRef AreaRouter::external_generate_external(Lsa::LsaRef lsar) { Type7Lsa *type7 = dynamic_cast(lsar.get()); XLOG_ASSERT(type7); OspfTypes::Version version = _ospf.get_version(); ASExternalLsa *aselsa= new ASExternalLsa(version); Lsa::LsaRef a(aselsa); Lsa_header& header = aselsa->get_header(); switch(version) { case OspfTypes::V2: header.set_options(get_options()); aselsa->set_external_route_tag(type7->get_external_route_tag()); break; case OspfTypes::V3: aselsa->set_f_bit(type7->get_f_bit()); if (aselsa->get_f_bit()) aselsa->set_forwarding_address_ipv6(type7-> get_forwarding_address_ipv6()); aselsa->set_t_bit(type7->get_t_bit()); if (aselsa->get_t_bit()) aselsa->set_external_route_tag(type7->get_external_route_tag()); break; } external_copy_net_nexthop(A::ZERO(), aselsa, type7); header. set_advertising_router(type7->get_header().get_advertising_router()); aselsa->set_metric(type7->get_metric()); aselsa->set_e_bit(type7->get_e_bit()); aselsa->set_self_originating(true); TimeVal now; _ospf.get_eventloop().current_time(now); aselsa->record_creation_time(now); aselsa->encode(); // If this LSA already exists in the database just return it. size_t index; if (find_lsa(a, index)) { return _db[index]; } return a; } template void AreaRouter::external_announce(Lsa::LsaRef lsar, bool /*push*/, bool redist) { switch(_ospf.get_version()) { case OspfTypes::V2: XLOG_ASSERT(lsar->external()); break; case OspfTypes::V3: XLOG_ASSERT(lsar->external() || (!lsar->known() && lsar->as_scope())); break; } switch(_area_type) { case OspfTypes::NORMAL: break; case OspfTypes::STUB: return; break; case OspfTypes::NSSA: { switch(_ospf.get_version()) { case OspfTypes::V2: break; case OspfTypes::V3: if (!lsar->known()) return; break; } if (!redist) return; bool indb; lsar = external_generate_type7(lsar, indb); if (indb) return; } break; } size_t index; if (find_lsa(lsar, index)) { XLOG_FATAL("LSA already in database: %s", cstring(*lsar)); return; } add_lsa(lsar); bool multicast_on_peer; publish(OspfTypes::ALLPEERS, OspfTypes::ALLNEIGHBOURS, lsar, multicast_on_peer); } template void AreaRouter::external_announce_complete() { if (!external_area_type()) return; push_lsas("external_announce_complete"); } template void AreaRouter::external_refresh(Lsa::LsaRef lsar) { XLOG_ASSERT(lsar->external()); TimeVal now; switch(_area_type) { case OspfTypes::NORMAL: break; case OspfTypes::STUB: return; break; case OspfTypes::NSSA: { bool indb; lsar = external_generate_type7(lsar, indb); XLOG_ASSERT(indb); _ospf.get_eventloop().current_time(now); update_age_and_seqno(lsar, now); } break; } size_t index; if (!find_lsa(lsar, index)) { XLOG_FATAL("LSA not in database: %s", cstring(*lsar)); return; } XLOG_ASSERT(lsar == _db[index]); bool multicast_on_peer; publish(OspfTypes::ALLPEERS, OspfTypes::ALLNEIGHBOURS, lsar, multicast_on_peer); push_lsas("external_refresh"); } template void AreaRouter::external_withdraw(Lsa::LsaRef lsar) { XLOG_ASSERT(lsar->external()); switch(_area_type) { case OspfTypes::NORMAL: break; case OspfTypes::STUB: return; break; case OspfTypes::NSSA: { bool indb; lsar = external_generate_type7(lsar, indb); XLOG_ASSERT(indb); if (!lsar->maxage()) lsar->set_maxage(); } break; } size_t index; if (!find_lsa(lsar, index)) { XLOG_FATAL("LSA not in database: %s", cstring(*lsar)); return; } XLOG_ASSERT(lsar == _db[index]); XLOG_ASSERT(lsar->maxage()); // XXX - Will cause a routing recomputation. #ifndef MAX_AGE_IN_DATABASE delete_lsa(lsar, index, false /* Don't invalidate */); #endif publish_all(lsar); } template bool AreaRouter::new_router_links(OspfTypes::PeerID peerid, const list& router_links) { if (0 == _peers.count(peerid)) { XLOG_WARNING("Peer not found %u", peerid); return false; } typename PeerMap::iterator i = _peers.find(peerid); PeerStateRef psr = i->second; psr->_router_links.clear(); psr->_router_links.insert(psr->_router_links.begin(), router_links.begin(), router_links.end()); refresh_router_lsa(); return true; } template bool AreaRouter::add_link_lsa(OspfTypes::PeerID peerid, Lsa::LsaRef lsar) { debug_msg("PeerID %u %s\n", peerid, cstring(*lsar)); XLOG_ASSERT(lsar->get_peerid() == peerid); add_lsa(lsar); update_link_lsa(peerid, lsar); return true; } template bool AreaRouter::update_link_lsa(OspfTypes::PeerID peerid, Lsa::LsaRef lsar) { debug_msg("PeerID %u %s\n", peerid, cstring(*lsar)); XLOG_ASSERT(lsar->get_peerid() == peerid); TimeVal now; _ospf.get_eventloop().current_time(now); update_age_and_seqno(lsar, now); lsar->get_timer() = _ospf.get_eventloop(). new_oneoff_after(TimeVal(OspfTypes::LSRefreshTime, 0), callback(this, &AreaRouter::refresh_link_lsa, peerid, lsar)); publish_all(lsar); return true; } template bool AreaRouter::withdraw_link_lsa(OspfTypes::PeerID peerid, Lsa::LsaRef lsar) { debug_msg("PeerID %u %s\n", peerid, cstring(*lsar)); XLOG_ASSERT(lsar->get_peerid() == peerid); // Clear the timer, don't want this LSA coming back. lsar->get_timer().clear(); size_t index; if (find_lsa(lsar, index)) delete_lsa(lsar, index, false /* Don't invalidate */); else XLOG_WARNING("Link-LSA not found in database %s", cstring(*lsar)); return true; } template void AreaRouter::refresh_link_lsa(OspfTypes::PeerID peerid, Lsa::LsaRef lsar) { debug_msg("PeerID %u %s\n", peerid, cstring(*lsar)); XLOG_ASSERT(lsar->get_peerid() == peerid); update_link_lsa(peerid, lsar); } template bool AreaRouter::generate_network_lsa(OspfTypes::PeerID peerid, OspfTypes::RouterID link_state_id, list& routers, uint32_t network_mask) { debug_msg("PeerID %u link state id %s\n", peerid, pr_id(link_state_id).c_str()); NetworkLsa *nlsa = new NetworkLsa(_ospf.get_version()); nlsa->set_self_originating(true); TimeVal now; _ospf.get_eventloop().current_time(now); nlsa->record_creation_time(now); Lsa_header& header = nlsa->get_header(); header.set_link_state_id(link_state_id); header.set_advertising_router(_ospf.get_router_id()); #ifdef MAX_AGE_IN_DATABASE size_t index; Lsa::LsaRef lsar = Lsa::LsaRef(nlsa); if (find_lsa(lsar, index)) { update_lsa(lsar, index); } else { add_lsa(lsar); } #else Lsa::LsaRef lsar = Lsa::LsaRef(nlsa); add_lsa(lsar); #endif switch(_ospf.get_version()) { case OspfTypes::V2: break; case OspfTypes::V3: generate_intra_area_prefix_lsa(peerid, lsar, link_state_id); break; } update_network_lsa(peerid, link_state_id, routers, network_mask); return true; } template bool AreaRouter::update_network_lsa(OspfTypes::PeerID peerid, OspfTypes::RouterID link_state_id, list& routers, uint32_t network_mask) { debug_msg("PeerID %u link state id %s\n", peerid, pr_id(link_state_id).c_str()); OspfTypes::Version version = _ospf.get_version(); Ls_request lsr(version, NetworkLsa(version).get_ls_type(), link_state_id, _ospf.get_router_id()); size_t index; if (!find_lsa(lsr, index)) { XLOG_FATAL("Couldn't find Network_lsa %s in LSA database", cstring(lsr)); return false; } NetworkLsa *nlsa = dynamic_cast(_db[index].get()); XLOG_ASSERT(nlsa); // If routers is empty this is a refresh. if (!routers.empty()) { list& attached_routers = nlsa->get_attached_routers(); attached_routers.clear(); attached_routers.push_back(_ospf.get_router_id());// Add this router list::iterator i; for (i = routers.begin(); i != routers.end(); i++) attached_routers.push_back(i->_router_id); } switch (version) { case OspfTypes::V2: nlsa->set_network_mask(network_mask); nlsa->get_header().set_options(get_options()); break; case OspfTypes::V3: uint32_t options = update_intra_area_prefix_lsa(peerid,_db[index]->get_ls_type(), link_state_id, routers); nlsa->set_options(options); break; } TimeVal now; _ospf.get_eventloop().current_time(now); update_age_and_seqno(_db[index], now); // Prime this Network-LSA to be refreshed. nlsa->get_timer() = _ospf.get_eventloop(). new_oneoff_after(TimeVal(OspfTypes::LSRefreshTime, 0), callback(this, &AreaRouter::refresh_network_lsa, peerid, _db[index], true /* timer */)); publish_all(_db[index]); return true; } template bool AreaRouter::withdraw_network_lsa(OspfTypes::PeerID peerid, OspfTypes::RouterID link_state_id) { debug_msg("PeerID %u link state id %s\n", peerid, pr_id(link_state_id).c_str()); OspfTypes::Version version = _ospf.get_version(); Ls_request lsr(version, NetworkLsa(version).get_ls_type(), link_state_id, _ospf.get_router_id()); size_t index; if (!find_lsa(lsr, index)) { XLOG_WARNING("Couldn't find Network_lsa %s in LSA database" " Did the Router ID change?", cstring(lsr)); return false; } Lsa::LsaRef lsar = _db[index]; premature_aging(lsar, index); switch(_ospf.get_version()) { case OspfTypes::V2: break; case OspfTypes::V3: withdraw_intra_area_prefix_lsa(peerid, lsar->get_ls_type(), link_state_id); break; } return true; } template void AreaRouter::refresh_network_lsa(OspfTypes::PeerID peerid, Lsa::LsaRef lsar, bool timer) { NetworkLsa *nlsa = dynamic_cast(lsar.get()); XLOG_ASSERT(nlsa); XLOG_ASSERT(nlsa->valid()); uint32_t network_mask = 0; switch(_ospf.get_version()) { case OspfTypes::V2: network_mask = nlsa->get_network_mask(); break; case OspfTypes::V3: break; } list routers; update_network_lsa(peerid, nlsa->get_header().get_link_state_id(), routers, network_mask); if (!timer) routing_schedule_total_recompute(); } inline bool operator==(const IPv6Prefix& lhs, const IPv6Prefix& rhs) { // We could check for lhs == rhs this will be such a rare // occurence why bother. if (lhs.use_metric() != rhs.use_metric()) return false; if (lhs.get_network() != rhs.get_network()) return false; if (lhs.get_prefix_options() != rhs.get_prefix_options()) return false; if (lhs.use_metric() && lhs.get_metric() != rhs.get_metric()) return false; return true; } inline bool operator<(const IPv6Prefix& lhs, const IPv6Prefix& rhs) { if (lhs.get_network() < rhs.get_network()) return true; if (lhs.get_prefix_options() < rhs.get_prefix_options()) return true; if (lhs.use_metric() && lhs.get_metric() < rhs.get_metric()) return true; return false; } inline bool operator==(const LinkLsa& lhs, const LinkLsa& rhs) { set lhs_set, rhs_set; list::const_iterator i; const list& lhs_prefixes = lhs.get_prefixes(); for (i = lhs_prefixes.begin(); i != lhs_prefixes.end(); i++) lhs_set.insert(*i); const list& rhs_prefixes = rhs.get_prefixes(); for (i = rhs_prefixes.begin(); i != rhs_prefixes.end(); i++) rhs_set.insert(*i); return lhs_set == rhs_set; } template bool AreaRouter::check_link_lsa(LinkLsa *nllsa, LinkLsa *ollsa) { XLOG_ASSERT(nllsa); // There is an existing Link-LSA if it is the same as the new // Link-LSA then there is nothing that needs to be done. if (ollsa) { if (*nllsa == *ollsa) return false; } return true; } template void AreaRouter::update_intra_area_prefix_lsa(OspfTypes::PeerID peerid) { PeerManager& pm = _ospf.get_peer_manager(); uint32_t interface_id = pm.get_interface_id(peerid); list routers; if (!pm.get_attached_routers(peerid, _area, routers)) XLOG_WARNING("Unable to get attached routers"); // Test to see that a Network-LSA and Intra-Area-Prefix-LSA have // already been generated. In particular during the database // exchange the router maybe the designated router for a peer but // no full adjacencies have yet been formed. if (routers.empty()) return; // An updated Network-LSA and Intra-Area-Prefix-LSA will be sent, // note if the options have not changed then it is not actually // necessary to send a new Network-LSA. update_network_lsa(peerid, interface_id, routers, 0); } template bool AreaRouter::generate_intra_area_prefix_lsa(OspfTypes::PeerID peerid, Lsa::LsaRef lsar, uint32_t interface_id) { debug_msg("PeerID %u interface id %d\n", peerid, interface_id); IntraAreaPrefixLsa *iaplsa = new IntraAreaPrefixLsa(_ospf.get_version()); iaplsa->set_self_originating(true); Lsa_header& header = iaplsa->get_header(); header.set_link_state_id(iaplsa->create_link_state_id(lsar->get_ls_type(), interface_id)); header.set_advertising_router(_ospf.get_router_id()); iaplsa->set_referenced_ls_type(lsar->get_ls_type()); OspfTypes::Version version = _ospf.get_version(); if (RouterLsa(version).get_ls_type() == lsar->get_ls_type()) { iaplsa->set_referenced_link_state_id(0); } else if (NetworkLsa(version).get_ls_type() == lsar->get_ls_type()) { iaplsa->set_referenced_link_state_id(lsar->get_header(). get_link_state_id()); } else { XLOG_FATAL("Unknown LS Type %#x %s\n", lsar->get_ls_type(), cstring(*lsar)); } iaplsa->set_referenced_advertising_router(lsar->get_header(). get_advertising_router()); add_lsa(Lsa::LsaRef(iaplsa)); return true; } template uint32_t AreaRouter::populate_prefix(OspfTypes::PeerID peerid, uint32_t interface_id, OspfTypes::RouterID router_id, list& prefixes) { debug_msg("PeerID %u interface id %d\n", peerid, interface_id); OspfTypes::Version version = _ospf.get_version(); Ls_request lsr(version, LinkLsa(version).get_ls_type(), interface_id, router_id); uint32_t options = 0; size_t index; if (find_lsa(lsr, index)) { LinkLsa *llsa = dynamic_cast(_db[index].get()); XLOG_ASSERT(llsa); options = llsa->get_options(); const list& link_prefixes = llsa->get_prefixes(); list::const_iterator i; for (i = link_prefixes.begin(); i != link_prefixes.end(); i++) { IPv6Prefix prefix(version, true); prefix = *i; if (prefix.get_nu_bit() || prefix.get_la_bit()) continue; if (prefix.get_network().masked_addr().is_linklocal_unicast()) continue; prefix.set_metric(0); list::iterator p; for (p = prefixes.begin(); p != prefixes.end(); p++) { if (p->get_network() == prefix.get_network()) break; } if (p == prefixes.end()) { prefixes.push_back(prefix); } else { p->set_prefix_options(p->get_prefix_options() | prefix.get_prefix_options()); } } } return options; } template uint32_t AreaRouter::update_intra_area_prefix_lsa(OspfTypes::PeerID peerid, uint16_t referenced_ls_type, uint32_t interface_id, const list& attached_routers) { debug_msg("PeerID %u interface id %d\n", peerid, interface_id); OspfTypes::Version version = _ospf.get_version(); Ls_request lsr(version, IntraAreaPrefixLsa(version).get_ls_type(), IntraAreaPrefixLsa(version). create_link_state_id(referenced_ls_type, interface_id), _ospf.get_router_id()); uint32_t options = 0; size_t index; if (!find_lsa(lsr, index)) { XLOG_FATAL("Couldn't find Intra-Area-Prefix-LSA %s in LSA database", cstring(lsr)); return options; } IntraAreaPrefixLsa *iaplsa = dynamic_cast(_db[index].get()); XLOG_ASSERT(iaplsa); // If attached_routers is empty this is a refresh. if (!attached_routers.empty()) { list& prefixes = iaplsa->get_prefixes(); prefixes.clear(); // Find our own Link-LSA for this interface and add the addresses. options = populate_prefix(peerid, interface_id, _ospf.get_router_id(), prefixes); list::const_iterator i; for (i = attached_routers.begin(); i!= attached_routers.end(); i++) options |= populate_prefix(peerid, i->_interface_id, i->_router_id, prefixes); } TimeVal now; _ospf.get_eventloop().current_time(now); update_age_and_seqno(_db[index], now); publish_all(_db[index]); return options; } template bool AreaRouter::withdraw_intra_area_prefix_lsa(OspfTypes::PeerID peerid, uint16_t referenced_ls_type, uint32_t interface_id) { debug_msg("PeerID %u interface id %d\n", peerid, interface_id); OspfTypes::Version version = _ospf.get_version(); Ls_request lsr(version, IntraAreaPrefixLsa(version).get_ls_type(), IntraAreaPrefixLsa(version). create_link_state_id(referenced_ls_type, interface_id), _ospf.get_router_id()); size_t index; if (!find_lsa(lsr, index)) { XLOG_WARNING("Couldn't find Intra-Area-Prefix-LSA %s in LSA database", cstring(lsr)); return false; } Lsa::LsaRef lsar = _db[index]; premature_aging(lsar, index); return true; } template void AreaRouter::refresh_intra_area_prefix_lsa(OspfTypes::PeerID /*peerid*/, uint16_t /*referenced_ls_type*/, uint32_t /*interface_id*/) { // Not required as the Intra-Area-Prefix-LSAs totally fate share // with the associated Router-LSA or Network-LSA. XLOG_UNFINISHED(); } template void AreaRouter::generate_default_route() { switch(_area_type) { case OspfTypes::NORMAL: return; break; case OspfTypes::STUB: case OspfTypes::NSSA: break; } if (!_stub_default_announce) return; if (!_ospf.get_peer_manager().area_border_router_p()) return; size_t index; if (find_default_route(index)) return; SummaryNetworkLsa *snlsa = new SummaryNetworkLsa(_ospf.get_version()); snlsa->set_self_originating(true); TimeVal now; _ospf.get_eventloop().current_time(now); snlsa->record_creation_time(now); Lsa_header& header = snlsa->get_header(); header.set_link_state_id(OspfTypes::DefaultDestination); header.set_advertising_router(_ospf.get_router_id()); switch (_ospf.get_version()) { case OspfTypes::V2: snlsa->set_network_mask(0); break; case OspfTypes::V3: // The IPv6Prefix will have the default route by default. XLOG_ASSERT(0 == snlsa->get_ipv6prefix().get_network().prefix_len()); break; } #ifdef MAX_AGE_IN_DATABASE XLOG_UNFINISHED(); #else add_lsa(Lsa::LsaRef(snlsa)); #endif // No need to set the cost it will be done in refresh_default_route(). refresh_default_route(); } template bool AreaRouter::find_default_route(size_t& index) { OspfTypes::Version version = _ospf.get_version(); Ls_request lsr(version, SummaryNetworkLsa(version).get_ls_type(), OspfTypes::DefaultDestination, _ospf.get_router_id()); if (!find_lsa(lsr, index)) { return false; } return true; } template void AreaRouter::save_default_route() { _saved_default_route = _invalid_lsa; switch(_area_type) { case OspfTypes::NORMAL: return; break; case OspfTypes::STUB: case OspfTypes::NSSA: break; } if (!_stub_default_announce) return; size_t index; if (!find_default_route(index)) { return; } _saved_default_route = _db[index]; delete_lsa(_saved_default_route, index, false /* Don't invalidate */); } template void AreaRouter::restore_default_route() { switch(_area_type) { case OspfTypes::NORMAL: return; break; case OspfTypes::STUB: case OspfTypes::NSSA: break; } if (!_stub_default_announce) return; // No LSA was saved so generate a new one. if (!_saved_default_route->valid()) { generate_default_route(); return; } // Restore the saved LSA. add_lsa(_saved_default_route); refresh_default_route(); } template void AreaRouter::withdraw_default_route() { size_t index; if (!find_default_route(index)) return; premature_aging(_db[index], index); } template void AreaRouter::refresh_default_route() { size_t index; if (!find_default_route(index)) { XLOG_WARNING("Couldn't find default route"); return; } SummaryNetworkLsa *snlsa = dynamic_cast(_db[index].get()); XLOG_ASSERT(snlsa); switch (_ospf.get_version()) { case OspfTypes::V2: snlsa->get_header().set_options(get_options()); break; case OspfTypes::V3: break; } snlsa->set_metric(_stub_default_cost); TimeVal now; _ospf.get_eventloop().current_time(now); update_age_and_seqno(_db[index], now); snlsa->get_timer() = _ospf.get_eventloop(). new_oneoff_after(TimeVal(OspfTypes::LSRefreshTime, 0), callback(this, &AreaRouter::refresh_default_route)); publish_all(_db[index]); } inline string pp_lsas(const list& lsas) { string output; list::const_iterator i; for (i = lsas.begin(); i != lsas.end(); i++) { output += "\n\t" + (*i)->str(); } return output; } template void AreaRouter::receive_lsas(OspfTypes::PeerID peerid, OspfTypes::NeighbourID nid, list& lsas, list& direct_ack, list& delayed_ack, bool is_router_dr, bool is_router_bdr, bool is_neighbour_dr) { debug_msg("PeerID %u NeighbourID %u %s\nbackup %s dr %s\n", peerid, nid, pp_lsas(lsas).c_str(), bool_c_str(is_router_bdr), bool_c_str(is_neighbour_dr)); OspfTypes::Version version = _ospf.get_version(); TimeVal now; _ospf.get_eventloop().current_time(now); routing_begin(); // RFC 2328 Section 13. The Flooding Procedure // Validate the incoming LSAs. list::const_iterator i; for (i = lsas.begin(); i != lsas.end(); i++) { // These LSAs came over the wire they must not be self originating. XLOG_ASSERT(!(*i)->get_self_originating()); // Record the creation time and initial age. (*i)->record_creation_time(now); // For OSPFv3 LSAs with Link-local scope store the incoming PeerID. switch(version) { case OspfTypes::V2: break; case OspfTypes::V3: if ((*i)->link_local_scope()) (*i)->set_peerid(peerid); break; } // (1) Validate the LSA's LS checksum. // (2) Check that the LSA's LS type is known. // Both checks already performed in the packet/LSA decoding process. // (3) In stub areas discard AS-external-LSA's (LS type = 5, 0x4005). switch(_area_type) { case OspfTypes::NORMAL: if ((*i)->type7()) continue; break; case OspfTypes::STUB: if ((*i)->type7()) continue; /* FALLTHROUGH */ case OspfTypes::NSSA: if ((*i)->external()) continue; switch(_ospf.get_version()) { case OspfTypes::V2: break; case OspfTypes::V3: // Note unknown LSAs are captured in this test. if (!(*i)->link_local_scope() && !(*i)->area_scope()) continue; #if 0 if (!(*i)->known()) { XLOG_INFO("%s", cstring(*(*i))); (*i)->set_tracing(true); } #endif break; } break; } const Lsa_header& lsah = (*i)->get_header(); size_t index; LsaSearch search = compare_lsa(lsah, index); #ifdef DEBUG_LOGGING debug_msg("Searching for %s\n", cstring(*(*i))); switch(search) { case NOMATCH: debug_msg("No match\n"); break; case NEWER: debug_msg("is newer than \n%s\n", cstring(*_db[index])); break; case OLDER: debug_msg("is older than \n%s\n", cstring(*_db[index])); break; case EQUIVALENT: debug_msg("is equivalent to \n%s\n", cstring(*_db[index])); break; } #endif // (4) MaxAge if (OspfTypes::MaxAge == lsah.get_ls_age()) { if (NOMATCH == search) { if (!neighbours_exchange_or_loading()) { delayed_ack.push_back(lsah); continue; } #ifndef MAX_AGE_IN_DATABASE // The continue is here to stop this incoming MaxAge LSA // being flooded back to the sender while in state // EXCHANGE or LOADING, otherwise neighbours in this // condition will respond to MaxAge LSAs with MaxAge LSAs. // The first time we see a MaxAge LSA we *should* // store the LSA in the database and add to // retransmission lists, when the LSA is ACK'd and no // longer appears on the retransmission lists it // should be removed. // In this code path we don't store the MaxAge LSA in // the database so we can't flood. // We are *probably* saved by the fact that if a // router had an LSA that was responded to by a MaxAge // LSA then the MaxAge LSA will already be on the // retransmission lists. continue; #endif } } // OSPFv3 only. // If this router is the designated router for this peer and // this is a Link-LSA it may be necessary for the router to // generate a new Intra-Area-Prefix-LSA. bool invoke_update_intra_area_prefix_lsa = false; switch(_ospf.get_version()) { case OspfTypes::V2: break; case OspfTypes::V3: if (!is_router_dr) break; LinkLsa *ollsa = 0; LinkLsa *nllsa = 0; switch(search) { case NEWER: ollsa = dynamic_cast(_db[index].get()); if (0 == ollsa) break; /*FALLTHROUGH*/ case NOMATCH: nllsa = dynamic_cast((*i).get()); if (0 == nllsa) break; invoke_update_intra_area_prefix_lsa = check_link_lsa(nllsa, ollsa); break; case OLDER: case EQUIVALENT: break; } break; } switch(search) { case NOMATCH: case NEWER: { // (5) New or newer LSA. // (a) If this LSA is too recent drop it. if (NEWER == search) { TimeVal then; _db[index]->get_creation_time(then); if ((now - then) < TimeVal(OspfTypes::MinLSArrival, 0)) { debug_msg("Rejecting LSA last one arrived less than " "%d second(s) ago\n %s\n%s", OspfTypes::MinLSArrival, cstring(*(*i)) , cstring(*(_db[index]))); XLOG_TRACE(_ospf.trace()._input_errors, "Rejecting LSA last one arrived less than " "%d second(s) ago\n%s\n%s", OspfTypes::MinLSArrival, cstring(*(*i)) , cstring(*(_db[index]))); continue; } } // This is out of sequence but doing it later makes no sense. // (f) Self orignating LSAs // RFC 2328 Section 13.4. Receiving self-originated LSAs bool match = false; if (NEWER == search) match = _db[index]->get_self_originating(); if (self_originated((*i), match, index)) { if (NOMATCH == search) publish_all((*i)); else publish_all(_db[index]); continue; } // (b) Flood this LSA to all of our neighbours. // RFC 2328 Section 13.3. Next step in the flooding procedure // If this is an AS-external-LSA send it to all areas. if ((*i)->external()) external_flood_all_areas((*i)); if ((*i)->type7()) external_type7_translate((*i)); switch(_ospf.get_version()) { case OspfTypes::V2: break; case OspfTypes::V3: if (!(*i)->known() && (*i)->as_scope()) external_flood_all_areas((*i)); break; } // Set to true if the LSA was multicast out of this // interface. If it was, there is no requirement to send an // ACK. bool multicast_on_peer; publish(peerid, nid, (*i), multicast_on_peer); // (c) Remove the current copy from neighbours // retransmission lists. If this is a new LSA then there // can be no version of this LSA on the neighbours // retransmission lists. If this is a newer version of an // existing LSA then the old LSA will be invalidated in // update LSA, therefore the neighbours will not try and // transmit it. // (d) Install the new LSA. if (NOMATCH == search) { add_lsa((*i)); } else { if ((*i)->external()) { // The LSA that was matched should have been // invalidated by the external code. So the new // LSA just needs to added to the database. XLOG_ASSERT(!_db[index]->valid()); add_lsa((*i)); } else { update_lsa((*i), index); } } // Start aging this LSA if its not a AS-External-LSA if (!(*i)->external()) age_lsa((*i)); routing_add(*i, NOMATCH != search); // (e) Possibly acknowledge this LSA. // RFC 2328 Section 13.5 Sending Link State Acknowledgment Packets debug_msg("LSA multicast out on peer: %s " "LSA from DR: %s interface in state backup: %s\n", bool_c_str(multicast_on_peer), bool_c_str(is_neighbour_dr), bool_c_str(is_router_bdr)); if (!multicast_on_peer) { if ((is_router_bdr && is_neighbour_dr) || !is_router_bdr) delayed_ack.push_back(lsah); } // Too late to do this now, its after (a). // (f) Self orignating LSAs // RFC 2328 Section 13.4. Receiving self-originated LSAs // OSPFv3 only. // If this router is the designated router for this peer and // this is a Link-LSA it may be necessary for the router to // generate a new Intra-Area-Prefix-LSA. switch(_ospf.get_version()) { case OspfTypes::V2: break; case OspfTypes::V3: if (invoke_update_intra_area_prefix_lsa) update_intra_area_prefix_lsa(peerid); break; } } break; case OLDER: // XXX - Is this really where case (6) belongs? // (6) If this LSA is on the sending neighbours request // list something has gone wrong. if (on_link_state_request_list(peerid, nid, (*i))) { event_bad_link_state_request(peerid, nid); goto out; } // (8) The database copy is more recent than the received LSA. // The LSA's LS sequence number is wrapping. if (_db[index]->get_header().get_ls_age() == OspfTypes::MaxAge && _db[index]->get_header().get_ls_sequence_number() == OspfTypes::MaxSequenceNumber) continue; // We have a more up to date copy of this LSA than our // neighbour so blast this LSA directly back to this neighbour. send_lsa(peerid, nid, _db[index]); break; case EQUIVALENT: // (7) The LSAs are equivalent. // (a) This might be an "implied acknowledgement". if (_db[index]->exists_nack(nid)) { _db[index]->remove_nack(nid); // (b) An "implied acknowledgement". if (is_router_bdr && is_neighbour_dr) delayed_ack.push_back(lsah); } else { // (b) Not an "implied acknowledgement". direct_ack.push_back(lsah); } break; } } out: push_lsas("receive_lsas"); external_push_all_areas(); routing_end(); _ospf.get_peer_manager().external_suppress_lsas(_area); } template bool AreaRouter::age_lsa(Lsa::LsaRef lsar) { size_t index; XLOG_ASSERT(!lsar->get_self_originating()); if (!find_lsa(lsar, index)) { XLOG_WARNING("LSA not in database: %s", cstring(*lsar)); return false; } lsar->get_timer() = _ospf.get_eventloop(). new_oneoff_after(TimeVal(OspfTypes::MaxAge - lsar->get_header().get_ls_age(), 0), callback(this, &AreaRouter::maxage_reached, lsar,index)); return true; } template void AreaRouter::maxage_reached(Lsa::LsaRef lsar, size_t i) { size_t index; XLOG_ASSERT(!lsar->external()); if (!find_lsa(lsar, index)) { XLOG_WARNING("LSA not in database: %s", cstring(*lsar)); return; } if (i != index) { XLOG_WARNING("Indexes don't match %u != %u %s", XORP_UINT_CAST(i), XORP_UINT_CAST(index), cstring(*_db[index])); return; } #ifdef PARANOIA if (!lsar->get_self_originating()) { TimeVal now; _ospf.get_eventloop().current_time(now); if (!lsar->maxage()) lsar->update_age(now); } #endif if (OspfTypes::MaxAge != lsar->get_header().get_ls_age()) XLOG_FATAL("LSA is not MaxAge %s", cstring(*lsar)); #ifndef MAX_AGE_IN_DATABASE delete_lsa(lsar, index, false /* Don't invalidate */); #endif publish_all(lsar); // Clear the timer otherwise there is a circular dependency. // The LSA contains a XorpTimer that points back to the LSA. lsar->get_timer().clear(); } template void AreaRouter::premature_aging(Lsa::LsaRef lsar, size_t i) { XLOG_ASSERT(lsar->get_self_originating()); if (!lsar->maxage()) lsar->set_maxage(); maxage_reached(lsar, i); } template void AreaRouter::increment_sequence_number(Lsa::LsaRef lsar) { XLOG_ASSERT(lsar->get_self_originating()); if (lsar->max_sequence_number()) { max_sequence_number_reached(lsar); return; } lsar->increment_sequence_number(); } template void AreaRouter::update_age_and_seqno(Lsa::LsaRef lsar, const TimeVal& now) { XLOG_ASSERT(lsar->get_self_originating()); if (lsar->max_sequence_number()) { max_sequence_number_reached(lsar); return; } lsar->update_age_and_seqno(now); } template void AreaRouter::max_sequence_number_reached(Lsa::LsaRef lsar) { XLOG_ASSERT(lsar->get_self_originating()); // Under normal circumstances this code path will be reached // every 680 years. XLOG_INFO("LSA reached MaxSequenceNumber %s", cstring(*lsar)); if (!lsar->maxage()) lsar->set_maxage(); if (_reincarnate.empty()) _reincarnate_timer = _ospf.get_eventloop(). new_periodic_ms(1000, callback(this, &AreaRouter::reincarnate)); _reincarnate.push_back(lsar); } template bool AreaRouter::reincarnate() { list::iterator i = _reincarnate.begin(); while(i != _reincarnate.end()) { XLOG_ASSERT((*i)->valid()); XLOG_ASSERT((*i)->maxage()); XLOG_ASSERT((*i)->max_sequence_number()); if ((*i)->empty_nack()) { TimeVal now; _ospf.get_eventloop().current_time(now); (*i)->revive(now); XLOG_INFO("Reviving an LSA that reached MaxSequenceNumber %s", cstring(*(*i))); publish_all((*i)); _reincarnate.erase(i++); } else { i++; } } if (_reincarnate.empty()) return false; return true; } template bool AreaRouter::add_lsa(Lsa::LsaRef lsar) { size_t index; XLOG_ASSERT(!find_lsa(lsar, index)); XLOG_ASSERT(lsar->valid()); // If there are no readers we can put this LSA into an empty slot. if (0 == _readers && !_empty_slots.empty()) { size_t esi = _empty_slots.front(); if (esi >= _last_entry) _last_entry = esi + 1; _db[esi] = lsar; _empty_slots.pop_front(); return true; } if (_last_entry < _allocated_entries) { _db[_last_entry] = lsar; } else { _db.push_back(lsar); _allocated_entries++; } _last_entry++; return true; } template bool AreaRouter::delete_lsa(Lsa::LsaRef lsar, size_t index, bool invalidate) { Lsa_header& dblsah = _db[index]->get_header(); XLOG_ASSERT(dblsah.get_ls_type() == lsar->get_header().get_ls_type()); XLOG_ASSERT(dblsah.get_link_state_id() == lsar->get_header().get_link_state_id()); XLOG_ASSERT(dblsah.get_advertising_router() == lsar->get_header().get_advertising_router()); XLOG_ASSERT(_db[index]->valid()); // This LSA is being deleted remove it from the routing computation. routing_delete(lsar); _db[index]->invalidate(invalidate); _db[index] = _invalid_lsa; _empty_slots.push_back(index); // _last_entry points one past the last entry, if the deleted LSA // was at the end of the array then the _last_entry pointer can be // decreased. while(0 != index && index + 1 == _last_entry && !_db[index]->valid() && 0 != _last_entry) { _last_entry--; index--; } return true; } template bool AreaRouter::update_lsa(Lsa::LsaRef lsar, size_t index) { Lsa_header& dblsah = _db[index]->get_header(); XLOG_ASSERT(dblsah.get_ls_type() == lsar->get_header().get_ls_type()); XLOG_ASSERT(dblsah.get_link_state_id() == lsar->get_header().get_link_state_id()); XLOG_ASSERT(dblsah.get_advertising_router() == lsar->get_header().get_advertising_router()); XLOG_ASSERT(_db[index]->valid()); // A LSA arriving over the wire should never replace a // self originating LSA. XLOG_ASSERT(!_db[index]->get_self_originating()); if (0 == _readers) { _db[index]->invalidate(); _db[index] = lsar; } else { delete_lsa(lsar, index, true /* Mark the LSA as invalid */); add_lsa(lsar); } return true; } template bool AreaRouter::find_lsa(const Ls_request& lsr, size_t& index) const { for(index = 0 ; index < _last_entry; index++) { if (!_db[index]->valid()) continue; Lsa_header& dblsah = _db[index]->get_header(); if (dblsah.get_ls_type() != lsr.get_ls_type()) continue; if (dblsah.get_link_state_id() != lsr.get_link_state_id()) continue; if (dblsah.get_advertising_router() != lsr.get_advertising_router()) continue; return true; } return false; } template bool AreaRouter::find_lsa(Lsa::LsaRef lsar, size_t& index) const { const Lsa_header lsah = lsar->get_header(); Ls_request lsr(_ospf.get_version(), lsah.get_ls_type(), lsah.get_link_state_id(), lsah.get_advertising_router()); return find_lsa(lsr, index); } template bool AreaRouter::find_network_lsa(uint32_t link_state_id, size_t& index) const { uint32_t ls_type = NetworkLsa(_ospf.get_version()).get_ls_type(); for(index = 0 ; index < _last_entry; index++) { if (!_db[index]->valid()) continue; Lsa_header& dblsah = _db[index]->get_header(); if (dblsah.get_ls_type() != ls_type) continue; if (dblsah.get_link_state_id() != link_state_id) continue; // Note we deliberately don't check for advertising router. return true; } return false; } template bool AreaRouter::find_router_lsa(uint32_t advertising_router, size_t& index) const { XLOG_ASSERT(OspfTypes::V3 == _ospf.get_version()); uint32_t ls_type = RouterLsa(_ospf.get_version()).get_ls_type(); // The index is set by the caller. for(; index < _last_entry; index++) { if (!_db[index]->valid()) continue; Lsa_header& dblsah = _db[index]->get_header(); if (dblsah.get_ls_type() != ls_type) continue; if (dblsah.get_advertising_router() != advertising_router) continue; // Note we deliberately don't check for the Link State ID. return true; } return false; } /** * RFC 2328 Section 13.1 Determining which LSA is newer. */ template typename AreaRouter::LsaSearch AreaRouter::compare_lsa(const Lsa_header& candidate, const Lsa_header& current) const { debug_msg("router id: %s\n", pr_id(_ospf.get_router_id()).c_str()); debug_msg("\ncandidate: %s\n current: %s\n", cstring(candidate), cstring(current)); const int32_t candidate_seqno = candidate.get_ls_sequence_number(); const int32_t current_seqno = current.get_ls_sequence_number(); if (current_seqno != candidate_seqno) { if (current_seqno > candidate_seqno) return OLDER; if (current_seqno < candidate_seqno) return NEWER; } if (current.get_ls_checksum() > candidate.get_ls_checksum()) return OLDER; if (current.get_ls_checksum() < candidate.get_ls_checksum()) return NEWER; if (current.get_ls_age() == candidate.get_ls_age()) return EQUIVALENT; if (current.get_ls_age() == OspfTypes::MaxAge) return OLDER; if (candidate.get_ls_age() == OspfTypes::MaxAge) return NEWER; if(abs(current.get_ls_age() - candidate.get_ls_age()) > OspfTypes::MaxAgeDiff) { return candidate.get_ls_age() < current.get_ls_age() ? NEWER : OLDER; } // These two LSAs are identical. return EQUIVALENT; } template typename AreaRouter::LsaSearch AreaRouter::compare_lsa(const Lsa_header& lsah, size_t& index) const { Ls_request lsr(_ospf.get_version(), lsah.get_ls_type(), lsah.get_link_state_id(), lsah.get_advertising_router()); if (find_lsa(lsr, index)) { if (!_db[index]->maxage()) { // Update the age before checking this field. TimeVal now; _ospf.get_eventloop().current_time(now); _db[index]->update_age(now); } return compare_lsa(lsah, _db[index]->get_header()); } return NOMATCH; } template typename AreaRouter::LsaSearch AreaRouter::compare_lsa(const Lsa_header& lsah) const { size_t index; return compare_lsa(lsah, index); } template bool AreaRouter::newer_lsa(const Lsa_header& lsah) const { switch(compare_lsa(lsah)) { case NOMATCH: case NEWER: return true; case EQUIVALENT: case OLDER: return false; } XLOG_UNREACHABLE(); return true; } template bool AreaRouter::get_lsas(const list& reqs, list& lsas) { TimeVal now; _ospf.get_eventloop().current_time(now); list::const_iterator i; for(i = reqs.begin(); i != reqs.end(); i++) { size_t index; if (!find_lsa(*i, index)) { XLOG_WARNING("Unable to find %s", cstring(*i)); return false; } Lsa::LsaRef lsar = _db[index]; // Start the delay timer so we won't transmit any more self // originating LSAs until the appropriate time has passed. if (lsar->get_self_originating()) _queue.fire(); if (!lsar->maxage()) lsar->update_age(now); lsas.push_back(lsar); } return true; } template DataBaseHandle AreaRouter::open_database(OspfTypes::PeerID peerid, bool& empty) { // While there are readers no entries can be add to the database // before the _last_entry. _readers++; // Snapshot the current last entry position. While the database is // open (_readers > 0) new entries will be added past _last_entry. DataBaseHandle dbh = DataBaseHandle(true, _last_entry, peerid); empty = !subsequent(dbh); return dbh; } template bool AreaRouter::valid_entry_database(OspfTypes::PeerID peerid, size_t index) { Lsa::LsaRef lsar = _db[index]; // This LSA is not valid. if (!lsar->valid()) return false; if (!lsar->maxage()) { TimeVal now; _ospf.get_eventloop().current_time(now); lsar->update_age(now); } switch(_ospf.get_version()) { case OspfTypes::V2: break; case OspfTypes::V3: if (lsar->link_local_scope() && lsar->get_peerid() != peerid) return false; break; } if (lsar->maxage()) { #ifdef MAX_AGE_IN_DATABASE // XXX - This is the only way that a MaxAge LSA gets removed // from the database. When this code is removed make // valid_entry and subsequent const again. // If we find a LSA with MaxAge in the database and the nack // list is empty it can be removed from the database. if (lsar->empty_nack()) delete_lsa(lsar, index, true /* Invalidate */); #endif return false; } // There is no wire format for this LSA. if (!lsar->available()) return false; return true; } template bool AreaRouter::subsequent(DataBaseHandle& dbh) { bool another = false; for (size_t index = dbh.position(); index < dbh.last(); index++) { if (!valid_entry_database(dbh.get_peerid(), index)) continue; another = true; break; } return another; } template Lsa::LsaRef AreaRouter::get_entry_database(DataBaseHandle& dbh, bool& last) { XLOG_ASSERT(dbh.valid()); uint32_t position; do { position = dbh.position(); if (position >= _db.size()) XLOG_FATAL("Index too far %d length %d", position, XORP_INT_CAST(_db.size())); dbh.advance(last); } while(!valid_entry_database(dbh.get_peerid(), position)); // If this is not the last entry make sure there is a subsequent // valid entry. if (!last) last = !subsequent(dbh); return _db[position]; } template void AreaRouter::close_database(DataBaseHandle& dbh) { XLOG_ASSERT(dbh.valid()); XLOG_ASSERT(0 != _readers); _readers--; if (subsequent(dbh)) XLOG_WARNING("Database closed with entries remaining"); dbh.invalidate(); } template void AreaRouter::clear_database(bool preserve_link_lsas) { for (size_t index = 0 ; index < _last_entry; index++) { if (!_db[index]->valid()) continue; if (_db[index]->external()) { _db[index] = _invalid_lsa; continue; } switch (_ospf.get_version()) { case OspfTypes::V2: break; case OspfTypes::V3: if (preserve_link_lsas && _db[index]->get_self_originating() && dynamic_cast(_db[index].get())) continue; break; } _db[index]->invalidate(); } } template void AreaRouter::maxage_type_database(uint16_t type) { for (size_t index = 0 ; index < _last_entry; index++) { if (!_db[index]->valid()) continue; if (!_db[index]->get_self_originating()) continue; if (_db[index]->get_ls_type() != type) continue; premature_aging(_db[index], index); } } template void AreaRouter::testing_print_link_state_database() const { fprintf(stderr, "****** DATABASE START (testing_print_link_state) ******\n"); for (size_t index = 0 ; index < _last_entry; index++) { Lsa::LsaRef lsar = _db[index]; if (!lsar->valid()) continue; // Please leave this as a fprintf its for debugging only. fprintf(stderr, "%s\n", cstring(*lsar)); } fprintf(stderr, "****** DATABASE END ********\n"); } template bool AreaRouter::update_router_links() { RouterLsa *router_lsa = dynamic_cast(_router_lsa.get()); XLOG_ASSERT(router_lsa); bool empty = router_lsa->get_router_links().empty(); router_lsa->get_router_links().clear(); typename PeerMap::iterator i; for(i = _peers.begin(); i != _peers.end(); i++) { PeerStateRef temp_psr = i->second; if (temp_psr->_up) { typename list::iterator j = temp_psr->_router_links.begin(); for(; j != temp_psr->_router_links.end(); j++) router_lsa->get_router_links().push_back(*j); } } // If we weren't advertising and we still aren't return. if (empty && router_lsa->get_router_links().empty()) return false; PeerManager& pm = _ospf.get_peer_manager(); router_lsa->set_v_bit(pm.virtual_link_endpoint(_area)); switch(_area_type) { case OspfTypes::NORMAL: router_lsa->set_e_bit(pm.as_boundary_router_p()); break; case OspfTypes::STUB: case OspfTypes::NSSA: router_lsa->set_e_bit(false); break; } router_lsa->set_b_bit(pm.area_border_router_p()); switch (_ospf.get_version()) { case OspfTypes::V2: router_lsa->get_header().set_options(get_options()); break; case OspfTypes::V3: router_lsa->set_options(get_options()); break; } TimeVal now; _ospf.get_eventloop().current_time(now); update_age_and_seqno(_router_lsa, now); // Prime this Router-LSA to be refreshed. router_lsa->get_timer() = _ospf.get_eventloop(). new_oneoff_after(TimeVal(OspfTypes::LSRefreshTime, 0), callback(this, &AreaRouter::refresh_router_lsa, /* timer */true)); return true; } template void AreaRouter::refresh_router_lsa(bool timer) { if (update_router_links()) { // publish the router LSA. _queue.add(_router_lsa); switch(_ospf.get_version()) { case OspfTypes::V2: break; case OspfTypes::V3: stub_networksV3(timer); break; } // This new Router-LSA is being announced, hence something has // changed in a link or a transit capability has // changed. Therefore the routing table needs to be recomputed. if (!timer) routing_schedule_total_recompute(); } } template void AreaRouter::stub_networksV3(bool timer) { debug_msg("timer %s\n", bool_c_str(timer)); OspfTypes::Version version = _ospf.get_version(); Ls_request lsr(version, IntraAreaPrefixLsa(version).get_ls_type(), IntraAreaPrefixLsa(version). create_link_state_id(_router_lsa->get_ls_type(), 0), _ospf.get_router_id()); // If the timer fired find that LSA update it and retransmit. if (timer) { size_t index; if (!find_lsa(lsr, index)) return; TimeVal now; _ospf.get_eventloop().current_time(now); update_age_and_seqno(_db[index], now); _queue.add(_db[index]); return; } // Generate the list of prefixes that should be sent. list prefixes; PeerManager& pm = _ospf.get_peer_manager(); typename PeerMap::iterator i; for(i = _peers.begin(); i != _peers.end(); i++) { PeerStateRef temp_psr = i->second; if (temp_psr->_up) { if (temp_psr->_router_links.empty()) { uint32_t interface_id = pm.get_interface_id(i->first); Ls_request lsr(version, LinkLsa(version).get_ls_type(), interface_id, _ospf.get_router_id()); size_t index; if (find_lsa(lsr, index)) { LinkLsa *llsa = dynamic_cast(_db[index].get()); XLOG_ASSERT(llsa); debug_msg("%s\n", cstring(*llsa)); const list& link_prefixes = llsa->get_prefixes(); list::const_iterator i; for (i = link_prefixes.begin(); i != link_prefixes.end(); i++){ IPv6Prefix prefix(version, true); prefix = *i; if (prefix.get_nu_bit() /*|| prefix.get_la_bit()*/) continue; if (prefix.get_network().masked_addr(). is_linklocal_unicast()) continue; prefix.set_metric(0); prefixes.push_back(prefix); } } } else if (configured_virtual_link()) { uint32_t interface_id = pm.get_interface_id(i->first); Ls_request lsr(version, LinkLsa(version).get_ls_type(), interface_id, _ospf.get_router_id()); size_t index; if (find_lsa(lsr, index)) { LinkLsa *llsa = dynamic_cast(_db[index].get()); XLOG_ASSERT(llsa); debug_msg("%s\n", cstring(*llsa)); const list& link_prefixes = llsa->get_prefixes(); list::const_iterator i; for (i = link_prefixes.begin(); i != link_prefixes.end(); i++){ IPv6Prefix prefix(version, true); prefix = *i; if (!prefix.get_la_bit()) continue; if (prefix.get_network().masked_addr(). is_linklocal_unicast()) continue; prefix.set_metric(0); prefixes.push_back(prefix); } } } } } // If there are no prefixes to send then remove the LSA if it was // previously sending anything. if (prefixes.empty()) { debug_msg("No prefixes computed\n"); size_t index; if (!find_lsa(lsr, index)) return; premature_aging(_db[index], index); return; } // If there is no Intra-Area-Prefix-LSA then generate one. size_t index; if (!find_lsa(lsr, index)) { if (!generate_intra_area_prefix_lsa(OspfTypes::ALLPEERS, _router_lsa, 0)) { XLOG_WARNING("Unable to generate an Intra-Area-Prefix-LSA"); return; } if (!find_lsa(lsr, index)) XLOG_FATAL("Unable to find %s", cstring(lsr)); Lsa::LsaRef lsar = _db[index]; IntraAreaPrefixLsa *iaplsa = dynamic_cast(lsar.get()); XLOG_ASSERT(iaplsa); list& nprefixes = iaplsa->get_prefixes(); nprefixes.insert(nprefixes.begin(), prefixes.begin(), prefixes.end()); TimeVal now; _ospf.get_eventloop().current_time(now); update_age_and_seqno(lsar, now); _queue.add(lsar); return; } // An Intra-Area-Prefix-LSA already exists if it is different to // the newly computed one then update the old one and publish. Lsa::LsaRef lsar = _db[index]; IntraAreaPrefixLsa *iaplsa = dynamic_cast(lsar.get()); XLOG_ASSERT(iaplsa); list& oprefixes = iaplsa->get_prefixes(); list::iterator j, k; bool found = false; for (j = oprefixes.begin(); j != oprefixes.end(); j++) { for (k = prefixes.begin(); k != prefixes.end(); k++) { if (*j == *k) { found = true; break; } } if (!found) break; } // They are identical nothing to do. if (found) return; oprefixes.clear(); oprefixes.insert(oprefixes.begin(), prefixes.begin(), prefixes.end()); TimeVal now; _ospf.get_eventloop().current_time(now); update_age_and_seqno(lsar, now); _queue.add(lsar); } template void AreaRouter::publish(const OspfTypes::PeerID peerid, const OspfTypes::NeighbourID nid, Lsa::LsaRef lsar, bool &multicast_on_peer) const { debug_msg("Publish: %s\n", cstring(*lsar)); TimeVal now; _ospf.get_eventloop().current_time(now); // Update the age field unless its self originating. if (lsar->get_self_originating()) { // if (OspfTypes::MaxSequenceNumber == lsar->get_ls_sequence_number()) // XLOG_FATAL("TBD: Flush this LSA and generate a new LSA"); } else { if (!lsar->maxage()) lsar->update_age(now); } typename PeerMap::const_iterator i; for(i = _peers.begin(); i != _peers.end(); i++) { PeerStateRef temp_psr = i->second; if (temp_psr->_up) { bool multicast; if (!_ospf.get_peer_manager(). queue_lsa(i->first, peerid, nid, lsar, multicast)) XLOG_FATAL("Unable to queue LSA"); // Did this LSA get broadcast/multicast on the // peer/interface that it came in on. if (peerid == i->first) multicast_on_peer = multicast; } } } template void AreaRouter::publish_all(Lsa::LsaRef lsar) { debug_msg("Publish: %s\n", cstring(*lsar)); bool multicast_on_peer; publish(OspfTypes::ALLPEERS, OspfTypes::ALLNEIGHBOURS, lsar, multicast_on_peer); push_lsas("publish_all"); // NOTE: a push after every LSA. } template void AreaRouter::push_lsas(const char* msg) { typename PeerMap::iterator i; for(i = _peers.begin(); i != _peers.end(); i++) { PeerStateRef temp_psr = i->second; if (temp_psr->_up) { if (!_ospf.get_peer_manager().push_lsas(i->first, msg)) XLOG_FATAL("Unable to push LSAs"); } } } template void AreaRouter::external_type7_translate(Lsa::LsaRef lsar) { Type7Lsa *t7 = dynamic_cast(lsar.get()); XLOG_ASSERT(t7); switch (_ospf.get_version()) { case OspfTypes::V2: if (t7->get_forwarding_address_ipv4() == IPv4::ZERO()) return; break; case OspfTypes::V3: if (!t7->get_f_bit()) return; break; } // If the propogate bit isn't set there is nothing todo. if (!external_propagate_bit(lsar)) return; switch(_translator_state) { case OspfTypes::ENABLED: case OspfTypes::ELECTED: break; case OspfTypes::DISABLED: return; break; } _external_flooding = true; // Convert this Type-7-LSA into an AS-External-LSA and flood. external_flood_all_areas(external_generate_external(lsar)); } template void AreaRouter::external_flood_all_areas(Lsa::LsaRef lsar) { debug_msg("Flood all areas %s\n", cstring(*lsar)); _external_flooding = true; PeerManager& pm = _ospf.get_peer_manager(); pm.external_announce(_area, lsar); } template void AreaRouter::external_push_all_areas() { if (_external_flooding) { PeerManager& pm = _ospf.get_peer_manager(); pm.external_announce_complete(_area); _external_flooding = false; } } template bool AreaRouter::neighbours_exchange_or_loading() const { typename PeerMap::const_iterator i; for(i = _peers.begin(); i != _peers.end(); i++) { if (_ospf.get_peer_manager(). neighbours_exchange_or_loading(i->first, _area)) return true; } return false; } template bool AreaRouter::neighbour_at_least_two_way(OspfTypes::RouterID rid) const { if (_ospf.get_testing()) return true; typename PeerMap::const_iterator i; for(i = _peers.begin(); i != _peers.end(); i++) { bool twoway; if (_ospf.get_peer_manager(). neighbour_at_least_two_way(i->first, _area, rid, twoway)) return twoway; } return false; } template bool AreaRouter::get_neighbour_address(OspfTypes::RouterID rid, uint32_t interface_id, A& neighbour_address) const { typename PeerMap::const_iterator i; for(i = _peers.begin(); i != _peers.end(); i++) { if (_ospf.get_peer_manager().get_neighbour_address(i->first, _area, rid, interface_id, neighbour_address)) return true; } return false; } template bool AreaRouter::on_link_state_request_list(const OspfTypes::PeerID peerid, const OspfTypes::NeighbourID nid, Lsa::LsaRef lsar) const { return _ospf.get_peer_manager(). on_link_state_request_list(peerid, _area, nid, lsar); } template bool AreaRouter::event_bad_link_state_request(const OspfTypes::PeerID peerid, const OspfTypes::NeighbourID nid) const { return _ospf.get_peer_manager(). event_bad_link_state_request(peerid, _area, nid); } template bool AreaRouter::send_lsa(const OspfTypes::PeerID peerid, const OspfTypes::NeighbourID nid, Lsa::LsaRef lsar) const { return _ospf.get_peer_manager().send_lsa(peerid, _area, nid, lsar); } template <> bool AreaRouter::self_originated_by_interface(Lsa::LsaRef lsar, IPv4) const { if (0 == dynamic_cast(lsar.get())) return false; IPv4 address(htonl(lsar->get_header().get_link_state_id())); return _ospf.get_peer_manager().known_interface_address(address); } template <> bool AreaRouter::self_originated_by_interface(Lsa::LsaRef, IPv6) const { XLOG_FATAL("Only IPv4 not IPv6"); return false; } template bool AreaRouter::self_originated(Lsa::LsaRef lsar, bool lsa_exists, size_t index) { // RFC 2328 Section 13.4. Receiving self-originated LSAs debug_msg("lsar: %s\nexists: %s index: %u\n", cstring((*lsar)), bool_c_str(lsa_exists), XORP_UINT_CAST(index)); if (lsa_exists) debug_msg("database copy: %s\n", cstring((*_db[index]))); bool originated = lsa_exists; if (!originated) { if (lsar->get_header().get_advertising_router() == _ospf.get_router_id()) { originated = true; } else { switch (_ospf.get_version()) { case OspfTypes::V2: if (self_originated_by_interface(lsar)) originated = true; break; case OspfTypes::V3: break; } } } if (!originated) return false; debug_msg("router id: %s\n", pr_id(_ospf.get_router_id()).c_str()); debug_msg("self originated: %s\n", cstring((*lsar))); // If we got this far this is a self-originated LSA that needs to // be removed from the wild. // A database copy of this LSA exists. The new LSA that arrived is // newer than the database copy. Copy the sequence number from the // new LSA into the database LSA increment it and flood. if (lsa_exists) { _db[index]->set_ls_sequence_number(lsar->get_ls_sequence_number()); lsar = _db[index]; increment_sequence_number(lsar); lsar->encode(); return true; } // This is a spurious LSA that was probably originated by us in // the past just get rid of it by setting it to MaxAge. if (!lsar->maxage()) lsar->set_maxage(); #ifdef MAX_AGE_IN_DATABASE debug_msg("Adding MaxAge lsa to database\n%s\n", cstring(*lsar)); add_lsa(lsar); #endif return true; } template void AreaRouter::RouterVertex(Vertex& v) { v.set_version(_ospf.get_version()); v.set_type(OspfTypes::Router); v.set_nodeid(_ospf.get_router_id()); v.set_origin(true); switch (_ospf.get_version()) { case OspfTypes::V2: v.set_lsa(_router_lsa); break; case OspfTypes::V3: v.get_lsas().push_back(_router_lsa); break; } } template void AreaRouter::routing_begin() { } template void AreaRouter::routing_add(Lsa::LsaRef lsar, bool known) { debug_msg("%s known %s\n", cstring(*lsar), bool_c_str(known)); } template void AreaRouter::routing_delete(Lsa::LsaRef lsar) { debug_msg("%s\n", cstring(*lsar)); routing_schedule_total_recompute(); } template void AreaRouter::routing_end() { routing_schedule_total_recompute(); } template void AreaRouter::routing_schedule_total_recompute() { if (_routing_recompute_timer.scheduled()) return; _routing_recompute_timer = _ospf.get_eventloop(). new_oneoff_after(TimeVal(_routing_recompute_delay, 0), callback(this, &AreaRouter::routing_timer)); } template void AreaRouter::routing_timer() { routing_total_recompute(); } template void AreaRouter::routing_total_recompute() { switch (_ospf.get_version()) { case OspfTypes::V2: routing_total_recomputeV2(); break; case OspfTypes::V3: routing_total_recomputeV3(); break; } } template <> void AreaRouter:: routing_area_rangesV2(const list >& r); template <> void AreaRouter::routing_inter_areaV2(); template <> void AreaRouter::routing_transit_areaV2(); template <> void AreaRouter::routing_as_externalV2(); template <> void AreaRouter:: routing_area_rangesV3(const list >& r, LsaTempStore& lsa_temp_store); template <> void AreaRouter::routing_inter_areaV3(); template <> void AreaRouter::routing_transit_areaV3(); template <> void AreaRouter::routing_as_externalV3(); template <> void AreaRouter::routing_total_recomputeV2() { #ifdef DEBUG_LOGGING //testing_print_link_state_database(); #endif // RFC 2328 16.1. Calculating the shortest-path tree for an area Spt spt(_ospf.trace()._spt); bool transit_capability = false; // Add this router to the SPT table. Vertex rv; RouterVertex(rv); spt.add_node(rv); spt.set_origin(rv); for (size_t index = 0 ; index < _last_entry; index++) { Lsa::LsaRef lsar = _db[index]; if (!lsar->valid() || lsar->maxage()) continue; RouterLsa *rlsa; if (0 != (rlsa = dynamic_cast(lsar.get()))) { if (rlsa->get_v_bit()) transit_capability = true; Vertex v; v.set_version(_ospf.get_version()); v.set_type(OspfTypes::Router); v.set_nodeid(rlsa->get_header().get_link_state_id()); v.set_lsa(lsar); // Don't add this router back again. if (spt.exists_node(v)) { debug_msg("%s Router %s\n", pr_id(_ospf.get_router_id()).c_str(), cstring(v)); if (rv == v) v.set_origin(rv.get_origin()); } else { debug_msg("%s Add %s\n", pr_id(_ospf.get_router_id()).c_str(), cstring(v)); spt.add_node(v); } debug_msg("%s Router-Lsa %s\n", pr_id(_ospf.get_router_id()).c_str(), cstring(*rlsa)); switch(_ospf.get_version()) { case OspfTypes::V2: routing_router_lsaV2(spt, v, rlsa); break; case OspfTypes::V3: routing_router_lsaV3(spt, v, rlsa); break; } } } // If the backbone area is configured to generate summaries and // the transit capability of this area just changed then all the // candidate summary routes need to be pushed through this area again. if (get_transit_capability() != transit_capability) { set_transit_capability(transit_capability); PeerManager& pm = _ospf.get_peer_manager(); if (pm.area_range_configured(OspfTypes::BACKBONE)) pm.summary_push(_area); } RoutingTable& routing_table = _ospf.get_routing_table(); routing_table.begin(_area); // Compute the SPT. list > r; spt.compute(r); // Compute the area range summaries. routing_area_rangesV2(r); start_virtual_link(); list >::const_iterator ri; for(ri = r.begin(); ri != r.end(); ri++) { debug_msg("Add route: Node: %s -> Nexthop %s\n", cstring(ri->node()), cstring(ri->nexthop())); Vertex node = ri->node(); Lsa::LsaRef lsar = node.get_lsa(); RouterLsa *rlsa; NetworkLsa *nlsa; RouteEntry route_entry; IPNet net; route_entry.set_destination_type(node.get_type()); if (OspfTypes::Router == node.get_type()) { rlsa = dynamic_cast(lsar.get()); XLOG_ASSERT(rlsa); check_for_virtual_linkV2((*ri), _router_lsa); if (!(rlsa->get_e_bit() || rlsa->get_b_bit())) continue; // Originating routers Router ID. route_entry.set_router_id(rlsa->get_header().get_link_state_id()); IPv4 addr; XLOG_ASSERT(find_interface_address(ri->prevhop().get_lsa(), lsar, addr)); net = IPNet(addr, IPv4::ADDR_BITLEN); route_entry.set_area_border_router(rlsa->get_b_bit()); route_entry.set_as_boundary_router(rlsa->get_e_bit()); } else { nlsa = dynamic_cast(lsar.get()); XLOG_ASSERT(nlsa); // route_entry.set_router_id(nlsa->get_header(). // get_advertising_router()); route_entry.set_address(nlsa->get_header().get_link_state_id()); IPv4 addr = IPv4(htonl(route_entry.get_address())); IPv4 mask = IPv4(htonl(nlsa->get_network_mask())); net = IPNet(addr, mask.mask_len()); } // If nexthop point back to the node itself then it it // directly connected. route_entry.set_directly_connected(ri->node() == ri->nexthop()); route_entry.set_path_type(RouteEntry::intra_area); route_entry.set_cost(ri->weight()); route_entry.set_type_2_cost(0); route_entry.set_nexthop(ri->nexthop().get_address_ipv4()); route_entry.set_advertising_router(lsar->get_header(). get_advertising_router()); route_entry.set_area(_area); route_entry.set_lsa(lsar); routing_table_add_entry(routing_table, net, route_entry, __PRETTY_FUNCTION__); } end_virtual_link(); // RFC 2328 Section 16.2. Calculating the inter-area routes if (_ospf.get_peer_manager().internal_router_p() || (backbone() && _ospf.get_peer_manager().area_border_router_p())) routing_inter_areaV2(); // RFC 2328 Section 16.3. Examining transit areas' summary-LSAs if (transit_capability && _ospf.get_peer_manager().area_border_router_p()) routing_transit_areaV2(); // RFC 2328 Section 16.4. Calculating AS external routes routing_as_externalV2(); routing_table.end(); if (backbone()) _ospf.get_peer_manager().routing_recompute_all_transit_areas(); } template <> void AreaRouter::routing_total_recomputeV2() { XLOG_FATAL("OSPFv2 with IPv6 not valid"); } template <> void AreaRouter::routing_total_recomputeV3() { XLOG_FATAL("OSPFv3 with IPv4 not valid"); } /** * Given a list of LSAs return the one with the lowest link state ID. */ inline Lsa::LsaRef get_router_lsa_lowest(const list& lsars) { // A router can have multiple Router-LSAs associated with it, // the options fields in all the Router-LSAs should all be the // same, however if they aren't the LSA with the lowest Link // State ID takes precedence. Unconditionally find and use the // Router-LSA with the lowest Link State ID. list::const_iterator i = lsars.begin(); XLOG_ASSERT(i != lsars.end()); Lsa::LsaRef lsar = *i++; for (; i != lsars.end(); i++) if ((*i)->get_header().get_link_state_id() < lsar->get_header().get_link_state_id()) lsar = *i; return lsar; } template <> void AreaRouter::routing_total_recomputeV3() { #ifdef DEBUG_LOGGING //testing_print_link_state_database(); #endif // RFC 2328 16.1. Calculating the shortest-path tree for an area Spt spt(_ospf.trace()._spt); bool transit_capability = false; // Add this router to the SPT table. Vertex rv; RouterVertex(rv); spt.add_node(rv); spt.set_origin(rv); LsaTempStore lsa_temp_store; for (size_t index = 0 ; index < _last_entry; index++) { Lsa::LsaRef lsar = _db[index]; if (!lsar->valid() || lsar->maxage()) continue; RouterLsa *rlsa; if (0 != (rlsa = dynamic_cast(lsar.get()))) { lsa_temp_store.add_router_lsa(lsar); if (rlsa->get_v_bit()) transit_capability = true; Vertex v; v.set_version(_ospf.get_version()); v.set_type(OspfTypes::Router); // In OSPFv3 a router may generate multiple Router-LSAs, // use the router ID as the nodeid. v.set_nodeid(rlsa->get_header().get_advertising_router()); v.get_lsas().push_back(lsar); // Don't add this router back again. if (spt.exists_node(v)) { debug_msg("%s Router %s\n", pr_id(_ospf.get_router_id()).c_str(), cstring(v)); if (rv == v) { v.set_origin(rv.get_origin()); } else { // XLOG_WARNING("TBD: Add LSA to vertex"); // v = spt.get_node(v); // v.get_lsas().push_back(lsar); } } else { debug_msg("%s Add %s\n", pr_id(_ospf.get_router_id()).c_str(), cstring(v)); spt.add_node(v); } debug_msg("%s Router-Lsa %s\n", pr_id(_ospf.get_router_id()).c_str(), cstring(*rlsa)); switch(_ospf.get_version()) { case OspfTypes::V2: routing_router_lsaV2(spt, v, rlsa); break; case OspfTypes::V3: routing_router_lsaV3(spt, v, rlsa); break; } } else { IntraAreaPrefixLsa *iaplsa; if (0 != (iaplsa = dynamic_cast(lsar.get()))) lsa_temp_store.add_intra_area_prefix_lsa(iaplsa); } } // If the backbone area is configured to generate summaries and // the transit capability of this area just changed then all the // candidate summary routes need to be pushed through this area again. if (get_transit_capability() != transit_capability) { set_transit_capability(transit_capability); PeerManager& pm = _ospf.get_peer_manager(); if (pm.area_range_configured(OspfTypes::BACKBONE)) pm.summary_push(_area); } RoutingTable& routing_table = _ospf.get_routing_table(); routing_table.begin(_area); // Compute the SPT. list > r; spt.compute(r); // Compute the area range summaries. routing_area_rangesV3(r, lsa_temp_store); start_virtual_link(); list >::const_iterator ri; for(ri = r.begin(); ri != r.end(); ri++) { debug_msg("Add route: Node: %s -> Nexthop %s\n", cstring(ri->node()), cstring(ri->nexthop())); Vertex node = ri->node(); list& lsars = node.get_lsas(); list::iterator i = lsars.begin(); XLOG_ASSERT(i != lsars.end()); Lsa::LsaRef lsar = *i++; #if 0 if (OspfTypes::Router == node.get_type()) { for (; i != lsars.end(); i++) if ((*i)->get_header().get_link_state_id() < lsar->get_header().get_link_state_id()) lsar = *i; } else { XLOG_ASSERT(1 == lsars.size()); } #endif if (OspfTypes::Router == node.get_type()) { lsar = get_router_lsa_lowest(lsa_temp_store. get_router_lsas(node.get_nodeid())); RouterLsa *rlsa = dynamic_cast(lsar.get()); XLOG_ASSERT(rlsa); check_for_virtual_linkV3((*ri), _router_lsa, lsa_temp_store); const list& lsai = lsa_temp_store.get_intra_area_prefix_lsas(node.get_nodeid()); if (!lsai.empty()) { RouteEntry route_entry; route_entry.set_destination_type(OspfTypes::Network); // route_entry.set_router_id(rlsa->get_header(). // get_advertising_router()); // route_entry.set_directly_connected(ri->node() == // ri->nexthop()); route_entry.set_directly_connected(false); route_entry.set_path_type(RouteEntry::intra_area); // route_entry.set_cost(ri->weight()); route_entry.set_type_2_cost(0); if (IPv6::ZERO() == ri->nexthop().get_address_ipv6()) { IPv6 global_address; if (!find_global_address(rlsa->get_header(). get_advertising_router(), rlsa->get_ls_type(), lsa_temp_store, global_address)) continue; route_entry.set_nexthop(global_address); route_entry.set_nexthop_id(OspfTypes::UNUSED_INTERFACE_ID); } else { route_entry.set_nexthop(ri->nexthop().get_address_ipv6()); route_entry.set_nexthop_id(ri->nexthop().get_nexthop_id()); } route_entry.set_advertising_router(lsar->get_header(). get_advertising_router()); route_entry.set_area(_area); route_entry.set_lsa(lsar); list prefixes; associated_prefixesV3(rlsa->get_ls_type(), 0, lsai, prefixes); list::iterator j; for (j = prefixes.begin(); j != prefixes.end(); j++) { if (j->get_nu_bit()) continue; if (j->get_network().contains(route_entry.get_nexthop())) continue; route_entry.set_cost(ri->weight() + j->get_metric()); routing_table_add_entry(routing_table, j->get_network(), route_entry, __PRETTY_FUNCTION__); } } if (rlsa->get_e_bit() || rlsa->get_b_bit()) { RouteEntry route_entry; route_entry.set_destination_type(OspfTypes::Router); route_entry.set_router_id(rlsa->get_header(). get_advertising_router()); route_entry.set_directly_connected(ri->node() == ri->nexthop()); route_entry.set_path_type(RouteEntry::intra_area); route_entry.set_cost(ri->weight()); route_entry.set_type_2_cost(0); // If this is a virtual link use the global address if // available. IPv6 router_address; if (IPv6::ZERO() == ri->nexthop().get_address_ipv6()) { if (!find_global_address(rlsa->get_header(). get_advertising_router(), rlsa->get_ls_type(), lsa_temp_store, router_address)) continue; } else { route_entry.set_nexthop(ri->nexthop().get_address_ipv6()); route_entry.set_nexthop_id(ri->nexthop().get_nexthop_id()); router_address = ri->nexthop().get_address_ipv6(); } route_entry.set_advertising_router(lsar->get_header(). get_advertising_router()); route_entry.set_area(_area); route_entry.set_lsa(lsar); route_entry.set_area_border_router(rlsa->get_b_bit()); route_entry.set_as_boundary_router(rlsa->get_e_bit()); IPNet net(router_address, IPv6::ADDR_BITLEN); routing_table_add_entry(routing_table, net, route_entry, __PRETTY_FUNCTION__); // routing_table_add_entry(routing_table, IPNet(), // route_entry); } } else { NetworkLsa *nlsa = dynamic_cast(lsar.get()); XLOG_ASSERT(nlsa); const list& lsai = lsa_temp_store.get_intra_area_prefix_lsas(node.get_nodeid()); if (!lsai.empty()) { RouteEntry route_entry; route_entry.set_destination_type(OspfTypes::Network); // route_entry.set_router_id(rlsa->get_header(). // get_advertising_router()); route_entry.set_directly_connected(ri->node() == ri->nexthop()); route_entry.set_path_type(RouteEntry::intra_area); // route_entry.set_cost(ri->weight()); route_entry.set_type_2_cost(0); route_entry.set_nexthop(ri->nexthop().get_address_ipv6()); route_entry.set_nexthop_id(ri->nexthop().get_nexthop_id()); route_entry.set_advertising_router(lsar->get_header(). get_advertising_router()); route_entry.set_area(_area); route_entry.set_lsa(lsar); list prefixes; associated_prefixesV3(nlsa->get_ls_type(), nlsa->get_header().get_link_state_id(), lsai, prefixes); list::iterator j; for (j = prefixes.begin(); j != prefixes.end(); j++) { if (j->get_nu_bit()) continue; route_entry.set_cost(ri->weight() + j->get_metric()); routing_table_add_entry(routing_table, j->get_network(), route_entry, __PRETTY_FUNCTION__); } } } #if 0 RouterLsa *rlsa; NetworkLsa *nlsa; RouteEntry route_entry; IPNet net; route_entry.set_destination_type(node.get_type()); if (OspfTypes::Router == node.get_type()) { rlsa = dynamic_cast(lsar.get()); XLOG_ASSERT(rlsa); check_for_virtual_link((*ri), _router_lsa); if (!(rlsa->get_e_bit() || rlsa->get_b_bit())) continue; // Originating routers Router ID. route_entry.set_router_id(rlsa->get_header().get_link_state_id()); IPv4 addr; XLOG_ASSERT(find_interface_address(ri->prevhop().get_lsa(), lsar, addr)); net = IPNet(addr, IPv4::ADDR_BITLEN); route_entry.set_area_border_router(rlsa->get_b_bit()); route_entry.set_as_boundary_router(rlsa->get_e_bit()); } else { nlsa = dynamic_cast(lsar.get()); XLOG_ASSERT(nlsa); // route_entry.set_router_id(nlsa->get_header(). // get_advertising_router()); route_entry.set_address(nlsa->get_header().get_link_state_id()); IPv4 addr = IPv4(htonl(route_entry.get_address())); IPv4 mask = IPv4(htonl(nlsa->get_network_mask())); net = IPNet(addr, mask.mask_len()); } // If nexthop point back to the node itself then it it // directly connected. route_entry.set_directly_connected(ri->node() == ri->nexthop()); route_entry.set_path_type(RouteEntry::intra_area); route_entry.set_cost(ri->weight()); route_entry.set_type_2_cost(0); route_entry.set_nexthop(ri->nexthop().get_address_ipv6()); route_entry.set_advertising_router(lsar->get_header(). get_advertising_router()); route_entry.set_area(_area); route_entry.set_lsa(lsar); routing_table_add_entry(routing_table, net, route_entry); #endif } end_virtual_link(); // RFC 2328 Section 16.2. Calculating the inter-area routes if (_ospf.get_peer_manager().internal_router_p() || (backbone() && _ospf.get_peer_manager().area_border_router_p())) routing_inter_areaV3(); // RFC 2328 Section 16.3. Examining transit areas' summary-LSAs if (transit_capability && _ospf.get_peer_manager().area_border_router_p()) routing_transit_areaV3(); // RFC 2328 Section 16.4. Calculating AS external routes routing_as_externalV3(); routing_table.end(); if (backbone()) _ospf.get_peer_manager().routing_recompute_all_transit_areas(); } template void AreaRouter::routing_table_add_entry(RoutingTable& routing_table, IPNet net, RouteEntry& route_entry, const char* msg) { // Verify that a route is not already in the table. In the OSPFv2 // case stub links are processed in the main processing loop // rather than later as the RFC suggests. Either due to // misconfiguration or races a Network-LSA and a stub link in // a Router-LSA can point to the same network. Therefore it is // necessary to check that a route is not already in the table. debug_msg("net %s\n%s\n", cstring(net), cstring(route_entry)); // If this is a router entry and the net is not valid // unconditionally place an add_entry call that will cause the // router to be indexed by router id. if (route_entry.get_destination_type() == OspfTypes::Router && !net.is_valid()) { routing_table.add_entry(_area, net, route_entry, msg); return; } XLOG_ASSERT(net.is_valid()); RouteEntry current_route_entry; if (routing_table.lookup_entry(_area, net, current_route_entry)) { if (current_route_entry.get_cost() > route_entry.get_cost()) { routing_table.replace_entry(_area, net, route_entry); } else if (current_route_entry.get_cost() == route_entry.get_cost()) { if (route_entry.get_advertising_router() < current_route_entry.get_advertising_router()) routing_table.replace_entry(_area, net, route_entry); } } else { routing_table.add_entry(_area, net, route_entry, msg); } } template <> void AreaRouter::routing_area_rangesV2(const list >& r) { // If there is only one area there are no other areas for which to // compute summaries. if (_ospf.get_peer_manager().internal_router_p()) return; // Do any of our intra area path fall within the summary range and // if they do is it an advertised range. If a network falls into // an advertised range then use the largest cost of the covered // networks. map, RouteEntry > ranges; list >::const_iterator ri; for(ri = r.begin(); ri != r.end(); ri++) { // if (ri->node() == ri->nexthop()) // continue; if (ri->node().get_type() == OspfTypes::Router) continue; Vertex node = ri->node(); Lsa::LsaRef lsar = node.get_lsa(); RouteEntry route_entry; route_entry.set_destination_type(OspfTypes::Network); IPNet net; NetworkLsa *nlsa = dynamic_cast(lsar.get()); XLOG_ASSERT(nlsa); route_entry.set_address(nlsa->get_header().get_link_state_id()); IPv4 addr = IPv4(htonl(route_entry.get_address())); IPv4 mask = IPv4(htonl(nlsa->get_network_mask())); net = IPNet(addr, mask.mask_len()); // Does this network fall into an area range? bool advertise; if (!area_range_covered(net, advertise)) continue; if (!advertise) continue; IPNet sumnet; if (!area_range_covering(net, sumnet)) { XLOG_FATAL("Net %s does not have a covering net", cstring(net)); continue; } route_entry.set_directly_connected(ri->node() == ri->nexthop()); route_entry.set_path_type(RouteEntry::intra_area); route_entry.set_cost(ri->weight()); route_entry.set_type_2_cost(0); route_entry.set_nexthop(ri->nexthop().get_address_ipv4()); route_entry.set_advertising_router(lsar->get_header(). get_advertising_router()); route_entry.set_area(_area); route_entry.set_lsa(lsar); // Mark this as a discard route. route_entry.set_discard(true); if (0 != ranges.count(sumnet)) { RouteEntry r = ranges[sumnet]; if (route_entry.get_cost() < r.get_cost()) continue; } ranges[sumnet] = route_entry; } // Send in the discard routes. RoutingTable& routing_table = _ospf.get_routing_table(); map, RouteEntry >::const_iterator i; for (i = ranges.begin(); i != ranges.end(); i++) { IPNet net = i->first; RouteEntry route_entry = i->second; routing_table.add_entry(_area, net, route_entry, __PRETTY_FUNCTION__); } } template <> void AreaRouter::routing_area_rangesV3(const list >& r, LsaTempStore& lsa_temp_store) { // If there is only one area there are no other areas for which to // compute summaries. if (_ospf.get_peer_manager().internal_router_p()) return; // Do any of our intra area path fall within the summary range and // if they do is it an advertised range. If a network falls into // an advertised range then use the largest cost of the covered // networks. map, RouteEntry > ranges; list >::const_iterator ri; for(ri = r.begin(); ri != r.end(); ri++) { // if (ri->node() == ri->nexthop()) // continue; if (ri->node().get_type() == OspfTypes::Router) continue; Vertex node = ri->node(); list& lsars = node.get_lsas(); list::iterator l = lsars.begin(); XLOG_ASSERT(l != lsars.end()); Lsa::LsaRef lsar = *l++; RouteEntry route_entry; route_entry.set_destination_type(OspfTypes::Network); IPNet net; NetworkLsa *nlsa = dynamic_cast(lsar.get()); XLOG_ASSERT(nlsa); #if 0 route_entry.set_address(nlsa->get_header().get_link_state_id()); IPv6 addr = IPv6(htonl(route_entry.get_address())); IPv6 mask = IPv6(htonl(nlsa->get_network_mask())); net = IPNet(addr, mask.mask_len()); #endif list& lsai = lsa_temp_store. get_intra_area_prefix_lsas(node.get_nodeid()); list prefixes; associated_prefixesV3(nlsa->get_ls_type(), nlsa->get_header().get_link_state_id(), lsai, prefixes); list::const_iterator i; for (i = prefixes.begin(); i != prefixes.end(); i++) { if (i->get_nu_bit()) continue; net = i->get_network(); // Does this network fall into an area range? bool advertise; if (!area_range_covered(net, advertise)) continue; if (!advertise) continue; IPNet sumnet; if (!area_range_covering(net, sumnet)) { XLOG_FATAL("Net %s does not have a covering net", cstring(net)); continue; } route_entry.set_directly_connected(ri->node() == ri->nexthop()); route_entry.set_path_type(RouteEntry::intra_area); route_entry.set_cost(ri->weight() + i->get_metric()); route_entry.set_type_2_cost(0); route_entry.set_nexthop(ri->nexthop().get_address_ipv6()); route_entry.set_nexthop_id(ri->nexthop().get_nexthop_id()); route_entry.set_advertising_router(lsar->get_header(). get_advertising_router()); route_entry.set_area(_area); route_entry.set_lsa(lsar); // Mark this as a discard route. route_entry.set_discard(true); if (0 != ranges.count(sumnet)) { RouteEntry r = ranges[sumnet]; if (route_entry.get_cost() < r.get_cost()) continue; } ranges[sumnet] = route_entry; } } // Send in the discard routes. RoutingTable& routing_table = _ospf.get_routing_table(); map, RouteEntry >::const_iterator i; for (i = ranges.begin(); i != ranges.end(); i++) { IPNet net = i->first; RouteEntry route_entry = i->second; routing_table.add_entry(_area, net, route_entry, __PRETTY_FUNCTION__); } } template <> void AreaRouter::routing_inter_areaV2() { // RFC 2328 Section 16.2. Calculating the inter-area routes for (size_t index = 0 ; index < _last_entry; index++) { Lsa::LsaRef lsar = _db[index]; if (!lsar->valid() || lsar->maxage()) continue; SummaryNetworkLsa *snlsa; // Type 3 SummaryRouterLsa *srlsa; // Type 4 uint32_t metric = 0; IPv4 mask; if (0 != (snlsa = dynamic_cast(lsar.get()))) { metric = snlsa->get_metric(); mask = IPv4(htonl(snlsa->get_network_mask())); } if (0 != (srlsa = dynamic_cast(lsar.get()))) { metric = srlsa->get_metric(); mask = IPv4::ALL_ONES(); } if (0 == snlsa && 0 == srlsa) continue; if (OspfTypes::LSInfinity == metric) continue; // (2) if (lsar->get_self_originating()) continue; uint32_t lsid = lsar->get_header().get_link_state_id(); IPNet n = IPNet(IPv4(htonl(lsid)), mask.mask_len()); // (3) if (snlsa) { bool active; if (area_range_covered(n, active)) { if (active) continue; } } // (4) uint32_t adv = lsar->get_header().get_advertising_router(); RoutingTable& routing_table = _ospf.get_routing_table(); RouteEntry rt; if (!routing_table.lookup_entry_by_advertising_router(_area, adv, rt)) continue; if (rt.get_advertising_router() != adv || rt.get_area() != _area) continue; uint32_t iac = rt.get_cost() + metric; // (5) bool add_entry = false; bool replace_entry = false; RouteEntry rtnet; if (routing_table.lookup_entry(n, rtnet)) { switch(rtnet.get_path_type()) { case RouteEntry::intra_area: break; case RouteEntry::inter_area: // XXX - Should be dealing with equal cost here. if (iac < rtnet.get_cost()) replace_entry = true; break; case RouteEntry::type1: case RouteEntry::type2: replace_entry = true; break; } } else { add_entry = true; } if (!add_entry && !replace_entry) continue; RouteEntry rtentry; if (snlsa) { rtentry.set_destination_type(OspfTypes::Network); rtentry.set_address(lsid); } else if (srlsa) { rtentry.set_destination_type(OspfTypes::Router); rtentry.set_router_id(lsid); rtentry.set_as_boundary_router(true); } else XLOG_UNREACHABLE(); rtentry.set_area(_area); // rtentry.set_directly_connected(rt.get_directly_connected()); rtentry.set_directly_connected(false); rtentry.set_path_type(RouteEntry::inter_area); rtentry.set_cost(iac); rtentry.set_nexthop(rt.get_nexthop()); rtentry.set_advertising_router(rt.get_advertising_router()); rtentry.set_lsa(lsar); if (add_entry) routing_table.add_entry(_area, n, rtentry, __PRETTY_FUNCTION__); if (replace_entry) routing_table.replace_entry(_area, n, rtentry); } } template <> void AreaRouter::routing_inter_areaV3() { // RFC 2328 Section 16.2. Calculating the inter-area routes for (size_t index = 0 ; index < _last_entry; index++) { Lsa::LsaRef lsar = _db[index]; if (!lsar->valid() || lsar->maxage()) continue; SummaryNetworkLsa *snlsa; // Type 3 SummaryRouterLsa *srlsa; // Type 4 uint32_t metric = 0; IPNet n; if (0 != (snlsa = dynamic_cast(lsar.get()))) { if (snlsa->get_ipv6prefix().get_nu_bit()) continue; if (snlsa->get_ipv6prefix().get_network().masked_addr(). is_linklocal_unicast()) continue; metric = snlsa->get_metric(); n = snlsa->get_ipv6prefix().get_network(); } if (0 != (srlsa = dynamic_cast(lsar.get()))) { metric = srlsa->get_metric(); } if (0 == snlsa && 0 == srlsa) continue; if (OspfTypes::LSInfinity == metric) continue; // (2) if (lsar->get_self_originating()) continue; // (3) if (snlsa) { bool active; if (area_range_covered(n, active)) { if (active) continue; } } // (4) uint32_t adv = lsar->get_header().get_advertising_router(); RoutingTable& routing_table = _ospf.get_routing_table(); RouteEntry rt; if (!routing_table.lookup_entry_by_advertising_router(_area, adv, rt)) continue; if (rt.get_advertising_router() != adv || rt.get_area() != _area) continue; uint32_t iac = rt.get_cost() + metric; // (5) bool add_entry = false; bool replace_entry = false; RouteEntry rtnet; bool found = false; OspfTypes::RouterID dest = 0; if (snlsa) { if (routing_table.lookup_entry(n, rtnet)) found = true; } else if (srlsa) { dest = srlsa->get_destination_id(); if (routing_table.lookup_entry_by_advertising_router(_area, dest, rtnet)) found = true; } else XLOG_UNREACHABLE(); if (found) { switch(rtnet.get_path_type()) { case RouteEntry::intra_area: break; case RouteEntry::inter_area: // XXX - Should be dealing with equal cost here. if (iac < rtnet.get_cost()) replace_entry = true; break; case RouteEntry::type1: case RouteEntry::type2: replace_entry = true; break; } } else { add_entry = true; } if (!add_entry && !replace_entry) continue; RouteEntry rtentry; if (snlsa) { rtentry.set_destination_type(OspfTypes::Network); // rtentry.set_address(lsid); } else if (srlsa) { rtentry.set_destination_type(OspfTypes::Router); rtentry.set_router_id(dest); rtentry.set_as_boundary_router(true); } else XLOG_UNREACHABLE(); rtentry.set_area(_area); // rtentry.set_directly_connected(rt.get_directly_connected()); rtentry.set_directly_connected(false); rtentry.set_path_type(RouteEntry::inter_area); rtentry.set_cost(iac); rtentry.set_nexthop(rt.get_nexthop()); rtentry.set_nexthop_id(rt.get_nexthop_id()); rtentry.set_advertising_router(rt.get_advertising_router()); rtentry.set_lsa(lsar); if (add_entry) routing_table.add_entry(_area, n, rtentry, __PRETTY_FUNCTION__); if (replace_entry) routing_table.replace_entry(_area, n, rtentry); } } template <> void AreaRouter::routing_transit_areaV2() { // RFC 2328 Section 16.3. Examining transit areas' summary-LSAs for (size_t index = 0 ; index < _last_entry; index++) { Lsa::LsaRef lsar = _db[index]; if (!lsar->valid() || lsar->maxage()) continue; SummaryNetworkLsa *snlsa; // Type 3 SummaryRouterLsa *srlsa; // Type 4 uint32_t metric = 0; IPv4 mask; if (0 != (snlsa = dynamic_cast(lsar.get()))) { metric = snlsa->get_metric(); mask = IPv4(htonl(snlsa->get_network_mask())); } if (0 != (srlsa = dynamic_cast(lsar.get()))) { metric = srlsa->get_metric(); mask = IPv4::ALL_ONES(); } if (0 == snlsa && 0 == srlsa) continue; if (OspfTypes::LSInfinity == metric) continue; // (2) if (lsar->get_self_originating()) continue; uint32_t lsid = lsar->get_header().get_link_state_id(); IPNet n = IPNet(IPv4(htonl(lsid)), mask.mask_len()); // (3) // Lookup this route first RoutingTable& routing_table = _ospf.get_routing_table(); RouteEntry rtnet; if (!routing_table.lookup_entry(n, rtnet)) continue; if (!backbone(rtnet.get_area())) continue; bool match = true; switch(rtnet.get_path_type()) { case RouteEntry::intra_area: case RouteEntry::inter_area: break; case RouteEntry::type1: case RouteEntry::type2: match = false; break; } if (!match) continue; // (4) // Is the BR reachable? uint32_t adv = lsar->get_header().get_advertising_router(); RouteEntry rtrtr; if (!routing_table. lookup_entry_by_advertising_router(rtnet.get_area(), adv, rtrtr)) continue; uint32_t iac = rtrtr.get_cost() + metric; // (5) if (rtnet.get_cost() <= iac) continue; // rtnet.set_area(_area); // rtnet.set_path_type(RouteEntry::inter_area); rtnet.set_cost(iac); rtnet.set_nexthop(rtrtr.get_nexthop()); rtnet.set_advertising_router(rtrtr.get_advertising_router()); rtnet.set_lsa(lsar); // Change the existing route, so if the original route goes // away the route will be removed. routing_table.replace_entry(rtnet.get_area(), n, rtnet); } } template <> void AreaRouter::routing_transit_areaV3() { // RFC 2328 Section 16.3. Examining transit areas' summary-LSAs for (size_t index = 0 ; index < _last_entry; index++) { Lsa::LsaRef lsar = _db[index]; if (!lsar->valid() || lsar->maxage()) continue; SummaryNetworkLsa *snlsa; // Type 3 SummaryRouterLsa *srlsa; // Type 4 uint32_t metric = 0; IPNet n; if (0 != (snlsa = dynamic_cast(lsar.get()))) { metric = snlsa->get_metric(); n = snlsa->get_ipv6prefix().get_network(); } if (0 != (srlsa = dynamic_cast(lsar.get()))) { metric = srlsa->get_metric(); } if (0 == snlsa && 0 == srlsa) continue; if (OspfTypes::LSInfinity == metric) continue; // (2) if (lsar->get_self_originating()) continue; // uint32_t lsid = lsar->get_header().get_link_state_id(); // IPNet n = IPNet(IPv4(htonl(lsid)), mask.mask_len()); // (3) // Lookup this route first RoutingTable& routing_table = _ospf.get_routing_table(); RouteEntry rtnet; OspfTypes::RouterID dest; if (snlsa) { if (!routing_table.lookup_entry(n, rtnet)) continue; } else if (srlsa) { dest = srlsa->get_destination_id(); if (!routing_table.lookup_entry_by_advertising_router(_area, dest, rtnet)) continue; } else XLOG_UNREACHABLE(); if (!backbone(rtnet.get_area())) continue; bool match = true; switch(rtnet.get_path_type()) { case RouteEntry::intra_area: case RouteEntry::inter_area: break; case RouteEntry::type1: case RouteEntry::type2: match = false; break; } if (!match) continue; // (4) // Is the BR reachable? uint32_t adv = lsar->get_header().get_advertising_router(); RouteEntry rtrtr; if (!routing_table. lookup_entry_by_advertising_router(rtnet.get_area(), adv, rtrtr)) continue; uint32_t iac = rtrtr.get_cost() + metric; // (5) if (rtnet.get_cost() <= iac) continue; // rtnet.set_area(_area); // rtnet.set_path_type(RouteEntry::inter_area); rtnet.set_cost(iac); rtnet.set_nexthop(rtrtr.get_nexthop()); rtnet.set_nexthop_id(rtrtr.get_nexthop_id()); rtnet.set_advertising_router(rtrtr.get_advertising_router()); rtnet.set_lsa(lsar); // Change the existing route, so if the original route goes // away the route will be removed. routing_table.replace_entry(rtnet.get_area(), n, rtnet); } } template <> void AreaRouter::routing_as_externalV2() { // RFC 2328 Section 16.4. Calculating AS external routes // RFC 3101 Section 2.5. Calculating Type-7 AS external routes for (size_t index = 0 ; index < _last_entry; index++) { Lsa::LsaRef lsar = _db[index]; if (!lsar->valid() || lsar->maxage() || lsar->get_self_originating()) continue; // Note that Type7Lsa is derived from ASExternalLsa so will // pass this test. ASExternalLsa *aselsa; if (0 == (aselsa = dynamic_cast(lsar.get()))) { continue; } if (OspfTypes::LSInfinity == aselsa->get_metric()) continue; // IPv4 mask = IPv4(htonl(aselsa->get_network_mask())); uint32_t lsid = lsar->get_header().get_link_state_id(); uint32_t adv = lsar->get_header().get_advertising_router(); // IPNet n = IPNet(IPv4(htonl(lsid)), mask.mask_len()); IPNet n = aselsa->get_network(IPv4::ZERO()); // (3) RoutingTable& routing_table = _ospf.get_routing_table(); RouteEntry rt; if (!routing_table.lookup_entry_by_advertising_router(_area, adv, rt)) continue; if (!rt.get_as_boundary_router()) return; // If a routing entry has been found it must be from this area. XLOG_ASSERT(rt.get_area() == _area); if (aselsa->type7() && 0 == n.prefix_len()) { if (_ospf.get_peer_manager().area_border_router_p()) { if (!external_propagate_bit(lsar)) continue; if (!_summaries) continue; } } IPv4 forwarding = aselsa->get_forwarding_address_ipv4(); if (IPv4(static_cast(0)) == forwarding) { forwarding = rt.get_nexthop(); } RouteEntry rtf; if (!routing_table.longest_match_entry(forwarding, rtf)) continue; if (!rtf.get_directly_connected()) forwarding = rtf.get_nexthop(); if (aselsa->external()) { if (RouteEntry::intra_area != rtf.get_path_type() && RouteEntry::inter_area != rtf.get_path_type()) continue; } if (aselsa->type7()) { if (RouteEntry::intra_area != rtf.get_path_type()) continue; } // (4) uint32_t x = rtf.get_cost(); // Cost specified by // ASBR/forwarding address uint32_t y = aselsa->get_metric(); // (5) RouteEntry rtentry; if (!aselsa->get_e_bit()) { // Type 1 rtentry.set_path_type(RouteEntry::type1); rtentry.set_cost(x + y); } else { // Type 2 rtentry.set_path_type(RouteEntry::type2); rtentry.set_cost(x); rtentry.set_type_2_cost(y); } // (6) bool add_entry = false; bool replace_entry = false; bool identical = false; RouteEntry rtnet; if (routing_table.lookup_entry(n, rtnet)) { switch(rtnet.get_path_type()) { case RouteEntry::intra_area: if (!_ospf.get_rfc1583_compatibility()) { if (RouteEntry::intra_area == rtf.get_path_type()) { if (!backbone(rtf.get_area()) && backbone(rtnet.get_area())) { replace_entry = true; } } } break; case RouteEntry::inter_area: break; case RouteEntry::type1: if (RouteEntry::type2 == rtentry.get_path_type()) { break; } if (rtentry.get_cost() < rtnet.get_cost()) { replace_entry = true; break; } if (rtentry.get_cost() == rtnet.get_cost()) identical = true; break; case RouteEntry::type2: if (RouteEntry::type1 == rtentry.get_path_type()) { replace_entry = true; break; } if (rtentry.get_type_2_cost() < rtnet.get_type_2_cost()) { replace_entry = true; break; } if (rtentry.get_type_2_cost() == rtnet.get_type_2_cost()) identical = true; break; } // (e) if (identical) { replace_entry = routing_compare_externals(rtnet.get_lsa(), lsar); } } else { add_entry = true; } if (!add_entry && !replace_entry) continue; rtentry.set_lsa(lsar); rtentry.set_destination_type(OspfTypes::Network); rtentry.set_address(lsid); rtentry.set_area(_area); rtentry.set_nexthop(forwarding); rtentry.set_advertising_router(aselsa->get_header(). get_advertising_router()); if (add_entry) routing_table.add_entry(_area, n, rtentry, __PRETTY_FUNCTION__); if (replace_entry) routing_table.replace_entry(_area, n, rtentry); } } template <> void AreaRouter::routing_as_externalV3() { // RFC 2328 Section 16.4. Calculating AS external routes // RFC 3101 Section 2.5. Calculating Type-7 AS external routes for (size_t index = 0 ; index < _last_entry; index++) { Lsa::LsaRef lsar = _db[index]; if (!lsar->valid() || lsar->maxage() || lsar->get_self_originating()) continue; // Note that Type7Lsa is derived from ASExternalLsa so will // pass this test. ASExternalLsa *aselsa; if (0 == (aselsa = dynamic_cast(lsar.get()))) { continue; } if (OspfTypes::LSInfinity == aselsa->get_metric()) continue; if (aselsa->get_ipv6prefix().get_nu_bit()) continue; if (aselsa->get_ipv6prefix().get_network().masked_addr(). is_linklocal_unicast()) continue; // IPv4 mask = IPv4(htonl(aselsa->get_network_mask())); // uint32_t lsid = lsar->get_header().get_link_state_id(); uint32_t adv = lsar->get_header().get_advertising_router(); // IPNet n = IPNet(IPv4(htonl(lsid)), mask.mask_len()); IPNet n = aselsa->get_network(IPv6::ZERO()); // (3) RoutingTable& routing_table = _ospf.get_routing_table(); RouteEntry rt; if (!routing_table.lookup_entry_by_advertising_router(_area, adv, rt)) continue; if (!rt.get_as_boundary_router()) return; // If a routing entry has been found it must be from this area. XLOG_ASSERT(rt.get_area() == _area); if (aselsa->type7() && 0 == n.prefix_len()) { if (_ospf.get_peer_manager().area_border_router_p()) { if (!external_propagate_bit(lsar)) continue; if (!_summaries) continue; } } IPv6 forwarding; uint32_t forwarding_id = OspfTypes::UNUSED_INTERFACE_ID; RouteEntry rtf; if (aselsa->get_f_bit()) { forwarding = aselsa->get_forwarding_address_ipv6(); if (!routing_table.longest_match_entry(forwarding, rtf)) continue; // if (!rtf.get_directly_connected()) { forwarding = rtf.get_nexthop(); forwarding_id = rtf.get_nexthop_id(); // } if (aselsa->external()) { if (RouteEntry::intra_area != rtf.get_path_type() && RouteEntry::inter_area != rtf.get_path_type()) continue; } if (aselsa->type7()) { if (RouteEntry::intra_area != rtf.get_path_type()) continue; } } else { forwarding = rt.get_nexthop(); forwarding_id = rt.get_nexthop_id(); rtf = rt; } // (4) uint32_t x = rtf.get_cost(); // Cost specified by // ASBR/forwarding address uint32_t y = aselsa->get_metric(); // (5) RouteEntry rtentry; if (!aselsa->get_e_bit()) { // Type 1 rtentry.set_path_type(RouteEntry::type1); rtentry.set_cost(x + y); } else { // Type 2 rtentry.set_path_type(RouteEntry::type2); rtentry.set_cost(x); rtentry.set_type_2_cost(y); } // (6) bool add_entry = false; bool replace_entry = false; bool identical = false; RouteEntry rtnet; if (routing_table.lookup_entry(n, rtnet)) { switch(rtnet.get_path_type()) { case RouteEntry::intra_area: #if 0 if (!_ospf.get_rfc1583_compatibility()) { if (RouteEntry::intra_area == rtf.get_path_type()) { if (!backbone(rtf.get_area()) && backbone(rtnet.get_area())) { replace_entry = true; } } } #endif break; case RouteEntry::inter_area: break; case RouteEntry::type1: if (RouteEntry::type2 == rtentry.get_path_type()) { break; } if (rtentry.get_cost() < rtnet.get_cost()) { replace_entry = true; break; } if (rtentry.get_cost() == rtnet.get_cost()) identical = true; break; case RouteEntry::type2: if (RouteEntry::type1 == rtentry.get_path_type()) { replace_entry = true; break; } if (rtentry.get_type_2_cost() < rtnet.get_type_2_cost()) { replace_entry = true; break; } if (rtentry.get_type_2_cost() == rtnet.get_type_2_cost()) identical = true; break; } // (e) if (identical) { replace_entry = routing_compare_externals(rtnet.get_lsa(), lsar); } } else { add_entry = true; } if (!add_entry && !replace_entry) continue; rtentry.set_lsa(lsar); rtentry.set_destination_type(OspfTypes::Network); // rtentry.set_address(lsid); rtentry.set_area(_area); rtentry.set_nexthop(forwarding); rtentry.set_nexthop_id(forwarding_id); rtentry.set_advertising_router(aselsa->get_header(). get_advertising_router()); if (add_entry) routing_table.add_entry(_area, n, rtentry, __PRETTY_FUNCTION__); if (replace_entry) routing_table.replace_entry(_area, n, rtentry); } } #if 0 template bool AreaRouter::associated_prefixesV3(const RouterLsa *rlsa, const list& lsai, list& prefixes) { list::const_iterator i; for (i = lsai.begin(); i != lsai.end(); i++) { if ((*i)->get_referenced_ls_type() != rlsa->get_ls_type()) continue; if ((*i)->get_referenced_link_state_id() != 0) { XLOG_WARNING("Referenced Link State ID " "should be zero %s", cstring(*(*i))); continue; } if ((*i)->get_referenced_advertising_router() != (*i)->get_header().get_advertising_router()) { XLOG_WARNING("Advertising router and Referenced " "Advertising router don't match %s", cstring(*(*i))); continue; } list& p = (*i)->get_prefixes(); list::const_iterator j; for (j = p.begin(); j != p.end(); j++) { prefixes.push_back(*j); } } return true; } template bool AreaRouter::associated_prefixesV3(NetworkLsa *nlsa, const list& lsai, list& prefixes) { list::const_iterator i; for (i = lsai.begin(); i != lsai.end(); i++) { if ((*i)->get_referenced_ls_type() != nlsa->get_ls_type()) continue; if ((*i)->get_referenced_link_state_id() != nlsa->get_header().get_link_state_id()) { continue; } if ((*i)->get_referenced_advertising_router() != (*i)->get_header().get_advertising_router()) { XLOG_WARNING("Advertising router and Referenced " "Advertising router don't match %s", cstring(*(*i))); continue; } list& p = (*i)->get_prefixes(); list::const_iterator j; for (j = p.begin(); j != p.end(); j++) { prefixes.push_back(*j); } } return true; } #endif template bool AreaRouter::associated_prefixesV3(uint16_t ls_type, uint32_t referenced_link_state_id, const list& lsai, list& prefixes) const { list::const_iterator i; for (i = lsai.begin(); i != lsai.end(); i++) { if ((*i)->get_referenced_ls_type() != ls_type) continue; if ((*i)->get_referenced_link_state_id() != referenced_link_state_id) { if (RouterLsa(_ospf.get_version()).get_ls_type() == ls_type) { XLOG_ASSERT(0 == referenced_link_state_id); XLOG_WARNING("Referenced Link State ID " "should be zero %s", cstring(*(*i))); } continue; } if ((*i)->get_referenced_advertising_router() != (*i)->get_header().get_advertising_router()) { XLOG_WARNING("Advertising router and Referenced " "Advertising router don't match %s", cstring(*(*i))); continue; } list& p = (*i)->get_prefixes(); list::const_iterator j; for (j = p.begin(); j != p.end(); j++) { prefixes.push_back(*j); } } return true; } template bool AreaRouter::routing_compare_externals(Lsa::LsaRef current, Lsa::LsaRef candidate) const { // RFC 3101 Section 2.5. (6) (e) Calculating Type-7 AS external routes. bool current_type7 = current->type7(); bool candidate_type7 = candidate->type7(); if (current_type7) current_type7 = external_propagate_bit(current); if (candidate_type7) candidate_type7 = external_propagate_bit(candidate); if (current_type7 == candidate_type7) { return candidate->get_header().get_advertising_router() > current->get_header().get_advertising_router(); } if (candidate_type7) return true; return false; } template bool AreaRouter::bidirectionalV2(RouterLink::Type rl_type, const uint32_t link_state_id, const RouterLink& rl, RouterLsa *rlsa, uint16_t& metric, uint32_t& interface_address) { XLOG_ASSERT(0 != rlsa); XLOG_ASSERT(rl_type == RouterLink::p2p || rl_type == RouterLink::vlink); XLOG_ASSERT(rl.get_type() == rl_type); // This is the edge from the Router-LSA to the Router-LSA. XLOG_ASSERT(rl.get_link_id() == rlsa->get_header().get_link_state_id()); XLOG_ASSERT(rl.get_link_id() == rlsa->get_header().get_advertising_router()); const list &rlinks = rlsa->get_router_links(); list::const_iterator l = rlinks.begin(); for(; l != rlinks.end(); l++) { if (l->get_link_id() == link_state_id && l->get_type() == rl_type) { metric = l->get_metric(); interface_address = l->get_link_data(); return true; } } return false; } template bool AreaRouter::bidirectional(const uint32_t link_state_id_or_adv, const RouterLink& rl, NetworkLsa *nlsa) const { XLOG_ASSERT(0 != nlsa); XLOG_ASSERT(rl.get_type() == RouterLink::transit); // This is the edge from the Router-LSA to the Network-LSA. switch(_ospf.get_version()) { case OspfTypes::V2: XLOG_ASSERT(rl.get_link_id() == nlsa->get_header().get_link_state_id()); break; case OspfTypes::V3: XLOG_ASSERT(rl.get_neighbour_interface_id() == nlsa->get_header().get_link_state_id()); XLOG_ASSERT(rl.get_neighbour_router_id() == nlsa->get_header().get_advertising_router()); break; } // Does the Network-LSA know about the Router-LSA. list& routers = nlsa->get_attached_routers(); list::const_iterator i; for (i = routers.begin(); i != routers.end(); i++) if (link_state_id_or_adv == *i) return true; return false; } template bool AreaRouter::bidirectionalV2(RouterLsa *rlsa, NetworkLsa *nlsa, uint32_t& interface_address) { XLOG_ASSERT(rlsa); XLOG_ASSERT(nlsa); const uint32_t link_state_id = nlsa->get_header().get_link_state_id(); const list &rlinks = rlsa->get_router_links(); list::const_iterator l = rlinks.begin(); for(; l != rlinks.end(); l++) { if (l->get_link_id() == link_state_id && l->get_type() == RouterLink::transit) { interface_address = l->get_link_data(); return true; } } return false; } template bool AreaRouter::bidirectionalV3(RouterLsa *rlsa, NetworkLsa *nlsa, uint32_t& interface_id) { XLOG_ASSERT(rlsa); XLOG_ASSERT(nlsa); const uint32_t link_state_id = nlsa->get_header().get_link_state_id(); const uint32_t adv = nlsa->get_header().get_advertising_router(); const list &rlinks = rlsa->get_router_links(); list::const_iterator l = rlinks.begin(); for(; l != rlinks.end(); l++) { if (l->get_neighbour_interface_id() == link_state_id && l->get_neighbour_router_id() == adv && l->get_type() == RouterLink::transit) { interface_id = l->get_interface_id(); return true; } } return false; } template bool AreaRouter::bidirectionalV3(RouterLink::Type rl_type, const uint32_t advertising_router, RouterLsa *rlsa, uint16_t& metric) { XLOG_ASSERT(rlsa); XLOG_ASSERT(rl_type == RouterLink::p2p || rl_type == RouterLink::vlink); const list &rlinks = rlsa->get_router_links(); list::const_iterator l = rlinks.begin(); for(; l != rlinks.end(); l++) { if (l->get_neighbour_router_id() == advertising_router && l->get_type() == rl_type) { metric = l->get_metric(); return true; } } return false; } /** * Update an edge in the normal case an edge will be added twice, * check that the metric has not changed. */ template inline void update_edge(Spt& spt, const Vertex& src, int metric, const Vertex& dst) { debug_msg("src %s metric %d dst %s\n", cstring(src), metric, cstring(dst)); if (!spt.add_edge(src, metric, dst)) { int current_metric; if (!spt.get_edge_weight(src, current_metric, dst)) XLOG_FATAL("Can't get edge weight between %s and %s", cstring(src), cstring(dst)); if (current_metric <= metric) return; // We should only get here if two vertexes have more than one // edge between them, in which case the nexthop for the // destination vertex must be updated. if (!spt.update_node(dst)) XLOG_FATAL("Can't update node %s", cstring(dst)); if (!spt.update_edge_weight(src, metric, dst)) XLOG_FATAL("Couldn't update edge between %s and %s", cstring(src), cstring(dst)); } } template void AreaRouter::routing_router_lsaV2(Spt& spt, const Vertex& src, RouterLsa *rlsa) { debug_msg("Vertex %s \n%s\n", cstring(src), cstring(*rlsa)); const list &rl = rlsa->get_router_links(); list::const_iterator l = rl.begin(); for(; l != rl.end(); l++) { // fprintf(stderr, "RouterLink %s\n", cstring(*l)); switch(l->get_type()) { case RouterLink::p2p: case RouterLink::vlink: routing_router_link_p2p_vlinkV2(spt, src, rlsa, *l); break; case RouterLink::transit: routing_router_link_transitV2(spt, src, rlsa, *l); break; case RouterLink::stub: routing_router_link_stubV2(spt, src, rlsa, *l); break; } } } template void AreaRouter::routing_router_link_p2p_vlinkV2(Spt& spt, const Vertex& src, RouterLsa *rlsa, RouterLink rl) { // Try and find the router link that this one points at. Ls_request lsr(_ospf.get_version(), RouterLsa(_ospf.get_version()).get_header().get_ls_type(), rl.get_link_id(), rl.get_link_id()); size_t index; if (find_lsa(lsr, index)) { Lsa::LsaRef lsapeer = _db[index]; // This can probably never happen if (lsapeer->maxage()) { XLOG_WARNING("LSA in database MaxAge\n%s", cstring(*lsapeer)); return; } // Check that this Router-LSA points back to the // original. uint16_t metric; uint32_t interface_address; if (!bidirectionalV2(rl.get_type(), rlsa->get_header().get_link_state_id(), rl, dynamic_cast(lsapeer.get()), metric, interface_address)) { return; } // The destination node may not exist if it doesn't // create it. Half the time it will exist Vertex dst; dst.set_version(_ospf.get_version()); dst.set_type(OspfTypes::Router); dst.set_nodeid(lsapeer->get_header().get_link_state_id()); dst.set_lsa(lsapeer); // If the src is the origin then set the address of the // dest. This is the nexthop address from the origin. if (src.get_origin()) { dst.set_address(IPv4(htonl(interface_address))); } if (!spt.exists_node(dst)) { spt.add_node(dst); } update_edge(spt, src, rl.get_metric(), dst); update_edge(spt, dst, metric, src); } } template void AreaRouter::routing_router_link_transitV2(Spt& spt, const Vertex& src, RouterLsa *rlsa, RouterLink rl) { size_t index; if (!find_network_lsa(rl.get_link_id(), index)) { return; } Lsa::LsaRef lsan = _db[index]; // This can probably never happen if (lsan->maxage()) { XLOG_WARNING("LSA in database MaxAge\n%s", cstring(*lsan)); return; } // Both nodes exist check for // bi-directional connectivity. NetworkLsa *nlsa = dynamic_cast(lsan.get()); XLOG_ASSERT(nlsa); if (!bidirectional(rlsa->get_header().get_link_state_id(), rl, nlsa)) { return; } uint32_t nlsid = lsan->get_header().get_link_state_id(); // Put both links back. If the network // vertex is not in the SPT put it in. // Create a destination node. Vertex dst; dst.set_version(_ospf.get_version()); dst.set_type(OspfTypes::Network); dst.set_nodeid(nlsid); dst.set_lsa(lsan); // If the src is the origin then set the address of the // dest. This is the nexthop address from the origin. if (src.get_origin()) { dst.set_address(IPv4(htonl(nlsid))); } if (!spt.exists_node(dst)) { spt.add_node(dst); } uint32_t rlsid = rlsa->get_header().get_link_state_id(); bool dr = rlsid == nlsa->get_header().get_advertising_router(); update_edge(spt, src, rl.get_metric(), dst); // Reverse edge update_edge(spt, dst, 0, src); if (!src.get_origin()) { return; } // We are here because this Network-LSA was either generated by // this router, or was generated by a router on a directly // connected interface. // It is necessary to make an edge to all the bidirectionally // connected routers. If we don't do this then if this router is // the designated router all the next hops will incorrectly point // at this router itself. If this is not the designated router and // hence did not generate the Network-LSA *all* next hops will go // through the designated router which is strictly correct but // does add an extra hop, if the designated router is not on the // direct path. If this is the designated router then it is safe // to make an edge after making the bidirectional check as this // router by definition has a full adjacency to all the // routers. If this is not the designated router then it is // necessary to make the 2-Way check. // Even though the extra edges have been made an edge to the // Network-LSA was also made. If this edge did not exist it would // appear that the network represented by the Network-LSA was not // directly connected because the route to it would be via one of // the neighbours. Therefore the original edge is left in, if for // some reason the edge to the Network-LSA starts to win over the // direct edge to a neighbour, then don't put an edge pointing to // the Network-LSA and put a marker into the vertex itself // stopping any nodes with the marker being installed in the // routing table. The comparator in the vertex is biased towards // router vertexs over network vertexs, so there should not be a // problem XLOG_ASSERT(src.get_origin()); const uint16_t router_ls_type = RouterLsa(_ospf.get_version()).get_header().get_ls_type(); list& attached_routers = nlsa->get_attached_routers(); list::iterator i; for (i = attached_routers.begin(); i != attached_routers.end(); i++) { // Don't make an edge back to this router. if (*i == rlsid) continue; // If this is the designated router then we now have a full // adjacency with all the attached routers as this router // created this list. If we are not the designated router then // we need to check that we are at least 2-Way. if (!dr) { if (!neighbour_at_least_two_way(*i)) continue; } // Find the Router-LSA that points back to this Network-LSA. Ls_request lsr(_ospf.get_version(), router_ls_type, *i, *i); size_t index; if (find_lsa(lsr, index)) { Lsa::LsaRef lsapeer = _db[index]; // This can probably never happen if (lsapeer->maxage()) { XLOG_WARNING("LSA in database MaxAge\n%s", cstring(*lsapeer)); continue; } uint32_t interface_address; if (!bidirectionalV2(dynamic_cast(lsapeer.get()), nlsa, interface_address)) continue; // Router-LSA <=> Network-LSA <=> Router-LSA. // There is bidirectional connectivity from the original // Router-LSA through to this one found in the // Network-LSA, make an edge. // The destination node may not exist if it doesn't // create it. Half the time it will exist Vertex dst; dst.set_version(_ospf.get_version()); dst.set_type(OspfTypes::Router); dst.set_nodeid(lsapeer->get_header().get_link_state_id()); dst.set_lsa(lsapeer); // If the src is the origin then set the address of the // dest. This is the nexthop address from the origin. if (src.get_origin()) { dst.set_address(IPv4(htonl(interface_address))); } if (!spt.exists_node(dst)) { spt.add_node(dst); } update_edge(spt, src, rl.get_metric(), dst); } } } template void AreaRouter::routing_router_link_stubV2(Spt& spt, const Vertex& src, RouterLsa *rlsa, RouterLink rl) { Vertex dst; dst.set_version(_ospf.get_version()); dst.set_type(OspfTypes::Network); // Set the host bits to generate a unique nodeid. dst.set_nodeid(rl.get_link_id() | ~rl.get_link_data()); // XXX Temporarily // Create a Network LSA to satisfy the routing calculation NetworkLsa *nlsa = new NetworkLsa(_ospf.get_version()); nlsa->get_header().set_link_state_id(rl.get_link_id()); nlsa->get_header().set_advertising_router(rlsa->get_header(). get_link_state_id()); nlsa->set_network_mask(rl.get_link_data()); Lsa::LsaRef lsan = Lsa::LsaRef(nlsa); // dst.set_lsa(lsan); // if (!spt.exists_node(dst)) { spt.add_node(dst); } spt.add_edge(src, rl.get_metric(), dst); } template void AreaRouter::routing_router_lsaV3(Spt& spt, const Vertex& src, RouterLsa *rlsa) { debug_msg("Spt %s Vertex %s \n%s\n", cstring(spt), cstring(src), cstring(*rlsa)); const list &rl = rlsa->get_router_links(); list::const_iterator l = rl.begin(); for(; l != rl.end(); l++) { // fprintf(stderr, "RouterLink %s\n", cstring(*l)); switch(l->get_type()) { case RouterLink::p2p: case RouterLink::vlink: routing_router_link_p2p_vlinkV3(spt, src, rlsa, *l); break; case RouterLink::transit: routing_router_link_transitV3(spt, src, rlsa, *l); break; case RouterLink::stub: XLOG_FATAL("OSPFv3 does not support type stub"); break; } } } template void AreaRouter::routing_router_link_p2p_vlinkV3(Spt& spt, const Vertex& src, RouterLsa *rlsa, RouterLink rl) { // Find the Router-LSA that is pointed at by this router link. // Remember that an OSPSv3 router can generate multiple // Router-LSAs so it is necessary to search all of them for the // corresponding router link. bool found = false; Lsa::LsaRef lsapeer; RouterLsa *rlsapeer = 0; uint16_t metric; for(size_t index = 0;; index++) { if (!find_router_lsa(rl.get_neighbour_router_id(), index)) return; lsapeer = _db[index]; // This can probably never happen if (lsapeer->maxage()) { XLOG_WARNING("LSA in database MaxAge\n%s", cstring(*lsapeer)); continue; } // fprintf(stderr, "peer %s\n", cstring(*lsapeer)); // Check that this Router-LSA points back to the original. rlsapeer = dynamic_cast(lsapeer.get()); XLOG_ASSERT(0 != rlsapeer); if (bidirectionalV3(rl.get_type(), rlsa->get_header().get_advertising_router(), rlsapeer, metric)) { found = true; break; } } if (!found) return; // The coressponding router link has been found but should it be used? Options options(_ospf.get_version(), rlsapeer->get_options()); if (!options.get_v6_bit()) return; if (!options.get_r_bit()) return; // The destination node may not exist if it doesn't // create it. Half the time it will exist Vertex dst; dst.set_version(_ospf.get_version()); dst.set_type(OspfTypes::Router); dst.set_nodeid(lsapeer->get_header().get_advertising_router()); dst.get_lsas().push_back(lsapeer); // If the src is the origin then set the address of the // dest. This is the nexthop address from the origin. if (src.get_origin()) { if (RouterLink::p2p == rl.get_type()) { // Find the nexthop address from the router's Link-LSA. If the // nexthop can't be found then there is no point putting // this router into the graph. If this is a directly adjacent // Virtual link then still use the Link-local address. A interface_address; if (!find_interface_address(rl.get_neighbour_router_id(), rl.get_neighbour_interface_id(), interface_address)) return; dst.set_address(interface_address); dst.set_nexthop_id(rl.get_interface_id()); } else if (RouterLink::vlink == rl.get_type()) { dst.set_address(IPv6::ZERO()); dst.set_nexthop_id(OspfTypes::UNUSED_INTERFACE_ID); } else { XLOG_FATAL("Unexpected router link %s", cstring(rl)); } } if (!spt.exists_node(dst)) { spt.add_node(dst); } update_edge(spt, src, rl.get_metric(), dst); update_edge(spt, dst, metric, src); } template void AreaRouter::routing_router_link_transitV3(Spt& spt, const Vertex& src, RouterLsa *rlsa, RouterLink rl) { OspfTypes::Version version = _ospf.get_version(); // Find the Network-LSA that this router link points at. Ls_request lsr(version, NetworkLsa(version).get_header().get_ls_type(), rl.get_neighbour_interface_id(), rl.get_neighbour_router_id()); size_t index; if (!find_lsa(lsr, index)) { return; } Lsa::LsaRef lsan = _db[index]; // This can probably never happen if (lsan->maxage()) { XLOG_WARNING("LSA in database MaxAge\n%s", cstring(*lsan)); return; } // Both nodes exist check for // bi-directional connectivity. NetworkLsa *nlsa = dynamic_cast(lsan.get()); XLOG_ASSERT(nlsa); if (!bidirectional(rlsa->get_header().get_advertising_router(), rl, nlsa)) { return; } // Put both links back. If the network // vertex is not in the SPT put it in. // Create a destination node. Vertex dst; dst.set_version(_ospf.get_version()); dst.set_type(OspfTypes::Network); dst.set_nodeid(lsan->get_header().get_advertising_router()); dst.set_interface_id(lsan->get_header().get_link_state_id()); dst.get_lsas().push_back(lsan); // If the src is the origin then set the address of the // dest. This is the nexthop address from the origin. if (src.get_origin()) { // Find the nexthop address from the router's Link-LSA. If the // nexthop can't be found then there is no point putting // this router into the graph. A interface_address; if (!find_interface_address(rl.get_neighbour_router_id(), rl.get_neighbour_interface_id(), interface_address)) return; dst.set_address(interface_address); dst.set_nexthop_id(rl.get_interface_id()); } if (!spt.exists_node(dst)) { spt.add_node(dst); } uint32_t rladv = rlsa->get_header().get_advertising_router(); bool dr = rladv == nlsa->get_header().get_advertising_router(); update_edge(spt, src, rl.get_metric(), dst); // Reverse edge update_edge(spt, dst, 0, src); if (!src.get_origin()) { return; } // We are here because this Network-LSA was either generated by // this router, or was generated by a router on a directly // connected interface. // It is necessary to make an edge to all the bidirectionally // connected routers. If we don't do this then if this router is // the designated router all the next hops will incorrectly point // at this router itself. If this is not the designated router and // hence did not generate the Network-LSA *all* next hops will go // through the designated router which is strictly correct but // does add an extra hop, if the designated router is not on the // direct path. If this is the designated router then it is safe // to make an edge after making the bidirectional check as this // router by definition has a full adjacency to all the // routers. If this is not the designated router then it is // necessary to make the 2-Way check. // Even though the extra edges have been made an edge to the // Network-LSA was also made. If this edge did not exist it would // appear that the network represented by the Network-LSA was not // directly connected because the route to it would be via one of // the neighbours. Therefore the original edge is left in, if for // some reason the edge to the Network-LSA starts to win over the // direct edge to a neighbour, then don't put an edge pointing to // the Network-LSA and put a marker into the vertex itself // stopping any nodes with the marker being installed in the // routing table. The comparator in the vertex is biased towards // router vertexs over network vertexs, so there should not be a // problem XLOG_ASSERT(src.get_origin()); list& attached_routers = nlsa->get_attached_routers(); list::iterator i; for (i = attached_routers.begin(); i != attached_routers.end(); i++) { // Don't make an edge back to this router. if (*i == rladv) continue; // If this is the designated router then we know have a full // adjacency with all the attached routers as this router // created this list. If we are not the designated router then // we need to check that we are at least 2-Way. if (!dr) { if (!neighbour_at_least_two_way(*i)) continue; } // Find the Router-LSA that points back to this Network-LSA. // Remember that an OSPSv3 router can generate multiple // Router-LSAs so it is necessary to search all of them for the // corresponding router link. bool found = false; Lsa::LsaRef lsapeer; RouterLsa *rlsapeer = 0; uint32_t interface_id; for(size_t index = 0;; index++) { if (!find_router_lsa(*i, index)) break; lsapeer = _db[index]; // This can probably never happen if (lsapeer->maxage()) { XLOG_WARNING("LSA in database MaxAge\n%s", cstring(*lsapeer)); continue; } // fprintf(stderr, "peer %s\n", cstring(*lsapeer)); rlsapeer = dynamic_cast(lsapeer.get()); XLOG_ASSERT(0 != rlsapeer); if (bidirectionalV3(rlsapeer, nlsa, interface_id)) { found = true; break; } } if (!found) continue; // The coressponding router link has been found but should it // be used? Options options(_ospf.get_version(), rlsapeer->get_options()); if (!options.get_v6_bit()) continue; if (!options.get_r_bit()) continue; uint32_t adv = lsapeer->get_header().get_advertising_router(); // Router-LSA <=> Network-LSA <=> Router-LSA. // There is bidirectional connectivity from the original // Router-LSA through to this one found in the // Network-LSA, make an edge. // The destination node may not exist if it doesn't // create it. Half the time it will exist Vertex dst; dst.set_version(_ospf.get_version()); dst.set_type(OspfTypes::Router); dst.set_nodeid(adv); dst.get_lsas().push_back(lsapeer); // If the src is the origin then set the address of the // dest. This is the nexthop address from the origin. if (src.get_origin()) { // Find the nexthop address from the router's Link-LSA. If the // nexthop can't be found then there is no point putting // this router into the graph. A interface_address; if (!find_interface_address(adv, interface_id, interface_address)) continue; dst.set_address(interface_address); dst.set_nexthop_id(rl.get_interface_id()); } if (!spt.exists_node(dst)) { spt.add_node(dst); } update_edge(spt, src, rl.get_metric(), dst); } } /*************************************************************************/ #ifdef UNFINISHED_INCREMENTAL_UPDATE template void AreaRouter::routing_begin() { XLOG_ASSERT(_new_lsas.empty()); #ifdef PARANOIA list > r; _spt.compute(r); XLOG_ASSERT(r.empty()); #endif // Put back this routers interfaces. routing_add(_router_lsa, true); } template void AreaRouter::routing_add(Lsa::LsaRef lsar, bool known) { debug_msg("%s\n", cstring(*lsar)); // XXX - This lookup is currently expensive after TODO 28 it will // be fine. size_t index; if (!find_lsa(lsar, index)) XLOG_FATAL("This LSA must be in the database\n%s\n", cstring(*lsar)); _new_lsas.push_back(Bucket(index, known)); } template void AreaRouter::routing_delete(Lsa::LsaRef lsar) { debug_msg("%s\n", cstring(*lsar)); RouterLsa *rlsa; if (0 != (rlsa = dynamic_cast(lsar.get()))) { if (rlsa->get_v_bit()) _TransitCapability--; } XLOG_WARNING("TBD \n%s", cstring(*lsar)); } template void AreaRouter::routing_end() { typename list::iterator i; RouterLsa *rlsa; NetworkLsa *nlsa; // First part of the routing computation only consider Router-LSAs i = _new_lsas.begin(); while (i != _new_lsas.end()) { Lsa::LsaRef lsar = _db[i->_index]; if (0 != (rlsa = dynamic_cast(lsar.get()))) { if (rlsa->get_v_bit()) _TransitCapability++; Vertex v; v.set_version(_ospf.get_version()); v.set_type(Vertex::Router); v.set_nodeid(rlsa->get_header().get_link_state_id()); // XXX // Really want to remove all the links but removing a node // and putting it back has the same effect. if (i->_known && _spt.exists_node(v)) { printf("%s Remove %s\n", pr_id(_ospf.get_router_id()).c_str(), cstring(v)); _spt.remove_node(v); } else { printf("%s New %s\n", pr_id(_ospf.get_router_id()).c_str(), cstring(v)); } printf("%s Add %s\n", pr_id(_ospf.get_router_id()).c_str(), cstring(v)); _spt.add_node(v); switch(_ospf.get_version()) { case OspfTypes::V2: routing_router_lsaV2(v, rlsa); break; case OspfTypes::V3: routing_router_lsaV3(v, rlsa); break; } _new_lsas.erase(i++); } else if (0 != (nlsa = dynamic_cast(lsar.get()))) { printf("%s %s\n", pr_id(_ospf.get_router_id()).c_str(), cstring(*nlsa)); i++; } else { i++; } } i = _new_lsas.begin(); while (i != _new_lsas.end()) { _new_lsas.erase(i++); } list > r; _spt.compute(r); list >::const_iterator ri; for(ri = r.begin(); ri != r.end(); ri++) XLOG_WARNING("TBD: Add route:\n%s %s", pr_id(_ospf.get_router_id()).c_str(), ri->str().c_str()); } #endif template class AreaRouter; template class AreaRouter; xorp/ospf/ospf.hh0000664000076400007640000006702011643705557014141 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __OSPF_OSPF_HH__ #define __OSPF_OSPF_HH__ /** * OSPF Types */ struct OspfTypes { /** * The OSPF version. */ enum Version {V2 = 2, V3 = 3}; /** * The type of an OSPF packet. */ typedef uint16_t Type; /** * Router ID. */ typedef uint32_t RouterID; /** * Area ID. */ typedef uint32_t AreaID; /** * Link Type */ enum LinkType { PointToPoint, BROADCAST, NBMA, PointToMultiPoint, VirtualLink }; /** * Authentication type: OSPFv2 standard header. */ typedef uint16_t AuType; static const AuType NULL_AUTHENTICATION = 0; static const AuType SIMPLE_PASSWORD = 1; static const AuType CRYPTOGRAPHIC_AUTHENTICATION = 2; /** * Area Type */ enum AreaType { NORMAL, // Normal Area STUB, // Stub Area NSSA, // Not-So-Stubby Area }; /** * Routing Entry Type. */ enum VertexType { Router, Network }; /** * NSSA Translator Role. */ enum NSSATranslatorRole { ALWAYS, CANDIDATE }; /** * NSSA Translator State. */ enum NSSATranslatorState { ENABLED, ELECTED, DISABLED, }; /** * The AreaID for the backbone area. */ static const AreaID BACKBONE = 0; /** * An opaque handle that identifies a peer. */ typedef uint32_t PeerID; /** * An opaque handle that identifies a neighbour. */ typedef uint32_t NeighbourID; /** * The IP protocol number used by OSPF. */ static const uint16_t IP_PROTOCOL_NUMBER = 89; /** * An identifier meaning all peers. No single peer can have this * identifier. */ static const PeerID ALLPEERS = 0; /** * An identifier meaning all neighbours. No single neighbour can * have this identifier. */ static const NeighbourID ALLNEIGHBOURS = 0; /** * An interface ID that will never be allocated OSPFv3 only. */ static const uint32_t UNUSED_INTERFACE_ID = 0; /** * * The maximum time between distinct originations of any particular * LSA. If the LS age field of one of the router's self-originated * LSAs reaches the value LSRefreshTime, a new instance of the LSA * is originated, even though the contents of the LSA (apart from * the LSA header) will be the same. The value of LSRefreshTime is * set to 30 minutes. */ static const uint32_t LSRefreshTime = 30 * 60; /** * The minimum time between distinct originations of any particular * LSA. The value of MinLSInterval is set to 5 seconds. */ static const uint32_t MinLSInterval = 5; /** * For any particular LSA, the minimum time that must elapse * between reception of new LSA instances during flooding. LSA * instances received at higher frequencies are discarded. The * value of MinLSArrival is set to 1 second. */ static const uint32_t MinLSArrival = 1; /** * The maximum age that an LSA can attain. When an LSA's LS age * field reaches MaxAge, it is reflooded in an attempt to flush the * LSA from the routing domain. LSAs of age MaxAge * are not used in the routing table calculation. The value of * MaxAge is set to 1 hour. */ static const uint32_t MaxAge = 60 * 60; /** * When the age of an LSA in the link state database hits a * multiple of CheckAge, the LSA's checksum is verified. An * incorrect checksum at this time indicates a serious error. The * value of CheckAge is set to 5 minutes. */ static const uint32_t CheckAge = 5 * 60; /* * The maximum time dispersion that can occur, as an LSA is flooded * throughout the AS. Most of this time is accounted for by the * LSAs sitting on router output queues (and therefore not aging) * during the flooding process. The value of MaxAgeDiff is set to * 15 minutes. */ static const int32_t MaxAgeDiff = 15 * 60; /* * The metric value indicating that the destination described by an * LSA is unreachable. Used in summary-LSAs and AS-external-LSAs as * an alternative to premature aging. It is * defined to be the 24-bit binary value of all ones: 0xffffff. */ static const uint32_t LSInfinity = 0xffffff; /* * The Destination ID that indicates the default route. This route * is used when no other matching routing table entry can be found. * The default destination can only be advertised in AS-external- * LSAs and in stub areas' type 3 summary-LSAs. Its value is the * IP address 0.0.0.0. Its associated Network Mask is also always * 0.0.0.0. */ static const uint32_t DefaultDestination = 0; /* * The value used for LS Sequence Number when originating the first * instance of any LSA. */ static const int32_t InitialSequenceNumber = 0x80000001; /* * The maximum value that LS Sequence Number can attain. */ static const int32_t MaxSequenceNumber = 0x7fffffff; }; /** * Interface name of a virtual link endpoint. */ #define VLINK "vlink" /** * MTU of a virtual link. */ #define VLINK_MTU 576 /** * XRL target name. */ #define TARGET_OSPFv2 "ospfv2" #define TARGET_OSPFv3 "ospfv3" /** * Get the XRL target name. */ inline const char * xrl_target(OspfTypes::Version version) { switch (version) { case OspfTypes::V2: return TARGET_OSPFv2; break; case OspfTypes::V3: return TARGET_OSPFv3; break; } XLOG_UNREACHABLE(); } /** * Pretty print a router or area ID. */ inline string pr_id(uint32_t id) { return IPv4(htonl(id)).str(); } /** * Set a router or area ID using dot notation: "128.16.64.16". */ inline uint32_t set_id(const char *addr) { return ntohl(IPv4(addr).addr()); } /** * Pretty print the link type. */ inline string pp_link_type(OspfTypes::LinkType link_type) { switch(link_type) { case OspfTypes::PointToPoint: return "PointToPoint"; case OspfTypes::BROADCAST: return "BROADCAST"; case OspfTypes::NBMA: return "NBMA"; case OspfTypes::PointToMultiPoint: return "PointToMultiPoint"; case OspfTypes::VirtualLink: return "VirtualLink"; } XLOG_UNREACHABLE(); } /** * Convert from a string to the type of area */ inline OspfTypes::LinkType from_string_to_link_type(const string& type, bool& status) { status = true; if (type == "p2p") return OspfTypes::PointToPoint; else if (type == "broadcast") return OspfTypes::BROADCAST; else if (type == "nbma") return OspfTypes::NBMA; else if (type == "p2m") return OspfTypes::PointToMultiPoint; else if (type == "vlink") return OspfTypes::VirtualLink; XLOG_WARNING("Unable to match %s", type.c_str()); status = false; return OspfTypes::BROADCAST; } /** * Pretty print the area type. */ inline string pp_area_type(OspfTypes::AreaType area_type) { switch(area_type) { case OspfTypes::NORMAL: return "NORMAL"; case OspfTypes::STUB: return "STUB"; case OspfTypes::NSSA: return "NSSA"; } XLOG_UNREACHABLE(); } /** * Convert from a string to the type of area */ inline OspfTypes::AreaType from_string_to_area_type(const string& type, bool& status) { status = true; if (type == "normal") return OspfTypes::NORMAL; else if (type == "stub") return OspfTypes::STUB; else if (type == "nssa") return OspfTypes::NSSA; XLOG_WARNING("Unable to match %s", type.c_str()); status = false; return OspfTypes::NORMAL; } /** * Router ID and associated interface ID. */ struct RouterInfo { RouterInfo(OspfTypes::RouterID router_id) : _router_id(router_id), _interface_id(0) {} RouterInfo(OspfTypes::RouterID router_id, uint32_t interface_id) : _router_id(router_id), _interface_id(interface_id) {} OspfTypes::RouterID _router_id; // Neighbour Router ID. uint32_t _interface_id; // Neighbour interface ID OSPFv3 only. }; /** * Neighbour information that is returned by XRLs. */ struct NeighbourInfo { string _address; // Address of neighbour. string _interface; // Interface name. string _state; // The current state. IPv4 _rid; // The neighbours router id. uint32_t _priority; // The priority in the hello packet. uint32_t _deadtime; // Number of seconds before the // peering is considered down. IPv4 _area; // The area this neighbour belongs to. uint32_t _opt; // The options on the hello packet. IPv4 _dr; // The designated router. IPv4 _bdr; // The backup designated router. uint32_t _up; // Time there has been neighbour awareness. uint32_t _adjacent; // Time peering has been adjacent. }; /** * OSPFv3 only, the information stored about an interface address. */ template struct AddressInfo { AddressInfo(A address, uint32_t prefix = 0, bool enabled = false) : _address(address), _prefix(prefix), _enabled(enabled) {} bool operator<(const AddressInfo& other) const { return _address < other._address; } A _address; // The address. uint32_t _prefix; // Prefix length associated with this address. bool _enabled; // True if the address should be used. }; #include "policy_varrw.hh" #include "io.hh" #include "exceptions.hh" #include "lsa.hh" #include "packet.hh" #include "transmit.hh" #include "peer_manager.hh" #include "external.hh" #include "vlink.hh" #include "routing_table.hh" #include "trace.hh" template class Ospf { public: Ospf(OspfTypes::Version version, EventLoop& eventloop, IO* io); /** * @return version of OSPF this implementation represents. */ OspfTypes::Version version() { return _version; } /** * @return true if ospf should still be running. */ bool running() { return _io->status() != SERVICE_SHUTDOWN; } /** * Status of process. */ ProcessStatus status(string& reason) { if (PROC_STARTUP == _process_status) { if (SERVICE_RUNNING == _io->status()) { _process_status = PROC_READY; _reason = "Running"; } } reason = _reason; return _process_status; } /** * Shutdown OSPF. */ void shutdown() { _io->shutdown(); _reason = "shutting down"; _process_status = PROC_SHUTDOWN; } /** * Used to send traffic on the IO interface. */ bool transmit(const string& interface, const string& vif, A dst, A src, int ttl, uint8_t* data, uint32_t len); /** * The callback method that is called when data arrives on the IO * interface. */ void receive(const string& interface, const string& vif, A dst, A src, uint8_t* data, uint32_t len); /** * Enable the interface/vif to receive frames. */ bool enable_interface_vif(const string& interface, const string& vif); /** * Disable this interface/vif from receiving frames. */ bool disable_interface_vif(const string& interface, const string& vif); /** * Is this interface/vif enabled? * This is a question asked of the FEA, has the interface/vif been * marked as up. * * @return true if it is. */ bool enabled(const string& interface, const string& vif); /** * Is this interface/vif/address enabled? * This is a question asked of the FEA, has the interface/vif been * marked as up. * * @return true if it is. */ bool enabled(const string& interface, const string& vif, A address); /** * Add a callback for tracking the interface/vif status. * * The callback will be invoked whenever the status of the tuple * (interface, vif) is changed from disabled to enabled * or vice-versa. * * @param cb the callback to register. */ void register_vif_status(typename IO::VifStatusCb cb) { _io->register_vif_status(cb); } /** * Add a callback for tracking the interface/vif/address status. * * The callback will be invoked whenever the status of the tuple * (interface, vif, address) is changed from disabled to enabled * or vice-versa. * * @param cb the callback to register. */ void register_address_status(typename IO::AddressStatusCb cb) { _io->register_address_status(cb); } /** * Get all addresses associated with this interface/vif. * * @param interface the name of the interface * @param vif the name of the vif * @param addresses (out argument) list of associated addresses * * @return true if there are no errors. */ bool get_addresses(const string& interface, const string& vif, list& addresses) const; /** * Get a link local address for this interface/vif if available. * * @param interface the name of the interface * @param vif the name of the vif * @param address (out argument) set if address is found. * * @return true if a link local address is available. * */ bool get_link_local_address(const string& interface, const string& vif, A& address); /** * Get the interface ID required for OSPFv3. * The vif argument is required for virtual links. */ bool get_interface_id(const string& interface, const string& vif, uint32_t& interface_id); /** * Given an interface ID return the interface and vif. */ bool get_interface_vif_by_interface_id(uint32_t interface_id, string& interface, string& vif); /** * @return prefix length for this address. */ bool get_prefix_length(const string& interface, const string& vif, A address, uint16_t& prefix_length); /** * @return the mtu for this interface. */ uint32_t get_mtu(const string& interface); /** * On the interface/vif join this multicast group. */ bool join_multicast_group(const string& interface, const string& vif, A mcast); /** * On the interface/vif leave this multicast group. */ bool leave_multicast_group(const string& interface, const string& vif, A mcast); /** * Set the hello interval in seconds. */ bool set_hello_interval(const string& interface, const string& vif, OspfTypes::AreaID area, uint16_t hello_interval); #if 0 /** * Set options. */ bool set_options(const string& interface, const string& vif, OspfTypes::AreaID area, uint32_t options); #endif /** * Create a virtual link * * @param rid neighbours router ID. */ bool create_virtual_link(OspfTypes::RouterID rid); /** * Delete a virtual link * * @param rid neighbours router ID. */ bool delete_virtual_link(OspfTypes::RouterID rid); /** * Attach this transit area to the neighbours router ID. */ bool transit_area_virtual_link(OspfTypes::RouterID rid, OspfTypes::AreaID transit_area); /** * Set router priority. */ bool set_router_priority(const string& interface, const string& vif, OspfTypes::AreaID area, uint8_t priority); /** * Set the router dead interval in seconds. */ bool set_router_dead_interval(const string& interface, const string& vif, OspfTypes::AreaID area, uint32_t router_dead_interval); /** * Set the interface cost. */ bool set_interface_cost(const string& interface, const string& vif, OspfTypes::AreaID area, uint16_t interface_cost); /** * Set the RxmtInterval. */ bool set_retransmit_interval(const string& interface, const string& vif, OspfTypes::AreaID area, uint16_t retransmit_interval); /** * Set InfTransDelay */ bool set_inftransdelay(const string& interface, const string& vif, OspfTypes::AreaID area, uint16_t inftransdelay); /** * Set a simple password authentication key. * * Note that the current authentication handler is replaced with * a simple password authentication handler. * * @param interface the interface name. * @param vif the vif name. * @param area the area ID. * @param password the password to set. * @param the error message (if error). * @return true on success, otherwise false. */ bool set_simple_authentication_key(const string& interface, const string& vif, OspfTypes::AreaID area, const string& password, string& error_msg); /** * Delete a simple password authentication key. * * Note that after the deletion the simple password authentication * handler is replaced with a Null authentication handler. * * @param interface the interface name. * @param vif the vif name. * @param area the area ID. * @param the error message (if error). * @return true on success, otherwise false. */ bool delete_simple_authentication_key(const string& interface, const string& vif, OspfTypes::AreaID area, string& error_msg); /** * Set an MD5 authentication key. * * Note that the current authentication handler is replaced with * an MD5 authentication handler. * * @param interface the interface name. * @param vif the vif name. * @param area the area ID. * @param key_id unique ID associated with key. * @param password phrase used for MD5 digest computation. * @param start_timeval start time when key becomes valid. * @param end_timeval end time when key becomes invalid. * @param max_time_drift the maximum time drift among all routers. * @param the error message (if error). * @return true on success, otherwise false. */ bool set_md5_authentication_key(const string& interface, const string& vif, OspfTypes::AreaID area, uint8_t key_id, const string& password, const TimeVal& start_timeval, const TimeVal& end_timeval, const TimeVal& max_time_drift, string& error_msg); /** * Delete an MD5 authentication key. * * Note that after the deletion if there are no more valid MD5 keys, * the MD5 authentication handler is replaced with a Null authentication * handler. * * @param interface the interface name. * @param vif the vif name. * @param area the area ID. * @param key_id the ID of the key to delete. * @param the error message (if error). * @return true on success, otherwise false. */ bool delete_md5_authentication_key(const string& interface, const string& vif, OspfTypes::AreaID area, uint8_t key_id, string& error_msg); /** * Toggle the passive status of an interface. */ bool set_passive(const string& interface, const string& vif, OspfTypes::AreaID area, bool passive, bool host); /** * If this is a "stub" or "nssa" area toggle the sending of a default * route. */ bool originate_default_route(OspfTypes::AreaID area, bool enable); /** * Set the StubDefaultCost, the default cost sent in a default route in a * "stub" or "nssa" area. */ bool stub_default_cost(OspfTypes::AreaID area, uint32_t cost); /** * Toggle the sending of summaries into "stub" or "nssa" areas. */ bool summaries(OspfTypes::AreaID area, bool enable); /** * Send router alerts in IP packets or not. */ bool set_ip_router_alert(bool alert); /** * Add area range. */ bool area_range_add(OspfTypes::AreaID area, IPNet net, bool advertise); /** * Delete area range. */ bool area_range_delete(OspfTypes::AreaID area, IPNet net); /** * Change the advertised state of this area. */ bool area_range_change_state(OspfTypes::AreaID area, IPNet net, bool advertise); /** * Get a single lsa from an area. A stateless mechanism to get LSAs. The * client of this interface should start from zero and continue to request * LSAs (incrementing index) until toohigh becomes true. * * @param area database that is being searched. * @param index into database starting from 0. * @param valid true if a LSA has been returned. Some index values do not * contain LSAs. This should not be considered an error. * @param toohigh true if no more LSA exist after this index. * @param self if true this LSA was originated by this router. * @param lsa if valid is true the LSA at index. */ bool get_lsa(const OspfTypes::AreaID area, const uint32_t index, bool& valid, bool& toohigh, bool& self, vector& lsa); /** * Get a list of all the configured areas. */ bool get_area_list(list& areas) const; /** * Get a list of all the neighbours. */ bool get_neighbour_list(list& neighbours) const; /** * Get state information about this neighbour. * * @param nid neighbour information is being request about. * @param ninfo if neighbour is found its information. * */ bool get_neighbour_info(OspfTypes::NeighbourID nid, NeighbourInfo& ninfo) const; /** * Clear the database. */ bool clear_database(); /** * Add route * * @param net network * @param nexthop * @param nexthop_id interface ID towards the nexthop * @param metric to network * @param equal true if this in another route to the same destination. * @param discard true if this is a discard route. * @param policytags policy info to the RIB. */ bool add_route(IPNet net, A nexthop, uint32_t nexthop_id, uint32_t metric, bool equal, bool discard, const PolicyTags& policytags); /** * Replace route * * @param net network * @param nexthop * @param nexthop_id interface ID towards the nexthop * @param metric to network * @param equal true if this in another route to the same destination. * @param discard true if this is a discard route. * @param policytags policy info to the RIB. */ bool replace_route(IPNet net, A nexthop, uint32_t nexthop_id, uint32_t metric, bool equal, bool discard, const PolicyTags& policytags); /** * Delete route */ bool delete_route(IPNet net); /** * Configure a policy filter * * @param filter Id of filter to configure. * @param conf Configuration of filter. */ void configure_filter(const uint32_t& filter, const string& conf); /** * Reset a policy filter. * * @param filter Id of filter to reset. */ void reset_filter(const uint32_t& filter); /** * Push routes through policy filters for re-filtering. */ void push_routes(); /** * Originate a route. * * @param net to announce * @param nexthop to forward to * @param metric * @param policytags policy-tags associated with route. * * @return true on success */ bool originate_route(const IPNet& net, const A& nexthop, const uint32_t& metric, const PolicyTags& policytags); /** * Withdraw a route. * * @param net to withdraw * * @return true on success */ bool withdraw_route(const IPNet& net); /** * Get the current OSPF version. */ OspfTypes::Version get_version() const { return _version; } /** * @return a reference to the eventloop, required for timers etc... */ EventLoop& get_eventloop() { return _eventloop; } /** * The test status of OSPF. */ void set_testing(bool testing) {_testing = testing; } /** * @return true if OSPF is being tested. */ bool get_testing() const { return _testing; } /** * @return a reference to the PeerManager. */ PeerManager& get_peer_manager() { return _peer_manager; } /** * @return a reference to the RoutingTable. */ RoutingTable& get_routing_table() { return _routing_table; } /** * @return a reference to the LSA decoder. */ LsaDecoder& get_lsa_decoder() { return _lsa_decoder; } /** * @return a reference to the policy filters */ PolicyFilters& get_policy_filters() { return _policy_filters; } /** * Get the Instance ID. */ uint8_t get_instance_id() const { XLOG_ASSERT(OspfTypes::V3 == get_version()); return _instance_id; } /** * Set the Instance ID. */ void set_instance_id(uint8_t instance_id) { XLOG_ASSERT(OspfTypes::V3 == get_version()); _instance_id = instance_id; } /** * Get the Router ID. */ OspfTypes::RouterID get_router_id() const { return _router_id; } /** * Set the Router ID. */ void set_router_id(OspfTypes::RouterID id); /** * Get RFC 1583 compatibility. */ bool get_rfc1583_compatibility() const { return _rfc1583_compatibility;} /** * Set RFC 1583 compatibility. */ void set_rfc1583_compatibility(bool compatibility) { // Don't check for equality we can use this as a hack to force // routing recomputation. _rfc1583_compatibility = compatibility; _peer_manager.routing_recompute_all_areas(); } Trace& trace() { return _trace; } private: const OspfTypes::Version _version; // OSPF version. EventLoop& _eventloop; bool _testing; // True when testing. IO* _io; // Indirection for sending and // receiving packets, as well as // adding and deleting routes. string _reason; ProcessStatus _process_status; PacketDecoder _packet_decoder; // Packet decoders. LsaDecoder _lsa_decoder; // LSA decoders. PeerManager _peer_manager; RoutingTable _routing_table; PolicyFilters _policy_filters; // The policy filters. uint8_t _instance_id; // OSPFv3 Only OspfTypes::RouterID _router_id; // Router ID. bool _rfc1583_compatibility; // Preference rules for route // selection. map _iidmap; // OSPFv3 only mapping of // interface/vif to Instance IDs. Trace _trace; // Trace variables. }; // The original design did not leave MaxAge LSAs in the database. When // an LSA reached MaxAge it was removed from the database and existed // only in retransmission lists. If an LSA was received which seemed // to be from a previous incarnation of OSPF it had its age set to // MaxAge and was fired out, also not being added to the database. // If while a MaxAge LSA is on the retransmission only, either a new // LSA such as a Network-LSA is generated or an updated LSA arrives a // second LSA can be created with the same tuple. Two LSAs // can exist on the retransmission list. Leaving the a MaxAge LSA in // the database solves both problems. // #define MAX_AGE_IN_DATABASE #define PARANOIA #endif // __OSPF_OSPF_HH__ xorp/ospf/peer_manager.hh0000664000076400007640000006335011421137511015600 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/ospf/peer_manager.hh,v 1.100 2008/10/02 21:57:48 bms Exp $ #ifndef __OSPF_PEER_MANAGER_HH__ #define __OSPF_PEER_MANAGER_HH__ template class Ospf; template class PeerOut; template class AreaRouter; template class RouteEntry; template class External; template class Vlink; /** * Peer Manager: * 1) Monitor the state of the interfaces. An interface going * up/down will be monitored and trigger either adjacency * attempts or new (withdraw) LSAs. * 2) Manage interface configuration state. Control peers and * area state. * 3) Accept incoming hello's and demultiplex to the correct peer. * 4) Manage the set of peers (peers are bound to interface and * area). The peers themselves are hidden and are only * exposed by reference (PeerID). */ template class PeerManager { public: PeerManager(Ospf& ospf) : _ospf(ospf), _next_peerid(OspfTypes::ALLPEERS + 1), _external(ospf, _areas) {} ~PeerManager(); /** * Check area type. * * Verify that this area can be set to this area type. */ bool check_area_type(OspfTypes::AreaID area, OspfTypes::AreaType area_type); /** * Create an area router. * @param area * @param area_type * @param permissive if true won't fail if the area already exists. */ bool create_area_router(OspfTypes::AreaID area, OspfTypes::AreaType area_type, bool permissive = true); AreaRouter *get_area_router(OspfTypes::AreaID area); /** * Change the type of this area. */ bool change_area_router_type(OspfTypes::AreaID area, OspfTypes::AreaType area_type); /** * Destroy an area router. */ bool destroy_area_router(OspfTypes::AreaID area); /** * Add area range. */ bool area_range_add(OspfTypes::AreaID area, IPNet net, bool advertise); /** * Delete area range. */ bool area_range_delete(OspfTypes::AreaID area, IPNet net); /** * Change the advertised state of this area. */ bool area_range_change_state(OspfTypes::AreaID area, IPNet net, bool advertise); /** * Get a single lsa from an area. A stateless mechanism to get LSAs. The * client of this interface should start from zero and continue to request * LSAs (incrementing index) until toohigh becomes true. * * @param area database that is being searched. * @param index into database starting from 0. * @param valid true if a LSA has been returned. Some index values do not * contain LSAs. This should not be considered an error. * @param toohigh true if no more LSA exist after this index. * @param self if true this LSA was originated by this router. * @param lsa if valid is true the LSA at index. */ bool get_lsa(const OspfTypes::AreaID area, const uint32_t index, bool& valid, bool& toohigh, bool& self, vector& lsa); /** * Get a list of all the configured areas. */ bool get_area_list(list& areas) const; /** * Get a list of all the neighbours. */ bool get_neighbour_list(list& neighbours) const; /** * Get state information about this neighbour. * * @param nid neighbour information is being request about. * @param ninfo if neighbour is found its information. * */ bool get_neighbour_info(OspfTypes::NeighbourID nid, NeighbourInfo& ninfo) const; /** * Convert an interface/vif to a PeerID. * Throw an exception if no mapping is found. */ OspfTypes::PeerID get_peerid(const string& interface, const string& vif) throw(BadPeer); /** * Given a PeerID convert it back to an interface and vif. */ bool get_interface_vif_by_peerid(OspfTypes::PeerID peerid, string& interface, string& vif) const; /** * Is this interface/vif/address enabled? * This is a question asked of the FEA, has the interface/vif been * marked as up. * * @return true if it is. */ bool enabled(const string& interface, const string& vif, A address); /** * Create a peer. * @param interface * @param vif * @param source address of transmitted packets. * @param linktype broadcast or point-2-point, etc... * @param area ID of area * * @return PeerID on success otherwise throw an exception. */ OspfTypes::PeerID create_peer(const string& interface, const string& vif, A source, OspfTypes::LinkType linktype, OspfTypes::AreaID area) throw(BadPeer); /** * Delete a peer. */ bool delete_peer(const OspfTypes::PeerID); /** * Take a peer up or down. */ bool set_state_peer(const OspfTypes::PeerID, bool state); /** * Set the link status of the peer. */ bool set_link_status_peer(const OspfTypes::PeerID, bool state); /** * Add an address to this peer OSPFv3 only. */ bool add_address_peer(const string& interface, const string& vif, OspfTypes::AreaID area, A addr); /** * Remove an address from this peer OSPFv3 only. */ bool remove_address_peer(const OspfTypes::PeerID, OspfTypes::AreaID area, A addr); /** * Set the state of the address on this peer OSPFv3 only. */ bool set_address_state_peer(const OspfTypes::PeerID, OspfTypes::AreaID area, A addr, bool enable); /** * Activate this peer OSPFv3 only. * Called once after the peer is created and every time a new * address is added, but not when an address is deleted. */ bool activate_peer(const string& interface, const string& vif, OspfTypes::AreaID area); /** * Update this peer OSPFv3 only. * Called every time a variable related to this peer is changed. */ bool update_peer(const string& interface, const string& vif, OspfTypes::AreaID area); /** * Update this peer OSPFv3 only. * Reset the addresses that should be advertised by this peer. */ bool recompute_addresses_peer(const OspfTypes::PeerID, OspfTypes::AreaID area); /** * Track the state of a vif. * Callback when the status of the vif changes. */ void vif_status_change(const string& interface, const string& vif, bool state); /** * Track the state of an address. * Callback when the status of the address changes. */ void address_status_change(const string& interface, const string& vif, A source, bool state); /** * Add a neighbour to the peer. */ bool add_neighbour(const OspfTypes::PeerID, OspfTypes::AreaID area, A neighbour_address, OspfTypes::RouterID); /** * Remove a neighbour from the peer. */ bool remove_neighbour(const OspfTypes::PeerID, OspfTypes::AreaID area, A neighbour_address, OspfTypes::RouterID rid); /** * Transmit packets */ bool transmit(const string& interface, const string& vif, A dst, A src, uint8_t* data, uint32_t len); /** * Demultiplex incoming packets to the associated peer. If the * packet contains LSAs send it to the LSA database manager if * appropriate. * * @param interface that packet arrived on * @param vif that packet arrived on * @param packet * @return true if the packet is now owned by the peer manager. */ bool receive(const string& interface, const string& vif, A dst, A src, Packet *packet) throw(BadPeer); /** * Drop all adjacencies and hence clear database. */ bool clear_database(); /** * Queue an LSA for transmission. * * @param peerid to queue the LSA on. * @param peer the LSA arrived on. * @param nid the LSA arrived on. * @param lsar the lsa * @param multicast_on_peer will this LSA get multicast on this peer. * * @return true on success. */ bool queue_lsa(const OspfTypes::PeerID peerid, const OspfTypes::PeerID peer, OspfTypes::NeighbourID nid, Lsa::LsaRef lsar, bool &multicast_on_peer); /** * Send (push) any queued LSAs. */ bool push_lsas(const OspfTypes::PeerID peerid, const char* msg); /** * Get the interface ID of this peer OSPFv3 only. */ uint32_t get_interface_id(const OspfTypes::PeerID peerid); /** * Return a list of the fully adjacent routers. */ bool get_attached_routers(const OspfTypes::PeerID peerid, OspfTypes::AreaID area, list& routers); /* * Does this address fall into a configured OSPF network making it * a valid nexthop address. * * @param address under test * * @return true if this address falls into a configured OSPF network. */ bool configured_network(const A address) const; /* * Is this one of the routers interface addresses, used to try and * detect self-originated LSAs. * * @param address under test * * @return true if this a known interface address. */ bool known_interface_address(const A address) const; /** * Are any of the neighbours of this peer in the state exchange or * loading. * * @param peerid * @param area * * @return true if any of the neighbours are in state exchange or loading. */ bool neighbours_exchange_or_loading(const OspfTypes::PeerID peerid, OspfTypes::AreaID area); /** * Is the state of the neighbour with the specified Router ID at * least 2-Way. * * @param peerid * @param area * @param rid Router ID * @param twoway if the neighbour is found true means the * neighbour is at least twoway. * * @return true if the neighbour is found. */ bool neighbour_at_least_two_way(const OspfTypes::PeerID peerid, OspfTypes::AreaID area, OspfTypes::RouterID rid, bool& twoway); /** * Neighbour's source address. * * @param peerid * @param area * @param rid Router ID * @param interface_id Interface ID. * @param neighbour_address set if neighbour is found. * * @return true if the neighbour is found. */ bool get_neighbour_address(const OspfTypes::PeerID peerid, OspfTypes::AreaID area, OspfTypes::RouterID rid, uint32_t interface_id, A& neighbour_address); /** * Is this LSA on this neighbours link state request list. * @param peerid * @param area * @param nid * * @return true if it is. */ bool on_link_state_request_list(const OspfTypes::PeerID peerid, OspfTypes::AreaID area, const OspfTypes::NeighbourID nid, Lsa::LsaRef lsar); /** * Generate a BadLSReq event. * * @param peerid * @param area * @param nid * * @return true if it is. */ bool event_bad_link_state_request(const OspfTypes::PeerID peerid, OspfTypes::AreaID area, const OspfTypes::NeighbourID nid); /** * Send this LSA directly to the neighbour. Do not place on * retransmission list. * * @param peerid * @param area * @param nid * @param lsar * * @return true on success */ bool send_lsa(const OspfTypes::PeerID peerid, OspfTypes::AreaID area, const OspfTypes::NeighbourID nid, Lsa::LsaRef lsar); /** * Upcall from a peer to notify the peer manager that a full * adjacency has been achieved or lost. * * @param peerid * @param rid neighbours router ID. * @param up true if the adjacency has become full, false if a * full adjacency has been lost. */ void adjacency_changed(const OspfTypes::PeerID peerid, OspfTypes::RouterID rid, bool up); /** * Track border router transitions. * * @param up true of the router just became an area border router, * false if the router was an area border router and is no longer. */ void area_border_router_transition(bool up) const; /** * Send a new Router-LSA in all areas. * * Typically called when one of the Router-LSA flags changes state. */ void refresh_router_lsas() const; /** * Create a virtual link peer. */ bool create_virtual_peer(OspfTypes::RouterID rid); /** * Delete a virtual link peer. */ bool delete_virtual_peer(OspfTypes::RouterID rid); /** * Are any of neighbours of this area a virtual link endpoint. * * @return true if any are. */ bool virtual_link_endpoint(OspfTypes::AreaID area) const; /** * Create a virtual link (Configuration). * * @param rid neighbours router ID. */ bool create_virtual_link(OspfTypes::RouterID rid); /** * Attach this transit area to the neighbours router ID (Configuration). */ bool transit_area_virtual_link(OspfTypes::RouterID rid, OspfTypes::AreaID transit_area); /** * Delete a virtual link (Configuration). * * @param rid neighbours router ID. */ bool delete_virtual_link(OspfTypes::RouterID rid); /** * Bring virtual link up (Upcall from area router). * * @param rid neighbours router ID. * @param source address of packets sent to this neighbour. * @param interface_cost * @param destination address of the neighbour router. */ void up_virtual_link(OspfTypes::RouterID rid, A source, uint16_t interface_cost, A destination); /** * Take this virtual link down (Upcall from area router). */ void down_virtual_link(OspfTypes::RouterID rid); /** * A packet was sent to a peer that rejected it so this may be a * virtual link candidate. */ bool receive_virtual_link(A dst, A src, Packet *packet); /** * Return the number of areas of the specified type. */ uint32_t area_count(OspfTypes::AreaType area_type) const; /** * Is this an internal router? */ bool internal_router_p() const; /** * Is this an area border router? */ bool area_border_router_p() const; /** * Is this a backbone router? */ bool backbone_router_p() const; /** * Is this an AS boundary router? */ bool as_boundary_router_p() const; /** * Compute the options that are sent in hello packets, data * description packets, LSA headers (OSPFv2), Router-LSAs * (OSPFv3) and Network-LSAs (OSPFv3). * */ uint32_t compute_options(OspfTypes::AreaType area_type); // Config (begin) /** * The router ID is about to change. */ void router_id_changing(); #if 0 /** * Set options. */ bool set_options(const PeerID, OspfTypes::AreaID area, uint32_t options); #endif /** * Set the interface address of this peer. */ bool set_interface_address(const OspfTypes::PeerID, A address); /** * Set the hello interval in seconds. */ bool set_hello_interval(const OspfTypes::PeerID, OspfTypes::AreaID area, uint16_t hello_interval); /** * Set router priority. */ bool set_router_priority(const OspfTypes::PeerID, OspfTypes::AreaID area, uint8_t priority); /** * Set the router dead interval in seconds. */ bool set_router_dead_interval(const OspfTypes::PeerID, OspfTypes::AreaID area, uint32_t router_dead_interval); /** * Set interface cost */ bool set_interface_cost(const OspfTypes::PeerID, OspfTypes::AreaID area, uint16_t interface_cost); /** * Set RxmtInterval */ bool set_retransmit_interval(const OspfTypes::PeerID, OspfTypes::AreaID area, uint16_t retransmit_interval); /** * Set InfTransDelay */ bool set_inftransdelay(const OspfTypes::PeerID, OspfTypes::AreaID area, uint16_t inftransdelay); /** * Set a simple password authentication key. * * Note that the current authentication handler is replaced with * a simple password authentication handler. * * @param peerid the peer ID. * @param area the area ID. * @param password the password to set. * @param the error message (if error). * @return true on success, otherwise false. */ bool set_simple_authentication_key(const OspfTypes::PeerID peerid, OspfTypes::AreaID area, const string& password, string& error_msg); /** * Delete a simple password authentication key. * * Note that after the deletion the simple password authentication * handler is replaced with a Null authentication handler. * * @param peerid the peer ID. * @param area the area ID. * @param the error message (if error). * @return true on success, otherwise false. */ bool delete_simple_authentication_key(const OspfTypes::PeerID peerid, OspfTypes::AreaID area, string& error_msg); /** * Set an MD5 authentication key. * * Note that the current authentication handler is replaced with * an MD5 authentication handler. * * @param peerid the peer ID. * @param area the area ID. * @param key_id unique ID associated with key. * @param password phrase used for MD5 digest computation. * @param start_timeval start time when key becomes valid. * @param end_timeval end time when key becomes invalid. * @param max_time_drift the maximum time drift among all routers. * @param the error message (if error). * @return true on success, otherwise false. */ bool set_md5_authentication_key(const OspfTypes::PeerID peerid, OspfTypes::AreaID area, uint8_t key_id, const string& password, const TimeVal& start_timeval, const TimeVal& end_timeval, const TimeVal& max_time_drift, string& error_msg); /** * Delete an MD5 authentication key. * * Note that after the deletion if there are no more valid MD5 keys, * the MD5 authentication handler is replaced with a Null authentication * handler. * * @param peerid the peer ID. * @param area the area ID. * @param key_id the ID of the key to delete. * @param the error message (if error). * @return true on success, otherwise false. */ bool delete_md5_authentication_key(const OspfTypes::PeerID peerid, OspfTypes::AreaID area, uint8_t key_id, string& error_msg); /** * Toggle the passive status of an interface. */ bool set_passive(const OspfTypes::PeerID, OspfTypes::AreaID area, bool passive, bool host); /** * If this is a "stub" or "nssa" area toggle the sending of a default * route. */ bool originate_default_route(OspfTypes::AreaID area, bool enable); /** * Set the StubDefaultCost, the default cost sent in a default route in a * "stub" or "nssa" area. */ bool stub_default_cost(OspfTypes::AreaID area, uint32_t cost); /** * Toggle the sending of summaries into "stub" or "nssa" areas. */ bool summaries(OspfTypes::AreaID area, bool enable); // Config (end) /** * Number of areas this router serves. */ size_t number_of_areas() const { return _areas.size(); } /** * A new route has been added to the routing table announce it to * all areas as it is a candidate for Summary-LSA generation. * * @param area that the route was introduced by. */ void summary_announce(OspfTypes::AreaID area, IPNet net, RouteEntry& rt); /** * A route has been deleted from the routing table. It may * previously have caused a Summary-LSA which now needs to be * withdrawn. * * @param area that the route was introduced by. */ void summary_withdraw(OspfTypes::AreaID area, IPNet net, RouteEntry& rt); /** * A route has been replaced in the routing table. If a generated * Summary-LSA exists it will need to have its metric or nexthop * modified. * * @param area that the route was introduced by. */ void summary_replace(OspfTypes::AreaID area, IPNet net, RouteEntry& rt, RouteEntry& previous_rt, OspfTypes::AreaID previous_area); /** * Send all the summary information to specified area. New areas * or stub areas that change from do not advertise can use this * hook to force all routes to be sent to the specified area. * * @param area that all routes should be sent to. */ void summary_push(OspfTypes::AreaID area); /** * In the specified area is the net covered by an area range. * * @param area being checked. * @param net that may be covered. * @param advertise if the area is covered set to advertise or do * not advertise. * * @return true if the area is covered. */ bool area_range_covered(OspfTypes::AreaID area, IPNet net, bool& advertise); /** * Does the specified area have any area ranges configured. * * The primary purpose is to discover if the backbone area has any * area ranges configured, this is required when an area becomes a * transit area. */ bool area_range_configured(OspfTypes::AreaID area); /** * An AS-External-LSA has arrived from this area announce it to * all others. * * The LSAs should not be scheduled for transmission until the * external_accounce_complete() is seen. In many cases a number of * LSAs may arrive in a single packet, waiting for the * external_announce_complete() offers an opportunity for * aggregation. * */ bool external_announce(OspfTypes::AreaID area, Lsa::LsaRef lsar); /** * Create an AS-External-LSA and announce it to all appropriate * areas. */ bool external_announce(const IPNet& net, const A& nexthop, const uint32_t& metric, const PolicyTags& policytags); /** * An AS-External-LSA is being withdrawn from this area withdraw from * all others. */ bool external_withdraw(OspfTypes::AreaID area, Lsa::LsaRef lsar); /** * Withdraw a previously created and announced AS-External-LSA * from all areas. */ bool external_withdraw(const IPNet& net); /** * Called to complete a series of calls to external_announce(area). */ bool external_announce_complete(OspfTypes::AreaID area); /** * When a new area is configured it can use this method to * * @param area that all AS-External-LSAs should be sent to. */ void external_push(OspfTypes::AreaID area); /** * Re-run the policy filters on all routes. */ void external_push_routes(); /** * Examine self originated AS-external-LSAs that may need to be * suppressed because another router's AS-external-LSA takes * precedence. */ void external_suppress_lsas(OspfTypes::AreaID area); /** * Recompute routing calculation all areas BACKBONE first. */ void routing_recompute_all_areas(); /** * Recompute routing calculation for all transit areas. */ void routing_recompute_all_transit_areas(); private: Ospf& _ospf; // Reference to the controlling class. OspfTypes::PeerID _next_peerid; // Next PeerID to allocate. map _pmap;// Map from interface/vif to PeerID. map *> _peers; // All of our peers map *> _areas; // All the areas External _external; // Management of AS-External-LSAs. Vlink _vlink; // Management of virtual links uint32_t _normal_cnt; // Number of normal areas. uint32_t _stub_cnt; // Number of stub areas. uint32_t _nssa_cnt; // Number of nssa areas. /** * Generate PeerID. * Internally we want to deal with peers as simple IDs not * interface/vif. * Throw an exception a mapping already exists. */ OspfTypes::PeerID create_peerid(const string& interface, const string& vif) throw(BadPeer); /** * Get rid of this mapping. */ void destroy_peerid(const string& interface, const string& vif) throw(BadPeer); /** * @return true if this route is a candidate for summarisation. */ bool summary_candidate(OspfTypes::AreaID area, IPNet net, RouteEntry& rt); /** * Saved summaries that can be introduced into a new area. */ struct Summary { Summary() {} Summary(OspfTypes::AreaID area, RouteEntry& rt) : _area(area), _rtentry(rt) {} OspfTypes::AreaID _area; RouteEntry _rtentry; }; map, Summary> _summaries; /** * Track the number of areas of each type. * * @param area_type being tracked. * @param up true if the area is being created, false if it is * being deleted. */ void track_area_count(OspfTypes::AreaType area_type, bool up); }; #endif // __OSPF_PEER_MANAGER_HH__ xorp/ospf/xrl_target.cc0000664000076400007640000006533611540225531015324 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #include "ospf_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/status_codes.h" #include "libxorp/service.hh" #include "libxorp/eventloop.hh" #include "libxorp/ipv4.hh" #include "libxorp/ipv6.hh" #include "libxipc/xrl_std_router.hh" #include "ospf.hh" #include "xrl_io.hh" #include "xrl_target.hh" static int decode_time_string(EventLoop& eventloop, const string& time_string, TimeVal& timeval) { const char* s; const char* format = "%Y-%m-%d.%H:%M"; struct tm tm; time_t result; if (time_string.empty()) { timeval = TimeVal::ZERO(); return (XORP_OK); } // // Initialize the parsed result with the current time, because // strptime(3) would not set/modify the unspecified members of the // time format (e.g, the timezone and the summer time flag). // TimeVal now; eventloop.current_time(now); time_t local_time = now.sec(); const struct tm* local_tm = localtime(&local_time); memcpy(&tm, local_tm, sizeof(tm)); s = xorp_strptime(time_string.c_str(), format, &tm); if ((s == NULL) || (*s != '\0')) { return (XORP_ERROR); } result = mktime(&tm); if (result == -1) return (XORP_ERROR); timeval = TimeVal(result, 0); return (XORP_OK); } XrlOspfV2Target::XrlOspfV2Target(XrlRouter *r, Ospf& ospf, XrlIO& io) : XrlOspfv2TargetBase(r), _ospf(ospf), _xrl_io(io) { } XrlCmdError XrlOspfV2Target::common_0_1_get_target_name(string& name) { name = "ospfv2"; return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV2Target::common_0_1_get_version(string& version) { version = "0.1"; return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV2Target::common_0_1_get_status(uint32_t& status, string& reason) { status = _ospf.status(reason); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV2Target::common_0_1_shutdown() { _ospf.shutdown(); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV2Target::common_0_1_startup() { // Starts by default...nothing to do here. return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV2Target::raw_packet4_client_0_1_recv( // Input values, const string& if_name, const string& vif_name, const IPv4& src_address, const IPv4& dst_address, const uint32_t& ip_protocol, const int32_t& ip_ttl, const int32_t& ip_tos, const bool& ip_router_alert, const bool& ip_internet_control, const vector& payload) { _xrl_io.recv(if_name, vif_name, src_address, dst_address, ip_protocol, ip_ttl, ip_tos, ip_router_alert, ip_internet_control, payload); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV2Target::policy_backend_0_1_configure(const uint32_t& filter, const string& conf) { debug_msg("policy filter: %u conf: %s\n", filter, conf.c_str()); try { _ospf.configure_filter(filter,conf); } catch(const PolicyException& e) { return XrlCmdError::COMMAND_FAILED("Filter configure failed: " + e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV2Target::policy_backend_0_1_reset(const uint32_t& filter) { debug_msg("policy filter reset: %u\n", filter); try { _ospf.reset_filter(filter); } catch(const PolicyException& e){ return XrlCmdError::COMMAND_FAILED("Filter reset failed: " + e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV2Target::policy_backend_0_1_push_routes() { debug_msg("policy route push\n"); _ospf.push_routes(); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV2Target::policy_redist4_0_1_add_route4(const IPv4Net& network, const bool& unicast, const bool& multicast, const IPv4& nexthop, const uint32_t& metric, const XrlAtomList& policytags) { //XLOG_INFO("Net: %s Nexthop: %s Unicast: %s Multicast %s metric %d\n", // cstring(network), cstring(nexthop), bool_c_str(unicast), // bool_c_str(multicast), metric); UNUSED(unicast); UNUSED(multicast); #if 0 // Routes coming from fib2mrib have unicast set as false, even though // they are dealing with unicast addresses, so ignore that here. This // allows policy redistribution of routes coming from fib2mrib. if (!unicast) return XrlCmdError::OKAY(); #endif if (!_ospf.originate_route(network, nexthop, metric, policytags)) { return XrlCmdError::COMMAND_FAILED("Network: " + network.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV2Target::policy_redist4_0_1_delete_route4(const IPv4Net& network, const bool& unicast, const bool& multicast) { //XLOG_INFO("Net: %s Unicast: %s Multicast %s\n", // cstring(network), bool_c_str(unicast), bool_c_str(multicast)); UNUSED(unicast); UNUSED(multicast); #if 0 if (!unicast) return XrlCmdError::OKAY(); #endif if (!_ospf.withdraw_route(network)) { return XrlCmdError::COMMAND_FAILED("Network: " + network.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV2Target::ospfv2_0_1_set_router_id(const IPv4& id) { OspfTypes::RouterID rid = ntohl(id.addr()); _ospf.set_router_id(rid); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV2Target::ospfv2_0_1_set_rfc1583_compatibility(const bool& compatibility) { _ospf.set_rfc1583_compatibility(compatibility); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV2Target::ospfv2_0_1_set_ip_router_alert(const bool& ip_router_alert) { if (!_ospf.set_ip_router_alert(ip_router_alert)) return XrlCmdError::COMMAND_FAILED("Failed to set IP router alert"); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV2Target::ospfv2_0_1_create_area_router(const IPv4& a, const string& type) { bool status; OspfTypes::AreaType t = from_string_to_area_type(type, status); if (!status) return XrlCmdError::COMMAND_FAILED("Unrecognised type " + type); OspfTypes::AreaID area = ntohl(a.addr()); if (!_ospf.get_peer_manager().create_area_router(area, t)) return XrlCmdError::COMMAND_FAILED("Failed to create area " + pr_id(area)); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV2Target::ospfv2_0_1_change_area_router_type(const IPv4& a, const string& type) { bool status; OspfTypes::AreaType t = from_string_to_area_type(type, status); if (!status) return XrlCmdError::COMMAND_FAILED("Unrecognised type " + type); OspfTypes::AreaID area = ntohl(a.addr()); if (!_ospf.get_peer_manager().change_area_router_type(area, t)) return XrlCmdError::COMMAND_FAILED("Failed to create area " + pr_id(area)); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV2Target::ospfv2_0_1_destroy_area_router(const IPv4& a) { OspfTypes::AreaID area = ntohl(a.addr()); if (!_ospf.get_peer_manager().destroy_area_router(area)) return XrlCmdError::COMMAND_FAILED("Failed to destroy area " + pr_id(area)); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV2Target::ospfv2_0_1_create_peer(const string& ifname, const string& vifname, const IPv4& addr, const string& type, const IPv4& a) { OspfTypes::AreaID area = ntohl(a.addr()); bool status; OspfTypes::LinkType linktype = from_string_to_link_type(type, status); if (!status) return XrlCmdError::COMMAND_FAILED("Unrecognised type " + type); try { _ospf.get_peer_manager().create_peer(ifname, vifname, addr, linktype, area); } catch(XorpException& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV2Target::ospfv2_0_1_delete_peer(const string& ifname, const string& vifname) { debug_msg("interface %s vif %s\n", ifname.c_str(), vifname.c_str()); OspfTypes::PeerID peerid; try { peerid = _ospf.get_peer_manager().get_peerid(ifname, vifname); } catch(XorpException& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } if (!_ospf.get_peer_manager().delete_peer(peerid)) return XrlCmdError::COMMAND_FAILED("Failed to delete peer"); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV2Target::ospfv2_0_1_set_peer_state(const string& ifname, const string& vifname, const bool& enable) { debug_msg("interface %s vif %s enable %s\n", ifname.c_str(), vifname.c_str(), bool_c_str(enable)); OspfTypes::PeerID peerid; try { peerid = _ospf.get_peer_manager().get_peerid(ifname, vifname); } catch(XorpException& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } if (!_ospf.get_peer_manager().set_state_peer(peerid, enable)) return XrlCmdError::COMMAND_FAILED("Failed to set peer state"); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV2Target::ospfv2_0_1_add_neighbour(const string& ifname, const string& vifname, const IPv4& addr, const IPv4& neighbour_address, const IPv4& neighbour_id) { OspfTypes::AreaID area = ntohl(addr.addr()); OspfTypes::RouterID rid = ntohl(neighbour_id.addr()); debug_msg("interface %s vif %s area %s address %s id %s\n", ifname.c_str(), vifname.c_str(), pr_id(area).c_str(), cstring(neighbour_address),pr_id(rid).c_str()); OspfTypes::PeerID peerid; try { peerid = _ospf.get_peer_manager().get_peerid(ifname, vifname); } catch(XorpException& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } if (!_ospf.get_peer_manager().add_neighbour(peerid, area, neighbour_address, rid)) return XrlCmdError::COMMAND_FAILED("Failed to add neighbour " + neighbour_address.str()); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV2Target::ospfv2_0_1_remove_neighbour(const string& ifname, const string& vifname, const IPv4& addr, const IPv4& neighbour_address, const IPv4& neighbour_id) { OspfTypes::AreaID area = ntohl(addr.addr()); OspfTypes::RouterID rid = ntohl(neighbour_id.addr()); debug_msg("interface %s vif %s area %s address %s id %s\n", ifname.c_str(), vifname.c_str(), pr_id(area).c_str(), cstring(neighbour_address),pr_id(rid).c_str()); OspfTypes::PeerID peerid; try { peerid = _ospf.get_peer_manager().get_peerid(ifname, vifname); } catch(XorpException& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } if (!_ospf.get_peer_manager().remove_neighbour(peerid, area, neighbour_address, rid)) return XrlCmdError::COMMAND_FAILED("Failed to remove neighbour" + neighbour_address.str()); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV2Target::ospfv2_0_1_create_virtual_link(const IPv4& neighbour_id, const IPv4& a) { OspfTypes::AreaID area = ntohl(a.addr()); OspfTypes::RouterID rid = ntohl(neighbour_id.addr()); debug_msg("Neighbour's router ID %s configuration area %s\n", pr_id(rid).c_str(), pr_id(area).c_str()); if (OspfTypes::BACKBONE != area) { return XrlCmdError:: COMMAND_FAILED(c_format("Virtual link must be in area %s", pr_id(OspfTypes::BACKBONE).c_str())); } if (!_ospf.create_virtual_link(rid)) return XrlCmdError::COMMAND_FAILED("Failed to create virtual link"); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV2Target::ospfv2_0_1_delete_virtual_link(const IPv4& neighbour_id) { OspfTypes::RouterID rid = ntohl(neighbour_id.addr()); if (!_ospf.delete_virtual_link(rid)) return XrlCmdError::COMMAND_FAILED("Failed to delete virtual link"); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV2Target::ospfv2_0_1_transit_area_virtual_link(const IPv4& neighbour_id, const IPv4& transit_area) { OspfTypes::RouterID rid = ntohl(neighbour_id.addr()); OspfTypes::AreaID area = ntohl(transit_area.addr()); if (!_ospf.transit_area_virtual_link(rid, area)) return XrlCmdError::COMMAND_FAILED("Failed to configure transit area"); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV2Target::ospfv2_0_1_set_interface_cost(const string& ifname, const string& vifname, const IPv4& a, const uint32_t& cost) { OspfTypes::AreaID area = ntohl(a.addr()); debug_msg("interface %s vif %s area %s cost %d\n", ifname.c_str(), vifname.c_str(), pr_id(area).c_str(), cost); if (!_ospf.set_interface_cost(ifname, vifname, area, cost)) return XrlCmdError::COMMAND_FAILED("Failed to set " "interface cost"); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV2Target::ospfv2_0_1_set_retransmit_interval(const string& ifname, const string& vifname, const IPv4& a, const uint32_t& interval) { OspfTypes::AreaID area = ntohl(a.addr()); debug_msg("interface %s vif %s area %s interval %d\n", ifname.c_str(), vifname.c_str(), pr_id(area).c_str(), interval); if (!_ospf.set_retransmit_interval(ifname, vifname, area, interval)) return XrlCmdError::COMMAND_FAILED("Failed to set " "RxmtInterval interval"); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV2Target::ospfv2_0_1_set_inftransdelay(const string& ifname, const string& vifname, const IPv4& a, const uint32_t& delay) { OspfTypes::AreaID area = ntohl(a.addr()); debug_msg("interface %s vif %s area %s delay %d\n", ifname.c_str(), vifname.c_str(), pr_id(area).c_str(), delay); if (!_ospf.set_inftransdelay(ifname, vifname, area, delay)) return XrlCmdError::COMMAND_FAILED("Failed to set " "inftransdelay delay"); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV2Target::ospfv2_0_1_set_router_priority(const string& ifname, const string& vifname, const IPv4& a, const uint32_t& priority) { OspfTypes::AreaID area = ntohl(a.addr()); debug_msg("interface %s vif %s area %s priority %d\n", ifname.c_str(), vifname.c_str(), pr_id(area).c_str(), priority); if (!_ospf.set_router_priority(ifname, vifname, area, priority)) return XrlCmdError::COMMAND_FAILED("Failed to set priority"); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV2Target::ospfv2_0_1_set_hello_interval(const string& ifname, const string& vifname, const IPv4& a, const uint32_t& interval) { OspfTypes::AreaID area = ntohl(a.addr()); debug_msg("interface %s vif %s area %s interval %d\n", ifname.c_str(), vifname.c_str(), pr_id(area).c_str(), interval); if (!_ospf.set_hello_interval(ifname, vifname, area, interval)) return XrlCmdError::COMMAND_FAILED("Failed to set hello interval"); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV2Target::ospfv2_0_1_set_router_dead_interval(const string& ifname, const string& vifname, const IPv4& a, const uint32_t& interval) { OspfTypes::AreaID area = ntohl(a.addr()); debug_msg("interface %s vif %s area %s interval %d\n", ifname.c_str(), vifname.c_str(), pr_id(area).c_str(), interval); if (!_ospf.set_router_dead_interval(ifname, vifname, area, interval)) return XrlCmdError::COMMAND_FAILED("Failed to set " "router dead interval"); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV2Target::ospfv2_0_1_set_simple_authentication_key( // Input values, const string& ifname, const string& vifname, const IPv4& area, const string& password) { string error_msg; OspfTypes::AreaID area_id = ntohl(area.addr()); debug_msg("set_simple_authentication_key(): " "interface %s vif %s area %s password %s\n", ifname.c_str(), vifname.c_str(), pr_id(area_id).c_str(), password.c_str()); if (!_ospf.set_simple_authentication_key(ifname, vifname, area_id, password, error_msg)) { error_msg = c_format("Failed to set simple authentication key: %s", error_msg.c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV2Target::ospfv2_0_1_delete_simple_authentication_key( // Input values, const string& ifname, const string& vifname, const IPv4& area) { string error_msg; OspfTypes::AreaID area_id = ntohl(area.addr()); debug_msg("delete_simple_authentication_key(): " "interface %s vif %s area %s\n", ifname.c_str(), vifname.c_str(), pr_id(area_id).c_str()); if (!_ospf.delete_simple_authentication_key(ifname, vifname, area_id, error_msg)) { error_msg = c_format("Failed to delete simple authentication key: %s", error_msg.c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV2Target::ospfv2_0_1_set_md5_authentication_key( // Input values, const string& ifname, const string& vifname, const IPv4& area, const uint32_t& key_id, const string& password, const string& start_time, const string& end_time, const uint32_t& max_time_drift) { string error_msg; TimeVal start_timeval = TimeVal::ZERO(); TimeVal end_timeval = TimeVal::MAXIMUM(); TimeVal max_time_drift_timeval = TimeVal::ZERO(); OspfTypes::AreaID area_id = ntohl(area.addr()); debug_msg("set_md5_authentication_key(): " "interface %s vif %s area %s key_id %u password %s " "start_time %s end_time %s max_time_drift %u\n", ifname.c_str(), vifname.c_str(), pr_id(area_id).c_str(), key_id, password.c_str(), start_time.c_str(), end_time.c_str(), max_time_drift); // // Check the key ID // if (key_id > 255) { error_msg = c_format("Invalid key ID %u (valid range is [0, 255])", XORP_UINT_CAST(key_id)); return XrlCmdError::COMMAND_FAILED(error_msg); } // // Decode the start and end time // if (! start_time.empty()) { if (decode_time_string(_ospf.get_eventloop(), start_time, start_timeval) != XORP_OK) { error_msg = c_format("Invalid start time: %s", start_time.c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } } if (! end_time.empty()) { if (decode_time_string(_ospf.get_eventloop(), end_time, end_timeval) != XORP_OK) { error_msg = c_format("Invalid end time: %s", end_time.c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } } // XXX: Allowed time drift seconds are [0--65534] and 65535 for infinity if (max_time_drift > 65535) { error_msg = c_format("Invalid maximum time drift seconds: %u " "(allowed range is [0--65535])", max_time_drift); } if (max_time_drift <= 65534) max_time_drift_timeval = TimeVal(max_time_drift, 0); else max_time_drift_timeval = TimeVal::MAXIMUM(); if (!_ospf.set_md5_authentication_key(ifname, vifname, area_id, key_id, password, start_timeval, end_timeval, max_time_drift_timeval, error_msg)) { error_msg = c_format("Failed to set MD5 authentication key: %s", error_msg.c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV2Target::ospfv2_0_1_delete_md5_authentication_key( // Input values, const string& ifname, const string& vifname, const IPv4& area, const uint32_t& key_id) { string error_msg; OspfTypes::AreaID area_id = ntohl(area.addr()); debug_msg("delete_md5_authentication_key(): " "interface %s vif %s area %s key_id %u\n", ifname.c_str(), vifname.c_str(), pr_id(area_id).c_str(), key_id); // // Check the key ID // if (key_id > 255) { error_msg = c_format("Invalid key ID %u (valid range is [0, 255])", XORP_UINT_CAST(key_id)); return XrlCmdError::COMMAND_FAILED(error_msg); } if (!_ospf.delete_md5_authentication_key(ifname, vifname, area_id, key_id, error_msg)) { error_msg = c_format("Failed to delete MD5 authentication key: %s", error_msg.c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV2Target::ospfv2_0_1_set_passive(const string& ifname, const string& vifname, const IPv4& a, const bool& passive, const bool& host) { OspfTypes::AreaID area = ntohl(a.addr()); debug_msg("interface %s vif %s area %s passive %s host %s\n", ifname.c_str(), vifname.c_str(), pr_id(area).c_str(), bool_c_str(passive), bool_c_str(host)); if (!_ospf.set_passive(ifname, vifname, area, passive, host)) return XrlCmdError::COMMAND_FAILED("Failed to configure make passive"); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV2Target::ospfv2_0_1_originate_default_route(const IPv4& a, const bool& enable) { OspfTypes::AreaID area = ntohl(a.addr()); debug_msg("area %s enable %s\n", pr_id(area).c_str(), bool_c_str(enable)); if (!_ospf.originate_default_route(area, enable)) return XrlCmdError:: COMMAND_FAILED("Failed to configure default route"); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV2Target::ospfv2_0_1_stub_default_cost(const IPv4& a, const uint32_t& cost) { OspfTypes::AreaID area = ntohl(a.addr()); debug_msg("area %s cost %u\n", pr_id(area).c_str(), cost); if (!_ospf.stub_default_cost(area, cost)) return XrlCmdError:: COMMAND_FAILED("Failed set StubDefaultCost"); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV2Target::ospfv2_0_1_summaries(const IPv4& a, const bool& enable) { OspfTypes::AreaID area = ntohl(a.addr()); debug_msg("area %s enable %s\n", pr_id(area).c_str(), bool_c_str(enable)); if (!_ospf.summaries(area, enable)) return XrlCmdError:: COMMAND_FAILED("Failed to configure summaries"); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV2Target::ospfv2_0_1_area_range_add(const IPv4& a, const IPv4Net& net, const bool& advertise) { OspfTypes::AreaID area = ntohl(a.addr()); debug_msg("area %s net %s advertise %s\n", pr_id(area).c_str(), cstring(net), bool_c_str(advertise)); if (!_ospf.area_range_add(area, net, advertise)) return XrlCmdError:: COMMAND_FAILED(c_format("Failed to add area range " "area %s net %s advertise %s\n", pr_id(area).c_str(), cstring(net), bool_c_str(advertise))); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV2Target::ospfv2_0_1_area_range_delete(const IPv4& a, const IPv4Net& net) { OspfTypes::AreaID area = ntohl(a.addr()); debug_msg("area %s net %s\n", pr_id(area).c_str(), cstring(net)); if (!_ospf.area_range_delete(area, net)) return XrlCmdError:: COMMAND_FAILED(c_format("Failed to delete area range " "area %s net %s\n", pr_id(area).c_str(), cstring(net))); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV2Target::ospfv2_0_1_area_range_change_state(const IPv4& a, const IPv4Net& net, const bool& advertise) { OspfTypes::AreaID area = ntohl(a.addr()); debug_msg("area %s net %s advertise %s\n", pr_id(area).c_str(), cstring(net), bool_c_str(advertise)); if (!_ospf.area_range_change_state(area, net, advertise)) return XrlCmdError:: COMMAND_FAILED(c_format("Failed to change area range " "area %s net %s advertise %s\n", pr_id(area).c_str(), cstring(net), bool_c_str(advertise))); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV2Target::ospfv2_0_1_trace(const string& tvar, const bool& enable) { debug_msg("trace variable %s enable %s\n", tvar.c_str(), bool_c_str(enable)); if (tvar == "all") { _ospf.trace().all(enable); } else { return XrlCmdError:: COMMAND_FAILED(c_format("Unknown variable %s", tvar.c_str())); } return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV2Target::XrlOspfV2Target::ospfv2_0_1_get_lsa(const IPv4& a, const uint32_t& index, bool& valid, bool& toohigh, bool& self, vector& lsa) { OspfTypes::AreaID area = ntohl(a.addr()); debug_msg("area %s index %u\n", pr_id(area).c_str(), index); if (!_ospf.get_lsa(area, index, valid, toohigh, self, lsa)) return XrlCmdError::COMMAND_FAILED("Unable to get LSA"); debug_msg("area %s index %u valid %s toohigh %s self %s\n", pr_id(area).c_str(), index, bool_c_str(valid), bool_c_str(toohigh), bool_c_str(self)); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV2Target::ospfv2_0_1_get_area_list(XrlAtomList& areas) { list arealist; if (!_ospf.get_area_list(arealist)) return XrlCmdError::COMMAND_FAILED("Unable to get area list"); list::const_iterator i; for (i = arealist.begin(); i != arealist.end(); i++) areas.append(XrlAtom(*i)); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV2Target::ospfv2_0_1_get_neighbour_list(XrlAtomList& neighbours) { list neighbourlist; if (!_ospf.get_neighbour_list(neighbourlist)) return XrlCmdError::COMMAND_FAILED("Unable to get neighbour list"); list::const_iterator i; for (i = neighbourlist.begin(); i != neighbourlist.end(); i++) neighbours.append(XrlAtom(*i)); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV2Target::ospfv2_0_1_get_neighbour_info(const uint32_t& nid, string& address, string& interface, string& state, IPv4& rid, uint32_t& priority, uint32_t& deadtime, IPv4& area, uint32_t& opt, IPv4& dr, IPv4& bdr, uint32_t& up, uint32_t& adjacent) { NeighbourInfo ninfo; if (!_ospf.get_neighbour_info(nid, ninfo)) return XrlCmdError::COMMAND_FAILED("Unable to get neighbour info"); #define copy_ninfo(var) var = ninfo._ ## var copy_ninfo(address); copy_ninfo(interface); copy_ninfo(state); copy_ninfo(rid); copy_ninfo(priority); copy_ninfo(deadtime); copy_ninfo(area); copy_ninfo(opt); copy_ninfo(dr); copy_ninfo(bdr); copy_ninfo(up); copy_ninfo(adjacent); #undef copy_ninfo return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV2Target::ospfv2_0_1_clear_database() { if (!_ospf.clear_database()) return XrlCmdError::COMMAND_FAILED("Unable clear database"); return XrlCmdError::OKAY(); } xorp/ospf/vertex.hh0000664000076400007640000001430311421137511014462 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/ospf/vertex.hh,v 1.18 2008/10/02 21:57:50 bms Exp $ #ifndef __OSPF_VERTEX_HH__ #define __OSPF_VERTEX_HH__ /** * Vertex required for computing the shortest path tree. */ class Vertex { public: Vertex() : _origin(false), _nexthop_id(OspfTypes::UNUSED_INTERFACE_ID) {} bool operator<(const Vertex& other) const { XLOG_ASSERT(get_version() == other.get_version()); switch(_version) { case OspfTypes::V2: if (_nodeid == other.get_nodeid()) return _t < other.get_type(); break; case OspfTypes::V3: if (_nodeid == other.get_nodeid() && _t != other.get_type()) return _t < other.get_type(); switch(_t) { case OspfTypes::Router: break; case OspfTypes::Network: if (_nodeid == other.get_nodeid()) return _interface_id < other.get_interface_id(); break; } break; } return _nodeid < other.get_nodeid(); } bool operator==(const Vertex& other) const { XLOG_ASSERT(get_version() == other.get_version()); return _nodeid == other.get_nodeid() && _t == other.get_type(); } void set_version(OspfTypes::Version v) { _version = v; } OspfTypes::Version get_version() const { return _version; } void set_type(OspfTypes::VertexType t) { _t = t; } OspfTypes::VertexType get_type() const { return _t; } void set_nodeid(uint32_t nodeid) { _nodeid = nodeid; } uint32_t get_nodeid() const { return _nodeid; } /** * OSPFv2 only. * Set the LSA that is responsible for this vertex. */ void set_lsa(Lsa::LsaRef lsar) { XLOG_ASSERT(OspfTypes::V2 == get_version()); XLOG_ASSERT(0 == _lsars.size()); _lsars.push_back(lsar); } /** * OSPFv2 only. * Get the LSA that is responsible for this vertex. */ Lsa::LsaRef get_lsa() const { XLOG_ASSERT(OspfTypes::V2 == get_version()); XLOG_ASSERT(1 == _lsars.size()); return *(_lsars.begin()); } /** * OSPFv3 only. * Return the list of LSAs that may be responsible for this vertex. */ list& get_lsas() { XLOG_ASSERT(OspfTypes::V3 == get_version()); return _lsars; } void set_interface_id(uint32_t interface_id) { XLOG_ASSERT(OspfTypes::V3 == get_version()); _interface_id = interface_id; } uint32_t get_interface_id() const { XLOG_ASSERT(OspfTypes::V3 == get_version()); return _interface_id; } void set_origin(bool origin) { _origin = origin; } bool get_origin() const { return _origin; } void set_address(IPv4 address) { XLOG_ASSERT(OspfTypes::V2 == get_version()); _address_ipv4 = address; } IPv4 get_address_ipv4() const { XLOG_ASSERT(OspfTypes::V2 == get_version()); return _address_ipv4; } void set_address(IPv6 address) { XLOG_ASSERT(OspfTypes::V3 == get_version()); _address_ipv6 = address; } IPv6 get_address_ipv6() const { XLOG_ASSERT(OspfTypes::V3 == get_version()); return _address_ipv6; } void set_nexthop_id(uint32_t nexthop_id) { XLOG_ASSERT(OspfTypes::V3 == get_version()); _nexthop_id = nexthop_id; } uint32_t get_nexthop_id() const { XLOG_ASSERT(OspfTypes::V3 == get_version()); return _nexthop_id; } string str() const { string output; switch(_version) { case OspfTypes::V2: output = "OSPFv2"; if (_origin) output += "(Origin)"; switch(_t) { case OspfTypes::Router: output += " Router"; break; case OspfTypes::Network: output += " Network"; break; } output += c_format(" %s(%#x) %s(%#x)", pr_id(_nodeid).c_str(), _nodeid, cstring(_address_ipv4), _address_ipv4.addr()); break; case OspfTypes::V3: output = "OSPFv3"; if (_origin) output += "(Origin)"; switch(_t) { case OspfTypes::Router: output += c_format(" Router %s(%#x)", pr_id(_nodeid).c_str(), _nodeid); break; case OspfTypes::Network: output += c_format(" Transit %s(%#x) %u", pr_id(_nodeid).c_str(), _nodeid, _interface_id); break; } output += c_format(" %s", cstring(_address_ipv6)); break; } return output; } private: /*const */OspfTypes::Version _version; OspfTypes::VertexType _t; // Router or Network (Transit in OSPFv3) uint32_t _nodeid; uint32_t _interface_id; // OSPFv3 Only bool _origin; // Is this the vertex of the router. // The address of the Vertex that should be used as the nexthop by // the origin. IPv4 _address_ipv4; IPv6 _address_ipv6; // If this vertex is directly connected to the origin this is the // interface ID that should be used with the nexthop. uint32_t _nexthop_id; list _lsars; // RFC 2328 Section 16.1. Calculating the shortest-path tree for an area: // Vertex (node) ID // A 32-bit number which together with the vertex type (router // or network) uniquely identifies the vertex. For router // vertices the Vertex ID is the router's OSPF Router ID. For // network vertices, it is the IP address of the network's // Designated Router. // RFC 2470 Section 3.8.1.Calculating the shortest path tree for an area: // o The Vertex ID for a router is the OSPF Router ID. The Vertex ID // for a transit network is a combination of the Interface ID and // OSPF Router ID of the network's Designated Router. }; #endif // __OSPF_VERTEX_HH__ xorp/ospf/lsa.cc0000664000076400007640000014605211540224231013717 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #include "ospf_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/callback.hh" #include "libxorp/ipv4.hh" #include "libxorp/ipv6.hh" #include "libxorp/ipnet.hh" #include "libxorp/status_codes.h" #include "libxorp/service.hh" #include "libxorp/eventloop.hh" #include "libproto/packet.hh" #include "ospf.hh" #include "lsa.hh" #include "fletcher_checksum.hh" /** * Verify the checksum of an LSA. * * The cool part of this checksum algorithm is that it is not necessry * to compare the computed checksum against the one in the packet; as * the computed value should always be zero. */ inline bool verify_checksum(uint8_t *buf, size_t len, size_t offset) { int32_t x, y; fletcher_checksum(buf, len, offset, x, y); if (!(255 == x && 255 == y)) { return false; } return true; } /** * Compute the checksum. */ inline uint16_t compute_checksum(uint8_t *buf, size_t len, size_t offset) { int32_t x, y; fletcher_checksum(buf, len, offset, x, y); return (x << 8) | (y); } /** * Get the length of this LSA and verify that the length is smaller * than the buffer and large enough to be a valid LSA. Otherwise throw * an exception. Don't modify the value if its greater than the * buffer. */ inline size_t get_lsa_len_from_header(const char *caller, uint8_t *buf, size_t len, size_t min_len) throw(InvalidPacket) { size_t tlen = Lsa_header::get_lsa_len_from_buffer(buf); if (tlen > len) { xorp_throw(InvalidPacket, c_format("%s header len %u larger than buffer %u", caller, XORP_UINT_CAST(tlen), XORP_UINT_CAST(len))); } else if(tlen < min_len) { xorp_throw(InvalidPacket, c_format("%s header len %u smaller than minimum LSA " "of this type %u", caller, XORP_UINT_CAST(tlen), XORP_UINT_CAST(min_len))); } else { len = tlen; } return len; } uint16_t Lsa_header::get_lsa_len_from_buffer(uint8_t *ptr) { return extract_16(&ptr[18]); } void Lsa_header::decode(Lsa_header& header, uint8_t *ptr) const throw(InvalidPacket) { OspfTypes::Version version = get_version(); // Lsa_header header(version); header.set_ls_age(extract_16(&ptr[0])); switch(version) { case OspfTypes::V2: header.set_options(ptr[2]); header.set_ls_type(ptr[3]); break; case OspfTypes::V3: header.set_ls_type(extract_16(&ptr[2])); break; } header.set_link_state_id(extract_32(&ptr[4])); header.set_advertising_router(extract_32(&ptr[8])); header.set_ls_sequence_number(extract_32(&ptr[12])); header.set_ls_checksum(extract_16(&ptr[16])); header.set_length(get_lsa_len_from_buffer(&ptr[0])); // return header; } /** * A LSA header is a fixed length, the caller should have allocated * enough space by calling the length() method. */ Lsa_header Lsa_header::decode(uint8_t *ptr) const throw(InvalidPacket) { Lsa_header header(get_version()); decode(header, ptr); return header; } void Lsa_header::decode_inline(uint8_t *ptr) throw(InvalidPacket) { decode(*this, ptr); } /** * A LSA header is a fixed length, the caller should have allocated * enough space by calling the length() method. */ size_t Lsa_header::copy_out(uint8_t *ptr) const { OspfTypes::Version version = get_version(); embed_16(&ptr[0], get_ls_age()); switch(version) { case OspfTypes::V2: ptr[2] = get_options(); ptr[3] = get_ls_type(); break; case OspfTypes::V3: embed_16(&ptr[2], get_ls_type()); break; } embed_32(&ptr[4], get_link_state_id()); embed_32(&ptr[8], get_advertising_router()); embed_32(&ptr[12], get_ls_sequence_number()); embed_16(&ptr[16], get_ls_checksum()); embed_16(&ptr[18], get_length()); return 20; } string Lsa_header::str() const { string output; output = c_format("LS age %4u", get_ls_age()); switch(get_version()) { case OspfTypes::V2: output += c_format(" Options %#4x %s", get_options(), cstring(Options(get_version(), get_options()))); break; case OspfTypes::V3: break; } output += c_format(" LS type %#x", get_ls_type()); output += c_format(" Link State ID %s", pr_id(get_link_state_id()).c_str()); output += c_format(" Advertising Router %s", pr_id(get_advertising_router()).c_str()); output += c_format(" LS sequence number %#x", get_ls_sequence_number()); output += c_format(" LS checksum %#x", get_ls_checksum()); output += c_format(" length %u", get_length()); return output; } inline uint16_t add_age(uint16_t current, uint16_t delta) { uint16_t age = current + delta; // The largest acceptable age for an LSA is MaxAge. return age < OspfTypes::MaxAge ? age : OspfTypes::MaxAge; } void Lsa::revive(const TimeVal& now) { Lsa_header& h = get_header(); XLOG_ASSERT(get_self_originating()); XLOG_ASSERT(h.get_ls_age() == OspfTypes::MaxAge); XLOG_ASSERT(h.get_ls_sequence_number() == OspfTypes::MaxSequenceNumber); set_transmitted(false); h.set_ls_sequence_number(OspfTypes::InitialSequenceNumber); get_header().set_ls_age(0); record_creation_time(now); encode(); } void Lsa::update_age_and_seqno(const TimeVal& now) { XLOG_ASSERT(get_self_originating()); // XLOG_ASSERT(get_header().get_ls_age() != OspfTypes::MaxAge); // If this LSA has been transmitted then its okay to bump the // sequence number. if (get_transmitted()) { set_transmitted(false); increment_sequence_number(); } get_header().set_ls_age(0); record_creation_time(now); encode(); } void Lsa::update_age(TimeVal now) { // Compute the new age value based on the current time. TimeVal tdiff = now - _creation; uint16_t age = add_age(_initial_age, tdiff.sec()); set_ls_age(age); } void Lsa::update_age_inftransdelay(uint8_t *ptr, uint16_t inftransdelay) { uint16_t age; age = extract_16(ptr); debug_msg("Current age %u\n", age); age = add_age(age, inftransdelay); debug_msg("Age with InfTransDelay added %u\n", age); embed_16(&ptr[0], age); } void Lsa::set_maxage() { set_ls_age(OspfTypes::MaxAge); } bool Lsa::maxage() const { return OspfTypes::MaxAge == _header.get_ls_age(); } bool Lsa::max_sequence_number() const { return OspfTypes::MaxSequenceNumber == _header.get_ls_sequence_number(); } void Lsa::set_ls_age(uint16_t age) { XLOG_ASSERT(age <= OspfTypes::MaxAge); if (OspfTypes::MaxAge == _header.get_ls_age()) XLOG_FATAL("Age already MaxAge(%d) being set to %d\n%s", OspfTypes::MaxAge, age, str().c_str()); // Update the stored age value. _header.set_ls_age(age); // If a stored packet exists update it as well. The age field is // not covered by the checksum so this is safe. if (_pkt.size() < sizeof(uint16_t)) return; // Update the age in the stored LSA itself. uint8_t *ptr = &_pkt[0]; embed_16(&ptr[0], _header.get_ls_age()); } /** * A link state request is a fixed length, the caller should have allocated * enough space by calling the length() method. */ Ls_request Ls_request::decode(uint8_t *ptr) throw(InvalidPacket) { OspfTypes::Version version = get_version(); Ls_request header(version); switch(version) { case OspfTypes::V2: header.set_ls_type(extract_32(&ptr[0])); break; case OspfTypes::V3: header.set_ls_type(extract_16(&ptr[2])); break; } header.set_link_state_id(extract_32(&ptr[4])); header.set_advertising_router(extract_32(&ptr[8])); return header; } /** * A link state request is a fixed length, the caller should have allocated * enough space by calling the length() method. */ size_t Ls_request::copy_out(uint8_t *ptr) const { OspfTypes::Version version = get_version(); switch(version) { case OspfTypes::V2: embed_32(&ptr[0], get_ls_type()); break; case OspfTypes::V3: embed_16(&ptr[2], get_ls_type()); break; } embed_32(&ptr[4], get_link_state_id()); embed_32(&ptr[8], get_advertising_router()); return 20; } string Ls_request::str() const { string output; output = c_format(" LS type %#x", get_ls_type()); output += c_format(" Link State ID %s", pr_id(get_link_state_id()).c_str()); output += c_format(" Advertising Router %s", pr_id(get_advertising_router()).c_str()); return output; } /* LsaDecoder */ LsaDecoder::~LsaDecoder() { // Free all the stored decoder packets. map::iterator i; for(i = _lsa_decoders.begin(); i != _lsa_decoders.end(); i++) delete i->second; delete _unknown_lsa_decoder; } void LsaDecoder::register_decoder(Lsa *lsa) { // Don't allow a registration to be overwritten. XLOG_ASSERT(_lsa_decoders.find(lsa->get_ls_type()) == _lsa_decoders.end()); _lsa_decoders[lsa->get_ls_type()] = lsa; // Keep a record of the smallest LSA that may be decoded. // This will be useful as sanity check in the packet decoder. if (0 == _min_lsa_length) _min_lsa_length = lsa->min_length(); else if (_min_lsa_length > lsa->min_length()) _min_lsa_length = lsa->min_length(); } void LsaDecoder::register_unknown_decoder(Lsa *lsa) { switch(get_version()) { case OspfTypes::V2: XLOG_FATAL("OSPFv2 does not have an Unknown-LSA decoder"); break; case OspfTypes::V3: break; } // Don't allow a registration to be overwritten. XLOG_ASSERT(0 == _unknown_lsa_decoder); _unknown_lsa_decoder = lsa; } Lsa::LsaRef LsaDecoder::decode(uint8_t *ptr, size_t& len) const throw(InvalidPacket) { OspfTypes::Version version = get_version(); Lsa_header header(version); if (len < header.length()) xorp_throw(InvalidPacket, c_format("LSA too short %u, must be at least %u", XORP_UINT_CAST(len), XORP_UINT_CAST(header.length()))); // XXX // The LSA header is going to be decoder here and again in the // actual LSA code. Could consider passing in the already decoded header. header.decode_inline(ptr); map::const_iterator i; uint16_t type = header.get_ls_type(); i = _lsa_decoders.find(type); if (i == _lsa_decoders.end()) { if (0 != _unknown_lsa_decoder) return _unknown_lsa_decoder->decode(ptr, len); xorp_throw(InvalidPacket, c_format("OSPF Version %u Unknown LSA Type %#x", version, type)); } Lsa *lsa = i->second; return lsa->decode(ptr, len); } /* IPv6Prefix */ size_t IPv6Prefix::length() const { XLOG_ASSERT(OspfTypes::V3 == get_version()); return bytes_per_prefix(get_network().prefix_len()); } IPv6Prefix IPv6Prefix::decode(uint8_t *ptr, size_t& len, uint8_t prefixlen, uint8_t option) const throw(InvalidPacket) { OspfTypes::Version version = get_version(); XLOG_ASSERT(OspfTypes::V3 == version); IPv6Prefix prefix(version, use_metric()); prefix.set_prefix_options(option); uint8_t addr[IPv6::ADDR_BYTELEN]; uint32_t bytes = bytes_per_prefix(prefixlen); if (bytes > sizeof(addr)) xorp_throw(InvalidPacket, c_format("Prefix length %u larger than %u", bytes, XORP_UINT_CAST(sizeof(addr)))); if (bytes > len) xorp_throw(InvalidPacket, c_format("Prefix length %u larger than packet %u", bytes, XORP_UINT_CAST(len))); memset(&addr[0], 0, IPv6::ADDR_BYTELEN); memcpy(&addr[0], ptr, bytes); IPv6 v6; v6.set_addr(&addr[0]); IPNet v6net(v6, prefixlen); prefix.set_network(v6net); len = bytes; return prefix; } /** * The caller should have called length() to pre-allocate the space * required. */ size_t IPv6Prefix::copy_out(uint8_t *ptr) const { XLOG_ASSERT(OspfTypes::V3 == get_version()); IPv6 v6 = get_network().masked_addr(); uint8_t buf[IPv6::ADDR_BYTELEN]; v6.copy_out(&buf[0]); size_t bytes = bytes_per_prefix(get_network().prefix_len()); memcpy(ptr, &buf[0], bytes); return bytes; } string IPv6Prefix::str() const { XLOG_ASSERT(OspfTypes::V3 == get_version()); string output; output = c_format("Options %#4x", get_prefix_options()); output += c_format(" DN-bit: %d", get_dn_bit()); output += c_format(" P-bit: %d", get_p_bit()); output += c_format(" MC-bit: %d", get_mc_bit()); output += c_format(" LA-bit: %d", get_la_bit()); output += c_format(" NU-bit: %d", get_nu_bit()); if (use_metric()) output += c_format(" Metric %u", get_metric()); output += c_format(" Address %s", cstring(get_network())); return output; } /* RouterLink */ size_t RouterLink::length() const { switch (get_version()) { case OspfTypes::V2: return 12; break; case OspfTypes::V3: return 16; break; } XLOG_UNREACHABLE(); return 0; } RouterLink RouterLink::decode(uint8_t *ptr, size_t& len) const throw(InvalidPacket) { if (len < length()) xorp_throw(InvalidPacket, c_format("RouterLink too short %u, must be at least %u", XORP_UINT_CAST(len), XORP_UINT_CAST(length()))); OspfTypes::Version version = get_version(); RouterLink link(version); uint8_t type; uint8_t tos_number = 0; switch (version) { case OspfTypes::V2: link.set_link_id(extract_32(&ptr[0])); link.set_link_data(extract_32(&ptr[4])); type = ptr[8]; switch (type) { case p2p: link.set_type(p2p); break; case transit: link.set_type(transit); break; case stub: link.set_type(stub); break; case vlink: link.set_type(vlink); break; default: xorp_throw(InvalidPacket, c_format("RouterLink illegal type should be 0..4 not %u", XORP_UINT_CAST(type))); break; } link.set_metric(extract_16(&ptr[10])); // XXX - This LSA may be carrying more metric info for other // TOS. We are going to ignore them. tos_number = ptr[9]; if (0 != tos_number) XLOG_INFO("Non zero number of TOS %u", tos_number); break; case OspfTypes::V3: type = ptr[0]; switch (type) { case p2p: link.set_type(p2p); break; case transit: link.set_type(transit); break; case vlink: link.set_type(vlink); break; default: xorp_throw(InvalidPacket, c_format("RouterLink illegal type should be 1,2 or 4 not %u", XORP_UINT_CAST(type))); break; } if (0 != ptr[1]) XLOG_INFO("RouterLink field that should be zero is %u", ptr[1]); link.set_metric(extract_16(&ptr[2])); link.set_interface_id(extract_32(&ptr[4])); link.set_neighbour_interface_id(extract_32(&ptr[8])); link.set_neighbour_router_id(extract_32(&ptr[12])); break; } len = length() + tos_number * 4; return link; } /** * The caller should have called length() to pre-allocate the space * required. */ size_t RouterLink::copy_out(uint8_t *ptr) const { OspfTypes::Version version = get_version(); switch(version) { case OspfTypes::V2: embed_32(&ptr[0], get_link_id()); embed_32(&ptr[4], get_link_data()); ptr[8] = get_type(); ptr[9] = 0; // TOS embed_16(&ptr[10], get_metric()); break; case OspfTypes::V3: ptr[0] = get_type(); ptr[1] = 0; embed_16(&ptr[2], get_metric()); embed_32(&ptr[4], get_interface_id()); embed_32(&ptr[8], get_neighbour_interface_id()); embed_32(&ptr[12], get_neighbour_router_id()); break; } return length(); } string RouterLink::str() const { string output; output = c_format("Type %u", get_type()); switch(get_type()) { case p2p: output += c_format(" Point-to-point"); break; case transit: output += c_format(" Transit network"); break; case stub: output += c_format(" Stub network"); break; case vlink: output += c_format(" Virtual Link"); break; } switch(get_version()) { case OspfTypes::V2: switch(get_type()) { case p2p: output += c_format(" Neighbours Router ID %s", pr_id(get_link_id()).c_str()); output += c_format(" Routers interface address %s", pr_id(get_link_data()).c_str()); break; case transit: output += c_format(" IP address of Designated router %s", pr_id(get_link_id()).c_str()); output += c_format(" Routers interface address %s", pr_id(get_link_data()).c_str()); break; case stub: output += c_format(" Subnet number %s", pr_id(get_link_id()).c_str()); output += c_format(" Mask %s", pr_id(get_link_data()).c_str()); break; case vlink: output += c_format(" Neighbours Router ID %s", pr_id(get_link_id()).c_str()); output += c_format(" Routers interface address %s", pr_id(get_link_data()).c_str()); break; } break; case OspfTypes::V3: output += c_format(" Interface ID %u", get_interface_id()); switch(get_type()) { case transit: output += c_format(" Designated Router Interface ID %u", get_neighbour_interface_id()); output += c_format(" Designated Router ID %s", pr_id(get_neighbour_router_id()).c_str()); break; default: output += c_format(" Neighbour Interface ID %u", get_neighbour_interface_id()); output += c_format(" Neighbour Router ID %s", pr_id(get_neighbour_router_id()).c_str()); break; } break; } output += c_format(" Metric %u", get_metric()); return output; } Lsa::LsaRef UnknownLsa::decode(uint8_t *buf, size_t& len) const throw(InvalidPacket) { OspfTypes::Version version = get_version(); size_t header_length = _header.length(); size_t required = header_length + min_length(); if (len < required) xorp_throw(InvalidPacket, c_format("Unknown-LSA too short %u, must be at least %u", XORP_UINT_CAST(len), XORP_UINT_CAST(required))); // This guy throws an exception of there is a problem. len = get_lsa_len_from_header("Unknown-LSA", buf, len, required); // Verify the checksum. if (!verify_checksum(buf + 2, len - 2, 16 - 2)) xorp_throw(InvalidPacket, c_format("LSA Checksum failed")); UnknownLsa *lsa = 0; try { lsa = new UnknownLsa(version, buf, len); // Decode the LSA Header. lsa->_header.decode_inline(buf); } catch(InvalidPacket& e) { delete lsa; throw e; } return Lsa::LsaRef(lsa); } bool UnknownLsa::encode() { XLOG_FATAL("Can't encode an Unknown-LSA"); return true; } string UnknownLsa::str() const { string output; output += "Unknown-LSA:\n"; if (!valid()) output += "INVALID\n"; output += _header.str(); return output; } Lsa::LsaRef RouterLsa::decode(uint8_t *buf, size_t& len) const throw(InvalidPacket) { OspfTypes::Version version = get_version(); size_t header_length = _header.length(); size_t required = header_length + min_length(); if (len < required) xorp_throw(InvalidPacket, c_format("Router-LSA too short %u, must be at least %u", XORP_UINT_CAST(len), XORP_UINT_CAST(required))); // This guy throws an exception of there is a problem. len = get_lsa_len_from_header("Router-LSA", buf, len, required); // Verify the checksum. if (!verify_checksum(buf + 2, len - 2, 16 - 2)) xorp_throw(InvalidPacket, c_format("LSA Checksum failed")); RouterLsa *lsa = 0; try { lsa = new RouterLsa(version, buf, len); size_t nlinks = 0; // Number of Links OSPFv2 Only // Decode the LSA Header. lsa->_header.decode_inline(buf); uint8_t flag = buf[header_length]; switch(version) { case OspfTypes::V2: lsa->set_nt_bit(flag & 0x10); lsa->set_v_bit(flag & 0x4); lsa->set_e_bit(flag & 0x2); lsa->set_b_bit(flag & 0x1); nlinks = extract_16(&buf[header_length + 2]); break; case OspfTypes::V3: lsa->set_nt_bit(flag & 0x10); lsa->set_w_bit(flag & 0x8); lsa->set_v_bit(flag & 0x4); lsa->set_e_bit(flag & 0x2); lsa->set_b_bit(flag & 0x1); lsa->set_options(extract_24(&buf[header_length + 1])); break; } // Extract the router links RouterLink rl(version); uint8_t *start = &buf[header_length + 4]; uint8_t *end = &buf[len]; while(start < end) { size_t link_len = end - start; lsa->get_router_links().push_back(rl.decode(start, link_len)); XLOG_ASSERT(0 != link_len); start += link_len; } switch(version) { case OspfTypes::V2: if (nlinks != lsa->get_router_links().size()) xorp_throw(InvalidPacket, c_format( "Router-LSA mismatch in router links" " expected %u received %u", XORP_UINT_CAST(nlinks), XORP_UINT_CAST(lsa-> get_router_links().size()))); break; case OspfTypes::V3: break; } } catch(InvalidPacket& e) { delete lsa; throw e; } return Lsa::LsaRef(lsa); } bool RouterLsa::encode() { OspfTypes::Version version = get_version(); size_t router_link_len = RouterLink(version).length(); size_t len = _header.length() + 4 + _router_links.size() * router_link_len; _pkt.resize(len); uint8_t *ptr = &_pkt[0]; // uint8_t *ptr = new uint8_t[len]; memset(ptr, 0, len); // Copy the header into the packet _header.set_ls_checksum(0); _header.set_length(len); size_t header_length = _header.copy_out(ptr); XLOG_ASSERT(len > header_length); uint8_t flag = 0; switch(version) { case OspfTypes::V2: if (get_nt_bit()) flag |= 0x10; if (get_v_bit()) flag |= 0x4; if (get_e_bit()) flag |= 0x2; if (get_b_bit()) flag |= 0x1; embed_16(&ptr[header_length + 2], _router_links.size()); break; case OspfTypes::V3: if (get_nt_bit()) flag |= 0x10; if (get_w_bit()) flag |= 0x8; if (get_v_bit()) flag |= 0x4; if (get_e_bit()) flag |= 0x2; if (get_b_bit()) flag |= 0x1; embed_24(&ptr[header_length + 1], get_options()); break; } ptr[header_length] = flag; // Copy out the router links. list &rl = get_router_links(); list::iterator i = rl.begin(); size_t index = header_length + 4; for (; i != rl.end(); i++, index += router_link_len) { (*i).copy_out(&ptr[index]); } XLOG_ASSERT(index == len); // Compute the checksum and write the whole header out again. _header.set_ls_checksum(compute_checksum(ptr + 2, len - 2, 16 - 2)); _header.copy_out(ptr); return true; } string RouterLsa::str() const { OspfTypes::Version version = get_version(); string output; output += "Router-LSA:\n"; if (!valid()) output += "INVALID\n"; output += _header.str(); output += "\n"; output += c_format("\tbit Nt %s\n", bool_c_str(get_nt_bit())); switch(version) { case OspfTypes::V2: break; case OspfTypes::V3: output += c_format("\tbit W %s\n", bool_c_str(get_w_bit())); break; } output += c_format("\tbit V %s\n", bool_c_str(get_v_bit())); output += c_format("\tbit E %s\n", bool_c_str(get_e_bit())); output += c_format("\tbit B %s", bool_c_str(get_b_bit())); switch(version) { case OspfTypes::V2: // # links, don't bother to store this info. break; case OspfTypes::V3: output += c_format("\n\tOptions %#x %s", get_options(), cstring(Options(get_version(), get_options()))); break; } const list &rl = _router_links; list::const_iterator i = rl.begin(); for (; i != rl.end(); i++) { output += "\n\t" + (*i).str(); } return output; } Lsa::LsaRef NetworkLsa::decode(uint8_t *buf, size_t& len) const throw(InvalidPacket) { OspfTypes::Version version = get_version(); size_t header_length = _header.length(); size_t required = header_length + min_length(); if (len < required) xorp_throw(InvalidPacket, c_format("Network-LSA too short %u, must be at least %u", XORP_UINT_CAST(len), XORP_UINT_CAST(required))); // This guy throws an exception of there is a problem. len = get_lsa_len_from_header("Network-LSA", buf, len, required); // Verify the checksum. if (!verify_checksum(buf + 2, len - 2, 16 - 2)) xorp_throw(InvalidPacket, c_format("LSA Checksum failed")); NetworkLsa *lsa = 0; try { lsa = new NetworkLsa(version, buf, len); // Decode the LSA Header. lsa->_header.decode_inline(buf); uint8_t *start = 0; switch(version) { case OspfTypes::V2: lsa->set_network_mask(extract_32(&buf[header_length])); start = &buf[header_length + 4]; break; case OspfTypes::V3: lsa->set_options(extract_24(&buf[header_length + 1])); start = &buf[header_length + 4]; break; } uint8_t *end = &buf[len]; while(start < end) { if (!(start < end)) xorp_throw(InvalidPacket, c_format("Network-LSA too short")); lsa->get_attached_routers().push_back(extract_32(start)); start += 4; } } catch(InvalidPacket& e) { delete lsa; throw e; } return Lsa::LsaRef(lsa); } bool NetworkLsa::encode() { OspfTypes::Version version = get_version(); size_t len = 0; switch(version) { case OspfTypes::V2: len = _header.length() + 4 + 4 * get_attached_routers().size(); break; case OspfTypes::V3: len = _header.length() + 4 + 4 * get_attached_routers().size(); break; } _pkt.resize(len); uint8_t *ptr = &_pkt[0]; // uint8_t *ptr = new uint8_t[len]; memset(ptr, 0, len); // Copy the header into the packet _header.set_ls_checksum(0); _header.set_length(len); size_t header_length = _header.copy_out(ptr); XLOG_ASSERT(len > header_length); size_t index = 0; switch(version) { case OspfTypes::V2: embed_32(&ptr[header_length], get_network_mask()); index = header_length + 4; break; case OspfTypes::V3: embed_24(&ptr[header_length + 1], get_options()); index = header_length + 4; break; } // Copy out the attached router state. list &ars = get_attached_routers(); list::iterator i = ars.begin(); for (; i != ars.end(); i++) { switch(version) { case OspfTypes::V2: embed_32(&ptr[index], *i); index += 4; break; case OspfTypes::V3: embed_32(&ptr[index], *i); index += 4; break; } } XLOG_ASSERT(index == len); // Compute the checksum and write the whole header out again. _header.set_ls_checksum(compute_checksum(ptr + 2, len - 2, 16 - 2)); _header.copy_out(ptr); return true; } string NetworkLsa::str() const { OspfTypes::Version version = get_version(); string output; output += "Network-LSA:\n"; if (!valid()) output += "INVALID\n"; output += _header.str(); switch(version) { case OspfTypes::V2: output += c_format("\n\tNetwork Mask %#x", get_network_mask()); break; case OspfTypes::V3: output += c_format("\n\tOptions %#x %s", get_options(), cstring(Options(get_version(), get_options()))); break; } list ars = _attached_routers; list::iterator i = ars.begin(); for (; i != ars.end(); i++) { switch(version) { case OspfTypes::V2: break; case OspfTypes::V3: break; } output += "\n\tAttached Router " + pr_id(*i); } return output; } Lsa::LsaRef SummaryNetworkLsa::decode(uint8_t *buf, size_t& len) const throw(InvalidPacket) { OspfTypes::Version version = get_version(); size_t header_length = _header.length(); size_t required = header_length + min_length(); if (len < required) xorp_throw(InvalidPacket, c_format("Summary-LSA too short %u, must be at least %u", XORP_UINT_CAST(len), XORP_UINT_CAST(required))); // This guy throws an exception of there is a problem. len = get_lsa_len_from_header("Summary-LSA", buf, len, required); // Verify the checksum. if (!verify_checksum(buf + 2, len - 2, 16 - 2)) xorp_throw(InvalidPacket, c_format("LSA Checksum failed")); SummaryNetworkLsa *lsa = 0; try { lsa = new SummaryNetworkLsa(version, buf, len); // Decode the LSA Header. lsa->_header.decode_inline(buf); switch(version) { case OspfTypes::V2: lsa->set_network_mask(extract_32(&buf[header_length])); lsa->set_metric(extract_24(&buf[header_length + 5])); break; case OspfTypes::V3: lsa->set_metric(extract_24(&buf[header_length + 1])); IPv6Prefix prefix(version); size_t space = len - IPV6_PREFIX_OFFSET; IPv6Prefix prefix_decoder(version); prefix = prefix_decoder.decode(&buf[header_length + 8], space, buf[header_length + 4], buf[header_length + 5]); size_t space_left = (len - (IPV6_PREFIX_OFFSET + space)); if (0 != space_left) xorp_throw(InvalidPacket, c_format("Space left in LSA %u bytes", XORP_UINT_CAST(space_left))); lsa->set_ipv6prefix(prefix); break; } } catch(InvalidPacket& e) { delete lsa; throw e; } return Lsa::LsaRef(lsa); } bool SummaryNetworkLsa::encode() { OspfTypes::Version version = get_version(); size_t len = 0; switch(version) { case OspfTypes::V2: len = _header.length() + 8; break; case OspfTypes::V3: len = _header.length() + 8 + get_ipv6prefix().length(); break; } _pkt.resize(len); uint8_t *ptr = &_pkt[0]; // uint8_t *ptr = new uint8_t[len]; memset(ptr, 0, len); // Copy the header into the packet _header.set_ls_checksum(0); _header.set_length(len); size_t header_length = _header.copy_out(ptr); XLOG_ASSERT(len > header_length); size_t index = 0; switch(version) { case OspfTypes::V2: embed_32(&ptr[header_length], get_network_mask()); embed_24(&ptr[header_length + 5], get_metric()); index = header_length + 8; break; case OspfTypes::V3: embed_24(&ptr[header_length + 1], get_metric()); IPv6Prefix prefix = get_ipv6prefix(); ptr[header_length + 4] = prefix.get_network().prefix_len(); ptr[header_length + 5] = prefix.get_prefix_options(); index = header_length + 8 + prefix.copy_out(&ptr[header_length + 8]); break; } XLOG_ASSERT(len == index); // Compute the checksum and write the whole header out again. _header.set_ls_checksum(compute_checksum(ptr + 2, len - 2, 16 - 2)); _header.copy_out(ptr); return true; } string SummaryNetworkLsa::str() const { OspfTypes::Version version = get_version(); string output; switch(version) { case OspfTypes::V2: output = "Summary-LSA:\n"; break; case OspfTypes::V3: output = "Inter-Area-Prefix-LSA:\n"; break; } if (!valid()) output += "INVALID\n"; output += _header.str(); switch(version) { case OspfTypes::V2: output += c_format("\n\tNetwork Mask %#x", get_network_mask()); output += c_format("\n\tMetric %d", get_metric()); break; case OspfTypes::V3: output += c_format("\n\tMetric %d", get_metric()); output += c_format("\n\tIPv6Prefix %s", cstring(get_ipv6prefix())); break; } return output; } Lsa::LsaRef SummaryRouterLsa::decode(uint8_t *buf, size_t& len) const throw(InvalidPacket) { OspfTypes::Version version = get_version(); size_t header_length = _header.length(); size_t required = header_length + min_length(); if (len < required) xorp_throw(InvalidPacket, c_format("Summary-LSA too short %u, must be at least %u", XORP_UINT_CAST(len), XORP_UINT_CAST(required))); // This guy throws an exception of there is a problem. len = get_lsa_len_from_header("Summary-LSA", buf, len, required); // Verify the checksum. if (!verify_checksum(buf + 2, len - 2, 16 - 2)) xorp_throw(InvalidPacket, c_format("LSA Checksum failed")); SummaryRouterLsa *lsa = 0; try { lsa = new SummaryRouterLsa(version, buf, len); // Decode the LSA Header. lsa->_header.decode_inline(buf); switch(version) { case OspfTypes::V2: lsa->set_network_mask(extract_32(&buf[header_length])); lsa->set_metric(extract_24(&buf[header_length + 5])); break; case OspfTypes::V3: lsa->set_options(extract_24(&buf[header_length + 1])); lsa->set_metric(extract_24(&buf[header_length + 5])); lsa->set_destination_id(extract_32(&buf[header_length + 8])); break; } } catch(InvalidPacket& e) { delete lsa; throw e; } return Lsa::LsaRef(lsa); } bool SummaryRouterLsa::encode() { OspfTypes::Version version = get_version(); size_t len = 0; switch(version) { case OspfTypes::V2: len = _header.length() + 8; break; case OspfTypes::V3: len = _header.length() + 12; break; } _pkt.resize(len); uint8_t *ptr = &_pkt[0]; // uint8_t *ptr = new uint8_t[len]; memset(ptr, 0, len); // Copy the header into the packet _header.set_ls_checksum(0); _header.set_length(len); size_t header_length = _header.copy_out(ptr); XLOG_ASSERT(len > header_length); size_t index = 0; switch(version) { case OspfTypes::V2: embed_32(&ptr[header_length], get_network_mask()); embed_24(&ptr[header_length + 5], get_metric()); index = header_length + 8; break; case OspfTypes::V3: embed_24(&ptr[header_length + 1], get_options()); embed_24(&ptr[header_length + 5], get_metric()); embed_32(&ptr[header_length + 8], get_destination_id()); index = header_length + 12; break; } XLOG_ASSERT(len == index); // Compute the checksum and write the whole header out again. _header.set_ls_checksum(compute_checksum(ptr + 2, len - 2, 16 - 2)); _header.copy_out(ptr); return true; } string SummaryRouterLsa::str() const { OspfTypes::Version version = get_version(); string output; switch(version) { case OspfTypes::V2: output = "Summary-LSA:\n"; break; case OspfTypes::V3: output = "Inter-Area-Router-LSA:\n"; break; } if (!valid()) output += "INVALID\n"; output += _header.str(); switch(version) { case OspfTypes::V2: output += c_format("\n\tNetwork Mask %#x", get_network_mask()); output += c_format("\n\tMetric %d", get_metric()); break; case OspfTypes::V3: output += c_format("\n\tOptions %#x %s", get_options(), cstring(Options(get_version(), get_options()))); output += c_format("\n\tMetric %d", get_metric()); output += c_format("\n\tDestination Router ID %s", pr_id(get_destination_id()).c_str()); break; } return output; } Lsa::LsaRef ASExternalLsa::decode(uint8_t *buf, size_t& len) const throw(InvalidPacket) { OspfTypes::Version version = get_version(); size_t header_length = _header.length(); size_t required = header_length + min_length(); if (len < required) xorp_throw(InvalidPacket, c_format("AS-External-LSA too short %u, " "must be at least %u", XORP_UINT_CAST(len), XORP_UINT_CAST(required))); // This guy throws an exception of there is a problem. len = get_lsa_len_from_header("AS-External-LSA", buf, len, required); // Verify the checksum. if (!verify_checksum(buf + 2, len - 2, 16 - 2)) xorp_throw(InvalidPacket, c_format("LSA Checksum failed")); ASExternalLsa *lsa = 0; try { // lsa = new this(version, buf, len); lsa = donew(version, buf, len); // Decode the LSA Header. lsa->_header.decode_inline(buf); uint8_t flag; switch(version) { case OspfTypes::V2: { lsa->set_network_mask(extract_32(&buf[header_length])); flag = buf[header_length + 4]; lsa->set_e_bit(flag & 0x80); lsa->set_metric(extract_24(&buf[header_length + 5])); IPv4 forwarding_address; forwarding_address.copy_in(&buf[header_length + 8]); lsa->set_forwarding_address_ipv4(forwarding_address); lsa->set_external_route_tag(extract_32(&buf[header_length + 12])); } break; case OspfTypes::V3: flag = buf[header_length]; lsa->set_e_bit(flag & 0x4); lsa->set_f_bit(flag & 0x2); lsa->set_t_bit(flag & 0x1); lsa->set_metric(extract_24(&buf[header_length + 1])); lsa->set_referenced_ls_type(extract_16(&buf[header_length + 6])); size_t space = len - IPV6_PREFIX_OFFSET; IPv6Prefix prefix_decoder(version); lsa->set_ipv6prefix(prefix_decoder.decode(&buf[header_length + 8], space, buf[header_length + 4], buf[header_length + 5])); size_t index = header_length + 8 + space; if (lsa->get_f_bit()) { if (index + IPv6::ADDR_BYTELEN > len) xorp_throw(InvalidPacket, c_format("AS-External-LSA" " bit F set, packet too short")); IPv6 address; address.copy_in(&buf[index]); lsa->set_forwarding_address_ipv6(address); index += IPv6::ADDR_BYTELEN; } if (lsa->get_t_bit()) { if (index + 4 > len) xorp_throw(InvalidPacket, c_format("AS-External-LSA" " bit T set, packet too short")); lsa->set_external_route_tag(extract_32(&buf[index])); index += 4; } if (0 != lsa->get_referenced_ls_type()) { if (index + 4 > len) xorp_throw(InvalidPacket, c_format("AS-External-LSA" " Referenced LS Type set, " "packet too short")); lsa->set_referenced_link_state_id(extract_32(&buf[index])); } break; } } catch(InvalidPacket& e) { delete lsa; throw e; } return Lsa::LsaRef(lsa); } bool ASExternalLsa::encode() { OspfTypes::Version version = get_version(); size_t len = 0; switch(version) { case OspfTypes::V2: len = _header.length() + 16; break; case OspfTypes::V3: len = _header.length() + 8 + get_ipv6prefix().length() + (get_f_bit() ? IPv6::ADDR_BYTELEN : 0) + (get_t_bit() ? 4 : 0) + (0 != get_referenced_ls_type() ? 4 : 0); break; } _pkt.resize(len); uint8_t *ptr = &_pkt[0]; // uint8_t *ptr = new uint8_t[len]; memset(ptr, 0, len); // Copy the header into the packet _header.set_ls_checksum(0); _header.set_length(len); size_t header_length = _header.copy_out(ptr); XLOG_ASSERT(len > header_length); size_t index = 0; uint8_t flag = 0; switch(version) { case OspfTypes::V2: { embed_32(&ptr[header_length], get_network_mask()); if (get_e_bit()) flag |= 0x80; ptr[header_length + 4] = flag; embed_24(&ptr[header_length + 5], get_metric()); IPv4 forwarding_address = get_forwarding_address_ipv4(); forwarding_address.copy_out(&ptr[header_length + 8]); embed_32(&ptr[header_length + 12], get_external_route_tag()); index = header_length + 16; } break; case OspfTypes::V3: if (get_e_bit()) flag |= 0x4; if (get_f_bit()) flag |= 0x2; if (get_t_bit()) flag |= 0x1; ptr[header_length] = flag; embed_24(&ptr[header_length + 1], get_metric()); embed_16(&ptr[header_length + 6], get_referenced_ls_type()); IPv6Prefix prefix = get_ipv6prefix(); ptr[header_length + 4] = prefix.get_network().prefix_len(); ptr[header_length + 5] = prefix.get_prefix_options(); index = header_length + 8 + prefix.copy_out(&ptr[header_length + 8]); if (get_f_bit()) { IPv6 forwarding_address = get_forwarding_address_ipv6(); forwarding_address.copy_out(&ptr[index]); index += IPv6::ADDR_BYTELEN; } if (get_t_bit()) { embed_32(&ptr[index], get_external_route_tag()); index += 4; } if (0 != get_referenced_ls_type()) { embed_32(&ptr[index], get_referenced_link_state_id()); index += 4; } break; } XLOG_ASSERT(len == index); // Compute the checksum and write the whole header out again. _header.set_ls_checksum(compute_checksum(ptr + 2, len - 2, 16 - 2)); _header.copy_out(ptr); return true; } template <> void ASExternalLsa::set_network(IPNet net) { XLOG_ASSERT(OspfTypes::V2 == get_version()); set_network_mask(ntohl(net.netmask().addr())); get_header().set_link_state_id(ntohl(net.masked_addr().addr())); } template <> void ASExternalLsa::set_network(IPNet net) { XLOG_ASSERT(OspfTypes::V3 == get_version()); IPv6Prefix prefix(get_version()); prefix.set_network(net); set_ipv6prefix(prefix); } template <> IPNet ASExternalLsa::get_network(IPv4) const { return IPNet(IPv4(htonl(get_header().get_link_state_id())), IPv4(htonl(get_network_mask())).mask_len()); } template <> IPNet ASExternalLsa::get_network(IPv6) const { return get_ipv6prefix().get_network(); } template <> void ASExternalLsa::set_forwarding_address(IPv4 forwarding_address_ipv4) { set_forwarding_address_ipv4(forwarding_address_ipv4); } template <> void ASExternalLsa::set_forwarding_address(IPv6 forwarding_address_ipv6) { set_forwarding_address_ipv6(forwarding_address_ipv6); } template <> IPv4 ASExternalLsa::get_forwarding_address(IPv4) const { return get_forwarding_address_ipv4(); } template <> IPv6 ASExternalLsa::get_forwarding_address(IPv6) const { return get_forwarding_address_ipv6(); } string ASExternalLsa::str() const { OspfTypes::Version version = get_version(); string output; output = str_name() + ":\n"; if (!valid()) output += "INVALID\n"; output += _header.str(); switch(version) { case OspfTypes::V2: output += c_format("\n\tNetwork Mask %#x", get_network_mask()); output += c_format("\n\tbit E %s", bool_c_str(get_e_bit())); output += c_format("\n\tMetric %d %#x", get_metric(), get_metric()); if (get_metric() == OspfTypes::LSInfinity) output += c_format(" LSInfinity"); output += c_format("\n\tForwarding address %s", cstring(get_forwarding_address_ipv4())); output += c_format("\n\tExternal Route Tag %#x", get_external_route_tag()); break; case OspfTypes::V3: output += c_format("\n\tbit E %s", bool_c_str(get_e_bit())); output += c_format("\n\tbit F %s", bool_c_str(get_f_bit())); output += c_format("\n\tbit T %s", bool_c_str(get_t_bit())); output += c_format("\n\tMetric %d %#x", get_metric(), get_metric()); if (get_metric() == OspfTypes::LSInfinity) output += c_format(" LSInfinity"); output += c_format("\n\tIPv6Prefix %s", cstring(get_ipv6prefix())); output += c_format("\n\tReferenced LS Type %#x", get_referenced_ls_type()); if (get_f_bit()) output += c_format("\n\tForwarding address %s", cstring(get_forwarding_address_ipv6())); if (get_t_bit()) output += c_format("\n\tExternal Route Tag %#x", get_external_route_tag()); if (0 != get_referenced_ls_type()) output += c_format("\n\tReferenced Link State ID %s", pr_id(get_referenced_link_state_id()).c_str()); break; } return output; } Lsa::LsaRef LinkLsa::decode(uint8_t *buf, size_t& len) const throw(InvalidPacket) { OspfTypes::Version version = get_version(); XLOG_ASSERT(OspfTypes::V3 == version); size_t header_length = _header.length(); size_t required = header_length + min_length(); if (len < required) xorp_throw(InvalidPacket, c_format("Link-LSA too short %u, must be at least %u", XORP_UINT_CAST(len), XORP_UINT_CAST(required))); // This guy throws an exception of there is a problem. len = get_lsa_len_from_header("Link-LSA", buf, len, required); // Verify the checksum. if (!verify_checksum(buf + 2, len - 2, 16 - 2)) xorp_throw(InvalidPacket, c_format("LSA Checksum failed")); LinkLsa *lsa = 0; try { lsa = new LinkLsa(version, buf, len); // Decode the LSA Header. lsa->_header.decode_inline(buf); uint8_t *start = 0; lsa->set_rtr_priority(extract_8(&buf[header_length + 0])); lsa->set_options(extract_24(&buf[header_length + 1])); IPv6 address; address.copy_in(&buf[header_length + 4]); lsa->set_link_local_address(address); size_t prefix_num = extract_32(&buf[header_length + 4 + IPv6::ADDR_BYTELEN]); start = &buf[header_length + 4 + IPv6::ADDR_BYTELEN + 4]; uint8_t *end = &buf[len]; IPv6Prefix decoder(version); while(start < end) { if (!(start + 2 < end)) xorp_throw(InvalidPacket, c_format("Link-LSA too short")); size_t space = end - (start + 4); IPv6Prefix prefix = decoder.decode(start + 4, space, extract_8(start), extract_8(start + 1)); lsa->get_prefixes().push_back(prefix); start += (space + 4); if (0 == --prefix_num) { if (start != end) xorp_throw(InvalidPacket, c_format("Link-LSA # prefixes read data left")); break; } } if (0 != prefix_num) if (start != end) xorp_throw(InvalidPacket, c_format("Link-LSA # %d left buffer depleted", XORP_UINT_CAST(prefix_num))); } catch(InvalidPacket& e) { delete lsa; throw e; } return Lsa::LsaRef(lsa); } bool LinkLsa::encode() { OspfTypes::Version version = get_version(); XLOG_ASSERT(OspfTypes::V3 == version); size_t len = _header.length() + 4 + IPv6::ADDR_BYTELEN + 4; list &ps = get_prefixes(); for(list::iterator i = ps.begin(); i != ps.end(); i++) len += i->length() + 4; _pkt.resize(len); uint8_t *ptr = &_pkt[0]; // uint8_t *ptr = new uint8_t[len]; memset(ptr, 0, len); // Copy the header into the packet _header.set_ls_checksum(0); _header.set_length(len); size_t header_length = _header.copy_out(ptr); XLOG_ASSERT(len > header_length); size_t index = 0; embed_8(&ptr[header_length], get_rtr_priority()); embed_24(&ptr[header_length + 1], get_options()); get_link_local_address().copy_out(&ptr[header_length + 4]); embed_32(&ptr[header_length + 4 + IPv6::ADDR_BYTELEN], ps.size()); index = header_length + 4 + IPv6::ADDR_BYTELEN + 4; // Copy out the IPv6 Prefixes. for(list::iterator i = ps.begin(); i != ps.end(); i++) { embed_8(&ptr[index], i->get_network().prefix_len()); embed_8(&ptr[index + 1], i->get_prefix_options()); index += i->copy_out(&ptr[index + 4]) + 4; } XLOG_ASSERT(index == len); // Compute the checksum and write the whole header out again. _header.set_ls_checksum(compute_checksum(ptr + 2, len - 2, 16 - 2)); _header.copy_out(ptr); return true; } string LinkLsa::str() const { OspfTypes::Version version = get_version(); XLOG_ASSERT(OspfTypes::V3 == version); string output; output += "Link-LSA:\n"; if (!valid()) output += "INVALID\n"; output += _header.str(); output += c_format("\n\tRtr Priority %d", get_rtr_priority()); output += c_format("\n\tOptions %#x %s", get_options(), cstring(Options(get_version(), get_options()))); output += c_format("\n\tLink-local Interface Address %s", cstring(get_link_local_address())); list ps = _prefixes; for(list::iterator i = ps.begin(); i != ps.end(); i++) output += "\n\tIPv6 Prefix " + i->str(); return output; } Lsa::LsaRef IntraAreaPrefixLsa::decode(uint8_t *buf, size_t& len) const throw(InvalidPacket) { OspfTypes::Version version = get_version(); XLOG_ASSERT(OspfTypes::V3 == version); size_t header_length = _header.length(); size_t required = header_length + min_length(); if (len < required) xorp_throw(InvalidPacket, c_format("Intra-Area-Prefix-LSA too short %u, " "must be at least %u", XORP_UINT_CAST(len), XORP_UINT_CAST(required))); // This guy throws an exception of there is a problem. len = get_lsa_len_from_header("Intra-Area-Prefix-LSA", buf, len, required); // Verify the checksum. if (!verify_checksum(buf + 2, len - 2, 16 - 2)) xorp_throw(InvalidPacket, c_format("LSA Checksum failed")); IntraAreaPrefixLsa *lsa = 0; try { lsa = new IntraAreaPrefixLsa(version, buf, len); // Decode the LSA Header. lsa->_header.decode_inline(buf); uint8_t *start = 0; size_t prefix_num = extract_16(&buf[header_length]); lsa->set_referenced_ls_type(extract_16(&buf[header_length + 2])); lsa->set_referenced_link_state_id(extract_32(&buf[header_length + 4])); lsa->set_referenced_advertising_router(extract_32(&buf[header_length + 8])); start = &buf[header_length + 2 + 2 + 4 + 4]; uint8_t *end = &buf[len]; IPv6Prefix decoder(version, true); while(start < end) { if (!(start + 2 < end)) xorp_throw(InvalidPacket, c_format("Intra-Area-Prefix-LSA " "too short")); size_t space = end - (start + 4); IPv6Prefix prefix = decoder.decode(start + 4, space, extract_8(start), extract_8(start + 1)); prefix.set_metric(extract_16(start + 2)); lsa->get_prefixes().push_back(prefix); start += (space + 4); if (0 == --prefix_num) { if (start != end) xorp_throw(InvalidPacket, c_format("Intra-Area-Prefix-LSA # prefixes " "read data left")); break; } } if (0 != prefix_num) if (start != end) xorp_throw(InvalidPacket, c_format("Intra-Area-Prefix-LSA # %d left " "buffer depleted", XORP_UINT_CAST(prefix_num))); } catch(InvalidPacket& e) { delete lsa; throw e; } return Lsa::LsaRef(lsa); } bool IntraAreaPrefixLsa::encode() { OspfTypes::Version version = get_version(); XLOG_ASSERT(OspfTypes::V3 == version); size_t len = _header.length() + 2 + 2 + 4 + 4; list &ps = get_prefixes(); for(list::iterator i = ps.begin(); i != ps.end(); i++) len += i->length() + 4; _pkt.resize(len); uint8_t *ptr = &_pkt[0]; // uint8_t *ptr = new uint8_t[len]; memset(ptr, 0, len); // Copy the header into the packet _header.set_ls_checksum(0); _header.set_length(len); size_t header_length = _header.copy_out(ptr); XLOG_ASSERT(len > header_length); embed_16(&ptr[header_length], ps.size()); embed_16(&ptr[header_length + 2], get_referenced_ls_type()); embed_32(&ptr[header_length + 4], get_referenced_link_state_id()); embed_32(&ptr[header_length + 8], get_referenced_advertising_router()); size_t index = header_length + 2 + 2 + 4 + 4; // Copy out the IPv6 Prefixes. for(list::iterator i = ps.begin(); i != ps.end(); i++) { embed_8(&ptr[index], i->get_network().prefix_len()); embed_8(&ptr[index + 1], i->get_prefix_options()); embed_16(&ptr[index + 2], i->get_metric()); index += i->copy_out(&ptr[index + 4]) + 4; } XLOG_ASSERT(index == len); // Compute the checksum and write the whole header out again. _header.set_ls_checksum(compute_checksum(ptr + 2, len - 2, 16 - 2)); _header.copy_out(ptr); return true; } string IntraAreaPrefixLsa::str() const { OspfTypes::Version version = get_version(); XLOG_ASSERT(OspfTypes::V3 == version); string output; output += "Intra-Area-Prefix-LSA:\n"; if (!valid()) output += "INVALID\n"; output += _header.str(); output += c_format("\n\tReferenced LS type %#x", get_referenced_ls_type()); if (get_referenced_ls_type() == RouterLsa(version).get_ls_type()) { output += c_format(" Router-LSA"); } else if (get_referenced_ls_type() == NetworkLsa(version).get_ls_type()) { output += c_format(" Network-LSA"); } else { output += c_format(" Unknown"); } output += c_format("\n\tReferenced Link State ID %s", pr_id(get_referenced_link_state_id()).c_str()); output += c_format("\n\tReferenced Advertising Router %s", pr_id(get_referenced_advertising_router()).c_str()); list ps = _prefixes; for(list::iterator i = ps.begin(); i != ps.end(); i++) output += "\n\tIPv6 Prefix " + i->str(); return output; } xorp/ospf/delay_queue.hh0000664000076400007640000000634511421137511015456 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/ospf/delay_queue.hh,v 1.10 2008/10/02 21:57:47 bms Exp $ #ifndef __OSPF_DELAY_QUEUE_HH__ #define __OSPF_DELAY_QUEUE_HH__ /** * Entries can be added to the queue at any rate. The callback is * invoked at the specified period to remove an entry from the queue. */ template class DelayQueue { public: typedef typename XorpCallback1::RefPtr DelayCallback; DelayQueue(EventLoop& eventloop, uint32_t delay, DelayCallback forward) : _eventloop(eventloop), _delay(delay), _forward(forward) {} /** * Add an entry to the queue. If the entry is already on the queue * it is not added again. */ void add(_Entry entry); /** * Start the timer running but don't add anything to the queue. */ void fire(); private: EventLoop& _eventloop; deque<_Entry> _queue; const uint32_t _delay; // Delay in seconds. DelayCallback _forward; // Invoked to forward an entry from the queue. XorpTimer _timer; // Timer that services the queue. /** * Invoked from the timer to take the next entry from the queue. */ void next(); }; template void DelayQueue<_Entry>::add(_Entry entry) { // If this entry is already on the queue just return. if (_queue.end() != find(_queue.begin(), _queue.end(), entry)) return; // If the timer is running push this entry to the back of the // queue and return. if (_timer.scheduled()) { _queue.push_back(entry); return; } // If the timer isn't running then we have been idle for more than // delay seconds. Forward this entry immediately and start the // timer. Start the timer first in case this code is re-entered. _timer = _eventloop.new_oneoff_after(TimeVal(_delay, 0), callback(this, &DelayQueue::next)); _forward->dispatch(entry); } template void DelayQueue<_Entry>::fire() { if (_timer.scheduled()) return; _timer = _eventloop.new_oneoff_after(TimeVal(_delay, 0), callback(this, &DelayQueue::next)); } template void DelayQueue<_Entry>::next() { if (_queue.empty()) return; _timer = _eventloop.new_oneoff_after(TimeVal(_delay, 0), callback(this, &DelayQueue::next)); _Entry entry = _queue.front(); _queue.pop_front(); _forward->dispatch(entry); } #endif // __OSPF_DELAY_QUEUE_HH__ xorp/ospf/lsa2.data0000664000076400007640000000070211421137511014316 0ustar greearbgreearb/* Age 0x0,0x01,*/ /* Options */ 0x02, /* LSA Type */ 0x05, /* Link State ID */ 0x02, 0x02, 0x08, 0x98, /* Advertising Router */ 0x0a, 0x00, 0x00, 0x32, /* LS Sequence Number */ 0x80, 0x00, 0x00, 0x01, /* LS Checksum */ 0xff, 0xb7, /* Length */ 0x00, 0x24, /* Netmask */ 0xff, 0xff, 0xff, 0xfc, /* External Type */ 0x80, /* Metric */ 0x00, 0x00, 0x1, /* Forwarding Address */ 0x0a, 0x00, 0x00, 0x32, /* External Route Tag */ 0x00, 0x00 xorp/ospf/lsa.hh0000664000076400007640000015344411421137511013736 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/ospf/lsa.hh,v 1.113 2008/10/02 21:57:47 bms Exp $ #ifndef __OSPF_LSA_HH__ #define __OSPF_LSA_HH__ /** * LSA Header. Common header for all LSAs. * Never store or pass a pointer, just deal with it inline. */ class Lsa_header { public: Lsa_header(OspfTypes::Version version) : _version(version), _LS_age(0), _options(0), _ls_type(0), _link_state_id(0), _advertising_router(0), _ls_sequence_number(OspfTypes::InitialSequenceNumber), _ls_checksum(0), _length(0) {} Lsa_header(const Lsa_header& rhs) { copy(rhs); } Lsa_header operator=(const Lsa_header& rhs) { if(&rhs == this) return *this; copy(rhs); return *this; } #define lsa_copy(var) var = rhs.var; void copy(const Lsa_header& rhs) { lsa_copy(_version); lsa_copy(_LS_age); lsa_copy(_options); lsa_copy(_ls_type); lsa_copy(_link_state_id); lsa_copy(_advertising_router); lsa_copy(_ls_sequence_number); lsa_copy(_ls_checksum); lsa_copy(_length); } #undef lsa_copy /** * @return the length of an LSA header. */ static size_t length() { return 20; } /** * Get the length of the LSA from the buffer provided */ static uint16_t get_lsa_len_from_buffer(uint8_t *ptr); /** * Decode a LSA header and return a LSA header inline not a pointer. */ Lsa_header decode(uint8_t *ptr) const throw(InvalidPacket); /** * Decode this lsa header in this context. */ void decode_inline(uint8_t *ptr) throw(InvalidPacket); /** * Copy a wire format representation to the pointer provided. * @return the number of bytes written. */ size_t copy_out(uint8_t *to_uint8) const; OspfTypes::Version get_version() const { return _version; } // LS age void set_ls_age(uint16_t ls_age) { _LS_age = ls_age; } uint16_t get_ls_age() const { return _LS_age; } // Options void set_options(uint8_t options) { XLOG_ASSERT(OspfTypes::V2 == get_version()); _options = options; } uint8_t get_options() const { XLOG_ASSERT(OspfTypes::V2 == get_version()); return _options; } // LS type void set_ls_type(uint16_t ls_type) { switch(get_version()) { case OspfTypes::V2: if (ls_type > 0xff) XLOG_WARNING("Attempt to set %#x in an 8 bit field", ls_type); _ls_type = ls_type & 0xff; break; case OspfTypes::V3: _ls_type = ls_type; break; } } uint16_t get_ls_type() const { return _ls_type; } // Link State ID void set_link_state_id(uint32_t link_state_id) { _link_state_id = link_state_id; } uint32_t get_link_state_id() const { return _link_state_id; } // Advertising Router void set_advertising_router(uint32_t advertising_router) { _advertising_router = advertising_router; } uint32_t get_advertising_router() const { return _advertising_router; } // LS sequence number void set_ls_sequence_number(int32_t ls_sequence_number) { _ls_sequence_number = ls_sequence_number; } int32_t get_ls_sequence_number() const { return _ls_sequence_number; } // LS checksum void set_ls_checksum(uint16_t ls_checksum) { _ls_checksum = ls_checksum; } uint16_t get_ls_checksum() const { return _ls_checksum; } // Length void set_length(uint16_t length) { _length = length; } uint16_t get_length() const { return _length; } /** * Generate a printable representation of the header. */ string str() const; private: void decode(Lsa_header& header, uint8_t *ptr) const throw(InvalidPacket); OspfTypes::Version _version; uint16_t _LS_age; uint8_t _options; // OSPFv2 Only uint16_t _ls_type; // OSPFv2 1 byte, OSPFv3 2 bytes. uint32_t _link_state_id; uint32_t _advertising_router; int32_t _ls_sequence_number; uint16_t _ls_checksum; uint16_t _length; }; /** * Compare the three fields that make an LSA equivalent. * * RFC 2328 Section 12.1. The LSA Header: * * "The LSA header contains the LS type, Link State ID and * Advertising Router fields. The combination of these three * fields uniquely identifies the LSA." */ inline bool operator==(const Lsa_header& lhs, const Lsa_header& rhs) { // We could check for lhs == rhs this will be such a rare // occurence why bother. if (lhs.get_ls_type() != rhs.get_ls_type()) return false; if (lhs.get_link_state_id() != rhs.get_link_state_id()) return false; if (lhs.get_advertising_router() != rhs.get_advertising_router()) return false; return true; } /** * RFC 2328 Section 13.7. Receiving link state acknowledgments * * All the fields in the header need to be compared except for the age. */ inline bool compare_all_header_fields(const Lsa_header& lhs, const Lsa_header& rhs) { // We could check for lhs == rhs this will be such a rare // occurence why bother. // Try and order the comparisons so in the no match case we blow // out early. #define lsa_header_compare(func) if (lhs.func != rhs.func) return false; lsa_header_compare(get_ls_checksum()); lsa_header_compare(get_length()); switch(lhs.get_version()) { case OspfTypes::V2: lsa_header_compare(get_options()); break; case OspfTypes::V3: break; } lsa_header_compare(get_ls_sequence_number()); lsa_header_compare(get_ls_type()); lsa_header_compare(get_link_state_id()); lsa_header_compare(get_advertising_router()); #undef lsa_header_compare return true; } /** * Link State Advertisement (LSA) * * A generic LSA. All actual LSAs should be derived from this LSA. */ class Lsa { public: /** * A reference counted pointer to an LSA which will be * automatically deleted. */ typedef ref_ptr LsaRef; Lsa(OspfTypes::Version version) : _header(version), _version(version), _valid(true), _self_originating(false), _initial_age(0), _transmitted(false), _trace(false), _peerid(OspfTypes::ALLPEERS) {} /** * Note passing in the LSA buffer does not imply that it will be * decoded. The buffer is just being stored. */ Lsa(OspfTypes::Version version, uint8_t *buf, size_t len) : _header(version), _version(version), _valid(true), _self_originating(false), _initial_age(0), _transmitted(false), _trace(false), _peerid(OspfTypes::ALLPEERS) { _pkt.resize(len); memcpy(&_pkt[0], buf, len); } virtual ~Lsa() {} OspfTypes::Version get_version() const { return _version; } /** * It is the responsibilty of the derived type to return this * information. * * @return The type this lsa represents. */ virtual uint16_t get_ls_type() const = 0; /** * OSPFv3 only is this a known LSA. * * @return true if this is a known LSA. */ virtual bool known() const { XLOG_ASSERT(OspfTypes::V3 == get_version()); return true; } /** * @return True if this is an AS-external-LSA. */ virtual bool external() const { return false; }; /** * @return True if this LSA is a Type-7-LSA. */ virtual bool type7() const { return false; } /** * It is the responsibilty of the derived type to return this * information. * @return The minmum possible length of this LSA. */ virtual size_t min_length() const = 0; /** * Decode an LSA. * @param buf pointer to buffer. * @param len length of the buffer on input set to the number of * bytes consumed on output. * * @return A reference to an LSA that manages its own memory. */ virtual LsaRef decode(uint8_t *buf, size_t& len) const throw(InvalidPacket) = 0; /** * Encode an LSA for transmission. * * @return True on success. */ virtual bool encode() = 0; /** * Get a reference to the raw LSA * * @param len The length of the LSA. * * @return pointer to start of the LSA. */ uint8_t *lsa(size_t &len) { len = _pkt.size(); XLOG_ASSERT(0 != len); return &_pkt[0]; } /** * Is a wire format version available? * * For all non self orignating LSAs there should be a wire version * available. * * Self originating LSAs such as Router LSAs can exist that do not * yet have any valid fields (no interfaces to describe). Use this * field to check if this LSA is available. * * @return true is a wire format version is available. */ bool available() const { return 0 != _pkt.size(); } Lsa_header& get_header() {return _header; } const Lsa_header& get_header() const {return _header; } /** * Is this LSA valid? */ bool valid() const { return _valid; } /** * Unconditionally clear the timer and invalidate the LSA if * required, the default is to invalidate the LSA. */ void invalidate(bool invalidate = true) { if(invalidate) _valid = false; _timer.clear(); } /** * @return true of this is a self originating LSA. */ bool get_self_originating() const { return _self_originating; } /** * Set the state of this LSA with respect to self originating or not. */ void set_self_originating(bool orig) { _self_originating = orig; } /** * Record the time of creation and initial age. * @param now the current time. */ void record_creation_time(TimeVal now) { _creation = now; _initial_age = _header.get_ls_age(); } /** * Get arrival time. * @param now out parameter which will be contain the time the LSA * arrived. */ void get_creation_time(TimeVal& now) { now = _creation; } /** * Revive an LSA that is at MaxAge and MaxSequenceNumber. The age * is taken back to zero and sequence number InitialSequenceNumber. */ void revive(const TimeVal& now); /** * Update the age and sequence number of a self originated * LSAs. Plus encode. */ void update_age_and_seqno(const TimeVal& now); /** * Update the age field based on the current time. * * @param now the current time. */ void update_age(TimeVal now); /** * Increment the age field of an LSA by inftransdelay. * * @param ptr to the age field, first field in a LSA. * @param inftransdelay delay to add in seconds. */ static void update_age_inftransdelay(uint8_t *ptr, uint16_t inftransdelay); /** * Set the age to MaxAge. */ void set_maxage(); /** * Is the age of this LSA MaxAge. */ bool maxage() const; /** * Is the LS Sequence Number MaxSequenceNumber? */ bool max_sequence_number() const; /** * Get the LS Sequence Number. */ int32_t get_ls_sequence_number() const { return _header.get_ls_sequence_number(); } /** * Set the LS Sequence Number. */ void set_ls_sequence_number(int32_t seqno) { _header.set_ls_sequence_number(seqno); } /** * Increment sequence number. */ void increment_sequence_number() { int32_t seqno = _header.get_ls_sequence_number(); if (OspfTypes:: MaxSequenceNumber == seqno) XLOG_FATAL("Bummer sequence number reached %d", OspfTypes::MaxSequenceNumber); seqno += 1; _header.set_ls_sequence_number(seqno); } /** * Add a neighbour ID to the NACK list. */ void add_nack(OspfTypes::NeighbourID nid) { _nack_list.insert(nid); } /** * Remove a neighbour ID from the NACK list. */ void remove_nack(OspfTypes::NeighbourID nid) { _nack_list.erase(nid); } /** * Does this neighbour exist on the NACK list. */ bool exists_nack(OspfTypes::NeighbourID nid) { return _nack_list.end() != _nack_list.find(nid); } /** * If the NACK list is empty return true. All of the neighbours * have now nacked this LSA. */ bool empty_nack() const { return _nack_list.empty(); } /** * @return true if this LSA has been transmitted. */ bool get_transmitted() { return _transmitted; } /** * Set the transmitted state of this LSA. */ void set_transmitted(bool t) { _transmitted = t; } /** * Get a reference to the internal timer. */ XorpTimer& get_timer() { return _timer; } void set_tracing(bool trace) { _trace = trace; } bool tracing() const { return _trace; } /** * For OSPFv3 only LSAs with Link-local flooding scope save the * ingress PeerID. */ void set_peerid(OspfTypes::PeerID peerid) { XLOG_ASSERT(OspfTypes::V3 == get_version()); XLOG_ASSERT(OspfTypes::ALLPEERS == _peerid); _peerid = peerid; } /** * For OSPFv3 only LSAs with Link-local flooding scope get the * ingress PeerID. */ OspfTypes::PeerID get_peerid() const { XLOG_ASSERT(OspfTypes::V3 == get_version()); XLOG_ASSERT(OspfTypes::ALLPEERS != _peerid); return _peerid; } // OSPFv3 only, if an LSA is not known and does not have the U-bit // set then it should be treated as if it has Link-local flooding scope. /** * OSPFv3 only Link-local scope. */ bool link_local_scope() const { XLOG_ASSERT(OspfTypes::V3 == get_version()); if (!understood()) return true; return 0 == (get_ls_type() & 0x6000); } /** * OSPFv3 only area scope. */ bool area_scope() const { XLOG_ASSERT(OspfTypes::V3 == get_version()); if (!understood()) return false; return 0x2000 == (get_ls_type() & 0x6000); } /** * OSPFv3 only AS scope. */ bool as_scope() const { XLOG_ASSERT(OspfTypes::V3 == get_version()); if (!understood()) return false; return 0x4000 == (get_ls_type() & 0x6000); } /** * Printable name of this LSA. */ virtual const char *name() const = 0; /** * Generate a printable representation of the LSA. */ virtual string str() const = 0; /** * Add the LSA type bindings. */ // void install_type(LsaType type, Lsa *lsa); protected: Lsa_header _header; // Common LSA header. vector _pkt; // Raw LSA. private: const OspfTypes::Version _version; bool _valid; // True if this LSA is still valid. bool _self_originating; // True if this LSA is self originating. uint16_t _initial_age; // Age when this LSA was created. TimeVal _creation; // Time when this LSA was created. XorpTimer _timer; // If this is a self originated LSA // this timer is used to retransmit // the LSA, otherwise this timer fires // when MaxAge is reached. bool _transmitted; // Set to true when this LSA is transmitted. bool _trace; // True if this LSA should be traced. // List of neighbours that have not yet acknowledged this LSA. set _nack_list; /** * Set the age and update the stored packet if it exists. */ void set_ls_age(uint16_t ls_age); /** * If this is an OSPFv3 LSA and the flooding scope is Link-local * then the PeerID should be set to the peer that the LSA is * associated with. */ OspfTypes::PeerID _peerid; /** * OSPFv3 only is the U-bit set, i.e. should this LSA be processed * as if it is understood; obviously if we already know about it * it is understood. */ bool understood() const { XLOG_ASSERT(OspfTypes::V3 == get_version()); if (known()) return true; else return 0x8000 == (get_ls_type() & 0x8000); } }; /** * LSA byte streams are decoded through this class. */ class LsaDecoder { public: LsaDecoder(OspfTypes::Version version) : _version(version), _min_lsa_length(0), _unknown_lsa_decoder(0) {} ~LsaDecoder(); /** * Register the LSA decoders, called multiple times with each LSA. * * @param LSA decoder */ void register_decoder(Lsa *lsa); /** * Register the unknown LSA decoder, called once. * * @param LSA decoder */ void register_unknown_decoder(Lsa *lsa); /** * Decode an LSA. * * @param buf pointer to buffer. * @param len length of the buffer on input set to the number of * bytes consumed on output. * * @return A reference to an LSA that manages its own memory. */ Lsa::LsaRef decode(uint8_t *ptr, size_t& len) const throw(InvalidPacket); /** * @return The length of the smallest LSA that can be decoded. */ size_t min_length() const { return _min_lsa_length + Lsa_header::length(); } /** * Validate type field. * If we know how to decode an LSA of this type we must know how * to process it. * * @return true if we know about this type of LSA. */ bool validate(uint16_t type) const { if (0 != _unknown_lsa_decoder) return true; return _lsa_decoders.end() != _lsa_decoders.find(type); } /** * Is an LSA of this type an AS-external-LSA? * * @return true if this type is an AS-external-LSA */ bool external(uint16_t type) { map::iterator i = _lsa_decoders.find(type); XLOG_ASSERT(_lsa_decoders.end() != i); return i->second->external(); } /** * Return the name of this LSA. */ const char *name(uint16_t type) const { map::const_iterator i = _lsa_decoders.find(type); XLOG_ASSERT(_lsa_decoders.end() != i); return i->second->name(); } OspfTypes::Version get_version() const { return _version; } private: const OspfTypes::Version _version; size_t _min_lsa_length; // The smallest LSA that can be // decoded, excluding LSA header. map _lsa_decoders; // OSPF LSA decoders Lsa * _unknown_lsa_decoder; // OSPF Unknown LSA decoder }; /** * RFC 2470 A.4.1 IPv6 Prefix Representation * OSPFv3 only */ class IPv6Prefix { public: static const uint8_t NU_bit = 0x1; static const uint8_t LA_bit = 0x2; static const uint8_t MC_bit = 0x4; static const uint8_t P_bit = 0x8; static const uint8_t DN_bit = 0x10; IPv6Prefix(OspfTypes::Version version, bool use_metric = false) : _version(version), _use_metric(use_metric), _metric(0), _prefix_options(0) { } IPv6Prefix(const IPv6Prefix& rhs) : _version(rhs._version), _use_metric(rhs._use_metric) { copy(rhs); } IPv6Prefix operator=(const IPv6Prefix& rhs) { if(&rhs == this) return *this; copy(rhs); return *this; } #define ipv6prefix_copy(var) var = rhs.var; void copy(const IPv6Prefix& rhs) { ipv6prefix_copy(_network); ipv6prefix_copy(_metric); ipv6prefix_copy(_prefix_options); } #undef ipv6prefix_copy /** * @return the number of bytes the encoded data will occupy. */ size_t length() const; /** * Decode a IPv6Prefix. * * @param buf pointer to buffer. * @param len length of the buffer on input set to the number of * bytes consumed on output. * @param prefixlen prefix length * @param option prefix option * * @return A IPv6Prefix. */ IPv6Prefix decode(uint8_t *ptr, size_t& len, uint8_t prefixlen, uint8_t option) const throw(InvalidPacket); /** * Copy a wire format representation to the pointer provided. * * length() should be called by the caller to verify enough space * is available. * @return the number of bytes written. */ size_t copy_out(uint8_t *to_uint8) const; /** * @return Number of bytes that will be occupied by this prefix. */ static size_t bytes_per_prefix(uint8_t prefix) { return ((prefix + 31) / 32) * 4; } OspfTypes::Version get_version() const { return _version; } void set_network(const IPNet& network) { XLOG_ASSERT(OspfTypes::V3 == get_version()); _network = network; } IPNet get_network() const { XLOG_ASSERT(OspfTypes::V3 == get_version()); return _network; } bool use_metric() const { return _use_metric; } void set_metric(uint16_t metric) { XLOG_ASSERT(_use_metric); _metric = metric; } uint16_t get_metric() const { XLOG_ASSERT(_use_metric); return _metric; } void set_prefix_options(uint8_t prefix_options) { XLOG_ASSERT(OspfTypes::V3 == get_version()); _prefix_options = prefix_options; } uint8_t get_prefix_options() const { XLOG_ASSERT(OspfTypes::V3 == get_version()); return _prefix_options; } void set_bit(bool set, uint8_t bit) { XLOG_ASSERT(OspfTypes::V3 == get_version()); if (set) _prefix_options |= bit; else _prefix_options &= ~bit; } bool get_bit(uint8_t bit) const { XLOG_ASSERT(OspfTypes::V3 == get_version()); return _prefix_options & bit ? true : false; } void set_nu_bit(bool set) { set_bit(set, NU_bit); } bool get_nu_bit() const { return get_bit(NU_bit); } void set_la_bit(bool set) { set_bit(set, LA_bit); } bool get_la_bit() const { return get_bit(LA_bit); } void set_mc_bit(bool set) { set_bit(set, MC_bit); } bool get_mc_bit() const { return get_bit(MC_bit); } void set_p_bit(bool set) { set_bit(set, P_bit); } bool get_p_bit() const { return get_bit(P_bit); } void set_dn_bit(bool set) { set_bit(set, DN_bit); } bool get_dn_bit() const { return get_bit(DN_bit); } /** * Generate a printable representation. */ string str() const; private: const OspfTypes::Version _version; const bool _use_metric; IPNet _network; uint16_t _metric; uint8_t _prefix_options; }; /** * Defines a link/interface, carried in a RouterLsa. */ class RouterLink { public: enum Type { p2p = 1, // Point-to-point connection to another router transit = 2, // Connection to a transit network stub = 3, // Connection to a stub network OSPFv2 only vlink = 4 // Virtual link }; RouterLink(OspfTypes::Version version) : _version(version), _type(p2p), _metric(0), _link_id(0), _link_data(0), _interface_id(0), _neighbour_interface_id(0), _neighbour_router_id(0) {} RouterLink(const RouterLink& rhs) : _version(rhs._version) { copy(rhs); } RouterLink operator=(const RouterLink& rhs) { if(&rhs == this) return *this; copy(rhs); return *this; } #define routerlink_copy(var) var = rhs.var; void copy(const RouterLink& rhs) { routerlink_copy(_type); routerlink_copy(_metric); switch (get_version()) { case OspfTypes::V2: routerlink_copy(_link_id); routerlink_copy(_link_data); break; case OspfTypes::V3: routerlink_copy(_interface_id); routerlink_copy(_neighbour_interface_id); routerlink_copy(_neighbour_router_id); break; } } #undef routerlink_copy #define routerlink_compare(var) if (var != rhs.var) return false; bool operator==(const RouterLink& rhs) { routerlink_compare(_type); routerlink_compare(_metric); switch (get_version()) { case OspfTypes::V2: routerlink_compare(_link_id); routerlink_compare(_link_data); break; case OspfTypes::V3: routerlink_compare(_interface_id); routerlink_compare(_neighbour_interface_id); routerlink_compare(_neighbour_router_id); break; } return true; } #undef routerlink_compare /** * @return the number of bytes the encoded data will occupy. */ size_t length() const; /** * Decode a RouterLink. * * @param buf pointer to buffer. * @param len length of the buffer on input set to the number of * bytes consumed on output. * * @return A RouterLink. */ RouterLink decode(uint8_t *ptr, size_t& len) const throw(InvalidPacket); /** * Copy a wire format representation to the pointer provided. * * length() should be called by the caller to verify enough space * is available. * @return the number of bytes written. */ size_t copy_out(uint8_t *to_uint8) const; OspfTypes::Version get_version() const { return _version; } // Type void set_type(Type t) { if (stub == t) XLOG_ASSERT(OspfTypes::V2 == get_version()); _type = t; } Type get_type() const { return _type; } // Metric void set_metric(uint16_t metric) { _metric = metric; } uint16_t get_metric() const { return _metric; } // Link ID void set_link_id(uint32_t link_id) { XLOG_ASSERT(OspfTypes::V2 == get_version()); _link_id = link_id; } uint32_t get_link_id() const { XLOG_ASSERT(OspfTypes::V2 == get_version()); return _link_id; } // Link Data void set_link_data(uint32_t link_data) { XLOG_ASSERT(OspfTypes::V2 == get_version()); _link_data = link_data; } uint32_t get_link_data() const { XLOG_ASSERT(OspfTypes::V2 == get_version()); return _link_data; } // Interface ID void set_interface_id(uint32_t interface_id) { XLOG_ASSERT(OspfTypes::V3 == get_version()); _interface_id = interface_id; } uint32_t get_interface_id() const { XLOG_ASSERT(OspfTypes::V3 == get_version()); return _interface_id; } // Neighbour Interface ID void set_neighbour_interface_id(uint32_t neighbour_interface_id) { XLOG_ASSERT(OspfTypes::V3 == get_version()); _neighbour_interface_id = neighbour_interface_id; } uint32_t get_neighbour_interface_id() const { XLOG_ASSERT(OspfTypes::V3 == get_version()); return _neighbour_interface_id; } // Neighbour Router ID void set_neighbour_router_id(uint32_t neighbour_router_id) { XLOG_ASSERT(OspfTypes::V3 == get_version()); _neighbour_router_id = neighbour_router_id; } uint32_t get_neighbour_router_id() const { XLOG_ASSERT(OspfTypes::V3 == get_version()); return _neighbour_router_id; } /** * Generate a printable representation of the header. */ string str() const; private: const OspfTypes::Version _version; Type _type; uint16_t _metric; // Only store TOS 0 metric uint32_t _link_id; // OSPFv2 Only uint32_t _link_data; // OSPFv2 Only uint32_t _interface_id; // OSPFv3 Only uint32_t _neighbour_interface_id;// OSPFv3 Only uint32_t _neighbour_router_id; // OSPFv3 Only }; class UnknownLsa : public Lsa { public: UnknownLsa(OspfTypes::Version version) : Lsa(version) {} UnknownLsa(OspfTypes::Version version, uint8_t *buf, size_t len) : Lsa(version, buf, len) {} /** * @return the minimum length of a Router-LSA. */ size_t min_length() const { switch(get_version()) { case OspfTypes::V2: XLOG_FATAL("OSPFv3 only"); break; case OspfTypes::V3: return 0; break; } XLOG_UNREACHABLE(); return 0; } uint16_t get_ls_type() const { switch(get_version()) { case OspfTypes::V2: XLOG_FATAL("OSPFv3 only"); break; case OspfTypes::V3: return _header.get_ls_type(); break; } XLOG_UNREACHABLE(); return 0; } /** * OSPFv3 only is this a known LSA, of course not this is the * unknown LSA. * * @return false. */ bool known() const { XLOG_ASSERT(OspfTypes::V3 == get_version()); return false; } /** * Decode an LSA. * @param buf pointer to buffer. * @param len length of the buffer on input set to the number of * bytes consumed on output. * * @return A reference to an LSA that manages its own memory. */ LsaRef decode(uint8_t *buf, size_t& len) const throw(InvalidPacket); bool encode(); /** * Printable name of this LSA. */ const char *name() const { return "Unknown"; } /** * Generate a printable representation. */ string str() const; }; class RouterLsa : public Lsa { public: RouterLsa(OspfTypes::Version version) : Lsa(version), _nt_bit(false), _w_bit(false), _v_bit(false), _e_bit(false), _b_bit(false), _options(0) { _header.set_ls_type(get_ls_type()); } RouterLsa(OspfTypes::Version version, uint8_t *buf, size_t len) : Lsa(version, buf, len) {} /** * @return the minimum length of a Router-LSA. */ size_t min_length() const { switch(get_version()) { case OspfTypes::V2: return 4; break; case OspfTypes::V3: return 4; break; } XLOG_UNREACHABLE(); return 0; } uint16_t get_ls_type() const { switch(get_version()) { case OspfTypes::V2: return 1; break; case OspfTypes::V3: return 0x2001; break; } XLOG_UNREACHABLE(); return 0; } /** * Decode an LSA. * @param buf pointer to buffer. * @param len length of the buffer on input set to the number of * bytes consumed on output. * * @return A reference to an LSA that manages its own memory. */ LsaRef decode(uint8_t *buf, size_t& len) const throw(InvalidPacket); bool encode(); // NSSA translation void set_nt_bit(bool bit) { _nt_bit = bit; } bool get_nt_bit() const { return _nt_bit; } // Wildcard multicast receiver! OSPFv3 Only void set_w_bit(bool bit) { XLOG_ASSERT(OspfTypes::V3 == get_version()); _w_bit = bit; } bool get_w_bit() const { XLOG_ASSERT(OspfTypes::V3 == get_version()); return _w_bit; } // Virtual link endpoint void set_v_bit(bool bit) { _v_bit = bit; } bool get_v_bit() const { return _v_bit; } // AS boundary router (E for external) void set_e_bit(bool bit) { _e_bit = bit; } bool get_e_bit() const { return _e_bit; } // Area border router. void set_b_bit(bool bit) { _b_bit = bit; } bool get_b_bit() const { return _b_bit; } void set_options(uint32_t options) { XLOG_ASSERT(OspfTypes::V3 == get_version()); if (options > 0xffffff) XLOG_WARNING("Attempt to set %#x in a 24 bit field", options); _options = options & 0xffffff; } uint32_t get_options() const { XLOG_ASSERT(OspfTypes::V3 == get_version()); return _options; } list& get_router_links() { return _router_links; } /** * Printable name of this LSA. */ const char *name() const { return "Router"; } /** * Generate a printable representation. */ string str() const; private: bool _nt_bit; // NSSA Translation. bool _w_bit; // Wildcard multicast receiver! OSPFv3 Only bool _v_bit; // Virtual link endpoint bool _e_bit; // AS boundary router (E for external) bool _b_bit; // Area border router. uint32_t _options; // OSPFv3 only. list _router_links; }; class NetworkLsa : public Lsa { public: NetworkLsa(OspfTypes::Version version) : Lsa(version) { _header.set_ls_type(get_ls_type()); } NetworkLsa(OspfTypes::Version version, uint8_t *buf, size_t len) : Lsa(version, buf, len) {} /** * @return the minimum length of a Network-LSA. */ size_t min_length() const { switch(get_version()) { case OspfTypes::V2: return 8; break; case OspfTypes::V3: return 8; break; } XLOG_UNREACHABLE(); return 0; } uint16_t get_ls_type() const { switch(get_version()) { case OspfTypes::V2: return 2; break; case OspfTypes::V3: return 0x2002; break; } XLOG_UNREACHABLE(); return 0; } /** * Decode an LSA. * @param buf pointer to buffer. * @param len length of the buffer on input set to the number of * bytes consumed on output. * * @return A reference to an LSA that manages its own memory. */ LsaRef decode(uint8_t *buf, size_t& len) const throw(InvalidPacket); bool encode(); void set_options(uint32_t options) { XLOG_ASSERT(OspfTypes::V3 == get_version()); if (options > 0xffffff) XLOG_WARNING("Attempt to set %#x in a 24 bit field", options); _options = options & 0xffffff; } uint32_t get_options() const { XLOG_ASSERT(OspfTypes::V3 == get_version()); return _options; } void set_network_mask(uint32_t network_mask) { XLOG_ASSERT(OspfTypes::V2 == get_version()); _network_mask = network_mask; } uint32_t get_network_mask() const { XLOG_ASSERT(OspfTypes::V2 == get_version()); return _network_mask; } list& get_attached_routers() { return _attached_routers; } /** * Printable name of this LSA. */ const char *name() const { return "Network"; } /** * Generate a printable representation. */ string str() const; private: uint32_t _options; // OSPFv3 only. uint32_t _network_mask; // OSPFv2 only. list _attached_routers; }; /** * OSPFv2: Summary-LSA Type 3 * OSPFv3: Inter-Area-Prefix-LSA */ class SummaryNetworkLsa : public Lsa { public: static const size_t IPV6_PREFIX_OFFSET = 28; SummaryNetworkLsa(OspfTypes::Version version) : Lsa(version), _ipv6prefix(version) { _header.set_ls_type(get_ls_type()); } SummaryNetworkLsa(OspfTypes::Version version, uint8_t *buf, size_t len) : Lsa(version, buf, len), _ipv6prefix(version) {} /** * @return the minimum length of a SummaryNetworkLsa. */ size_t min_length() const { switch(get_version()) { case OspfTypes::V2: return 8; break; case OspfTypes::V3: return 8; break; } XLOG_UNREACHABLE(); return 0; } uint16_t get_ls_type() const { switch(get_version()) { case OspfTypes::V2: return 3; break; case OspfTypes::V3: return 0x2003; break; } XLOG_UNREACHABLE(); return 0; } /** * Decode an LSA. * @param buf pointer to buffer. * @param len length of the buffer on input set to the number of * bytes consumed on output. * * @return A reference to an LSA that manages its own memory. */ LsaRef decode(uint8_t *buf, size_t& len) const throw(InvalidPacket); bool encode(); void set_metric(uint32_t metric) { _metric = metric; } uint32_t get_metric() const { return _metric; } void set_network_mask(uint32_t network_mask) { XLOG_ASSERT(OspfTypes::V2 == get_version()); _network_mask = network_mask; } uint32_t get_network_mask() const { XLOG_ASSERT(OspfTypes::V2 == get_version()); return _network_mask; } void set_ipv6prefix(const IPv6Prefix& ipv6prefix) { XLOG_ASSERT(OspfTypes::V3 == get_version()); _ipv6prefix = ipv6prefix; } IPv6Prefix get_ipv6prefix() const { XLOG_ASSERT(OspfTypes::V3 == get_version()); return _ipv6prefix;; } /** * Printable name of this LSA. */ const char *name() const { return "SummaryN"; } /** * Generate a printable representation. */ string str() const; private: uint32_t _metric; uint32_t _network_mask; // OSPFv2 only. IPv6Prefix _ipv6prefix; // OSPFv3 only. }; /** * OSPFv2: Summary-LSA Type 4 * OSPFv3: Inter-Area-Router-LSA */ class SummaryRouterLsa : public Lsa { public: SummaryRouterLsa(OspfTypes::Version version) : Lsa(version) { _header.set_ls_type(get_ls_type()); } SummaryRouterLsa(OspfTypes::Version version, uint8_t *buf, size_t len) : Lsa(version, buf, len) {} /** * @return the minimum length of a RouterLSA. */ size_t min_length() const { switch(get_version()) { case OspfTypes::V2: return 8; break; case OspfTypes::V3: return 12; break; } XLOG_UNREACHABLE(); return 0; } uint16_t get_ls_type() const { switch(get_version()) { case OspfTypes::V2: return 4; break; case OspfTypes::V3: return 0x2004; break; } XLOG_UNREACHABLE(); return 0; } /** * Decode an LSA. * @param buf pointer to buffer. * @param len length of the buffer on input set to the number of * bytes consumed on output. * * @return A reference to an LSA that manages its own memory. */ LsaRef decode(uint8_t *buf, size_t& len) const throw(InvalidPacket); bool encode(); void set_options(uint32_t options) { XLOG_ASSERT(OspfTypes::V3 == get_version()); if (options > 0xffffff) XLOG_WARNING("Attempt to set %#x in a 24 bit field", options); _options = options & 0xffffff; } uint32_t get_options() const { XLOG_ASSERT(OspfTypes::V3 == get_version()); return _options; } void set_metric(uint32_t metric) { _metric = metric; } uint32_t get_metric() const { return _metric; } void set_network_mask(uint32_t network_mask) { XLOG_ASSERT(OspfTypes::V2 == get_version()); _network_mask = network_mask; } uint32_t get_network_mask() const { XLOG_ASSERT(OspfTypes::V2 == get_version()); return _network_mask; } void set_destination_id(OspfTypes::RouterID destination_id) { XLOG_ASSERT(OspfTypes::V3 == get_version()); _destination_id = destination_id; } OspfTypes::RouterID get_destination_id() const { XLOG_ASSERT(OspfTypes::V3 == get_version()); return _destination_id; } /** * Printable name of this LSA. */ const char *name() const { return "SummaryR"; } /** * Generate a printable representation. */ string str() const; private: uint32_t _metric; uint32_t _network_mask; // OSPFv2 only. uint8_t _options; // OSPFv3 only. OspfTypes::RouterID _destination_id;// OSPFv3 only. }; /** * AS-external-LSA */ class ASExternalLsa : public Lsa { public: static const size_t IPV6_PREFIX_OFFSET = 28; ASExternalLsa(OspfTypes::Version version) : Lsa(version), _network_mask(0), _e_bit(false), _f_bit(false), _t_bit(false), _ipv6prefix(version), _referenced_ls_type(0), _metric(0), _external_route_tag(0), _referenced_link_state_id(0) { _header.set_ls_type(get_ls_type()); } ASExternalLsa(OspfTypes::Version version, uint8_t *buf, size_t len) : Lsa(version, buf, len), _ipv6prefix(version) {} /** * @return the minimum length of an AS-external-LSA. */ size_t min_length() const { switch(get_version()) { case OspfTypes::V2: return 16; break; case OspfTypes::V3: return 8; break; } XLOG_UNREACHABLE(); return 0; } uint16_t get_ls_type() const { switch(get_version()) { case OspfTypes::V2: return 5; break; case OspfTypes::V3: return 0x4005; break; } XLOG_UNREACHABLE(); return 0; } /** * @return True this is an AS-external-LSA. */ bool external() const {return true; }; /** * Decode an LSA. * @param buf pointer to buffer. * @param len length of the buffer on input set to the number of * bytes consumed on output. * * @return A reference to an LSA that manages its own memory. */ LsaRef decode(uint8_t *buf, size_t& len) const throw(InvalidPacket); bool encode(); void set_network_mask(uint32_t network_mask) { XLOG_ASSERT(OspfTypes::V2 == get_version()); _network_mask = network_mask; } uint32_t get_network_mask() const { XLOG_ASSERT(OspfTypes::V2 == get_version()); return _network_mask; } void set_ipv6prefix(const IPv6Prefix& ipv6prefix) { XLOG_ASSERT(OspfTypes::V3 == get_version()); _ipv6prefix = ipv6prefix; } IPv6Prefix get_ipv6prefix() const { XLOG_ASSERT(OspfTypes::V3 == get_version()); return _ipv6prefix;; } template void set_network(IPNet); template IPNet get_network(A) const; void set_e_bit(bool bit) { _e_bit = bit; } bool get_e_bit() const { return _e_bit; } void set_f_bit(bool bit) { XLOG_ASSERT(OspfTypes::V3 == get_version()); _f_bit = bit; } bool get_f_bit() const { XLOG_ASSERT(OspfTypes::V3 == get_version()); return _f_bit; } void set_t_bit(bool bit) { XLOG_ASSERT(OspfTypes::V3 == get_version()); _t_bit = bit; } bool get_t_bit() const { XLOG_ASSERT(OspfTypes::V3 == get_version()); return _t_bit; } void set_referenced_ls_type(uint16_t referenced_ls_type) { XLOG_ASSERT(OspfTypes::V3 == get_version()); _referenced_ls_type = referenced_ls_type; } uint16_t get_referenced_ls_type() const { XLOG_ASSERT(OspfTypes::V3 == get_version()); return _referenced_ls_type; } void set_forwarding_address_ipv6(IPv6 forwarding_address_ipv6) { XLOG_ASSERT(OspfTypes::V3 == get_version()); XLOG_ASSERT(_f_bit); _forwarding_address_ipv6 = forwarding_address_ipv6; } IPv6 get_forwarding_address_ipv6() const { XLOG_ASSERT(OspfTypes::V3 == get_version()); XLOG_ASSERT(_f_bit); return _forwarding_address_ipv6; } void set_metric(uint32_t metric) { _metric = metric; } uint32_t get_metric() const { return _metric; } void set_forwarding_address_ipv4(IPv4 forwarding_address_ipv4) { XLOG_ASSERT(OspfTypes::V2 == get_version()); _forwarding_address_ipv4 = forwarding_address_ipv4; } IPv4 get_forwarding_address_ipv4() const { XLOG_ASSERT(OspfTypes::V2 == get_version()); return _forwarding_address_ipv4; } template void set_forwarding_address(A); template A get_forwarding_address(A) const; void set_external_route_tag(uint32_t external_route_tag) { switch(get_version()) { case OspfTypes::V2: break; case OspfTypes::V3: XLOG_ASSERT(_t_bit); break; } _external_route_tag = external_route_tag; } uint32_t get_external_route_tag() const { switch(get_version()) { case OspfTypes::V2: break; case OspfTypes::V3: XLOG_ASSERT(_t_bit); break; } return _external_route_tag; } void set_referenced_link_state_id(uint32_t referenced_link_state_id) { XLOG_ASSERT(OspfTypes::V3 == get_version()); if (0 == _referenced_ls_type) XLOG_WARNING("Referenced LS Type is zero"); _referenced_link_state_id = referenced_link_state_id; } uint32_t get_referenced_link_state_id() const { XLOG_ASSERT(OspfTypes::V3 == get_version()); if (0 == _referenced_ls_type) XLOG_WARNING("Referenced LS Type is zero"); return _referenced_link_state_id; } /** * Create a new instance of this LSA, allows the decode routine to * call either this or the Type7 donew. */ virtual ASExternalLsa *donew(OspfTypes::Version version, uint8_t *buf, size_t len) const { return new ASExternalLsa(version, buf, len); } /** * Name used in the str() method. */ virtual string str_name() const { return "As-External-LSA"; } /** * Printable name of this LSA. */ const char *name() const { return get_e_bit() ? "ASExt-2" : "ASExt-1"; } void set_suppressed_lsa(Lsa::LsaRef lsar) { _suppressed_lsa = lsar; } Lsa::LsaRef get_suppressed_lsa() const { return _suppressed_lsa; } void release_suppressed_lsa() { _suppressed_lsa.release(); } /** * Generate a printable representation. */ string str() const; private: uint32_t _network_mask; // OSPFv2 only. bool _e_bit; bool _f_bit; // OSPFv3 only. bool _t_bit; // OSPFv3 only. IPv6Prefix _ipv6prefix; // OSPFv3 only. uint16_t _referenced_ls_type; // OSPFv3 only. IPv6 _forwarding_address; // OSPFv3 only. uint32_t _metric; IPv4 _forwarding_address_ipv4; // OSPFv2 only. IPv6 _forwarding_address_ipv6; // OSPFv3 only. uint32_t _external_route_tag; uint32_t _referenced_link_state_id; // OSPFv3 only. Lsa::LsaRef _suppressed_lsa; // If a self originated LSA is // being suppressed here it is. }; /** * Type-7 LSA used to convey external routing information in NSSAs. */ class Type7Lsa : public ASExternalLsa { public: Type7Lsa(OspfTypes::Version version) : ASExternalLsa(version) { _header.set_ls_type(get_ls_type()); } Type7Lsa(OspfTypes::Version version, uint8_t *buf, size_t len) : ASExternalLsa(version, buf, len) {} uint16_t get_ls_type() const { switch(get_version()) { case OspfTypes::V2: return 7; break; case OspfTypes::V3: return 0x2007; break; } XLOG_UNREACHABLE(); return 0; } /** * @return True this is an AS-external-LSA. */ bool external() const {return false; }; /** * @return True if this LSA is a Type-7-LSA. */ bool type7() const { return true; } virtual ASExternalLsa *donew(OspfTypes::Version version, uint8_t *buf, size_t len) const { return new Type7Lsa(version, buf, len); } /** * Name used in the str() method. */ string str_name() const { return "Type-7-LSA"; } /** * Printable name of this LSA. */ const char *name() const { return get_e_bit() ? "Type7-2" : "Type7-1"; } }; /** * OSPFv3 only: Link-LSA */ class LinkLsa : public Lsa { public: LinkLsa(OspfTypes::Version version) : Lsa(version) { XLOG_ASSERT(OspfTypes::V3 == get_version()); _header.set_ls_type(get_ls_type()); } LinkLsa(OspfTypes::Version version, uint8_t *buf, size_t len) : Lsa(version, buf, len) { XLOG_ASSERT(OspfTypes::V3 == get_version()); } /** * @return the minimum length of a Link-LSA. */ size_t min_length() const { return 24; } uint16_t get_ls_type() const { return 8; } /** * Decode an LSA. * @param buf pointer to buffer. * @param len length of the buffer on input set to the number of * bytes consumed on output. * * @return A reference to an LSA that manages its own memory. */ LsaRef decode(uint8_t *buf, size_t& len) const throw(InvalidPacket); bool encode(); void set_rtr_priority(uint8_t rtr_priority) { _rtr_priority = rtr_priority; } uint8_t get_rtr_priority() const { return _rtr_priority; } void set_options(uint32_t options) { if (options > 0xffffff) XLOG_WARNING("Attempt to set %#x in a 24 bit field", options); _options = options & 0xffffff; } uint32_t get_options() const { return _options; } void set_link_local_address(IPv6 link_local_address) { _link_local_address = link_local_address; } IPv6 get_link_local_address() const { return _link_local_address; } const list& get_prefixes() const { return _prefixes; } list& get_prefixes() { return _prefixes; } /** * Printable name of this LSA. */ const char *name() const { return "Link"; } /** * Generate a printable representation. */ string str() const; private: uint8_t _rtr_priority; uint32_t _options; IPv6 _link_local_address; list _prefixes; }; /** * OSPFv3 only: Intra-Area-Prefix-LSA */ class IntraAreaPrefixLsa : public Lsa { public: IntraAreaPrefixLsa(OspfTypes::Version version) : Lsa(version) { XLOG_ASSERT(OspfTypes::V3 == get_version()); _header.set_ls_type(get_ls_type()); } IntraAreaPrefixLsa(OspfTypes::Version version, uint8_t *buf, size_t len) : Lsa(version, buf, len) { XLOG_ASSERT(OspfTypes::V3 == get_version()); } /** * @return the minimum length of a Link-LSA. */ size_t min_length() const { return 12; } uint16_t get_ls_type() const { return 0x2009; } /** * Decode an LSA. * @param buf pointer to buffer. * @param len length of the buffer on input set to the number of * bytes consumed on output. * * @return A reference to an LSA that manages its own memory. */ LsaRef decode(uint8_t *buf, size_t& len) const throw(InvalidPacket); bool encode(); void set_referenced_ls_type(uint16_t referenced_ls_type) { _referenced_ls_type = referenced_ls_type; } uint16_t get_referenced_ls_type() const { return _referenced_ls_type; } void set_referenced_link_state_id(uint32_t referenced_link_state_id) { _referenced_link_state_id = referenced_link_state_id; } uint32_t get_referenced_link_state_id() const { return _referenced_link_state_id; } void set_referenced_advertising_router(uint32_t referenced_adv_router) { _referenced_advertising_router = referenced_adv_router; } uint32_t get_referenced_advertising_router() const { return _referenced_advertising_router; } list& get_prefixes() { return _prefixes; } /** * Given a referenced LS type and an interface ID generate a * candidate Link State ID for Intra-Area-Prefix-LSAs. This is * *NOT* part of the protocol, just a way to create a unique * mapping. * * The underlying assumption is that for every area only one * Intra-Area-Prefix-LSA will be generated per Router-LSA and * Network-LSA. More importantly one Router-LSA will be generated * per area although it is legal to generate many. The size of the * Router-LSA is a function of the number of interfaces on the * generating router and for the time being we only generate one * Router-LSA. If the number of interfaces exceeds the capacity of * a single Router-LSA then this method and the Router-LSA code * will need to be re-visited. */ uint32_t create_link_state_id(uint16_t ls_type, uint32_t interface_id) const { XLOG_ASSERT(OspfTypes::V3 == get_version()); if (RouterLsa(get_version()).get_ls_type() == ls_type) { return OspfTypes::UNUSED_INTERFACE_ID; } else if (NetworkLsa(get_version()).get_ls_type() == ls_type) { return interface_id; } else { XLOG_FATAL("Unknown LS Type %#x\n", ls_type); } return 0; } /** * Printable name of this LSA. */ const char *name() const { return "IntraArPfx"; } /** * Generate a printable representation. */ string str() const; private: uint16_t _referenced_ls_type; uint32_t _referenced_link_state_id; uint32_t _referenced_advertising_router; list _prefixes; }; #if 0 class LsaTransmit : class Transmit { public: bool valid(); bool multiple() { return false;} Transmit *clone(); uint8_t *generate(size_t &len); private: LsaRef _lsaref; // LSA. } #endif /** * Link State Request as sent in a Link State Request Packet. * Never store or pass a pointer, just deal with it inline. */ class Ls_request { public: Ls_request(OspfTypes::Version version) : _version(version), _ls_type(0),_link_state_id(0), _advertising_router(0) {} Ls_request(OspfTypes::Version version, uint32_t ls_type, uint32_t link_state_id, uint32_t advertising_router) : _version(version), _ls_type(ls_type),_link_state_id(link_state_id), _advertising_router(advertising_router) {} Ls_request(const Ls_request& rhs) { copy(rhs); } Ls_request operator=(const Ls_request& rhs) { if(&rhs == this) return *this; copy(rhs); return *this; } #define ls_copy(var) var = rhs.var; void copy(const Ls_request& rhs) { ls_copy(_version); ls_copy(_ls_type); ls_copy(_link_state_id); ls_copy(_advertising_router); } #undef ls_copy /** * @return the length of an link state request header. */ static size_t length() { return 12; } /** * Decode a Link State Request and return value inline not a pointer. */ Ls_request decode(uint8_t *ptr) throw(InvalidPacket); /** * Copy a wire format representation to the pointer provided. * @return the number of bytes written. */ size_t copy_out(uint8_t *to_uint8) const; OspfTypes::Version get_version() const { return _version; } // LS type void set_ls_type(uint32_t ls_type) { switch(get_version()) { case OspfTypes::V2: _ls_type = ls_type; break; case OspfTypes::V3: if (ls_type > 0xffff) XLOG_WARNING("Attempt to set %#x in an 16 bit field", ls_type); _ls_type = ls_type & 0xffff; break; } } uint32_t get_ls_type() const { return _ls_type; } // Link State ID void set_link_state_id(uint32_t link_state_id) { _link_state_id = link_state_id; } uint32_t get_link_state_id() const { return _link_state_id; } // Advertising Router void set_advertising_router(uint32_t advertising_router) { _advertising_router = advertising_router; } uint32_t get_advertising_router() const { return _advertising_router; } /** * Generate a printable representation of the request. */ string str() const; private: OspfTypes::Version _version; uint32_t _ls_type; // OSPFv2 4 bytes, OSPFv3 2 bytes. uint32_t _link_state_id; uint32_t _advertising_router; }; /** * The definitive list of LSAs. All decoder lists should be primed * using this function. */ inline void initialise_lsa_decoder(OspfTypes::Version version, LsaDecoder& lsa_decoder) { switch(version) { case OspfTypes::V2: break; case OspfTypes::V3: lsa_decoder.register_unknown_decoder(new UnknownLsa(version)); break; } lsa_decoder.register_decoder(new RouterLsa(version)); lsa_decoder.register_decoder(new NetworkLsa(version)); lsa_decoder.register_decoder(new SummaryNetworkLsa(version)); lsa_decoder.register_decoder(new SummaryRouterLsa(version)); lsa_decoder.register_decoder(new ASExternalLsa(version)); lsa_decoder.register_decoder(new Type7Lsa(version)); switch(version) { case OspfTypes::V2: break; case OspfTypes::V3: lsa_decoder.register_decoder(new LinkLsa(version)); lsa_decoder.register_decoder(new IntraAreaPrefixLsa(version)); break; } } /** * Given an address and a mask generate an IPNet both of the values * are in host order. */ inline IPNet lsa_to_net(uint32_t lsid, uint32_t mask) { IPv4 prefix = IPv4(htonl(mask)); IPNet net = IPNet(IPv4(htonl(lsid)), prefix.mask_len()); return net; } /** * Given a link state ID and a mask both in host order return a link * state ID with the host bits set. */ inline uint32_t set_host_bits(uint32_t lsid, uint32_t mask) { return lsid | ~mask; } #endif // __OSPF_LSA_HH__ xorp/ospf/SConscript0000664000076400007640000000742511631505706014655 0ustar greearbgreearb# Copyright (c) 2009-2011 XORP, Inc and Others # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # $XORP$ import os Import('env') subdirs = [ 'tests', 'tools', ] SConscript(dirs = subdirs, exports='env') env = env.Clone() is_shared = env.has_key('SHAREDLIBS') env.AppendUnique(CPPPATH = [ '#', '$BUILDDIR', ]) env.PrependUnique(LIBPATH = [ '$BUILDDIR/libxorp', '$BUILDDIR/libcomm', '$BUILDDIR/libxipc', '$BUILDDIR/libproto', '$BUILDDIR/libfeaclient', '$BUILDDIR/policy/backend', '$BUILDDIR/policy/common', '$BUILDDIR/xrl/interfaces', '$BUILDDIR/xrl/targets', '$BUILDDIR/mrt', '.' ]) env.AppendUnique(LIBS = [ 'xorp_ospf', # Refers to the library, not the executable. 'xst_ospfv2', 'xorp_policy_backend', 'xorp_policy_common', 'xorp_fea_client', 'xif_rib', 'xif_finder_event_notifier', 'xif_fea_ifmgr_mirror', 'xif_fea_ifmgr_replicator', 'xif_fea_rawpkt4', 'xst_fea_ifmgr_mirror', 'xorp_ipc', 'xorp_core', 'xorp_proto', 'xorp_comm', ]) if not (env.has_key('disable_ipv6') and env['disable_ipv6']): env.AppendUnique(LIBS = [ 'xif_fea_rawpkt6', 'xst_ospfv3', ]) env.Replace(RPATH = [ env.Literal(env['xorp_module_rpath']) ]) libxorp_ospf_srcs = [ 'auth.cc', 'area_router.cc', 'external.cc', 'fletcher_checksum.cc', 'lsa.cc', 'ospf.cc', 'packet.cc', 'peer_manager.cc', 'peer.cc', 'policy_varrw.cc', 'routing_table.cc', 'xrl_io.cc', 'xrl_target.cc', 'vlink.cc' ] if not (env.has_key('disable_ipv6') and env['disable_ipv6']): libxorp_ospf_srcs.append('xrl_target3.cc') if not (env.has_key('disable_profile') and env['disable_profile']): env.AppendUnique(LIBS = [ 'xif_profile_client', ]) if (env.has_key('mingw') and env['mingw']): env.Append(LIBS = [ 'ws2_32', 'iphlpapi', # 'mprapi', 'regex', 'winmm', ]) env.Append(LIBS = ['xorp_core', 'crypto', 'ws2_32']) if is_shared: libxorp_ospf = env.SharedLibrary(target = 'libxorp_ospf', source = libxorp_ospf_srcs, LIBS = '') if env['rtld_origin']: for obj in libxorp_ospf: env.AddPostAction(libxorp_ospf, env.Symlink(obj.abspath, os.path.join(env['xorp_alias_libdir'], str(obj)))) else: libxorp_ospf = env.StaticLibrary(target = 'libxorp_ospf', source = libxorp_ospf_srcs, LIBS = '') ospfv2srcs = [ 'xorp_ospfv2.cc', ] ospfv2 = env.Program(target = 'xorp_ospfv2', source = ospfv2srcs) env.Alias('install', env.InstallProgram(env['xorp_moduledir'], ospfv2)) if is_shared: env.Alias('install', env.InstallLibrary(env['xorp_libdir'], libxorp_ospf)) if not (env.has_key('disable_ipv6') and env['disable_ipv6']): ospfv3srcs = [ 'xorp_ospfv3.cc' ] ospfv3 = env.Program(target = 'xorp_ospfv3', source = ospfv3srcs) env.Alias('install', env.InstallProgram(env['xorp_moduledir'], ospfv3)) Default(ospfv2, ospfv3) else: Default(ospfv2) xorp/ospf/packet.hh0000664000076400007640000005307011635757530014440 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/ospf/packet.hh,v 1.44 2008/10/02 21:57:48 bms Exp $ #ifndef __OSPF_PACKET_HH__ #define __OSPF_PACKET_HH__ /** * All packet decode routines must inherit from this interface. * Also provides some utility routines to perform common packet processing. */ class Packet { public: static const size_t STANDARD_HEADER_V2 = 24; static const size_t STANDARD_HEADER_V3 = 16; static const size_t VERSION_OFFSET = 0; static const size_t TYPE_OFFSET = 1; static const size_t LEN_OFFSET = 2; static const size_t ROUTER_ID_OFFSET = 4; static const size_t AREA_ID_OFFSET = 8; static const size_t CHECKSUM_OFFSET = 12; // OSPFv2 only. static const size_t AUTH_TYPE_OFFSET = 14; static const size_t AUTH_PAYLOAD_OFFSET = 16; static const size_t AUTH_PAYLOAD_SIZE = 8; // OSPFv3 only. static const size_t INSTANCE_ID_OFFSET = 14; Packet(OspfTypes::Version version) : _version(version), _valid(false), _router_id(0), _area_id(0), _auth_type(0), _instance_id(0) { memset(&_auth[0], 0, sizeof(_auth)); } virtual ~Packet() {} /** * Decode standard header. * * Used by the derived classes to extract the standard header and * options field. * * @param len if the frame is larger than what is specified by the * header length field drop the length down to the header length. * * @return the offset where the specific header starts. */ size_t decode_standard_header(uint8_t *ptr, size_t& len) throw(InvalidPacket); /** * Decode the packet. * The returned packet must be free'd. */ virtual Packet *decode(uint8_t *ptr, size_t len) const throw(InvalidPacket) = 0; /** * Encode standard header. * * Used by the derived classes to put the standard header in the * packet. Which includes the checksum so all other fields should * already be in the packet. * * @param ptr location to start writing header. * @param len size of the buffer. * @return The offset that the header has been written to. Returns * 0 on failure, such as the buffer is too small. */ size_t encode_standard_header(uint8_t *ptr, size_t len); /** * Encode the packet. * * @param pkt vector into which the packet should be placed. * @return true if the encoding suceeded. */ virtual bool encode(vector& pkt) = 0; /** * Store the original packet, required for authentication. */ void store(uint8_t *ptr, size_t len) { _pkt.resize(len); memcpy(&_pkt[0], ptr, len); } /** * Get a reference to the original packet data, required for * authentication. */ vector& get() { return _pkt; } /** * @return The version this packet represents. */ OspfTypes::Version get_version() const { return _version; } /** * It is the responsibilty of the derived type to return this * information. * @return The type this packet represents. */ virtual OspfTypes::Type get_type() const = 0; /** * Get the Router ID. */ OspfTypes::RouterID get_router_id() const { return _router_id; } /** * Set the Router ID. */ void set_router_id(OspfTypes::RouterID id) { _router_id = id; } /** * Get the Area ID. */ OspfTypes::AreaID get_area_id() const { return _area_id; } /** * Set the Area ID. */ void set_area_id(OspfTypes::AreaID id) { _area_id = id; } /** * Get the Auth Type. */ uint16_t get_auth_type() const { return _auth_type; } /** * Set the Auth Type. */ void set_auth_type(uint16_t auth_type) { _auth_type = auth_type; } /** * Get the Instance ID. */ uint8_t get_instance_id() const { XLOG_ASSERT(OspfTypes::V3 == get_version()); return _instance_id; } /** * Set the Instance ID. */ void set_instance_id(uint8_t instance_id) { XLOG_ASSERT(OspfTypes::V3 == get_version()); _instance_id = instance_id; } /** * @return the standard header length for this version of OSPF. */ size_t get_standard_header_length() { switch(_version) { case OspfTypes::V2: return STANDARD_HEADER_V2; break; case OspfTypes::V3: return STANDARD_HEADER_V3; break; } XLOG_UNREACHABLE(); return 0; } /** * Decode Point. * * If a packet is being decoded the standard header has already been * processed. This method returns the offset at which the specific * data starts. * * @return The offset at which a derived class should start decoding. */ size_t decode_point() const; /** * Generate a printable representation of the standard header. * Used by the derived classes implementing str(). */ string standard() const; /** * Generate a printable representation of the packet. */ virtual string str() const = 0; private: const OspfTypes::Version _version; vector _pkt; // Raw packet /** * Set to true when the standard header fields are set. */ bool _valid; /** * Standard header. * Version and Type information are constant so therefore not present. */ OspfTypes::RouterID _router_id; OspfTypes::AreaID _area_id; OspfTypes::AuType _auth_type; // OSPFv2 Only uint8_t _auth[AUTH_PAYLOAD_SIZE]; // OSPFv2 Only uint8_t _instance_id; // OSPFv3 Only }; /** * Packet byte streams are decoded through this class. */ class PacketDecoder { public: ~PacketDecoder(); /** * Register the packet/decode routines * * @param packet decoder */ void register_decoder(Packet *packet); #if 0 /** * Register the packet/decode routines * * @param packet decoder * @param version OSPF version of the decoder * @param type of decoder */ void register_decoder(Packet *packet, OspfTypes::Version version, OspfTypes::Type type); #endif /** * Decode byte stream. * * @param ptr to data packet * @param length of data packet * * @return a packet structure, which must be free'd */ Packet *decode(uint8_t *ptr, size_t len) throw(InvalidPacket); private: map _ospfv2; // OSPFv2 Packet decoders map _ospfv3; // OSPFv3 Packet decoders }; /** * Hello packet */ class HelloPacket : public Packet { public: static const size_t MINIMUM_LENGTH = 20; // The minumum length // of a hello packet. // The same for OSPFv2 // and OSPFv3. How did // that happen? static const size_t DESIGNATED_ROUTER_OFFSET = 12; static const size_t BACKUP_DESIGNATED_ROUTER_OFFSET = 16; // OSPFv2 static const size_t NETWORK_MASK_OFFSET = 0; static const size_t HELLO_INTERVAL_V2_OFFSET = 4; static const size_t OPTIONS_V2_OFFSET = 6; static const size_t ROUTER_PRIORITY_V2_OFFSET = 7; static const size_t ROUTER_DEAD_INTERVAL_V2_OFFSET = 8; // OSPFv3 static const size_t INTERFACE_ID_OFFSET = 0; static const size_t ROUTER_PRIORITY_V3_OFFSET = 4; static const size_t OPTIONS_V3_OFFSET = 4; static const size_t HELLO_INTERVAL_V3_OFFSET = 8; static const size_t ROUTER_DEAD_INTERVAL_V3_OFFSET = 10; HelloPacket(OspfTypes::Version version) : Packet(version), _network_mask(0), _interface_id(0), _hello_interval(0), _options(0), _router_priority(0), _router_dead_interval(0) {} OspfTypes::Type get_type() const { return 1; } Packet *decode(uint8_t *ptr, size_t len) const throw(InvalidPacket); /** * Encode the packet. * * @param pkt vector into which the packet should be placed. * @return true if the encoding succeeded. */ bool encode(vector& pkt); // Network Mask. void set_network_mask(uint32_t network_mask) { XLOG_ASSERT(OspfTypes::V2 == get_version()); _network_mask = network_mask; } uint32_t get_network_mask() const { XLOG_ASSERT(OspfTypes::V2 == get_version()); return _network_mask; } // Interface ID. void set_interface_id(uint32_t interface_id) { XLOG_ASSERT(OspfTypes::V3 == get_version()); _interface_id = interface_id; } uint32_t get_interface_id() const { XLOG_ASSERT(OspfTypes::V3 == get_version()); return _interface_id; } // Hello Interval. void set_hello_interval(uint16_t hello_interval) { _hello_interval = hello_interval; } uint16_t get_hello_interval() const { return _hello_interval; } // Options. void set_options(uint32_t options) { switch(get_version()) { case OspfTypes::V2: if (options > 0xff) XLOG_WARNING("Attempt to set %#x in an 8 bit field", options); _options = options & 0xff; break; case OspfTypes::V3: if (options > 0xffffff) XLOG_WARNING("Attempt to set %#x in a 24 bit field", options); _options = options & 0xffffff; break; } } uint32_t get_options() const { return _options; } // Router Priority. void set_router_priority(uint8_t router_priority) { _router_priority = router_priority; } uint8_t get_router_priority() const { return _router_priority; } // Router Dead Interval. void set_router_dead_interval(uint32_t router_dead_interval) { switch(get_version()) { case OspfTypes::V2: _router_dead_interval = router_dead_interval; break; case OspfTypes::V3: if ( router_dead_interval > 0xffff) XLOG_WARNING("Attempt to set %#x in a 16 bit field", router_dead_interval); _router_dead_interval = router_dead_interval & 0xffff; break; } } uint32_t get_router_dead_interval() const { return _router_dead_interval; } // Designated Router void set_designated_router(OspfTypes::RouterID dr) { _dr = dr; } OspfTypes::RouterID get_designated_router() const { return _dr; } // Backup Designated Router void set_backup_designated_router(OspfTypes::RouterID bdr) { _bdr = bdr; } OspfTypes::RouterID get_backup_designated_router() const { return _bdr; } list& get_neighbours() { return _neighbours; } /** * Generate a printable representation of the packet. */ string str() const; private: uint32_t _network_mask; // OSPF V2 Only uint32_t _interface_id; // OSPF V3 Only uint16_t _hello_interval; uint32_t _options; // Large enough to accomodate V2 and V3 options uint8_t _router_priority; // Router Priority. uint32_t _router_dead_interval; // In seconds. OspfTypes::RouterID _dr; // Designated router. OspfTypes::RouterID _bdr; // Backup Designated router. list _neighbours; // Router IDs of neighbours. }; /** * Database Description Packet */ class DataDescriptionPacket : public Packet { public: DataDescriptionPacket(OspfTypes::Version version) : Packet(version), _interface_mtu(0), _options(0), _i_bit(false), _m_bit(false), _ms_bit(false), _DD_seqno(0) {} OspfTypes::Type get_type() const { return 2; } size_t minimum_length() const { switch(get_version()) { case OspfTypes::V2: return 8; break; case OspfTypes::V3: return 12; break; } XLOG_UNREACHABLE(); return 0; } Packet *decode(uint8_t *ptr, size_t len) const throw(InvalidPacket); /** * Encode the packet. * * @param pkt vector into which the packet should be placed. * @return true if the encoding suceeded. */ bool encode(vector& pkt); // Interface MTU void set_interface_mtu(uint16_t mtu) { _interface_mtu = mtu; } uint16_t get_interface_mtu() const { return _interface_mtu; } // Options. void set_options(uint32_t options) { switch(get_version()) { case OspfTypes::V2: if (options > 0xff) XLOG_WARNING("Attempt to set %#x in an 8 bit field", options); _options = options & 0xff; break; case OspfTypes::V3: if (options > 0xffffff) XLOG_WARNING("Attempt to set %#x in a 24 bit field", options); _options = options & 0xffffff; break; } } uint32_t get_options() const { return _options; } // Init bit. void set_i_bit(bool bit) { _i_bit = bit; } bool get_i_bit() const { return _i_bit; } // More bit. void set_m_bit(bool bit) { _m_bit = bit; } bool get_m_bit() const { return _m_bit; } // Master/Slave bit void set_ms_bit(bool bit) { _ms_bit = bit; } bool get_ms_bit() const { return _ms_bit; } // Database description sequence number. void set_dd_seqno(uint32_t seqno) { _DD_seqno = seqno; } uint32_t get_dd_seqno() const { return _DD_seqno; } list& get_lsa_headers() { return _lsa_headers; } /** * Generate a printable representation of the packet. */ string str() const; private: uint16_t _interface_mtu; uint32_t _options; // Large enough to accomodate V2 and V3 options bool _i_bit; // The init bit. bool _m_bit; // The more bit. bool _ms_bit; // The Master/Slave bit. uint32_t _DD_seqno; // Database description sequence number. list _lsa_headers; }; /** * Link State Request Packet */ class LinkStateRequestPacket : public Packet { public: LinkStateRequestPacket(OspfTypes::Version version) : Packet(version) {} OspfTypes::Type get_type() const { return 3; } Packet *decode(uint8_t *ptr, size_t len) const throw(InvalidPacket); /** * Encode the packet. * * @param pkt vector into which the packet should be placed. * @return true if the encoding succeeded. */ bool encode(vector& pkt); list& get_ls_request() { return _ls_request; } /** * Generate a printable representation of the packet. */ string str() const; private: list _ls_request; }; /** * Link State Update Packet */ class LinkStateUpdatePacket : public Packet { public: LinkStateUpdatePacket(OspfTypes::Version version, LsaDecoder& lsa_decoder) : Packet(version), _lsa_decoder(lsa_decoder) {} OspfTypes::Type get_type() const { return 4; } Packet *decode(uint8_t *ptr, size_t len) const throw(InvalidPacket); /** * Encode the packet. * * @param pkt vector into which the packet should be placed. * @return true if the encoding succeeded. */ bool encode(vector& pkt); /** * Encode the packet. * * @param pkt vector into which the packet should be placed. * @param inftransdelay add this delay to the age field of each LSA. * @return true if the encoding succeeded. */ bool encode(vector& pkt, uint16_t inftransdelay); list& get_lsas() { return _lsas; } /** * Generate a printable representation of the packet. */ string str() const; private: LsaDecoder& _lsa_decoder; // LSA decoders. // The packet contains a field with the number of LSAs that it // contains there is no point in storing it. list _lsas; // The list of LSAs in the packet. }; /** * Link State Acknowledgement Packet */ class LinkStateAcknowledgementPacket : public Packet { public: LinkStateAcknowledgementPacket(OspfTypes::Version version) : Packet(version) {} OspfTypes::Type get_type() const { return 5; } Packet *decode(uint8_t *ptr, size_t len) const throw(InvalidPacket); /** * Encode the packet. * * @param pkt vector into which the packet should be placed. * @return true if the encoding succeeded. */ bool encode(vector& pkt); list& get_lsa_headers() { return _lsa_headers; } /** * Generate a printable representation of the packet. */ string str() const; private: list _lsa_headers; }; /** * The definitive list of packets. All decoder lists should be primed * using this function. */ inline void initialise_packet_decoder(OspfTypes::Version version, PacketDecoder& packet_decoder, LsaDecoder& lsa_decoder) { packet_decoder.register_decoder(new HelloPacket(version)); packet_decoder.register_decoder(new DataDescriptionPacket(version)); packet_decoder.register_decoder(new LinkStateUpdatePacket(version, lsa_decoder)); packet_decoder.register_decoder(new LinkStateRequestPacket(version)); packet_decoder. register_decoder(new LinkStateAcknowledgementPacket(version)); } /** * Helper class to manipulate the options field in packets and LSAs. */ class Options { public: static const uint32_t V6_bit = 0x1; static const uint32_t E_bit = 0x2; static const uint32_t MC_bit = 0x4; static const uint32_t N_bit = 0x8; static const uint32_t P_bit = N_bit; static const uint32_t R_bit = 0x10; static const uint32_t EA_bit = 0x10; static const uint32_t DC_bit = 0x20; Options(OspfTypes::Version version, uint32_t options) : _version(version), _options(options) { } void set_bit(bool set, uint32_t bit) { if (set) _options |= bit; else _options &= ~bit; } bool get_bit(uint32_t bit) const { return _options & bit ? true : false; } void set_v6_bit(bool set) { XLOG_ASSERT(OspfTypes::V3 == _version); set_bit(set, V6_bit); } bool get_v6_bit() const { XLOG_ASSERT(OspfTypes::V3 == _version); return get_bit(V6_bit); } void set_e_bit(bool set) { set_bit(set, E_bit); } bool get_e_bit() const { return get_bit(E_bit); } void set_mc_bit(bool set) { set_bit(set, MC_bit); } bool get_mc_bit() const { return get_bit(MC_bit); } void set_n_bit(bool set) { set_bit(set, N_bit); } bool get_n_bit() const { return get_bit(N_bit); } void set_p_bit(bool set) { set_n_bit(set); } bool get_p_bit() const { return get_n_bit(); } void set_r_bit(bool set) { XLOG_ASSERT(OspfTypes::V3 == _version); set_bit(set, R_bit); } bool get_r_bit() const { XLOG_ASSERT(OspfTypes::V3 == _version); return get_bit(R_bit); } void set_ea_bit(bool set) { XLOG_ASSERT(OspfTypes::V2 == _version); set_bit(set, EA_bit); } bool get_ea_bit() const { XLOG_ASSERT(OspfTypes::V2 == _version); return get_bit(EA_bit); } void set_dc_bit(bool set) { set_bit(set, DC_bit); } bool get_dc_bit() const { return get_bit(DC_bit); } uint32_t get_options() { return _options; } string pp_bool(bool val) const { return val ? "1" : "0"; } string str() const { string out; switch(_version) { case OspfTypes::V2: out = "DC: " + pp_bool(get_dc_bit()); out += " EA: " + pp_bool(get_ea_bit()); out += " N/P: " + pp_bool(get_n_bit()); out += " MC: " + pp_bool(get_mc_bit()); out += " E: " + pp_bool(get_e_bit()); break; case OspfTypes::V3: out = "DC: " + pp_bool(get_dc_bit()); out += " R: " + pp_bool(get_r_bit()); out += " N: " + pp_bool(get_n_bit()); out += " MC: " + pp_bool(get_mc_bit()); out += " E: " + pp_bool(get_e_bit()); out += " V6: " + pp_bool(get_v6_bit()); break; } return out; } private: OspfTypes::Version _version; uint32_t _options; }; /** * Verify the checksum of an IPv6 PDU, throw an exception if the * checksum doesn't match. * * In IPv6 the payload is not checksummed it is up to the protocol to * checksum its own payload. The checksum includes a pseduo header * that is described in RFC 2460 section 8.1 * * @param src Source address of packet. * @param dst Destination address of packet. * @param data pointer to payload. * @param len length of payload. * @param checksum_offset offset of checksum in the payload. * @param protocol protocol number. */ template void ipv6_checksum_verify(const A& src, const A& dst, const uint8_t *data, size_t len, size_t checksum_offset, uint8_t protocol) throw(InvalidPacket); /** * Compute the IPv6 checksum and apply it to the packet provided. If * the checksum_offset is outside the packet then an exception is thrown. * * In IPv6 the payload is not checksummed it is up to the protocol to * checksum its own payload. The checksum includes a pseduo header * that is described in RFC 2460 section 8.1 * * @param src Source address of packet. * @param dst Destination address of packet. * @param data pointer to payload. * @param len length of payload. * @param checksum_offset offset of checksum in the payload. * @param protocol protocol number. */ template void ipv6_checksum_apply(const A& src, const A& dst, uint8_t *data, size_t len, size_t checksum_offset, uint8_t protocol) throw(InvalidPacket); #endif // __OSPF_PACKET_HH__ xorp/ospf/exceptions.hh0000664000076400007640000000241211421137511015324 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/ospf/exceptions.hh,v 1.8 2008/10/02 21:57:47 bms Exp $ #ifndef __OSPF_EXCEPTIONS_HH__ #define __OSPF_EXCEPTIONS_HH__ /** * Bad Peer exception. */ class BadPeer : public XorpReasonedException { public: BadPeer(const char* file, size_t line, const string init_why = "") : XorpReasonedException("BadPeer", file, line, init_why) {} }; #endif // __OSPF_EXCEPTIONS_HH__ xorp/ospf/policy_varrw.hh0000664000076400007640000000372111421137511015667 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/ospf/policy_varrw.hh,v 1.15 2008/10/02 21:57:48 bms Exp $ #ifndef __OSPF_POLICY_VARRRW_HH__ #define __OSPF_POLICY_VARRRW_HH__ #include "policy/backend/single_varrw.hh" #include "policy/common/element_factory.hh" #include "policy/backend/policy_filters.hh" #include "policy/backend/policytags.hh" template class OspfVarRW : public SingleVarRW { public: enum { VAR_NETWORK = VAR_PROTOCOL, VAR_NEXTHOP, VAR_METRIC, VAR_EBIT, }; OspfVarRW(IPNet& network, A& nexthop, uint32_t& metric, bool& e_bit, uint32_t& tag, bool& tag_set, PolicyTags& policytags); // SingleVarRW inteface: void start_read(); Element* single_read(const Id& id); void single_write(const Id& id, const Element& e); private: void start_read_common(); void single_write_common(const Id& id, const Element& e); IPNet& _network; A& _nexthop; uint32_t& _metric; bool& _e_bit; uint32_t& _tag; bool& _tag_set; PolicyTags& _policytags; ElementFactory _ef; }; #endif // __OSPF_POLICY_VARRRW_HH__ xorp/ospf/README0000664000076400007640000000131411421137511013502 0ustar greearbgreearb# # $XORP: xorp/ospf/README,v 1.6 2007/03/20 22:02:05 atanu Exp $ # Open Shortest Path First (OSPF) Implementation ============================================== This directory contains the XORP implementation of the OSPF protocol. Configuration ============= Startup ======= Documentation ============= The programming documentation is in: ${XORP}/docs/kdoc/html/ospf/ Testing ======= As with most XORP processes, running "gmake check" in the ospf directory will run a set of validation checks. Any new functionality committed to ospf must come with it's own set of validation checks. Status ====== July 2008: OSPFv2 and OSPFv3 are complete, the NSSA implementation does not support translator election. xorp/ospf/fletcher_checksum.cc0000664000076400007640000000310011421137511016602 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "ospf_module.h" #include "libxorp/xorp.h" #include "fletcher_checksum.hh" /* ** return number modulo 255 most importantly convert negative numbers ** to a ones complement representation. */ inline int32_t onecomp(int32_t a) { int32_t res; res = a % 255; if(res <= 0) res = 255 + res; return res; } /* ** generate iso checksums. */ void fletcher_checksum(uint8_t *bufp, size_t len, size_t off, int32_t& x, int32_t& y) { int32_t c0 = 0, c1 = 0; for(size_t i = 0; i < len; i++) { c0 = bufp[i] + c0; c1 = c1 + c0; } c0 %= 255; c1 %= 255; off += 1; // C Arrays are from 0 not 1. x = onecomp(-c1 + (len - off) * c0); y = onecomp(c1 - (len - off + 1) * c0); } xorp/ospf/xorp_ospfv3.cc0000664000076400007640000000466511540225531015437 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "ospf_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/service.hh" #include "libxorp/status_codes.h" #include "libxorp/eventloop.hh" #include "libxipc/xrl_std_router.hh" #include "ospf.hh" #include "io.hh" #include "xrl_io.hh" #include "xrl_target.hh" #include "xrl_target3.hh" int main(int /*argc*/, char **argv) { XorpUnexpectedHandler x(xorp_unexpected_handler); // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_level_set_verbose(XLOG_LEVEL_WARNING, XLOG_VERBOSE_HIGH); xlog_level_set_verbose(XLOG_LEVEL_INFO, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); try { EventLoop eventloop; string feaname = "fea"; string ribname = "rib"; XrlStdRouter xrl_router(eventloop, TARGET_OSPFv3); XrlIO io_ipv6(eventloop, xrl_router, feaname, ribname); Ospf ospf_ipv6(OspfTypes::V3, eventloop, &io_ipv6); XrlOspfV3Target v3target(&xrl_router, ospf_ipv6, io_ipv6); wait_until_xrl_router_is_ready(eventloop, xrl_router); io_ipv6.startup(); setup_dflt_sighandlers(); while (xorp_do_run && ospf_ipv6.running()) eventloop.run(); } catch(...) { xorp_catch_standard_exceptions(); } // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); debug_msg("Bye!\n"); return 0; } xorp/ospf/test_common.hh0000664000076400007640000000401611421137511015474 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/ospf/test_common.hh,v 1.8 2008/10/02 21:57:49 bms Exp $ #ifndef __OSPF_TEST_COMMON_HH__ #define __OSPF_TEST_COMMON_HH__ /** * Compute legal values for the options fields. */ inline uint32_t compute_options(OspfTypes::Version version, OspfTypes::AreaType area_type) { // Set/UnSet E-Bit. Options options(version, 0); switch(area_type) { case OspfTypes::NORMAL: options.set_e_bit(true); break; case OspfTypes::STUB: case OspfTypes::NSSA: options.set_e_bit(false); break; } switch (version) { case OspfTypes::V2: break; case OspfTypes::V3: options.set_v6_bit(true); break; } return options.get_options(); } /** * The type fields when saving an LSA database. */ enum TLV { TLV_VERSION = 1, // The first entry in a file 4 byte version number. TLV_SYSTEM_INFO = 2,// A string defining the creation system. TLV_OSPF_VERSION = 3,// The OSPF version that the database came from. TLV_AREA = 4, // AREA that the following LSAs belong to 4 bytes TLV_LSA = 5 // Binary LSA. }; const uint32_t TLV_CURRENT_VERSION = 1; // Current version number #endif // __OSPF_TEST_COMMON_HH__ xorp/ospf/tests/0000775000076400007640000000000011633743677014012 5ustar greearbgreearbxorp/ospf/tests/test_build_lsa.hh0000664000076400007640000000367711421137511017320 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/ospf/test_build_lsa.hh,v 1.11 2008/10/02 21:57:49 bms Exp $ #ifndef __OSPF_TEST_BUILD_LSA_HH__ #define __OSPF_TEST_BUILD_LSA_HH__ #include "libxorp/tokenize.hh" class BuildLsa { public: BuildLsa(OspfTypes::Version version) : _version(version) {} /** * From a textual representation build an LSA that can be used for * testing. */ Lsa *generate(Args& args); private: OspfTypes::Version _version; Options get_options(Lsa *lsa); void set_options(Lsa *lsa, Options& options); bool common_header(Lsa *lsa, const string& word, Args& args); bool router_link(RouterLsa *lsa, const string& word, Args& args); Lsa *router_lsa(Args& args); Lsa *network_lsa(Args& args); IPv6Prefix ipv6prefix(Args& args, bool use_metric = false); Lsa *summary_network_lsa(Args& args); Lsa *summary_router_lsa(Args& args); Lsa *as_external_lsa(Args& args); Lsa *type_7_lsa(Args& args); Lsa *link_lsa(Args& args); Lsa *intra_area_prefix_lsa(Args& args); }; #endif // __OSPF_TEST_BUILD_LSA_HH__ xorp/ospf/tests/test_routing_interactive.cc0000664000076400007640000002044011421137511021417 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "ospf_module.h" #include "libxorp/xorp.h" #include "libxorp/test_main.hh" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/callback.hh" #include "libxorp/ipv4.hh" #include "libxorp/ipv6.hh" #include "libxorp/ipnet.hh" #include "libxorp/status_codes.h" #include "libxorp/service.hh" #include "libxorp/eventloop.hh" #include "libproto/spt.hh" #include "ospf.hh" #include "delay_queue.hh" #include "vertex.hh" #include "area_router.hh" #include "debug_io.hh" #include "test_args.hh" #include "test_build_lsa.hh" template class Routing { public: Routing(OspfTypes::Version version, bool verbose, int verbose_level); /** * Send a command to route tester. */ bool cmd(Args& args) throw(InvalidString); private: OspfTypes::Version _version; EventLoop _eventloop; TestInfo _info; DebugIO _io; Ospf _ospf; OspfTypes::AreaID _selected_area; /** * Return a pointer to the selected area router. */ AreaRouter *get_area_router(Args& args) { PeerManager& pm = _ospf.get_peer_manager(); AreaRouter *area_router = pm.get_area_router(_selected_area); if (0 == area_router) xorp_throw(InvalidString, c_format("Invalid area <%s> [%s]", pr_id(_selected_area).c_str(), args.original_line().c_str())); return area_router; } }; template Routing::Routing(OspfTypes::Version version, bool verbose, int verbose_level) : _version(version), _info("routing", verbose, verbose_level, cout), _io(_info, version, _eventloop), _ospf(version, _eventloop, &_io) { _io.startup(); _ospf.trace().all(verbose); _ospf.set_testing(true); } template bool Routing::cmd(Args& args) throw(InvalidString) { string word; while (args.get_next(word)) { if (_info.verbose()) { cout << "[" << word << "]" << endl; cout << args.original_line() << endl; } if ("#" == word.substr(0,1)) { // CMD: # return true; } else if ("set_router_id" == word) { // CMD: set_router_id _ospf.set_router_id(set_id(get_next_word(args, "set_router_id"). c_str())); } else if ("create" == word) { // CMD: create OspfTypes::AreaID area = set_id(get_next_word(args, "create").c_str()); PeerManager& pm = _ospf.get_peer_manager(); bool okay; word = get_next_word(args, "area_type"); OspfTypes::AreaType area_type = from_string_to_area_type(word, okay); if (!okay) xorp_throw(InvalidString, c_format("<%s> is not a valid area type", word.c_str())); if (!pm.create_area_router(area, area_type, false /* !permissive*/)) xorp_throw(InvalidString, c_format("Failed to create area <%s> [%s]", pr_id(area).c_str(), args.original_line().c_str())); } else if ("select" == word) { // CMD: select OspfTypes::AreaID area = set_id(get_next_word(args, "select").c_str()); PeerManager& pm = _ospf.get_peer_manager(); if (0 == pm.get_area_router(area)) xorp_throw(InvalidString, c_format("Invalid area <%s> [%s]", pr_id(area).c_str(), args.original_line().c_str())); _selected_area = area; } else if ("replace" == word) { // CMD: replace // Replace this routers RouterLSA. AreaRouter *area_router = get_area_router(args); BuildLsa blsa(_version); Lsa *lsa = blsa.generate(args); if (0 == lsa) xorp_throw(InvalidString, c_format("Couldn't form a LSA [%s]", args.original_line().c_str())); Lsa::LsaRef lsar(lsa); lsar->set_self_originating(true); area_router->testing_replace_router_lsa(lsar); } else if ("add" == word) { // CMD: add AreaRouter *area_router = get_area_router(args); BuildLsa blsa(_version); Lsa *lsa = blsa.generate(args); if (0 == lsa) xorp_throw(InvalidString, c_format("Couldn't form a LSA [%s]", args.original_line().c_str())); Lsa::LsaRef lsar(lsa); area_router->testing_add_lsa(lsar); } else if ("compute" == word) { // CMD: compute OspfTypes::AreaID area = set_id(get_next_word(args, "compute").c_str()); PeerManager& pm = _ospf.get_peer_manager(); AreaRouter *area_router = pm.get_area_router(area); if (0 == area_router) xorp_throw(InvalidString, c_format("Invalid area <%s> [%s]", pr_id(area).c_str(), args.original_line().c_str())); if (_info.verbose()) area_router->testing_print_link_state_database(); area_router->testing_routing_total_recompute(); } else if ("destroy" == word) { // CMD: destroy OspfTypes::AreaID area = set_id(get_next_word(args, "destroy").c_str()); PeerManager& pm = _ospf.get_peer_manager(); if (!pm.destroy_area_router(area)) xorp_throw(InvalidString, c_format("Failed to delete area <%s> [%s]", pr_id(area).c_str(), args.original_line().c_str())); } else if ("verify_routing_table_size" == word) { // CMD: verify_routing_table_size uint32_t expected_count = get_next_number(args, "verify_routing_table_size"); uint32_t actual_count = _io.routing_table_size(); if (expected_count != actual_count) xorp_throw(InvalidString, c_format("Routing table size expected %d actual %d" " [%s]", expected_count, actual_count, args.original_line().c_str())); } else if ("verify_routing_entry" == word) { // CMD: verify_routing_entry IPNet net(get_next_word(args, "verify_routing_entry").c_str()); A nexthop(get_next_word(args, "verify_routing_entry").c_str()); uint32_t metric = get_next_number(args, "verify_routing_entry"); bool equal = get_next_word(args, "verify_routing_entry") == "true" ? true : false; bool discard = get_next_word(args, "verify_routing_entry") == "true" ? true : false; if (!_io.routing_table_verify(net, nexthop, metric, equal,discard)) xorp_throw(InvalidString, c_format("Matching routing table entry not found" " [%s]", args.original_line().c_str())); } else { xorp_throw(InvalidString, c_format("Unknown command <%s>. [%s]", word.c_str(), args.original_line().c_str())) } } return true; } template int go(OspfTypes::Version version, bool verbose, int verbose_level) { Routing routing(version, verbose, verbose_level); try { for(;;) { string line; if (!getline(cin, line)) return 0; Args args(line); if (!routing.cmd(args)) return -1; } } catch(...) { xorp_print_standard_exceptions(); return -1; } } int main(int argc, char **argv) { XorpUnexpectedHandler x(xorp_unexpected_handler); xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); TestMain t(argc, argv); bool v2 = t.get_optional_flag("-2", "--OSPFv2", "OSPFv2"); bool v3 = t.get_optional_flag("-3", "--OSPFv3", "OSPFv3"); t.complete_args_parsing(); if (0 != t.exit()) return t.exit(); OspfTypes::Version version; if (v2 == v3 || v2) { version = OspfTypes::V2; return go(version, t.get_verbose(), t.get_verbose_level()); } else { version = OspfTypes::V3; return go(version, t.get_verbose(), t.get_verbose_level()); } xlog_stop(); xlog_exit(); return 0; } xorp/ospf/tests/packet2.data0000664000076400007640000000314511421137511016154 0ustar greearbgreearb// $XORP: xorp/ospf/packet1.data,v 1.2 2006/10/16 07:21:04 atanu Exp $ // Bad OSPF packet based on packet1.data with the fields in the Network-LSA // corrected. The number of LSAs is still bad and a number of fields in the // Router-LSA are bad. /* OSPF Version */ 0x02, /* OSPF Packet Type */ 0x04, /* Packet Length */ 0x00, 0x5c, /* Source OSPF Router ID */ 0x7f, 0x00, 0x00, 0x01, /* Area ID */ 0x00, 0x00, 0x00, 0x00, /* Packet Checksum */ 0xc8, 0xf0, /*0x3d,0xf6,*/ /* Auth Type */ 0x00, 0x00, /* Auth Data */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* LS Update Packet */ /* Number of LSAs: 98304515 (BAD) */ 0x05, 0xdc, 0x02, 0x03, /* Network LSA */ /* LS Age */ 0x00, 0x00, /* Options */ 0x00, /* LSA Type: 2 (Network LSA) */ 0x02, /* Link State ID */ 0x00, 0x3c,0x02, 0x01, /* Advertising Router */ 0x7f, 0x00, 0x00, 0x01, /* LS Sequence number */ 0x7f, 0x00, 0x00, 0x01, /* LS Checksum */ 0x9e, 0xaf, /* Length */ 0x00, 28, /* Netmask */ 0x00, 0x00, 0x00, 0x14, /* Attached Router */ 0x00, 0x3c, 0x02, 0x01, /* Router LSA */ /* LS Age */ 0x01, 0x01, /* Options */ 0x01, /* LSA Type: 1 (Router LSA) */ 0x01, /* Link State ID */ 0x01, 0x01, 0x01, 0x01, /* Advertising Router */ 0x80, 0x00, 0x00, 0x01, /* LS Sequence number */ 0x00, 0x00, 0x00, 0x14, /* LS Checksum */ 0x00, 0x3c, /*142, 19,*/ /* Length: 513 (BAD) */ 0x02, 0x01, /*0,36,*/ /* Flags */ 0x01, 0x01, /* # Links: 513 (BAD) */ 0x02, 0x01, /*0x0,0x1,*/ /* Link ID */ 0x01, 0x01, 0x02, 0x01, /* Link Data */ 0x80, 0x00, 0x00, 0x01, /* Type (BAD) */ 0x00, /*1,*/ /* # TOS */ 0x00, /* Metric */ 0x00, 0x14 xorp/ospf/tests/test_packet.cc0000664000076400007640000013424011633743677016633 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "ospf_module.h" #include "libxorp/xorp.h" #include "libxorp/test_main.hh" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/ipv4.hh" #include "libxorp/ipv6.hh" #include "libxorp/ipnet.hh" #include "libxorp/status_codes.h" #include "libxorp/service.hh" #include "libxorp/eventloop.hh" #include "libxorp/tokenize.hh" #include "ospf.hh" #include "packet.hh" #include "test_common.hh" // Make sure that all tests free up any memory that they use. This will // allow us to use the leak checker program. /* * Compare two packets * * @return true when they are equal */ inline bool compare_packets(TestInfo& info, vector& pkt1, vector& pkt2) { if (pkt1.size() != pkt2.size()) { DOUT(info) << "Packet lengths don't match " << pkt1.size() << " " << pkt2.size() << endl; return false; } if (0 != memcmp(&pkt1[0], &pkt2[0], pkt1.size())) { for(size_t i = 0; i < pkt1.size(); i++) { if (pkt1[i] != pkt2[i]) { DOUT(info) << "mismatch at byte position " << i << endl; DOUT(info) << "bytes " << (int)pkt1[i] << " " << (int)pkt2[i] << endl; break; } } return false; } return true; } /* * Compare the string renditions of packets * * @return true when they are equal */ inline bool compare_packet_strings(TestInfo& info, string str1, string str2) { if (str1 != str2) { if (info.verbose()) { vector token1; vector token2; tokenize(str1, token1, "\n"); tokenize(str2, token2, "\n"); vector::iterator i1 = token1.begin(); vector::iterator i2 = token2.begin(); for(;;) { if (token1.end() == i1 || token2.end() == i2) { DOUT(info) << "Ran out of tokens\n"; break; } if (*i1 != *i2) { DOUT(info) << *i1 << " *** DOES NOT MATCH ***" << *i2 << endl; break; } i1++; i2++; } } return false; } return true; } /** * Modify the frame lengths to see if we can freak the decoder. */ inline bool packet_bad_length(TestInfo& info, OspfTypes::Version version, vector& pkt) { PacketDecoder packet_decoder; LsaDecoder lsa_decoder(version); initialise_lsa_decoder(version, lsa_decoder); initialise_packet_decoder(version, packet_decoder, lsa_decoder); uint8_t *ptr = &pkt[0]; Packet *packet = packet_decoder.decode(ptr, pkt.size()); // Try shortening the frame. try { packet = packet_decoder.decode(ptr, pkt.size() - 1); DOUT(info) << "Accepted short frame (bad)\n"; return false; } catch(InvalidPacket& e) { DOUT(info) << "Rejected short frame (good): " << e.str() << endl; } // If we are going to feed in an oversize frame make sure that the // space is actually allocated. OpenBSD actually notices if the // space is not allocated. const int oversize = 64; vector large_pkt; large_pkt.resize(pkt.size() + oversize); ptr = &large_pkt[0]; memcpy(ptr, &pkt[0], pkt.size()); // Make the frame longer. for (int i = 0; i < oversize; i++) { try { packet = packet_decoder.decode(ptr, pkt.size() + i); DOUT(info) << "Accepted large frame (good)\n"; } catch(InvalidPacket& e) { DOUT(info) << "Didn't accept large frame (bad): " << e.str() << endl; return false; } vector pktn; packet->encode(pktn); if (!compare_packets(info, pkt, pktn)) { DOUT(info) << " The frame is " << i << " bytes larger decoded differently " << packet->str() << endl; return false; } } DOUT(info) << packet->str() << endl; return true; } /** * Fill all the fields except for the 8 byte auth in a V2 hello packet. */ inline void populate_helloV2(HelloPacket *hello) { hello->set_router_id(set_id("128.16.64.16")); hello->set_area_id(set_id("4.3.2.1")); hello->set_auth_type(5); hello->set_network_mask(0xffff0000); hello->set_hello_interval(9876); hello->set_options(compute_options(OspfTypes::V2, OspfTypes::NORMAL)); hello->set_router_priority(42); hello->set_router_dead_interval(66000); hello->set_designated_router(set_id("1.2.3.4")); hello->set_backup_designated_router(set_id("2.4.6.8")); // Add some neighbours. hello->get_neighbours().push_back(set_id("10.11.12.13")); hello->get_neighbours().push_back(set_id("11.12.13.14")); } /** * Fill all the fields in a V3 hello packet. */ inline void populate_helloV3(HelloPacket *hello) { hello->set_router_id(set_id("128.16.64.16")); hello->set_area_id(set_id("4.3.2.1")); hello->set_instance_id(5); hello->set_interface_id(12345678); hello->set_hello_interval(9876); hello->set_options(compute_options(OspfTypes::V3, OspfTypes::NORMAL)); hello->set_router_priority(42); hello->set_router_dead_interval(6600); hello->set_designated_router(set_id("1.2.3.4")); hello->set_backup_designated_router(set_id("2.4.6.8")); // Add some neighbours. hello->get_neighbours().push_back(set_id("10.11.12.13")); hello->get_neighbours().push_back(set_id("11.12.13.14")); } inline void populate_hello(HelloPacket *hello, OspfTypes::Version version) { switch(version) { case OspfTypes::V2: populate_helloV2(hello); break; case OspfTypes::V3: populate_helloV3(hello); break; } } inline void populate_standard_header(Packet *packet, OspfTypes::Version version) { packet->set_router_id(set_id("128.16.64.16")); packet->set_area_id(set_id("4.3.2.1")); switch(version) { case OspfTypes::V2: packet->set_auth_type(5); break; case OspfTypes::V3: packet->set_instance_id(5); break; } } inline void populate_lsa_header(Lsa_header& header, OspfTypes::Version version) { header.set_ls_age(500); switch(version) { case OspfTypes::V2: header.set_options(compute_options(version, OspfTypes::NORMAL)); break; case OspfTypes::V3: break; } header.set_link_state_id(0x01020304); header.set_advertising_router(0x04030201); header.set_ls_sequence_number(0x0A0B0C0D); header.set_ls_checksum(0x1234); header.set_length(200); } inline void populate_data_description(DataDescriptionPacket *ddp, OspfTypes::Version version) { populate_standard_header(ddp, version); ddp->set_interface_mtu(1500); ddp->set_options(0xfe); ddp->set_i_bit(true); ddp->set_m_bit(true); ddp->set_ms_bit(true); ddp->set_dd_seqno(0x01020304); // Create a LSA Header to add Lsa_header header(version); populate_lsa_header(header, version); ddp->get_lsa_headers().push_back(header); } inline void populate_link_state_request(LinkStateRequestPacket *lsrp, OspfTypes::Version version) { populate_standard_header(lsrp, version); lsrp->get_ls_request(). push_back(Ls_request(version, 0xff, 0x0a0b0c0d, 0x12345678)); } inline void populate_router_lsa(RouterLsa *rlsa, OspfTypes::Version version) { populate_lsa_header(rlsa->get_header(), version); switch(version) { case OspfTypes::V2: break; case OspfTypes::V3: rlsa->set_w_bit(true); rlsa->set_options(compute_options(version, OspfTypes::NORMAL)); break; } RouterLink rl1(version); rl1.set_type(RouterLink::p2p); rl1.set_metric(2); switch(version) { case OspfTypes::V2: rl1.set_link_id(3); rl1.set_link_data(4); break; case OspfTypes::V3: rl1.set_interface_id(3); rl1.set_neighbour_interface_id(4); rl1.set_neighbour_router_id(5); break; } rlsa->get_router_links().push_back(rl1); RouterLink rl2(version); rl2.set_type(RouterLink::transit); rl2.set_metric(2); switch(version) { case OspfTypes::V2: rl2.set_link_id(3); rl2.set_link_data(4); break; case OspfTypes::V3: rl2.set_interface_id(3); rl2.set_neighbour_interface_id(4); rl2.set_neighbour_router_id(5); break; } rlsa->get_router_links().push_back(rl2); RouterLink rl3(version); switch(version) { case OspfTypes::V2: rl3.set_metric(2); rl3.set_type(RouterLink::stub); rl3.set_link_id(3); rl3.set_link_data(4); rlsa->get_router_links().push_back(rl3); break; case OspfTypes::V3: break; } RouterLink rl4(version); rl4.set_type(RouterLink::vlink); rl4.set_metric(2); switch(version) { case OspfTypes::V2: rl4.set_link_id(3); rl4.set_link_data(4); break; case OspfTypes::V3: rl4.set_interface_id(3); rl4.set_neighbour_interface_id(4); rl4.set_neighbour_router_id(5); break; } rlsa->get_router_links().push_back(rl4); // This will set the checksum and the length. rlsa->encode(); } inline void populate_network_lsa(NetworkLsa *nlsa, OspfTypes::Version version) { populate_lsa_header(nlsa->get_header(), version); switch(version) { case OspfTypes::V2: break; case OspfTypes::V3: nlsa->set_options(compute_options(version, OspfTypes::NORMAL)); break; } switch(version) { case OspfTypes::V2: nlsa->set_network_mask(0xffff0000); break; case OspfTypes::V3: break; } nlsa->get_attached_routers().push_back(set_id("128.16.64.16")); nlsa->get_attached_routers().push_back(set_id("128.16.64.32")); // This will set the checksum and the length. nlsa->encode(); } inline void populate_summary_network_lsa(SummaryNetworkLsa *snlsa, OspfTypes::Version version) { populate_lsa_header(snlsa->get_header(), version); snlsa->set_metric(5); switch(version) { case OspfTypes::V2: snlsa->set_network_mask(0xffff0000); break; case OspfTypes::V3: IPNet net("2001:468:e21:c800:220:edff:fe61:f033", 64); IPv6Prefix prefix(version); prefix.set_network(net); prefix.set_nu_bit(false); prefix.set_la_bit(false); prefix.set_mc_bit(false); prefix.set_p_bit(false); prefix.set_dn_bit(false); snlsa->set_ipv6prefix(prefix); break; } // This will set the checksum and the length. snlsa->encode(); } inline void populate_summary_router_lsa(SummaryRouterLsa *srlsa, OspfTypes::Version version) { populate_lsa_header(srlsa->get_header(), version); srlsa->set_metric(5); switch(version) { case OspfTypes::V2: srlsa->set_network_mask(0xffff0000); break; case OspfTypes::V3: srlsa->set_options(compute_options(version, OspfTypes::NORMAL)); srlsa->set_destination_id(set_id("128.16.64.32")); break; } // This will set the checksum and the length. srlsa->encode(); } inline void populate_as_external_lsa(ASExternalLsa *aelsa, OspfTypes::Version version) { populate_lsa_header(aelsa->get_header(), version); aelsa->set_metric(OspfTypes::LSInfinity); switch(version) { case OspfTypes::V2: aelsa->set_network_mask(0xffff0000); aelsa->set_e_bit(true); aelsa->set_forwarding_address_ipv4(IPv4("10.10.10.10")); break; case OspfTypes::V3: aelsa->set_e_bit(true); aelsa->set_f_bit(true); aelsa->set_t_bit(true); aelsa->set_referenced_ls_type(2); IPNet net("2001:468:e21:c800:220:edff:fe61:f033", 64); IPv6Prefix prefix(version); prefix.set_network(net); prefix.set_nu_bit(true); prefix.set_la_bit(true); prefix.set_mc_bit(true); prefix.set_p_bit(true); prefix.set_dn_bit(true); aelsa->set_ipv6prefix(prefix); aelsa->set_forwarding_address_ipv6( IPv6("2001:468:e21:c800:220:edff:fe61:f033")); aelsa->set_referenced_link_state_id(0x10); break; } aelsa->set_external_route_tag(0x42); // This will set the checksum and the length. aelsa->encode(); } inline void populate_link_lsa(LinkLsa *llsa, OspfTypes::Version version) { populate_lsa_header(llsa->get_header(), version); llsa->set_rtr_priority(42); llsa->set_options(compute_options(version, OspfTypes::NORMAL)); llsa->set_link_local_address(IPv6("fe80::202:b3ff:fe19:be47")); IPNet net1("2001:468:e21:c800:220:edff:fe61:f033", 64); IPv6Prefix prefix1(version); prefix1.set_network(net1); prefix1.set_nu_bit(true); prefix1.set_la_bit(true); prefix1.set_mc_bit(true); prefix1.set_p_bit(true); prefix1.set_dn_bit(true); llsa->get_prefixes().push_back(prefix1); IPNet net2("2001:700:0:fff1::2", 64); IPv6Prefix prefix2(version); prefix2.set_network(net2); prefix2.set_nu_bit(false); prefix2.set_la_bit(false); prefix2.set_mc_bit(false); prefix2.set_p_bit(false); prefix2.set_dn_bit(false); llsa->get_prefixes().push_back(prefix2); } inline void populate_intra_area_prefix_lsa(IntraAreaPrefixLsa *iaplsa, OspfTypes::Version version) { populate_lsa_header(iaplsa->get_header(), version); iaplsa->set_referenced_ls_type(0x2001); iaplsa->set_referenced_link_state_id(1); iaplsa->set_referenced_advertising_router(set_id("192.1.1.2")); IPNet net1("2001:468:e21:c800:220:edff:fe61:f033", 64); IPv6Prefix prefix1(version, true); prefix1.set_network(net1); prefix1.set_metric(256); prefix1.set_nu_bit(true); prefix1.set_la_bit(true); prefix1.set_mc_bit(true); prefix1.set_p_bit(true); prefix1.set_dn_bit(true); iaplsa->get_prefixes().push_back(prefix1); IPNet net2("2001:700:0:fff1::2", 64); IPv6Prefix prefix2(version, true); prefix2.set_network(net2); prefix2.set_metric(3841); prefix2.set_nu_bit(false); prefix2.set_la_bit(false); prefix2.set_mc_bit(false); prefix2.set_p_bit(false); prefix2.set_dn_bit(false); iaplsa->get_prefixes().push_back(prefix2); } inline void populate_link_state_update(LinkStateUpdatePacket *lsup, OspfTypes::Version version) { populate_standard_header(lsup, version); RouterLsa *rlsa = new RouterLsa(version); populate_router_lsa(rlsa, version); lsup->get_lsas().push_back(Lsa::LsaRef(rlsa)); rlsa = new RouterLsa(version); populate_router_lsa(rlsa, version); lsup->get_lsas().push_back(Lsa::LsaRef(rlsa)); NetworkLsa *nlsa = new NetworkLsa(version); populate_network_lsa(nlsa, version); lsup->get_lsas().push_back(Lsa::LsaRef(nlsa)); nlsa = new NetworkLsa(version); populate_network_lsa(nlsa, version); lsup->get_lsas().push_back(Lsa::LsaRef(nlsa)); SummaryNetworkLsa *snlsa = new SummaryNetworkLsa(version); populate_summary_network_lsa(snlsa, version); lsup->get_lsas().push_back(Lsa::LsaRef(snlsa)); snlsa = new SummaryNetworkLsa(version); populate_summary_network_lsa(snlsa, version); lsup->get_lsas().push_back(Lsa::LsaRef(snlsa)); SummaryRouterLsa *srlsa = new SummaryRouterLsa(version); populate_summary_router_lsa(srlsa, version); lsup->get_lsas().push_back(Lsa::LsaRef(srlsa)); srlsa = new SummaryRouterLsa(version); populate_summary_router_lsa(srlsa, version); lsup->get_lsas().push_back(Lsa::LsaRef(srlsa)); ASExternalLsa *aelsa = new ASExternalLsa(version); populate_as_external_lsa(aelsa, version); lsup->get_lsas().push_back(Lsa::LsaRef(aelsa)); aelsa = new ASExternalLsa(version); populate_as_external_lsa(aelsa, version); lsup->get_lsas().push_back(Lsa::LsaRef(aelsa)); } inline void populate_link_state_acknowledgement(LinkStateAcknowledgementPacket *lsack, OspfTypes::Version version) { populate_standard_header(lsack, version); // Create a LSA Header to add Lsa_header header(version); populate_lsa_header(header, version); lsack->get_lsa_headers().push_back(header); } bool hello_packet_print(TestInfo& info) { HelloPacket *hello= new HelloPacket(OspfTypes::V2); populate_hello(hello, OspfTypes::V2); DOUT(info) << hello->str() << endl; delete hello; hello= new HelloPacket(OspfTypes::V3); populate_hello(hello, OspfTypes::V3); DOUT(info) << hello->str() << endl; delete hello; return true; } bool hello_packet_compare(TestInfo& info, OspfTypes::Version version) { HelloPacket *hello1= new HelloPacket(version); populate_hello(hello1, version); DOUT(info) << hello1->str() << endl; // Encode the hello packet. vector pkt1; hello1->encode(pkt1); // Now decode the packet. // Create a new packet to provide the decoder. HelloPacket *hello2= new HelloPacket(version); HelloPacket *hello3 = dynamic_cast(hello2->decode(&pkt1[0], pkt1.size())); DOUT(info) << hello3->str() << endl; // Encode the second packet and compare. vector pkt2; hello3->encode(pkt2); if (!compare_packets(info, pkt1, pkt2)) return false; if (!compare_packet_strings(info, hello1->str(), hello3->str())) return false; delete hello1; delete hello2; delete hello3; return true; } bool hello_packet_bad_length(TestInfo& info, OspfTypes::Version version) { HelloPacket *packet = new HelloPacket(version); populate_hello(packet, version); DOUT(info) << packet->str() << endl; // Encode the hello packet. vector pkt; packet->encode(pkt); delete packet; return packet_bad_length(info, version, pkt); } bool data_description_packet_print(TestInfo& info) { DataDescriptionPacket *ddp= new DataDescriptionPacket(OspfTypes::V2); populate_data_description(ddp, OspfTypes::V2); DOUT(info) << ddp->str() << endl; delete ddp; ddp = new DataDescriptionPacket(OspfTypes::V3); populate_data_description(ddp, OspfTypes::V3); DOUT(info) << ddp->str() << endl; delete ddp; return true; } bool data_description_packet_compare(TestInfo& info, OspfTypes::Version version) { DataDescriptionPacket *ddp1= new DataDescriptionPacket(version); populate_data_description(ddp1, version); DOUT(info) << ddp1->str() << endl; // Encode the Data Description Packet. vector pkt1; ddp1->encode(pkt1); // Now decode the packet. // Create a new packet to provide the decoder. DataDescriptionPacket *ddp2= new DataDescriptionPacket(version); DataDescriptionPacket *ddp3 = dynamic_cast(ddp2-> decode(&pkt1[0], pkt1.size())); DOUT(info) << ddp3->str() << endl; // Encode the second packet and compare. vector pkt2; ddp3->encode(pkt2); if (!compare_packets(info, pkt1, pkt2)) return false; if (!compare_packet_strings(info, ddp1->str(), ddp3->str())) return false; delete ddp1; delete ddp2; delete ddp3; return true; } bool data_description_packet_bad_length(TestInfo& info, OspfTypes::Version version) { DataDescriptionPacket *packet = new DataDescriptionPacket(version); populate_data_description(packet, version); DOUT(info) << packet->str() << endl; // Encode the hello packet. vector pkt; packet->encode(pkt); delete packet; return packet_bad_length(info, version, pkt); } bool link_state_update_packet_print(TestInfo& info) { LsaDecoder lsa_decoder(OspfTypes::V2); LinkStateUpdatePacket *lsup; lsup = new LinkStateUpdatePacket(OspfTypes::V2, lsa_decoder); populate_link_state_update(lsup, OspfTypes::V2); DOUT(info) << lsup->str() << endl; delete lsup; lsup = new LinkStateUpdatePacket(OspfTypes::V3, lsa_decoder); populate_link_state_update(lsup, OspfTypes::V3); DOUT(info) << lsup->str() << endl; delete lsup; return true; } bool link_state_update_packet_compare(TestInfo& info, OspfTypes::Version version) { LsaDecoder lsa_decoder(version); initialise_lsa_decoder(version, lsa_decoder); LinkStateUpdatePacket *lsup1; lsup1 = new LinkStateUpdatePacket(version, lsa_decoder); populate_link_state_update(lsup1, version); DOUT(info) << lsup1->str() << endl; // Encode the Link State Update Packet. vector pkt1; lsup1->encode(pkt1); // Now decode the packet. // Create a new packet to provide the decoder. LinkStateUpdatePacket *lsup2; lsup2 = new LinkStateUpdatePacket(version, lsa_decoder); LinkStateUpdatePacket *lsup3 = dynamic_cast(lsup2-> decode(&pkt1[0], pkt1.size())); DOUT(info) << lsup3->str() << endl; // Encode the second packet and compare. vector pkt2; lsup3->encode(pkt2); if (!compare_packets(info, pkt1, pkt2)) return false; if (!compare_packet_strings(info, lsup1->str(), lsup3->str())) return false; delete lsup1; delete lsup2; delete lsup3; return true; } bool link_state_update_packet_bad_length(TestInfo& info, OspfTypes::Version version) { LsaDecoder lsa_decoder(OspfTypes::V2); LinkStateUpdatePacket *packet = new LinkStateUpdatePacket(version, lsa_decoder); populate_link_state_update(packet, version); DOUT(info) << packet->str() << endl; // Encode the hello packet. vector pkt; packet->encode(pkt); delete packet; return packet_bad_length(info, version, pkt); } bool link_state_request_packet_print(TestInfo& info) { LinkStateRequestPacket *lsrp= new LinkStateRequestPacket(OspfTypes::V2); populate_link_state_request(lsrp, OspfTypes::V2); DOUT(info) << lsrp->str() << endl; delete lsrp; lsrp = new LinkStateRequestPacket(OspfTypes::V3); populate_link_state_request(lsrp, OspfTypes::V3); DOUT(info) << lsrp->str() << endl; delete lsrp; return true; } bool link_state_request_packet_compare(TestInfo& info, OspfTypes::Version version) { LinkStateRequestPacket *lsrp1= new LinkStateRequestPacket(version); populate_link_state_request(lsrp1, version); DOUT(info) << lsrp1->str() << endl; // Encode the Data Description Packet. vector pkt1; lsrp1->encode(pkt1); // Now decode the packet. // Create a new packet to provide the decoder. LinkStateRequestPacket *lsrp2= new LinkStateRequestPacket(version); LinkStateRequestPacket *lsrp3 = dynamic_cast(lsrp2-> decode(&pkt1[0], pkt1.size())); DOUT(info) << lsrp3->str() << endl; // Encode the second packet and compare. vector pkt2; lsrp3->encode(pkt2); if (!compare_packets(info, pkt1, pkt2)) return false; if (!compare_packet_strings(info, lsrp1->str(), lsrp3->str())) return false; delete lsrp1; delete lsrp2; delete lsrp3; return true; } bool link_state_request_packet_bad_length(TestInfo& info, OspfTypes::Version version) { LinkStateRequestPacket *packet = new LinkStateRequestPacket(version); populate_link_state_request(packet, version); DOUT(info) << packet->str() << endl; // Encode the hello packet. vector pkt; packet->encode(pkt); delete packet; return packet_bad_length(info, version, pkt); } bool link_state_acknowledgement_packet_print(TestInfo& info) { LinkStateAcknowledgementPacket *lsrp = new LinkStateAcknowledgementPacket(OspfTypes::V2); populate_link_state_acknowledgement(lsrp, OspfTypes::V2); DOUT(info) << lsrp->str() << endl; delete lsrp; lsrp = new LinkStateAcknowledgementPacket(OspfTypes::V3); populate_link_state_acknowledgement(lsrp, OspfTypes::V3); DOUT(info) << lsrp->str() << endl; delete lsrp; return true; } bool link_state_acknowledgement_packet_compare(TestInfo& info, OspfTypes::Version version) { LinkStateAcknowledgementPacket *lsrp1 = new LinkStateAcknowledgementPacket(version); populate_link_state_acknowledgement(lsrp1, version); DOUT(info) << lsrp1->str() << endl; // Encode the Link State Acknowledgement Packet. vector pkt1; lsrp1->encode(pkt1); // Now decode the packet. // Create a new packet to provide the decoder. LinkStateAcknowledgementPacket *lsrp2 = new LinkStateAcknowledgementPacket(version); LinkStateAcknowledgementPacket *lsrp3 = dynamic_cast(lsrp2-> decode(&pkt1[0], pkt1.size())); DOUT(info) << lsrp3->str() << endl; // Encode the second packet and compare. vector pkt2; lsrp3->encode(pkt2); if (!compare_packets(info, pkt1, pkt2)) return false; if (!compare_packet_strings(info, lsrp1->str(), lsrp3->str())) return false; delete lsrp1; delete lsrp2; delete lsrp3; return true; } bool link_state_acknowledgement_packet_bad_length(TestInfo& info, OspfTypes::Version version) { LinkStateAcknowledgementPacket *packet = new LinkStateAcknowledgementPacket(version); populate_link_state_acknowledgement(packet, version); DOUT(info) << packet->str() << endl; // Encode the hello packet. vector pkt; packet->encode(pkt); delete packet; return packet_bad_length(info, version, pkt); } bool packet_decoder1(TestInfo& info, OspfTypes::Version version) { PacketDecoder dec; HelloPacket hello(version); populate_hello(&hello, version); vector pkt; hello.encode(pkt); // An attempt to decode a packet with no decoders installed should // fail. try { dec.decode(&pkt[0], pkt.size()); } catch(InvalidPacket& e) { DOUT(info) << "Caught exception: " << e.str() << endl; return true; } return false; } bool packet_decoder2(TestInfo& info, OspfTypes::Version version) { PacketDecoder dec; // Install a decoder for hello packets. Packet *hello_decoder = new HelloPacket(version); dec.register_decoder(hello_decoder); HelloPacket hello(version); populate_hello(&hello, version); vector pkt; hello.encode(pkt); // An attempt to decode a packet with a decoder installed should succeed. Packet *packet = dec.decode(&pkt[0], pkt.size()); DOUT(info) << packet->str() << endl; delete packet; return true; } uint8_t bad_packet1[] = { #include "packet1.data" }; uint8_t bad_packet2[] = { #include "packet2.data" }; uint8_t bad_packet3[] = { #include "packet3.data" }; bool packet_decode_bad_packet(TestInfo& info, OspfTypes::Version version, uint8_t *ptr, size_t len) { PacketDecoder packet_decoder; LsaDecoder lsa_decoder(version); initialise_lsa_decoder(version, lsa_decoder); initialise_packet_decoder(version, packet_decoder, lsa_decoder); try { packet_decoder.decode(ptr, len); DOUT(info) << "Accepted bad packet (bad)\n"; return false; } catch(InvalidPacket& e) { DOUT(info) << "Rejected bad packet (good): " << e.str() << endl; return true; } XLOG_UNREACHABLE(); return false; } /* Packet stuff above - LSA stuff below */ inline void fill_vector(vector& pkt, uint8_t *ptr, size_t len) { pkt.resize(len); for(size_t i = 0; i < len; i++) pkt[i] = ptr[i]; } bool router_lsa_print(TestInfo& info) { RouterLsa *rlsa = new RouterLsa(OspfTypes::V2); populate_router_lsa(rlsa, OspfTypes::V2); DOUT(info) << rlsa->str() << endl; delete rlsa; rlsa = new RouterLsa(OspfTypes::V3); populate_router_lsa(rlsa, OspfTypes::V3); DOUT(info) << rlsa->str() << endl; XLOG_ASSERT(rlsa->known()); XLOG_ASSERT(!rlsa->link_local_scope()); XLOG_ASSERT(rlsa->area_scope()); XLOG_ASSERT(!rlsa->as_scope()); delete rlsa; return true; } bool network_lsa_print(TestInfo& info) { NetworkLsa *nlsa = new NetworkLsa(OspfTypes::V2); populate_network_lsa(nlsa, OspfTypes::V2); DOUT(info) << nlsa->str() << endl; delete nlsa; nlsa = new NetworkLsa(OspfTypes::V3); populate_network_lsa(nlsa, OspfTypes::V3); DOUT(info) << nlsa->str() << endl; XLOG_ASSERT(nlsa->known()); XLOG_ASSERT(!nlsa->link_local_scope()); XLOG_ASSERT(nlsa->area_scope()); XLOG_ASSERT(!nlsa->as_scope()); delete nlsa; return true; } bool summary_network_lsa_print(TestInfo& info) { SummaryNetworkLsa *snlsa = new SummaryNetworkLsa(OspfTypes::V2); populate_summary_network_lsa(snlsa, OspfTypes::V2); DOUT(info) << snlsa->str() << endl; delete snlsa; snlsa = new SummaryNetworkLsa(OspfTypes::V3); populate_summary_network_lsa(snlsa, OspfTypes::V3); DOUT(info) << snlsa->str() << endl; XLOG_ASSERT(snlsa->known()); XLOG_ASSERT(!snlsa->link_local_scope()); XLOG_ASSERT(snlsa->area_scope()); XLOG_ASSERT(!snlsa->as_scope()); delete snlsa; return true; } bool summary_router_lsa_print(TestInfo& info) { SummaryRouterLsa *srlsa = new SummaryRouterLsa(OspfTypes::V2); populate_summary_router_lsa(srlsa, OspfTypes::V2); DOUT(info) << srlsa->str() << endl; delete srlsa; srlsa = new SummaryRouterLsa(OspfTypes::V3); populate_summary_router_lsa(srlsa, OspfTypes::V3); DOUT(info) << srlsa->str() << endl; XLOG_ASSERT(srlsa->known()); XLOG_ASSERT(!srlsa->link_local_scope()); XLOG_ASSERT(srlsa->area_scope()); XLOG_ASSERT(!srlsa->as_scope()); delete srlsa; return true; } bool as_external_lsa_print(TestInfo& info) { ASExternalLsa *aelsa = new ASExternalLsa(OspfTypes::V2); populate_as_external_lsa(aelsa, OspfTypes::V2); DOUT(info) << aelsa->str() << endl; delete aelsa; aelsa = new ASExternalLsa(OspfTypes::V3); populate_as_external_lsa(aelsa, OspfTypes::V3); DOUT(info) << aelsa->str() << endl; XLOG_ASSERT(aelsa->known()); XLOG_ASSERT(!aelsa->link_local_scope()); XLOG_ASSERT(!aelsa->area_scope()); XLOG_ASSERT(aelsa->as_scope()); delete aelsa; return true; } bool type7_lsa_print(TestInfo& info) { Type7Lsa *type7 = new Type7Lsa(OspfTypes::V2); populate_as_external_lsa(type7, OspfTypes::V2); DOUT(info) << type7->str() << endl; delete type7; type7 = new Type7Lsa(OspfTypes::V3); populate_as_external_lsa(type7, OspfTypes::V3); DOUT(info) << type7->str() << endl; XLOG_ASSERT(type7->known()); XLOG_ASSERT(!type7->link_local_scope()); XLOG_ASSERT(type7->area_scope()); XLOG_ASSERT(!type7->as_scope()); delete type7; return true; } bool link_lsa_print(TestInfo& info) { LinkLsa *llsa = new LinkLsa(OspfTypes::V3); populate_link_lsa(llsa, OspfTypes::V3); DOUT(info) << llsa->str() << endl; XLOG_ASSERT(llsa->known()); XLOG_ASSERT(llsa->link_local_scope()); XLOG_ASSERT(!llsa->area_scope()); XLOG_ASSERT(!llsa->as_scope()); delete llsa; return true; } bool intra_area_prefix_lsa_print(TestInfo& info) { IntraAreaPrefixLsa *iaplsa = new IntraAreaPrefixLsa(OspfTypes::V3); populate_intra_area_prefix_lsa(iaplsa, OspfTypes::V3); DOUT(info) << iaplsa->str() << endl; delete iaplsa; return true; } bool router_lsa_compare(TestInfo& info, OspfTypes::Version version) { RouterLsa *rlsa1= new RouterLsa(version); populate_router_lsa(rlsa1, version); DOUT(info) << rlsa1->str() << endl; // Encode the Router-LSA. rlsa1->encode(); size_t len1; uint8_t *ptr1 = rlsa1->lsa(len1); // Now decode the packet. // Create a new packet to provide the decoder. RouterLsa *rlsa2= new RouterLsa(version); Lsa::LsaRef rlsa3 = rlsa2->decode(ptr1, len1); DOUT(info) << rlsa3->str() << endl; // Encode the second packet and compare. rlsa3->encode(); DOUT(info) << rlsa3->str() << endl; size_t len2; uint8_t *ptr2 = rlsa3->lsa(len2); vector pkt1; fill_vector(pkt1, ptr1, len1); vector pkt2; fill_vector(pkt2, ptr2, len2); if (!compare_packets(info, pkt1, pkt2)) return false; if (!compare_packet_strings(info, rlsa1->str(), rlsa3->str())) return false; delete rlsa1; delete rlsa2; return true; } bool network_lsa_compare(TestInfo& info, OspfTypes::Version version) { NetworkLsa *nlsa1= new NetworkLsa(version); populate_network_lsa(nlsa1, version); DOUT(info) << nlsa1->str() << endl; // Encode the Network-LSA. nlsa1->encode(); size_t len1; uint8_t *ptr1 = nlsa1->lsa(len1); // Now decode the packet. // Create a new packet to provide the decoder. NetworkLsa *nlsa2= new NetworkLsa(version); Lsa::LsaRef nlsa3 = nlsa2->decode(ptr1, len1); DOUT(info) << nlsa3->str() << endl; // Encode the second packet and compare. nlsa3->encode(); DOUT(info) << nlsa3->str() << endl; size_t len2; uint8_t *ptr2 = nlsa3->lsa(len2); vector pkt1; fill_vector(pkt1, ptr1, len1); vector pkt2; fill_vector(pkt2, ptr2, len2); if (!compare_packets(info, pkt1, pkt2)) return false; if (!compare_packet_strings(info, nlsa1->str(), nlsa3->str())) return false; delete nlsa1; delete nlsa2; return true; } bool summary_network_lsa_compare(TestInfo& info, OspfTypes::Version version) { SummaryNetworkLsa *snlsa1= new SummaryNetworkLsa(version); populate_summary_network_lsa(snlsa1, version); DOUT(info) << snlsa1->str() << endl; // Encode the SummaryNetwork-LSA. snlsa1->encode(); size_t len1; uint8_t *ptr1 = snlsa1->lsa(len1); // Now decode the packet. // Create a new packet to provide the decoder. SummaryNetworkLsa *snlsa2= new SummaryNetworkLsa(version); Lsa::LsaRef snlsa3 = snlsa2->decode(ptr1, len1); DOUT(info) << snlsa3->str() << endl; // Encode the second packet and compare. snlsa3->encode(); DOUT(info) << snlsa3->str() << endl; size_t len2; uint8_t *ptr2 = snlsa3->lsa(len2); vector pkt1; fill_vector(pkt1, ptr1, len1); vector pkt2; fill_vector(pkt2, ptr2, len2); if (!compare_packets(info, pkt1, pkt2)) return false; if (!compare_packet_strings(info, snlsa1->str(), snlsa3->str())) return false; delete snlsa1; delete snlsa2; return true; } bool summary_router_lsa_compare(TestInfo& info, OspfTypes::Version version) { SummaryRouterLsa *srlsa1= new SummaryRouterLsa(version); populate_summary_router_lsa(srlsa1, version); DOUT(info) << srlsa1->str() << endl; // Encode the SummaryRouter-LSA. srlsa1->encode(); size_t len1; uint8_t *ptr1 = srlsa1->lsa(len1); // Now decode the packet. // Create a new packet to provide the decoder. SummaryRouterLsa *srlsa2= new SummaryRouterLsa(version); Lsa::LsaRef srlsa3 = srlsa2->decode(ptr1, len1); DOUT(info) << srlsa3->str() << endl; // Encode the second packet and compare. srlsa3->encode(); DOUT(info) << srlsa3->str() << endl; size_t len2; uint8_t *ptr2 = srlsa3->lsa(len2); vector pkt1; fill_vector(pkt1, ptr1, len1); vector pkt2; fill_vector(pkt2, ptr2, len2); if (!compare_packets(info, pkt1, pkt2)) return false; if (!compare_packet_strings(info, srlsa1->str(), srlsa3->str())) return false; delete srlsa1; delete srlsa2; return true; } bool as_external_lsa_compare(TestInfo& info, OspfTypes::Version version) { ASExternalLsa *aelsa1= new ASExternalLsa(version); populate_as_external_lsa(aelsa1, version); DOUT(info) << aelsa1->str() << endl; // Encode the AS-External-LSA. aelsa1->encode(); size_t len1; uint8_t *ptr1 = aelsa1->lsa(len1); // Now decode the packet. // Create a new packet to provide the decoder. ASExternalLsa *aelsa2= new ASExternalLsa(version); Lsa::LsaRef aelsa3 = aelsa2->decode(ptr1, len1); DOUT(info) << aelsa3->str() << endl; // Encode the second packet and compare. aelsa3->encode(); DOUT(info) << aelsa3->str() << endl; size_t len2; uint8_t *ptr2 = aelsa3->lsa(len2); vector pkt1; fill_vector(pkt1, ptr1, len1); vector pkt2; fill_vector(pkt2, ptr2, len2); if (!compare_packets(info, pkt1, pkt2)) return false; if (!compare_packet_strings(info, aelsa1->str(), aelsa3->str())) return false; delete aelsa1; delete aelsa2; return true; } bool type7_lsa_compare(TestInfo& info, OspfTypes::Version version) { Type7Lsa *type71= new Type7Lsa(version); populate_as_external_lsa(type71, version); DOUT(info) << type71->str() << endl; // Encode the Type7-LSA. type71->encode(); size_t len1; uint8_t *ptr1 = type71->lsa(len1); // Now decode the packet. // Create a new packet to provide the decoder. Type7Lsa *type72= new Type7Lsa(version); Lsa::LsaRef type73 = type72->decode(ptr1, len1); DOUT(info) << type73->str() << endl; // Encode the second packet and compare. type73->encode(); DOUT(info) << type73->str() << endl; size_t len2; uint8_t *ptr2 = type73->lsa(len2); vector pkt1; fill_vector(pkt1, ptr1, len1); vector pkt2; fill_vector(pkt2, ptr2, len2); if (!compare_packets(info, pkt1, pkt2)) return false; if (!compare_packet_strings(info, type71->str(), type73->str())) return false; delete type71; delete type72; return true; } bool link_lsa_compare(TestInfo& info, OspfTypes::Version version) { LinkLsa *llsa1= new LinkLsa(version); populate_link_lsa(llsa1, version); DOUT(info) << llsa1->str() << endl; // Encode the Link-LSA. llsa1->encode(); size_t len1; uint8_t *ptr1 = llsa1->lsa(len1); // Now decode the packet. // Create a new packet to provide the decoder. LinkLsa *llsa2= new LinkLsa(version); Lsa::LsaRef llsa3 = llsa2->decode(ptr1, len1); DOUT(info) << llsa3->str() << endl; // Encode the second packet and compare. llsa3->encode(); DOUT(info) << llsa3->str() << endl; size_t len2; uint8_t *ptr2 = llsa3->lsa(len2); vector pkt1; fill_vector(pkt1, ptr1, len1); vector pkt2; fill_vector(pkt2, ptr2, len2); if (!compare_packets(info, pkt1, pkt2)) return false; if (!compare_packet_strings(info, llsa1->str(), llsa3->str())) return false; delete llsa1; delete llsa2; return true; } bool intra_area_prefix_lsa_compare(TestInfo& info, OspfTypes::Version version) { IntraAreaPrefixLsa *iaplsa1= new IntraAreaPrefixLsa(version); populate_intra_area_prefix_lsa(iaplsa1, version); DOUT(info) << iaplsa1->str() << endl; // Encode the Link-LSA. iaplsa1->encode(); size_t len1; uint8_t *ptr1 = iaplsa1->lsa(len1); // Now decode the packet. // Create a new packet to provide the decoder. IntraAreaPrefixLsa *iaplsa2= new IntraAreaPrefixLsa(version); Lsa::LsaRef iaplsa3 = iaplsa2->decode(ptr1, len1); DOUT(info) << iaplsa3->str() << endl; // Encode the second packet and compare. iaplsa3->encode(); DOUT(info) << iaplsa3->str() << endl; size_t len2; uint8_t *ptr2 = iaplsa3->lsa(len2); vector pkt1; fill_vector(pkt1, ptr1, len1); vector pkt2; fill_vector(pkt2, ptr2, len2); if (!compare_packets(info, pkt1, pkt2)) return false; if (!compare_packet_strings(info, iaplsa1->str(), iaplsa3->str())) return false; delete iaplsa1; delete iaplsa2; return true; } bool lsa_decoder1(TestInfo& info, OspfTypes::Version version) { LsaDecoder dec(version); RouterLsa router_lsa(version); populate_router_lsa(&router_lsa, version); router_lsa.encode(); size_t len; uint8_t *ptr = router_lsa.lsa(len); // An attempt to decode a packet with no decoders installed should // fail. try { dec.decode(ptr, len); } catch(InvalidPacket& e) { DOUT(info) << "Caught exception: " << e.str() << endl; return true; } return false; } bool lsa_decoder2(TestInfo& info, OspfTypes::Version version) { LsaDecoder dec(version); // Install a decoder for router LSAs. RouterLsa *router_lsa_decoder = new RouterLsa(version); dec.register_decoder(router_lsa_decoder); RouterLsa router_lsa(version); populate_router_lsa(&router_lsa, version); router_lsa.encode(); size_t len; uint8_t *ptr = router_lsa.lsa(len); // An attempt to decode a LSA with a decoder installed should succeed. Lsa::LsaRef lsaref = dec.decode(ptr, len); DOUT(info) << lsaref->str() << endl; return true; } bool lsa_decoder3(TestInfo& info, OspfTypes::Version version) { LsaDecoder dec(version); // Install the unknown LSA decoder. UnknownLsa *unknown_lsa_decoder = new UnknownLsa(version); dec.register_unknown_decoder(unknown_lsa_decoder); RouterLsa router_lsa(version); populate_router_lsa(&router_lsa, version); router_lsa.encode(); size_t len; uint8_t *ptr = router_lsa.lsa(len); // The only decoder installed is the unknown decoder which should // be able to handle the Router-LSA. Lsa::LsaRef lsaref = dec.decode(ptr, len); DOUT(info) << lsaref->str() << endl; return true; } int main(int argc, char **argv) { XorpUnexpectedHandler x(xorp_unexpected_handler); xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); TestMain t(argc, argv); string test = t.get_optional_args("-t", "--test", "run only the specified test"); t.complete_args_parsing(); struct test { string test_name; XorpCallback1::RefPtr cb; } tests[] = { {"hello_print", callback(hello_packet_print)}, {"data_description_print", callback(data_description_packet_print)}, {"link_state_update_print", callback(link_state_update_packet_print)}, {"link_state_request_print",callback(link_state_request_packet_print)}, {"link_state_acknowledgement_print", callback(link_state_acknowledgement_packet_print)}, {"router_lsa_print", callback(router_lsa_print)}, {"network_lsa_print", callback(network_lsa_print)}, {"summary_network_lsa_print", callback(summary_network_lsa_print)}, {"summary_router_lsa_print", callback(summary_router_lsa_print)}, {"as_external_lsa_print", callback(as_external_lsa_print)}, {"type7_lsa_print", callback(type7_lsa_print)}, {"link_lsa_print", callback(link_lsa_print)}, {"intra_area_prefix_lsa_print", callback(intra_area_prefix_lsa_print)}, {"hello_compareV2", callback(hello_packet_compare, OspfTypes::V2)}, {"hello_compareV3", callback(hello_packet_compare, OspfTypes::V3)}, {"hello_badlenV2", callback(hello_packet_bad_length, OspfTypes::V2)}, {"hello_badlenV3", callback(hello_packet_bad_length, OspfTypes::V2)}, {"ddp_compareV2", callback(data_description_packet_compare, OspfTypes::V2)}, {"ddp_compareV3", callback(data_description_packet_compare, OspfTypes::V3)}, {"ddp_badlenV2", callback(data_description_packet_bad_length, OspfTypes::V2)}, {"ddp_badlenV3", callback(data_description_packet_bad_length, OspfTypes::V3)}, {"lsup_compareV2", callback(link_state_update_packet_compare, OspfTypes::V2)}, {"lsup_compareV3", callback(link_state_update_packet_compare, OspfTypes::V3)}, {"lsup_badlenV2", callback(link_state_update_packet_bad_length, OspfTypes::V2)}, {"lsup_badlenV3", callback(link_state_update_packet_bad_length, OspfTypes::V3)}, {"lsrp_compareV2", callback(link_state_request_packet_compare, OspfTypes::V2)}, {"lsrp_compareV3", callback(link_state_request_packet_compare, OspfTypes::V3)}, {"lsrp_badlenV2", callback(link_state_request_packet_bad_length, OspfTypes::V2)}, {"lsrp_badlenV3", callback(link_state_request_packet_bad_length, OspfTypes::V3)}, {"lsap_compareV2", callback(link_state_acknowledgement_packet_compare, OspfTypes::V2)}, {"lsap_compareV3", callback(link_state_acknowledgement_packet_compare, OspfTypes::V3)}, {"lsap_badlenV2",callback(link_state_acknowledgement_packet_bad_length, OspfTypes::V2)}, {"lsap_badlenV3",callback(link_state_acknowledgement_packet_bad_length, OspfTypes::V3)}, {"packet_decoder1V2", callback(packet_decoder1, OspfTypes::V2)}, {"packet_decoder1V3", callback(packet_decoder1, OspfTypes::V3)}, {"packet_decoder2V2", callback(packet_decoder2, OspfTypes::V2)}, {"packet_decoder2V3", callback(packet_decoder2, OspfTypes::V3)}, {"packet_decode_bad1V2", callback(packet_decode_bad_packet, OspfTypes::V2, bad_packet1, sizeof(bad_packet1))}, {"packet_decode_bad2V2", callback(packet_decode_bad_packet, OspfTypes::V2, bad_packet2, sizeof(bad_packet2))}, {"packet_decode_bad3V2", callback(packet_decode_bad_packet, OspfTypes::V2, bad_packet3, sizeof(bad_packet3))}, {"router_lsa_compareV2", callback(router_lsa_compare, OspfTypes::V2)}, {"router_lsa_compareV3", callback(router_lsa_compare, OspfTypes::V3)}, {"network_lsa_compareV2",callback(network_lsa_compare, OspfTypes::V2)}, {"network_lsa_compareV3",callback(network_lsa_compare, OspfTypes::V3)}, {"summary_network_lsa_compareV2", callback(summary_network_lsa_compare, OspfTypes::V2)}, {"summary_network_lsa_compareV3", callback(summary_network_lsa_compare, OspfTypes::V3)}, {"summary_router_lsa_compareV2", callback(summary_router_lsa_compare, OspfTypes::V2)}, {"summary_router_lsa_compareV3", callback(summary_router_lsa_compare, OspfTypes::V3)}, {"as_external_lsa_compareV2", callback(as_external_lsa_compare, OspfTypes::V2)}, {"as_external_lsa_compareV3", callback(as_external_lsa_compare, OspfTypes::V3)}, {"type7_lsa_compareV2", callback(type7_lsa_compare, OspfTypes::V2)}, {"type7_lsa_compareV3", callback(type7_lsa_compare, OspfTypes::V3)}, {"link_lsa_compareV3", callback(link_lsa_compare, OspfTypes::V3)}, {"intra_area_prefix_lsa_compareV3", callback(intra_area_prefix_lsa_compare, OspfTypes::V3)}, {"lsa_decoder1V2", callback(lsa_decoder1, OspfTypes::V2)}, {"lsa_decoder1V3", callback(lsa_decoder1, OspfTypes::V3)}, {"lsa_decoder2V2", callback(lsa_decoder2, OspfTypes::V2)}, {"lsa_decoder2V3", callback(lsa_decoder2, OspfTypes::V3)}, {"lsa_decoder3V3", callback(lsa_decoder3, OspfTypes::V3)}, }; try { if (test.empty()) { for (size_t i = 0; i < sizeof(tests) / sizeof(struct test); i++) t.run(tests[i].test_name, tests[i].cb); } else { for (size_t i = 0; i < sizeof(tests) / sizeof(struct test); i++) if (test == tests[i].test_name) { t.run(tests[i].test_name, tests[i].cb); return t.exit(); } t.failed("No test with name " + test + " found\n"); } } catch(...) { xorp_catch_standard_exceptions(); } xlog_stop(); xlog_exit(); return t.exit(); } xorp/ospf/tests/test_build_lsa_main.cc0000664000076400007640000000433211421137511020277 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "ospf_module.h" #include "libxorp/xorp.h" #include "libxorp/test_main.hh" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/callback.hh" #include "libxorp/ipv4.hh" #include "libxorp/ipv6.hh" #include "libxorp/ipnet.hh" #include "libxorp/status_codes.h" #include "libxorp/service.hh" #include "libxorp/eventloop.hh" #include "ospf.hh" #include "test_args.hh" #include "test_build_lsa.hh" int main(int argc, char **argv) { XorpUnexpectedHandler x(xorp_unexpected_handler); xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); TestMain t(argc, argv); string lsa_description = t.get_optional_args("-l", "--lsa", "lsa description"); bool v2 = t.get_optional_flag("-2", "--OSPFv2", "OSPFv2"); bool v3 = t.get_optional_flag("-3", "--OSPFv3", "OSPFv3"); t.complete_args_parsing(); if (0 != t.exit()) return t.exit(); OspfTypes::Version version; if (v2 == v3 || v2) { version = OspfTypes::V2; } else { version = OspfTypes::V3; } Args args(lsa_description); BuildLsa blsa(version); try { Lsa *lsa = blsa.generate(args); if (0 == lsa) return -1; printf("%s\n", cstring(*lsa)); } catch(...) { xorp_print_standard_exceptions(); return -1; } xlog_stop(); xlog_exit(); return 0; } xorp/ospf/tests/test_peering.cc0000664000076400007640000003647711540224231017003 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #define DEBUG_LOGGING #define DEBUG_PRINT_FUNCTION_NAME #include "ospf_module.h" #include "libxorp/xorp.h" #include "libxorp/test_main.hh" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/ipv4.hh" #include "libxorp/ipv6.hh" #include "libxorp/ipnet.hh" #include "libxorp/status_codes.h" #include "libxorp/service.hh" #include "libxorp/eventloop.hh" #include #include "ospf.hh" #include "debug_io.hh" // Make sure that all tests free up any memory that they use. This will // allow us to use the leak checker program. /** * Bind together a set of IO classes in order to form a virtual subnet * for testing, one instance per subnet. */ template class EmulateSubnet { public: EmulateSubnet(TestInfo& info, EventLoop& eventloop) : _info(info), _eventloop(eventloop), _queue_add(1), _queue_remove(2) {} /** * Receive frames * * All frames generated by an OSPF instances arrive here. Note * that a frame arriving from one OSPF instance is not sent * directly to another. The frames are queued and only when OSPF * instance gives back control to the eventloop are the frames * forwarded. This ensures that two OSPF instances are not in each * others call graphs, which can cause re-entrancy problems. */ void receive_frames(const string& interface, const string& vif, A dst, A src, uint8_t* data, uint32_t len, const string instance) { DOUT(_info) << "receive(" << instance << "," << interface << "," << vif << "," << dst.str() << "," << src.str() << "," << len << "...)" << endl; _queue[_queue_add]. push_back(Frame(interface, vif, dst, src, data, len, instance)); if (_timer.scheduled()) return; XLOG_ASSERT(_queue[_queue_add].size() == 1); _timer = _eventloop. new_oneoff_after_ms(10, callback(this, &EmulateSubnet::next)); } /** * Bind together a set of interfaces. */ void bind_interfaces(const string& instance, const string& interface, const string& vif, DebugIO& io) { DOUT(_info) << instance << ": " << interface << "/" << vif << endl; io.register_forward(callback(this, &EmulateSubnet::receive_frames, instance)); _ios[Multiplex(instance, interface, vif)] = &io; } private: TestInfo& _info; EventLoop& _eventloop; struct Multiplex { Multiplex(const string& instance, const string& interface, const string& vif) : _instance(instance), _interface(interface), _vif(vif) {} bool operator <(const Multiplex& him) const { return him._instance < _instance; } const string _instance; const string _interface; const string _vif; }; map *> _ios; struct Frame { Frame(string interface, const string vif, A dst, A src, uint8_t* data, uint32_t len, string instance) : _interface(interface), _vif(vif), _dst(dst), _src(src), _instance(instance) { _pkt.resize(len); memcpy(&_pkt[0], data, len); } string _interface; string _vif; A _dst; A _src; vector _pkt; string _instance; }; XorpTimer _timer; deque _queue[2]; int _queue_add; int _queue_remove; void next() { if (0 == _queue_add) { _queue_add = 1; _queue_remove = 0; } else { _queue_add = 0; _queue_remove = 1; } while (!_queue[_queue_remove].empty()) { Frame frame = _queue[_queue_remove].front(); _queue[_queue_remove].pop_front(); forward(frame); } } void forward(Frame frame) { uint8_t* data = &frame._pkt[0]; uint32_t len = frame._pkt.size(); typename map *>::iterator i; for(i = _ios.begin(); i != _ios.end(); i++) { Multiplex m = (*i).first; if (m._instance == frame._instance) continue; DOUT(_info) << "Send to: " << m._instance << ": " << m._interface << "/" << m._vif << " " << len << endl; (*i).second->receive(m._interface, m._vif, frame._dst, frame._src, data, len); } } }; // Reduce the hello interval from 10 to 1 second to speed up the test. uint16_t hello_interval = 1; // Do not stop a tests allow it to run forever to observe timers. bool forever = false; /** * Configure a single peering. Nothing is really expected to go wrong * but the test is useful to verify the normal path through the code. */ template bool single_peer(TestInfo& info, OspfTypes::Version version) { EventLoop eventloop; DebugIO io(info, version, eventloop); io.startup(); Ospf ospf(version, eventloop, &io); ospf.set_router_id(set_id("0.0.0.1")); OspfTypes::AreaID area = set_id("128.16.64.16"); const uint16_t interface_cost = 10; const uint16_t inftransdelay = 2; PeerManager& pm = ospf.get_peer_manager(); // Create an area if (!pm.create_area_router(area, OspfTypes::NORMAL)) { DOUT(info) << "Failed to create area\n"; return false; } // Create a peer associated with this area. const string interface = "eth0"; const string vif = "vif0"; A src; switch(src.ip_version()) { case 4: src = "192.150.187.78"; break; case 6: src = "2001:468:e21:c800:220:edff:fe61:f033"; break; default: XLOG_FATAL("Unknown IP version %d", src.ip_version()); break; } OspfTypes::PeerID peerid = pm. create_peer(interface, vif, src, OspfTypes::BROADCAST, area); switch(version) { case OspfTypes::V2: break; case OspfTypes::V3: pm.activate_peer(interface, vif, area); break; } if (!ospf.set_hello_interval(interface, vif, area, hello_interval)) { DOUT(info) << "Failed to set hello interval\n"; return false; } if (!ospf.set_router_dead_interval(interface, vif, area, 4 * hello_interval)) { DOUT(info) << "Failed to set router dead interval\n"; return false; } if (!ospf.set_interface_cost(interface, vif, area, interface_cost)) { DOUT(info) << "Failed to set interface cost\n"; return false; } if (!ospf.set_inftransdelay(interface, vif,area, inftransdelay)) { DOUT(info) << "Failed to set inftransdelay\n"; return false; } // Bring the peering up if (!pm.set_state_peer(peerid, true)) { DOUT(info) << "Failed enable peer\n"; return false; } if (forever) while (ospf.running()) eventloop.run(); bool timeout = false; XorpTimer t = eventloop.set_flag_after(TimeVal(10 * hello_interval ,0), &timeout); while (ospf.running() && !timeout) { eventloop.run(); if (2 == io.packets()) break; } if (timeout) { DOUT(info) << "No packets sent, test timed out\n"; return false; } // Take the peering down if (!pm.set_state_peer(peerid, false)) { DOUT(info) << "Failed to disable peer\n"; return false; } // Delete the peer. if (!pm.delete_peer(peerid)) { DOUT(info) << "Failed to delete peer\n"; return false; } // Delete the area if (!pm.destroy_area_router(area)) { DOUT(info) << "Failed to delete area\n"; return false; } return true; } enum Stagger { NOSTAGGER, STAGGER1, STAGGER2}; string suppress; /** * Configure two peerings. Nothing is really expected to go wrong * but the test is useful to verify the normal path through the code. */ template bool two_peers(TestInfo& info, OspfTypes::Version version, OspfTypes:: LinkType linktype, Stagger stagger) { EventLoop eventloop; bool verbose[2]; verbose[0] = info.verbose(); verbose[1] = info.verbose(); if (suppress == "") ; else if (suppress == "ospf1") verbose[0] = false; else if (suppress == "ospf2") verbose[1] = false; else { info.out() << "illegal value for suppress" << suppress << endl; return false; } TestInfo info1(info.test_name() + "(ospf1)" , verbose[0], info.verbose_level(), info.out()); TestInfo info2(info.test_name() + "(ospf2)" , verbose[1], info.verbose_level(), info.out()); DebugIO io_1(info1, version, eventloop); io_1.startup(); DebugIO io_2(info2, version, eventloop); io_2.startup(); Ospf ospf_1(version, eventloop, &io_1); Ospf ospf_2(version, eventloop, &io_2); ospf_1.set_router_id(set_id("192.150.187.1")); ospf_2.set_router_id(set_id("192.150.187.2")); const uint16_t interface_cost = 10; const uint16_t inftransdelay = 20; OspfTypes::AreaID area = set_id("128.16.64.16"); PeerManager& pm_1 = ospf_1.get_peer_manager(); PeerManager& pm_2 = ospf_2.get_peer_manager(); pm_1.create_area_router(area, OspfTypes::NORMAL); pm_2.create_area_router(area, OspfTypes::NORMAL); const string interface_1 = "eth1"; const string interface_2 = "eth2"; const string vif_1 = "vif1"; const string vif_2 = "vif2"; A src_1, src_2; switch(src_1.ip_version()) { case 4: src_1 = "10.10.10.1"; src_2 = "10.10.10.2"; break; case 6: src_1 = "2001::1"; src_2 = "2001::2"; break; default: XLOG_FATAL("Unknown IP version %d", src_1.ip_version()); break; } OspfTypes::PeerID peerid_1 = pm_1. create_peer(interface_1, vif_1, src_1, linktype, area); OspfTypes::PeerID peerid_2 = pm_2. create_peer(interface_2, vif_2, src_2, linktype, area); switch(version) { case OspfTypes::V2: break; case OspfTypes::V3: pm_1.activate_peer(interface_1, vif_1, area); pm_2.activate_peer(interface_2, vif_2, area); break; } switch(linktype) { case OspfTypes::PointToPoint: pm_1.add_neighbour(peerid_1, area, src_2, ospf_2.get_router_id()); pm_2.add_neighbour(peerid_2, area, src_1, ospf_1.get_router_id()); break; case OspfTypes::BROADCAST: break; case OspfTypes::NBMA: case OspfTypes::PointToMultiPoint: case OspfTypes::VirtualLink: XLOG_UNFINISHED(); break; } ospf_1.set_hello_interval(interface_1, vif_1, area, hello_interval); ospf_1.set_router_dead_interval(interface_1, vif_1, area, 4 * hello_interval); ospf_1.set_interface_cost(interface_1, vif_1, area, interface_cost); ospf_1.set_inftransdelay(interface_1, vif_1, area, inftransdelay); ospf_2.set_hello_interval(interface_2, vif_2, area, hello_interval); ospf_2.set_router_dead_interval(interface_2, vif_2, area, 4 * hello_interval); ospf_2.set_interface_cost(interface_2, vif_2, area, interface_cost); ospf_2.set_inftransdelay(interface_2, vif_2, area, inftransdelay); EmulateSubnet emu(info, eventloop); emu.bind_interfaces("ospf1", interface_1, vif_1, io_1); emu.bind_interfaces("ospf2", interface_2, vif_2, io_2); if (STAGGER1 != stagger) pm_1.set_state_peer(peerid_1, true); if (STAGGER2 != stagger) pm_2.set_state_peer(peerid_2, true); if (forever) while (ospf_1.running() && ospf_2.running()) eventloop.run(); bool timeout = false; XorpTimer t = eventloop.set_flag_after(TimeVal(20 * hello_interval, 0), &timeout); const int expected = 32; while (ospf_1.running() && ospf_2.running() && !timeout) { eventloop.run(); if (expected <= io_1.packets() + io_2.packets()) break; if (STAGGER1 == stagger && 1 == io_2.packets()) pm_1.set_state_peer(peerid_1, true); if (STAGGER2 == stagger && 1 == io_1.packets()) pm_2.set_state_peer(peerid_2, true); } if (timeout) { DOUT(info) << io_1.packets() << " packets sent " << expected << " expected test timed out\n"; return false; } // Delete the neighbours switch(linktype) { case OspfTypes::PointToPoint: pm_1.remove_neighbour(peerid_1, area, src_2, ospf_2.get_router_id()); pm_2.remove_neighbour(peerid_2, area, src_1, ospf_1.get_router_id()); break; case OspfTypes::BROADCAST: break; case OspfTypes::NBMA: case OspfTypes::PointToMultiPoint: case OspfTypes::VirtualLink: XLOG_UNFINISHED(); break; } // Take the peering down pm_1.set_state_peer(peerid_1, false); pm_2.set_state_peer(peerid_2, false); // Delete the peers. if (!pm_1.delete_peer(peerid_1)) { DOUT(info) << "Failed to delete peer\n"; return false; } if (!pm_2.delete_peer(peerid_2)) { DOUT(info) << "Failed to delete peer\n"; return false; } // Delete the areas. if (!pm_1.destroy_area_router(area)) { DOUT(info) << "Failed to delete area\n"; return false; } // Delete the areas. if (!pm_2.destroy_area_router(area)) { DOUT(info) << "Failed to delete area\n"; return false; } return true; } int main(int argc, char **argv) { XorpUnexpectedHandler x(xorp_unexpected_handler); xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); TestMain t(argc, argv); string test = t.get_optional_args("-t", "--test", "run only the specified test"); string hello_interval_arg = t.get_optional_args("-h", "--hello", "hello interval"); suppress = t.get_optional_args("-s", "--suppress", "verbose output"); forever = t.get_optional_flag("-f", "--forever", "Don't terminate test"); t.complete_args_parsing(); if (!hello_interval_arg.empty()) hello_interval = atoi(hello_interval_arg.c_str()); struct test { string test_name; XorpCallback1::RefPtr cb; } tests[] = { {"single_peerV2", callback(single_peer, OspfTypes::V2)}, {"single_peerV3", callback(single_peer, OspfTypes::V3)}, {"two_peersV2", callback(two_peers, OspfTypes::V2, OspfTypes::BROADCAST, NOSTAGGER)}, {"two_peersV3", callback(two_peers, OspfTypes::V3, OspfTypes::BROADCAST, NOSTAGGER)}, {"two_peersV2s1", callback(two_peers, OspfTypes::V2, OspfTypes::BROADCAST, STAGGER1)}, {"two_peersV3s1", callback(two_peers, OspfTypes::V3, OspfTypes::BROADCAST, STAGGER1)}, {"two_peersV2s2", callback(two_peers, OspfTypes::V2, OspfTypes::BROADCAST, STAGGER2)}, {"two_peersV3s2", callback(two_peers, OspfTypes::V3, OspfTypes::BROADCAST, STAGGER2)}, {"p2pV2", callback(two_peers, OspfTypes::V2, OspfTypes::PointToPoint, NOSTAGGER)}, {"p2pV3", callback(two_peers, OspfTypes::V3, OspfTypes::PointToPoint, NOSTAGGER)}, }; try { if (test.empty()) { for (size_t i = 0; i < sizeof(tests) / sizeof(struct test); i++) t.run(tests[i].test_name, tests[i].cb); } else { for (size_t i = 0; i < sizeof(tests) / sizeof(struct test); i++) if (test == tests[i].test_name) { t.run(tests[i].test_name, tests[i].cb); return t.exit(); } t.failed("No test with name " + test + " found\n"); } } catch(...) { xorp_catch_standard_exceptions(); } xlog_stop(); xlog_exit(); return t.exit(); } xorp/ospf/tests/test_routing_table.cc0000664000076400007640000004047511540225531020205 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #define DEBUG_LOGGING #define DEBUG_PRINT_FUNCTION_NAME #include "ospf_module.h" #include "libxorp/xorp.h" #include "libxorp/test_main.hh" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/callback.hh" #include "libxorp/tlv.hh" #include "libxorp/ipv4.hh" #include "libxorp/ipv6.hh" #include "libxorp/ipnet.hh" #include "libxorp/status_codes.h" #include "libxorp/service.hh" #include "libxorp/eventloop.hh" #include #include "libproto/spt.hh" #include "ospf.hh" #include "debug_io.hh" #include "delay_queue.hh" #include "vertex.hh" #include "area_router.hh" // Make sure that all tests free up any memory that they use. This will // allow us to use the leak checker program. /** * Simple test to verify that assigning a RouteEntry works. */ template bool rt1(TestInfo& info, OspfTypes::Version version) { RouteEntry rt1; RouteEntry rt2; OspfTypes::VertexType destination_type = OspfTypes::Network; bool discard = true; bool direct = true; uint32_t address = 108; OspfTypes::AreaID area = 55; typename RouteEntry::PathType path_type = RouteEntry::type2; uint32_t cost = 2007; uint32_t type_2_cost = 1000; A nexthop; switch(nexthop.ip_version()) { case 4: nexthop = "192.150.187.78"; break; case 6: nexthop = "2001:468:e21:c800:220:edff:fe61:f033"; break; default: XLOG_FATAL("Unknown IP version %d", nexthop.ip_version()); break; } uint32_t nexthop_id = 2000000; uint32_t advertising_router = 1500; Lsa::LsaRef lsar(new RouterLsa(version)); bool filtered = true; rt1.set_destination_type(destination_type); rt1.set_discard(discard); rt1.set_directly_connected(direct); rt1.set_address(address); rt1.set_area(area); rt1.set_path_type(RouteEntry::type2); rt1.set_cost(cost); rt1.set_type_2_cost(type_2_cost); rt1.set_nexthop(nexthop); rt1.set_nexthop_id(nexthop_id); rt1.set_advertising_router(advertising_router); rt1.set_lsa(lsar); rt1.set_filtered(filtered); rt2 = rt1; if (destination_type != rt2.get_destination_type()) { DOUT(info) << "Failed to copy value of destination type\n"; return false; } if (discard != rt2.get_discard()) { DOUT(info) << "Failed to copy discard value\n"; return false; } if (direct != rt2.get_directly_connected()) { DOUT(info) << "Failed to copy directly connected value\n"; return false; } if (address != rt2.get_address()) { DOUT(info) << "Failed to copy value of address\n"; return false; } if (area != rt2.get_area()) { DOUT(info) << "Failed to copy value of area\n"; return false; } if (path_type != rt2.get_path_type()) { DOUT(info) << "Failed to copy value of path type\n"; return false; } if (cost != rt2.get_cost()) { DOUT(info) << "Failed to copy value of cost\n"; return false; } if (type_2_cost != rt2.get_type_2_cost()) { DOUT(info) << "Failed to copy value of type 2 cost\n"; return false; } if (nexthop != rt2.get_nexthop()) { DOUT(info) << "Failed to copy value of nexthop\n"; return false; } if (nexthop_id != rt2.get_nexthop_id()) { DOUT(info) << "Failed to copy value of nexthop ID\n"; return false; } if (advertising_router != rt2.get_advertising_router()) { DOUT(info) << "Failed to copy value of advertising router\n"; return false; } if (lsar != rt2.get_lsa()) { DOUT(info) << "Failed to copy value of LSA\n"; return false; } if (filtered != rt2.get_filtered()) { DOUT(info) << "Failed to copy filtered value\n"; return false; } return true; } /** * Verify that an internal routing entry correctly recomputes the * winning route. */ template bool ire1(TestInfo& info, OspfTypes::Version /*version*/) { RouteEntry rt1; RouteEntry rt2; InternalRouteEntry ire; rt1.set_area(1); rt2.set_area(2); ire.add_entry(1, rt1); ire.add_entry(2, rt2); RouteEntry& win = ire.get_entry(); if (win.get_area() != rt2.get_area()) { DOUT(info) << "The entry with the largest area ID should win\n"; return false; } return true; } /** * Verify that copying a internal routing entry works. */ template bool ire2(TestInfo& info, OspfTypes::Version /*version*/) { RouteEntry rt1; RouteEntry rt2; InternalRouteEntry *ire = new InternalRouteEntry; rt1.set_area(1); rt2.set_area(2); rt1.set_cost(5); rt2.set_cost(5); ire->add_entry(1, rt1); ire->add_entry(2, rt2); RouteEntry& win = ire->get_entry(); if (win.get_area() != rt2.get_area()) { DOUT(info) << "The entry with the largest area ID should win\n"; delete ire; return false; } InternalRouteEntry ire_copy = *ire; delete ire; RouteEntry& rtc = ire_copy.get_entry(); if (rtc.get_cost() != 5) { DOUT(info) << "Cost should be 5 not " << rtc.get_cost() << endl; return false; } return true; } /** * Verify that the trie code works as advertised. */ template bool trie1(TestInfo& info, OspfTypes::Version version) { Trie > *current; current = new Trie >; typename Trie >::iterator i; InternalRouteEntry ire; IPNet net; switch(net.masked_addr().ip_version()) { case 4: net = IPNet("192.150.187.108/32"); break; case 6: net = IPNet("2001:468:e21:c800:220:edff:fe61:f033/128"); break; default: XLOG_FATAL("Unknown IP version %d", net.masked_addr().ip_version()); break; } current->insert(net, ire); i = current->lookup_node(net); if (current->end() == i) { DOUT(info) << "Key not found\n"; delete current; return false; } RouterLsa *rlsa = new RouterLsa(version); Lsa::LsaRef lsar(rlsa); InternalRouteEntry& irentry1 = i.payload(); A nexthop; RouteEntry rt1; rt1.set_lsa(lsar); rt1.set_cost(1); nexthop = rt1.get_nexthop(); rt1.set_nexthop(++nexthop); RouteEntry rt2; rt2.set_lsa(lsar); rt2.set_cost(2); rt2.set_nexthop(++nexthop); OspfTypes::AreaID az = set_id("0.0.0.0"); OspfTypes::AreaID a1 = set_id("0.0.0.1"); irentry1.add_entry(az, rt1); i = current->lookup_node(net); if (current->end() == i) { DOUT(info) << "Key not found\n"; delete current; return false; } InternalRouteEntry& irentry2 = i.payload(); irentry2.add_entry(a1, rt2); i = current->lookup_node(net); if (current->end() == i) { DOUT(info) << "Key not found\n"; delete current; return false; } InternalRouteEntry& irentry3 = i.payload(); bool winner_changed; irentry3.delete_entry(az, winner_changed); typename Trie >::iterator tic; for (tic = current->begin(); tic != current->end(); tic++) { RouteEntry& rte = tic.payload().get_entry(); DOUT(info) << "Key: " << tic.key().str() << " Payload: " << rte.str() << endl; } delete current; return true; } /** * Call trie1 one hundred times. */ template bool trie2(TestInfo& info, OspfTypes::Version version) { for (int i = 0; i < 100; i++) if (!trie1(info, version)) return false; return true; } /** * Verify that no host routes pointing at a router make it through to * the routing table. */ template bool routing1(TestInfo& info, OspfTypes::Version /*version*/) { OspfTypes::Version version = OspfTypes::V2; EventLoop eventloop; DebugIO io(info, version, eventloop); io.startup(); Ospf ospf(version, eventloop, &io); ospf.trace().all(info.verbose()); OspfTypes::AreaID az = set_id("0.0.0.0"); OspfTypes::AreaID a1 = set_id("0.0.0.1"); RoutingTable& routing_table = ospf.get_routing_table(); RouterLsa *rlsa = new RouterLsa(version); Lsa::LsaRef lsar(rlsa); RouteEntry route_entry1; RouteEntry route_entry2; route_entry1.set_directly_connected(true); route_entry2.set_directly_connected(true); /****************************************/ routing_table.begin(a1); route_entry1.set_area(a1); route_entry1.set_lsa(lsar); routing_table.add_entry(a1, IPNet("192.150.187.108/32"), route_entry1, "entry1a"); routing_table.end(); /****************************************/ routing_table.begin(a1); route_entry1.set_area(a1); route_entry1.set_lsa(lsar); routing_table.add_entry(a1, IPNet("192.150.187.108/32"), route_entry1, "entry1b"); routing_table.end(); /****************************************/ routing_table.begin(az); route_entry2.set_area(az); route_entry2.set_lsa(lsar); routing_table.add_entry(az, IPNet("192.150.187.108/32"), route_entry2, "entry2"); routing_table.end(); /****************************************/ routing_table.begin(a1); routing_table.end(); /****************************************/ routing_table.begin(az); routing_table.end(); if (0 != io.routing_table_size()) { DOUT(info) << "Routing table should be empty not " << io.routing_table_size() << endl; return false; } return true; } /** * At the time of writing OSPFv3 behaved differently to OSPFv2 with * respect to router entries. In OSPFv2 router entries are added as * host routes as well as being indexed by the advertising router. In * OSPFv3 router entries are only in the Adv database. Just verify * that add and replace work correctly for OSPFv3 */ template bool routing2(TestInfo& info, OspfTypes::Version /*version*/) { OspfTypes::Version version = OspfTypes::V3; EventLoop eventloop; DebugIO io(info, version, eventloop); io.startup(); Ospf ospf(version, eventloop, &io); ospf.trace().all(info.verbose()); OspfTypes::AreaID az = set_id("0.0.0.0"); OspfTypes::AreaID a1 = set_id("0.0.0.1"); RoutingTable& routing_table = ospf.get_routing_table(); RouterLsa *rlsa = new RouterLsa(version); Lsa::LsaRef lsar(rlsa); IPNet netv("5f00:0000:c001:0200::/56"); IPNet net; RouteEntry route_entry1; RouteEntry route_entry2; route_entry1.set_directly_connected(true); route_entry2.set_directly_connected(true); route_entry1.set_destination_type(OspfTypes::Router); route_entry2.set_destination_type(OspfTypes::Router); route_entry1.set_router_id(set_id("0.0.0.1")); route_entry2.set_router_id(set_id("0.0.0.1")); /****************************************/ routing_table.begin(a1); route_entry1.set_area(a1); route_entry1.set_lsa(lsar); // Add an entry with a non zero net if (routing_table.add_entry(a1, netv, route_entry1)) { DOUT(info) << "Accepted an entry with a non-zero net " << route_entry1.str() << endl; return false; } route_entry2.set_area(a1); route_entry2.set_lsa(lsar); // Replace an entry with a non zero net if (routing_table.replace_entry(a1, netv, route_entry2)) { DOUT(info) << "Accepted an entry with a non-zero net " << route_entry1.str() << endl; return false; } routing_table.end(); /****************************************/ routing_table.begin(a1); route_entry1.set_area(a1); route_entry1.set_lsa(lsar); // Add an ordinary entry should work if (!routing_table.add_entry(a1, net, route_entry1)) { DOUT(info) << "Failed to add a simple entry " << route_entry1.str() << endl; return false; } route_entry2.set_area(a1); route_entry2.set_lsa(lsar); // Add an entry with an existing advertising router this should generate // an error. if (routing_table.add_entry(a1, net, route_entry2)) { DOUT(info) << "Accepted an entry with the same advertising router " << route_entry2.str() << endl; return false; } routing_table.end(); /****************************************/ routing_table.begin(a1); route_entry1.set_area(a1); route_entry1.set_lsa(lsar); // Add an ordinary entry should work if (!routing_table.add_entry(a1, net, route_entry1)) { DOUT(info) << "Failed to add a simple entry " << route_entry1.str() << endl; return false; } route_entry2.set_area(a1); route_entry2.set_lsa(lsar); // Replace entry if (!routing_table.replace_entry(a1, net, route_entry2)) { DOUT(info) << "Replacing entry failed " << route_entry2.str() << endl; return false; } routing_table.end(); /****************************************/ routing_table.begin(a1); route_entry1.set_area(a1); route_entry1.set_lsa(lsar); // Perform a replace with nothing to replace. if (routing_table.replace_entry(a1, net, route_entry1)) { DOUT(info) << "Replaced an entry that didn't exist " << route_entry1.str() << endl; return false; } routing_table.end(); /****************************************/ routing_table.begin(a1); route_entry1.set_area(a1); route_entry1.set_lsa(lsar); routing_table.add_entry(a1, net, route_entry1); routing_table.end(); /****************************************/ routing_table.begin(a1); route_entry1.set_area(a1); route_entry1.set_lsa(lsar); routing_table.add_entry(a1, net, route_entry1); routing_table.end(); /****************************************/ routing_table.begin(az); route_entry2.set_area(az); route_entry2.set_lsa(lsar); routing_table.add_entry(az, net, route_entry2); routing_table.end(); /****************************************/ routing_table.begin(a1); routing_table.end(); /****************************************/ routing_table.begin(az); routing_table.end(); if (0 != io.routing_table_size()) { DOUT(info) << "Routing table should be empty not " << io.routing_table_size() << endl; return false; } return true; } int main(int argc, char **argv) { XorpUnexpectedHandler x(xorp_unexpected_handler); xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); TestMain t(argc, argv); string test = t.get_optional_args("-t", "--test", "run only the specified test"); t.complete_args_parsing(); struct test { string test_name; XorpCallback1::RefPtr cb; } tests[] = { {"rt1v2", callback(rt1, OspfTypes::V2)}, {"rt1v3", callback(rt1, OspfTypes::V3)}, {"ire1v2", callback(ire1, OspfTypes::V2)}, {"ire1v3", callback(ire1, OspfTypes::V3)}, {"ire2v2", callback(ire2, OspfTypes::V2)}, {"ire2v3", callback(ire2, OspfTypes::V3)}, {"trie1v2", callback(trie1, OspfTypes::V2)}, {"trie1v3", callback(trie1, OspfTypes::V3)}, {"trie2v2", callback(trie2, OspfTypes::V2)}, {"trie2v3", callback(trie2, OspfTypes::V3)}, {"r1v2", callback(routing1, OspfTypes::V2)}, // {"r1v3", callback(routing1, OspfTypes::V3)}, // {"r2v2", callback(routing1, OspfTypes::V2)}, // {"r2v3", callback(routing2, OspfTypes::V3)}, }; try { if (test.empty()) { for (size_t i = 0; i < sizeof(tests) / sizeof(struct test); i++) t.run(tests[i].test_name, tests[i].cb); } else { for (size_t i = 0; i < sizeof(tests) / sizeof(struct test); i++) if (test == tests[i].test_name) { t.run(tests[i].test_name, tests[i].cb); return t.exit(); } t.failed("No test with name " + test + " found\n"); } } catch(...) { xorp_catch_standard_exceptions(); } xlog_stop(); xlog_exit(); return t.exit(); } xorp/ospf/tests/test_routing_database.cc0000664000076400007640000002365711540224231020661 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #define DEBUG_LOGGING #define DEBUG_PRINT_FUNCTION_NAME #include "ospf_module.h" #include "libxorp/xorp.h" #include "libxorp/test_main.hh" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/callback.hh" #include "libxorp/tlv.hh" #include "libxorp/ipv4.hh" #include "libxorp/ipv6.hh" #include "libxorp/ipnet.hh" #include "libxorp/status_codes.h" #include "libxorp/service.hh" #include "libxorp/eventloop.hh" #include "libxorp/tokenize.hh" #include #include "libproto/spt.hh" #include "ospf.hh" #include "debug_io.hh" #include "delay_queue.hh" #include "vertex.hh" #include "area_router.hh" #include "test_common.hh" // Make sure that all tests free up any memory that they use. This will // allow us to use the leak checker program. inline bool type_check(TestInfo& info, string message, uint32_t expected, uint32_t actual) { if (expected != actual) { DOUT(info) << message << " should be " << expected << " is " << actual << endl; return false; } return true; } /** * Read in LSA database files written by print_lsas.cc and pretty * print it. */ bool pp(TestInfo& info, string fname) { if (0 == fname.size()) { DOUT(info) << "No filename supplied\n"; return true; } OspfTypes::Version version = OspfTypes::V2; Tlv tlv; if (!tlv.open(fname, true /* read */)) { DOUT(info) << "Failed to open " << fname << endl; return false; } vector data; uint32_t type; while (tlv.read(type, data)) { switch(static_cast(type)) { case TLV_VERSION: uint32_t tlv_version; if (!tlv.get32(data, 0, tlv_version)) { return false; } DOUT(info) << "Version: " << tlv_version << endl; break; case TLV_SYSTEM_INFO: data.resize(data.size() + 1); data[data.size() - 1] = 0; DOUT(info) << "Built on " << &data[0] << endl; break; case TLV_OSPF_VERSION: uint32_t ospf_version; if (!tlv.get32(data, 0, ospf_version)) { return false; } switch(ospf_version) { case 2: version = OspfTypes::V2; break; case 3: version = OspfTypes::V3; break; } DOUT(info) << "OSPFv" << ospf_version << endl; break; case TLV_AREA: OspfTypes::AreaID area; if (!tlv.get32(data, 0, area)) { return false; } DOUT(info) << "Area: " << pr_id(area) << endl; break; case TLV_LSA: // By the time we get here we are assuming that // TLV_OSPF_VERSION has been read. LsaDecoder lsa_decoder(version); initialise_lsa_decoder(version, lsa_decoder); size_t len = data.size(); Lsa::LsaRef lsar = lsa_decoder.decode(&data[0], len); DOUT(info) << lsar->str() << endl; break; } } return true; } /** * Read in LSA database files written by print_lsas.cc and run the * routing computation. */ template bool routing(TestInfo& info, OspfTypes::Version version, string fname, string areas) { if (0 == fname.size()) { DOUT(info) << "No filename supplied\n"; return true; } EventLoop eventloop; TestInfo ioinfo("routing", true, 0, info.out()); DebugIO io(ioinfo, version, eventloop); io.startup(); Ospf ospf(version, eventloop, &io); ospf.trace().all(info.verbose()); ospf.set_testing(true); Tlv tlv; if (!tlv.open(fname, true /* read */)) { DOUT(info) << "Failed to open " << fname << endl; return false; } vector data; uint32_t type; // Read the version field and check it. if (!tlv.read(type, data)) { DOUT(info) << "Failed to read " << fname << endl; return false; } if (!type_check(info, "TLV Version", TLV_VERSION, type)) return false; uint32_t tlv_version; if (!tlv.get32(data, 0, tlv_version)) { return false; } if (!type_check(info, "Version", TLV_CURRENT_VERSION, tlv_version)) return false; // Read the system info if (!tlv.read(type, data)) { DOUT(info) << "Failed to read " << fname << endl; return false; } if (!type_check(info, "TLV System info", TLV_SYSTEM_INFO, type)) return false; data.resize(data.size() + 1); data[data.size() - 1] = 0; DOUT(info) << "Built on " << &data[0] << endl; // Get OSPF version if (!tlv.read(type, data)) { DOUT(info) << "Failed to read " << fname << endl; return false; } if (!type_check(info, "TLV OSPF Version", TLV_OSPF_VERSION, type)) return false; uint32_t ospf_version; if (!tlv.get32(data, 0, ospf_version)) { return false; } if (!type_check(info, "OSPF Version", version, ospf_version)) return false; // OSPF area if (!tlv.read(type, data)) { DOUT(info) << "Failed to read " << fname << endl; return false; } if (!type_check(info, "TLV OSPF Area", TLV_AREA, type)) return false; OspfTypes::AreaID area; if (!tlv.get32(data, 0, area)) { return false; } PeerManager& pm = ospf.get_peer_manager(); pm.create_area_router(area, OspfTypes::NORMAL); AreaRouter *ar = pm.get_area_router(area); XLOG_ASSERT(ar); // The first LSA is this routers Router-LSA. if (!tlv.read(type, data)) { DOUT(info) << "Failed to read " << fname << endl; return false; } if (!type_check(info, "TLV LSA", TLV_LSA, type)) return false; LsaDecoder lsa_decoder(version); initialise_lsa_decoder(version, lsa_decoder); size_t len = data.size(); Lsa::LsaRef lsar = lsa_decoder.decode(&data[0], len); DOUT(info) << lsar->str() << endl; ospf.set_router_id(lsar->get_header().get_advertising_router()); lsar->set_self_originating(true); ar->testing_replace_router_lsa(lsar); bool first_in_area = false; while (tlv.read(type, data)) { switch(static_cast(type)) { case TLV_VERSION: uint32_t tlv_version; if (!tlv.get32(data, 0, tlv_version)) { return false; } DOUT(info) << "Version: " << tlv_version << endl; break; case TLV_SYSTEM_INFO: data.resize(data.size() + 1); data[data.size() - 1] = 0; DOUT(info) << "Built on " << &data[0] << endl; break; case TLV_OSPF_VERSION: uint32_t ospf_version; if (!tlv.get32(data, 0, ospf_version)) { return false; } switch(ospf_version) { case 2: version = OspfTypes::V2; break; case 3: version = OspfTypes::V3; break; } DOUT(info) << "OSPFv" << ospf_version << endl; break; case TLV_AREA: OspfTypes::AreaID area; if (!tlv.get32(data, 0, area)) { return false; } pm.create_area_router(area, OspfTypes::NORMAL); ar = pm.get_area_router(area); XLOG_ASSERT(ar); first_in_area = true; DOUT(info) << "Area: " << pr_id(area) << endl; break; case TLV_LSA: // By the time we get here we are assuming that // TLV_OSPF_VERSION has been read. LsaDecoder lsa_decoder(version); initialise_lsa_decoder(version, lsa_decoder); size_t len = data.size(); Lsa::LsaRef lsar = lsa_decoder.decode(&data[0], len); if (lsar->get_header().get_advertising_router() == ospf.get_router_id()) lsar->set_self_originating(true); if (first_in_area) { RouterLsa *rlsa = dynamic_cast(lsar.get()); if (rlsa && rlsa->get_header().get_advertising_router() == ospf.get_router_id()) { lsar->set_self_originating(true); ar->testing_replace_router_lsa(lsar); first_in_area = false; } else { ar->testing_add_lsa(lsar); } } else { ar->testing_add_lsa(lsar); } DOUT(info) << lsar->str() << endl; break; } } if (areas.empty()) { pm.routing_recompute_all_areas(); return true; } vector tokens; tokenize(areas, tokens); for (vector::iterator i = tokens.begin(); i != tokens.end(); i++) { ar = pm.get_area_router(set_id(i->c_str())); XLOG_ASSERT(ar); ar->testing_routing_total_recompute(); } return true; } int main(int argc, char **argv) { XorpUnexpectedHandler x(xorp_unexpected_handler); xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); TestMain t(argc, argv); string test = t.get_optional_args("-t", "--test", "run only the specified test"); string fname = t.get_optional_args("-f", "--filename", "lsa database"); string areas = t.get_optional_args("-a", "--area", "areas to compute"); t.complete_args_parsing(); struct test { string test_name; XorpCallback1::RefPtr cb; } tests[] = { {"pp", callback(pp, fname)}, {"v2", callback(routing, OspfTypes::V2, fname, areas)}, {"v3", callback(routing, OspfTypes::V3, fname, areas)}, }; try { if (test.empty()) { for (size_t i = 0; i < sizeof(tests) / sizeof(struct test); i++) t.run(tests[i].test_name, tests[i].cb); } else { for (size_t i = 0; i < sizeof(tests) / sizeof(struct test); i++) if (test == tests[i].test_name) { t.run(tests[i].test_name, tests[i].cb); return t.exit(); } t.failed("No test with name " + test + " found\n"); } } catch(...) { xorp_catch_standard_exceptions(); } xlog_stop(); xlog_exit(); return t.exit(); } xorp/ospf/tests/test_routing1.py0000775000076400007640000011616411421137511017162 0ustar greearbgreearb#!/usr/bin/env python # Copyright (c) 2001-2009 XORP, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # $XORP: xorp/ospf/test_routing1.py,v 1.32 2008/10/02 21:57:49 bms Exp $ import getopt import sys import os TESTS=[ # Fields: # 0: Test function # 1: True if this test works # 2: Protocol under test (optional), allowed values 'v2' or 'v3' ['print_lsasV2', True], ['print_lsasV3', True], ['test1', True, 'v2'], ['test2', True, 'v2'], ['r1V2', True, 'v2'], ['r1V3', True, 'v3'], ['r2V3', True, 'v3'], ['r3V3', True, 'v3'], ['r4V3', True, 'v3'], ['r5V3', True, 'v3'], ['r6V3', True, 'v3'], ['r7V3', True, 'v3'], ['r8V2', True, 'v2'], ['r9V2', True, 'v2'], ['r10V2', True, 'v2'], ['r11V3', True, 'v3'], ['r12V3', True, 'v3'], ['r13V3', True, 'v3'], ['r14V3', True, 'v3'], ] def start_routing_interactive(verbose, protocol): """ Helper function to start the test_routing_interactive program """ if protocol == 'v2': flags = ' --OSPFv2' elif protocol == 'v3': flags = ' --OSPFv3' else: print "unknown protocol " + protocol sys.exit(-1) if verbose: flags = flags + ' -v' fp = os.popen(os.path.abspath('test_routing_interactive') + flags, "w") return fp def print_lsasV2(verbose, protocol): """ Run the build lsa program with all the known LSAs and settings. Verifies that the program has been built and gives examples of how to define LSAs. """ options='DC-bit EA-bit N/P-bit MC-bit E-bit' common_header='age 1800 %s lsid 1.2.3.4 adv 5.6.7.8 seqno 1 cksum 1' % \ options RouterLsa='RouterLsa %s bit-NT bit-V bit-E bit-B' % common_header RouterLsa=RouterLsa + ' p2p lsid 10.10.10.10 ldata 11.11.11.11 metric 42' NetworkLsa='NetworkLsa %s netmask 0xffffff00' % common_header NetworkLsa=NetworkLsa + ' add-router 1.2.3.4' SummaryNetworkLsa='SummaryNetworkLsa %s \ netmask 0xffffff00 \ metric 42' % common_header SummaryRouterLsa='SummaryRouterLsa %s \ netmask 0xffffff00 \ metric 42' % common_header ASExternalLsa='ASExternalLsa %s \ netmask 0xffff0000 \ bit-E metric 45 \ forward4 9.10.11.12 \ tag 0x40' % common_header Type7Lsa='Type7Lsa %s \ netmask 0xffff0000 \ bit-E metric 45 \ forward4 9.10.11.12 \ tag 0x40' % common_header lsas = [RouterLsa, NetworkLsa, SummaryNetworkLsa, SummaryRouterLsa, \ ASExternalLsa, Type7Lsa] for i in lsas: if 0 != os.system('%s --OSPFv2 -l "%s"' % \ (os.path.abspath('test_build_lsa_main'), i)): return False return True def print_lsasV3(verbose, protocol): """ Run the build lsa program with all the known LSAs and settings. Verifies that the program has been built and gives examples of how to define LSAs. """ common_header='age 1800 lsid 1.2.3.4 adv 5.6.7.8 seqno 1 cksum 1' options='DC-bit R-bit N-bit MC-bit E-bit V6-bit' RouterLsa='RouterLsa %s bit-NT bit-W bit-B bit-E bit-V' % common_header RouterLsa=RouterLsa + ' ' + options RouterLsa=RouterLsa + ' p2p iid 1 nid 2 nrid 0.0.0.3 metric 42' NetworkLsa='NetworkLsa %s' % common_header NetworkLsa=NetworkLsa + ' ' + options NetworkLsa=NetworkLsa + ' add-router 1.2.3.4' SummaryNetworkLsa='SummaryNetworkLsa %s metric 42 ' % common_header IPv6Prefix='IPv6Prefix 5f00:0000:c001::/48 DN-bit \ P-bit MC-bit LA-bit NU-bit' SummaryNetworkLsa=SummaryNetworkLsa + ' ' + IPv6Prefix SummaryRouterLsa='SummaryRouterLsa %s' % common_header SummaryRouterLsa=SummaryRouterLsa + ' ' + options SummaryRouterLsa=SummaryRouterLsa + ' metric 42 drid 1.2.3.4' ASExternalLsa='ASExternalLsa %s' % common_header ASExternalLsa=ASExternalLsa + ' bit-E bit-F bit-T metric 45' ASExternalLsa=ASExternalLsa + ' ' + IPv6Prefix ASExternalLsa=ASExternalLsa + ' ' + 'rlstype 2' ASExternalLsa=ASExternalLsa + ' ' + 'forward6 5f00:0000:c001::00' ASExternalLsa=ASExternalLsa + ' ' + 'tag 0x40' ASExternalLsa=ASExternalLsa + ' ' + 'rlsid 1.2.3.4' Type7Lsa='Type7Lsa %s' % common_header Type7Lsa=Type7Lsa + ' bit-E bit-F bit-T metric 45' Type7Lsa=Type7Lsa + ' ' + IPv6Prefix Type7Lsa=Type7Lsa + ' ' + 'rlstype 2' Type7Lsa=Type7Lsa + ' ' + 'forward6 5f00:0000:c001::00' Type7Lsa=Type7Lsa + ' ' + 'tag 0x40' Type7Lsa=Type7Lsa + ' ' + 'rlsid 1.2.3.4' LinkLsa='LinkLsa %s' % common_header LinkLsa=LinkLsa + ' rtr-priority 42' LinkLsa=LinkLsa + ' ' + options LinkLsa=LinkLsa + ' link-local-address fe80:0001::' LinkLsa=LinkLsa + ' ' + IPv6Prefix LinkLsa=LinkLsa + ' ' + IPv6Prefix IntraAreaPrefixLsa='IntraAreaPrefixLsa %s' % common_header IntraAreaPrefixLsa=IntraAreaPrefixLsa + ' rlstype 0x2001' IntraAreaPrefixLsa=IntraAreaPrefixLsa + ' rlsid 1.2.3.4' IntraAreaPrefixLsa=IntraAreaPrefixLsa + ' radv 9.8.7.6' IntraAreaPrefixLsa=IntraAreaPrefixLsa + ' ' + IPv6Prefix + ' metric 1' IntraAreaPrefixLsa=IntraAreaPrefixLsa + ' ' + IPv6Prefix + ' metric 2' IntraAreaPrefixLsa_r='IntraAreaPrefixLsa %s' % common_header IntraAreaPrefixLsa_r=IntraAreaPrefixLsa_r + ' rlstype RouterLsa' IntraAreaPrefixLsa_r=IntraAreaPrefixLsa_r + ' rlsid 1.2.3.4' IntraAreaPrefixLsa_r=IntraAreaPrefixLsa_r + ' radv 9.8.7.6' IntraAreaPrefixLsa_r=IntraAreaPrefixLsa_r + ' ' + IPv6Prefix + ' metric 1' IntraAreaPrefixLsa_r=IntraAreaPrefixLsa_r + ' ' + IPv6Prefix + ' metric 2' IntraAreaPrefixLsa_n='IntraAreaPrefixLsa %s' % common_header IntraAreaPrefixLsa_n=IntraAreaPrefixLsa_n + ' rlstype NetworkLsa' IntraAreaPrefixLsa_n=IntraAreaPrefixLsa_n + ' rlsid 1.2.3.4' IntraAreaPrefixLsa_n=IntraAreaPrefixLsa_n + ' radv 9.8.7.6' IntraAreaPrefixLsa_n=IntraAreaPrefixLsa_n + ' ' + IPv6Prefix + ' metric 1' IntraAreaPrefixLsa_n=IntraAreaPrefixLsa_n + ' ' + IPv6Prefix + ' metric 2' IntraAreaPrefixLsa_u='IntraAreaPrefixLsa %s' % common_header IntraAreaPrefixLsa_u=IntraAreaPrefixLsa_u + ' rlstype 42' IntraAreaPrefixLsa_u=IntraAreaPrefixLsa_u + ' rlsid 1.2.3.4' IntraAreaPrefixLsa_u=IntraAreaPrefixLsa_u + ' radv 9.8.7.6' IntraAreaPrefixLsa_u=IntraAreaPrefixLsa_u + ' ' + IPv6Prefix + ' metric 1' IntraAreaPrefixLsa_u=IntraAreaPrefixLsa_u + ' ' + IPv6Prefix + ' metric 2' lsas = [RouterLsa, NetworkLsa, SummaryNetworkLsa, SummaryRouterLsa, \ ASExternalLsa, Type7Lsa, LinkLsa, \ IntraAreaPrefixLsa, IntraAreaPrefixLsa_r, IntraAreaPrefixLsa_n, \ IntraAreaPrefixLsa_u] for i in lsas: if 0 != os.system('%s --OSPFv3 -l "%s"' % \ (os.path.abspath('test_build_lsa_main'), i)): return False return True def test1(verbose, protocol): """ Create an area and then destroy it. """ fp = start_routing_interactive(verbose, protocol) command = """ create 0.0.0.0 normal destroy 0.0.0.0 create 0.0.0.0 normal destroy 0.0.0.0 """ print >>fp, command if not fp.close(): return True else: return False def test2(verbose, protocol): """ Introduce a RouterLsa and a NetworkLsa """ fp = start_routing_interactive(verbose, protocol) command = """ create 0.0.0.0 normal select 0.0.0.0 replace RouterLsa add NetworkLsa compute 0.0.0.0 """ print >>fp, command if not fp.close(): return True else: return False def r1V2(verbose, protocol): """ Some of the routers from Figure 2. in RFC 2328. Single area. This router is R6. """ fp = start_routing_interactive(verbose, protocol) RT6 = "RouterLsa E-bit lsid 0.0.0.6 adv 0.0.0.6 \ p2p lsid 0.0.0.3 ldata 0.0.0.4 metric 6 \ p2p lsid 0.0.0.5 ldata 0.0.0.6 metric 6 \ p2p lsid 0.0.0.10 ldata 0.0.0.11 metric 7 \ " RT3 = "RouterLsa E-bit lsid 0.0.0.3 adv 0.0.0.3 \ p2p lsid 0.0.0.6 ldata 0.0.0.7 metric 8 \ stub lsid 0.4.0.0 ldata 255.255.0.0 metric 2 \ " command = """ set_router_id 0.0.0.6 create 0.0.0.0 normal select 0.0.0.0 replace %s add %s compute 0.0.0.0 verify_routing_table_size 1 verify_routing_entry 0.4.0.0/16 0.0.0.7 8 false false """ % (RT6,RT3) print >>fp, command if not fp.close(): return True else: return False def r1V3(verbose, protocol): """ This test is the OSPFv3 version of the r1V2 although the OSPF for IPV6 RFC does not have a point to point link in Figure 1. """ fp = start_routing_interactive(verbose, protocol) RT6 = "RouterLsa E-bit lsid 0.0.0.1 adv 0.0.0.6 \ p2p iid 1 nid 1 nrid 0.0.0.3 metric 6 \ " RT3 = "RouterLsa R-bit V6-bit E-bit lsid 0.0.0.1 adv 0.0.0.3 \ p2p iid 1 nid 1 nrid 0.0.0.6 metric 8 \ " RT3_LINK = "LinkLsa lsid 0.0.0.1 adv 0.0.0.3 \ link-local-address fe80:0001::3" RT3_INTRA = "IntraAreaPrefixLsa lsid 0.0.0.1 adv 0.0.0.3 \ rlstype 0x2001 rlsid 0.0.0.0 radv 0.0.0.3 \ IPv6Prefix 5f00:0000:c001:0200::/56 metric 3 \ " command = """ set_router_id 0.0.0.6 create 0.0.0.0 normal select 0.0.0.0 replace %s add %s add %s add %s compute 0.0.0.0 verify_routing_table_size 1 verify_routing_entry 5f00:0000:c001:0200::/56 fe80:0001::3 9 false false """ % (RT6,RT3,RT3_LINK,RT3_INTRA) print >>fp, command if not fp.close(): return True else: return False def r2V3(verbose, protocol): """ This test is based on Figure 1 in RFC2740. This router it RT1, RT1 is also the designated router for N3. """ fp = start_routing_interactive(verbose, protocol) RT1 = "RouterLsa V6-bit E-bit R-bit lsid 1.0.0.1 adv 0.0.0.1 \ transit iid 1 nid 1 nrid 0.0.0.1 metric 1 \ " RT1_LINK = "LinkLsa lsid 0.0.0.1 adv 0.0.0.1 \ link-local-address fe80:0002::1" # N1 - Not used as RT1 is this router. RT1_INTRA_R = "IntraAreaPrefixLsa lsid 0.0.0.1 adv 0.0.0.1 \ rlstype RouterLsa rlsid 0.0.0.0 radv 0.0.0.1 \ IPv6Prefix 5f00:0000:c001:0200::/56 metric 1 \ " # N3 - Not used as RT1 is this router. RT1_INTRA_N = "IntraAreaPrefixLsa lsid 0.0.0.2 adv 0.0.0.1 \ rlstype NetworkLsa rlsid 0.0.0.1 radv 0.0.0.1 \ IPv6Prefix 5f00:0000:c001:0100::/56 metric 1 \ " RT1_NETWORK = "NetworkLsa lsid 0.0.0.1 adv 0.0.0.1 \ add-router 0.0.0.1 \ add-router 0.0.0.2 \ add-router 0.0.0.3 \ add-router 0.0.0.4 \ " RT2 = "RouterLsa V6-bit E-bit R-bit lsid 0.0.0.1 adv 0.0.0.2 \ transit iid 2 nid 1 nrid 0.0.0.1 metric 1 \ " RT2_LINK = "LinkLsa lsid 0.0.0.2 adv 0.0.0.2 \ link-local-address fe80:0002::2" # N2 RT2_INTRA = "IntraAreaPrefixLsa lsid 0.0.0.1 adv 0.0.0.2 \ rlstype RouterLsa rlsid 0.0.0.0 radv 0.0.0.2 \ IPv6Prefix 5f00:0000:c001:0300::/56 metric 3 \ " RT3 = "RouterLsa V6-bit E-bit R-bit lsid 0.0.0.1 adv 0.0.0.3 \ transit iid 1 nid 1 nrid 0.0.0.1 metric 1 \ " RT3_LINK = "LinkLsa lsid 0.0.0.1 adv 0.0.0.3 \ link-local-address fe80:0001::3" # N4 RT3_INTRA = "IntraAreaPrefixLsa lsid 0.0.0.1 adv 0.0.0.3 \ rlstype RouterLsa rlsid 0.0.0.0 radv 0.0.0.3 \ IPv6Prefix 5f00:0000:c001:0400::/56 metric 2 \ " RT4 = "RouterLsa V6-bit E-bit R-bit lsid 0.0.0.1 adv 0.0.0.4 \ transit iid 1 nid 1 nrid 0.0.0.1 metric 1 \ " RT4_LINK = "LinkLsa lsid 0.0.0.1 adv 0.0.0.4 \ link-local-address fe80:0001::4" command = """ set_router_id 0.0.0.1 create 0.0.0.0 normal select 0.0.0.0 replace %s add %s add %s add %s add %s add %s add %s add %s add %s add %s add %s add %s add %s compute 0.0.0.0 verify_routing_table_size 2 verify_routing_entry 5f00:0000:c001:0300::/56 fe80:0002::2 4 false false verify_routing_entry 5f00:0000:c001:0400::/56 fe80:0001::3 3 false false """ % (RT1,RT1_LINK,RT1_INTRA_R,RT1_INTRA_N,RT1_NETWORK, RT2,RT2_LINK,RT2_INTRA, RT3,RT3_LINK,RT3_INTRA, RT4,RT4_LINK) print >>fp, command if not fp.close(): return True else: return False def r3V3(verbose, protocol): """ Verify the correct processing of Network-LSAs from non-directly connected interface. """ # RT1 is this router, RT2 in connected to RT1 via p2p, RT2 generates a # Network-LSA for N1. The LSAs from the other routers N1 are not required. # + # +---+1 1+---+5 | # |RT1|---------|RT2|----|N1 # +---+ +---+ | # + # Network IPv6 prefix # ----------------------------------- # N1 5f00:0000:c001:0200::/56 # Router Interface Interface ID link-local address # ------------------------------------------------------- # RT1 to RT2 1 fe80:0001::1 # RT2 to RT1 1 fe80:0001::2 # to N3 20 fe80:0002::2 fp = start_routing_interactive(verbose, protocol) RT1 = "RouterLsa V6-bit E-bit R-bit lsid 42.0.0.1 adv 0.0.0.1 \ p2p iid 1 nid 1 nrid 0.0.0.2 metric 1 \ " RT1_LINK = "LinkLsa lsid 0.0.0.1 adv 0.0.0.1 \ link-local-address fe80:0001::1" RT2 = "RouterLsa V6-bit E-bit R-bit lsid 42.0.0.1 adv 0.0.0.2 \ p2p iid 1 nid 1 nrid 0.0.0.1 metric 1 \ transit iid 20 nid 20 nrid 0.0.0.2 metric 5 \ " RT2_LINK = "LinkLsa lsid 0.0.0.1 adv 0.0.0.2 \ link-local-address fe80:0001::2" RT2_INTRA = "IntraAreaPrefixLsa lsid 42.0.0.2 adv 0.0.0.2 \ rlstype NetworkLsa rlsid 0.0.0.20 radv 0.0.0.2 \ IPv6Prefix 5f00:0000:c001:0200::/56 metric 1 \ " RT2_NETWORK = "NetworkLsa lsid 0.0.0.20 adv 0.0.0.2 \ add-router 0.0.0.2 \ " command = """ set_router_id 0.0.0.1 create 0.0.0.0 normal select 0.0.0.0 replace %s add %s add %s add %s add %s add %s compute 0.0.0.0 verify_routing_table_size 1 verify_routing_entry 5f00:0000:c001:0200::/56 fe80:0001::2 7 false false """ % (RT1,RT1_LINK, RT2,RT2_LINK,RT2_INTRA,RT2_NETWORK) print >>fp, command if not fp.close(): return True else: return False def r4V3(verbose, protocol): """ This test is based on Figure 1 in RFC2740. This router it RT1, RT2 is the designated router for N3. """ fp = start_routing_interactive(verbose, protocol) RT1 = "RouterLsa V6-bit E-bit R-bit lsid 1.0.0.1 adv 0.0.0.1 \ transit iid 1 nid 2 nrid 0.0.0.2 metric 1 \ " RT1_LINK = "LinkLsa lsid 0.0.0.1 adv 0.0.0.1 \ link-local-address fe80:0002::1" # N1 RT1_INTRA = "IntraAreaPrefixLsa lsid 0.0.0.1 adv 0.0.0.1 \ rlstype RouterLsa rlsid 0.0.0.0 radv 0.0.0.1 \ IPv6Prefix 5f00:0000:c001:0200::/56 metric 1 \ " RT2 = "RouterLsa V6-bit E-bit R-bit lsid 0.0.0.1 adv 0.0.0.2 \ transit iid 2 nid 2 nrid 0.0.0.2 metric 1 \ " RT2_LINK = "LinkLsa lsid 0.0.0.2 adv 0.0.0.2 \ link-local-address fe80:0002::2" # N2 RT2_INTRA_R = "IntraAreaPrefixLsa lsid 0.0.0.1 adv 0.0.0.2 \ rlstype RouterLsa rlsid 0.0.0.0 radv 0.0.0.2 \ IPv6Prefix 5f00:0000:c001:0300::/56 metric 3 \ " # N3 RT2_INTRA_N = "IntraAreaPrefixLsa lsid 0.0.0.2 adv 0.0.0.2 \ rlstype NetworkLsa rlsid 0.0.0.2 radv 0.0.0.2 \ IPv6Prefix 5f00:0000:c001:0100::/56 metric 1 \ " RT2_NETWORK = "NetworkLsa lsid 0.0.0.2 adv 0.0.0.2 \ add-router 0.0.0.1 \ add-router 0.0.0.2 \ add-router 0.0.0.3 \ add-router 0.0.0.4 \ " RT3 = "RouterLsa V6-bit E-bit R-bit lsid 0.0.0.1 adv 0.0.0.3 \ transit iid 1 nid 2 nrid 0.0.0.2 metric 1 \ " RT3_LINK = "LinkLsa lsid 0.0.0.1 adv 0.0.0.3 \ link-local-address fe80:0001::3" # N4 RT3_INTRA = "IntraAreaPrefixLsa lsid 0.0.0.1 adv 0.0.0.3 \ rlstype RouterLsa rlsid 0.0.0.0 radv 0.0.0.3 \ IPv6Prefix 5f00:0000:c001:0400::/56 metric 2 \ " RT4 = "RouterLsa V6-bit E-bit R-bit lsid 0.0.0.1 adv 0.0.0.4 \ transit iid 1 nid 2 nrid 0.0.0.2 metric 1 \ " RT4_LINK = "LinkLsa lsid 0.0.0.1 adv 0.0.0.4 \ link-local-address fe80:0001::4" command = """ set_router_id 0.0.0.1 create 0.0.0.0 normal select 0.0.0.0 replace %s add %s add %s add %s add %s add %s add %s add %s add %s add %s add %s add %s add %s compute 0.0.0.0 verify_routing_table_size 2 verify_routing_entry 5f00:0000:c001:0300::/56 fe80:0002::2 4 false false verify_routing_entry 5f00:0000:c001:0400::/56 fe80:0001::3 3 false false """ % (RT1,RT1_LINK,RT1_INTRA, RT2,RT2_LINK,RT2_INTRA_R,RT2_INTRA_N,RT2_NETWORK, RT3,RT3_LINK,RT3_INTRA, RT4,RT4_LINK) print >>fp, command if not fp.close(): return True else: return False def r5V3(verbose, protocol): """ Verify the correct processing of Inter-Area-Prefix-LSAs. """ # RT1 is this router. # RT2 is an area border router that generates an inter-area-prefix-LSAs. # RT1 is connected to RT2 via p2p. # + # +---+1 1+---+5 | # |RT1|---------|RT2|----| Other Area # +---+ +---+ | # + # Network IPv6 prefix # ----------------------------------- # Other Area 5f00:0000:c001:0200::/56 # Router Interface Interface ID link-local address # ------------------------------------------------------- # RT1 to RT2 1 fe80:0001::1 # RT2 to RT1 1 fe80:0001::2 # to N3 20 fe80:0002::2 fp = start_routing_interactive(verbose, protocol) RT1 = "RouterLsa V6-bit E-bit R-bit lsid 42.0.0.1 adv 0.0.0.1 \ p2p iid 1 nid 1 nrid 0.0.0.2 metric 1 \ " RT1_LINK = "LinkLsa lsid 0.0.0.1 adv 0.0.0.1 \ link-local-address fe80:0001::1" RT2 = "RouterLsa bit-B V6-bit E-bit R-bit lsid 42.0.0.1 adv 0.0.0.2 \ p2p iid 1 nid 1 nrid 0.0.0.1 metric 1 \ transit iid 20 nid 20 nrid 0.0.0.2 metric 5 \ " RT2_LINK = "LinkLsa lsid 0.0.0.1 adv 0.0.0.2 \ link-local-address fe80:0001::2" RT2_INTER = "SummaryNetworkLsa lsid 42.0.0.2 adv 0.0.0.2 \ metric 6 \ IPv6Prefix 5f00:0000:c001:0200::/56 \ " command = """ set_router_id 0.0.0.1 create 0.0.0.0 normal select 0.0.0.0 replace %s add %s add %s add %s add %s compute 0.0.0.0 verify_routing_table_size 1 verify_routing_entry 5f00:0000:c001:0200::/56 fe80:0001::2 7 false false """ % (RT1,RT1_LINK, RT2,RT2_LINK,RT2_INTER) print >>fp, command if not fp.close(): return True else: return False def r6V3(verbose, protocol): """ Verify the correct processing of AS-External-LSAs. """ # RT1 is this router. # RT2 is an AS boundary router that generates an AS-External-LSA # RT1 is connected to RT2 via p2p. # # +---+1 1+---+5 # |RT1|---------|RT2| # +---+ +---+ # # Network IPv6 prefix # ----------------------------------- # External address 5f00:0000:c001:0200::/56 # Router Interface Interface ID link-local address # ------------------------------------------------------- # RT1 to RT2 1 fe80:0001::1 # RT2 to RT1 1 fe80:0001::2 fp = start_routing_interactive(verbose, protocol) RT1 = "RouterLsa V6-bit E-bit R-bit lsid 42.0.0.1 adv 0.0.0.1 \ p2p iid 1 nid 1 nrid 0.0.0.2 metric 1 \ " RT1_LINK = "LinkLsa lsid 0.0.0.1 adv 0.0.0.1 \ link-local-address fe80:0001::1" RT2 = "RouterLsa bit-E V6-bit E-bit R-bit lsid 42.0.0.1 adv 0.0.0.2 \ p2p iid 1 nid 1 nrid 0.0.0.1 metric 1 \ " RT2_LINK = "LinkLsa lsid 0.0.0.1 adv 0.0.0.2 \ link-local-address fe80:0001::2" RT2_INTER = "ASExternalLsa lsid 42.0.0.2 adv 0.0.0.2 \ metric 6 \ IPv6Prefix 5f00:0000:c001:0200::/56 \ " command = """ set_router_id 0.0.0.1 create 0.0.0.0 normal select 0.0.0.0 replace %s add %s add %s add %s add %s compute 0.0.0.0 verify_routing_table_size 1 verify_routing_entry 5f00:0000:c001:0200::/56 fe80:0001::2 7 false false """ % (RT1,RT1_LINK, RT2,RT2_LINK,RT2_INTER) print >>fp, command if not fp.close(): return True else: return False def r7V3(verbose, protocol): """ Verify virtual links are correctly processed when the router is directly adjacent to the other router. """ # RT1 is this router. # RT2 is a virtual neighbour. # RT1 is directly connected to RT2 via a virtual link. # # +---+1 1+---+5 # |RT1|---------|RT2| # +---+ +---+ # # Network IPv6 prefix # ----------------------------------- # External address 5f00:0000:c001:0200::/56 # Router Interface Interface ID link-local address global address # ---------------------------------------------------------------------- # RT1 to RT2 1 fe80:0001::1 5f00::0001 # RT2 to RT1 1 fe80:0001::2 5f00::0002 fp = start_routing_interactive(verbose, protocol) RT1 = "RouterLsa V6-bit E-bit R-bit lsid 42.0.0.1 adv 0.0.0.1 \ vlink iid 1 nid 1 nrid 0.0.0.2 metric 1 \ " RT1_LINK = "LinkLsa lsid 0.0.0.1 adv 0.0.0.1 \ link-local-address fe80:0001::1" RT1_INTRA_R = "IntraAreaPrefixLsa lsid 0.0.0.0 adv 0.0.0.1 \ rlstype RouterLsa rlsid 0.0.0.0 radv 0.0.0.1 \ IPv6Prefix 5f00::0001/128 metric 1 LA-bit \ " RT2 = "RouterLsa bit-E V6-bit E-bit R-bit lsid 42.0.0.1 adv 0.0.0.2 \ vlink iid 1 nid 1 nrid 0.0.0.1 metric 1 \ " RT2_LINK = "LinkLsa lsid 0.0.0.1 adv 0.0.0.2 \ link-local-address fe80:0001::2" RT2_INTRA_R = "IntraAreaPrefixLsa lsid 0.0.0.0 adv 0.0.0.2 \ rlstype RouterLsa rlsid 0.0.0.0 radv 0.0.0.2 \ IPv6Prefix 5f00::0002/128 metric 1 LA-bit \ IPv6Prefix 5f00:0000:c001:0200::/56 metric 1 \ " command = """ set_router_id 0.0.0.1 create 0.0.0.0 normal select 0.0.0.0 replace %s add %s add %s add %s add %s add %s compute 0.0.0.0 verify_routing_table_size 1 verify_routing_entry 5f00:0000:c001:0200::/56 5f00::0002 2 false false """ % (RT1,RT1_LINK,RT1_INTRA_R, RT2,RT2_LINK,RT2_INTRA_R) print >>fp, command if not fp.close(): return True else: return False def r8V2(verbose, protocol): """ Simple two router case the next two tests will add another link between RT1 and RT2. """ # RT1 is this router # RT2 is its direct neighbour # A single route should be installed for 10.1.1.0/24 via 10.1.2.1 # +-----+ +-----+ # | RT1 | | RT2 | # | | | | # | | | | # 10.2.2.0/24| | | |10.1.1.0/24 # -----------| | | |------------ # | | 10.1.2.0/24 | | # | |---------------------| | # | |10.1.2.2 10.1.2.1| | # | | metric 1 | | # +-----+ +-----+ fp = start_routing_interactive(verbose, protocol) RT1 = "RouterLsa E-bit lsid 127.1.0.2 adv 127.1.0.2 \ stub lsid 10.2.2.0 ldata 255.255.255.0 metric 1 \ transit lsid 10.1.2.2 ldata 10.1.2.2 metric 1 \ " RT2 = "RouterLsa E-bit lsid 127.1.0.1 adv 127.1.0.1 \ stub lsid 10.1.1.0 ldata 255.255.255.0 metric 1 \ transit lsid 10.1.2.2 ldata 10.1.2.1 metric 1 \ " RT1_NETWORK1 = "NetworkLsa E-bit lsid 10.1.2.2 adv 127.1.0.2 \ netmask 0xffffff00 \ add-router 127.1.0.2 \ add-router 127.1.0.1 \ " RT1_NETWORK2 = "NetworkLsa E-bit lsid 99.1.1.2 adv 127.1.0.2 \ netmask 0xffffff00 \ add-router 127.1.0.2 \ add-router 127.1.0.1 \ " command = """ set_router_id 127.1.0.2 create 0.0.0.0 normal select 0.0.0.0 replace %s add %s add %s add %s compute 0.0.0.0 verify_routing_table_size 1 verify_routing_entry 10.1.1.0/24 10.1.2.1 2 false false """ % (RT1, RT2, RT1_NETWORK1, RT1_NETWORK2) print >>fp, command if not fp.close(): return True else: return False def r9V2(verbose, protocol): """ Test that when there are two paths between two routers the one with the lowest metric is choosen. This test is the same as the previous test apart from the addition of the more expensive path. """ # RT1 is this router # RT2 is its direct neighbour via two interfaces. # A single route should be installed for 10.1.1.0/24 via 10.1.2.1 # +-----+ +-----+ # | RT1 | 99.1.2.0/24 | RT2 | # | |---------------------| | # | |99.1.2.2 99.1.2.1| | # 10.2.2.0/24| | metric 10 | |10.1.1.0/24 # -----------| | | |------------ # | | 10.1.2.0/24 | | # | |---------------------| | # | |10.1.2.2 10.1.2.1| | # | | metric 1 | | # +-----+ +-----+ fp = start_routing_interactive(verbose, protocol) RT1 = "RouterLsa E-bit lsid 127.1.0.2 adv 127.1.0.2 \ age 1074 seqno 0x8000001a cksum 0x9976 \ transit lsid 99.1.1.2 ldata 99.1.1.2 metric 10 \ stub lsid 10.2.2.0 ldata 255.255.255.0 metric 1 \ transit lsid 10.1.2.2 ldata 10.1.2.2 metric 1 \ " RT2 = "RouterLsa E-bit lsid 127.1.0.1 adv 127.1.0.1 \ age 1076 seqno 0x8000001a cksum 0x4cc9 \ transit lsid 99.1.1.2 ldata 99.1.1.1 metric 10 \ stub lsid 10.1.1.0 ldata 255.255.255.0 metric 1 \ transit lsid 10.1.2.2 ldata 10.1.2.1 metric 1 \ " RT1_NETWORK1 = "NetworkLsa E-bit lsid 10.1.2.2 adv 127.1.0.2 \ age 1075 seqno 0x80000019 cksum 0x3676 \ netmask 0xffffff00 \ add-router 127.1.0.2 \ add-router 127.1.0.1 \ " RT1_NETWORK2 = "NetworkLsa E-bit lsid 99.1.1.2 adv 127.1.0.2 \ age 1074 seqno 0x80000019 cksum 0xb79c \ netmask 0xffffff00 \ add-router 127.1.0.2 \ add-router 127.1.0.1 \ " command = """ set_router_id 127.1.0.2 create 0.0.0.0 normal select 0.0.0.0 replace %s add %s add %s add %s compute 0.0.0.0 verify_routing_table_size 1 verify_routing_entry 10.1.1.0/24 10.1.2.1 2 false false """ % (RT1, RT2, RT1_NETWORK1, RT1_NETWORK2) print >>fp, command if not fp.close(): return True else: return False def r10V2(verbose, protocol): """ Test that when there are two paths between two routers the one with the lowest metric is choosen. This test is identical to the previous test apart from the order of the transit links in the Router-LSAs. """ # RT1 is this router # RT2 is its direct neighbour via two interfaces. # A single route should be installed for 10.1.1.0/24 via 10.1.2.1 # +-----+ +-----+ # | RT1 | 99.1.2.0/24 | RT2 | # | |---------------------| | # | |99.1.2.2 99.1.2.1| | # 10.2.2.0/24| | metric 10 | |10.1.1.0/24 # -----------| | | |------------ # | | 10.1.2.0/24 | | # | |---------------------| | # | |10.1.2.2 10.1.2.1| | # | | metric 1 | | # +-----+ +-----+ fp = start_routing_interactive(verbose, protocol) RT1 = "RouterLsa E-bit lsid 127.1.0.2 adv 127.1.0.2 \ age 1074 seqno 0x8000001a cksum 0x9976 \ transit lsid 10.1.2.2 ldata 10.1.2.2 metric 1 \ stub lsid 10.2.2.0 ldata 255.255.255.0 metric 1 \ transit lsid 99.1.1.2 ldata 99.1.1.2 metric 10 \ " RT2 = "RouterLsa E-bit lsid 127.1.0.1 adv 127.1.0.1 \ age 1076 seqno 0x8000001a cksum 0x4cc9 \ transit lsid 10.1.2.2 ldata 10.1.2.1 metric 1 \ stub lsid 10.1.1.0 ldata 255.255.255.0 metric 1 \ transit lsid 99.1.1.2 ldata 99.1.1.1 metric 10 \ " RT1_NETWORK1 = "NetworkLsa E-bit lsid 10.1.2.2 adv 127.1.0.2 \ age 1075 seqno 0x80000019 cksum 0x3676 \ netmask 0xffffff00 \ add-router 127.1.0.2 \ add-router 127.1.0.1 \ " RT1_NETWORK2 = "NetworkLsa E-bit lsid 99.1.1.2 adv 127.1.0.2 \ age 1074 seqno 0x80000019 cksum 0xb79c \ netmask 0xffffff00 \ add-router 127.1.0.2 \ add-router 127.1.0.1 \ " command = """ set_router_id 127.1.0.2 create 0.0.0.0 normal select 0.0.0.0 replace %s add %s add %s add %s compute 0.0.0.0 verify_routing_table_size 1 verify_routing_entry 10.1.1.0/24 10.1.2.1 2 false false """ % (RT1, RT2, RT1_NETWORK1, RT1_NETWORK2) print >>fp, command if not fp.close(): return True else: return False def r11database(): """ Return the database for test r11V3 """ # RT1 is this router. # RTC is an area border router that generates an Inter-Area-Prefix-LSA. # RT1 is connected to RTC via transit. # + # +---+1 1+---+1 | # |RT1|---------|RTC|----| Other Area # +---+ +---+ | # + # Network IPv6 prefix # ----------------------------------- # Other Area 3ffe:4725:c404::/64 # Router Interface Interface ID link-local address # ------------------------------------------------------- # RT1 to RTC 4 fe80::8:800:200c:4300 # RTC to RT1 3 fe80::8:800:200c:4350 RT1 = "RouterLsa V6-bit E-bit R-bit lsid 0.0.0.0 adv 10.80.52.103 \ transit iid 4 nid 4 nrid 10.80.52.103 metric 1 \ " RT1_LINK = "LinkLsa lsid 0.0.0.4 adv 10.80.52.103 \ link-local-address fe80::8:800:200c:4300" RT1_NETWORK = "NetworkLsa lsid 0.0.0.4 adv 10.80.52.103 \ add-router 10.80.52.103 \ add-router 10.80.52.107 \ " RTC = "RouterLsa bit-B V6-bit E-bit R-bit lsid 0.0.0.1 adv 10.80.52.107 \ transit iid 3 nid 4 nrid 10.80.52.103 metric 1 \ " RTC_LINK = "LinkLsa lsid 0.0.0.3 adv 10.80.52.107 \ link-local-address fe80::8:800:200c:4350" RTC_INTER = "SummaryNetworkLsa lsid 0.0.0.2 adv 10.80.52.107 \ metric 1 \ IPv6Prefix 3ffe:4725:c404::/64 \ " database = """ replace %s add %s add %s add %s add %s add %s """ % (RT1,RT1_LINK,RT1_NETWORK, RTC,RTC_LINK,RTC_INTER) return database def r11V3(verbose, protocol): """ Verify the correct processing of Inter-Area-Prefix-LSAs, when this router is connected to the generating router by a Network-LSA. """ fp = start_routing_interactive(verbose, protocol) command = """ set_router_id 10.80.52.103 create 0.0.0.0 normal select 0.0.0.0 %s compute 0.0.0.0 verify_routing_table_size 1 verify_routing_entry 3ffe:4725:c404::/64 fe80::8:800:200c:4350 2 false false """ % r11database() print >>fp, command if not fp.close(): return True else: return False def r12database(): """ Return the database for test r12V3 """ # RT1 is this router. # RTB is an area border router that generates an Intra-Area-Prefix-LSA. # RT1 is connected to RTB via transit. # + # +---+1 1+---+1 | # |RT1|---------|RTB|----| Other Area # +---+ +---+ | # + # Network IPv6 prefix # ----------------------------------- # Other Area 3ffe:4725:c404::/64 # Router Interface Interface ID link-local address # ------------------------------------------------------- # RT1 to RTB 5 fe80::8:800:200c:4200 # RTB to RT1 2 fe80::8:800:200c:4250 RT1 = "RouterLsa V6-bit E-bit R-bit lsid 0.0.0.0 adv 10.80.52.103 \ transit iid 5 nid 5 nrid 10.80.52.103 metric 1 \ " RT1_LINK = "LinkLsa lsid 0.0.0.5 adv 10.80.52.103 \ link-local-address fe80::8:800:200c:4200" RT1_NETWORK = "NetworkLsa lsid 0.0.0.5 adv 10.80.52.103 \ add-router 10.80.52.103 \ add-router 10.80.52.106 \ " RTB = "RouterLsa bit-B V6-bit E-bit R-bit lsid 0.0.0.1 adv 10.80.52.106 \ transit iid 2 nid 5 nrid 10.80.52.103 metric 1 \ " RTB_LINK = "LinkLsa lsid 0.0.0.2 adv 10.80.52.106 \ link-local-address fe80::8:800:200c:4250" RTB_INTRA = "IntraAreaPrefixLsa lsid 0.0.0.2 adv 10.80.52.106 \ rlstype 0x2001 rlsid 0.0.0.0 radv 10.80.52.106 \ IPv6Prefix 3ffe:4725:c404::/64 metric 3 \ " database = """ replace %s add %s add %s add %s add %s add %s """ % (RT1,RT1_LINK,RT1_NETWORK, RTB,RTB_LINK,RTB_INTRA) return database def r12V3(verbose, protocol): """ Verify the correct processing of Intra-Area-Prefix-LSAs. """ fp = start_routing_interactive(verbose, protocol) command = """ set_router_id 10.80.52.103 create 36.0.0.0 normal select 36.0.0.0 %s compute 36.0.0.0 verify_routing_table_size 1 verify_routing_entry 3ffe:4725:c404::/64 fe80::8:800:200c:4250 4 false false """ % r12database() print >>fp, command if not fp.close(): return True else: return False def r13V3(verbose, protocol): """ Verify that Intra-Area-Prefix-LSAs always win over Inter-Area-Prefix-LSAs. The same route is introduced into thebackbone 0.0.0.0 and area 36.0.0.0, In the backbone 0.0.0.0 it is an Inter-Area-Prefix-LSA, in area 36.0.0.0 it is an Intra-Area-Prefix-LSA. The two area databases are introduced in various orders and the computations run to make sure that however the LSAs are introduced the IntraAreaPrefixLsa always wins. """ init = """ set_router_id 10.80.52.103 create 0.0.0.0 normal create 36.0.0.0 normal """ area0 = """ select 0.0.0.0 %s """ % (r11database()) verify0 = """ verify_routing_table_size 1 verify_routing_entry 3ffe:4725:c404::/64 fe80::8:800:200c:4350 2 false false """ area36 = """ select 36.0.0.0 %s """ % (r12database()) verify36 = """ verify_routing_table_size 1 verify_routing_entry 3ffe:4725:c404::/64 fe80::8:800:200c:4250 4 false false """ fp = start_routing_interactive(verbose, protocol) print >>fp, init print >>fp, area0 print >>fp, "compute 0.0.0.0" print >>fp, verify0 print >>fp, area36 print >>fp, "compute 36.0.0.0" print >>fp, verify36 if fp.close(): return False fp = start_routing_interactive(verbose, protocol) print >>fp, init print >>fp, area36 print >>fp, "compute 36.0.0.0" print >>fp, verify36 print >>fp, area0 print >>fp, "compute 0.0.0.0" print >>fp, verify36 if fp.close(): return False fp = start_routing_interactive(verbose, protocol) print >>fp, init print >>fp, area0 print >>fp, area36 print >>fp, "compute 0.0.0.0" print >>fp, verify0 if fp.close(): return False return True # http://bugzilla.xorp.org/bugzilla/show_bug.cgi?id=770 def r14V3(verbose, protocol): """ If two or more routers AS boundary or area border routers had the same link-local address then only one router would be installed in the routing table. If the router that did not get its routes installed was an AS boundary router then all AS-External-LSAs that it introduced would not be installed in the routing table. """ # RT1 is this router. # RT2 is adjacent to RT1 # RTC is an AS boundary router that generates an AS-External-LSA # external IPv6 connection # | # | # | RTC RT2 RT1 # Cisco --------------- XORP-1.5 -------------- XORP-1.5 # 195.113.144.2 147.229.255.254 147.229.9.41 # fe80::207:e9ff:fe1a:b2f<->fe80::230:48ff:fe87:d512 # Network IPv6 prefix # ----------------------------------- # External address 2000::/3 # Router Interface Interface ID link-local address # ------------------------------------------------------- # RT1 to RT2 1 fe80::230:48ff:fe87:d512 # RT2 to RT1 2 fe80::207:e9ff:fe1a:b2f # RT2 to RTC 1 fe80:0001::1 # RTC to RT2 1 fe80:0001::2 fp = start_routing_interactive(verbose, protocol) RT2_ID = "147.229.255.254" # RT2_ID = "247.229.255.254" RTC_LINK_LOCAL= "fe80:0001::2" RT1 = "RouterLsa V6-bit R-bit lsid 42.0.0.1 adv 147.229.9.41 \ p2p iid 1 nid 2 nrid %s metric 1 \ " % RT2_ID RT1_LINK = "LinkLsa lsid 0.0.0.1 adv 147.229.9.41 \ link-local-address fe80::230:48ff:fe87:d512 \ " RT2 = "RouterLsa bit-E V6-bit R-bit lsid 42.0.0.1 adv %s \ p2p iid 1 nid 1 nrid 195.113.144.2 metric 1 \ p2p iid 2 nid 1 nrid 147.229.9.41 metric 1 \ " % RT2_ID RT2_LINK1 = "LinkLsa lsid 0.0.0.1 adv %s \ link-local-address fe80:0001::2 \ " % RT2_ID RT2_LINK2 = "LinkLsa lsid 0.0.0.2 adv %s \ link-local-address fe80::207:e9ff:fe1a:b2f \ " % RT2_ID RTC = "RouterLsa bit-E V6-bit R-bit E-bit lsid 42.0.0.1 adv 195.113.144.2 \ p2p iid 1 nid 1 nrid %s metric 1 \ " % RT2_ID RTC_LINK = "LinkLsa lsid 0.0.0.1 adv 195.113.144.2 \ link-local-address %s \ " % RTC_LINK_LOCAL RTC_AS_EXTERNAL = "ASExternalLsa lsid 42.0.0.2 adv 195.113.144.2 \ metric 1 \ IPv6Prefix 2000::/3 \ " command = """ set_router_id 147.229.9.41 create 0.0.0.0 normal select 0.0.0.0 replace %s add %s add %s add %s add %s add %s add %s add %s compute 0.0.0.0 verify_routing_table_size 1 verify_routing_entry 2000::/3 fe80::207:e9ff:fe1a:b2f 3 false false """ % (RT1,RT1_LINK, RT2,RT2_LINK1,RT2_LINK2, RTC,RTC_LINK,RTC_AS_EXTERNAL) print >>fp, command if not fp.close(): return True else: return False def main(): def usage(): us = \ "usage: %s [-h|--help] [-v|--verbose] ][-t|--test] [-b|--bad]" print us % sys.argv[0] try: opts, args = getopt.getopt(sys.argv[1:], "hvt:b", \ ["help", \ "verbose", \ "test=", \ "bad", \ ]) except getopt.GetoptError: usage() sys.exit(1) bad = False tests = [] verbose = False for o, a in opts: if o in ("-h", "--help"): usage() sys.exit() if o in ("-v", "--verbose"): verbose = True if o in ("-t", "--test"): tests.append(a) if o in ("-b", "--bad"): bad = True if not tests: for i in TESTS: if bad != i[1]: tests.append(i[0]) print tests for i in tests: protocol = 'unknown' for j in TESTS: if j[0] == i: if len(j) > 2: protocol = j[2] test = i + '(verbose,protocol)' print 'Running: ' + i, if not eval(test): print "FAILED" sys.exit(-1) else: print sys.exit(0) main() # Local Variables: # mode: python # py-indent-offset: 4 # End: xorp/ospf/tests/SConscript0000664000076400007640000000423611421137511016004 0ustar greearbgreearb# Copyright (c) 2009 XORP, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # # $XORP$ import os Import("env") env = env.Clone() is_shared = env.has_key('SHAREDLIBS') env.AppendUnique(CPPPATH = [ '#', '$BUILDDIR', '$BUILDDIR/ospf', '$BUILDDIR/ospf/tests', ]) env.AppendUnique(LIBPATH = [ '$BUILDDIR/ospf', '$BUILDDIR/libfeaclient', '$BUILDDIR/policy/backend', '$BUILDDIR/policy/common', '$BUILDDIR/xrl/interfaces', '$BUILDDIR/xrl/targets', '$BUILDDIR/libxipc', '$BUILDDIR/libproto', '$BUILDDIR/libxorp', '$BUILDDIR/libcomm', ]) env.AppendUnique(LIBS = [ 'xif_rib', 'xif_finder_event_notifier', 'xorp_fea_client', 'xif_fea_ifmgr_mirror', 'xif_fea_ifmgr_replicator', 'xst_fea_ifmgr_mirror', 'xif_fea_rawpkt4', 'xif_fea_rawpkt6', 'xorp_policy_backend', 'xorp_policy_common', 'xorp_ipc', 'xorp_proto', 'xorp_core', 'xorp_comm', ]) # XXX Do we need to link against this now? if is_shared: env.PrependUnique(LIBS = [ 'xst_ospfv2', 'xst_ospfv3', ]) env.PrependUnique(LIBS = [ 'xorp_ospf', ]) # XXX NOTYET: compound test; two modules, one test scaffold, run from # test_routing.py. #'build_lsa', 'build_lsa_main' simple_cpp_tests = [ 'checksum', 'packet', 'peering', 'routing', #'routing_database', # NOTYET #'routing_interactive', # NOTYET 'routing_table', ] cpp_test_targets = [] for ct in simple_cpp_tests: cpp_test_targets.append(env.AutoTest(target = 'test_%s' % ct, source = 'test_%s.cc' % ct)) xorp/ospf/tests/packet3.data0000664000076400007640000000274511421137511016162 0ustar greearbgreearb// $XORP: xorp/ospf/packet2.data,v 1.1 2006/10/16 08:41:29 atanu Exp $ // Bad OSPF packet based on packet1.data with the fields in the Network-LSA // and Router-LSA corrected, the number of LSAs is still bad. /* OSPF Version */ 0x02, /* OSPF Packet Type */ 0x04, /* Packet Length */ 0x00, 0x5c, /* Source OSPF Router ID */ 0x7f, 0x00, 0x00, 0x01, /* Area ID */ 0x00, 0x00, 0x00, 0x00, /* Packet Checksum */ 0x3d, 0xf6, /* Auth Type */ 0x00, 0x00, /* Auth Data */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* LS Update Packet */ /* Number of LSAs: 98304515 (BAD) */ 0x05, 0xdc, 0x02, 0x03, /* Network LSA */ /* LS Age */ 0x00, 0x00, /* Options */ 0x00, /* LSA Type: 2 (Network LSA) */ 0x02, /* Link State ID */ 0x00, 0x3c,0x02, 0x01, /* Advertising Router */ 0x7f, 0x00, 0x00, 0x01, /* LS Sequence number */ 0x7f, 0x00, 0x00, 0x01, /* LS Checksum */ 0x9e, 0xaf, /* Length */ 0x00, 0x1c, /* Netmask */ 0x00, 0x00, 0x00, 0x14, /* Attached Router */ 0x00, 0x3c, 0x02, 0x01, /* Router LSA */ /* LS Age */ 0x01, 0x01, /* Options */ 0x01, /* LSA Type: 1 (Router LSA) */ 0x01, /* Link State ID */ 0x01, 0x01, 0x01, 0x01, /* Advertising Router */ 0x80, 0x00, 0x00, 0x01, /* LS Sequence number */ 0x00, 0x00, 0x00, 0x14, /* LS Checksum */ 0x8e, 0x13, /* Length */ 0x00, 0x24, /* Flags */ 0x01, 0x01, /* # Links */ 0x00, 0x01, /* Link ID */ 0x01, 0x01, 0x02, 0x01, /* Link Data */ 0x80, 0x00, 0x00, 0x01, /* Type */ 0x01, /* # TOS */ 0x00, /* Metric */ 0x00, 0x14, xorp/ospf/tests/test_args.hh0000664000076400007640000000475111421137511016310 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/ospf/test_args.hh,v 1.6 2008/10/02 21:57:49 bms Exp $ #ifndef __OSPF_TEST_ARGS_HH__ #define __OSPF_TEST_ARGS_HH__ #include "libxorp/tokenize.hh" /** * Break a string into a sequence of space separated words. */ class Args { public: Args(string& line) : _line(line), _pos(0) { tokenize(line, _words); } /** * @param word if a word is available it is placed here. * @return true if a word has been returned. */ bool get_next(string& word) { if (_pos >= _words.size()) return false; word = _words[_pos++]; return true; } void push_back() { if (_pos > 1) _pos--; } const string& original_line() const { return _line; } private: const string _line; vector _words; size_t _pos; }; /** * Get a number in base 8,10 or 16. */ inline uint32_t get_number(const string& word) throw(InvalidString) { char *endptr; uint32_t number = strtoul(word.c_str(), &endptr, 0); if (0 != *endptr) xorp_throw(InvalidString, c_format("<%s> is not a number", word.c_str())); return number; } /** * Get the next word throw an exception if its not present. */ inline string get_next_word(Args& args, const string& varname) throw(InvalidString) { string var; if (!args.get_next(var)) xorp_throw(InvalidString, c_format("No argument to %s. [%s]", varname.c_str(), args.original_line().c_str())); return var; } inline uint32_t get_next_number(Args& args, const string& varname) throw(InvalidString) { return get_number(get_next_word(args, varname)); } #endif // __OSPF_TEST_ARGS_HH__ xorp/ospf/tests/test_build_lsa.cc0000664000076400007640000003551511421137511017302 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "ospf_module.h" #include "libxorp/xorp.h" #include "libxorp/test_main.hh" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/callback.hh" #include "libxorp/ipv4.hh" #include "libxorp/ipv6.hh" #include "libxorp/ipnet.hh" #include "libxorp/status_codes.h" #include "libxorp/service.hh" #include "libxorp/eventloop.hh" #include "ospf.hh" #include "packet.hh" #include "test_args.hh" #include "test_build_lsa.hh" Lsa * BuildLsa::generate(Args& args) { string word; if (!args.get_next(word)) return 0; Lsa *lsa = 0; if ("RouterLsa" == word) { lsa = router_lsa(args); } else if ("NetworkLsa" == word) { lsa = network_lsa(args); } else if ("SummaryNetworkLsa" == word) { lsa = summary_network_lsa(args); } else if ("SummaryRouterLsa" == word) { lsa = summary_router_lsa(args); } else if ("ASExternalLsa" == word) { lsa = as_external_lsa(args); } else if ("Type7Lsa" == word) { lsa = type_7_lsa(args); } else if ("LinkLsa" == word) { lsa = link_lsa(args); } else if ("IntraAreaPrefixLsa" == word) { lsa = intra_area_prefix_lsa(args); } else { xorp_throw(InvalidString, c_format("Unknown LSA name <%s>. [%s]", word.c_str(), args.original_line().c_str())) } return lsa; } template bool getit(Lsa *lsa, OspfTypes::Version version, Options& options) { A *alsa = dynamic_cast(lsa); if (alsa) { options = Options(version, alsa->get_options()); return true; } return false; } Options BuildLsa::get_options(Lsa *lsa) { switch(_version) { case OspfTypes::V2: return Options(_version, lsa->get_header().get_options()); break; case OspfTypes::V3: { Options options(_version, 0); if (getit(lsa, _version, options)) return options; if (getit(lsa, _version, options)) return options; if (getit(lsa, _version, options)) return options; if (getit(lsa, _version, options)) return options; xorp_throw(InvalidString, c_format("%s LSA does not have an options field (get)", lsa->name())); } break; } return Options(_version, 0); } template bool setit(Lsa *lsa, Options& options) { A *alsa = dynamic_cast(lsa); if (alsa) { alsa->set_options(options.get_options()); return true; } return false; } void BuildLsa::set_options(Lsa *lsa, Options& options) { switch(_version) { case OspfTypes::V2: lsa->get_header().set_options(options.get_options()); break; case OspfTypes::V3: { if (setit(lsa, options)) return; if (setit(lsa, options)) return; if (setit(lsa, options)) return; if (setit(lsa, options)) return; xorp_throw(InvalidString, c_format("%s LSA does not have an options field (set)", lsa->name())); } break; } } bool BuildLsa::common_header(Lsa *lsa, const string& word, Args& args) { // Note: OSPFv3 LSAs do not have an options field in the common // header. it reduces the amount of code to process the options // for the OSPFv3 case here. The get_options() and set_options() // will perform the relevant magic. If an attempt is made to set // an option on an OSPFv3 LSA that does not support options an // exception will be thrown. Some options are version specific in // the case of a version mismatch the option code will abort(). if ("age" == word) { string age; if (!args.get_next(age)) xorp_throw(InvalidString, c_format("No argument to age. [%s]", args.original_line().c_str())); lsa->get_header().set_ls_age(get_number(age)); } else if ("V6-bit" == word) { Options options = get_options(lsa); options.set_v6_bit(true); set_options(lsa, options); } else if ("E-bit" == word) { Options options = get_options(lsa); options.set_e_bit(true); set_options(lsa, options); } else if ("MC-bit" == word) { Options options = get_options(lsa); options.set_mc_bit(true); set_options(lsa, options); } else if ("N/P-bit" == word || "N-bit" == word) { Options options = get_options(lsa); options.set_n_bit(true); set_options(lsa, options); } else if ("R-bit" == word) { Options options = get_options(lsa); options.set_r_bit(true); set_options(lsa, options); } else if ("EA-bit" == word) { Options options = get_options(lsa); options.set_ea_bit(true); set_options(lsa, options); } else if ("DC-bit" == word) { Options options = get_options(lsa); options.set_dc_bit(true); set_options(lsa, options); } else if ("lsid" == word) { lsa->get_header(). set_link_state_id(set_id(get_next_word(args, word).c_str())); } else if ("adv" == word) { lsa->get_header(). set_advertising_router(set_id(get_next_word(args, word).c_str())); } else if ("seqno" == word) { lsa->get_header(). set_ls_sequence_number(get_next_number(args, word)); } else if ("cksum" == word) { lsa->get_header().set_ls_checksum(get_next_number(args, word)); } else { return false; } return true; } bool BuildLsa::router_link(RouterLsa *rlsa, const string& word, Args& args) { RouterLink rl(_version); if ("p2p" == word) { rl.set_type(RouterLink::p2p); } else if ("transit" == word) { rl.set_type(RouterLink::transit); } else if ("stub" == word) { rl.set_type(RouterLink::stub); } else if ("vlink" == word) { rl.set_type(RouterLink::vlink); } else { return false; } string nword; while(args.get_next(nword)) { if ("lsid" == nword) { // OSPFv2 rl.set_link_id(set_id(get_next_word(args, nword).c_str())); } else if ("ldata" == nword) { // OSPFv2 rl.set_link_data(set_id(get_next_word(args, nword).c_str())); } else if ("metric" == nword) { rl.set_metric(get_next_number(args, nword)); } else if ("iid" == nword) { // OSPFv3 rl.set_interface_id(get_next_number(args, nword)); } else if ("nid" == nword) { // OSPFv3 rl.set_neighbour_interface_id(get_next_number(args, nword)); } else if ("nrid" == nword) { // OSPFv3 rl.set_neighbour_router_id(set_id(get_next_word(args, nword). c_str())); } else { args.push_back(); break; } } rlsa->get_router_links().push_back(rl); return true; } Lsa * BuildLsa::router_lsa(Args& args) { RouterLsa *lsa = new RouterLsa(_version); string word; while(args.get_next(word)) { if (common_header(lsa, word, args)) continue; if (router_link(lsa, word, args)) continue; if ("bit-NT" == word) { lsa->set_nt_bit(true); } else if ("bit-W" == word) { // OSPFv3 lsa->set_w_bit(true); } else if ("bit-V" == word) { lsa->set_v_bit(true); } else if ("bit-E" == word) { lsa->set_e_bit(true); } else if ("bit-B" == word) { lsa->set_b_bit(true); } else { xorp_throw(InvalidString, c_format("Unknown option <%s>. [%s]", word.c_str(), args.original_line().c_str())); } } return lsa; } Lsa * BuildLsa::network_lsa(Args& args) { NetworkLsa *lsa = new NetworkLsa(_version); string word; while(args.get_next(word)) { if (common_header(lsa, word, args)) continue; if ("netmask" == word) { lsa->set_network_mask(get_next_number(args, word)); } else if ("add-router" == word) { lsa->get_attached_routers(). push_back(set_id(get_next_word(args, word).c_str())); } else { xorp_throw(InvalidString, c_format("Unknown option <%s>. [%s]", word.c_str(), args.original_line().c_str())); } } return lsa; } IPv6Prefix BuildLsa::ipv6prefix(Args& args, bool use_metric) { IPv6Prefix prefix(_version, use_metric); prefix.set_network(IPNet(get_next_word(args, "IPv6Prefix").c_str())); string nword; while(args.get_next(nword)) { if ("DN-bit" == nword) { prefix.set_dn_bit(true); } else if ("P-bit" == nword) { prefix.set_p_bit(true); } else if ("MC-bit" == nword) { prefix.set_mc_bit(true); } else if ("LA-bit" == nword) { prefix.set_la_bit(true); } else if ("NU-bit" == nword) { prefix.set_nu_bit(true); } else if (prefix.use_metric() && "metric" == nword) { prefix.set_metric(get_next_number(args, nword)); } else { args.push_back(); break; } } return prefix; } Lsa * BuildLsa::summary_network_lsa(Args& args) { SummaryNetworkLsa *lsa = new SummaryNetworkLsa(_version); string word; while(args.get_next(word)) { if (common_header(lsa, word, args)) continue; if ("netmask" == word) { // OSPFv2 lsa->set_network_mask(get_next_number(args, word)); } else if ("metric" == word) { lsa->set_metric(get_next_number(args, word)); } else if ("IPv6Prefix" == word) { // OSPFv3 lsa->set_ipv6prefix(ipv6prefix(args)); } else { xorp_throw(InvalidString, c_format("Unknown option <%s>. [%s]", word.c_str(), args.original_line().c_str())); } } return lsa; } Lsa * BuildLsa::summary_router_lsa(Args& args) { SummaryRouterLsa *lsa = new SummaryRouterLsa(_version); string word; while(args.get_next(word)) { if (common_header(lsa, word, args)) continue; if ("netmask" == word) { // OSPFv2 lsa->set_network_mask(get_next_number(args, word)); } else if ("metric" == word) { lsa->set_metric(get_next_number(args, word)); } else if ("drid" == word) { // OSPFv3 lsa->set_destination_id(set_id(get_next_word(args, word).c_str())); } else { xorp_throw(InvalidString, c_format("Unknown option <%s>. [%s]", word.c_str(), args.original_line().c_str())); } } return lsa; } Lsa * BuildLsa::as_external_lsa(Args& args) { ASExternalLsa *lsa = new ASExternalLsa(_version); string word; while(args.get_next(word)) { if (common_header(lsa, word, args)) continue; if ("netmask" == word) { lsa->set_network_mask(get_next_number(args, word)); } else if ("bit-E" == word) { lsa->set_e_bit(true); } else if ("bit-F" == word) { // OSPFv3 only lsa->set_f_bit(true); } else if ("bit-T" == word) { // OSPFv3 only lsa->set_t_bit(true); } else if ("metric" == word) { lsa->set_metric(get_next_number(args, word)); } else if ("IPv6Prefix" == word) { // OSPFv3 lsa->set_ipv6prefix(ipv6prefix(args)); } else if ("rlstype" == word) { // OSPFv3 lsa->set_referenced_ls_type(get_next_number(args, word)); } else if ("forward4" == word) { lsa->set_forwarding_address_ipv4(get_next_word(args,word).c_str()); } else if ("forward6" == word) { lsa->set_forwarding_address_ipv6(get_next_word(args,word).c_str()); } else if ("tag" == word) { lsa->set_external_route_tag(get_next_number(args, word)); } else if ("rlsid" == word) { lsa->set_referenced_link_state_id(set_id(get_next_word(args, word) .c_str())); } else { xorp_throw(InvalidString, c_format("Unknown option <%s>. [%s]", word.c_str(), args.original_line().c_str())); } } return lsa; } Lsa * BuildLsa::type_7_lsa(Args& args) { Type7Lsa *lsa = new Type7Lsa(_version); string word; while(args.get_next(word)) { if (common_header(lsa, word, args)) continue; if ("netmask" == word) { lsa->set_network_mask(get_next_number(args, word)); } else if ("bit-E" == word) { lsa->set_e_bit(true); } else if ("bit-F" == word) { // OSPFv3 only lsa->set_f_bit(true); } else if ("bit-T" == word) { // OSPFv3 only lsa->set_t_bit(true); } else if ("metric" == word) { lsa->set_metric(get_next_number(args, word)); } else if ("IPv6Prefix" == word) { // OSPFv3 lsa->set_ipv6prefix(ipv6prefix(args)); } else if ("rlstype" == word) { // OSPFv3 lsa->set_referenced_ls_type(get_next_number(args, word)); } else if ("forward4" == word) { lsa->set_forwarding_address_ipv4(get_next_word(args,word).c_str()); } else if ("forward6" == word) { lsa->set_forwarding_address_ipv6(get_next_word(args,word).c_str()); } else if ("tag" == word) { lsa->set_external_route_tag(get_next_number(args, word)); } else if ("rlsid" == word) { lsa->set_referenced_link_state_id(set_id(get_next_word(args, word) .c_str())); } else { xorp_throw(InvalidString, c_format("Unknown option <%s>. [%s]", word.c_str(), args.original_line().c_str())); } } return lsa; } Lsa * BuildLsa::link_lsa(Args& args) // OSPFv3 only { LinkLsa *lsa = new LinkLsa(_version); string word; while(args.get_next(word)) { if (common_header(lsa, word, args)) continue; if ("rtr-priority" == word) { lsa->set_rtr_priority(get_next_number(args, word)); } else if ("link-local-address" == word) { lsa->set_link_local_address(get_next_word(args, word).c_str()); } else if ("IPv6Prefix" == word) { lsa->get_prefixes().push_back(ipv6prefix(args)); } else { xorp_throw(InvalidString, c_format("Unknown option <%s>. [%s]", word.c_str(), args.original_line().c_str())); } } return lsa; } Lsa * BuildLsa::intra_area_prefix_lsa(Args& args) // OSPFv3 only { IntraAreaPrefixLsa *lsa = new IntraAreaPrefixLsa(_version); string word; while(args.get_next(word)) { if (common_header(lsa, word, args)) continue; if ("rlstype" == word) { string nword = get_next_word(args, word); uint16_t referenced_ls_type; if ("RouterLsa" == nword) { referenced_ls_type = RouterLsa(_version).get_ls_type(); } else if ("NetworkLsa" == nword) { referenced_ls_type = NetworkLsa(_version).get_ls_type(); } else { referenced_ls_type = get_number(nword); } lsa->set_referenced_ls_type(referenced_ls_type); } else if ("rlsid" == word) { lsa->set_referenced_link_state_id(set_id(get_next_word(args, word) .c_str())); } else if ("radv" == word) { lsa->set_referenced_advertising_router(set_id(get_next_word(args, word) .c_str())); } else if ("IPv6Prefix" == word) { lsa->get_prefixes().push_back(ipv6prefix(args, true)); } else { xorp_throw(InvalidString, c_format("Unknown option <%s>. [%s]", word.c_str(), args.original_line().c_str())); } } return lsa; } xorp/ospf/tests/test_checksum.cc0000664000076400007640000000550111421137511017136 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #define DEBUG_LOGGING #define DEBUG_PRINT_FUNCTION_NAME #include "ospf_module.h" #include "libxorp/xorp.h" #include "libxorp/test_main.hh" #include "libxorp/exceptions.hh" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "fletcher_checksum.hh" uint8_t data[] = { #include "iso512.data" }; uint8_t lsa1[] = { #include "lsa1.data" }; uint8_t lsa2[] = { #include "lsa2.data" }; bool test_checksum(TestInfo& info, const char *name, uint8_t* data, size_t len) { DOUT(info) << "Testing: " << name << endl; int32_t x, y; fletcher_checksum(data, len, 0 /* offset */, x, y); DOUT(info) << "x: " << x << " y: " << y << endl; if (!(255 == x && 255 == y)) { DOUT(info) << "Both values must be 255\n"; return false; } return true; } int main(int argc, char **argv) { XorpUnexpectedHandler x(xorp_unexpected_handler); xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); TestMain t(argc, argv); string test = t.get_optional_args("-t", "--test", "run only the specified test"); t.complete_args_parsing(); struct test { string test_name; XorpCallback1::RefPtr cb; } tests[] = { {"test1", callback(test_checksum, "iso512.data", &data[0], sizeof(data))}, {"test2", callback(test_checksum, "lsa1.data", &lsa1[0], sizeof(lsa1))}, {"test3", callback(test_checksum, "lsa2.data", &lsa2[0], sizeof(lsa2))}, }; try { if (test.empty()) { for (size_t i = 0; i < sizeof(tests) / sizeof(struct test); i++) t.run(tests[i].test_name, tests[i].cb); } else { for (size_t i = 0; i < sizeof(tests) / sizeof(struct test); i++) if (test == tests[i].test_name) { t.run(tests[i].test_name, tests[i].cb); return t.exit(); } t.failed("No test with name " + test + " found\n"); } } catch(...) { xorp_catch_standard_exceptions(); } xlog_stop(); xlog_exit(); return t.exit(); } xorp/ospf/tests/packet1.data0000664000076400007640000000244411421137511016154 0ustar greearbgreearb// $XORP: xorp/ospf/packet1.data,v 1.1 2006/10/16 06:50:04 atanu Exp $ // Bad OSPF packet the number of LSAs is illegal as well as the length of // the first LSA being too short. It is the illegal length that caused the // problem. // // http://www.xorp.org/bugzilla/show_bug.cgi?id=664 /* OSPF Version */ 0x02, /* OSPF Packet Type */ 0x04, /* Packet Length */ 0x00, 0x5c, /* Source OSPF Router ID */ 0x7f, 0x00, 0x00, 0x01, /* Area ID */ 0x00, 0x00, 0x00, 0x00, /* Packet Checksum */ 0xe7, 0xba, /* Auth Type */ 0x00, 0x00, /* Auth Data */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* LS Update Packet */ /* Number of LSAs: 98304515 (BAD) */ 0x05, 0xdc, 0x02, 0x03, /* Network LSA */ /* LS Age */ 0x00, 0x00, /* Options */ 0x00, /* LSA Type: 2 (Network LSA) */ 0x02, /* Link State ID */ 0x00, 0x3c,0x02, 0x01, /* Advertising Router */ 0x7f, 0x00, 0x00, 0x01, /* LS Sequence number */ 0x7f, 0x00, 0x00, 0x01, /* LS Checksum */ 0x80, 0x00, /* Length: 1 (BAD) */ 0x00, 0x01, /* Netmask */ 0x00, 0x00, 0x00, 0x14, /* Attached Router */ 0x00, 0x3c, 0x02, 0x01, /* Garbage */ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x80, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x3c, 0x02, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x02, 0x01, 0x80, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14 xorp/ospf/tests/test_routing.cc0000664000076400007640000013257611540224231017036 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #define DEBUG_LOGGING #define DEBUG_PRINT_FUNCTION_NAME #include "ospf_module.h" #include "libxorp/xorp.h" #include "libxorp/test_main.hh" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/callback.hh" #include "libxorp/tlv.hh" #include "libxorp/ipv4.hh" #include "libxorp/ipv6.hh" #include "libxorp/ipnet.hh" #include "libxorp/status_codes.h" #include "libxorp/service.hh" #include "libxorp/eventloop.hh" #include #include "libproto/spt.hh" #include "ospf.hh" #include "debug_io.hh" #include "delay_queue.hh" #include "vertex.hh" #include "area_router.hh" #include "test_common.hh" // Make sure that all tests free up any memory that they use. This will // allow us to use the leak checker program. void p2p(OspfTypes::Version version, RouterLsa *rlsa, OspfTypes::RouterID id, uint32_t link_data, uint32_t metric) { RouterLink rl(version); rl.set_type(RouterLink::p2p); rl.set_metric(metric); switch(version) { case OspfTypes::V2: rl.set_link_id(id); rl.set_link_data(link_data); break; case OspfTypes::V3: rl.set_interface_id(id); rl.set_neighbour_interface_id(0); rl.set_neighbour_router_id(id); break; } rlsa->get_router_links().push_back(rl); } void transit(OspfTypes::Version version, RouterLsa *rlsa, OspfTypes::RouterID id, uint32_t link_data, uint32_t metric) { RouterLink rl(version); rl.set_type(RouterLink::transit); rl.set_metric(metric); switch(version) { case OspfTypes::V2: rl.set_link_id(id); rl.set_link_data(link_data); break; case OspfTypes::V3: rl.set_interface_id(id); rl.set_neighbour_interface_id(0); rl.set_neighbour_router_id(id); break; } rlsa->get_router_links().push_back(rl); } void stub(OspfTypes::Version version, RouterLsa *rlsa, OspfTypes::RouterID id, uint32_t link_data, uint32_t metric) { RouterLink rl(version); switch(version) { case OspfTypes::V2: break; case OspfTypes::V3: return; break; } rl.set_type(RouterLink::stub); rl.set_metric(metric); switch(version) { case OspfTypes::V2: rl.set_link_id(id); rl.set_link_data(link_data); break; case OspfTypes::V3: rl.set_interface_id(id); rl.set_neighbour_interface_id(0); rl.set_neighbour_router_id(id); break; } rlsa->get_router_links().push_back(rl); } Lsa::LsaRef create_router_lsa(OspfTypes::Version version, uint32_t link_state_id, uint32_t advertising_router) { RouterLsa *rlsa = new RouterLsa(version); Lsa_header& header = rlsa->get_header(); uint32_t options = compute_options(version, OspfTypes::NORMAL); // Set the header fields. switch(version) { case OspfTypes::V2: header.set_options(options); break; case OspfTypes::V3: rlsa->set_options(options); break; } header.set_link_state_id(link_state_id); header.set_advertising_router(advertising_router); return Lsa::LsaRef(rlsa); } Lsa::LsaRef create_network_lsa(OspfTypes::Version version, uint32_t link_state_id, uint32_t advertising_router, uint32_t mask) { NetworkLsa *nlsa = new NetworkLsa(version); Lsa_header& header = nlsa->get_header(); uint32_t options = compute_options(version, OspfTypes::NORMAL); // Set the header fields. switch(version) { case OspfTypes::V2: header.set_options(options); nlsa->set_network_mask(mask); break; case OspfTypes::V3: nlsa->set_options(options); break; } header.set_link_state_id(link_state_id); header.set_advertising_router(advertising_router); return Lsa::LsaRef(nlsa); } Lsa::LsaRef create_summary_lsa(OspfTypes::Version version, uint32_t link_state_id, uint32_t advertising_router, uint32_t mask, uint32_t metric) { SummaryNetworkLsa *snlsa = new SummaryNetworkLsa(version); Lsa_header& header = snlsa->get_header(); uint32_t options = compute_options(version, OspfTypes::NORMAL); // Set the header fields. switch(version) { case OspfTypes::V2: header.set_options(options); snlsa->set_network_mask(mask); break; case OspfTypes::V3: XLOG_WARNING("TBD"); break; } header.set_link_state_id(link_state_id); header.set_advertising_router(advertising_router); snlsa->set_metric(metric); return Lsa::LsaRef(snlsa); } Lsa::LsaRef create_summary_lsa(OspfTypes::Version version, uint32_t link_state_id, uint32_t advertising_router, uint32_t metric) { SummaryRouterLsa *srlsa = new SummaryRouterLsa(version); Lsa_header& header = srlsa->get_header(); uint32_t options = compute_options(version, OspfTypes::NORMAL); // Set the header fields. switch(version) { case OspfTypes::V2: header.set_options(options); break; case OspfTypes::V3: break; } header.set_link_state_id(link_state_id); header.set_advertising_router(advertising_router); srlsa->set_metric(metric); return Lsa::LsaRef(srlsa); } Lsa::LsaRef create_external_lsa(OspfTypes::Version version, uint32_t link_state_id, uint32_t advertising_router) { ASExternalLsa *aselsa = new ASExternalLsa(version); Lsa_header& header = aselsa->get_header(); uint32_t options = compute_options(version, OspfTypes::NORMAL); // Set the header fields. switch(version) { case OspfTypes::V2: header.set_options(options); break; case OspfTypes::V3: break; } header.set_link_state_id(link_state_id); header.set_advertising_router(advertising_router); return Lsa::LsaRef(aselsa); } template bool verify_routes(TestInfo& info, uint32_t lineno, DebugIO& io, uint32_t routes) { if (routes != io.routing_table_size()) { DOUT(info) << "Line number: " << lineno << endl; DOUT(info) << "Expecting " << routes << " routes " << "got " << io.routing_table_size() << endl; return false; } return true; } // This is the origin. Lsa::LsaRef create_RT6(OspfTypes::Version version) { Lsa::LsaRef lsar = create_router_lsa(version, 6, 6); RouterLsa *rlsa = dynamic_cast(lsar.get()); XLOG_ASSERT(rlsa); // Link to RT3 p2p(version, rlsa, 3, 4, 6); // Link to RT5 p2p(version, rlsa, 5, 6, 6); // Link to RT10 XXX need to look at this more carefully. p2p(version, rlsa, 10, 11, 7); rlsa->encode(); rlsa->set_self_originating(true); return lsar; } Lsa::LsaRef create_RT3(OspfTypes::Version version) { Lsa::LsaRef lsar = create_router_lsa(version, 3, 3); RouterLsa *rlsa = dynamic_cast(lsar.get()); XLOG_ASSERT(rlsa); // Link to RT6 p2p(version, rlsa, 6, 7, 8); // Network to N4 stub(version, rlsa, (4 << 16), 0xffff0000, 2); // Network to N3 // network(version, rlsa, 3, 1); rlsa->encode(); return lsar; } // Some of the routers from Figure 2. in RFC 2328. Single area. // This router is R6. template bool routing1(TestInfo& info, OspfTypes::Version version) { EventLoop eventloop; DebugIO io(info, version, eventloop); io.startup(); Ospf ospf(version, eventloop, &io); ospf.trace().all(info.verbose()); ospf.set_router_id(set_id("0.0.0.6")); OspfTypes::AreaID area = set_id("128.16.64.16"); PeerManager& pm = ospf.get_peer_manager(); // Create an area pm.create_area_router(area, OspfTypes::NORMAL); // Create a peer associated with this area. const string interface = "eth0"; const string vif = "vif0"; A src; switch(src.ip_version()) { case 4: src = "192.150.187.78"; break; case 6: src = "2001:468:e21:c800:220:edff:fe61:f033"; break; default: XLOG_FATAL("Unknown IP version %d", src.ip_version()); break; } OspfTypes::PeerID peerid = pm. create_peer(interface, vif, src, OspfTypes::BROADCAST, area); // Bring the peering up if (!pm.set_state_peer(peerid, true)) { DOUT(info) << "Failed enable peer\n"; return false; } AreaRouter *ar = pm.get_area_router(area); XLOG_ASSERT(ar); ar->testing_replace_router_lsa(create_RT6(version)); ar->testing_add_lsa(create_RT3(version)); if (info.verbose()) ar->testing_print_link_state_database(); ar->testing_routing_total_recompute(); ar->testing_delete_lsa(create_RT3(version)); // At the time of writing the OSPFv3 routing table computations // were not complete, when they are remove this test. if (OspfTypes::V2 == version) { // At this point there should be a single route in the routing // table. if (!verify_routes(info, __LINE__, io, 1)) return false; const uint32_t routes = 1; if (routes != io.routing_table_size()) { DOUT(info) << "Expecting " << routes << " routes " << "got " << io.routing_table_size() << endl; return false; } if (!io.routing_table_verify(IPNet("0.4.0.0/16"), A("0.0.0.7"), 8, false, false)) { DOUT(info) << "Mismatch in routing table\n"; return false; } } // Now delete the routes. if (info.verbose()) ar->testing_print_link_state_database(); ar->testing_routing_total_recompute(); // Take the peering down if (!pm.set_state_peer(peerid, false)) { DOUT(info) << "Failed to disable peer\n"; return false; } // Delete the peer. if (!pm.delete_peer(peerid)) { DOUT(info) << "Failed to delete peer\n"; return false; } // Delete the area if (!pm.destroy_area_router(area)) { DOUT(info) << "Failed to delete area\n"; return false; } // The routing table should be empty now. if (0 != io.routing_table_size()) { DOUT(info) << "Expecting no routes " << "got " << io.routing_table_size() << endl; return false; } return true; } // Attempting to reproduce: // http://www.xorp.org/bugzilla/show_bug.cgi?id=226 bool routing2(TestInfo& info) { OspfTypes::Version version = OspfTypes::V2; EventLoop eventloop; DebugIO io(info, version, eventloop); io.startup(); Ospf ospf(version, eventloop, &io); ospf.trace().all(info.verbose()); OspfTypes::RouterID rid = set_id("10.0.8.161"); ospf.set_router_id(rid); OspfTypes::AreaID area = set_id("0.0.0.0"); PeerManager& pm = ospf.get_peer_manager(); // Create an area pm.create_area_router(area, OspfTypes::NORMAL); // Create a peer associated with this area. IPv4 src("172.16.1.1"); const string interface = "eth0"; const string vif = "vif0"; OspfTypes::PeerID peerid = pm. create_peer(interface, vif, src, OspfTypes::BROADCAST, area); // Bring the peering up if (!pm.set_state_peer(peerid, true)) { DOUT(info) << "Failed enable peer\n"; return false; } AreaRouter *ar = pm.get_area_router(area); XLOG_ASSERT(ar); // Create this router's Router-LSA Lsa::LsaRef lsar; lsar = create_router_lsa(version, rid, rid); RouterLsa *rlsa; rlsa = dynamic_cast(lsar.get()); XLOG_ASSERT(rlsa); transit(version, rlsa, set_id("172.16.1.2"), set_id("172.16.1.1"), 1); lsar->encode(); lsar->set_self_originating(true); ar->testing_replace_router_lsa(lsar); // Create the peer's Router-LSA OspfTypes::RouterID prid = set_id("172.16.1.2"); lsar = create_router_lsa(version, prid, prid); rlsa = dynamic_cast(lsar.get()); XLOG_ASSERT(rlsa); transit(version, rlsa, set_id("172.16.1.2"), set_id("172.16.1.2"), 1); stub(version, rlsa, set_id("172.16.2.1"), 0xffffffff, 1); stub(version, rlsa, set_id("172.16.1.100"), 0xffffffff, 1); lsar->encode(); ar->testing_add_lsa(lsar); // Create the Network-LSA that acts as the binding glue. lsar = create_network_lsa(version, prid, prid, 0xfffffffc); NetworkLsa *nlsa; nlsa = dynamic_cast(lsar.get()); XLOG_ASSERT(nlsa); nlsa->get_attached_routers().push_back(prid); nlsa->get_attached_routers().push_back(rid); lsar->encode(); ar->testing_add_lsa(lsar); /*********************************************************/ if (info.verbose()) ar->testing_print_link_state_database(); ar->testing_routing_total_recompute(); if (!verify_routes(info, __LINE__, io, 2)) return false; if (!io.routing_table_verify(IPNet("172.16.1.100/32"), IPv4("172.16.1.2"), 2, false, false)) { DOUT(info) << "Mismatch in routing table\n"; return false; } if (!io.routing_table_verify(IPNet("172.16.2.1/32"), IPv4("172.16.1.2"), 2, false, false)) { DOUT(info) << "Mismatch in routing table\n"; return false; } /*********************************************************/ ar->testing_delete_lsa(lsar); if (info.verbose()) ar->testing_print_link_state_database(); ar->testing_routing_total_recompute(); if (!verify_routes(info, __LINE__, io, 0)) return false; /*********************************************************/ // Create the Network-LSA again as the first one has been invalidated. lsar = create_network_lsa(version, prid, prid, 0xfffffffc); nlsa = dynamic_cast(lsar.get()); XLOG_ASSERT(nlsa); nlsa->get_attached_routers().push_back(prid); nlsa->get_attached_routers().push_back(rid); lsar->encode(); ar->testing_add_lsa(lsar); if (info.verbose()) ar->testing_print_link_state_database(); ar->testing_routing_total_recompute(); if (!verify_routes(info, __LINE__, io, 2)) return false; if (!io.routing_table_verify(IPNet("172.16.1.100/32"), IPv4("172.16.1.2"), 2, false, false)) { DOUT(info) << "Mismatch in routing table\n"; return false; } if (!io.routing_table_verify(IPNet("172.16.2.1/32"), IPv4("172.16.1.2"), 2, false, false)) { DOUT(info) << "Mismatch in routing table\n"; return false; } /*********************************************************/ ar->testing_delete_lsa(lsar); if (info.verbose()) ar->testing_print_link_state_database(); ar->testing_routing_total_recompute(); if (!verify_routes(info, __LINE__, io, 0)) return false; /*********************************************************/ // Take the peering down if (!pm.set_state_peer(peerid, false)) { DOUT(info) << "Failed to disable peer\n"; return false; } // Delete the peer. if (!pm.delete_peer(peerid)) { DOUT(info) << "Failed to delete peer\n"; return false; } // Delete the area if (!pm.destroy_area_router(area)) { DOUT(info) << "Failed to delete area\n"; return false; } // The routing table should be empty now. if (!verify_routes(info, __LINE__, io, 0)) return false; return true; } inline bool type_check(TestInfo& info, string message, uint32_t expected, uint32_t actual) { if (expected != actual) { DOUT(info) << message << " should be " << expected << " is " << actual << endl; return false; } return true; } /** * Read in LSA database files written by print_lsas.cc and run the * routing computation. */ template bool routing3(TestInfo& info, OspfTypes::Version version, string fname) { if (0 == fname.size()) { DOUT(info) << "No filename supplied\n"; return true; } EventLoop eventloop; DebugIO io(info, version, eventloop); io.startup(); Ospf ospf(version, eventloop, &io); ospf.trace().all(info.verbose()); Tlv tlv; if (!tlv.open(fname, true /* read */)) { DOUT(info) << "Failed to open " << fname << endl; return false; } vector data; uint32_t type; // Read the version field and check it. if (!tlv.read(type, data)) { DOUT(info) << "Failed to read " << fname << endl; return false; } if (!type_check(info, "TLV Version", TLV_VERSION, type)) return false; uint32_t tlv_version; if (!tlv.get32(data, 0, tlv_version)) { return false; } if (!type_check(info, "Version", TLV_CURRENT_VERSION, tlv_version)) return false; // Read the system info if (!tlv.read(type, data)) { DOUT(info) << "Failed to read " << fname << endl; return false; } if (!type_check(info, "TLV System info", TLV_SYSTEM_INFO, type)) return false; data.resize(data.size() + 1); data[data.size() - 1] = 0; DOUT(info) << "Built on " << &data[0] << endl; // Get OSPF version if (!tlv.read(type, data)) { DOUT(info) << "Failed to read " << fname << endl; return false; } if (!type_check(info, "TLV OSPF Version", TLV_OSPF_VERSION, type)) return false; uint32_t ospf_version; if (!tlv.get32(data, 0, ospf_version)) { return false; } if (!type_check(info, "OSPF Version", version, ospf_version)) return false; // OSPF area if (!tlv.read(type, data)) { DOUT(info) << "Failed to read " << fname << endl; return false; } if (!type_check(info, "TLV OSPF Area", TLV_AREA, type)) return false; OspfTypes::AreaID area; if (!tlv.get32(data, 0, area)) { return false; } PeerManager& pm = ospf.get_peer_manager(); pm.create_area_router(area, OspfTypes::NORMAL); AreaRouter *ar = pm.get_area_router(area); XLOG_ASSERT(ar); // The first LSA is this routers Router-LSA. if (!tlv.read(type, data)) { DOUT(info) << "Failed to read " << fname << endl; return false; } if (!type_check(info, "TLV LSA", TLV_LSA, type)) return false; LsaDecoder lsa_decoder(version); initialise_lsa_decoder(version, lsa_decoder); size_t len = data.size(); Lsa::LsaRef lsar = lsa_decoder.decode(&data[0], len); DOUT(info) << lsar->str() << endl; ospf.set_router_id(lsar->get_header().get_advertising_router()); lsar->set_self_originating(true); ar->testing_replace_router_lsa(lsar); // Keep reading LSAs until we run out or hit a new area. for(;;) { if (!tlv.read(type, data)) break; if (TLV_LSA != type) break; size_t len = data.size(); Lsa::LsaRef lsar = lsa_decoder.decode(&data[0], len); ar->testing_add_lsa(lsar); } if (info.verbose()) ar->testing_print_link_state_database(); ar->testing_routing_total_recompute(); return true; } /** * This is a similar problem to: * http://www.xorp.org/bugzilla/show_bug.cgi?id=295 * * Two Router-LSAs an a Network-LSA, the router under consideration * generated the Network-LSA. The other Router-LSA has the e and b * bits set so it must be installed in the routing table to help with * the routing computation. An AS-External-LSA generated by the other router. */ bool routing4(TestInfo& info) { OspfTypes::Version version = OspfTypes::V2; EventLoop eventloop; DebugIO io(info, version, eventloop); io.startup(); Ospf ospf(version, eventloop, &io); ospf.trace().all(info.verbose()); OspfTypes::AreaID area = set_id("0.0.0.0"); PeerManager& pm = ospf.get_peer_manager(); pm.create_area_router(area, OspfTypes::NORMAL); AreaRouter *ar = pm.get_area_router(area); XLOG_ASSERT(ar); OspfTypes::RouterID rid = set_id("10.0.1.1"); ospf.set_router_id(rid); // Create this router's Router-LSA Lsa::LsaRef lsar; lsar = create_router_lsa(version, rid, rid); RouterLsa *rlsa; rlsa = dynamic_cast(lsar.get()); XLOG_ASSERT(rlsa); transit(version, rlsa, rid, rid, 1 /* metric */); lsar->encode(); lsar->set_self_originating(true); ar->testing_replace_router_lsa(lsar); // Create the peer's Router-LSA OspfTypes::RouterID prid = set_id("10.0.1.6"); lsar = create_router_lsa(version, prid, prid); rlsa = dynamic_cast(lsar.get()); XLOG_ASSERT(rlsa); transit(version, rlsa, rid, prid, 1); rlsa->set_e_bit(true); rlsa->set_b_bit(true); lsar->encode(); ar->testing_add_lsa(lsar); // Create the Network-LSA that acts as the binding glue. lsar = create_network_lsa(version, rid, rid, 0xffff0000); NetworkLsa *nlsa; nlsa = dynamic_cast(lsar.get()); XLOG_ASSERT(nlsa); nlsa->get_attached_routers().push_back(rid); nlsa->get_attached_routers().push_back(prid); lsar->encode(); ar->testing_add_lsa(lsar); // Create the AS-External-LSA from the peer. lsar = create_external_lsa(version, set_id("10.20.0.0"), prid); ASExternalLsa *aselsa; aselsa = dynamic_cast(lsar.get()); XLOG_ASSERT(aselsa); aselsa->set_network_mask(0xffff0000); aselsa->set_metric(1); aselsa->set_forwarding_address_ipv4(IPv4("10.0.1.6")); lsar->encode(); ar->testing_add_lsa(lsar); /*********************************************************/ if (info.verbose()) ar->testing_print_link_state_database(); ar->testing_routing_total_recompute(); if (!verify_routes(info, __LINE__, io, 1)) return false; #if 0 // This route made it to the routing table but not to the RIB if (!io.routing_table_verify(IPNet("10.0.1.6/32"), IPv4("10.0.1.6"), 1, false, false)) { DOUT(info) << "Mismatch in routing table\n"; return false; } #endif if (!io.routing_table_verify(IPNet("10.20.0.0/16"), IPv4("10.0.1.6"), 2, false, false)) { DOUT(info) << "Mismatch in routing table\n"; return false; } return true; } /** * http://www.xorp.org/bugzilla/show_bug.cgi?id=359 * Used to demonstrate that routes would be introduced from dead LSAs. */ bool routing5(TestInfo& info) { OspfTypes::Version version = OspfTypes::V2; EventLoop eventloop; DebugIO io(info, version, eventloop); io.startup(); Ospf ospf(version, eventloop, &io); ospf.trace().all(info.verbose()); OspfTypes::AreaID area = set_id("0.0.0.0"); PeerManager& pm = ospf.get_peer_manager(); pm.create_area_router(area, OspfTypes::NORMAL); AreaRouter *ar = pm.get_area_router(area); XLOG_ASSERT(ar); OspfTypes::RouterID rid = set_id("10.0.1.1"); ospf.set_router_id(rid); // Create this router's Router-LSA Lsa::LsaRef lsar; lsar = create_router_lsa(version, rid, rid); RouterLsa *rlsa; rlsa = dynamic_cast(lsar.get()); XLOG_ASSERT(rlsa); transit(version, rlsa, rid, rid, 1 /* metric */); lsar->encode(); lsar->set_self_originating(true); ar->testing_replace_router_lsa(lsar); // Create the peer's Router-LSA OspfTypes::RouterID prid = set_id("10.0.1.6"); lsar = create_router_lsa(version, prid, prid); rlsa = dynamic_cast(lsar.get()); XLOG_ASSERT(rlsa); transit(version, rlsa, rid, prid, 1); rlsa->set_e_bit(true); rlsa->set_b_bit(true); lsar->encode(); ar->testing_add_lsa(lsar); // Create the second peer's Router-LSA OspfTypes::RouterID pprid = set_id("10.0.1.8"); lsar = create_router_lsa(version, pprid, pprid); rlsa = dynamic_cast(lsar.get()); XLOG_ASSERT(rlsa); transit(version, rlsa, rid, pprid, 1); rlsa->set_e_bit(true); rlsa->set_b_bit(true); lsar->encode(); ar->testing_add_lsa(lsar); // Create the Network-LSA that acts as the binding glue. lsar = create_network_lsa(version, rid, rid, 0xffff0000); NetworkLsa *nlsa; nlsa = dynamic_cast(lsar.get()); XLOG_ASSERT(nlsa); nlsa->get_attached_routers().push_back(rid); nlsa->get_attached_routers().push_back(prid); lsar->encode(); ar->testing_add_lsa(lsar); // Create the AS-External-LSA from the peer. lsar = create_external_lsa(version, set_id("10.20.0.0"), prid); ASExternalLsa *aselsa; aselsa = dynamic_cast(lsar.get()); XLOG_ASSERT(aselsa); aselsa->set_network_mask(0xffff0000); aselsa->set_metric(1); aselsa->set_forwarding_address_ipv4(IPv4("10.0.1.6")); lsar->encode(); ar->testing_add_lsa(lsar); // Create the AS-External-LSA from the second peer. lsar = create_external_lsa(version, set_id("10.21.0.0"), pprid); aselsa = dynamic_cast(lsar.get()); XLOG_ASSERT(aselsa); aselsa->set_network_mask(0xffff0000); aselsa->set_metric(1); aselsa->set_forwarding_address_ipv4(IPv4("10.0.1.8")); lsar->encode(); ar->testing_add_lsa(lsar); /*********************************************************/ if (info.verbose()) ar->testing_print_link_state_database(); ar->testing_routing_total_recompute(); if (!verify_routes(info, __LINE__, io, 1)) return false; if (!io.routing_table_verify(IPNet("10.20.0.0/16"), IPv4("10.0.1.6"), 2, false, false)) { DOUT(info) << "Mismatch in routing table\n"; return false; } return true; } /** * http://www.xorp.org/bugzilla/show_bug.cgi?id=374 * To test various forwarding address variants. */ bool routing6(TestInfo& info) { OspfTypes::Version version = OspfTypes::V2; EventLoop eventloop; DebugIO io(info, version, eventloop); io.startup(); Ospf ospf(version, eventloop, &io); ospf.trace().all(info.verbose()); OspfTypes::AreaID area = set_id("0.0.0.0"); PeerManager& pm = ospf.get_peer_manager(); pm.create_area_router(area, OspfTypes::NORMAL); AreaRouter *ar = pm.get_area_router(area); XLOG_ASSERT(ar); OspfTypes::RouterID rid = set_id("10.0.1.1"); ospf.set_router_id(rid); // Create this router's Router-LSA Lsa::LsaRef lsar; lsar = create_router_lsa(version, rid, rid); RouterLsa *rlsa; rlsa = dynamic_cast(lsar.get()); XLOG_ASSERT(rlsa); transit(version, rlsa, rid, rid, 1 /* metric */); lsar->encode(); lsar->set_self_originating(true); ar->testing_replace_router_lsa(lsar); // Create the peer's Router-LSA OspfTypes::RouterID prid = set_id("10.0.1.6"); lsar = create_router_lsa(version, prid, prid); rlsa = dynamic_cast(lsar.get()); XLOG_ASSERT(rlsa); transit(version, rlsa, rid, prid, 1); rlsa->set_e_bit(true); rlsa->set_b_bit(true); lsar->encode(); ar->testing_add_lsa(lsar); // Create the Network-LSA that acts as the binding glue. lsar = create_network_lsa(version, rid, rid, 0xffff0000); NetworkLsa *nlsa; nlsa = dynamic_cast(lsar.get()); XLOG_ASSERT(nlsa); nlsa->get_attached_routers().push_back(rid); nlsa->get_attached_routers().push_back(prid); lsar->encode(); ar->testing_add_lsa(lsar); // Create the AS-External-LSA from the peer with peer IP as forwarding // address. lsar = create_external_lsa(version, set_id("10.20.0.0"), prid); ASExternalLsa *aselsa; aselsa = dynamic_cast(lsar.get()); XLOG_ASSERT(aselsa); aselsa->set_network_mask(0xffff0000); aselsa->set_metric(1); aselsa->set_forwarding_address_ipv4(IPv4("10.0.1.6")); lsar->encode(); ar->testing_add_lsa(lsar); // Create the AS-External-LSA from the peer with 0.0.0.0 as forwarding // address. lsar = create_external_lsa(version, set_id("10.21.0.0"), prid); aselsa = dynamic_cast(lsar.get()); XLOG_ASSERT(aselsa); aselsa->set_network_mask(0xffff0000); aselsa->set_metric(1); aselsa->set_forwarding_address_ipv4(IPv4("0.0.0.0")); lsar->encode(); ar->testing_add_lsa(lsar); // Create the AS-External-LSA from the peer with third router (not in // OSPF domain) as forwarding address. lsar = create_external_lsa(version, set_id("10.22.0.0"), prid); aselsa = dynamic_cast(lsar.get()); XLOG_ASSERT(aselsa); aselsa->set_network_mask(0xffff0000); aselsa->set_metric(1); aselsa->set_forwarding_address_ipv4(IPv4("10.0.1.8")); lsar->encode(); ar->testing_add_lsa(lsar); /*********************************************************/ if (info.verbose()) ar->testing_print_link_state_database(); ar->testing_routing_total_recompute(); if (!verify_routes(info, __LINE__, io, 3)) return false; if (!io.routing_table_verify(IPNet("10.20.0.0/16"), IPv4("10.0.1.6"), 2, false, false)) { DOUT(info) << "Mismatch in routing table\n"; return false; } if (!io.routing_table_verify(IPNet("10.21.0.0/16"), IPv4("10.0.1.6"), 2, false, false)) { DOUT(info) << "Mismatch in routing table\n"; return false; } if (!io.routing_table_verify(IPNet("10.22.0.0/16"), IPv4("10.0.1.8"), 2, false, false)) { DOUT(info) << "Mismatch in routing table\n"; return false; } return true; } /** * http://www.xorp.org/bugzilla/show_bug.cgi?id=375 * To test various forwarding address variants in external LSA originated * not by directly connected neighbour. * +-------------+ DR +----------+ DR +----------+ * | This router | 10.0.0.0/16 | prid | 10.2.0.0/16 | pprid | * | 10.0.1.1 |-------------| 10.0.1.6 |-------------| 10.2.1.6 | * +-------------+ +----------+ +----------+ * 10.0.1.1 10.0.1.6 10.2.1.1 10.2.1.6 */ bool routing7(TestInfo& info) { OspfTypes::Version version = OspfTypes::V2; EventLoop eventloop; DebugIO io(info, version, eventloop); io.startup(); Ospf ospf(version, eventloop, &io); ospf.trace().all(info.verbose()); OspfTypes::AreaID area = set_id("0.0.0.0"); PeerManager& pm = ospf.get_peer_manager(); pm.create_area_router(area, OspfTypes::NORMAL); AreaRouter *ar = pm.get_area_router(area); XLOG_ASSERT(ar); OspfTypes::RouterID rid = set_id("10.0.1.1"); ospf.set_router_id(rid); // Create this router's Router-LSA Lsa::LsaRef lsar; lsar = create_router_lsa(version, rid, rid); RouterLsa *rlsa; rlsa = dynamic_cast(lsar.get()); XLOG_ASSERT(rlsa); transit(version, rlsa, rid, rid, 1 /* metric */); lsar->encode(); lsar->set_self_originating(true); ar->testing_replace_router_lsa(lsar); // Create the peer's Router-LSA OspfTypes::RouterID prid = set_id("10.0.1.6"); lsar = create_router_lsa(version, prid, prid); rlsa = dynamic_cast(lsar.get()); XLOG_ASSERT(rlsa); transit(version, rlsa, rid, prid, 1); transit(version, rlsa, set_id("10.2.1.1"), set_id("10.2.1.1"), 1); rlsa->set_e_bit(true); rlsa->set_b_bit(true); lsar->encode(); ar->testing_add_lsa(lsar); // Create the Network-LSA that acts as the binding glue. lsar = create_network_lsa(version, rid, rid, 0xffff0000); NetworkLsa *nlsa; nlsa = dynamic_cast(lsar.get()); XLOG_ASSERT(nlsa); nlsa->get_attached_routers().push_back(rid); nlsa->get_attached_routers().push_back(prid); lsar->encode(); ar->testing_add_lsa(lsar); // Create the Router-LSA from peer behind 10.0.1.6. OspfTypes::RouterID pprid = set_id("10.2.1.6"); lsar = create_router_lsa(version, pprid, pprid); rlsa = dynamic_cast(lsar.get()); XLOG_ASSERT(rlsa); transit(version, rlsa, set_id("10.2.1.1"), pprid, 1); rlsa->set_e_bit(true); rlsa->set_b_bit(true); lsar->encode(); ar->testing_add_lsa(lsar); // Network LSA to bind prid and pprid together. lsar = create_network_lsa(version, set_id("10.2.1.1"), prid, 0xffff0000); nlsa = dynamic_cast(lsar.get()); XLOG_ASSERT(nlsa); nlsa->get_attached_routers().push_back(prid); nlsa->get_attached_routers().push_back(pprid); lsar->encode(); ar->testing_add_lsa(lsar); // Create the AS-External-LSA from the peer with peer IP as forwarding // address. lsar = create_external_lsa(version, set_id("10.20.0.0"), pprid); ASExternalLsa *aselsa; aselsa = dynamic_cast(lsar.get()); XLOG_ASSERT(aselsa); aselsa->set_network_mask(0xffff0000); aselsa->set_metric(1); aselsa->set_forwarding_address_ipv4(IPv4("10.2.1.6")); lsar->encode(); ar->testing_add_lsa(lsar); // Create the AS-External-LSA from the peer with 0.0.0.0 as forwarding // address. lsar = create_external_lsa(version, set_id("10.21.0.0"), pprid); aselsa = dynamic_cast(lsar.get()); XLOG_ASSERT(aselsa); aselsa->set_network_mask(0xffff0000); aselsa->set_metric(1); aselsa->set_forwarding_address_ipv4(IPv4("0.0.0.0")); lsar->encode(); ar->testing_add_lsa(lsar); // Create the AS-External-LSA from the peer with third router (not in // OSPF domain) as forwarding address. lsar = create_external_lsa(version, set_id("10.22.0.0"), pprid); aselsa = dynamic_cast(lsar.get()); XLOG_ASSERT(aselsa); aselsa->set_network_mask(0xffff0000); aselsa->set_metric(1); aselsa->set_forwarding_address_ipv4(IPv4("10.2.1.8")); lsar->encode(); ar->testing_add_lsa(lsar); /*********************************************************/ if (info.verbose()) ar->testing_print_link_state_database(); ar->testing_routing_total_recompute(); if (!verify_routes(info, __LINE__, io, 4)) return false; if (!io.routing_table_verify(IPNet("10.2.0.0/16"), IPv4("10.0.1.6"), 2, false, false)) { DOUT(info) << "Mismatch in routing table\n"; return false; } if (!io.routing_table_verify(IPNet("10.20.0.0/16"), IPv4("10.0.1.6"), 3, false, false)) { DOUT(info) << "Mismatch in routing table\n"; return false; } if (!io.routing_table_verify(IPNet("10.21.0.0/16"), IPv4("10.0.1.6"), 2, false, false)) { DOUT(info) << "Mismatch in routing table\n"; return false; } if (!io.routing_table_verify(IPNet("10.22.0.0/16"), IPv4("10.0.1.6"), 3, false, false)) { DOUT(info) << "Mismatch in routing table\n"; return false; } return true; } /** * http://www.xorp.org/bugzilla/show_bug.cgi?id=395 * Verify that a Summary-LSA with the DefaultDestination is correctly * installed in the routing table. */ bool routing8(TestInfo& info) { OspfTypes::Version version = OspfTypes::V2; EventLoop eventloop; DebugIO io(info, version, eventloop); io.startup(); Ospf ospf(version, eventloop, &io); ospf.trace().all(info.verbose()); OspfTypes::AreaID area = set_id("0.0.0.0"); PeerManager& pm = ospf.get_peer_manager(); pm.create_area_router(area, OspfTypes::NORMAL); AreaRouter *ar = pm.get_area_router(area); XLOG_ASSERT(ar); OspfTypes::RouterID rid = set_id("10.0.1.1"); ospf.set_router_id(rid); // Create this router's Router-LSA Lsa::LsaRef lsar; lsar = create_router_lsa(version, rid, rid); RouterLsa *rlsa; rlsa = dynamic_cast(lsar.get()); XLOG_ASSERT(rlsa); transit(version, rlsa, rid, rid, 1 /* metric */); lsar->encode(); lsar->set_self_originating(true); ar->testing_replace_router_lsa(lsar); // Create the peer's Router-LSA OspfTypes::RouterID prid = set_id("10.0.1.6"); lsar = create_router_lsa(version, prid, prid); rlsa = dynamic_cast(lsar.get()); XLOG_ASSERT(rlsa); transit(version, rlsa, rid, prid, 1); rlsa->set_b_bit(true); lsar->encode(); ar->testing_add_lsa(lsar); // Create the Network-LSA that acts as the binding glue. lsar = create_network_lsa(version, rid, rid, 0xffff0000); NetworkLsa *nlsa; nlsa = dynamic_cast(lsar.get()); XLOG_ASSERT(nlsa); nlsa->get_attached_routers().push_back(rid); nlsa->get_attached_routers().push_back(prid); lsar->encode(); ar->testing_add_lsa(lsar); // Create the Summary-LSA with the DefaultDestination. lsar = create_summary_lsa(version, OspfTypes::DefaultDestination, prid, 0 /* mask */, 1 /* metric */); lsar->encode(); ar->testing_add_lsa(lsar); /*********************************************************/ if (info.verbose()) ar->testing_print_link_state_database(); ar->testing_routing_total_recompute(); if (!verify_routes(info, __LINE__, io, 1)) return false; if (!io.routing_table_verify(IPNet("0.0.0.0/0"), IPv4("10.0.1.6"), 2, false, false)) { DOUT(info) << "Mismatch in routing table\n"; return false; } return true; } /** * The same route can be in the routing table more than once either * due to a mis-configuration or simple race. * * +------------+ * |This router | * |128.16.64.16| * +------------+ * | 10.0.0.16 10.0.0.1 10.0.0.2 * ------------------------------------------------ 10.0.0.0/16 * | | * +----------+ +----------+ * | p1 | | p2 | * | 0.0.0.1 | | 0.0.0.2 | * +----------+ +----------+ * | | * ------------------------------------------------ 20.0.0.0/16 * 20.0.0.1 20.0.0.2 * * Network 10.0.0.0/16 the Network-LSA is generated by this router. * Network 20.0.0.0/16 the Network-LSA is generated by p1. * The Router-LSA from p2 is announcing 20.0.0.0/16 as a stub link. * * An attempt will be made to introduce 20.0.0.0/16 both from the * Network-LSA and the the stub link of the Router-LSA. */ bool routing9(TestInfo& info) { OspfTypes::Version version = OspfTypes::V2; EventLoop eventloop; DebugIO io(info, version, eventloop); io.startup(); Ospf ospf(version, eventloop, &io); ospf.trace().all(info.verbose()); OspfTypes::AreaID area = set_id("0.0.0.0"); PeerManager& pm = ospf.get_peer_manager(); pm.create_area_router(area, OspfTypes::NORMAL); AreaRouter *ar = pm.get_area_router(area); XLOG_ASSERT(ar); OspfTypes::RouterID rid = set_id("128.16.64.16"); ospf.set_router_id(rid); // Create this router's Router-LSA Lsa::LsaRef lsar; lsar = create_router_lsa(version, rid, rid); RouterLsa *rlsa; rlsa = dynamic_cast(lsar.get()); XLOG_ASSERT(rlsa); transit(version, rlsa, set_id("10.0.0.16"), set_id("10.0.0.16"), 1); lsar->encode(); lsar->set_self_originating(true); ar->testing_replace_router_lsa(lsar); // Create the p1 Router-LSA OspfTypes::RouterID p1_rid = set_id("0.0.0.1"); lsar = create_router_lsa(version, p1_rid, p1_rid); rlsa = dynamic_cast(lsar.get()); XLOG_ASSERT(rlsa); transit(version, rlsa, set_id("10.0.0.16"), set_id("10.0.0.1"), 1); transit(version, rlsa, set_id("20.0.0.1"), set_id("20.0.0.1"), 1); lsar->encode(); ar->testing_add_lsa(lsar); // Create the p2 Router-LSA OspfTypes::RouterID p2_rid = set_id("0.0.0.2"); lsar = create_router_lsa(version, p2_rid, p2_rid); rlsa = dynamic_cast(lsar.get()); XLOG_ASSERT(rlsa); transit(version, rlsa, set_id("10.0.0.16"), set_id("10.0.0.2"), 1); stub(version, rlsa, set_id("20.0.0.0"), 0xffff0000, 1); // This Router-LSA has not yet transitioned to the the transit below. // transit(version, rlsa, set_id("20.0.0.1"), set_id("20.0.0.2"), 1); lsar->encode(); ar->testing_add_lsa(lsar); // Create the Network-LSA that acts as the binding glue on the // 10.0.0.0/16 network. lsar = create_network_lsa(version, set_id("10.0.0.16"), rid, 0xffff0000); NetworkLsa *nlsa; nlsa = dynamic_cast(lsar.get()); XLOG_ASSERT(nlsa); nlsa->get_attached_routers().push_back(rid); nlsa->get_attached_routers().push_back(p1_rid); nlsa->get_attached_routers().push_back(p2_rid); lsar->encode(); ar->testing_add_lsa(lsar); // Create the Network-LSA that acts as the binding glue on the // 20.0.0.0/16 network. lsar = create_network_lsa(version, set_id("20.0.0.1"), p1_rid, 0xffff0000); nlsa = dynamic_cast(lsar.get()); XLOG_ASSERT(nlsa); nlsa->get_attached_routers().push_back(p1_rid); nlsa->get_attached_routers().push_back(p2_rid); lsar->encode(); ar->testing_add_lsa(lsar); /*********************************************************/ if (info.verbose()) ar->testing_print_link_state_database(); ar->testing_routing_total_recompute(); if (!verify_routes(info, __LINE__, io, 1)) return false; if (!io.routing_table_verify(IPNet("20.0.0.0/16"), IPv4("10.0.0.1"), 2, false, false)) { DOUT(info) << "Mismatch in routing table\n"; return false; } return true; } bool routing10(TestInfo& info) { OspfTypes::Version version = OspfTypes::V2; EventLoop eventloop; DebugIO io(info, version, eventloop); io.startup(); Ospf ospf(version, eventloop, &io); ospf.trace().all(info.verbose()); OspfTypes::AreaID area = set_id("0.0.0.0"); PeerManager& pm = ospf.get_peer_manager(); pm.create_area_router(area, OspfTypes::NORMAL); AreaRouter *ar = pm.get_area_router(area); XLOG_ASSERT(ar); OspfTypes::RouterID prid = set_id("10.10.10.149"); ospf.set_router_id(prid); Lsa::LsaRef lsar; RouterLsa *rlsa; // Set testing to true to force the 2-Way test to return true. ospf.set_testing(true); // Create the peer 1 (DR) Router-LSA OspfTypes::RouterID rid = set_id("10.10.10.125"); lsar = create_router_lsa(version, rid, rid); rlsa = dynamic_cast(lsar.get()); XLOG_ASSERT(rlsa); transit(version, rlsa, rid, rid, 1); rlsa->set_e_bit(true); rlsa->set_b_bit(true); lsar->encode(); ar->testing_add_lsa(lsar); // Create this router's Router-LSA lsar = create_router_lsa(version, prid, prid); rlsa = dynamic_cast(lsar.get()); XLOG_ASSERT(rlsa); transit(version, rlsa, rid, prid, 1 /* metric */); lsar->encode(); lsar->set_self_originating(true); ar->testing_replace_router_lsa(lsar); // Create the peer 2 Router-LSA OspfTypes::RouterID pprid = set_id("10.10.10.18"); lsar = create_router_lsa(version, pprid, pprid); rlsa = dynamic_cast(lsar.get()); XLOG_ASSERT(rlsa); transit(version, rlsa, rid, pprid, 1); rlsa->set_e_bit(true); rlsa->set_b_bit(true); lsar->encode(); ar->testing_add_lsa(lsar); // Create the Network-LSA that acts as the binding glue. lsar = create_network_lsa(version, rid, rid, 0xffffff00); NetworkLsa *nlsa; nlsa = dynamic_cast(lsar.get()); XLOG_ASSERT(nlsa); nlsa->get_attached_routers().push_back(rid); nlsa->get_attached_routers().push_back(prid); nlsa->get_attached_routers().push_back(pprid); lsar->encode(); ar->testing_add_lsa(lsar); // Peer 2 originates External LSA lsar = create_external_lsa(version, set_id("10.20.30.0"), pprid); ASExternalLsa *aselsa; aselsa = dynamic_cast(lsar.get()); XLOG_ASSERT(aselsa); aselsa->set_network_mask(0xffffff00); aselsa->set_metric(1); aselsa->set_forwarding_address_ipv4(IPv4("0.0.0.0")); lsar->encode(); ar->testing_add_lsa(lsar); if (info.verbose()) ar->testing_print_link_state_database(); ar->testing_routing_total_recompute(); if (!verify_routes(info, __LINE__, io, 1)) return false; if (!io.routing_table_verify(IPNet("10.20.30.0/24"), IPv4("10.10.10.18"), 2, false, false)) { DOUT(info) << "Mismatch in routing table\n"; return false; } return true; } int main(int argc, char **argv) { XorpUnexpectedHandler x(xorp_unexpected_handler); xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); TestMain t(argc, argv); string test = t.get_optional_args("-t", "--test", "run only the specified test"); string fname =t.get_optional_args("-f", "--filename", "lsa database"); t.complete_args_parsing(); struct test { string test_name; XorpCallback1::RefPtr cb; } tests[] = { {"r1V2", callback(routing1, OspfTypes::V2)}, {"r1V3", callback(routing1, OspfTypes::V3)}, {"r2", callback(routing2)}, {"r3V2", callback(routing3, OspfTypes::V2, fname)}, {"r3V3", callback(routing3, OspfTypes::V3, fname)}, {"r4", callback(routing4)}, {"r5", callback(routing5)}, {"r6", callback(routing6)}, {"r7", callback(routing7)}, {"r8", callback(routing8)}, {"r9", callback(routing9)}, {"r10", callback(routing10)}, }; try { if (test.empty()) { for (size_t i = 0; i < sizeof(tests) / sizeof(struct test); i++) t.run(tests[i].test_name, tests[i].cb); } else { for (size_t i = 0; i < sizeof(tests) / sizeof(struct test); i++) if (test == tests[i].test_name) { t.run(tests[i].test_name, tests[i].cb); return t.exit(); } t.failed("No test with name " + test + " found\n"); } } catch(...) { xorp_catch_standard_exceptions(); } xlog_stop(); xlog_exit(); return t.exit(); } xorp/ospf/xrl_io.cc0000664000076400007640000012007711631770161014444 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #include "ospf_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/callback.hh" #include "libxorp/ipv4.hh" #include "libxorp/ipv6.hh" #include "libxorp/ipnet.hh" #include "libxorp/status_codes.h" #include "libxorp/service.hh" #include "libxorp/eventloop.hh" #include "xrl/interfaces/fea_rawpkt4_xif.hh" #ifdef HAVE_IPV6 #include "xrl/interfaces/fea_rawpkt6_xif.hh" #endif #include "xrl/interfaces/rib_xif.hh" #include "ospf.hh" #include "xrl_io.hh" template void XrlIO::recv(const string& interface, const string& vif, A src, A dst, uint8_t ip_protocol, int32_t ip_ttl, int32_t ip_tos, bool ip_router_alert, bool ip_internet_control, const vector& payload) { debug_msg("recv(interface = %s, vif = %s, src = %s, dst = %s, " "protocol = %u, ttl = %d tos = 0x%x router_alert = %s, " "internet_control = %s payload_size = %u\n", interface.c_str(), vif.c_str(), src.str().c_str(), dst.str().c_str(), XORP_UINT_CAST(ip_protocol), XORP_INT_CAST(ip_ttl), ip_tos, bool_c_str(ip_router_alert), bool_c_str(ip_internet_control), XORP_UINT_CAST(payload.size())); if (IO::_receive_cb.is_empty()) return; // // XXX: create a copy of the payload, because the callback's argument // is not const-ified. // vector payload_copy(payload); // Actual receive code is in ospf.cc: Ospf::receive IO::_receive_cb->dispatch(interface, vif, dst, src, &payload_copy[0], payload_copy.size()); } template <> bool XrlIO::send(const string& interface, const string& vif, IPv4 dst, IPv4 src, int ttl, uint8_t* data, uint32_t len) { bool success; debug_msg("XrlIO::send send(interface = %s, vif = %s, src = %s, dst = %s, " "payload_size = %u\n", interface.c_str(), vif.c_str(), src.str().c_str(), dst.str().c_str(), XORP_UINT_CAST(len)); // Copy the payload vector payload(len); memcpy(&payload[0], data, len); XrlRawPacket4V0p1Client fea_client(&_xrl_router); success = fea_client.send_send( _feaname.c_str(), interface, vif, src, dst, get_ip_protocol_number(), ttl, -1, // XXX: let the FEA set TOS get_ip_router_alert(), true, // ip_internet_control payload, callback(this, &XrlIO::send_cb, interface, vif)); return success; } template void XrlIO::send_cb(const XrlError& xrl_error, string interface, string vif) { UNUSED(interface); UNUSED(vif); switch (xrl_error.error_code()) { case OKAY: // Success break; case REPLY_TIMED_OUT: XLOG_ERROR("Cannot send a packet on interface %s vif %s: %s", interface.c_str(), vif.c_str(), xrl_error.str().c_str()); break; case RESOLVE_FAILED: case SEND_FAILED: case SEND_FAILED_TRANSIENT: case NO_SUCH_METHOD: XLOG_ERROR("Cannot send a packet on interface %s vif %s: %s", interface.c_str(), vif.c_str(), xrl_error.str().c_str()); break; case NO_FINDER: // Finders can time-out and go away when system is under extreme load. // I think they will be re-built again later, so don't crash here...just // print error and soldier on. --Ben XLOG_ERROR("Cannot send a packet on interface %s vif %s (NO_FINDER): %s", interface.c_str(), vif.c_str(), xrl_error.str().c_str()); break; case BAD_ARGS: case COMMAND_FAILED: case INTERNAL_ERROR: XLOG_ERROR("Cannot send a packet on interface %s vif %s: %s", interface.c_str(), vif.c_str(), xrl_error.str().c_str()); break; } } template <> bool XrlIO::enable_interface_vif(const string& interface, const string& vif) { bool success; XLOG_WARNING("XRL-IO: Enable Interface %s Vif %s\n", interface.c_str(), vif.c_str()); XrlRawPacket4V0p1Client fea_client(&_xrl_router); success = fea_client.send_register_receiver( _feaname.c_str(), _xrl_router.instance_name(), interface, vif, get_ip_protocol_number(), false, // disable multicast loopback callback(this, &XrlIO::enable_interface_vif_cb, interface, vif)); return success; } template void XrlIO::enable_interface_vif_cb(const XrlError& xrl_error, string interface, string vif) { UNUSED(interface); UNUSED(vif); switch (xrl_error.error_code()) { case OKAY: // Success break; case REPLY_TIMED_OUT: XLOG_ERROR("Cannot enable interface %s vif %s: %s", interface.c_str(), vif.c_str(), xrl_error.str().c_str()); break; case RESOLVE_FAILED: case SEND_FAILED: case SEND_FAILED_TRANSIENT: case NO_SUCH_METHOD: XLOG_ERROR("Cannot enable interface %s vif %s: %s", interface.c_str(), vif.c_str(), xrl_error.str().c_str()); break; case NO_FINDER: // XXX - Temporarily code dump if this condition occurs. XLOG_FATAL("NO FINDER"); break; case BAD_ARGS: case COMMAND_FAILED: case INTERNAL_ERROR: XLOG_FATAL("Cannot enable interface %s vif %s: %s", interface.c_str(), vif.c_str(), xrl_error.str().c_str()); break; } } template <> bool XrlIO::disable_interface_vif(const string& interface, const string& vif) { bool success; XLOG_WARNING("XRL-IO: Disable Interface %s Vif %s\n", interface.c_str(), vif.c_str()); XrlRawPacket4V0p1Client fea_client(&_xrl_router); success = fea_client.send_unregister_receiver( _feaname.c_str(), _xrl_router.instance_name(), interface, vif, get_ip_protocol_number(), callback(this, &XrlIO::disable_interface_vif_cb, interface, vif)); return success; } template void XrlIO::disable_interface_vif_cb(const XrlError& xrl_error, string interface, string vif) { UNUSED(interface); UNUSED(vif); switch (xrl_error.error_code()) { case OKAY: // Success break; case REPLY_TIMED_OUT: XLOG_ERROR("Cannot disable interface %s vif %s: %s", interface.c_str(), vif.c_str(), xrl_error.str().c_str()); break; case RESOLVE_FAILED: case SEND_FAILED: case SEND_FAILED_TRANSIENT: case NO_SUCH_METHOD: XLOG_ERROR("Cannot disable interface %s vif %s: %s", interface.c_str(), vif.c_str(), xrl_error.str().c_str()); break; case NO_FINDER: // XXX - Temporarily code dump if this condition occurs. XLOG_FATAL("NO FINDER"); break; case BAD_ARGS: case COMMAND_FAILED: case INTERNAL_ERROR: XLOG_FATAL("Cannot disable interface %s vif %s: %s", interface.c_str(), vif.c_str(), xrl_error.str().c_str()); break; } } template bool XrlIO::is_interface_enabled(const string& interface) const { debug_msg("Interface %s\n", interface.c_str()); const IfMgrIfAtom* fi = ifmgr_iftree().find_interface(interface); if (fi == NULL) return false; return (fi->enabled() && (! fi->no_carrier())); } template bool XrlIO::is_vif_enabled(const string& interface, const string& vif) const { debug_msg("Interface %s Vif %s\n", interface.c_str(), vif.c_str()); if (! is_interface_enabled(interface)) return false; const IfMgrVifAtom* fv = ifmgr_iftree().find_vif(interface, vif); if (fv == NULL) return false; return (fv->enabled()); } template <> bool XrlIO::is_address_enabled(const string& interface, const string& vif, const IPv4& address) const { if (! is_vif_enabled(interface, vif)) { XLOG_INFO("vif %s/%s is not enabled.\n", interface.c_str(), vif.c_str()); return false; } const IfMgrIPv4Atom* fa = ifmgr_iftree().find_addr(interface, vif, address); if (fa == NULL) { XLOG_INFO("Cannot find ipv4 atom: %s/%s addr: %s\n", interface.c_str(), vif.c_str(), cstring(address)); return false; } bool rv = fa->enabled(); if (!rv) { XLOG_INFO("IPv4 atom: %s/%s addr: %s is not enabled.\n", interface.c_str(), vif.c_str(), cstring(address)); } return rv; } template <> bool XrlIO::get_addresses(const string& interface, const string& vif, list& addresses) const { debug_msg("Interface %s Vif %s\n", interface.c_str(), vif.c_str()); const IfMgrVifAtom* fv = ifmgr_iftree().find_vif(interface, vif); if (fv == NULL) return false; IfMgrVifAtom::IPv4Map::const_iterator i; for (i = fv->ipv4addrs().begin(); i != fv->ipv4addrs().end(); i++) addresses.push_back(i->second.addr()); return true; } template <> bool XrlIO::get_link_local_address(const string& interface, const string& vif, IPv4& address) { debug_msg("Interface %s Vif %s\n", interface.c_str(), vif.c_str()); const IfMgrVifAtom* fv = ifmgr_iftree().find_vif(interface, vif); if (fv == NULL) return false; IfMgrVifAtom::IPv4Map::const_iterator i; for (i = fv->ipv4addrs().begin(); i != fv->ipv4addrs().end(); i++) { if (i->second.addr().is_linklocal_unicast()) { address = i->second.addr(); return true; } } return false; } template bool XrlIO::get_interface_id(const string& interface, uint32_t& interface_id) { debug_msg("Interface %s\n", interface.c_str()); const IfMgrIfAtom* fi = ifmgr_iftree().find_interface(interface); if (fi == NULL) return false; interface_id = fi->pif_index(); return true; } template <> uint32_t XrlIO::get_prefix_length(const string& interface, const string& vif, IPv4 address) { debug_msg("Interface %s Vif %s Address %s\n", interface.c_str(), vif.c_str(), cstring(address)); const IfMgrIPv4Atom* fa = ifmgr_iftree().find_addr(interface, vif, address); if (fa == NULL) return 0; return (fa->prefix_len()); } template uint32_t XrlIO::get_mtu(const string& interface) { debug_msg("Interface %s\n", interface.c_str()); const IfMgrIfAtom* fi = ifmgr_iftree().find_interface(interface); if (fi == NULL) return 0; return (fi->mtu()); } template <> bool XrlIO::join_multicast_group(const string& interface, const string& vif, IPv4 mcast) { bool success; debug_msg("Join Interface %s Vif %s mcast %s\n", interface.c_str(), vif.c_str(), cstring(mcast)); XrlRawPacket4V0p1Client fea_client(&_xrl_router); success = fea_client.send_join_multicast_group( _feaname.c_str(), _xrl_router.instance_name(), interface, vif, get_ip_protocol_number(), mcast, callback(this, &XrlIO::join_multicast_group_cb, interface, vif)); return success; } template void XrlIO::join_multicast_group_cb(const XrlError& xrl_error, string interface, string vif) { UNUSED(interface); UNUSED(vif); switch (xrl_error.error_code()) { case OKAY: // Success break; case REPLY_TIMED_OUT: XLOG_ERROR("Cannot join a multicast group on interface %s vif %s: %s (TIMED_OUT)", interface.c_str(), vif.c_str(), xrl_error.str().c_str()); break; case RESOLVE_FAILED: case SEND_FAILED: case SEND_FAILED_TRANSIENT: case NO_SUCH_METHOD: XLOG_ERROR("Cannot join a multicast group on interface %s vif %s: %s (RESOLVE or SEND failed, or not such method)", interface.c_str(), vif.c_str(), xrl_error.str().c_str()); break; case NO_FINDER: // XXX - Temporarily core dump if this condition occurs. XLOG_FATAL("NO FINDER"); break; case BAD_ARGS: case COMMAND_FAILED: case INTERNAL_ERROR: XLOG_ERROR("Cannot join a multicast group on interface %s vif %s: %s (BAD_ARGS, CMD_FAILED, INTERNAL_ERR)", interface.c_str(), vif.c_str(), xrl_error.str().c_str()); break; } } template <> bool XrlIO::leave_multicast_group(const string& interface, const string& vif, IPv4 mcast) { bool success; debug_msg("Leave Interface %s Vif %s mcast %s\n", interface.c_str(), vif.c_str(), cstring(mcast)); XrlRawPacket4V0p1Client fea_client(&_xrl_router); success = fea_client.send_leave_multicast_group( _feaname.c_str(), _xrl_router.instance_name(), interface, vif, get_ip_protocol_number(), mcast, callback(this, &XrlIO::leave_multicast_group_cb, interface, vif)); return success; } template void XrlIO::leave_multicast_group_cb(const XrlError& xrl_error, string interface, string vif) { UNUSED(interface); UNUSED(vif); switch (xrl_error.error_code()) { case OKAY: // Success break; case REPLY_TIMED_OUT: XLOG_ERROR("Cannot leave a multicast group on interface %s vif %s: %s", interface.c_str(), vif.c_str(), xrl_error.str().c_str()); break; case RESOLVE_FAILED: case SEND_FAILED: case SEND_FAILED_TRANSIENT: case NO_SUCH_METHOD: XLOG_ERROR("Cannot leave a multicast group on interface %s vif %s: %s", interface.c_str(), vif.c_str(), xrl_error.str().c_str()); break; case NO_FINDER: // XXX - Temporarily code dump if this condition occurs. XLOG_FATAL("NO FINDER"); break; case BAD_ARGS: case COMMAND_FAILED: case INTERNAL_ERROR: XLOG_ERROR("Cannot leave a multicast group on interface %s vif %s: %s", interface.c_str(), vif.c_str(), xrl_error.str().c_str()); break; } } template void XrlIO::register_rib() { XrlRibV0p1Client rib(&_xrl_router); //create our tables //ebgp - v4 //name - "ebgp" //unicast - true //multicast - true if(!rib.send_add_igp_table4(_ribname.c_str(), "ospf", _xrl_router.class_name(), _xrl_router.instance_name(), true, true, callback(this, &XrlIO::rib_command_done, true, "add_table"))) { XLOG_FATAL("Failed to add OSPF table to RIB"); } #ifdef HAVE_IPV6 if(!rib.send_add_igp_table6(_ribname.c_str(), "ospf", _xrl_router.class_name(), _xrl_router.instance_name(), true, true, callback(this, &XrlIO::rib_command_done, true, "add_table"))) { XLOG_FATAL("Failed to add OSPF table to RIB"); } #endif } template void XrlIO::unregister_rib() { XrlRibV0p1Client rib(&_xrl_router); if(!rib.send_delete_igp_table4(_ribname.c_str(), "ospf", _xrl_router.class_name(), _xrl_router.instance_name(), true, true, callback(this, &XrlIO::rib_command_done, false, "delete table"))) { XLOG_FATAL("Failed to delete OSPF table to RIB"); } #ifdef HAVE_IPV6 if(!rib.send_delete_igp_table6(_ribname.c_str(), "ospf", _xrl_router.class_name(), _xrl_router.instance_name(), true, true, callback(this, &XrlIO::rib_command_done, false, "delete table"))) { XLOG_FATAL("Failed to delete OSPF table to RIB"); } #endif } template void XrlIO::rib_command_done(const XrlError& error, bool up, const char *comment) { debug_msg("callback %s %s\n", comment, cstring(error)); switch (error.error_code()) { case OKAY: break; case REPLY_TIMED_OUT: // We should really be using a reliable transport where // this error cannot happen. But it has so lets retry if we can. XLOG_ERROR("callback: %s %s", comment, cstring(error)); break; case RESOLVE_FAILED: case SEND_FAILED: case SEND_FAILED_TRANSIENT: case NO_SUCH_METHOD: XLOG_ERROR("callback: %s %s", comment, error.str().c_str()); break; case NO_FINDER: // XXX - Temporarily code dump if this condition occurs. XLOG_FATAL("NO FINDER"); break; case BAD_ARGS: case COMMAND_FAILED: case INTERNAL_ERROR: XLOG_FATAL("callback: %s %s", comment, error.str().c_str()); break; } if (up) component_up("rib_command_done"); else component_down("rib_command_done"); } template bool XrlIO::add_route(IPNet net, A nexthop, uint32_t nexthop_id, uint32_t metric, bool equal, bool discard, const PolicyTags& policytags) { debug_msg("Net %s Nexthop %s metric %d equal %s discard %s policy %s\n", cstring(net), cstring(nexthop), metric, bool_c_str(equal), bool_c_str(discard), cstring(policytags)); _rib_queue.queue_add_route(_ribname, net, nexthop, nexthop_id, metric, policytags); return true; } template bool XrlIO::replace_route(IPNet net, A nexthop, uint32_t nexthop_id, uint32_t metric, bool equal, bool discard, const PolicyTags& policytags) { debug_msg("Net %s Nexthop %s metric %d equal %s discard %s policy %s\n", cstring(net), cstring(nexthop), metric, bool_c_str(equal), bool_c_str(discard), cstring(policytags)); // XXX - The queue should support replace see TODO 36. _rib_queue.queue_delete_route(_ribname, net); _rib_queue.queue_add_route(_ribname, net, nexthop, nexthop_id, metric, policytags); return true; } template bool XrlIO::delete_route(IPNet net) { debug_msg("Net %s\n", cstring(net)); _rib_queue.queue_delete_route(_ribname, net); return true; } template XrlQueue::XrlQueue(EventLoop& eventloop, XrlRouter& xrl_router) : _io(0), _eventloop(eventloop), _xrl_router(xrl_router), _flying(0) { } template EventLoop& XrlQueue::eventloop() const { return _eventloop; } template void XrlQueue::queue_add_route(string ribname, const IPNet& net, const A& nexthop, uint32_t nexthop_id, uint32_t metric, const PolicyTags& policytags) { Queued q; #if 0 if (_bgp.profile().enabled(profile_route_rpc_in)) _bgp.profile().log(profile_route_rpc_in, c_format("add %s", net.str().c_str())); #endif q.add = true; q.ribname = ribname; q.net = net; q.nexthop = nexthop; q.nexthop_id = nexthop_id; q.metric = metric; q.comment = c_format("add_route: ribname %s net %s nexthop %s", ribname.c_str(), net.str().c_str(), nexthop.str().c_str()); q.policytags = policytags; _xrl_queue.push_back(q); start(); } template void XrlQueue::queue_delete_route(string ribname, const IPNet& net) { Queued q; #if 0 if (_bgp.profile().enabled(profile_route_rpc_in)) _bgp.profile().log(profile_route_rpc_in, c_format("delete %s", net.str().c_str())); #endif q.add = false; q.ribname = ribname; q.net = net; q.comment = c_format("delete_route: ribname %s net %s", ribname.c_str(), net.str().c_str()); _xrl_queue.push_back(q); start(); } template bool XrlQueue::busy() { return 0 != _flying; } template void XrlQueue::start() { // If we are currently busy don't attempt to send any more XRLs. #if 0 if (busy()) return; #else if (maximum_number_inflight()) return; #endif // Now there are no outstanding XRLs try and send as many of the queued // route commands as possible as possible. for(;;) { debug_msg("queue length %u\n", XORP_UINT_CAST(_xrl_queue.size())); if(_xrl_queue.empty()) { debug_msg("Output no longer busy\n"); #if 0 _rib_ipc_handler.output_no_longer_busy(); #endif return; } typename deque::Queued>::const_iterator qi; qi = _xrl_queue.begin(); XLOG_ASSERT(qi != _xrl_queue.end()); Queued q = *qi; const char *protocol = "ospf"; bool sent = sendit_spec(q, protocol); if (sent) { _flying++; _xrl_queue.pop_front(); if (maximum_number_inflight()) return; continue; } // We expect that the send may fail if the socket buffer is full. // It should therefore be the case that we have some route // adds/deletes in flight. If _flying is zero then something // unexpected has happended. We have no outstanding sends and // still its gone to poo. XLOG_ASSERT(0 != _flying); // We failed to send the last XRL. Don't attempt to send any more. return; } } template<> bool XrlQueue::sendit_spec(Queued& q, const char *protocol) { bool sent; bool unicast = true; bool multicast = false; XrlRibV0p1Client rib(&_xrl_router); if(q.add) { debug_msg("adding route from %s peer to rib\n", protocol); #if 0 if (_bgp.profile().enabled(profile_route_rpc_out)) _bgp.profile().log(profile_route_rpc_out, c_format("add %s", q.net.str().c_str())); #endif sent = rib. send_add_route4(q.ribname.c_str(), protocol, unicast, multicast, q.net, q.nexthop, q.metric, q.policytags.xrl_atomlist(), callback(this, &XrlQueue::route_command_done, q.comment)); if (!sent) XLOG_WARNING("scheduling add route %s failed", q.net.str().c_str()); } else { debug_msg("deleting route from %s peer to rib\n", protocol); #if 0 if (_bgp.profile().enabled(profile_route_rpc_out)) _bgp.profile().log(profile_route_rpc_out, c_format("delete %s", q.net.str().c_str())); #endif sent = rib. send_delete_route4(q.ribname.c_str(), protocol, unicast, multicast, q.net, ::callback(this, &XrlQueue::route_command_done, q.comment)); if (!sent) XLOG_WARNING("scheduling delete route %s failed", q.net.str().c_str()); } return sent; } template void XrlQueue::route_command_done(const XrlError& error, const string comment) { _flying--; debug_msg("callback %s %s\n", comment.c_str(), error.str().c_str()); switch (error.error_code()) { case OKAY: break; case REPLY_TIMED_OUT: // We should really be using a reliable transport where // this error cannot happen. But it has so lets retry if we can. XLOG_WARNING("callback: %s %s", comment.c_str(), error.str().c_str()); break; case RESOLVE_FAILED: case SEND_FAILED: case SEND_FAILED_TRANSIENT: case NO_SUCH_METHOD: XLOG_ERROR("callback: %s %s", comment.c_str(), error.str().c_str()); break; case NO_FINDER: // XXX - Temporarily code dump if this condition occurs. XLOG_FATAL("NO FINDER"); // _ospf.finder_death(__FILE__, __LINE__); break; case BAD_ARGS: case COMMAND_FAILED: case INTERNAL_ERROR: // XXX - Make this XLOG_FATAL when this has been debugged. // TODO 40. XLOG_ERROR("callback: %s %s", comment.c_str(), error.str().c_str()); break; } // Fire of more requests. start(); } template void XrlIO::tree_complete() { // // XXX: we use same actions when the tree is completed or updates are made // updates_made(); } template<> void XrlIO::updates_made() { IfMgrIfTree::IfMap::const_iterator ii; IfMgrIfAtom::VifMap::const_iterator vi; IfMgrVifAtom::IPv4Map::const_iterator ai; const IfMgrIfAtom* if_atom; const IfMgrIfAtom* other_if_atom; const IfMgrVifAtom* vif_atom; const IfMgrVifAtom* other_vif_atom; const IfMgrIPv4Atom* addr_atom; const IfMgrIPv4Atom* other_addr_atom; XLOG_WARNING("XrlIO::updates_made, _iftree:\n%s", _iftree.toString().c_str()); XLOG_WARNING("XrlIO::updates_made, ifmgr_iftree:\n%s", ifmgr_iftree().toString().c_str()); // // Check whether the old interfaces, vifs and addresses are still there // for (ii = _iftree.interfaces().begin(); ii != _iftree.interfaces().end(); ++ii) { bool is_old_interface_enabled = false; bool is_new_interface_enabled = false; bool is_old_vif_enabled = false; bool is_new_vif_enabled = false; bool is_old_address_enabled = false; bool is_new_address_enabled = false; if_atom = &ii->second; is_old_interface_enabled = if_atom->enabled(); is_old_interface_enabled &= (! if_atom->no_carrier()); // Check the interface other_if_atom = ifmgr_iftree().find_interface(if_atom->name()); if (other_if_atom == NULL) { // The interface has disappeared is_new_interface_enabled = false; } else { is_new_interface_enabled = other_if_atom->enabled(); is_new_interface_enabled &= (! other_if_atom->no_carrier()); } if ((is_old_interface_enabled != is_new_interface_enabled) && (! _interface_status_cb.is_empty())) { // The interface's enabled flag has changed _interface_status_cb->dispatch(if_atom->name(), is_new_interface_enabled); } for (vi = if_atom->vifs().begin(); vi != if_atom->vifs().end(); ++vi) { vif_atom = &vi->second; is_old_vif_enabled = vif_atom->enabled(); is_old_vif_enabled &= is_old_interface_enabled; // Check the vif other_vif_atom = ifmgr_iftree().find_vif(if_atom->name(), vif_atom->name()); if (other_vif_atom == NULL) { // The vif has disappeared is_new_vif_enabled = false; } else { is_new_vif_enabled = other_vif_atom->enabled(); } is_new_vif_enabled &= is_new_interface_enabled; if ((is_old_vif_enabled != is_new_vif_enabled) && (! _vif_status_cb.is_empty())) { // The vif's enabled flag has changed XLOG_WARNING("Vif: %s/%s changed enabled state to: %i, in XrlIO::updates_made\n", if_atom->name().c_str(), vif_atom->name().c_str(), (int)(is_new_vif_enabled)); _vif_status_cb->dispatch(if_atom->name(), vif_atom->name(), is_new_vif_enabled); } for (ai = vif_atom->ipv4addrs().begin(); ai != vif_atom->ipv4addrs().end(); ++ai) { addr_atom = &ai->second; is_old_address_enabled = addr_atom->enabled(); is_old_address_enabled &= is_old_vif_enabled; // Check the address other_addr_atom = ifmgr_iftree().find_addr(if_atom->name(), vif_atom->name(), addr_atom->addr()); if (other_addr_atom == NULL) { // The address has disappeared is_new_address_enabled = false; } else { is_new_address_enabled = other_addr_atom->enabled(); } is_new_address_enabled &= is_new_vif_enabled; if ((is_old_address_enabled != is_new_address_enabled) && (! _address_status_cb.is_empty())) { // The address's enabled flag has changed _address_status_cb->dispatch(if_atom->name(), vif_atom->name(), addr_atom->addr(), is_new_address_enabled); } } } } // // Check for new interfaces, vifs and addresses // for (ii = ifmgr_iftree().interfaces().begin(); ii != ifmgr_iftree().interfaces().end(); ++ii) { if_atom = &ii->second; // Check the interface other_if_atom = _iftree.find_interface(if_atom->name()); if (other_if_atom == NULL) { // A new interface if (if_atom->enabled() && (! if_atom->no_carrier()) && (! _interface_status_cb.is_empty())) { _interface_status_cb->dispatch(if_atom->name(), true); } } for (vi = if_atom->vifs().begin(); vi != if_atom->vifs().end(); ++vi) { vif_atom = &vi->second; // Check the vif other_vif_atom = _iftree.find_vif(if_atom->name(), vif_atom->name()); if (other_vif_atom == NULL) { // A new vif if (if_atom->enabled() && (! if_atom->no_carrier()) && (vif_atom->enabled()) && (! _vif_status_cb.is_empty())) { XLOG_WARNING("Vif: %s/%s changed enabled state to TRUE (new vif), in XrlIO::updates_made\n", if_atom->name().c_str(), vif_atom->name().c_str()); _vif_status_cb->dispatch(if_atom->name(), vif_atom->name(), true); } } for (ai = vif_atom->ipv4addrs().begin(); ai != vif_atom->ipv4addrs().end(); ++ai) { addr_atom = &ai->second; // Check the address other_addr_atom = _iftree.find_addr(if_atom->name(), vif_atom->name(), addr_atom->addr()); if (other_addr_atom == NULL) { // A new address if (if_atom->enabled() && (! if_atom->no_carrier()) && (vif_atom->enabled()) && (addr_atom->enabled()) && (! _address_status_cb.is_empty())) { _address_status_cb->dispatch(if_atom->name(), vif_atom->name(), addr_atom->addr(), true); } } } } } // // Update the local copy of the interface tree // _iftree = ifmgr_iftree(); } template class XrlQueue; template class XrlIO; /** IPv6 Stuff */ #ifdef HAVE_IPV6 template <> bool XrlIO::send(const string& interface, const string& vif, IPv6 dst, IPv6 src, int ttl, uint8_t* data, uint32_t len) { bool success; debug_msg("send(%s,%s,%s,%s,%p,%d\n", interface.c_str(), vif.c_str(), dst.str().c_str(), src.str().c_str(), data, len); // Copy the payload vector payload(len); memcpy(&payload[0], data, len); XrlAtomList ext_headers_type; XrlAtomList ext_headers_payload; XrlRawPacket6V0p1Client fea_client(&_xrl_router); success = fea_client.send_send( _feaname.c_str(), interface, vif, src, dst, get_ip_protocol_number(), dst.is_multicast() ? 1 : ttl, -1, // XXX: let the FEA set TOS get_ip_router_alert(), true, // ip_internet_control ext_headers_type, ext_headers_payload, payload, callback(this, &XrlIO::send_cb, interface, vif)); return success; } template <> bool XrlIO::enable_interface_vif(const string& interface, const string& vif) { bool success; debug_msg("Enable Interface %s Vif %s\n", interface.c_str(), vif.c_str()); XrlRawPacket6V0p1Client fea_client(&_xrl_router); success = fea_client.send_register_receiver( _feaname.c_str(), _xrl_router.instance_name(), interface, vif, get_ip_protocol_number(), false, // disable multicast loopback callback(this, &XrlIO::enable_interface_vif_cb, interface, vif)); return success; } template <> bool XrlIO::disable_interface_vif(const string& interface, const string& vif) { bool success; debug_msg("Disable Interface %s Vif %s\n", interface.c_str(), vif.c_str()); XrlRawPacket6V0p1Client fea_client(&_xrl_router); success = fea_client.send_unregister_receiver( _feaname.c_str(), _xrl_router.instance_name(), interface, vif, get_ip_protocol_number(), callback(this, &XrlIO::disable_interface_vif_cb, interface, vif)); return success; } template <> bool XrlIO::is_address_enabled(const string& interface, const string& vif, const IPv6& address) const { debug_msg("Interface %s Vif %s Address %s\n", interface.c_str(), vif.c_str(), cstring(address)); if (! is_vif_enabled(interface, vif)) return false; const IfMgrIPv6Atom* fa = ifmgr_iftree().find_addr(interface, vif, address); if (fa == NULL) return false; return (fa->enabled()); } template <> bool XrlIO::get_addresses(const string& interface, const string& vif, list& addresses) const { debug_msg("Interface %s Vif %s\n", interface.c_str(), vif.c_str()); const IfMgrVifAtom* fv = ifmgr_iftree().find_vif(interface, vif); if (fv == NULL) return false; IfMgrVifAtom::IPv6Map::const_iterator i; for (i = fv->ipv6addrs().begin(); i != fv->ipv6addrs().end(); i++) addresses.push_back(i->second.addr()); return true; } template <> bool XrlIO::get_link_local_address(const string& interface, const string& vif, IPv6& address) { debug_msg("Interface %s Vif %s\n", interface.c_str(), vif.c_str()); const IfMgrVifAtom* fv = ifmgr_iftree().find_vif(interface, vif); if (fv == NULL) return false; IfMgrVifAtom::IPv6Map::const_iterator i; for (i = fv->ipv6addrs().begin(); i != fv->ipv6addrs().end(); i++) { if (i->second.addr().is_linklocal_unicast()) { address = i->second.addr(); return true; } } return false; } template <> uint32_t XrlIO::get_prefix_length(const string& interface, const string& vif, IPv6 address) { debug_msg("Interface %s Vif %s Address %s\n", interface.c_str(), vif.c_str(), cstring(address)); const IfMgrIPv6Atom* fa = ifmgr_iftree().find_addr(interface, vif, address); if (fa == NULL) return 0; return (fa->prefix_len()); } template <> bool XrlIO::join_multicast_group(const string& interface, const string& vif, IPv6 mcast) { bool success; debug_msg("Join Interface %s Vif %s mcast %s\n", interface.c_str(), vif.c_str(), cstring(mcast)); XrlRawPacket6V0p1Client fea_client(&_xrl_router); success = fea_client.send_join_multicast_group( _feaname.c_str(), _xrl_router.instance_name(), interface, vif, get_ip_protocol_number(), mcast, callback(this, &XrlIO::join_multicast_group_cb, interface, vif)); return success; } template <> bool XrlIO::leave_multicast_group(const string& interface, const string& vif, IPv6 mcast) { bool success; debug_msg("Leave Interface %s Vif %s mcast %s\n", interface.c_str(), vif.c_str(), cstring(mcast)); XrlRawPacket6V0p1Client fea_client(&_xrl_router); success = fea_client.send_leave_multicast_group( _feaname.c_str(), _xrl_router.instance_name(), interface, vif, get_ip_protocol_number(), mcast, callback(this, &XrlIO::leave_multicast_group_cb, interface, vif)); return success; } template<> bool XrlQueue::sendit_spec(Queued& q, const char *protocol) { bool sent = false; bool unicast = true; bool multicast = false; XrlRibV0p1Client rib(&_xrl_router); if(q.add) { debug_msg("adding route from %s peer to rib\n", protocol); #if 0 if (_bgp.profile().enabled(profile_route_rpc_out)) _bgp.profile().log(profile_route_rpc_out, c_format("add %s", q.net.str().c_str())); #endif if (OspfTypes::UNUSED_INTERFACE_ID == q.nexthop_id) { sent = rib. send_add_route6(q.ribname.c_str(), protocol, unicast, multicast, q.net, q.nexthop, q.metric, q.policytags.xrl_atomlist(), callback(this, &XrlQueue::route_command_done, q.comment)); } else { string interface; string vif; XLOG_ASSERT(_io); if (!_io->get_interface_vif_by_interface_id(q.nexthop_id, interface, vif)) { XLOG_ERROR("Unable to find interface/vif associated with %u", q.nexthop_id); return false; } sent = rib. send_add_interface_route6(q.ribname.c_str(), protocol, unicast, multicast, q.net, q.nexthop, interface, vif, q.metric, q.policytags.xrl_atomlist(), callback(this, &XrlQueue::route_command_done, q.comment)); } if (!sent) XLOG_WARNING("scheduling add route %s failed", q.net.str().c_str()); } else { debug_msg("deleting route from %s peer to rib\n", protocol); #if 0 if (_bgp.profile().enabled(profile_route_rpc_out)) _bgp.profile().log(profile_route_rpc_out, c_format("delete %s", q.net.str().c_str())); #endif sent = rib. send_delete_route6(q.ribname.c_str(), protocol, unicast, multicast, q.net, callback(this, &XrlQueue::route_command_done, q.comment)); if (!sent) XLOG_WARNING("scheduling delete route %s failed", q.net.str().c_str()); } return sent; } template<> void XrlIO::updates_made() { IfMgrIfTree::IfMap::const_iterator ii; IfMgrIfAtom::VifMap::const_iterator vi; IfMgrVifAtom::IPv6Map::const_iterator ai; const IfMgrIfAtom* if_atom; const IfMgrIfAtom* other_if_atom; const IfMgrVifAtom* vif_atom; const IfMgrVifAtom* other_vif_atom; const IfMgrIPv6Atom* addr_atom; const IfMgrIPv6Atom* other_addr_atom; // // Check whether the old interfaces, vifs and addresses are still there // for (ii = _iftree.interfaces().begin(); ii != _iftree.interfaces().end(); ++ii) { bool is_old_interface_enabled = false; bool is_new_interface_enabled = false; bool is_old_vif_enabled = false; bool is_new_vif_enabled = false; bool is_old_address_enabled = false; bool is_new_address_enabled = false; if_atom = &ii->second; is_old_interface_enabled = if_atom->enabled(); is_old_interface_enabled &= (! if_atom->no_carrier()); // Check the interface other_if_atom = ifmgr_iftree().find_interface(if_atom->name()); if (other_if_atom == NULL) { // The interface has disappeared is_new_interface_enabled = false; } else { is_new_interface_enabled = other_if_atom->enabled(); is_new_interface_enabled &= (! other_if_atom->no_carrier()); } if ((is_old_interface_enabled != is_new_interface_enabled) && (! _interface_status_cb.is_empty())) { // The interface's enabled flag has changed _interface_status_cb->dispatch(if_atom->name(), is_new_interface_enabled); } for (vi = if_atom->vifs().begin(); vi != if_atom->vifs().end(); ++vi) { vif_atom = &vi->second; is_old_vif_enabled = vif_atom->enabled(); is_old_vif_enabled &= is_old_interface_enabled; // Check the vif other_vif_atom = ifmgr_iftree().find_vif(if_atom->name(), vif_atom->name()); if (other_vif_atom == NULL) { // The vif has disappeared is_new_vif_enabled = false; } else { is_new_vif_enabled = other_vif_atom->enabled(); } is_new_vif_enabled &= is_new_interface_enabled; if ((is_old_vif_enabled != is_new_vif_enabled) && (! _vif_status_cb.is_empty())) { // The vif's enabled flag has changed XLOG_WARNING("Vif: %s/%s changed enabled state to: %i, in XrlIO::updates_made\n", if_atom->name().c_str(), vif_atom->name().c_str(), (int)(is_new_vif_enabled)); _vif_status_cb->dispatch(if_atom->name(), vif_atom->name(), is_new_vif_enabled); } for (ai = vif_atom->ipv6addrs().begin(); ai != vif_atom->ipv6addrs().end(); ++ai) { addr_atom = &ai->second; is_old_address_enabled = addr_atom->enabled(); is_old_address_enabled &= is_old_vif_enabled; // Check the address other_addr_atom = ifmgr_iftree().find_addr(if_atom->name(), vif_atom->name(), addr_atom->addr()); if (other_addr_atom == NULL) { // The address has disappeared is_new_address_enabled = false; } else { is_new_address_enabled = other_addr_atom->enabled(); } is_new_address_enabled &= is_new_vif_enabled; if ((is_old_address_enabled != is_new_address_enabled) && (! _address_status_cb.is_empty())) { // The address's enabled flag has changed _address_status_cb->dispatch(if_atom->name(), vif_atom->name(), addr_atom->addr(), is_new_address_enabled); } } } } // // Check for new interfaces, vifs and addresses // for (ii = ifmgr_iftree().interfaces().begin(); ii != ifmgr_iftree().interfaces().end(); ++ii) { if_atom = &ii->second; // Check the interface other_if_atom = _iftree.find_interface(if_atom->name()); if (other_if_atom == NULL) { // A new interface if (if_atom->enabled() && (! if_atom->no_carrier()) && (! _interface_status_cb.is_empty())) { _interface_status_cb->dispatch(if_atom->name(), true); } } for (vi = if_atom->vifs().begin(); vi != if_atom->vifs().end(); ++vi) { vif_atom = &vi->second; // Check the vif other_vif_atom = _iftree.find_vif(if_atom->name(), vif_atom->name()); if (other_vif_atom == NULL) { // A new vif if (if_atom->enabled() && (! if_atom->no_carrier()) && (vif_atom->enabled()) && (! _vif_status_cb.is_empty())) { XLOG_WARNING("Vif: %s/%s changed enabled state to TRUE (new vif), in XrlIO::updates_made\n", if_atom->name().c_str(), vif_atom->name().c_str()); _vif_status_cb->dispatch(if_atom->name(), vif_atom->name(), true); } } for (ai = vif_atom->ipv6addrs().begin(); ai != vif_atom->ipv6addrs().end(); ++ai) { addr_atom = &ai->second; // Check the address other_addr_atom = _iftree.find_addr(if_atom->name(), vif_atom->name(), addr_atom->addr()); if (other_addr_atom == NULL) { // A new address if (if_atom->enabled() && (! if_atom->no_carrier()) && (vif_atom->enabled()) && (addr_atom->enabled()) && (! _address_status_cb.is_empty())) { _address_status_cb->dispatch(if_atom->name(), vif_atom->name(), addr_atom->addr(), true); } } } } } // // Update the local copy of the interface tree // _iftree = ifmgr_iftree(); } template class XrlQueue; template class XrlIO; #endif // ipv6 xorp/ospf/tools/0000775000076400007640000000000011631507213013766 5ustar greearbgreearbxorp/ospf/tools/print_lsas.cc0000664000076400007640000003220711540225531016456 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // Get LSAs (in raw binary) from OSPF and print them. // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #include "ospf/ospf_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/ipv4.hh" #include "libxorp/ipv6.hh" #include "libxorp/service.hh" #include "libxorp/status_codes.h" #include "libxorp/eventloop.hh" #include "libxorp/tlv.hh" #ifdef HAVE_GETOPT_H #include #endif #ifdef HAVE_SYS_UTSNAME_H #include #endif #include "libxipc/xrl_std_router.hh" #include "xrl/interfaces/ospfv2_xif.hh" #ifdef HAVE_IPV6 #include "xrl/interfaces/ospfv3_xif.hh" #endif #include "ospf/ospf.hh" #include "ospf/test_common.hh" /** * return OS name. */ string host_os() { #ifdef HAVE_UNAME struct utsname name; if (0 != uname(&name)) { return HOST_OS_NAME; } return name.sysname; #endif return HOST_OS_NAME; } /** * Get the list of configured areas */ class GetAreaList { public: GetAreaList(XrlStdRouter& xrl_router, OspfTypes::Version version) : _xrl_router(xrl_router), _version(version), _done(false), _fail(false) { } void add(IPv4 area) { _areas.push_back(area); } void start() { switch(_version) { case OspfTypes::V2: { XrlOspfv2V0p1Client ospfv2(&_xrl_router); ospfv2.send_get_area_list(xrl_target(_version), callback(this, &GetAreaList::response)); } break; case OspfTypes::V3: { #ifdef HAVE_IPV6 XrlOspfv3V0p1Client ospfv3(&_xrl_router); ospfv3.send_get_area_list(xrl_target(_version), callback(this, &GetAreaList::response)); #endif } break; } } bool busy() { return !_done; } bool fail() { return _fail; } list& get() { return _areas; } private: void response(const XrlError& error, const XrlAtomList *atomlist) { _done = true; if (XrlError::OKAY() != error) { XLOG_WARNING("Attempt to get lsa failed"); _fail = true; return; } const size_t size = atomlist->size(); for (size_t i = 0; i < size; i++) _areas.push_back(IPv4(htonl(atomlist->get(i).uint32()))); } private: XrlStdRouter &_xrl_router; OspfTypes::Version _version; bool _done; bool _fail; list _areas; }; /** * Fetch all the LSAs for one area. */ class FetchDB { public: FetchDB(XrlStdRouter& xrl_router, OspfTypes::Version version, IPv4 area) : _xrl_router(xrl_router), _version(version), _lsa_decoder(version), _done(false), _fail(false), _area(area), _index(0) { initialise_lsa_decoder(version, _lsa_decoder); } void start() { switch(_version) { case OspfTypes::V2: { XrlOspfv2V0p1Client ospfv2(&_xrl_router); ospfv2.send_get_lsa(xrl_target(_version), _area, _index, callback(this, &FetchDB::response)); } break; case OspfTypes::V3: { #ifdef HAVE_IPV6 XrlOspfv3V0p1Client ospfv3(&_xrl_router); ospfv3.send_get_lsa(xrl_target(_version), _area, _index, callback(this, &FetchDB::response)); #endif } break; } } bool busy() { return !_done; } bool fail() { return _fail; } list& get_lsas() { return _lsas; } private: void response(const XrlError& error, const bool *valid, const bool *toohigh, const bool *self, const vector* lsa) { if (XrlError::OKAY() != error) { XLOG_WARNING("Attempt to get lsa failed"); _done = true; _fail = true; return; } if (*valid) { size_t len = lsa->size(); Lsa::LsaRef lsar = _lsa_decoder. decode(const_cast(&(*lsa)[0]), len); lsar->set_self_originating(*self); _lsas.push_back(lsar); } if (*toohigh) { _done = true; return; } _index++; start(); } private: XrlStdRouter &_xrl_router; OspfTypes::Version _version; LsaDecoder _lsa_decoder; bool _done; bool _fail; const IPv4 _area; uint32_t _index; list _lsas; }; /** * Filter LSAs. If the filter is empty everything is accepted, if the * filter contains types then only an LSA matching a type is passed. */ class FilterLSA { public: void add(uint16_t type) { _filter.push_back(type); } bool accept(uint16_t type) { if (_filter.empty()) return true; if (_filter.end() != find(_filter.begin(), _filter.end(), type)) return true; return false; } private: list _filter; }; /** * The base class that needs to be implemented by all print routines. */ class Output { public: Output(OspfTypes::Version version, FilterLSA& filter) : _version(version), _filter(filter) {} virtual ~Output() {} virtual bool begin() { return true; } void print_area(string area) { printf(" OSPF link state database, Area %s\n", area.c_str()); } virtual bool begin_area(string area) { _area = area; return true; } virtual bool print(Lsa::LsaRef lsar) { printf("%s\n", lsar->str().c_str()); return true; } virtual bool end_area(string /*area*/) { _area = ""; return true; } virtual bool end() { return true; } protected: OspfTypes::Version _version; FilterLSA& _filter; string _area; }; class Brief : public Output { public: Brief(OspfTypes::Version version, FilterLSA& filter) : Output(version, filter) {} bool begin_area(string area) { print_area(area); switch(_version) { case OspfTypes::V2: printf(" Type ID Adv " "Rtr Seq Age Opt Cksum Len\n"); break; case OspfTypes::V3: printf(" Type ID Adv " "Rtr Seq Age Cksum Len\n"); break; } return true; } bool print(Lsa::LsaRef lsar) { switch(_version) { case OspfTypes::V2: printf("%-8s", lsar->name()); break; case OspfTypes::V3: printf("%-11s", lsar->name()); break; } printf("%c", lsar->get_self_originating() ? '*' : ' '); Lsa_header& header = lsar->get_header(); printf("%-17s", pr_id(header.get_link_state_id()).c_str()); printf("%-17s", pr_id(header.get_advertising_router()).c_str()); printf("%-#12x", header.get_ls_sequence_number()); printf("%4d", header.get_ls_age()); switch(_version) { case OspfTypes::V2: printf(" %-#5x", header.get_options()); break; case OspfTypes::V3: printf(" "); break; } printf("%-#7x", header.get_ls_checksum()); printf("%3d", header.get_length()); printf("\n"); return true; } }; class Detail : public Output { public: Detail(OspfTypes::Version version, FilterLSA& filter) : Output(version, filter) {} bool begin_area(string area) { print_area(area); return true; } }; class Summary : public Output { public: Summary(OspfTypes::Version version, FilterLSA& filter) : Output(version, filter), _externals(0) {} bool begin_area(string area) { printf("Area %s\n", area.c_str()); _summary.clear(); return true; } bool print(Lsa::LsaRef lsar) { uint16_t type = lsar->get_ls_type(); if (0 == _summary.count(type)) { _summary[type] = 1; } else { _summary[type] = _summary[type] + 1; } return true; } bool end_area(string /*area*/) { LsaDecoder lsa_decoder(_version); initialise_lsa_decoder(_version, lsa_decoder); map::const_iterator i; for (i = _summary.begin(); i != _summary.end(); i++) { uint16_t type = (*i).first; uint32_t count = (*i).second; if (lsa_decoder.external(type)) { if (0 == _externals) _externals = count; } else { printf("%5d %s LSAs\n", count, lsa_decoder.name(type)); } } return true; } bool end() { if (_filter.accept(ASExternalLsa(_version).get_ls_type())) { printf("Externals:\n"); if (0 != _externals) printf("%5d External LSAs\n", _externals); } return true; } private: uint32_t _externals; // Number of AS-External-LSAs. map _summary; }; class Save : public Output { public: Save(OspfTypes::Version version, FilterLSA& filter, string& fname) : Output(version, filter), _fname(fname) {} bool begin() { if (!_tlv.open(_fname, false /* write */)) { XLOG_ERROR("Unable to open %s", _fname.c_str()); return false; } // 1) Version vector data; data.resize(sizeof(uint32_t)); _tlv.put32(data, 0, TLV_VERSION); if (!_tlv.write(TLV_CURRENT_VERSION, data)) return false; // 2) System info string host = host_os(); uint32_t len = host.size(); data.resize(len); memcpy(&data[0], host.c_str(), len); if (!_tlv.write(TLV_SYSTEM_INFO, data)) return false; // 3) OSPF Version data.resize(sizeof(uint32_t)); _tlv.put32(data, 0, _version); if (!_tlv.write(TLV_OSPF_VERSION, data)) return false; return true; } bool begin_area(string area) { vector data; data.resize(sizeof(uint32_t)); _tlv.put32(data, 0, ntohl(IPv4(area.c_str()).addr())); return _tlv.write(TLV_AREA, data); } bool print(Lsa::LsaRef lsar) { size_t len; uint8_t *ptr = lsar->lsa(len); vector data; data.resize(len); memcpy(&data[0], ptr, len); return _tlv.write(TLV_LSA, data); } bool end() { return _tlv.close(); } private: string _fname; Tlv _tlv; }; enum Pstyle { BRIEF, DETAIL, SUMMARY, SAVE }; int usage(const char *myname) { fprintf(stderr, "usage: %s [-a area] [-f type] [-s] [-b] [-d] [-S fname]\n", myname); return -1; } int main(int argc, char **argv) { XorpUnexpectedHandler x(xorp_unexpected_handler); // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); OspfTypes::Version version = OspfTypes::V2; Pstyle pstyle = BRIEF; FilterLSA filter; string area; string fname; int c; while ((c = getopt(argc, argv, "23a:f:sbdS:")) != -1) { switch (c) { case '2': version = OspfTypes::V2; break; case '3': version = OspfTypes::V3; break; case 'a': area = optarg; break; case 'f': { char *endptr; uint32_t number = strtoul(optarg, &endptr, 0); if (0 != *endptr) { XLOG_ERROR("<%s> is not a number", optarg); return -1; } filter.add(number); } break; case 's': pstyle = SUMMARY; break; case 'b': pstyle = BRIEF; break; case 'd': pstyle = DETAIL; break; case 'S': pstyle = SAVE; fname = optarg; break; default: return usage(argv[0]); } } Output *output = 0; switch (pstyle) { case BRIEF: output = new Brief(version, filter); break; case DETAIL: output = new Detail(version, filter); break; case SUMMARY: output = new Summary(version, filter); break; case SAVE: output = new Save(version, filter, fname); break; } try { EventLoop eventloop; XrlStdRouter xrl_router(eventloop, "print_lsas"); debug_msg("Waiting for router"); xrl_router.finalize(); wait_until_xrl_router_is_ready(eventloop, xrl_router); debug_msg("\n"); GetAreaList get_area_list(xrl_router, version); if (area.empty()) { get_area_list.start(); while(get_area_list.busy()) eventloop.run(); if (get_area_list.fail()) { XLOG_ERROR("Failed to get area list"); return -1; } } else { get_area_list.add(IPv4(area.c_str())); } if (!output->begin()) return -1; list& area_list = get_area_list.get(); list::const_iterator i; for (i = area_list.begin(); i != area_list.end(); i++) { FetchDB fetchdb(xrl_router, version, *i); fetchdb.start(); while(fetchdb.busy()) eventloop.run(); if (fetchdb.fail()) { XLOG_ERROR("Failed to fetch area %s", i->str().c_str()); return -1; } if (!output->begin_area(i->str())) return -1; list& lsas = fetchdb.get_lsas(); list::const_iterator j; for (j = lsas.begin(); j != lsas.end(); j++) { uint16_t type = (*j)->get_ls_type(); if (!filter.accept(type)) continue; if (!output->print(*j)) return -1; } if (!output->end_area(i->str())) return -1; } if (!output->end()) return -1; delete output; } catch (...) { xorp_catch_standard_exceptions(); } xlog_stop(); xlog_exit(); return 0; } xorp/ospf/tools/print_neighbours.cc0000664000076400007640000002355511540225531017667 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // Print information about OSPF neighbours // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #include "ospf/ospf_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/ipv4.hh" #include "libxorp/ipv6.hh" #include "libxorp/service.hh" #include "libxorp/status_codes.h" #include "libxorp/eventloop.hh" #include "libxorp/tlv.hh" #ifdef HAVE_GETOPT_H #include #endif #include "libxipc/xrl_std_router.hh" #include "xrl/interfaces/ospfv2_xif.hh" #ifdef HAVE_IPV6 #include "xrl/interfaces/ospfv3_xif.hh" #endif #include "ospf/ospf.hh" #include "ospf/test_common.hh" /** * Get the list of neighbours */ class GetNeighbourList { public: GetNeighbourList(XrlStdRouter& xrl_router, OspfTypes::Version version) : _xrl_router(xrl_router), _version(version), _done(false), _fail(false) { } void start() { switch(_version) { case OspfTypes::V2: { XrlOspfv2V0p1Client ospfv2(&_xrl_router); ospfv2. send_get_neighbour_list(xrl_target(_version), callback(this, &GetNeighbourList::response)); } break; case OspfTypes::V3: { #ifdef HAVE_IPV6 XrlOspfv3V0p1Client ospfv3(&_xrl_router); ospfv3. send_get_neighbour_list(xrl_target(_version), callback(this, &GetNeighbourList::response)); #endif } break; } } bool busy() { return !_done; } bool fail() { return _fail; } list& get() { return _nlist; } private: void response(const XrlError& error, const XrlAtomList *atomlist) { _done = true; if (XrlError::OKAY() != error) { XLOG_WARNING("Attempt to get neighbour list failed"); _fail = true; return; } const size_t size = atomlist->size(); for (size_t i = 0; i < size; i++) _nlist.push_back(atomlist->get(i).uint32()); } private: XrlStdRouter &_xrl_router; OspfTypes::Version _version; bool _done; bool _fail; list _nlist; }; /** * Get the Neighbour info for all the requested neighbours. */ class GetNeighbours { public: GetNeighbours(XrlStdRouter& xrl_router, OspfTypes::Version version, list& nlist) : _xrl_router(xrl_router), _version(version), _done(false), _fail(false), _nlist(nlist),_index(_nlist.begin()) {} void start() { if (_nlist.end() == _index) { _done = true; return; } switch(_version) { case OspfTypes::V2: { XrlOspfv2V0p1Client ospfv2(&_xrl_router); ospfv2.send_get_neighbour_info(xrl_target(_version), *_index, callback(this, &GetNeighbours::response)); } break; case OspfTypes::V3: { #ifdef HAVE_IPV6 XrlOspfv3V0p1Client ospfv3(&_xrl_router); ospfv3.send_get_neighbour_info(xrl_target(_version), *_index, callback(this, &GetNeighbours::response)); #endif } break; } } bool busy() { return !_done; } bool fail() { return _fail; } list& get_ninfo() { return _ninfo; } private: void response(const XrlError& error, const string* address, const string* interface, const string* state, const IPv4* rid, const uint32_t* priority, const uint32_t* deadtime, const IPv4* area, const uint32_t* opt, const IPv4* dr, const IPv4* bdr, const uint32_t* up, const uint32_t* adjacent) { if (XrlError::OKAY() != error) { XLOG_WARNING("Attempt to get neighbour info failed"); _done = true; _fail = true; return; } NeighbourInfo ninfo; #define copy_ninfo(var) ninfo._ ## var = *var copy_ninfo(address); copy_ninfo(interface); copy_ninfo(state); copy_ninfo(rid); copy_ninfo(priority); copy_ninfo(deadtime); copy_ninfo(area); copy_ninfo(opt); copy_ninfo(dr); copy_ninfo(bdr); copy_ninfo(up); copy_ninfo(adjacent); #undef copy_ninfo _ninfo.push_back(ninfo); _index++; start(); } private: XrlStdRouter &_xrl_router; OspfTypes::Version _version; bool _done; bool _fail; list _nlist; list::iterator _index; list _ninfo; }; /** * Filter the neighbours. If the filter is empty then accept * everything, if the filter has any entries match against the * neighbour address or the router ID. */ class FilterNeighbour { public: void add(IPv4 id) { _filter.push_back(id); } bool accept(const NeighbourInfo& ninfo) const { if (_filter.empty()) return true; list::const_iterator i; for (i = _filter.begin(); i != _filter.end(); i++) { if (ninfo._address == (*i).str() || ninfo._rid.str() == (*i).str()) return true; } return false; } private: list _filter; }; /** * The base class that needs to be implemented by all print routines. */ class Output { public: Output(OspfTypes::Version version) : _version(version) {} virtual ~Output() {} virtual bool begin() { printf(" Address Interface State" " ID Pri Dead\n"); return true; } void print_first_line(const NeighbourInfo& ninfo) { string address = ""; switch(_version) { case OspfTypes::V2: address = ninfo._address; break; case OspfTypes::V3: break; } printf("%-17s", address.c_str()); printf("%-23s", ninfo._interface.c_str()); printf("%-10s", ninfo._state.c_str()); printf("%-16s", ninfo._rid.str().c_str()); printf("%4d", ninfo._priority); printf("%6d", ninfo._deadtime); printf("\n"); switch(_version) { case OspfTypes::V2: break; case OspfTypes::V3: printf("%-17s", ninfo._address.c_str()); printf("\n"); break; } } virtual bool print(const NeighbourInfo& ninfo) { print_first_line(ninfo); return true; } protected: OspfTypes::Version _version; }; class Brief : public Output { public: Brief(OspfTypes::Version version) : Output(version) {} }; class Detail : public Output { public: Detail(OspfTypes::Version version) : Output(version) {} string uptime(uint32_t t) { const uint32_t mins_in_hour = 3600; uint32_t hours = t / mins_in_hour; uint32_t hour_remainder = t % mins_in_hour; uint32_t mins = hour_remainder / 60; uint32_t secs = hour_remainder % 60; return c_format("%02d:%02d:%02d", hours, mins, secs); } bool print(const NeighbourInfo& ninfo) { print_first_line(ninfo); printf(" Area %s, opt %#x, DR %s, BDR %s\n",ninfo._area.str().c_str(), ninfo._opt, ninfo._dr.str().c_str(), ninfo._bdr.str().c_str()); printf(" Up %s", uptime(ninfo._up).c_str()); if (0 != ninfo._adjacent) printf(", adjacent %s", uptime(ninfo._adjacent).c_str()); printf("\n"); return true; } }; enum Pstyle { BRIEF, DETAIL, }; int usage(const char *myname) { fprintf(stderr, "usage: %s [-f x.x.x.x] [-b] [-d]\n", myname); return -1; } int main(int argc, char **argv) { XorpUnexpectedHandler x(xorp_unexpected_handler); // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); OspfTypes::Version version = OspfTypes::V2; Pstyle pstyle = BRIEF; FilterNeighbour filter; int c; while ((c = getopt(argc, argv, "23f:bd")) != -1) { switch (c) { case '2': version = OspfTypes::V2; break; case '3': version = OspfTypes::V3; break; case 'f': try { filter.add(optarg); } catch(...) {} break; case 'b': pstyle = BRIEF; break; case 'd': pstyle = DETAIL; break; default: return usage(argv[0]); } } Output *output = 0; switch (pstyle) { case BRIEF: output = new Brief(version); break; case DETAIL: output = new Detail(version); break; } try { EventLoop eventloop; XrlStdRouter xrl_router(eventloop, "print_neighbours"); debug_msg("Waiting for router"); xrl_router.finalize(); wait_until_xrl_router_is_ready(eventloop, xrl_router); debug_msg("\n"); GetNeighbourList get_neighbour_list(xrl_router, version); get_neighbour_list.start(); while(get_neighbour_list.busy()) eventloop.run(); if (get_neighbour_list.fail()) { XLOG_ERROR("Failed to get neighbour list"); return -1; } list& nlist = get_neighbour_list.get(); GetNeighbours get_neighbours(xrl_router, version, nlist); get_neighbours.start(); while(get_neighbours.busy()) eventloop.run(); if (get_neighbours.fail()) { XLOG_ERROR("Failed to get neighbour info"); return -1; } if (!output->begin()) return -1; list& ninfo = get_neighbours.get_ninfo(); list::const_iterator i; for (i = ninfo.begin(); i != ninfo.end(); i++) { if (!filter.accept(*i)) continue; if (!output->print(*i)) return -1; } delete output; } catch (...) { xorp_catch_standard_exceptions(); } xlog_stop(); xlog_exit(); return 0; } xorp/ospf/tools/SConscript0000664000076400007640000000524611631507213016007 0ustar greearbgreearb# Copyright (c) 2009-2011 XORP, Inc and Others # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # $XORP$ import os Import('env') env = env.Clone() is_shared = env.has_key('SHAREDLIBS') env.AppendUnique(CPPPATH = [ '#', '$BUILDDIR', ]) env.AppendUnique(LIBPATH = [ '$BUILDDIR/libxorp', '$BUILDDIR/libcomm', '$BUILDDIR/libxipc', '$BUILDDIR/libproto', '$BUILDDIR/xrl/interfaces', '$BUILDDIR/xrl/targets', '.', ]) env.AppendUnique(LIBS = [ 'xif_ospfv2', 'xorp_ipc', 'xorp_core', 'xorp_proto', 'xorp_comm', ]) if (env.has_key('mingw') and env['mingw']): env.Append(LIBS = [ 'iphlpapi', # 'mprapi', 'regex', 'winmm', ]) env.Append(LIBS = ['xorp_core', 'crypto', 'ws2_32']) if not (env.has_key('disable_ipv6') and env['disable_ipv6']): env.AppendUnique(LIBS = [ 'xif_ospfv3', ]) env.Replace(RPATH = [ env.Literal(env['xorp_tool_rpath']) ]) cleardbsrcs = [ 'clear_database.cc' ] printlsassrcs = [ 'print_lsas.cc', ] # deal with shared code if is_shared: printlsassrcs += [ '../lsa.cc', '../fletcher_checksum.cc' ] else: obj_fletcher = env.StaticObject(target='tools_fletcher', source='../fletcher_checksum.cc') obj_lsa = env.StaticObject(target='tools_lsa', source='../lsa.cc') printlsassrcs += [ obj_fletcher, obj_lsa ] printneighborssrcs = [ 'print_neighbours.cc' ] cleardb = env.Program(target = 'ospf_clear_database', source = cleardbsrcs) printlsas = env.Program(target = 'ospf_print_lsas', source = printlsassrcs) printneighbors = env.Program(target = 'ospf_print_neighbours', source = printneighborssrcs) env.Alias('install', env.InstallProgram(env['xorp_tooldir'], cleardb)) env.Alias('install', env.InstallProgram(env['xorp_tooldir'], printlsas)) env.Alias('install', env.InstallProgram(env['xorp_tooldir'], printneighbors)) Default(cleardb, printlsas, printneighbors) xorp/ospf/tools/clear_database.cc0000664000076400007640000000754211540225531017216 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2007-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // Print information about OSPF neighbours // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #include "ospf/ospf_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/ipv4.hh" #include "libxorp/ipv6.hh" #include "libxorp/service.hh" #include "libxorp/status_codes.h" #include "libxorp/eventloop.hh" #include "libxorp/tlv.hh" #ifdef HAVE_GETOPT_H #include #endif #include "libxipc/xrl_std_router.hh" #include "xrl/interfaces/ospfv2_xif.hh" #ifdef HAVE_IPV6 #include "xrl/interfaces/ospfv3_xif.hh" #endif #include "ospf/ospf.hh" #include "ospf/test_common.hh" class ClearDatabase { public: ClearDatabase(XrlStdRouter& xrl_router, OspfTypes::Version version) : _xrl_router(xrl_router), _version(version), _done(false), _fail(false) { } void start() { switch(_version) { case OspfTypes::V2: { XrlOspfv2V0p1Client ospfv2(&_xrl_router); ospfv2.send_clear_database(xrl_target(_version), callback(this, &ClearDatabase::response)); } break; case OspfTypes::V3: { #ifdef HAVE_IPV6 XrlOspfv3V0p1Client ospfv3(&_xrl_router); ospfv3.send_clear_database(xrl_target(_version), callback(this, &ClearDatabase::response)); #endif } break; } } bool busy() { return !_done; } bool fail() { return _fail; } private: void response(const XrlError& error) { _done = true; if (XrlError::OKAY() != error) { XLOG_WARNING("Attempt to clear database"); _fail = true; return; } } private: XrlStdRouter &_xrl_router; OspfTypes::Version _version; bool _done; bool _fail; }; int usage(const char *myname) { fprintf(stderr, "usage: %s [-2|-3]\n", myname); return -1; } int main(int argc, char **argv) { XorpUnexpectedHandler x(xorp_unexpected_handler); // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); OspfTypes::Version version = OspfTypes::V2; int c; while ((c = getopt(argc, argv, "23")) != -1) { switch (c) { case '2': version = OspfTypes::V2; break; case '3': version = OspfTypes::V3; break; default: return usage(argv[0]); } } try { EventLoop eventloop; XrlStdRouter xrl_router(eventloop, "clear_database"); debug_msg("Waiting for router"); xrl_router.finalize(); wait_until_xrl_router_is_ready(eventloop, xrl_router); debug_msg("\n"); ClearDatabase clear_database(xrl_router, version); clear_database.start(); while(clear_database.busy()) eventloop.run(); if (clear_database.fail()) { XLOG_ERROR("Failed to clear database"); return -1; } } catch (...) { xorp_catch_standard_exceptions(); } xlog_stop(); xlog_exit(); return 0; } xorp/ospf/peer.cc0000664000076400007640000042002111703345405014072 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2012 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #include "ospf_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/callback.hh" #include "libxorp/ipv4.hh" #include "libxorp/ipv6.hh" #include "libxorp/ipnet.hh" #include "libxorp/status_codes.h" #include "libxorp/service.hh" #include "libxorp/eventloop.hh" #ifndef XORP_USE_USTL #include #endif // XXX // The system header files are here to get the sizeof an ipv4 and ipv6 // header. Really should have a define somewhere in XORP for this. #ifdef HAVE_NETINET_IN_SYSTM_H #include #endif #ifdef HAVE_NETINET_IP_H #include #endif #ifdef HAVE_NETINET_IP6_H #include #endif #include "libproto/spt.hh" #include "ospf.hh" #include "delay_queue.hh" #include "vertex.hh" #include "area_router.hh" #include "auth.hh" #include "peer.hh" /** * Oh which linktype should multicast be enabled. */ inline bool do_multicast(OspfTypes::LinkType linktype) { switch(linktype) { case OspfTypes::PointToPoint: case OspfTypes::BROADCAST: return true; break; case OspfTypes::NBMA: case OspfTypes::PointToMultiPoint: case OspfTypes::VirtualLink: return false; break; } XLOG_UNREACHABLE(); return true; } template PeerOut:: PeerOut(Ospf& ospf, const string interface, const string vif, const OspfTypes::PeerID peerid, const A interface_address, OspfTypes::LinkType linktype, OspfTypes::AreaID area, OspfTypes::AreaType area_type) : _ospf(ospf), _interface(interface), _vif(vif), _peerid(peerid), _interface_id(0), _interface_address(interface_address), _interface_prefix_length(0), _interface_mtu(0), _interface_cost(1), // Must be greater than 0. _inftransdelay(1), // Must be greater than 0. _linktype(linktype), _running(false), _link_status(false), _status(false), _receiving(false) { _areas[area] = new Peer(ospf, *this, area, area_type); } template PeerOut::~PeerOut() { set_state(false); typename map *>::iterator i; for(i = _areas.begin(); i != _areas.end(); i++) delete (*i).second; } template <> bool PeerOut::match(IPv4 source, string& interface, string& vif) { if (get_interface_address() == source) { interface = _interface; vif = _vif; return true; } return false; } template <> bool PeerOut::match(IPv6 source, string& interface, string& vif) { map *>::const_iterator i; for(i = _areas.begin(); i != _areas.end(); i++) { if ((*i).second->match(source)) { interface = _interface; vif = _vif; return true; } } return false; } template void PeerOut::set_mask(Peer *peer) { if (typeid(A) != typeid(IPv4)) return; peer-> set_network_mask(ntohl(IPv4::make_prefix(get_interface_prefix_length()) .addr())); } template uint16_t PeerOut::get_interface_prefix_length() const { if (!(0 != _interface_prefix_length || VLINK == _interface)) { XLOG_WARNING("ERROR: PeerOut: %s/%s has bad prefix: %i address: %s\n", _interface.c_str(), _vif.c_str(), _interface_prefix_length, _interface_address.str().c_str()); } return _interface_prefix_length; } template uint16_t PeerOut::get_frame_size() const { uint16_t router_alert = 4 * sizeof(uint8_t);// Router Alert uint16_t frame = get_interface_mtu() - router_alert; switch(_ospf.get_version()) { case OspfTypes::V2: frame -= sizeof(struct ip); x_static_assert(20 == sizeof(struct ip)); break; case OspfTypes::V3: #ifdef HAVE_NETINET_IP6_H frame -= sizeof(struct ip6_hdr); x_static_assert(40 == sizeof(struct ip6_hdr)); #else frame -= 40; #endif break; } return frame; } template bool PeerOut::get_areas(list &areas) const { typename map *>::const_iterator i; for(i = _areas.begin(); i != _areas.end(); i++) { areas.push_back((*i).first); } return true; } template bool PeerOut::add_area(OspfTypes::AreaID area, OspfTypes::AreaType area_type) { debug_msg("Area %s\n", pr_id(area).c_str()); // Only OSPFv3 allows a peer to be connected to multiple areas. XLOG_ASSERT(OspfTypes::V3 == _ospf.get_version()); Peer *peer = _areas[area] = new Peer(_ospf, *this, area, area_type); set_mask(peer); if (_running) peer->start(); return true; } template bool PeerOut::change_area_router_type(OspfTypes::AreaID area, OspfTypes::AreaType area_type) { debug_msg("Area %s Type %s\n", pr_id(area).c_str(), pp_area_type(area_type).c_str()); // All the peers are notified when an area type is changed if (0 == _areas.count(area)) { return false; } _areas[area]->change_area_router_type(area_type); return true; } template bool PeerOut::remove_area(OspfTypes::AreaID area) { debug_msg("Area %s\n", pr_id(area).c_str()); // All the peers are notified when an area is deleted. if (0 == _areas.count(area)) { return false; } delete _areas[area]; _areas.erase(_areas.find(area)); // If this peer is no longer serving any areas it can be deleted. if (_areas.empty()) return true; else return false; } template bool PeerOut::add_neighbour(OspfTypes::AreaID area, A neighbour_address, OspfTypes::RouterID rid) { if (0 == _areas.count(area)) { XLOG_ERROR("Unknown Area %s", pr_id(area).c_str()); return false; } return _areas[area]->add_neighbour(neighbour_address, rid); } template bool PeerOut::remove_neighbour(OspfTypes::AreaID area, A neighbour_address, OspfTypes::RouterID rid) { if (0 == _areas.count(area)) { XLOG_ERROR("Unknown Area %s", pr_id(area).c_str()); return false; } return _areas[area]->remove_neighbour(neighbour_address, rid); } template void PeerOut::set_state(bool state) { debug_msg("state %s\n", state ? "up" : "down"); _status = state; peer_change(); } template void PeerOut::set_link_status(bool state, const char* dbg) { XLOG_WARNING("Setting PeerOut link status to: %i dbg: %s vif: %s old-status: %i\n", (int)(state), dbg, get_if_name().c_str(), _link_status); _link_status = state; peer_change(); } template void PeerOut::peer_change() { XLOG_WARNING("PeerOut, peer_change on interface: %s running: %i status: %i link-status: %i", get_if_name().c_str(), (int)(_running), (int)(_status), (int)(_link_status)); if (_running) { if (!(_status && _link_status)) { take_down_peering(); _running = false; } } else { if (_status && _link_status) { _running = true; _running = bring_up_peering(); } } } template bool PeerOut::transmit(typename Transmit::TransmitRef tr) { if (!_running) { XLOG_WARNING("Attempt to transmit while peer is not running"); return false; } do { if (!tr->valid()) return true; size_t len; uint8_t *ptr = tr->generate(len); _ospf.get_peer_manager().transmit(_interface, _vif, tr->destination(), tr->source(), ptr, len); } while(tr->multiple()); return true; } template bool PeerOut::receive(A dst, A src, Packet *packet) throw(BadPeer) { XLOG_TRACE(_ospf.trace()._packets, "peer-out-rcv: dst %s src %s %s\n", cstring(dst), cstring(src), cstring(*packet)); if (!_running) { // There is a window that may occasionally get hit. XLOG_WARNING("Packet arrived while peer is not running"); return false; } OspfTypes::AreaID area = packet->get_area_id(); // Does the area ID in the packet match any that are expecting. if (0 == _areas.count(area)) { if (OspfTypes::BACKBONE == area) { return _ospf.get_peer_manager(). receive_virtual_link(dst, src, packet); } xorp_throw(BadPeer, c_format("Area %s not handled by %s/%s", pr_id(packet->get_area_id()).c_str(), _interface.c_str(), _vif.c_str())); } switch(_ospf.get_version()) { case OspfTypes::V2: break; case OspfTypes::V3: // Non link-local addreses are only allowed for virtual links // which by definition are in the backbone. if (!src.is_linklocal_unicast() && OspfTypes::BACKBONE != area && get_linktype() != OspfTypes::VirtualLink) { typename map *>::const_iterator i; for(i = _areas.begin(); i != _areas.end(); i++) XLOG_WARNING("area %s:", pr_id((*i).first).c_str()); XLOG_WARNING("Packet has not been sent with a link-local address " "%s %s", cstring(src), cstring(*packet)); return false; } break; } return _areas[area]->receive(dst, src, packet); } template bool PeerOut::send_lsa(OspfTypes::AreaID area, const OspfTypes::NeighbourID nid, Lsa::LsaRef lsar) { if (0 == _areas.count(area)) { XLOG_ERROR("Unknown Area %s", pr_id(area).c_str()); return false; } return _areas[area]->send_lsa(nid, lsar); } template bool PeerOut::queue_lsa(OspfTypes::PeerID peerid, OspfTypes::NeighbourID nid, Lsa::LsaRef lsar, bool &multicast_on_peer) const { typename map *>::const_iterator i; for(i = _areas.begin(); i != _areas.end(); i++) { if (!(*i).second->queue_lsa(peerid, nid, lsar, multicast_on_peer)) return false; } return true; } template bool PeerOut::push_lsas(const char* message) { typename map *>::iterator i; for(i = _areas.begin(); i != _areas.end(); i++) { if (!(*i).second->push_lsas(message)) return false; } return true; } template bool PeerOut::neighbours_exchange_or_loading(OspfTypes::AreaID area) { if (0 == _areas.count(area)) { XLOG_ERROR("Unknown Area %s", pr_id(area).c_str()); return false; } return _areas[area]->neighbours_exchange_or_loading(); } template bool PeerOut::neighbour_at_least_two_way(OspfTypes::AreaID area, OspfTypes::RouterID rid, bool& twoway) { if (0 == _areas.count(area)) { XLOG_ERROR("Unknown Area %s", pr_id(area).c_str()); return false; } return _areas[area]->neighbour_at_least_two_way(rid, twoway); } template bool PeerOut::get_neighbour_address(OspfTypes::AreaID area, OspfTypes::RouterID rid, uint32_t interface_id, A& neighbour_address) { if (0 == _areas.count(area)) { XLOG_ERROR("Unknown Area %s", pr_id(area).c_str()); return false; } return _areas[area]-> get_neighbour_address(rid, interface_id, neighbour_address); } template bool PeerOut::on_link_state_request_list(OspfTypes::AreaID area, const OspfTypes::NeighbourID nid, Lsa::LsaRef lsar) { if (0 == _areas.count(area)) { XLOG_ERROR("Unknown Area %s", pr_id(area).c_str()); return false; } return _areas[area]->on_link_state_request_list(nid, lsar); } template bool PeerOut::event_bad_link_state_request(OspfTypes::AreaID area, const OspfTypes::NeighbourID nid) { if (0 == _areas.count(area)) { XLOG_ERROR("Unknown Area %s", pr_id(area).c_str()); return false; } return _areas[area]->event_bad_link_state_request(nid); } template bool PeerOut::virtual_link_endpoint(OspfTypes::AreaID area) { if (0 == _areas.count(area)) { // Can be call opportunistically // XLOG_ERROR("Unknown Area %s", pr_id(area).c_str()); return false; } return _areas[area]->virtual_link_endpoint(); } template bool PeerOut::bring_up_peering() { uint32_t interface_id = 0; switch (_ospf.get_version()) { case OspfTypes::V2: break; case OspfTypes::V3: // Get the interface ID. if (!_ospf.get_interface_id(_interface, _vif, interface_id)) { XLOG_ERROR("Unable to get interface ID for %s", _interface.c_str()); return false; } set_interface_id(interface_id); // Get the link-local address to use for transmission unless // this is a virtual link. if (OspfTypes::VirtualLink != get_linktype()) { A link_local_address; if (!_ospf.get_link_local_address(_interface, _vif, link_local_address)) { XLOG_ERROR("Unable to get link local address for %s/%s", _interface.c_str(), _vif.c_str()); return false; } set_interface_address(link_local_address); } break; } // Get the prefix length. A source = get_interface_address(); // This is effectively this->set_prefix_length, as it's pass-by-reference. if (!_ospf.get_prefix_length(_interface, _vif, source, _interface_prefix_length)) { XLOG_ERROR("Unable to get prefix length for %s/%s/%s", _interface.c_str(), _vif.c_str(), cstring(source)); return false; } // Get the MTU. _interface_mtu = _ospf.get_mtu(_interface); if (0 == _interface_mtu) { XLOG_ERROR("Unable to get MTU for %s", _interface.c_str()); return false; } start_receiving_packets(); typename map *>::iterator i; for(i = _areas.begin(); i != _areas.end(); i++) { set_mask((*i).second); (*i).second->start(); AreaRouter *area_router = _ospf.get_peer_manager().get_area_router((*i).first); XLOG_ASSERT(area_router); area_router->peer_up(_peerid); } return true; } template void PeerOut::take_down_peering() { typename map *>::iterator i; for(i = _areas.begin(); i != _areas.end(); i++) { (*i).second->stop(); AreaRouter *area_router = _ospf.get_peer_manager().get_area_router((*i).first); XLOG_ASSERT(area_router); area_router->peer_down(_peerid); } XLOG_WARNING("PeerOut, take_down_peering on interface: %s", get_if_name().c_str()); stop_receiving_packets(); } template bool PeerOut::get_passive() { typename map *>::iterator i; // If all the peers are passive we will consider the peerout passive. // This is used to determine if we should or should not receive packets. // If in the future we allow multiple peers to be configured then // passive will have to be handled by dropping packets in software // in the peer input routines. for(i = _areas.begin(); i != _areas.end(); i++) { if (!(*i).second->get_passive()) return false; } return true; } template void PeerOut::start_receiving_packets() { if (_receiving) return; if (!_running) return; if (get_passive()) return; // Start receiving packets on this peering. _ospf.enable_interface_vif(_interface, _vif); if (do_multicast(get_linktype())) join_multicast_group(A::OSPFIGP_ROUTERS()); _receiving = true; } template void PeerOut::stop_receiving_packets() { if (!_receiving) return; XLOG_WARNING("PeerOut, stop_receiving_packets on interface: %s", get_if_name().c_str()); // Stop receiving packets on this peering. if (do_multicast(get_linktype())) leave_multicast_group(A::OSPFIGP_ROUTERS()); _ospf.disable_interface_vif(_interface, _vif); _receiving = false; } template void PeerOut::router_id_changing() { typename map *>::const_iterator i; for(i = _areas.begin(); i != _areas.end(); i++) { (*i).second->router_id_changing(); } } template bool PeerOut::set_interface_id(uint32_t interface_id) { _interface_id = interface_id; typename map *>::const_iterator i; for(i = _areas.begin(); i != _areas.end(); i++) { (*i).second->set_interface_id(interface_id); } return true; } template bool PeerOut::get_attached_routers(OspfTypes::AreaID area, list& routers) { if (0 == _areas.count(area)) { XLOG_ERROR("Unknown Area %s", pr_id(area).c_str()); return false; } return _areas[area]->get_attached_routers(routers); } template bool PeerOut::add_advertise_net(OspfTypes::AreaID area, A addr, uint32_t prefix) { if (0 == _areas.count(area)) { XLOG_ERROR("Unknown Area %s", pr_id(area).c_str()); return false; } return _areas[area]->add_advertise_net(addr, prefix); } template bool PeerOut::remove_all_nets(OspfTypes::AreaID area) { if (0 == _areas.count(area)) { XLOG_ERROR("Unknown Area %s", pr_id(area).c_str()); return false; } return _areas[area]->remove_all_nets(); } template bool PeerOut::update_nets(OspfTypes::AreaID area) { if (0 == _areas.count(area)) { XLOG_ERROR("Unknown Area %s", pr_id(area).c_str()); return false; } return _areas[area]->update_nets(); } template bool PeerOut::set_hello_interval(OspfTypes::AreaID area, uint16_t hello_interval) { if (0 == _areas.count(area)) { XLOG_ERROR("Unknown Area %s", pr_id(area).c_str()); return false; } return _areas[area]->set_hello_interval(hello_interval); } template bool PeerOut::set_options(OspfTypes::AreaID area, uint32_t options) { if (0 == _areas.count(area)) { XLOG_ERROR("Unknown Area %s", pr_id(area).c_str()); return false; } return _areas[area]->set_options(options); } template bool PeerOut::set_retransmit_interval(OspfTypes::AreaID area, uint16_t retransmit_interval) { if (0 == _areas.count(area)) { XLOG_ERROR("Unknown Area %s", pr_id(area).c_str()); return false; } return _areas[area]->set_rxmt_interval(retransmit_interval); } template bool PeerOut::set_router_priority(OspfTypes::AreaID area, uint8_t priority) { if (0 == _areas.count(area)) { XLOG_ERROR("Unknown Area %s", pr_id(area).c_str()); return false; } return _areas[area]->set_router_priority(priority); } template bool PeerOut::set_router_dead_interval(OspfTypes::AreaID area, uint32_t router_dead_interval) { if (0 == _areas.count(area)) { XLOG_ERROR("Unknown Area %s", pr_id(area).c_str()); return false; } return _areas[area]->set_router_dead_interval(router_dead_interval); } template bool PeerOut::set_simple_authentication_key(OspfTypes::AreaID area, const string& password, string& error_msg) { switch(_ospf.get_version()) { case OspfTypes::V2: break; case OspfTypes::V3: XLOG_FATAL("OSPFv3 does not support authentication"); break; } if (0 == _areas.count(area)) { error_msg = c_format("Unknown Area %s", pr_id(area).c_str()); return false; } return _areas[area]->set_simple_authentication_key(password, error_msg); } template bool PeerOut::delete_simple_authentication_key(OspfTypes::AreaID area, string& error_msg) { switch(_ospf.get_version()) { case OspfTypes::V2: break; case OspfTypes::V3: XLOG_FATAL("OSPFv3 does not support authentication"); break; } if (0 == _areas.count(area)) { error_msg = c_format("Unknown Area %s", pr_id(area).c_str()); return false; } return _areas[area]->delete_simple_authentication_key(error_msg); } template bool PeerOut::set_md5_authentication_key(OspfTypes::AreaID area, uint8_t key_id, const string& password, const TimeVal& start_timeval, const TimeVal& end_timeval, const TimeVal& max_time_drift, string& error_msg) { switch(_ospf.get_version()) { case OspfTypes::V2: break; case OspfTypes::V3: XLOG_FATAL("OSPFv3 does not support authentication"); break; } if (0 == _areas.count(area)) { error_msg = c_format("Unknown Area %s", pr_id(area).c_str()); return false; } return _areas[area]->set_md5_authentication_key(key_id, password, start_timeval, end_timeval, max_time_drift, error_msg); } template bool PeerOut::delete_md5_authentication_key(OspfTypes::AreaID area, uint8_t key_id, string& error_msg) { switch(_ospf.get_version()) { case OspfTypes::V2: break; case OspfTypes::V3: XLOG_FATAL("OSPFv3 does not support authentication"); break; } if (0 == _areas.count(area)) { error_msg = c_format("Unknown Area %s", pr_id(area).c_str()); return false; } return _areas[area]->delete_md5_authentication_key(key_id, error_msg); } template bool PeerOut::set_passive(OspfTypes::AreaID area, bool passive, bool host) { if (0 == _areas.count(area)) { XLOG_ERROR("Unknown Area %s", pr_id(area).c_str()); return false; } return _areas[area]->set_passive(passive, host); } template bool PeerOut::set_interface_cost(uint16_t interface_cost) { _interface_cost = interface_cost; typename map *>::iterator i; for(i = _areas.begin(); i != _areas.end(); i++) (*i).second->update_router_links(); return true; } template bool PeerOut::get_neighbour_list(list& neighbours) const { typename map *>::const_iterator i; for(i = _areas.begin(); i != _areas.end(); i++) { (*i).second->get_neighbour_list(neighbours); } return true; } template bool PeerOut::get_neighbour_info(OspfTypes::NeighbourID nid, NeighbourInfo& ninfo) const { typename map *>::const_iterator i; for(i = _areas.begin(); i != _areas.end(); i++) { if ((*i).second->get_neighbour_info(nid, ninfo)) { return true; } } return false; } template set >& PeerOut::get_address_info(OspfTypes::AreaID area) { if (0 == _areas.count(area)) { XLOG_ERROR("Unknown Area %s unable to return address info", pr_id(area).c_str()); return _dummy; } return _areas[area]->get_address_info(); } /****************************************/ template bool Peer::initV3() { if (OspfTypes::VirtualLink == get_linktype()) return true; // Never need to delete this as the ref_ptr will tidy up. LinkLsa *llsa = new LinkLsa(_ospf.get_version()); llsa->set_self_originating(true); TimeVal now; _ospf.get_eventloop().current_time(now); llsa->record_creation_time(now); llsa->set_peerid(get_peerid()); _link_lsa = Lsa::LsaRef(llsa); return true; } template bool Peer::goV3() { if (OspfTypes::VirtualLink == get_linktype()) return true; populate_link_lsa(); get_area_router()->add_link_lsa(get_peerid(), _link_lsa); return true; } template bool Peer::shutdownV3() { if (OspfTypes::VirtualLink == get_linktype()) return true; get_area_router()->withdraw_link_lsa(get_peerid(), _link_lsa); return true; } template <> void Peer::populate_link_lsa() { } template <> void Peer::populate_link_lsa() { XLOG_ASSERT(OspfTypes::VirtualLink != get_linktype()); LinkLsa *llsa = dynamic_cast(_link_lsa.get()); XLOG_ASSERT(llsa); llsa->get_header().set_link_state_id(get_interface_id()); llsa->get_header().set_advertising_router(_ospf.get_router_id()); // The router priority is set in the set_router_priority method. // The options are set in the set_options method. llsa->set_link_local_address(get_interface_address()); } template bool Peer::match(IPv6 source) const { if (OspfTypes::VirtualLink == get_linktype()) return false; LinkLsa *llsa = dynamic_cast(_link_lsa.get()); XLOG_ASSERT(llsa); const list& link_prefixes = llsa->get_prefixes(); list::const_iterator i; for (i = link_prefixes.begin(); i != link_prefixes.end(); i++) if (i->get_network().masked_addr() == source) return true; return false; } template bool Peer::add_neighbour(A neighbour_address, OspfTypes::RouterID rid) { switch(get_linktype()) { case OspfTypes::PointToPoint: if (0 != _neighbours.size()) { XLOG_ERROR("A PointToPoint link should have only one neighbour"); return false; } break; case OspfTypes::BROADCAST: // Just allow it there isn't any harm. break; case OspfTypes::NBMA: XLOG_UNFINISHED(); break; case OspfTypes::PointToMultiPoint: // Allow multiple neighbours to be added. break; case OspfTypes::VirtualLink: break; } Neighbour *n = find_neighbour(neighbour_address, rid); if (0 == n) { n = new Neighbour(_ospf, *this, rid, neighbour_address, Neighbour::_ticket++, get_linktype()); _neighbours.push_back(n); } else { XLOG_ERROR("Neighbour exists %s", cstring(*n)); return false; } update_router_links(); return true; } template bool Peer::remove_neighbour(A neighbour_address, OspfTypes::RouterID rid) { Neighbour *n = find_neighbour(neighbour_address, rid); if (0 == n) { XLOG_ERROR("Neighbour not found Address: %s RouterID %s", cstring(neighbour_address), pr_id(rid).c_str()); return false; } typename list *>::iterator ni; for(ni = _neighbours.begin(); ni != _neighbours.end(); ni++) { if (*ni == n) { (*ni)->event_kill_neighbour(); delete (*ni); _neighbours.erase(ni); update_router_links(); return true; } } return false; } template <> bool Peer::belongs(IPv4 addr) const { return addr == get_interface_address(); } template <> bool Peer::belongs(IPv6 addr) const { if (addr == get_interface_address()) return true; return match(addr); } template bool Peer::receive(A dst, A src, Packet *packet) { // Please note that the return status from this method determines // if the packet is freed. A return status of false means free the // packet it hasn't been stored. XLOG_TRACE(_ospf.trace()._packets, "peer-rcv: dst %s src %s %s\n", cstring(dst), cstring(src), cstring(*packet)); switch(_ospf.get_version()) { case OspfTypes::V2: break; case OspfTypes::V3: if (packet->get_instance_id() != _ospf.get_instance_id()) { XLOG_TRACE(_ospf.trace()._input_errors, "Instance ID does not match %d\n%s", _ospf.get_instance_id(), cstring(*packet)); return false; } break; } // RFC 2328 Section 8.2. Receiving protocol packets // As the packet has reached this far a bunch of the tests have // already been performed. if (!belongs(dst) && dst != A::OSPFIGP_ROUTERS() && dst != A::OSPFIGP_DESIGNATED_ROUTERS()) { XLOG_TRACE(_ospf.trace()._input_errors, "Destination address not acceptable %s\n%s", cstring(dst), cstring(*packet)); return false; } if (src == get_interface_address() && (dst == A::OSPFIGP_ROUTERS() || dst == A::OSPFIGP_DESIGNATED_ROUTERS())) { XLOG_TRACE(_ospf.trace()._input_errors, "Dropping self originated packet %s\n%s", cstring(src), cstring(*packet)); return false; } switch(get_linktype()) { case OspfTypes::BROADCAST: case OspfTypes::NBMA: case OspfTypes::PointToMultiPoint: switch(_ospf.get_version()) { case OspfTypes::V2: { const uint16_t plen = get_interface_prefix_length(); if ((plen == 0) || (IPNet(get_interface_address(), plen) != IPNet(src, plen))) { XLOG_TRACE(_ospf.trace()._input_errors, "Dropping packet from foreign network %s\n", cstring(IPNet(src, plen))); return false; } } break; case OspfTypes::V3: break; } break; case OspfTypes::VirtualLink: break; case OspfTypes::PointToPoint: break; } if (dst == A::OSPFIGP_DESIGNATED_ROUTERS()) { switch(get_state()) { case Peer::Down: case Peer::Loopback: case Peer::Waiting: case Peer::Point2Point: case Peer::DR_other: XLOG_TRACE(_ospf.trace()._input_errors, "Must be in state DR or backup to receive ALLDRouters\n"); return false; break; case Peer::Backup: break; case Peer::DR: break; } } // Authenticate packet. Neighbour *n = find_neighbour(src, packet->get_router_id()); bool new_peer = true; if (0 == n) new_peer = true; else new_peer = false; if (! _auth_handler.verify(packet->get(), src, new_peer)) { XLOG_TRACE(_ospf.trace()._input_errors, "Authentication failed: %s", _auth_handler.error().c_str()); return false; } HelloPacket *hello; DataDescriptionPacket *dd; LinkStateRequestPacket *lsrp; LinkStateUpdatePacket *lsup; LinkStateAcknowledgementPacket *lsap; if (0 != (hello = dynamic_cast(packet))) { return process_hello_packet(dst, src, hello); } else if(0 != (dd = dynamic_cast(packet))) { return process_data_description_packet(dst, src, dd); } else if(0 != (lsrp = dynamic_cast(packet))) { return process_link_state_request_packet(dst, src, lsrp); } else if(0 != (lsup = dynamic_cast(packet))) { return process_link_state_update_packet(dst, src, lsup); } else if(0 != (lsap = dynamic_cast (packet))) { return process_link_state_acknowledgement_packet(dst, src, lsap); } else { // A packet of unknown type will only get this far if // the deocoder has recognised it. Packets with bad type // fields will not get here. XLOG_FATAL("Unknown packet type %u", packet->get_type()); } XLOG_TRACE(_ospf.trace()._packets, "Done with receive packet."); return false; } template bool Peer::accept_lsa(Lsa::LsaRef lsar) const { switch(get_linktype()) { case OspfTypes::BROADCAST: case OspfTypes::NBMA: case OspfTypes::PointToMultiPoint: case OspfTypes::PointToPoint: break; case OspfTypes::VirtualLink: if (lsar->external()) return false; } return true;; } template bool Peer::send_lsa(const OspfTypes::NeighbourID nid, Lsa::LsaRef lsar) const { if (!accept_lsa(lsar)) return true; typename list *>::const_iterator n; for(n = _neighbours.begin(); n != _neighbours.end(); n++) if ((*n)->get_neighbour_id() == nid) return (*n)->send_lsa(lsar); XLOG_UNREACHABLE(); return false; } template bool Peer::queue_lsa(OspfTypes::PeerID peerid, OspfTypes::NeighbourID nid, Lsa::LsaRef lsar, bool &multicast_on_peer) const { debug_msg("lsa %s nid %d \n", cstring(*lsar), nid); if (!accept_lsa(lsar)) return true; multicast_on_peer = false; typename list *>::const_iterator n; for(n = _neighbours.begin(); n != _neighbours.end(); n++) if (!(*n)->queue_lsa(peerid, nid, lsar, multicast_on_peer)) return false; return true; } template bool Peer::push_lsas(const char* message) { typename list *>::iterator n; for(n = _neighbours.begin(); n != _neighbours.end(); n++) if (!(*n)->push_lsas(message)) return false; return true; } template bool Peer::do_dr_or_bdr() const { switch(get_linktype()) { case OspfTypes::BROADCAST: case OspfTypes::NBMA: return true; break; case OspfTypes::PointToMultiPoint: case OspfTypes::VirtualLink: case OspfTypes::PointToPoint: return false; break; } XLOG_UNREACHABLE(); return false; } template bool Peer::is_DR() const { XLOG_ASSERT(do_dr_or_bdr()); if (DR == get_state()) { if(get_candidate_id() != get_designated_router()) XLOG_WARNING("State DR %s != %s Did the router ID change?", pr_id(get_candidate_id()).c_str(), pr_id(get_designated_router()).c_str()); return true; } return false; } template bool Peer::is_BDR() const { XLOG_ASSERT(do_dr_or_bdr()); if (Backup == get_state()) { if (get_candidate_id() != get_backup_designated_router()) XLOG_WARNING("State Backup %s != %s Did the router ID change?", pr_id(get_candidate_id()).c_str(), pr_id(get_backup_designated_router()).c_str()); return true; } return false; } template bool Peer::is_DR_or_BDR() const { XLOG_ASSERT(do_dr_or_bdr()); XLOG_ASSERT(!(is_DR() && is_BDR())); if (is_DR()) return true; if (is_BDR()) return true; return false; } template bool Peer::neighbours_exchange_or_loading() const { typename list *>::const_iterator n; for(n = _neighbours.begin(); n != _neighbours.end(); n++) { typename Neighbour::State state = (*n)->get_state(); if (Neighbour::Exchange == state || Neighbour::Loading == state) return true; } return false; } template bool Peer::neighbour_at_least_two_way(OspfTypes::RouterID rid, bool& twoway) { typename list *>::const_iterator n; for(n = _neighbours.begin(); n != _neighbours.end(); n++) if ((*n)->get_router_id() == rid) { twoway = (*n)->get_state() >= Neighbour::TwoWay; return true; } return false; } template bool Peer::get_neighbour_address(OspfTypes::RouterID rid, uint32_t interface_id, A& neighbour_address) { typename list *>::const_iterator n; for(n = _neighbours.begin(); n != _neighbours.end(); n++) if ((*n)->get_router_id() == rid) { const HelloPacket *hello = (*n)->get_hello_packet(); if (0 == hello) return false; if (hello->get_interface_id() == interface_id) { neighbour_address = (*n)->get_neighbour_address(); return true; } return false; } return false; } template bool Peer::on_link_state_request_list(const OspfTypes::NeighbourID nid, Lsa::LsaRef lsar) const { typename list *>::const_iterator n; for(n = _neighbours.begin(); n != _neighbours.end(); n++) if ((*n)->get_neighbour_id() == nid) return (*n)->on_link_state_request_list(lsar); XLOG_UNREACHABLE(); return false; } template bool Peer::event_bad_link_state_request(const OspfTypes::NeighbourID nid) const { typename list *>::const_iterator n; for(n = _neighbours.begin(); n != _neighbours.end(); n++) if ((*n)->get_neighbour_id() == nid) { (*n)->event_bad_link_state_request(); return true; } XLOG_UNREACHABLE(); return false; } template bool Peer::virtual_link_endpoint() const { typename list *>::const_iterator n; for(n = _neighbours.begin(); n != _neighbours.end(); n++) { // If this peer is associated with a virtual link it should // have only one neighbour. if (OspfTypes::VirtualLink == (*n)->get_linktype() && Neighbour::Full == (*n)->get_state()) return true; } return false; } template void Peer::send_direct_acks(OspfTypes::NeighbourID nid, list& ack) { // A direct ACK is only sent back to the neighbour that sent the // original LSA. if (ack.empty()) return; bool multicast_on_peer; typename list *>::const_iterator n; for(n = _neighbours.begin(); n != _neighbours.end(); n++) if ((*n)->get_neighbour_id() == nid) { if (!(*n)->send_ack(ack, /* direct */true, multicast_on_peer)) XLOG_WARNING("Failed to send ACK"); XLOG_ASSERT(!multicast_on_peer); return; } XLOG_UNREACHABLE(); } template void Peer::send_delayed_acks(OspfTypes::NeighbourID /*nid*/, list& ack) { // A delayed ACK is sent to all neighbours. if (ack.empty()) return; // XXX - The sending of these ACKs should be delayed. // One possible implementation would be to hold a delayed ack list // in the peer. If when this method is called the list is empty // the list is populated with acks that have been provided. Then a // timer is started that is less than the retransmit value. If the // list is non-empty when this method is called the provided acks // are added to the list suppressing duplicates. The timer will // fire and send the ACKs and empty the list. Currently ACKs are // generated for every update packet, so its not possible that the // ACKs will not fit into a single packet. If a list with a delay // is used it is possible that all the ACKS will not fit into a // single packet. // XXX bool multicast_on_peer; typename list *>::const_iterator n; for(n = _neighbours.begin(); n != _neighbours.end(); n++) { (*n)->send_ack(ack, /* direct */false, multicast_on_peer); // If this is broadcast peer it is only necessary to send the // packet once. if (multicast_on_peer) return; } } template Neighbour * Peer::find_neighbour(A src, OspfTypes::RouterID rid) { typename list *>::iterator n; switch(get_linktype()) { case OspfTypes::BROADCAST: case OspfTypes::NBMA: case OspfTypes::PointToMultiPoint: for(n = _neighbours.begin(); n != _neighbours.end(); n++) if ((*n)->get_neighbour_address() == src) return *n; break; case OspfTypes::VirtualLink: case OspfTypes::PointToPoint: for(n = _neighbours.begin(); n != _neighbours.end(); n++) if ((*n)->get_router_id() == rid) return *n; break; } return 0; } template bool Peer::is_neighbour_DR_or_BDR(OspfTypes::NeighbourID nid) const { XLOG_ASSERT(do_dr_or_bdr()); typename list *>::const_iterator n; for(n = _neighbours.begin(); n != _neighbours.end(); n++) if ((*n)->get_neighbour_id() == nid) return (*n)->is_neighbour_DR_or_BDR(); XLOG_UNREACHABLE(); return false; } template bool Peer::process_hello_packet(A dst, A src, HelloPacket *hello) { XLOG_TRACE(_ospf.trace()._packets, "hello-pkt: dst %s src %s %s\n", cstring(dst),cstring(src),cstring(*hello)); // Sanity check this hello packet. // Check the network masks - OSPFv2 only. switch(_ospf.get_version()) { case OspfTypes::V2: if (OspfTypes::PointToPoint == get_linktype() || OspfTypes::VirtualLink == get_linktype()) break; if (_hello_packet.get_network_mask() != hello->get_network_mask()) { XLOG_TRACE(_ospf.trace()._input_errors, "Network masks don't match %#x %s", _hello_packet.get_network_mask(), hello->str().c_str()); return false; } break; case OspfTypes::V3: break; } // Check the hello interval. if (_hello_packet.get_hello_interval() != hello->get_hello_interval()) { XLOG_TRACE(_ospf.trace()._input_errors, "Hello intervals don't match %d %s", _hello_packet.get_hello_interval(), hello->str().c_str()); return false; } // Check the router dead interval. if (_hello_packet.get_router_dead_interval() != hello->get_router_dead_interval()) { XLOG_TRACE(_ospf.trace()._input_errors, "Router dead intervals don't match %d %s", _hello_packet.get_router_dead_interval(), hello->str().c_str()); return false; } // Compare our and the received E-Bit they must match. if ((_hello_packet.get_options() & Options::E_bit) != (hello->get_options() & Options::E_bit)) { XLOG_TRACE(_ospf.trace()._input_errors, "E-bit does not match %s", hello->str().c_str()); return false; } // Compare our and the received N-Bit they must match. if ((_hello_packet.get_options() & Options::N_bit) != (hello->get_options() & Options::N_bit)) { XLOG_TRACE(_ospf.trace()._input_errors, "N-bit does not match %s", hello->str().c_str()); return false; } Neighbour *n = find_neighbour(src, hello->get_router_id()); if (0 == n) { // If this isn't a BROADCAST interface don't just make friends. if (OspfTypes::BROADCAST != _peerout.get_linktype()) return false; n = new Neighbour(_ospf, *this, hello->get_router_id(), src, Neighbour::_ticket++, get_linktype()); _neighbours.push_back(n); } n->event_hello_received(hello); return true; } template bool Peer::process_data_description_packet(A dst, A src, DataDescriptionPacket *dd) { XLOG_TRACE(_ospf.trace()._packets, "data-desc-pkt: dst %s src %s %s\n", cstring(dst),cstring(src),cstring(*dd)); Neighbour *n = find_neighbour(src, dd->get_router_id()); if (0 == n) { XLOG_TRACE(_ospf.trace()._input_errors, "No matching neighbour found source %s %s", cstring(src), cstring(*dd)); return false; } // Perform the MTU check. if (dd->get_interface_mtu() > get_interface_mtu()) { XLOG_TRACE(_ospf.trace()._input_errors, "Received MTU larger than %d %s", get_interface_mtu(), cstring(*dd)); return false; } n->data_description_received(dd); return false; // Never keep a copy of the packet. } template bool Peer::process_link_state_request_packet(A dst, A src, LinkStateRequestPacket *lsrp) { XLOG_TRACE(_ospf.trace()._packets, "link-state-req-pkt: dst %s src %s %s\n", cstring(dst),cstring(src),cstring(*lsrp)); Neighbour *n = find_neighbour(src, lsrp->get_router_id()); if (0 == n) { XLOG_TRACE(_ospf.trace()._input_errors, "No matching neighbour found source %s %s", cstring(src), cstring(*lsrp)); return false; } n->link_state_request_received(lsrp); return false; // Never keep a copy of the packet. } template bool Peer::process_link_state_update_packet(A dst, A src, LinkStateUpdatePacket *lsup) { XLOG_TRACE(_ospf.trace()._packets, "link-state-update-pkt: dst %s src %s %s\n", cstring(dst),cstring(src),cstring(*lsup)); Neighbour *n = find_neighbour(src, lsup->get_router_id()); if (0 == n) { XLOG_TRACE(_ospf.trace()._input_errors, "No matching neighbour found source %s %s", cstring(src), cstring(*lsup)); return false; } XLOG_TRACE(_ospf.trace()._packets, "link-state-update-pkt: telling neighbour, neigh-size: %i\n", (int)(_neighbours.size())); n->link_state_update_received(lsup); XLOG_TRACE(_ospf.trace()._packets, "link-state-update-pkt: done\n"); return false; // Never keep a copy of the packet. } template bool Peer::process_link_state_acknowledgement_packet(A dst, A src, LinkStateAcknowledgementPacket *lsap) { XLOG_TRACE(_ospf.trace()._packets, "dst %s src %s %s\n", cstring(dst),cstring(src),cstring(*lsap)); Neighbour *n = find_neighbour(src, lsap->get_router_id()); if (0 == n) { XLOG_TRACE(_ospf.trace()._input_errors, "No matching neighbour found source %s %s", cstring(src), cstring(*lsap)); return false; } n->link_state_acknowledgement_received(lsap); return false; // Never keep a copy of the packet. } template void Peer::start() { go(); _enabled = true; // _interface_state = Down; set_designated_router(set_id("0.0.0.0")); set_backup_designated_router(set_id("0.0.0.0")); if (_passive) event_loop_ind(); else event_interface_up(); } template void Peer::stop() { _enabled = false; event_interface_down(); shutdown(); } template void Peer::change_area_router_type(OspfTypes::AreaType area_type) { bool enabled = _enabled; if (enabled) stop(); set_area_type(area_type); if (enabled) start(); } template void Peer::event_interface_up() { XLOG_TRACE(_ospf.trace()._interface_events, "Event(InterfaceUp) Interface(%s) State(%s) ", get_if_name().c_str(), pp_interface_state(get_state()).c_str()); XLOG_ASSERT(Down == get_state()); switch(get_linktype()) { case OspfTypes::PointToPoint: change_state(Point2Point); start_hello_timer(); break; case OspfTypes::BROADCAST: // Not eligible to be the designated router. if (0 == _hello_packet.get_router_priority()) { change_state(DR_other); start_hello_timer(); } else { change_state(Waiting); start_hello_timer(); start_wait_timer(); } break; case OspfTypes::NBMA: XLOG_UNFINISHED(); break; case OspfTypes::PointToMultiPoint: change_state(Point2Point); start_hello_timer(); break; case OspfTypes::VirtualLink: change_state(Point2Point); start_hello_timer(); break; } update_router_links(); XLOG_ASSERT(Down != get_state()); } template void Peer::event_wait_timer() { XLOG_TRACE(_ospf.trace()._interface_events, "Event(WaitTimer) Interface(%s) State(%s) ", get_if_name().c_str(), pp_interface_state(get_state()).c_str()); switch(get_state()) { case Down: case Loopback: XLOG_FATAL("Unexpected state %s", pp_interface_state(get_state()).c_str()); break; case Waiting: compute_designated_router_and_backup_designated_router(); // The user has set the priority to zero while the router is in // state waiting. if (0 == _hello_packet.get_router_priority()) { debug_msg("State(%s) Priority(%d)\n", pp_interface_state(get_state()).c_str(), _hello_packet.get_router_priority()); if (get_state() == Waiting) change_state(DR_other); } XLOG_ASSERT(get_state() == DR_other || get_state() == Backup || get_state() == DR); break; case Point2Point: case DR_other: case Backup: case DR: XLOG_FATAL("Unexpected state %s", pp_interface_state(get_state()).c_str()); break; } update_router_links(); // Start sending hello packets. start_hello_timer(); } template void Peer::event_backup_seen() { XLOG_TRACE(_ospf.trace()._interface_events, "Event(BackupSeen) Interface(%s) State(%s) ", get_if_name().c_str(), pp_interface_state(get_state()).c_str()); switch(get_state()) { case Down: case Loopback: XLOG_FATAL("Unexpected state %s", pp_interface_state(get_state()).c_str()); break; case Waiting: stop_wait_timer(); compute_designated_router_and_backup_designated_router(); XLOG_ASSERT(get_state() == DR_other || get_state() == Backup || get_state() == DR); break; case Point2Point: case DR_other: case Backup: case DR: XLOG_FATAL("Unexpected state %s", pp_interface_state(get_state()).c_str()); break; } update_router_links(); } template void Peer::event_neighbour_change() { XLOG_TRACE(_ospf.trace()._interface_events, "Event(NeighborChange) Interface(%s) State(%s) ", get_if_name().c_str(), pp_interface_state(get_state()).c_str()); switch(get_state()) { case Down: break; case Waiting: break; case Loopback: case Point2Point: XLOG_WARNING("Unexpected state %s", pp_interface_state(get_state()).c_str()); break; case DR_other: case Backup: case DR: compute_designated_router_and_backup_designated_router(); XLOG_ASSERT(get_state() == DR_other || get_state() == Backup || get_state() == DR); break; } update_router_links(); } template void Peer::event_loop_ind() { XLOG_WARNING("Event(LoopInd) Interface(%s) State(%s) ", get_if_name().c_str(), pp_interface_state(get_state()).c_str()); change_state(Loopback); tear_down_state(); update_router_links(); remove_neighbour_state(); _peerout.stop_receiving_packets(); } template void Peer::event_unloop_ind() { XLOG_TRACE(_ospf.trace()._interface_events, "Event(UnLoopInd) Interface(%s) State(%s) ", get_if_name().c_str(), pp_interface_state(get_state()).c_str()); switch(get_state()) { case Down: XLOG_WARNING("Unexpected state %s", pp_interface_state(get_state()).c_str()); break; case Loopback: change_state(Down); break; case Waiting: case Point2Point: case DR_other: case Backup: case DR: XLOG_WARNING("Unexpected state %s", pp_interface_state(get_state()).c_str()); break; } update_router_links(); _peerout.start_receiving_packets(); } template void Peer::event_interface_down() { XLOG_TRACE(_ospf.trace()._interface_events, "Event(InterfaceDown) Interface(%s) State(%s) ", get_if_name().c_str(), pp_interface_state(get_state()).c_str()); change_state(Down); tear_down_state(); update_router_links(); remove_neighbour_state(); } template void Peer::schedule_event(const char *event) { if (_scheduled_events.empty()) { _event_timer = _ospf.get_eventloop(). new_oneoff_after_ms(0, callback(this, &Peer::process_scheduled_events)); } _scheduled_events.push_back(event); } template void Peer::process_scheduled_events() { struct event { string event_name; XorpCallback0::RefPtr cb; } events[] = { {"NeighbourChange", callback(this, &Peer::event_neighbour_change)}, {"BackupSeen", callback(this, &Peer::event_backup_seen)}, }; _scheduled_events.unique(); list::const_iterator e; for(e = _scheduled_events.begin(); e != _scheduled_events.end(); e++) { bool found = false; for (size_t i = 0; i < sizeof(events) / sizeof(struct event); i++) { if ((*e) == events[i].event_name) { events[i].cb->dispatch(); found = true; break; } } if (!found) XLOG_FATAL("Unknown event %s", (*e).c_str()); } _scheduled_events.clear(); } template void Peer::start_hello_timer() { // XXX - The hello packet should have all its parameters set. // Schedule one for the future. _hello_timer = _ospf.get_eventloop(). new_periodic_ms(_hello_packet.get_hello_interval() * 1000, callback(this, &Peer::send_hello_packet)); // Send one immediately. send_hello_packet(); // A more logical way of structuring the code may have been to get // send_hello_packet to schedule the next hello packet. The // problem with such an approach is that the interval between // transmissions would not be fixed. Any elasticity would allow // hello packets to become synchronized which is bad. } template void Peer::stop_hello_timer() { _hello_timer.clear(); } template void Peer::restart_hello_timer() { if (_hello_timer.scheduled()) { stop_hello_timer(); start_hello_timer(); } } template void Peer::start_wait_timer() { _wait_timer = _ospf.get_eventloop(). new_oneoff_after(TimeVal(_hello_packet.get_router_dead_interval(), 0), callback(this, &Peer::event_wait_timer)); } template void Peer::stop_wait_timer() { _wait_timer.clear(); } #if 0 template uint32_t Peer::send_options() { // Set/UnSet E-Bit. Options options(_ospf.get_version(), 0); switch(_area_type) { case OspfTypes::NORMAL: options.set_e_bit(true); break; case OspfTypes::STUB: case OspfTypes::NSSA: options.set_e_bit(false); break; } return options.get_options(); } #endif template void Peer::populate_common_header(Packet& packet) { switch(_ospf.get_version()) { case OspfTypes::V2: break; case OspfTypes::V3: packet.set_instance_id(_ospf.get_instance_id()); break; } // Fetch the router ID. packet.set_router_id(_ospf.get_router_id()); // Set the Area ID packet.set_area_id(get_area_id()); } template bool Peer::send_hello_packet() { vector pkt; // Fetch the router ID. _hello_packet.set_router_id(_ospf.get_router_id()); #if 0 // Options. _hello_packet.set_options(send_options()); #endif // Put the neighbours into the hello packet. _hello_packet.get_neighbours().clear(); typename list *>::iterator n; for(n = _neighbours.begin(); n != _neighbours.end(); n++) { if ((*n)->announce_in_hello_packet()) _hello_packet.get_neighbours().push_back((*n)->get_router_id()); } _hello_packet.encode(pkt); get_auth_handler().generate(pkt); SimpleTransmit *transmit = 0; switch(get_linktype()) { case OspfTypes::PointToPoint: case OspfTypes::BROADCAST: transmit = new SimpleTransmit(pkt, A::OSPFIGP_ROUTERS(), _peerout.get_interface_address()); break; case OspfTypes::NBMA: XLOG_UNFINISHED(); break; case OspfTypes::PointToMultiPoint: // At the time of writing virtual links had only one neighbour. case OspfTypes::VirtualLink: for(n = _neighbours.begin(); n != _neighbours.end(); n++) { transmit = new SimpleTransmit(pkt, (*n)->get_neighbour_address(), _peerout.get_interface_address()); typename Transmit::TransmitRef tr(transmit); _peerout.transmit(tr); } return true; break; } typename Transmit::TransmitRef tr(transmit); _peerout.transmit(tr); return true; } template <> OspfTypes::RouterID Peer::get_candidate_id(IPv4 source_address, OspfTypes::RouterID) { return ntohl(source_address.addr()); } template <> OspfTypes::RouterID Peer::get_candidate_id(IPv6, OspfTypes::RouterID router_id) { return router_id; } template <> OspfTypes::RouterID Peer::get_candidate_id(IPv4) const { return ntohl(_peerout.get_interface_address().addr()); } template <> OspfTypes::RouterID Peer::get_candidate_id(IPv6) const { return _ospf.get_router_id(); } template OspfTypes::RouterID Peer::backup_designated_router(list& candidates) const { XLOG_ASSERT(do_dr_or_bdr()); // Step (2) // Calculate the the new backup designated router. // Look for routers that do not consider themselves to be the DR // but do consider themselves to the the BDR. Candidate c(set_id("0.0.0.0"), set_id("0.0.0.0"), set_id("0.0.0.0"), set_id("0.0.0.0"), 0); typename list::const_iterator i; for(i = candidates.begin(); i != candidates.end(); i++) { XLOG_TRACE(_ospf.trace()._election, "Candidate: %s ", cstring((*i))); if ((*i)._candidate_id != (*i)._dr && (*i)._candidate_id == (*i)._bdr) { if ((*i)._router_priority > c._router_priority) c = *i; else if ((*i)._router_priority == c._router_priority && (*i)._router_id > c._router_id) c = *i; } } // It is possible that no router was selected because no router // had itself as BDR. if (0 == c._router_priority) { for(i = candidates.begin(); i != candidates.end(); i++) { if ((*i)._candidate_id != (*i)._dr) { if ((*i)._router_priority > c._router_priority) c = *i; else if ((*i)._router_priority == c._router_priority && (*i)._router_id > c._router_id) c = *i; } } } XLOG_TRACE(_ospf.trace()._election, "New BDR %s", pr_id(c._candidate_id).c_str()); return c._candidate_id; } template OspfTypes::RouterID Peer::designated_router(list& candidates, OspfTypes::RouterID backup_designated_router) const { XLOG_ASSERT(do_dr_or_bdr()); // Step (3) // Calculate the designated router. Candidate c(set_id("0.0.0.0"), set_id("0.0.0.0"), set_id("0.0.0.0"), set_id("0.0.0.0"), 0); typename list::const_iterator i; for(i = candidates.begin(); i != candidates.end(); i++) { XLOG_TRACE(_ospf.trace()._election, "Candidate: %s ", cstring((*i))); if ((*i)._candidate_id == (*i)._dr) { if ((*i)._router_priority > c._router_priority) c = *i; else if ((*i)._router_priority == c._router_priority && (*i)._router_id > c._router_id) c = *i; } } // It is possible that no router was selected because no router // had itself as DR. Therefore just select the backup designated router. if (0 == c._router_priority) { XLOG_TRACE(_ospf.trace()._election,"New DR chose BDR %s", pr_id(backup_designated_router).c_str()); return backup_designated_router; } XLOG_TRACE(_ospf.trace()._election, "New DR %s", pr_id(c._candidate_id).c_str()); return c._candidate_id; } template void Peer::compute_designated_router_and_backup_designated_router() { XLOG_ASSERT(do_dr_or_bdr()); XLOG_TRACE(_ospf.trace()._election, "Start election: DR %s BDR %s", pr_id(get_designated_router()).c_str(), pr_id(get_backup_designated_router()).c_str()); list candidates; // Is this router a candidate? if (0 != _hello_packet.get_router_priority()) { candidates. push_back(Candidate(get_candidate_id(), _ospf.get_router_id(), _hello_packet.get_designated_router(), _hello_packet.get_backup_designated_router(), _hello_packet.get_router_priority())); } // Go through the neighbours and pick possible candidates. typename list *>::const_iterator n; for (n = _neighbours.begin(); n != _neighbours.end(); n++) { const HelloPacket *hello = (*n)->get_hello_packet(); if (0 == hello) continue; // A priority of 0 means a router is not a candidate. if (0 != hello->get_router_priority() && Neighbour::TwoWay <= (*n)->get_state()) { candidates. push_back(Candidate((*n)->get_candidate_id(), (*n)->get_router_id(), hello->get_designated_router(), hello->get_backup_designated_router(), hello->get_router_priority())); } } // Step (2) // Calculate the the new backup designated router. OspfTypes::RouterID bdr = backup_designated_router(candidates); // Step (3) // Calculate the designated router. OspfTypes::RouterID dr = designated_router(candidates, bdr); // Step (4) // If the router has become the DR or BDR or it was the DR or BDR // and no longer is then steps (2) and (3) need to be repeated. // If nothing has changed out of here. if (_hello_packet.get_designated_router() == dr && _hello_packet.get_backup_designated_router() == bdr) if (Waiting != get_state()) { XLOG_TRACE(_ospf.trace()._election, "End election: No change"); return; } bool recompute = false; // Has this router just become the DR or BDR if (get_candidate_id() == dr && _hello_packet.get_designated_router() != dr) recompute = true; if (get_candidate_id() == bdr && _hello_packet.get_backup_designated_router() != bdr) recompute = true; // Was this router the DR or BDR if (get_candidate_id() != dr && _hello_packet.get_designated_router() == get_candidate_id()) recompute = true; if (get_candidate_id() != bdr && _hello_packet.get_backup_designated_router() == get_candidate_id()) recompute = true; if (recompute) { // If this router was the DR or BDR and the priority was set // to 0 we can get here. if (0 != _hello_packet.get_router_priority()) { typename list::iterator i = candidates.begin(); // Verify that the first entry in the candidate list is // this router. XLOG_ASSERT((*i)._candidate_id == get_candidate_id()); // Update the DR and BDR (*i)._dr = dr; (*i)._bdr = bdr; } // Repeat steps (2) and (3). bdr = backup_designated_router(candidates); dr = designated_router(candidates, bdr); } XLOG_TRACE(_ospf.trace()._election, "End election: DR %s BDR %s", pr_id(dr).c_str(), pr_id(bdr).c_str()); // Step(5) set_designated_router(dr); set_backup_designated_router(bdr); if (get_candidate_id() == dr) change_state(DR); else if (get_candidate_id() == bdr) change_state(Backup); else change_state(DR_other); // Step(6) if(OspfTypes::NBMA == get_linktype()) XLOG_UNFINISHED(); // Step(7) // Need to send AdjOK to all neighbours that are least 2-Way. for(n = _neighbours.begin(); n != _neighbours.end(); n++) if (Neighbour::TwoWay <= (*n)->get_state()) (*n)->event_adj_ok(); } #if 0 /** * Utility function to extract a 32 quantity, basically an IPv4 * address. The code uses templates so sometimes code is generated to * extract a 32 bit quantity from an IPv6 address. * */ template uint32_t get32(A a); template <> inline uint32_t get32(IPv4 a) { return a.addr(); } template <> inline uint32_t get32(IPv6) { XLOG_FATAL("Only IPv4 not IPv6"); return 0; } #endif template void Peer::update_router_links() { debug_msg("Interface(%s) State(%s) Linktype(%s)\n", get_if_name().c_str(), pp_interface_state(get_state()).c_str(), pp_link_type(get_linktype()).c_str()); OspfTypes::Version version = _ospf.get_version(); // Save the old router links so that its possible to tell if the // router links have changed. list router_links; router_links.insert(router_links.begin(), _router_links.begin(), _router_links.end()); _router_links.clear(); switch(version) { case OspfTypes::V2: update_router_linksV2(_router_links); break; case OspfTypes::V3: if (0 != _neighbours.size()) update_router_linksV3(_router_links); break; } list::iterator i, j; bool equal = false; if (router_links.size() == _router_links.size()) { for (i = router_links.begin(); i != router_links.end(); i++) { equal = false; for (j = _router_links.begin(); j != _router_links.end(); j++) { if(*i == *j) { equal = true; break; } } if (equal == false) break; } } if (equal == false) { get_area_router()->new_router_links(get_peerid(), _router_links); } } template <> uint32_t Peer::get_designated_router_interface_id(IPv4) const { XLOG_ASSERT(do_dr_or_bdr()); switch(_ospf.get_version()) { case OspfTypes::V2: XLOG_FATAL("OSPFv3 Only"); break; case OspfTypes::V3: break; } XLOG_UNREACHABLE(); return 0; } template <> uint32_t Peer::get_designated_router_interface_id(IPv6) const { XLOG_ASSERT(do_dr_or_bdr()); switch(_ospf.get_version()) { case OspfTypes::V2: XLOG_FATAL("OSPFv3 Only"); break; case OspfTypes::V3: break; } switch(get_state()) { case Down: case Loopback: case Waiting: case Point2Point: break; case Backup: case DR_other: { // Scan through the neighbours until we find the DR. list *>::const_iterator n; for(n = _neighbours.begin(); n != _neighbours.end(); n++) if (get_designated_router() == (*n)->get_router_id()) { XLOG_ASSERT((*n)->get_hello_packet()); return (*n)->get_hello_packet()->get_interface_id(); break; } XLOG_FATAL("Designated router not found"); } break; case DR: // If this is the DR simple. return get_interface_id(); break; } XLOG_FATAL("Designated router interface ID " "available in states DR, DR Other and Backup not %s", pp_interface_state(get_state()).c_str()); return 0; } template <> void Peer::update_router_linksV2(list& router_links) { OspfTypes::Version version = _ospf.get_version(); RouterLink router_link(version); switch(get_state()) { case Down: // No links return; break; case Loopback: { // XXX - We should be checking to see if this is p2p unnumbered. uint16_t prefix_length; if (_passive_host) { prefix_length = IPv4::ADDR_BITLEN; router_link.set_metric(0); } else { prefix_length = get_interface_prefix_length(); router_link.set_metric(_peerout.get_interface_cost()); } IPNet net(get_interface_address(), prefix_length); router_link.set_type(RouterLink::stub); router_link.set_link_id(ntohl(net.masked_addr().addr())); router_link.set_link_data(ntohl(net.netmask().addr())); router_links.push_back(router_link); return; } case Waiting: break; case Point2Point: // Dealt with below in PointToPoint case. break; case DR_other: break; case Backup: break; case DR: break; } switch(get_linktype()) { case OspfTypes::PointToPoint: { // RFC 2328 Section 12.4.1.1. Describing point-to-point interfaces // There should be a single neighbour configured if (1 != _neighbours.size()) { XLOG_ERROR("A single neighbour should be configured"); return; } list *>::const_iterator n = _neighbours.begin(); XLOG_ASSERT(n != _neighbours.end()); if (Neighbour::Full == (*n)->get_state()) { router_link.set_type(RouterLink::p2p); router_link.set_link_id((*n)->get_router_id()); // We are not dealing with the unnumbered interface case. router_link.set_link_data(ntohl(get_interface_address().addr())); router_link.set_metric(_peerout.get_interface_cost()); router_links.push_back(router_link); } switch(get_state()) { case Down: break; case Loopback: break; case Waiting: break; case Point2Point: // If a prefix length has been set to 32 // (IPv4::ADDR_BITLEN) no network has been configured, // announce a host route, otherwise advertise the network. if (IPv4::ADDR_BITLEN == _peerout.get_interface_prefix_length()) { // Option 1 router_link.set_type(RouterLink::stub); router_link. set_link_id(ntohl((*n)->get_neighbour_address().addr())); router_link.set_link_data(0xffffffff); router_link.set_metric(_peerout.get_interface_cost()); router_links.push_back(router_link); } else { // Option 2 router_link.set_type(RouterLink::stub); IPNet net(get_interface_address(), _peerout.get_interface_prefix_length()); router_link.set_link_id(ntohl(net.masked_addr().addr())); router_link.set_link_data(ntohl(net.netmask().addr())); router_link.set_metric(_peerout.get_interface_cost()); router_links.push_back(router_link); } break; case DR_other: break; case Backup: break; case DR: break; } } break; case OspfTypes::BROADCAST: case OspfTypes::NBMA: { // RFC 2328 Section 12.4.1.2.Describing broadcast and NBMA interfaces bool adjacent = false; switch(get_state()) { case Down: break; case Loopback: break; case Waiting: { router_link.set_type(RouterLink::stub); IPNet net(get_interface_address(), _peerout.get_interface_prefix_length()); router_link.set_link_id(ntohl(net.masked_addr().addr())); router_link.set_link_data(ntohl(net.netmask().addr())); router_link.set_metric(_peerout.get_interface_cost()); router_links.push_back(router_link); } break; case Point2Point: XLOG_UNFINISHED(); break; case Backup: // XXX - Is this correct for the backup state? case DR_other: { // Do we have an adjacency with the DR? list *>::iterator n; for(n = _neighbours.begin(); n != _neighbours.end(); n++) if (get_designated_router() == (*n)->get_candidate_id()) { break; } if (n == _neighbours.end()) return; if (Neighbour::Full == (*n)->get_state()) adjacent = true; } /* FALLTHROUGH */ case DR: { // Try and find any adjacency. list *>::iterator n; for(n = _neighbours.begin(); n != _neighbours.end(); n++) if (Neighbour::Full == (*n)->get_state()) { adjacent = true; break; } if (adjacent) { router_link.set_type(RouterLink::transit); router_link.set_link_id(get_designated_router()); router_link.set_link_data(ntohl(get_interface_address(). addr())); } else { router_link.set_type(RouterLink::stub); IPNet net(get_interface_address(), _peerout.get_interface_prefix_length()); router_link.set_link_id(ntohl(net.masked_addr().addr())); router_link.set_link_data(ntohl(net.netmask().addr())); } router_link.set_metric(_peerout.get_interface_cost()); router_links.push_back(router_link); } break; } } break; case OspfTypes::PointToMultiPoint: { // RFC 2328 Section 12.4.1.4. Describing Point-to-MultiPoint interfaces router_link.set_type(RouterLink::stub); router_link.set_link_id(ntohl(get_interface_address().addr())); router_link.set_link_data(0xffffffff); router_link.set_metric(0); router_links.push_back(router_link); list *>::iterator n; for(n = _neighbours.begin(); n != _neighbours.end(); n++) { if (Neighbour::Full == (*n)->get_state()) { router_link.set_type(RouterLink::p2p); router_link.set_link_id((*n)->get_router_id()); router_link. set_link_data(ntohl(get_interface_address().addr())); router_link.set_metric(_peerout.get_interface_cost()); router_links.push_back(router_link); } } } break; case OspfTypes::VirtualLink: // At the time of writing virtual links had only one neighbour. list *>::iterator n; for(n = _neighbours.begin(); n != _neighbours.end(); n++) { if (Neighbour::Full == (*n)->get_state()) { router_link.set_type(RouterLink::vlink); router_link.set_link_id((*n)->get_router_id()); router_link. set_link_data(ntohl(get_interface_address().addr())); router_link.set_metric(_peerout.get_interface_cost()); router_links.push_back(router_link); } } break; } } template <> void Peer::update_router_linksV2(list&) { XLOG_FATAL("OSPFv2 can't carry IPv6 addresses"); } template <> void Peer::update_router_linksV3(list&) { XLOG_FATAL("OSPFv3 can't carry IPv4 addresses yet!"); } template <> void Peer::update_router_linksV3(list& router_links) { RouterLink router_link(OspfTypes::V3); switch(get_state()) { case Down: case Loopback: // No links return; break; case Waiting: break; case Point2Point: break; case DR_other: break; case Backup: break; case DR: break; } router_link.set_interface_id(get_interface_id()); router_link.set_metric(_peerout.get_interface_cost()); switch(get_linktype()) { case OspfTypes::PointToPoint: { // Find the neighbour. If the neighbour is fully adjacent then // configure a router link. list *>::iterator n = _neighbours.begin(); if (n != _neighbours.end() && Neighbour::Full == (*n)->get_state()) { router_link.set_type(RouterLink::p2p); XLOG_ASSERT((*n)->get_hello_packet()); router_link.set_neighbour_interface_id((*n)->get_hello_packet()-> get_interface_id()); router_link.set_neighbour_router_id((*n)->get_router_id()); router_links.push_back(router_link); } } break; case OspfTypes::BROADCAST: case OspfTypes::NBMA: { bool adjacent = false; switch(get_state()) { case Down: break; case Loopback: break; case Waiting: break; case Point2Point: break; case Backup: // XXX - Is this correct for the backup state? case DR_other: { // Do we have an adjacency with the DR? list *>::iterator n; for(n = _neighbours.begin(); n != _neighbours.end(); n++) if (get_designated_router() == (*n)->get_candidate_id()) { break; } if (n == _neighbours.end()) return; if (Neighbour::Full == (*n)->get_state()) adjacent = true; } /* FALLTHROUGH */ case DR: { // Try and find any adjacency. list *>::iterator n; for(n = _neighbours.begin(); n != _neighbours.end(); n++) if (Neighbour::Full == (*n)->get_state()) { adjacent = true; break; } if (adjacent) { router_link.set_type(RouterLink::transit); // DR interface ID. router_link. set_neighbour_interface_id( get_designated_router_interface_id()); // DR router ID. router_link.set_neighbour_router_id(get_designated_router()); // get_area_router()->add_router_link(get_peerid(), router_link); router_links.push_back(router_link); } } break; } } break; case OspfTypes::PointToMultiPoint: { list *>::iterator n; for(n = _neighbours.begin(); n != _neighbours.end(); n++) { if (Neighbour::Full == (*n)->get_state()) { router_link.set_type(RouterLink::p2p); router_link. set_neighbour_interface_id((*n)->get_hello_packet()-> get_interface_id()); router_link.set_neighbour_router_id((*n)->get_router_id()); router_links.push_back(router_link); } } } break; case OspfTypes::VirtualLink: // At the time of writing virtual links had only one neighbour. list *>::iterator n; for(n = _neighbours.begin(); n != _neighbours.end(); n++) { if (Neighbour::Full == (*n)->get_state()) { router_link.set_type(RouterLink::vlink); router_link. set_neighbour_interface_id((*n)->get_hello_packet()-> get_interface_id()); router_link.set_neighbour_router_id((*n)->get_router_id()); router_links.push_back(router_link); } } break; } } template void Peer::designated_router_changed(bool yes) { list routers; get_attached_routers(routers); if (routers.empty()) return; uint32_t network_mask = 0; uint32_t link_state_id = 0; switch(_ospf.get_version()) { case OspfTypes::V2: network_mask = get_network_mask(); link_state_id = get_candidate_id(); break; case OspfTypes::V3: link_state_id = get_interface_id(); break; } // Yipee we just became the DR. if (yes) { get_area_router()->generate_network_lsa(get_peerid(), link_state_id, routers, network_mask); } else { get_area_router()->withdraw_network_lsa(get_peerid(), link_state_id); } } template void Peer::adjacency_change(bool up) { XLOG_ASSERT(do_dr_or_bdr()); XLOG_ASSERT(is_DR()); list routers; uint32_t network_mask = 0; uint32_t link_state_id = 0; switch(_ospf.get_version()) { case OspfTypes::V2: network_mask = get_network_mask(); link_state_id = get_candidate_id(); break; case OspfTypes::V3: link_state_id = get_interface_id(); break; } get_attached_routers(routers); if (up) { if (1 == routers.size()) { get_area_router()->generate_network_lsa(get_peerid(), link_state_id, routers, network_mask); } else { get_area_router()->update_network_lsa(get_peerid(), link_state_id, routers, network_mask); } } else { if (routers.empty()) { get_area_router()->withdraw_network_lsa(get_peerid(), link_state_id); } else { get_area_router()->update_network_lsa(get_peerid(), link_state_id, routers, network_mask); } } } template bool Peer::get_neighbour_list(list& neighbours) const { typename list *>::const_iterator n; for(n = _neighbours.begin(); n != _neighbours.end(); n++) neighbours.push_back((*n)->get_neighbour_id()); return true; } template bool Peer::get_neighbour_info(OspfTypes::NeighbourID nid, NeighbourInfo& ninfo) const { typename list *>::const_iterator n; for(n = _neighbours.begin(); n != _neighbours.end(); n++) { if ((*n)->get_neighbour_id() == nid) { return (*n)->get_neighbour_info(ninfo); } } return false; } template set >& Peer::get_address_info() { return _address_info; } template bool Peer::get_attached_routers(list& routers) { typename list *>::const_iterator n; for(n = _neighbours.begin(); n != _neighbours.end(); n++) if (Neighbour::Full == (*n)->get_state()) { switch(_ospf.get_version()) { case OspfTypes::V2: routers.push_back(RouterInfo((*n)->get_router_id())); break; case OspfTypes::V3: routers.push_back(RouterInfo((*n)->get_router_id(), (*n)->get_hello_packet()-> get_interface_id())); break; } } return true; } template void Peer::tear_down_state() { stop_hello_timer(); stop_wait_timer(); } template void Peer::remove_neighbour_state() { typename list *>::iterator n = _neighbours.begin(); while (n != _neighbours.end()) { (*n)->event_kill_neighbour(); // The assumption here is that only a linktype of BROADCAST // can have been dynamically created. So its acceptable to // delete it. if (OspfTypes::BROADCAST == (*n)->get_linktype()) { delete (*n); _neighbours.erase(n++); } else { n++; } } _scheduled_events.clear(); } template string Peer::pp_interface_state(InterfaceState is) { switch(is) { case Peer::Down: return "Down"; case Peer::Loopback: return "Loopback"; case Peer::Waiting: return "Waiting"; case Peer::Point2Point: return "Point-to-point"; case Peer::DR_other: return "DR Other"; case Peer::Backup: return "Backup"; case Peer::DR: return "DR"; } XLOG_UNREACHABLE(); } template void Peer::router_id_changing() { // RFC 2328 Section 12.4.2. Network-LSAs // The router ID is about to change so flush out any Network-LSAs // originated by this router. if (Peer::DR == get_state()) { list routers; get_attached_routers(routers); if (routers.empty()) return; uint32_t link_state_id = 0; switch(_ospf.get_version()) { case OspfTypes::V2: link_state_id = get_candidate_id(); break; case OspfTypes::V3: link_state_id = get_interface_id(); break; } get_area_router()->withdraw_network_lsa(get_peerid(), link_state_id); } } template bool Peer::set_network_mask(uint32_t network_mask) { _hello_packet.set_network_mask(network_mask); return true; } template uint32_t Peer::get_network_mask() const { return _hello_packet.get_network_mask(); } template bool Peer::set_interface_id(uint32_t interface_id) { _hello_packet.set_interface_id(interface_id); return true; } template uint32_t Peer::get_interface_id() const { return _hello_packet.get_interface_id(); } template <> bool Peer::add_advertise_net(IPv4 /*addr*/, uint32_t /*prefix_length*/) { XLOG_FATAL("Only IPv6 not IPv4"); return true; } template <> bool Peer::add_advertise_net(IPv6 addr, uint32_t prefix_length) { XLOG_ASSERT(OspfTypes::VirtualLink != get_linktype()); LinkLsa *llsa = dynamic_cast(_link_lsa.get()); XLOG_ASSERT(llsa); if (addr.is_linklocal_unicast()) return false; IPv6Prefix prefix(_ospf.get_version()); prefix.set_network(IPNet(addr, prefix_length)); llsa->get_prefixes().push_back(prefix); // Add a host route that can be used if necessary to advertise a // virtual link endpoint. IPv6Prefix host_prefix(_ospf.get_version()); host_prefix.set_network(IPNet(addr, IPv6::ADDR_BITLEN)); host_prefix.set_la_bit(true); llsa->get_prefixes().push_back(host_prefix); return true; } template <> bool Peer::remove_all_nets() { XLOG_FATAL("Only IPv6 not IPv4"); return true; } template <> bool Peer::remove_all_nets() { XLOG_ASSERT(OspfTypes::VirtualLink != get_linktype()); LinkLsa *llsa = dynamic_cast(_link_lsa.get()); XLOG_ASSERT(llsa); llsa->get_prefixes().clear(); return true; } template <> bool Peer::update_nets() { XLOG_FATAL("Only IPv6 not IPv4"); return true; } template <> bool Peer::update_nets() { bool status = get_area_router()->update_link_lsa(get_peerid(), _link_lsa); if (do_dr_or_bdr() && is_DR()) get_area_router()->update_intra_area_prefix_lsa(get_peerid()); return status; } template bool Peer::set_hello_interval(uint16_t hello_interval) { _hello_packet.set_hello_interval(hello_interval); restart_hello_timer(); return true; } template bool Peer::set_options(uint32_t options) { _hello_packet.set_options(options); switch(_ospf.get_version()) { case OspfTypes::V2: break; case OspfTypes::V3: if (OspfTypes::VirtualLink != get_linktype()) { LinkLsa *llsa = dynamic_cast(_link_lsa.get()); XLOG_ASSERT(llsa); llsa->set_options(options); get_area_router()->update_link_lsa(get_peerid(), _link_lsa); } break; } return true; } template uint32_t Peer::get_options() const { return _hello_packet.get_options(); } template bool Peer::set_router_priority(uint8_t priority) { _hello_packet.set_router_priority(priority); switch(_ospf.get_version()) { case OspfTypes::V2: break; case OspfTypes::V3: if (OspfTypes::VirtualLink != get_linktype()) { LinkLsa *llsa = dynamic_cast(_link_lsa.get()); XLOG_ASSERT(llsa); llsa->set_rtr_priority(priority); get_area_router()->update_link_lsa(get_peerid(), _link_lsa); } break; } switch(get_state()) { case Down: case Loopback: case Waiting: case Point2Point: break; case DR_other: case Backup: case DR: compute_designated_router_and_backup_designated_router(); break; } return true; } template bool Peer::set_router_dead_interval(uint32_t router_dead_interval) { _hello_packet.set_router_dead_interval(router_dead_interval); return true; } template uint32_t Peer::get_router_dead_interval() const { return _hello_packet.get_router_dead_interval(); } template bool Peer::set_simple_authentication_key(const string& password, string& error_msg) { return get_auth_handler().set_simple_authentication_key(password, error_msg); } template bool Peer::delete_simple_authentication_key(string& error_msg) { return get_auth_handler().delete_simple_authentication_key(error_msg); } template bool Peer::set_md5_authentication_key(uint8_t key_id, const string& password, const TimeVal& start_timeval, const TimeVal& end_timeval, const TimeVal& max_time_drift, string& error_msg) { return get_auth_handler().set_md5_authentication_key(key_id, password, start_timeval, end_timeval, max_time_drift, error_msg); } template bool Peer::delete_md5_authentication_key(uint8_t key_id, string& error_msg) { return get_auth_handler().delete_md5_authentication_key(key_id, error_msg); } template bool Peer::set_passive(bool passive, bool host) { if (_passive == passive && _passive_host == host) return true; bool change_state = !(_passive == passive); _passive = passive; _passive_host = host; if (!_enabled) return true; if (change_state) { if (passive) { XLOG_WARNING("Peer, set_passive on nterface: %s passive: %i host: %i", get_if_name().c_str(), (int)(passive), (int)(host)); event_loop_ind(); } else { event_unloop_ind(); event_interface_up(); } } else { update_router_links(); } return true; } template bool Peer::get_passive() const { return _passive; } template bool Peer::set_rxmt_interval(uint32_t rxmt_interval) { _rxmt_interval = rxmt_interval; return true; } template uint32_t Peer::get_rxmt_interval() { return _rxmt_interval; } template void Peer::change_state(InterfaceState state) { InterfaceState previous_state = get_state(); set_state(state); if (previous_state == state) return; if (Peer::DR == state) designated_router_changed(true); if (Peer::DR == previous_state) designated_router_changed(false); bool was_dr_or_bdr = Peer::DR == previous_state || Peer::Backup == previous_state; bool is_dr_or_bdr = Peer::DR == state || Peer::Backup == state; if (is_dr_or_bdr != was_dr_or_bdr) { if (is_dr_or_bdr) { _peerout.join_multicast_group(A::OSPFIGP_DESIGNATED_ROUTERS()); } else { _peerout.leave_multicast_group(A::OSPFIGP_DESIGNATED_ROUTERS()); } } } template bool Peer::set_designated_router(OspfTypes::RouterID dr) { _hello_packet.set_designated_router(dr); return true; } template OspfTypes::RouterID Peer::get_designated_router() const { XLOG_ASSERT(do_dr_or_bdr()); return _hello_packet.get_designated_router(); } template bool Peer::set_backup_designated_router(OspfTypes::RouterID dr) { _hello_packet.set_backup_designated_router(dr); return true; } template OspfTypes::RouterID Peer::get_backup_designated_router() const { XLOG_ASSERT(do_dr_or_bdr()); return _hello_packet.get_backup_designated_router(); } /****************************************/ /** * Initialise the static variables that are used to generate unique * NeighbourIDs. */ template <> OspfTypes::NeighbourID Neighbour::_ticket = OspfTypes::ALLNEIGHBOURS + 1; template <> OspfTypes::NeighbourID Neighbour::_ticket = OspfTypes::ALLNEIGHBOURS + 1; template const char* Neighbour::pp_state(State ns) { switch(ns) { case Neighbour::Down: return "Down"; case Neighbour::Attempt: return "Attempt"; case Neighbour::Init: return "Init"; case Neighbour::TwoWay: return "TwoWay"; case Neighbour::ExStart: return "ExStart"; case Neighbour::Exchange: return "Exchange"; case Neighbour::Loading: return "Loading"; case Neighbour::Full: return "Full"; } XLOG_UNREACHABLE(); } /** * RFC 2328 Section 10.4. Whether to become adjacent */ template bool Neighbour::establish_adjacency_p() const { bool become_adjacent = false; switch(get_linktype()) { case OspfTypes::BROADCAST: case OspfTypes::NBMA: // The router itself is the Designated Router // The router itself is the Backup Designated Router // The neighboring router is the Designated Router // The neighboring router is the Backup Designated Router if (is_DR_or_BDR() || is_neighbour_DR_or_BDR()) become_adjacent = true; break; case OspfTypes::PointToPoint: case OspfTypes::PointToMultiPoint: case OspfTypes::VirtualLink: become_adjacent = true; break; } return become_adjacent; } template bool Neighbour::is_DR() const { XLOG_ASSERT(_peer.do_dr_or_bdr()); return _peer.is_DR(); } template bool Neighbour::is_BDR() const { XLOG_ASSERT(_peer.do_dr_or_bdr()); return _peer.is_BDR(); } template bool Neighbour::is_DR_or_BDR() const { XLOG_ASSERT(_peer.do_dr_or_bdr()); return _peer.is_DR_or_BDR(); } template bool Neighbour::is_neighbour_DR() const { XLOG_ASSERT(_peer.do_dr_or_bdr()); if (get_candidate_id() == _peer.get_designated_router()) return true; return false; } template bool Neighbour::is_neighbour_DR_or_BDR() const { XLOG_ASSERT(_peer.do_dr_or_bdr()); if (get_candidate_id() == _peer.get_designated_router()) return true; if (get_candidate_id() == _peer.get_backup_designated_router()) return true; return false; } template void Neighbour::start_inactivity_timer() { _inactivity_timer = _ospf.get_eventloop(). new_oneoff_after(TimeVal(_peer.get_router_dead_interval(), 0), callback(this, &Neighbour::event_inactivity_timer)); } template void Neighbour::stop_inactivity_timer() { _inactivity_timer.unschedule(); } /** * Simple wrapper for retransmission timers that simplified debugging. */ class RxmtWrapper { public: RxmtWrapper() {} RxmtWrapper(Neighbour::RxmtCallback rcb, const char *diagnostic) : _rcb(rcb), _diagnostic(diagnostic) {} RxmtWrapper(const RxmtWrapper& rhs) { _rcb = rhs._rcb; _diagnostic = rhs._diagnostic; } RxmtWrapper operator=(const RxmtWrapper& rhs) { if(&rhs == this) return *this; _rcb = rhs._rcb; _diagnostic = rhs._diagnostic; return *this; } bool doit() { debug_msg("retransmission: %s\n", str().c_str()); return _rcb->dispatch(); } string str() { return _diagnostic; } private: Neighbour::RxmtCallback _rcb; string _diagnostic; }; template void Neighbour::start_rxmt_timer(uint32_t index, RxmtCallback rcb, bool immediate, const char *comment) { XLOG_TRACE(_ospf.trace()._neighbour_events, "start_rxmt_timer: %p %s [%i] interval: %lims Neighbour: %s State: %s %s\n", this, _peer.get_if_name().c_str(), index, (long)(_peer.get_rxmt_interval() * 1000), pr_id(get_candidate_id()).c_str(), pp_state(get_state()), comment); XLOG_ASSERT(index < TIMERS); // Any outstanding timers should already have been cancelled. XLOG_ASSERT(0 == _rxmt_wrapper[index]); _rxmt_wrapper[index] = new RxmtWrapper(rcb, c_format("%s %s", _peer.get_if_name().c_str(), comment).c_str()); _rxmt_timer[index] = _ospf.get_eventloop(). new_periodic_ms(_peer.get_rxmt_interval() * 1000, callback(_rxmt_wrapper[index], &RxmtWrapper::doit)); // Send one immediately. Do this last so all state is set. if (immediate) rcb->dispatch(); } template void Neighbour::stop_rxmt_timer(uint32_t index, const char *comment) { XLOG_TRACE(_ospf.trace()._neighbour_events, "stop_rxmt_timer: %p %s index: %i Neighbour: %s State: %s %s\n", this, _peer.get_if_name().c_str(), (int)(index), pr_id(get_candidate_id()).c_str(), pp_state(get_state()), comment); debug_msg("stop_rxmt_timer: %p %s %s\n", this, _peer.get_if_name().c_str(), comment); XLOG_ASSERT(index < TIMERS); if (_rxmt_wrapper[index]) { debug_msg("\tstopping %s\n", cstring(*_rxmt_wrapper[index])); delete _rxmt_wrapper[index]; _rxmt_wrapper[index] = 0; } _rxmt_timer[index].unschedule(); } template void Neighbour::ensure_retransmitter_running(const char* message) { string msg(message); msg += ": ensure_retransmitter_running"; if (_rxmt_wrapper[FULL]) { // Timer is already running. We don't want to stop & restart because // then it will probably fire later than it should. So, just // return with no changes. return; } start_rxmt_timer(FULL, callback(this, &Neighbour::retransmitter), false, msg.c_str()); } template bool Neighbour::retransmitter() { // When there is nothing left to retransmit stop the timer bool more = false; XLOG_TRACE(_ospf.trace()._neighbour_events, "retransmitter, ls_req_list.size(): %i lsa_rxmit.size(): %i Interface(%s) Neighbour(%s) State(%s)", (int)(_ls_request_list.size()), (int)(_lsa_rxmt.size()), _peer.get_if_name().c_str(), pr_id(get_candidate_id()).c_str(), pp_state(get_state())); if (!_ls_request_list.empty()) { LinkStateRequestPacket lsrp(_ospf.get_version()); size_t lsr_len = 0; list::iterator i; for (i = _ls_request_list.begin(); i != _ls_request_list.end(); i++) { if (lsrp.get_standard_header_length() + Ls_request::length() + lsr_len < _peer.get_frame_size()) { lsr_len += Ls_request::length(); lsrp.get_ls_request(). push_back(Ls_request(i->get_version(), i->get_ls_type(), i->get_link_state_id(), i->get_advertising_router())); } else { send_link_state_request_packet(lsrp); lsrp.get_ls_request().clear(); lsr_len = 0; // RFC 2328 Section 10.9 // There should be at most one Link State Request packet // outstanding at any one time. break; } } if (!lsrp.get_ls_request().empty()) send_link_state_request_packet(lsrp); more = true; } if (!_lsa_rxmt.empty()) { TimeVal now; _ospf.get_eventloop().current_time(now); LinkStateUpdatePacket lsup(_ospf.get_version(), _ospf.get_lsa_decoder()); size_t lsas_len = 0; list::iterator i = _lsa_rxmt.begin(); while (i != _lsa_rxmt.end()) { if ((*i)->valid() && (*i)->exists_nack(_neighbourid)) { if (!(*i)->maxage()) (*i)->update_age(now); size_t len; (*i)->lsa(len); if (lsup.get_standard_header_length() + len + lsas_len < _peer.get_frame_size()) { lsas_len += len; lsup.get_lsas().push_back(*i); } else { XLOG_TRACE(_ospf.trace()._retransmit, "retransmit: %s %s", str().c_str(), cstring(lsup)); send_link_state_update_packet(lsup, true /* direct */); lsup.get_lsas().clear(); lsas_len = 0; } i++; } else { _lsa_rxmt.erase(i++); } } if (!lsup.get_lsas().empty()) { XLOG_TRACE(_ospf.trace()._retransmit, "retransmit: %s %s", str().c_str(), cstring(lsup)); send_link_state_update_packet(lsup, true /* direct */); } more = true; } return more; } template void Neighbour::build_data_description_packet() { // Clear out previous LSA headers. _data_description_packet.get_lsa_headers().clear(); if (_all_headers_sent) return; // Open the database if the handle is invalid. if (!_database_handle.valid()) { bool empty; _database_handle = get_area_router()->open_database(_peer.get_peerid(), empty); if (empty) goto out; } else { // Make sure there are LSAs left in the database. if (!get_area_router()->subsequent(_database_handle)) goto out; } bool last; do { Lsa::LsaRef lsa = get_area_router()-> get_entry_database(_database_handle, last); // Don't summarize AS-External-LSAs over virtual adjacencies. if (!(OspfTypes::VirtualLink == get_linktype() && lsa->external())) { _data_description_packet.get_lsa_headers(). push_back(lsa->get_header()); // XXX - We are testing to see if there is space left in the // packet by repeatedly encoding, this is very inefficient. We // should encode to find the base size of the packet and then // compare against the constant LSA header length each time. vector pkt; _data_description_packet.encode(pkt); if (pkt.size() + Lsa_header::length() >= _peer.get_frame_size()) return; } } while(last == false); out: // No more headers. _data_description_packet.set_m_bit(false); get_area_router()->close_database(_database_handle); _all_headers_sent = true; } template bool Neighbour::send_data_description_packet() { XLOG_TRACE(_ospf.trace()._neighbour_events, "send_data_description_packet, Interface(%s) Neighbour(%s) State(%s)", _peer.get_if_name().c_str(), pr_id(get_candidate_id()).c_str(), pp_state(get_state())); _peer.populate_common_header(_data_description_packet); switch(get_linktype()) { case OspfTypes::PointToPoint: case OspfTypes::BROADCAST: case OspfTypes::NBMA: case OspfTypes::PointToMultiPoint: _data_description_packet.set_interface_mtu(_peer.get_interface_mtu()); break; case OspfTypes::VirtualLink: _data_description_packet.set_interface_mtu(0); break; } _data_description_packet.set_options(_peer.get_options()); vector pkt; _data_description_packet.encode(pkt); get_auth_handler().generate(pkt); SimpleTransmit *transmit = 0; switch(get_linktype()) { case OspfTypes::PointToPoint: transmit = new SimpleTransmit(pkt, A::OSPFIGP_ROUTERS(), _peer.get_interface_address()); break; case OspfTypes::BROADCAST: case OspfTypes::NBMA: case OspfTypes::PointToMultiPoint: transmit = new SimpleTransmit(pkt, get_neighbour_address(), _peer.get_interface_address()); break; case OspfTypes::VirtualLink: transmit = new SimpleTransmit(pkt, get_neighbour_address(), _peer.get_interface_address()); break; } typename Transmit::TransmitRef tr(transmit); _peer.transmit(tr); return true; } /** * RFC 2328 Section 10.8. Sending Database Description Packets. (ExStart) */ template void Neighbour::start_sending_data_description_packets(const char *event_name, bool immediate) { XLOG_ASSERT(ExStart == get_state()); XLOG_TRACE(_ospf.trace()._neighbour_events, "start_sending_data_description_packets, Event(%s) Interface(%s) Neighbour(%s) " "State(%s)", event_name, _peer.get_if_name().c_str(), pr_id(get_candidate_id()).c_str(), pp_state(get_state())); // Clear out the request list. _ls_request_list.clear(); uint32_t seqno = _data_description_packet.get_dd_seqno(); _data_description_packet.set_dd_seqno(++seqno); _data_description_packet.set_i_bit(true); _data_description_packet.set_m_bit(true); _data_description_packet.set_ms_bit(true); _data_description_packet.get_lsa_headers().clear(); start_rxmt_timer(INITIAL, callback(this, &Neighbour::send_data_description_packet), immediate, c_format("send_data_description from %s", event_name).c_str()); } template bool Neighbour::send_link_state_request_packet(LinkStateRequestPacket& lsrp) { _peer.populate_common_header(lsrp); vector pkt; lsrp.encode(pkt); get_auth_handler().generate(pkt); SimpleTransmit *transmit = 0; switch(get_linktype()) { case OspfTypes::PointToPoint: transmit = new SimpleTransmit(pkt, A::OSPFIGP_ROUTERS(), _peer.get_interface_address()); break; case OspfTypes::BROADCAST: case OspfTypes::NBMA: case OspfTypes::PointToMultiPoint: case OspfTypes::VirtualLink: transmit = new SimpleTransmit(pkt, get_neighbour_address(), _peer.get_interface_address()); break; } typename Transmit::TransmitRef tr(transmit); _peer.transmit(tr); return true; } template bool Neighbour::send_link_state_update_packet(LinkStateUpdatePacket& lsup, bool direct) { _peer.populate_common_header(lsup); vector pkt; lsup.encode(pkt, _peer.get_inftransdelay()); get_auth_handler().generate(pkt); SimpleTransmit *transmit = 0; switch(get_linktype()) { case OspfTypes::PointToPoint: transmit = new SimpleTransmit(pkt, A::OSPFIGP_ROUTERS(), _peer.get_interface_address()); break; case OspfTypes::BROADCAST: { A dest; if (direct) { dest = get_neighbour_address(); } else { if (is_DR_or_BDR()) { dest = A::OSPFIGP_ROUTERS(); } else { dest = A::OSPFIGP_DESIGNATED_ROUTERS(); } } transmit = new SimpleTransmit(pkt, dest, _peer.get_interface_address()); } break; case OspfTypes::NBMA: case OspfTypes::PointToMultiPoint: case OspfTypes::VirtualLink: transmit = new SimpleTransmit(pkt, get_neighbour_address(), _peer.get_interface_address()); break; } typename Transmit::TransmitRef tr(transmit); _peer.transmit(tr); return true; } template bool Neighbour::send_link_state_ack_packet(LinkStateAcknowledgementPacket& lsap, bool direct, bool& multicast_on_peer) { _peer.populate_common_header(lsap); vector pkt; lsap.encode(pkt); get_auth_handler().generate(pkt); SimpleTransmit *transmit = 0; multicast_on_peer = false; switch(get_linktype()) { case OspfTypes::PointToPoint: transmit = new SimpleTransmit(pkt, A::OSPFIGP_ROUTERS(), _peer.get_interface_address()); break; case OspfTypes::BROADCAST: { A dest; if (direct) { dest = get_neighbour_address(); } else { multicast_on_peer = true; if (is_DR_or_BDR()) { dest = A::OSPFIGP_ROUTERS(); } else { dest = A::OSPFIGP_DESIGNATED_ROUTERS(); } } transmit = new SimpleTransmit(pkt, dest, _peer.get_interface_address()); } break; case OspfTypes::NBMA: case OspfTypes::PointToMultiPoint: case OspfTypes::VirtualLink: transmit = new SimpleTransmit(pkt, get_neighbour_address(), _peer.get_interface_address()); break; } typename Transmit::TransmitRef tr(transmit); _peer.transmit(tr); return true; } template void Neighbour::tear_down_state(State previous_state) { stop_inactivity_timer(); for (uint32_t i = 0; i < TIMERS; i++) stop_rxmt_timer(i, "Tear Down State"); _all_headers_sent = false; if (_database_handle.valid()) get_area_router()->close_database(_database_handle); _ls_request_list.clear(); // If this assertion ever occurs then the nack list on the LSAs on // the queue must be cleared. The top of push_lsas shows how this // can be done. If the current state is less than exchange just // calling push_lsas will do the trick. XLOG_ASSERT(_lsa_queue.empty()); list::iterator i = _lsa_rxmt.begin(); for (i = _lsa_rxmt.begin(); i != _lsa_rxmt.end(); i++) (*i)->remove_nack(_neighbourid); _lsa_rxmt.clear(); if (_peer.do_dr_or_bdr() && is_DR() && Full == previous_state) _peer.adjacency_change(false); if (TwoWay <= previous_state) { if (_peer.do_dr_or_bdr()) _peer.schedule_event("NeighbourChange"); else _peer.update_router_links(); } } /** * RFC 2328 Section 10.5 Receiving Hello Packets, neighbour component. */ template void Neighbour::event_hello_received(HelloPacket *hello) { XLOG_TRACE(_ospf.trace()._neighbour_events, "Event(HelloReceived) Interface(%s) Neighbour(%s) DR (%s) BDR (%s) " "State(%s)", _peer.get_if_name().c_str(), pr_id(get_candidate_id()).c_str(), pr_id(hello->get_designated_router()).c_str(), pr_id(hello->get_backup_designated_router()).c_str(), pp_state(get_state())); debug_msg("ID = %s interface state <%s> neighbour state <%s> %s\n", pr_id(get_candidate_id()).c_str(), Peer::pp_interface_state(_peer.get_state()).c_str(), pp_state(get_state()), cstring(*hello)); switch(get_state()) { case Down: // It is legal to delete 0! delete _hello_packet; _hello_packet = 0; change_state(Init); break; case Attempt: case Init: case TwoWay: case ExStart: case Exchange: case Loading: case Full: break; } bool first = 0 ==_hello_packet; uint8_t previous_router_priority = 0; OspfTypes::RouterID previous_dr = 0; OspfTypes::RouterID previous_bdr = 0; if (first) { XLOG_ASSERT(!_inactivity_timer.scheduled()); if (_peer.do_dr_or_bdr()) { previous_router_priority = hello->get_router_priority(); previous_dr = hello->get_designated_router(); previous_bdr = hello->get_backup_designated_router(); } } else { if (hello->get_router_id() != _hello_packet->get_router_id()) { XLOG_INFO("Router ID changed from %s to %s", pr_id(_hello_packet->get_router_id()).c_str(), pr_id(hello->get_router_id()).c_str()); } if (_peer.do_dr_or_bdr()) { previous_router_priority = _hello_packet->get_router_priority(); previous_dr = _hello_packet->get_designated_router(); previous_bdr = _hello_packet->get_backup_designated_router(); } delete _hello_packet; } _hello_packet = hello; start_inactivity_timer(); // Search for this router in the neighbour list. list li = hello->get_neighbours(); list::iterator i = li.begin(); for(; i != li.end(); i++) { if ((*i) == _ospf.get_router_id()) break; } if (i == li.end()) { // This neighbour has no knowledge of this router. event_1_way_received(); return; } event_2_way_received(); // Don't attempt to compute the DR or BDR if this is not BROADCAST or NBMA. if (!_peer.do_dr_or_bdr()) return; if (previous_router_priority != hello->get_router_priority()) _peer.schedule_event("NeighbourChange"); // Everything below here it trying to figure out if a // "NeighbourChange" event should be scheduled. Which is not // allowed in state Waiting. if (Peer::Waiting == _peer.get_state()) { if ((get_candidate_id() == hello->get_designated_router() && set_id("0.0.0.0") == hello->get_backup_designated_router()) || get_candidate_id() == hello->get_backup_designated_router()) { _peer.schedule_event("BackupSeen"); } } // If the neighbour is declaring itself the designated router and // it had not previously. if ((get_candidate_id() == hello->get_designated_router() && previous_dr != hello->get_designated_router())) { _peer.schedule_event("NeighbourChange"); } // If the neighbour is not declaring itself the designated router and // it had previously. if ((get_candidate_id() == previous_dr && previous_dr != hello->get_designated_router())) { _peer.schedule_event("NeighbourChange"); } // If the neighbour is declaring itself the designated backup router and // it had not previously. if ((get_candidate_id() == hello->get_backup_designated_router() && previous_bdr != hello->get_backup_designated_router())) { _peer.schedule_event("NeighbourChange"); } // If the neighbour is not declaring itself the designated backup // router and it had previously. if ((get_candidate_id() == previous_bdr && previous_bdr != hello->get_backup_designated_router())) { _peer.schedule_event("NeighbourChange"); } if (OspfTypes::NBMA == get_linktype()) XLOG_WARNING("TBD"); } template void Neighbour::event_1_way_received() { XLOG_TRACE(_ospf.trace()._neighbour_events, "Event(1-WayReceived) Interface(%s) Neighbour(%s) State(%s)", _peer.get_if_name().c_str(), pr_id(get_candidate_id()).c_str(), pp_state(get_state())); switch(get_state()) { case Down: case Attempt: XLOG_WARNING("Unexpected state %s", pp_state(get_state())); break; case Init: // No change break; case TwoWay: case ExStart: case Exchange: case Loading: case Full: change_state(Init); break; } } template void Neighbour::event_2_way_received() { const char *event_name = "2-WayReceived"; XLOG_TRACE(_ospf.trace()._neighbour_events, "Event(%s) Interface(%s) Neighbour(%s) State(%s)", event_name, _peer.get_if_name().c_str(), pr_id(get_candidate_id()).c_str(), pp_state(get_state())); switch(get_state()) { case Down: XLOG_WARNING("Unhandled state %s", pp_state(get_state())); break; case Attempt: XLOG_ASSERT(get_linktype() == OspfTypes::NBMA); break; case Init: if (establish_adjacency_p()) { change_state(ExStart); start_sending_data_description_packets(event_name); } else { change_state(TwoWay); } if (_peer.do_dr_or_bdr()) _peer.schedule_event("NeighbourChange"); break; case TwoWay: case ExStart: case Exchange: case Loading: case Full: // Cool nothing to do. break; } } /** * Save specific fields (not all) in Database Description Packets: * initialize(I), more(M), master(MS), options, DD sequence number. * Please turn this into "operator=". */ inline void assign(DataDescriptionPacket& lhs, const DataDescriptionPacket& rhs) { if (&lhs == &rhs) return; lhs.set_i_bit(rhs.get_i_bit()); lhs.set_m_bit(rhs.get_m_bit()); lhs.set_ms_bit(rhs.get_ms_bit()); lhs.set_options(rhs.get_options()); lhs.set_dd_seqno(rhs.get_dd_seqno()); } /** * Compare specific fields (not all) in Database Description Packets: * initialize(I), more(M), master(MS), options, DD sequence number. */ inline bool operator==(const DataDescriptionPacket& lhs, const DataDescriptionPacket& rhs) { if (&lhs == &rhs) return true; if (lhs.get_i_bit() != rhs.get_i_bit()) return false; if (lhs.get_m_bit() != rhs.get_m_bit()) return false; if (lhs.get_ms_bit() != rhs.get_ms_bit()) return false; if (lhs.get_options() != rhs.get_options()) return false; if (lhs.get_dd_seqno() != rhs.get_dd_seqno()) return false; return true; } /** * RFC 2328 Section 10.6 Receiving Database Description Packets, * neighbour component. */ template void Neighbour::data_description_received(DataDescriptionPacket *dd) { XLOG_TRACE(_ospf.trace()._neighbour_events, "Event(DataDescriptionReceived-pseudo-event) Interface(%s) Neighbour(%s) State(%s)", _peer.get_if_name().c_str(), pr_id(get_candidate_id()).c_str(), pp_state(get_state())); debug_msg("ID = %s interface state <%s> neighbour state <%s> %s\n", pr_id(get_candidate_id()).c_str(), Peer::pp_interface_state(_peer.get_state()).c_str(), pp_state(get_state()), cstring(*dd)); switch(get_state()) { case Down: // Reject Packet break; case Attempt: // Reject Packet break; case Init: event_2_way_received(); if (ExStart == get_state()) ;// FALLTHROUGH else break; case ExStart: { bool negotiation_done = false; // Save some fields for later duplicate detection. assign(_last_dd, *dd); _all_headers_sent = false; if (dd->get_i_bit() && dd->get_m_bit() && dd->get_ms_bit() && dd->get_lsa_headers().empty() && dd->get_router_id() > _ospf.get_router_id()) { // Router is slave // Use this sequence number to respond to the poll _data_description_packet.set_dd_seqno(dd->get_dd_seqno()); // This is the slave switch off the master slave bit. _data_description_packet.set_ms_bit(false); negotiation_done = true; } if (!dd->get_i_bit() && !dd->get_ms_bit() && _data_description_packet.get_dd_seqno() == dd->get_dd_seqno() && dd->get_router_id() < _ospf.get_router_id()) { // Router is master // Bump the sequence number of the master. _data_description_packet. set_dd_seqno(_data_description_packet.get_dd_seqno() + 1); if (!extract_lsa_headers(dd)) return; negotiation_done = true; } if (negotiation_done) { event_negotiation_done(); } } break; case TwoWay: // Ignore Packet break; case Exchange: { // Check for duplicates if (_last_dd == *dd) { if (_last_dd.get_ms_bit()) {// Router is slave send_data_description_packet(); } else { // Router is master // Discard } return; } // Make sure the saved value is the same as the incoming. if (_last_dd.get_ms_bit() != dd->get_ms_bit()) { XLOG_TRACE(_ospf.trace()._neighbour_events, "Neighbour(%s) sequence mismatch: " "MS expected %s got %s", pr_id(get_candidate_id()).c_str(), bool_c_str(_last_dd.get_ms_bit()), bool_c_str(dd->get_ms_bit())); event_sequence_number_mismatch(); break; } if (dd->get_i_bit()) { XLOG_TRACE(_ospf.trace()._neighbour_events, "Neighbour(%s) sequence mismatch: I-Bit set", pr_id(get_candidate_id()).c_str()); event_sequence_number_mismatch(); break; } if (dd->get_options() != _last_dd.get_options()) { XLOG_TRACE(_ospf.trace()._neighbour_events, "Neighbour(%s) sequence mismatch: (options)", pr_id(get_candidate_id()).c_str()); event_sequence_number_mismatch(); break; } bool in_sequence = false; if (!_data_description_packet.get_ms_bit()) { // Router is slave if (_data_description_packet.get_dd_seqno() + 1 == dd->get_dd_seqno()) in_sequence = true; } else { // Router is master if (_data_description_packet.get_dd_seqno() == dd->get_dd_seqno()) in_sequence = true; } if (!in_sequence) { XLOG_TRACE(_ospf.trace()._neighbour_events, "Neighbour(%s) sequence mismatch: Out of sequence", pr_id(get_candidate_id()).c_str()); event_sequence_number_mismatch(); break; } if (!extract_lsa_headers(dd)) return; if (!_data_description_packet.get_ms_bit()) { // Router is slave _data_description_packet.set_dd_seqno(dd->get_dd_seqno()); build_data_description_packet(); if (!_data_description_packet.get_m_bit() && !dd->get_m_bit()) { event_exchange_done(); } send_data_description_packet(); } else { // Router is master if (_all_headers_sent && !dd->get_m_bit()) { event_exchange_done(); } else { _data_description_packet. set_dd_seqno(_data_description_packet.get_dd_seqno() + 1); build_data_description_packet(); send_data_description_packet(); } } // Save some fields for later duplicate detection. assign(_last_dd, *dd); } break; case Loading: case Full: // Check for duplicates if (_last_dd == *dd) { if (_last_dd.get_ms_bit()) { // Router is slave send_data_description_packet(); } else { // Router is master // Discard } } else { event_sequence_number_mismatch(); } break; } } template bool Neighbour::extract_lsa_headers(DataDescriptionPacket *dd) { list li = dd->get_lsa_headers(); list::const_iterator i; for (i = li.begin(); i != li.end(); i++) { uint16_t ls_type = i->get_ls_type(); // Do we recognise this LS type? // If the LSA decoder knows about about the LSA then we // must be good. if (!_ospf.get_lsa_decoder().validate(ls_type)) { XLOG_TRACE(_ospf.trace()._input_errors, "Unknown LS type %u %s", ls_type, cstring(*dd)); event_sequence_number_mismatch(); return false; } // Deal with AS-external-LSA's (LS type = 5, 0x4005). switch(_peer.get_area_type()) { case OspfTypes::NORMAL: break; case OspfTypes::STUB: case OspfTypes::NSSA: if (_ospf.get_lsa_decoder().external(ls_type)) { XLOG_TRACE(_ospf.trace()._input_errors, "AS-external-LSA not allowed in %s area %s", pp_area_type(_peer.get_area_type()).c_str(), cstring(*dd)); event_sequence_number_mismatch(); return false; } break; } // Check to see if this is a newer LSA. if (get_area_router()->newer_lsa(*i)) _ls_request_list.push_back((*i)); } return true; } /** * RFC 2328 Section 10.7. Receiving Link State Request Packets */ template void Neighbour::link_state_request_received(LinkStateRequestPacket *lsrp) { XLOG_TRACE(_ospf.trace()._neighbour_events, "Event(LinkStateRequestReceived-pseudo-event) Interface(%s) Neighbour(%s) State(%s)", _peer.get_if_name().c_str(), pr_id(get_candidate_id()).c_str(), pp_state(get_state())); debug_msg("ID = %s interface state <%s> neighbour state <%s> %s\n", pr_id(get_candidate_id()).c_str(), Peer::pp_interface_state(_peer.get_state()).c_str(), pp_state(get_state()), cstring(*lsrp)); switch(get_state()) { case Down: case Attempt: case Init: case TwoWay: case ExStart: // Ignore return; case Exchange: case Loading: case Full: break; } list lsas; if (!get_area_router()->get_lsas(lsrp->get_ls_request(), lsas)) { event_bad_link_state_request(); return; } LinkStateUpdatePacket lsup(_ospf.get_version(), _ospf.get_lsa_decoder()); size_t lsas_len = 0; list::iterator i; for (i = lsas.begin(); i != lsas.end(); i++) { XLOG_ASSERT((*i)->valid()); size_t len; (*i)->lsa(len); (*i)->set_transmitted(true); if (lsup.get_standard_header_length() + len + lsas_len < _peer.get_frame_size()) { lsas_len += len; lsup.get_lsas().push_back(*i); } else { send_link_state_update_packet(lsup); lsup.get_lsas().clear(); lsas_len = 0; } } if (!lsup.get_lsas().empty()) send_link_state_update_packet(lsup); } template void Neighbour::link_state_update_received(LinkStateUpdatePacket *lsup) { XLOG_TRACE(_ospf.trace()._neighbour_events, "Event(LinkStateUpdateReceived-pseudo-event) Interface(%s) Neighbour(%s) State(%s)", _peer.get_if_name().c_str(), pr_id(get_candidate_id()).c_str(), pp_state(get_state())); switch(get_state()) { case Down: case Attempt: case Init: case TwoWay: case ExStart: // Ignore return; case Exchange: case Loading: case Full: break; } list direct_ack, delayed_ack; bool is_router_dr = false; bool is_router_bdr = false; bool is_neighbour_dr = false; if (_peer.do_dr_or_bdr()) { is_router_dr = is_DR(); is_router_bdr = is_BDR(); is_neighbour_dr = is_neighbour_DR(); } XLOG_TRACE(_ospf.trace()._neighbour_events, "isDR: %i isBDR: %i isNeighbourDR: %i lsa_rxmit sz: %i lsaup sz: %i\n", (int)(is_router_dr), (int)(is_router_bdr), (int)(is_neighbour_dr), (int)(_lsa_rxmt.size()), (int)(lsup->get_lsas().size())); get_area_router()-> receive_lsas(_peer.get_peerid(), _neighbourid, lsup->get_lsas(), direct_ack, delayed_ack, is_router_dr, is_router_bdr, is_neighbour_dr); // A more efficient way of sending the direct ack. // bool multicast_on_peer; // send_ack(direct_ack, /*direct*/true, multicast_on_peer); _peer.send_direct_acks(get_neighbour_id(), direct_ack); _peer.send_delayed_acks(get_neighbour_id(), delayed_ack); int iterations = 0; #ifndef MAX_AGE_IN_DATABASE // MaxAge LSAs are in the retransmission list with no connection // to the database. The LSAs can either be removed due to an ACK // or because of a new LSA. If an incoming LSA matches a MaxAge // LSA remove the MaxAge LSA. An incoming LSA can be rewritten and // placed on the retransmission list, leave these alone. // XLOG_TRACE(_ospf.trace()._neighbour_events, "MAX_AGE_IN_DATABASE is not defined.\n"); again: for (list::iterator i = _lsa_rxmt.begin(); i != _lsa_rxmt.end(); i++) { iterations++; if (!(*i)->maxage()) continue; if ((*i)->max_sequence_number()) continue; list& lsas = lsup->get_lsas(); list::const_iterator j; for (j = lsas.begin(); j != lsas.end(); j++) { // Possibly rewritten iterations++; if (*i == *j) { continue; } if ((*i).get()->get_header() == (*j).get()->get_header()) { //XLOG_INFO("Same LSA\n%s\n%s", cstring(*(*i)), cstring(*(*j))); _lsa_rxmt.erase(i); goto again; } } } #endif // Have any of the update packets satisfied outstanding requests? if (_ls_request_list.empty()) { XLOG_TRACE(_ospf.trace()._neighbour_events, "_ls_request_list is empty\n"); return; } list& lsas = lsup->get_lsas(); list::const_iterator i; list::iterator j; int iter2 = 0; for (i = lsas.begin(); i != lsas.end(); i++) { //XLOG_TRACE(_ospf.trace()._neighbour_events, "lsa: %s\n", (*i)->str().c_str()); for (j = _ls_request_list.begin(); j != _ls_request_list.end(); j++) { //XLOG_TRACE(_ospf.trace()._neighbour_events, "lsa-req: %s\n", j->str().c_str()); iter2++; if ((*j) == (*i)->get_header()) { XLOG_TRACE(_ospf.trace()._neighbour_events, "Header matched, erasing j\n"); _ls_request_list.erase(j); break; } } } if (_ls_request_list.empty()) event_loading_done(); XLOG_TRACE(_ospf.trace()._neighbour_events, "done w/link-state-ack-rcvd, iterations: %i iter2: %i\n", iterations, iter2); } template void Neighbour:: link_state_acknowledgement_received(LinkStateAcknowledgementPacket *lsap) { XLOG_TRACE(_ospf.trace()._neighbour_events, "Event(LinkStateAcknowledgementReceived-pseudo-event) Interface(%s) Neighbour(%s) State(%s)", _peer.get_if_name().c_str(), pr_id(get_candidate_id()).c_str(), pp_state(get_state())); debug_msg("ID = %s interface state <%s> neighbour state <%s> %s\n", pr_id(get_candidate_id()).c_str(), Peer::pp_interface_state(_peer.get_state()).c_str(), pp_state(get_state()), cstring(*lsap)); switch(get_state()) { case Down: case Attempt: case Init: case TwoWay: case ExStart: // Ignore return; case Exchange: case Loading: case Full: break; } // Find the LSA on the retransmission list. // 1) Remove the NACK state. // 2) Remove from the retransmisson list. list& headers = lsap->get_lsa_headers(); list::iterator i; list::iterator j; for (i = headers.begin(); i != headers.end(); i++) { // Neither the found or partial variable are required they // exist solely for monitoring. The double lookup is also // unnecessary. A call to compare_all_header_fields is all // that is required. //bool found = false; //bool partial = false; for (j = _lsa_rxmt.begin(); j != _lsa_rxmt.end(); j++) { if ((*i) == (*j)->get_header()) { //partial = true; if (compare_all_header_fields((*i),(*j)->get_header())) { //found = true; (*j)->remove_nack(get_neighbour_id()); _lsa_rxmt.erase(j); break; } } } // Its probably going to be a common occurence that an ACK // arrives for a LSA that has since been updated. A LSA that // we don't know about at all is more interesting. #if 0 if (!found && !partial) { XLOG_TRACE(_ospf.trace()._input_errors, "Ack for LSA not in retransmission list.\n%s\n%s", cstring(*i), cstring(*lsap)); // Print the retransmission list. list::iterator k; for (k = _lsa_rxmt.begin(); k != _lsa_rxmt.end(); k++) { XLOG_TRACE(_ospf.trace()._input_errors, "Retransmit entry %s", cstring(*(*k))); } } #endif } } template bool Neighbour::send_lsa(Lsa::LsaRef lsar) { LinkStateUpdatePacket lsup(_ospf.get_version(), _ospf.get_lsa_decoder()); lsup.get_lsas().push_back(lsar); _peer.populate_common_header(lsup); vector pkt; lsup.encode(pkt, _peer.get_inftransdelay()); get_auth_handler().generate(pkt); SimpleTransmit *transmit; transmit = new SimpleTransmit(pkt, get_neighbour_address(), _peer.get_interface_address()); typename Transmit::TransmitRef tr(transmit); _peer.transmit(tr); return true; } template bool Neighbour::queue_lsa(OspfTypes::PeerID peerid, OspfTypes::NeighbourID nid, Lsa::LsaRef lsar, bool& multicast_on_peer) { // RFC 2328 Section 13.3. Next step in the flooding procedure XLOG_TRACE(lsar->tracing(), "Attempting to queue %s", cstring(*lsar)); switch(_ospf.get_version()) { case OspfTypes::V2: break; case OspfTypes::V3: if (lsar->link_local_scope()) { if (lsar->get_peerid() != _peer.get_peerid()) { XLOG_TRACE(lsar->tracing(), "Not queued Link-local %s", cstring(*lsar)); return true; } } break; } // (1) switch(get_state()) { case Down: case Attempt: case Init: case TwoWay: case ExStart: // (a) Neighbour is in too low a state so return. XLOG_TRACE(lsar->tracing(), "Not queued state too low %s", cstring(*lsar)); return true; case Exchange: case Loading: { // (b) See if this LSA is on the link state request list. Lsa_header& lsah = lsar->get_header(); list::iterator i = find(_ls_request_list.begin(), _ls_request_list.end(), lsah); if (i != _ls_request_list.end()) { switch(get_area_router()->compare_lsa(lsah, *i)) { case AreaRouter::NOMATCH: XLOG_UNREACHABLE(); break; case AreaRouter::EQUIVALENT: _ls_request_list.erase(i); if (_ls_request_list.empty()) event_loading_done(); return true; break; case AreaRouter::NEWER: _ls_request_list.erase(i); if (_ls_request_list.empty()) event_loading_done(); break; case AreaRouter::OLDER: return true; break; } } } break; case Full: break; } // (c) If the neighbour IDs match then this is the neighbour that this // LSA was originally received on. if (_neighbourid == nid) { XLOG_TRACE(lsar->tracing(), "LSA came from this neighbour %s", cstring(*lsar)); return true; } #ifndef MAX_AGE_IN_DATABASE // Look for the LSA by tuple. In all but the case // described below it is sufficient to compare LSA pointers as // below to decide if two LSAs are equivalent. Principally because // all LSAs are in the database and two LSAs cannot exist in the // database with the same tuple. If an LSA reaches MaxAge and is // being withdrawn it is removed from the database. There is a // window where the old LSA exists only in the retransmission list // and a new LSA is inserted into the database and an attempt is // made to flood it. A common instance of this is that after a // crash a neighbour may send the router its own LSA which it // attempts to flush by setting the MaxAge and then moments later // it puts the same LSA in the database. // XXX // Once we are happy with this fix then combine this with the loop // below and do away with this define. list::iterator i; for (i = _lsa_rxmt.begin(); i != _lsa_rxmt.end(); i++) { if (lsar != (*i) && (*i).get()->get_header() == lsar.get()->get_header()) { // XLOG_ASSERT((*i)->maxage()); // XLOG_INFO("Same LSA\n%s\n%s", cstring(*(*i)), cstring(*lsar)); _lsa_rxmt.erase(i); break; } } #endif // (d) If this LSA isn't already on the retransmit queue add it. if (find(_lsa_rxmt.begin(), _lsa_rxmt.end(), lsar) == _lsa_rxmt.end()) _lsa_rxmt.push_back(lsar); // Add this neighbour ID to the set of unacknowledged neighbours. lsar->add_nack(_neighbourid); // (2) By now we should be thinking about sending this LSA out. // Did this LSA arrive on this peer (interface). if (_peer.get_peerid() == peerid ) { // (3) If this LSA arrived from the designated router or the // backup designated router. Chances are high that our // neighbours have received this LSA already. if (_peer.do_dr_or_bdr() && _peer.is_neighbour_DR_or_BDR(nid)) { XLOG_TRACE(lsar->tracing(), "Peers neighbour is DR or BDR %s", cstring(*lsar)); return true; } // (4) If this peer (interface) is in state Backup then out of // here. if (_peer.get_state() == Peer::Backup) { XLOG_TRACE(lsar->tracing(), "Peer state is backup%s", cstring(*lsar)); return true; } } // (5) This LSA should be flooded now. If it hasn't already been // multicast out of this peer/inteface. switch(get_linktype()) { case OspfTypes::BROADCAST: if (multicast_on_peer) { XLOG_TRACE(lsar->tracing(), "LSA has already been multicast %s", cstring(*lsar)); return true; } else multicast_on_peer = true; break; case OspfTypes::NBMA: case OspfTypes::PointToMultiPoint: case OspfTypes::VirtualLink: case OspfTypes::PointToPoint: break; } // Increment LSA's LS age by InfTransDelay performed in // send_link_state_update_packet. _lsa_queue.push_back(lsar); XLOG_TRACE(lsar->tracing(), "Queued successful %s", cstring(*lsar)); return true; } template bool Neighbour::push_lsas(const char* message) { // Typically push_lsas will be called immediately after one or // more calls to queue_lsa, it therefore shouldn't be possible for // the state to change. If the state was less than exchange then // queue_lsa shouldn't have queued anything. if (get_state() < Exchange) { list::iterator i; for (i = _lsa_queue.begin(); i != _lsa_queue.end(); i++) (*i)->remove_nack(_neighbourid); _lsa_queue.clear(); return true; } LinkStateUpdatePacket lsup(_ospf.get_version(), _ospf.get_lsa_decoder()); size_t lsas_len = 0; list::iterator i; for (i = _lsa_queue.begin(); i != _lsa_queue.end(); i++) { if ((*i)->valid() && (*i)->exists_nack(_neighbourid)) { size_t len; (*i)->lsa(len); (*i)->set_transmitted(true); if (lsup.get_standard_header_length() + len + lsas_len < _peer.get_frame_size()) { lsas_len += len; lsup.get_lsas().push_back(*i); } else { send_link_state_update_packet(lsup); lsup.get_lsas().clear(); lsas_len = 0; } } } if (!lsup.get_lsas().empty()) send_link_state_update_packet(lsup); // All the LSAs on the queue should now be scheduled for // sending. Zap the queue. _lsa_queue.clear(); ensure_retransmitter_running(message); return true; } template bool Neighbour::on_link_state_request_list(Lsa::LsaRef lsar) const { if (_ls_request_list.end() != find(_ls_request_list.begin(), _ls_request_list.end(), lsar->get_header())) return true; return false; } template bool Neighbour::send_ack(list& ack, bool direct, bool& multicast_on_peer) { switch(get_state()) { case Down: case Attempt: case Init: case TwoWay: case ExStart: multicast_on_peer = false; return false; case Exchange: case Loading: case Full: break; } LinkStateAcknowledgementPacket lsap(_ospf.get_version()); list& l = lsap.get_lsa_headers(); l.insert(l.begin(), ack.begin(), ack.end()); return send_link_state_ack_packet(lsap, direct, multicast_on_peer); } template void Neighbour::change_state(State state) { State previous_state = get_state(); set_state(state); XLOG_TRACE(_ospf.trace()._neighbour_events, "Neighbour: %s changing state: %s -> %s", _peer.get_if_name().c_str(), pp_state(previous_state), pp_state(state)); if (Full == state || Full == previous_state) _ospf.get_peer_manager(). adjacency_changed(_peer.get_peerid(), get_router_id(), Full == state); if (Full == state) _ospf.get_eventloop().current_time(_adjacency_time); // If we are dropping down states tear down any higher level state. if (previous_state > state) tear_down_state(previous_state); if (Down == state) get_auth_handler().reset(); } template void Neighbour::event_negotiation_done() { XLOG_TRACE(_ospf.trace()._neighbour_events, "Event(NegotiationDone) Interface(%s) Neighbour(%s) State(%s)", _peer.get_if_name().c_str(), pr_id(get_candidate_id()).c_str(), pp_state(get_state())); switch(get_state()) { case Down: break; case Attempt: break; case Init: break; case TwoWay: break; case ExStart: change_state(Exchange); // Inferred from the specification. _data_description_packet.set_i_bit(false); build_data_description_packet(); // If we are the master start sending description packets. if (!_last_dd.get_ms_bit()) { stop_rxmt_timer(INITIAL, "NegotiationDone (master)"); start_rxmt_timer(INITIAL, callback(this, &Neighbour:: send_data_description_packet), true, "send_data_description from NegotiationDone"); } else { // We have now agreed we are the slave so stop retransmitting. stop_rxmt_timer(INITIAL, "NegotiationDone (slave)"); // Send a response to the poll send_data_description_packet(); } break; case Exchange: break; case Loading: break; case Full: break; } } template void Neighbour::event_sequence_number_mismatch() { const char *event_name = "SequenceNumberMismatch"; event_SequenceNumberMismatch_or_BadLSReq(event_name); } template void Neighbour::event_exchange_done() { XLOG_TRACE(_ospf.trace()._neighbour_events, "Event(ExchangeDone) Interface(%s) Neighbour(%s) State(%s) ls-req-list-size: %i", _peer.get_if_name().c_str(), pr_id(get_candidate_id()).c_str(), pp_state(get_state()), (int)(_ls_request_list.size())); switch(get_state()) { case Down: break; case Attempt: break; case Init: break; case TwoWay: break; case ExStart: break; case Exchange: change_state(Loading); // Stop any retransmissions, only the master should have any running. if (!_last_dd.get_ms_bit()) stop_rxmt_timer(INITIAL, "ExchangeDone"); // The protocol allows link state request packets to be sent // before the database exchange has taken place. For the time // being wait until the exchange is over before sending // requests. Currently we only have a single retransmit timer // although this isn't really an issue. if (_ls_request_list.empty()) { event_loading_done(); return; } ensure_retransmitter_running("event_exchange_done, state Exchange"); debug_msg("link state request list count: %d\n", XORP_INT_CAST(_ls_request_list.size())); break; case Loading: break; case Full: break; } } template void Neighbour::event_bad_link_state_request() { const char *event_name = "BadLSReq"; event_SequenceNumberMismatch_or_BadLSReq(event_name); } template void Neighbour::event_loading_done() { XLOG_TRACE(_ospf.trace()._neighbour_events, "Event(LoadingDone) Interface(%s) Neighbour(%s) State(%s)", _peer.get_if_name().c_str(), pr_id(get_candidate_id()).c_str(), pp_state(get_state())); switch(get_state()) { case Down: break; case Attempt: break; case Init: break; case TwoWay: break; case ExStart: break; case Exchange: break; case Loading: change_state(Full); _peer.update_router_links(); if (_peer.do_dr_or_bdr() && is_DR()) _peer.adjacency_change(true); break; case Full: break; } } template void Neighbour::event_kill_neighbour() { XLOG_TRACE(_ospf.trace()._neighbour_events, "Event(KillNbr) Interface(%s) Neighbour(%s) State(%s)", _peer.get_if_name().c_str(), pr_id(get_candidate_id()).c_str(), pp_state(get_state())); switch(get_state()) { case Down: break; case Attempt: case Init: case TwoWay: case ExStart: case Exchange: case Loading: case Full: change_state(Down); break; } } template void Neighbour::event_adj_ok() { const char *event_name = "AdjOK?"; XLOG_TRACE(_ospf.trace()._neighbour_events, "Event(%s) Interface(%s) Neighbour(%s) State(%s)", event_name, _peer.get_if_name().c_str(), pr_id(get_candidate_id()).c_str(), pp_state(get_state())); switch(get_state()) { case Down: case Attempt: case Init: // Nothing break; case TwoWay: if (establish_adjacency_p()) { change_state(ExStart); start_sending_data_description_packets(event_name); } break; case ExStart: case Exchange: case Loading: case Full: if (!establish_adjacency_p()) change_state(TwoWay); break; } } template void Neighbour::event_inactivity_timer() { XLOG_TRACE(_ospf.trace()._neighbour_events, "Event(InactivityTimer) Interface(%s) Neighbour(%s) State(%s)", _peer.get_if_name().c_str(), pr_id(get_candidate_id()).c_str(), pp_state(get_state())); change_state(Down); // The saved hello packet is no longer required as it has timed // out. Use the presence of a hello packet to decide if this // neighbour should be included in this routers hello packets. delete _hello_packet; _hello_packet = 0; } template void Neighbour::event_SequenceNumberMismatch_or_BadLSReq(const char *event_name) { XLOG_TRACE(_ospf.trace()._neighbour_events, "Event(%s) Interface(%s) Neighbour(%s) State(%s)", event_name, _peer.get_if_name().c_str(), pr_id(get_candidate_id()).c_str(), pp_state(get_state())); switch(get_state()) { case Down: case Attempt: case Init: case TwoWay: case ExStart: XLOG_WARNING("Event %s in state %s not possible", event_name, pp_state(get_state())); break; case Exchange: case Loading: case Full: change_state(ExStart); // Don't send this packet immediately wait for the retransmit interval. start_sending_data_description_packets(event_name, false); break; } } template bool Neighbour::get_neighbour_info(NeighbourInfo& ninfo) const { uint32_t priority = 0; uint32_t options = 0; uint32_t dr = 0; uint32_t bdr = 0; if (_hello_packet) { priority = _hello_packet->get_router_priority(); options = _hello_packet->get_options(); dr = _hello_packet->get_designated_router(); bdr = _hello_packet->get_backup_designated_router(); } TimeVal remain; if (!_inactivity_timer.time_remaining(remain)) remain = TimeVal(0,0); ninfo._address = get_neighbour_address().str(); ninfo._interface = _peer.get_if_name(); ninfo._state = pp_state(get_state()); ninfo._rid = IPv4(htonl(get_router_id())); ninfo._priority = priority; ninfo._deadtime = remain.sec(); ninfo._area = IPv4(htonl(_peer.get_area_id())); ninfo._opt = options; ninfo._dr = IPv4(htonl(dr)); ninfo._bdr = IPv4(htonl(bdr)); TimeVal now, diff; _ospf.get_eventloop().current_time(now); diff = now - _creation_time; ninfo._up = diff.sec(); if (Full == get_state()) { diff = now - _adjacency_time; ninfo._adjacent = diff.sec(); } else { ninfo._adjacent = 0; } return true; } template class PeerOut; template class PeerOut; xorp/ospf/auth.hh0000664000076400007640000004602011540224231014105 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/ospf/auth.hh,v 1.16 2008/11/21 00:07:55 atanu Exp $ #ifndef __OSPF_AUTH_HH__ #define __OSPF_AUTH_HH__ #include class EventLoop; /** * @short Base clase for OSPFv2 authentication mechanisms. * * The AuthHandlerBase class defines the interfaces for OSPFv2 * authentication handlers. Handlers are responsible for * authenticating inbound datagrams and adding authentication data to * outbound datagrams. * * Error during authentication set an error buffer that clients may * query using the error() method. */ class AuthHandlerBase { public: virtual ~AuthHandlerBase(); /** * Get the effective name of the authentication scheme. * * @return the name of the authentication scheme. */ virtual const char* effective_name() const = 0; /** * Reset the authentication state. */ virtual void reset() = 0; /** * Additional bytes that will be added to the payload. * * @return the number of additional bytes that need to be added to * the payload. */ virtual uint32_t additional_payload() const = 0; /** * Inbound authentication method. * * @param packet the packet to verify. * @param src_addr the source address of the packet. * @param new_peer true if this is a new peer. * @return true if packet passes authentication checks, false otherwise. */ virtual bool authenticate_inbound(const vector& packet, const IPv4& src_addr, bool new_peer) = 0; /** * Outbound authentication method. * * @param packet the packet to authenticate. * @return true if packet was successfully authenticated, false when * no valid keys are present. */ virtual bool authenticate_outbound(vector& packet) = 0; /** * Get textual description of last error. */ const string& error() const; protected: /** * Reset textual description of last error. */ void reset_error(); /** * Set textual description of latest error. */ void set_error(const string& error_msg); private: string _error; }; /** * @short OSPFv2 Authentication handler when no authentication scheme is * employed. */ class NullAuthHandler : public AuthHandlerBase { public: static const OspfTypes::AuType AUTH_TYPE = OspfTypes::NULL_AUTHENTICATION; /** * Get the effective name of the authentication scheme. * * @return the name of the authentication scheme. */ const char* effective_name() const; /** * Get the method-specific name of the authentication scheme. * * @return the method-specific name of the authentication scheme. */ static const char* auth_type_name(); /** * Reset the authentication state. */ void reset(); /** * Additional bytes that will be added to the payload. * * @return the number of additional bytes that need to be added to * the payload. */ uint32_t additional_payload() const; /** * Inbound authentication method. * * @param packet the packet to verify. * @param src_addr the source address of the packet. * @param new_peer true if this is a new peer. * @return true if packet passes authentication checks, false otherwise. */ bool authenticate_inbound(const vector& packet, const IPv4& src_addr, bool new_peer); /** * Outbound authentication method. * * @param packet the packet to authenticate. * @return true if packet was successfully authenticated, false when * no valid keys are present. */ bool authenticate_outbound(vector& packet); }; /** * @short OSPFv2 Authentication handler for plaintext scheme. */ class PlaintextAuthHandler : public AuthHandlerBase { public: static const OspfTypes::AuType AUTH_TYPE = OspfTypes::SIMPLE_PASSWORD; /** * Get the effective name of the authentication scheme. * * @return the name of the authentication scheme. */ const char* effective_name() const; /** * Get the method-specific name of the authentication scheme. * * @return the method-specific name of the authentication scheme. */ static const char* auth_type_name(); /** * Reset the authentication state. */ void reset(); /** * Additional bytes that will be added to the payload. * * @return the number of additional bytes that need to be added to * the payload. */ uint32_t additional_payload() const; /** * Inbound authentication method. * * @param packet the packet to verify. * @param src_addr the source address of the packet. * @param new_peer true if this is a new peer. * @return true if packet passes authentication checks, false otherwise. */ bool authenticate_inbound(const vector& packet, const IPv4& src_addr, bool new_peer); /** * Outbound authentication method. * * @param packet the packet to authenticate. * @return true if packet was successfully authenticated, false when * no valid keys are present. */ bool authenticate_outbound(vector& packet); /** * Get the authentication key. * * @return the authentication key. */ const string& key() const; /** * Set the authentication key. * * @param plaintext_key the plain-text key. */ void set_key(const string& plaintext_key); private: string _key; uint8_t _key_data[Packet::AUTH_PAYLOAD_SIZE]; }; /** * @short OSPFv2 Authentication handler for MD5 scheme. * * Class to check inbound MD5 authenticated packets and add * authentication data to outbound OSPF packets. The OSPFv2 MD5 * authentication scheme is described in Section D.3 of RFC 2328. */ class MD5AuthHandler : public AuthHandlerBase { public: static const OspfTypes::AuType AUTH_TYPE = OspfTypes::CRYPTOGRAPHIC_AUTHENTICATION; /** * Class to hold MD5 key information. */ class MD5Key { public: /** * Construct an MD5 Key. * * @param key_id unique ID associated with key. * @param key phrase used for MD5 digest computation. * @param start_timeval start time when key becomes valid. * @param end_timeval end time when key becomes invalid. * @param max_time_drift the maximum time drift among all routers. * @param start_timer the timer to mark when the key becomes valid. * @param end_timer the timer to mark when the key becomes invalid. */ MD5Key(uint8_t key_id, const string& key, const TimeVal& start_timeval, const TimeVal& end_timeval, const TimeVal& max_time_drift, XorpTimer start_timer, XorpTimer end_timer); /** * Get the ID associated with the key. */ uint8_t id() const { return _id; } /** * Get pointer to key data. The data is of size KEY_BYTES. */ const char* key_data() const { return _key_data; } /** * Get the size of the key data in bytes. */ uint32_t key_data_bytes() const { return KEY_BYTES; } /** * Get key data as a string. */ string key() const; /** * Get the start time of the key. */ const TimeVal& start_timeval() const { return _start_timeval; } /** * Get the end time of the key. */ const TimeVal& end_timeval() const { return _end_timeval; } /** * Get the maximum time drift among all routers. */ const TimeVal& max_time_drift() const { return _max_time_drift; } /** * Get indication of whether key is persistent. */ bool is_persistent() const { return _is_persistent; } /** * Set the flag whether the key is persistent. * * @param v if true the key is persistent. */ void set_persistent(bool v) { _is_persistent = v; } /** * Get whether ID matches a particular value (convenient for STL * algorithms). */ bool id_matches(uint8_t o) const { return _id == o; } /** * Get key validity status of key at a particular time. * * @param when the time to test whether the key is valid. */ bool valid_at(const TimeVal& when) const; /** * Reset the key for all sources. */ void reset(); /** * Reset the key for a particular source. * * @param src_addr the source address. */ void reset(const IPv4& src_addr); /** * Indicate whether valid packets have been received from a source * with this key ID. * * @param src_addr the source address. * @return true if a packet has been received from the source, * otherwise false. */ bool packets_received(const IPv4& src_addr) const; /** * Get last received sequence number from a source. * * @param src_addr the source address. * @return last sequence number seen from the source. Value may be * garbage if no packets have been received (check first with * @ref packets_received()). */ uint32_t last_seqno_recv(const IPv4& src_addr) const; /** * Set last sequence number received from a source. This method * implicitly set packets received to true. * * @param src_addr the source address. * @param seqno the last sequence number received from the source. */ void set_last_seqno_recv(const IPv4& src_addr, uint32_t seqno); /** * Get next sequence number for outbound packets. The counter * is automatically updated with each call of this method. */ uint32_t next_seqno_out() { return _o_seqno++; } protected: static const uint32_t KEY_BYTES = 16; uint8_t _id; // Key ID char _key_data[KEY_BYTES]; // Key data TimeVal _start_timeval; // Start time of the key TimeVal _end_timeval; // End time of the key TimeVal _max_time_drift; // Max. time drift among all routers bool _is_persistent; // True if key is persistent map _pkts_recv; // True if packets received map _lr_seqno; // Last received seqno uint32_t _o_seqno; // Next outbound sequence number XorpTimer _start_timer; // Key start timer XorpTimer _stop_timer; // Key stop timer friend class MD5AuthHandler; }; typedef list KeyChain; public: /** * Constructor * * @param eventloop the EventLoop instance to used for time reference. */ MD5AuthHandler(EventLoop& eventloop); /** * Get the effective name of the authentication scheme. * * @return the name of the authentication scheme. */ const char* effective_name() const; /** * Get the method-specific name of the authentication scheme. * * @return the method-specific name of the authentication scheme. */ static const char* auth_type_name(); /** * Reset the authentication state. */ void reset(); /** * Additional bytes that will be added to the payload. * * @return the number of additional bytes that need to be added to * the payload. */ uint32_t additional_payload() const; /** * Inbound authentication method. * * @param packet the packet to verify. * @param src_addr the source address of the packet. * @param new_peer true if this is a new peer. * @return true if packet passes authentication checks, false otherwise. */ bool authenticate_inbound(const vector& packet, const IPv4& src_addr, bool new_peer); /** * Outbound authentication method. * * @param packet the packet to authenticate. * @return true if packet was successfully authenticated, false when * no valid keys are present. */ bool authenticate_outbound(vector& packet); /** * Add a key to the MD5 key chain. * * If the key already exists, it is updated with the new settings. * * @param key_id unique ID associated with key. * @param key phrase used for MD5 digest computation. * @param start_timeval start time when key becomes valid. * @param end_timeval end time when key becomes invalid. * @param max_time_drift the maximum time drift among all routers. * @param error_msg the error message (if error). * @return true on success, false if end time is less than start time * or key has already expired. */ bool add_key(uint8_t key_id, const string& key, const TimeVal& start_timeval, const TimeVal& end_timeval, const TimeVal& max_time_drift, string& error_msg); /** * Remove a key from the MD5 key chain. * * @param key_id unique ID of key to be removed. * @param error_msg the error message (if error). * @return true if the key was found and removed, otherwise false. */ bool remove_key(uint8_t key_id, string& error_msg); /** * A callback that a key from the MD5 key chain has become valid. * * @param key_id unique ID of the key that has become valid. */ void key_start_cb(uint8_t key_id); /** * A callback that a key from the MD5 key chain has expired and is invalid. * * @param key_id unique ID of the key that has expired. */ void key_stop_cb(uint8_t key_id); /** * Select the best key for outbound messages. * * The chosen key is the one with most recent start-time in the past. * If there is more than one key that matches the criteria, then select * the key with greatest ID. * * @param now current time. */ MD5Key* best_outbound_key(const TimeVal& now); /** * Reset the keys for all sources. */ void reset_keys(); /** * Get all valid keys managed by the MD5AuthHandler. * * @return list of all valid keys. */ const KeyChain& valid_key_chain() const { return _valid_key_chain; } /** * Get all invalid keys managed by the MD5AuthHandler. * * @return list of all invalid keys. */ const KeyChain& invalid_key_chain() const { return _invalid_key_chain; } /** * Test where the MD5AuthHandler contains any keys. * * @return if the MD5AuthHandler contains any keys, otherwise false. */ bool empty() const; protected: EventLoop& _eventloop; // The event loop KeyChain _valid_key_chain; // The set of all valid keys KeyChain _invalid_key_chain; // The set of all invalid keys NullAuthHandler _null_handler; // Null handler if no valid keys }; /** * This is the class that should be instantiated to access * authentication. */ class Auth { public: Auth(EventLoop& eventloop) : _eventloop(eventloop), _auth_handler(NULL) { set_method("none"); } ~Auth() { if (_auth_handler != NULL) { delete _auth_handler; _auth_handler = NULL; } } bool set_method(const string& method) { if (_auth_handler != NULL) { delete _auth_handler; _auth_handler = NULL; } if ("none" == method) { _auth_handler = new NullAuthHandler; return true; } if ("simple" == method) { _auth_handler = new PlaintextAuthHandler; return true; } if ("md5" == method) { _auth_handler = new MD5AuthHandler(_eventloop); return true; } // Never allow _auth to be zero. set_method("none"); return false; } /** * Apply the authentication scheme to the packet. */ void generate(vector& pkt) { XLOG_ASSERT(_auth_handler != NULL); _auth_handler->authenticate_outbound(pkt); } /** * Verify that this packet has passed the authentication scheme. */ bool verify(vector& pkt, const IPv4& src_addr, bool new_peer) { XLOG_ASSERT(_auth_handler != NULL); return _auth_handler->authenticate_inbound(pkt, src_addr, new_peer); } bool verify(vector& pkt, const IPv6& src_addr, bool new_peer) { UNUSED(pkt); UNUSED(src_addr); UNUSED(new_peer); return true; } /** * Additional bytes that will be added to the payload. */ uint32_t additional_payload() const { XLOG_ASSERT(_auth_handler != NULL); return _auth_handler->additional_payload(); } const string& error() const { XLOG_ASSERT(_auth_handler != NULL); return _auth_handler->error(); } /** * Called to notify authentication system to reset. */ void reset() { XLOG_ASSERT(_auth_handler != NULL); _auth_handler->reset(); } /** * Set a simple password authentication key. * * Note that the current authentication handler is replaced with * a simple password authentication handler. * * @param password the password to set. * @param the error message (if error). * @return true on success, otherwise false. */ bool set_simple_authentication_key(const string& password, string& error_msg); /** * Delete a simple password authentication key. * * Note that after the deletion the simple password authentication * handler is replaced with a Null authentication handler. * * @param the error message (if error). * @return true on success, otherwise false. */ bool delete_simple_authentication_key(string& error_msg); /** * Set an MD5 authentication key. * * Note that the current authentication handler is replaced with * an MD5 authentication handler. * * @param key_id unique ID associated with key. * @param password phrase used for MD5 digest computation. * @param start_timeval start time when key becomes valid. * @param end_timeval end time when key becomes invalid. * @param max_time_drift the maximum time drift among all routers. * @param the error message (if error). * @return true on success, otherwise false. */ bool set_md5_authentication_key(uint8_t key_id, const string& password, const TimeVal& start_timeval, const TimeVal& end_timeval, const TimeVal& max_time_drift, string& error_msg); /** * Delete an MD5 authentication key. * * Note that after the deletion if there are no more valid MD5 keys, * the MD5 authentication handler is replaced with a Null authentication * handler. * * @param key_id the ID of the key to delete. * @param the error message (if error). * @return true on success, otherwise false. */ bool delete_md5_authentication_key(uint8_t key_id, string& error_msg); private: EventLoop& _eventloop; // The event loop AuthHandlerBase* _auth_handler; // The authentication handler }; #endif // __OSPF_AUTH_HH__ xorp/ospf/trace.hh0000664000076400007640000000423111421137511014242 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/ospf/trace.hh,v 1.21 2008/10/02 21:57:50 bms Exp $ #ifndef __OSPF_TRACE_HH__ #define __OSPF_TRACE_HH__ /** * All the trace variables in one place. */ struct Trace { Trace() : _input_errors(true), _interface_events(false), _neighbour_events(false), _spt(false), _import_policy(false), _export_policy(false), _virtual_link(false), _find_interface_address(false), _routes(false), _retransmit(false), _election(false), _packets(false) // Don't forget to add new variables to the all() method. {} /* * Set all flags */ void all(bool val) { _input_errors = _interface_events = _neighbour_events = _spt = _import_policy = _export_policy = _virtual_link = _find_interface_address = _routes = _retransmit = _election = _packets = val; } bool _input_errors; bool _interface_events; bool _neighbour_events; bool _spt; /* Shortest Path Tree */ bool _import_policy; bool _export_policy; bool _virtual_link; bool _find_interface_address; bool _routes; // add,replace,delete route. bool _retransmit; bool _election; // DR and BDR election. bool _packets; // Incoming and outgoing packets. }; #endif // __OSPF_TRACE_HH__ xorp/ospf/ospf.cc0000664000076400007640000005153011643705557014126 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #include "ospf_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/callback.hh" #include "libxorp/ipv4.hh" #include "libxorp/ipv6.hh" #include "libxorp/ipnet.hh" #include "libxorp/status_codes.h" #include "libxorp/service.hh" #include "libxorp/eventloop.hh" #include "ospf.hh" template Ospf::Ospf(OspfTypes::Version version, EventLoop& eventloop, IO* io) : _version(version), _eventloop(eventloop), _testing(false), _io(io), _reason("Waiting for IO"), _process_status(PROC_STARTUP), _lsa_decoder(version), _peer_manager(*this), _routing_table(*this), _instance_id(0), _router_id(0), _rfc1583_compatibility(false) { // Register the LSAs and packets with the associated decoder. initialise_lsa_decoder(version, _lsa_decoder); initialise_packet_decoder(version, _packet_decoder, _lsa_decoder); // Now that all the packet decoders are in place register for // receiving packets. _io->register_receive(callback(this,&Ospf::receive)); // The peer manager will solicit packets from the various interfaces. // Make sure that this value can never be allocated as an interface ID. _iidmap[""] = OspfTypes::UNUSED_INTERFACE_ID; } /** * All packets for OSPF are received through this interface. All good * packets are sent to the peer manager which verifies that the packet * is expected and authenticates the packet if necessary. The peer * manager can choose to accept the packet in which case it becomes * the owner. If the packet is rejected this routine will delete the * packet. */ template void Ospf::receive(const string& interface, const string& vif, A dst, A src, uint8_t* data, uint32_t len) { XLOG_TRACE(trace()._packets, "Ospf::received packet, Interface %s Vif %s dst %s src %s data %p len %u\n", interface.c_str(), vif.c_str(), dst.str().c_str(), src.str().c_str(), data, len); Packet *packet; try { // If the transport is IPv6 then the checksum verification has // to include the pseudo header. In the IPv4 case this // function is a noop. ipv6_checksum_verify(src, dst, data, len, Packet::CHECKSUM_OFFSET, _io->get_ip_protocol_number()); packet = _packet_decoder.decode(data, len); } catch(InvalidPacket& e) { XLOG_ERROR("%s", cstring(e)); return; } XLOG_TRACE(trace()._packets, "%s\n", cstring(*packet)); // We have a packet and its good. bool packet_accepted = false; try { packet_accepted = _peer_manager.receive(interface, vif, dst, src, packet); } catch(BadPeer& e) { XLOG_ERROR("%s", cstring(e)); } if (!packet_accepted) delete packet; } template bool Ospf::enable_interface_vif(const string& interface, const string& vif) { XLOG_TRACE(trace()._packets, "Enable Interface %s Vif %s\n", interface.c_str(), vif.c_str()); if (string(VLINK) == interface) return true; return _io->enable_interface_vif(interface, vif); } template bool Ospf::disable_interface_vif(const string& interface, const string& vif) { XLOG_WARNING("Disable Interface %s Vif %s\n", interface.c_str(), vif.c_str()); if (string(VLINK) == interface) return true; return _io->disable_interface_vif(interface, vif); } template bool Ospf::enabled(const string& interface, const string& vif) { debug_msg("Interface %s Vif %s\n", interface.c_str(), vif.c_str()); return _io->is_vif_enabled(interface, vif); } template bool Ospf::enabled(const string& interface, const string& vif, A address) { debug_msg("Interface %s Vif %s Address %s\n", interface.c_str(), vif.c_str(), cstring(address)); return _io->is_address_enabled(interface, vif, address); } template bool Ospf::get_addresses(const string& interface, const string& vif, list& addresses) const { debug_msg("Interface %s Vif %s\n", interface.c_str(), vif.c_str()); return _io->get_addresses(interface, vif, addresses); } template bool Ospf::get_link_local_address(const string& interface, const string& vif, A& address) { debug_msg("Interface %s Vif %s\n", interface.c_str(), vif.c_str()); return _io->get_link_local_address(interface, vif, address); } template bool Ospf::get_interface_id(const string& interface, const string& vif, uint32_t& interface_id) { debug_msg("Interface %s Vif %s\n", interface.c_str(), vif.c_str()); string concat = interface + "/" + vif; if (0 == _iidmap.count(concat)) { if (string(VLINK) == interface) interface_id = 100000; else _io->get_interface_id(interface, interface_id); bool match; do { match = false; typename map::iterator i; for(i = _iidmap.begin(); i != _iidmap.end(); i++) if ((*i).second == interface_id) { interface_id++; match = true; break; } } while(match); _iidmap[concat] = interface_id; } interface_id = _iidmap[concat]; XLOG_ASSERT(OspfTypes::UNUSED_INTERFACE_ID != interface_id); debug_msg("Interface %s Vif %s ID = %u\n", interface.c_str(), vif.c_str(), interface_id); _io->set_interface_mapping(interface_id, interface, vif); return true; } template bool Ospf::get_interface_vif_by_interface_id(uint32_t interface_id, string& interface, string& vif) { typename map::iterator i; for(i = _iidmap.begin(); i != _iidmap.end(); i++) { if ((*i).second == interface_id) { string concat = (*i).first; interface = concat.substr(0, concat.find('/')); vif = concat.substr(concat.find('/') + 1, concat.size() - 1); // fprintf(stderr, "interface <%s> vif <%s>\n", interface.c_str(), // vif.c_str()); return true; } } return false; } template bool Ospf::get_prefix_length(const string& interface, const string& vif, A address, uint16_t& prefix_length) { debug_msg("Interface %s Vif %s Address %s\n", interface.c_str(), vif.c_str(), cstring(address)); if (string(VLINK) == interface) { prefix_length = 0; return true; } prefix_length = _io->get_prefix_length(interface, vif, address); return 0 == prefix_length ? false : true; } template uint32_t Ospf::get_mtu(const string& interface) { debug_msg("Interface %s\n", interface.c_str()); if (string(VLINK) == interface) return VLINK_MTU; return _io->get_mtu(interface); } template bool Ospf::join_multicast_group(const string& interface, const string& vif, A mcast) { debug_msg("Interface %s Vif %s mcast %s\n", interface.c_str(), vif.c_str(), cstring(mcast)); return _io->join_multicast_group(interface, vif, mcast); } template bool Ospf::leave_multicast_group(const string& interface, const string& vif, A mcast) { debug_msg("Interface %s Vif %s mcast %s\n", interface.c_str(), vif.c_str(), cstring(mcast)); return _io->leave_multicast_group(interface, vif, mcast); } template bool Ospf::transmit(const string& interface, const string& vif, A dst, A src, int ttl, uint8_t* data, uint32_t len) { XLOG_TRACE(trace()._packets, "Interface %s Vif %s ttl %d data %p len %u\n", interface.c_str(), vif.c_str(), ttl, data, len); debug_msg("Interface %s Vif %s ttl %d data %p len %u\n", interface.c_str(), vif.c_str(), ttl, data, len); // If the transport is IPv6 then the checksum has to include the // pseudo header. In the IPv4 case this function is a noop. ipv6_checksum_apply(src, dst, data, len, Packet::CHECKSUM_OFFSET, _io->get_ip_protocol_number()); if (trace()._packets) { try { // Decode the packet in order to pretty print it. Packet *packet = _packet_decoder.decode(data, len); XLOG_TRACE(trace()._packets, "Transmit: %s\n", cstring(*packet)); delete packet; } catch(InvalidPacket& e) { XLOG_TRACE(trace()._packets, "Unable to decode packet\n"); } } #ifdef DEBUG_LOGGING try { // Decode the packet in order to pretty print it. Packet *packet = _packet_decoder.decode(data, len); debug_msg("Transmit: %s\n", cstring(*packet)); delete packet; } catch(InvalidPacket& e) { debug_msg("Unable to decode packet\n"); } #endif return _io->send(interface, vif, dst, src, ttl, data, len); } template bool Ospf::set_hello_interval(const string& interface, const string& vif, OspfTypes::AreaID area, uint16_t hello_interval) { try { _peer_manager.set_hello_interval(_peer_manager. get_peerid(interface, vif), area, hello_interval); } catch(BadPeer& e) { XLOG_ERROR("%s", cstring(e)); return false; } return true; } #if 0 template bool Ospf::set_options(const string& interface, const string& vif, OspfTypes::AreaID area, uint32_t options) { try { _peer_manager.set_options(_peer_manager.get_peerid(interface, vif), area, options); } catch(BadPeer& e) { XLOG_ERROR("%s", cstring(e)); return false; } return true; } #endif template bool Ospf::create_virtual_link(OspfTypes::RouterID rid) { try { _peer_manager.create_virtual_link(rid); } catch(BadPeer& e) { XLOG_ERROR("%s", cstring(e)); return false; } return true; } template bool Ospf::delete_virtual_link(OspfTypes::RouterID rid) { try { _peer_manager.delete_virtual_link(rid); } catch(BadPeer& e) { XLOG_ERROR("%s", cstring(e)); return false; } return true; } template bool Ospf::transit_area_virtual_link(OspfTypes::RouterID rid, OspfTypes::AreaID transit_area) { try { _peer_manager.transit_area_virtual_link(rid, transit_area); } catch(BadPeer& e) { XLOG_ERROR("%s", cstring(e)); return false; } return true; } template bool Ospf::set_router_priority(const string& interface, const string& vif, OspfTypes::AreaID area, uint8_t priority) { try { _peer_manager.set_router_priority(_peer_manager. get_peerid(interface, vif), area, priority); } catch(BadPeer& e) { XLOG_ERROR("%s", cstring(e)); return false; } return true; } template bool Ospf::set_router_dead_interval(const string& interface, const string& vif, OspfTypes::AreaID area, uint32_t router_dead_interval) { try { _peer_manager.set_router_dead_interval(_peer_manager. get_peerid(interface,vif), area, router_dead_interval); } catch(BadPeer& e) { XLOG_ERROR("%s", cstring(e)); return false; } return true; } template bool Ospf::set_interface_cost(const string& interface, const string& vif, OspfTypes::AreaID area, uint16_t interface_cost) { try { _peer_manager.set_interface_cost(_peer_manager. get_peerid(interface,vif), area, interface_cost); } catch(BadPeer& e) { XLOG_ERROR("%s", cstring(e)); return false; } return true; } template bool Ospf::set_retransmit_interval(const string& interface, const string& vif, OspfTypes::AreaID area, uint16_t retransmit_interval) { if (0 == retransmit_interval) { XLOG_ERROR("Zero is not a legal value for RxmtInterval"); return false; } try { _peer_manager.set_retransmit_interval(_peer_manager. get_peerid(interface,vif), area, retransmit_interval); } catch(BadPeer& e) { XLOG_ERROR("%s", cstring(e)); return false; } return true; } template bool Ospf::set_inftransdelay(const string& interface, const string& vif, OspfTypes::AreaID area, uint16_t inftransdelay) { if (0 == inftransdelay) { XLOG_ERROR("Zero is not a legal value for inftransdelay"); return false; } try { _peer_manager.set_inftransdelay(_peer_manager. get_peerid(interface,vif), area, inftransdelay); } catch(BadPeer& e) { XLOG_ERROR("%s", cstring(e)); return false; } return true; } template bool Ospf::set_simple_authentication_key(const string& interface, const string& vif, OspfTypes::AreaID area, const string& password, string& error_msg) { OspfTypes::PeerID peerid; try { peerid = _peer_manager.get_peerid(interface, vif); } catch(BadPeer& e) { error_msg = e.str(); XLOG_ERROR("%s", error_msg.c_str()); return false; } if (_peer_manager.set_simple_authentication_key(peerid, area, password, error_msg) != true) { XLOG_ERROR("%s", error_msg.c_str()); return false; } return true; } template bool Ospf::delete_simple_authentication_key(const string& interface, const string& vif, OspfTypes::AreaID area, string& error_msg) { OspfTypes::PeerID peerid; try { peerid = _peer_manager.get_peerid(interface, vif); } catch(BadPeer& e) { error_msg = e.str(); XLOG_ERROR("%s", error_msg.c_str()); return false; } if (_peer_manager.delete_simple_authentication_key(peerid, area, error_msg) != true) { XLOG_ERROR("%s", error_msg.c_str()); return false; } return true; } template bool Ospf::set_md5_authentication_key(const string& interface, const string& vif, OspfTypes::AreaID area, uint8_t key_id, const string& password, const TimeVal& start_timeval, const TimeVal& end_timeval, const TimeVal& max_time_drift, string& error_msg) { OspfTypes::PeerID peerid; try { peerid = _peer_manager.get_peerid(interface, vif); } catch(BadPeer& e) { error_msg = e.str(); XLOG_ERROR("%s", error_msg.c_str()); return false; } if (_peer_manager.set_md5_authentication_key(peerid, area, key_id, password, start_timeval, end_timeval, max_time_drift, error_msg) != true) { XLOG_ERROR("%s", error_msg.c_str()); return false; } return true; } template bool Ospf::delete_md5_authentication_key(const string& interface, const string& vif, OspfTypes::AreaID area, uint8_t key_id, string& error_msg) { OspfTypes::PeerID peerid; try { peerid = _peer_manager.get_peerid(interface, vif); } catch(BadPeer& e) { error_msg = e.str(); XLOG_ERROR("%s", error_msg.c_str()); return false; } if (_peer_manager.delete_md5_authentication_key(peerid, area, key_id, error_msg) != true) { XLOG_ERROR("%s", error_msg.c_str()); return false; } return true; } template bool Ospf::set_passive(const string& interface, const string& vif, OspfTypes::AreaID area, bool passive, bool host) { try { _peer_manager.set_passive(_peer_manager. get_peerid(interface,vif), area, passive, host); } catch(BadPeer& e) { XLOG_ERROR("%s", cstring(e)); return false; } return true; } template bool Ospf::originate_default_route(OspfTypes::AreaID area, bool enable) { debug_msg("Area %s enable %s\n", pr_id(area).c_str(), bool_c_str(enable)); return _peer_manager.originate_default_route(area, enable); } template bool Ospf::stub_default_cost(OspfTypes::AreaID area, uint32_t cost) { debug_msg("Area %s cost %u\n", pr_id(area).c_str(), cost); return _peer_manager.stub_default_cost(area, cost); } template bool Ospf::summaries(OspfTypes::AreaID area, bool enable) { debug_msg("Area %s enable %s\n", pr_id(area).c_str(), bool_c_str(enable)); return _peer_manager.summaries(area, enable); } template bool Ospf::set_ip_router_alert(bool alert) { return _io->set_ip_router_alert(alert); } template bool Ospf::area_range_add(OspfTypes::AreaID area, IPNet net, bool advertise) { debug_msg("Area %s Net %s advertise %s\n", pr_id(area).c_str(), cstring(net), bool_c_str(advertise)); return _peer_manager.area_range_add(area, net, advertise); } template bool Ospf::area_range_delete(OspfTypes::AreaID area, IPNet net) { debug_msg("Area %s Net %s\n", pr_id(area).c_str(), cstring(net)); return _peer_manager.area_range_delete(area, net); } template bool Ospf::area_range_change_state(OspfTypes::AreaID area, IPNet net, bool advertise) { debug_msg("Area %s Net %s advertise %s\n", pr_id(area).c_str(), cstring(net), bool_c_str(advertise)); return _peer_manager.area_range_change_state(area, net, advertise); } template bool Ospf::get_lsa(const OspfTypes::AreaID area, const uint32_t index, bool& valid, bool& toohigh, bool& self, vector& lsa) { debug_msg("Area %s index %u\n", pr_id(area).c_str(), index); return _peer_manager.get_lsa(area, index, valid, toohigh, self, lsa); } template bool Ospf::get_area_list(list& areas) const { debug_msg("\n"); return _peer_manager.get_area_list(areas); } template bool Ospf::get_neighbour_list(list& neighbours) const { debug_msg("\n"); return _peer_manager.get_neighbour_list(neighbours); } template bool Ospf::get_neighbour_info(OspfTypes::NeighbourID nid, NeighbourInfo& ninfo) const { return _peer_manager.get_neighbour_info(nid, ninfo); } template bool Ospf::clear_database() { return _peer_manager.clear_database(); } template bool Ospf::add_route(IPNet net, A nexthop, uint32_t nexthop_id, uint32_t metric, bool equal, bool discard, const PolicyTags& policytags) { debug_msg("Net %s Nexthop %s metric %d equal %s discard %s policy %s\n", cstring(net), cstring(nexthop), metric, bool_c_str(equal), bool_c_str(discard), cstring(policytags)); XLOG_TRACE(trace()._routes, "Add route " "Net %s Nexthop %s metric %d equal %s discard %s policy %s\n", cstring(net), cstring(nexthop), metric, bool_c_str(equal), bool_c_str(discard), cstring(policytags)); return _io->add_route(net, nexthop, nexthop_id, metric, equal, discard, policytags); } template bool Ospf::replace_route(IPNet net, A nexthop, uint32_t nexthop_id, uint32_t metric, bool equal, bool discard, const PolicyTags& policytags) { debug_msg("Net %s Nexthop %s metric %d equal %s discard %s policy %s\n", cstring(net), cstring(nexthop), metric, bool_c_str(equal), bool_c_str(discard), cstring(policytags)); XLOG_TRACE(trace()._routes, "Replace route " "Net %s Nexthop %s metric %d equal %s discard %s policy %s\n", cstring(net), cstring(nexthop), metric, bool_c_str(equal), bool_c_str(discard), cstring(policytags)); return _io->replace_route(net, nexthop, nexthop_id, metric, equal, discard, policytags); } template bool Ospf::delete_route(IPNet net) { debug_msg("Net %s\n", cstring(net)); XLOG_TRACE(trace()._routes, "Delete route Net %s\n", cstring(net)); return _io->delete_route(net); } template void Ospf::configure_filter(const uint32_t& filter, const string& conf) { _policy_filters.configure(filter,conf); } template void Ospf::reset_filter(const uint32_t& filter) { _policy_filters.reset(filter); } template void Ospf::push_routes() { _peer_manager.external_push_routes(); _routing_table.push_routes(); } template bool Ospf::originate_route(const IPNet& net, const A& nexthop, const uint32_t& metric, const PolicyTags& policytags) { return _peer_manager.external_announce(net, nexthop, metric, policytags); } template bool Ospf::withdraw_route(const IPNet& net) { return _peer_manager.external_withdraw(net); } template void Ospf::set_router_id(OspfTypes::RouterID id) { _peer_manager.router_id_changing(); _router_id = id; } template class Ospf; template class Ospf; xorp/ospf/policy_varrw.cc0000664000076400007640000001074311540224231015655 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #include "ospf_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/ipv4.hh" #include "libxorp/ipv6.hh" #include "libxorp/ipnet.hh" #include "libxorp/status_codes.h" #include "libxorp/service.hh" #include "libxorp/eventloop.hh" #include "libproto/spt.hh" #include "ospf.hh" #include "policy_varrw.hh" template OspfVarRW::OspfVarRW(IPNet& network, A& nexthop, uint32_t& metric, bool& e_bit, uint32_t& tag, bool& tag_set, PolicyTags& policytags) : _network(network), _nexthop(nexthop), _metric(metric), _e_bit(e_bit), _tag(tag), _tag_set(tag_set), _policytags(policytags) { } template <> void OspfVarRW::start_read() { initialize(VAR_NETWORK, _ef.create(ElemIPv4Net::id, _network.str().c_str())); initialize(VAR_NEXTHOP, _ef.create(ElemIPv4NextHop::id, _nexthop.str().c_str())); start_read_common(); } template <> void OspfVarRW::start_read() { initialize(VAR_NETWORK, _ef.create(ElemIPv6Net::id, _network.str().c_str())); initialize(VAR_NEXTHOP, _ef.create(ElemIPv6NextHop::id, _nexthop.str().c_str())); start_read_common(); } template void OspfVarRW::start_read_common() { initialize(VAR_POLICYTAGS, _policytags.element()); initialize(VAR_METRIC, _ef.create(ElemU32::id, c_format("%u", _metric).c_str())); initialize(VAR_EBIT, _ef.create(ElemU32::id, c_format("%u", _e_bit ? 2 : 1).c_str())); // XXX which tag wins? Element* element = _policytags.element_tag(); ElemU32* e = dynamic_cast(element); if (e != NULL && e->val()) _tag = e->val(); delete element; initialize(VAR_TAG, _ef.create(ElemU32::id, c_format("%u", _tag).c_str())); } template Element* OspfVarRW::single_read(const Id& /*id*/) { XLOG_UNREACHABLE(); return 0; } template <> void OspfVarRW::single_write(const Id& id, const Element& e) { switch (id) { case VAR_NETWORK: { const ElemIPv4Net* eip = dynamic_cast(&e); XLOG_ASSERT(eip != NULL); _network = IPNet(eip->val()); } break; case VAR_NEXTHOP: { const ElemIPv4NextHop* eip = dynamic_cast(&e); XLOG_ASSERT(eip != NULL); _nexthop = IPv4(eip->val()); } break; default: single_write_common(id, e); break; } } template void OspfVarRW::single_write_common(const Id& id, const Element& e) { switch (id) { case VAR_POLICYTAGS: _policytags.set_ptags(e); break; case VAR_METRIC: { const ElemU32& u32 = dynamic_cast(e); _metric = u32.val(); } break; case VAR_EBIT: { const ElemU32& b = dynamic_cast(e); _e_bit = b.val() == 2 ? true : false; } break; case VAR_TAG: { const ElemU32& u32 = dynamic_cast(e); _tag = u32.val(); _policytags.set_tag(e); } break; default: XLOG_WARNING("Unexpected Id %d %s", id, cstring(e)); } } template <> void OspfVarRW::single_write(const Id& id, const Element& e) { switch (id) { case VAR_NETWORK: { const ElemIPv6Net* eip = dynamic_cast(&e); XLOG_ASSERT(eip != NULL); _network = IPNet(eip->val()); } break; case VAR_NEXTHOP: { const ElemIPv6NextHop* eip = dynamic_cast(&e); XLOG_ASSERT(eip != NULL); _nexthop = IPv6(eip->val()); } break; default: single_write_common(id, e); break; } } template class OspfVarRW; template class OspfVarRW; xorp/ospf/xrl_target3.cc0000664000076400007640000006157411540225531015407 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #include "ospf_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/status_codes.h" #include "libxorp/service.hh" #include "libxorp/eventloop.hh" #include "libxorp/ipv4.hh" #include "libxorp/ipv6.hh" #include "libxipc/xrl_std_router.hh" #include "ospf.hh" #include "xrl_io.hh" #include "xrl_target3.hh" XrlOspfV3Target::XrlOspfV3Target(XrlRouter *r, /*Ospf& ospf_ipv4, */ Ospf& ospf_ipv6, /*XrlIO& io_ipv4,*/ XrlIO& io_ipv6) : XrlOspfv3TargetBase(r), /*_ospf_ipv4(ospf_ipv4),*/ _ospf_ipv6(ospf_ipv6), /*_xrl_io_ipv4(io_ipv4),*/ _xrl_io_ipv6(io_ipv6) { } XrlCmdError XrlOspfV3Target::common_0_1_get_target_name(string& name) { name = "ospfv3"; return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV3Target::common_0_1_get_version(string& version) { version = "0.1"; return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV3Target::common_0_1_get_status(uint32_t& status, string& reason) { status = _ospf_ipv6.status(reason); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV3Target::common_0_1_startup() { // Starts by default...nothing to do here. return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV3Target::common_0_1_shutdown() { _ospf_ipv6.shutdown(); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV3Target::raw_packet4_client_0_1_recv( // Input values, const string& /*if_name*/, const string& /*vif_name*/, const IPv4& /*src_address*/, const IPv4& /*dst_address*/, const uint32_t& /*ip_protocol*/, const int32_t& /*ip_ttl*/, const int32_t& /*ip_tos*/, const bool& /*ip_router_alert*/, const bool& /*ip_internet_control*/, const vector& /*payload*/) { #if 0 _xrl_io_ipv4.recv(if_name, vif_name, src_address, dst_address, ip_protocol, ip_ttl, ip_tos, ip_router_alert, ip_internet_control, payload); #else XLOG_UNFINISHED(); #endif return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV3Target::raw_packet6_client_0_1_recv( // Input values, const string& if_name, const string& vif_name, const IPv6& src_address, const IPv6& dst_address, const uint32_t& ip_protocol, const int32_t& ip_ttl, const int32_t& ip_tos, const bool& ip_router_alert, const bool& ip_internet_control, const XrlAtomList& ext_headers_type, const XrlAtomList& ext_headers_payload, const vector& payload) { UNUSED(ext_headers_type); UNUSED(ext_headers_payload); _xrl_io_ipv6.recv(if_name, vif_name, src_address, dst_address, ip_protocol, ip_ttl, ip_tos, ip_router_alert, ip_internet_control, payload); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV3Target::policy_backend_0_1_configure(const uint32_t& filter, const string& conf) { debug_msg("policy filter: %u conf: %s\n", filter, conf.c_str()); try { _ospf_ipv6.configure_filter(filter,conf); } catch(const PolicyException& e) { return XrlCmdError::COMMAND_FAILED("Filter configure failed: " + e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV3Target::policy_backend_0_1_reset(const uint32_t& filter) { debug_msg("policy filter reset: %u\n", filter); try { _ospf_ipv6.reset_filter(filter); } catch(const PolicyException& e){ return XrlCmdError::COMMAND_FAILED("Filter reset failed: " + e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV3Target::policy_backend_0_1_push_routes() { debug_msg("policy route push\n"); _ospf_ipv6.push_routes(); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV3Target::policy_redist6_0_1_add_route6(const IPv6Net& network, const bool& unicast, const bool& multicast, const IPv6& nexthop, const uint32_t& metric, const XrlAtomList& policytags) { debug_msg("Net: %s Nexthop: %s Unicast: %s Multicast %s metric %d\n", cstring(network), cstring(nexthop), bool_c_str(unicast), bool_c_str(multicast), metric); if (!unicast) return XrlCmdError::OKAY(); if (!_ospf_ipv6.originate_route(network, nexthop, metric, policytags)) { return XrlCmdError::COMMAND_FAILED("Network: " + network.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV3Target::policy_redist6_0_1_delete_route6(const IPv6Net& network, const bool& unicast, const bool& multicast) { debug_msg("Net: %s Unicast: %s Multicast %s\n", cstring(network), bool_c_str(unicast), bool_c_str(multicast)); if (!unicast) return XrlCmdError::OKAY(); if (!_ospf_ipv6.withdraw_route(network)) { return XrlCmdError::COMMAND_FAILED("Network: " + network.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV3Target::ospfv3_0_1_set_instance_id(const uint32_t& id) { _ospf_ipv6.set_instance_id(id); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV3Target::ospfv3_0_1_set_router_id(const IPv4& id) { OspfTypes::RouterID rid = ntohl(id.addr()); _ospf_ipv6.set_router_id(rid); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV3Target::ospfv3_0_1_set_ip_router_alert(const bool& ip_router_alert) { if (!_ospf_ipv6.set_ip_router_alert(ip_router_alert)) return XrlCmdError::COMMAND_FAILED("Failed to set IP router alert"); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV3Target::ospfv3_0_1_create_area_router(const IPv4& a, const string& type) { bool status; OspfTypes::AreaType t = from_string_to_area_type(type, status); if (!status) return XrlCmdError::COMMAND_FAILED("Unrecognised type " + type); OspfTypes::AreaID area = ntohl(a.addr()); if (!_ospf_ipv6.get_peer_manager().create_area_router(area, t)) return XrlCmdError::COMMAND_FAILED("Failed to create area " + pr_id(area)); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV3Target::ospfv3_0_1_change_area_router_type(const IPv4& a, const string& type) { bool status; OspfTypes::AreaType t = from_string_to_area_type(type, status); if (!status) return XrlCmdError::COMMAND_FAILED("Unrecognised type " + type); OspfTypes::AreaID area = ntohl(a.addr()); if (!_ospf_ipv6.get_peer_manager().change_area_router_type(area, t)) return XrlCmdError::COMMAND_FAILED("Failed to create area " + pr_id(area)); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV3Target::ospfv3_0_1_destroy_area_router(const IPv4& a) { OspfTypes::AreaID area = ntohl(a.addr()); if (!_ospf_ipv6.get_peer_manager().destroy_area_router(area)) return XrlCmdError::COMMAND_FAILED("Failed to destroy area " + pr_id(area)); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV3Target::ospfv3_0_1_create_peer(const string& ifname, const string& vifname, const string& type, const IPv4& a) { OspfTypes::AreaID area = ntohl(a.addr()); bool status; OspfTypes::LinkType linktype = from_string_to_link_type(type, status); if (!status) return XrlCmdError::COMMAND_FAILED("Unrecognised type " + type); try { _ospf_ipv6.get_peer_manager().create_peer(ifname, vifname, IPv6::ZERO(), linktype, area); } catch(XorpException& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV3Target::ospfv3_0_1_delete_peer(const string& ifname, const string& vifname) { debug_msg("interface %s vif %s\n", ifname.c_str(), vifname.c_str()); OspfTypes::PeerID peerid; try { peerid = _ospf_ipv6.get_peer_manager().get_peerid(ifname, vifname); } catch(XorpException& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } if (!_ospf_ipv6.get_peer_manager().delete_peer(peerid)) return XrlCmdError::COMMAND_FAILED("Failed to delete peer"); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV3Target::ospfv3_0_1_set_peer_state(const string& ifname, const string& vifname, const bool& enable) { debug_msg("interface %s vif %s enable %s\n", ifname.c_str(), vifname.c_str(), bool_c_str(enable)); OspfTypes::PeerID peerid; try { peerid = _ospf_ipv6.get_peer_manager().get_peerid(ifname, vifname); } catch(XorpException& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } if (!_ospf_ipv6.get_peer_manager().set_state_peer(peerid, enable)) return XrlCmdError::COMMAND_FAILED("Failed to set peer state"); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV3Target::ospfv3_0_1_add_address_peer(const string& ifname, const string& vifname, const IPv4& a, const IPv6& addr) { OspfTypes::AreaID area = ntohl(a.addr()); debug_msg("interface %s vif %s area %s address %s\n", ifname.c_str(), vifname.c_str(), pr_id(area).c_str(), cstring(addr)); if (!_ospf_ipv6.get_peer_manager().add_address_peer(ifname, vifname, area, addr)) return XrlCmdError::COMMAND_FAILED("Failed to add address"); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV3Target::ospfv3_0_1_remove_address_peer(const string& ifname, const string& vifname, const IPv4& a, const IPv6& addr) { OspfTypes::AreaID area = ntohl(a.addr()); debug_msg("interface %s vif %s area %s address %s\n", ifname.c_str(), vifname.c_str(), pr_id(area).c_str(), cstring(addr)); OspfTypes::PeerID peerid; try { peerid = _ospf_ipv6.get_peer_manager().get_peerid(ifname, vifname); } catch(XorpException& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } if (!_ospf_ipv6.get_peer_manager().remove_address_peer(peerid, area, addr)) return XrlCmdError::COMMAND_FAILED("Failed to remove address"); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV3Target::ospfv3_0_1_set_address_state_peer(const string& ifname, const string& vifname, const IPv4& a, const IPv6& addr, const bool& enable) { OspfTypes::AreaID area = ntohl(a.addr()); debug_msg("interface %s vif %s area %s address %s enable %s\n", ifname.c_str(), vifname.c_str(), pr_id(area).c_str(), cstring(addr), bool_c_str(enable)); OspfTypes::PeerID peerid; try { peerid = _ospf_ipv6.get_peer_manager().get_peerid(ifname, vifname); } catch(XorpException& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } if (!_ospf_ipv6.get_peer_manager(). set_address_state_peer(peerid, area, addr, enable)) return XrlCmdError::COMMAND_FAILED("Failed to set address state"); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV3Target::ospfv3_0_1_activate_peer(const string& ifname, const string& vifname, const IPv4& a) { OspfTypes::AreaID area = ntohl(a.addr()); debug_msg("interface %s vif %s area %s\n", ifname.c_str(), vifname.c_str(), pr_id(area).c_str()); if (!_ospf_ipv6.get_peer_manager().activate_peer(ifname, vifname, area)) return XrlCmdError::COMMAND_FAILED("Failed to activate peer"); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV3Target::ospfv3_0_1_update_peer(const string& ifname, const string& vifname, const IPv4& a) { OspfTypes::AreaID area = ntohl(a.addr()); debug_msg("interface %s vif %s area %s\n", ifname.c_str(), vifname.c_str(), pr_id(area).c_str()); if (!_ospf_ipv6.get_peer_manager().update_peer(ifname, vifname, area)) return XrlCmdError::COMMAND_FAILED("Failed to update peer"); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV3Target::ospfv3_0_1_add_neighbour(const string& ifname, const string& vifname, const IPv4& addr, const IPv6& neighbour_address, const IPv4& neighbour_id) { OspfTypes::AreaID area = ntohl(addr.addr()); OspfTypes::RouterID rid = ntohl(neighbour_id.addr()); debug_msg("interface %s vif %s area %s address %s id %s\n", ifname.c_str(), vifname.c_str(), pr_id(area).c_str(), cstring(neighbour_address),pr_id(rid).c_str()); OspfTypes::PeerID peerid; try { peerid = _ospf_ipv6.get_peer_manager().get_peerid(ifname, vifname); } catch(XorpException& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } if (!_ospf_ipv6.get_peer_manager().add_neighbour(peerid, area, neighbour_address, rid)) return XrlCmdError::COMMAND_FAILED("Failed to add neighbour " + neighbour_address.str()); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV3Target::ospfv3_0_1_remove_neighbour(const string& ifname, const string& vifname, const IPv4& addr, const IPv6& neighbour_address, const IPv4& neighbour_id) { OspfTypes::AreaID area = ntohl(addr.addr()); OspfTypes::RouterID rid = ntohl(neighbour_id.addr()); debug_msg("interface %s vif %s area %s address %s id %s\n", ifname.c_str(), vifname.c_str(), pr_id(area).c_str(), cstring(neighbour_address),pr_id(rid).c_str()); OspfTypes::PeerID peerid; try { peerid = _ospf_ipv6.get_peer_manager().get_peerid(ifname, vifname); } catch(XorpException& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } if (!_ospf_ipv6.get_peer_manager().remove_neighbour(peerid, area, neighbour_address, rid)) return XrlCmdError::COMMAND_FAILED("Failed to remove neighbour" + neighbour_address.str()); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV3Target::ospfv3_0_1_create_virtual_link(const IPv4& neighbour_id, const IPv4& a) { OspfTypes::AreaID area = ntohl(a.addr()); OspfTypes::RouterID rid = ntohl(neighbour_id.addr()); debug_msg("Neighbour's router ID %s configuration area %s\n", pr_id(rid).c_str(), pr_id(area).c_str()); if (OspfTypes::BACKBONE != area) { return XrlCmdError:: COMMAND_FAILED(c_format("Virtual link must be in area %s", pr_id(OspfTypes::BACKBONE).c_str())); } if (!_ospf_ipv6.create_virtual_link(rid)) return XrlCmdError::COMMAND_FAILED("Failed to create virtual link"); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV3Target::ospfv3_0_1_delete_virtual_link(const IPv4& neighbour_id) { OspfTypes::RouterID rid = ntohl(neighbour_id.addr()); if (!_ospf_ipv6.delete_virtual_link(rid)) return XrlCmdError::COMMAND_FAILED("Failed to delete virtual link"); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV3Target::ospfv3_0_1_transit_area_virtual_link(const IPv4& neighbour_id, const IPv4& transit_area) { OspfTypes::RouterID rid = ntohl(neighbour_id.addr()); OspfTypes::AreaID area = ntohl(transit_area.addr()); if (!_ospf_ipv6.transit_area_virtual_link(rid, area)) return XrlCmdError::COMMAND_FAILED("Failed to configure transit area"); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV3Target::ospfv3_0_1_set_interface_cost(const string& ifname, const string& vifname, const IPv4& a, const uint32_t& cost) { OspfTypes::AreaID area = ntohl(a.addr()); debug_msg("interface %s vif %s area %s cost %d\n", ifname.c_str(), vifname.c_str(), pr_id(area).c_str(), cost); if (!_ospf_ipv6.set_interface_cost(ifname, vifname, area, cost)) return XrlCmdError::COMMAND_FAILED("Failed to set " "interface cost"); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV3Target::ospfv3_0_1_set_retransmit_interval(const string& ifname, const string& vifname, const IPv4& a, const uint32_t& interval) { OspfTypes::AreaID area = ntohl(a.addr()); debug_msg("interface %s vif %s area %s interval %d\n", ifname.c_str(), vifname.c_str(), pr_id(area).c_str(), interval); if (!_ospf_ipv6.set_retransmit_interval(ifname, vifname, area, interval)) return XrlCmdError::COMMAND_FAILED("Failed to set " "RxmtInterval interval"); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV3Target::ospfv3_0_1_set_inftransdelay(const string& ifname, const string& vifname, const IPv4& a, const uint32_t& delay) { OspfTypes::AreaID area = ntohl(a.addr()); debug_msg("interface %s vif %s area %s delay %d\n", ifname.c_str(), vifname.c_str(), pr_id(area).c_str(), delay); if (!_ospf_ipv6.set_inftransdelay(ifname, vifname, area, delay)) return XrlCmdError::COMMAND_FAILED("Failed to set " "inftransdelay delay"); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV3Target::ospfv3_0_1_set_router_priority(const string& ifname, const string& vifname, const IPv4& a, const uint32_t& priority) { OspfTypes::AreaID area = ntohl(a.addr()); debug_msg("interface %s vif %s area %s priority %d\n", ifname.c_str(), vifname.c_str(), pr_id(area).c_str(), priority); if (!_ospf_ipv6.set_router_priority(ifname, vifname, area, priority)) return XrlCmdError::COMMAND_FAILED("Failed to set priority"); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV3Target::ospfv3_0_1_set_hello_interval(const string& ifname, const string& vifname, const IPv4& a, const uint32_t& interval) { OspfTypes::AreaID area = ntohl(a.addr()); debug_msg("interface %s vif %s area %s interval %d\n", ifname.c_str(), vifname.c_str(), pr_id(area).c_str(), interval); if (!_ospf_ipv6.set_hello_interval(ifname, vifname, area, interval)) return XrlCmdError::COMMAND_FAILED("Failed to set hello interval"); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV3Target::ospfv3_0_1_set_router_dead_interval(const string& ifname, const string& vifname, const IPv4& a, const uint32_t& interval) { OspfTypes::AreaID area = ntohl(a.addr()); debug_msg("interface %s vif %s area %s interval %d\n", ifname.c_str(), vifname.c_str(), pr_id(area).c_str(), interval); if (!_ospf_ipv6.set_router_dead_interval(ifname, vifname, area, interval)) return XrlCmdError::COMMAND_FAILED("Failed to set " "router dead interval"); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV3Target::ospfv3_0_1_set_passive(const string& ifname, const string& vifname, const IPv4& a, const bool& passive) { OspfTypes::AreaID area = ntohl(a.addr()); debug_msg("interface %s vif %s area %s passive %s\n", ifname.c_str(), vifname.c_str(), pr_id(area).c_str(), bool_c_str(passive)); if (!_ospf_ipv6.set_passive(ifname, vifname, area, passive, false)) return XrlCmdError::COMMAND_FAILED("Failed to configure make passive"); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV3Target::ospfv3_0_1_originate_default_route(const IPv4& a, const bool& enable) { OspfTypes::AreaID area = ntohl(a.addr()); debug_msg("area %s enable %s\n", pr_id(area).c_str(), bool_c_str(enable)); if (!_ospf_ipv6.originate_default_route(area, enable)) return XrlCmdError:: COMMAND_FAILED("Failed to configure default route"); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV3Target::ospfv3_0_1_stub_default_cost(const IPv4& a, const uint32_t& cost) { OspfTypes::AreaID area = ntohl(a.addr()); debug_msg("area %s cost %u\n", pr_id(area).c_str(), cost); if (!_ospf_ipv6.stub_default_cost(area, cost)) return XrlCmdError:: COMMAND_FAILED("Failed set StubDefaultCost"); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV3Target::ospfv3_0_1_summaries(const IPv4& a, const bool& enable) { OspfTypes::AreaID area = ntohl(a.addr()); debug_msg("area %s enable %s\n", pr_id(area).c_str(), bool_c_str(enable)); if (!_ospf_ipv6.summaries(area, enable)) return XrlCmdError:: COMMAND_FAILED("Failed to configure summaries"); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV3Target::ospfv3_0_1_area_range_add(const IPv4& a, const IPv6Net& net, const bool& advertise) { OspfTypes::AreaID area = ntohl(a.addr()); debug_msg("area %s net %s advertise %s\n", pr_id(area).c_str(), cstring(net), bool_c_str(advertise)); if (!_ospf_ipv6.area_range_add(area, net, advertise)) return XrlCmdError:: COMMAND_FAILED(c_format("Failed to add area range " "area %s net %s advertise %s\n", pr_id(area).c_str(), cstring(net), bool_c_str(advertise))); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV3Target::ospfv3_0_1_area_range_delete(const IPv4& a, const IPv6Net& net) { OspfTypes::AreaID area = ntohl(a.addr()); debug_msg("area %s net %s\n", pr_id(area).c_str(), cstring(net)); if (!_ospf_ipv6.area_range_delete(area, net)) return XrlCmdError:: COMMAND_FAILED(c_format("Failed to delete area range " "area %s net %s\n", pr_id(area).c_str(), cstring(net))); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV3Target::ospfv3_0_1_area_range_change_state(const IPv4& a, const IPv6Net& net, const bool& advertise) { OspfTypes::AreaID area = ntohl(a.addr()); debug_msg("area %s net %s advertise %s\n", pr_id(area).c_str(), cstring(net), bool_c_str(advertise)); if (!_ospf_ipv6.area_range_change_state(area, net, advertise)) return XrlCmdError:: COMMAND_FAILED(c_format("Failed to change area range " "area %s net %s advertise %s\n", pr_id(area).c_str(), cstring(net), bool_c_str(advertise))); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV3Target::ospfv3_0_1_trace(const string& tvar, const bool& enable) { debug_msg("trace variable %s enable %s\n", tvar.c_str(), bool_c_str(enable)); if (tvar == "all") { _ospf_ipv6.trace().all(enable); } else { return XrlCmdError:: COMMAND_FAILED(c_format("Unknown variable %s", tvar.c_str())); } return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV3Target::XrlOspfV3Target::ospfv3_0_1_get_lsa(const IPv4& a, const uint32_t& index, bool& valid, bool& toohigh, bool& self, vector& lsa) { OspfTypes::AreaID area = ntohl(a.addr()); debug_msg("area %s index %u\n", pr_id(area).c_str(), index); if (!_ospf_ipv6.get_lsa(area, index, valid, toohigh, self, lsa)) return XrlCmdError::COMMAND_FAILED("Unable to get LSA"); debug_msg("area %s index %u valid %s toohigh %s self %s\n", pr_id(area).c_str(), index, bool_c_str(valid), bool_c_str(toohigh), bool_c_str(self)); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV3Target::ospfv3_0_1_get_area_list(XrlAtomList& areas) { list arealist; if (!_ospf_ipv6.get_area_list(arealist)) return XrlCmdError::COMMAND_FAILED("Unable to get area list"); list::const_iterator i; for (i = arealist.begin(); i != arealist.end(); i++) areas.append(XrlAtom(*i)); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV3Target::ospfv3_0_1_get_neighbour_list(XrlAtomList& neighbours) { list neighbourlist; if (!_ospf_ipv6.get_neighbour_list(neighbourlist)) return XrlCmdError::COMMAND_FAILED("Unable to get neighbour list"); list::const_iterator i; for (i = neighbourlist.begin(); i != neighbourlist.end(); i++) neighbours.append(XrlAtom(*i)); return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV3Target::ospfv3_0_1_get_neighbour_info(const uint32_t& nid, string& address, string& interface, string& state, IPv4& rid, uint32_t& priority, uint32_t& deadtime, IPv4& area, uint32_t& opt, IPv4& dr, IPv4& bdr, uint32_t& up, uint32_t& adjacent) { NeighbourInfo ninfo; if (!_ospf_ipv6.get_neighbour_info(nid, ninfo)) return XrlCmdError::COMMAND_FAILED("Unable to get neighbour info"); #define copy_ninfo(var) var = ninfo._ ## var copy_ninfo(address); copy_ninfo(interface); copy_ninfo(state); copy_ninfo(rid); copy_ninfo(priority); copy_ninfo(deadtime); copy_ninfo(area); copy_ninfo(opt); copy_ninfo(dr); copy_ninfo(bdr); copy_ninfo(up); copy_ninfo(adjacent); #undef copy_ninfo return XrlCmdError::OKAY(); } XrlCmdError XrlOspfV3Target::ospfv3_0_1_clear_database() { if (!_ospf_ipv6.clear_database()) return XrlCmdError::COMMAND_FAILED("Unable clear database"); return XrlCmdError::OKAY(); } xorp/ospf/external.cc0000664000076400007640000005416311540224231014763 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #include "ospf_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/ipv4.hh" #include "libxorp/ipv6.hh" #include "libxorp/ipnet.hh" #include "libxorp/status_codes.h" #include "libxorp/service.hh" #include "libxorp/eventloop.hh" #include "libproto/spt.hh" #include "ospf.hh" #include "delay_queue.hh" #include "vertex.hh" #include "area_router.hh" #include "external.hh" #include "policy_varrw.hh" template External::External(Ospf& ospf, map *>& areas) : _ospf(ospf), _areas(areas), _originating(0), _lsid(1) { } template bool External::announce(OspfTypes::AreaID area, Lsa::LsaRef lsar) { switch(_ospf.get_version()) { case OspfTypes::V2: XLOG_ASSERT(lsar->external()); break; case OspfTypes::V3: XLOG_ASSERT(lsar->external() || (!lsar->known() && lsar->as_scope())); break; } XLOG_ASSERT(!lsar->get_self_originating()); suppress_self(lsar); update_lsa(lsar); typename map *>::iterator i; for (i = _areas.begin(); i != _areas.end(); i++) { if ((*i).first == area) continue; (*i).second->external_announce(lsar, false /* push */, false /* redist */); } lsar->get_timer() = _ospf.get_eventloop(). new_oneoff_after(TimeVal(OspfTypes::MaxAge - lsar->get_header().get_ls_age(), 0), callback(this, &External::maxage_reached, lsar)); return true; } template bool External::announce_complete(OspfTypes::AreaID area) { typename map *>::iterator i; for (i = _areas.begin(); i != _areas.end(); i++) { if ((*i).first == area) continue; (*i).second->external_announce_complete(); } return true; } template void External::push(AreaRouter *area_router) { XLOG_ASSERT(area_router); ASExternalDatabase::iterator i; for(i = _lsas.begin(); i != _lsas.end(); i++) area_router-> external_announce((*i), true /* push */, (*i)->get_self_originating() /* redist */); } template <> void External::unique_link_state_id(Lsa::LsaRef lsar) { ASExternalDatabase::iterator i = _lsas.find(lsar); if (i == _lsas.end()) return; Lsa::LsaRef lsar_in_db = *i; XLOG_ASSERT(lsar_in_db->get_self_originating()); ASExternalLsa *aselsa = dynamic_cast(lsar.get()); XLOG_ASSERT(aselsa); ASExternalLsa *aselsa_in_db = dynamic_cast(lsar_in_db.get()); XLOG_ASSERT(aselsa_in_db); if (aselsa->get_network_mask() == aselsa_in_db->get_network_mask()) return; IPv4 mask = IPv4(htonl(aselsa->get_network_mask())); IPv4 mask_in_db = IPv4(htonl(aselsa_in_db->get_network_mask())); XLOG_ASSERT(mask != mask_in_db); // Be very careful the AS-external-LSAs are stored in a set and // the comparator method uses the link state ID. If the link state // ID of the LSA in the database is going to be changed then it // must first be pulled out of the database and then re-inserted. // The simple case the new LSA is more specific so its host bits // can be set and we are done. if (mask_in_db.mask_len() < mask.mask_len()) { Lsa_header& header = lsar->get_header(); header.set_link_state_id(set_host_bits(header.get_link_state_id(), ntohl(mask.addr()))); lsar->encode(); return; } // The harder case, the LSA already in the database needs to be // changed. First pull it out of the database. delete_lsa(lsar_in_db); Lsa_header& header = lsar_in_db->get_header(); header.set_link_state_id(set_host_bits(header.get_link_state_id(), ntohl(mask_in_db.addr()))); lsar_in_db->encode(); update_lsa(lsar_in_db); refresh(lsar_in_db); } template <> void External::unique_link_state_id(Lsa::LsaRef /*lsar*/) { } template <> ASExternalDatabase::iterator External::unique_find_lsa(Lsa::LsaRef lsar, const IPNet& net) { ASExternalDatabase::iterator i = find_lsa(lsar); if (i == _lsas.end()) return i; Lsa::LsaRef lsar_in_db = *i; XLOG_ASSERT(lsar_in_db->get_self_originating()); ASExternalLsa *aselsa_in_db = dynamic_cast(lsar_in_db.get()); XLOG_ASSERT(aselsa_in_db); IPv4 mask_in_db = IPv4(htonl(aselsa_in_db->get_network_mask())); // If the mask/prefix lengths match then the LSA has been found. if (mask_in_db.mask_len() == net.prefix_len()) return i; // The incoming LSA is about to be modified. Lsa_header& header = lsar->get_header(); // Set the host bits and try to find it again. header.set_link_state_id(set_host_bits(header.get_link_state_id(), ntohl(net.netmask().addr()))); // Recursive return unique_find_lsa(lsar, net); } template <> ASExternalDatabase::iterator External::unique_find_lsa(Lsa::LsaRef lsar, const IPNet& /*net*/) { return find_lsa(lsar); } template <> void External::set_net_nexthop_lsid(ASExternalLsa *aselsa, IPNet net, IPv4 nexthop) { aselsa->set_network(net); aselsa->set_forwarding_address(nexthop); } template <> void External::set_net_nexthop_lsid(ASExternalLsa *aselsa, IPNet net, IPv6 nexthop) { aselsa->set_network(net); if (!nexthop.is_linklocal_unicast() && !nexthop.is_zero()) { aselsa->set_f_bit(true); aselsa->set_forwarding_address(nexthop); } // Note entries in the _lsmap are never removed, this guarantees // for the life of OSPF that the same network to link state ID // mapping exists. If this is a problem on a withdraw remove the // entry, will need to add another argument. Note that it is // possible to receive a withdraw from the policy manager with no // preceding announce. uint32_t lsid = 0; if (0 == _lsmap.count(net)) { lsid = _lsid++; _lsmap[net] = lsid; } else { lsid = _lsmap[net]; } Lsa_header& header = aselsa->get_header(); header.set_link_state_id(lsid); } template bool External::announce(IPNet net, A nexthop, uint32_t metric, const PolicyTags& policytags) { debug_msg("net %s nexthop %s metric %u\n", cstring(net), cstring(nexthop), metric); _originating++; if (1 == _originating) _ospf.get_peer_manager().refresh_router_lsas(); bool ebit = true; uint32_t tag = 0; bool tag_set = false; /** * If the nexthop address is not configured for OSPF then it won't * be reachable, so set the nexthop to zero. */ if (!_ospf.get_peer_manager().configured_network(nexthop)) nexthop = A::ZERO(); if (!do_filtering(net, nexthop, metric, ebit, tag, tag_set, policytags)) return true; OspfTypes::Version version = _ospf.version(); // Don't worry about the memory it will be freed when the LSA goes // out of scope. ASExternalLsa *aselsa = new ASExternalLsa(version); Lsa::LsaRef lsar(aselsa); Lsa_header& header = aselsa->get_header(); switch(version) { case OspfTypes::V2: header.set_options(_ospf.get_peer_manager(). compute_options(OspfTypes::NORMAL)); aselsa->set_external_route_tag(tag); break; case OspfTypes::V3: if (tag_set) { aselsa->set_t_bit(true); aselsa->set_external_route_tag(tag); } break; } set_net_nexthop_lsid(aselsa, net, nexthop); header.set_advertising_router(_ospf.get_router_id()); aselsa->set_metric(metric); aselsa->set_e_bit(ebit); aselsa->set_self_originating(true); if (suppress_candidate(lsar, net, nexthop, metric)) return true; announce_lsa(lsar); return true; } template void External::announce_lsa(Lsa::LsaRef lsar) { TimeVal now; _ospf.get_eventloop().current_time(now); lsar->record_creation_time(now); lsar->encode(); unique_link_state_id(lsar); update_lsa(lsar); typename map *>::iterator i; for (i = _areas.begin(); i != _areas.end(); i++) { (*i).second->external_announce(lsar, false /* push */, true /* redist */); (*i).second->external_announce_complete(); } start_refresh_timer(lsar); } template void External::push_routes() { // XLOG_WARNING("TBD - policy route pushing"); } template bool External::do_filtering(IPNet& network, A& nexthop, uint32_t& metric, bool& e_bit, uint32_t& tag, bool& tag_set, const PolicyTags& policytags) { try { PolicyTags ptags = policytags; OspfVarRW varrw(network, nexthop, metric, e_bit, tag,tag_set,ptags); XLOG_TRACE(_ospf.trace()._export_policy, "[OSPF] Running filter: %s on route: %s\n", filter::filter2str(filter::EXPORT), cstring(network)); bool accepted = _ospf.get_policy_filters(). run_filter(filter::EXPORT, varrw); if (!accepted) return accepted; // XXX - Do I need to do any matching here. } catch(const PolicyException& e) { XLOG_WARNING("PolicyException: %s", e.str().c_str()); return false; } return true; } template void External::start_refresh_timer(Lsa::LsaRef lsar) { lsar->get_timer() = _ospf.get_eventloop(). new_oneoff_after(TimeVal(OspfTypes::LSRefreshTime, 0), callback(this, &External::refresh, lsar)); } template void External::refresh(Lsa::LsaRef lsar) { XLOG_ASSERT(lsar->valid()); TimeVal now; _ospf.get_eventloop().current_time(now); lsar->update_age_and_seqno(now); typename map *>::iterator i; for (i = _areas.begin(); i != _areas.end(); i++) { (*i).second->external_refresh(lsar); } start_refresh_timer(lsar); } template bool External::withdraw(const IPNet& net) { debug_msg("net %s\n", cstring(net)); _originating--; if (0 == _originating) _ospf.get_peer_manager().refresh_router_lsas(); #ifdef SUPPRESS_DB suppress_database_delete(net, true /* invalidate */); #endif // Construct an LSA that will match the one in the database. OspfTypes::Version version = _ospf.version(); ASExternalLsa *aselsa = new ASExternalLsa(version); Lsa_header& header = aselsa->get_header(); set_net_nexthop_lsid(aselsa, net, A::ZERO()); header.set_advertising_router(_ospf.get_router_id()); Lsa::LsaRef searchlsar = aselsa; ASExternalDatabase::iterator i = unique_find_lsa(searchlsar, net); if (i == _lsas.end()) { // The LSA may not have been found because it has been filtered. debug_msg("Lsa not found for net %s", cstring(net)); return true; // XLOG_ERROR("Lsa not found for net %s", cstring(net)); // return false; } Lsa::LsaRef lsar = *i; if (!lsar->get_self_originating()) { XLOG_FATAL("Matching LSA is not self originated %s", cstring(*lsar)); return false; } lsar->set_maxage(); maxage_reached(lsar); return true; } template bool External::clear_database() { _lsas.clear(); #ifdef SUPPRESS_DB _suppress_db.delete_all_nodes(); #endif return true; } template void External::suppress_lsas(OspfTypes::AreaID area) { RoutingTable &rt = _ospf.get_routing_table(); RouteEntry rte; list::iterator i; for (i = _suppress_temp.begin(); i != _suppress_temp.end(); i++) { ASExternalLsa *aselsa = dynamic_cast((*i).get()); XLOG_ASSERT(aselsa); Lsa::LsaRef olsar = aselsa->get_suppressed_lsa(); aselsa->release_suppressed_lsa(); if (!rt.lookup_entry_by_advertising_router(area, aselsa->get_header(). get_advertising_router(), rte)) continue; Lsa::LsaRef nlsar = clone_lsa(olsar); #ifdef SUPPRESS_DB suppress_database_add(nlsar, aselsa->get_network(A::ZERO())); #endif aselsa->set_suppressed_lsa(nlsar); olsar->set_maxage(); maxage_reached(olsar); } _suppress_temp.clear(); } template void External::suppress_route_announce(OspfTypes::AreaID area, IPNet /*net*/, RouteEntry& rte) { switch (rte.get_destination_type()) { case OspfTypes::Router: return; break; case OspfTypes::Network: break; } Lsa::LsaRef lsar = rte.get_lsa(); ASExternalLsa *aselsa = dynamic_cast(lsar.get()); if (0 == aselsa) return; // Should simplify these two methods into a single method without // the intermediate queueing. XLOG_ASSERT(_suppress_temp.empty()); suppress_self(lsar); suppress_lsas(area); } template void External::suppress_route_withdraw(OspfTypes::AreaID /*area*/, IPNet /*net*/, RouteEntry& rte) { switch (rte.get_destination_type()) { case OspfTypes::Router: return; break; case OspfTypes::Network: break; } suppress_release_lsa(rte.get_lsa()); } template Lsa::LsaRef External::clone_lsa(Lsa::LsaRef olsar) { XLOG_ASSERT(olsar->get_self_originating()); ASExternalLsa *olsa = dynamic_cast(olsar.get()); XLOG_ASSERT(olsa); OspfTypes::Version version = _ospf.version(); ASExternalLsa *nlsa = new ASExternalLsa(version); switch(version) { case OspfTypes::V2: nlsa->get_header().set_options(olsa->get_header().get_options()); nlsa->set_external_route_tag(olsa->get_external_route_tag()); break; case OspfTypes::V3: XLOG_ASSERT(olsa->get_f_bit()); if (olsa->get_t_bit()) { nlsa->set_t_bit(true); nlsa->set_external_route_tag(olsa->get_external_route_tag()); } break; } set_net_nexthop_lsid(nlsa, olsa->get_network(A::ZERO()), olsa->get_forwarding_address(A::ZERO())); nlsa->get_header().set_advertising_router(_ospf.get_router_id()); nlsa->set_metric(olsa->get_metric()); nlsa->set_e_bit(olsa->get_e_bit()); nlsa->set_self_originating(true); Lsa::LsaRef nlsar(nlsa); // Remember to call unique_link_state_id(lsar) if this LSA is ever // transmitted. return nlsar; } template bool External::suppress_candidate(Lsa::LsaRef lsar, IPNet net, A nexthop, uint32_t metric) { if (A::ZERO() == nexthop) return false; RoutingTable &rt = _ospf.get_routing_table(); RouteEntry rte; if (!rt.lookup_entry(net, rte)) return false; Lsa::LsaRef rlsar = rte.get_lsa(); ASExternalLsa *aselsa = dynamic_cast(rlsar.get()); if (!aselsa) return false; // Make sure the announcing router is reachable. if (!rt.lookup_entry_by_advertising_router(rte.get_area(), aselsa->get_header(). get_advertising_router(), rte)) return false; OspfTypes::Version version = _ospf.version(); switch(version) { case OspfTypes::V2: break; case OspfTypes::V3: if (!aselsa->get_f_bit()) return false; break; } if (aselsa->get_forwarding_address(A::ZERO()) != nexthop) return false; if (aselsa->get_metric() != metric) return false; if (aselsa->get_header().get_advertising_router() < _ospf.get_router_id()) return false; aselsa->set_suppressed_lsa(lsar); #ifdef SUPPRESS_DB suppress_database_add(lsar, net); #endif return true; } #ifdef SUPPRESS_DB template void External::suppress_database_add(Lsa::LsaRef lsar, const IPNet& net) { _suppress_db.insert(net, lsar); } template void External::suppress_database_delete(const IPNet& net, bool invalidate) { typename Trie::iterator i = _suppress_db.lookup_node(net); if (_suppress_db.end() == i) { // XLOG_WARNING("LSA matching %s not found", cstring(net)); return; } if (invalidate) i.payload()->invalidate(); _suppress_db.erase(i); } #endif template void External::suppress_self(Lsa::LsaRef lsar) { ASExternalLsa *aselsa = dynamic_cast(lsar.get()); XLOG_ASSERT(aselsa); // This may be a refresh of previously announce AS-external-LSA. bool suppressed = false; Lsa::LsaRef nlsar; ASExternalDatabase::iterator i = find_lsa(lsar); if (_lsas.end() != i) { nlsar = aselsa->get_suppressed_lsa(); if (0 != nlsar.get()) { aselsa->release_suppressed_lsa(); if (nlsar->valid()) suppressed = true; } } // If it was previously suppressed and is no longer it needs to be // announced. if (!suppress_self_check(lsar)) { if (suppressed) { #ifdef SUPPRESS_DB suppress_database_delete(aselsa->get_network(A::ZERO()), false); #endif announce_lsa(nlsar); } return; } Lsa::LsaRef olsar = find_lsa_by_net(aselsa->get_network(A::ZERO())); XLOG_ASSERT(0 != olsar.get()); aselsa->set_suppressed_lsa(olsar); // If this self orignated AS-external-LSA was already being // suppressed nothing more todo. if (suppressed) return; suppress_queue_lsa(lsar); } template bool External::suppress_self_check(Lsa::LsaRef lsar) { XLOG_ASSERT(lsar->external()); XLOG_ASSERT(!lsar->get_self_originating()); ASExternalLsa *aselsa = dynamic_cast(lsar.get()); XLOG_ASSERT(aselsa); OspfTypes::Version version = _ospf.version(); switch(version) { case OspfTypes::V2: break; case OspfTypes::V3: if (!aselsa->get_f_bit()) return false; break; } if (aselsa->get_forwarding_address(A::ZERO()) == A::ZERO()) return false; if (aselsa->get_header().get_advertising_router() < _ospf.get_router_id()) return false; Lsa::LsaRef olsar = find_lsa_by_net(aselsa->get_network(A::ZERO())); if (0 == olsar.get()) return false; ASExternalLsa *olsa = dynamic_cast(olsar.get()); XLOG_ASSERT(olsa); switch(version) { case OspfTypes::V2: break; case OspfTypes::V3: if (!olsa->get_f_bit()) return false; break; } if (olsa->get_forwarding_address(A::ZERO()) == A::ZERO()) return false; if (olsa->get_metric() != aselsa->get_metric()) return false; return true; } template Lsa::LsaRef External::find_lsa_by_net(IPNet net) { ASExternalLsa *tlsa = new ASExternalLsa(_ospf.get_version()); Lsa::LsaRef tlsar(tlsa); tlsa->get_header().set_advertising_router(_ospf.get_router_id()); set_net_nexthop_lsid(tlsa, net, A::ZERO()); Lsa::LsaRef lsar; ASExternalDatabase::iterator i = find_lsa(tlsar); if (i != _lsas.end()) lsar = *i; return lsar; } template void External::suppress_queue_lsa(Lsa::LsaRef lsar) { _suppress_temp.push_back(lsar); } template void External::suppress_maxage(Lsa::LsaRef lsar) { XLOG_ASSERT(lsar->external()); XLOG_ASSERT(lsar->maxage()); if (lsar->get_self_originating()) return; suppress_release_lsa(lsar); } template void External::suppress_release_lsa(Lsa::LsaRef lsar) { // If this LSA was suppressing a self originated LSA then announce // it now. ASExternalLsa *aselsa = dynamic_cast(lsar.get()); if (0 == aselsa) return; Lsa::LsaRef nlsar = aselsa->get_suppressed_lsa(); if (0 == nlsar.get()) return; aselsa->release_suppressed_lsa(); if (!nlsar->valid()) return; #ifdef SUPPRESS_DB suppress_database_delete(aselsa->get_network(A::ZERO()), false); #endif announce_lsa(nlsar); } template ASExternalDatabase::iterator External::find_lsa(Lsa::LsaRef lsar) { return _lsas.find(lsar); } template void External::update_lsa(Lsa::LsaRef lsar) { ASExternalDatabase::iterator i = _lsas.find(lsar); if (i != _lsas.end()) { (*i)->invalidate(); _lsas.erase(i); } _lsas.insert(lsar); } template void External::delete_lsa(Lsa::LsaRef lsar) { ASExternalDatabase::iterator i = find_lsa(lsar); XLOG_ASSERT(i != _lsas.end()); _lsas.erase(i); } template void External::maxage_reached(Lsa::LsaRef lsar) { XLOG_ASSERT(lsar->external()); // XLOG_ASSERT(!lsar->get_self_originating()); ASExternalDatabase::iterator i = find_lsa(lsar); if (i == _lsas.end()) XLOG_FATAL("LSA not in database: %s", cstring(*lsar)); if (!lsar->maxage()) { TimeVal now; _ospf.get_eventloop().current_time(now); lsar->update_age(now); } if (!lsar->maxage()) XLOG_FATAL("LSA is not MaxAge %s", cstring(*lsar)); suppress_maxage(lsar); delete_lsa(lsar); typename map *>::iterator ia; for (ia = _areas.begin(); ia != _areas.end(); ia++) { (*ia).second->external_withdraw(lsar); } // Clear the timer otherwise there is a circular dependency. // The LSA contains a XorpTimer that points back to the LSA. lsar->get_timer().clear(); } void ASExternalDatabase::clear() { set ::iterator i; for(i = _lsas.begin(); i != _lsas.end(); i++) (*i)->invalidate(); _lsas.clear(); } ASExternalDatabase::iterator ASExternalDatabase::find(Lsa::LsaRef lsar) { return _lsas.find(lsar); #if 0 Lsa_header& header = lsar->get_header(); uint32_t link_state_id = header.get_link_state_id(); uint32_t advertising_router = header.get_advertising_router(); list ::iterator i; for(i = _lsas.begin(); i != _lsas.end(); i++) { if ((*i)->get_header().get_link_state_id() == link_state_id && (*i)->get_header().get_advertising_router() == advertising_router){ return i; } } return _lsas.end(); #endif } template class External; template class External; xorp/ospf/ospf_module.h0000664000076400007640000000221411421137511015307 0ustar greearbgreearb/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- */ /* vim:set sts=4 ts=8: */ /* * Copyright (c) 2001-2009 XORP, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 2, June * 1991 as published by the Free Software Foundation. Redistribution * and/or modification of this program under the terms of any other * version of the GNU General Public License is not permitted. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, * see the GNU General Public License, Version 2, a copy of which can be * found in the XORP LICENSE.gpl file. * * XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; * http://xorp.net */ /* * $XORP: xorp/ospf/ospf_module.h,v 1.3 2008/10/02 21:57:48 bms Exp $ */ /* * Module definitions. */ /* XXX: this file must be included first by each *.c or *.cc file in a module */ #define XORP_MODULE_NAME "OSPF" #define XORP_MODULE_VERSION "0.1" xorp/ospf/vlink.cc0000664000076400007640000001710111421137511014255 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #include "ospf_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/ipv4.hh" #include "libxorp/ipv6.hh" #include "libxorp/ipnet.hh" #include "libxorp/status_codes.h" #include "libxorp/service.hh" #include "libxorp/eventloop.hh" #include "ospf.hh" #include "vlink.hh" template bool Vlink::create_vlink(OspfTypes::RouterID rid) { if (0 != _vlinks.count(rid)) { XLOG_WARNING("Virtual link to %s already exists", pr_id(rid).c_str()); return false; } Vstate v; _vlinks[rid] = v; return true; } template bool Vlink::delete_vlink(OspfTypes::RouterID rid) { if (0 == _vlinks.count(rid)) { XLOG_WARNING("Virtual link to %s doesn't exist", pr_id(rid).c_str()); return false; } _vlinks.erase(_vlinks.find(rid)); return true; } template bool Vlink::set_transit_area(OspfTypes::RouterID rid, OspfTypes::AreaID transit_area) { if (0 == _vlinks.count(rid)) { XLOG_WARNING("Virtual link to %s doesn't exist", pr_id(rid).c_str()); return false; } typename map ::iterator i = _vlinks.find(rid); XLOG_ASSERT(_vlinks.end() != i); i->second._transit_area = transit_area; return true; } template bool Vlink::get_transit_area(OspfTypes::RouterID rid, OspfTypes::AreaID& transit_area) const { if (0 == _vlinks.count(rid)) { XLOG_WARNING("Virtual link to %s doesn't exist", pr_id(rid).c_str()); return false; } typename map ::const_iterator i = _vlinks.find(rid); XLOG_ASSERT(_vlinks.end() != i); transit_area = i->second._transit_area; return true; } template bool Vlink::set_transit_area_notified(OspfTypes::RouterID rid, bool notified) { if (0 == _vlinks.count(rid)) { XLOG_WARNING("Virtual link to %s doesn't exist", pr_id(rid).c_str()); return false; } typename map ::iterator i = _vlinks.find(rid); XLOG_ASSERT(_vlinks.end() != i); i->second._notified = notified; return true; } template bool Vlink::get_transit_area_notified(OspfTypes::RouterID rid) const { if (0 == _vlinks.count(rid)) { XLOG_WARNING("Virtual link to %s doesn't exist", pr_id(rid).c_str()); return false; } typename map ::const_iterator i = _vlinks.find(rid); XLOG_ASSERT(_vlinks.end() != i); return i->second._notified; } template bool Vlink::add_address(OspfTypes::RouterID rid, A source, A destination) { if (0 == _vlinks.count(rid)) { XLOG_WARNING("Virtual link to %s doesn't exist", pr_id(rid).c_str()); return false; } typename map ::iterator i = _vlinks.find(rid); XLOG_ASSERT(_vlinks.end() != i); i->second._source = source; i->second._destination = destination; return true; } template bool Vlink::get_address(OspfTypes::RouterID rid, A& source, A& destination) { if (0 == _vlinks.count(rid)) { XLOG_WARNING("Virtual link to %s doesn't exist", pr_id(rid).c_str()); return false; } typename map ::iterator i = _vlinks.find(rid); XLOG_ASSERT(_vlinks.end() != i); source = i->second._source; destination = i->second._destination; return true; } template bool Vlink::get_interface_vif(OspfTypes::RouterID rid, string& interface, string& vif) const { if (0 == _vlinks.count(rid)) { XLOG_WARNING("Virtual link to %s doesn't exist", pr_id(rid).c_str()); return false; } typename map ::const_iterator i = _vlinks.find(rid); XLOG_ASSERT(_vlinks.end() != i); // XLOG_ASSERT(A::ZERO() != i->second._source); // XLOG_ASSERT(A::ZERO() != i->second._destination); interface = VLINK; vif = pr_id(rid); return true; } template bool Vlink::add_peerid(OspfTypes::RouterID rid, OspfTypes::PeerID peerid) { if (0 == _vlinks.count(rid)) { XLOG_WARNING("Virtual link to %s doesn't exist", pr_id(rid).c_str()); return false; } typename map ::iterator i = _vlinks.find(rid); XLOG_ASSERT(_vlinks.end() != i); i->second._peerid = peerid; return true; } template OspfTypes::PeerID Vlink::get_peerid(OspfTypes::RouterID rid) const { if (0 == _vlinks.count(rid)) { XLOG_WARNING("Virtual link to %s doesn't exist", pr_id(rid).c_str()); return OspfTypes::ALLPEERS; } typename map ::const_iterator i = _vlinks.find(rid); XLOG_ASSERT(_vlinks.end() != i); return i->second._peerid; } template bool Vlink::set_physical_interface_vif(OspfTypes::RouterID rid, string& interface, string& vif) { if (0 == _vlinks.count(rid)) { XLOG_WARNING("Virtual link to %s doesn't exist", pr_id(rid).c_str()); return false; } typename map ::iterator i = _vlinks.find(rid); XLOG_ASSERT(_vlinks.end() != i); i->second._physical_interface = interface; i->second._physical_vif = vif; return true; } template bool Vlink::get_physical_interface_vif(A source, A destination, string& interface, string& vif) const { typename map::const_iterator i; for(i = _vlinks.begin(); i != _vlinks.end(); i++) { if (i->second._source == source && i->second._destination == destination) { interface = i->second._physical_interface; vif = i->second._physical_vif; return true; } } return false; } template OspfTypes::PeerID Vlink::get_peerid(A source, A destination) const { typename map::const_iterator i; for(i = _vlinks.begin(); i != _vlinks.end(); i++) { if (i->second._source == source && i->second._destination == destination) { return i->second._peerid; } } return OspfTypes::ALLPEERS; } template void Vlink::get_router_ids(OspfTypes::AreaID transit_area, list& rids) const { typename map::const_iterator i; for(i = _vlinks.begin(); i != _vlinks.end(); i++) { if (i->second._transit_area == transit_area) { rids.push_back(i->first); } } } template void Vlink::area_removed(OspfTypes::AreaID area) { typename map::iterator i; for(i = _vlinks.begin(); i != _vlinks.end(); i++) { if (i->second._transit_area == area) { i->second._notified = false; } } } template class Vlink; template class Vlink; xorp/ospf/auth.cc0000664000076400007640000005530311540225531014103 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #include "ospf_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/ipv4.hh" #include "libxorp/ipv6.hh" #include "libxorp/ipnet.hh" #include "libxorp/status_codes.h" #include "libxorp/service.hh" #include "libxorp/eventloop.hh" #include "libproto/packet.hh" #include "ospf.hh" #include "auth.hh" /** * RFC 1141 Incremental Updating of the Internet Checksum */ inline uint16_t incremental_checksum(uint32_t orig, uint32_t add) { // Assumes that the original value of the modified field was zero. uint32_t sum = orig + (~add & 0xffff); sum = (sum & 0xffff) + (sum >> 16); return sum; } /** * Create a printable string of an array of data */ inline string printable(const uint8_t* p, size_t max_size) { string output; for (size_t i = 0; i < max_size; i++, p++) { if ('\0' == *p) break; if (xorp_isprint(*p)) output += *p; else output += c_format("[%#x]", *p); } return output; } // ---------------------------------------------------------------------------- // AuthHandlerBase implementation AuthHandlerBase::~AuthHandlerBase() { } const string& AuthHandlerBase::error() const { return _error; } inline void AuthHandlerBase::reset_error() { if (_error.empty() == false) _error.erase(); } inline void AuthHandlerBase::set_error(const string& error_msg) { _error = error_msg; } // ---------------------------------------------------------------------------- // NullAuthHandler implementation const char* NullAuthHandler::effective_name() const { return auth_type_name(); } const char* NullAuthHandler::auth_type_name() { return "none"; } void NullAuthHandler::reset() { } uint32_t NullAuthHandler::additional_payload() const { return 0; } bool NullAuthHandler::authenticate_inbound(const vector& pkt, const IPv4&, bool) { // TODO: here we should check whether the packet is too large if (pkt.size() < Packet::STANDARD_HEADER_V2) { set_error(c_format("packet too small (%u bytes)", XORP_UINT_CAST(pkt.size()))); return false; } const uint8_t *ptr = &pkt[0]; OspfTypes::AuType autype = extract_16(&ptr[Packet::AUTH_TYPE_OFFSET]); if (AUTH_TYPE != autype) { set_error(c_format("unexpected authentication data (type %d)", autype)); return false; } reset_error(); return true; } bool NullAuthHandler::authenticate_outbound(vector& pkt) { XLOG_ASSERT(pkt.size() >= Packet::STANDARD_HEADER_V2); uint8_t *ptr = &pkt[0]; embed_16(&ptr[Packet::AUTH_TYPE_OFFSET], AUTH_TYPE); embed_16(&ptr[Packet::CHECKSUM_OFFSET], // RFC 1141 incremental_checksum(extract_16(&ptr[Packet::CHECKSUM_OFFSET]), AUTH_TYPE)); reset_error(); return (true); } // ---------------------------------------------------------------------------- // Plaintext handler implementation const char* PlaintextAuthHandler::effective_name() const { return auth_type_name(); } const char* PlaintextAuthHandler::auth_type_name() { return "simple"; } void PlaintextAuthHandler::reset() { } uint32_t PlaintextAuthHandler::additional_payload() const { return 0; } bool PlaintextAuthHandler::authenticate_inbound(const vector& pkt, const IPv4&, bool) { // TODO: here we should check whether the packet is too large if (pkt.size() < Packet::STANDARD_HEADER_V2) { set_error(c_format("packet too small (%u bytes)", XORP_UINT_CAST(pkt.size()))); return false; } const uint8_t *ptr = &pkt[0]; OspfTypes::AuType autype = extract_16(&ptr[Packet::AUTH_TYPE_OFFSET]); if (AUTH_TYPE != autype) { set_error("not a plaintext authenticated packet"); return false; } if (0 != memcmp(&ptr[Packet::AUTH_PAYLOAD_OFFSET], &_key_data[0], sizeof(_key_data))) { string passwd = printable(&ptr[Packet::AUTH_PAYLOAD_OFFSET], Packet::AUTH_PAYLOAD_SIZE); set_error(c_format("wrong password \"%s\"", passwd.c_str())); return false; } reset_error(); return true; } bool PlaintextAuthHandler::authenticate_outbound(vector& pkt) { XLOG_ASSERT(pkt.size() >= Packet::STANDARD_HEADER_V2); uint8_t *ptr = &pkt[0]; embed_16(&ptr[Packet::AUTH_TYPE_OFFSET], AUTH_TYPE); embed_16(&ptr[Packet::CHECKSUM_OFFSET], // RFC 1141 incremental_checksum(extract_16(&ptr[Packet::CHECKSUM_OFFSET]), AUTH_TYPE)); memcpy(&ptr[Packet::AUTH_PAYLOAD_OFFSET], &_key_data[0], sizeof(_key_data)); reset_error(); return (true); } void PlaintextAuthHandler::set_key(const string& plaintext_key) { _key = string(plaintext_key, 0, Packet::AUTH_PAYLOAD_SIZE); memset(&_key_data[0], 0, sizeof(_key_data)); size_t len = _key.size(); if (len > sizeof(_key_data)) len = sizeof(_key_data); memcpy(&_key_data[0], _key.c_str(), len); } const string& PlaintextAuthHandler::key() const { return _key; } // ---------------------------------------------------------------------------- // MD5AuthHandler::MD5Key implementation MD5AuthHandler::MD5Key::MD5Key(uint8_t key_id, const string& key, const TimeVal& start_timeval, const TimeVal& end_timeval, const TimeVal& max_time_drift, XorpTimer start_timer, XorpTimer stop_timer) : _id(key_id), _start_timeval(start_timeval), _end_timeval(end_timeval), _max_time_drift(max_time_drift), _is_persistent(false), _o_seqno(0), _start_timer(start_timer), _stop_timer(stop_timer) { string::size_type n = key.copy(_key_data, 16); if (n < KEY_BYTES) { memset(_key_data + n, 0, KEY_BYTES - n); } } string MD5AuthHandler::MD5Key::key() const { return string(_key_data, 0, 16); } bool MD5AuthHandler::MD5Key::valid_at(const TimeVal& when) const { if (is_persistent()) return true; return ((_start_timeval <= when) && (when <= _end_timeval)); } void MD5AuthHandler::MD5Key::reset() { // // Reset the seqno // _lr_seqno.clear(); // // Reset the flag that a packet has been received // _pkts_recv.clear(); } void MD5AuthHandler::MD5Key::reset(const IPv4& src_addr) { map::iterator seqno_iter; map::iterator recv_iter; // // Reset the seqno // seqno_iter = _lr_seqno.find(src_addr); if (seqno_iter != _lr_seqno.end()) _lr_seqno.erase(seqno_iter); // // Reset the flag that a packet has been received // recv_iter = _pkts_recv.find(src_addr); if (recv_iter != _pkts_recv.end()) _pkts_recv.erase(recv_iter); } bool MD5AuthHandler::MD5Key::packets_received(const IPv4& src_addr) const { map::const_iterator iter; iter = _pkts_recv.find(src_addr); if (iter == _pkts_recv.end()) return (false); return (iter->second); } uint32_t MD5AuthHandler::MD5Key::last_seqno_recv(const IPv4& src_addr) const { map::const_iterator iter; iter = _lr_seqno.find(src_addr); if (iter == _lr_seqno.end()) return (0); return (iter->second); } void MD5AuthHandler::MD5Key::set_last_seqno_recv(const IPv4& src_addr, uint32_t seqno) { map::iterator seqno_iter; map::iterator recv_iter; // // Set the seqno // seqno_iter = _lr_seqno.find(src_addr); if (seqno_iter == _lr_seqno.end()) _lr_seqno.insert(make_pair(src_addr, seqno)); else seqno_iter->second = seqno; // // Set the flag that a packet has been received // recv_iter = _pkts_recv.find(src_addr); if (recv_iter == _pkts_recv.end()) _pkts_recv.insert(make_pair(src_addr, true)); else recv_iter->second = true; } // ---------------------------------------------------------------------------- // MD5AuthHandler implementation MD5AuthHandler::MD5AuthHandler(EventLoop& eventloop) : _eventloop(eventloop) { } const char* MD5AuthHandler::effective_name() const { // // XXX: if no valid keys, then don't use any authentication // if (_valid_key_chain.empty()) { return (_null_handler.effective_name()); } return auth_type_name(); } const char* MD5AuthHandler::auth_type_name() { return "md5"; } void MD5AuthHandler::reset() { // // XXX: if no valid keys, then don't use any authentication // if (_valid_key_chain.empty()) { _null_handler.reset(); return; } reset_keys(); } uint32_t MD5AuthHandler::additional_payload() const { // // XXX: if no valid keys, then don't use any authentication // if (_valid_key_chain.empty()) { return (_null_handler.additional_payload()); } return 0; } bool MD5AuthHandler::authenticate_inbound(const vector& pkt, const IPv4& src_addr, bool new_peer) { // // XXX: if no valid keys, then don't use any authentication // if (_valid_key_chain.empty()) { if (_null_handler.authenticate_inbound(pkt, src_addr, new_peer) != true) { set_error(_null_handler.error()); return (false); } reset_error(); return (true); } // TODO: here we should check whether the packet is too large if (pkt.size() < Packet::STANDARD_HEADER_V2) { set_error(c_format("packet too small (%u bytes)", XORP_UINT_CAST(pkt.size()))); return false; } const uint8_t *ptr = &pkt[0]; OspfTypes::AuType autype = extract_16(&ptr[Packet::AUTH_TYPE_OFFSET]); if (AUTH_TYPE != autype) { set_error("not an MD5 authenticated packet"); return false; } uint8_t key_id = ptr[Packet::AUTH_PAYLOAD_OFFSET + 2]; uint32_t seqno = extract_32(&ptr[Packet::AUTH_PAYLOAD_OFFSET + 4]); KeyChain::iterator k = find_if(_valid_key_chain.begin(), _valid_key_chain.end(), bind2nd(mem_fun_ref(&MD5Key::id_matches), key_id)); if (k == _valid_key_chain.end()) { set_error(c_format("packet with key ID %d for which no key is " "configured", key_id)); return false; } MD5Key* key = &(*k); if (new_peer) key->reset(src_addr); uint32_t last_seqno_recv = key->last_seqno_recv(src_addr); if (key->packets_received(src_addr) && !(new_peer && seqno == 0) && (seqno - last_seqno_recv >= 0x7fffffff)) { set_error(c_format("bad sequence number 0x%08x < 0x%08x", XORP_UINT_CAST(seqno), XORP_UINT_CAST(last_seqno_recv))); return false; } MD5_CTX ctx; uint8_t digest[MD5_DIGEST_LENGTH]; // length to compute MD5 over uint32_t md5_packet_length = extract_16(&ptr[Packet::LEN_OFFSET]); MD5_Init(&ctx); MD5_Update(&ctx, &ptr[0], md5_packet_length); MD5_Update(&ctx, key->key_data(), key->key_data_bytes()); MD5_Final(&digest[0], &ctx); if (0 != memcmp(&digest[0], &ptr[md5_packet_length], MD5_DIGEST_LENGTH)) { set_error(c_format("authentication digest doesn't match local key " "(key ID = %d)", key->id())); // #define DUMP_BAD_MD5 #ifdef DUMP_BAD_MD5 const char badmd5[] = "/tmp/ospf_badmd5"; // If the file already exists don't dump anything. The file // should contain and only one packet. if (-1 == access(badmd5, R_OK)) { XLOG_INFO("Dumping bad MD5 to %s", badmd5); FILE *fp = fopen(badmd5, "w"); fwrite(&pkt[0], pkt.size(), 1 , fp); fclose(fp); } #endif return false; } // Update sequence number only after packet has passed digest check key->set_last_seqno_recv(src_addr, seqno); reset_error(); return true; } bool MD5AuthHandler::authenticate_outbound(vector& pkt) { TimeVal now; vector trailer; _eventloop.current_time(now); MD5Key* key = best_outbound_key(now); // // XXX: if no valid keys, then don't use any authentication // if (key == NULL) { if (_null_handler.authenticate_outbound(pkt) != true) { set_error(_null_handler.error()); return (false); } reset_error(); return (true); } XLOG_ASSERT(pkt.size() >= Packet::STANDARD_HEADER_V2); uint8_t *ptr = &pkt[0]; // Set the authentication type and zero out the checksum. embed_16(&ptr[Packet::AUTH_TYPE_OFFSET], AUTH_TYPE); embed_16(&ptr[Packet::CHECKSUM_OFFSET], 0); // Set config in the authentication block. embed_16(&ptr[Packet::AUTH_PAYLOAD_OFFSET], 0); ptr[Packet::AUTH_PAYLOAD_OFFSET + 2] = key->id(); ptr[Packet::AUTH_PAYLOAD_OFFSET + 3] = MD5_DIGEST_LENGTH; embed_32(&ptr[Packet::AUTH_PAYLOAD_OFFSET + 4], key->next_seqno_out()); // Add the space for the digest at the end of the packet. size_t pend = pkt.size(); pkt.resize(pkt.size() + MD5_DIGEST_LENGTH); ptr = &pkt[0]; MD5_CTX ctx; MD5_Init(&ctx); MD5_Update(&ctx, &ptr[0], pend); MD5_Update(&ctx, key->key_data(), key->key_data_bytes()); MD5_Final(&ptr[pend], &ctx); reset_error(); return (true); } bool MD5AuthHandler::add_key(uint8_t key_id, const string& key, const TimeVal& start_timeval, const TimeVal& end_timeval, const TimeVal& max_time_drift, string& error_msg) { TimeVal now, adj_timeval; XorpTimer start_timer, end_timer; string dummy_error_msg; _eventloop.current_time(now); if (start_timeval > end_timeval) { error_msg = c_format("Start time is later than the end time"); return false; } if (end_timeval < now) { error_msg = c_format("End time is in the past"); return false; } // Adjust the time when we start using the key to accept messages adj_timeval = start_timeval; if (adj_timeval >= max_time_drift) adj_timeval -= max_time_drift; else adj_timeval = TimeVal::ZERO(); if (adj_timeval > now) { start_timer = _eventloop.new_oneoff_at( adj_timeval, callback(this, &MD5AuthHandler::key_start_cb, key_id)); } // Adjust the time when we stop using the key to accept messages adj_timeval = end_timeval; if (TimeVal::MAXIMUM() - max_time_drift > adj_timeval) adj_timeval += max_time_drift; else adj_timeval = TimeVal::MAXIMUM(); if (adj_timeval != TimeVal::MAXIMUM()) { end_timer = _eventloop.new_oneoff_at( adj_timeval, callback(this, &MD5AuthHandler::key_stop_cb, key_id)); } // // XXX: If we are using the last authentication key that has expired, // move it to the list of invalid keys. // if (_valid_key_chain.size() == 1) { MD5Key& key = _valid_key_chain.front(); if (key.is_persistent()) { key.set_persistent(false); _invalid_key_chain.push_back(key); _valid_key_chain.pop_front(); } } // XXX: for simplicity just try to remove the key even if it doesn't exist remove_key(key_id, dummy_error_msg); // Add the new key to the appropriate chain MD5Key new_key = MD5Key(key_id, key, start_timeval, end_timeval, max_time_drift, start_timer, end_timer); if (start_timer.scheduled()) _invalid_key_chain.push_back(new_key); else _valid_key_chain.push_back(new_key); return true; } bool MD5AuthHandler::remove_key(uint8_t key_id, string& error_msg) { KeyChain::iterator i; // Check among all valid keys i = find_if(_valid_key_chain.begin(), _valid_key_chain.end(), bind2nd(mem_fun_ref(&MD5Key::id_matches), key_id)); if (i != _valid_key_chain.end()) { _valid_key_chain.erase(i); return true; } // Check among all invalid keys i = find_if(_invalid_key_chain.begin(), _invalid_key_chain.end(), bind2nd(mem_fun_ref(&MD5Key::id_matches), key_id)); if (i != _invalid_key_chain.end()) { _invalid_key_chain.erase(i); return true; } error_msg = c_format("No such key"); return false; } void MD5AuthHandler::key_start_cb(uint8_t key_id) { KeyChain::iterator i; // Find the key among all invalid keys and move it to the valid keys i = find_if(_invalid_key_chain.begin(), _invalid_key_chain.end(), bind2nd(mem_fun_ref(&MD5Key::id_matches), key_id)); if (i != _invalid_key_chain.end()) { MD5Key& key = *i; _valid_key_chain.push_back(key); _invalid_key_chain.erase(i); } } void MD5AuthHandler::key_stop_cb(uint8_t key_id) { KeyChain::iterator i; // Find the key among all valid keys and move it to the invalid keys i = find_if(_valid_key_chain.begin(), _valid_key_chain.end(), bind2nd(mem_fun_ref(&MD5Key::id_matches), key_id)); if (i != _valid_key_chain.end()) { MD5Key& key = *i; // // XXX: If the last key expires then keep using it as per // RFC 2328 Appendix D.3 until the lifetime is extended, the key // is deleted by network management, or a new key is configured. // if (_valid_key_chain.size() == 1) { XLOG_WARNING("Last authentication key (key ID = %u) has expired. " "Will keep using it until its lifetime is extended, " "the key is deleted, or a new key is configured.", key_id); key.set_persistent(true); return; } _invalid_key_chain.push_back(key); _valid_key_chain.erase(i); } } /** * Select the best key for outbound messages. * * The chosen key is the one with most recent start-time in the past. * If there is more than one key that matches the criteria, then select * the key with greatest ID. * * @param now current time. */ MD5AuthHandler::MD5Key* MD5AuthHandler::best_outbound_key(const TimeVal& now) { MD5Key* best_key = NULL; KeyChain::iterator iter; for (iter = _valid_key_chain.begin(); iter != _valid_key_chain.end(); ++iter) { MD5Key* key = &(*iter); if (! key->valid_at(now)) continue; if (best_key == NULL) { best_key = key; continue; } // Select the key with most recent start-time if (best_key->start_timeval() > key->start_timeval()) continue; if (best_key->start_timeval() < key->start_timeval()) { best_key = key; continue; } // The start time is same hence select the key with greatest ID if (best_key->id() > key->id()) continue; if (best_key->id() < key->id()) { best_key = key; continue; } XLOG_UNREACHABLE(); // XXX: we cannot have two keys with same ID } return (best_key); } void MD5AuthHandler::reset_keys() { KeyChain::iterator iter; for (iter = _valid_key_chain.begin(); iter != _valid_key_chain.end(); ++iter) { iter->reset(); } } bool MD5AuthHandler::empty() const { return (_valid_key_chain.empty() && _invalid_key_chain.empty()); } // ---------------------------------------------------------------------------- // Auth implementation bool Auth::set_simple_authentication_key(const string& password, string& error_msg) { PlaintextAuthHandler* plaintext_ah = NULL; XLOG_ASSERT(_auth_handler != NULL); plaintext_ah = dynamic_cast(_auth_handler); if (plaintext_ah == NULL) { set_method(PlaintextAuthHandler::auth_type_name()); } plaintext_ah = dynamic_cast(_auth_handler); XLOG_ASSERT(plaintext_ah != NULL); plaintext_ah->set_key(password); error_msg = ""; return (true); } bool Auth::delete_simple_authentication_key(string& error_msg) { PlaintextAuthHandler* plaintext_ah = NULL; XLOG_ASSERT(_auth_handler != NULL); plaintext_ah = dynamic_cast(_auth_handler); if (plaintext_ah != NULL) { // // XXX: Here we should return a mismatch error. // However, if we are adding both a simple password and MD5 handlers, // then the rtrmgr configuration won't match the protocol state. // Ideally, the rtrmgr and xorpsh shouldn't allow the user to add // both handlers. For the time being we need to live with this // limitation and therefore we shouldn't return an error here. // return (true); #if 0 error_msg = c_format("Cannot delete simple password authentication: " "no such is configured"); return (false); // XXX: not a simple password authentication handler #endif } // // Install an empty handler and delete the simple authentication handler // set_method(NullAuthHandler::auth_type_name()); error_msg = ""; return (true); } bool Auth::set_md5_authentication_key(uint8_t key_id, const string& password, const TimeVal& start_timeval, const TimeVal& end_timeval, const TimeVal& max_time_drift, string& error_msg) { MD5AuthHandler* md5_ah = NULL; XLOG_ASSERT(_auth_handler != NULL); md5_ah = dynamic_cast(_auth_handler); if (md5_ah != NULL) { if (md5_ah->add_key(key_id, password, start_timeval, end_timeval, max_time_drift, error_msg) != true) { error_msg = c_format("MD5 key add failed: %s", error_msg.c_str()); return (false); } return (true); } // Create a new MD5 authentication handler and delete the old handler md5_ah = new MD5AuthHandler(_eventloop); if (md5_ah->add_key(key_id, password, start_timeval, end_timeval, max_time_drift, error_msg) != true) { error_msg = c_format("MD5 key add failed: %s", error_msg.c_str()); delete md5_ah; return (false); } delete _auth_handler; _auth_handler = md5_ah; return (true); } bool Auth::delete_md5_authentication_key(uint8_t key_id, string& error_msg) { MD5AuthHandler* md5_ah = NULL; XLOG_ASSERT(_auth_handler != NULL); md5_ah = dynamic_cast(_auth_handler); if (md5_ah != NULL) { // // XXX: Here we should return a mismatch error. // However, if we are adding both a simple password and MD5 handlers, // then the rtrmgr configuration won't match the protocol state. // Ideally, the rtrmgr and xorpsh shouldn't allow the user to add // both handlers. For the time being we need to live with this // limitation and therefore we shouldn't return an error here. // return (true); #if 0 error_msg = c_format("Cannot delete MD5 password authentication: " "no such is configured"); return (false); #endif } XLOG_ASSERT(md5_ah != NULL); // // Remove the key // if (md5_ah->remove_key(key_id, error_msg) != true) { error_msg = c_format("Invalid MD5 key ID %u: %s", XORP_UINT_CAST(key_id), error_msg.c_str()); return (false); } // // If the last key, then install an empty handler and delete the MD5 // authentication handler. // if (md5_ah->empty()) { set_method(NullAuthHandler::auth_type_name()); } return (true); } xorp/ospf/xrl_io.hh0000664000076400007640000003072411643705557014467 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/ospf/xrl_io.hh,v 1.35 2008/11/14 12:44:19 bms Exp $ #ifndef __OSPF_XRL_IO_HH__ #define __OSPF_XRL_IO_HH__ #include "libxipc/xrl_router.hh" #include "libfeaclient/ifmgr_xrl_mirror.hh" #include "policy/backend/policytags.hh" #include "io.hh" class EventLoop; template class XrlIO; /** * XXX - This should be moved to its own file. * * Queue route adds and deletes to the RIB. */ template class XrlQueue { public: XrlQueue(EventLoop& eventloop, XrlRouter& xrl_router); void set_io(XrlIO *io) { _io = io; } void queue_add_route(string ribname, const IPNet& net, const A& nexthop, uint32_t nexthop_id, uint32_t metric, const PolicyTags& policytags); void queue_delete_route(string ribname, const IPNet& net); bool busy(); private: static const size_t WINDOW = 100; // Maximum number of XRLs // allowed in flight. XrlIO *_io; EventLoop& _eventloop; XrlRouter& _xrl_router; struct Queued { bool add; string ribname; IPNet net; A nexthop; uint32_t nexthop_id; uint32_t metric; string comment; PolicyTags policytags; }; deque _xrl_queue; size_t _flying; //XRLs currently in flight /** * Maximum number in flight */ bool maximum_number_inflight() const { return _flying >= WINDOW; } /** * Start the transmission of XRLs to tbe RIB. */ void start(); /** * The specialised method called by sendit to deal with IPv4/IPv6. * * @param q the queued command. * @param protocol "ospf" * @return True if the add/delete was queued. */ bool sendit_spec(Queued& q, const char *protocol); EventLoop& eventloop() const; void route_command_done(const XrlError& error, const string comment); }; /** * Concrete implementation of IO using XRLs. */ template class XrlIO : public IO, public IfMgrHintObserver, public ServiceChangeObserverBase { public: XrlIO(EventLoop& eventloop, XrlRouter& xrl_router, const string& feaname, const string& ribname) : _eventloop(eventloop), _xrl_router(xrl_router), _feaname(feaname), _ribname(ribname), _component_count(0), _ifmgr(eventloop, feaname.c_str(), _xrl_router.finder_address(), _xrl_router.finder_port()), _rib_queue(eventloop, xrl_router) { _ifmgr.set_observer(this); _ifmgr.attach_hint_observer(this); _rib_queue.set_io(this); // // TODO: for now startup inside the constructor. Ideally, we want // to startup after the FEA birth event. // // startup(); } ~XrlIO() { // // TODO: for now shutdown inside the destructor. Ideally, we want // to shutdown gracefully before we call the destructor. // // shutdown(); _ifmgr.detach_hint_observer(this); _ifmgr.unset_observer(this); } /** * Startup operation. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int startup() { // // XXX: when the startup is completed, // IfMgrHintObserver::tree_complete() will be called. // if (_ifmgr.startup() != XORP_OK) { ServiceBase::set_status(SERVICE_FAILED); return (XORP_ERROR); } register_rib(); component_up("startup"); return (XORP_OK); } /** * Shutdown operation. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int shutdown() { // // XXX: when the shutdown is completed, XrlIO::status_change() // will be called. // unregister_rib(); component_down("shutdown"); return (_ifmgr.shutdown()); } /** * Called when internal subsystem comes up. */ void component_up(string /*name*/) { // fprintf(stderr, "Component: %s count %d\n", name.c_str(), // _component_count + 1); _component_count++; // XXX - Should really get every component to register at // initialisation time and track the individual // status. Simpler to uncomment the printfs and track the count. #ifdef HAVE_IPV6 if (4 == _component_count) ServiceBase::set_status(SERVICE_RUNNING); #else if (3 == _component_count) ServiceBase::set_status(SERVICE_RUNNING); #endif } /** * Called when internal subsystem goes down. */ void component_down(string /*name*/) { // fprintf(stderr, "Component: %s count %d\n", name.c_str(), // _component_count - 1); _component_count--; if (0 == _component_count) ServiceBase::set_status(SERVICE_SHUTDOWN); else ServiceBase::set_status(SERVICE_SHUTTING_DOWN); } /** * Receiver Raw frames. */ void recv(const string& interface, const string& vif, A src, A dst, uint8_t ip_protocol, int32_t ip_ttl, int32_t ip_tos, bool ip_router_alert, bool ip_internet_control, const vector& payload); /** * Send Raw frames. */ bool send(const string& interface, const string& vif, A dst, A src, int ttl, uint8_t* data, uint32_t len); /** * Enable the interface/vif to receive frames. */ bool enable_interface_vif(const string& interface, const string& vif); /** * Disable this interface/vif from receiving frames. */ bool disable_interface_vif(const string& interface, const string& vif); /** * Test whether an interface is enabled. * * @param interface the name of the interface to test. * @return true if it exists and is enabled, otherwise false. */ bool is_interface_enabled(const string& interface) const; /** * Test whether an interface/vif is enabled. * * @param interface the name of the interface to test. * @param vif the name of the vif to test. * @return true if it exists and is enabled, otherwise false. */ bool is_vif_enabled(const string& interface, const string& vif) const; /** * Test whether an interface/vif/address is enabled. * * @param interface the name of the interface to test. * @param vif the name of the vif to test. * @param address the address to test. * @return true if it exists and is enabled, otherwise false. */ bool is_address_enabled(const string& interface, const string& vif, const A& address) const; /** * Get all addresses associated with this interface/vif. * * @param interface the name of the interface * @param vif the name of the vif * @param addresses (out argument) list of associated addresses * * @return true if there are no errors. */ bool get_addresses(const string& interface, const string& vif, list& addresses) const; /** * Get a link local address for this interface/vif if available. * * @param interface the name of the interface * @param vif the name of the vif * @param address (out argument) set if address is found. * * @return true if a link local address is available. */ bool get_link_local_address(const string& interface, const string& vif, A& address); /** * Get the interface ID. * * @param interface the name of the interface. * @param interface_id the value if found.. * @return true if the interface ID has been found.. */ bool get_interface_id(const string& interface, uint32_t& interface_id); /** * Obtain the subnet prefix length for an interface/vif/address. * * @param interface the name of the interface. * @param vif the name of the vif. * @param address the address. * @return the subnet prefix length for the address. */ uint32_t get_prefix_length(const string& interface, const string& vif, A address); /** * Obtain the MTU for an interface. * * @param the name of the interface. * @return the mtu for the interface. */ uint32_t get_mtu(const string& interface); /** * On the interface/vif join this multicast group. */ bool join_multicast_group(const string& interface, const string& vif, A mcast); /** * On the interface/vif leave this multicast group. */ bool leave_multicast_group(const string& interface, const string& vif, A mcast); /** * Register with the RIB. */ void register_rib(); /** * Remove registration from the RIB. */ void unregister_rib(); void rib_command_done(const XrlError& error, bool up, const char *comment); /** * Add route to RIB. * * @param net network * @param nexthop * @param nexthop_id interface ID towards the nexthop * @param metric to network * @param equal true if this in another route to the same destination. * @param discard true if this is a discard route. * @param policytags policy info to the RIB. */ bool add_route(IPNet net, A nexthop, uint32_t nexthop_id, uint32_t metric, bool equal, bool discard, const PolicyTags& policytags); /** * Replace route in RIB. * * @param net network * @param nexthop * @param nexthop_id interface ID towards the nexthop * @param metric to network * @param equal true if this in another route to the same destination. * @param discard true if this is a discard route. * @param policytags policy info to the RIB. */ bool replace_route(IPNet net, A nexthop, uint32_t nexthop_id, uint32_t metric, bool equal, bool discard, const PolicyTags& policytags); /** * Delete route from RIB. */ bool delete_route(IPNet net); private: /** * A method invoked when the status of a service changes. * * @param service the service whose status has changed. * @param old_status the old status. * @param new_status the new status. */ void status_change(ServiceBase* service, ServiceStatus old_status, ServiceStatus new_status) { if (old_status == new_status) return; if (SERVICE_RUNNING == new_status) component_up(service->service_name()); if (SERVICE_SHUTDOWN == new_status) component_down(service->service_name()); } /** * Obtain a pointer to the interface manager service base. * * @return a pointer to the interface manager service base. */ const ServiceBase* ifmgr_mirror_service_base() const { return dynamic_cast(&_ifmgr); } /** * Obtain a reference to the interface manager's interface tree. * * @return a reference to the interface manager's interface tree. */ const IfMgrIfTree& ifmgr_iftree() const { return _ifmgr.iftree(); } /** * An IfMgrHintObserver method invoked when the initial interface tree * information has been received. */ void tree_complete(); /** * An IfMgrHintObserver method invoked whenever the interface tree * information has been changed. */ void updates_made(); // // XRL callbacks // void send_cb(const XrlError& xrl_error, string interface, string vif); void enable_interface_vif_cb(const XrlError& xrl_error, string interface, string vif); void disable_interface_vif_cb(const XrlError& xrl_error, string interface, string vif); void join_multicast_group_cb(const XrlError& xrl_error, string interface, string vif); void leave_multicast_group_cb(const XrlError& xrl_error, string interface, string vif); EventLoop& _eventloop; XrlRouter& _xrl_router; string _feaname; string _ribname; uint32_t _component_count; IfMgrXrlMirror _ifmgr; XrlQueue _rib_queue; // // A local copy with the interface state information // IfMgrIfTree _iftree; }; #endif // __OSPF_XRL_IO_HH__ xorp/ospf/io.hh0000664000076400007640000002063311421137511013557 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/ospf/io.hh,v 1.31 2008/11/14 12:44:19 bms Exp $ #ifndef __OSPF_IO_HH__ #define __OSPF_IO_HH__ /** * An abstract class that defines packet reception and * transmission. The details of how packets are received or transmitted * are therefore hidden from the internals of the OSPF code. */ template class IO : public ServiceBase { public: IO() : _ip_router_alert(false) {} virtual ~IO() {} /** * Get OSPF protocol number. */ uint16_t get_ip_protocol_number() const { return OspfTypes::IP_PROTOCOL_NUMBER; } /** * Send Raw frames. */ virtual bool send(const string& interface, const string& vif, A dst, A src, int ttl, uint8_t* data, uint32_t len) = 0; /** * Send router alerts in IP packets? */ bool set_ip_router_alert(bool alert) { _ip_router_alert = alert; return true; } /** * Get router alert state. */ bool get_ip_router_alert() const { return _ip_router_alert; } typedef typename XorpCallback6::RefPtr ReceiveCallback; /** * Register for receiving raw frames. */ void register_receive(ReceiveCallback cb) { _receive_cb = cb; } /** * Enable the interface/vif to receive frames. */ virtual bool enable_interface_vif(const string& interface, const string& vif) = 0; /** * Disable this interface/vif from receiving frames. */ virtual bool disable_interface_vif(const string& interface, const string& vif) = 0; /** * Test whether this interface is enabled. * * @return true if it exists and is enabled, otherwise false. */ virtual bool is_interface_enabled(const string& interface) const = 0; /** * Test whether this interface/vif is enabled. * * @return true if it exists and is enabled, otherwise false. */ virtual bool is_vif_enabled(const string& interface, const string& vif) const = 0; /** * Test whether this interface/vif/address is enabled. * * @return true if it exists and is enabled, otherwise false. */ virtual bool is_address_enabled(const string& interface, const string& vif, const A& address) const = 0; typedef typename XorpCallback2::RefPtr InterfaceStatusCb; typedef typename XorpCallback3::RefPtr VifStatusCb; typedef typename XorpCallback4::RefPtr AddressStatusCb; /** * Add a callback for tracking the interface status. * * The callback will be invoked whenever the status of the interface * is changed from disabled to enabled or vice-versa. * * @param cb the callback to register. */ void register_interface_status(InterfaceStatusCb cb) { _interface_status_cb = cb; } /** * Add a callback for tracking the interface/vif status. * * The callback will be invoked whenever the status of the interface/vif * is changed from disabled to enabled or vice-versa. * * @param cb the callback to register. */ void register_vif_status(VifStatusCb cb) { _vif_status_cb = cb; } /** * Add a callback for tracking the interface/vif/address status. * * The callback will be invoked whenever the status of the tuple * (interface, vif, address) is changed from disabled to enabled * or vice-versa. * * @param cb the callback to register. */ void register_address_status(AddressStatusCb cb) { _address_status_cb = cb; } /** * Get all addresses associated with this interface/vif. * * @param interface the name of the interface * @param vif the name of the vif * @param addresses (out argument) list of associated addresses * * @return true if there are no errors. */ virtual bool get_addresses(const string& interface, const string& vif, list& addresses) const = 0; /** * Get a link local address for this interface/vif if available. * * @param interface the name of the interface * @param vif the name of the vif * @param address (out argument) set if address is found. * * @return true if a link local address is available. */ virtual bool get_link_local_address(const string& interface, const string& vif, A& address) = 0; /** * @return the interface id for this interface. */ virtual bool get_interface_id(const string& interface, uint32_t& interface_id) = 0; /** * @return prefix length for this address. */ virtual uint32_t get_prefix_length(const string& interface, const string& vif, A address) = 0; /** * @return the mtu for this interface. */ virtual uint32_t get_mtu(const string& interface) = 0; /** * On the interface/vif join this multicast group. */ virtual bool join_multicast_group(const string& interface, const string& vif, A mcast) = 0; /** * On the interface/vif leave this multicast group. */ virtual bool leave_multicast_group(const string& interface, const string& vif, A mcast) = 0; /** * Add route * * @param net network * @param nexthop * @param nexthop_id interface ID towards the nexthop * @param metric to network * @param equal true if this in another route to the same destination. * @param discard true if this is a discard route. * @param policytags policy info to the RIB. */ virtual bool add_route(IPNet net, A nexthop, uint32_t nexthop_id, uint32_t metric, bool equal, bool discard, const PolicyTags& policytags) = 0; /** * Replace route * * @param net network * @param nexthop * @param nexthop_id interface ID towards the nexthop * @param metric to network * @param equal true if this in another route to the same destination. * @param discard true if this is a discard route. * @param policytags policy info to the RIB. */ virtual bool replace_route(IPNet net, A nexthop, uint32_t nexthop_id, uint32_t metric, bool equal, bool discard, const PolicyTags& policytags) = 0; /** * Delete route */ virtual bool delete_route(IPNet net) = 0; /** * Store a mapping of the OSPF internal interface ID to * interface/vif. This will be required by when installing a route. */ void set_interface_mapping(uint32_t interface_id, const string& interface, const string& vif) { interface_vif iv; iv._interface_name = interface; iv._vif_name = vif; _interface_vif[interface_id] = iv; } /** * Given an OSPF internal interface ID return the interface/vif. */ bool get_interface_vif_by_interface_id(uint32_t interface_id, string& interface, string& vif) { if (0 == _interface_vif.count(interface_id)) return false; interface_vif iv = _interface_vif[interface_id]; interface = iv._interface_name; vif = iv._vif_name; return true; } protected: ReceiveCallback _receive_cb; InterfaceStatusCb _interface_status_cb; VifStatusCb _vif_status_cb; AddressStatusCb _address_status_cb; bool _ip_router_alert; struct interface_vif { string _interface_name; string _vif_name; }; map _interface_vif; }; #endif // __OSPF_IO_HH__ xorp/ospf/transmit.hh0000664000076400007640000000634011421137511015010 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/ospf/transmit.hh,v 1.8 2008/10/02 21:57:50 bms Exp $ #ifndef __OSPF_TRANSMIT_HH__ #define __OSPF_TRANSMIT_HH__ /** * The base class for all transmissions. * * Typically a large number of Transmit objects could be queued for * transmission. By the time a Transmit object is scheduled for * transmission circumstances could have changed. It may no longer be * appropriate to transmit this packet. Therefore allow a transmit * object to become invalid. A single transmit object can generate a * number of packets. For example for the database synchronisation * process. * */ template class Transmit { public: typedef ref_ptr TransmitRef; virtual ~Transmit() {} /** * Is this object still valid? * * @return True if this transmit object is still valid. */ virtual bool valid() = 0; /** * A transmit object may be able to generate multiple packets; not * just one. * * @return True if this object can be invoked multiple times? */ virtual bool multiple() = 0; /** * Make a copy of this object. If the same data is being sent to * multiple locations, provide a mechanism to make a copy for each * location. */ virtual TransmitRef clone() = 0; /** * Generate a packet for transmission. * * @param len length of the encoded packet. * * @return A pointer that must be delete'd. */ virtual uint8_t *generate(size_t &len) = 0; /** * @return the destination address of this packet. */ virtual A destination() = 0; /** * @return the source address of this packet. */ virtual A source() = 0; }; /** * A transmit object that sends fixed data. */ template class SimpleTransmit : public Transmit { public: SimpleTransmit(vector& pkt, A dst, A src) : _dst(dst), _src(src) { _pkt.resize(pkt.size()); memcpy(&_pkt[0], &pkt[0], pkt.size()); } bool valid() { return true; } bool multiple() { return false; } typename Transmit::TransmitRef clone() { return this; } uint8_t *generate(size_t &len) { len = _pkt.size(); return &_pkt[0]; } A destination() { return _dst; } A source() { return _src; } private: vector _pkt; A _dst; // Destination address A _src; // Source address }; #endif // __OSPF_TRANSMIT_HH__ xorp/ospf/lsa1.data0000664000076400007640000000070211421137511014315 0ustar greearbgreearb/* Age 0x0,0x01,*/ /* Options */ 0x02, /* LSA Type */ 0x05, /* Link State ID */ 0x02, 0x02, 0x03, 0x84, /* Advertising Router */ 0x0a, 0x00, 0x00, 0x32, /* LS Sequence Number */ 0x80, 0x00, 0x00, 0x01, /* LS Checksum */ 0xff, 0xd0, /* Length */ 0x00, 0x24, /* Netmask */ 0xff, 0xff, 0xff, 0xfc, /* External Type */ 0x80, /* Metric */ 0x00, 0x00, 0x1, /* Forwarding Address */ 0x0a, 0x00, 0x00, 0x32, /* External Route Tag */ 0x00, 0x00 xorp/ospf/routing_table.cc0000664000076400007640000005160011703345405016000 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2012 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #include "ospf_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/callback.hh" #include "libxorp/ipv4.hh" #include "libxorp/ipv6.hh" #include "libxorp/ipnet.hh" #include "libxorp/status_codes.h" #include "libxorp/service.hh" #include "libxorp/eventloop.hh" #include #include "libproto/spt.hh" #include "ospf.hh" #include "delay_queue.hh" #include "vertex.hh" #include "area_router.hh" template void RoutingTable::begin(OspfTypes::AreaID area) { debug_msg("area %s\n", pr_id(area).c_str()); XLOG_ASSERT(!_in_transaction); _in_transaction = true; _adv.clear_area(area); delete _previous; _previous = _current; _current = new Trie >; // It is possible that multiple areas have added route to the // routing table. This area is about to add or replace all its // routes again. All routes from other areas must be preserved. if (0 == _previous) // First time return; typename Trie >::iterator tip; for (tip = _previous->begin(); tip != _previous->end(); tip++) { // This should be a copy not a reference. InternalRouteEntry ire = tip.payload(); debug_msg("ire %s\n", cstring(ire)); // If this entry contains a route from this area delete it. bool winner_changed; ire.delete_entry(area, winner_changed); // If there are no other routes don't put a copy in current. if (ire.empty()) { debug_msg("empty ire %s only this area was present\n", cstring(ire)); continue; } debug_msg("kept ire %s as other areas are present\n", cstring(ire)); _current->insert(tip.key(), ire); } } template bool RoutingTable::add_entry(OspfTypes::AreaID area, IPNet net, const RouteEntry& rt, const char* msg) { debug_msg("area %s %s %s msg: %s\n", pr_id(area).c_str(), cstring(net), const_cast&>(rt).str().c_str(), msg); XLOG_ASSERT(_in_transaction); XLOG_ASSERT(area == rt.get_area()); XLOG_ASSERT(rt.get_directly_connected() || rt.get_nexthop() != A::ZERO()); bool status = true; // This router entry needs to be placed in the table. // In the OSPFv2 IPv4 case the {router ID}/32 is also placed in // the table as it has to be unique and makes for easy lookup. // In the OSPFv3 case lookups of a router will be done by router // ID only, so don't make an entry by net, plus there is no way of // guaranteeing a unique net as the net will be a link-local address. if (rt.get_destination_type() == OspfTypes::Router) { string dbg(msg); dbg += ": RT::add_entry"; status = _adv.add_entry(area, rt.get_router_id(), rt, dbg.c_str()); switch(_ospf.get_version()) { case OspfTypes::V2: break; case OspfTypes::V3: return true; } } typename Trie >::iterator i; i = _current->lookup_node(net); if (_current->end() == i) { InternalRouteEntry ire; i = _current->insert(net, ire); // i = _current->lookup_node(net); } InternalRouteEntry& irentry = i.payload(); irentry.add_entry(area, rt); return status; } template bool RoutingTable::replace_entry(OspfTypes::AreaID area, IPNet net, const RouteEntry& rt) { debug_msg("area %s %s\n", pr_id(area).c_str(), cstring(net)); XLOG_ASSERT(_in_transaction); bool status = true; if (rt.get_destination_type() == OspfTypes::Router) { status = _adv.replace_entry(area, rt.get_router_id(), rt, "RT::replace_entry"); switch(_ospf.get_version()) { case OspfTypes::V2: break; case OspfTypes::V3: return true; } } typename Trie >::iterator i; i = _current->lookup_node(net); if (_current->end() == i) { return add_entry(area, net, rt, __PRETTY_FUNCTION__); } InternalRouteEntry& irentry = i.payload(); irentry.replace_entry(area, rt); return status; } template bool RoutingTable::lookup_entry(A router, RouteEntry& rt) { debug_msg("%s\n", cstring(router)); if (0 == _current) return false; IPNet net(router, A::ADDR_BITLEN); typename Trie >::iterator i; i = _current->lookup_node(net); if (_current->end() == i) return false; InternalRouteEntry& irentry = i.payload(); rt = irentry.get_entry(); return true; } template bool RoutingTable::lookup_entry(OspfTypes::AreaID area, A router, RouteEntry& rt) { debug_msg("area %s %s\n", pr_id(area).c_str(), cstring(router)); if (0 == _current) return false; IPNet net(router, A::ADDR_BITLEN); typename Trie >::iterator i; i = _current->lookup_node(net); if (_current->end() == i) return false; InternalRouteEntry& irentry = i.payload(); return irentry.get_entry(area, rt); } template bool RoutingTable::lookup_entry(IPNet net, RouteEntry& rt) { debug_msg("%s\n", cstring(net)); if (0 == _current) return false; typename Trie >::iterator i; i = _current->lookup_node(net); if (_current->end() == i) return false; InternalRouteEntry& irentry = i.payload(); rt = irentry.get_entry(); return true; } template bool RoutingTable::lookup_entry(OspfTypes::AreaID area, IPNet net, RouteEntry& rt) { debug_msg("%s\n", cstring(net)); if (0 == _current) return false; typename Trie >::iterator i; i = _current->lookup_node(net); if (_current->end() == i) return false; InternalRouteEntry& irentry = i.payload(); return irentry.get_entry(area, rt); } template bool RoutingTable::lookup_entry_by_advertising_router(OspfTypes::AreaID area, uint32_t adv, RouteEntry& rt) { debug_msg("area %s %s\n", pr_id(area).c_str(), pr_id(adv).c_str()); if (0 == _current) return false; return _adv.lookup_entry(area, adv, rt); } template bool RoutingTable::longest_match_entry(A nexthop, RouteEntry& rt) { debug_msg("%s\n", cstring(nexthop)); if (0 == _current) return false; typename Trie >::iterator i; i = _current->find(nexthop); if (_current->end() == i) return false; InternalRouteEntry& irentry = i.payload(); rt = irentry.get_entry(); return true; } template void RoutingTable::end() { debug_msg("\n"); XLOG_ASSERT(_in_transaction); _in_transaction = false; typename Trie >::iterator tip; typename Trie >::iterator tic; // If there is no previous routing table just install the current // table and return. if (0 == _previous) { for (tic = _current->begin(); tic != _current->end(); tic++) { RouteEntry& rt = tic.payload().get_entry(); if (!add_route(rt.get_area(), tic.key(), rt.get_nexthop(), rt.get_cost(), rt, true)) { XLOG_WARNING("Add of %s failed", cstring(tic.key())); } } return; } // Sweep through the previous table looking up routes in the // current table. If no route is found then: delete route. // Sweep through the current table looking up entries in the // previous table. // - No route found: add route. // - Route Found // - If the routes match do nothing. // - If the routes are different: replace route. for (tip = _previous->begin(); tip != _previous->end(); tip++) { if (_current->end() == _current->lookup_node(tip.key())) { RouteEntry& rt = tip.payload().get_entry(); if (!delete_route(rt.get_area(), tip.key(), rt, true)) { XLOG_WARNING("Delete of %s failed", cstring(tip.key())); } } } for (tic = _current->begin(); tic != _current->end(); tic++) { tip = _previous->lookup_node(tic.key()); RouteEntry& rt = tic.payload().get_entry(); if (_previous->end() == tip) { if (!add_route(rt.get_area(), tic.key(), rt.get_nexthop(), rt.get_cost(), rt, true)) { XLOG_WARNING("Add of %s failed", cstring(tic.key())); } } else { RouteEntry& rt_previous = tip.payload().get_entry(); if (rt.get_nexthop() != rt_previous.get_nexthop() || rt.get_cost() != rt_previous.get_cost()) { if (!replace_route(rt.get_area(), tip.key(), rt.get_nexthop(), rt.get_cost(), rt, rt_previous, rt_previous.get_area())) { XLOG_WARNING("Replace of %s failed", cstring(tip.key())); } } else { rt.set_filtered(rt_previous.get_filtered()); } } } } template void RoutingTable::remove_area(OspfTypes::AreaID area) { XLOG_ASSERT(!_in_transaction); if (0 == _current) return; // Sweep through the current table and delete any routes that came // from this area. // XXX - Should consider this a candidate for running as a // background task. typename Trie >::iterator tic; for (tic = _current->begin(); tic != _current->begin(); tic++) { InternalRouteEntry& ire = tic.payload(); RouteEntry& rt = ire.get_entry(); // If the winning entry is for this area delete it from the // routing table. if (rt.get_area() == area) delete_route(area, tic.key(), rt, true); // Unconditionally remove the area, it may be a losing route. bool winner_changed; if (!ire.delete_entry(area, winner_changed)) continue; // No more route entries exist so remove this internal entry. if (ire.empty()) { _current->erase(tic); continue; } // If a new winner has emerged add it to the routing table. if (winner_changed) { add_route(area, tic.key(), rt.get_nexthop(), rt.get_cost(), ire.get_entry(), true); } } } template bool RoutingTable::add_route(OspfTypes::AreaID area, IPNet net, A nexthop, uint32_t metric, RouteEntry& rt, bool summaries) { debug_msg("ADD ROUTE area %s net %s nexthop %s metric %u\n", pr_id(area).c_str(), cstring(net), cstring(nexthop), metric); bool result = true; if (!rt.get_discard()) { PolicyTags policytags; bool accepted = do_filtering(net, nexthop, metric, rt, policytags); rt.set_filtered(!accepted); if (accepted) result = _ospf.add_route(net, nexthop, rt.get_nexthop_id(), metric, false /* equal */, false /* discard */, policytags); } else { XLOG_WARNING("TBD - installing discard routes"); result = false; } if (summaries) _ospf.get_peer_manager().summary_announce(area, net, rt); return result; } template bool RoutingTable::delete_route(OspfTypes::AreaID area, IPNet net, RouteEntry& rt, bool summaries) { debug_msg("DELETE ROUTE area %s %s filtered %s\n", pr_id(area).c_str(), cstring(net), bool_c_str(rt.get_filtered())); bool result; if (!rt.get_discard()) { if (!rt.get_filtered()) result = _ospf.delete_route(net); else result = true; } else { XLOG_WARNING("TBD - removing discard routes"); result = false; } if (summaries) _ospf.get_peer_manager().summary_withdraw(area, net, rt); return result; } template bool RoutingTable::replace_route(OspfTypes::AreaID area, IPNet net, A nexthop, uint32_t metric, RouteEntry& rt, RouteEntry& previous_rt, OspfTypes::AreaID previous_area) { debug_msg("REPLACE ROUTE area %s %s\n", pr_id(area).c_str(), cstring(net)); bool result = delete_route(previous_area, net, previous_rt, false); if (!result) XLOG_WARNING("Failed to delete: %s", cstring(net)); result = add_route(area, net, nexthop, metric, rt, false); _ospf.get_peer_manager().summary_replace(area, net, rt, previous_rt, previous_area); return result; } template void RoutingTable::push_routes() { typename Trie >::iterator tic; if (0 == _current) return; for (tic = _current->begin(); tic != _current->end(); tic++) { RouteEntry& rt = tic.payload().get_entry(); if (rt.get_discard()) continue; PolicyTags policytags; IPNet net = tic.key(); A nexthop = rt.get_nexthop(); uint32_t nexthop_id = rt.get_nexthop_id(); uint32_t metric = rt.get_cost(); bool accepted = do_filtering(net, nexthop, metric, rt, policytags); if (accepted) { if (!rt.get_filtered()) { _ospf.replace_route(net, nexthop, nexthop_id, metric, false /* equal */, false /* discard */, policytags); } else { _ospf.add_route(net, nexthop, nexthop_id, metric, false /* equal */, false /* discard */, policytags); } } else { if (!rt.get_filtered()) { _ospf.delete_route(net); } } rt.set_filtered(!accepted); } } template bool RoutingTable::do_filtering(IPNet& net, A& nexthop, uint32_t& metric, RouteEntry& rt, PolicyTags& policytags) { // The OSPF routing table needs to contain directly connected // routes and routes to routers to satisfy requirements for // AS-External-LSAs and Summary-LSAs. Drop them here so they don't // make it the the RIB. if (/*net.contains(nexthop) ||*/ OspfTypes::Router == rt.get_destination_type() || rt.get_directly_connected()) return false; // The import policy filter. try { bool e_bit; uint32_t tag; bool tag_set; OspfVarRW varrw(net, nexthop, metric, e_bit, tag, tag_set, policytags); // Import filtering bool accepted; debug_msg("[OSPF] Running filter: %s on route: %s\n", filter::filter2str(filter::IMPORT), cstring(net)); XLOG_TRACE(_ospf.trace()._import_policy, "[OSPF] Running filter: %s on route: %s\n", filter::filter2str(filter::IMPORT), cstring(net)); accepted = _ospf.get_policy_filters(). run_filter(filter::IMPORT, varrw); // Route Rejected if (!accepted) return accepted; OspfVarRW varrw2(net, nexthop, metric, e_bit, tag, tag_set, policytags); // Export source-match filtering debug_msg("[OSPF] Running filter: %s on route: %s\n", filter::filter2str(filter::EXPORT_SOURCEMATCH), cstring(net)); XLOG_TRACE(_ospf.trace()._import_policy, "[OSPF] Running filter: %s on route: %s\n", filter::filter2str(filter::EXPORT_SOURCEMATCH), cstring(net)); _ospf.get_policy_filters(). run_filter(filter::EXPORT_SOURCEMATCH, varrw2); return accepted; } catch(const PolicyException& e) { XLOG_WARNING("PolicyException: %s", e.str().c_str()); return false; } return true; } template bool InternalRouteEntry::add_entry(OspfTypes::AreaID area, const RouteEntry& rt) { // An entry for this *area* should not already exist. XLOG_ASSERT(0 == _entries.count(area)); if (0 == _entries.size()) { _entries[area] = rt; reset_winner(); return true; } // Add the entry and compute the winner. _entries[area] = rt; reset_winner(); return true; } template bool InternalRouteEntry::replace_entry(OspfTypes::AreaID area, const RouteEntry& rt) { bool winner_changed; delete_entry(area, winner_changed); return add_entry(area, rt); } template bool InternalRouteEntry::delete_entry(OspfTypes::AreaID area, bool& winner_changed) { if (0 == _entries.count(area)) return false; _entries.erase(_entries.find(area)); winner_changed = reset_winner(); return true; } template RouteEntry& InternalRouteEntry::get_entry() const { XLOG_ASSERT(0 != _winner); return *_winner; } template bool InternalRouteEntry::get_entry(OspfTypes::AreaID area, RouteEntry& rt) const { typename map >::const_iterator i; if (_entries.end() == (i = _entries.find(area))) return false; rt = i->second; return true; } template bool InternalRouteEntry::reset_winner() { RouteEntry *old_winner = _winner; _winner = 0; typename map >::iterator i; for (i = _entries.begin(); i != _entries.end(); i++) { if (i == _entries.begin()) { _winner = &(i->second); continue; } RouteEntry& comp = i->second; if (comp.get_path_type() < _winner->get_path_type()) { _winner = ∁ continue; } if (comp.get_path_type() == _winner->get_path_type()) { if (comp.get_cost() < _winner->get_cost()) { _winner = ∁ continue; } if (comp.get_cost() == _winner->get_cost()) { if (comp.get_area() > _winner->get_area()) _winner = ∁ continue; } } } return _winner != old_winner; } template string InternalRouteEntry::str() { string output; typename map >::iterator i; for (i = _entries.begin(); i != _entries.end(); i++) { output += "Area: " + pr_id(i->first) + " " + i->second.str() + " "; if (&(i->second) == _winner) output += "winner "; } return output; } template void Adv::clear_area(OspfTypes::AreaID area) { debug_msg("Clearing area %s\n", pr_id(area).c_str()); if (0 == _adv.count(area)) return; typename ADV::iterator i = _adv.find(area); XLOG_ASSERT(_adv.end() != i); i->second.clear(); } template bool Adv::add_entry(OspfTypes::AreaID area, uint32_t adv, const RouteEntry& rt, const char* dbg) { UNUSED(dbg); // if logging is compiled out debug_msg("Add entry area %s adv %s\n", pr_id(area).c_str(), pr_id(adv).c_str()); XLOG_ASSERT(dynamic_cast(rt.get_lsa().get())|| dynamic_cast(rt.get_lsa().get())); if (0 == _adv.count(area)) { AREA a; a[adv] = rt; _adv[area] = a; return true; } typename ADV::iterator i = _adv.find(area); XLOG_ASSERT(_adv.end() != i); typename AREA::iterator j = i->second.find(adv); if (i->second.end() != j) { XLOG_WARNING("An entry with this advertising router already exists, area:" " %s adv: %s dbg: %s existing: %s\nrt->LSA:\n%s", pr_id(area).c_str(), pr_id(adv).c_str(), dbg, cstring(*(j->second.get_lsa())), cstring(*rt.get_lsa())); return false; } AREA& aref = _adv[area]; aref[adv] = rt; return true; } template bool Adv::replace_entry(OspfTypes::AreaID area, uint32_t adv, const RouteEntry& rt, const char* dbg) { UNUSED(dbg); // if logging is compiled out debug_msg("Add entry area %s adv %s\n", pr_id(area).c_str(), pr_id(adv).c_str()); XLOG_ASSERT(dynamic_cast(rt.get_lsa().get())|| dynamic_cast(rt.get_lsa().get())); if (0 == _adv.count(area)) { XLOG_WARNING("There should already be an entry for this area, dbg: %s rt->LSA:\n%s", dbg, cstring(*rt.get_lsa())); AREA a; a[adv] = rt; _adv[area] = a; return false; } bool status = true; typename ADV::iterator i = _adv.find(area); XLOG_ASSERT(_adv.end() != i); typename AREA::iterator j = i->second.find(adv); if (i->second.end() == j) { XLOG_WARNING("There should already be an entry with this adv, dbg: %s rt->LSA:\n%s", dbg, cstring(*rt.get_lsa())); status = false; } AREA& aref = _adv[area]; aref[adv] = rt; return status; } template bool Adv::lookup_entry(OspfTypes::AreaID area, uint32_t adv, RouteEntry& rt) const { debug_msg("Lookup entry area %s adv %s\n", pr_id(area).c_str(), pr_id(adv).c_str()); if (0 == _adv.count(area)) { return false; } typename ADV::const_iterator i = _adv.find(area); XLOG_ASSERT(_adv.end() != i); typename AREA::const_iterator j = i->second.find(adv); if (i->second.end() == j) return false; rt = j->second; debug_msg("Found area %s adv %s\n", pr_id(area).c_str(), pr_id(adv).c_str()); return true; } template class RouteEntry; template class RouteEntry; template class InternalRouteEntry; template class InternalRouteEntry; template class Adv; template class Adv; template class RoutingTable; template class RoutingTable; xorp/ospf/peer_manager.cc0000664000076400007640000015726011631770031015575 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #include "ospf_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/callback.hh" #include "libxorp/ipv4.hh" #include "libxorp/ipv6.hh" #include "libxorp/ipnet.hh" #include "libxorp/status_codes.h" #include "libxorp/service.hh" #include "libxorp/eventloop.hh" #include "libproto/spt.hh" #include "ospf.hh" #include "packet.hh" #include "delay_queue.hh" #include "vertex.hh" #include "area_router.hh" #include "auth.hh" #include "peer.hh" #include "peer_manager.hh" #ifndef VLINK_DEF_TTL #define VLINK_DEF_TTL 64 // Default TTL for virtual link packets. #endif template PeerManager::~PeerManager() { // Remove all the areas, this should cause all the peers to be // removed. Every call to destroy_area_router will change _areas, // so call begin again each time. for(;;) { typename map *>::iterator i; i = _areas.begin(); if (i == _areas.end()) break; destroy_area_router((*i).first); } XLOG_ASSERT(_pmap.empty()); XLOG_ASSERT(_peers.empty()); XLOG_ASSERT(_areas.empty()); } template bool PeerManager::check_area_type(OspfTypes::AreaID area, OspfTypes::AreaType area_type) { debug_msg("Area %s Type %s\n", pr_id(area).c_str(), pp_area_type(area_type).c_str()); bool allowed = true; if (OspfTypes::BACKBONE == area) { switch(area_type) { case OspfTypes::NORMAL: break; case OspfTypes::STUB: /*FALLTHROUGH*/ case OspfTypes::NSSA: allowed = false; break; } } return allowed; } template bool PeerManager::create_area_router(OspfTypes::AreaID area, OspfTypes::AreaType area_type, bool permissive) { debug_msg("Area %s Type %s\n", pr_id(area).c_str(), pp_area_type(area_type).c_str()); // Check this area doesn't already exist. if (0 != _areas.count(area)) { XLOG_WARNING("Area %s already exists\n", pr_id(area).c_str()); if (permissive) return true; return false; } if (!check_area_type(area, area_type)) { XLOG_ERROR("Area %s cannot be %s", pr_id(area).c_str(), pp_area_type(area_type).c_str()); return false; } track_area_count(area_type, true /* increment */); bool old_border_router_state = area_border_router_p(); _areas[area] = new AreaRouter(_ospf, area, area_type); _areas[area]->startup(); // If we just became a border router force an updated Router-LSA // to be generated by the first area. // XXX Should subsume the refreshing of the Router-LSA into the // generic area border router transition method. if (area_border_router_p() != old_border_router_state) { if (!_ospf.get_testing()) { refresh_router_lsas(); area_border_router_transition(true /* up */); } } // Inform this area if any virtual links are configured. list rids; _vlink.get_router_ids(area, rids); list::const_iterator i; for (i = rids.begin(); i != rids.end(); i++) transit_area_virtual_link(*i, area); return true; } template AreaRouter * PeerManager::get_area_router(OspfTypes::AreaID area) { debug_msg("Area %s\n", pr_id(area).c_str()); // Check this area exists. if (0 == _areas.count(area)) { XLOG_ERROR("Area %s doesn't exist\n", pr_id(area).c_str()); return 0; } return _areas[area]; } template bool PeerManager::change_area_router_type(OspfTypes::AreaID area, OspfTypes::AreaType area_type) { debug_msg("Area %s Type %s\n", pr_id(area).c_str(), pp_area_type(area_type).c_str()); // Verify this area exists. if (0 == _areas.count(area)) { XLOG_ERROR("Area %s doesn't exist", pr_id(area).c_str()); return false; } if (_areas[area]->get_area_type() == area_type) return true; if (!check_area_type(area, area_type)) { XLOG_ERROR("Area %s cannot be %s", pr_id(area).c_str(), pp_area_type(area_type).c_str()); return false; } track_area_count(_areas[area]->get_area_type(), false /* decrement */); track_area_count(area_type, true /* increment */); _areas[area]->change_area_router_type(area_type); // Notify all peers that the type of this area has changed. The // area to peer mapping is not held so all peers must be notified. // When the correct area is found set the new options for the // hello packet. typename map *>::iterator i; for(i = _peers.begin(); i != _peers.end(); i++) if ((*i).second->change_area_router_type(area, area_type)) (*i).second->set_options(area, compute_options(area_type)); return true; } template bool PeerManager::destroy_area_router(OspfTypes::AreaID area) { debug_msg("Area %s\n", pr_id(area).c_str()); // Verify this area exists. if (0 == _areas.count(area)) { XLOG_ERROR("Area %s doesn't exist\n", pr_id(area).c_str()); return false; } track_area_count(_areas[area]->get_area_type(), false /* decrement */); _areas[area]->shutdown(); // Notify the peers that this area is being removed. If this is // the only area that the peer belonged to the peer can signify // this and the peer can be removed. typename map *>::iterator i; for(i = _peers.begin(); i != _peers.end();) if ((*i).second->remove_area(area)) { delete_peer((*i).first); i = _peers.begin(); } else i++; bool old_border_router_state = area_border_router_p(); delete _areas[area]; _areas.erase(_areas.find(area)); // If we are no longer a border router force an updated Router-LSA // in the existing area. // XXX Should subsume the refreshing of the Router-LSA into the // generic area border router transition method. if (area_border_router_p() != old_border_router_state) { if (!_ospf.get_testing()) { refresh_router_lsas(); area_border_router_transition(false /* down */); } } // Flag to the virtual link code that this area is going away. _vlink.area_removed(area); return true; } template bool PeerManager::area_range_add(OspfTypes::AreaID area, IPNet net, bool advertise) { debug_msg("Area %s Net %s advertise %s\n", pr_id(area).c_str(), cstring(net), bool_c_str(advertise)); AreaRouter *area_router = get_area_router(area); // Verify that this area is known. if (0 == area_router) { XLOG_WARNING("Unknown area %s", pr_id(area).c_str()); return false; } return area_router->area_range_add(net, advertise); } template bool PeerManager::area_range_delete(OspfTypes::AreaID area, IPNet net) { debug_msg("Area %s Net %s\n", pr_id(area).c_str(), cstring(net)); AreaRouter *area_router = get_area_router(area); // Verify that this area is known. if (0 == area_router) { XLOG_WARNING("Unknown area %s", pr_id(area).c_str()); return false; } return area_router->area_range_delete(net); } template bool PeerManager::area_range_change_state(OspfTypes::AreaID area, IPNet net, bool advertise) { debug_msg("Area %s Net %s advertise %s\n", pr_id(area).c_str(), cstring(net), bool_c_str(advertise)); AreaRouter *area_router = get_area_router(area); // Verify that this area is known. if (0 == area_router) { XLOG_WARNING("Unknown area %s", pr_id(area).c_str()); return false; } return area_router->area_range_change_state(net, advertise); } template bool PeerManager::get_lsa(const OspfTypes::AreaID area, const uint32_t index, bool& valid, bool& toohigh, bool& self, vector& lsa) { debug_msg("Area %s index %u\n", pr_id(area).c_str(), index); AreaRouter *area_router = get_area_router(area); // Verify that this area is known. if (0 == area_router) { XLOG_WARNING("Unknown area %s", pr_id(area).c_str()); return false; } return area_router->get_lsa(index, valid, toohigh, self, lsa); } template bool PeerManager::get_area_list(list& areas) const { typename map *>::const_iterator i; for(i = _areas.begin(); i != _areas.end(); i++) areas.push_back((*i).first); return true; } template bool PeerManager::get_neighbour_list(list& neighbours) const { typename map *>::const_iterator i; for(i = _peers.begin(); i != _peers.end(); i++) (*i).second->get_neighbour_list(neighbours); return true; } template bool PeerManager::get_neighbour_info(OspfTypes::NeighbourID nid, NeighbourInfo& ninfo) const { list neighbours; typename map *>::const_iterator i; for(i = _peers.begin(); i != _peers.end(); i++) { (*i).second->get_neighbour_list(neighbours); list::const_iterator j; for (j = neighbours.begin(); j != neighbours.end(); j++) { if (*j == nid) { return (*i).second->get_neighbour_info(nid, ninfo); } } neighbours.clear(); } return false; } template OspfTypes::PeerID PeerManager::create_peerid(const string& interface, const string& vif) throw(BadPeer) { debug_msg("Interface %s Vif %s\n", interface.c_str(), vif.c_str()); string concat = interface + "/" + vif; if (0 != _pmap.count(concat)) xorp_throw(BadPeer, c_format("Mapping for %s already exists", concat.c_str())); OspfTypes::PeerID peerid = _next_peerid++; _pmap[concat] = peerid; return peerid; } template OspfTypes::PeerID PeerManager::get_peerid(const string& interface, const string& vif) throw(BadPeer) { debug_msg("Interface %s Vif %s\n", interface.c_str(), vif.c_str()); string concat = interface + "/" + vif; if (0 == _pmap.count(concat)) xorp_throw(BadPeer, c_format("No mapping for %s exists", concat.c_str())); return _pmap[concat]; } template bool PeerManager::get_interface_vif_by_peerid(OspfTypes::PeerID peerid, string& interface, string& vif) const { debug_msg("PeerID %u\n", peerid); typename map::const_iterator pi; for(pi = _pmap.begin(); pi != _pmap.end(); pi++) { if ((*pi).second == peerid) { string concat = (*pi).first; interface = concat.substr(0, concat.find('/')); vif = concat.substr(concat.find('/') + 1, concat.size() - 1); return true; } } return false; } template void PeerManager::destroy_peerid(const string& interface, const string& vif) throw(BadPeer) { debug_msg("Interface %s Vif %s\n", interface.c_str(), vif.c_str()); string concat = interface + "/" + vif; if (0 == _pmap.count(concat)) xorp_throw(BadPeer, c_format("No mapping for %s exists", concat.c_str())); _pmap.erase(_pmap.find(concat)); } template bool PeerManager::enabled(const string& interface, const string& vif, A address) { debug_msg("Interface %s Vif %s Address %s\n", interface.c_str(), vif.c_str(), cstring(address)); switch (_ospf.get_version()) { case OspfTypes::V2: break; case OspfTypes::V3: // If the address is set to zero then try and get the // link-local address. if (A::ZERO() == address) { if (!_ospf.get_link_local_address(interface, vif, address)) if (_ospf.enabled(interface, vif)) XLOG_WARNING("link-local address must be configured " "on %s/%s", interface.c_str(), vif.c_str()); } break; } return _ospf.enabled(interface, vif, address); } template OspfTypes::PeerID PeerManager::create_peer(const string& interface, const string& vif, A source, OspfTypes::LinkType linktype, OspfTypes::AreaID area) throw(BadPeer) { debug_msg("Interface %s Vif %s source net %s linktype %u area %s\n", interface.c_str(), vif.c_str(), cstring(source),linktype, pr_id(area).c_str()); AreaRouter *area_router = get_area_router(area); // Verify that this area is known. if (0 == area_router) xorp_throw(BadPeer, c_format("Unknown Area %s", pr_id(area).c_str())); OspfTypes::PeerID peerid = create_peerid(interface, vif); // If we got this far create_peerid did not throw an exception so // this interface/vif is unique. _peers[peerid] = new PeerOut(_ospf, interface, vif, peerid, source, linktype, area, area_router->get_area_type()); // Pass in the option to be sent by the hello packet. _peers[peerid]->set_options(area, compute_options(area_router->get_area_type())); // These two registrations need to be made only once per // invocation of OSPF, however, at this point we know that the interface // mirror is up and running. _ospf.register_vif_status(callback(this, &PeerManager:: vif_status_change)); _ospf.register_address_status(callback(this, &PeerManager:: address_status_change)); area_router->add_peer(peerid); // If the interface, vif and source are up the peer will start running. _peers[peerid]->set_link_status(enabled(interface, vif, source), "create_peer"); return peerid; } template bool PeerManager::delete_peer(const OspfTypes::PeerID peerid) { debug_msg("PeerID %u\n", peerid); if (0 == _peers.count(peerid)) { XLOG_ERROR("Unknown PeerID %u", peerid); return false; } delete _peers[peerid]; _peers.erase(_peers.find(peerid)); // Tell *all* area routers that this peer is being deleted. // It is simpler to do this than hold the reverse mappings. typename map *>::iterator i; for(i = _areas.begin(); i != _areas.end(); i++) (*i).second->delete_peer(peerid); // Remove the interface/vif to PeerID mapping typename map::iterator pi; for(pi = _pmap.begin(); pi != _pmap.end(); pi++) if ((*pi).second == peerid) { _pmap.erase(pi); break; } return true; } template bool PeerManager::set_state_peer(const OspfTypes::PeerID peerid, bool state) { debug_msg("PeerID %u\n", peerid); if (0 == _peers.count(peerid)) { XLOG_ERROR("Unknown PeerID %u", peerid); return false; } _peers[peerid]->set_state(state); return true; } template bool PeerManager::set_link_status_peer(const OspfTypes::PeerID peerid, bool state) { debug_msg("PeerID %u\n", peerid); if (0 == _peers.count(peerid)) { XLOG_ERROR("Unknown PeerID %u", peerid); return false; } _peers[peerid]->set_link_status(state, "PeerManager::set_link_status_peer"); return true; } template bool PeerManager::add_address_peer(const string& interface, const string& vif, OspfTypes::AreaID area, A addr) { debug_msg("interface %s vif %s area %s address %s\n", interface.c_str(), vif.c_str(), pr_id(area).c_str(), cstring(addr)); // Get the prefix length. uint16_t prefix; if (!_ospf.get_prefix_length(interface, vif, addr, prefix)) { XLOG_WARNING("Unable to get prefix for %s ", cstring(addr)); return false; } // An exception will be thrown if there is a problem. OspfTypes::PeerID peerid = get_peerid(interface, vif); set >& info = _peers[peerid]->get_address_info(area); info.insert(AddressInfo(addr, prefix)); recompute_addresses_peer(peerid, area); return true; } template bool PeerManager::remove_address_peer(const OspfTypes::PeerID peerid, OspfTypes::AreaID area, A addr) { debug_msg("PeerID %u, area %s address %s\n", peerid, pr_id(area).c_str(), cstring(addr)); if (0 == _peers.count(peerid)) { XLOG_ERROR("Unknown PeerID %u", peerid); return false; } set >& info = _peers[peerid]->get_address_info(area); info.erase(AddressInfo(addr)); recompute_addresses_peer(peerid, area); return true; } template bool PeerManager::set_address_state_peer(const OspfTypes::PeerID peerid, OspfTypes::AreaID area, A addr, bool enable) { debug_msg("PeerID %u, area %s address %s enable %s\n", peerid, pr_id(area).c_str(), cstring(addr), bool_c_str(enable)); if (0 == _peers.count(peerid)) { XLOG_ERROR("Unknown PeerID %u", peerid); return false; } set >& info = _peers[peerid]->get_address_info(area); typename set >:: iterator i = info.find(AddressInfo(addr)); if (i == info.end()) { XLOG_ERROR("Couldn't find %s", cstring(addr)); return false; } AddressInfo naddr((*i)._address, (*i)._prefix, enable); info.erase(i); info.insert(naddr); recompute_addresses_peer(peerid, area); return true; } template bool PeerManager::activate_peer(const string& interface, const string& vif, OspfTypes::AreaID area) { debug_msg("interface %s vif %s area %s\n", interface.c_str(), vif.c_str(), pr_id(area).c_str()); // An exception will be thrown if there is a problem. OspfTypes::PeerID peerid = get_peerid(interface, vif); recompute_addresses_peer(peerid, area); A source = _peers[peerid]->get_interface_address(); _peers[peerid]->set_link_status(enabled(interface, vif, source), "activate_peer"); return true; } template bool PeerManager::update_peer(const string& interface, const string& vif, OspfTypes::AreaID area) { debug_msg("interface %s vif %s area %s\n", interface.c_str(), vif.c_str(), pr_id(area).c_str()); // Not currently required. return true; } template bool PeerManager::recompute_addresses_peer(const OspfTypes::PeerID peerid, OspfTypes::AreaID area) { debug_msg("PeerID %u area %s\n", peerid, pr_id(area).c_str()); if (0 == _peers.count(peerid)) { XLOG_ERROR("Unknown PeerID %u", peerid); return false; } set >& info = _peers[peerid]->get_address_info(area); // Unconditionally remove all the global addresses that are being // advertised. _peers[peerid]->remove_all_nets(area); // If no addresses have been configured then advertise all the // configured addresses. if (info.empty()) { string interface, vif; if (!get_interface_vif_by_peerid(peerid, interface, vif)) { XLOG_ERROR("Unable to find interface/vif associated with " "PeerID %u", peerid); return false; } // Before trying to get the addresses verify that this // interface/vif exists and has a usable address configured. if (!enabled(interface, vif, _peers[peerid]->get_interface_address())) return false; list addresses; if (!_ospf.get_addresses(interface, vif, addresses)) { XLOG_ERROR("Unable to find addresses on %s/%s ", interface.c_str(), vif.c_str()); return false; } typename list::iterator i; for (i = addresses.begin(); i != addresses.end(); i++) { if ((*i).is_linklocal_unicast()) continue; uint16_t interface_prefix_length; if (!_ospf.get_prefix_length(interface, vif, *i, interface_prefix_length)) { XLOG_ERROR("Unable to get prefix length for %s", cstring(*i)); continue; } if (!_peers[peerid]->add_advertise_net(area, (*i), interface_prefix_length)) { XLOG_WARNING("Unable to advertise %s in Link-LSA\n", cstring(*i)); } } } else { typename set >::iterator i; for (i = info.begin(); i != info.end(); i++) { if ((*i)._enabled) { if (!_peers[peerid]->add_advertise_net(area, (*i)._address, (*i)._prefix)) { XLOG_WARNING("Unable to advertise %s in Link-LSA\n", cstring((*i)._address)); } } } } // Force out a new Link-LSA. return _peers[peerid]->update_nets(area); } template void PeerManager::vif_status_change(const string& interface, const string& vif, bool state) { XLOG_WARNING("interface %s vif %s state %s\n", interface.c_str(), vif.c_str(), bool_c_str(state)); OspfTypes::PeerID peerid; // All interface/vif/address changes on the host come through // here, ignore the changes that are not for OSPF. try { peerid = get_peerid(interface, vif); } catch(...) { return; } if (0 == _peers.count(peerid)) { XLOG_ERROR("Unknown PeerID %u", peerid); return; } _peers[peerid]->set_link_status(state, "PeerManager::vif_status_change"); return; } template void PeerManager::address_status_change(const string& interface, const string& vif, A source, bool state) { debug_msg("interface %s vif %s address %s state %s\n", interface.c_str(), vif.c_str(), cstring(source), bool_c_str(state)); OspfTypes::PeerID peerid; // All interface/vif/address changes on the host come through // here, ignore the changes that are not for OSPF. try { peerid = get_peerid(interface, vif); } catch(...) { return; } if (0 == _peers.count(peerid)) { XLOG_ERROR("Unknown PeerID %u", peerid); return; } _peers[peerid]->set_link_status(enabled(interface, vif, _peers[peerid]-> get_interface_address()), "address_status_change"); switch(_ospf.get_version()) { case OspfTypes::V2: break; case OspfTypes::V3: list areas; _peers[peerid]->get_areas(areas); list::iterator i; for (i = areas.begin(); i != areas.end(); i++) recompute_addresses_peer(peerid, *i); break; } return; } template bool PeerManager::add_neighbour(const OspfTypes::PeerID peerid, OspfTypes::AreaID area, A neighbour_address, OspfTypes::RouterID rid) { if (0 == _peers.count(peerid)) { XLOG_ERROR("Unknown PeerID %u", peerid); return false; } return _peers[peerid]->add_neighbour(area, neighbour_address, rid); } template bool PeerManager::remove_neighbour(const OspfTypes::PeerID peerid, OspfTypes::AreaID area, A neighbour_address, OspfTypes::RouterID rid) { if (0 == _peers.count(peerid)) { XLOG_ERROR("Unknown PeerID %u", peerid); return false; } return _peers[peerid]->remove_neighbour(area, neighbour_address, rid); } template bool PeerManager::transmit(const string& interface, const string& vif, A dst, A src, uint8_t* data, uint32_t len) { XLOG_TRACE(_ospf.trace()._packets, "PeerManager::transmit Interface %s Vif %s data %p len %u src: %s dst: %s\n", interface.c_str(), vif.c_str(), data, len, src.str().c_str(), dst.str().c_str()); if (string(VLINK) == interface) { string interface; string vif; if (_vlink.get_physical_interface_vif(src, dst, interface, vif)) return _ospf.transmit(interface, vif, dst, src, VLINK_DEF_TTL, data, len); // We didn't find a match fall through. } return _ospf.transmit(interface, vif, dst, src, -1, data, len); } template bool PeerManager::receive(const string& interface, const string& vif, A dst, A src, Packet *packet) throw(BadPeer) { XLOG_TRACE(_ospf.trace()._packets, "Interface %s Vif %s src %s dst %s %s\n", interface.c_str(), vif.c_str(), cstring(dst), cstring(src), cstring((*packet))); OspfTypes::PeerID peerid = get_peerid(interface, vif); XLOG_ASSERT(0 != _peers.count(peerid)); return _peers[peerid]->receive(dst, src, packet); } template bool PeerManager::clear_database() { // Drop all adjacencies. typename map *>::const_iterator p; for(p = _peers.begin(); p != _peers.end(); p++) { // If this peer is up then taking it down and bringing it up // should drop all adjacencies. if( (*p).second->get_state()) { (*p).second->set_state(false); (*p).second->set_state(true); } } // Clear the AS-External-LSA database. _external.clear_database(); // Clear the area databases. typename map *>::const_iterator a; for (a = _areas.begin(); a != _areas.end(); a++) { // Changing the area type of the router will delete the // Router-LSA generated by this router and revive it. (*a).second->change_area_router_type((*a).second->get_area_type()); } // Recompute the routes in all areas to withdraw the routes and // remove virtual links. This will also cause the summary routes // to be withdrawn. Should not be required as dropping the // adjacencies should prompt a route recomputation. routing_recompute_all_areas(); typename map, Summary>::iterator i; for(i = _summaries.begin(); i != _summaries.end(); i++) XLOG_WARNING("Summary not removed %s %s", cstring(i->first), cstring(i->second._rtentry)); return true; } template bool PeerManager::queue_lsa(const OspfTypes::PeerID peerid, const OspfTypes::PeerID peer, OspfTypes::NeighbourID nid, Lsa::LsaRef lsar, bool &multicast_on_peer) { if (0 == _peers.count(peerid)) { XLOG_ERROR("Unknown PeerID %u", peerid); return false; } return _peers[peerid]->queue_lsa(peer, nid, lsar, multicast_on_peer); } template bool PeerManager::push_lsas(const OspfTypes::PeerID peerid, const char* msg) { if (0 == _peers.count(peerid)) { XLOG_ERROR("Unknown PeerID %u", peerid); return false; } return _peers[peerid]->push_lsas(msg); } template uint32_t PeerManager::get_interface_id(const OspfTypes::PeerID peerid) { if (0 == _peers.count(peerid)) { XLOG_ERROR("Unknown PeerID %u", peerid); return 0; } return _peers[peerid]->get_interface_id(); } template bool PeerManager::get_attached_routers(const OspfTypes::PeerID peerid, OspfTypes::AreaID area, list& routers) { if (0 == _peers.count(peerid)) { XLOG_ERROR("Unknown PeerID %u", peerid); return false; } return _peers[peerid]->get_attached_routers(area, routers); } template bool PeerManager::configured_network(const A address) const { typename map *>::const_iterator i; for(i = _peers.begin(); i != _peers.end(); i++) { if ((*i).second->get_interface_prefix_length() != 0) { IPNet net((*i).second->get_interface_address(), (*i).second->get_interface_prefix_length()); if (net.contains(address)) return true; } } return false; } template bool PeerManager::known_interface_address(const A address) const { // XXX // Note we are only checking the interface addresses of the // configured peers. We should be checking the interface addresses // with the FEA. typename map *>::const_iterator i; for(i = _peers.begin(); i != _peers.end(); i++) if ((*i).second->get_interface_address() == address) return true; return false; } template bool PeerManager::neighbours_exchange_or_loading(const OspfTypes::PeerID peerid, OspfTypes::AreaID area) { if (0 == _peers.count(peerid)) { XLOG_ERROR("Unknown PeerID %u", peerid); return false; } return _peers[peerid]->neighbours_exchange_or_loading(area); } template bool PeerManager::neighbour_at_least_two_way(const OspfTypes::PeerID peerid, OspfTypes::AreaID area, OspfTypes::RouterID rid, bool& twoway) { if (0 == _peers.count(peerid)) { XLOG_ERROR("Unknown PeerID %u", peerid); return false; } return _peers[peerid]->neighbour_at_least_two_way(area, rid, twoway); } template bool PeerManager::get_neighbour_address(const OspfTypes::PeerID peerid, OspfTypes::AreaID area, OspfTypes::RouterID rid, uint32_t interface_id, A& neighbour_address) { if (0 == _peers.count(peerid)) { XLOG_ERROR("Unknown PeerID %u", peerid); return false; } return _peers[peerid]-> get_neighbour_address(area, rid, interface_id, neighbour_address); } template bool PeerManager::on_link_state_request_list(const OspfTypes::PeerID peerid, OspfTypes::AreaID area, const OspfTypes::NeighbourID nid, Lsa::LsaRef lsar) { if (0 == _peers.count(peerid)) { XLOG_ERROR("Unknown PeerID %u", peerid); return false; } return _peers[peerid]->on_link_state_request_list(area, nid, lsar); } template bool PeerManager::event_bad_link_state_request(const OspfTypes::PeerID peerid, OspfTypes::AreaID area, const OspfTypes::NeighbourID nid) { if (0 == _peers.count(peerid)) { XLOG_ERROR("Unknown PeerID %u", peerid); return false; } return _peers[peerid]->event_bad_link_state_request(area, nid); } template bool PeerManager::send_lsa(const OspfTypes::PeerID peerid, OspfTypes::AreaID area, const OspfTypes::NeighbourID nid, Lsa::LsaRef lsar) { if (0 == _peers.count(peerid)) { XLOG_ERROR("Unknown PeerID %u", peerid); return false; } return _peers[peerid]->send_lsa(area, nid, lsar); } template void PeerManager::adjacency_changed(const OspfTypes::PeerID peerid, OspfTypes::RouterID rid, bool up) { if (0 == _peers.count(peerid)) XLOG_FATAL("Unknown PeerID %u", peerid); // Is this neighbour a virtual link? if (!_peers[peerid]->virtual_link_endpoint(OspfTypes::BACKBONE)) return; OspfTypes::AreaID transit_area; if (!_vlink.get_transit_area(rid, transit_area)) return; list rids; _vlink.get_router_ids(transit_area, rids); uint32_t fully_adjacent_virtual_links = 0; typename list::const_iterator i; for(i = rids.begin(); i != rids.end(); i++) { OspfTypes::PeerID peerid = _vlink.get_peerid(*i); typename map *>::const_iterator j; j = _peers.find(peerid); if(j == _peers.end()) { // A peerid can be removed and the vlink database is not notified. // Happens during shutdown. XLOG_WARNING("Peer not found %d", peerid); continue; } if ((*j).second->virtual_link_endpoint(OspfTypes::BACKBONE)) fully_adjacent_virtual_links++; } // Only care if there are no fully adjacent virtual links left or // this is the first fully adjacent virtual link. switch(fully_adjacent_virtual_links) { case 0: XLOG_ASSERT(!up); break; case 1: XLOG_ASSERT(up); break; default: return; } AreaRouter *area_router = get_area_router(transit_area); if (0 == area_router) { XLOG_WARNING("Unknown area %s", pr_id(transit_area).c_str()); return; } area_router->refresh_router_lsa(); } template void PeerManager::area_border_router_transition(bool up) const { typename map *>::const_iterator i; for (i = _areas.begin(); i != _areas.end(); i++) { (*i).second->area_border_router_transition(up); } } template void PeerManager::refresh_router_lsas() const { typename map *>::const_iterator i; for (i = _areas.begin(); i != _areas.end(); i++) { (*i).second->refresh_router_lsa(); } } template bool PeerManager::create_virtual_peer(OspfTypes::RouterID rid) { string ifname; string vifname; if (!_vlink.get_interface_vif(rid, ifname, vifname)) { XLOG_FATAL("Router ID not found %s", pr_id(rid).c_str()); return false; } OspfTypes::PeerID peerid; try { peerid = create_peer(ifname, vifname, A::ZERO(), OspfTypes::VirtualLink, OspfTypes::BACKBONE); } catch(XorpException& e) { XLOG_ERROR("%s", cstring(e)); return false; } if (!_vlink.add_peerid(rid, peerid)) { XLOG_FATAL("Router ID not found %s", pr_id(rid).c_str()); return false; } return true; } template bool PeerManager::delete_virtual_peer(OspfTypes::RouterID rid) { OspfTypes::PeerID peerid = _vlink.get_peerid(rid); if (OspfTypes::ALLPEERS != peerid) { try { delete_peer(peerid); } catch(XorpException& e) { XLOG_ERROR("%s", cstring(e)); } // This PeerID has now been deleted so remove it from the record. // This is not strictly necessary as we are about to delete // this virtual link, but is is possible that in the future // removing the virtual link from the area router may cause an // upcall. _vlink.add_peerid(rid, OspfTypes::ALLPEERS); } return true; } template bool PeerManager::virtual_link_endpoint(OspfTypes::AreaID area) const { list rids; _vlink.get_router_ids(area, rids); typename list::const_iterator i; for(i = rids.begin(); i != rids.end(); i++) { OspfTypes::PeerID peerid = _vlink.get_peerid(*i); typename map *>::const_iterator j; j = _peers.find(peerid); if(j == _peers.end()) { // A peerid can be removed and the vlink database is not notified. // Happens during shutdown. XLOG_WARNING("Peer not found %d", peerid); continue; } if ((*j).second->virtual_link_endpoint(OspfTypes::BACKBONE)) return true; } return false; } template bool PeerManager::create_virtual_link(OspfTypes::RouterID rid) { XLOG_TRACE(_ospf.trace()._virtual_link, "Create virtual link rid %s\n", pr_id(rid).c_str()); if (!_vlink.create_vlink(rid)) return false; return create_virtual_peer(rid); } template bool PeerManager::transit_area_virtual_link(OspfTypes::RouterID rid, OspfTypes::AreaID transit_area) { XLOG_TRACE(_ospf.trace()._virtual_link, "Add transit area to virtual link rid %s transit area %s\n", pr_id(rid).c_str(), pr_id(transit_area).c_str()); OspfTypes::AreaID oarea; if (!_vlink.get_transit_area(rid, oarea)) return false; // Has the current transit area been told about this router ID. bool notified = _vlink.get_transit_area_notified(rid); if (oarea == transit_area) { if (notified) return true; AreaRouter *area = get_area_router(transit_area); if (0 == area) return false; // Might be a stub area turning us down. if (!area->add_virtual_link(rid)) return false; _vlink.set_transit_area_notified(rid, true); return true; } // We are now dealing with two separate areas. if (!_vlink.set_transit_area(rid, transit_area)) return false; if (notified) { if (OspfTypes::BACKBONE != oarea) { AreaRouter *parea = get_area_router(oarea); if (parea) parea->remove_virtual_link(rid); } } AreaRouter *area = get_area_router(transit_area); _vlink.set_transit_area_notified(rid, false); if (0 == area) { return false; } if (!area->add_virtual_link(rid)) { return false; } _vlink.set_transit_area_notified(rid, true); return true; } template bool PeerManager::delete_virtual_link(OspfTypes::RouterID rid) { XLOG_TRACE(_ospf.trace()._virtual_link, "Delete virtual link rid %s\n", pr_id(rid).c_str()); delete_virtual_peer(rid); // If a transit area is configured then remove this virtual link // from that area. OspfTypes::AreaID transit_area; if (!_vlink.get_transit_area(rid, transit_area)) { XLOG_WARNING("Couldn't find rid %s", pr_id(rid).c_str()); return false; } if (OspfTypes::BACKBONE != transit_area) { AreaRouter *area = get_area_router(transit_area); // Having no associated area is perfectly legal. if (0 != area) { area->remove_virtual_link(rid); } } return _vlink.delete_vlink(rid); } template void PeerManager::up_virtual_link(OspfTypes::RouterID rid, A source, uint16_t interface_cost, A destination) { XLOG_TRACE(_ospf.trace()._virtual_link, "Virtual link up rid %s source %s cost %d destination %s\n", pr_id(rid).c_str(), cstring(source), interface_cost, cstring(destination)); if (!_vlink.add_address(rid, source, destination)) { XLOG_FATAL("Router ID not found %s", pr_id(rid).c_str()); return; } string ifname; string vifname; if (!_vlink.get_interface_vif(rid, ifname, vifname)) { XLOG_FATAL("Router ID not found %s", pr_id(rid).c_str()); return; } OspfTypes::PeerID peerid = _vlink.get_peerid(rid); // Scan through the peers and find the interface and vif that // match the source address. typename map *>::const_iterator i; for(i = _peers.begin(); i != _peers.end(); i++) { if ((*i).second->match(source, ifname, vifname)) { if (!_vlink.set_physical_interface_vif(rid, ifname, vifname)) XLOG_FATAL("Router ID not found %s", pr_id(rid).c_str()); break; } } if (!set_interface_address(peerid, source)) return; if (!set_interface_cost(peerid, OspfTypes::BACKBONE, interface_cost)) return; if (!add_neighbour(peerid, OspfTypes::BACKBONE, destination, rid)) return; if (!set_state_peer(peerid, true)) return; if (!set_link_status_peer(peerid, true)) return; } template void PeerManager::down_virtual_link(OspfTypes::RouterID rid) { XLOG_TRACE(_ospf.trace()._virtual_link, "Virtual link down rid %s\n", pr_id(rid).c_str()); OspfTypes::PeerID peerid = _vlink.get_peerid(rid); if (OspfTypes::ALLPEERS == peerid) { XLOG_WARNING("No peer found when dropping virtual link %s", pr_id(rid).c_str()); return; } if (!set_state_peer(peerid, false)) return; A source, destination; if (!_vlink.get_address(rid, source, destination)) { XLOG_FATAL("Router ID not found %s", pr_id(rid).c_str()); return; } if (!remove_neighbour(peerid, OspfTypes::BACKBONE, destination, rid)) return; } template bool PeerManager::receive_virtual_link(A dst, A src, Packet *packet) { XLOG_TRACE(_ospf.trace()._virtual_link, "Virtual link receive dest %s src %s packet %s\n", cstring(dst), cstring(src), cstring(*packet)); OspfTypes::PeerID peerid = _vlink.get_peerid(dst, src); if (OspfTypes::ALLPEERS == peerid) return false; XLOG_ASSERT(0 != _peers.count(peerid)); return _peers[peerid]->receive(dst, src, packet); return false; } template uint32_t PeerManager::compute_options(OspfTypes::AreaType area_type) { // Set/UnSet E-Bit. Options options(_ospf.get_version(), 0); switch(area_type) { case OspfTypes::NORMAL: options.set_e_bit(true); options.set_n_bit(false); break; case OspfTypes::STUB: options.set_e_bit(false); options.set_n_bit(false); break; case OspfTypes::NSSA: options.set_e_bit(false); options.set_n_bit(true); break; } switch (_ospf.get_version()) { case OspfTypes::V2: break; case OspfTypes::V3: // XXX - Unconditionally set the router bit which means that // we want to participate in the routing, the passive and // loopback settings should be taken into account, when // setting this value. options.set_r_bit(true); options.set_v6_bit(true); break; } return options.get_options(); } #if 0 template bool PeerManager::set_options(const OspfTypes::PeerID peerid, OspfTypes::AreaID area, uint32_t options) { if (0 == _peers.count(peerid)) { XLOG_ERROR("Unknown PeerID %u", peerid); return false; } return _peers[peerid]->set_options(area, options); } #endif template void PeerManager::router_id_changing() { typename map *>::const_iterator i; for(i = _peers.begin(); i != _peers.end(); i++) (*i).second->router_id_changing(); } template bool PeerManager::set_interface_address(const OspfTypes::PeerID peerid, A address) { if (0 == _peers.count(peerid)) { XLOG_ERROR("Unknown PeerID %u", peerid); return false; } return _peers[peerid]->set_interface_address(address); } template bool PeerManager::set_hello_interval(const OspfTypes::PeerID peerid, OspfTypes::AreaID area, uint16_t hello_interval) { if (0 == _peers.count(peerid)) { XLOG_ERROR("Unknown PeerID %u", peerid); return false; } return _peers[peerid]->set_hello_interval(area, hello_interval); } template bool PeerManager::set_router_priority(const OspfTypes::PeerID peerid, OspfTypes::AreaID area, uint8_t priority) { if (0 == _peers.count(peerid)) { XLOG_ERROR("Unknown PeerID %u", peerid); return false; } return _peers[peerid]->set_router_priority(area, priority); } template bool PeerManager::set_router_dead_interval(const OspfTypes::PeerID peerid, OspfTypes::AreaID area, uint32_t router_dead_interval) { if (0 == _peers.count(peerid)) { XLOG_ERROR("Unknown PeerID %u", peerid); return false; } return _peers[peerid]-> set_router_dead_interval(area, router_dead_interval); } template bool PeerManager::set_interface_cost(const OspfTypes::PeerID peerid, OspfTypes::AreaID /*area*/, uint16_t interface_cost) { if (0 == _peers.count(peerid)) { XLOG_ERROR("Unknown PeerID %u", peerid); return false; } return _peers[peerid]->set_interface_cost(interface_cost); } template bool PeerManager::set_retransmit_interval(const OspfTypes::PeerID peerid, OspfTypes::AreaID area, uint16_t retransmit_interval) { if (0 == _peers.count(peerid)) { XLOG_ERROR("Unknown PeerID %u", peerid); return false; } return _peers[peerid]->set_retransmit_interval(area, retransmit_interval); } template bool PeerManager::set_inftransdelay(const OspfTypes::PeerID peerid, OspfTypes::AreaID /*area*/, uint16_t inftransdelay) { if (0 == _peers.count(peerid)) { XLOG_ERROR("Unknown PeerID %u", peerid); return false; } return _peers[peerid]->set_inftransdelay(inftransdelay); } template bool PeerManager::set_simple_authentication_key(const OspfTypes::PeerID peerid, OspfTypes::AreaID area, const string& password, string& error_msg) { if (0 == _peers.count(peerid)) { error_msg = c_format("Unknown PeerID %u", peerid); return false; } return _peers[peerid]->set_simple_authentication_key(area, password, error_msg); } template bool PeerManager::delete_simple_authentication_key(const OspfTypes::PeerID peerid, OspfTypes::AreaID area, string& error_msg) { if (0 == _peers.count(peerid)) { error_msg = c_format("Unknown PeerID %u", peerid); return false; } return _peers[peerid]->delete_simple_authentication_key(area, error_msg); } template bool PeerManager::set_md5_authentication_key(const OspfTypes::PeerID peerid, OspfTypes::AreaID area, uint8_t key_id, const string& password, const TimeVal& start_timeval, const TimeVal& end_timeval, const TimeVal& max_time_drift, string& error_msg) { if (0 == _peers.count(peerid)) { error_msg = c_format("Unknown PeerID %u", peerid); return false; } return _peers[peerid]->set_md5_authentication_key(area, key_id, password, start_timeval, end_timeval, max_time_drift, error_msg); } template bool PeerManager::delete_md5_authentication_key(const OspfTypes::PeerID peerid, OspfTypes::AreaID area, uint8_t key_id, string& error_msg) { if (0 == _peers.count(peerid)) { error_msg = c_format("Unknown PeerID %u", peerid); return false; } return _peers[peerid]->delete_md5_authentication_key(area, key_id, error_msg); } template bool PeerManager::set_passive(const OspfTypes::PeerID peerid, OspfTypes::AreaID area, bool passive, bool host) { if (0 == _peers.count(peerid)) { XLOG_ERROR("Unknown PeerID %u", peerid); return false; } return _peers[peerid]->set_passive(area, passive, host); } template bool PeerManager::originate_default_route(OspfTypes::AreaID area, bool enable) { debug_msg("Area %s enable %s\n", pr_id(area).c_str(), bool_c_str(enable)); AreaRouter *area_router = get_area_router(area); // Verify that this area is known. if (0 == area_router) { XLOG_WARNING("Unknown area %s", pr_id(area).c_str()); return false; } return area_router->originate_default_route(enable); } template bool PeerManager::stub_default_cost(OspfTypes::AreaID area, uint32_t cost) { debug_msg("Area %s cost %u\n", pr_id(area).c_str(), cost); AreaRouter *area_router = get_area_router(area); // Verify that this area is known. if (0 == area_router) { XLOG_WARNING("Unknown area %s", pr_id(area).c_str()); return false; } return area_router->stub_default_cost(cost); } template bool PeerManager::summaries(OspfTypes::AreaID area, bool enable) { debug_msg("Area %s enable %s\n", pr_id(area).c_str(), bool_c_str(enable)); AreaRouter *area_router = get_area_router(area); // Verify that this area is known. if (0 == area_router) { XLOG_WARNING("Unknown area %s", pr_id(area).c_str()); return false; } return area_router->summaries(enable); } template void PeerManager::track_area_count(OspfTypes::AreaType area_type, bool up) { int delta = up ? 1 : -1; switch(area_type) { case OspfTypes::NORMAL: _normal_cnt += delta; break; case OspfTypes::STUB: _stub_cnt += delta; break; case OspfTypes::NSSA: _nssa_cnt += delta; break; } } template uint32_t PeerManager::area_count(OspfTypes::AreaType area_type) const { switch(area_type) { case OspfTypes::NORMAL: return _normal_cnt; break; case OspfTypes::STUB: return _stub_cnt; break; case OspfTypes::NSSA: return _nssa_cnt; break; } XLOG_UNREACHABLE(); return 0; } template bool PeerManager::internal_router_p() const { // True if are connected to only one area. return 1 == _areas.size() ? true : false; } template bool PeerManager::area_border_router_p() const { // True if this router is connected to multiple areas.. return 1 < _areas.size() ? true : false; } static const OspfTypes::AreaID BACKBONE = OspfTypes::BACKBONE; template bool PeerManager::backbone_router_p() const { // True if one of the areas the router is connected to is the // backbone area. // XXX - The line below should be OspfTypes::BACKBONE the gcc34 // compiler rejected this hence the local declaration. return 1 == _areas.count(BACKBONE) ? true : false; } template bool PeerManager::as_boundary_router_p() const { return _external.as_boundary_router_p(); } template void PeerManager::external_push_routes() { _external.push_routes(); } template void PeerManager::external_suppress_lsas(OspfTypes::AreaID area) { _external.suppress_lsas(area); } template void PeerManager::routing_recompute_all_areas() { typename map *>::const_iterator i; for (i = _areas.begin(); i != _areas.end(); i++) if ((*i).first == BACKBONE) { (*i).second->routing_total_recompute(); break; } // Once the backbone is recomputed any transit areas will also be // recomputed; so they don't need to be computed again. for (i = _areas.begin(); i != _areas.end(); i++) if ((*i).first != BACKBONE) { if (!(*i).second->get_transit_capability()) (*i).second->routing_total_recompute(); break; } } template void PeerManager::routing_recompute_all_transit_areas() { typename map *>::const_iterator i; for (i = _areas.begin(); i != _areas.end(); i++) if ((*i).first != BACKBONE) if ((*i).second->get_transit_capability()) (*i).second->routing_total_recompute(); } template bool PeerManager::summary_candidate(OspfTypes::AreaID area, IPNet net, RouteEntry& rt) { debug_msg("Area %s net %s rentry %s\n", pr_id(area).c_str(), cstring(net), cstring(rt)); if (_ospf.get_testing()) return false; // RFC 2328 Section 12.4.3. Summary-LSAs // Select routes that are candidate for summarisation. bool candidate = false; // if (rt.get_directly_connected()) { // debug_msg("Rejected directly connected route\n"); // return false; // } switch (rt.get_destination_type()) { case OspfTypes::Router: if (rt.get_as_boundary_router()) candidate = true; break; case OspfTypes::Network: candidate = true; break; } if (!candidate) { debug_msg("Rejected not an AS boundary or Network\n"); return false; } switch (rt.get_path_type()) { case RouteEntry::intra_area: case RouteEntry::inter_area: candidate = true; break; case RouteEntry::type1: case RouteEntry::type2: candidate = false; break; } debug_msg("%s\n", candidate ? "Accepted" : "Rejected not an intra/inter area route"); return candidate; } template void PeerManager::summary_announce(OspfTypes::AreaID area, IPNet net, RouteEntry& rt) { debug_msg("Area %s net %s rentry %s\n", pr_id(area).c_str(), cstring(net), cstring(rt)); if (!summary_candidate(area, net, rt)) return; _external.suppress_route_announce(area, net, rt); // Save this route for possible later replay. XLOG_ASSERT(0 == _summaries.count(net)); Summary s(area, rt); _summaries.insert(make_pair(net, s)); if (!area_border_router_p()) return; typename map *>::const_iterator i; for (i = _areas.begin(); i != _areas.end(); i++) if ((*i).first != area) (*i).second->summary_announce(area, net, rt, false); } template void PeerManager::summary_withdraw(OspfTypes::AreaID area, IPNet net, RouteEntry& rt) { debug_msg("Area %s net %s\n", pr_id(area).c_str(), cstring(net)); if (!summary_candidate(area, net, rt)) return; _external.suppress_route_withdraw(area, net, rt); // Remove this saved route. XLOG_ASSERT(1 == _summaries.count(net)); _summaries.erase(_summaries.find(net)); // This is an optimisation that will cause problems if the area // remove in the routing table becomes a background task. If we // transition from two areas to one area and the routes from the // other area are withdrawn in the background this test will stop // the withdraws making it through. // if (!area_border_router_p()) // return; typename map *>::const_iterator i; for (i = _areas.begin(); i != _areas.end(); i++) if ((*i).first != area) (*i).second->summary_withdraw(area, net, rt); } template void PeerManager::summary_replace(OspfTypes::AreaID area, IPNet net, RouteEntry& rt, RouteEntry& previous_rt, OspfTypes::AreaID previous_area) { debug_msg("Area %s net %s rentry %s\n", pr_id(area).c_str(), cstring(net), cstring(rt)); bool previous = summary_candidate(previous_area, net, previous_rt); bool current = summary_candidate(area, net, rt); if (current != previous) { if (previous) summary_withdraw(previous_area, net, previous_rt); if (current) summary_announce(area, net, rt); return; } XLOG_ASSERT(current == previous); // Don't need to check for !previous as it is the same as current. if (!current) return; XLOG_ASSERT(current); XLOG_ASSERT(previous); _external.suppress_route_withdraw(previous_area, net, previous_rt); _external.suppress_route_announce(area, net, rt); XLOG_ASSERT(1 == _summaries.count(net)); _summaries.erase(_summaries.find(net)); Summary s(area, rt); _summaries.insert(make_pair(net, s)); typename map *>::const_iterator i; for (i = _areas.begin(); i != _areas.end(); i++) { if ((*i).first == area) { if (area != previous_area) (*i).second->summary_withdraw(previous_area, net, previous_rt); continue; } if ((*i).first == previous_area) { if (area != previous_area) (*i).second->summary_announce(area, net, rt, false); continue; } (*i).second->summary_replace(area, net, rt, previous_rt,previous_area); } } template void PeerManager::summary_push(OspfTypes::AreaID area) { debug_msg("Area %s\n", pr_id(area).c_str()); AreaRouter *area_router = get_area_router(area); if (0 == area_router) { XLOG_WARNING("Unknown area %s", pr_id(area).c_str()); return; } if (!area_border_router_p()) return; typename map, Summary>::const_iterator i; for (i = _summaries.begin(); i != _summaries.end(); i++) { IPNet net = (*i).first; Summary s = (*i).second; if (s._area == area) continue; area_router->summary_announce(s._area, net, s._rtentry, true); } } template bool PeerManager::area_range_covered(OspfTypes::AreaID area, IPNet net, bool& advertise) { debug_msg("Area %s net %s\n", pr_id(area).c_str(), cstring(net)); AreaRouter *area_router = get_area_router(area); if (0 == area_router) { XLOG_WARNING("Unknown area %s", pr_id(area).c_str()); return false; } return area_router->area_range_covered(net, advertise); } template bool PeerManager::area_range_configured(OspfTypes::AreaID area) { debug_msg("Area %s\n", pr_id(area).c_str()); AreaRouter *area_router = get_area_router(area); if (0 == area_router) { XLOG_WARNING("Unknown area %s", pr_id(area).c_str()); return false; } return area_router->area_range_configured(); } template bool PeerManager::external_announce(OspfTypes::AreaID area, Lsa::LsaRef lsar) { debug_msg("Area %s LSA %s\n", pr_id(area).c_str(), cstring(*lsar)); return _external.announce(area, lsar); } template bool PeerManager::external_announce(const IPNet& net, const A& nexthop, const uint32_t& metric, const PolicyTags& policytags) { debug_msg("Net %s nexthop %s metric %u\n", cstring(net), cstring(nexthop), metric); return _external.announce(net, nexthop, metric, policytags); } template bool PeerManager::external_withdraw(OspfTypes::AreaID area, Lsa::LsaRef lsar) { debug_msg("Area %s LSA %s\n", pr_id(area).c_str(), cstring(*lsar)); XLOG_UNREACHABLE(); return true; } template bool PeerManager::external_withdraw(const IPNet& net) { debug_msg("Net %s\n", cstring(net)); return _external.withdraw(net); } template bool PeerManager::external_announce_complete(OspfTypes::AreaID area) { debug_msg("Area %s\n", pr_id(area).c_str()); return _external.announce_complete(area); } template void PeerManager::external_push(OspfTypes::AreaID area) { debug_msg("Area %s\n", pr_id(area).c_str()); AreaRouter *area_router = get_area_router(area); // Verify that this area is known. if (0 == area_router) { XLOG_FATAL("Unknown area %s", pr_id(area).c_str()); } return _external.push(area_router); } template class PeerManager; template class PeerManager; xorp/ospf/external.hh0000664000076400007640000002070011421137511014765 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/ospf/external.hh,v 1.24 2008/10/02 21:57:47 bms Exp $ #ifndef __OSPF_EXTERNAL_HH__ #define __OSPF_EXTERNAL_HH__ #include /** * Storage for AS-external-LSAs with efficient access. */ class ASExternalDatabase { public: struct compare { bool operator ()(const Lsa::LsaRef a, const Lsa::LsaRef b) const { if (a->get_header().get_link_state_id() == b->get_header().get_link_state_id()) return a->get_header().get_advertising_router() < b->get_header().get_advertising_router(); return a->get_header().get_link_state_id() < b->get_header().get_link_state_id(); } }; typedef set ::iterator iterator; iterator begin() { return _lsas.begin(); } iterator end() { return _lsas.end(); } void erase(iterator i) { _lsas.erase(i); } void insert(Lsa::LsaRef lsar) { _lsas.insert(lsar); } void clear(); iterator find(Lsa::LsaRef lsar); private: set _lsas; // Stored AS-external-LSAs. }; /** * Handle AS-external-LSAs. */ template class External { public: External(Ospf& ospf, map *>& areas); /** * Candidate for announcing to other areas. Store this LSA for * future replay into other areas. Also arrange for the MaxAge * timer to start running. * * @param area the AS-external-LSA came from. * * @return true if this LSA should be propogated to other areas. */ bool announce(OspfTypes::AreaID area, Lsa::LsaRef lsar); /** * Called to complete a series of calls to announce(). */ bool announce_complete(OspfTypes::AreaID area); /** * Provide this area with the stored AS-external-LSAs. */ void push(AreaRouter *area_router); /** * A true external route redistributed from the RIB (announce). */ bool announce(IPNet net, A nexthop, uint32_t metric, const PolicyTags& policytags); /** * A true external route redistributed from the RIB (withdraw). */ bool withdraw(const IPNet& net); /** * Clear the AS-external-LSA database. */ bool clear_database(); /** * Suppress or remove AS-external-LSAs that are originated by * this router that should yield to externally generated * AS-external-LSAs as described in RFC 2328 Section 12.4.4. By * time this method is called the routing table should have been * updated so it should be clear if the other router is reachable. */ void suppress_lsas(OspfTypes::AreaID area); /** * A route has just been added to the routing table. */ void suppress_route_announce(OspfTypes::AreaID area, IPNet net, RouteEntry& rt); /** * A route has just been withdrawn from the routing table. */ void suppress_route_withdraw(OspfTypes::AreaID area, IPNet net, RouteEntry& rt); /** * Is this an AS boundary router? */ bool as_boundary_router_p() const { return _originating != 0; } /** * Re-run the policy filters on all routes. */ void push_routes(); private: Ospf& _ospf; // Reference to the controlling class. map *>& _areas; // All the areas ASExternalDatabase _lsas; // Stored AS-external-LSAs. uint32_t _originating; // Number of AS-external-LSAs // that are currently being originated. uint32_t _lsid; // OSPFv3 only next Link State ID. map, uint32_t> _lsmap; // OSPFv3 only list _suppress_temp; // LSAs that could possibly // suppress self originated LSAs #ifdef SUPPRESS_DB Trie _suppress_db; // Database of suppressed self // originated LSAs #endif /** * Find this LSA */ ASExternalDatabase::iterator find_lsa(Lsa::LsaRef lsar); /** * Add this LSA to the database if it already exists replace it * with this entry. */ void update_lsa(Lsa::LsaRef lsar); /** * Delete this LSA from the database. */ void delete_lsa(Lsa::LsaRef lsar); /** * This LSA has reached MaxAge get rid of it from the database and * flood it out of all areas. */ void maxage_reached(Lsa::LsaRef lsar); /** * Networks with same network number but different prefix lengths * can generate the same link state ID. When generating a new LSA * if a collision occurs use: * RFC 2328 Appendix E. An algorithm for assigning Link State IDs * to resolve the clash. */ void unique_link_state_id(Lsa::LsaRef lsar); /** * Networks with same network number but different prefix lengths * can generate the same link state ID. When looking for an LSA * make sure that there the lsar that matches the net is found. * * @param lsar search for this LSA in the database. WARNING this * LSA may have its link state ID field modifed by the search. * @param net that must match the LSA. */ ASExternalDatabase::iterator unique_find_lsa(Lsa::LsaRef lsar, const IPNet& net); /** * Set the network, nexthop and link state ID. */ void set_net_nexthop_lsid(ASExternalLsa *aselsa, IPNet net, A nexthop); /** * Send this self originated LSA out. */ void announce_lsa(Lsa::LsaRef lsar); /** * Pass this outbound AS-external-LSA through the policy filter. */ bool do_filtering(IPNet& network, A& nexthop, uint32_t& metric, bool& e_bit, uint32_t& tag, bool& tag_set, const PolicyTags& policytags); /** * Start the refresh timer. */ void start_refresh_timer(Lsa::LsaRef lsar); /** * Called every LSRefreshTime seconds to refresh this LSA. */ void refresh(Lsa::LsaRef lsar); /** * Clone a self orignated LSA that is about to be removed for * possible later introduction. */ Lsa::LsaRef clone_lsa(Lsa::LsaRef lsar); /** * Is this a self originated AS-external-LSA a candidate for suppression? */ bool suppress_candidate(Lsa::LsaRef lsar, IPNet net, A nexthop, uint32_t metric); #ifdef SUPPRESS_DB /** * Store a self originated AS-external-LSA that has been * suppressed for possible future revival. */ void suppress_database_add(Lsa::LsaRef lsar, const IPNet& net); /** * Delete a self originated AS-external-LSA that is no longer * being redistributed. */ void suppress_database_delete(const IPNet& net, bool invalidate); #endif /** * Should this AS-external-LSA cause a self originated LSA to be * suppressed. */ void suppress_self(Lsa::LsaRef lsar); /** * Should this AS-external-LSA cause a self originated LSA to be * suppressed. */ bool suppress_self_check(Lsa::LsaRef lsar); /** * Find a self originated AS-external-LSA by network. */ Lsa::LsaRef find_lsa_by_net(IPNet net); /** * This LSA if its advertising router is reachable matches a self * origniated LSA that will be suppressed. Store until the routing * computation has completed and the routing table can be checked. */ void suppress_queue_lsa(Lsa::LsaRef lsar); /** * An AS-external-LSA has reached MaxAge and is being withdrawn. * check to see if it was suppressing a self originated LSA. */ void suppress_maxage(Lsa::LsaRef lsar); /** * If this is an AS-external-LSA that is suppressing a self * originated route, then release it. */ void suppress_release_lsa(Lsa::LsaRef lsar); }; #endif // __OSPF_EXTERNAL_HH__ xorp/deb_hand.mak0000664000076400007640000000627611421137511014105 0ustar greearbgreearb# Create .deb without using dpkg tools. # # Author: Tim Wegener # # Use 'include deb_hand.mak' after defining the user variables in a local # makefile. # # The 'data' rule must be customised in the local make file. # This rule should make a 'data' directory containing the full file # layout of the installed package. # # This makefile will create a debian-binary file a control directory and a # a build directory in the current directory. # Do 'make clobber' to remove these generated files. # # Destination: # PACKAGE_DIR - directory where package (and support files) will be built # defaults to the current directory # # Sources: # SOURCE_DIR - directory containing files to be packaged # ICON_SOURCE - 26x26 icon file for maemo # description.txt - description with summary on first line # preinst, postinst, prerm, postrm - optional control shell scripts # These fields are used to build the control file: # PACKAGE = # VERSION = # ARCH = # SECTION = # PRIORITY = # MAINTAINER = # DEPENDS = # # SOURCE_DIR = # ICON_SOURCE = # (ICON_SOURCE is optional) # *** NO USER CHANGES REQUIRED BEYOND THIS POINT *** CONTROL_EXTRAS ?= ${wildcard preinst postinst prerm postrm} ${PACKAGE_DIR}/control: ${PACKAGE_DIR}/data ${CONTROL_EXTRAS} description.txt \ ${ICON_SOURCE} rm -rf $@ mkdir $@ ifneq (${CONTROL_EXTRAS},) cp ${CONTROL_EXTRAS} $@ endif # Make control file. echo "Package: ${PACKAGE}" > $@/control echo "Version: ${VERSION}" >> $@/control echo "Section: ${SECTION}" >> $@/control echo "Priority: ${PRIORITY}" >> $@/control echo "Architecture: ${ARCH}" >> $@/control echo "Depends: ${DEPENDS}" >> $@/control echo "Installed-Size: ${shell du -s ${PACKAGE_DIR}/data|cut -f1}" \ >> $@/control echo "Maintainer: ${MAINTAINER}" >> $@/control echo -n "Description:" >> $@/control cat description.txt | gawk '{print " "$$0;}' >> $@/control ifneq (${ICON_SOURCE},) echo "Maemo-Icon-26:" >> $@/control base64 ${ICON_SOURCE} | gawk '{print " "$$0;}' >> $@/control endif # Make md5sums. cd ${PACKAGE_DIR}/data && find . -type f -exec md5sum {} \; \ | sed -e 's| \./||' \ > ../md5sums ${PACKAGE_DIR}/debian-binary: mkdir -p ${PACKAGE_DIR} echo "2.0" > $@ ${PACKAGE_DIR}/build: ${PACKAGE_DIR}/debian-binary ${PACKAGE_DIR}/control \ ${PACKAGE_DIR}/data rm -rf $@ mkdir $@ cp ${PACKAGE_DIR}/debian-binary $@/ cd ${PACKAGE_DIR}/control && tar czvf ../build/control.tar.gz ./* cd ${PACKAGE_DIR}/data && tar czvf ../build/data.tar.gz ./* # Convert GNU ar to BSD ar that debian requires. # Note: Order of files within ar archive is important! # NOTE: This doesn't actually work, and doesn't seem to be generally required. --Ben ${PACKAGE_DIR}/${PACKAGE}_${VERSION}_${ARCH}.deb: ${PACKAGE_DIR}/build ar -rc $@tmp $ $@fail #rm -f $@tmp #mv $@fail $@ mv $@tmp $@ .PHONY: data data: ${PACKAGE_DIR}/data .PHONY: control control: ${PACKAGE_DIR}/control .PHONY: build build: ${PACKAGE_DIR}/build .PHONY: deb deb: ${PACKAGE_DIR}/${PACKAGE}_${VERSION}_${ARCH}.deb clobber:: rm -rf ${PACKAGE_DIR}/debian_binary ${PACKAGE_DIR}/control \ ${PACKAGE_DIR}/data ${PACKAGE_DIR}/build xorp/libxorp/0000775000076400007640000000000011703345405013341 5ustar greearbgreearbxorp/libxorp/xorp_osdep_mid.h0000664000076400007640000001561611540224230016525 0ustar greearbgreearb/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- */ /* vim:set sts=4 ts=8: */ /* * Copyright (c) 2001-2011 XORP, Inc and Others * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License, Version * 2.1, June 1999 as published by the Free Software Foundation. * Redistribution and/or modification of this program under the terms of * any other version of the GNU Lesser General Public License is not * permitted. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, * see the GNU Lesser General Public License, Version 2.1, a copy of * which can be found in the XORP LICENSE.lgpl file. * * XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; * http://xorp.net */ #ifndef __LIBXORP_XORP_OSDEP_MID_H__ #define __LIBXORP_XORP_OSDEP_MID_H__ /*------------------------------------------------------------------------*/ /* * Windows-specific preprocessor definitions. */ #if defined(HOST_OS_WINDOWS) #include #include #include #include #include #include #ifdef __cplusplus extern "C" { #endif #ifndef IPPROTO_PIM #define IPPROTO_PIM 103 #endif #ifndef IN_EXPERIMENTAL #define IN_EXPERIMENTAL(i) (((uint32_t)(i) & 0xf0000000) == 0xf0000000) #endif #ifndef IN_BADCLASS #define IN_BADCLASS(i) (((uint32_t)(i) & 0xf0000000) == 0xf0000000) #endif #ifndef INADDR_MAX_LOCAL_GROUP #define INADDR_MAX_LOCAL_GROUP (uint32_t)0xe00000ff /* 224.0.0.255 */ #endif #ifndef IN_LOOPBACKNET #define IN_LOOPBACKNET 127 #endif #ifndef IFNAMSIZ #define IFNAMSIZ 255 #endif #ifndef IF_NAMESIZE #define IF_NAMESIZE 255 #endif #ifndef IP_MAXPACKET #define IP_MAXPACKET 65535 #endif #ifndef MAXHOSTNAMELEN #define MAXHOSTNAMELEN MAX_HOSTNAME_LEN #endif #ifndef IPVERSION #define IPVERSION 4 #endif /* * iovec is common enough that we ship our own for Windows, * with the members swapped around to be compatible with * Winsock2's scatter/gather send functions. */ struct iovec { u_long iov_len; /* Length. (len in WSABUF) */ char *iov_base; /* Base address. (buf in WSABUF) */ }; /* From netinet/ip.h */ /* * Structure of an internet header, naked of options. */ struct ip { uint8_t ip_h_v; /* header & version..which is first depends on byte order. */ //#if __BYTE_ORDER == __LITTLE_ENDIAN // unsigned int ip_hl:4; /* header length */ // unsigned int ip_v:4; /* version */ //#endif //#if __BYTE_ORDER == __BIG_ENDIAN // unsigned int ip_v:4; /* version */ // unsigned int ip_hl:4; /* header length */ //#endif uint8_t ip_tos; /* type of service */ unsigned short ip_len; /* total length */ unsigned short ip_id; /* identification */ unsigned short ip_off; /* fragment offset field */ #define IP_RF 0x8000 /* reserved fragment flag */ #define IP_DF 0x4000 /* dont fragment flag */ #define IP_MF 0x2000 /* more fragments flag */ #define IP_OFFMASK 0x1fff /* mask for fragmenting bits */ uint8_t ip_ttl; /* time to live */ uint8_t ip_p; /* protocol */ unsigned short ip_sum; /* checksum */ struct in_addr ip_src, ip_dst; /* source and dest address */ }; struct ip6_hdr { union { struct ip6_hdrctl { uint32_t ip6_un1_flow; /* 4 bits version, 8 bits TC, 20 bits flow-ID */ uint16_t ip6_un1_plen; /* payload length */ uint8_t ip6_un1_nxt; /* next header */ uint8_t ip6_un1_hlim; /* hop limit */ } ip6_un1; uint8_t ip6_un2_vfc; /* 4 bits version, top 4 bits tclass */ } ip6_ctlun; struct in6_addr ip6_src; /* source address */ struct in6_addr ip6_dst; /* destination address */ }; #define ip6_flow ip6_ctlun.ip6_un1.ip6_un1_flow /*------------------------------------------------------------------------*/ #ifdef __cplusplus } #endif typedef HANDLE xfd_t; typedef SOCKET xsock_t; typedef unsigned long gid_t; /* XXX: This is a hack. */ typedef unsigned long uid_t; /* XXX: This is a hack. */ #ifdef _NO_OLDNAMES /* * When _NO_OLDNAMES is defined, we need to put certain things * back into the namespace. * * XXX: Workaround a problem whereby some of the tests which ship with * GNU autoconf will try to define pid_t in an environment which * does not have it. */ //#ifdef pid_t //#undef pid_t //#endif //typedef _off_t off_t; //typedef _pid_t pid_t; typedef long ssize_t; /* XXX: This is a hack. */ typedef _sigset_t sigset_t; /* XXX: Appease libtecla. */ /* XXX: This is a hack. */ #define getpid() ( (pid_t) GetCurrentProcessId() ) #define isascii(c) __isascii((c)) #define snprintf _snprintf #define sleep _sleep #define strdup(s) _strdup((s)) #define strcasecmp(s1, s2) _stricmp((s1),(s2)) #define vsnprintf _vsnprintf #else /* !_NO_OLDNAMES */ #define sleep Sleep #endif #define XORP_BAD_FD INVALID_HANDLE_VALUE #define XORP_BAD_SOCKET INVALID_SOCKET /* * Windows expects (char *) and (const char *) pointers for transmitted * data and socket option data. */ #define XORP_BUF_CAST(x) ((char *)(x)) #define XORP_CONST_BUF_CAST(x) ((const char *)(x)) #define XORP_SOCKOPT_CAST(x) ((char *)(x)) /* I/O errors pertaining to non-blocking sockets. */ #ifndef EWOULDBLOCK #define EWOULDBLOCK WSAEWOULDBLOCK #endif #else /* !HOST_OS_WINDOWS */ /* Use a portable file descriptor type. */ typedef int xfd_t; typedef int xsock_t; /* Use Windows-a-likes for checking return values from I/O syscalls. */ #define XORP_BAD_FD ((xfd_t) -1) #define XORP_BAD_SOCKET XORP_BAD_FD /* * The rest of the world expects void pointers to transmitted * data and socket option data. */ #define XORP_BUF_CAST(x) ((void *)(x)) #define XORP_CONST_BUF_CAST(x) ((const void *)(x)) #define XORP_SOCKOPT_CAST(x) (x) #endif /* HOST_OS_WINDOWS */ /*------------------------------------------------------------------------*/ /* * Portable missing functions. */ #ifdef __cplusplus extern "C" { #endif #ifndef HAVE_STRLCPY size_t strlcpy(char *dst, const char *src, size_t size); #endif /* * IP address presentation routines. */ #ifndef HAVE_INET_PTON int inet_pton(int af, const char *src, void *dst); #endif #ifndef HAVE_INET_NTOP const char *inet_ntop(int af, const void *src, char *dst, size_t size); #endif /* * getopt. */ #ifdef NEED_GETOPT extern char *optarg; extern int optind; extern int optopt; extern int opterr; extern int optreset; int getopt(int argc, char * const argv[], const char *optstring); #endif /* NEED_GETOPT */ #ifdef __cplusplus } #endif #endif /* __LIBXORP_XORP_OSDEP_MID_H__ */ xorp/libxorp/ipnet.cc0000664000076400007640000001000411421137511014754 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "xorp.h" #include "ipnet.hh" // // Storage for defining specialized class methods for the IPNet template // class. // // // XXX: This method applies only for IPv4, hence we don't provide IPv6 // specialized version. // template <> const IPNet IPNet::ip_class_a_base_prefix() { return IPNet(IPv4::CLASS_A_BASE(), IPv4::ip_class_a_base_address_mask_len()); } // // XXX: This method applies only for IPv4, hence we don't provide IPv6 // specialized version. // template <> const IPNet IPNet::ip_class_b_base_prefix() { return IPNet(IPv4::CLASS_B_BASE(), IPv4::ip_class_b_base_address_mask_len()); } // // XXX: This method applies only for IPv4, hence we don't provide IPv6 // specialized version. // template <> const IPNet IPNet::ip_class_c_base_prefix() { return IPNet(IPv4::CLASS_C_BASE(), IPv4::ip_class_c_base_address_mask_len()); } // // XXX: This method applies only for IPv4, hence we don't provide IPv6 // specialized version. // template <> const IPNet IPNet::ip_experimental_base_prefix() { return IPNet(IPv4::EXPERIMENTAL_BASE(), IPv4::ip_experimental_base_address_mask_len()); } // // XXX: This method applies only for IPv4, hence we don't provide IPv6 // specialized version. // template <> bool IPNet::is_class_a() const { return (ip_class_a_base_prefix().contains(*this)); } // // XXX: This method applies only for IPv4, hence we don't provide IPv6 // specialized version. // template <> bool IPNet::is_class_b() const { return (ip_class_b_base_prefix().contains(*this)); } // // XXX: This method applies only for IPv4, hence we don't provide IPv6 // specialized version. // template <> bool IPNet::is_class_c() const { return (ip_class_c_base_prefix().contains(*this)); } // // XXX: This method applies only for IPv4, hence we don't provide IPv6 // specialized version. // template <> bool IPNet::is_experimental() const { return (ip_experimental_base_prefix().contains(*this)); } template <> bool IPNet::is_unicast() const { // // In case of IPv4 all prefixes that fall within the Class A, Class B or // Class C address space are unicast. // Note that the default route (0.0.0.0/0 for IPv4 or ::/0 for IPv6) // is also considered an unicast prefix. // if (prefix_len() == 0) { // The default route or a valid unicast route return (true); } if (ip_class_a_base_prefix().contains(*this) || ip_class_b_base_prefix().contains(*this) || ip_class_c_base_prefix().contains(*this)) { return (true); } return (false); } template <> bool IPNet::is_unicast() const { // // In case of IPv6 all prefixes that don't contain the multicast // address space are unicast. // Note that the default route (0.0.0.0/0 for IPv4 or ::/0 for IPv6) // is also considered an unicast prefix. // if (prefix_len() == 0) { // The default route or a valid unicast route return (true); } IPNet base_prefix = ip_multicast_base_prefix(); if (this->contains(base_prefix)) return (false); if (base_prefix.contains(*this)) return (false); return (true); } xorp/libxorp/xorp_osdep_begin.h0000664000076400007640000000444211630245722017044 0ustar greearbgreearb/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- */ /* vim:set sts=4 ts=8: */ /* * Copyright (c) 2001-2011 XORP, Inc and Others * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License, Version * 2.1, June 1999 as published by the Free Software Foundation. * Redistribution and/or modification of this program under the terms of * any other version of the GNU Lesser General Public License is not * permitted. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, * see the GNU Lesser General Public License, Version 2.1, a copy of * which can be found in the XORP LICENSE.lgpl file. * * XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; * http://xorp.net */ #ifndef __LIBXORP_XORP_OSDEP_BEGIN_H__ #define __LIBXORP_XORP_OSDEP_BEGIN_H__ #ifdef HOST_OS_WINDOWS #define USE_WIN_DISPATCHER 1 #define WIN32_LEAN_AND_MEAN /* Do not include most headers */ #define _UNICODE /* Use Unicode APIs */ #define UNICODE #define FD_SETSIZE 1024 /* Default is 64, use BSD default */ #define WINVER 0x502 /* Windows Server 2003 target */ #define _WIN32_WINNT 0x502 /* * Reduce the size of the Windows namespace, by leaving out * that which we do not need. * (Source: Windows Systems Programming 3e, Hart, 2004) */ #define NOATOM #define NOCLIPBOARD #define NOCOMM #define NOCTLMGR #define NOCOLOR #define NODEFERWINDOWPOS #define NODESKTOP #define NODRAWTEXT #define NOEXTAPI #define NOGDICAPMASKS #define NOHELP #define NOICONS #define NOTIME #define NOIMM #define NOKANJI #define NOKERNEL #define NOKEYSTATES #define NOMCX #define NOMEMMGR #define NOMENUS #define NOMETAFILE #define NOMSG #define NONCMESSAGES #define NOPROFILER #define NORASTEROPS #define NORESOURCE #define NOSCROLL #define NOSERVICE #define NOSHOWWINDOW #define NOSOUND #define NOSYSCOMMANDS #define NOSYSMETRICS #define NOSYSPARAMS #define NOTEXTMETRIC #define NOVIRTUALKEYCODES #define NOWH #define NOWINDOWSTATION #define NOWINMESSAGES #define NOWINOFFSETS #define NOWINSTTLES #define OEMRESOURCE #endif /* HOST_OS_WINDOWS */ #endif /* __LIBXORP_XORP_OSDEP_BEGIN_H__ */ xorp/libxorp/ipv4.hh0000664000076400007640000006157311540224227014555 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __LIBXORP_IPV4_HH__ #define __LIBXORP_IPV4_HH__ #include "libxorp/xorp.h" #include "libxorp/exceptions.hh" #include "libxorp/range.hh" #include "libxorp/utils.hh" struct in_addr; /** * @short IPv4 address class * * The IPv4 address class is a trivial class for handling IPv4 * addresses and for performing operations on them such as printing * and masking. */ class IPv4 { public: typedef in_addr InAddrType; typedef sockaddr_in SockAddrType; public: /** * Default constructor * * The address value is initialized to INADDR_ANY. */ IPv4() { _addr = 0; } /** * Constructor from another IPv4 address. * * @param ipv4 the IPv4 address to assign the address value from. */ IPv4(const IPv4& ipv4) : _addr(ipv4._addr) {} /** * Constructor from an integer value. * * @param value 32-bit unsigned integer to assign to the address. */ explicit IPv4(uint32_t value) { _addr = value; } /** * Constructor from a (uint8_t *) memory pointer. * * @param from_uint8 the pointer to the memory to copy the address value * from. */ explicit IPv4(const uint8_t *from_uint8); /** * Constructor from in_addr structure. * * @param from_in_addr the storage to copy the address value from. */ IPv4(const in_addr& from_in_addr); /** * Constructor from sockaddr structure. * * @param sa sockaddr to construct IPv4 addr from. */ IPv4(const sockaddr& sa) throw (InvalidFamily); /** * Constructor from sockaddr_storage structure. * * @param ss sockaddr_storage to construct IPv4 addr from. */ IPv4(const sockaddr_storage& ss) throw (InvalidFamily); /** * Constructor from sockaddr_in structure. * * @param sin sockaddr_in to construct IPv4 addr from. */ IPv4(const sockaddr_in& sin) throw (InvalidFamily); /** * Constructor from a string. * * @param from_cstring C-style string in the IPv4 dotted decimal * human-readable format used for initialization. */ IPv4(const char *from_string) throw (InvalidString); /** * Copy the IPv4 raw address to specified memory location. * * @param: to_uint8 the pointer to the memory to copy the address to. * @return the number of copied octets. */ size_t copy_out(uint8_t *to_uint8) const; /** * Copy the IP4 raw address to an in_addr structure. * * @param to_in_addr the storage to copy the address to. * @return the number of copied octets. */ size_t copy_out(in_addr& to_in_addr) const; /** * Copy the IPv4 raw address to a sockaddr structure. * * Copy the raw address held within an IPv4 instance to an sockaddr * structure and assign appropriately and set fields within sockaddr * appropriately. * * @param to_sockaddr the storage to copy the address to. * @return the number of copied octets. */ size_t copy_out(sockaddr& to_sockaddr) const; /** * Copy the IPv4 raw address to a sockaddr_storage structure. * * Copy the raw address held within an IPv4 instance to an sockaddr_storage * structure and assign appropriately and set fields within * sockaddr_storage appropriately. * * @param to_sockaddr_storage the storage to copy the address to. * @return the number of copied octets. */ size_t copy_out(sockaddr_storage& to_sockaddr_storage) const; /** * Copy the IPv4 raw address to a sockaddr_in structure. * * Copy the raw address held within an IPv4 instance to an sockaddr_in * structure and assign appropriately and set fields within sockaddr_in * appropriately. * * @param to_sockaddr_in the storage to copy the address to. * @return the number of copied octets. */ size_t copy_out(sockaddr_in& to_sockaddr_in) const; /** * Copy a raw IPv4 address from specified memory location into IPv4 * structure. * * @param from_uint8 the memory address to copy the address from. * @return the number of copied octets. */ size_t copy_in(const uint8_t *from_uint8); /** * Copy a raw IPv4 address from a in_addr structure into IPv4 structure. * * @param from_in_addr the storage to copy the address from. * @return the number of copied octets. */ size_t copy_in(const in_addr& from_in_addr); /** * Copy a raw address from a sockaddr structure into IPv4 structure. * * Note that the address in the sockaddr structure must be of IPv4 address * family. * * @param from_sockaddr the storage to copy the address from. * @return the number of copied octets. */ size_t copy_in(const sockaddr& from_sockaddr) throw (InvalidFamily); /** * Copy a raw address from a sockaddr_storage structure into IPv4 * structure. * * Note that the address in the sockaddr_storage structure must be of * IPv4 address family. * * @param from_sockaddr_storage the storage to copy the address from. * @return the number of copied octets. */ size_t copy_in(const sockaddr_storage& from_sockaddr_storage) throw (InvalidFamily); /** * Copy a raw address from a sockaddr_in structure into IPv4 structure. * * Note that the address in the sockaddr structure must be of IPv4 address * family. * * @param from_sockaddr_in the storage to copy the address from. * @return the number of copied octets. */ size_t copy_in(const sockaddr_in& from_sockaddr_in) throw (InvalidFamily); /** * Bitwise-Negation Operator * * @return address complement (i.e., all 0s become 1s, and vice-versa). */ IPv4 operator~() const { return IPv4(~_addr); } /** * OR Operator * * @param other the right-hand operand to OR with. * @return bitwise OR of two addresses. */ IPv4 operator|(const IPv4& other) const { return IPv4(_addr | other._addr); } /** * AND Operator * * @param other the right-hand operand to AND with. * @return bitwise AND of two addresses. */ IPv4 operator&(const IPv4& other) const { return IPv4(_addr & other._addr); } /** * XOR Operator * * @return eXclusive-OR of two addresses. */ IPv4 operator^(const IPv4& other) const { return IPv4(_addr ^ other._addr); } /** * Operator << * * @param left_shift the number of bits to shift to the left. * @return IPv4 address that is shift bitwise to the left. */ IPv4 operator<<(uint32_t left_shift) const; /** * Operator >> * * @param right_shift the number of bits to shift to the right. * @return IPv4 address that is shift bitwise to the right. */ IPv4 operator>>(uint32_t right_shift) const; /** * Less-Than Operator * * @param other the right-hand operand to compare against. * @return true if the left-hand operand is numerically smaller than the * right-hand operand. */ bool operator<(const IPv4& other) const; /** * Equality Operator * * @param other the right-hand operand to compare against. * @return true if the left-hand operand is numerically same as the * right-hand operand. */ bool operator==(const IPv4& other) const { return (_addr == other._addr); } /** * Not-Equal Operator * * @param other the right-hand operand to compare against. * @return true if the left-hand operand is numerically not same as the * right-hand operand. */ bool operator!=(const IPv4& other) const { return (_addr != other._addr); } /** * Equality Operator for @ref IPv4 against @ref IPv4Range operand. * * @param rhs the right-hand @ref IPv4Range operand. * @return true if the value of the left-hand operand falls inside * the range defined by the right-hand operand. */ bool operator==(const IPv4Range& rhs) const { return (_addr >= rhs.low().addr() && _addr <= rhs.high().addr()); } /** * Not-equal Operator for @ref IPv4 against @ref IPv4Range operand. * * @param rhs the right-hand @ref IPv4Range operand. * @return true if the value of the left-hand operand falls outside * the range defined by the right-hand operand. */ bool operator!=(const IPv4Range& rhs) const { return (_addr < rhs.low().addr() || _addr > rhs.high().addr()); } /** * Less-than comparison for @ref IPv4 against @ref IPv4Range operand. * * @param rhs the right-hand @ref IPv4Range operand. * @return true if the value of the left-hand operand is bellow * the range defined by the right-hand operand. */ bool operator<(const IPv4Range& rhs) const { return (_addr < rhs.low().addr()); } /** * Less-than or equal comparison for @ref IPv4 against @ref IPv4Range * * @param rhs the right-hand @ref IPv4Range operand. * @return true if the value of the left-hand operand is bellow or within * the range defined by the right-hand operand. */ bool operator<=(const IPv4Range& rhs) const { return (_addr <= rhs.high().addr()); } /** * Greater-than comparison for @ref IPv4 against @ref IPv4Range operand. * * @param rhs the right-hand @ref IPv4Range operand. * @return true if the value of the left-hand operand is above * the range defined by the right-hand operand. */ bool operator>(const IPv4Range& rhs) const { return (_addr > rhs.high().addr()); } /** * Greater-than or equal comparison for @ref IPv4 against @ref IPv4Range * * @param rhs the right-hand @ref IPv4Range operand. * @return true if the value of the left-hand operand is above or within * the range defined by the right-hand operand. */ bool operator>=(const IPv4Range& rhs) const { return (_addr >= rhs.low().addr()); } /** * Decrement Operator * * The numerical value of this address is decremented by one. * However, if the address value before the decrement was all-0s, * after the decrement its value would be all-1s (i.e., it will * wrap-around). * * @return a reference to this address after it was decremented by one. */ IPv4& operator--(); /** * Increment Operator * * The numerical value of this address is incremented by one. * However, if the address value before the increment was all-1s, * after the increment its value would be all-0s (i.e., it will * wrap-around). * * @return a reference to this address after it was incremented by one. */ IPv4& operator++(); /** * Convert this address from binary form to presentation format. * * @return C++ string with the human-readable ASCII representation * of the address. */ string str() const; /** * Test if this address is numerically zero. * * @return true if the address is numerically zero. */ bool is_zero() const { return (_addr == 0); } /** * Test if this address is a valid unicast address. * * Note that the numerically zero address is excluded. * * @return true if the address is a valid unicast address. */ bool is_unicast() const; /** * Test if this address is a valid multicast address. * * @return true if the address is a valid multicast address. */ bool is_multicast() const; /** * Test if this address belongs to the IPv4 Class A * address space (0.0.0.0/1). * * @return true if the address is a valid Class A address. */ bool is_class_a() const; /** * Test if this address belongs to the IPv4 Class B * address space (128.0.0.0/2). * * @return true if the address is a valid Class B address. */ bool is_class_b() const; /** * Test if this address belongs to the IPv4 Class C * address space (192.0.0.0/3). * * @return true if the address is a valid Class C address. */ bool is_class_c() const; /** * Test if this address belongs to the IPv4 experimental Class E * address space (240.0.0.0/4). * * @return true if the address is a valid experimental address. */ bool is_experimental() const; /** * Test if this address is a valid link-local unicast address. * * @return true if the address is a valid unicast address, * and the scope of the address is link-local. */ bool is_linklocal_unicast() const; /** * Test if this address is a valid interface-local multicast address. * * Note that "node-local" multicast addresses were renamed * to "interface-local" by RFC-3513. * * @return true if the address is a valid multicast address, * and the scope of the address is interface-local. * XXX: in IPv4 there is no interface-local multicast scope, therefore * the return value is always false. */ bool is_interfacelocal_multicast() const; /** * Test if this address is a valid node-local multicast address. * * Note that "node-local" multicast addresses were renamed * to "interface-local" by RFC-3513. * This method is kept for backward compatibility. * * @return true if the address is a valid multicast address, * and the scope of the address is node-local. * XXX: in IPv4 there is no node-local multicast scope, therefore * the return value is always false. */ bool is_nodelocal_multicast() const { return is_interfacelocal_multicast(); } /** * Test if this address is a valid link-local multicast address. * * @return true if the address is a valid multicast address, * and the scope of the address is link-local. */ bool is_linklocal_multicast() const; /** * Test if this address is a valid loopback address. * * @return true if the address is a valid loopback address. */ bool is_loopback() const; /** * Get the address octet-size. * * Note that this is a static function and can be used without * a particular object. Example: * size_t my_size = IPv4::addr_bytelen(); * size_t my_size = ipv4.addr_bytelen(); * * @return address size in number of octets. */ static size_t addr_bytelen() { x_static_assert(sizeof(IPv4) == sizeof(uint32_t)); return sizeof(IPv4); } /** * Get the address bit-length. * * Note that this is a static function and can be used without * a particular object. Example: * uint32_t my_bitlen = IPv4::addr_bitlen(); * uint32_t my_bitlen = ipv4.addr_bitlen(); * * @return address size in number of bits. */ static uint32_t addr_bitlen() { return uint32_t(8 * sizeof(uint8_t) * addr_bytelen()); } /** * Get the mask length for the multicast base address. * * Note that this is a static function and can be used without * a particular object. Example: * uint32_t my_len = IPv4::ip_multicast_base_address_mask_len(); * uint32_t my_len = ipv4.ip_multicast_base_address_mask_len(); * * @return the multicast base address mask length for family AF_INET. */ static uint32_t ip_multicast_base_address_mask_len() { #define IP_MULTICAST_BASE_ADDRESS_MASK_LEN_IPV4 4 return (IP_MULTICAST_BASE_ADDRESS_MASK_LEN_IPV4); #undef IP_MULTICAST_BASE_ADDRESS_MASK_LEN_IPV4 } /** * Get the mask length for the Class A base address. * * Note that this is a static function and can be used without * a particular object. Example: * uint32_t my_len = IPv4::ip_class_a_base_address_mask_len(); * uint32_t my_len = ipv4.ip_class_a_base_address_mask_len(); * * @return the Class A base address mask length for family AF_INET. */ static uint32_t ip_class_a_base_address_mask_len() { #define IP_CLASS_A_BASE_ADDRESS_MASK_LEN_IPV4 1 return (IP_CLASS_A_BASE_ADDRESS_MASK_LEN_IPV4); #undef IP_CLASS_A_BASE_ADDRESS_MASK_LEN_IPV4 } /** * Get the mask length for the Class B base address. * * Note that this is a static function and can be used without * a particular object. Example: * uint32_t my_len = IPv4::ip_class_b_base_address_mask_len(); * uint32_t my_len = ipv4.ip_class_b_base_address_mask_len(); * * @return the Class B base address mask length for family AF_INET. */ static uint32_t ip_class_b_base_address_mask_len() { #define IP_CLASS_B_BASE_ADDRESS_MASK_LEN_IPV4 2 return (IP_CLASS_B_BASE_ADDRESS_MASK_LEN_IPV4); #undef IP_CLASS_B_BASE_ADDRESS_MASK_LEN_IPV4 } /** * Get the mask length for the Class C base address. * * Note that this is a static function and can be used without * a particular object. Example: * uint32_t my_len = IPv4::ip_class_c_base_address_mask_len(); * uint32_t my_len = ipv4.ip_class_c_base_address_mask_len(); * * @return the Class C base address mask length for family AF_INET. */ static uint32_t ip_class_c_base_address_mask_len() { #define IP_CLASS_C_BASE_ADDRESS_MASK_LEN_IPV4 3 return (IP_CLASS_C_BASE_ADDRESS_MASK_LEN_IPV4); #undef IP_CLASS_C_BASE_ADDRESS_MASK_LEN_IPV4 } /** * Get the mask length for the experimental base address. * * Note that this is a static function and can be used without * a particular object. Example: * uint32_t my_len = IPv4::ip_experimental_base_address_mask_len(); * uint32_t my_len = ipv4.ip_experimental_base_address_mask_len(); * * @return the experimental base address mask length for family AF_INET. */ static uint32_t ip_experimental_base_address_mask_len() { #define IP_EXPERIMENTAL_BASE_ADDRESS_MASK_LEN_IPV4 4 return (IP_EXPERIMENTAL_BASE_ADDRESS_MASK_LEN_IPV4); #undef IP_EXPERIMENTAL_BASE_ADDRESS_MASK_LEN_IPV4 } /** * Make an IPv4 mask prefix. * * @param mask_len the length of the mask to create. * @return a new IPv4 address that contains a mask of length @ref mask_len. */ static IPv4 make_prefix(uint32_t mask_len) throw (InvalidNetmaskLength); /** * Make an IPv4 address prefix. * * @param prefix_len the length of the mask of the prefix to create. * @return a new IPv4 address created by masking this address with a mask * of length @ref prefix_len. */ IPv4 mask_by_prefix_len(uint32_t mask_len) const throw (InvalidNetmaskLength) { return (*this) & make_prefix(mask_len); } /** * Get the mask length. * * @return the prefix length of the contiguous mask presumably stored * as an IPv4 address. */ uint32_t mask_len() const; /** * Get the uint32_t raw value of this address. * * @return the value of this IPv4 address as an unsigned 32-bit integer. */ uint32_t addr() const { return _addr; } /** * Set the address value. * * @param value unsigned 32-bit integer value to set the address to. */ void set_addr(uint32_t value) { _addr = value; } /** * Constant for address family */ enum { AF = AF_INET }; /** * Constant for IP protocol version */ enum { IPV = 4 }; /** * Get the address family. * * @return the address family of this address. */ static int af() { return AF; } /** * Get the IP protocol version. * * @return the IP protocol version of this address. */ static uint32_t ip_version() { return IPV; } /** * Get the human-readable string with the IP protocol version. * * @return the human-readable string with the IP protocol version of * this address. */ static const string& ip_version_str(); /** * Extract bits from an address. * * @param lsb starting bit position (from the right) to extract. * @param len number of bits to extract. The maximum value is 32. * @return the first @ref len bits starting from the rightmost * position @ref lsb. The returned bits are in host order. */ uint32_t bits(uint32_t lsb, uint32_t len) const; /** * Count the number of bits that are set in this address. * * @return the number of bits that are set in this address. */ uint32_t bit_count() const; /** * Count the number of leading zeroes in this address. * * @return the number of leading zeroes in this address. */ uint32_t leading_zero_count() const; /** * Pre-defined IPv4 address constants. */ static const IPv4& ZERO(int af = AF_INET); static const IPv4& ANY(int af = AF_INET); static const IPv4& ALL_ONES(int af = AF_INET); static const IPv4& LOOPBACK(int af = AF_INET); static const IPv4& MULTICAST_BASE(int af = AF_INET); static const IPv4& MULTICAST_ALL_SYSTEMS(int af = AF_INET); static const IPv4& MULTICAST_ALL_ROUTERS(int af = AF_INET); static const IPv4& DVMRP_ROUTERS(int af = AF_INET); static const IPv4& OSPFIGP_ROUTERS(int af = AF_INET); static const IPv4& OSPFIGP_DESIGNATED_ROUTERS(int af = AF_INET); static const IPv4& RIP2_ROUTERS(int af = AF_INET); static const IPv4& PIM_ROUTERS(int af = AF_INET); static const IPv4& SSM_ROUTERS(int af = AF_INET); static const IPv4& CLASS_A_BASE(int af = AF_INET); static const IPv4& CLASS_B_BASE(int af = AF_INET); static const IPv4& CLASS_C_BASE(int af = AF_INET); static const IPv4& EXPERIMENTAL_BASE(int af = AF_INET); /** * Number of bits in address as a constant. */ static const uint32_t ADDR_BITLEN = 32; /** * Number of bytes in address as a constant. */ static const uint32_t ADDR_BYTELEN = ADDR_BITLEN / 8; private: uint32_t _addr; // The address value (in network-order) }; inline uint32_t IPv4::bits(uint32_t lsb, uint32_t len) const { uint32_t mask = ~(0xffffffffU << len); if (len >= 32) mask = 0xffffffffU; // XXX: shifting with >= 32 bits is undefined return (ntohl(_addr) >> lsb) & mask; } inline uint32_t IPv4::bit_count() const { // XXX: no need for ntohl() return (xorp_bit_count_uint32(_addr)); } inline uint32_t IPv4::leading_zero_count() const { return (xorp_leading_zero_count_uint32(ntohl(_addr))); } struct IPv4Constants { static const IPv4 zero, any, all_ones, loopback, multicast_base, multicast_all_systems, multicast_all_routers, dvmrp_routers, ospfigp_routers, ospfigp_designated_routers, rip2_routers, pim_routers, ssm_routers, class_a_base, class_b_base, class_c_base, experimental_base; }; inline const IPv4& IPv4::ZERO(int) { return IPv4Constants::zero; } inline const IPv4& IPv4::ANY(int) { return IPv4Constants::any; } inline const IPv4& IPv4::ALL_ONES(int) { return IPv4Constants::all_ones; } inline const IPv4& IPv4::LOOPBACK(int) { return IPv4Constants::loopback; } inline const IPv4& IPv4::MULTICAST_BASE(int) { return IPv4Constants::multicast_base; } inline const IPv4& IPv4::MULTICAST_ALL_SYSTEMS(int) { return IPv4Constants::multicast_all_systems; } inline const IPv4& IPv4::MULTICAST_ALL_ROUTERS(int) { return IPv4Constants::multicast_all_routers; } inline const IPv4& IPv4::DVMRP_ROUTERS(int) { return IPv4Constants::dvmrp_routers; } inline const IPv4& IPv4::OSPFIGP_ROUTERS(int) { return IPv4Constants::ospfigp_routers; } inline const IPv4& IPv4::OSPFIGP_DESIGNATED_ROUTERS(int) { return IPv4Constants::ospfigp_designated_routers; } inline const IPv4& IPv4::RIP2_ROUTERS(int) { return IPv4Constants::rip2_routers; } inline const IPv4& IPv4::PIM_ROUTERS(int) { return IPv4Constants::pim_routers; } inline const IPv4& IPv4::SSM_ROUTERS(int) { return IPv4Constants::ssm_routers; } inline const IPv4& IPv4::CLASS_A_BASE(int) { return IPv4Constants::class_a_base; } inline const IPv4& IPv4::CLASS_B_BASE(int) { return IPv4Constants::class_b_base; } inline const IPv4& IPv4::CLASS_C_BASE(int) { return IPv4Constants::class_c_base; } inline const IPv4& IPv4::EXPERIMENTAL_BASE(int) { return IPv4Constants::experimental_base; } #endif // __LIBXORP_IPV4_HH__ xorp/libxorp/ref_ptr.hh0000664000076400007640000003252411540224227015326 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/libxorp/ref_ptr.hh,v 1.26 2008/10/02 21:57:32 bms Exp $ #ifndef __LIBXORP_REF_PTR_HH__ #define __LIBXORP_REF_PTR_HH__ #include "libxorp/xorp.h" /** * @short class for maintaining the storage of counters used by ref_ptr. * * The ref_counter_pool is a singleton class that maintains the counters * for all ref_ptr objects. The counters are maintained in a vector. This * class is used by ref_ptr and not intended any other purpose. */ class ref_counter_pool { private: vector _counters; int32_t _free_index; int32_t _balance; static const int32_t LAST_FREE = -1; static ref_counter_pool _the_instance; /** * Expand counter storage. */ void grow(); public: /** * Create a new counter. * @return index associated with counter. */ int32_t new_counter(); /** * Increment the count associated with counter by 1. * @param index the counter to increment. */ int32_t incr_counter(int32_t index); /** * Decrement the count associated with counter by 1. * @param index the counter to decrement. */ int32_t decr_counter(int32_t index); /** * Get the count associated with counter. * @param index of the counter to query. * @return the counter value. */ int32_t count(int32_t index); /** * Recycle counter. Places counter on free-list. * @param index of the counter to recycle. */ void recycle(int32_t index); /** * Dumps counter info to stdout. Debugging function. */ void dump(); /** * Sanity check internal data structure. Debugging function. */ void check(); /** * Check index is on free list. */ bool on_free_list(int32_t index); /** * Return number of valid ref pointer entries in pool. */ int32_t balance() const { return _balance; } /** * @return singleton ref_counter_pool. */ static ref_counter_pool& instance(); ref_counter_pool(); }; /** * @short Reference Counted Pointer Class. * * The ref_ptr class is a strong reference class. It maintains a count of * how many references to an object exist and releases the memory associated * with the object when the reference count reaches zero. The reference * pointer can be dereferenced like an ordinary pointer to call methods * on the reference counted object. * * At the time of writing the only supported memory management is * through the new and delete operators. At a future date, this class * should support the STL allocator classes or an equivalent to * provide greater flexibility. */ template class ref_ptr { public: /** * Construct a reference pointer for object. * * @param p pointer to object to be reference counted. p must be * allocated using operator new as it will be destructed using delete * when the reference count reaches zero. */ ref_ptr(_Tp* __p = 0) : _M_ptr(__p), _M_index(0) { if (_M_ptr) _M_index = ref_counter_pool::instance().new_counter(); } /** * Copy Constructor * * Constructs a reference pointer for object. Raises reference count * associated with object by 1. */ ref_ptr(const ref_ptr& __r) : _M_ptr(0), _M_index(-1) { ref(&__r); } /** * Assignment Operator * * Assigns reference pointer to new object. */ ref_ptr& operator=(const ref_ptr& __r) { if (&__r != this) { unref(); ref(&__r); } return *this; } /** * Destruct reference pointer instance and lower reference count on * object being tracked. The object being tracked will be deleted if * the reference count falls to zero because of the destruction of the * reference pointer. */ ~ref_ptr() { unref(); } /** * Dereference reference counted object. * @return reference to object. */ _Tp& operator*() const { return *_M_ptr; } /** * Dereference pointer to reference counted object. * @return pointer to object. */ _Tp* operator->() const { return _M_ptr; } /** * Dereference pointer to reference counted object. * @return pointer to object. */ _Tp* get() const { return _M_ptr; } #ifdef XORP_USE_USTL // Compare pointed-to items. bool operator==(const ref_ptr& rp) const { if (_M_ptr == rp._M_ptr) return true; if (_M_ptr && rp._M_ptr) return (*_M_ptr == *rp._M_ptr); return false; } // Compare pointed-to items. bool operator< (const ref_ptr& b) { if (_M_ptr && b._M_ptr) return (*_M_ptr < *b._M_ptr); if (_M_ptr == b._M_ptr) return false; if (b._M_ptr) return true; } #else /** * Equality Operator * @return true if reference pointers refer to same object. */ bool operator==(const ref_ptr& rp) const { return _M_ptr == rp._M_ptr; } #endif /** * Check if reference pointer refers to an object or whether it has * been assigned a null object. * @return true if reference pointer refers to a null object. */ bool is_empty() const { return _M_ptr == 0; } /** * @return true if reference pointer represents only reference to object. */ bool is_only() const { return ref_counter_pool::instance().count(_M_index) == 1; } /** * @param n minimum count. * @return true if there are at least n references to object. */ bool at_least(int32_t n) const { return ref_counter_pool::instance().count(_M_index) >= n; } /** * Release reference on object. The reference pointers underlying * object is set to null, and the former object is destructed if * necessary. */ void release() const { unref(); } /* mimic functionality of boost weak_ptr, same as release() */ void reset() const { unref(); } ref_ptr(_Tp* data, int32_t index) : _M_ptr(data), _M_index(index) { ref_counter_pool::instance().incr_counter(_M_index); } protected: /** * Add reference. */ void ref(const ref_ptr* __r) const { _M_ptr = __r->_M_ptr; _M_index = __r->_M_index; if (_M_ptr) { ref_counter_pool::instance().incr_counter(_M_index); } } /** * Remove reference. */ void unref() const { if (_M_ptr && ref_counter_pool::instance().decr_counter(_M_index) == 0) { delete _M_ptr; } _M_ptr = 0; } mutable _Tp* _M_ptr; mutable int32_t _M_index; // index in ref_counter_pool }; #if 0 template ref_ptr::ref_ptr(const ref_ptr<_Tp>& __r) : _M_ptr(0), _M_index(_r->_M_index) { ref_counter_pool::instance().incr_counter(_M_index); } #endif /** * @short class for maintaining the storage of counters used by cref_ptr. * * The cref_counter_pool is a singleton class that maintains the counters * for all cref_ptr objects. The counters are maintained in a vector. This * class is used by cref_ptr and not intended any other purpose. */ class cref_counter_pool { private: struct pool_item { int32_t count; void* data; }; vector _counters; int32_t _free_index; static const int32_t LAST_FREE = -1; static cref_counter_pool _the_instance; /** * Expand counter storage. */ void grow(); public: /** * Create a new counter. * @return index associated with counter. */ int32_t new_counter(void *data); /** * Increment the count associated with counter by 1. * @param index the counter to increment. */ int32_t incr_counter(int32_t index); /** * Decrement the count associated with counter by 1. * @param index the counter to decrement. */ int32_t decr_counter(int32_t index); /** * Get the count associated with counter. * @param index of the counter to query. * @return the counter value. */ int32_t count(int32_t index); void* data(int32_t index); /** * Recycle counter. Places counter on free-list. * @param index of the counter to recycle. */ void recycle(int32_t index); /** * Dumps counter info to stdout. Debugging function. */ void dump(); /** * Sanity check internal data structure. Debugging function. */ void check(); /** * @return singleton cref_counter_pool. */ static cref_counter_pool& instance(); cref_counter_pool(); }; /** * @short Compact Reference Counted Pointer Class. * * The cref_ptr class is a strong reference class. It maintains a count of * how many references to an object exist and releases the memory associated * with the object when the reference count reaches zero. The reference * pointer can be dereferenced like an ordinary pointer to call methods * on the reference counted object. * * In contrast to the ref_ptr class, accessing the pointer requires * two levels of indirection as the pointer is stored in the same * table as the counter. This is a performance hit, but means each * cref_ptr only consumes 4-bytes. * * At the time of writing the only supported memory management is * through the new and delete operators. At a future date, this class * should support the STL allocator classes or an equivalent to * provide greater flexibility. */ template class cref_ptr { public: /** * Construct a compact reference pointer for object. * * @param p pointer to object to be reference counted. p must be * allocated using operator new as it will be destructed using delete * when the reference count reaches zero. */ cref_ptr(_Tp* __p = 0) : _M_index(cref_counter_pool::instance().new_counter(__p)) {} /** * Copy Constructor * * Constructs a reference pointer for object. Raises reference count * associated with object by 1. */ cref_ptr(const cref_ptr& __r) : _M_index(-1) { ref(&__r); } /** * Assignment Operator * * Assigns reference pointer to new object. */ cref_ptr& operator=(const cref_ptr& __r) { if (&__r != this) { unref(); ref(&__r); } return *this; } /** * Destruct reference pointer instance and lower reference count on * object being tracked. The object being tracked will be deleted if * the reference count falls to zero because of the destruction of the * reference pointer. */ ~cref_ptr() { unref(); } /** * Dereference pointer to reference counted object. * @return pointer to object. */ _Tp* get() const { return reinterpret_cast<_Tp*> (cref_counter_pool::instance().data(_M_index)); } /** * Dereference reference counted object. * @return reference to object. */ _Tp& operator*() const { return *(get()); } /** * Dereference pointer to reference counted object. * @return pointer to object. */ _Tp* operator->() const { return get(); } /** * Equality Operator * @return true if reference pointers refer to same object. */ bool operator==(const cref_ptr& rp) const { return get() == rp.get(); } /** * Check if reference pointer refers to an object or whether it has * been assigned a null object. * @return true if reference pointer refers to a null object. */ bool is_empty() const { return _M_index < 0 || 0 == get(); } /** * @return true if reference pointer represents only reference to object. */ bool is_only() const { return cref_counter_pool::instance().count(_M_index) == 1; } /** * @param n minimum count. * @return true if there are at least n references to object. */ bool at_least(int32_t n) const { return cref_counter_pool::instance().count(_M_index) >= n; } /** * Release reference on object. The reference pointers underlying * object is set to null, and the former object is destructed if * necessary. */ void release() const { unref(); } private: /** * Add reference. */ void ref(const cref_ptr* __r) const { _M_index = __r->_M_index; cref_counter_pool::instance().incr_counter(_M_index); } /** * Remove reference. */ void unref() const { if (_M_index >= 0 && cref_counter_pool::instance().decr_counter(_M_index) == 0) { delete get(); _M_index = -1; } } mutable int32_t _M_index; // index in cref_counter_pool }; #endif // __LIBXORP_REF_PTR_HH__ xorp/libxorp/buffer.hh0000664000076400007640000000637111540224227015137 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __LIBXORP_BUFFER_HH__ #define __LIBXORP_BUFFER_HH__ #include #include "xorp.h" #include "exceptions.hh" /** * @short A class for storing buffered data. * * This class can be used to conveniently store data. * Note: currently it has limited functionalities; more will be added * in the future. */ class Buffer { public: /** * Constructor of a buffer of specified size. * * @param init_max_size the maximum amount of data that can be stored * in the buffer. */ explicit Buffer(size_t init_max_size) : _max_size(init_max_size) { _data = new uint8_t[_max_size]; reset(); } /** * Destructor */ ~Buffer() { delete[] _data; } /** * Reset/remove the data in the buffer. */ void reset() { _cur_size = 0; memset(_data, 0, _max_size); } /** * @return the maximum amount of data that can be stored in the buffer. */ size_t max_size() const { return (_max_size); } /** * @return the amount of data in the buffer. */ size_t data_size() const { return (_cur_size); } /** * Get the data value of the octet at the specified offset. * * @param offset the data offset from the beginning of the buffer. * @return the data value at @ref offset. */ uint8_t data(size_t offset) const throw (InvalidBufferOffset) { if (offset >= _max_size) xorp_throw(InvalidBufferOffset, "invalid get data() offset"); return (_data[offset]); } /** * @return a pointer to the data in the buffer. */ uint8_t *data() { return (_data); } /** * Add a data octet to the buffer. * * @param value the value of the data octet to add to the buffer. * @return @ref XORP_OK on success, otherwise @ref XORP_ERROR. */ int add_data(uint8_t value) { if (is_full()) return (XORP_ERROR); _data[_cur_size++] = value; return (XORP_OK); } /** * Test if the buffer is full. * * @return true if the buffer is full, otherwise false. */ bool is_full() const { return (_cur_size >= _max_size); } private: size_t _max_size; // The maximum amount of data to store size_t _cur_size; // The current amount of stored data uint8_t *_data; // Pointer to the buffer with the data }; #endif // __LIBXORP_BUFFER_HH__ xorp/libxorp/random.c0000664000076400007640000004673011540224227014774 0ustar greearbgreearb/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- */ /* vim:set sts=4 ts=8: */ /* * Copyright (c) 2001-2011 XORP, Inc and Others * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License, Version * 2.1, June 1999 as published by the Free Software Foundation. * Redistribution and/or modification of this program under the terms of * any other version of the GNU Lesser General Public License is not * permitted. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, * see the GNU Lesser General Public License, Version 2.1, a copy of * which can be found in the XORP LICENSE.lgpl file. * * XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; * http://xorp.net */ #include "libxorp/xorp.h" #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_SYS_UIO_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_FCNTL_H #include #endif /* * Local implementation of random(3). * * It is always used instead of the system random(3) implementation so it * will be easier to obtain repeatable results across different systems * (when we have support for simulations). */ /* * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifdef HAVE_SYS_TIME_H #include /* for srandomdev() */ #endif #ifdef HAVE_FCNTL_H #include /* for srandomdev() */ #endif #ifdef HAVE_STDINT_H #include #endif #include #include #ifdef HAVE_UNISTD_H #include /* for srandomdev() */ #endif #include "random.h" /* * random.c: * * An improved random number generation package. In addition to the standard * rand()/srand() like interface, this package also has a special state info * interface. The initstate() routine is called with a seed, an array of * bytes, and a count of how many bytes are being passed in; this array is * then initialized to contain information for random number generation with * that much state information. Good sizes for the amount of state * information are 32, 64, 128, and 256 bytes. The state can be switched by * calling the setstate() routine with the same array as was initiallized * with initstate(). By default, the package runs with 128 bytes of state * information and generates far better random numbers than a linear * congruential generator. If the amount of state information is less than * 32 bytes, a simple linear congruential R.N.G. is used. * * Internally, the state information is treated as an array of uint32_t's; the * zeroeth element of the array is the type of R.N.G. being used (small * integer); the remainder of the array is the state information for the * R.N.G. Thus, 32 bytes of state information will give 7 ints worth of * state information, which will allow a degree seven polynomial. (Note: * the zeroeth word of state information also has some other information * stored in it -- see setstate() for details). * * The random number generation technique is a linear feedback shift register * approach, employing trinomials (since there are fewer terms to sum up that * way). In this approach, the least significant bit of all the numbers in * the state table will act as a linear feedback shift register, and will * have period 2^deg - 1 (where deg is the degree of the polynomial being * used, assuming that the polynomial is irreducible and primitive). The * higher order bits will have longer periods, since their values are also * influenced by pseudo-random carries out of the lower bits. The total * period of the generator is approximately deg*(2**deg - 1); thus doubling * the amount of state information has a vast influence on the period of the * generator. Note: the deg*(2**deg - 1) is an approximation only good for * large deg, when the period of the shift is the dominant factor. * With deg equal to seven, the period is actually much longer than the * 7*(2**7 - 1) predicted by this formula. * * Modified 28 December 1994 by Jacob S. Rosenberg. * The following changes have been made: * All references to the type u_int have been changed to unsigned long. * All references to type int have been changed to type long. Other * cleanups have been made as well. A warning for both initstate and * setstate has been inserted to the effect that on Sparc platforms * the 'arg_state' variable must be forced to begin on word boundaries. * This can be easily done by casting a long integer array to char *. * The overall logic has been left STRICTLY alone. This software was * tested on both a VAX and Sun SpacsStation with exactly the same * results. The new version and the original give IDENTICAL results. * The new version is somewhat faster than the original. As the * documentation says: "By default, the package runs with 128 bytes of * state information and generates far better random numbers than a linear * congruential generator. If the amount of state information is less than * 32 bytes, a simple linear congruential R.N.G. is used." For a buffer of * 128 bytes, this new version runs about 19 percent faster and for a 16 * byte buffer it is about 5 percent faster. */ /* * For each of the currently supported random number generators, we have a * break value on the amount of state information (you need at least this * many bytes of state info to support this random number generator), a degree * for the polynomial (actually a trinomial) that the R.N.G. is based on, and * the separation between the two lower order coefficients of the trinomial. */ #define TYPE_0 0 /* linear congruential */ #define BREAK_0 8 #define DEG_0 0 #define SEP_0 0 #define TYPE_1 1 /* x**7 + x**3 + 1 */ #define BREAK_1 32 #define DEG_1 7 #define SEP_1 3 #define TYPE_2 2 /* x**15 + x + 1 */ #define BREAK_2 64 #define DEG_2 15 #define SEP_2 1 #define TYPE_3 3 /* x**31 + x**3 + 1 */ #define BREAK_3 128 #define DEG_3 31 #define SEP_3 3 #define TYPE_4 4 /* x**63 + x + 1 */ #define BREAK_4 256 #define DEG_4 63 #define SEP_4 1 /* * Array versions of the above information to make code run faster -- * relies on fact that TYPE_i == i. */ #define MAX_TYPES 5 /* max number of types above */ #ifdef USE_WEAK_SEEDING #define NSHUFF 0 #else /* !USE_WEAK_SEEDING */ #define NSHUFF 50 /* to drop some "seed -> 1st value" linearity */ #endif /* !USE_WEAK_SEEDING */ static const int degrees[MAX_TYPES] = { DEG_0, DEG_1, DEG_2, DEG_3, DEG_4 }; static const int seps [MAX_TYPES] = { SEP_0, SEP_1, SEP_2, SEP_3, SEP_4 }; /* * Initially, everything is set up as if from: * * initstate(1, randtbl, 128); * * Note that this initialization takes advantage of the fact that srandom() * advances the front and rear pointers 10*rand_deg times, and hence the * rear pointer which starts at 0 will also end up at zero; thus the zeroeth * element of the state information, which contains info about the current * position of the rear pointer is just * * MAX_TYPES * (rptr - state) + TYPE_3 == TYPE_3. */ static uint32_t randtbl[DEG_3 + 1] = { TYPE_3, #ifdef USE_WEAK_SEEDING /* Historic implementation compatibility */ /* The random sequences do not vary much with the seed */ 0x9a319039, 0x32d9c024, 0x9b663182, 0x5da1f342, 0xde3b81e0, 0xdf0a6fb5, 0xf103bc02, 0x48f340fb, 0x7449e56b, 0xbeb1dbb0, 0xab5c5918, 0x946554fd, 0x8c2e680f, 0xeb3d799f, 0xb11ee0b7, 0x2d436b86, 0xda672e2a, 0x1588ca88, 0xe369735d, 0x904f35f7, 0xd7158fd6, 0x6fa6f051, 0x616e6b96, 0xac94efdc, 0x36413f93, 0xc622c298, 0xf5a42ab8, 0x8a88d77b, 0xf5ad9d0e, 0x8999220b, 0x27fb47b9, #else /* !USE_WEAK_SEEDING */ 0x991539b1, 0x16a5bce3, 0x6774a4cd, 0x3e01511e, 0x4e508aaa, 0x61048c05, 0xf5500617, 0x846b7115, 0x6a19892c, 0x896a97af, 0xdb48f936, 0x14898454, 0x37ffd106, 0xb58bff9c, 0x59e17104, 0xcf918a49, 0x09378c83, 0x52c7a471, 0x8d293ea9, 0x1f4fc301, 0xc3db71be, 0x39b44e1c, 0xf8a44ef9, 0x4c8b80b1, 0x19edc328, 0x87bf4bdd, 0xc9b240e5, 0xe9ee4b1b, 0x4382aee7, 0x535b6b41, 0xf3bec5da #endif /* !USE_WEAK_SEEDING */ }; /* * fptr and rptr are two pointers into the state info, a front and a rear * pointer. These two pointers are always rand_sep places aparts, as they * cycle cyclically through the state information. (Yes, this does mean we * could get away with just one pointer, but the code for random() is more * efficient this way). The pointers are left positioned as they would be * from the call * * initstate(1, randtbl, 128); * * (The position of the rear pointer, rptr, is really 0 (as explained above * in the initialization of randtbl) because the state table pointer is set * to point to randtbl[1] (as explained below). */ static uint32_t *fptr = &randtbl[SEP_3 + 1]; static uint32_t *rptr = &randtbl[1]; /* * The following things are the pointer to the state information table, the * type of the current generator, the degree of the current polynomial being * used, and the separation between the two pointers. Note that for efficiency * of random(), we remember the first location of the state information, not * the zeroeth. Hence it is valid to access state[-1], which is used to * store the type of the R.N.G. Also, we remember the last location, since * this is more efficient than indexing every time to find the address of * the last element to see if the front and rear pointers have wrapped. */ static uint32_t *state = &randtbl[1]; static int rand_type = TYPE_3; static int rand_deg = DEG_3; static int rand_sep = SEP_3; static uint32_t *end_ptr = &randtbl[DEG_3 + 1]; static inline uint32_t good_rand(int32_t); static inline uint32_t good_rand (x) int32_t x; { #ifdef USE_WEAK_SEEDING /* * Historic implementation compatibility. * The random sequences do not vary much with the seed, * even with overflowing. */ return (1103515245 * x + 12345); #else /* !USE_WEAK_SEEDING */ /* * Compute x = (7^5 * x) mod (2^31 - 1) * wihout overflowing 31 bits: * (2^31 - 1) = 127773 * (7^5) + 2836 * From "Random number generators: good ones are hard to find", * Park and Miller, Communications of the ACM, vol. 31, no. 10, * October 1988, p. 1195. */ int32_t hi, lo; /* Can't be initialized with 0, so use another value. */ if (x == 0) x = 123459876; hi = x / 127773; lo = x % 127773; x = 16807 * lo - 2836 * hi; if (x < 0) x += 0x7fffffff; return (x); #endif /* !USE_WEAK_SEEDING */ } /* * srandom: * * Initialize the random number generator based on the given seed. If the * type is the trivial no-state-information type, just remember the seed. * Otherwise, initializes state[] based on the given "seed" via a linear * congruential generator. Then, the pointers are set to known locations * that are exactly rand_sep places apart. Lastly, it cycles the state * information a given number of times to get rid of any initial dependencies * introduced by the L.C.R.N.G. Note that the initialization of randtbl[] * for default usage relies on values produced by this routine. */ void xorp_srandom(x) unsigned long x; { int i, lim; state[0] = (uint32_t)x; if (rand_type == TYPE_0) lim = NSHUFF; else { for (i = 1; i < rand_deg; i++) state[i] = good_rand(state[i - 1]); fptr = &state[rand_sep]; rptr = &state[0]; lim = 10 * rand_deg; } for (i = 0; i < lim; i++) (void)xorp_random(); } /* * srandomdev: * * Many programs choose the seed value in a totally predictable manner. * This often causes problems. We seed the generator using the much more * secure random(4) interface. Note that this particular seeding * procedure can generate states which are impossible to reproduce by * calling srandom() with any value, since the succeeding terms in the * state buffer are no longer derived from the LC algorithm applied to * a fixed seed. */ void xorp_srandomdev() { int fd, done; size_t len; if (rand_type == TYPE_0) len = sizeof state[0]; else len = rand_deg * sizeof state[0]; done = 0; #ifdef HOST_OS_WINDOWS /* XXX: We need to use CAPI to get hardware randomness. */ UNUSED(fd); #else fd = open("/dev/random", O_RDONLY, 0); if (fd >= 0) { if (read(fd, (void *) state, len) == (ssize_t) len) done = 1; close(fd); } #endif if (!done) { #ifdef HOST_OS_WINDOWS /* * XXX: If we didn't get hardware randomness, fall back on * something else. */ #else struct timeval tv; unsigned long junk = 0; unsigned long junk_seed[2]; /* * XXX: We need "junk_seed" to set the "junk" value to avoid * compiler warning about using an uninitialized variable. */ memset(&junk_seed[0], 0, sizeof(junk_seed[0])); junk = junk_seed[1]; gettimeofday(&tv, NULL); xorp_srandom((getpid() << 16) ^ tv.tv_sec ^ tv.tv_usec ^ junk); #endif return; } if (rand_type != TYPE_0) { fptr = &state[rand_sep]; rptr = &state[0]; } } /* * initstate: * * Initialize the state information in the given array of n bytes for future * random number generation. Based on the number of bytes we are given, and * the break values for the different R.N.G.'s, we choose the best (largest) * one we can and set things up for it. srandom() is then called to * initialize the state information. * * Note that on return from srandom(), we set state[-1] to be the type * multiplexed with the current value of the rear pointer; this is so * successive calls to initstate() won't lose this information and will be * able to restart with setstate(). * * Note: the first thing we do is save the current state, if any, just like * setstate() so that it doesn't matter when initstate is called. * * Returns a pointer to the old state. * * Note: The Sparc platform requires that arg_state begin on an int * word boundary; otherwise a bus error will occur. Even so, lint will * complain about mis-alignment, but you should disregard these messages. */ char * xorp_initstate(seed, arg_state, n) unsigned long seed; /* seed for R.N.G. */ char *arg_state; /* pointer to state array */ long n; /* # bytes of state info */ { char *ostate = (char *)(&state[-1]); uint32_t *int_arg_state = (uint32_t *)(void *)arg_state; if (rand_type == TYPE_0) state[-1] = rand_type; else state[-1] = MAX_TYPES * (rptr - state) + rand_type; if (n < BREAK_0) { (void)fprintf(stderr, "random: not enough state (%ld bytes); ignored.\n", n); return (0); } if (n < BREAK_1) { rand_type = TYPE_0; rand_deg = DEG_0; rand_sep = SEP_0; } else if (n < BREAK_2) { rand_type = TYPE_1; rand_deg = DEG_1; rand_sep = SEP_1; } else if (n < BREAK_3) { rand_type = TYPE_2; rand_deg = DEG_2; rand_sep = SEP_2; } else if (n < BREAK_4) { rand_type = TYPE_3; rand_deg = DEG_3; rand_sep = SEP_3; } else { rand_type = TYPE_4; rand_deg = DEG_4; rand_sep = SEP_4; } state = int_arg_state + 1; /* first location */ end_ptr = &state[rand_deg]; /* must set end_ptr before srandom */ xorp_srandom(seed); if (rand_type == TYPE_0) int_arg_state[0] = rand_type; else int_arg_state[0] = MAX_TYPES * (rptr - state) + rand_type; return (ostate); } /* * setstate: * * Restore the state from the given state array. * * Note: it is important that we also remember the locations of the pointers * in the current state information, and restore the locations of the pointers * from the old state information. This is done by multiplexing the pointer * location into the zeroeth word of the state information. * * Note that due to the order in which things are done, it is OK to call * setstate() with the same state as the current state. * * Returns a pointer to the old state information. * * Note: The Sparc platform requires that arg_state begin on an int * word boundary; otherwise a bus error will occur. Even so, lint will * complain about mis-alignment, but you should disregard these messages. */ char * xorp_setstate(arg_state) char *arg_state; /* pointer to state array */ { uint32_t *new_state = (uint32_t *)(void *)arg_state; uint32_t type = new_state[0] % MAX_TYPES; uint32_t rear = new_state[0] / MAX_TYPES; char *ostate = (char *)(&state[-1]); if (rand_type == TYPE_0) state[-1] = rand_type; else state[-1] = MAX_TYPES * (rptr - state) + rand_type; switch(type) { case TYPE_0: case TYPE_1: case TYPE_2: case TYPE_3: case TYPE_4: rand_type = type; rand_deg = degrees[type]; rand_sep = seps[type]; break; default: (void)fprintf(stderr, "random: state info corrupted; not changed.\n"); } state = new_state + 1; if (rand_type != TYPE_0) { rptr = &state[rear]; fptr = &state[(rear + rand_sep) % rand_deg]; } end_ptr = &state[rand_deg]; /* set end_ptr too */ return (ostate); } /* * random: * * If we are using the trivial TYPE_0 R.N.G., just do the old linear * congruential bit. Otherwise, we do our fancy trinomial stuff, which is * the same in all the other cases due to all the global variables that have * been set up. The basic operation is to add the number at the rear pointer * into the one at the front pointer. Then both pointers are advanced to * the next location cyclically in the table. The value returned is the sum * generated, reduced to 31 bits by throwing away the "least random" low bit. * * Note: the code takes advantage of the fact that both the front and * rear pointers can't wrap on the same call by not testing the rear * pointer if the front one has wrapped. * * Returns a 31-bit random number. */ long xorp_random() { uint32_t i; uint32_t *f, *r; if (rand_type == TYPE_0) { i = state[0]; state[0] = i = (good_rand(i)) & 0x7fffffff; } else { /* * Use local variables rather than static variables for speed. */ f = fptr; r = rptr; *f += *r; i = (*f >> 1) & 0x7fffffff; /* chucking least random bit */ if (++f >= end_ptr) { f = state; ++r; } else if (++r >= end_ptr) { r = state; } fptr = f; rptr = r; } return ((long)i); } xorp/libxorp/heap.cc0000664000076400007640000002277311540224227014575 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // Portions of this code originally derived from: // FreeBSD dummynet code, (C) 2001 Luigi Rizzo. #include "libxorp_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/eventloop.hh" #include "libxorp/xlog.h" #include "heap.hh" #include #ifdef _TEST_HEAP_CODE #define DBG(x) x #else #define DBG(x) #endif /* * A heap entry is made of a key and a pointer to the actual * object stored in the heap. * The heap is an array of heap_entry entries, dynamically allocated. * Current size is "size", with "elements" actually in use. * The heap normally supports only ordered insert and extract from the top. * If we want to extract an object from the middle of the heap, we * have to know where the object itself is located in the heap (or we * need to scan the whole array). To this purpose, an object has a * field (int) which contains the index of the object itself into the * heap. When the object is moved, the field must also be updated. * The offset of the index in the object is stored in the 'offset' * field in the heap descriptor. The assumption is that this offset * is non-zero if we want to support extract from the middle. */ /* * Heap management functions. * * In the heap, first node is element 0. Children of i are 2i+1 and 2i+2. * Some macros help finding parent/children so we can optimize them. */ #define HEAP_FATHER(x) ( ( (x) - 1 ) / 2 ) #define HEAP_LEFT(x) ( 2*(x) + 1 ) #define HEAP_SWAP(a, b, buffer) { buffer = a ; a = b ; b = buffer ; } #define HEAP_INCREMENT 15 int Heap::resize(int new_size) { struct heap_entry *p; if (_size >= new_size) { XLOG_ERROR("Bogus call inside heap::resize: have %d want %d", _size, new_size); return 0; } new_size = (new_size + HEAP_INCREMENT ) & ~HEAP_INCREMENT ; p = new struct heap_entry[new_size]; if (p == NULL) { XLOG_ERROR("Heap resize %d failed", new_size); return 1; // Error } if (_size > 0) { memcpy(p, _p, _size * sizeof(*p)); delete[] _p; } _p = p; _size = new_size; return 0; } /* * Insert an element in the heap. * Normally (p != NULL) we insert p in a new slot and reorder the heap. * If p == NULL, then the element is already in place, and 'son' is the * position where to start the bubble-up. * * If offset > 0 the position (index, int) of the element in the heap is * also stored in the element itself at the given offset in bytes. */ #define SET_OFFSET(node) \ do { \ if (_intrude) \ _p[node].object->_pos_in_heap = node ; \ } while (0) /* * RESET_OFFSET is used for sanity checks. It sets offset to an invalid value. */ #define RESET_OFFSET(node) \ do { \ if (_intrude) \ _p[node].object->_pos_in_heap = NOT_IN_HEAP ; \ } while (0) // inner implementation of push -- if p == NULL, the element is already // there, so start bubbling up from 'son'. Otherwise, push in new element p // with key k void Heap::push(Heap_Key k, HeapBase *p, int son) { if (p != 0) { // Insert new element at the end, possibly resize debug_msg("insert key %u.%06u ptr %p\n", XORP_UINT_CAST(k.sec()), XORP_UINT_CAST(k.usec()), p); son = _elements; if (son == _size) { // need resize... if (resize(_elements + 1)) return; // Failure... } _p[son].object = p ; _p[son].key = k ; _elements++ ; } while (son > 0) { // Bubble up int father = HEAP_FATHER(son); struct heap_entry tmp; if (_p[father].key <= _p[son].key) break; // Found right position // Son smaller than father, swap and repeat HEAP_SWAP(_p[son], _p[father], tmp); SET_OFFSET(son); son = father; } SET_OFFSET(son); } // // Remove top element from heap, or obj if obj != NULL // void Heap::pop_obj(HeapBase *obj) { int child, father, max_entry = _elements - 1 ; if (max_entry < 0) { XLOG_ERROR("Extract from empty heap 0x%p", this); return; } father = 0 ; // Default: move up smallest child if (obj != NULL) { // Extract specific element, index is at offset if (!_intrude) { XLOG_FATAL("*** heap_extract from middle " "not supported on this heap!!!"); } father = obj->_pos_in_heap; if (father < 0 || father >= _elements) { XLOG_FATAL("-- heap_extract, father %d out of bound 0..%d", father, _elements); } if (_p[father].object != obj) { XLOG_FATAL("-- bad obj 0x%p instead of 0x%p at %d", _p[father].object, obj, father); } debug_msg("-- delete key %u\n", XORP_UINT_CAST(_p[father].key.sec())); } RESET_OFFSET(father); child = HEAP_LEFT(father); // left child while (child <= max_entry) { // valid entry if (child != max_entry && _p[child+1].key < _p[child].key ) child = child + 1; // take right child, otherwise left _p[father] = _p[child]; SET_OFFSET(father); father = child; child = HEAP_LEFT(child); // left child for next loop } _elements--; if (father != max_entry) { // // Fill hole with last entry and bubble up, reusing the insert code // _p[father] = _p[max_entry]; push(father); // this one cannot fail } DBG(verify()); } #if 1 /* * change object position and update references * XXX this one is never used! */ void Heap::move(Heap_Key new_key, HeapBase *object) { int temp; int i; int max_entry = _elements - 1; struct heap_entry buf; if (!_intrude) XLOG_FATAL("cannot move items on this heap"); i = object->_pos_in_heap; if (new_key < _p[i].key) { // must move up _p[i].key = new_key; for (; i > 0 && new_key < _p[(temp = HEAP_FATHER(i))].key; i = temp) { // bubble up HEAP_SWAP(_p[i], _p[temp], buf); SET_OFFSET(i); } } else { // must move down _p[i].key = new_key; while ( (temp = HEAP_LEFT(i)) <= max_entry) { // found left child if ((temp != max_entry) && _p[temp+1].key < _p[temp].key) temp++; // select child with min key if (_p[temp].key < new_key) { // go down HEAP_SWAP(_p[i], _p[temp], buf); SET_OFFSET(i); } else { break; } i = temp; } } SET_OFFSET(i); } #endif // heap_move, unused // heapify() will reorganize data inside an array to maintain the // heap property. It is needed when we delete a bunch of entries. void Heap::heapify() { int i; for (i = 0; i < _elements; i++) push(i); } Heap::Heap() : _size(0), _elements(0), _intrude(false), _p(NULL) { //printf("created heap (dflt), this: %p\n", this); } Heap::Heap(bool intrude) : _size(0), _elements(0), _intrude(intrude), _p(NULL) { //printf("created heap, this: %p\n", this); } Heap::~Heap() { //printf("deleting heap, this: %p\n", this); if (_p != NULL) delete[] _p; } void Heap::verify() { int i; for (i = 1; i < _elements; i++) { if ( _p[i].key < _p[(i - 1) / 2 ].key) { XLOG_WARNING("+++ heap violated at %d", (i - 1) / 2); #ifdef _TEST_HEAP_CODE print_all((i - 1) / 2); #endif return; } } } #ifdef _TEST_HEAP_CODE void Heap::print() { fprintf(stderr, "-- heap at 0x%p\n", this); fprintf(stderr, "\tsize is %d, offset %d\n", _size, _offset); } void Heap::print_all(int base) { int depth = 1; int l = 1; // nodes to print on this level const char *blanks = ""; for (int i = base; i < _elements; i = i * 2 + 1) depth *= 2; depth = (depth /4); for (int i = base; i < _elements; i = i * 2 + 1) { for (int j = i; j < _elements && j < i + l; j++) fprintf(stderr, "%*s[%2d]%3ld%*s", (depth / 2) * 7, blanks, j, _p[j].key.sec(), depth * 7, blanks); fprintf(stderr, "\n"); l = l * 2; depth /= 2; } } #ifdef _TEST_HEAP_CODE_HARNESS struct foo { Heap_Key t; int index; int the_pos; }; int main(int argc, char *argv[]) { struct foo a[1000]; fprintf(stderr, "-- Hello World of heaps...\n"); Heap *h = new Heap(OFFSET_OF(a[0], the_pos)); h->print(); for (int i = 0; i < 100; i++) { a[i].t.tv_sec = 1000 - i; a[i].t.tv_usec = 1000 + i; a[i].index = i; h->push(a[i].t, &a[i]); } for (int i = 0; i < 100; i += 2) { h->print_all(0); h->pop_obj(&a[i]); } for (;;) { struct heap_entry *e = h->top(); if (e == 0) break; h->print_all(0); fprintf(stderr, "++ got %ld.%06ld 0x%p\n", e->key.sec(), e->key.usec(), e->object); h->pop(); } } #endif // _TEST_HEAP_CODE_HARNESS #endif // _TEST_HEAP_CODE xorp/libxorp/round_robin.hh0000664000076400007640000000454311421137511016202 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2006-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/libxorp/round_robin.hh,v 1.8 2008/10/02 21:57:33 bms Exp $ #ifndef __LIBXORP_ROUND_ROBIN_HH__ #define __LIBXORP_ROUND_ROBIN_HH__ class RoundRobin; /** * Objects stored in the RoundRobinQueue should inherit from this class. */ class RoundRobinObjBase { public: RoundRobinObjBase(); int weight() const { return _weight; } void set_weight(int v) { _weight = v; } RoundRobinObjBase* next() const { return _next; } RoundRobinObjBase* prev() const { return _prev; } void set_next(RoundRobinObjBase* next) { _next = next; } void set_prev(RoundRobinObjBase* prev) { _prev = prev; } bool scheduled() const; private: int _weight; // Links to build a circular list RoundRobinObjBase* _next; RoundRobinObjBase* _prev; }; /** * The Round-robin queue. */ class RoundRobinQueue { public: RoundRobinQueue(); void push(RoundRobinObjBase* obj, int weight); void pop_obj(RoundRobinObjBase* obj); void pop(); RoundRobinObjBase* get_next_entry(); /** * Get the number of elements in the heap. * * @return the number of elements in the heap. */ size_t size() const { return _elements; } private: void link_object(RoundRobinObjBase* obj, int weight); void unlink_object(RoundRobinObjBase* obj); RoundRobinObjBase* _next_to_run; int _run_count; // How many times we've run the current task in a row int _elements; }; #endif // __LIBXORP_ROUND_ROBIN_HH__ xorp/libxorp/xlog.c0000664000076400007640000006737211703345405014475 0ustar greearbgreearb/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- */ /* vim:set sts=4 ts=8: */ /* * Copyright (c) 2001-2012 XORP, Inc and Others * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License, Version * 2.1, June 1999 as published by the Free Software Foundation. * Redistribution and/or modification of this program under the terms of * any other version of the GNU Lesser General Public License is not * permitted. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, * see the GNU Lesser General Public License, Version 2.1, a copy of * which can be found in the XORP LICENSE.lgpl file. * * XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; * http://xorp.net */ /* * Message logging utility. */ #include "libxorp_module.h" #include "libxorp/xorp.h" #ifdef HAVE_FCNTL_H #include #endif #ifdef HAVE_SYSLOG_H #include #endif #include "xlog.h" /* * Exported variables */ /* * Local constants definitions */ #define MAX_XLOG_OUTPUTS 10 /* * Local structures, typedefs and macros */ #ifdef UNUSED #undef UNUSED #endif #define UNUSED(x) (x) = (x) /* * Local variables */ static int init_flag = 0; static int start_flag = 0; static pid_t pid = 0; static char *preamble_string = NULL; static char *process_name_string = NULL; static FILE *xlog_outputs_file[MAX_XLOG_OUTPUTS]; static xlog_output_func_t xlog_outputs_func[MAX_XLOG_OUTPUTS]; static void *xlog_outputs_obj[MAX_XLOG_OUTPUTS]; static size_t xlog_output_file_count = 0; static size_t xlog_output_func_count = 0; static FILE *fp_default = NULL; int xlog_level_enabled[XLOG_LEVEL_MAX]; static xlog_verbose_t xlog_verbose_level[XLOG_LEVEL_MAX]; /* * XXX: the log level names below has to be consistent with the XLOG_LEVEL_* * values. */ static const char *xlog_level_names[XLOG_LEVEL_MAX] = { "FATAL", "ERROR", "WARNING", "INFO", "TRACE", /* XXX: temp, to be removed; see Bugzilla entry 795 */ "RTRMGR_ONLY_NO_PREAMBLE", }; /* * Local functions prototypes */ static void xlog_record_va(xlog_level_t log_level, const char* module_name, const char *where, const char* format, va_list ap); static int xlog_write(FILE* fp, const char* fmt, ...); static int xlog_write_va(FILE* fp, const char* fmt, va_list ap); static int xlog_flush(FILE* fp); /* * **************************************************************************** * Control functions to init/exit/start/stop/etc the log utility. * **************************************************************************** */ /** * xlog_init: * @argv0 The path to the executable. * @preamble_message A string that will become part of the preamble string. * * Initialize the log utility. * As part of the initialization, the preamble string will be set to * <@module_name><@preamble_message> * * Return value: 0 on success, otherwise -1. **/ int xlog_init(const char *argv0, const char *preamble_message) { const char* process_name; xlog_level_t level; if (init_flag) return (-1); pid = getpid(); if (process_name_string != NULL) { free(process_name_string); process_name_string = NULL; } process_name = strrchr(argv0, '/'); if (process_name != NULL) process_name++; /* Skip the last '/' */ if (process_name == NULL) process_name = argv0; if (process_name != NULL) process_name_string = strdup(process_name); /* Set the preamble string */ xlog_set_preamble(preamble_message); /* Enable all log messages by default, and set default verbose level */ for (level = XLOG_LEVEL_MIN; level < XLOG_LEVEL_MAX; level++) { xlog_enable(level); xlog_verbose_level[level] = XLOG_VERBOSE_LOW; /* Default */ } xlog_verbose_level[XLOG_LEVEL_FATAL] = XLOG_VERBOSE_HIGH; /* XXX */ /* XXX: temp, to be removed; see Bugzilla entry 795 */ xlog_verbose_level[XLOG_LEVEL_RTRMGR_ONLY_NO_PREAMBLE] = XLOG_VERBOSE_RTRMGR_ONLY_NO_PREAMBLE; init_flag = 1; return (0); } /** * xlog_exit: * @void: * * Gracefully exit logging. * * Return value: 0 on success, othewise -1. **/ int xlog_exit(void) { int i; xlog_level_t level; if (! init_flag) return (-1); if (start_flag) xlog_stop(); /* Reset local variables */ init_flag = 0; pid = 0; if (process_name_string != NULL) { free (process_name_string); process_name_string = NULL; } if (preamble_string != NULL) { free (preamble_string); preamble_string = NULL; } for (i = 0; i < MAX_XLOG_OUTPUTS; i++) { xlog_outputs_file[i] = NULL; xlog_outputs_func[i] = NULL; xlog_outputs_obj[i] = NULL; } xlog_output_file_count = 0; xlog_output_func_count = 0; fp_default = 0; for (level = XLOG_LEVEL_MIN; level < XLOG_LEVEL_MAX; level++) { xlog_disable(level); xlog_verbose_level[level] = XLOG_VERBOSE_LOW; /* Default */ } xlog_verbose_level[XLOG_LEVEL_FATAL] = XLOG_VERBOSE_HIGH; /* XXX */ /* XXX: temp, to be removed; see Bugzilla entry 795 */ xlog_verbose_level[XLOG_LEVEL_RTRMGR_ONLY_NO_PREAMBLE] = XLOG_VERBOSE_RTRMGR_ONLY_NO_PREAMBLE; return (0); } /** * xlog_start: * @void: * * Start logging. * * Return value: 0 on success, otherwise -1. **/ int xlog_start(void) { if (! init_flag) return (-1); if (start_flag) return (-1); start_flag = 1; return (0); } /** * xlog_stop: * @void: * * Stop logging. * * Return value: 0 on success, otherwise -1. **/ int xlog_stop(void) { if (! start_flag) return (-1); start_flag = 0; return (0); } int xlog_is_running(void) { return start_flag; } /** * xlog_enable: * @log_level: The message type (e.g., %XLOG_LEVEL_WARNING) to enable * the logging for. * * Enable logging for messages of type &log_level_t. * By default, all message types are enabled. * * Return value: 0 on success, otherwise -1. **/ int xlog_enable(xlog_level_t log_level) { if (XLOG_LEVEL_MAX <= log_level) return (-1); xlog_level_enabled[log_level] = 1; return (0); } /** * xlog_disable: * @log_level: The message type (e.g., %XLOG_LEVEL_WARNING) to disable * the logging for. * * Enable logging for messages of type &log_level_t. * XXX: %XLOG_LEVEL_FATAL cannot be disabled. * * Return value: 0 on success, otherwise -1. **/ int xlog_disable(xlog_level_t log_level) { if (XLOG_LEVEL_MAX <= log_level) return (-1); /* XXX: XLOG_LEVEL_FATAL can never be disabled */ if (log_level == XLOG_LEVEL_FATAL) return (-1); xlog_level_enabled[log_level] = 0; return (0); } /** * xlog_set_preamble: * @text: The preamble string, or NULL if no preamble. * * Set the preamble string for the log entries. **/ void xlog_set_preamble(const char* text) { /* Free the memory for the old preamble */ if (preamble_string != NULL) { free(preamble_string); preamble_string = NULL; } /* Duplicate the new preamble string */ if (text != NULL) { preamble_string = strdup(text); } } /** * xlog_process_name: * * Get the process name as set by xlog_init. * Return value: pointer to process name on success, NULL otherwise. **/ const char* xlog_process_name() { return process_name_string; } /** * xlog_set_verbose: * @verbose_level: The level of verbosity #xlog_verbose_t * (higher is more verbose). * * Set the level of verbosity (#xlog_verbose_t) for the log entries. * Applies for all type of messages except for %XLOG_LEVEL_FATAL * which always is set to the most verbose level. **/ void xlog_set_verbose(xlog_verbose_t verbose_level) { int i; if (XLOG_VERBOSE_HIGH < verbose_level) verbose_level = XLOG_VERBOSE_HIGH; for (i = 0; i < XLOG_LEVEL_MAX; i++) { if (i == XLOG_LEVEL_FATAL) continue; /* XXX: XLOG_LEVEL_FATAL cannot be changed */ /* XXX: temp, to be removed; see Bugzilla entry 795 */ if (i == XLOG_LEVEL_RTRMGR_ONLY_NO_PREAMBLE) continue; xlog_verbose_level[i] = verbose_level; } } /** * xlog_level_set_verbose: * @log_level: The message type #xlog_level_t to set the verbosity of. * @verbose_level: The level of verbosity #xlog_verbose_t * (higher is more verbose). * * Set the level of verbosity (#xlog_verbose_t) for the log entries * of messages of type #xlog_level_t. * Note: %XLOG_LEVEL_FATAL verbosity cannot be changed, and is * always set to the most verbose level. **/ void xlog_level_set_verbose(xlog_level_t log_level, xlog_verbose_t verbose_level) { if (XLOG_LEVEL_MAX <= log_level) return; if (log_level == XLOG_LEVEL_FATAL) return; /* XXX: XLOG_LEVEL_FATAL cannot be changed */ /* XXX: temp, to be removed; see Bugzilla entry 795 */ if (log_level == XLOG_LEVEL_RTRMGR_ONLY_NO_PREAMBLE) return; if (XLOG_VERBOSE_HIGH < verbose_level) verbose_level = XLOG_VERBOSE_HIGH; xlog_verbose_level[log_level] = verbose_level; } void _xlog_with_level(int log_level, const char *module_name, int line, const char *file, const char *function, const char *fmt, ...) { va_list ap; static char where_buf[8000]; // we are single threaded, global buffer is good enough. snprintf(where_buf, sizeof(where_buf), "%s:%d %s", file, line, (function) ? function : "(unknown_func)"); va_start(ap, fmt); xlog_record_va(log_level, module_name, where_buf, fmt, ap); va_end(ap); } /* * **************************************************************************** * Print functions * **************************************************************************** */ /** * xlog_record_va: * @log_level: The log level of the message to write. * @module_name: The name of the module this message applies to. * @where: Where the log was generated (e.g., the file name, line number, etc). * @format: The printf() format of the message to write. * Note that a trailing newline is added if none is present. * @ap: The vararg list of arguments. * * Write a log message. **/ static void xlog_record_va(xlog_level_t log_level, const char *module_name, const char *where, const char *format, va_list ap) { const char *preamble_lead; const char *process_name_lead; char *buf_payload_ptr = NULL, *buf_preamble_ptr = NULL; char *buf_output_ptr = NULL; int buf_output_size; size_t i; #ifdef SIGPIPE sig_t sigpipe_handler; #endif if (! start_flag) { if (! init_flag) fprintf(stderr, "Logging must be initialized first by xlog_init()\n"); if (! start_flag) fprintf(stderr, "Logging must be started first by xlog_start()\n"); abort(); } if ((xlog_output_file_count == 0) && (xlog_output_func_count == 0)) return; if (XLOG_LEVEL_MAX <= log_level) return; /* Invalid log level */ if (! xlog_level_enabled[log_level]) return; /* The log level is disabled */ #ifdef SIGPIPE sigpipe_handler = signal(SIGPIPE, SIG_IGN); #endif preamble_lead = (preamble_string) ? preamble_string : ""; process_name_lead = (process_name_string) ? process_name_string : ""; /* Special case for the pre-amble level */ if (log_level == XLOG_LEVEL_RTRMGR_ONLY_NO_PREAMBLE) { x_asprintf(&buf_preamble_ptr, ""); } else { /* * Prepare the preamble string to write. * XXX: we need to prepare it once, otherwise the time may be * different when we write to more than one outputs. */ switch (xlog_verbose_level[log_level]) { case XLOG_VERBOSE_LOW: /* The minimum log information */ x_asprintf(&buf_preamble_ptr, "[ %s %s %s %s ] ", xlog_localtime2string(), xlog_level_names[log_level], process_name_lead, module_name); break; case XLOG_VERBOSE_MEDIUM: /* Add preamble string if non-NULL */ x_asprintf(&buf_preamble_ptr, "[ %s %s %s %s %s ] ", xlog_localtime2string(), preamble_lead, xlog_level_names[log_level], process_name_lead, module_name); break; case XLOG_VERBOSE_HIGH: /* Most verbose */ default: x_asprintf(&buf_preamble_ptr, "[ %s %s %s %s:%d %s %s ] ", xlog_localtime2string(), preamble_lead, xlog_level_names[log_level], process_name_lead, (int)pid, module_name, where); break; } } /* * Prepare the payload string to write */ x_vasprintf(&buf_payload_ptr, format, ap); if ((buf_preamble_ptr == NULL) && ((buf_payload_ptr == NULL) || buf_payload_ptr[0] == '\0')) goto cleanup_label; /* * Prepare the output string to write. * XXX: here we explicitly add the '\n' at the end of the * output string, because the XLOG message format implies it. */ buf_output_size = x_asprintf(&buf_output_ptr, "%s%s\n", buf_preamble_ptr, buf_payload_ptr); if ((buf_output_ptr == NULL) || (buf_output_ptr[0] == '\0') || (buf_output_size < 0)) { goto cleanup_label; } /* Remove our '\n' from the end if the payload itself already has one */ if (buf_output_size >= 2) { char n1, n2; n1 = buf_output_ptr[buf_output_size - 2]; n2 = buf_output_ptr[buf_output_size - 1]; if ((n1 == '\n') && (n2 == '\n')) buf_output_ptr[buf_output_size - 1] = '\0'; } /* * Write to the file descriptors */ for (i = 0; i < xlog_output_file_count; ) { FILE *fp = xlog_outputs_file[i]; if (xlog_write(fp, "%s", buf_output_ptr) || xlog_flush(fp)) { xlog_remove_output(fp); continue; } i++; } /* * Write to the functions */ for (i = 0; i < xlog_output_func_count; ) { xlog_output_func_t func = xlog_outputs_func[i]; void *obj = xlog_outputs_obj[i]; if (func(obj, log_level, buf_output_ptr) < 0) { xlog_remove_output_func(func, obj); continue; } i++; } cleanup_label: /* * Cleanup */ if (buf_preamble_ptr) free(buf_preamble_ptr); if (buf_payload_ptr) free(buf_payload_ptr); if (buf_output_ptr) free(buf_output_ptr); #ifdef SIGPIPE signal(SIGPIPE, sigpipe_handler); #endif } /** * xlog_write: * @fp: The file descriptor to write to. * @fmt: The printf() style format of the message to write. * @: * * Format a message for writing and call a function to write it to @fp. * * Return value: 0 on success, otherwise -1. **/ static int xlog_write(FILE* fp, const char* fmt, ...) { va_list ap; int retval; va_start(ap, fmt); retval = xlog_write_va(fp, fmt, ap); va_end(ap); return (retval); } /** * xlog_write_va: * @fp: The file descriptor to write to. * @fmt: The printf() style format of the message to write. * @ap: The vararg list of arguments. * * Write a message to @fp. * * Return value: 0 on success, otherwise -1. **/ static int xlog_write_va(FILE* fp, const char* fmt, va_list ap) { assert(fp != NULL); if (vfprintf(fp, fmt, ap) < 0) { return (-1); } return (0); } /** * xlog_flush: * @fp: The file descriptor to flush. * * Flushes all buffered data for output stream @fp. * * Return value: 0 on success, otherwise -1. **/ static int xlog_flush(FILE* fp) { if (! fflush(fp)) return (0); else return (-1); } /* * **************************************************************************** * Output stream functions * **************************************************************************** */ /** * xlog_add_output: * @fp: The file descriptor to add to the set of output streams. * * Add a file descriptor to the set of output streams. * * Return value: 0 on success, otherwise -1. **/ int xlog_add_output(FILE* fp) { size_t i; for (i = 0; i < xlog_output_file_count; i++) { if (xlog_outputs_file[i] == fp) { return (0); } } if (i < MAX_XLOG_OUTPUTS) { xlog_outputs_file[i] = fp; xlog_output_file_count++; return (0); } return (-1); } /** * xlog_remove_output: * @fp: The file descriptor to remove from the set of output streams. * * Remove a file descriptor from the set of output streams. * * Return value: 0 on success, otherwise -1. **/ int xlog_remove_output(FILE* fp) { size_t i, j; for (i = 0; i < xlog_output_file_count; i++) { if (xlog_outputs_file[i] == fp) { for(j = i + 1; j < xlog_output_file_count; j++) { xlog_outputs_file[j - 1] = xlog_outputs_file[j]; } xlog_output_file_count--; return (0); } } return (-1); } /** * xlog_add_output_func: * @func: The function to add to the set of output streams. * @obj: The object to supply @func with when called. * * Add a processing function and an object to the set of output streams. * * Return value: 0 on success, otherwise -1. **/ int xlog_add_output_func(xlog_output_func_t func, void *obj) { size_t i; for (i = 0; i < xlog_output_func_count; i++) { if ((xlog_outputs_func[i] == func) && (xlog_outputs_obj[i] == obj)) { return (0); } } if (i < MAX_XLOG_OUTPUTS) { xlog_outputs_func[i] = func; xlog_outputs_obj[i] = obj; xlog_output_func_count++; return (0); } return (-1); } /** * xlog_remove_output_func: * @func: The function to remove from the set of output streams. * @obj: The object that @func was supplied with. * * Remove a processing function and an object from the set of output streams. * * Return value: 0 on success, otherwise -1. **/ int xlog_remove_output_func(xlog_output_func_t func, void *obj) { size_t i, j; for (i = 0; i < xlog_output_func_count; i++) { if ((xlog_outputs_func[i] == func) && (xlog_outputs_obj[i] == obj)) { for(j = i + 1; j < xlog_output_func_count; j++) { xlog_outputs_func[j - 1] = xlog_outputs_func[j]; xlog_outputs_obj[j - 1] = xlog_outputs_obj[j]; } xlog_output_func_count--; return (0); } } return (-1); } /** * xlog_add_default_output: * @void: * * Add the default output stream to the set of output streams. * XXX: right now the default is '/dev/stderr', but it should eventually be: * `/dev/console' if the process has sufficient permissions, * and `/dev/stderr' otherwise. * * Return value: 0 on success, otherwise -1. **/ int xlog_add_default_output(void) { #ifdef HOST_OS_WINDOWS FILE *fp = NULL; HANDLE hstd = INVALID_HANDLE_VALUE; /* * XXX: We may need to duplicate these handles; * fclose() on _open_osfhandle() derived handles results * in the underlying handle being closed. */ hstd = GetStdHandle(STD_ERROR_HANDLE); fp = _fdopen(_open_osfhandle((long)hstd, _O_TEXT), "w"); if (fp != NULL) return (xlog_add_output(fp)); fp = fopen("CONOUT$", "w"); if (fp != NULL) return (xlog_add_output(fp)); hstd = GetStdHandle(STD_OUTPUT_HANDLE), fp = _fdopen(_open_osfhandle((long)hstd, _O_TEXT), "w"); if (fp != NULL) return (xlog_add_output(fp)); #else /* !HOST_OS_WINDOWS */ const char* defaults[] = { /* The default outputs (in preference order) */ "/dev/stderr", /* XXX: temporary this is the default */ "/dev/console", "/dev/stdout" }; size_t ndefaults = sizeof(defaults) / sizeof(defaults[0]); size_t i; /* * Attempt to open default output stream, console first in case * we are root, then stderr. */ for (i = 0; fp_default == NULL && i < ndefaults; i++) { if ((fp_default = fopen(defaults[i], "w")) != NULL) { return (xlog_add_output(fp_default)); } } #endif /* HOST_OS_WINDOWS */ return -1; } /** * xlog_remove_default_output: * @void: * * Remove the default output stream from the set of output streams. * * Return value: 0 on success, otherwise -1. **/ int xlog_remove_default_output(void) { int r = 0; if (fp_default != NULL) { r = xlog_remove_output(fp_default); fclose (fp_default); fp_default = NULL; } return (r); } /** * xlog_localtime2string: * @void: * * Compute the current local time and return it as a string in the format: * Year/Month/Day Hour:Minute:Second.Microsecond * Example: 2002/02/05 20:22:09.808632 * Note that the returned string uses statically allocated memory, * and does not need to be de-allocated. * * Return value: A statically allocated string with the local time using * the format described above. * XXX: This function uses a BSD-specific function, gettimeofday(). **/ const char * xlog_localtime2string(void) { #ifdef HOST_OS_WINDOWS static char buf[64]; SYSTEMTIME st; DWORD usecs; GetSystemTime(&st); usecs = st.wMilliseconds * 1000; snprintf(buf, sizeof(buf), "%u/%u/%u %u:%u:%u.%lu", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, usecs); return (buf); #else /* !HOST_OS_WINDOWS */ static char ret_buf[64]; struct timeval tv; time_t clock; struct tm *tm; gettimeofday(&tv, NULL); clock = tv.tv_sec; tm = localtime(&clock); time_t sofar = strftime(ret_buf, sizeof(ret_buf), "%Y/%m/%d %H:%M:%S", tm); if (sofar == 0) { snprintf(ret_buf, sizeof(ret_buf), "strftime ERROR"); return (ret_buf); } #ifdef XORP_LOG_PRINT_USECS snprintf(ret_buf + sofar, sizeof(ret_buf) - sofar, ".%lu", (unsigned long)tv.tv_usec); #endif return (ret_buf); #endif /* HOST_OS_WINDOWS */ } /** * x_vasprintf: * @ret: A pointer to the string pointer to store the result. * @format: The printf(3)-style format. * @ap: The variable arguments for @format. * * A local implementation of vasprintf(3). * Note that vasprintf(3) is not POSIX, so we don't attempt to * check whether it is available and use the system implementation * instead of our own below. * Part of the reasoning for not using it is technical: * on Linux vasprintf(3) is declared #ifdef __USE_GNU in , * and it is not healthy to define __USE_GNU just to make the code * compile on Linux. * * Return value: (From FreeBSD vasprintf(3) manual page). * The number of characters printed (not including the * trailing '\0' used to end output to strings). Also, set @*ret to be * a pointer to a buffer sufficiently large to hold the formatted string. * This pointer should be passed to free(3) to release the allocated * storage when it is no longer needed. If sufficient space cannot * be allocated, will return -1 and set @ret to be a NULL pointer. * * TODO: this function and x_asprintf() below probably should * be renamed to vasprintf() and asprintf() and go somewhere else, * something like "compat.c" . **/ int x_vasprintf(char **ret, const char *format, va_list ap) { size_t i, buf_size = 1024 + 1; char *buf_ptr = NULL; int ret_size; #ifdef HAVE_VA_COPY va_list temp; #endif for (i = 0; i < 3; i++) { /* * XXX: two iterations should be sufficient to compute the * buffer size and allocate it, but anyway we try one more time */ buf_ptr = malloc(buf_size); if (buf_ptr == NULL) break; /* Cannot allocate memory */ buf_ptr[0] = '\0'; /* XXX * va_copy does not exist in older compilers like gcc * 2.95. On some architectures like the i386 using a va_list * argument multiple times doesn't seem to cause a problem. On * the AMD 64 it is required. */ #ifdef HAVE_VA_COPY va_copy(temp, ap); ret_size = vsnprintf(buf_ptr, buf_size, format, temp); #else ret_size = vsnprintf(buf_ptr, buf_size, format, ap); #endif if (ret_size < 0) break; /* Cannot format the string */ if ((size_t)ret_size < buf_size) { *ret = buf_ptr; return (ret_size); } /* The allocated buffer was too small. Try again. */ free(buf_ptr); buf_ptr = NULL; buf_size = ret_size + 1; /* XXX: include space for trailing '\0' */ } /* * Error: cannot format the string or cannot allocate enough memory */ if (buf_ptr != NULL) free(buf_ptr); *ret = NULL; return (-1); } /** * x_asprintf: * @ret: A pointer to the string pointer to store the result. * @format: The printf(3)-style format. * @...: The variable arguments for @format. * * A local implementation of asprintf(3) if it is missing. * If vasprintf(3) is available, it is called instead. * * Return value: (From FreeBSD asprintf(3) manual page). * The number of characters printed (not including the * trailing '\0' used to end output to strings). Also, set @*ret to be * a pointer to a buffer sufficiently large to hold the formatted string. * This pointer should be passed to free(3) to release the allocated * storage when it is no longer needed. If sufficient space cannot * be allocated, will return -1 and set @ret to be a NULL pointer. **/ int x_asprintf(char **ret, const char *format, ...) { va_list ap; int ret_size; va_start(ap, format); ret_size = x_vasprintf(ret, format, ap); va_end(ap); return (ret_size); } /* * **************************************************************************** * Syslog interface functions, conforming to X/Open. * **************************************************************************** */ #if defined(HAVE_SYSLOG_H) && defined(HAVE_SYSLOG) typedef struct _code { const char *c_name; int32_t c_val; } SYSLOG_CODE; static SYSLOG_CODE prioritynames[] = { { "alert", LOG_ALERT, }, { "crit", LOG_CRIT, }, { "debug", LOG_DEBUG, }, { "emerg", LOG_EMERG, }, { "err", LOG_ERR, }, { "info", LOG_INFO, }, { "notice", LOG_NOTICE, }, { "warning", LOG_WARNING, }, { NULL, -1, } }; static SYSLOG_CODE facilitynames[] = { { "auth", LOG_AUTH, }, { "cron", LOG_CRON, }, { "daemon", LOG_DAEMON, }, { "kern", LOG_KERN, }, { "lpr", LOG_LPR, }, { "mail", LOG_MAIL, }, { "news", LOG_NEWS, }, { "user", LOG_USER, }, { "uucp", LOG_UUCP, }, { "local0", LOG_LOCAL0, }, { "local1", LOG_LOCAL1, }, { "local2", LOG_LOCAL2, }, { "local3", LOG_LOCAL3, }, { "local4", LOG_LOCAL4, }, { "local5", LOG_LOCAL5, }, { "local6", LOG_LOCAL6, }, { "local7", LOG_LOCAL7, }, { NULL, -1, } }; static int32_t xlog_level_to_syslog_priority(xlog_level_t xloglevel) { switch (xloglevel) { case XLOG_LEVEL_FATAL: return (LOG_CRIT); break; case XLOG_LEVEL_ERROR: return (LOG_ERR); break; case XLOG_LEVEL_WARNING: return (LOG_WARNING); break; case XLOG_LEVEL_INFO: return (LOG_INFO); break; case XLOG_LEVEL_RTRMGR_ONLY_NO_PREAMBLE: /* XXX: temp, to be removed; see Bugzilla entry 795 */ return (LOG_INFO); break; default: XLOG_UNREACHABLE(); } return (-1); } static int xlog_syslog_output_func(void *obj, xlog_level_t level, const char *msg) { int32_t facility = (intptr_t)obj; int32_t priority = xlog_level_to_syslog_priority(level); syslog(facility|priority, "%s", msg); return (0); } /* * Parse . */ static int xlog_parse_syslog_spec(const char *syslogspec, int *facility, int *priority) { int retval; int8_t i; int32_t xfacility, xpriority; char *facname, *priname, *tmpspec; SYSLOG_CODE* sc; retval = -1; facname = priname = tmpspec = NULL; tmpspec = strdup(syslogspec); if (tmpspec == NULL) return (retval); priname = strchr(tmpspec, '.'); if (priname != NULL) *priname = '\0'; facname = tmpspec; xfacility = -1; for (i = 0, sc = &facilitynames[0]; sc->c_val != -1; ++sc, ++i) { if (0 == strcasecmp(sc->c_name, facname)) { xfacility = i; break; } } if (xfacility == -1) goto out; *facility = xfacility; if (priname != NULL && ++priname != '\0') { xpriority = -1; for (i = 0, sc = &prioritynames[0]; sc->c_val != -1; ++sc, ++i) { if (0 == strcasecmp(sc->c_name, priname)) { xpriority = i; break; } } if (xpriority == -1) { goto out; } *priority = xpriority; } else *priority = LOG_WARNING; retval = 0; out: free(tmpspec); return (retval); } int xlog_add_syslog_output(const char *syslogspec) { int32_t facility = -1; int32_t priority = -1; if (-1 == xlog_parse_syslog_spec(syslogspec, &facility, &priority)) return (-1); openlog("xorp", LOG_PID | LOG_NDELAY | LOG_CONS, facility); xlog_add_output_func(xlog_syslog_output_func, (void *)(intptr_t)facility); return (0); } #else /* !(HAVE_SYSLOG_H && HAVE_SYSLOG) */ int xlog_add_syslog_output(const char *syslogspec) { return (-1); UNUSED(syslogspec); } #endif /* HAVE_SYSLOG_H && HAVE_SYSLOG */ xorp/libxorp/selector.hh0000664000076400007640000001670411630257324015513 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __LIBXORP_SELECTOR_HH__ #define __LIBXORP_SELECTOR_HH__ #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_SYS_TIME_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #include "libxorp/xorp.h" #ifndef USE_WIN_DISPATCHER #include "callback.hh" #include "ioevents.hh" #include "task.hh" class ClockBase; class SelectorList; class TimeVal; /** * Selector event type masks. */ enum SelectorMask { SEL_NONE = 0x0, // No events SEL_RD = 0x01, // Read events SEL_WR = 0x02, // Write events SEL_EX = 0x04, // Exception events SEL_ALL = SEL_RD | SEL_WR | SEL_EX // All events }; class SelectorTag; typedef ref_ptr Selector; /** * @short Abstract class used to receive SelectorList notifications * * A SelectorListObserverBase abstract class can be subtyped to create classes * that will receive @ref SelectorList events. * All methods in this class are private, since they must only be invoked by * the friend class SelectorList */ class SelectorListObserverBase { public: virtual ~SelectorListObserverBase(); private: /** * This function will get called when a new file descriptor is * added to the SelectorList. */ virtual void notify_added(XorpFd fd, const SelectorMask& mask) = 0; /** * This function will get called when a new file descriptor is * removed from the SelectorList. */ virtual void notify_removed(XorpFd fd, const SelectorMask& mask) = 0; SelectorList * _observed; friend class SelectorList; }; /** * @short A class to provide an interface to I/O multiplexing. * * A SelectorList provides an entity where callbacks for pending I/O * operations on file descriptors may be registered. The callbacks * are invoked when one of the @ref wait_and_dispatch methods is called * and I/O is pending on the particular descriptors. */ class SelectorList : public NONCOPYABLE { public: /** * Default constructor. */ SelectorList(ClockBase* clock); /** * Destructor. */ virtual ~SelectorList(); void set_debug(bool v) { _is_debug = v;} bool is_debug() const { return (_is_debug); } /** * Add a hook for pending I/O operations on a callback. * * Only one callback may be registered for each possible I/O event * type (read, write, exception). * * Multiple event types may share the same callback. If multiple * event types share the same callback and multiple types of event * are pending, the callback is invoked just once and the mask * argument to the callback shows which events are pending. * * @param file descriptor. * * @param mask mask of I/O event types * that should invoke callback. An OR'ed combination of the * available @ref SelectorMask values. * * @param scb callback object that will be invoked when the * descriptor. * * @return true if function succeeds, false otherwise. */ bool add_ioevent_cb(XorpFd fd, IoEventType type, const IoEventCb& cb, int priority = XorpTask::PRIORITY_DEFAULT); /** * Remove hooks for pending I/O operations. * * @param fd the file descriptor. * * @param mask of event types to be removed, e.g. an OR'ed * combination of the available @ref SelectorMask values. */ void remove_ioevent_cb(XorpFd fd, IoEventType type = IOT_ANY); /** * Find out if any of the selectors are ready. * * @return true if any selector is ready. */ bool ready(); /** * Find out the highest priority from the ready file descriptors. * * @return the priority of the highest priority ready file descriptor. */ int get_ready_priority(bool force); /** * Wait for a pending I/O events and invoke callbacks when they * become ready. * * @param timeout the maximum period to wait for. * * @return the number of callbacks that were made. */ int wait_and_dispatch(TimeVal& timeout); /** * Wait for a pending I/O events and invoke callbacks when they * become ready. * * @param millisecs the maximum period in milliseconds to wait for. * * @return the number of callbacks that were made. */ int wait_and_dispatch(int millisecs); /** * Get the count of the descriptors that have been added. * * @return the count of the descriptors that have been added. */ size_t descriptor_count() const { return _descriptor_count; } /** * Get a copy of the current list of monitored file descriptors in * Unix fd_set format * * @param the selected mask as @ref SelectorMask (SEL_RD, SEL_WR, or SEL_EX) * * @return the selected fd_set. */ void get_fd_set(SelectorMask selected_mask, fd_set& fds) const; /** * Get a the value of the largest monitored file descriptor * * @return the maximum fd. */ int get_max_fd() const; /** * Set the SelectorObserver object that will receive notifications * * @return void */ void set_observer(SelectorListObserverBase& obs); /** * Remove the SelectorObserver object that receives notifications * * @return void */ void remove_observer(); protected: void callback_bad_descriptors(); private: int do_select(struct timeval* to, bool force); private: enum { // correspond to SelectorMask; correspondence checked with // x_static_assert SEL_RD_IDX = 0, SEL_WR_IDX = 1, SEL_EX_IDX = 2, SEL_MAX_IDX = 3 }; #define GOOD_NODE_MAGIC 0x12345678 struct Node { int magic; // catch memory errors. int _mask[SEL_MAX_IDX]; IoEventCb _cb[SEL_MAX_IDX]; // Reverse mapping of legacy UNIX event to IoEvent IoEventType _iot[SEL_MAX_IDX]; int _priority[SEL_MAX_IDX]; Node(); ~Node(); Node(const Node& rhs); // copy constructor Node& operator=(const Node& rhs); // operator= overload bool add_okay(SelectorMask m, IoEventType type, const IoEventCb& cb, int priority); int run_hooks(SelectorMask m, XorpFd fd); void clear(SelectorMask m); bool is_empty(); }; ClockBase* _clock; SelectorListObserverBase * _observer; fd_set _fds[SEL_MAX_IDX]; fd_set _testfds[SEL_MAX_IDX]; int _testfds_n; int _maxpri_fd; int _maxpri_sel; int _last_served_fd; int _last_served_sel; vector _selector_entries; int _maxfd; size_t _descriptor_count; bool _is_debug; }; #endif // USE_WIN_DISPATCHER #endif // __LIBXORP_SELECTOR_HH__ xorp/libxorp/safe_callback_obj.hh0000664000076400007640000000612511540225527017253 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/libxorp/safe_callback_obj.hh,v 1.11 2008/10/02 21:57:33 bms Exp $ #ifndef __LIBXORP_SAFE_CALLBACK_OBJ_HH__ #define __LIBXORP_SAFE_CALLBACK_OBJ_HH__ #include "xorp.h" class SafeCallbackBase; /** * @short Base class for objects that are callback safe. * * Objects that wish to be callback safe should be derived from this * class. The class works in conjunction with SafeCallbackBase and * between them implement callback and callback-object tracking. When * a CallbackSafeObject is destructed it informs all the callbacks that * refer to it that this is the case and invalidates (sets to null) * the object they point to. * * Copy operations are not supported. It's hard to know what the * correct thing to do on assignment or copy, so best bet is not * to do anything. */ class CallbackSafeObject : public NONCOPYABLE { public: CallbackSafeObject() {} virtual ~CallbackSafeObject(); void ref_cb(SafeCallbackBase* scb); void unref_cb(SafeCallbackBase* scb); protected: vector _cbs; }; /** * @short Base class for safe callbacks. * * These are object callbacks that are only dispatched if target of * callback is non-null. */ class SafeCallbackBase : public NONCOPYABLE { public: /** * Constructor. * * Informs CallbackSafeObject that this callback operates on it. */ SafeCallbackBase(CallbackSafeObject* o); /** * Destructor. * * Informs CallbackSafeObject that is tracking callback * instances that this callback no longer exists. */ ~SafeCallbackBase(); void invalidate(); bool valid() const; protected: SafeCallbackBase(); // Not directly constructible protected: CallbackSafeObject* _cso; }; // ---------------------------------------------------------------------------- // Inline CallbackSafeObject methods inline void CallbackSafeObject::ref_cb(SafeCallbackBase* scb) { _cbs.push_back(scb); } inline void CallbackSafeObject::unref_cb(SafeCallbackBase* scb) { vector::iterator i = find(_cbs.begin(), _cbs.end(), scb); if (i != _cbs.end()) _cbs.erase(i); } #endif // __LIBXORP_SAFE_CALLBACK_OBJ_HH__ xorp/libxorp/utils.cc0000664000076400007640000001425111540224230015002 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "xorp.h" #include "c_format.hh" #include "utils.hh" list split(const string& s, char ch) { list parts; string s2 = s; size_t ix; ix = s2.find(ch); while (ix != string::npos) { parts.push_back(s2.substr(0, ix)); s2 = s2.substr(ix + 1, s2.size() - ix); ix = s2.find(ch); } if (!s2.empty()) parts.push_back(s2); return parts; } string strip_empty_spaces(const string& s) { string res = s; // Strip the heading and trailing empty spaces while (!res.empty()) { size_t len = res.length(); if ((res[0] == ' ') || (res[0] == '\t')) { res = res.substr(1, len - 1); continue; } if ((res[len - 1] == ' ') || (res[len - 1] == '\t')) { res = res.substr(0, res.length() - 1); continue; } break; } return res; } bool has_empty_space(const string& s) { string::size_type space; space = s.find(' '); if (space == string::npos) space = s.find('\t'); if (space != string::npos) return (true); return (false); } #ifdef HOST_OS_WINDOWS void win_quote_args(const list& args, string& cmdline) { list::const_iterator curarg; for (curarg = args.begin(); curarg != args.end(); ++curarg) { cmdline += " "; if ((curarg->length() == 0 || string::npos != curarg->find_first_of("\n\t \"") || string::npos != curarg->find("\\\\"))) { string tmparg(*curarg); string::size_type len = tmparg.length(); string::size_type pos = 0; while (pos < len && string::npos != pos) { pos = tmparg.find_first_of("\\\"", pos); if (tmparg[pos] == '\\') { if (tmparg[pos+1] == '\\') { pos++; } else if (pos+1 == len || tmparg[pos+1] == '\"') { tmparg.insert(pos, "\\"); pos += 2; } } else if (tmparg[pos] == '\"') { tmparg.insert(pos, "\\"); pos += 2; } } cmdline += "\"" + tmparg + "\""; } else { cmdline += *curarg; } } } #endif // HOST_OS_WINDOWS const char* xorp_basename(const char* argv0) { const char* p = strrchr(argv0, PATH_DELIMITER_CHAR); if (p != NULL) { return p + 1; } return argv0; } FILE* xorp_make_temporary_file(const string& tmp_dir, const string& filename_template, string& final_filename, string& errmsg) { #ifdef HOST_OS_WINDOWS char dirname[MAXPATHLEN]; #endif // HOST_OS_WINDOWS char filename[MAXPATHLEN]; list cand_tmp_dirs; char* value; FILE* fp; if (filename_template.empty()) { errmsg = "Empty file name template"; return (NULL); } // // Create the list of candidate temporary directories // // Get the values of environment variables value = getenv("TMPDIR"); if (value != NULL) cand_tmp_dirs.push_back(value); #ifdef HOST_OS_WINDOWS value = getenv("TEMP"); if (value != NULL) cand_tmp_dirs.push_back(value); value = getenv("TMP"); if (value != NULL) cand_tmp_dirs.push_back(value); #endif // HOST_OS_WINDOWS // Argument "tmp_dir" if it is not an empty string if (! tmp_dir.empty()) cand_tmp_dirs.push_back(tmp_dir); // The system-specific path of the directory designated for temporary files #ifdef HOST_OS_WINDOWS size_t size; size = GetTempPathA(sizeof(dirname), dirname); if (size >= sizeof(dirname)) { errmsg = c_format("Internal error: directory name buffer size is too " "small (allocated %u required %u)", XORP_UINT_CAST(sizeof(dirname)), XORP_UINT_CAST(size + 1)); return (NULL); } if (size != 0) { cand_tmp_dirs.push_back(dirname); } #endif // HOST_OS_WINDOWS // The "P_tmpdir" directory if this macro is defined #ifdef P_tmpdir cand_tmp_dirs.push_back(P_tmpdir); #endif // A list of hard-coded directory names #ifdef HOST_OS_WINDOWS cand_tmp_dirs.push_back("C:\\TEMP"); #else cand_tmp_dirs.push_back("/tmp"); cand_tmp_dirs.push_back("/usr/tmp"); cand_tmp_dirs.push_back("/var/tmp"); #endif // // Find the first directory that allows us to create the temporary file // list::iterator iter; for (iter = cand_tmp_dirs.begin(); iter != cand_tmp_dirs.end(); ++iter) { string tmp_dir = *iter; if (tmp_dir.empty()) continue; // Remove the trailing '/' (or '\') from the directory name if (tmp_dir.substr(tmp_dir.size() - 1, 1) == PATH_DELIMITER_STRING) tmp_dir.erase(tmp_dir.size() - 1); filename[0] = '\0'; #ifdef HOST_OS_WINDOWS // Get the temporary filename and open the file snprintf(dirname, sizeof(dirname)/sizeof(dirname[0]), "%s", tmp_dir.c_str()); if (GetTempFileNameA(dirname, filename_template.c_str(), 0, filename) == 0) continue; fp = fopen(filename, "w+"); if (fp == NULL) continue; #else // ! HOST_OS_WINDOWS // Compose the temporary file name and try to create the file string tmp_filename = tmp_dir + PATH_DELIMITER_STRING + filename_template + ".XXXXXX"; snprintf(filename, sizeof(filename)/sizeof(filename[0]), "%s", tmp_filename.c_str()); int fd = mkstemp(filename); if (fd == -1) continue; // Associate a stream with the file descriptor fp = fdopen(fd, "w+"); if (fp == NULL) { close(fd); continue; } #endif // ! HOST_OS_WINDOWS // Success final_filename = filename; return (fp); } errmsg = "Cannot find a directory to create the temporary file"; return (NULL); } xorp/libxorp/popen.cc0000664000076400007640000003422011540224227014767 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // Part of this program is derived from the following software: // - FreeBSD's popen(3) implementation // // The above software is covered by the following license(s): // /* * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software written by Ken Arnold and * published in UNIX Review, Vol. 6, No. 8. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD: src/lib/libc/gen/popen.c,v 1.14 2000/01/27 23:06:19 jasone Exp $ */ #include "libxorp_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/c_format.hh" #include "libxorp/utils.hh" #ifdef HAVE_SYS_PARAM_H #include #endif #ifdef HAVE_SYS_WAIT_H #include #endif #ifdef HAVE_FCNTL_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_VFORK_H #include #endif #ifdef HAVE_PATHS_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #include #include #include #include #include "popen.hh" #ifdef HOST_OS_WINDOWS extern char **_environ; #else extern char **environ; #endif // XXX: static instance static struct pid_s { struct pid_s *next; FILE *fp_out; FILE *fp_err; #ifdef HOST_OS_WINDOWS DWORD pid; HANDLE ph; HANDLE h_out_child; // child ends of pipes visible in parent HANDLE h_err_child; // need to be closed after termination. #else pid_t pid; #endif bool is_closed; int pstat; // The process wait status if is_closed is true } *pidlist; pid_t popen2(const string& command, const list& arguments, FILE *& outstream, FILE *&errstream, bool redirect_stderr_to_stdout) { #ifdef HOST_OS_WINDOWS struct pid_s *cur; FILE *iop_out, *iop_err; HANDLE hout[2], herr[2]; SECURITY_ATTRIBUTES pipesa = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE }; STARTUPINFOA si; PROCESS_INFORMATION pi; outstream = NULL; errstream = NULL; if (CreatePipe(&hout[0], &hout[1], &pipesa, 0) == 0) return (0); #if 0 if (redirect_stderr_to_stdout) { if (0 == DuplicateHandle(GetCurrentProcess(), hout[0], GetCurrentProcess(), &herr[0], 0, TRUE, DUPLICATE_SAME_ACCESS)) { CloseHandle(hout[0]); CloseHandle(hout[1]); return (0); } if (0 == DuplicateHandle(GetCurrentProcess(), hout[1], GetCurrentProcess(), &herr[1], 0, TRUE, DUPLICATE_SAME_ACCESS)) { CloseHandle(hout[0]); CloseHandle(hout[1]); return (0); } } else #endif // 0 { if (0 == CreatePipe(&herr[0], &herr[1], &pipesa, 0)) { CloseHandle(hout[0]); CloseHandle(hout[1]); return (0); } } if ((cur = (struct pid_s*)malloc(sizeof(struct pid_s))) == NULL) { CloseHandle(hout[0]); CloseHandle(hout[1]); CloseHandle(herr[0]); CloseHandle(herr[1]); return (0); } GetStartupInfoA(&si); si.dwFlags = STARTF_USESTDHANDLES; si.hStdInput = GetStdHandle(STD_INPUT_HANDLE); si.hStdOutput = hout[1]; si.hStdError = herr[1]; if (redirect_stderr_to_stdout) si.hStdError = hout[1]; #if 0 // We need to close the child ends of the pipe when the child terminates. // We need not do this for the parent ends; fclose() does this for us. cur->h_out_child = hout[1]; cur->h_err_child = herr[1]; #endif // 0 // XXX: We currently force the program name to be escaped with quotes // before munging the command line for CreateProcess(). string escaped_args = "\"" + command + "\""; win_quote_args(arguments, escaped_args); debug_msg("Trying to execute: '%s'\n", escaped_args.c_str()); if (CreateProcessA(NULL, const_cast(escaped_args.c_str()), NULL, NULL, TRUE, CREATE_DEFAULT_ERROR_MODE | CREATE_SUSPENDED, NULL, NULL, &si, &pi) == 0) { DWORD err = GetLastError(); string error_msg = c_format("Execution of %s failed: %u", command.c_str(), XORP_UINT_CAST(err)); UNUSED(error_msg); CloseHandle(hout[0]); CloseHandle(hout[1]); CloseHandle(herr[0]); CloseHandle(herr[1]); return (0); } /* Parent; assume _fdopen can't fail. */ iop_out = _fdopen(_open_osfhandle((long)hout[0], _O_RDONLY|_O_TEXT), "r"); iop_err = _fdopen(_open_osfhandle((long)herr[0], _O_RDONLY|_O_TEXT), "r"); setvbuf(iop_out, NULL, _IONBF, 0); setvbuf(iop_err, NULL, _IONBF, 0); /* Link into list of file descriptors. */ cur->fp_out = iop_out; cur->fp_err = iop_err; cur->pid = pi.dwProcessId; cur->ph = pi.hProcess; cur->is_closed = false; cur->pstat = 0; cur->next = pidlist; pidlist = cur; outstream = iop_out; errstream = iop_err; /* Kick off the child process's main thread. */ ResumeThread(pi.hThread); return (cur->pid); #else // ! HOST_OS_WINDOWS struct pid_s *cur; FILE *iop_out, *iop_err; int pdes_out[2], pdes_err[2], pid; size_t argv_size = 1 + arguments.size() + 1; const char **argv = reinterpret_cast(malloc(argv_size * sizeof(char *))); struct pid_s *p; outstream = NULL; errstream = NULL; if (pipe(pdes_out) < 0) { free(argv); return 0; } if (pipe(pdes_err) < 0) { (void)close(pdes_out[0]); (void)close(pdes_out[1]); free(argv); return 0; } if ((cur = (struct pid_s*)malloc(sizeof(struct pid_s))) == NULL) { (void)close(pdes_out[0]); (void)close(pdes_out[1]); (void)close(pdes_err[0]); (void)close(pdes_err[1]); free(argv); return 0; } /* Disable blocking on read */ int fl; fl = fcntl(pdes_out[0], F_GETFL); if (fcntl(pdes_out[0], F_SETFL, fl | O_NONBLOCK) == -1) { XLOG_FATAL("Cannot set O_NONBLOCK on file descriptor %d", pdes_out[0]); (void)close(pdes_out[0]); (void)close(pdes_out[1]); (void)close(pdes_err[0]); (void)close(pdes_err[1]); free(argv); return 0; } fl = fcntl(pdes_err[0], F_GETFL); if (fcntl(pdes_err[0], F_SETFL, fl | O_NONBLOCK) == -1) { XLOG_FATAL("Cannot set O_NONBLOCK on file descriptor %d", pdes_err[0]); (void)close(pdes_out[0]); (void)close(pdes_out[1]); (void)close(pdes_err[0]); (void)close(pdes_err[1]); free(argv); return 0; } // // Create the array with the command and the arguments // argv[0] = xorp_basename(command.c_str()); size_t i; list::const_iterator iter; for (i = 0, iter = arguments.begin(); iter != arguments.end(); ++i, ++iter) { argv[i + 1] = iter->c_str(); } argv[argv_size - 1] = NULL; switch (pid = vfork()) { case -1: /* Error. */ (void)close(pdes_out[0]); (void)close(pdes_out[1]); (void)close(pdes_err[0]); (void)close(pdes_err[1]); free(cur); free(argv); return 0; /* NOTREACHED */ case 0: /* Child. */ // // Unblock all signals that may have been blocked by the parent // sigset_t sigset; sigfillset(&sigset); sigprocmask(SIG_UNBLOCK, &sigset, NULL); /* * The dup2() to STDIN_FILENO is repeated to avoid * writing to pdes[1], which might corrupt the * parent's copy. This isn't good enough in * general, since the _exit() is no return, so * the compiler is free to corrupt all the local * variables. */ (void)close(pdes_out[0]); (void)close(pdes_err[0]); setvbuf(stdout, NULL, _IONBF, 0); setvbuf(stderr, NULL, _IONBF, 0); if (redirect_stderr_to_stdout) { // // Redirect stderr to stdout // bool do_close_pdes_out = false; bool do_close_pdes_err = false; if ((pdes_out[1] != STDOUT_FILENO) && (pdes_out[1] != STDERR_FILENO)) { do_close_pdes_out = true; } if ((pdes_err[1] != STDOUT_FILENO) && (pdes_err[1] != STDERR_FILENO)) { do_close_pdes_err = true; } if (pdes_out[1] != STDOUT_FILENO) { (void)dup2(pdes_out[1], STDOUT_FILENO); } if (pdes_out[1] != STDERR_FILENO) { (void)dup2(pdes_out[1], STDERR_FILENO); } if (do_close_pdes_out) (void)close(pdes_out[1]); if (do_close_pdes_err) (void)close(pdes_err[1]); } else { if (pdes_out[1] != STDOUT_FILENO) { (void)dup2(pdes_out[1], STDOUT_FILENO); (void)close(pdes_out[1]); } if (pdes_err[1] != STDERR_FILENO) { (void)dup2(pdes_err[1], STDERR_FILENO); (void)close(pdes_err[1]); } } for (p = pidlist; p; p = p->next) { (void)close(fileno(p->fp_out)); (void)close(fileno(p->fp_err)); } // Set the process as a group leader setpgid(0, 0); execve(const_cast(command.c_str()), const_cast(argv), environ); // // XXX: don't call any function that may corrupt the state of the // parent process, otherwise the result is unpredictable. // _exit(127); /* NOTREACHED */ } /* Parent; assume fdopen can't fail. */ iop_out = fdopen(pdes_out[0], "r"); iop_err = fdopen(pdes_err[0], "r"); setvbuf(iop_out, NULL, _IONBF, 0); setvbuf(iop_err, NULL, _IONBF, 0); (void)close(pdes_out[1]); (void)close(pdes_err[1]); free(argv); /* Link into list of file descriptors. */ cur->fp_out = iop_out; cur->fp_err = iop_err; cur->pid = pid; cur->is_closed = false; cur->pstat = 0; cur->next = pidlist; pidlist = cur; outstream = iop_out; errstream = iop_err; return pid; #endif // ! HOST_OS_WINDOWS } /* * pclose -- * Pclose returns -1 if stream is not associated with a `popened' command, * if already `pclosed', or waitpid returns an error. */ int pclose2(FILE *iop_out, bool dont_wait) { register struct pid_s *cur, *last; int pstat = 0; pid_t pid = 0; /* Find the appropriate file pointer. */ for (last = NULL, cur = pidlist; cur; last = cur, cur = cur->next) if (cur->fp_out == iop_out) break; if (cur == NULL) return (-1); pid = cur->pid; if (dont_wait || cur->is_closed) { if (cur->is_closed) pstat = cur->pstat; else pstat = 0; // XXX: imitating the result of wait4(WNOHANG) } #ifdef HOST_OS_WINDOWS if (! (dont_wait || cur->is_closed)) { DWORD dwStat = 0; BOOL result = GetExitCodeProcess(cur->ph, (LPDWORD)&dwStat); while (dwStat == STILL_ACTIVE) { WaitForSingleObject(cur->ph, INFINITE); result = GetExitCodeProcess(cur->ph, (LPDWORD)&dwStat); } XLOG_ASSERT(result != 0); pstat = (int)dwStat; } (void)fclose(cur->fp_out); (void)fclose(cur->fp_err); if (! (dont_wait || cur->is_closed)) { // CloseHandle(cur->h_out_child); // CloseHandle(cur->h_err_child); CloseHandle(cur->ph); } #else // ! HOST_OS_WINDOWS (void)fclose(cur->fp_out); (void)fclose(cur->fp_err); if (dont_wait || cur->is_closed) { if (cur->is_closed) pstat = cur->pstat; else pstat = 0; // XXX: imitating the result of wait4(WNOHANG) } else { do { pid = wait4(cur->pid, &pstat, 0, (struct rusage *)0); } while (pid == -1 && errno == EINTR); } #endif // ! HOST_OS_WINDOWS /* Remove the entry from the linked list. */ if (last == NULL) pidlist = cur->next; else last->next = cur->next; free(cur); return (pid == -1 ? -1 : pstat); } int popen2_mark_as_closed(pid_t pid, int wait_status) { struct pid_s *cur; for (cur = pidlist; cur != NULL; cur = cur->next) { if (static_cast(cur->pid) == pid) break; } if (cur == NULL) return (-1); cur->is_closed = true; cur->pstat = wait_status; return (0); } #ifdef HOST_OS_WINDOWS /* * Return the process handle given the process ID. * The handle we get from CreateProcess() has privileges which * the OpenProcess() handle doesn't get. */ HANDLE pgethandle(pid_t pid) { struct pid_s *cur; for (cur = pidlist; cur != NULL; cur = cur->next) { if (static_cast(cur->pid) == pid) break; } if (cur == NULL) return (INVALID_HANDLE_VALUE); return (cur->ph); } #endif // HOST_OS_WINDOWS xorp/libxorp/timeval.hh0000664000076400007640000003756211631741244015341 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __LIBXORP_TIMEVAL_HH__ #define __LIBXORP_TIMEVAL_HH__ #include "xorp.h" #include "random.h" #ifdef HAVE_SYS_TIME_H #include #endif #include // TODO: Move this out-of-line (let compiler make the decision) // TODO: Allow setting values, and remove return-by-value where possible. /** * @short TimeVal class * * TimeVal class is used for storing time value. Similar to "struct timeval", * the time value is in seconds and microseconds. */ class TimeVal { public: static const int32_t ONE_MILLION = 1000000; static const int32_t ONE_THOUSAND = 1000; #ifdef HOST_OS_WINDOWS /* * The difference between the beginning of the Windows epoch * (1601-01-01-00-00) and the UNIX epoch (1970-01-01-00-00) * is 134,774 days on the Julian calendar. Compute this * difference in seconds and use it as a constant. * XXX: This does not take account of leap seconds. */ static const int64_t UNIX_WIN32_EPOCH_DIFF = (134774LL * 24LL * 60LL * 60LL); static const int32_t UNITS_100NS_PER_1US = 10; #endif public: /** * Default constructor */ TimeVal() : _sec(0), _usec(0) {} /** * Constructor for given seconds and microseconds. * * @param sec the number of seconds. * @param usec the number of microseconds. */ TimeVal(int32_t sec, int32_t usec) : _sec(sec), _usec(usec) {} #ifndef HOST_OS_WINDOWS /** * Constructor for given "struct timeval". * * @param timeval the "struct timeval" time value to initialize this * object with. */ explicit TimeVal(const timeval& timeval) : _sec(timeval.tv_sec), _usec(timeval.tv_usec) {} #ifdef HAVE_STRUCT_TIMESPEC /** * Constructor for given POSIX "struct timespec". * * @param timespec the "struct timespec" time value to initialize this * object with. */ explicit TimeVal(const timespec& timespec) : _sec(timespec.tv_sec), _usec(timespec.tv_nsec / 1000) {} #endif #else /* HOST_OS_WINDOWS */ /** * Constructor for given "FILETIME". * * @param ft the "FILETIME" time value to initialize this object with. */ explicit TimeVal(const FILETIME& ft) { copy_in(ft); } #endif /* !HOST_OS_WINDOWS */ /** * Constructor for given double-float time value. * * @param d the double-float time value to initialize this object with. */ explicit TimeVal(const double& d); /** * Get the number of seconds. * * @return the number of seconds. */ int32_t sec() const { return _sec; } /** * Get the number of microseconds. * * @return the number of microseconds. */ int32_t usec() const { return _usec; } /** * @return seconds and microseconds as a string. */ string str() const; /** * Pretty print the time * * @return the time as formated by ctime(3) without the newline. */ string pretty_print() const; /** * Get zero value. */ static TimeVal ZERO(); /** * Get the maximum permitted value. */ static TimeVal MAXIMUM(); /** * Get the minimum permitted value. */ static TimeVal MINIMUM(); /** * Copy the time value from a timeval structure. * * @param timeval the storage to copy the time from. * @return the number of copied octets. */ size_t copy_in(const timeval& timeval); /** * Copy the time value to a timeval structure. * * @param timeval the storage to copy the time to. * @return the number of copied octets. */ size_t copy_out(timeval& timeval) const; #ifdef HAVE_STRUCT_TIMESPEC /** * Copy the time value from a POSIX timespec structure. * * @param timespec the storage to copy the time from. * @return the number of copied octets. */ size_t copy_in(const timespec& timespec); /** * Copy the time value to a POSIX timespec structure. * * @param timespec the storage to copy the time to. * @return the number of copied octets. */ size_t copy_out(timespec& timespec) const; #endif /** * Return an int64_t containing the total number of milliseconds * in the underlying structure. * This is intended for convenience when working with Win32 APIs. * XXX: This may overflow if _sec is too big. * * @return the number of milliseconds in total. */ int64_t to_ms() const; void set_ms(int64_t ms); #ifdef HOST_OS_WINDOWS /** * Copy the time value from a FILETIME structure. * * @param filetime the storage to copy the time from. * @return the number of copied octets. */ size_t copy_in(const FILETIME& filetime); /** * Copy the time value to a FILETIME structure. * * @param filetime the storage to copy the time to. * @return the number of copied octets. */ size_t copy_out(FILETIME& filetime) const; #endif /* HOST_OS_WINDOWS */ /** * Convert a TimeVal value to a double-float value. * * @return the double-float value of this TimeVal time. */ double get_double() const { return (_sec * 1.0 + _usec * 1.0e-6); } /** * Assignment Operator */ TimeVal& operator=(const TimeVal& other); /** * Equality Operator * * @param other the right-hand operand to compare against. * @return true if the left-hand operand is numerically same as the * right-hand operand. */ bool operator==(const TimeVal& other) const; /** * Less-Than Operator * * @param other the right-hand operand to compare against. * @return true if the left-hand operand is numerically smaller than the * right-hand operand. */ bool operator<(const TimeVal& other) const; /** * Assign-Sum Operator * * @param delta the TimeVal value to add to this TimeVal object. * @return the TimeVal value after the addition of @ref delta. */ const TimeVal& operator+=(const TimeVal& delta); /** * Addition Operator * * @param other the TimeVal value to add to the value of this * TimeVal object. * @return the TimeVal value after the addition of @ref other. */ TimeVal operator+(const TimeVal& other) const; /** * Assign-Difference Operator * * @param delta the TimeVal value to substract from this TimeVal object. * @return the TimeVal value after the substraction of @ref delta. */ const TimeVal& operator-=(const TimeVal& delta); /** * Substraction Operator * * @param other the TimeVal value to substract from the value of this * TimeVal object. * @return the TimeVal value after the substraction of @ref other. */ TimeVal operator-(const TimeVal& other) const; /** * Multiplication Operator for integer operand * * @param n the integer value used in multiplying the value of this * object with. * @return the TimeVal value of multiplying the value of this object * by @ref n. */ TimeVal operator*(int n) const; /** * Multiplication Operator for unsigned integer operand * * @param n the unsigned integer value used in multiplying the value of * this object with. * @return the TimeVal value of multiplying the value of this object * by @ref n. */ TimeVal operator*(unsigned int n) const; /** * Multiplication Operator for double float operand * * @param d the double float value used in multiplying the value of this * object with. * @return the TimeVal value of multiplying the value of this object * by @ref d. */ TimeVal operator*(const double& d) const; /** * Division Operator for integer operand * * @param n the integer value used in dividing the value of this * object with. * @return the TimeVal value of dividing the value of this object * by @ref n. */ TimeVal operator/(int n) const; /** * Division Operator for unsigned integer operand * * @param n the unsigned integer value used in dividing the value of this * object with. * @return the TimeVal value of dividing the value of this object * by @ref n. */ TimeVal operator/(unsigned int n) const; /** * Division Operator for double-float operand * * @param d the double-float value used in dividing the value of this * object with. * @return the TimeVal value of dividing the value of this object * by @ref d. */ TimeVal operator/(const double& d) const; /** * Test if this time value is numerically zero. * * @return true if the address is numerically zero. */ bool is_zero() const { return ((_sec == 0) && (_usec == 0)); } private: TimeVal(int i); // Not implemented int32_t _sec; // The number of seconds int32_t _usec; // The number of microseconds }; inline TimeVal::TimeVal(const double& d) : _sec((int32_t)d), _usec((int32_t)((d - ((double)_sec)) * ONE_MILLION + 0.5e-6)) { // // Adjust // if (_usec >= ONE_MILLION) { _sec += _usec / ONE_MILLION; _usec %= ONE_MILLION; } } inline size_t TimeVal::copy_in(const timeval& timeval) { _sec = timeval.tv_sec; _usec = timeval.tv_usec; return (sizeof(_sec) + sizeof(_usec)); } inline size_t TimeVal::copy_out(timeval& timeval) const { timeval.tv_sec = _sec; timeval.tv_usec = _usec; return (sizeof(_sec) + sizeof(_usec)); } #ifdef HAVE_STRUCT_TIMESPEC inline size_t TimeVal::copy_in(const timespec& timespec) { _sec = timespec.tv_sec; _usec = timespec.tv_nsec / 1000; return (sizeof(_sec) + sizeof(_usec)); } inline size_t TimeVal::copy_out(timespec& timespec) const { timespec.tv_sec = _sec; timespec.tv_nsec = _usec * 1000; return (sizeof(_sec) + sizeof(_usec)); } #endif #ifdef HOST_OS_WINDOWS /* * Convert Windows time to a BSD struct timeval using 64-bit integer * arithmetic, by the following steps: * 1. Scale Windows' 100ns resolution to BSD's 1us resolution. * 2. Subtract the difference since the beginning of their respective epochs. * 3. Extract the appropriate fractional parts from the 64-bit * integer used to represent Windows time. * Both UNIX and NT time systems correspond to UTC, therefore leap second * correction is NTP's problem. */ inline size_t TimeVal::copy_in(const FILETIME& filetime) { ULARGE_INTEGER ui; ui.LowPart = filetime.dwLowDateTime; ui.HighPart = filetime.dwHighDateTime; ui.QuadPart /= UNITS_100NS_PER_1US; ui.QuadPart -= UNIX_WIN32_EPOCH_DIFF * ONE_MILLION; _usec = ui.QuadPart % ONE_MILLION; _sec = ui.QuadPart / ONE_MILLION; return (sizeof(filetime.dwLowDateTime) + sizeof(filetime.dwHighDateTime)); } inline size_t TimeVal::copy_out(FILETIME& filetime) const { ULARGE_INTEGER ui; ui.QuadPart = _sec + UNIX_WIN32_EPOCH_DIFF; ui.QuadPart *= ONE_MILLION; ui.QuadPart += _usec; ui.QuadPart *= UNITS_100NS_PER_1US; filetime.dwLowDateTime = ui.LowPart; filetime.dwHighDateTime = ui.HighPart; return (sizeof(filetime.dwLowDateTime) + sizeof(filetime.dwHighDateTime)); } #endif /* HOST_OS_WINDOWS */ inline TimeVal& TimeVal::operator=(const TimeVal& other) { _sec = other.sec(); _usec = other.usec(); return *this; } inline bool TimeVal::operator==(const TimeVal& other) const { return (_sec == other.sec()) && (_usec == other.usec()); } inline bool TimeVal::operator<(const TimeVal& other) const { return (_sec == other.sec()) ? _usec < other.usec() : _sec < other.sec(); } inline const TimeVal& TimeVal::operator+=(const TimeVal& delta) { _sec += delta.sec(); _usec += delta.usec(); if (_usec >= ONE_MILLION) { _sec++; _usec -= ONE_MILLION; } return (*this); } inline TimeVal TimeVal::operator+(const TimeVal& other) const { TimeVal tmp_tv(*this); return tmp_tv += other; } inline const TimeVal& TimeVal::operator-=(const TimeVal& delta) { _sec -= delta.sec(); if (_usec < delta.usec()) { // Compensate _sec--; _usec += ONE_MILLION; } _usec -= delta.usec(); return (*this); } inline TimeVal TimeVal::operator-(const TimeVal& other) const { TimeVal tmp_tv(*this); return tmp_tv -= other; } inline TimeVal TimeVal::operator*(int n) const { uint32_t tmp_sec, tmp_usec; tmp_usec = _usec * n; tmp_sec = _sec * n + tmp_usec / ONE_MILLION; tmp_usec %= ONE_MILLION; return TimeVal(tmp_sec, tmp_usec); } inline TimeVal TimeVal::operator*(unsigned int n) const { return (*this)*(static_cast(n)); } inline TimeVal TimeVal::operator*(const double& d) const { return TimeVal(get_double() * d); } inline TimeVal TimeVal::operator/(int n) const { return TimeVal(_sec / n, ((_sec % n) * ONE_MILLION + _usec) / n); } inline TimeVal TimeVal::operator/(unsigned int n) const { return (*this)/(static_cast(n)); } inline TimeVal TimeVal::operator/(const double& d) const { return TimeVal(get_double() / d); } inline TimeVal TimeVal::ZERO() { return TimeVal(0, 0); } inline TimeVal TimeVal::MAXIMUM() { return TimeVal(0x7fffffff, ONE_MILLION - 1); } inline TimeVal TimeVal::MINIMUM() { return TimeVal(- 0x7fffffff - 1, - (ONE_MILLION - 1)); } /** * Prefix unary minus. */ inline TimeVal operator-(const TimeVal& v) { return TimeVal(-v.sec(), -v.usec()); } /** * Multiply TimeVal by integer. */ inline TimeVal operator*(int n, const TimeVal& t) { return t * n; } /** * Multiply TimeVal by double. */ inline TimeVal operator*(const double& d, const TimeVal& t) { return t * d; } /** * Generate a TimeVal value from a uniform random distribution between * specified bounds. * @param lower lower bound of generated value. * @param upper upper bound of generated value. * @return value chosen from uniform random distribution. */ inline TimeVal random_uniform(const TimeVal& lower, const TimeVal& upper) { double d = (upper - lower).get_double(); d *= double(xorp_random()) / double(XORP_RANDOM_MAX); return lower + TimeVal(d); } /** * Generate a TimeVal value from a uniform random distribution between * zero and specified bound. * @param upper upper bound of generated value. * @return value chosen from uniform random distribution. */ inline TimeVal random_uniform(const TimeVal& upper) { double d = upper.get_double(); d *= double(xorp_random()) / double(XORP_RANDOM_MAX); return TimeVal(d); } /** * Generate a TimeVal value from a uniform random distribution between * the bounds center - factor * center and center + factor * center. * If the lower bound is less than TimeVal::ZERO() it is rounded up to * TimeVal::ZERO(). * * @param center mid-point of generated time value. * @param factor the spread of the uniform random distribution. * @return value chosen from uniform random distribution. */ inline TimeVal random_uniform(const TimeVal& center, const double& factor) { TimeVal l = max(center - center * factor, TimeVal::ZERO()); TimeVal u = center + center * factor; return random_uniform(l, u); } #endif // __LIBXORP_TIMEVAL_HH__ xorp/libxorp/utility.h0000664000076400007640000001377011540224230015214 0ustar greearbgreearb/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- */ /* vim:set sts=4 ts=8: */ /* * Copyright (c) 2001-2011 XORP, Inc and Others * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License, Version * 2.1, June 1999 as published by the Free Software Foundation. * Redistribution and/or modification of this program under the terms of * any other version of the GNU Lesser General Public License is not * permitted. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, * see the GNU Lesser General Public License, Version 2.1, a copy of * which can be found in the XORP LICENSE.lgpl file. * * XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; * http://xorp.net */ #ifndef __LIBXORP_UTILITY_H__ #define __LIBXORP_UTILITY_H__ /* * Compile time assertion. */ #ifndef x_static_assert // +0 is to work around clang bug. #define x_static_assert(a) switch ((a) + 0) case 0: case ((a) + 0): #endif /* x_static_assert */ /* * A macro to avoid compilation warnings about unused functions arguments. * XXX: this should be used only in C. In C++ just remove the argument name * in the function definition. */ #ifndef UNUSED #define UNUSED(var) x_static_assert(sizeof(var) != 0) #endif /* UNUSED */ #ifdef __cplusplus #define cstring(s) (s).str().c_str() #endif /* * XORP code uses typedefs (e.g. uint32_t, int32_t) rather than using * the base types, because the 'C' language allows a compiler to use a * natural size for base type. The XORP code is internally consistent * in this usage, one problem arises with format strings in printf * style functions. * * In order to print a size_t or uint32_t with "%u" it is necessary to * cast to an unsigned int. On Mac OS X a size_t is not an unsigned * int. On windows uint32_t is not an unsigned int. * * In order to print a int32_t with a "%d" it is necessary to cast to * a signed int. On windows int32_t is not a signed int. * * The two convenience macros are provided to perform the cast. */ #ifdef __cplusplus #define XORP_UINT_CAST(x) static_cast(x) #define XORP_INT_CAST(x) static_cast(x) #else #define XORP_UINT_CAST(x) (unsigned int)(x) #define XORP_INT_CAST(x) (int)(x) #endif /* * On some architectures casting a "(struct sockaddr *)" pointer to * "(struct sockaddr_in *)" or "(struct sockaddr_in6 *)" pointer generates * a warning like: * warning: cast from 'sockaddr*' to 'sockaddr_in*' increases required * alignment of target type * * In general such casting shouldn't create any alignment issues and * shouldn't generate such warning. * To get around the problem we use the help of a "void" pointer. * * If the casting actually creates an alignment problem, then we need * to copy the "struct sockaddr" content to "struct sockaddr_in" or * "struct sockaddr_in6" placeholder. * Doing this (without using local static storage) might requite changing * the semantics hence we don't provide the implementation. */ #ifdef __cplusplus inline const struct sockaddr_in * sockaddr2sockaddr_in(const struct sockaddr* sa) { const void* v = sa; return (reinterpret_cast(v)); } inline struct sockaddr_in * sockaddr2sockaddr_in(struct sockaddr* sa) { void* v = sa; return (reinterpret_cast(v)); } inline const struct sockaddr_in6 * sockaddr2sockaddr_in6(const struct sockaddr* sa) { const void* v = sa; return (reinterpret_cast(v)); } inline struct sockaddr_in6 * sockaddr2sockaddr_in6(struct sockaddr* sa) { void* v = sa; return (reinterpret_cast(v)); } inline const struct sockaddr * sockaddr_storage2sockaddr(const struct sockaddr_storage* ss) { const void* v = ss; return (reinterpret_cast(v)); } inline struct sockaddr * sockaddr_storage2sockaddr(struct sockaddr_storage* ss) { void* v = ss; return (reinterpret_cast(v)); } #endif /* __cplusplus */ #define ADD_POINTER(pointer, size, type) \ ((type)(void *)(((uint8_t *)(pointer)) + (size))) /* * Micro-optimization: byte ordering fix for constants. htonl uses * CPU instructions, whereas the macro below can be handled by the * compiler front-end for literal values. */ #if __BYTE_ORDER == __BIG_ENDIAN # define htonl_literal(x) (x) #elif __BYTE_ORDER == __LITTLE_ENDIAN # define htonl_literal(x) \ ((((x) & 0x000000ffU) << 24) | (((x) & 0x0000ff00U) << 8) | \ (((x) & 0x00ff0000U) >> 8) | (((x) & 0xff000000U) >> 24)) #else # error "Endian detection is broken." #endif /* * Various ctype(3) wrappers that work properly even if the value of the int * argument is not representable as an unsigned char and doesn't have the * value of EOF. */ #ifdef __cplusplus extern "C" { #endif extern int xorp_isalnum(int c); extern int xorp_isalpha(int c); /* * TODO: for now comment-out xorp_isblank(), because isblank(3) is introduced * with ISO C99, and may not always be available on the system. */ /* extern int xorp_isblank(int c); */ extern int xorp_iscntrl(int c); extern int xorp_isdigit(int c); extern int xorp_isgraph(int c); extern int xorp_islower(int c); extern int xorp_isprint(int c); extern int xorp_ispunct(int c); extern int xorp_isspace(int c); extern int xorp_isupper(int c); extern int xorp_isxdigit(int c); extern int xorp_tolower(int c); extern int xorp_toupper(int c); /* * A strptime(3) wrapper that uses a local implementation of strptime(3) * if the operating system doesn't have one. Note that the particular * implementation is inside file "libxorp/strptime.c". */ extern char *xorp_strptime(const char *buf, const char *fmt, struct tm *tm); /* * Function to return C-string representation of a boolean: "true" of "false". */ extern const char *bool_c_str(int v); #ifdef __cplusplus } #endif #endif /* __LIBXORP_UTILITY_H__ */ xorp/libxorp/profile.cc0000664000076400007640000002044611540224227015313 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "libxorp_module.h" #include "xorp.h" #include "libxorp/timeval.hh" #include "libxorp/timer.hh" #include "xlog.h" #include "debug.h" #include "profile.hh" Profile::Profile() : _profile_cnt(0) { } Profile::~Profile() { while (!_profiles.empty()) { profiles::iterator i = _profiles.begin(); i->second->zap(); _profiles.erase(i); } } void Profile::create(const string& pname, const string& comment) throw(PVariableExists) { // Catch initialization problems. #ifndef XORP_USE_USTL if (_profiles.count(pname)) xorp_throw(PVariableExists, pname.c_str()); #endif ProfileState *p = new ProfileState(comment, false, false, new logentries); _profiles[pname] = ref_ptr(p); } void Profile::log(const string& pname, string comment) throw(PVariableUnknown,PVariableNotEnabled) { profiles::iterator i = _profiles.find(pname); // Catch any mispelt pnames. if (i == _profiles.end()) xorp_throw(PVariableUnknown, pname.c_str()); // In order to be logging, we must be enabled. if (!i->second->enabled()) xorp_throw(PVariableNotEnabled, pname.c_str()); #if 0 // Make sure that this variable is not locked. if (!i->second->locked()) xorp_throw(PVariableLocked, pname.c_str()); #endif TimeVal tv; TimerList::system_gettimeofday(&tv); i->second->logptr()->push_back(ProfileLogEntry(tv, comment)); } void Profile::enable(const string& pname) throw(PVariableUnknown,PVariableLocked) { profiles::iterator i = _profiles.find(pname); // Catch any mispelt pnames. if (i == _profiles.end()) xorp_throw(PVariableUnknown, pname.c_str()); // If this profile name is already enabled, get out of here // without updating the counter. if (i->second->enabled()) return; // Don't allow a locked entry to be enabled. if (i->second->locked()) xorp_throw(PVariableLocked, pname.c_str()); i->second->set_enabled(true); _profile_cnt++; } void Profile::disable(const string& pname) throw(PVariableUnknown) { profiles::iterator i = _profiles.find(pname); // Catch any mispelt pnames. if (i == _profiles.end()) xorp_throw(PVariableUnknown, pname.c_str()); // If this profile name is already disabled, get out of here // without updating the counter. if (!i->second->enabled()) return; i->second->set_enabled(false); _profile_cnt--; } void Profile::lock_log(const string& pname) throw(PVariableUnknown,PVariableLocked) { profiles::iterator i = _profiles.find(pname); // Catch any mispelt pnames. if (i == _profiles.end()) xorp_throw(PVariableUnknown, pname.c_str()); // Don't allow a locked entry to be locked again. if (i->second->locked()) xorp_throw(PVariableLocked, pname.c_str()); // Disable logging. disable(pname); // Lock the entry i->second->set_locked(true); i->second->set_iterator(i->second->logptr()->begin()); } bool Profile::read_log(const string& pname, ProfileLogEntry& entry) throw(PVariableUnknown,PVariableNotLocked) { profiles::iterator i = _profiles.find(pname); // Catch any mispelt pnames. if (i == _profiles.end()) xorp_throw(PVariableUnknown, pname.c_str()); // Verify that the log entry is locked if (!i->second->locked()) xorp_throw(PVariableNotLocked, pname.c_str()); logentries::iterator li; i->second->get_iterator(li); if (li == i->second->logptr()->end()) return false; entry = *li; i->second->set_iterator(++li); return true; } void Profile::release_log(const string& pname) throw(PVariableUnknown,PVariableNotLocked) { profiles::iterator i = _profiles.find(pname); // Catch any mispelt pnames. if (i == _profiles.end()) xorp_throw(PVariableUnknown, pname.c_str()); // Verify that the log entry is locked if (!i->second->locked()) xorp_throw(PVariableNotLocked, pname.c_str()); // Unlock the entry i->second->set_locked(false); } void Profile::clear(const string& pname) throw(PVariableUnknown,PVariableLocked) { profiles::iterator i = _profiles.find(pname); // Catch any mispelt pnames. if (i == _profiles.end()) xorp_throw(PVariableUnknown, pname.c_str()); // Don't allow a locked entry to be cleared. if (i->second->locked()) xorp_throw(PVariableLocked, pname.c_str()); i->second->logptr()->clear(); } #if 0 class List: public unary_function >, void> { public: void operator()(const pair >& p) { _result += p.first; _result += "\t"; _result += c_format("%d", p.second->size()); _result += "\t"; _result += p.second->enabled() ? "enabled" : "disabled"; _result += "\t"; _result += p.second->comment(); _result += "\n"; } string result() const { return _result; } private: string _result; }; #endif string Profile::get_list() const { ostringstream oss; profiles::const_iterator i = _profiles.begin(); while (i != _profiles.end()) { oss << i->first << "\t" << i->second->size() << "\t" << (i->second->enabled() ? "enabled" : "disabled") << "\t" << i->second->comment() << "\n"; i++; } return oss.str(); } // simple profiler SP::SAMPLE SP::sampler_time() { TimeVal tv; TimerList::system_gettimeofday(&tv); SAMPLE ret = tv.sec(); ret *= (SAMPLE) 1000000; ret += (SAMPLE) tv.usec(); return ret; } #ifdef __HAVE_TSC__ // XXX watch out on SMP systems - make sure u're always reading the same tsc // (i.e., same core running the process - set affinity. On Linux use taskset). // Or disable smp. -sorbo SP::SAMPLE SP::sampler_tsc(void) { uint64_t tsc; __asm__ volatile (".byte 0x0f, 0x31" : "=A" (tsc)); return tsc; } #endif // __HAVE_TSC__ namespace SP { SAMPLE _samples[SP_MAX_SAMPLES]; const char* _desc[SP_MAX_SAMPLES]; unsigned _samplec; #ifdef __HAVE_TSC__ SAMPLER _sampler = SP::sampler_tsc; #else SAMPLER _sampler = SP::sampler_time; #endif // __HAVE_TSC__ } void SP::set_sampler(SAMPLER sampler) { _sampler = sampler; } void SP::add_sample(const char* desc) { if (!_sampler) return; XLOG_ASSERT(_samplec < SP_MAX_SAMPLES); _samples[_samplec] = _sampler(); _desc[_samplec] = desc; _samplec++; } void SP::print_samples() { if (!_samplec) return; double total = _samples[_samplec - 1] - _samples[0]; printf("\n"); printf("Absolute time\tElapsed time\tPercentage\tDescription\n"); for (unsigned i = 0; i < _samplec; i++) { #ifdef HOST_OS_WINDOWS printf("%I64u\t", (long long unsigned) _samples[i]); #else printf("%llu\t", (long long unsigned) _samples[i]); #endif if (i != 0) { SAMPLE a, b, diff; a = _samples[i - 1]; b = _samples[i]; XLOG_ASSERT(a <= b); diff = b - a; #ifdef HOST_OS_WINDOWS printf("%12I64u\t%10.2f\t", (long long unsigned) diff, (double) diff / total * 100.0); #else printf("%12llu\t%10.2f\t", (long long unsigned) diff, (double) diff / total * 100.0); #endif } else printf("\t\t\t\t"); printf("%s\n", _desc[i]); } #ifdef HOST_OS_WINDOWS printf("Total %I64u\n", (long long unsigned) total); #else printf("Total %llu\n", (long long unsigned) total); #endif printf("\n"); _samplec = 0; } SP::SAMPLE SP::sample() { if (_sampler) return _sampler(); return 0; } xorp/libxorp/TODO0000664000076400007640000000240411421137511014023 0ustar greearbgreearb# # $XORP: xorp/libxorp/TODO,v 1.6 2006/03/08 01:09:07 pavlin Exp $ # * Fix libxorp/test_ipvx and libxorp/test_ipvxnet for Linux RedHat-7.2 (kernel 2.4.18), i.e. xorp4. Currently, they coredump in some weird way. * Merge test_ipnet with test_ipv{4,6,x}net (or remove it if the new test programs cover all tests in test_ipnet). * Remove/disable IPvX::copy_out(struct sockaddr_in&) for IPv6 addresses. Similarly, remove/disable IPvX::copy_out(struct sockaddr_in6&) for IPv4 addresses. Do the same thing for the corresponding copy_in(). (OK???) * Rename ConfigParam::_initial_value and get_initial_value() to default_value. * Rename Trie to SomethingMoreAppropriate(TM) and rename Trie::route() to SomethingMoreAppropriate::payload() * Rename Transaction to SomethingBatchLike(TM). * Rename IPvX *Vif::addr_ptr() to IPvX& Vif::vif_addr() instead? * All constants such as IP address size, should be static class constants. * Need to do something about multiple EventLoop instances (breaks XORP IPC). Some alternatives: - We have an event loop instance count that stops multiple instances. - EventLoop::run() becomes a static member that runs all the event loops. - Change the way IPC learns about the event loop. * Do kdoc-ify ref_trie.hh and trie.hh xorp/libxorp/ipv4.cc0000664000076400007640000002265711421137511014540 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "libxorp/xorp.h" #ifdef HAVE_SYS_SOCKET_H #include #endif #ifdef HAVE_NETINET_IN_H #include #endif #ifdef HAVE_ARPA_INET_H #include #endif #include "ipv4.hh" IPv4::IPv4(const uint8_t *from_uint8) { memcpy(&_addr, from_uint8, sizeof(_addr)); } IPv4::IPv4(const in_addr& from_in_addr) { _addr = from_in_addr.s_addr; } IPv4::IPv4(const sockaddr& sa) throw (InvalidFamily) { if (sa.sa_family != AF_INET) xorp_throw(InvalidFamily, sa.sa_family); const sockaddr_in* sin = sockaddr2sockaddr_in(&sa); _addr = sin->sin_addr.s_addr; } IPv4::IPv4(const sockaddr_storage& ss) throw (InvalidFamily) { if (ss.ss_family != AF_INET) xorp_throw(InvalidFamily, ss.ss_family); const sockaddr* sa = sockaddr_storage2sockaddr(&ss); const sockaddr_in* sin = sockaddr2sockaddr_in(sa); _addr = sin->sin_addr.s_addr; } IPv4::IPv4(const sockaddr_in& sin) throw(InvalidFamily) { if (sin.sin_family != AF_INET) xorp_throw(InvalidFamily, sin.sin_family); _addr = sin.sin_addr.s_addr; } IPv4::IPv4(const char *from_cstring) throw (InvalidString) { if (from_cstring == NULL) xorp_throw(InvalidString, "Null value" ); if (inet_pton(AF_INET, from_cstring, &_addr) <= 0) xorp_throw(InvalidString, c_format("Bad IPv4 \"%s\"", from_cstring)); } /** * Copy the raw address to memory pointed by @to. * @return the number of copied octets. */ size_t IPv4::copy_out(uint8_t *to_uint8) const { memcpy(to_uint8, &_addr, addr_bytelen()); return addr_bytelen(); } /** * Copy the raw address to @to_in_addr. * @return the number of copied octets. */ size_t IPv4::copy_out(in_addr& to_in_addr) const { return (copy_out((uint8_t *)&to_in_addr)); } /** * Copy the raw address to @to_sockaddr, and assign appropriately * the rest of the fields in @to_sockaddr. * @return the number of copied octets. */ size_t IPv4::copy_out(struct sockaddr& to_sockaddr) const { return (copy_out(*sockaddr2sockaddr_in(&to_sockaddr))); } /** * Copy the raw address to @to_sockaddr_storage, and assign appropriately * the rest of the fields in @to_sockaddr_storage. * @return the number of copied octets. */ size_t IPv4::copy_out(struct sockaddr_storage& to_sockaddr_storage) const { return (copy_out(*sockaddr_storage2sockaddr(&to_sockaddr_storage))); } /** * Copy the raw address to @to_sockaddr_in, and assign appropriately * the rest of the fields in @to_sockaddr_in. * @return the number of copied octets. */ size_t IPv4::copy_out(struct sockaddr_in& to_sockaddr_in) const { memset(&to_sockaddr_in, 0, sizeof(to_sockaddr_in)); #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN to_sockaddr_in.sin_len = sizeof(to_sockaddr_in); #endif to_sockaddr_in.sin_family = AF_INET; to_sockaddr_in.sin_port = 0; // XXX: not used return (copy_out(to_sockaddr_in.sin_addr)); } /** * Copy a raw address from the memory pointed by @from_uint8. * @return the number of copied octets. */ size_t IPv4::copy_in(const uint8_t *from_uint8) { memcpy(&_addr, from_uint8, addr_bytelen()); return (addr_bytelen()); } /** * Copy a raw address of family %AF_INET from @from_in_addr. * @return the number of copied octets. */ size_t IPv4::copy_in(const in_addr& from_in_addr) { return (copy_in(reinterpret_cast(&from_in_addr))); } /** * Copy a raw address from @from_sockaddr. * @return the number of copied octets. */ size_t IPv4::copy_in(const sockaddr& from_sockaddr) throw (InvalidFamily) { return (copy_in(*sockaddr2sockaddr_in(&from_sockaddr))); } /** * Copy a raw address from @from_sockaddr_storage. * @return the number of copied octets. */ size_t IPv4::copy_in(const sockaddr_storage& from_sockaddr_storage) throw (InvalidFamily) { return (copy_in(*sockaddr_storage2sockaddr(&from_sockaddr_storage))); } /** * Copy a raw address from @from_sockaddr_in. * @return the number of copied octets. */ size_t IPv4::copy_in(const sockaddr_in& from_sockaddr_in) throw (InvalidFamily) { if (from_sockaddr_in.sin_family != AF_INET) xorp_throw(InvalidFamily, from_sockaddr_in.sin_family); return (copy_in(from_sockaddr_in.sin_addr)); } IPv4 IPv4::operator<<(uint32_t left_shift) const { if (left_shift >= 32) { // Clear all bits. // XXX: special case, because in C the behavior is undefined. return (IPv4::ZERO()); } uint32_t tmp_addr = ntohl(_addr) << left_shift; return IPv4(htonl(tmp_addr)); } IPv4 IPv4::operator>>(uint32_t right_shift) const { if (right_shift >= 32) { // Clear all bits. // XXX: special case, because in C the behavior is undefined. return IPv4::ZERO(); } uint32_t tmp_addr = ntohl(_addr) >> right_shift; return IPv4(htonl(tmp_addr)); } bool IPv4::operator<(const IPv4& other) const { return ntohl(_addr) < ntohl(other._addr); } IPv4 IPv4::make_prefix(uint32_t mask_len) throw (InvalidNetmaskLength) { if (mask_len > 32) xorp_throw(InvalidNetmaskLength, mask_len); uint32_t m = (mask_len == 0) ? 0 : ((~0U) << (32 - mask_len)); return IPv4(htonl(m)); } uint32_t IPv4::mask_len() const { uint32_t ctr = 0; uint32_t shift = ntohl(_addr); for (int i = 0; i < 32; i++) { if ((shift & 0x80000000U) != 0) { ctr++; shift = shift << 1; } else { return ctr; } } return ctr; } IPv4& IPv4::operator--() { uint32_t tmp_addr = ntohl(_addr) - 1; _addr = htonl(tmp_addr); return *this; } IPv4& IPv4::operator++() { uint32_t tmp_addr = ntohl(_addr) + 1; _addr = htonl(tmp_addr); return *this; } string IPv4::str() const { struct in_addr in; in.s_addr = _addr; return (inet_ntoa(in)); // XXX: implicitly create string return object } bool IPv4::is_unicast() const { uint32_t addr4 = ntohl(_addr); return (! (IN_MULTICAST(addr4) || IN_BADCLASS(addr4) || (addr4 == 0))); } bool IPv4::is_multicast() const { uint32_t addr4 = ntohl(_addr); return (IN_MULTICAST(addr4)); } bool IPv4::is_class_a() const { uint32_t addr4 = ntohl(_addr); return (IN_CLASSA(addr4)); } bool IPv4::is_class_b() const { uint32_t addr4 = ntohl(_addr); return (IN_CLASSB(addr4)); } bool IPv4::is_class_c() const { uint32_t addr4 = ntohl(_addr); return (IN_CLASSC(addr4)); } bool IPv4::is_experimental() const { uint32_t addr4 = ntohl(_addr); // // XXX: We use IN_BADCLASS() instead of IN_EXPERIMENTAL(), because // the definition of IN_EXPERIMENTAL() is broken in Linux's // (it covers all addresses that start with 0xe0000000, which includes // multicast as well). // return (IN_BADCLASS(addr4)); } // XXX: in IPv4 there is no link-local unicast scope, therefore // the return value is always false. bool IPv4::is_linklocal_unicast() const { return (false); } // XXX: in IPv4 there is no interface-local multicast scope, therefore // the return value is always false. bool IPv4::is_interfacelocal_multicast() const { return (false); } bool IPv4::is_linklocal_multicast() const { uint32_t addr4 = ntohl(_addr); return (IN_MULTICAST(addr4) && (addr4 <= INADDR_MAX_LOCAL_GROUP)); } bool IPv4::is_loopback() const { static const uint32_t loopnet = IN_LOOPBACKNET << 24; uint32_t addr4 = ntohl(_addr); return ((addr4 & IN_CLASSA_NET) == loopnet); } const string& IPv4::ip_version_str() { static const string IP_VERSION_STR("IPv4"); return IP_VERSION_STR; } const IPv4 IPv4Constants::zero(IPv4(static_cast(0x0U))); const IPv4 IPv4Constants::any(IPv4Constants::zero); const IPv4 IPv4Constants::all_ones(IPv4(0xffffffffU)); const IPv4 IPv4Constants::loopback(IPv4(htonl_literal(0x7f000001U))); const IPv4 IPv4Constants::multicast_base(IPv4(htonl_literal(0xe0000000U))); const IPv4 IPv4Constants::multicast_all_systems(IPv4(htonl_literal(0xe0000001U))); const IPv4 IPv4Constants::multicast_all_routers(IPv4(htonl_literal(0xe0000002U))); const IPv4 IPv4Constants::dvmrp_routers(IPv4(htonl_literal(0xe0000004U))); const IPv4 IPv4Constants::ospfigp_routers(IPv4(htonl_literal(0xe0000005U))); const IPv4 IPv4Constants::ospfigp_designated_routers(IPv4(htonl_literal(0xe0000006U))); const IPv4 IPv4Constants::rip2_routers(IPv4(htonl_literal(0xe0000009U))); const IPv4 IPv4Constants::pim_routers(IPv4(htonl_literal(0xe000000dU))); const IPv4 IPv4Constants::ssm_routers(IPv4(htonl_literal(0xe0000016U))); const IPv4 IPv4Constants::class_a_base(IPv4(htonl_literal(0x00000000U))); const IPv4 IPv4Constants::class_b_base(IPv4(htonl_literal(0x80000000U))); const IPv4 IPv4Constants::class_c_base(IPv4(htonl_literal(0xc0000000U))); const IPv4 IPv4Constants::experimental_base(IPv4(htonl_literal(0xf0000000U))); xorp/libxorp/mac.cc0000664000076400007640000001123711421137511014406 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "libxorp/xorp.h" #include "libxorp/ether_compat.h" #include "mac.hh" Mac::Mac() { memset(_addr, 0, sizeof(_addr)); } Mac::Mac(const uint8_t* from_uint8) { copy_in(from_uint8); } Mac::Mac(const char* from_cstring) throw (InvalidString) { copy_in(from_cstring); } Mac::Mac(const struct ether_addr& from_ether_addr) { copy_in(from_ether_addr); } Mac::Mac(const struct sockaddr& from_sockaddr) { copy_in(from_sockaddr); } size_t Mac::copy_out(uint8_t* to_uint8) const { memcpy(to_uint8, _addr, sizeof(_addr)); return (sizeof(_addr)); } size_t Mac::copy_out(struct ether_addr& to_ether_addr) const { memcpy(&to_ether_addr, _addr, sizeof(_addr)); return (sizeof(_addr)); } size_t Mac::copy_out(struct sockaddr& to_sockaddr) const { memset(&to_sockaddr, 0, sizeof(to_sockaddr)); #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN to_sockaddr.sa_len = sizeof(to_sockaddr); #endif #ifdef AF_LINK to_sockaddr.sa_family = AF_LINK; #else to_sockaddr.sa_family = AF_UNSPEC; #endif uint8_t* sa_data = reinterpret_cast(to_sockaddr.sa_data); return (copy_out(sa_data)); } size_t Mac::copy_in(const uint8_t* from_uint8) { memcpy(_addr, from_uint8, sizeof(_addr)); return (sizeof(_addr)); } size_t Mac::copy_in(const struct ether_addr& from_ether_addr) { memcpy(_addr, &from_ether_addr, sizeof(_addr)); return (sizeof(_addr)); } size_t Mac::copy_in(const struct sockaddr& from_sockaddr) { const uint8_t* sa_data = reinterpret_cast(from_sockaddr.sa_data); return (copy_in(sa_data)); } size_t Mac::copy_in(const char* from_cstring) throw (InvalidString) { const struct ether_addr* eap; if (from_cstring == NULL) xorp_throw(InvalidString, "Null value"); #ifdef HAVE_ETHER_ATON_R struct ether_addr ea; if (ether_aton_r(from_cstring, &ea) == NULL) xorp_throw(InvalidString, c_format("Bad Mac \"%s\"", from_cstring)); eap = &ea; #else // ! HAVE_ETHER_ATON_R // // XXX: We need to const_cast the ether_aton() argument, because // on some OS (e.g., MacOS X 10.2.3 ?) the ether_aton(3) declaration // is broken (missing "const" in the argument). // eap = ether_aton(const_cast(from_cstring)); if (eap == NULL) xorp_throw(InvalidString, c_format("Bad Mac \"%s\"", from_cstring)); #endif // ! HAVE_ETHER_ATON_R return (copy_in(*eap)); } bool Mac::operator<(const Mac& other) const { size_t i; for (i = 0; i < (sizeof(_addr) - 1); i++) { // XXX: Loop ends intentionally one octet earlier if (_addr[i] != other._addr[i]) break; } return (_addr[i] < other._addr[i]); } bool Mac::operator==(const Mac& other) const { return (memcmp(_addr, other._addr, sizeof(_addr)) == 0); } bool Mac::operator!=(const Mac& other) const { return (memcmp(_addr, other._addr, sizeof(_addr)) != 0); } string Mac::str() const { struct ether_addr ea; copy_out(ea); #ifdef HAVE_ETHER_NTOA_R char str_buffer[sizeof "ff:ff:ff:ff:ff:ff"]; ether_ntoa_r(&ea, str_buffer); return (str_buffer); // XXX: implicitly create string return object #else // ! HAVE_ETHER_NTOA_R return (ether_ntoa(&ea)); // XXX: implicitly create string return object #endif // ! HAVE_ETHER_NTOA_R } bool Mac::is_unicast() const { return (! is_multicast()); } bool Mac::is_multicast() const { return (_addr[0] & MULTICAST_BIT); } const Mac MacConstants::zero(Mac("00:00:00:00:00:00")); const Mac MacConstants::all_ones(Mac("ff:ff:ff:ff:ff:ff")); const Mac MacConstants::broadcast(Mac("ff:ff:ff:ff:ff:ff")); const Mac MacConstants::stp_multicast(Mac("01:80:c2:00:00:00")); const Mac MacConstants::lldp_multicast(Mac("01:80:c2:00:00:0e")); const Mac MacConstants::gmrp_multicast(Mac("01:80:c2:00:00:20")); const Mac MacConstants::gvrp_multicast(Mac("01:80:c2:00:00:21")); xorp/libxorp/buffered_asyncio.cc0000664000076400007640000001646411630245313017166 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "libxorp_module.h" #include "xorp.h" #include "debug.h" #include "xlog.h" #include "buffered_asyncio.hh" extern bool is_pseudo_error(const char* name, XorpFd fd, int error_num); BufferedAsyncReader::BufferedAsyncReader(EventLoop& e, XorpFd fd, size_t reserve_bytes, const Callback& cb, int priority) : _eventloop(e), _fd(fd), _cb(cb), _buffer(reserve_bytes), _last_error(0), _priority(priority) { _config.head = &_buffer[0]; _config.head_bytes = 0; _config.trigger_bytes = 1; _config.reserve_bytes = reserve_bytes; } BufferedAsyncReader::~BufferedAsyncReader() { stop(); } string BufferedAsyncReader::toString() const { ostringstream oss; oss << "head_bytes: " << _config.head_bytes << " trigger-bytes: " << _config.trigger_bytes << " reserve-bytes: " << _config.reserve_bytes << " fd: " << _fd.str() << " last_error: " << _last_error << " priority: " << _priority << endl; return oss.str(); } inline void BufferedAsyncReader::provision_trigger_bytes() { size_t post_head_bytes = _buffer.size() - (_config.head - &_buffer[0]); if (_config.head + _config.head_bytes == &_buffer[0] + _buffer.size() || _config.trigger_bytes >= post_head_bytes || post_head_bytes < _buffer.size() / 2) { memmove(&_buffer[0], _config.head, _config.head_bytes); _config.head = &_buffer[0]; } } bool BufferedAsyncReader::set_trigger_bytes(size_t bytes) { if (bytes > _config.reserve_bytes) return false; _config.trigger_bytes = bytes; provision_trigger_bytes(); return true; } size_t BufferedAsyncReader::trigger_bytes() const { return _config.trigger_bytes; } bool BufferedAsyncReader::dispose(size_t bytes) { if (_config.head_bytes < bytes) return false; _config.head += bytes; _config.head_bytes -= bytes; return true; } bool BufferedAsyncReader::set_reserve_bytes(size_t bytes) { if (_config.reserve_bytes > bytes) return false; size_t head_off = _config.head - &_buffer[0]; _buffer.resize(bytes); _config.head = &_buffer[0] + head_off; _config.reserve_bytes = bytes; return true; } size_t BufferedAsyncReader::reserve_bytes() const { return _config.reserve_bytes; } size_t BufferedAsyncReader::available_bytes() const { return _config.head_bytes; } void BufferedAsyncReader::start() { if (_eventloop.add_ioevent_cb(_fd, IOT_READ, callback(this, &BufferedAsyncReader::io_event), _priority) == false) { XLOG_ERROR("BufferedAsyncReader: failed to add I/O event callback."); } #ifdef HOST_OS_WINDOWS if (_eventloop.add_ioevent_cb(_fd, IOT_DISCONNECT, callback(this, &BufferedAsyncReader::io_event), _priority) == false) { XLOG_ERROR("BufferedAsyncReader: failed to add I/O event callback."); } #endif if (_config.head_bytes >= _config.trigger_bytes) { _ready_timer = _eventloop.new_oneoff_after_ms(0, callback(this, &BufferedAsyncReader::announce_event, DATA)); } debug_msg("%p start\n", this); } void BufferedAsyncReader::stop() { debug_msg("%p stop\n", this); #ifdef HOST_OS_WINDOWS _eventloop.remove_ioevent_cb(_fd, IOT_DISCONNECT); #endif _eventloop.remove_ioevent_cb(_fd, IOT_READ); _ready_timer.unschedule(); } void BufferedAsyncReader::io_event(XorpFd fd, IoEventType type) { assert(fd == _fd); #ifndef HOST_OS_WINDOWS assert(type == IOT_READ); #else // Explicitly handle disconnection events if (type == IOT_DISCONNECT) { XLOG_ASSERT(fd.is_socket()); stop(); announce_event(END_OF_FILE); return; } #endif uint8_t* tail = _config.head + _config.head_bytes; size_t tail_bytes = _buffer.size() - (tail - &_buffer[0]); assert(tail_bytes >= 1); assert(tail + tail_bytes == &_buffer[0] + _buffer.size()); ssize_t read_bytes = -1; #ifdef HOST_OS_WINDOWS if (fd.is_socket()) { read_bytes = ::recvfrom(fd.getSocket(), (char *)tail, tail_bytes, 0, NULL, 0); _last_error = WSAGetLastError(); WSASetLastError(ERROR_SUCCESS); } else { (void)ReadFile(fd, (LPVOID)tail, (DWORD)tail_bytes, (LPDWORD)&read_bytes, NULL); _last_error = GetLastError(); SetLastError(ERROR_SUCCESS); } #else errno = 0; _last_error = 0; read_bytes = ::read(fd, tail, tail_bytes); if (read_bytes < 0) _last_error = errno; errno = 0; #endif if (read_bytes > 0) { _config.head_bytes += read_bytes; if (_config.head_bytes >= _config.trigger_bytes) { debug_msg("YES notify - buffered I/O %u / %u\n", XORP_UINT_CAST(_config.head_bytes), XORP_UINT_CAST(_config.trigger_bytes)); announce_event(DATA); } else { debug_msg("NO notify - buffered I/O %u / %u read %d\n", XORP_UINT_CAST(_config.head_bytes), XORP_UINT_CAST(_config.trigger_bytes), XORP_INT_CAST(read_bytes)); } } else if (read_bytes == 0) { announce_event(END_OF_FILE); } else { if (is_pseudo_error("BufferedAsyncReader", fd, _last_error)) return; XLOG_ERROR("read error %d", _last_error); stop(); announce_event(OS_ERROR); } } void BufferedAsyncReader::announce_event(Event ev) { if (ev == DATA && _config.head_bytes < _config.trigger_bytes) { // // We might get here because a read returns more data than a user // wants to process. They exit the callback with more data in their // buffer than the threshold event so we schedule a timer to // prod them again, but in the meantime an I/O event occurs // and pre-empts the timer callback. // Another example could be when a previous callback modifies // the threshold value. // Basically, we don't want to call the user below threshold. // debug_msg("announce_event: DATA (head_bytes = %u, trigger_bytes = %u)", XORP_UINT_CAST(_config.head_bytes), XORP_UINT_CAST(_config.trigger_bytes)); return; } // // Take a reference to callback and a copy of it's count. If it's // count falls between here and when the callback dispatch returns // the current instance has been deleted and we should return // without accessing any member state. // assert(_cb.is_only() == true); Callback cb = _cb; cb->dispatch(this, ev, _config.head, _config.head_bytes); if (cb.is_only() == true) return; // We've been deleted! Just leave provision_trigger_bytes(); if (_config.head_bytes >= _config.trigger_bytes) { _ready_timer = _eventloop.new_oneoff_after_ms(0, callback(this, &BufferedAsyncReader::announce_event, DATA)); } } xorp/libxorp/asyncio.hh0000664000076400007640000003164311631464534015343 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __LIBXORP_ASYNCIO_HH__ #define __LIBXORP_ASYNCIO_HH__ #include "libxorp/xorp.h" #ifdef HAVE_FCNTL_H #include #endif #include "libxorp/xorpfd.hh" #include "libxorp/callback.hh" #include "libxorp/eventloop.hh" #include "libxorp/ipvx.hh" struct iovec; // Asynchronous file transfer classes. These utilize XORP EventLoop // and the IoEvent framework to read / write files asynchronously. The // user creates and AsyncFile{Reader,Writer} and adds a buffer for // reading / writing with add_buffer(). A callback is provided with // each buffer is called every time I/O happens on the buffer. // // Note that in case of AsyncFileWriter the user can use add_data() to // add the data to write/send, and that data will be stored/buffered // internally by AsyncFileWriter itself. // // Reading/Writing only begins when start() is called, and normally // continues until there are no buffers left. // ---------------------------------------------------------------------------- // AsyncFileOperator - Abstract base class for asynchronous file operators. /** * @short Base class for asynchronous file transfer operations. * * Asynchronous file transfer operations allow data to be transferred * to or from user supplied buffers. A callback is invoked on each * transfer. Transfer stops when the available buffers are exhausted. */ class AsyncFileOperator { public: enum Event { DATA = 1, // I/O occured FLUSHING = 2, // Buffer is being flushed OS_ERROR = 4, // I/O Error has occurred, check error() END_OF_FILE = 8, // End of file reached (applies to read only) WOULDBLOCK = 16 // I/O would block the current thread }; /** * Callback type user provides when adding buffers to sub-classes * AsyncFileOperator. Callback's are on a per buffer basis and * invoked any time some I/O is performed. The offset field * refers to the offset of the last byte read, or written, from * the start of the buffer. * * Callback has arguments: * ErrorCode e, * uint8_t* buffer, * size_t buffer_bytes, * size_t offset */ typedef XorpCallback4::RefPtr Callback; public: /** * @return the number of buffers available. */ virtual size_t buffers_remaining() const = 0; /** * Stop asynchronous operation and clear list of buffers. */ virtual void flush_buffers() = 0; /** * Start asynchronous operation. * * @return true on success, false if no buffers are available. */ virtual bool start() = 0; /** * Stop asynchronous operation. */ virtual void stop() = 0; /** * Resume stopped asynchronous operation. * * @return true on success, false if no buffers are available. */ bool resume() { return start(); } /** * @return true if asynchronous I/O is started. */ bool running() const { return _running; } /** * @return file descriptor undergoing asynchronous operation. */ XorpFd fd() const { return _fd; } /** * @return the last error code returned by the underlying OS. */ int error() const { return _last_error; } virtual string toString() const; protected: AsyncFileOperator(EventLoop& e, XorpFd fd, int priority = XorpTask::PRIORITY_DEFAULT) : _eventloop(e), _fd(fd), _running(false), _last_error(0), _priority(priority) { #ifndef HOST_OS_WINDOWS int fl = fcntl(fd, F_GETFL); assert(fl & O_NONBLOCK); #endif } virtual ~AsyncFileOperator(); EventLoop& _eventloop; XorpFd _fd; bool _running; int _last_error; int _priority; }; /** * @short Read asynchronously from a file. */ class AsyncFileReader : public AsyncFileOperator { public: /** * @param e EventLoop that object should associate itself with. * @param fd a file descriptor to read from. */ AsyncFileReader(EventLoop& e, XorpFd fd, int priority = XorpTask::PRIORITY_DEFAULT); virtual ~AsyncFileReader(); /** * Add an additional buffer for reading to. * * Note that the buffer with the data is managed by the user. * * @param buffer pointer to buffer. * @param buffer_bytes size of buffer in bytes. * @param cb Callback object to invoke when I/O is performed. */ void add_buffer(uint8_t* buffer, size_t buffer_bytes, const Callback& cb); /** * Add an additional buffer for reading to. * * Note that the buffer with the data is managed by the user. * * @param buffer pointer to buffer. * @param buffer_bytes size of buffer in bytes. * @param offset starting point for read operation. * @param cb Callback object to invoke when I/O is performed. */ void add_buffer_with_offset(uint8_t* buffer, size_t buffer_bytes, size_t offset, const Callback& cb); /** * Start asynchronous operation. * * @return true on success, false if no buffers are available. */ bool start(); /** * Stop asynchronous operation. */ void stop(); /** * @return the number of buffers available. */ size_t buffers_remaining() const { return _buffers.size(); } /** * Stop asynchronous operation and clear list of buffers. */ void flush_buffers(); virtual string toString() const; protected: class BufferInfo : public NONCOPYABLE { public: BufferInfo(uint8_t* b, size_t bb, Callback cb) : _buffer(b), _buffer_bytes(bb), _offset(0), _cb(cb) {} BufferInfo(uint8_t* b, size_t bb, size_t off, Callback cb) : _buffer(b), _buffer_bytes(bb), _offset(off), _cb(cb) {} void dispatch_callback(AsyncFileOperator::Event e) { _cb->dispatch(e, _buffer, _buffer_bytes, _offset); } uint8_t* buffer() { return (_buffer); } size_t buffer_bytes() const { return (_buffer_bytes); } size_t offset() const { return (_offset); } void incr_offset(size_t done) { _offset += done; } private: BufferInfo(); // Not directly constructible uint8_t* _buffer; size_t _buffer_bytes; size_t _offset; Callback _cb; }; void read(XorpFd fd, IoEventType type); void complete_transfer(int err, ssize_t done); list _buffers; #ifdef HOST_OS_WINDOWS void disconnect(XorpFd fd, IoEventType type); XorpTask _deferred_io_task; bool _disconnect_added; #endif }; /** * @short Write asynchronously to non-blocking file. */ class AsyncFileWriter : public NONCOPYABLE, public AsyncFileOperator { public: /** * @param e EventLoop that object should associate itself with. * @param fd a file descriptor marked as non-blocking to write to. * @param coalesce the number of buffers to coalesce for each write() * system call. */ AsyncFileWriter(EventLoop& e, XorpFd fd, uint32_t coalesce = 1, int priority = XorpTask::PRIORITY_DEFAULT); virtual ~AsyncFileWriter(); /** * Add an additional buffer for writing from. * * Note that the buffer with the data is managed by the user. * * @param buffer pointer to buffer. * @param buffer_bytes size of buffer in bytes. * @param cb Callback object to invoke when I/O is performed. */ void add_buffer(const uint8_t* buffer, size_t buffer_bytes, const Callback& cb); /** * Add an additional buffer for writing from by using sendto(2). * * Note that sendto()-buffers are never coalesced with other buffers. * * @param buffer pointer to buffer. * @param buffer_bytes size of buffer in bytes. * @param dst_addr the destination address to send the data to. * @param dst_port the destination port (in host order) to send the * data to. * @param cb Callback object to invoke when I/O is performed. */ void add_buffer_sendto(const uint8_t* buffer, size_t buffer_bytes, const IPvX& dst_addr, uint16_t dst_port, const Callback& cb); /** * Add an additional buffer for writing from. * * @param buffer pointer to buffer. * @param buffer_bytes size of buffer in bytes. * @param offset the starting point to write from in the buffer. * @param cb Callback object to invoke when I/O is performed. */ void add_buffer_with_offset(const uint8_t* buffer, size_t buffer_bytes, size_t offset, const Callback& cb); /** * Add additional data for writing from. * * Note that the data is stored to write is stored internally by * AsyncFileWriter. * * @param data the data to write. * @param cb Callback object to invoke when I/O is performed. */ void add_data(const vector& data, const Callback& cb); /** * Add additional data for writing from by using sendto(2). * * Note that the data is stored to write is stored internally by * AsyncFileWriter. * Note that sendto()-buffers are never coalesced with other buffers. * * @param data the data to send. * @param dst_addr the destination address to send the data to. * @param dst_port the destination port (in host order) to send the * data to. * @param cb Callback object to invoke when I/O is performed. */ void add_data_sendto(const vector& data, const IPvX& dst_addr, uint16_t dst_port, const Callback& cb); /** * Start asynchronous operation. * * @return true on success, false if no buffers are available. */ bool start(); /** * Stop asynchronous operation. */ void stop(); /** * @return the number of buffers available. */ size_t buffers_remaining() const { return _buffers.size(); } /** * Stop asynchronous operation and clear list of buffers. */ void flush_buffers(); virtual string toString() const; private: AsyncFileWriter(); // Not directly constructible protected: class BufferInfo : public NONCOPYABLE { public: BufferInfo(const uint8_t* b, size_t bb, const Callback& cb) : _buffer(b), _buffer_bytes(bb), _offset(0), _dst_port(0), _cb(cb), _is_sendto(false) {} BufferInfo(const uint8_t* b, size_t bb, const IPvX& dst_addr, uint16_t dst_port, const Callback& cb) : _buffer(b), _buffer_bytes(bb), _offset(0), _dst_addr(dst_addr), _dst_port(dst_port), _cb(cb), _is_sendto(true) {} BufferInfo(const uint8_t* b, size_t bb, size_t off, const Callback& cb) : _buffer(b), _buffer_bytes(bb), _offset(off), _dst_port(0), _cb(cb), _is_sendto(false) {} BufferInfo(const vector& data, const Callback& cb) : _data(data), _buffer(&_data[0]), _buffer_bytes(_data.size()), _offset(0), _dst_port(0), _cb(cb), _is_sendto(false) {} BufferInfo(const vector& data, const IPvX& dst_addr, uint16_t dst_port, const Callback& cb) : _data(data), _buffer(&_data[0]), _buffer_bytes(_data.size()), _offset(0), _dst_addr(dst_addr), _dst_port(dst_port), _cb(cb), _is_sendto(true) {} void dispatch_callback(AsyncFileOperator::Event e) { _cb->dispatch(e, _buffer, _buffer_bytes, _offset); } const uint8_t* buffer() const { return (_buffer); } size_t buffer_bytes() const { return (_buffer_bytes); } size_t offset() const { return (_offset); } void incr_offset(size_t done) { _offset += done; } const IPvX& dst_addr() const { return (_dst_addr); } uint16_t dst_port() const { return (_dst_port); } bool is_sendto() const { return (_is_sendto); } private: BufferInfo(); // Not directly constructible const vector _data; // Local copy of the data const uint8_t* _buffer; size_t _buffer_bytes; size_t _offset; const IPvX _dst_addr; const uint16_t _dst_port; Callback _cb; bool _is_sendto; }; void write(XorpFd, IoEventType); void complete_transfer(ssize_t done); uint32_t _coalesce; struct iovec* _iov; ref_ptr _dtoken; list _buffers; #ifdef HOST_OS_WINDOWS void disconnect(XorpFd fd, IoEventType type); XorpTask _deferred_io_task; #endif }; #endif // __LIBXORP_ASYNCIO_HH__ xorp/libxorp/test_main.hh0000664000076400007640000002270311540224227015646 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/libxorp/test_main.hh,v 1.21 2008/10/02 21:57:34 bms Exp $ #ifndef __LIBXORP_TEST_MAIN_HH__ #define __LIBXORP_TEST_MAIN_HH__ #include "xorp.h" #include "callback.hh" /** * Macro to use to generate debugging output when the verbose flag is * enabled. */ #define DOUT(info) \ if (info.verbose()) \ info.out() << __FUNCTION__ << ":" \ << __LINE__ << ":" \ << info.test_name() << ": " /** * Only generate debugging output if the verbose_level is equal to or above * threshold. */ #define DOUT_LEVEL(info, level) \ if (info.verbose() && info.verbose_level() >= level) \ info.out() << __FUNCTION__ << ":" \ << __LINE__ << ":" \ << info.test_name() << ": " /** * This class is passed as the first argument to each test function/method. */ class TestInfo { public: TestInfo(string myname, bool verbose, int verbose_level, ostream& o) : _myname(myname), _verbose(verbose), _verbose_level(verbose_level), _ostream(o) { } TestInfo(const TestInfo& rhs) : _myname(rhs._myname), _verbose(rhs._verbose), _verbose_level(rhs._verbose_level), _ostream(rhs._ostream) { } /* * @return The name of the current test. */ string test_name() { return _myname; } /* * @return True if the verbose flag has been enabled. */ bool verbose() { return _verbose; } /* * @return The verbose level. */ int verbose_level() { return _verbose_level; } /* * @return The stream to which output should be sent. */ ostream& out() { return _ostream; } private: string _myname; bool _verbose; int _verbose_level; ostream& _ostream; }; /** * A helper class for test programs. * * This class is used to parse the command line arguments and return * the exit status from the test functions/methods. An example of how * to use this class can be found in test_test_main.cc. * */ class TestMain { public: /** * Start the parsing of command line arguments. */ TestMain(int argc, char * const argv[]) : _verbose(false), _verbose_level(0), _exit_status(true) { _progname = argv[0]; for (int i = 1; i < argc; i++) { string argname; string argvalue = ""; Arg a; // Argument flag if (argv[i][0] == '-') { // Long form argument. if (argv[i][1] == '-') { argname = argv[i]; } else { argname = argv[i][1]; argname = "-" + argname; if ('\0' != argv[i][2]) { argvalue = &argv[i][2]; } } // Try and get the argument value if we don't already // have it. if ("" == argvalue && (i + 1) < argc) { if (argv[i + 1][0] != '-') { i++; argvalue = argv[i]; } } if ("" == argvalue) a = Arg(Arg::FLAG, argname); else a = Arg(Arg::VALUE, argname, argvalue); } else { a = Arg(Arg::REST, argv[i]); } _args.push_back(a); } } /** * Get an optional argument from the command line. * * @param short_form The short form of the argument e.g. "-t". * @param long_form The long form of the argument * e.g. "--testing". * @param description The description of this argument that will * be used in the usage message. * @return the argument or "" if not found or an error occured in * previous parsing of the arguments. */ string get_optional_args(const string &short_form, const string &long_form, const string &description) { _usage += short_form + "|" + long_form + " arg\t" + description + "\n"; if (false == _exit_status) return ""; list::iterator i; for (i = _args.begin(); i != _args.end(); i++) { if (short_form == i->name() || long_form == i->name()) { bool has_value; string value; value = i->value(has_value); if (!has_value) { _exit_status = false; return ""; } _args.erase(i); return value; } } return ""; } /** * Get an optional flag from the command line. * * @param short_form The short form of the argument e.g. "-t". * @param long_form The long form of the argument * e.g. "--testing". * @param description The description of this argument that will * be used in the usage message. * @return true if the flag is present or false found or an error * occured in previous parsing of the arguments. */ bool get_optional_flag(const string &short_form, const string &long_form, const string &description) { _usage += short_form + "|" + long_form + " arg\t" + description + "\n"; if (false == _exit_status) return false; list::iterator i; for (i = _args.begin(); i != _args.end(); i++) { if (short_form == i->name() || long_form == i->name()) { _args.erase(i); return true; } } return false; } /** * Complete parsing the arguments. * * Process generic arguments and verify that there are no * arguments left unprocessed. */ void complete_args_parsing() { _verbose = get_optional_flag("-v", "--verbose", "Verbose"); string level = get_optional_args("-l", "--verbose-level","Verbose level"); if ("" != level) _verbose_level = atoi(level.c_str()); bool h = get_optional_flag("-h", "--help","Print help information"); bool q = get_optional_flag("-?", "--help","Print help information"); if (h || q) { cerr << usage(); ::exit(-1); } if (!_args.empty()) { list::iterator i; for (i = _args.begin(); i != _args.end(); i++) { cerr << "Unused argument: " << i->name() << endl; } cerr << usage(); _exit_status = false; } } /** * Get the state of the verbose flag. Used by test programs that * don't use the run method to run tests. */ bool get_verbose() const { return _verbose; } /** * Get the the verbose level, should only be used if get_verbose() * is true. Used by test programs that don't use the run method to * run the tests. */ int get_verbose_level() const { return _verbose_level; } /** * Run a test function/method. The test function/method is passed * a TestInfo. The test function/method should return * true for success and "false for * failure. * * To run a function call "test": * run("test", callback(test)); * * @param test_name The name of the test. * @param cb Callback object. */ void run(string test_name, XorpCallback1::RefPtr cb) { if (false == _exit_status) return; // if (_verbose) cout << "Running: " << test_name << endl; TestInfo info(test_name, _verbose, _verbose_level, cout); if (!cb->dispatch(info)) { _exit_status = false; cerr << "Test Failed: " << test_name << endl; } else { cout << "Test Passed: " << test_name << endl; } } /** * @return The usage string. */ const string usage() { return "Usage " + _progname + ":\n" + _usage; } /** * Mark the tests as having failed. Used for setting an error * condition from outside a test. * * @param error Error string. */ void failed(string error) { _error_string += error; _exit_status = false; } /** * Must be called at the end of the tests. * * @return The status of the tests. Should be passed to exit(). */ int exit() { if ("" != _error_string) cerr << _error_string; return _exit_status ? 0 : -1; } private: class Arg; string _progname; list _args; bool _verbose; int _verbose_level; bool _exit_status; string _error_string; string _usage; class Arg { public: typedef enum {FLAG, VALUE, REST} arg_type; Arg() {} Arg(arg_type a, string name, string value = "") : _arg_type(a), _name(name), _value(value) { // debug_msg("Argument type = %d flag name = %s value = %s\n", // a, name.c_str(), value.c_str()); } Arg(const Arg& rhs) { copy(rhs); } Arg& operator=(const Arg& rhs) { if (&rhs == this) return *this; copy(rhs); return *this; } void copy(const Arg& rhs) { _arg_type = rhs._arg_type; _name = rhs._name; _value = rhs._value; } const string& name() { return _name; } const string& value(bool& has_value) { if (VALUE != _arg_type) { cerr << "Argument " << _name << " was not provided with a value\n"; has_value = false; } else has_value = true; return _value; } private: arg_type _arg_type; string _name; string _value; }; }; #endif // __LIBXORP_TEST_MAIN_HH__ xorp/libxorp/bug_catcher.cc0000664000076400007640000000010111421137511016100 0ustar greearbgreearb #include "bug_catcher.hh" unsigned int BugCatcher::_cnt = 0; xorp/libxorp/transaction.cc0000664000076400007640000001367011540224230016173 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include #include "libxorp/debug.h" #include "libxorp/random.h" #include "libxorp/eventloop.hh" #include "libxorp/transaction.hh" /* ------------------------------------------------------------------------- */ /* Inline TransactionManager::Transaction methods */ inline void TransactionManager::Transaction::add(const Operation& op) { _ops.push_back(op); _op_count++; defer_timeout(); } inline void TransactionManager::Transaction::commit() { // // Unschedule timeout timer, defense against a calls to // EventLoop.run() in whacky places. // _timeout_timer.unschedule(); while (_ops.empty() == false) { // Copy front of list, not a biggie data is refcnt'ed Operation op = _ops.front(); // Erase item from list so if error occurs we don't have to // reference anything on list again; i.e., error handler could // abort this transaction... _ops.erase(_ops.begin()); _op_count--; // // Deref ref_ptr to get reference to operation // TransactionOperation& top = *(op.get()); bool success = top.dispatch(); // // Give manager a chance to deal with success / error // _mgr->operation_result(success, top); } } inline void TransactionManager::Transaction::flush() { while (_ops.empty() == false) { _ops.erase(_ops.begin()); _op_count--; } } inline void TransactionManager::Transaction::defer_timeout() { uint32_t timeout_ms = _mgr->timeout_ms(); if (timeout_ms) _timeout_timer.schedule_after_ms(timeout_ms); } inline void TransactionManager::Transaction::cancel_timeout() { _timeout_timer.clear(); } /* ------------------------------------------------------------------------- */ /* Transaction Manager methods */ void TransactionManager::crank_tid() { // // This would be very bad if number of pending transactions is large. // In practice the bad case should be well outside bounds of this code. // _next_tid++; do { _next_tid += (xorp_random() & 0xfffff); } while (_transactions.find(_next_tid) != _transactions.end()); } bool TransactionManager::start(uint32_t& new_tid) { if (pending() == max_pending()) return false; crank_tid(); if (timeout_ms()) { XorpTimer t = _e.new_oneoff_after_ms( timeout_ms(), callback(this,&TransactionManager::timeout, _next_tid) ); _transactions.insert(TransactionDB::value_type( _next_tid, Transaction(*this, t)) ); } else { _transactions.insert(TransactionDB::value_type(_next_tid, Transaction(*this))); } new_tid = _next_tid; return true; } bool TransactionManager::abort(uint32_t tid) { TransactionDB::iterator i = _transactions.find(tid); if (i == _transactions.end()) return false; _transactions.erase(i); return true; } void TransactionManager::pre_commit(uint32_t /* tid */) {} void TransactionManager::post_commit(uint32_t /* tid */) {} void TransactionManager::operation_result(bool /* success */, const TransactionOperation& /* op */) {} bool TransactionManager::flush(uint32_t tid) { TransactionDB::iterator i = _transactions.find(tid); if (i == _transactions.end()) { return false; } Transaction& t = i->second; t.flush(); return true; } bool TransactionManager::commit(uint32_t tid) { if (_transactions.find(tid) == _transactions.end()) { return false; } pre_commit(tid); // // Check user did not do abort transaction in pre_commit(). This // comes at a cost since we've already done find(), but is better than // dereferencing an invalid iterator. We could disallow/prevent // abort by swapping transaction operations onto a temporary list // and deleting the transaction, but there are cases where abort // might be desirable in pre_commit and the double lookup is not // that expensive. // TransactionDB::iterator i = _transactions.find(tid); if (i == _transactions.end()) { return false; } Transaction& t = i->second; // // Sanity check, perhaps not the most appropriate place for this // assert(t.operations().size() == t.size()); // // Commit all operations in queue // t.commit(); // // Sanity check, perhaps not the most appropriate place for this // assert(t.operations().size() == t.size()); // // Erase transaction // _transactions.erase(i); post_commit(tid); return true; } bool TransactionManager::add(uint32_t tid, const Operation& op) { TransactionDB::iterator i = _transactions.find(tid); if (i == _transactions.end()) return false; i->second.add(op); return true; } bool TransactionManager::retrieve_size(uint32_t tid, uint32_t& count) const { TransactionDB::const_iterator i = _transactions.find(tid); if (i == _transactions.end()) return false; count = i->second.size(); return true; } void TransactionManager::timeout(uint32_t tid) { TransactionDB::iterator i = _transactions.find(tid); if (i == _transactions.end()) return; debug_msg("Timing out transaction id %u\n", XORP_UINT_CAST(tid)); _transactions.erase(i); } xorp/libxorp/daemon.h0000664000076400007640000000371411540224227014757 0ustar greearbgreearb/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- */ /* vim:set sts=4 ts=8: */ /* * Copyright (c) 2001-2011 XORP, Inc and Others * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License, Version * 2.1, June 1999 as published by the Free Software Foundation. * Redistribution and/or modification of this program under the terms of * any other version of the GNU Lesser General Public License is not * permitted. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, * see the GNU Lesser General Public License, Version 2.1, a copy of * which can be found in the XORP LICENSE.lgpl file. * * XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; * http://xorp.net */ #ifndef __LIBXORP_DAEMON_H__ #define __LIBXORP_DAEMON_H__ #ifdef __cplusplus extern "C" { #endif #ifndef _PATH_DEVNULL #define _PATH_DEVNULL "/dev/null" #endif /** * @short Options for xorp_daemonize(). */ enum { DAEMON_CHDIR = 0, DAEMON_NOCHDIR = 1, DAEMON_CLOSE = 0, DAEMON_NOCLOSE = 1 } xorp_daemon_t; /** * A local implementation of daemon(3). * * Fork a new process and detach from parent in a controlled way, allowing * XORP processes to run as UNIX daemons. * Uses the POSIX setsid() to detach from the controlling terminal. * * The parent SHOULD use the platform _exit() function to exit. * * This function is a no-op under Microsoft Windows. * * @param nochdir set to 0 if the process should chdir to / once detached. * @param noclose set to 0 if the process should close * * @return -1 if any error occurred, 0 if we are in the child process, * otherwise the return value is the child process ID. */ int xorp_daemonize(int nochdir, int noclose); #ifdef __cplusplus } #endif #endif /* __LIBXORP_DAEMON_H__ */ xorp/libxorp/fea_share.hh0000664000076400007640000000174011535503566015610 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2011: Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // #ifndef __LIBXORP_FEA_SHARE_HH__ #define __LIBXORP_FEA_SHARE_HH__ enum IfStringTypeE { IF_STRING_PARENT_IFNAME, IF_STRING_IFTYPE, IF_STRING_VID }; #endif xorp/libxorp/last_git_md5sum.txt0000664000076400007640000000000711703360431017173 0ustar greearbgreearb510d56fxorp/libxorp/time_slice.cc0000664000076400007640000000310311421137511015754 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // Time-slice class implementation. // #include "libxorp_module.h" #include "xorp.h" #include "time_slice.hh" // // Exported variables // // // Local constants definitions // // // Local structures/classes, typedefs and macros // // // Local variables // // // Local functions prototypes // TimeSlice::TimeSlice(uint32_t usec_limit, size_t test_iter_frequency) : _test_iter_frequency(test_iter_frequency), _remain_iter(test_iter_frequency) { _time_slice_limit = TimeVal(0, usec_limit); TimerList::system_gettimeofday(&_time_slice_start); } void TimeSlice::reset() { TimerList::system_gettimeofday(&_time_slice_start); _remain_iter = _test_iter_frequency; } xorp/libxorp/xorp_tests.hh0000664000076400007640000000527211540225530016075 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2010-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __LIBXORP_TESTS_HH__ #define __LIBXORP_TESTS_HH__ #define print_passed(a) cout << "Test Passed (" << a << ") " << __FUNCTION__ \ << " " << __FILE__ << ":" << __LINE__ << endl; #define print_failed(a) cerr << "Test Failed (" << a << ") " << __FUNCTION__ \ << " " << __FILE__ << ":" << __LINE__ << endl; // // printf(3)-like facility to conditionally print a message if verbosity // is enabled. // #define verbose_log(x...) _verbose_log(__FILE__,__LINE__, x) #define _verbose_log(file, line, x...) \ do { \ if (verbose()) { \ printf("From %s:%d: ", file, line); \ printf(x); \ } \ } while(0) // // Test and print a message whether two strings are lexicographically same. // The strings can be either C or C++ style. // #define verbose_match(s1, s2) \ _verbose_match(__FILE__, __LINE__, s1, s2) bool _verbose_match(const char* file, int line, const string& s1, const string& s2) { bool match = s1 == s2; _verbose_log(file, line, "%s: Comparing %s == %s\n", match ? "Test Passed" : "Test Failed", s1.c_str(), s2.c_str()); if (match == false) incr_failures(); return match; } // // Test and print a message whether a condition is true. // // The first argument is the condition to test. // The second argument is a string with a brief description of the tested // condition. // #define verbose_assert(cond, desc) \ _verbose_assert(__FILE__, __LINE__, cond, desc) bool _verbose_assert(const char* file, int line, bool cond, const string& desc) { _verbose_log(file, line, "%s: Testing %s\n", cond ? "Test Passed" : "Test Failed", desc.c_str()); if (cond == false) incr_failures(); return cond; } #endif // __LIBXORP_TESTS_HH__ xorp/libxorp/timer.hh0000664000076400007640000003711011635757530015016 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __LIBXORP_TIMER_HH__ #define __LIBXORP_TIMER_HH__ #include #ifdef HAVE_SYS_TIME_H #include #endif #include "timeval.hh" #include "heap.hh" #include "callback.hh" #include "task.hh" class XorpTimer; class TimerNode; class TimerList; class ClockBase; typedef XorpCallback0::RefPtr OneoffTimerCallback; // PeriodicTimerCallback methods should return true to reschedule typedef XorpCallback0::RefPtr PeriodicTimerCallback; typedef XorpCallback1::RefPtr BasicTimerCallback; /** * @short Abstract class used to receive TimerList notifications * * TimerListObserverBase is a class that can be subtyped to receive * notifications on when timers are created or expired. All the methods in * this class are private, since they must only be invoked by the friend class, * TimerList * * @see TimerList */ class TimerListObserverBase { public: virtual ~TimerListObserverBase(); private: /** * This function will get called when a timer is scheduled. Periodic * timers will produce periodic notifications. */ virtual void notify_scheduled(const TimeVal&) = 0; /** * This function will get called when a timer is unscheduled. */ virtual void notify_unscheduled(const TimeVal&) = 0; TimerList * _observed; friend class TimerList; }; /** * @short XorpTimer class * * Timers allow callbacks to be made at a specific time in the future. * They are ordinarily created via TimerList methods, and they * must be associated with an TimerList object in order to be * runnable. * * @see TimerList */ class XorpTimer { public: /** * @return true if the timer has been scheduled, and the callback * associated with this timer has not been called yet. */ bool scheduled() const; /** * @return the expiry time of the @ref XorpTimer */ const TimeVal& expiry() const; /** * Get the remaining time until the timer expires. * * @param remain the return-by-reference value with the remaining * time until the timer expires. If the current time is beyond * the expire time (e.g., if we are behind schedule with the timer * processing), the return time is zero. * @return true if the remaining time has meaningful value (e.g., * if timer was scheduled), otherwise false. */ bool time_remaining(TimeVal& remain) const; /** * Expire the @ref XorpTimer object when the TimerList is next run. */ void schedule_now(int priority = XorpTask::PRIORITY_DEFAULT); /** * Schedule the @ref XorpTimer object at a given time. */ void schedule_at(const TimeVal& when, int priority = XorpTask::PRIORITY_DEFAULT); /** * Schedule the @ref XorpTimer object to expire in @ref wait * after the current time. */ void schedule_after(const TimeVal& wait, int priority = XorpTask::PRIORITY_DEFAULT); /** * Schedule the @ref XorpTimer object. * * @param ms milliseconds from the current time. */ void schedule_after_ms(int ms, int priority = XorpTask::PRIORITY_DEFAULT); /** * Reschedule the @ref XorpTimer object. * * @param wait time from the most recent expiry. */ void reschedule_after(const TimeVal& wait); /** * Reschedule the @ref XorpTimer object. * * @param ms milliseconds from the most recent expiry. */ void reschedule_after_ms(int ms); /** * Unschedule the @ref XorpTimer object. The XorpTimer callback is not * invoked. */ void unschedule(); // unschedule if scheduled /** * Release reference to underlying state. */ void clear(); // erase timer XorpTimer() : _node(NULL) { } XorpTimer(TimerList* list, BasicTimerCallback cb); XorpTimer(const XorpTimer&); ~XorpTimer(); XorpTimer& operator=(const XorpTimer&); TimerNode* node() const { return _node; } private: TimerNode* _node; XorpTimer(TimerNode* n); friend class TimerList; }; /** * @short XorpTimer creation and scheduling entity * * A TimerList is a scheduling entity that provides a means to * create @ref XorpTimer objects and run them. * * XorpTimer objects created via TimerList methods contain pointers to * reference counted elements maintained in the TimerList. The * elements on the list need to be referenced by XorpTimer objects or * the underlying timer callbacks are never made. For instance: *
TimerList timer_list;

XorpTimer t = timer_list.new_oneoff_after(TimeVal(0, 100000),
			callback(some_function, some_arg));

new_oneoff_after(TimeVal(0, 200000), my_callback_b, my_parameter_a);

while ( ! timer_list.empty() ) {
	timer_list.run();
}
* * my_callback_a is called 100000us after the @ref XorpTimer * object is created. * my_callback_b is never called * because no XorpTimer references the underlying element on the TimerList * after TimerList::new_oneoff_after() is called. */ class TimerList { public: /** * @param clock clock object to use to query time. */ TimerList(ClockBase* clock); ~TimerList(); /** * Expire all pending @ref XorpTimer objects associated with @ref * TimerList. */ void run(); /** * Create a XorpTimer that will be scheduled once. * * @param when the absolute time when the timer expires. * @param ocb callback object that is invoked when timer expires. * * @return the @ref XorpTimer created. */ XorpTimer new_oneoff_at(const TimeVal& when, const OneoffTimerCallback& ocb, int priority = XorpTask::PRIORITY_DEFAULT); /** * Create a XorpTimer that will be scheduled once. * * @param wait the relative time when the timer expires. * @param ocb callback object that is invoked when timer expires. * * @return the @ref XorpTimer created. */ XorpTimer new_oneoff_after(const TimeVal& wait, const OneoffTimerCallback& ocb, int priority = XorpTask::PRIORITY_DEFAULT); void remove_timer(XorpTimer& t); /** * Create a XorpTimer that will invoke a callback periodically. * * @param wait the period when the timer expires. * @param pcb user callback object that is invoked when timer expires. * If the callback returns false the periodic XorpTimer is unscheduled. * * @return the @ref XorpTimer created. */ XorpTimer new_periodic(const TimeVal& wait, const PeriodicTimerCallback& pcb, int priority = XorpTask::PRIORITY_DEFAULT); /** * Create a XorpTimer to set a flag. * * @param when the absolute time when the timer expires. * * @param flag_ptr pointer to a boolean variable that is set to * @ref to_value when the @ref XorpTimer expires. * * @return the @ref XorpTimer created. */ XorpTimer set_flag_at(const TimeVal& when, bool* flag_ptr, bool to_value = true, int priority = XorpTask::PRIORITY_DEFAULT); /** * Create a XorpTimer to set a flag. * * @param wait the relative time when the timer expires. * * @param flag_ptr pointer to a boolean variable that is set to * @ref to_value when the @ref XorpTimer expires. * * @return the @ref XorpTimer created. */ XorpTimer set_flag_after(const TimeVal& wait, bool* flag_ptr, bool to_value = true, int priority = XorpTask::PRIORITY_DEFAULT); /** * Custom XorpTimer creation method. The @ref XorpTimer object created * needs to be explicitly scheduled with the available @ref XorpTimer * methods. * * @param hook user function to be invoked when XorpTimer expires. * * @param thunk user argument to be passed when user's function is * invoked. * * @return the @ref XorpTimer created. */ XorpTimer new_timer(const BasicTimerCallback& cb) { return XorpTimer(this, cb); } /** * @return true if there no @ref XorpTimer objects currently scheduled on * list. */ bool empty() const; /** * @return the number of scheduled objects. */ size_t size() const; /** * Query the next XorpTimer Expiry time. * * @param tv reference that is assigned expiry time of next timer. * If there is no @ref XorpTimer pending, this value is assigned the * maximum @ref TimeVal::MAXIMUM(). The first function returns the * absolute time at which the timer expires, where the second returns the * difference between now and the expiry time. * * @return true if there is a XorpTimer awaiting expiry, false otherwise. */ bool get_next_delay(TimeVal& tv) const; /** * Get the priority of the highest priority timer that has expired. * * @return the priority of the expired timer, or INFINITE_PRIORITY * if no timer has expired. */ int get_expired_priority() const; /** * Read the latest known value from the clock used by @ref * TimerList object. * * @param now the return-by-reference value with the current time. */ void current_time(TimeVal& now) const; /** * Advance time. This method fetches the time from clock object * associated with the TimerList and sets the TimerList current * time to this value. */ void advance_time(); /** * Default time querier. * * Get the current time. This method is analogous to calling * the underlying operating system's 'get current system time' * function and is implemented as a call to advance_time() * followed by a call to current_time(). * * @param tv a pointer to the @ref TimeVal storage to store the current * time. */ static void system_gettimeofday(TimeVal* tv); /** * Suspend process execution for a defined interval. * * This methid is analogous to calling sleep(3) or usleep(3), * and is implemented as a call to sleep(3) and/or usleep(3) * followed by a call to advance_time(). * * @param tv the period of time to suspend execution. */ static void system_sleep(const TimeVal& tv); /** * Register an observer object with this class * * @param obs an observer object derived from @ref TimerListObserverBase */ void set_observer(TimerListObserverBase& obs); /** * Unregister the current observer */ void remove_observer(); /** * Get pointer to sole TimerList instance. * * @return pointer if TimerList has been constructed, NULL otherwise. */ static TimerList* instance(); private: void schedule_node(TimerNode* t); // insert in time ordered pos. void unschedule_node(TimerNode* t); // remove from list void acquire_lock() const { /* nothing, for now */ } bool attempt_lock() const { return true; } void release_lock() const { /* nothing, for now */ } // find or create the heap assoicated with this priority level Heap* find_heap(int priority); // expire the highest priority timer bool expire_one(int worst_priority); private: // The following is not a noncopyable candidate. TimerList(const TimerList&); // Not copyable. TimerList& operator=(const TimerList&); // Assignable only by self. private: // we need one heap for each priority level map _heaplist; ClockBase* _clock; TimerListObserverBase* _observer; #ifdef HOST_OS_WINDOWS HANDLE _hirestimer; #endif friend class TimerNode; friend class TimerListObserverBase; }; class TimerNode : public NONCOPYABLE, public HeapBase { protected: TimerNode(TimerList*, BasicTimerCallback); virtual ~TimerNode(); void add_ref(); void release_ref(); // we want this even if it is never called, to override the // default supplied by the compiler. TimerNode(const TimerNode&); // never called TimerNode& operator=(const TimerNode&); bool scheduled() const { return _pos_in_heap >= 0; } int priority() const { return _priority; } const TimeVal& expiry() const { return _expires; } bool time_remaining(TimeVal& remain) const; void schedule_at(const TimeVal&, int priority); void schedule_after(const TimeVal& wait, int priority); void reschedule_after(const TimeVal& wait); void unschedule(); virtual void expire(XorpTimer&, void*); int _ref_cnt; // Number of referring XorpTimer objects TimeVal _expires; // Expiration time BasicTimerCallback _cb; int _priority; // Scheduling priority TimerList* _list; // TimerList this node is associated w. friend class XorpTimer; friend class TimerList; }; // ---------------------------------------------------------------------------- // inline Timer methods inline XorpTimer::XorpTimer(TimerNode* n) : _node(n) { if (_node) _node->add_ref(); } inline XorpTimer::XorpTimer(TimerList* tlist, BasicTimerCallback cb) : _node(new TimerNode(tlist, cb)) { if (_node) _node->add_ref(); } inline XorpTimer::XorpTimer(const XorpTimer& t) : _node(t._node) { if (_node) _node->add_ref(); } inline XorpTimer::~XorpTimer() { if (_node) _node->release_ref(); } inline XorpTimer& XorpTimer::operator=(const XorpTimer& t) { if (t._node) t._node->add_ref(); if (_node) _node->release_ref(); _node = t._node; return *this; } inline bool XorpTimer::scheduled() const { return _node && _node->scheduled(); } inline const TimeVal& XorpTimer::expiry() const { assert(_node); return _node->expiry(); } inline bool XorpTimer::time_remaining(TimeVal& remain) const { if (_node == NULL) { remain = TimeVal::ZERO(); return (false); } return (_node->time_remaining(remain)); } inline void XorpTimer::schedule_at(const TimeVal& t, int priority) { assert(_node); _node->schedule_at(t, priority); } inline void XorpTimer::schedule_after(const TimeVal& wait, int priority) { assert(_node); _node->schedule_after(wait, priority); } inline void XorpTimer::schedule_after_ms(int ms, int priority) { assert(_node); TimeVal wait(ms / 1000, (ms % 1000) * 1000); _node->schedule_after(wait, priority); } inline void XorpTimer::schedule_now(int priority) { schedule_after(TimeVal::ZERO(), priority); } inline void XorpTimer::reschedule_after(const TimeVal& wait) { assert(_node); _node->reschedule_after(wait); } inline void XorpTimer::reschedule_after_ms(int ms) { assert(_node); TimeVal wait(ms / 1000, (ms % 1000) * 1000); _node->reschedule_after(wait); } inline void XorpTimer::unschedule() { if (_node) _node->unschedule(); } inline void XorpTimer::clear() { if (_node) _node->release_ref(); _node = 0; } #endif // __LIBXORP_TIMER_HH__ xorp/libxorp/c_format.cc0000664000076400007640000000626711540224227015452 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "libxorp_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "c_format.hh" #ifndef HOST_OS_WINDOWS #define HAVE_C99_SNPRINTF // [v]snprintf() conforms to ISO C99 spec #endif #define FORMAT_BUFSIZE 4096 void c_format_validate(const char* fmt, int exp_count) { const char *p = fmt; int state = 0; int count = 0; while(*p != 0) { if (state == 0) { if (*p == '%') { count++; state = 1; } } else { switch (*p) { case 'd': case 'i': case 'o': case 'u': case 'x': case 'X': case 'D': case 'O': case 'U': case 'e': case 'E': case 'f': case 'g': case 'G': case 'c': case 's': case 'p': //parameter type specifiers state = 0; break; case '%': //escaped percent state = 0; count--; break; case 'n': //we don't permit %n fprintf(stderr, "%%n detected in c_format()\n"); abort(); case '*': //field width or precision also needs a parameter count++; break; } } p++; } if (exp_count != count) { abort(); } } string do_c_format(const char* fmt, ...) { size_t buf_size = FORMAT_BUFSIZE; // Default buffer size vector b(buf_size); va_list ap; do { va_start(ap, fmt); int ret = vsnprintf(&b[0], buf_size, fmt, ap); #ifndef HAVE_C99_SNPRINTF // We have an evil vsnprintf() implementation (MSVC) if (ret != -1 && ((size_t)ret < buf_size)) { string r = string(&b[0]); // Buffer size is OK va_end(ap); return r; } buf_size += FORMAT_BUFSIZE; #else // HAVE_C99_SNPRINTF // We have a C99 compliant implementation if ((size_t)ret < buf_size) { string r = string(&b[0]); // Buffer size is OK va_end(ap); return r; } buf_size = ret + 1; // Add space for the extra '\0' #endif // HAVE_C99_SNPRINTF b.resize(buf_size); } while (true); XLOG_UNREACHABLE(); } #ifdef TESTING_C_FORMAT_123 int main(int, char**) { c_format("%d", 3); printf("%s", c_format("hello%%\n").c_str()); printf("%s", c_format("hello %d\n", 27).c_str()); printf("%s", c_format("hello %3d %%%s\n", 27, "xyz").c_str()); printf("%s", c_format("hello %*d %%%s\n", 5, 27, "xyz").c_str()); printf("%s", c_format("hello %%%*n\n").c_str()); } #endif // TESTING_C_FORMAT_123 xorp/libxorp/vif.cc0000664000076400007640000002060511540224230014426 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "xorp.h" #include "vif.hh" VifAddr::VifAddr(const IPvX& ipvx_addr) : _addr(ipvx_addr), _subnet_addr(ipvx_addr.af()), _broadcast_addr(ipvx_addr.af()), _peer_addr(ipvx_addr.af()) { } #ifdef XORP_USE_USTL VifAddr::VifAddr() : _addr(AF_INET), _subnet_addr(AF_INET), _broadcast_addr(AF_INET), _peer_addr(AF_INET) { } #endif VifAddr::VifAddr(const IPvX& ipvx_addr, const IPvXNet& ipvxnet_subnet_addr, const IPvX& ipvx_broadcast_addr, const IPvX& ipvx_peer_addr) : _addr(ipvx_addr), _subnet_addr(ipvxnet_subnet_addr), _broadcast_addr(ipvx_broadcast_addr), _peer_addr(ipvx_peer_addr) { } bool VifAddr::is_same_subnet(const IPvXNet& ipvxnet) const { return (_subnet_addr.contains(ipvxnet)); } bool VifAddr::is_same_subnet(const IPvX& ipvx_addr) const { return (_subnet_addr.contains(ipvx_addr)); } // // Return C++ string representation of the VifAddr object in a // human-friendy form. // string VifAddr::str() const { ostringstream oss; oss << "addr: " << _addr.str() << " subnet: " << _subnet_addr.str() << " broadcast: " << _broadcast_addr.str() << " peer: " << _peer_addr.str(); return oss.str(); } bool VifAddr::operator==(const VifAddr& other) const { return ((addr() == other.addr()) && (subnet_addr() == other.subnet_addr()) && (broadcast_addr() == other.broadcast_addr()) && (peer_addr() == other.peer_addr())); } // // Vif constructor // Vif::Vif(const string& vifname, const string& ifname) : _name(vifname), _ifname(ifname) { set_pif_index(0); set_vif_index(0); set_pim_register(false); set_p2p(false); set_loopback(false); set_discard(false); set_unreachable(false); set_management(false); set_multicast_capable(false); set_broadcast_capable(false); set_underlying_vif_up(false); set_mtu(0); } // // Vif copy constructor // Vif::Vif(const Vif& vif) { _name = vif.name(); _ifname = vif.ifname(); set_pif_index(vif.pif_index()); set_vif_index(vif.vif_index()); _addr_list = vif.addr_list(); set_pim_register(vif.is_pim_register()); set_p2p(vif.is_p2p()); set_loopback(vif.is_loopback()); set_discard(vif.is_discard()); set_unreachable(vif.is_unreachable()); set_management(vif.is_management()); set_multicast_capable(vif.is_multicast_capable()); set_broadcast_capable(vif.is_broadcast_capable()); set_underlying_vif_up(vif.is_underlying_vif_up()); set_mtu(vif.mtu()); } // // Vif destructor // Vif::~Vif() { } // // Return C++ string representation of the Vif object in a // human-friendy form. // string Vif::str() const { string r; // The vif name r += "Vif["; r += _name; r += "]"; // The physical and virtual indexes r += " pif_index: "; r += c_format("%d", pif_index()); r += " vif_index: "; r += c_format("%d", vif_index()); // The list of addresses list::const_iterator iter; for (iter = _addr_list.begin(); iter != _addr_list.end(); ++iter) { r += " "; r += iter->str(); } // The flags r += " Flags:"; if (is_p2p()) r += " P2P"; if (is_pim_register()) r += " PIM_REGISTER"; if (is_multicast_capable()) r += " MULTICAST"; if (is_broadcast_capable()) r += " BROADCAST"; if (is_loopback()) r += " LOOPBACK"; if (is_discard()) r += " DISCARD"; if (is_unreachable()) r += " UNREACHABLE"; if (is_management()) r += " MANAGEMENT"; if (is_underlying_vif_up()) r += " UNDERLYING_VIF_UP"; r += c_format(" MTU: %u", XORP_UINT_CAST(mtu())); return r; } bool Vif::operator==(const Vif& other) const { return ((name() == other.name()) && (pif_index() == other.pif_index()) && (vif_index() == other.vif_index()) && (addr_list() == other.addr_list()) && (is_pim_register() == other.is_pim_register()) && (is_p2p() == other.is_p2p()) && (is_loopback() == other.is_loopback()) && (is_discard() == other.is_discard()) && (is_unreachable() == other.is_unreachable()) && (is_management() == other.is_management()) && (is_multicast_capable() == other.is_multicast_capable()) && (is_broadcast_capable() == other.is_broadcast_capable()) && (is_underlying_vif_up() == other.is_underlying_vif_up()) && (mtu() == other.mtu())); } const IPvX * Vif::addr_ptr() const { list::const_iterator iter; for (iter = _addr_list.begin(); iter != _addr_list.end(); ++iter) { const VifAddr *vif_addr = &(*iter); if (vif_addr->addr().is_unicast()) return (&vif_addr->addr()); } return (NULL); } int Vif::add_address(const VifAddr& vif_addr) { if (is_my_vif_addr(vif_addr)) return (XORP_ERROR); _addr_list.push_back(vif_addr); return (XORP_OK); } int Vif::add_address(const IPvX& ipvx_addr, const IPvXNet& ipvxnet_subnet_addr, const IPvX& ipvx_broadcast_addr, const IPvX& ipvx_peer_addr) { const VifAddr vif_addr(ipvx_addr, ipvxnet_subnet_addr, ipvx_broadcast_addr, ipvx_peer_addr); return add_address(vif_addr); } int Vif::add_address(const IPvX& ipvx_addr) { const VifAddr vif_addr(ipvx_addr); return add_address(vif_addr); } int Vif::delete_address(const IPvX& ipvx_addr) { list::iterator iter; for (iter = _addr_list.begin(); iter != _addr_list.end(); ++iter) { if ((iter)->is_my_addr(ipvx_addr)) { _addr_list.erase(iter); return (XORP_OK); } } return (XORP_ERROR); } VifAddr * Vif::find_address(const IPvX& ipvx_addr) { list::iterator iter; for (iter = _addr_list.begin(); iter != _addr_list.end(); ++iter) { if ((iter)->is_my_addr(ipvx_addr)) { return &(*iter); } } return (NULL); } const VifAddr * Vif::find_address(const IPvX& ipvx_addr) const { list::const_iterator iter; for (iter = _addr_list.begin(); iter != _addr_list.end(); ++iter) { if ((iter)->is_my_addr(ipvx_addr)) { return &(*iter); } } return (NULL); } bool Vif::is_my_addr(const IPvX& ipvx_addr) const { list::const_iterator iter; for (iter = _addr_list.begin(); iter != _addr_list.end(); ++iter) { if ((iter)->is_my_addr(ipvx_addr)) { return (true); } } return (false); } bool Vif::is_my_vif_addr(const VifAddr& vif_addr) const { list::const_iterator iter; for (iter = _addr_list.begin(); iter != _addr_list.end(); ++iter) { const VifAddr& tmp_vif_addr = *iter; if (tmp_vif_addr == vif_addr) return (true); } return (false); } bool Vif::is_same_subnet(const IPvXNet& ipvxnet) const { list::const_iterator iter; if (is_pim_register()) return (false); for (iter = _addr_list.begin(); iter != _addr_list.end(); ++iter) { if ((iter)->is_same_subnet(ipvxnet)) { return (true); } } return (false); } bool Vif::is_same_subnet(const IPvX& ipvx_addr) const { list::const_iterator iter; if (is_pim_register()) return (false); for (iter = _addr_list.begin(); iter != _addr_list.end(); ++iter) { if ((iter)->is_same_subnet(ipvx_addr)) { return (true); } } return (false); } bool Vif::is_same_p2p(const IPvX& ipvx_addr) const { list::const_iterator iter; if (is_pim_register() || (! is_p2p())) return (false); for (iter = _addr_list.begin(); iter != _addr_list.end(); ++iter) { if ((iter)->is_my_addr(ipvx_addr) || ((iter)->peer_addr() == ipvx_addr)) { return (true); } } return (false); } xorp/libxorp/strptime.c0000664000076400007640000003520511540225527015362 0ustar greearbgreearb/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- */ /* vim:set sts=4 ts=8: */ /* * Copyright (c) 2001-2011 XORP, Inc and Others * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License, Version * 2.1, June 1999 as published by the Free Software Foundation. * Redistribution and/or modification of this program under the terms of * any other version of the GNU Lesser General Public License is not * permitted. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, * see the GNU Lesser General Public License, Version 2.1, a copy of * which can be found in the XORP LICENSE.lgpl file. * * XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; * http://xorp.net */ /* * strptime(3) implementation: a parser for data and time string */ /* * XXX: the particular implementation is taken from NetBSD-current * (as of 2006/02/06). * The rest is a front-end for it. * Note that the local implementation is used only if the system * doesn't have its own strptime(3) implementation. * * The changes are: * - Included and * - The global function named strptime() is redefined as static * and is renamed to local_strptime(). * - The inclusion of some header files is removed. * - Sone unused structure definitions are removed. * - Conditional usage of __weak_alias() is removed. * - TM_YEAR_BASE is defined conditionally. * - A local implementation of UNUSED() is added. * - All usage of isspace() is replaced with xorp_isspace(). * - New function xorp_strptime() is added as a front-end for * the local implementation. * - Replaced "u_char" with "unsigned char" and "uint" * with "unsigned int". */ #include "xorp_config.h" /* * XXX: Linux's glibc2 and NetBSD need to define _XOPEN_SOURCE for strptime(3). * * On the other hand, OpenBSD-3.9 cannot process (included by * "libxorp/xorp.h") if _XOPEN_SOURCE is defined. * Hence, as an exception we include some stuff before "libxorp/xorp.h", * but then we undefine _XOPEN_SOURCE */ #define _XOPEN_SOURCE 500 #include #ifdef HAVE_STRINGS_H #include #endif #undef _XOPEN_SOURCE #include /* * XXX: don't include "libxorp/xorp.h", because it cannot be compiled * on Solaris 10 if _XOPEN_SOURCE is defined: _XOPEN_SOURCE itself * controls the definition of some other symbols. * Instead, include "xorp_config.h" and all other header files that may * be needed. */ /* #include "libxorp/xorp.h" */ #include "libxorp/utility.h" #ifndef TM_YEAR_BASE #define TM_YEAR_BASE 1900 #endif static inline void * UNCONST(const void *a) { return ((const char *)a - (const char *)0) + (char *)0; } /* * Copyright (c) 1994 Winning Strategies, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Winning Strategies, Inc. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ typedef struct { const char *abday[7]; const char *day[7]; const char *abmon[12]; const char *mon[12]; const char *am_pm[2]; const char *d_t_fmt; const char *d_fmt; const char *t_fmt; const char *t_fmt_ampm; } _TimeLocale; /* $NetBSD: _def_time.c,v 1.8 2005/06/12 05:21:27 lukem Exp $ */ /* * Written by J.T. Conklin . * Public domain. */ #if defined(LIBC_SCCS) && !defined(lint) __RCSID("$NetBSD: _def_time.c,v 1.8 2005/06/12 05:21:27 lukem Exp $"); #endif /* LIBC_SCCS and not lint */ const _TimeLocale _DefaultTimeLocale = { { "Sun","Mon","Tue","Wed","Thu","Fri","Sat", }, { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }, { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }, { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" }, { "AM", "PM" }, "%a %b %e %H:%M:%S %Y", "%m/%d/%y", "%H:%M:%S", "%I:%M:%S %p" }; const _TimeLocale *_CurrentTimeLocale = &_DefaultTimeLocale; /* $NetBSD: strptime.c,v 1.25 2005/11/29 03:12:00 christos Exp $ */ /*- * Copyright (c) 1997, 1998, 2005 The NetBSD Foundation, Inc. * All rights reserved. * * This code was contributed to The NetBSD Foundation by Klaus Klein. * Heavily optimised by David Laight * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #if defined(LIBC_SCCS) && !defined(lint) __RCSID("$NetBSD: strptime.c,v 1.25 2005/11/29 03:12:00 christos Exp $"); #endif #define _ctloc(x) (_CurrentTimeLocale->x) /* * We do not implement alternate representations. However, we always * check whether a given modifier is allowed for a certain conversion. */ #define ALT_E 0x01 #define ALT_O 0x02 #define LEGAL_ALT(x) { if (alt_format & ~(x)) return NULL; } #ifndef HAVE_STRPTIME static const unsigned char *conv_num(const unsigned char *, int *, unsigned int, unsigned int); static const unsigned char *find_string(const unsigned char *, int *, const char * const *, const char * const *, int); static char * local_strptime(const char *buf, const char *fmt, struct tm *tm) { unsigned char c; const unsigned char *bp; int alt_format, i, split_year = 0; const char *new_fmt; bp = (const unsigned char *)buf; while (bp != NULL && (c = *fmt++) != '\0') { /* Clear `alternate' modifier prior to new conversion. */ alt_format = 0; i = 0; /* Eat up white-space. */ if (xorp_isspace(c)) { while (xorp_isspace(*bp)) bp++; continue; } if (c != '%') goto literal; again: switch (c = *fmt++) { case '%': /* "%%" is converted to "%". */ literal: if (c != *bp++) return NULL; LEGAL_ALT(0); continue; /* * "Alternative" modifiers. Just set the appropriate flag * and start over again. */ case 'E': /* "%E?" alternative conversion modifier. */ LEGAL_ALT(0); alt_format |= ALT_E; goto again; case 'O': /* "%O?" alternative conversion modifier. */ LEGAL_ALT(0); alt_format |= ALT_O; goto again; /* * "Complex" conversion rules, implemented through recursion. */ case 'c': /* Date and time, using the locale's format. */ new_fmt = _ctloc(d_t_fmt); goto recurse; case 'D': /* The date as "%m/%d/%y". */ new_fmt = "%m/%d/%y"; LEGAL_ALT(0); goto recurse; case 'R': /* The time as "%H:%M". */ new_fmt = "%H:%M"; LEGAL_ALT(0); goto recurse; case 'r': /* The time in 12-hour clock representation. */ new_fmt =_ctloc(t_fmt_ampm); LEGAL_ALT(0); goto recurse; case 'T': /* The time as "%H:%M:%S". */ new_fmt = "%H:%M:%S"; LEGAL_ALT(0); goto recurse; case 'X': /* The time, using the locale's format. */ new_fmt =_ctloc(t_fmt); goto recurse; case 'x': /* The date, using the locale's format. */ new_fmt =_ctloc(d_fmt); recurse: bp = (const unsigned char *)local_strptime((const char *)bp, new_fmt, tm); LEGAL_ALT(ALT_E); continue; /* * "Elementary" conversion rules. */ case 'A': /* The day of week, using the locale's form. */ case 'a': bp = find_string(bp, &tm->tm_wday, _ctloc(day), _ctloc(abday), 7); LEGAL_ALT(0); continue; case 'B': /* The month, using the locale's form. */ case 'b': case 'h': bp = find_string(bp, &tm->tm_mon, _ctloc(mon), _ctloc(abmon), 12); LEGAL_ALT(0); continue; case 'C': /* The century number. */ i = 20; bp = conv_num(bp, &i, 0, 99); i = i * 100 - TM_YEAR_BASE; if (split_year) i += tm->tm_year % 100; split_year = 1; tm->tm_year = i; LEGAL_ALT(ALT_E); continue; case 'd': /* The day of month. */ case 'e': bp = conv_num(bp, &tm->tm_mday, 1, 31); LEGAL_ALT(ALT_O); continue; case 'k': /* The hour (24-hour clock representation). */ LEGAL_ALT(0); /* FALLTHROUGH */ case 'H': bp = conv_num(bp, &tm->tm_hour, 0, 23); LEGAL_ALT(ALT_O); continue; case 'l': /* The hour (12-hour clock representation). */ LEGAL_ALT(0); /* FALLTHROUGH */ case 'I': bp = conv_num(bp, &tm->tm_hour, 1, 12); if (tm->tm_hour == 12) tm->tm_hour = 0; LEGAL_ALT(ALT_O); continue; case 'j': /* The day of year. */ i = 1; bp = conv_num(bp, &i, 1, 366); tm->tm_yday = i - 1; LEGAL_ALT(0); continue; case 'M': /* The minute. */ bp = conv_num(bp, &tm->tm_min, 0, 59); LEGAL_ALT(ALT_O); continue; case 'm': /* The month. */ i = 1; bp = conv_num(bp, &i, 1, 12); tm->tm_mon = i - 1; LEGAL_ALT(ALT_O); continue; case 'p': /* The locale's equivalent of AM/PM. */ bp = find_string(bp, &i, _ctloc(am_pm), NULL, 2); if (tm->tm_hour > 11) return NULL; tm->tm_hour += i * 12; LEGAL_ALT(0); continue; case 'S': /* The seconds. */ bp = conv_num(bp, &tm->tm_sec, 0, 61); LEGAL_ALT(ALT_O); continue; case 'U': /* The week of year, beginning on sunday. */ case 'W': /* The week of year, beginning on monday. */ /* * XXX This is bogus, as we can not assume any valid * information present in the tm structure at this * point to calculate a real value, so just check the * range for now. */ bp = conv_num(bp, &i, 0, 53); LEGAL_ALT(ALT_O); continue; case 'w': /* The day of week, beginning on sunday. */ bp = conv_num(bp, &tm->tm_wday, 0, 6); LEGAL_ALT(ALT_O); continue; case 'Y': /* The year. */ i = TM_YEAR_BASE; /* just for data sanity... */ bp = conv_num(bp, &i, 0, 9999); tm->tm_year = i - TM_YEAR_BASE; LEGAL_ALT(ALT_E); continue; case 'y': /* The year within 100 years of the epoch. */ /* LEGAL_ALT(ALT_E | ALT_O); */ bp = conv_num(bp, &i, 0, 99); if (split_year) /* preserve century */ i += (tm->tm_year / 100) * 100; else { split_year = 1; if (i <= 68) i = i + 2000 - TM_YEAR_BASE; else i = i + 1900 - TM_YEAR_BASE; } tm->tm_year = i; continue; /* * Miscellaneous conversions. */ case 'n': /* Any kind of white-space. */ case 't': while (xorp_isspace(*bp)) bp++; LEGAL_ALT(0); continue; default: /* Unknown/unsupported conversion. */ return NULL; } } return (char *)UNCONST(bp); } static const unsigned char * conv_num(const unsigned char *buf, int *dest, unsigned int llim, unsigned int ulim) { unsigned int result = 0; unsigned char ch; /* The limit also determines the number of valid digits. */ unsigned int rulim = ulim; ch = *buf; if (ch < '0' || ch > '9') return NULL; do { result *= 10; result += ch - '0'; rulim /= 10; ch = *++buf; } while ((result * 10 <= ulim) && rulim && ch >= '0' && ch <= '9'); if (result < llim || result > ulim) return NULL; *dest = result; return buf; } /* * XXX: The Windows' equivalent of strncasecmp(3) is called _strnicmp() * hence we need to do some renaming here. */ #ifdef HOST_OS_WINDOWS #define STRNCASECMP(s1, s2, len) _strnicmp(s1, s2, len) #else #define STRNCASECMP(s1, s2, len) strncasecmp(s1, s2, len) #endif static const unsigned char * find_string(const unsigned char *bp, int *tgt, const char * const *n1, const char * const *n2, int c) { int i; unsigned int len; /* check full name - then abbreviated ones */ for (; n1 != NULL; n1 = n2, n2 = NULL) { for (i = 0; i < c; i++, n1++) { len = strlen(*n1); if (STRNCASECMP(*n1, (const char *)bp, len) == 0) { *tgt = i; return bp + len; } } } /* Nothing matched */ return NULL; } #endif /* HAVE_STRPTIME */ char * xorp_strptime(const char *buf, const char *fmt, struct tm *tm) { #ifdef HAVE_STRPTIME return strptime(buf, fmt, tm); #else return local_strptime(buf, fmt, tm); #endif } xorp/libxorp/xorp.h0000664000076400007640000001315411540225530014501 0ustar greearbgreearb/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- */ /* vim:set sts=4 ts=8: */ /* * Copyright (c) 2001-2011 XORP, Inc and Others * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License, Version * 2.1, June 1999 as published by the Free Software Foundation. * Redistribution and/or modification of this program under the terms of * any other version of the GNU Lesser General Public License is not * permitted. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, * see the GNU Lesser General Public License, Version 2.1, a copy of * which can be found in the XORP LICENSE.lgpl file. * * XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; * http://xorp.net */ #ifndef __LIBXORP_XORP_H__ #define __LIBXORP_XORP_H__ #include "xorp_config.h" #ifdef HAVE_ENDIAN_H # include #endif #ifndef BYTE_ORDER #ifdef __BYTE_ORDER #define BYTE_ORDER __BYTE_ORDER #define LITTLE_ENDIAN __LITTLE_ENDIAN #define BIG_ENDIAN __BIG_ENDIAN #else /* ! _BYTE_ORDER */ /* * Presume that the scons logic figured defined WORDS_BIGENDIAN * or not. */ #ifndef __LITTLE_ENDIAN #define __LITTLE_ENDIAN 1234 #endif #ifndef __BIG_ENDIAN #define __BIG_ENDIAN 4321 #endif #ifndef LITTLE_ENDIAN #define LITTLE_ENDIAN __LITTLE_ENDIAN #endif #ifndef BIG_ENDIAN #define BIG_ENDIAN __BIG_ENDIAN #endif #ifdef WORDS_BIGENDIAN #define BYTE_ORDER BIG_ENDIAN #define __BYTE_ORDER BIG_ENDIAN #else #define BYTE_ORDER LITTLE_ENDIAN #define __BYTE_ORDER LITTLE_ENDIAN #endif /* * XXX: The old C preprocessor error if BYTE_ORDER is not defined. * * #error "BYTE_ORDER not defined! Define it to either LITTLE_ENDIAN (e.g. i386, vax) or BIG_ENDIAN (e.g. 68000, ibm, net) based on your architecture!" */ #endif /* ! __BYTE_ORDER */ #endif /* ! BYTE_ORDER */ #ifdef __cplusplus # ifdef XORP_USE_USTL # include using namespace ustl; # else # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # if !defined(__STL_NO_NAMESPACES) using namespace std; # endif /* namespace std */ # endif /* else, not ustl */ #endif /* c++ */ #ifdef HAVE_STDINT_H # include #endif #ifdef HAVE_INTTYPES_H # include #endif #if defined (__cplusplus) # if defined (USE_BOOST) && defined (HAS_BOOST_NONCOPYABLE_INC) # include # define NONCOPYABLE boost::noncopyable # else # define NONCOPYABLE xorp_noncopyable # include "xorp_noncopyable.hh" # endif #endif /* * Include sys/cdefs.h to define __BEGIN_DECLS and __END_DECLS. Even if * this file exists, not all platforms define these macros. */ #ifdef HAVE_SYS_CDEFS_H # include #endif /* * Define C++ decls wrappers if not previously defined. */ #ifndef __BEGIN_DECLS # if defined(__cplusplus) # define __BEGIN_DECLS extern "C" { # define __END_DECLS }; # else /* __BEGIN_DECLS */ # define __BEGIN_DECLS # define __END_DECLS # endif /* __BEGIN_DECLS */ #endif /* __BEGIN_DECLS */ #include "xorp_osdep_begin.h" #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_SYS_PARAM_H #include #endif #ifdef HAVE_SYS_TIME_H #include #endif #ifdef HAVE_SYS_SOCKET_H #include #endif /* * XXX: Include upfront so we can create a work-around because * of a problematic name collusion in the Solaris-10 header file. * That file contains "struct map *" pointer that prevents us from * including the STL file. */ #ifdef HAVE_NET_IF_H #define map solaris_class_map #include #undef map #endif #ifdef HAVE_NETINET_IN_H #include #endif #ifdef HAVE_SYS_STAT_H #include #endif #include #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #include #include #include #include #include #ifdef HAVE_SYSLOG_H #include #endif #include #include "utility.h" #include "xorp_osdep_mid.h" #if defined (__cplusplus) && !defined(__STL_NO_NAMESPACES) #ifndef XORP_USE_USTL using namespace std::rel_ops; #endif #endif /* * Misc. definitions that may be missing from the system header files. * TODO: this should go to a different header file. */ #ifndef NBBY #define NBBY (8) /* number of bits in a byte */ #endif /* * Redefine NULL as per Stroustrup to get additional error checking. */ #ifdef NULL # undef NULL # if defined __GNUG__ && \ (__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 8)) # define NULL (__null) # else # if !defined(__cplusplus) # define NULL ((void*)0) # else # define NULL (0) # endif # endif #endif /* NULL */ /* * Boolean values */ #if (!defined(TRUE)) || (!defined(FALSE)) #ifdef TRUE #undef TRUE #endif #ifdef FALSE #undef FALSE #endif #define FALSE (0) #define TRUE (!FALSE) #endif /* TRUE, FALSE */ #ifndef __cplusplus typedef enum { true = TRUE, false = FALSE } bool; #endif typedef bool bool_t; /* * Generic error return codes */ #ifdef XORP_OK #undef XORP_OK #endif #ifdef XORP_ERROR #undef XORP_ERROR #endif #define XORP_OK (0) #define XORP_ERROR (-1) #include "xorp_osdep_end.h" #endif /* __LIBXORP_XORP_H__ */ xorp/libxorp/selector.cc0000664000076400007640000003677711630245754015521 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "libxorp_module.h" #include "libxorp/xorp.h" #ifndef USE_WIN_DISPATCHER #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/xorpfd.hh" #include "libxorp/timeval.hh" #include "libxorp/clock.hh" #include "libxorp/eventloop.hh" #include "libxorp/utility.h" #include "selector.hh" // ---------------------------------------------------------------------------- // Helper function to deal with translating between old and new // I/O event notification mechanisms. static SelectorMask map_ioevent_to_selectormask(const IoEventType type) { SelectorMask mask = SEL_NONE; // Convert new event type to legacy UNIX event mask used by SelectorList. switch (type) { case IOT_READ: mask = SEL_RD; break; case IOT_WRITE: mask = SEL_WR; break; case IOT_EXCEPTION: mask = SEL_EX; break; case IOT_ACCEPT: mask = SEL_RD; break; case IOT_CONNECT: mask = SEL_WR; break; case IOT_DISCONNECT: mask = SEL_EX; // XXX: Disconnection isn't a distinct event in UNIX break; case IOT_ANY: mask = SEL_ALL; break; } return (mask); } // ---------------------------------------------------------------------------- // SelectorList::Node methods inline SelectorList::Node::Node() { magic = GOOD_NODE_MAGIC; for (int i = 0; i= 0) && (idx < SEL_MAX_IDX)); // 2. Check that bits in 'mode' are not already registered // TODO: This is an overkill: we don't need to check the whole array, // but only _mask[idx] for (int i = 0; i < SEL_MAX_IDX; i++) { if (_mask[i] & m) { return false; } } // 2. Register in the selected slot and add entry. // XXX: TODO: Determine if the node we're about to add is for // an accept event, so we know how to map it back. if (!_mask[idx]) { _mask[idx] = m; _cb[idx] = IoEventCb(cb); _iot[idx] = type; _priority[idx] = priority; return true; } assert(0); return false; } inline int SelectorList::Node::run_hooks(SelectorMask m, XorpFd fd) { int n = 0; /* * This is nasty. We dispatch the callbacks here associated with * the file descriptor fd. Unfortunately these callbacks can * manipulate the mask and callbacks associated with the * descriptor, ie the data change beneath our feet. At no time do * we want to call a callback that has been removed so we can't * just copy the data before starting the dispatch process. We do * not want to perform another callback here on a masked bit that * we have already done a callback on. We therefore keep track of * the bits already matched with the variable already_matched. * * An alternate fix is to change the semantics of add_ioevent_cb so * there is one callback for each I/O event. * * Yet another alternative is to have an object, let's call it a * Selector that is a handle state of a file descriptor: ie an fd, a * mask, a callback and an enabled flag. We would process the Selector * state individually. */ SelectorMask already_matched = SelectorMask(0); for (int i = 0; i < SEL_MAX_IDX; i++) { assert(magic == GOOD_NODE_MAGIC); SelectorMask match = SelectorMask(_mask[i] & m & ~already_matched); if (match) { assert(_cb[i].is_empty() == false); _cb[i]->dispatch(fd, _iot[i]); assert(magic == GOOD_NODE_MAGIC); n++; } already_matched = SelectorMask(already_matched | match); } return n; } inline void SelectorList::Node::clear(SelectorMask zap) { for (size_t i = 0; i < SEL_MAX_IDX; i++) { _mask[i] &= ~zap; if (_mask[i] == 0) { _cb[i].release(); _priority[i] = XorpTask::PRIORITY_INFINITY; } } } inline bool SelectorList::Node::is_empty() { return ((_mask[SEL_RD_IDX] == 0) && (_mask[SEL_WR_IDX] == 0) && (_mask[SEL_EX_IDX] == 0)); } // ---------------------------------------------------------------------------- // SelectorList implementation // NOTE: It is possible for callbacks to add an event, that that event can // cause the selector_entries to be resized. See: add_ioevent_cb // This in turn deletes the old memory in the vector. This this causes // Node::run_hooks to be accessing deleted memory (ie, 'this' was deleted during // the call to dispatch(). // Seems like a lot of pain to fix this right, so in the meantime, will pre-allocate // logs of space in the selector_entries vector in hopes we do not have to resize. SelectorList::SelectorList(ClockBase *clock) : _clock(clock), _observer(NULL), _testfds_n(0), _last_served_fd(-1), _last_served_sel(-1), // XXX: Preallocate to work around use-after-free in Node::run_hooks(). _selector_entries(1024), _maxfd(0), _descriptor_count(0), _is_debug(false) { x_static_assert(SEL_RD == (1 << SEL_RD_IDX) && SEL_WR == (1 << SEL_WR_IDX) && SEL_EX == (1 << SEL_EX_IDX) && SEL_MAX_IDX == 3); for (int i = 0; i < SEL_MAX_IDX; i++) FD_ZERO(&_fds[i]); } SelectorList::~SelectorList() { } bool SelectorList::add_ioevent_cb(XorpFd fd, IoEventType type, const IoEventCb& cb, int priority) { SelectorMask mask = map_ioevent_to_selectormask(type); if (mask == 0) { XLOG_FATAL("SelectorList::add_ioevent_cb: attempt to add invalid event " "type (type = %d)\n", type); } if (!fd.is_valid()) { XLOG_FATAL("SelectorList::add_ioevent_cb: attempt to add invalid file " "descriptor (fd = %s)\n", fd.str().c_str()); } if (fd.getSocket() >= _maxfd) { _maxfd = fd; if ((size_t)fd >= _selector_entries.size()) { _selector_entries.resize(fd + 32); } } bool no_selectors_with_fd = _selector_entries[fd].is_empty(); if (_selector_entries[fd].add_okay(mask, type, cb, priority) == false) { return false; } if (no_selectors_with_fd) _descriptor_count++; for (int i = 0; i < SEL_MAX_IDX; i++) { if (mask & (1 << i)) { FD_SET(fd, &_fds[i]); if (_observer) _observer->notify_added(fd, mask); } } return true; } void SelectorList::remove_ioevent_cb(XorpFd fd, IoEventType type) { bool found = false; if (fd < 0 || fd >= (int)_selector_entries.size()) { XLOG_ERROR("Attempting to remove fd = %d that is outside range of " "file descriptors 0..%u", (int)fd, XORP_UINT_CAST(_selector_entries.size())); return; } SelectorMask mask = map_ioevent_to_selectormask(type); for (int i = 0; i < SEL_MAX_IDX; i++) { if (mask & (1 << i) && FD_ISSET(fd, &_fds[i])) { found = true; FD_CLR(fd, &_fds[i]); if (_observer) _observer->notify_removed(fd, ((SelectorMask) (1 << i))); } } if (! found) { // XXX: no event that needs to be removed has been found return; } _selector_entries[fd].clear(mask); if (_selector_entries[fd].is_empty()) { assert(FD_ISSET(fd, &_fds[SEL_RD_IDX]) == 0); assert(FD_ISSET(fd, &_fds[SEL_WR_IDX]) == 0); assert(FD_ISSET(fd, &_fds[SEL_EX_IDX]) == 0); _descriptor_count--; } } bool SelectorList::ready() { fd_set testfds[SEL_MAX_IDX]; int n = 0; memcpy(testfds, _fds, sizeof(_fds)); struct timeval tv_zero; tv_zero.tv_sec = 0; tv_zero.tv_usec = 0; n = ::select(_maxfd + 1, &testfds[SEL_RD_IDX], &testfds[SEL_WR_IDX], &testfds[SEL_EX_IDX], &tv_zero); if (n < 0) { switch (errno) { case EBADF: callback_bad_descriptors(); break; case EINVAL: XLOG_FATAL("Bad select argument"); break; case EINTR: // The system call was interrupted by a signal, hence return // immediately to the event loop without printing an error. debug_msg("SelectorList::ready() interrupted by a signal\n"); break; default: XLOG_ERROR("SelectorList::ready() failed: %s", strerror(errno)); break; } return false; } if (n == 0) return false; else return true; } int SelectorList::do_select(struct timeval* to, bool force) { if (!force && _testfds_n > 0) return _testfds_n; _maxpri_fd = _maxpri_sel = -1; memcpy(_testfds, _fds, sizeof(_fds)); _testfds_n = ::select(_maxfd + 1, &_testfds[SEL_RD_IDX], &_testfds[SEL_WR_IDX], &_testfds[SEL_EX_IDX], to); if (!to || to->tv_sec > 0) _clock->advance_time(); if (_testfds_n < 0) { switch (errno) { case EBADF: callback_bad_descriptors(); break; case EINVAL: XLOG_FATAL("Bad select argument"); break; case EINTR: // The system call was interrupted by a signal, hence return // immediately to the event loop without printing an error. debug_msg("SelectorList::ready() interrupted by a signal\n"); break; default: XLOG_ERROR("SelectorList::ready() failed: %s", strerror(errno)); break; } } return _testfds_n; } int SelectorList::get_ready_priority(bool force) { struct timeval tv_zero; tv_zero.tv_sec = 0; tv_zero.tv_usec = 0; if (do_select(&tv_zero, force) <= 0) return XorpTask::PRIORITY_INFINITY; if (_maxpri_fd != -1) return _selector_entries[_maxpri_fd]._priority[_maxpri_sel]; int max_priority = XorpTask::PRIORITY_INFINITY; // // Test the priority of remaining events for the last served file // descriptor. // bool found_one = false; if ((_last_served_fd >= 0) && (_last_served_fd <= _maxfd)) { for (int sel_idx = _last_served_sel + 1; sel_idx < SEL_MAX_IDX; sel_idx++) { if (FD_ISSET(_last_served_fd, &_testfds[sel_idx])) { int p = _selector_entries[_last_served_fd]._priority[sel_idx]; if ((p < max_priority) || (!found_one)) { found_one = true; max_priority = p; _maxpri_fd = _last_served_fd; _maxpri_sel = sel_idx; } } } } for (int i = 0; i <= _maxfd; i++) { // // Use (_last_served_fd + 1) as a starting offset in the round-robin // search for the next file descriptor with the best priority. // int fd = (i + _last_served_fd + 1) % (_maxfd + 1); for (int sel_idx = 0; sel_idx < SEL_MAX_IDX; sel_idx++) { if (FD_ISSET(fd, &_testfds[sel_idx])) { int p = _selector_entries[fd]._priority[sel_idx]; if ((p < max_priority) || (!found_one)) { found_one = true; max_priority = p; _maxpri_fd = fd; _maxpri_sel = sel_idx; } } } } XLOG_ASSERT(_maxpri_fd != -1); return max_priority; } int SelectorList::wait_and_dispatch(TimeVal& timeout) { int n = 0; if (timeout == TimeVal::MAXIMUM()) n = do_select(NULL, false); else { struct timeval tv_to; timeout.copy_out(tv_to); n = do_select(&tv_to, false); } if (n <= 0) return 0; get_ready_priority(false); XLOG_ASSERT(_maxpri_fd != -1); // I hit this assert while trying to add 5000 BGP originate-routes via call_xrl: // /usr/local/xorp/bgp/harness/originate_routes.pl ip=10.1.1.0 nh=10.1.1.1 sigb=4 amt=5000 // I cannot figure out how this assert could happen..unless maybe there is some re-entry issue or // similar. Going to deal with things as best as possible w/out asserting. // TODO: Re-write this logic entirely to be less crufty all around. if (!(FD_ISSET(_maxpri_fd, &_testfds[_maxpri_sel]))) { _testfds_n = 0; _maxpri_fd = -1; _maxpri_sel = -1; return 0; } FD_CLR(_maxpri_fd, &_testfds[_maxpri_sel]); SelectorMask sm = SEL_NONE; switch (_maxpri_sel) { case SEL_RD_IDX: sm = SEL_RD; break; case SEL_WR_IDX: sm = SEL_WR; break; case SEL_EX_IDX: sm = SEL_EX; break; default: XLOG_ASSERT(false); } XLOG_ASSERT((_maxpri_fd >= 0) && (_maxpri_fd < (int)(_selector_entries.size()))); XLOG_ASSERT(_selector_entries[_maxpri_fd].magic == GOOD_NODE_MAGIC); _selector_entries[_maxpri_fd].run_hooks(sm, _maxpri_fd); _last_served_fd = _maxpri_fd; _last_served_sel = _maxpri_sel; _maxpri_fd = -1; _testfds_n--; XLOG_ASSERT(_testfds_n >= 0); return 1; // XXX what does the return value mean? } int SelectorList::wait_and_dispatch(int millisecs) { TimeVal t(millisecs / 1000, (millisecs % 1000) * 1000); return wait_and_dispatch(t); } void SelectorList::get_fd_set(SelectorMask selected_mask, fd_set& fds) const { if (SEL_RD == selected_mask) fds = _fds [SEL_RD_IDX]; if (SEL_WR == selected_mask) fds = _fds [SEL_WR_IDX]; if (SEL_EX == selected_mask) fds = _fds [SEL_EX_IDX]; return; } int SelectorList::get_max_fd() const { return _maxfd; } // // Note that this method should be called only if there are bad file // descriptors. // void SelectorList::callback_bad_descriptors() { int bc = 0; /* bad descriptor count */ for (int fd = 0; fd <= _maxfd; fd++) { if (_selector_entries[fd].is_empty() == true) continue; /* * Check whether fd is valid. */ struct stat sb; if ((fstat(fd, &sb) < 0) && (errno == EBADF)) { // // Force callbacks, should force read/writes that fail and // client should remove descriptor from list. // XLOG_ERROR("SelectorList found file descriptor %d no longer " "valid.", fd); _selector_entries[fd].run_hooks(SEL_ALL, fd); bc++; } } // // Assert should only fail if we called this method when there were // no bad file descriptors, or if fstat() didn't return the appropriate // error. // XLOG_ASSERT(bc != 0); } void SelectorList::set_observer(SelectorListObserverBase& obs) { _observer = &obs; _observer->_observed = this; return; } void SelectorList::remove_observer() { if (_observer) _observer->_observed = NULL; _observer = NULL; return; } SelectorListObserverBase::~SelectorListObserverBase() { if (_observed) _observed->remove_observer(); } #endif // !HOST_OS_WINDOWS xorp/libxorp/eventloop.cc0000664000076400007640000001725711635757530015711 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "libxorp_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxipc/finder.hh" #include "eventloop.hh" #include #include // // Number of EventLoop instances. // int eventloop_instance_count = 0; int xorp_do_run = 1; char xorp_sig_msg_buffer[64]; EnvTrace eloop_trace("ELOOPTRACE"); //Trap some common signals to allow graceful exit. // NOTE: Cannot do logging here, that logic is not re-entrant. // Copy msg into xorp-sig_msg_buffer instead..main program can check // if they want, and we also register an atexit handler to print it out // on exit. void dflt_sig_handler(int signo) { //reestablish signal handler signal(signo, (&dflt_sig_handler)); switch (signo) { case SIGTERM: strncpy(xorp_sig_msg_buffer, "SIGTERM received", sizeof(xorp_sig_msg_buffer)); goto do_terminate; case SIGINT: strncpy(xorp_sig_msg_buffer, "SIGINT received", sizeof(xorp_sig_msg_buffer)); goto do_terminate; #ifndef HOST_OS_WINDOWS case SIGXCPU: strncpy(xorp_sig_msg_buffer, "SIGINT received", sizeof(xorp_sig_msg_buffer)); goto do_terminate; case SIGXFSZ: strncpy(xorp_sig_msg_buffer, "SIGINT received", sizeof(xorp_sig_msg_buffer)); goto do_terminate; #endif default: // This is a coding error and we need to fix it. assert("WARNING: Ignoring un-handled error in dflt_sig_handler." == NULL); return; }//switch do_terminate: xorp_do_run = 0; // Now, kick any selects that are blocking, #ifndef HOST_OS_WINDOWS // SIGURG seems harmless enough to use. kill(getpid(), SIGURG); #else // Maybe this will work on Windows raise(SIGINT); #endif }//dflt_sig_handler void xorp_sig_atexit() { if (xorp_sig_msg_buffer[0]) { cerr << "WARNING: Process: " << getpid() << " has message from dflt_sig_handler: " << xorp_sig_msg_buffer << endl; } } void setup_dflt_sighandlers() { memset(xorp_sig_msg_buffer, 0, sizeof(xorp_sig_msg_buffer)); atexit(xorp_sig_atexit); signal(SIGTERM, dflt_sig_handler); signal(SIGINT, dflt_sig_handler); #ifndef HOST_OS_WINDOWS signal(SIGXCPU, dflt_sig_handler); signal(SIGXFSZ, dflt_sig_handler); #endif } EventLoop::EventLoop() : _clock(new SystemClock), _timer_list(_clock), _aggressiveness(0), _last_ev_run(0), _last_warned(0), _is_debug(false), #ifdef USE_WIN_DISPATCHER _win_dispatcher(_clock) #else _selector_list(_clock) #endif { XLOG_ASSERT(eventloop_instance_count == 0); XLOG_ASSERT(_last_ev_run == 0); eventloop_instance_count++; // Totally unnecessary initialization should stop complaints from // clever tools. for (int i = 0; i < XorpTask::PRIORITY_INFINITY; i++) _last_ev_type[i] = true; // // XXX: Ignore SIGPIPE, because we always check the return code. // If the program needs to install a SIGPIPE signal handler, the // handler must be installed after the EventLoop instance is created. // #ifdef SIGPIPE signal(SIGPIPE, SIG_IGN); #endif } EventLoop::~EventLoop() { eventloop_instance_count--; XLOG_ASSERT(eventloop_instance_count == 0); // // XXX: The _clock pointer is invalidated for other EventLoop fields // that might be using it. // delete _clock; _clock = NULL; } void EventLoop::run() { static const time_t MAX_ALLOWED = (XORP_HELLO_TIMER_MS/1000) + 2; TimeVal t; _timer_list.advance_time(); _timer_list.current_time(t); if (_last_ev_run == 0) _last_ev_run = t.sec(); time_t now = t.sec(); time_t diff = now - _last_ev_run; if (now - _last_warned > 0 && (diff > MAX_ALLOWED)) { XLOG_WARNING("%d seconds between calls to EventLoop::run", (int)diff); _last_warned = now; } // standard eventloop run do_work(); // select() could cause a large delay and will advance_time. Re-read // current time. _timer_list.current_time(t); _last_ev_run = t.sec(); } void EventLoop::do_work() { TimeVal t; TimeVal start; _timer_list.get_next_delay(t); // Run timers if they need it. if (t == TimeVal::ZERO()) { _timer_list.current_time(start); _timer_list.run(); if (eloop_trace.on()) { _timer_list.advance_time(); TimeVal n2; _timer_list.current_time(n2); if (n2.to_ms() > start.to_ms() + 20) { XLOG_INFO("timer-list run took too long to run: %lims\n", (long)(n2.to_ms() - start.to_ms())); } } } if (!_task_list.empty()) { _timer_list.current_time(start); _task_list.run(); if (eloop_trace.on()) { _timer_list.advance_time(); TimeVal n2; _timer_list.current_time(n2); if (n2.to_ms() > start.to_ms() + 20) { XLOG_INFO("task-list run took too long to run: %lims\n", (long)(n2.to_ms() - start.to_ms())); } } if (!_task_list.empty()) { // Run task again as soon as possible. t.set_ms(0); } } // If we are trying to shut down..make sure the event loop // doesn't hang forever. if (!xorp_do_run) { if ((t == TimeVal::MAXIMUM()) || (t.to_ms() > 1000)) { t = TimeVal(1, 0); // one sec } } _timer_list.current_time(start); #if USE_WIN_DISPATCHER // Seeing weird slownesses on windows..going to cap the timeout and see // if that helps. if (t.to_ms() > 100) t.set_ms(100); else if (t.to_ms() < 0) t.set_ms(0); _win_dispatcher.wait_and_dispatch(t); #else _selector_list.wait_and_dispatch(t); #endif if (eloop_trace.on()) { TimeVal n2; _timer_list.current_time(n2); if (n2.to_ms() > start.to_ms() + t.to_ms() + 20) { XLOG_INFO("wait-and-dispatch took too long to run: %lims\n", (long)(n2.to_ms() - start.to_ms())); } } } bool EventLoop::add_ioevent_cb(XorpFd fd, IoEventType type, const IoEventCb& cb, int priority) { #ifdef USE_WIN_DISPATCHER return _win_dispatcher.add_ioevent_cb(fd, type, cb, priority); #else return _selector_list.add_ioevent_cb(fd, type, cb, priority); #endif } bool EventLoop::remove_ioevent_cb(XorpFd fd, IoEventType type) { #ifdef USE_WIN_DISPATCHER return _win_dispatcher.remove_ioevent_cb(fd, type); #else _selector_list.remove_ioevent_cb(fd, type); return true; #endif } size_t EventLoop::descriptor_count() const { #ifdef USE_WIN_DISPATCHER return _win_dispatcher.descriptor_count(); #else return _selector_list.descriptor_count(); #endif } /** Remove timer from timer list. */ void EventLoop::remove_timer(XorpTimer& t) { _timer_list.remove_timer(t); } XorpTask EventLoop::new_oneoff_task(const OneoffTaskCallback& cb, int priority, int weight) { return _task_list.new_oneoff_task(cb, priority, weight); } XorpTask EventLoop::new_task(const RepeatedTaskCallback& cb, int priority, int weight) { return _task_list.new_task(cb, priority, weight); } void EventLoop::set_aggressiveness(int num) { _aggressiveness = num; } xorp/libxorp/ipv4net.hh0000664000076400007640000000221411421137511015244 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/libxorp/ipv4net.hh,v 1.10 2008/10/02 21:57:31 bms Exp $ #ifndef __LIBXORP_IPV4NET_HH__ #define __LIBXORP_IPV4NET_HH__ #include "ipv4.hh" #include "ipnet.hh" typedef IPNet IPv4Net; #endif // __LIBXORP_IPV4NET_HH__ xorp/libxorp/backtrace.cc0000664000076400007640000001363111540224227015570 0ustar greearbgreearb/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- */ /* vim:set sts=4 ts=8: */ /* * Copyright (c) 2009-2011 XORP, Inc and Others * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License, Version * 2.1, June 1999 as published by the Free Software Foundation. * Redistribution and/or modification of this program under the terms of * any other version of the GNU Lesser General Public License is not * permitted. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, * see the GNU Lesser General Public License, Version 2.1, a copy of * which can be found in the XORP LICENSE.lgpl file. * * XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; * http://xorp.net */ #include "xorp_config.h" #include "libxorp/xorp.h" //#include "libxorp/debug.h" #include #ifdef HAVE_EXECINFO_H #include #endif #ifdef HAVE_CXXABI_H #include #endif /* * Support for C/C++ symbolic backtraces, for runtime diagnostics. * Intended for use in both debug and production builds. */ #ifdef DEBUG_BACKTRACE static const char **_xorp_backtrace_alloc(); static _xorp_backtrace_free(const char **data); #endif /** * @short Wrapper function for the platform's backtrace facilities, * if present. * @return pointer to an array of char*, allocated with malloc(). */ extern "C" const char ** xorp_backtrace_alloc() { #ifdef DEBUG_BACKTRACE return (_xorp_backtrace_alloc()); #else return (NULL); #endif } extern "C" void xorp_backtrace_free(const char **data) { #ifdef DEBUG_BACKTRACE return (_xorp_backtrace_free()); #else return (NULL); #endif } /** * Back-end implementation of runtime backtrace facility. * * @return pointer to an array of char* containing each backtrace line. The * storage must be freed with xorp_backtrace_free(). * * XXX MUST be exception safe, apart from std::bad_alloc. * * Implementation notes: * Currently it is implemented using the GLIBC-compatible backtrace() * and backtrace_symbols() functions. On GLIBC systems, these are part * of the libc library. On BSD systems, these are part of libexecinfo, * a third-party implementation. * * It is not guaranteed to produce correct results when the C/C++ runtime * heap is corrupt, or when the stack is corrupt. * * The output of backtrace() is an array of C strings containing * function names and offsets. It will try to resolve the return addresses * on the stack to symbols, using dladdr(), which is arch-independent. * In some situations this might require building all executables with * '-rdynamic', to get meaningful backtraces from stripped binaries. * * It knows nothing about arguments or other stack frame entities. * As most of the code here is C++, we need to post-process this output. * This involves string processing, which in turn needs a working heap * to store temporary allocations. * * FUTURE: This could be implemented using e.g. libunwind, libdwarf * to produce richer output (e.g. using separate symbol files * from the production shipping binaries, much like Microsoft's PDB format), * and this might insulate us from heap corruption. * * The approach taken really needs to be similar to that of a last-change * exception handler, to be useful in situations of catastrophic heap * corruption. * * XXX use of vector or array is a boost::scoped_array candidate. * But we're in C and ABI land here, so stick to C. * One malloc here is bad enough if the heap is toasted. * */ #ifdef DEBUG_BACKTRACE static const int BACKTRACE_MAX = 128; static const char ** _xorp_backtrace_alloc() { #if defined(HAVE_BACKTRACE) && defined(HAVE_BACKTRACE_SYMBOLS) int i, nentries; void **entries, **final; char **syms, **cp, **np; entries = calloc(BACKTRACE_MAX, sizeof(void *)); assert(entries != NULL); nentries = backtrace(entries, BACKTRACE_MAX); if (nentries == 0) { free(entries); return (NULL); } // TODO skip first entry (it's this function). // We'll probably get inlined, so don't skip both. syms = backtrace_symbols(entries, nentries); assert(syms != NULL); /* Dispose of now-unused raw backtrace. */ free(entries); /* * The C string vector returned by backtrace_symbols() is * allocated with malloc(), however, the symbol names themselves * are usually owned by the dynamic linker, and SHOULD NOT be freed. * * However, as we need to rewrite these names for C++ demangling, * and we'll be passing them around, we need to copy them. We also * need to delimit the array, as the caller doesn't see 'nentries'. * This allows us to have * * If the demangler is available from the C++ ABI, we do this * piecemeal. Otherwise, we reallocate upfront. * * XXX The libexecinfo implementation produces different output * than that of GLIBC. For now, let's produce consistent output, * and parse what we get. */ final = calloc(nentries+1, sizeof(char *)); assert(final != NULL); #ifdef HAVE___CXA_DEMANGLE for (i = 0; i < ) { } #else /* !HAVE___CXA_DEMANGLE */ // TODO: munge the args in each line. for (i = 0, cp = syms, np = final; i < nentries; i++, cp++, final++) final = strdup(*cp); /* If out of heap space, all bets are off. */ assert(*final != NULL); } *cp = NULL; #endif /* HAVE___CXA_DEMANGLE */ return (final); #else /* ! (defined(HAVE_BACKTRACE) && defined(HAVE_BACKTRACE_SYMBOLS)) */ return (NULL); #endif /* defined(HAVE_BACKTRACE) && defined(HAVE_BACKTRACE_SYMBOLS) */ } // XXX: CONSTIFY static void _xorp_backtrace_free(const char **data) { char **cp; for (cp = (char **)data; cp != NULL; cp++) free(*cp); free(data); } #endif /* DEBUG_BACKTRACE */ xorp/libxorp/exceptions.cc0000664000076400007640000001251111540224227016026 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "exceptions.hh" #include #include #ifndef XORP_USE_USTL #include #endif XorpException::XorpException(const char* init_what, const char* file, size_t line) : _what(init_what), _file(file), _line(line) { } XorpException::~XorpException() { } const string XorpException::where() const { return c_format("line %u of %s", XORP_UINT_CAST(_line), _file); } const string XorpException::why() const { return "Not specified"; } string XorpException::str() const { return what() + " from " + where() + ": " + why(); } XorpReasonedException::XorpReasonedException(const char* init_what, const char* file, size_t line, const string& init_why) : XorpException(init_what, file, line), _why(init_why) { } const string XorpReasonedException::why() const { return ( _why.size() != 0 ) ? _why : string("Not specified"); } InvalidString::InvalidString(const char* file, size_t line, const string& init_why) : XorpReasonedException("InvalidString", file, line, init_why) { } InvalidAddress::InvalidAddress(const char* file, size_t line, const string& init_why) : XorpReasonedException("InvalidAddress", file, line, init_why) { } InvalidPort::InvalidPort(const char* file, size_t line, const string& init_why) : XorpReasonedException("InvalidPort", file, line, init_why) { } InvalidCast::InvalidCast(const char* file, size_t line, const string& init_why) : XorpReasonedException("XorpCast", file, line, init_why) { } InvalidBufferOffset::InvalidBufferOffset(const char* file, size_t line, const string& init_why) : XorpReasonedException("XorpInvalidBufferOffset", file, line, init_why) { } InvalidFamily::InvalidFamily(const char* file, size_t line, int af) : XorpException("XorpInvalidFamily", file, line), _af(af) { } const string InvalidFamily::why() const { return c_format("Unknown IP family - %d", _af); } InvalidPacket::InvalidPacket(const char* file, size_t line, const string& init_why) : XorpReasonedException("XorpInvalidPacket", file, line, init_why) { } InvalidNetmaskLength::InvalidNetmaskLength(const char* file, size_t line, int netmask_length) : XorpException("XorpInvalidNetmaskLength", file, line), _netmask_length (netmask_length) { // There was a case where fea was crashing due to un-caught exception. // Somehow, no useful info was being printed other than the exception // name. So, add some logging here just in case it happens again. // (On reboot, couldn't cause the problem to happen again, so not sure // I actually fixed the root cause in fea yet.) cerr << "Creating InvalidNetmaskLength exception, file: " << file << ":" << line << " netmask_length: " << netmask_length << endl; } const string InvalidNetmaskLength::why() const { return c_format("Invalid netmask length - %d", _netmask_length); } // ---------------------------------------------------------------------------- // Handlers void xorp_catch_standard_exceptions() { xorp_print_standard_exceptions(); terminate(); } void xorp_print_standard_exceptions() { try { throw; // Re-throw so we can inspect exception type } catch (const XorpException& xe) { cerr << xe.what() << " from " << xe.where() << " -> " << xe.why() << "\n"; } catch (const exception& e) { cerr << "Standard exception: " << e.what() << " (name = \"" << typeid(e).name() << "\")\n"; } } void xorp_unexpected_handler(void) { cerr << "Unexpected exception: " << "\tthrown did not correspond to specification - fix code.\n"; xorp_catch_standard_exceptions(); } // ---------------------------------------------------------------------------- // EXAMPLE //#define XORP_EXAMPLE_USAGE #ifdef XORP_EXAMPLE_USAGE #include void foo() { // do some stuff that happens to throw the non-descript exception // let's say invalid characters are "la-la" xorp_throw(XorpInvalidString, xorp_format_string("invalid characters occurred \"%s\"", "la-la")); } int main() { XorpUnexpectedHandler x(xorp_unexpected_handler); try { foo(); // will throw a XorpInvalidString bitset<8> bs; bs.set(1000); // will throw out_of_range("bitset"); foo(); } catch (...) { xorp_catch_standard_exceptions(); } return 0; } #endif xorp/libxorp/xorp_osdep_end.h0000664000076400007640000000501611605175666016537 0ustar greearbgreearb/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- */ /* vim:set sts=4 ts=8: */ /* * Copyright (c) 2001-2011 XORP, Inc and Others * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License, Version * 2.1, June 1999 as published by the Free Software Foundation. * Redistribution and/or modification of this program under the terms of * any other version of the GNU Lesser General Public License is not * permitted. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, * see the GNU Lesser General Public License, Version 2.1, a copy of * which can be found in the XORP LICENSE.lgpl file. * * XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; * http://xorp.net */ #ifndef __LIBXORP_XORP_OSDEP_END_H__ #define __LIBXORP_XORP_OSDEP_END_H__ #ifdef HOST_OS_WINDOWS #include "win_io.h" /* * Numerous kludges for purging items from the Windows namespace * which collide with the XORP namespace exist here. */ #ifdef __cplusplus extern "C" { #endif /* * XXX: ServiceStatus enum members collide * with some of the definitions in , which we don't use * [yet], so undefine some of them out of the way for now. */ #undef SERVICE_RUNNING #undef SERVICE_PAUSED /* * XXX: RouteCmd::Cmd enum collides with the * preprocessor define DELETE in . */ #undef DELETE /* * XXX: _export member name collides with * the preprocessor define _export in . */ #undef _export /* * XXX: enums collide with the preprocessor define * ERROR in . */ #undef ERROR /* * XXX: gai_strerror() is #define'd to one of two prototyped functions * by both the MinGW w32api version of the SDK, and the PSDK itself. * However the functions don't actually exist. * The Windows PSDK itself defines both functions as inline C functions. * Here we try to deal with both cases by explicitly defining a single * function prototype for the Win32 case, and purging the preprocessor macro. */ #ifdef gai_strerror #undef gai_strerror #endif char *gai_strerror(int ecode); #ifdef __cplusplus } #endif #define XSTRERROR win_strerror(GetLastError()) /* End of windows code */ #else /* Non windows code */ #define XSTRERROR strerror(errno) #endif #endif /* __LIBXORP_XORP_OSDEP_END_H__ */ xorp/libxorp/build_info.cc0000664000076400007640000000141611703360431015760 0ustar greearbgreearb // The build_info.cc file is Autogenerated, modifying by hand // will be a waste of time. You should modify the create_buildinfo.sh // script or the build_info.prefix file instead. #include "build_info.hh" const char* BuildInfo::getBuildMachine() { return "Linux build-32 2.6.34.9-69.fc13.i686.PAE i686 i686"; } const char* BuildInfo::getBuilder() { return "root"; } const char* BuildInfo::getBuildDate() { return "Wed Jan 11 11:20:57 PST 2012"; } const char* BuildInfo::getShortBuildDate() { return "2012-01-11 11:20"; } const char* BuildInfo::getGitLog() { return "510d56fb Update publish script with LANforge version.\n" "caeaa4f8 Update rls notes with shortlog.\n" "22e15569 Update HTML for rls 1.8.5\n"; } const char* BuildInfo::getGitVersion() { return "510d56f"; } xorp/libxorp/c_format.hh0000664000076400007640000001130211421137511015443 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/libxorp/c_format.hh,v 1.14 2008/10/03 17:41:31 atanu Exp $ #ifndef __LIBXORP_C_FORMAT_HH__ #define __LIBXORP_C_FORMAT_HH__ #include "libxorp/xorp.h" // // c_format is a macro that creates a string from a c-style format // string. It takes the same arguments as printf, but %n is illegal and // will cause abort to be called. // // Pseudo prototype: // string c_format(const char* format, ...); // // In practice c_format is a nasty macro, but by doing this we can check // the compile time arguments are sane and the run time arguments. // #define c_format(format, args...) \ (c_format_validate(format, arg_count(args)), do_c_format(format, ## args)) // // Template magic to allow us to count the number of varargs passed to // the macro c_format. We could also count the size of the var args data // for extra protection if we were doing the formatting ourselves... // // Comment this out to find unnecessary calls to c_format() inline int arg_count() { return 0; } template inline int arg_count(A) { return 1; } template inline int arg_count(A,B) { return 2; } template inline int arg_count(A,B,C) { return 3; } template inline int arg_count(A,B,C,D) { return 4; } template inline int arg_count(A,B,C,D,E) { return 5; } template inline int arg_count(A,B,C,D,E,F) { return 6; } template inline int arg_count(A,B,C,D,E,F,G) { return 7; } template inline int arg_count(A,B,C,D,E,F,G,H) { return 8; } template inline int arg_count(A,B,C,D,E,F,G,H,I) { return 9; } template inline int arg_count(A,B,C,D,E,F,G,H,I,J) { return 10; } template inline int arg_count(A,B,C,D,E,F,G,H,I,J,K) { return 11; } template inline int arg_count(A,B,C,D,E,F,G,H,I,J,K,L) { return 12; } template inline int arg_count(A,B,C,D,E,F,G,H,I,J,K,L,M) { return 13; } template inline int arg_count(A,B,C,D,E,F,G,H,I,J,K,L,M,N) { return 14; } template inline int arg_count(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O) { return 15; } template inline int arg_count(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P) { return 16; } template inline int arg_count(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q) { return 17; } void c_format_validate(const char* fmt, int n); #if defined(__printflike) string do_c_format(const char* fmt, ...) __printflike(1,2); #elif (defined(__GNUC__)) string do_c_format(const char* fmt, ...) __attribute__((__format__(printf, 1, 2))); #else string do_c_format(const char* fmt, ...); #endif #endif // __LIBXORP_C_FORMAT_HH__ xorp/libxorp/inet_ntop.c0000664000076400007640000001270011532046567015513 0ustar greearbgreearb/* vim:set sts=4 ts=8: */ /* Copyright (c) 1996 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #include "libxorp/xorp.h" #ifndef HAVE_INET_NTOP #ifdef HOST_OS_WINDOWS #include #include #ifndef EAFNOSUPPORT #define EAFNOSUPPORT WSAEAFNOSUPPORT #endif #ifndef IN6ADDRSZ #define IN6ADDRSZ 16 #endif #ifndef INT16SZ #define INT16SZ 2 #endif #else /* ! HOST_OS_WINDOWS */ #include #include #include #include #include #include #include #include #include #endif /* ! HOST_OS_WINDOWS */ /* * WARNING: Don't even consider trying to compile this on a system where * sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX. */ static const char *inet_ntop4(const u_char *src, char *dst, size_t size); static const char *inet_ntop6(const u_char *src, char *dst, size_t size); /* char * * inet_ntop(af, src, dst, size) * convert a network format address to presentation format. * return: * pointer to presentation format address (`dst'), or NULL (see errno). * author: * Paul Vixie, 1996. */ const char * inet_ntop(int af, const void *src, char *dst, size_t size) { switch (af) { case AF_INET: return (inet_ntop4(src, dst, size)); case AF_INET6: return (inet_ntop6(src, dst, size)); default: errno = EAFNOSUPPORT; return (NULL); } /* NOTREACHED */ } /* const char * * inet_ntop4(src, dst, size) * format an IPv4 address, more or less like inet_ntoa() * return: * `dst' (as a const) * notes: * (1) uses no statics * (2) takes a u_char* not an in_addr as input * author: * Paul Vixie, 1996. */ static const char * inet_ntop4(const u_char *src, char *dst, size_t size) { static const char fmt[] = "%u.%u.%u.%u"; char tmp[sizeof "255.255.255.255"]; int l; l = snprintf(tmp, size, fmt, src[0], src[1], src[2], src[3]); if (l <= 0 || (size_t)l >= size) { errno = ENOSPC; return (NULL); } strlcpy(dst, tmp, size); return (dst); } /* const char * * inet_ntop6(src, dst, size) * convert IPv6 binary address into presentation (printable) format * author: * Paul Vixie, 1996. */ static const char * inet_ntop6(const u_char *src, char *dst, size_t size) { /* * Note that int32_t and int16_t need only be "at least" large enough * to contain a value of the specified size. On some systems, like * Crays, there is no such thing as an integer variable with 16 bits. * Keep this in mind if you think this function should have been coded * to use pointer overlays. All the world's not a VAX. */ char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"]; char *tp, *ep; struct { int base, len; } best, cur; u_int words[IN6ADDRSZ / INT16SZ]; int i; int advance; /* * Preprocess: * Copy the input (bytewise) array into a wordwise array. * Find the longest run of 0x00's in src[] for :: shorthanding. */ memset(words, '\0', sizeof words); for (i = 0; i < IN6ADDRSZ; i++) words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3)); best.base = -1; cur.base = -1; for (i = 0; i < (IN6ADDRSZ / INT16SZ); i++) { if (words[i] == 0) { if (cur.base == -1) cur.base = i, cur.len = 1; else cur.len++; } else { if (cur.base != -1) { if (best.base == -1 || cur.len > best.len) best = cur; cur.base = -1; } } } if (cur.base != -1) { if (best.base == -1 || cur.len > best.len) best = cur; } if (best.base != -1 && best.len < 2) best.base = -1; /* * Format the result. */ tp = tmp; ep = tmp + sizeof(tmp); for (i = 0; i < (IN6ADDRSZ / INT16SZ) && tp < ep; i++) { /* Are we inside the best run of 0x00's? */ if (best.base != -1 && i >= best.base && i < (best.base + best.len)) { if (i == best.base) { if (tp + 1 >= ep) return (NULL); *tp++ = ':'; } continue; } /* Are we following an initial run of 0x00s or any real hex? */ if (i != 0) { if (tp + 1 >= ep) return (NULL); *tp++ = ':'; } /* Is this address an encapsulated IPv4? */ if (i == 6 && best.base == 0 && (best.len == 6 || (best.len == 5 && words[5] == 0xffff))) { if (!inet_ntop4(src+12, tp, (size_t)(ep - tp))) return (NULL); tp += strlen(tp); break; } advance = snprintf(tp, ep - tp, "%x", words[i]); if (advance <= 0 || advance >= ep - tp) return (NULL); tp += advance; } /* Was it a trailing run of 0x00's? */ if (best.base != -1 && (best.base + best.len) == (IN6ADDRSZ / INT16SZ)) { if (tp + 1 >= ep) return (NULL); *tp++ = ':'; } if (tp + 1 >= ep) return (NULL); *tp++ = '\0'; /* * Check for overflow, copy, and we're done. */ if ((size_t)(tp - tmp) > size) { errno = ENOSPC; return (NULL); } strlcpy(dst, tmp, size); return (dst); } #endif /* ! HAVE_INET_NTOP */ xorp/libxorp/ipvx.cc0000664000076400007640000004471311540224227014644 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "xorp.h" #include "ipvx.hh" #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_SYS_SOCKET_H #include #endif #ifdef HAVE_NETINET_IN_H #include #endif #ifdef HAVE_ARPA_INET_H #include #endif // // Static class members // // Construct an IN_ADDR_ANY address of family AF_INET. IPvX::IPvX() : _af(AF_INET) { memset(_addr, 0, sizeof(_addr)); } // Construct an IN_ADDR_ANY address of @family. IPvX::IPvX(int family) throw (InvalidFamily) { if ((family != AF_INET) && (family != AF_INET6)) xorp_throw(InvalidFamily, family); _af = family; memset(_addr, 0, sizeof(_addr)); } // Construct an IPvX address by copying the raw address from @from_uint8 // memory. IPvX::IPvX(int family, const uint8_t *from_uint8) throw (InvalidFamily) { assert(from_uint8 != NULL); _af = family; memset(_addr, 0, sizeof(_addr)); memcpy(_addr, from_uint8, addr_bytelen()); } IPvX::IPvX(const IPv4& ipv4) { x_static_assert(sizeof(_addr) >= sizeof(IPv4)); x_static_assert(sizeof(IPv4) == 4); _af = AF_INET; memset(_addr, 0, sizeof(_addr)); memcpy(_addr, &ipv4, 4); } IPvX::IPvX(const IPv6& ipv6) { x_static_assert(sizeof(_addr) >= sizeof(IPv6)); x_static_assert(sizeof(IPv6) == 16); _af = AF_INET6; memcpy(_addr, &ipv6, 16); } IPvX::IPvX(const in_addr& from_in_addr) { copy_in(AF_INET, reinterpret_cast(&from_in_addr)); } IPvX::IPvX(const in6_addr& from_in6_addr) { copy_in(AF_INET6, reinterpret_cast(&from_in6_addr)); } IPvX::IPvX(const sockaddr& from_sockaddr) throw (InvalidFamily) { copy_in(from_sockaddr); } IPvX::IPvX(const sockaddr_storage& from_sockaddr_storage) throw (InvalidFamily) { copy_in(from_sockaddr_storage); } IPvX::IPvX(const sockaddr_in& from_sockaddr_in) throw (InvalidFamily) { copy_in(from_sockaddr_in); } IPvX::IPvX(const sockaddr_in6& from_sockaddr_in6) throw (InvalidFamily) { copy_in(from_sockaddr_in6); } IPvX::IPvX(char const *from_cstring) throw (InvalidString) { memset(_addr, 0, sizeof(_addr)); if (from_cstring == NULL) { xorp_throw(InvalidString, "Null value"); } else if (inet_pton(AF_INET, from_cstring, _addr) > 0) { _af = AF_INET; } else if (inet_pton(AF_INET6, from_cstring, _addr) > 0) { _af = AF_INET6; } else { xorp_throw(InvalidString, c_format("Bad IPvX \"%s\"", from_cstring)); } } // TODO: if this method is used very often, then reimplement it // by directly ~ of r._addr (see IPv6 implementation for details). IPvX IPvX::operator~() const { if (is_ipv4()) { return (~get_ipv4()); // XXX: implicitly create IPvX return object } else { return (~get_ipv6()); // XXX: implicitly create IPvX return object } } IPvX IPvX::operator|(const IPvX& other) const throw (InvalidCast) { if (is_ipv4()) { return get_ipv4() | other.get_ipv4(); } else { return get_ipv6() | other.get_ipv6(); } } IPvX IPvX::operator&(const IPvX& other) const throw (InvalidCast) { if (is_ipv4()) { return get_ipv4() & other.get_ipv4(); } else { return get_ipv6() & other.get_ipv6(); } } IPvX IPvX::operator^(const IPvX& other) const throw (InvalidCast) { if (is_ipv4()) { return get_ipv4() ^ other.get_ipv4(); } else { return get_ipv6() ^ other.get_ipv6(); } } IPvX IPvX::operator<<(uint32_t left_shift) const { if (is_ipv4()) { return get_ipv4() << left_shift; } else { return get_ipv6() << left_shift; } } IPvX IPvX::operator>>(uint32_t right_shift) const { if (is_ipv4()) { return get_ipv4() >> right_shift; } else { return get_ipv6() >> right_shift; } } bool IPvX::operator<(const IPvX& other) const { x_static_assert(sizeof(_addr) == 16); int i; for (i = 0; i < 3; i++) { // Loop ends intentionally at 3 not 4. if (_addr[i] != other._addr[i]) { break; } } return ntohl(_addr[i]) < ntohl(other._addr[i]); } bool IPvX::operator==(const IPvX& other) const { if (is_ipv4() && other.is_ipv4()) return (get_ipv4() == other.get_ipv4()); else if (is_ipv6() && other.is_ipv6()) return (get_ipv6() == other.get_ipv6()); else return false; } bool IPvX::operator!=(const IPvX& other) const { if (is_ipv4() && other.is_ipv4()) return (get_ipv4() != other.get_ipv4()); else if (is_ipv6() && other.is_ipv6()) return (get_ipv6() != other.get_ipv6()); else return true; } IPvX& IPvX::operator--() { if (is_ipv4()) { *this = --get_ipv4(); } else { *this = --get_ipv6(); } return (*this); } IPvX& IPvX::operator++() { if (is_ipv4()) { *this = ++get_ipv4(); } else { *this = ++get_ipv6(); } return (*this); } IPvX IPvX::make_prefix(int family, uint32_t mask_len) throw (InvalidFamily, InvalidNetmaskLength) { if (family == AF_INET) return IPv4::make_prefix(mask_len); else if (family == AF_INET6) return IPv6::make_prefix(mask_len); else xorp_throw(InvalidFamily, family); return IPvX(0); /* Not Reached */ } IPvX IPvX::mask_by_prefix_len(uint32_t prefix_len) const throw (InvalidNetmaskLength) { if (_af == AF_INET) return get_ipv4().mask_by_prefix_len(prefix_len); else return get_ipv6().mask_by_prefix_len(prefix_len); } string IPvX::str() const { if (_af == AF_INET) return get_ipv4().str(); else return get_ipv6().str(); } uint32_t IPvX::mask_len() const { if (is_ipv4()) return get_ipv4().mask_len(); else return get_ipv6().mask_len(); } /** * Copy the raw address to memory pointed by @to. * @return the number of copied octets. */ size_t IPvX::copy_out(uint8_t *to_uint8) const { memcpy(to_uint8, _addr, addr_bytelen()); return (addr_bytelen()); } /** * Copy the raw address to @in_addr. * @return the number of copied octets. */ size_t IPvX::copy_out(struct in_addr& to_in_addr) const throw (InvalidFamily) { if (_af != AF_INET) xorp_throw(InvalidFamily, _af); return (copy_out((uint8_t *)&to_in_addr)); } /** * Copy the raw address to @in6_addr. * @return the number of copied octets. */ size_t IPvX::copy_out(struct in6_addr& to_in6_addr) const throw (InvalidFamily) { if (_af != AF_INET6) xorp_throw(InvalidFamily, _af); return (copy_out((uint8_t *)&to_in6_addr)); } /** * Copy the raw address to @to_sockaddr, and assign appropriately * the rest of the fields in @to_sockaddr. * @return the number of copied octets. */ size_t IPvX::copy_out(struct sockaddr& to_sockaddr) const throw (InvalidFamily) { struct sockaddr *sa = &to_sockaddr; switch (_af) { case AF_INET: return (copy_out(*sockaddr2sockaddr_in(sa))); case AF_INET6: return (copy_out(*sockaddr2sockaddr_in6(sa))); default: xorp_throw(InvalidFamily, _af); } return ((size_t)-1); } /** * Copy the raw address to @to_sockaddr_storage, and assign appropriately * the rest of the fields in @to_sockaddr_storage. * @return the number of copied octets. */ size_t IPvX::copy_out(struct sockaddr_storage& to_sockaddr_storage) const throw (InvalidFamily) { struct sockaddr *sa = sockaddr_storage2sockaddr(&to_sockaddr_storage); switch (_af) { case AF_INET: return (copy_out(*sockaddr2sockaddr_in(sa))); case AF_INET6: return (copy_out(*sockaddr2sockaddr_in6(sa))); default: xorp_throw(InvalidFamily, _af); } return ((size_t)-1); } /** * Copy the raw address to @to_sockaddr_in, and assign appropriately * the rest of the fields in @to_sockaddr_in. * @return the number of copied octets. */ size_t IPvX::copy_out(struct sockaddr_in& to_sockaddr_in) const throw (InvalidFamily) { switch (_af) { case AF_INET: memset(&to_sockaddr_in, 0, sizeof(to_sockaddr_in)); #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN to_sockaddr_in.sin_len = sizeof(to_sockaddr_in); #endif to_sockaddr_in.sin_family = _af; to_sockaddr_in.sin_port = 0; // XXX: not used return (copy_out(to_sockaddr_in.sin_addr)); default: xorp_throw(InvalidFamily, _af); } return ((size_t)-1); } /** * Copy the raw address to @to_sockaddr_in6, and assign appropriately * the rest of the fields in @to_sockaddr_in6. * @return the number of copied octets. */ size_t IPvX::copy_out(struct sockaddr_in6& to_sockaddr_in6) const throw (InvalidFamily) { switch (_af) { case AF_INET6: memset(&to_sockaddr_in6, 0, sizeof(to_sockaddr_in6)); #ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN to_sockaddr_in6.sin6_len = sizeof(to_sockaddr_in6); #endif to_sockaddr_in6.sin6_family = _af; #ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID #ifdef IPV6_STACK_KAME // // XXX: In case of KAME the local interface index (also the link-local // scope_id) is encoded in the third and fourth octet of an IPv6 // address (for link-local unicast/multicast addresses or // interface-local multicast addresses only). // if (is_linklocal_unicast() || is_linklocal_multicast() || is_interfacelocal_multicast()) { uint32_t addr0 = ntohl(_addr[0]); uint16_t zoneid = (addr0 & 0xffff); // XXX: 16 bits only to_sockaddr_in6.sin6_scope_id = zoneid; } #endif // IPV6_STACK_KAME #endif // HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID return (copy_out(to_sockaddr_in6.sin6_addr)); default: xorp_throw(InvalidFamily, _af); } return ((size_t)-1); } /** * Copy a raw address of family @family from the memory pointed by @from_uint8. * @return the number of copied octets. */ size_t IPvX::copy_in(int family, const uint8_t *from_uint8) throw (InvalidFamily) { _af = family; switch (_af) { case AF_INET: memset(_addr, 0, sizeof(_addr)); // FALLTHROUGH case AF_INET6: memcpy(_addr, from_uint8, addr_bytelen()); return (addr_bytelen()); default: xorp_throw(InvalidFamily, _af); } return ((size_t)-1); } /** * Copy a raw address of family %AF_INET from @from_in_addr. * @return the number of copied octets. */ size_t IPvX::copy_in(const in_addr& from_in_addr) { return (copy_in(AF_INET, reinterpret_cast(&from_in_addr))); } /** * Copy a raw address of family %AF_INET6 from @from_in6_addr. * @return the number of copied octets. */ size_t IPvX::copy_in(const in6_addr& from_in6_addr) { return (copy_in(AF_INET6, reinterpret_cast(&from_in6_addr))); } /** * Copy a raw address from @from_sockaddr. * @return the number of copied octets. */ size_t IPvX::copy_in(const sockaddr& from_sockaddr) throw (InvalidFamily) { const struct sockaddr *sa = &from_sockaddr; switch (sa->sa_family) { case AF_INET: return (copy_in(*sockaddr2sockaddr_in(sa))); case AF_INET6: return (copy_in(*sockaddr2sockaddr_in6(sa))); default: xorp_throw(InvalidFamily, sa->sa_family); } return ((size_t)-1); } /** * Copy a raw address from @from_sockaddr_storage. * @return the number of copied octets. */ size_t IPvX::copy_in(const sockaddr_storage& from_sockaddr_storage) throw (InvalidFamily) { const struct sockaddr *sa = sockaddr_storage2sockaddr(&from_sockaddr_storage); switch (sa->sa_family) { case AF_INET: return (copy_in(*sockaddr2sockaddr_in(sa))); case AF_INET6: return (copy_in(*sockaddr2sockaddr_in6(sa))); default: xorp_throw(InvalidFamily, sa->sa_family); } return ((size_t)-1); } /** * Copy a raw address from @from_sockaddr_in. * @return the number of copied octets. */ size_t IPvX::copy_in(const sockaddr_in& from_sockaddr_in) throw (InvalidFamily) { _af = from_sockaddr_in.sin_family; switch (_af) { case AF_INET: return (copy_in(from_sockaddr_in.sin_addr)); default: xorp_throw(InvalidFamily, _af); } return ((size_t)-1); } /** * Copy a raw address from @from_sockaddr_in6. * @return the number of copied octets. */ size_t IPvX::copy_in(const sockaddr_in6& from_sockaddr_in6) throw (InvalidFamily) { _af = from_sockaddr_in6.sin6_family; switch (_af) { case AF_INET6: return (copy_in(from_sockaddr_in6.sin6_addr)); default: xorp_throw(InvalidFamily, _af); } return ((size_t)-1); } bool IPvX::is_zero() const { if (_af == AF_INET) return get_ipv4().is_zero(); return get_ipv6().is_zero(); } bool IPvX::is_unicast() const { if (_af == AF_INET) return get_ipv4().is_unicast(); return get_ipv6().is_unicast(); } bool IPvX::is_multicast() const { if (_af == AF_INET) return get_ipv4().is_multicast(); return get_ipv6().is_multicast(); } bool IPvX::is_class_a() const { if (_af == AF_INET) return get_ipv4().is_class_a(); return (false); // XXX: this method applies only for IPv4 } bool IPvX::is_class_b() const { if (_af == AF_INET) return get_ipv4().is_class_b(); return (false); // XXX: this method applies only for IPv4 } bool IPvX::is_class_c() const { if (_af == AF_INET) return get_ipv4().is_class_c(); return (false); // XXX: this method applies only for IPv4 } bool IPvX::is_experimental() const { if (_af == AF_INET) return get_ipv4().is_experimental(); return (false); // XXX: this method applies only for IPv4 } bool IPvX::is_linklocal_unicast() const { if (_af == AF_INET) return get_ipv4().is_linklocal_unicast(); return get_ipv6().is_linklocal_unicast(); } bool IPvX::is_interfacelocal_multicast() const { if (_af == AF_INET) return get_ipv4().is_interfacelocal_multicast(); return get_ipv6().is_interfacelocal_multicast(); } bool IPvX::is_linklocal_multicast() const { if (_af == AF_INET) return get_ipv4().is_linklocal_multicast(); return get_ipv6().is_linklocal_multicast(); } bool IPvX::is_loopback() const { if (_af == AF_INET) return get_ipv4().is_loopback(); return get_ipv6().is_loopback(); } /** * @return IP protocol version. */ uint32_t IPvX::ip_version() const throw (InvalidFamily) { if (_af == AF_INET) return (IPv4::ip_version()); if (_af == AF_INET6) return (IPv6::ip_version()); xorp_throw(InvalidFamily, _af); return ((uint32_t)-1); } /** * @return IP protocol version string. */ const string& IPvX::ip_version_str() const throw (InvalidFamily) { if (_af == AF_INET) return (IPv4::ip_version_str()); if (_af != AF_INET6) xorp_throw(InvalidFamily, _af); return (IPv6::ip_version_str()); } size_t IPvX::addr_bytelen(int family) throw (InvalidFamily) { if (family == AF_INET) return (IPv4::addr_bytelen()); if (family == AF_INET6) return (IPv6::addr_bytelen()); xorp_throw(InvalidFamily, family); return ((size_t)-1); } uint32_t IPvX::ip_multicast_base_address_mask_len(int family) throw (InvalidFamily) { if (family == AF_INET) return (IPv4::ip_multicast_base_address_mask_len()); if (family == AF_INET6) return (IPv6::ip_multicast_base_address_mask_len()); xorp_throw(InvalidFamily, family); return ((uint32_t)-1); } uint32_t IPvX::ip_class_a_base_address_mask_len(int family) throw (InvalidFamily) { if (family == AF_INET) return (IPv4::ip_class_a_base_address_mask_len()); // XXX: this method applies only for IPv4 xorp_throw(InvalidFamily, family); return ((uint32_t)-1); } uint32_t IPvX::ip_class_b_base_address_mask_len(int family) throw (InvalidFamily) { if (family == AF_INET) return (IPv4::ip_class_b_base_address_mask_len()); // XXX: this method applies only for IPv4 xorp_throw(InvalidFamily, family); return ((uint32_t)-1); } uint32_t IPvX::ip_class_c_base_address_mask_len(int family) throw (InvalidFamily) { if (family == AF_INET) return (IPv4::ip_class_c_base_address_mask_len()); // XXX: this method applies only for IPv4 xorp_throw(InvalidFamily, family); return ((uint32_t)-1); } uint32_t IPvX::ip_experimental_base_address_mask_len(int family) throw (InvalidFamily) { if (family == AF_INET) return (IPv4::ip_experimental_base_address_mask_len()); // XXX: this method applies only for IPv4 xorp_throw(InvalidFamily, family); return ((uint32_t)-1); } // // IPvX "Constants" // #define IPVX_CONSTANT_ACCESSOR(name) \ const IPvX& IPvX::name(int family) throw (InvalidFamily) \ { \ static const IPvX c4_##name (IPv4::name()); /* IPv4 constant */ \ static const IPvX c6_##name (IPv6::name()); /* IPv6 constant */ \ if (family == AF_INET) \ return c4_##name; \ if (family == AF_INET6) \ return c6_##name; \ xorp_throw(InvalidFamily, family); \ } // IPvX "Constant" that applies only for IPv4 #define IPVX_CONSTANT_ACCESSOR_IPV4(name) \ const IPvX& IPvX::name(int family) throw (InvalidFamily) \ { \ static const IPvX c4_##name (IPv4::name()); /* IPv4 constant */ \ if (family == AF_INET) \ return c4_##name; \ xorp_throw(InvalidFamily, family); \ } // IPvX "Constant" that applies only for IPv6 #define IPVX_CONSTANT_ACCESSOR_IPV6(name) \ const IPvX& IPvX::name(int family) throw (InvalidFamily) \ { \ static const IPvX c6_##name (IPv6::name()); /* IPv6 constant */ \ if (family == AF_INET6) \ return c6_##name; \ xorp_throw(InvalidFamily, family); \ } IPVX_CONSTANT_ACCESSOR(ZERO); IPVX_CONSTANT_ACCESSOR(ANY); IPVX_CONSTANT_ACCESSOR(ALL_ONES); IPVX_CONSTANT_ACCESSOR(LOOPBACK); IPVX_CONSTANT_ACCESSOR(MULTICAST_BASE); IPVX_CONSTANT_ACCESSOR(MULTICAST_ALL_SYSTEMS); IPVX_CONSTANT_ACCESSOR(MULTICAST_ALL_ROUTERS); IPVX_CONSTANT_ACCESSOR(DVMRP_ROUTERS); IPVX_CONSTANT_ACCESSOR(OSPFIGP_ROUTERS); IPVX_CONSTANT_ACCESSOR(OSPFIGP_DESIGNATED_ROUTERS); IPVX_CONSTANT_ACCESSOR(RIP2_ROUTERS); IPVX_CONSTANT_ACCESSOR(PIM_ROUTERS); IPVX_CONSTANT_ACCESSOR(SSM_ROUTERS); IPVX_CONSTANT_ACCESSOR_IPV4(CLASS_A_BASE); IPVX_CONSTANT_ACCESSOR_IPV4(CLASS_B_BASE); IPVX_CONSTANT_ACCESSOR_IPV4(CLASS_C_BASE); IPVX_CONSTANT_ACCESSOR_IPV4(EXPERIMENTAL_BASE); xorp/libxorp/minitraits.hh0000664000076400007640000000473011421137511016043 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/libxorp/minitraits.hh,v 1.10 2008/10/02 21:57:32 bms Exp $ #ifndef __LIBXORP_MINITRAITS_HH__ #define __LIBXORP_MINITRAITS_HH__ /** * @short Class to determine subset of type traits. * * This class can be use to determine the non-const form of a type. * It is a temporary fix for g++ 2.96 (Redhat) which has problems * tracking const pointer types in templates. */ template class MiniTraits { template struct UnConst { typedef U Result; }; template struct UnConst { typedef U Result; }; public: typedef typename UnConst::Result NonConst; }; /** * @short Class to determine if two types are base and derived. * * This class tests whether a pointer for type B is useable as pointer * to type D. Typically, this implies that B is a base for D. It may also * imply that B is void, or B and D are the same type. * * How this works? Overloaded definition of function X::f(). The * first of which takes a const B* pointer as an argument and returns * are char. The second of which takes a ... and returns a double. * sizeof is used to determine the size of the return type that would * used if the code were executed. Thus, if B and D are compatible * pointer types then sizeof(X::f()) for both of them is sizeof(char). */ template class BaseAndDerived { struct X { static char f(const B*); static double f(...); }; public: static const bool True = ( sizeof(X::f((D*)0)) == sizeof(X::f((B*)0)) ); }; #endif // __LIBXORP_MINITRAITS_HH__ xorp/libxorp/mac.hh0000664000076400007640000001706111421137511014421 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/libxorp/mac.hh,v 1.32 2008/10/10 00:38:20 pavlin Exp $ #ifndef __LIBXORP_MAC_HH__ #define __LIBXORP_MAC_HH__ #include "libxorp/xorp.h" #include "libxorp/ether_compat.h" #include "libxorp/exceptions.hh" /** * @short IEEE standard 48-bit address. */ class Mac { public: /** * Default constructor. */ Mac(); /** * Constructor from a (uint8_t *) memory pointer. * * @param from_uint8 the pointer to the memory to copy the address value * from. */ explicit Mac(const uint8_t* from_uint8); /** * Constructor from a string. * * @param from_cstring C-style string of the form XX:XX:XX:XX:XX:XX * where X represents a hex-digit. * @throws InvalidString if string passed does not match expected format. */ Mac(const char* from_cstring) throw (InvalidString); /** * Constructor from ether_addr structure. * * @param from_ether_addr the ether_addr structure to construct the * Mac address from. */ Mac(const struct ether_addr& from_ether_addr); /** * Constructor from sockaddr structure. * * @param from_sockaddr the sockaddr structure to construct the * Mac address from. */ Mac(const struct sockaddr& from_sockaddr); /** * Copy the Mac raw address to specified memory location. * * @param: to_uint8 the pointer to the memory to copy the address to. * @return the number of copied octets. */ size_t copy_out(uint8_t* to_uint8) const; /** * Copy the Mac raw address to ether_addr structure. * * @param to_ether_addr the storage to copy the address to. * @return the number of copied octets. */ size_t copy_out(struct ether_addr& to_ether_addr) const; /** * Copy the Mac raw address to sockaddr structure. * * @param to_sockaddr the storage to copy the address to. * @return the number of copied octets. */ size_t copy_out(struct sockaddr& to_sockaddr) const; /** * Copy a raw Mac address from specified memory location into * Mac container. * * @param from_uint8 the memory address to copy the address from. * @return the number of copied octets. */ size_t copy_in(const uint8_t* from_uint8); /** * Copy a raw Mac address from ether_addr structure into Mac container. * * @param from_ether_addr the storage to copy the address from. * @return the number of copied octets. */ size_t copy_in(const struct ether_addr& from_ether_addr); /** * Copy a raw Mac address from sockaddr structure into Mac container. * * @param from_sockaddr the storage to copy the address from. * @return the number of copied octets. */ size_t copy_in(const struct sockaddr& from_sockaddr); /** * Copy a Mac address from a string. * * @param from_cstring C-style string of the form XX:XX:XX:XX:XX:XX * where X represents a hex-digit. * @throws InvalidString if string passed does not match expected format. */ size_t copy_in(const char* from_cstring) throw (InvalidString); /** * Less-Than Operator * * @param other the right-hand operand to compare against. * @return true if the left-hand operand is numerically smaller than the * right-hand operand. */ bool operator<(const Mac& other) const; /** * Equality Operator * * @param other the right-hand operand to compare against. * @return true if the left-hand operand is numerically same as the * right-hand operand. */ bool operator==(const Mac& other) const; /** * Not-Equal Operator * * @param other the right-hand operand to compare against. * @return true if the left-hand operand is numerically not same as the * right-hand operand. */ bool operator!=(const Mac& other) const; /** * Convert this address from binary form to presentation format. * * @return C++ string with the human-readable ASCII representation * of the address. */ string str() const; /** * Get the raw value of this address. * * @return the value of this MAC address as a pointer to an array * of unsigned bytes. */ const uint8_t* addr() const { return _addr; } /** * Get the size of the raw Mac address (in octets). * * @return the size of the raw Mac address (in octets). */ static size_t addr_bytelen() { return (ADDR_BYTELEN); } /** * Get the size of the raw Mac address (in number of bits). * * @return the size of the raw Mac address (in number of bits). */ static uint32_t addr_bitlen() { return (ADDR_BITLEN); } /** * Test if this address is numerically zero. * * @return true if the address is numerically zero. */ bool is_zero() const { return *this == ZERO(); } /** * Test if this address is a valid unicast address. * * @return true if the address is a valid unicast address. */ bool is_unicast() const; /** * Test if this address is a valid multicast address. * * @return true if the address is a valid multicast address. */ bool is_multicast() const; /** * Number of bits in address as a constant. */ static const uint32_t ADDR_BITLEN = 48; /** * Number of bytes in address as a constant. */ static const uint32_t ADDR_BYTELEN = ADDR_BITLEN / 8; /** * The multicast bit in the first octet of the address. */ static const uint8_t MULTICAST_BIT = 0x1; /** * Pre-defined Mac address constants. */ static const Mac& ZERO(); static const Mac& ALL_ONES(); static const Mac& BROADCAST(); static const Mac& STP_MULTICAST(); static const Mac& LLDP_MULTICAST(); static const Mac& GMRP_MULTICAST(); static const Mac& GVRP_MULTICAST(); private: uint8_t _addr[ADDR_BYTELEN]; // The address value (in network-order) }; struct MacConstants { static const Mac zero, all_ones, broadcast, stp_multicast, lldp_multicast, gmrp_multicast, gvrp_multicast; }; inline const Mac& Mac::ZERO() { return MacConstants::zero; } inline const Mac& Mac::ALL_ONES() { return MacConstants::all_ones; } inline const Mac& Mac::BROADCAST() { return MacConstants::broadcast; } inline const Mac& Mac::STP_MULTICAST() { return MacConstants::stp_multicast; } inline const Mac& Mac::LLDP_MULTICAST() { return MacConstants::lldp_multicast; } inline const Mac& Mac::GMRP_MULTICAST() { return MacConstants::gmrp_multicast; } inline const Mac& Mac::GVRP_MULTICAST() { return MacConstants::gvrp_multicast; } #endif // __LIBXORP_MAC_HH__ xorp/libxorp/win_dispatcher.cc0000664000076400007640000005032111631760261016655 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "libxorp/libxorp_module.h" #include "libxorp/xorp.h" #ifdef HOST_OS_WINDOWS // This entire file is for Windows only. #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/win_io.h" #include "libxorp/xorpfd.hh" #include "libxorp/timeval.hh" #include "libxorp/clock.hh" #include "libxorp/callback.hh" #include "libxorp/ioevents.hh" #include "libxorp/eventloop.hh" #include "libxorp/win_dispatcher.hh" static inline int _wsa2ioe(const long wsaevent) { switch (wsaevent) { case FD_READ: return IOT_READ; case FD_WRITE: return IOT_WRITE; case FD_OOB: return IOT_EXCEPTION; case FD_ACCEPT: return IOT_ACCEPT; case FD_CONNECT: return IOT_CONNECT; case FD_CLOSE: return IOT_DISCONNECT; } return -1; } static inline long _ioe2wsa(const IoEventType ioevent) { switch (ioevent) { case IOT_READ: return FD_READ; case IOT_WRITE: return FD_WRITE; case IOT_EXCEPTION: return FD_OOB; case IOT_ACCEPT: return FD_ACCEPT; case IOT_CONNECT: return FD_CONNECT; case IOT_DISCONNECT: return FD_CLOSE; case IOT_ANY: return (FD_READ|FD_WRITE|FD_OOB|FD_ACCEPT|FD_CONNECT|FD_CLOSE); } return 0; } WinDispatcher::WinDispatcher(ClockBase* clock) : _clock(clock), _descriptor_count(0) { // Avoid introducing a circular dependency upon libcomm by calling // WSAStartup() and WSACleanup() ourselves. It's safe for the same // thread within the same process to call it more than once. WSADATA wsadata; WORD version = MAKEWORD(2, 2); int retval = WSAStartup(version, &wsadata); if (retval != 0) { XLOG_FATAL("WSAStartup() error: %s", win_strerror(GetLastError())); } _handles.reserve(MAXIMUM_WAIT_OBJECTS); } WinDispatcher::~WinDispatcher() { // Purge all pending socket event notifications from Winsock. for (SocketEventMap::iterator ii = _socket_event_map.begin(); ii != _socket_event_map.end(); ++ii) { WSAEventSelect(ii->first, NULL, 0); } WSACleanup(); } bool WinDispatcher::add_socket_cb(XorpFd& fd, IoEventType type, const IoEventCb& cb, int priority) { UNUSED(priority); // TODO: XXX: we should really use this // XXX: Currently we only support 1 callback per socket/event tuple. // Check that the socket does not already have a callback // registered for it. IoEventMap::iterator ii = _callback_map.find(IoEventTuple(fd, type)); if (ii != _callback_map.end()) { debug_msg("callback already registered.\n"); return false; } long oldmask; long newmask = _ioe2wsa(type); if (newmask == 0 || (newmask & ~WSAEVENT_SUPPORTED) != 0) { debug_msg("IoEventType does not map to a valid Winsock async " "event mask.\n"); return false; } HANDLE hevent = WSA_INVALID_EVENT; // Check if there are notifications already requested for // this socket. Listening sockets must only have IOT_ACCEPT pending. SocketEventMap::iterator jj = _socket_event_map.find(fd); if (jj != _socket_event_map.end()) { hevent = jj->second.first; oldmask = jj->second.second; if (((oldmask & ~FD_ACCEPT) && (newmask & FD_ACCEPT)) || ((oldmask & FD_ACCEPT) && (newmask & ~FD_ACCEPT))) { debug_msg("attempt to register a socket for other events " "in addition to IOT_ACCEPT.\n"); return false; } } else { // // This is a new map entry. Create an event object and // wire it up to the main dispatch. // hevent = WSACreateEvent(); oldmask = 0; #if 1 // Paranoid check to see if new event handle is already // in the vector we pass off to WFMO. vector::iterator qq; for (qq = _handles.begin(); qq != _handles.end(); qq++) { if (*qq == hevent) { XLOG_FATAL("New event handle already exists in _handles"); } } #endif // Insert the new entry into the socket event map. pair p = _socket_event_map.insert(std::make_pair(fd, std::make_pair(hevent, oldmask))); XLOG_ASSERT(p.second == true); jj = p.first; // Insert the new event object into the WFMO handle vector, // and the event->socket map used for WFMO dispatch. _handles.push_back(hevent); XLOG_ASSERT(_handles.size() < MAXIMUM_WAIT_OBJECTS); pair q = _event_socket_map.insert(std::make_pair(hevent, fd)); XLOG_ASSERT(q.second == true); // Increment the descriptor count only if this is a new entry _descriptor_count++; } XLOG_ASSERT(hevent != WSA_INVALID_EVENT); XLOG_ASSERT(jj != _socket_event_map.end()); // An existing map entry merely needs to have its WSA event mask // updated, and a call to WSAEventSelect() to update the mask. newmask |= oldmask; int retval = WSAEventSelect(fd, hevent, newmask); if (retval != 0) { // XXX: Need more paranoid error checking here. debug_msg("WSAEventSelect() error: %s", win_strerror(WSAGetLastError())); } jj->second.second = newmask; // Wire up the XORP I/O callback. _callback_map.insert(std::make_pair(IoEventTuple(fd, type), cb)); return true; } bool WinDispatcher::add_handle_cb(XorpFd& fd, IoEventType type, const IoEventCb& cb, int priority) { UNUSED(priority); // TODO: XXX: we should really use this // You cannot currently register for anything other // than an IOT_EXCEPTION event on a Windows object handle because // there is no way of telling why an object was signalled -- // they are 'special'. if (fd.is_pipe() || fd.is_console()) { XLOG_ASSERT(type == IOT_READ || type == IOT_DISCONNECT); } else { XLOG_ASSERT(type == IOT_EXCEPTION); } // Check that we haven't exceeded the handle limit. if (_handles.size() == MAXIMUM_WAIT_OBJECTS) { XLOG_WARNING("exceeded Windows object handle count\n"); return false; } // XXX: Currently we only support 1 callback per Windows object/event. // Check that the object does not already have a callback // registered for it. IoEventMap::iterator ii = _callback_map.find(IoEventTuple(fd, type)); if (ii != _callback_map.end()) { XLOG_WARNING("callback already registered for object\n"); return false; } // Register the callback. Add fd to the object handle array. _callback_map.insert(std::make_pair(IoEventTuple(fd, type), cb)); if (fd.is_pipe()) { _polled_pipes.push_back(fd); } else { _handles.push_back(fd); } _descriptor_count++; return true; } bool WinDispatcher::add_ioevent_cb(XorpFd fd, IoEventType type, const IoEventCb& cb, int priority) { debug_msg("Adding event %d to object %s\n", type, fd.str().c_str()); switch (fd.type()) { case XorpFd::FDTYPE_SOCKET: return add_socket_cb(fd, type, cb, priority); break; case XorpFd::FDTYPE_PIPE: case XorpFd::FDTYPE_CONSOLE: case XorpFd::FDTYPE_PROCESS: return add_handle_cb(fd, type, cb, priority); break; default: break; } return false; } bool WinDispatcher::remove_socket_cb(XorpFd& fd, IoEventType type) { bool unregistered = false; int retval; SocketEventMap::iterator ii = _socket_event_map.find(fd); if (ii == _socket_event_map.end()) { debug_msg("Attempt to remove unmapped callback of type %d " "from socket %s.\n", type, fd.str().c_str()); return false; } // Compute the new event mask. Set it to zero if we were asked to // deregister this socket completely. long delmask = _ioe2wsa(type); XLOG_ASSERT(delmask != -1); long newmask = ii->second.second & ~delmask; HANDLE hevent = ii->second.first; if (newmask != 0) { // WSA event mask is non-zero; just update mask. retval = WSAEventSelect(fd, hevent, newmask); if (retval != 0) { debug_msg("WSAEventSelect() error: %s", win_strerror(WSAGetLastError())); } ii->second.second = newmask; } else { // WSA event mask is now zero; purge completely. retval = WSAEventSelect(fd, NULL, 0); if (retval != 0) { debug_msg("WSAEventSelect() error: %s", win_strerror(WSAGetLastError())); } bool found = false; vector::iterator qq; for (qq = _handles.begin(); qq != _handles.end(); qq++) { if (*qq == hevent) { found = true; qq = _handles.erase(qq); break; } } XLOG_ASSERT(found == true); WSACloseEvent(hevent); EventSocketMap::iterator jj = _event_socket_map.find(hevent); XLOG_ASSERT(jj != _event_socket_map.end()); _socket_event_map.erase(ii); _event_socket_map.erase(jj); // Decrement the descriptor count only if the entry was purged _descriptor_count--; } // // Deal with IOT_ANY shorthand removals as per old-style // dispatch management code (some code notably BGP does not // remove its callbacks individually). // // XXX: Requests to remove all callbacks in the old model's // shorthand will mean the return value of this function // can't be relied upon as its meaning is then overloaded. // int imin, imax; if (type == IOT_ANY) { imin = IOT_READ; imax = IOT_DISCONNECT; } else { imin = imax = (int)type; } for (int i = imin; i <= imax; i++) { IoEventType itype = static_cast(i); IoEventMap::iterator jj = _callback_map.find(IoEventTuple(fd, itype)); if (jj != _callback_map.end()) { _callback_map.erase(jj); unregistered = true; } else { debug_msg("Attempt to remove a nonexistent callback of " "type %d from socket %s.\n", itype, fd.str().c_str()); } } return unregistered; } bool WinDispatcher::remove_handle_cb(XorpFd& fd, IoEventType type) { if (fd.is_pipe()) { XLOG_ASSERT(type == IOT_READ || type == IOT_DISCONNECT); for (vector::iterator ii = _polled_pipes.begin(); ii != _polled_pipes.end(); ++ii) { if (*ii == fd) { ii = _polled_pipes.erase(ii); _callback_map.erase(IoEventTuple(fd, type)); _descriptor_count--; return true; } } } else { if (fd.is_console()) { XLOG_ASSERT(type == IOT_READ); } else { XLOG_ASSERT(type == IOT_EXCEPTION); } for (vector::iterator ii = _handles.begin(); ii != _handles.end(); ++ii) { if (*ii == fd) { ii = _handles.erase(ii); _callback_map.erase(IoEventTuple(fd, type)); _descriptor_count--; return true; } } } return false; } bool WinDispatcher::remove_ioevent_cb(XorpFd fd, IoEventType type) { debug_msg("Removing event %d from object %s\n", type, fd.str().c_str()); switch (fd.type()) { case XorpFd::FDTYPE_SOCKET: return remove_socket_cb(fd, type); case XorpFd::FDTYPE_PIPE: case XorpFd::FDTYPE_CONSOLE: case XorpFd::FDTYPE_PROCESS: return remove_handle_cb(fd, type); break; default: break; } return false; } int WinDispatcher::get_ready_priority() { // TODO: XXX: THIS IS COMPLETELY BOGUS return XorpTask::PRIORITY_DEFAULT; } bool WinDispatcher::ready() { DWORD retval; // TODO: XXX: THIS IS PROBABLY BOGUS for (vector::iterator ii = _polled_pipes.begin(); ii != _polled_pipes.end(); ++ii) { ssize_t result = win_pipe_read(*ii, NULL, 0); if (result == WINIO_ERROR_HASINPUT || result == WINIO_ERROR_DISCONNECT) { return true; } } if (_handles.empty()) { return false; } retval = WaitForMultipleObjects(_handles.size(), &_handles[0], FALSE, 0); if (retval == WAIT_TIMEOUT) { return false; } return true; } void WinDispatcher::wait_and_dispatch(int ms) { bool more_to_do = true; bool first = true; // // Wait or sleep. Do not enter a state where APCs may be called; // they are not compatible with the XORP event loop. // If we need to fix the sleep quantum to deal with polled objects, do so. // if ((!_polled_pipes.empty()) && (ms > POLLED_INTERVAL_MS || ms < 0)) ms = POLLED_INTERVAL_MS; vector tmp_handles(_handles); while (more_to_do) { more_to_do = do_wait_and_dispatch(tmp_handles, ms, first); // After initial call, do the rest with zero timeout so we // can process the rest of the 'ready' handles. ms = 0; first = false; } } /** Returns true if more work to be done. */ bool WinDispatcher::do_wait_and_dispatch(vector& handles, int ms, bool first) { DWORD retval; if (handles.empty()) { // We probably don't want to sleep forever with no pending waits, // as Win32 doesn't have the same concept of signals as UNIX does. if (ms > 0) { Sleep(ms); } retval = WAIT_TIMEOUT; } else { retval = WaitForMultipleObjects(handles.size(), &handles[0], FALSE, ms); } //XLOG_WARNING("Done waiting, retval: %i", retval); _clock->advance_time(); if (first) { // Reads need to be handled first because they may come from // dying processes picked up by the handle poll. if (!_polled_pipes.empty()) dispatch_pipe_reads(); } // The order of the if clauses here is important. if (retval == WAIT_FAILED) { DWORD lasterr = GetLastError(); if (lasterr == ERROR_INVALID_HANDLE && !handles.empty()) { callback_bad_handle(); } else { // Programming error. XLOG_FATAL("WaitForMultipleObjects(%d,%p,%d,%d) failed " "with the error code %lu (%s). " "Please report this error to the XORP core team.", handles.empty() ? 0 : handles.size(), handles.empty() ? NULL : &handles[0], FALSE, ms, lasterr, win_strerror(lasterr)); } } else if (retval == WAIT_TIMEOUT) { // The timeout period elapsed. This is normal. Fall through. } else if (retval <= (WAIT_OBJECT_0 + handles.size() - 1)) { // // An object in handles was signalled. Dispatch its callback. // Check if it's an event associated with a socket first. // HANDLE eh = handles[retval - WAIT_OBJECT_0]; EventSocketMap::iterator qq = _event_socket_map.find(eh); if (qq != _event_socket_map.end()) { dispatch_sockevent(qq->first, qq->second); } else { // It's not an Event, so it must be something else. // Figure out what it is, and deal with it. XorpFd efd(eh); XLOG_ASSERT(efd.type() != XorpFd::FDTYPE_SOCKET); XLOG_ASSERT(efd.type() != XorpFd::FDTYPE_PIPE); IoEventType evtype = (efd.type() == XorpFd::FDTYPE_CONSOLE) ? IOT_READ : IOT_EXCEPTION; IoEventMap::iterator jj = _callback_map.find(IoEventTuple(efd, evtype)); if (jj != _callback_map.end()) { jj->second->dispatch(efd, evtype); } else { XLOG_ERROR("no callback for object %s", efd.str().c_str()); } } if (handles.size() > 1) { for (vector::iterator ii = handles.begin(); ii != handles.end(); ++ii) { if (*ii == eh) { ii = handles.erase(ii); break; } } return true; } return false; } else { // Programming error. XLOG_FATAL("WaitForMultipleObjects(%d,%p,%d,%d) returned an " "unhandled return value %lu. " "Please report this error to the XORP core team.", handles.empty() ? 0 : handles.size(), handles.empty() ? NULL : &handles[0], FALSE, ms, retval); } return false; } void WinDispatcher::dispatch_pipe_reads() { ssize_t result; for (vector::iterator ii = _polled_pipes.begin(); ii != _polled_pipes.end(); ii++) { result = win_pipe_read(*ii, NULL, 0); if (result == WINIO_ERROR_HASINPUT) { // // Polled pipes *must* have a read handler. // IoEventMap::iterator jj = _callback_map.find( IoEventTuple(*ii, IOT_READ)); XLOG_ASSERT(jj != _callback_map.end()); jj->second->dispatch(*ii, IOT_READ); } else if (result == WINIO_ERROR_DISCONNECT) { // // Polled pipes may optionally have a disconnection handler. // This is used by the FEA. // IoEventMap::iterator jj = _callback_map.find( IoEventTuple(*ii, IOT_DISCONNECT)); if (jj != _callback_map.end()) { jj->second->dispatch(*ii, IOT_DISCONNECT); } } } } void WinDispatcher::dispatch_sockevent(HANDLE hevent, XorpFd fd) { int err; WSANETWORKEVENTS netevents; memset(&netevents, 0, sizeof(netevents)); err = WSAEnumNetworkEvents(fd, hevent, &netevents); if (err != 0) { if (WSAGetLastError() == WSAENOTSOCK) { // The socket might have been closed or EOF'd by the time // we get the event. XLOG_WARNING("WSAEnumNetworkEvents() returned WSAENOTSOCK for " "socket %s", fd.str().c_str()); callback_bad_socket(fd); } return; } // Short circuit mask check if no events occured. if (netevents.lNetworkEvents == 0) { debug_msg("event %p signalled but event mask is zero\n", hevent); return; } for (int evbit = 0; evbit < FD_MAX_EVENTS ; evbit++) { int evflag = 1 << evbit; if ((evflag & netevents.lNetworkEvents) == evflag) { debug_msg("processing event %d\n", evflag); int itype = _wsa2ioe(evflag); if (itype == -1) { debug_msg("Can't map WSA event %d to an IoEventType", evflag); continue; } IoEventType type = static_cast(itype); IoEventMap::iterator jj = _callback_map.find(IoEventTuple(fd, type)); if (jj == _callback_map.end()) { debug_msg("IoEventType %d on socket %s does not map to a " "callback.\n", itype, fd.str().c_str()); continue; } TimeVal start; if (eloop_trace.on()) { _clock->advance_time(); _clock->current_time(start); } jj->second->dispatch(fd, type); if (eloop_trace.on()) { TimeVal now; _clock->advance_time(); _clock->current_time(now); if (now.to_ms() > start.to_ms() + 1000) { XLOG_INFO("socket callback took too long to run: %lims, fd: %s type: %i\n", (long)(now.to_ms() - start.to_ms()), cstring(fd), (int)(type)); } } } } } // // If a socket is detected as being bad, force all callbacks // registered for it to be invoked, just like // SelectorList::callback_bad_fd() on the UNIX path. // // Just as with the UNIX path, removal of the bad socket is // the caller's responsibility. // // We can't detect this case at WFMO level because the condition // has been detected on the socket, not the associated event object // which XORP actually waits on. // void WinDispatcher::callback_bad_socket(XorpFd& fd) { for (int i = IOT_READ; i < IOT_DISCONNECT; ++i) { IoEventType type = static_cast(i); IoEventMap::iterator jj = _callback_map.find(IoEventTuple(fd, type)); if (jj == _callback_map.end()) continue; jj->second->dispatch(fd, type); } } // // If an opaque Windows handle is detected as being bad, force // all of its callbacks to be invoked. // // Just as with the UNIX path, removal of the bad handle is // the caller's responsibility. // // This sweeps the entire handle vector for bad handles. // void WinDispatcher::callback_bad_handle() { DWORD dwFlags; for (vector::iterator ii = _handles.begin(); ii != _handles.end(); ++ii) { if (GetHandleInformation(*ii, &dwFlags) == 0) { EventSocketMap::iterator jj = _event_socket_map.find(*ii); if (jj != _event_socket_map.end()) { // // The bad handle is an Event handle associated with a // socket and therefore managed within the event framework. // // Bad sockets are meant to be caught (and handled) by // dispatch_sockevent() and callback_bad_socket() // respectively. // // This means Something Is Horribly Wrong. Abort fatally. // XLOG_FATAL("Event handle associated with a socket is bad."); } // // Force all of this handle's callbacks to be invoked. // XorpFd fd(*ii); for (int i = IOT_READ; i < IOT_DISCONNECT; ++i) { IoEventType type = static_cast(i); IoEventMap::iterator kk = _callback_map.find(IoEventTuple(fd, type)); if (kk == _callback_map.end()) continue; kk->second->dispatch(fd, type); } } } } #endif // HOST_OS_WINDOWS xorp/libxorp/debug.c0000664000076400007640000000771211540225527014603 0ustar greearbgreearb/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- */ /* vim:set sts=4 ts=8: */ /* * Copyright (c) 2001-2011 XORP, Inc and Others * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License, Version * 2.1, June 1999 as published by the Free Software Foundation. * Redistribution and/or modification of this program under the terms of * any other version of the GNU Lesser General Public License is not * permitted. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, * see the GNU Lesser General Public License, Version 2.1, a copy of * which can be found in the XORP LICENSE.lgpl file. * * XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; * http://xorp.net */ #include "libxorp/xorp.h" #include "libxorp/debug.h" static uint32_t dbg_indent = 0; void _xdebug_set_indent(uint32_t n) { assert(n < 10000); dbg_indent = n; } static const char* _xdebug_preamble(const char* file, int line, const char* func) { static size_t sbuf_bytes = 256; static char* sbuf = 0; static int spid = 0; size_t req_bytes; if (sbuf == 0) { sbuf = (char*)malloc(sbuf_bytes); spid = (int)getpid(); } req_bytes = 2 * 20 + strlen(file) + 1; if (func) req_bytes += strlen(func); if (req_bytes > sbuf_bytes) { sbuf_bytes = req_bytes; sbuf = (char*)realloc(sbuf, sbuf_bytes); } #ifdef XORP_LOG_PRINT_USECS struct timeval tv; gettimeofday(&tv, NULL); unsigned long long us = tv.tv_usec; us += (tv.tv_sec * 1000000); /* Format is [time-us] + [] * * The and formatting is for cutting and pasting as * arguments to emacs, vi, vim, nedit, etc, but not ed :-( */ if (func) { #ifdef HOST_OS_WINDOWS snprintf(sbuf, sbuf_bytes, "[ %d %I64u %+5d %s %s ] ", spid, us, line, file, func); #else snprintf(sbuf, sbuf_bytes, "[ %d %llu %+5d %s %s ] ", spid, us, line, file, func); #endif } else { #ifdef HOST_OS_WINDOWS snprintf(sbuf, sbuf_bytes, "[ %d %I64u %+5d %s ] ", spid, us, line, file); #else snprintf(sbuf, sbuf_bytes, "[ %d %llu %+5d %s ] ", spid, us, line, file); #endif } #else /* Format is [time-us] + [] * * The and formatting is for cutting and pasting as * arguments to emacs, vi, vim, nedit, etc, but not ed :-( */ if (func) { snprintf(sbuf, sbuf_bytes, "[ %d %+5d %s %s ] ", spid, line, file, func); } else { snprintf(sbuf, sbuf_bytes, "[ %d %+5d %s ] ", spid, line, file); } #endif return sbuf; } /** Common printing routine */ __inline static void _xdebug_msg_va(const char* file, int line, const char* func, const char* fmt, va_list ap) { uint32_t i; fprintf(stderr, "%s", _xdebug_preamble(file, line, func)); for (i = 0; i < dbg_indent; i++) { fprintf(stderr, " "); } vfprintf(stderr, fmt, ap); } /** Debug printing function for systems with varargs preprocessors */ void _xdebug_msg_long(const char* file, int line, const char* func, const char* fmt, ...) { va_list ap; va_start(ap, fmt); _xdebug_msg_va(file, line, func, fmt, ap); va_end(ap); } /** State and functions for systems without varargs preprocessors */ static const char* the_file; static int the_line; static const char* the_func; void _xdebug_entry(const char* file, int line, const char* func) { the_file = file; the_line = line; the_func = func; } void _xdebug_msg_short(const char* fmt, ...) { va_list ap; va_start(ap, fmt); _xdebug_msg_va(the_file, the_line, the_func, fmt, ap); va_end(ap); } void _xdebug_null(const char* fmt, ...) { UNUSED(fmt); } xorp/libxorp/round_robin.cc0000664000076400007640000000556211421137511016172 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2006-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "libxorp_module.h" #include "libxorp/xorp.h" #include #include "libxorp/debug.h" #include "libxorp/eventloop.hh" #include "libxorp/xlog.h" #include "round_robin.hh" RoundRobinObjBase::RoundRobinObjBase() : _weight(0), _next(NULL), _prev(NULL) { } bool RoundRobinObjBase::scheduled() const { return (_next != NULL); } RoundRobinQueue::RoundRobinQueue() : _next_to_run(NULL), _run_count(0), _elements(0) { } void RoundRobinQueue::push(RoundRobinObjBase* obj, int weight) { XLOG_ASSERT(obj != NULL); XLOG_ASSERT(weight > 0); link_object(obj, weight); } void RoundRobinQueue::pop_obj(RoundRobinObjBase* obj) { XLOG_ASSERT(obj != NULL); unlink_object(obj); } void RoundRobinQueue::pop() { XLOG_ASSERT(_next_to_run != NULL); pop_obj(_next_to_run); } RoundRobinObjBase* RoundRobinQueue::get_next_entry() { RoundRobinObjBase* top = _next_to_run; if (top != NULL) { XLOG_ASSERT(_run_count < top->weight()); _run_count++; if (_run_count == top->weight()) { _next_to_run = _next_to_run->next(); _run_count = 0; } } return top; } void RoundRobinQueue::link_object(RoundRobinObjBase* obj, int weight) { obj->set_weight(weight); if (_next_to_run == NULL) { // This is the first element _next_to_run = obj; _run_count = 0; obj->set_next(obj); obj->set_prev(obj); } else { // // Insert just before the next entry to be run, so this is at the // end of the queue. // obj->set_next(_next_to_run); obj->set_prev(_next_to_run->prev()); obj->prev()->set_next(obj); obj->next()->set_prev(obj); } _elements++; } void RoundRobinQueue::unlink_object(RoundRobinObjBase* obj) { if (obj->next() == obj) { // This is the only item in the list _next_to_run = NULL; } else { if (_next_to_run == obj) { _next_to_run = obj->next(); _run_count = 0; } obj->prev()->set_next(obj->next()); obj->next()->set_prev(obj->prev()); } obj->set_prev(NULL); obj->set_next(NULL); _elements--; } xorp/libxorp/token.hh0000664000076400007640000000636211540224230015000 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/libxorp/token.hh,v 1.11 2008/10/02 21:57:36 bms Exp $ #ifndef __LIBXORP_TOKEN_HH__ #define __LIBXORP_TOKEN_HH__ // // Token related definitions. // #include "xorp.h" // // Constants definitions // // // Structures/classes, typedefs and macros // // // Global variables // // // Global functions prototypes // /** * Copy a token. * * If the token contains token separators, enclose it within quotations. * * @param token_org the token to copy. * @return the copy of the token with token separators enclosed within * quotations. */ string copy_token(const string& token_org); /** * Pop a token from a token line. * * @param token_line the token line to pop a token from. * @return the first token from the front of the line. Also, * @ref token_line is modified to exlude that token. */ string pop_token(string& token_line); /** * Test if a character is a token separator. * * Currently, the is_space(3) characters and '|' are considered as * token separators. * * @param c the character to test whether it is token separator. * @return true if @ref c is a token separator, otherwise false. */ bool is_token_separator(const char c); /** * Test if a token line contains more tokens. * * @param token_line the token line to test. * @return true if @ref token_line contains more tokens, otherwise false. */ bool has_more_tokens(const string& token_line); /** * Split a token line into a vector with the tokens. * * @param token_line the token line to split. * @return a vector with all tokens. */ vector token_line2vector(const string& token_line); /** * Split a token line into a list with the tokens. * * @param token_line the token line to split. * @return a list with all tokens. */ list token_line2list(const string& token_line); /** * Combine a vector with the tokens into a single line with spaces as * separators. * * @param token_vector the vector with the tokens. * @return a line with the tokens separated by spaces. */ string token_vector2line(const vector& token_vector); /** * Combine a list with the tokens into a single line with spaces as * separators. * * @param token_list the list with the tokens. * @return a line with the tokens separated by spaces. */ string token_list2line(const list& token_list); #endif // __LIBXORP_TOKEN_HH__ xorp/libxorp/win_dispatcher.hh0000664000076400007640000002144711631723627016703 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/libxorp/win_dispatcher.hh,v 1.23 2008/10/02 21:57:36 bms Exp $ #ifndef __LIBXORP_WIN_DISPATCHER_HH__ #define __LIBXORP_WIN_DISPATCHER_HH__ #include "libxorp/xorp.h" #include "libxorp/xorpfd.hh" #include "libxorp/timeval.hh" #include "libxorp/ioevents.hh" #include "libxorp/task.hh" #include #include #ifdef HOST_OS_WINDOWS class ClockBase; /** * A tuple specifying a Windows object and I/O event type. */ struct IoEventTuple { private: XorpFd _fd; IoEventType _type; public: IoEventTuple(XorpFd fd, IoEventType type) : _fd(fd), _type(type) {} XorpFd fd() { return (_fd); }; IoEventType type() { return (_type); }; bool operator ==(const IoEventTuple& rhand) const { return ((_fd == rhand._fd) && (_type == rhand._type)); } bool operator !=(const IoEventTuple& rhand) const { return (!((_fd == rhand._fd) && (_type == rhand._type))); } bool operator >(const IoEventTuple& rhand) const { if (_fd != rhand._fd) return ((int)_type > (int)rhand._type); else return (_fd > rhand._fd); } bool operator <(const IoEventTuple& rhand) const { if (_fd != rhand._fd) return (_fd < rhand._fd); else return ((int)_type < (int)rhand._type); } }; /** * A map of Windows object handle and I/O event type to an I/O callback for * each member. */ typedef map IoEventMap; /** * A map of Windows event handle to socket handle, used for faster * dispatch of socket events on return from WFMO(). * Note that XorpFd is used as the value type. */ typedef map EventSocketMap; /** * A value-type tuple of a Windows event handle and a WSA event mask. */ typedef std::pair MaskEventPair; /** * A map of sockets to a MaskEventPair tuple. */ typedef map SocketEventMap; /** * Mask of Winsock events for which the I/O event framework is able * to dispatch callbacks. */ #define WSAEVENT_SUPPORTED \ (FD_READ | FD_WRITE | FD_OOB | FD_ACCEPT | FD_CONNECT | FD_CLOSE) /** * @short A class to provide an interface to Windows native I/O multiplexing. * * A WinDispatcher provides an entity where callbacks for pending I/O * operations on Windows sockets and other Windows system objects may * be registered. The callbacks are invoked when the @ref wait_and_dispatch * method is called, and I/O is pending on a descriptor, or a Windows * object's state has been set to signalled. * * The entire event loop is driven from the Win32 API call * WaitForMultipleObjects(), which means there is a hard 64-object limit. * * This class is the glue which binds XORP to Windows. There are tricks * in the AsyncFileOperator classes which are tightly coupled with what * happens here. Most of these tricks exist because systems-level * asynchronous I/O facilities cannot easily be integrated into XORP's * current design. * * There is no way of telling specific I/O events apart on objects other * than sockets without actually trying to service the I/O. The class * uses WSAEventSelect() internally to map socket handles to event * handles, and much of the complexity in this class is there to deal * with this schism in Windows low-level I/O. * * The class emulates the ability of UNIX-like systems to interrupt a * process waiting in select() for pending I/O on pipes, by using a * time-slice quantum to periodically poll the pipes. This quantum is * currently hard-coded to 250ms. * * Sockets are optimized to be the faster dispatch path, as this is where * the bulk of XORP I/O processing happens. * * WinDispatcher should only be exposed to @ref EventLoop. */ class WinDispatcher { public: /** * Default constructor. */ WinDispatcher(ClockBase *clock); /** * Destructor. */ virtual ~WinDispatcher(); /** * Add a hook for pending I/O operations on a callback. * * Only one callback may be registered for each possible IoEventType, * with the following exceptions. * * If the @ref XorpFd maps to a Windows socket handle, multiple * callbacks may be registered for different IoEventTypes, but one * and only one callback may be registered for the handle if a * callback is registered for the IOT_ACCEPT event. * * If the @ref XorpFd maps to a Windows pipe or console handle, * callbacks may only be registered for the IOT_READ and IOT_DISCONNECT * events. * * If the @ref XorpFd corresponds to any other kind of Windows object * handle, only a single callback may be registered, and the IoEventType * must be IOT_EXCEPTION. This mechanism is typically used to direct * the class to wait on Windows event or process handles. * * @param fd a Windows object handle encapsulated in a @ref XorpFd . * @param type the @ref IoEventType which should trigger the callback. * @param cb callback object which shall be invoked when the event is * triggered. * @param priority the XorpTask priority at which this callback should * be run. * @return true if function succeeds, false otherwise. */ bool add_ioevent_cb(XorpFd fd, IoEventType type, const IoEventCb& cb, int priority = XorpTask::PRIORITY_DEFAULT); /** * Remove hooks for pending I/O operations. * * @param fd the file descriptor. * @param type the @ref IoEventType to remove the callback for; the * special value IOT_ANY may be specified to remove all such callbacks. * @return true if function succeeds, false otherwise. */ bool remove_ioevent_cb(XorpFd fd, IoEventType type); /** * Find out if any of the selectors are ready. * * @return true if any selector is ready. */ bool ready(); /** * Find out the highest priority from the ready file descriptors. * * @return the priority of the highest priority ready file descriptor. */ int get_ready_priority(); /** * Wait for a pending I/O events and invoke callbacks when they * become ready. * * @param timeout the maximum period to wait for. */ void wait_and_dispatch(TimeVal& timeout) { if (timeout == TimeVal::MAXIMUM()) wait_and_dispatch(INFINITE); else wait_and_dispatch(timeout.to_ms()); } /** * Wait for a pending I/O events and invoke callbacks when they * become ready. * * @param millisecs the maximum period in milliseconds to wait for. */ void wait_and_dispatch(int ms); /** * Get the count of the descriptors that have been added. * * @return the count of the descriptors that have been added. */ size_t descriptor_count() const { return _descriptor_count; } protected: /** * No user-servicable parts here at the moment. */ void dispatch_sockevent(HANDLE hevent, XorpFd fd); private: /** Returns true if more work to be done. */ bool do_wait_and_dispatch(vector& handles, int ms, bool first); bool add_socket_cb(XorpFd& fd, IoEventType type, const IoEventCb& cb, int priority); bool add_handle_cb(XorpFd& fd, IoEventType type, const IoEventCb& cb, int priority); void callback_bad_handle(); void callback_bad_socket(XorpFd& fd); bool remove_socket_cb(XorpFd& fd, IoEventType type); bool remove_handle_cb(XorpFd& fd, IoEventType type); void dispatch_pipe_reads(); private: static const int POLLED_INTERVAL_MS = 250; private: ClockBase* _clock; // System clock IoEventMap _callback_map; // XorpFd + IoEventType -> Callback SocketEventMap _socket_event_map; // Socket -> Event + Mask EventSocketMap _event_socket_map; // Event -> Socket vector _handles; // All Win32 handles pending wait vector _polled_pipes; // Pipe handles pending poll size_t _descriptor_count; // Count for socket/event handlers }; #endif // HOST_OS_WINDOWS #endif // __LIBXORP_WIN_DISPATCHER_HH__ xorp/libxorp/build_info.prefix0000664000076400007640000000032211640674252016674 0ustar greearbgreearb // The build_info.cc file is Autogenerated, modifying by hand // will be a waste of time. You should modify the create_buildinfo.sh // script or the build_info.prefix file instead. #include "build_info.hh" xorp/libxorp/daemon.c0000664000076400007640000000612211703345405014751 0ustar greearbgreearb/* vim:set sts=4 ts=8: */ /*- * Copyright (c) 1990-2012 XORP, Inc and Others, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #if 0 #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)daemon.c 8.1 (Berkeley) 6/4/93"; #endif /* LIBC_SCCS and not lint */ #include __FBSDID("$FreeBSD: src/lib/libc/gen/daemon.c,v 1.8 2007/01/09 00:27:53 imp Exp $"); #endif #include "libxorp_module.h" #include "libxorp/xorp.h" #ifndef HOST_OS_WINDOWS #include #include #include #include #include #include #endif /* ! HOST_OS_WINDOWS */ #include "daemon.h" int xorp_daemonize(int nochdir, int noclose) { #ifndef HOST_OS_WINDOWS struct sigaction osa, sa; int fd; pid_t newgrp, newpid; int oerrno; int osa_ok; /* A SIGHUP may be thrown when the parent exits below. */ sigemptyset(&sa.sa_mask); sa.sa_handler = SIG_IGN; sa.sa_flags = 0; osa_ok = sigaction(SIGHUP, &sa, &osa); switch (newpid = fork()) { case -1: return (-1); case 0: break; default: return (newpid); } newgrp = setsid(); oerrno = errno; if (osa_ok != -1) sigaction(SIGHUP, &osa, NULL); if (newgrp == -1) { errno = oerrno; return (-1); } if (!nochdir) { if (chdir("/") < 0) { perror("chdir"); } } if (!noclose && (fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) { (void)dup2(fd, STDIN_FILENO); (void)dup2(fd, STDOUT_FILENO); (void)dup2(fd, STDERR_FILENO); if (fd > 2) (void)close(fd); } #else /* HOST_OS_WINDOWS */ UNUSED(nochdir); UNUSED(noclose); #endif /* HOST_OS_WINDOWS */ return (0); } xorp/libxorp/debug.h0000664000076400007640000001657511631755677014636 0ustar greearbgreearb/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- */ /* vim:set sts=4 ts=8: */ /* * Copyright (c) 2001-2011 XORP, Inc and Others * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License, Version * 2.1, June 1999 as published by the Free Software Foundation. * Redistribution and/or modification of this program under the terms of * any other version of the GNU Lesser General Public License is not * permitted. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, * see the GNU Lesser General Public License, Version 2.1, a copy of * which can be found in the XORP LICENSE.lgpl file. * * XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; * http://xorp.net */ #ifndef __LIBXORP_DEBUG_H__ #define __LIBXORP_DEBUG_H__ #include "libxorp/xorp.h" #ifdef __cplusplus class EnvTrace { public: EnvTrace(const char* e) { _do_trace = 0; const char* elt = getenv(e); if (elt) _do_trace = atoi(elt); } bool on() const { return _do_trace > 0; } int getTrace() const { return _do_trace; } protected: int _do_trace; }; #endif /* * This file defines the debug_msg(), the macro responsible for * generating debug messages. It takes the same arguments as * printf(), e.g. * * debug_msg("The number is %d\n", 5); * * Provided DEBUG_LOGGING is defined before this file is included, * debugging messages are enabled for a particular source file. If * `configure' is run with optional flag --enable-debug-msgs, then all * debug_msg statements in built files will be active. This latter option * turns on a huge amount of debug output statements which may not be desired. * * An additional macro with pseudo prototype * debug_msg_indent(uint32_t n); * also exists. It sets the indentation after the debug_msg line and file * preamble to n spaces. * * About this file * --------------- * * There is some additional unpleasantness in this header for * `configure' related magic. * * The macro CPP_SUPPORTS_VA_ARGS is defined by `configure' tests if the * C preprocessor supports macros with variable length arguments. We * use the GNU specific (args...) syntax for variable length arguments * as the c9x standard (__VA_ARGS__) breaks when the preprocessor is * invoked via g++. * * The macro DEBUG_PRINT_FUNCTION_NAME is intended to be defined as * the result of a `configure' command line option for people who like * this feature. */ /* * The following defines and notes we defined __printfike if it does not * already exist. The expansion of this macro uses a gcc extension to * check format strings. */ #ifndef __printflike #ifdef __GNUC__ #define __printflike(fmt,va1) __attribute__((__format__(printf, fmt, va1))) #define __libxorp_debug_defined_printflike #else #define __printflike(fmt, va1) #define __libxorp_debug_defined_printflike #endif #endif /* __printflike */ // TODO: Make this configurable in scons (remove it from xlog.h too) #define XORP_LOG_PRINT_USECS /* * `configure' defines DEBUG_LOGGING_GLOBAL, user may define DEBUG_LOGGING. */ #if defined(DEBUG_LOGGING) || defined(DEBUG_LOGGING_GLOBAL) # if !defined(DEBUG_LOGGING) # define DEBUG_LOGGING # elif !defined(DEBUG_LOGGING_GLOBAL) # define DEBUG_LOGGING_GLOBAL # endif /* DEBUG_LOGGING */ # ifdef CPP_SUPPORTS_GNU_VA_ARGS # ifdef DEBUG_PRINT_FUNCTION_NAME # define debug_msg(args...) \ _xdebug_msg_long(__FILE__,__LINE__,__FUNCTION__,args) # else # define debug_msg(args...) \ _xdebug_msg_long(__FILE__,__LINE__,0,args) # endif # else # ifdef DEBUG_PRINT_FUNCTION_NAME # define debug_msg \ _xdebug_entry(__FILE__,__LINE__,__FUNCTION__), \ _xdebug_msg_short # else # define debug_msg \ _xdebug_entry(__FILE__,__LINE__,0), _xdebug_msg_short # endif # endif # # define debug_msg_indent(n) _xdebug_set_indent(n) # #else # ifdef __cplusplus /* This cruft and the ensuing version of debug_msg mean that the debug enabled * and disabled versions of debug_msg have similar sematics, ie both are * likely to be broken or neither is broken following a change. However, * this comes at non-zero cost so an NDEBUG build would probably want to * define debug_msg to be empty (enabling optimization may not stop * calls to c_str() in format arguments). */ inline void swallow_args(const char*) {} template inline void swallow_args(const char*, A) {} template inline void swallow_args(const char*, A, B) {} template inline void swallow_args(const char*, A, B, C) {} template inline void swallow_args(const char*, A, B, C, D) {} template inline void swallow_args(const char*, A, B, C, D, E) {} template inline void swallow_args(const char*, A, B, C, D, E, F) {} template inline void swallow_args(const char*, A, B, C, D, E, F, G) {} template inline void swallow_args(const char*, A, B, C, D, E, F, G, H) {} template inline void swallow_args(const char*, A, B, C, D, E, F, G, H, I) {} template inline void swallow_args(const char*, A, B, C, D, E, F, G, H, I, J) {} template inline void swallow_args(const char*, A, B, C, D, E, F, G, H, I, J, K) {} template inline void swallow_args(const char*, A, B, C, D, E, F, G, H, I, J, K, L) {} inline void check_args(const char* fmt, ...) __printflike(1,2); inline void check_args(const char*, ...) {} #define debug_msg(args...) \ do { \ if (0) { \ check_args(args); \ swallow_args(args); \ } \ } while (0) # else # ifdef CPP_SUPPORTS_GNU_VA_ARGS # define debug_msg(args...) # else # define debug_msg if (1) ; else _xdebug_null # endif # endif # # define debug_msg_indent(x) # #endif #ifdef __cplusplus extern "C" { #endif /* Function for systems with variable argument macros */ void _xdebug_msg_long(const char* file, int line, const char* fn, const char* format, ...) __printflike(4, 5); /* Functions for systems without variable argument macros */ void _xdebug_entry(const char* file, int line, const char* fn); void _xdebug_msg_short(const char* format, ...) __printflike(1,2); void _xdebug_null(const char* format, ...) __printflike(1,2); /* Misc */ void _xdebug_set_indent(uint32_t n); /* Undefine __printflike if we defined it */ #ifdef __libxorp_debug_defined_printflike #undef __libxorp_debug_defined_printflike #undef __printflike #endif /* __libxorp_debug_defined_printflike */ #ifdef __cplusplus } #endif #endif /* __LIBXORP_DEBUG_H__ */ xorp/libxorp/nexthop.cc0000664000076400007640000000464411421137511015337 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "libxorp_module.h" #include "xorp.h" #include "nexthop.hh" template IPNextHop
::IPNextHop(const A& from_ipaddr) : _addr(from_ipaddr) { } template IPPeerNextHop::IPPeerNextHop(const A& from_ipaddr) : IPNextHop(from_ipaddr) { } template string IPPeerNextHop::str() const { string nh = "NH:"; return nh + this->_addr.str(); } template IPEncapsNextHop::IPEncapsNextHop(const A& from_ipaddr) : IPNextHop(from_ipaddr) { } template string IPEncapsNextHop::str() const { string enh = "NH->"; return enh + this->_addr.str(); } template IPExternalNextHop::IPExternalNextHop(const A& from_ipaddr) : IPNextHop(from_ipaddr) { } template string IPExternalNextHop::str() const { return string("Ext>") + this->_addr.str(); } DiscardNextHop::DiscardNextHop() : NextHop() { } string DiscardNextHop::str() const { return string("DISCARD"); } UnreachableNextHop::UnreachableNextHop() : NextHop() { } string UnreachableNextHop::str() const { return string("UNREACHABLE"); } template class IPNextHop; template class IPNextHop; template class IPNextHop; template class IPPeerNextHop; template class IPPeerNextHop; template class IPPeerNextHop; template class IPExternalNextHop; template class IPExternalNextHop; template class IPExternalNextHop; template class IPEncapsNextHop; template class IPEncapsNextHop; template class IPEncapsNextHop; xorp/libxorp/token.cc0000664000076400007640000001313511540224230014762 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // Token processing functions. // #include "token.hh" #include // // Exported variables // // // Local constants definitions // // // Local structures/classes, typedefs and macros // // // Local variables // // // Local functions prototypes // // Copy a token from @token_org // If it contains token separators, enclose it within quotations string copy_token(const string& token_org) { size_t i; bool is_enclose_quotations = false; string token; for (i = 0; i < token_org.size(); i++) { if (is_token_separator(token_org[i])) { is_enclose_quotations = true; break; } } if (is_enclose_quotations) { token = "\""; token += token_org; token += "\""; } else { token = token_org; } return (token); } // Pop a token from @token_line // @return the token, and modify @token_line to exclude the return token. string pop_token(string& token_line) { size_t i = 0; string token; bool is_escape_begin = false; // True if reached quotation mark bool is_escape_end = false; // Check for empty token line if (token_line.empty()) return (token); // Skip the spaces in front for (i = 0; i < token_line.length(); i++) { if (xorp_isspace(token_line[i])) { continue; } break; } // Check if we reached the end of the token line if (i == token_line.length()) { #ifdef XORP_USE_USTL token_line.erase((uoff_t)(0), i); #else token_line.erase(0, i); #endif return (token); } if (token_line[i] == '"') { is_escape_begin = true; i++; } // Get the substring until any other token separator for ( ; i < token_line.length(); i++) { if (is_escape_end) { if (! is_token_separator(token_line[i])) { // RETURN ERROR } break; } if (is_escape_begin) { if (token_line[i] == '"') { is_escape_end = true; continue; } } if (is_token_separator(token_line[i]) && !is_escape_begin) { if ((token_line[i] == '|') && (token.empty())) { // XXX: "|" with or without a space around it is a token itself token += token_line[i]; i++; } break; } token += token_line[i]; } #ifdef XORP_USE_USTL token_line.erase((uoff_t)(0), i); #else token_line.erase(0, i); #endif if (is_escape_begin && !is_escape_end) { // RETURN ERROR } return (token); } bool is_token_separator(const char c) { if (xorp_isspace(c) || c == '|') return (true); return (false); } bool has_more_tokens(const string& token_line) { string tmp_token_line = token_line; string token = pop_token(tmp_token_line); return (token.size() > 0); } /** * Split a token line into a vector with the tokens. * * @param token_line the token line to split. * @return a vector with all tokens. */ vector token_line2vector(const string& token_line) { string token_line_org(token_line); string token; vector token_vector_result; do { token = pop_token(token_line_org); if (token.empty()) break; token_vector_result.push_back(token); } while (true); return (token_vector_result); } /** * Split a token line into a list with the tokens. * * @param token_line the token line to split. * @return a list with all tokens. */ list token_line2list(const string& token_line) { string token_line_org(token_line); string token; list token_list_result; do { token = pop_token(token_line_org); if (token.empty()) break; token_list_result.push_back(token); } while (true); return (token_list_result); } /** * Combine a vector with the tokens into a single line with spaces as * separators. * * @param token_vector the vector with the tokens. * @return a line with the tokens separated by spaces. */ string token_vector2line(const vector& token_vector) { string token_line_result; vector::const_iterator iter; for (iter = token_vector.begin(); iter != token_vector.end(); ++iter) { const string& token = *iter; if (! token_line_result.empty()) token_line_result += " "; // XXX: the token separator token_line_result += token; } return (token_line_result); } /** * Combine a list with the tokens into a single line with spaces as * separators. * * @param token_list the list with the tokens. * @return a line with the tokens separated by spaces. */ string token_list2line(const list& token_list) { string token_line_result; list::const_iterator iter; for (iter = token_list.begin(); iter != token_list.end(); ++iter) { const string& token = *iter; if (! token_line_result.empty()) token_line_result += " "; // XXX: the token separator token_line_result += token; } return (token_line_result); } xorp/libxorp/gai_strerror.c0000664000076400007640000000313211540224227016203 0ustar greearbgreearb/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- */ /* vim:set sts=4 ts=8: */ /* * Copyright (c) 2001-2011 XORP, Inc and Others * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License, Version * 2.1, June 1999 as published by the Free Software Foundation. * Redistribution and/or modification of this program under the terms of * any other version of the GNU Lesser General Public License is not * permitted. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, * see the GNU Lesser General Public License, Version 2.1, a copy of * which can be found in the XORP LICENSE.lgpl file. * * XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; * http://xorp.net */ /* * $XORP: xorp/libxorp/gai_strerror.c,v 1.9 2008/10/02 21:57:30 bms Exp $ */ #include "xorp.h" #ifdef HOST_OS_WINDOWS #include "win_io.h" /* * gai_strerror() is actually implemented as an inline on Windows; it is * not present in the Winsock2 DLLs. The 'real' inline functions are missing * from the MinGW copy of the PSDK, presumably for licensing reasons. * * There is an accompanying kludge to purge the namespace for this function * to be visible in . * * This now simply calls the win_strerror() utility routine in win_io.c. */ char * gai_strerror(int ecode) { return (win_strerror(ecode)); } #endif /* HOST_OS_WINDOWS */ xorp/libxorp/callback.cc0000664000076400007640000000530311540224227015402 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "libxorp_module.h" #include "libxorp/xorp.h" /* * This code is only compiled if DEBUG_CALLBACKS is defined. All callback * debugging entails is watching that individual callbacks do not take * too long in their dispatch method. Since XORP is event and timer * driven taking anything more than a few seconds is potentially bad. * Ideally the longest dispatch times should be of the order of 10-50 * milliseconds. */ #ifdef DEBUG_CALLBACKS #include "libxorp/xlog.h" #include "libxorp/callback.hh" #include "libxorp/c_format.hh" #include "libxorp/timeval.hh" #include "libxorp/timer.hh" /** * Maximum callback dispatch duration. */ static const TimeVal MAX_CALLBACK_DURATION(5, 0); /** * Stack because one callback can dispatch another and callback data * structures can be deleted so we can't rely on file and line stored * there. */ struct CBStackElement { TimeVal start; const char* file; int line; CBStackElement(const TimeVal& now, const char* f, int l) : start(now), file(f), line(l) {} }; static stack cb_stack; void trace_dispatch_enter(const char* file, int line) { TimeVal now; TimerList::system_gettimeofday(&now); cb_stack.push(CBStackElement(now, file, line)); } void trace_dispatch_leave() { XLOG_ASSERT(cb_stack.empty() == false); TimeVal now; TimerList::system_gettimeofday(&now); const CBStackElement& e = cb_stack.top(); TimeVal delta = now - e.start; if (delta >= MAX_CALLBACK_DURATION) { string s = c_format("Callback originating at %s:%d took " "%d.%06d seconds\n", e.file, e.line, delta.sec(), delta.usec()); if (xlog_is_running()) { XLOG_ERROR("%s", s.c_str()); } else { fprintf(stderr, "ERROR: %s\n", s.c_str()); } } cb_stack.pop(); } #endif /* DEBUG_CALLBACKS */ xorp/libxorp/config_param.hh0000664000076400007640000001252411421137511016305 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/libxorp/config_param.hh,v 1.17 2008/10/02 21:57:30 bms Exp $ #ifndef __LIBXORP_CONFIG_PARAM_HH__ #define __LIBXORP_CONFIG_PARAM_HH__ #include "libxorp/callback.hh" /** * @short A class for storing a configuration parameter. * * This class can be used to store a configuration parameter. * Such parameter has a current and a default value. */ template class ConfigParam { public: typedef typename XorpCallback1::RefPtr UpdateCallback; /** * Constructor of a parameter with an initial value. * * Create a configurable parameter, and initialize its initial * and current value. * * @param value the initial and current value to initialize the * parameter to. */ explicit ConfigParam(const T& value) : _value(value), _initial_value(value) {} /** * Constructor of a parameter with an initial value and a callback. * * Create a configurable parameter, initialize it, and assign * a callback method that will be invoked when the value changes. * * @param value the initial and current value to initialize the * parameter to. * * @param update_cb the callback method that will be invoked when the * value changes. */ ConfigParam(const T& value, const UpdateCallback& update_cb) : _value(value), _initial_value(value), _update_cb(update_cb) {} /** * Destructor */ virtual ~ConfigParam() {} /** * Get the current value of the parameter. * * @return the current value of the parameter. */ const T& get() const { return (_value); } /** * Set the current value of the parameter. * * @param value the current value to set the parameter to. */ void set(const T& value) { _value = value; if (! _update_cb.is_empty()) _update_cb->dispatch(_value); } /** * Assignment operator * * @param value the value to assign to the parameter. * @return the parameter with the new value assigned. */ ConfigParam& operator=(const T& value) { set(value); return (*this); } /** * Increment Operator (prefix). * * The numerical value of this configuration parameter is incremented * by one. * * @return a reference to this configuration parameter after it was * incremented by one. */ const T& operator++() { return (incr()); } /** * Increment Operator (postfix). * * The numerical value of this configuration parameter is incremented * by one. * * @return the value of this configuration parameter before it was * incremented by one. */ T operator++(int) { T old_value = _value; incr(); return (old_value); } /** * Increment Operator. * * The numerical value of this configuration parameter is incremented * by one. * * @return a reference to this configuration parameter after it was * incremented by one. */ const T& incr() { set(_value + 1); return (_value); } /** * Decrement Operator (prefix). * * The numerical value of this configuration parameter is decremented * by one. * * @return a reference to this configuration parameter after it was * decremented by one. */ const T& operator--() { return (decr()); } /** * Decrement Operator (postfix). * * The numerical value of this configuration parameter is decremented * by one. * * @return the value of this configuration parameter before it was * decremented by one. */ T operator--(int) { T old_value = _value; decr(); return (old_value); } /** * Decrement Operator. * * The numerical value of this configuration parameter is decremented * by one. * * @return a reference to this configuration parameter after it was * decremented by one. */ const T& decr() { set(_value - 1); return (_value); } /** * Get the initial value of the parameter. */ const T& get_initial_value() const { return (_initial_value); } /** * Reset the current value of the parameter to its initial value. */ void reset() { set(_initial_value); } private: T _value; // The current value T _initial_value; // The initial value UpdateCallback _update_cb; // Callback invoked when _value changes }; #endif // __LIBXORP_CONFIG_PARAM_HH__ xorp/libxorp/ipvxnet.hh0000664000076400007640000002665411540225527015375 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/libxorp/ipvxnet.hh,v 1.22 2008/10/02 21:57:31 bms Exp $ #ifndef __LIBXORP_IPVXNET_HH__ #define __LIBXORP_IPVXNET_HH__ #include "xorp.h" #include "ipnet.hh" #include "ipvx.hh" #include "ipv4net.hh" #include "ipv6net.hh" /** * Base IPvXNet just has @ref IPNet methods. IPvXNet is derived * from BaseIPvXNet and has IPvX specific functions such as whether * contained type is an IPv4 network or an IPv6 network. * * See @ref IPNet for available methods. */ typedef IPNet BaseIPvXNet; template<> inline IPNet::IPNet(const IPvX& ipvx, uint8_t prefix_len) throw (InvalidNetmaskLength) : _prefix_len(prefix_len) { if (prefix_len > ipvx.addr_bitlen()) xorp_throw(InvalidNetmaskLength, prefix_len); _masked_addr = ipvx.mask_by_prefix_len(prefix_len); } template <> inline void IPNet::initialize_from_string(const char *cp) throw (InvalidString, InvalidNetmaskLength) { char *slash = strrchr(const_cast(cp), '/'); if (slash == 0) xorp_throw(InvalidString, "Missing slash"); if (*(slash + 1) == 0) xorp_throw(InvalidString, "Missing prefix length"); _prefix_len = atoi(slash + 1); string addr = string(cp, slash - cp); _masked_addr = IPvX(addr.c_str()).mask_by_prefix_len(_prefix_len); } /** * IPvXNet class. Container for IPv4 and IPv6 networks. * * Also see @ref IPNet for available methods. */ class IPvXNet : public BaseIPvXNet { public: /** * Constructor for a specified address family. * * Creates a network address of specified family, and address value of * INADDR_ANY or IN6ADDR_ANY (for IPv4 and IPv6 respectively). * * @param family the address family. */ explicit IPvXNet(int family) throw (InvalidFamily) : BaseIPvXNet(IPvX::ZERO(family), 0) {} #ifdef XORP_USE_USTL IPvXNet() : BaseIPvXNet(IPvX::ZERO(AF_INET), 0) {} #endif /** * Copy constructor for BaseIPvXNet subnet address * * @param n the subnet to copy from. */ IPvXNet(const BaseIPvXNet& n) : BaseIPvXNet(n) {} /** * Copy constructor for IPvXNet subnet address * * @param n the subnet to copy from. */ IPvXNet(const IPvXNet& n) : BaseIPvXNet(n) {} /** * Copy constructor for IPv4Net subnet address * * @param v4net the subnet to copy from. */ IPvXNet(const IPv4Net& v4net) : BaseIPvXNet(v4net.masked_addr(), v4net.prefix_len()) {} /** * Copy constructor for IPv6Net subnet address * * @param v6net the subnet to copy from. */ IPvXNet(const IPv6Net& v6net) : BaseIPvXNet(v6net.masked_addr(), v6net.prefix_len()) {} /** * Constructor from a string. * * @param from_cstring C-style string with slash separated address * and prefix length. * Examples: "12.34.56/24", "1234:5678/32::" */ IPvXNet(const char *cp) throw (InvalidString, InvalidNetmaskLength) : BaseIPvXNet(cp) {} /** * Constructor from a given base address and a prefix length. * * @param a base address for the subnet. * @param prefix_len length of subnet mask. */ IPvXNet(const IPvX& a, uint8_t prefix_len) throw (InvalidNetmaskLength) : BaseIPvXNet(a, prefix_len) {} // The following methods are specific to IPvXNet /** * Test if this subnet is IPv4 subnet. * * @return true if the subnet is IPv4. */ bool is_ipv4() const { return masked_addr().is_ipv4(); } /** * Test if this subnet is IPv6 subnet. * * @return true if the subnet is IPv6. */ bool is_ipv6() const { return masked_addr().is_ipv6(); } /** * Get IPv4Net subnet. * * @return IPv4Net subnet contained with IPvXNet structure. */ IPv4Net get_ipv4net() const throw (InvalidCast) { return IPv4Net(masked_addr().get_ipv4(), prefix_len()); } /** * Get IPv6Net subnet. * * @return IPv6Net subnet contained with IPvXNet structure. */ IPv6Net get_ipv6net() const throw (InvalidCast) { return IPv6Net(masked_addr().get_ipv6(), prefix_len()); } /** * Assign address value to an IPv4Net subnet. * * @param to_ipv4net IPv4Net subnet to be assigned IPv4Net value contained * within this subnet. */ void get(IPv4Net& to_ipv4net) const throw (InvalidCast) { to_ipv4net = get_ipv4net(); } /** * Assign address value to an IPv6Net subnet. * * @param to_ipv6net IPv6Net subnet to be assigned IPv6Net value contained * within this subnet. */ void get(IPv6Net& to_ipv6net) const throw (InvalidCast) { to_ipv6net = get_ipv6net(); } /** * Get the address family. * * @return the address family of this subnet (AF_INET or AF_INET6). */ int af() const { return masked_addr().af(); } /** * Test if this subnet is a unicast prefix. * * In case of IPv4 all prefixes that fall within the Class A, Class B or * Class C address space are unicast. * In case of IPv6 all prefixes that don't contain the multicast * address space are unicast. * Note that the default route (0.0.0.0/0 for IPv4 or ::/0 for IPv6) * is also considered an unicast prefix. * * @return true if this subnet is a unicast prefix. */ bool is_unicast() const { if (is_ipv4()) { return (get_ipv4net().is_unicast()); } else { return (get_ipv6net().is_unicast()); } } /** * Return the subnet containing all multicast addresses. * * Note that this is a static function and can be used without * a particular object. Example: * IPvXNet my_prefix = IPvXNet::ip_multicast_base_prefix(my_family); * * @param family the address family. * @return the multicast base prefix address for address * family of @ref family. */ static IPvXNet ip_multicast_base_prefix(int family) throw (InvalidFamily) { return IPvXNet(IPvX::MULTICAST_BASE(family), IPvX::ip_multicast_base_address_mask_len(family)); } /** * Return the subnet containing all IPv4 Class A addresses * (0.0.0.0/1). * * This method applies only for IPv4. * Note that this is a static function and can be used without * a particular object. Example: * IPvXNet my_prefix = IPvXNet::ip_class_a_base_prefix(my_family); * * @param family the address family. * @return the Class A base prefix address for address * family of @ref family. */ static IPvXNet ip_class_a_base_prefix(int family) throw (InvalidFamily) { return IPvXNet(IPvX::CLASS_A_BASE(family), IPvX::ip_class_a_base_address_mask_len(family)); } /** * Return the subnet containing all IPv4 Class B addresses * (128.0.0.0/2). * * This method applies only for IPv4. * Note that this is a static function and can be used without * a particular object. Example: * IPvXNet my_prefix = IPvXNet::ip_class_b_base_prefix(my_family); * * @param family the address family. * @return the Class B base prefix address for address * family of @ref family. */ static IPvXNet ip_class_b_base_prefix(int family) throw (InvalidFamily) { return IPvXNet(IPvX::CLASS_B_BASE(family), IPvX::ip_class_b_base_address_mask_len(family)); } /** * Return the subnet containing all IPv4 Class C addresses * (192.0.0.0/3). * * This method applies only for IPv4. * Note that this is a static function and can be used without * a particular object. Example: * IPvXNet my_prefix = IPvXNet::ip_class_c_base_prefix(my_family); * * @param family the address family. * @return the Class C base prefix address for address * family of @ref family. */ static IPvXNet ip_class_c_base_prefix(int family) throw (InvalidFamily) { return IPvXNet(IPvX::CLASS_C_BASE(family), IPvX::ip_class_c_base_address_mask_len(family)); } /** * Return the subnet containing all IPv4 experimental Class E addresses * (240.0.0.0/4). * * This method applies only for IPv4. * Note that this is a static function and can be used without * a particular object. Example: * IPvXNet my_prefix = IPvXNet::ip_experimental_base_prefix(my_family); * * @param family the address family. * @return the experimental base prefix address for address * family of @ref family. */ static IPvXNet ip_experimental_base_prefix(int family) throw (InvalidFamily) { return IPvXNet(IPvX::EXPERIMENTAL_BASE(family), IPvX::ip_experimental_base_address_mask_len(family)); } /** * Test if this subnet is within the multicast address range. * * @return true if this subnet is within the multicast address range. */ bool is_multicast() const { return (ip_multicast_base_prefix(masked_addr().af()).contains(*this)); } /** * Test if this subnet is within the IPv4 Class A * address range (0.0.0.0/1). * * This method applies only for IPv4, and always returns false for IPv6. * * @return true if this subnet is within the IPv4 Class A address * range. */ bool is_class_a() const { if (is_ipv4()) { return (ip_class_a_base_prefix(masked_addr().af()).contains(*this)); } return (false); } /** * Test if this subnet is within the IPv4 Class B * address range (128.0.0.0/2). * * This method applies only for IPv4, and always returns false for IPv6. * * @return true if this subnet is within the IPv4 Class B address * range. */ bool is_class_b() const { if (is_ipv4()) { return (ip_class_b_base_prefix(masked_addr().af()).contains(*this)); } return (false); } /** * Test if this subnet is within the IPv4 Class C * address range (192.0.0.0/3). * * This method applies only for IPv4, and always returns false for IPv6. * * @return true if this subnet is within the IPv4 Class C address * range. */ bool is_class_c() const { if (is_ipv4()) { return (ip_class_c_base_prefix(masked_addr().af()).contains(*this)); } return (false); } /** * Test if this subnet is within the IPv4 experimental Class E * address range (240.0.0.0/4). * * This method applies only for IPv4, and always returns false for IPv6. * * @return true if this subnet is within the IPv4 experimental address * range. */ bool is_experimental() const { if (is_ipv4()) { return (ip_experimental_base_prefix(masked_addr().af()).contains(*this)); } return (false); } }; #endif // __LIBXORP_IPVXNET_HH__ xorp/libxorp/utils.hh0000664000076400007640000003066511540225530015027 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __LIBXORP_UTILS_HH__ #define __LIBXORP_UTILS_HH__ #include "utility.h" // // Set of utilities // #define PATH_CURDIR "." #define PATH_PARENT ".." #define NT_PATH_DELIMITER_CHAR '\\' #define NT_PATH_DELIMITER_STRING "\\" #define NT_PATH_ENV_DELIMITER_CHAR ';' #define NT_PATH_ENV_DELIMITER_STRING ";" #define NT_PATH_DRIVE_DELIMITER_CH ':' #define NT_EXECUTABLE_SUFFIX ".exe" // NT specific #define NT_PATH_UNC_PREFIX "\\\\" #define NT_PATH_DRIVE_SUFFIX ":" #define UNIX_PATH_DELIMITER_CHAR '/' #define UNIX_PATH_DELIMITER_STRING "/" #define UNIX_PATH_ENV_DELIMITER_CHAR ':' #define UNIX_PATH_ENV_DELIMITER_STRING ":" #define UNIX_EXECUTABLE_SUFFIX "" #ifdef HOST_OS_WINDOWS #define PATH_DELIMITER_CHAR NT_PATH_DELIMITER_CHAR #define PATH_DELIMITER_STRING NT_PATH_DELIMITER_STRING #define PATH_ENV_DELIMITER_CHAR NT_PATH_ENV_DELIMITER_CHAR #define PATH_ENV_DELIMITER_STRING NT_PATH_ENV_DELIMITER_STRING #define EXECUTABLE_SUFFIX NT_EXECUTABLE_SUFFIX #else // ! HOST_OS_WINDOWS #define PATH_DELIMITER_CHAR UNIX_PATH_DELIMITER_CHAR #define PATH_DELIMITER_STRING UNIX_PATH_DELIMITER_STRING #define PATH_ENV_DELIMITER_CHAR UNIX_PATH_ENV_DELIMITER_CHAR #define PATH_ENV_DELIMITER_STRING UNIX_PATH_ENV_DELIMITER_STRING #define EXECUTABLE_SUFFIX UNIX_EXECUTABLE_SUFFIX #endif // ! HOST_OS_WINDOWS /** * Convert a UNIX style path to the platform's native path format. * * @param path the UNIX style path to be converted. * @return the converted path. */ inline string unix_path_to_native(const string& unixpath) { #ifdef HOST_OS_WINDOWS string nativepath = unixpath; string::size_type n = 0; while (string::npos != (n = nativepath.find(UNIX_PATH_DELIMITER_CHAR, n))) { nativepath[n] = NT_PATH_DELIMITER_CHAR; } return (nativepath); #else // ! HOST_OS_WINDOWS return string(unixpath); #endif // ! HOST_OS_WINDOWS } /** * Determine if a provided native path string is an absolute path, or * possibly relative to a user's home directory under UNIX. * * @param path a path in native format to inspect. * @param homeok allow paths relative to a home directory to be regarded * as absolute paths by this function. * @return true if the path if satisfies the criteria for an absolute path. */ inline bool is_absolute_path(const string& path, bool homeok = false) { if (path.empty()) return false; #ifdef HOST_OS_WINDOWS if ((path.find(NT_PATH_UNC_PREFIX) == 0) || ((path.size() >= 2) && isalpha(path[0]) && path[1] == ':')) return true; return false; UNUSED(homeok); #else // ! HOST_OS_WINDOWS if (path[0] == '/') return true; if (homeok && path[0] == '~') return true; return false; #endif // ! HOST_OS_WINDOWS } /** * Tokenize a string by breaking into separate strings when separator * character is encountered. * * @param s string to be tokenized. * @param sep separator to break string it. * @return list of tokens. */ list split(const string& s, char sep); /** * Remove the heading and trailing empty spaces from string value. * * @param s string that may have heading and trailing empty spaces. * @return copy of the string with heading and trailing empty spaces removed. */ string strip_empty_spaces(const string& s); /** * Test if a string contains an empty space (i.e., or ). * * @param s the string to test. * @return true if the string contains an empty space, otherwise false. */ bool has_empty_space(const string& s); /** * Return basename of a path. * * @param argv0 the argv[0] supplied to main(). */ const char* xorp_basename(const char* argv0); /** * Template to delete a list of pointers, and the objects pointed to. * * @param delete_list the list of pointers to objects to delete. */ template void delete_pointers_list(list& delete_list) { list tmp_list; // Swap the elements, so the original container never contains // entries that point to deleted elements. swap(tmp_list, delete_list); for (typename list::iterator iter = tmp_list.begin(); iter != tmp_list.end(); ++iter) { T *elem = (*iter); delete elem; } tmp_list.clear(); } /** * Template to delete an array of pointers, and the objects pointed to. * * @param delete_vector the vector of pointers to objects to delete. */ template void delete_pointers_vector(vector& delete_vector) { vector tmp_vector; // Swap the elements, so the original container never contains // entries that point to deleted elements. swap(tmp_vector, delete_vector); for (typename vector::iterator iter = tmp_vector.begin(); iter != tmp_vector.end(); ++iter) { T *elem = (*iter); delete elem; } tmp_vector.clear(); } /** * Create a temporary file with unique name. * * This function takes the given file name template, and adds a suffix * to it to create an unique file name in a chosen temporary directory. * The temporary directory is selected from the following list by choosing * the first directory that allows us the create the temporary file * (in the given order): * (a) The value of one of the following environment variables if defined * (in the given order): * - "TMPDIR" * - "TEMP" (Windows only) * - "TMP" (Windows only) * (b) Argument @ref tmp_dir if it is not an empty string. * (c) The system-specific path of the directory designated for * temporary files: * - GetTempPath() (Windows only) * (d) The "P_tmpdir" directory if this macro is defined (typically in * the include file). * (e) The first directory from the following list we can write to: * - "C:\TEMP" (Windows only) * - "/tmp" (UNIX only) * - "/usr/tmp" (UNIX only) * - "/var/tmp" (UNIX only) * * For example, if the file name template is "foo", and if the chosen * temporary directory is "/tmp", then the temporary file name would be * like "/tmp/foo.XXXXXX" where "XXXXXX" is alpha-numerical suffix * chosen automatically to compose the unique file name. The created file * has mode 0600 (i.e., readable and writable only by the owner). * * @param tmp_dir the preferred directory to store the file. * @param filename_template the file name template. * @param final_filename a return-by-reference argument that on success * returns the final name of the temporary file (including the directory name). * @param errmsg the error message (if error). * @return a file descriptor pointer for the temporary file (opened for * reading and writing) on success, otherwise NULL. */ FILE* xorp_make_temporary_file(const string& tmp_dir, const string& filename_template, string& final_filename, string& errmsg); #ifdef HOST_OS_WINDOWS /** * Helper function to quote command line arguments for MSVCRT-linked programs. * * Given an argv array represented by an STL list of strings, and a * writable command line string, walk through the arguments and perform * quoting/escaping according to the rules for invoking programs linked * against the Microsoft Visual C Runtime Library. * * This function is necessary because the Win32 CreateProcess() API * function accepts a single command line string, as opposed to a * UNIX-style argv[] array; arguments must therefore be delimited by * white space, so white space in arguments themselves must be quoted. * * Note that such extensive quoting shouldn't be performed for the * pathname to the executable -- generally it is desirable to only * wrap the path name with quotes, and then pass that string to this * helper function. * * @param args list of argument strings to be escaped. * @param cmdline string to which the escaped command line should be appended. */ void win_quote_args(const list& args, string& cmdline); #endif // HOST_OS_WINDOWS /** * Count the number of bits that are set in a 32-bit wide integer. * * Code taken from the following URL (the "Population Count (Ones Count)" * algorithm): * http://aggregate.org/MAGIC/ * * Note: this solution appears to be faster even compared to the * "Parallel Count" and "MIT HACKMEM Count" from: * http://www-db.stanford.edu/~manku/bitcount/bitcount.html * * @param x the value to test. * @return the number of bits that are set in @ref x. */ inline uint32_t xorp_bit_count_uint32(uint32_t x) { // // 32-bit recursive reduction using SWAR... // but first step is mapping 2-bit values // into sum of 2 1-bit values in sneaky way. // x -= ((x >> 1) & 0x55555555); x = (((x >> 2) & 0x33333333) + (x & 0x33333333)); x = (((x >> 4) + x) & 0x0f0f0f0f); x += (x >> 8); x += (x >> 16); x &= 0x0000003f; return (x); } /** * Count the number of leading zeroes in a 32-bit wide integer. * * Code taken from the following URL (the "Leading Zero Count" algorithm): * http://aggregate.org/MAGIC/ * * @param x the value to test. * @return the number of leading zeroes in @ref x. */ inline uint32_t xorp_leading_zero_count_uint32(uint32_t x) { x |= (x >> 1); x |= (x >> 2); x |= (x >> 4); x |= (x >> 8); x |= (x >> 16); return (32 - xorp_bit_count_uint32(x)); } /** * A template class for aligning buffer data with a particular data type. * * The technically correct solution is to allocate (using malloc()) * new buffer and copy the original data to it. By definition, the * malloc()-ed data is aligned, and therefore it can be casted to the * desired type. * * The more efficient solution (but probably technically incorrect), * is to assume that the first byte of "vector" buffer is aligned * similar to malloc()-ed data, and therefore it can be casted to the * desired type without creating a copy of it. * * The desired behavior can be chosen by setting the * AlignData::_data_is_copied constant to true or false. * Note that the constant is predefined for all AlignData instances. * If necessary, the constant can become a variable that can have * different value for each AlignData instance. */ template class AlignData { public: /** * Constructor. * * @param buffer the buffer with the data. */ AlignData(const vector& buffer) { if (_data_is_copied) { _data = malloc(sizeof(uint8_t) * buffer.size()); memcpy(_data, &buffer[0], sizeof(uint8_t) * buffer.size()); _const_data = _data; } else { _data = NULL; _const_data = &buffer[0]; } _payload = reinterpret_cast(_const_data); } /** * Destructor. */ ~AlignData() { if (_data != NULL) free(_data); } /** * Get the aligned payload from the beginning of the buffer. * * @return the aligned payload from the beginning of the buffer. */ const A* payload() const { return (_payload); } /** * Get the aligned payload by given offset from the beginning of the * buffer. * * Note that the given offset itself is suppose to point to aligned * location. * * @param offset the offset from the beginning of the buffer. * @return the aligned payload by given offset from the beginning of * the buffer. */ const A* payload_by_offset(size_t offset) const { const void* offset_data; offset_data = reinterpret_cast(_const_data) + offset; return (reinterpret_cast(offset_data)); } private: // Damn..code assumes data is not coppied, see: // NetlinkSocket::force_recvmsg_flgs // Will just have to assume that alignment is OK till we can clean this up. --Ben static const bool _data_is_copied = false; void* _data; const void* _const_data; const A* _payload; }; #endif // __LIBXORP_UTILS_HH__ xorp/libxorp/popen.hh0000664000076400007640000000260411540224227015002 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/libxorp/popen.hh,v 1.11 2008/10/02 21:57:32 bms Exp $ #ifndef __LIBXORP_POPEN_HH__ #define __LIBXORP_POPEN_HH__ pid_t popen2(const string& command, const list& arguments, FILE *& outstream, FILE *& errstream, bool redirect_stderr_to_stdout); int pclose2(FILE *iop_out, bool dont_wait); int popen2_mark_as_closed(pid_t pid, int wait_status); #ifdef HOST_OS_WINDOWS HANDLE pgethandle(pid_t pid); #endif #endif // __LIBXORP_POPEN_HH__ xorp/libxorp/SConscript0000664000076400007640000000624511703345405015362 0ustar greearbgreearb# Copyright (c) 2009-2012 XORP, Inc and Others # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # $ID$ import os Import("env") subdirs = [ 'tests' ] SConscript(dirs = subdirs, exports='env') env = env.Clone() is_shared = env.has_key('SHAREDLIBS') env.AppendUnique(CPPPATH = [ '#' ]) # External libraries. # On BSD, and others, we need -lrt for clock_gettime(). if not (env.has_key('mingw') and env['mingw']): env.AppendUnique(LIBS = [ 'rt' ]) else: env.AppendUnique(LIBS = [ 'ws2_32' ]) libxorp_core_srcs = [ # C files 'daemon.c', 'debug.c', 'ether_compat.c', 'gai_strerror.c', 'getopt.c', 'inet_ntop.c', 'inet_pton.c', 'random.c', 'strlcpy.c', 'strptime.c', 'utility.c', 'win_io.c', 'xlog.c', # C++ files 'asyncio.cc', 'buffered_asyncio.cc', 'bug_catcher.cc', 'c_format.cc', 'callback.cc', 'clock.cc', 'eventloop.cc', 'exceptions.cc', 'heap.cc', 'ipnet.cc', 'ipv4.cc', 'ipv6.cc', 'ipvx.cc', 'mac.cc', 'nexthop.cc', 'popen.cc', 'ref_ptr.cc', 'round_robin.cc', 'run_command.cc', 'safe_callback_obj.cc', 'selector.cc', 'service.cc', 'task.cc', 'time_slice.cc', 'timer.cc', 'timeval.cc', 'token.cc', 'transaction.cc', 'utils.cc', 'vif.cc', 'win_dispatcher.cc', ] if not (env.has_key('disable_profile') and env['disable_profile']): libxorp_core_srcs.append('profile.cc'); # build_info.cc will be generated by the SConstruct file # when this option is enabled. if (env.has_key('enable_buildinfo') and env['enable_buildinfo']): libxorp_core_srcs.append('build_info.cc'); if is_shared: libxorp_core = env.SharedLibrary(target = 'libxorp_core', source = libxorp_core_srcs) # Symlink the library into builddir's copy of lib, so that # rtld $ORIGIN references will pick it up automatically without # relinking. This syntax looks a little weird, as it makes the # action of building the symlink a 'PostAction' in the SCons # dependency graph. # We basically do this for every SharedLibrary we potentially # need to see for binaries not yet installed from the BUILDDIR. if env['rtld_origin']: for obj in libxorp_core: env.AddPostAction(libxorp_core, env.Symlink(obj.abspath, os.path.join(env['xorp_alias_libdir'], str(obj)))) else: libxorp_core = env.StaticLibrary(target = 'libxorp_core', source = libxorp_core_srcs) if is_shared: env.Alias('install', env.InstallLibrary(env['xorp_libdir'], libxorp_core)) Default(libxorp_core) xorp/libxorp/callback_debug.hh0000664000076400007640000700655211421137511016575 0ustar greearbgreearb/* * Copyright (c) 2001-2009 XORP, Inc. * See the XORP LICENSE.lgpl file for licensing, conditions, and warranties * on use. * * This file is PROGRAMMATICALLY GENERATED. * * This instance was generated with: * callback-gen.py -d -b 6 -l 15 */ /** * @libdoc Callbacks * * @sect Callback Overview * * XORP is an asynchronous programming environment and as a result there * are many places where callbacks are useful. Callbacks are typically * invoked to signify the completion or advancement of an asynchronous * operation. * * XORP provides a generic and flexible callback interface that utilizes * overloaded templatized functions for for generating callbacks * in conjunction with many small templatized classes. Whilst this makes * the syntax a little ugly, it provides a great deal of flexibility. * * XorpCallbacks are callback objects are created by the callback() * function which returns a reference pointer to a newly created callback * object. The callback is invoked by calling dispatch(), eg. *

#include "libxorp/xorp.h"
#include "libxorp/callback.hh"


static void hello_world() {
    cout << "Hello World" << endl;
}

int main() {
    // Typedef callback() return type for readability.  SimpleCallback
    // declares a XorpCallback taking 0 arguments and of return type void.
    typedef XorpCallback0::RefPtr SimpleCallback;

    // Create XorpCallback object using callback()
    SimpleCallback cb = callback(hello_world);

    // Invoke callback, results in call to hello_world.
    cb->dispatch();
    return 0;
}

* * The callback() method is overloaded and can also be used to create * callbacks to member functions, eg. *

#include "libxorp/xorp.h"
#include "libxorp/callback.hh"


class Foo {
public:
    void hello_world() {
	cout << "Foo::Hello World" << endl;
    }
};

int main() {
    typedef XorpCallback0::RefPtr SimpleCallback;

    Foo f;

    // Create a callback to a member function
    SimpleCallback cb = callback(&f, &Foo::hello_world);

    // Invoke f.hello_world
    cb->dispatch();

    return 0;
}

* * In addition, to being able to invoke member functions, callbacks can * also store arguments to functions. eg. *

#include "libxorp/xorp.h"
#include "libxorp/callback.hh"


static int sum(int x, int y) {
    cout << "sum(x = " << x << ", y = " << y << ")" << endl;
    return x + y;
}

int main() {
    // Callback to function returning "int"
    typedef XorpCallback0::RefPtr NoArgCallback;

    NoArgCallback cb1 = callback(sum, 1, 2);
    cout << "cb1->dispatch() returns " << cb1->dispatch() << endl; // "3"
    cout << endl;

    // Callback to function returning int and taking an integer argument
    typedef XorpCallback1::RefPtr OneIntArgCallback;

    OneIntArgCallback cb2 = callback(sum, 5);
    cout << "cb2->dispatch(10) returns " << cb2->dispatch(10) << endl; // 15
    cout << endl;

    cout << "cb2->dispatch(20) returns " << cb2->dispatch(20) << endl; // 25
    cout << endl;

    // Callback to function returning int and taking  2 integer arguments
    typedef XorpCallback2::RefPtr TwoIntArgCallback;

    TwoIntArgCallback cb3 = callback(sum);
    cout << "cb3->dispatch() returns " << cb3->dispatch(50, -50) << endl; // 0

    return 0;
}

* * Bound arguments, as with member functions, are implemented by the * overloading of the callback() method. At dispatch time, the bound * arguments are last arguments past to the wrappered function. If you * compile and run the program you will see: *
sum(x = 10, y = 5)
cb2->dispatch(10) returns 15
* * and: *
sum(x = 20, y = 5)
cb2->dispatch(20) returns 25
* * for the one bound argument cases. * * @sect Declaring Callback Types * * There are a host of XorpCallbackN types. The N denotes the number * of arguments that will be passed at dispatch time by the callback * invoker. The template parameters to XorpCallbackN types are the * return value followed by the types of arguments that will be passed * at dispatch time. Thus type: * *
XorpCallback1::RefPtr
 * 
* * corresponds to callback object returning a double when invoked and * requiring an integer argument to passed at dispatch time. * * When arguments are bound to a callback they are not specified * in the templatized argument list. So the above declaration is good * for a function taking an integer argument followed by upto the * maximum number of bound arguments. * * Note: In this header file, support is provided for upto %d bound * arguments and %d dispatch arguments. * * @sect Ref Pointer Helpers * * Callback objects may be set to NULL, since they use reference pointers * to store the objects. Callbacks may be unset using the ref_ptr::release() * method: *
    cb.release();
* and to tested using the ref_ptr::is_empty() method:
if (! cb.is_empty()) {
    cb->dispatch();
}
* * In many instances, the RefPtr associated with a callback on an object * will be stored by the object itself. For instance, a class may own a * timer object and the associated timer expiry callback which is * a member function of the containing class. Because the containing class * owns the callback object corresponding the timer callback, there is * never an opportunity for the callback to be dispatched on a deleted object * or with invalid data. */ #ifndef INCLUDED_FROM_CALLBACK_HH #error "This file should be included through libxorp/callback.hh" #endif #ifndef __XORP_CALLBACK_HH__ #define __XORP_CALLBACK_HH__ #include "minitraits.hh" #include "ref_ptr.hh" #include "safe_callback_obj.hh" #if defined(__GNUC__) && (__GNUC__ < 3) #define callback(x...) dbg_callback(__FILE__,__LINE__,x) #else #define callback(...) dbg_callback(__FILE__,__LINE__,__VA_ARGS__) #endif void trace_dispatch_enter(const char* file, int line); void trace_dispatch_leave(); #define record_dispatch_enter() trace_dispatch_enter( this->file(), this->line() ) #define record_dispatch_leave() trace_dispatch_leave() /////////////////////////////////////////////////////////////////////////////// // // Code relating to callbacks with 0 late args // /** * @short Base class for callbacks with 0 dispatch time args. */ template struct XorpCallback0 { typedef ref_ptr RefPtr; XorpCallback0(const char* file, int line) : _file(file), _line(line) {} virtual ~XorpCallback0() {} virtual R dispatch() = 0; const char* file() const { return _file; } int line() const { return _line; } private: const char* _file; int _line; }; /** * @short Callback object for functions with 0 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpFunctionCallback0B0 : public XorpCallback0 { typedef R (*F)(); XorpFunctionCallback0B0(const char* file, int line, F f) : XorpCallback0(file, line), _f(f) {} R dispatch() { record_dispatch_enter(); R r = (*_f)(); record_dispatch_leave(); return r; } protected: F _f; }; /** * @short Callback object for void functions with 0 dispatch time * arguments and 0 bound (stored) arguments. */ template <> struct XorpFunctionCallback0B0 : public XorpCallback0 { typedef void (*F)(); XorpFunctionCallback0B0(const char* file, int line, F f) : XorpCallback0(file, line), _f(f) {} void dispatch() { record_dispatch_enter(); (*_f)(); record_dispatch_leave(); } protected: F _f; }; /** * Factory function that creates a callback object targetted at a * function with 0 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback0::RefPtr dbg_callback(const char* file, int line, R (*f)()) { return typename XorpCallback0::RefPtr(new XorpFunctionCallback0B0(file, line, f)); } /** * @short Callback object for member methods with 0 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpMemberCallback0B0 : public XorpCallback0 { typedef R (O::*M)() ; XorpMemberCallback0B0(const char* file, int line, O* o, M m) : XorpCallback0(file, line), _o(o), _m(m) {} R dispatch() { record_dispatch_enter(); R r = ((*_o).*_m)(); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for void member methods with 0 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpMemberCallback0B0 : public XorpCallback0 { typedef void (O::*M)() ; XorpMemberCallback0B0(const char* file, int line, O* o, M m) : XorpCallback0(file, line), _o(o), _m(m) {} void dispatch() { record_dispatch_enter(); ((*_o).*_m)(); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for safe member methods with 0 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpSafeMemberCallback0B0 : public XorpMemberCallback0B0, public SafeCallbackBase { typedef typename XorpMemberCallback0B0::M M; XorpSafeMemberCallback0B0(const char* file, int line, O* o, M m) : XorpMemberCallback0B0(file, line, o, m), SafeCallbackBase(o) {} ~XorpSafeMemberCallback0B0() {} R dispatch() { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback0B0::dispatch(); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 0 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpSafeMemberCallback0B0 : public XorpMemberCallback0B0, public SafeCallbackBase { typedef typename XorpMemberCallback0B0::M M; XorpSafeMemberCallback0B0(const char* file, int line, O* o, M m) : XorpMemberCallback0B0(file, line, o, m), SafeCallbackBase(o) {} ~XorpSafeMemberCallback0B0() {} void dispatch() { if (valid()) { record_dispatch_enter(); XorpMemberCallback0B0::dispatch(); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory0B0 { static XorpMemberCallback0B0* make(const char* file, int line, O* o, R (O::*p)()) { return new XorpSafeMemberCallback0B0(file, line, o, p); } }; template struct XorpMemberCallbackFactory0B0 { static XorpMemberCallback0B0* make(const char* file, int line, O* o, R (O::*p)()) { return new XorpMemberCallback0B0(file, line, o, p); }; }; /** * Factory function that creates a callback object targetted at a * member function with 0 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback0::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)()) { return XorpMemberCallbackFactory0B0::True>::make(file, line, o, p); } /** * Factory function that creates a callback object targetted at a * member function with 0 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback0::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)()) { return XorpMemberCallbackFactory0B0::True>::make(file, line, &o, p); } /** * @short Callback object for const member methods with 0 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstMemberCallback0B0 : public XorpCallback0 { typedef R (O::*M)() const; XorpConstMemberCallback0B0(const char* file, int line, O* o, M m) : XorpCallback0(file, line), _o(o), _m(m) {} R dispatch() { record_dispatch_enter(); R r = ((*_o).*_m)(); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for void const member methods with 0 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstMemberCallback0B0 : public XorpCallback0 { typedef void (O::*M)() const; XorpConstMemberCallback0B0(const char* file, int line, O* o, M m) : XorpCallback0(file, line), _o(o), _m(m) {} void dispatch() { record_dispatch_enter(); ((*_o).*_m)(); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for const safe member methods with 0 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback0B0 : public XorpConstMemberCallback0B0, public SafeCallbackBase { typedef typename XorpConstMemberCallback0B0::M M; XorpConstSafeMemberCallback0B0(const char* file, int line, O* o, M m) : XorpConstMemberCallback0B0(file, line, o, m), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback0B0() {} R dispatch() { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback0B0::dispatch(); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 0 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback0B0 : public XorpConstMemberCallback0B0, public SafeCallbackBase { typedef typename XorpConstMemberCallback0B0::M M; XorpConstSafeMemberCallback0B0(const char* file, int line, O* o, M m) : XorpConstMemberCallback0B0(file, line, o, m), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback0B0() {} void dispatch() { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback0B0::dispatch(); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory0B0 { static XorpConstMemberCallback0B0* make(const char* file, int line, O* o, R (O::*p)() const) { return new XorpConstSafeMemberCallback0B0(file, line, o, p); } }; template struct XorpConstMemberCallbackFactory0B0 { static XorpConstMemberCallback0B0* make(const char* file, int line, O* o, R (O::*p)() const) { return new XorpConstMemberCallback0B0(file, line, o, p); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 0 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback0::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)() const) { return XorpConstMemberCallbackFactory0B0::True>::make(file, line, o, p); } /** * Factory function that creates a callback object targetted at a * const member function with 0 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback0::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)() const) { return XorpConstMemberCallbackFactory0B0::True>::make(file, line, &o, p); } /** * @short Callback object for functions with 0 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpFunctionCallback0B1 : public XorpCallback0 { typedef R (*F)(BA1); XorpFunctionCallback0B1(const char* file, int line, F f, BA1 ba1) : XorpCallback0(file, line), _f(f), _ba1(ba1) {} R dispatch() { record_dispatch_enter(); R r = (*_f)(_ba1); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; }; /** * @short Callback object for void functions with 0 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpFunctionCallback0B1 : public XorpCallback0 { typedef void (*F)(BA1); XorpFunctionCallback0B1(const char* file, int line, F f, BA1 ba1) : XorpCallback0(file, line), _f(f), _ba1(ba1) {} void dispatch() { record_dispatch_enter(); (*_f)(_ba1); record_dispatch_leave(); } protected: F _f; BA1 _ba1; }; /** * Factory function that creates a callback object targetted at a * function with 0 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback0::RefPtr dbg_callback(const char* file, int line, R (*f)(BA1), BA1 ba1) { return typename XorpCallback0::RefPtr(new XorpFunctionCallback0B1(file, line, f, ba1)); } /** * @short Callback object for member methods with 0 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpMemberCallback0B1 : public XorpCallback0 { typedef R (O::*M)(BA1) ; XorpMemberCallback0B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpCallback0(file, line), _o(o), _m(m), _ba1(ba1) {} R dispatch() { record_dispatch_enter(); R r = ((*_o).*_m)(_ba1); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for void member methods with 0 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpMemberCallback0B1 : public XorpCallback0 { typedef void (O::*M)(BA1) ; XorpMemberCallback0B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpCallback0(file, line), _o(o), _m(m), _ba1(ba1) {} void dispatch() { record_dispatch_enter(); ((*_o).*_m)(_ba1); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for safe member methods with 0 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpSafeMemberCallback0B1 : public XorpMemberCallback0B1, public SafeCallbackBase { typedef typename XorpMemberCallback0B1::M M; XorpSafeMemberCallback0B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpMemberCallback0B1(file, line, o, m, ba1), SafeCallbackBase(o) {} ~XorpSafeMemberCallback0B1() {} R dispatch() { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback0B1::dispatch(); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 0 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpSafeMemberCallback0B1 : public XorpMemberCallback0B1, public SafeCallbackBase { typedef typename XorpMemberCallback0B1::M M; XorpSafeMemberCallback0B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpMemberCallback0B1(file, line, o, m, ba1), SafeCallbackBase(o) {} ~XorpSafeMemberCallback0B1() {} void dispatch() { if (valid()) { record_dispatch_enter(); XorpMemberCallback0B1::dispatch(); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory0B1 { static XorpMemberCallback0B1* make(const char* file, int line, O* o, R (O::*p)(BA1), BA1 ba1) { return new XorpSafeMemberCallback0B1(file, line, o, p, ba1); } }; template struct XorpMemberCallbackFactory0B1 { static XorpMemberCallback0B1* make(const char* file, int line, O* o, R (O::*p)(BA1), BA1 ba1) { return new XorpMemberCallback0B1(file, line, o, p, ba1); }; }; /** * Factory function that creates a callback object targetted at a * member function with 0 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback0::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(BA1), BA1 ba1) { return XorpMemberCallbackFactory0B1::True>::make(file, line, o, p, ba1); } /** * Factory function that creates a callback object targetted at a * member function with 0 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback0::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(BA1), BA1 ba1) { return XorpMemberCallbackFactory0B1::True>::make(file, line, &o, p, ba1); } /** * @short Callback object for const member methods with 0 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstMemberCallback0B1 : public XorpCallback0 { typedef R (O::*M)(BA1) const; XorpConstMemberCallback0B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpCallback0(file, line), _o(o), _m(m), _ba1(ba1) {} R dispatch() { record_dispatch_enter(); R r = ((*_o).*_m)(_ba1); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for void const member methods with 0 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstMemberCallback0B1 : public XorpCallback0 { typedef void (O::*M)(BA1) const; XorpConstMemberCallback0B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpCallback0(file, line), _o(o), _m(m), _ba1(ba1) {} void dispatch() { record_dispatch_enter(); ((*_o).*_m)(_ba1); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for const safe member methods with 0 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback0B1 : public XorpConstMemberCallback0B1, public SafeCallbackBase { typedef typename XorpConstMemberCallback0B1::M M; XorpConstSafeMemberCallback0B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpConstMemberCallback0B1(file, line, o, m, ba1), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback0B1() {} R dispatch() { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback0B1::dispatch(); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 0 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback0B1 : public XorpConstMemberCallback0B1, public SafeCallbackBase { typedef typename XorpConstMemberCallback0B1::M M; XorpConstSafeMemberCallback0B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpConstMemberCallback0B1(file, line, o, m, ba1), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback0B1() {} void dispatch() { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback0B1::dispatch(); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory0B1 { static XorpConstMemberCallback0B1* make(const char* file, int line, O* o, R (O::*p)(BA1) const, BA1 ba1) { return new XorpConstSafeMemberCallback0B1(file, line, o, p, ba1); } }; template struct XorpConstMemberCallbackFactory0B1 { static XorpConstMemberCallback0B1* make(const char* file, int line, O* o, R (O::*p)(BA1) const, BA1 ba1) { return new XorpConstMemberCallback0B1(file, line, o, p, ba1); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 0 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback0::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(BA1) const, BA1 ba1) { return XorpConstMemberCallbackFactory0B1::True>::make(file, line, o, p, ba1); } /** * Factory function that creates a callback object targetted at a * const member function with 0 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback0::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(BA1) const, BA1 ba1) { return XorpConstMemberCallbackFactory0B1::True>::make(file, line, &o, p, ba1); } /** * @short Callback object for functions with 0 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpFunctionCallback0B2 : public XorpCallback0 { typedef R (*F)(BA1, BA2); XorpFunctionCallback0B2(const char* file, int line, F f, BA1 ba1, BA2 ba2) : XorpCallback0(file, line), _f(f), _ba1(ba1), _ba2(ba2) {} R dispatch() { record_dispatch_enter(); R r = (*_f)(_ba1, _ba2); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; }; /** * @short Callback object for void functions with 0 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpFunctionCallback0B2 : public XorpCallback0 { typedef void (*F)(BA1, BA2); XorpFunctionCallback0B2(const char* file, int line, F f, BA1 ba1, BA2 ba2) : XorpCallback0(file, line), _f(f), _ba1(ba1), _ba2(ba2) {} void dispatch() { record_dispatch_enter(); (*_f)(_ba1, _ba2); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; }; /** * Factory function that creates a callback object targetted at a * function with 0 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback0::RefPtr dbg_callback(const char* file, int line, R (*f)(BA1, BA2), BA1 ba1, BA2 ba2) { return typename XorpCallback0::RefPtr(new XorpFunctionCallback0B2(file, line, f, ba1, ba2)); } /** * @short Callback object for member methods with 0 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpMemberCallback0B2 : public XorpCallback0 { typedef R (O::*M)(BA1, BA2) ; XorpMemberCallback0B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback0(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} R dispatch() { record_dispatch_enter(); R r = ((*_o).*_m)(_ba1, _ba2); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for void member methods with 0 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpMemberCallback0B2 : public XorpCallback0 { typedef void (O::*M)(BA1, BA2) ; XorpMemberCallback0B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback0(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} void dispatch() { record_dispatch_enter(); ((*_o).*_m)(_ba1, _ba2); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for safe member methods with 0 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpSafeMemberCallback0B2 : public XorpMemberCallback0B2, public SafeCallbackBase { typedef typename XorpMemberCallback0B2::M M; XorpSafeMemberCallback0B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpMemberCallback0B2(file, line, o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpSafeMemberCallback0B2() {} R dispatch() { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback0B2::dispatch(); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 0 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpSafeMemberCallback0B2 : public XorpMemberCallback0B2, public SafeCallbackBase { typedef typename XorpMemberCallback0B2::M M; XorpSafeMemberCallback0B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpMemberCallback0B2(file, line, o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpSafeMemberCallback0B2() {} void dispatch() { if (valid()) { record_dispatch_enter(); XorpMemberCallback0B2::dispatch(); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory0B2 { static XorpMemberCallback0B2* make(const char* file, int line, O* o, R (O::*p)(BA1, BA2), BA1 ba1, BA2 ba2) { return new XorpSafeMemberCallback0B2(file, line, o, p, ba1, ba2); } }; template struct XorpMemberCallbackFactory0B2 { static XorpMemberCallback0B2* make(const char* file, int line, O* o, R (O::*p)(BA1, BA2), BA1 ba1, BA2 ba2) { return new XorpMemberCallback0B2(file, line, o, p, ba1, ba2); }; }; /** * Factory function that creates a callback object targetted at a * member function with 0 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback0::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(BA1, BA2), BA1 ba1, BA2 ba2) { return XorpMemberCallbackFactory0B2::True>::make(file, line, o, p, ba1, ba2); } /** * Factory function that creates a callback object targetted at a * member function with 0 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback0::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(BA1, BA2), BA1 ba1, BA2 ba2) { return XorpMemberCallbackFactory0B2::True>::make(file, line, &o, p, ba1, ba2); } /** * @short Callback object for const member methods with 0 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstMemberCallback0B2 : public XorpCallback0 { typedef R (O::*M)(BA1, BA2) const; XorpConstMemberCallback0B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback0(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} R dispatch() { record_dispatch_enter(); R r = ((*_o).*_m)(_ba1, _ba2); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for void const member methods with 0 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstMemberCallback0B2 : public XorpCallback0 { typedef void (O::*M)(BA1, BA2) const; XorpConstMemberCallback0B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback0(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} void dispatch() { record_dispatch_enter(); ((*_o).*_m)(_ba1, _ba2); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for const safe member methods with 0 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback0B2 : public XorpConstMemberCallback0B2, public SafeCallbackBase { typedef typename XorpConstMemberCallback0B2::M M; XorpConstSafeMemberCallback0B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpConstMemberCallback0B2(file, line, o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback0B2() {} R dispatch() { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback0B2::dispatch(); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 0 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback0B2 : public XorpConstMemberCallback0B2, public SafeCallbackBase { typedef typename XorpConstMemberCallback0B2::M M; XorpConstSafeMemberCallback0B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpConstMemberCallback0B2(file, line, o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback0B2() {} void dispatch() { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback0B2::dispatch(); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory0B2 { static XorpConstMemberCallback0B2* make(const char* file, int line, O* o, R (O::*p)(BA1, BA2) const, BA1 ba1, BA2 ba2) { return new XorpConstSafeMemberCallback0B2(file, line, o, p, ba1, ba2); } }; template struct XorpConstMemberCallbackFactory0B2 { static XorpConstMemberCallback0B2* make(const char* file, int line, O* o, R (O::*p)(BA1, BA2) const, BA1 ba1, BA2 ba2) { return new XorpConstMemberCallback0B2(file, line, o, p, ba1, ba2); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 0 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback0::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(BA1, BA2) const, BA1 ba1, BA2 ba2) { return XorpConstMemberCallbackFactory0B2::True>::make(file, line, o, p, ba1, ba2); } /** * Factory function that creates a callback object targetted at a * const member function with 0 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback0::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(BA1, BA2) const, BA1 ba1, BA2 ba2) { return XorpConstMemberCallbackFactory0B2::True>::make(file, line, &o, p, ba1, ba2); } /** * @short Callback object for functions with 0 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpFunctionCallback0B3 : public XorpCallback0 { typedef R (*F)(BA1, BA2, BA3); XorpFunctionCallback0B3(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback0(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch() { record_dispatch_enter(); R r = (*_f)(_ba1, _ba2, _ba3); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; }; /** * @short Callback object for void functions with 0 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpFunctionCallback0B3 : public XorpCallback0 { typedef void (*F)(BA1, BA2, BA3); XorpFunctionCallback0B3(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback0(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch() { record_dispatch_enter(); (*_f)(_ba1, _ba2, _ba3); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; }; /** * Factory function that creates a callback object targetted at a * function with 0 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback0::RefPtr dbg_callback(const char* file, int line, R (*f)(BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return typename XorpCallback0::RefPtr(new XorpFunctionCallback0B3(file, line, f, ba1, ba2, ba3)); } /** * @short Callback object for member methods with 0 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpMemberCallback0B3 : public XorpCallback0 { typedef R (O::*M)(BA1, BA2, BA3) ; XorpMemberCallback0B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback0(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch() { record_dispatch_enter(); R r = ((*_o).*_m)(_ba1, _ba2, _ba3); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for void member methods with 0 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpMemberCallback0B3 : public XorpCallback0 { typedef void (O::*M)(BA1, BA2, BA3) ; XorpMemberCallback0B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback0(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch() { record_dispatch_enter(); ((*_o).*_m)(_ba1, _ba2, _ba3); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for safe member methods with 0 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpSafeMemberCallback0B3 : public XorpMemberCallback0B3, public SafeCallbackBase { typedef typename XorpMemberCallback0B3::M M; XorpSafeMemberCallback0B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpMemberCallback0B3(file, line, o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpSafeMemberCallback0B3() {} R dispatch() { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback0B3::dispatch(); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 0 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpSafeMemberCallback0B3 : public XorpMemberCallback0B3, public SafeCallbackBase { typedef typename XorpMemberCallback0B3::M M; XorpSafeMemberCallback0B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpMemberCallback0B3(file, line, o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpSafeMemberCallback0B3() {} void dispatch() { if (valid()) { record_dispatch_enter(); XorpMemberCallback0B3::dispatch(); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory0B3 { static XorpMemberCallback0B3* make(const char* file, int line, O* o, R (O::*p)(BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpSafeMemberCallback0B3(file, line, o, p, ba1, ba2, ba3); } }; template struct XorpMemberCallbackFactory0B3 { static XorpMemberCallback0B3* make(const char* file, int line, O* o, R (O::*p)(BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpMemberCallback0B3(file, line, o, p, ba1, ba2, ba3); }; }; /** * Factory function that creates a callback object targetted at a * member function with 0 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback0::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return XorpMemberCallbackFactory0B3::True>::make(file, line, o, p, ba1, ba2, ba3); } /** * Factory function that creates a callback object targetted at a * member function with 0 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback0::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return XorpMemberCallbackFactory0B3::True>::make(file, line, &o, p, ba1, ba2, ba3); } /** * @short Callback object for const member methods with 0 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstMemberCallback0B3 : public XorpCallback0 { typedef R (O::*M)(BA1, BA2, BA3) const; XorpConstMemberCallback0B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback0(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch() { record_dispatch_enter(); R r = ((*_o).*_m)(_ba1, _ba2, _ba3); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for void const member methods with 0 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstMemberCallback0B3 : public XorpCallback0 { typedef void (O::*M)(BA1, BA2, BA3) const; XorpConstMemberCallback0B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback0(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch() { record_dispatch_enter(); ((*_o).*_m)(_ba1, _ba2, _ba3); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for const safe member methods with 0 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback0B3 : public XorpConstMemberCallback0B3, public SafeCallbackBase { typedef typename XorpConstMemberCallback0B3::M M; XorpConstSafeMemberCallback0B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpConstMemberCallback0B3(file, line, o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback0B3() {} R dispatch() { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback0B3::dispatch(); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 0 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback0B3 : public XorpConstMemberCallback0B3, public SafeCallbackBase { typedef typename XorpConstMemberCallback0B3::M M; XorpConstSafeMemberCallback0B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpConstMemberCallback0B3(file, line, o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback0B3() {} void dispatch() { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback0B3::dispatch(); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory0B3 { static XorpConstMemberCallback0B3* make(const char* file, int line, O* o, R (O::*p)(BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpConstSafeMemberCallback0B3(file, line, o, p, ba1, ba2, ba3); } }; template struct XorpConstMemberCallbackFactory0B3 { static XorpConstMemberCallback0B3* make(const char* file, int line, O* o, R (O::*p)(BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpConstMemberCallback0B3(file, line, o, p, ba1, ba2, ba3); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 0 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback0::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return XorpConstMemberCallbackFactory0B3::True>::make(file, line, o, p, ba1, ba2, ba3); } /** * Factory function that creates a callback object targetted at a * const member function with 0 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback0::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return XorpConstMemberCallbackFactory0B3::True>::make(file, line, &o, p, ba1, ba2, ba3); } /** * @short Callback object for functions with 0 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpFunctionCallback0B4 : public XorpCallback0 { typedef R (*F)(BA1, BA2, BA3, BA4); XorpFunctionCallback0B4(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback0(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch() { record_dispatch_enter(); R r = (*_f)(_ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; }; /** * @short Callback object for void functions with 0 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpFunctionCallback0B4 : public XorpCallback0 { typedef void (*F)(BA1, BA2, BA3, BA4); XorpFunctionCallback0B4(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback0(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch() { record_dispatch_enter(); (*_f)(_ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; }; /** * Factory function that creates a callback object targetted at a * function with 0 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback0::RefPtr dbg_callback(const char* file, int line, R (*f)(BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return typename XorpCallback0::RefPtr(new XorpFunctionCallback0B4(file, line, f, ba1, ba2, ba3, ba4)); } /** * @short Callback object for member methods with 0 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpMemberCallback0B4 : public XorpCallback0 { typedef R (O::*M)(BA1, BA2, BA3, BA4) ; XorpMemberCallback0B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback0(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch() { record_dispatch_enter(); R r = ((*_o).*_m)(_ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for void member methods with 0 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpMemberCallback0B4 : public XorpCallback0 { typedef void (O::*M)(BA1, BA2, BA3, BA4) ; XorpMemberCallback0B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback0(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch() { record_dispatch_enter(); ((*_o).*_m)(_ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for safe member methods with 0 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpSafeMemberCallback0B4 : public XorpMemberCallback0B4, public SafeCallbackBase { typedef typename XorpMemberCallback0B4::M M; XorpSafeMemberCallback0B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpMemberCallback0B4(file, line, o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpSafeMemberCallback0B4() {} R dispatch() { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback0B4::dispatch(); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 0 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpSafeMemberCallback0B4 : public XorpMemberCallback0B4, public SafeCallbackBase { typedef typename XorpMemberCallback0B4::M M; XorpSafeMemberCallback0B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpMemberCallback0B4(file, line, o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpSafeMemberCallback0B4() {} void dispatch() { if (valid()) { record_dispatch_enter(); XorpMemberCallback0B4::dispatch(); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory0B4 { static XorpMemberCallback0B4* make(const char* file, int line, O* o, R (O::*p)(BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpSafeMemberCallback0B4(file, line, o, p, ba1, ba2, ba3, ba4); } }; template struct XorpMemberCallbackFactory0B4 { static XorpMemberCallback0B4* make(const char* file, int line, O* o, R (O::*p)(BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpMemberCallback0B4(file, line, o, p, ba1, ba2, ba3, ba4); }; }; /** * Factory function that creates a callback object targetted at a * member function with 0 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback0::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpMemberCallbackFactory0B4::True>::make(file, line, o, p, ba1, ba2, ba3, ba4); } /** * Factory function that creates a callback object targetted at a * member function with 0 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback0::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpMemberCallbackFactory0B4::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4); } /** * @short Callback object for const member methods with 0 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstMemberCallback0B4 : public XorpCallback0 { typedef R (O::*M)(BA1, BA2, BA3, BA4) const; XorpConstMemberCallback0B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback0(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch() { record_dispatch_enter(); R r = ((*_o).*_m)(_ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for void const member methods with 0 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstMemberCallback0B4 : public XorpCallback0 { typedef void (O::*M)(BA1, BA2, BA3, BA4) const; XorpConstMemberCallback0B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback0(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch() { record_dispatch_enter(); ((*_o).*_m)(_ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for const safe member methods with 0 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback0B4 : public XorpConstMemberCallback0B4, public SafeCallbackBase { typedef typename XorpConstMemberCallback0B4::M M; XorpConstSafeMemberCallback0B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpConstMemberCallback0B4(file, line, o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback0B4() {} R dispatch() { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback0B4::dispatch(); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 0 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback0B4 : public XorpConstMemberCallback0B4, public SafeCallbackBase { typedef typename XorpConstMemberCallback0B4::M M; XorpConstSafeMemberCallback0B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpConstMemberCallback0B4(file, line, o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback0B4() {} void dispatch() { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback0B4::dispatch(); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory0B4 { static XorpConstMemberCallback0B4* make(const char* file, int line, O* o, R (O::*p)(BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpConstSafeMemberCallback0B4(file, line, o, p, ba1, ba2, ba3, ba4); } }; template struct XorpConstMemberCallbackFactory0B4 { static XorpConstMemberCallback0B4* make(const char* file, int line, O* o, R (O::*p)(BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpConstMemberCallback0B4(file, line, o, p, ba1, ba2, ba3, ba4); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 0 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback0::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpConstMemberCallbackFactory0B4::True>::make(file, line, o, p, ba1, ba2, ba3, ba4); } /** * Factory function that creates a callback object targetted at a * const member function with 0 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback0::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpConstMemberCallbackFactory0B4::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4); } /** * @short Callback object for functions with 0 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpFunctionCallback0B5 : public XorpCallback0 { typedef R (*F)(BA1, BA2, BA3, BA4, BA5); XorpFunctionCallback0B5(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback0(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch() { record_dispatch_enter(); R r = (*_f)(_ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; }; /** * @short Callback object for void functions with 0 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpFunctionCallback0B5 : public XorpCallback0 { typedef void (*F)(BA1, BA2, BA3, BA4, BA5); XorpFunctionCallback0B5(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback0(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch() { record_dispatch_enter(); (*_f)(_ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; }; /** * Factory function that creates a callback object targetted at a * function with 0 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback0::RefPtr dbg_callback(const char* file, int line, R (*f)(BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return typename XorpCallback0::RefPtr(new XorpFunctionCallback0B5(file, line, f, ba1, ba2, ba3, ba4, ba5)); } /** * @short Callback object for member methods with 0 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpMemberCallback0B5 : public XorpCallback0 { typedef R (O::*M)(BA1, BA2, BA3, BA4, BA5) ; XorpMemberCallback0B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback0(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch() { record_dispatch_enter(); R r = ((*_o).*_m)(_ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for void member methods with 0 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpMemberCallback0B5 : public XorpCallback0 { typedef void (O::*M)(BA1, BA2, BA3, BA4, BA5) ; XorpMemberCallback0B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback0(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch() { record_dispatch_enter(); ((*_o).*_m)(_ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for safe member methods with 0 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpSafeMemberCallback0B5 : public XorpMemberCallback0B5, public SafeCallbackBase { typedef typename XorpMemberCallback0B5::M M; XorpSafeMemberCallback0B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpMemberCallback0B5(file, line, o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpSafeMemberCallback0B5() {} R dispatch() { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback0B5::dispatch(); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 0 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpSafeMemberCallback0B5 : public XorpMemberCallback0B5, public SafeCallbackBase { typedef typename XorpMemberCallback0B5::M M; XorpSafeMemberCallback0B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpMemberCallback0B5(file, line, o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpSafeMemberCallback0B5() {} void dispatch() { if (valid()) { record_dispatch_enter(); XorpMemberCallback0B5::dispatch(); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory0B5 { static XorpMemberCallback0B5* make(const char* file, int line, O* o, R (O::*p)(BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpSafeMemberCallback0B5(file, line, o, p, ba1, ba2, ba3, ba4, ba5); } }; template struct XorpMemberCallbackFactory0B5 { static XorpMemberCallback0B5* make(const char* file, int line, O* o, R (O::*p)(BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpMemberCallback0B5(file, line, o, p, ba1, ba2, ba3, ba4, ba5); }; }; /** * Factory function that creates a callback object targetted at a * member function with 0 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback0::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpMemberCallbackFactory0B5::True>::make(file, line, o, p, ba1, ba2, ba3, ba4, ba5); } /** * Factory function that creates a callback object targetted at a * member function with 0 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback0::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpMemberCallbackFactory0B5::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4, ba5); } /** * @short Callback object for const member methods with 0 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstMemberCallback0B5 : public XorpCallback0 { typedef R (O::*M)(BA1, BA2, BA3, BA4, BA5) const; XorpConstMemberCallback0B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback0(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch() { record_dispatch_enter(); R r = ((*_o).*_m)(_ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for void const member methods with 0 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstMemberCallback0B5 : public XorpCallback0 { typedef void (O::*M)(BA1, BA2, BA3, BA4, BA5) const; XorpConstMemberCallback0B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback0(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch() { record_dispatch_enter(); ((*_o).*_m)(_ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for const safe member methods with 0 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback0B5 : public XorpConstMemberCallback0B5, public SafeCallbackBase { typedef typename XorpConstMemberCallback0B5::M M; XorpConstSafeMemberCallback0B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpConstMemberCallback0B5(file, line, o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback0B5() {} R dispatch() { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback0B5::dispatch(); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 0 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback0B5 : public XorpConstMemberCallback0B5, public SafeCallbackBase { typedef typename XorpConstMemberCallback0B5::M M; XorpConstSafeMemberCallback0B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpConstMemberCallback0B5(file, line, o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback0B5() {} void dispatch() { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback0B5::dispatch(); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory0B5 { static XorpConstMemberCallback0B5* make(const char* file, int line, O* o, R (O::*p)(BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpConstSafeMemberCallback0B5(file, line, o, p, ba1, ba2, ba3, ba4, ba5); } }; template struct XorpConstMemberCallbackFactory0B5 { static XorpConstMemberCallback0B5* make(const char* file, int line, O* o, R (O::*p)(BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpConstMemberCallback0B5(file, line, o, p, ba1, ba2, ba3, ba4, ba5); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 0 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback0::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpConstMemberCallbackFactory0B5::True>::make(file, line, o, p, ba1, ba2, ba3, ba4, ba5); } /** * Factory function that creates a callback object targetted at a * const member function with 0 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback0::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpConstMemberCallbackFactory0B5::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4, ba5); } /** * @short Callback object for functions with 0 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpFunctionCallback0B6 : public XorpCallback0 { typedef R (*F)(BA1, BA2, BA3, BA4, BA5, BA6); XorpFunctionCallback0B6(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback0(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch() { record_dispatch_enter(); R r = (*_f)(_ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; BA6 _ba6; }; /** * @short Callback object for void functions with 0 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpFunctionCallback0B6 : public XorpCallback0 { typedef void (*F)(BA1, BA2, BA3, BA4, BA5, BA6); XorpFunctionCallback0B6(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback0(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch() { record_dispatch_enter(); (*_f)(_ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; BA6 _ba6; }; /** * Factory function that creates a callback object targetted at a * function with 0 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback0::RefPtr dbg_callback(const char* file, int line, R (*f)(BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return typename XorpCallback0::RefPtr(new XorpFunctionCallback0B6(file, line, f, ba1, ba2, ba3, ba4, ba5, ba6)); } /** * @short Callback object for member methods with 0 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpMemberCallback0B6 : public XorpCallback0 { typedef R (O::*M)(BA1, BA2, BA3, BA4, BA5, BA6) ; XorpMemberCallback0B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback0(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch() { record_dispatch_enter(); R r = ((*_o).*_m)(_ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for void member methods with 0 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpMemberCallback0B6 : public XorpCallback0 { typedef void (O::*M)(BA1, BA2, BA3, BA4, BA5, BA6) ; XorpMemberCallback0B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback0(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch() { record_dispatch_enter(); ((*_o).*_m)(_ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for safe member methods with 0 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpSafeMemberCallback0B6 : public XorpMemberCallback0B6, public SafeCallbackBase { typedef typename XorpMemberCallback0B6::M M; XorpSafeMemberCallback0B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpMemberCallback0B6(file, line, o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpSafeMemberCallback0B6() {} R dispatch() { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback0B6::dispatch(); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 0 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpSafeMemberCallback0B6 : public XorpMemberCallback0B6, public SafeCallbackBase { typedef typename XorpMemberCallback0B6::M M; XorpSafeMemberCallback0B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpMemberCallback0B6(file, line, o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpSafeMemberCallback0B6() {} void dispatch() { if (valid()) { record_dispatch_enter(); XorpMemberCallback0B6::dispatch(); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory0B6 { static XorpMemberCallback0B6* make(const char* file, int line, O* o, R (O::*p)(BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpSafeMemberCallback0B6(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); } }; template struct XorpMemberCallbackFactory0B6 { static XorpMemberCallback0B6* make(const char* file, int line, O* o, R (O::*p)(BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpMemberCallback0B6(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); }; }; /** * Factory function that creates a callback object targetted at a * member function with 0 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback0::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpMemberCallbackFactory0B6::True>::make(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * Factory function that creates a callback object targetted at a * member function with 0 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback0::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpMemberCallbackFactory0B6::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * @short Callback object for const member methods with 0 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstMemberCallback0B6 : public XorpCallback0 { typedef R (O::*M)(BA1, BA2, BA3, BA4, BA5, BA6) const; XorpConstMemberCallback0B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback0(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch() { record_dispatch_enter(); R r = ((*_o).*_m)(_ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for void const member methods with 0 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstMemberCallback0B6 : public XorpCallback0 { typedef void (O::*M)(BA1, BA2, BA3, BA4, BA5, BA6) const; XorpConstMemberCallback0B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback0(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch() { record_dispatch_enter(); ((*_o).*_m)(_ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for const safe member methods with 0 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback0B6 : public XorpConstMemberCallback0B6, public SafeCallbackBase { typedef typename XorpConstMemberCallback0B6::M M; XorpConstSafeMemberCallback0B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpConstMemberCallback0B6(file, line, o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback0B6() {} R dispatch() { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback0B6::dispatch(); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 0 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback0B6 : public XorpConstMemberCallback0B6, public SafeCallbackBase { typedef typename XorpConstMemberCallback0B6::M M; XorpConstSafeMemberCallback0B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpConstMemberCallback0B6(file, line, o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback0B6() {} void dispatch() { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback0B6::dispatch(); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory0B6 { static XorpConstMemberCallback0B6* make(const char* file, int line, O* o, R (O::*p)(BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpConstSafeMemberCallback0B6(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); } }; template struct XorpConstMemberCallbackFactory0B6 { static XorpConstMemberCallback0B6* make(const char* file, int line, O* o, R (O::*p)(BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpConstMemberCallback0B6(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 0 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback0::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpConstMemberCallbackFactory0B6::True>::make(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * Factory function that creates a callback object targetted at a * const member function with 0 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback0::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpConstMemberCallbackFactory0B6::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /////////////////////////////////////////////////////////////////////////////// // // Code relating to callbacks with 1 late args // /** * @short Base class for callbacks with 1 dispatch time args. */ template struct XorpCallback1 { typedef ref_ptr RefPtr; XorpCallback1(const char* file, int line) : _file(file), _line(line) {} virtual ~XorpCallback1() {} virtual R dispatch(A1) = 0; const char* file() const { return _file; } int line() const { return _line; } private: const char* _file; int _line; }; /** * @short Callback object for functions with 1 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpFunctionCallback1B0 : public XorpCallback1 { typedef R (*F)(A1); XorpFunctionCallback1B0(const char* file, int line, F f) : XorpCallback1(file, line), _f(f) {} R dispatch(A1 a1) { record_dispatch_enter(); R r = (*_f)(a1); record_dispatch_leave(); return r; } protected: F _f; }; /** * @short Callback object for void functions with 1 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpFunctionCallback1B0 : public XorpCallback1 { typedef void (*F)(A1); XorpFunctionCallback1B0(const char* file, int line, F f) : XorpCallback1(file, line), _f(f) {} void dispatch(A1 a1) { record_dispatch_enter(); (*_f)(a1); record_dispatch_leave(); } protected: F _f; }; /** * Factory function that creates a callback object targetted at a * function with 1 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback1::RefPtr dbg_callback(const char* file, int line, R (*f)(A1)) { return typename XorpCallback1::RefPtr(new XorpFunctionCallback1B0(file, line, f)); } /** * @short Callback object for member methods with 1 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpMemberCallback1B0 : public XorpCallback1 { typedef R (O::*M)(A1) ; XorpMemberCallback1B0(const char* file, int line, O* o, M m) : XorpCallback1(file, line), _o(o), _m(m) {} R dispatch(A1 a1) { record_dispatch_enter(); R r = ((*_o).*_m)(a1); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for void member methods with 1 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpMemberCallback1B0 : public XorpCallback1 { typedef void (O::*M)(A1) ; XorpMemberCallback1B0(const char* file, int line, O* o, M m) : XorpCallback1(file, line), _o(o), _m(m) {} void dispatch(A1 a1) { record_dispatch_enter(); ((*_o).*_m)(a1); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for safe member methods with 1 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpSafeMemberCallback1B0 : public XorpMemberCallback1B0, public SafeCallbackBase { typedef typename XorpMemberCallback1B0::M M; XorpSafeMemberCallback1B0(const char* file, int line, O* o, M m) : XorpMemberCallback1B0(file, line, o, m), SafeCallbackBase(o) {} ~XorpSafeMemberCallback1B0() {} R dispatch(A1 a1) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback1B0::dispatch(a1); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 1 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpSafeMemberCallback1B0 : public XorpMemberCallback1B0, public SafeCallbackBase { typedef typename XorpMemberCallback1B0::M M; XorpSafeMemberCallback1B0(const char* file, int line, O* o, M m) : XorpMemberCallback1B0(file, line, o, m), SafeCallbackBase(o) {} ~XorpSafeMemberCallback1B0() {} void dispatch(A1 a1) { if (valid()) { record_dispatch_enter(); XorpMemberCallback1B0::dispatch(a1); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory1B0 { static XorpMemberCallback1B0* make(const char* file, int line, O* o, R (O::*p)(A1)) { return new XorpSafeMemberCallback1B0(file, line, o, p); } }; template struct XorpMemberCallbackFactory1B0 { static XorpMemberCallback1B0* make(const char* file, int line, O* o, R (O::*p)(A1)) { return new XorpMemberCallback1B0(file, line, o, p); }; }; /** * Factory function that creates a callback object targetted at a * member function with 1 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback1::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1)) { return XorpMemberCallbackFactory1B0::True>::make(file, line, o, p); } /** * Factory function that creates a callback object targetted at a * member function with 1 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback1::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1)) { return XorpMemberCallbackFactory1B0::True>::make(file, line, &o, p); } /** * @short Callback object for const member methods with 1 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstMemberCallback1B0 : public XorpCallback1 { typedef R (O::*M)(A1) const; XorpConstMemberCallback1B0(const char* file, int line, O* o, M m) : XorpCallback1(file, line), _o(o), _m(m) {} R dispatch(A1 a1) { record_dispatch_enter(); R r = ((*_o).*_m)(a1); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for void const member methods with 1 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstMemberCallback1B0 : public XorpCallback1 { typedef void (O::*M)(A1) const; XorpConstMemberCallback1B0(const char* file, int line, O* o, M m) : XorpCallback1(file, line), _o(o), _m(m) {} void dispatch(A1 a1) { record_dispatch_enter(); ((*_o).*_m)(a1); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for const safe member methods with 1 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback1B0 : public XorpConstMemberCallback1B0, public SafeCallbackBase { typedef typename XorpConstMemberCallback1B0::M M; XorpConstSafeMemberCallback1B0(const char* file, int line, O* o, M m) : XorpConstMemberCallback1B0(file, line, o, m), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback1B0() {} R dispatch(A1 a1) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback1B0::dispatch(a1); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 1 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback1B0 : public XorpConstMemberCallback1B0, public SafeCallbackBase { typedef typename XorpConstMemberCallback1B0::M M; XorpConstSafeMemberCallback1B0(const char* file, int line, O* o, M m) : XorpConstMemberCallback1B0(file, line, o, m), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback1B0() {} void dispatch(A1 a1) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback1B0::dispatch(a1); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory1B0 { static XorpConstMemberCallback1B0* make(const char* file, int line, O* o, R (O::*p)(A1) const) { return new XorpConstSafeMemberCallback1B0(file, line, o, p); } }; template struct XorpConstMemberCallbackFactory1B0 { static XorpConstMemberCallback1B0* make(const char* file, int line, O* o, R (O::*p)(A1) const) { return new XorpConstMemberCallback1B0(file, line, o, p); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 1 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback1::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1) const) { return XorpConstMemberCallbackFactory1B0::True>::make(file, line, o, p); } /** * Factory function that creates a callback object targetted at a * const member function with 1 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback1::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1) const) { return XorpConstMemberCallbackFactory1B0::True>::make(file, line, &o, p); } /** * @short Callback object for functions with 1 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpFunctionCallback1B1 : public XorpCallback1 { typedef R (*F)(A1, BA1); XorpFunctionCallback1B1(const char* file, int line, F f, BA1 ba1) : XorpCallback1(file, line), _f(f), _ba1(ba1) {} R dispatch(A1 a1) { record_dispatch_enter(); R r = (*_f)(a1, _ba1); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; }; /** * @short Callback object for void functions with 1 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpFunctionCallback1B1 : public XorpCallback1 { typedef void (*F)(A1, BA1); XorpFunctionCallback1B1(const char* file, int line, F f, BA1 ba1) : XorpCallback1(file, line), _f(f), _ba1(ba1) {} void dispatch(A1 a1) { record_dispatch_enter(); (*_f)(a1, _ba1); record_dispatch_leave(); } protected: F _f; BA1 _ba1; }; /** * Factory function that creates a callback object targetted at a * function with 1 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback1::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, BA1), BA1 ba1) { return typename XorpCallback1::RefPtr(new XorpFunctionCallback1B1(file, line, f, ba1)); } /** * @short Callback object for member methods with 1 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpMemberCallback1B1 : public XorpCallback1 { typedef R (O::*M)(A1, BA1) ; XorpMemberCallback1B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpCallback1(file, line), _o(o), _m(m), _ba1(ba1) {} R dispatch(A1 a1) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, _ba1); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for void member methods with 1 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpMemberCallback1B1 : public XorpCallback1 { typedef void (O::*M)(A1, BA1) ; XorpMemberCallback1B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpCallback1(file, line), _o(o), _m(m), _ba1(ba1) {} void dispatch(A1 a1) { record_dispatch_enter(); ((*_o).*_m)(a1, _ba1); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for safe member methods with 1 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpSafeMemberCallback1B1 : public XorpMemberCallback1B1, public SafeCallbackBase { typedef typename XorpMemberCallback1B1::M M; XorpSafeMemberCallback1B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpMemberCallback1B1(file, line, o, m, ba1), SafeCallbackBase(o) {} ~XorpSafeMemberCallback1B1() {} R dispatch(A1 a1) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback1B1::dispatch(a1); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 1 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpSafeMemberCallback1B1 : public XorpMemberCallback1B1, public SafeCallbackBase { typedef typename XorpMemberCallback1B1::M M; XorpSafeMemberCallback1B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpMemberCallback1B1(file, line, o, m, ba1), SafeCallbackBase(o) {} ~XorpSafeMemberCallback1B1() {} void dispatch(A1 a1) { if (valid()) { record_dispatch_enter(); XorpMemberCallback1B1::dispatch(a1); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory1B1 { static XorpMemberCallback1B1* make(const char* file, int line, O* o, R (O::*p)(A1, BA1), BA1 ba1) { return new XorpSafeMemberCallback1B1(file, line, o, p, ba1); } }; template struct XorpMemberCallbackFactory1B1 { static XorpMemberCallback1B1* make(const char* file, int line, O* o, R (O::*p)(A1, BA1), BA1 ba1) { return new XorpMemberCallback1B1(file, line, o, p, ba1); }; }; /** * Factory function that creates a callback object targetted at a * member function with 1 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback1::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, BA1), BA1 ba1) { return XorpMemberCallbackFactory1B1::True>::make(file, line, o, p, ba1); } /** * Factory function that creates a callback object targetted at a * member function with 1 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback1::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, BA1), BA1 ba1) { return XorpMemberCallbackFactory1B1::True>::make(file, line, &o, p, ba1); } /** * @short Callback object for const member methods with 1 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstMemberCallback1B1 : public XorpCallback1 { typedef R (O::*M)(A1, BA1) const; XorpConstMemberCallback1B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpCallback1(file, line), _o(o), _m(m), _ba1(ba1) {} R dispatch(A1 a1) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, _ba1); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for void const member methods with 1 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstMemberCallback1B1 : public XorpCallback1 { typedef void (O::*M)(A1, BA1) const; XorpConstMemberCallback1B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpCallback1(file, line), _o(o), _m(m), _ba1(ba1) {} void dispatch(A1 a1) { record_dispatch_enter(); ((*_o).*_m)(a1, _ba1); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for const safe member methods with 1 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback1B1 : public XorpConstMemberCallback1B1, public SafeCallbackBase { typedef typename XorpConstMemberCallback1B1::M M; XorpConstSafeMemberCallback1B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpConstMemberCallback1B1(file, line, o, m, ba1), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback1B1() {} R dispatch(A1 a1) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback1B1::dispatch(a1); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 1 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback1B1 : public XorpConstMemberCallback1B1, public SafeCallbackBase { typedef typename XorpConstMemberCallback1B1::M M; XorpConstSafeMemberCallback1B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpConstMemberCallback1B1(file, line, o, m, ba1), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback1B1() {} void dispatch(A1 a1) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback1B1::dispatch(a1); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory1B1 { static XorpConstMemberCallback1B1* make(const char* file, int line, O* o, R (O::*p)(A1, BA1) const, BA1 ba1) { return new XorpConstSafeMemberCallback1B1(file, line, o, p, ba1); } }; template struct XorpConstMemberCallbackFactory1B1 { static XorpConstMemberCallback1B1* make(const char* file, int line, O* o, R (O::*p)(A1, BA1) const, BA1 ba1) { return new XorpConstMemberCallback1B1(file, line, o, p, ba1); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 1 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback1::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, BA1) const, BA1 ba1) { return XorpConstMemberCallbackFactory1B1::True>::make(file, line, o, p, ba1); } /** * Factory function that creates a callback object targetted at a * const member function with 1 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback1::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, BA1) const, BA1 ba1) { return XorpConstMemberCallbackFactory1B1::True>::make(file, line, &o, p, ba1); } /** * @short Callback object for functions with 1 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpFunctionCallback1B2 : public XorpCallback1 { typedef R (*F)(A1, BA1, BA2); XorpFunctionCallback1B2(const char* file, int line, F f, BA1 ba1, BA2 ba2) : XorpCallback1(file, line), _f(f), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1) { record_dispatch_enter(); R r = (*_f)(a1, _ba1, _ba2); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; }; /** * @short Callback object for void functions with 1 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpFunctionCallback1B2 : public XorpCallback1 { typedef void (*F)(A1, BA1, BA2); XorpFunctionCallback1B2(const char* file, int line, F f, BA1 ba1, BA2 ba2) : XorpCallback1(file, line), _f(f), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1) { record_dispatch_enter(); (*_f)(a1, _ba1, _ba2); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; }; /** * Factory function that creates a callback object targetted at a * function with 1 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback1::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, BA1, BA2), BA1 ba1, BA2 ba2) { return typename XorpCallback1::RefPtr(new XorpFunctionCallback1B2(file, line, f, ba1, ba2)); } /** * @short Callback object for member methods with 1 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpMemberCallback1B2 : public XorpCallback1 { typedef R (O::*M)(A1, BA1, BA2) ; XorpMemberCallback1B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback1(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, _ba1, _ba2); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for void member methods with 1 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpMemberCallback1B2 : public XorpCallback1 { typedef void (O::*M)(A1, BA1, BA2) ; XorpMemberCallback1B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback1(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1) { record_dispatch_enter(); ((*_o).*_m)(a1, _ba1, _ba2); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for safe member methods with 1 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpSafeMemberCallback1B2 : public XorpMemberCallback1B2, public SafeCallbackBase { typedef typename XorpMemberCallback1B2::M M; XorpSafeMemberCallback1B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpMemberCallback1B2(file, line, o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpSafeMemberCallback1B2() {} R dispatch(A1 a1) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback1B2::dispatch(a1); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 1 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpSafeMemberCallback1B2 : public XorpMemberCallback1B2, public SafeCallbackBase { typedef typename XorpMemberCallback1B2::M M; XorpSafeMemberCallback1B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpMemberCallback1B2(file, line, o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpSafeMemberCallback1B2() {} void dispatch(A1 a1) { if (valid()) { record_dispatch_enter(); XorpMemberCallback1B2::dispatch(a1); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory1B2 { static XorpMemberCallback1B2* make(const char* file, int line, O* o, R (O::*p)(A1, BA1, BA2), BA1 ba1, BA2 ba2) { return new XorpSafeMemberCallback1B2(file, line, o, p, ba1, ba2); } }; template struct XorpMemberCallbackFactory1B2 { static XorpMemberCallback1B2* make(const char* file, int line, O* o, R (O::*p)(A1, BA1, BA2), BA1 ba1, BA2 ba2) { return new XorpMemberCallback1B2(file, line, o, p, ba1, ba2); }; }; /** * Factory function that creates a callback object targetted at a * member function with 1 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback1::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, BA1, BA2), BA1 ba1, BA2 ba2) { return XorpMemberCallbackFactory1B2::True>::make(file, line, o, p, ba1, ba2); } /** * Factory function that creates a callback object targetted at a * member function with 1 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback1::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, BA1, BA2), BA1 ba1, BA2 ba2) { return XorpMemberCallbackFactory1B2::True>::make(file, line, &o, p, ba1, ba2); } /** * @short Callback object for const member methods with 1 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstMemberCallback1B2 : public XorpCallback1 { typedef R (O::*M)(A1, BA1, BA2) const; XorpConstMemberCallback1B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback1(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, _ba1, _ba2); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for void const member methods with 1 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstMemberCallback1B2 : public XorpCallback1 { typedef void (O::*M)(A1, BA1, BA2) const; XorpConstMemberCallback1B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback1(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1) { record_dispatch_enter(); ((*_o).*_m)(a1, _ba1, _ba2); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for const safe member methods with 1 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback1B2 : public XorpConstMemberCallback1B2, public SafeCallbackBase { typedef typename XorpConstMemberCallback1B2::M M; XorpConstSafeMemberCallback1B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpConstMemberCallback1B2(file, line, o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback1B2() {} R dispatch(A1 a1) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback1B2::dispatch(a1); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 1 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback1B2 : public XorpConstMemberCallback1B2, public SafeCallbackBase { typedef typename XorpConstMemberCallback1B2::M M; XorpConstSafeMemberCallback1B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpConstMemberCallback1B2(file, line, o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback1B2() {} void dispatch(A1 a1) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback1B2::dispatch(a1); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory1B2 { static XorpConstMemberCallback1B2* make(const char* file, int line, O* o, R (O::*p)(A1, BA1, BA2) const, BA1 ba1, BA2 ba2) { return new XorpConstSafeMemberCallback1B2(file, line, o, p, ba1, ba2); } }; template struct XorpConstMemberCallbackFactory1B2 { static XorpConstMemberCallback1B2* make(const char* file, int line, O* o, R (O::*p)(A1, BA1, BA2) const, BA1 ba1, BA2 ba2) { return new XorpConstMemberCallback1B2(file, line, o, p, ba1, ba2); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 1 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback1::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, BA1, BA2) const, BA1 ba1, BA2 ba2) { return XorpConstMemberCallbackFactory1B2::True>::make(file, line, o, p, ba1, ba2); } /** * Factory function that creates a callback object targetted at a * const member function with 1 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback1::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, BA1, BA2) const, BA1 ba1, BA2 ba2) { return XorpConstMemberCallbackFactory1B2::True>::make(file, line, &o, p, ba1, ba2); } /** * @short Callback object for functions with 1 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpFunctionCallback1B3 : public XorpCallback1 { typedef R (*F)(A1, BA1, BA2, BA3); XorpFunctionCallback1B3(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback1(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1) { record_dispatch_enter(); R r = (*_f)(a1, _ba1, _ba2, _ba3); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; }; /** * @short Callback object for void functions with 1 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpFunctionCallback1B3 : public XorpCallback1 { typedef void (*F)(A1, BA1, BA2, BA3); XorpFunctionCallback1B3(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback1(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1) { record_dispatch_enter(); (*_f)(a1, _ba1, _ba2, _ba3); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; }; /** * Factory function that creates a callback object targetted at a * function with 1 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback1::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return typename XorpCallback1::RefPtr(new XorpFunctionCallback1B3(file, line, f, ba1, ba2, ba3)); } /** * @short Callback object for member methods with 1 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpMemberCallback1B3 : public XorpCallback1 { typedef R (O::*M)(A1, BA1, BA2, BA3) ; XorpMemberCallback1B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback1(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, _ba1, _ba2, _ba3); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for void member methods with 1 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpMemberCallback1B3 : public XorpCallback1 { typedef void (O::*M)(A1, BA1, BA2, BA3) ; XorpMemberCallback1B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback1(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1) { record_dispatch_enter(); ((*_o).*_m)(a1, _ba1, _ba2, _ba3); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for safe member methods with 1 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpSafeMemberCallback1B3 : public XorpMemberCallback1B3, public SafeCallbackBase { typedef typename XorpMemberCallback1B3::M M; XorpSafeMemberCallback1B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpMemberCallback1B3(file, line, o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpSafeMemberCallback1B3() {} R dispatch(A1 a1) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback1B3::dispatch(a1); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 1 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpSafeMemberCallback1B3 : public XorpMemberCallback1B3, public SafeCallbackBase { typedef typename XorpMemberCallback1B3::M M; XorpSafeMemberCallback1B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpMemberCallback1B3(file, line, o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpSafeMemberCallback1B3() {} void dispatch(A1 a1) { if (valid()) { record_dispatch_enter(); XorpMemberCallback1B3::dispatch(a1); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory1B3 { static XorpMemberCallback1B3* make(const char* file, int line, O* o, R (O::*p)(A1, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpSafeMemberCallback1B3(file, line, o, p, ba1, ba2, ba3); } }; template struct XorpMemberCallbackFactory1B3 { static XorpMemberCallback1B3* make(const char* file, int line, O* o, R (O::*p)(A1, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpMemberCallback1B3(file, line, o, p, ba1, ba2, ba3); }; }; /** * Factory function that creates a callback object targetted at a * member function with 1 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback1::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return XorpMemberCallbackFactory1B3::True>::make(file, line, o, p, ba1, ba2, ba3); } /** * Factory function that creates a callback object targetted at a * member function with 1 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback1::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return XorpMemberCallbackFactory1B3::True>::make(file, line, &o, p, ba1, ba2, ba3); } /** * @short Callback object for const member methods with 1 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstMemberCallback1B3 : public XorpCallback1 { typedef R (O::*M)(A1, BA1, BA2, BA3) const; XorpConstMemberCallback1B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback1(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, _ba1, _ba2, _ba3); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for void const member methods with 1 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstMemberCallback1B3 : public XorpCallback1 { typedef void (O::*M)(A1, BA1, BA2, BA3) const; XorpConstMemberCallback1B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback1(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1) { record_dispatch_enter(); ((*_o).*_m)(a1, _ba1, _ba2, _ba3); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for const safe member methods with 1 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback1B3 : public XorpConstMemberCallback1B3, public SafeCallbackBase { typedef typename XorpConstMemberCallback1B3::M M; XorpConstSafeMemberCallback1B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpConstMemberCallback1B3(file, line, o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback1B3() {} R dispatch(A1 a1) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback1B3::dispatch(a1); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 1 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback1B3 : public XorpConstMemberCallback1B3, public SafeCallbackBase { typedef typename XorpConstMemberCallback1B3::M M; XorpConstSafeMemberCallback1B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpConstMemberCallback1B3(file, line, o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback1B3() {} void dispatch(A1 a1) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback1B3::dispatch(a1); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory1B3 { static XorpConstMemberCallback1B3* make(const char* file, int line, O* o, R (O::*p)(A1, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpConstSafeMemberCallback1B3(file, line, o, p, ba1, ba2, ba3); } }; template struct XorpConstMemberCallbackFactory1B3 { static XorpConstMemberCallback1B3* make(const char* file, int line, O* o, R (O::*p)(A1, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpConstMemberCallback1B3(file, line, o, p, ba1, ba2, ba3); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 1 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback1::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return XorpConstMemberCallbackFactory1B3::True>::make(file, line, o, p, ba1, ba2, ba3); } /** * Factory function that creates a callback object targetted at a * const member function with 1 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback1::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return XorpConstMemberCallbackFactory1B3::True>::make(file, line, &o, p, ba1, ba2, ba3); } /** * @short Callback object for functions with 1 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpFunctionCallback1B4 : public XorpCallback1 { typedef R (*F)(A1, BA1, BA2, BA3, BA4); XorpFunctionCallback1B4(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback1(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1) { record_dispatch_enter(); R r = (*_f)(a1, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; }; /** * @short Callback object for void functions with 1 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpFunctionCallback1B4 : public XorpCallback1 { typedef void (*F)(A1, BA1, BA2, BA3, BA4); XorpFunctionCallback1B4(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback1(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1) { record_dispatch_enter(); (*_f)(a1, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; }; /** * Factory function that creates a callback object targetted at a * function with 1 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback1::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return typename XorpCallback1::RefPtr(new XorpFunctionCallback1B4(file, line, f, ba1, ba2, ba3, ba4)); } /** * @short Callback object for member methods with 1 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpMemberCallback1B4 : public XorpCallback1 { typedef R (O::*M)(A1, BA1, BA2, BA3, BA4) ; XorpMemberCallback1B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback1(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for void member methods with 1 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpMemberCallback1B4 : public XorpCallback1 { typedef void (O::*M)(A1, BA1, BA2, BA3, BA4) ; XorpMemberCallback1B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback1(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1) { record_dispatch_enter(); ((*_o).*_m)(a1, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for safe member methods with 1 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpSafeMemberCallback1B4 : public XorpMemberCallback1B4, public SafeCallbackBase { typedef typename XorpMemberCallback1B4::M M; XorpSafeMemberCallback1B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpMemberCallback1B4(file, line, o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpSafeMemberCallback1B4() {} R dispatch(A1 a1) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback1B4::dispatch(a1); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 1 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpSafeMemberCallback1B4 : public XorpMemberCallback1B4, public SafeCallbackBase { typedef typename XorpMemberCallback1B4::M M; XorpSafeMemberCallback1B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpMemberCallback1B4(file, line, o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpSafeMemberCallback1B4() {} void dispatch(A1 a1) { if (valid()) { record_dispatch_enter(); XorpMemberCallback1B4::dispatch(a1); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory1B4 { static XorpMemberCallback1B4* make(const char* file, int line, O* o, R (O::*p)(A1, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpSafeMemberCallback1B4(file, line, o, p, ba1, ba2, ba3, ba4); } }; template struct XorpMemberCallbackFactory1B4 { static XorpMemberCallback1B4* make(const char* file, int line, O* o, R (O::*p)(A1, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpMemberCallback1B4(file, line, o, p, ba1, ba2, ba3, ba4); }; }; /** * Factory function that creates a callback object targetted at a * member function with 1 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback1::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpMemberCallbackFactory1B4::True>::make(file, line, o, p, ba1, ba2, ba3, ba4); } /** * Factory function that creates a callback object targetted at a * member function with 1 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback1::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpMemberCallbackFactory1B4::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4); } /** * @short Callback object for const member methods with 1 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstMemberCallback1B4 : public XorpCallback1 { typedef R (O::*M)(A1, BA1, BA2, BA3, BA4) const; XorpConstMemberCallback1B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback1(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for void const member methods with 1 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstMemberCallback1B4 : public XorpCallback1 { typedef void (O::*M)(A1, BA1, BA2, BA3, BA4) const; XorpConstMemberCallback1B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback1(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1) { record_dispatch_enter(); ((*_o).*_m)(a1, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for const safe member methods with 1 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback1B4 : public XorpConstMemberCallback1B4, public SafeCallbackBase { typedef typename XorpConstMemberCallback1B4::M M; XorpConstSafeMemberCallback1B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpConstMemberCallback1B4(file, line, o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback1B4() {} R dispatch(A1 a1) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback1B4::dispatch(a1); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 1 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback1B4 : public XorpConstMemberCallback1B4, public SafeCallbackBase { typedef typename XorpConstMemberCallback1B4::M M; XorpConstSafeMemberCallback1B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpConstMemberCallback1B4(file, line, o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback1B4() {} void dispatch(A1 a1) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback1B4::dispatch(a1); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory1B4 { static XorpConstMemberCallback1B4* make(const char* file, int line, O* o, R (O::*p)(A1, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpConstSafeMemberCallback1B4(file, line, o, p, ba1, ba2, ba3, ba4); } }; template struct XorpConstMemberCallbackFactory1B4 { static XorpConstMemberCallback1B4* make(const char* file, int line, O* o, R (O::*p)(A1, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpConstMemberCallback1B4(file, line, o, p, ba1, ba2, ba3, ba4); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 1 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback1::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpConstMemberCallbackFactory1B4::True>::make(file, line, o, p, ba1, ba2, ba3, ba4); } /** * Factory function that creates a callback object targetted at a * const member function with 1 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback1::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpConstMemberCallbackFactory1B4::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4); } /** * @short Callback object for functions with 1 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpFunctionCallback1B5 : public XorpCallback1 { typedef R (*F)(A1, BA1, BA2, BA3, BA4, BA5); XorpFunctionCallback1B5(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback1(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1) { record_dispatch_enter(); R r = (*_f)(a1, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; }; /** * @short Callback object for void functions with 1 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpFunctionCallback1B5 : public XorpCallback1 { typedef void (*F)(A1, BA1, BA2, BA3, BA4, BA5); XorpFunctionCallback1B5(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback1(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1) { record_dispatch_enter(); (*_f)(a1, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; }; /** * Factory function that creates a callback object targetted at a * function with 1 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback1::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return typename XorpCallback1::RefPtr(new XorpFunctionCallback1B5(file, line, f, ba1, ba2, ba3, ba4, ba5)); } /** * @short Callback object for member methods with 1 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpMemberCallback1B5 : public XorpCallback1 { typedef R (O::*M)(A1, BA1, BA2, BA3, BA4, BA5) ; XorpMemberCallback1B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback1(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for void member methods with 1 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpMemberCallback1B5 : public XorpCallback1 { typedef void (O::*M)(A1, BA1, BA2, BA3, BA4, BA5) ; XorpMemberCallback1B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback1(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1) { record_dispatch_enter(); ((*_o).*_m)(a1, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for safe member methods with 1 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpSafeMemberCallback1B5 : public XorpMemberCallback1B5, public SafeCallbackBase { typedef typename XorpMemberCallback1B5::M M; XorpSafeMemberCallback1B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpMemberCallback1B5(file, line, o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpSafeMemberCallback1B5() {} R dispatch(A1 a1) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback1B5::dispatch(a1); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 1 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpSafeMemberCallback1B5 : public XorpMemberCallback1B5, public SafeCallbackBase { typedef typename XorpMemberCallback1B5::M M; XorpSafeMemberCallback1B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpMemberCallback1B5(file, line, o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpSafeMemberCallback1B5() {} void dispatch(A1 a1) { if (valid()) { record_dispatch_enter(); XorpMemberCallback1B5::dispatch(a1); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory1B5 { static XorpMemberCallback1B5* make(const char* file, int line, O* o, R (O::*p)(A1, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpSafeMemberCallback1B5(file, line, o, p, ba1, ba2, ba3, ba4, ba5); } }; template struct XorpMemberCallbackFactory1B5 { static XorpMemberCallback1B5* make(const char* file, int line, O* o, R (O::*p)(A1, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpMemberCallback1B5(file, line, o, p, ba1, ba2, ba3, ba4, ba5); }; }; /** * Factory function that creates a callback object targetted at a * member function with 1 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback1::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpMemberCallbackFactory1B5::True>::make(file, line, o, p, ba1, ba2, ba3, ba4, ba5); } /** * Factory function that creates a callback object targetted at a * member function with 1 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback1::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpMemberCallbackFactory1B5::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4, ba5); } /** * @short Callback object for const member methods with 1 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstMemberCallback1B5 : public XorpCallback1 { typedef R (O::*M)(A1, BA1, BA2, BA3, BA4, BA5) const; XorpConstMemberCallback1B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback1(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for void const member methods with 1 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstMemberCallback1B5 : public XorpCallback1 { typedef void (O::*M)(A1, BA1, BA2, BA3, BA4, BA5) const; XorpConstMemberCallback1B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback1(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1) { record_dispatch_enter(); ((*_o).*_m)(a1, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for const safe member methods with 1 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback1B5 : public XorpConstMemberCallback1B5, public SafeCallbackBase { typedef typename XorpConstMemberCallback1B5::M M; XorpConstSafeMemberCallback1B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpConstMemberCallback1B5(file, line, o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback1B5() {} R dispatch(A1 a1) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback1B5::dispatch(a1); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 1 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback1B5 : public XorpConstMemberCallback1B5, public SafeCallbackBase { typedef typename XorpConstMemberCallback1B5::M M; XorpConstSafeMemberCallback1B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpConstMemberCallback1B5(file, line, o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback1B5() {} void dispatch(A1 a1) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback1B5::dispatch(a1); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory1B5 { static XorpConstMemberCallback1B5* make(const char* file, int line, O* o, R (O::*p)(A1, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpConstSafeMemberCallback1B5(file, line, o, p, ba1, ba2, ba3, ba4, ba5); } }; template struct XorpConstMemberCallbackFactory1B5 { static XorpConstMemberCallback1B5* make(const char* file, int line, O* o, R (O::*p)(A1, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpConstMemberCallback1B5(file, line, o, p, ba1, ba2, ba3, ba4, ba5); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 1 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback1::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpConstMemberCallbackFactory1B5::True>::make(file, line, o, p, ba1, ba2, ba3, ba4, ba5); } /** * Factory function that creates a callback object targetted at a * const member function with 1 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback1::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpConstMemberCallbackFactory1B5::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4, ba5); } /** * @short Callback object for functions with 1 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpFunctionCallback1B6 : public XorpCallback1 { typedef R (*F)(A1, BA1, BA2, BA3, BA4, BA5, BA6); XorpFunctionCallback1B6(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback1(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1) { record_dispatch_enter(); R r = (*_f)(a1, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; BA6 _ba6; }; /** * @short Callback object for void functions with 1 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpFunctionCallback1B6 : public XorpCallback1 { typedef void (*F)(A1, BA1, BA2, BA3, BA4, BA5, BA6); XorpFunctionCallback1B6(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback1(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1) { record_dispatch_enter(); (*_f)(a1, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; BA6 _ba6; }; /** * Factory function that creates a callback object targetted at a * function with 1 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback1::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return typename XorpCallback1::RefPtr(new XorpFunctionCallback1B6(file, line, f, ba1, ba2, ba3, ba4, ba5, ba6)); } /** * @short Callback object for member methods with 1 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpMemberCallback1B6 : public XorpCallback1 { typedef R (O::*M)(A1, BA1, BA2, BA3, BA4, BA5, BA6) ; XorpMemberCallback1B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback1(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for void member methods with 1 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpMemberCallback1B6 : public XorpCallback1 { typedef void (O::*M)(A1, BA1, BA2, BA3, BA4, BA5, BA6) ; XorpMemberCallback1B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback1(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1) { record_dispatch_enter(); ((*_o).*_m)(a1, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for safe member methods with 1 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpSafeMemberCallback1B6 : public XorpMemberCallback1B6, public SafeCallbackBase { typedef typename XorpMemberCallback1B6::M M; XorpSafeMemberCallback1B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpMemberCallback1B6(file, line, o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpSafeMemberCallback1B6() {} R dispatch(A1 a1) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback1B6::dispatch(a1); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 1 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpSafeMemberCallback1B6 : public XorpMemberCallback1B6, public SafeCallbackBase { typedef typename XorpMemberCallback1B6::M M; XorpSafeMemberCallback1B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpMemberCallback1B6(file, line, o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpSafeMemberCallback1B6() {} void dispatch(A1 a1) { if (valid()) { record_dispatch_enter(); XorpMemberCallback1B6::dispatch(a1); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory1B6 { static XorpMemberCallback1B6* make(const char* file, int line, O* o, R (O::*p)(A1, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpSafeMemberCallback1B6(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); } }; template struct XorpMemberCallbackFactory1B6 { static XorpMemberCallback1B6* make(const char* file, int line, O* o, R (O::*p)(A1, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpMemberCallback1B6(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); }; }; /** * Factory function that creates a callback object targetted at a * member function with 1 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback1::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpMemberCallbackFactory1B6::True>::make(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * Factory function that creates a callback object targetted at a * member function with 1 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback1::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpMemberCallbackFactory1B6::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * @short Callback object for const member methods with 1 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstMemberCallback1B6 : public XorpCallback1 { typedef R (O::*M)(A1, BA1, BA2, BA3, BA4, BA5, BA6) const; XorpConstMemberCallback1B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback1(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for void const member methods with 1 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstMemberCallback1B6 : public XorpCallback1 { typedef void (O::*M)(A1, BA1, BA2, BA3, BA4, BA5, BA6) const; XorpConstMemberCallback1B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback1(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1) { record_dispatch_enter(); ((*_o).*_m)(a1, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for const safe member methods with 1 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback1B6 : public XorpConstMemberCallback1B6, public SafeCallbackBase { typedef typename XorpConstMemberCallback1B6::M M; XorpConstSafeMemberCallback1B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpConstMemberCallback1B6(file, line, o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback1B6() {} R dispatch(A1 a1) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback1B6::dispatch(a1); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 1 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback1B6 : public XorpConstMemberCallback1B6, public SafeCallbackBase { typedef typename XorpConstMemberCallback1B6::M M; XorpConstSafeMemberCallback1B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpConstMemberCallback1B6(file, line, o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback1B6() {} void dispatch(A1 a1) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback1B6::dispatch(a1); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory1B6 { static XorpConstMemberCallback1B6* make(const char* file, int line, O* o, R (O::*p)(A1, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpConstSafeMemberCallback1B6(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); } }; template struct XorpConstMemberCallbackFactory1B6 { static XorpConstMemberCallback1B6* make(const char* file, int line, O* o, R (O::*p)(A1, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpConstMemberCallback1B6(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 1 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback1::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpConstMemberCallbackFactory1B6::True>::make(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * Factory function that creates a callback object targetted at a * const member function with 1 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback1::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpConstMemberCallbackFactory1B6::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /////////////////////////////////////////////////////////////////////////////// // // Code relating to callbacks with 2 late args // /** * @short Base class for callbacks with 2 dispatch time args. */ template struct XorpCallback2 { typedef ref_ptr RefPtr; XorpCallback2(const char* file, int line) : _file(file), _line(line) {} virtual ~XorpCallback2() {} virtual R dispatch(A1, A2) = 0; const char* file() const { return _file; } int line() const { return _line; } private: const char* _file; int _line; }; /** * @short Callback object for functions with 2 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpFunctionCallback2B0 : public XorpCallback2 { typedef R (*F)(A1, A2); XorpFunctionCallback2B0(const char* file, int line, F f) : XorpCallback2(file, line), _f(f) {} R dispatch(A1 a1, A2 a2) { record_dispatch_enter(); R r = (*_f)(a1, a2); record_dispatch_leave(); return r; } protected: F _f; }; /** * @short Callback object for void functions with 2 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpFunctionCallback2B0 : public XorpCallback2 { typedef void (*F)(A1, A2); XorpFunctionCallback2B0(const char* file, int line, F f) : XorpCallback2(file, line), _f(f) {} void dispatch(A1 a1, A2 a2) { record_dispatch_enter(); (*_f)(a1, a2); record_dispatch_leave(); } protected: F _f; }; /** * Factory function that creates a callback object targetted at a * function with 2 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback2::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2)) { return typename XorpCallback2::RefPtr(new XorpFunctionCallback2B0(file, line, f)); } /** * @short Callback object for member methods with 2 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpMemberCallback2B0 : public XorpCallback2 { typedef R (O::*M)(A1, A2) ; XorpMemberCallback2B0(const char* file, int line, O* o, M m) : XorpCallback2(file, line), _o(o), _m(m) {} R dispatch(A1 a1, A2 a2) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for void member methods with 2 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpMemberCallback2B0 : public XorpCallback2 { typedef void (O::*M)(A1, A2) ; XorpMemberCallback2B0(const char* file, int line, O* o, M m) : XorpCallback2(file, line), _o(o), _m(m) {} void dispatch(A1 a1, A2 a2) { record_dispatch_enter(); ((*_o).*_m)(a1, a2); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for safe member methods with 2 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpSafeMemberCallback2B0 : public XorpMemberCallback2B0, public SafeCallbackBase { typedef typename XorpMemberCallback2B0::M M; XorpSafeMemberCallback2B0(const char* file, int line, O* o, M m) : XorpMemberCallback2B0(file, line, o, m), SafeCallbackBase(o) {} ~XorpSafeMemberCallback2B0() {} R dispatch(A1 a1, A2 a2) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback2B0::dispatch(a1, a2); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 2 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpSafeMemberCallback2B0 : public XorpMemberCallback2B0, public SafeCallbackBase { typedef typename XorpMemberCallback2B0::M M; XorpSafeMemberCallback2B0(const char* file, int line, O* o, M m) : XorpMemberCallback2B0(file, line, o, m), SafeCallbackBase(o) {} ~XorpSafeMemberCallback2B0() {} void dispatch(A1 a1, A2 a2) { if (valid()) { record_dispatch_enter(); XorpMemberCallback2B0::dispatch(a1, a2); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory2B0 { static XorpMemberCallback2B0* make(const char* file, int line, O* o, R (O::*p)(A1, A2)) { return new XorpSafeMemberCallback2B0(file, line, o, p); } }; template struct XorpMemberCallbackFactory2B0 { static XorpMemberCallback2B0* make(const char* file, int line, O* o, R (O::*p)(A1, A2)) { return new XorpMemberCallback2B0(file, line, o, p); }; }; /** * Factory function that creates a callback object targetted at a * member function with 2 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback2::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2)) { return XorpMemberCallbackFactory2B0::True>::make(file, line, o, p); } /** * Factory function that creates a callback object targetted at a * member function with 2 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback2::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2)) { return XorpMemberCallbackFactory2B0::True>::make(file, line, &o, p); } /** * @short Callback object for const member methods with 2 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstMemberCallback2B0 : public XorpCallback2 { typedef R (O::*M)(A1, A2) const; XorpConstMemberCallback2B0(const char* file, int line, O* o, M m) : XorpCallback2(file, line), _o(o), _m(m) {} R dispatch(A1 a1, A2 a2) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for void const member methods with 2 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstMemberCallback2B0 : public XorpCallback2 { typedef void (O::*M)(A1, A2) const; XorpConstMemberCallback2B0(const char* file, int line, O* o, M m) : XorpCallback2(file, line), _o(o), _m(m) {} void dispatch(A1 a1, A2 a2) { record_dispatch_enter(); ((*_o).*_m)(a1, a2); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for const safe member methods with 2 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback2B0 : public XorpConstMemberCallback2B0, public SafeCallbackBase { typedef typename XorpConstMemberCallback2B0::M M; XorpConstSafeMemberCallback2B0(const char* file, int line, O* o, M m) : XorpConstMemberCallback2B0(file, line, o, m), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback2B0() {} R dispatch(A1 a1, A2 a2) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback2B0::dispatch(a1, a2); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 2 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback2B0 : public XorpConstMemberCallback2B0, public SafeCallbackBase { typedef typename XorpConstMemberCallback2B0::M M; XorpConstSafeMemberCallback2B0(const char* file, int line, O* o, M m) : XorpConstMemberCallback2B0(file, line, o, m), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback2B0() {} void dispatch(A1 a1, A2 a2) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback2B0::dispatch(a1, a2); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory2B0 { static XorpConstMemberCallback2B0* make(const char* file, int line, O* o, R (O::*p)(A1, A2) const) { return new XorpConstSafeMemberCallback2B0(file, line, o, p); } }; template struct XorpConstMemberCallbackFactory2B0 { static XorpConstMemberCallback2B0* make(const char* file, int line, O* o, R (O::*p)(A1, A2) const) { return new XorpConstMemberCallback2B0(file, line, o, p); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 2 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback2::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2) const) { return XorpConstMemberCallbackFactory2B0::True>::make(file, line, o, p); } /** * Factory function that creates a callback object targetted at a * const member function with 2 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback2::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2) const) { return XorpConstMemberCallbackFactory2B0::True>::make(file, line, &o, p); } /** * @short Callback object for functions with 2 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpFunctionCallback2B1 : public XorpCallback2 { typedef R (*F)(A1, A2, BA1); XorpFunctionCallback2B1(const char* file, int line, F f, BA1 ba1) : XorpCallback2(file, line), _f(f), _ba1(ba1) {} R dispatch(A1 a1, A2 a2) { record_dispatch_enter(); R r = (*_f)(a1, a2, _ba1); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; }; /** * @short Callback object for void functions with 2 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpFunctionCallback2B1 : public XorpCallback2 { typedef void (*F)(A1, A2, BA1); XorpFunctionCallback2B1(const char* file, int line, F f, BA1 ba1) : XorpCallback2(file, line), _f(f), _ba1(ba1) {} void dispatch(A1 a1, A2 a2) { record_dispatch_enter(); (*_f)(a1, a2, _ba1); record_dispatch_leave(); } protected: F _f; BA1 _ba1; }; /** * Factory function that creates a callback object targetted at a * function with 2 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback2::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, BA1), BA1 ba1) { return typename XorpCallback2::RefPtr(new XorpFunctionCallback2B1(file, line, f, ba1)); } /** * @short Callback object for member methods with 2 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpMemberCallback2B1 : public XorpCallback2 { typedef R (O::*M)(A1, A2, BA1) ; XorpMemberCallback2B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpCallback2(file, line), _o(o), _m(m), _ba1(ba1) {} R dispatch(A1 a1, A2 a2) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, _ba1); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for void member methods with 2 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpMemberCallback2B1 : public XorpCallback2 { typedef void (O::*M)(A1, A2, BA1) ; XorpMemberCallback2B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpCallback2(file, line), _o(o), _m(m), _ba1(ba1) {} void dispatch(A1 a1, A2 a2) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, _ba1); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for safe member methods with 2 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpSafeMemberCallback2B1 : public XorpMemberCallback2B1, public SafeCallbackBase { typedef typename XorpMemberCallback2B1::M M; XorpSafeMemberCallback2B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpMemberCallback2B1(file, line, o, m, ba1), SafeCallbackBase(o) {} ~XorpSafeMemberCallback2B1() {} R dispatch(A1 a1, A2 a2) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback2B1::dispatch(a1, a2); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 2 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpSafeMemberCallback2B1 : public XorpMemberCallback2B1, public SafeCallbackBase { typedef typename XorpMemberCallback2B1::M M; XorpSafeMemberCallback2B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpMemberCallback2B1(file, line, o, m, ba1), SafeCallbackBase(o) {} ~XorpSafeMemberCallback2B1() {} void dispatch(A1 a1, A2 a2) { if (valid()) { record_dispatch_enter(); XorpMemberCallback2B1::dispatch(a1, a2); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory2B1 { static XorpMemberCallback2B1* make(const char* file, int line, O* o, R (O::*p)(A1, A2, BA1), BA1 ba1) { return new XorpSafeMemberCallback2B1(file, line, o, p, ba1); } }; template struct XorpMemberCallbackFactory2B1 { static XorpMemberCallback2B1* make(const char* file, int line, O* o, R (O::*p)(A1, A2, BA1), BA1 ba1) { return new XorpMemberCallback2B1(file, line, o, p, ba1); }; }; /** * Factory function that creates a callback object targetted at a * member function with 2 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback2::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, BA1), BA1 ba1) { return XorpMemberCallbackFactory2B1::True>::make(file, line, o, p, ba1); } /** * Factory function that creates a callback object targetted at a * member function with 2 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback2::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, BA1), BA1 ba1) { return XorpMemberCallbackFactory2B1::True>::make(file, line, &o, p, ba1); } /** * @short Callback object for const member methods with 2 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstMemberCallback2B1 : public XorpCallback2 { typedef R (O::*M)(A1, A2, BA1) const; XorpConstMemberCallback2B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpCallback2(file, line), _o(o), _m(m), _ba1(ba1) {} R dispatch(A1 a1, A2 a2) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, _ba1); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for void const member methods with 2 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstMemberCallback2B1 : public XorpCallback2 { typedef void (O::*M)(A1, A2, BA1) const; XorpConstMemberCallback2B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpCallback2(file, line), _o(o), _m(m), _ba1(ba1) {} void dispatch(A1 a1, A2 a2) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, _ba1); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for const safe member methods with 2 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback2B1 : public XorpConstMemberCallback2B1, public SafeCallbackBase { typedef typename XorpConstMemberCallback2B1::M M; XorpConstSafeMemberCallback2B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpConstMemberCallback2B1(file, line, o, m, ba1), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback2B1() {} R dispatch(A1 a1, A2 a2) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback2B1::dispatch(a1, a2); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 2 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback2B1 : public XorpConstMemberCallback2B1, public SafeCallbackBase { typedef typename XorpConstMemberCallback2B1::M M; XorpConstSafeMemberCallback2B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpConstMemberCallback2B1(file, line, o, m, ba1), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback2B1() {} void dispatch(A1 a1, A2 a2) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback2B1::dispatch(a1, a2); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory2B1 { static XorpConstMemberCallback2B1* make(const char* file, int line, O* o, R (O::*p)(A1, A2, BA1) const, BA1 ba1) { return new XorpConstSafeMemberCallback2B1(file, line, o, p, ba1); } }; template struct XorpConstMemberCallbackFactory2B1 { static XorpConstMemberCallback2B1* make(const char* file, int line, O* o, R (O::*p)(A1, A2, BA1) const, BA1 ba1) { return new XorpConstMemberCallback2B1(file, line, o, p, ba1); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 2 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback2::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, BA1) const, BA1 ba1) { return XorpConstMemberCallbackFactory2B1::True>::make(file, line, o, p, ba1); } /** * Factory function that creates a callback object targetted at a * const member function with 2 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback2::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, BA1) const, BA1 ba1) { return XorpConstMemberCallbackFactory2B1::True>::make(file, line, &o, p, ba1); } /** * @short Callback object for functions with 2 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpFunctionCallback2B2 : public XorpCallback2 { typedef R (*F)(A1, A2, BA1, BA2); XorpFunctionCallback2B2(const char* file, int line, F f, BA1 ba1, BA2 ba2) : XorpCallback2(file, line), _f(f), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2) { record_dispatch_enter(); R r = (*_f)(a1, a2, _ba1, _ba2); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; }; /** * @short Callback object for void functions with 2 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpFunctionCallback2B2 : public XorpCallback2 { typedef void (*F)(A1, A2, BA1, BA2); XorpFunctionCallback2B2(const char* file, int line, F f, BA1 ba1, BA2 ba2) : XorpCallback2(file, line), _f(f), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2) { record_dispatch_enter(); (*_f)(a1, a2, _ba1, _ba2); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; }; /** * Factory function that creates a callback object targetted at a * function with 2 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback2::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, BA1, BA2), BA1 ba1, BA2 ba2) { return typename XorpCallback2::RefPtr(new XorpFunctionCallback2B2(file, line, f, ba1, ba2)); } /** * @short Callback object for member methods with 2 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpMemberCallback2B2 : public XorpCallback2 { typedef R (O::*M)(A1, A2, BA1, BA2) ; XorpMemberCallback2B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback2(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, _ba1, _ba2); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for void member methods with 2 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpMemberCallback2B2 : public XorpCallback2 { typedef void (O::*M)(A1, A2, BA1, BA2) ; XorpMemberCallback2B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback2(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, _ba1, _ba2); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for safe member methods with 2 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpSafeMemberCallback2B2 : public XorpMemberCallback2B2, public SafeCallbackBase { typedef typename XorpMemberCallback2B2::M M; XorpSafeMemberCallback2B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpMemberCallback2B2(file, line, o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpSafeMemberCallback2B2() {} R dispatch(A1 a1, A2 a2) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback2B2::dispatch(a1, a2); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 2 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpSafeMemberCallback2B2 : public XorpMemberCallback2B2, public SafeCallbackBase { typedef typename XorpMemberCallback2B2::M M; XorpSafeMemberCallback2B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpMemberCallback2B2(file, line, o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpSafeMemberCallback2B2() {} void dispatch(A1 a1, A2 a2) { if (valid()) { record_dispatch_enter(); XorpMemberCallback2B2::dispatch(a1, a2); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory2B2 { static XorpMemberCallback2B2* make(const char* file, int line, O* o, R (O::*p)(A1, A2, BA1, BA2), BA1 ba1, BA2 ba2) { return new XorpSafeMemberCallback2B2(file, line, o, p, ba1, ba2); } }; template struct XorpMemberCallbackFactory2B2 { static XorpMemberCallback2B2* make(const char* file, int line, O* o, R (O::*p)(A1, A2, BA1, BA2), BA1 ba1, BA2 ba2) { return new XorpMemberCallback2B2(file, line, o, p, ba1, ba2); }; }; /** * Factory function that creates a callback object targetted at a * member function with 2 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback2::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, BA1, BA2), BA1 ba1, BA2 ba2) { return XorpMemberCallbackFactory2B2::True>::make(file, line, o, p, ba1, ba2); } /** * Factory function that creates a callback object targetted at a * member function with 2 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback2::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, BA1, BA2), BA1 ba1, BA2 ba2) { return XorpMemberCallbackFactory2B2::True>::make(file, line, &o, p, ba1, ba2); } /** * @short Callback object for const member methods with 2 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstMemberCallback2B2 : public XorpCallback2 { typedef R (O::*M)(A1, A2, BA1, BA2) const; XorpConstMemberCallback2B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback2(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, _ba1, _ba2); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for void const member methods with 2 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstMemberCallback2B2 : public XorpCallback2 { typedef void (O::*M)(A1, A2, BA1, BA2) const; XorpConstMemberCallback2B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback2(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, _ba1, _ba2); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for const safe member methods with 2 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback2B2 : public XorpConstMemberCallback2B2, public SafeCallbackBase { typedef typename XorpConstMemberCallback2B2::M M; XorpConstSafeMemberCallback2B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpConstMemberCallback2B2(file, line, o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback2B2() {} R dispatch(A1 a1, A2 a2) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback2B2::dispatch(a1, a2); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 2 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback2B2 : public XorpConstMemberCallback2B2, public SafeCallbackBase { typedef typename XorpConstMemberCallback2B2::M M; XorpConstSafeMemberCallback2B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpConstMemberCallback2B2(file, line, o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback2B2() {} void dispatch(A1 a1, A2 a2) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback2B2::dispatch(a1, a2); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory2B2 { static XorpConstMemberCallback2B2* make(const char* file, int line, O* o, R (O::*p)(A1, A2, BA1, BA2) const, BA1 ba1, BA2 ba2) { return new XorpConstSafeMemberCallback2B2(file, line, o, p, ba1, ba2); } }; template struct XorpConstMemberCallbackFactory2B2 { static XorpConstMemberCallback2B2* make(const char* file, int line, O* o, R (O::*p)(A1, A2, BA1, BA2) const, BA1 ba1, BA2 ba2) { return new XorpConstMemberCallback2B2(file, line, o, p, ba1, ba2); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 2 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback2::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, BA1, BA2) const, BA1 ba1, BA2 ba2) { return XorpConstMemberCallbackFactory2B2::True>::make(file, line, o, p, ba1, ba2); } /** * Factory function that creates a callback object targetted at a * const member function with 2 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback2::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, BA1, BA2) const, BA1 ba1, BA2 ba2) { return XorpConstMemberCallbackFactory2B2::True>::make(file, line, &o, p, ba1, ba2); } /** * @short Callback object for functions with 2 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpFunctionCallback2B3 : public XorpCallback2 { typedef R (*F)(A1, A2, BA1, BA2, BA3); XorpFunctionCallback2B3(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback2(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2) { record_dispatch_enter(); R r = (*_f)(a1, a2, _ba1, _ba2, _ba3); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; }; /** * @short Callback object for void functions with 2 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpFunctionCallback2B3 : public XorpCallback2 { typedef void (*F)(A1, A2, BA1, BA2, BA3); XorpFunctionCallback2B3(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback2(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2) { record_dispatch_enter(); (*_f)(a1, a2, _ba1, _ba2, _ba3); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; }; /** * Factory function that creates a callback object targetted at a * function with 2 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback2::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return typename XorpCallback2::RefPtr(new XorpFunctionCallback2B3(file, line, f, ba1, ba2, ba3)); } /** * @short Callback object for member methods with 2 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpMemberCallback2B3 : public XorpCallback2 { typedef R (O::*M)(A1, A2, BA1, BA2, BA3) ; XorpMemberCallback2B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback2(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, _ba1, _ba2, _ba3); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for void member methods with 2 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpMemberCallback2B3 : public XorpCallback2 { typedef void (O::*M)(A1, A2, BA1, BA2, BA3) ; XorpMemberCallback2B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback2(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, _ba1, _ba2, _ba3); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for safe member methods with 2 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpSafeMemberCallback2B3 : public XorpMemberCallback2B3, public SafeCallbackBase { typedef typename XorpMemberCallback2B3::M M; XorpSafeMemberCallback2B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpMemberCallback2B3(file, line, o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpSafeMemberCallback2B3() {} R dispatch(A1 a1, A2 a2) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback2B3::dispatch(a1, a2); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 2 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpSafeMemberCallback2B3 : public XorpMemberCallback2B3, public SafeCallbackBase { typedef typename XorpMemberCallback2B3::M M; XorpSafeMemberCallback2B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpMemberCallback2B3(file, line, o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpSafeMemberCallback2B3() {} void dispatch(A1 a1, A2 a2) { if (valid()) { record_dispatch_enter(); XorpMemberCallback2B3::dispatch(a1, a2); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory2B3 { static XorpMemberCallback2B3* make(const char* file, int line, O* o, R (O::*p)(A1, A2, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpSafeMemberCallback2B3(file, line, o, p, ba1, ba2, ba3); } }; template struct XorpMemberCallbackFactory2B3 { static XorpMemberCallback2B3* make(const char* file, int line, O* o, R (O::*p)(A1, A2, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpMemberCallback2B3(file, line, o, p, ba1, ba2, ba3); }; }; /** * Factory function that creates a callback object targetted at a * member function with 2 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback2::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return XorpMemberCallbackFactory2B3::True>::make(file, line, o, p, ba1, ba2, ba3); } /** * Factory function that creates a callback object targetted at a * member function with 2 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback2::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return XorpMemberCallbackFactory2B3::True>::make(file, line, &o, p, ba1, ba2, ba3); } /** * @short Callback object for const member methods with 2 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstMemberCallback2B3 : public XorpCallback2 { typedef R (O::*M)(A1, A2, BA1, BA2, BA3) const; XorpConstMemberCallback2B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback2(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, _ba1, _ba2, _ba3); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for void const member methods with 2 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstMemberCallback2B3 : public XorpCallback2 { typedef void (O::*M)(A1, A2, BA1, BA2, BA3) const; XorpConstMemberCallback2B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback2(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, _ba1, _ba2, _ba3); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for const safe member methods with 2 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback2B3 : public XorpConstMemberCallback2B3, public SafeCallbackBase { typedef typename XorpConstMemberCallback2B3::M M; XorpConstSafeMemberCallback2B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpConstMemberCallback2B3(file, line, o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback2B3() {} R dispatch(A1 a1, A2 a2) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback2B3::dispatch(a1, a2); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 2 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback2B3 : public XorpConstMemberCallback2B3, public SafeCallbackBase { typedef typename XorpConstMemberCallback2B3::M M; XorpConstSafeMemberCallback2B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpConstMemberCallback2B3(file, line, o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback2B3() {} void dispatch(A1 a1, A2 a2) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback2B3::dispatch(a1, a2); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory2B3 { static XorpConstMemberCallback2B3* make(const char* file, int line, O* o, R (O::*p)(A1, A2, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpConstSafeMemberCallback2B3(file, line, o, p, ba1, ba2, ba3); } }; template struct XorpConstMemberCallbackFactory2B3 { static XorpConstMemberCallback2B3* make(const char* file, int line, O* o, R (O::*p)(A1, A2, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpConstMemberCallback2B3(file, line, o, p, ba1, ba2, ba3); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 2 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback2::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return XorpConstMemberCallbackFactory2B3::True>::make(file, line, o, p, ba1, ba2, ba3); } /** * Factory function that creates a callback object targetted at a * const member function with 2 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback2::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return XorpConstMemberCallbackFactory2B3::True>::make(file, line, &o, p, ba1, ba2, ba3); } /** * @short Callback object for functions with 2 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpFunctionCallback2B4 : public XorpCallback2 { typedef R (*F)(A1, A2, BA1, BA2, BA3, BA4); XorpFunctionCallback2B4(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback2(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2) { record_dispatch_enter(); R r = (*_f)(a1, a2, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; }; /** * @short Callback object for void functions with 2 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpFunctionCallback2B4 : public XorpCallback2 { typedef void (*F)(A1, A2, BA1, BA2, BA3, BA4); XorpFunctionCallback2B4(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback2(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2) { record_dispatch_enter(); (*_f)(a1, a2, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; }; /** * Factory function that creates a callback object targetted at a * function with 2 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback2::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return typename XorpCallback2::RefPtr(new XorpFunctionCallback2B4(file, line, f, ba1, ba2, ba3, ba4)); } /** * @short Callback object for member methods with 2 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpMemberCallback2B4 : public XorpCallback2 { typedef R (O::*M)(A1, A2, BA1, BA2, BA3, BA4) ; XorpMemberCallback2B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback2(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for void member methods with 2 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpMemberCallback2B4 : public XorpCallback2 { typedef void (O::*M)(A1, A2, BA1, BA2, BA3, BA4) ; XorpMemberCallback2B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback2(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for safe member methods with 2 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpSafeMemberCallback2B4 : public XorpMemberCallback2B4, public SafeCallbackBase { typedef typename XorpMemberCallback2B4::M M; XorpSafeMemberCallback2B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpMemberCallback2B4(file, line, o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpSafeMemberCallback2B4() {} R dispatch(A1 a1, A2 a2) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback2B4::dispatch(a1, a2); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 2 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpSafeMemberCallback2B4 : public XorpMemberCallback2B4, public SafeCallbackBase { typedef typename XorpMemberCallback2B4::M M; XorpSafeMemberCallback2B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpMemberCallback2B4(file, line, o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpSafeMemberCallback2B4() {} void dispatch(A1 a1, A2 a2) { if (valid()) { record_dispatch_enter(); XorpMemberCallback2B4::dispatch(a1, a2); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory2B4 { static XorpMemberCallback2B4* make(const char* file, int line, O* o, R (O::*p)(A1, A2, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpSafeMemberCallback2B4(file, line, o, p, ba1, ba2, ba3, ba4); } }; template struct XorpMemberCallbackFactory2B4 { static XorpMemberCallback2B4* make(const char* file, int line, O* o, R (O::*p)(A1, A2, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpMemberCallback2B4(file, line, o, p, ba1, ba2, ba3, ba4); }; }; /** * Factory function that creates a callback object targetted at a * member function with 2 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback2::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpMemberCallbackFactory2B4::True>::make(file, line, o, p, ba1, ba2, ba3, ba4); } /** * Factory function that creates a callback object targetted at a * member function with 2 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback2::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpMemberCallbackFactory2B4::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4); } /** * @short Callback object for const member methods with 2 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstMemberCallback2B4 : public XorpCallback2 { typedef R (O::*M)(A1, A2, BA1, BA2, BA3, BA4) const; XorpConstMemberCallback2B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback2(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for void const member methods with 2 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstMemberCallback2B4 : public XorpCallback2 { typedef void (O::*M)(A1, A2, BA1, BA2, BA3, BA4) const; XorpConstMemberCallback2B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback2(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for const safe member methods with 2 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback2B4 : public XorpConstMemberCallback2B4, public SafeCallbackBase { typedef typename XorpConstMemberCallback2B4::M M; XorpConstSafeMemberCallback2B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpConstMemberCallback2B4(file, line, o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback2B4() {} R dispatch(A1 a1, A2 a2) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback2B4::dispatch(a1, a2); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 2 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback2B4 : public XorpConstMemberCallback2B4, public SafeCallbackBase { typedef typename XorpConstMemberCallback2B4::M M; XorpConstSafeMemberCallback2B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpConstMemberCallback2B4(file, line, o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback2B4() {} void dispatch(A1 a1, A2 a2) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback2B4::dispatch(a1, a2); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory2B4 { static XorpConstMemberCallback2B4* make(const char* file, int line, O* o, R (O::*p)(A1, A2, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpConstSafeMemberCallback2B4(file, line, o, p, ba1, ba2, ba3, ba4); } }; template struct XorpConstMemberCallbackFactory2B4 { static XorpConstMemberCallback2B4* make(const char* file, int line, O* o, R (O::*p)(A1, A2, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpConstMemberCallback2B4(file, line, o, p, ba1, ba2, ba3, ba4); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 2 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback2::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpConstMemberCallbackFactory2B4::True>::make(file, line, o, p, ba1, ba2, ba3, ba4); } /** * Factory function that creates a callback object targetted at a * const member function with 2 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback2::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpConstMemberCallbackFactory2B4::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4); } /** * @short Callback object for functions with 2 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpFunctionCallback2B5 : public XorpCallback2 { typedef R (*F)(A1, A2, BA1, BA2, BA3, BA4, BA5); XorpFunctionCallback2B5(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback2(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2) { record_dispatch_enter(); R r = (*_f)(a1, a2, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; }; /** * @short Callback object for void functions with 2 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpFunctionCallback2B5 : public XorpCallback2 { typedef void (*F)(A1, A2, BA1, BA2, BA3, BA4, BA5); XorpFunctionCallback2B5(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback2(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2) { record_dispatch_enter(); (*_f)(a1, a2, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; }; /** * Factory function that creates a callback object targetted at a * function with 2 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback2::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return typename XorpCallback2::RefPtr(new XorpFunctionCallback2B5(file, line, f, ba1, ba2, ba3, ba4, ba5)); } /** * @short Callback object for member methods with 2 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpMemberCallback2B5 : public XorpCallback2 { typedef R (O::*M)(A1, A2, BA1, BA2, BA3, BA4, BA5) ; XorpMemberCallback2B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback2(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for void member methods with 2 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpMemberCallback2B5 : public XorpCallback2 { typedef void (O::*M)(A1, A2, BA1, BA2, BA3, BA4, BA5) ; XorpMemberCallback2B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback2(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for safe member methods with 2 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpSafeMemberCallback2B5 : public XorpMemberCallback2B5, public SafeCallbackBase { typedef typename XorpMemberCallback2B5::M M; XorpSafeMemberCallback2B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpMemberCallback2B5(file, line, o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpSafeMemberCallback2B5() {} R dispatch(A1 a1, A2 a2) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback2B5::dispatch(a1, a2); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 2 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpSafeMemberCallback2B5 : public XorpMemberCallback2B5, public SafeCallbackBase { typedef typename XorpMemberCallback2B5::M M; XorpSafeMemberCallback2B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpMemberCallback2B5(file, line, o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpSafeMemberCallback2B5() {} void dispatch(A1 a1, A2 a2) { if (valid()) { record_dispatch_enter(); XorpMemberCallback2B5::dispatch(a1, a2); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory2B5 { static XorpMemberCallback2B5* make(const char* file, int line, O* o, R (O::*p)(A1, A2, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpSafeMemberCallback2B5(file, line, o, p, ba1, ba2, ba3, ba4, ba5); } }; template struct XorpMemberCallbackFactory2B5 { static XorpMemberCallback2B5* make(const char* file, int line, O* o, R (O::*p)(A1, A2, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpMemberCallback2B5(file, line, o, p, ba1, ba2, ba3, ba4, ba5); }; }; /** * Factory function that creates a callback object targetted at a * member function with 2 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback2::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpMemberCallbackFactory2B5::True>::make(file, line, o, p, ba1, ba2, ba3, ba4, ba5); } /** * Factory function that creates a callback object targetted at a * member function with 2 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback2::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpMemberCallbackFactory2B5::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4, ba5); } /** * @short Callback object for const member methods with 2 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstMemberCallback2B5 : public XorpCallback2 { typedef R (O::*M)(A1, A2, BA1, BA2, BA3, BA4, BA5) const; XorpConstMemberCallback2B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback2(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for void const member methods with 2 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstMemberCallback2B5 : public XorpCallback2 { typedef void (O::*M)(A1, A2, BA1, BA2, BA3, BA4, BA5) const; XorpConstMemberCallback2B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback2(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for const safe member methods with 2 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback2B5 : public XorpConstMemberCallback2B5, public SafeCallbackBase { typedef typename XorpConstMemberCallback2B5::M M; XorpConstSafeMemberCallback2B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpConstMemberCallback2B5(file, line, o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback2B5() {} R dispatch(A1 a1, A2 a2) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback2B5::dispatch(a1, a2); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 2 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback2B5 : public XorpConstMemberCallback2B5, public SafeCallbackBase { typedef typename XorpConstMemberCallback2B5::M M; XorpConstSafeMemberCallback2B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpConstMemberCallback2B5(file, line, o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback2B5() {} void dispatch(A1 a1, A2 a2) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback2B5::dispatch(a1, a2); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory2B5 { static XorpConstMemberCallback2B5* make(const char* file, int line, O* o, R (O::*p)(A1, A2, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpConstSafeMemberCallback2B5(file, line, o, p, ba1, ba2, ba3, ba4, ba5); } }; template struct XorpConstMemberCallbackFactory2B5 { static XorpConstMemberCallback2B5* make(const char* file, int line, O* o, R (O::*p)(A1, A2, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpConstMemberCallback2B5(file, line, o, p, ba1, ba2, ba3, ba4, ba5); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 2 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback2::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpConstMemberCallbackFactory2B5::True>::make(file, line, o, p, ba1, ba2, ba3, ba4, ba5); } /** * Factory function that creates a callback object targetted at a * const member function with 2 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback2::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpConstMemberCallbackFactory2B5::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4, ba5); } /** * @short Callback object for functions with 2 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpFunctionCallback2B6 : public XorpCallback2 { typedef R (*F)(A1, A2, BA1, BA2, BA3, BA4, BA5, BA6); XorpFunctionCallback2B6(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback2(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2) { record_dispatch_enter(); R r = (*_f)(a1, a2, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; BA6 _ba6; }; /** * @short Callback object for void functions with 2 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpFunctionCallback2B6 : public XorpCallback2 { typedef void (*F)(A1, A2, BA1, BA2, BA3, BA4, BA5, BA6); XorpFunctionCallback2B6(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback2(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2) { record_dispatch_enter(); (*_f)(a1, a2, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; BA6 _ba6; }; /** * Factory function that creates a callback object targetted at a * function with 2 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback2::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return typename XorpCallback2::RefPtr(new XorpFunctionCallback2B6(file, line, f, ba1, ba2, ba3, ba4, ba5, ba6)); } /** * @short Callback object for member methods with 2 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpMemberCallback2B6 : public XorpCallback2 { typedef R (O::*M)(A1, A2, BA1, BA2, BA3, BA4, BA5, BA6) ; XorpMemberCallback2B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback2(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for void member methods with 2 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpMemberCallback2B6 : public XorpCallback2 { typedef void (O::*M)(A1, A2, BA1, BA2, BA3, BA4, BA5, BA6) ; XorpMemberCallback2B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback2(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for safe member methods with 2 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpSafeMemberCallback2B6 : public XorpMemberCallback2B6, public SafeCallbackBase { typedef typename XorpMemberCallback2B6::M M; XorpSafeMemberCallback2B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpMemberCallback2B6(file, line, o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpSafeMemberCallback2B6() {} R dispatch(A1 a1, A2 a2) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback2B6::dispatch(a1, a2); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 2 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpSafeMemberCallback2B6 : public XorpMemberCallback2B6, public SafeCallbackBase { typedef typename XorpMemberCallback2B6::M M; XorpSafeMemberCallback2B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpMemberCallback2B6(file, line, o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpSafeMemberCallback2B6() {} void dispatch(A1 a1, A2 a2) { if (valid()) { record_dispatch_enter(); XorpMemberCallback2B6::dispatch(a1, a2); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory2B6 { static XorpMemberCallback2B6* make(const char* file, int line, O* o, R (O::*p)(A1, A2, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpSafeMemberCallback2B6(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); } }; template struct XorpMemberCallbackFactory2B6 { static XorpMemberCallback2B6* make(const char* file, int line, O* o, R (O::*p)(A1, A2, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpMemberCallback2B6(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); }; }; /** * Factory function that creates a callback object targetted at a * member function with 2 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback2::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpMemberCallbackFactory2B6::True>::make(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * Factory function that creates a callback object targetted at a * member function with 2 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback2::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpMemberCallbackFactory2B6::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * @short Callback object for const member methods with 2 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstMemberCallback2B6 : public XorpCallback2 { typedef R (O::*M)(A1, A2, BA1, BA2, BA3, BA4, BA5, BA6) const; XorpConstMemberCallback2B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback2(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for void const member methods with 2 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstMemberCallback2B6 : public XorpCallback2 { typedef void (O::*M)(A1, A2, BA1, BA2, BA3, BA4, BA5, BA6) const; XorpConstMemberCallback2B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback2(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for const safe member methods with 2 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback2B6 : public XorpConstMemberCallback2B6, public SafeCallbackBase { typedef typename XorpConstMemberCallback2B6::M M; XorpConstSafeMemberCallback2B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpConstMemberCallback2B6(file, line, o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback2B6() {} R dispatch(A1 a1, A2 a2) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback2B6::dispatch(a1, a2); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 2 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback2B6 : public XorpConstMemberCallback2B6, public SafeCallbackBase { typedef typename XorpConstMemberCallback2B6::M M; XorpConstSafeMemberCallback2B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpConstMemberCallback2B6(file, line, o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback2B6() {} void dispatch(A1 a1, A2 a2) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback2B6::dispatch(a1, a2); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory2B6 { static XorpConstMemberCallback2B6* make(const char* file, int line, O* o, R (O::*p)(A1, A2, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpConstSafeMemberCallback2B6(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); } }; template struct XorpConstMemberCallbackFactory2B6 { static XorpConstMemberCallback2B6* make(const char* file, int line, O* o, R (O::*p)(A1, A2, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpConstMemberCallback2B6(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 2 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback2::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpConstMemberCallbackFactory2B6::True>::make(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * Factory function that creates a callback object targetted at a * const member function with 2 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback2::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpConstMemberCallbackFactory2B6::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /////////////////////////////////////////////////////////////////////////////// // // Code relating to callbacks with 3 late args // /** * @short Base class for callbacks with 3 dispatch time args. */ template struct XorpCallback3 { typedef ref_ptr RefPtr; XorpCallback3(const char* file, int line) : _file(file), _line(line) {} virtual ~XorpCallback3() {} virtual R dispatch(A1, A2, A3) = 0; const char* file() const { return _file; } int line() const { return _line; } private: const char* _file; int _line; }; /** * @short Callback object for functions with 3 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpFunctionCallback3B0 : public XorpCallback3 { typedef R (*F)(A1, A2, A3); XorpFunctionCallback3B0(const char* file, int line, F f) : XorpCallback3(file, line), _f(f) {} R dispatch(A1 a1, A2 a2, A3 a3) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3); record_dispatch_leave(); return r; } protected: F _f; }; /** * @short Callback object for void functions with 3 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpFunctionCallback3B0 : public XorpCallback3 { typedef void (*F)(A1, A2, A3); XorpFunctionCallback3B0(const char* file, int line, F f) : XorpCallback3(file, line), _f(f) {} void dispatch(A1 a1, A2 a2, A3 a3) { record_dispatch_enter(); (*_f)(a1, a2, a3); record_dispatch_leave(); } protected: F _f; }; /** * Factory function that creates a callback object targetted at a * function with 3 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback3::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3)) { return typename XorpCallback3::RefPtr(new XorpFunctionCallback3B0(file, line, f)); } /** * @short Callback object for member methods with 3 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpMemberCallback3B0 : public XorpCallback3 { typedef R (O::*M)(A1, A2, A3) ; XorpMemberCallback3B0(const char* file, int line, O* o, M m) : XorpCallback3(file, line), _o(o), _m(m) {} R dispatch(A1 a1, A2 a2, A3 a3) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for void member methods with 3 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpMemberCallback3B0 : public XorpCallback3 { typedef void (O::*M)(A1, A2, A3) ; XorpMemberCallback3B0(const char* file, int line, O* o, M m) : XorpCallback3(file, line), _o(o), _m(m) {} void dispatch(A1 a1, A2 a2, A3 a3) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for safe member methods with 3 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpSafeMemberCallback3B0 : public XorpMemberCallback3B0, public SafeCallbackBase { typedef typename XorpMemberCallback3B0::M M; XorpSafeMemberCallback3B0(const char* file, int line, O* o, M m) : XorpMemberCallback3B0(file, line, o, m), SafeCallbackBase(o) {} ~XorpSafeMemberCallback3B0() {} R dispatch(A1 a1, A2 a2, A3 a3) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback3B0::dispatch(a1, a2, a3); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 3 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpSafeMemberCallback3B0 : public XorpMemberCallback3B0, public SafeCallbackBase { typedef typename XorpMemberCallback3B0::M M; XorpSafeMemberCallback3B0(const char* file, int line, O* o, M m) : XorpMemberCallback3B0(file, line, o, m), SafeCallbackBase(o) {} ~XorpSafeMemberCallback3B0() {} void dispatch(A1 a1, A2 a2, A3 a3) { if (valid()) { record_dispatch_enter(); XorpMemberCallback3B0::dispatch(a1, a2, a3); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory3B0 { static XorpMemberCallback3B0* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3)) { return new XorpSafeMemberCallback3B0(file, line, o, p); } }; template struct XorpMemberCallbackFactory3B0 { static XorpMemberCallback3B0* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3)) { return new XorpMemberCallback3B0(file, line, o, p); }; }; /** * Factory function that creates a callback object targetted at a * member function with 3 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback3::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3)) { return XorpMemberCallbackFactory3B0::True>::make(file, line, o, p); } /** * Factory function that creates a callback object targetted at a * member function with 3 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback3::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3)) { return XorpMemberCallbackFactory3B0::True>::make(file, line, &o, p); } /** * @short Callback object for const member methods with 3 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstMemberCallback3B0 : public XorpCallback3 { typedef R (O::*M)(A1, A2, A3) const; XorpConstMemberCallback3B0(const char* file, int line, O* o, M m) : XorpCallback3(file, line), _o(o), _m(m) {} R dispatch(A1 a1, A2 a2, A3 a3) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for void const member methods with 3 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstMemberCallback3B0 : public XorpCallback3 { typedef void (O::*M)(A1, A2, A3) const; XorpConstMemberCallback3B0(const char* file, int line, O* o, M m) : XorpCallback3(file, line), _o(o), _m(m) {} void dispatch(A1 a1, A2 a2, A3 a3) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for const safe member methods with 3 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback3B0 : public XorpConstMemberCallback3B0, public SafeCallbackBase { typedef typename XorpConstMemberCallback3B0::M M; XorpConstSafeMemberCallback3B0(const char* file, int line, O* o, M m) : XorpConstMemberCallback3B0(file, line, o, m), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback3B0() {} R dispatch(A1 a1, A2 a2, A3 a3) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback3B0::dispatch(a1, a2, a3); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 3 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback3B0 : public XorpConstMemberCallback3B0, public SafeCallbackBase { typedef typename XorpConstMemberCallback3B0::M M; XorpConstSafeMemberCallback3B0(const char* file, int line, O* o, M m) : XorpConstMemberCallback3B0(file, line, o, m), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback3B0() {} void dispatch(A1 a1, A2 a2, A3 a3) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback3B0::dispatch(a1, a2, a3); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory3B0 { static XorpConstMemberCallback3B0* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3) const) { return new XorpConstSafeMemberCallback3B0(file, line, o, p); } }; template struct XorpConstMemberCallbackFactory3B0 { static XorpConstMemberCallback3B0* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3) const) { return new XorpConstMemberCallback3B0(file, line, o, p); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 3 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback3::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3) const) { return XorpConstMemberCallbackFactory3B0::True>::make(file, line, o, p); } /** * Factory function that creates a callback object targetted at a * const member function with 3 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback3::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3) const) { return XorpConstMemberCallbackFactory3B0::True>::make(file, line, &o, p); } /** * @short Callback object for functions with 3 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpFunctionCallback3B1 : public XorpCallback3 { typedef R (*F)(A1, A2, A3, BA1); XorpFunctionCallback3B1(const char* file, int line, F f, BA1 ba1) : XorpCallback3(file, line), _f(f), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, _ba1); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; }; /** * @short Callback object for void functions with 3 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpFunctionCallback3B1 : public XorpCallback3 { typedef void (*F)(A1, A2, A3, BA1); XorpFunctionCallback3B1(const char* file, int line, F f, BA1 ba1) : XorpCallback3(file, line), _f(f), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3) { record_dispatch_enter(); (*_f)(a1, a2, a3, _ba1); record_dispatch_leave(); } protected: F _f; BA1 _ba1; }; /** * Factory function that creates a callback object targetted at a * function with 3 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback3::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, BA1), BA1 ba1) { return typename XorpCallback3::RefPtr(new XorpFunctionCallback3B1(file, line, f, ba1)); } /** * @short Callback object for member methods with 3 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpMemberCallback3B1 : public XorpCallback3 { typedef R (O::*M)(A1, A2, A3, BA1) ; XorpMemberCallback3B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpCallback3(file, line), _o(o), _m(m), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, _ba1); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for void member methods with 3 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpMemberCallback3B1 : public XorpCallback3 { typedef void (O::*M)(A1, A2, A3, BA1) ; XorpMemberCallback3B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpCallback3(file, line), _o(o), _m(m), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, _ba1); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for safe member methods with 3 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpSafeMemberCallback3B1 : public XorpMemberCallback3B1, public SafeCallbackBase { typedef typename XorpMemberCallback3B1::M M; XorpSafeMemberCallback3B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpMemberCallback3B1(file, line, o, m, ba1), SafeCallbackBase(o) {} ~XorpSafeMemberCallback3B1() {} R dispatch(A1 a1, A2 a2, A3 a3) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback3B1::dispatch(a1, a2, a3); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 3 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpSafeMemberCallback3B1 : public XorpMemberCallback3B1, public SafeCallbackBase { typedef typename XorpMemberCallback3B1::M M; XorpSafeMemberCallback3B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpMemberCallback3B1(file, line, o, m, ba1), SafeCallbackBase(o) {} ~XorpSafeMemberCallback3B1() {} void dispatch(A1 a1, A2 a2, A3 a3) { if (valid()) { record_dispatch_enter(); XorpMemberCallback3B1::dispatch(a1, a2, a3); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory3B1 { static XorpMemberCallback3B1* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, BA1), BA1 ba1) { return new XorpSafeMemberCallback3B1(file, line, o, p, ba1); } }; template struct XorpMemberCallbackFactory3B1 { static XorpMemberCallback3B1* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, BA1), BA1 ba1) { return new XorpMemberCallback3B1(file, line, o, p, ba1); }; }; /** * Factory function that creates a callback object targetted at a * member function with 3 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback3::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, BA1), BA1 ba1) { return XorpMemberCallbackFactory3B1::True>::make(file, line, o, p, ba1); } /** * Factory function that creates a callback object targetted at a * member function with 3 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback3::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, BA1), BA1 ba1) { return XorpMemberCallbackFactory3B1::True>::make(file, line, &o, p, ba1); } /** * @short Callback object for const member methods with 3 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstMemberCallback3B1 : public XorpCallback3 { typedef R (O::*M)(A1, A2, A3, BA1) const; XorpConstMemberCallback3B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpCallback3(file, line), _o(o), _m(m), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, _ba1); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for void const member methods with 3 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstMemberCallback3B1 : public XorpCallback3 { typedef void (O::*M)(A1, A2, A3, BA1) const; XorpConstMemberCallback3B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpCallback3(file, line), _o(o), _m(m), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, _ba1); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for const safe member methods with 3 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback3B1 : public XorpConstMemberCallback3B1, public SafeCallbackBase { typedef typename XorpConstMemberCallback3B1::M M; XorpConstSafeMemberCallback3B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpConstMemberCallback3B1(file, line, o, m, ba1), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback3B1() {} R dispatch(A1 a1, A2 a2, A3 a3) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback3B1::dispatch(a1, a2, a3); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 3 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback3B1 : public XorpConstMemberCallback3B1, public SafeCallbackBase { typedef typename XorpConstMemberCallback3B1::M M; XorpConstSafeMemberCallback3B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpConstMemberCallback3B1(file, line, o, m, ba1), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback3B1() {} void dispatch(A1 a1, A2 a2, A3 a3) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback3B1::dispatch(a1, a2, a3); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory3B1 { static XorpConstMemberCallback3B1* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, BA1) const, BA1 ba1) { return new XorpConstSafeMemberCallback3B1(file, line, o, p, ba1); } }; template struct XorpConstMemberCallbackFactory3B1 { static XorpConstMemberCallback3B1* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, BA1) const, BA1 ba1) { return new XorpConstMemberCallback3B1(file, line, o, p, ba1); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 3 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback3::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, BA1) const, BA1 ba1) { return XorpConstMemberCallbackFactory3B1::True>::make(file, line, o, p, ba1); } /** * Factory function that creates a callback object targetted at a * const member function with 3 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback3::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, BA1) const, BA1 ba1) { return XorpConstMemberCallbackFactory3B1::True>::make(file, line, &o, p, ba1); } /** * @short Callback object for functions with 3 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpFunctionCallback3B2 : public XorpCallback3 { typedef R (*F)(A1, A2, A3, BA1, BA2); XorpFunctionCallback3B2(const char* file, int line, F f, BA1 ba1, BA2 ba2) : XorpCallback3(file, line), _f(f), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, _ba1, _ba2); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; }; /** * @short Callback object for void functions with 3 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpFunctionCallback3B2 : public XorpCallback3 { typedef void (*F)(A1, A2, A3, BA1, BA2); XorpFunctionCallback3B2(const char* file, int line, F f, BA1 ba1, BA2 ba2) : XorpCallback3(file, line), _f(f), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3) { record_dispatch_enter(); (*_f)(a1, a2, a3, _ba1, _ba2); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; }; /** * Factory function that creates a callback object targetted at a * function with 3 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback3::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, BA1, BA2), BA1 ba1, BA2 ba2) { return typename XorpCallback3::RefPtr(new XorpFunctionCallback3B2(file, line, f, ba1, ba2)); } /** * @short Callback object for member methods with 3 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpMemberCallback3B2 : public XorpCallback3 { typedef R (O::*M)(A1, A2, A3, BA1, BA2) ; XorpMemberCallback3B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback3(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, _ba1, _ba2); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for void member methods with 3 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpMemberCallback3B2 : public XorpCallback3 { typedef void (O::*M)(A1, A2, A3, BA1, BA2) ; XorpMemberCallback3B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback3(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, _ba1, _ba2); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for safe member methods with 3 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpSafeMemberCallback3B2 : public XorpMemberCallback3B2, public SafeCallbackBase { typedef typename XorpMemberCallback3B2::M M; XorpSafeMemberCallback3B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpMemberCallback3B2(file, line, o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpSafeMemberCallback3B2() {} R dispatch(A1 a1, A2 a2, A3 a3) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback3B2::dispatch(a1, a2, a3); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 3 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpSafeMemberCallback3B2 : public XorpMemberCallback3B2, public SafeCallbackBase { typedef typename XorpMemberCallback3B2::M M; XorpSafeMemberCallback3B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpMemberCallback3B2(file, line, o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpSafeMemberCallback3B2() {} void dispatch(A1 a1, A2 a2, A3 a3) { if (valid()) { record_dispatch_enter(); XorpMemberCallback3B2::dispatch(a1, a2, a3); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory3B2 { static XorpMemberCallback3B2* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, BA1, BA2), BA1 ba1, BA2 ba2) { return new XorpSafeMemberCallback3B2(file, line, o, p, ba1, ba2); } }; template struct XorpMemberCallbackFactory3B2 { static XorpMemberCallback3B2* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, BA1, BA2), BA1 ba1, BA2 ba2) { return new XorpMemberCallback3B2(file, line, o, p, ba1, ba2); }; }; /** * Factory function that creates a callback object targetted at a * member function with 3 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback3::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, BA1, BA2), BA1 ba1, BA2 ba2) { return XorpMemberCallbackFactory3B2::True>::make(file, line, o, p, ba1, ba2); } /** * Factory function that creates a callback object targetted at a * member function with 3 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback3::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, BA1, BA2), BA1 ba1, BA2 ba2) { return XorpMemberCallbackFactory3B2::True>::make(file, line, &o, p, ba1, ba2); } /** * @short Callback object for const member methods with 3 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstMemberCallback3B2 : public XorpCallback3 { typedef R (O::*M)(A1, A2, A3, BA1, BA2) const; XorpConstMemberCallback3B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback3(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, _ba1, _ba2); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for void const member methods with 3 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstMemberCallback3B2 : public XorpCallback3 { typedef void (O::*M)(A1, A2, A3, BA1, BA2) const; XorpConstMemberCallback3B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback3(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, _ba1, _ba2); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for const safe member methods with 3 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback3B2 : public XorpConstMemberCallback3B2, public SafeCallbackBase { typedef typename XorpConstMemberCallback3B2::M M; XorpConstSafeMemberCallback3B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpConstMemberCallback3B2(file, line, o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback3B2() {} R dispatch(A1 a1, A2 a2, A3 a3) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback3B2::dispatch(a1, a2, a3); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 3 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback3B2 : public XorpConstMemberCallback3B2, public SafeCallbackBase { typedef typename XorpConstMemberCallback3B2::M M; XorpConstSafeMemberCallback3B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpConstMemberCallback3B2(file, line, o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback3B2() {} void dispatch(A1 a1, A2 a2, A3 a3) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback3B2::dispatch(a1, a2, a3); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory3B2 { static XorpConstMemberCallback3B2* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, BA1, BA2) const, BA1 ba1, BA2 ba2) { return new XorpConstSafeMemberCallback3B2(file, line, o, p, ba1, ba2); } }; template struct XorpConstMemberCallbackFactory3B2 { static XorpConstMemberCallback3B2* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, BA1, BA2) const, BA1 ba1, BA2 ba2) { return new XorpConstMemberCallback3B2(file, line, o, p, ba1, ba2); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 3 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback3::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, BA1, BA2) const, BA1 ba1, BA2 ba2) { return XorpConstMemberCallbackFactory3B2::True>::make(file, line, o, p, ba1, ba2); } /** * Factory function that creates a callback object targetted at a * const member function with 3 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback3::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, BA1, BA2) const, BA1 ba1, BA2 ba2) { return XorpConstMemberCallbackFactory3B2::True>::make(file, line, &o, p, ba1, ba2); } /** * @short Callback object for functions with 3 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpFunctionCallback3B3 : public XorpCallback3 { typedef R (*F)(A1, A2, A3, BA1, BA2, BA3); XorpFunctionCallback3B3(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback3(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, _ba1, _ba2, _ba3); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; }; /** * @short Callback object for void functions with 3 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpFunctionCallback3B3 : public XorpCallback3 { typedef void (*F)(A1, A2, A3, BA1, BA2, BA3); XorpFunctionCallback3B3(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback3(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3) { record_dispatch_enter(); (*_f)(a1, a2, a3, _ba1, _ba2, _ba3); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; }; /** * Factory function that creates a callback object targetted at a * function with 3 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback3::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return typename XorpCallback3::RefPtr(new XorpFunctionCallback3B3(file, line, f, ba1, ba2, ba3)); } /** * @short Callback object for member methods with 3 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpMemberCallback3B3 : public XorpCallback3 { typedef R (O::*M)(A1, A2, A3, BA1, BA2, BA3) ; XorpMemberCallback3B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback3(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, _ba1, _ba2, _ba3); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for void member methods with 3 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpMemberCallback3B3 : public XorpCallback3 { typedef void (O::*M)(A1, A2, A3, BA1, BA2, BA3) ; XorpMemberCallback3B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback3(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, _ba1, _ba2, _ba3); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for safe member methods with 3 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpSafeMemberCallback3B3 : public XorpMemberCallback3B3, public SafeCallbackBase { typedef typename XorpMemberCallback3B3::M M; XorpSafeMemberCallback3B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpMemberCallback3B3(file, line, o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpSafeMemberCallback3B3() {} R dispatch(A1 a1, A2 a2, A3 a3) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback3B3::dispatch(a1, a2, a3); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 3 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpSafeMemberCallback3B3 : public XorpMemberCallback3B3, public SafeCallbackBase { typedef typename XorpMemberCallback3B3::M M; XorpSafeMemberCallback3B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpMemberCallback3B3(file, line, o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpSafeMemberCallback3B3() {} void dispatch(A1 a1, A2 a2, A3 a3) { if (valid()) { record_dispatch_enter(); XorpMemberCallback3B3::dispatch(a1, a2, a3); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory3B3 { static XorpMemberCallback3B3* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpSafeMemberCallback3B3(file, line, o, p, ba1, ba2, ba3); } }; template struct XorpMemberCallbackFactory3B3 { static XorpMemberCallback3B3* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpMemberCallback3B3(file, line, o, p, ba1, ba2, ba3); }; }; /** * Factory function that creates a callback object targetted at a * member function with 3 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback3::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return XorpMemberCallbackFactory3B3::True>::make(file, line, o, p, ba1, ba2, ba3); } /** * Factory function that creates a callback object targetted at a * member function with 3 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback3::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return XorpMemberCallbackFactory3B3::True>::make(file, line, &o, p, ba1, ba2, ba3); } /** * @short Callback object for const member methods with 3 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstMemberCallback3B3 : public XorpCallback3 { typedef R (O::*M)(A1, A2, A3, BA1, BA2, BA3) const; XorpConstMemberCallback3B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback3(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, _ba1, _ba2, _ba3); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for void const member methods with 3 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstMemberCallback3B3 : public XorpCallback3 { typedef void (O::*M)(A1, A2, A3, BA1, BA2, BA3) const; XorpConstMemberCallback3B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback3(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, _ba1, _ba2, _ba3); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for const safe member methods with 3 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback3B3 : public XorpConstMemberCallback3B3, public SafeCallbackBase { typedef typename XorpConstMemberCallback3B3::M M; XorpConstSafeMemberCallback3B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpConstMemberCallback3B3(file, line, o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback3B3() {} R dispatch(A1 a1, A2 a2, A3 a3) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback3B3::dispatch(a1, a2, a3); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 3 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback3B3 : public XorpConstMemberCallback3B3, public SafeCallbackBase { typedef typename XorpConstMemberCallback3B3::M M; XorpConstSafeMemberCallback3B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpConstMemberCallback3B3(file, line, o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback3B3() {} void dispatch(A1 a1, A2 a2, A3 a3) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback3B3::dispatch(a1, a2, a3); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory3B3 { static XorpConstMemberCallback3B3* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpConstSafeMemberCallback3B3(file, line, o, p, ba1, ba2, ba3); } }; template struct XorpConstMemberCallbackFactory3B3 { static XorpConstMemberCallback3B3* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpConstMemberCallback3B3(file, line, o, p, ba1, ba2, ba3); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 3 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback3::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return XorpConstMemberCallbackFactory3B3::True>::make(file, line, o, p, ba1, ba2, ba3); } /** * Factory function that creates a callback object targetted at a * const member function with 3 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback3::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return XorpConstMemberCallbackFactory3B3::True>::make(file, line, &o, p, ba1, ba2, ba3); } /** * @short Callback object for functions with 3 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpFunctionCallback3B4 : public XorpCallback3 { typedef R (*F)(A1, A2, A3, BA1, BA2, BA3, BA4); XorpFunctionCallback3B4(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback3(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; }; /** * @short Callback object for void functions with 3 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpFunctionCallback3B4 : public XorpCallback3 { typedef void (*F)(A1, A2, A3, BA1, BA2, BA3, BA4); XorpFunctionCallback3B4(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback3(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3) { record_dispatch_enter(); (*_f)(a1, a2, a3, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; }; /** * Factory function that creates a callback object targetted at a * function with 3 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback3::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return typename XorpCallback3::RefPtr(new XorpFunctionCallback3B4(file, line, f, ba1, ba2, ba3, ba4)); } /** * @short Callback object for member methods with 3 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpMemberCallback3B4 : public XorpCallback3 { typedef R (O::*M)(A1, A2, A3, BA1, BA2, BA3, BA4) ; XorpMemberCallback3B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback3(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for void member methods with 3 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpMemberCallback3B4 : public XorpCallback3 { typedef void (O::*M)(A1, A2, A3, BA1, BA2, BA3, BA4) ; XorpMemberCallback3B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback3(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for safe member methods with 3 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpSafeMemberCallback3B4 : public XorpMemberCallback3B4, public SafeCallbackBase { typedef typename XorpMemberCallback3B4::M M; XorpSafeMemberCallback3B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpMemberCallback3B4(file, line, o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpSafeMemberCallback3B4() {} R dispatch(A1 a1, A2 a2, A3 a3) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback3B4::dispatch(a1, a2, a3); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 3 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpSafeMemberCallback3B4 : public XorpMemberCallback3B4, public SafeCallbackBase { typedef typename XorpMemberCallback3B4::M M; XorpSafeMemberCallback3B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpMemberCallback3B4(file, line, o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpSafeMemberCallback3B4() {} void dispatch(A1 a1, A2 a2, A3 a3) { if (valid()) { record_dispatch_enter(); XorpMemberCallback3B4::dispatch(a1, a2, a3); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory3B4 { static XorpMemberCallback3B4* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpSafeMemberCallback3B4(file, line, o, p, ba1, ba2, ba3, ba4); } }; template struct XorpMemberCallbackFactory3B4 { static XorpMemberCallback3B4* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpMemberCallback3B4(file, line, o, p, ba1, ba2, ba3, ba4); }; }; /** * Factory function that creates a callback object targetted at a * member function with 3 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback3::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpMemberCallbackFactory3B4::True>::make(file, line, o, p, ba1, ba2, ba3, ba4); } /** * Factory function that creates a callback object targetted at a * member function with 3 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback3::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpMemberCallbackFactory3B4::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4); } /** * @short Callback object for const member methods with 3 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstMemberCallback3B4 : public XorpCallback3 { typedef R (O::*M)(A1, A2, A3, BA1, BA2, BA3, BA4) const; XorpConstMemberCallback3B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback3(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for void const member methods with 3 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstMemberCallback3B4 : public XorpCallback3 { typedef void (O::*M)(A1, A2, A3, BA1, BA2, BA3, BA4) const; XorpConstMemberCallback3B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback3(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for const safe member methods with 3 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback3B4 : public XorpConstMemberCallback3B4, public SafeCallbackBase { typedef typename XorpConstMemberCallback3B4::M M; XorpConstSafeMemberCallback3B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpConstMemberCallback3B4(file, line, o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback3B4() {} R dispatch(A1 a1, A2 a2, A3 a3) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback3B4::dispatch(a1, a2, a3); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 3 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback3B4 : public XorpConstMemberCallback3B4, public SafeCallbackBase { typedef typename XorpConstMemberCallback3B4::M M; XorpConstSafeMemberCallback3B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpConstMemberCallback3B4(file, line, o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback3B4() {} void dispatch(A1 a1, A2 a2, A3 a3) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback3B4::dispatch(a1, a2, a3); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory3B4 { static XorpConstMemberCallback3B4* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpConstSafeMemberCallback3B4(file, line, o, p, ba1, ba2, ba3, ba4); } }; template struct XorpConstMemberCallbackFactory3B4 { static XorpConstMemberCallback3B4* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpConstMemberCallback3B4(file, line, o, p, ba1, ba2, ba3, ba4); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 3 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback3::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpConstMemberCallbackFactory3B4::True>::make(file, line, o, p, ba1, ba2, ba3, ba4); } /** * Factory function that creates a callback object targetted at a * const member function with 3 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback3::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpConstMemberCallbackFactory3B4::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4); } /** * @short Callback object for functions with 3 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpFunctionCallback3B5 : public XorpCallback3 { typedef R (*F)(A1, A2, A3, BA1, BA2, BA3, BA4, BA5); XorpFunctionCallback3B5(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback3(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; }; /** * @short Callback object for void functions with 3 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpFunctionCallback3B5 : public XorpCallback3 { typedef void (*F)(A1, A2, A3, BA1, BA2, BA3, BA4, BA5); XorpFunctionCallback3B5(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback3(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3) { record_dispatch_enter(); (*_f)(a1, a2, a3, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; }; /** * Factory function that creates a callback object targetted at a * function with 3 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback3::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return typename XorpCallback3::RefPtr(new XorpFunctionCallback3B5(file, line, f, ba1, ba2, ba3, ba4, ba5)); } /** * @short Callback object for member methods with 3 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpMemberCallback3B5 : public XorpCallback3 { typedef R (O::*M)(A1, A2, A3, BA1, BA2, BA3, BA4, BA5) ; XorpMemberCallback3B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback3(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for void member methods with 3 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpMemberCallback3B5 : public XorpCallback3 { typedef void (O::*M)(A1, A2, A3, BA1, BA2, BA3, BA4, BA5) ; XorpMemberCallback3B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback3(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for safe member methods with 3 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpSafeMemberCallback3B5 : public XorpMemberCallback3B5, public SafeCallbackBase { typedef typename XorpMemberCallback3B5::M M; XorpSafeMemberCallback3B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpMemberCallback3B5(file, line, o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpSafeMemberCallback3B5() {} R dispatch(A1 a1, A2 a2, A3 a3) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback3B5::dispatch(a1, a2, a3); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 3 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpSafeMemberCallback3B5 : public XorpMemberCallback3B5, public SafeCallbackBase { typedef typename XorpMemberCallback3B5::M M; XorpSafeMemberCallback3B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpMemberCallback3B5(file, line, o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpSafeMemberCallback3B5() {} void dispatch(A1 a1, A2 a2, A3 a3) { if (valid()) { record_dispatch_enter(); XorpMemberCallback3B5::dispatch(a1, a2, a3); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory3B5 { static XorpMemberCallback3B5* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpSafeMemberCallback3B5(file, line, o, p, ba1, ba2, ba3, ba4, ba5); } }; template struct XorpMemberCallbackFactory3B5 { static XorpMemberCallback3B5* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpMemberCallback3B5(file, line, o, p, ba1, ba2, ba3, ba4, ba5); }; }; /** * Factory function that creates a callback object targetted at a * member function with 3 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback3::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpMemberCallbackFactory3B5::True>::make(file, line, o, p, ba1, ba2, ba3, ba4, ba5); } /** * Factory function that creates a callback object targetted at a * member function with 3 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback3::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpMemberCallbackFactory3B5::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4, ba5); } /** * @short Callback object for const member methods with 3 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstMemberCallback3B5 : public XorpCallback3 { typedef R (O::*M)(A1, A2, A3, BA1, BA2, BA3, BA4, BA5) const; XorpConstMemberCallback3B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback3(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for void const member methods with 3 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstMemberCallback3B5 : public XorpCallback3 { typedef void (O::*M)(A1, A2, A3, BA1, BA2, BA3, BA4, BA5) const; XorpConstMemberCallback3B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback3(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for const safe member methods with 3 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback3B5 : public XorpConstMemberCallback3B5, public SafeCallbackBase { typedef typename XorpConstMemberCallback3B5::M M; XorpConstSafeMemberCallback3B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpConstMemberCallback3B5(file, line, o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback3B5() {} R dispatch(A1 a1, A2 a2, A3 a3) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback3B5::dispatch(a1, a2, a3); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 3 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback3B5 : public XorpConstMemberCallback3B5, public SafeCallbackBase { typedef typename XorpConstMemberCallback3B5::M M; XorpConstSafeMemberCallback3B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpConstMemberCallback3B5(file, line, o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback3B5() {} void dispatch(A1 a1, A2 a2, A3 a3) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback3B5::dispatch(a1, a2, a3); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory3B5 { static XorpConstMemberCallback3B5* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpConstSafeMemberCallback3B5(file, line, o, p, ba1, ba2, ba3, ba4, ba5); } }; template struct XorpConstMemberCallbackFactory3B5 { static XorpConstMemberCallback3B5* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpConstMemberCallback3B5(file, line, o, p, ba1, ba2, ba3, ba4, ba5); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 3 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback3::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpConstMemberCallbackFactory3B5::True>::make(file, line, o, p, ba1, ba2, ba3, ba4, ba5); } /** * Factory function that creates a callback object targetted at a * const member function with 3 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback3::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpConstMemberCallbackFactory3B5::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4, ba5); } /** * @short Callback object for functions with 3 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpFunctionCallback3B6 : public XorpCallback3 { typedef R (*F)(A1, A2, A3, BA1, BA2, BA3, BA4, BA5, BA6); XorpFunctionCallback3B6(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback3(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; BA6 _ba6; }; /** * @short Callback object for void functions with 3 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpFunctionCallback3B6 : public XorpCallback3 { typedef void (*F)(A1, A2, A3, BA1, BA2, BA3, BA4, BA5, BA6); XorpFunctionCallback3B6(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback3(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3) { record_dispatch_enter(); (*_f)(a1, a2, a3, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; BA6 _ba6; }; /** * Factory function that creates a callback object targetted at a * function with 3 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback3::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return typename XorpCallback3::RefPtr(new XorpFunctionCallback3B6(file, line, f, ba1, ba2, ba3, ba4, ba5, ba6)); } /** * @short Callback object for member methods with 3 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpMemberCallback3B6 : public XorpCallback3 { typedef R (O::*M)(A1, A2, A3, BA1, BA2, BA3, BA4, BA5, BA6) ; XorpMemberCallback3B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback3(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for void member methods with 3 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpMemberCallback3B6 : public XorpCallback3 { typedef void (O::*M)(A1, A2, A3, BA1, BA2, BA3, BA4, BA5, BA6) ; XorpMemberCallback3B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback3(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for safe member methods with 3 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpSafeMemberCallback3B6 : public XorpMemberCallback3B6, public SafeCallbackBase { typedef typename XorpMemberCallback3B6::M M; XorpSafeMemberCallback3B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpMemberCallback3B6(file, line, o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpSafeMemberCallback3B6() {} R dispatch(A1 a1, A2 a2, A3 a3) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback3B6::dispatch(a1, a2, a3); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 3 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpSafeMemberCallback3B6 : public XorpMemberCallback3B6, public SafeCallbackBase { typedef typename XorpMemberCallback3B6::M M; XorpSafeMemberCallback3B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpMemberCallback3B6(file, line, o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpSafeMemberCallback3B6() {} void dispatch(A1 a1, A2 a2, A3 a3) { if (valid()) { record_dispatch_enter(); XorpMemberCallback3B6::dispatch(a1, a2, a3); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory3B6 { static XorpMemberCallback3B6* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpSafeMemberCallback3B6(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); } }; template struct XorpMemberCallbackFactory3B6 { static XorpMemberCallback3B6* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpMemberCallback3B6(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); }; }; /** * Factory function that creates a callback object targetted at a * member function with 3 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback3::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpMemberCallbackFactory3B6::True>::make(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * Factory function that creates a callback object targetted at a * member function with 3 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback3::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpMemberCallbackFactory3B6::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * @short Callback object for const member methods with 3 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstMemberCallback3B6 : public XorpCallback3 { typedef R (O::*M)(A1, A2, A3, BA1, BA2, BA3, BA4, BA5, BA6) const; XorpConstMemberCallback3B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback3(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for void const member methods with 3 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstMemberCallback3B6 : public XorpCallback3 { typedef void (O::*M)(A1, A2, A3, BA1, BA2, BA3, BA4, BA5, BA6) const; XorpConstMemberCallback3B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback3(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for const safe member methods with 3 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback3B6 : public XorpConstMemberCallback3B6, public SafeCallbackBase { typedef typename XorpConstMemberCallback3B6::M M; XorpConstSafeMemberCallback3B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpConstMemberCallback3B6(file, line, o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback3B6() {} R dispatch(A1 a1, A2 a2, A3 a3) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback3B6::dispatch(a1, a2, a3); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 3 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback3B6 : public XorpConstMemberCallback3B6, public SafeCallbackBase { typedef typename XorpConstMemberCallback3B6::M M; XorpConstSafeMemberCallback3B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpConstMemberCallback3B6(file, line, o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback3B6() {} void dispatch(A1 a1, A2 a2, A3 a3) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback3B6::dispatch(a1, a2, a3); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory3B6 { static XorpConstMemberCallback3B6* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpConstSafeMemberCallback3B6(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); } }; template struct XorpConstMemberCallbackFactory3B6 { static XorpConstMemberCallback3B6* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpConstMemberCallback3B6(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 3 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback3::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpConstMemberCallbackFactory3B6::True>::make(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * Factory function that creates a callback object targetted at a * const member function with 3 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback3::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpConstMemberCallbackFactory3B6::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /////////////////////////////////////////////////////////////////////////////// // // Code relating to callbacks with 4 late args // /** * @short Base class for callbacks with 4 dispatch time args. */ template struct XorpCallback4 { typedef ref_ptr RefPtr; XorpCallback4(const char* file, int line) : _file(file), _line(line) {} virtual ~XorpCallback4() {} virtual R dispatch(A1, A2, A3, A4) = 0; const char* file() const { return _file; } int line() const { return _line; } private: const char* _file; int _line; }; /** * @short Callback object for functions with 4 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpFunctionCallback4B0 : public XorpCallback4 { typedef R (*F)(A1, A2, A3, A4); XorpFunctionCallback4B0(const char* file, int line, F f) : XorpCallback4(file, line), _f(f) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4); record_dispatch_leave(); return r; } protected: F _f; }; /** * @short Callback object for void functions with 4 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpFunctionCallback4B0 : public XorpCallback4 { typedef void (*F)(A1, A2, A3, A4); XorpFunctionCallback4B0(const char* file, int line, F f) : XorpCallback4(file, line), _f(f) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4); record_dispatch_leave(); } protected: F _f; }; /** * Factory function that creates a callback object targetted at a * function with 4 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback4::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4)) { return typename XorpCallback4::RefPtr(new XorpFunctionCallback4B0(file, line, f)); } /** * @short Callback object for member methods with 4 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpMemberCallback4B0 : public XorpCallback4 { typedef R (O::*M)(A1, A2, A3, A4) ; XorpMemberCallback4B0(const char* file, int line, O* o, M m) : XorpCallback4(file, line), _o(o), _m(m) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for void member methods with 4 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpMemberCallback4B0 : public XorpCallback4 { typedef void (O::*M)(A1, A2, A3, A4) ; XorpMemberCallback4B0(const char* file, int line, O* o, M m) : XorpCallback4(file, line), _o(o), _m(m) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for safe member methods with 4 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpSafeMemberCallback4B0 : public XorpMemberCallback4B0, public SafeCallbackBase { typedef typename XorpMemberCallback4B0::M M; XorpSafeMemberCallback4B0(const char* file, int line, O* o, M m) : XorpMemberCallback4B0(file, line, o, m), SafeCallbackBase(o) {} ~XorpSafeMemberCallback4B0() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback4B0::dispatch(a1, a2, a3, a4); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 4 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpSafeMemberCallback4B0 : public XorpMemberCallback4B0, public SafeCallbackBase { typedef typename XorpMemberCallback4B0::M M; XorpSafeMemberCallback4B0(const char* file, int line, O* o, M m) : XorpMemberCallback4B0(file, line, o, m), SafeCallbackBase(o) {} ~XorpSafeMemberCallback4B0() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { if (valid()) { record_dispatch_enter(); XorpMemberCallback4B0::dispatch(a1, a2, a3, a4); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory4B0 { static XorpMemberCallback4B0* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4)) { return new XorpSafeMemberCallback4B0(file, line, o, p); } }; template struct XorpMemberCallbackFactory4B0 { static XorpMemberCallback4B0* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4)) { return new XorpMemberCallback4B0(file, line, o, p); }; }; /** * Factory function that creates a callback object targetted at a * member function with 4 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback4::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4)) { return XorpMemberCallbackFactory4B0::True>::make(file, line, o, p); } /** * Factory function that creates a callback object targetted at a * member function with 4 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback4::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4)) { return XorpMemberCallbackFactory4B0::True>::make(file, line, &o, p); } /** * @short Callback object for const member methods with 4 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstMemberCallback4B0 : public XorpCallback4 { typedef R (O::*M)(A1, A2, A3, A4) const; XorpConstMemberCallback4B0(const char* file, int line, O* o, M m) : XorpCallback4(file, line), _o(o), _m(m) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for void const member methods with 4 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstMemberCallback4B0 : public XorpCallback4 { typedef void (O::*M)(A1, A2, A3, A4) const; XorpConstMemberCallback4B0(const char* file, int line, O* o, M m) : XorpCallback4(file, line), _o(o), _m(m) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for const safe member methods with 4 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback4B0 : public XorpConstMemberCallback4B0, public SafeCallbackBase { typedef typename XorpConstMemberCallback4B0::M M; XorpConstSafeMemberCallback4B0(const char* file, int line, O* o, M m) : XorpConstMemberCallback4B0(file, line, o, m), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback4B0() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback4B0::dispatch(a1, a2, a3, a4); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 4 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback4B0 : public XorpConstMemberCallback4B0, public SafeCallbackBase { typedef typename XorpConstMemberCallback4B0::M M; XorpConstSafeMemberCallback4B0(const char* file, int line, O* o, M m) : XorpConstMemberCallback4B0(file, line, o, m), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback4B0() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback4B0::dispatch(a1, a2, a3, a4); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory4B0 { static XorpConstMemberCallback4B0* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4) const) { return new XorpConstSafeMemberCallback4B0(file, line, o, p); } }; template struct XorpConstMemberCallbackFactory4B0 { static XorpConstMemberCallback4B0* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4) const) { return new XorpConstMemberCallback4B0(file, line, o, p); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 4 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback4::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4) const) { return XorpConstMemberCallbackFactory4B0::True>::make(file, line, o, p); } /** * Factory function that creates a callback object targetted at a * const member function with 4 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback4::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4) const) { return XorpConstMemberCallbackFactory4B0::True>::make(file, line, &o, p); } /** * @short Callback object for functions with 4 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpFunctionCallback4B1 : public XorpCallback4 { typedef R (*F)(A1, A2, A3, A4, BA1); XorpFunctionCallback4B1(const char* file, int line, F f, BA1 ba1) : XorpCallback4(file, line), _f(f), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, _ba1); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; }; /** * @short Callback object for void functions with 4 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpFunctionCallback4B1 : public XorpCallback4 { typedef void (*F)(A1, A2, A3, A4, BA1); XorpFunctionCallback4B1(const char* file, int line, F f, BA1 ba1) : XorpCallback4(file, line), _f(f), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, _ba1); record_dispatch_leave(); } protected: F _f; BA1 _ba1; }; /** * Factory function that creates a callback object targetted at a * function with 4 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback4::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, BA1), BA1 ba1) { return typename XorpCallback4::RefPtr(new XorpFunctionCallback4B1(file, line, f, ba1)); } /** * @short Callback object for member methods with 4 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpMemberCallback4B1 : public XorpCallback4 { typedef R (O::*M)(A1, A2, A3, A4, BA1) ; XorpMemberCallback4B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpCallback4(file, line), _o(o), _m(m), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, _ba1); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for void member methods with 4 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpMemberCallback4B1 : public XorpCallback4 { typedef void (O::*M)(A1, A2, A3, A4, BA1) ; XorpMemberCallback4B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpCallback4(file, line), _o(o), _m(m), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, _ba1); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for safe member methods with 4 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpSafeMemberCallback4B1 : public XorpMemberCallback4B1, public SafeCallbackBase { typedef typename XorpMemberCallback4B1::M M; XorpSafeMemberCallback4B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpMemberCallback4B1(file, line, o, m, ba1), SafeCallbackBase(o) {} ~XorpSafeMemberCallback4B1() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback4B1::dispatch(a1, a2, a3, a4); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 4 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpSafeMemberCallback4B1 : public XorpMemberCallback4B1, public SafeCallbackBase { typedef typename XorpMemberCallback4B1::M M; XorpSafeMemberCallback4B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpMemberCallback4B1(file, line, o, m, ba1), SafeCallbackBase(o) {} ~XorpSafeMemberCallback4B1() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { if (valid()) { record_dispatch_enter(); XorpMemberCallback4B1::dispatch(a1, a2, a3, a4); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory4B1 { static XorpMemberCallback4B1* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, BA1), BA1 ba1) { return new XorpSafeMemberCallback4B1(file, line, o, p, ba1); } }; template struct XorpMemberCallbackFactory4B1 { static XorpMemberCallback4B1* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, BA1), BA1 ba1) { return new XorpMemberCallback4B1(file, line, o, p, ba1); }; }; /** * Factory function that creates a callback object targetted at a * member function with 4 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback4::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, BA1), BA1 ba1) { return XorpMemberCallbackFactory4B1::True>::make(file, line, o, p, ba1); } /** * Factory function that creates a callback object targetted at a * member function with 4 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback4::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, BA1), BA1 ba1) { return XorpMemberCallbackFactory4B1::True>::make(file, line, &o, p, ba1); } /** * @short Callback object for const member methods with 4 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstMemberCallback4B1 : public XorpCallback4 { typedef R (O::*M)(A1, A2, A3, A4, BA1) const; XorpConstMemberCallback4B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpCallback4(file, line), _o(o), _m(m), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, _ba1); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for void const member methods with 4 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstMemberCallback4B1 : public XorpCallback4 { typedef void (O::*M)(A1, A2, A3, A4, BA1) const; XorpConstMemberCallback4B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpCallback4(file, line), _o(o), _m(m), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, _ba1); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for const safe member methods with 4 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback4B1 : public XorpConstMemberCallback4B1, public SafeCallbackBase { typedef typename XorpConstMemberCallback4B1::M M; XorpConstSafeMemberCallback4B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpConstMemberCallback4B1(file, line, o, m, ba1), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback4B1() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback4B1::dispatch(a1, a2, a3, a4); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 4 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback4B1 : public XorpConstMemberCallback4B1, public SafeCallbackBase { typedef typename XorpConstMemberCallback4B1::M M; XorpConstSafeMemberCallback4B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpConstMemberCallback4B1(file, line, o, m, ba1), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback4B1() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback4B1::dispatch(a1, a2, a3, a4); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory4B1 { static XorpConstMemberCallback4B1* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, BA1) const, BA1 ba1) { return new XorpConstSafeMemberCallback4B1(file, line, o, p, ba1); } }; template struct XorpConstMemberCallbackFactory4B1 { static XorpConstMemberCallback4B1* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, BA1) const, BA1 ba1) { return new XorpConstMemberCallback4B1(file, line, o, p, ba1); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 4 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback4::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, BA1) const, BA1 ba1) { return XorpConstMemberCallbackFactory4B1::True>::make(file, line, o, p, ba1); } /** * Factory function that creates a callback object targetted at a * const member function with 4 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback4::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, BA1) const, BA1 ba1) { return XorpConstMemberCallbackFactory4B1::True>::make(file, line, &o, p, ba1); } /** * @short Callback object for functions with 4 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpFunctionCallback4B2 : public XorpCallback4 { typedef R (*F)(A1, A2, A3, A4, BA1, BA2); XorpFunctionCallback4B2(const char* file, int line, F f, BA1 ba1, BA2 ba2) : XorpCallback4(file, line), _f(f), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, _ba1, _ba2); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; }; /** * @short Callback object for void functions with 4 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpFunctionCallback4B2 : public XorpCallback4 { typedef void (*F)(A1, A2, A3, A4, BA1, BA2); XorpFunctionCallback4B2(const char* file, int line, F f, BA1 ba1, BA2 ba2) : XorpCallback4(file, line), _f(f), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, _ba1, _ba2); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; }; /** * Factory function that creates a callback object targetted at a * function with 4 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback4::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, BA1, BA2), BA1 ba1, BA2 ba2) { return typename XorpCallback4::RefPtr(new XorpFunctionCallback4B2(file, line, f, ba1, ba2)); } /** * @short Callback object for member methods with 4 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpMemberCallback4B2 : public XorpCallback4 { typedef R (O::*M)(A1, A2, A3, A4, BA1, BA2) ; XorpMemberCallback4B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback4(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, _ba1, _ba2); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for void member methods with 4 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpMemberCallback4B2 : public XorpCallback4 { typedef void (O::*M)(A1, A2, A3, A4, BA1, BA2) ; XorpMemberCallback4B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback4(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, _ba1, _ba2); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for safe member methods with 4 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpSafeMemberCallback4B2 : public XorpMemberCallback4B2, public SafeCallbackBase { typedef typename XorpMemberCallback4B2::M M; XorpSafeMemberCallback4B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpMemberCallback4B2(file, line, o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpSafeMemberCallback4B2() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback4B2::dispatch(a1, a2, a3, a4); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 4 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpSafeMemberCallback4B2 : public XorpMemberCallback4B2, public SafeCallbackBase { typedef typename XorpMemberCallback4B2::M M; XorpSafeMemberCallback4B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpMemberCallback4B2(file, line, o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpSafeMemberCallback4B2() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { if (valid()) { record_dispatch_enter(); XorpMemberCallback4B2::dispatch(a1, a2, a3, a4); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory4B2 { static XorpMemberCallback4B2* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, BA1, BA2), BA1 ba1, BA2 ba2) { return new XorpSafeMemberCallback4B2(file, line, o, p, ba1, ba2); } }; template struct XorpMemberCallbackFactory4B2 { static XorpMemberCallback4B2* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, BA1, BA2), BA1 ba1, BA2 ba2) { return new XorpMemberCallback4B2(file, line, o, p, ba1, ba2); }; }; /** * Factory function that creates a callback object targetted at a * member function with 4 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback4::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, BA1, BA2), BA1 ba1, BA2 ba2) { return XorpMemberCallbackFactory4B2::True>::make(file, line, o, p, ba1, ba2); } /** * Factory function that creates a callback object targetted at a * member function with 4 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback4::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, BA1, BA2), BA1 ba1, BA2 ba2) { return XorpMemberCallbackFactory4B2::True>::make(file, line, &o, p, ba1, ba2); } /** * @short Callback object for const member methods with 4 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstMemberCallback4B2 : public XorpCallback4 { typedef R (O::*M)(A1, A2, A3, A4, BA1, BA2) const; XorpConstMemberCallback4B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback4(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, _ba1, _ba2); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for void const member methods with 4 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstMemberCallback4B2 : public XorpCallback4 { typedef void (O::*M)(A1, A2, A3, A4, BA1, BA2) const; XorpConstMemberCallback4B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback4(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, _ba1, _ba2); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for const safe member methods with 4 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback4B2 : public XorpConstMemberCallback4B2, public SafeCallbackBase { typedef typename XorpConstMemberCallback4B2::M M; XorpConstSafeMemberCallback4B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpConstMemberCallback4B2(file, line, o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback4B2() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback4B2::dispatch(a1, a2, a3, a4); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 4 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback4B2 : public XorpConstMemberCallback4B2, public SafeCallbackBase { typedef typename XorpConstMemberCallback4B2::M M; XorpConstSafeMemberCallback4B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpConstMemberCallback4B2(file, line, o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback4B2() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback4B2::dispatch(a1, a2, a3, a4); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory4B2 { static XorpConstMemberCallback4B2* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, BA1, BA2) const, BA1 ba1, BA2 ba2) { return new XorpConstSafeMemberCallback4B2(file, line, o, p, ba1, ba2); } }; template struct XorpConstMemberCallbackFactory4B2 { static XorpConstMemberCallback4B2* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, BA1, BA2) const, BA1 ba1, BA2 ba2) { return new XorpConstMemberCallback4B2(file, line, o, p, ba1, ba2); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 4 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback4::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, BA1, BA2) const, BA1 ba1, BA2 ba2) { return XorpConstMemberCallbackFactory4B2::True>::make(file, line, o, p, ba1, ba2); } /** * Factory function that creates a callback object targetted at a * const member function with 4 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback4::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, BA1, BA2) const, BA1 ba1, BA2 ba2) { return XorpConstMemberCallbackFactory4B2::True>::make(file, line, &o, p, ba1, ba2); } /** * @short Callback object for functions with 4 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpFunctionCallback4B3 : public XorpCallback4 { typedef R (*F)(A1, A2, A3, A4, BA1, BA2, BA3); XorpFunctionCallback4B3(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback4(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, _ba1, _ba2, _ba3); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; }; /** * @short Callback object for void functions with 4 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpFunctionCallback4B3 : public XorpCallback4 { typedef void (*F)(A1, A2, A3, A4, BA1, BA2, BA3); XorpFunctionCallback4B3(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback4(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, _ba1, _ba2, _ba3); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; }; /** * Factory function that creates a callback object targetted at a * function with 4 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback4::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return typename XorpCallback4::RefPtr(new XorpFunctionCallback4B3(file, line, f, ba1, ba2, ba3)); } /** * @short Callback object for member methods with 4 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpMemberCallback4B3 : public XorpCallback4 { typedef R (O::*M)(A1, A2, A3, A4, BA1, BA2, BA3) ; XorpMemberCallback4B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback4(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, _ba1, _ba2, _ba3); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for void member methods with 4 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpMemberCallback4B3 : public XorpCallback4 { typedef void (O::*M)(A1, A2, A3, A4, BA1, BA2, BA3) ; XorpMemberCallback4B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback4(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, _ba1, _ba2, _ba3); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for safe member methods with 4 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpSafeMemberCallback4B3 : public XorpMemberCallback4B3, public SafeCallbackBase { typedef typename XorpMemberCallback4B3::M M; XorpSafeMemberCallback4B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpMemberCallback4B3(file, line, o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpSafeMemberCallback4B3() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback4B3::dispatch(a1, a2, a3, a4); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 4 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpSafeMemberCallback4B3 : public XorpMemberCallback4B3, public SafeCallbackBase { typedef typename XorpMemberCallback4B3::M M; XorpSafeMemberCallback4B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpMemberCallback4B3(file, line, o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpSafeMemberCallback4B3() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { if (valid()) { record_dispatch_enter(); XorpMemberCallback4B3::dispatch(a1, a2, a3, a4); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory4B3 { static XorpMemberCallback4B3* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpSafeMemberCallback4B3(file, line, o, p, ba1, ba2, ba3); } }; template struct XorpMemberCallbackFactory4B3 { static XorpMemberCallback4B3* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpMemberCallback4B3(file, line, o, p, ba1, ba2, ba3); }; }; /** * Factory function that creates a callback object targetted at a * member function with 4 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback4::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return XorpMemberCallbackFactory4B3::True>::make(file, line, o, p, ba1, ba2, ba3); } /** * Factory function that creates a callback object targetted at a * member function with 4 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback4::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return XorpMemberCallbackFactory4B3::True>::make(file, line, &o, p, ba1, ba2, ba3); } /** * @short Callback object for const member methods with 4 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstMemberCallback4B3 : public XorpCallback4 { typedef R (O::*M)(A1, A2, A3, A4, BA1, BA2, BA3) const; XorpConstMemberCallback4B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback4(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, _ba1, _ba2, _ba3); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for void const member methods with 4 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstMemberCallback4B3 : public XorpCallback4 { typedef void (O::*M)(A1, A2, A3, A4, BA1, BA2, BA3) const; XorpConstMemberCallback4B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback4(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, _ba1, _ba2, _ba3); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for const safe member methods with 4 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback4B3 : public XorpConstMemberCallback4B3, public SafeCallbackBase { typedef typename XorpConstMemberCallback4B3::M M; XorpConstSafeMemberCallback4B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpConstMemberCallback4B3(file, line, o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback4B3() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback4B3::dispatch(a1, a2, a3, a4); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 4 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback4B3 : public XorpConstMemberCallback4B3, public SafeCallbackBase { typedef typename XorpConstMemberCallback4B3::M M; XorpConstSafeMemberCallback4B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpConstMemberCallback4B3(file, line, o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback4B3() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback4B3::dispatch(a1, a2, a3, a4); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory4B3 { static XorpConstMemberCallback4B3* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpConstSafeMemberCallback4B3(file, line, o, p, ba1, ba2, ba3); } }; template struct XorpConstMemberCallbackFactory4B3 { static XorpConstMemberCallback4B3* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpConstMemberCallback4B3(file, line, o, p, ba1, ba2, ba3); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 4 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback4::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return XorpConstMemberCallbackFactory4B3::True>::make(file, line, o, p, ba1, ba2, ba3); } /** * Factory function that creates a callback object targetted at a * const member function with 4 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback4::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return XorpConstMemberCallbackFactory4B3::True>::make(file, line, &o, p, ba1, ba2, ba3); } /** * @short Callback object for functions with 4 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpFunctionCallback4B4 : public XorpCallback4 { typedef R (*F)(A1, A2, A3, A4, BA1, BA2, BA3, BA4); XorpFunctionCallback4B4(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback4(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; }; /** * @short Callback object for void functions with 4 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpFunctionCallback4B4 : public XorpCallback4 { typedef void (*F)(A1, A2, A3, A4, BA1, BA2, BA3, BA4); XorpFunctionCallback4B4(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback4(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; }; /** * Factory function that creates a callback object targetted at a * function with 4 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback4::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return typename XorpCallback4::RefPtr(new XorpFunctionCallback4B4(file, line, f, ba1, ba2, ba3, ba4)); } /** * @short Callback object for member methods with 4 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpMemberCallback4B4 : public XorpCallback4 { typedef R (O::*M)(A1, A2, A3, A4, BA1, BA2, BA3, BA4) ; XorpMemberCallback4B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback4(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for void member methods with 4 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpMemberCallback4B4 : public XorpCallback4 { typedef void (O::*M)(A1, A2, A3, A4, BA1, BA2, BA3, BA4) ; XorpMemberCallback4B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback4(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for safe member methods with 4 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpSafeMemberCallback4B4 : public XorpMemberCallback4B4, public SafeCallbackBase { typedef typename XorpMemberCallback4B4::M M; XorpSafeMemberCallback4B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpMemberCallback4B4(file, line, o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpSafeMemberCallback4B4() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback4B4::dispatch(a1, a2, a3, a4); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 4 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpSafeMemberCallback4B4 : public XorpMemberCallback4B4, public SafeCallbackBase { typedef typename XorpMemberCallback4B4::M M; XorpSafeMemberCallback4B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpMemberCallback4B4(file, line, o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpSafeMemberCallback4B4() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { if (valid()) { record_dispatch_enter(); XorpMemberCallback4B4::dispatch(a1, a2, a3, a4); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory4B4 { static XorpMemberCallback4B4* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpSafeMemberCallback4B4(file, line, o, p, ba1, ba2, ba3, ba4); } }; template struct XorpMemberCallbackFactory4B4 { static XorpMemberCallback4B4* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpMemberCallback4B4(file, line, o, p, ba1, ba2, ba3, ba4); }; }; /** * Factory function that creates a callback object targetted at a * member function with 4 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback4::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpMemberCallbackFactory4B4::True>::make(file, line, o, p, ba1, ba2, ba3, ba4); } /** * Factory function that creates a callback object targetted at a * member function with 4 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback4::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpMemberCallbackFactory4B4::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4); } /** * @short Callback object for const member methods with 4 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstMemberCallback4B4 : public XorpCallback4 { typedef R (O::*M)(A1, A2, A3, A4, BA1, BA2, BA3, BA4) const; XorpConstMemberCallback4B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback4(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for void const member methods with 4 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstMemberCallback4B4 : public XorpCallback4 { typedef void (O::*M)(A1, A2, A3, A4, BA1, BA2, BA3, BA4) const; XorpConstMemberCallback4B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback4(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for const safe member methods with 4 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback4B4 : public XorpConstMemberCallback4B4, public SafeCallbackBase { typedef typename XorpConstMemberCallback4B4::M M; XorpConstSafeMemberCallback4B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpConstMemberCallback4B4(file, line, o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback4B4() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback4B4::dispatch(a1, a2, a3, a4); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 4 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback4B4 : public XorpConstMemberCallback4B4, public SafeCallbackBase { typedef typename XorpConstMemberCallback4B4::M M; XorpConstSafeMemberCallback4B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpConstMemberCallback4B4(file, line, o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback4B4() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback4B4::dispatch(a1, a2, a3, a4); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory4B4 { static XorpConstMemberCallback4B4* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpConstSafeMemberCallback4B4(file, line, o, p, ba1, ba2, ba3, ba4); } }; template struct XorpConstMemberCallbackFactory4B4 { static XorpConstMemberCallback4B4* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpConstMemberCallback4B4(file, line, o, p, ba1, ba2, ba3, ba4); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 4 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback4::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpConstMemberCallbackFactory4B4::True>::make(file, line, o, p, ba1, ba2, ba3, ba4); } /** * Factory function that creates a callback object targetted at a * const member function with 4 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback4::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpConstMemberCallbackFactory4B4::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4); } /** * @short Callback object for functions with 4 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpFunctionCallback4B5 : public XorpCallback4 { typedef R (*F)(A1, A2, A3, A4, BA1, BA2, BA3, BA4, BA5); XorpFunctionCallback4B5(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback4(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; }; /** * @short Callback object for void functions with 4 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpFunctionCallback4B5 : public XorpCallback4 { typedef void (*F)(A1, A2, A3, A4, BA1, BA2, BA3, BA4, BA5); XorpFunctionCallback4B5(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback4(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; }; /** * Factory function that creates a callback object targetted at a * function with 4 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback4::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return typename XorpCallback4::RefPtr(new XorpFunctionCallback4B5(file, line, f, ba1, ba2, ba3, ba4, ba5)); } /** * @short Callback object for member methods with 4 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpMemberCallback4B5 : public XorpCallback4 { typedef R (O::*M)(A1, A2, A3, A4, BA1, BA2, BA3, BA4, BA5) ; XorpMemberCallback4B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback4(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for void member methods with 4 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpMemberCallback4B5 : public XorpCallback4 { typedef void (O::*M)(A1, A2, A3, A4, BA1, BA2, BA3, BA4, BA5) ; XorpMemberCallback4B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback4(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for safe member methods with 4 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpSafeMemberCallback4B5 : public XorpMemberCallback4B5, public SafeCallbackBase { typedef typename XorpMemberCallback4B5::M M; XorpSafeMemberCallback4B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpMemberCallback4B5(file, line, o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpSafeMemberCallback4B5() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback4B5::dispatch(a1, a2, a3, a4); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 4 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpSafeMemberCallback4B5 : public XorpMemberCallback4B5, public SafeCallbackBase { typedef typename XorpMemberCallback4B5::M M; XorpSafeMemberCallback4B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpMemberCallback4B5(file, line, o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpSafeMemberCallback4B5() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { if (valid()) { record_dispatch_enter(); XorpMemberCallback4B5::dispatch(a1, a2, a3, a4); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory4B5 { static XorpMemberCallback4B5* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpSafeMemberCallback4B5(file, line, o, p, ba1, ba2, ba3, ba4, ba5); } }; template struct XorpMemberCallbackFactory4B5 { static XorpMemberCallback4B5* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpMemberCallback4B5(file, line, o, p, ba1, ba2, ba3, ba4, ba5); }; }; /** * Factory function that creates a callback object targetted at a * member function with 4 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback4::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpMemberCallbackFactory4B5::True>::make(file, line, o, p, ba1, ba2, ba3, ba4, ba5); } /** * Factory function that creates a callback object targetted at a * member function with 4 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback4::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpMemberCallbackFactory4B5::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4, ba5); } /** * @short Callback object for const member methods with 4 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstMemberCallback4B5 : public XorpCallback4 { typedef R (O::*M)(A1, A2, A3, A4, BA1, BA2, BA3, BA4, BA5) const; XorpConstMemberCallback4B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback4(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for void const member methods with 4 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstMemberCallback4B5 : public XorpCallback4 { typedef void (O::*M)(A1, A2, A3, A4, BA1, BA2, BA3, BA4, BA5) const; XorpConstMemberCallback4B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback4(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for const safe member methods with 4 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback4B5 : public XorpConstMemberCallback4B5, public SafeCallbackBase { typedef typename XorpConstMemberCallback4B5::M M; XorpConstSafeMemberCallback4B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpConstMemberCallback4B5(file, line, o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback4B5() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback4B5::dispatch(a1, a2, a3, a4); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 4 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback4B5 : public XorpConstMemberCallback4B5, public SafeCallbackBase { typedef typename XorpConstMemberCallback4B5::M M; XorpConstSafeMemberCallback4B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpConstMemberCallback4B5(file, line, o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback4B5() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback4B5::dispatch(a1, a2, a3, a4); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory4B5 { static XorpConstMemberCallback4B5* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpConstSafeMemberCallback4B5(file, line, o, p, ba1, ba2, ba3, ba4, ba5); } }; template struct XorpConstMemberCallbackFactory4B5 { static XorpConstMemberCallback4B5* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpConstMemberCallback4B5(file, line, o, p, ba1, ba2, ba3, ba4, ba5); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 4 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback4::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpConstMemberCallbackFactory4B5::True>::make(file, line, o, p, ba1, ba2, ba3, ba4, ba5); } /** * Factory function that creates a callback object targetted at a * const member function with 4 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback4::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpConstMemberCallbackFactory4B5::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4, ba5); } /** * @short Callback object for functions with 4 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpFunctionCallback4B6 : public XorpCallback4 { typedef R (*F)(A1, A2, A3, A4, BA1, BA2, BA3, BA4, BA5, BA6); XorpFunctionCallback4B6(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback4(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; BA6 _ba6; }; /** * @short Callback object for void functions with 4 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpFunctionCallback4B6 : public XorpCallback4 { typedef void (*F)(A1, A2, A3, A4, BA1, BA2, BA3, BA4, BA5, BA6); XorpFunctionCallback4B6(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback4(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; BA6 _ba6; }; /** * Factory function that creates a callback object targetted at a * function with 4 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback4::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return typename XorpCallback4::RefPtr(new XorpFunctionCallback4B6(file, line, f, ba1, ba2, ba3, ba4, ba5, ba6)); } /** * @short Callback object for member methods with 4 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpMemberCallback4B6 : public XorpCallback4 { typedef R (O::*M)(A1, A2, A3, A4, BA1, BA2, BA3, BA4, BA5, BA6) ; XorpMemberCallback4B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback4(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for void member methods with 4 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpMemberCallback4B6 : public XorpCallback4 { typedef void (O::*M)(A1, A2, A3, A4, BA1, BA2, BA3, BA4, BA5, BA6) ; XorpMemberCallback4B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback4(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for safe member methods with 4 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpSafeMemberCallback4B6 : public XorpMemberCallback4B6, public SafeCallbackBase { typedef typename XorpMemberCallback4B6::M M; XorpSafeMemberCallback4B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpMemberCallback4B6(file, line, o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpSafeMemberCallback4B6() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback4B6::dispatch(a1, a2, a3, a4); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 4 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpSafeMemberCallback4B6 : public XorpMemberCallback4B6, public SafeCallbackBase { typedef typename XorpMemberCallback4B6::M M; XorpSafeMemberCallback4B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpMemberCallback4B6(file, line, o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpSafeMemberCallback4B6() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { if (valid()) { record_dispatch_enter(); XorpMemberCallback4B6::dispatch(a1, a2, a3, a4); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory4B6 { static XorpMemberCallback4B6* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpSafeMemberCallback4B6(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); } }; template struct XorpMemberCallbackFactory4B6 { static XorpMemberCallback4B6* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpMemberCallback4B6(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); }; }; /** * Factory function that creates a callback object targetted at a * member function with 4 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback4::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpMemberCallbackFactory4B6::True>::make(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * Factory function that creates a callback object targetted at a * member function with 4 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback4::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpMemberCallbackFactory4B6::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * @short Callback object for const member methods with 4 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstMemberCallback4B6 : public XorpCallback4 { typedef R (O::*M)(A1, A2, A3, A4, BA1, BA2, BA3, BA4, BA5, BA6) const; XorpConstMemberCallback4B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback4(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for void const member methods with 4 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstMemberCallback4B6 : public XorpCallback4 { typedef void (O::*M)(A1, A2, A3, A4, BA1, BA2, BA3, BA4, BA5, BA6) const; XorpConstMemberCallback4B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback4(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for const safe member methods with 4 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback4B6 : public XorpConstMemberCallback4B6, public SafeCallbackBase { typedef typename XorpConstMemberCallback4B6::M M; XorpConstSafeMemberCallback4B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpConstMemberCallback4B6(file, line, o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback4B6() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback4B6::dispatch(a1, a2, a3, a4); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 4 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback4B6 : public XorpConstMemberCallback4B6, public SafeCallbackBase { typedef typename XorpConstMemberCallback4B6::M M; XorpConstSafeMemberCallback4B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpConstMemberCallback4B6(file, line, o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback4B6() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback4B6::dispatch(a1, a2, a3, a4); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory4B6 { static XorpConstMemberCallback4B6* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpConstSafeMemberCallback4B6(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); } }; template struct XorpConstMemberCallbackFactory4B6 { static XorpConstMemberCallback4B6* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpConstMemberCallback4B6(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 4 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback4::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpConstMemberCallbackFactory4B6::True>::make(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * Factory function that creates a callback object targetted at a * const member function with 4 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback4::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpConstMemberCallbackFactory4B6::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /////////////////////////////////////////////////////////////////////////////// // // Code relating to callbacks with 5 late args // /** * @short Base class for callbacks with 5 dispatch time args. */ template struct XorpCallback5 { typedef ref_ptr RefPtr; XorpCallback5(const char* file, int line) : _file(file), _line(line) {} virtual ~XorpCallback5() {} virtual R dispatch(A1, A2, A3, A4, A5) = 0; const char* file() const { return _file; } int line() const { return _line; } private: const char* _file; int _line; }; /** * @short Callback object for functions with 5 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpFunctionCallback5B0 : public XorpCallback5 { typedef R (*F)(A1, A2, A3, A4, A5); XorpFunctionCallback5B0(const char* file, int line, F f) : XorpCallback5(file, line), _f(f) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5); record_dispatch_leave(); return r; } protected: F _f; }; /** * @short Callback object for void functions with 5 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpFunctionCallback5B0 : public XorpCallback5 { typedef void (*F)(A1, A2, A3, A4, A5); XorpFunctionCallback5B0(const char* file, int line, F f) : XorpCallback5(file, line), _f(f) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5); record_dispatch_leave(); } protected: F _f; }; /** * Factory function that creates a callback object targetted at a * function with 5 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback5::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5)) { return typename XorpCallback5::RefPtr(new XorpFunctionCallback5B0(file, line, f)); } /** * @short Callback object for member methods with 5 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpMemberCallback5B0 : public XorpCallback5 { typedef R (O::*M)(A1, A2, A3, A4, A5) ; XorpMemberCallback5B0(const char* file, int line, O* o, M m) : XorpCallback5(file, line), _o(o), _m(m) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for void member methods with 5 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpMemberCallback5B0 : public XorpCallback5 { typedef void (O::*M)(A1, A2, A3, A4, A5) ; XorpMemberCallback5B0(const char* file, int line, O* o, M m) : XorpCallback5(file, line), _o(o), _m(m) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for safe member methods with 5 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpSafeMemberCallback5B0 : public XorpMemberCallback5B0, public SafeCallbackBase { typedef typename XorpMemberCallback5B0::M M; XorpSafeMemberCallback5B0(const char* file, int line, O* o, M m) : XorpMemberCallback5B0(file, line, o, m), SafeCallbackBase(o) {} ~XorpSafeMemberCallback5B0() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback5B0::dispatch(a1, a2, a3, a4, a5); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 5 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpSafeMemberCallback5B0 : public XorpMemberCallback5B0, public SafeCallbackBase { typedef typename XorpMemberCallback5B0::M M; XorpSafeMemberCallback5B0(const char* file, int line, O* o, M m) : XorpMemberCallback5B0(file, line, o, m), SafeCallbackBase(o) {} ~XorpSafeMemberCallback5B0() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { if (valid()) { record_dispatch_enter(); XorpMemberCallback5B0::dispatch(a1, a2, a3, a4, a5); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory5B0 { static XorpMemberCallback5B0* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5)) { return new XorpSafeMemberCallback5B0(file, line, o, p); } }; template struct XorpMemberCallbackFactory5B0 { static XorpMemberCallback5B0* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5)) { return new XorpMemberCallback5B0(file, line, o, p); }; }; /** * Factory function that creates a callback object targetted at a * member function with 5 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback5::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5)) { return XorpMemberCallbackFactory5B0::True>::make(file, line, o, p); } /** * Factory function that creates a callback object targetted at a * member function with 5 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback5::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5)) { return XorpMemberCallbackFactory5B0::True>::make(file, line, &o, p); } /** * @short Callback object for const member methods with 5 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstMemberCallback5B0 : public XorpCallback5 { typedef R (O::*M)(A1, A2, A3, A4, A5) const; XorpConstMemberCallback5B0(const char* file, int line, O* o, M m) : XorpCallback5(file, line), _o(o), _m(m) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for void const member methods with 5 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstMemberCallback5B0 : public XorpCallback5 { typedef void (O::*M)(A1, A2, A3, A4, A5) const; XorpConstMemberCallback5B0(const char* file, int line, O* o, M m) : XorpCallback5(file, line), _o(o), _m(m) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for const safe member methods with 5 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback5B0 : public XorpConstMemberCallback5B0, public SafeCallbackBase { typedef typename XorpConstMemberCallback5B0::M M; XorpConstSafeMemberCallback5B0(const char* file, int line, O* o, M m) : XorpConstMemberCallback5B0(file, line, o, m), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback5B0() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback5B0::dispatch(a1, a2, a3, a4, a5); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 5 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback5B0 : public XorpConstMemberCallback5B0, public SafeCallbackBase { typedef typename XorpConstMemberCallback5B0::M M; XorpConstSafeMemberCallback5B0(const char* file, int line, O* o, M m) : XorpConstMemberCallback5B0(file, line, o, m), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback5B0() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback5B0::dispatch(a1, a2, a3, a4, a5); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory5B0 { static XorpConstMemberCallback5B0* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5) const) { return new XorpConstSafeMemberCallback5B0(file, line, o, p); } }; template struct XorpConstMemberCallbackFactory5B0 { static XorpConstMemberCallback5B0* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5) const) { return new XorpConstMemberCallback5B0(file, line, o, p); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 5 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback5::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5) const) { return XorpConstMemberCallbackFactory5B0::True>::make(file, line, o, p); } /** * Factory function that creates a callback object targetted at a * const member function with 5 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback5::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5) const) { return XorpConstMemberCallbackFactory5B0::True>::make(file, line, &o, p); } /** * @short Callback object for functions with 5 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpFunctionCallback5B1 : public XorpCallback5 { typedef R (*F)(A1, A2, A3, A4, A5, BA1); XorpFunctionCallback5B1(const char* file, int line, F f, BA1 ba1) : XorpCallback5(file, line), _f(f), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, _ba1); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; }; /** * @short Callback object for void functions with 5 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpFunctionCallback5B1 : public XorpCallback5 { typedef void (*F)(A1, A2, A3, A4, A5, BA1); XorpFunctionCallback5B1(const char* file, int line, F f, BA1 ba1) : XorpCallback5(file, line), _f(f), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, _ba1); record_dispatch_leave(); } protected: F _f; BA1 _ba1; }; /** * Factory function that creates a callback object targetted at a * function with 5 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback5::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, BA1), BA1 ba1) { return typename XorpCallback5::RefPtr(new XorpFunctionCallback5B1(file, line, f, ba1)); } /** * @short Callback object for member methods with 5 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpMemberCallback5B1 : public XorpCallback5 { typedef R (O::*M)(A1, A2, A3, A4, A5, BA1) ; XorpMemberCallback5B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpCallback5(file, line), _o(o), _m(m), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, _ba1); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for void member methods with 5 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpMemberCallback5B1 : public XorpCallback5 { typedef void (O::*M)(A1, A2, A3, A4, A5, BA1) ; XorpMemberCallback5B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpCallback5(file, line), _o(o), _m(m), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, _ba1); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for safe member methods with 5 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpSafeMemberCallback5B1 : public XorpMemberCallback5B1, public SafeCallbackBase { typedef typename XorpMemberCallback5B1::M M; XorpSafeMemberCallback5B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpMemberCallback5B1(file, line, o, m, ba1), SafeCallbackBase(o) {} ~XorpSafeMemberCallback5B1() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback5B1::dispatch(a1, a2, a3, a4, a5); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 5 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpSafeMemberCallback5B1 : public XorpMemberCallback5B1, public SafeCallbackBase { typedef typename XorpMemberCallback5B1::M M; XorpSafeMemberCallback5B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpMemberCallback5B1(file, line, o, m, ba1), SafeCallbackBase(o) {} ~XorpSafeMemberCallback5B1() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { if (valid()) { record_dispatch_enter(); XorpMemberCallback5B1::dispatch(a1, a2, a3, a4, a5); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory5B1 { static XorpMemberCallback5B1* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1), BA1 ba1) { return new XorpSafeMemberCallback5B1(file, line, o, p, ba1); } }; template struct XorpMemberCallbackFactory5B1 { static XorpMemberCallback5B1* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1), BA1 ba1) { return new XorpMemberCallback5B1(file, line, o, p, ba1); }; }; /** * Factory function that creates a callback object targetted at a * member function with 5 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback5::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1), BA1 ba1) { return XorpMemberCallbackFactory5B1::True>::make(file, line, o, p, ba1); } /** * Factory function that creates a callback object targetted at a * member function with 5 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback5::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, BA1), BA1 ba1) { return XorpMemberCallbackFactory5B1::True>::make(file, line, &o, p, ba1); } /** * @short Callback object for const member methods with 5 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstMemberCallback5B1 : public XorpCallback5 { typedef R (O::*M)(A1, A2, A3, A4, A5, BA1) const; XorpConstMemberCallback5B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpCallback5(file, line), _o(o), _m(m), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, _ba1); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for void const member methods with 5 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstMemberCallback5B1 : public XorpCallback5 { typedef void (O::*M)(A1, A2, A3, A4, A5, BA1) const; XorpConstMemberCallback5B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpCallback5(file, line), _o(o), _m(m), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, _ba1); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for const safe member methods with 5 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback5B1 : public XorpConstMemberCallback5B1, public SafeCallbackBase { typedef typename XorpConstMemberCallback5B1::M M; XorpConstSafeMemberCallback5B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpConstMemberCallback5B1(file, line, o, m, ba1), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback5B1() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback5B1::dispatch(a1, a2, a3, a4, a5); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 5 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback5B1 : public XorpConstMemberCallback5B1, public SafeCallbackBase { typedef typename XorpConstMemberCallback5B1::M M; XorpConstSafeMemberCallback5B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpConstMemberCallback5B1(file, line, o, m, ba1), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback5B1() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback5B1::dispatch(a1, a2, a3, a4, a5); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory5B1 { static XorpConstMemberCallback5B1* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1) const, BA1 ba1) { return new XorpConstSafeMemberCallback5B1(file, line, o, p, ba1); } }; template struct XorpConstMemberCallbackFactory5B1 { static XorpConstMemberCallback5B1* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1) const, BA1 ba1) { return new XorpConstMemberCallback5B1(file, line, o, p, ba1); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 5 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback5::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1) const, BA1 ba1) { return XorpConstMemberCallbackFactory5B1::True>::make(file, line, o, p, ba1); } /** * Factory function that creates a callback object targetted at a * const member function with 5 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback5::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, BA1) const, BA1 ba1) { return XorpConstMemberCallbackFactory5B1::True>::make(file, line, &o, p, ba1); } /** * @short Callback object for functions with 5 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpFunctionCallback5B2 : public XorpCallback5 { typedef R (*F)(A1, A2, A3, A4, A5, BA1, BA2); XorpFunctionCallback5B2(const char* file, int line, F f, BA1 ba1, BA2 ba2) : XorpCallback5(file, line), _f(f), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, _ba1, _ba2); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; }; /** * @short Callback object for void functions with 5 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpFunctionCallback5B2 : public XorpCallback5 { typedef void (*F)(A1, A2, A3, A4, A5, BA1, BA2); XorpFunctionCallback5B2(const char* file, int line, F f, BA1 ba1, BA2 ba2) : XorpCallback5(file, line), _f(f), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, _ba1, _ba2); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; }; /** * Factory function that creates a callback object targetted at a * function with 5 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback5::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, BA1, BA2), BA1 ba1, BA2 ba2) { return typename XorpCallback5::RefPtr(new XorpFunctionCallback5B2(file, line, f, ba1, ba2)); } /** * @short Callback object for member methods with 5 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpMemberCallback5B2 : public XorpCallback5 { typedef R (O::*M)(A1, A2, A3, A4, A5, BA1, BA2) ; XorpMemberCallback5B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback5(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, _ba1, _ba2); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for void member methods with 5 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpMemberCallback5B2 : public XorpCallback5 { typedef void (O::*M)(A1, A2, A3, A4, A5, BA1, BA2) ; XorpMemberCallback5B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback5(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, _ba1, _ba2); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for safe member methods with 5 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpSafeMemberCallback5B2 : public XorpMemberCallback5B2, public SafeCallbackBase { typedef typename XorpMemberCallback5B2::M M; XorpSafeMemberCallback5B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpMemberCallback5B2(file, line, o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpSafeMemberCallback5B2() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback5B2::dispatch(a1, a2, a3, a4, a5); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 5 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpSafeMemberCallback5B2 : public XorpMemberCallback5B2, public SafeCallbackBase { typedef typename XorpMemberCallback5B2::M M; XorpSafeMemberCallback5B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpMemberCallback5B2(file, line, o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpSafeMemberCallback5B2() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { if (valid()) { record_dispatch_enter(); XorpMemberCallback5B2::dispatch(a1, a2, a3, a4, a5); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory5B2 { static XorpMemberCallback5B2* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2), BA1 ba1, BA2 ba2) { return new XorpSafeMemberCallback5B2(file, line, o, p, ba1, ba2); } }; template struct XorpMemberCallbackFactory5B2 { static XorpMemberCallback5B2* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2), BA1 ba1, BA2 ba2) { return new XorpMemberCallback5B2(file, line, o, p, ba1, ba2); }; }; /** * Factory function that creates a callback object targetted at a * member function with 5 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback5::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2), BA1 ba1, BA2 ba2) { return XorpMemberCallbackFactory5B2::True>::make(file, line, o, p, ba1, ba2); } /** * Factory function that creates a callback object targetted at a * member function with 5 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback5::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2), BA1 ba1, BA2 ba2) { return XorpMemberCallbackFactory5B2::True>::make(file, line, &o, p, ba1, ba2); } /** * @short Callback object for const member methods with 5 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstMemberCallback5B2 : public XorpCallback5 { typedef R (O::*M)(A1, A2, A3, A4, A5, BA1, BA2) const; XorpConstMemberCallback5B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback5(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, _ba1, _ba2); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for void const member methods with 5 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstMemberCallback5B2 : public XorpCallback5 { typedef void (O::*M)(A1, A2, A3, A4, A5, BA1, BA2) const; XorpConstMemberCallback5B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback5(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, _ba1, _ba2); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for const safe member methods with 5 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback5B2 : public XorpConstMemberCallback5B2, public SafeCallbackBase { typedef typename XorpConstMemberCallback5B2::M M; XorpConstSafeMemberCallback5B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpConstMemberCallback5B2(file, line, o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback5B2() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback5B2::dispatch(a1, a2, a3, a4, a5); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 5 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback5B2 : public XorpConstMemberCallback5B2, public SafeCallbackBase { typedef typename XorpConstMemberCallback5B2::M M; XorpConstSafeMemberCallback5B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpConstMemberCallback5B2(file, line, o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback5B2() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback5B2::dispatch(a1, a2, a3, a4, a5); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory5B2 { static XorpConstMemberCallback5B2* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2) const, BA1 ba1, BA2 ba2) { return new XorpConstSafeMemberCallback5B2(file, line, o, p, ba1, ba2); } }; template struct XorpConstMemberCallbackFactory5B2 { static XorpConstMemberCallback5B2* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2) const, BA1 ba1, BA2 ba2) { return new XorpConstMemberCallback5B2(file, line, o, p, ba1, ba2); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 5 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback5::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2) const, BA1 ba1, BA2 ba2) { return XorpConstMemberCallbackFactory5B2::True>::make(file, line, o, p, ba1, ba2); } /** * Factory function that creates a callback object targetted at a * const member function with 5 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback5::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2) const, BA1 ba1, BA2 ba2) { return XorpConstMemberCallbackFactory5B2::True>::make(file, line, &o, p, ba1, ba2); } /** * @short Callback object for functions with 5 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpFunctionCallback5B3 : public XorpCallback5 { typedef R (*F)(A1, A2, A3, A4, A5, BA1, BA2, BA3); XorpFunctionCallback5B3(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback5(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, _ba1, _ba2, _ba3); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; }; /** * @short Callback object for void functions with 5 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpFunctionCallback5B3 : public XorpCallback5 { typedef void (*F)(A1, A2, A3, A4, A5, BA1, BA2, BA3); XorpFunctionCallback5B3(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback5(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, _ba1, _ba2, _ba3); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; }; /** * Factory function that creates a callback object targetted at a * function with 5 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback5::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return typename XorpCallback5::RefPtr(new XorpFunctionCallback5B3(file, line, f, ba1, ba2, ba3)); } /** * @short Callback object for member methods with 5 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpMemberCallback5B3 : public XorpCallback5 { typedef R (O::*M)(A1, A2, A3, A4, A5, BA1, BA2, BA3) ; XorpMemberCallback5B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback5(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, _ba1, _ba2, _ba3); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for void member methods with 5 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpMemberCallback5B3 : public XorpCallback5 { typedef void (O::*M)(A1, A2, A3, A4, A5, BA1, BA2, BA3) ; XorpMemberCallback5B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback5(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, _ba1, _ba2, _ba3); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for safe member methods with 5 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpSafeMemberCallback5B3 : public XorpMemberCallback5B3, public SafeCallbackBase { typedef typename XorpMemberCallback5B3::M M; XorpSafeMemberCallback5B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpMemberCallback5B3(file, line, o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpSafeMemberCallback5B3() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback5B3::dispatch(a1, a2, a3, a4, a5); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 5 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpSafeMemberCallback5B3 : public XorpMemberCallback5B3, public SafeCallbackBase { typedef typename XorpMemberCallback5B3::M M; XorpSafeMemberCallback5B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpMemberCallback5B3(file, line, o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpSafeMemberCallback5B3() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { if (valid()) { record_dispatch_enter(); XorpMemberCallback5B3::dispatch(a1, a2, a3, a4, a5); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory5B3 { static XorpMemberCallback5B3* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpSafeMemberCallback5B3(file, line, o, p, ba1, ba2, ba3); } }; template struct XorpMemberCallbackFactory5B3 { static XorpMemberCallback5B3* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpMemberCallback5B3(file, line, o, p, ba1, ba2, ba3); }; }; /** * Factory function that creates a callback object targetted at a * member function with 5 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback5::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return XorpMemberCallbackFactory5B3::True>::make(file, line, o, p, ba1, ba2, ba3); } /** * Factory function that creates a callback object targetted at a * member function with 5 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback5::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return XorpMemberCallbackFactory5B3::True>::make(file, line, &o, p, ba1, ba2, ba3); } /** * @short Callback object for const member methods with 5 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstMemberCallback5B3 : public XorpCallback5 { typedef R (O::*M)(A1, A2, A3, A4, A5, BA1, BA2, BA3) const; XorpConstMemberCallback5B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback5(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, _ba1, _ba2, _ba3); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for void const member methods with 5 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstMemberCallback5B3 : public XorpCallback5 { typedef void (O::*M)(A1, A2, A3, A4, A5, BA1, BA2, BA3) const; XorpConstMemberCallback5B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback5(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, _ba1, _ba2, _ba3); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for const safe member methods with 5 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback5B3 : public XorpConstMemberCallback5B3, public SafeCallbackBase { typedef typename XorpConstMemberCallback5B3::M M; XorpConstSafeMemberCallback5B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpConstMemberCallback5B3(file, line, o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback5B3() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback5B3::dispatch(a1, a2, a3, a4, a5); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 5 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback5B3 : public XorpConstMemberCallback5B3, public SafeCallbackBase { typedef typename XorpConstMemberCallback5B3::M M; XorpConstSafeMemberCallback5B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpConstMemberCallback5B3(file, line, o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback5B3() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback5B3::dispatch(a1, a2, a3, a4, a5); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory5B3 { static XorpConstMemberCallback5B3* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpConstSafeMemberCallback5B3(file, line, o, p, ba1, ba2, ba3); } }; template struct XorpConstMemberCallbackFactory5B3 { static XorpConstMemberCallback5B3* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpConstMemberCallback5B3(file, line, o, p, ba1, ba2, ba3); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 5 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback5::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return XorpConstMemberCallbackFactory5B3::True>::make(file, line, o, p, ba1, ba2, ba3); } /** * Factory function that creates a callback object targetted at a * const member function with 5 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback5::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return XorpConstMemberCallbackFactory5B3::True>::make(file, line, &o, p, ba1, ba2, ba3); } /** * @short Callback object for functions with 5 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpFunctionCallback5B4 : public XorpCallback5 { typedef R (*F)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4); XorpFunctionCallback5B4(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback5(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; }; /** * @short Callback object for void functions with 5 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpFunctionCallback5B4 : public XorpCallback5 { typedef void (*F)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4); XorpFunctionCallback5B4(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback5(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; }; /** * Factory function that creates a callback object targetted at a * function with 5 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback5::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return typename XorpCallback5::RefPtr(new XorpFunctionCallback5B4(file, line, f, ba1, ba2, ba3, ba4)); } /** * @short Callback object for member methods with 5 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpMemberCallback5B4 : public XorpCallback5 { typedef R (O::*M)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4) ; XorpMemberCallback5B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback5(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for void member methods with 5 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpMemberCallback5B4 : public XorpCallback5 { typedef void (O::*M)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4) ; XorpMemberCallback5B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback5(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for safe member methods with 5 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpSafeMemberCallback5B4 : public XorpMemberCallback5B4, public SafeCallbackBase { typedef typename XorpMemberCallback5B4::M M; XorpSafeMemberCallback5B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpMemberCallback5B4(file, line, o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpSafeMemberCallback5B4() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback5B4::dispatch(a1, a2, a3, a4, a5); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 5 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpSafeMemberCallback5B4 : public XorpMemberCallback5B4, public SafeCallbackBase { typedef typename XorpMemberCallback5B4::M M; XorpSafeMemberCallback5B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpMemberCallback5B4(file, line, o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpSafeMemberCallback5B4() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { if (valid()) { record_dispatch_enter(); XorpMemberCallback5B4::dispatch(a1, a2, a3, a4, a5); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory5B4 { static XorpMemberCallback5B4* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpSafeMemberCallback5B4(file, line, o, p, ba1, ba2, ba3, ba4); } }; template struct XorpMemberCallbackFactory5B4 { static XorpMemberCallback5B4* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpMemberCallback5B4(file, line, o, p, ba1, ba2, ba3, ba4); }; }; /** * Factory function that creates a callback object targetted at a * member function with 5 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback5::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpMemberCallbackFactory5B4::True>::make(file, line, o, p, ba1, ba2, ba3, ba4); } /** * Factory function that creates a callback object targetted at a * member function with 5 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback5::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpMemberCallbackFactory5B4::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4); } /** * @short Callback object for const member methods with 5 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstMemberCallback5B4 : public XorpCallback5 { typedef R (O::*M)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4) const; XorpConstMemberCallback5B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback5(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for void const member methods with 5 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstMemberCallback5B4 : public XorpCallback5 { typedef void (O::*M)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4) const; XorpConstMemberCallback5B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback5(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for const safe member methods with 5 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback5B4 : public XorpConstMemberCallback5B4, public SafeCallbackBase { typedef typename XorpConstMemberCallback5B4::M M; XorpConstSafeMemberCallback5B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpConstMemberCallback5B4(file, line, o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback5B4() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback5B4::dispatch(a1, a2, a3, a4, a5); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 5 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback5B4 : public XorpConstMemberCallback5B4, public SafeCallbackBase { typedef typename XorpConstMemberCallback5B4::M M; XorpConstSafeMemberCallback5B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpConstMemberCallback5B4(file, line, o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback5B4() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback5B4::dispatch(a1, a2, a3, a4, a5); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory5B4 { static XorpConstMemberCallback5B4* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpConstSafeMemberCallback5B4(file, line, o, p, ba1, ba2, ba3, ba4); } }; template struct XorpConstMemberCallbackFactory5B4 { static XorpConstMemberCallback5B4* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpConstMemberCallback5B4(file, line, o, p, ba1, ba2, ba3, ba4); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 5 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback5::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpConstMemberCallbackFactory5B4::True>::make(file, line, o, p, ba1, ba2, ba3, ba4); } /** * Factory function that creates a callback object targetted at a * const member function with 5 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback5::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpConstMemberCallbackFactory5B4::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4); } /** * @short Callback object for functions with 5 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpFunctionCallback5B5 : public XorpCallback5 { typedef R (*F)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4, BA5); XorpFunctionCallback5B5(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback5(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; }; /** * @short Callback object for void functions with 5 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpFunctionCallback5B5 : public XorpCallback5 { typedef void (*F)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4, BA5); XorpFunctionCallback5B5(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback5(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; }; /** * Factory function that creates a callback object targetted at a * function with 5 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback5::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return typename XorpCallback5::RefPtr(new XorpFunctionCallback5B5(file, line, f, ba1, ba2, ba3, ba4, ba5)); } /** * @short Callback object for member methods with 5 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpMemberCallback5B5 : public XorpCallback5 { typedef R (O::*M)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4, BA5) ; XorpMemberCallback5B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback5(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for void member methods with 5 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpMemberCallback5B5 : public XorpCallback5 { typedef void (O::*M)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4, BA5) ; XorpMemberCallback5B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback5(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for safe member methods with 5 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpSafeMemberCallback5B5 : public XorpMemberCallback5B5, public SafeCallbackBase { typedef typename XorpMemberCallback5B5::M M; XorpSafeMemberCallback5B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpMemberCallback5B5(file, line, o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpSafeMemberCallback5B5() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback5B5::dispatch(a1, a2, a3, a4, a5); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 5 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpSafeMemberCallback5B5 : public XorpMemberCallback5B5, public SafeCallbackBase { typedef typename XorpMemberCallback5B5::M M; XorpSafeMemberCallback5B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpMemberCallback5B5(file, line, o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpSafeMemberCallback5B5() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { if (valid()) { record_dispatch_enter(); XorpMemberCallback5B5::dispatch(a1, a2, a3, a4, a5); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory5B5 { static XorpMemberCallback5B5* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpSafeMemberCallback5B5(file, line, o, p, ba1, ba2, ba3, ba4, ba5); } }; template struct XorpMemberCallbackFactory5B5 { static XorpMemberCallback5B5* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpMemberCallback5B5(file, line, o, p, ba1, ba2, ba3, ba4, ba5); }; }; /** * Factory function that creates a callback object targetted at a * member function with 5 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback5::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpMemberCallbackFactory5B5::True>::make(file, line, o, p, ba1, ba2, ba3, ba4, ba5); } /** * Factory function that creates a callback object targetted at a * member function with 5 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback5::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpMemberCallbackFactory5B5::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4, ba5); } /** * @short Callback object for const member methods with 5 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstMemberCallback5B5 : public XorpCallback5 { typedef R (O::*M)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4, BA5) const; XorpConstMemberCallback5B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback5(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for void const member methods with 5 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstMemberCallback5B5 : public XorpCallback5 { typedef void (O::*M)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4, BA5) const; XorpConstMemberCallback5B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback5(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for const safe member methods with 5 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback5B5 : public XorpConstMemberCallback5B5, public SafeCallbackBase { typedef typename XorpConstMemberCallback5B5::M M; XorpConstSafeMemberCallback5B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpConstMemberCallback5B5(file, line, o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback5B5() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback5B5::dispatch(a1, a2, a3, a4, a5); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 5 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback5B5 : public XorpConstMemberCallback5B5, public SafeCallbackBase { typedef typename XorpConstMemberCallback5B5::M M; XorpConstSafeMemberCallback5B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpConstMemberCallback5B5(file, line, o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback5B5() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback5B5::dispatch(a1, a2, a3, a4, a5); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory5B5 { static XorpConstMemberCallback5B5* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpConstSafeMemberCallback5B5(file, line, o, p, ba1, ba2, ba3, ba4, ba5); } }; template struct XorpConstMemberCallbackFactory5B5 { static XorpConstMemberCallback5B5* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpConstMemberCallback5B5(file, line, o, p, ba1, ba2, ba3, ba4, ba5); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 5 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback5::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpConstMemberCallbackFactory5B5::True>::make(file, line, o, p, ba1, ba2, ba3, ba4, ba5); } /** * Factory function that creates a callback object targetted at a * const member function with 5 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback5::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpConstMemberCallbackFactory5B5::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4, ba5); } /** * @short Callback object for functions with 5 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpFunctionCallback5B6 : public XorpCallback5 { typedef R (*F)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4, BA5, BA6); XorpFunctionCallback5B6(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback5(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; BA6 _ba6; }; /** * @short Callback object for void functions with 5 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpFunctionCallback5B6 : public XorpCallback5 { typedef void (*F)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4, BA5, BA6); XorpFunctionCallback5B6(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback5(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; BA6 _ba6; }; /** * Factory function that creates a callback object targetted at a * function with 5 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback5::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return typename XorpCallback5::RefPtr(new XorpFunctionCallback5B6(file, line, f, ba1, ba2, ba3, ba4, ba5, ba6)); } /** * @short Callback object for member methods with 5 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpMemberCallback5B6 : public XorpCallback5 { typedef R (O::*M)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4, BA5, BA6) ; XorpMemberCallback5B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback5(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for void member methods with 5 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpMemberCallback5B6 : public XorpCallback5 { typedef void (O::*M)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4, BA5, BA6) ; XorpMemberCallback5B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback5(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for safe member methods with 5 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpSafeMemberCallback5B6 : public XorpMemberCallback5B6, public SafeCallbackBase { typedef typename XorpMemberCallback5B6::M M; XorpSafeMemberCallback5B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpMemberCallback5B6(file, line, o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpSafeMemberCallback5B6() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback5B6::dispatch(a1, a2, a3, a4, a5); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 5 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpSafeMemberCallback5B6 : public XorpMemberCallback5B6, public SafeCallbackBase { typedef typename XorpMemberCallback5B6::M M; XorpSafeMemberCallback5B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpMemberCallback5B6(file, line, o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpSafeMemberCallback5B6() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { if (valid()) { record_dispatch_enter(); XorpMemberCallback5B6::dispatch(a1, a2, a3, a4, a5); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory5B6 { static XorpMemberCallback5B6* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpSafeMemberCallback5B6(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); } }; template struct XorpMemberCallbackFactory5B6 { static XorpMemberCallback5B6* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpMemberCallback5B6(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); }; }; /** * Factory function that creates a callback object targetted at a * member function with 5 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback5::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpMemberCallbackFactory5B6::True>::make(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * Factory function that creates a callback object targetted at a * member function with 5 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback5::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpMemberCallbackFactory5B6::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * @short Callback object for const member methods with 5 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstMemberCallback5B6 : public XorpCallback5 { typedef R (O::*M)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4, BA5, BA6) const; XorpConstMemberCallback5B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback5(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for void const member methods with 5 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstMemberCallback5B6 : public XorpCallback5 { typedef void (O::*M)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4, BA5, BA6) const; XorpConstMemberCallback5B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback5(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for const safe member methods with 5 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback5B6 : public XorpConstMemberCallback5B6, public SafeCallbackBase { typedef typename XorpConstMemberCallback5B6::M M; XorpConstSafeMemberCallback5B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpConstMemberCallback5B6(file, line, o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback5B6() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback5B6::dispatch(a1, a2, a3, a4, a5); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 5 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback5B6 : public XorpConstMemberCallback5B6, public SafeCallbackBase { typedef typename XorpConstMemberCallback5B6::M M; XorpConstSafeMemberCallback5B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpConstMemberCallback5B6(file, line, o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback5B6() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback5B6::dispatch(a1, a2, a3, a4, a5); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory5B6 { static XorpConstMemberCallback5B6* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpConstSafeMemberCallback5B6(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); } }; template struct XorpConstMemberCallbackFactory5B6 { static XorpConstMemberCallback5B6* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpConstMemberCallback5B6(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 5 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback5::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpConstMemberCallbackFactory5B6::True>::make(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * Factory function that creates a callback object targetted at a * const member function with 5 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback5::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpConstMemberCallbackFactory5B6::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /////////////////////////////////////////////////////////////////////////////// // // Code relating to callbacks with 6 late args // /** * @short Base class for callbacks with 6 dispatch time args. */ template struct XorpCallback6 { typedef ref_ptr RefPtr; XorpCallback6(const char* file, int line) : _file(file), _line(line) {} virtual ~XorpCallback6() {} virtual R dispatch(A1, A2, A3, A4, A5, A6) = 0; const char* file() const { return _file; } int line() const { return _line; } private: const char* _file; int _line; }; /** * @short Callback object for functions with 6 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpFunctionCallback6B0 : public XorpCallback6 { typedef R (*F)(A1, A2, A3, A4, A5, A6); XorpFunctionCallback6B0(const char* file, int line, F f) : XorpCallback6(file, line), _f(f) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6); record_dispatch_leave(); return r; } protected: F _f; }; /** * @short Callback object for void functions with 6 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpFunctionCallback6B0 : public XorpCallback6 { typedef void (*F)(A1, A2, A3, A4, A5, A6); XorpFunctionCallback6B0(const char* file, int line, F f) : XorpCallback6(file, line), _f(f) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6); record_dispatch_leave(); } protected: F _f; }; /** * Factory function that creates a callback object targetted at a * function with 6 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback6::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6)) { return typename XorpCallback6::RefPtr(new XorpFunctionCallback6B0(file, line, f)); } /** * @short Callback object for member methods with 6 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpMemberCallback6B0 : public XorpCallback6 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6) ; XorpMemberCallback6B0(const char* file, int line, O* o, M m) : XorpCallback6(file, line), _o(o), _m(m) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for void member methods with 6 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpMemberCallback6B0 : public XorpCallback6 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6) ; XorpMemberCallback6B0(const char* file, int line, O* o, M m) : XorpCallback6(file, line), _o(o), _m(m) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for safe member methods with 6 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpSafeMemberCallback6B0 : public XorpMemberCallback6B0, public SafeCallbackBase { typedef typename XorpMemberCallback6B0::M M; XorpSafeMemberCallback6B0(const char* file, int line, O* o, M m) : XorpMemberCallback6B0(file, line, o, m), SafeCallbackBase(o) {} ~XorpSafeMemberCallback6B0() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback6B0::dispatch(a1, a2, a3, a4, a5, a6); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 6 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpSafeMemberCallback6B0 : public XorpMemberCallback6B0, public SafeCallbackBase { typedef typename XorpMemberCallback6B0::M M; XorpSafeMemberCallback6B0(const char* file, int line, O* o, M m) : XorpMemberCallback6B0(file, line, o, m), SafeCallbackBase(o) {} ~XorpSafeMemberCallback6B0() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { if (valid()) { record_dispatch_enter(); XorpMemberCallback6B0::dispatch(a1, a2, a3, a4, a5, a6); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory6B0 { static XorpMemberCallback6B0* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6)) { return new XorpSafeMemberCallback6B0(file, line, o, p); } }; template struct XorpMemberCallbackFactory6B0 { static XorpMemberCallback6B0* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6)) { return new XorpMemberCallback6B0(file, line, o, p); }; }; /** * Factory function that creates a callback object targetted at a * member function with 6 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback6::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6)) { return XorpMemberCallbackFactory6B0::True>::make(file, line, o, p); } /** * Factory function that creates a callback object targetted at a * member function with 6 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback6::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6)) { return XorpMemberCallbackFactory6B0::True>::make(file, line, &o, p); } /** * @short Callback object for const member methods with 6 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstMemberCallback6B0 : public XorpCallback6 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6) const; XorpConstMemberCallback6B0(const char* file, int line, O* o, M m) : XorpCallback6(file, line), _o(o), _m(m) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for void const member methods with 6 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstMemberCallback6B0 : public XorpCallback6 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6) const; XorpConstMemberCallback6B0(const char* file, int line, O* o, M m) : XorpCallback6(file, line), _o(o), _m(m) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for const safe member methods with 6 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback6B0 : public XorpConstMemberCallback6B0, public SafeCallbackBase { typedef typename XorpConstMemberCallback6B0::M M; XorpConstSafeMemberCallback6B0(const char* file, int line, O* o, M m) : XorpConstMemberCallback6B0(file, line, o, m), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback6B0() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback6B0::dispatch(a1, a2, a3, a4, a5, a6); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 6 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback6B0 : public XorpConstMemberCallback6B0, public SafeCallbackBase { typedef typename XorpConstMemberCallback6B0::M M; XorpConstSafeMemberCallback6B0(const char* file, int line, O* o, M m) : XorpConstMemberCallback6B0(file, line, o, m), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback6B0() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback6B0::dispatch(a1, a2, a3, a4, a5, a6); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory6B0 { static XorpConstMemberCallback6B0* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6) const) { return new XorpConstSafeMemberCallback6B0(file, line, o, p); } }; template struct XorpConstMemberCallbackFactory6B0 { static XorpConstMemberCallback6B0* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6) const) { return new XorpConstMemberCallback6B0(file, line, o, p); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 6 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback6::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6) const) { return XorpConstMemberCallbackFactory6B0::True>::make(file, line, o, p); } /** * Factory function that creates a callback object targetted at a * const member function with 6 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback6::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6) const) { return XorpConstMemberCallbackFactory6B0::True>::make(file, line, &o, p); } /** * @short Callback object for functions with 6 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpFunctionCallback6B1 : public XorpCallback6 { typedef R (*F)(A1, A2, A3, A4, A5, A6, BA1); XorpFunctionCallback6B1(const char* file, int line, F f, BA1 ba1) : XorpCallback6(file, line), _f(f), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, _ba1); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; }; /** * @short Callback object for void functions with 6 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpFunctionCallback6B1 : public XorpCallback6 { typedef void (*F)(A1, A2, A3, A4, A5, A6, BA1); XorpFunctionCallback6B1(const char* file, int line, F f, BA1 ba1) : XorpCallback6(file, line), _f(f), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, _ba1); record_dispatch_leave(); } protected: F _f; BA1 _ba1; }; /** * Factory function that creates a callback object targetted at a * function with 6 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback6::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, BA1), BA1 ba1) { return typename XorpCallback6::RefPtr(new XorpFunctionCallback6B1(file, line, f, ba1)); } /** * @short Callback object for member methods with 6 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpMemberCallback6B1 : public XorpCallback6 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, BA1) ; XorpMemberCallback6B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpCallback6(file, line), _o(o), _m(m), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, _ba1); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for void member methods with 6 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpMemberCallback6B1 : public XorpCallback6 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, BA1) ; XorpMemberCallback6B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpCallback6(file, line), _o(o), _m(m), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, _ba1); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for safe member methods with 6 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpSafeMemberCallback6B1 : public XorpMemberCallback6B1, public SafeCallbackBase { typedef typename XorpMemberCallback6B1::M M; XorpSafeMemberCallback6B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpMemberCallback6B1(file, line, o, m, ba1), SafeCallbackBase(o) {} ~XorpSafeMemberCallback6B1() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback6B1::dispatch(a1, a2, a3, a4, a5, a6); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 6 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpSafeMemberCallback6B1 : public XorpMemberCallback6B1, public SafeCallbackBase { typedef typename XorpMemberCallback6B1::M M; XorpSafeMemberCallback6B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpMemberCallback6B1(file, line, o, m, ba1), SafeCallbackBase(o) {} ~XorpSafeMemberCallback6B1() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { if (valid()) { record_dispatch_enter(); XorpMemberCallback6B1::dispatch(a1, a2, a3, a4, a5, a6); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory6B1 { static XorpMemberCallback6B1* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1), BA1 ba1) { return new XorpSafeMemberCallback6B1(file, line, o, p, ba1); } }; template struct XorpMemberCallbackFactory6B1 { static XorpMemberCallback6B1* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1), BA1 ba1) { return new XorpMemberCallback6B1(file, line, o, p, ba1); }; }; /** * Factory function that creates a callback object targetted at a * member function with 6 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback6::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1), BA1 ba1) { return XorpMemberCallbackFactory6B1::True>::make(file, line, o, p, ba1); } /** * Factory function that creates a callback object targetted at a * member function with 6 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback6::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1), BA1 ba1) { return XorpMemberCallbackFactory6B1::True>::make(file, line, &o, p, ba1); } /** * @short Callback object for const member methods with 6 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstMemberCallback6B1 : public XorpCallback6 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, BA1) const; XorpConstMemberCallback6B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpCallback6(file, line), _o(o), _m(m), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, _ba1); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for void const member methods with 6 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstMemberCallback6B1 : public XorpCallback6 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, BA1) const; XorpConstMemberCallback6B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpCallback6(file, line), _o(o), _m(m), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, _ba1); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for const safe member methods with 6 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback6B1 : public XorpConstMemberCallback6B1, public SafeCallbackBase { typedef typename XorpConstMemberCallback6B1::M M; XorpConstSafeMemberCallback6B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpConstMemberCallback6B1(file, line, o, m, ba1), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback6B1() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback6B1::dispatch(a1, a2, a3, a4, a5, a6); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 6 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback6B1 : public XorpConstMemberCallback6B1, public SafeCallbackBase { typedef typename XorpConstMemberCallback6B1::M M; XorpConstSafeMemberCallback6B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpConstMemberCallback6B1(file, line, o, m, ba1), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback6B1() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback6B1::dispatch(a1, a2, a3, a4, a5, a6); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory6B1 { static XorpConstMemberCallback6B1* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1) const, BA1 ba1) { return new XorpConstSafeMemberCallback6B1(file, line, o, p, ba1); } }; template struct XorpConstMemberCallbackFactory6B1 { static XorpConstMemberCallback6B1* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1) const, BA1 ba1) { return new XorpConstMemberCallback6B1(file, line, o, p, ba1); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 6 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback6::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1) const, BA1 ba1) { return XorpConstMemberCallbackFactory6B1::True>::make(file, line, o, p, ba1); } /** * Factory function that creates a callback object targetted at a * const member function with 6 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback6::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1) const, BA1 ba1) { return XorpConstMemberCallbackFactory6B1::True>::make(file, line, &o, p, ba1); } /** * @short Callback object for functions with 6 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpFunctionCallback6B2 : public XorpCallback6 { typedef R (*F)(A1, A2, A3, A4, A5, A6, BA1, BA2); XorpFunctionCallback6B2(const char* file, int line, F f, BA1 ba1, BA2 ba2) : XorpCallback6(file, line), _f(f), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, _ba1, _ba2); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; }; /** * @short Callback object for void functions with 6 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpFunctionCallback6B2 : public XorpCallback6 { typedef void (*F)(A1, A2, A3, A4, A5, A6, BA1, BA2); XorpFunctionCallback6B2(const char* file, int line, F f, BA1 ba1, BA2 ba2) : XorpCallback6(file, line), _f(f), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, _ba1, _ba2); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; }; /** * Factory function that creates a callback object targetted at a * function with 6 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback6::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, BA1, BA2), BA1 ba1, BA2 ba2) { return typename XorpCallback6::RefPtr(new XorpFunctionCallback6B2(file, line, f, ba1, ba2)); } /** * @short Callback object for member methods with 6 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpMemberCallback6B2 : public XorpCallback6 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, BA1, BA2) ; XorpMemberCallback6B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback6(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, _ba1, _ba2); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for void member methods with 6 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpMemberCallback6B2 : public XorpCallback6 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, BA1, BA2) ; XorpMemberCallback6B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback6(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, _ba1, _ba2); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for safe member methods with 6 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpSafeMemberCallback6B2 : public XorpMemberCallback6B2, public SafeCallbackBase { typedef typename XorpMemberCallback6B2::M M; XorpSafeMemberCallback6B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpMemberCallback6B2(file, line, o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpSafeMemberCallback6B2() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback6B2::dispatch(a1, a2, a3, a4, a5, a6); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 6 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpSafeMemberCallback6B2 : public XorpMemberCallback6B2, public SafeCallbackBase { typedef typename XorpMemberCallback6B2::M M; XorpSafeMemberCallback6B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpMemberCallback6B2(file, line, o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpSafeMemberCallback6B2() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { if (valid()) { record_dispatch_enter(); XorpMemberCallback6B2::dispatch(a1, a2, a3, a4, a5, a6); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory6B2 { static XorpMemberCallback6B2* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2), BA1 ba1, BA2 ba2) { return new XorpSafeMemberCallback6B2(file, line, o, p, ba1, ba2); } }; template struct XorpMemberCallbackFactory6B2 { static XorpMemberCallback6B2* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2), BA1 ba1, BA2 ba2) { return new XorpMemberCallback6B2(file, line, o, p, ba1, ba2); }; }; /** * Factory function that creates a callback object targetted at a * member function with 6 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback6::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2), BA1 ba1, BA2 ba2) { return XorpMemberCallbackFactory6B2::True>::make(file, line, o, p, ba1, ba2); } /** * Factory function that creates a callback object targetted at a * member function with 6 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback6::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2), BA1 ba1, BA2 ba2) { return XorpMemberCallbackFactory6B2::True>::make(file, line, &o, p, ba1, ba2); } /** * @short Callback object for const member methods with 6 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstMemberCallback6B2 : public XorpCallback6 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, BA1, BA2) const; XorpConstMemberCallback6B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback6(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, _ba1, _ba2); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for void const member methods with 6 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstMemberCallback6B2 : public XorpCallback6 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, BA1, BA2) const; XorpConstMemberCallback6B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback6(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, _ba1, _ba2); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for const safe member methods with 6 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback6B2 : public XorpConstMemberCallback6B2, public SafeCallbackBase { typedef typename XorpConstMemberCallback6B2::M M; XorpConstSafeMemberCallback6B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpConstMemberCallback6B2(file, line, o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback6B2() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback6B2::dispatch(a1, a2, a3, a4, a5, a6); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 6 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback6B2 : public XorpConstMemberCallback6B2, public SafeCallbackBase { typedef typename XorpConstMemberCallback6B2::M M; XorpConstSafeMemberCallback6B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpConstMemberCallback6B2(file, line, o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback6B2() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback6B2::dispatch(a1, a2, a3, a4, a5, a6); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory6B2 { static XorpConstMemberCallback6B2* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2) const, BA1 ba1, BA2 ba2) { return new XorpConstSafeMemberCallback6B2(file, line, o, p, ba1, ba2); } }; template struct XorpConstMemberCallbackFactory6B2 { static XorpConstMemberCallback6B2* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2) const, BA1 ba1, BA2 ba2) { return new XorpConstMemberCallback6B2(file, line, o, p, ba1, ba2); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 6 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback6::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2) const, BA1 ba1, BA2 ba2) { return XorpConstMemberCallbackFactory6B2::True>::make(file, line, o, p, ba1, ba2); } /** * Factory function that creates a callback object targetted at a * const member function with 6 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback6::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2) const, BA1 ba1, BA2 ba2) { return XorpConstMemberCallbackFactory6B2::True>::make(file, line, &o, p, ba1, ba2); } /** * @short Callback object for functions with 6 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpFunctionCallback6B3 : public XorpCallback6 { typedef R (*F)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3); XorpFunctionCallback6B3(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback6(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, _ba1, _ba2, _ba3); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; }; /** * @short Callback object for void functions with 6 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpFunctionCallback6B3 : public XorpCallback6 { typedef void (*F)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3); XorpFunctionCallback6B3(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback6(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, _ba1, _ba2, _ba3); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; }; /** * Factory function that creates a callback object targetted at a * function with 6 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback6::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return typename XorpCallback6::RefPtr(new XorpFunctionCallback6B3(file, line, f, ba1, ba2, ba3)); } /** * @short Callback object for member methods with 6 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpMemberCallback6B3 : public XorpCallback6 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3) ; XorpMemberCallback6B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback6(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, _ba1, _ba2, _ba3); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for void member methods with 6 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpMemberCallback6B3 : public XorpCallback6 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3) ; XorpMemberCallback6B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback6(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, _ba1, _ba2, _ba3); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for safe member methods with 6 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpSafeMemberCallback6B3 : public XorpMemberCallback6B3, public SafeCallbackBase { typedef typename XorpMemberCallback6B3::M M; XorpSafeMemberCallback6B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpMemberCallback6B3(file, line, o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpSafeMemberCallback6B3() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback6B3::dispatch(a1, a2, a3, a4, a5, a6); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 6 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpSafeMemberCallback6B3 : public XorpMemberCallback6B3, public SafeCallbackBase { typedef typename XorpMemberCallback6B3::M M; XorpSafeMemberCallback6B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpMemberCallback6B3(file, line, o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpSafeMemberCallback6B3() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { if (valid()) { record_dispatch_enter(); XorpMemberCallback6B3::dispatch(a1, a2, a3, a4, a5, a6); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory6B3 { static XorpMemberCallback6B3* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpSafeMemberCallback6B3(file, line, o, p, ba1, ba2, ba3); } }; template struct XorpMemberCallbackFactory6B3 { static XorpMemberCallback6B3* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpMemberCallback6B3(file, line, o, p, ba1, ba2, ba3); }; }; /** * Factory function that creates a callback object targetted at a * member function with 6 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback6::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return XorpMemberCallbackFactory6B3::True>::make(file, line, o, p, ba1, ba2, ba3); } /** * Factory function that creates a callback object targetted at a * member function with 6 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback6::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return XorpMemberCallbackFactory6B3::True>::make(file, line, &o, p, ba1, ba2, ba3); } /** * @short Callback object for const member methods with 6 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstMemberCallback6B3 : public XorpCallback6 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3) const; XorpConstMemberCallback6B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback6(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, _ba1, _ba2, _ba3); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for void const member methods with 6 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstMemberCallback6B3 : public XorpCallback6 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3) const; XorpConstMemberCallback6B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback6(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, _ba1, _ba2, _ba3); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for const safe member methods with 6 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback6B3 : public XorpConstMemberCallback6B3, public SafeCallbackBase { typedef typename XorpConstMemberCallback6B3::M M; XorpConstSafeMemberCallback6B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpConstMemberCallback6B3(file, line, o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback6B3() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback6B3::dispatch(a1, a2, a3, a4, a5, a6); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 6 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback6B3 : public XorpConstMemberCallback6B3, public SafeCallbackBase { typedef typename XorpConstMemberCallback6B3::M M; XorpConstSafeMemberCallback6B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpConstMemberCallback6B3(file, line, o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback6B3() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback6B3::dispatch(a1, a2, a3, a4, a5, a6); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory6B3 { static XorpConstMemberCallback6B3* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpConstSafeMemberCallback6B3(file, line, o, p, ba1, ba2, ba3); } }; template struct XorpConstMemberCallbackFactory6B3 { static XorpConstMemberCallback6B3* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpConstMemberCallback6B3(file, line, o, p, ba1, ba2, ba3); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 6 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback6::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return XorpConstMemberCallbackFactory6B3::True>::make(file, line, o, p, ba1, ba2, ba3); } /** * Factory function that creates a callback object targetted at a * const member function with 6 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback6::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return XorpConstMemberCallbackFactory6B3::True>::make(file, line, &o, p, ba1, ba2, ba3); } /** * @short Callback object for functions with 6 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpFunctionCallback6B4 : public XorpCallback6 { typedef R (*F)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4); XorpFunctionCallback6B4(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback6(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; }; /** * @short Callback object for void functions with 6 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpFunctionCallback6B4 : public XorpCallback6 { typedef void (*F)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4); XorpFunctionCallback6B4(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback6(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; }; /** * Factory function that creates a callback object targetted at a * function with 6 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback6::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return typename XorpCallback6::RefPtr(new XorpFunctionCallback6B4(file, line, f, ba1, ba2, ba3, ba4)); } /** * @short Callback object for member methods with 6 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpMemberCallback6B4 : public XorpCallback6 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4) ; XorpMemberCallback6B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback6(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for void member methods with 6 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpMemberCallback6B4 : public XorpCallback6 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4) ; XorpMemberCallback6B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback6(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for safe member methods with 6 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpSafeMemberCallback6B4 : public XorpMemberCallback6B4, public SafeCallbackBase { typedef typename XorpMemberCallback6B4::M M; XorpSafeMemberCallback6B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpMemberCallback6B4(file, line, o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpSafeMemberCallback6B4() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback6B4::dispatch(a1, a2, a3, a4, a5, a6); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 6 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpSafeMemberCallback6B4 : public XorpMemberCallback6B4, public SafeCallbackBase { typedef typename XorpMemberCallback6B4::M M; XorpSafeMemberCallback6B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpMemberCallback6B4(file, line, o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpSafeMemberCallback6B4() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { if (valid()) { record_dispatch_enter(); XorpMemberCallback6B4::dispatch(a1, a2, a3, a4, a5, a6); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory6B4 { static XorpMemberCallback6B4* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpSafeMemberCallback6B4(file, line, o, p, ba1, ba2, ba3, ba4); } }; template struct XorpMemberCallbackFactory6B4 { static XorpMemberCallback6B4* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpMemberCallback6B4(file, line, o, p, ba1, ba2, ba3, ba4); }; }; /** * Factory function that creates a callback object targetted at a * member function with 6 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback6::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpMemberCallbackFactory6B4::True>::make(file, line, o, p, ba1, ba2, ba3, ba4); } /** * Factory function that creates a callback object targetted at a * member function with 6 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback6::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpMemberCallbackFactory6B4::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4); } /** * @short Callback object for const member methods with 6 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstMemberCallback6B4 : public XorpCallback6 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4) const; XorpConstMemberCallback6B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback6(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for void const member methods with 6 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstMemberCallback6B4 : public XorpCallback6 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4) const; XorpConstMemberCallback6B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback6(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for const safe member methods with 6 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback6B4 : public XorpConstMemberCallback6B4, public SafeCallbackBase { typedef typename XorpConstMemberCallback6B4::M M; XorpConstSafeMemberCallback6B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpConstMemberCallback6B4(file, line, o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback6B4() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback6B4::dispatch(a1, a2, a3, a4, a5, a6); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 6 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback6B4 : public XorpConstMemberCallback6B4, public SafeCallbackBase { typedef typename XorpConstMemberCallback6B4::M M; XorpConstSafeMemberCallback6B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpConstMemberCallback6B4(file, line, o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback6B4() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback6B4::dispatch(a1, a2, a3, a4, a5, a6); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory6B4 { static XorpConstMemberCallback6B4* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpConstSafeMemberCallback6B4(file, line, o, p, ba1, ba2, ba3, ba4); } }; template struct XorpConstMemberCallbackFactory6B4 { static XorpConstMemberCallback6B4* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpConstMemberCallback6B4(file, line, o, p, ba1, ba2, ba3, ba4); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 6 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback6::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpConstMemberCallbackFactory6B4::True>::make(file, line, o, p, ba1, ba2, ba3, ba4); } /** * Factory function that creates a callback object targetted at a * const member function with 6 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback6::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpConstMemberCallbackFactory6B4::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4); } /** * @short Callback object for functions with 6 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpFunctionCallback6B5 : public XorpCallback6 { typedef R (*F)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4, BA5); XorpFunctionCallback6B5(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback6(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; }; /** * @short Callback object for void functions with 6 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpFunctionCallback6B5 : public XorpCallback6 { typedef void (*F)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4, BA5); XorpFunctionCallback6B5(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback6(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; }; /** * Factory function that creates a callback object targetted at a * function with 6 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback6::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return typename XorpCallback6::RefPtr(new XorpFunctionCallback6B5(file, line, f, ba1, ba2, ba3, ba4, ba5)); } /** * @short Callback object for member methods with 6 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpMemberCallback6B5 : public XorpCallback6 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4, BA5) ; XorpMemberCallback6B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback6(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for void member methods with 6 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpMemberCallback6B5 : public XorpCallback6 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4, BA5) ; XorpMemberCallback6B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback6(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for safe member methods with 6 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpSafeMemberCallback6B5 : public XorpMemberCallback6B5, public SafeCallbackBase { typedef typename XorpMemberCallback6B5::M M; XorpSafeMemberCallback6B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpMemberCallback6B5(file, line, o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpSafeMemberCallback6B5() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback6B5::dispatch(a1, a2, a3, a4, a5, a6); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 6 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpSafeMemberCallback6B5 : public XorpMemberCallback6B5, public SafeCallbackBase { typedef typename XorpMemberCallback6B5::M M; XorpSafeMemberCallback6B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpMemberCallback6B5(file, line, o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpSafeMemberCallback6B5() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { if (valid()) { record_dispatch_enter(); XorpMemberCallback6B5::dispatch(a1, a2, a3, a4, a5, a6); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory6B5 { static XorpMemberCallback6B5* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpSafeMemberCallback6B5(file, line, o, p, ba1, ba2, ba3, ba4, ba5); } }; template struct XorpMemberCallbackFactory6B5 { static XorpMemberCallback6B5* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpMemberCallback6B5(file, line, o, p, ba1, ba2, ba3, ba4, ba5); }; }; /** * Factory function that creates a callback object targetted at a * member function with 6 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback6::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpMemberCallbackFactory6B5::True>::make(file, line, o, p, ba1, ba2, ba3, ba4, ba5); } /** * Factory function that creates a callback object targetted at a * member function with 6 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback6::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpMemberCallbackFactory6B5::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4, ba5); } /** * @short Callback object for const member methods with 6 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstMemberCallback6B5 : public XorpCallback6 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4, BA5) const; XorpConstMemberCallback6B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback6(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for void const member methods with 6 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstMemberCallback6B5 : public XorpCallback6 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4, BA5) const; XorpConstMemberCallback6B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback6(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for const safe member methods with 6 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback6B5 : public XorpConstMemberCallback6B5, public SafeCallbackBase { typedef typename XorpConstMemberCallback6B5::M M; XorpConstSafeMemberCallback6B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpConstMemberCallback6B5(file, line, o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback6B5() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback6B5::dispatch(a1, a2, a3, a4, a5, a6); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 6 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback6B5 : public XorpConstMemberCallback6B5, public SafeCallbackBase { typedef typename XorpConstMemberCallback6B5::M M; XorpConstSafeMemberCallback6B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpConstMemberCallback6B5(file, line, o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback6B5() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback6B5::dispatch(a1, a2, a3, a4, a5, a6); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory6B5 { static XorpConstMemberCallback6B5* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpConstSafeMemberCallback6B5(file, line, o, p, ba1, ba2, ba3, ba4, ba5); } }; template struct XorpConstMemberCallbackFactory6B5 { static XorpConstMemberCallback6B5* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpConstMemberCallback6B5(file, line, o, p, ba1, ba2, ba3, ba4, ba5); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 6 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback6::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpConstMemberCallbackFactory6B5::True>::make(file, line, o, p, ba1, ba2, ba3, ba4, ba5); } /** * Factory function that creates a callback object targetted at a * const member function with 6 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback6::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpConstMemberCallbackFactory6B5::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4, ba5); } /** * @short Callback object for functions with 6 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpFunctionCallback6B6 : public XorpCallback6 { typedef R (*F)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4, BA5, BA6); XorpFunctionCallback6B6(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback6(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; BA6 _ba6; }; /** * @short Callback object for void functions with 6 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpFunctionCallback6B6 : public XorpCallback6 { typedef void (*F)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4, BA5, BA6); XorpFunctionCallback6B6(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback6(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; BA6 _ba6; }; /** * Factory function that creates a callback object targetted at a * function with 6 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback6::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return typename XorpCallback6::RefPtr(new XorpFunctionCallback6B6(file, line, f, ba1, ba2, ba3, ba4, ba5, ba6)); } /** * @short Callback object for member methods with 6 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpMemberCallback6B6 : public XorpCallback6 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4, BA5, BA6) ; XorpMemberCallback6B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback6(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for void member methods with 6 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpMemberCallback6B6 : public XorpCallback6 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4, BA5, BA6) ; XorpMemberCallback6B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback6(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for safe member methods with 6 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpSafeMemberCallback6B6 : public XorpMemberCallback6B6, public SafeCallbackBase { typedef typename XorpMemberCallback6B6::M M; XorpSafeMemberCallback6B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpMemberCallback6B6(file, line, o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpSafeMemberCallback6B6() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback6B6::dispatch(a1, a2, a3, a4, a5, a6); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 6 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpSafeMemberCallback6B6 : public XorpMemberCallback6B6, public SafeCallbackBase { typedef typename XorpMemberCallback6B6::M M; XorpSafeMemberCallback6B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpMemberCallback6B6(file, line, o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpSafeMemberCallback6B6() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { if (valid()) { record_dispatch_enter(); XorpMemberCallback6B6::dispatch(a1, a2, a3, a4, a5, a6); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory6B6 { static XorpMemberCallback6B6* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpSafeMemberCallback6B6(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); } }; template struct XorpMemberCallbackFactory6B6 { static XorpMemberCallback6B6* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpMemberCallback6B6(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); }; }; /** * Factory function that creates a callback object targetted at a * member function with 6 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback6::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpMemberCallbackFactory6B6::True>::make(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * Factory function that creates a callback object targetted at a * member function with 6 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback6::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpMemberCallbackFactory6B6::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * @short Callback object for const member methods with 6 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstMemberCallback6B6 : public XorpCallback6 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4, BA5, BA6) const; XorpConstMemberCallback6B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback6(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for void const member methods with 6 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstMemberCallback6B6 : public XorpCallback6 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4, BA5, BA6) const; XorpConstMemberCallback6B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback6(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for const safe member methods with 6 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback6B6 : public XorpConstMemberCallback6B6, public SafeCallbackBase { typedef typename XorpConstMemberCallback6B6::M M; XorpConstSafeMemberCallback6B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpConstMemberCallback6B6(file, line, o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback6B6() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback6B6::dispatch(a1, a2, a3, a4, a5, a6); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 6 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback6B6 : public XorpConstMemberCallback6B6, public SafeCallbackBase { typedef typename XorpConstMemberCallback6B6::M M; XorpConstSafeMemberCallback6B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpConstMemberCallback6B6(file, line, o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback6B6() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback6B6::dispatch(a1, a2, a3, a4, a5, a6); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory6B6 { static XorpConstMemberCallback6B6* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpConstSafeMemberCallback6B6(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); } }; template struct XorpConstMemberCallbackFactory6B6 { static XorpConstMemberCallback6B6* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpConstMemberCallback6B6(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 6 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback6::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpConstMemberCallbackFactory6B6::True>::make(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * Factory function that creates a callback object targetted at a * const member function with 6 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback6::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpConstMemberCallbackFactory6B6::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /////////////////////////////////////////////////////////////////////////////// // // Code relating to callbacks with 7 late args // /** * @short Base class for callbacks with 7 dispatch time args. */ template struct XorpCallback7 { typedef ref_ptr RefPtr; XorpCallback7(const char* file, int line) : _file(file), _line(line) {} virtual ~XorpCallback7() {} virtual R dispatch(A1, A2, A3, A4, A5, A6, A7) = 0; const char* file() const { return _file; } int line() const { return _line; } private: const char* _file; int _line; }; /** * @short Callback object for functions with 7 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpFunctionCallback7B0 : public XorpCallback7 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7); XorpFunctionCallback7B0(const char* file, int line, F f) : XorpCallback7(file, line), _f(f) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, a7); record_dispatch_leave(); return r; } protected: F _f; }; /** * @short Callback object for void functions with 7 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpFunctionCallback7B0 : public XorpCallback7 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7); XorpFunctionCallback7B0(const char* file, int line, F f) : XorpCallback7(file, line), _f(f) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, a7); record_dispatch_leave(); } protected: F _f; }; /** * Factory function that creates a callback object targetted at a * function with 7 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback7::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, A7)) { return typename XorpCallback7::RefPtr(new XorpFunctionCallback7B0(file, line, f)); } /** * @short Callback object for member methods with 7 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpMemberCallback7B0 : public XorpCallback7 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7) ; XorpMemberCallback7B0(const char* file, int line, O* o, M m) : XorpCallback7(file, line), _o(o), _m(m) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for void member methods with 7 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpMemberCallback7B0 : public XorpCallback7 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7) ; XorpMemberCallback7B0(const char* file, int line, O* o, M m) : XorpCallback7(file, line), _o(o), _m(m) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for safe member methods with 7 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpSafeMemberCallback7B0 : public XorpMemberCallback7B0, public SafeCallbackBase { typedef typename XorpMemberCallback7B0::M M; XorpSafeMemberCallback7B0(const char* file, int line, O* o, M m) : XorpMemberCallback7B0(file, line, o, m), SafeCallbackBase(o) {} ~XorpSafeMemberCallback7B0() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback7B0::dispatch(a1, a2, a3, a4, a5, a6, a7); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 7 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpSafeMemberCallback7B0 : public XorpMemberCallback7B0, public SafeCallbackBase { typedef typename XorpMemberCallback7B0::M M; XorpSafeMemberCallback7B0(const char* file, int line, O* o, M m) : XorpMemberCallback7B0(file, line, o, m), SafeCallbackBase(o) {} ~XorpSafeMemberCallback7B0() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { if (valid()) { record_dispatch_enter(); XorpMemberCallback7B0::dispatch(a1, a2, a3, a4, a5, a6, a7); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory7B0 { static XorpMemberCallback7B0* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7)) { return new XorpSafeMemberCallback7B0(file, line, o, p); } }; template struct XorpMemberCallbackFactory7B0 { static XorpMemberCallback7B0* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7)) { return new XorpMemberCallback7B0(file, line, o, p); }; }; /** * Factory function that creates a callback object targetted at a * member function with 7 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback7::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7)) { return XorpMemberCallbackFactory7B0::True>::make(file, line, o, p); } /** * Factory function that creates a callback object targetted at a * member function with 7 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback7::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7)) { return XorpMemberCallbackFactory7B0::True>::make(file, line, &o, p); } /** * @short Callback object for const member methods with 7 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstMemberCallback7B0 : public XorpCallback7 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7) const; XorpConstMemberCallback7B0(const char* file, int line, O* o, M m) : XorpCallback7(file, line), _o(o), _m(m) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for void const member methods with 7 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstMemberCallback7B0 : public XorpCallback7 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7) const; XorpConstMemberCallback7B0(const char* file, int line, O* o, M m) : XorpCallback7(file, line), _o(o), _m(m) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for const safe member methods with 7 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback7B0 : public XorpConstMemberCallback7B0, public SafeCallbackBase { typedef typename XorpConstMemberCallback7B0::M M; XorpConstSafeMemberCallback7B0(const char* file, int line, O* o, M m) : XorpConstMemberCallback7B0(file, line, o, m), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback7B0() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback7B0::dispatch(a1, a2, a3, a4, a5, a6, a7); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 7 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback7B0 : public XorpConstMemberCallback7B0, public SafeCallbackBase { typedef typename XorpConstMemberCallback7B0::M M; XorpConstSafeMemberCallback7B0(const char* file, int line, O* o, M m) : XorpConstMemberCallback7B0(file, line, o, m), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback7B0() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback7B0::dispatch(a1, a2, a3, a4, a5, a6, a7); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory7B0 { static XorpConstMemberCallback7B0* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7) const) { return new XorpConstSafeMemberCallback7B0(file, line, o, p); } }; template struct XorpConstMemberCallbackFactory7B0 { static XorpConstMemberCallback7B0* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7) const) { return new XorpConstMemberCallback7B0(file, line, o, p); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 7 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback7::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7) const) { return XorpConstMemberCallbackFactory7B0::True>::make(file, line, o, p); } /** * Factory function that creates a callback object targetted at a * const member function with 7 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback7::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7) const) { return XorpConstMemberCallbackFactory7B0::True>::make(file, line, &o, p); } /** * @short Callback object for functions with 7 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpFunctionCallback7B1 : public XorpCallback7 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, BA1); XorpFunctionCallback7B1(const char* file, int line, F f, BA1 ba1) : XorpCallback7(file, line), _f(f), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, _ba1); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; }; /** * @short Callback object for void functions with 7 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpFunctionCallback7B1 : public XorpCallback7 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, BA1); XorpFunctionCallback7B1(const char* file, int line, F f, BA1 ba1) : XorpCallback7(file, line), _f(f), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, a7, _ba1); record_dispatch_leave(); } protected: F _f; BA1 _ba1; }; /** * Factory function that creates a callback object targetted at a * function with 7 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback7::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, A7, BA1), BA1 ba1) { return typename XorpCallback7::RefPtr(new XorpFunctionCallback7B1(file, line, f, ba1)); } /** * @short Callback object for member methods with 7 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpMemberCallback7B1 : public XorpCallback7 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, BA1) ; XorpMemberCallback7B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpCallback7(file, line), _o(o), _m(m), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, _ba1); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for void member methods with 7 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpMemberCallback7B1 : public XorpCallback7 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, BA1) ; XorpMemberCallback7B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpCallback7(file, line), _o(o), _m(m), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, _ba1); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for safe member methods with 7 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpSafeMemberCallback7B1 : public XorpMemberCallback7B1, public SafeCallbackBase { typedef typename XorpMemberCallback7B1::M M; XorpSafeMemberCallback7B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpMemberCallback7B1(file, line, o, m, ba1), SafeCallbackBase(o) {} ~XorpSafeMemberCallback7B1() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback7B1::dispatch(a1, a2, a3, a4, a5, a6, a7); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 7 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpSafeMemberCallback7B1 : public XorpMemberCallback7B1, public SafeCallbackBase { typedef typename XorpMemberCallback7B1::M M; XorpSafeMemberCallback7B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpMemberCallback7B1(file, line, o, m, ba1), SafeCallbackBase(o) {} ~XorpSafeMemberCallback7B1() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { if (valid()) { record_dispatch_enter(); XorpMemberCallback7B1::dispatch(a1, a2, a3, a4, a5, a6, a7); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory7B1 { static XorpMemberCallback7B1* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1), BA1 ba1) { return new XorpSafeMemberCallback7B1(file, line, o, p, ba1); } }; template struct XorpMemberCallbackFactory7B1 { static XorpMemberCallback7B1* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1), BA1 ba1) { return new XorpMemberCallback7B1(file, line, o, p, ba1); }; }; /** * Factory function that creates a callback object targetted at a * member function with 7 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback7::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1), BA1 ba1) { return XorpMemberCallbackFactory7B1::True>::make(file, line, o, p, ba1); } /** * Factory function that creates a callback object targetted at a * member function with 7 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback7::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1), BA1 ba1) { return XorpMemberCallbackFactory7B1::True>::make(file, line, &o, p, ba1); } /** * @short Callback object for const member methods with 7 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstMemberCallback7B1 : public XorpCallback7 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, BA1) const; XorpConstMemberCallback7B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpCallback7(file, line), _o(o), _m(m), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, _ba1); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for void const member methods with 7 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstMemberCallback7B1 : public XorpCallback7 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, BA1) const; XorpConstMemberCallback7B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpCallback7(file, line), _o(o), _m(m), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, _ba1); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for const safe member methods with 7 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback7B1 : public XorpConstMemberCallback7B1, public SafeCallbackBase { typedef typename XorpConstMemberCallback7B1::M M; XorpConstSafeMemberCallback7B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpConstMemberCallback7B1(file, line, o, m, ba1), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback7B1() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback7B1::dispatch(a1, a2, a3, a4, a5, a6, a7); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 7 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback7B1 : public XorpConstMemberCallback7B1, public SafeCallbackBase { typedef typename XorpConstMemberCallback7B1::M M; XorpConstSafeMemberCallback7B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpConstMemberCallback7B1(file, line, o, m, ba1), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback7B1() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback7B1::dispatch(a1, a2, a3, a4, a5, a6, a7); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory7B1 { static XorpConstMemberCallback7B1* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1) const, BA1 ba1) { return new XorpConstSafeMemberCallback7B1(file, line, o, p, ba1); } }; template struct XorpConstMemberCallbackFactory7B1 { static XorpConstMemberCallback7B1* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1) const, BA1 ba1) { return new XorpConstMemberCallback7B1(file, line, o, p, ba1); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 7 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback7::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1) const, BA1 ba1) { return XorpConstMemberCallbackFactory7B1::True>::make(file, line, o, p, ba1); } /** * Factory function that creates a callback object targetted at a * const member function with 7 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback7::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1) const, BA1 ba1) { return XorpConstMemberCallbackFactory7B1::True>::make(file, line, &o, p, ba1); } /** * @short Callback object for functions with 7 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpFunctionCallback7B2 : public XorpCallback7 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2); XorpFunctionCallback7B2(const char* file, int line, F f, BA1 ba1, BA2 ba2) : XorpCallback7(file, line), _f(f), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, _ba1, _ba2); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; }; /** * @short Callback object for void functions with 7 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpFunctionCallback7B2 : public XorpCallback7 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2); XorpFunctionCallback7B2(const char* file, int line, F f, BA1 ba1, BA2 ba2) : XorpCallback7(file, line), _f(f), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, a7, _ba1, _ba2); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; }; /** * Factory function that creates a callback object targetted at a * function with 7 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback7::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2), BA1 ba1, BA2 ba2) { return typename XorpCallback7::RefPtr(new XorpFunctionCallback7B2(file, line, f, ba1, ba2)); } /** * @short Callback object for member methods with 7 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpMemberCallback7B2 : public XorpCallback7 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2) ; XorpMemberCallback7B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback7(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, _ba1, _ba2); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for void member methods with 7 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpMemberCallback7B2 : public XorpCallback7 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2) ; XorpMemberCallback7B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback7(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, _ba1, _ba2); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for safe member methods with 7 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpSafeMemberCallback7B2 : public XorpMemberCallback7B2, public SafeCallbackBase { typedef typename XorpMemberCallback7B2::M M; XorpSafeMemberCallback7B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpMemberCallback7B2(file, line, o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpSafeMemberCallback7B2() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback7B2::dispatch(a1, a2, a3, a4, a5, a6, a7); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 7 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpSafeMemberCallback7B2 : public XorpMemberCallback7B2, public SafeCallbackBase { typedef typename XorpMemberCallback7B2::M M; XorpSafeMemberCallback7B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpMemberCallback7B2(file, line, o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpSafeMemberCallback7B2() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { if (valid()) { record_dispatch_enter(); XorpMemberCallback7B2::dispatch(a1, a2, a3, a4, a5, a6, a7); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory7B2 { static XorpMemberCallback7B2* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2), BA1 ba1, BA2 ba2) { return new XorpSafeMemberCallback7B2(file, line, o, p, ba1, ba2); } }; template struct XorpMemberCallbackFactory7B2 { static XorpMemberCallback7B2* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2), BA1 ba1, BA2 ba2) { return new XorpMemberCallback7B2(file, line, o, p, ba1, ba2); }; }; /** * Factory function that creates a callback object targetted at a * member function with 7 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback7::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2), BA1 ba1, BA2 ba2) { return XorpMemberCallbackFactory7B2::True>::make(file, line, o, p, ba1, ba2); } /** * Factory function that creates a callback object targetted at a * member function with 7 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback7::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2), BA1 ba1, BA2 ba2) { return XorpMemberCallbackFactory7B2::True>::make(file, line, &o, p, ba1, ba2); } /** * @short Callback object for const member methods with 7 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstMemberCallback7B2 : public XorpCallback7 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2) const; XorpConstMemberCallback7B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback7(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, _ba1, _ba2); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for void const member methods with 7 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstMemberCallback7B2 : public XorpCallback7 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2) const; XorpConstMemberCallback7B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback7(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, _ba1, _ba2); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for const safe member methods with 7 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback7B2 : public XorpConstMemberCallback7B2, public SafeCallbackBase { typedef typename XorpConstMemberCallback7B2::M M; XorpConstSafeMemberCallback7B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpConstMemberCallback7B2(file, line, o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback7B2() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback7B2::dispatch(a1, a2, a3, a4, a5, a6, a7); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 7 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback7B2 : public XorpConstMemberCallback7B2, public SafeCallbackBase { typedef typename XorpConstMemberCallback7B2::M M; XorpConstSafeMemberCallback7B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpConstMemberCallback7B2(file, line, o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback7B2() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback7B2::dispatch(a1, a2, a3, a4, a5, a6, a7); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory7B2 { static XorpConstMemberCallback7B2* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2) const, BA1 ba1, BA2 ba2) { return new XorpConstSafeMemberCallback7B2(file, line, o, p, ba1, ba2); } }; template struct XorpConstMemberCallbackFactory7B2 { static XorpConstMemberCallback7B2* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2) const, BA1 ba1, BA2 ba2) { return new XorpConstMemberCallback7B2(file, line, o, p, ba1, ba2); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 7 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback7::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2) const, BA1 ba1, BA2 ba2) { return XorpConstMemberCallbackFactory7B2::True>::make(file, line, o, p, ba1, ba2); } /** * Factory function that creates a callback object targetted at a * const member function with 7 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback7::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2) const, BA1 ba1, BA2 ba2) { return XorpConstMemberCallbackFactory7B2::True>::make(file, line, &o, p, ba1, ba2); } /** * @short Callback object for functions with 7 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpFunctionCallback7B3 : public XorpCallback7 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3); XorpFunctionCallback7B3(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback7(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, _ba1, _ba2, _ba3); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; }; /** * @short Callback object for void functions with 7 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpFunctionCallback7B3 : public XorpCallback7 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3); XorpFunctionCallback7B3(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback7(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, a7, _ba1, _ba2, _ba3); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; }; /** * Factory function that creates a callback object targetted at a * function with 7 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback7::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return typename XorpCallback7::RefPtr(new XorpFunctionCallback7B3(file, line, f, ba1, ba2, ba3)); } /** * @short Callback object for member methods with 7 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpMemberCallback7B3 : public XorpCallback7 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3) ; XorpMemberCallback7B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback7(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, _ba1, _ba2, _ba3); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for void member methods with 7 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpMemberCallback7B3 : public XorpCallback7 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3) ; XorpMemberCallback7B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback7(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, _ba1, _ba2, _ba3); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for safe member methods with 7 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpSafeMemberCallback7B3 : public XorpMemberCallback7B3, public SafeCallbackBase { typedef typename XorpMemberCallback7B3::M M; XorpSafeMemberCallback7B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpMemberCallback7B3(file, line, o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpSafeMemberCallback7B3() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback7B3::dispatch(a1, a2, a3, a4, a5, a6, a7); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 7 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpSafeMemberCallback7B3 : public XorpMemberCallback7B3, public SafeCallbackBase { typedef typename XorpMemberCallback7B3::M M; XorpSafeMemberCallback7B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpMemberCallback7B3(file, line, o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpSafeMemberCallback7B3() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { if (valid()) { record_dispatch_enter(); XorpMemberCallback7B3::dispatch(a1, a2, a3, a4, a5, a6, a7); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory7B3 { static XorpMemberCallback7B3* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpSafeMemberCallback7B3(file, line, o, p, ba1, ba2, ba3); } }; template struct XorpMemberCallbackFactory7B3 { static XorpMemberCallback7B3* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpMemberCallback7B3(file, line, o, p, ba1, ba2, ba3); }; }; /** * Factory function that creates a callback object targetted at a * member function with 7 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback7::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return XorpMemberCallbackFactory7B3::True>::make(file, line, o, p, ba1, ba2, ba3); } /** * Factory function that creates a callback object targetted at a * member function with 7 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback7::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return XorpMemberCallbackFactory7B3::True>::make(file, line, &o, p, ba1, ba2, ba3); } /** * @short Callback object for const member methods with 7 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstMemberCallback7B3 : public XorpCallback7 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3) const; XorpConstMemberCallback7B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback7(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, _ba1, _ba2, _ba3); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for void const member methods with 7 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstMemberCallback7B3 : public XorpCallback7 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3) const; XorpConstMemberCallback7B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback7(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, _ba1, _ba2, _ba3); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for const safe member methods with 7 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback7B3 : public XorpConstMemberCallback7B3, public SafeCallbackBase { typedef typename XorpConstMemberCallback7B3::M M; XorpConstSafeMemberCallback7B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpConstMemberCallback7B3(file, line, o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback7B3() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback7B3::dispatch(a1, a2, a3, a4, a5, a6, a7); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 7 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback7B3 : public XorpConstMemberCallback7B3, public SafeCallbackBase { typedef typename XorpConstMemberCallback7B3::M M; XorpConstSafeMemberCallback7B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpConstMemberCallback7B3(file, line, o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback7B3() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback7B3::dispatch(a1, a2, a3, a4, a5, a6, a7); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory7B3 { static XorpConstMemberCallback7B3* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpConstSafeMemberCallback7B3(file, line, o, p, ba1, ba2, ba3); } }; template struct XorpConstMemberCallbackFactory7B3 { static XorpConstMemberCallback7B3* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpConstMemberCallback7B3(file, line, o, p, ba1, ba2, ba3); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 7 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback7::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return XorpConstMemberCallbackFactory7B3::True>::make(file, line, o, p, ba1, ba2, ba3); } /** * Factory function that creates a callback object targetted at a * const member function with 7 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback7::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return XorpConstMemberCallbackFactory7B3::True>::make(file, line, &o, p, ba1, ba2, ba3); } /** * @short Callback object for functions with 7 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpFunctionCallback7B4 : public XorpCallback7 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4); XorpFunctionCallback7B4(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback7(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; }; /** * @short Callback object for void functions with 7 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpFunctionCallback7B4 : public XorpCallback7 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4); XorpFunctionCallback7B4(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback7(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, a7, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; }; /** * Factory function that creates a callback object targetted at a * function with 7 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback7::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return typename XorpCallback7::RefPtr(new XorpFunctionCallback7B4(file, line, f, ba1, ba2, ba3, ba4)); } /** * @short Callback object for member methods with 7 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpMemberCallback7B4 : public XorpCallback7 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4) ; XorpMemberCallback7B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback7(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for void member methods with 7 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpMemberCallback7B4 : public XorpCallback7 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4) ; XorpMemberCallback7B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback7(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for safe member methods with 7 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpSafeMemberCallback7B4 : public XorpMemberCallback7B4, public SafeCallbackBase { typedef typename XorpMemberCallback7B4::M M; XorpSafeMemberCallback7B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpMemberCallback7B4(file, line, o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpSafeMemberCallback7B4() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback7B4::dispatch(a1, a2, a3, a4, a5, a6, a7); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 7 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpSafeMemberCallback7B4 : public XorpMemberCallback7B4, public SafeCallbackBase { typedef typename XorpMemberCallback7B4::M M; XorpSafeMemberCallback7B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpMemberCallback7B4(file, line, o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpSafeMemberCallback7B4() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { if (valid()) { record_dispatch_enter(); XorpMemberCallback7B4::dispatch(a1, a2, a3, a4, a5, a6, a7); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory7B4 { static XorpMemberCallback7B4* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpSafeMemberCallback7B4(file, line, o, p, ba1, ba2, ba3, ba4); } }; template struct XorpMemberCallbackFactory7B4 { static XorpMemberCallback7B4* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpMemberCallback7B4(file, line, o, p, ba1, ba2, ba3, ba4); }; }; /** * Factory function that creates a callback object targetted at a * member function with 7 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback7::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpMemberCallbackFactory7B4::True>::make(file, line, o, p, ba1, ba2, ba3, ba4); } /** * Factory function that creates a callback object targetted at a * member function with 7 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback7::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpMemberCallbackFactory7B4::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4); } /** * @short Callback object for const member methods with 7 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstMemberCallback7B4 : public XorpCallback7 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4) const; XorpConstMemberCallback7B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback7(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for void const member methods with 7 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstMemberCallback7B4 : public XorpCallback7 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4) const; XorpConstMemberCallback7B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback7(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for const safe member methods with 7 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback7B4 : public XorpConstMemberCallback7B4, public SafeCallbackBase { typedef typename XorpConstMemberCallback7B4::M M; XorpConstSafeMemberCallback7B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpConstMemberCallback7B4(file, line, o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback7B4() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback7B4::dispatch(a1, a2, a3, a4, a5, a6, a7); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 7 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback7B4 : public XorpConstMemberCallback7B4, public SafeCallbackBase { typedef typename XorpConstMemberCallback7B4::M M; XorpConstSafeMemberCallback7B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpConstMemberCallback7B4(file, line, o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback7B4() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback7B4::dispatch(a1, a2, a3, a4, a5, a6, a7); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory7B4 { static XorpConstMemberCallback7B4* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpConstSafeMemberCallback7B4(file, line, o, p, ba1, ba2, ba3, ba4); } }; template struct XorpConstMemberCallbackFactory7B4 { static XorpConstMemberCallback7B4* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpConstMemberCallback7B4(file, line, o, p, ba1, ba2, ba3, ba4); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 7 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback7::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpConstMemberCallbackFactory7B4::True>::make(file, line, o, p, ba1, ba2, ba3, ba4); } /** * Factory function that creates a callback object targetted at a * const member function with 7 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback7::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpConstMemberCallbackFactory7B4::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4); } /** * @short Callback object for functions with 7 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpFunctionCallback7B5 : public XorpCallback7 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4, BA5); XorpFunctionCallback7B5(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback7(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; }; /** * @short Callback object for void functions with 7 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpFunctionCallback7B5 : public XorpCallback7 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4, BA5); XorpFunctionCallback7B5(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback7(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, a7, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; }; /** * Factory function that creates a callback object targetted at a * function with 7 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback7::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return typename XorpCallback7::RefPtr(new XorpFunctionCallback7B5(file, line, f, ba1, ba2, ba3, ba4, ba5)); } /** * @short Callback object for member methods with 7 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpMemberCallback7B5 : public XorpCallback7 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4, BA5) ; XorpMemberCallback7B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback7(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for void member methods with 7 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpMemberCallback7B5 : public XorpCallback7 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4, BA5) ; XorpMemberCallback7B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback7(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for safe member methods with 7 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpSafeMemberCallback7B5 : public XorpMemberCallback7B5, public SafeCallbackBase { typedef typename XorpMemberCallback7B5::M M; XorpSafeMemberCallback7B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpMemberCallback7B5(file, line, o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpSafeMemberCallback7B5() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback7B5::dispatch(a1, a2, a3, a4, a5, a6, a7); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 7 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpSafeMemberCallback7B5 : public XorpMemberCallback7B5, public SafeCallbackBase { typedef typename XorpMemberCallback7B5::M M; XorpSafeMemberCallback7B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpMemberCallback7B5(file, line, o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpSafeMemberCallback7B5() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { if (valid()) { record_dispatch_enter(); XorpMemberCallback7B5::dispatch(a1, a2, a3, a4, a5, a6, a7); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory7B5 { static XorpMemberCallback7B5* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpSafeMemberCallback7B5(file, line, o, p, ba1, ba2, ba3, ba4, ba5); } }; template struct XorpMemberCallbackFactory7B5 { static XorpMemberCallback7B5* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpMemberCallback7B5(file, line, o, p, ba1, ba2, ba3, ba4, ba5); }; }; /** * Factory function that creates a callback object targetted at a * member function with 7 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback7::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpMemberCallbackFactory7B5::True>::make(file, line, o, p, ba1, ba2, ba3, ba4, ba5); } /** * Factory function that creates a callback object targetted at a * member function with 7 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback7::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpMemberCallbackFactory7B5::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4, ba5); } /** * @short Callback object for const member methods with 7 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstMemberCallback7B5 : public XorpCallback7 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4, BA5) const; XorpConstMemberCallback7B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback7(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for void const member methods with 7 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstMemberCallback7B5 : public XorpCallback7 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4, BA5) const; XorpConstMemberCallback7B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback7(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for const safe member methods with 7 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback7B5 : public XorpConstMemberCallback7B5, public SafeCallbackBase { typedef typename XorpConstMemberCallback7B5::M M; XorpConstSafeMemberCallback7B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpConstMemberCallback7B5(file, line, o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback7B5() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback7B5::dispatch(a1, a2, a3, a4, a5, a6, a7); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 7 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback7B5 : public XorpConstMemberCallback7B5, public SafeCallbackBase { typedef typename XorpConstMemberCallback7B5::M M; XorpConstSafeMemberCallback7B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpConstMemberCallback7B5(file, line, o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback7B5() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback7B5::dispatch(a1, a2, a3, a4, a5, a6, a7); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory7B5 { static XorpConstMemberCallback7B5* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpConstSafeMemberCallback7B5(file, line, o, p, ba1, ba2, ba3, ba4, ba5); } }; template struct XorpConstMemberCallbackFactory7B5 { static XorpConstMemberCallback7B5* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpConstMemberCallback7B5(file, line, o, p, ba1, ba2, ba3, ba4, ba5); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 7 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback7::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpConstMemberCallbackFactory7B5::True>::make(file, line, o, p, ba1, ba2, ba3, ba4, ba5); } /** * Factory function that creates a callback object targetted at a * const member function with 7 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback7::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpConstMemberCallbackFactory7B5::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4, ba5); } /** * @short Callback object for functions with 7 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpFunctionCallback7B6 : public XorpCallback7 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4, BA5, BA6); XorpFunctionCallback7B6(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback7(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; BA6 _ba6; }; /** * @short Callback object for void functions with 7 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpFunctionCallback7B6 : public XorpCallback7 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4, BA5, BA6); XorpFunctionCallback7B6(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback7(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, a7, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; BA6 _ba6; }; /** * Factory function that creates a callback object targetted at a * function with 7 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback7::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return typename XorpCallback7::RefPtr(new XorpFunctionCallback7B6(file, line, f, ba1, ba2, ba3, ba4, ba5, ba6)); } /** * @short Callback object for member methods with 7 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpMemberCallback7B6 : public XorpCallback7 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4, BA5, BA6) ; XorpMemberCallback7B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback7(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for void member methods with 7 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpMemberCallback7B6 : public XorpCallback7 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4, BA5, BA6) ; XorpMemberCallback7B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback7(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for safe member methods with 7 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpSafeMemberCallback7B6 : public XorpMemberCallback7B6, public SafeCallbackBase { typedef typename XorpMemberCallback7B6::M M; XorpSafeMemberCallback7B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpMemberCallback7B6(file, line, o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpSafeMemberCallback7B6() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback7B6::dispatch(a1, a2, a3, a4, a5, a6, a7); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 7 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpSafeMemberCallback7B6 : public XorpMemberCallback7B6, public SafeCallbackBase { typedef typename XorpMemberCallback7B6::M M; XorpSafeMemberCallback7B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpMemberCallback7B6(file, line, o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpSafeMemberCallback7B6() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { if (valid()) { record_dispatch_enter(); XorpMemberCallback7B6::dispatch(a1, a2, a3, a4, a5, a6, a7); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory7B6 { static XorpMemberCallback7B6* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpSafeMemberCallback7B6(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); } }; template struct XorpMemberCallbackFactory7B6 { static XorpMemberCallback7B6* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpMemberCallback7B6(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); }; }; /** * Factory function that creates a callback object targetted at a * member function with 7 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback7::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpMemberCallbackFactory7B6::True>::make(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * Factory function that creates a callback object targetted at a * member function with 7 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback7::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpMemberCallbackFactory7B6::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * @short Callback object for const member methods with 7 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstMemberCallback7B6 : public XorpCallback7 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4, BA5, BA6) const; XorpConstMemberCallback7B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback7(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for void const member methods with 7 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstMemberCallback7B6 : public XorpCallback7 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4, BA5, BA6) const; XorpConstMemberCallback7B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback7(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for const safe member methods with 7 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback7B6 : public XorpConstMemberCallback7B6, public SafeCallbackBase { typedef typename XorpConstMemberCallback7B6::M M; XorpConstSafeMemberCallback7B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpConstMemberCallback7B6(file, line, o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback7B6() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback7B6::dispatch(a1, a2, a3, a4, a5, a6, a7); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 7 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback7B6 : public XorpConstMemberCallback7B6, public SafeCallbackBase { typedef typename XorpConstMemberCallback7B6::M M; XorpConstSafeMemberCallback7B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpConstMemberCallback7B6(file, line, o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback7B6() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback7B6::dispatch(a1, a2, a3, a4, a5, a6, a7); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory7B6 { static XorpConstMemberCallback7B6* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpConstSafeMemberCallback7B6(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); } }; template struct XorpConstMemberCallbackFactory7B6 { static XorpConstMemberCallback7B6* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpConstMemberCallback7B6(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 7 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback7::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpConstMemberCallbackFactory7B6::True>::make(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * Factory function that creates a callback object targetted at a * const member function with 7 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback7::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpConstMemberCallbackFactory7B6::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /////////////////////////////////////////////////////////////////////////////// // // Code relating to callbacks with 8 late args // /** * @short Base class for callbacks with 8 dispatch time args. */ template struct XorpCallback8 { typedef ref_ptr RefPtr; XorpCallback8(const char* file, int line) : _file(file), _line(line) {} virtual ~XorpCallback8() {} virtual R dispatch(A1, A2, A3, A4, A5, A6, A7, A8) = 0; const char* file() const { return _file; } int line() const { return _line; } private: const char* _file; int _line; }; /** * @short Callback object for functions with 8 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpFunctionCallback8B0 : public XorpCallback8 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8); XorpFunctionCallback8B0(const char* file, int line, F f) : XorpCallback8(file, line), _f(f) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8); record_dispatch_leave(); return r; } protected: F _f; }; /** * @short Callback object for void functions with 8 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpFunctionCallback8B0 : public XorpCallback8 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8); XorpFunctionCallback8B0(const char* file, int line, F f) : XorpCallback8(file, line), _f(f) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, a7, a8); record_dispatch_leave(); } protected: F _f; }; /** * Factory function that creates a callback object targetted at a * function with 8 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback8::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, A7, A8)) { return typename XorpCallback8::RefPtr(new XorpFunctionCallback8B0(file, line, f)); } /** * @short Callback object for member methods with 8 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpMemberCallback8B0 : public XorpCallback8 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8) ; XorpMemberCallback8B0(const char* file, int line, O* o, M m) : XorpCallback8(file, line), _o(o), _m(m) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for void member methods with 8 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpMemberCallback8B0 : public XorpCallback8 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8) ; XorpMemberCallback8B0(const char* file, int line, O* o, M m) : XorpCallback8(file, line), _o(o), _m(m) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for safe member methods with 8 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpSafeMemberCallback8B0 : public XorpMemberCallback8B0, public SafeCallbackBase { typedef typename XorpMemberCallback8B0::M M; XorpSafeMemberCallback8B0(const char* file, int line, O* o, M m) : XorpMemberCallback8B0(file, line, o, m), SafeCallbackBase(o) {} ~XorpSafeMemberCallback8B0() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback8B0::dispatch(a1, a2, a3, a4, a5, a6, a7, a8); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 8 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpSafeMemberCallback8B0 : public XorpMemberCallback8B0, public SafeCallbackBase { typedef typename XorpMemberCallback8B0::M M; XorpSafeMemberCallback8B0(const char* file, int line, O* o, M m) : XorpMemberCallback8B0(file, line, o, m), SafeCallbackBase(o) {} ~XorpSafeMemberCallback8B0() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { if (valid()) { record_dispatch_enter(); XorpMemberCallback8B0::dispatch(a1, a2, a3, a4, a5, a6, a7, a8); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory8B0 { static XorpMemberCallback8B0* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8)) { return new XorpSafeMemberCallback8B0(file, line, o, p); } }; template struct XorpMemberCallbackFactory8B0 { static XorpMemberCallback8B0* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8)) { return new XorpMemberCallback8B0(file, line, o, p); }; }; /** * Factory function that creates a callback object targetted at a * member function with 8 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback8::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8)) { return XorpMemberCallbackFactory8B0::True>::make(file, line, o, p); } /** * Factory function that creates a callback object targetted at a * member function with 8 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback8::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8)) { return XorpMemberCallbackFactory8B0::True>::make(file, line, &o, p); } /** * @short Callback object for const member methods with 8 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstMemberCallback8B0 : public XorpCallback8 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8) const; XorpConstMemberCallback8B0(const char* file, int line, O* o, M m) : XorpCallback8(file, line), _o(o), _m(m) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for void const member methods with 8 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstMemberCallback8B0 : public XorpCallback8 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8) const; XorpConstMemberCallback8B0(const char* file, int line, O* o, M m) : XorpCallback8(file, line), _o(o), _m(m) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for const safe member methods with 8 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback8B0 : public XorpConstMemberCallback8B0, public SafeCallbackBase { typedef typename XorpConstMemberCallback8B0::M M; XorpConstSafeMemberCallback8B0(const char* file, int line, O* o, M m) : XorpConstMemberCallback8B0(file, line, o, m), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback8B0() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback8B0::dispatch(a1, a2, a3, a4, a5, a6, a7, a8); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 8 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback8B0 : public XorpConstMemberCallback8B0, public SafeCallbackBase { typedef typename XorpConstMemberCallback8B0::M M; XorpConstSafeMemberCallback8B0(const char* file, int line, O* o, M m) : XorpConstMemberCallback8B0(file, line, o, m), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback8B0() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback8B0::dispatch(a1, a2, a3, a4, a5, a6, a7, a8); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory8B0 { static XorpConstMemberCallback8B0* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8) const) { return new XorpConstSafeMemberCallback8B0(file, line, o, p); } }; template struct XorpConstMemberCallbackFactory8B0 { static XorpConstMemberCallback8B0* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8) const) { return new XorpConstMemberCallback8B0(file, line, o, p); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 8 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback8::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8) const) { return XorpConstMemberCallbackFactory8B0::True>::make(file, line, o, p); } /** * Factory function that creates a callback object targetted at a * const member function with 8 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback8::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8) const) { return XorpConstMemberCallbackFactory8B0::True>::make(file, line, &o, p); } /** * @short Callback object for functions with 8 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpFunctionCallback8B1 : public XorpCallback8 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, BA1); XorpFunctionCallback8B1(const char* file, int line, F f, BA1 ba1) : XorpCallback8(file, line), _f(f), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; }; /** * @short Callback object for void functions with 8 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpFunctionCallback8B1 : public XorpCallback8 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, BA1); XorpFunctionCallback8B1(const char* file, int line, F f, BA1 ba1) : XorpCallback8(file, line), _f(f), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1); record_dispatch_leave(); } protected: F _f; BA1 _ba1; }; /** * Factory function that creates a callback object targetted at a * function with 8 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback8::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, BA1), BA1 ba1) { return typename XorpCallback8::RefPtr(new XorpFunctionCallback8B1(file, line, f, ba1)); } /** * @short Callback object for member methods with 8 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpMemberCallback8B1 : public XorpCallback8 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, BA1) ; XorpMemberCallback8B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpCallback8(file, line), _o(o), _m(m), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for void member methods with 8 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpMemberCallback8B1 : public XorpCallback8 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, BA1) ; XorpMemberCallback8B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpCallback8(file, line), _o(o), _m(m), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for safe member methods with 8 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpSafeMemberCallback8B1 : public XorpMemberCallback8B1, public SafeCallbackBase { typedef typename XorpMemberCallback8B1::M M; XorpSafeMemberCallback8B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpMemberCallback8B1(file, line, o, m, ba1), SafeCallbackBase(o) {} ~XorpSafeMemberCallback8B1() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback8B1::dispatch(a1, a2, a3, a4, a5, a6, a7, a8); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 8 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpSafeMemberCallback8B1 : public XorpMemberCallback8B1, public SafeCallbackBase { typedef typename XorpMemberCallback8B1::M M; XorpSafeMemberCallback8B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpMemberCallback8B1(file, line, o, m, ba1), SafeCallbackBase(o) {} ~XorpSafeMemberCallback8B1() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { if (valid()) { record_dispatch_enter(); XorpMemberCallback8B1::dispatch(a1, a2, a3, a4, a5, a6, a7, a8); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory8B1 { static XorpMemberCallback8B1* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1), BA1 ba1) { return new XorpSafeMemberCallback8B1(file, line, o, p, ba1); } }; template struct XorpMemberCallbackFactory8B1 { static XorpMemberCallback8B1* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1), BA1 ba1) { return new XorpMemberCallback8B1(file, line, o, p, ba1); }; }; /** * Factory function that creates a callback object targetted at a * member function with 8 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback8::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1), BA1 ba1) { return XorpMemberCallbackFactory8B1::True>::make(file, line, o, p, ba1); } /** * Factory function that creates a callback object targetted at a * member function with 8 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback8::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1), BA1 ba1) { return XorpMemberCallbackFactory8B1::True>::make(file, line, &o, p, ba1); } /** * @short Callback object for const member methods with 8 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstMemberCallback8B1 : public XorpCallback8 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, BA1) const; XorpConstMemberCallback8B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpCallback8(file, line), _o(o), _m(m), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for void const member methods with 8 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstMemberCallback8B1 : public XorpCallback8 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, BA1) const; XorpConstMemberCallback8B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpCallback8(file, line), _o(o), _m(m), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for const safe member methods with 8 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback8B1 : public XorpConstMemberCallback8B1, public SafeCallbackBase { typedef typename XorpConstMemberCallback8B1::M M; XorpConstSafeMemberCallback8B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpConstMemberCallback8B1(file, line, o, m, ba1), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback8B1() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback8B1::dispatch(a1, a2, a3, a4, a5, a6, a7, a8); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 8 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback8B1 : public XorpConstMemberCallback8B1, public SafeCallbackBase { typedef typename XorpConstMemberCallback8B1::M M; XorpConstSafeMemberCallback8B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpConstMemberCallback8B1(file, line, o, m, ba1), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback8B1() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback8B1::dispatch(a1, a2, a3, a4, a5, a6, a7, a8); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory8B1 { static XorpConstMemberCallback8B1* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1) const, BA1 ba1) { return new XorpConstSafeMemberCallback8B1(file, line, o, p, ba1); } }; template struct XorpConstMemberCallbackFactory8B1 { static XorpConstMemberCallback8B1* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1) const, BA1 ba1) { return new XorpConstMemberCallback8B1(file, line, o, p, ba1); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 8 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback8::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1) const, BA1 ba1) { return XorpConstMemberCallbackFactory8B1::True>::make(file, line, o, p, ba1); } /** * Factory function that creates a callback object targetted at a * const member function with 8 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback8::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1) const, BA1 ba1) { return XorpConstMemberCallbackFactory8B1::True>::make(file, line, &o, p, ba1); } /** * @short Callback object for functions with 8 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpFunctionCallback8B2 : public XorpCallback8 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2); XorpFunctionCallback8B2(const char* file, int line, F f, BA1 ba1, BA2 ba2) : XorpCallback8(file, line), _f(f), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1, _ba2); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; }; /** * @short Callback object for void functions with 8 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpFunctionCallback8B2 : public XorpCallback8 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2); XorpFunctionCallback8B2(const char* file, int line, F f, BA1 ba1, BA2 ba2) : XorpCallback8(file, line), _f(f), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1, _ba2); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; }; /** * Factory function that creates a callback object targetted at a * function with 8 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback8::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2), BA1 ba1, BA2 ba2) { return typename XorpCallback8::RefPtr(new XorpFunctionCallback8B2(file, line, f, ba1, ba2)); } /** * @short Callback object for member methods with 8 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpMemberCallback8B2 : public XorpCallback8 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2) ; XorpMemberCallback8B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback8(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1, _ba2); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for void member methods with 8 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpMemberCallback8B2 : public XorpCallback8 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2) ; XorpMemberCallback8B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback8(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1, _ba2); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for safe member methods with 8 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpSafeMemberCallback8B2 : public XorpMemberCallback8B2, public SafeCallbackBase { typedef typename XorpMemberCallback8B2::M M; XorpSafeMemberCallback8B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpMemberCallback8B2(file, line, o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpSafeMemberCallback8B2() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback8B2::dispatch(a1, a2, a3, a4, a5, a6, a7, a8); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 8 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpSafeMemberCallback8B2 : public XorpMemberCallback8B2, public SafeCallbackBase { typedef typename XorpMemberCallback8B2::M M; XorpSafeMemberCallback8B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpMemberCallback8B2(file, line, o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpSafeMemberCallback8B2() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { if (valid()) { record_dispatch_enter(); XorpMemberCallback8B2::dispatch(a1, a2, a3, a4, a5, a6, a7, a8); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory8B2 { static XorpMemberCallback8B2* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2), BA1 ba1, BA2 ba2) { return new XorpSafeMemberCallback8B2(file, line, o, p, ba1, ba2); } }; template struct XorpMemberCallbackFactory8B2 { static XorpMemberCallback8B2* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2), BA1 ba1, BA2 ba2) { return new XorpMemberCallback8B2(file, line, o, p, ba1, ba2); }; }; /** * Factory function that creates a callback object targetted at a * member function with 8 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback8::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2), BA1 ba1, BA2 ba2) { return XorpMemberCallbackFactory8B2::True>::make(file, line, o, p, ba1, ba2); } /** * Factory function that creates a callback object targetted at a * member function with 8 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback8::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2), BA1 ba1, BA2 ba2) { return XorpMemberCallbackFactory8B2::True>::make(file, line, &o, p, ba1, ba2); } /** * @short Callback object for const member methods with 8 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstMemberCallback8B2 : public XorpCallback8 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2) const; XorpConstMemberCallback8B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback8(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1, _ba2); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for void const member methods with 8 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstMemberCallback8B2 : public XorpCallback8 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2) const; XorpConstMemberCallback8B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback8(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1, _ba2); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for const safe member methods with 8 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback8B2 : public XorpConstMemberCallback8B2, public SafeCallbackBase { typedef typename XorpConstMemberCallback8B2::M M; XorpConstSafeMemberCallback8B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpConstMemberCallback8B2(file, line, o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback8B2() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback8B2::dispatch(a1, a2, a3, a4, a5, a6, a7, a8); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 8 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback8B2 : public XorpConstMemberCallback8B2, public SafeCallbackBase { typedef typename XorpConstMemberCallback8B2::M M; XorpConstSafeMemberCallback8B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpConstMemberCallback8B2(file, line, o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback8B2() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback8B2::dispatch(a1, a2, a3, a4, a5, a6, a7, a8); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory8B2 { static XorpConstMemberCallback8B2* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2) const, BA1 ba1, BA2 ba2) { return new XorpConstSafeMemberCallback8B2(file, line, o, p, ba1, ba2); } }; template struct XorpConstMemberCallbackFactory8B2 { static XorpConstMemberCallback8B2* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2) const, BA1 ba1, BA2 ba2) { return new XorpConstMemberCallback8B2(file, line, o, p, ba1, ba2); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 8 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback8::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2) const, BA1 ba1, BA2 ba2) { return XorpConstMemberCallbackFactory8B2::True>::make(file, line, o, p, ba1, ba2); } /** * Factory function that creates a callback object targetted at a * const member function with 8 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback8::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2) const, BA1 ba1, BA2 ba2) { return XorpConstMemberCallbackFactory8B2::True>::make(file, line, &o, p, ba1, ba2); } /** * @short Callback object for functions with 8 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpFunctionCallback8B3 : public XorpCallback8 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3); XorpFunctionCallback8B3(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback8(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1, _ba2, _ba3); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; }; /** * @short Callback object for void functions with 8 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpFunctionCallback8B3 : public XorpCallback8 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3); XorpFunctionCallback8B3(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback8(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1, _ba2, _ba3); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; }; /** * Factory function that creates a callback object targetted at a * function with 8 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback8::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return typename XorpCallback8::RefPtr(new XorpFunctionCallback8B3(file, line, f, ba1, ba2, ba3)); } /** * @short Callback object for member methods with 8 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpMemberCallback8B3 : public XorpCallback8 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3) ; XorpMemberCallback8B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback8(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1, _ba2, _ba3); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for void member methods with 8 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpMemberCallback8B3 : public XorpCallback8 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3) ; XorpMemberCallback8B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback8(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1, _ba2, _ba3); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for safe member methods with 8 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpSafeMemberCallback8B3 : public XorpMemberCallback8B3, public SafeCallbackBase { typedef typename XorpMemberCallback8B3::M M; XorpSafeMemberCallback8B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpMemberCallback8B3(file, line, o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpSafeMemberCallback8B3() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback8B3::dispatch(a1, a2, a3, a4, a5, a6, a7, a8); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 8 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpSafeMemberCallback8B3 : public XorpMemberCallback8B3, public SafeCallbackBase { typedef typename XorpMemberCallback8B3::M M; XorpSafeMemberCallback8B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpMemberCallback8B3(file, line, o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpSafeMemberCallback8B3() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { if (valid()) { record_dispatch_enter(); XorpMemberCallback8B3::dispatch(a1, a2, a3, a4, a5, a6, a7, a8); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory8B3 { static XorpMemberCallback8B3* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpSafeMemberCallback8B3(file, line, o, p, ba1, ba2, ba3); } }; template struct XorpMemberCallbackFactory8B3 { static XorpMemberCallback8B3* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpMemberCallback8B3(file, line, o, p, ba1, ba2, ba3); }; }; /** * Factory function that creates a callback object targetted at a * member function with 8 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback8::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return XorpMemberCallbackFactory8B3::True>::make(file, line, o, p, ba1, ba2, ba3); } /** * Factory function that creates a callback object targetted at a * member function with 8 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback8::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return XorpMemberCallbackFactory8B3::True>::make(file, line, &o, p, ba1, ba2, ba3); } /** * @short Callback object for const member methods with 8 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstMemberCallback8B3 : public XorpCallback8 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3) const; XorpConstMemberCallback8B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback8(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1, _ba2, _ba3); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for void const member methods with 8 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstMemberCallback8B3 : public XorpCallback8 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3) const; XorpConstMemberCallback8B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback8(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1, _ba2, _ba3); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for const safe member methods with 8 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback8B3 : public XorpConstMemberCallback8B3, public SafeCallbackBase { typedef typename XorpConstMemberCallback8B3::M M; XorpConstSafeMemberCallback8B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpConstMemberCallback8B3(file, line, o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback8B3() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback8B3::dispatch(a1, a2, a3, a4, a5, a6, a7, a8); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 8 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback8B3 : public XorpConstMemberCallback8B3, public SafeCallbackBase { typedef typename XorpConstMemberCallback8B3::M M; XorpConstSafeMemberCallback8B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpConstMemberCallback8B3(file, line, o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback8B3() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback8B3::dispatch(a1, a2, a3, a4, a5, a6, a7, a8); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory8B3 { static XorpConstMemberCallback8B3* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpConstSafeMemberCallback8B3(file, line, o, p, ba1, ba2, ba3); } }; template struct XorpConstMemberCallbackFactory8B3 { static XorpConstMemberCallback8B3* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpConstMemberCallback8B3(file, line, o, p, ba1, ba2, ba3); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 8 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback8::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return XorpConstMemberCallbackFactory8B3::True>::make(file, line, o, p, ba1, ba2, ba3); } /** * Factory function that creates a callback object targetted at a * const member function with 8 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback8::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return XorpConstMemberCallbackFactory8B3::True>::make(file, line, &o, p, ba1, ba2, ba3); } /** * @short Callback object for functions with 8 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpFunctionCallback8B4 : public XorpCallback8 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4); XorpFunctionCallback8B4(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback8(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; }; /** * @short Callback object for void functions with 8 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpFunctionCallback8B4 : public XorpCallback8 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4); XorpFunctionCallback8B4(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback8(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; }; /** * Factory function that creates a callback object targetted at a * function with 8 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback8::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return typename XorpCallback8::RefPtr(new XorpFunctionCallback8B4(file, line, f, ba1, ba2, ba3, ba4)); } /** * @short Callback object for member methods with 8 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpMemberCallback8B4 : public XorpCallback8 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4) ; XorpMemberCallback8B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback8(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for void member methods with 8 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpMemberCallback8B4 : public XorpCallback8 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4) ; XorpMemberCallback8B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback8(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for safe member methods with 8 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpSafeMemberCallback8B4 : public XorpMemberCallback8B4, public SafeCallbackBase { typedef typename XorpMemberCallback8B4::M M; XorpSafeMemberCallback8B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpMemberCallback8B4(file, line, o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpSafeMemberCallback8B4() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback8B4::dispatch(a1, a2, a3, a4, a5, a6, a7, a8); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 8 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpSafeMemberCallback8B4 : public XorpMemberCallback8B4, public SafeCallbackBase { typedef typename XorpMemberCallback8B4::M M; XorpSafeMemberCallback8B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpMemberCallback8B4(file, line, o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpSafeMemberCallback8B4() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { if (valid()) { record_dispatch_enter(); XorpMemberCallback8B4::dispatch(a1, a2, a3, a4, a5, a6, a7, a8); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory8B4 { static XorpMemberCallback8B4* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpSafeMemberCallback8B4(file, line, o, p, ba1, ba2, ba3, ba4); } }; template struct XorpMemberCallbackFactory8B4 { static XorpMemberCallback8B4* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpMemberCallback8B4(file, line, o, p, ba1, ba2, ba3, ba4); }; }; /** * Factory function that creates a callback object targetted at a * member function with 8 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback8::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpMemberCallbackFactory8B4::True>::make(file, line, o, p, ba1, ba2, ba3, ba4); } /** * Factory function that creates a callback object targetted at a * member function with 8 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback8::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpMemberCallbackFactory8B4::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4); } /** * @short Callback object for const member methods with 8 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstMemberCallback8B4 : public XorpCallback8 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4) const; XorpConstMemberCallback8B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback8(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for void const member methods with 8 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstMemberCallback8B4 : public XorpCallback8 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4) const; XorpConstMemberCallback8B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback8(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for const safe member methods with 8 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback8B4 : public XorpConstMemberCallback8B4, public SafeCallbackBase { typedef typename XorpConstMemberCallback8B4::M M; XorpConstSafeMemberCallback8B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpConstMemberCallback8B4(file, line, o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback8B4() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback8B4::dispatch(a1, a2, a3, a4, a5, a6, a7, a8); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 8 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback8B4 : public XorpConstMemberCallback8B4, public SafeCallbackBase { typedef typename XorpConstMemberCallback8B4::M M; XorpConstSafeMemberCallback8B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpConstMemberCallback8B4(file, line, o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback8B4() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback8B4::dispatch(a1, a2, a3, a4, a5, a6, a7, a8); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory8B4 { static XorpConstMemberCallback8B4* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpConstSafeMemberCallback8B4(file, line, o, p, ba1, ba2, ba3, ba4); } }; template struct XorpConstMemberCallbackFactory8B4 { static XorpConstMemberCallback8B4* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpConstMemberCallback8B4(file, line, o, p, ba1, ba2, ba3, ba4); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 8 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback8::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpConstMemberCallbackFactory8B4::True>::make(file, line, o, p, ba1, ba2, ba3, ba4); } /** * Factory function that creates a callback object targetted at a * const member function with 8 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback8::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpConstMemberCallbackFactory8B4::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4); } /** * @short Callback object for functions with 8 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpFunctionCallback8B5 : public XorpCallback8 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4, BA5); XorpFunctionCallback8B5(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback8(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; }; /** * @short Callback object for void functions with 8 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpFunctionCallback8B5 : public XorpCallback8 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4, BA5); XorpFunctionCallback8B5(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback8(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; }; /** * Factory function that creates a callback object targetted at a * function with 8 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback8::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return typename XorpCallback8::RefPtr(new XorpFunctionCallback8B5(file, line, f, ba1, ba2, ba3, ba4, ba5)); } /** * @short Callback object for member methods with 8 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpMemberCallback8B5 : public XorpCallback8 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4, BA5) ; XorpMemberCallback8B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback8(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for void member methods with 8 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpMemberCallback8B5 : public XorpCallback8 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4, BA5) ; XorpMemberCallback8B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback8(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for safe member methods with 8 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpSafeMemberCallback8B5 : public XorpMemberCallback8B5, public SafeCallbackBase { typedef typename XorpMemberCallback8B5::M M; XorpSafeMemberCallback8B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpMemberCallback8B5(file, line, o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpSafeMemberCallback8B5() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback8B5::dispatch(a1, a2, a3, a4, a5, a6, a7, a8); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 8 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpSafeMemberCallback8B5 : public XorpMemberCallback8B5, public SafeCallbackBase { typedef typename XorpMemberCallback8B5::M M; XorpSafeMemberCallback8B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpMemberCallback8B5(file, line, o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpSafeMemberCallback8B5() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { if (valid()) { record_dispatch_enter(); XorpMemberCallback8B5::dispatch(a1, a2, a3, a4, a5, a6, a7, a8); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory8B5 { static XorpMemberCallback8B5* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpSafeMemberCallback8B5(file, line, o, p, ba1, ba2, ba3, ba4, ba5); } }; template struct XorpMemberCallbackFactory8B5 { static XorpMemberCallback8B5* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpMemberCallback8B5(file, line, o, p, ba1, ba2, ba3, ba4, ba5); }; }; /** * Factory function that creates a callback object targetted at a * member function with 8 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback8::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpMemberCallbackFactory8B5::True>::make(file, line, o, p, ba1, ba2, ba3, ba4, ba5); } /** * Factory function that creates a callback object targetted at a * member function with 8 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback8::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpMemberCallbackFactory8B5::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4, ba5); } /** * @short Callback object for const member methods with 8 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstMemberCallback8B5 : public XorpCallback8 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4, BA5) const; XorpConstMemberCallback8B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback8(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for void const member methods with 8 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstMemberCallback8B5 : public XorpCallback8 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4, BA5) const; XorpConstMemberCallback8B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback8(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for const safe member methods with 8 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback8B5 : public XorpConstMemberCallback8B5, public SafeCallbackBase { typedef typename XorpConstMemberCallback8B5::M M; XorpConstSafeMemberCallback8B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpConstMemberCallback8B5(file, line, o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback8B5() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback8B5::dispatch(a1, a2, a3, a4, a5, a6, a7, a8); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 8 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback8B5 : public XorpConstMemberCallback8B5, public SafeCallbackBase { typedef typename XorpConstMemberCallback8B5::M M; XorpConstSafeMemberCallback8B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpConstMemberCallback8B5(file, line, o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback8B5() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback8B5::dispatch(a1, a2, a3, a4, a5, a6, a7, a8); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory8B5 { static XorpConstMemberCallback8B5* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpConstSafeMemberCallback8B5(file, line, o, p, ba1, ba2, ba3, ba4, ba5); } }; template struct XorpConstMemberCallbackFactory8B5 { static XorpConstMemberCallback8B5* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpConstMemberCallback8B5(file, line, o, p, ba1, ba2, ba3, ba4, ba5); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 8 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback8::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpConstMemberCallbackFactory8B5::True>::make(file, line, o, p, ba1, ba2, ba3, ba4, ba5); } /** * Factory function that creates a callback object targetted at a * const member function with 8 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback8::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpConstMemberCallbackFactory8B5::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4, ba5); } /** * @short Callback object for functions with 8 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpFunctionCallback8B6 : public XorpCallback8 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4, BA5, BA6); XorpFunctionCallback8B6(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback8(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; BA6 _ba6; }; /** * @short Callback object for void functions with 8 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpFunctionCallback8B6 : public XorpCallback8 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4, BA5, BA6); XorpFunctionCallback8B6(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback8(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; BA6 _ba6; }; /** * Factory function that creates a callback object targetted at a * function with 8 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback8::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return typename XorpCallback8::RefPtr(new XorpFunctionCallback8B6(file, line, f, ba1, ba2, ba3, ba4, ba5, ba6)); } /** * @short Callback object for member methods with 8 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpMemberCallback8B6 : public XorpCallback8 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4, BA5, BA6) ; XorpMemberCallback8B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback8(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for void member methods with 8 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpMemberCallback8B6 : public XorpCallback8 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4, BA5, BA6) ; XorpMemberCallback8B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback8(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for safe member methods with 8 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpSafeMemberCallback8B6 : public XorpMemberCallback8B6, public SafeCallbackBase { typedef typename XorpMemberCallback8B6::M M; XorpSafeMemberCallback8B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpMemberCallback8B6(file, line, o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpSafeMemberCallback8B6() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback8B6::dispatch(a1, a2, a3, a4, a5, a6, a7, a8); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 8 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpSafeMemberCallback8B6 : public XorpMemberCallback8B6, public SafeCallbackBase { typedef typename XorpMemberCallback8B6::M M; XorpSafeMemberCallback8B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpMemberCallback8B6(file, line, o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpSafeMemberCallback8B6() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { if (valid()) { record_dispatch_enter(); XorpMemberCallback8B6::dispatch(a1, a2, a3, a4, a5, a6, a7, a8); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory8B6 { static XorpMemberCallback8B6* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpSafeMemberCallback8B6(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); } }; template struct XorpMemberCallbackFactory8B6 { static XorpMemberCallback8B6* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpMemberCallback8B6(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); }; }; /** * Factory function that creates a callback object targetted at a * member function with 8 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback8::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpMemberCallbackFactory8B6::True>::make(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * Factory function that creates a callback object targetted at a * member function with 8 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback8::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpMemberCallbackFactory8B6::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * @short Callback object for const member methods with 8 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstMemberCallback8B6 : public XorpCallback8 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4, BA5, BA6) const; XorpConstMemberCallback8B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback8(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for void const member methods with 8 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstMemberCallback8B6 : public XorpCallback8 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4, BA5, BA6) const; XorpConstMemberCallback8B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback8(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for const safe member methods with 8 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback8B6 : public XorpConstMemberCallback8B6, public SafeCallbackBase { typedef typename XorpConstMemberCallback8B6::M M; XorpConstSafeMemberCallback8B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpConstMemberCallback8B6(file, line, o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback8B6() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback8B6::dispatch(a1, a2, a3, a4, a5, a6, a7, a8); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 8 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback8B6 : public XorpConstMemberCallback8B6, public SafeCallbackBase { typedef typename XorpConstMemberCallback8B6::M M; XorpConstSafeMemberCallback8B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpConstMemberCallback8B6(file, line, o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback8B6() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback8B6::dispatch(a1, a2, a3, a4, a5, a6, a7, a8); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory8B6 { static XorpConstMemberCallback8B6* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpConstSafeMemberCallback8B6(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); } }; template struct XorpConstMemberCallbackFactory8B6 { static XorpConstMemberCallback8B6* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpConstMemberCallback8B6(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 8 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback8::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpConstMemberCallbackFactory8B6::True>::make(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * Factory function that creates a callback object targetted at a * const member function with 8 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback8::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpConstMemberCallbackFactory8B6::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /////////////////////////////////////////////////////////////////////////////// // // Code relating to callbacks with 9 late args // /** * @short Base class for callbacks with 9 dispatch time args. */ template struct XorpCallback9 { typedef ref_ptr RefPtr; XorpCallback9(const char* file, int line) : _file(file), _line(line) {} virtual ~XorpCallback9() {} virtual R dispatch(A1, A2, A3, A4, A5, A6, A7, A8, A9) = 0; const char* file() const { return _file; } int line() const { return _line; } private: const char* _file; int _line; }; /** * @short Callback object for functions with 9 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpFunctionCallback9B0 : public XorpCallback9 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9); XorpFunctionCallback9B0(const char* file, int line, F f) : XorpCallback9(file, line), _f(f) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9); record_dispatch_leave(); return r; } protected: F _f; }; /** * @short Callback object for void functions with 9 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpFunctionCallback9B0 : public XorpCallback9 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9); XorpFunctionCallback9B0(const char* file, int line, F f) : XorpCallback9(file, line), _f(f) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9); record_dispatch_leave(); } protected: F _f; }; /** * Factory function that creates a callback object targetted at a * function with 9 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback9::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9)) { return typename XorpCallback9::RefPtr(new XorpFunctionCallback9B0(file, line, f)); } /** * @short Callback object for member methods with 9 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpMemberCallback9B0 : public XorpCallback9 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9) ; XorpMemberCallback9B0(const char* file, int line, O* o, M m) : XorpCallback9(file, line), _o(o), _m(m) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for void member methods with 9 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpMemberCallback9B0 : public XorpCallback9 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9) ; XorpMemberCallback9B0(const char* file, int line, O* o, M m) : XorpCallback9(file, line), _o(o), _m(m) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for safe member methods with 9 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpSafeMemberCallback9B0 : public XorpMemberCallback9B0, public SafeCallbackBase { typedef typename XorpMemberCallback9B0::M M; XorpSafeMemberCallback9B0(const char* file, int line, O* o, M m) : XorpMemberCallback9B0(file, line, o, m), SafeCallbackBase(o) {} ~XorpSafeMemberCallback9B0() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback9B0::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 9 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpSafeMemberCallback9B0 : public XorpMemberCallback9B0, public SafeCallbackBase { typedef typename XorpMemberCallback9B0::M M; XorpSafeMemberCallback9B0(const char* file, int line, O* o, M m) : XorpMemberCallback9B0(file, line, o, m), SafeCallbackBase(o) {} ~XorpSafeMemberCallback9B0() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { if (valid()) { record_dispatch_enter(); XorpMemberCallback9B0::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory9B0 { static XorpMemberCallback9B0* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9)) { return new XorpSafeMemberCallback9B0(file, line, o, p); } }; template struct XorpMemberCallbackFactory9B0 { static XorpMemberCallback9B0* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9)) { return new XorpMemberCallback9B0(file, line, o, p); }; }; /** * Factory function that creates a callback object targetted at a * member function with 9 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback9::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9)) { return XorpMemberCallbackFactory9B0::True>::make(file, line, o, p); } /** * Factory function that creates a callback object targetted at a * member function with 9 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback9::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9)) { return XorpMemberCallbackFactory9B0::True>::make(file, line, &o, p); } /** * @short Callback object for const member methods with 9 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstMemberCallback9B0 : public XorpCallback9 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9) const; XorpConstMemberCallback9B0(const char* file, int line, O* o, M m) : XorpCallback9(file, line), _o(o), _m(m) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for void const member methods with 9 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstMemberCallback9B0 : public XorpCallback9 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9) const; XorpConstMemberCallback9B0(const char* file, int line, O* o, M m) : XorpCallback9(file, line), _o(o), _m(m) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for const safe member methods with 9 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback9B0 : public XorpConstMemberCallback9B0, public SafeCallbackBase { typedef typename XorpConstMemberCallback9B0::M M; XorpConstSafeMemberCallback9B0(const char* file, int line, O* o, M m) : XorpConstMemberCallback9B0(file, line, o, m), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback9B0() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback9B0::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 9 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback9B0 : public XorpConstMemberCallback9B0, public SafeCallbackBase { typedef typename XorpConstMemberCallback9B0::M M; XorpConstSafeMemberCallback9B0(const char* file, int line, O* o, M m) : XorpConstMemberCallback9B0(file, line, o, m), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback9B0() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback9B0::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory9B0 { static XorpConstMemberCallback9B0* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9) const) { return new XorpConstSafeMemberCallback9B0(file, line, o, p); } }; template struct XorpConstMemberCallbackFactory9B0 { static XorpConstMemberCallback9B0* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9) const) { return new XorpConstMemberCallback9B0(file, line, o, p); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 9 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback9::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9) const) { return XorpConstMemberCallbackFactory9B0::True>::make(file, line, o, p); } /** * Factory function that creates a callback object targetted at a * const member function with 9 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback9::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9) const) { return XorpConstMemberCallbackFactory9B0::True>::make(file, line, &o, p); } /** * @short Callback object for functions with 9 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpFunctionCallback9B1 : public XorpCallback9 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1); XorpFunctionCallback9B1(const char* file, int line, F f, BA1 ba1) : XorpCallback9(file, line), _f(f), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; }; /** * @short Callback object for void functions with 9 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpFunctionCallback9B1 : public XorpCallback9 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1); XorpFunctionCallback9B1(const char* file, int line, F f, BA1 ba1) : XorpCallback9(file, line), _f(f), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1); record_dispatch_leave(); } protected: F _f; BA1 _ba1; }; /** * Factory function that creates a callback object targetted at a * function with 9 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback9::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1), BA1 ba1) { return typename XorpCallback9::RefPtr(new XorpFunctionCallback9B1(file, line, f, ba1)); } /** * @short Callback object for member methods with 9 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpMemberCallback9B1 : public XorpCallback9 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1) ; XorpMemberCallback9B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpCallback9(file, line), _o(o), _m(m), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for void member methods with 9 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpMemberCallback9B1 : public XorpCallback9 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1) ; XorpMemberCallback9B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpCallback9(file, line), _o(o), _m(m), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for safe member methods with 9 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpSafeMemberCallback9B1 : public XorpMemberCallback9B1, public SafeCallbackBase { typedef typename XorpMemberCallback9B1::M M; XorpSafeMemberCallback9B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpMemberCallback9B1(file, line, o, m, ba1), SafeCallbackBase(o) {} ~XorpSafeMemberCallback9B1() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback9B1::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 9 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpSafeMemberCallback9B1 : public XorpMemberCallback9B1, public SafeCallbackBase { typedef typename XorpMemberCallback9B1::M M; XorpSafeMemberCallback9B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpMemberCallback9B1(file, line, o, m, ba1), SafeCallbackBase(o) {} ~XorpSafeMemberCallback9B1() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { if (valid()) { record_dispatch_enter(); XorpMemberCallback9B1::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory9B1 { static XorpMemberCallback9B1* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1), BA1 ba1) { return new XorpSafeMemberCallback9B1(file, line, o, p, ba1); } }; template struct XorpMemberCallbackFactory9B1 { static XorpMemberCallback9B1* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1), BA1 ba1) { return new XorpMemberCallback9B1(file, line, o, p, ba1); }; }; /** * Factory function that creates a callback object targetted at a * member function with 9 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback9::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1), BA1 ba1) { return XorpMemberCallbackFactory9B1::True>::make(file, line, o, p, ba1); } /** * Factory function that creates a callback object targetted at a * member function with 9 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback9::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1), BA1 ba1) { return XorpMemberCallbackFactory9B1::True>::make(file, line, &o, p, ba1); } /** * @short Callback object for const member methods with 9 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstMemberCallback9B1 : public XorpCallback9 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1) const; XorpConstMemberCallback9B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpCallback9(file, line), _o(o), _m(m), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for void const member methods with 9 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstMemberCallback9B1 : public XorpCallback9 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1) const; XorpConstMemberCallback9B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpCallback9(file, line), _o(o), _m(m), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for const safe member methods with 9 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback9B1 : public XorpConstMemberCallback9B1, public SafeCallbackBase { typedef typename XorpConstMemberCallback9B1::M M; XorpConstSafeMemberCallback9B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpConstMemberCallback9B1(file, line, o, m, ba1), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback9B1() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback9B1::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 9 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback9B1 : public XorpConstMemberCallback9B1, public SafeCallbackBase { typedef typename XorpConstMemberCallback9B1::M M; XorpConstSafeMemberCallback9B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpConstMemberCallback9B1(file, line, o, m, ba1), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback9B1() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback9B1::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory9B1 { static XorpConstMemberCallback9B1* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1) const, BA1 ba1) { return new XorpConstSafeMemberCallback9B1(file, line, o, p, ba1); } }; template struct XorpConstMemberCallbackFactory9B1 { static XorpConstMemberCallback9B1* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1) const, BA1 ba1) { return new XorpConstMemberCallback9B1(file, line, o, p, ba1); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 9 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback9::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1) const, BA1 ba1) { return XorpConstMemberCallbackFactory9B1::True>::make(file, line, o, p, ba1); } /** * Factory function that creates a callback object targetted at a * const member function with 9 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback9::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1) const, BA1 ba1) { return XorpConstMemberCallbackFactory9B1::True>::make(file, line, &o, p, ba1); } /** * @short Callback object for functions with 9 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpFunctionCallback9B2 : public XorpCallback9 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2); XorpFunctionCallback9B2(const char* file, int line, F f, BA1 ba1, BA2 ba2) : XorpCallback9(file, line), _f(f), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1, _ba2); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; }; /** * @short Callback object for void functions with 9 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpFunctionCallback9B2 : public XorpCallback9 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2); XorpFunctionCallback9B2(const char* file, int line, F f, BA1 ba1, BA2 ba2) : XorpCallback9(file, line), _f(f), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1, _ba2); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; }; /** * Factory function that creates a callback object targetted at a * function with 9 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback9::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2), BA1 ba1, BA2 ba2) { return typename XorpCallback9::RefPtr(new XorpFunctionCallback9B2(file, line, f, ba1, ba2)); } /** * @short Callback object for member methods with 9 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpMemberCallback9B2 : public XorpCallback9 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2) ; XorpMemberCallback9B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback9(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1, _ba2); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for void member methods with 9 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpMemberCallback9B2 : public XorpCallback9 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2) ; XorpMemberCallback9B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback9(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1, _ba2); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for safe member methods with 9 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpSafeMemberCallback9B2 : public XorpMemberCallback9B2, public SafeCallbackBase { typedef typename XorpMemberCallback9B2::M M; XorpSafeMemberCallback9B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpMemberCallback9B2(file, line, o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpSafeMemberCallback9B2() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback9B2::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 9 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpSafeMemberCallback9B2 : public XorpMemberCallback9B2, public SafeCallbackBase { typedef typename XorpMemberCallback9B2::M M; XorpSafeMemberCallback9B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpMemberCallback9B2(file, line, o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpSafeMemberCallback9B2() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { if (valid()) { record_dispatch_enter(); XorpMemberCallback9B2::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory9B2 { static XorpMemberCallback9B2* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2), BA1 ba1, BA2 ba2) { return new XorpSafeMemberCallback9B2(file, line, o, p, ba1, ba2); } }; template struct XorpMemberCallbackFactory9B2 { static XorpMemberCallback9B2* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2), BA1 ba1, BA2 ba2) { return new XorpMemberCallback9B2(file, line, o, p, ba1, ba2); }; }; /** * Factory function that creates a callback object targetted at a * member function with 9 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback9::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2), BA1 ba1, BA2 ba2) { return XorpMemberCallbackFactory9B2::True>::make(file, line, o, p, ba1, ba2); } /** * Factory function that creates a callback object targetted at a * member function with 9 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback9::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2), BA1 ba1, BA2 ba2) { return XorpMemberCallbackFactory9B2::True>::make(file, line, &o, p, ba1, ba2); } /** * @short Callback object for const member methods with 9 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstMemberCallback9B2 : public XorpCallback9 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2) const; XorpConstMemberCallback9B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback9(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1, _ba2); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for void const member methods with 9 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstMemberCallback9B2 : public XorpCallback9 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2) const; XorpConstMemberCallback9B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback9(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1, _ba2); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for const safe member methods with 9 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback9B2 : public XorpConstMemberCallback9B2, public SafeCallbackBase { typedef typename XorpConstMemberCallback9B2::M M; XorpConstSafeMemberCallback9B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpConstMemberCallback9B2(file, line, o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback9B2() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback9B2::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 9 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback9B2 : public XorpConstMemberCallback9B2, public SafeCallbackBase { typedef typename XorpConstMemberCallback9B2::M M; XorpConstSafeMemberCallback9B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpConstMemberCallback9B2(file, line, o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback9B2() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback9B2::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory9B2 { static XorpConstMemberCallback9B2* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2) const, BA1 ba1, BA2 ba2) { return new XorpConstSafeMemberCallback9B2(file, line, o, p, ba1, ba2); } }; template struct XorpConstMemberCallbackFactory9B2 { static XorpConstMemberCallback9B2* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2) const, BA1 ba1, BA2 ba2) { return new XorpConstMemberCallback9B2(file, line, o, p, ba1, ba2); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 9 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback9::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2) const, BA1 ba1, BA2 ba2) { return XorpConstMemberCallbackFactory9B2::True>::make(file, line, o, p, ba1, ba2); } /** * Factory function that creates a callback object targetted at a * const member function with 9 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback9::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2) const, BA1 ba1, BA2 ba2) { return XorpConstMemberCallbackFactory9B2::True>::make(file, line, &o, p, ba1, ba2); } /** * @short Callback object for functions with 9 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpFunctionCallback9B3 : public XorpCallback9 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3); XorpFunctionCallback9B3(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback9(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1, _ba2, _ba3); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; }; /** * @short Callback object for void functions with 9 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpFunctionCallback9B3 : public XorpCallback9 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3); XorpFunctionCallback9B3(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback9(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1, _ba2, _ba3); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; }; /** * Factory function that creates a callback object targetted at a * function with 9 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback9::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return typename XorpCallback9::RefPtr(new XorpFunctionCallback9B3(file, line, f, ba1, ba2, ba3)); } /** * @short Callback object for member methods with 9 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpMemberCallback9B3 : public XorpCallback9 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3) ; XorpMemberCallback9B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback9(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1, _ba2, _ba3); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for void member methods with 9 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpMemberCallback9B3 : public XorpCallback9 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3) ; XorpMemberCallback9B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback9(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1, _ba2, _ba3); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for safe member methods with 9 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpSafeMemberCallback9B3 : public XorpMemberCallback9B3, public SafeCallbackBase { typedef typename XorpMemberCallback9B3::M M; XorpSafeMemberCallback9B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpMemberCallback9B3(file, line, o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpSafeMemberCallback9B3() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback9B3::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 9 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpSafeMemberCallback9B3 : public XorpMemberCallback9B3, public SafeCallbackBase { typedef typename XorpMemberCallback9B3::M M; XorpSafeMemberCallback9B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpMemberCallback9B3(file, line, o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpSafeMemberCallback9B3() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { if (valid()) { record_dispatch_enter(); XorpMemberCallback9B3::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory9B3 { static XorpMemberCallback9B3* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpSafeMemberCallback9B3(file, line, o, p, ba1, ba2, ba3); } }; template struct XorpMemberCallbackFactory9B3 { static XorpMemberCallback9B3* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpMemberCallback9B3(file, line, o, p, ba1, ba2, ba3); }; }; /** * Factory function that creates a callback object targetted at a * member function with 9 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback9::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return XorpMemberCallbackFactory9B3::True>::make(file, line, o, p, ba1, ba2, ba3); } /** * Factory function that creates a callback object targetted at a * member function with 9 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback9::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return XorpMemberCallbackFactory9B3::True>::make(file, line, &o, p, ba1, ba2, ba3); } /** * @short Callback object for const member methods with 9 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstMemberCallback9B3 : public XorpCallback9 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3) const; XorpConstMemberCallback9B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback9(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1, _ba2, _ba3); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for void const member methods with 9 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstMemberCallback9B3 : public XorpCallback9 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3) const; XorpConstMemberCallback9B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback9(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1, _ba2, _ba3); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for const safe member methods with 9 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback9B3 : public XorpConstMemberCallback9B3, public SafeCallbackBase { typedef typename XorpConstMemberCallback9B3::M M; XorpConstSafeMemberCallback9B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpConstMemberCallback9B3(file, line, o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback9B3() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback9B3::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 9 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback9B3 : public XorpConstMemberCallback9B3, public SafeCallbackBase { typedef typename XorpConstMemberCallback9B3::M M; XorpConstSafeMemberCallback9B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpConstMemberCallback9B3(file, line, o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback9B3() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback9B3::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory9B3 { static XorpConstMemberCallback9B3* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpConstSafeMemberCallback9B3(file, line, o, p, ba1, ba2, ba3); } }; template struct XorpConstMemberCallbackFactory9B3 { static XorpConstMemberCallback9B3* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpConstMemberCallback9B3(file, line, o, p, ba1, ba2, ba3); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 9 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback9::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return XorpConstMemberCallbackFactory9B3::True>::make(file, line, o, p, ba1, ba2, ba3); } /** * Factory function that creates a callback object targetted at a * const member function with 9 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback9::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return XorpConstMemberCallbackFactory9B3::True>::make(file, line, &o, p, ba1, ba2, ba3); } /** * @short Callback object for functions with 9 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpFunctionCallback9B4 : public XorpCallback9 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4); XorpFunctionCallback9B4(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback9(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; }; /** * @short Callback object for void functions with 9 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpFunctionCallback9B4 : public XorpCallback9 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4); XorpFunctionCallback9B4(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback9(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; }; /** * Factory function that creates a callback object targetted at a * function with 9 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback9::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return typename XorpCallback9::RefPtr(new XorpFunctionCallback9B4(file, line, f, ba1, ba2, ba3, ba4)); } /** * @short Callback object for member methods with 9 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpMemberCallback9B4 : public XorpCallback9 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4) ; XorpMemberCallback9B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback9(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for void member methods with 9 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpMemberCallback9B4 : public XorpCallback9 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4) ; XorpMemberCallback9B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback9(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for safe member methods with 9 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpSafeMemberCallback9B4 : public XorpMemberCallback9B4, public SafeCallbackBase { typedef typename XorpMemberCallback9B4::M M; XorpSafeMemberCallback9B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpMemberCallback9B4(file, line, o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpSafeMemberCallback9B4() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback9B4::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 9 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpSafeMemberCallback9B4 : public XorpMemberCallback9B4, public SafeCallbackBase { typedef typename XorpMemberCallback9B4::M M; XorpSafeMemberCallback9B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpMemberCallback9B4(file, line, o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpSafeMemberCallback9B4() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { if (valid()) { record_dispatch_enter(); XorpMemberCallback9B4::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory9B4 { static XorpMemberCallback9B4* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpSafeMemberCallback9B4(file, line, o, p, ba1, ba2, ba3, ba4); } }; template struct XorpMemberCallbackFactory9B4 { static XorpMemberCallback9B4* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpMemberCallback9B4(file, line, o, p, ba1, ba2, ba3, ba4); }; }; /** * Factory function that creates a callback object targetted at a * member function with 9 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback9::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpMemberCallbackFactory9B4::True>::make(file, line, o, p, ba1, ba2, ba3, ba4); } /** * Factory function that creates a callback object targetted at a * member function with 9 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback9::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpMemberCallbackFactory9B4::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4); } /** * @short Callback object for const member methods with 9 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstMemberCallback9B4 : public XorpCallback9 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4) const; XorpConstMemberCallback9B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback9(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for void const member methods with 9 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstMemberCallback9B4 : public XorpCallback9 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4) const; XorpConstMemberCallback9B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback9(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for const safe member methods with 9 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback9B4 : public XorpConstMemberCallback9B4, public SafeCallbackBase { typedef typename XorpConstMemberCallback9B4::M M; XorpConstSafeMemberCallback9B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpConstMemberCallback9B4(file, line, o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback9B4() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback9B4::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 9 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback9B4 : public XorpConstMemberCallback9B4, public SafeCallbackBase { typedef typename XorpConstMemberCallback9B4::M M; XorpConstSafeMemberCallback9B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpConstMemberCallback9B4(file, line, o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback9B4() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback9B4::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory9B4 { static XorpConstMemberCallback9B4* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpConstSafeMemberCallback9B4(file, line, o, p, ba1, ba2, ba3, ba4); } }; template struct XorpConstMemberCallbackFactory9B4 { static XorpConstMemberCallback9B4* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpConstMemberCallback9B4(file, line, o, p, ba1, ba2, ba3, ba4); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 9 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback9::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpConstMemberCallbackFactory9B4::True>::make(file, line, o, p, ba1, ba2, ba3, ba4); } /** * Factory function that creates a callback object targetted at a * const member function with 9 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback9::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpConstMemberCallbackFactory9B4::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4); } /** * @short Callback object for functions with 9 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpFunctionCallback9B5 : public XorpCallback9 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4, BA5); XorpFunctionCallback9B5(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback9(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; }; /** * @short Callback object for void functions with 9 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpFunctionCallback9B5 : public XorpCallback9 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4, BA5); XorpFunctionCallback9B5(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback9(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; }; /** * Factory function that creates a callback object targetted at a * function with 9 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback9::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return typename XorpCallback9::RefPtr(new XorpFunctionCallback9B5(file, line, f, ba1, ba2, ba3, ba4, ba5)); } /** * @short Callback object for member methods with 9 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpMemberCallback9B5 : public XorpCallback9 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4, BA5) ; XorpMemberCallback9B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback9(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for void member methods with 9 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpMemberCallback9B5 : public XorpCallback9 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4, BA5) ; XorpMemberCallback9B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback9(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for safe member methods with 9 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpSafeMemberCallback9B5 : public XorpMemberCallback9B5, public SafeCallbackBase { typedef typename XorpMemberCallback9B5::M M; XorpSafeMemberCallback9B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpMemberCallback9B5(file, line, o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpSafeMemberCallback9B5() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback9B5::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 9 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpSafeMemberCallback9B5 : public XorpMemberCallback9B5, public SafeCallbackBase { typedef typename XorpMemberCallback9B5::M M; XorpSafeMemberCallback9B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpMemberCallback9B5(file, line, o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpSafeMemberCallback9B5() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { if (valid()) { record_dispatch_enter(); XorpMemberCallback9B5::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory9B5 { static XorpMemberCallback9B5* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpSafeMemberCallback9B5(file, line, o, p, ba1, ba2, ba3, ba4, ba5); } }; template struct XorpMemberCallbackFactory9B5 { static XorpMemberCallback9B5* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpMemberCallback9B5(file, line, o, p, ba1, ba2, ba3, ba4, ba5); }; }; /** * Factory function that creates a callback object targetted at a * member function with 9 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback9::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpMemberCallbackFactory9B5::True>::make(file, line, o, p, ba1, ba2, ba3, ba4, ba5); } /** * Factory function that creates a callback object targetted at a * member function with 9 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback9::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpMemberCallbackFactory9B5::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4, ba5); } /** * @short Callback object for const member methods with 9 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstMemberCallback9B5 : public XorpCallback9 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4, BA5) const; XorpConstMemberCallback9B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback9(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for void const member methods with 9 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstMemberCallback9B5 : public XorpCallback9 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4, BA5) const; XorpConstMemberCallback9B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback9(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for const safe member methods with 9 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback9B5 : public XorpConstMemberCallback9B5, public SafeCallbackBase { typedef typename XorpConstMemberCallback9B5::M M; XorpConstSafeMemberCallback9B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpConstMemberCallback9B5(file, line, o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback9B5() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback9B5::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 9 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback9B5 : public XorpConstMemberCallback9B5, public SafeCallbackBase { typedef typename XorpConstMemberCallback9B5::M M; XorpConstSafeMemberCallback9B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpConstMemberCallback9B5(file, line, o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback9B5() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback9B5::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory9B5 { static XorpConstMemberCallback9B5* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpConstSafeMemberCallback9B5(file, line, o, p, ba1, ba2, ba3, ba4, ba5); } }; template struct XorpConstMemberCallbackFactory9B5 { static XorpConstMemberCallback9B5* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpConstMemberCallback9B5(file, line, o, p, ba1, ba2, ba3, ba4, ba5); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 9 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback9::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpConstMemberCallbackFactory9B5::True>::make(file, line, o, p, ba1, ba2, ba3, ba4, ba5); } /** * Factory function that creates a callback object targetted at a * const member function with 9 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback9::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpConstMemberCallbackFactory9B5::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4, ba5); } /** * @short Callback object for functions with 9 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpFunctionCallback9B6 : public XorpCallback9 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4, BA5, BA6); XorpFunctionCallback9B6(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback9(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; BA6 _ba6; }; /** * @short Callback object for void functions with 9 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpFunctionCallback9B6 : public XorpCallback9 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4, BA5, BA6); XorpFunctionCallback9B6(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback9(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; BA6 _ba6; }; /** * Factory function that creates a callback object targetted at a * function with 9 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback9::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return typename XorpCallback9::RefPtr(new XorpFunctionCallback9B6(file, line, f, ba1, ba2, ba3, ba4, ba5, ba6)); } /** * @short Callback object for member methods with 9 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpMemberCallback9B6 : public XorpCallback9 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4, BA5, BA6) ; XorpMemberCallback9B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback9(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for void member methods with 9 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpMemberCallback9B6 : public XorpCallback9 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4, BA5, BA6) ; XorpMemberCallback9B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback9(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for safe member methods with 9 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpSafeMemberCallback9B6 : public XorpMemberCallback9B6, public SafeCallbackBase { typedef typename XorpMemberCallback9B6::M M; XorpSafeMemberCallback9B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpMemberCallback9B6(file, line, o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpSafeMemberCallback9B6() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback9B6::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 9 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpSafeMemberCallback9B6 : public XorpMemberCallback9B6, public SafeCallbackBase { typedef typename XorpMemberCallback9B6::M M; XorpSafeMemberCallback9B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpMemberCallback9B6(file, line, o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpSafeMemberCallback9B6() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { if (valid()) { record_dispatch_enter(); XorpMemberCallback9B6::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory9B6 { static XorpMemberCallback9B6* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpSafeMemberCallback9B6(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); } }; template struct XorpMemberCallbackFactory9B6 { static XorpMemberCallback9B6* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpMemberCallback9B6(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); }; }; /** * Factory function that creates a callback object targetted at a * member function with 9 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback9::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpMemberCallbackFactory9B6::True>::make(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * Factory function that creates a callback object targetted at a * member function with 9 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback9::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpMemberCallbackFactory9B6::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * @short Callback object for const member methods with 9 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstMemberCallback9B6 : public XorpCallback9 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4, BA5, BA6) const; XorpConstMemberCallback9B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback9(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for void const member methods with 9 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstMemberCallback9B6 : public XorpCallback9 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4, BA5, BA6) const; XorpConstMemberCallback9B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback9(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for const safe member methods with 9 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback9B6 : public XorpConstMemberCallback9B6, public SafeCallbackBase { typedef typename XorpConstMemberCallback9B6::M M; XorpConstSafeMemberCallback9B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpConstMemberCallback9B6(file, line, o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback9B6() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback9B6::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 9 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback9B6 : public XorpConstMemberCallback9B6, public SafeCallbackBase { typedef typename XorpConstMemberCallback9B6::M M; XorpConstSafeMemberCallback9B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpConstMemberCallback9B6(file, line, o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback9B6() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback9B6::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory9B6 { static XorpConstMemberCallback9B6* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpConstSafeMemberCallback9B6(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); } }; template struct XorpConstMemberCallbackFactory9B6 { static XorpConstMemberCallback9B6* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpConstMemberCallback9B6(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 9 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback9::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpConstMemberCallbackFactory9B6::True>::make(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * Factory function that creates a callback object targetted at a * const member function with 9 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback9::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpConstMemberCallbackFactory9B6::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /////////////////////////////////////////////////////////////////////////////// // // Code relating to callbacks with 10 late args // /** * @short Base class for callbacks with 10 dispatch time args. */ template struct XorpCallback10 { typedef ref_ptr RefPtr; XorpCallback10(const char* file, int line) : _file(file), _line(line) {} virtual ~XorpCallback10() {} virtual R dispatch(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10) = 0; const char* file() const { return _file; } int line() const { return _line; } private: const char* _file; int _line; }; /** * @short Callback object for functions with 10 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpFunctionCallback10B0 : public XorpCallback10 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10); XorpFunctionCallback10B0(const char* file, int line, F f) : XorpCallback10(file, line), _f(f) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); record_dispatch_leave(); return r; } protected: F _f; }; /** * @short Callback object for void functions with 10 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpFunctionCallback10B0 : public XorpCallback10 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10); XorpFunctionCallback10B0(const char* file, int line, F f) : XorpCallback10(file, line), _f(f) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); record_dispatch_leave(); } protected: F _f; }; /** * Factory function that creates a callback object targetted at a * function with 10 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback10::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10)) { return typename XorpCallback10::RefPtr(new XorpFunctionCallback10B0(file, line, f)); } /** * @short Callback object for member methods with 10 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpMemberCallback10B0 : public XorpCallback10 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10) ; XorpMemberCallback10B0(const char* file, int line, O* o, M m) : XorpCallback10(file, line), _o(o), _m(m) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for void member methods with 10 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpMemberCallback10B0 : public XorpCallback10 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10) ; XorpMemberCallback10B0(const char* file, int line, O* o, M m) : XorpCallback10(file, line), _o(o), _m(m) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for safe member methods with 10 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpSafeMemberCallback10B0 : public XorpMemberCallback10B0, public SafeCallbackBase { typedef typename XorpMemberCallback10B0::M M; XorpSafeMemberCallback10B0(const char* file, int line, O* o, M m) : XorpMemberCallback10B0(file, line, o, m), SafeCallbackBase(o) {} ~XorpSafeMemberCallback10B0() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback10B0::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 10 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpSafeMemberCallback10B0 : public XorpMemberCallback10B0, public SafeCallbackBase { typedef typename XorpMemberCallback10B0::M M; XorpSafeMemberCallback10B0(const char* file, int line, O* o, M m) : XorpMemberCallback10B0(file, line, o, m), SafeCallbackBase(o) {} ~XorpSafeMemberCallback10B0() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { if (valid()) { record_dispatch_enter(); XorpMemberCallback10B0::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory10B0 { static XorpMemberCallback10B0* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10)) { return new XorpSafeMemberCallback10B0(file, line, o, p); } }; template struct XorpMemberCallbackFactory10B0 { static XorpMemberCallback10B0* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10)) { return new XorpMemberCallback10B0(file, line, o, p); }; }; /** * Factory function that creates a callback object targetted at a * member function with 10 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback10::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10)) { return XorpMemberCallbackFactory10B0::True>::make(file, line, o, p); } /** * Factory function that creates a callback object targetted at a * member function with 10 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback10::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10)) { return XorpMemberCallbackFactory10B0::True>::make(file, line, &o, p); } /** * @short Callback object for const member methods with 10 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstMemberCallback10B0 : public XorpCallback10 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10) const; XorpConstMemberCallback10B0(const char* file, int line, O* o, M m) : XorpCallback10(file, line), _o(o), _m(m) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for void const member methods with 10 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstMemberCallback10B0 : public XorpCallback10 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10) const; XorpConstMemberCallback10B0(const char* file, int line, O* o, M m) : XorpCallback10(file, line), _o(o), _m(m) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for const safe member methods with 10 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback10B0 : public XorpConstMemberCallback10B0, public SafeCallbackBase { typedef typename XorpConstMemberCallback10B0::M M; XorpConstSafeMemberCallback10B0(const char* file, int line, O* o, M m) : XorpConstMemberCallback10B0(file, line, o, m), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback10B0() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback10B0::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 10 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback10B0 : public XorpConstMemberCallback10B0, public SafeCallbackBase { typedef typename XorpConstMemberCallback10B0::M M; XorpConstSafeMemberCallback10B0(const char* file, int line, O* o, M m) : XorpConstMemberCallback10B0(file, line, o, m), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback10B0() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback10B0::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory10B0 { static XorpConstMemberCallback10B0* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10) const) { return new XorpConstSafeMemberCallback10B0(file, line, o, p); } }; template struct XorpConstMemberCallbackFactory10B0 { static XorpConstMemberCallback10B0* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10) const) { return new XorpConstMemberCallback10B0(file, line, o, p); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 10 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback10::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10) const) { return XorpConstMemberCallbackFactory10B0::True>::make(file, line, o, p); } /** * Factory function that creates a callback object targetted at a * const member function with 10 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback10::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10) const) { return XorpConstMemberCallbackFactory10B0::True>::make(file, line, &o, p); } /** * @short Callback object for functions with 10 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpFunctionCallback10B1 : public XorpCallback10 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1); XorpFunctionCallback10B1(const char* file, int line, F f, BA1 ba1) : XorpCallback10(file, line), _f(f), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; }; /** * @short Callback object for void functions with 10 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpFunctionCallback10B1 : public XorpCallback10 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1); XorpFunctionCallback10B1(const char* file, int line, F f, BA1 ba1) : XorpCallback10(file, line), _f(f), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1); record_dispatch_leave(); } protected: F _f; BA1 _ba1; }; /** * Factory function that creates a callback object targetted at a * function with 10 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback10::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1), BA1 ba1) { return typename XorpCallback10::RefPtr(new XorpFunctionCallback10B1(file, line, f, ba1)); } /** * @short Callback object for member methods with 10 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpMemberCallback10B1 : public XorpCallback10 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1) ; XorpMemberCallback10B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpCallback10(file, line), _o(o), _m(m), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for void member methods with 10 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpMemberCallback10B1 : public XorpCallback10 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1) ; XorpMemberCallback10B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpCallback10(file, line), _o(o), _m(m), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for safe member methods with 10 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpSafeMemberCallback10B1 : public XorpMemberCallback10B1, public SafeCallbackBase { typedef typename XorpMemberCallback10B1::M M; XorpSafeMemberCallback10B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpMemberCallback10B1(file, line, o, m, ba1), SafeCallbackBase(o) {} ~XorpSafeMemberCallback10B1() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback10B1::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 10 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpSafeMemberCallback10B1 : public XorpMemberCallback10B1, public SafeCallbackBase { typedef typename XorpMemberCallback10B1::M M; XorpSafeMemberCallback10B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpMemberCallback10B1(file, line, o, m, ba1), SafeCallbackBase(o) {} ~XorpSafeMemberCallback10B1() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { if (valid()) { record_dispatch_enter(); XorpMemberCallback10B1::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory10B1 { static XorpMemberCallback10B1* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1), BA1 ba1) { return new XorpSafeMemberCallback10B1(file, line, o, p, ba1); } }; template struct XorpMemberCallbackFactory10B1 { static XorpMemberCallback10B1* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1), BA1 ba1) { return new XorpMemberCallback10B1(file, line, o, p, ba1); }; }; /** * Factory function that creates a callback object targetted at a * member function with 10 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback10::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1), BA1 ba1) { return XorpMemberCallbackFactory10B1::True>::make(file, line, o, p, ba1); } /** * Factory function that creates a callback object targetted at a * member function with 10 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback10::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1), BA1 ba1) { return XorpMemberCallbackFactory10B1::True>::make(file, line, &o, p, ba1); } /** * @short Callback object for const member methods with 10 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstMemberCallback10B1 : public XorpCallback10 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1) const; XorpConstMemberCallback10B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpCallback10(file, line), _o(o), _m(m), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for void const member methods with 10 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstMemberCallback10B1 : public XorpCallback10 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1) const; XorpConstMemberCallback10B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpCallback10(file, line), _o(o), _m(m), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for const safe member methods with 10 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback10B1 : public XorpConstMemberCallback10B1, public SafeCallbackBase { typedef typename XorpConstMemberCallback10B1::M M; XorpConstSafeMemberCallback10B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpConstMemberCallback10B1(file, line, o, m, ba1), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback10B1() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback10B1::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 10 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback10B1 : public XorpConstMemberCallback10B1, public SafeCallbackBase { typedef typename XorpConstMemberCallback10B1::M M; XorpConstSafeMemberCallback10B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpConstMemberCallback10B1(file, line, o, m, ba1), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback10B1() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback10B1::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory10B1 { static XorpConstMemberCallback10B1* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1) const, BA1 ba1) { return new XorpConstSafeMemberCallback10B1(file, line, o, p, ba1); } }; template struct XorpConstMemberCallbackFactory10B1 { static XorpConstMemberCallback10B1* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1) const, BA1 ba1) { return new XorpConstMemberCallback10B1(file, line, o, p, ba1); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 10 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback10::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1) const, BA1 ba1) { return XorpConstMemberCallbackFactory10B1::True>::make(file, line, o, p, ba1); } /** * Factory function that creates a callback object targetted at a * const member function with 10 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback10::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1) const, BA1 ba1) { return XorpConstMemberCallbackFactory10B1::True>::make(file, line, &o, p, ba1); } /** * @short Callback object for functions with 10 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpFunctionCallback10B2 : public XorpCallback10 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2); XorpFunctionCallback10B2(const char* file, int line, F f, BA1 ba1, BA2 ba2) : XorpCallback10(file, line), _f(f), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1, _ba2); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; }; /** * @short Callback object for void functions with 10 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpFunctionCallback10B2 : public XorpCallback10 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2); XorpFunctionCallback10B2(const char* file, int line, F f, BA1 ba1, BA2 ba2) : XorpCallback10(file, line), _f(f), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1, _ba2); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; }; /** * Factory function that creates a callback object targetted at a * function with 10 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback10::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2), BA1 ba1, BA2 ba2) { return typename XorpCallback10::RefPtr(new XorpFunctionCallback10B2(file, line, f, ba1, ba2)); } /** * @short Callback object for member methods with 10 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpMemberCallback10B2 : public XorpCallback10 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2) ; XorpMemberCallback10B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback10(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1, _ba2); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for void member methods with 10 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpMemberCallback10B2 : public XorpCallback10 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2) ; XorpMemberCallback10B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback10(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1, _ba2); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for safe member methods with 10 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpSafeMemberCallback10B2 : public XorpMemberCallback10B2, public SafeCallbackBase { typedef typename XorpMemberCallback10B2::M M; XorpSafeMemberCallback10B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpMemberCallback10B2(file, line, o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpSafeMemberCallback10B2() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback10B2::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 10 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpSafeMemberCallback10B2 : public XorpMemberCallback10B2, public SafeCallbackBase { typedef typename XorpMemberCallback10B2::M M; XorpSafeMemberCallback10B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpMemberCallback10B2(file, line, o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpSafeMemberCallback10B2() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { if (valid()) { record_dispatch_enter(); XorpMemberCallback10B2::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory10B2 { static XorpMemberCallback10B2* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2), BA1 ba1, BA2 ba2) { return new XorpSafeMemberCallback10B2(file, line, o, p, ba1, ba2); } }; template struct XorpMemberCallbackFactory10B2 { static XorpMemberCallback10B2* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2), BA1 ba1, BA2 ba2) { return new XorpMemberCallback10B2(file, line, o, p, ba1, ba2); }; }; /** * Factory function that creates a callback object targetted at a * member function with 10 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback10::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2), BA1 ba1, BA2 ba2) { return XorpMemberCallbackFactory10B2::True>::make(file, line, o, p, ba1, ba2); } /** * Factory function that creates a callback object targetted at a * member function with 10 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback10::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2), BA1 ba1, BA2 ba2) { return XorpMemberCallbackFactory10B2::True>::make(file, line, &o, p, ba1, ba2); } /** * @short Callback object for const member methods with 10 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstMemberCallback10B2 : public XorpCallback10 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2) const; XorpConstMemberCallback10B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback10(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1, _ba2); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for void const member methods with 10 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstMemberCallback10B2 : public XorpCallback10 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2) const; XorpConstMemberCallback10B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback10(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1, _ba2); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for const safe member methods with 10 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback10B2 : public XorpConstMemberCallback10B2, public SafeCallbackBase { typedef typename XorpConstMemberCallback10B2::M M; XorpConstSafeMemberCallback10B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpConstMemberCallback10B2(file, line, o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback10B2() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback10B2::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 10 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback10B2 : public XorpConstMemberCallback10B2, public SafeCallbackBase { typedef typename XorpConstMemberCallback10B2::M M; XorpConstSafeMemberCallback10B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpConstMemberCallback10B2(file, line, o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback10B2() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback10B2::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory10B2 { static XorpConstMemberCallback10B2* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2) const, BA1 ba1, BA2 ba2) { return new XorpConstSafeMemberCallback10B2(file, line, o, p, ba1, ba2); } }; template struct XorpConstMemberCallbackFactory10B2 { static XorpConstMemberCallback10B2* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2) const, BA1 ba1, BA2 ba2) { return new XorpConstMemberCallback10B2(file, line, o, p, ba1, ba2); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 10 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback10::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2) const, BA1 ba1, BA2 ba2) { return XorpConstMemberCallbackFactory10B2::True>::make(file, line, o, p, ba1, ba2); } /** * Factory function that creates a callback object targetted at a * const member function with 10 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback10::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2) const, BA1 ba1, BA2 ba2) { return XorpConstMemberCallbackFactory10B2::True>::make(file, line, &o, p, ba1, ba2); } /** * @short Callback object for functions with 10 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpFunctionCallback10B3 : public XorpCallback10 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3); XorpFunctionCallback10B3(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback10(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1, _ba2, _ba3); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; }; /** * @short Callback object for void functions with 10 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpFunctionCallback10B3 : public XorpCallback10 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3); XorpFunctionCallback10B3(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback10(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1, _ba2, _ba3); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; }; /** * Factory function that creates a callback object targetted at a * function with 10 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback10::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return typename XorpCallback10::RefPtr(new XorpFunctionCallback10B3(file, line, f, ba1, ba2, ba3)); } /** * @short Callback object for member methods with 10 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpMemberCallback10B3 : public XorpCallback10 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3) ; XorpMemberCallback10B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback10(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1, _ba2, _ba3); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for void member methods with 10 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpMemberCallback10B3 : public XorpCallback10 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3) ; XorpMemberCallback10B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback10(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1, _ba2, _ba3); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for safe member methods with 10 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpSafeMemberCallback10B3 : public XorpMemberCallback10B3, public SafeCallbackBase { typedef typename XorpMemberCallback10B3::M M; XorpSafeMemberCallback10B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpMemberCallback10B3(file, line, o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpSafeMemberCallback10B3() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback10B3::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 10 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpSafeMemberCallback10B3 : public XorpMemberCallback10B3, public SafeCallbackBase { typedef typename XorpMemberCallback10B3::M M; XorpSafeMemberCallback10B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpMemberCallback10B3(file, line, o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpSafeMemberCallback10B3() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { if (valid()) { record_dispatch_enter(); XorpMemberCallback10B3::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory10B3 { static XorpMemberCallback10B3* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpSafeMemberCallback10B3(file, line, o, p, ba1, ba2, ba3); } }; template struct XorpMemberCallbackFactory10B3 { static XorpMemberCallback10B3* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpMemberCallback10B3(file, line, o, p, ba1, ba2, ba3); }; }; /** * Factory function that creates a callback object targetted at a * member function with 10 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback10::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return XorpMemberCallbackFactory10B3::True>::make(file, line, o, p, ba1, ba2, ba3); } /** * Factory function that creates a callback object targetted at a * member function with 10 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback10::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return XorpMemberCallbackFactory10B3::True>::make(file, line, &o, p, ba1, ba2, ba3); } /** * @short Callback object for const member methods with 10 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstMemberCallback10B3 : public XorpCallback10 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3) const; XorpConstMemberCallback10B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback10(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1, _ba2, _ba3); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for void const member methods with 10 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstMemberCallback10B3 : public XorpCallback10 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3) const; XorpConstMemberCallback10B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback10(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1, _ba2, _ba3); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for const safe member methods with 10 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback10B3 : public XorpConstMemberCallback10B3, public SafeCallbackBase { typedef typename XorpConstMemberCallback10B3::M M; XorpConstSafeMemberCallback10B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpConstMemberCallback10B3(file, line, o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback10B3() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback10B3::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 10 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback10B3 : public XorpConstMemberCallback10B3, public SafeCallbackBase { typedef typename XorpConstMemberCallback10B3::M M; XorpConstSafeMemberCallback10B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpConstMemberCallback10B3(file, line, o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback10B3() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback10B3::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory10B3 { static XorpConstMemberCallback10B3* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpConstSafeMemberCallback10B3(file, line, o, p, ba1, ba2, ba3); } }; template struct XorpConstMemberCallbackFactory10B3 { static XorpConstMemberCallback10B3* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpConstMemberCallback10B3(file, line, o, p, ba1, ba2, ba3); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 10 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback10::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return XorpConstMemberCallbackFactory10B3::True>::make(file, line, o, p, ba1, ba2, ba3); } /** * Factory function that creates a callback object targetted at a * const member function with 10 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback10::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return XorpConstMemberCallbackFactory10B3::True>::make(file, line, &o, p, ba1, ba2, ba3); } /** * @short Callback object for functions with 10 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpFunctionCallback10B4 : public XorpCallback10 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4); XorpFunctionCallback10B4(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback10(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; }; /** * @short Callback object for void functions with 10 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpFunctionCallback10B4 : public XorpCallback10 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4); XorpFunctionCallback10B4(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback10(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; }; /** * Factory function that creates a callback object targetted at a * function with 10 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback10::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return typename XorpCallback10::RefPtr(new XorpFunctionCallback10B4(file, line, f, ba1, ba2, ba3, ba4)); } /** * @short Callback object for member methods with 10 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpMemberCallback10B4 : public XorpCallback10 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4) ; XorpMemberCallback10B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback10(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for void member methods with 10 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpMemberCallback10B4 : public XorpCallback10 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4) ; XorpMemberCallback10B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback10(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for safe member methods with 10 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpSafeMemberCallback10B4 : public XorpMemberCallback10B4, public SafeCallbackBase { typedef typename XorpMemberCallback10B4::M M; XorpSafeMemberCallback10B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpMemberCallback10B4(file, line, o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpSafeMemberCallback10B4() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback10B4::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 10 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpSafeMemberCallback10B4 : public XorpMemberCallback10B4, public SafeCallbackBase { typedef typename XorpMemberCallback10B4::M M; XorpSafeMemberCallback10B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpMemberCallback10B4(file, line, o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpSafeMemberCallback10B4() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { if (valid()) { record_dispatch_enter(); XorpMemberCallback10B4::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory10B4 { static XorpMemberCallback10B4* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpSafeMemberCallback10B4(file, line, o, p, ba1, ba2, ba3, ba4); } }; template struct XorpMemberCallbackFactory10B4 { static XorpMemberCallback10B4* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpMemberCallback10B4(file, line, o, p, ba1, ba2, ba3, ba4); }; }; /** * Factory function that creates a callback object targetted at a * member function with 10 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback10::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpMemberCallbackFactory10B4::True>::make(file, line, o, p, ba1, ba2, ba3, ba4); } /** * Factory function that creates a callback object targetted at a * member function with 10 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback10::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpMemberCallbackFactory10B4::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4); } /** * @short Callback object for const member methods with 10 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstMemberCallback10B4 : public XorpCallback10 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4) const; XorpConstMemberCallback10B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback10(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for void const member methods with 10 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstMemberCallback10B4 : public XorpCallback10 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4) const; XorpConstMemberCallback10B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback10(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for const safe member methods with 10 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback10B4 : public XorpConstMemberCallback10B4, public SafeCallbackBase { typedef typename XorpConstMemberCallback10B4::M M; XorpConstSafeMemberCallback10B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpConstMemberCallback10B4(file, line, o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback10B4() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback10B4::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 10 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback10B4 : public XorpConstMemberCallback10B4, public SafeCallbackBase { typedef typename XorpConstMemberCallback10B4::M M; XorpConstSafeMemberCallback10B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpConstMemberCallback10B4(file, line, o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback10B4() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback10B4::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory10B4 { static XorpConstMemberCallback10B4* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpConstSafeMemberCallback10B4(file, line, o, p, ba1, ba2, ba3, ba4); } }; template struct XorpConstMemberCallbackFactory10B4 { static XorpConstMemberCallback10B4* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpConstMemberCallback10B4(file, line, o, p, ba1, ba2, ba3, ba4); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 10 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback10::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpConstMemberCallbackFactory10B4::True>::make(file, line, o, p, ba1, ba2, ba3, ba4); } /** * Factory function that creates a callback object targetted at a * const member function with 10 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback10::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpConstMemberCallbackFactory10B4::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4); } /** * @short Callback object for functions with 10 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpFunctionCallback10B5 : public XorpCallback10 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4, BA5); XorpFunctionCallback10B5(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback10(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; }; /** * @short Callback object for void functions with 10 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpFunctionCallback10B5 : public XorpCallback10 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4, BA5); XorpFunctionCallback10B5(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback10(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; }; /** * Factory function that creates a callback object targetted at a * function with 10 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback10::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return typename XorpCallback10::RefPtr(new XorpFunctionCallback10B5(file, line, f, ba1, ba2, ba3, ba4, ba5)); } /** * @short Callback object for member methods with 10 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpMemberCallback10B5 : public XorpCallback10 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4, BA5) ; XorpMemberCallback10B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback10(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for void member methods with 10 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpMemberCallback10B5 : public XorpCallback10 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4, BA5) ; XorpMemberCallback10B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback10(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for safe member methods with 10 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpSafeMemberCallback10B5 : public XorpMemberCallback10B5, public SafeCallbackBase { typedef typename XorpMemberCallback10B5::M M; XorpSafeMemberCallback10B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpMemberCallback10B5(file, line, o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpSafeMemberCallback10B5() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback10B5::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 10 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpSafeMemberCallback10B5 : public XorpMemberCallback10B5, public SafeCallbackBase { typedef typename XorpMemberCallback10B5::M M; XorpSafeMemberCallback10B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpMemberCallback10B5(file, line, o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpSafeMemberCallback10B5() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { if (valid()) { record_dispatch_enter(); XorpMemberCallback10B5::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory10B5 { static XorpMemberCallback10B5* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpSafeMemberCallback10B5(file, line, o, p, ba1, ba2, ba3, ba4, ba5); } }; template struct XorpMemberCallbackFactory10B5 { static XorpMemberCallback10B5* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpMemberCallback10B5(file, line, o, p, ba1, ba2, ba3, ba4, ba5); }; }; /** * Factory function that creates a callback object targetted at a * member function with 10 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback10::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpMemberCallbackFactory10B5::True>::make(file, line, o, p, ba1, ba2, ba3, ba4, ba5); } /** * Factory function that creates a callback object targetted at a * member function with 10 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback10::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpMemberCallbackFactory10B5::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4, ba5); } /** * @short Callback object for const member methods with 10 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstMemberCallback10B5 : public XorpCallback10 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4, BA5) const; XorpConstMemberCallback10B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback10(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for void const member methods with 10 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstMemberCallback10B5 : public XorpCallback10 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4, BA5) const; XorpConstMemberCallback10B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback10(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for const safe member methods with 10 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback10B5 : public XorpConstMemberCallback10B5, public SafeCallbackBase { typedef typename XorpConstMemberCallback10B5::M M; XorpConstSafeMemberCallback10B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpConstMemberCallback10B5(file, line, o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback10B5() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback10B5::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 10 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback10B5 : public XorpConstMemberCallback10B5, public SafeCallbackBase { typedef typename XorpConstMemberCallback10B5::M M; XorpConstSafeMemberCallback10B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpConstMemberCallback10B5(file, line, o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback10B5() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback10B5::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory10B5 { static XorpConstMemberCallback10B5* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpConstSafeMemberCallback10B5(file, line, o, p, ba1, ba2, ba3, ba4, ba5); } }; template struct XorpConstMemberCallbackFactory10B5 { static XorpConstMemberCallback10B5* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpConstMemberCallback10B5(file, line, o, p, ba1, ba2, ba3, ba4, ba5); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 10 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback10::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpConstMemberCallbackFactory10B5::True>::make(file, line, o, p, ba1, ba2, ba3, ba4, ba5); } /** * Factory function that creates a callback object targetted at a * const member function with 10 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback10::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpConstMemberCallbackFactory10B5::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4, ba5); } /** * @short Callback object for functions with 10 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpFunctionCallback10B6 : public XorpCallback10 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4, BA5, BA6); XorpFunctionCallback10B6(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback10(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; BA6 _ba6; }; /** * @short Callback object for void functions with 10 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpFunctionCallback10B6 : public XorpCallback10 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4, BA5, BA6); XorpFunctionCallback10B6(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback10(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; BA6 _ba6; }; /** * Factory function that creates a callback object targetted at a * function with 10 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback10::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return typename XorpCallback10::RefPtr(new XorpFunctionCallback10B6(file, line, f, ba1, ba2, ba3, ba4, ba5, ba6)); } /** * @short Callback object for member methods with 10 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpMemberCallback10B6 : public XorpCallback10 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4, BA5, BA6) ; XorpMemberCallback10B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback10(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for void member methods with 10 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpMemberCallback10B6 : public XorpCallback10 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4, BA5, BA6) ; XorpMemberCallback10B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback10(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for safe member methods with 10 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpSafeMemberCallback10B6 : public XorpMemberCallback10B6, public SafeCallbackBase { typedef typename XorpMemberCallback10B6::M M; XorpSafeMemberCallback10B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpMemberCallback10B6(file, line, o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpSafeMemberCallback10B6() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback10B6::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 10 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpSafeMemberCallback10B6 : public XorpMemberCallback10B6, public SafeCallbackBase { typedef typename XorpMemberCallback10B6::M M; XorpSafeMemberCallback10B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpMemberCallback10B6(file, line, o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpSafeMemberCallback10B6() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { if (valid()) { record_dispatch_enter(); XorpMemberCallback10B6::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory10B6 { static XorpMemberCallback10B6* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpSafeMemberCallback10B6(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); } }; template struct XorpMemberCallbackFactory10B6 { static XorpMemberCallback10B6* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpMemberCallback10B6(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); }; }; /** * Factory function that creates a callback object targetted at a * member function with 10 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback10::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpMemberCallbackFactory10B6::True>::make(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * Factory function that creates a callback object targetted at a * member function with 10 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback10::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpMemberCallbackFactory10B6::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * @short Callback object for const member methods with 10 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstMemberCallback10B6 : public XorpCallback10 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4, BA5, BA6) const; XorpConstMemberCallback10B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback10(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for void const member methods with 10 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstMemberCallback10B6 : public XorpCallback10 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4, BA5, BA6) const; XorpConstMemberCallback10B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback10(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for const safe member methods with 10 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback10B6 : public XorpConstMemberCallback10B6, public SafeCallbackBase { typedef typename XorpConstMemberCallback10B6::M M; XorpConstSafeMemberCallback10B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpConstMemberCallback10B6(file, line, o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback10B6() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback10B6::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 10 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback10B6 : public XorpConstMemberCallback10B6, public SafeCallbackBase { typedef typename XorpConstMemberCallback10B6::M M; XorpConstSafeMemberCallback10B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpConstMemberCallback10B6(file, line, o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback10B6() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback10B6::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory10B6 { static XorpConstMemberCallback10B6* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpConstSafeMemberCallback10B6(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); } }; template struct XorpConstMemberCallbackFactory10B6 { static XorpConstMemberCallback10B6* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpConstMemberCallback10B6(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 10 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback10::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpConstMemberCallbackFactory10B6::True>::make(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * Factory function that creates a callback object targetted at a * const member function with 10 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback10::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpConstMemberCallbackFactory10B6::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /////////////////////////////////////////////////////////////////////////////// // // Code relating to callbacks with 11 late args // /** * @short Base class for callbacks with 11 dispatch time args. */ template struct XorpCallback11 { typedef ref_ptr RefPtr; XorpCallback11(const char* file, int line) : _file(file), _line(line) {} virtual ~XorpCallback11() {} virtual R dispatch(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11) = 0; const char* file() const { return _file; } int line() const { return _line; } private: const char* _file; int _line; }; /** * @short Callback object for functions with 11 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpFunctionCallback11B0 : public XorpCallback11 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11); XorpFunctionCallback11B0(const char* file, int line, F f) : XorpCallback11(file, line), _f(f) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); record_dispatch_leave(); return r; } protected: F _f; }; /** * @short Callback object for void functions with 11 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpFunctionCallback11B0 : public XorpCallback11 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11); XorpFunctionCallback11B0(const char* file, int line, F f) : XorpCallback11(file, line), _f(f) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); record_dispatch_leave(); } protected: F _f; }; /** * Factory function that creates a callback object targetted at a * function with 11 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback11::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11)) { return typename XorpCallback11::RefPtr(new XorpFunctionCallback11B0(file, line, f)); } /** * @short Callback object for member methods with 11 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpMemberCallback11B0 : public XorpCallback11 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11) ; XorpMemberCallback11B0(const char* file, int line, O* o, M m) : XorpCallback11(file, line), _o(o), _m(m) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for void member methods with 11 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpMemberCallback11B0 : public XorpCallback11 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11) ; XorpMemberCallback11B0(const char* file, int line, O* o, M m) : XorpCallback11(file, line), _o(o), _m(m) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for safe member methods with 11 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpSafeMemberCallback11B0 : public XorpMemberCallback11B0, public SafeCallbackBase { typedef typename XorpMemberCallback11B0::M M; XorpSafeMemberCallback11B0(const char* file, int line, O* o, M m) : XorpMemberCallback11B0(file, line, o, m), SafeCallbackBase(o) {} ~XorpSafeMemberCallback11B0() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback11B0::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 11 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpSafeMemberCallback11B0 : public XorpMemberCallback11B0, public SafeCallbackBase { typedef typename XorpMemberCallback11B0::M M; XorpSafeMemberCallback11B0(const char* file, int line, O* o, M m) : XorpMemberCallback11B0(file, line, o, m), SafeCallbackBase(o) {} ~XorpSafeMemberCallback11B0() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { if (valid()) { record_dispatch_enter(); XorpMemberCallback11B0::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory11B0 { static XorpMemberCallback11B0* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11)) { return new XorpSafeMemberCallback11B0(file, line, o, p); } }; template struct XorpMemberCallbackFactory11B0 { static XorpMemberCallback11B0* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11)) { return new XorpMemberCallback11B0(file, line, o, p); }; }; /** * Factory function that creates a callback object targetted at a * member function with 11 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback11::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11)) { return XorpMemberCallbackFactory11B0::True>::make(file, line, o, p); } /** * Factory function that creates a callback object targetted at a * member function with 11 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback11::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11)) { return XorpMemberCallbackFactory11B0::True>::make(file, line, &o, p); } /** * @short Callback object for const member methods with 11 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstMemberCallback11B0 : public XorpCallback11 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11) const; XorpConstMemberCallback11B0(const char* file, int line, O* o, M m) : XorpCallback11(file, line), _o(o), _m(m) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for void const member methods with 11 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstMemberCallback11B0 : public XorpCallback11 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11) const; XorpConstMemberCallback11B0(const char* file, int line, O* o, M m) : XorpCallback11(file, line), _o(o), _m(m) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for const safe member methods with 11 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback11B0 : public XorpConstMemberCallback11B0, public SafeCallbackBase { typedef typename XorpConstMemberCallback11B0::M M; XorpConstSafeMemberCallback11B0(const char* file, int line, O* o, M m) : XorpConstMemberCallback11B0(file, line, o, m), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback11B0() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback11B0::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 11 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback11B0 : public XorpConstMemberCallback11B0, public SafeCallbackBase { typedef typename XorpConstMemberCallback11B0::M M; XorpConstSafeMemberCallback11B0(const char* file, int line, O* o, M m) : XorpConstMemberCallback11B0(file, line, o, m), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback11B0() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback11B0::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory11B0 { static XorpConstMemberCallback11B0* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11) const) { return new XorpConstSafeMemberCallback11B0(file, line, o, p); } }; template struct XorpConstMemberCallbackFactory11B0 { static XorpConstMemberCallback11B0* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11) const) { return new XorpConstMemberCallback11B0(file, line, o, p); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 11 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback11::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11) const) { return XorpConstMemberCallbackFactory11B0::True>::make(file, line, o, p); } /** * Factory function that creates a callback object targetted at a * const member function with 11 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback11::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11) const) { return XorpConstMemberCallbackFactory11B0::True>::make(file, line, &o, p); } /** * @short Callback object for functions with 11 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpFunctionCallback11B1 : public XorpCallback11 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1); XorpFunctionCallback11B1(const char* file, int line, F f, BA1 ba1) : XorpCallback11(file, line), _f(f), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; }; /** * @short Callback object for void functions with 11 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpFunctionCallback11B1 : public XorpCallback11 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1); XorpFunctionCallback11B1(const char* file, int line, F f, BA1 ba1) : XorpCallback11(file, line), _f(f), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1); record_dispatch_leave(); } protected: F _f; BA1 _ba1; }; /** * Factory function that creates a callback object targetted at a * function with 11 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback11::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1), BA1 ba1) { return typename XorpCallback11::RefPtr(new XorpFunctionCallback11B1(file, line, f, ba1)); } /** * @short Callback object for member methods with 11 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpMemberCallback11B1 : public XorpCallback11 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1) ; XorpMemberCallback11B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpCallback11(file, line), _o(o), _m(m), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for void member methods with 11 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpMemberCallback11B1 : public XorpCallback11 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1) ; XorpMemberCallback11B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpCallback11(file, line), _o(o), _m(m), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for safe member methods with 11 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpSafeMemberCallback11B1 : public XorpMemberCallback11B1, public SafeCallbackBase { typedef typename XorpMemberCallback11B1::M M; XorpSafeMemberCallback11B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpMemberCallback11B1(file, line, o, m, ba1), SafeCallbackBase(o) {} ~XorpSafeMemberCallback11B1() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback11B1::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 11 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpSafeMemberCallback11B1 : public XorpMemberCallback11B1, public SafeCallbackBase { typedef typename XorpMemberCallback11B1::M M; XorpSafeMemberCallback11B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpMemberCallback11B1(file, line, o, m, ba1), SafeCallbackBase(o) {} ~XorpSafeMemberCallback11B1() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { if (valid()) { record_dispatch_enter(); XorpMemberCallback11B1::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory11B1 { static XorpMemberCallback11B1* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1), BA1 ba1) { return new XorpSafeMemberCallback11B1(file, line, o, p, ba1); } }; template struct XorpMemberCallbackFactory11B1 { static XorpMemberCallback11B1* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1), BA1 ba1) { return new XorpMemberCallback11B1(file, line, o, p, ba1); }; }; /** * Factory function that creates a callback object targetted at a * member function with 11 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback11::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1), BA1 ba1) { return XorpMemberCallbackFactory11B1::True>::make(file, line, o, p, ba1); } /** * Factory function that creates a callback object targetted at a * member function with 11 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback11::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1), BA1 ba1) { return XorpMemberCallbackFactory11B1::True>::make(file, line, &o, p, ba1); } /** * @short Callback object for const member methods with 11 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstMemberCallback11B1 : public XorpCallback11 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1) const; XorpConstMemberCallback11B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpCallback11(file, line), _o(o), _m(m), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for void const member methods with 11 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstMemberCallback11B1 : public XorpCallback11 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1) const; XorpConstMemberCallback11B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpCallback11(file, line), _o(o), _m(m), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for const safe member methods with 11 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback11B1 : public XorpConstMemberCallback11B1, public SafeCallbackBase { typedef typename XorpConstMemberCallback11B1::M M; XorpConstSafeMemberCallback11B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpConstMemberCallback11B1(file, line, o, m, ba1), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback11B1() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback11B1::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 11 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback11B1 : public XorpConstMemberCallback11B1, public SafeCallbackBase { typedef typename XorpConstMemberCallback11B1::M M; XorpConstSafeMemberCallback11B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpConstMemberCallback11B1(file, line, o, m, ba1), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback11B1() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback11B1::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory11B1 { static XorpConstMemberCallback11B1* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1) const, BA1 ba1) { return new XorpConstSafeMemberCallback11B1(file, line, o, p, ba1); } }; template struct XorpConstMemberCallbackFactory11B1 { static XorpConstMemberCallback11B1* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1) const, BA1 ba1) { return new XorpConstMemberCallback11B1(file, line, o, p, ba1); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 11 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback11::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1) const, BA1 ba1) { return XorpConstMemberCallbackFactory11B1::True>::make(file, line, o, p, ba1); } /** * Factory function that creates a callback object targetted at a * const member function with 11 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback11::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1) const, BA1 ba1) { return XorpConstMemberCallbackFactory11B1::True>::make(file, line, &o, p, ba1); } /** * @short Callback object for functions with 11 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpFunctionCallback11B2 : public XorpCallback11 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2); XorpFunctionCallback11B2(const char* file, int line, F f, BA1 ba1, BA2 ba2) : XorpCallback11(file, line), _f(f), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1, _ba2); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; }; /** * @short Callback object for void functions with 11 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpFunctionCallback11B2 : public XorpCallback11 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2); XorpFunctionCallback11B2(const char* file, int line, F f, BA1 ba1, BA2 ba2) : XorpCallback11(file, line), _f(f), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1, _ba2); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; }; /** * Factory function that creates a callback object targetted at a * function with 11 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback11::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2), BA1 ba1, BA2 ba2) { return typename XorpCallback11::RefPtr(new XorpFunctionCallback11B2(file, line, f, ba1, ba2)); } /** * @short Callback object for member methods with 11 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpMemberCallback11B2 : public XorpCallback11 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2) ; XorpMemberCallback11B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback11(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1, _ba2); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for void member methods with 11 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpMemberCallback11B2 : public XorpCallback11 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2) ; XorpMemberCallback11B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback11(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1, _ba2); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for safe member methods with 11 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpSafeMemberCallback11B2 : public XorpMemberCallback11B2, public SafeCallbackBase { typedef typename XorpMemberCallback11B2::M M; XorpSafeMemberCallback11B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpMemberCallback11B2(file, line, o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpSafeMemberCallback11B2() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback11B2::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 11 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpSafeMemberCallback11B2 : public XorpMemberCallback11B2, public SafeCallbackBase { typedef typename XorpMemberCallback11B2::M M; XorpSafeMemberCallback11B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpMemberCallback11B2(file, line, o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpSafeMemberCallback11B2() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { if (valid()) { record_dispatch_enter(); XorpMemberCallback11B2::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory11B2 { static XorpMemberCallback11B2* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2), BA1 ba1, BA2 ba2) { return new XorpSafeMemberCallback11B2(file, line, o, p, ba1, ba2); } }; template struct XorpMemberCallbackFactory11B2 { static XorpMemberCallback11B2* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2), BA1 ba1, BA2 ba2) { return new XorpMemberCallback11B2(file, line, o, p, ba1, ba2); }; }; /** * Factory function that creates a callback object targetted at a * member function with 11 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback11::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2), BA1 ba1, BA2 ba2) { return XorpMemberCallbackFactory11B2::True>::make(file, line, o, p, ba1, ba2); } /** * Factory function that creates a callback object targetted at a * member function with 11 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback11::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2), BA1 ba1, BA2 ba2) { return XorpMemberCallbackFactory11B2::True>::make(file, line, &o, p, ba1, ba2); } /** * @short Callback object for const member methods with 11 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstMemberCallback11B2 : public XorpCallback11 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2) const; XorpConstMemberCallback11B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback11(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1, _ba2); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for void const member methods with 11 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstMemberCallback11B2 : public XorpCallback11 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2) const; XorpConstMemberCallback11B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback11(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1, _ba2); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for const safe member methods with 11 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback11B2 : public XorpConstMemberCallback11B2, public SafeCallbackBase { typedef typename XorpConstMemberCallback11B2::M M; XorpConstSafeMemberCallback11B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpConstMemberCallback11B2(file, line, o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback11B2() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback11B2::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 11 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback11B2 : public XorpConstMemberCallback11B2, public SafeCallbackBase { typedef typename XorpConstMemberCallback11B2::M M; XorpConstSafeMemberCallback11B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpConstMemberCallback11B2(file, line, o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback11B2() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback11B2::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory11B2 { static XorpConstMemberCallback11B2* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2) const, BA1 ba1, BA2 ba2) { return new XorpConstSafeMemberCallback11B2(file, line, o, p, ba1, ba2); } }; template struct XorpConstMemberCallbackFactory11B2 { static XorpConstMemberCallback11B2* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2) const, BA1 ba1, BA2 ba2) { return new XorpConstMemberCallback11B2(file, line, o, p, ba1, ba2); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 11 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback11::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2) const, BA1 ba1, BA2 ba2) { return XorpConstMemberCallbackFactory11B2::True>::make(file, line, o, p, ba1, ba2); } /** * Factory function that creates a callback object targetted at a * const member function with 11 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback11::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2) const, BA1 ba1, BA2 ba2) { return XorpConstMemberCallbackFactory11B2::True>::make(file, line, &o, p, ba1, ba2); } /** * @short Callback object for functions with 11 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpFunctionCallback11B3 : public XorpCallback11 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3); XorpFunctionCallback11B3(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback11(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1, _ba2, _ba3); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; }; /** * @short Callback object for void functions with 11 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpFunctionCallback11B3 : public XorpCallback11 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3); XorpFunctionCallback11B3(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback11(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1, _ba2, _ba3); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; }; /** * Factory function that creates a callback object targetted at a * function with 11 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback11::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return typename XorpCallback11::RefPtr(new XorpFunctionCallback11B3(file, line, f, ba1, ba2, ba3)); } /** * @short Callback object for member methods with 11 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpMemberCallback11B3 : public XorpCallback11 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3) ; XorpMemberCallback11B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback11(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1, _ba2, _ba3); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for void member methods with 11 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpMemberCallback11B3 : public XorpCallback11 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3) ; XorpMemberCallback11B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback11(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1, _ba2, _ba3); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for safe member methods with 11 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpSafeMemberCallback11B3 : public XorpMemberCallback11B3, public SafeCallbackBase { typedef typename XorpMemberCallback11B3::M M; XorpSafeMemberCallback11B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpMemberCallback11B3(file, line, o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpSafeMemberCallback11B3() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback11B3::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 11 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpSafeMemberCallback11B3 : public XorpMemberCallback11B3, public SafeCallbackBase { typedef typename XorpMemberCallback11B3::M M; XorpSafeMemberCallback11B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpMemberCallback11B3(file, line, o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpSafeMemberCallback11B3() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { if (valid()) { record_dispatch_enter(); XorpMemberCallback11B3::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory11B3 { static XorpMemberCallback11B3* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpSafeMemberCallback11B3(file, line, o, p, ba1, ba2, ba3); } }; template struct XorpMemberCallbackFactory11B3 { static XorpMemberCallback11B3* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpMemberCallback11B3(file, line, o, p, ba1, ba2, ba3); }; }; /** * Factory function that creates a callback object targetted at a * member function with 11 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback11::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return XorpMemberCallbackFactory11B3::True>::make(file, line, o, p, ba1, ba2, ba3); } /** * Factory function that creates a callback object targetted at a * member function with 11 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback11::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return XorpMemberCallbackFactory11B3::True>::make(file, line, &o, p, ba1, ba2, ba3); } /** * @short Callback object for const member methods with 11 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstMemberCallback11B3 : public XorpCallback11 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3) const; XorpConstMemberCallback11B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback11(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1, _ba2, _ba3); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for void const member methods with 11 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstMemberCallback11B3 : public XorpCallback11 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3) const; XorpConstMemberCallback11B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback11(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1, _ba2, _ba3); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for const safe member methods with 11 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback11B3 : public XorpConstMemberCallback11B3, public SafeCallbackBase { typedef typename XorpConstMemberCallback11B3::M M; XorpConstSafeMemberCallback11B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpConstMemberCallback11B3(file, line, o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback11B3() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback11B3::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 11 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback11B3 : public XorpConstMemberCallback11B3, public SafeCallbackBase { typedef typename XorpConstMemberCallback11B3::M M; XorpConstSafeMemberCallback11B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpConstMemberCallback11B3(file, line, o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback11B3() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback11B3::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory11B3 { static XorpConstMemberCallback11B3* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpConstSafeMemberCallback11B3(file, line, o, p, ba1, ba2, ba3); } }; template struct XorpConstMemberCallbackFactory11B3 { static XorpConstMemberCallback11B3* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpConstMemberCallback11B3(file, line, o, p, ba1, ba2, ba3); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 11 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback11::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return XorpConstMemberCallbackFactory11B3::True>::make(file, line, o, p, ba1, ba2, ba3); } /** * Factory function that creates a callback object targetted at a * const member function with 11 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback11::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return XorpConstMemberCallbackFactory11B3::True>::make(file, line, &o, p, ba1, ba2, ba3); } /** * @short Callback object for functions with 11 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpFunctionCallback11B4 : public XorpCallback11 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4); XorpFunctionCallback11B4(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback11(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; }; /** * @short Callback object for void functions with 11 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpFunctionCallback11B4 : public XorpCallback11 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4); XorpFunctionCallback11B4(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback11(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; }; /** * Factory function that creates a callback object targetted at a * function with 11 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback11::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return typename XorpCallback11::RefPtr(new XorpFunctionCallback11B4(file, line, f, ba1, ba2, ba3, ba4)); } /** * @short Callback object for member methods with 11 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpMemberCallback11B4 : public XorpCallback11 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4) ; XorpMemberCallback11B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback11(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for void member methods with 11 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpMemberCallback11B4 : public XorpCallback11 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4) ; XorpMemberCallback11B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback11(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for safe member methods with 11 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpSafeMemberCallback11B4 : public XorpMemberCallback11B4, public SafeCallbackBase { typedef typename XorpMemberCallback11B4::M M; XorpSafeMemberCallback11B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpMemberCallback11B4(file, line, o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpSafeMemberCallback11B4() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback11B4::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 11 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpSafeMemberCallback11B4 : public XorpMemberCallback11B4, public SafeCallbackBase { typedef typename XorpMemberCallback11B4::M M; XorpSafeMemberCallback11B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpMemberCallback11B4(file, line, o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpSafeMemberCallback11B4() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { if (valid()) { record_dispatch_enter(); XorpMemberCallback11B4::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory11B4 { static XorpMemberCallback11B4* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpSafeMemberCallback11B4(file, line, o, p, ba1, ba2, ba3, ba4); } }; template struct XorpMemberCallbackFactory11B4 { static XorpMemberCallback11B4* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpMemberCallback11B4(file, line, o, p, ba1, ba2, ba3, ba4); }; }; /** * Factory function that creates a callback object targetted at a * member function with 11 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback11::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpMemberCallbackFactory11B4::True>::make(file, line, o, p, ba1, ba2, ba3, ba4); } /** * Factory function that creates a callback object targetted at a * member function with 11 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback11::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpMemberCallbackFactory11B4::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4); } /** * @short Callback object for const member methods with 11 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstMemberCallback11B4 : public XorpCallback11 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4) const; XorpConstMemberCallback11B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback11(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for void const member methods with 11 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstMemberCallback11B4 : public XorpCallback11 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4) const; XorpConstMemberCallback11B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback11(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for const safe member methods with 11 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback11B4 : public XorpConstMemberCallback11B4, public SafeCallbackBase { typedef typename XorpConstMemberCallback11B4::M M; XorpConstSafeMemberCallback11B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpConstMemberCallback11B4(file, line, o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback11B4() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback11B4::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 11 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback11B4 : public XorpConstMemberCallback11B4, public SafeCallbackBase { typedef typename XorpConstMemberCallback11B4::M M; XorpConstSafeMemberCallback11B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpConstMemberCallback11B4(file, line, o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback11B4() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback11B4::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory11B4 { static XorpConstMemberCallback11B4* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpConstSafeMemberCallback11B4(file, line, o, p, ba1, ba2, ba3, ba4); } }; template struct XorpConstMemberCallbackFactory11B4 { static XorpConstMemberCallback11B4* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpConstMemberCallback11B4(file, line, o, p, ba1, ba2, ba3, ba4); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 11 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback11::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpConstMemberCallbackFactory11B4::True>::make(file, line, o, p, ba1, ba2, ba3, ba4); } /** * Factory function that creates a callback object targetted at a * const member function with 11 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback11::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpConstMemberCallbackFactory11B4::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4); } /** * @short Callback object for functions with 11 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpFunctionCallback11B5 : public XorpCallback11 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4, BA5); XorpFunctionCallback11B5(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback11(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; }; /** * @short Callback object for void functions with 11 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpFunctionCallback11B5 : public XorpCallback11 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4, BA5); XorpFunctionCallback11B5(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback11(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; }; /** * Factory function that creates a callback object targetted at a * function with 11 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback11::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return typename XorpCallback11::RefPtr(new XorpFunctionCallback11B5(file, line, f, ba1, ba2, ba3, ba4, ba5)); } /** * @short Callback object for member methods with 11 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpMemberCallback11B5 : public XorpCallback11 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4, BA5) ; XorpMemberCallback11B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback11(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for void member methods with 11 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpMemberCallback11B5 : public XorpCallback11 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4, BA5) ; XorpMemberCallback11B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback11(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for safe member methods with 11 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpSafeMemberCallback11B5 : public XorpMemberCallback11B5, public SafeCallbackBase { typedef typename XorpMemberCallback11B5::M M; XorpSafeMemberCallback11B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpMemberCallback11B5(file, line, o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpSafeMemberCallback11B5() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback11B5::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 11 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpSafeMemberCallback11B5 : public XorpMemberCallback11B5, public SafeCallbackBase { typedef typename XorpMemberCallback11B5::M M; XorpSafeMemberCallback11B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpMemberCallback11B5(file, line, o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpSafeMemberCallback11B5() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { if (valid()) { record_dispatch_enter(); XorpMemberCallback11B5::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory11B5 { static XorpMemberCallback11B5* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpSafeMemberCallback11B5(file, line, o, p, ba1, ba2, ba3, ba4, ba5); } }; template struct XorpMemberCallbackFactory11B5 { static XorpMemberCallback11B5* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpMemberCallback11B5(file, line, o, p, ba1, ba2, ba3, ba4, ba5); }; }; /** * Factory function that creates a callback object targetted at a * member function with 11 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback11::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpMemberCallbackFactory11B5::True>::make(file, line, o, p, ba1, ba2, ba3, ba4, ba5); } /** * Factory function that creates a callback object targetted at a * member function with 11 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback11::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpMemberCallbackFactory11B5::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4, ba5); } /** * @short Callback object for const member methods with 11 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstMemberCallback11B5 : public XorpCallback11 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4, BA5) const; XorpConstMemberCallback11B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback11(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for void const member methods with 11 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstMemberCallback11B5 : public XorpCallback11 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4, BA5) const; XorpConstMemberCallback11B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback11(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for const safe member methods with 11 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback11B5 : public XorpConstMemberCallback11B5, public SafeCallbackBase { typedef typename XorpConstMemberCallback11B5::M M; XorpConstSafeMemberCallback11B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpConstMemberCallback11B5(file, line, o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback11B5() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback11B5::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 11 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback11B5 : public XorpConstMemberCallback11B5, public SafeCallbackBase { typedef typename XorpConstMemberCallback11B5::M M; XorpConstSafeMemberCallback11B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpConstMemberCallback11B5(file, line, o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback11B5() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback11B5::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory11B5 { static XorpConstMemberCallback11B5* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpConstSafeMemberCallback11B5(file, line, o, p, ba1, ba2, ba3, ba4, ba5); } }; template struct XorpConstMemberCallbackFactory11B5 { static XorpConstMemberCallback11B5* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpConstMemberCallback11B5(file, line, o, p, ba1, ba2, ba3, ba4, ba5); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 11 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback11::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpConstMemberCallbackFactory11B5::True>::make(file, line, o, p, ba1, ba2, ba3, ba4, ba5); } /** * Factory function that creates a callback object targetted at a * const member function with 11 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback11::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpConstMemberCallbackFactory11B5::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4, ba5); } /** * @short Callback object for functions with 11 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpFunctionCallback11B6 : public XorpCallback11 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4, BA5, BA6); XorpFunctionCallback11B6(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback11(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; BA6 _ba6; }; /** * @short Callback object for void functions with 11 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpFunctionCallback11B6 : public XorpCallback11 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4, BA5, BA6); XorpFunctionCallback11B6(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback11(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; BA6 _ba6; }; /** * Factory function that creates a callback object targetted at a * function with 11 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback11::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return typename XorpCallback11::RefPtr(new XorpFunctionCallback11B6(file, line, f, ba1, ba2, ba3, ba4, ba5, ba6)); } /** * @short Callback object for member methods with 11 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpMemberCallback11B6 : public XorpCallback11 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4, BA5, BA6) ; XorpMemberCallback11B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback11(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for void member methods with 11 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpMemberCallback11B6 : public XorpCallback11 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4, BA5, BA6) ; XorpMemberCallback11B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback11(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for safe member methods with 11 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpSafeMemberCallback11B6 : public XorpMemberCallback11B6, public SafeCallbackBase { typedef typename XorpMemberCallback11B6::M M; XorpSafeMemberCallback11B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpMemberCallback11B6(file, line, o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpSafeMemberCallback11B6() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback11B6::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 11 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpSafeMemberCallback11B6 : public XorpMemberCallback11B6, public SafeCallbackBase { typedef typename XorpMemberCallback11B6::M M; XorpSafeMemberCallback11B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpMemberCallback11B6(file, line, o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpSafeMemberCallback11B6() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { if (valid()) { record_dispatch_enter(); XorpMemberCallback11B6::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory11B6 { static XorpMemberCallback11B6* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpSafeMemberCallback11B6(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); } }; template struct XorpMemberCallbackFactory11B6 { static XorpMemberCallback11B6* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpMemberCallback11B6(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); }; }; /** * Factory function that creates a callback object targetted at a * member function with 11 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback11::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpMemberCallbackFactory11B6::True>::make(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * Factory function that creates a callback object targetted at a * member function with 11 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback11::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpMemberCallbackFactory11B6::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * @short Callback object for const member methods with 11 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstMemberCallback11B6 : public XorpCallback11 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4, BA5, BA6) const; XorpConstMemberCallback11B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback11(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for void const member methods with 11 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstMemberCallback11B6 : public XorpCallback11 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4, BA5, BA6) const; XorpConstMemberCallback11B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback11(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for const safe member methods with 11 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback11B6 : public XorpConstMemberCallback11B6, public SafeCallbackBase { typedef typename XorpConstMemberCallback11B6::M M; XorpConstSafeMemberCallback11B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpConstMemberCallback11B6(file, line, o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback11B6() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback11B6::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 11 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback11B6 : public XorpConstMemberCallback11B6, public SafeCallbackBase { typedef typename XorpConstMemberCallback11B6::M M; XorpConstSafeMemberCallback11B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpConstMemberCallback11B6(file, line, o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback11B6() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback11B6::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory11B6 { static XorpConstMemberCallback11B6* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpConstSafeMemberCallback11B6(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); } }; template struct XorpConstMemberCallbackFactory11B6 { static XorpConstMemberCallback11B6* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpConstMemberCallback11B6(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 11 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback11::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpConstMemberCallbackFactory11B6::True>::make(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * Factory function that creates a callback object targetted at a * const member function with 11 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback11::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpConstMemberCallbackFactory11B6::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /////////////////////////////////////////////////////////////////////////////// // // Code relating to callbacks with 12 late args // /** * @short Base class for callbacks with 12 dispatch time args. */ template struct XorpCallback12 { typedef ref_ptr RefPtr; XorpCallback12(const char* file, int line) : _file(file), _line(line) {} virtual ~XorpCallback12() {} virtual R dispatch(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12) = 0; const char* file() const { return _file; } int line() const { return _line; } private: const char* _file; int _line; }; /** * @short Callback object for functions with 12 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpFunctionCallback12B0 : public XorpCallback12 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12); XorpFunctionCallback12B0(const char* file, int line, F f) : XorpCallback12(file, line), _f(f) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); record_dispatch_leave(); return r; } protected: F _f; }; /** * @short Callback object for void functions with 12 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpFunctionCallback12B0 : public XorpCallback12 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12); XorpFunctionCallback12B0(const char* file, int line, F f) : XorpCallback12(file, line), _f(f) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); record_dispatch_leave(); } protected: F _f; }; /** * Factory function that creates a callback object targetted at a * function with 12 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback12::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12)) { return typename XorpCallback12::RefPtr(new XorpFunctionCallback12B0(file, line, f)); } /** * @short Callback object for member methods with 12 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpMemberCallback12B0 : public XorpCallback12 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12) ; XorpMemberCallback12B0(const char* file, int line, O* o, M m) : XorpCallback12(file, line), _o(o), _m(m) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for void member methods with 12 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpMemberCallback12B0 : public XorpCallback12 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12) ; XorpMemberCallback12B0(const char* file, int line, O* o, M m) : XorpCallback12(file, line), _o(o), _m(m) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for safe member methods with 12 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpSafeMemberCallback12B0 : public XorpMemberCallback12B0, public SafeCallbackBase { typedef typename XorpMemberCallback12B0::M M; XorpSafeMemberCallback12B0(const char* file, int line, O* o, M m) : XorpMemberCallback12B0(file, line, o, m), SafeCallbackBase(o) {} ~XorpSafeMemberCallback12B0() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback12B0::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 12 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpSafeMemberCallback12B0 : public XorpMemberCallback12B0, public SafeCallbackBase { typedef typename XorpMemberCallback12B0::M M; XorpSafeMemberCallback12B0(const char* file, int line, O* o, M m) : XorpMemberCallback12B0(file, line, o, m), SafeCallbackBase(o) {} ~XorpSafeMemberCallback12B0() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { if (valid()) { record_dispatch_enter(); XorpMemberCallback12B0::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory12B0 { static XorpMemberCallback12B0* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12)) { return new XorpSafeMemberCallback12B0(file, line, o, p); } }; template struct XorpMemberCallbackFactory12B0 { static XorpMemberCallback12B0* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12)) { return new XorpMemberCallback12B0(file, line, o, p); }; }; /** * Factory function that creates a callback object targetted at a * member function with 12 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback12::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12)) { return XorpMemberCallbackFactory12B0::True>::make(file, line, o, p); } /** * Factory function that creates a callback object targetted at a * member function with 12 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback12::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12)) { return XorpMemberCallbackFactory12B0::True>::make(file, line, &o, p); } /** * @short Callback object for const member methods with 12 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstMemberCallback12B0 : public XorpCallback12 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12) const; XorpConstMemberCallback12B0(const char* file, int line, O* o, M m) : XorpCallback12(file, line), _o(o), _m(m) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for void const member methods with 12 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstMemberCallback12B0 : public XorpCallback12 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12) const; XorpConstMemberCallback12B0(const char* file, int line, O* o, M m) : XorpCallback12(file, line), _o(o), _m(m) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for const safe member methods with 12 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback12B0 : public XorpConstMemberCallback12B0, public SafeCallbackBase { typedef typename XorpConstMemberCallback12B0::M M; XorpConstSafeMemberCallback12B0(const char* file, int line, O* o, M m) : XorpConstMemberCallback12B0(file, line, o, m), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback12B0() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback12B0::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 12 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback12B0 : public XorpConstMemberCallback12B0, public SafeCallbackBase { typedef typename XorpConstMemberCallback12B0::M M; XorpConstSafeMemberCallback12B0(const char* file, int line, O* o, M m) : XorpConstMemberCallback12B0(file, line, o, m), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback12B0() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback12B0::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory12B0 { static XorpConstMemberCallback12B0* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12) const) { return new XorpConstSafeMemberCallback12B0(file, line, o, p); } }; template struct XorpConstMemberCallbackFactory12B0 { static XorpConstMemberCallback12B0* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12) const) { return new XorpConstMemberCallback12B0(file, line, o, p); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 12 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback12::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12) const) { return XorpConstMemberCallbackFactory12B0::True>::make(file, line, o, p); } /** * Factory function that creates a callback object targetted at a * const member function with 12 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback12::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12) const) { return XorpConstMemberCallbackFactory12B0::True>::make(file, line, &o, p); } /** * @short Callback object for functions with 12 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpFunctionCallback12B1 : public XorpCallback12 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1); XorpFunctionCallback12B1(const char* file, int line, F f, BA1 ba1) : XorpCallback12(file, line), _f(f), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; }; /** * @short Callback object for void functions with 12 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpFunctionCallback12B1 : public XorpCallback12 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1); XorpFunctionCallback12B1(const char* file, int line, F f, BA1 ba1) : XorpCallback12(file, line), _f(f), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1); record_dispatch_leave(); } protected: F _f; BA1 _ba1; }; /** * Factory function that creates a callback object targetted at a * function with 12 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback12::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1), BA1 ba1) { return typename XorpCallback12::RefPtr(new XorpFunctionCallback12B1(file, line, f, ba1)); } /** * @short Callback object for member methods with 12 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpMemberCallback12B1 : public XorpCallback12 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1) ; XorpMemberCallback12B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpCallback12(file, line), _o(o), _m(m), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for void member methods with 12 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpMemberCallback12B1 : public XorpCallback12 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1) ; XorpMemberCallback12B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpCallback12(file, line), _o(o), _m(m), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for safe member methods with 12 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpSafeMemberCallback12B1 : public XorpMemberCallback12B1, public SafeCallbackBase { typedef typename XorpMemberCallback12B1::M M; XorpSafeMemberCallback12B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpMemberCallback12B1(file, line, o, m, ba1), SafeCallbackBase(o) {} ~XorpSafeMemberCallback12B1() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback12B1::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 12 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpSafeMemberCallback12B1 : public XorpMemberCallback12B1, public SafeCallbackBase { typedef typename XorpMemberCallback12B1::M M; XorpSafeMemberCallback12B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpMemberCallback12B1(file, line, o, m, ba1), SafeCallbackBase(o) {} ~XorpSafeMemberCallback12B1() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { if (valid()) { record_dispatch_enter(); XorpMemberCallback12B1::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory12B1 { static XorpMemberCallback12B1* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1), BA1 ba1) { return new XorpSafeMemberCallback12B1(file, line, o, p, ba1); } }; template struct XorpMemberCallbackFactory12B1 { static XorpMemberCallback12B1* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1), BA1 ba1) { return new XorpMemberCallback12B1(file, line, o, p, ba1); }; }; /** * Factory function that creates a callback object targetted at a * member function with 12 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback12::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1), BA1 ba1) { return XorpMemberCallbackFactory12B1::True>::make(file, line, o, p, ba1); } /** * Factory function that creates a callback object targetted at a * member function with 12 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback12::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1), BA1 ba1) { return XorpMemberCallbackFactory12B1::True>::make(file, line, &o, p, ba1); } /** * @short Callback object for const member methods with 12 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstMemberCallback12B1 : public XorpCallback12 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1) const; XorpConstMemberCallback12B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpCallback12(file, line), _o(o), _m(m), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for void const member methods with 12 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstMemberCallback12B1 : public XorpCallback12 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1) const; XorpConstMemberCallback12B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpCallback12(file, line), _o(o), _m(m), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for const safe member methods with 12 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback12B1 : public XorpConstMemberCallback12B1, public SafeCallbackBase { typedef typename XorpConstMemberCallback12B1::M M; XorpConstSafeMemberCallback12B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpConstMemberCallback12B1(file, line, o, m, ba1), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback12B1() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback12B1::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 12 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback12B1 : public XorpConstMemberCallback12B1, public SafeCallbackBase { typedef typename XorpConstMemberCallback12B1::M M; XorpConstSafeMemberCallback12B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpConstMemberCallback12B1(file, line, o, m, ba1), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback12B1() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback12B1::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory12B1 { static XorpConstMemberCallback12B1* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1) const, BA1 ba1) { return new XorpConstSafeMemberCallback12B1(file, line, o, p, ba1); } }; template struct XorpConstMemberCallbackFactory12B1 { static XorpConstMemberCallback12B1* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1) const, BA1 ba1) { return new XorpConstMemberCallback12B1(file, line, o, p, ba1); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 12 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback12::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1) const, BA1 ba1) { return XorpConstMemberCallbackFactory12B1::True>::make(file, line, o, p, ba1); } /** * Factory function that creates a callback object targetted at a * const member function with 12 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback12::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1) const, BA1 ba1) { return XorpConstMemberCallbackFactory12B1::True>::make(file, line, &o, p, ba1); } /** * @short Callback object for functions with 12 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpFunctionCallback12B2 : public XorpCallback12 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2); XorpFunctionCallback12B2(const char* file, int line, F f, BA1 ba1, BA2 ba2) : XorpCallback12(file, line), _f(f), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1, _ba2); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; }; /** * @short Callback object for void functions with 12 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpFunctionCallback12B2 : public XorpCallback12 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2); XorpFunctionCallback12B2(const char* file, int line, F f, BA1 ba1, BA2 ba2) : XorpCallback12(file, line), _f(f), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1, _ba2); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; }; /** * Factory function that creates a callback object targetted at a * function with 12 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback12::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2), BA1 ba1, BA2 ba2) { return typename XorpCallback12::RefPtr(new XorpFunctionCallback12B2(file, line, f, ba1, ba2)); } /** * @short Callback object for member methods with 12 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpMemberCallback12B2 : public XorpCallback12 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2) ; XorpMemberCallback12B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback12(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1, _ba2); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for void member methods with 12 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpMemberCallback12B2 : public XorpCallback12 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2) ; XorpMemberCallback12B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback12(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1, _ba2); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for safe member methods with 12 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpSafeMemberCallback12B2 : public XorpMemberCallback12B2, public SafeCallbackBase { typedef typename XorpMemberCallback12B2::M M; XorpSafeMemberCallback12B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpMemberCallback12B2(file, line, o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpSafeMemberCallback12B2() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback12B2::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 12 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpSafeMemberCallback12B2 : public XorpMemberCallback12B2, public SafeCallbackBase { typedef typename XorpMemberCallback12B2::M M; XorpSafeMemberCallback12B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpMemberCallback12B2(file, line, o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpSafeMemberCallback12B2() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { if (valid()) { record_dispatch_enter(); XorpMemberCallback12B2::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory12B2 { static XorpMemberCallback12B2* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2), BA1 ba1, BA2 ba2) { return new XorpSafeMemberCallback12B2(file, line, o, p, ba1, ba2); } }; template struct XorpMemberCallbackFactory12B2 { static XorpMemberCallback12B2* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2), BA1 ba1, BA2 ba2) { return new XorpMemberCallback12B2(file, line, o, p, ba1, ba2); }; }; /** * Factory function that creates a callback object targetted at a * member function with 12 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback12::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2), BA1 ba1, BA2 ba2) { return XorpMemberCallbackFactory12B2::True>::make(file, line, o, p, ba1, ba2); } /** * Factory function that creates a callback object targetted at a * member function with 12 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback12::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2), BA1 ba1, BA2 ba2) { return XorpMemberCallbackFactory12B2::True>::make(file, line, &o, p, ba1, ba2); } /** * @short Callback object for const member methods with 12 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstMemberCallback12B2 : public XorpCallback12 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2) const; XorpConstMemberCallback12B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback12(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1, _ba2); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for void const member methods with 12 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstMemberCallback12B2 : public XorpCallback12 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2) const; XorpConstMemberCallback12B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback12(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1, _ba2); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for const safe member methods with 12 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback12B2 : public XorpConstMemberCallback12B2, public SafeCallbackBase { typedef typename XorpConstMemberCallback12B2::M M; XorpConstSafeMemberCallback12B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpConstMemberCallback12B2(file, line, o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback12B2() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback12B2::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 12 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback12B2 : public XorpConstMemberCallback12B2, public SafeCallbackBase { typedef typename XorpConstMemberCallback12B2::M M; XorpConstSafeMemberCallback12B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpConstMemberCallback12B2(file, line, o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback12B2() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback12B2::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory12B2 { static XorpConstMemberCallback12B2* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2) const, BA1 ba1, BA2 ba2) { return new XorpConstSafeMemberCallback12B2(file, line, o, p, ba1, ba2); } }; template struct XorpConstMemberCallbackFactory12B2 { static XorpConstMemberCallback12B2* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2) const, BA1 ba1, BA2 ba2) { return new XorpConstMemberCallback12B2(file, line, o, p, ba1, ba2); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 12 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback12::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2) const, BA1 ba1, BA2 ba2) { return XorpConstMemberCallbackFactory12B2::True>::make(file, line, o, p, ba1, ba2); } /** * Factory function that creates a callback object targetted at a * const member function with 12 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback12::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2) const, BA1 ba1, BA2 ba2) { return XorpConstMemberCallbackFactory12B2::True>::make(file, line, &o, p, ba1, ba2); } /** * @short Callback object for functions with 12 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpFunctionCallback12B3 : public XorpCallback12 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3); XorpFunctionCallback12B3(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback12(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1, _ba2, _ba3); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; }; /** * @short Callback object for void functions with 12 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpFunctionCallback12B3 : public XorpCallback12 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3); XorpFunctionCallback12B3(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback12(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1, _ba2, _ba3); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; }; /** * Factory function that creates a callback object targetted at a * function with 12 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback12::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return typename XorpCallback12::RefPtr(new XorpFunctionCallback12B3(file, line, f, ba1, ba2, ba3)); } /** * @short Callback object for member methods with 12 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpMemberCallback12B3 : public XorpCallback12 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3) ; XorpMemberCallback12B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback12(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1, _ba2, _ba3); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for void member methods with 12 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpMemberCallback12B3 : public XorpCallback12 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3) ; XorpMemberCallback12B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback12(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1, _ba2, _ba3); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for safe member methods with 12 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpSafeMemberCallback12B3 : public XorpMemberCallback12B3, public SafeCallbackBase { typedef typename XorpMemberCallback12B3::M M; XorpSafeMemberCallback12B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpMemberCallback12B3(file, line, o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpSafeMemberCallback12B3() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback12B3::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 12 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpSafeMemberCallback12B3 : public XorpMemberCallback12B3, public SafeCallbackBase { typedef typename XorpMemberCallback12B3::M M; XorpSafeMemberCallback12B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpMemberCallback12B3(file, line, o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpSafeMemberCallback12B3() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { if (valid()) { record_dispatch_enter(); XorpMemberCallback12B3::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory12B3 { static XorpMemberCallback12B3* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpSafeMemberCallback12B3(file, line, o, p, ba1, ba2, ba3); } }; template struct XorpMemberCallbackFactory12B3 { static XorpMemberCallback12B3* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpMemberCallback12B3(file, line, o, p, ba1, ba2, ba3); }; }; /** * Factory function that creates a callback object targetted at a * member function with 12 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback12::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return XorpMemberCallbackFactory12B3::True>::make(file, line, o, p, ba1, ba2, ba3); } /** * Factory function that creates a callback object targetted at a * member function with 12 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback12::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return XorpMemberCallbackFactory12B3::True>::make(file, line, &o, p, ba1, ba2, ba3); } /** * @short Callback object for const member methods with 12 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstMemberCallback12B3 : public XorpCallback12 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3) const; XorpConstMemberCallback12B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback12(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1, _ba2, _ba3); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for void const member methods with 12 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstMemberCallback12B3 : public XorpCallback12 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3) const; XorpConstMemberCallback12B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback12(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1, _ba2, _ba3); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for const safe member methods with 12 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback12B3 : public XorpConstMemberCallback12B3, public SafeCallbackBase { typedef typename XorpConstMemberCallback12B3::M M; XorpConstSafeMemberCallback12B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpConstMemberCallback12B3(file, line, o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback12B3() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback12B3::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 12 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback12B3 : public XorpConstMemberCallback12B3, public SafeCallbackBase { typedef typename XorpConstMemberCallback12B3::M M; XorpConstSafeMemberCallback12B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpConstMemberCallback12B3(file, line, o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback12B3() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback12B3::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory12B3 { static XorpConstMemberCallback12B3* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpConstSafeMemberCallback12B3(file, line, o, p, ba1, ba2, ba3); } }; template struct XorpConstMemberCallbackFactory12B3 { static XorpConstMemberCallback12B3* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpConstMemberCallback12B3(file, line, o, p, ba1, ba2, ba3); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 12 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback12::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return XorpConstMemberCallbackFactory12B3::True>::make(file, line, o, p, ba1, ba2, ba3); } /** * Factory function that creates a callback object targetted at a * const member function with 12 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback12::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return XorpConstMemberCallbackFactory12B3::True>::make(file, line, &o, p, ba1, ba2, ba3); } /** * @short Callback object for functions with 12 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpFunctionCallback12B4 : public XorpCallback12 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4); XorpFunctionCallback12B4(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback12(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; }; /** * @short Callback object for void functions with 12 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpFunctionCallback12B4 : public XorpCallback12 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4); XorpFunctionCallback12B4(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback12(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; }; /** * Factory function that creates a callback object targetted at a * function with 12 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback12::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return typename XorpCallback12::RefPtr(new XorpFunctionCallback12B4(file, line, f, ba1, ba2, ba3, ba4)); } /** * @short Callback object for member methods with 12 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpMemberCallback12B4 : public XorpCallback12 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4) ; XorpMemberCallback12B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback12(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for void member methods with 12 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpMemberCallback12B4 : public XorpCallback12 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4) ; XorpMemberCallback12B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback12(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for safe member methods with 12 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpSafeMemberCallback12B4 : public XorpMemberCallback12B4, public SafeCallbackBase { typedef typename XorpMemberCallback12B4::M M; XorpSafeMemberCallback12B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpMemberCallback12B4(file, line, o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpSafeMemberCallback12B4() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback12B4::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 12 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpSafeMemberCallback12B4 : public XorpMemberCallback12B4, public SafeCallbackBase { typedef typename XorpMemberCallback12B4::M M; XorpSafeMemberCallback12B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpMemberCallback12B4(file, line, o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpSafeMemberCallback12B4() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { if (valid()) { record_dispatch_enter(); XorpMemberCallback12B4::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory12B4 { static XorpMemberCallback12B4* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpSafeMemberCallback12B4(file, line, o, p, ba1, ba2, ba3, ba4); } }; template struct XorpMemberCallbackFactory12B4 { static XorpMemberCallback12B4* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpMemberCallback12B4(file, line, o, p, ba1, ba2, ba3, ba4); }; }; /** * Factory function that creates a callback object targetted at a * member function with 12 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback12::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpMemberCallbackFactory12B4::True>::make(file, line, o, p, ba1, ba2, ba3, ba4); } /** * Factory function that creates a callback object targetted at a * member function with 12 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback12::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpMemberCallbackFactory12B4::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4); } /** * @short Callback object for const member methods with 12 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstMemberCallback12B4 : public XorpCallback12 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4) const; XorpConstMemberCallback12B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback12(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for void const member methods with 12 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstMemberCallback12B4 : public XorpCallback12 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4) const; XorpConstMemberCallback12B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback12(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for const safe member methods with 12 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback12B4 : public XorpConstMemberCallback12B4, public SafeCallbackBase { typedef typename XorpConstMemberCallback12B4::M M; XorpConstSafeMemberCallback12B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpConstMemberCallback12B4(file, line, o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback12B4() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback12B4::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 12 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback12B4 : public XorpConstMemberCallback12B4, public SafeCallbackBase { typedef typename XorpConstMemberCallback12B4::M M; XorpConstSafeMemberCallback12B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpConstMemberCallback12B4(file, line, o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback12B4() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback12B4::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory12B4 { static XorpConstMemberCallback12B4* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpConstSafeMemberCallback12B4(file, line, o, p, ba1, ba2, ba3, ba4); } }; template struct XorpConstMemberCallbackFactory12B4 { static XorpConstMemberCallback12B4* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpConstMemberCallback12B4(file, line, o, p, ba1, ba2, ba3, ba4); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 12 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback12::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpConstMemberCallbackFactory12B4::True>::make(file, line, o, p, ba1, ba2, ba3, ba4); } /** * Factory function that creates a callback object targetted at a * const member function with 12 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback12::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpConstMemberCallbackFactory12B4::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4); } /** * @short Callback object for functions with 12 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpFunctionCallback12B5 : public XorpCallback12 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4, BA5); XorpFunctionCallback12B5(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback12(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; }; /** * @short Callback object for void functions with 12 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpFunctionCallback12B5 : public XorpCallback12 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4, BA5); XorpFunctionCallback12B5(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback12(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; }; /** * Factory function that creates a callback object targetted at a * function with 12 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback12::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return typename XorpCallback12::RefPtr(new XorpFunctionCallback12B5(file, line, f, ba1, ba2, ba3, ba4, ba5)); } /** * @short Callback object for member methods with 12 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpMemberCallback12B5 : public XorpCallback12 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4, BA5) ; XorpMemberCallback12B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback12(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for void member methods with 12 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpMemberCallback12B5 : public XorpCallback12 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4, BA5) ; XorpMemberCallback12B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback12(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for safe member methods with 12 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpSafeMemberCallback12B5 : public XorpMemberCallback12B5, public SafeCallbackBase { typedef typename XorpMemberCallback12B5::M M; XorpSafeMemberCallback12B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpMemberCallback12B5(file, line, o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpSafeMemberCallback12B5() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback12B5::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 12 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpSafeMemberCallback12B5 : public XorpMemberCallback12B5, public SafeCallbackBase { typedef typename XorpMemberCallback12B5::M M; XorpSafeMemberCallback12B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpMemberCallback12B5(file, line, o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpSafeMemberCallback12B5() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { if (valid()) { record_dispatch_enter(); XorpMemberCallback12B5::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory12B5 { static XorpMemberCallback12B5* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpSafeMemberCallback12B5(file, line, o, p, ba1, ba2, ba3, ba4, ba5); } }; template struct XorpMemberCallbackFactory12B5 { static XorpMemberCallback12B5* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpMemberCallback12B5(file, line, o, p, ba1, ba2, ba3, ba4, ba5); }; }; /** * Factory function that creates a callback object targetted at a * member function with 12 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback12::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpMemberCallbackFactory12B5::True>::make(file, line, o, p, ba1, ba2, ba3, ba4, ba5); } /** * Factory function that creates a callback object targetted at a * member function with 12 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback12::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpMemberCallbackFactory12B5::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4, ba5); } /** * @short Callback object for const member methods with 12 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstMemberCallback12B5 : public XorpCallback12 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4, BA5) const; XorpConstMemberCallback12B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback12(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for void const member methods with 12 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstMemberCallback12B5 : public XorpCallback12 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4, BA5) const; XorpConstMemberCallback12B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback12(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for const safe member methods with 12 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback12B5 : public XorpConstMemberCallback12B5, public SafeCallbackBase { typedef typename XorpConstMemberCallback12B5::M M; XorpConstSafeMemberCallback12B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpConstMemberCallback12B5(file, line, o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback12B5() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback12B5::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 12 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback12B5 : public XorpConstMemberCallback12B5, public SafeCallbackBase { typedef typename XorpConstMemberCallback12B5::M M; XorpConstSafeMemberCallback12B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpConstMemberCallback12B5(file, line, o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback12B5() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback12B5::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory12B5 { static XorpConstMemberCallback12B5* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpConstSafeMemberCallback12B5(file, line, o, p, ba1, ba2, ba3, ba4, ba5); } }; template struct XorpConstMemberCallbackFactory12B5 { static XorpConstMemberCallback12B5* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpConstMemberCallback12B5(file, line, o, p, ba1, ba2, ba3, ba4, ba5); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 12 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback12::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpConstMemberCallbackFactory12B5::True>::make(file, line, o, p, ba1, ba2, ba3, ba4, ba5); } /** * Factory function that creates a callback object targetted at a * const member function with 12 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback12::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpConstMemberCallbackFactory12B5::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4, ba5); } /** * @short Callback object for functions with 12 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpFunctionCallback12B6 : public XorpCallback12 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4, BA5, BA6); XorpFunctionCallback12B6(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback12(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; BA6 _ba6; }; /** * @short Callback object for void functions with 12 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpFunctionCallback12B6 : public XorpCallback12 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4, BA5, BA6); XorpFunctionCallback12B6(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback12(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; BA6 _ba6; }; /** * Factory function that creates a callback object targetted at a * function with 12 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback12::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return typename XorpCallback12::RefPtr(new XorpFunctionCallback12B6(file, line, f, ba1, ba2, ba3, ba4, ba5, ba6)); } /** * @short Callback object for member methods with 12 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpMemberCallback12B6 : public XorpCallback12 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4, BA5, BA6) ; XorpMemberCallback12B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback12(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for void member methods with 12 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpMemberCallback12B6 : public XorpCallback12 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4, BA5, BA6) ; XorpMemberCallback12B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback12(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for safe member methods with 12 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpSafeMemberCallback12B6 : public XorpMemberCallback12B6, public SafeCallbackBase { typedef typename XorpMemberCallback12B6::M M; XorpSafeMemberCallback12B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpMemberCallback12B6(file, line, o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpSafeMemberCallback12B6() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback12B6::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 12 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpSafeMemberCallback12B6 : public XorpMemberCallback12B6, public SafeCallbackBase { typedef typename XorpMemberCallback12B6::M M; XorpSafeMemberCallback12B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpMemberCallback12B6(file, line, o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpSafeMemberCallback12B6() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { if (valid()) { record_dispatch_enter(); XorpMemberCallback12B6::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory12B6 { static XorpMemberCallback12B6* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpSafeMemberCallback12B6(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); } }; template struct XorpMemberCallbackFactory12B6 { static XorpMemberCallback12B6* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpMemberCallback12B6(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); }; }; /** * Factory function that creates a callback object targetted at a * member function with 12 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback12::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpMemberCallbackFactory12B6::True>::make(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * Factory function that creates a callback object targetted at a * member function with 12 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback12::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpMemberCallbackFactory12B6::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * @short Callback object for const member methods with 12 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstMemberCallback12B6 : public XorpCallback12 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4, BA5, BA6) const; XorpConstMemberCallback12B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback12(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for void const member methods with 12 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstMemberCallback12B6 : public XorpCallback12 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4, BA5, BA6) const; XorpConstMemberCallback12B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback12(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for const safe member methods with 12 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback12B6 : public XorpConstMemberCallback12B6, public SafeCallbackBase { typedef typename XorpConstMemberCallback12B6::M M; XorpConstSafeMemberCallback12B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpConstMemberCallback12B6(file, line, o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback12B6() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback12B6::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 12 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback12B6 : public XorpConstMemberCallback12B6, public SafeCallbackBase { typedef typename XorpConstMemberCallback12B6::M M; XorpConstSafeMemberCallback12B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpConstMemberCallback12B6(file, line, o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback12B6() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback12B6::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory12B6 { static XorpConstMemberCallback12B6* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpConstSafeMemberCallback12B6(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); } }; template struct XorpConstMemberCallbackFactory12B6 { static XorpConstMemberCallback12B6* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpConstMemberCallback12B6(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 12 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback12::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpConstMemberCallbackFactory12B6::True>::make(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * Factory function that creates a callback object targetted at a * const member function with 12 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback12::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpConstMemberCallbackFactory12B6::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /////////////////////////////////////////////////////////////////////////////// // // Code relating to callbacks with 13 late args // /** * @short Base class for callbacks with 13 dispatch time args. */ template struct XorpCallback13 { typedef ref_ptr RefPtr; XorpCallback13(const char* file, int line) : _file(file), _line(line) {} virtual ~XorpCallback13() {} virtual R dispatch(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13) = 0; const char* file() const { return _file; } int line() const { return _line; } private: const char* _file; int _line; }; /** * @short Callback object for functions with 13 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpFunctionCallback13B0 : public XorpCallback13 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13); XorpFunctionCallback13B0(const char* file, int line, F f) : XorpCallback13(file, line), _f(f) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); record_dispatch_leave(); return r; } protected: F _f; }; /** * @short Callback object for void functions with 13 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpFunctionCallback13B0 : public XorpCallback13 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13); XorpFunctionCallback13B0(const char* file, int line, F f) : XorpCallback13(file, line), _f(f) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); record_dispatch_leave(); } protected: F _f; }; /** * Factory function that creates a callback object targetted at a * function with 13 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback13::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13)) { return typename XorpCallback13::RefPtr(new XorpFunctionCallback13B0(file, line, f)); } /** * @short Callback object for member methods with 13 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpMemberCallback13B0 : public XorpCallback13 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13) ; XorpMemberCallback13B0(const char* file, int line, O* o, M m) : XorpCallback13(file, line), _o(o), _m(m) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for void member methods with 13 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpMemberCallback13B0 : public XorpCallback13 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13) ; XorpMemberCallback13B0(const char* file, int line, O* o, M m) : XorpCallback13(file, line), _o(o), _m(m) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for safe member methods with 13 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpSafeMemberCallback13B0 : public XorpMemberCallback13B0, public SafeCallbackBase { typedef typename XorpMemberCallback13B0::M M; XorpSafeMemberCallback13B0(const char* file, int line, O* o, M m) : XorpMemberCallback13B0(file, line, o, m), SafeCallbackBase(o) {} ~XorpSafeMemberCallback13B0() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback13B0::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 13 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpSafeMemberCallback13B0 : public XorpMemberCallback13B0, public SafeCallbackBase { typedef typename XorpMemberCallback13B0::M M; XorpSafeMemberCallback13B0(const char* file, int line, O* o, M m) : XorpMemberCallback13B0(file, line, o, m), SafeCallbackBase(o) {} ~XorpSafeMemberCallback13B0() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { if (valid()) { record_dispatch_enter(); XorpMemberCallback13B0::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory13B0 { static XorpMemberCallback13B0* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13)) { return new XorpSafeMemberCallback13B0(file, line, o, p); } }; template struct XorpMemberCallbackFactory13B0 { static XorpMemberCallback13B0* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13)) { return new XorpMemberCallback13B0(file, line, o, p); }; }; /** * Factory function that creates a callback object targetted at a * member function with 13 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback13::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13)) { return XorpMemberCallbackFactory13B0::True>::make(file, line, o, p); } /** * Factory function that creates a callback object targetted at a * member function with 13 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback13::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13)) { return XorpMemberCallbackFactory13B0::True>::make(file, line, &o, p); } /** * @short Callback object for const member methods with 13 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstMemberCallback13B0 : public XorpCallback13 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13) const; XorpConstMemberCallback13B0(const char* file, int line, O* o, M m) : XorpCallback13(file, line), _o(o), _m(m) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for void const member methods with 13 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstMemberCallback13B0 : public XorpCallback13 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13) const; XorpConstMemberCallback13B0(const char* file, int line, O* o, M m) : XorpCallback13(file, line), _o(o), _m(m) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for const safe member methods with 13 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback13B0 : public XorpConstMemberCallback13B0, public SafeCallbackBase { typedef typename XorpConstMemberCallback13B0::M M; XorpConstSafeMemberCallback13B0(const char* file, int line, O* o, M m) : XorpConstMemberCallback13B0(file, line, o, m), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback13B0() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback13B0::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 13 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback13B0 : public XorpConstMemberCallback13B0, public SafeCallbackBase { typedef typename XorpConstMemberCallback13B0::M M; XorpConstSafeMemberCallback13B0(const char* file, int line, O* o, M m) : XorpConstMemberCallback13B0(file, line, o, m), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback13B0() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback13B0::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory13B0 { static XorpConstMemberCallback13B0* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13) const) { return new XorpConstSafeMemberCallback13B0(file, line, o, p); } }; template struct XorpConstMemberCallbackFactory13B0 { static XorpConstMemberCallback13B0* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13) const) { return new XorpConstMemberCallback13B0(file, line, o, p); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 13 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback13::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13) const) { return XorpConstMemberCallbackFactory13B0::True>::make(file, line, o, p); } /** * Factory function that creates a callback object targetted at a * const member function with 13 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback13::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13) const) { return XorpConstMemberCallbackFactory13B0::True>::make(file, line, &o, p); } /** * @short Callback object for functions with 13 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpFunctionCallback13B1 : public XorpCallback13 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1); XorpFunctionCallback13B1(const char* file, int line, F f, BA1 ba1) : XorpCallback13(file, line), _f(f), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; }; /** * @short Callback object for void functions with 13 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpFunctionCallback13B1 : public XorpCallback13 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1); XorpFunctionCallback13B1(const char* file, int line, F f, BA1 ba1) : XorpCallback13(file, line), _f(f), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1); record_dispatch_leave(); } protected: F _f; BA1 _ba1; }; /** * Factory function that creates a callback object targetted at a * function with 13 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback13::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1), BA1 ba1) { return typename XorpCallback13::RefPtr(new XorpFunctionCallback13B1(file, line, f, ba1)); } /** * @short Callback object for member methods with 13 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpMemberCallback13B1 : public XorpCallback13 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1) ; XorpMemberCallback13B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpCallback13(file, line), _o(o), _m(m), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for void member methods with 13 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpMemberCallback13B1 : public XorpCallback13 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1) ; XorpMemberCallback13B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpCallback13(file, line), _o(o), _m(m), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for safe member methods with 13 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpSafeMemberCallback13B1 : public XorpMemberCallback13B1, public SafeCallbackBase { typedef typename XorpMemberCallback13B1::M M; XorpSafeMemberCallback13B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpMemberCallback13B1(file, line, o, m, ba1), SafeCallbackBase(o) {} ~XorpSafeMemberCallback13B1() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback13B1::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 13 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpSafeMemberCallback13B1 : public XorpMemberCallback13B1, public SafeCallbackBase { typedef typename XorpMemberCallback13B1::M M; XorpSafeMemberCallback13B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpMemberCallback13B1(file, line, o, m, ba1), SafeCallbackBase(o) {} ~XorpSafeMemberCallback13B1() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { if (valid()) { record_dispatch_enter(); XorpMemberCallback13B1::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory13B1 { static XorpMemberCallback13B1* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1), BA1 ba1) { return new XorpSafeMemberCallback13B1(file, line, o, p, ba1); } }; template struct XorpMemberCallbackFactory13B1 { static XorpMemberCallback13B1* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1), BA1 ba1) { return new XorpMemberCallback13B1(file, line, o, p, ba1); }; }; /** * Factory function that creates a callback object targetted at a * member function with 13 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback13::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1), BA1 ba1) { return XorpMemberCallbackFactory13B1::True>::make(file, line, o, p, ba1); } /** * Factory function that creates a callback object targetted at a * member function with 13 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback13::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1), BA1 ba1) { return XorpMemberCallbackFactory13B1::True>::make(file, line, &o, p, ba1); } /** * @short Callback object for const member methods with 13 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstMemberCallback13B1 : public XorpCallback13 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1) const; XorpConstMemberCallback13B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpCallback13(file, line), _o(o), _m(m), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for void const member methods with 13 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstMemberCallback13B1 : public XorpCallback13 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1) const; XorpConstMemberCallback13B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpCallback13(file, line), _o(o), _m(m), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for const safe member methods with 13 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback13B1 : public XorpConstMemberCallback13B1, public SafeCallbackBase { typedef typename XorpConstMemberCallback13B1::M M; XorpConstSafeMemberCallback13B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpConstMemberCallback13B1(file, line, o, m, ba1), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback13B1() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback13B1::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 13 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback13B1 : public XorpConstMemberCallback13B1, public SafeCallbackBase { typedef typename XorpConstMemberCallback13B1::M M; XorpConstSafeMemberCallback13B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpConstMemberCallback13B1(file, line, o, m, ba1), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback13B1() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback13B1::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory13B1 { static XorpConstMemberCallback13B1* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1) const, BA1 ba1) { return new XorpConstSafeMemberCallback13B1(file, line, o, p, ba1); } }; template struct XorpConstMemberCallbackFactory13B1 { static XorpConstMemberCallback13B1* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1) const, BA1 ba1) { return new XorpConstMemberCallback13B1(file, line, o, p, ba1); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 13 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback13::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1) const, BA1 ba1) { return XorpConstMemberCallbackFactory13B1::True>::make(file, line, o, p, ba1); } /** * Factory function that creates a callback object targetted at a * const member function with 13 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback13::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1) const, BA1 ba1) { return XorpConstMemberCallbackFactory13B1::True>::make(file, line, &o, p, ba1); } /** * @short Callback object for functions with 13 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpFunctionCallback13B2 : public XorpCallback13 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2); XorpFunctionCallback13B2(const char* file, int line, F f, BA1 ba1, BA2 ba2) : XorpCallback13(file, line), _f(f), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1, _ba2); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; }; /** * @short Callback object for void functions with 13 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpFunctionCallback13B2 : public XorpCallback13 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2); XorpFunctionCallback13B2(const char* file, int line, F f, BA1 ba1, BA2 ba2) : XorpCallback13(file, line), _f(f), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1, _ba2); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; }; /** * Factory function that creates a callback object targetted at a * function with 13 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback13::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2), BA1 ba1, BA2 ba2) { return typename XorpCallback13::RefPtr(new XorpFunctionCallback13B2(file, line, f, ba1, ba2)); } /** * @short Callback object for member methods with 13 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpMemberCallback13B2 : public XorpCallback13 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2) ; XorpMemberCallback13B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback13(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1, _ba2); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for void member methods with 13 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpMemberCallback13B2 : public XorpCallback13 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2) ; XorpMemberCallback13B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback13(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1, _ba2); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for safe member methods with 13 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpSafeMemberCallback13B2 : public XorpMemberCallback13B2, public SafeCallbackBase { typedef typename XorpMemberCallback13B2::M M; XorpSafeMemberCallback13B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpMemberCallback13B2(file, line, o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpSafeMemberCallback13B2() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback13B2::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 13 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpSafeMemberCallback13B2 : public XorpMemberCallback13B2, public SafeCallbackBase { typedef typename XorpMemberCallback13B2::M M; XorpSafeMemberCallback13B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpMemberCallback13B2(file, line, o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpSafeMemberCallback13B2() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { if (valid()) { record_dispatch_enter(); XorpMemberCallback13B2::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory13B2 { static XorpMemberCallback13B2* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2), BA1 ba1, BA2 ba2) { return new XorpSafeMemberCallback13B2(file, line, o, p, ba1, ba2); } }; template struct XorpMemberCallbackFactory13B2 { static XorpMemberCallback13B2* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2), BA1 ba1, BA2 ba2) { return new XorpMemberCallback13B2(file, line, o, p, ba1, ba2); }; }; /** * Factory function that creates a callback object targetted at a * member function with 13 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback13::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2), BA1 ba1, BA2 ba2) { return XorpMemberCallbackFactory13B2::True>::make(file, line, o, p, ba1, ba2); } /** * Factory function that creates a callback object targetted at a * member function with 13 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback13::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2), BA1 ba1, BA2 ba2) { return XorpMemberCallbackFactory13B2::True>::make(file, line, &o, p, ba1, ba2); } /** * @short Callback object for const member methods with 13 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstMemberCallback13B2 : public XorpCallback13 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2) const; XorpConstMemberCallback13B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback13(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1, _ba2); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for void const member methods with 13 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstMemberCallback13B2 : public XorpCallback13 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2) const; XorpConstMemberCallback13B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback13(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1, _ba2); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for const safe member methods with 13 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback13B2 : public XorpConstMemberCallback13B2, public SafeCallbackBase { typedef typename XorpConstMemberCallback13B2::M M; XorpConstSafeMemberCallback13B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpConstMemberCallback13B2(file, line, o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback13B2() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback13B2::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 13 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback13B2 : public XorpConstMemberCallback13B2, public SafeCallbackBase { typedef typename XorpConstMemberCallback13B2::M M; XorpConstSafeMemberCallback13B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpConstMemberCallback13B2(file, line, o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback13B2() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback13B2::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory13B2 { static XorpConstMemberCallback13B2* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2) const, BA1 ba1, BA2 ba2) { return new XorpConstSafeMemberCallback13B2(file, line, o, p, ba1, ba2); } }; template struct XorpConstMemberCallbackFactory13B2 { static XorpConstMemberCallback13B2* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2) const, BA1 ba1, BA2 ba2) { return new XorpConstMemberCallback13B2(file, line, o, p, ba1, ba2); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 13 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback13::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2) const, BA1 ba1, BA2 ba2) { return XorpConstMemberCallbackFactory13B2::True>::make(file, line, o, p, ba1, ba2); } /** * Factory function that creates a callback object targetted at a * const member function with 13 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback13::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2) const, BA1 ba1, BA2 ba2) { return XorpConstMemberCallbackFactory13B2::True>::make(file, line, &o, p, ba1, ba2); } /** * @short Callback object for functions with 13 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpFunctionCallback13B3 : public XorpCallback13 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3); XorpFunctionCallback13B3(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback13(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1, _ba2, _ba3); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; }; /** * @short Callback object for void functions with 13 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpFunctionCallback13B3 : public XorpCallback13 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3); XorpFunctionCallback13B3(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback13(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1, _ba2, _ba3); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; }; /** * Factory function that creates a callback object targetted at a * function with 13 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback13::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return typename XorpCallback13::RefPtr(new XorpFunctionCallback13B3(file, line, f, ba1, ba2, ba3)); } /** * @short Callback object for member methods with 13 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpMemberCallback13B3 : public XorpCallback13 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3) ; XorpMemberCallback13B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback13(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1, _ba2, _ba3); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for void member methods with 13 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpMemberCallback13B3 : public XorpCallback13 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3) ; XorpMemberCallback13B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback13(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1, _ba2, _ba3); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for safe member methods with 13 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpSafeMemberCallback13B3 : public XorpMemberCallback13B3, public SafeCallbackBase { typedef typename XorpMemberCallback13B3::M M; XorpSafeMemberCallback13B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpMemberCallback13B3(file, line, o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpSafeMemberCallback13B3() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback13B3::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 13 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpSafeMemberCallback13B3 : public XorpMemberCallback13B3, public SafeCallbackBase { typedef typename XorpMemberCallback13B3::M M; XorpSafeMemberCallback13B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpMemberCallback13B3(file, line, o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpSafeMemberCallback13B3() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { if (valid()) { record_dispatch_enter(); XorpMemberCallback13B3::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory13B3 { static XorpMemberCallback13B3* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpSafeMemberCallback13B3(file, line, o, p, ba1, ba2, ba3); } }; template struct XorpMemberCallbackFactory13B3 { static XorpMemberCallback13B3* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpMemberCallback13B3(file, line, o, p, ba1, ba2, ba3); }; }; /** * Factory function that creates a callback object targetted at a * member function with 13 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback13::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return XorpMemberCallbackFactory13B3::True>::make(file, line, o, p, ba1, ba2, ba3); } /** * Factory function that creates a callback object targetted at a * member function with 13 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback13::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return XorpMemberCallbackFactory13B3::True>::make(file, line, &o, p, ba1, ba2, ba3); } /** * @short Callback object for const member methods with 13 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstMemberCallback13B3 : public XorpCallback13 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3) const; XorpConstMemberCallback13B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback13(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1, _ba2, _ba3); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for void const member methods with 13 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstMemberCallback13B3 : public XorpCallback13 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3) const; XorpConstMemberCallback13B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback13(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1, _ba2, _ba3); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for const safe member methods with 13 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback13B3 : public XorpConstMemberCallback13B3, public SafeCallbackBase { typedef typename XorpConstMemberCallback13B3::M M; XorpConstSafeMemberCallback13B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpConstMemberCallback13B3(file, line, o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback13B3() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback13B3::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 13 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback13B3 : public XorpConstMemberCallback13B3, public SafeCallbackBase { typedef typename XorpConstMemberCallback13B3::M M; XorpConstSafeMemberCallback13B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpConstMemberCallback13B3(file, line, o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback13B3() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback13B3::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory13B3 { static XorpConstMemberCallback13B3* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpConstSafeMemberCallback13B3(file, line, o, p, ba1, ba2, ba3); } }; template struct XorpConstMemberCallbackFactory13B3 { static XorpConstMemberCallback13B3* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpConstMemberCallback13B3(file, line, o, p, ba1, ba2, ba3); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 13 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback13::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return XorpConstMemberCallbackFactory13B3::True>::make(file, line, o, p, ba1, ba2, ba3); } /** * Factory function that creates a callback object targetted at a * const member function with 13 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback13::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return XorpConstMemberCallbackFactory13B3::True>::make(file, line, &o, p, ba1, ba2, ba3); } /** * @short Callback object for functions with 13 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpFunctionCallback13B4 : public XorpCallback13 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4); XorpFunctionCallback13B4(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback13(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; }; /** * @short Callback object for void functions with 13 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpFunctionCallback13B4 : public XorpCallback13 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4); XorpFunctionCallback13B4(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback13(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; }; /** * Factory function that creates a callback object targetted at a * function with 13 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback13::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return typename XorpCallback13::RefPtr(new XorpFunctionCallback13B4(file, line, f, ba1, ba2, ba3, ba4)); } /** * @short Callback object for member methods with 13 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpMemberCallback13B4 : public XorpCallback13 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4) ; XorpMemberCallback13B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback13(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for void member methods with 13 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpMemberCallback13B4 : public XorpCallback13 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4) ; XorpMemberCallback13B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback13(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for safe member methods with 13 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpSafeMemberCallback13B4 : public XorpMemberCallback13B4, public SafeCallbackBase { typedef typename XorpMemberCallback13B4::M M; XorpSafeMemberCallback13B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpMemberCallback13B4(file, line, o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpSafeMemberCallback13B4() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback13B4::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 13 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpSafeMemberCallback13B4 : public XorpMemberCallback13B4, public SafeCallbackBase { typedef typename XorpMemberCallback13B4::M M; XorpSafeMemberCallback13B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpMemberCallback13B4(file, line, o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpSafeMemberCallback13B4() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { if (valid()) { record_dispatch_enter(); XorpMemberCallback13B4::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory13B4 { static XorpMemberCallback13B4* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpSafeMemberCallback13B4(file, line, o, p, ba1, ba2, ba3, ba4); } }; template struct XorpMemberCallbackFactory13B4 { static XorpMemberCallback13B4* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpMemberCallback13B4(file, line, o, p, ba1, ba2, ba3, ba4); }; }; /** * Factory function that creates a callback object targetted at a * member function with 13 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback13::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpMemberCallbackFactory13B4::True>::make(file, line, o, p, ba1, ba2, ba3, ba4); } /** * Factory function that creates a callback object targetted at a * member function with 13 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback13::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpMemberCallbackFactory13B4::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4); } /** * @short Callback object for const member methods with 13 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstMemberCallback13B4 : public XorpCallback13 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4) const; XorpConstMemberCallback13B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback13(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for void const member methods with 13 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstMemberCallback13B4 : public XorpCallback13 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4) const; XorpConstMemberCallback13B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback13(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for const safe member methods with 13 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback13B4 : public XorpConstMemberCallback13B4, public SafeCallbackBase { typedef typename XorpConstMemberCallback13B4::M M; XorpConstSafeMemberCallback13B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpConstMemberCallback13B4(file, line, o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback13B4() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback13B4::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 13 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback13B4 : public XorpConstMemberCallback13B4, public SafeCallbackBase { typedef typename XorpConstMemberCallback13B4::M M; XorpConstSafeMemberCallback13B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpConstMemberCallback13B4(file, line, o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback13B4() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback13B4::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory13B4 { static XorpConstMemberCallback13B4* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpConstSafeMemberCallback13B4(file, line, o, p, ba1, ba2, ba3, ba4); } }; template struct XorpConstMemberCallbackFactory13B4 { static XorpConstMemberCallback13B4* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpConstMemberCallback13B4(file, line, o, p, ba1, ba2, ba3, ba4); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 13 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback13::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpConstMemberCallbackFactory13B4::True>::make(file, line, o, p, ba1, ba2, ba3, ba4); } /** * Factory function that creates a callback object targetted at a * const member function with 13 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback13::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpConstMemberCallbackFactory13B4::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4); } /** * @short Callback object for functions with 13 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpFunctionCallback13B5 : public XorpCallback13 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4, BA5); XorpFunctionCallback13B5(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback13(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; }; /** * @short Callback object for void functions with 13 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpFunctionCallback13B5 : public XorpCallback13 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4, BA5); XorpFunctionCallback13B5(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback13(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; }; /** * Factory function that creates a callback object targetted at a * function with 13 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback13::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return typename XorpCallback13::RefPtr(new XorpFunctionCallback13B5(file, line, f, ba1, ba2, ba3, ba4, ba5)); } /** * @short Callback object for member methods with 13 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpMemberCallback13B5 : public XorpCallback13 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4, BA5) ; XorpMemberCallback13B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback13(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for void member methods with 13 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpMemberCallback13B5 : public XorpCallback13 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4, BA5) ; XorpMemberCallback13B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback13(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for safe member methods with 13 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpSafeMemberCallback13B5 : public XorpMemberCallback13B5, public SafeCallbackBase { typedef typename XorpMemberCallback13B5::M M; XorpSafeMemberCallback13B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpMemberCallback13B5(file, line, o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpSafeMemberCallback13B5() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback13B5::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 13 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpSafeMemberCallback13B5 : public XorpMemberCallback13B5, public SafeCallbackBase { typedef typename XorpMemberCallback13B5::M M; XorpSafeMemberCallback13B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpMemberCallback13B5(file, line, o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpSafeMemberCallback13B5() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { if (valid()) { record_dispatch_enter(); XorpMemberCallback13B5::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory13B5 { static XorpMemberCallback13B5* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpSafeMemberCallback13B5(file, line, o, p, ba1, ba2, ba3, ba4, ba5); } }; template struct XorpMemberCallbackFactory13B5 { static XorpMemberCallback13B5* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpMemberCallback13B5(file, line, o, p, ba1, ba2, ba3, ba4, ba5); }; }; /** * Factory function that creates a callback object targetted at a * member function with 13 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback13::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpMemberCallbackFactory13B5::True>::make(file, line, o, p, ba1, ba2, ba3, ba4, ba5); } /** * Factory function that creates a callback object targetted at a * member function with 13 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback13::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpMemberCallbackFactory13B5::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4, ba5); } /** * @short Callback object for const member methods with 13 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstMemberCallback13B5 : public XorpCallback13 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4, BA5) const; XorpConstMemberCallback13B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback13(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for void const member methods with 13 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstMemberCallback13B5 : public XorpCallback13 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4, BA5) const; XorpConstMemberCallback13B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback13(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for const safe member methods with 13 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback13B5 : public XorpConstMemberCallback13B5, public SafeCallbackBase { typedef typename XorpConstMemberCallback13B5::M M; XorpConstSafeMemberCallback13B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpConstMemberCallback13B5(file, line, o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback13B5() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback13B5::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 13 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback13B5 : public XorpConstMemberCallback13B5, public SafeCallbackBase { typedef typename XorpConstMemberCallback13B5::M M; XorpConstSafeMemberCallback13B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpConstMemberCallback13B5(file, line, o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback13B5() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback13B5::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory13B5 { static XorpConstMemberCallback13B5* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpConstSafeMemberCallback13B5(file, line, o, p, ba1, ba2, ba3, ba4, ba5); } }; template struct XorpConstMemberCallbackFactory13B5 { static XorpConstMemberCallback13B5* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpConstMemberCallback13B5(file, line, o, p, ba1, ba2, ba3, ba4, ba5); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 13 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback13::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpConstMemberCallbackFactory13B5::True>::make(file, line, o, p, ba1, ba2, ba3, ba4, ba5); } /** * Factory function that creates a callback object targetted at a * const member function with 13 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback13::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpConstMemberCallbackFactory13B5::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4, ba5); } /** * @short Callback object for functions with 13 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpFunctionCallback13B6 : public XorpCallback13 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4, BA5, BA6); XorpFunctionCallback13B6(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback13(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; BA6 _ba6; }; /** * @short Callback object for void functions with 13 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpFunctionCallback13B6 : public XorpCallback13 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4, BA5, BA6); XorpFunctionCallback13B6(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback13(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; BA6 _ba6; }; /** * Factory function that creates a callback object targetted at a * function with 13 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback13::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return typename XorpCallback13::RefPtr(new XorpFunctionCallback13B6(file, line, f, ba1, ba2, ba3, ba4, ba5, ba6)); } /** * @short Callback object for member methods with 13 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpMemberCallback13B6 : public XorpCallback13 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4, BA5, BA6) ; XorpMemberCallback13B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback13(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for void member methods with 13 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpMemberCallback13B6 : public XorpCallback13 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4, BA5, BA6) ; XorpMemberCallback13B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback13(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for safe member methods with 13 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpSafeMemberCallback13B6 : public XorpMemberCallback13B6, public SafeCallbackBase { typedef typename XorpMemberCallback13B6::M M; XorpSafeMemberCallback13B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpMemberCallback13B6(file, line, o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpSafeMemberCallback13B6() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback13B6::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 13 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpSafeMemberCallback13B6 : public XorpMemberCallback13B6, public SafeCallbackBase { typedef typename XorpMemberCallback13B6::M M; XorpSafeMemberCallback13B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpMemberCallback13B6(file, line, o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpSafeMemberCallback13B6() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { if (valid()) { record_dispatch_enter(); XorpMemberCallback13B6::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory13B6 { static XorpMemberCallback13B6* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpSafeMemberCallback13B6(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); } }; template struct XorpMemberCallbackFactory13B6 { static XorpMemberCallback13B6* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpMemberCallback13B6(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); }; }; /** * Factory function that creates a callback object targetted at a * member function with 13 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback13::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpMemberCallbackFactory13B6::True>::make(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * Factory function that creates a callback object targetted at a * member function with 13 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback13::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpMemberCallbackFactory13B6::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * @short Callback object for const member methods with 13 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstMemberCallback13B6 : public XorpCallback13 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4, BA5, BA6) const; XorpConstMemberCallback13B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback13(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for void const member methods with 13 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstMemberCallback13B6 : public XorpCallback13 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4, BA5, BA6) const; XorpConstMemberCallback13B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback13(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for const safe member methods with 13 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback13B6 : public XorpConstMemberCallback13B6, public SafeCallbackBase { typedef typename XorpConstMemberCallback13B6::M M; XorpConstSafeMemberCallback13B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpConstMemberCallback13B6(file, line, o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback13B6() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback13B6::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 13 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback13B6 : public XorpConstMemberCallback13B6, public SafeCallbackBase { typedef typename XorpConstMemberCallback13B6::M M; XorpConstSafeMemberCallback13B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpConstMemberCallback13B6(file, line, o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback13B6() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback13B6::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory13B6 { static XorpConstMemberCallback13B6* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpConstSafeMemberCallback13B6(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); } }; template struct XorpConstMemberCallbackFactory13B6 { static XorpConstMemberCallback13B6* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpConstMemberCallback13B6(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 13 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback13::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpConstMemberCallbackFactory13B6::True>::make(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * Factory function that creates a callback object targetted at a * const member function with 13 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback13::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpConstMemberCallbackFactory13B6::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /////////////////////////////////////////////////////////////////////////////// // // Code relating to callbacks with 14 late args // /** * @short Base class for callbacks with 14 dispatch time args. */ template struct XorpCallback14 { typedef ref_ptr RefPtr; XorpCallback14(const char* file, int line) : _file(file), _line(line) {} virtual ~XorpCallback14() {} virtual R dispatch(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14) = 0; const char* file() const { return _file; } int line() const { return _line; } private: const char* _file; int _line; }; /** * @short Callback object for functions with 14 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpFunctionCallback14B0 : public XorpCallback14 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14); XorpFunctionCallback14B0(const char* file, int line, F f) : XorpCallback14(file, line), _f(f) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); record_dispatch_leave(); return r; } protected: F _f; }; /** * @short Callback object for void functions with 14 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpFunctionCallback14B0 : public XorpCallback14 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14); XorpFunctionCallback14B0(const char* file, int line, F f) : XorpCallback14(file, line), _f(f) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); record_dispatch_leave(); } protected: F _f; }; /** * Factory function that creates a callback object targetted at a * function with 14 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback14::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14)) { return typename XorpCallback14::RefPtr(new XorpFunctionCallback14B0(file, line, f)); } /** * @short Callback object for member methods with 14 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpMemberCallback14B0 : public XorpCallback14 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14) ; XorpMemberCallback14B0(const char* file, int line, O* o, M m) : XorpCallback14(file, line), _o(o), _m(m) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for void member methods with 14 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpMemberCallback14B0 : public XorpCallback14 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14) ; XorpMemberCallback14B0(const char* file, int line, O* o, M m) : XorpCallback14(file, line), _o(o), _m(m) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for safe member methods with 14 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpSafeMemberCallback14B0 : public XorpMemberCallback14B0, public SafeCallbackBase { typedef typename XorpMemberCallback14B0::M M; XorpSafeMemberCallback14B0(const char* file, int line, O* o, M m) : XorpMemberCallback14B0(file, line, o, m), SafeCallbackBase(o) {} ~XorpSafeMemberCallback14B0() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback14B0::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 14 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpSafeMemberCallback14B0 : public XorpMemberCallback14B0, public SafeCallbackBase { typedef typename XorpMemberCallback14B0::M M; XorpSafeMemberCallback14B0(const char* file, int line, O* o, M m) : XorpMemberCallback14B0(file, line, o, m), SafeCallbackBase(o) {} ~XorpSafeMemberCallback14B0() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { if (valid()) { record_dispatch_enter(); XorpMemberCallback14B0::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory14B0 { static XorpMemberCallback14B0* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14)) { return new XorpSafeMemberCallback14B0(file, line, o, p); } }; template struct XorpMemberCallbackFactory14B0 { static XorpMemberCallback14B0* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14)) { return new XorpMemberCallback14B0(file, line, o, p); }; }; /** * Factory function that creates a callback object targetted at a * member function with 14 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback14::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14)) { return XorpMemberCallbackFactory14B0::True>::make(file, line, o, p); } /** * Factory function that creates a callback object targetted at a * member function with 14 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback14::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14)) { return XorpMemberCallbackFactory14B0::True>::make(file, line, &o, p); } /** * @short Callback object for const member methods with 14 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstMemberCallback14B0 : public XorpCallback14 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14) const; XorpConstMemberCallback14B0(const char* file, int line, O* o, M m) : XorpCallback14(file, line), _o(o), _m(m) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for void const member methods with 14 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstMemberCallback14B0 : public XorpCallback14 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14) const; XorpConstMemberCallback14B0(const char* file, int line, O* o, M m) : XorpCallback14(file, line), _o(o), _m(m) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for const safe member methods with 14 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback14B0 : public XorpConstMemberCallback14B0, public SafeCallbackBase { typedef typename XorpConstMemberCallback14B0::M M; XorpConstSafeMemberCallback14B0(const char* file, int line, O* o, M m) : XorpConstMemberCallback14B0(file, line, o, m), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback14B0() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback14B0::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 14 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback14B0 : public XorpConstMemberCallback14B0, public SafeCallbackBase { typedef typename XorpConstMemberCallback14B0::M M; XorpConstSafeMemberCallback14B0(const char* file, int line, O* o, M m) : XorpConstMemberCallback14B0(file, line, o, m), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback14B0() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback14B0::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory14B0 { static XorpConstMemberCallback14B0* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14) const) { return new XorpConstSafeMemberCallback14B0(file, line, o, p); } }; template struct XorpConstMemberCallbackFactory14B0 { static XorpConstMemberCallback14B0* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14) const) { return new XorpConstMemberCallback14B0(file, line, o, p); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 14 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback14::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14) const) { return XorpConstMemberCallbackFactory14B0::True>::make(file, line, o, p); } /** * Factory function that creates a callback object targetted at a * const member function with 14 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback14::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14) const) { return XorpConstMemberCallbackFactory14B0::True>::make(file, line, &o, p); } /** * @short Callback object for functions with 14 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpFunctionCallback14B1 : public XorpCallback14 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1); XorpFunctionCallback14B1(const char* file, int line, F f, BA1 ba1) : XorpCallback14(file, line), _f(f), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; }; /** * @short Callback object for void functions with 14 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpFunctionCallback14B1 : public XorpCallback14 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1); XorpFunctionCallback14B1(const char* file, int line, F f, BA1 ba1) : XorpCallback14(file, line), _f(f), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1); record_dispatch_leave(); } protected: F _f; BA1 _ba1; }; /** * Factory function that creates a callback object targetted at a * function with 14 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback14::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1), BA1 ba1) { return typename XorpCallback14::RefPtr(new XorpFunctionCallback14B1(file, line, f, ba1)); } /** * @short Callback object for member methods with 14 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpMemberCallback14B1 : public XorpCallback14 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1) ; XorpMemberCallback14B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpCallback14(file, line), _o(o), _m(m), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for void member methods with 14 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpMemberCallback14B1 : public XorpCallback14 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1) ; XorpMemberCallback14B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpCallback14(file, line), _o(o), _m(m), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for safe member methods with 14 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpSafeMemberCallback14B1 : public XorpMemberCallback14B1, public SafeCallbackBase { typedef typename XorpMemberCallback14B1::M M; XorpSafeMemberCallback14B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpMemberCallback14B1(file, line, o, m, ba1), SafeCallbackBase(o) {} ~XorpSafeMemberCallback14B1() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback14B1::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 14 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpSafeMemberCallback14B1 : public XorpMemberCallback14B1, public SafeCallbackBase { typedef typename XorpMemberCallback14B1::M M; XorpSafeMemberCallback14B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpMemberCallback14B1(file, line, o, m, ba1), SafeCallbackBase(o) {} ~XorpSafeMemberCallback14B1() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { if (valid()) { record_dispatch_enter(); XorpMemberCallback14B1::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory14B1 { static XorpMemberCallback14B1* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1), BA1 ba1) { return new XorpSafeMemberCallback14B1(file, line, o, p, ba1); } }; template struct XorpMemberCallbackFactory14B1 { static XorpMemberCallback14B1* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1), BA1 ba1) { return new XorpMemberCallback14B1(file, line, o, p, ba1); }; }; /** * Factory function that creates a callback object targetted at a * member function with 14 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback14::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1), BA1 ba1) { return XorpMemberCallbackFactory14B1::True>::make(file, line, o, p, ba1); } /** * Factory function that creates a callback object targetted at a * member function with 14 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback14::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1), BA1 ba1) { return XorpMemberCallbackFactory14B1::True>::make(file, line, &o, p, ba1); } /** * @short Callback object for const member methods with 14 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstMemberCallback14B1 : public XorpCallback14 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1) const; XorpConstMemberCallback14B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpCallback14(file, line), _o(o), _m(m), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for void const member methods with 14 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstMemberCallback14B1 : public XorpCallback14 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1) const; XorpConstMemberCallback14B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpCallback14(file, line), _o(o), _m(m), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for const safe member methods with 14 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback14B1 : public XorpConstMemberCallback14B1, public SafeCallbackBase { typedef typename XorpConstMemberCallback14B1::M M; XorpConstSafeMemberCallback14B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpConstMemberCallback14B1(file, line, o, m, ba1), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback14B1() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback14B1::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 14 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback14B1 : public XorpConstMemberCallback14B1, public SafeCallbackBase { typedef typename XorpConstMemberCallback14B1::M M; XorpConstSafeMemberCallback14B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpConstMemberCallback14B1(file, line, o, m, ba1), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback14B1() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback14B1::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory14B1 { static XorpConstMemberCallback14B1* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1) const, BA1 ba1) { return new XorpConstSafeMemberCallback14B1(file, line, o, p, ba1); } }; template struct XorpConstMemberCallbackFactory14B1 { static XorpConstMemberCallback14B1* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1) const, BA1 ba1) { return new XorpConstMemberCallback14B1(file, line, o, p, ba1); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 14 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback14::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1) const, BA1 ba1) { return XorpConstMemberCallbackFactory14B1::True>::make(file, line, o, p, ba1); } /** * Factory function that creates a callback object targetted at a * const member function with 14 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback14::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1) const, BA1 ba1) { return XorpConstMemberCallbackFactory14B1::True>::make(file, line, &o, p, ba1); } /** * @short Callback object for functions with 14 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpFunctionCallback14B2 : public XorpCallback14 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2); XorpFunctionCallback14B2(const char* file, int line, F f, BA1 ba1, BA2 ba2) : XorpCallback14(file, line), _f(f), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1, _ba2); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; }; /** * @short Callback object for void functions with 14 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpFunctionCallback14B2 : public XorpCallback14 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2); XorpFunctionCallback14B2(const char* file, int line, F f, BA1 ba1, BA2 ba2) : XorpCallback14(file, line), _f(f), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1, _ba2); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; }; /** * Factory function that creates a callback object targetted at a * function with 14 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback14::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2), BA1 ba1, BA2 ba2) { return typename XorpCallback14::RefPtr(new XorpFunctionCallback14B2(file, line, f, ba1, ba2)); } /** * @short Callback object for member methods with 14 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpMemberCallback14B2 : public XorpCallback14 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2) ; XorpMemberCallback14B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback14(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1, _ba2); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for void member methods with 14 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpMemberCallback14B2 : public XorpCallback14 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2) ; XorpMemberCallback14B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback14(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1, _ba2); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for safe member methods with 14 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpSafeMemberCallback14B2 : public XorpMemberCallback14B2, public SafeCallbackBase { typedef typename XorpMemberCallback14B2::M M; XorpSafeMemberCallback14B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpMemberCallback14B2(file, line, o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpSafeMemberCallback14B2() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback14B2::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 14 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpSafeMemberCallback14B2 : public XorpMemberCallback14B2, public SafeCallbackBase { typedef typename XorpMemberCallback14B2::M M; XorpSafeMemberCallback14B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpMemberCallback14B2(file, line, o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpSafeMemberCallback14B2() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { if (valid()) { record_dispatch_enter(); XorpMemberCallback14B2::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory14B2 { static XorpMemberCallback14B2* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2), BA1 ba1, BA2 ba2) { return new XorpSafeMemberCallback14B2(file, line, o, p, ba1, ba2); } }; template struct XorpMemberCallbackFactory14B2 { static XorpMemberCallback14B2* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2), BA1 ba1, BA2 ba2) { return new XorpMemberCallback14B2(file, line, o, p, ba1, ba2); }; }; /** * Factory function that creates a callback object targetted at a * member function with 14 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback14::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2), BA1 ba1, BA2 ba2) { return XorpMemberCallbackFactory14B2::True>::make(file, line, o, p, ba1, ba2); } /** * Factory function that creates a callback object targetted at a * member function with 14 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback14::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2), BA1 ba1, BA2 ba2) { return XorpMemberCallbackFactory14B2::True>::make(file, line, &o, p, ba1, ba2); } /** * @short Callback object for const member methods with 14 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstMemberCallback14B2 : public XorpCallback14 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2) const; XorpConstMemberCallback14B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback14(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1, _ba2); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for void const member methods with 14 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstMemberCallback14B2 : public XorpCallback14 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2) const; XorpConstMemberCallback14B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback14(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1, _ba2); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for const safe member methods with 14 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback14B2 : public XorpConstMemberCallback14B2, public SafeCallbackBase { typedef typename XorpConstMemberCallback14B2::M M; XorpConstSafeMemberCallback14B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpConstMemberCallback14B2(file, line, o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback14B2() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback14B2::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 14 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback14B2 : public XorpConstMemberCallback14B2, public SafeCallbackBase { typedef typename XorpConstMemberCallback14B2::M M; XorpConstSafeMemberCallback14B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpConstMemberCallback14B2(file, line, o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback14B2() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback14B2::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory14B2 { static XorpConstMemberCallback14B2* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2) const, BA1 ba1, BA2 ba2) { return new XorpConstSafeMemberCallback14B2(file, line, o, p, ba1, ba2); } }; template struct XorpConstMemberCallbackFactory14B2 { static XorpConstMemberCallback14B2* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2) const, BA1 ba1, BA2 ba2) { return new XorpConstMemberCallback14B2(file, line, o, p, ba1, ba2); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 14 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback14::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2) const, BA1 ba1, BA2 ba2) { return XorpConstMemberCallbackFactory14B2::True>::make(file, line, o, p, ba1, ba2); } /** * Factory function that creates a callback object targetted at a * const member function with 14 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback14::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2) const, BA1 ba1, BA2 ba2) { return XorpConstMemberCallbackFactory14B2::True>::make(file, line, &o, p, ba1, ba2); } /** * @short Callback object for functions with 14 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpFunctionCallback14B3 : public XorpCallback14 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3); XorpFunctionCallback14B3(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback14(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1, _ba2, _ba3); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; }; /** * @short Callback object for void functions with 14 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpFunctionCallback14B3 : public XorpCallback14 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3); XorpFunctionCallback14B3(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback14(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1, _ba2, _ba3); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; }; /** * Factory function that creates a callback object targetted at a * function with 14 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback14::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return typename XorpCallback14::RefPtr(new XorpFunctionCallback14B3(file, line, f, ba1, ba2, ba3)); } /** * @short Callback object for member methods with 14 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpMemberCallback14B3 : public XorpCallback14 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3) ; XorpMemberCallback14B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback14(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1, _ba2, _ba3); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for void member methods with 14 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpMemberCallback14B3 : public XorpCallback14 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3) ; XorpMemberCallback14B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback14(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1, _ba2, _ba3); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for safe member methods with 14 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpSafeMemberCallback14B3 : public XorpMemberCallback14B3, public SafeCallbackBase { typedef typename XorpMemberCallback14B3::M M; XorpSafeMemberCallback14B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpMemberCallback14B3(file, line, o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpSafeMemberCallback14B3() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback14B3::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 14 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpSafeMemberCallback14B3 : public XorpMemberCallback14B3, public SafeCallbackBase { typedef typename XorpMemberCallback14B3::M M; XorpSafeMemberCallback14B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpMemberCallback14B3(file, line, o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpSafeMemberCallback14B3() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { if (valid()) { record_dispatch_enter(); XorpMemberCallback14B3::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory14B3 { static XorpMemberCallback14B3* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpSafeMemberCallback14B3(file, line, o, p, ba1, ba2, ba3); } }; template struct XorpMemberCallbackFactory14B3 { static XorpMemberCallback14B3* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpMemberCallback14B3(file, line, o, p, ba1, ba2, ba3); }; }; /** * Factory function that creates a callback object targetted at a * member function with 14 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback14::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return XorpMemberCallbackFactory14B3::True>::make(file, line, o, p, ba1, ba2, ba3); } /** * Factory function that creates a callback object targetted at a * member function with 14 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback14::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return XorpMemberCallbackFactory14B3::True>::make(file, line, &o, p, ba1, ba2, ba3); } /** * @short Callback object for const member methods with 14 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstMemberCallback14B3 : public XorpCallback14 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3) const; XorpConstMemberCallback14B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback14(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1, _ba2, _ba3); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for void const member methods with 14 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstMemberCallback14B3 : public XorpCallback14 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3) const; XorpConstMemberCallback14B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback14(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1, _ba2, _ba3); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for const safe member methods with 14 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback14B3 : public XorpConstMemberCallback14B3, public SafeCallbackBase { typedef typename XorpConstMemberCallback14B3::M M; XorpConstSafeMemberCallback14B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpConstMemberCallback14B3(file, line, o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback14B3() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback14B3::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 14 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback14B3 : public XorpConstMemberCallback14B3, public SafeCallbackBase { typedef typename XorpConstMemberCallback14B3::M M; XorpConstSafeMemberCallback14B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpConstMemberCallback14B3(file, line, o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback14B3() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback14B3::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory14B3 { static XorpConstMemberCallback14B3* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpConstSafeMemberCallback14B3(file, line, o, p, ba1, ba2, ba3); } }; template struct XorpConstMemberCallbackFactory14B3 { static XorpConstMemberCallback14B3* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpConstMemberCallback14B3(file, line, o, p, ba1, ba2, ba3); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 14 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback14::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return XorpConstMemberCallbackFactory14B3::True>::make(file, line, o, p, ba1, ba2, ba3); } /** * Factory function that creates a callback object targetted at a * const member function with 14 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback14::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return XorpConstMemberCallbackFactory14B3::True>::make(file, line, &o, p, ba1, ba2, ba3); } /** * @short Callback object for functions with 14 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpFunctionCallback14B4 : public XorpCallback14 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4); XorpFunctionCallback14B4(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback14(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; }; /** * @short Callback object for void functions with 14 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpFunctionCallback14B4 : public XorpCallback14 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4); XorpFunctionCallback14B4(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback14(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; }; /** * Factory function that creates a callback object targetted at a * function with 14 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback14::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return typename XorpCallback14::RefPtr(new XorpFunctionCallback14B4(file, line, f, ba1, ba2, ba3, ba4)); } /** * @short Callback object for member methods with 14 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpMemberCallback14B4 : public XorpCallback14 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4) ; XorpMemberCallback14B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback14(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for void member methods with 14 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpMemberCallback14B4 : public XorpCallback14 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4) ; XorpMemberCallback14B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback14(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for safe member methods with 14 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpSafeMemberCallback14B4 : public XorpMemberCallback14B4, public SafeCallbackBase { typedef typename XorpMemberCallback14B4::M M; XorpSafeMemberCallback14B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpMemberCallback14B4(file, line, o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpSafeMemberCallback14B4() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback14B4::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 14 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpSafeMemberCallback14B4 : public XorpMemberCallback14B4, public SafeCallbackBase { typedef typename XorpMemberCallback14B4::M M; XorpSafeMemberCallback14B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpMemberCallback14B4(file, line, o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpSafeMemberCallback14B4() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { if (valid()) { record_dispatch_enter(); XorpMemberCallback14B4::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory14B4 { static XorpMemberCallback14B4* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpSafeMemberCallback14B4(file, line, o, p, ba1, ba2, ba3, ba4); } }; template struct XorpMemberCallbackFactory14B4 { static XorpMemberCallback14B4* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpMemberCallback14B4(file, line, o, p, ba1, ba2, ba3, ba4); }; }; /** * Factory function that creates a callback object targetted at a * member function with 14 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback14::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpMemberCallbackFactory14B4::True>::make(file, line, o, p, ba1, ba2, ba3, ba4); } /** * Factory function that creates a callback object targetted at a * member function with 14 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback14::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpMemberCallbackFactory14B4::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4); } /** * @short Callback object for const member methods with 14 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstMemberCallback14B4 : public XorpCallback14 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4) const; XorpConstMemberCallback14B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback14(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for void const member methods with 14 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstMemberCallback14B4 : public XorpCallback14 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4) const; XorpConstMemberCallback14B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback14(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for const safe member methods with 14 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback14B4 : public XorpConstMemberCallback14B4, public SafeCallbackBase { typedef typename XorpConstMemberCallback14B4::M M; XorpConstSafeMemberCallback14B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpConstMemberCallback14B4(file, line, o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback14B4() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback14B4::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 14 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback14B4 : public XorpConstMemberCallback14B4, public SafeCallbackBase { typedef typename XorpConstMemberCallback14B4::M M; XorpConstSafeMemberCallback14B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpConstMemberCallback14B4(file, line, o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback14B4() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback14B4::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory14B4 { static XorpConstMemberCallback14B4* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpConstSafeMemberCallback14B4(file, line, o, p, ba1, ba2, ba3, ba4); } }; template struct XorpConstMemberCallbackFactory14B4 { static XorpConstMemberCallback14B4* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpConstMemberCallback14B4(file, line, o, p, ba1, ba2, ba3, ba4); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 14 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback14::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpConstMemberCallbackFactory14B4::True>::make(file, line, o, p, ba1, ba2, ba3, ba4); } /** * Factory function that creates a callback object targetted at a * const member function with 14 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback14::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpConstMemberCallbackFactory14B4::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4); } /** * @short Callback object for functions with 14 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpFunctionCallback14B5 : public XorpCallback14 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4, BA5); XorpFunctionCallback14B5(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback14(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; }; /** * @short Callback object for void functions with 14 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpFunctionCallback14B5 : public XorpCallback14 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4, BA5); XorpFunctionCallback14B5(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback14(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; }; /** * Factory function that creates a callback object targetted at a * function with 14 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback14::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return typename XorpCallback14::RefPtr(new XorpFunctionCallback14B5(file, line, f, ba1, ba2, ba3, ba4, ba5)); } /** * @short Callback object for member methods with 14 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpMemberCallback14B5 : public XorpCallback14 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4, BA5) ; XorpMemberCallback14B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback14(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for void member methods with 14 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpMemberCallback14B5 : public XorpCallback14 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4, BA5) ; XorpMemberCallback14B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback14(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for safe member methods with 14 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpSafeMemberCallback14B5 : public XorpMemberCallback14B5, public SafeCallbackBase { typedef typename XorpMemberCallback14B5::M M; XorpSafeMemberCallback14B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpMemberCallback14B5(file, line, o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpSafeMemberCallback14B5() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback14B5::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 14 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpSafeMemberCallback14B5 : public XorpMemberCallback14B5, public SafeCallbackBase { typedef typename XorpMemberCallback14B5::M M; XorpSafeMemberCallback14B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpMemberCallback14B5(file, line, o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpSafeMemberCallback14B5() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { if (valid()) { record_dispatch_enter(); XorpMemberCallback14B5::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory14B5 { static XorpMemberCallback14B5* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpSafeMemberCallback14B5(file, line, o, p, ba1, ba2, ba3, ba4, ba5); } }; template struct XorpMemberCallbackFactory14B5 { static XorpMemberCallback14B5* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpMemberCallback14B5(file, line, o, p, ba1, ba2, ba3, ba4, ba5); }; }; /** * Factory function that creates a callback object targetted at a * member function with 14 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback14::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpMemberCallbackFactory14B5::True>::make(file, line, o, p, ba1, ba2, ba3, ba4, ba5); } /** * Factory function that creates a callback object targetted at a * member function with 14 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback14::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpMemberCallbackFactory14B5::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4, ba5); } /** * @short Callback object for const member methods with 14 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstMemberCallback14B5 : public XorpCallback14 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4, BA5) const; XorpConstMemberCallback14B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback14(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for void const member methods with 14 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstMemberCallback14B5 : public XorpCallback14 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4, BA5) const; XorpConstMemberCallback14B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback14(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for const safe member methods with 14 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback14B5 : public XorpConstMemberCallback14B5, public SafeCallbackBase { typedef typename XorpConstMemberCallback14B5::M M; XorpConstSafeMemberCallback14B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpConstMemberCallback14B5(file, line, o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback14B5() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback14B5::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 14 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback14B5 : public XorpConstMemberCallback14B5, public SafeCallbackBase { typedef typename XorpConstMemberCallback14B5::M M; XorpConstSafeMemberCallback14B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpConstMemberCallback14B5(file, line, o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback14B5() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback14B5::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory14B5 { static XorpConstMemberCallback14B5* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpConstSafeMemberCallback14B5(file, line, o, p, ba1, ba2, ba3, ba4, ba5); } }; template struct XorpConstMemberCallbackFactory14B5 { static XorpConstMemberCallback14B5* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpConstMemberCallback14B5(file, line, o, p, ba1, ba2, ba3, ba4, ba5); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 14 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback14::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpConstMemberCallbackFactory14B5::True>::make(file, line, o, p, ba1, ba2, ba3, ba4, ba5); } /** * Factory function that creates a callback object targetted at a * const member function with 14 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback14::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpConstMemberCallbackFactory14B5::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4, ba5); } /** * @short Callback object for functions with 14 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpFunctionCallback14B6 : public XorpCallback14 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4, BA5, BA6); XorpFunctionCallback14B6(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback14(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; BA6 _ba6; }; /** * @short Callback object for void functions with 14 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpFunctionCallback14B6 : public XorpCallback14 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4, BA5, BA6); XorpFunctionCallback14B6(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback14(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; BA6 _ba6; }; /** * Factory function that creates a callback object targetted at a * function with 14 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback14::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return typename XorpCallback14::RefPtr(new XorpFunctionCallback14B6(file, line, f, ba1, ba2, ba3, ba4, ba5, ba6)); } /** * @short Callback object for member methods with 14 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpMemberCallback14B6 : public XorpCallback14 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4, BA5, BA6) ; XorpMemberCallback14B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback14(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for void member methods with 14 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpMemberCallback14B6 : public XorpCallback14 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4, BA5, BA6) ; XorpMemberCallback14B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback14(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for safe member methods with 14 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpSafeMemberCallback14B6 : public XorpMemberCallback14B6, public SafeCallbackBase { typedef typename XorpMemberCallback14B6::M M; XorpSafeMemberCallback14B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpMemberCallback14B6(file, line, o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpSafeMemberCallback14B6() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback14B6::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 14 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpSafeMemberCallback14B6 : public XorpMemberCallback14B6, public SafeCallbackBase { typedef typename XorpMemberCallback14B6::M M; XorpSafeMemberCallback14B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpMemberCallback14B6(file, line, o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpSafeMemberCallback14B6() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { if (valid()) { record_dispatch_enter(); XorpMemberCallback14B6::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory14B6 { static XorpMemberCallback14B6* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpSafeMemberCallback14B6(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); } }; template struct XorpMemberCallbackFactory14B6 { static XorpMemberCallback14B6* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpMemberCallback14B6(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); }; }; /** * Factory function that creates a callback object targetted at a * member function with 14 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback14::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpMemberCallbackFactory14B6::True>::make(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * Factory function that creates a callback object targetted at a * member function with 14 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback14::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpMemberCallbackFactory14B6::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * @short Callback object for const member methods with 14 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstMemberCallback14B6 : public XorpCallback14 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4, BA5, BA6) const; XorpConstMemberCallback14B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback14(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for void const member methods with 14 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstMemberCallback14B6 : public XorpCallback14 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4, BA5, BA6) const; XorpConstMemberCallback14B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback14(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for const safe member methods with 14 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback14B6 : public XorpConstMemberCallback14B6, public SafeCallbackBase { typedef typename XorpConstMemberCallback14B6::M M; XorpConstSafeMemberCallback14B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpConstMemberCallback14B6(file, line, o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback14B6() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback14B6::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 14 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback14B6 : public XorpConstMemberCallback14B6, public SafeCallbackBase { typedef typename XorpConstMemberCallback14B6::M M; XorpConstSafeMemberCallback14B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpConstMemberCallback14B6(file, line, o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback14B6() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback14B6::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory14B6 { static XorpConstMemberCallback14B6* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpConstSafeMemberCallback14B6(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); } }; template struct XorpConstMemberCallbackFactory14B6 { static XorpConstMemberCallback14B6* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpConstMemberCallback14B6(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 14 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback14::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpConstMemberCallbackFactory14B6::True>::make(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * Factory function that creates a callback object targetted at a * const member function with 14 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback14::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpConstMemberCallbackFactory14B6::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /////////////////////////////////////////////////////////////////////////////// // // Code relating to callbacks with 15 late args // /** * @short Base class for callbacks with 15 dispatch time args. */ template struct XorpCallback15 { typedef ref_ptr RefPtr; XorpCallback15(const char* file, int line) : _file(file), _line(line) {} virtual ~XorpCallback15() {} virtual R dispatch(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15) = 0; const char* file() const { return _file; } int line() const { return _line; } private: const char* _file; int _line; }; /** * @short Callback object for functions with 15 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpFunctionCallback15B0 : public XorpCallback15 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15); XorpFunctionCallback15B0(const char* file, int line, F f) : XorpCallback15(file, line), _f(f) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); record_dispatch_leave(); return r; } protected: F _f; }; /** * @short Callback object for void functions with 15 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpFunctionCallback15B0 : public XorpCallback15 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15); XorpFunctionCallback15B0(const char* file, int line, F f) : XorpCallback15(file, line), _f(f) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); record_dispatch_leave(); } protected: F _f; }; /** * Factory function that creates a callback object targetted at a * function with 15 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback15::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15)) { return typename XorpCallback15::RefPtr(new XorpFunctionCallback15B0(file, line, f)); } /** * @short Callback object for member methods with 15 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpMemberCallback15B0 : public XorpCallback15 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15) ; XorpMemberCallback15B0(const char* file, int line, O* o, M m) : XorpCallback15(file, line), _o(o), _m(m) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for void member methods with 15 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpMemberCallback15B0 : public XorpCallback15 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15) ; XorpMemberCallback15B0(const char* file, int line, O* o, M m) : XorpCallback15(file, line), _o(o), _m(m) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for safe member methods with 15 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpSafeMemberCallback15B0 : public XorpMemberCallback15B0, public SafeCallbackBase { typedef typename XorpMemberCallback15B0::M M; XorpSafeMemberCallback15B0(const char* file, int line, O* o, M m) : XorpMemberCallback15B0(file, line, o, m), SafeCallbackBase(o) {} ~XorpSafeMemberCallback15B0() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback15B0::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 15 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpSafeMemberCallback15B0 : public XorpMemberCallback15B0, public SafeCallbackBase { typedef typename XorpMemberCallback15B0::M M; XorpSafeMemberCallback15B0(const char* file, int line, O* o, M m) : XorpMemberCallback15B0(file, line, o, m), SafeCallbackBase(o) {} ~XorpSafeMemberCallback15B0() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { if (valid()) { record_dispatch_enter(); XorpMemberCallback15B0::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory15B0 { static XorpMemberCallback15B0* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15)) { return new XorpSafeMemberCallback15B0(file, line, o, p); } }; template struct XorpMemberCallbackFactory15B0 { static XorpMemberCallback15B0* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15)) { return new XorpMemberCallback15B0(file, line, o, p); }; }; /** * Factory function that creates a callback object targetted at a * member function with 15 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback15::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15)) { return XorpMemberCallbackFactory15B0::True>::make(file, line, o, p); } /** * Factory function that creates a callback object targetted at a * member function with 15 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback15::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15)) { return XorpMemberCallbackFactory15B0::True>::make(file, line, &o, p); } /** * @short Callback object for const member methods with 15 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstMemberCallback15B0 : public XorpCallback15 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15) const; XorpConstMemberCallback15B0(const char* file, int line, O* o, M m) : XorpCallback15(file, line), _o(o), _m(m) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for void const member methods with 15 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstMemberCallback15B0 : public XorpCallback15 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15) const; XorpConstMemberCallback15B0(const char* file, int line, O* o, M m) : XorpCallback15(file, line), _o(o), _m(m) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for const safe member methods with 15 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback15B0 : public XorpConstMemberCallback15B0, public SafeCallbackBase { typedef typename XorpConstMemberCallback15B0::M M; XorpConstSafeMemberCallback15B0(const char* file, int line, O* o, M m) : XorpConstMemberCallback15B0(file, line, o, m), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback15B0() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback15B0::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 15 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback15B0 : public XorpConstMemberCallback15B0, public SafeCallbackBase { typedef typename XorpConstMemberCallback15B0::M M; XorpConstSafeMemberCallback15B0(const char* file, int line, O* o, M m) : XorpConstMemberCallback15B0(file, line, o, m), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback15B0() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback15B0::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory15B0 { static XorpConstMemberCallback15B0* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15) const) { return new XorpConstSafeMemberCallback15B0(file, line, o, p); } }; template struct XorpConstMemberCallbackFactory15B0 { static XorpConstMemberCallback15B0* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15) const) { return new XorpConstMemberCallback15B0(file, line, o, p); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 15 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback15::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15) const) { return XorpConstMemberCallbackFactory15B0::True>::make(file, line, o, p); } /** * Factory function that creates a callback object targetted at a * const member function with 15 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback15::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15) const) { return XorpConstMemberCallbackFactory15B0::True>::make(file, line, &o, p); } /** * @short Callback object for functions with 15 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpFunctionCallback15B1 : public XorpCallback15 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1); XorpFunctionCallback15B1(const char* file, int line, F f, BA1 ba1) : XorpCallback15(file, line), _f(f), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; }; /** * @short Callback object for void functions with 15 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpFunctionCallback15B1 : public XorpCallback15 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1); XorpFunctionCallback15B1(const char* file, int line, F f, BA1 ba1) : XorpCallback15(file, line), _f(f), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1); record_dispatch_leave(); } protected: F _f; BA1 _ba1; }; /** * Factory function that creates a callback object targetted at a * function with 15 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback15::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1), BA1 ba1) { return typename XorpCallback15::RefPtr(new XorpFunctionCallback15B1(file, line, f, ba1)); } /** * @short Callback object for member methods with 15 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpMemberCallback15B1 : public XorpCallback15 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1) ; XorpMemberCallback15B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpCallback15(file, line), _o(o), _m(m), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for void member methods with 15 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpMemberCallback15B1 : public XorpCallback15 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1) ; XorpMemberCallback15B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpCallback15(file, line), _o(o), _m(m), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for safe member methods with 15 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpSafeMemberCallback15B1 : public XorpMemberCallback15B1, public SafeCallbackBase { typedef typename XorpMemberCallback15B1::M M; XorpSafeMemberCallback15B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpMemberCallback15B1(file, line, o, m, ba1), SafeCallbackBase(o) {} ~XorpSafeMemberCallback15B1() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback15B1::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 15 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpSafeMemberCallback15B1 : public XorpMemberCallback15B1, public SafeCallbackBase { typedef typename XorpMemberCallback15B1::M M; XorpSafeMemberCallback15B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpMemberCallback15B1(file, line, o, m, ba1), SafeCallbackBase(o) {} ~XorpSafeMemberCallback15B1() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { if (valid()) { record_dispatch_enter(); XorpMemberCallback15B1::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory15B1 { static XorpMemberCallback15B1* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1), BA1 ba1) { return new XorpSafeMemberCallback15B1(file, line, o, p, ba1); } }; template struct XorpMemberCallbackFactory15B1 { static XorpMemberCallback15B1* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1), BA1 ba1) { return new XorpMemberCallback15B1(file, line, o, p, ba1); }; }; /** * Factory function that creates a callback object targetted at a * member function with 15 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback15::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1), BA1 ba1) { return XorpMemberCallbackFactory15B1::True>::make(file, line, o, p, ba1); } /** * Factory function that creates a callback object targetted at a * member function with 15 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback15::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1), BA1 ba1) { return XorpMemberCallbackFactory15B1::True>::make(file, line, &o, p, ba1); } /** * @short Callback object for const member methods with 15 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstMemberCallback15B1 : public XorpCallback15 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1) const; XorpConstMemberCallback15B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpCallback15(file, line), _o(o), _m(m), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for void const member methods with 15 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstMemberCallback15B1 : public XorpCallback15 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1) const; XorpConstMemberCallback15B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpCallback15(file, line), _o(o), _m(m), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for const safe member methods with 15 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback15B1 : public XorpConstMemberCallback15B1, public SafeCallbackBase { typedef typename XorpConstMemberCallback15B1::M M; XorpConstSafeMemberCallback15B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpConstMemberCallback15B1(file, line, o, m, ba1), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback15B1() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback15B1::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 15 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback15B1 : public XorpConstMemberCallback15B1, public SafeCallbackBase { typedef typename XorpConstMemberCallback15B1::M M; XorpConstSafeMemberCallback15B1(const char* file, int line, O* o, M m, BA1 ba1) : XorpConstMemberCallback15B1(file, line, o, m, ba1), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback15B1() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback15B1::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory15B1 { static XorpConstMemberCallback15B1* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1) const, BA1 ba1) { return new XorpConstSafeMemberCallback15B1(file, line, o, p, ba1); } }; template struct XorpConstMemberCallbackFactory15B1 { static XorpConstMemberCallback15B1* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1) const, BA1 ba1) { return new XorpConstMemberCallback15B1(file, line, o, p, ba1); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 15 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback15::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1) const, BA1 ba1) { return XorpConstMemberCallbackFactory15B1::True>::make(file, line, o, p, ba1); } /** * Factory function that creates a callback object targetted at a * const member function with 15 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback15::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1) const, BA1 ba1) { return XorpConstMemberCallbackFactory15B1::True>::make(file, line, &o, p, ba1); } /** * @short Callback object for functions with 15 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpFunctionCallback15B2 : public XorpCallback15 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2); XorpFunctionCallback15B2(const char* file, int line, F f, BA1 ba1, BA2 ba2) : XorpCallback15(file, line), _f(f), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1, _ba2); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; }; /** * @short Callback object for void functions with 15 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpFunctionCallback15B2 : public XorpCallback15 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2); XorpFunctionCallback15B2(const char* file, int line, F f, BA1 ba1, BA2 ba2) : XorpCallback15(file, line), _f(f), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1, _ba2); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; }; /** * Factory function that creates a callback object targetted at a * function with 15 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback15::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2), BA1 ba1, BA2 ba2) { return typename XorpCallback15::RefPtr(new XorpFunctionCallback15B2(file, line, f, ba1, ba2)); } /** * @short Callback object for member methods with 15 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpMemberCallback15B2 : public XorpCallback15 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2) ; XorpMemberCallback15B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback15(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1, _ba2); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for void member methods with 15 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpMemberCallback15B2 : public XorpCallback15 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2) ; XorpMemberCallback15B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback15(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1, _ba2); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for safe member methods with 15 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpSafeMemberCallback15B2 : public XorpMemberCallback15B2, public SafeCallbackBase { typedef typename XorpMemberCallback15B2::M M; XorpSafeMemberCallback15B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpMemberCallback15B2(file, line, o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpSafeMemberCallback15B2() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback15B2::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 15 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpSafeMemberCallback15B2 : public XorpMemberCallback15B2, public SafeCallbackBase { typedef typename XorpMemberCallback15B2::M M; XorpSafeMemberCallback15B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpMemberCallback15B2(file, line, o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpSafeMemberCallback15B2() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { if (valid()) { record_dispatch_enter(); XorpMemberCallback15B2::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory15B2 { static XorpMemberCallback15B2* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2), BA1 ba1, BA2 ba2) { return new XorpSafeMemberCallback15B2(file, line, o, p, ba1, ba2); } }; template struct XorpMemberCallbackFactory15B2 { static XorpMemberCallback15B2* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2), BA1 ba1, BA2 ba2) { return new XorpMemberCallback15B2(file, line, o, p, ba1, ba2); }; }; /** * Factory function that creates a callback object targetted at a * member function with 15 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback15::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2), BA1 ba1, BA2 ba2) { return XorpMemberCallbackFactory15B2::True>::make(file, line, o, p, ba1, ba2); } /** * Factory function that creates a callback object targetted at a * member function with 15 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback15::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2), BA1 ba1, BA2 ba2) { return XorpMemberCallbackFactory15B2::True>::make(file, line, &o, p, ba1, ba2); } /** * @short Callback object for const member methods with 15 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstMemberCallback15B2 : public XorpCallback15 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2) const; XorpConstMemberCallback15B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback15(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1, _ba2); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for void const member methods with 15 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstMemberCallback15B2 : public XorpCallback15 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2) const; XorpConstMemberCallback15B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback15(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1, _ba2); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for const safe member methods with 15 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback15B2 : public XorpConstMemberCallback15B2, public SafeCallbackBase { typedef typename XorpConstMemberCallback15B2::M M; XorpConstSafeMemberCallback15B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpConstMemberCallback15B2(file, line, o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback15B2() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback15B2::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 15 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback15B2 : public XorpConstMemberCallback15B2, public SafeCallbackBase { typedef typename XorpConstMemberCallback15B2::M M; XorpConstSafeMemberCallback15B2(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2) : XorpConstMemberCallback15B2(file, line, o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback15B2() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback15B2::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory15B2 { static XorpConstMemberCallback15B2* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2) const, BA1 ba1, BA2 ba2) { return new XorpConstSafeMemberCallback15B2(file, line, o, p, ba1, ba2); } }; template struct XorpConstMemberCallbackFactory15B2 { static XorpConstMemberCallback15B2* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2) const, BA1 ba1, BA2 ba2) { return new XorpConstMemberCallback15B2(file, line, o, p, ba1, ba2); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 15 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback15::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2) const, BA1 ba1, BA2 ba2) { return XorpConstMemberCallbackFactory15B2::True>::make(file, line, o, p, ba1, ba2); } /** * Factory function that creates a callback object targetted at a * const member function with 15 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback15::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2) const, BA1 ba1, BA2 ba2) { return XorpConstMemberCallbackFactory15B2::True>::make(file, line, &o, p, ba1, ba2); } /** * @short Callback object for functions with 15 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpFunctionCallback15B3 : public XorpCallback15 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3); XorpFunctionCallback15B3(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback15(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1, _ba2, _ba3); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; }; /** * @short Callback object for void functions with 15 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpFunctionCallback15B3 : public XorpCallback15 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3); XorpFunctionCallback15B3(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback15(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1, _ba2, _ba3); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; }; /** * Factory function that creates a callback object targetted at a * function with 15 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback15::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return typename XorpCallback15::RefPtr(new XorpFunctionCallback15B3(file, line, f, ba1, ba2, ba3)); } /** * @short Callback object for member methods with 15 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpMemberCallback15B3 : public XorpCallback15 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3) ; XorpMemberCallback15B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback15(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1, _ba2, _ba3); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for void member methods with 15 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpMemberCallback15B3 : public XorpCallback15 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3) ; XorpMemberCallback15B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback15(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1, _ba2, _ba3); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for safe member methods with 15 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpSafeMemberCallback15B3 : public XorpMemberCallback15B3, public SafeCallbackBase { typedef typename XorpMemberCallback15B3::M M; XorpSafeMemberCallback15B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpMemberCallback15B3(file, line, o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpSafeMemberCallback15B3() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback15B3::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 15 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpSafeMemberCallback15B3 : public XorpMemberCallback15B3, public SafeCallbackBase { typedef typename XorpMemberCallback15B3::M M; XorpSafeMemberCallback15B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpMemberCallback15B3(file, line, o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpSafeMemberCallback15B3() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { if (valid()) { record_dispatch_enter(); XorpMemberCallback15B3::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory15B3 { static XorpMemberCallback15B3* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpSafeMemberCallback15B3(file, line, o, p, ba1, ba2, ba3); } }; template struct XorpMemberCallbackFactory15B3 { static XorpMemberCallback15B3* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpMemberCallback15B3(file, line, o, p, ba1, ba2, ba3); }; }; /** * Factory function that creates a callback object targetted at a * member function with 15 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback15::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return XorpMemberCallbackFactory15B3::True>::make(file, line, o, p, ba1, ba2, ba3); } /** * Factory function that creates a callback object targetted at a * member function with 15 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback15::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return XorpMemberCallbackFactory15B3::True>::make(file, line, &o, p, ba1, ba2, ba3); } /** * @short Callback object for const member methods with 15 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstMemberCallback15B3 : public XorpCallback15 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3) const; XorpConstMemberCallback15B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback15(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1, _ba2, _ba3); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for void const member methods with 15 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstMemberCallback15B3 : public XorpCallback15 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3) const; XorpConstMemberCallback15B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback15(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1, _ba2, _ba3); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for const safe member methods with 15 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback15B3 : public XorpConstMemberCallback15B3, public SafeCallbackBase { typedef typename XorpConstMemberCallback15B3::M M; XorpConstSafeMemberCallback15B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpConstMemberCallback15B3(file, line, o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback15B3() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback15B3::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 15 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback15B3 : public XorpConstMemberCallback15B3, public SafeCallbackBase { typedef typename XorpConstMemberCallback15B3::M M; XorpConstSafeMemberCallback15B3(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpConstMemberCallback15B3(file, line, o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback15B3() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback15B3::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory15B3 { static XorpConstMemberCallback15B3* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpConstSafeMemberCallback15B3(file, line, o, p, ba1, ba2, ba3); } }; template struct XorpConstMemberCallbackFactory15B3 { static XorpConstMemberCallback15B3* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpConstMemberCallback15B3(file, line, o, p, ba1, ba2, ba3); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 15 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback15::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return XorpConstMemberCallbackFactory15B3::True>::make(file, line, o, p, ba1, ba2, ba3); } /** * Factory function that creates a callback object targetted at a * const member function with 15 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback15::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return XorpConstMemberCallbackFactory15B3::True>::make(file, line, &o, p, ba1, ba2, ba3); } /** * @short Callback object for functions with 15 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpFunctionCallback15B4 : public XorpCallback15 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4); XorpFunctionCallback15B4(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback15(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; }; /** * @short Callback object for void functions with 15 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpFunctionCallback15B4 : public XorpCallback15 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4); XorpFunctionCallback15B4(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback15(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; }; /** * Factory function that creates a callback object targetted at a * function with 15 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback15::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return typename XorpCallback15::RefPtr(new XorpFunctionCallback15B4(file, line, f, ba1, ba2, ba3, ba4)); } /** * @short Callback object for member methods with 15 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpMemberCallback15B4 : public XorpCallback15 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4) ; XorpMemberCallback15B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback15(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for void member methods with 15 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpMemberCallback15B4 : public XorpCallback15 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4) ; XorpMemberCallback15B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback15(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for safe member methods with 15 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpSafeMemberCallback15B4 : public XorpMemberCallback15B4, public SafeCallbackBase { typedef typename XorpMemberCallback15B4::M M; XorpSafeMemberCallback15B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpMemberCallback15B4(file, line, o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpSafeMemberCallback15B4() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback15B4::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 15 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpSafeMemberCallback15B4 : public XorpMemberCallback15B4, public SafeCallbackBase { typedef typename XorpMemberCallback15B4::M M; XorpSafeMemberCallback15B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpMemberCallback15B4(file, line, o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpSafeMemberCallback15B4() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { if (valid()) { record_dispatch_enter(); XorpMemberCallback15B4::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory15B4 { static XorpMemberCallback15B4* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpSafeMemberCallback15B4(file, line, o, p, ba1, ba2, ba3, ba4); } }; template struct XorpMemberCallbackFactory15B4 { static XorpMemberCallback15B4* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpMemberCallback15B4(file, line, o, p, ba1, ba2, ba3, ba4); }; }; /** * Factory function that creates a callback object targetted at a * member function with 15 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback15::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpMemberCallbackFactory15B4::True>::make(file, line, o, p, ba1, ba2, ba3, ba4); } /** * Factory function that creates a callback object targetted at a * member function with 15 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback15::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpMemberCallbackFactory15B4::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4); } /** * @short Callback object for const member methods with 15 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstMemberCallback15B4 : public XorpCallback15 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4) const; XorpConstMemberCallback15B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback15(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for void const member methods with 15 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstMemberCallback15B4 : public XorpCallback15 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4) const; XorpConstMemberCallback15B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback15(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1, _ba2, _ba3, _ba4); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for const safe member methods with 15 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback15B4 : public XorpConstMemberCallback15B4, public SafeCallbackBase { typedef typename XorpConstMemberCallback15B4::M M; XorpConstSafeMemberCallback15B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpConstMemberCallback15B4(file, line, o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback15B4() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback15B4::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 15 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback15B4 : public XorpConstMemberCallback15B4, public SafeCallbackBase { typedef typename XorpConstMemberCallback15B4::M M; XorpConstSafeMemberCallback15B4(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpConstMemberCallback15B4(file, line, o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback15B4() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback15B4::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory15B4 { static XorpConstMemberCallback15B4* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpConstSafeMemberCallback15B4(file, line, o, p, ba1, ba2, ba3, ba4); } }; template struct XorpConstMemberCallbackFactory15B4 { static XorpConstMemberCallback15B4* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpConstMemberCallback15B4(file, line, o, p, ba1, ba2, ba3, ba4); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 15 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback15::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpConstMemberCallbackFactory15B4::True>::make(file, line, o, p, ba1, ba2, ba3, ba4); } /** * Factory function that creates a callback object targetted at a * const member function with 15 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback15::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpConstMemberCallbackFactory15B4::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4); } /** * @short Callback object for functions with 15 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpFunctionCallback15B5 : public XorpCallback15 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4, BA5); XorpFunctionCallback15B5(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback15(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; }; /** * @short Callback object for void functions with 15 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpFunctionCallback15B5 : public XorpCallback15 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4, BA5); XorpFunctionCallback15B5(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback15(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; }; /** * Factory function that creates a callback object targetted at a * function with 15 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback15::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return typename XorpCallback15::RefPtr(new XorpFunctionCallback15B5(file, line, f, ba1, ba2, ba3, ba4, ba5)); } /** * @short Callback object for member methods with 15 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpMemberCallback15B5 : public XorpCallback15 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4, BA5) ; XorpMemberCallback15B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback15(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for void member methods with 15 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpMemberCallback15B5 : public XorpCallback15 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4, BA5) ; XorpMemberCallback15B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback15(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for safe member methods with 15 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpSafeMemberCallback15B5 : public XorpMemberCallback15B5, public SafeCallbackBase { typedef typename XorpMemberCallback15B5::M M; XorpSafeMemberCallback15B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpMemberCallback15B5(file, line, o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpSafeMemberCallback15B5() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback15B5::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 15 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpSafeMemberCallback15B5 : public XorpMemberCallback15B5, public SafeCallbackBase { typedef typename XorpMemberCallback15B5::M M; XorpSafeMemberCallback15B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpMemberCallback15B5(file, line, o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpSafeMemberCallback15B5() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { if (valid()) { record_dispatch_enter(); XorpMemberCallback15B5::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory15B5 { static XorpMemberCallback15B5* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpSafeMemberCallback15B5(file, line, o, p, ba1, ba2, ba3, ba4, ba5); } }; template struct XorpMemberCallbackFactory15B5 { static XorpMemberCallback15B5* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpMemberCallback15B5(file, line, o, p, ba1, ba2, ba3, ba4, ba5); }; }; /** * Factory function that creates a callback object targetted at a * member function with 15 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback15::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpMemberCallbackFactory15B5::True>::make(file, line, o, p, ba1, ba2, ba3, ba4, ba5); } /** * Factory function that creates a callback object targetted at a * member function with 15 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback15::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpMemberCallbackFactory15B5::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4, ba5); } /** * @short Callback object for const member methods with 15 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstMemberCallback15B5 : public XorpCallback15 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4, BA5) const; XorpConstMemberCallback15B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback15(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for void const member methods with 15 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstMemberCallback15B5 : public XorpCallback15 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4, BA5) const; XorpConstMemberCallback15B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback15(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1, _ba2, _ba3, _ba4, _ba5); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for const safe member methods with 15 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback15B5 : public XorpConstMemberCallback15B5, public SafeCallbackBase { typedef typename XorpConstMemberCallback15B5::M M; XorpConstSafeMemberCallback15B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpConstMemberCallback15B5(file, line, o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback15B5() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback15B5::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 15 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback15B5 : public XorpConstMemberCallback15B5, public SafeCallbackBase { typedef typename XorpConstMemberCallback15B5::M M; XorpConstSafeMemberCallback15B5(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpConstMemberCallback15B5(file, line, o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback15B5() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback15B5::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory15B5 { static XorpConstMemberCallback15B5* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpConstSafeMemberCallback15B5(file, line, o, p, ba1, ba2, ba3, ba4, ba5); } }; template struct XorpConstMemberCallbackFactory15B5 { static XorpConstMemberCallback15B5* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpConstMemberCallback15B5(file, line, o, p, ba1, ba2, ba3, ba4, ba5); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 15 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback15::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpConstMemberCallbackFactory15B5::True>::make(file, line, o, p, ba1, ba2, ba3, ba4, ba5); } /** * Factory function that creates a callback object targetted at a * const member function with 15 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback15::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpConstMemberCallbackFactory15B5::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4, ba5); } /** * @short Callback object for functions with 15 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpFunctionCallback15B6 : public XorpCallback15 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4, BA5, BA6); XorpFunctionCallback15B6(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback15(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { record_dispatch_enter(); R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; BA6 _ba6; }; /** * @short Callback object for void functions with 15 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpFunctionCallback15B6 : public XorpCallback15 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4, BA5, BA6); XorpFunctionCallback15B6(const char* file, int line, F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback15(file, line), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { record_dispatch_enter(); (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; BA6 _ba6; }; /** * Factory function that creates a callback object targetted at a * function with 15 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback15::RefPtr dbg_callback(const char* file, int line, R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return typename XorpCallback15::RefPtr(new XorpFunctionCallback15B6(file, line, f, ba1, ba2, ba3, ba4, ba5, ba6)); } /** * @short Callback object for member methods with 15 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpMemberCallback15B6 : public XorpCallback15 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4, BA5, BA6) ; XorpMemberCallback15B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback15(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for void member methods with 15 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpMemberCallback15B6 : public XorpCallback15 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4, BA5, BA6) ; XorpMemberCallback15B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback15(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for safe member methods with 15 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpSafeMemberCallback15B6 : public XorpMemberCallback15B6, public SafeCallbackBase { typedef typename XorpMemberCallback15B6::M M; XorpSafeMemberCallback15B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpMemberCallback15B6(file, line, o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpSafeMemberCallback15B6() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { if (valid()) { record_dispatch_enter(); R r = XorpMemberCallback15B6::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void safe member methods with 15 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpSafeMemberCallback15B6 : public XorpMemberCallback15B6, public SafeCallbackBase { typedef typename XorpMemberCallback15B6::M M; XorpSafeMemberCallback15B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpMemberCallback15B6(file, line, o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpSafeMemberCallback15B6() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { if (valid()) { record_dispatch_enter(); XorpMemberCallback15B6::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); record_dispatch_leave(); } } }; template struct XorpMemberCallbackFactory15B6 { static XorpMemberCallback15B6* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpSafeMemberCallback15B6(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); } }; template struct XorpMemberCallbackFactory15B6 { static XorpMemberCallback15B6* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpMemberCallback15B6(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); }; }; /** * Factory function that creates a callback object targetted at a * member function with 15 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback15::RefPtr dbg_callback(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpMemberCallbackFactory15B6::True>::make(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * Factory function that creates a callback object targetted at a * member function with 15 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback15::RefPtr dbg_callback(const char* file, int line, O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpMemberCallbackFactory15B6::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * @short Callback object for const member methods with 15 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstMemberCallback15B6 : public XorpCallback15 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4, BA5, BA6) const; XorpConstMemberCallback15B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback15(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { record_dispatch_enter(); R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for void const member methods with 15 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstMemberCallback15B6 : public XorpCallback15 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4, BA5, BA6) const; XorpConstMemberCallback15B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback15(file, line), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { record_dispatch_enter(); ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); record_dispatch_leave(); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for const safe member methods with 15 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback15B6 : public XorpConstMemberCallback15B6, public SafeCallbackBase { typedef typename XorpConstMemberCallback15B6::M M; XorpConstSafeMemberCallback15B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpConstMemberCallback15B6(file, line, o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback15B6() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { if (valid()) { record_dispatch_enter(); R r = XorpConstMemberCallback15B6::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); record_dispatch_leave(); return r; } } }; /** * @short Callback object for void const safe member methods with 15 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback15B6 : public XorpConstMemberCallback15B6, public SafeCallbackBase { typedef typename XorpConstMemberCallback15B6::M M; XorpConstSafeMemberCallback15B6(const char* file, int line, O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpConstMemberCallback15B6(file, line, o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback15B6() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { if (valid()) { record_dispatch_enter(); XorpConstMemberCallback15B6::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); record_dispatch_leave(); } } }; template struct XorpConstMemberCallbackFactory15B6 { static XorpConstMemberCallback15B6* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpConstSafeMemberCallback15B6(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); } }; template struct XorpConstMemberCallbackFactory15B6 { static XorpConstMemberCallback15B6* make(const char* file, int line, O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpConstMemberCallback15B6(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 15 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback15::RefPtr dbg_callback(const char* file, int line, const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpConstMemberCallbackFactory15B6::True>::make(file, line, o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * Factory function that creates a callback object targetted at a * const member function with 15 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback15::RefPtr dbg_callback(const char* file, int line, const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpConstMemberCallbackFactory15B6::True>::make(file, line, &o, p, ba1, ba2, ba3, ba4, ba5, ba6); } #endif /* __XORP_CALLBACK_HH__ */ xorp/libxorp/heap.hh0000664000076400007640000001144111540224227014575 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // Portions of this code originally derived from: // FreeBSD dummynet code, (C) 2001 Luigi Rizzo. #ifndef __LIBXORP_HEAP_HH__ #define __LIBXORP_HEAP_HH__ #include #ifdef HAVE_STDLIB_H #include #endif #include #ifdef HAVE_SYS_TIME_H #include #endif #include "timeval.hh" #include "bug_catcher.hh" /** * @short Heap class * * Heap implements a priority queue, mostly used by @ref Timer * objects. This implementation supports removal of arbitrary objects * from the heap, even if they are not located at the top. To support * this the objects must inherit from HeapBase which contains an index * to its own positon within the heap. * */ const int NOT_IN_HEAP = -1 ; /** * Objects stored on the heap should inherit from this class. If * removal from arbitary positions (not just head) is required. */ class HeapBase { public: HeapBase() : _pos_in_heap(NOT_IN_HEAP) {} virtual ~HeapBase() {} int _pos_in_heap; // position of this object in the heap }; class Heap /*: public BugCatcher*/ { friend class TimerList; protected: typedef TimeVal Heap_Key ; struct heap_entry { Heap_Key key; /* sorting key. Topmost element is smallest one */ HeapBase *object; /* object pointer */ } ; public: /** * Default constructor used to build a standard heap with no support for * removal from the middle. */ Heap(); /** * Constructor used to build a standard heap with support for * removal from the middle. Should be used with something * like: *
     * struct _foo { ... ; int my_index ; ... } x;
     * ...
     * Heap *h = new Heap (OFFSET_OF(x, my_index));
     * 
*/ explicit Heap(bool); // heap supporting removal from the middle /** * Destructor */ virtual ~Heap(); /** * Push an object into the heap by using a sorting key. * * @param k the sorting key. * @param p the object to push into the heap. */ void push(Heap_Key k, HeapBase *p) { push(k, p, 0); } /** * Bubble-up an object in the heap. * * Note: this probably should not be exposed. * * @param i the offset of the object to bubble-up. */ void push(int i) { push( Heap_Key(0, 0) /* anything */, NULL, i); } /** * Move an object in the heap according to the new key. * Note: can only be used if the heap supports removal from the middle. * * @param new_key the new key. * @param object the object to move. */ void move(Heap_Key new_key, HeapBase *object); /** * Get a pointer to the entry at the top of the heap. * * Both the key and the value can be derived from the return value. * * @return the pointer to the entry at the top of the heap. */ struct heap_entry *top() const { return (_p == 0 || _elements == 0) ? 0 : &(_p[0]) ; } /** * Get the number of elements in the heap. * * @return the number of elements in the heap. */ size_t size() const { return _elements; } /** * Remove the object top of the heap. */ void pop() { pop_obj(0); } /** * Remove an object from an arbitrary position in the heap. * * Note: only valid if the heap supports this kind of operation. * * @param p the object to remove if not NULL, otherwise the top element * from the heap. */ void pop_obj(HeapBase *p); /** * Rebuild the heap structure. */ void heapify(); #ifdef _TEST_HEAP_CODE void print(); void print_all(int); #endif private: void push(Heap_Key key, HeapBase *p, int son); int resize(int new_size); void verify(); int _size; int _elements ; bool _intrude ; // True if the object holds the heap position struct heap_entry *_p ; // really an array of "size" entries }; #endif // __LIBXORP_HEAP_HH__ xorp/libxorp/exceptions.hh0000664000076400007640000001575211540225527016056 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/libxorp/exceptions.hh,v 1.12 2009/01/05 18:30:57 jtc Exp $ #ifndef __LIBXORP_EXCEPTIONS_HH__ #define __LIBXORP_EXCEPTIONS_HH__ #include "xorp.h" #ifdef XORP_USE_USTL #include using namespace std; #else #include #endif #include #include "libxorp/c_format.hh" /** * Macro to known insert values into exception arguments. */ #define xorp_throw(_class, args...) \ throw _class(__FILE__, __LINE__, args); #define xorp_throw0(_class) \ throw _class(__FILE__, __LINE__); /** * @short A base class for XORP exceptions. */ class XorpException { public: /** * Constructor for a given type for exception, file name, * and file line number. * * @param init_what the type of exception. * @param file the file name where the exception was thrown. * @param line the line in @ref file where the exception was thrown. */ XorpException(const char* init_what, const char* file, size_t line); /** * Destructor */ virtual ~XorpException(); /** * Get the type of this exception. * * @return the string with the type of this exception. */ const string& what() const { return _what; } /** * Get the location for throwing an exception. * * @return the string with the location (file name and file line number) * for throwing an exception. */ const string where() const; /** * Get the reason for throwing an exception. * * @return the string with the reason for throwing an exception. */ virtual const string why() const; /** * Convert this exception from binary form to presentation format. * * @return C++ string with the human-readable ASCII representation * of the exception. */ string str() const; protected: string _what; // The type of exception const char* _file; // The file name where exception occured size_t _line; // The line number where exception occured }; /** * @short A base class for XORP exceptions that keeps the reason for exception. */ class XorpReasonedException : public XorpException { public: /** * Constructor for a given type for exception, file name, * file line number, and a reason. * * @param init_what the type of exception. * @param file the file name where the exception was thrown. * @param line the line in @ref file where the exception was thrown. * @param init_why the reason for the exception that was thrown. */ XorpReasonedException(const char* init_what, const char* file, size_t line, const string& init_why); /** * Get the reason for throwing an exception. * * @return the string with the reason for throwing an exception. */ const string why() const; protected: string _why; // The reason for the exception }; // ---------------------------------------------------------------------------- // Standard XORP exceptions /** * @short A standard XORP exception that is thrown if a string is invalid. */ class InvalidString : public XorpReasonedException { public: InvalidString(const char* file, size_t line, const string& init_why = ""); }; /** * @short A standard XORP exception that is thrown if an address is invalid. */ class InvalidAddress : public XorpReasonedException { public: InvalidAddress(const char* file, size_t line, const string& init_why = ""); }; /** * @short A standard XORP exception that is thrown if a port is invalid. */ class InvalidPort : public XorpReasonedException { public: InvalidPort(const char* file, size_t line, const string& init_why = ""); }; /** * @short A standard XORP exception that is thrown if a cast is invalid. */ class InvalidCast : public XorpReasonedException { public: InvalidCast(const char* file, size_t line, const string& init_why = ""); }; /** * @short A standard XORP exception that is thrown if a buffer ofset is * invalid. */ class InvalidBufferOffset : public XorpReasonedException { public: InvalidBufferOffset(const char* file, size_t line, const string& init_why = ""); }; /** * @short A standard XORP exception that is thrown if an address family is * invalid. */ class InvalidFamily : public XorpException { public: InvalidFamily(const char* file, size_t line, int af); const string why() const; protected: int _af; }; /** * @short A standard XORP exception that is thrown if the packet is invalid. */ class InvalidPacket : public XorpReasonedException { public: InvalidPacket(const char* file, size_t line, const string& init_why = ""); }; /** * @short A standard XORP exception that is thrown if an IP netmask length is * invalid. */ class InvalidNetmaskLength : public XorpException { public: InvalidNetmaskLength(const char* file, size_t line, int netmask_length); const string why() const; protected: int _netmask_length; }; // ---------------------------------------------------------------------------- /** * Print diagnostic message if exception is derived from @ref XorpException or * from standard exceptions, and terminate the program. */ void xorp_catch_standard_exceptions(); /** * Print diagnostic message if exception is derived from @ref XorpException or * from standard exceptions. * * Note that unlike @ref xorp_catch_standard_exceptions(), the program * does NOT terminate. */ void xorp_print_standard_exceptions(); /** * Unexpected exceptions are programming errors and this handler can * be installed with XorpUnexpectedHandler to print out as much * information as possible. The default behaviour is just to dump * core, but it very hard to detect what happened. */ void xorp_unexpected_handler(); /** * XorpUnexpectedHandler installs the xorp_unexpected_handler when * instantiated and re-installed the previous handler when * uninstantiated. */ class XorpUnexpectedHandler { public: XorpUnexpectedHandler(unexpected_handler h = xorp_unexpected_handler) { _oh = set_unexpected(h); } ~XorpUnexpectedHandler() { set_unexpected(_oh); } private: unexpected_handler _oh; }; #endif // __LIBXORP_EXCEPTIONS_HH__ xorp/libxorp/bug_catcher.hh0000664000076400007640000000252711703345405016136 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2012 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // #ifndef __XORP_BUG_CATCHER_INC__ #define __XORP_BUG_CATCHER_INC__ #include class BugCatcher { private: unsigned int magic; static unsigned int _cnt; public: #define X_BUG_CATCHER_MAGIC 0x1234543 BugCatcher() { magic = X_BUG_CATCHER_MAGIC; _cnt++; } BugCatcher(const BugCatcher& rhs) { magic = rhs.magic; _cnt++; } virtual ~BugCatcher() { assert_not_deleted(); magic = 0xdeadbeef; _cnt--; } virtual void assert_not_deleted() const { assert(magic == X_BUG_CATCHER_MAGIC); } static int get_instance_count() { return _cnt; } }; #endif xorp/libxorp/README0000664000076400007640000000071411421137511014215 0ustar greearbgreearb# # $XORP: xorp/libxorp/README,v 1.1.1.1 2002/12/11 23:56:04 hodson Exp $ # LIBXORP: XORP Common Library Libxorp is a set of classes for basic XORP functionalities such as IP addresses, IP subnets, etc. Documentation ============= Overview material is present in: ${XORP}/docs/libxorp The programming documentation is in: ${XORP}/docs/kdoc/html/libxorp Status ====== This code is functional and widely used by practically all other XORP components. xorp/libxorp/ipv6.hh0000664000076400007640000005375411540224227014561 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/libxorp/ipv6.hh,v 1.41 2008/10/02 21:57:31 bms Exp $ #ifndef __LIBXORP_IPV6_HH__ #define __LIBXORP_IPV6_HH__ #include "libxorp/xorp.h" #include "libxorp/exceptions.hh" #include "libxorp/range.hh" #include "libxorp/utils.hh" struct in6_addr; /** * @short IPv6 address class * * The IPv6 address class is a trivial class for handling IPv6 * addresses and for performing operations on them such as printing * and masking. */ class IPv6 { public: typedef struct in6_addr InAddrType; typedef struct sockaddr_in6 SockAddrType; public: /** * Default constructor * * The address value is initialized to IN6ADDR_ANY. */ IPv6() { _addr[0] = _addr[1] = _addr[2] = _addr[3] = 0; } /** * Constructor from a (uint8_t *) memory pointer. * * @param from_uint8 the pointer to the memory to copy the address value * from. */ explicit IPv6(const uint8_t *from_uint8); /** * Constructor from a (uint32_t *) memory pointer. * * @param from_uint32 the pointer to the memory to copy the address value * from. */ explicit IPv6(const uint32_t *from_uint32); /** * Constructor from in6_addr structure. * * @param from_in6_addr the storage to copy the address value from. */ IPv6(const in6_addr& from_in6_addr); /** * Constructor from sockaddr structure. * * @param sa sockaddr to construct IPv6 addr from. */ IPv6(const sockaddr& sa) throw (InvalidFamily); /** * Constructor from sockaddr_storage structure. * * @param ss sockaddr_storage to construct IPv6 addr from. */ IPv6(const sockaddr_storage& ss) throw (InvalidFamily); /** * Constructor from sockaddr_in6 structure. * * @param sin6 sockaddr_in6 to construct IPv6 addr from. */ IPv6(const sockaddr_in6& sin6) throw (InvalidFamily); /** * Constructor from a string. * * @param from_cstring C-style string in the IPv6 canonical human-readable. * format used for initialization. */ IPv6(const char *from_cstring) throw (InvalidString); /** * Copy the IPv6 raw address to specified memory location. * * @param: to_uint8 the pointer to the memory to copy the address to. * @return the number of copied octets. */ size_t copy_out(uint8_t *to_uint8) const; /** * Copy the IPv6 raw address to an in6_addr structure. * * @param to_in6_addr the storage to copy the address to. * @return the number of copied octets. */ size_t copy_out(in6_addr& to_in6_addr) const; /** * Copy the IPv6 raw address to a sockaddr structure. * * Copy the raw address held within an IPv6 instance to an sockaddr * structure and assign appropriately and set fields within sockaddr * appropriately. * * @param to_sockaddr the storage to copy the address to. * @return the number of copied octets. */ size_t copy_out(sockaddr& to_sockaddr) const; /** * Copy the IPv6 raw address to a sockaddr_storage structure. * * Copy the raw address held within an IPv6 instance to an sockaddr_storage * structure and assign appropriately and set fields within * sockaddr_storage appropriately. * * @param to_sockaddr_storage the storage to copy the address to. * @return the number of copied octets. */ size_t copy_out(sockaddr_storage& to_sockaddr_storage) const; /** * Copy the IPv6 raw address to a sockaddr_in6 structure. * * Copy the raw address held within an IPv6 instance to a sockaddr_in6 * structure and assign appropriately and set fields within sockaddr_in * appropriately. * * @param to_sockaddr_in6 the storage to copy the address to. * @return the number of copied octets. */ size_t copy_out(sockaddr_in6& to_sockaddr_in6) const; /** * Copy a raw IPv6 address from specificed memory location into IPv6 * structure. * * @param from_uint8 the memory address to copy the address from. * @return the number of copied octets. */ size_t copy_in(const uint8_t *from_uint8); /** * Copy a raw IPv6 address from a in6_addr structure into IPv6 structure. * * @param from_in6_addr the storage to copy the address from. * @return the number of copied octets. */ size_t copy_in(const in6_addr& from_in6_addr); /** * Copy a raw IPv6 address from a sockaddr structure into IPv6 structure. * * Note that the address in the sockaddr structure must be of IPv6 address * family. * * @param from_sockaddr the storage to copy the address from. * @return the number of copied octets. */ size_t copy_in(const sockaddr& from_sockaddr) throw (InvalidFamily); /** * Copy a raw IPv6 address from a sockaddr_storage structure into IPv6 * structure. * * Note that the address in the sockaddr_storage structure must be of * IPv6 address family. * * @param from_sockaddr_storage the storage to copy the address from. * @return the number of copied octets. */ size_t copy_in(const sockaddr_storage& from_sockaddr_storage) throw (InvalidFamily); /** * Copy a raw address from sockaddr_in6 structure into IPv6 structure. * * Note that the address in the sockaddr structure must be of IPv6 address * family. * * @param from_sockaddr_in6 the storage to copy the address from. * @return the number of copied octets. */ size_t copy_in(const sockaddr_in6& from_sockaddr_in6) throw (InvalidFamily); /** * Bitwise-Negation Operator * * @return address complement (i.e., all 0s become 1s, and vice-versa). */ IPv6 operator~() const; /** * OR Operator * * @param other the right-hand operand to OR with. * @return bitwise OR of two addresses. */ IPv6 operator|(const IPv6& other) const; /** * AND Operator * * @param other the right-hand operand to AND with. * @return bitwise AND of two addresses. */ IPv6 operator&(const IPv6& other) const; /** * XOR Operator * * @param other the right-hand operand to XOR with. * @return bitwize eXclusive-OR of two addresses. */ IPv6 operator^(const IPv6& other) const; /** * Operator << * * @param left_shift the number of bits to shift to the left. * @return IPv6 address that is shift bitwise to the left. */ IPv6 operator<<(uint32_t left_shift) const; /** * Operator >> * * @param right_shift the number of bits to shift to the right. * @return IPv6 address that is shift bitwise to the right. */ IPv6 operator>>(uint32_t right_shift) const; /** * Less-Than Operator * * @param other the right-hand operand to compare against. * @return true if the left-hand operand is numerically smaller than the * right-hand operand. */ bool operator<(const IPv6& other) const; /** * Equality Operator * * @param other the right-hand operand to compare against. * @return true if the left-hand operand is numerically same as the * right-hand operand. */ bool operator==(const IPv6& other) const; /** * Not-Equal Operator * * @param other the right-hand operand to compare against. * @return true if the left-hand operand is numerically not same as the * right-hand operand. */ bool operator!=(const IPv6& other) const; /** * Equality Operator for @ref IPv6 against @ref IPv6Range operand. * * @param rhs the right-hand @ref IPv6Range operand. * @return true if the value of the left-hand operand falls inside * the range defined by the right-hand operand. */ bool operator==(const IPv6Range& rhs) const { return (*this >= rhs.low() && *this <= rhs.high()); } /** * Non-equality Operator for @ref IPv6 against @ref IPv6Range operand. * * @param rhs the right-hand @ref IPv6Range operand. * @return true if the value of the left-hand operand falls outside * the range defined by the right-hand operand. */ bool operator!=(const IPv6Range& rhs) const { return (*this < rhs.low() || *this > rhs.high()); } /** * Less-than comparison for @ref IPv6 against @ref IPv6Range operand. * * @param rhs the right-hand @ref IPv6Range operand. * @return true if the value of the left-hand operand is bellow * the range defined by the right-hand operand. */ bool operator<(const IPv6Range& rhs) const { return (*this < rhs.low()); } /** * Less-than or equal comparison for @ref IPv6 against @ref IPv6Range * * @param rhs the right-hand @ref IPv6Range operand. * @return true if the value of the left-hand operand is bellow or within * the range defined by the right-hand operand. */ bool operator<=(const IPv6Range& rhs) const { return (*this <= rhs.high()); } /** * Greater-than comparison for @ref IPv6 against @ref IPv6Range operand. * * @param rhs the right-hand @ref IPv6Range operand. * @return true if the value of the left-hand operand is above * the range defined by the right-hand operand. */ bool operator>(const IPv6Range& rhs) const { return (*this > rhs.high()); } /** * Greater-than or equal comparison for @ref IPv6 against @ref IPv6Range * * @param rhs the right-hand @ref IPv6Range operand. * @return true if the value of the left-hand operand is above or within * the range defined by the right-hand operand. */ bool operator>=(const IPv6Range& rhs) const { return (*this >= rhs.low()); } /** * Decrement Operator * * The numerical value of this address is decremented by one. * However, if the address value before the decrement was all-0s, * after the decrement its value would be all-1s (i.e., it will * wrap-around). * * @return a reference to this address after it was decremented by one. */ IPv6& operator--(); /** * Increment Operator * * The numerical value of this address is incremented by one. * However, if the address value before the increment was all-1s, * after the increment its value would be all-0s (i.e., it will * wrap-around). * * @return a reference to this address after it was incremented by one. */ IPv6& operator++(); /** * Convert this address from binary form to presentation format. * * @return C++ string with the human-readable ASCII representation * of the address. */ string str() const; /** * Test if this address is numerically zero. * * @return true if the address is numerically zero. */ bool is_zero() const { return *this == ZERO(); } /** * Test if this address is a valid unicast address. * * Note that the numerically zero address is excluded. * * @return true if the address is a valid unicast address. */ bool is_unicast() const; /** * Test if this address is a valid multicast address. * * @return true if the address is a valid multicast address. */ bool is_multicast() const; /** * Test if this address is a valid link-local unicast address. * * @return true if the address is a valid unicast address, * and the scope of the address is link-local. */ bool is_linklocal_unicast() const; /** * Test if this address is a valid interface-local multicast address. * * Note that "node-local" multicast addresses were renamed * to "interface-local" by RFC-3513. * * @return true if the address is a valid multicast address, * and the scope of the address is interface-local. */ bool is_interfacelocal_multicast() const; /** * Test if this address is a valid node-local multicast address. * * Note that "node-local" multicast addresses were renamed * to "interface-local" by RFC-3513. * This method is kept for backward compatibility. * * @return true if the address is a valid multicast address, * and the scope of the address is node-local. */ bool is_nodelocal_multicast() const { return is_interfacelocal_multicast(); } /** * Test if this address is a valid link-local multicast address. * * @return true if the address is a valid multicast address, * and the scope of the address is link-local. */ bool is_linklocal_multicast() const; /** * Test if this address is the loopback address. * * @return true if the address is the loopback address. */ bool is_loopback() const; /** * Get the address octet-size. * * Note that this is a static function and can be used without * a particular object. Example: * size_t my_size = IPv6::addr_bytelen(); * size_t my_size = ipv6.addr_bytelen(); * * @return address size in number of octets. */ static size_t addr_bytelen() { x_static_assert(sizeof(IPv6) == 4 * sizeof(uint32_t)); return sizeof(IPv6); } /** * Get the address bit-length. * * Note that this is a static function and can be used without * a particular object. Example: * uint32_t my_bitlen = IPv6::addr_bitlen(); * uint32_t my_bitlen = ipv6.addr_bitlen(); * * @return address size in number of bits. */ static uint32_t addr_bitlen() { return uint32_t(8 * sizeof(uint8_t) * addr_bytelen()); } /** * Get the mask length for the multicast base address. * * Note that this is a static function and can be used without * a particular object. Example: * size_t my_len = IPv6::ip_multicast_base_address_mask_len(); * size_t my_len = ipv6.ip_multicast_base_address_mask_len(); * * @return the multicast base address mask_len for family AF_INET6. */ static uint32_t ip_multicast_base_address_mask_len() { #define IP_MULTICAST_BASE_ADDRESS_MASK_LEN_IPV6 8 return (IP_MULTICAST_BASE_ADDRESS_MASK_LEN_IPV6); #undef IP_MULTICAST_BASE_ADDRESS_MASK_LEN_IPV6 } /** * Make an IPv6 mask prefix. * * @param mask_len the length of the mask to create. * @return a new IPv6 address that contains a mask of length @ref mask_len. */ static const IPv6& make_prefix(uint32_t mask_len) throw (InvalidNetmaskLength); /** * Make an IPv6 address prefix. * * @param prefix_len the length of the mask of the prefix to create. * @return a new IPv6 address created by masking this address with a mask * of length @ref prefix_len. */ IPv6 mask_by_prefix_len(uint32_t prefix_len) const throw (InvalidNetmaskLength) { return (*this) & make_prefix(prefix_len); } /** * Get the mask length. * * @return the prefix length of the contiguous mask presumably stored * as an IPv6 address. */ uint32_t mask_len() const; /** * Get the uint32_t raw value of this address. * * @return the value of this IPv6 address as a pointer to an array * of 4 unsigned 32-bit integers. */ const uint32_t *addr() const { return _addr; } /** * Set the address value. * * @param from the pointer to an array of 16 8-bit unsigned integers * with the value to set this address to. */ void set_addr(const uint8_t *from_uint8) { memcpy(_addr, from_uint8, 16); } /** * Constant for address family */ enum { AF = AF_INET6 }; /** * Constant for IP protocol version */ enum { IPV = 6 }; /** * Get the address family. * * @return the address family of this address. */ static int af() { return AF; } /** * Get the IP protocol version. * * @return the IP protocol version of this address. */ static uint32_t ip_version() { return IPV; } /** * Get the human-readable string with the IP protocol version. * * @return the human-readable string with the IP protocol version of * this address. */ static const string& ip_version_str(); /** * Extract bits from an address. * * @param lsb starting bit position (from the right) to extract. * @param len number of bits to extract. The maximum value is 32. * @return the first @ref len bits starting from the rightmost * position @ref lsb. The returned bits are in host order. */ uint32_t bits(uint32_t lsb, uint32_t len) const; /** * Count the number of bits that are set in this address. * * @return the number of bits that are set in this address. */ uint32_t bit_count() const; /** * Count the number of leading zeroes in this address. * * @return the number of leading zeroes in this address. */ uint32_t leading_zero_count() const; /** * Pre-defined IPv6 address constants. */ static const IPv6& ZERO(int af = AF_INET6); static const IPv6& ANY(int af = AF_INET6); static const IPv6& ALL_ONES(int af = AF_INET6); static const IPv6& LOOPBACK(int af = AF_INET6); static const IPv6& MULTICAST_BASE(int af = AF_INET6); static const IPv6& MULTICAST_ALL_SYSTEMS(int af = AF_INET6); static const IPv6& MULTICAST_ALL_ROUTERS(int af = AF_INET6); static const IPv6& DVMRP_ROUTERS(int af = AF_INET6); static const IPv6& OSPFIGP_ROUTERS(int af = AF_INET6); static const IPv6& OSPFIGP_DESIGNATED_ROUTERS(int af = AF_INET6); static const IPv6& RIP2_ROUTERS(int af = AF_INET6); static const IPv6& PIM_ROUTERS(int af = AF_INET6); static const IPv6& SSM_ROUTERS(int af = AF_INET6); /** * Number of bits in address as a constant. */ static const uint32_t ADDR_BITLEN = 128; /** * Number of bytes in address as a constant. */ static const uint32_t ADDR_BYTELEN = ADDR_BITLEN / 8; private: uint32_t _addr[4]; // The address value (in network-order) }; inline uint32_t IPv6::bits(uint32_t lsb, uint32_t len) const { uint32_t mask = ~(0xffffffffU << len); if (len >= 32) mask = 0xffffffffU; // XXX: shifting with >= 32 bits is undefined return ntohl((*this >> lsb)._addr[3]) & mask; } inline uint32_t IPv6::bit_count() const { // XXX: no need for ntohl() return (xorp_bit_count_uint32(_addr[0]) + xorp_bit_count_uint32(_addr[1]) + xorp_bit_count_uint32(_addr[2]) + xorp_bit_count_uint32(_addr[3])); } inline uint32_t IPv6::leading_zero_count() const { uint32_t r = 0; for (int i = 0; i < 4; i++) { if (_addr[i] != 0) { r += xorp_leading_zero_count_uint32(ntohl(_addr[i])); break; } r += 32; } return (r); } struct IPv6Constants { static const IPv6 zero, any, all_ones, loopback, multicast_base, multicast_all_systems, multicast_all_routers, dvmrp_routers, ospfigp_routers, ospfigp_designated_routers, rip2_routers, pim_routers, ssm_routers; }; inline const IPv6& IPv6::ZERO(int) { return IPv6Constants::zero; } inline const IPv6& IPv6::ANY(int) { return IPv6Constants::any; } inline const IPv6& IPv6::ALL_ONES(int) { return IPv6Constants::all_ones; } inline const IPv6& IPv6::LOOPBACK(int) { return IPv6Constants::loopback; } inline const IPv6& IPv6::MULTICAST_BASE(int) { return IPv6Constants::multicast_base; } inline const IPv6& IPv6::MULTICAST_ALL_SYSTEMS(int) { return IPv6Constants::multicast_all_systems; } inline const IPv6& IPv6::MULTICAST_ALL_ROUTERS(int) { return IPv6Constants::multicast_all_routers; } inline const IPv6& IPv6::DVMRP_ROUTERS(int) { return IPv6Constants::dvmrp_routers; } inline const IPv6& IPv6::OSPFIGP_ROUTERS(int) { return IPv6Constants::ospfigp_routers; } inline const IPv6& IPv6::OSPFIGP_DESIGNATED_ROUTERS(int) { return IPv6Constants::ospfigp_designated_routers; } inline const IPv6& IPv6::RIP2_ROUTERS(int) { return IPv6Constants::rip2_routers; } inline const IPv6& IPv6::PIM_ROUTERS(int) { return IPv6Constants::pim_routers; } inline const IPv6& IPv6::SSM_ROUTERS(int) { return IPv6Constants::ssm_routers; } inline IPv6 IPv6::operator~() const { uint32_t tmp_addr[4]; tmp_addr[0] = ~_addr[0]; tmp_addr[1] = ~_addr[1]; tmp_addr[2] = ~_addr[2]; tmp_addr[3] = ~_addr[3]; return IPv6(tmp_addr); } inline IPv6 IPv6::operator|(const IPv6& other) const { uint32_t tmp_addr[4]; tmp_addr[0] = _addr[0] | other._addr[0]; tmp_addr[1] = _addr[1] | other._addr[1]; tmp_addr[2] = _addr[2] | other._addr[2]; tmp_addr[3] = _addr[3] | other._addr[3]; return IPv6(tmp_addr); } inline IPv6 IPv6::operator&(const IPv6& other) const { uint32_t tmp_addr[4]; tmp_addr[0] = _addr[0] & other._addr[0]; tmp_addr[1] = _addr[1] & other._addr[1]; tmp_addr[2] = _addr[2] & other._addr[2]; tmp_addr[3] = _addr[3] & other._addr[3]; return IPv6(tmp_addr); } inline IPv6 IPv6::operator^(const IPv6& other) const { uint32_t tmp_addr[4]; tmp_addr[0] = _addr[0] ^ other._addr[0]; tmp_addr[1] = _addr[1] ^ other._addr[1]; tmp_addr[2] = _addr[2] ^ other._addr[2]; tmp_addr[3] = _addr[3] ^ other._addr[3]; return IPv6(tmp_addr); } #endif // __LIBXORP_IPV6_HH__ xorp/libxorp/ref_trie.hh0000664000076400007640000011250611421137511015460 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/libxorp/ref_trie.hh,v 1.34 2008/10/02 21:57:32 bms Exp $ #ifndef __LIBXORP_REF_TRIE_HH__ #define __LIBXORP_REF_TRIE_HH__ #include "ipnet.hh" // Macros //#define VALIDATE_XORP_TRIE //#define DEBUG_LOGGING #include "xlog.h" #include "debug.h" #include "minitraits.hh" #include "stack" #define NODE_DELETED 0x8000 #define NODE_REFS_MASK 0x7fff /* * This module implements a trie to support route lookups. * */ template class RefTriePostOrderIterator; template class RefTriePreOrderIterator; template class RefTrie; /** * @short RefTrieNode definition * * RefTrieNode's are the elements of a RefTrie. * Each node is associated to a Key and possibly a Payload. * Nodes with a Payload ("full") can have 0, 1 or 2 children. * Nodes without a Payload ("empty") can only be internal nodes, * and MUST have 2 children (or they have no reason to exist). * * Children have a Key which is strictly contained in their * parent's Key -- more precisely, they are in either the left * or the right half of the parent's Key. The branch to which * a child is attached (left or right) is defined accordingly. */ template class RefTrieNode { public: typedef IPNet
Key; typedef typename MiniTraits::NonConst PPayload; /** * Constructors */ RefTrieNode() : _up(0), _left(0), _right(0), _k(Key()), _p(0), _references(0){} RefTrieNode(const Key& key, const Payload& p, RefTrieNode* up = 0) : _up(up), _left(0), _right(0), _k(key), _p(new PPayload(p)), _references(0) {} RefTrieNode(const Key& key, RefTrieNode* up = 0) : _up(up), _left(0), _right(0), _k(key), _p(0), _references(0) {} ~RefTrieNode() { // assert that the node has been deleted and it's reference // count is zero XLOG_ASSERT((_references&(NODE_DELETED|NODE_REFS_MASK)) == NODE_DELETED); if (_p) delete_payload(_p); } /** * add a node to a subtree * @return a pointer to the node. */ static RefTrieNode *insert(RefTrieNode **root, const Key& key, const Payload& p, bool& replaced); /** * erase current node, replumb. Returns the new root. */ RefTrieNode *erase(); /** * main search routine. Given a key, returns a node. */ RefTrieNode *find(const Key& key) ; const RefTrieNode *const_find(const Key& key) const { return const_cast(this)->find(key); } /** * aux search routine. * Given a key, returns a subtree contained in the key, irrespective * of the presence of a payload in the node. */ RefTrieNode *find_subtree(const Key &key); /** * Given a key, find the node with that key and a payload. * If the next doesn't exist or does not have a payload, find * the next node in the iterator sequence. XXX check the description. */ RefTrieNode* lower_bound(const Key &key); RefTrieNode* get_left() { return this->_left; } RefTrieNode* get_right() { return this->_right; } RefTrieNode* get_parent() { return this->_up; } bool has_payload() const { return _p != NULL; } bool has_active_payload() const { return ((_p != NULL) && ((_references & NODE_DELETED) == 0)); } const Payload &const_p() const { return *_p; } Payload &p() { return *_p; } void set_payload(const Payload& p) { if (_p) delete_payload(_p); _p = new PPayload(p); // clear the DELETED flag _references ^= _references & NODE_DELETED; } uint32_t references() const { return _references & NODE_REFS_MASK; } void incr_refcount() { XLOG_ASSERT((_references & NODE_REFS_MASK) != NODE_REFS_MASK); _references++; // printf("++ _references = %d\n", _references & NODE_REFS_MASK); } void decr_refcount() { XLOG_ASSERT((_references & NODE_REFS_MASK) > 0); _references--; // printf("-- _references = %d\n", _references & NODE_REFS_MASK); } bool deleted() const { return ((_references & NODE_DELETED) != 0); } const Key &k() const { return _k; } void print(int indent, const char *msg) const; string str() const; /** * helper function to delete an entire subtree (including the * root). */ void delete_subtree() { if (_left) _left->delete_subtree(); if (_right) _right->delete_subtree(); // keep the destructor happy _references = NODE_DELETED; delete this; /* and we are gone too */ } /** * debugging, validates a node by checking pointers and Key invariants. */ void validate(const RefTrieNode *parent) const { UNUSED(parent); #ifdef VALIDATE_XORP_TRIE if (_up != parent) { fprintf(stderr, "bad parent _up %x vs %x", (int)_up, (int)parent); abort(); } if (_up && _k.contains(_up->_k)) { fprintf(stderr, "bad subnet order"); abort(); } if (_p == NULL && (!_left || !_right)) { fprintf(stderr, "useless internal node"); abort(); } if (_left) _left->validate(this); if (_right) _right->validate(this); #endif } /** * @return the leftmost node under this node */ bool is_left() const { return _up && this == _up->_left; } RefTrieNode *leftmost() { RefTrieNode *n = this; while (n->_left || n->_right) n = (n->_left ? n->_left : n->_right); return n; } /** * @return the boundaries ("lo" and "hi") of the largest range that * contains 'a' and maps to the same route entry. * * Algorithm: *
     *		n = find(a);
     * 		if we have no route (hence no default), provide a fake 0/0;
     *		set lo and hi to the boundaries of the current node.
     *
     * if n.is_a_leaf() we are done (results are the extremes of the entry)
     * Otherwise: we are in an intermediate node, and a can be in positions
     * 1..5 if the node has 2 children, or 1'..3' if it has 1 child.
     *
     *	n:		|---------------.----------------|
     *  a:                1    2        3      4     5
     *                       |--X--|         |--Y--|
     *
     *  a:                1'    2'        3'
     *                       |--X--|
     *
     * Behaviour is the following:
     *  case 1 and 1':	lo already set, hi = (lowest address in X)-1
     *  case 2 and 2': set n = X and repeat
     *  case 3: lo = (highest addr in X)+1, hi = (lowest addr in Y)-1
     *  case 3': lo = (highest addr in X)+1, hi is already set
     *  case 4: set n = Y and repeat
     *  case 5:	lo = (highest addr in Y)+1, hi is already set
     * 
*/ void find_bounds(const A& a, A &lo, A &hi) const { RefTrieNode def = RefTrieNode(); const RefTrieNode *n = const_find(Key(a, a.addr_bitlen())); if (n == NULL) { // create a fake default entry def._left = const_cast(this); def._right = NULL; n = &def; } lo = n->_k.masked_addr(); hi = n->_k.top_addr(); for (const RefTrieNode *prev = NULL; prev != n;) { prev = n; RefTrieNode *x = (n->_left ? n->_left : n->_right); if (x == NULL) break; if (a < x->_k.masked_addr()) { // case 1 and 1' hi = x->low(); --hi; } else if (a <= x->_k.top_addr()) { // case 2 and 2' n = x; // and continue } else if (n->_left == NULL || n->_right == NULL) { // case 3' lo = x->high(); ++lo; } else if (a < n->_right->_k.masked_addr()) { // case 3 lo = x->high(); ++lo; hi = n->_right->low(); --hi; } else if (a <= n->_right->_k.top_addr()) { // case 4: n = n->_right; // and continue } else { // case 5: lo = n->_right->high(); ++lo; } } // ensure destructor sanity check is happy def._references |= NODE_DELETED; } /** * @return the lowest address in a subtree which has a route. * Search starting from left or right until a full node is found. */ A low() const { const RefTrieNode *n = this; while (!(n->has_active_payload()) && (n->_left || n->_right)) n = (n->_left ? n->_left : n->_right); return n->_k.masked_addr(); } /** * @return the highest address in a subtree which has a route. * Search starting from right or left until a full node is found. */ A high() const { const RefTrieNode *n = this; while (!(n->has_active_payload()) && (n->_right || n->_left)) n = (n->_right ? n->_right : n->_left); return n->_k.top_addr(); } private: /* delete_payload is a separate method to allow specialization */ void delete_payload(Payload* p) { delete p; } void dump(const char *msg) const { #if 0 debug_msg(" %s %s %s\n", msg, _k.str().c_str(), _p ? "PL" : "[]"); debug_msg(" U %s\n", _up ? _up->_k.str().c_str() : "NULL"); debug_msg(" L %s\n", _left ? _left->_k.str().c_str() : "NULL"); debug_msg(" R %s\n", _right ? _right->_k.str().c_str() : "NULL"); #endif UNUSED(msg); } RefTrieNode *_up, *_left, *_right; Key _k; PPayload *_p; uint32_t _references; }; /** * Postorder Iterator on a trie. * * _cur points to the current object, _root contains the search key for * root of the subtree we want to scan. The iterator skips over empty * nodes, and visits the subtree in depth-first, left-to-right order. * The keys returned by this iterator are not sorted by prefix length. */ template class RefTriePostOrderIterator { public: typedef IPNet
Key; typedef ::RefTrie RefTrie; typedef RefTrieNode Node; /** * Constructors */ RefTriePostOrderIterator() { _cur = NULL; _trie = NULL; } /** * constructor for exact searches: both the current node and the search * key are taken from n, so the iterator will only loop once. */ RefTriePostOrderIterator(const RefTrie* trie, Node *n) { _trie = trie; _cur = n; if (_cur) { _cur->incr_refcount(); _root = n->k(); } } /** * construct for subtree scanning: the root key is set explicitly, * and the current node is set according to the search order. */ RefTriePostOrderIterator(const RefTrie* trie, Node *n, const Key &k) { _trie = trie; _root = k; _cur = n; if (_cur) { begin(); _cur->incr_refcount(); } } RefTriePostOrderIterator(const RefTriePostOrderIterator& x) { // printf("copy constructor , new node: %p\n", x._cur); _trie = x._trie; _cur = x._cur; _root = x._root; if (_cur) _cur->incr_refcount(); } ~RefTriePostOrderIterator() { if (_cur) { _cur->decr_refcount(); if (_cur->deleted() && _cur->references() == 0) { // XXX uglyness alert. // printf("erasing node %p from iterator destructor\n", _cur); const_cast(_trie)-> set_root(_cur->erase()); } } } /** * move to the starting position according to the visiting order */ RefTriePostOrderIterator * begin() { Node * n = _cur; while (n->get_parent() && _root.contains(n->get_parent()->k())) n = n->get_parent(); _cur = n->leftmost(); return this; } /** * Postfix increment * * Updates position of iterator in tree. * @return position of iterator before increment. */ RefTriePostOrderIterator operator ++(int) { // printf("postfix iterator: node was %p\n", _cur); RefTriePostOrderIterator x = *this; next(); return x; } /** * Prefix increment * * Updates position of iterator in tree. * @return position of iterator after increment. */ RefTriePostOrderIterator& operator ++() { next(); return *this; } /** * Conversion operator * * Converts into a PreOrderIterator */ operator RefTriePreOrderIterator() const { return RefTriePreOrderIterator(_trie, _cur, _root); } void next() const { Node * oldnode = _cur; do { if (_cur->get_parent() == NULL) { _cur = NULL; break; // cannot backtrack, finished } bool was_left_child = node_is_left(_cur); _cur = _cur->get_parent(); // backtrack one level, then explore the leftmost path // on the right branch if not done already. if (was_left_child && _cur->get_right()) { _cur = _cur->get_right()->leftmost(); } if (_root.contains(_cur->k()) == false) { _cur = NULL; break; } } while (_cur->has_payload() == false); // found a good node. if (_cur) _cur->incr_refcount(); // cleanup if this reduces the reference count on a deleted // node to zero if (oldnode) { oldnode->decr_refcount(); if (oldnode->deleted() && oldnode->references() == 0) { // XXX uglyness alert. // printf("erasing node %p from prefix increment\n", oldnode); const_cast(_trie)->set_root(oldnode->erase()); } } } void force_valid() const { while (_cur && _cur->deleted()) next(); } Node *cur() const { return _cur; } bool operator==(const RefTriePostOrderIterator & x) const { force_valid(); x.force_valid(); return (_cur == x._cur); } bool operator!=(const RefTriePostOrderIterator & x) const { force_valid(); x.force_valid(); return (_cur != x._cur); } Payload & payload() { /* Usage note: the node the iterator points at can be deleted. If there is any possibility that the node might have been deleted since the iterator was last examined, the user should compare this iterator with end() to force the iterator to move to the next undeleted node or move to end() if there is no undeleted node after the node that was deleted. */ /* Implementation node: We could have put a call to force_valid here, but the force_valid can move the iterator to the end of the trie, which would cause _cur to become NULL. Then we'd have to assert here anyway. Doing it this way makes it more likely that a failure to check will be noticed early */ XLOG_ASSERT(!_cur->deleted()); return _cur->p(); }; const Key & key() const { /* see payload() for usage note*/ XLOG_ASSERT(!_cur->deleted()); return _cur->k(); }; RefTriePostOrderIterator& operator=(const RefTriePostOrderIterator& x) { // printf("operator= , old node: %p new node: %p\n", _cur, x._cur); Node *oldnode = _cur; _cur = x._cur; _root = x._root; // need to increment before decrement, as the decrement might // cause deletion, which would be bad if the old Node was the // same as the new Node. if (_cur) _cur->incr_refcount(); if (oldnode) { oldnode->decr_refcount(); if (oldnode->deleted() && oldnode->references() == 0) { // XXX uglyness alert. // printf("erasing node %p from prefix increment\n", oldnode); const_cast(_trie)-> set_root(oldnode->erase()); } } _trie = x._trie; return *this; } private: friend class RefTriePreOrderIterator; mutable Node *_cur; /*this is mutable because const methods can cause the iterator to move from a deleted node to the first non-deleted node or end. */ bool node_is_left(Node* n) const { return n->get_parent() && n == n->get_parent()->get_left(); } Key _root; const RefTrie* _trie; }; /* * Preorder Iterator on a trie. * * _cur points to the current object, _root contains the search key for * root of the subtree we want to scan. The iterator does preorder traversal, * that is, current node first, then left then right. This guarantees that * keys returned are sorted by prefix length. */ template class RefTriePreOrderIterator { public: typedef IPNet Key; typedef ::RefTrie RefTrie; typedef RefTrieNode Node; /** * Constructors */ RefTriePreOrderIterator() { _cur = NULL; _trie = NULL; } /** * constructor for exact searches: both the current node and the search * key are taken from n, so the iterator will only loop once. */ explicit RefTriePreOrderIterator(const RefTrie* trie, Node *n) { _trie = trie; _cur = n; if (_cur) { _cur->incr_refcount(); _root = n->k(); } } /** * construct for subtree scanning: the root key is set explicitly, * and the current node is set according to the search order. */ RefTriePreOrderIterator(const RefTrie* trie, Node *n, const Key &k) { _trie = trie; _root = k; _cur = n; if (_cur) { begin(); _cur->incr_refcount(); } } RefTriePreOrderIterator(const RefTriePreOrderIterator& x) { // printf("copy constructor , new node: %p\n", x._cur); _trie = x._trie; _cur = x._cur; _root = x._root; if (_cur) _cur->incr_refcount(); } ~RefTriePreOrderIterator() { if (_cur) { _cur->decr_refcount(); if (_cur->deleted() && _cur->references() == 0) { // XXX uglyness alert. // printf("erasing node %p from iterator destructor\n", _cur); const_cast(_trie)-> set_root(_cur->erase()); } } } /** * move to the starting position according to the visiting order */ RefTriePreOrderIterator * begin() { while (_cur->get_parent() && _root.contains(_cur->get_parent()->k())) _cur = _cur->get_parent(); return this; } /** * Postfix increment * * Updates position of iterator in tree. * @return position of iterator before increment. */ RefTriePreOrderIterator operator ++(int) { RefTriePreOrderIterator x = *this; next(); return x; } /** * Prefix increment * * Updates position of iterator in tree. * @return position of iterator after increment. */ RefTriePreOrderIterator& operator ++() { next(); return *this; } /** * Conversion operator * * Converts into a PostOrderIterator */ operator RefTriePostOrderIterator() const { return RefTriePostOrderIterator(_trie, _cur, _root); } void next() const { Node * oldnode = _cur; if (_cur == NULL) return; do { if (_cur->get_left()) _cur=_cur->get_left(); else { bool was_right_child = node_is_right(_cur); while (!(_cur->get_right() && !was_right_child) && _cur->get_parent() && _root.contains(_cur->get_parent()->k())) { was_right_child = node_is_right(_cur); _cur = _cur->get_parent(); } if (!was_right_child && _cur->get_right()) _cur = _cur->get_right(); else { _cur = NULL; break; } } } while (_cur->has_payload() == false); // found a good node. if (_cur) _cur->incr_refcount(); // cleanup if this reduces the reference count on a deleted // node to zero if (oldnode) { oldnode->decr_refcount(); if (oldnode->deleted() && oldnode->references() == 0) { // XXX uglyness alert. // printf("erasing node %p from prefix increment\n", oldnode); const_cast(_trie)->set_root(oldnode->erase()); } } } void force_valid() const { while (_cur && _cur->deleted()) next(); } Node *cur() const { return _cur; } bool operator==(const RefTriePreOrderIterator & x) const { force_valid(); x.force_valid(); return (_cur == x._cur); } bool operator!=(const RefTriePreOrderIterator & x) const { force_valid(); x.force_valid(); return (_cur != x._cur); } Payload & payload() { /* Usage note: the node the iterator points at can be deleted. If there is any possibility that the node might have been deleted since the iterator was last examined, the user should compare this iterator with end() to force the iterator to move to the next undeleted node or move to end() if there is no undeleted node after the node that was deleted. */ /* Implementation node: We could have put a call to force_valid here, but the force_valid can move the iterator to the end of the trie, which would cause _cur to become NULL. Then we'd have to assert here anyway. Doing it this way makes it more likely that a failure to check will be noticed early */ XLOG_ASSERT(!_cur->deleted()); return _cur->p(); }; const Key & key() const { /* see payload() for usage note*/ XLOG_ASSERT(!_cur->deleted()); return _cur->k(); }; RefTriePreOrderIterator& operator=(const RefTriePreOrderIterator& x) { // printf("operator= , old node: %p new node: %p\n", _cur, x._cur); Node *oldnode = _cur; _cur = x._cur; _root = x._root; // need to increment before decrement, as the decrement might // cause deleetion, which would be bad if the old Node was the // same as the new Node. if (_cur) _cur->incr_refcount(); if (oldnode) { oldnode->decr_refcount(); if (oldnode->deleted() && oldnode->references() == 0) { // XXX uglyness alert. // printf("erasing node %p from iter assignment\n", oldnode); const_cast(_trie)->set_root(oldnode->erase()); } } _trie = x._trie; return *this; } private: friend class RefTriePostOrderIterator; mutable Node* _cur; /*this is mutable because const methods can cause the iterator to move from a deleted node to the first non-deleted node or end. */ bool node_is_right(Node* n) const { return n->get_parent() && n == n->get_parent()->get_right(); } Key _root; const RefTrie* _trie; }; /** * The RefTrie itself * * The trie support insertion and deletion of Key,Payload pairs, * and lookup by Key (which can be an address or a subnet). * * Additional methods are supported to provide access via iterators. */ template class RefTrie { public: typedef IPNet Key; typedef RefTrieNode Node; typedef RefTriePostOrderIterator iterator; typedef RefTriePostOrderIterator PostOrderIterator; typedef RefTriePreOrderIterator PreOrderIterator; /** * stl map interface */ RefTrie() : _root(0), _payload_count(0), _deleted(false) {} virtual ~RefTrie() { delete_all_nodes(); } void delete_self() { _deleted = true; if (_root == NULL) delete this; } void set_root(Node *root) { _root = root; if (_deleted) { delete this; } } /** * insert a key, payload pair, returns an iterator * to the newly inserted node. * Prints a warning message if the new entry overwrites an * existing full node. */ iterator insert(const Key & net, const Payload& p) { bool replaced = false; Node *out = Node::insert(&_root, net, p, replaced); if (replaced) { fprintf(stderr, "overwriting a full node"); // XXX fprintf(stderr, "net %s\n", net.str().c_str()); } else { _payload_count++; } #if 0 printf("out->references: %d\n", out->references()); if (out->deleted()) printf("node is deleted\n"); #endif return iterator(this, out); } /** * delete the node with the given key. */ void erase(const Key &k) { erase(find(k)); } /** * delete the node pointed by the iterator. */ void erase(iterator i) { if (_root && i.cur() && i.cur()->has_active_payload()) { _payload_count--; // printf("explicit erase on node %p\n", i.cur()); _root = const_cast(i.cur())->erase(); // XXX should invalidate i ? } } /** * given a key, returns an iterator to the entry with the * longest matching prefix. */ iterator find(const Key &k) const { return iterator(this, _root->find(k)); } /** * given an address, returns an iterator to the entry with the * longest matching prefix. */ iterator find(const A& a) const { return find(Key(a, a.addr_bitlen())); } iterator lower_bound(const Key &k) const { #ifdef NOTDEF iterator i = lookup_node(k); if (i != end()) return i; #endif return iterator(this, _root->lower_bound(k)); } iterator begin() const { return iterator(this, _root, IPNet()); } const iterator end() const { return iterator(const_cast(this), 0); } void delete_all_nodes() { if (_root) _root->delete_subtree(); _root = NULL; _payload_count = 0; } /** * lookup a subnet, must return exact match if found, end() if not. * */ iterator lookup_node(const Key & k) const { Node *n = _root->find(k); return (n && n->k() == k) ? iterator(this, n) : end(); } /** * returns an iterator to the subtree rooted at or below * the key passed as parameter. */ iterator search_subtree(const Key &key) const { return iterator(this, _root->find_subtree(key), key); } /** * find_less_specific asks the question: if I were to add this * net to the trie, what would be its parent node? * net may or may not already be in the trie. * Implemented as a find() with a less specific key. */ iterator find_less_specific(const Key &key) const { // there are no less specific routes than the default route if(key.prefix_len() == 0) return end(); Key x(key.masked_addr(), key.prefix_len() - 1); return iterator(this, _root->find(x)); } /** * return the lower and higher address in the range that contains a * and would map to the same route. */ void find_bounds(const A& a, A &lo, A &hi) const { _root->find_bounds(a, lo, hi); } #if 0 // compatibility stuff, has to go /* * return the lower and higher address in the range that contains a * and would map to the same route. */ A find_lower_bound(const A a) const { A lo, hi; _root->find_bounds(a, lo, hi); return lo; } A find_higher_bound(const A a) const { A lo, hi; _root->find_bounds(a, lo, hi); return hi; } #endif // compatibility int route_count() const { return _payload_count; } void print() const; string str() const; private: void validate() { if (_root) _root->validate(NULL); } Node *_root; int _payload_count; /** * RefTrie's nodes are reference counted, so it's possible to * delete all the nodes in the trie, and for the actually routes * to remain around until their reference counts go to zero * because iterators still point at a node. If you then delete * the trie itself, the nodes will be deleted but the iterator * will be invalidated. Instead delete_self() should be called, * which sets _deleted, and only finally deletes the trie when all * the nodes themselves have gone away. */ bool _deleted; }; /** * add subnet/payload to the tree at *root. * * @return a pointer to the newly inserted node. */ template RefTrieNode * RefTrieNode::insert(RefTrieNode **root, const Key& x, const Payload& p, bool& replaced) { /* * Loop until done in the following: * * If *root == NULL, create a new RefTrieNode containing x and we are DONE. * Otherwise consider the possible cases of overlaps between the subnets * in *root (call it y) and x (+ indicates the middle of the interval): * * y = (*root) .|===+===| * * x 0 .|---+---| * x A |--| . . * x B . . |--| * x C . |-|. * x D . .|-| * x E |----------+----------| * x F |----------+-----------| * * case 0: Same subnet. Store payload if *root if empty, replace otherwise. * case A: allocate a new empty root, make old *root the right child, * make a new node with x the left child. DONE. * case B: allocate a new empty root, make old *root the left child, * make a new node with x the right child. DONE. * case C: repeat with root = &((*root)->left) * case D: repeat with root = &((*root)->right) * case E: *root = new node with x, old *root the right child, DONE. * case F: *root = new node with x, old *root the left child, DONE. * * In all case, when we exit the loop, newroot contains the new value to * be assigned to *root; */ RefTrieNode **oldroot = root; // do we need it ? RefTrieNode *newroot = NULL, *parent = NULL, *me = NULL; debug_msg("++ insert %s\n", x.str().c_str()); for (;;) { newroot = *root; if (newroot == NULL) { me = newroot = new RefTrieNode(x, p, parent); break; } parent = newroot->_up; Key y = newroot->_k; if (x == y) { /* case 0 */ debug_msg("case 0\n"); replaced = newroot->has_payload() & (!newroot->deleted()); newroot->set_payload(p); me = newroot; break; } // boundaries of x and y, and their midpoints. A x_m = x.masked_addr() | ( ~(x.netmask()) >> 1 ); A y_m = y.masked_addr() | ( ~(y.netmask()) >> 1 ); A x_l = x.masked_addr(); A x_h = x.top_addr(); A y_l = y.masked_addr(); A y_h = y.top_addr(); if (x_h < y_l) { /* case A */ debug_msg("case A: |--x--| |--y--|\n"); Key k = Key::common_subnet(x, y); newroot = new RefTrieNode(k, parent); // create new root newroot->_right = *root; // old root goes right newroot->_right->_up = newroot; newroot->_left = me = new RefTrieNode(x, p, newroot); break; } else if (y_h < x_l) { /* case B */ debug_msg("case B: |--y--| |--x--|\n"); Key k = Key::common_subnet(x, y); newroot = new RefTrieNode(k, parent); // create new root newroot->_left = *root; newroot->_left->_up = newroot; newroot->_right = me = new RefTrieNode(x, p, newroot); break; } else if (x_l >= y_l && x_h <= y_m) { /* case C */ debug_msg("case C: |--x-.----|\n"); parent = *root; root = &(newroot->_left); } else if (x_l > y_m && x_h <= y_h) { /* case D */ debug_msg("case D: |----.-x--|\n"); parent = *root; root = &(newroot->_right); } else if (y_l > x_m && y_h <= x_h) { /* case E */ debug_msg("case E: |----.-Y--|\n"); newroot = me = new RefTrieNode(x, p, parent); newroot->_right = *root; newroot->_right->_up = newroot; break; } else if (y_l >= x_l && y_h <= x_m) { /* case F */ debug_msg("case F: |--Y-.----|\n"); newroot = me = new RefTrieNode(x, p, parent); newroot->_left = *root; newroot->_left->_up = newroot; break; } else abort(); // impossible case in RefTrieNode::insert() } *root = newroot; if (*oldroot) (*oldroot)->validate(NULL); // (*oldroot)->print(0, ""); return me; } /** * Remove this node, cleanup useless internal nodes. * * @return a pointer to the root of the trie. */ template RefTrieNode * RefTrieNode::erase() { RefTrieNode *me, *parent, *child; if ((_references & NODE_REFS_MASK) > 0) { _references |= NODE_DELETED; me = this; } else { _references |= NODE_DELETED; if (_p) { delete_payload(_p); _p = NULL; } debug_msg("++ erase %s\n", this->_k.str().c_str()); /* * If the node ("me") exists, has no payload and at most one child, * then it is a useless internal node which needs to be removed by * linking the child to the parent. If the child is NULL, we need * to repeat the process up. */ for (me = this; me && me->_p == NULL && (me->_left == NULL || me->_right == NULL); ) { // me->dump("erase"); // debugging // Find parent and the one possible child (both can be NULL). parent = me->_up; child = me->_left ? me->_left : me->_right; if (child != NULL) // if the child exists, link it to child->_up = parent; // its new parent if (parent == NULL) // no parent, child becomes new root parent = child; else { // i have a parent, link my child to it (left or right) if (parent->_left == me) parent->_left = child; else parent->_right = child; } // if we're deleting an intermediate node, mark it as // deleted to satisfy destructor sanity checks if (me->has_payload() == false) me->_references |= NODE_DELETED; delete me; // nuke the node me = parent; } } // now navigate up to find and return the new root of the trie for ( ; me && me->_up ; me = me->_up) ; return me; } /** * Finds the most specific entry in the subtree rooted at r * that contains the desired key and has a Payload */ template RefTrieNode * RefTrieNode::find(const Key &key) { RefTrieNode * cand = NULL; RefTrieNode * r = this; for ( ; r && r->_k.contains(key) ; ) { if (r->_p && !r->deleted()) cand = r; // we have a candidate. if (r->_left && r->_left->_k.contains(key)) r = r->_left; else // should check that right contains(key), but r = r->_right; // the loop condition will do it for us. } return cand; } /** * See the comment in the class definition. */ template RefTrieNode * RefTrieNode::lower_bound(const Key &key) { RefTrieNode * cand = NULL; RefTrieNode * r = this; // printf("lower bound: %s\n", key.str().c_str()); for ( ; r && r->_k.contains(key) ; ) { cand = r; // any node is good, irrespective of payload. if (r->_left && r->_left->_k.contains(key)) r = r->_left; else // should check that right contains(key), but r = r->_right; // the loop condition will do it for us. } if (cand == NULL) cand = this; if (cand->_k == key) { // we found an exact match if (cand->_p) { // we also have a payload, so we are done. // printf("exact match\n"); return cand; } else { // no payload, skip to the next (in postorder) // node in the entire tree (null key as root) RefTriePostOrderIterator iterator(NULL, cand, Key()); ++iterator; return iterator.cur(); } } // printf("no exact match\n"); // No exact match exists. // cand holds what would be the parent of the node, if it existed. while (cand != NULL) { // printf("cand = %s\n", cand->str().c_str()); if (cand->_left && (key < cand->_left->_k)) { return cand->_left->leftmost(); } if (cand->_right && (key < cand->_right->_k)) { return cand->_right->leftmost(); } cand = cand->_up; } return NULL; } /** * Finds the subtree of key. */ template RefTrieNode * RefTrieNode::find_subtree(const Key &key) { RefTrieNode *r = this; RefTrieNode *cand = r && key.contains(r->_k) ? r : NULL; for ( ; r && r->_k.contains(key) ; ) { cand = r; // we have a candidate. if (r->_left && r->_left->_k.contains(key)) r = r->_left; else // should check that right contains(key), but r = r->_right; // the loop condition will do it for us. } return cand; } template void RefTrieNode::print(int indent, const char *msg) const { #ifdef DEBUG_LOGGING debug_msg_indent(indent); if (this == NULL) { debug_msg("%sNULL\n", msg); return; } debug_msg("%skey: %s %s\n", msg, _k.str().c_str(), _p ? "PL" : "[]"); debug_msg(" U: %s\n", _up ? _up->_k.str().c_str() : "NULL"); _left->print(indent+4, "L: "); _right->print(indent+4, "R: "); debug_msg_indent(0); #endif /* DEBUG_LOGGING */ UNUSED(indent); UNUSED(msg); } template string RefTrieNode::str() const { string s; if (this == NULL) { s = "NULL"; return s; } s = c_format("key: %s ", _k.str().c_str()); if (_p) s += "PL"; else s += "[]"; if ((_references & NODE_DELETED) != 0) s += " *DEL*"; s += c_format("\n U: %s\n", _up ? _up->_k.str().c_str() : "NULL"); return s; } template void RefTrie::print() const { // this is called print - it should NOT use debug_msg!!! printf("---- print trie ---\n"); _root->print(0, ""); iterator ti; for (ti = begin() ; ti != end() ; ti++) { printf("*** node: %-26s ", ti.cur()->k().str().c_str()); if (ti.cur()->has_active_payload()) printf("PL\n"); else if (ti.cur()->has_payload()) printf("PL *DELETED* (%u refs)\n", XORP_UINT_CAST(ti.cur()->references())); else printf("[]\n"); } printf("---------------\n"); } template string RefTrie::str() const { string s = _root->str(); iterator ti; for (ti = begin() ; ti != end() ; ti++) { s += c_format("*** node: %-26s ", ti.cur()->k().str().c_str()); if (ti.cur()->has_active_payload()) s += "PL\n"; else if (ti.cur()->has_payload()) s += c_format("PL *DELETED* (%u refs)\n", XORP_UINT_CAST(ti.cur()->references())); else s += "[]\n"; } s += ("---------------\n"); return s; } #endif // __LIBXORP_REF_TRIE_HH__ xorp/libxorp/task.cc0000664000076400007640000001430111421137511014603 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8 sw=4: // Copyright (c) 2006-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "libxorp_module.h" #include "xorp.h" #include "xlog.h" #include "task.hh" // ---------------------------------------------------------------------------- // TaskNode methods TaskNode::TaskNode(TaskList* task_list, BasicTaskCallback cb) : _task_list(task_list), _cb(cb), _ref_cnt(0), _priority(0), _weight(0) { debug_msg("TaskNode constructor %p\n", this); } TaskNode::~TaskNode() { debug_msg("TaskNode destructor %p\n", this); unschedule(); } void TaskNode::add_ref() { _ref_cnt++; debug_msg("add_ref on %p, now ref_cnt = %d\n", this, _ref_cnt); } void TaskNode::release_ref() { if (--_ref_cnt <= 0) delete this; } void TaskNode::schedule(int priority, int weight) { debug_msg("TaskNode schedule %p p = %d, w = %d\n", this, priority, weight); XLOG_ASSERT(_task_list != NULL); unschedule(); _priority = priority; _weight = weight; _task_list->schedule_node(this); } void TaskNode::reschedule() { XLOG_ASSERT(_task_list != NULL); unschedule(); _task_list->schedule_node(this); } void TaskNode::unschedule() { if (scheduled()) _task_list->unschedule_node(this); } // ---------------------------------------------------------------------------- // Specialized Tasks. These are what the XorpTask objects returned by // the TaskList XorpTask creation methods (e.g. TaskList::new_oneoff_at(), etc) // actually refer to. class OneoffTaskNode2 : public TaskNode { public: OneoffTaskNode2(TaskList* task_list, const OneoffTaskCallback& cb) : TaskNode(task_list, callback(this, &OneoffTaskNode2::run)), _cb(cb) {} private: void run(XorpTask& xorp_task) { debug_msg("OneoffTaskNode2::run() %p\n", this); // // XXX: we have to unschedule before the callback dispatch, in case // the callback decides to schedules again the task. // xorp_task.unschedule(); _cb->dispatch(); } OneoffTaskCallback _cb; }; class RepeatedTaskNode2 : public TaskNode { public: RepeatedTaskNode2(TaskList* task_list, const RepeatedTaskCallback& cb) : TaskNode(task_list, callback(this, &RepeatedTaskNode2::run)), _cb(cb) {} private: void run(XorpTask& xorp_task) { if (! _cb->dispatch()) { xorp_task.unschedule(); } } RepeatedTaskCallback _cb; }; // ---------------------------------------------------------------------------- // XorpTask void XorpTask::unschedule() { if (_task_node != NULL) _task_node->unschedule(); } bool XorpTask::scheduled() const { if (_task_node != NULL) return _task_node->scheduled(); else return false; } // ---------------------------------------------------------------------------- // TaskList TaskList::~TaskList() { #ifdef notyet // Attempting to plug the leak causes problems elsewhere. while (! _rr_list.empty()) { map::iterator ii = _rr_list.begin(); delete (*ii).second; _rr_list.erase(ii); } #endif } XorpTask TaskList::new_oneoff_task(const OneoffTaskCallback& cb, int priority, int weight) { debug_msg("new oneoff task %p p = %d, w = %d\n", this, priority, weight); TaskNode* task_node = new OneoffTaskNode2(this, cb); task_node->schedule(priority, weight); return XorpTask(task_node); } XorpTask TaskList::new_task(const RepeatedTaskCallback& cb, int priority, int weight) { debug_msg("new task %p p = %d, w = %d\n", this, priority, weight); TaskNode* task_node = new RepeatedTaskNode2(this, cb); task_node->schedule(priority, weight); return XorpTask(task_node); } int TaskList::get_runnable_priority() const { map::const_iterator rri; for (rri = _rr_list.begin(); rri != _rr_list.end(); ++rri) { if (rri->second->size() != 0) { return rri->first; } } return XorpTask::PRIORITY_INFINITY; } bool TaskList::empty() const { bool result = true; map::const_iterator rri; for (rri = _rr_list.begin(); rri != _rr_list.end(); ++rri) { if (rri->second->size() != 0) { result = false; break; } } return result; } void TaskList::run() { map::const_iterator rri; debug_msg("TaskList run()\n"); for (rri = _rr_list.begin(); rri != _rr_list.end(); ++rri) { RoundRobinQueue* rr = rri->second; if (rr->size() != 0) { TaskNode* task_node = static_cast(rr->get_next_entry()); debug_msg("node to run: %p\n", task_node); XorpTask xorp_task(task_node); task_node->run(xorp_task); return; } } } RoundRobinQueue* TaskList::find_round_robin(int priority) { map::iterator rri = _rr_list.find(priority); if (rri == _rr_list.end()) { RoundRobinQueue* rr = new RoundRobinQueue(); _rr_list[priority] = rr; return rr; } else { return rri->second; } } void TaskList::schedule_node(TaskNode* task_node) { debug_msg("TaskList::schedule_node: n = %p\n", task_node); RoundRobinObjBase* obj = static_cast(task_node); RoundRobinQueue* rr = find_round_robin(task_node->priority()); rr->push(obj, task_node->weight()); } void TaskList::unschedule_node(TaskNode* task_node) { debug_msg("TaskList::unschedule_node: n = %p\n", task_node); RoundRobinObjBase* obj = static_cast(task_node); RoundRobinQueue* rr = find_round_robin(task_node->priority()); rr->pop_obj(obj); } xorp/libxorp/timeval.cc0000664000076400007640000000306611631741244015317 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "timeval.hh" #include "c_format.hh" string TimeVal::str() const { return c_format("%d.%06d", XORP_INT_CAST(_sec), XORP_INT_CAST(_usec)); } int64_t TimeVal::to_ms() const { int64_t ms = _usec / 1000; // Round a truncated fraction of <1ms to 1ms, not zero. if (_sec == 0 && ms == 0 && _usec != 0) ms = 1; else { ms += (int64_t)(_sec) * 1000LL; } return (ms); } void TimeVal::set_ms(int64_t ms) { if (ms == 0) { _sec = _usec = 0; } else { _sec = ms / 1000; _usec = (ms % 1000) * 1000; } } string TimeVal::pretty_print() const { time_t t = static_cast(_sec); return c_format("%.24s", asctime(localtime(&t))); } xorp/libxorp/callback-gen.py0000664000076400007640000005715611421137511016226 0ustar greearbgreearb""" C++ generic callback template generator Notes: This program generates types and methods for creating and invoking callbacks in C++ with arbitrary bound arguments and late arguments passed in. At present the generated code assumes that returning void is permitted by the compiler. Some additional hacking will be needed to support the specializations necessary when this is not the case, but on our main build system this is not an issue. The notion of having a script to generate templates was borrowed from David Mazieres callback.h file (in the async lib distributed with sfs). The code generated bears strong similarities to David's work, but was largely written independently of it. """ import getopt, sys, string def aname(type): """Return arg name derived from type""" return type.lower() def decl_arg(t): return "%s %s" % (t, aname(t)) def decl_args(types): return map(decl_arg, types) def mem_arg(t): return "_%s" % aname(t) def mem_args(types): return map(mem_arg, types) def mem_decl(t): return "%s %s" % (t, mem_arg(t)) def mem_decls(types): return map(mem_decl, types) def cons_arg(t): return "%s(%s)" % (mem_arg(t), aname(t)) def cons_args(types): return map(cons_arg, types) def call_args(types): return map(aname, types) def class_arg(t): return "class %s" % t def class_args(types): return map(class_arg, types) def first_arg(args): return args[0] def first_args(tuple_list): return map(first_arg, tuple_list) def second_arg(args): return args[1] def second_args(tuple_list): return map(second_arg, tuple_list) def flatten_pair(p): return p[0] + " " + p[1] def flatten_pair_list(tuple_list): return map(flatten_pair, tuple_list) def csv(l, comma = ", "): """ Transform list of strings into a comma separated values string """ s = '' n = len(l) if (n >= 1): s = "%s" % l[0] for i in range(1,n): s += "%s%s" % (comma, l[i]) return s; def joining_csv(l, comma = ", "): """ Transform list of strings into a comma separated values string suitable for appending to an existing comma separated value string. """ s = '' n = len(l) for i in range(0,n): s += "%s%s" % (comma, l[i]) return s; def starting_csv(l, comma = ", "): """ Starts a comma separated list. Last element is follow by comma on the assumption that the list will continue after l. """ s = '' n = len(l) for i in range(0,n): s += "%s%s" % (l[i], comma) return s; def output_header(args, dbg): from time import time, localtime, strftime print \ """/* * Copyright (c) 2001-2009 XORP, Inc. * See the XORP LICENSE.lgpl file for licensing, conditions, and warranties * on use. * * This file is PROGRAMMATICALLY GENERATED. *""" print " * This instance was generated with:\n * ", ls = args[0].rfind("/") if (ls >= 0): o = args[0][ls + 1:] else: o = args[0] for a in args[1:]: o += " %s" % a print o.strip() print " */" print """ /** * @libdoc Callbacks * * @sect Callback Overview * * XORP is an asynchronous programming environment and as a result there * are many places where callbacks are useful. Callbacks are typically * invoked to signify the completion or advancement of an asynchronous * operation. * * XORP provides a generic and flexible callback interface that utilizes * overloaded templatized functions for for generating callbacks * in conjunction with many small templatized classes. Whilst this makes * the syntax a little ugly, it provides a great deal of flexibility. * * XorpCallbacks are callback objects are created by the callback() * function which returns a reference pointer to a newly created callback * object. The callback is invoked by calling dispatch(), eg. *

#include "libxorp/xorp.h"
#include "libxorp/callback.hh"


static void hello_world() {
    cout << "Hello World" << endl;
}

int main() {
    // Typedef callback() return type for readability.  SimpleCallback
    // declares a XorpCallback taking 0 arguments and of return type void.
    typedef XorpCallback0::RefPtr SimpleCallback;

    // Create XorpCallback object using callback()
    SimpleCallback cb = callback(hello_world);

    // Invoke callback, results in call to hello_world.
    cb->dispatch();
    return 0;
}

* * The callback() method is overloaded and can also be used to create * callbacks to member functions, eg. *

#include "libxorp/xorp.h"
#include "libxorp/callback.hh"


class Foo {
public:
    void hello_world() {
	cout << "Foo::Hello World" << endl;
    }
};

int main() {
    typedef XorpCallback0::RefPtr SimpleCallback;

    Foo f;

    // Create a callback to a member function
    SimpleCallback cb = callback(&f, &Foo::hello_world);

    // Invoke f.hello_world
    cb->dispatch();

    return 0;
}

* * In addition, to being able to invoke member functions, callbacks can * also store arguments to functions. eg. *

#include "libxorp/xorp.h"
#include "libxorp/callback.hh"


static int sum(int x, int y) {
    cout << "sum(x = " << x << ", y = " << y << ")" << endl;
    return x + y;
}

int main() {
    // Callback to function returning "int"
    typedef XorpCallback0::RefPtr NoArgCallback;

    NoArgCallback cb1 = callback(sum, 1, 2);
    cout << "cb1->dispatch() returns " << cb1->dispatch() << endl; // "3"
    cout << endl;

    // Callback to function returning int and taking an integer argument
    typedef XorpCallback1::RefPtr OneIntArgCallback;

    OneIntArgCallback cb2 = callback(sum, 5);
    cout << "cb2->dispatch(10) returns " << cb2->dispatch(10) << endl; // 15
    cout << endl;

    cout << "cb2->dispatch(20) returns " << cb2->dispatch(20) << endl; // 25
    cout << endl;

    // Callback to function returning int and taking  2 integer arguments
    typedef XorpCallback2::RefPtr TwoIntArgCallback;

    TwoIntArgCallback cb3 = callback(sum);
    cout << "cb3->dispatch() returns " << cb3->dispatch(50, -50) << endl; // 0

    return 0;
}

* * Bound arguments, as with member functions, are implemented by the * overloading of the callback() method. At dispatch time, the bound * arguments are last arguments past to the wrappered function. If you * compile and run the program you will see: *
sum(x = 10, y = 5)
cb2->dispatch(10) returns 15
* * and: *
sum(x = 20, y = 5)
cb2->dispatch(20) returns 25
* * for the one bound argument cases. * * @sect Declaring Callback Types * * There are a host of XorpCallbackN types. The N denotes the number * of arguments that will be passed at dispatch time by the callback * invoker. The template parameters to XorpCallbackN types are the * return value followed by the types of arguments that will be passed * at dispatch time. Thus type: * *
XorpCallback1::RefPtr
 * 
* * corresponds to callback object returning a double when invoked and * requiring an integer argument to passed at dispatch time. * * When arguments are bound to a callback they are not specified * in the templatized argument list. So the above declaration is good * for a function taking an integer argument followed by upto the * maximum number of bound arguments. * * Note: In this header file, support is provided for upto %d bound * arguments and %d dispatch arguments. * * @sect Ref Pointer Helpers * * Callback objects may be set to NULL, since they use reference pointers * to store the objects. Callbacks may be unset using the ref_ptr::release() * method: *
    cb.release();
* and to tested using the ref_ptr::is_empty() method:
if (! cb.is_empty()) {
    cb->dispatch();
}
* * In many instances, the RefPtr associated with a callback on an object * will be stored by the object itself. For instance, a class may own a * timer object and the associated timer expiry callback which is * a member function of the containing class. Because the containing class * owns the callback object corresponding the timer callback, there is * never an opportunity for the callback to be dispatched on a deleted object * or with invalid data. */ """ print """ #ifndef INCLUDED_FROM_CALLBACK_HH #error "This file should be included through libxorp/callback.hh" #endif #ifndef __XORP_CALLBACK_HH__ #define __XORP_CALLBACK_HH__ #include "minitraits.hh" #include "ref_ptr.hh" #include "safe_callback_obj.hh" """ if (dbg): print \ """ #if defined(__GNUC__) && (__GNUC__ < 3) #define callback(x...) dbg_callback(__FILE__,__LINE__,x) #else #define callback(...) dbg_callback(__FILE__,__LINE__,__VA_ARGS__) #endif void trace_dispatch_enter(const char* file, int line); void trace_dispatch_leave(); #define record_dispatch_enter() trace_dispatch_enter( this->file(), \ this->line() ) #define record_dispatch_leave() trace_dispatch_leave() """ def output_trailer(): print "#endif /* __XORP_CALLBACK_HH__ */" def output_kdoc_base_class(nl): print "/**" print " * @short Base class for callbacks with %d dispatch time args." % nl print " */" def output_kdoc_class(target, nl, nb): if (target != ''): target = target.strip() + ' ' print "/**" print " * @short Callback object for %swith %d dispatch time" % (target, nl) print " * arguments and %d bound (stored) arguments." % nb print " */" def output_kdoc_factory_function(target, nl, nb): if (target != ''): target = target.strip() + ' ' print "/**" print " * Factory function that creates a callback object targetted at a" print " * %swith %d dispatch time arguments and %d bound arguments." % (target, nl, nb) print " */" def output_base(l_types, dbg): n = len(l_types) print "///////////////////////////////////////////////////////////////////////////////" print "//" print "// Code relating to callbacks with %d late args" % n print "//" print output_kdoc_base_class(n) print "template" % joining_csv(class_args(l_types)) print "struct XorpCallback%d {" % n print " typedef ref_ptr RefPtr;\n" % n if (dbg): print " XorpCallback%d(const char* file, int line)" % n print "\t: _file(file), _line(line) {}" print " virtual ~XorpCallback%d() {}" % n print " virtual R dispatch(%s)" % csv(l_types) + " = 0;" if (dbg): print " const char* file() const\t\t{ return _file; }" print " int line() const\t\t\t{ return _line; }" print "private:" print " const char* _file;" print " int _line;" print "};\n" def output_rest(l_types, b_types, dbg): nl = len(l_types) nb = len(b_types) base_class = "XorpCallback%d" % (nl, joining_csv(l_types)) void_base_class = "XorpCallback%d" % (nl, joining_csv(l_types)) if (dbg): debug_args = (("const char*", "file"), ("int", "line")) else: debug_args = (()) output_kdoc_class("functions", nl, nb) o = "template \n" % \ joining_csv(class_args(l_types) + class_args(b_types)) o += "struct XorpFunctionCallback%dB%d : public %s {\n" \ % (nl, nb, base_class) o += " typedef R (*F)(%s);\n" % csv(l_types + b_types) o += " XorpFunctionCallback%dB%d(" % (nl, nb) o += starting_csv(flatten_pair_list(debug_args)) o += "F f%s)\n\t: %s(%s),\n\t _f(f)%s\n {}\n" \ % (joining_csv(decl_args(b_types)), base_class, csv(second_args(debug_args)), joining_csv(cons_args(b_types))) o += " R dispatch(%s) {\n" % csv(decl_args(l_types)) if (dbg): o += "\trecord_dispatch_enter();\n" o += "\tR r = (*_f)(%s);\n" % csv(call_args(l_types) + mem_args(b_types)) if (dbg): o += "\trecord_dispatch_leave();\n" o += "\treturn r;\n" o += " }\n" o += "protected:\n F _f;\n" for ba in mem_decls(b_types): o += " %s;\n" % ba o += "};\n" print o print ' ' output_kdoc_class("void functions", nl, nb) o = "" o += "template <%s>\n" % \ csv(class_args(l_types) + class_args(b_types)) o += "struct XorpFunctionCallback%dB%d : public %s {\n" \ % (nl, nb, joining_csv(l_types + b_types), void_base_class) o += " typedef void (*F)(%s);\n" % csv(l_types + b_types) o += " XorpFunctionCallback%dB%d(" % (nl, nb) o += starting_csv(flatten_pair_list(debug_args)) o += "F f%s)\n\t: %s(%s),\n\t _f(f)%s\n {}\n" \ % (joining_csv(decl_args(b_types)), void_base_class, csv(second_args(debug_args)), joining_csv(cons_args(b_types))) o += " void dispatch(%s) {\n" % csv(decl_args(l_types)) if (dbg): o += "\trecord_dispatch_enter();\n" o += "\t(*_f)(%s);\n" % csv(call_args(l_types) + mem_args(b_types)) if (dbg): o += "\trecord_dispatch_leave();\n" o += " }\n" o += "protected:\n F _f;\n" for ba in mem_decls(b_types): o += " %s;\n" % ba o += "};\n" print o print ' ' output_kdoc_factory_function("function", nl, nb) o = "template \n" % joining_csv(class_args(l_types + b_types)) o += "typename XorpCallback%d::RefPtr\n" % (nl, joining_csv(l_types)) if (dbg): o += "dbg_" o += "callback(" o += starting_csv(flatten_pair_list(debug_args)) o += "R (*f)(%s)%s) {\n" % (csv(l_types + b_types), joining_csv(decl_args(b_types))) o += " return typename XorpCallback%d::RefPtr(new XorpFunctionCallback%dB%d(%sf%s));\n" \ % (nl, joining_csv(l_types), nl, nb, joining_csv(l_types + b_types), starting_csv(second_args(debug_args)), joining_csv(call_args(b_types))) o += "}" print o print for CONST,const in [('',''), ('Const', ' const')]: output_kdoc_class("%s member methods" % const, nl, nb) o = "" o += "template \n" % (joining_csv(class_args(l_types + b_types))) o += "struct Xorp%sMemberCallback%dB%d : public %s {\n" \ % (CONST, nl, nb, base_class) o += " typedef R (O::*M)(%s) %s;\n" % (csv(l_types + b_types), const) o += " Xorp%sMemberCallback%dB%d(" % (CONST, nl, nb) o += starting_csv(flatten_pair_list(debug_args)) o += "O* o, M m%s)\n\t :" % joining_csv(decl_args(b_types)) o += " %s(%s),\n\t " % (base_class, csv(second_args(debug_args))) o += "_o(o), _m(m)%s {}\n" % joining_csv(cons_args(b_types)) o += " R dispatch(%s) {\n" % csv(decl_args(l_types)) if (dbg): o += "\trecord_dispatch_enter();\n" o += "\tR r = ((*_o).*_m)(%s);\n" % csv(call_args(l_types) + mem_args(b_types)) if (dbg): o += "\trecord_dispatch_leave();\n" o += "\treturn r;\n" o += " }\n" o += "protected:\n" o += " O* _o; // Callback's target object\n" o += " M _m; // Callback's target method\n" for ba in mem_decls(b_types): o += " %s; // Bound argument\n" % ba o += "};\n" print o output_kdoc_class("void%s member methods" % const, nl, nb) o = "" o += "template \n" % joining_csv(class_args(l_types) + class_args(b_types)) o += "struct Xorp%sMemberCallback%dB%d\n" % (CONST, nl, nb, joining_csv(l_types + b_types)) o += ": public %s {\n" % void_base_class o += " typedef void (O::*M)(%s) %s;\n" % (csv(l_types + b_types), const) o += " Xorp%sMemberCallback%dB%d(" % (CONST, nl, nb) o += starting_csv(flatten_pair_list(debug_args)) o += "O* o, M m%s)\n\t :" % joining_csv(decl_args(b_types)) o += " %s(%s),\n\t " % (void_base_class, csv(second_args(debug_args))) o += "_o(o), _m(m)%s {}\n" % joining_csv(cons_args(b_types)) o += " void dispatch(%s) {\n" % csv(decl_args(l_types)) if (dbg): o += "\trecord_dispatch_enter();\n" o += "\t((*_o).*_m)(%s);\n" % csv(call_args(l_types) + mem_args(b_types)) if (dbg): o += "\trecord_dispatch_leave();\n" o += " }\n" o += "protected:\n" o += " O* _o; // Callback's target object\n" o += " M _m; // Callback's target method\n" for ba in mem_decls(b_types): o += " %s; // Bound argument\n" % ba o += "};\n" print o output_kdoc_class("%s safe member methods" % const, nl, nb) o = "" o += "template \n" % (joining_csv(class_args(l_types + b_types))) o += "struct Xorp%sSafeMemberCallback%dB%d\n" % (CONST, nl, nb) o += " : public Xorp%sMemberCallback%dB%d,\n" % (CONST, nl, nb, (joining_csv(l_types + b_types))) o += " public SafeCallbackBase {\n" o += " typedef typename Xorp%sMemberCallback%dB%d::M M;\n" % (CONST, nl, nb, joining_csv(l_types + b_types)) o += " Xorp%sSafeMemberCallback%dB%d(" % (CONST, nl, nb) o += starting_csv(flatten_pair_list(debug_args)) o += "O* o, M m%s)\n\t : " % joining_csv(decl_args(b_types)) o += "Xorp%sMemberCallback%dB%d(" % (CONST, nl, nb, joining_csv(l_types + b_types)) o += starting_csv(second_args(debug_args)) o += "o, m" o += joining_csv(call_args(b_types)) o += "),\n\t SafeCallbackBase(o) {}\n" o += " ~Xorp%sSafeMemberCallback%dB%d() {}\n" % (CONST, nl, nb) o += " R dispatch(%s) {\n" % (csv(decl_args(l_types))) o += "\tif (valid()) {\n" if (dbg): o += "\t record_dispatch_enter();\n" o += "\t R r = Xorp%sMemberCallback%dB%d::dispatch(%s);\n" % (CONST, nl, nb, joining_csv(l_types + b_types), csv(call_args(l_types))) if (dbg): o += "\t record_dispatch_leave();\n" o += "\t return r;\n" o += "\t}\n" o += " }\n" o += "};\n" print o output_kdoc_class("void%s safe member methods" % const, nl, nb) o = "" o += "template \n" % (joining_csv(class_args(l_types + b_types))) o += "struct Xorp%sSafeMemberCallback%dB%d\n" % (CONST, nl, nb, joining_csv(l_types + b_types)) o += " : public Xorp%sMemberCallback%dB%d,\n" % (CONST, nl, nb, (joining_csv(l_types + b_types))) o += " public SafeCallbackBase {\n" o += " typedef typename Xorp%sMemberCallback%dB%d::M M;\n" % (CONST, nl, nb, joining_csv(l_types + b_types)) o += " Xorp%sSafeMemberCallback%dB%d(" % (CONST, nl, nb) o += starting_csv(flatten_pair_list(debug_args)) o += "O* o, M m%s)\n\t : " % joining_csv(decl_args(b_types)) o += "Xorp%sMemberCallback%dB%d(" % (CONST, nl, nb, joining_csv(l_types + b_types)) o += starting_csv(second_args(debug_args)) o += "o, m" o += joining_csv(call_args(b_types)) o += "),\n\t SafeCallbackBase(o) {}\n" o += " ~Xorp%sSafeMemberCallback%dB%d() {}\n" % (CONST, nl, nb) o += " void dispatch(%s) {\n" % (csv(decl_args(l_types))) o += "\tif (valid()) {\n" if (dbg): o += "\t record_dispatch_enter();\n" o += "\t Xorp%sMemberCallback%dB%d::dispatch(%s);\n" % (CONST, nl, nb, joining_csv(l_types + b_types), csv(call_args(l_types))) if (dbg): o += "\t record_dispatch_leave();\n" o += "\t}\n" o += " }\n" o += "};\n" print o o = "" o += "template \n" \ % (joining_csv(class_args(l_types) + class_args(b_types))) o += "struct Xorp%sMemberCallbackFactory%dB%d\n" % (CONST, nl, nb) o += "{\n" o += " static Xorp%sMemberCallback%dB%d*\n" \ % (CONST, nl, nb, joining_csv(l_types + b_types)) o += " make(%s" % starting_csv(flatten_pair_list(debug_args)) o += "O* o, R (O::*p)(%s)%s%s)\n" \ % (csv(l_types + b_types), const, joining_csv(decl_args(b_types))) o += " {\n" o += "\treturn new Xorp%sSafeMemberCallback%dB%d(" \ % (CONST, nl, nb, joining_csv(l_types + b_types)) o += starting_csv(second_args(debug_args)) o += "o, p%s);\n" % joining_csv(call_args(b_types)) o += " }\n" o += "};\n" o += "\n" o += "template \n" \ % (joining_csv(class_args(l_types) + class_args(b_types))) o += "struct Xorp%sMemberCallbackFactory%dB%d\n" \ % (CONST, nl, nb, joining_csv(l_types + b_types)) o += "{\n" o += " static Xorp%sMemberCallback%dB%d*\n" \ % (CONST, nl, nb, joining_csv(l_types + b_types)) o += " make(%s" % starting_csv(flatten_pair_list(debug_args)) o += "O* o, R (O::*p)(%s)%s%s)\n" \ % (csv(l_types + b_types), const, joining_csv(decl_args(b_types))) o += " {\n" o += "\treturn new Xorp%sMemberCallback%dB%d(" \ % (CONST, nl, nb, joining_csv(l_types + b_types)) o += starting_csv(second_args(debug_args)) o += "o, p%s);\n" % joining_csv(call_args(b_types)) o += " };\n" o += "};\n" print o for p,q in [('*', ''), ('&', '&')]: output_kdoc_factory_function("%s member function" % const, nl, nb) o = "template typename " \ % joining_csv(class_args(l_types) + class_args(b_types)) o += "XorpCallback%s::RefPtr\n" % (nl, joining_csv(l_types)) if (dbg): o += "dbg_" o += "callback(" o += starting_csv(flatten_pair_list(debug_args)) o += "%s O%s o, R (O::*p)(%s)%s%s)\n" \ % (const, p, csv(l_types + b_types), const, joining_csv(decl_args(b_types))) o += "{\n" o += " return Xorp%sMemberCallbackFactory%dB%d<" % (CONST, nl, nb) o += "R, %s O%s, BaseAndDerived::True>::make(" % (const, joining_csv(l_types + b_types)) o += starting_csv(second_args(debug_args)) o += "%so, p%s);\n" % (q, joining_csv(call_args(b_types))) o += "}\n" print o print ' ' def cb_gen(max_bound, max_late, dbg): l_types = [] for l in range(0, max_late): if (l): l_types.append("A%d" % l); output_base(l_types, dbg) b_types = [] for b in range (0, max_bound): if (b): b_types.append("BA%d" % b) output_rest(l_types, b_types, dbg) us = \ """Usage: %s [options] -h Display usage information -d debug information (file and line and dispatch watchdog) -b Set maximum number of bound argument -l Set maximum number of late arguments""" def main(): def usage(): print us % sys.argv[0] try: opts, args = getopt.getopt(sys.argv[1:], "hdb:l:",\ ["help", "debug", "bound-args=", "late-args="]) except getopt.GetoptError: usage() sys.exit(1) nb = 4 nl = 4 dbg = 0 for o, a in opts: if (o in ("-h", "--help")): usage() sys.exit() if (o in ("-b", "--bound-args")): nb = int(a) if (o in ("-l", "--late-args")): nl = int(a) if (o in ("-d", "--debug")): dbg = 1 output_header(sys.argv[:], dbg) cb_gen(nb + 1, nl + 1, dbg) output_trailer() if __name__ == '__main__': main() xorp/libxorp/xorpfd.hh0000664000076400007640000001537011630246167015176 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __LIBXORP_XORPFD_HH__ #define __LIBXORP_XORPFD_HH__ #include "xorp.h" #include "c_format.hh" /** * @short XorpFd definition * * XorpFd is a wrapper class used to encapsulate a file descriptor. * * It exists because of fundamental differences between UNIX and Windows * in terms of how the two families of operating systems deal with file * descriptors; in most flavours of UNIX, all file descriptors are * created equal, and may be represented using an 'int' type which is * usually 32 bits wide. In Windows, sockets are of type SOCKET, which * is a typedef alias of u_int; whereas all other system objects are * of type HANDLE, which in turn is a typedef alias of 'void *'. * * The situation is made even more confusing by the fact that under * Windows, SOCKETs and HANDLEs may both be passed to various Windows * API functions. * * In order to prevent a situation where the developer has to explicitly * cast all arguments passed to such functions (in order to keep the XORP * code base compatible across all the operating systems we support), we * define a wrapper class with casting operators for the underlying types. * * When constructed, we always initialize the encapsulated file descriptor * to an invalid value appropriate to the OS under which we are running. * * The non-Windows case is very simple. We do not define both sets of * functions at once so that the compiler will flag as an error those * situations where file descriptors are being used in a UNIX-like way, * i.e. where developers try to exploit the fact that UNIX file descriptors * are monotonically increasing integers. * * XXX: Because Windows defines HANDLE in terms of a pointer, but also * defines SOCKET in terms of a 32-bit-wide unsigned integer, beware of * mixing 32-bit and 64-bit comparisons under Win64 when working with * socket APIs (or indeed any C/C++ library which will potentially do * work with sockets under Win64 such as libcomm). */ #ifdef HOST_OS_WINDOWS #define BAD_XORPFD INVALID_HANDLE_VALUE #else #define BAD_XORPFD (-1) #endif #ifndef HOST_OS_WINDOWS // Non-Windows code. class XorpFd { public: XorpFd() : _filedesc(BAD_XORPFD) {} XorpFd(int fd) : _filedesc(fd) {} operator int() const { return _filedesc; } int getSocket() const { return _filedesc; } string str() const { return c_format("%d", _filedesc); } void clear() { _filedesc = BAD_XORPFD; } bool is_valid() const { return (_filedesc != BAD_XORPFD); } private: int _filedesc; }; #else // HOST_OS_WINDOWS // Windows code. class XorpFd { public: enum WinFdType { FDTYPE_ERROR, // Invalid handle or method failure FDTYPE_FILE, // Disk file FDTYPE_CONSOLE, // Console or character device FDTYPE_PIPE, // Named or anonymous pipe FDTYPE_SOCKET, // Socket FDTYPE_PROCESS, // Process handle FDTYPE_OTHER // Unknown handle type }; private: // // Helper function to return what kind of object the encapsulated // Windows object handle points to. Optimized for sockets. // WinFdType get_type() const { if (!this->is_valid()) return (FDTYPE_ERROR); // Try to find invalid handles quickly at the cost of 1 syscall. DWORD dwflags; if (GetHandleInformation(*this, &dwflags) == 0) return (FDTYPE_ERROR); struct sockaddr_storage ss; socklen_t len = sizeof(ss); int ret = getsockname(getSocket(), (struct sockaddr *)&ss, &len); if (ret != -1) return (FDTYPE_SOCKET); else if (GetLastError() == WSAEINVAL) return (FDTYPE_ERROR); DWORD ntype = GetFileType(*this); switch (ntype) { case FILE_TYPE_CHAR: return (FDTYPE_CONSOLE); break; case FILE_TYPE_DISK: return (FDTYPE_FILE); break; case FILE_TYPE_PIPE: return (FDTYPE_PIPE); break; default: if (GetLastError() != NO_ERROR) { if (0 != GetProcessId(*this)) { return (FDTYPE_PROCESS); } return (FDTYPE_ERROR); } break; } return (FDTYPE_OTHER); } public: XorpFd() : _filedesc(BAD_XORPFD), _type(FDTYPE_ERROR) {} XorpFd(HANDLE h) : _filedesc(h), _type(get_type()) {} // _get_osfhandle() returns a long. We need to force a call // to get_type() to discover the underlying handle type. XorpFd(long l) : _filedesc(reinterpret_cast(l)), _type(get_type()) {} XorpFd(SOCKET s) : _filedesc(reinterpret_cast(s)), _type(FDTYPE_SOCKET) {} XorpFd(const XorpFd& rhand) : _filedesc(rhand._filedesc), _type(rhand._type) {} operator HANDLE() const { return _filedesc; } operator SOCKET() const { return getSocket(); } SOCKET getSocket() const { return reinterpret_cast(_filedesc); } void clear() { _filedesc = BAD_XORPFD; _type = FDTYPE_ERROR; } string str() const { return c_format("%p", _filedesc); } bool is_valid() const { return (_filedesc != BAD_XORPFD); } WinFdType type() const { return _type; } bool is_console() const { return (_type == FDTYPE_CONSOLE); } bool is_process() const { return (_type == FDTYPE_PROCESS); } bool is_pipe() const { return (_type == FDTYPE_PIPE); } bool is_socket() const { return (_type == FDTYPE_SOCKET); } // On Windows, HANDLE is a void *. // Because there are several cast operators, and any may be // invoked implicitly in the context of an expression containing // an instance of XorpFd, we must disambiguate by providing // comparison operators here. bool operator ==(const XorpFd& rhand) const { return (_filedesc == rhand._filedesc); } bool operator !=(const XorpFd& rhand) const { return (_filedesc != rhand._filedesc); } bool operator >(const XorpFd& rhand) const { return (_filedesc > rhand._filedesc); } bool operator <(const XorpFd& rhand) const { return (_filedesc < rhand._filedesc); } private: HANDLE _filedesc; WinFdType _type; }; #endif // HOST_OS_WINDOWS #endif // __LIBXORP_XORPFD_HH__ xorp/libxorp/ipv6.cc0000664000076400007640000003161211540224227014534 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "libxorp/xorp.h" #ifdef HAVE_SYS_SOCKET_H #include #endif #ifdef HAVE_NETINET_IN_H #include #endif #ifdef HAVE_ARPA_INET_H #include #endif #include "ipv6.hh" IPv6::IPv6(const uint8_t* from_uint8) { memcpy(_addr, from_uint8, sizeof(_addr)); } IPv6::IPv6(const uint32_t* from_uint32) { _addr[0] = from_uint32[0]; _addr[1] = from_uint32[1]; _addr[2] = from_uint32[2]; _addr[3] = from_uint32[3]; } IPv6::IPv6(const in6_addr& from_in6_addr) { memcpy(_addr, &from_in6_addr, sizeof(_addr)); } IPv6::IPv6(const sockaddr& sa) throw (InvalidFamily) { if (sa.sa_family != AF_INET6) xorp_throw(InvalidFamily, sa.sa_family); const sockaddr_in6* sin6 = sockaddr2sockaddr_in6(&sa); memcpy(_addr, sin6->sin6_addr.s6_addr, sizeof(_addr)); } IPv6::IPv6(const sockaddr_storage& ss) throw (InvalidFamily) { if (ss.ss_family != AF_INET6) xorp_throw(InvalidFamily, ss.ss_family); const sockaddr* sa = sockaddr_storage2sockaddr(&ss); const sockaddr_in6* sin6 = sockaddr2sockaddr_in6(sa); memcpy(_addr, sin6->sin6_addr.s6_addr, sizeof(_addr)); } IPv6::IPv6(const sockaddr_in6& sin6) throw (InvalidFamily) { if (sin6.sin6_family != AF_INET6) xorp_throw(InvalidFamily, sin6.sin6_family); memcpy(_addr, sin6.sin6_addr.s6_addr, sizeof(_addr)); } IPv6::IPv6(const char* from_cstring) throw (InvalidString) { if (from_cstring == NULL) xorp_throw(InvalidString, "Null value" ); if (inet_pton(AF_INET6, from_cstring, &_addr[0]) <= 0) xorp_throw(InvalidString, c_format("Bad IPv6 \"%s\"", from_cstring)); } /** * Copy the raw address to memory pointed by @to. * @return the number of copied octets. */ size_t IPv6::copy_out(uint8_t* to_uint8) const { memcpy(to_uint8, _addr, addr_bytelen()); return addr_bytelen(); } /** * Copy the raw address to @in6_addr. * @return the number of copied octets. */ size_t IPv6::copy_out(struct in6_addr& to_in6_addr) const { return (copy_out((uint8_t* )&to_in6_addr)); } /** * Copy the raw address to @to_sockaddr, and assign appropriately * the rest of the fields in @to_sockaddr. * @return the number of copied octets. */ size_t IPv6::copy_out(struct sockaddr& to_sockaddr) const { return (copy_out(*sockaddr2sockaddr_in6(&to_sockaddr))); } /** * Copy the raw address to @to_sockaddr_storage, and assign appropriately * the rest of the fields in @to_sockaddr_storage. * @return the number of copied octets. */ size_t IPv6::copy_out(struct sockaddr_storage& to_sockaddr_storage) const { return (copy_out(*sockaddr_storage2sockaddr(&to_sockaddr_storage))); } /** * Copy the raw address to @to_sockaddr_in6, and assign appropriately * the rest of the fields in @to_sockaddr_in6. * @return the number of copied octets. */ size_t IPv6::copy_out(struct sockaddr_in6& to_sockaddr_in6) const { memset(&to_sockaddr_in6, 0, sizeof(to_sockaddr_in6)); #ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN to_sockaddr_in6.sin6_len = sizeof(to_sockaddr_in6); #endif to_sockaddr_in6.sin6_family = AF_INET6; #ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID #ifdef IPV6_STACK_KAME // // XXX: In case of KAME the local interface index (also the link-local // scope_id) is encoded in the third and fourth octet of an IPv6 // address (for link-local unicast/multicast addresses or // interface-local multicast addresses only). // if (is_linklocal_unicast() || is_linklocal_multicast() || is_interfacelocal_multicast()) { uint32_t addr0 = ntohl(_addr[0]); uint16_t zoneid = (addr0 & 0xffff); // XXX: 16 bits only to_sockaddr_in6.sin6_scope_id = zoneid; } #endif // IPV6_STACK_KAME #endif // HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID return (copy_out(to_sockaddr_in6.sin6_addr)); } /** * Copy a raw address from the memory pointed by @from_uint8. * @return the number of copied octets. */ size_t IPv6::copy_in(const uint8_t* from_uint8) { memcpy(_addr, from_uint8, addr_bytelen()); return (addr_bytelen()); } /** * Copy a raw address of family %AF_INET6 from @from_in6_addr. * @return the number of copied octets. */ size_t IPv6::copy_in(const in6_addr& from_in6_addr) { return (copy_in(reinterpret_cast(&from_in6_addr))); } /** * Copy a raw address from @from_sockaddr. * @return the number of copied octets. */ size_t IPv6::copy_in(const sockaddr& from_sockaddr) throw (InvalidFamily) { return (copy_in(*sockaddr2sockaddr_in6(&from_sockaddr))); } /** * Copy a raw address from @from_sockaddr_storage. * @return the number of copied octets. */ size_t IPv6::copy_in(const sockaddr_storage& from_sockaddr_storage) throw (InvalidFamily) { return (copy_in(*sockaddr_storage2sockaddr(&from_sockaddr_storage))); } /** * Copy a raw address from @from_sockaddr_in6. * @return the number of copied octets. */ size_t IPv6::copy_in(const sockaddr_in6& from_sockaddr_in6) throw (InvalidFamily) { if (from_sockaddr_in6.sin6_family != AF_INET6) xorp_throw(InvalidFamily, from_sockaddr_in6.sin6_family); return (copy_in(from_sockaddr_in6.sin6_addr)); } IPv6 IPv6::operator<<(uint32_t ls) const { uint32_t tmp_addr[4]; x_static_assert(sizeof(_addr) == sizeof(tmp_addr)); // Shift the words, and at the same time convert them into host-order switch (ls / 32) { case 0: tmp_addr[0] = ntohl(_addr[0]); tmp_addr[1] = ntohl(_addr[1]); tmp_addr[2] = ntohl(_addr[2]); tmp_addr[3] = ntohl(_addr[3]); break; case 1: tmp_addr[0] = ntohl(_addr[1]); tmp_addr[1] = ntohl(_addr[2]); tmp_addr[2] = ntohl(_addr[3]); tmp_addr[3] = 0; break; case 2: tmp_addr[0] = ntohl(_addr[2]); tmp_addr[1] = ntohl(_addr[3]); tmp_addr[2] = 0; tmp_addr[3] = 0; break; case 3: tmp_addr[0] = ntohl(_addr[3]); tmp_addr[1] = 0; tmp_addr[2] = 0; tmp_addr[3] = 0; break; default: // ls >= 128; clear all bits return ZERO(); } ls &= 0x1f; if (ls != 0) { uint32_t rs = 32 - ls; tmp_addr[0] = (tmp_addr[0] << ls) | (tmp_addr[1] >> rs); tmp_addr[1] = (tmp_addr[1] << ls) | (tmp_addr[2] >> rs); tmp_addr[2] = (tmp_addr[2] << ls) | (tmp_addr[3] >> rs); tmp_addr[3] = tmp_addr[3] << ls; } // Convert the words back into network-order tmp_addr[0] = htonl(tmp_addr[0]); tmp_addr[1] = htonl(tmp_addr[1]); tmp_addr[2] = htonl(tmp_addr[2]); tmp_addr[3] = htonl(tmp_addr[3]); return IPv6(tmp_addr); } IPv6 IPv6::operator>>(uint32_t rs) const { uint32_t tmp_addr[4]; x_static_assert(sizeof(_addr) == sizeof(tmp_addr)); // Shift the words, and at the same time convert them into host-order switch (rs / 32) { case 0: tmp_addr[3] = ntohl(_addr[3]); tmp_addr[2] = ntohl(_addr[2]); tmp_addr[1] = ntohl(_addr[1]); tmp_addr[0] = ntohl(_addr[0]); break; case 1: tmp_addr[3] = ntohl(_addr[2]); tmp_addr[2] = ntohl(_addr[1]); tmp_addr[1] = ntohl(_addr[0]); tmp_addr[0] = 0; break; case 2: tmp_addr[3] = ntohl(_addr[1]); tmp_addr[2] = ntohl(_addr[0]); tmp_addr[1] = 0; tmp_addr[0] = 0; break; case 3: tmp_addr[3] = ntohl(_addr[0]); tmp_addr[2] = 0; tmp_addr[1] = 0; tmp_addr[0] = 0; break; default: // rs >= 128; clear all bits return ZERO(); } rs &= 0x1f; if (rs != 0) { uint32_t ls = 32 - rs; tmp_addr[3] = (tmp_addr[3] >> rs) | (tmp_addr[2] << ls); tmp_addr[2] = (tmp_addr[2] >> rs) | (tmp_addr[1] << ls); tmp_addr[1] = (tmp_addr[1] >> rs) | (tmp_addr[0] << ls); tmp_addr[0] = (tmp_addr[0] >> rs); } // Convert the words back into network-order tmp_addr[0] = htonl(tmp_addr[0]); tmp_addr[1] = htonl(tmp_addr[1]); tmp_addr[2] = htonl(tmp_addr[2]); tmp_addr[3] = htonl(tmp_addr[3]); return IPv6(tmp_addr); } bool IPv6::operator<(const IPv6& other) const { int i; x_static_assert(sizeof(_addr) == 16); for (i = 0; i < 3; i++) { // XXX: Loop ends intentionally at 3 not 4 if (_addr[i] != other._addr[i]) break; } return ntohl(_addr[i]) < ntohl(other._addr[i]); } bool IPv6::operator==(const IPv6& other) const { return ((_addr[0] == other._addr[0]) && (_addr[1] == other._addr[1]) && (_addr[2] == other._addr[2]) && (_addr[3] == other._addr[3])); } bool IPv6::operator!=(const IPv6& other) const { return ((_addr[0] != other._addr[0]) || (_addr[1] != other._addr[1]) || (_addr[2] != other._addr[2]) || (_addr[3] != other._addr[3])); } IPv6& IPv6::operator--() { for (int i = 3; i >= 0; i--) { if (_addr[i] == 0) { _addr[i] = 0xffffffffU; } else { uint32_t tmp_addr = ntohl(_addr[i]) - 1; _addr[i] = htonl(tmp_addr); return *this; } } return *this; } IPv6& IPv6::operator++() { for (int i = 3; i >= 0; i--) { if (_addr[i] == 0xffffffffU) { _addr[i] = 0; } else { uint32_t tmp_addr = ntohl(_addr[i]) + 1; _addr[i] = htonl(tmp_addr); return *this; } } return *this; } static uint32_t init_prefixes(IPv6* v6prefix) { uint32_t u[4]; u[0] = u[1] = u[2] = u[3] = 0xffffffff; IPv6 a1(u); for (int i = 0; i <= 128; i++) { v6prefix[i] = a1 << (128 - i); } return 128; } const IPv6& IPv6::make_prefix(uint32_t mask_len) throw (InvalidNetmaskLength) { static IPv6 v6prefix[129]; static uint32_t n_inited_prefixes = init_prefixes(&v6prefix[0]); if (mask_len > n_inited_prefixes) xorp_throw(InvalidNetmaskLength, mask_len); return v6prefix[mask_len]; } string IPv6::str() const { char str_buffer[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"]; inet_ntop(AF_INET6, &_addr[0], str_buffer, sizeof(str_buffer)); return (str_buffer); // XXX: implicitly create string return object } bool IPv6::is_unicast() const { // Icky casting because of alternate definitions of IN6_IS_ADDR_MULTICAST uint32_t* nc = const_cast(_addr); in6_addr* addr6 = reinterpret_cast(nc); return (! (IN6_IS_ADDR_MULTICAST(addr6) || IN6_IS_ADDR_UNSPECIFIED(const_cast(addr6)))); } bool IPv6::is_multicast() const { // Icky casting because of alternate definitions of IN6_IS_ADDR_MULTICAST uint32_t* nc = const_cast(_addr); in6_addr* addr6 = reinterpret_cast(nc); return (IN6_IS_ADDR_MULTICAST(addr6)); } bool IPv6::is_linklocal_unicast() const { const in6_addr* addr6 = reinterpret_cast(_addr); return (IN6_IS_ADDR_LINKLOCAL(addr6)); } bool IPv6::is_interfacelocal_multicast() const { // Icky casting because of alternate definitions // of IN6_IS_ADDR_MC_NODELOCAL uint32_t* nc = const_cast(_addr); in6_addr* addr6 = reinterpret_cast(nc); return (IN6_IS_ADDR_MC_NODELOCAL(addr6)); } bool IPv6::is_linklocal_multicast() const { // Icky casting because of alternate definitions // of IN6_IS_ADDR_MC_LINKLOCAL uint32_t* nc = const_cast(_addr); in6_addr* addr6 = reinterpret_cast(nc); return (IN6_IS_ADDR_MC_LINKLOCAL(addr6)); } bool IPv6::is_loopback() const { const uint32_t* nc = _addr; const in6_addr* addr6 = reinterpret_cast(nc); return IN6_IS_ADDR_LOOPBACK(addr6); } uint32_t IPv6::mask_len() const { uint32_t ctr = 0; for (int j = 0; j < 4; j++) { uint32_t shift = ntohl(_addr[j]); for (int i = 0; i < 32; i++) { if ((shift & 0x80000000U) != 0) { ctr++; shift = shift << 1; } else { return ctr; } } } return ctr; } const string& IPv6::ip_version_str() { static const string IP_VERSION_STR("IPv6"); return IP_VERSION_STR; } const IPv6 IPv6Constants::zero("::"); const IPv6 IPv6Constants::any(IPv6Constants::zero); const IPv6 IPv6Constants::all_ones(~IPv6Constants::zero); const IPv6 IPv6Constants::loopback("::1"); const IPv6 IPv6Constants::multicast_base("FF00::"); const IPv6 IPv6Constants::multicast_all_systems("FF02::1"); const IPv6 IPv6Constants::multicast_all_routers("FF02::2"); const IPv6 IPv6Constants::dvmrp_routers("FF02::4"); const IPv6 IPv6Constants::ospfigp_routers("FF02::5"); const IPv6 IPv6Constants::ospfigp_designated_routers("FF02::6"); const IPv6 IPv6Constants::rip2_routers("FF02::9"); const IPv6 IPv6Constants::pim_routers("FF02::D"); const IPv6 IPv6Constants::ssm_routers("FF02::16"); xorp/libxorp/xlog.h0000664000076400007640000003401411703345405014465 0ustar greearbgreearb/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- */ /* vim:set sts=4 ts=8: */ /* * Copyright (c) 2001-2012 XORP, Inc and Others * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License, Version * 2.1, June 1999 as published by the Free Software Foundation. * Redistribution and/or modification of this program under the terms of * any other version of the GNU Lesser General Public License is not * permitted. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, * see the GNU Lesser General Public License, Version 2.1, a copy of * which can be found in the XORP LICENSE.lgpl file. * * XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; * http://xorp.net */ #ifndef __LIBXORP_XLOG_H__ #define __LIBXORP_XLOG_H__ #include "libxorp/xorp.h" // TODO: Make this configurable in scons #define XORP_LOG_PRINT_USECS /* * The following defines and notes we defined __printfike if it does not * already exist. The expansion of this macro uses a gcc extension to * check format strings. */ #ifndef __printflike #ifdef __GNUC__ #define __printflike(fmt,va1) __attribute__((__format__(printf, fmt, va1))) #define __libxorp_xlog_defined_printflike #else #define __printflike(fmt, va1) #define __libxorp_xlog_defined_printflike #endif /* __GNUC__ */ #endif /* __printflike */ /** * @short XORP logging functions. * * The xlog functions provide a similar role to syslog. The log * messages may be output to multiple output streams simulataneously. */ # ifdef __cplusplus extern "C" { # endif /** * The log levels. Typically used only by @ref xlog_enable() * and @ref xlog_disable() */ typedef enum { XLOG_LEVEL_MIN = 0, /* 0 */ XLOG_LEVEL_FATAL = 0, /* 0 */ XLOG_LEVEL_ERROR, /* 1 */ XLOG_LEVEL_WARNING, /* 2 */ XLOG_LEVEL_INFO, /* 3 */ XLOG_LEVEL_TRACE, /* 4 */ XLOG_LEVEL_RTRMGR_ONLY_NO_PREAMBLE, /* 5 */ /* XXX: temp, to be removed; see Bugzilla entry 795 */ XLOG_LEVEL_MAX } xlog_level_t; /** * The messages verbose level. Typically used only by @ref xlog_set_verbose() * and @ref xlog_level_set_verbose() */ typedef enum { XLOG_VERBOSE_LOW = 0, /* 0 */ XLOG_VERBOSE_MEDIUM, /* 1 */ XLOG_VERBOSE_HIGH, /* 2 */ XLOG_VERBOSE_RTRMGR_ONLY_NO_PREAMBLE, /* 3 */ /* XXX: temp, to be removed; see Bugzilla entry 795 */ XLOG_VERBOSE_MAX } xlog_verbose_t; /* Don't modify this directly outside the xlog.[ch] files */ extern int xlog_level_enabled[XLOG_LEVEL_MAX]; /** * The type of add-on functions to process the log messages. */ typedef int (*xlog_output_func_t)(void *obj, xlog_level_t level, const char *msg); /** * A macro used for internal purpose to generate the appropriate xlog code */ #if (!defined XORP_MODULE_NAME) && (!defined XORP_LIBRARY_NAME) #error You MUST have in your local directory a file like #error "foo_module.h" that has defined inside the module #error or library name. E.g.: #define XORP_MODULE_NAME "BGP" #error or #define XORP_LIBRARY_NAME #error This "foo_module.h" must be the first included by #error each *.c and *.cc file. #endif #ifdef XORP_LIBRARY_NAME # define _XLOG_MODULE_NAME XORP_LIBRARY_NAME #else # define _XLOG_MODULE_NAME XORP_MODULE_NAME #endif #define XLOG_FN(__log_level, fmt...) \ do { \ if (xlog_level_enabled[__log_level]) \ _xlog_with_level(__log_level, _XLOG_MODULE_NAME, __LINE__, \ __FILE__, __FUNCTION__, fmt); \ } while (0) /** * Initialize the log utility. * * As part of the initialization, the preamble string will be set to * <@ref process_name><@ref preamble_message> * Use in preference to @ref xlog_set_preamble which will be removed. * * @param argv0 the path of the process executable from which the program name * will be extract to become part of the preamble string. * * @param preamble_message a string that will become part of the * preamble string. * * @return 0 on success, otherwise -1. */ int xlog_init(const char *argv0, const char *preamble_message); /** * Gracefully exit logging. * * @return 0 on success, otherwise -1. */ int xlog_exit(void); /** * Start logging. * * @return 0 on success, otherwise -1. */ int xlog_start(void); /** * Stop logging. * * @return 0 on success, otherwise -1. */ int xlog_stop(void); /** * Check if xlog is running. * * @return non-zero if xlog is running, otherwise 0. */ int xlog_is_running(void); /** * Enable logging for messages of a given type (@ref log_level_t). * * By default, all message types are enabled. * * @param log_level the message type @ref xlog_level_t * (e.g., @ref XLOG_LEVEL_WARNING) to enable the logging for. * @return 0 on success, otherwise -1. */ int xlog_enable(xlog_level_t log_level); /** * Disable logging for messages of a given type (@ref log_level_t). * * Note: @ref XLOG_LEVEL_FATAL cannot be disabled. * * @param log_level the message type @ref xlog_level_t * (e.g., @ref XLOG_LEVEL_WARNING) to disable the logging for. * @return 0 on success, otherwise -1. */ int xlog_disable(xlog_level_t log_level); /** * Set the preamble string for the log entries. * * @param text the preamble string, or NULL if no preamble. */ void xlog_set_preamble(const char *text); /** * Get process name as set with xlog_init. * * @return pointer to name on success, NULL otherwise. */ const char* xlog_process_name(void); /** * Set the level of verbosity (@ref xlog_verbose_t) for the log entries. * * Applies for all type of messages except for @ref XLOG_LEVEL_FATAL * which always is set to the most verbose level. * * @param verbose_level the level of verbosity @ref xlog_verbose_t * (higher is more verbose). */ void xlog_set_verbose(xlog_verbose_t verbose_level); /** * Set the level of verbosity (@ref xlog_verbose_t) for the log entries * of messages of a given type (@ref xlog_level_t). * * Note: @ref XLOG_LEVEL_FATAL verbosity cannot be changed, and is * always set to the most verbose level. * * @param log_level the message type @ref xlog_level_t to set the * verbosity of. * @param verbose_level the level of verbosity @ref xlog_verbose_t * (higher is more verbose). */ void xlog_level_set_verbose(xlog_level_t log_level, xlog_verbose_t verbose_level); /** * Add a file descriptor to the set of output streams. * * @param fp the file descriptor to add to the set of output streams. * @return 0 on success, otherwise -1. */ int xlog_add_output(FILE *fp); /** * Remove a file descriptor from the set of output streams. * * @param fp the file descriptor to remove from the set of output streams. * @return 0 on success, otherwise -1. */ int xlog_remove_output(FILE *fp); /** * Add a processing function and an object to the set of output streams. * * @param func the function to add to the set of output streams. * @param obj the object to supply @ref func with when called. * * @return 0 on success, otherwise -1. */ int xlog_add_output_func(xlog_output_func_t func, void *obj); /** * Add a channel which goes to the X/Open compatible system log aka syslog. * * @param syslogspec the syslog facility and priority as a string * "facility.priority" * @return 0 on success, otherwise -1. */ int xlog_add_syslog_output(const char *syslogspec); /** * Remove a processing function and an object from the set of output streams. * * @param func the function to remove from the set of output streams. * @param obj the object that @ref func was supplied with. * * @return 0 on success, otherwise -1. */ int xlog_remove_output_func(xlog_output_func_t func, void *obj); /** * Add default output stream to list of output streams. * * XXX: right now the default is '/dev/stderr', but it should eventually be: * `/dev/console' if the process has sufficient permissions, * and `/dev/stderr' otherwise. * * @return 0 on success, otherwise -1. */ int xlog_add_default_output(void); /** * Remove the default output stream from the set of output streams. * * @return 0 on success, otherwise -1. */ int xlog_remove_default_output(void); /** * Write a message to the xlog output streams. * * @param module_name the name of the module this message applies to. * @param line the line number in the file this message applies to. * @param file the file name this message applies to. * @param function the function name this message applies to. * @param format the printf()-style format of the message to write. * Note that a trailing newline is added if none is present. * @param ... the arguments for @ref format. */ void _xlog_with_level(int log_level, const char *module_name, int line, const char *file, const char *function, const char *format, ...) __printflike(6,7); #ifdef L_FATAL # define XLOG_FATAL(fmt...) \ do { \ _xlog_with_level(XLOG_LEVEL_FATAL, _XLOG_MODULE_NAME, \ __LINE__, __FILE__, __FUNCTION__, \ fmt); \ assert(0); \ } while (0) #else # define XLOG_FATAL(fmt...) assert(0); #endif #ifdef L_ERROR #define XLOG_ERROR(fmt...) XLOG_FN(XLOG_LEVEL_ERROR, fmt) #else #define XLOG_ERROR(fmt...) do{}while(0) #endif #ifdef L_WARNING #define XLOG_WARNING(fmt...) XLOG_FN(XLOG_LEVEL_WARNING, fmt) #else #define XLOG_WARNING(fmt...) do{}while(0) #endif #ifdef L_INFO #define XLOG_INFO(fmt...) XLOG_FN(XLOG_LEVEL_INFO, fmt) #else #define XLOG_INFO(fmt...) do{}while(0) #endif /** * Write a message without a preamble to the xlog output streams. */ #ifdef L_OTHER #define XLOG_RTRMGR_ONLY_NO_PREAMBLE(fmt...) XLOG_FN(XLOG_LEVEL_RTRMGR_ONLY_NO_PREAMBLE, fmt) #else #define XLOG_RTRMGR_ONLY_NO_PREAMBLE(fmt...) do{}while(0) #endif #ifdef L_ASSERT /** * XORP replacement for assert(3). * * Note that it cannot be conditionally disabled and logs error through * the standard XLOG mechanism. * * @param assertion the assertion condition. */ #define XLOG_ASSERT(assertion) \ do { \ if (!(assertion)) { \ XLOG_FATAL(#assertion); \ } \ } while (0) #else # define XLOG_ASSERT(assertion) assert(assertion) #endif /** * A marker that can be used to indicate code that should never be executed. * * Note that it cannot be conditionally disabled and logs error through * the standard XLOG mechanism. * Always calls XLOG_FATAL. */ #ifdef L_OTHER #define XLOG_UNREACHABLE() \ do { \ XLOG_FATAL("Internal fatal error: unreachable code reached"); \ exit(1); /* unreached: keep the compiler happy */ \ } while (0) #else # define XLOG_UNREACHABLE() assert(0) #endif /** * A marker that can be used to indicate code that is not yet * implemented and hence should not be run. * * Note that it cannot be conditionally disabled and logs error through * the standard XLOG mechanism. * Always calls XLOG_FATAL. */ #ifdef L_OTHER #define XLOG_UNFINISHED() \ do { \ XLOG_FATAL("Internal fatal error: unfinished code reached"); \ exit(1); /* unreached: keep the compiler happy */ \ } while (0) #else # define XLOG_UNFINISHED() assert(0) #endif /* * The macros below define the XLOG_TRACE(), the macro responsible for * generating trace messages. It takes the same arguments as * printf(), except that the very first argument is a boolean * that defines whether the trace message will be output. * Note that a trailing newline is added if none is present. * E.g., * * XLOG_TRACE(cond_variable, "The number is %d", 5); * */ #ifdef L_TRACE # define XLOG_TRACE(__flg, args...) \ do { \ if (__flg && xlog_level_enabled[XLOG_LEVEL_TRACE]) { \ _xlog_with_level(XLOG_LEVEL_TRACE, XORP_MODULE_NAME, \ __LINE__, __FILE__, __FUNCTION__, \ args); \ } \ } while(0) #else # define XLOG_TRACE(args...) do{} while(0) #endif /** * Compute the current local time and return it as a string. * * The return string has the format: * Year/Month/Day Hour:Minute:Second.Microsecond * Example: 2002/02/05 20:22:09.808632 * Note that the returned string uses statically allocated memory, * and does not need to be de-allocated. * * @return a statically allocated string with the local time using * the format described above. */ const char *xlog_localtime2string(void); /** * A local implementation of vasprintf(3). * * If vasprintf(3) is available, it is called instead. * * @param ret a pointer to the string pointer to store the result. * @param format the printf(3)-style format. * @param ap the variable arguments for @ref format. * * @return (From FreeBSD vasprintf(3) manual page): * The number of characters printed (not including the trailing '\0' * used to end output to strings). Also, set the value pointed to by * ret to be a pointer to a buffer sufficiently large to hold the * formatted string. This pointer should be passed to free(3) to * release the allocated storage when it is no longer needed. If * sufficient space cannot be allocated, will return -1 and set @ref ret * to be a NULL pointer. */ int x_vasprintf(char **ret, const char *format, va_list ap); /** * A local implementation of asprintf(3). * * @param ret a pointer to the string pointer to store the result. * @param format the printf(3)-style format. * @param ... the variable arguments for @ref format. * * @return (From FreeBSD asprintf(3) manual page): * The number of characters printed (not including the * trailing '\0' used to end output to strings). Also, set ret to be * a pointer to a buffer sufficiently large to hold the formatted string. * This pointer should be passed to free(3) to release the allocated * storage when it is no longer needed. If sufficient space cannot * be allocated, will return -1 and set @ref ret to be a NULL pointer. */ int x_asprintf(char **ret, const char *format, ...); /* Undefine __printflike if we defined it */ #ifdef __libxorp_xlog_defined_printflike #undef __libxorp_xlog_defined_printflike #undef __printflike #endif /* __libxorp_xlog_defined_printflike */ # ifdef __cplusplus } # endif #endif /* __LIBXORP_XLOG_H__ */ xorp/libxorp/callback.hh0000664000076400007640000000241311421137511015410 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/libxorp/callback.hh,v 1.23 2008/10/02 21:57:29 bms Exp $ #ifndef __LIBXORP_CALLBACK_HH__ #define __LIBXORP_CALLBACK_HH__ #include "libxorp/xorp.h" #define INCLUDED_FROM_CALLBACK_HH #ifdef DEBUG_CALLBACKS #include "callback_debug.hh" #else #include "callback_nodebug.hh" #endif #undef INCLUDED_FROM_CALLBACK_HH #endif // __LIBXORP_CALLBACK_HH__ xorp/libxorp/libxorp_module.h0000664000076400007640000000243411421137511016533 0ustar greearbgreearb/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- */ /* vim:set sts=4 ts=8: */ /* * Copyright (c) 2001-2009 XORP, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License, Version * 2.1, June 1999 as published by the Free Software Foundation. * Redistribution and/or modification of this program under the terms of * any other version of the GNU Lesser General Public License is not * permitted. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, * see the GNU Lesser General Public License, Version 2.1, a copy of * which can be found in the XORP LICENSE.lgpl file. * * XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; * http://xorp.net */ /* * $XORP: xorp/libxorp/libxorp_module.h,v 1.11 2008/10/02 21:57:31 bms Exp $ */ /* * Module definitions. */ #ifndef __LIBXORP_LIBXORP_MODULE_H__ #define __LIBXORP_LIBXORP_MODULE_H__ #ifndef XORP_MODULE_NAME #define XORP_MODULE_NAME "LIBXORP" #endif #ifndef XORP_MODULE_VERSION #define XORP_MODULE_VERSION "0.1" #endif #endif /* __LIBXORP_LIBXORP_MODULE_H__ */ xorp/libxorp/clock.hh0000664000076400007640000000364511633743677015004 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/libxorp/clock.hh,v 1.9 2008/10/02 21:57:30 bms Exp $ #ifndef __LIBXORP_CLOCK_HH__ #define __LIBXORP_CLOCK_HH__ class TimeVal; class ClockBase { public: virtual ~ClockBase(); /** * Update internal concept of time. */ virtual void advance_time() = 0; /** * Get time it was when advance_time() was last called. Successive calls * to current_time return the same value. Time only advances when * advance_time() is called. * * @param tv TimeVal to be filled in with current time. */ virtual void current_time(TimeVal& tv) = 0; }; /** * An implementation of ClockBase that uses the underlying system's * 'get current system time' function as it's clock source. */ class SystemClock : public ClockBase, NONCOPYABLE { public: SystemClock(); virtual ~SystemClock(); void advance_time(); void current_time(TimeVal& tv); private: TimeVal* _tv; #ifdef __WIN32__ int ms_time_res; #endif }; #endif // __LIBXORP_CLOCK_HH__ xorp/libxorp/ipvx.hh0000664000076400007640000007322011421137511014646 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/libxorp/ipvx.hh,v 1.35 2009/01/05 18:30:58 jtc Exp $ #ifndef __LIBXORP_IPVX_HH__ #define __LIBXORP_IPVX_HH__ #include "ipv4.hh" #include "ipv6.hh" /** * @short Basic IPvX class (for both IPv4 and IPv6) */ class IPvX { public: /** * Default contructor * * Creates an IPvX address with address family of AF_INET, and * address value of INADDR_ANY (i.e., containing IPv4(0)). */ IPvX(); /** * Constructor for a specified address family. * * Creates an address of specified family, and address value of * INADDR_ANY or IN6ADDR_ANY (for IPv4 and IPv6 respectively). * * @param family the address family. */ explicit IPvX(int family) throw (InvalidFamily); /** * Constructor from a (uint8_t *) memory pointer. * * Creates an address of specified family, and address value * by using data from specified location. * * @param family the address family. * @param from_uint8 the pointer to the memory to copy the address value * from. */ IPvX(int family, const uint8_t *from_uint8) throw (InvalidFamily); /** * Constructor from an IPv4 address. * * Create an IPvX address from an IPv4 address. * * @param ipv4 the IPv4 address to assign the address value from. */ IPvX(const IPv4& ipv4); /** * Constructor from an IPv6 address. * * Create an IPvX address from an IPv6 address. * * @param ipv6 the IPv6 address to assign the address value from. */ IPvX(const IPv6& ipv6); /** * Constructor from in_addr structure. * * Note that this address must be of AF_INET family. * * @param from_in_addr the storage to copy the address value from. */ IPvX(const in_addr& from_in_addr); /** * Constructor from in6_addr structure. * * Note that this address must be of AF_INET6 family. * * @param from_in6_addr the storage to copy the address value from. */ IPvX(const in6_addr& from_in6_addr); /** * Constructor from sockaddr structure. * * @param from_sockaddr the storage to copy the address from. */ IPvX(const sockaddr& from_sockaddr) throw (InvalidFamily); /** * Constructor from sockaddr_storage structure. * * @param from_sockaddr_storage the storage to copy the address from. */ IPvX(const sockaddr_storage& from_sockaddr_storage) throw (InvalidFamily); /** * Constructor from sockaddr_in structure. * * @param from_sockaddr_in the storage to copy the address from. * * @exception InvalidFamily thrown if @a from_sockaddr_in is not a * AF_INET sockaddr. */ IPvX(const sockaddr_in& from_sockaddr_in) throw (InvalidFamily); /** * Constructor from sockaddr_in6 structure. * * @param from_sockaddr_in6 the storage to copy the address from. * * @exception InvalidFamily thrown if @a from_sockaddr_in6 is not a * AF_INET6 sockaddr. */ IPvX(const sockaddr_in6& from_sockaddr_in6) throw (InvalidFamily); /** * Constructor from a string. * * @param from_cstring C-style string in the IPv4 dotted decimal * or IPv6 canonical human-readable format used for initialization. */ IPvX(const char *from_cstring) throw (InvalidString); /** * Copy the IPvX raw address to specified memory location. * * @param: to_uint8 the pointer to the memory to copy the address to. * @return the number of copied octets. */ size_t copy_out(uint8_t *to_uint8) const; /** * Copy the IPvX raw address to an in_addr structure. * * Note that this address must be of AF_INET family. * * @param to_in_addr the storage to copy the address to. * @return the number of copied octets. */ size_t copy_out(in_addr& to_in_addr) const throw (InvalidFamily); /** * Copy the IPvX raw address to an in6_addr structure. * * Note that this address must be of AF_INET6 family. * * @param to_in6_addr the storage to copy the address to. * @return the number of copied octets. */ size_t copy_out(in6_addr& to_in6_addr) const throw (InvalidFamily); /** * Copy the IPvX raw address to a sockaddr structure. * * Copy the raw address held within an IPvX instance to an sockaddr * structure and assign appropriately and set fields within sockaddr * appropriately. The underlying address representation may be either * IPv4 or IPv6. * * @param to_sockaddr the storage to copy the address to. * @return the number of copied octets. */ size_t copy_out(sockaddr& to_sockaddr) const throw (InvalidFamily); /** * Copy the IPvX raw address to a sockaddr_storage structure. * * Copy the raw address held within an IPvX instance to an sockaddr_storage * structure and assign appropriately and set fields within * sockaddr_storage appropriately. The underlying address representation * may be either IPv4 or IPv6. * * @param to_sockaddr_storage the storage to copy the address to. * @return the number of copied octets. */ size_t copy_out(sockaddr_storage& to_sockaddr_storage) const throw (InvalidFamily); /** * Copy the IPvX raw address to a sockaddr_in structure. * * Copy the raw address held within an IPvX instance to an * sockaddr_in structure and assign appropriately and set fields * within @a to_sockaddr_in appropriately. * * Note that this address must be of AF_INET family. * * @param to_sockaddr_in the storage to copy the address to. * @return the number of copied octets. */ size_t copy_out(sockaddr_in& to_sockaddr_in) const throw (InvalidFamily); /** * Copy the IPvX raw address to a sockaddr_in6 structure. * * Copy the raw address held within an IPvX instance to a * sockaddr_in6 structure and assign appropriately and set fields * within @a to_sockaddr_in6 appropriately. * * Note that this address must be of AF_INET6 family. * * @param to_sockaddr_in6 the storage to copy the address to. * @return the number of copied octets. */ size_t copy_out(sockaddr_in6& to_sockaddr_in6) const throw (InvalidFamily); /** * Copy a raw address of specified family type from specified memory * location into IPvX structure. * * @param family the address family. * @param from_uint8 the memory address to copy the address from. * @return the number of copied octets. */ size_t copy_in(int family, const uint8_t *from_uint8) throw (InvalidFamily); /** * Copy a raw IPv4 address from a in_addr structure into IPvX structure. * * @param from_in_addr the storage to copy the address from. * @return the number of copied octets. */ size_t copy_in(const in_addr& from_in_addr); /** * Copy a raw IPv6 address from a in6_addr structure into IPvX structure. * * @param from_in6_addr the storage to copy the address from. * @return the number of copied octets. */ size_t copy_in(const in6_addr& from_in6_addr); /** * Copy a raw address from a sockaddr structure into IPvX structure. * * Copy a raw address from a sockaddr structure, and set internal * address family appropriately. * * @param from_sockaddr the storage to copy the address from. * @return the number of copied octets. */ size_t copy_in(const sockaddr& from_sockaddr) throw (InvalidFamily); /** * Copy a raw address from a sockaddr_storage structure into IPvX * structure. * * Copy a raw address from a sockaddr_storage structure, and set internal * address family appropriately. * * @param from_sockaddr_storage the storage to copy the address from. * @return the number of copied octets. */ size_t copy_in(const sockaddr_storage& from_sockaddr_storage) throw (InvalidFamily); /** * Copy a raw address from a sockaddr_in structure into IPvX structure. * * Copy a raw address from a sockaddr_in structure, and set internal * address family appropriately. * * @param from_sockaddr_in the storage to copy the address from. * @return the number of copied octets. * * @exception InvalidFamily thrown if @a from_sockaddr_in is not a * AF_INET sockaddr. */ size_t copy_in(const sockaddr_in& from_sockaddr_in) throw (InvalidFamily); /** * Copy a raw address from sockaddr_in6 structure into IPvX structure. * * Copy a raw address from sockaddr_in6 structure, and set internal * address family appropriately. * * @param from_sockaddr_in6 the storage to copy the address from. * @return the number of copied octets. * * @exception InvalidFamily thrown if @a from_sockaddr_in6 is not a * AF_INET6 sockaddr. */ size_t copy_in(const sockaddr_in6& from_sockaddr_in6) throw (InvalidFamily); /** * Bitwise-Negation Operator * * @return address complement (i.e., all 0s become 1s, and vice-versa). */ IPvX operator~() const; /** * OR Operator * * @param other the right-hand operand to OR with. * @return bitwise OR of two addresses. */ IPvX operator|(const IPvX& other) const throw (InvalidCast); /** * AND Operator * * @param other the right-hand operand to AND with. * @return bitwise AND of two addresses. */ IPvX operator&(const IPvX& other) const throw (InvalidCast); /** * XOR Operator * * @param other the right-hand operand to XOR with. * @return bitwize eXclusive-OR of two addresses. */ IPvX operator^(const IPvX& other) const throw (InvalidCast); /** * Operator << * * @param left_shift the number of bits to shift to the left. * @return IPvX address that is shift bitwise to the left. */ IPvX operator<<(uint32_t left_shift) const; /** * Operator >> * * @param right_shift the number of bits to shift to the right. * @return IPvX address that is shift bitwise to the right. */ IPvX operator>>(uint32_t right_shift) const; /** * Less-Than Operator * * @param other the right-hand operand to compare against. * @return true if the left-hand operand is numerically smaller than the * right-hand operand. */ bool operator<(const IPvX& other) const; /** * Equality Operator * * @param other the right-hand operand to compare against. * @return true if the left-hand operand is numerically same as the * right-hand operand. */ bool operator==(const IPvX& other) const; /** * Not-Equal Operator * * @param other the right-hand operand to compare against. * @return true if the left-hand operand is numerically not same as the * right-hand operand. */ bool operator!=(const IPvX& other) const; /** * Decrement Operator * * The numerical value of this address is decremented by one. * However, if the address value before the decrement was all-0s, * after the decrement its value would be all-1s (i.e., it will * wrap-around). * * @return a reference to this address after it was decremented by one. */ IPvX& operator--(); /** * Increment Operator * * The numerical value of this address is incremented by one. * However, if the address value before the increment was all-1s, * after the increment its value would be all-0s (i.e., it will * wrap-around). * * @return a reference to this address after it was incremented by one. */ IPvX& operator++(); /** * Convert this address from binary form to presentation format. * * @return C++ string with the human-readable ASCII representation * of the address. */ string str() const; /** * Test if this address is numerically zero. * * @return true if the address is numerically zero. */ bool is_zero() const; /** * Test if this address is a valid unicast address. * * @return true if the address is a valid unicast address. */ bool is_unicast() const; /** * Test if this address is a valid multicast address. * * @return true if the address is a valid multicast address. */ bool is_multicast() const; /** * Test if this address belongs to the IPv4 Class A * address space (0.0.0.0/1). * * This method applies only for IPv4, and always returns false for IPv6. * * @return true if the address is a valid Class A address. */ bool is_class_a() const; /** * Test if this address belongs to the IPv4 Class B * address space (128.0.0.0/2). * * This method applies only for IPv4, and always returns false for IPv6. * * @return true if the address is a valid Class B address. */ bool is_class_b() const; /** * Test if this address belongs to the IPv4 Class C * address space (192.0.0.0/3). * * This method applies only for IPv4, and always returns false for IPv6. * * @return true if the address is a valid Class C address. */ bool is_class_c() const; /** * Test if this address belongs to the IPv4 experimental Class E * address space (240.0.0.0/4). * * This method applies only for IPv4, and always returns false for IPv6. * * @return true if the address is a valid experimental address. */ bool is_experimental() const; /** * Test if this address is a valid link-local unicast address. * * @return true if the address is a valid unicast address, * and the scope of the address is link-local. */ bool is_linklocal_unicast() const; /** * Test if this address is a valid interface-local multicast address. * * Note that "node-local" multicast addresses were renamed * to "interface-local" by RFC-3513. * * @return true if the address is a valid multicast address, * and the scope of the address is interface-local. */ bool is_interfacelocal_multicast() const; /** * Test if this address is a valid node-local multicast address. * * Note that "node-local" multicast addresses were renamed * to "interface-local" by RFC-3513. * This method is kept for backward compatibility. * * @return true if the address is a valid multicast address, * and the scope of the address is node-local. */ bool is_nodelocal_multicast() const { return is_interfacelocal_multicast(); } /** * Test if this address is a valid link-local multicast address. * * @return true if the address is a valid multicast address, * and the scope of the address is link-local. */ bool is_linklocal_multicast() const; /** * Test if this address is a valid loopback address. * * @return true if this address is a valid loopback address. */ bool is_loopback() const; /** * Get the address octet-size. * * Note that this is a static function and is to be used without * a particular object. Example: * size_t my_size = IPvX::addr_bytelen(my_family); * * @param family the address family. * @return address size in number of octets for an address of * address family of @ref family. */ static size_t addr_bytelen(int family) throw (InvalidFamily); /** * Get the address octet-size for this address. * * Note that this is not a static function, hence it has to be used with * a particular object. Example: * size_t my_size = ipvx.addr_bytelen(); * * @param family the address family. * @return address size in number of octets for this IPvX address. */ size_t addr_bytelen() const { return IPvX::addr_bytelen(_af); } /** * Get the address bit-length. * * Note that this is a static function and is to be used without * a particular object. Example: * uint32_t my_bitlen = IPvX::addr_bitlen(my_family); * * @param family the address family. * @return address size in number of bits for an address of * address family of @ref family. */ static uint32_t addr_bitlen(int family) throw (InvalidFamily) { return uint32_t(8 * sizeof(uint8_t) * addr_bytelen(family)); } /** * Get the address bit-length for this address. * * Note that this is not a static function, hence it has to be used with * a particular object. Example: * uint32_t my_bitlen = ipvx.addr_bitlen(); * * @param family the address family. * @return address size in number of bits for this IPvX address. */ uint32_t addr_bitlen() const { return uint32_t(8 * sizeof(uint8_t) * addr_bytelen()); } /** * Get the mask length for the multicast base address. * * Note that this is a static function and can be used without * a particular object. Example: * uint32_t my_len = IPvX::ip_multicast_base_address_mask_len(my_family); * * @param family the address family. * @return the multicast base address mask length for an address of * address family of @ref family. */ static uint32_t ip_multicast_base_address_mask_len(int family) throw (InvalidFamily); /** * Get the mask length for the multicast base address for this address. * * Note that this is not a static function, hence it has to be used with * a particular object. Example: * size_t my_len = ipvx.ip_multicast_base_address_mask_len(); * * @param family the address family. * @return the multicast base address mask length for this IPvX address. */ uint32_t ip_multicast_base_address_mask_len() const { return IPvX::ip_multicast_base_address_mask_len(_af); } /** * Get the mask length for the Class A base address. * * This method applies only for IPv4. * Note that this is a static function and can be used without * a particular object. Example: * uint32_t my_len = IPvX::ip_class_a_base_address_mask_len(my_family); * * @param family the address family. * @return the Class A base address mask length for an address of * address family of @ref family. */ static uint32_t ip_class_a_base_address_mask_len(int family) throw (InvalidFamily); /** * Get the mask length for the Class A base address for this address. * * This method applies only for IPv4. * Note that this is not a static function, hence it has to be used with * a particular object. Example: * size_t my_len = ipvx.ip_class_a_base_address_mask_len(); * * @param family the address family. * @return the Class A base address mask length for this IPvX address. */ uint32_t ip_class_a_base_address_mask_len() const throw (InvalidFamily) { return IPvX::ip_class_a_base_address_mask_len(_af); } /** * Get the mask length for the Class B base address. * * This method applies only for IPv4. * Note that this is a static function and can be used without * a particular object. Example: * uint32_t my_len = IPvX::ip_class_b_base_address_mask_len(my_family); * * @param family the address family. * @return the Class B base address mask length for an address of * address family of @ref family. */ static uint32_t ip_class_b_base_address_mask_len(int family) throw (InvalidFamily); /** * Get the mask length for the Class B base address for this address. * * This method applies only for IPv4. * Note that this is not a static function, hence it has to be used with * a particular object. Example: * size_t my_len = ipvx.ip_class_b_base_address_mask_len(); * * @param family the address family. * @return the Class B base address mask length for this IPvX address. */ uint32_t ip_class_b_base_address_mask_len() const throw (InvalidFamily) { return IPvX::ip_class_b_base_address_mask_len(_af); } /** * Get the mask length for the Class C base address. * * This method applies only for IPv4. * Note that this is a static function and can be used without * a particular object. Example: * uint32_t my_len = IPvX::ip_class_c_base_address_mask_len(my_family); * * @param family the address family. * @return the Class C base address mask length for an address of * address family of @ref family. */ static uint32_t ip_class_c_base_address_mask_len(int family) throw (InvalidFamily); /** * Get the mask length for the Class C base address for this address. * * This method applies only for IPv4. * Note that this is not a static function, hence it has to be used with * a particular object. Example: * size_t my_len = ipvx.ip_class_c_base_address_mask_len(); * * @param family the address family. * @return the Class C base address mask length for this IPvX address. */ uint32_t ip_class_c_base_address_mask_len() const throw (InvalidFamily) { return IPvX::ip_class_c_base_address_mask_len(_af); } /** * Get the mask length for the experimental base address. * * This method applies only for IPv4. * Note that this is a static function and can be used without * a particular object. Example: * uint32_t my_len = IPvX::ip_experimental_base_address_mask_len(my_family); * * @param family the address family. * @return the experimental base address mask length for an address of * address family of @ref family. */ static uint32_t ip_experimental_base_address_mask_len(int family) throw (InvalidFamily); /** * Get the mask length for the experimental base address for this address. * * This method applies only for IPv4. * Note that this is not a static function, hence it has to be used with * a particular object. Example: * size_t my_len = ipvx.ip_experimental_base_address_mask_len(); * * @param family the address family. * @return the experimental base address mask length for this IPvX address. */ uint32_t ip_experimental_base_address_mask_len() const throw (InvalidFamily) { return IPvX::ip_experimental_base_address_mask_len(_af); } /** * Make an IPvX mask prefix. * * @param family the address family. * @param mask_len the length of the mask to create. * @return a new IPvX address that contains a mask of length @ref mask_len. */ static IPvX make_prefix(int family, uint32_t mask_len) throw (InvalidFamily, InvalidNetmaskLength); /** * Make an IPvX mask prefix for the address family of this address. * * @param mask_len the length of the mask to create. * @return a new IPvX address that contains a mask of length @ref mask_len. */ IPvX make_prefix(uint32_t mask_len) const throw (InvalidNetmaskLength) { return IPvX::make_prefix(_af, mask_len); } /** * Make an IPvX address prefix. * * @param prefix_len the length of the mask of the prefix to create. * @return a new IPvX address created by masking this address with a mask * of length @ref prefix_len. */ IPvX mask_by_prefix_len(uint32_t prefix_len) const throw (InvalidNetmaskLength); /** * Get the mask length. * * @return the prefix length of the contiguous mask presumably stored * as an IPvX address. */ uint32_t mask_len() const; /** * Test if this address is IPv4 address. * * @return true if the address is IPv4. */ bool is_ipv4() const { return (_af == AF_INET); } /** * Test if this address is IPv6 address. * * @return true if the address is IPv6. */ bool is_ipv6() const { return (_af == AF_INET6); } /** * Get IPv4 address. * * @return IPv4 address contained with IPvX structure. */ IPv4 get_ipv4() const throw (InvalidCast); /** * Get IPv6 address. * * @return IPv6 address contained with IPvX structure. */ IPv6 get_ipv6() const throw (InvalidCast); /** * Assign address value to an IPv4 address. * * @param to_ipv4 IPv4 address to be assigned IPv4 value contained * within this address. */ void get(IPv4& to_ipv4) const throw (InvalidCast) { to_ipv4 = get_ipv4(); } /** * Assign address value to an IPv6 address. * * @param to_ipv6 IPv6 address to be assigned IPv4 value contained * within this address. */ void get(IPv6& to_ipv6) const throw (InvalidCast) { to_ipv6 = get_ipv6(); } /** * Get the address family. * * @return the address family of this address (AF_INET or AF_INET6). */ int af() const { return (_af); } /** * Get the IP protocol version. * * @return the IP protocol version of this address. */ uint32_t ip_version() const throw (InvalidFamily); /** * Get the human-readable string with the IP protocol version. * * @return the human-readable string with the IP protocol version of * this address. */ const string& ip_version_str() const throw (InvalidFamily); /** * Extract bits from an address. * * @param lsb starting bit position (from the right) to extract. * @param len number of bits to extract. The maximum value is 32. * @return the first @ref len bits starting from the rightmost * position @ref lsb. The returned bits are in host order. */ uint32_t bits(uint32_t lsb, uint32_t len) const throw (InvalidFamily); /** * Count the number of bits that are set in this address. * * @return the number of bits that are set in this address. */ uint32_t bit_count() const; /** * Count the number of leading zeroes in this address. * * @return the number of leading zeroes in this address. */ uint32_t leading_zero_count() const; /** * Pre-defined IPvX address constants. */ static const IPvX& ZERO(int family) throw (InvalidFamily); static const IPvX& ANY(int family) throw (InvalidFamily); static const IPvX& ALL_ONES(int family) throw (InvalidFamily); static const IPvX& LOOPBACK(int family) throw (InvalidFamily); static const IPvX& MULTICAST_BASE(int family) throw (InvalidFamily); static const IPvX& MULTICAST_ALL_SYSTEMS(int family) throw (InvalidFamily); static const IPvX& MULTICAST_ALL_ROUTERS(int family) throw (InvalidFamily); static const IPvX& DVMRP_ROUTERS(int family) throw (InvalidFamily); static const IPvX& OSPFIGP_ROUTERS(int family) throw (InvalidFamily); static const IPvX& OSPFIGP_DESIGNATED_ROUTERS(int family) throw (InvalidFamily); static const IPvX& RIP2_ROUTERS(int family) throw (InvalidFamily); static const IPvX& PIM_ROUTERS(int family) throw (InvalidFamily); static const IPvX& SSM_ROUTERS(int family) throw (InvalidFamily); static const IPvX& CLASS_A_BASE(int family) throw (InvalidFamily); static const IPvX& CLASS_B_BASE(int family) throw (InvalidFamily); static const IPvX& CLASS_C_BASE(int family) throw (InvalidFamily); static const IPvX& EXPERIMENTAL_BASE(int family) throw (InvalidFamily); private: friend class IPv4; friend class IPv6; uint32_t _addr[4]; // Underlay address value for casting to IPv4 and IPv6 int _af; // The address family AF_INET or AF_INET6 }; inline IPv4 IPvX::get_ipv4() const throw (InvalidCast) { if (_af == AF_INET) return IPv4(_addr[0]); xorp_throw(InvalidCast, "Miscast as IPv4"); } inline IPv6 IPvX::get_ipv6() const throw (InvalidCast) { if (_af == AF_INET6) return IPv6(&_addr[0]); xorp_throw(InvalidCast, "Miscast as IPv6"); } inline uint32_t IPvX::bits(uint32_t lsb, uint32_t len) const throw (InvalidFamily) { uint32_t mask = ~(0xffffffffU << len); if (len >= 32) mask = 0xffffffffU; // XXX: shifting with >= 32 bits is undefined if (_af == AF_INET) return ntohl((*this >> lsb)._addr[0]) & mask; if (_af == AF_INET6) return ntohl((*this >> lsb)._addr[3]) & mask; xorp_throw(InvalidFamily, _af); return (0x0U); } inline uint32_t IPvX::bit_count() const { if (_af == AF_INET) return get_ipv4().bit_count(); return get_ipv6().bit_count(); } inline uint32_t IPvX::leading_zero_count() const { if (_af == AF_INET) return get_ipv4().leading_zero_count(); return get_ipv6().leading_zero_count(); } // // Front-end functions that can be used by C programs. // TODO: this is not the right place for this. // We need a system for exporting API to C programs. // inline size_t family2addr_bytelen(const int family) { return IPvX::addr_bytelen(family); } inline uint32_t family2addr_bitlen(const int family) { return IPvX::addr_bitlen(family); } #endif // __LIBXORP_IPVX_HH__ xorp/libxorp/safe_callback_obj.cc0000664000076400007640000000351211540224227017232 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "libxorp_module.h" #include "libxorp/xorp.h" #include "safe_callback_obj.hh" // ---------------------------------------------------------------------------- // SafeCallbackBase implementation SafeCallbackBase::SafeCallbackBase(CallbackSafeObject* o) : _cso(o) { _cso->ref_cb(this); } SafeCallbackBase::~SafeCallbackBase() { if (valid()) invalidate(); } void SafeCallbackBase::invalidate() { if (valid()) { _cso->unref_cb(this); _cso = 0; } } bool SafeCallbackBase::valid() const { return _cso != 0; } // ---------------------------------------------------------------------------- // CallbackSafeObject implementation CallbackSafeObject::~CallbackSafeObject() { vector::iterator i = _cbs.begin(); while (_cbs.empty() == false) { SafeCallbackBase* scb = *i; if (scb == 0) { _cbs.erase(_cbs.begin()); continue; } if (scb->valid()) { scb->invalidate(); } } } xorp/libxorp/create_buildinfo.sh0000775000076400007640000000667711642423731017217 0ustar greearbgreearb#!/bin/sh # This script auto-creates the build_info.cc file. # Used by: scons ... enable_buildinfo=yes # to compile some build information into xorp. # # # Usage: ./libxorp/create_buildinfo.sh # If build_info.cc is newer than create_buildinfo.sh, and # if the git tag hasn't changed, then do not re-create # build_info.cc # Uses sed, git, date, and uname commands, if available. # git history is only available if compiled within a git tree. cd libxorp BINFO=build_info.cc if [ -f $BINFO ] then if [ create_buildinfo.sh -ot $BINFO ] then if [ -f last_git_md5sum.txt ] then git log -1 --pretty=format:%h > tst_git_md5sum.txt if diff -q last_git_md5sum.txt tst_git_md5sum.txt > /dev/null then # Files are the same, do nothing. echo "Not re-creating $BINFO file." cd - exit 0 else echo "Re-creating $BINFO: md5sums changed." fi else echo "Re-creating $BINFO: old md5sum doesn't exist." fi else echo "Re-creating $BINFO: builder script newer than build_info.cc" fi else echo "Creating $BINFO: file doesn't exist yet." fi cat build_info.prefix > $BINFO if uname -mrspn > /dev/null 2>&1 then echo "const char* BuildInfo::getBuildMachine() { return \"`uname -mrspn`\"; }" >> $BINFO else echo "Warning: uname -mrspn does not function on this system." echo "const char* BuildInfo::getBuildMachine() { return \"Unknown\"; }" >> $BINFO fi echo "" >> $BINFO echo "const char* BuildInfo::getBuilder() { return \"${USER}\"; }" >> $BINFO echo "" >> $BINFO if date > /dev/null 2>&1 then echo "const char* BuildInfo::getBuildDate() { return \"`date`\"; }" >> $BINFO else echo "Warning: 'date' does not function on this system." echo "const char* BuildInfo::getBuildDate() { return \"Unknown\"; }" >> $BINFO fi echo "" >> $BINFO if date '+%F %H:%M' > /dev/null 2>&1 then echo "const char* BuildInfo::getShortBuildDate() { return \"`date '+%F %H:%M'`\"; }" >> $BINFO else echo "Warning: date +%F %H:%M does not function on this system." echo "const char* BuildInfo::getShortBuildDate() { return \"Unknown\"; }" >> $BINFO fi echo "" >> $BINFO if which sed > /dev/null 2>&1 && which git > /dev/null 2>&1 then if [ -x `which sed` ] && [ -x `which git` ] then if git log -1 > /dev/null 2>&1 then cat >> "${BINFO}" << EOF const char* BuildInfo::getGitLog() { return `git log -3 --abbrev=8 --abbrev-commit --pretty=oneline | sed -e 's|\\\\|\\\\\\\\|g' -e 's|"|\\\\"|g' -e 's|\(.*\)|"\1\\\n"|'`; } EOF else echo "NOTE: Not a git repository, no git history in build-info." cat >> "${BINFO}" << EOF const char* BuildInfo::getGitLog() { return "Cannot detect, not a git repository"; } EOF fi else echo "NOTE: No functional sed and/or git, no git history in build-info." cat >> "${BINFO}" << EOF const char* BuildInfo::getGitLog() { return "Cannot detect, sed and/or git is not executable"; } EOF fi else echo "NOTE: No sed and/or git, no git history in build-info." cat >> "${BINFO}" << EOF const char* BuildInfo::getGitLog() { return "Cannot detect, sed and/or git is not available"; } EOF fi echo "const char* BuildInfo::getGitVersion() { return " >> $BINFO if which git > /dev/null 2>&1 then if git log -1 > /dev/null 2>&1 then git log -1 --pretty=format:%h > last_git_md5sum.txt echo "\"`git log -1 --pretty=format:%h`\"; }" >> $BINFO else echo "\"00000000\"; }" >> $BINFO fi else echo "\"00000000\"; }" >> $BINFO fi cd - exit 0 xorp/libxorp/tests/0000775000076400007640000000000011633463661014512 5ustar greearbgreearbxorp/libxorp/tests/test_vif.cc0000664000076400007640000004511511540224230016632 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "libxorp_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/exceptions.hh" #ifdef HAVE_GETOPT_H #include #endif #include "vif.hh" // // XXX: MODIFY FOR YOUR TEST PROGRAM // static const char *program_name = "test_vif"; static const char *program_description = "Test Vif address class"; static const char *program_version_id = "0.1"; static const char *program_date = "December 4, 2002"; static const char *program_copyright = "See file LICENSE"; static const char *program_return_value = "0 on success, 1 if test error, 2 if internal error"; static bool s_verbose = false; bool verbose() { return s_verbose; } void set_verbose(bool v) { s_verbose = v; } static int s_failures = 0; bool failures() { return s_failures; } void incr_failures() { s_failures++; } #include "xorp_tests.hh" /** * Print program info to output stream. * * @param stream the output stream the print the program info to. */ static void print_program_info(FILE *stream) { fprintf(stream, "Name: %s\n", program_name); fprintf(stream, "Description: %s\n", program_description); fprintf(stream, "Version: %s\n", program_version_id); fprintf(stream, "Date: %s\n", program_date); fprintf(stream, "Copyright: %s\n", program_copyright); fprintf(stream, "Return: %s\n", program_return_value); } /** * Print program usage information to the stderr. * * @param progname the name of the program. */ static void usage(const char* progname) { print_program_info(stderr); fprintf(stderr, "usage: %s [-v] [-h]\n", progname); fprintf(stderr, " -h : usage (this message)\n"); fprintf(stderr, " -v : verbose output\n"); } /** * Test VifAddr valid constructors. */ void test_vif_addr_valid_constructors() { // // Constructor for a given address. // VifAddr vif_addr1(IPvX("11.11.11.11")); verbose_match(vif_addr1.str(), "addr: 11.11.11.11 subnet: 0.0.0.0/0 broadcast: 0.0.0.0 peer: 0.0.0.0"); // // Constructor for a given address, and its associated addresses. // VifAddr vif_addr2(IPvX("22.22.22.22"), IPvXNet("22.22.22.0/24"), IPvX("22.22.22.255"), IPvX("0.0.0.0")); verbose_match(vif_addr2.str(), "addr: 22.22.22.22 subnet: 22.22.22.0/24 broadcast: 22.22.22.255 peer: 0.0.0.0"); VifAddr vif_addr3(IPvX("33.33.33.33"), IPvXNet("0.0.0.0/0"), IPvX("0.0.0.0"), IPvX("33.33.33.44")); verbose_match(vif_addr3.str(), "addr: 33.33.33.33 subnet: 0.0.0.0/0 broadcast: 0.0.0.0 peer: 33.33.33.44"); } /** * Test VifAddr invalid constructors. */ void test_vif_addr_invalid_constructors() { // // Currently, there are no VifAddr invalid constructors. // } /** * Test VifAddr methods. */ void test_vif_addr_methods() { VifAddr vif_addr_a(IPvX("11.11.11.11"), IPvXNet("11.11.11.0/24"), IPvX("11.11.11.255"), IPvX("0.0.0.0")); VifAddr vif_addr_b(IPvX("22.22.22.22"), IPvXNet("22.22.22.0/24"), IPvX("22.22.22.255"), IPvX("0.0.0.0")); VifAddr vif_addr_c(IPvX("33.33.33.33"), IPvXNet("0.0.0.0/0"), IPvX("0.0.0.0"), IPvX("33.33.33.44")); // // Get the interface address. // verbose_match(vif_addr_a.addr().str(), "11.11.11.11"); // // Get the subnet address. // verbose_match(vif_addr_a.subnet_addr().str(), "11.11.11.0/24"); // // Get the broadcast address. // verbose_match(vif_addr_a.broadcast_addr().str(), "11.11.11.255"); // // Get the peer address. // verbose_match(vif_addr_c.peer_addr().str(), "33.33.33.44"); // // Set the interface address. // vif_addr_a.set_addr(IPvX("33.33.33.33")); verbose_match(vif_addr_a.addr().str(), "33.33.33.33"); // // Set the subnet address. // vif_addr_a.set_subnet_addr(IPvXNet("33.33.33.0/24")); verbose_match(vif_addr_a.subnet_addr().str(), "33.33.33.0/24"); // // Set the broadcast address. // vif_addr_a.set_broadcast_addr(IPvX("33.33.33.255")); verbose_match(vif_addr_a.broadcast_addr().str(), "33.33.33.255"); // // Set the peer address. // vif_addr_c.set_peer_addr(IPvX("33.33.33.55")); verbose_match(vif_addr_c.peer_addr().str(), "33.33.33.55"); // // Test whether is the same interface address. // verbose_assert(vif_addr_b.is_my_addr(IPvX("22.22.22.22")), "is_my_addr()"); verbose_assert(! vif_addr_b.is_my_addr(IPvX("22.22.22.33")), "is_my_addr()"); // // Test whether a subnet address is a subset of my subnet address. // verbose_assert(vif_addr_b.is_same_subnet(IPvXNet("22.22.22.0/24")), "is_same_subnet(IPvXNet)"); verbose_assert(vif_addr_b.is_same_subnet(IPvXNet("22.22.22.128/25")), "is_same_subnet(IPvXNet)"); verbose_assert(! vif_addr_b.is_same_subnet(IPvXNet("22.22.33.0/24")), "is_same_subnet(IPvXNet)"); // // Test whether an address belongs to my subnet. // verbose_assert(vif_addr_b.is_same_subnet(IPvX("22.22.22.33")), "is_same_subnet(IPvX)"); verbose_assert(! vif_addr_b.is_same_subnet(IPvX("22.22.33.33")), "is_same_subnet(IPvX)"); } /** * Test VifAddr operators. */ void test_vif_addr_operators() { VifAddr vif_addr_a(IPvX("11.11.11.11"), IPvXNet("11.11.11.0/24"), IPvX("11.11.11.255"), IPvX("0.0.0.0")); VifAddr vif_addr_b(IPvX("22.22.22.22"), IPvXNet("22.22.22.0/24"), IPvX("22.22.22.255"), IPvX("0.0.0.0")); // // Equality Operator // verbose_assert(vif_addr_a == vif_addr_a, "operator=="); verbose_assert(!(vif_addr_a == vif_addr_b), "operator=="); // // Not-Equal Operator // verbose_assert(!(vif_addr_a != vif_addr_a), "operator!="); verbose_assert(vif_addr_a != vif_addr_b, "operator!="); } /** * Test Vif valid constructors. */ void test_vif_valid_constructors() { Vif vif1("vif1"); Vif vif2("vif2", "ifname2"); Vif vif3(vif2); UNUSED(vif1); UNUSED(vif2); UNUSED(vif3); } /** * Test Vif invalid constructors. */ void test_vif_invalid_constructors() { // // Currently, there are no Vif invalid constructors. // } /** * Test Vif methods. */ void test_vif_methods() { Vif vif1("vif1", "ifname1"); // // Get the vif name. // verbose_match(vif1.name(), "vif1"); // // Get the name of the physical interface associated with vif. // verbose_match(vif1.ifname(), "ifname1"); // // Set the name of the physical interface associated with vif. // vif1.set_ifname("ifname1_1"); verbose_match(vif1.ifname(), "ifname1_1"); vif1.set_ifname("ifname1"); // // Set and get the physical interface index. // vif1.set_pif_index(1); verbose_assert(vif1.pif_index() == 1, "pif_index()"); // // Set and get the virtual interface index. // vif1.set_vif_index(2); verbose_assert(vif1.vif_index() == 2, "vif_index()"); // // Test if this vif is a PIM Register interface. // vif1.set_pim_register(true); verbose_assert(vif1.is_pim_register(), "is_pim_register()"); vif1.set_pim_register(false); verbose_assert(! vif1.is_pim_register(), "is_pim_register()"); // // Test if this vif is a point-to-point interface. // vif1.set_p2p(true); verbose_assert(vif1.is_p2p(), "is_p2p()"); vif1.set_p2p(false); verbose_assert(! vif1.is_p2p(), "is_p2p()"); // // Test if this vif is a loopback interface. // vif1.set_loopback(true); verbose_assert(vif1.is_loopback(), "is_loopback()"); vif1.set_loopback(false); verbose_assert(! vif1.is_loopback(), "is_loopback()"); // // Test if this vif is a discard interface. // vif1.set_discard(true); verbose_assert(vif1.is_discard(), "is_discard()"); vif1.set_discard(false); verbose_assert(! vif1.is_discard(), "is_discard()"); // // Test if this vif is an unreachable interface. // vif1.set_unreachable(true); verbose_assert(vif1.is_unreachable(), "is_unreachable()"); vif1.set_unreachable(false); verbose_assert(! vif1.is_unreachable(), "is_unreachable()"); // // Test if this vif is a management interface. // vif1.set_management(true); verbose_assert(vif1.is_management(), "is_management()"); vif1.set_management(false); verbose_assert(! vif1.is_management(), "is_management()"); // // Test if this vif is multicast capable. // vif1.set_multicast_capable(true); verbose_assert(vif1.is_multicast_capable(), "is_multicast_capable()"); vif1.set_multicast_capable(false); verbose_assert(! vif1.is_multicast_capable(), "is_multicast_capable()"); // // Test if this vif is broadcast capable. // vif1.set_broadcast_capable(true); verbose_assert(vif1.is_broadcast_capable(), "is_broadcast_capable()"); vif1.set_broadcast_capable(false); verbose_assert(! vif1.is_broadcast_capable(), "is_broadcast_capable()"); // // Test if this vif is broadcast capable. // vif1.set_broadcast_capable(true); verbose_assert(vif1.is_broadcast_capable(), "is_broadcast_capable()"); vif1.set_broadcast_capable(false); verbose_assert(! vif1.is_broadcast_capable(), "is_broadcast_capable()"); // // Test if the underlying vif is UP. // vif1.set_underlying_vif_up(true); verbose_assert(vif1.is_underlying_vif_up(), "is_underlying_vif_up()"); vif1.set_underlying_vif_up(false); verbose_assert(! vif1.is_underlying_vif_up(), "is_underlying_vif_up()"); // // Test the MTU. // vif1.set_mtu(2000); verbose_assert(vif1.mtu() == 2000, "mtu()"); // // Get the default list of all addresses for this vif. // list addr_list; addr_list = vif1.addr_list(); verbose_assert(addr_list.size() == 0, "default addr_list()"); // // Get the first vif address when no addresses were added. // verbose_assert(vif1.addr_ptr() == NULL, "default addr_ptr()"); } /** * Test Vif address manipulation. */ void test_vif_manipulate_address() { Vif vif1("vif1", "ifname1"); VifAddr vif_addr_a(IPvX("11.11.11.11"), IPvXNet("11.11.11.0/24"), IPvX("11.11.11.255"), IPvX("0.0.0.0")); VifAddr vif_addr_aa(IPvX("11.11.11.11"), IPvXNet("11.11.11.128/25"), IPvX("11.11.11.127"), IPvX("0.0.0.0")); VifAddr vif_addr_b(IPvX("22.22.22.22"), IPvXNet("22.22.22.0/24"), IPvX("22.22.22.255"), IPvX("0.0.0.0")); VifAddr vif_addr_c(IPvX("33.33.33.33"), IPvXNet("0.0.0.0/0"), IPvX("0.0.0.0"), IPvX("33.33.33.44")); // // Assign vif capabilities // vif1.set_broadcast_capable(true); // // Add a VifAddr address to the interface. // verbose_assert(vif1.add_address(vif_addr_a) == XORP_OK, "add_address()"); verbose_assert(vif1.add_address(vif_addr_a) == XORP_ERROR, "add_address()"); verbose_assert(vif1.addr_list().size() == 1, "addr_list()"); verbose_assert(vif1.addr_ptr() != NULL, "addr_ptr()"); verbose_assert(*vif1.addr_ptr() == vif_addr_a.addr(), "addr_ptr()"); // // Add an IPvX address and all related information to the interface. // verbose_assert(vif1.add_address(vif_addr_b.addr(), vif_addr_b.subnet_addr(), vif_addr_b.broadcast_addr(), vif_addr_b.peer_addr()) == XORP_OK, "add_address()"); verbose_assert(vif1.add_address(vif_addr_b.addr(), vif_addr_b.subnet_addr(), vif_addr_b.broadcast_addr(), vif_addr_b.peer_addr()) == XORP_ERROR, "add_address()"); verbose_assert(vif1.addr_list().size() == 2, "addr_list()"); verbose_assert(vif1.addr_ptr() != NULL, "addr_ptr()"); verbose_assert(*vif1.addr_ptr() == vif_addr_a.addr(), "addr_ptr()"); // // Add an IPvX address to the interface. // verbose_assert(vif1.add_address(vif_addr_c.addr()) == XORP_OK, "add_address()"); verbose_assert(vif1.add_address(vif_addr_c.addr()) == XORP_ERROR, "add_address()"); verbose_assert(vif1.addr_list().size() == 3, "addr_list()"); verbose_assert(vif1.addr_ptr() != NULL, "addr_ptr()"); verbose_assert(*vif1.addr_ptr() == vif_addr_a.addr(), "addr_ptr()"); // // Delete an IPvX address from the interface. // verbose_assert(vif1.delete_address(vif_addr_c.addr()) == XORP_OK, "delete_address()"); verbose_assert(vif1.delete_address(vif_addr_c.addr()) == XORP_ERROR, "delete_address()"); verbose_assert(vif1.addr_list().size() == 2, "addr_list()"); verbose_assert(vif1.addr_ptr() != NULL, "addr_ptr()"); verbose_assert(*vif1.addr_ptr() == vif_addr_a.addr(), "addr_ptr()"); // // Find a VifAddr that corresponds to an IPvX address. // verbose_assert(vif1.find_address(vif_addr_a.addr()) != NULL, "find_address()"); verbose_assert(*vif1.find_address(vif_addr_a.addr()) == vif_addr_a, "find_address()"); // // Test if an IPvX address belongs to this vif. // verbose_assert(vif1.is_my_addr(vif_addr_a.addr()), "is_my_addr()"); verbose_assert(! vif1.is_my_addr(vif_addr_c.addr()), "is_my_addr()"); // // Test if an VifAddr is belongs to this vif. // verbose_assert(vif1.is_my_vif_addr(vif_addr_a), "is_my_vif_addr()"); verbose_assert(! vif1.is_my_vif_addr(vif_addr_c), "is_my_vif_addr()"); // // Test if a subnet address is a subset of one of the subnet // addresses of this vif. // verbose_assert(vif1.is_same_subnet(vif_addr_a.subnet_addr()), "is_same_subnet(IPvXNet)"); verbose_assert(vif1.is_same_subnet(vif_addr_aa.subnet_addr()), "is_same_subnet(IPvXNet)"); verbose_assert(! vif1.is_same_subnet(IPvXNet("99.99.99.0/24")), "is_same_subnet(IPvXNet)"); // // Test if an IPvX address belongs to one of the subnet // addresses of this vif. // verbose_assert(vif1.is_same_subnet(IPvX("11.11.11.22")), "is_same_subnet(IPvX)"); verbose_assert(! vif1.is_same_subnet(IPvX("99.99.99.99")), "is_same_subnet(IPvX)"); // // Change vif capabilities and try again // vif1.set_broadcast_capable(false); vif1.set_p2p(true); verbose_assert(vif1.is_same_subnet(vif_addr_a.subnet_addr()), "is_same_subnet(IPvXNet)"); verbose_assert(vif1.is_same_subnet(IPvX("11.11.11.22")), "is_same_subnet(IPvX)"); // Restore the vif capabilities vif1.set_broadcast_capable(true); vif1.set_p2p(false); // // Assign vif capabilities // vif1.set_broadcast_capable(false); vif1.set_p2p(true); vif1.add_address(vif_addr_c); // // Test if a given address belongs to the same point-to-point link // as this vif. // verbose_assert(vif1.is_same_p2p(IPvX("33.33.33.33")), "is_same_p2p(IPvX)"); verbose_assert(vif1.is_same_p2p(IPvX("33.33.33.44")), "is_same_p2p(IPvX)"); verbose_assert(! vif1.is_same_p2p(IPvX("33.33.33.99")), "is_same_p2p(IPvX)"); // // Convert this Vif from binary form to presentation format. // verbose_match(vif1.str(), "Vif[vif1] pif_index: 0 vif_index: 0 addr: 11.11.11.11 subnet: 11.11.11.0/24 broadcast: 11.11.11.255 peer: 0.0.0.0 addr: 22.22.22.22 subnet: 22.22.22.0/24 broadcast: 22.22.22.255 peer: 0.0.0.0 addr: 33.33.33.33 subnet: 0.0.0.0/0 broadcast: 0.0.0.0 peer: 33.33.33.44 Flags: P2P MTU: 0"); } /** * Test Vif operators. */ void test_vif_operators() { Vif vif1("vif1", "ifname1"); Vif vif2("vif2"); VifAddr vif_addr_a(IPvX("11.11.11.11"), IPvXNet("11.11.11.0/24"), IPvX("11.11.11.255"), IPvX("0.0.0.0")); VifAddr vif_addr_b(IPvX("22.22.22.22"), IPvXNet("22.22.22.0/24"), IPvX("22.22.22.255"), IPvX("0.0.0.0")); // // Equality Operator // verbose_assert(vif1 == vif1, "operator=="); verbose_assert(!(vif1 == vif2), "operator=="); // // Not-Equal Operator // verbose_assert(!(vif1 != vif1), "operator!="); verbose_assert(vif1 != vif2, "operator!="); // // Equality Operator // vif1.add_address(vif_addr_a); vif1.add_address(vif_addr_b); Vif vif3(vif1); verbose_assert(vif1 == vif3, "operator=="); // // Not-Equal Operator // verbose_assert(!(vif1 != vif3), "operator!="); } int main(int argc, char * const argv[]) { int ret_value = 0; // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); int ch; while ((ch = getopt(argc, argv, "hv")) != -1) { switch (ch) { case 'v': set_verbose(true); break; case 'h': case '?': default: usage(argv[0]); xlog_stop(); xlog_exit(); if (ch == 'h') return (0); else return (1); } } argc -= optind; argv += optind; XorpUnexpectedHandler x(xorp_unexpected_handler); try { test_vif_addr_valid_constructors(); test_vif_addr_invalid_constructors(); test_vif_addr_methods(); test_vif_addr_operators(); test_vif_valid_constructors(); test_vif_invalid_constructors(); test_vif_methods(); test_vif_manipulate_address(); test_vif_operators(); ret_value = failures() ? 1 : 0; } catch (...) { // Internal error xorp_print_standard_exceptions(); ret_value = 2; } // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); return (ret_value); } xorp/libxorp/tests/test_service.cc0000664000076400007640000001725311540224230017510 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "libxorp_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/exceptions.hh" #include "libxorp/eventloop.hh" #ifdef HAVE_GETOPT_H #include #endif #include "service.hh" // // This test is fairly straightforward. We implement two classes // TestService and TestServiceChangeObserver. TestService is derived // from ServiceBase and implements ServiceBase::startup() and // ServiceBase::shutdown(). With both methods, invocation sets the // current status to the relevant intermediate state (SERVICE_STARTING or // SERVICE_SHUTTING_DOWN) and also sets a timer. When the timer expires it // sets the current state to the requested state (SERVICE_RUNNING or // SERVICE_SHUTDOWN). The timer expiry time is after TRANS_MS milliseconds. // // TestServiceChangeObserver assumes it is going to be informed of // these changes and verifies that the origin and order of changes // matches those it expects. // // The main body of the test is in test_main. It instantiates and // associates instances of TestService and TestServiceChangeObserver. // It uses timers to trigger the calling of TestService::startup() and // TestService::shutdown(). // // ---------------------------------------------------------------------------- // Constants static const uint32_t TRANS_MS = 100; // Transition time (millisecs) static const uint32_t EXIT_MS = 5 * TRANS_MS; // Time to exit (millisecs) // ---------------------------------------------------------------------------- // Verbose output static bool s_verbose = false; bool verbose() { return s_verbose; } void set_verbose(bool v) { s_verbose = v; } static int s_failures = 0; bool failures() { return (s_failures)? (true) : (false); } void incr_failures() { s_failures++; } void reset_failures() { s_failures = 0; } #include "libxorp/xorp_tests.hh" // ---------------------------------------------------------------------------- // TestService implementation class TestService : public ServiceBase { public: TestService(EventLoop& e) : _e(e) {} int startup() { set_status(SERVICE_STARTING, "Waiting for timed start event"); _xt = _e.new_oneoff_after_ms(TRANS_MS, callback(this, &TestService::go_running)); return (XORP_OK); } int shutdown() { set_status(SERVICE_SHUTTING_DOWN, "Waiting for timed shutdown event"); _xt = _e.new_oneoff_after_ms(TRANS_MS, callback(this, &TestService::go_shutdown)); return (XORP_OK); } protected: void go_running() { set_status(SERVICE_RUNNING); } void go_shutdown() { set_status(SERVICE_SHUTDOWN); } protected: EventLoop& _e; XorpTimer _xt; // Timer for simulated status transitions }; // ---------------------------------------------------------------------------- // TestServiceChangeObserver implementation class TestServiceChangeObserver : public ServiceChangeObserverBase { public: TestServiceChangeObserver(const TestService* expected_service) : _s(expected_service), _cc(0), _bc(0) { } void status_change(ServiceBase* service, ServiceStatus old_status, ServiceStatus new_status) { if (service != _s) { verbose_log("Wrong service argument\n"); _bc++; return; } ServiceStatus e_old, e_new; e_old = e_new = SERVICE_FAILED; // pessimism is... switch (_cc++) { case 0: // First change expected SERVICE_READY -> SERVICE_STARTING e_old = SERVICE_READY; e_new = SERVICE_STARTING; break; case 1: // Second change expected SERVICE_STARTING -> SERVICE_RUNNING e_old = SERVICE_STARTING; e_new = SERVICE_RUNNING; break; case 2: // Third change expected SERVICE_RUNNING -> SERVICE_SHUTTING_DOWN e_old = SERVICE_RUNNING; e_new = SERVICE_SHUTTING_DOWN; break; case 3: // Fourth change expected SERVICE_SHUTTING_DOWN -> SERVICE_SHUTDOWN e_old = SERVICE_SHUTTING_DOWN; e_new = SERVICE_SHUTDOWN; break; default: verbose_log("%u. Too many changes.\n", XORP_UINT_CAST(_cc)); } if (e_old == old_status && e_new == new_status) { verbose_log("%u. Good transition: %s -> %s (%s)\n", XORP_UINT_CAST(_cc), service_status_name(e_old), service_status_name(e_new), service->status_note().c_str()); return; } verbose_log("%u. Bad transition: Got %s -> %s (%s) " "Expected %s -> %s\n", XORP_UINT_CAST(_cc), service_status_name(old_status), service_status_name(new_status), service_status_name(e_old), service_status_name(e_new), service->status_note().c_str()); // Record bad change _bc++; } bool changes_okay() const { return _cc == 4 && _bc == 0; } protected: const ServiceBase* _s; // Expected service uint32_t _cc; // Change count uint32_t _bc; // number of bad changes }; // ---------------------------------------------------------------------------- // Guts of Test static void startup_test_service(TestService* ts) { ts->startup(); } static void shutdown_test_service(TestService* ts) { ts->shutdown(); } static int run_test() { EventLoop e; TestService ts(e); TestServiceChangeObserver o(&ts); ts.set_observer(&o); XorpTimer startup = e.new_oneoff_after_ms(TRANS_MS, callback(&startup_test_service, &ts)); XorpTimer shutdown = e.new_oneoff_after_ms(EXIT_MS, callback(&shutdown_test_service, &ts)); bool timed_out = false; XorpTimer timeout = e.set_flag_after_ms(EXIT_MS + 3 * TRANS_MS, &timed_out); while (timed_out == false && ts.status() != SERVICE_SHUTDOWN) { e.run(); } if (timed_out) { verbose_log("Test timed out.\n"); return -1; } if (o.changes_okay() == false) { verbose_log("Changes not okay.\n"); return -1; } return 0; } static void usage(const char* argv0) { fprintf(stderr, "usage: %s [-v]\n", argv0); fprintf(stderr, "A test program for XORP service classes\n"); } int main(int argc, char* const* argv) { // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); int ch; while ((ch = getopt(argc, argv, "hv")) != -1) { switch (ch) { case 'v': set_verbose(true); break; case 'h': case '?': default: usage(argv[0]); xlog_stop(); xlog_exit(); return -1; } } argc -= optind; argv += optind; int r = 0; XorpUnexpectedHandler x(xorp_unexpected_handler); try { r = run_test(); } catch (...) { xorp_catch_standard_exceptions(); } // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); return r; } xorp/libxorp/tests/test_asyncio.cc0000664000076400007640000003255111540224227017521 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "libxorp_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xorpfd.hh" #include "libxorp/xlog.h" #include "libxorp/random.h" #ifdef HAVE_FCNTL_H #include #endif #ifdef HAVE_WINDOWS_H #include #endif #ifdef HAVE_WINSOCK2_H #include #endif #ifdef HAVE_WS2TCPIP_H #include #endif #include "asyncio.hh" static bool s_verbose = false; bool verbose() { return s_verbose; } void set_verbose(bool v) { s_verbose = v; } static int s_failures = 0; bool failures() { return s_failures; } void incr_failures() { s_failures++; } #include "xorp_tests.hh" static const int TIMEOUT_MS = 2000; static const int MAX_ITERS = 50; static const int MAX_BUFFERS = 200; static const int MAX_BUFFER_BYTES = 1000000; #ifdef DETAILED_DEBUG static int bytes_read = 0; static int bytes_written = 0; #endif // // XXX: Below is a copy of few libcomm functions. We include them here, // because nothing inside the libxorp directory should depend on any // other XORP library. // /** * local_comm_init: * @void: * * Library initialization. Need be called only once, during startup. * XXX: Not currently thread-safe. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ int local_comm_init(void) { static int init_flag = 0; if (init_flag) return (XORP_OK); #ifdef HOST_OS_WINDOWS { int result; WORD version; WSADATA wsadata; version = MAKEWORD(2, 2); result = WSAStartup(version, &wsadata); if (result != 0) { return (XORP_ERROR); } if (LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wVersion) != 2) { (void)WSACleanup(); return (XORP_ERROR); } } #endif // HOST_OS_WINDOWS init_flag = 1; return (XORP_OK); } /** * local_comm_exit: * @void: * * Library termination/cleanup. Must be called at process exit. * XXX: Not currently thread-safe. * **/ void local_comm_exit(void) { #ifdef HOST_OS_WINDOWS (void)WSACleanup(); #endif } /** * comm_sock_pair: * * Create a pair of connected sockets. The sockets will be created in * the blocking state by default, and with no additional socket options set. * * On Windows platforms, a domain of AF_UNIX, AF_INET, or AF_INET6 must * be specified. For the AF_UNIX case, the sockets created will actually * be in the AF_INET domain. The protocol field is ignored. * * On UNIX, this function simply wraps the socketpair() system call. * * XXX: There may be UNIX platforms lacking socketpair() where we * have to emulate it. * * @param domain the domain of the socket (e.g., AF_INET, AF_INET6). * @param type the type of the socket (e.g., SOCK_STREAM, SOCK_DGRAM). * @param protocol the particular protocol to be used with the socket. * @param sv pointer to an array of two xsock_t handles to receive the * allocated socket pair. * * @return XORP_OK if the socket pair was created, otherwise if any error * is encountered, XORP_ERROR. **/ static int local_comm_sock_pair(int domain, int type, int protocol, xsock_t sv[2]) { #ifndef HOST_OS_WINDOWS if (socketpair(domain, type, protocol, sv) == -1) { XLOG_ERROR("socketpair() failed: %s", strerror(errno)); return (XORP_ERROR); } return (XORP_OK); #else // HOST_OS_WINDOWS struct sockaddr_storage ss; struct sockaddr_in *psin; socklen_t sslen; SOCKET st[3]; u_long optval; int numtries, error, intdomain; unsigned short port; static const int CSP_LOWPORT = 40000; static const int CSP_HIGHPORT = 65536; #ifdef HAVE_IPV6 struct sockaddr_in6 *psin6; #endif UNUSED(protocol); if (domain != AF_UNIX && domain != AF_INET #ifdef HAVE_IPV6 && domain != AF_INET6 #endif ) { XLOG_ERROR("Invalid socket domain: %d", domain); return (XORP_ERROR); } intdomain = domain; if (intdomain == AF_UNIX) intdomain = AF_INET; st[0] = st[1] = st[2] = INVALID_SOCKET; st[2] = socket(intdomain, type, 0); if (st[2] == INVALID_SOCKET) goto error; memset(&ss, 0, sizeof(ss)); psin = (struct sockaddr_in *)&ss; #ifdef HAVE_IPV6 psin6 = (struct sockaddr_in6 *)&ss; if (intdomain == AF_INET6) { sslen = sizeof(struct sockaddr_in6); ss.ss_family = AF_INET6; psin6->sin6_addr = in6addr_loopback; } else #endif // HAVE_IPV6 { sslen = sizeof(struct sockaddr_in); ss.ss_family = AF_INET; psin->sin_addr.s_addr = htonl(INADDR_LOOPBACK); } numtries = 3; do { port = htons((xorp_random() % (CSP_LOWPORT - CSP_HIGHPORT)) + CSP_LOWPORT); #ifdef HAVE_IPV6 if (intdomain == AF_INET6) psin6->sin6_port = port; else #endif psin->sin_port = port; error = bind(st[2], (struct sockaddr *)&ss, sslen); if (error == 0) break; if ((error != 0) && ((WSAGetLastError() != WSAEADDRNOTAVAIL) || (WSAGetLastError() != WSAEADDRINUSE))) break; } while (--numtries > 0); if (error != 0) goto error; error = listen(st[2], 5); if (error != 0) goto error; st[0] = socket(intdomain, type, 0); if (st[0] == INVALID_SOCKET) goto error; optval = 1L; error = ioctlsocket(st[0], FIONBIO, &optval); if (error != 0) goto error; error = connect(st[0], (struct sockaddr *)&ss, sslen); if (error != 0 && WSAGetLastError() != WSAEWOULDBLOCK) goto error; numtries = 3; do { st[1] = accept(st[2], NULL, NULL); if (st[1] != INVALID_SOCKET) { break; } else { if (WSAGetLastError() == WSAEWOULDBLOCK) { SleepEx(100, TRUE); } else { break; } } } while (--numtries > 0); if (st[1] == INVALID_SOCKET) goto error; // Squelch inherited socket event mask (void)WSAEventSelect(st[1], NULL, 0); // // XXX: Should use getsockname() here to verify that the client socket // is connected. // optval = 0L; error = ioctlsocket(st[0], FIONBIO, &optval); if (error != 0) goto error; closesocket(st[2]); sv[0] = st[0]; sv[1] = st[1]; return (XORP_OK); error: if (st[0] != INVALID_SOCKET) closesocket(st[0]); if (st[1] != INVALID_SOCKET) closesocket(st[1]); if (st[2] != INVALID_SOCKET) closesocket(st[2]); return (XORP_ERROR); #endif // HOST_OS_WINDOWS } /** * local_comm_sock_set_blocking: * * Set the blocking or non-blocking mode of an existing socket. * @sock: The socket whose blocking mode is to be set. * @is_blocking: If non-zero, then the socket will be blocking, otherwise * non-blocking. * * Return value: XORP_OK if the operation was successful, otherwise * if any error is encountered, XORP_ERROR. **/ static int local_comm_sock_set_blocking(xsock_t sock, int is_blocking) { #ifdef HOST_OS_WINDOWS u_long opt; int flags; if (is_blocking) opt = 0; else opt = 1; flags = ioctlsocket(sock, FIONBIO, &opt); if (flags != 0) { XLOG_ERROR("FIONBIO error"); return (XORP_ERROR); } #else // !HOST_OS_WINDOWS int flags; if ( (flags = fcntl(sock, F_GETFL, 0)) < 0) { XLOG_ERROR("F_GETFL error"); return (XORP_ERROR); } if (is_blocking) flags &= ~O_NONBLOCK; else flags |= O_NONBLOCK; if (fcntl(sock, F_SETFL, flags) < 0) { XLOG_ERROR("F_SETFL error"); return (XORP_ERROR); } #endif // HOST_OS_WINDOWS return (XORP_OK); } /** * local_comm_sock_close: * @sock: The socket to close. * * Close a socket. * * Return value: %XORP_OK on success, otherwise %XORP_ERROR. **/ static int local_comm_sock_close(xsock_t sock) { int ret; #ifndef HOST_OS_WINDOWS ret = close(sock); #else (void)WSAEventSelect(sock, NULL, 0); ret = closesocket(sock); #endif if (ret < 0) { XLOG_ERROR("Error closing socket (socket = %d)", sock); return (XORP_ERROR); } return (XORP_OK); } static void writer_check(AsyncFileReader::Event ev, const uint8_t* buf, size_t bytes, size_t offset, uint8_t* exp_buf,XorpTimer* t) { assert(ev == AsyncFileWriter::DATA || ev == AsyncFileWriter::FLUSHING); assert(buf == exp_buf); assert(offset <= bytes); #ifdef DETAILED_DEBUG bytes_written += bytes; #endif // Defer timeout t->schedule_after_ms(TIMEOUT_MS); } static void reader_check(AsyncFileReader::Event ev, const uint8_t* buf, size_t bytes, size_t offset, uint8_t* exp_buf, uint8_t data_value, XorpTimer* t) { assert(ev == AsyncFileReader::DATA || ev == AsyncFileReader::FLUSHING); assert(buf == exp_buf); assert(offset <= bytes); // Defer timeout t->schedule_after_ms(TIMEOUT_MS); if (offset == bytes) { #ifdef DETAILED_DEBUG bytes_read += bytes; #endif // Check buffer is filled with expected value (== iteration no) for (size_t i = 0; i < bytes; i++) { assert(buf[i] == data_value); } } } static void reader_eof_check(AsyncFileReader::Event ev, const uint8_t* buf, size_t bytes, size_t offset, uint8_t* exp_buf, bool* done) { assert(ev == AsyncFileReader::END_OF_FILE); assert(buf == exp_buf); assert(offset <= bytes); assert(offset == 0); *done = true; } static void timeout() { fprintf(stderr, "Timed out"); exit(1); } static void run_test() { EventLoop e; xsock_t s[2]; // pair of sockets - one for read, one for write if (local_comm_sock_pair(AF_UNIX, SOCK_STREAM, 0, s) != XORP_OK) { print_failed("Failed to open socket pair"); exit(1); } print_passed("open socket"); if (local_comm_sock_set_blocking(s[0], 0) != XORP_OK) { print_failed("Failed to set socket non-blocking"); exit(1); } print_passed("set nonblock"); if (local_comm_sock_set_blocking(s[1], 0) != XORP_OK) { print_failed("Failed to set socket non-blocking"); exit(1); } print_passed("set nonblock[1]"); AsyncFileWriter afw(e, s[0]); AsyncFileReader afr(e, s[1]); static uint8_t msg[MAX_BUFFER_BYTES]; const size_t msg_bytes = sizeof(msg) / sizeof(msg[0]); XorpTimer t = e.new_oneoff_after_ms(TIMEOUT_MS, callback(timeout)); uint32_t bytes_transferred = 0; for (int i = 0; i <= MAX_ITERS; i++) { // set value of each bytes in buffer to be test iteration number // then we can check for corruption memset(msg, i, msg_bytes); bool was_started = afr.start(); UNUSED(was_started); assert(was_started == false); // can't start no buffer // Choose number of buffers to use int n = 1 + (xorp_random() % MAX_BUFFERS); printf("%d ", n); fflush(stdout); while (n >= 0) { // Size of buffer add must be at least 1 size_t b_bytes = 1 + (xorp_random() % (MAX_BUFFER_BYTES - 1)); afw.add_buffer(msg, b_bytes, callback(&writer_check, msg, &t)); afr.add_buffer(msg, b_bytes, callback(&reader_check, msg, (uint8_t)i, &t)); n--; bytes_transferred += b_bytes; } // XXX: Because of the new ioevent semantics, start and stop // calls must be exactly matched in Win32. afr.stop(); afw.start(); afw.stop(); afw.start(); // Just walk thru starting and afr.start(); afr.stop(); afr.start(); // stopping... while (afw.running() || afr.running()) { e.run(); #ifdef DETAILED_DEBUG printf("bytes_read = %d bytes_written = %d\n", bytes_read, bytes_written); fflush(stdout); #endif } assert(afw.buffers_remaining() == 0 && afr.buffers_remaining() == 0); afw.stop(); // utterly redundant call to stop() afr.stop(); // utterly redundant call to stop() assert(afw.start() == false); // can't start, no buffers assert(afr.start() == false); // can't start, no buffers } // test END_OF_FILE local_comm_sock_close(s[0]); // close writer's file descriptor bool eof_test_done = false; afr.add_buffer(msg, msg_bytes, callback(reader_eof_check, msg, &eof_test_done)); afr.start(); while (eof_test_done == false) e.run(); char buf[500]; snprintf(buf, 500, "\nTransfered %u bytes between AsyncFileWriter and " "AsyncFileReader.\n", XORP_UINT_CAST(bytes_transferred)); print_passed(buf); } int main(int /* argc */, char *argv[]) { // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); // Some of test generates warnings - under normal circumstances the // end user wants to know, but here not. xlog_disable(XLOG_LEVEL_WARNING); if (local_comm_init() != XORP_OK) { XLOG_ERROR("Failed to initialization socket communication facility"); return 1; } run_test(); local_comm_exit(); // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); return 0; } xorp/libxorp/tests/test_run_command.sh0000775000076400007640000000045711421137511020403 0ustar greearbgreearb#!/bin/sh # # $XORP$ # # The Windows version of the test_run_command regression test needs to be # run from an external (i.e. non-MSYS) shell, or else it will fail. # if [ x"$OSTYPE" = xmsys ]; then exec_cmd="cmd //c test_run_command.exe" else exec_cmd="./test_run_command" fi exec $exec_cmd $* xorp/libxorp/tests/test_timer.cc0000664000076400007640000001216011540224230017160 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // demo program to test timers and event loops (and show // how to use them // #include #include "libxorp_module.h" #include "libxorp/timer.hh" #include "libxorp/eventloop.hh" #include "libxorp/xlog.h" int fired = 0 ; // callback for non-periodic timer. Does not need to return a value static void some_foo() { fired++; printf("O"); fflush(stdout); } // callback for a periodic timer. If true, the timer is rescheduled. static bool print_dot() { printf("."); fflush(stdout); return true; } static void test_many(EventLoop& e) { #define N 100 int i; XorpTimer a[N]; fired = 0 ; fprintf(stderr, "++ create a bunch of timers to fire in about 2s\n"); for (i=0; i 2 || err < 0) return false; // XXX could return true, but lets hardcode values for safety. return (_counter1 == 10 && _counter2 == 20 && _counter3 == 1); } bool test_priority1() { _counter1 = 0; _counter2 = 0; _counter3 = 0; _t1 = _eventloop.new_task(callback(this, &TestTask::handler1), 3, 1); _t2 = _eventloop.new_task(callback(this, &TestTask::handler2), 4, 1); _t3 = _eventloop.new_oneoff_task(callback(this, &TestTask::handler3), 5, 1); for (int i = 0; i < 11; i++) { _eventloop.run(); } _t1.unschedule(); _t2.unschedule(); _t3.unschedule(); XLOG_ASSERT(_eventloop.events_pending() == false); debug_msg("counter1 = %d counter2 = %d counter3 = %d\n", _counter1, _counter2, _counter3); // task 3 and 2 must be starved if (_counter2 > 0 || _counter3 > 0) return false; // task 1 must get all runs if (_counter1 == 0) return false; // XXX could return true. return (_counter1 == 11 && _counter2 == 0 && _counter3 == 0); } bool test_priority2() { _counter1 = 0; _counter2 = 0; _counter3 = 0; _t1 = _eventloop.new_task(callback(this, &TestTask::handler1b), 3, 1); _t2 = _eventloop.new_task(callback(this, &TestTask::handler2), 4, 1); _t3 = _eventloop.new_oneoff_task(callback(this, &TestTask::handler3), 2, 1); for (int i = 0; i < 16; i++) { _eventloop.run(); } XLOG_ASSERT(_t1.scheduled() == false); _t2.unschedule(); XLOG_ASSERT(_t3.scheduled() == false); XLOG_ASSERT(_eventloop.events_pending() == false); debug_msg("counter1 = %d counter2 = %d counter3 = %d\n", _counter1, _counter2, _counter3); // task 1 and 3 must complete if (_counter1 != 10 || _counter3 != 1) return false; // task 2 must get the rest of the cycles int aggressiveness = 0; // eventloop's aggressiveness int cycles = 16; cycles *= 1 + aggressiveness; // runs per event loop cycles -= _counter1 + _counter3; if (_counter2 != cycles) return false; // XXX could return true return (_counter1 == 10 && _counter2 == 5 && _counter3 == 1); } bool handler1() { _counter1++; return true; } bool handler2() { _counter2++; return true; } void handler3() { _counter3++; } bool handler1b() { _counter1++; if (_counter1 < 10) return true; else return false; } private: EventLoop& _eventloop; XorpTask _t1; XorpTask _t2; XorpTask _t3; int _counter1; int _counter2; int _counter3; }; static void run_test() { EventLoop e; TestTask test1(e); if (test1.test_weights()) { print_passed("test weights"); } else { print_failed("test weights"); exit(1); } if (test1.test_priority1()) { print_passed("test priority 1"); } else { print_failed("test priority 1"); exit(1); } if (test1.test_priority2()) { print_passed("test priority 2"); } else { print_failed("test priority 2"); exit(1); } } int main(int /* argc */, const char* argv[]) { // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); run_test(); // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); return 0; } xorp/libxorp/tests/test_ipv4.cc0000664000076400007640000010305211540224227016731 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "libxorp_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/exceptions.hh" #ifdef HAVE_GETOPT_H #include #endif #include "ipv4.hh" // // XXX: MODIFY FOR YOUR TEST PROGRAM // static const char *program_name = "test_ipv4"; static const char *program_description = "Test IPv4 address class"; static const char *program_version_id = "0.1"; static const char *program_date = "December 2, 2002"; static const char *program_copyright = "See file LICENSE"; static const char *program_return_value = "0 on success, 1 if test error, 2 if internal error"; static bool s_verbose = false; bool verbose() { return s_verbose; } void set_verbose(bool v) { s_verbose = v; } static int s_failures = 0; bool failures() { return s_failures; } void incr_failures() { s_failures++; } #include "libxorp/xorp_tests.hh" /** * Print program info to output stream. * * @param stream the output stream the print the program info to. */ static void print_program_info(FILE *stream) { fprintf(stream, "Name: %s\n", program_name); fprintf(stream, "Description: %s\n", program_description); fprintf(stream, "Version: %s\n", program_version_id); fprintf(stream, "Date: %s\n", program_date); fprintf(stream, "Copyright: %s\n", program_copyright); fprintf(stream, "Return: %s\n", program_return_value); } /** * Print program usage information to the stderr. * * @param progname the name of the program. */ static void usage(const char* progname) { print_program_info(stderr); fprintf(stderr, "usage: %s [-v] [-h]\n", progname); fprintf(stderr, " -h : usage (this message)\n"); fprintf(stderr, " -v : verbose output\n"); } /** * Test IPv4 valid constructors. */ void test_ipv4_valid_constructors() { // Test values for IPv4 address: "12.34.56.78" const char *addr_string = "12.34.56.78"; uint32_t ui = htonl((12 << 24) | (34 << 16) | (56 << 8) | 78); struct in_addr in_addr; in_addr.s_addr = ui; struct sockaddr_in sin; memset(&sin, 0, sizeof(sin)); #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN sin.sin_len = sizeof(sin); #endif sin.sin_family = AF_INET; sin.sin_addr.s_addr = ui; struct sockaddr *sap = (struct sockaddr *)&sin; struct sockaddr_storage *ssp = (struct sockaddr_storage *)&sin; // // Default constructor. // IPv4 ip1; verbose_match(ip1.str(), "0.0.0.0"); // // Constructor from a string. // IPv4 ip2(addr_string); verbose_match(ip2.str(), addr_string); // // Constructor from another IPv4 address. // IPv4 ip3(ip2); verbose_match(ip3.str(), addr_string); // // Constructor from an integer value. // IPv4 ip4(ui); verbose_match(ip4.str(), addr_string); // // Constructor from a (uint8_t *) memory pointer. // IPv4 ip5((uint8_t *)&ui); verbose_match(ip5.str(), addr_string); // // Constructor from in_addr structure. // IPv4 ip6(in_addr); verbose_match(ip6.str(), addr_string); // // Constructor from sockaddr structure. // IPv4 ip7(*sap); verbose_match(ip7.str(), addr_string); // // Constructor from sockaddr_storage structure. // IPv4 ip8(*ssp); verbose_match(ip8.str(), addr_string); // // Constructor from sockaddr_in structure. // IPv4 ip9(sin); verbose_match(ip9.str(), addr_string); } /** * Test IPv4 invalid constructors. */ void test_ipv4_invalid_constructors() { // Test values for IPv4 address: "12.34.56.78" struct sockaddr_in sin; memset(&sin, 0, sizeof(sin)); #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN sin.sin_len = sizeof(sin); #endif sin.sin_family = AF_UNSPEC; // Note: invalid IP address family sin.sin_addr.s_addr = htonl((12 << 24) | (34 << 16) | (56 << 8) | 78); struct sockaddr *sap = (struct sockaddr *)&sin; struct sockaddr_storage *ssp = (struct sockaddr_storage *)&sin; // // Constructor from an invalid address string. // try { // Invalid address string: note the typo -- lack of a "dot" after "12" IPv4 ip("1234.56.78"); verbose_log("Cannot catch invalid IP address \"1234.56.78\" : FAIL\n"); incr_failures(); UNUSED(ip); } catch (const InvalidString& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } // // Constructor from an invalid sockaddr structure. // try { IPv4 ip(*sap); verbose_log("Cannot catch invalid IP address family AF_UNSPEC : FAIL\n"); incr_failures(); UNUSED(ip); } catch (const InvalidFamily& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } // // Constructor from an invalid sockaddr_storage structure. // try { IPv4 ip(*ssp); verbose_log("Cannot catch invalid IP address family AF_UNSPEC : FAIL\n"); incr_failures(); UNUSED(ip); } catch (const InvalidFamily& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } // // Constructor from an invalid sockaddr_in structure. // try { IPv4 ip(sin); verbose_log("Cannot catch invalid IP address family AF_UNSPEC : FAIL\n"); incr_failures(); UNUSED(ip); } catch (const InvalidFamily& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } } /** * Test IPv4 valid copy in/out methods. */ void test_ipv4_valid_copy_in_out() { // Test values for IPv4 address: "12.34.56.78" const char *addr_string4 = "12.34.56.78"; uint32_t ui = htonl((12 << 24) | (34 << 16) | (56 << 8) | 78); struct in_addr in_addr; in_addr.s_addr = ui; struct sockaddr_in sin; memset(&sin, 0, sizeof(sin)); #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN sin.sin_len = sizeof(sin); #endif sin.sin_family = AF_INET; sin.sin_addr.s_addr = ui; struct sockaddr *sap; struct sockaddr_storage *ssp; // // Copy the IPv4 raw address to specified memory location. // IPv4 ip1(addr_string4); uint8_t ip1_uint8[4]; verbose_assert(ip1.copy_out(&ip1_uint8[0]) == 4, "copy_out(uint8_t *) for IPv4 address"); verbose_assert(memcmp(&ui, &ip1_uint8[0], 4) == 0, "compare copy_out(uint8_t *) for IPv4 address"); // // Copy the IPv4 raw address to an in_addr structure. // IPv4 ip3(addr_string4); struct in_addr ip3_in_addr; verbose_assert(ip3.copy_out(ip3_in_addr) == 4, "copy_out(in_addr&) for IPv4 address"); verbose_assert(memcmp(&in_addr, &ip3_in_addr, 4) == 0, "compare copy_out(in_addr&) for IPv4 address"); // // Copy the IPv4 raw address to a sockaddr structure. // IPv4 ip5(addr_string4); struct sockaddr_in ip5_sockaddr_in; sap = (struct sockaddr *)&ip5_sockaddr_in; verbose_assert(ip5.copy_out(*sap) == 4, "copy_out(sockaddr&) for IPv4 address"); verbose_assert(memcmp(&sin, &ip5_sockaddr_in, sizeof(sin)) == 0, "compare copy_out(sockaddr&) for IPv4 address"); // // Copy the IPv4 raw address to a sockaddr_storage structure. // IPv4 ip5_2(addr_string4); struct sockaddr_in ip5_2_sockaddr_in; ssp = (struct sockaddr_storage *)&ip5_2_sockaddr_in; verbose_assert(ip5_2.copy_out(*ssp) == 4, "copy_out(sockaddr_storage&) for IPv4 address"); verbose_assert(memcmp(&sin, &ip5_2_sockaddr_in, sizeof(sin)) == 0, "compare copy_out(sockaddr_storage&) for IPv4 address"); // // Copy the IPv4 raw address to a sockaddr_in structure. // IPv4 ip7(addr_string4); struct sockaddr_in ip7_sockaddr_in; verbose_assert(ip7.copy_out(ip7_sockaddr_in) == 4, "copy_out(sockaddr_in&) for IPv4 address"); verbose_assert(memcmp(&sin, &ip7_sockaddr_in, sizeof(sin)) == 0, "compare copy_out(sockaddr_in&) for IPv4 address"); // // Copy a raw address into IPv4 structure. // IPv4 ip11; verbose_assert(ip11.copy_in((uint8_t *)&ui) == 4, "copy_in(uint8_t *) for IPv4 address"); verbose_match(ip11.str(), addr_string4); // // Copy a raw IPv4 address from a in_addr structure into IPv4 structure. // IPv4 ip13; verbose_assert(ip13.copy_in(in_addr) == 4, "copy_in(in_addr&) for IPv4 address"); verbose_match(ip13.str(), addr_string4); // // Copy a raw address from a sockaddr structure into IPv4 structure. // IPv4 ip15; sap = (struct sockaddr *)&sin; verbose_assert(ip15.copy_in(*sap) == 4, "copy_in(sockaddr&) for IPv4 address"); verbose_match(ip15.str(), addr_string4); // // Copy a raw address from a sockaddr_storage structure into IPv4 // structure. // IPv4 ip15_2; ssp = (struct sockaddr_storage *)&sin; verbose_assert(ip15_2.copy_in(*ssp) == 4, "copy_in(sockaddr_storage&) for IPv4 address"); verbose_match(ip15_2.str(), addr_string4); // // Copy a raw address from a sockaddr_in structure into IPv4 structure. // IPv4 ip17; verbose_assert(ip17.copy_in(sin) == 4, "copy_in(sockaddr_in&) for IPv4 address"); verbose_match(ip17.str(), addr_string4); } /** * Test IPv4 invalid copy in/out methods. */ void test_ipv4_invalid_copy_in_out() { // Test values for IPv4 address: "12.34.56.78" // const char *addr_string4 = "12.34.56.78"; struct sockaddr_in sin; memset(&sin, 0, sizeof(sin)); #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN sin.sin_len = sizeof(sin); #endif sin.sin_family = AF_UNSPEC; // Note: invalid IP address family sin.sin_addr.s_addr = htonl((12 << 24) | (34 << 16) | (56 << 8) | 78); struct sockaddr *sap; struct sockaddr_storage *ssp; // // Copy-in from a sockaddr structure for invalid address family. // try { IPv4 ip; sap = (struct sockaddr *)&sin; ip.copy_in(*sap); verbose_log("Cannot catch invalid IP address family AF_UNSPEC : FAIL\n"); incr_failures(); } catch (const InvalidFamily& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } // // Copy-in from a sockaddr_storage structure for invalid address family. // try { IPv4 ip; ssp = (struct sockaddr_storage *)&sin; ip.copy_in(*ssp); verbose_log("Cannot catch invalid IP address family AF_UNSPEC : FAIL\n"); incr_failures(); } catch (const InvalidFamily& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } // // Copy-in from a sockaddr_in structure for invalid address family. // try { IPv4 ip; ip.copy_in(sin); verbose_log("Cannot catch invalid IP address family AF_UNSPEC : FAIL\n"); incr_failures(); } catch (const InvalidFamily& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } } /** * Test IPv4 operators. */ void test_ipv4_operators() { IPv4 ip_a("0.255.0.255"); IPv4 ip_b("255.0.255.255"); IPv4 ip_not_a("255.0.255.0"); IPv4 ip_a_or_b("255.255.255.255"); IPv4 ip_a_and_b("0.0.0.255"); IPv4 ip_a_xor_b("255.255.255.0"); // // Equality Operator // verbose_assert(ip_a == ip_a, "operator=="); verbose_assert(!(ip_a == ip_b), "operator=="); // // Not-Equal Operator // verbose_assert(!(ip_a != ip_a), "operator!="); verbose_assert(ip_a != ip_b, "operator!="); // // Less-Than Operator // verbose_assert(ip_a < ip_b, "operator<"); // // Bitwise-Negation Operator // verbose_assert(~ip_a == ip_not_a, "operator~"); // // OR Operator // verbose_assert((ip_a | ip_b) == ip_a_or_b, "operator|"); // // AND Operator // verbose_assert((ip_a & ip_b) == ip_a_and_b, "operator&"); // // XOR Operator // verbose_assert((ip_a ^ ip_b) == ip_a_xor_b, "operator^"); // // Operator << // verbose_assert(IPv4("0.255.0.255") << 16 == IPv4("0.255.0.0"), "operator<<"); verbose_assert(IPv4("0.255.0.0") << 1 == IPv4("1.254.0.0"), "operator<<"); // // Operator >> // verbose_assert(IPv4("0.255.0.255") >> 16 == IPv4("0.0.0.255"), "operator>>"); verbose_assert(IPv4("0.0.0.255") >> 1 == IPv4("0.0.0.127"), "operator>>"); // // Decrement Operator // verbose_assert(--IPv4("0.255.0.255") == IPv4("0.255.0.254"), "operator--()"); verbose_assert(--IPv4("0.0.0.0") == IPv4("255.255.255.255"), "operator--()"); // // Increment Operator // verbose_assert(++IPv4("0.255.0.254") == IPv4("0.255.0.255"), "operator++()"); verbose_assert(++IPv4("255.255.255.255") == IPv4("0.0.0.0"), "operator++()"); } /** * Test IPv4 address type. */ void test_ipv4_address_type() { IPv4 ip4_zero("0.0.0.0"); // Zero, not unicast IPv4 ip4_unicast1("0.0.0.1"); // Unicast IPv4 ip4_unicast2("1.2.3.4"); // Unicast IPv4 ip4_unicast3("223.255.255.255"); // Unicast IPv4 ip4_class_a1("0.0.0.0"); // Class A IPv4 ip4_class_a2("12.34.56.78"); // Class A IPv4 ip4_class_a3("127.255.255.255"); // Class A IPv4 ip4_class_b1("128.0.0.0"); // Class B IPv4 ip4_class_b2("128.2.3.4"); // Class B IPv4 ip4_class_b3("191.255.255.255"); // Class B IPv4 ip4_class_c1("192.0.0.0"); // Class C IPv4 ip4_class_c2("192.2.3.4"); // Class C IPv4 ip4_class_c3("223.255.255.255"); // Class C IPv4 ip4_multicast1("224.0.0.0"); // Multicast IPv4 ip4_multicast2("224.2.3.4"); // Multicast IPv4 ip4_multicast3("239.255.255.255"); // Multicast IPv4 ip4_experimental1("240.0.0.0"); // Experimental IPv4 ip4_experimental2("240.2.3.4"); // Experimental IPv4 ip4_experimental3("255.255.255.255"); // Experimental // IPv4 ip4_multicast_linklocal1("224.0.0.1"); // Link-local multicast IPv4 ip4_loopback1("127.0.0.1"); // Loopback IPv4 ip4_loopback2("127.255.255.255"); // Loopback // // Test if an address is numerically zero. // verbose_assert(ip4_zero.is_zero() == true, "is_zero()"); verbose_assert(ip4_unicast1.is_zero() == false, "is_zero()"); verbose_assert(ip4_unicast2.is_zero() == false, "is_zero()"); verbose_assert(ip4_unicast3.is_zero() == false, "is_zero()"); verbose_assert(ip4_class_a1.is_zero() == true, "is_zero()"); verbose_assert(ip4_class_a2.is_zero() == false, "is_zero()"); verbose_assert(ip4_class_a3.is_zero() == false, "is_zero()"); verbose_assert(ip4_class_b1.is_zero() == false, "is_zero()"); verbose_assert(ip4_class_b2.is_zero() == false, "is_zero()"); verbose_assert(ip4_class_b3.is_zero() == false, "is_zero()"); verbose_assert(ip4_class_c1.is_zero() == false, "is_zero()"); verbose_assert(ip4_class_c2.is_zero() == false, "is_zero()"); verbose_assert(ip4_class_c3.is_zero() == false, "is_zero()"); verbose_assert(ip4_multicast1.is_zero() == false, "is_zero()"); verbose_assert(ip4_multicast2.is_zero() == false, "is_zero()"); verbose_assert(ip4_multicast3.is_zero() == false, "is_zero()"); verbose_assert(ip4_experimental1.is_zero() == false, "is_zero()"); verbose_assert(ip4_experimental2.is_zero() == false, "is_zero()"); verbose_assert(ip4_experimental3.is_zero() == false, "is_zero()"); // // Test if an address is a valid unicast address. // verbose_assert(ip4_zero.is_unicast() == false, "is_unicast()"); verbose_assert(ip4_unicast1.is_unicast() == true, "is_unicast()"); verbose_assert(ip4_unicast2.is_unicast() == true, "is_unicast()"); verbose_assert(ip4_unicast3.is_unicast() == true, "is_unicast()"); verbose_assert(ip4_class_a1.is_unicast() == false, "is_unicast()"); verbose_assert(ip4_class_a2.is_unicast() == true, "is_unicast()"); verbose_assert(ip4_class_a3.is_unicast() == true, "is_unicast()"); verbose_assert(ip4_class_b1.is_unicast() == true, "is_unicast()"); verbose_assert(ip4_class_b2.is_unicast() == true, "is_unicast()"); verbose_assert(ip4_class_b3.is_unicast() == true, "is_unicast()"); verbose_assert(ip4_class_c1.is_unicast() == true, "is_unicast()"); verbose_assert(ip4_class_c2.is_unicast() == true, "is_unicast()"); verbose_assert(ip4_class_c3.is_unicast() == true, "is_unicast()"); verbose_assert(ip4_multicast1.is_unicast() == false, "is_unicast()"); verbose_assert(ip4_multicast2.is_unicast() == false, "is_unicast()"); verbose_assert(ip4_multicast3.is_unicast() == false, "is_unicast()"); verbose_assert(ip4_experimental1.is_unicast() == false, "is_unicast()"); verbose_assert(ip4_experimental2.is_unicast() == false, "is_unicast()"); verbose_assert(ip4_experimental3.is_unicast() == false, "is_unicast()"); // // Test if an address is a valid multicast address. // verbose_assert(ip4_zero.is_multicast() == false, "is_multicast()"); verbose_assert(ip4_unicast1.is_multicast() == false, "is_multicast()"); verbose_assert(ip4_unicast2.is_multicast() == false, "is_multicast()"); verbose_assert(ip4_unicast3.is_multicast() == false, "is_multicast()"); verbose_assert(ip4_class_a1.is_multicast() == false, "is_multicast()"); verbose_assert(ip4_class_a2.is_multicast() == false, "is_multicast()"); verbose_assert(ip4_class_a3.is_multicast() == false, "is_multicast()"); verbose_assert(ip4_class_b1.is_multicast() == false, "is_multicast()"); verbose_assert(ip4_class_b2.is_multicast() == false, "is_multicast()"); verbose_assert(ip4_class_b3.is_multicast() == false, "is_multicast()"); verbose_assert(ip4_class_c1.is_multicast() == false, "is_multicast()"); verbose_assert(ip4_class_c2.is_multicast() == false, "is_multicast()"); verbose_assert(ip4_class_c3.is_multicast() == false, "is_multicast()"); verbose_assert(ip4_multicast1.is_multicast() == true, "is_multicast()"); verbose_assert(ip4_multicast2.is_multicast() == true, "is_multicast()"); verbose_assert(ip4_multicast3.is_multicast() == true, "is_multicast()"); verbose_assert(ip4_experimental1.is_multicast() == false, "is_multicast()"); verbose_assert(ip4_experimental2.is_multicast() == false, "is_multicast()"); verbose_assert(ip4_experimental3.is_multicast() == false, "is_multicast()"); // // Test if an address is a valid Class A address. // verbose_assert(ip4_zero.is_class_a() == true, "is_class_a()"); verbose_assert(ip4_unicast1.is_class_a() == true, "is_class_a()"); verbose_assert(ip4_unicast2.is_class_a() == true, "is_class_a()"); verbose_assert(ip4_unicast3.is_class_a() == false, "is_class_a()"); verbose_assert(ip4_class_a1.is_class_a() == true, "is_class_a()"); verbose_assert(ip4_class_a2.is_class_a() == true, "is_class_a()"); verbose_assert(ip4_class_a3.is_class_a() == true, "is_class_a()"); verbose_assert(ip4_class_b1.is_class_a() == false, "is_class_a()"); verbose_assert(ip4_class_b2.is_class_a() == false, "is_class_a()"); verbose_assert(ip4_class_b3.is_class_a() == false, "is_class_a()"); verbose_assert(ip4_class_c1.is_class_a() == false, "is_class_a()"); verbose_assert(ip4_class_c2.is_class_a() == false, "is_class_a()"); verbose_assert(ip4_class_c3.is_class_a() == false, "is_class_a()"); verbose_assert(ip4_multicast1.is_class_a() == false, "is_class_a()"); verbose_assert(ip4_multicast2.is_class_a() == false, "is_class_a()"); verbose_assert(ip4_multicast3.is_class_a() == false, "is_class_a()"); verbose_assert(ip4_experimental1.is_class_a() == false, "is_class_a()"); verbose_assert(ip4_experimental2.is_class_a() == false, "is_class_a()"); verbose_assert(ip4_experimental3.is_class_a() == false, "is_class_a()"); // // Test if an address is a valid Class B address. // verbose_assert(ip4_zero.is_class_b() == false, "is_class_b()"); verbose_assert(ip4_unicast1.is_class_b() == false, "is_class_b()"); verbose_assert(ip4_unicast2.is_class_b() == false, "is_class_b()"); verbose_assert(ip4_unicast3.is_class_b() == false, "is_class_b()"); verbose_assert(ip4_class_a1.is_class_b() == false, "is_class_b()"); verbose_assert(ip4_class_a2.is_class_b() == false, "is_class_b()"); verbose_assert(ip4_class_a3.is_class_b() == false, "is_class_b()"); verbose_assert(ip4_class_b1.is_class_b() == true, "is_class_b()"); verbose_assert(ip4_class_b2.is_class_b() == true, "is_class_b()"); verbose_assert(ip4_class_b3.is_class_b() == true, "is_class_b()"); verbose_assert(ip4_class_c1.is_class_b() == false, "is_class_b()"); verbose_assert(ip4_class_c2.is_class_b() == false, "is_class_b()"); verbose_assert(ip4_class_c3.is_class_b() == false, "is_class_b()"); verbose_assert(ip4_multicast1.is_class_b() == false, "is_class_b()"); verbose_assert(ip4_multicast2.is_class_b() == false, "is_class_b()"); verbose_assert(ip4_multicast3.is_class_b() == false, "is_class_b()"); verbose_assert(ip4_experimental1.is_class_b() == false, "is_class_b()"); verbose_assert(ip4_experimental2.is_class_b() == false, "is_class_b()"); verbose_assert(ip4_experimental3.is_class_b() == false, "is_class_b()"); // // Test if an address is a valid Class C address. // verbose_assert(ip4_zero.is_class_c() == false, "is_class_c()"); verbose_assert(ip4_unicast1.is_class_c() == false, "is_class_c()"); verbose_assert(ip4_unicast2.is_class_c() == false, "is_class_c()"); verbose_assert(ip4_unicast3.is_class_c() == true, "is_class_c()"); verbose_assert(ip4_class_a1.is_class_c() == false, "is_class_c()"); verbose_assert(ip4_class_a2.is_class_c() == false, "is_class_c()"); verbose_assert(ip4_class_a3.is_class_c() == false, "is_class_c()"); verbose_assert(ip4_class_b1.is_class_c() == false, "is_class_c()"); verbose_assert(ip4_class_b2.is_class_c() == false, "is_class_c()"); verbose_assert(ip4_class_b3.is_class_c() == false, "is_class_c()"); verbose_assert(ip4_class_c1.is_class_c() == true, "is_class_c()"); verbose_assert(ip4_class_c2.is_class_c() == true, "is_class_c()"); verbose_assert(ip4_class_c3.is_class_c() == true, "is_class_c()"); verbose_assert(ip4_multicast1.is_class_c() == false, "is_class_c()"); verbose_assert(ip4_multicast2.is_class_c() == false, "is_class_c()"); verbose_assert(ip4_multicast3.is_class_c() == false, "is_class_c()"); verbose_assert(ip4_experimental1.is_class_c() == false, "is_class_c()"); verbose_assert(ip4_experimental2.is_class_c() == false, "is_class_c()"); verbose_assert(ip4_experimental3.is_class_c() == false, "is_class_c()"); // // Test if an address is a valid experimental address. // verbose_assert(ip4_zero.is_experimental() == false, "is_experimental()"); verbose_assert(ip4_unicast1.is_experimental() == false, "is_experimental()"); verbose_assert(ip4_unicast2.is_experimental() == false, "is_experimental()"); verbose_assert(ip4_unicast3.is_experimental() == false, "is_experimental()"); verbose_assert(ip4_class_a1.is_experimental() == false, "is_experimental()"); verbose_assert(ip4_class_a2.is_experimental() == false, "is_experimental()"); verbose_assert(ip4_class_a3.is_experimental() == false, "is_experimental()"); verbose_assert(ip4_class_b1.is_experimental() == false, "is_experimental()"); verbose_assert(ip4_class_b2.is_experimental() == false, "is_experimental()"); verbose_assert(ip4_class_b3.is_experimental() == false, "is_experimental()"); verbose_assert(ip4_class_c1.is_experimental() == false, "is_experimental()"); verbose_assert(ip4_class_c2.is_experimental() == false, "is_experimental()"); verbose_assert(ip4_class_c3.is_experimental() == false, "is_experimental()"); verbose_assert(ip4_multicast1.is_experimental() == false, "is_experimental()"); verbose_assert(ip4_multicast2.is_experimental() == false, "is_experimental()"); verbose_assert(ip4_multicast3.is_experimental() == false, "is_experimental()"); verbose_assert(ip4_experimental1.is_experimental() == true, "is_experimental()"); verbose_assert(ip4_experimental2.is_experimental() == true, "is_experimental()"); verbose_assert(ip4_experimental3.is_experimental() == true, "is_experimental()"); // // Test if an address is a valid link-local unicast address. // verbose_assert(ip4_zero.is_linklocal_unicast() == false, "is_linklocal_unicast()"); verbose_assert(ip4_unicast1.is_linklocal_unicast() == false, "is_linklocal_unicast()"); verbose_assert(ip4_unicast2.is_linklocal_unicast() == false, "is_linklocal_unicast()"); verbose_assert(ip4_unicast3.is_linklocal_unicast() == false, "is_linklocal_unicast()"); // // Test if an address is a valid interface-local multicast address. // verbose_assert(ip4_multicast1.is_interfacelocal_multicast() == false, "is_interfacelocal_multicast()"); verbose_assert(ip4_multicast2.is_interfacelocal_multicast() == false, "is_interfacelocal_multicast()"); verbose_assert(ip4_multicast3.is_interfacelocal_multicast() == false, "is_interfacelocal_multicast()"); // // Test if an address is a valid link-local multicast address. // verbose_assert(ip4_multicast_linklocal1.is_linklocal_multicast() == true, "is_linklocal_multicast()"); verbose_assert(ip4_multicast2.is_linklocal_multicast() == false, "is_linklocal_multicast()"); verbose_assert(ip4_multicast3.is_linklocal_multicast() == false, "is_linklocal_multicast()"); // // Test if an address is a valid loopback address. // verbose_assert(ip4_loopback1.is_loopback() == true, "is_loopback()"); verbose_assert(ip4_loopback2.is_loopback() == true, "is_loopback()"); verbose_assert(ip4_zero.is_loopback() == false, "is_loopback()"); verbose_assert(ip4_unicast1.is_loopback() == false, "is_loopback()"); verbose_assert(ip4_unicast2.is_loopback() == false, "is_loopback()"); verbose_assert(ip4_unicast3.is_loopback() == false, "is_loopback()"); } /** * Test IPv4 address constant values. */ void test_ipv4_address_const() { // // Test the address octet-size. // verbose_assert(IPv4::addr_bytelen() == 4, "addr_bytelen()"); // // Test the address bit-length. // verbose_assert(IPv4::addr_bitlen() == 32, "addr_bitlen()"); // // Test the mask length for the multicast base address. // verbose_assert(IPv4::ip_multicast_base_address_mask_len() == 4, "ip_multicast_base_address_mask_len()"); // // Test the mask length for the Class A base address. // verbose_assert(IPv4::ip_class_a_base_address_mask_len() == 1, "ip_class_a_base_address_mask_len()"); // // Test the mask length for the Class B base address. // verbose_assert(IPv4::ip_class_b_base_address_mask_len() == 2, "ip_class_b_base_address_mask_len()"); // // Test the mask length for the Class C base address. // verbose_assert(IPv4::ip_class_c_base_address_mask_len() == 3, "ip_class_c_base_address_mask_len()"); // // Test the mask length for the experimental base address. // verbose_assert(IPv4::ip_experimental_base_address_mask_len() == 4, "ip_experimental_base_address_mask_len()"); // // Test the address family. // verbose_assert(IPv4::af() == AF_INET, "af()"); // // Test the IP protocol version. // verbose_assert(IPv4::ip_version() == 4, "ip_version()"); verbose_assert(IPv4::ip_version_str() == "IPv4", "ip_version_str()"); // // Test pre-defined constant addresses // verbose_assert(IPv4::ZERO() == IPv4("0.0.0.0"), "ZERO()"); verbose_assert(IPv4::ANY() == IPv4("0.0.0.0"), "ANY()"); verbose_assert(IPv4::ALL_ONES() == IPv4("255.255.255.255"), "ALL_ONES()"); verbose_assert(IPv4::LOOPBACK() == IPv4("127.0.0.1"), "LOOPBACK()"); verbose_assert(IPv4::MULTICAST_BASE() == IPv4("224.0.0.0"), "MULTICAST_BASE()"); verbose_assert(IPv4::MULTICAST_ALL_SYSTEMS() == IPv4("224.0.0.1"), "MULTICAST_ALL_SYSTEMS()"); verbose_assert(IPv4::MULTICAST_ALL_ROUTERS() == IPv4("224.0.0.2"), "MULTICAST_ALL_ROUTERS()"); verbose_assert(IPv4::DVMRP_ROUTERS() == IPv4("224.0.0.4"), "DVMRP_ROUTERS()"); verbose_assert(IPv4::OSPFIGP_ROUTERS() == IPv4("224.0.0.5"), "OSPFIGP_ROUTERS()"); verbose_assert(IPv4::OSPFIGP_DESIGNATED_ROUTERS() == IPv4("224.0.0.6"), "OSPIGP_DESIGNATED_ROUTERS()"); verbose_assert(IPv4::RIP2_ROUTERS() == IPv4("224.0.0.9"), "RIP2_ROUTERS()"); verbose_assert(IPv4::PIM_ROUTERS() == IPv4("224.0.0.13"), "PIM_ROUTERS()"); verbose_assert(IPv4::SSM_ROUTERS() == IPv4("224.0.0.22"), "SSM_ROUTERS()"); verbose_assert(IPv4::CLASS_A_BASE() == IPv4("0.0.0.0"), "CLASS_A_BASE()"); verbose_assert(IPv4::CLASS_B_BASE() == IPv4("128.0.0.0"), "CLASS_B_BASE()"); verbose_assert(IPv4::CLASS_C_BASE() == IPv4("192.0.0.0"), "CLASS_C_BASE()"); verbose_assert(IPv4::EXPERIMENTAL_BASE() == IPv4("240.0.0.0"), "EXPERIMENTAL_BASE()"); } /** * Test IPv4 address manipulation. */ void test_ipv4_manipulate_address() { // // Test making an IPv4 mask prefix. // verbose_assert(IPv4().make_prefix(24) == IPv4("255.255.255.0"), "make_prefix()"); verbose_assert(IPv4().make_prefix(0) == IPv4("0.0.0.0"), "make_prefix()"); verbose_assert(IPv4().make_prefix(32) == IPv4("255.255.255.255"), "make_prefix()"); // // Test making an IPv4 address prefix. // verbose_assert( IPv4("12.34.56.78").mask_by_prefix_len(24) == IPv4("12.34.56.0"), "mask_by_prefix_len()" ); // // Test getting the prefix length of the contiguous mask. // verbose_assert(IPv4("255.255.255.0").mask_len() == 24, "mask_len()"); // // Test getting the raw value of the address. // uint32_t n = htonl((12 << 24) | (34 << 16) | (56 << 8) | 78); verbose_assert(IPv4("12.34.56.78").addr() == n, "addr()"); // // Test setting the address value // IPv4 ip_a("1.2.3.4"); ip_a.set_addr(htonl((12 << 24) | (34 << 16) | (56 << 8) | 78)); verbose_assert(ip_a == IPv4("12.34.56.78"), "set_addr()"); // // Test extracting bits from an address. // verbose_assert(IPv4("12.34.56.78").bits(0, 8) == 78, "bits()"); // // Test counting the number of bits in an address. // verbose_assert(IPv4::ZERO().bit_count() == 0, "bit_count()"); verbose_assert(IPv4::ALL_ONES().bit_count() == 32, "bit_count()"); verbose_assert(IPv4("240.15.240.15").bit_count() == 16, "bit_count()"); // // Test counting the number of leading zeroes in an address. // verbose_assert(IPv4::ZERO().leading_zero_count() == 32, "leading_zero_count()"); verbose_assert(IPv4::ALL_ONES().leading_zero_count() == 0, "leading_zero_count()"); verbose_assert(IPv4("0.1.255.255").leading_zero_count() == 15, "leading_zero_count()"); } /** * Test IPv4 invalid address manipulation. */ void test_ipv4_invalid_manipulate_address() { const char *addr_string4 = "12.34.56.78"; // // Test making an invalid IPv4 mask prefix. // try { // Invalid prefix length IPv4 ip(IPv4::make_prefix(IPv4::addr_bitlen() + 1)); verbose_log("Cannot catch invalid IPv4 mask prefix with length %u : FAIL\n", XORP_UINT_CAST(IPv4::addr_bitlen() + 1)); incr_failures(); UNUSED(ip); } catch (const InvalidNetmaskLength& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } // // Test masking with an invalid IPv4 mask prefix. // try { // Invalid mask prefix IPv4 ip(addr_string4); ip.mask_by_prefix_len(IPv4::addr_bitlen() + 1); verbose_log("Cannot catch masking with an invalid IPv4 mask prefix with length %u : FAIL\n", XORP_UINT_CAST(IPv4::addr_bitlen() + 1)); incr_failures(); } catch (const InvalidNetmaskLength& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } } int main(int argc, char * const argv[]) { int ret_value = 0; // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); int ch; while ((ch = getopt(argc, argv, "hv")) != -1) { switch (ch) { case 'v': set_verbose(true); break; case 'h': case '?': default: usage(argv[0]); xlog_stop(); xlog_exit(); if (ch == 'h') return (0); else return (1); } } argc -= optind; argv += optind; XorpUnexpectedHandler x(xorp_unexpected_handler); try { test_ipv4_valid_constructors(); test_ipv4_invalid_constructors(); test_ipv4_valid_copy_in_out(); test_ipv4_invalid_copy_in_out(); test_ipv4_operators(); test_ipv4_address_type(); test_ipv4_address_const(); test_ipv4_manipulate_address(); test_ipv4_invalid_manipulate_address(); ret_value = failures() ? 1 : 0; } catch (...) { // Internal error xorp_print_standard_exceptions(); ret_value = 2; } // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); return (ret_value); } xorp/libxorp/tests/test_ipnet.cc0000664000076400007640000001555711540224227017202 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "libxorp_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/exceptions.hh" #ifdef HAVE_GETOPT_H #include #endif #include "ipv4net.hh" // ---------------------------------------------------------------------------- // Verbose output static bool s_verbose = false; bool verbose() { return s_verbose; } void set_verbose(bool v) { s_verbose = v; } static int s_failures = 0; bool failures() { return s_failures; } void incr_failures() { s_failures++; } #include "libxorp/xorp_tests.hh" // ---------------------------------------------------------------------------- // The tests static bool v4_serialization_test() { struct s_data { const char* net; IPv4 v4; uint32_t prefix_len; } srep[] = { { "128.16.92.160/27", IPv4(htonl_literal(0x80105ca0U)), 27 }, { "128.16.0.0/16", IPv4(htonl_literal(0x80100000U)), 16 }, { "255.255.255.255/32", IPv4(htonl_literal(0xffffffffU)), 32}, { "0.0.0.0/16", IPv4(htonl_literal(0x00000000U)), 16 }, }; for (size_t i = 0; i < sizeof(srep) / sizeof(srep[0]); i++) { IPv4Net n(srep[i].net); if (n.prefix_len() != srep[i].prefix_len) { verbose_log("Test Failed: item %u bad prefix_len %u\n", XORP_UINT_CAST(i), XORP_UINT_CAST(n.prefix_len())); return false; } else if (n.masked_addr() != srep[i].v4) { verbose_log("Test Failed: item %u bad addr %s != %s\n", XORP_UINT_CAST(i), n.masked_addr().str().c_str(), srep[i].v4.str().c_str()); return false; } else if (n.netmask() != IPv4::make_prefix(n.prefix_len())) { verbose_log("Test Failed: item %u bad netmask %s != %s\n", XORP_UINT_CAST(i), n.netmask().str().c_str(), IPv4::make_prefix(n.prefix_len()).str().c_str()); return false; } IPv4Net u (n.str().c_str()); if (u != n) { verbose_log("Test Failed: item %u to string and back failed.", XORP_UINT_CAST(i)); return false; } } verbose_log("Test Passed: serialization test.\n"); return true; } static bool v4_less_than_test() { const char* d[] = { "128.16.0.0/24", "128.16.64.0/24", "128.16.0.0/16", "128.17.0.0/24" }; for (size_t i = 1; i < sizeof(d) / sizeof(d[0]); i++) { IPv4Net lower(d[i - 1]); IPv4Net upper(d[i]); if (!(lower < upper)) { verbose_log("Test Failed: %s is not less than %s\n", lower.str().c_str(), upper.str().c_str()); return false; } } verbose_log("Test Passed: operator< test.\n"); return true; } static bool v4_is_unicast_test() { IPv4Net uni("128.16.0.0/24"); // regular unicast IPv4Net multi("224.0.0.0/24"); // multicast, not valid IPv4Net odd1("128.0.0.0/1"); // not valid, includes multicast IPv4Net odd2("192.0.0.0/2"); // not valid, includes multicast IPv4Net odd3("0.0.0.0/1"); // only unicast, should be valid IPv4Net odd4("128.0.0.0/2"); // only unicast, should be valid IPv4Net deflt("0.0.0.0/0"); // default route, is valid if (!uni.is_unicast()) { verbose_log("Test Failed: %s failed is_unicast test.\n", uni.str().c_str()); return false; } if (multi.is_unicast()) { verbose_log("Test Failed: %s failed is_unicast test.\n", multi.str().c_str()); return false; } if (odd1.is_unicast()) { verbose_log("Test Failed: %s failed is_unicast test.\n", odd1.str().c_str()); return false; } if (odd2.is_unicast()) { verbose_log("Test Failed: %s failed is_unicast test.\n", odd2.str().c_str()); return false; } if (!odd3.is_unicast()) { verbose_log("Test Failed: %s failed is_unicast test.\n", odd3.str().c_str()); return false; } if (!odd4.is_unicast()) { verbose_log("Test Failed: %s failed is_unicast test.\n", odd4.str().c_str()); return false; } if (!deflt.is_unicast()) { verbose_log("Test Failed: %s failed is_unicast test.\n", deflt.str().c_str()); return false; } verbose_log("Test Passed: is_unicast test.\n"); return true; } static bool v4_overlap_test() { // Address that overlap with netmask struct x { const char* a; const char* b; size_t overlap; } data[] = { { "180.10.10.0", "180.10.8.0", 22 }, { "180.10.10.0", "180.10.200.0", 16 }, { "180.10.20.0", "180.255.200.0", 8 }, }; for (size_t i = 0; i < sizeof(data) / sizeof(data[0]); i++) { for (size_t p = 0; p < 32; p++) { IPv4 a(data[i].a); IPv4 b(data[i].b); IPv4Net u(a, p); IPv4Net v(b, p); if (u.is_overlap(v) != (p <= data[i].overlap)) { verbose_log("Test Failed: bad overlap %u\n", XORP_UINT_CAST(p)); return -1; } } } verbose_log("Test Passed: overlap test.\n"); return true; } static bool test_ipv4net_okay() { return (v4_serialization_test() == true) & (v4_less_than_test() == true) & (v4_overlap_test() == true) & (v4_is_unicast_test() == true); } // ---------------------------------------------------------------------------- // Usage static void usage(const char* argv0) { fprintf(stderr, "usage: %s [-v]\n", argv0); fprintf(stderr, "A test program for XORP ipnet classes\n"); } // ---------------------------------------------------------------------------- // main int main(int argc, char* const* argv) { // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); int ch; while ((ch = getopt(argc, argv, "hv")) != -1) { switch (ch) { case 'v': set_verbose(true); break; case 'h': case '?': default: usage(argv[0]); xlog_stop(); xlog_exit(); return -1; } } argc -= optind; argv += optind; int r = 0; XorpUnexpectedHandler x(xorp_unexpected_handler); try { if (test_ipv4net_okay() == false) { r = -2; } } catch (...) { xorp_catch_standard_exceptions(); r = -3; } // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); return r; } xorp/libxorp/tests/test_mac.cc0000664000076400007640000003304411540224230016604 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "libxorp_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/exceptions.hh" #include "libxorp/timer.hh" #include "libxorp/test_main.hh" #ifdef HAVE_GETOPT_H #include #endif #include "mac.hh" // // TODO: XXX: remove after the switch to the TestMain facility is completed // #if 0 // // XXX: MODIFY FOR YOUR TEST PROGRAM // static const char *program_name = "test_mac"; static const char *program_description = "Test Mac address class"; static const char *program_version_id = "0.1"; static const char *program_date = "October 17, 2007"; static const char *program_copyright = "See file LICENSE"; static const char *program_return_value = "0 on success, 1 if test error, 2 if internal error"; #endif // 0 static bool s_verbose = false; bool verbose() { return s_verbose; } void set_verbose(bool v) { s_verbose = v; } static int s_failures = 0; bool failures() { return (s_failures)? (true) : (false); } void incr_failures() { s_failures++; } void reset_failures() { s_failures = 0; } #include "libxorp/xorp_tests.hh" // // TODO: XXX: remove after the switch to the TestMain facility is completed // #if 0 /** * Print program info to output stream. * * @param stream the output stream the print the program info to. */ static void print_program_info(FILE *stream) { fprintf(stream, "Name: %s\n", program_name); fprintf(stream, "Description: %s\n", program_description); fprintf(stream, "Version: %s\n", program_version_id); fprintf(stream, "Date: %s\n", program_date); fprintf(stream, "Copyright: %s\n", program_copyright); fprintf(stream, "Return: %s\n", program_return_value); } /** * Print program usage information to the stderr. * * @param progname the name of the program. */ static void usage(const char* progname) { print_program_info(stderr); fprintf(stderr, "usage: %s [-v] [-h]\n", progname); fprintf(stderr, " -h : usage (this message)\n"); fprintf(stderr, " -v : verbose output\n"); } #endif // 0 /** * Test Mac valid constructors. */ bool test_mac_valid_constructors(TestInfo& test_info) { UNUSED(test_info); // Test values for Mac address: "11:22:33:44:55:66" const char* addr_string = "11:22:33:44:55:66"; const uint8_t ui8_addr[] = { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66 }; struct ether_addr ether_addr; struct sockaddr sa; // Initialize the structures memcpy(ðer_addr, ui8_addr, sizeof(ui8_addr)); memset(&sa, 0, sizeof(sa)); memcpy(sa.sa_data, ui8_addr, sizeof(ui8_addr)); // // Default constructor. // Mac mac1; verbose_match(mac1.str(), Mac::ZERO().str()); // // Constructor from a (uint8_t *) memory pointer. // Mac mac2(ui8_addr); verbose_match(mac2.str(), addr_string); // // Constructor from a string. // Mac mac3(addr_string); verbose_match(mac3.str(), addr_string); // // Constructor from ether_addr structure. // Mac mac4(ether_addr); verbose_match(mac4.str(), addr_string); // // Constructor from sockaddr structure. // Mac mac5(sa); verbose_match(mac5.str(), addr_string); // // Constructor from another Mac address. // Mac mac6(mac2); verbose_match(mac6.str(), addr_string); return (! failures()); } /** * Test Mac invalid constructors. */ bool test_mac_invalid_constructors(TestInfo& test_info) { UNUSED(test_info); // // Constructor from an invalid address string. // try { // Invalid address string Mac mac("hello"); verbose_log("Cannot catch invalid Mac address \"hello\" : FAIL\n"); incr_failures(); UNUSED(mac); } catch (const InvalidString& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } return (! failures()); } /** * Test Mac valid copy in/out methods. */ bool test_mac_valid_copy_in_out(TestInfo& test_info) { UNUSED(test_info); // Test values for Mac address: "11:22:33:44:55:66" const char* addr_string = "11:22:33:44:55:66"; const uint8_t ui8_addr[] = { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66 }; struct ether_addr ether_addr; struct sockaddr sa; // Initialize the structures memcpy(ðer_addr, ui8_addr, sizeof(ui8_addr)); memset(&sa, 0, sizeof(sa)); memcpy(sa.sa_data, ui8_addr, sizeof(ui8_addr)); // // Copy the Mac raw address to specified memory location. // Mac mac1(addr_string); uint8_t mac1_uint8[6]; verbose_assert(mac1.copy_out(&mac1_uint8[0]) == 6, "copy_out(uint8_t *) for Mac address"); verbose_assert(memcmp(&ui8_addr, &mac1_uint8[0], 6) == 0, "compare copy_out(uint8_t *) for Mac address"); // // Copy the Mac raw address to ether_addr structure. // Mac mac2(addr_string); struct ether_addr mac2_ether_addr; verbose_assert(mac2.copy_out(mac2_ether_addr) == 6, "copy_out(struct ether_addr) for Mac address"); verbose_assert(memcmp(&ui8_addr, &mac2_ether_addr, 6) == 0, "compare copy_out(struct ether_addr) for Mac address"); // // Copy the Mac raw address to sockaddr structure. // Mac mac3(addr_string); struct sockaddr mac3_sockaddr; verbose_assert(mac3.copy_out(mac3_sockaddr) == 6, "copy_out(struct sockaddr) for Mac address"); verbose_assert(memcmp(&ui8_addr, &mac3_sockaddr.sa_data, 6) == 0, "compare copy_out(struct sockaddr) for Mac address"); // // Copy a raw address into Mac address. // Mac mac4; verbose_assert(mac4.copy_in(&ui8_addr[0]) == 6, "copy_in(uint8_t *) for Mac address"); verbose_match(mac4.str(), addr_string); // // Copy an ether_addr struct address into Mac address. // Mac mac5; verbose_assert(mac5.copy_in(ether_addr) == 6, "copy_in(struct ether_addr) for Mac address"); verbose_match(mac5.str(), addr_string); // // Copy a sockaddr struct address into Mac address. // Mac mac6; verbose_assert(mac6.copy_in(sa) == 6, "copy_in(struct sockaddr) for Mac address"); verbose_match(mac6.str(), addr_string); // // Copy a string address into Mac address. // Mac mac7; verbose_assert(mac7.copy_in(addr_string) == 6, "copy_in(char *) for Mac address"); verbose_match(mac7.str(), addr_string); return (! failures()); } /** * Test Mac invalid copy in/out methods. */ bool test_mac_invalid_copy_in_out(TestInfo& test_info) { UNUSED(test_info); // // Copy-in from invalid string. // try { Mac mac; mac.copy_in("hello"); verbose_log("Cannot catch invalid Mac address \"hello\" : FAIL\n"); incr_failures(); } catch (const InvalidString& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } return (! failures()); } /** * Test Mac operators. */ bool test_mac_operators(TestInfo& test_info) { UNUSED(test_info); Mac mac_a("00:00:00:00:00:00"); Mac mac_b("11:11:11:11:11:11"); // // Less-Than Operator // verbose_assert(mac_a < mac_b, "operator<"); // // Equality Operator // verbose_assert(mac_a == mac_a, "operator=="); verbose_assert(!(mac_a == mac_b), "operator=="); // // Not-Equal Operator // verbose_assert(!(mac_a != mac_a), "operator!="); verbose_assert(mac_a != mac_b, "operator!="); return (! failures()); } /** * Test Mac address type. */ bool test_mac_address_type(TestInfo& test_info) { UNUSED(test_info); Mac mac_empty; // Empty address Mac mac_zero("00:00:00:00:00:00"); // Zero address Mac mac_unicast("00:11:11:11:11:11"); // Unicast Mac mac_multicast("01:22:22:22:22:22"); // Multicast // // Test the size of an address (in octets). // verbose_assert(mac_empty.addr_bytelen() == 6, "addr_bytelen()"); verbose_assert(mac_zero.addr_bytelen() == 6, "addr_bytelen()"); verbose_assert(mac_unicast.addr_bytelen() == 6, "addr_bytelen()"); verbose_assert(mac_multicast.addr_bytelen() == 6, "addr_bytelen()"); // // Test the size of an address (in bits). // verbose_assert(mac_empty.addr_bitlen() == 48, "addr_bitlen()"); verbose_assert(mac_zero.addr_bitlen() == 48, "addr_bitlen()"); verbose_assert(mac_unicast.addr_bitlen() == 48, "addr_bitlen()"); verbose_assert(mac_multicast.addr_bitlen() == 48, "addr_bitlen()"); // // Test if an address is numerically zero. // verbose_assert(mac_empty.is_zero() == true, "is_zero()"); verbose_assert(mac_zero.is_zero() == true, "is_zero()"); verbose_assert(mac_unicast.is_zero() == false, "is_zero()"); verbose_assert(mac_multicast.is_zero() == false, "is_zero()"); // // Test if an address is a valid unicast address. // verbose_assert(mac_empty.is_unicast() == true, "is_unicast()"); verbose_assert(mac_zero.is_unicast() == true, "is_unicast()"); verbose_assert(mac_unicast.is_unicast() == true, "is_unicast()"); verbose_assert(mac_multicast.is_unicast() == false, "is_unicast()"); // // Test if an address is a valid multicast address. // verbose_assert(mac_empty.is_multicast() == false, "is_multicast()"); verbose_assert(mac_zero.is_multicast() == false, "is_multicast()"); verbose_assert(mac_unicast.is_multicast() == false, "is_multicast()"); verbose_assert(mac_multicast.is_multicast() == true, "is_multicast()"); return (! failures()); } /** * Test Mac address constant values. */ bool test_mac_address_const(TestInfo& test_info) { UNUSED(test_info); // // Test the number of bits in address // verbose_assert(Mac::ADDR_BITLEN == 48, "ADDR_BITLEN"); // // Test the number of bytes in address // verbose_assert(Mac::ADDR_BYTELEN == 6, "ADDR_BYTELEN"); // // Test the multicast bit in the first octet of the address // verbose_assert(Mac::MULTICAST_BIT == 0x1, "MULTICAST_BIT"); // // Test pre-defined constant addresses // verbose_assert(Mac::ZERO() == Mac("00:00:00:00:00:00"), "ZERO()"); verbose_assert(Mac::ALL_ONES() == Mac("ff:ff:ff:ff:ff:ff"), "ALL_ONES()"); verbose_assert(Mac::BROADCAST() == Mac("ff:ff:ff:ff:ff:ff"), "BROADCAST()"); verbose_assert(Mac::STP_MULTICAST() == Mac("01:80:c2:00:00:00"), "STP_MULTICAST()"); verbose_assert(Mac::LLDP_MULTICAST() == Mac("01:80:c2:00:00:0e"), "LLDP_MULTICAST()"); verbose_assert(Mac::GMRP_MULTICAST() == Mac("01:80:c2:00:00:20"), "GMRP_MULTICAST()"); verbose_assert(Mac::GVRP_MULTICAST() == Mac("01:80:c2:00:00:21"), "GVRP_MULTICAST()"); return (! failures()); } int main(int argc, char * const argv[]) { XorpUnexpectedHandler x(xorp_unexpected_handler); // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); TestMain test_main(argc, argv); string test = test_main.get_optional_args("-t", "--test", "run only the specified test"); test_main.complete_args_parsing(); // // TODO: XXX: a temporary glue until we complete the switch to the // TestMain facility. // if (test_main.get_verbose()) set_verbose(true); struct test { string test_name; XorpCallback1::RefPtr cb; bool run_by_default; } tests[] = { { "test_mac_valid_constructors", callback(test_mac_valid_constructors), true }, { "test_mac_invalid_constructors", callback(test_mac_invalid_constructors), true }, { "test_mac_valid_copy_in_out", callback(test_mac_valid_copy_in_out), true }, { "test_mac_invalid_copy_in_out", callback(test_mac_invalid_copy_in_out), true }, { "test_mac_operators", callback(test_mac_operators), true }, { "test_mac_address_type", callback(test_mac_address_type), true }, { "test_mac_address_const", callback(test_mac_address_const), true } }; try { if (test.empty()) { for (size_t i = 0; i < sizeof(tests) / sizeof(struct test); i++) { if (! tests[i].run_by_default) continue; reset_failures(); test_main.run(tests[i].test_name, tests[i].cb); } } else { for (size_t i = 0; i < sizeof(tests) / sizeof(struct test); i++) { if (test == tests[i].test_name) { reset_failures(); test_main.run(tests[i].test_name, tests[i].cb); return test_main.exit(); } } test_main.failed("No test with name " + test + " found\n"); } } catch (...) { xorp_print_standard_exceptions(); } // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); return test_main.exit(); } xorp/libxorp/tests/test_sched.cc0000664000076400007640000002671011540224230017134 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "libxorp_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/eventloop.hh" #include "libxorp/exceptions.hh" #include "libxorp/asyncio.hh" #ifdef HAVE_GETOPT_H #include #endif // XXX we can't depend on libcomm because make check will compile and perform // testing together, so when we're testing libxorp, libcomm won't be built yet. namespace Libcomm { #include "libcomm/comm_user.c" #include "libcomm/comm_sock.c" } // namespace Libcomm // // Test the scheduling of file descriptor handling, tasks and timers, checking // for fariness, starvation and deadlocks. // #define TEST_ASSERT(x) \ if (!(x)) \ xorp_throw(TestException, "Test Failed: assertion failed") namespace { EventLoop _eventloop; int _debug; bool _run_fail = false; class TestException : public XorpReasonedException { public: TestException(const char* file, size_t line, const string& why = "") : XorpReasonedException("TestException", file, line, why) {} }; class Socket { public: Socket(const xsock_t& s); ~Socket(); void write_sync(unsigned len); void write(unsigned len); void write_cb(AsyncFileWriter::Event ev, const uint8_t* buf, size_t len, size_t done); void read(unsigned len); void read_cb(AsyncFileReader::Event ev, const uint8_t* buf, size_t len, size_t done); bool did_read(); bool did_write(); unsigned readb(); unsigned writeb(); void set_priority(int priority); private: typedef map BUFFERS; // data -> len void create_ops(int priority); uint8_t* create_buffer(unsigned len); void io_cb(AsyncFileOperator::Event ev, const uint8_t* buf, size_t len, size_t done); xsock_t _s; AsyncFileReader *_reader; AsyncFileWriter *_writer; BUFFERS _buffers; unsigned _readb; unsigned _writeb; bool _did_read; bool _did_write; }; class SocketPair { public: SocketPair(); ~SocketPair(); void write_sync(unsigned len); void read(unsigned len); bool did_read(); unsigned readb(); Socket& s0(); Socket& s1(); void set_priority(int priority); private: Socket* _s[2]; }; Socket::Socket(const xsock_t& s) : _s(s), _reader(NULL), _writer(NULL), _readb(0), _writeb(0), _did_read(false), _did_write(false) { if (Libcomm::comm_sock_set_blocking(_s, 0) != XORP_OK) xorp_throw(TestException, "comm_sock_set_blocking()"); create_ops(XorpTask::PRIORITY_DEFAULT); } Socket::~Socket() { delete _reader; delete _writer; if (Libcomm::comm_sock_close(_s) != XORP_OK) xorp_throw(TestException, "comm_sock_close()"); for (BUFFERS::iterator i = _buffers.begin(); i != _buffers.end(); ++i) delete [] i->first; } void Socket::set_priority(int priority) { create_ops(priority); } void Socket::create_ops(int priority) { delete _reader; delete _writer; if (_reader) { XLOG_ASSERT(_writer); XLOG_ASSERT(!_reader->running()); XLOG_ASSERT(!_writer->running()); } _reader = new AsyncFileReader(_eventloop, _s, priority); _writer = new AsyncFileWriter(_eventloop, _s, priority); } void Socket::write_sync(unsigned len) { write(len); // XXX need a watchdog in case we deadlock (i.e., TX queue fills). while (_writer->running()) _eventloop.run(); } void Socket::write(unsigned len) { uint8_t* buf = create_buffer(len); _writer->add_buffer(buf, len, callback(this, &Socket::write_cb)); if (!_writer->running()) _writer->start(); } uint8_t* Socket::create_buffer(unsigned len) { uint8_t* buf = new uint8_t[len]; XLOG_ASSERT(buf); XLOG_ASSERT(_buffers.find(buf) == _buffers.end()); _buffers[buf] = len; return buf; } void Socket::write_cb(AsyncFileWriter::Event ev, const uint8_t* buf, size_t len, size_t done) { io_cb(ev, buf, len, done); _writeb += done; _did_write = true; } void Socket::read(unsigned len) { uint8_t* buf = create_buffer(len); _reader->add_buffer(buf, len, callback(this, &Socket::read_cb)); if (!_reader->running()) _reader->start(); } void Socket::read_cb(AsyncFileReader::Event ev, const uint8_t* buf, size_t len, size_t done) { io_cb(ev, buf, len, done); _readb += done; _did_read = true; } void Socket::io_cb(AsyncFileReader::Event ev, const uint8_t* buf, size_t len, size_t done) { XLOG_ASSERT(ev == AsyncFileOperator::DATA); BUFFERS::iterator i = _buffers.find(const_cast(buf)); XLOG_ASSERT(i != _buffers.end()); XLOG_ASSERT(i->second == len); XLOG_ASSERT(len == done); delete i->first; _buffers.erase(i); } bool Socket::did_read() { bool x = _did_read; _did_read = false; return x; } bool Socket::did_write() { bool x = _did_write; _did_write = false; return x; } unsigned Socket::readb() { return _readb; } unsigned Socket::writeb() { return _writeb; } SocketPair::SocketPair() { xsock_t s[2]; memset(_s, 0, sizeof(_s)); if (Libcomm::comm_sock_pair(AF_UNIX, SOCK_STREAM, 0, s) != XORP_OK) xorp_throw(TestException, "comm_sock_pair()"); for (unsigned i = 0; i < 2; i++) _s[i] = new Socket(s[i]); } SocketPair::~SocketPair() { for (unsigned i = 0; i < 2; i++) delete _s[i]; } unsigned SocketPair::readb() { return s0().readb(); } Socket& SocketPair::s0() { return *_s[0]; } Socket& SocketPair::s1() { return *_s[1]; } bool SocketPair::did_read() { return s0().did_read(); } void SocketPair::write_sync(unsigned len) { return s1().write_sync(len); } void SocketPair::read(unsigned len) { return s0().read(len); } void SocketPair::set_priority(int priority) { for (unsigned i = 0; i < 2; i++) _s[i]->set_priority(priority); } void wait_for_read(SocketPair* s, const unsigned num) { while (1) { for (unsigned i = 0; i < num; i++) { if (s[i].did_read()) return; } _eventloop.run(); } } void xprintf(const char *fmt, ...) { va_list ap; if (!_debug) return; va_start(ap, fmt); vprintf(fmt, ap); va_end(ap); } void feed(SocketPair* s, unsigned num, unsigned len = 1024) { for (unsigned i = 0; i < num; i++) s[i].write_sync(len); } void eat(SocketPair* s, unsigned num, unsigned len = 1) { for (unsigned i = 0; i < num; i++) s[i].read(len); } void eat_and_wait(SocketPair* s, unsigned num, unsigned len = 1) { eat(s, num, len); wait_for_read(s, num); } // Two readers with same priority - see if one starves. void test_fd_2read_starve(void) { const unsigned num = 2; SocketPair s[num]; unsigned times = 10; xprintf("Running %s\n", __FUNCTION__); // give both sockets stuff to read feed(s, num); // read stuff from both sockets for (unsigned i = 0; i < times; i++) eat_and_wait(s, 2); // check what happened for (unsigned i = 0; i < num; i++) { unsigned x = s[i].readb(); xprintf("Socket %u read %u\n", i, x); TEST_ASSERT(x != 0); } #if 0 // could check for fairness while we're at it XLOG_ASSERT((times & 1) == 0); TEST_ASSERT(s[0].readb() == s[1].readb()); #endif } // Reading & writing on the same FD - see if one operation starves. void test_fd_read_write_starve() { SocketPair p; unsigned times = 10; xprintf("Running %s\n", __FUNCTION__); // give it enough stuff to read feed(&p, 1); // read & write on socket. Socket& s = p.s0(); for (unsigned i = 0; i < times; i++) { s.read(1); s.write(1); while (!s.did_read() && !s.did_write()) _eventloop.run(); } // check results xprintf("Read %u Wrote %u\n", s.readb(), s.writeb()); TEST_ASSERT(s.readb() != 0); TEST_ASSERT(s.writeb() != 0); #if 0 // check fairness XLOG_ASSERT((times & 1) == 0); TEST_ASSERT(s.readb() == s.writeb()); #endif } // There are two low priority readers, and both should get serviced round robin. // However, we introduce a high priority reader "every other round" and check // whether the low priority readers get starved. void test_fd_1high_2low_starve(void) { const unsigned num = 2; SocketPair s[num], high; unsigned times = 10; xprintf("Running %s\n", __FUNCTION__); high.set_priority(XorpTask::PRIORITY_HIGH); // give the sockets something to eat feed(s, num); feed(&high, 1); // constantly have the low priority guys read, and introduce the high // priority guy after each low priority event. for (unsigned i = 0; i < times; i++) { // read eat_and_wait(s, num); // OK our low priority dudes read something. Lets get the high priority // guy into the game to see if scheduling state gets "reset". eat(&high, 1); while (!high.did_read()) _eventloop.run(); for (unsigned j = 0; j < num; j++) { XLOG_ASSERT(!s[0].did_read()); } } // see if any of the low priority guys got starved for (unsigned i = 0; i < num; i++) { unsigned x = s[i].readb(); xprintf("Socket %u read %u\n", i, x); TEST_ASSERT(x != 0); } } struct Test { void (*_run)(void); bool _fails; const char* _desc; }; struct Test _tests[] = { { test_fd_2read_starve, false, "Two readers, same priority, check starve" }, { test_fd_read_write_starve, false, "RW same FD & priority, check starve" }, { test_fd_1high_2low_starve, true, "High pri. starving some low pri." }, }; void do_tests(void) { for (unsigned i = 0; i < sizeof(_tests) / sizeof(*_tests); ++i) { struct Test& t = _tests[i]; if (!_run_fail && t._fails) { xprintf("Skipping test #%u [%s]\n", i + 1, t._desc); continue; } xprintf("Running test #%u%s [%s]\n", i + 1, t._fails ? " (known to fail)" : "", t._desc); t._run(); } } } // anonymous namespace int main(int argc, char *argv[]) { int rc = 0; int ch; xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); while ((ch = getopt(argc, argv, "hvf")) != -1) { switch (ch) { case 'h': printf("Usage: %s \n" "-h\thelp\n" "-v\tverbose\n" "-f\trun tests known to fail\n" , argv[0]); exit(1); case 'f': _run_fail = true; break; case 'v': _debug++; break; } } if (Libcomm::comm_init() != XORP_OK) XLOG_FATAL("comm_init()"); try { do_tests(); } catch(const TestException& e) { cout << "TestException: " << e.str() << endl; rc = 1; } if (rc == 0) xprintf("All good\n"); else xprintf("=(\n"); Libcomm::comm_exit(); xlog_stop(); xlog_exit(); exit(rc); } xorp/libxorp/tests/SConscript0000664000076400007640000000354111540225530016513 0ustar greearbgreearb# Copyright (c) 2009-2011 XORP, Inc and Others # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # $XORP$ import os Import("env") env = env.Clone() env.AppendUnique(CPPPATH = [ '#', '$BUILDDIR', '$BUILDDIR/libxorp', ]) env.AppendUnique(LIBPATH = [ '$BUILDDIR/libxorp', '$BUILDDIR/libcomm', ]) env.AppendUnique(LIBS = [ 'xorp_core', 'xorp_comm' ]) tests = [ 'asyncio', 'callback', 'config_param', 'heap', 'ipnet', 'ipv4', 'ipv4net', 'ipv6', 'ipv6net', 'ipvx', 'ipvxnet', 'mac', 'observers', 'ref_ptr', 'ref_trie', 'run_command', # Wrapper script needed on Windows MinGW. 'sched', 'service', 'task', 'test_main', 'time_slice', 'timer', 'timeval', 'trie', 'types', 'utils', 'vif', ] if not (env.has_key('disable_profile') and env['disable_profile']): tests.append('profile') libxorptestpath = '$exec_prefix/libxorp/tests' test_targets = [] for t in tests: test_targets.append(env.AutoTest(target = 'test_%s' % t, source = 'test_%s.cc' % t)) if env['enable_tests']: env.Alias('install', env.InstallProgram(libxorptestpath, 'test_%s' %t)) if env['enable_tests']: Default(test_targets) xorp/libxorp/tests/test_ref_trie.cc0000664000076400007640000004712411540224230017647 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "libxorp_module.h" #include "xorp.h" #include "ipv4net.hh" #include "ipv6net.hh" #include "ref_trie.hh" static bool s_verbose = false; bool verbose() { return s_verbose; } void set_verbose(bool v) { s_verbose = v; } static int s_failures = 0; bool failures() { return (s_failures)? (true) : (false); } void incr_failures() { s_failures++; } void reset_failures() { s_failures = 0; } #include "libxorp/xorp_tests.hh" class IPv4RouteEntry {}; class IPv6RouteEntry {}; RefTrie trie; RefTrie trie6; void test(IPv4Net test_net, IPv4RouteEntry *test_route) { printf("-----------------------------------------------\n"); printf("looking up net: %s\n", test_net.str().c_str()); RefTrie::iterator ti = trie.lookup_node(test_net); if (ti == trie.end()) { print_failed("no result"); trie.print(); abort(); } const IPv4RouteEntry *r = ti.payload(); if (r==test_route) { print_passed("trie lookup net"); } else { print_failed("trie lookup net"); printf("route=%p\n", r); trie.print(); abort(); } printf("-----------\n"); } void test_find(IPv4 test_addr, IPv4RouteEntry *test_route) { printf("-----------------------------------------------\n"); printf("looking up net: %s\n", test_addr.str().c_str()); RefTrie::iterator ti = trie.find(test_addr); if (ti == trie.end()) { printf("Test Failed: no result\n"); trie.print(); abort(); } const IPv4RouteEntry *r = ti.payload(); if (r==test_route) { print_passed("trie find"); } else { print_failed("trie lookup net"); printf("route=%p\n", r); trie.print(); abort(); } printf("-----------\n"); } void test_less_specific(IPv4Net test_net, IPv4RouteEntry *test_route) { printf("-----------------------------------------------\n"); printf("looking up less specific for net: %s\n", test_net.str().c_str()); RefTrie::iterator ti = trie.find_less_specific(test_net); if (ti == trie.end()) { if (test_route == NULL) { print_passed("lookup less specific net"); printf("-----------\n"); return; } print_failed("no result\n"); trie.print(); abort(); } const IPv4RouteEntry *r = ti.payload(); if (r==test_route) { print_passed(""); } else { print_failed(""); printf("route=%p\n", r); trie.print(); abort(); } printf("-----------\n"); } void test_upper_bound(IPv4 test_addr, IPv4 test_answer) { printf("-----------------------------------------------\n"); printf("looking up upper bound: %s\n", test_addr.str().c_str()); IPv4 lo, hi; trie.find_bounds(test_addr, lo, hi); IPv4 result= hi; if (test_answer == result) { print_passed(result.str()); } else { print_failed(""); printf("answer should be %s, result was %s\n", test_answer.str().c_str(), result.str().c_str()); trie.print(); abort(); } printf("-----------\n"); } void test_lower_bound(IPv4 test_addr, IPv4 test_answer) { printf("-----------------------------------------------\n"); printf("looking up lower bound: %s\n", test_addr.str().c_str()); IPv4 lo, hi; trie.find_bounds(test_addr, lo, hi); IPv4 result= lo; if (test_answer == result) { print_passed(result.str()); } else { print_failed(""); printf("answer should be %s, result was %s\n", test_answer.str().c_str(), result.str().c_str()); trie.print(); abort(); } printf("-----------\n"); } void test6(IPv6Net test_net, IPv6RouteEntry *test_route) { printf("-----------------------------------------------\n"); printf("looking up net: %s\n", test_net.str().c_str()); RefTrie::iterator ti = trie6.lookup_node(test_net); if (ti == trie6.end()) { print_failed("no result\n"); trie.print(); abort(); } const IPv6RouteEntry *r = ti.payload(); if (r==test_route) { print_passed(""); } else { print_failed(""); printf("route=%p\n", r); trie.print(); abort(); } printf("-----------\n"); } void test_find6(IPv6 test_addr, IPv6RouteEntry *test_route) { printf("-----------------------------------------------\n"); printf("looking up net: %s\n", test_addr.str().c_str()); RefTrie::iterator ti = trie6.find(test_addr); const IPv6RouteEntry *r = ti.payload(); if (ti == trie6.end()) { print_failed("no result\n"); trie.print(); abort(); } if (r==test_route) { print_passed(""); } else { print_failed(""); printf("route=%p\n", r); trie.print(); abort(); } printf("-----------\n"); } void test_upper_bound6(IPv6 test_addr, IPv6 test_answer) { printf("-----------------------------------------------\n"); printf("looking up upper bound: %s\n", test_addr.str().c_str()); IPv6 lo, hi; trie6.find_bounds(test_addr, lo, hi); IPv6 result= hi; if (test_answer == result) { print_passed(result.str()); } else { print_failed(""); printf("answer should be %s, result was %s\n", test_answer.str().c_str(), result.str().c_str()); trie.print(); abort(); } printf("-----------\n"); } void test_equals_preorder() { printf("Remove entry that is being pointed at by an iterator\n"); // Create an trie and install a single entry RefTrie test_trie; IPv4RouteEntry d1; IPv4Net n1("1.2.1.0/24"); printf("adding n1: %s route: %p\n", n1.str().c_str(), &d1); test_trie.insert(n1, &d1); // Create an iterator pointing at this entry RefTrie::PreOrderIterator iter; iter = test_trie.begin(); // Erase the entry test_trie.erase(iter); // The iterator is now the only thing pointing at the entry. // Zap the iterator to allow the entry that is being pointed at to // be removed. Used to triger a bug in the iterator code. RefTrie::PreOrderIterator empty; iter = empty; print_passed(""); } void test_equals_postorder() { printf("Remove entry that is being pointed at by an iterator\n"); // Create an trie and install a single entry RefTrie test_trie; IPv4RouteEntry d1; IPv4Net n1("1.2.1.0/24"); printf("adding n1: %s route: %p\n", n1.str().c_str(), &d1); test_trie.insert(n1, &d1); // Create an iterator pointing at this entry RefTrie::PostOrderIterator iter; iter = test_trie.begin(); // Erase the entry test_trie.erase(iter); // The iterator is now the only thing pointing at the entry. // Zap the iterator to allow the entry that is being pointed at to // be removed. Used to triger a bug in the iterator code. RefTrie::PostOrderIterator empty; iter = empty; print_passed(""); } int main() { IPv4RouteEntry d1; IPv4Net n1(IPv4("1.2.1.0"), 24); printf("adding n1: %s route: %p\n", n1.str().c_str(), &d1); trie.insert(n1, &d1); test(n1, &d1); IPv4RouteEntry d2; IPv4Net n2(IPv4("1.2.0.0"), 16); printf("\n\nadding n2: %s route: %p\n", n2.str().c_str(), &d2); trie.insert(n2, &d2); test(n1, &d1); test(n2, &d2); IPv4RouteEntry d3; IPv4Net n3(IPv4("1.2.3.0"), 24); printf("\n\nadding n3: %s route: %p\n", n3.str().c_str(), &d3); trie.insert(n3, &d3); test(n1, &d1); test(n2, &d2); test(n3, &d3); IPv4RouteEntry d4; IPv4Net n4(IPv4("1.2.128.0"), 24); printf("\n\nadding n4: %s route: %p\n", n4.str().c_str(), &d4); trie.insert(n4, &d4); test(n1, &d1); test(n2, &d2); test(n3, &d3); test(n4, &d4); IPv4RouteEntry d5; IPv4Net n5(IPv4("1.2.0.0"), 20); printf("\n\nadding n5: %s route: %p\n", n5.str().c_str(), &d5); trie.insert(n5, &d5); test(n1, &d1); test(n2, &d2); test(n3, &d3); test(n4, &d4); test(n5, &d5); trie.print(); trie.erase(n5); test(n1, &d1); test(n2, &d2); test(n3, &d3); test(n4, &d4); trie.print(); trie.erase(n1); test(n2, &d2); test(n3, &d3); test(n4, &d4); trie.print(); trie.erase(n2); test(n3, &d3); test(n4, &d4); trie.print(); trie.insert(n2, &d2); trie.erase(n3); test(n2, &d2); test(n4, &d4); trie.print(); IPv4RouteEntry d6; IPv4Net n6(IPv4("1.2.192.0"), 24); printf("\n\nadding n6: %s route: %p\n", n6.str().c_str(), &d6); trie.insert(n6, &d6); test(n2, &d2); test(n4, &d4); test(n6, &d6); test_find(IPv4("1.2.192.1"), &d6); test_find(IPv4("1.2.191.1"), &d2); trie.print(); test_upper_bound(IPv4("1.2.190.1"), IPv4("1.2.191.255")); test_lower_bound(IPv4("1.2.190.1"), IPv4("1.2.129.0")); test_upper_bound(IPv4("1.2.120.1"), IPv4("1.2.127.255")); test_lower_bound(IPv4("1.2.120.1"), IPv4("1.2.0.0")); test_upper_bound(IPv4("1.2.192.1"), IPv4("1.2.192.255")); test_lower_bound(IPv4("1.2.192.1"), IPv4("1.2.192.0")); test_upper_bound(IPv4("1.2.128.1"), IPv4("1.2.128.255")); test_lower_bound(IPv4("1.2.128.1"), IPv4("1.2.128.0")); test_upper_bound(IPv4("1.2.193.1"), IPv4("1.2.255.255")); test_lower_bound(IPv4("1.2.193.1"), IPv4("1.2.193.0")); trie.erase(n4); test_upper_bound(IPv4("1.2.128.1"), IPv4("1.2.191.255")); test_lower_bound(IPv4("1.2.128.1"), IPv4("1.2.0.0")); test_lower_bound(IPv4("1.2.190.1"), IPv4("1.2.0.0")); trie.erase(n6); test_upper_bound(IPv4("1.2.128.1"), IPv4("1.2.255.255")); test_lower_bound(IPv4("1.2.128.1"), IPv4("1.2.0.0")); trie.erase(n2); test_upper_bound(IPv4("1.2.128.1"), IPv4("255.255.255.255")); test_lower_bound(IPv4("1.2.128.1"), IPv4("0.0.0.0")); IPv6RouteEntry d7; IPv6 ip6a("fe80::2c0:4fff:fe68:8c58"); IPv6Net ip6net(ip6a, 96); trie6.insert(ip6net, &d7); test6(ip6net, &d7); test_find6(ip6a, &d7); test_upper_bound6(ip6a, IPv6("fe80::2c0:4fff:ffff:ffff")); //attempts to test remaining code paths trie.print(); trie.insert(n2, &d2); trie.insert(n3, &d3); trie.insert(n1, &d1); IPv4RouteEntry d8; IPv4Net n8(IPv4("1.2.3.0"), 23); trie.insert(n8, &d8); trie.print(); trie.erase(n8); IPv4RouteEntry d9; IPv4Net n9(IPv4("1.2.2.0"), 24); trie.insert(n9, &d9); trie.print(); trie.erase(n9); trie.erase(n2); IPv4RouteEntry d10; IPv4Net n10(IPv4("1.2.0.0"), 15); trie.insert(n10, &d10); /* 1.2.0.0/15 */ trie.print(); trie.erase(n10); trie.insert(n2, &d2); /* 1.2.0.0/16 */ trie.print(); IPv4RouteEntry d11; IPv4Net n11(IPv4("1.0.0.0"), 14); trie.insert(n11, &d11); trie.print(); trie.insert(n10, &d10); /* 1.2.0.0/15 */ trie.print(); trie.erase(n11); IPv4RouteEntry d12; IPv4Net n12(IPv4("1.3.0.0"), 17); trie.insert(n12, &d12); trie.print(); trie.erase(n2); test_find(IPv4("1.2.2.1"), &d10); test_less_specific(n1, &d10); /* 1.2.1.0/24 */ test_less_specific(n3, &d10); /* 1.2.3.0/24 */ test_less_specific(n12, &d10); /* 1.3.0.0/17 */ test_less_specific(n10, NULL); /* 1.2.0.0/15 */ test_less_specific(n4, &d10); /* 1.2.128.0/24 */ trie.print(); test_lower_bound(IPv4("1.2.128.1"), IPv4("1.2.4.0")); test_upper_bound(IPv4("1.2.0.1"), IPv4("1.2.0.255")); trie.print(); trie.erase(n10); /* 1.0.0.0/14 */ trie.insert(n11, &d11); /* 1.0.0.0/14 */ trie.print(); test_upper_bound(IPv4("1.0.0.1"), IPv4("1.2.0.255")); trie.erase(n11); test_lower_bound(IPv4("1.0.0.1"), IPv4("0.0.0.0")); test_upper_bound(IPv4("1.3.128.1"), IPv4("255.255.255.255")); test_upper_bound(IPv4("1.2.2.1"), IPv4("1.2.2.255")); trie.print(); RefTrie::iterator iter; IPv4Net subroot(IPv4("1.2.0.0"), 21); iter = trie.search_subtree(subroot); while (iter!=trie.end()) { printf("------\n"); iter++; } trie.erase(n3); trie.erase(n1); trie.insert(n11, &d11); trie.print(); iter = trie.search_subtree(subroot); while (iter!=trie.end()) { printf("------\n"); iter++; } trie.insert(n1, &d1); trie.erase(n1); iter = trie.search_subtree(subroot); while (iter!=trie.end()) { printf("------\n"); iter++; } trie.erase(n12); trie.insert(n1, &d1); trie.insert(n3, &d3); trie.erase(n1); iter = trie.search_subtree(subroot); while (iter!=trie.end()) { printf("------\n"); iter++; } trie.insert(n10, &d10); trie.insert(n1, &d1); trie.insert(n9, &d9); trie.print(); iter = trie.search_subtree(n10); while (iter!=trie.end()) { printf("------\n"); iter++; } trie.print(); iter = trie.search_subtree(n3); while (iter!=trie.end()) { printf("------\n"); iter++; } //------------------------------------------------------- printf("Test replacement of interior node\n"); IPv4Net n13(IPv4("1.2.2.0"), 23); IPv4RouteEntry d13; trie.insert(n13, &d13); trie.print(); //------------------------------------------------------- printf("==================\nTest of begin()\n"); trie.erase(n11); trie.erase(n10); trie.print(); //------------------------------------------------------- printf("==================\nTest of lower_bound()\n"); trie.print(); iter = trie.lower_bound(n1); /*1.2.1.0/24*/ assert(iter.key() == n1); iter = trie.lower_bound(n9); /*1.2.2.0/24*/ assert(iter.key() == n9); iter = trie.lower_bound(n3); /*1.2.3.0/24*/ assert(iter.key() == n3); iter = trie.lower_bound(n13); /*1.2.2.0/23*/ assert(iter.key() == n13); iter = trie.lower_bound(IPNet("1.2.1.128/25")); assert(iter.key() == n1); /*1.2.1.0/24*/ iter = trie.lower_bound(IPNet("1.2.4.128/25")); assert(iter == trie.end()); iter = trie.lower_bound(IPNet("1.2.1.0/23")); assert(iter.key() == n9); /*1.2.2.0/24*/ iter = trie.lower_bound(IPNet("1.0.0.0/24")); if (iter != trie.end()) printf("iter.key = %s\n", iter.key().str().c_str()); else printf("iter = end\n"); assert(iter.key() == n1); /*1.2.1.0/24*/ //------------------------------------------------------- printf("Test /32 prefix works\n"); IPv4Net n14(IPv4("9.9.9.9"), 32); IPv4RouteEntry d14; IPv4Net n15(IPv4("9.9.9.9"), 24); IPv4RouteEntry d15; trie.insert(n14, &d14); trie.insert(n15, &d15); test_find(IPv4("9.9.9.9"), &d14); test_find(IPv4("9.9.9.8"), &d15); trie.erase(n14); trie.erase(n15); trie.erase(n1); trie.erase(n3); trie.erase(n9); trie.erase(n13); printf("==================\nTest of preorder iterator\n"); // this is the list of subnets in prefix order. The test consists in // inserting a list of unsorted subnets and check that the preorder // iterator retrieves them in the proper order. const char * subnets[] = { "1.2.0.0/16", "1.2.0.0/20", "1.2.1.0/24", "1.2.3.0/24", "1.2.128.0/24" }; IPv4RouteEntry d16; IPv4Net n16(subnets[3]); printf("adding n16: %s route: %p\n", n16.str().c_str(), &d16); trie.insert(n16, &d16); IPv4RouteEntry d17; IPv4Net n17(subnets[2]); printf("adding n17: %s route: %p\n", n17.str().c_str(), &d17); trie.insert(n17, &d17); IPv4RouteEntry d18; IPv4Net n18(subnets[0]); printf("adding n18: %s route: %p\n", n18.str().c_str(), &d18); trie.insert(n18, &d18); IPv4RouteEntry d19; IPv4Net n19(subnets[4]); printf("adding n19: %s route: %p\n", n19.str().c_str(), &d19); trie.insert(n19, &d19); IPv4RouteEntry d20; IPv4Net n20(subnets[1]); printf("adding n20: %s route: %p\n", n20.str().c_str(), &d20); trie.insert(n20, &d20); //------------------------------------------------------- printf("-----------\n"); printf("Test of prefix increment (++ti)\n"); printf("-----------\n"); RefTrie::PreOrderIterator ti; int subnetidx = 0; for (ti = trie.begin() ; ti != trie.end() ; ++ti) { printf("*** node: %-26s %s\n", ti.cur()->k().str().c_str(), ti.cur()->has_payload() ? "PL" : "[]"); if (strcmp(subnets[subnetidx++],ti.cur()->k().str().c_str())) { print_failed("invalid traversal order detected"); exit(1); } if (!ti.cur()->has_payload()) { print_failed("iterator should not return empty nodes!"); exit(1); } } if (subnetidx != 5) { print_failed("iterator missing nodes!"); exit(1); } print_passed(""); //------------------------------------------------------- printf("-----------\n"); printf("Test of postfix increment (ti++)\n"); printf("-----------\n"); subnetidx = 0; for (ti = trie.begin() ; ti != trie.end() ; ti++) { printf("*** node: %-26s %s\n", ti.cur()->k().str().c_str(), ti.cur()->has_payload() ? "PL" : "[]"); if (strcmp(subnets[subnetidx++],ti.cur()->k().str().c_str())) { print_failed("invalid traversal order detected"); exit(1); } if (!ti.cur()->has_payload()) { print_failed("iterator should not return empty nodes!"); exit(1); } } if (subnetidx != 5) { print_failed("iterator missing nodes!"); exit(1); } print_passed(""); test_equals_postorder(); test_equals_preorder(); #if 0 //------------------------------------------------------- printf("-----------\n"); printf("Test of iterator, with insertion\n"); printf("-----------\n"); IPv4RouteEntry d21; IPv4Net n21("1.2.2.0", 24); int cnt; int insert_pos = 0; int entries = trie.route_count(); RefTrie::iterator iteri; for(cnt = 0, iteri = trie.begin(); iteri != trie.end(); iteri++, cnt++) { if (n21 > iteri.cur()->k()) { printf("*** node: %-26s %s\n", iteri.cur()->k().str().c_str(), iteri.cur()->has_payload() ? "PL" : "[]"); insert_pos = cnt; break; } } for(cnt = 0, iteri = trie.begin(); iteri != trie.end(); iteri++, cnt++) { if (cnt == insert_pos) { printf("adding n21: %s route: %p\n", n21.str().c_str(), &d21); trie.insert(n21, &d21); } printf("*** node: %-26s %s\n", iteri.cur()->k().str().c_str(), iteri.cur()->has_payload() ? "PL" : "[]"); } if(entries != cnt) { fprintf(stderr, "Failed to print correct number of nodes inserted\n"); exit(1); } printf("Test Passed: " __FILE__ " " __METHOD__ " " "\n"); IPv4Net prev = trie.begin().cur()->k(); for(iteri = trie.begin(); iteri != trie.end(); iteri++, cnt++) { printf("*** node: %-26s %s\n", iteri.cur()->k().str().c_str(), iteri.cur()->has_payload() ? "PL" : "[]"); if (iteri.cur()->k() < prev) { fprintf(stderr, "Test Failed: " __FILE__ " " __METHOD__ " " "Ordering problem\n"); exit(1); } prev = iteri.cur()->k(); } printf("Test Passed: " __FILE__ " " __METHOD__ " " "\n"); #endif } xorp/libxorp/tests/test_ipvx.cc0000664000076400007640000017154511540224230017043 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "libxorp_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/exceptions.hh" #ifdef HAVE_GETOPT_H #include #endif #include "ipvx.hh" // // XXX: MODIFY FOR YOUR TEST PROGRAM // static const char *program_name = "test_ipvx"; static const char *program_description = "Test IPvX address class"; static const char *program_version_id = "0.1"; static const char *program_date = "December 2, 2002"; static const char *program_copyright = "See file LICENSE"; static const char *program_return_value = "0 on success, 1 if test error, 2 if internal error"; static bool s_verbose = false; bool verbose() { return s_verbose; } void set_verbose(bool v) { s_verbose = v; } static int s_failures = 0; bool failures() { return s_failures; } void incr_failures() { s_failures++; } #include "libxorp/xorp_tests.hh" /** * Print program info to output stream. * * @param stream the output stream the print the program info to. */ static void print_program_info(FILE *stream) { fprintf(stream, "Name: %s\n", program_name); fprintf(stream, "Description: %s\n", program_description); fprintf(stream, "Version: %s\n", program_version_id); fprintf(stream, "Date: %s\n", program_date); fprintf(stream, "Copyright: %s\n", program_copyright); fprintf(stream, "Return: %s\n", program_return_value); } /** * Print program usage information to the stderr. * * @param progname the name of the program. */ static void usage(const char* progname) { print_program_info(stderr); fprintf(stderr, "usage: %s [-v] [-h]\n", progname); fprintf(stderr, " -h : usage (this message)\n"); fprintf(stderr, " -v : verbose output\n"); fprintf(stderr, "Return 0 on success, 1 if test error, 2 if internal error.\n"); } /** * Test IPvX valid constructors. */ void test_ipvx_valid_constructors() { // Test values for IPv4 address: "12.34.56.78" const char *addr_string4 = "12.34.56.78"; uint32_t ui = htonl((12 << 24) | (34 << 16) | (56 << 8) | 78); struct in_addr in_addr; in_addr.s_addr = ui; struct sockaddr_in sin; memset(&sin, 0, sizeof(sin)); #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN sin.sin_len = sizeof(sin); #endif sin.sin_family = AF_INET; sin.sin_addr.s_addr = ui; // Test values for IPv6 address: "1234:5678:9abc:def0:fed:cba9:8765:4321" const char *addr_string6 = "1234:5678:9abc:def0:fed:cba9:8765:4321"; struct in6_addr in6_addr = { { { 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x0f, 0xed, 0xcb, 0xa9, 0x87, 0x65, 0x43, 0x21 } } }; uint8_t ui8[16]; uint32_t ui32[4]; memcpy(&ui8[0], &in6_addr, sizeof(in6_addr)); memcpy(&ui32[0], &in6_addr, sizeof(in6_addr)); struct sockaddr_in6 sin6; memset(&sin6, 0, sizeof(sin6)); #ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN sin6.sin6_len = sizeof(sin6); #endif sin6.sin6_family = AF_INET6; sin6.sin6_addr = in6_addr; struct sockaddr *sap; struct sockaddr_storage *ssp; // // Default constructor. // IPvX ip1; verbose_assert(ip1.af() == AF_INET, "Default constructor"); verbose_match(ip1.str(), "0.0.0.0"); // // Constructor for a specified address family: IPv4. // IPvX ip2(AF_INET); verbose_assert(ip2.af() == AF_INET, "Constructor for AF_INET address family"); verbose_match(ip2.str(), "0.0.0.0"); // // Constructor for a specified address family: IPv6. // IPvX ip3(AF_INET6); verbose_assert(ip3.af() == AF_INET6, "Constructor for AF_INET6 address family"); verbose_match(ip3.str(), "::"); // // Constructor from a string: IPv4. // IPvX ip4(addr_string4); verbose_match(ip4.str(), addr_string4); // // Constructor from a string: IPv6. // IPvX ip5(addr_string6); verbose_match(ip5.str(), addr_string6); // // Constructor from another IPvX address: IPv4. // IPvX ip6(ip4); verbose_match(ip6.str(), addr_string4); // // Constructor from another IPvX address: IPv6. // IPvX ip7(ip5); verbose_match(ip7.str(), addr_string6); // // Constructor from a (uint8_t *) memory pointer: IPv4. // IPvX ip8(AF_INET, (uint8_t *)&ui); verbose_match(ip8.str(), addr_string4); // // Constructor from a (uint8_t *) memory pointer: IPv6. // IPvX ip9(AF_INET6, &ui8[0]); verbose_match(ip9.str(), addr_string6); // // Constructor from an IPv4 address. // IPv4 ip10_ipv4(addr_string4); IPvX ip10(ip10_ipv4); verbose_match(ip10.str(), addr_string4); // // Constructor from an IPv6 address. // IPv6 ip11_ipv6(addr_string6); IPvX ip11(ip11_ipv6); verbose_match(ip11.str(), addr_string6); // // Constructor from in_addr structure: IPv4. // IPvX ip12(in_addr); verbose_match(ip12.str(), addr_string4); // // Constructor from in_addr structure: IPv6. // IPvX ip13(in6_addr); verbose_match(ip13.str(), addr_string6); // // Constructor from sockaddr structure: IPv4 // sap = (struct sockaddr *)&sin; IPvX ip14(*sap); verbose_match(ip14.str(), addr_string4); // // Constructor from sockaddr_storage structure: IPv4 // ssp = (struct sockaddr_storage *)&sin; IPvX ip14_2(*sap); verbose_match(ip14_2.str(), addr_string4); // // Constructor from sockaddr structure: IPv6 // sap = (struct sockaddr *)&sin6; IPvX ip15(*sap); verbose_match(ip15.str(), addr_string6); // // Constructor from sockaddr_storage structure: IPv6 // ssp = (struct sockaddr_storage *)&sin6; IPvX ip15_2(*ssp); verbose_match(ip15_2.str(), addr_string6); // // Constructor from sockaddr_in structure: IPv4 // IPvX ip16(sin); verbose_match(ip16.str(), addr_string4); // // Constructor from sockaddr_in6 structure: IPv6 // IPvX ip19(sin6); verbose_match(ip19.str(), addr_string6); } /** * Test IPvX invalid constructors. */ void test_ipvx_invalid_constructors() { // Test values for IPv4 address: "12.34.56.78" struct sockaddr_in sin; memset(&sin, 0, sizeof(sin)); #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN sin.sin_len = sizeof(sin); #endif sin.sin_family = AF_UNSPEC; // Note: invalid IP address family sin.sin_addr.s_addr = htonl((12 << 24) | (34 << 16) | (56 << 8) | 78); // Test values for IPv6 address: "1234:5678:9abc:def0:fed:cba9:8765:4321" struct in6_addr in6_addr = { { { 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x0f, 0xed, 0xcb, 0xa9, 0x87, 0x65, 0x43, 0x21 } } }; uint8_t ui8[16]; uint32_t ui32[4]; memcpy(&ui8[0], &in6_addr, sizeof(in6_addr)); memcpy(&ui32[0], &in6_addr, sizeof(in6_addr)); struct sockaddr_in6 sin6; memset(&sin6, 0, sizeof(sin6)); #ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN sin6.sin6_len = sizeof(sin6); #endif sin6.sin6_family = AF_UNSPEC; // Note: invalid IP address family sin6.sin6_addr = in6_addr; struct sockaddr *sap; struct sockaddr_storage *ssp; // // Constructor for invalid address family. // try { IPvX ip(AF_UNSPEC); verbose_log("Cannot catch invalid IP address family AF_UNSPEC : FAIL\n"); incr_failures(); UNUSED(ip); } catch (const InvalidFamily& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } // // Constructor from a (uint8_t *) memory pointer for invalid address family. // try { IPvX ip(AF_UNSPEC, &ui8[0]); verbose_log("Cannot catch invalid IP address family AF_UNSPEC : FAIL\n"); incr_failures(); UNUSED(ip); } catch (const InvalidFamily& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } // // Constructor from an invalid address string: IPv4. // try { // Invalid address string: note the typo -- lack of a "dot" after "12" IPvX ip("1234.56.78"); verbose_log("Cannot catch invalid IP address \"1234.56.78\" : FAIL\n"); incr_failures(); UNUSED(ip); } catch (const InvalidString& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } // // Constructor from an invalid address string: IPv6. // try { // Invalid address string: note the typo -- ';' instead of ':' // after 8765 IPvX ip("1234:5678:9abc:def0:fed:cba9:8765;4321"); verbose_log("Cannot catch invalid IP address \"1234:5678:9abc:def0:fed:cba9:8765;4321\" : FAIL\n"); incr_failures(); UNUSED(ip); } catch (const InvalidString& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } // // Constructor from an invalid sockaddr structure. // try { sap = (struct sockaddr *)&sin; IPvX ip(*sap); verbose_log("Cannot catch invalid IP address family AF_UNSPEC : FAIL\n"); incr_failures(); UNUSED(ip); } catch (const InvalidFamily& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } // // Constructor from an invalid sockaddr_storage structure. // try { ssp = (struct sockaddr_storage *)&sin; IPvX ip(*ssp); verbose_log("Cannot catch invalid IP address family AF_UNSPEC : FAIL\n"); incr_failures(); UNUSED(ip); } catch (const InvalidFamily& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } // // Constructor from an invalid sockaddr_in structure. // try { IPvX ip(sin); verbose_log("Cannot catch invalid IP address family AF_UNSPEC : FAIL\n"); incr_failures(); UNUSED(ip); } catch (const InvalidFamily& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } // // Constructor from an invalid sockaddr_in6 structure. // try { IPvX ip(sin6); verbose_log("Cannot catch invalid IP address family AF_UNSPEC : FAIL\n"); incr_failures(); UNUSED(ip); } catch (const InvalidFamily& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } // // Constructor from sockaddr_in structure: IPv6 // try { struct in6_addr in6_addr = { { { 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x0f, 0xed, 0xcb, 0xa9, 0x87, 0x65, 0x43, 0x21 } } }; struct sockaddr_in6 sin6; memset(&sin6, 0, sizeof(sin6)); #ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN sin6.sin6_len = sizeof(sin6); #endif sin6.sin6_family = AF_INET6; sin6.sin6_addr = in6_addr; IPvX ip( *((struct sockaddr_in *) &sin6)); verbose_log("Cannot catch sockaddr_in6 passed as sockaddr_in : FAIL"); incr_failures(); UNUSED(ip); } catch (const InvalidFamily& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } // // Constructor from sockaddr_in6 structure: IPv4 // try { uint32_t ui = htonl((12 << 24) | (34 << 16) | (56 << 8) | 78); struct sockaddr_in sin; memset(&sin, 0, sizeof(sin)); #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN sin.sin_len = sizeof(sin); #endif sin.sin_family = AF_INET; sin.sin_addr.s_addr = ui; IPvX ip( *((struct sockaddr_in6 *) &sin)); verbose_log("Cannot catch sockaddr_in passed as sockaddr_in6 : FAIL"); incr_failures(); UNUSED(ip); } catch (const InvalidFamily& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } } /** * Test IPvX valid copy in/out methods. */ void test_ipvx_valid_copy_in_out() { // Test values for IPv4 address: "12.34.56.78" const char *addr_string4 = "12.34.56.78"; uint32_t ui = htonl((12 << 24) | (34 << 16) | (56 << 8) | 78); struct in_addr in_addr; in_addr.s_addr = ui; struct sockaddr_in sin; memset(&sin, 0, sizeof(sin)); #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN sin.sin_len = sizeof(sin); #endif sin.sin_family = AF_INET; sin.sin_addr.s_addr = ui; // Test values for IPv6 address: "1234:5678:9abc:def0:fed:cba9:8765:4321" const char *addr_string6 = "1234:5678:9abc:def0:fed:cba9:8765:4321"; struct in6_addr in6_addr = { { { 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x0f, 0xed, 0xcb, 0xa9, 0x87, 0x65, 0x43, 0x21 } } }; uint8_t ui8[16]; uint32_t ui32[4]; memcpy(&ui8[0], &in6_addr, sizeof(in6_addr)); memcpy(&ui32[0], &in6_addr, sizeof(in6_addr)); struct sockaddr_in6 sin6; memset(&sin6, 0, sizeof(sin6)); #ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN sin6.sin6_len = sizeof(sin6); #endif sin6.sin6_family = AF_INET6; sin6.sin6_addr = in6_addr; struct sockaddr *sap; struct sockaddr_storage *ssp; // // Copy the IPvX raw address to specified memory location: IPv4. // IPvX ip1(addr_string4); uint8_t ip1_uint8[4]; verbose_assert(ip1.copy_out(&ip1_uint8[0]) == 4, "copy_out(uint8_t *) for IPv4 address"); verbose_assert(memcmp(&ui, &ip1_uint8[0], 4) == 0, "compare copy_out(uint8_t *) for IPv4 address"); // // Copy the IPvX raw address to specified memory location: IPv6. // IPvX ip2(addr_string6); uint8_t ip2_uint8[16]; verbose_assert(ip2.copy_out(&ip2_uint8[0]) == 16, "copy_out(uint8_t *) for IPv6 address"); verbose_assert(memcmp(&ui8[0], &ip2_uint8[0], 16) == 0, "compare copy_out(uint8_t *) for IPv6 address"); // // Copy the IPvX raw address to an in_addr structure: IPv4. // IPvX ip3(addr_string4); struct in_addr ip3_in_addr; verbose_assert(ip3.copy_out(ip3_in_addr) == 4, "copy_out(in_addr&) for IPv4 address"); verbose_assert(memcmp(&in_addr, &ip3_in_addr, 4) == 0, "compare copy_out(in_addr&) for IPv4 address"); // // Copy the IPvX raw address to an in6_addr structure: IPv6. // IPvX ip4(addr_string6); struct in6_addr ip4_in6_addr; verbose_assert(ip4.copy_out(ip4_in6_addr) == 16, "copy_out(in6_addr&) for IPv6 address"); verbose_assert(memcmp(&in6_addr, &ip4_in6_addr, 16) == 0, "compare copy_out(in6_addr&) for IPv6 address"); // // Copy the IPvX raw address to a sockaddr structure: IPv4. // IPvX ip5(addr_string4); struct sockaddr_in ip5_sockaddr_in; sap = (struct sockaddr *)&ip5_sockaddr_in; verbose_assert(ip5.copy_out(*sap) == 4, "copy_out(sockaddr&) for IPv4 address"); verbose_assert(memcmp(&sin, &ip5_sockaddr_in, sizeof(sin)) == 0, "compare copy_out(sockaddr&) for IPv4 address"); // // Copy the IPvX raw address to a sockaddr_storage structure: IPv4. // IPvX ip5_2(addr_string4); struct sockaddr_in ip5_2_sockaddr_in; ssp = (struct sockaddr_storage *)&ip5_2_sockaddr_in; verbose_assert(ip5_2.copy_out(*ssp) == 4, "copy_out(sockaddr_storage&) for IPv4 address"); verbose_assert(memcmp(&sin, &ip5_2_sockaddr_in, sizeof(sin)) == 0, "compare copy_out(sockaddr_storage&) for IPv4 address"); // // Copy the IPvX raw address to a sockaddr structure: IPv6. // IPvX ip6(addr_string6); struct sockaddr_in6 ip6_sockaddr_in6; sap = (struct sockaddr *)&ip6_sockaddr_in6; verbose_assert(ip6.copy_out(*sap) == 16, "copy_out(sockaddr&) for IPv6 address"); verbose_assert(memcmp(&sin6, &ip6_sockaddr_in6, sizeof(sin6)) == 0, "compare copy_out(sockaddr&) for IPv6 address"); // // Copy the IPvX raw address to a sockaddr_storage structure: IPv6. // IPvX ip6_2(addr_string6); struct sockaddr_in6 ip6_2_sockaddr_in6; ssp = (struct sockaddr_storage *)&ip6_2_sockaddr_in6; verbose_assert(ip6_2.copy_out(*ssp) == 16, "copy_out(sockaddr_storage&) for IPv6 address"); verbose_assert(memcmp(&sin6, &ip6_2_sockaddr_in6, sizeof(sin6)) == 0, "compare copy_out(sockaddr_storage&) for IPv6 address"); // // Copy the IPvX raw address to a sockaddr_in structure: IPv4. // IPvX ip7(addr_string4); struct sockaddr_in ip7_sockaddr_in; verbose_assert(ip7.copy_out(ip7_sockaddr_in) == 4, "copy_out(sockaddr_in&) for IPv4 address"); verbose_assert(memcmp(&sin, &ip7_sockaddr_in, sizeof(sin)) == 0, "compare copy_out(sockaddr_in&) for IPv4 address"); // // Copy the IPvX raw address to a sockaddr_in6 structure: IPv6. // IPvX ip10(addr_string6); struct sockaddr_in6 ip10_sockaddr_in6; verbose_assert(ip10.copy_out(ip10_sockaddr_in6) == 16, "copy_out(sockaddr_in6&) for IPv6 address"); verbose_assert(memcmp(&sin6, &ip10_sockaddr_in6, sizeof(sin6)) == 0, "compare copy_out(sockaddr_in6&) for IPv6 address"); // // Copy a raw address of specified family type into IPvX structure: IPv4. // IPvX ip11(AF_INET); verbose_assert(ip11.copy_in(AF_INET, (uint8_t *)&ui) == 4, "copy_in(uint8_t *) for IPv4 address"); verbose_match(ip11.str(), addr_string4); // // Copy a raw address of specified family type into IPvX structure: IPv6. // IPvX ip12(AF_INET6); verbose_assert(ip12.copy_in(AF_INET6, &ui8[0]) == 16, "copy_in(uint8_t *) for IPv6 address"); verbose_match(ip12.str(), addr_string6); // // Copy a raw IPv4 address from a in_addr structure into IPvX structure. // IPvX ip13(AF_INET); verbose_assert(ip13.copy_in(in_addr) == 4, "copy_in(in_addr&) for IPv4 address"); verbose_match(ip13.str(), addr_string4); // // Copy a raw IPv6 address from a in6_addr structure into IPvX structure. // IPvX ip14(AF_INET6); verbose_assert(ip14.copy_in(in6_addr) == 16, "copy_in(in6_addr&) for IPv6 address"); verbose_match(ip14.str(), addr_string6); // // Copy a raw address from a sockaddr structure into IPvX structure: IPv4. // IPvX ip15(AF_INET); sap = (struct sockaddr *)&sin; verbose_assert(ip15.copy_in(*sap) == 4, "copy_in(sockaddr&) for IPv4 address"); verbose_match(ip15.str(), addr_string4); // // Copy a raw address from a sockaddr_storage structure into IPvX // structure: IPv4. // IPvX ip15_2(AF_INET); ssp = (struct sockaddr_storage *)&sin; verbose_assert(ip15_2.copy_in(*ssp) == 4, "copy_in(sockaddr_storage&) for IPv4 address"); verbose_match(ip15_2.str(), addr_string4); // // Copy a raw address from a sockaddr structure into IPvX structure: IPv6. // IPvX ip16(AF_INET6); sap = (struct sockaddr *)&sin6; verbose_assert(ip16.copy_in(*sap) == 16, "copy_in(sockaddr&) for IPv6 address"); verbose_match(ip16.str(), addr_string6); // // Copy a raw address from a sockaddr_storage structure into IPvX // structure: IPv6. // IPvX ip16_2(AF_INET6); ssp = (struct sockaddr_storage *)&sin6; verbose_assert(ip16_2.copy_in(*ssp) == 16, "copy_in(sockaddr_storage&) for IPv6 address"); verbose_match(ip16_2.str(), addr_string6); // // Copy a raw address from a sockaddr_in structure into IPvX structure: IPv4. // IPvX ip17(AF_INET); verbose_assert(ip17.copy_in(sin) == 4, "copy_in(sockaddr_in&) for IPv4 address"); verbose_match(ip17.str(), addr_string4); // // Copy a raw address from a sockaddr_in6 structure into IPvX structure: IPv6. // IPvX ip20(AF_INET6); verbose_assert(ip20.copy_in(sin6) == 16, "copy_in(sockaddr_in6&) for IPv6 address"); verbose_match(ip20.str(), addr_string6); } /** * Test IPvX invalid copy in/out methods. */ void test_ipvx_invalid_copy_in_out() { // Test values for IPv4 address: "12.34.56.78" const char *addr_string4 = "12.34.56.78"; struct sockaddr_in sin; memset(&sin, 0, sizeof(sin)); #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN sin.sin_len = sizeof(sin); #endif sin.sin_family = AF_UNSPEC; // Note: invalid IP address family sin.sin_addr.s_addr = htonl((12 << 24) | (34 << 16) | (56 << 8) | 78); // Test values for IPv6 address: "1234:5678:9abc:def0:fed:cba9:8765:4321" const char *addr_string6 = "1234:5678:9abc:def0:fed:cba9:8765:4321"; struct in6_addr in6_addr = { { { 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x0f, 0xed, 0xcb, 0xa9, 0x87, 0x65, 0x43, 0x21 } } }; uint8_t ui8[16]; uint32_t ui32[4]; memcpy(&ui8[0], &in6_addr, sizeof(in6_addr)); memcpy(&ui32[0], &in6_addr, sizeof(in6_addr)); struct sockaddr_in6 sin6; memset(&sin6, 0, sizeof(sin6)); #ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN sin6.sin6_len = sizeof(sin6); #endif sin6.sin6_family = AF_UNSPEC; // Note: invalid IP address family sin6.sin6_addr = in6_addr; struct sockaddr *sap; struct sockaddr_storage *ssp; // // Mismatch copy-out: copy-out IPv6 address to in_addr structure. // try { IPvX ip(addr_string6); struct in_addr in_addr; ip.copy_out(in_addr); verbose_log("Cannot catch mismatch copy-out : FAIL\n"); incr_failures(); } catch (const InvalidFamily& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } // // Mismatch copy-out: copy-out IPv4 address to in_addr6 structure. // try { IPvX ip(addr_string4); struct in6_addr in6_addr; ip.copy_out(in6_addr); verbose_log("Cannot catch mismatch copy-out : FAIL\n"); incr_failures(); } catch (const InvalidFamily& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } // // Mismatch copy-out: copy-out IPv6 address to sockaddr_in structure. // try { IPvX ip(addr_string6); struct sockaddr_in sockaddr_in; ip.copy_out(sockaddr_in); verbose_log("Cannot catch mismatch copy-out : FAIL\n"); incr_failures(); } catch (const InvalidFamily& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } // // Mismatch copy-out: copy-out IPv4 address to sockaddr_in6 structure. // try { IPvX ip(addr_string4); struct sockaddr_in6 sockaddr_in6; ip.copy_out(sockaddr_in6); verbose_log("Cannot catch mismatch copy-out : FAIL\n"); incr_failures(); } catch (const InvalidFamily& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } // XXX: we should test for copy_out() to sockaddr, sockaddr_storage, // sockaddr_in, sockaddr_in6 structures that throw InvalidFamily. // To do so we must creast first IPvX with invalid address family. // However, this doesn't seem possible, hence we skip those checks. // // Copy-in from a (uint8_t *) memory pointer for invalid address family. // try { IPvX ip(AF_INET); ip.copy_in(AF_UNSPEC, &ui8[0]); verbose_log("Cannot catch invalid IP address family AF_UNSPEC : FAIL\n"); incr_failures(); } catch (const InvalidFamily& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } // // Copy-in from a sockaddr structure for invalid address family. // try { IPvX ip(AF_INET); sap = (struct sockaddr *)&sin; ip.copy_in(*sap); verbose_log("Cannot catch invalid IP address family AF_UNSPEC : FAIL\n"); incr_failures(); } catch (const InvalidFamily& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } // // Copy-in from a sockaddr_storage structure for invalid address family. // try { IPvX ip(AF_INET); ssp = (struct sockaddr_storage *)&sin; ip.copy_in(*ssp); verbose_log("Cannot catch invalid IP address family AF_UNSPEC : FAIL\n"); incr_failures(); } catch (const InvalidFamily& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } // // Copy-in from a sockaddr_in structure for invalid address family. // try { IPvX ip(AF_INET); ip.copy_in(sin); verbose_log("Cannot catch invalid IP address family AF_UNSPEC : FAIL\n"); incr_failures(); } catch (const InvalidFamily& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } // // Copy-in from a sockaddr_in6 structure for invalid address family. // try { IPvX ip(AF_INET6); ip.copy_in(sin6); verbose_log("Cannot catch invalid IP address family AF_UNSPEC : FAIL\n"); incr_failures(); } catch (const InvalidFamily& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } // // Copy-in from a sockaddr_in structure for IPv6 // try { struct in6_addr in6_addr = { { { 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x0f, 0xed, 0xcb, 0xa9, 0x87, 0x65, 0x43, 0x21 } } }; struct sockaddr_in6 sin6; memset(&sin6, 0, sizeof(sin6)); #ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN sin6.sin6_len = sizeof(sin6); #endif sin6.sin6_family = AF_INET6; sin6.sin6_addr = in6_addr; IPvX ip(AF_INET6); ip.copy_in( *((struct sockaddr_in *) &sin6)); verbose_log("Cannot catch invalid IP address family AF_INET6 : FAIL\n"); incr_failures(); } catch (const InvalidFamily& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } try { uint32_t ui = htonl((12 << 24) | (34 << 16) | (56 << 8) | 78); struct sockaddr_in sin; memset(&sin, 0, sizeof(sin)); #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN sin.sin_len = sizeof(sin); #endif sin.sin_family = AF_INET; sin.sin_addr.s_addr = ui; IPvX ip(AF_INET); ip.copy_in( *((struct sockaddr_in6*) &sin) ); verbose_log("Cannot catch invalid IP address family AF_INET : FAIL\n"); incr_failures(); } catch (const InvalidFamily& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } } /** * Test IPvX operators. */ void test_ipvx_operators() { IPv4 ip4_a("0.255.0.255"); IPv4 ip4_b("255.0.255.255"); IPv4 ip4_not_a("255.0.255.0"); IPv4 ip4_a_or_b("255.255.255.255"); IPv4 ip4_a_and_b("0.0.0.255"); IPv4 ip4_a_xor_b("255.255.255.0"); IPvX ip6_a("0000:ffff:0000:ffff:0000:ffff:0000:ffff"); IPvX ip6_b("ffff:0000:ffff:0000:ffff:0000:ffff:ffff"); IPvX ip6_not_a("ffff:0000:ffff:0000:ffff:0000:ffff:0000"); IPvX ip6_a_or_b("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"); IPvX ip6_a_and_b("::ffff"); IPvX ip6_a_xor_b("ffff:ffff:ffff:ffff:ffff:ffff:ffff:0000"); IPvX ip4_local_a("127.0.0.1"); IPvX ip4_local_b("127.0.0.1"); // // Equality Operator // verbose_assert(ip4_a == ip4_a, "operator=="); verbose_assert(!(ip4_a == ip4_b), "operator=="); verbose_assert(ip6_a == ip6_a, "operator=="); verbose_assert(!(ip6_a == ip6_b), "operator=="); // // Not-Equal Operator // verbose_assert(!(ip4_a != ip4_a), "operator!="); verbose_assert(ip4_a != ip4_b, "operator!="); verbose_assert(!(ip6_a != ip6_a), "operator!="); verbose_assert(ip6_a != ip6_b, "operator!="); // // Less-Than Operator // verbose_assert(ip4_a < ip4_b, "operator<"); verbose_assert(ip6_a < ip6_b, "operator<"); // // Greater-Than or Equal // verbose_assert(ip4_local_a >= ip4_local_b, "operator>="); verbose_assert(ip4_local_b >= ip4_local_a, "operator>="); // // Bitwise-Negation Operator // verbose_assert(~ip4_a == ip4_not_a, "operator~"); verbose_assert(~ip6_a == ip6_not_a, "operator~"); // // OR Operator // verbose_assert((ip4_a | ip4_b) == ip4_a_or_b, "operator|"); verbose_assert((ip6_a | ip6_b) == ip6_a_or_b, "operator|"); // // AND Operator // verbose_assert((ip4_a & ip4_b) == ip4_a_and_b, "operator&"); verbose_assert((ip6_a & ip6_b) == ip6_a_and_b, "operator&"); // // XOR Operator // verbose_assert((ip4_a ^ ip4_b) == ip4_a_xor_b, "operator^"); verbose_assert((ip6_a ^ ip6_b) == ip6_a_xor_b, "operator^"); // // Operator << // verbose_assert(IPvX("0.255.0.255") << 16 == IPvX("0.255.0.0"), "operator<<"); verbose_assert(IPvX("0.255.0.0") << 1 == IPvX("1.254.0.0"), "operator<<"); verbose_assert(IPvX("0000:ffff:0000:ffff:0000:ffff:0000:ffff") << 16 == IPvX("ffff:0000:ffff:0000:ffff:0000:ffff:0000"), "operator<<"); verbose_assert(IPvX("0000:ffff:0000:ffff:0000:ffff:0000:ffff") << 1 == IPvX("0001:fffe:0001:fffe:0001:fffe:0001:fffe"), "operator<<"); // // Operator >> // verbose_assert(IPvX("0.255.0.255") >> 16 == IPvX("0.0.0.255"), "operator>>"); verbose_assert(IPvX("0.0.0.255") >> 1 == IPvX("0.0.0.127"), "operator>>"); verbose_assert(IPvX("0000:ffff:0000:ffff:0000:ffff:0000:ffff") >> 16 == IPvX("0000:0000:ffff:0000:ffff:0000:ffff:0000"), "operator>>"); verbose_assert(IPvX("0000:ffff:0000:ffff:0000:ffff:0000:ffff") >> 1 == IPvX("0000:7fff:8000:7fff:8000:7fff:8000:7fff"), "operator>>"); // // Decrement Operator // verbose_assert(--IPvX("0.255.0.255") == IPvX("0.255.0.254"), "operator--()"); verbose_assert(--IPvX("0.0.0.0") == IPvX("255.255.255.255"), "operator--()"); verbose_assert(--IPvX("0000:ffff:0000:ffff:0000:ffff:0000:ffff") == IPvX("0000:ffff:0000:ffff:0000:ffff:0000:fffe"), "operator--()"); verbose_assert(--IPvX("0000:0000:0000:0000:0000:0000:0000:0000") == IPvX("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"), "operator--()"); // // Increment Operator // verbose_assert(++IPvX("0.255.0.254") == IPvX("0.255.0.255"), "operator++()"); verbose_assert(++IPvX("255.255.255.255") == IPvX("0.0.0.0"), "operator++()"); verbose_assert(++IPvX("0000:ffff:0000:ffff:0000:ffff:0000:ffff") == IPvX("0000:ffff:0000:ffff:0000:ffff:0001:0000"), "operator++()"); verbose_assert(++IPvX("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff") == IPvX("0000:0000:0000:0000:0000:0000:0000:0000"), "operator++()"); } /** * Test IPvX address type. */ void test_ipvx_address_type() { IPvX ip4_zero("0.0.0.0"); // Zero, not unicast IPvX ip4_unicast1("0.0.0.1"); // Unicast IPvX ip4_unicast2("1.2.3.4"); // Unicast IPvX ip4_unicast3("223.255.255.255"); // Unicast IPvX ip4_class_a1("0.0.0.0"); // Class A IPvX ip4_class_a2("12.34.56.78"); // Class A IPvX ip4_class_a3("127.255.255.255"); // Class A IPvX ip4_class_b1("128.0.0.0"); // Class B IPvX ip4_class_b2("128.2.3.4"); // Class B IPvX ip4_class_b3("191.255.255.255"); // Class B IPvX ip4_class_c1("192.0.0.0"); // Class C IPvX ip4_class_c2("192.2.3.4"); // Class C IPvX ip4_class_c3("223.255.255.255"); // Class C IPvX ip4_multicast1("224.0.0.0"); // Multicast IPvX ip4_multicast2("224.2.3.4"); // Multicast IPvX ip4_multicast3("239.255.255.255"); // Multicast IPvX ip4_experimental1("240.0.0.0"); // Experimental IPvX ip4_experimental2("240.2.3.4"); // Experimental IPvX ip4_experimental3("255.255.255.255"); // Experimental // IPvX ip4_multicast_linklocal1("224.0.0.1"); // Link-local multicast IPvX ip4_loopback1("127.0.0.1"); // Loopback IPvX ip4_loopback2("127.255.255.255"); // Loopback // IPvX ip6_zero("::"); // Zero, not unicast IPvX ip6_unicast1("::1"); // Unicast IPvX ip6_unicast2("2000::1"); // Unicast IPvX ip6_unicast3("feff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"); // Unicast IPvX ip6_multicast1("ff00::"); // Multicast IPvX ip6_multicast2("ffff::2:3:4"); // Multicast IPvX ip6_multicast3("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");// Multicast // IPvX ip6_unicast_linklocal1("fe80::2"); // Link-local unicast IPvX ip6_multicast_interfacelocal1("ff01::1"); // Interface-local multicast IPvX ip6_multicast_linklocal1("ff02::2"); // Link-local multicast IPvX ip6_loopback1("::1"); // Loopback // // Test if an address is numerically zero: IPv4 // verbose_assert(ip4_zero.is_zero() == true, "is_zero()"); verbose_assert(ip4_unicast1.is_zero() == false, "is_zero()"); verbose_assert(ip4_unicast2.is_zero() == false, "is_zero()"); verbose_assert(ip4_unicast3.is_zero() == false, "is_zero()"); verbose_assert(ip4_class_a1.is_zero() == true, "is_zero()"); verbose_assert(ip4_class_a2.is_zero() == false, "is_zero()"); verbose_assert(ip4_class_a3.is_zero() == false, "is_zero()"); verbose_assert(ip4_class_b1.is_zero() == false, "is_zero()"); verbose_assert(ip4_class_b2.is_zero() == false, "is_zero()"); verbose_assert(ip4_class_b3.is_zero() == false, "is_zero()"); verbose_assert(ip4_class_c1.is_zero() == false, "is_zero()"); verbose_assert(ip4_class_c2.is_zero() == false, "is_zero()"); verbose_assert(ip4_class_c3.is_zero() == false, "is_zero()"); verbose_assert(ip4_multicast1.is_zero() == false, "is_zero()"); verbose_assert(ip4_multicast2.is_zero() == false, "is_zero()"); verbose_assert(ip4_multicast3.is_zero() == false, "is_zero()"); verbose_assert(ip4_experimental1.is_zero() == false, "is_zero()"); verbose_assert(ip4_experimental2.is_zero() == false, "is_zero()"); verbose_assert(ip4_experimental3.is_zero() == false, "is_zero()"); // // Test if an address is numerically zero: IPv6 // verbose_assert(ip6_zero.is_zero() == true, "is_zero()"); verbose_assert(ip6_unicast1.is_zero() == false, "is_zero()"); verbose_assert(ip6_unicast2.is_zero() == false, "is_zero()"); verbose_assert(ip6_unicast3.is_zero() == false, "is_zero()"); verbose_assert(ip6_multicast1.is_zero() == false, "is_zero()"); verbose_assert(ip6_multicast2.is_zero() == false, "is_zero()"); verbose_assert(ip6_multicast3.is_zero() == false, "is_zero()"); // // Test if an address is a valid unicast address: IPv4 // verbose_assert(ip4_zero.is_unicast() == false, "is_unicast()"); verbose_assert(ip4_unicast1.is_unicast() == true, "is_unicast()"); verbose_assert(ip4_unicast2.is_unicast() == true, "is_unicast()"); verbose_assert(ip4_unicast3.is_unicast() == true, "is_unicast()"); verbose_assert(ip4_class_a1.is_unicast() == false, "is_unicast()"); verbose_assert(ip4_class_a2.is_unicast() == true, "is_unicast()"); verbose_assert(ip4_class_a3.is_unicast() == true, "is_unicast()"); verbose_assert(ip4_class_b1.is_unicast() == true, "is_unicast()"); verbose_assert(ip4_class_b2.is_unicast() == true, "is_unicast()"); verbose_assert(ip4_class_b3.is_unicast() == true, "is_unicast()"); verbose_assert(ip4_class_c1.is_unicast() == true, "is_unicast()"); verbose_assert(ip4_class_c2.is_unicast() == true, "is_unicast()"); verbose_assert(ip4_class_c3.is_unicast() == true, "is_unicast()"); verbose_assert(ip4_multicast1.is_unicast() == false, "is_unicast()"); verbose_assert(ip4_multicast2.is_unicast() == false, "is_unicast()"); verbose_assert(ip4_multicast3.is_unicast() == false, "is_unicast()"); verbose_assert(ip4_experimental1.is_unicast() == false, "is_unicast()"); verbose_assert(ip4_experimental2.is_unicast() == false, "is_unicast()"); verbose_assert(ip4_experimental3.is_unicast() == false, "is_unicast()"); // // Test if an address is a valid unicast address: IPv6 // verbose_assert(ip6_zero.is_unicast() == false, "is_unicast()"); verbose_assert(ip6_unicast1.is_unicast() == true, "is_unicast()"); verbose_assert(ip6_unicast2.is_unicast() == true, "is_unicast()"); verbose_assert(ip6_unicast3.is_unicast() == true, "is_unicast()"); verbose_assert(ip6_multicast1.is_unicast() == false, "is_unicast()"); verbose_assert(ip6_multicast2.is_unicast() == false, "is_unicast()"); verbose_assert(ip6_multicast3.is_unicast() == false, "is_unicast()"); // // Test if an address is a valid multicast address: IPv4 // verbose_assert(ip4_zero.is_multicast() == false, "is_multicast()"); verbose_assert(ip4_unicast1.is_multicast() == false, "is_multicast()"); verbose_assert(ip4_unicast2.is_multicast() == false, "is_multicast()"); verbose_assert(ip4_unicast3.is_multicast() == false, "is_multicast()"); verbose_assert(ip4_class_a1.is_multicast() == false, "is_multicast()"); verbose_assert(ip4_class_a2.is_multicast() == false, "is_multicast()"); verbose_assert(ip4_class_a3.is_multicast() == false, "is_multicast()"); verbose_assert(ip4_class_b1.is_multicast() == false, "is_multicast()"); verbose_assert(ip4_class_b2.is_multicast() == false, "is_multicast()"); verbose_assert(ip4_class_b3.is_multicast() == false, "is_multicast()"); verbose_assert(ip4_class_c1.is_multicast() == false, "is_multicast()"); verbose_assert(ip4_class_c2.is_multicast() == false, "is_multicast()"); verbose_assert(ip4_class_c3.is_multicast() == false, "is_multicast()"); verbose_assert(ip4_multicast1.is_multicast() == true, "is_multicast()"); verbose_assert(ip4_multicast2.is_multicast() == true, "is_multicast()"); verbose_assert(ip4_multicast3.is_multicast() == true, "is_multicast()"); verbose_assert(ip4_experimental1.is_multicast() == false, "is_multicast()"); verbose_assert(ip4_experimental2.is_multicast() == false, "is_multicast()"); verbose_assert(ip4_experimental3.is_multicast() == false, "is_multicast()"); // // Test if an address is a valid multicast address: IPv6 // verbose_assert(ip6_zero.is_multicast() == false, "is_multicast()"); verbose_assert(ip6_unicast1.is_multicast() == false, "is_multicast()"); verbose_assert(ip6_unicast2.is_multicast() == false, "is_multicast()"); verbose_assert(ip6_unicast3.is_multicast() == false, "is_multicast()"); verbose_assert(ip6_multicast1.is_multicast() == true, "is_multicast()"); verbose_assert(ip6_multicast2.is_multicast() == true, "is_multicast()"); verbose_assert(ip6_multicast3.is_multicast() == true, "is_multicast()"); // // Test if an address is a valid Class A address: IPv4 // // XXX: This test applies only for IPv4. verbose_assert(ip4_zero.is_class_a() == true, "is_class_a()"); verbose_assert(ip4_unicast1.is_class_a() == true, "is_class_a()"); verbose_assert(ip4_unicast2.is_class_a() == true, "is_class_a()"); verbose_assert(ip4_unicast3.is_class_a() == false, "is_class_a()"); verbose_assert(ip4_class_a1.is_class_a() == true, "is_class_a()"); verbose_assert(ip4_class_a2.is_class_a() == true, "is_class_a()"); verbose_assert(ip4_class_a3.is_class_a() == true, "is_class_a()"); verbose_assert(ip4_class_b1.is_class_a() == false, "is_class_a()"); verbose_assert(ip4_class_b2.is_class_a() == false, "is_class_a()"); verbose_assert(ip4_class_b3.is_class_a() == false, "is_class_a()"); verbose_assert(ip4_class_c1.is_class_a() == false, "is_class_a()"); verbose_assert(ip4_class_c2.is_class_a() == false, "is_class_a()"); verbose_assert(ip4_class_c3.is_class_a() == false, "is_class_a()"); verbose_assert(ip4_multicast1.is_class_a() == false, "is_class_a()"); verbose_assert(ip4_multicast2.is_class_a() == false, "is_class_a()"); verbose_assert(ip4_multicast3.is_class_a() == false, "is_class_a()"); verbose_assert(ip4_experimental1.is_class_a() == false, "is_class_a()"); verbose_assert(ip4_experimental2.is_class_a() == false, "is_class_a()"); verbose_assert(ip4_experimental3.is_class_a() == false, "is_class_a()"); // // Test if an address is a valid Class B address: IPv4 // // XXX: This test applies only for IPv4. verbose_assert(ip4_zero.is_class_b() == false, "is_class_b()"); verbose_assert(ip4_unicast1.is_class_b() == false, "is_class_b()"); verbose_assert(ip4_unicast2.is_class_b() == false, "is_class_b()"); verbose_assert(ip4_unicast3.is_class_b() == false, "is_class_b()"); verbose_assert(ip4_class_a1.is_class_b() == false, "is_class_b()"); verbose_assert(ip4_class_a2.is_class_b() == false, "is_class_b()"); verbose_assert(ip4_class_a3.is_class_b() == false, "is_class_b()"); verbose_assert(ip4_class_b1.is_class_b() == true, "is_class_b()"); verbose_assert(ip4_class_b2.is_class_b() == true, "is_class_b()"); verbose_assert(ip4_class_b3.is_class_b() == true, "is_class_b()"); verbose_assert(ip4_class_c1.is_class_b() == false, "is_class_b()"); verbose_assert(ip4_class_c2.is_class_b() == false, "is_class_b()"); verbose_assert(ip4_class_c3.is_class_b() == false, "is_class_b()"); verbose_assert(ip4_multicast1.is_class_b() == false, "is_class_b()"); verbose_assert(ip4_multicast2.is_class_b() == false, "is_class_b()"); verbose_assert(ip4_multicast3.is_class_b() == false, "is_class_b()"); verbose_assert(ip4_experimental1.is_class_b() == false, "is_class_b()"); verbose_assert(ip4_experimental2.is_class_b() == false, "is_class_b()"); verbose_assert(ip4_experimental3.is_class_b() == false, "is_class_b()"); // // Test if an address is a valid Class C address: IPv4 // // XXX: This test applies only for IPv4. verbose_assert(ip4_zero.is_class_c() == false, "is_class_c()"); verbose_assert(ip4_unicast1.is_class_c() == false, "is_class_c()"); verbose_assert(ip4_unicast2.is_class_c() == false, "is_class_c()"); verbose_assert(ip4_unicast3.is_class_c() == true, "is_class_c()"); verbose_assert(ip4_class_a1.is_class_c() == false, "is_class_c()"); verbose_assert(ip4_class_a2.is_class_c() == false, "is_class_c()"); verbose_assert(ip4_class_a3.is_class_c() == false, "is_class_c()"); verbose_assert(ip4_class_b1.is_class_c() == false, "is_class_c()"); verbose_assert(ip4_class_b2.is_class_c() == false, "is_class_c()"); verbose_assert(ip4_class_b3.is_class_c() == false, "is_class_c()"); verbose_assert(ip4_class_c1.is_class_c() == true, "is_class_c()"); verbose_assert(ip4_class_c2.is_class_c() == true, "is_class_c()"); verbose_assert(ip4_class_c3.is_class_c() == true, "is_class_c()"); verbose_assert(ip4_multicast1.is_class_c() == false, "is_class_c()"); verbose_assert(ip4_multicast2.is_class_c() == false, "is_class_c()"); verbose_assert(ip4_multicast3.is_class_c() == false, "is_class_c()"); verbose_assert(ip4_experimental1.is_class_c() == false, "is_class_c()"); verbose_assert(ip4_experimental2.is_class_c() == false, "is_class_c()"); verbose_assert(ip4_experimental3.is_class_c() == false, "is_class_c()"); // // Test if an address is a valid experimental address: IPv4 // // XXX: This test applies only for IPv4. verbose_assert(ip4_zero.is_experimental() == false, "is_experimental()"); verbose_assert(ip4_unicast1.is_experimental() == false, "is_experimental()"); verbose_assert(ip4_unicast2.is_experimental() == false, "is_experimental()"); verbose_assert(ip4_unicast3.is_experimental() == false, "is_experimental()"); verbose_assert(ip4_class_a1.is_experimental() == false, "is_experimental()"); verbose_assert(ip4_class_a2.is_experimental() == false, "is_experimental()"); verbose_assert(ip4_class_a3.is_experimental() == false, "is_experimental()"); verbose_assert(ip4_class_b1.is_experimental() == false, "is_experimental()"); verbose_assert(ip4_class_b2.is_experimental() == false, "is_experimental()"); verbose_assert(ip4_class_b3.is_experimental() == false, "is_experimental()"); verbose_assert(ip4_class_c1.is_experimental() == false, "is_experimental()"); verbose_assert(ip4_class_c2.is_experimental() == false, "is_experimental()"); verbose_assert(ip4_class_c3.is_experimental() == false, "is_experimental()"); verbose_assert(ip4_multicast1.is_experimental() == false, "is_experimental()"); verbose_assert(ip4_multicast2.is_experimental() == false, "is_experimental()"); verbose_assert(ip4_multicast3.is_experimental() == false, "is_experimental()"); verbose_assert(ip4_experimental1.is_experimental() == true, "is_experimental()"); verbose_assert(ip4_experimental2.is_experimental() == true, "is_experimental()"); verbose_assert(ip4_experimental3.is_experimental() == true, "is_experimental()"); // // Test if an address is a valid link-local unicast address: IPv4 // verbose_assert(ip4_zero.is_linklocal_unicast() == false, "is_linklocal_unicast()"); verbose_assert(ip4_unicast1.is_linklocal_unicast() == false, "is_linklocal_unicast()"); verbose_assert(ip4_unicast2.is_linklocal_unicast() == false, "is_linklocal_unicast()"); verbose_assert(ip4_unicast3.is_linklocal_unicast() == false, "is_linklocal_unicast()"); // // Test if an address is a valid link-local unicast address: IPv6 // verbose_assert(ip6_unicast_linklocal1.is_linklocal_unicast() == true, "is_linklocal_unicast()"); verbose_assert(ip6_unicast1.is_linklocal_unicast() == false, "is_linklocal_unicast()"); verbose_assert(ip6_unicast2.is_linklocal_unicast() == false, "is_linklocal_unicast()"); verbose_assert(ip6_unicast3.is_linklocal_unicast() == false, "is_linklocal_unicast()"); // // Test if an address is a valid interface-local multicast address: IPv4 // verbose_assert(ip4_multicast1.is_interfacelocal_multicast() == false, "is_interfacelocal_multicast()"); verbose_assert(ip4_multicast2.is_interfacelocal_multicast() == false, "is_interfacelocal_multicast()"); verbose_assert(ip4_multicast3.is_interfacelocal_multicast() == false, "is_interfacelocal_multicast()"); // // Test if an address is a valid interface-local multicast address: IPv6 // verbose_assert(ip6_multicast_interfacelocal1.is_interfacelocal_multicast() == true, "is_interfacelocal_multicast()"); verbose_assert(ip6_multicast1.is_interfacelocal_multicast() == false, "is_interfacelocal_multicast()"); verbose_assert(ip6_multicast2.is_interfacelocal_multicast() == false, "is_interfacelocal_multicast()"); verbose_assert(ip6_multicast3.is_interfacelocal_multicast() == false, "is_interfacelocal_multicast()"); // // Test if an address is a valid link-local multicast address: IPv4 // verbose_assert(ip4_multicast_linklocal1.is_linklocal_multicast() == true, "is_linklocal_multicast()"); verbose_assert(ip4_multicast2.is_linklocal_multicast() == false, "is_linklocal_multicast()"); verbose_assert(ip4_multicast3.is_linklocal_multicast() == false, "is_linklocal_multicast()"); // // Test if an address is a valid link-local multicast address: IPv6 // verbose_assert(ip6_multicast_linklocal1.is_linklocal_multicast() == true, "is_linklocal_multicast()"); verbose_assert(ip6_multicast1.is_linklocal_multicast() == false, "is_linklocal_multicast()"); verbose_assert(ip6_multicast2.is_linklocal_multicast() == false, "is_linklocal_multicast()"); verbose_assert(ip6_multicast3.is_linklocal_multicast() == false, "is_linklocal_multicast()"); // // Test if an address is a valid loopback address: IPv4 // verbose_assert(ip4_loopback1.is_loopback() == true, "is_loopback()"); verbose_assert(ip4_loopback2.is_loopback() == true, "is_loopback()"); verbose_assert(ip4_zero.is_loopback() == false, "is_loopback()"); verbose_assert(ip4_unicast1.is_loopback() == false, "is_loopback()"); verbose_assert(ip4_unicast2.is_loopback() == false, "is_loopback()"); verbose_assert(ip4_unicast3.is_loopback() == false, "is_loopback()"); // // Test if an address is a valid loopback address: IPv6 // verbose_assert(ip6_loopback1.is_loopback() == true, "is_loopback()"); verbose_assert(ip6_zero.is_loopback() == false, "is_loopback()"); verbose_assert(ip6_unicast2.is_loopback() == false, "is_loopback()"); verbose_assert(ip6_unicast3.is_loopback() == false, "is_loopback()"); } /** * Test IPvX address constant values. */ void test_ipvx_address_const() { // // Test the address octet-size. // verbose_assert(IPvX::addr_bytelen(AF_INET) == 4, "addr_bytelen()"); verbose_assert(IPvX::addr_bytelen(AF_INET6) == 16, "addr_bytelen()"); // // Test the address bit-length. // verbose_assert(IPvX::addr_bitlen(AF_INET) == 32, "addr_bitlen()"); verbose_assert(IPvX::addr_bitlen(AF_INET6) == 128, "addr_bitlen()"); // // Test the mask length for the multicast base address. // verbose_assert(IPvX::ip_multicast_base_address_mask_len(AF_INET) == 4, "ip_multicast_base_address_mask_len()"); verbose_assert(IPvX::ip_multicast_base_address_mask_len(AF_INET6) == 8, "ip_multicast_base_address_mask_len()"); // // Test the mask length for the experimental base address. // // XXX: This test applies only for IPv4. verbose_assert(IPvX::ip_experimental_base_address_mask_len(AF_INET) == 4, "ip_experimental_base_address_mask_len()"); // // Test the address family. // IPvX ip1(AF_INET); verbose_assert(ip1.af() == AF_INET, "af()"); IPvX ip2(AF_INET6); verbose_assert(ip2.af() == AF_INET6, "af()"); // // Test the IP protocol version. // IPvX ip3(AF_INET); verbose_assert(ip3.ip_version() == 4, "ip_version()"); verbose_assert(ip3.ip_version_str() == "IPv4", "ip_version_str()"); IPvX ip4(AF_INET6); verbose_assert(ip4.ip_version() == 6, "ip_version()"); verbose_assert(ip4.ip_version_str() == "IPv6", "ip_version_str()"); // // Test pre-defined constant addresses // verbose_assert(IPvX::ZERO(AF_INET) == IPvX("0.0.0.0"), "ZERO()"); verbose_assert(IPvX::ZERO(AF_INET6) == IPvX("::"), "ZERO()"); verbose_assert(IPvX::ANY(AF_INET) == IPvX("0.0.0.0"), "ANY()"); verbose_assert(IPvX::ANY(AF_INET6) == IPvX("::"), "ANY()"); verbose_assert(IPvX::ALL_ONES(AF_INET) == IPvX("255.255.255.255"), "ALL_ONES()"); verbose_assert(IPvX::ALL_ONES(AF_INET6) == IPvX("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"), "ALL_ONES()"); verbose_assert(IPvX::LOOPBACK(AF_INET) == IPvX("127.0.0.1"), "LOOPBACK()"); verbose_assert(IPvX::LOOPBACK(AF_INET6) == IPvX("::1"), "LOOPBACK()"); verbose_assert(IPvX::MULTICAST_BASE(AF_INET) == IPvX("224.0.0.0"), "MULTICAST_BASE()"); verbose_assert(IPvX::MULTICAST_BASE(AF_INET6) == IPvX("ff00::"), "MULTICAST_BASE()"); verbose_assert(IPvX::MULTICAST_ALL_SYSTEMS(AF_INET) == IPvX("224.0.0.1"), "MULTICAST_ALL_SYSTEMS()"); verbose_assert(IPvX::MULTICAST_ALL_SYSTEMS(AF_INET6) == IPvX("ff02::1"), "MULTICAST_ALL_SYSTEMS()"); verbose_assert(IPvX::MULTICAST_ALL_ROUTERS(AF_INET) == IPvX("224.0.0.2"), "MULTICAST_ALL_ROUTERS()"); verbose_assert(IPvX::MULTICAST_ALL_ROUTERS(AF_INET6) == IPvX("ff02::2"), "MULTICAST_ALL_ROUTERS()"); verbose_assert(IPvX::DVMRP_ROUTERS(AF_INET) == IPvX("224.0.0.4"), "DVMRP_ROUTERS()"); verbose_assert(IPvX::DVMRP_ROUTERS(AF_INET6) == IPvX("ff02::4"), "DVMRP_ROUTERS()"); verbose_assert(IPvX::OSPFIGP_ROUTERS(AF_INET) == IPvX("224.0.0.5"), "OSPFIGP_ROUTERS()"); verbose_assert(IPvX::OSPFIGP_ROUTERS(AF_INET6) == IPvX("ff02::5"), "OSPFIGP_ROUTERS()"); verbose_assert(IPvX::OSPFIGP_DESIGNATED_ROUTERS(AF_INET) == IPvX("224.0.0.6"), "OSPIGP_DESIGNATED_ROUTERS()"); verbose_assert(IPvX::OSPFIGP_DESIGNATED_ROUTERS(AF_INET6) == IPvX("ff02::6"), "OSPIGP_DESIGNATED_ROUTERS()"); verbose_assert(IPvX::RIP2_ROUTERS(AF_INET) == IPvX("224.0.0.9"), "RIP2_ROUTERS()"); verbose_assert(IPvX::RIP2_ROUTERS(AF_INET6) == IPvX("ff02::9"), "RIP2_ROUTERS()"); verbose_assert(IPvX::PIM_ROUTERS(AF_INET) == IPvX("224.0.0.13"), "PIM_ROUTERS()"); verbose_assert(IPvX::PIM_ROUTERS(AF_INET6) == IPvX("ff02::d"), "PIM_ROUTERS()"); verbose_assert(IPvX::SSM_ROUTERS(AF_INET) == IPvX("224.0.0.22"), "SSM_ROUTERS()"); verbose_assert(IPvX::SSM_ROUTERS(AF_INET6) == IPvX("ff02::16"), "SSM_ROUTERS()"); // XXX: This test applies only for IPv4. verbose_assert(IPvX::EXPERIMENTAL_BASE(AF_INET) == IPvX("240.0.0.0"), "EXPERIMENTAL_BASE()"); } /** * Test IPvX address manipulation. */ void test_ipvx_manipulate_address() { const char *addr_string4 = "12.34.56.78"; const char *addr_string6 = "1234:5678:9abc:def0:fed:cba9:8765:4321"; // // Test making an IPvX mask prefix. // verbose_assert(IPvX().make_prefix(AF_INET, 24) == IPvX("255.255.255.0"), "make_prefix()"); verbose_assert(IPvX().make_prefix(AF_INET, 0) == IPvX("0.0.0.0"), "make_prefix()"); verbose_assert(IPvX().make_prefix(AF_INET, 32) == IPvX("255.255.255.255"), "make_prefix()"); verbose_assert(IPvX().make_prefix(AF_INET6, 24) == IPvX("ffff:ff00::"), "make_prefix()"); verbose_assert(IPvX().make_prefix(AF_INET6, 0) == IPvX("::"), "make_prefix()"); verbose_assert(IPvX().make_prefix(AF_INET6, 128) == IPvX("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"), "make_prefix()"); // // Test making an IPvX mask prefix for the address family of this address. // IPvX ip04(AF_INET); verbose_assert(ip04.make_prefix(24) == IPvX("255.255.255.0"), "make_prefix()"); IPvX ip06(AF_INET6); verbose_assert(ip06.make_prefix(24) == IPvX("ffff:ff00::"), "make_prefix()"); // // Test making an IPvX address prefix. // verbose_assert( IPvX("12.34.56.78").mask_by_prefix_len(24) == IPvX("12.34.56.0"), "mask_by_prefix_len()" ); verbose_assert( IPvX("1234:5678:9abc:def0:fed:cba9:8765:4321").mask_by_prefix_len(24) == IPvX("1234:5600::"), "mask_by_prefix_len()" ); // // Test getting the prefix length of the contiguous mask. // verbose_assert(IPvX("255.255.255.0").mask_len() == 24, "mask_len()"); verbose_assert(IPvX("ffff:ff00::").mask_len() == 24, "mask_len()"); // XXX: for IPvX we don't have addr() and set_addr() methods, hence // we don't test them. // // Test extracting bits from an address. // verbose_assert(IPvX("12.34.56.78").bits(0, 8) == 78, "bits()"); verbose_assert(IPvX("1234:5678:9abc:def0:fed:cba9:8765:4321").bits(0, 8) == 0x21, "bits()"); // // Test counting the number of bits in an address. // verbose_assert(IPvX::ZERO(AF_INET).bit_count() == 0, "bit_count()"); verbose_assert(IPvX::ALL_ONES(AF_INET).bit_count() == 32, "bit_count()"); verbose_assert(IPvX("240.15.240.15").bit_count() == 16, "bit_count()"); verbose_assert(IPvX::ZERO(AF_INET6).bit_count() == 0, "bit_count()"); verbose_assert(IPvX::ALL_ONES(AF_INET6).bit_count() == 128, "bit_count()"); verbose_assert(IPvX("f00f:0ff0:f00f:0ff0:f00f:0ff0:f00f:0ff0").bit_count() == 64, "bit_count()"); // // Test counting the number of leading zeroes in an address. // verbose_assert(IPvX::ZERO(AF_INET).leading_zero_count() == 32, "leading_zero_count()"); verbose_assert(IPvX::ALL_ONES(AF_INET).leading_zero_count() == 0, "leading_zero_count()"); verbose_assert(IPvX("0.15.255.255").leading_zero_count() == 12, "leading_zero_count()"); verbose_assert(IPvX::ZERO(AF_INET6).leading_zero_count() == 128, "leading_zero_count()"); verbose_assert(IPvX::ALL_ONES(AF_INET6).leading_zero_count() == 0, "leading_zero_count()"); verbose_assert(IPv6("0000:0000:0000:0001:ffff:ffff:ffff:ffff").leading_zero_count() == 63, "leading_zero_count()"); // // Test if this address is IPv4 address. // IPvX ip1(AF_INET); verbose_assert(ip1.is_ipv4() == true, "is_ipv4()"); // // Test if this address is IPv6 address. // IPvX ip2(AF_INET6); verbose_assert(ip2.is_ipv6() == true, "is_ipv6()"); // // Get IPv4 address. // IPvX ip3(addr_string4); IPv4 ip3_ipv4(addr_string4); verbose_assert(ip3.get_ipv4() == ip3_ipv4, "get_ipv4()"); // // Get IPv6 address. // IPvX ip4(addr_string6); IPv6 ip4_ipv6(addr_string6); verbose_assert(ip4.get_ipv6() == ip4_ipv6, "get_ipv6()"); // // Assign address value to an IPv4 address. // IPvX ip5(addr_string4); IPv4 ip5_ipv4(addr_string4); IPv4 ip5_ipv4_tmp; ip5.get(ip5_ipv4_tmp); verbose_assert(ip5_ipv4_tmp == ip5_ipv4, "get(IPv4& to_ipv4)"); // // Assign address value to an IPv6 address. // IPvX ip6(addr_string6); IPv6 ip6_ipv6(addr_string6); IPv6 ip6_ipv6_tmp; ip6.get(ip6_ipv6_tmp); verbose_assert(ip6_ipv6_tmp == ip6_ipv6, "get(IPv6& to_ipv6)"); } /** * Test IPvX invalid address manipulation. */ void test_ipvx_invalid_manipulate_address() { const char *addr_string4 = "12.34.56.78"; const char *addr_string6 = "1234:5678:9abc:def0:fed:cba9:8765:4321"; // // Get invalid IPv4 address. // try { IPvX ip(addr_string6); // Note: initialized with IPv6 address IPv4 ip_ipv4; ip_ipv4 = ip.get_ipv4(); verbose_log("Cannot catch invalid get_ipv4() : FAIL\n"); incr_failures(); } catch (const InvalidCast& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } // // Get invalid IPv6 address. // try { IPvX ip(addr_string4); // Note: initialized with IPv4 address IPv6 ip_ipv6; ip_ipv6 = ip.get_ipv6(); verbose_log("Cannot catch invalid get_ipv4() : FAIL\n"); incr_failures(); } catch (const InvalidCast& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } // // Assign invalid address value to an IPv4 address. // try { IPvX ip(addr_string6); // Note: initialized with IPv6 address IPv4 ip_ipv4; ip.get(ip_ipv4); verbose_log("Cannot catch invalid get(IPv4& to_ipv4) : FAIL\n"); incr_failures(); } catch (const InvalidCast& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } // // Assign invalid address value to an IPv6 address. // try { IPvX ip(addr_string4); // Note: initialized with IPv4 address IPv6 ip_ipv6; ip.get(ip_ipv6); verbose_log("Cannot catch invalid get(IPv6& to_ipv6) : FAIL\n"); incr_failures(); } catch (const InvalidCast& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } // // Test making an invalid IPvX mask prefix. // try { // Invalid prefix length IPvX ip(IPvX::make_prefix(AF_UNSPEC, 0)); verbose_log("Cannot catch invalid IP address family AF_UNSPEC : FAIL\n"); incr_failures(); UNUSED(ip); } catch (const InvalidFamily& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } try { // Invalid prefix length: IPv4 IPvX ip(IPvX::make_prefix(AF_INET, IPvX::addr_bitlen(AF_INET) + 1)); verbose_log("Cannot catch invalid IPv4 mask prefix with length %u : FAIL\n", XORP_UINT_CAST(IPvX::addr_bitlen(AF_INET) + 1)); incr_failures(); UNUSED(ip); } catch (const InvalidNetmaskLength& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } try { // Invalid prefix length: IPv6 IPvX ip(IPvX::make_prefix(AF_INET6, IPvX::addr_bitlen(AF_INET6) + 1)); verbose_log("Cannot catch invalid IPv6 mask prefix with length %u : FAIL\n", XORP_UINT_CAST(IPvX::addr_bitlen(AF_INET6) + 1)); incr_failures(); UNUSED(ip); } catch (const InvalidNetmaskLength& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } // // Test masking with an invalid IPvX mask prefix. // try { // Invalid mask prefix: IPv4 IPvX ip(addr_string4); ip.mask_by_prefix_len(IPvX::addr_bitlen(AF_INET) + 1); verbose_log("Cannot catch masking with an invalid IPv4 mask prefix with length %u : FAIL\n", XORP_UINT_CAST(IPvX::addr_bitlen(AF_INET) + 1)); incr_failures(); } catch (const InvalidNetmaskLength& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } try { // Invalid mask prefix: IPv6 IPvX ip(addr_string6); ip.mask_by_prefix_len(IPvX::addr_bitlen(AF_INET6) + 1); verbose_log("Cannot catch masking with an invalid IPv6 mask prefix with length %u : FAIL\n", XORP_UINT_CAST(IPvX::addr_bitlen(AF_INET6) + 1)); incr_failures(); } catch (const InvalidNetmaskLength& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } } int main(int argc, char * const argv[]) { int ret_value = 0; // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); int ch; while ((ch = getopt(argc, argv, "hv")) != -1) { switch (ch) { case 'v': set_verbose(true); break; case 'h': case '?': default: usage(argv[0]); xlog_stop(); xlog_exit(); if (ch == 'h') return (0); else return (1); } } argc -= optind; argv += optind; XorpUnexpectedHandler x(xorp_unexpected_handler); try { test_ipvx_valid_constructors(); test_ipvx_invalid_constructors(); test_ipvx_valid_copy_in_out(); test_ipvx_invalid_copy_in_out(); test_ipvx_operators(); test_ipvx_address_type(); test_ipvx_address_const(); test_ipvx_manipulate_address(); test_ipvx_invalid_manipulate_address(); ret_value = failures() ? 1 : 0; } catch (...) { // Internal error xorp_print_standard_exceptions(); ret_value = 2; } // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); return (ret_value); } xorp/libxorp/tests/test_types.cc0000664000076400007640000000623711540224230017214 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "libxorp_module.h" #include "xorp.h" #include "xlog.h" #include "ipv4net.hh" #include "ipv6net.hh" template static bool test_serialization(const string& name, const C& c) { string serialized = c.str(); if (c.str() == C(serialized.c_str()).str()) { cout << "Passed Test: serialization test for " << name << endl; return false; } cerr << "Failed Test: serialization test for " << name << endl; return true; } static int test_serializations() { if (test_serialization("IPv4", IPv4("10.1.2.3"))) { return -1; } else if (test_serialization("IPv4Net", IPv4Net("10.1.2.3/24"))) { return -1; } else if (test_serialization("IPv6", IPv6("fe80::2c0:4fff:fe68:8c58"))) { return -1; } else if (test_serialization("IPv6", IPv6Net("fe80::2c0:4fff:fe68:8c58/23"))) { return -1; } return 0; } static int test_ipv4_operators() { IPv4 ip4("192.150.187.250"); if (IPv4("0.0.0.0") == ip4) { cerr << "First IPv4 equality test failed." << endl; return -1; } else if (IPv4("192.150.187.250") != ip4) { cerr << "Second IPv4 equality test failed." << endl; return -1; }else if (IPv4("192.150.187.251") != ++ip4) { cerr << "Failed IPv4 increment test" << endl; return -1; } else if (IPv4("192.150.187.250") != --ip4) { cerr << "Failed IPv4 decrement test" << endl; return -1; } else if ((ip4 << 1) != IPv4("129.45.119.244")) { cerr << "Failed IPv4 left roll test" << endl; return -1; } else if ((ip4 >> 1) != IPv4("96.75.93.253")) { cerr << "Failed IPv4 right roll test" << endl; return -1; } return 0; } int main (int /* argc */, char *argv[]) { // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); XorpUnexpectedHandler x(xorp_unexpected_handler); int r = -1; try { r = test_serializations(); r |= test_ipv4_operators(); } catch (...) { xorp_catch_standard_exceptions(); } // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); return r; } xorp/libxorp/tests/test_ref_ptr.cc0000664000076400007640000001421311540224230017502 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "libxorp_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/exceptions.hh" #ifdef HAVE_GETOPT_H #include #endif #include "ref_ptr.hh" // // XXX: MODIFY FOR YOUR TEST PROGRAM // static const char *program_name = "test_ref_ptr"; static const char *program_description = "Test ref_ptr classes"; static const char *program_version_id = "0.1"; static const char *program_date = "April 1, 2003"; static const char *program_copyright = "See file LICENSE"; static const char *program_return_value = "0 on success, 1 if test error, 2 if internal error"; static bool s_verbose = false; bool verbose() { return s_verbose; } void set_verbose(bool v) { s_verbose = v; } static int s_failures = 0; bool failures() { return (s_failures)? (true) : (false); } void incr_failures() { s_failures++; } void reset_failures() { s_failures = 0; } #include "libxorp/xorp_tests.hh" /** * Print program info to output stream. * * @param stream the output stream the print the program info to. */ static void print_program_info(FILE *stream) { fprintf(stream, "Name: %s\n", program_name); fprintf(stream, "Description: %s\n", program_description); fprintf(stream, "Version: %s\n", program_version_id); fprintf(stream, "Date: %s\n", program_date); fprintf(stream, "Copyright: %s\n", program_copyright); fprintf(stream, "Return: %s\n", program_return_value); } /** * Print program usage information to the stderr. * * @param progname the name of the program. */ static void usage(const char* progname) { print_program_info(stderr); fprintf(stderr, "usage: %s [-v] [-h]\n", progname); fprintf(stderr, " -h : usage (this message)\n"); fprintf(stderr, " -v : verbose output\n"); } /** * A simple class that sets a boolean flag to true when destructed. */ class FlagSetDestructor { public: FlagSetDestructor(bool& flag_to_set) : _flag(flag_to_set) {} ~FlagSetDestructor() { _flag = true; } protected: bool& _flag; }; /** * Run through tests of some common operations on a ref_ptr object. */ template static int play_with_counts(Rp& r, int32_t initial_cnt) { Rp copy1 = r; if (copy1 != r) { verbose_log("Failed on operator="); return 1; } { Rp copy2 = r; if (copy2.at_least(initial_cnt + 2) == false) { verbose_log("Failed with number of copies\n"); return 1; } if (copy2.at_least(initial_cnt + 3) == true) { verbose_log("Failed with number of copies\n"); return 1; } if (copy2 != r) { verbose_log("Failed on operator="); return 1; } if (copy2 != copy1) { verbose_log("Failed on operator="); return 1; } } if (copy1.at_least(initial_cnt + 1) == false) { verbose_log("Failed with number of copies\n"); return 1; } if (copy1.at_least(initial_cnt + 2) == true) { verbose_log("Failed to remove reference when pointer went out of " "scope\n"); return 1; } copy1 = 0; if (r.at_least(initial_cnt + 1) == true) { verbose_log("Failed to remove reference when pointer assigned\n"); return 1; } return 0; } static int run_test() { verbose_log("Running ref_ptr test:\n"); bool deleted = false; { ref_ptr rp = new FlagSetDestructor(deleted); { if (play_with_counts(rp, 1)) { return 1; } } } if (deleted == false) { verbose_log("Failed to delete object.\n"); return 1; } verbose_log("Pass.\n"); verbose_log("Running cref_ptr test:\n"); deleted = false; { cref_ptr rp = new FlagSetDestructor(deleted); { if (play_with_counts(rp, 1)) { return 1; } } } if (deleted == false) { verbose_log("Failed to delete object.\n"); return 1; } verbose_log("Pass.\n"); return 0; }; int main(int argc, char * const argv[]) { int ret_value = 0; // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); int ch; while ((ch = getopt(argc, argv, "hv")) != -1) { switch (ch) { case 'v': set_verbose(true); break; case 'h': case '?': default: usage(argv[0]); xlog_stop(); xlog_exit(); if (ch == 'h') return (0); else return (1); } } argc -= optind; argv += optind; XorpUnexpectedHandler x(xorp_unexpected_handler); try { for (uint32_t i = 0 ; i < 100; i++) { ret_value = run_test(); if (ret_value == 0) { if (ref_counter_pool::instance().balance() != 0) { verbose_log("Ref count balance (%d != 0) non-zero at end", XORP_INT_CAST(ref_counter_pool::instance().balance())); ret_value = 1; } } } } catch (...) { // Internal error xorp_print_standard_exceptions(); ret_value = 2; } // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); return (ret_value); } xorp/libxorp/tests/test_timeval.cc0000664000076400007640000001421311540224230017502 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "libxorp_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/exceptions.hh" #ifdef HAVE_GETOPT_H #include #endif #include "timeval.hh" // // XXX: MODIFY FOR YOUR TEST PROGRAM // static const char *program_name = "test_timeval"; static const char *program_description = "Test TimeVal class"; static const char *program_version_id = "0.1"; static const char *program_date = "April 2, 2003"; static const char *program_copyright = "See file LICENSE"; static const char *program_return_value = "0 on success, 1 if test error, 2 if internal error"; static bool s_verbose = false; bool verbose() { return s_verbose; } void set_verbose(bool v) { s_verbose = v; } static int s_failures = 0; bool failures() { return s_failures; } void incr_failures() { s_failures++; } #include "xorp_tests.hh" /** * Print program info to output stream. * * @param stream the output stream the print the program info to. */ static void print_program_info(FILE *stream) { fprintf(stream, "Name: %s\n", program_name); fprintf(stream, "Description: %s\n", program_description); fprintf(stream, "Version: %s\n", program_version_id); fprintf(stream, "Date: %s\n", program_date); fprintf(stream, "Copyright: %s\n", program_copyright); fprintf(stream, "Return: %s\n", program_return_value); } /** * Print program usage information to the stderr. * * @param progname the name of the program. */ static void usage(const char* progname) { print_program_info(stderr); fprintf(stderr, "usage: %s [-v] [-h]\n", progname); fprintf(stderr, " -h : usage (this message)\n"); fprintf(stderr, " -v : verbose output\n"); } static void test_timeval_constants() { // // Test TimeVal::MAXIMUM() // verbose_assert(TimeVal::ZERO() < TimeVal::MAXIMUM(), "compare TimeVal::ZERO() < TimeVal::MAXIMUM()"); // // Test TimeVal::MINIMUM() // verbose_assert(TimeVal::MINIMUM() < TimeVal::ZERO(), "compare TimeVal::MINIMUM() < TimeVal::ZERO()"); } static void test_timeval_operators() { TimeVal t(100, 100); TimeVal mt = -t; TimeVal half_t(50, 50); // // Addition Operator // verbose_assert(t + mt == TimeVal::ZERO(), "operator+"); // // Substraction Operator // verbose_assert(t - half_t == half_t, "operator-"); // // Multiplication Operator // verbose_assert(half_t * 2 == t, "operator*"); verbose_assert(half_t * 2.0 == t, "operator* (double float)"); // // Division Operator // verbose_assert(t / 2 == half_t, "operator/"); verbose_assert(t / 2.0 == half_t, "operator/ (double float)"); } static void test_timeval_type() { TimeVal timeval_zero = TimeVal::ZERO(); TimeVal timeval_onesec = TimeVal(1, 0); TimeVal timeval_oneusec = TimeVal(0, 1); TimeVal timeval_max = TimeVal::MAXIMUM(); // // Test TimeVal::is_zero() // verbose_assert(timeval_zero.is_zero() == true, "is_zero()"); verbose_assert(timeval_onesec.is_zero() == false, "is_zero()"); verbose_assert(timeval_oneusec.is_zero() == false, "is_zero()"); verbose_assert(timeval_max.is_zero() == false, "is_zero()"); } static void test_timeval_random_uniform() { static const int TEST_INTERVAL = 30; static const double LOWER_BOUND = 0.75F; TimeVal interval(TEST_INTERVAL, 0); TimeVal tmin((TEST_INTERVAL * LOWER_BOUND) - 1); // Double expression TimeVal tmax(TEST_INTERVAL + 1, 0); // // Calculate jitter which is uniformly distributed as a fraction // of TEST_INTERVAL between LOWER_BOUND (0.75) and 1.0. // TimeVal lower_bound_timeval = TimeVal(interval.get_double() * LOWER_BOUND); TimeVal result = random_uniform(lower_bound_timeval, interval); // // Thest that the output is not outside of expected range // string msg = c_format("time random uniform distribution: " "whether time %s is in range [%s, %s]", result.str().c_str(), tmin.str().c_str(), tmax.str().c_str()); verbose_assert((tmin <= result) && (result <= tmax), msg.c_str()); } int main(int argc, char * const argv[]) { int ret_value = 0; // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); int ch; while ((ch = getopt(argc, argv, "hv")) != -1) { switch (ch) { case 'v': set_verbose(true); break; case 'h': case '?': default: usage(argv[0]); xlog_stop(); xlog_exit(); if (ch == 'h') return (0); else return (1); } } argc -= optind; argv += optind; XorpUnexpectedHandler x(xorp_unexpected_handler); try { test_timeval_constants(); test_timeval_operators(); test_timeval_type(); test_timeval_random_uniform(); ret_value = failures() ? 1 : 0; } catch (...) { // Internal error xorp_print_standard_exceptions(); ret_value = 2; } // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); return (ret_value); } xorp/libxorp/tests/test_ipv4net.cc0000664000076400007640000004762111633463661017463 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "libxorp_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/exceptions.hh" #include "libxorp/timer.hh" #include "libxorp/test_main.hh" #ifdef HAVE_GETOPT_H #include #endif #include "ipv4net.hh" // // TODO: XXX: remove after the switch to the TestMain facility is completed // #if 0 // // XXX: MODIFY FOR YOUR TEST PROGRAM // static const char *program_name = "test_ipv4net"; static const char *program_description = "Test IPv4Net address class"; static const char *program_version_id = "0.1"; static const char *program_date = "December 2, 2002"; static const char *program_copyright = "See file LICENSE"; static const char *program_return_value = "0 on success, 1 if test error, 2 if internal error"; #endif // 0 static bool s_verbose = false; bool verbose() { return s_verbose; } void set_verbose(bool v) { s_verbose = v; } static int s_failures = 0; bool failures() { return (s_failures)? (true) : (false); } void incr_failures() { s_failures++; } void reset_failures() { s_failures = 0; } #include "libxorp/xorp_tests.hh" // // TODO: XXX: remove after the switch to the TestMain facility is completed // #if 0 /** * Print program info to output stream. * * @param stream the output stream the print the program info to. */ static void print_program_info(FILE *stream) { fprintf(stream, "Name: %s\n", program_name); fprintf(stream, "Description: %s\n", program_description); fprintf(stream, "Version: %s\n", program_version_id); fprintf(stream, "Date: %s\n", program_date); fprintf(stream, "Copyright: %s\n", program_copyright); fprintf(stream, "Return: %s\n", program_return_value); } /** * Print program usage information to the stderr. * * @param progname the name of the program. */ static void usage(const char* progname) { print_program_info(stderr); fprintf(stderr, "usage: %s [-v] [-h]\n", progname); fprintf(stderr, " -h : usage (this message)\n"); fprintf(stderr, " -v : verbose output\n"); } #endif // 0 /** * Test IPv4Net valid constructors. */ bool test_ipv4net_valid_constructors(TestInfo& test_info) { UNUSED(test_info); // Test values for IPv4 address: "12.34.56.78" const char *addr_string = "12.34.56.78"; uint32_t ui = htonl((12 << 24) | (34 << 16) | (56 << 8) | 78); struct sockaddr_in sin; memset(&sin, 0, sizeof(sin)); #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN sin.sin_len = sizeof(sin); #endif sin.sin_family = AF_INET; sin.sin_addr.s_addr = ui; const char *netaddr_string = "12.34.56.0/24"; // // Default constructor. // IPv4Net ipnet1; verbose_match(ipnet1.str(), "0.0.0.0/0"); // // Constructor from a given base address and a prefix length. // IPv4 ip2(addr_string); IPv4Net ipnet2(ip2, 24); verbose_match(ipnet2.str(), "12.34.56.0/24"); // // Constructor from a string. // IPv4Net ipnet3(netaddr_string); verbose_match(ipnet3.str(), netaddr_string); // // Constructor from another IPv4Net address. // IPv4Net ipnet4(ipnet3); verbose_match(ipnet4.str(), netaddr_string); return (! failures()); } /** * Test IPv4Net invalid constructors. */ bool test_ipv4net_invalid_constructors(TestInfo& test_info) { UNUSED(test_info); // // Constructor for invalid prefix length. // try { IPv4 ip("12.34.56.78"); IPv4Net ipnet(ip, ip.addr_bitlen() + 1); verbose_log("Cannot catch invalid prefix length : FAIL\n"); incr_failures(); UNUSED(ipnet); } catch (const InvalidNetmaskLength& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } // // Constructor from an invalid address string. // try { // Invalid address string: note the typo -- lack of prefix length IPv4Net ipnet("12.34.56.78/"); verbose_log("Cannot catch invalid IP network address \"12.34.56.78/\" : FAIL\n"); incr_failures(); UNUSED(ipnet); } catch (const InvalidString& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } // // Constructor from an address string with invalid prefix length. // try { // Invalid address string: prefix length too long IPv4Net ipnet("12.34.56.78/33"); verbose_log("Cannot catch invalid IP network address \"12.34.56.78/33\" : FAIL\n"); incr_failures(); UNUSED(ipnet); } catch (const InvalidNetmaskLength& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } return (! failures()); } /** * Test IPv4Net operators. */ bool test_ipv4net_operators(TestInfo& test_info) { UNUSED(test_info); IPv4Net ipnet_a("12.34.0.0/16"); IPv4Net ipnet_b("12.35.0.0/16"); IPv4Net ipnet_c("12.34.56.0/24"); // // Assignment operator // IPv4Net ipnet1; ipnet1 = ipnet_a; verbose_assert(ipnet1.str() == ipnet_a.str(), "operator="); // // Equality Operator // verbose_assert(ipnet_a == ipnet_a, "operator=="); verbose_assert(!(ipnet_a == ipnet_b), "operator=="); verbose_assert(!(ipnet_a == ipnet_c), "operator=="); // // Less-Than Operator // verbose_assert((IPv4Net("12.34.0.0/16") < IPv4Net("12.34.0.0/16")) == false, "operator<"); verbose_assert((IPv4Net("12.34.0.0/16") < IPv4Net("12.34.56.0/24")) == false, "operator<"); verbose_assert((IPv4Net("12.34.0.0/16") < IPv4Net("12.0.0.0/8")) == true, "operator<"); verbose_assert((IPv4Net("12.34.0.0/16") < IPv4Net("12.35.0.0/16")) == true, "operator<"); // // Decrement Operator // verbose_assert(--IPv4Net("12.34.0.0/16") == IPv4Net("12.33.0.0/16"), "operator--()"); verbose_assert(--IPv4Net("0.0.0.0/16") == IPv4Net("255.255.0.0/16"), "operator--()"); // // Increment Operator // verbose_assert(++IPv4Net("12.34.0.0/16") == IPv4Net("12.35.0.0/16"), "operator++()"); verbose_assert(++IPv4Net("255.255.0.0/16") == IPv4Net("0.0.0.0/16"), "operator++()"); // // Test if the object contains a real (non-default) value. // verbose_assert(! IPv4Net().is_valid(), "is_valid()"); verbose_assert(! IPv4Net("0.0.0.0/0").is_valid(), "is_valid()"); verbose_assert(IPv4Net("0.0.0.0/1").is_valid(), "is_valid()"); return (! failures()); } /** * Test IPv4Net address type. */ bool test_ipv4net_address_type(TestInfo& test_info) { UNUSED(test_info); IPv4Net ipnet_default("0.0.0.0/0"); // Default route: unicast IPv4Net ipnet_unicast1("0.0.0.0/1"); // Unicast IPv4Net ipnet_unicast2("12.34.0.0/16"); // Unicast IPv4Net ipnet_unicast3("128.0.0.0/2"); // Unicast IPv4Net ipnet_unicast4("128.16.0.0/24"); // Unicast IPv4Net ipnet_unicast5("192.0.0.0/3"); // Unicast IPv4Net ipnet_multicast1("224.0.0.0/4"); // Multicast IPv4Net ipnet_multicast2("224.0.0.0/24"); // Multicast IPv4Net ipnet_multicast3("224.0.1.0/24"); // Multicast IPv4Net ipnet_class_a1("0.0.0.0/1"); // Class A IPv4Net ipnet_class_a2("12.34.0.0/16"); // Class A IPv4Net ipnet_class_b1("128.0.0.0/2"); // Class B IPv4Net ipnet_class_b2("130.2.3.0/24"); // Class B IPv4Net ipnet_class_c1("192.0.0.0/3"); // Class C IPv4Net ipnet_class_c2("192.2.3.4/32"); // Class C IPv4Net ipnet_experimental1("240.0.0.0/4"); // Experimental IPv4Net ipnet_experimental2("240.0.1.0/16"); // Experimental IPv4Net ipnet_odd1("128.0.0.0/1"); // Odd: includes multicast IPv4Net ipnet_odd2("192.0.0.0/2"); // Odd: includes multicast // // Test if a subnet is within the unicast address range. // verbose_assert(ipnet_default.is_unicast() == true, "is_unicast()"); verbose_assert(ipnet_unicast1.is_unicast() == true, "is_unicast()"); verbose_assert(ipnet_unicast2.is_unicast() == true, "is_unicast()"); verbose_assert(ipnet_unicast3.is_unicast() == true, "is_unicast()"); verbose_assert(ipnet_unicast4.is_unicast() == true, "is_unicast()"); verbose_assert(ipnet_unicast5.is_unicast() == true, "is_unicast()"); verbose_assert(ipnet_multicast1.is_unicast() == false, "is_unicast()"); verbose_assert(ipnet_multicast2.is_unicast() == false, "is_unicast()"); verbose_assert(ipnet_multicast3.is_unicast() == false, "is_unicast()"); verbose_assert(ipnet_class_a1.is_unicast() == true, "is_unicast()"); verbose_assert(ipnet_class_a2.is_unicast() == true, "is_unicast()"); verbose_assert(ipnet_class_b1.is_unicast() == true, "is_unicast()"); verbose_assert(ipnet_class_b2.is_unicast() == true, "is_unicast()"); verbose_assert(ipnet_class_c1.is_unicast() == true, "is_unicast()"); verbose_assert(ipnet_class_c2.is_unicast() == true, "is_unicast()"); verbose_assert(ipnet_experimental1.is_unicast() == false, "is_unicast()"); verbose_assert(ipnet_experimental2.is_unicast() == false, "is_unicast()"); verbose_assert(ipnet_odd1.is_unicast() == false, "is_unicast()"); verbose_assert(ipnet_odd2.is_unicast() == false, "is_unicast()"); // // Test if a subnet is within the multicast address range. // verbose_assert(ipnet_default.is_multicast() == false, "is_multicast()"); verbose_assert(ipnet_unicast1.is_multicast() == false, "is_multicast()"); verbose_assert(ipnet_unicast2.is_multicast() == false, "is_multicast()"); verbose_assert(ipnet_unicast3.is_multicast() == false, "is_multicast()"); verbose_assert(ipnet_unicast4.is_multicast() == false, "is_multicast()"); verbose_assert(ipnet_unicast5.is_multicast() == false, "is_multicast()"); verbose_assert(ipnet_multicast1.is_multicast() == true, "is_multicast()"); verbose_assert(ipnet_multicast2.is_multicast() == true, "is_multicast()"); verbose_assert(ipnet_multicast3.is_multicast() == true, "is_multicast()"); verbose_assert(ipnet_class_a1.is_multicast() == false, "is_multicast()"); verbose_assert(ipnet_class_a2.is_multicast() == false, "is_multicast()"); verbose_assert(ipnet_class_b1.is_multicast() == false, "is_multicast()"); verbose_assert(ipnet_class_b2.is_multicast() == false, "is_multicast()"); verbose_assert(ipnet_class_c1.is_multicast() == false, "is_multicast()"); verbose_assert(ipnet_class_c2.is_multicast() == false, "is_multicast()"); verbose_assert(ipnet_experimental1.is_multicast() == false, "is_multicast()"); verbose_assert(ipnet_experimental2.is_multicast() == false, "is_multicast()"); verbose_assert(ipnet_odd1.is_multicast() == false, "is_multicast()"); verbose_assert(ipnet_odd2.is_multicast() == false, "is_multicast()"); // // Test if a subnet is within the experimental address range. // verbose_assert(ipnet_default.is_experimental() == false, "is_experimental()"); verbose_assert(ipnet_unicast1.is_experimental() == false, "is_experimental()"); verbose_assert(ipnet_unicast2.is_experimental() == false, "is_experimental()"); verbose_assert(ipnet_unicast3.is_experimental() == false, "is_experimental()"); verbose_assert(ipnet_unicast4.is_experimental() == false, "is_experimental()"); verbose_assert(ipnet_unicast5.is_experimental() == false, "is_experimental()"); verbose_assert(ipnet_multicast1.is_experimental() == false, "is_experimental()"); verbose_assert(ipnet_multicast2.is_experimental() == false, "is_experimental()"); verbose_assert(ipnet_multicast3.is_experimental() == false, "is_experimental()"); verbose_assert(ipnet_class_a1.is_experimental() == false, "is_experimental()"); verbose_assert(ipnet_class_a2.is_experimental() == false, "is_experimental()"); verbose_assert(ipnet_class_b1.is_experimental() == false, "is_experimental()"); verbose_assert(ipnet_class_b2.is_experimental() == false, "is_experimental()"); verbose_assert(ipnet_class_c1.is_experimental() == false, "is_experimental()"); verbose_assert(ipnet_class_c2.is_experimental() == false, "is_experimental()"); verbose_assert(ipnet_experimental1.is_experimental() == true, "is_experimental()"); verbose_assert(ipnet_experimental2.is_experimental() == true, "is_experimental()"); verbose_assert(ipnet_odd1.is_experimental() == false, "is_experimental()"); verbose_assert(ipnet_odd2.is_experimental() == false, "is_experimental()"); return (! failures()); } /** * Test IPv4Net address overlap. */ bool test_ipv4net_address_overlap(TestInfo& test_info) { UNUSED(test_info); IPv4Net ipnet_a("12.34.0.0/16"); IPv4Net ipnet_b("12.35.0.0/16"); IPv4Net ipnet_c("12.34.56.0/24"); IPv4Net ipnet_d("12.32.0.0/16"); IPv4Net ipnet_e("12.33.0.0/16"); IPv4Net ipnet_f("1.2.1.0/24"); IPv4Net ipnet_g("1.2.3.0/24"); // // Test if subnets overlap. // verbose_assert(ipnet_a.is_overlap(ipnet_b) == false, "is_overlap()"); verbose_assert(ipnet_a.is_overlap(ipnet_c) == true, "is_overlap()"); verbose_assert(ipnet_c.is_overlap(ipnet_a) == true, "is_overlap()"); // // Test if a subnet contains (or is equal to) another subnet. // verbose_assert(ipnet_a.contains(ipnet_a) == true, "contains(IPv4Net)"); verbose_assert(ipnet_a.contains(ipnet_b) == false, "contains(IPv4Net)"); verbose_assert(ipnet_a.contains(ipnet_c) == true, "contains(IPv4Net)"); verbose_assert(ipnet_c.contains(ipnet_a) == false, "contains(IPv4Net)"); // // Test if an address is within a subnet. // verbose_assert(ipnet_a.contains(ipnet_a.masked_addr()) == true, "contains(IPv4)"); verbose_assert(ipnet_a.contains(ipnet_b.masked_addr()) == false, "contains(IPv4)"); verbose_assert(ipnet_a.contains(ipnet_c.masked_addr()) == true, "contains(IPv4)"); // // Determine the number of the most significant bits overlapping with // another subnet. // verbose_assert(ipnet_a.overlap(ipnet_a) == 16, "overlap()"); verbose_assert(ipnet_a.overlap(ipnet_b) == 15, "overlap()"); verbose_assert(ipnet_a.overlap(ipnet_c) == 16, "overlap()"); verbose_assert(ipnet_d.overlap(ipnet_e) == 15, "overlap()"); verbose_assert(ipnet_f.overlap(ipnet_g) == 22, "overlap()"); return (! failures()); } /** * Test performance of IPv4Net address overlap. */ bool test_performance_ipv4net_address_overlap(TestInfo& test_info) { UNUSED(test_info); IPv4Net ipnet_a("255.0.0.0/8"); IPv4Net ipnet_b("255.255.255.0/24"); // // Test overlapping subnets. // do { size_t i; size_t c = 0; TimeVal begin_timeval, end_timeval, delta_timeval; TimerList::system_gettimeofday(&begin_timeval); for (i = 0; i < 0xffffff; i++) { c += ipnet_a.overlap(ipnet_b); } TimerList::system_gettimeofday(&end_timeval); delta_timeval = end_timeval - begin_timeval; verbose_log("Execution time IPv4Net::overlap(): %s seconds\n", delta_timeval.str().c_str()); } while (false); return (! failures()); } /** * Test IPv4Net address constant values. */ bool test_ipv4net_address_const(TestInfo& test_info) { UNUSED(test_info); IPv4Net ipnet_a("12.34.0.0/16"); // // Test the address family. // verbose_assert(IPv4Net::af() == AF_INET, "af()"); // // Get the base address. // IPv4 ip1; ip1 = ipnet_a.masked_addr(); verbose_match(ip1.str(), "12.34.0.0"); // // Get the prefix length. // verbose_assert(ipnet_a.prefix_len() == 16, "prefix_len()"); // // Get the network mask. // IPv4 ip2; ip2 = ipnet_a.netmask(); verbose_match(ip2.str(), "255.255.0.0"); // // Return the subnet containing all multicast addresses. // verbose_match(IPv4Net::ip_multicast_base_prefix().str(), "224.0.0.0/4"); // // Return the subnet containing all Class A addresses. // verbose_match(IPv4Net::ip_class_a_base_prefix().str(), "0.0.0.0/1"); // // Return the subnet containing all Class B addresses. // verbose_match(IPv4Net::ip_class_b_base_prefix().str(), "128.0.0.0/2"); // // Return the subnet containing all Class C addresses. // verbose_match(IPv4Net::ip_class_c_base_prefix().str(), "192.0.0.0/3"); // // Return the subnet containing all experimental addresses. // verbose_match(IPv4Net::ip_experimental_base_prefix().str(), "240.0.0.0/4"); return (! failures()); } /** * Test IPv4Net address manipulation. */ bool test_ipv4net_manipulate_address(TestInfo& test_info) { UNUSED(test_info); IPv4Net ipnet_a("12.34.0.0/16"); // // Get the highest address within this subnet. // verbose_match(ipnet_a.top_addr().str(), "12.34.255.255"); // // Get the smallest subnet containing both subnets. // verbose_match(IPv4Net::common_subnet(IPv4Net("12.34.1.0/24"), IPv4Net("12.34.128.0/24")).str(), "12.34.0.0/16"); return (! failures()); } int main(int argc, char * const argv[]) { XorpUnexpectedHandler x(xorp_unexpected_handler); // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); TestMain test_main(argc, argv); string test = test_main.get_optional_args("-t", "--test", "run only the specified test"); test_main.complete_args_parsing(); // // TODO: XXX: a temporary glue until we complete the switch to the // TestMain facility. // if (test_main.get_verbose()) set_verbose(true); struct test { string test_name; XorpCallback1::RefPtr cb; bool run_by_default; } tests[] = { { "test_ipv4net_valid_constructors", callback(test_ipv4net_valid_constructors), true }, { "test_ipv4net_invalid_constructors", callback(test_ipv4net_invalid_constructors), true }, { "test_ipv4net_operators", callback(test_ipv4net_operators), true }, { "test_ipv4net_address_type", callback(test_ipv4net_address_type), true }, { "test_ipv4net_address_overlap", callback(test_ipv4net_address_overlap), true }, { "test_ipv4net_address_const", callback(test_ipv4net_address_const), true }, { "test_ipv4net_manipulate_address", callback(test_ipv4net_manipulate_address), true }, { "test_performance_ipv4net_address_overlap", callback(test_performance_ipv4net_address_overlap), false } }; try { if (test.empty()) { for (size_t i = 0; i < sizeof(tests) / sizeof(struct test); i++) { if (! tests[i].run_by_default) continue; reset_failures(); test_main.run(tests[i].test_name, tests[i].cb); } } else { for (size_t i = 0; i < sizeof(tests) / sizeof(struct test); i++) { if (test == tests[i].test_name) { reset_failures(); test_main.run(tests[i].test_name, tests[i].cb); return test_main.exit(); } } test_main.failed("No test with name " + test + " found\n"); } } catch (...) { xorp_print_standard_exceptions(); } // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); return test_main.exit(); } xorp/libxorp/tests/test_config_param.cc0000664000076400007640000001640611540224227020502 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "libxorp_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/exceptions.hh" #ifdef HAVE_GETOPT_H #include #endif #include "config_param.hh" // // XXX: MODIFY FOR YOUR TEST PROGRAM // static const char *program_name = "test_config_param"; static const char *program_description = "Test ConfigParam class"; static const char *program_version_id = "0.1"; static const char *program_date = "December 4, 2002"; static const char *program_copyright = "See file LICENSE"; static const char *program_return_value = "0 on success, 1 if test error, 2 if internal error"; static bool s_verbose = false; bool verbose() { return s_verbose; } void set_verbose(bool v) { s_verbose = v; } static int s_failures = 0; bool failures() { return s_failures; } void incr_failures() { s_failures++; } #include "libxorp/xorp_tests.hh" /** * Print program info to output stream. * * @param stream the output stream the print the program info to. */ static void print_program_info(FILE *stream) { fprintf(stream, "Name: %s\n", program_name); fprintf(stream, "Description: %s\n", program_description); fprintf(stream, "Version: %s\n", program_version_id); fprintf(stream, "Date: %s\n", program_date); fprintf(stream, "Copyright: %s\n", program_copyright); fprintf(stream, "Return: %s\n", program_return_value); } /** * Print program usage information to the stderr. * * @param progname the name of the program. */ static void usage(const char* progname) { print_program_info(stderr); fprintf(stderr, "usage: %s [-v] [-h]\n", progname); fprintf(stderr, " -h : usage (this message)\n"); fprintf(stderr, " -v : verbose output\n"); } // // The callback value argument when config_param_update_callback() is called. // int config_param_update_callback_value = 0; /** * The callback method that will be invoked when the value of ConfigParam * changes. */ void config_param_update_callback(int new_value) { config_param_update_callback_value = new_value; verbose_log("ConfigParam callback called with new value = %d\n", new_value); } /** * Test ConfigParam. */ void test_config_param() { // // Constructor of a parameter with an initial value. // ConfigParam config_param1(1); // // Constructor of a parameter with an initial value and a callback. // ConfigParam config_param2(2, callback(config_param_update_callback)); // // Get the current value of the parameter. // verbose_assert(config_param1.get() == 1, "get()"); verbose_assert(config_param2.get() == 2, "get()"); // // Set the current value of the parameter. // config_param1.set(11); config_param2.set(22); verbose_assert(config_param1.get() == 11, "get()"); verbose_assert(config_param2.get() == 22, "get()"); verbose_assert(config_param_update_callback_value == 22, "config parameter update callback value"); // // Assignment operator // config_param1 = 111; config_param2 = 222; verbose_assert(config_param1.get() == 111, "get()"); verbose_assert(config_param2.get() == 222, "get()"); verbose_assert(config_param_update_callback_value == 222, "config parameter update callback value"); // // Increment and decrement operators (prefix) // config_param2 = 111; verbose_assert(++config_param2 == 112, "++ (prefix)"); verbose_assert(config_param_update_callback_value == 112, "config parameter update callback value"); verbose_assert(--config_param2 == 111, "-- (prefix)"); verbose_assert(config_param_update_callback_value == 111, "config parameter update callback value"); // // Increment and decrement operators (postfix) // config_param2 = 111; verbose_assert(config_param2++ == 111, "++ (postfix)"); verbose_assert(config_param2.get() == 112, "++ (postfix)"); verbose_assert(config_param_update_callback_value == 112, "config parameter update callback value"); verbose_assert(config_param2-- == 112, "-- (postfix)"); verbose_assert(config_param2.get() == 111, "-- (postfix)"); verbose_assert(config_param_update_callback_value == 111, "config parameter update callback value"); // // Methods incr() and decr() // config_param2 = 111; config_param2.incr(); verbose_assert(config_param2.get() == 112, "incr()"); verbose_assert(config_param_update_callback_value == 112, "config parameter update callback value"); config_param2.decr(); verbose_assert(config_param2.get() == 111, "decr()"); verbose_assert(config_param_update_callback_value == 111, "config parameter update callback value"); // // Get the initial value of the parameter. // verbose_assert(config_param1.get_initial_value() == 1, "get_initial_value()"); verbose_assert(config_param2.get_initial_value() == 2, "get_initial_value()"); // // Reset the current value of the parameter to its initial value. // config_param1.reset(); config_param2.reset(); verbose_assert(config_param1.get() == 1, "get()"); verbose_assert(config_param2.get() == 2, "get()"); verbose_assert(config_param_update_callback_value == 2, "config parameter update callback value"); } int main(int argc, char * const argv[]) { int ret_value = 0; // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); int ch; while ((ch = getopt(argc, argv, "hv")) != -1) { switch (ch) { case 'v': set_verbose(true); break; case 'h': case '?': default: usage(argv[0]); xlog_stop(); xlog_exit(); if (ch == 'h') return (0); else return (1); } } argc -= optind; argv += optind; XorpUnexpectedHandler x(xorp_unexpected_handler); try { test_config_param(); ret_value = failures() ? 1 : 0; } catch (...) { // Internal error xorp_print_standard_exceptions(); ret_value = 2; } // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); return (ret_value); } xorp/libxorp/tests/test_heap.cc0000664000076400007640000001315411540224227016767 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "libxorp_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/exceptions.hh" #ifdef HAVE_GETOPT_H #include #endif #include "heap.hh" // // XXX: MODIFY FOR YOUR TEST PROGRAM // static const char *program_name = "test_heap"; static const char *program_description = "Test Heap class"; static const char *program_version_id = "0.1"; static const char *program_date = "July 12, 2004"; static const char *program_copyright = "See file LICENSE"; static const char *program_return_value = "0 on success, 1 if test error, 2 if internal error"; static bool s_verbose = false; bool verbose() { return s_verbose; } void set_verbose(bool v) { s_verbose = v; } static int s_failures = 0; bool failures() { return s_failures; } void incr_failures() { s_failures++; } #include "libxorp/xorp_tests.hh" /** * Print program info to output stream. * * @param stream the output stream the print the program info to. */ static void print_program_info(FILE *stream) { fprintf(stream, "Name: %s\n", program_name); fprintf(stream, "Description: %s\n", program_description); fprintf(stream, "Version: %s\n", program_version_id); fprintf(stream, "Date: %s\n", program_date); fprintf(stream, "Copyright: %s\n", program_copyright); fprintf(stream, "Return: %s\n", program_return_value); } /** * Print program usage information to the stderr. * * @param progname the name of the program. */ static void usage(const char* progname) { print_program_info(stderr); fprintf(stderr, "usage: %s [-v] [-h]\n", progname); fprintf(stderr, " -h : usage (this message)\n"); fprintf(stderr, " -v : verbose output\n"); fprintf(stderr, "Return 0 on success, 1 if test error, 2 if internal error.\n"); } class TestHeap : public Heap { public: TestHeap() : Heap(0) {} void test_heap_push(); void test_heap_push_same_value(); }; /** * Test Heap valid constructors. */ void test_heap_valid_constructors() { } /** * Test Heap invalid constructors. */ void test_heap_invalid_constructors() { } void TestHeap::test_heap_push() { struct heap_entry* he; int i1 = 1; int i2 = 2; int i3 = 3; push(TimeVal(0, i1), reinterpret_cast(&i1)); push(TimeVal(0, i2), reinterpret_cast(&i2)); push(TimeVal(0, i3), reinterpret_cast(&i3)); verbose_assert(size() == 3, "heap size"); he = top(); verbose_assert(he->object == reinterpret_cast(&i1), "heap top value 1"); pop(); verbose_assert(size() == 2, "heap size"); he = top(); verbose_assert(he->object == reinterpret_cast(&i2), "heap top value 2"); pop(); verbose_assert(size() == 1, "heap size"); he = top(); verbose_assert(he->object == reinterpret_cast(&i3), "heap top value 3"); pop(); verbose_assert(size() == 0, "heap size"); } void TestHeap::test_heap_push_same_value() { struct heap_entry* he; int i1_1 = 1; int i1_2 = 1; push(TimeVal(0, i1_1), reinterpret_cast(&i1_1)); push(TimeVal(0, i1_2), reinterpret_cast(&i1_2)); verbose_assert(size() == 2, "heap size"); he = top(); verbose_assert(he->object == reinterpret_cast(&i1_1), "heap top value 1_1"); pop(); verbose_assert(size() == 1, "heap size"); he = top(); verbose_assert(he->object == reinterpret_cast(&i1_2), "Test heap top value 1_2"); pop(); verbose_assert(size() == 0, "heap size"); } int main(int argc, char * const argv[]) { int ret_value = 0; // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); int ch; while ((ch = getopt(argc, argv, "hv")) != -1) { switch (ch) { case 'v': set_verbose(true); break; case 'h': case '?': default: usage(argv[0]); xlog_stop(); xlog_exit(); if (ch == 'h') return (0); else return (1); } } argc -= optind; argv += optind; XorpUnexpectedHandler x(xorp_unexpected_handler); try { TestHeap heap; test_heap_valid_constructors(); test_heap_invalid_constructors(); heap.test_heap_push(); heap.test_heap_push_same_value(); ret_value = failures() ? 1 : 0; } catch (...) { // Internal error xorp_print_standard_exceptions(); ret_value = 2; } // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); return (ret_value); } xorp/libxorp/tests/test_utils.cc0000664000076400007640000001061311540224230017201 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "libxorp_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/exceptions.hh" #include "libxorp/utils.hh" #ifdef HAVE_GETOPT_H #include #endif // // XXX: MODIFY FOR YOUR TEST PROGRAM // static const char *program_name = "test_utils"; static const char *program_description = "Test a set of XORP utilities"; static const char *program_version_id = "0.1"; static const char *program_date = "July 1, 2005"; static const char *program_copyright = "See file LICENSE"; static const char *program_return_value = "0 on success, 1 if test error, 2 if internal error"; static bool s_verbose = false; bool verbose() { return s_verbose; } void set_verbose(bool v) { s_verbose = v; } static int s_failures = 0; bool failures() { return s_failures; } void incr_failures() { s_failures++; } #include "xorp_tests.hh" /** * Print program info to output stream. * * @param stream the output stream the print the program info to. */ static void print_program_info(FILE *stream) { fprintf(stream, "Name: %s\n", program_name); fprintf(stream, "Description: %s\n", program_description); fprintf(stream, "Version: %s\n", program_version_id); fprintf(stream, "Date: %s\n", program_date); fprintf(stream, "Copyright: %s\n", program_copyright); fprintf(stream, "Return: %s\n", program_return_value); } /** * Print program usage information to the stderr. * * @param progname the name of the program. */ static void usage(const char* progname) { print_program_info(stderr); fprintf(stderr, "usage: %s [-v] [-h]\n", progname); fprintf(stderr, " -h : usage (this message)\n"); fprintf(stderr, " -v : verbose output\n"); fprintf(stderr, "Return 0 on success, 1 if test error, 2 if internal error.\n"); } /** * Test creating a temporary file. */ static void test_xorp_make_temporary_file() { string tmp_dir, filename_template, final_filename, errmsg; tmp_dir = ""; // Use default directory filename_template = "test_xorp_make_temporary_file"; FILE* fp = xorp_make_temporary_file(tmp_dir, filename_template, final_filename, errmsg); verbose_assert(fp != NULL, "Creating a temporary file"); // // Cleanup // if (fp != NULL) { // Close and unlink the temporary file fclose(fp); #ifdef HOST_OS_WINDOWS DeleteFileA(final_filename.c_str()); #else unlink(final_filename.c_str()); #endif } } int main(int argc, char * const argv[]) { int ret_value = 0; // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); int ch; while ((ch = getopt(argc, argv, "hv")) != -1) { switch (ch) { case 'v': set_verbose(true); break; case 'h': case '?': default: usage(argv[0]); xlog_stop(); xlog_exit(); if (ch == 'h') return (0); else return (1); } } argc -= optind; argv += optind; XorpUnexpectedHandler x(xorp_unexpected_handler); try { test_xorp_make_temporary_file(); ret_value = failures() ? 1 : 0; } catch (...) { // Internal error xorp_print_standard_exceptions(); ret_value = 2; } // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); return (ret_value); } xorp/libxorp/tests/test_time_slice.cc0000664000076400007640000001555611540224230020171 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "libxorp_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/eventloop.hh" #include "libxorp/exceptions.hh" #ifdef HAVE_GETOPT_H #include #endif #include "time_slice.hh" // // XXX: MODIFY FOR YOUR TEST PROGRAM // static const char *program_name = "test_time_slice"; static const char *program_description = "Test TimeSlice class"; static const char *program_version_id = "0.1"; static const char *program_date = "December 4, 2002"; static const char *program_copyright = "See file LICENSE"; static const char *program_return_value = "0 on success, 1 if test error, 2 if internal error"; static bool s_verbose = false; bool verbose() { return s_verbose; } void set_verbose(bool v) { s_verbose = v; } static int s_failures = 0; bool failures() { return (s_failures)? (true) : (false); } void incr_failures() { s_failures++; } void reset_failures() { s_failures = 0; } #include "libxorp/xorp_tests.hh" /** * Print program info to output stream. * * @param stream the output stream the print the program info to. */ static void print_program_info(FILE *stream) { fprintf(stream, "Name: %s\n", program_name); fprintf(stream, "Description: %s\n", program_description); fprintf(stream, "Version: %s\n", program_version_id); fprintf(stream, "Date: %s\n", program_date); fprintf(stream, "Copyright: %s\n", program_copyright); fprintf(stream, "Return: %s\n", program_return_value); } /** * Print program usage information to the stderr. * * @param progname the name of the program. */ static void usage(const char* progname) { print_program_info(stderr); fprintf(stderr, "usage: %s [-v] [-h]\n", progname); fprintf(stderr, " -h : usage (this message)\n"); fprintf(stderr, " -v : verbose output\n"); } /** * Test TimeSlice valid constructors. */ void test_time_slice_valid_constructors() { // // Constructor for a given time limit and test frequency. // TimeSlice time_slice(10, 20); UNUSED(time_slice); } /** * Test TimeSlice invalid constructors. */ void test_time_slice_invalid_constructors() { // // Currently, there are no TimeSlice invalid constructors. // } /** * Test TimeSlice operators. */ void test_time_slice_operators() { // // Currently, there are no TimeSlice operators. // } /** * Supporting function: sleep for a given number of seconds. */ void slow_function(unsigned int sleep_seconds) { TimerList::system_sleep(TimeVal(sleep_seconds, 0)); } /** * Supporting function: sleep for a given number of microseconds. */ void fast_function(unsigned int sleep_microseconds) { TimerList::system_sleep(TimeVal(0, sleep_microseconds)); } /** * Test TimeSlice operations. */ void test_time_slice_operations() { unsigned int sec; unsigned int usec; unsigned int i, iter; // // Test single slow function. // verbose_log("TEST 'SINGLE SLOW_FUNCTION' BEGIN:\n"); verbose_log("Begin time = %s\n", xlog_localtime2string()); TimeSlice time_slice1(2000000, 1); // 2s, test every 1 iter sec = 3; verbose_log("Running slow function for %d seconds...\n", sec); slow_function(sec); verbose_log("End time = %s\n", xlog_localtime2string()); verbose_assert(time_slice1.is_expired(), "is_expired() for a single slow function"); verbose_log("TEST 'SINGLE SLOW_FUNCTION' END:\n\n"); // // Test single fast function. // verbose_log("TEST 'SINGLE FAST_FUNCTION' BEGIN:\n"); verbose_log("Begin time = %s\n", xlog_localtime2string()); TimeSlice time_slice2(2000000, 1); // 2s, test every 1 iter usec = 3; verbose_log("Running fast function for %d microseconds...\n", usec); fast_function(usec); verbose_log("End time = %s\n", xlog_localtime2string()); verbose_assert(! time_slice2.is_expired(), "is_expired() for a single fast function"); verbose_log("TEST 'SINGLE FAST_FUNCTION' END:\n\n"); // // Test fast function run multiple times. // verbose_log("TEST 'MULTI FAST_FUNCTION' BEGIN:\n"); verbose_log("Begin time = %s\n", xlog_localtime2string()); TimeSlice time_slice3(2000000, 10); // 2s, test every 10 iter usec = 3; iter = 1000000; verbose_log("Running fast function %d times for %d microseconds each...\n", iter, usec); bool time_expired = false; for (i = 0; i < iter; i++) { if (time_slice3.is_expired()) { time_expired = true; break; } fast_function(usec); } verbose_log("End time = %s\n", xlog_localtime2string()); verbose_assert(time_expired, c_format("is_expired() for multiple iterations of a fast function (expired after %d iterations)", i)); verbose_log("TEST 'MULTI FAST_FUNCTION' END:\n\n"); } int main(int argc, char * const argv[]) { int ret_value = 0; // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); int ch; while ((ch = getopt(argc, argv, "hv")) != -1) { switch (ch) { case 'v': set_verbose(true); break; case 'h': case '?': default: usage(argv[0]); xlog_stop(); xlog_exit(); if (ch == 'h') return (0); else return (1); } } argc -= optind; argv += optind; XorpUnexpectedHandler x(xorp_unexpected_handler); try { EventLoop eventloop; test_time_slice_valid_constructors(); test_time_slice_invalid_constructors(); test_time_slice_operators(); test_time_slice_operations(); ret_value = failures() ? 1 : 0; UNUSED(eventloop); } catch (...) { // Internal error xorp_print_standard_exceptions(); ret_value = 2; } // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); return (ret_value); } xorp/libxorp/tests/test_ipv6.cc0000664000076400007640000006352011540224227016740 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "libxorp_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/exceptions.hh" #ifdef HAVE_GETOPT_H #include #endif #include "ipv6.hh" // // XXX: MODIFY FOR YOUR TEST PROGRAM // static const char *program_name = "test_ipv6"; static const char *program_description = "Test IPv6 address class"; static const char *program_version_id = "0.1"; static const char *program_date = "December 2, 2002"; static const char *program_copyright = "See file LICENSE"; static const char *program_return_value = "0 on success, 1 if test error, 2 if internal error"; static bool s_verbose = false; bool verbose() { return s_verbose; } void set_verbose(bool v) { s_verbose = v; } static int s_failures = 0; bool failures() { return s_failures; } void incr_failures() { s_failures++; } #include "libxorp/xorp_tests.hh" /** * Print program info to output stream. * * @param stream the output stream the print the program info to. */ static void print_program_info(FILE *stream) { fprintf(stream, "Name: %s\n", program_name); fprintf(stream, "Description: %s\n", program_description); fprintf(stream, "Version: %s\n", program_version_id); fprintf(stream, "Date: %s\n", program_date); fprintf(stream, "Copyright: %s\n", program_copyright); fprintf(stream, "Return: %s\n", program_return_value); } /** * Print program usage information to the stderr. * * @param progname the name of the program. */ static void usage(const char* progname) { print_program_info(stderr); fprintf(stderr, "usage: %s [-v] [-h]\n", progname); fprintf(stderr, " -h : usage (this message)\n"); fprintf(stderr, " -v : verbose output\n"); fprintf(stderr, "Return 0 on success, 1 if test error, 2 if internal error.\n"); } /** * Test IPv6 valid constructors. */ void test_ipv6_valid_constructors() { // Test values for IPv6 address: "1234:5678:9abc:def0:fed:cba9:8765:4321" const char *addr_string = "1234:5678:9abc:def0:fed:cba9:8765:4321"; struct in6_addr in6_addr = { { { 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x0f, 0xed, 0xcb, 0xa9, 0x87, 0x65, 0x43, 0x21 } } }; uint8_t ui8[16]; uint32_t ui32[4]; memcpy(&ui8[0], &in6_addr, sizeof(in6_addr)); memcpy(&ui32[0], &in6_addr, sizeof(in6_addr)); struct sockaddr_in6 sin6; memset(&sin6, 0, sizeof(sin6)); #ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN sin6.sin6_len = sizeof(sin6); #endif sin6.sin6_family = AF_INET6; sin6.sin6_addr = in6_addr; struct sockaddr *sap = (struct sockaddr *)&sin6; struct sockaddr_storage *ssp = (struct sockaddr_storage *)&sin6; // // Default constructor. // IPv6 ip1; verbose_match(ip1.str(), "::"); // // Constructor from a string. // IPv6 ip2(addr_string); verbose_match(ip2.str(), addr_string); // // Constructor from another IPv6 address. // IPv6 ip3(ip2); verbose_match(ip3.str(), addr_string); // // Constructor from a (uint8_t *) memory pointer. // IPv6 ip4(ui8); verbose_match(ip4.str(), addr_string); // // Constructor from a (uint32_t *) memory pointer. // IPv6 ip5(ui32); verbose_match(ip5.str(), addr_string); // // Constructor from in6_addr structure. // IPv6 ip6(in6_addr); verbose_match(ip6.str(), addr_string); // // Constructor from sockaddr structure. // IPv6 ip7(*sap); verbose_match(ip7.str(), addr_string); // // Constructor from sockaddr_storage structure. // IPv6 ip8(*ssp); verbose_match(ip8.str(), addr_string); // // Constructor from sockaddr_in6 structure. // IPv6 ip9(sin6); verbose_match(ip9.str(), addr_string); } /** * Test IPv6 invalid constructors. */ void test_ipv6_invalid_constructors() { // Test values for IPv6 address: "1234:5678:9abc:def0:fed:cba9:8765:4321" struct in6_addr in6_addr = { { { 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x0f, 0xed, 0xcb, 0xa9, 0x87, 0x65, 0x43, 0x21 } } }; struct sockaddr_in6 sin6; memset(&sin6, 0, sizeof(sin6)); #ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN sin6.sin6_len = sizeof(sin6); #endif sin6.sin6_family = AF_UNSPEC; // Note: invalid IP address family sin6.sin6_addr = in6_addr; struct sockaddr *sap = (struct sockaddr *)&sin6; struct sockaddr_storage *ssp = (struct sockaddr_storage *)&sin6; // // Constructor from an invalid address string. // try { // Invalid address string: note the typo -- ';' instead of ':' // after 8765 IPv6 ip("1234:5678:9abc:def0:fed:cba9:8765;4321"); verbose_log("Cannot catch invalid IP address \"1234:5678:9abc:def0:fed:cba9:8765;4321\" : FAIL\n"); incr_failures(); UNUSED(ip); } catch (const InvalidString& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } // // Constructor from an invalid sockaddr structure. // try { IPv6 ip(*sap); verbose_log("Cannot catch invalid IP address family AF_UNSPEC : FAIL\n"); incr_failures(); UNUSED(ip); } catch (const InvalidFamily& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } // // Constructor from an invalid sockaddr_storage structure. // try { IPv6 ip(*ssp); verbose_log("Cannot catch invalid IP address family AF_UNSPEC : FAIL\n"); incr_failures(); UNUSED(ip); } catch (const InvalidFamily& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } // // Constructor from an invalid sockaddr_in6 structure. // try { IPv6 ip(sin6); verbose_log("Cannot catch invalid IP address family AF_UNSPEC : FAIL\n"); incr_failures(); UNUSED(ip); } catch (const InvalidFamily& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } } /** * Test IPv6 valid copy in/out methods. */ void test_ipv6_valid_copy_in_out() { // Test values for IPv6 address: "1234:5678:9abc:def0:fed:cba9:8765:4321" const char *addr_string6 = "1234:5678:9abc:def0:fed:cba9:8765:4321"; struct in6_addr in6_addr = { { { 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x0f, 0xed, 0xcb, 0xa9, 0x87, 0x65, 0x43, 0x21 } } }; uint8_t ui8[16]; uint32_t ui32[4]; memcpy(&ui8[0], &in6_addr, sizeof(in6_addr)); memcpy(&ui32[0], &in6_addr, sizeof(in6_addr)); struct sockaddr_in6 sin6; memset(&sin6, 0, sizeof(sin6)); #ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN sin6.sin6_len = sizeof(sin6); #endif sin6.sin6_family = AF_INET6; sin6.sin6_addr = in6_addr; struct sockaddr *sap; struct sockaddr_storage *ssp; // // Copy the IPv6 raw address to specified memory location. // IPv6 ip2(addr_string6); uint8_t ip2_uint8[16]; verbose_assert(ip2.copy_out(&ip2_uint8[0]) == 16, "copy_out(uint8_t *) for IPv6 address"); verbose_assert(memcmp(&ui8[0], &ip2_uint8[0], 16) == 0, "compare copy_out(uint8_t *) for IPv6 address"); // // Copy the IPv6 raw address to an in6_addr structure. // IPv6 ip4(addr_string6); struct in6_addr ip4_in6_addr; verbose_assert(ip4.copy_out(ip4_in6_addr) == 16, "copy_out(in6_addr&) for IPv6 address"); verbose_assert(memcmp(&in6_addr, &ip4_in6_addr, 16) == 0, "compare copy_out(in6_addr&) for IPv6 address"); // // Copy the IPv6 raw address to a sockaddr structure. // IPv6 ip6(addr_string6); struct sockaddr_in6 ip6_sockaddr_in6; sap = (struct sockaddr *)&ip6_sockaddr_in6; verbose_assert(ip6.copy_out(*sap) == 16, "copy_out(sockaddr&) for IPv6 address"); verbose_assert(memcmp(&sin6, &ip6_sockaddr_in6, sizeof(sin6)) == 0, "compare copy_out(sockaddr&) for IPv6 address"); // // Copy the IPv6 raw address to a sockaddr_storage structure. // IPv6 ip6_2(addr_string6); struct sockaddr_in6 ip6_2_sockaddr_in6; ssp = (struct sockaddr_storage *)&ip6_2_sockaddr_in6; verbose_assert(ip6_2.copy_out(*ssp) == 16, "copy_out(sockaddr_storage&) for IPv6 address"); verbose_assert(memcmp(&sin6, &ip6_2_sockaddr_in6, sizeof(sin6)) == 0, "compare copy_out(sockaddr_storage&) for IPv6 address"); // // Copy the IPv6 raw address to a sockaddr_in6 structure. // IPv6 ip10(addr_string6); struct sockaddr_in6 ip10_sockaddr_in6; verbose_assert(ip10.copy_out(ip10_sockaddr_in6) == 16, "copy_out(sockaddr_in6&) for IPv6 address"); verbose_assert(memcmp(&sin6, &ip10_sockaddr_in6, sizeof(sin6)) == 0, "compare copy_out(sockaddr_in6&) for IPv6 address"); // // Copy a raw address into IPv6 structure. // IPv6 ip12; verbose_assert(ip12.copy_in(&ui8[0]) == 16, "copy_in(uint8_t *) for IPv6 address"); verbose_match(ip12.str(), addr_string6); // // Copy a raw IPv6 address from a in6_addr structure into IPv6 structure. // IPv6 ip14; verbose_assert(ip14.copy_in(in6_addr) == 16, "copy_in(in6_addr&) for IPv6 address"); verbose_match(ip14.str(), addr_string6); // // Copy a raw address from a sockaddr structure into IPv6 structure. // IPv6 ip16; sap = (struct sockaddr *)&sin6; verbose_assert(ip16.copy_in(*sap) == 16, "copy_in(sockaddr&) for IPv6 address"); verbose_match(ip16.str(), addr_string6); // // Copy a raw address from a sockaddr_storage structure into IPv6 // structure. // IPv6 ip16_2; ssp = (struct sockaddr_storage *)&sin6; verbose_assert(ip16_2.copy_in(*ssp) == 16, "copy_in(sockaddr_storage&) for IPv6 address"); verbose_match(ip16_2.str(), addr_string6); // // Copy a raw address from a sockaddr_in6 structure into IPv6 structure. // IPv6 ip20; verbose_assert(ip20.copy_in(sin6) == 16, "copy_in(sockaddr_in6&) for IPv6 address"); verbose_match(ip20.str(), addr_string6); } /** * Test IPv6 invalid copy in/out methods. */ void test_ipv6_invalid_copy_in_out() { // Test values for IPv6 address: "1234:5678:9abc:def0:fed:cba9:8765:4321" // const char *addr_string6 = "1234:5678:9abc:def0:fed:cba9:8765:4321"; struct in6_addr in6_addr = { { { 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x0f, 0xed, 0xcb, 0xa9, 0x87, 0x65, 0x43, 0x21 } } }; uint8_t ui8[16]; uint32_t ui32[4]; memcpy(&ui8[0], &in6_addr, sizeof(in6_addr)); memcpy(&ui32[0], &in6_addr, sizeof(in6_addr)); struct sockaddr_in6 sin6; memset(&sin6, 0, sizeof(sin6)); #ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN sin6.sin6_len = sizeof(sin6); #endif sin6.sin6_family = AF_UNSPEC; // Note: invalid IP address family sin6.sin6_addr = in6_addr; struct sockaddr *sap; struct sockaddr_storage *ssp; // // Copy-in from a sockaddr structure for invalid address family. // try { IPv6 ip; sap = (struct sockaddr *)&sin6; ip.copy_in(*sap); verbose_log("Cannot catch invalid IP address family AF_UNSPEC : FAIL\n"); incr_failures(); } catch (const InvalidFamily& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } // // Copy-in from a sockaddr_storage structure for invalid address family. // try { IPv6 ip; ssp = (struct sockaddr_storage *)&sin6; ip.copy_in(*ssp); verbose_log("Cannot catch invalid IP address family AF_UNSPEC : FAIL\n"); incr_failures(); } catch (const InvalidFamily& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } // // Copy-in from a sockaddr_in6 structure for invalid address family. // try { IPv6 ip; ip.copy_in(sin6); verbose_log("Cannot catch invalid IP address family AF_UNSPEC : FAIL\n"); incr_failures(); } catch (const InvalidFamily& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } } /** * Test IPv6 operators. */ void test_ipv6_operators() { IPv6 ip_a("0000:ffff:0000:ffff:0000:ffff:0000:ffff"); IPv6 ip_b("ffff:0000:ffff:0000:ffff:0000:ffff:ffff"); IPv6 ip_not_a("ffff:0000:ffff:0000:ffff:0000:ffff:0000"); IPv6 ip_a_or_b("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"); IPv6 ip_a_and_b("::ffff"); IPv6 ip_a_xor_b("ffff:ffff:ffff:ffff:ffff:ffff:ffff:0000"); // // Equality Operator // verbose_assert(ip_a == ip_a, "operator=="); verbose_assert(!(ip_a == ip_b), "operator=="); // // Not-Equal Operator // verbose_assert(!(ip_a != ip_a), "operator!="); verbose_assert(ip_a != ip_b, "operator!="); // // Less-Than Operator // verbose_assert(ip_a < ip_b, "operator<"); // // Bitwise-Negation Operator // verbose_assert(~ip_a == ip_not_a, "operator~"); // // OR Operator // verbose_assert((ip_a | ip_b) == ip_a_or_b, "operator|"); // // AND Operator // verbose_assert((ip_a & ip_b) == ip_a_and_b, "operator&"); // // XOR Operator // verbose_assert((ip_a ^ ip_b) == ip_a_xor_b, "operator^"); // // Operator << // verbose_assert(IPv6("0000:ffff:0000:ffff:0000:ffff:0000:ffff") << 16 == IPv6("ffff:0000:ffff:0000:ffff:0000:ffff:0000"), "operator<<"); verbose_assert(IPv6("0000:ffff:0000:ffff:0000:ffff:0000:ffff") << 1 == IPv6("0001:fffe:0001:fffe:0001:fffe:0001:fffe"), "operator<<"); // // Operator >> // verbose_assert(IPv6("0000:ffff:0000:ffff:0000:ffff:0000:ffff") >> 16 == IPv6("0000:0000:ffff:0000:ffff:0000:ffff:0000"), "operator>>"); verbose_assert(IPv6("0000:ffff:0000:ffff:0000:ffff:0000:ffff") >> 1 == IPv6("0000:7fff:8000:7fff:8000:7fff:8000:7fff"), "operator>>"); // // Decrement Operator // verbose_assert(--IPv6("0000:ffff:0000:ffff:0000:ffff:0000:ffff") == IPv6("0000:ffff:0000:ffff:0000:ffff:0000:fffe"), "operator--()"); verbose_assert(--IPv6("0000:0000:0000:0000:0000:0000:0000:0000") == IPv6("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"), "operator--()"); // // Increment Operator // verbose_assert(++IPv6("0000:ffff:0000:ffff:0000:ffff:0000:ffff") == IPv6("0000:ffff:0000:ffff:0000:ffff:0001:0000"), "operator++()"); verbose_assert(++IPv6("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff") == IPv6("0000:0000:0000:0000:0000:0000:0000:0000"), "operator++()"); } /** * Test IPv6 address type. */ void test_ipv6_address_type() { IPv6 ip6_zero("::"); // Zero, not unicast IPv6 ip6_unicast1("::1"); // Unicast IPv6 ip6_unicast2("2000::1"); // Unicast IPv6 ip6_unicast3("feff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"); // Unicast IPv6 ip6_multicast1("ff00::"); // Multicast IPv6 ip6_multicast2("ffff::2:3:4"); // Multicast IPv6 ip6_multicast3("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");// Multicast // IPv6 ip6_unicast_linklocal1("fe80::2"); // Link-local unicast IPv6 ip6_multicast_interfacelocal1("ff01::1"); // Interface-local multicast IPv6 ip6_multicast_linklocal1("ff02::2"); // Link-local multicast IPv6 ip6_loopback1("::1"); // Loopback // // Test if an address is numerically zero. // verbose_assert(ip6_zero.is_zero() == true, "is_zero()"); verbose_assert(ip6_unicast1.is_zero() == false, "is_zero()"); verbose_assert(ip6_unicast2.is_zero() == false, "is_zero()"); verbose_assert(ip6_unicast3.is_zero() == false, "is_zero()"); verbose_assert(ip6_multicast1.is_zero() == false, "is_zero()"); verbose_assert(ip6_multicast2.is_zero() == false, "is_zero()"); verbose_assert(ip6_multicast3.is_zero() == false, "is_zero()"); // // Test if an address is a valid unicast address. // verbose_assert(ip6_zero.is_unicast() == false, "is_unicast()"); verbose_assert(ip6_unicast1.is_unicast() == true, "is_unicast()"); verbose_assert(ip6_unicast2.is_unicast() == true, "is_unicast()"); verbose_assert(ip6_unicast3.is_unicast() == true, "is_unicast()"); verbose_assert(ip6_multicast1.is_unicast() == false, "is_unicast()"); verbose_assert(ip6_multicast2.is_unicast() == false, "is_unicast()"); verbose_assert(ip6_multicast3.is_unicast() == false, "is_unicast()"); // // Test if an address is a valid multicast address. // verbose_assert(ip6_zero.is_multicast() == false, "is_multicast()"); verbose_assert(ip6_unicast1.is_multicast() == false, "is_multicast()"); verbose_assert(ip6_unicast2.is_multicast() == false, "is_multicast()"); verbose_assert(ip6_unicast3.is_multicast() == false, "is_multicast()"); verbose_assert(ip6_multicast1.is_multicast() == true, "is_multicast()"); verbose_assert(ip6_multicast2.is_multicast() == true, "is_multicast()"); verbose_assert(ip6_multicast3.is_multicast() == true, "is_multicast()"); // // Test if an address is a valid link-local unicast address. // verbose_assert(ip6_unicast_linklocal1.is_linklocal_unicast() == true, "is_linklocal_unicast()"); verbose_assert(ip6_unicast1.is_linklocal_unicast() == false, "is_linklocal_unicast()"); verbose_assert(ip6_unicast2.is_linklocal_unicast() == false, "is_linklocal_unicast()"); verbose_assert(ip6_unicast3.is_linklocal_unicast() == false, "is_linklocal_unicast()"); // // Test if an address is a valid interface-local multicast address. // verbose_assert(ip6_multicast_interfacelocal1.is_interfacelocal_multicast() == true, "is_interfacelocal_multicast()"); verbose_assert(ip6_multicast1.is_interfacelocal_multicast() == false, "is_interfacelocal_multicast()"); verbose_assert(ip6_multicast2.is_interfacelocal_multicast() == false, "is_interfacelocal_multicast()"); verbose_assert(ip6_multicast3.is_interfacelocal_multicast() == false, "is_interfacelocal_multicast()"); // // Test if an address is a valid link-local multicast address. // verbose_assert(ip6_multicast_linklocal1.is_linklocal_multicast() == true, "is_linklocal_multicast()"); verbose_assert(ip6_multicast1.is_linklocal_multicast() == false, "is_linklocal_multicast()"); verbose_assert(ip6_multicast2.is_linklocal_multicast() == false, "is_linklocal_multicast()"); verbose_assert(ip6_multicast3.is_linklocal_multicast() == false, "is_linklocal_multicast()"); // // Test if an address is a valid loopback address. // verbose_assert(ip6_loopback1.is_loopback() == true, "is_loopback()"); verbose_assert(ip6_zero.is_loopback() == false, "is_loopback()"); verbose_assert(ip6_unicast2.is_loopback() == false, "is_loopback()"); verbose_assert(ip6_unicast3.is_loopback() == false, "is_loopback()"); } /** * Test IPv6 address constant values. */ void test_ipv6_address_const() { // // Test the address octet-size. // verbose_assert(IPv6::addr_bytelen() == 16, "addr_bytelen()"); // // Test the address bit-length. // verbose_assert(IPv6::addr_bitlen() == 128, "addr_bitlen()"); // // Test the mask length for the multicast base address. // verbose_assert(IPv6::ip_multicast_base_address_mask_len() == 8, "ip_multicast_base_address_mask_len()"); // // Test the address family. // verbose_assert(IPv6::af() == AF_INET6, "af()"); // // Test the IP protocol version. // verbose_assert(IPv6::ip_version() == 6, "ip_version()"); verbose_assert(IPv6::ip_version_str() == "IPv6", "ip_version_str()"); // // Test pre-defined constant addresses // verbose_assert(IPv6::ZERO() == IPv6("::"), "ZERO()"); verbose_assert(IPv6::ANY() == IPv6("::"), "ANY()"); verbose_assert(IPv6::ALL_ONES() == IPv6("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"), "ALL_ONES()"); verbose_assert(IPv6::LOOPBACK() == IPv6("::1"), "LOOPBACK()"); verbose_assert(IPv6::MULTICAST_BASE() == IPv6("ff00::"), "MULTICAST_BASE()"); verbose_assert(IPv6::MULTICAST_ALL_SYSTEMS() == IPv6("ff02::1"), "MULTICAST_ALL_SYSTEMS()"); verbose_assert(IPv6::MULTICAST_ALL_ROUTERS() == IPv6("ff02::2"), "MULTICAST_ALL_ROUTERS()"); verbose_assert(IPv6::DVMRP_ROUTERS() == IPv6("ff02::4"), "DVMRP_ROUTERS()"); verbose_assert(IPv6::OSPFIGP_ROUTERS() == IPv6("ff02::5"), "OSPFIGP_ROUTERS()"); verbose_assert(IPv6::OSPFIGP_DESIGNATED_ROUTERS() == IPv6("ff02::6"), "OSPIGP_DESIGNATED_ROUTERS()"); verbose_assert(IPv6::RIP2_ROUTERS() == IPv6("ff02::9"), "RIP2_ROUTERS()"); verbose_assert(IPv6::PIM_ROUTERS() == IPv6("ff02::d"), "PIM_ROUTERS()"); verbose_assert(IPv6::SSM_ROUTERS() == IPv6("ff02::16"), "SSM_ROUTERS()"); } /** * Test IPv6 address manipulation. */ void test_ipv6_manipulate_address() { // // Test making an IPv6 mask prefix. // verbose_assert(IPv6().make_prefix(24) == IPv6("ffff:ff00::"), "make_prefix()"); verbose_assert(IPv6().make_prefix(0) == IPv6("::"), "make_prefix()"); verbose_assert(IPv6().make_prefix(128) == IPv6("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"), "make_prefix()"); // // Test making an IPv6 address prefix. // verbose_assert( IPv6("1234:5678:9abc:def0:fed:cba9:8765:4321").mask_by_prefix_len(24) == IPv6("1234:5600::"), "mask_by_prefix_len()" ); // // Test getting the prefix length of the contiguous mask. // verbose_assert(IPv6("ffff:ff00::").mask_len() == 24, "mask_len()"); // // Test getting the raw value of the address. // struct in6_addr in6_addr = { { { 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x0f, 0xed, 0xcb, 0xa9, 0x87, 0x65, 0x43, 0x21 } } }; uint32_t ui32[4]; memcpy(&ui32[0], &in6_addr, sizeof(in6_addr)); verbose_assert( memcmp(IPv6("1234:5678:9abc:def0:fed:cba9:8765:4321").addr(), ui32, sizeof(ui32)) == 0, "addr()"); // // Test setting the address value // uint8_t ui8[16]; memcpy(&ui8[0], &in6_addr, sizeof(in6_addr)); IPv6 ip_a("ffff::"); ip_a.set_addr(&ui8[0]); verbose_assert(ip_a == IPv6("1234:5678:9abc:def0:fed:cba9:8765:4321"), "set_addr()"); // // Test extracting bits from an address. // verbose_assert(IPv6("1234:5678:9abc:def0:fed:cba9:8765:4321").bits(0, 8) == 0x21, "bits()"); // // Test counting the number of bits in an address. // verbose_assert(IPv6::ZERO().bit_count() == 0, "bit_count()"); verbose_assert(IPv6::ALL_ONES().bit_count() == 128, "bit_count()"); verbose_assert(IPv6("f00f:0ff0:f00f:0ff0:f00f:0ff0:f00f:0ff0").bit_count() == 64, "bit_count()"); // // Test counting the number of leading zeroes in an address. // verbose_assert(IPv6::ZERO().leading_zero_count() == 128, "leading_zero_count()"); verbose_assert(IPv6::ALL_ONES().leading_zero_count() == 0, "leading_zero_count()"); verbose_assert(IPv6("0000:0000:0000:0001:ffff:ffff:ffff:ffff").leading_zero_count() == 63, "leading_zero_count()"); } /** * Test IPv6 invalid address manipulation. */ void test_ipv6_invalid_manipulate_address() { const char *addr_string6 = "1234:5678:9abc:def0:fed:cba9:8765:4321"; // // Test making an invalid IPv6 mask prefix. // try { // Invalid prefix length IPv6 ip(IPv6::make_prefix(IPv6::addr_bitlen() + 1)); verbose_log("Cannot catch invalid IPv6 mask prefix with length %u : FAIL\n", XORP_UINT_CAST(IPv6::addr_bitlen() + 1)); incr_failures(); UNUSED(ip); } catch (const InvalidNetmaskLength& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } // // Test masking with an invalid IPv6 mask prefix. // try { // Invalid mask prefix IPv6 ip(addr_string6); ip.mask_by_prefix_len(IPv6::addr_bitlen() + 1); verbose_log("Cannot catch masking with an invalid IPv6 mask prefix with length %u : FAIL\n", XORP_UINT_CAST(IPv6::addr_bitlen() + 1)); incr_failures(); } catch (const InvalidNetmaskLength& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } } int main(int argc, char * const argv[]) { int ret_value = 0; // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); int ch; while ((ch = getopt(argc, argv, "hv")) != -1) { switch (ch) { case 'v': set_verbose(true); break; case 'h': case '?': default: usage(argv[0]); xlog_stop(); xlog_exit(); if (ch == 'h') return (0); else return (1); } } argc -= optind; argv += optind; XorpUnexpectedHandler x(xorp_unexpected_handler); try { test_ipv6_valid_constructors(); test_ipv6_invalid_constructors(); test_ipv6_valid_copy_in_out(); test_ipv6_invalid_copy_in_out(); test_ipv6_operators(); test_ipv6_address_type(); test_ipv6_address_const(); test_ipv6_manipulate_address(); test_ipv6_invalid_manipulate_address(); ret_value = failures() ? 1 : 0; } catch (...) { // Internal error xorp_print_standard_exceptions(); ret_value = 2; } // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); return (ret_value); } xorp/libxorp/tests/test_profile.cc0000664000076400007640000001473211540224230017507 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "libxorp_module.h" #include "xorp.h" #include "debug.h" #include "xlog.h" #include "test_main.hh" #include "exceptions.hh" #include "profile.hh" #include "clock.hh" #include "timeval.hh" #include "timer.hh" #ifndef DEBUG_LOGGING #define DEBUG_LOGGING #endif /* DEBUG_LOGGING */ #ifndef DEBUG_PRINT_FUNCTION_NAME #define DEBUG_PRINT_FUNCTION_NAME #endif /* DEBUG_PRINT_FUNCTION_NAME */ bool test1(TestInfo& info) { Profile p; string ar = "add_route"; p.create(ar); // It shouldn't be possible to create this variable again. try { p.create(ar); DOUT(info) << "Create variable twice!!! " << ar << endl; return false; } catch(PVariableExists& p) { DOUT(info) << "Exception " << p.str() << endl; } catch(...) { DOUT(info) << "Unknown Exception\n"; return false; } // Enable this variable. // XXX - This should enable global profiling. p.enable(ar); // Make sure that this variable is not enabled. string bogus = "bogus"; // Test for an unknown variable. try { if (p.enabled(bogus)) { DOUT(info) << "Testing for a bogus variable succeeded " << bogus << endl; return false; } return false; } catch(PVariableUnknown& p) { DOUT(info) << "Exception " << p.str() << endl; } catch(...) { DOUT(info) << "Unknown Exception\n"; return false; } // Disable this variable. // XXX - This should disable global profiling. p.disable(ar); // Testing for a bogus variable will return false, but no // exception will be thrown as global profiling is now disabled. if (p.enabled(bogus)) { DOUT(info) << "Testing for a bogus variable succeeded " << bogus << endl; return false; } // Try and log to a bogus variable. try { p.log(bogus, c_format("wow")); DOUT(info) << "Testing for a bogus variable succeeded " << bogus << endl; return false; } catch(PVariableUnknown& p) { DOUT(info) << "Exception " << p.str() << endl; } catch(...) { DOUT(info) << "Unknown Exception\n"; return false; } // Try and log to a valid variable that is not enabled. try { p.log(ar, c_format("wow")); DOUT(info) << "Logging to a valid disabled variable worked!!! " << ar << endl; return false; } catch(PVariableNotEnabled& p) { DOUT(info) << "Exception " << p.str() << endl; } catch(...) { DOUT(info) << "Unknown Exception\n"; return false; } // Enable the variable for logging. p.enable(ar); string message = "wow"; // Logging should succeed now. try { p.log(ar, message); DOUT(info) << "Logging succeeded " << ar << endl; } catch(PVariableNotEnabled& p) { DOUT(info) << "Exception " << p.str() << endl; } catch(...) { DOUT(info) << "Unknown Exception\n"; return false; } // Disable the logging. p.disable(ar); // Lock the log. p.lock_log(ar); ProfileLogEntry ple; if (!p.read_log(ar, ple)) { DOUT(info) << "There should be one entry\n"; return false; } if (ple.loginfo() != message) { DOUT(info) << "Expected " << message << " got " << ple.loginfo() <::RefPtr cb; } tests[] = { {"test1", callback(test1)}, {"test2", callback(test2)}, {"test3", callback(test3)}, {"test4", callback(test4)}, }; try { if (test.empty()) { for (size_t i = 0; i < sizeof(tests) / sizeof(struct test); i++) t.run(tests[i].test_name, tests[i].cb); } else { for (size_t i = 0; i < sizeof(tests) / sizeof(struct test); i++) if (test == tests[i].test_name) { t.run(tests[i].test_name, tests[i].cb); return t.exit(); } t.failed("No test with name " + test + " found\n"); } } catch(...) { xorp_catch_standard_exceptions(); } xlog_stop(); xlog_exit(); return t.exit(); } xorp/libxorp/tests/test_test_main.cc0000664000076400007640000000725311421137511020035 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "libxorp_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/exceptions.hh" #include "test_main.hh" bool test1(TestInfo& info) { DOUT(info) << "verbose on\n"; return true; } bool test2(TestInfo& info) { DOUT(info) << "verbose on level = " << info.verbose_level() << endl; int level = 100; DOUT_LEVEL(info, level) << "debugging level >= " << level << endl; return true; } bool test3(TestInfo& info, bool fail) { DOUT(info) << info.test_name() << " Test will " << (fail ? "fail" : "succeed") << endl; if (fail) return false; return true; } bool test4(TestInfo& info, const char *mess) { DOUT(info) << "verbose on level = " << info.verbose_level() << " message = " << mess << endl; return true; } bool test5(TestInfo& info, bool exception) { DOUT(info) << info.test_name() << " Test will " << (exception ? "throw exception" : "succeed") << endl; if (exception) xorp_throw(InvalidString, "Hello"); return true; } bool test6(TestInfo& info, bool exception) { DOUT(info) << info.test_name() << " Test will " << (exception ? "throw exception" : "succeed") << endl; if (exception) throw("Unexpected exception"); return true; } int main(int argc, char **argv) { XorpUnexpectedHandler x(xorp_unexpected_handler); xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); TestMain t(argc, argv); string test = t.get_optional_args("-t", "--test", "run only the specified test"); bool fail = t.get_optional_flag("-f", "--fail", "fail test3"); string mess = t.get_optional_args("-m", "--message", "pass message to test4"); bool exception = t.get_optional_flag("-e", "--exception", "throw xorp exception test5"); bool unexpected = t.get_optional_flag("-u", "--unexpected", "throw unexpected exception test6"); t.complete_args_parsing(); struct test { string test_name; XorpCallback1::RefPtr cb; } tests[] = { {"test1", callback(test1)}, {"test2", callback(test2)}, {"test3", callback(test3, fail)}, {"test4", callback(test4, mess.c_str())}, {"test5", callback(test5, exception)}, {"test6", callback(test6, unexpected)}, }; try { if ("" == test) { for (size_t i = 0; i < sizeof(tests) / sizeof(struct test); i++) t.run(tests[i].test_name, tests[i].cb); } else { for (size_t i = 0; i < sizeof(tests) / sizeof(struct test); i++) if (test == tests[i].test_name) { t.run(tests[i].test_name, tests[i].cb); return t.exit(); } t.failed("No test with name " + test + " found\n"); } } catch(...) { xorp_catch_standard_exceptions(); } xlog_stop(); xlog_exit(); return t.exit(); } xorp/libxorp/tests/test_run_command.cc0000664000076400007640000005701311540224230020350 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "libxorp_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/exceptions.hh" #ifdef HAVE_GETOPT_H #include #endif #include "run_command.hh" #ifdef HOST_OS_WINDOWS #define MSYS_DEFAULT_ROOT "C:\\MinGW" #define SLEEP_PATH "\\bin\\sleep.exe" #define AWK_PATH "\\bin\\gawk.exe" // Invocation via shell needs UNIX style path #define AWK_VIA_SHELL_PATH "/bin/gawk" #else #define SLEEP_PATH "/bin/sleep" string AWK_PATH("/usr/bin/awk"); string& AWK_VIA_SHELL_PATH = AWK_PATH; #endif static string cmdroot = ""; #define SLEEP_ARGUMENT "10000" // // XXX: MODIFY FOR YOUR TEST PROGRAM // static const char *program_name = "test_run_command"; static const char *program_description = "Test a class for running an external program"; static const char *program_version_id = "0.1"; static const char *program_date = "November 22, 2004"; static const char *program_copyright = "See file LICENSE"; static const char *program_return_value = "0 on success, 1 if test error, 2 if internal error"; static bool s_verbose = false; bool verbose() { return s_verbose; } void set_verbose(bool v) { s_verbose = v; } static int s_failures = 0; bool failures() { return (s_failures)? (true) : (false); } void incr_failures() { s_failures++; } void reset_failures() { s_failures = 0; } #include "libxorp/xorp_tests.hh" /** * Print program info to output stream. * * @param stream the output stream the print the program info to. */ static void print_program_info(FILE *stream) { fprintf(stream, "Name: %s\n", program_name); fprintf(stream, "Description: %s\n", program_description); fprintf(stream, "Version: %s\n", program_version_id); fprintf(stream, "Date: %s\n", program_date); fprintf(stream, "Copyright: %s\n", program_copyright); fprintf(stream, "Return: %s\n", program_return_value); } /** * Print program usage information to the stderr. * * @param progname the name of the program. */ static void usage(const char* progname) { print_program_info(stderr); fprintf(stderr, "usage: %s [-v] [-h]\n", progname); fprintf(stderr, " -h : usage (this message)\n"); fprintf(stderr, " -v : verbose output\n"); fprintf(stderr, "Return 0 on success, 1 if test error, 2 if internal error.\n"); } // // Local state // static bool is_interrupted = false; static void signal_handler(int sig) { is_interrupted = true; UNUSED(sig); } /** * @short A class used for testing of @see RunCommand. */ class TestRunCommand { public: TestRunCommand() { clear(); } void clear() { _is_stdout_received = false; _is_stderr_received = false; _is_done_received = false; _is_done_failed = false; _stdout_msg.erase(); _stderr_msg.erase(); _done_error_msg.erase(); } bool is_stdout_received() const { return _is_stdout_received; } bool is_stderr_received() const { return _is_stderr_received; } bool is_done_received() const { return _is_done_received; } bool is_done_failed() const { return _is_done_failed; } const string& stdout_msg() const { return _stdout_msg; } const string& stderr_msg() const { return _stderr_msg; } const string& done_error_msg() const { return _done_error_msg; } void command_stdout_cb(RunCommand* run_command, const string& output) { private_stdout_cb(output); UNUSED(run_command); } void command_stderr_cb(RunCommand* run_command, const string& output) { private_stderr_cb(output); UNUSED(run_command); } void command_done_cb(RunCommand* run_command, bool success, const string& error_msg) { private_done_cb(success, error_msg); UNUSED(run_command); } void shell_command_stdout_cb(RunShellCommand* run_command, const string& output) { private_stdout_cb(output); UNUSED(run_command); } void shell_command_stderr_cb(RunShellCommand* run_command, const string& output) { private_stderr_cb(output); UNUSED(run_command); } void shell_command_done_cb(RunShellCommand* run_command, bool success, const string& error_msg) { private_done_cb(success, error_msg); UNUSED(run_command); } private: void private_stdout_cb(const string& output) { _is_stdout_received = true; _stdout_msg += output; } void private_stderr_cb(const string& output) { _is_stderr_received = true; _stderr_msg += output; } void private_done_cb(bool success, const string& error_msg) { _is_done_received = true; if (! success) { _is_done_failed = !success; _done_error_msg += error_msg; } } bool _is_stdout_received; bool _is_stderr_received; bool _is_done_received; bool _is_done_failed; string _stdout_msg; string _stderr_msg; string _done_error_msg; }; /** * Test RunCommand executing an invalid command. */ static void test_execute_invalid_command() { EventLoop eventloop; TestRunCommand test_run_command; bool done = false; // // Try to start an invalid command. // list argument_list; argument_list.push_back("-no-such-flags"); argument_list.push_back("-more-bogus-flags"); RunCommand run_command(eventloop, "/no/such/command", argument_list, callback(test_run_command, &TestRunCommand::command_stdout_cb), callback(test_run_command, &TestRunCommand::command_stderr_cb), callback(test_run_command, &TestRunCommand::command_done_cb), false /* redirect_stderr_to_stdout */); int ret_value = run_command.execute(); if (ret_value != XORP_OK) { verbose_assert(true, "Executing an invalid command"); return; } XorpTimer timeout_timer = eventloop.set_flag_after(TimeVal(10, 0), &done, true); while (!done) { if (is_interrupted || test_run_command.is_done_received()) { break; } eventloop.run(); } if (is_interrupted) { verbose_log("Command interrupted by user\n"); incr_failures(); run_command.terminate(); return; } verbose_assert(test_run_command.is_done_received() && test_run_command.is_done_failed(), "Executing an invalid command"); run_command.terminate(); } /** * Test RunCommand executing a command with invalid arguments. */ static void test_execute_invalid_arguments() { EventLoop eventloop; TestRunCommand test_run_command; bool done = false; // // Try to start an invalid command. // list argument_list; argument_list.push_back("-no-such-flags"); argument_list.push_back("-more-bogus-flags"); RunCommand run_command(eventloop, cmdroot + SLEEP_PATH, argument_list, callback(test_run_command, &TestRunCommand::command_stdout_cb), callback(test_run_command, &TestRunCommand::command_stderr_cb), callback(test_run_command, &TestRunCommand::command_done_cb), false /* redirect_stderr_to_stdout */); int ret_value = run_command.execute(); if (ret_value != XORP_OK) { verbose_assert(true, "Executing a command with invalid arguments"); return; } XorpTimer timeout_timer = eventloop.set_flag_after(TimeVal(10, 0), &done, true); while (!done) { if (is_interrupted || test_run_command.is_done_received()) { break; } eventloop.run(); } if (is_interrupted) { verbose_log("Command interrupted by user\n"); incr_failures(); run_command.terminate(); return; } verbose_assert(test_run_command.is_done_received() && test_run_command.is_done_failed(), "Executing a command with invalid arguments"); run_command.terminate(); } /** * Test RunCommand executing and terminating a command. */ static void test_execute_terminate_command() { EventLoop eventloop; TestRunCommand test_run_command; bool done = false; // // Start a sleep(1) command that should not terminate anytime soon // list argument_list; argument_list.push_back(SLEEP_ARGUMENT); RunCommand run_command(eventloop, cmdroot + SLEEP_PATH, argument_list, callback(test_run_command, &TestRunCommand::command_stdout_cb), callback(test_run_command, &TestRunCommand::command_stderr_cb), callback(test_run_command, &TestRunCommand::command_done_cb), false /* redirect_stderr_to_stdout */); if (run_command.execute() != XORP_OK) { verbose_log("Cannot execute command %s : FAIL\n", run_command.command().c_str()); incr_failures(); return; } XorpTimer timeout_timer = eventloop.set_flag_after(TimeVal(10, 0), &done, true); while (!done) { if (is_interrupted || test_run_command.is_stdout_received() || test_run_command.is_stderr_received() || test_run_command.is_done_received()) { break; } eventloop.run(); } if (is_interrupted) { verbose_log("Command interrupted by user\n"); incr_failures(); run_command.terminate(); return; } if (test_run_command.is_stdout_received()) { verbose_log("Unexpected stdout output (%s) : FAIL\n", test_run_command.stdout_msg().c_str()); incr_failures(); run_command.terminate(); return; } if (test_run_command.is_stderr_received()) { verbose_log("Unexpected stderr output (%s) : FAIL\n", test_run_command.stderr_msg().c_str()); incr_failures(); run_command.terminate(); return; } if (test_run_command.is_done_received()) { verbose_log("Unexpected command termination : FAIL\n"); incr_failures(); run_command.terminate(); return; } // // Terminate the command and test the termination succeeded // run_command.terminate(); done = false; timeout_timer = eventloop.set_flag_after(TimeVal(10, 0), &done, true); while (!done) { if (is_interrupted || test_run_command.is_done_received()) { break; } eventloop.run(); } if (is_interrupted) { verbose_log("Command interrupted by user\n"); incr_failures(); run_command.terminate(); return; } bool success = false; do { if (! test_run_command.is_done_received()) { verbose_log("Command failed, done not received\n"); break; } success = true; break; } while (false); verbose_assert(success, "Executing and terminating a command"); } /** * Test RunCommand command stdout reading. */ static void test_command_stdout_reading() { EventLoop eventloop; TestRunCommand test_run_command; bool done = false; string stdout_msg_in = "Line1 on stdout"; string stdout_msg_out = stdout_msg_in; // // Try to start an invalid command. // string awk_script = c_format("BEGIN { " "printf(\"%s\") > \"/dev/stdout\"; " "exit 0;}", stdout_msg_in.c_str()); list argument_list; argument_list.push_back(awk_script); RunCommand run_command(eventloop, cmdroot + AWK_PATH, argument_list, callback(test_run_command, &TestRunCommand::command_stdout_cb), callback(test_run_command, &TestRunCommand::command_stderr_cb), callback(test_run_command, &TestRunCommand::command_done_cb), false /* redirect_stderr_to_stdout */); int ret_value = run_command.execute(); if (ret_value != XORP_OK) { verbose_assert(false, "Command stdout reading"); return; } XorpTimer timeout_timer = eventloop.set_flag_after(TimeVal(10, 0), &done, true); while (!done) { if (is_interrupted || test_run_command.is_done_received()) { break; } eventloop.run(); } if (is_interrupted) { verbose_log("Command interrupted by user\n"); incr_failures(); run_command.terminate(); return; } bool success = false; do { if (! test_run_command.is_done_received()) { verbose_log("Command failed, done not received\n"); break; } if (test_run_command.is_done_failed()) { verbose_log("Command failed, done failed\n"); break; } if (test_run_command.is_stderr_received()) { verbose_log("Command failed, unexpected stderr output\n"); break; } if (! test_run_command.is_stdout_received()) { verbose_log("Command failed, no stdout output\n"); break; } if (test_run_command.stdout_msg() != stdout_msg_out) { verbose_log("Command failed, stdout output mismatch\n"); break; } success = true; break; } while (false); verbose_assert(success, "Command stdout reading"); run_command.terminate(); } /** * Test RunShellCommand command stdout reading. */ static void test_shell_command_stdout_reading() { EventLoop eventloop; TestRunCommand test_run_command; bool done = false; string stdout_msg_in = "Line1 on stdout"; string stdout_msg_out = stdout_msg_in; // // Try to start an invalid command. // string awk_script = c_format("'BEGIN { " "printf(\"%s\") > \"/dev/stdout\"; " "exit 0;}'", stdout_msg_in.c_str()); RunShellCommand run_command(eventloop, AWK_VIA_SHELL_PATH, awk_script, callback(test_run_command, &TestRunCommand::shell_command_stdout_cb), callback(test_run_command, &TestRunCommand::shell_command_stderr_cb), callback(test_run_command, &TestRunCommand::shell_command_done_cb)); int ret_value = run_command.execute(); if (ret_value != XORP_OK) { verbose_assert(false, "Shell command stdout reading"); return; } XorpTimer timeout_timer = eventloop.set_flag_after(TimeVal(10, 0), &done, true); while (!done) { if (is_interrupted || test_run_command.is_done_received()) { break; } eventloop.run(); } if (is_interrupted) { verbose_log("Command interrupted by user\n"); incr_failures(); run_command.terminate(); return; } bool success = false; do { if (! test_run_command.is_done_received()) { verbose_log("Command failed, done not received\n"); break; } if (test_run_command.is_done_failed()) { verbose_log("Command failed, done failed\n"); break; } if (test_run_command.is_stderr_received()) { verbose_log("Command failed, unexpected stderr output\n"); break; } if (! test_run_command.is_stdout_received()) { verbose_log("Command failed, no stdout output\n"); break; } if (test_run_command.stdout_msg() != stdout_msg_out) { verbose_log("Command failed, stdout output mismatch\n"); break; } success = true; break; } while (false); verbose_assert(success, "Shell command stdout reading"); run_command.terminate(); } /** * Test RunCommand command stderr reading. */ static void test_command_stderr_reading() { EventLoop eventloop; TestRunCommand test_run_command; bool done = false; string stdout_msg_in = "Line1 on stdout"; string stderr_msg_in = "Line1 on stderr"; string stderr_msg_out = stderr_msg_in; // // Try to start an invalid command. // string awk_script = c_format("BEGIN { " "printf(\"%s\") > \"/dev/stdout\"; " "printf(\"%s\") > \"/dev/stderr\"; " "exit 1;}", stdout_msg_in.c_str(), stderr_msg_in.c_str()); list argument_list; argument_list.push_back(awk_script); RunCommand run_command(eventloop, cmdroot + AWK_PATH, argument_list, callback(test_run_command, &TestRunCommand::command_stdout_cb), callback(test_run_command, &TestRunCommand::command_stderr_cb), callback(test_run_command, &TestRunCommand::command_done_cb), false /* redirect_stderr_to_stdout */); int ret_value = run_command.execute(); if (ret_value != XORP_OK) { verbose_assert(false, "Command stderr reading"); return; } XorpTimer timeout_timer = eventloop.set_flag_after(TimeVal(10, 0), &done, true); while (!done) { if (is_interrupted || test_run_command.is_done_received()) { break; } eventloop.run(); } if (is_interrupted) { verbose_log("Command interrupted by user\n"); incr_failures(); run_command.terminate(); return; } bool success = false; do { if (! test_run_command.is_done_received()) { verbose_log("Command failed, done not received\n"); break; } if (! test_run_command.is_done_failed()) { verbose_log("Command failed, done did not indicate failure\n"); break; } if (! test_run_command.is_stdout_received()) { verbose_log("Command failed, unexpected stdout output\n"); break; } if (! test_run_command.is_stderr_received()) { verbose_log("Command failed, no stderr output\n"); break; } if (test_run_command.stderr_msg() != stderr_msg_out) { verbose_log("Command failed, stderr output mismatch\n"); break; } success = true; break; } while (false); verbose_assert(success, "Command stderr reading"); run_command.terminate(); } /** * Test RunCommand command redirect stderr to stdout reading. */ static void test_command_redirect_stderr_to_stdout_reading() { EventLoop eventloop; TestRunCommand test_run_command; bool done = false; string stderr_msg_in = "Line1 on stderr"; string stdout_msg_out = stderr_msg_in; // // Try to start an invalid command. // string awk_script = c_format("BEGIN { " "printf(\"%s\") > \"/dev/stderr\"; " "exit 0;}", stderr_msg_in.c_str()); list argument_list; argument_list.push_back(awk_script); RunCommand run_command(eventloop, cmdroot + AWK_PATH, argument_list, callback(test_run_command, &TestRunCommand::command_stdout_cb), callback(test_run_command, &TestRunCommand::command_stderr_cb), callback(test_run_command, &TestRunCommand::command_done_cb), true /* redirect_stderr_to_stdout */); int ret_value = run_command.execute(); if (ret_value != XORP_OK) { verbose_assert(false, "Command redirect stderr to stdout reading"); return; } XorpTimer timeout_timer = eventloop.set_flag_after(TimeVal(10, 0), &done, true); while (!done) { if (is_interrupted || test_run_command.is_done_received()) { break; } eventloop.run(); } if (is_interrupted) { verbose_log("Command interrupted by user\n"); incr_failures(); run_command.terminate(); return; } bool success = false; do { if (! test_run_command.is_done_received()) { verbose_log("Command failed, done not received\n"); break; } if (test_run_command.is_done_failed()) { verbose_log("Command failed, done failed\n"); break; } if (test_run_command.is_stderr_received()) { verbose_log("Command failed, unexpected stderr output\n"); break; } if (! test_run_command.is_stdout_received()) { verbose_log("Command failed, no stdout output\n"); break; } if (test_run_command.stdout_msg() != stdout_msg_out) { verbose_log("Command failed, stdout output mismatch\n"); break; } success = true; break; } while (false); verbose_assert(success, "Command redirect stderr to stdout reading"); run_command.terminate(); } /** * Test RunCommand command termination failure. */ static void test_command_termination_failure() { EventLoop eventloop; TestRunCommand test_run_command; bool done = false; string stdout_msg_in = "Line1 on stdout"; string stdout_msg_out = stdout_msg_in; // // Try to start an invalid command. // string awk_script = c_format("BEGIN { " "printf(\"%s\") > \"/dev/stdout\"; " "exit 1;}", stdout_msg_in.c_str()); list argument_list; argument_list.push_back(awk_script); RunCommand run_command(eventloop, cmdroot + AWK_PATH, argument_list, callback(test_run_command, &TestRunCommand::command_stdout_cb), callback(test_run_command, &TestRunCommand::command_stderr_cb), callback(test_run_command, &TestRunCommand::command_done_cb), false /* redirect_stderr_to_stdout */); int ret_value = run_command.execute(); if (ret_value != XORP_OK) { verbose_assert(false, "Command termination failure"); return; } XorpTimer timeout_timer = eventloop.set_flag_after(TimeVal(10, 0), &done, true); while (!done) { if (is_interrupted || test_run_command.is_done_received()) { break; } eventloop.run(); } if (is_interrupted) { verbose_log("Command interrupted by user\n"); incr_failures(); run_command.terminate(); return; } bool success = false; do { if (! test_run_command.is_done_received()) { verbose_log("Command failed, done not received\n"); break; } if (! test_run_command.is_done_failed()) { verbose_log("Command failed, done did not indicate failure\n"); break; } if (test_run_command.is_stderr_received()) { verbose_log("Command failed, unexpected stderr output\n"); break; } if (! test_run_command.is_stdout_received()) { verbose_log("Command failed, no stdout output\n"); break; } if (test_run_command.stdout_msg() != stdout_msg_out) { verbose_log("Command failed, stdout output mismatch\n"); break; } success = true; break; } while (false); verbose_assert(success, "Command termination failure"); run_command.terminate(); } int main(int argc, char * const argv[]) { int ret_value = 0; // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); #ifdef HOST_OS_WINDOWS if (cmdroot == "") { char *_cmdroot = getenv("MSYSROOT"); if (_cmdroot == NULL) _cmdroot = const_cast(MSYS_DEFAULT_ROOT); cmdroot = string(_cmdroot); } #else struct stat st; if (stat(AWK_PATH.c_str(), &st) == -1) AWK_PATH = "/bin/awk"; XLOG_ASSERT(stat(AWK_PATH.c_str(), &st) == 0); #endif int ch; while ((ch = getopt(argc, argv, "hv")) != -1) { switch (ch) { case 'v': set_verbose(true); break; case 'h': case '?': default: usage(argv[0]); xlog_stop(); xlog_exit(); if (ch == 'h') return (0); else return (1); } } argc -= optind; argv += optind; XorpUnexpectedHandler x(xorp_unexpected_handler); try { // // Intercept the typical signals that the user may use to stop // the program. // signal(SIGTERM, signal_handler); signal(SIGINT, signal_handler); test_execute_invalid_command(); test_execute_invalid_arguments(); test_execute_terminate_command(); test_command_stdout_reading(); test_shell_command_stdout_reading(); test_command_stderr_reading(); test_command_redirect_stderr_to_stdout_reading(); test_command_termination_failure(); ret_value = failures() ? 1 : 0; } catch (...) { // Internal error xorp_print_standard_exceptions(); ret_value = 2; } // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); return (ret_value); } xorp/libxorp/tests/test_callback.cc0000664000076400007640000000667011540224227017613 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // Callback test program // #include #include #include "xorp.h" #include "callback.hh" static bool s_verbose = false; bool verbose() { return s_verbose; } void set_verbose(bool v) { s_verbose = v; } static int s_failures = 0; bool failures() { return s_failures; } void incr_failures() { s_failures++; } #include "libxorp/xorp_tests.hh" typedef XorpCallback1::RefPtr TestCallback; class Widget { public: Widget() {} ~Widget() {} void notify(int event) { printf("Widget event %d\n", event); } void funky_notify(int event, TestCallback cb) { printf("Funky, "); cb->dispatch(event); } private: }; class SafeWidget : public CallbackSafeObject { public: SafeWidget(int* counter) : _counter(counter) {} void notify(int event) { printf("SafeWidget event %d\n", event); *_counter = *_counter + 1; } private: int* _counter; }; int main() { Widget w; TestCallback cbm; // The callback should be empty if (!cbm.is_empty()) { print_failed("callback is not empty"); return -1; } // Set the callback cbm = callback(&w, &Widget::notify); // Test that the callback is not empty if (cbm.is_empty()) { print_failed("ERROR: callback is empty"); return -1; } // Call the callback cbm->dispatch(3); // call w.notify(3); cbm = callback(&w, &Widget::funky_notify, callback(&w, &Widget::funky_notify, callback(&w, &Widget::notify) ) ); cbm->dispatch(4); cbm = callback(&w, &Widget::notify); for (int i = 0; i < 8; i++) { cbm = callback(&w, &Widget::funky_notify, cbm); } cbm->dispatch(5); // Show safe callback - only dispatches if object referred to in // callback() method exists. ie object deletion prevents further // dispatches upon object int counter = 0; const int stop_point = 5; SafeWidget* sw = new SafeWidget(&counter); cbm = callback(sw, &SafeWidget::notify); for (int i = 0; i < 10; ++i) { if (i == stop_point) delete sw; cbm->dispatch(i); } if (counter != stop_point) { print_failed("safe callback executed after object deletion"); return -1; } // Test destructor of callback correctly interacts with // CallbackSafeObject correctly. { int counter2 = 0; SafeWidget* sw = new SafeWidget(&counter2); { TestCallback cbset[10]; for (int i = 0; i < 10; i++) { cbset[i] = callback(sw, &SafeWidget::notify); } cbset[0] = 0; } delete sw; } print_passed("Callback tests"); return (0); } xorp/libxorp/tests/test_trie.cc0000664000076400007640000004332711540224230017014 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "libxorp_module.h" #include "xorp.h" #include "ipv4net.hh" #include "ipv6net.hh" #include "trie.hh" static bool s_verbose = false; bool verbose() { return s_verbose; } void set_verbose(bool v) { s_verbose = v; } static int s_failures = 0; bool failures() { return (s_failures)? (true) : (false); } void incr_failures() { s_failures++; } void reset_failures() { s_failures = 0; } #include "libxorp/xorp_tests.hh" class IPv4RouteEntry {}; class IPv6RouteEntry {}; Trie trie; Trie trie6; void test(IPv4Net test_net, IPv4RouteEntry *test_route) { printf("-----------------------------------------------\n"); printf("looking up net: %s\n", test_net.str().c_str()); Trie::iterator ti = trie.lookup_node(test_net); if (ti == trie.end()) { print_failed("no result"); trie.print(); abort(); } const IPv4RouteEntry *r = ti.payload(); if (r==test_route) { print_passed(""); } else { print_failed(""); printf("route=%p\n", r); trie.print(); abort(); } printf("-----------\n"); } void test_find(IPv4 test_addr, IPv4RouteEntry *test_route) { printf("-----------------------------------------------\n"); printf("looking up net: %s\n", test_addr.str().c_str()); Trie::iterator ti = trie.find(test_addr); if (ti == trie.end()) { print_failed("no result"); trie.print(); abort(); } const IPv4RouteEntry *r = ti.payload(); if (r==test_route) { print_passed(""); } else { print_failed(""); printf("route=%p\n", r); trie.print(); abort(); } printf("-----------\n"); } void test_less_specific(IPv4Net test_net, IPv4RouteEntry *test_route) { printf("-----------------------------------------------\n"); printf("looking up less specific for net: %s\n", test_net.str().c_str()); Trie::iterator ti = trie.find_less_specific(test_net); if (ti == trie.end()) { if (test_route == NULL) { print_passed(""); printf("-----------\n"); return; } print_failed("no result"); trie.print(); abort(); } const IPv4RouteEntry *r = ti.payload(); if (r==test_route) { print_passed(""); } else { print_failed(""); printf("route=%p\n", r); trie.print(); abort(); } printf("-----------\n"); } void test_upper_bound(IPv4 test_addr, IPv4 test_answer) { printf("-----------------------------------------------\n"); printf("looking up upper bound: %s\n", test_addr.str().c_str()); IPv4 lo, hi; trie.find_bounds(test_addr, lo, hi); IPv4 result= hi; if (test_answer == result) { print_passed(result.str()); } else { print_failed(""); printf("answer should be %s, result was %s\n", test_answer.str().c_str(), result.str().c_str()); trie.print(); abort(); } printf("-----------\n"); } void test_lower_bound(IPv4 test_addr, IPv4 test_answer) { printf("-----------------------------------------------\n"); printf("looking up lower bound: %s\n", test_addr.str().c_str()); IPv4 lo, hi; trie.find_bounds(test_addr, lo, hi); IPv4 result= lo; if (test_answer == result) { print_passed(result.str()); } else { print_failed(""); printf("Fail: answer should be %s, result was %s\n", test_answer.str().c_str(), result.str().c_str()); trie.print(); abort(); } printf("-----------\n"); } void test6(IPv6Net test_net, IPv6RouteEntry *test_route) { printf("-----------------------------------------------\n"); printf("looking up net: %s\n", test_net.str().c_str()); Trie::iterator ti = trie6.lookup_node(test_net); if (ti == trie6.end()) { print_failed("no result"); trie.print(); abort(); } const IPv6RouteEntry *r = ti.payload(); if (r==test_route) { print_passed(""); } else { print_failed(""); printf("route=%p\n", r); trie.print(); abort(); } printf("-----------\n"); } void test_find6(IPv6 test_addr, IPv6RouteEntry *test_route) { printf("-----------------------------------------------\n"); printf("looking up net: %s\n", test_addr.str().c_str()); Trie::iterator ti = trie6.find(test_addr); const IPv6RouteEntry *r = ti.payload(); if (ti == trie6.end()) { print_failed("no result"); trie.print(); abort(); } if (r==test_route) { print_passed(""); } else { print_failed(""); printf("route=%p\n", r); trie.print(); abort(); } printf("-----------\n"); } void test_upper_bound6(IPv6 test_addr, IPv6 test_answer) { printf("-----------------------------------------------\n"); printf("looking up upper bound: %s\n", test_addr.str().c_str()); IPv6 lo, hi; trie6.find_bounds(test_addr, lo, hi); IPv6 result= hi; if (test_answer == result) { print_passed(result.str()); } else { print_failed(""); printf("Fail: answer should be %s, result was %s\n", test_answer.str().c_str(), result.str().c_str()); trie.print(); abort(); } printf("-----------\n"); } void test_find_subtree() { printf("Find subtree\n"); Trie trie_subtree; IPv4Net n1(IPv4("169.229.0.136"), 29); trie_subtree.insert(n1, "169.229.0.136/29"); IPv4Net n2(IPv4("192.150.187.0"), 25); trie_subtree.insert(n2, "192.150.187.0/25"); IPv4Net n3(IPv4("192.168.254.2"), 32); trie_subtree.insert(n3, "192.168.254.2/32"); IPv4Net n4(IPv4("192.168.254.3"), 32); trie_subtree.insert(n4, "192.168.254.3/32"); trie_subtree.print(); IPv4Net nsearch1(IPv4("192.150.187.248"), 29); Trie::iterator iter; iter = trie_subtree.search_subtree(nsearch1); for( ;iter != trie_subtree.end(); iter++) { print_failed(""); printf("subtree = %s\n", iter.payload().c_str()); abort(); } IPv4Net n5(IPv4("192.150.187.248"), 29); trie_subtree.insert(n5, "192.150.187.248/29"); IPv4Net nsearch2(IPv4("192.150.187.0"), 25); trie_subtree.erase(n2); trie_subtree.print(); iter = trie_subtree.search_subtree(nsearch2); for( ;iter != trie_subtree.end(); iter++) { print_failed(""); printf("subtree = %s\n", iter.payload().c_str()); abort(); } print_passed(""); } int main() { //test that find works OK with an empty trie (ie finds nothing). IPv4 a("1.0.0.0"); Trie::iterator ti_post = trie.find(a); if (ti_post != trie.end()) { printf("FAIL\n"); abort(); } print_passed("find on empty trie found nothing"); IPv4RouteEntry d1; IPv4Net n1(IPv4("1.2.1.0"), 24); printf("adding n1: %s route: %p\n", n1.str().c_str(), &d1); trie.insert(n1, &d1); test(n1, &d1); IPv4RouteEntry d2; IPv4Net n2(IPv4("1.2.0.0"), 16); printf("\n\nadding n2: %s route: %p\n", n2.str().c_str(), &d2); trie.insert(n2, &d2); test(n1, &d1); test(n2, &d2); IPv4RouteEntry d3; IPv4Net n3(IPv4("1.2.3.0"), 24); printf("\n\nadding n3: %s route: %p\n", n3.str().c_str(), &d3); trie.insert(n3, &d3); test(n1, &d1); test(n2, &d2); test(n3, &d3); IPv4RouteEntry d4; IPv4Net n4(IPv4("1.2.128.0"), 24); printf("\n\nadding n4: %s route: %p\n", n4.str().c_str(), &d4); trie.insert(n4, &d4); test(n1, &d1); test(n2, &d2); test(n3, &d3); test(n4, &d4); IPv4RouteEntry d5; IPv4Net n5(IPv4("1.2.0.0"), 20); printf("\n\nadding n5: %s route: %p\n", n5.str().c_str(), &d5); trie.insert(n5, &d5); test(n1, &d1); test(n2, &d2); test(n3, &d3); test(n4, &d4); test(n5, &d5); trie.print(); trie.erase(n5); test(n1, &d1); test(n2, &d2); test(n3, &d3); test(n4, &d4); trie.print(); trie.erase(n1); test(n2, &d2); test(n3, &d3); test(n4, &d4); trie.print(); trie.erase(n2); test(n3, &d3); test(n4, &d4); trie.print(); trie.insert(n2, &d2); trie.erase(n3); test(n2, &d2); test(n4, &d4); trie.print(); IPv4RouteEntry d6; IPv4Net n6(IPv4("1.2.192.0"), 24); printf("\n\nadding n6: %s route: %p\n", n6.str().c_str(), &d6); trie.insert(n6, &d6); test(n2, &d2); test(n4, &d4); test(n6, &d6); test_find(IPv4("1.2.192.1"), &d6); test_find(IPv4("1.2.191.1"), &d2); trie.print(); test_upper_bound(IPv4("1.2.190.1"), IPv4("1.2.191.255")); test_lower_bound(IPv4("1.2.190.1"), IPv4("1.2.129.0")); test_upper_bound(IPv4("1.2.120.1"), IPv4("1.2.127.255")); test_lower_bound(IPv4("1.2.120.1"), IPv4("1.2.0.0")); test_upper_bound(IPv4("1.2.192.1"), IPv4("1.2.192.255")); test_lower_bound(IPv4("1.2.192.1"), IPv4("1.2.192.0")); test_upper_bound(IPv4("1.2.128.1"), IPv4("1.2.128.255")); test_lower_bound(IPv4("1.2.128.1"), IPv4("1.2.128.0")); test_upper_bound(IPv4("1.2.193.1"), IPv4("1.2.255.255")); test_lower_bound(IPv4("1.2.193.1"), IPv4("1.2.193.0")); trie.erase(n4); test_upper_bound(IPv4("1.2.128.1"), IPv4("1.2.191.255")); test_lower_bound(IPv4("1.2.128.1"), IPv4("1.2.0.0")); test_lower_bound(IPv4("1.2.190.1"), IPv4("1.2.0.0")); trie.erase(n6); test_upper_bound(IPv4("1.2.128.1"), IPv4("1.2.255.255")); test_lower_bound(IPv4("1.2.128.1"), IPv4("1.2.0.0")); trie.erase(n2); test_upper_bound(IPv4("1.2.128.1"), IPv4("255.255.255.255")); test_lower_bound(IPv4("1.2.128.1"), IPv4("0.0.0.0")); IPv6RouteEntry d7; IPv6 ip6a("fe80::2c0:4fff:fe68:8c58"); IPv6Net ip6net(ip6a, 96); trie6.insert(ip6net, &d7); test6(ip6net, &d7); test_find6(ip6a, &d7); test_upper_bound6(ip6a, IPv6("fe80::2c0:4fff:ffff:ffff")); //attempts to test remaining code paths trie.print(); trie.insert(n2, &d2); trie.insert(n3, &d3); trie.insert(n1, &d1); IPv4RouteEntry d8; IPv4Net n8(IPv4("1.2.3.0"), 23); trie.insert(n8, &d8); trie.print(); trie.erase(n8); IPv4RouteEntry d9; IPv4Net n9(IPv4("1.2.2.0"), 24); trie.insert(n9, &d9); trie.print(); trie.erase(n9); trie.erase(n2); IPv4RouteEntry d10; IPv4Net n10(IPv4("1.2.0.0"), 15); trie.insert(n10, &d10); /* 1.2.0.0/15 */ trie.print(); trie.erase(n10); trie.insert(n2, &d2); /* 1.2.0.0/16 */ trie.print(); IPv4RouteEntry d11; IPv4Net n11(IPv4("1.0.0.0"), 14); trie.insert(n11, &d11); trie.print(); trie.insert(n10, &d10); /* 1.2.0.0/15 */ trie.print(); trie.erase(n11); IPv4RouteEntry d12; IPv4Net n12(IPv4("1.3.0.0"), 17); trie.insert(n12, &d12); trie.print(); trie.erase(n2); test_find(IPv4("1.2.2.1"), &d10); test_less_specific(n1, &d10); /* 1.2.1.0/24 */ test_less_specific(n3, &d10); /* 1.2.3.0/24 */ test_less_specific(n12, &d10); /* 1.3.0.0/17 */ test_less_specific(n10, NULL); /* 1.2.0.0/15 */ test_less_specific(n4, &d10); /* 1.2.128.0/24 */ trie.print(); test_lower_bound(IPv4("1.2.128.1"), IPv4("1.2.4.0")); test_upper_bound(IPv4("1.2.0.1"), IPv4("1.2.0.255")); trie.print(); trie.erase(n10); /* 1.0.0.0/14 */ trie.insert(n11, &d11); /* 1.0.0.0/14 */ trie.print(); test_upper_bound(IPv4("1.0.0.1"), IPv4("1.2.0.255")); trie.erase(n11); test_lower_bound(IPv4("1.0.0.1"), IPv4("0.0.0.0")); test_upper_bound(IPv4("1.3.128.1"), IPv4("255.255.255.255")); test_upper_bound(IPv4("1.2.2.1"), IPv4("1.2.2.255")); trie.print(); Trie::iterator iter; IPv4Net subroot(IPv4("1.2.0.0"), 21); iter = trie.search_subtree(subroot); while (iter!=trie.end()) { printf("------\n"); iter++; } trie.erase(n3); trie.erase(n1); trie.insert(n11, &d11); trie.print(); iter = trie.search_subtree(subroot); while (iter!=trie.end()) { printf("------\n"); iter++; } trie.insert(n1, &d1); trie.erase(n1); iter = trie.search_subtree(subroot); while (iter!=trie.end()) { printf("------\n"); iter++; } trie.erase(n12); trie.insert(n1, &d1); trie.insert(n3, &d3); trie.erase(n1); iter = trie.search_subtree(subroot); while (iter!=trie.end()) { printf("------\n"); iter++; } trie.insert(n10, &d10); trie.insert(n1, &d1); trie.insert(n9, &d9); trie.print(); iter = trie.search_subtree(n10); while (iter!=trie.end()) { printf("------\n"); iter++; } trie.print(); iter = trie.search_subtree(n3); while (iter!=trie.end()) { printf("------\n"); iter++; } //------------------------------------------------------- printf("Test replacement of interior node\n"); IPv4Net n13(IPv4("1.2.2.0"), 23); IPv4RouteEntry d13; trie.insert(n13, &d13); trie.print(); //------------------------------------------------------- printf("==================\nTest of begin()\n"); trie.erase(n11); trie.erase(n10); trie.print(); //------------------------------------------------------- printf("==================\nTest of lower_bound()\n"); trie.print(); iter = trie.lower_bound(n1); /*1.2.1.0/24*/ assert(iter.key() == n1); iter = trie.lower_bound(n9); /*1.2.2.0/24*/ assert(iter.key() == n9); iter = trie.lower_bound(n3); /*1.2.3.0/24*/ assert(iter.key() == n3); iter = trie.lower_bound(n13); /*1.2.2.0/23*/ assert(iter.key() == n13); iter = trie.lower_bound(IPNet("1.2.1.128/25")); assert(iter.key() == n1); /*1.2.1.0/24*/ iter = trie.lower_bound(IPNet("1.2.4.128/25")); assert(iter == trie.end()); iter = trie.lower_bound(IPNet("1.2.1.0/23")); assert(iter.key() == n9); /*1.2.2.0/24*/ iter = trie.lower_bound(IPNet("1.0.0.0/24")); if (iter != trie.end()) printf("iter.key = %s\n", iter.key().str().c_str()); else printf("iter = end\n"); assert(iter.key() == n1); /*1.2.1.0/24*/ //------------------------------------------------------- printf("Test /32 prefix works\n"); IPv4Net n14(IPv4("9.9.9.9"), 32); IPv4RouteEntry d14; IPv4Net n15(IPv4("9.9.9.9"), 24); IPv4RouteEntry d15; trie.insert(n14, &d14); trie.insert(n15, &d15); test_find(IPv4("9.9.9.9"), &d14); test_find(IPv4("9.9.9.8"), &d15); trie.erase(n14); trie.erase(n15); printf("==================\nTest of preorder iterator\n"); // this is the list of subnets in prefix order. The test consists in // inserting a list of unsorted subnets and check that the iterator // retrieves them in the proper order. Trie > preotrie; const char * subnets[] = { "1.2.0.0/16", "1.2.0.0/20", "1.2.1.0/24", "1.2.3.0/24", "1.2.128.0/24" }; IPv4RouteEntry d16; IPv4Net n16(subnets[3]); printf("adding n16: %s route: %p\n", n16.str().c_str(), &d16); preotrie.insert(n16, &d16); IPv4RouteEntry d17; IPv4Net n17(subnets[2]); printf("adding n17: %s route: %p\n", n17.str().c_str(), &d17); preotrie.insert(n17, &d17); IPv4RouteEntry d18; IPv4Net n18(subnets[0]); printf("adding n18: %s route: %p\n", n18.str().c_str(), &d18); preotrie.insert(n18, &d18); IPv4RouteEntry d19; IPv4Net n19(subnets[4]); printf("adding n19: %s route: %p\n", n19.str().c_str(), &d19); preotrie.insert(n19, &d19); IPv4RouteEntry d20; IPv4Net n20(subnets[1]); printf("adding n20: %s route: %p\n", n20.str().c_str(), &d20); preotrie.insert(n20, &d20); //------------------------------------------------------- printf("-----------\n"); printf("Test of prefix increment (++ti)\n"); printf("-----------\n"); TriePreOrderIterator ti; int subnetidx = 0; for (ti = preotrie.begin() ; ti != preotrie.end() ; ti++) { printf("*** node: %-26s %s\n", ti.cur()->k().str().c_str(), ti.cur()->has_payload() ? "PL" : "[]"); if (strcmp(subnets[subnetidx++],ti.cur()->k().str().c_str())) { fprintf(stderr, "invalid traversal order detected!\n"); exit(1); } if (!ti.cur()->has_payload()) { fprintf(stderr, "iterator should not return empty nodes!\n"); exit(1); } } if (subnetidx != 5) { fprintf(stderr, "iterator missing nodes!\n"); exit(1); } print_passed(""); //------------------------------------------------------- printf("-----------\n"); printf("Test of postfix increment (ti++)\n"); printf("-----------\n"); subnetidx = 0; for (ti = preotrie.begin() ; ti != preotrie.end() ; ++ti) { printf("*** node: %-26s %s\n", ti.cur()->k().str().c_str(), ti.cur()->has_payload() ? "PL" : "[]"); if (strcmp(subnets[subnetidx++],ti.cur()->k().str().c_str())) { print_failed("invalid traversal order detected!"); exit(1); } if (!ti.cur()->has_payload()) { print_failed("iterator should not return empty nodes!"); exit(1); } } if (subnetidx != 5) { print_failed("iterator missing nodes!"); exit(1); } print_passed(""); test_find_subtree(); } xorp/libxorp/tests/test_ipvxnet.cc0000664000076400007640000010054211633463661017557 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "libxorp_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/exceptions.hh" #include "libxorp/timer.hh" #include "libxorp/test_main.hh" #ifdef HAVE_GETOPT_H #include #endif #include "ipvxnet.hh" // // TODO: XXX: remove after the switch to the TestMain facility is completed // #if 0 // // XXX: MODIFY FOR YOUR TEST PROGRAM // static const char *program_name = "test_ipvxnet"; static const char *program_description = "Test IPvXNet address class"; static const char *program_version_id = "0.1"; static const char *program_date = "December 2, 2002"; static const char *program_copyright = "See file LICENSE"; static const char *program_return_value = "0 on success, 1 if test error, 2 if internal error"; #endif // 0 static bool s_verbose = false; bool verbose() { return s_verbose; } void set_verbose(bool v) { s_verbose = v; } static int s_failures = 0; bool failures() { return (s_failures)? (true) : (false); } void incr_failures() { s_failures++; } void reset_failures() { s_failures = 0; } #include "libxorp/xorp_tests.hh" // // TODO: XXX: remove after the switch to the TestMain facility is completed // #if 0 /** * Print program info to output stream. * * @param stream the output stream the print the program info to. */ static void print_program_info(FILE *stream) { fprintf(stream, "Name: %s\n", program_name); fprintf(stream, "Description: %s\n", program_description); fprintf(stream, "Version: %s\n", program_version_id); fprintf(stream, "Date: %s\n", program_date); fprintf(stream, "Copyright: %s\n", program_copyright); fprintf(stream, "Return: %s\n", program_return_value); } /** * Print program usage information to the stderr. * * @param progname the name of the program. */ static void usage(const char* progname) { print_program_info(stderr); fprintf(stderr, "usage: %s [-v] [-h]\n", progname); fprintf(stderr, " -h : usage (this message)\n"); fprintf(stderr, " -v : verbose output\n"); } #endif // 0 /** * Test IPvXNet valid constructors. */ bool test_ipvxnet_valid_constructors(TestInfo& test_info) { UNUSED(test_info); // Test values for IPv4 address: "12.34.56.78" const char *addr_string4 = "12.34.56.78"; uint32_t ui = htonl((12 << 24) | (34 << 16) | (56 << 8) | 78); struct sockaddr_in sin; memset(&sin, 0, sizeof(sin)); #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN sin.sin_len = sizeof(sin); #endif sin.sin_family = AF_INET; sin.sin_addr.s_addr = ui; const char *netaddr_string4 = "12.34.56.0/24"; // Test values for IPv6 address: "1234:5678:9abc:def0:fed:cba9:8765:4321" const char *addr_string6 = "1234:5678:9abc:def0:fed:cba9:8765:4321"; struct in6_addr in6_addr = { { { 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x0f, 0xed, 0xcb, 0xa9, 0x87, 0x65, 0x43, 0x21 } } }; uint8_t ui8[16]; uint32_t ui32[4]; memcpy(&ui8[0], &in6_addr, sizeof(in6_addr)); memcpy(&ui32[0], &in6_addr, sizeof(in6_addr)); struct sockaddr_in6 sin6; memset(&sin6, 0, sizeof(sin6)); #ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN sin6.sin6_len = sizeof(sin6); #endif sin6.sin6_family = AF_INET6; sin6.sin6_addr = in6_addr; const char *netaddr_string6 = "1234:5678::/32"; // // Constructor for a specified address family: IPv4. // IPvXNet ipnet1(AF_INET); verbose_assert(ipnet1.af() == AF_INET, "Constructor for AF_INET address family"); verbose_match(ipnet1.str(), "0.0.0.0/0"); // // Constructor for a specified address family: IPv6. // IPvXNet ipnet2(AF_INET6); verbose_assert(ipnet2.af() == AF_INET6, "Constructor for AF_INET6 address family"); verbose_match(ipnet2.str(), "::/0"); // // Constructor from a given base address and a prefix length: IPv4. // IPvX ipnet3_ipvx(addr_string4); IPvXNet ipnet3(ipnet3_ipvx, 16); verbose_match(ipnet3.str(), "12.34.0.0/16"); // // Constructor from a given base address and a prefix length: IPv6. // IPvX ipnet4_ipvx(addr_string6); IPvXNet ipnet4(ipnet4_ipvx, 32); verbose_match(ipnet4.str(), "1234:5678::/32"); // // Constructor from a string: IPv4. // IPvXNet ipnet5(netaddr_string4); verbose_match(ipnet5.str(), netaddr_string4); // // Constructor from a string: IPv6. // IPvXNet ipnet6(netaddr_string6); verbose_match(ipnet6.str(), netaddr_string6); // // Constructor from another IPvXNet address: IPv4. // IPvXNet ipnet7_ipvxnet(netaddr_string4); IPvXNet ipnet7(ipnet7_ipvxnet); verbose_match(ipnet7.str(), netaddr_string4); // // Constructor from another IPvXNet address: IPv6. // IPvXNet ipnet8_ipvxnet(netaddr_string6); IPvXNet ipnet8(ipnet8_ipvxnet); verbose_match(ipnet8.str(), netaddr_string6); // // Constructor from an IPv4Net address. // IPv4Net ipnet9_ipv4net(netaddr_string4); IPvXNet ipnet9(ipnet9_ipv4net); verbose_match(ipnet9.str(), netaddr_string4); // // Constructor from an IPv6Net address. // IPv6Net ipnet10_ipv6net(netaddr_string6); IPvXNet ipnet10(ipnet10_ipv6net); verbose_match(ipnet10.str(), netaddr_string6); return (! failures()); } /** * Test IPvXNet invalid constructors. */ bool test_ipvxnet_invalid_constructors(TestInfo& test_info) { UNUSED(test_info); // // Constructor for invalid address family. // try { IPvXNet ipnet(AF_UNSPEC); verbose_log("Cannot catch invalid IP address family AF_UNSPEC : FAIL\n"); incr_failures(); UNUSED(ipnet); } catch (const InvalidFamily& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } // // Constructor for invalid prefix length: IPv4. // try { IPvX ip("12.34.56.78"); IPvXNet ipnet(ip, ip.addr_bitlen() + 1); verbose_log("Cannot catch invalid prefix length : FAIL\n"); incr_failures(); UNUSED(ipnet); } catch (const InvalidNetmaskLength& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } // // Constructor for invalid prefix length: IPv6. // try { IPvX ip("1234:5678:9abc:def0:fed:cba9:8765:4321"); IPvXNet ipnet(ip, ip.addr_bitlen() + 1); verbose_log("Cannot catch invalid prefix length : FAIL\n"); incr_failures(); UNUSED(ipnet); } catch (const InvalidNetmaskLength& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } // // Constructor from an invalid address string: IPv4. // try { // Invalid address string: note the typo -- lack of prefix length IPvXNet ipnet("12.34.56.78/"); verbose_log("Cannot catch invalid IP network address \"12.34.56.78/\" : FAIL\n"); incr_failures(); UNUSED(ipnet); } catch (const InvalidString& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } // // Constructor from an invalid address string: IPv6. // try { // Invalid address string: note the typo -- lack of prefix length IPvXNet ipnet("1234:5678::/"); verbose_log("Cannot catch invalid IP network address \"1234:5678::/\" : FAIL\n"); incr_failures(); UNUSED(ipnet); } catch (const InvalidString& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } // // Constructor from an address string with invalid prefix length: IPv4. // try { // Invalid address string: prefix length too long IPvXNet ipnet("12.34.56.78/33"); verbose_log("Cannot catch invalid IP network address \"12.34.56.78/33\" : FAIL\n"); incr_failures(); UNUSED(ipnet); } catch (const InvalidNetmaskLength& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } // // Constructor from an address string with invalid prefix length: IPv6 // try { // Invalid address string: prefix length too long IPvXNet ipnet("1234:5678::/129"); verbose_log("Cannot catch invalid IP network address \"1234:5678::/129\" : FAIL\n"); incr_failures(); UNUSED(ipnet); } catch (const InvalidNetmaskLength& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } return (! failures()); } /** * Test IPvXNet operators. */ bool test_ipvxnet_operators(TestInfo& test_info) { UNUSED(test_info); IPv4Net ipnet4_a("12.34.0.0/16"); IPv4Net ipnet4_b("12.35.0.0/16"); IPv4Net ipnet4_c("12.34.56.0/24"); IPvXNet ipnet6_a("1234:5678::/32"); IPvXNet ipnet6_b("1234:5679::/32"); IPvXNet ipnet6_c("1234:5678:9abc::/48"); // // Assignment operator // IPvXNet ipnet1(AF_INET); ipnet1 = ipnet4_a; verbose_assert(ipnet1.str() == ipnet4_a.str(), "operator="); IPvXNet ipnet2(AF_INET6); ipnet2 = ipnet6_a; verbose_assert(ipnet2.str() == ipnet6_a.str(), "operator="); // // Equality Operator // verbose_assert(ipnet4_a == ipnet4_a, "operator=="); verbose_assert(!(ipnet4_a == ipnet4_b), "operator=="); verbose_assert(!(ipnet4_a == ipnet4_c), "operator=="); verbose_assert(ipnet6_a == ipnet6_a, "operator=="); verbose_assert(!(ipnet6_a == ipnet6_b), "operator=="); verbose_assert(!(ipnet6_a == ipnet6_c), "operator=="); // // Less-Than Operator // verbose_assert((IPvXNet("12.34.0.0/16") < IPvXNet("12.34.0.0/16")) == false, "operator<"); verbose_assert((IPvXNet("12.34.0.0/16") < IPvXNet("12.34.56.0/24")) == false, "operator<"); verbose_assert((IPvXNet("12.34.0.0/16") < IPvXNet("12.0.0.0/8")) == true, "operator<"); verbose_assert((IPvXNet("12.34.0.0/16") < IPvXNet("12.35.0.0/16")) == true, "operator<"); verbose_assert((IPvXNet("1234:5678::/32") < IPvXNet("1234:5678::/32")) == false, "operator<"); verbose_assert((IPvXNet("1234:5678::/32") < IPvXNet("1234:5678:9abc::/48")) == false, "operator<"); verbose_assert((IPvXNet("1234:5678::/32") < IPvXNet("1234::/16")) == true, "operator<"); verbose_assert((IPvXNet("1234:5678::/32") < IPvXNet("1234:5679::/32")) == true, "operator<"); // // Decrement Operator // verbose_assert(--IPvXNet("12.34.0.0/16") == IPvXNet("12.33.0.0/16"), "operator--()"); verbose_assert(--IPvXNet("0.0.0.0/16") == IPvXNet("255.255.0.0/16"), "operator--()"); verbose_assert(--IPvXNet("1234:5678::/32") == IPvXNet("1234:5677::/32"), "operator--()"); verbose_assert(--IPvXNet("::/32") == IPvXNet("ffff:ffff::/32"), "operator--()"); // // Increment Operator // verbose_assert(++IPvXNet("12.34.0.0/16") == IPvXNet("12.35.0.0/16"), "operator++()"); verbose_assert(++IPvXNet("255.255.0.0/16") == IPvXNet("0.0.0.0/16"), "operator++()"); verbose_assert(++IPvXNet("1234:5678::/32") == IPvXNet("1234:5679::/32"), "operator++()"); verbose_assert(++IPvXNet("ffff:ffff::/32") == IPvXNet("::/32"), "operator++()"); // // Test if the object contains a real (non-default) value. // verbose_assert(! IPvXNet(AF_INET).is_valid(), "is_valid()"); verbose_assert(! IPvXNet("0.0.0.0/0").is_valid(), "is_valid()"); verbose_assert(IPvXNet("0.0.0.0/1").is_valid(), "is_valid()"); verbose_assert(! IPvXNet(AF_INET6).is_valid(), "is_valid()"); verbose_assert(! IPvXNet("::/0").is_valid(), "is_valid()"); verbose_assert(IPvXNet("::/1").is_valid(), "is_valid()"); return (! failures()); } /** * Test IPvXNet address type. */ bool test_ipvxnet_address_type(TestInfo& test_info) { UNUSED(test_info); IPvXNet ipnet4_default("0.0.0.0/0"); // Default route: unicast IPvXNet ipnet4_unicast1("0.0.0.0/1"); // Unicast IPvXNet ipnet4_unicast2("12.34.0.0/16"); // Unicast IPvXNet ipnet4_unicast3("128.0.0.0/2"); // Unicast IPvXNet ipnet4_unicast4("128.16.0.0/24"); // Unicast IPvXNet ipnet4_unicast5("192.0.0.0/3"); // Unicast IPvXNet ipnet4_multicast1("224.0.0.0/4"); // Multicast IPvXNet ipnet4_multicast2("224.0.0.0/24"); // Multicast IPvXNet ipnet4_multicast3("224.0.1.0/24"); // Multicast IPvXNet ipnet4_class_a1("0.0.0.0/1"); // Class A IPvXNet ipnet4_class_a2("12.34.0.0/16"); // Class A IPvXNet ipnet4_class_b1("128.0.0.0/2"); // Class B IPvXNet ipnet4_class_b2("130.2.3.0/24"); // Class B IPvXNet ipnet4_class_c1("192.0.0.0/3"); // Class C IPvXNet ipnet4_class_c2("192.2.3.4/32"); // Class C IPvXNet ipnet4_experimental1("240.0.0.0/4"); // Experimental IPvXNet ipnet4_experimental2("240.0.1.0/16"); // Experimental IPvXNet ipnet4_odd1("128.0.0.0/1"); // Odd: includes multicast IPvXNet ipnet4_odd2("192.0.0.0/2"); // Odd: includes multicast IPvXNet ipnet6_default("::/0"); // Default route: unicast IPvXNet ipnet6_unicast1("::/1"); // Unicast IPvXNet ipnet6_unicast2("1234:5678::/32"); // Unicast IPvXNet ipnet6_multicast1("ff00::/8"); // Multicast IPvXNet ipnet6_multicast2("ff00:1::/32"); // Multicast IPvXNet ipnet6_odd1("8000::/1"); // Odd: includes multicast IPvXNet ipnet6_odd2("fe00::/7"); // Odd: includes multicast // // Test if a subnet is within the unicast address range: IPv4. // verbose_assert(ipnet4_default.is_unicast() == true, "is_unicast()"); verbose_assert(ipnet4_unicast1.is_unicast() == true, "is_unicast()"); verbose_assert(ipnet4_unicast2.is_unicast() == true, "is_unicast()"); verbose_assert(ipnet4_unicast3.is_unicast() == true, "is_unicast()"); verbose_assert(ipnet4_unicast4.is_unicast() == true, "is_unicast()"); verbose_assert(ipnet4_unicast5.is_unicast() == true, "is_unicast()"); verbose_assert(ipnet4_multicast1.is_unicast() == false, "is_unicast()"); verbose_assert(ipnet4_multicast2.is_unicast() == false, "is_unicast()"); verbose_assert(ipnet4_multicast3.is_unicast() == false, "is_unicast()"); verbose_assert(ipnet4_class_a1.is_unicast() == true, "is_unicast()"); verbose_assert(ipnet4_class_a2.is_unicast() == true, "is_unicast()"); verbose_assert(ipnet4_class_b1.is_unicast() == true, "is_unicast()"); verbose_assert(ipnet4_class_b2.is_unicast() == true, "is_unicast()"); verbose_assert(ipnet4_class_c1.is_unicast() == true, "is_unicast()"); verbose_assert(ipnet4_class_c2.is_unicast() == true, "is_unicast()"); verbose_assert(ipnet4_experimental1.is_unicast() == false, "is_unicast()"); verbose_assert(ipnet4_experimental2.is_unicast() == false, "is_unicast()"); verbose_assert(ipnet4_odd1.is_unicast() == false, "is_unicast()"); verbose_assert(ipnet4_odd2.is_unicast() == false, "is_unicast()"); // // Test if a subnet is within the unicast address range: IPv6. // verbose_assert(ipnet6_default.is_unicast() == true, "is_unicast()"); verbose_assert(ipnet6_unicast1.is_unicast() == true, "is_unicast()"); verbose_assert(ipnet6_unicast2.is_unicast() == true, "is_unicast()"); verbose_assert(ipnet6_multicast1.is_unicast() == false, "is_unicast()"); verbose_assert(ipnet6_multicast2.is_unicast() == false, "is_unicast()"); verbose_assert(ipnet6_odd1.is_unicast() == false, "is_unicast()"); verbose_assert(ipnet6_odd2.is_unicast() == false, "is_unicast()"); // // Test if a subnet is within the multicast address range: IPv4. // verbose_assert(ipnet4_default.is_multicast() == false, "is_multicast()"); verbose_assert(ipnet4_unicast1.is_multicast() == false, "is_multicast()"); verbose_assert(ipnet4_unicast2.is_multicast() == false, "is_multicast()"); verbose_assert(ipnet4_unicast3.is_multicast() == false, "is_multicast()"); verbose_assert(ipnet4_unicast4.is_multicast() == false, "is_multicast()"); verbose_assert(ipnet4_unicast5.is_multicast() == false, "is_multicast()"); verbose_assert(ipnet4_multicast1.is_multicast() == true, "is_multicast()"); verbose_assert(ipnet4_multicast2.is_multicast() == true, "is_multicast()"); verbose_assert(ipnet4_multicast3.is_multicast() == true, "is_multicast()"); verbose_assert(ipnet4_class_a1.is_multicast() == false, "is_multicast()"); verbose_assert(ipnet4_class_a2.is_multicast() == false, "is_multicast()"); verbose_assert(ipnet4_class_b1.is_multicast() == false, "is_multicast()"); verbose_assert(ipnet4_class_b2.is_multicast() == false, "is_multicast()"); verbose_assert(ipnet4_class_c1.is_multicast() == false, "is_multicast()"); verbose_assert(ipnet4_class_c2.is_multicast() == false, "is_multicast()"); verbose_assert(ipnet4_experimental1.is_multicast() == false, "is_multicast()"); verbose_assert(ipnet4_experimental2.is_multicast() == false, "is_multicast()"); verbose_assert(ipnet4_odd1.is_multicast() == false, "is_multicast()"); verbose_assert(ipnet4_odd2.is_multicast() == false, "is_multicast()"); // // Test if a subnet is within the multicast address range: IPv6. // verbose_assert(ipnet6_default.is_multicast() == false, "is_multicast()"); verbose_assert(ipnet6_unicast1.is_multicast() == false, "is_multicast()"); verbose_assert(ipnet6_unicast2.is_multicast() == false, "is_multicast()"); verbose_assert(ipnet6_multicast1.is_multicast() == true, "is_multicast()"); verbose_assert(ipnet6_multicast2.is_multicast() == true, "is_multicast()"); verbose_assert(ipnet6_odd1.is_multicast() == false, "is_multicast()"); verbose_assert(ipnet6_odd2.is_multicast() == false, "is_multicast()"); // // Test if a subnet is within the experimental address range: IPv4. // // XXX: This test applies only for IPv4. verbose_assert(ipnet4_default.is_experimental() == false, "is_experimental()"); verbose_assert(ipnet4_unicast1.is_experimental() == false, "is_experimental()"); verbose_assert(ipnet4_unicast2.is_experimental() == false, "is_experimental()"); verbose_assert(ipnet4_unicast3.is_experimental() == false, "is_experimental()"); verbose_assert(ipnet4_unicast4.is_experimental() == false, "is_experimental()"); verbose_assert(ipnet4_unicast5.is_experimental() == false, "is_experimental()"); verbose_assert(ipnet4_multicast1.is_experimental() == false, "is_experimental()"); verbose_assert(ipnet4_multicast2.is_experimental() == false, "is_experimental()"); verbose_assert(ipnet4_multicast3.is_experimental() == false, "is_experimental()"); verbose_assert(ipnet4_class_a1.is_experimental() == false, "is_experimental()"); verbose_assert(ipnet4_class_a2.is_experimental() == false, "is_experimental()"); verbose_assert(ipnet4_class_b1.is_experimental() == false, "is_experimental()"); verbose_assert(ipnet4_class_b2.is_experimental() == false, "is_experimental()"); verbose_assert(ipnet4_class_c1.is_experimental() == false, "is_experimental()"); verbose_assert(ipnet4_class_c2.is_experimental() == false, "is_experimental()"); verbose_assert(ipnet4_experimental1.is_experimental() == true, "is_experimental()"); verbose_assert(ipnet4_experimental2.is_experimental() == true, "is_experimental()"); verbose_assert(ipnet4_odd1.is_experimental() == false, "is_experimental()"); verbose_assert(ipnet4_odd2.is_experimental() == false, "is_experimental()"); return (! failures()); } /** * Test IPvXNet address overlap. */ bool test_ipvxnet_address_overlap(TestInfo& test_info) { UNUSED(test_info); IPvXNet ipnet4_a("12.34.0.0/16"); IPvXNet ipnet4_b("12.35.0.0/16"); IPvXNet ipnet4_c("12.34.56.0/24"); IPvXNet ipnet4_d("12.32.0.0/16"); IPvXNet ipnet4_e("12.33.0.0/16"); IPvXNet ipnet4_f("1.2.1.0/24"); IPvXNet ipnet4_g("1.2.3.0/24"); IPvXNet ipnet6_a("1234:5678::/32"); IPvXNet ipnet6_b("1234:5679::/32"); IPvXNet ipnet6_c("1234:5678:9abc::/48"); IPvXNet ipnet6_d("1234:0020::/32"); IPvXNet ipnet6_e("1234:0021::/32"); IPvXNet ipnet6_f("0001:0002:0001::/48"); IPvXNet ipnet6_g("0001:0002:0003::/48"); // // Test if subnets overlap. // verbose_assert(ipnet4_a.is_overlap(ipnet4_b) == false, "is_overlap()"); verbose_assert(ipnet4_a.is_overlap(ipnet4_c) == true, "is_overlap()"); verbose_assert(ipnet4_c.is_overlap(ipnet4_a) == true, "is_overlap()"); verbose_assert(ipnet6_a.is_overlap(ipnet6_b) == false, "is_overlap()"); verbose_assert(ipnet6_a.is_overlap(ipnet6_c) == true, "is_overlap()"); verbose_assert(ipnet6_c.is_overlap(ipnet6_a) == true, "is_overlap()"); // // Test if a subnet contains (or is equal to) another subnet. // verbose_assert(ipnet4_a.contains(ipnet4_a) == true, "contains(IPv4Net)"); verbose_assert(ipnet4_a.contains(ipnet4_b) == false, "contains(IPv4Net)"); verbose_assert(ipnet4_a.contains(ipnet4_c) == true, "contains(IPv4Net)"); verbose_assert(ipnet4_c.contains(ipnet4_a) == false, "contains(IPv4Net)"); verbose_assert(ipnet6_a.contains(ipnet6_a) == true, "contains(IPvXNet)"); verbose_assert(ipnet6_a.contains(ipnet6_b) == false, "contains(IPvXNet)"); verbose_assert(ipnet6_a.contains(ipnet6_c) == true, "contains(IPvXNet)"); verbose_assert(ipnet6_c.contains(ipnet6_a) == false, "contains(IPvXNet)"); // // Test if an address is within a subnet. // verbose_assert(ipnet4_a.contains(ipnet4_a.masked_addr()) == true, "contains(IPv4)"); verbose_assert(ipnet4_a.contains(ipnet4_b.masked_addr()) == false, "contains(IPv4)"); verbose_assert(ipnet4_a.contains(ipnet4_c.masked_addr()) == true, "contains(IPv4)"); verbose_assert(ipnet6_a.contains(ipnet6_a.masked_addr()) == true, "contains(IPvX)"); verbose_assert(ipnet6_a.contains(ipnet6_b.masked_addr()) == false, "contains(IPvX)"); verbose_assert(ipnet6_a.contains(ipnet6_c.masked_addr()) == true, "contains(IPvX)"); // // Determine the number of the most significant bits overlapping with // another subnet. // verbose_assert(ipnet4_a.overlap(ipnet4_a) == 16, "overlap()"); verbose_assert(ipnet4_a.overlap(ipnet4_b) == 15, "overlap()"); verbose_assert(ipnet4_a.overlap(ipnet4_c) == 16, "overlap()"); verbose_assert(ipnet4_d.overlap(ipnet4_e) == 15, "overlap()"); verbose_assert(ipnet4_f.overlap(ipnet4_g) == 22, "overlap()"); verbose_assert(ipnet6_a.overlap(ipnet6_a) == 32, "overlap()"); verbose_assert(ipnet6_a.overlap(ipnet6_b) == 31, "overlap()"); verbose_assert(ipnet6_a.overlap(ipnet6_c) == 32, "overlap()"); verbose_assert(ipnet6_d.overlap(ipnet6_e) == 31, "overlap()"); verbose_assert(ipnet6_f.overlap(ipnet6_g) == 46, "overlap()"); return (! failures()); } /** * Test performance of IPvXNet address overlap. */ bool test_performance_ipvxnet_address_overlap(TestInfo& test_info) { UNUSED(test_info); IPvXNet ipnet4_a("255.0.0.0/8"); IPvXNet ipnet4_b("255.255.255.0/24"); IPv6Net ipnet6_a("ffff:ffff::/32"); IPv6Net ipnet6_b("ffff:ffff:ffff:ffff:ffff:ffff::/96"); // // Test overlapping subnets. // do { size_t i; size_t c = 0; TimeVal begin_timeval, end_timeval, delta_timeval; TimerList::system_gettimeofday(&begin_timeval); for (i = 0; i < 0xffffff; i++) { c += ipnet4_a.overlap(ipnet4_b); } TimerList::system_gettimeofday(&end_timeval); delta_timeval = end_timeval - begin_timeval; verbose_log("Execution time IPvXNet::overlap(): %s seconds\n", delta_timeval.str().c_str()); } while (false); // // Test overlapping subnets. // do { size_t i; size_t c = 0; TimeVal begin_timeval, end_timeval, delta_timeval; TimerList::system_gettimeofday(&begin_timeval); for (i = 0; i < 0xffffff; i++) { c += ipnet6_a.overlap(ipnet6_b); } TimerList::system_gettimeofday(&end_timeval); delta_timeval = end_timeval - begin_timeval; verbose_log("Execution time IPvXNet::overlap(): %s seconds\n", delta_timeval.str().c_str()); } while (false); return (! failures()); } /** * Test IPvXNet address constant values. */ bool test_ipvxnet_address_const(TestInfo& test_info) { UNUSED(test_info); IPvXNet ipnet4_a("12.34.0.0/16"); IPvXNet ipnet6_a("1234:5678::/32"); // // Test the address family. // verbose_assert(ipnet4_a.af() == AF_INET, "af()"); verbose_assert(ipnet6_a.af() == AF_INET6, "af()"); // // Get the base address: IPv4. // IPvX ip1(AF_INET); ip1 = ipnet4_a.masked_addr(); verbose_match(ip1.str(), "12.34.0.0"); // // Get the base address: IPv6. // IPvX ip2(AF_INET6); ip2 = ipnet6_a.masked_addr(); verbose_match(ip2.str(), "1234:5678::"); // // Get the prefix length: IPv4. // verbose_assert(ipnet4_a.prefix_len() == 16, "prefix_len()"); // // Get the prefix length: IPv6. // verbose_assert(ipnet6_a.prefix_len() == 32, "prefix_len()"); // // Get the network mask: IPv4. // IPvX ip3(AF_INET); ip3 = ipnet4_a.netmask(); verbose_match(ip3.str(), "255.255.0.0"); // // Get the network mask: IPv6. // IPvX ip4(AF_INET6); ip4 = ipnet6_a.netmask(); verbose_match(ip4.str(), "ffff:ffff::"); // // Return the subnet containing all multicast addresses: IPv4. // verbose_match(IPvXNet::ip_multicast_base_prefix(AF_INET).str(), "224.0.0.0/4"); // // Return the subnet containing all multicast addresses: IPv6. // verbose_match(IPvXNet::ip_multicast_base_prefix(AF_INET6).str(), "ff00::/8"); // // Return the subnet containing all Class A addresses. // // XXX: This test applies only for IPv4. verbose_match(IPvXNet::ip_class_a_base_prefix(AF_INET).str(), "0.0.0.0/1"); // // Return the subnet containing all Class B addresses. // // XXX: This test applies only for IPv4. verbose_match(IPvXNet::ip_class_b_base_prefix(AF_INET).str(), "128.0.0.0/2"); // // Return the subnet containing all Class C addresses. // // XXX: This test applies only for IPv4. verbose_match(IPvXNet::ip_class_c_base_prefix(AF_INET).str(), "192.0.0.0/3"); // // Return the subnet containing all experimental addresses. // // XXX: This test applies only for IPv4. verbose_match(IPvXNet::ip_experimental_base_prefix(AF_INET).str(), "240.0.0.0/4"); return (! failures()); } /** * Test IPvXNet address manipulation. */ bool test_ipvxnet_manipulate_address(TestInfo& test_info) { UNUSED(test_info); IPvXNet ipnet4_a("12.34.0.0/16"); IPvXNet ipnet6_a("1234:5678::/32"); // // Get the highest address within this subnet: IPv4. // verbose_match(ipnet4_a.top_addr().str(), "12.34.255.255"); // // Get the highest address within this subnet: IPv6. // verbose_match(ipnet6_a.top_addr().str(), "1234:5678:ffff:ffff:ffff:ffff:ffff:ffff"); // // Get the smallest subnet containing both subnets: IPv4. // verbose_match(IPvXNet::common_subnet(IPvXNet("12.34.1.0/24"), IPvXNet("12.34.128.0/24")).str(), "12.34.0.0/16"); // // Get the smallest subnet containing both subnets: IPv6. // verbose_match(IPvXNet::common_subnet(IPvXNet("1234:5678:1::/48"), IPvXNet("1234:5678:8000::/48")).str(), "1234:5678::/32"); return (! failures()); } /** * Test IPvXNet invalid address manipulation. */ bool test_ipvxnet_invalid_manipulate_address(TestInfo& test_info) { UNUSED(test_info); IPvXNet ipnet4_a("12.34.0.0/16"); IPvXNet ipnet6_a("1234:5678::/32"); // // Get invalid IPv4Net address. // try { IPvXNet ipnet(ipnet6_a); // Note: initialized with IPv6 address IPv4Net ipnet_ipv4; ipnet_ipv4 = ipnet.get_ipv4net(); verbose_log("Cannot catch invalid get_ipv4net() : FAIL\n"); incr_failures(); } catch (const InvalidCast& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } // // Get invalid IPv6Net address. // try { IPvXNet ipnet(ipnet4_a); // Note: initialized with IPv4 address IPv6Net ipnet_ipv6; ipnet_ipv6 = ipnet.get_ipv6net(); verbose_log("Cannot catch invalid get_ipv6net() : FAIL\n"); incr_failures(); } catch (const InvalidCast& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } // // Assign invalid IPv4Net address. // try { IPvXNet ipnet(ipnet6_a); // Note: initialized with IPv6 address IPv4Net ipnet_ipv4; ipnet.get(ipnet_ipv4); verbose_log("Cannot catch invalid get(IPv4Net& to_ipv4net) : FAIL\n"); incr_failures(); } catch (const InvalidCast& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } // // Assign invalid IPv6Net address. // try { IPvXNet ipnet(ipnet4_a); // Note: initialized with IPv4 address IPv6Net ipnet_ipv6; ipnet.get(ipnet_ipv6); verbose_log("Cannot catch invalid get(IPv6Net& to_ipv6net) : FAIL\n"); incr_failures(); } catch (const InvalidCast& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } // // Get multicast base subnet for invalid address family. // try { IPvXNet ipnet(IPvXNet::ip_multicast_base_prefix(AF_UNSPEC)); verbose_log("Cannot catch invalid IP address family AF_UNSPEC : FAIL\n"); incr_failures(); UNUSED(ipnet); } catch (const InvalidFamily& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } return (! failures()); } int main(int argc, char * const argv[]) { XorpUnexpectedHandler x(xorp_unexpected_handler); // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); TestMain test_main(argc, argv); string test = test_main.get_optional_args("-t", "--test", "run only the specified test"); test_main.complete_args_parsing(); // // TODO: XXX: a temporary glue until we complete the switch to the // TestMain facility. // if (test_main.get_verbose()) set_verbose(true); struct test { string test_name; XorpCallback1::RefPtr cb; bool run_by_default; } tests[] = { { "test_ipvxnet_valid_constructors", callback(test_ipvxnet_valid_constructors), true }, { "test_ipvxnet_invalid_constructors", callback(test_ipvxnet_invalid_constructors), true }, { "test_ipvxnet_operators", callback(test_ipvxnet_operators), true }, { "test_ipvxnet_address_type", callback(test_ipvxnet_address_type), true }, { "test_ipvxnet_address_overlap", callback(test_ipvxnet_address_overlap), true }, { "test_ipvxnet_address_const", callback(test_ipvxnet_address_const), true }, { "test_ipvxnet_manipulate_address", callback(test_ipvxnet_manipulate_address), true }, { "test_ipvxnet_invalid_manipulate_address", callback(test_ipvxnet_invalid_manipulate_address), true }, { "test_performance_ipvxnet_address_overlap", callback(test_performance_ipvxnet_address_overlap), false } }; try { if (test.empty()) { for (size_t i = 0; i < sizeof(tests) / sizeof(struct test); i++) { if (! tests[i].run_by_default) continue; reset_failures(); test_main.run(tests[i].test_name, tests[i].cb); } } else { for (size_t i = 0; i < sizeof(tests) / sizeof(struct test); i++) { if (test == tests[i].test_name) { reset_failures(); test_main.run(tests[i].test_name, tests[i].cb); return test_main.exit(); } } test_main.failed("No test with name " + test + " found\n"); } } catch (...) { xorp_print_standard_exceptions(); } // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); return test_main.exit(); } xorp/libxorp/tests/test_ipv6net.cc0000664000076400007640000003705611540224230017446 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "libxorp_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/exceptions.hh" #include "libxorp/timer.hh" #include "libxorp/test_main.hh" #ifdef HAVE_GETOPT_H #include #endif #include "ipv6net.hh" // // TODO: XXX: remove after the switch to the TestMain facility is completed // #if 0 // // XXX: MODIFY FOR YOUR TEST PROGRAM // static const char *program_name = "test_ipv6net"; static const char *program_description = "Test IPv6Net address class"; static const char *program_version_id = "0.1"; static const char *program_date = "December 2, 2002"; static const char *program_copyright = "See file LICENSE"; static const char *program_return_value = "0 on success, 1 if test error, 2 if internal error"; #endif // 0 static bool s_verbose = false; bool verbose() { return s_verbose; } void set_verbose(bool v) { s_verbose = v; } static int s_failures = 0; bool failures() { return (s_failures)? (true) : (false); } void incr_failures() { s_failures++; } void reset_failures() { s_failures = 0; } #include "libxorp/xorp_tests.hh" // // TODO: XXX: remove after the switch to the TestMain facility is completed // #if 0 /** * Print program info to output stream. * * @param stream the output stream the print the program info to. */ static void print_program_info(FILE *stream) { fprintf(stream, "Name: %s\n", program_name); fprintf(stream, "Description: %s\n", program_description); fprintf(stream, "Version: %s\n", program_version_id); fprintf(stream, "Date: %s\n", program_date); fprintf(stream, "Copyright: %s\n", program_copyright); fprintf(stream, "Return: %s\n", program_return_value); } /** * Print program usage information to the stderr. * * @param progname the name of the program. */ static void usage(const char* progname) { print_program_info(stderr); fprintf(stderr, "usage: %s [-v] [-h]\n", progname); fprintf(stderr, " -h : usage (this message)\n"); fprintf(stderr, " -v : verbose output\n"); } #endif // 0 /** * Test IPv6Net valid constructors. */ bool test_ipv6net_valid_constructors(TestInfo& test_info) { UNUSED(test_info); // Test values for IPv6 address: "1234:5678:9abc:def0:fed:cba9:8765:4321" const char *addr_string = "1234:5678:9abc:def0:fed:cba9:8765:4321"; struct in6_addr in6_addr = { { { 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x0f, 0xed, 0xcb, 0xa9, 0x87, 0x65, 0x43, 0x21 } } }; uint8_t ui8[16]; uint32_t ui32[4]; memcpy(&ui8[0], &in6_addr, sizeof(in6_addr)); memcpy(&ui32[0], &in6_addr, sizeof(in6_addr)); struct sockaddr_in6 sin6; memset(&sin6, 0, sizeof(sin6)); #ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN sin6.sin6_len = sizeof(sin6); #endif sin6.sin6_family = AF_INET6; sin6.sin6_addr = in6_addr; const char *netaddr_string = "1234:5678::/32"; // // Default constructor. // IPv6Net ipnet1; verbose_match(ipnet1.str(), "::/0"); // // Constructor from a given base address and a prefix length. // IPv6 ip2(addr_string); IPv6Net ipnet2(ip2, 32); verbose_match(ipnet2.str(), "1234:5678::/32"); // // Constructor from a string. // IPv6Net ipnet3(netaddr_string); verbose_match(ipnet3.str(), netaddr_string); // // Constructor from another IPv6Net address. // IPv6Net ipnet4(ipnet3); verbose_match(ipnet4.str(), netaddr_string); return (! failures()); } /** * Test IPv6Net invalid constructors. */ bool test_ipv6net_invalid_constructors(TestInfo& test_info) { UNUSED(test_info); // // Constructor for invalid prefix length. // try { IPv6 ip("1234:5678:9abc:def0:fed:cba9:8765:4321"); IPv6Net ipnet(ip, ip.addr_bitlen() + 1); verbose_log("Cannot catch invalid prefix length : FAIL\n"); incr_failures(); UNUSED(ipnet); } catch (const InvalidNetmaskLength& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } // // Constructor from an invalid address string. // try { // Invalid address string: note the typo -- lack of prefix length IPv6Net ipnet("1234:5678::/"); verbose_log("Cannot catch invalid IP network address \"1234:5678::/\" : FAIL\n"); incr_failures(); UNUSED(ipnet); } catch (const InvalidString& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } // // Constructor from an address string with invalid prefix length. // try { // Invalid address string: prefix length too long IPv6Net ipnet("1234:5678::/129"); verbose_log("Cannot catch invalid IP network address \"1234:5678::/129\" : FAIL\n"); incr_failures(); UNUSED(ipnet); } catch (const InvalidNetmaskLength& e) { // The problem was caught verbose_log("%s : OK\n", e.str().c_str()); } return (! failures()); } /** * Test IPv6Net operators. */ bool test_ipv6net_operators(TestInfo& test_info) { UNUSED(test_info); IPv6Net ipnet_a("1234:5678::/32"); IPv6Net ipnet_b("1234:5679::/32"); IPv6Net ipnet_c("1234:5678:9abc::/48"); // // Assignment operator // IPv6Net ipnet1; ipnet1 = ipnet_a; verbose_assert(ipnet1.str() == ipnet_a.str(), "operator="); // // Equality Operator // verbose_assert(ipnet_a == ipnet_a, "operator=="); verbose_assert(!(ipnet_a == ipnet_b), "operator=="); verbose_assert(!(ipnet_a == ipnet_c), "operator=="); // // Less-Than Operator // verbose_assert((IPv6Net("1234:5678::/32") < IPv6Net("1234:5678::/32")) == false, "operator<"); verbose_assert((IPv6Net("1234:5678::/32") < IPv6Net("1234:5678:9abc::/48")) == false, "operator<"); verbose_assert((IPv6Net("1234:5678::/32") < IPv6Net("1234::/16")) == true, "operator<"); verbose_assert((IPv6Net("1234:5678::/32") < IPv6Net("1234:5679::/32")) == true, "operator<"); // // Decrement Operator // verbose_assert(--IPv6Net("1234:5678::/32") == IPv6Net("1234:5677::/32"), "operator--()"); verbose_assert(--IPv6Net("::/32") == IPv6Net("ffff:ffff::/32"), "operator--()"); // // Increment Operator // verbose_assert(++IPv6Net("1234:5678::/32") == IPv6Net("1234:5679::/32"), "operator++()"); verbose_assert(++IPv6Net("ffff:ffff::/32") == IPv6Net("::/32"), "operator++()"); // // Test if the object contains a real (non-default) value. // verbose_assert(! IPv6Net().is_valid(), "is_valid()"); verbose_assert(! IPv6Net("::/0").is_valid(), "is_valid()"); verbose_assert(IPv6Net("::/1").is_valid(), "is_valid()"); return (! failures()); } /** * Test IPv6Net address type. */ bool test_ipv6net_address_type(TestInfo& test_info) { UNUSED(test_info); IPv6Net ipnet_default("::/0"); // Default route: unicast IPv6Net ipnet_unicast1("::/1"); // Unicast IPv6Net ipnet_unicast2("1234:5678::/32"); // Unicast IPv6Net ipnet_multicast1("ff00::/8"); // Multicast IPv6Net ipnet_multicast2("ff00:1::/32"); // Multicast IPv6Net ipnet_odd1("8000::/1"); // Odd: includes multicast IPv6Net ipnet_odd2("fe00::/7"); // Odd: includes multicast // // Test if a subnet is within the unicast address range. // verbose_assert(ipnet_default.is_unicast() == true, "is_unicast()"); verbose_assert(ipnet_unicast1.is_unicast() == true, "is_unicast()"); verbose_assert(ipnet_unicast2.is_unicast() == true, "is_unicast()"); verbose_assert(ipnet_multicast1.is_unicast() == false, "is_unicast()"); verbose_assert(ipnet_multicast2.is_unicast() == false, "is_unicast()"); verbose_assert(ipnet_odd1.is_unicast() == false, "is_unicast()"); verbose_assert(ipnet_odd2.is_unicast() == false, "is_unicast()"); // // Test if a subnet is within the multicast address range. // verbose_assert(ipnet_default.is_multicast() == false, "is_multicast()"); verbose_assert(ipnet_unicast1.is_multicast() == false, "is_multicast()"); verbose_assert(ipnet_unicast2.is_multicast() == false, "is_multicast()"); verbose_assert(ipnet_multicast1.is_multicast() == true, "is_multicast()"); verbose_assert(ipnet_multicast2.is_multicast() == true, "is_multicast()"); verbose_assert(ipnet_odd1.is_multicast() == false, "is_multicast()"); verbose_assert(ipnet_odd2.is_multicast() == false, "is_multicast()"); return (! failures()); } /** * Test IPv6Net address overlap. */ bool test_ipv6net_address_overlap(TestInfo& test_info) { UNUSED(test_info); IPv6Net ipnet_a("1234:5678::/32"); IPv6Net ipnet_b("1234:5679::/32"); IPv6Net ipnet_c("1234:5678:9abc::/48"); IPv6Net ipnet_d("1234:0020::/32"); IPv6Net ipnet_e("1234:0021::/32"); IPv6Net ipnet_f("0001:0002:0001::/48"); IPv6Net ipnet_g("0001:0002:0003::/48"); // // Test if subnets overlap. // verbose_assert(ipnet_a.is_overlap(ipnet_b) == false, "is_overlap()"); verbose_assert(ipnet_a.is_overlap(ipnet_c) == true, "is_overlap()"); verbose_assert(ipnet_c.is_overlap(ipnet_a) == true, "is_overlap()"); // // Test if a subnet contains (or is equal to) another subnet. // verbose_assert(ipnet_a.contains(ipnet_a) == true, "contains(IPv6Net)"); verbose_assert(ipnet_a.contains(ipnet_b) == false, "contains(IPv6Net)"); verbose_assert(ipnet_a.contains(ipnet_c) == true, "contains(IPv6Net)"); verbose_assert(ipnet_c.contains(ipnet_a) == false, "contains(IPv6Net)"); // // Test if an address is within a subnet. // verbose_assert(ipnet_a.contains(ipnet_a.masked_addr()) == true, "contains(IPv6)"); verbose_assert(ipnet_a.contains(ipnet_b.masked_addr()) == false, "contains(IPv6)"); verbose_assert(ipnet_a.contains(ipnet_c.masked_addr()) == true, "contains(IPv6)"); // // Determine the number of the most significant bits overlapping with // another subnet. // verbose_assert(ipnet_a.overlap(ipnet_a) == 32, "overlap()"); verbose_assert(ipnet_a.overlap(ipnet_b) == 31, "overlap()"); verbose_assert(ipnet_a.overlap(ipnet_c) == 32, "overlap()"); verbose_assert(ipnet_d.overlap(ipnet_e) == 31, "overlap()"); verbose_assert(ipnet_f.overlap(ipnet_g) == 46, "overlap()"); return (! failures()); } /** * Test performance of IPv6Net address overlap. */ bool test_performance_ipv6net_address_overlap(TestInfo& test_info) { UNUSED(test_info); IPv6Net ipnet_a("ffff:ffff::/32"); IPv6Net ipnet_b("ffff:ffff:ffff:ffff:ffff:ffff::/96"); // // Test overlapping subnets. // do { size_t i; size_t c = 0; TimeVal begin_timeval, end_timeval, delta_timeval; TimerList::system_gettimeofday(&begin_timeval); for (i = 0; i < 0xffffff; i++) { c += ipnet_a.overlap(ipnet_b); } TimerList::system_gettimeofday(&end_timeval); delta_timeval = end_timeval - begin_timeval; verbose_log("Execution time IPv6Net::overlap(): %s seconds\n", delta_timeval.str().c_str()); } while (false); return (! failures()); } /** * Test IPv6Net address constant values. */ bool test_ipv6net_address_const(TestInfo& test_info) { UNUSED(test_info); IPv6Net ipnet_a("1234:5678::/32"); // // Test the address family. // verbose_assert(IPv6Net::af() == AF_INET6, "af()"); // // Get the base address. // IPv6 ip1; ip1 = ipnet_a.masked_addr(); verbose_match(ip1.str(), "1234:5678::"); // // Get the prefix length. // verbose_assert(ipnet_a.prefix_len() == 32, "prefix_len()"); // // Get the network mask. // IPv6 ip2; ip2 = ipnet_a.netmask(); verbose_match(ip2.str(), "ffff:ffff::"); // // Return the subnet containing all multicast addresses. // verbose_match(IPv6Net::ip_multicast_base_prefix().str(), "ff00::/8"); return (! failures()); } /** * Test IPv6Net address manipulation. */ bool test_ipv6net_manipulate_address(TestInfo& test_info) { UNUSED(test_info); IPv6Net ipnet_a("1234:5678::/32"); // // Get the highest address within this subnet. // verbose_match(ipnet_a.top_addr().str(), "1234:5678:ffff:ffff:ffff:ffff:ffff:ffff"); // // Get the smallest subnet containing both subnets. // verbose_match(IPv6Net::common_subnet(IPv6Net("1234:5678:1::/48"), IPv6Net("1234:5678:8000::/48")).str(), "1234:5678::/32"); return (! failures()); } int main(int argc, char * const argv[]) { XorpUnexpectedHandler x(xorp_unexpected_handler); // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); TestMain test_main(argc, argv); string test = test_main.get_optional_args("-t", "--test", "run only the specified test"); test_main.complete_args_parsing(); // // TODO: XXX: a temporary glue until we complete the switch to the // TestMain facility. // if (test_main.get_verbose()) set_verbose(true); struct test { string test_name; XorpCallback1::RefPtr cb; bool run_by_default; } tests[] = { { "test_ipv6net_valid_constructors", callback(test_ipv6net_valid_constructors), true }, { "test_ipv6net_invalid_constructors", callback(test_ipv6net_invalid_constructors), true }, { "test_ipv6net_operators", callback(test_ipv6net_operators), true }, { "test_ipv6net_address_type", callback(test_ipv6net_address_type), true }, { "test_ipv6net_address_overlap", callback(test_ipv6net_address_overlap), true }, { "test_ipv6net_address_const", callback(test_ipv6net_address_const), true }, { "test_ipv6net_manipulate_address", callback(test_ipv6net_manipulate_address), true }, { "test_performance_ipv6net_address_overlap", callback(test_performance_ipv6net_address_overlap), false } }; try { if (test.empty()) { for (size_t i = 0; i < sizeof(tests) / sizeof(struct test); i++) { if (! tests[i].run_by_default) continue; reset_failures(); test_main.run(tests[i].test_name, tests[i].cb); } } else { for (size_t i = 0; i < sizeof(tests) / sizeof(struct test); i++) { if (test == tests[i].test_name) { reset_failures(); test_main.run(tests[i].test_name, tests[i].cb); return test_main.exit(); } } test_main.failed("No test with name " + test + " found\n"); } } catch (...) { xorp_print_standard_exceptions(); } // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); return test_main.exit(); } xorp/libxorp/tests/test_observers.cc0000664000076400007640000001711211540224230020054 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // Test program to the Observer classes for TimerList and SelectorList // #include "libxorp_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/xorpfd.hh" #ifdef HAVE_FCNTL_H #include #endif #include "timer.hh" #include "eventloop.hh" static bool s_verbose = false; bool verbose() { return s_verbose; } void set_verbose(bool v) { s_verbose = v; } static int s_failures = 0; bool failures() { return (s_failures)? (true) : (false); } void incr_failures() { s_failures++; } void reset_failures() { s_failures = 0; } #include "libxorp/xorp_tests.hh" #ifndef HOST_OS_WINDOWS static int fired(0); static int add_rem_fd_counter(0); static int add_rem_mask_counter(0); static bool fd_read_notification_called = false; static bool fd_write_notification_called = false; static bool fd_exeption_notification_called = false; static bool fd_removal_notification_called = false; static bool schedule_notification_called = false; static bool unschedule_notification_called = false; static bool one_fd_rem_per_each_fd_add_notif = false; static bool no_notifications_beyond_this_point = false; void print_tv(FILE * s, TimeVal a) { fprintf(s, "%lu.%06lu", (unsigned long)a.sec(), (unsigned long)a.usec()); fflush(s); } // callback for non-periodic timer. Does not need to return a value static void some_foo() { fired++; printf("#"); fflush(stdout); } // callback for a periodic timer. If true, the timer is rescheduled. static bool print_dot() { printf("."); fflush(stdout); return true; } // callback for selector. static void do_the_twist(XorpFd fd, IoEventType event) { printf("fd: %s event: %0x is here\n", fd.str().c_str(), event); } // Implement the SelectorList observer and notification functions class mySelectorListObserver : public SelectorListObserverBase { void notify_added(XorpFd, const SelectorMask&); void notify_removed(XorpFd, const SelectorMask&); }; void mySelectorListObserver::notify_added(XorpFd fd, const SelectorMask& mask) { fprintf(stderr, "notif added fd: %s mask: %#0x\n", fd.str().c_str(), mask); add_rem_fd_counter+=fd; add_rem_mask_counter+=mask; switch (mask) { case SEL_RD: fd_read_notification_called = true; break; case SEL_WR: fd_write_notification_called = true; break; case SEL_EX: fd_exeption_notification_called = true; break; default: print_failed("invalid mask notification"); exit(1); } } void mySelectorListObserver::notify_removed(XorpFd fd, const SelectorMask& mask) { fprintf(stderr, "notif removed fd: %s mask: %#0x\n", fd.str().c_str(), mask); add_rem_fd_counter-=fd; add_rem_mask_counter-=mask; if (no_notifications_beyond_this_point) { print_failed("duplicate removal!!"); exit(1); } fd_removal_notification_called = true; } // Implement TimerList observer and notification functions class myTimerListObserver : public TimerListObserverBase { void notify_scheduled(const TimeVal&); void notify_unscheduled(const TimeVal&); }; void myTimerListObserver::notify_scheduled(const TimeVal& tv) { fprintf(stderr, "notif sched ");print_tv(stderr, tv);fprintf(stderr, "\n"); schedule_notification_called = true; } void myTimerListObserver::notify_unscheduled(const TimeVal& tv) { fprintf(stderr, "notif unsch "); print_tv(stderr, tv);fprintf(stderr, "\n"); unschedule_notification_called = true; } void run_test() { EventLoop e; mySelectorListObserver sel_obs; myTimerListObserver timer_obs; e.selector_list().set_observer(sel_obs); e.timer_list().set_observer(timer_obs); XorpTimer show_stopper; show_stopper = e.new_oneoff_after_ms(500, callback(some_foo)); assert(show_stopper.scheduled()); XorpTimer zzz = e.new_periodic_ms(30, callback(print_dot)); assert(zzz.scheduled()); XorpFd fd[2]; int pipefds[2]; #ifndef HOST_OS_WINDOWS if (pipe(pipefds)) { print_failed("unable to generate file descriptors for test"); exit(2); } fd[0] = XorpFd(pipefds[0]); fd[1] = XorpFd(pipefds[1]); #else // HOST_OS_WINDOWS if (_pipe(pipefds, 65536, _O_BINARY)) { fprintf(stderr, "unable to generate file descriptors for test\n"); exit(2); } fd[0] = XorpFd(_get_osfhandle(pipefds[0])); fd[1] = XorpFd(_get_osfhandle(pipefds[1])); #endif // HOST_OS_WINDOWS XorpCallback2::RefPtr cb = callback(do_the_twist); e.selector_list().add_ioevent_cb(fd[0], IOT_READ, cb); e.selector_list().add_ioevent_cb(fd[1], IOT_WRITE, cb); e.selector_list().add_ioevent_cb(fd[0], IOT_EXCEPTION, cb); while(show_stopper.scheduled()) { assert(zzz.scheduled()); e.run(); // run will return after one or more pending events // have fired. e.selector_list().remove_ioevent_cb(fd[0], IOT_READ); e.selector_list().remove_ioevent_cb(fd[1], IOT_WRITE); e.selector_list().remove_ioevent_cb(fd[0], IOT_EXCEPTION); } zzz.unschedule(); // these should not raise notifications since they have already been removed no_notifications_beyond_this_point = true; e.selector_list().remove_ioevent_cb(fd[0], IOT_READ); e.selector_list().remove_ioevent_cb(fd[1], IOT_WRITE); #ifndef HOST_OS_WINDOWS close(fd[0]); close(fd[1]); #else _close(fd[0]); _close(fd[1]); #endif one_fd_rem_per_each_fd_add_notif = (add_rem_fd_counter == 0) && (add_rem_mask_counter == 0); if (!one_fd_rem_per_each_fd_add_notif) { print_failed(""); fprintf(stderr, "cumulative fd and mask should both be 0 but are "); fprintf(stderr, "fd: %d mask: %d\n", add_rem_fd_counter, add_rem_mask_counter); exit(1); } if (!(fd_read_notification_called && fd_write_notification_called && fd_exeption_notification_called && fd_removal_notification_called && schedule_notification_called && unschedule_notification_called)) { print_failed("Some notifications not called correctly"); exit(1); } print_passed("observers"); } #endif // !HOST_OS_WINDOWS int main(int /* argc */, const char* argv[]) { // TODO: enable the test for Windows #ifndef HOST_OS_WINDOWS // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); fprintf(stderr, "------------------------------------------------------\n"); run_test(); fprintf(stderr, "------------------------------------------------------\n"); // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); #else // HOST_OS_WINDOWS UNUSED(argv); #endif // HOST_OS_WINDOWS return 0; } xorp/libxorp/range.hh0000664000076400007640000001513711540224227014762 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/libxorp/range.hh,v 1.11 2008/10/02 21:57:32 bms Exp $ #ifndef __LIBXORP_RANGE_HH__ #define __LIBXORP_RANGE_HH__ class IPv4; class IPv6; /** * @short A template class for implementing linear ranges X..Y * * We define a linear range by its low and high inclusive boundaries. * It's the user's responisibility to ensure that the condition * (_low <= _high) always holds! */ template class Range { public: /** * Default constructor */ Range() {} /** * Constructor from a single value. */ explicit Range(T value) { _low = _high = value; } /** * Constructor from two values. */ explicit Range(T low, T high) { _low = low; _high = high; } const T& low() const { return _low; } const T& high() const { return _high; } protected: T _low; T _high; }; /** * @short A linear range class (uint32_t low)..(uint32_t high) * * Inherits from templatized general Range class. * Provides specialized constructor from string and str() method. */ class U32Range: public Range { public: /** * Default constructor */ U32Range() { Range::_low = Range::_high = 0; } /** * Constructor from a string. */ U32Range(const char *from_cstr) { string from_string = string(from_cstr); string::size_type delim = from_string.find("..", 0); if (delim == string::npos) { _low = _high = strtoul(from_cstr, NULL, 10); } else if (delim > 0 && (from_string.length() - delim > 2)) { _low = strtoul(from_string.substr(0, delim).c_str(), NULL, 10); _high = strtoul(from_string.substr(delim + 2, from_string.length()).c_str(), NULL, 10); } else { xorp_throw(InvalidString, "Syntax error"); } } /** * Convert the range to a human-readable format. * * @return C++ string. */ string str() const { ostringstream os; os << _low; if (_low < _high) os << ".." << _high; return os.str(); } }; /** * Equality Operator for @ref uint32_t against @ref U32Range operand. * * @param lhs the left-hand @ref uint32_t type operand. * @param rhs the right-hand @ref U32Range operand. * @return true if the value of the left-hand operand falls inside * the range defined by the right-hand operand. */ inline bool operator==(const uint32_t& lhs, const U32Range& rhs) { return (lhs >= rhs.low() && lhs <= rhs.high()); } /** * Non-equality Operator for @ref uint32_t against @ref U32Range operand. * * @param lhs the left-hand @ref uint32_t type operand. * @param rhs the right-hand @ref U32Range operand. * @return true if the value of the left-hand operand falls outside * the range defined by the right-hand operand. */ inline bool operator!=(const uint32_t& lhs, const U32Range& rhs) { return (lhs < rhs.low() || lhs > rhs.high()); } /** * Less-than comparison for @ref uint32_t against @ref U32Range operand. * * @param lhs the left-hand @ref uint32_t type operand. * @param rhs the right-hand @ref U32Range operand. * @return true if the value of the left-hand operand is bellow * the range defined by the right-hand operand. */ inline bool operator<(const uint32_t& lhs, const U32Range& rhs) { return (lhs < rhs.low()); } /** * Less-than or equal comparison for @ref uint32_t against @ref U32Range * * @param lhs the left-hand @ref uint32_t type operand. * @param rhs the right-hand @ref U32Range operand. * @return true if the value of the left-hand operand is bellow or within * the range defined by the right-hand operand. */ inline bool operator<=(const uint32_t& lhs, const U32Range& rhs) { return (lhs <= rhs.high()); } /** * Greater-than comparison for @ref uint32_t against @ref U32Range operand. * * @param lhs the left-hand @ref uint32_t type operand. * @param rhs the right-hand @ref U32Range operand. * @return true if the value of the left-hand operand is above * the range defined by the right-hand operand. */ inline bool operator>(const uint32_t& lhs, const U32Range& rhs) { return (lhs > rhs.high()); } /** * Greater-than or equal comparison for @ref uint32_t against @ref U32Range * * @param lhs the left-hand @ref uint32_t type operand. * @param rhs the right-hand @ref U32Range operand. * @return true if the value of the left-hand operand is above or within * the range defined by the right-hand operand. */ inline bool operator>=(const uint32_t& lhs, const U32Range& rhs) { return (lhs >= rhs.low()); } /** * @short A linear IPvX class template (IPvX low)..(IPvX high) * * Inherits from templatized general Range class. * Provides a specialized constructor from string and str() method. */ template class IPvXRange: public Range { public: /** * Default constructor */ IPvXRange() {} /** * Constructor from a string. */ IPvXRange(const char *from_cstr) { string from_string = string(from_cstr); string::size_type delim = from_string.find("..", 0); if (delim == string::npos) Range::_low = Range::_high = T(from_cstr); else if (delim > 0 && (from_string.length() - delim > 2)) { Range::_low = T(from_string.substr(0, delim).c_str()); Range::_high = T(from_string.substr(delim + 2, from_string.length()) .c_str()); } else { xorp_throw(InvalidString, "Syntax error"); } } /** * Convert the range to a human-readable format. * * @return C++ string. */ string str() const { ostringstream os; os << Range::_low.str(); if (Range::_low < Range::_high) os << ".." << Range::_high.str(); return os.str(); } }; typedef IPvXRange IPv4Range; typedef IPvXRange IPv6Range; #endif // __LIBXORP_RANGE_HH__ xorp/libxorp/buffered_asyncio.hh0000664000076400007640000001153711625536517017211 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __LIBXORP_BUFFERED_ASYNCIO_HH__ #define __LIBXORP_BUFFERED_ASYNCIO_HH__ #include "libxorp/xorp.h" #ifdef HAVE_FCNTL_H #include #endif #include "libxorp/callback.hh" #include "libxorp/eventloop.hh" class BufferedAsyncReader : public NONCOPYABLE { public: enum Event { DATA = 1, OS_ERROR = 2, END_OF_FILE = 3 }; /* * Callback has arguments: * BufferedAsyncReader* caller, * ErrorCode e, * uint8_t* buffer, * size_t buffer_bytes, * size_t offset * * The callback is invoked when data arrives, when an error * occurs, or the end of file is detected. For data, the callback is * only invoked when the threshold is crossed. */ typedef XorpCallback4::RefPtr Callback; /** * Constructor. * * @param e the eventloop. * @param fd the file descriptor. * @param reserve_bytes the number of bytes to reserve in the data buffer. * @param cb the callback to invoke. * @param priority the task priority for the eventloop operations. */ BufferedAsyncReader(EventLoop& e, XorpFd fd, size_t reserve_bytes, const Callback& cb, int priority = XorpTask::PRIORITY_DEFAULT); virtual ~BufferedAsyncReader(); /** * Set threshold for event notification. Only when this threshold * is reached the consumers callback invoked. If more data is already * available, then the event notification will be triggered * through a 0 second timer. This provides an opportunity for * other tasks to run. * * Calling this method may cause the internal buffer state to * change. If it is called from within a consumer callback, then * the buffer pointer may become invalid and dereferencing the * pointer should be avoided. * * @param bytes the number of threshold bytes. * @return true on success, false if bytes is larger than reserve. */ bool set_trigger_bytes(size_t bytes); /** * Get the current threshold for event notification. */ size_t trigger_bytes() const; /** * Acknowledge data at the start of the buffer is finished with. * * Typically, a consumer would call this from within their * callback to say this number of bytes has been processed and can * be discarded. * * @param bytes the number of bytes to dispose. * @return true on success, false if bytes is larger than the number * of available bytes. */ bool dispose(size_t bytes); /** * Set reserve for maximum amount of data to receive. * * @param bytes the number of bytes to reserve. * @return true on success, false if error. */ bool set_reserve_bytes(size_t bytes); /** * Get reserve for maximum amount of data to receive. */ size_t reserve_bytes() const; /** * Get the number of currently available bytes. */ size_t available_bytes() const; int error() const { return _last_error; } /** * Start. * * Start asynchrous reads. */ void start(); /** * Stop. * * Stop asynchrous reading. */ void stop(); virtual string toString() const; private: BufferedAsyncReader(); // Not directly constructible private: void io_event(XorpFd fd, IoEventType type); void announce_event(Event e); void provision_trigger_bytes(); struct Config { uint8_t* head; // The beginning of data size_t head_bytes; // The number of available bytes with data size_t trigger_bytes; // The number of bytes to trigger cb delivery size_t reserve_bytes; // The number of bytes to reserve for data }; Config _config; EventLoop& _eventloop; XorpFd _fd; Callback _cb; vector _buffer; XorpTimer _ready_timer; int _last_error; int _priority; }; #endif // __LIBXORP_BUFFERED_ASYNCIO_HH__ xorp/libxorp/win_io.c0000664000076400007640000002160211540224230014761 0ustar greearbgreearb/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- */ /* vim:set sts=4 ts=8: */ /* * Copyright (c) 2001-2011 XORP, Inc and Others * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License, Version * 2.1, June 1999 as published by the Free Software Foundation. * Redistribution and/or modification of this program under the terms of * any other version of the GNU Lesser General Public License is not * permitted. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, * see the GNU Lesser General Public License, Version 2.1, a copy of * which can be found in the XORP LICENSE.lgpl file. * * XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; * http://xorp.net */ #include "libxorp/xorp.h" #ifdef HOST_OS_WINDOWS #include "win_io.h" /* Size of the statically allocated win_strerror() buffer. */ #define WIN_STRERROR_BUF_SIZE 1024 /* Maximum number of console input records to buffer on stack per read. */ #define MAX_INPUT_RECORDS 64 /** * win_strerror_r: * * A friendly wrapper for FormatMessageA() which uses static storage * (much like the Open Group's strerror_r()) to render the return * value of GetLastError() into a string. * * @param errnum the error code returned by GetLastError(). * @param strerrbuf the destination buffer for the message string. * @param buflen the size of the destination buffer. * @return 0 if successful, ERANGE, or EINVAL. */ int win_strerror_r(DWORD errnum, char *strerrbuf, size_t buflen) { DWORD result; result = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_MAX_WIDTH_MASK, NULL, errnum, 0, (LPSTR)strerrbuf, buflen, NULL); if (result == 0) { result = GetLastError(); if (result == ERROR_BUFFER_OVERFLOW) { strerrbuf[buflen-1] = '\0'; return (ERANGE); } else { strerrbuf[0] = '\0'; return (EINVAL); } } return (0); } /** * win_strerror: * * This is the thread-unsafe version of the win_strerror_r() function which * formats the message into a user-provided buffer. * * @param errnum the error code returned by GetLastError(). * @return a pointer to a NUL-terminated C string in static storage. */ char * win_strerror(DWORD errnum) { static char buf[WIN_STRERROR_BUF_SIZE]; win_strerror_r(errnum, buf, sizeof(buf)); return (buf); } /** * win_con_input_filter: * * Helper function to filter only the KeyEvents we are interested in. * If the record passed to this function has an EventType other than * KEY_EVENT, the results are undefined. * * Key codes are translated to their ASCII or ANSI VT100 equivalents. * * @param ke pointer to a key event record to be filtered. * @param buf pointer to a buffer where translated key events should be stored. * @param remaining the number of bytes remaining in the buffer. * * @return the number of 8-bit (ASCII or UTF-8) characters written to the * buffer; otherwise, zero if the event record passed was not valid. */ static ssize_t win_con_input_filter(const KEY_EVENT_RECORD *ke, char *buf, size_t remaining) { int ch = ke->uChar.AsciiChar; int chsz = 0; switch (ke->wVirtualKeyCode) { /* single-character ASCII control sequences */ case VK_SPACE: case VK_TAB: case VK_RETURN: case VK_BACK: case VK_DELETE: case VK_HOME: #if 0 case VK_END: case VK_INSERT: #endif if (remaining < 1) return (0); chsz = 1; switch (ke->wVirtualKeyCode) { case VK_SPACE: *buf++ = ' '; break; case VK_TAB: *buf++ = '\t'; break; case VK_RETURN: *buf++ = '\n'; break; case VK_BACK: /* Backspace */ *buf++ = '\b'; break; case VK_DELETE: *buf++ = 0x7F; break; case VK_HOME: /* XXX: This translation may be incorrect. */ *buf++ = '\r'; break; #if 0 case VK_END: /* XXX: This translation is incorrect. */ *buf = '\r'; break; case VK_INSERT: /* XXX: This translation is incorrect. */ *buf = '\r'; break; #endif } break; /* three-character VT100 control sequences */ case VK_UP: case VK_DOWN: case VK_LEFT: case VK_RIGHT: if (remaining < 3) return (0); chsz = 3; *buf++ = '\033'; *buf++ = '['; switch (ke->wVirtualKeyCode) { case VK_UP: *buf++ = 'A'; break; case VK_DOWN: *buf++ = 'B'; break; case VK_LEFT: *buf++ = 'C'; break; case VK_RIGHT: *buf++ = 'D'; break; } break; /* all other single ASCII characters */ default: if (ch >= 1 && ch <= 255) { chsz = 1; *buf++ = ch; } break; } return (chsz); } /** * win_con_read: * * Read keyboard input from a Windows console in a non-blocking mode, with * similar semantics to the POSIX read() function. * * Also performs translation of raw Windows keycodes to ANSI VT100 terminal * control sequences. * * If the handle passed to this function is not a Windows console input * handle, the results are undefined. * * @param h Windows console input handle. * @param buf pointer to an input buffer where keypresses are to be stored. * @param bufsize size of the input buffer. * * @return the total number of 8-bit characters read, which may include * keypresses translated into ANSI VT100 control sequences; * 0 if the function would have blocked waiting for input; * -1 if an error occurred; * -2 if the function was called with a NULL or 0-length buffer * to poll for input, and input was present in the buffer. */ ssize_t win_con_read(HANDLE h, void *buf, size_t bufsize) { INPUT_RECORD inr[MAX_INPUT_RECORDS]; ssize_t ncharsread, remaining, nch; DWORD nevents; char *cp; uint32_t i; BOOL result; result = PeekConsoleInputA(h, inr, sizeof(inr)/sizeof(inr[0]), &nevents); if (result == FALSE) { #if 0 fprintf(stderr, "PeekConsoleInputA() error: %s\n", win_strerror(GetLastError())); #endif return (-1); } if (buf == NULL || bufsize == 0) { if (nevents > 0) { return (WINIO_ERROR_HASINPUT); } else if (nevents == 0) { return (0); } else { return (-1); } } cp = buf; remaining = (ssize_t)bufsize; ncharsread = 0; for (i = 0; i < nevents; i++) { if ((inr[i].EventType == KEY_EVENT) && (inr[i].Event.KeyEvent.bKeyDown == TRUE)) { if (remaining == 0) break; nch = win_con_input_filter(&inr[i].Event.KeyEvent, cp, remaining); if (nch != 0) { ncharsread += nch; remaining -= nch; } } } if (nevents > 0) FlushConsoleInputBuffer(h); return (ncharsread); } /** * win_pipe_read: * * Read input from a Windows pipe in a non-blocking mode, with similar * semantics to the POSIX read() function. * If the handle passed to this function is not a Windows pipe handle, the * results are undefined. * * XXX: This function is designed to work with byte-mode pipes. Because * a message-mode pipe may be opened in byte-mode for read, we rely on * the client consuming all data as it becomes available, i.e. we do not * make a distinction between byte-mode and message-mode pipes. This only * applies to the client side of code which connects to a *named* pipe * and uses this function to read from it. * * @param h Windows pipe handle. * @param buf pointer to an input buffer where input is to be stored. * @param bufsize size of the input buffer. * @return the number of bytes read, or an error code; * 0 if the function would have blocked waiting for input; * -1 if a general I/O error occurred; * -2 if there is data waiting but the function was called with invalid * buf and bufsize arguments; * -3 if the pipe was disconnected or broken. */ ssize_t win_pipe_read(HANDLE h, void *buf, size_t bufsize) { BOOL result; DWORD nbytesavail, dwbytesread; ssize_t nbytesread; result = PeekNamedPipe(h, NULL, 0, NULL, &nbytesavail, NULL); if (result == FALSE) { result = GetLastError(); #if 0 fprintf(stderr, "PeekNamedPipe() error: %s\n", win_strerror(result)); #endif if (result == ERROR_BROKEN_PIPE || result == ERROR_PIPE_NOT_CONNECTED) return (WINIO_ERROR_DISCONNECT); return (WINIO_ERROR_IOERROR); } if (buf == NULL || bufsize == 0) { if (nbytesavail > 0) return (WINIO_ERROR_HASINPUT); return (WINIO_ERROR_IOERROR); } nbytesread = 0; if (nbytesavail > 0) { if (nbytesavail > bufsize) nbytesavail = bufsize; result = ReadFile(h, buf, nbytesavail, &dwbytesread, NULL); if (result == FALSE) { #if 0 fprintf(stderr, "ReadFile() error: %s\n", win_strerror(GetLastError())); #endif return (WINIO_ERROR_IOERROR); } nbytesread = (ssize_t)dwbytesread; } return (nbytesread); } #endif /* HOST_OS_WINDOWS */ xorp/libxorp/ether_compat.c0000664000076400007640000001337711421137511016164 0ustar greearbgreearb/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- */ /* vim:set sts=4 ts=8: */ /* * Copyright (c) 2001-2009 XORP, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License, Version * 2.1, June 1999 as published by the Free Software Foundation. * Redistribution and/or modification of this program under the terms of * any other version of the GNU Lesser General Public License is not * permitted. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, * see the GNU Lesser General Public License, Version 2.1, a copy of * which can be found in the XORP LICENSE.lgpl file. * * XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; * http://xorp.net */ /* * Part of this software is derived from the following file(s): * tcpdump/addrtoname.c (from FreeBSD) * lib/libc/net/ether_addr.c (from FreeBSD) * The copyright message(s) with the original file(s) is/are included below. */ /* * Copyright (c) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that: (1) source code distributions * retain the above copyright notice and this paragraph in its entirety, (2) * distributions including binary code include the above copyright notice and * this paragraph in its entirety in the documentation or other materials * provided with the distribution, and (3) all advertising materials mentioning * features or use of this software display the following acknowledgement: * ``This product includes software developed by the University of California, * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of * the University nor the names of its contributors may be used to endorse * or promote products derived from this software without specific prior * written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * Internet, ethernet, port, and protocol string to address * and address to string conversion routines * * $FreeBSD: src/contrib/tcpdump/addrtoname.c,v 1.12 2004/03/31 14:57:24 bms Exp $ */ /* * Copyright (c) 1995 * Bill Paul . All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * ethernet address conversion and lookup routines * * Written by Bill Paul * Center for Telecommunications Research * Columbia University, New York City */ #if 0 __FBSDID("$FreeBSD: /repoman/r/ncvs/src/lib/libc/net/ether_addr.c,v 1.15 2002/04/08 07:51:10 ru Exp $"); #endif #include "libxorp/xorp.h" #include "libxorp/utility.h" #include "libxorp/ether_compat.h" #ifdef HAVE_STDINT_H #include #endif #include #include #ifndef HAVE_ETHER_NTOA /* XXX: returns a pointer to static storage. */ char * ether_ntoa(const struct ether_addr *e) { static const char hex[] = "0123456789abcdef"; static char buf[sizeof("00:00:00:00:00:00")]; char *cp; const uint8_t *ep; unsigned int i, j; cp = buf; ep = (const uint8_t *)e->octet; if ((j = *ep >> 4) != 0) *cp++ = hex[j]; *cp++ = hex[*ep++ & 0xf]; for (i = 5; (int)--i >= 0;) { *cp++ = ':'; if ((j = *ep >> 4) != 0) *cp++ = hex[j]; *cp++ = hex[*ep++ & 0xf]; } *cp = '\0'; return (buf); } #endif /* !HAVE_ETHER_NTOA */ #ifndef HAVE_ETHER_ATON /* * Convert 's' which has the form "x:x:x:x:x:x" into a new * ethernet address. * XXX: returns a pointer to static storage. */ struct ether_addr * ether_aton(const char *s) { int i; static struct ether_addr o; unsigned int od[6]; i = sscanf(s, "%x:%x:%x:%x:%x:%x", &od[0], &od[1], &od[2], &od[3], &od[4], &od[5]); if (i != 6) return (NULL); for (i = 0; i < 6; i++) o.octet[i] = od[i]; return (&o); } #endif /* !HAVE_ETHER_ATON */ xorp/libxorp/status_codes.h0000664000076400007640000001211311421137511016202 0ustar greearbgreearb/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- */ /* vim:set sts=4 ts=8: */ /* * Copyright (c) 2001-2009 XORP, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License, Version * 2.1, June 1999 as published by the Free Software Foundation. * Redistribution and/or modification of this program under the terms of * any other version of the GNU Lesser General Public License is not * permitted. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, * see the GNU Lesser General Public License, Version 2.1, a copy of * which can be found in the XORP LICENSE.lgpl file. * * XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; * http://xorp.net */ /* * $XORP: xorp/libxorp/status_codes.h,v 1.14 2008/10/02 21:57:33 bms Exp $ */ #ifndef __LIBXORP_STATUS_CODES_H__ #define __LIBXORP_STATUS_CODES_H__ /** *
 * Explanation of Process States
 * -----------------------------
 *    +-------------> PROC_NULL
 *    |                   |
 *    |                   | (1)
 *    |                   V
 *    |             PROC_STARTUP
 *    |                   |
 *    |                   | (2)
 *    |                   V
 *    |             PROC_NOT_READY
 *    |                |     ^
 *    |(9)         (3) |     | (4)
 *    |                V     |
 *    |               PROC_READY
 *    |               /        \
 *    |           (5)/          \(6)
 *    |             V            V
 *    |      PROC_SHUTDOWN  PROC_FAILED
 *    |             \           /
 *    |           (7)\         /(8)
 *    |               \       /
 *    |                V     V
 *    |               PROC_DONE
 *    |                   |
 *    |                   |
 *    |                   |
 *    |                   V
 *    +-------------------+
 * Events/Actions
 * --------------
 * (1) Register with finder.
 * (2) External dependencies satisfied, ready to be configured.
 * (3) Finished processing any config changes, ready for other
 *     processes that depend on this process to be configured.
 * (4) Received a config change that needs to be processed before other
 *     processes that depend on this process are configured.
 * (5) Received a request for a clean shutdown.
 * (6) Something failed, this process is no longer functioning correctly.
 * (7) The shutdown has completed.
 * (8) The process has completed the cleanup after the failure.
 * (9) Deregister with finder.
 *
 * States
 * ------
 * PROC_NULL        Process is not registered with finder.  It may or may
 *                  not be running.
 *
 * PROC_STARTUP     Process is registered with finder, but is waiting
 *                  on some other processes before it is ready to be
 *                  configured.
 *
 * PROC_NOT_READY   For any reason, the process is not ready for processes
 *                  that depend on this process to be configured or
 *                  reconfigured.  A common reason is that this
 *                  process just received a config change, and is
 *                  still in the process of making the config change
 *                  active.
 *
 * PROC_READY       Process is running normally.  Processes that depend
 *                  on the state of this process can be configured or
 *                  reconfigured.
 *
 * PROC_SHUTDOWN    Process has received a shutdown request is shutting
 *                  down cleanly.  Normally the process will terminate by
 *                  itself after being in this state.
 *
 * PROC_FAILED      Process has suffered a fatal error, and is in the
 *                  process of cleaning up the mess.  Normally the process
 *                  will terminate by itself after being in this state.
 *
 * PROC_DONE        The process has completed operation, but is still
 *                  capable of responding to XRLs.
 *
 * Notes
 * -----
 *
 * A process may spend zero time in PROC_STARTUP, PROC_NOT_READY,
 * PROC_READY, PROC_SHUTDOWN, PROC_FAILED, or PROC_DONE states.  For
 * example, a process may effectively go directly from PROC_NULL to
 * PROC_READY state on startup if there are no dependencies that need
 * to be taken into account.  A process may go from PROC_STARTUP or
 * PROC_NOT_READY states to PROC_SHUTDOWN or PROC_FAILED states
 * without spending any time in PROC_READY state if required.
 *
 * On reconfiguration, a process does not need to go to NOT_READY
 * state unless it needs to delay the reconfiguration of processes
 * that depend on the completion of this reconfiguration.
 *
 * After shutdown or a failure, the process may remain indefinitely in
 * PROC_DONE state (e.g., if the process itself shoudn't really exit
 * but rather await further instructions).
 * 
*/ typedef enum { PROC_NULL = 0, PROC_STARTUP = 1, PROC_NOT_READY = 2, PROC_READY = 3, PROC_SHUTDOWN = 4, PROC_FAILED = 5, PROC_DONE = 6 } ProcessStatus; #endif /* __LIBXORP_STATUS_CODES_H__ */ xorp/libxorp/ioevents.hh0000664000076400007640000000317311540225527015523 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __LIBXORP_IOEVENTS_HH__ #define __LIBXORP_IOEVENTS_HH__ #include "xorpfd.hh" #include "callback.hh" /** * @short I/O event type. * * Enumeration of various event types supported by the I/O callback facade. */ enum IoEventType { IOT_READ, // Object is ready to read IOT_WRITE, // Object is ready to write IOT_EXCEPTION, // Object has exceptional condition IOT_ACCEPT, // Socket: Inbound connection available for accept() IOT_CONNECT, // Socket: Outgoing connect() has completed IOT_DISCONNECT, // Socket: Peer disconnected IOT_ANY // Match any kind of event (for removal) }; typedef XorpCallback2::RefPtr IoEventCb; #endif // __LIBXORP_IOEVENTS_HH__ xorp/libxorp/ref_ptr.cc0000664000076400007640000001474411421137511015315 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "libxorp/xorp.h" #include "ref_ptr.hh" /////////////////////////////////////////////////////////////////////////////// // // Debugging macro's // #define POOL_PARANOIA(x) /* x */ #define VERBOSE_POOL_PARANOIA(x) /* x */ /////////////////////////////////////////////////////////////////////////////// // // ref_counter_pool implementation // ref_counter_pool ref_counter_pool::_the_instance; ref_counter_pool& ref_counter_pool::instance() { return ref_counter_pool::_the_instance; } void ref_counter_pool::grow() { size_t old_size = _counters.size(); _counters.resize(old_size + old_size / 8 + 1); for (size_t i = old_size; i < _counters.size(); i++) { _counters[i] = _free_index; _free_index = i; } POOL_PARANOIA(check()); } void ref_counter_pool::check() { int32_t i = _free_index; size_t n = 0; VERBOSE_POOL_PARANOIA(cout << "L: "); while (_counters[i] != LAST_FREE) { VERBOSE_POOL_PARANOIA(cout << i << " "); i = _counters[i]; n++; if (n == _counters.size()) { dump(); abort(); } } VERBOSE_POOL_PARANOIA(cout << endl); } bool ref_counter_pool::on_free_list(int32_t index) { int32_t i = _free_index; size_t n = 0; while (_counters[i] != LAST_FREE) { if (i == index) { return true; } i = _counters[i]; n++; if (n == _counters.size()) { dump(); abort(); } } return false; } void ref_counter_pool::dump() { for (size_t i = 0; i < _counters.size(); i++) { cout << i << " " << _counters[i] << endl; } cout << "Free index: " << _free_index << endl; cout << "Balance: " << _balance << endl; } ref_counter_pool::ref_counter_pool() { const size_t n = 1; _counters.resize(n); _counters[n - 1] = LAST_FREE; _free_index = 0; grow(); grow(); VERBOSE_POOL_PARANOIA(dump()); } int32_t ref_counter_pool::new_counter() { VERBOSE_POOL_PARANOIA(dump()); if (_counters[_free_index] == LAST_FREE) { grow(); } POOL_PARANOIA(assert(_counters[_free_index] != LAST_FREE)); POOL_PARANOIA(check()); int32_t new_counter = _free_index; _free_index = _counters[_free_index]; _counters[new_counter] = 1; ++_balance; POOL_PARANOIA(check()); return new_counter; } int32_t ref_counter_pool::incr_counter(int32_t index) { POOL_PARANOIA(assert(on_free_list(index) == false)); POOL_PARANOIA(check()); assert((size_t)index < _counters.size()); ++_counters[index]; ++_balance; POOL_PARANOIA(check()); return _counters[index]; } int32_t ref_counter_pool::decr_counter(int32_t index) { POOL_PARANOIA(assert((size_t)index < _counters.size())); int32_t c = --_counters[index]; --_balance; if (c == 0) { POOL_PARANOIA(check()); /* recycle */ _counters[index] = _free_index; _free_index = index; VERBOSE_POOL_PARANOIA(dump()); POOL_PARANOIA(check()); } assert(c >= 0); return c; } int32_t ref_counter_pool::count(int32_t index) { POOL_PARANOIA(assert((size_t)index < _counters.size())); return _counters[index]; } /////////////////////////////////////////////////////////////////////////////// // // cref_counter_pool implementation // cref_counter_pool cref_counter_pool::_the_instance; cref_counter_pool& cref_counter_pool::instance() { return cref_counter_pool::_the_instance; } void cref_counter_pool::grow() { size_t old_size = _counters.size(); _counters.resize(2 * old_size); for (size_t i = old_size; i < _counters.size(); i++) { _counters[i].count = _free_index; _free_index = i; } } void cref_counter_pool::check() { int32_t i = _free_index; size_t n = 0; VERBOSE_POOL_PARANOIA(cout << "L: "); while (_counters[i].count != LAST_FREE) { VERBOSE_POOL_PARANOIA(cout << i << " "); i = _counters[i].count; n++; if (n == _counters.size()) { dump(); abort(); } } VERBOSE_POOL_PARANOIA(cout << endl); } void cref_counter_pool::dump() { for (size_t i = 0; i < _counters.size(); i++) { cout << i << " " << _counters[i].count << endl; } cout << "Free index: " << _free_index << endl; } cref_counter_pool::cref_counter_pool() { const size_t n = 1; _counters.resize(n); _free_index = 0; // first free item _counters[n - 1].count = LAST_FREE; grow(); grow(); VERBOSE_POOL_PARANOIA(dump()); } int32_t cref_counter_pool::new_counter(void* data) { VERBOSE_POOL_PARANOIA(dump()); if (_counters[_free_index].count == LAST_FREE) { grow(); } POOL_PARANOIA(assert(_counters[_free_index].count != LAST_FREE)); POOL_PARANOIA(check()); int32_t new_counter = _free_index; _free_index = _counters[_free_index].count; _counters[new_counter].count = 1; _counters[new_counter].data = data; POOL_PARANOIA(check()); return new_counter; } int32_t cref_counter_pool::incr_counter(int32_t index) { POOL_PARANOIA(check()); assert((size_t)index < _counters.size()); _counters[index].count++; POOL_PARANOIA(check()); return _counters[index].count; } int32_t cref_counter_pool::decr_counter(int32_t index) { POOL_PARANOIA(assert((size_t)index < _counters.size())); int32_t c = --_counters[index].count; if (c == 0) { POOL_PARANOIA(check()); /* recycle */ _counters[index].count = _free_index; _free_index = index; VERBOSE_POOL_PARANOIA(dump()); POOL_PARANOIA(check()); } assert(c >= 0); return c; } int32_t cref_counter_pool::count(int32_t index) { POOL_PARANOIA(assert((size_t)index < _counters.size())); return _counters[index].count; } void* cref_counter_pool::data(int32_t index) { POOL_PARANOIA(assert((size_t)index < _counters.size())); return _counters[index].data; } xorp/libxorp/profile.hh0000664000076400007640000002101411540225527015321 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/libxorp/profile.hh,v 1.18 2008/10/02 21:57:32 bms Exp $ #ifndef __LIBXORP_PROFILE_HH__ #define __LIBXORP_PROFILE_HH__ #ifdef XORP_DISABLE_PROFILE # define Profile int # define PROFILE(a) /* do nothing */ #else # define PROFILE(a) a #include "xorp.h" #include "timeval.hh" #include "exceptions.hh" #include "ref_ptr.hh" /** * Container keyed by profile variable holding log entries. * * A helper class used by XORP processes to support profiling. */ class PVariableUnknown : public XorpReasonedException { public: PVariableUnknown(const char* file, size_t line, const string init_why = "") : XorpReasonedException("PVariableUnknown", file, line, init_why) {} }; class PVariableExists : public XorpReasonedException { public: PVariableExists(const char* file, size_t line, const string init_why = "") : XorpReasonedException("PVariableExists", file, line, init_why) {} }; class PVariableNotEnabled : public XorpReasonedException { public: PVariableNotEnabled(const char* file, size_t line, const string init_why = "") : XorpReasonedException("PVariableNotEnabled", file, line, init_why) {} }; class PVariableLocked : public XorpReasonedException { public: PVariableLocked(const char* file, size_t line, const string init_why = "") : XorpReasonedException("PVariableLocked", file, line, init_why) {} }; class PVariableNotLocked : public XorpReasonedException { public: PVariableNotLocked(const char* file, size_t line, const string init_why = "") : XorpReasonedException("PVariableNotLocked", file, line, init_why) {} }; class ProfileLogEntry { public: ProfileLogEntry() {} ProfileLogEntry(TimeVal time, string loginfo) : _time(time), _loginfo(loginfo) {} TimeVal time() {return _time;} string& loginfo() {return _loginfo;} private: TimeVal _time; // Time the profile was recorded. string _loginfo; // The profile data. }; /** * Support for profiling XORP. Save the time that an event occured for * later retrieval. */ class Profile { public: typedef list logentries; // Profiling info class ProfileState { public: ProfileState() : _enabled(false), _locked(false), _log(NULL) {} ProfileState(const string& comment, bool enabled, bool locked, logentries *log) : _comment(comment), _enabled(enabled), _locked(locked), _log(log) {} void set_enabled(bool v) { _enabled = v; } bool enabled() const { return _enabled; } void set_locked(bool v) { _locked = v; } bool locked() const { return _locked; } logentries *logptr() const { return _log; } void zap() const { delete _log; } void set_iterator(const logentries::iterator& i) { _i = i; } void get_iterator(logentries::iterator& i) { i = _i; } int size() const { return _log->size(); } const string& comment() const {return _comment;} private: const string _comment; // Textual description of this variable. bool _enabled; // True, if profiling is enabled. bool _locked; // True, if we are currently reading the log. logentries::iterator _i;// pointer into the log logentries *_log; }; typedef map > profiles; Profile(); ~Profile(); /** * Create a new profile variable. */ void create(const string& pname, const string& comment = "") throw(PVariableExists); /** * Test for this profile variable being enabled. * * @return true if this profile is enabled. */ bool enabled(const string& pname) throw(PVariableUnknown) { // This is the most frequently called method hence make it // inline. As an optimisation if no profiling is enabled don't // perform any string maniplulation or lookups. // If global profiling has not been enabled get out of here. if (0 == _profile_cnt) return false; profiles::iterator i = _profiles.find(pname); // Catch any mispelt pnames. if (i == _profiles.end()) xorp_throw(PVariableUnknown, pname.c_str()); return i->second->enabled(); } /** * Add an entry to the profile log. */ void log(const string& pname, string comment) throw(PVariableUnknown,PVariableNotEnabled); /** * Enable tracing. * * @param profile variable. */ void enable(const string& pname) throw(PVariableUnknown,PVariableLocked); /** * Disable tracing. * @param profile variable. */ void disable(const string& pname) throw(PVariableUnknown); /** * Lock the log in preparation for reading log entries. */ void lock_log(const string& pname) throw(PVariableUnknown,PVariableLocked); /** * Read the next log entry; * @param pname * @param entry log entry * @return True a entry has been returned. */ bool read_log(const string& pname, ProfileLogEntry& entry) throw(PVariableUnknown,PVariableNotLocked); /** * Release the log. */ void release_log(const string& pname) throw(PVariableUnknown,PVariableNotLocked); /** * Clear the profiledata. */ void clear(const string& pname) throw(PVariableUnknown,PVariableLocked); /** * @return A newline separated list of profiling variables along * with the associated comments. */ string get_list() const; private: int _profile_cnt; // Number of variables that are enabled. profiles _profiles; }; #ifdef PROFILE_UTILS_REQUIRED /** * Utility methods to be used by programs providing profiling. */ class ProfileUtils { public: static void transmit_log(const string& pname, XrlStdRouter *xrl_router, const string& instance_name, Profile *profile) { ProfileLogEntry ple; if (profile->read_log(pname, ple)) { TimeVal t = ple.time(); XrlProfileClientV0p1Client pc(xrl_router); pc.send_log(instance_name.c_str(), pname, t.sec(), t.usec(), ple.loginfo(), callback(ProfileUtils::transmit_callback, pname, xrl_router, instance_name, profile)); } else { // Unlock the log entry. profile->release_log(pname); ProfileUtils::transmit_finished(pname, xrl_router, instance_name); } } static void transmit_callback(const XrlError& error, const string pname, XrlStdRouter *xrl_router, const string instance_name, Profile *profile) { if (XrlError::OKAY() != error) { XLOG_WARNING("%s", error.error_msg()); // Unlock the log entry. profile->release_log(pname); return; } ProfileUtils::transmit_log(pname, xrl_router, instance_name, profile); } static void transmit_finished(const string& pname, XrlStdRouter *xrl_router, const string& instance_name) { debug_msg("pname = %s instance_name = %s\n", pname.c_str(), instance_name.c_str()); XrlProfileClientV0p1Client pc(xrl_router); pc.send_finished(instance_name.c_str(), pname, callback(ProfileUtils::transmit_finished_callback, pname)); } static void transmit_finished_callback(const XrlError& error, const string /*pname*/) { if (XrlError::OKAY() != error) XLOG_WARNING("%s", error.error_msg()); } private: ProfileUtils(); // Don't allow instantiation ProfileUtils(const ProfileUtils&); }; #endif // PROFILE_UTILS_REQUIRED // simple but fast profiling support #define SP_MAX_SAMPLES 128 namespace SP { typedef uint64_t SAMPLE; typedef SAMPLE (*SAMPLER)(void); void set_sampler(SAMPLER sampler); void add_sample(const char* desc); void print_samples(); SAMPLE sample(); SAMPLE sampler_time(); #if defined(__i386__) && defined(__GNUC__) #define __HAVE_TSC__ SAMPLE sampler_tsc(); #endif } // namespace SP #endif // profile #endif // __LIBXORP_PROFILE_HH__ xorp/libxorp/random.h0000664000076400007640000000304211421137511014763 0ustar greearbgreearb/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- */ /* vim:set sts=4 ts=8: */ /* * Copyright (c) 2001-2009 XORP, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License, Version * 2.1, June 1999 as published by the Free Software Foundation. * Redistribution and/or modification of this program under the terms of * any other version of the GNU Lesser General Public License is not * permitted. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, * see the GNU Lesser General Public License, Version 2.1, a copy of * which can be found in the XORP LICENSE.lgpl file. * * XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; * http://xorp.net */ /* * $XORP: xorp/libxorp/random.h,v 1.10 2008/10/02 21:57:32 bms Exp $ */ #ifndef __LIBXORP_RANDOM_H__ #define __LIBXORP_RANDOM_H__ #ifdef __cplusplus extern "C" { #endif /* * Define the maximum bound of the random() function * as per 4.2BSD / SUSV2: * http://www.opengroup.org/onlinepubs/007908799/xsh/initstate.html */ #define XORP_RANDOM_MAX 0x7FFFFFFF /* 2^31 - 1 in 2's complement */ long xorp_random(void); void xorp_srandom(unsigned long); char* xorp_initstate(unsigned long, char *, long); char* xorp_setstate(char *); void xorp_srandomdev(void); #ifdef __cplusplus } #endif #endif /* __LIBXORP_RANDOM_H__ */ xorp/libxorp/xorp_noncopyable.hh0000664000076400007640000000266411540225530017246 0ustar greearbgreearb/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- */ /* vim:set sts=4 ts=8: */ /* * Copyright (c) 2001-2011 XORP, Inc and Others * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License, Version * 2.1, June 1999 as published by the Free Software Foundation. * Redistribution and/or modification of this program under the terms of * any other version of the GNU Lesser General Public License is not * permitted. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, * see the GNU Lesser General Public License, Version 2.1, a copy of * which can be found in the XORP LICENSE.lgpl file. * * XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; * http://xorp.net */ // Idea from boost::noncopyable /** Class is not supposed to be coppied, so we don't implement the copy * methods, but we do declare them. This will cause a compile error if anyone * ever tries to copy objects descended from xorp_noncopyable. */ #ifndef __XORP_NONCOPYABLE__INC_ #define __XORP_NONCOPYABLE__INC_ class xorp_noncopyable { private: xorp_noncopyable(const xorp_noncopyable& rhs); xorp_noncopyable& operator=(const xorp_noncopyable& rhs); public: xorp_noncopyable() { } }; #endif xorp/libxorp/task.hh0000664000076400007640000001276111625524306014635 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2006-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __LIBXORP_TASK_HH__ #define __LIBXORP_TASK_HH__ #include "debug.h" #include "round_robin.hh" #include "callback.hh" class TaskList; class TaskNode; class XorpTask; typedef XorpCallback0::RefPtr OneoffTaskCallback; typedef XorpCallback0::RefPtr RepeatedTaskCallback; typedef XorpCallback1::RefPtr BasicTaskCallback; class TaskNode : public NONCOPYABLE, public RoundRobinObjBase { public: TaskNode(TaskList* task_list, BasicTaskCallback cb); virtual ~TaskNode(); void add_ref(); void release_ref(); void schedule(int priority, int weight); void reschedule(); void unschedule(); int priority() const { return _priority; } int weight() const { return _weight; } virtual void run(XorpTask &) {}; // Implemented by children private: TaskList* _task_list; // TaskList this node is associated with BasicTaskCallback _cb; int _ref_cnt; // Number of referring XorpTask objects int _priority; // Scheduling priority int _weight; // Scheduling weight }; class XorpTask { public: XorpTask() : _task_node(NULL) {} XorpTask(const XorpTask& xorp_task); XorpTask(TaskNode* task_node); XorpTask(TaskList* task_list, BasicTaskCallback cb); ~XorpTask(); // // Task/Timer priorities. Those are suggested values. // static const int PRIORITY_HIGHEST = 0; static const int PRIORITY_XRL_KEEPALIVE = 1; static const int PRIORITY_HIGH = 2; static const int PRIORITY_DEFAULT = 4; static const int PRIORITY_BACKGROUND = 7; static const int PRIORITY_LOWEST = 9; static const int PRIORITY_INFINITY = 255; // // Task/Timer weights. Those are suggested values. // static const int WEIGHT_DEFAULT = 1; XorpTask& operator=(const XorpTask& other); void schedule(int priority, int weight); void reschedule(); void unschedule(); bool scheduled() const; private: TaskNode* _task_node; }; class TaskList { public: ~TaskList(); /** * Expire all pending @ref XorpTask objects associated with @ref * TaskList. */ void run(); /** * Create a XorpTask that will be scheduled only once. * * @param cb callback object that is invoked when task is run. * * @return the @ref XorpTask created. */ XorpTask new_oneoff_task(const OneoffTaskCallback& cb, int priority = XorpTask::PRIORITY_DEFAULT, int weight = XorpTask::WEIGHT_DEFAULT); /** * Create a repeated XorpTask. * * @param cb callback object that is invoked when task is run. * If the callback returns true, the task will continue to run, * otherwise it will be unscheduled. * * @return the @ref XorpTask created. */ XorpTask new_task(const RepeatedTaskCallback& cb, int priority = XorpTask::PRIORITY_DEFAULT, int weight = XorpTask::WEIGHT_DEFAULT); /** * Get the priority of the highest priority runnable task. * * @return the priority (lowest value is best priority). */ int get_runnable_priority() const; void schedule_node(TaskNode* node); void unschedule_node(TaskNode* node); bool empty() const; private: RoundRobinQueue* find_round_robin(int priority); map _rr_list; }; // ---------------------------------------------------------------------------- // inline Task methods inline XorpTask::XorpTask(TaskList* task_list, BasicTaskCallback cb) : _task_node(new TaskNode(task_list, cb)) { if (_task_node != NULL) _task_node->add_ref(); } inline XorpTask::XorpTask(const XorpTask& xorp_task) : _task_node(xorp_task._task_node) { debug_msg("XorpTask copy constructor %p, n = %p\n", this, _task_node); if (_task_node != NULL) _task_node->add_ref(); } inline XorpTask::~XorpTask() { debug_msg("XorpTask destructor %p, n = %p\n", this, _task_node); if (_task_node != NULL) _task_node->release_ref(); } inline XorpTask::XorpTask(TaskNode* task_node) : _task_node(task_node) { debug_msg("XorpTask constructor %p, n = %p\n", this, _task_node); if (_task_node) _task_node->add_ref(); } inline XorpTask& XorpTask::operator=(const XorpTask& other) { if (other._task_node != NULL) other._task_node->add_ref(); if (_task_node != NULL) _task_node->release_ref(); _task_node = other._task_node; return *this; } inline void XorpTask::schedule(int priority, int weight) { _task_node->schedule(priority, weight); } inline void XorpTask::reschedule() { _task_node->reschedule(); } #endif // __LIBXORP_TASK_HH__ xorp/libxorp/timer.cc0000664000076400007640000003174211635757530015011 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8 sw=4: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // Portions of this code originally derived from: // timer.{cc,hh} -- portable timers. Linux kernel module uses Linux timers // Eddie Kohler // // Copyright (c) 1999-2000 Massachusetts Institute of Technology // Copyright (c) 2011-2011 XORP, Inc and Others // Copyright (c) 2011-2011 XORP, Inc and Others #include "libxorp_module.h" #include "xorp.h" #include "xlog.h" #include "timer.hh" #include "clock.hh" // Implementation Notes: // // Event scheduling happens through the TimerList. The TimerList is // comprised of TimerNode's that contain an expiry time, a callback to // make when the timer expires, and a thunk value to pass with the // callback. TimerNode's that are not scheduled are not part of the // TimerList, but remain "associated" with their originating list so // can be rescheduled by calling TimerNode methods (eg // reschedule_after()). // // User applications deal with XorpTimer objects that are pointers to // TimerNodes. There is some magic with XorpTimer objects: they enforce // reference counting of TimerNodes. When there are no XorpTimers // referring to a particular TimerNode it is de-scheduled and // destroyed. This makes it difficult, though not impossible, for // timer event callbacks to be invoked that pass invalid stack or heap // memory into the callback. Under normal usage we expect XorpTimer // objects and the associated thunk values to have similar scope so // they both exist and disappear at the same time. //----------------------------------------------------------------------------- // Constants // ---------------------------------------------------------------------------- // TimerNode methods TimerNode::TimerNode(TimerList* l, BasicTimerCallback cb) : _ref_cnt(0), _cb(cb), _list(l) { } TimerNode::~TimerNode() { unschedule(); } void TimerNode::add_ref() { _ref_cnt++; } void TimerNode::release_ref() { if (--_ref_cnt <= 0) delete this; } void TimerNode::expire(XorpTimer& xorp_timer, void*) { // XXX: Implemented by children. Might be called only for custom timers. if (! _cb.is_empty()) _cb->dispatch(xorp_timer); } bool TimerNode::time_remaining(TimeVal& remain) const { TimeVal now; assert(_list); _list->current_time(now); remain = expiry(); if (remain <= now) remain = TimeVal::ZERO(); else remain -= now; return (true); } void TimerNode::unschedule() { if (scheduled()) _list->unschedule_node(this); } void TimerNode::schedule_at(const TimeVal& t, int priority) { assert(_list); unschedule(); _expires = t; _priority = priority; _list->schedule_node(this); } void TimerNode::schedule_after(const TimeVal& wait, int priority) { assert(_list); unschedule(); TimeVal now; _list->current_time(now); _expires = now + wait; _priority = priority; _list->schedule_node(this); } void TimerNode::reschedule_after(const TimeVal& wait) { assert(_list); unschedule(); _expires = _expires + wait; _list->schedule_node(this); } // ---------------------------------------------------------------------------- // Specialized Timers. These are what the XorpTimer objects returned by // the TimerList XorpTimer creation methods (e.g. TimerList::new_oneoff_at(), etc) // actually refer to/ class OneoffTimerNode2 : public TimerNode { public: OneoffTimerNode2(TimerList *l, const OneoffTimerCallback& cb) : TimerNode (l, callback(this, &OneoffTimerNode2::expire, (void*)0)), _cb(cb) {} private: OneoffTimerCallback _cb; void expire(XorpTimer&, void*) { _cb->dispatch(); } }; class PeriodicTimerNode2 : public TimerNode { public: PeriodicTimerNode2(TimerList *l, const PeriodicTimerCallback& cb, const TimeVal& period) : TimerNode(l, callback(this, &PeriodicTimerNode2::expire, (void*)0)), _cb(cb), _period(period) { } private: PeriodicTimerCallback _cb; TimeVal _period; void expire(XorpTimer& t, void*) { if (_cb->dispatch()) t.reschedule_after(_period); } }; // ---------------------------------------------------------------------------- // TimerList implemention TimerList* the_timerlist = NULL; int timerlist_instance_count; TimerList::TimerList(ClockBase* clock) : _clock(clock), _observer(NULL) { assert(the_timerlist == NULL); assert(timerlist_instance_count == 0); #ifdef HOST_OS_WINDOWS _hirestimer = CreateWaitableTimer(NULL, TRUE, NULL); assert(_hirestimer != NULL); #endif // HOST_OS_WINDOWS the_timerlist = this; timerlist_instance_count++; } TimerList::~TimerList() { //fprintf(stderr, "heaplist-size: %i\n", (int)(_heaplist.size())); // Delete all of the heaps we've previously created map::const_iterator hi; Heap* tmp = NULL; for (hi = _heaplist.begin(); hi != _heaplist.end(); ++hi) { tmp = hi->second; //fprintf(stderr, "deleting heap: %p\n", tmp); fflush(stderr); delete tmp; } _heaplist.clear(); #ifdef HOST_OS_WINDOWS if (_hirestimer != NULL) CloseHandle(_hirestimer); //timeEndPeriod(1); #endif // HOST_OS_WINDOWS timerlist_instance_count--; the_timerlist = NULL; } TimerList* TimerList::instance() { return the_timerlist; } void TimerList::current_time(TimeVal& now) const { _clock->current_time(now); } void TimerList::advance_time() { _clock->advance_time(); } void TimerList::system_gettimeofday(TimeVal* tv) { TimerList* instance = TimerList::instance(); if (!instance) { SystemClock s; TimerList timer = TimerList(&s); timer.system_gettimeofday(tv); } else { instance->advance_time(); instance->current_time(*tv); } } /* * Call the underlying system's 'alertable wait' function. */ void TimerList::system_sleep(const TimeVal& tv) { TimerList* instance = TimerList::instance(); #ifdef HOST_OS_WINDOWS DWORD ms = tv.to_ms(); if (ms == 0 || ms > 10) { Sleep(ms); } else { FILETIME ft; tv.copy_out(ft); assert(instance->_hirestimer != NULL); reinterpret_cast(&ft)->QuadPart *= -1; SetWaitableTimer(instance->_hirestimer, (LARGE_INTEGER *)&ft, 0, NULL, NULL, FALSE); WaitForSingleObject(instance->_hirestimer, INFINITE); } #else // ! HOST_OS_WINDOWS if (tv.sec() > 0) sleep(tv.sec()); if (tv.usec() > 0) usleep(tv.usec()); #endif // ! HOST_OS_WINDOWS instance->advance_time(); } Heap* TimerList::find_heap(int priority) { map::iterator hi = _heaplist.find(priority); if (hi == _heaplist.end()) { Heap* h = new Heap(true); //printf("created new heap in find_heap, ptr: %p\n", h); _heaplist[priority] = h; return h; } else { return hi->second; } } XorpTimer TimerList::new_oneoff_at(const TimeVal& tv, const OneoffTimerCallback& cb, int priority) { TimerNode* n = new OneoffTimerNode2(this, cb); n->schedule_at(tv, priority); return XorpTimer(n); } XorpTimer TimerList::new_oneoff_after(const TimeVal& wait, const OneoffTimerCallback& cb, int priority) { TimerNode* n = new OneoffTimerNode2(this, cb); n->schedule_after(wait, priority); return XorpTimer(n); } void TimerList::remove_timer(XorpTimer& t) { if (t.node()) t.node()->unschedule(); } XorpTimer TimerList::new_periodic(const TimeVal& wait, const PeriodicTimerCallback& cb, int priority) { TimerNode* n = new PeriodicTimerNode2(this, cb, wait); n->schedule_after(wait, priority); return XorpTimer(n); } static void set_flag_hook(bool* flag_ptr, bool to_value) { assert(flag_ptr); *flag_ptr = to_value; } XorpTimer TimerList::set_flag_at(const TimeVal& tv, bool *flag_ptr, bool to_value, int priority) { assert(flag_ptr); *flag_ptr = false; return new_oneoff_at(tv, callback(set_flag_hook, flag_ptr, to_value), priority); } XorpTimer TimerList::set_flag_after(const TimeVal& wait, bool *flag_ptr, bool to_value, int priority) { assert(flag_ptr); *flag_ptr = false; return new_oneoff_after(wait, callback(set_flag_hook, flag_ptr, to_value), priority); } int TimerList::get_expired_priority() const { TimeVal now; current_time(now); // // Run through in increasing priority until we find a timer to expire // map::const_iterator hi; for (hi = _heaplist.begin(); hi != _heaplist.end(); ++hi) { int priority = hi->first; struct Heap::heap_entry *n = hi->second->top(); if (n != 0 && now >= n->key) { return priority; } } return XorpTask::PRIORITY_INFINITY; } void TimerList::run() { // // Run through in increasing priority until we find a timer to expire // map::iterator hi; for (hi = _heaplist.begin(); hi != _heaplist.end(); ++hi) { int priority = hi->first; if(expire_one(priority)) { return; } } } /** * Expire one timer. * * The timer we expire is the highest priority (lowest priority * number) timer that is less than or equal to the the parameter * worst_priority. */ bool TimerList::expire_one(int worst_priority) { static const TimeVal WAY_BACK_GAP(15, 0); TimeVal now; current_time(now); struct Heap::heap_entry *n; map::iterator hi; for (hi = _heaplist.begin(); hi != _heaplist.end() && hi->first <= worst_priority; ++hi) { Heap* heap = hi->second; while ((n = heap->top()) != 0 && n->key <= now) { // // Throw a wobbly if we're a long way behind. // // We shouldn't write code that generates this message, it // means too long was spent in a timer callback or handling a // file descriptor event. We can expect bad things (tm) to be // correlated with the appearance of this message. // TimeVal tardiness = now - n->key; if (tardiness > WAY_BACK_GAP) { XLOG_WARNING("Timer Expiry *much* later than scheduled: " "behind by %s seconds", tardiness.str().c_str()); } TimerNode *t = static_cast(n->object); heap->pop(); // _hook() requires a XorpTimer as first argument, we have // only a timernode, so we have to create a temporary // timer to invoke the hook. XorpTimer placeholder(t); t->expire(placeholder, 0); return true; } } return false; } bool TimerList::empty() const { bool result = true; acquire_lock(); map::const_iterator hi; for (hi = _heaplist.begin(); hi != _heaplist.end(); ++hi) { if (hi->second->top() != 0) result = false; } release_lock(); return result; } size_t TimerList::size() const { size_t result = 0; acquire_lock(); map::const_iterator hi; for (hi = _heaplist.begin(); hi != _heaplist.end(); ++hi) { result += hi->second->size(); } release_lock(); return result; } bool TimerList::get_next_delay(TimeVal& tv) const { struct Heap::heap_entry *t = NULL; acquire_lock(); // find the earliest key map::const_iterator hi; for (hi = _heaplist.begin(); hi != _heaplist.end(); ++hi) { struct Heap::heap_entry *tmp_t = hi->second->top(); if (tmp_t == 0) continue; if (t == 0 || (tmp_t->key < t->key)) t = tmp_t; } release_lock(); if (t == 0) { tv = TimeVal::MAXIMUM(); return false; } else { TimeVal now; _clock->current_time(now); if (t->key > now) { // next event is in the future tv = t->key - now ; } else { // next event is already in the past, return 0.0 tv = TimeVal::ZERO(); } return true; } } void TimerList::schedule_node(TimerNode* n) { acquire_lock(); Heap *heap = find_heap(n->priority()); heap->push(n->expiry(), n); release_lock(); if (_observer) _observer->notify_scheduled(n->expiry()); assert(n->scheduled()); } void TimerList::unschedule_node(TimerNode *n) { acquire_lock(); Heap *heap = find_heap(n->priority()); heap->pop_obj(n); release_lock(); if (_observer) _observer->notify_unscheduled(n->expiry()); } void TimerList::set_observer(TimerListObserverBase& obs) { _observer = &obs; _observer->_observed = this; } void TimerList::remove_observer() { if (_observer) _observer->_observed = NULL; _observer = NULL; } TimerListObserverBase::~TimerListObserverBase() { if (_observed) _observed->remove_observer(); } xorp/libxorp/service.hh0000664000076400007640000001701611635757530015341 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __LIBXORP_SERVICE_HH__ #define __LIBXORP_SERVICE_HH__ #include "bug_catcher.hh" /** * Enumeration of states objects derived from ServiceBase may be in. */ enum ServiceStatus { SERVICE_READY = 0x001, // Ready for startup SERVICE_STARTING = 0x002, // Starting up SERVICE_RUNNING = 0x004, // Running, service operational SERVICE_PAUSING = 0x008, // Transitioning to paused state SERVICE_PAUSED = 0x010, // Paused, non-operational SERVICE_RESUMING = 0x020, // Resuming from pause SERVICE_SHUTTING_DOWN = 0x040, // Transitioning to shutdown SERVICE_SHUTDOWN = 0x080, // Shutdown, non-operational SERVICE_FAILED = 0x100, // Failed, non-operational SERVICE_ALL = SERVICE_READY | SERVICE_STARTING | SERVICE_RUNNING | SERVICE_PAUSING | SERVICE_PAUSED | SERVICE_RESUMING | SERVICE_SHUTTING_DOWN | SERVICE_SHUTDOWN | SERVICE_FAILED }; /** * Get text description of enumerated service status. * * @param s service status to recover name for. */ const char* service_status_name(ServiceStatus s); class ServiceChangeObserverBase; /** * @short Base class for Services. * * This class provides a base for services within Xorp processes. A * service instance is an entity that can logically started and * stopped and typically needs some asynchronous processing in order * to start and stop. An example service within a routing process * would be a RIB communicator service, which needs to co-ordinate * with the RIB which is within a different process and may be on a * different machine. * * A service may be started and shutdown by calling @ref startup() and * @ref shutdown(). The status of a service may be determined by * calling @ref status(). Additional notes on the current status may be * obtained by calling @ref status_note(). * * Synchronous service status changes may be received through the @ref * ServiceChangeObserverBase class. Instances of objects derived from * this class can register for status change notifications in a * Service instance by calling @ref set_observer(). */ class ServiceBase : public BugCatcher { public: ServiceBase(const string& name = "Unknown"); virtual ~ServiceBase() = 0; /** * Start service. Service should transition from SERVICE_READY to * SERVICE_STARTING immediately and onto SERVICE_RUNNING or * SERVICE_FAILED in the near future. * * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int startup() = 0; /** * Shutdown service. Service should transition from SERVICE_RUNNING to * SERVICE_SHUTTING_DOWN immediately and onto SERVICE_SHUTDOWN or * SERVICE_FAILED in the near future. * * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int shutdown() = 0; /** * Reset service. Service should transition in SERVICE_READY from * whichever state it is in. * * The default implementation always returns false as there is no * default behaviour. * * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int reset(); /** * Pause service. Service should transition from SERVICE_RUNNING to * SERVICE_PAUSING and asynchronously into SERVICE_PAUSED. * * The default implementation always returns false as there is no * default behaviour. * * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int pause(); /** * Resume paused service. Service should transition from SERVICE_PAUSED * to SERVICE_PAUSING and asynchronously into SERVICE_RUNNING. * * The default implementation always returns false as there is no * default behaviour. * * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int resume(); /** * Get name of service. * * @return name of service. May be empty if not set in constructor. */ const string& service_name() const { return _name; } /** * Get the current status. */ ServiceStatus status() const { return _status; } /** * Get annotation associated with current status. The annotation when * set is an explanation of the state, ie "waiting for Y" */ const string& status_note() const { return _note; } /** * Get a character representation of the current service status. */ const char* status_name() const; /** * Set service status change observer. The observer will receive * synchronous notifications of changes in service state. * * @param so service change observer to add. * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_observer(ServiceChangeObserverBase* so); /** * Remove service status change observer. * * @param so observer to remove. * @return XORP_OK on success, otherwise XORP_ERROR. */ int unset_observer(ServiceChangeObserverBase* so); protected: /** * Set current status. * * @param status new status. * @param note comment on new service status. */ void set_status(ServiceStatus status, const string& note); /** * Set current status and clear status note. * * @param status new status. */ void set_status(ServiceStatus status); protected: string _name; ServiceStatus _status; string _note; ServiceChangeObserverBase* _observer; }; /** * @short Base class for service status change observer. */ class ServiceChangeObserverBase { public: virtual ~ServiceChangeObserverBase() = 0; virtual void status_change(ServiceBase* service, ServiceStatus old_status, ServiceStatus new_status) = 0; }; /** * @short Selective Change Observer. * * Forwards limited subset of status changes to a status change observer. */ class ServiceFilteredChangeObserver : public ServiceChangeObserverBase { public: /** * Constructor. * * Only changes from the states represented in @ref from_mask to * the states represented in @ref to_mask are reported. * * @param child recipient of status changes. * @param from_mask mask of states left to trigger changes. * @param to_mask mask of states entered to trigger changes. */ ServiceFilteredChangeObserver(ServiceChangeObserverBase* child, ServiceStatus from_mask, ServiceStatus to_mask); protected: void status_change(ServiceBase* service, ServiceStatus old_status, ServiceStatus new_status); protected: ServiceChangeObserverBase* _child; ServiceStatus _from_mask; ServiceStatus _to_mask; }; #endif // __LIBXORP_SERVICE_HH__ xorp/libxorp/strlcpy.c0000664000076400007640000000321611421137511015201 0ustar greearbgreearb/* $OpenBSD: strlcpy.c,v 1.10 2005/08/08 08:05:37 espie Exp $ */ /* vim:set sts=4 ts=8: */ /* * Copyright (c) 1998 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "libxorp/xorp.h" #ifndef HAVE_STRLCPY #include #include /* * Copy src to string dst of size siz. At most siz-1 characters * will be copied. Always NUL terminates (unless siz == 0). * Returns strlen(src); if retval >= siz, truncation occurred. */ size_t strlcpy(char *dst, const char *src, size_t siz) { char *d = dst; const char *s = src; size_t n = siz; /* Copy as many bytes as will fit */ if (n != 0 && --n != 0) { do { if ((*d++ = *s++) == 0) break; } while (--n != 0); } /* Not enough room in dst, add NUL and traverse rest of src */ if (n == 0) { if (siz != 0) *d = '\0'; /* NUL-terminate dst */ while (*s++) ; } return(s - src - 1); /* count does not include NUL */ } #endif /* ! HAVE_STRLCPY */ xorp/libxorp/ipv6net.hh0000664000076400007640000000221411421137511015246 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/libxorp/ipv6net.hh,v 1.10 2008/10/02 21:57:31 bms Exp $ #ifndef __LIBXORP_IPV6NET_HH__ #define __LIBXORP_IPV6NET_HH__ #include "ipv6.hh" #include "ipnet.hh" typedef IPNet IPv6Net; #endif // __LIBXORP_IPV6NET_HH__ xorp/libxorp/vif.hh0000664000076400007640000004232111540224230014437 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/libxorp/vif.hh,v 1.23 2008/10/02 21:57:36 bms Exp $ #ifndef __LIBXORP_VIF_HH__ #define __LIBXORP_VIF_HH__ #include "xorp.h" #include "ipv4.hh" #include "ipv6.hh" #include "ipvx.hh" #include "ipvxnet.hh" /** * @short Virtual interface address class. * * VifAddr holds information about an address of a virtual interface. * A virtual interface may have more than one VifAddr. */ class VifAddr { public: /** * Constructor for a given address. * * @param ipvx_addr the interface address. */ explicit VifAddr(const IPvX& ipvx_addr); /** * Constructor for a given address, and its associated addresses. * * @param ipvx_addr the interface address. * @param ipvx_subnet_addr the subnet address. * @param ipvx_broadcast_addr the broadcast address (if the interface * is broadcast-capable). * @param ipvx_peer_addr the peer address (if the interface is * point-to-point). */ VifAddr(const IPvX& ipvx_addr, const IPvXNet& ipvx_subnet_addr, const IPvX& ipvx_broadcast_addr, const IPvX& ipvx_peer_addr); #ifdef XORP_USE_USTL VifAddr(); // AF_INET #endif /** * Get the interface address. * * @return the interface address. */ const IPvX& addr() const { return (_addr); } /** * Get the subnet address. * * @return the subnet address of the interface. */ const IPvXNet& subnet_addr() const { return (_subnet_addr); } /** * Get the broadcast address. * * The broadcast address is valid only if the interface is broadcast * capable. * * @return the broadcast address of the interface. */ const IPvX& broadcast_addr() const { return (_broadcast_addr); } /** * Get the peer address. * * The peer address is valid only if the interface is point-to-point. * * @return the peer address of the interface. */ const IPvX& peer_addr() const { return (_peer_addr); } /** * Set the interface address. * * @param v the interface address to set to the interface. */ void set_addr(const IPvX& v) { _addr = v; } /** * Set the subnet address. * * @param v the subnet address to set to the interface. */ void set_subnet_addr(const IPvXNet& v) { _subnet_addr = v; } /** * Set the broadcast address. * * @param v the broadcast address to set to the interface. */ void set_broadcast_addr(const IPvX& v) { _broadcast_addr = v; } /** * Set the peer address. * * @param v the peer address to set to the interface. */ void set_peer_addr(const IPvX& v) { _peer_addr = v; } /** * Test whether if an IPvX address is same as my address. * * @param ipvx_addr the address to test whether is the same as my * interface address. * * @return true if @ref ipvx_addr is same as my interface address, * otherwise false. */ bool is_my_addr(const IPvX& ipvx_addr) const { return (addr() == ipvx_addr); } /** * Test if a given subnet address is a subset of my subnet address. * * @param ipvxnet the subnet address to test whether is a subset of * my subnet address. * * @return true if @ref ipvxnet is a subset of my subnet address, * otherwise false. */ bool is_same_subnet(const IPvXNet& ipvxnet) const; /** * Test whether an address belongs to my subnet address. * * @param ipvx_addr the address to test whether it belongs to my subnet * address. * @return true if @ref ipvx_addr belongs to my subnet address, * otherwise false. */ bool is_same_subnet(const IPvX& ipvx_addr) const; /** * Convert this address from binary form to presentation format. * * @return C++ string with the human-readable ASCII representation * of the address. */ string str() const; /** * Equality Operator * * @param other the right-hand operand to compare against. * @return true if the left-hand operand is numerically same as the * right-hand operand. */ bool operator==(const VifAddr& other) const; private: IPvX _addr; // IP address of the vif IPvXNet _subnet_addr; // Subnet address on the vif IPvX _broadcast_addr; // Network broadcast address IPvX _peer_addr; // Peer address (on p2p links) }; /** * @short Virtual Interface class. * * Vif holds information about a virtual interface. A Vif may * represent a physical interface, or may represent more abstract * entities such as the Discard or Unreachable interface, or a VLAN * on a physical interface. */ class Vif { public: /** * Constructor for a given virtual interface name. * * @param vifname string representation of the virtual interface * (e.g., "port 0"). * @param ifname string representation of associated interface. */ explicit Vif(const string& vifname, const string& ifname = string("")); #ifdef XORP_USE_USTL Vif() { } #endif /** * Constructor to clone a Vif. * * @param vif the virtual interface to clone. */ Vif(const Vif& vif); /** * Destructor */ virtual ~Vif(); /** * Convert this Vif from binary form to presentation format. * * @return C++ string with the human-readable ASCII representation * of the Vif. */ string str() const; /** * Equality Operator * * @param other the right-hand operand to compare against. * @return true if the left-hand operand is numerically same as the * right-hand operand. */ bool operator==(const Vif& other) const; /** * Get the vif name. * * @return a string representation of the vif name. */ const string& name() const { return _name; } /** * Get the name of the physical interface associated with vif. * * @return string representation of interface name. */ const string& ifname() const { return _ifname; } /** * Set the name of the physical interface associated with vif. * * @param ifname */ void set_ifname(const string& ifname) { _ifname = ifname; } /** * Get the physical interface index. * * @return the physical interface index (if applicable). */ uint32_t pif_index() const { return (_pif_index); } /** * Set the physical interface index. * * @param v the value of the physical interface index to set to. */ void set_pif_index(uint32_t v) { _pif_index = v; } /** * Various vif_index related values. */ enum { VIF_INDEX_INVALID = ((uint32_t)~0), VIF_INDEX_MAX = ((uint32_t)~0) }; /** * Get the virtual interface index. * * @return the virtual interface index. */ uint32_t vif_index() const { return (_vif_index); } /** * Set the virtual interface index. * * @param v the value of the virtual interface index to set to. */ void set_vif_index(uint32_t v) { _vif_index = v; } /** * Test if this vif is a PIM Register interface. * * @return true if this vif is a PIM Register interface, otherwise false. */ bool is_pim_register() const { return _is_pim_register; } /** * Test if this vif is a point-to-point interface. * * @return true if this vif is a point-to-point interface, otherwise false. */ bool is_p2p() const { return _is_p2p; } /** * Test if this vif is a loopback interface. * * @return true if this vif is a loopback interface, otherwise false. */ bool is_loopback() const { return _is_loopback; } /** * Test if this vif is a discard interface. * * @return true if this vif is a discard interface, otherwise false. */ bool is_discard() const { return _is_discard; } /** * Test if this vif is an unreachable interface. * * @return true if this vif is an unreachable interface, otherwise false. */ bool is_unreachable() const { return _is_unreachable; } /** * Test if this vif is a management interface. * * @return true if this vif is a management interface, otherwise false. */ bool is_management() const { return _is_management; } /** * Test if this vif is multicast capable. * * @return true if this vif is multicast capable, otherwise false. */ bool is_multicast_capable() const { return _is_multicast_capable; } /** * Test if this vif is broadcast capable. * * @return true if this vif is broadcast capable, otherwise false. */ bool is_broadcast_capable() const { return _is_broadcast_capable; } /** * Test if the underlying vif is UP. * * An example of an underlying vif is the corresponding interface * inside the kernel, or the MFEA interface which a PIM interface * matches to. * * @return true if the underlying vif is UP (when applicable), otherwise * false. */ bool is_underlying_vif_up() const { return _is_underlying_vif_up; } /** * Get the MTU of the vif. * * @return the MTU of the vif. */ uint32_t mtu() const { return _mtu; } /** * Set/reset the vif as a PIM Register interface. * * @param v if true, then set this vif as a PIM Register interface, * otherwise reset it. */ void set_pim_register(bool v) { _is_pim_register = v; } /** * Set/reset the vif as a point-to-point interface. * * @param v if true, then set this vif as a point-to-point interface, * otherwise reset it. */ void set_p2p(bool v) { _is_p2p = v; } /** * Set/reset the vif as a loopback interface. * * @param v if true, then set this vif as a loopback interface, * otherwise reset it. */ void set_loopback(bool v) { _is_loopback = v; } /** * Set/reset the vif as a discard interface. * * @param v if true, then set this vif as a discard interface, * otherwise reset it. */ void set_discard(bool v) { _is_discard = v; } /** * Set/reset the vif as an unreachable interface. * * @param v if true, then set this vif as an unreachable interface, * otherwise reset it. */ void set_unreachable(bool v) { _is_unreachable = v; } /** * Set/reset the vif as a management interface. * * @param v if true, then set this vif as a management interface, * otherwise reset it. */ void set_management(bool v) { _is_management = v; } /** * Set/reset the vif as multicast capable. * * @param v if true, then set this vif as multicast capable, * otherwise reset it. */ void set_multicast_capable(bool v) { _is_multicast_capable = v; } /** * Set/reset the vif as broadcast capable. * * @param v if true, then set this vif as broadcast capable, * otherwise reset it. */ void set_broadcast_capable(bool v) { _is_broadcast_capable = v; } /** * Set/reset the underlying vif status (when applicable). * * An example of an underlying vif is the corresponding interface * inside the kernel, or the MFEA interface which a PIM interface * matches to. * * @param v if true, then set the underlying vif status as UP, * otherwise the underlying vif status is set to DOWN. */ void set_underlying_vif_up(bool v) { _is_underlying_vif_up = v; } /** * Set the MTU of the vif. * * @param v the MTU of the vif. */ void set_mtu(uint32_t v) { _mtu = v; } /** * Get the list of all addresses for this vif. * * @return the list of all addresses for this vif (@ref VifAddr). */ const list& addr_list() const { return _addr_list; } /** * Get the first vif address. * * @return a pointer to the first valid interface address of this vif, * or NULL if no addresses. */ const IPvX* addr_ptr() const; /** * Add a VifAddr address to the interface. * * @param vif_addr the VifAddr (@ref VifAddr) to add to the list * of addresses for this vif. * * @return XORP_OK if a new address, otherwise XORP_ERROR. */ int add_address(const VifAddr& vif_addr); /** * Add an IPvX address and all related information to the interface. * * @param ipvx_addr the interface address. * @param ipvxnet_subnet_addr the subnet address. * @param ipvx_broadcast_addr the broadcast address. * @param ipvx_peer_addr the peer address. * @return XORP_OK if a new address, otherwise XORP_ERROR. */ int add_address(const IPvX& ipvx_addr, const IPvXNet& ipvxnet_subnet_addr, const IPvX& ipvx_broadcast_addr, const IPvX& ipvx_peer_addr); /** * Add an IPvX address to the interface. * * @param ipvx_addr the interface address. * @return XORP_OK if a new address, otherwise XORP_ERROR. */ int add_address(const IPvX& ipvx_addr); /** * Delete an IPvX address from the interface. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int delete_address(const IPvX& ipvx_addr); /** * Find a VifAddr that corresponds to an IPvX address. * * @param ipvx_addr the IPvX address to search for. * @return a pointer to the VifAddr for address @ref ipvx_addr * if found, otherwise NULL. */ VifAddr *find_address(const IPvX& ipvx_addr); /** * Find a const VifAddr that corresponds to an IPvX address. * * @param ipvx_addr the IPvX address to search for. * @return a const pointer to the VifAddr for address @ref ipvx_addr * if found, otherwise NULL. */ const VifAddr *find_address(const IPvX& ipvx_addr) const; /** * Test if an IPvX address belongs to this vif. * * @param ipvx_addr the IPvX address to test whether belongs to this vif. * @return true if @ref ipvx_addr belongs to this vif, otherwise false. */ bool is_my_addr(const IPvX& ipvx_addr) const; /** * Test if an VifAddr is belongs to this vif. * * @param vif_addr the VifAddr address to test whether belongs to this vif. * @return true if @ref vif_addr belongs to this vif, otherwise false. */ bool is_my_vif_addr(const VifAddr& vif_addr) const; /** * Test if a given subnet address is a subset of one of the subnet * addresses of this vif. * * @param ipvxnet the subnet address to test against. * @return true if @ref ipvxnet is a subset of one of the subnet * addresses of this vif, otherwise false. */ bool is_same_subnet(const IPvXNet& ipvxnet) const; /** * Test if a given address belongs to one of the subnet addresses * of this vif. * * @param ipvx_addr the address to test against. * @return true if @ref ipvx_addr belongs to one of the subnet addresses * of this vif, otherwise false. */ bool is_same_subnet(const IPvX& ipvx_addr) const; /** * Test if a given address belongs to the same point-to-point link * as this vif. * * @param ipvx_addr the address to test against. * @return true if @ref ipvx_addr belongs to the same point-to-point link * as this vif, otherwise false. */ bool is_same_p2p(const IPvX& ipvx_addr) const; private: string _name; // The vif name string _ifname; // The physical interface name (if applicable) uint32_t _pif_index; // Physical interface index (if applicable), // or 0 if invalid. uint32_t _vif_index; // Virtual interface index bool _is_pim_register; // PIM Register vif bool _is_p2p; // Point-to-point interface bool _is_loopback; // Loopback interface bool _is_discard; // Discard interface bool _is_unreachable; // Unreachable interface bool _is_management; // Management interface bool _is_multicast_capable; // Multicast-capable interface bool _is_broadcast_capable; // Broadcast-capable interface bool _is_underlying_vif_up; // True if underlying vif is up uint32_t _mtu; // The MTU of the vif list _addr_list; // The list of addresses for this vif }; #endif // __LIBXORP_VIF_HH__ xorp/libxorp/getopt.c0000664000076400007640000001241611540225527015014 0ustar greearbgreearb/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- */ /* vim:set sts=4 ts=8: */ /* * Copyright (c) 2001-2011 XORP, Inc and Others * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License, Version * 2.1, June 1999 as published by the Free Software Foundation. * Redistribution and/or modification of this program under the terms of * any other version of the GNU Lesser General Public License is not * permitted. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, * see the GNU Lesser General Public License, Version 2.1, a copy of * which can be found in the XORP LICENSE.lgpl file. * * XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; * http://xorp.net */ /* * Part of this software is derived from the following file(s): * - FreeBSD's "src/lib/libc/stdlib/getopt.c" * * The copyright message(s) with the original file(s) is/are included below. */ /* $NetBSD: getopt.c,v 1.26 2003/08/07 16:43:40 agc Exp $ */ /* * Copyright (c) 1987, 1993, 1994 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include "libxorp/xorp.h" #include #ifdef HAVE_STDLIB_H #include #endif #include #ifdef HAVE_UNISTD_H #include #endif #ifdef NEED_GETOPT int opterr = 1, /* if error message should be printed */ optind = 1, /* index into parent argv vector */ optopt, /* character checked for validity */ optreset; /* reset getopt */ char *optarg; /* argument associated with option */ #define BADCH (int)'?' #define BADARG (int)':' #define EMSG "" /* * getopt -- * Parse argc/argv argument vector. */ int freebsd_getopt(nargc, nargv, ostr) int nargc; char * const nargv[]; const char *ostr; { static char *place = EMSG; /* option letter processing */ char *oli; /* option letter list index */ if (optreset || *place == 0) { /* update scanning pointer */ optreset = 0; place = nargv[optind]; if (optind >= nargc || *place++ != '-') { /* Argument is absent or is not an option */ place = EMSG; return (-1); } optopt = *place++; if (optopt == '-' && *place == 0) { /* "--" => end of options */ ++optind; place = EMSG; return (-1); } if (optopt == 0) { /* Solitary '-', treat as a '-' option if the program (eg su) is looking for it. */ place = EMSG; if (strchr(ostr, '-') == NULL) return (-1); optopt = '-'; } } else optopt = *place++; /* See if option letter is one the caller wanted... */ if (optopt == ':' || (oli = strchr(ostr, optopt)) == NULL) { if (*place == 0) ++optind; if (opterr && *ostr != ':') (void)fprintf(stderr, "%s: illegal option -- %c\n", _getprogname(), optopt); return (BADCH); } /* Does this option need an argument? */ if (oli[1] != ':') { /* don't need argument */ optarg = NULL; if (*place == 0) ++optind; } else { /* Option-argument is either the rest of this argument or the entire next argument. */ if (*place) optarg = place; else if (nargc > ++optind) optarg = nargv[optind]; else { /* option-argument absent */ place = EMSG; if (*ostr == ':') return (BADARG); if (opterr) (void)fprintf(stderr, "%s: option requires an argument -- %c\n", _getprogname(), optopt); return (BADCH); } place = EMSG; ++optind; } return (optopt); /* return option letter */ } #endif /* NEED_GETOPT */ xorp/libxorp/ether_compat.h0000664000076400007640000000436411421137511016165 0ustar greearbgreearb/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- */ /* vim:set sts=4 ts=8: */ /* * Copyright (c) 2001-2009 XORP, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License, Version * 2.1, June 1999 as published by the Free Software Foundation. * Redistribution and/or modification of this program under the terms of * any other version of the GNU Lesser General Public License is not * permitted. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, * see the GNU Lesser General Public License, Version 2.1, a copy of * which can be found in the XORP LICENSE.lgpl file. * * XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; * http://xorp.net */ /* * $XORP: xorp/libxorp/ether_compat.h,v 1.20 2008/10/28 14:08:18 bms Exp $ */ /* Ethernet manipulation compatibility functions */ #ifndef __LIBXORP_ETHER_COMPAT_H__ #define __LIBXORP_ETHER_COMPAT_H__ #include "libxorp/xorp.h" #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_SYS_SOCKET_H #include #endif #ifdef HAVE_SYS_ETHERNET_H #include #endif #ifdef HAVE_NET_ETHERNET_H #include #endif #ifdef HAVE_NET_IF_H #include #endif #ifdef HAVE_NET_IF_ETHER_H #include #endif #ifdef HAVE_NETINET_IN_H #include #endif #ifdef HAVE_NETINET_IF_ETHER_H #include #endif #ifdef HAVE_NETINET_ETHER_H #include #endif #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ #ifndef HAVE_STRUCT_ETHER_ADDR #ifndef ETHER_ADDR_LEN #define ETHER_ADDR_LEN 6 #endif struct ether_addr { char octet[ETHER_ADDR_LEN]; }; #endif /* HAVE_STRUCT_ETHER_ADDR */ #ifndef HAVE_ETHER_ATON struct ether_addr* ether_aton(const char *a); #endif #ifndef HAVE_ETHER_NTOA char* ether_ntoa(const struct ether_addr* ea); #endif #ifndef ETHERTYPE_IP #define ETHERTYPE_IP 0x0800 #endif #ifndef ETHERTYPE_ARP #define ETHERTYPE_ARP 0x0806 #endif #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* __LIBXORP_ETHER_COMPAT_H__ */ xorp/libxorp/asnum.hh0000664000076400007640000001655111540224227015012 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/libxorp/asnum.hh,v 1.20 2008/10/02 21:57:28 bms Exp $ #ifndef __LIBXORP_ASNUM_HH__ #define __LIBXORP_ASNUM_HH__ #include "libxorp/xorp.h" #include "libxorp/exceptions.hh" #ifdef HAVE_INTTYPES_H #include #endif #include "c_format.hh" /** * @short A class for storing an AS number used by protocols such as BGP * * This class can be used to store an AS number that can be either 16 * or 32 bits. Originally, the AS numbers were defined as 16-bit * unsigned numbers. Later the "extended" AS numbers were introduced, * which are unsigned 32-bit numbers. Conventional terminology refers * to the 32-bit version as 4-byte AS numbers rather than 32-bit AS * numbers, so we'll try and stick with that where it makes sense. * * 2-byte numbers are expanded to 32-bits by extending them with 0's in front. * 4-byte numbers are represented in a 2-byte AS path, by a special * 16-bit value, AS_TRAN, which will be allocated by IANA. * Together with any AsPath containing AS_TRAN, we will always see a * AS4_PATH attribute which contains the full 32-bit representation * of the path. So there is no loss of information. * * IANA refers to NEW_AS_PATH, but the latest internet drafts refer to * AS4_PATH. They're the same thing, but I the latter is preferred so * we'll use that. * * The internal representation of an AsNum is 32-bit in host order. * * The canonical string form of a 4-byte AS number is ., so * decimal 65536 ends up being printed as "1.0". * * An AsNum must always be initialized, so the default constructor * is never called. */ class AsNum { public: static const uint16_t AS_INVALID = 0; // XXX IANA-reserved static const uint16_t AS_TRAN = 23456; // IANA /** * Constructor. * @param value the value to assign to this AS number. */ explicit AsNum(const uint32_t value) : _as(value) { } explicit AsNum(const uint16_t value) : _as(value) {} explicit AsNum(int value) { assert(value >= 0 && value <= 0xffff); _as = value; } /** * construct from a 2-byte buffer in memory */ explicit AsNum(const uint8_t *d) { _as = (d[0] << 8) + d[1]; } /** * construct from a 2-byte buffer in memory or a 4 byte buffer (in * net byte order). * * The 4byte parameter is mostly to distinguish this from the * 2-byte constructor above. */ explicit AsNum(const uint8_t *d, bool fourbyte) { if (fourbyte) { // 4 byte version memcpy(&_as, d, 4); _as = htonl(_as); } else { // 2 byte version _as = (d[0] << 8) + d[1]; } } /** * construct from a string, either as a decimal number in the * range 1-65535, or as two decimal numbers x.y, where x and y are * in the range 0-65535 */ explicit AsNum(const string& as_str) throw(InvalidString) { bool four_byte = false; bool seen_digit = false; for (uint32_t i = 0; i < as_str.size(); i++) { if (as_str[i] == '.') { if (four_byte || seen_digit == false) { // more than one dot, or no number before the first dot. xorp_throw(InvalidString, c_format("Bad AS number \"%s\"", as_str.c_str())); } else { four_byte = true; seen_digit = false; } } else if (!isdigit(as_str[i])) { // got some disallowed character xorp_throw(InvalidString, c_format("Bad AS number \"%s\"", as_str.c_str())); } else { seen_digit = true; } } if (seen_digit == false) { // either no digit here, or no digit after the dot xorp_throw(InvalidString, c_format("Bad AS number \"%s\"", as_str.c_str())); } // got here, so the text is in the right format if (!four_byte) { _as = atoi(as_str.c_str()); if (_as < 1 || _as > 65535) { // out of range xorp_throw(InvalidString, c_format("Bad AS number \"%s\"", as_str.c_str())); } } else { uint32_t upper = strtoul(as_str.c_str(), NULL, 10); uint32_t lower = strtoul(strchr(as_str.c_str(), '.') + 1, NULL, 10); if (upper > 65535 || lower > 65535) { // out of range xorp_throw(InvalidString, c_format("Bad AS number \"%s\"", as_str.c_str())); } _as = (upper << 16) | lower; } } /** * Get the non-extended AS number value. * * @return the non-extended AS number value. */ uint16_t as() const { return extended() ? AS_TRAN : _as; } /** * Get the extended AS number value. * * @return the extended AS number value. */ uint32_t as4() const { return _as; } /** * copy the 16-bit value into a 2-byte memory buffer */ void copy_out(uint8_t *d) const { uint16_t x = as(); d[0] = (x >> 8) & 0xff; d[1] = x & 0xff; } /** * copy the 32-bit value into a 4-byte network byte order memory buffer */ void copy_out4(uint8_t *d) const { // note - buffer may be unaligned, so use memcpy uint32_t x = htonl(_as); memcpy(d, &x, 4); } /** * Test if this is an extended AS number. * * @return true if this is an extended AS number. */ bool extended() const { return _as>0xffff;}; /** * Equality Operator * * @param other the right-hand operand to compare against. * @return true if the left-hand operand is numerically same as the * right-hand operand. */ bool operator==(const AsNum& x) const { return _as == x._as; } /** * Less-Than Operator * @return true if the left-hand operand is numerically smaller than the * right-hand operand. */ bool operator<(const AsNum& x) const { return _as < x._as; } /** * Convert this AS number from binary form to presentation format. * * @return C++ string with the human-readable ASCII representation * of the AS number. */ string str() const { if (extended()) return c_format("AS/%u.%u", (_as >> 16)&0xffff, _as&0xffff); else return c_format("AS/%u", XORP_UINT_CAST(_as)); } string short_str() const { if (extended()) return c_format("%u.%u", (_as >> 16)&0xffff, _as&0xffff); else return c_format("%u", XORP_UINT_CAST(_as)); } string fourbyte_str() const { // this version forces the long canonical format return c_format("%u.%u", (_as >> 16)&0xffff, _as&0xffff); } private: uint32_t _as; // The value of the AS number #ifdef XORP_USE_USTL public: AsNum() { }; #else AsNum(); // forbidden #endif }; #endif // __LIBXORP_ASNUM_HH__ xorp/libxorp/tlv.hh0000664000076400007640000000761611421137511014473 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/libxorp/tlv.hh,v 1.9 2008/10/02 21:57:36 bms Exp $ #ifndef __LIBXORP_TLV_HH__ #define __LIBXORP_TLV_HH__ /** * * Read and Write TLV records. */ class Tlv { public: Tlv() : _fp(0) {} /** * Open file for reading or writing. * * @param fname filename or '-' for stdin/stdout. * @param read true if the file is to be opened for reading, false * if opened for writing. * * @return true on success */ bool open(string& fname, bool read) { if ("-" == fname) { _fp = read ? stdin : stdout; } else { _fp = fopen(fname.c_str(), read ? "r" : "w"); } if (0 == _fp) return false; return true; } /** * Read a TLV entry from the file. * * @return type of entry. * @return data read. * * @return true on success */ bool read(uint32_t& type, vector& data) { uint32_t n; if (1 != fread(&n, sizeof(n), 1, _fp)) return false; type = ntohl(n); if (1 != fread(&n, sizeof(n), 1, _fp)) return false; uint32_t len = ntohl(n); data.resize(len); if (len != fread(&data[0], 1, len, _fp)) return false; return true; } /** * Write a TLV entry to the file. * * @return type of entry. * @return data to be written. * * @return true on success */ bool write(uint32_t type, vector& data) { uint32_t n = htonl(type); if (1 != fwrite(&n, sizeof(n), 1, _fp)) return false; uint32_t len = data.size(); n = htonl(len); if (1 != fwrite(&n, sizeof(n), 1, _fp)) return false; if (len != fwrite(&data[0], 1, len, _fp)) return false; return true; } /** * Close the TLV file. * * @return true on success */ bool close() { if (0 == _fp) { return false; } return 0 == fclose(_fp) ? true : false; } /* * Get a 32 bit unsigned int from the data array. * * @param data array containing the value. * @param offset in data to start reading. * @param u32 value read from array. * * @return true on success */ bool get32(vector& data, uint32_t offset, uint32_t& u32) { if (data.size() < offset + sizeof(u32)) return false; u32 = data[offset + 0]; u32 <<= 8; u32 |= data[offset + 1]; u32 <<= 8; u32 |= data[offset + 2]; u32 <<= 8; u32 |= data[offset + 3]; return true; } /* * Put a 32 bit unsigned int into the data array. * * @param data array into which to place the value. * @param offset in data to start writing. * @param u32 value to put into the array. * * @return true on success */ bool put32(vector& data, uint32_t offset, uint32_t u32) { if (data.size() < offset + sizeof(u32)) return false; data[offset + 0] = (u32 >> 24) & 0xff; data[offset + 1] = (u32 >> 16) & 0xff; data[offset + 2] = (u32 >> 8) & 0xff; data[offset + 3] = u32 & 0xff; return true; } private: FILE *_fp; }; #endif // __LIBXORP_TLV_HH__ xorp/libxorp/run_command.cc0000664000076400007640000005456011540224227016161 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "libxorp_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/eventloop.hh" #include "libxorp/exceptions.hh" #include "libxorp/xorpfd.hh" #include "libxorp/asyncio.hh" #include "libxorp/popen.hh" #ifdef HOST_OS_WINDOWS #include "libxorp/win_io.h" #endif #ifdef HAVE_PATHS_H #include #endif #ifdef HAVE_SYS_WAIT_H #include #endif #include "run_command.hh" #ifdef HOST_OS_WINDOWS #ifndef _PATH_BSHELL #define _PATH_BSHELL "C:\\MSYS\\bin\\sh.exe" #endif #ifdef fileno #undef fileno #endif #define fileno(stream) (_get_osfhandle(_fileno(stream))) #else // ! HOST_OS_WINDOWS #ifndef _PATH_BSHELL #define _PATH_BSHELL "/bin/sh" #endif #endif // ! HOST_OS_WINDOWS static map pid2command; #ifndef HOST_OS_WINDOWS static void child_handler(int signo) { XLOG_ASSERT(signo == SIGCHLD); // // XXX: Wait for any child process. // If we are executing any child process outside of the RunProcess // mechanism, then the waitpid() here may create a wait() blocking // problem for those processes. If this ever becomes an issue, then // we should try non-blocking waitpid() for each pid in the // pid2command map. // do { pid_t pid = 0; int wait_status = 0; map::iterator iter; pid = waitpid(-1, &wait_status, WUNTRACED | WNOHANG); debug_msg("pid=%d, wait status=%d\n", XORP_INT_CAST(pid), wait_status); if (pid <= 0) return; // XXX: no more child processes XLOG_ASSERT(pid > 0); popen2_mark_as_closed(pid, wait_status); iter = pid2command.find(pid); if (iter == pid2command.end()) { // XXX: we don't know anything about the exited process continue; } RunCommandBase* run_command = iter->second; run_command->wait_status_changed(wait_status); } while (true); } #endif // ! HOST_OS_WINDOWS static string get_path_bshell() { #ifndef HOST_OS_WINDOWS return (string(_PATH_BSHELL)); #else // HOST_OS_WINDOWS // // Use the DOS style path to the user's shell specified in the // environment if available; otherwise, fall back to hard-coded default. // static string path_bshell; if (path_bshell.empty()) { char* g = getenv("SHELL"); if (g != NULL) path_bshell = string(g); else path_bshell = string(_PATH_BSHELL); } return (path_bshell); #endif // HOST_OS_WINDOWS } RunCommand::RunCommand(EventLoop& eventloop, const string& command, const list& argument_list, RunCommand::OutputCallback stdout_cb, RunCommand::OutputCallback stderr_cb, RunCommand::DoneCallback done_cb, bool redirect_stderr_to_stdout, int task_priority) : RunCommandBase(eventloop, command, command, task_priority), _stdout_cb(stdout_cb), _stderr_cb(stderr_cb), _done_cb(done_cb), _redirect_stderr_to_stdout(redirect_stderr_to_stdout) { set_argument_list(argument_list); } RunShellCommand::RunShellCommand(EventLoop& eventloop, const string& command, const string& argument_string, RunShellCommand::OutputCallback stdout_cb, RunShellCommand::OutputCallback stderr_cb, RunShellCommand::DoneCallback done_cb, int task_priority) : RunCommandBase(eventloop, get_path_bshell(), command, task_priority), _stdout_cb(stdout_cb), _stderr_cb(stderr_cb), _done_cb(done_cb) { list l; string final_command_argument_string = command + " " + argument_string; l.push_back("-c"); l.push_back(final_command_argument_string); set_argument_list(l); } RunCommandBase::RunCommandBase(EventLoop& eventloop, const string& command, const string& real_command_name, int task_priority) : _eventloop(eventloop), _command(command), _real_command_name(real_command_name), _stdout_file_reader(NULL), _stderr_file_reader(NULL), _stdout_stream(NULL), _stderr_stream(NULL), _last_stdout_offset(0), _last_stderr_offset(0), _pid(0), _is_error(false), _is_running(false), _command_is_exited(false), _command_is_signal_terminated(false), _command_is_coredumped(false), _command_is_stopped(false), _command_exit_status(0), _command_term_signal(0), _command_stop_signal(0), _stdout_eof_received(false), _stderr_eof_received(false), _task_priority(task_priority) { memset(_stdout_buffer, 0, BUF_SIZE); memset(_stderr_buffer, 0, BUF_SIZE); _done_timer = _eventloop.new_timer(callback(this, &RunCommandBase::done)); } RunCommandBase::~RunCommandBase() { cleanup(); } void RunCommandBase::cleanup() { terminate_with_prejudice(); close_output(); // Remove the entry from the pid map and perform other cleanup if (_pid != 0) { pid2command.erase(_pid); _pid = 0; } _done_timer.unschedule(); _is_running = false; unblock_child_signals(); } int RunCommandBase::block_child_signals() { #ifndef HOST_OS_WINDOWS // // Block SIGCHLD signal in current signal mask // int r; sigset_t sigchld_sigset; r = sigemptyset(&sigchld_sigset); XLOG_ASSERT(r >= 0); r = sigaddset(&sigchld_sigset, SIGCHLD); XLOG_ASSERT(r >= 0); if (sigprocmask(SIG_BLOCK, &sigchld_sigset, NULL) < 0) { XLOG_ERROR("Failed to block SIGCHLD in current signal mask: %s", strerror(errno)); return (XORP_ERROR); } #endif // ! HOST_OS_WINDOWS return (XORP_OK); } int RunCommandBase::unblock_child_signals() { #ifndef HOST_OS_WINDOWS // // Unblock SIGCHLD signal in current signal mask // int r; sigset_t sigchld_sigset; r = sigemptyset(&sigchld_sigset); XLOG_ASSERT(r >= 0); r = sigaddset(&sigchld_sigset, SIGCHLD); XLOG_ASSERT(r >= 0); if (sigprocmask(SIG_UNBLOCK, &sigchld_sigset, NULL) < 0) { XLOG_ERROR("Failed to unblock SIGCHLD in current signal mask: %s", strerror(errno)); return (XORP_ERROR); } #endif // ! HOST_OS_WINDOWS return (XORP_OK); } int RunCommandBase::execute() { string error_msg; if (_is_running) return (XORP_OK); // XXX: already running // // Save the current execution ID, and set the new execution ID // _exec_id.save_current_exec_id(); if (_exec_id.set_effective_exec_id(error_msg) != XORP_OK) { XLOG_ERROR("Failed to set effective execution ID: %s", error_msg.c_str()); _exec_id.restore_saved_exec_id(error_msg); return (XORP_ERROR); } #ifndef HOST_OS_WINDOWS signal(SIGCHLD, child_handler); #endif // // Temporary block the child signals // block_child_signals(); // // Run the command // ifstream tst("XORP_USE_VALGRIND"); if (tst) { string vcmd("/usr/bin/valgrind"); list args(_argument_list); string uniq; size_t slash = _command.find_last_of("/"); if (slash != string::npos) { uniq = _command.substr(slash + 1); } else { uniq = _command; } char* value = getenv("XORP_FINDER_SERVER_PORT"); if (value != NULL) { uniq.append("-"); uniq.append(value); } // Move any old valgrind logs out of the way. string lf0("valgrind_" + uniq + ".txt"); string lf1("valgrind_" + uniq + ".txt.1"); string lf2("valgrind_" + uniq + ".txt.2"); rename(lf1.c_str(), lf2.c_str()); rename(lf0.c_str(), lf1.c_str()); args.push_front(_command); args.push_front("--track-origins=yes"); args.push_front("--leak-check=full"); args.push_front("--log-file=" + lf0); tst.close(); _pid = popen2(vcmd, args, _stdout_stream, _stderr_stream, redirect_stderr_to_stdout()); } else { _pid = popen2(_command, _argument_list, _stdout_stream, _stderr_stream, redirect_stderr_to_stdout()); } if (_stdout_stream == NULL) { XLOG_ERROR("Failed to execute command \"%s\"", _command.c_str()); cleanup(); _exec_id.restore_saved_exec_id(error_msg); return (XORP_ERROR); } // Insert the new process to the map of processes XLOG_ASSERT(pid2command.find(_pid) == pid2command.end()); pid2command[_pid] = this; #ifdef HOST_OS_WINDOWS // We need to obtain the handle directly from the popen code because // the handle returned by CreateProcess() has the privileges we need. _ph = pgethandle(_pid); // If the handle is invalid, the process failed to start. if (_ph == INVALID_HANDLE_VALUE) { XLOG_ERROR("Failed to execute command \"%s\"", _command.c_str()); cleanup(); _exec_id.restore_saved_exec_id(error_msg); return (XORP_ERROR); } // We can't rely on end-of-file indicators alone on Win32 to determine // when a child process died; we must wait for it in the event loop. if (!_eventloop.add_ioevent_cb( _ph, IOT_EXCEPTION, callback(this, &RunCommandBase::win_proc_done_cb))) XLOG_FATAL("Cannot add child process handle to event loop.\n"); #endif // HOST_OS_WINDOWS // Create the stdout and stderr readers _stdout_file_reader = new AsyncFileReader(_eventloop, XorpFd(fileno(_stdout_stream)), task_priority()); _stdout_file_reader->add_buffer( _stdout_buffer, BUF_SIZE, callback(this, &RunCommandBase::append_data)); if (! _stdout_file_reader->start()) { XLOG_ERROR("Failed to start a stdout reader for command \"%s\"", _command.c_str()); cleanup(); _exec_id.restore_saved_exec_id(error_msg); return (XORP_ERROR); } _stderr_file_reader = new AsyncFileReader(_eventloop, XorpFd(fileno(_stderr_stream)), task_priority()); _stderr_file_reader->add_buffer( _stderr_buffer, BUF_SIZE, callback(this, &RunCommandBase::append_data)); if (! _stderr_file_reader->start()) { XLOG_ERROR("Failed to start a stderr reader for command \"%s\"", _command.c_str()); cleanup(); _exec_id.restore_saved_exec_id(error_msg); return (XORP_ERROR); } _is_running = true; // // Restore the saved execution ID // _exec_id.restore_saved_exec_id(error_msg); // // Unblock the child signals that were blocked earlier // unblock_child_signals(); return (XORP_OK); } void RunCommandBase::terminate() { terminate_process(false); } void RunCommandBase::terminate_with_prejudice() { terminate_process(true); } void RunCommandBase::terminate_process(bool with_prejudice) { // Kill the process group if (0 != _pid) { #ifdef HOST_OS_WINDOWS UNUSED(with_prejudice); // // _ph will be invalid if the process didn't start. // _ph will be valid if the process handle is open but the // process is no longer running. Calling TerminateProcess() // on a valid handle to a process which has terminated results // in an ACCESS_DENIED error. // Don't close the handle. pclose2() does this. // if (_ph != INVALID_HANDLE_VALUE) { DWORD result; GetExitCodeProcess(_ph, &result); if (result == STILL_ACTIVE) { DWORD result = 0; result = TerminateProcess(_ph, 32768); if (result == 0) { XLOG_WARNING("TerminateProcess(): %s", win_strerror(GetLastError())); } } } #else // ! HOST_OS_WINDOWS if (with_prejudice) killpg(_pid, SIGKILL); else killpg(_pid, SIGTERM); #endif // ! HOST_OS_WINDOWS } } void RunCommandBase::wait_status_changed(int wait_status) { set_command_status(wait_status); // // XXX: Schedule a timer to complete the command so we can return // control to the caller. // // TODO: Temporary print any errors and catch any exceptions // (for debugging purpose). try { errno = 0; _done_timer.schedule_now(); } catch(...) { XLOG_ERROR("Error scheduling RunCommand::_done_timer: %d", errno); xorp_catch_standard_exceptions(); } } void RunCommandBase::close_output() { // // XXX: we should close stderr before stdout, because // close_stdout_output() invokes pclose2() that performs the // final cleanup. // close_stderr_output(); close_stdout_output(); } void RunCommandBase::close_stdout_output() { if (_stdout_file_reader != NULL) { delete _stdout_file_reader; _stdout_file_reader = NULL; } if (_stdout_stream != NULL) { #ifdef HOST_OS_WINDOWS // pclose2() will close the process handle from under us. _eventloop.remove_ioevent_cb(_ph, IOT_EXCEPTION); _ph = INVALID_HANDLE_VALUE; // // XXX: in case of Windows we don't use the SIGCHLD mechanism // hence the process is done when the I/O is completed. // int status = pclose2(_stdout_stream, false); _stdout_stream = NULL; set_command_status(status); _command_is_exited = true; // XXX: A Windows hack #else // ! HOST_OS_WINDOWS pclose2(_stdout_stream, true); _stdout_stream = NULL; #endif // ! HOST_OS_WINDOWS } } void RunCommandBase::close_stderr_output() { if (_stderr_file_reader != NULL) { delete _stderr_file_reader; _stderr_file_reader = NULL; } // // XXX: don't call pclose2(_stderr_stream), because pclose2(_stdout_stream) // automatically takes care of the _stderr_stream as well. // _stderr_stream = NULL; } void RunCommandBase::set_command_status(int status) { // Reset state _command_is_exited = false; _command_is_signal_terminated = false; _command_is_coredumped = false; _command_is_stopped = false; _command_exit_status = 0; _command_term_signal = 0; _command_stop_signal = 0; // Set the command status #ifdef HOST_OS_WINDOWS _command_exit_status = status; #else // ! HOST_OS_WINDOWS if (status >= 0) { _command_is_exited = WIFEXITED(status); _command_is_signal_terminated = WIFSIGNALED(status); _command_is_stopped = WIFSTOPPED(status); if (_command_is_exited) _command_exit_status = WEXITSTATUS(status); if (_command_is_signal_terminated) { _command_term_signal = WTERMSIG(status); _command_is_coredumped = WCOREDUMP(status); } if (_command_is_stopped) { _command_stop_signal = WSTOPSIG(status); } } #endif // ! HOST_OS_WINDOWS if (_command_is_stopped) stopped_cb_dispatch(_command_stop_signal); } void RunCommandBase::append_data(AsyncFileOperator::Event event, const uint8_t* buffer, size_t /* buffer_bytes */, size_t offset) { size_t* last_offset_ptr = NULL; bool is_stdout = false; if (buffer == _stdout_buffer) { is_stdout = true; last_offset_ptr = &_last_stdout_offset; } else { XLOG_ASSERT(buffer == _stderr_buffer); is_stdout = false; last_offset_ptr = &_last_stderr_offset; } if ((event != AsyncFileOperator::END_OF_FILE) && (event != AsyncFileOperator::DATA)) { // // Something bad happened. // XXX: ideally we'd figure out what. // int error_code = 0; if (is_stdout) error_code = _stdout_file_reader->error(); else error_code = _stderr_file_reader->error(); io_done(event, error_code); return; } // // Either DATA or END_OF_FILE // XLOG_ASSERT(offset >= *last_offset_ptr); if (offset != *last_offset_ptr) { const char* p = (const char*)buffer + *last_offset_ptr; size_t len = offset - *last_offset_ptr; debug_msg("len = %u\n", XORP_UINT_CAST(len)); if (!_is_error) { if (is_stdout) stdout_cb_dispatch(string(p, len)); else stderr_cb_dispatch(string(p, len)); } else { _error_msg.append(p, p + len); } *last_offset_ptr = offset; } if (offset == BUF_SIZE) { // The buffer is exhausted *last_offset_ptr = 0; if (is_stdout) { memset(_stdout_buffer, 0, BUF_SIZE); _stdout_file_reader->add_buffer( _stdout_buffer, BUF_SIZE, callback(this, &RunCommandBase::append_data)); _stdout_file_reader->start(); } else { memset(_stderr_buffer, 0, BUF_SIZE); _stderr_file_reader->add_buffer( _stderr_buffer, BUF_SIZE, callback(this, &RunCommandBase::append_data)); _stderr_file_reader->start(); } } if (event == AsyncFileOperator::END_OF_FILE) { if (is_stdout) { _stdout_eof_received = true; } else { _stderr_eof_received = true; } do { if (_stdout_eof_received && (_stderr_eof_received || redirect_stderr_to_stdout())) { io_done(event, 0); break; } if ((! is_stdout) && _stderr_eof_received) { close_stderr_output(); break; } break; } while (false); return; } } void RunCommandBase::io_done(AsyncFileOperator::Event event, int error_code) { if (event != AsyncFileOperator::END_OF_FILE) { string prefix, suffix; _is_error = true; if (_error_msg.size()) { prefix = "["; suffix = "]"; } _error_msg += prefix; _error_msg += c_format("Command \"%s\" terminated because of " "unexpected event (event = 0x%x error = %d).", _real_command_name.c_str(), event, error_code); _error_msg += suffix; terminate_with_prejudice(); } close_output(); done(_done_timer); } void RunCommandBase::done(XorpTimer& done_timer) { string prefix, suffix, reason; done_timer.unschedule(); if (_stdout_stream != NULL) return; // XXX: I/O is not done yet if (! (_command_is_exited || _command_is_signal_terminated)) return; // XXX: the command is not done yet // Remove the entry from the pid map pid2command.erase(_pid); _pid = 0; _done_timer.unschedule(); _is_running = false; if (_error_msg.size()) { prefix = "["; suffix = "]"; } _error_msg += prefix; if (_command_is_exited && (_command_exit_status != 0)) { _is_error = true; if (! reason.empty()) reason += "; "; reason += c_format("exited with exit status %d", _command_exit_status); } if (_command_is_signal_terminated) { _is_error = true; if (! reason.empty()) reason += "; "; reason += c_format("terminated with signal %d", _command_term_signal); } if (_command_is_coredumped) { _is_error = true; if (! reason.empty()) reason += "; "; reason += c_format("aborted with a core dump"); } if (! reason.empty()) { _error_msg += c_format("Command \"%s\": %s.", _real_command_name.c_str(), reason.c_str()); } _error_msg += suffix; done_cb_dispatch(!_is_error, _error_msg); // // XXX: the callback will delete us. Don't do anything more in this method. // // delete this; } void RunCommandBase::set_exec_id(const ExecId& v) { _exec_id = v; } RunCommandBase::ExecId::ExecId() : _uid(0), _gid(0), _is_uid_set(false), _is_gid_set(false), _saved_uid(0), _saved_gid(0), _is_exec_id_saved(false) { } RunCommandBase::ExecId::ExecId(uid_t uid) : _uid(uid), _gid(0), _is_uid_set(true), _is_gid_set(false), _saved_uid(0), _saved_gid(0), _is_exec_id_saved(false) { } RunCommandBase::ExecId::ExecId(uid_t uid, gid_t gid) : _uid(uid), _gid(gid), _is_uid_set(true), _is_gid_set(true), _saved_uid(0), _saved_gid(0), _is_exec_id_saved(false) { } void RunCommandBase::ExecId::save_current_exec_id() { #ifndef HOST_OS_WINDOWS _saved_uid = getuid(); _saved_gid = getgid(); #endif _is_exec_id_saved = true; } int RunCommandBase::ExecId::restore_saved_exec_id(string& error_msg) const { #ifdef HOST_OS_WINDOWS UNUSED(error_msg); #else // ! HOST_OS_WINDOWS if (! _is_exec_id_saved) return (XORP_OK); // Nothing to do, because nothing was saved if (seteuid(saved_uid()) != 0) { error_msg = c_format("Cannot restore saved user ID to %u: %s", XORP_UINT_CAST(saved_uid()), strerror(errno)); return (XORP_ERROR); } if (setegid(saved_gid()) != 0) { error_msg = c_format("Cannot restore saved group ID to %u: %s", XORP_UINT_CAST(saved_gid()), strerror(errno)); return (XORP_ERROR); } #endif // ! HOST_OS_WINDOWS return (XORP_OK); } int RunCommandBase::ExecId::set_effective_exec_id(string& error_msg) { #ifdef HOST_OS_WINDOWS UNUSED(error_msg); #else // ! HOST_OS_WINDOWS if (! is_set()) return (XORP_OK); // // Set the effective group ID // if (is_gid_set() && (gid() != saved_gid())) { if (setegid(gid()) != 0) { error_msg = c_format("Cannot set the effective group ID to %u: %s", XORP_UINT_CAST(gid()), strerror(errno)); return (XORP_ERROR); } } // // Set the effective user ID // if (is_uid_set() && (uid() != saved_uid())) { if (seteuid(uid()) != 0) { error_msg = c_format("Cannot set effective user ID to %u: %s", XORP_UINT_CAST(uid()), strerror(errno)); return (XORP_ERROR); } } #endif // ! HOST_OS_WINDOWS return (XORP_OK); } bool RunCommandBase::ExecId::is_set() const { return (is_uid_set() || is_gid_set()); } void RunCommandBase::ExecId::reset() { _uid = 0; _gid = 0; _is_uid_set = false; _is_gid_set = false; _saved_uid = 0; _saved_gid = 0; _is_exec_id_saved = false; } #ifdef HOST_OS_WINDOWS // // Hack to get asynchronous notification of Win32 process death. // The process handle will be SIGNALLED when the process terminates. // Because Win32 pipes must be polled, we must make sure that RunCommand // does not tear down the pipes until everything is read, otherwise the // process death event will be dispatched before the pipe I/O events. // We schedule a timer to make sure output is read from the pipes // before they close, as Win32 pipes lose data when closed; the parent // must hold handles for both ends of the pipe, unlike UNIX. // void RunCommandBase::win_proc_done_cb(XorpFd fd, IoEventType type) { XLOG_ASSERT(type == IOT_EXCEPTION); XLOG_ASSERT(static_cast(fd) == _ph); _eventloop.remove_ioevent_cb(_ph, IOT_EXCEPTION); _reaper_timer = _eventloop.new_oneoff_after_ms( WIN32_PROC_TIMEOUT_MS, callback(this, &RunCommandBase::win_proc_reaper_cb)); XLOG_ASSERT(_reaper_timer.scheduled()); } void RunCommandBase::win_proc_reaper_cb() { io_done(AsyncFileOperator::END_OF_FILE, 0); } #endif // HOST_OS_WINDOWS xorp/libxorp/ipnet.hh0000664000076400007640000004440411540225527015010 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/libxorp/ipnet.hh,v 1.38 2009/01/05 18:30:57 jtc Exp $ #ifndef __LIBXORP_IPNET_HH__ #define __LIBXORP_IPNET_HH__ #include "xorp.h" #include "exceptions.hh" #include "c_format.hh" #include "range.hh" #include "utils.hh" #include "ipv4.hh" #include "ipv6.hh" /** * @short A template class for subnets * * A "subnet" is specified by a base "address" and a "prefix length". */ template class IPNet { public: /** * Default constructor taking no parameters. * * Default value has INADDR_ANY/0. */ IPNet() : _prefix_len(0) {} /** * Constructor from a given base address and a prefix length. * * @param a base address for the subnet. * @param prefix_len length of subnet mask (e.g., class C nets would have * prefix_len=24). */ IPNet(const A& a, uint8_t prefix_len) throw (InvalidNetmaskLength) : _masked_addr(a), _prefix_len(prefix_len) { if (prefix_len > A::addr_bitlen()) xorp_throw(InvalidNetmaskLength, prefix_len); _masked_addr = a.mask_by_prefix_len(prefix_len); } /** * Constructor from a string. * * @param from_cstring C-style string with slash separated address * and prefix length. */ IPNet(const char *from_cstring) throw (InvalidString, InvalidNetmaskLength) { initialize_from_string(from_cstring); } /** * Copy constructor * * @param n the subnet to copy from. */ IPNet(const IPNet& n) { _masked_addr = n.masked_addr(); _prefix_len = n.prefix_len(); } /** * Assignment operator * * @param n the subnet to assign from. * @return the subnet after the assignment. */ IPNet& operator=(const IPNet& n) { _masked_addr = n.masked_addr(); _prefix_len = n.prefix_len(); return *this; } /** * Equality Operator * * @param other the right-hand operand to compare against. * @return true if the left-hand operand is numerically same as the * right-hand operand. */ bool operator==(const IPNet& other) const { return ((prefix_len() == other.prefix_len()) && (masked_addr() == other.masked_addr())); } /** * Less-than comparison for subnets (see body for description). * * @param other the right-hand side of the comparison. * @return true if the left-hand side is "smaller" than the right-hand * side according to the chosen order. */ bool operator<(const IPNet& other) const; /** * Decrement Operator * * The numerical value of the prefix address is decrement by one. * Example: decrementing 128.2.0.0/16 results in 128.1.0.0/16. * * @return a reference to this subnet after the decrement */ IPNet& operator--(); /** * Increment Operator * * The numerical value of the prefix address is incremented by one. * Example: incrementing 128.2.0.0/16 results in 128.3.0.0/16. * * @return a reference to this subnet after the increment */ IPNet& operator++(); /** * Equality Operator for @ref U32Range operand. * * @param range the right-hand operand to compare against. * @return true if the prefix length falls inside the range defined * by the right-hand operand. */ bool operator==(const U32Range& range) const { return (prefix_len() == range); } /** * Non-equality Operator for @ref U32Range operand. * * @param range the right-hand operand to compare against. * @return true if the prefix length falls outside the range defined * by the right-hand operand. */ bool operator!=(const U32Range& range) const { return (prefix_len() != range); } /** * Less-than comparison for prefix lengths for @ref U32Range operand. * * @param range the right-hand side of the comparison. * @return true if the prefix length is bellow the range defined * by the right-hand operand. */ bool operator<(const U32Range& range) const { return (prefix_len() < range); }; /** * Less-than or equal comparison for prefix lengths for @ref U32Range * operand. * * @param range the right-hand side of the comparison. * @return true if the prefix length is bellow or within the range * defined by the right-hand operand. */ bool operator<=(const U32Range& range) const { return (prefix_len() <= range); }; /** * Greater-than comparison for prefix lengths for @ref U32Range operand. * * @param range the right-hand side of the comparison. * @return true if the prefix length is above the range defined * by the right-hand operand. */ bool operator>(const U32Range& range) const { return (prefix_len() > range); }; /** * Greater-than or equal comparison for prefix lengths for @ref U32Range * operand. * * @param range the right-hand side of the comparison. * @return true if the prefix length is above or within the range * defined by the right-hand operand. */ bool operator>=(const U32Range& range) const { return (prefix_len() >= range); }; /** * Convert this address from binary form to presentation format. * * @return C++ string with the human-readable ASCII representation * of the address. */ string str() const; /** * Test if the object contains a real (non-default) value. * * @return true if the object stores a real (non-default) value. */ bool is_valid() const { return _prefix_len != 0; } /** * Test if subnets overlap. * * @param other the subnet to compare against. * @return true if there is some overlap between the two subnets. */ bool is_overlap(const IPNet& other) const; /** * Test if a subnet contains (or is equal to) another subnet. * * in LaTeX, x.contains(y) would be $x \superseteq y$ * * @param other the subnet to test against. * @return true if this subnet contains or is equal to @ref other. */ bool contains(const IPNet& other) const; /** * Test if an address is within a subnet. * * @param addr the address to test against. * @return true if @ref addr is within this subnet. */ bool contains(const A& addr) const { return addr.mask_by_prefix_len(_prefix_len) == _masked_addr; } /** * Determine the number of the most significant bits overlapping with * another subnet. * * @param other the subnet to test against. * @return the number of bits overlapping between @ref other and * this subnet. */ uint32_t overlap(const IPNet& other) const; /** * Get the address family. * * @return the address family of this address. */ static int af() { return A::af(); } /** * Get the base address. * * @return the base address for this subnet. */ const A& masked_addr() const { return _masked_addr; } A& masked_addr_nc() { return _masked_addr; } /** * Get the prefix length. * * @return the prefix length for this subnet. */ uint8_t prefix_len() const { return _prefix_len; } void set_prefix_len(uint8_t v) { _prefix_len = v; } /** * Get the network mask. * * @return the netmask associated with this subnet. */ A netmask() const { return _masked_addr.make_prefix(_prefix_len); } /** * Test if this subnet is a unicast prefix. * * In case of IPv4 all prefixes that fall within the Class A, Class B or * Class C address space are unicast. * In case of IPv6 all prefixes that don't contain the multicast * address space are unicast. * Note that the default route (0.0.0.0/0 for IPv4 or ::/0 for IPv6) * is also considered an unicast prefix. * * @return true if this subnet is a unicast prefix. */ bool is_unicast() const; /** * Return the subnet containing all multicast addresses. * * Note that this is a static function and can be used without * a particular object. Example: * IPv4Net my_prefix = IPv4Net::ip_multicast_base_prefix(); * IPv4Net my_prefix = ipv4net.ip_multicast_base_prefix(); * * @return the subnet containing multicast addresses. */ static const IPNet
ip_multicast_base_prefix() { return IPNet(A::MULTICAST_BASE(), A::ip_multicast_base_address_mask_len()); } /** * Return the subnet containing all IPv4 Class A addresses * (0.0.0.0/1). * * This method applies only for IPv4. * Note that this is a static function and can be used without * a particular object. Example: * IPv4Net my_prefix = IPv4Net::ip_class_a_base_prefix(); * IPv4Net my_prefix = ipv4net.ip_class_a_base_prefix(); * * @return the subnet containing Class A addresses. */ static const IPNet ip_class_a_base_prefix(); /** * Return the subnet containing all IPv4 Class B addresses * (128.0.0.0/2). * * This method applies only for IPv4. * Note that this is a static function and can be used without * a particular object. Example: * IPv4Net my_prefix = IPv4Net::ip_class_b_base_prefix(); * IPv4Net my_prefix = ipv4net.ip_class_b_base_prefix(); * * @return the subnet containing Class B addresses. */ static const IPNet ip_class_b_base_prefix(); /** * Return the subnet containing all IPv4 Class C addresses * (192.0.0.0/3). * * This method applies only for IPv4. * Note that this is a static function and can be used without * a particular object. Example: * IPv4Net my_prefix = IPv4Net::ip_class_c_base_prefix(); * IPv4Net my_prefix = ipv4net.ip_class_c_base_prefix(); * * @return the subnet containing Class C addresses. */ static const IPNet ip_class_c_base_prefix(); /** * Return the subnet containing all IPv4 experimental Class E addresses * (240.0.0.0/4). * * This method applies only for IPv4. * Note that this is a static function and can be used without * a particular object. Example: * IPv4Net my_prefix = IPv4Net::ip_experimental_base_prefix(); * IPv4Net my_prefix = ipv4net.ip_experimental_base_prefix(); * * @return the subnet containing experimental addresses. */ static const IPNet ip_experimental_base_prefix(); /** * Test if this subnet is within the multicast address range. * * @return true if this subnet is within the multicast address range. */ bool is_multicast() const { return (ip_multicast_base_prefix().contains(*this)); } /** * Test if this subnet is within the IPv4 Class A * address range (0.0.0.0/1). * * This method applies only for IPv4. * * @return true if this subnet is within the IPv4 Class A address * range. */ bool is_class_a() const; /** * Test if this subnet is within the IPv4 Class B * address range (128.0.0.0/2). * * This method applies only for IPv4. * * @return true if this subnet is within the IPv4 Class B address * range. */ bool is_class_b() const; /** * Test if this subnet is within the IPv4 Class C * address range (192.0.0.0/3). * * This method applies only for IPv4. * * @return true if this subnet is within the IPv4 Class C address * range. */ bool is_class_c() const; /** * Test if this subnet is within the IPv4 experimental Class E * address range (240.0.0.0/4). * * This method applies only for IPv4. * * @return true if this subnet is within the IPv4 experimental address * range. */ bool is_experimental() const; /** * Get the highest address within this subnet. * * @return the highest address within this subnet. */ A top_addr() const { return _masked_addr | ~netmask(); } /** * Get the smallest subnet containing both subnets. * * @return the smallest subnet containing both subnets passed * as arguments. */ static IPNet common_subnet(const IPNet x, const IPNet y) { return IPNet(x.masked_addr(), x.overlap(y)); } private: void initialize_from_string(const char *s) throw (InvalidString, InvalidNetmaskLength); A _masked_addr; uint8_t _prefix_len; }; /* ------------------------------------------------------------------------- */ /* Deferred method definitions */ template bool IPNet::operator<(const IPNet& other) const { #if 1 /* * say x = A/B and y = C/D, then * * x < y : * * if x.contains(y) // equal is fine * return false * else if y.strictly_contains(x) // equal already taken care of * return true * else * return A < C * * |---x---| * x=y |---y---| * x>y |-y-| * xcontains(other)) return false; else if (other.contains(*this)) return true; else return this->masked_addr() < other.masked_addr(); #else // old code const A& maddr_him = other.masked_addr(); uint8_t his_prefix_len = other.prefix_len(); //the ordering is important because we want the longest match to //be first. For example, we want the following: // 128.16.0.0/24 < 128.16.64.0/24 < 128.16.0.0/16 < 128.17.0.0/24 if (_prefix_len == his_prefix_len) return _masked_addr < maddr_him; // we need to check the case when one subnet is a subset of // the other if (_prefix_len < his_prefix_len) { A test_addr(maddr_him.mask_by_prefix_len(_prefix_len)); if (_masked_addr == test_addr) { //his subnet is a subset of mine, so he goes first. return (false); } } else if (_prefix_len > his_prefix_len) { A test_addr(_masked_addr.mask_by_prefix_len(his_prefix_len)); if (maddr_him == test_addr) { //my subnet is a subset of his, so I go first. return (true); } } //the subnets don't overlap (or are identical), so just order by address if (_masked_addr < maddr_him) { return (true); } return (false); #endif } template string IPNet::str() const { return _masked_addr.str() + c_format("/%u", XORP_UINT_CAST(_prefix_len)); } template bool IPNet::is_overlap(const IPNet& other) const { if (masked_addr().af() != other.masked_addr().af()) return (false); if (prefix_len() > other.prefix_len()) { // I have smaller prefix size IPNet other_masked(masked_addr(), other.prefix_len()); return (other_masked.masked_addr() == other.masked_addr()); } if (prefix_len() < other.prefix_len()) { // I have bigger prefix size IPNet other_masked(other.masked_addr(), prefix_len()); return (other_masked.masked_addr() == masked_addr()); } // Same prefix size return (other.masked_addr() == masked_addr()); } template bool IPNet::contains(const IPNet& other) const { if (masked_addr().af() != other.masked_addr().af()) return (false); if (prefix_len() > other.prefix_len()) { // I have smaller prefix size, hence I don't contain other. return (false); } if (prefix_len() < other.prefix_len()) { // I have bigger prefix size IPNet other_masked(other.masked_addr(), prefix_len()); return (other_masked.masked_addr() == masked_addr()); } // Same prefix size return (other.masked_addr() == masked_addr()); } template void IPNet::initialize_from_string(const char *cp) throw (InvalidString, InvalidNetmaskLength) { char *slash = strrchr(const_cast(cp), '/'); if (slash == 0) xorp_throw(InvalidString, "Missing slash"); if (*(slash + 1) == 0) xorp_throw(InvalidString, "Missing prefix length"); char *n = slash + 1; while (*n != 0) { if (*n < '0' || *n > '9') { xorp_throw(InvalidString, "Bad prefix length"); } n++; } _prefix_len = atoi(slash + 1); string addr = string(cp, slash - cp); _masked_addr = A(addr.c_str()).mask_by_prefix_len(_prefix_len); } template IPNet& IPNet::operator--() { _masked_addr = _masked_addr >> (_masked_addr.addr_bitlen() - _prefix_len); --_masked_addr; _masked_addr = _masked_addr << (_masked_addr.addr_bitlen() - _prefix_len); return (*this); } template IPNet& IPNet::operator++() { _masked_addr = _masked_addr >> (_masked_addr.addr_bitlen() - _prefix_len); ++_masked_addr; _masked_addr = _masked_addr << (_masked_addr.addr_bitlen() - _prefix_len); return (*this); } template inline uint32_t IPNet::overlap(const IPNet& other) const { if (masked_addr().af() != other.masked_addr().af()) return 0; A xor_addr = masked_addr() ^ other.masked_addr(); uint32_t done = xor_addr.leading_zero_count(); uint32_t p = (prefix_len() < other.prefix_len()) ? prefix_len() : other.prefix_len(); if (done > p) done = p; return done; } /** * Determine the number of the most significant bits overlapping * between two subnets. * * @param a1 the first subnet. * @param a2 the subnet. * @return the number of bits overlapping between @ref a1 and @ref a2. */ template uint32_t overlap(const IPNet& a1, const IPNet& a2) { return a1.overlap(a2); } #endif // __LIBXORP_IPNET_HH__ xorp/libxorp/transaction.hh0000664000076400007640000002013211540225530016200 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/libxorp/transaction.hh,v 1.14 2008/10/02 21:57:36 bms Exp $ #ifndef __LIBXORP_TRANSACTION_HH__ #define __LIBXORP_TRANSACTION_HH__ #include "libxorp/xorp.h" #include "libxorp/eventloop.hh" #include "libxorp/ref_ptr.hh" /** * @short Base class for operations within a Transaction. * * TransactionOperations when realized through derived classes are * operations that can be held and dispatched at a later time. The * @ref TransactionManager class is provided as a container for * TransactionOperations. * * NB TransactionOperation is analogous to the Command pattern in the * BoF: Design Patterns , Erich Gamma, Richard Helm, Ralph Johnson, * John Vlissides, Addison Wesley, 1995, ISBN 0-201-63361-2. */ class TransactionOperation { public: /** * Destructor */ virtual ~TransactionOperation() {} /** * Dispatch operation * * @return true on success, false on error. */ virtual bool dispatch() = 0; /** * @return string representation of operation. */ virtual string str() const = 0; }; /** * @short A class for managing transactions. * * The TransactionManager creates, manages, and dispatches * transactions. A Transaction is comprised of a sequence of @ref * TransactionOperation s. Each transaction is uniquely identified by * a transaction id. */ class TransactionManager { public: typedef ref_ptr Operation; /** * Constuctor with a given event loop, timeout, and max pending commits. * * @param e the EventLoop instance. * * @param timeout_ms the inter-operation addition timeout. If zero, * timeouts are not used, otherwise a timeout will occur * and the transaction aborted if the transaction is not * updated for timeout_ms. * * @param max_pending the maximum number of uncommitted transactions * pending commit. */ TransactionManager(EventLoop& e, uint32_t timeout_ms = 0, uint32_t max_pending = 10) : _e(e), _timeout_ms(timeout_ms), _max_pending(max_pending), _next_tid(0) { } /** * Destructor */ virtual ~TransactionManager() {} /** * Start transaction * * @param new_tid variable to assigned new transaction id. * * @return true on success, false if maximum number of pending * transactions is reached. */ bool start(uint32_t& new_tid); /** * Commit transaction * * @param tid the transaction ID. * @return true on success, false on error. */ bool commit(uint32_t tid); /** * Abort transaction * * @param tid the transaction ID. * @return true on success, false on error. */ bool abort(uint32_t tid); /** * Add operation to transaction. * * @param tid the transaction ID. * @param operation to be added. * @return true on success, false if tid is invalid. */ virtual bool add(uint32_t tid, const Operation& op); /** * Retrieve number of operations in pending transaction. * * @param tid the transaction ID. * * @param count variable to be assigned number of operations in * transaction. * * @return true if tid is valid, false otherwise. */ bool retrieve_size(uint32_t tid, uint32_t& count) const; /** * Get the inter-operation additional timeout. * * If the inter-operation addition timeout is zero, * timeouts are not used, otherwise a timeout will occur * and the transaction aborted if the transaction is not * updated for timeout_ms. * * @return the inter-operation additional timeout. */ uint32_t timeout_ms() const { return _timeout_ms; } /** * Get the maximum number of uncommited pending transactions. * * @return the maximum number of uncommitted transactions pending commit. */ uint32_t max_pending() const { return _max_pending; } /** * Get the current number of uncommited pending transactions. * * @return the current number of uncommitted transactions pending commit. */ uint32_t pending() const { return _transactions.size(); } protected: /** * Overrideable function that can be called before the first * operation in a commit is dispatched. * * Default implementation is a no-op. */ virtual void pre_commit(uint32_t tid); /** * Overrideable function that can be called after commit occurs * * Default implementation is a no-op. */ virtual void post_commit(uint32_t tid); /** * Overrideable function that is called immediately after an individual * operation is dispatched. * * Default implementation is a no-op. * * @param success whether the operation succeed. * * @param op the operation. */ virtual void operation_result(bool success, const TransactionOperation& op); /** * Flush operations in transaction list. May be use by @ref * operation_result methods to safely prevent further operations * being dispatched when errors are detected. flush() always * succeeds if transaction exists. * * @param tid transaction id of transaction to be flushed. * * @return true if transaction exists, false otherwise. */ bool flush(uint32_t tid); protected: /** * Transaction class, just a list of operations to be dispatched. * * It is defined here so classes derived from TransactionManager * can operate, eg sort operations in list, before committing transaction. */ class Transaction { public: typedef list OperationList; Transaction(TransactionManager& mgr, const XorpTimer& timeout_timer) : _mgr(&mgr), _timeout_timer(timeout_timer), _op_count(0) {} Transaction(TransactionManager& mgr) : _mgr(&mgr), _op_count(0) {} Transaction() { _mgr = NULL; } Transaction& operator=(const Transaction& rhs) { _mgr = rhs._mgr; _ops = rhs._ops; _timeout_timer = rhs._timeout_timer; _op_count = rhs._op_count; return *this; } /** Add an operation to list */ void add(const Operation& op); /** Dispatch all operations on list */ void commit(); /** Flush all operations on list */ void flush(); /** Defer timeout by TransactionManagers timeout interval */ void defer_timeout(); /** Cancel timeout timer */ void cancel_timeout(); /** Get the list of operations */ OperationList& operations() { return _ops; } /** Get the length of the operations list. */ uint32_t size() const { return _op_count; } private: TransactionManager* _mgr; OperationList _ops; XorpTimer _timeout_timer; uint32_t _op_count; }; private: /** Called when timeout timer expires */ void timeout(uint32_t tid); /** Increment next transaction id by a randomized amount. */ void crank_tid(); private: typedef map TransactionDB; EventLoop& _e; TransactionDB _transactions; uint32_t _timeout_ms; uint32_t _max_pending; uint32_t _next_tid; friend class Transaction; // for Transaction to call operation_result() }; #endif // __LIBXORP_TRANSACTION_HH__ xorp/libxorp/run_command.hh0000664000076400007640000004257311635757530016211 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __LIBXORP_RUN_COMMAND_HH__ #define __LIBXORP_RUN_COMMAND_HH__ #include "libxorp/asyncio.hh" #include "libxorp/callback.hh" #include "libxorp/timer.hh" class EventLoop; /** * @short Base virtual class for running an external command. */ class RunCommandBase { public: class ExecId; /** * Constructor for a given command. * * @param eventloop the event loop. * @param command the command to execute. * @param real_command_name the real command name. * @param task_priority the priority to read stdout and stderr. */ RunCommandBase(EventLoop& eventloop, const string& command, const string& real_command_name, int task_priority = XorpTask::PRIORITY_DEFAULT); /** * Destructor. */ virtual ~RunCommandBase(); /** * Set the list with the arguments for the command to execute. * * @param v the list with the arguments for the command to execute. */ void set_argument_list(const list& v) { _argument_list = v; } /** * Execute the command. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int execute(); /** * Terminate the command. */ void terminate(); /** * Terminate the command with prejudice. */ void terminate_with_prejudice(); /** * Get the name of the command to execute. * * @return a string with the name of the command to execute. */ const string& command() const { return _command; } /** * Get the list with the arguments of the command to execute. * * @return a string with the arguments of the command to execute. */ const list& argument_list() const { return _argument_list; } /** * Receive a notification that the status of the process has changed. * * @param wait_status the wait status. */ void wait_status_changed(int wait_status); /** * Test if the command has exited. * * @return true if the command has exited, otherwise false. */ bool is_exited() const { return _command_is_exited; } /** * Test if the command has been terminated by a signal. * * @return true if the command has been terminated by a signal, * otherwise false. */ bool is_signal_terminated() const { return _command_is_signal_terminated; } /** * Test if the command has coredumped. * * @return true if the command has coredumped, otherwise false. */ bool is_coredumped() const { return _command_is_coredumped; } /** * Test if the command has been stopped. * * @return true if the command has been stopped, otherwise false. */ bool is_stopped() const { return _command_is_stopped; } /** * Get the command exit status. * * @return the command exit status. */ int exit_status() const { return _command_exit_status; } /** * Get the signal that terminated the command. * * @return the signal that terminated the command. */ int term_signal() const { return _command_term_signal; } /** * Get the signal that stopped the command. * * @return the signal that stopped the command. */ int stop_signal() const { return _command_stop_signal; } /** * Get the priority to use when reading output from the command. * * @return the stdout and stderr priority. */ int task_priority() const { return _task_priority; } /** * Get a reference to the ExecId object. * * @return a reference to the ExecId object that is used * for setting the execution ID when running the command. */ ExecId& exec_id() { return (_exec_id); } /** * Set the execution ID for executing the command. * * @param v the execution ID. */ void set_exec_id(const ExecId& v); /** * @short Class for setting the execution ID when running the command. */ class ExecId { public: /** * Default constructor. */ ExecId(); /** * Constructor for a given user ID. * * @param uid the user ID. */ ExecId(uid_t uid); /** * Constructor for a given user ID and group ID. * * @param uid the user ID. * @param gid the group ID. */ ExecId(uid_t uid, gid_t gid); /** * Save the current execution ID. */ void save_current_exec_id(); /** * Restore the previously saved execution ID. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int restore_saved_exec_id(string& error_msg) const; /** * Set the effective execution ID. * * @param error_msg the error message (if error). * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_effective_exec_id(string& error_msg); /** * Test if the execution ID is set. * * @return true if the execution ID is set, otherwise false. */ bool is_set() const; /** * Get the user ID. * * @return the user ID. */ uid_t uid() const { return (_uid); } /** * Get the group ID. * * @return the group ID. */ gid_t gid() const { return (_gid); } /** * Set the user ID. * * @param v the user ID. */ void set_uid(uid_t v) { _uid = v; _is_uid_set = true; } /** * Set the group ID. * * @param v the group ID. */ void set_gid(gid_t v) { _gid = v; _is_gid_set = true; } /** * Test if the user ID was assigned. * * @return true if the user ID was assigned, otherwise false. */ bool is_uid_set() const { return (_is_uid_set); } /** * Test if the group ID was assigned. * * @return true if the group ID was assigned, otherwise false. */ bool is_gid_set() const { return (_is_gid_set); } /** * Reset the assigned user ID and group ID. */ void reset(); private: uid_t saved_uid() const { return (_saved_uid); } uid_t saved_gid() const { return (_saved_gid); } uid_t _uid; gid_t _gid; bool _is_uid_set; bool _is_gid_set; uid_t _saved_uid; gid_t _saved_gid; bool _is_exec_id_saved; }; private: /** * A pure virtual method called when there is output on the program's * stdout. * * @param output the string with the output. */ virtual void stdout_cb_dispatch(const string& output) = 0; /** * A pure virtual method called when there is output on the program's * stderr. * * @param output the string with the output. */ virtual void stderr_cb_dispatch(const string& output) = 0; /** * A pure virtual method called when the program execution is completed. * * @param success if true the program execution has succeeded, otherwise * it has failed. * @param error_msg if error, the string with the error message. */ virtual void done_cb_dispatch(bool success, const string& error_msg) = 0; /** * A pure virtual method called when the program has been stopped. * * @param stop_signal the signal used to stop the program. */ virtual void stopped_cb_dispatch(int stop_signal) = 0; /** * Test if the stderr should be redirected to stdout. * * @return true if the stderr should be redirected to stdout, otherwise * false. */ virtual bool redirect_stderr_to_stdout() const = 0; /** * Cleanup state. */ void cleanup(); /** * Block child process signal(s) in current signal mask. */ int block_child_signals(); /** * Unblock child process signal(s) in current signal mask. */ int unblock_child_signals(); /** * Terminate the process. * * @param with_prejudice if true then terminate the process with * prejudice, otherwise attempt to kill it gracefully. */ void terminate_process(bool with_prejudice); /** * Close the output for the command. */ void close_output(); /** * Close the stdout output for the command. */ void close_stdout_output(); /** * Close the stderr output for the command. */ void close_stderr_output(); /** * Set the command status. * * @param status the command status. */ void set_command_status(int status); /** * Append data from the command. * * @param event the event from the command (@see AsyncFileOperator::Event). * @param buffer the buffer with the data. * @param buffer_bytes the maximum number of bytes in the buffer. * @param offset offset of the last byte read. */ void append_data(AsyncFileOperator::Event event, const uint8_t* buffer, size_t buffer_bytes, size_t offset); /** * The command's I/O operation has completed. * * @param event the last event from the command. * (@see AsyncFileOperator::Event). * @param error_code the error code if the @ref event indicates an error * (e.g., if it is not equal to AsyncFileOperator::END_OF_FILE), otherwise * its value is ignored. */ void io_done(AsyncFileOperator::Event event, int error_code); /** * The command has completed. * * @param done_timer the timer associated with the event. */ void done(XorpTimer& done_timer); #ifdef HOST_OS_WINDOWS static const int WIN32_PROC_TIMEOUT_MS = 500; void win_proc_done_cb(XorpFd fd, IoEventType type); void win_proc_reaper_cb(); #endif static const size_t BUF_SIZE = 8192; EventLoop& _eventloop; string _command; string _real_command_name; list _argument_list; AsyncFileReader* _stdout_file_reader; AsyncFileReader* _stderr_file_reader; FILE* _stdout_stream; FILE* _stderr_stream; uint8_t _stdout_buffer[BUF_SIZE]; uint8_t _stderr_buffer[BUF_SIZE]; size_t _last_stdout_offset; size_t _last_stderr_offset; pid_t _pid; #ifdef HOST_OS_WINDOWS HANDLE _ph; XorpTimer _reaper_timer; #endif bool _is_error; string _error_msg; bool _is_running; ExecId _exec_id; bool _command_is_exited; bool _command_is_signal_terminated; bool _command_is_coredumped; bool _command_is_stopped; int _command_exit_status; int _command_term_signal; int _command_stop_signal; XorpTimer _done_timer; bool _stdout_eof_received; bool _stderr_eof_received; int _task_priority; }; /** * @short A class for running an external command. */ class RunCommand : public RunCommandBase { public: typedef XorpCallback2::RefPtr OutputCallback; typedef XorpCallback3::RefPtr DoneCallback; typedef XorpCallback2::RefPtr StoppedCallback; /** * Constructor for a given command and its list with arguments. * * @param eventloop the event loop. * @param command the command to execute. * @param argument_list the list with the arguments for the command * to execute. * @param stdout_cb the callback to call when there is data on the * standard output. * @param stderr_cb the callback to call when there is data on the * standard error. * @param done_cb the callback to call when the command is completed. * @param redirect_stderr_to_stdout if true redirect the stderr to stdout. * @param task_priority the priority to read stdout and stderr. */ RunCommand(EventLoop& eventloop, const string& command, const list& argument_list, RunCommand::OutputCallback stdout_cb, RunCommand::OutputCallback stderr_cb, RunCommand::DoneCallback done_cb, bool redirect_stderr_to_stdout, int task_priority = XorpTask::PRIORITY_DEFAULT); /** * Set the callback to dispatch when the program is stopped. * * @param cb the callback's value. */ void set_stopped_cb(StoppedCallback cb) { _stopped_cb = cb; } private: /** * A method called when there is output on the program's stdout. * * @param output the string with the output. */ void stdout_cb_dispatch(const string& output) { _stdout_cb->dispatch(this, output); } /** * A method called when there is output on the program's stderr. * * @param output the string with the output. */ void stderr_cb_dispatch(const string& output) { _stderr_cb->dispatch(this, output); } /** * A method called when the program execution is completed. * * @param success if true the program execution has succeeded, otherwise * it has failed. * @param error_msg if error, the string with the error message. */ void done_cb_dispatch(bool success, const string& error_msg) { _done_cb->dispatch(this, success, error_msg); } /** * A method called when the program has been stopped. * * @param stop_signal the signal used to stop the program. */ void stopped_cb_dispatch(int stop_signal) { if (! _stopped_cb.is_empty()) _stopped_cb->dispatch(this, stop_signal); } /** * Test if the stderr should be redirected to stdout. * * @return true if the stderr should be redirected to stdout, otherwise * false. */ bool redirect_stderr_to_stdout() const { return (_redirect_stderr_to_stdout); } OutputCallback _stdout_cb; OutputCallback _stderr_cb; DoneCallback _done_cb; StoppedCallback _stopped_cb; bool _redirect_stderr_to_stdout; }; /** * @short A class for running an external command by invoking a shell. */ class RunShellCommand : public RunCommandBase { public: typedef XorpCallback2::RefPtr OutputCallback; typedef XorpCallback3::RefPtr DoneCallback; typedef XorpCallback2::RefPtr StoppedCallback; /** * Constructor for a given command and its list with arguments. * * @param eventloop the event loop. * @param command the command to execute. * @param argument_string the string with the arguments for the command * to execute. * @param stdout_cb the callback to call when there is data on the * standard output. * @param stderr_cb the callback to call when there is data on the * standard error. * @param done_cb the callback to call when the command is completed. * @param task_priority the priority to read stdout and stderr. */ RunShellCommand(EventLoop& eventloop, const string& command, const string& argument_string, RunShellCommand::OutputCallback stdout_cb, RunShellCommand::OutputCallback stderr_cb, RunShellCommand::DoneCallback done_cb, int task_priority = XorpTask::PRIORITY_DEFAULT); /** * Set the callback to dispatch when the program is stopped. * * @param cb the callback's value. */ void set_stopped_cb(StoppedCallback cb) { _stopped_cb = cb; } private: /** * A method called when there is output on the program's stdout. * * @param output the string with the output. */ void stdout_cb_dispatch(const string& output) { _stdout_cb->dispatch(this, output); } /** * A method called when there is output on the program's stderr. * * @param output the string with the output. */ void stderr_cb_dispatch(const string& output) { _stderr_cb->dispatch(this, output); } /** * A method called when the program execution is completed. * * @param success if true the program execution has succeeded, otherwise * it has failed. * @param error_msg if error, the string with the error message. */ void done_cb_dispatch(bool success, const string& error_msg) { _done_cb->dispatch(this, success, error_msg); } /** * A method called when the program has been stopped. * * @param stop_signal the signal used to stop the program. */ void stopped_cb_dispatch(int stop_signal) { if (! _stopped_cb.is_empty()) _stopped_cb->dispatch(this, stop_signal); } /** * Test if the stderr should be redirected to stdout. * * @return true if the stderr should be redirected to stdout, otherwise * false. */ bool redirect_stderr_to_stdout() const { // // XXX: Redirecting stderr to stdout is always disabled by defailt. // If we want stderr to be redirected, this should be specified // by the executed command itself. E.g.: // "my_command my_args 2>&1" // return (false); } OutputCallback _stdout_cb; OutputCallback _stderr_cb; DoneCallback _done_cb; StoppedCallback _stopped_cb; }; #endif // __LIBXORP_RUN_COMMAND_HH__ xorp/libxorp/tokenize.hh0000664000076400007640000000300111540225530015477 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __LIBXORP_TOKENIZE_HH__ #define __LIBXORP_TOKENIZE_HH__ /* ** Tokenizer. */ inline void tokenize(const string& str, vector& tokens, const string& delimiters = " ") { string::size_type begin = str.find_first_not_of(delimiters, 0); string::size_type end = str.find_first_of(delimiters, begin); while(string::npos != begin || string::npos != end) { tokens.push_back(str.substr(begin, end - begin)); begin = str.find_first_not_of(delimiters, end); end = str.find_first_of(delimiters, begin); } } #endif // __LIBXORP_TOKENIZE_HH__ xorp/libxorp/time_slice.hh0000664000076400007640000000752311421137511016000 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/libxorp/time_slice.hh,v 1.13 2008/10/02 21:57:35 bms Exp $ #ifndef __LIBXORP_TIME_SLICE_HH__ #define __LIBXORP_TIME_SLICE_HH__ // // Time-slice class declaration. // #ifdef HAVE_SYS_TIME_H #include #endif #include "timer.hh" #include "timeval.hh" // // Constants definitions // // // Structures/classes, typedefs and macros // /** * @short A class for computing whether some processing is taking too long. * * This class can be used to compute whether some processing is taking * too long time to complete. It is up to the program that uses * TimeSlice to check whether the processing is taking too long, * and suspend processing of that task if necessary. * * Example of usage:
    TimeSlice time_slice(100000, 20); // 100ms, test every 20th iteration
    
    for (size_t i = 0; i < 1000000000; i++) {
        if (time_slice.is_expired()) {
            // Stop processing.
            // Schedule a timer after 0 ms to continue processing.
            // If needed, save state to continue from here.
            return;
        }
        // Do something CPU-hungry
    }
    time_slice.reset(); // Prepare it again for later usage if needed
*/ class TimeSlice { public: /** * Constructor for a given time limit and test frequency. * * Create a TimeSlice object that will measure time slices * with given frequency of testing. * The time slice is measured once in @ref test_iter_frequency * times when method is_expired() is called. * * @param usec_limit the time slice to measure in microseconds. * @param test_iter_frequency the frequency of measuring the time slice. */ TimeSlice(uint32_t usec_limit, size_t test_iter_frequency); /** * Reset the TimeSlice object. */ void reset(); /** * Test if the time slice has expired. * * If the time slice has expired, automatically prepare this object * to start measuring again the time slice. * * @return true if the time slice has expired, otherwise false. */ bool is_expired(); private: TimeVal _time_slice_limit; // The time slice to measure const size_t _test_iter_frequency; // The frequency of measuring TimeVal _time_slice_start; // When the time slice started size_t _remain_iter; // Remaning iterations to // check the time }; // // Deferred definitions // inline bool TimeSlice::is_expired() { if (--_remain_iter) return (false); // Don't test the time yet _remain_iter = _test_iter_frequency; // Test if the time slice has expired TimeVal now; TimerList::system_gettimeofday(&now); if (now - _time_slice_limit < _time_slice_start) return (false); // The time slice has not expired // The time slice has expired _time_slice_start = now; return (true); } // // Global variables // // // Global functions prototypes // #endif // __LIBXORP_TIME_SLICE_HH__ xorp/libxorp/asyncio.cc0000664000076400007640000005525711632177530015336 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "libxorp_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "libxorp/eventloop.hh" #include #ifdef HAVE_SYS_UIO_H #include #endif #include "xorpfd.hh" #include "asyncio.hh" #ifdef HOST_OS_WINDOWS # define EDGE_TRIGGERED_READ_LATENCY // IOT_READ may be delayed. # define EDGE_TRIGGERED_WRITES // IOT_WRITE is edge triggered. # include "win_dispatcher.hh" # include "win_io.h" #endif static class TraceAIO { public: TraceAIO() { _do_trace = !(getenv("AIOTRACE") == 0); } bool on() const { return _do_trace; } protected: bool _do_trace; } aio_trace; // ---------------------------------------------------------------------------- // Utility bool is_pseudo_error(const char* name, XorpFd fd, int error_num) { UNUSED(fd); UNUSED(name); switch (error_num) { #ifdef HOST_OS_WINDOWS case ERROR_IO_PENDING: XLOG_WARNING("%s (fd = %p) got ERROR_IO_PENDING, continuing.", name, (void *)fd); return true; case WSAEINTR: XLOG_WARNING("%s (fd = %p) got WSAEINTR, continuing.", name, (void *)fd); return true; case WSAEWOULDBLOCK: XLOG_WARNING("%s (fd = %p) got WSAEWOULDBLOCK, continuing.", name, (void *)fd); return true; case WSAEINPROGRESS: XLOG_WARNING("%s (fd = %p) got WSAEINPROGRESS, continuing.", name, (void *)fd); return true; #else // ! HOST_OS_WINDOWS case EINTR: XLOG_WARNING("%s (fd = %d) got EINTR, continuing.", name, XORP_INT_CAST(fd)); return true; case EWOULDBLOCK: XLOG_WARNING("%s (fd = %d) got EWOULDBLOCK, continuing.", name, XORP_INT_CAST(fd)); return true; #endif // ! HOST_OS_WINDOWS } return false; } // ---------------------------------------------------------------------------- AsyncFileOperator::~AsyncFileOperator() { } string AsyncFileOperator::toString() const { ostringstream oss; oss << " fd: " << _fd.str() << " running: " << _running << " last_err: " << _last_error << " priority: " << _priority << flush; return oss.str(); } // ---------------------------------------------------------------------------- // AsyncFileReader read method and entry hook AsyncFileReader::AsyncFileReader(EventLoop& e, XorpFd fd, int priority) : AsyncFileOperator(e, fd, priority) { } AsyncFileReader::~AsyncFileReader() { stop(); delete_pointers_list(_buffers); } void AsyncFileReader::add_buffer(uint8_t* b, size_t b_bytes, const Callback& cb) { assert(b_bytes != 0); _buffers.push_back(new BufferInfo(b, b_bytes, cb)); if (aio_trace.on()) { XLOG_INFO("afr: %p add_buffer sz: %i buffers: %i\n", this, (int)(b_bytes), (int)(_buffers.size())); } } void AsyncFileReader::add_buffer_with_offset(uint8_t* b, size_t b_bytes, size_t off, const Callback& cb) { assert(off < b_bytes); _buffers.push_back(new BufferInfo(b, b_bytes, off, cb)); if (aio_trace.on()) { XLOG_INFO("afr: %p add_buffer_w/offset sz: %i buffers: %i\n", this, (int)(b_bytes), (int)(_buffers.size())); } } #ifdef HOST_OS_WINDOWS void AsyncFileReader::disconnect(XorpFd fd, IoEventType type) { assert(type == IOT_DISCONNECT); assert(fd == _fd); assert(fd.is_valid()); assert(fd.is_socket()); debug_msg("IOT_DISCONNECT close detected (reader side)\n"); BufferInfo* head = _buffers.front(); head->dispatch_callback(END_OF_FILE); } #endif // HOST_OS_WINDOWS void AsyncFileReader::read(XorpFd fd, IoEventType type) { #ifdef EDGE_TRIGGERED_READ_LATENCY if (_running == false) return; #endif assert(type == IOT_READ); assert(fd == _fd); assert(_buffers.empty() == false); debug_msg("Buffer count %u\n", XORP_UINT_CAST(_buffers.size())); BufferInfo* head = _buffers.front(); ssize_t done = 0; #ifdef HOST_OS_WINDOWS BOOL result = FALSE; switch (fd.type()) { case XorpFd::FDTYPE_SOCKET: done = recv(_fd.getSocket(), (char *)(head->buffer() + head->offset()), head->buffer_bytes() - head->offset(), 0); if (done == SOCKET_ERROR) { _last_error = WSAGetLastError(); done = -1; XLOG_WARNING("read error: _fd: %i offset: %i total-len: %i error: %s\n", (int)(_fd), (int)(head->offset()), (int)(head->buffer_bytes()), XSTRERROR); } else if (done == 0) { // Graceful close; make sure complete_transfer() gets this. debug_msg("graceful close detected\n"); _last_error = WSAENOTCONN; done = -1; } break; case XorpFd::FDTYPE_PIPE: // XXX: Return values need review. done = win_pipe_read(_fd, head->buffer() + head->offset(), head->buffer_bytes() - head->offset()); _last_error = GetLastError(); break; case XorpFd::FDTYPE_CONSOLE: // XXX: Return values need review. done = win_con_read(_fd, head->buffer() + head->offset(), head->buffer_bytes() - head->offset()); _last_error = GetLastError(); break; case XorpFd::FDTYPE_FILE: result = ReadFile(_fd, (LPVOID)(head->buffer() + head->offset()), (DWORD)(head->buffer_bytes() - head->offset()), (LPDWORD)&done, NULL); if (result == FALSE) { _last_error = GetLastError(); SetLastError(ERROR_SUCCESS); } break; default: XLOG_FATAL("Invalid descriptor type."); break; } #else // ! HOST_OS_WINDOWS errno = 0; _last_error = 0; done = ::read(_fd, head->buffer() + head->offset(), head->buffer_bytes() - head->offset()); if (done < 0) { _last_error = errno; XLOG_WARNING("read error: _fd: %i offset: %i total-len: %i error: %s\n", (int)(_fd), (int)(head->offset()), (int)(head->buffer_bytes()), strerror(errno)); } errno = 0; #endif // ! HOST_OS_WINDOWS if (aio_trace.on()) { XLOG_INFO("afr: %p Read %d bytes, last-err: %i\n", this, XORP_INT_CAST(done), _last_error); } if (done < 0 && is_pseudo_error("AsyncFileReader", _fd, _last_error)) { return; } complete_transfer(_last_error, done); #ifdef EDGE_TRIGGERED_READ_LATENCY // // If there's still data which we can read without blocking, // and we didn't fill our buffers, then try to read again // without waiting for an IOT_READ dispatch, as it may not come, // or be delayed due to latency between the primary thread // and the Winsock thread. // if (_fd.is_socket() && !_deferred_io_task.scheduled()) { u_long remaining = 0; int result = ioctlsocket(_fd.getSocket(), FIONREAD, &remaining); if (result != SOCKET_ERROR && remaining > 0) { _deferred_io_task = _eventloop.new_oneoff_task( callback(this, &AsyncFileReader::read, _fd, IOT_READ)); XLOG_ASSERT(_deferred_io_task.scheduled()); } } #endif // EDGE_TRIGGERED_READ_LATENCY } // transfer_complete() invokes callbacks if necessary and updates buffer // variables and buffer list. void AsyncFileReader::complete_transfer(int err, ssize_t done) { // XXX careful after callback is invoked: "this" maybe deleted, so do // not reference any object state after callback. if (done > 0) { BufferInfo* head = _buffers.front(); head->incr_offset(done); if (head->offset() == head->buffer_bytes()) { _buffers.pop_front(); if (_buffers.empty()) { stop(); } head->dispatch_callback(DATA); delete head; } else { head->dispatch_callback(DATA); } return; } BufferInfo* head = _buffers.front(); if (err != 0 || done < 0) { stop(); head->dispatch_callback(OS_ERROR); } else { head->dispatch_callback(END_OF_FILE); } } bool AsyncFileReader::start() { if (_running) return true; if (_buffers.empty() == true) { XLOG_WARNING("Could not start reader - no buffers available"); return false; } EventLoop& e = _eventloop; if (e.add_ioevent_cb(_fd, IOT_READ, callback(this, &AsyncFileReader::read), _priority) == false) { XLOG_ERROR("AsyncFileReader: Failed to add ioevent callback."); return false; } #ifdef HOST_OS_WINDOWS // Windows notifies us of disconnections using a separate flag. // The file descriptor may no longer be valid when we stop, so // mark the IOT_DISCONNECT callback as being added using a boolean. _disconnect_added = false; if (_fd.is_socket()) { _disconnect_added = e.add_ioevent_cb( _fd, IOT_DISCONNECT, callback(this, &AsyncFileReader::disconnect), _priority); if (_disconnect_added == false) { XLOG_ERROR("AsyncFileReader: Failed to add ioevent callback."); _eventloop.remove_ioevent_cb(_fd, IOT_READ); return false; } } #endif // HOST_OS_WINDOWS debug_msg("%p start\n", this); _running = true; return _running; } void AsyncFileReader::stop() { debug_msg("%p stop\n", this); _eventloop.remove_ioevent_cb(_fd, IOT_READ); #ifdef EDGE_TRIGGERED_WRITES _deferred_io_task.unschedule(); #endif #ifdef HOST_OS_WINDOWS if (_disconnect_added == true) { _eventloop.remove_ioevent_cb(_fd, IOT_DISCONNECT); _disconnect_added = false; } #endif _running = false; } void AsyncFileReader::flush_buffers() { stop(); while (_buffers.empty() == false) { BufferInfo* head = _buffers.front(); _buffers.pop_front(); head->dispatch_callback(FLUSHING); delete head; } } string AsyncFileReader::toString() const { ostringstream oss; oss << AsyncFileOperator::toString() << " buffers: " << _buffers.size() << endl; #ifdef HOST_OS_WINDOWS oss << " disconnect-added: " << _disconnect_added << endl; #endif return oss.str(); } // ---------------------------------------------------------------------------- // AsyncFileWriter write method and entry hook #ifndef MAX_IOVEC #define MAX_IOVEC 16 #endif AsyncFileWriter::AsyncFileWriter(EventLoop& e, XorpFd fd, uint32_t coalesce, int priority) : AsyncFileOperator(e, fd, priority) { static const uint32_t max_coalesce = 16; _coalesce = (coalesce > MAX_IOVEC) ? MAX_IOVEC : coalesce; if (_coalesce > max_coalesce) { _coalesce = max_coalesce; } _iov = new iovec[_coalesce]; _dtoken = new int; } AsyncFileWriter::~AsyncFileWriter() { stop(); delete[] _iov; delete_pointers_list(_buffers); } void AsyncFileWriter::add_buffer(const uint8_t* b, size_t b_bytes, const Callback& cb) { assert(b_bytes != 0); _buffers.push_back(new BufferInfo(b, b_bytes, cb)); #ifdef EDGE_TRIGGERED_WRITES if (_running && !_deferred_io_task.scheduled()) { _deferred_io_task = _eventloop.new_oneoff_task( callback(this, &AsyncFileWriter::write, _fd, IOT_WRITE)); XLOG_ASSERT(_deferred_io_task.scheduled()); } #endif // EDGE_TRIGGERED_WRITES if (aio_trace.on()) { XLOG_INFO("afw: %p add_buffer sz: %i buffers: %i\n", this, (int)(b_bytes), (int)(_buffers.size())); } } void AsyncFileWriter::add_buffer_sendto(const uint8_t* b, size_t b_bytes, const IPvX& dst_addr, uint16_t dst_port, const Callback& cb) { assert(b_bytes != 0); _buffers.push_back(new BufferInfo(b, b_bytes, dst_addr, dst_port, cb)); #ifdef EDGE_TRIGGERED_WRITES if (_running && !_deferred_io_task.scheduled()) { _deferred_io_task = _eventloop.new_oneoff_task( callback(this, &AsyncFileWriter::write, _fd, IOT_WRITE)); XLOG_ASSERT(_deferred_io_task.scheduled()); } #endif // EDGE_TRIGGERED_WRITES if (aio_trace.on()) { XLOG_INFO("afw: %p add_buffer-sendto sz: %i buffers: %i\n", this, (int)(b_bytes), (int)(_buffers.size())); } } void AsyncFileWriter::add_buffer_with_offset(const uint8_t* b, size_t b_bytes, size_t off, const Callback& cb) { assert(off < b_bytes); _buffers.push_back(new BufferInfo(b, b_bytes, off, cb)); #ifdef EDGE_TRIGGERED_WRITES if (_running && !_deferred_io_task.scheduled()) { _deferred_io_task = _eventloop.new_oneoff_task( callback(this, &AsyncFileWriter::write, _fd, IOT_WRITE)); XLOG_ASSERT(_deferred_io_task.scheduled()); } #endif // EDGE_TRIGGERED_WRITES if (aio_trace.on()) { XLOG_INFO("afw: %p add_buffer-w/offset sz: %i buffers: %i\n", this, (int)(b_bytes), (int)(_buffers.size())); } } void AsyncFileWriter::add_data(const vector& data, const Callback& cb) { assert(data.size() != 0); _buffers.push_back(new BufferInfo(data, cb)); #ifdef EDGE_TRIGGERED_WRITES if (_running && !_deferred_io_task.scheduled()) { _deferred_io_task = _eventloop.new_oneoff_task( callback(this, &AsyncFileWriter::write, _fd, IOT_WRITE)); XLOG_ASSERT(_deferred_io_task.scheduled()); } #endif // EDGE_TRIGGERED_WRITES if (aio_trace.on()) { XLOG_INFO("afw: %p add_data sz: %i buffers: %i\n", this, (int)(data.size()), (int)(_buffers.size())); } } void AsyncFileWriter::add_data_sendto(const vector& data, const IPvX& dst_addr, uint16_t dst_port, const Callback& cb) { assert(data.size() != 0); _buffers.push_back(new BufferInfo(data, dst_addr, dst_port, cb)); #ifdef EDGE_TRIGGERED_WRITES if (_running && !_deferred_io_task.scheduled()) { _deferred_io_task = _eventloop.new_oneoff_task( callback(this, &AsyncFileWriter::write, _fd, IOT_WRITE)); XLOG_ASSERT(_deferred_io_task.scheduled()); } #endif // EDGE_TRIGGERED_WRITES if (aio_trace.on()) { XLOG_INFO("afw: %p add_data-sendto sz: %i buffers: %i\n", this, (int)(data.size()), (int)(_buffers.size())); } } string AsyncFileWriter::toString() const { ostringstream oss; oss << AsyncFileOperator::toString() << " buffers: " << _buffers.size() << endl; return oss.str(); } // // Different UNIX platforms have different iov.iov_base types which // we can fix at compile time. The general idea of writev doesn't // change much across UNIX platforms. // template static void iov_place(T*& iov_base, U& iov_len, uint8_t* data, size_t data_len) { x_static_assert(sizeof(T*) == sizeof(uint8_t*)); iov_base = reinterpret_cast(data); iov_len = data_len; } #ifdef HOST_OS_WINDOWS void AsyncFileWriter::disconnect(XorpFd fd, IoEventType type) { assert(type == IOT_DISCONNECT); assert(fd == _fd); assert(fd.is_valid()); assert(fd.is_socket()); debug_msg("IOT_DISCONNECT close detected (writer side)\n"); } #endif // HOST_OS_WINDOWS void AsyncFileWriter::write(XorpFd fd, IoEventType type) { bool is_sendto = false; IPvX dst_addr; uint16_t dst_port = 0; uint32_t iov_cnt = 0; size_t total_bytes = 0; ssize_t done = 0; int flags = 0; #ifndef HOST_OS_WINDOWS bool mod_signals = true; #endif #ifdef MSG_NOSIGNAL flags |= MSG_NOSIGNAL; mod_signals = false; #endif #ifdef EDGE_TRIGGERED_WRITES if (!_running) return; #endif assert(type == IOT_WRITE); assert(fd == _fd); assert(_buffers.empty() == false); _last_error = 0; errno = 0; // // Group together a number of buffers. // If the buffer is sendto()-type, then send that buffer on its own. // list::const_iterator i = _buffers.begin(); while (i != _buffers.end()) { const BufferInfo* bi = *i; is_sendto = bi->is_sendto(); if (is_sendto && (iov_cnt > 0)) { // XXX: Send first all buffers before this sendto()-type break; } uint8_t* u = const_cast(bi->buffer() + bi->offset()); size_t u_bytes = bi->buffer_bytes() - bi->offset(); iov_place(_iov[iov_cnt].iov_base, _iov[iov_cnt].iov_len, u, u_bytes); total_bytes += u_bytes; assert(total_bytes != 0); iov_cnt++; if (is_sendto) { dst_addr = bi->dst_addr(); dst_port = bi->dst_port(); break; } if (iov_cnt == _coalesce) break; ++i; } if (is_sendto) { // // Use sendto(2) to send the data from the first buffer only // XLOG_ASSERT(! dst_addr.is_zero()); switch (dst_addr.af()) { case AF_INET: { struct sockaddr_in sin; dst_addr.copy_out(sin); sin.sin_port = htons(dst_port); done = ::sendto(_fd.getSocket(), XORP_CONST_BUF_CAST(_iov[0].iov_base), _iov[0].iov_len, flags, reinterpret_cast(&sin), sizeof(sin)); break; } #ifdef HAVE_IPV6 case AF_INET6: { struct sockaddr_in6 sin6; dst_addr.copy_out(sin6); sin6.sin6_port = htons(dst_port); done = ::sendto(_fd.getSocket(), XORP_CONST_BUF_CAST(_iov[0].iov_base), _iov[0].iov_len, flags, reinterpret_cast(&sin6), sizeof(sin6)); break; } #endif // HAVE_IPV6 default: XLOG_ERROR("Address family %d is not supported", dst_addr.af()); done = _iov[0].iov_len; // XXX: Pretend that the data was sent break; } if (done < 0) { #ifdef HOST_OS_WINDOWS _last_error = WSAGetLastError(); #else _last_error = errno; #endif } } else { // // Write the data to the socket/file descriptor // #ifdef HOST_OS_WINDOWS if (fd.is_socket()) { // Socket handles take non-blocking writes. // WSASend() approximates writev(). int result = WSASend(_fd.getSocket(), (LPWSABUF)_iov, iov_cnt, (LPDWORD)&done, 0, NULL, NULL); _last_error = (result == SOCKET_ERROR) ? WSAGetLastError() : 0; if (_last_error != 0) { done = -1; debug_msg("writer: winsock error %d\n", _last_error); } } else { // Non-socket handles take blocking writes. // There is no writev() equivalent, so emulate it. BOOL result = TRUE; DWORD done2; for (uint32_t j = 0; j < iov_cnt; j++) { done2 = 0; result = WriteFile(_fd, (LPVOID)_iov[j].iov_base, (DWORD)_iov[j].iov_len, (LPDWORD)&done2, NULL); done += done2; if (result == FALSE) break; } _last_error = (result == FALSE) ? GetLastError() : 0; } #else // ! HOST_OS_WINDOWS if ((iov_cnt == 1) && (! mod_signals)) { // // No need for coalesce, so use send(2). This saves us // two sigaction calls since we can pass the MSG_NOSIGNAL flag. // done = ::send(_fd, XORP_CONST_BUF_CAST(_iov[0].iov_base), _iov[0].iov_len, flags); if (done < 0) _last_error = errno; } else { done = ::writev(_fd, _iov, (int)iov_cnt); if (done < 0) _last_error = errno; } errno = 0; #endif // ! HOST_OS_WINDOWS } if (aio_trace.on()) { XLOG_INFO("afw: %p Wrote %d of %u bytes, last-err: %i\n", this, XORP_INT_CAST(done), XORP_UINT_CAST(total_bytes), _last_error); } if (done < 0 && is_pseudo_error("AsyncFileWriter", _fd, _last_error)) { XLOG_WARNING("Write error %d\n", _last_error); return; } complete_transfer(done); #ifdef EDGE_TRIGGERED_WRITES if (!(_buffers.empty() || _deferred_io_task.scheduled())) { _deferred_io_task = _eventloop.new_oneoff_task( callback(this, &AsyncFileWriter::write, _fd, IOT_WRITE)); XLOG_ASSERT(_deferred_io_task.scheduled()); } #endif // EDGE_TRIGGERED_WRITES } // transfer_complete() invokes callbacks if necessary and updates buffer // variables and buffer list. void AsyncFileWriter::complete_transfer(ssize_t sdone) { if (sdone < 0) { do { // XXX: don't print an error if the error code is EPIPE #ifdef EPIPE if (_last_error == EPIPE) break; #endif XLOG_ERROR("Write error %d\n", _last_error); } while (false); stop(); BufferInfo* head = _buffers.front(); head->dispatch_callback(OS_ERROR); return; } size_t notified = 0; size_t done = (size_t)sdone; // // This is a trick to detect if the instance of the current object is // deleted mid-callback. If so the method should not touch any part of // the instance state afterwards and should just return. Okay, so how // to tell if the current AsyncFileWriter instance is deleted? // // The key observation is that _dtoken is a reference counted object // associated with the current instance. Another reference is made to it // here bumping the reference count from 1 to 2. If after invoking // a callback the instance count is no longer 2 then the AsyncFileWriter // instance was deleted in the callback. // ref_ptr stack_token = _dtoken; while (notified != done) { assert(notified <= done); assert(_buffers.empty() == false); BufferInfo* head = _buffers.front(); assert(head->buffer_bytes() >= head->offset()); size_t bytes_needed = head->buffer_bytes() - head->offset(); if (done - notified >= bytes_needed) { // // All data in this buffer has been written // head->incr_offset(bytes_needed); assert(head->offset() == head->buffer_bytes()); // Detach head buffer and update state _buffers.pop_front(); if (_buffers.empty()) { stop(); } assert(stack_token.is_only() == false); head->dispatch_callback(DATA); delete head; if (stack_token.is_only() == true) { // "this" instance of AsyncFileWriter was deleted by the // calback, return immediately. return; } notified += bytes_needed; continue; } else { // // Not enough data has been written // head->incr_offset(done - notified); assert(head->offset() < head->buffer_bytes()); return; } } } bool AsyncFileWriter::start() { if (_running) return true; if (_buffers.empty() == true) { XLOG_WARNING("Could not start writer - no buffers available"); return false; } EventLoop& e = _eventloop; if (e.add_ioevent_cb(_fd, IOT_WRITE, callback(this, &AsyncFileWriter::write), _priority) == false) { XLOG_ERROR("AsyncFileWriter: Failed to add I/O event callback."); return false; } #ifdef EDGE_TRIGGERED_WRITES if (!_deferred_io_task.scheduled()) { _deferred_io_task = _eventloop.new_oneoff_task( callback(this, &AsyncFileWriter::write, _fd, IOT_WRITE)); } #endif // EDGE_TRIGGERED_WRITES _running = true; debug_msg("%p start\n", this); return _running; } void AsyncFileWriter::stop() { debug_msg("%p stop\n", this); #ifdef EDGE_TRIGGERED_WRITES _deferred_io_task.unschedule(); #endif // EDGE_TRIGGERED_WRITES _eventloop.remove_ioevent_cb(_fd, IOT_WRITE); _running = false; } void AsyncFileWriter::flush_buffers() { stop(); while (_buffers.empty() == false) { BufferInfo* head = _buffers.front(); _buffers.pop_front(); head->dispatch_callback(FLUSHING); delete head; } } xorp/libxorp/callback_nodebug.hh0000664000076400007640000633615511421137511017136 0ustar greearbgreearb/* * Copyright (c) 2001-2009 XORP, Inc. * See the XORP LICENSE.lgpl file for licensing, conditions, and warranties * on use. * * This file is PROGRAMMATICALLY GENERATED. * * This instance was generated with: * callback-gen.py -b 6 -l 15 */ /** * @libdoc Callbacks * * @sect Callback Overview * * XORP is an asynchronous programming environment and as a result there * are many places where callbacks are useful. Callbacks are typically * invoked to signify the completion or advancement of an asynchronous * operation. * * XORP provides a generic and flexible callback interface that utilizes * overloaded templatized functions for for generating callbacks * in conjunction with many small templatized classes. Whilst this makes * the syntax a little ugly, it provides a great deal of flexibility. * * XorpCallbacks are callback objects are created by the callback() * function which returns a reference pointer to a newly created callback * object. The callback is invoked by calling dispatch(), eg. *

#include "libxorp/xorp.h"
#include "libxorp/callback.hh"


static void hello_world() {
    cout << "Hello World" << endl;
}

int main() {
    // Typedef callback() return type for readability.  SimpleCallback
    // declares a XorpCallback taking 0 arguments and of return type void.
    typedef XorpCallback0::RefPtr SimpleCallback;

    // Create XorpCallback object using callback()
    SimpleCallback cb = callback(hello_world);

    // Invoke callback, results in call to hello_world.
    cb->dispatch();
    return 0;
}

* * The callback() method is overloaded and can also be used to create * callbacks to member functions, eg. *

#include "libxorp/xorp.h"
#include "libxorp/callback.hh"


class Foo {
public:
    void hello_world() {
	cout << "Foo::Hello World" << endl;
    }
};

int main() {
    typedef XorpCallback0::RefPtr SimpleCallback;

    Foo f;

    // Create a callback to a member function
    SimpleCallback cb = callback(&f, &Foo::hello_world);

    // Invoke f.hello_world
    cb->dispatch();

    return 0;
}

* * In addition, to being able to invoke member functions, callbacks can * also store arguments to functions. eg. *

#include "libxorp/xorp.h"
#include "libxorp/callback.hh"


static int sum(int x, int y) {
    cout << "sum(x = " << x << ", y = " << y << ")" << endl;
    return x + y;
}

int main() {
    // Callback to function returning "int"
    typedef XorpCallback0::RefPtr NoArgCallback;

    NoArgCallback cb1 = callback(sum, 1, 2);
    cout << "cb1->dispatch() returns " << cb1->dispatch() << endl; // "3"
    cout << endl;

    // Callback to function returning int and taking an integer argument
    typedef XorpCallback1::RefPtr OneIntArgCallback;

    OneIntArgCallback cb2 = callback(sum, 5);
    cout << "cb2->dispatch(10) returns " << cb2->dispatch(10) << endl; // 15
    cout << endl;

    cout << "cb2->dispatch(20) returns " << cb2->dispatch(20) << endl; // 25
    cout << endl;

    // Callback to function returning int and taking  2 integer arguments
    typedef XorpCallback2::RefPtr TwoIntArgCallback;

    TwoIntArgCallback cb3 = callback(sum);
    cout << "cb3->dispatch() returns " << cb3->dispatch(50, -50) << endl; // 0

    return 0;
}

* * Bound arguments, as with member functions, are implemented by the * overloading of the callback() method. At dispatch time, the bound * arguments are last arguments past to the wrappered function. If you * compile and run the program you will see: *
sum(x = 10, y = 5)
cb2->dispatch(10) returns 15
* * and: *
sum(x = 20, y = 5)
cb2->dispatch(20) returns 25
* * for the one bound argument cases. * * @sect Declaring Callback Types * * There are a host of XorpCallbackN types. The N denotes the number * of arguments that will be passed at dispatch time by the callback * invoker. The template parameters to XorpCallbackN types are the * return value followed by the types of arguments that will be passed * at dispatch time. Thus type: * *
XorpCallback1::RefPtr
 * 
* * corresponds to callback object returning a double when invoked and * requiring an integer argument to passed at dispatch time. * * When arguments are bound to a callback they are not specified * in the templatized argument list. So the above declaration is good * for a function taking an integer argument followed by upto the * maximum number of bound arguments. * * Note: In this header file, support is provided for upto %d bound * arguments and %d dispatch arguments. * * @sect Ref Pointer Helpers * * Callback objects may be set to NULL, since they use reference pointers * to store the objects. Callbacks may be unset using the ref_ptr::release() * method: *
    cb.release();
* and to tested using the ref_ptr::is_empty() method:
if (! cb.is_empty()) {
    cb->dispatch();
}
* * In many instances, the RefPtr associated with a callback on an object * will be stored by the object itself. For instance, a class may own a * timer object and the associated timer expiry callback which is * a member function of the containing class. Because the containing class * owns the callback object corresponding the timer callback, there is * never an opportunity for the callback to be dispatched on a deleted object * or with invalid data. */ #ifndef INCLUDED_FROM_CALLBACK_HH #error "This file should be included through libxorp/callback.hh" #endif #ifndef __XORP_CALLBACK_HH__ #define __XORP_CALLBACK_HH__ #include "minitraits.hh" #include "ref_ptr.hh" #include "safe_callback_obj.hh" /////////////////////////////////////////////////////////////////////////////// // // Code relating to callbacks with 0 late args // /** * @short Base class for callbacks with 0 dispatch time args. */ template struct XorpCallback0 { typedef ref_ptr RefPtr; virtual ~XorpCallback0() {} virtual R dispatch() = 0; }; /** * @short Callback object for functions with 0 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpFunctionCallback0B0 : public XorpCallback0 { typedef R (*F)(); XorpFunctionCallback0B0(F f) : XorpCallback0(), _f(f) {} R dispatch() { R r = (*_f)(); return r; } protected: F _f; }; /** * @short Callback object for void functions with 0 dispatch time * arguments and 0 bound (stored) arguments. */ template <> struct XorpFunctionCallback0B0 : public XorpCallback0 { typedef void (*F)(); XorpFunctionCallback0B0(F f) : XorpCallback0(), _f(f) {} void dispatch() { (*_f)(); } protected: F _f; }; /** * Factory function that creates a callback object targetted at a * function with 0 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback0::RefPtr callback(R (*f)()) { return typename XorpCallback0::RefPtr(new XorpFunctionCallback0B0(f)); } /** * @short Callback object for member methods with 0 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpMemberCallback0B0 : public XorpCallback0 { typedef R (O::*M)() ; XorpMemberCallback0B0(O* o, M m) : XorpCallback0(), _o(o), _m(m) {} R dispatch() { R r = ((*_o).*_m)(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for void member methods with 0 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpMemberCallback0B0 : public XorpCallback0 { typedef void (O::*M)() ; XorpMemberCallback0B0(O* o, M m) : XorpCallback0(), _o(o), _m(m) {} void dispatch() { ((*_o).*_m)(); } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for safe member methods with 0 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpSafeMemberCallback0B0 : public XorpMemberCallback0B0, public SafeCallbackBase { typedef typename XorpMemberCallback0B0::M M; XorpSafeMemberCallback0B0(O* o, M m) : XorpMemberCallback0B0(o, m), SafeCallbackBase(o) {} ~XorpSafeMemberCallback0B0() {} R dispatch() { if (valid()) { R r = XorpMemberCallback0B0::dispatch(); return r; } } }; /** * @short Callback object for void safe member methods with 0 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpSafeMemberCallback0B0 : public XorpMemberCallback0B0, public SafeCallbackBase { typedef typename XorpMemberCallback0B0::M M; XorpSafeMemberCallback0B0(O* o, M m) : XorpMemberCallback0B0(o, m), SafeCallbackBase(o) {} ~XorpSafeMemberCallback0B0() {} void dispatch() { if (valid()) { XorpMemberCallback0B0::dispatch(); } } }; template struct XorpMemberCallbackFactory0B0 { static XorpMemberCallback0B0* make(O* o, R (O::*p)()) { return new XorpSafeMemberCallback0B0(o, p); } }; template struct XorpMemberCallbackFactory0B0 { static XorpMemberCallback0B0* make(O* o, R (O::*p)()) { return new XorpMemberCallback0B0(o, p); }; }; /** * Factory function that creates a callback object targetted at a * member function with 0 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback0::RefPtr callback( O* o, R (O::*p)()) { return XorpMemberCallbackFactory0B0::True>::make(o, p); } /** * Factory function that creates a callback object targetted at a * member function with 0 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback0::RefPtr callback( O& o, R (O::*p)()) { return XorpMemberCallbackFactory0B0::True>::make(&o, p); } /** * @short Callback object for const member methods with 0 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstMemberCallback0B0 : public XorpCallback0 { typedef R (O::*M)() const; XorpConstMemberCallback0B0(O* o, M m) : XorpCallback0(), _o(o), _m(m) {} R dispatch() { R r = ((*_o).*_m)(); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for void const member methods with 0 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstMemberCallback0B0 : public XorpCallback0 { typedef void (O::*M)() const; XorpConstMemberCallback0B0(O* o, M m) : XorpCallback0(), _o(o), _m(m) {} void dispatch() { ((*_o).*_m)(); } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for const safe member methods with 0 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback0B0 : public XorpConstMemberCallback0B0, public SafeCallbackBase { typedef typename XorpConstMemberCallback0B0::M M; XorpConstSafeMemberCallback0B0(O* o, M m) : XorpConstMemberCallback0B0(o, m), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback0B0() {} R dispatch() { if (valid()) { R r = XorpConstMemberCallback0B0::dispatch(); return r; } } }; /** * @short Callback object for void const safe member methods with 0 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback0B0 : public XorpConstMemberCallback0B0, public SafeCallbackBase { typedef typename XorpConstMemberCallback0B0::M M; XorpConstSafeMemberCallback0B0(O* o, M m) : XorpConstMemberCallback0B0(o, m), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback0B0() {} void dispatch() { if (valid()) { XorpConstMemberCallback0B0::dispatch(); } } }; template struct XorpConstMemberCallbackFactory0B0 { static XorpConstMemberCallback0B0* make(O* o, R (O::*p)() const) { return new XorpConstSafeMemberCallback0B0(o, p); } }; template struct XorpConstMemberCallbackFactory0B0 { static XorpConstMemberCallback0B0* make(O* o, R (O::*p)() const) { return new XorpConstMemberCallback0B0(o, p); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 0 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback0::RefPtr callback( const O* o, R (O::*p)() const) { return XorpConstMemberCallbackFactory0B0::True>::make(o, p); } /** * Factory function that creates a callback object targetted at a * const member function with 0 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback0::RefPtr callback( const O& o, R (O::*p)() const) { return XorpConstMemberCallbackFactory0B0::True>::make(&o, p); } /** * @short Callback object for functions with 0 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpFunctionCallback0B1 : public XorpCallback0 { typedef R (*F)(BA1); XorpFunctionCallback0B1(F f, BA1 ba1) : XorpCallback0(), _f(f), _ba1(ba1) {} R dispatch() { R r = (*_f)(_ba1); return r; } protected: F _f; BA1 _ba1; }; /** * @short Callback object for void functions with 0 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpFunctionCallback0B1 : public XorpCallback0 { typedef void (*F)(BA1); XorpFunctionCallback0B1(F f, BA1 ba1) : XorpCallback0(), _f(f), _ba1(ba1) {} void dispatch() { (*_f)(_ba1); } protected: F _f; BA1 _ba1; }; /** * Factory function that creates a callback object targetted at a * function with 0 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback0::RefPtr callback(R (*f)(BA1), BA1 ba1) { return typename XorpCallback0::RefPtr(new XorpFunctionCallback0B1(f, ba1)); } /** * @short Callback object for member methods with 0 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpMemberCallback0B1 : public XorpCallback0 { typedef R (O::*M)(BA1) ; XorpMemberCallback0B1(O* o, M m, BA1 ba1) : XorpCallback0(), _o(o), _m(m), _ba1(ba1) {} R dispatch() { R r = ((*_o).*_m)(_ba1); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for void member methods with 0 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpMemberCallback0B1 : public XorpCallback0 { typedef void (O::*M)(BA1) ; XorpMemberCallback0B1(O* o, M m, BA1 ba1) : XorpCallback0(), _o(o), _m(m), _ba1(ba1) {} void dispatch() { ((*_o).*_m)(_ba1); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for safe member methods with 0 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpSafeMemberCallback0B1 : public XorpMemberCallback0B1, public SafeCallbackBase { typedef typename XorpMemberCallback0B1::M M; XorpSafeMemberCallback0B1(O* o, M m, BA1 ba1) : XorpMemberCallback0B1(o, m, ba1), SafeCallbackBase(o) {} ~XorpSafeMemberCallback0B1() {} R dispatch() { if (valid()) { R r = XorpMemberCallback0B1::dispatch(); return r; } } }; /** * @short Callback object for void safe member methods with 0 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpSafeMemberCallback0B1 : public XorpMemberCallback0B1, public SafeCallbackBase { typedef typename XorpMemberCallback0B1::M M; XorpSafeMemberCallback0B1(O* o, M m, BA1 ba1) : XorpMemberCallback0B1(o, m, ba1), SafeCallbackBase(o) {} ~XorpSafeMemberCallback0B1() {} void dispatch() { if (valid()) { XorpMemberCallback0B1::dispatch(); } } }; template struct XorpMemberCallbackFactory0B1 { static XorpMemberCallback0B1* make(O* o, R (O::*p)(BA1), BA1 ba1) { return new XorpSafeMemberCallback0B1(o, p, ba1); } }; template struct XorpMemberCallbackFactory0B1 { static XorpMemberCallback0B1* make(O* o, R (O::*p)(BA1), BA1 ba1) { return new XorpMemberCallback0B1(o, p, ba1); }; }; /** * Factory function that creates a callback object targetted at a * member function with 0 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback0::RefPtr callback( O* o, R (O::*p)(BA1), BA1 ba1) { return XorpMemberCallbackFactory0B1::True>::make(o, p, ba1); } /** * Factory function that creates a callback object targetted at a * member function with 0 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback0::RefPtr callback( O& o, R (O::*p)(BA1), BA1 ba1) { return XorpMemberCallbackFactory0B1::True>::make(&o, p, ba1); } /** * @short Callback object for const member methods with 0 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstMemberCallback0B1 : public XorpCallback0 { typedef R (O::*M)(BA1) const; XorpConstMemberCallback0B1(O* o, M m, BA1 ba1) : XorpCallback0(), _o(o), _m(m), _ba1(ba1) {} R dispatch() { R r = ((*_o).*_m)(_ba1); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for void const member methods with 0 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstMemberCallback0B1 : public XorpCallback0 { typedef void (O::*M)(BA1) const; XorpConstMemberCallback0B1(O* o, M m, BA1 ba1) : XorpCallback0(), _o(o), _m(m), _ba1(ba1) {} void dispatch() { ((*_o).*_m)(_ba1); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for const safe member methods with 0 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback0B1 : public XorpConstMemberCallback0B1, public SafeCallbackBase { typedef typename XorpConstMemberCallback0B1::M M; XorpConstSafeMemberCallback0B1(O* o, M m, BA1 ba1) : XorpConstMemberCallback0B1(o, m, ba1), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback0B1() {} R dispatch() { if (valid()) { R r = XorpConstMemberCallback0B1::dispatch(); return r; } } }; /** * @short Callback object for void const safe member methods with 0 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback0B1 : public XorpConstMemberCallback0B1, public SafeCallbackBase { typedef typename XorpConstMemberCallback0B1::M M; XorpConstSafeMemberCallback0B1(O* o, M m, BA1 ba1) : XorpConstMemberCallback0B1(o, m, ba1), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback0B1() {} void dispatch() { if (valid()) { XorpConstMemberCallback0B1::dispatch(); } } }; template struct XorpConstMemberCallbackFactory0B1 { static XorpConstMemberCallback0B1* make(O* o, R (O::*p)(BA1) const, BA1 ba1) { return new XorpConstSafeMemberCallback0B1(o, p, ba1); } }; template struct XorpConstMemberCallbackFactory0B1 { static XorpConstMemberCallback0B1* make(O* o, R (O::*p)(BA1) const, BA1 ba1) { return new XorpConstMemberCallback0B1(o, p, ba1); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 0 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback0::RefPtr callback( const O* o, R (O::*p)(BA1) const, BA1 ba1) { return XorpConstMemberCallbackFactory0B1::True>::make(o, p, ba1); } /** * Factory function that creates a callback object targetted at a * const member function with 0 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback0::RefPtr callback( const O& o, R (O::*p)(BA1) const, BA1 ba1) { return XorpConstMemberCallbackFactory0B1::True>::make(&o, p, ba1); } /** * @short Callback object for functions with 0 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpFunctionCallback0B2 : public XorpCallback0 { typedef R (*F)(BA1, BA2); XorpFunctionCallback0B2(F f, BA1 ba1, BA2 ba2) : XorpCallback0(), _f(f), _ba1(ba1), _ba2(ba2) {} R dispatch() { R r = (*_f)(_ba1, _ba2); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; }; /** * @short Callback object for void functions with 0 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpFunctionCallback0B2 : public XorpCallback0 { typedef void (*F)(BA1, BA2); XorpFunctionCallback0B2(F f, BA1 ba1, BA2 ba2) : XorpCallback0(), _f(f), _ba1(ba1), _ba2(ba2) {} void dispatch() { (*_f)(_ba1, _ba2); } protected: F _f; BA1 _ba1; BA2 _ba2; }; /** * Factory function that creates a callback object targetted at a * function with 0 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback0::RefPtr callback(R (*f)(BA1, BA2), BA1 ba1, BA2 ba2) { return typename XorpCallback0::RefPtr(new XorpFunctionCallback0B2(f, ba1, ba2)); } /** * @short Callback object for member methods with 0 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpMemberCallback0B2 : public XorpCallback0 { typedef R (O::*M)(BA1, BA2) ; XorpMemberCallback0B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback0(), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} R dispatch() { R r = ((*_o).*_m)(_ba1, _ba2); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for void member methods with 0 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpMemberCallback0B2 : public XorpCallback0 { typedef void (O::*M)(BA1, BA2) ; XorpMemberCallback0B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback0(), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} void dispatch() { ((*_o).*_m)(_ba1, _ba2); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for safe member methods with 0 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpSafeMemberCallback0B2 : public XorpMemberCallback0B2, public SafeCallbackBase { typedef typename XorpMemberCallback0B2::M M; XorpSafeMemberCallback0B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpMemberCallback0B2(o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpSafeMemberCallback0B2() {} R dispatch() { if (valid()) { R r = XorpMemberCallback0B2::dispatch(); return r; } } }; /** * @short Callback object for void safe member methods with 0 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpSafeMemberCallback0B2 : public XorpMemberCallback0B2, public SafeCallbackBase { typedef typename XorpMemberCallback0B2::M M; XorpSafeMemberCallback0B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpMemberCallback0B2(o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpSafeMemberCallback0B2() {} void dispatch() { if (valid()) { XorpMemberCallback0B2::dispatch(); } } }; template struct XorpMemberCallbackFactory0B2 { static XorpMemberCallback0B2* make(O* o, R (O::*p)(BA1, BA2), BA1 ba1, BA2 ba2) { return new XorpSafeMemberCallback0B2(o, p, ba1, ba2); } }; template struct XorpMemberCallbackFactory0B2 { static XorpMemberCallback0B2* make(O* o, R (O::*p)(BA1, BA2), BA1 ba1, BA2 ba2) { return new XorpMemberCallback0B2(o, p, ba1, ba2); }; }; /** * Factory function that creates a callback object targetted at a * member function with 0 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback0::RefPtr callback( O* o, R (O::*p)(BA1, BA2), BA1 ba1, BA2 ba2) { return XorpMemberCallbackFactory0B2::True>::make(o, p, ba1, ba2); } /** * Factory function that creates a callback object targetted at a * member function with 0 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback0::RefPtr callback( O& o, R (O::*p)(BA1, BA2), BA1 ba1, BA2 ba2) { return XorpMemberCallbackFactory0B2::True>::make(&o, p, ba1, ba2); } /** * @short Callback object for const member methods with 0 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstMemberCallback0B2 : public XorpCallback0 { typedef R (O::*M)(BA1, BA2) const; XorpConstMemberCallback0B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback0(), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} R dispatch() { R r = ((*_o).*_m)(_ba1, _ba2); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for void const member methods with 0 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstMemberCallback0B2 : public XorpCallback0 { typedef void (O::*M)(BA1, BA2) const; XorpConstMemberCallback0B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback0(), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} void dispatch() { ((*_o).*_m)(_ba1, _ba2); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for const safe member methods with 0 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback0B2 : public XorpConstMemberCallback0B2, public SafeCallbackBase { typedef typename XorpConstMemberCallback0B2::M M; XorpConstSafeMemberCallback0B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpConstMemberCallback0B2(o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback0B2() {} R dispatch() { if (valid()) { R r = XorpConstMemberCallback0B2::dispatch(); return r; } } }; /** * @short Callback object for void const safe member methods with 0 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback0B2 : public XorpConstMemberCallback0B2, public SafeCallbackBase { typedef typename XorpConstMemberCallback0B2::M M; XorpConstSafeMemberCallback0B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpConstMemberCallback0B2(o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback0B2() {} void dispatch() { if (valid()) { XorpConstMemberCallback0B2::dispatch(); } } }; template struct XorpConstMemberCallbackFactory0B2 { static XorpConstMemberCallback0B2* make(O* o, R (O::*p)(BA1, BA2) const, BA1 ba1, BA2 ba2) { return new XorpConstSafeMemberCallback0B2(o, p, ba1, ba2); } }; template struct XorpConstMemberCallbackFactory0B2 { static XorpConstMemberCallback0B2* make(O* o, R (O::*p)(BA1, BA2) const, BA1 ba1, BA2 ba2) { return new XorpConstMemberCallback0B2(o, p, ba1, ba2); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 0 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback0::RefPtr callback( const O* o, R (O::*p)(BA1, BA2) const, BA1 ba1, BA2 ba2) { return XorpConstMemberCallbackFactory0B2::True>::make(o, p, ba1, ba2); } /** * Factory function that creates a callback object targetted at a * const member function with 0 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback0::RefPtr callback( const O& o, R (O::*p)(BA1, BA2) const, BA1 ba1, BA2 ba2) { return XorpConstMemberCallbackFactory0B2::True>::make(&o, p, ba1, ba2); } /** * @short Callback object for functions with 0 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpFunctionCallback0B3 : public XorpCallback0 { typedef R (*F)(BA1, BA2, BA3); XorpFunctionCallback0B3(F f, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback0(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch() { R r = (*_f)(_ba1, _ba2, _ba3); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; }; /** * @short Callback object for void functions with 0 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpFunctionCallback0B3 : public XorpCallback0 { typedef void (*F)(BA1, BA2, BA3); XorpFunctionCallback0B3(F f, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback0(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch() { (*_f)(_ba1, _ba2, _ba3); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; }; /** * Factory function that creates a callback object targetted at a * function with 0 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback0::RefPtr callback(R (*f)(BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return typename XorpCallback0::RefPtr(new XorpFunctionCallback0B3(f, ba1, ba2, ba3)); } /** * @short Callback object for member methods with 0 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpMemberCallback0B3 : public XorpCallback0 { typedef R (O::*M)(BA1, BA2, BA3) ; XorpMemberCallback0B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback0(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch() { R r = ((*_o).*_m)(_ba1, _ba2, _ba3); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for void member methods with 0 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpMemberCallback0B3 : public XorpCallback0 { typedef void (O::*M)(BA1, BA2, BA3) ; XorpMemberCallback0B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback0(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch() { ((*_o).*_m)(_ba1, _ba2, _ba3); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for safe member methods with 0 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpSafeMemberCallback0B3 : public XorpMemberCallback0B3, public SafeCallbackBase { typedef typename XorpMemberCallback0B3::M M; XorpSafeMemberCallback0B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpMemberCallback0B3(o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpSafeMemberCallback0B3() {} R dispatch() { if (valid()) { R r = XorpMemberCallback0B3::dispatch(); return r; } } }; /** * @short Callback object for void safe member methods with 0 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpSafeMemberCallback0B3 : public XorpMemberCallback0B3, public SafeCallbackBase { typedef typename XorpMemberCallback0B3::M M; XorpSafeMemberCallback0B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpMemberCallback0B3(o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpSafeMemberCallback0B3() {} void dispatch() { if (valid()) { XorpMemberCallback0B3::dispatch(); } } }; template struct XorpMemberCallbackFactory0B3 { static XorpMemberCallback0B3* make(O* o, R (O::*p)(BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpSafeMemberCallback0B3(o, p, ba1, ba2, ba3); } }; template struct XorpMemberCallbackFactory0B3 { static XorpMemberCallback0B3* make(O* o, R (O::*p)(BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpMemberCallback0B3(o, p, ba1, ba2, ba3); }; }; /** * Factory function that creates a callback object targetted at a * member function with 0 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback0::RefPtr callback( O* o, R (O::*p)(BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return XorpMemberCallbackFactory0B3::True>::make(o, p, ba1, ba2, ba3); } /** * Factory function that creates a callback object targetted at a * member function with 0 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback0::RefPtr callback( O& o, R (O::*p)(BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return XorpMemberCallbackFactory0B3::True>::make(&o, p, ba1, ba2, ba3); } /** * @short Callback object for const member methods with 0 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstMemberCallback0B3 : public XorpCallback0 { typedef R (O::*M)(BA1, BA2, BA3) const; XorpConstMemberCallback0B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback0(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch() { R r = ((*_o).*_m)(_ba1, _ba2, _ba3); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for void const member methods with 0 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstMemberCallback0B3 : public XorpCallback0 { typedef void (O::*M)(BA1, BA2, BA3) const; XorpConstMemberCallback0B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback0(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch() { ((*_o).*_m)(_ba1, _ba2, _ba3); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for const safe member methods with 0 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback0B3 : public XorpConstMemberCallback0B3, public SafeCallbackBase { typedef typename XorpConstMemberCallback0B3::M M; XorpConstSafeMemberCallback0B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpConstMemberCallback0B3(o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback0B3() {} R dispatch() { if (valid()) { R r = XorpConstMemberCallback0B3::dispatch(); return r; } } }; /** * @short Callback object for void const safe member methods with 0 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback0B3 : public XorpConstMemberCallback0B3, public SafeCallbackBase { typedef typename XorpConstMemberCallback0B3::M M; XorpConstSafeMemberCallback0B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpConstMemberCallback0B3(o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback0B3() {} void dispatch() { if (valid()) { XorpConstMemberCallback0B3::dispatch(); } } }; template struct XorpConstMemberCallbackFactory0B3 { static XorpConstMemberCallback0B3* make(O* o, R (O::*p)(BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpConstSafeMemberCallback0B3(o, p, ba1, ba2, ba3); } }; template struct XorpConstMemberCallbackFactory0B3 { static XorpConstMemberCallback0B3* make(O* o, R (O::*p)(BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpConstMemberCallback0B3(o, p, ba1, ba2, ba3); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 0 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback0::RefPtr callback( const O* o, R (O::*p)(BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return XorpConstMemberCallbackFactory0B3::True>::make(o, p, ba1, ba2, ba3); } /** * Factory function that creates a callback object targetted at a * const member function with 0 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback0::RefPtr callback( const O& o, R (O::*p)(BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return XorpConstMemberCallbackFactory0B3::True>::make(&o, p, ba1, ba2, ba3); } /** * @short Callback object for functions with 0 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpFunctionCallback0B4 : public XorpCallback0 { typedef R (*F)(BA1, BA2, BA3, BA4); XorpFunctionCallback0B4(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback0(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch() { R r = (*_f)(_ba1, _ba2, _ba3, _ba4); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; }; /** * @short Callback object for void functions with 0 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpFunctionCallback0B4 : public XorpCallback0 { typedef void (*F)(BA1, BA2, BA3, BA4); XorpFunctionCallback0B4(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback0(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch() { (*_f)(_ba1, _ba2, _ba3, _ba4); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; }; /** * Factory function that creates a callback object targetted at a * function with 0 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback0::RefPtr callback(R (*f)(BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return typename XorpCallback0::RefPtr(new XorpFunctionCallback0B4(f, ba1, ba2, ba3, ba4)); } /** * @short Callback object for member methods with 0 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpMemberCallback0B4 : public XorpCallback0 { typedef R (O::*M)(BA1, BA2, BA3, BA4) ; XorpMemberCallback0B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback0(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch() { R r = ((*_o).*_m)(_ba1, _ba2, _ba3, _ba4); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for void member methods with 0 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpMemberCallback0B4 : public XorpCallback0 { typedef void (O::*M)(BA1, BA2, BA3, BA4) ; XorpMemberCallback0B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback0(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch() { ((*_o).*_m)(_ba1, _ba2, _ba3, _ba4); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for safe member methods with 0 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpSafeMemberCallback0B4 : public XorpMemberCallback0B4, public SafeCallbackBase { typedef typename XorpMemberCallback0B4::M M; XorpSafeMemberCallback0B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpMemberCallback0B4(o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpSafeMemberCallback0B4() {} R dispatch() { if (valid()) { R r = XorpMemberCallback0B4::dispatch(); return r; } } }; /** * @short Callback object for void safe member methods with 0 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpSafeMemberCallback0B4 : public XorpMemberCallback0B4, public SafeCallbackBase { typedef typename XorpMemberCallback0B4::M M; XorpSafeMemberCallback0B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpMemberCallback0B4(o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpSafeMemberCallback0B4() {} void dispatch() { if (valid()) { XorpMemberCallback0B4::dispatch(); } } }; template struct XorpMemberCallbackFactory0B4 { static XorpMemberCallback0B4* make(O* o, R (O::*p)(BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpSafeMemberCallback0B4(o, p, ba1, ba2, ba3, ba4); } }; template struct XorpMemberCallbackFactory0B4 { static XorpMemberCallback0B4* make(O* o, R (O::*p)(BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpMemberCallback0B4(o, p, ba1, ba2, ba3, ba4); }; }; /** * Factory function that creates a callback object targetted at a * member function with 0 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback0::RefPtr callback( O* o, R (O::*p)(BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpMemberCallbackFactory0B4::True>::make(o, p, ba1, ba2, ba3, ba4); } /** * Factory function that creates a callback object targetted at a * member function with 0 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback0::RefPtr callback( O& o, R (O::*p)(BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpMemberCallbackFactory0B4::True>::make(&o, p, ba1, ba2, ba3, ba4); } /** * @short Callback object for const member methods with 0 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstMemberCallback0B4 : public XorpCallback0 { typedef R (O::*M)(BA1, BA2, BA3, BA4) const; XorpConstMemberCallback0B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback0(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch() { R r = ((*_o).*_m)(_ba1, _ba2, _ba3, _ba4); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for void const member methods with 0 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstMemberCallback0B4 : public XorpCallback0 { typedef void (O::*M)(BA1, BA2, BA3, BA4) const; XorpConstMemberCallback0B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback0(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch() { ((*_o).*_m)(_ba1, _ba2, _ba3, _ba4); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for const safe member methods with 0 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback0B4 : public XorpConstMemberCallback0B4, public SafeCallbackBase { typedef typename XorpConstMemberCallback0B4::M M; XorpConstSafeMemberCallback0B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpConstMemberCallback0B4(o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback0B4() {} R dispatch() { if (valid()) { R r = XorpConstMemberCallback0B4::dispatch(); return r; } } }; /** * @short Callback object for void const safe member methods with 0 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback0B4 : public XorpConstMemberCallback0B4, public SafeCallbackBase { typedef typename XorpConstMemberCallback0B4::M M; XorpConstSafeMemberCallback0B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpConstMemberCallback0B4(o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback0B4() {} void dispatch() { if (valid()) { XorpConstMemberCallback0B4::dispatch(); } } }; template struct XorpConstMemberCallbackFactory0B4 { static XorpConstMemberCallback0B4* make(O* o, R (O::*p)(BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpConstSafeMemberCallback0B4(o, p, ba1, ba2, ba3, ba4); } }; template struct XorpConstMemberCallbackFactory0B4 { static XorpConstMemberCallback0B4* make(O* o, R (O::*p)(BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpConstMemberCallback0B4(o, p, ba1, ba2, ba3, ba4); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 0 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback0::RefPtr callback( const O* o, R (O::*p)(BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpConstMemberCallbackFactory0B4::True>::make(o, p, ba1, ba2, ba3, ba4); } /** * Factory function that creates a callback object targetted at a * const member function with 0 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback0::RefPtr callback( const O& o, R (O::*p)(BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpConstMemberCallbackFactory0B4::True>::make(&o, p, ba1, ba2, ba3, ba4); } /** * @short Callback object for functions with 0 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpFunctionCallback0B5 : public XorpCallback0 { typedef R (*F)(BA1, BA2, BA3, BA4, BA5); XorpFunctionCallback0B5(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback0(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch() { R r = (*_f)(_ba1, _ba2, _ba3, _ba4, _ba5); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; }; /** * @short Callback object for void functions with 0 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpFunctionCallback0B5 : public XorpCallback0 { typedef void (*F)(BA1, BA2, BA3, BA4, BA5); XorpFunctionCallback0B5(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback0(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch() { (*_f)(_ba1, _ba2, _ba3, _ba4, _ba5); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; }; /** * Factory function that creates a callback object targetted at a * function with 0 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback0::RefPtr callback(R (*f)(BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return typename XorpCallback0::RefPtr(new XorpFunctionCallback0B5(f, ba1, ba2, ba3, ba4, ba5)); } /** * @short Callback object for member methods with 0 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpMemberCallback0B5 : public XorpCallback0 { typedef R (O::*M)(BA1, BA2, BA3, BA4, BA5) ; XorpMemberCallback0B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback0(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch() { R r = ((*_o).*_m)(_ba1, _ba2, _ba3, _ba4, _ba5); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for void member methods with 0 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpMemberCallback0B5 : public XorpCallback0 { typedef void (O::*M)(BA1, BA2, BA3, BA4, BA5) ; XorpMemberCallback0B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback0(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch() { ((*_o).*_m)(_ba1, _ba2, _ba3, _ba4, _ba5); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for safe member methods with 0 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpSafeMemberCallback0B5 : public XorpMemberCallback0B5, public SafeCallbackBase { typedef typename XorpMemberCallback0B5::M M; XorpSafeMemberCallback0B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpMemberCallback0B5(o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpSafeMemberCallback0B5() {} R dispatch() { if (valid()) { R r = XorpMemberCallback0B5::dispatch(); return r; } } }; /** * @short Callback object for void safe member methods with 0 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpSafeMemberCallback0B5 : public XorpMemberCallback0B5, public SafeCallbackBase { typedef typename XorpMemberCallback0B5::M M; XorpSafeMemberCallback0B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpMemberCallback0B5(o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpSafeMemberCallback0B5() {} void dispatch() { if (valid()) { XorpMemberCallback0B5::dispatch(); } } }; template struct XorpMemberCallbackFactory0B5 { static XorpMemberCallback0B5* make(O* o, R (O::*p)(BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpSafeMemberCallback0B5(o, p, ba1, ba2, ba3, ba4, ba5); } }; template struct XorpMemberCallbackFactory0B5 { static XorpMemberCallback0B5* make(O* o, R (O::*p)(BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpMemberCallback0B5(o, p, ba1, ba2, ba3, ba4, ba5); }; }; /** * Factory function that creates a callback object targetted at a * member function with 0 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback0::RefPtr callback( O* o, R (O::*p)(BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpMemberCallbackFactory0B5::True>::make(o, p, ba1, ba2, ba3, ba4, ba5); } /** * Factory function that creates a callback object targetted at a * member function with 0 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback0::RefPtr callback( O& o, R (O::*p)(BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpMemberCallbackFactory0B5::True>::make(&o, p, ba1, ba2, ba3, ba4, ba5); } /** * @short Callback object for const member methods with 0 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstMemberCallback0B5 : public XorpCallback0 { typedef R (O::*M)(BA1, BA2, BA3, BA4, BA5) const; XorpConstMemberCallback0B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback0(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch() { R r = ((*_o).*_m)(_ba1, _ba2, _ba3, _ba4, _ba5); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for void const member methods with 0 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstMemberCallback0B5 : public XorpCallback0 { typedef void (O::*M)(BA1, BA2, BA3, BA4, BA5) const; XorpConstMemberCallback0B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback0(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch() { ((*_o).*_m)(_ba1, _ba2, _ba3, _ba4, _ba5); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for const safe member methods with 0 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback0B5 : public XorpConstMemberCallback0B5, public SafeCallbackBase { typedef typename XorpConstMemberCallback0B5::M M; XorpConstSafeMemberCallback0B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpConstMemberCallback0B5(o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback0B5() {} R dispatch() { if (valid()) { R r = XorpConstMemberCallback0B5::dispatch(); return r; } } }; /** * @short Callback object for void const safe member methods with 0 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback0B5 : public XorpConstMemberCallback0B5, public SafeCallbackBase { typedef typename XorpConstMemberCallback0B5::M M; XorpConstSafeMemberCallback0B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpConstMemberCallback0B5(o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback0B5() {} void dispatch() { if (valid()) { XorpConstMemberCallback0B5::dispatch(); } } }; template struct XorpConstMemberCallbackFactory0B5 { static XorpConstMemberCallback0B5* make(O* o, R (O::*p)(BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpConstSafeMemberCallback0B5(o, p, ba1, ba2, ba3, ba4, ba5); } }; template struct XorpConstMemberCallbackFactory0B5 { static XorpConstMemberCallback0B5* make(O* o, R (O::*p)(BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpConstMemberCallback0B5(o, p, ba1, ba2, ba3, ba4, ba5); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 0 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback0::RefPtr callback( const O* o, R (O::*p)(BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpConstMemberCallbackFactory0B5::True>::make(o, p, ba1, ba2, ba3, ba4, ba5); } /** * Factory function that creates a callback object targetted at a * const member function with 0 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback0::RefPtr callback( const O& o, R (O::*p)(BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpConstMemberCallbackFactory0B5::True>::make(&o, p, ba1, ba2, ba3, ba4, ba5); } /** * @short Callback object for functions with 0 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpFunctionCallback0B6 : public XorpCallback0 { typedef R (*F)(BA1, BA2, BA3, BA4, BA5, BA6); XorpFunctionCallback0B6(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback0(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch() { R r = (*_f)(_ba1, _ba2, _ba3, _ba4, _ba5, _ba6); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; BA6 _ba6; }; /** * @short Callback object for void functions with 0 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpFunctionCallback0B6 : public XorpCallback0 { typedef void (*F)(BA1, BA2, BA3, BA4, BA5, BA6); XorpFunctionCallback0B6(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback0(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch() { (*_f)(_ba1, _ba2, _ba3, _ba4, _ba5, _ba6); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; BA6 _ba6; }; /** * Factory function that creates a callback object targetted at a * function with 0 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback0::RefPtr callback(R (*f)(BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return typename XorpCallback0::RefPtr(new XorpFunctionCallback0B6(f, ba1, ba2, ba3, ba4, ba5, ba6)); } /** * @short Callback object for member methods with 0 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpMemberCallback0B6 : public XorpCallback0 { typedef R (O::*M)(BA1, BA2, BA3, BA4, BA5, BA6) ; XorpMemberCallback0B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback0(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch() { R r = ((*_o).*_m)(_ba1, _ba2, _ba3, _ba4, _ba5, _ba6); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for void member methods with 0 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpMemberCallback0B6 : public XorpCallback0 { typedef void (O::*M)(BA1, BA2, BA3, BA4, BA5, BA6) ; XorpMemberCallback0B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback0(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch() { ((*_o).*_m)(_ba1, _ba2, _ba3, _ba4, _ba5, _ba6); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for safe member methods with 0 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpSafeMemberCallback0B6 : public XorpMemberCallback0B6, public SafeCallbackBase { typedef typename XorpMemberCallback0B6::M M; XorpSafeMemberCallback0B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpMemberCallback0B6(o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpSafeMemberCallback0B6() {} R dispatch() { if (valid()) { R r = XorpMemberCallback0B6::dispatch(); return r; } } }; /** * @short Callback object for void safe member methods with 0 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpSafeMemberCallback0B6 : public XorpMemberCallback0B6, public SafeCallbackBase { typedef typename XorpMemberCallback0B6::M M; XorpSafeMemberCallback0B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpMemberCallback0B6(o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpSafeMemberCallback0B6() {} void dispatch() { if (valid()) { XorpMemberCallback0B6::dispatch(); } } }; template struct XorpMemberCallbackFactory0B6 { static XorpMemberCallback0B6* make(O* o, R (O::*p)(BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpSafeMemberCallback0B6(o, p, ba1, ba2, ba3, ba4, ba5, ba6); } }; template struct XorpMemberCallbackFactory0B6 { static XorpMemberCallback0B6* make(O* o, R (O::*p)(BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpMemberCallback0B6(o, p, ba1, ba2, ba3, ba4, ba5, ba6); }; }; /** * Factory function that creates a callback object targetted at a * member function with 0 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback0::RefPtr callback( O* o, R (O::*p)(BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpMemberCallbackFactory0B6::True>::make(o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * Factory function that creates a callback object targetted at a * member function with 0 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback0::RefPtr callback( O& o, R (O::*p)(BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpMemberCallbackFactory0B6::True>::make(&o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * @short Callback object for const member methods with 0 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstMemberCallback0B6 : public XorpCallback0 { typedef R (O::*M)(BA1, BA2, BA3, BA4, BA5, BA6) const; XorpConstMemberCallback0B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback0(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch() { R r = ((*_o).*_m)(_ba1, _ba2, _ba3, _ba4, _ba5, _ba6); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for void const member methods with 0 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstMemberCallback0B6 : public XorpCallback0 { typedef void (O::*M)(BA1, BA2, BA3, BA4, BA5, BA6) const; XorpConstMemberCallback0B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback0(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch() { ((*_o).*_m)(_ba1, _ba2, _ba3, _ba4, _ba5, _ba6); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for const safe member methods with 0 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback0B6 : public XorpConstMemberCallback0B6, public SafeCallbackBase { typedef typename XorpConstMemberCallback0B6::M M; XorpConstSafeMemberCallback0B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpConstMemberCallback0B6(o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback0B6() {} R dispatch() { if (valid()) { R r = XorpConstMemberCallback0B6::dispatch(); return r; } } }; /** * @short Callback object for void const safe member methods with 0 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback0B6 : public XorpConstMemberCallback0B6, public SafeCallbackBase { typedef typename XorpConstMemberCallback0B6::M M; XorpConstSafeMemberCallback0B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpConstMemberCallback0B6(o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback0B6() {} void dispatch() { if (valid()) { XorpConstMemberCallback0B6::dispatch(); } } }; template struct XorpConstMemberCallbackFactory0B6 { static XorpConstMemberCallback0B6* make(O* o, R (O::*p)(BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpConstSafeMemberCallback0B6(o, p, ba1, ba2, ba3, ba4, ba5, ba6); } }; template struct XorpConstMemberCallbackFactory0B6 { static XorpConstMemberCallback0B6* make(O* o, R (O::*p)(BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpConstMemberCallback0B6(o, p, ba1, ba2, ba3, ba4, ba5, ba6); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 0 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback0::RefPtr callback( const O* o, R (O::*p)(BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpConstMemberCallbackFactory0B6::True>::make(o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * Factory function that creates a callback object targetted at a * const member function with 0 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback0::RefPtr callback( const O& o, R (O::*p)(BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpConstMemberCallbackFactory0B6::True>::make(&o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /////////////////////////////////////////////////////////////////////////////// // // Code relating to callbacks with 1 late args // /** * @short Base class for callbacks with 1 dispatch time args. */ template struct XorpCallback1 { typedef ref_ptr RefPtr; virtual ~XorpCallback1() {} virtual R dispatch(A1) = 0; }; /** * @short Callback object for functions with 1 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpFunctionCallback1B0 : public XorpCallback1 { typedef R (*F)(A1); XorpFunctionCallback1B0(F f) : XorpCallback1(), _f(f) {} R dispatch(A1 a1) { R r = (*_f)(a1); return r; } protected: F _f; }; /** * @short Callback object for void functions with 1 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpFunctionCallback1B0 : public XorpCallback1 { typedef void (*F)(A1); XorpFunctionCallback1B0(F f) : XorpCallback1(), _f(f) {} void dispatch(A1 a1) { (*_f)(a1); } protected: F _f; }; /** * Factory function that creates a callback object targetted at a * function with 1 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback1::RefPtr callback(R (*f)(A1)) { return typename XorpCallback1::RefPtr(new XorpFunctionCallback1B0(f)); } /** * @short Callback object for member methods with 1 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpMemberCallback1B0 : public XorpCallback1 { typedef R (O::*M)(A1) ; XorpMemberCallback1B0(O* o, M m) : XorpCallback1(), _o(o), _m(m) {} R dispatch(A1 a1) { R r = ((*_o).*_m)(a1); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for void member methods with 1 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpMemberCallback1B0 : public XorpCallback1 { typedef void (O::*M)(A1) ; XorpMemberCallback1B0(O* o, M m) : XorpCallback1(), _o(o), _m(m) {} void dispatch(A1 a1) { ((*_o).*_m)(a1); } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for safe member methods with 1 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpSafeMemberCallback1B0 : public XorpMemberCallback1B0, public SafeCallbackBase { typedef typename XorpMemberCallback1B0::M M; XorpSafeMemberCallback1B0(O* o, M m) : XorpMemberCallback1B0(o, m), SafeCallbackBase(o) {} ~XorpSafeMemberCallback1B0() {} R dispatch(A1 a1) { if (valid()) { R r = XorpMemberCallback1B0::dispatch(a1); return r; } } }; /** * @short Callback object for void safe member methods with 1 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpSafeMemberCallback1B0 : public XorpMemberCallback1B0, public SafeCallbackBase { typedef typename XorpMemberCallback1B0::M M; XorpSafeMemberCallback1B0(O* o, M m) : XorpMemberCallback1B0(o, m), SafeCallbackBase(o) {} ~XorpSafeMemberCallback1B0() {} void dispatch(A1 a1) { if (valid()) { XorpMemberCallback1B0::dispatch(a1); } } }; template struct XorpMemberCallbackFactory1B0 { static XorpMemberCallback1B0* make(O* o, R (O::*p)(A1)) { return new XorpSafeMemberCallback1B0(o, p); } }; template struct XorpMemberCallbackFactory1B0 { static XorpMemberCallback1B0* make(O* o, R (O::*p)(A1)) { return new XorpMemberCallback1B0(o, p); }; }; /** * Factory function that creates a callback object targetted at a * member function with 1 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback1::RefPtr callback( O* o, R (O::*p)(A1)) { return XorpMemberCallbackFactory1B0::True>::make(o, p); } /** * Factory function that creates a callback object targetted at a * member function with 1 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback1::RefPtr callback( O& o, R (O::*p)(A1)) { return XorpMemberCallbackFactory1B0::True>::make(&o, p); } /** * @short Callback object for const member methods with 1 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstMemberCallback1B0 : public XorpCallback1 { typedef R (O::*M)(A1) const; XorpConstMemberCallback1B0(O* o, M m) : XorpCallback1(), _o(o), _m(m) {} R dispatch(A1 a1) { R r = ((*_o).*_m)(a1); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for void const member methods with 1 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstMemberCallback1B0 : public XorpCallback1 { typedef void (O::*M)(A1) const; XorpConstMemberCallback1B0(O* o, M m) : XorpCallback1(), _o(o), _m(m) {} void dispatch(A1 a1) { ((*_o).*_m)(a1); } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for const safe member methods with 1 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback1B0 : public XorpConstMemberCallback1B0, public SafeCallbackBase { typedef typename XorpConstMemberCallback1B0::M M; XorpConstSafeMemberCallback1B0(O* o, M m) : XorpConstMemberCallback1B0(o, m), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback1B0() {} R dispatch(A1 a1) { if (valid()) { R r = XorpConstMemberCallback1B0::dispatch(a1); return r; } } }; /** * @short Callback object for void const safe member methods with 1 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback1B0 : public XorpConstMemberCallback1B0, public SafeCallbackBase { typedef typename XorpConstMemberCallback1B0::M M; XorpConstSafeMemberCallback1B0(O* o, M m) : XorpConstMemberCallback1B0(o, m), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback1B0() {} void dispatch(A1 a1) { if (valid()) { XorpConstMemberCallback1B0::dispatch(a1); } } }; template struct XorpConstMemberCallbackFactory1B0 { static XorpConstMemberCallback1B0* make(O* o, R (O::*p)(A1) const) { return new XorpConstSafeMemberCallback1B0(o, p); } }; template struct XorpConstMemberCallbackFactory1B0 { static XorpConstMemberCallback1B0* make(O* o, R (O::*p)(A1) const) { return new XorpConstMemberCallback1B0(o, p); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 1 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback1::RefPtr callback( const O* o, R (O::*p)(A1) const) { return XorpConstMemberCallbackFactory1B0::True>::make(o, p); } /** * Factory function that creates a callback object targetted at a * const member function with 1 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback1::RefPtr callback( const O& o, R (O::*p)(A1) const) { return XorpConstMemberCallbackFactory1B0::True>::make(&o, p); } /** * @short Callback object for functions with 1 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpFunctionCallback1B1 : public XorpCallback1 { typedef R (*F)(A1, BA1); XorpFunctionCallback1B1(F f, BA1 ba1) : XorpCallback1(), _f(f), _ba1(ba1) {} R dispatch(A1 a1) { R r = (*_f)(a1, _ba1); return r; } protected: F _f; BA1 _ba1; }; /** * @short Callback object for void functions with 1 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpFunctionCallback1B1 : public XorpCallback1 { typedef void (*F)(A1, BA1); XorpFunctionCallback1B1(F f, BA1 ba1) : XorpCallback1(), _f(f), _ba1(ba1) {} void dispatch(A1 a1) { (*_f)(a1, _ba1); } protected: F _f; BA1 _ba1; }; /** * Factory function that creates a callback object targetted at a * function with 1 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback1::RefPtr callback(R (*f)(A1, BA1), BA1 ba1) { return typename XorpCallback1::RefPtr(new XorpFunctionCallback1B1(f, ba1)); } /** * @short Callback object for member methods with 1 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpMemberCallback1B1 : public XorpCallback1 { typedef R (O::*M)(A1, BA1) ; XorpMemberCallback1B1(O* o, M m, BA1 ba1) : XorpCallback1(), _o(o), _m(m), _ba1(ba1) {} R dispatch(A1 a1) { R r = ((*_o).*_m)(a1, _ba1); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for void member methods with 1 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpMemberCallback1B1 : public XorpCallback1 { typedef void (O::*M)(A1, BA1) ; XorpMemberCallback1B1(O* o, M m, BA1 ba1) : XorpCallback1(), _o(o), _m(m), _ba1(ba1) {} void dispatch(A1 a1) { ((*_o).*_m)(a1, _ba1); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for safe member methods with 1 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpSafeMemberCallback1B1 : public XorpMemberCallback1B1, public SafeCallbackBase { typedef typename XorpMemberCallback1B1::M M; XorpSafeMemberCallback1B1(O* o, M m, BA1 ba1) : XorpMemberCallback1B1(o, m, ba1), SafeCallbackBase(o) {} ~XorpSafeMemberCallback1B1() {} R dispatch(A1 a1) { if (valid()) { R r = XorpMemberCallback1B1::dispatch(a1); return r; } } }; /** * @short Callback object for void safe member methods with 1 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpSafeMemberCallback1B1 : public XorpMemberCallback1B1, public SafeCallbackBase { typedef typename XorpMemberCallback1B1::M M; XorpSafeMemberCallback1B1(O* o, M m, BA1 ba1) : XorpMemberCallback1B1(o, m, ba1), SafeCallbackBase(o) {} ~XorpSafeMemberCallback1B1() {} void dispatch(A1 a1) { if (valid()) { XorpMemberCallback1B1::dispatch(a1); } } }; template struct XorpMemberCallbackFactory1B1 { static XorpMemberCallback1B1* make(O* o, R (O::*p)(A1, BA1), BA1 ba1) { return new XorpSafeMemberCallback1B1(o, p, ba1); } }; template struct XorpMemberCallbackFactory1B1 { static XorpMemberCallback1B1* make(O* o, R (O::*p)(A1, BA1), BA1 ba1) { return new XorpMemberCallback1B1(o, p, ba1); }; }; /** * Factory function that creates a callback object targetted at a * member function with 1 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback1::RefPtr callback( O* o, R (O::*p)(A1, BA1), BA1 ba1) { return XorpMemberCallbackFactory1B1::True>::make(o, p, ba1); } /** * Factory function that creates a callback object targetted at a * member function with 1 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback1::RefPtr callback( O& o, R (O::*p)(A1, BA1), BA1 ba1) { return XorpMemberCallbackFactory1B1::True>::make(&o, p, ba1); } /** * @short Callback object for const member methods with 1 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstMemberCallback1B1 : public XorpCallback1 { typedef R (O::*M)(A1, BA1) const; XorpConstMemberCallback1B1(O* o, M m, BA1 ba1) : XorpCallback1(), _o(o), _m(m), _ba1(ba1) {} R dispatch(A1 a1) { R r = ((*_o).*_m)(a1, _ba1); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for void const member methods with 1 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstMemberCallback1B1 : public XorpCallback1 { typedef void (O::*M)(A1, BA1) const; XorpConstMemberCallback1B1(O* o, M m, BA1 ba1) : XorpCallback1(), _o(o), _m(m), _ba1(ba1) {} void dispatch(A1 a1) { ((*_o).*_m)(a1, _ba1); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for const safe member methods with 1 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback1B1 : public XorpConstMemberCallback1B1, public SafeCallbackBase { typedef typename XorpConstMemberCallback1B1::M M; XorpConstSafeMemberCallback1B1(O* o, M m, BA1 ba1) : XorpConstMemberCallback1B1(o, m, ba1), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback1B1() {} R dispatch(A1 a1) { if (valid()) { R r = XorpConstMemberCallback1B1::dispatch(a1); return r; } } }; /** * @short Callback object for void const safe member methods with 1 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback1B1 : public XorpConstMemberCallback1B1, public SafeCallbackBase { typedef typename XorpConstMemberCallback1B1::M M; XorpConstSafeMemberCallback1B1(O* o, M m, BA1 ba1) : XorpConstMemberCallback1B1(o, m, ba1), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback1B1() {} void dispatch(A1 a1) { if (valid()) { XorpConstMemberCallback1B1::dispatch(a1); } } }; template struct XorpConstMemberCallbackFactory1B1 { static XorpConstMemberCallback1B1* make(O* o, R (O::*p)(A1, BA1) const, BA1 ba1) { return new XorpConstSafeMemberCallback1B1(o, p, ba1); } }; template struct XorpConstMemberCallbackFactory1B1 { static XorpConstMemberCallback1B1* make(O* o, R (O::*p)(A1, BA1) const, BA1 ba1) { return new XorpConstMemberCallback1B1(o, p, ba1); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 1 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback1::RefPtr callback( const O* o, R (O::*p)(A1, BA1) const, BA1 ba1) { return XorpConstMemberCallbackFactory1B1::True>::make(o, p, ba1); } /** * Factory function that creates a callback object targetted at a * const member function with 1 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback1::RefPtr callback( const O& o, R (O::*p)(A1, BA1) const, BA1 ba1) { return XorpConstMemberCallbackFactory1B1::True>::make(&o, p, ba1); } /** * @short Callback object for functions with 1 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpFunctionCallback1B2 : public XorpCallback1 { typedef R (*F)(A1, BA1, BA2); XorpFunctionCallback1B2(F f, BA1 ba1, BA2 ba2) : XorpCallback1(), _f(f), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1) { R r = (*_f)(a1, _ba1, _ba2); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; }; /** * @short Callback object for void functions with 1 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpFunctionCallback1B2 : public XorpCallback1 { typedef void (*F)(A1, BA1, BA2); XorpFunctionCallback1B2(F f, BA1 ba1, BA2 ba2) : XorpCallback1(), _f(f), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1) { (*_f)(a1, _ba1, _ba2); } protected: F _f; BA1 _ba1; BA2 _ba2; }; /** * Factory function that creates a callback object targetted at a * function with 1 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback1::RefPtr callback(R (*f)(A1, BA1, BA2), BA1 ba1, BA2 ba2) { return typename XorpCallback1::RefPtr(new XorpFunctionCallback1B2(f, ba1, ba2)); } /** * @short Callback object for member methods with 1 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpMemberCallback1B2 : public XorpCallback1 { typedef R (O::*M)(A1, BA1, BA2) ; XorpMemberCallback1B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback1(), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1) { R r = ((*_o).*_m)(a1, _ba1, _ba2); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for void member methods with 1 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpMemberCallback1B2 : public XorpCallback1 { typedef void (O::*M)(A1, BA1, BA2) ; XorpMemberCallback1B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback1(), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1) { ((*_o).*_m)(a1, _ba1, _ba2); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for safe member methods with 1 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpSafeMemberCallback1B2 : public XorpMemberCallback1B2, public SafeCallbackBase { typedef typename XorpMemberCallback1B2::M M; XorpSafeMemberCallback1B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpMemberCallback1B2(o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpSafeMemberCallback1B2() {} R dispatch(A1 a1) { if (valid()) { R r = XorpMemberCallback1B2::dispatch(a1); return r; } } }; /** * @short Callback object for void safe member methods with 1 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpSafeMemberCallback1B2 : public XorpMemberCallback1B2, public SafeCallbackBase { typedef typename XorpMemberCallback1B2::M M; XorpSafeMemberCallback1B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpMemberCallback1B2(o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpSafeMemberCallback1B2() {} void dispatch(A1 a1) { if (valid()) { XorpMemberCallback1B2::dispatch(a1); } } }; template struct XorpMemberCallbackFactory1B2 { static XorpMemberCallback1B2* make(O* o, R (O::*p)(A1, BA1, BA2), BA1 ba1, BA2 ba2) { return new XorpSafeMemberCallback1B2(o, p, ba1, ba2); } }; template struct XorpMemberCallbackFactory1B2 { static XorpMemberCallback1B2* make(O* o, R (O::*p)(A1, BA1, BA2), BA1 ba1, BA2 ba2) { return new XorpMemberCallback1B2(o, p, ba1, ba2); }; }; /** * Factory function that creates a callback object targetted at a * member function with 1 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback1::RefPtr callback( O* o, R (O::*p)(A1, BA1, BA2), BA1 ba1, BA2 ba2) { return XorpMemberCallbackFactory1B2::True>::make(o, p, ba1, ba2); } /** * Factory function that creates a callback object targetted at a * member function with 1 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback1::RefPtr callback( O& o, R (O::*p)(A1, BA1, BA2), BA1 ba1, BA2 ba2) { return XorpMemberCallbackFactory1B2::True>::make(&o, p, ba1, ba2); } /** * @short Callback object for const member methods with 1 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstMemberCallback1B2 : public XorpCallback1 { typedef R (O::*M)(A1, BA1, BA2) const; XorpConstMemberCallback1B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback1(), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1) { R r = ((*_o).*_m)(a1, _ba1, _ba2); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for void const member methods with 1 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstMemberCallback1B2 : public XorpCallback1 { typedef void (O::*M)(A1, BA1, BA2) const; XorpConstMemberCallback1B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback1(), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1) { ((*_o).*_m)(a1, _ba1, _ba2); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for const safe member methods with 1 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback1B2 : public XorpConstMemberCallback1B2, public SafeCallbackBase { typedef typename XorpConstMemberCallback1B2::M M; XorpConstSafeMemberCallback1B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpConstMemberCallback1B2(o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback1B2() {} R dispatch(A1 a1) { if (valid()) { R r = XorpConstMemberCallback1B2::dispatch(a1); return r; } } }; /** * @short Callback object for void const safe member methods with 1 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback1B2 : public XorpConstMemberCallback1B2, public SafeCallbackBase { typedef typename XorpConstMemberCallback1B2::M M; XorpConstSafeMemberCallback1B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpConstMemberCallback1B2(o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback1B2() {} void dispatch(A1 a1) { if (valid()) { XorpConstMemberCallback1B2::dispatch(a1); } } }; template struct XorpConstMemberCallbackFactory1B2 { static XorpConstMemberCallback1B2* make(O* o, R (O::*p)(A1, BA1, BA2) const, BA1 ba1, BA2 ba2) { return new XorpConstSafeMemberCallback1B2(o, p, ba1, ba2); } }; template struct XorpConstMemberCallbackFactory1B2 { static XorpConstMemberCallback1B2* make(O* o, R (O::*p)(A1, BA1, BA2) const, BA1 ba1, BA2 ba2) { return new XorpConstMemberCallback1B2(o, p, ba1, ba2); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 1 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback1::RefPtr callback( const O* o, R (O::*p)(A1, BA1, BA2) const, BA1 ba1, BA2 ba2) { return XorpConstMemberCallbackFactory1B2::True>::make(o, p, ba1, ba2); } /** * Factory function that creates a callback object targetted at a * const member function with 1 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback1::RefPtr callback( const O& o, R (O::*p)(A1, BA1, BA2) const, BA1 ba1, BA2 ba2) { return XorpConstMemberCallbackFactory1B2::True>::make(&o, p, ba1, ba2); } /** * @short Callback object for functions with 1 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpFunctionCallback1B3 : public XorpCallback1 { typedef R (*F)(A1, BA1, BA2, BA3); XorpFunctionCallback1B3(F f, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback1(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1) { R r = (*_f)(a1, _ba1, _ba2, _ba3); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; }; /** * @short Callback object for void functions with 1 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpFunctionCallback1B3 : public XorpCallback1 { typedef void (*F)(A1, BA1, BA2, BA3); XorpFunctionCallback1B3(F f, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback1(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1) { (*_f)(a1, _ba1, _ba2, _ba3); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; }; /** * Factory function that creates a callback object targetted at a * function with 1 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback1::RefPtr callback(R (*f)(A1, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return typename XorpCallback1::RefPtr(new XorpFunctionCallback1B3(f, ba1, ba2, ba3)); } /** * @short Callback object for member methods with 1 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpMemberCallback1B3 : public XorpCallback1 { typedef R (O::*M)(A1, BA1, BA2, BA3) ; XorpMemberCallback1B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback1(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1) { R r = ((*_o).*_m)(a1, _ba1, _ba2, _ba3); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for void member methods with 1 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpMemberCallback1B3 : public XorpCallback1 { typedef void (O::*M)(A1, BA1, BA2, BA3) ; XorpMemberCallback1B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback1(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1) { ((*_o).*_m)(a1, _ba1, _ba2, _ba3); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for safe member methods with 1 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpSafeMemberCallback1B3 : public XorpMemberCallback1B3, public SafeCallbackBase { typedef typename XorpMemberCallback1B3::M M; XorpSafeMemberCallback1B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpMemberCallback1B3(o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpSafeMemberCallback1B3() {} R dispatch(A1 a1) { if (valid()) { R r = XorpMemberCallback1B3::dispatch(a1); return r; } } }; /** * @short Callback object for void safe member methods with 1 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpSafeMemberCallback1B3 : public XorpMemberCallback1B3, public SafeCallbackBase { typedef typename XorpMemberCallback1B3::M M; XorpSafeMemberCallback1B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpMemberCallback1B3(o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpSafeMemberCallback1B3() {} void dispatch(A1 a1) { if (valid()) { XorpMemberCallback1B3::dispatch(a1); } } }; template struct XorpMemberCallbackFactory1B3 { static XorpMemberCallback1B3* make(O* o, R (O::*p)(A1, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpSafeMemberCallback1B3(o, p, ba1, ba2, ba3); } }; template struct XorpMemberCallbackFactory1B3 { static XorpMemberCallback1B3* make(O* o, R (O::*p)(A1, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpMemberCallback1B3(o, p, ba1, ba2, ba3); }; }; /** * Factory function that creates a callback object targetted at a * member function with 1 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback1::RefPtr callback( O* o, R (O::*p)(A1, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return XorpMemberCallbackFactory1B3::True>::make(o, p, ba1, ba2, ba3); } /** * Factory function that creates a callback object targetted at a * member function with 1 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback1::RefPtr callback( O& o, R (O::*p)(A1, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return XorpMemberCallbackFactory1B3::True>::make(&o, p, ba1, ba2, ba3); } /** * @short Callback object for const member methods with 1 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstMemberCallback1B3 : public XorpCallback1 { typedef R (O::*M)(A1, BA1, BA2, BA3) const; XorpConstMemberCallback1B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback1(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1) { R r = ((*_o).*_m)(a1, _ba1, _ba2, _ba3); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for void const member methods with 1 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstMemberCallback1B3 : public XorpCallback1 { typedef void (O::*M)(A1, BA1, BA2, BA3) const; XorpConstMemberCallback1B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback1(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1) { ((*_o).*_m)(a1, _ba1, _ba2, _ba3); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for const safe member methods with 1 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback1B3 : public XorpConstMemberCallback1B3, public SafeCallbackBase { typedef typename XorpConstMemberCallback1B3::M M; XorpConstSafeMemberCallback1B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpConstMemberCallback1B3(o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback1B3() {} R dispatch(A1 a1) { if (valid()) { R r = XorpConstMemberCallback1B3::dispatch(a1); return r; } } }; /** * @short Callback object for void const safe member methods with 1 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback1B3 : public XorpConstMemberCallback1B3, public SafeCallbackBase { typedef typename XorpConstMemberCallback1B3::M M; XorpConstSafeMemberCallback1B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpConstMemberCallback1B3(o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback1B3() {} void dispatch(A1 a1) { if (valid()) { XorpConstMemberCallback1B3::dispatch(a1); } } }; template struct XorpConstMemberCallbackFactory1B3 { static XorpConstMemberCallback1B3* make(O* o, R (O::*p)(A1, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpConstSafeMemberCallback1B3(o, p, ba1, ba2, ba3); } }; template struct XorpConstMemberCallbackFactory1B3 { static XorpConstMemberCallback1B3* make(O* o, R (O::*p)(A1, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpConstMemberCallback1B3(o, p, ba1, ba2, ba3); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 1 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback1::RefPtr callback( const O* o, R (O::*p)(A1, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return XorpConstMemberCallbackFactory1B3::True>::make(o, p, ba1, ba2, ba3); } /** * Factory function that creates a callback object targetted at a * const member function with 1 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback1::RefPtr callback( const O& o, R (O::*p)(A1, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return XorpConstMemberCallbackFactory1B3::True>::make(&o, p, ba1, ba2, ba3); } /** * @short Callback object for functions with 1 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpFunctionCallback1B4 : public XorpCallback1 { typedef R (*F)(A1, BA1, BA2, BA3, BA4); XorpFunctionCallback1B4(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback1(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1) { R r = (*_f)(a1, _ba1, _ba2, _ba3, _ba4); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; }; /** * @short Callback object for void functions with 1 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpFunctionCallback1B4 : public XorpCallback1 { typedef void (*F)(A1, BA1, BA2, BA3, BA4); XorpFunctionCallback1B4(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback1(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1) { (*_f)(a1, _ba1, _ba2, _ba3, _ba4); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; }; /** * Factory function that creates a callback object targetted at a * function with 1 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback1::RefPtr callback(R (*f)(A1, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return typename XorpCallback1::RefPtr(new XorpFunctionCallback1B4(f, ba1, ba2, ba3, ba4)); } /** * @short Callback object for member methods with 1 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpMemberCallback1B4 : public XorpCallback1 { typedef R (O::*M)(A1, BA1, BA2, BA3, BA4) ; XorpMemberCallback1B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback1(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1) { R r = ((*_o).*_m)(a1, _ba1, _ba2, _ba3, _ba4); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for void member methods with 1 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpMemberCallback1B4 : public XorpCallback1 { typedef void (O::*M)(A1, BA1, BA2, BA3, BA4) ; XorpMemberCallback1B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback1(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1) { ((*_o).*_m)(a1, _ba1, _ba2, _ba3, _ba4); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for safe member methods with 1 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpSafeMemberCallback1B4 : public XorpMemberCallback1B4, public SafeCallbackBase { typedef typename XorpMemberCallback1B4::M M; XorpSafeMemberCallback1B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpMemberCallback1B4(o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpSafeMemberCallback1B4() {} R dispatch(A1 a1) { if (valid()) { R r = XorpMemberCallback1B4::dispatch(a1); return r; } } }; /** * @short Callback object for void safe member methods with 1 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpSafeMemberCallback1B4 : public XorpMemberCallback1B4, public SafeCallbackBase { typedef typename XorpMemberCallback1B4::M M; XorpSafeMemberCallback1B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpMemberCallback1B4(o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpSafeMemberCallback1B4() {} void dispatch(A1 a1) { if (valid()) { XorpMemberCallback1B4::dispatch(a1); } } }; template struct XorpMemberCallbackFactory1B4 { static XorpMemberCallback1B4* make(O* o, R (O::*p)(A1, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpSafeMemberCallback1B4(o, p, ba1, ba2, ba3, ba4); } }; template struct XorpMemberCallbackFactory1B4 { static XorpMemberCallback1B4* make(O* o, R (O::*p)(A1, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpMemberCallback1B4(o, p, ba1, ba2, ba3, ba4); }; }; /** * Factory function that creates a callback object targetted at a * member function with 1 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback1::RefPtr callback( O* o, R (O::*p)(A1, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpMemberCallbackFactory1B4::True>::make(o, p, ba1, ba2, ba3, ba4); } /** * Factory function that creates a callback object targetted at a * member function with 1 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback1::RefPtr callback( O& o, R (O::*p)(A1, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpMemberCallbackFactory1B4::True>::make(&o, p, ba1, ba2, ba3, ba4); } /** * @short Callback object for const member methods with 1 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstMemberCallback1B4 : public XorpCallback1 { typedef R (O::*M)(A1, BA1, BA2, BA3, BA4) const; XorpConstMemberCallback1B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback1(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1) { R r = ((*_o).*_m)(a1, _ba1, _ba2, _ba3, _ba4); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for void const member methods with 1 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstMemberCallback1B4 : public XorpCallback1 { typedef void (O::*M)(A1, BA1, BA2, BA3, BA4) const; XorpConstMemberCallback1B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback1(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1) { ((*_o).*_m)(a1, _ba1, _ba2, _ba3, _ba4); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for const safe member methods with 1 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback1B4 : public XorpConstMemberCallback1B4, public SafeCallbackBase { typedef typename XorpConstMemberCallback1B4::M M; XorpConstSafeMemberCallback1B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpConstMemberCallback1B4(o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback1B4() {} R dispatch(A1 a1) { if (valid()) { R r = XorpConstMemberCallback1B4::dispatch(a1); return r; } } }; /** * @short Callback object for void const safe member methods with 1 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback1B4 : public XorpConstMemberCallback1B4, public SafeCallbackBase { typedef typename XorpConstMemberCallback1B4::M M; XorpConstSafeMemberCallback1B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpConstMemberCallback1B4(o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback1B4() {} void dispatch(A1 a1) { if (valid()) { XorpConstMemberCallback1B4::dispatch(a1); } } }; template struct XorpConstMemberCallbackFactory1B4 { static XorpConstMemberCallback1B4* make(O* o, R (O::*p)(A1, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpConstSafeMemberCallback1B4(o, p, ba1, ba2, ba3, ba4); } }; template struct XorpConstMemberCallbackFactory1B4 { static XorpConstMemberCallback1B4* make(O* o, R (O::*p)(A1, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpConstMemberCallback1B4(o, p, ba1, ba2, ba3, ba4); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 1 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback1::RefPtr callback( const O* o, R (O::*p)(A1, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpConstMemberCallbackFactory1B4::True>::make(o, p, ba1, ba2, ba3, ba4); } /** * Factory function that creates a callback object targetted at a * const member function with 1 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback1::RefPtr callback( const O& o, R (O::*p)(A1, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpConstMemberCallbackFactory1B4::True>::make(&o, p, ba1, ba2, ba3, ba4); } /** * @short Callback object for functions with 1 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpFunctionCallback1B5 : public XorpCallback1 { typedef R (*F)(A1, BA1, BA2, BA3, BA4, BA5); XorpFunctionCallback1B5(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback1(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1) { R r = (*_f)(a1, _ba1, _ba2, _ba3, _ba4, _ba5); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; }; /** * @short Callback object for void functions with 1 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpFunctionCallback1B5 : public XorpCallback1 { typedef void (*F)(A1, BA1, BA2, BA3, BA4, BA5); XorpFunctionCallback1B5(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback1(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1) { (*_f)(a1, _ba1, _ba2, _ba3, _ba4, _ba5); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; }; /** * Factory function that creates a callback object targetted at a * function with 1 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback1::RefPtr callback(R (*f)(A1, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return typename XorpCallback1::RefPtr(new XorpFunctionCallback1B5(f, ba1, ba2, ba3, ba4, ba5)); } /** * @short Callback object for member methods with 1 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpMemberCallback1B5 : public XorpCallback1 { typedef R (O::*M)(A1, BA1, BA2, BA3, BA4, BA5) ; XorpMemberCallback1B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback1(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1) { R r = ((*_o).*_m)(a1, _ba1, _ba2, _ba3, _ba4, _ba5); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for void member methods with 1 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpMemberCallback1B5 : public XorpCallback1 { typedef void (O::*M)(A1, BA1, BA2, BA3, BA4, BA5) ; XorpMemberCallback1B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback1(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1) { ((*_o).*_m)(a1, _ba1, _ba2, _ba3, _ba4, _ba5); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for safe member methods with 1 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpSafeMemberCallback1B5 : public XorpMemberCallback1B5, public SafeCallbackBase { typedef typename XorpMemberCallback1B5::M M; XorpSafeMemberCallback1B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpMemberCallback1B5(o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpSafeMemberCallback1B5() {} R dispatch(A1 a1) { if (valid()) { R r = XorpMemberCallback1B5::dispatch(a1); return r; } } }; /** * @short Callback object for void safe member methods with 1 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpSafeMemberCallback1B5 : public XorpMemberCallback1B5, public SafeCallbackBase { typedef typename XorpMemberCallback1B5::M M; XorpSafeMemberCallback1B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpMemberCallback1B5(o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpSafeMemberCallback1B5() {} void dispatch(A1 a1) { if (valid()) { XorpMemberCallback1B5::dispatch(a1); } } }; template struct XorpMemberCallbackFactory1B5 { static XorpMemberCallback1B5* make(O* o, R (O::*p)(A1, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpSafeMemberCallback1B5(o, p, ba1, ba2, ba3, ba4, ba5); } }; template struct XorpMemberCallbackFactory1B5 { static XorpMemberCallback1B5* make(O* o, R (O::*p)(A1, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpMemberCallback1B5(o, p, ba1, ba2, ba3, ba4, ba5); }; }; /** * Factory function that creates a callback object targetted at a * member function with 1 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback1::RefPtr callback( O* o, R (O::*p)(A1, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpMemberCallbackFactory1B5::True>::make(o, p, ba1, ba2, ba3, ba4, ba5); } /** * Factory function that creates a callback object targetted at a * member function with 1 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback1::RefPtr callback( O& o, R (O::*p)(A1, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpMemberCallbackFactory1B5::True>::make(&o, p, ba1, ba2, ba3, ba4, ba5); } /** * @short Callback object for const member methods with 1 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstMemberCallback1B5 : public XorpCallback1 { typedef R (O::*M)(A1, BA1, BA2, BA3, BA4, BA5) const; XorpConstMemberCallback1B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback1(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1) { R r = ((*_o).*_m)(a1, _ba1, _ba2, _ba3, _ba4, _ba5); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for void const member methods with 1 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstMemberCallback1B5 : public XorpCallback1 { typedef void (O::*M)(A1, BA1, BA2, BA3, BA4, BA5) const; XorpConstMemberCallback1B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback1(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1) { ((*_o).*_m)(a1, _ba1, _ba2, _ba3, _ba4, _ba5); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for const safe member methods with 1 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback1B5 : public XorpConstMemberCallback1B5, public SafeCallbackBase { typedef typename XorpConstMemberCallback1B5::M M; XorpConstSafeMemberCallback1B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpConstMemberCallback1B5(o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback1B5() {} R dispatch(A1 a1) { if (valid()) { R r = XorpConstMemberCallback1B5::dispatch(a1); return r; } } }; /** * @short Callback object for void const safe member methods with 1 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback1B5 : public XorpConstMemberCallback1B5, public SafeCallbackBase { typedef typename XorpConstMemberCallback1B5::M M; XorpConstSafeMemberCallback1B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpConstMemberCallback1B5(o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback1B5() {} void dispatch(A1 a1) { if (valid()) { XorpConstMemberCallback1B5::dispatch(a1); } } }; template struct XorpConstMemberCallbackFactory1B5 { static XorpConstMemberCallback1B5* make(O* o, R (O::*p)(A1, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpConstSafeMemberCallback1B5(o, p, ba1, ba2, ba3, ba4, ba5); } }; template struct XorpConstMemberCallbackFactory1B5 { static XorpConstMemberCallback1B5* make(O* o, R (O::*p)(A1, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpConstMemberCallback1B5(o, p, ba1, ba2, ba3, ba4, ba5); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 1 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback1::RefPtr callback( const O* o, R (O::*p)(A1, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpConstMemberCallbackFactory1B5::True>::make(o, p, ba1, ba2, ba3, ba4, ba5); } /** * Factory function that creates a callback object targetted at a * const member function with 1 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback1::RefPtr callback( const O& o, R (O::*p)(A1, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpConstMemberCallbackFactory1B5::True>::make(&o, p, ba1, ba2, ba3, ba4, ba5); } /** * @short Callback object for functions with 1 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpFunctionCallback1B6 : public XorpCallback1 { typedef R (*F)(A1, BA1, BA2, BA3, BA4, BA5, BA6); XorpFunctionCallback1B6(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback1(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1) { R r = (*_f)(a1, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; BA6 _ba6; }; /** * @short Callback object for void functions with 1 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpFunctionCallback1B6 : public XorpCallback1 { typedef void (*F)(A1, BA1, BA2, BA3, BA4, BA5, BA6); XorpFunctionCallback1B6(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback1(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1) { (*_f)(a1, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; BA6 _ba6; }; /** * Factory function that creates a callback object targetted at a * function with 1 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback1::RefPtr callback(R (*f)(A1, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return typename XorpCallback1::RefPtr(new XorpFunctionCallback1B6(f, ba1, ba2, ba3, ba4, ba5, ba6)); } /** * @short Callback object for member methods with 1 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpMemberCallback1B6 : public XorpCallback1 { typedef R (O::*M)(A1, BA1, BA2, BA3, BA4, BA5, BA6) ; XorpMemberCallback1B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback1(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1) { R r = ((*_o).*_m)(a1, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for void member methods with 1 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpMemberCallback1B6 : public XorpCallback1 { typedef void (O::*M)(A1, BA1, BA2, BA3, BA4, BA5, BA6) ; XorpMemberCallback1B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback1(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1) { ((*_o).*_m)(a1, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for safe member methods with 1 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpSafeMemberCallback1B6 : public XorpMemberCallback1B6, public SafeCallbackBase { typedef typename XorpMemberCallback1B6::M M; XorpSafeMemberCallback1B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpMemberCallback1B6(o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpSafeMemberCallback1B6() {} R dispatch(A1 a1) { if (valid()) { R r = XorpMemberCallback1B6::dispatch(a1); return r; } } }; /** * @short Callback object for void safe member methods with 1 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpSafeMemberCallback1B6 : public XorpMemberCallback1B6, public SafeCallbackBase { typedef typename XorpMemberCallback1B6::M M; XorpSafeMemberCallback1B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpMemberCallback1B6(o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpSafeMemberCallback1B6() {} void dispatch(A1 a1) { if (valid()) { XorpMemberCallback1B6::dispatch(a1); } } }; template struct XorpMemberCallbackFactory1B6 { static XorpMemberCallback1B6* make(O* o, R (O::*p)(A1, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpSafeMemberCallback1B6(o, p, ba1, ba2, ba3, ba4, ba5, ba6); } }; template struct XorpMemberCallbackFactory1B6 { static XorpMemberCallback1B6* make(O* o, R (O::*p)(A1, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpMemberCallback1B6(o, p, ba1, ba2, ba3, ba4, ba5, ba6); }; }; /** * Factory function that creates a callback object targetted at a * member function with 1 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback1::RefPtr callback( O* o, R (O::*p)(A1, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpMemberCallbackFactory1B6::True>::make(o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * Factory function that creates a callback object targetted at a * member function with 1 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback1::RefPtr callback( O& o, R (O::*p)(A1, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpMemberCallbackFactory1B6::True>::make(&o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * @short Callback object for const member methods with 1 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstMemberCallback1B6 : public XorpCallback1 { typedef R (O::*M)(A1, BA1, BA2, BA3, BA4, BA5, BA6) const; XorpConstMemberCallback1B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback1(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1) { R r = ((*_o).*_m)(a1, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for void const member methods with 1 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstMemberCallback1B6 : public XorpCallback1 { typedef void (O::*M)(A1, BA1, BA2, BA3, BA4, BA5, BA6) const; XorpConstMemberCallback1B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback1(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1) { ((*_o).*_m)(a1, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for const safe member methods with 1 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback1B6 : public XorpConstMemberCallback1B6, public SafeCallbackBase { typedef typename XorpConstMemberCallback1B6::M M; XorpConstSafeMemberCallback1B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpConstMemberCallback1B6(o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback1B6() {} R dispatch(A1 a1) { if (valid()) { R r = XorpConstMemberCallback1B6::dispatch(a1); return r; } } }; /** * @short Callback object for void const safe member methods with 1 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback1B6 : public XorpConstMemberCallback1B6, public SafeCallbackBase { typedef typename XorpConstMemberCallback1B6::M M; XorpConstSafeMemberCallback1B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpConstMemberCallback1B6(o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback1B6() {} void dispatch(A1 a1) { if (valid()) { XorpConstMemberCallback1B6::dispatch(a1); } } }; template struct XorpConstMemberCallbackFactory1B6 { static XorpConstMemberCallback1B6* make(O* o, R (O::*p)(A1, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpConstSafeMemberCallback1B6(o, p, ba1, ba2, ba3, ba4, ba5, ba6); } }; template struct XorpConstMemberCallbackFactory1B6 { static XorpConstMemberCallback1B6* make(O* o, R (O::*p)(A1, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpConstMemberCallback1B6(o, p, ba1, ba2, ba3, ba4, ba5, ba6); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 1 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback1::RefPtr callback( const O* o, R (O::*p)(A1, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpConstMemberCallbackFactory1B6::True>::make(o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * Factory function that creates a callback object targetted at a * const member function with 1 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback1::RefPtr callback( const O& o, R (O::*p)(A1, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpConstMemberCallbackFactory1B6::True>::make(&o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /////////////////////////////////////////////////////////////////////////////// // // Code relating to callbacks with 2 late args // /** * @short Base class for callbacks with 2 dispatch time args. */ template struct XorpCallback2 { typedef ref_ptr RefPtr; virtual ~XorpCallback2() {} virtual R dispatch(A1, A2) = 0; }; /** * @short Callback object for functions with 2 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpFunctionCallback2B0 : public XorpCallback2 { typedef R (*F)(A1, A2); XorpFunctionCallback2B0(F f) : XorpCallback2(), _f(f) {} R dispatch(A1 a1, A2 a2) { R r = (*_f)(a1, a2); return r; } protected: F _f; }; /** * @short Callback object for void functions with 2 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpFunctionCallback2B0 : public XorpCallback2 { typedef void (*F)(A1, A2); XorpFunctionCallback2B0(F f) : XorpCallback2(), _f(f) {} void dispatch(A1 a1, A2 a2) { (*_f)(a1, a2); } protected: F _f; }; /** * Factory function that creates a callback object targetted at a * function with 2 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback2::RefPtr callback(R (*f)(A1, A2)) { return typename XorpCallback2::RefPtr(new XorpFunctionCallback2B0(f)); } /** * @short Callback object for member methods with 2 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpMemberCallback2B0 : public XorpCallback2 { typedef R (O::*M)(A1, A2) ; XorpMemberCallback2B0(O* o, M m) : XorpCallback2(), _o(o), _m(m) {} R dispatch(A1 a1, A2 a2) { R r = ((*_o).*_m)(a1, a2); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for void member methods with 2 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpMemberCallback2B0 : public XorpCallback2 { typedef void (O::*M)(A1, A2) ; XorpMemberCallback2B0(O* o, M m) : XorpCallback2(), _o(o), _m(m) {} void dispatch(A1 a1, A2 a2) { ((*_o).*_m)(a1, a2); } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for safe member methods with 2 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpSafeMemberCallback2B0 : public XorpMemberCallback2B0, public SafeCallbackBase { typedef typename XorpMemberCallback2B0::M M; XorpSafeMemberCallback2B0(O* o, M m) : XorpMemberCallback2B0(o, m), SafeCallbackBase(o) {} ~XorpSafeMemberCallback2B0() {} R dispatch(A1 a1, A2 a2) { if (valid()) { R r = XorpMemberCallback2B0::dispatch(a1, a2); return r; } } }; /** * @short Callback object for void safe member methods with 2 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpSafeMemberCallback2B0 : public XorpMemberCallback2B0, public SafeCallbackBase { typedef typename XorpMemberCallback2B0::M M; XorpSafeMemberCallback2B0(O* o, M m) : XorpMemberCallback2B0(o, m), SafeCallbackBase(o) {} ~XorpSafeMemberCallback2B0() {} void dispatch(A1 a1, A2 a2) { if (valid()) { XorpMemberCallback2B0::dispatch(a1, a2); } } }; template struct XorpMemberCallbackFactory2B0 { static XorpMemberCallback2B0* make(O* o, R (O::*p)(A1, A2)) { return new XorpSafeMemberCallback2B0(o, p); } }; template struct XorpMemberCallbackFactory2B0 { static XorpMemberCallback2B0* make(O* o, R (O::*p)(A1, A2)) { return new XorpMemberCallback2B0(o, p); }; }; /** * Factory function that creates a callback object targetted at a * member function with 2 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback2::RefPtr callback( O* o, R (O::*p)(A1, A2)) { return XorpMemberCallbackFactory2B0::True>::make(o, p); } /** * Factory function that creates a callback object targetted at a * member function with 2 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback2::RefPtr callback( O& o, R (O::*p)(A1, A2)) { return XorpMemberCallbackFactory2B0::True>::make(&o, p); } /** * @short Callback object for const member methods with 2 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstMemberCallback2B0 : public XorpCallback2 { typedef R (O::*M)(A1, A2) const; XorpConstMemberCallback2B0(O* o, M m) : XorpCallback2(), _o(o), _m(m) {} R dispatch(A1 a1, A2 a2) { R r = ((*_o).*_m)(a1, a2); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for void const member methods with 2 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstMemberCallback2B0 : public XorpCallback2 { typedef void (O::*M)(A1, A2) const; XorpConstMemberCallback2B0(O* o, M m) : XorpCallback2(), _o(o), _m(m) {} void dispatch(A1 a1, A2 a2) { ((*_o).*_m)(a1, a2); } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for const safe member methods with 2 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback2B0 : public XorpConstMemberCallback2B0, public SafeCallbackBase { typedef typename XorpConstMemberCallback2B0::M M; XorpConstSafeMemberCallback2B0(O* o, M m) : XorpConstMemberCallback2B0(o, m), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback2B0() {} R dispatch(A1 a1, A2 a2) { if (valid()) { R r = XorpConstMemberCallback2B0::dispatch(a1, a2); return r; } } }; /** * @short Callback object for void const safe member methods with 2 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback2B0 : public XorpConstMemberCallback2B0, public SafeCallbackBase { typedef typename XorpConstMemberCallback2B0::M M; XorpConstSafeMemberCallback2B0(O* o, M m) : XorpConstMemberCallback2B0(o, m), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback2B0() {} void dispatch(A1 a1, A2 a2) { if (valid()) { XorpConstMemberCallback2B0::dispatch(a1, a2); } } }; template struct XorpConstMemberCallbackFactory2B0 { static XorpConstMemberCallback2B0* make(O* o, R (O::*p)(A1, A2) const) { return new XorpConstSafeMemberCallback2B0(o, p); } }; template struct XorpConstMemberCallbackFactory2B0 { static XorpConstMemberCallback2B0* make(O* o, R (O::*p)(A1, A2) const) { return new XorpConstMemberCallback2B0(o, p); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 2 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback2::RefPtr callback( const O* o, R (O::*p)(A1, A2) const) { return XorpConstMemberCallbackFactory2B0::True>::make(o, p); } /** * Factory function that creates a callback object targetted at a * const member function with 2 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback2::RefPtr callback( const O& o, R (O::*p)(A1, A2) const) { return XorpConstMemberCallbackFactory2B0::True>::make(&o, p); } /** * @short Callback object for functions with 2 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpFunctionCallback2B1 : public XorpCallback2 { typedef R (*F)(A1, A2, BA1); XorpFunctionCallback2B1(F f, BA1 ba1) : XorpCallback2(), _f(f), _ba1(ba1) {} R dispatch(A1 a1, A2 a2) { R r = (*_f)(a1, a2, _ba1); return r; } protected: F _f; BA1 _ba1; }; /** * @short Callback object for void functions with 2 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpFunctionCallback2B1 : public XorpCallback2 { typedef void (*F)(A1, A2, BA1); XorpFunctionCallback2B1(F f, BA1 ba1) : XorpCallback2(), _f(f), _ba1(ba1) {} void dispatch(A1 a1, A2 a2) { (*_f)(a1, a2, _ba1); } protected: F _f; BA1 _ba1; }; /** * Factory function that creates a callback object targetted at a * function with 2 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback2::RefPtr callback(R (*f)(A1, A2, BA1), BA1 ba1) { return typename XorpCallback2::RefPtr(new XorpFunctionCallback2B1(f, ba1)); } /** * @short Callback object for member methods with 2 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpMemberCallback2B1 : public XorpCallback2 { typedef R (O::*M)(A1, A2, BA1) ; XorpMemberCallback2B1(O* o, M m, BA1 ba1) : XorpCallback2(), _o(o), _m(m), _ba1(ba1) {} R dispatch(A1 a1, A2 a2) { R r = ((*_o).*_m)(a1, a2, _ba1); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for void member methods with 2 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpMemberCallback2B1 : public XorpCallback2 { typedef void (O::*M)(A1, A2, BA1) ; XorpMemberCallback2B1(O* o, M m, BA1 ba1) : XorpCallback2(), _o(o), _m(m), _ba1(ba1) {} void dispatch(A1 a1, A2 a2) { ((*_o).*_m)(a1, a2, _ba1); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for safe member methods with 2 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpSafeMemberCallback2B1 : public XorpMemberCallback2B1, public SafeCallbackBase { typedef typename XorpMemberCallback2B1::M M; XorpSafeMemberCallback2B1(O* o, M m, BA1 ba1) : XorpMemberCallback2B1(o, m, ba1), SafeCallbackBase(o) {} ~XorpSafeMemberCallback2B1() {} R dispatch(A1 a1, A2 a2) { if (valid()) { R r = XorpMemberCallback2B1::dispatch(a1, a2); return r; } } }; /** * @short Callback object for void safe member methods with 2 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpSafeMemberCallback2B1 : public XorpMemberCallback2B1, public SafeCallbackBase { typedef typename XorpMemberCallback2B1::M M; XorpSafeMemberCallback2B1(O* o, M m, BA1 ba1) : XorpMemberCallback2B1(o, m, ba1), SafeCallbackBase(o) {} ~XorpSafeMemberCallback2B1() {} void dispatch(A1 a1, A2 a2) { if (valid()) { XorpMemberCallback2B1::dispatch(a1, a2); } } }; template struct XorpMemberCallbackFactory2B1 { static XorpMemberCallback2B1* make(O* o, R (O::*p)(A1, A2, BA1), BA1 ba1) { return new XorpSafeMemberCallback2B1(o, p, ba1); } }; template struct XorpMemberCallbackFactory2B1 { static XorpMemberCallback2B1* make(O* o, R (O::*p)(A1, A2, BA1), BA1 ba1) { return new XorpMemberCallback2B1(o, p, ba1); }; }; /** * Factory function that creates a callback object targetted at a * member function with 2 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback2::RefPtr callback( O* o, R (O::*p)(A1, A2, BA1), BA1 ba1) { return XorpMemberCallbackFactory2B1::True>::make(o, p, ba1); } /** * Factory function that creates a callback object targetted at a * member function with 2 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback2::RefPtr callback( O& o, R (O::*p)(A1, A2, BA1), BA1 ba1) { return XorpMemberCallbackFactory2B1::True>::make(&o, p, ba1); } /** * @short Callback object for const member methods with 2 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstMemberCallback2B1 : public XorpCallback2 { typedef R (O::*M)(A1, A2, BA1) const; XorpConstMemberCallback2B1(O* o, M m, BA1 ba1) : XorpCallback2(), _o(o), _m(m), _ba1(ba1) {} R dispatch(A1 a1, A2 a2) { R r = ((*_o).*_m)(a1, a2, _ba1); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for void const member methods with 2 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstMemberCallback2B1 : public XorpCallback2 { typedef void (O::*M)(A1, A2, BA1) const; XorpConstMemberCallback2B1(O* o, M m, BA1 ba1) : XorpCallback2(), _o(o), _m(m), _ba1(ba1) {} void dispatch(A1 a1, A2 a2) { ((*_o).*_m)(a1, a2, _ba1); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for const safe member methods with 2 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback2B1 : public XorpConstMemberCallback2B1, public SafeCallbackBase { typedef typename XorpConstMemberCallback2B1::M M; XorpConstSafeMemberCallback2B1(O* o, M m, BA1 ba1) : XorpConstMemberCallback2B1(o, m, ba1), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback2B1() {} R dispatch(A1 a1, A2 a2) { if (valid()) { R r = XorpConstMemberCallback2B1::dispatch(a1, a2); return r; } } }; /** * @short Callback object for void const safe member methods with 2 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback2B1 : public XorpConstMemberCallback2B1, public SafeCallbackBase { typedef typename XorpConstMemberCallback2B1::M M; XorpConstSafeMemberCallback2B1(O* o, M m, BA1 ba1) : XorpConstMemberCallback2B1(o, m, ba1), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback2B1() {} void dispatch(A1 a1, A2 a2) { if (valid()) { XorpConstMemberCallback2B1::dispatch(a1, a2); } } }; template struct XorpConstMemberCallbackFactory2B1 { static XorpConstMemberCallback2B1* make(O* o, R (O::*p)(A1, A2, BA1) const, BA1 ba1) { return new XorpConstSafeMemberCallback2B1(o, p, ba1); } }; template struct XorpConstMemberCallbackFactory2B1 { static XorpConstMemberCallback2B1* make(O* o, R (O::*p)(A1, A2, BA1) const, BA1 ba1) { return new XorpConstMemberCallback2B1(o, p, ba1); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 2 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback2::RefPtr callback( const O* o, R (O::*p)(A1, A2, BA1) const, BA1 ba1) { return XorpConstMemberCallbackFactory2B1::True>::make(o, p, ba1); } /** * Factory function that creates a callback object targetted at a * const member function with 2 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback2::RefPtr callback( const O& o, R (O::*p)(A1, A2, BA1) const, BA1 ba1) { return XorpConstMemberCallbackFactory2B1::True>::make(&o, p, ba1); } /** * @short Callback object for functions with 2 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpFunctionCallback2B2 : public XorpCallback2 { typedef R (*F)(A1, A2, BA1, BA2); XorpFunctionCallback2B2(F f, BA1 ba1, BA2 ba2) : XorpCallback2(), _f(f), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2) { R r = (*_f)(a1, a2, _ba1, _ba2); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; }; /** * @short Callback object for void functions with 2 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpFunctionCallback2B2 : public XorpCallback2 { typedef void (*F)(A1, A2, BA1, BA2); XorpFunctionCallback2B2(F f, BA1 ba1, BA2 ba2) : XorpCallback2(), _f(f), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2) { (*_f)(a1, a2, _ba1, _ba2); } protected: F _f; BA1 _ba1; BA2 _ba2; }; /** * Factory function that creates a callback object targetted at a * function with 2 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback2::RefPtr callback(R (*f)(A1, A2, BA1, BA2), BA1 ba1, BA2 ba2) { return typename XorpCallback2::RefPtr(new XorpFunctionCallback2B2(f, ba1, ba2)); } /** * @short Callback object for member methods with 2 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpMemberCallback2B2 : public XorpCallback2 { typedef R (O::*M)(A1, A2, BA1, BA2) ; XorpMemberCallback2B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback2(), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2) { R r = ((*_o).*_m)(a1, a2, _ba1, _ba2); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for void member methods with 2 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpMemberCallback2B2 : public XorpCallback2 { typedef void (O::*M)(A1, A2, BA1, BA2) ; XorpMemberCallback2B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback2(), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2) { ((*_o).*_m)(a1, a2, _ba1, _ba2); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for safe member methods with 2 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpSafeMemberCallback2B2 : public XorpMemberCallback2B2, public SafeCallbackBase { typedef typename XorpMemberCallback2B2::M M; XorpSafeMemberCallback2B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpMemberCallback2B2(o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpSafeMemberCallback2B2() {} R dispatch(A1 a1, A2 a2) { if (valid()) { R r = XorpMemberCallback2B2::dispatch(a1, a2); return r; } } }; /** * @short Callback object for void safe member methods with 2 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpSafeMemberCallback2B2 : public XorpMemberCallback2B2, public SafeCallbackBase { typedef typename XorpMemberCallback2B2::M M; XorpSafeMemberCallback2B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpMemberCallback2B2(o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpSafeMemberCallback2B2() {} void dispatch(A1 a1, A2 a2) { if (valid()) { XorpMemberCallback2B2::dispatch(a1, a2); } } }; template struct XorpMemberCallbackFactory2B2 { static XorpMemberCallback2B2* make(O* o, R (O::*p)(A1, A2, BA1, BA2), BA1 ba1, BA2 ba2) { return new XorpSafeMemberCallback2B2(o, p, ba1, ba2); } }; template struct XorpMemberCallbackFactory2B2 { static XorpMemberCallback2B2* make(O* o, R (O::*p)(A1, A2, BA1, BA2), BA1 ba1, BA2 ba2) { return new XorpMemberCallback2B2(o, p, ba1, ba2); }; }; /** * Factory function that creates a callback object targetted at a * member function with 2 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback2::RefPtr callback( O* o, R (O::*p)(A1, A2, BA1, BA2), BA1 ba1, BA2 ba2) { return XorpMemberCallbackFactory2B2::True>::make(o, p, ba1, ba2); } /** * Factory function that creates a callback object targetted at a * member function with 2 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback2::RefPtr callback( O& o, R (O::*p)(A1, A2, BA1, BA2), BA1 ba1, BA2 ba2) { return XorpMemberCallbackFactory2B2::True>::make(&o, p, ba1, ba2); } /** * @short Callback object for const member methods with 2 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstMemberCallback2B2 : public XorpCallback2 { typedef R (O::*M)(A1, A2, BA1, BA2) const; XorpConstMemberCallback2B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback2(), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2) { R r = ((*_o).*_m)(a1, a2, _ba1, _ba2); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for void const member methods with 2 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstMemberCallback2B2 : public XorpCallback2 { typedef void (O::*M)(A1, A2, BA1, BA2) const; XorpConstMemberCallback2B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback2(), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2) { ((*_o).*_m)(a1, a2, _ba1, _ba2); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for const safe member methods with 2 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback2B2 : public XorpConstMemberCallback2B2, public SafeCallbackBase { typedef typename XorpConstMemberCallback2B2::M M; XorpConstSafeMemberCallback2B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpConstMemberCallback2B2(o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback2B2() {} R dispatch(A1 a1, A2 a2) { if (valid()) { R r = XorpConstMemberCallback2B2::dispatch(a1, a2); return r; } } }; /** * @short Callback object for void const safe member methods with 2 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback2B2 : public XorpConstMemberCallback2B2, public SafeCallbackBase { typedef typename XorpConstMemberCallback2B2::M M; XorpConstSafeMemberCallback2B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpConstMemberCallback2B2(o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback2B2() {} void dispatch(A1 a1, A2 a2) { if (valid()) { XorpConstMemberCallback2B2::dispatch(a1, a2); } } }; template struct XorpConstMemberCallbackFactory2B2 { static XorpConstMemberCallback2B2* make(O* o, R (O::*p)(A1, A2, BA1, BA2) const, BA1 ba1, BA2 ba2) { return new XorpConstSafeMemberCallback2B2(o, p, ba1, ba2); } }; template struct XorpConstMemberCallbackFactory2B2 { static XorpConstMemberCallback2B2* make(O* o, R (O::*p)(A1, A2, BA1, BA2) const, BA1 ba1, BA2 ba2) { return new XorpConstMemberCallback2B2(o, p, ba1, ba2); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 2 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback2::RefPtr callback( const O* o, R (O::*p)(A1, A2, BA1, BA2) const, BA1 ba1, BA2 ba2) { return XorpConstMemberCallbackFactory2B2::True>::make(o, p, ba1, ba2); } /** * Factory function that creates a callback object targetted at a * const member function with 2 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback2::RefPtr callback( const O& o, R (O::*p)(A1, A2, BA1, BA2) const, BA1 ba1, BA2 ba2) { return XorpConstMemberCallbackFactory2B2::True>::make(&o, p, ba1, ba2); } /** * @short Callback object for functions with 2 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpFunctionCallback2B3 : public XorpCallback2 { typedef R (*F)(A1, A2, BA1, BA2, BA3); XorpFunctionCallback2B3(F f, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback2(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2) { R r = (*_f)(a1, a2, _ba1, _ba2, _ba3); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; }; /** * @short Callback object for void functions with 2 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpFunctionCallback2B3 : public XorpCallback2 { typedef void (*F)(A1, A2, BA1, BA2, BA3); XorpFunctionCallback2B3(F f, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback2(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2) { (*_f)(a1, a2, _ba1, _ba2, _ba3); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; }; /** * Factory function that creates a callback object targetted at a * function with 2 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback2::RefPtr callback(R (*f)(A1, A2, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return typename XorpCallback2::RefPtr(new XorpFunctionCallback2B3(f, ba1, ba2, ba3)); } /** * @short Callback object for member methods with 2 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpMemberCallback2B3 : public XorpCallback2 { typedef R (O::*M)(A1, A2, BA1, BA2, BA3) ; XorpMemberCallback2B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback2(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2) { R r = ((*_o).*_m)(a1, a2, _ba1, _ba2, _ba3); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for void member methods with 2 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpMemberCallback2B3 : public XorpCallback2 { typedef void (O::*M)(A1, A2, BA1, BA2, BA3) ; XorpMemberCallback2B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback2(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2) { ((*_o).*_m)(a1, a2, _ba1, _ba2, _ba3); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for safe member methods with 2 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpSafeMemberCallback2B3 : public XorpMemberCallback2B3, public SafeCallbackBase { typedef typename XorpMemberCallback2B3::M M; XorpSafeMemberCallback2B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpMemberCallback2B3(o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpSafeMemberCallback2B3() {} R dispatch(A1 a1, A2 a2) { if (valid()) { R r = XorpMemberCallback2B3::dispatch(a1, a2); return r; } } }; /** * @short Callback object for void safe member methods with 2 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpSafeMemberCallback2B3 : public XorpMemberCallback2B3, public SafeCallbackBase { typedef typename XorpMemberCallback2B3::M M; XorpSafeMemberCallback2B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpMemberCallback2B3(o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpSafeMemberCallback2B3() {} void dispatch(A1 a1, A2 a2) { if (valid()) { XorpMemberCallback2B3::dispatch(a1, a2); } } }; template struct XorpMemberCallbackFactory2B3 { static XorpMemberCallback2B3* make(O* o, R (O::*p)(A1, A2, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpSafeMemberCallback2B3(o, p, ba1, ba2, ba3); } }; template struct XorpMemberCallbackFactory2B3 { static XorpMemberCallback2B3* make(O* o, R (O::*p)(A1, A2, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpMemberCallback2B3(o, p, ba1, ba2, ba3); }; }; /** * Factory function that creates a callback object targetted at a * member function with 2 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback2::RefPtr callback( O* o, R (O::*p)(A1, A2, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return XorpMemberCallbackFactory2B3::True>::make(o, p, ba1, ba2, ba3); } /** * Factory function that creates a callback object targetted at a * member function with 2 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback2::RefPtr callback( O& o, R (O::*p)(A1, A2, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return XorpMemberCallbackFactory2B3::True>::make(&o, p, ba1, ba2, ba3); } /** * @short Callback object for const member methods with 2 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstMemberCallback2B3 : public XorpCallback2 { typedef R (O::*M)(A1, A2, BA1, BA2, BA3) const; XorpConstMemberCallback2B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback2(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2) { R r = ((*_o).*_m)(a1, a2, _ba1, _ba2, _ba3); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for void const member methods with 2 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstMemberCallback2B3 : public XorpCallback2 { typedef void (O::*M)(A1, A2, BA1, BA2, BA3) const; XorpConstMemberCallback2B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback2(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2) { ((*_o).*_m)(a1, a2, _ba1, _ba2, _ba3); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for const safe member methods with 2 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback2B3 : public XorpConstMemberCallback2B3, public SafeCallbackBase { typedef typename XorpConstMemberCallback2B3::M M; XorpConstSafeMemberCallback2B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpConstMemberCallback2B3(o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback2B3() {} R dispatch(A1 a1, A2 a2) { if (valid()) { R r = XorpConstMemberCallback2B3::dispatch(a1, a2); return r; } } }; /** * @short Callback object for void const safe member methods with 2 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback2B3 : public XorpConstMemberCallback2B3, public SafeCallbackBase { typedef typename XorpConstMemberCallback2B3::M M; XorpConstSafeMemberCallback2B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpConstMemberCallback2B3(o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback2B3() {} void dispatch(A1 a1, A2 a2) { if (valid()) { XorpConstMemberCallback2B3::dispatch(a1, a2); } } }; template struct XorpConstMemberCallbackFactory2B3 { static XorpConstMemberCallback2B3* make(O* o, R (O::*p)(A1, A2, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpConstSafeMemberCallback2B3(o, p, ba1, ba2, ba3); } }; template struct XorpConstMemberCallbackFactory2B3 { static XorpConstMemberCallback2B3* make(O* o, R (O::*p)(A1, A2, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpConstMemberCallback2B3(o, p, ba1, ba2, ba3); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 2 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback2::RefPtr callback( const O* o, R (O::*p)(A1, A2, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return XorpConstMemberCallbackFactory2B3::True>::make(o, p, ba1, ba2, ba3); } /** * Factory function that creates a callback object targetted at a * const member function with 2 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback2::RefPtr callback( const O& o, R (O::*p)(A1, A2, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return XorpConstMemberCallbackFactory2B3::True>::make(&o, p, ba1, ba2, ba3); } /** * @short Callback object for functions with 2 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpFunctionCallback2B4 : public XorpCallback2 { typedef R (*F)(A1, A2, BA1, BA2, BA3, BA4); XorpFunctionCallback2B4(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback2(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2) { R r = (*_f)(a1, a2, _ba1, _ba2, _ba3, _ba4); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; }; /** * @short Callback object for void functions with 2 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpFunctionCallback2B4 : public XorpCallback2 { typedef void (*F)(A1, A2, BA1, BA2, BA3, BA4); XorpFunctionCallback2B4(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback2(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2) { (*_f)(a1, a2, _ba1, _ba2, _ba3, _ba4); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; }; /** * Factory function that creates a callback object targetted at a * function with 2 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback2::RefPtr callback(R (*f)(A1, A2, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return typename XorpCallback2::RefPtr(new XorpFunctionCallback2B4(f, ba1, ba2, ba3, ba4)); } /** * @short Callback object for member methods with 2 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpMemberCallback2B4 : public XorpCallback2 { typedef R (O::*M)(A1, A2, BA1, BA2, BA3, BA4) ; XorpMemberCallback2B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback2(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2) { R r = ((*_o).*_m)(a1, a2, _ba1, _ba2, _ba3, _ba4); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for void member methods with 2 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpMemberCallback2B4 : public XorpCallback2 { typedef void (O::*M)(A1, A2, BA1, BA2, BA3, BA4) ; XorpMemberCallback2B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback2(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2) { ((*_o).*_m)(a1, a2, _ba1, _ba2, _ba3, _ba4); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for safe member methods with 2 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpSafeMemberCallback2B4 : public XorpMemberCallback2B4, public SafeCallbackBase { typedef typename XorpMemberCallback2B4::M M; XorpSafeMemberCallback2B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpMemberCallback2B4(o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpSafeMemberCallback2B4() {} R dispatch(A1 a1, A2 a2) { if (valid()) { R r = XorpMemberCallback2B4::dispatch(a1, a2); return r; } } }; /** * @short Callback object for void safe member methods with 2 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpSafeMemberCallback2B4 : public XorpMemberCallback2B4, public SafeCallbackBase { typedef typename XorpMemberCallback2B4::M M; XorpSafeMemberCallback2B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpMemberCallback2B4(o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpSafeMemberCallback2B4() {} void dispatch(A1 a1, A2 a2) { if (valid()) { XorpMemberCallback2B4::dispatch(a1, a2); } } }; template struct XorpMemberCallbackFactory2B4 { static XorpMemberCallback2B4* make(O* o, R (O::*p)(A1, A2, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpSafeMemberCallback2B4(o, p, ba1, ba2, ba3, ba4); } }; template struct XorpMemberCallbackFactory2B4 { static XorpMemberCallback2B4* make(O* o, R (O::*p)(A1, A2, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpMemberCallback2B4(o, p, ba1, ba2, ba3, ba4); }; }; /** * Factory function that creates a callback object targetted at a * member function with 2 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback2::RefPtr callback( O* o, R (O::*p)(A1, A2, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpMemberCallbackFactory2B4::True>::make(o, p, ba1, ba2, ba3, ba4); } /** * Factory function that creates a callback object targetted at a * member function with 2 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback2::RefPtr callback( O& o, R (O::*p)(A1, A2, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpMemberCallbackFactory2B4::True>::make(&o, p, ba1, ba2, ba3, ba4); } /** * @short Callback object for const member methods with 2 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstMemberCallback2B4 : public XorpCallback2 { typedef R (O::*M)(A1, A2, BA1, BA2, BA3, BA4) const; XorpConstMemberCallback2B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback2(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2) { R r = ((*_o).*_m)(a1, a2, _ba1, _ba2, _ba3, _ba4); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for void const member methods with 2 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstMemberCallback2B4 : public XorpCallback2 { typedef void (O::*M)(A1, A2, BA1, BA2, BA3, BA4) const; XorpConstMemberCallback2B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback2(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2) { ((*_o).*_m)(a1, a2, _ba1, _ba2, _ba3, _ba4); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for const safe member methods with 2 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback2B4 : public XorpConstMemberCallback2B4, public SafeCallbackBase { typedef typename XorpConstMemberCallback2B4::M M; XorpConstSafeMemberCallback2B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpConstMemberCallback2B4(o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback2B4() {} R dispatch(A1 a1, A2 a2) { if (valid()) { R r = XorpConstMemberCallback2B4::dispatch(a1, a2); return r; } } }; /** * @short Callback object for void const safe member methods with 2 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback2B4 : public XorpConstMemberCallback2B4, public SafeCallbackBase { typedef typename XorpConstMemberCallback2B4::M M; XorpConstSafeMemberCallback2B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpConstMemberCallback2B4(o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback2B4() {} void dispatch(A1 a1, A2 a2) { if (valid()) { XorpConstMemberCallback2B4::dispatch(a1, a2); } } }; template struct XorpConstMemberCallbackFactory2B4 { static XorpConstMemberCallback2B4* make(O* o, R (O::*p)(A1, A2, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpConstSafeMemberCallback2B4(o, p, ba1, ba2, ba3, ba4); } }; template struct XorpConstMemberCallbackFactory2B4 { static XorpConstMemberCallback2B4* make(O* o, R (O::*p)(A1, A2, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpConstMemberCallback2B4(o, p, ba1, ba2, ba3, ba4); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 2 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback2::RefPtr callback( const O* o, R (O::*p)(A1, A2, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpConstMemberCallbackFactory2B4::True>::make(o, p, ba1, ba2, ba3, ba4); } /** * Factory function that creates a callback object targetted at a * const member function with 2 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback2::RefPtr callback( const O& o, R (O::*p)(A1, A2, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpConstMemberCallbackFactory2B4::True>::make(&o, p, ba1, ba2, ba3, ba4); } /** * @short Callback object for functions with 2 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpFunctionCallback2B5 : public XorpCallback2 { typedef R (*F)(A1, A2, BA1, BA2, BA3, BA4, BA5); XorpFunctionCallback2B5(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback2(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2) { R r = (*_f)(a1, a2, _ba1, _ba2, _ba3, _ba4, _ba5); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; }; /** * @short Callback object for void functions with 2 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpFunctionCallback2B5 : public XorpCallback2 { typedef void (*F)(A1, A2, BA1, BA2, BA3, BA4, BA5); XorpFunctionCallback2B5(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback2(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2) { (*_f)(a1, a2, _ba1, _ba2, _ba3, _ba4, _ba5); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; }; /** * Factory function that creates a callback object targetted at a * function with 2 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback2::RefPtr callback(R (*f)(A1, A2, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return typename XorpCallback2::RefPtr(new XorpFunctionCallback2B5(f, ba1, ba2, ba3, ba4, ba5)); } /** * @short Callback object for member methods with 2 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpMemberCallback2B5 : public XorpCallback2 { typedef R (O::*M)(A1, A2, BA1, BA2, BA3, BA4, BA5) ; XorpMemberCallback2B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback2(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2) { R r = ((*_o).*_m)(a1, a2, _ba1, _ba2, _ba3, _ba4, _ba5); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for void member methods with 2 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpMemberCallback2B5 : public XorpCallback2 { typedef void (O::*M)(A1, A2, BA1, BA2, BA3, BA4, BA5) ; XorpMemberCallback2B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback2(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2) { ((*_o).*_m)(a1, a2, _ba1, _ba2, _ba3, _ba4, _ba5); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for safe member methods with 2 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpSafeMemberCallback2B5 : public XorpMemberCallback2B5, public SafeCallbackBase { typedef typename XorpMemberCallback2B5::M M; XorpSafeMemberCallback2B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpMemberCallback2B5(o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpSafeMemberCallback2B5() {} R dispatch(A1 a1, A2 a2) { if (valid()) { R r = XorpMemberCallback2B5::dispatch(a1, a2); return r; } } }; /** * @short Callback object for void safe member methods with 2 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpSafeMemberCallback2B5 : public XorpMemberCallback2B5, public SafeCallbackBase { typedef typename XorpMemberCallback2B5::M M; XorpSafeMemberCallback2B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpMemberCallback2B5(o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpSafeMemberCallback2B5() {} void dispatch(A1 a1, A2 a2) { if (valid()) { XorpMemberCallback2B5::dispatch(a1, a2); } } }; template struct XorpMemberCallbackFactory2B5 { static XorpMemberCallback2B5* make(O* o, R (O::*p)(A1, A2, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpSafeMemberCallback2B5(o, p, ba1, ba2, ba3, ba4, ba5); } }; template struct XorpMemberCallbackFactory2B5 { static XorpMemberCallback2B5* make(O* o, R (O::*p)(A1, A2, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpMemberCallback2B5(o, p, ba1, ba2, ba3, ba4, ba5); }; }; /** * Factory function that creates a callback object targetted at a * member function with 2 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback2::RefPtr callback( O* o, R (O::*p)(A1, A2, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpMemberCallbackFactory2B5::True>::make(o, p, ba1, ba2, ba3, ba4, ba5); } /** * Factory function that creates a callback object targetted at a * member function with 2 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback2::RefPtr callback( O& o, R (O::*p)(A1, A2, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpMemberCallbackFactory2B5::True>::make(&o, p, ba1, ba2, ba3, ba4, ba5); } /** * @short Callback object for const member methods with 2 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstMemberCallback2B5 : public XorpCallback2 { typedef R (O::*M)(A1, A2, BA1, BA2, BA3, BA4, BA5) const; XorpConstMemberCallback2B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback2(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2) { R r = ((*_o).*_m)(a1, a2, _ba1, _ba2, _ba3, _ba4, _ba5); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for void const member methods with 2 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstMemberCallback2B5 : public XorpCallback2 { typedef void (O::*M)(A1, A2, BA1, BA2, BA3, BA4, BA5) const; XorpConstMemberCallback2B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback2(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2) { ((*_o).*_m)(a1, a2, _ba1, _ba2, _ba3, _ba4, _ba5); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for const safe member methods with 2 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback2B5 : public XorpConstMemberCallback2B5, public SafeCallbackBase { typedef typename XorpConstMemberCallback2B5::M M; XorpConstSafeMemberCallback2B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpConstMemberCallback2B5(o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback2B5() {} R dispatch(A1 a1, A2 a2) { if (valid()) { R r = XorpConstMemberCallback2B5::dispatch(a1, a2); return r; } } }; /** * @short Callback object for void const safe member methods with 2 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback2B5 : public XorpConstMemberCallback2B5, public SafeCallbackBase { typedef typename XorpConstMemberCallback2B5::M M; XorpConstSafeMemberCallback2B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpConstMemberCallback2B5(o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback2B5() {} void dispatch(A1 a1, A2 a2) { if (valid()) { XorpConstMemberCallback2B5::dispatch(a1, a2); } } }; template struct XorpConstMemberCallbackFactory2B5 { static XorpConstMemberCallback2B5* make(O* o, R (O::*p)(A1, A2, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpConstSafeMemberCallback2B5(o, p, ba1, ba2, ba3, ba4, ba5); } }; template struct XorpConstMemberCallbackFactory2B5 { static XorpConstMemberCallback2B5* make(O* o, R (O::*p)(A1, A2, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpConstMemberCallback2B5(o, p, ba1, ba2, ba3, ba4, ba5); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 2 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback2::RefPtr callback( const O* o, R (O::*p)(A1, A2, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpConstMemberCallbackFactory2B5::True>::make(o, p, ba1, ba2, ba3, ba4, ba5); } /** * Factory function that creates a callback object targetted at a * const member function with 2 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback2::RefPtr callback( const O& o, R (O::*p)(A1, A2, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpConstMemberCallbackFactory2B5::True>::make(&o, p, ba1, ba2, ba3, ba4, ba5); } /** * @short Callback object for functions with 2 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpFunctionCallback2B6 : public XorpCallback2 { typedef R (*F)(A1, A2, BA1, BA2, BA3, BA4, BA5, BA6); XorpFunctionCallback2B6(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback2(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2) { R r = (*_f)(a1, a2, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; BA6 _ba6; }; /** * @short Callback object for void functions with 2 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpFunctionCallback2B6 : public XorpCallback2 { typedef void (*F)(A1, A2, BA1, BA2, BA3, BA4, BA5, BA6); XorpFunctionCallback2B6(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback2(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2) { (*_f)(a1, a2, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; BA6 _ba6; }; /** * Factory function that creates a callback object targetted at a * function with 2 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback2::RefPtr callback(R (*f)(A1, A2, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return typename XorpCallback2::RefPtr(new XorpFunctionCallback2B6(f, ba1, ba2, ba3, ba4, ba5, ba6)); } /** * @short Callback object for member methods with 2 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpMemberCallback2B6 : public XorpCallback2 { typedef R (O::*M)(A1, A2, BA1, BA2, BA3, BA4, BA5, BA6) ; XorpMemberCallback2B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback2(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2) { R r = ((*_o).*_m)(a1, a2, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for void member methods with 2 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpMemberCallback2B6 : public XorpCallback2 { typedef void (O::*M)(A1, A2, BA1, BA2, BA3, BA4, BA5, BA6) ; XorpMemberCallback2B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback2(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2) { ((*_o).*_m)(a1, a2, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for safe member methods with 2 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpSafeMemberCallback2B6 : public XorpMemberCallback2B6, public SafeCallbackBase { typedef typename XorpMemberCallback2B6::M M; XorpSafeMemberCallback2B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpMemberCallback2B6(o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpSafeMemberCallback2B6() {} R dispatch(A1 a1, A2 a2) { if (valid()) { R r = XorpMemberCallback2B6::dispatch(a1, a2); return r; } } }; /** * @short Callback object for void safe member methods with 2 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpSafeMemberCallback2B6 : public XorpMemberCallback2B6, public SafeCallbackBase { typedef typename XorpMemberCallback2B6::M M; XorpSafeMemberCallback2B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpMemberCallback2B6(o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpSafeMemberCallback2B6() {} void dispatch(A1 a1, A2 a2) { if (valid()) { XorpMemberCallback2B6::dispatch(a1, a2); } } }; template struct XorpMemberCallbackFactory2B6 { static XorpMemberCallback2B6* make(O* o, R (O::*p)(A1, A2, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpSafeMemberCallback2B6(o, p, ba1, ba2, ba3, ba4, ba5, ba6); } }; template struct XorpMemberCallbackFactory2B6 { static XorpMemberCallback2B6* make(O* o, R (O::*p)(A1, A2, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpMemberCallback2B6(o, p, ba1, ba2, ba3, ba4, ba5, ba6); }; }; /** * Factory function that creates a callback object targetted at a * member function with 2 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback2::RefPtr callback( O* o, R (O::*p)(A1, A2, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpMemberCallbackFactory2B6::True>::make(o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * Factory function that creates a callback object targetted at a * member function with 2 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback2::RefPtr callback( O& o, R (O::*p)(A1, A2, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpMemberCallbackFactory2B6::True>::make(&o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * @short Callback object for const member methods with 2 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstMemberCallback2B6 : public XorpCallback2 { typedef R (O::*M)(A1, A2, BA1, BA2, BA3, BA4, BA5, BA6) const; XorpConstMemberCallback2B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback2(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2) { R r = ((*_o).*_m)(a1, a2, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for void const member methods with 2 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstMemberCallback2B6 : public XorpCallback2 { typedef void (O::*M)(A1, A2, BA1, BA2, BA3, BA4, BA5, BA6) const; XorpConstMemberCallback2B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback2(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2) { ((*_o).*_m)(a1, a2, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for const safe member methods with 2 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback2B6 : public XorpConstMemberCallback2B6, public SafeCallbackBase { typedef typename XorpConstMemberCallback2B6::M M; XorpConstSafeMemberCallback2B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpConstMemberCallback2B6(o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback2B6() {} R dispatch(A1 a1, A2 a2) { if (valid()) { R r = XorpConstMemberCallback2B6::dispatch(a1, a2); return r; } } }; /** * @short Callback object for void const safe member methods with 2 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback2B6 : public XorpConstMemberCallback2B6, public SafeCallbackBase { typedef typename XorpConstMemberCallback2B6::M M; XorpConstSafeMemberCallback2B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpConstMemberCallback2B6(o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback2B6() {} void dispatch(A1 a1, A2 a2) { if (valid()) { XorpConstMemberCallback2B6::dispatch(a1, a2); } } }; template struct XorpConstMemberCallbackFactory2B6 { static XorpConstMemberCallback2B6* make(O* o, R (O::*p)(A1, A2, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpConstSafeMemberCallback2B6(o, p, ba1, ba2, ba3, ba4, ba5, ba6); } }; template struct XorpConstMemberCallbackFactory2B6 { static XorpConstMemberCallback2B6* make(O* o, R (O::*p)(A1, A2, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpConstMemberCallback2B6(o, p, ba1, ba2, ba3, ba4, ba5, ba6); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 2 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback2::RefPtr callback( const O* o, R (O::*p)(A1, A2, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpConstMemberCallbackFactory2B6::True>::make(o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * Factory function that creates a callback object targetted at a * const member function with 2 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback2::RefPtr callback( const O& o, R (O::*p)(A1, A2, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpConstMemberCallbackFactory2B6::True>::make(&o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /////////////////////////////////////////////////////////////////////////////// // // Code relating to callbacks with 3 late args // /** * @short Base class for callbacks with 3 dispatch time args. */ template struct XorpCallback3 { typedef ref_ptr RefPtr; virtual ~XorpCallback3() {} virtual R dispatch(A1, A2, A3) = 0; }; /** * @short Callback object for functions with 3 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpFunctionCallback3B0 : public XorpCallback3 { typedef R (*F)(A1, A2, A3); XorpFunctionCallback3B0(F f) : XorpCallback3(), _f(f) {} R dispatch(A1 a1, A2 a2, A3 a3) { R r = (*_f)(a1, a2, a3); return r; } protected: F _f; }; /** * @short Callback object for void functions with 3 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpFunctionCallback3B0 : public XorpCallback3 { typedef void (*F)(A1, A2, A3); XorpFunctionCallback3B0(F f) : XorpCallback3(), _f(f) {} void dispatch(A1 a1, A2 a2, A3 a3) { (*_f)(a1, a2, a3); } protected: F _f; }; /** * Factory function that creates a callback object targetted at a * function with 3 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback3::RefPtr callback(R (*f)(A1, A2, A3)) { return typename XorpCallback3::RefPtr(new XorpFunctionCallback3B0(f)); } /** * @short Callback object for member methods with 3 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpMemberCallback3B0 : public XorpCallback3 { typedef R (O::*M)(A1, A2, A3) ; XorpMemberCallback3B0(O* o, M m) : XorpCallback3(), _o(o), _m(m) {} R dispatch(A1 a1, A2 a2, A3 a3) { R r = ((*_o).*_m)(a1, a2, a3); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for void member methods with 3 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpMemberCallback3B0 : public XorpCallback3 { typedef void (O::*M)(A1, A2, A3) ; XorpMemberCallback3B0(O* o, M m) : XorpCallback3(), _o(o), _m(m) {} void dispatch(A1 a1, A2 a2, A3 a3) { ((*_o).*_m)(a1, a2, a3); } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for safe member methods with 3 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpSafeMemberCallback3B0 : public XorpMemberCallback3B0, public SafeCallbackBase { typedef typename XorpMemberCallback3B0::M M; XorpSafeMemberCallback3B0(O* o, M m) : XorpMemberCallback3B0(o, m), SafeCallbackBase(o) {} ~XorpSafeMemberCallback3B0() {} R dispatch(A1 a1, A2 a2, A3 a3) { if (valid()) { R r = XorpMemberCallback3B0::dispatch(a1, a2, a3); return r; } } }; /** * @short Callback object for void safe member methods with 3 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpSafeMemberCallback3B0 : public XorpMemberCallback3B0, public SafeCallbackBase { typedef typename XorpMemberCallback3B0::M M; XorpSafeMemberCallback3B0(O* o, M m) : XorpMemberCallback3B0(o, m), SafeCallbackBase(o) {} ~XorpSafeMemberCallback3B0() {} void dispatch(A1 a1, A2 a2, A3 a3) { if (valid()) { XorpMemberCallback3B0::dispatch(a1, a2, a3); } } }; template struct XorpMemberCallbackFactory3B0 { static XorpMemberCallback3B0* make(O* o, R (O::*p)(A1, A2, A3)) { return new XorpSafeMemberCallback3B0(o, p); } }; template struct XorpMemberCallbackFactory3B0 { static XorpMemberCallback3B0* make(O* o, R (O::*p)(A1, A2, A3)) { return new XorpMemberCallback3B0(o, p); }; }; /** * Factory function that creates a callback object targetted at a * member function with 3 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback3::RefPtr callback( O* o, R (O::*p)(A1, A2, A3)) { return XorpMemberCallbackFactory3B0::True>::make(o, p); } /** * Factory function that creates a callback object targetted at a * member function with 3 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback3::RefPtr callback( O& o, R (O::*p)(A1, A2, A3)) { return XorpMemberCallbackFactory3B0::True>::make(&o, p); } /** * @short Callback object for const member methods with 3 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstMemberCallback3B0 : public XorpCallback3 { typedef R (O::*M)(A1, A2, A3) const; XorpConstMemberCallback3B0(O* o, M m) : XorpCallback3(), _o(o), _m(m) {} R dispatch(A1 a1, A2 a2, A3 a3) { R r = ((*_o).*_m)(a1, a2, a3); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for void const member methods with 3 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstMemberCallback3B0 : public XorpCallback3 { typedef void (O::*M)(A1, A2, A3) const; XorpConstMemberCallback3B0(O* o, M m) : XorpCallback3(), _o(o), _m(m) {} void dispatch(A1 a1, A2 a2, A3 a3) { ((*_o).*_m)(a1, a2, a3); } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for const safe member methods with 3 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback3B0 : public XorpConstMemberCallback3B0, public SafeCallbackBase { typedef typename XorpConstMemberCallback3B0::M M; XorpConstSafeMemberCallback3B0(O* o, M m) : XorpConstMemberCallback3B0(o, m), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback3B0() {} R dispatch(A1 a1, A2 a2, A3 a3) { if (valid()) { R r = XorpConstMemberCallback3B0::dispatch(a1, a2, a3); return r; } } }; /** * @short Callback object for void const safe member methods with 3 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback3B0 : public XorpConstMemberCallback3B0, public SafeCallbackBase { typedef typename XorpConstMemberCallback3B0::M M; XorpConstSafeMemberCallback3B0(O* o, M m) : XorpConstMemberCallback3B0(o, m), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback3B0() {} void dispatch(A1 a1, A2 a2, A3 a3) { if (valid()) { XorpConstMemberCallback3B0::dispatch(a1, a2, a3); } } }; template struct XorpConstMemberCallbackFactory3B0 { static XorpConstMemberCallback3B0* make(O* o, R (O::*p)(A1, A2, A3) const) { return new XorpConstSafeMemberCallback3B0(o, p); } }; template struct XorpConstMemberCallbackFactory3B0 { static XorpConstMemberCallback3B0* make(O* o, R (O::*p)(A1, A2, A3) const) { return new XorpConstMemberCallback3B0(o, p); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 3 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback3::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3) const) { return XorpConstMemberCallbackFactory3B0::True>::make(o, p); } /** * Factory function that creates a callback object targetted at a * const member function with 3 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback3::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3) const) { return XorpConstMemberCallbackFactory3B0::True>::make(&o, p); } /** * @short Callback object for functions with 3 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpFunctionCallback3B1 : public XorpCallback3 { typedef R (*F)(A1, A2, A3, BA1); XorpFunctionCallback3B1(F f, BA1 ba1) : XorpCallback3(), _f(f), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3) { R r = (*_f)(a1, a2, a3, _ba1); return r; } protected: F _f; BA1 _ba1; }; /** * @short Callback object for void functions with 3 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpFunctionCallback3B1 : public XorpCallback3 { typedef void (*F)(A1, A2, A3, BA1); XorpFunctionCallback3B1(F f, BA1 ba1) : XorpCallback3(), _f(f), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3) { (*_f)(a1, a2, a3, _ba1); } protected: F _f; BA1 _ba1; }; /** * Factory function that creates a callback object targetted at a * function with 3 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback3::RefPtr callback(R (*f)(A1, A2, A3, BA1), BA1 ba1) { return typename XorpCallback3::RefPtr(new XorpFunctionCallback3B1(f, ba1)); } /** * @short Callback object for member methods with 3 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpMemberCallback3B1 : public XorpCallback3 { typedef R (O::*M)(A1, A2, A3, BA1) ; XorpMemberCallback3B1(O* o, M m, BA1 ba1) : XorpCallback3(), _o(o), _m(m), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3) { R r = ((*_o).*_m)(a1, a2, a3, _ba1); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for void member methods with 3 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpMemberCallback3B1 : public XorpCallback3 { typedef void (O::*M)(A1, A2, A3, BA1) ; XorpMemberCallback3B1(O* o, M m, BA1 ba1) : XorpCallback3(), _o(o), _m(m), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3) { ((*_o).*_m)(a1, a2, a3, _ba1); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for safe member methods with 3 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpSafeMemberCallback3B1 : public XorpMemberCallback3B1, public SafeCallbackBase { typedef typename XorpMemberCallback3B1::M M; XorpSafeMemberCallback3B1(O* o, M m, BA1 ba1) : XorpMemberCallback3B1(o, m, ba1), SafeCallbackBase(o) {} ~XorpSafeMemberCallback3B1() {} R dispatch(A1 a1, A2 a2, A3 a3) { if (valid()) { R r = XorpMemberCallback3B1::dispatch(a1, a2, a3); return r; } } }; /** * @short Callback object for void safe member methods with 3 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpSafeMemberCallback3B1 : public XorpMemberCallback3B1, public SafeCallbackBase { typedef typename XorpMemberCallback3B1::M M; XorpSafeMemberCallback3B1(O* o, M m, BA1 ba1) : XorpMemberCallback3B1(o, m, ba1), SafeCallbackBase(o) {} ~XorpSafeMemberCallback3B1() {} void dispatch(A1 a1, A2 a2, A3 a3) { if (valid()) { XorpMemberCallback3B1::dispatch(a1, a2, a3); } } }; template struct XorpMemberCallbackFactory3B1 { static XorpMemberCallback3B1* make(O* o, R (O::*p)(A1, A2, A3, BA1), BA1 ba1) { return new XorpSafeMemberCallback3B1(o, p, ba1); } }; template struct XorpMemberCallbackFactory3B1 { static XorpMemberCallback3B1* make(O* o, R (O::*p)(A1, A2, A3, BA1), BA1 ba1) { return new XorpMemberCallback3B1(o, p, ba1); }; }; /** * Factory function that creates a callback object targetted at a * member function with 3 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback3::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, BA1), BA1 ba1) { return XorpMemberCallbackFactory3B1::True>::make(o, p, ba1); } /** * Factory function that creates a callback object targetted at a * member function with 3 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback3::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, BA1), BA1 ba1) { return XorpMemberCallbackFactory3B1::True>::make(&o, p, ba1); } /** * @short Callback object for const member methods with 3 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstMemberCallback3B1 : public XorpCallback3 { typedef R (O::*M)(A1, A2, A3, BA1) const; XorpConstMemberCallback3B1(O* o, M m, BA1 ba1) : XorpCallback3(), _o(o), _m(m), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3) { R r = ((*_o).*_m)(a1, a2, a3, _ba1); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for void const member methods with 3 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstMemberCallback3B1 : public XorpCallback3 { typedef void (O::*M)(A1, A2, A3, BA1) const; XorpConstMemberCallback3B1(O* o, M m, BA1 ba1) : XorpCallback3(), _o(o), _m(m), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3) { ((*_o).*_m)(a1, a2, a3, _ba1); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for const safe member methods with 3 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback3B1 : public XorpConstMemberCallback3B1, public SafeCallbackBase { typedef typename XorpConstMemberCallback3B1::M M; XorpConstSafeMemberCallback3B1(O* o, M m, BA1 ba1) : XorpConstMemberCallback3B1(o, m, ba1), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback3B1() {} R dispatch(A1 a1, A2 a2, A3 a3) { if (valid()) { R r = XorpConstMemberCallback3B1::dispatch(a1, a2, a3); return r; } } }; /** * @short Callback object for void const safe member methods with 3 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback3B1 : public XorpConstMemberCallback3B1, public SafeCallbackBase { typedef typename XorpConstMemberCallback3B1::M M; XorpConstSafeMemberCallback3B1(O* o, M m, BA1 ba1) : XorpConstMemberCallback3B1(o, m, ba1), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback3B1() {} void dispatch(A1 a1, A2 a2, A3 a3) { if (valid()) { XorpConstMemberCallback3B1::dispatch(a1, a2, a3); } } }; template struct XorpConstMemberCallbackFactory3B1 { static XorpConstMemberCallback3B1* make(O* o, R (O::*p)(A1, A2, A3, BA1) const, BA1 ba1) { return new XorpConstSafeMemberCallback3B1(o, p, ba1); } }; template struct XorpConstMemberCallbackFactory3B1 { static XorpConstMemberCallback3B1* make(O* o, R (O::*p)(A1, A2, A3, BA1) const, BA1 ba1) { return new XorpConstMemberCallback3B1(o, p, ba1); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 3 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback3::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, BA1) const, BA1 ba1) { return XorpConstMemberCallbackFactory3B1::True>::make(o, p, ba1); } /** * Factory function that creates a callback object targetted at a * const member function with 3 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback3::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, BA1) const, BA1 ba1) { return XorpConstMemberCallbackFactory3B1::True>::make(&o, p, ba1); } /** * @short Callback object for functions with 3 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpFunctionCallback3B2 : public XorpCallback3 { typedef R (*F)(A1, A2, A3, BA1, BA2); XorpFunctionCallback3B2(F f, BA1 ba1, BA2 ba2) : XorpCallback3(), _f(f), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3) { R r = (*_f)(a1, a2, a3, _ba1, _ba2); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; }; /** * @short Callback object for void functions with 3 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpFunctionCallback3B2 : public XorpCallback3 { typedef void (*F)(A1, A2, A3, BA1, BA2); XorpFunctionCallback3B2(F f, BA1 ba1, BA2 ba2) : XorpCallback3(), _f(f), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3) { (*_f)(a1, a2, a3, _ba1, _ba2); } protected: F _f; BA1 _ba1; BA2 _ba2; }; /** * Factory function that creates a callback object targetted at a * function with 3 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback3::RefPtr callback(R (*f)(A1, A2, A3, BA1, BA2), BA1 ba1, BA2 ba2) { return typename XorpCallback3::RefPtr(new XorpFunctionCallback3B2(f, ba1, ba2)); } /** * @short Callback object for member methods with 3 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpMemberCallback3B2 : public XorpCallback3 { typedef R (O::*M)(A1, A2, A3, BA1, BA2) ; XorpMemberCallback3B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback3(), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3) { R r = ((*_o).*_m)(a1, a2, a3, _ba1, _ba2); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for void member methods with 3 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpMemberCallback3B2 : public XorpCallback3 { typedef void (O::*M)(A1, A2, A3, BA1, BA2) ; XorpMemberCallback3B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback3(), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3) { ((*_o).*_m)(a1, a2, a3, _ba1, _ba2); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for safe member methods with 3 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpSafeMemberCallback3B2 : public XorpMemberCallback3B2, public SafeCallbackBase { typedef typename XorpMemberCallback3B2::M M; XorpSafeMemberCallback3B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpMemberCallback3B2(o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpSafeMemberCallback3B2() {} R dispatch(A1 a1, A2 a2, A3 a3) { if (valid()) { R r = XorpMemberCallback3B2::dispatch(a1, a2, a3); return r; } } }; /** * @short Callback object for void safe member methods with 3 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpSafeMemberCallback3B2 : public XorpMemberCallback3B2, public SafeCallbackBase { typedef typename XorpMemberCallback3B2::M M; XorpSafeMemberCallback3B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpMemberCallback3B2(o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpSafeMemberCallback3B2() {} void dispatch(A1 a1, A2 a2, A3 a3) { if (valid()) { XorpMemberCallback3B2::dispatch(a1, a2, a3); } } }; template struct XorpMemberCallbackFactory3B2 { static XorpMemberCallback3B2* make(O* o, R (O::*p)(A1, A2, A3, BA1, BA2), BA1 ba1, BA2 ba2) { return new XorpSafeMemberCallback3B2(o, p, ba1, ba2); } }; template struct XorpMemberCallbackFactory3B2 { static XorpMemberCallback3B2* make(O* o, R (O::*p)(A1, A2, A3, BA1, BA2), BA1 ba1, BA2 ba2) { return new XorpMemberCallback3B2(o, p, ba1, ba2); }; }; /** * Factory function that creates a callback object targetted at a * member function with 3 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback3::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, BA1, BA2), BA1 ba1, BA2 ba2) { return XorpMemberCallbackFactory3B2::True>::make(o, p, ba1, ba2); } /** * Factory function that creates a callback object targetted at a * member function with 3 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback3::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, BA1, BA2), BA1 ba1, BA2 ba2) { return XorpMemberCallbackFactory3B2::True>::make(&o, p, ba1, ba2); } /** * @short Callback object for const member methods with 3 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstMemberCallback3B2 : public XorpCallback3 { typedef R (O::*M)(A1, A2, A3, BA1, BA2) const; XorpConstMemberCallback3B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback3(), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3) { R r = ((*_o).*_m)(a1, a2, a3, _ba1, _ba2); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for void const member methods with 3 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstMemberCallback3B2 : public XorpCallback3 { typedef void (O::*M)(A1, A2, A3, BA1, BA2) const; XorpConstMemberCallback3B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback3(), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3) { ((*_o).*_m)(a1, a2, a3, _ba1, _ba2); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for const safe member methods with 3 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback3B2 : public XorpConstMemberCallback3B2, public SafeCallbackBase { typedef typename XorpConstMemberCallback3B2::M M; XorpConstSafeMemberCallback3B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpConstMemberCallback3B2(o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback3B2() {} R dispatch(A1 a1, A2 a2, A3 a3) { if (valid()) { R r = XorpConstMemberCallback3B2::dispatch(a1, a2, a3); return r; } } }; /** * @short Callback object for void const safe member methods with 3 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback3B2 : public XorpConstMemberCallback3B2, public SafeCallbackBase { typedef typename XorpConstMemberCallback3B2::M M; XorpConstSafeMemberCallback3B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpConstMemberCallback3B2(o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback3B2() {} void dispatch(A1 a1, A2 a2, A3 a3) { if (valid()) { XorpConstMemberCallback3B2::dispatch(a1, a2, a3); } } }; template struct XorpConstMemberCallbackFactory3B2 { static XorpConstMemberCallback3B2* make(O* o, R (O::*p)(A1, A2, A3, BA1, BA2) const, BA1 ba1, BA2 ba2) { return new XorpConstSafeMemberCallback3B2(o, p, ba1, ba2); } }; template struct XorpConstMemberCallbackFactory3B2 { static XorpConstMemberCallback3B2* make(O* o, R (O::*p)(A1, A2, A3, BA1, BA2) const, BA1 ba1, BA2 ba2) { return new XorpConstMemberCallback3B2(o, p, ba1, ba2); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 3 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback3::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, BA1, BA2) const, BA1 ba1, BA2 ba2) { return XorpConstMemberCallbackFactory3B2::True>::make(o, p, ba1, ba2); } /** * Factory function that creates a callback object targetted at a * const member function with 3 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback3::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, BA1, BA2) const, BA1 ba1, BA2 ba2) { return XorpConstMemberCallbackFactory3B2::True>::make(&o, p, ba1, ba2); } /** * @short Callback object for functions with 3 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpFunctionCallback3B3 : public XorpCallback3 { typedef R (*F)(A1, A2, A3, BA1, BA2, BA3); XorpFunctionCallback3B3(F f, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback3(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3) { R r = (*_f)(a1, a2, a3, _ba1, _ba2, _ba3); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; }; /** * @short Callback object for void functions with 3 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpFunctionCallback3B3 : public XorpCallback3 { typedef void (*F)(A1, A2, A3, BA1, BA2, BA3); XorpFunctionCallback3B3(F f, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback3(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3) { (*_f)(a1, a2, a3, _ba1, _ba2, _ba3); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; }; /** * Factory function that creates a callback object targetted at a * function with 3 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback3::RefPtr callback(R (*f)(A1, A2, A3, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return typename XorpCallback3::RefPtr(new XorpFunctionCallback3B3(f, ba1, ba2, ba3)); } /** * @short Callback object for member methods with 3 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpMemberCallback3B3 : public XorpCallback3 { typedef R (O::*M)(A1, A2, A3, BA1, BA2, BA3) ; XorpMemberCallback3B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback3(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3) { R r = ((*_o).*_m)(a1, a2, a3, _ba1, _ba2, _ba3); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for void member methods with 3 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpMemberCallback3B3 : public XorpCallback3 { typedef void (O::*M)(A1, A2, A3, BA1, BA2, BA3) ; XorpMemberCallback3B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback3(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3) { ((*_o).*_m)(a1, a2, a3, _ba1, _ba2, _ba3); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for safe member methods with 3 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpSafeMemberCallback3B3 : public XorpMemberCallback3B3, public SafeCallbackBase { typedef typename XorpMemberCallback3B3::M M; XorpSafeMemberCallback3B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpMemberCallback3B3(o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpSafeMemberCallback3B3() {} R dispatch(A1 a1, A2 a2, A3 a3) { if (valid()) { R r = XorpMemberCallback3B3::dispatch(a1, a2, a3); return r; } } }; /** * @short Callback object for void safe member methods with 3 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpSafeMemberCallback3B3 : public XorpMemberCallback3B3, public SafeCallbackBase { typedef typename XorpMemberCallback3B3::M M; XorpSafeMemberCallback3B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpMemberCallback3B3(o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpSafeMemberCallback3B3() {} void dispatch(A1 a1, A2 a2, A3 a3) { if (valid()) { XorpMemberCallback3B3::dispatch(a1, a2, a3); } } }; template struct XorpMemberCallbackFactory3B3 { static XorpMemberCallback3B3* make(O* o, R (O::*p)(A1, A2, A3, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpSafeMemberCallback3B3(o, p, ba1, ba2, ba3); } }; template struct XorpMemberCallbackFactory3B3 { static XorpMemberCallback3B3* make(O* o, R (O::*p)(A1, A2, A3, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpMemberCallback3B3(o, p, ba1, ba2, ba3); }; }; /** * Factory function that creates a callback object targetted at a * member function with 3 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback3::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return XorpMemberCallbackFactory3B3::True>::make(o, p, ba1, ba2, ba3); } /** * Factory function that creates a callback object targetted at a * member function with 3 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback3::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return XorpMemberCallbackFactory3B3::True>::make(&o, p, ba1, ba2, ba3); } /** * @short Callback object for const member methods with 3 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstMemberCallback3B3 : public XorpCallback3 { typedef R (O::*M)(A1, A2, A3, BA1, BA2, BA3) const; XorpConstMemberCallback3B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback3(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3) { R r = ((*_o).*_m)(a1, a2, a3, _ba1, _ba2, _ba3); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for void const member methods with 3 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstMemberCallback3B3 : public XorpCallback3 { typedef void (O::*M)(A1, A2, A3, BA1, BA2, BA3) const; XorpConstMemberCallback3B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback3(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3) { ((*_o).*_m)(a1, a2, a3, _ba1, _ba2, _ba3); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for const safe member methods with 3 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback3B3 : public XorpConstMemberCallback3B3, public SafeCallbackBase { typedef typename XorpConstMemberCallback3B3::M M; XorpConstSafeMemberCallback3B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpConstMemberCallback3B3(o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback3B3() {} R dispatch(A1 a1, A2 a2, A3 a3) { if (valid()) { R r = XorpConstMemberCallback3B3::dispatch(a1, a2, a3); return r; } } }; /** * @short Callback object for void const safe member methods with 3 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback3B3 : public XorpConstMemberCallback3B3, public SafeCallbackBase { typedef typename XorpConstMemberCallback3B3::M M; XorpConstSafeMemberCallback3B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpConstMemberCallback3B3(o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback3B3() {} void dispatch(A1 a1, A2 a2, A3 a3) { if (valid()) { XorpConstMemberCallback3B3::dispatch(a1, a2, a3); } } }; template struct XorpConstMemberCallbackFactory3B3 { static XorpConstMemberCallback3B3* make(O* o, R (O::*p)(A1, A2, A3, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpConstSafeMemberCallback3B3(o, p, ba1, ba2, ba3); } }; template struct XorpConstMemberCallbackFactory3B3 { static XorpConstMemberCallback3B3* make(O* o, R (O::*p)(A1, A2, A3, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpConstMemberCallback3B3(o, p, ba1, ba2, ba3); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 3 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback3::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return XorpConstMemberCallbackFactory3B3::True>::make(o, p, ba1, ba2, ba3); } /** * Factory function that creates a callback object targetted at a * const member function with 3 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback3::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return XorpConstMemberCallbackFactory3B3::True>::make(&o, p, ba1, ba2, ba3); } /** * @short Callback object for functions with 3 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpFunctionCallback3B4 : public XorpCallback3 { typedef R (*F)(A1, A2, A3, BA1, BA2, BA3, BA4); XorpFunctionCallback3B4(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback3(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3) { R r = (*_f)(a1, a2, a3, _ba1, _ba2, _ba3, _ba4); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; }; /** * @short Callback object for void functions with 3 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpFunctionCallback3B4 : public XorpCallback3 { typedef void (*F)(A1, A2, A3, BA1, BA2, BA3, BA4); XorpFunctionCallback3B4(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback3(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3) { (*_f)(a1, a2, a3, _ba1, _ba2, _ba3, _ba4); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; }; /** * Factory function that creates a callback object targetted at a * function with 3 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback3::RefPtr callback(R (*f)(A1, A2, A3, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return typename XorpCallback3::RefPtr(new XorpFunctionCallback3B4(f, ba1, ba2, ba3, ba4)); } /** * @short Callback object for member methods with 3 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpMemberCallback3B4 : public XorpCallback3 { typedef R (O::*M)(A1, A2, A3, BA1, BA2, BA3, BA4) ; XorpMemberCallback3B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback3(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3) { R r = ((*_o).*_m)(a1, a2, a3, _ba1, _ba2, _ba3, _ba4); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for void member methods with 3 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpMemberCallback3B4 : public XorpCallback3 { typedef void (O::*M)(A1, A2, A3, BA1, BA2, BA3, BA4) ; XorpMemberCallback3B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback3(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3) { ((*_o).*_m)(a1, a2, a3, _ba1, _ba2, _ba3, _ba4); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for safe member methods with 3 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpSafeMemberCallback3B4 : public XorpMemberCallback3B4, public SafeCallbackBase { typedef typename XorpMemberCallback3B4::M M; XorpSafeMemberCallback3B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpMemberCallback3B4(o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpSafeMemberCallback3B4() {} R dispatch(A1 a1, A2 a2, A3 a3) { if (valid()) { R r = XorpMemberCallback3B4::dispatch(a1, a2, a3); return r; } } }; /** * @short Callback object for void safe member methods with 3 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpSafeMemberCallback3B4 : public XorpMemberCallback3B4, public SafeCallbackBase { typedef typename XorpMemberCallback3B4::M M; XorpSafeMemberCallback3B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpMemberCallback3B4(o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpSafeMemberCallback3B4() {} void dispatch(A1 a1, A2 a2, A3 a3) { if (valid()) { XorpMemberCallback3B4::dispatch(a1, a2, a3); } } }; template struct XorpMemberCallbackFactory3B4 { static XorpMemberCallback3B4* make(O* o, R (O::*p)(A1, A2, A3, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpSafeMemberCallback3B4(o, p, ba1, ba2, ba3, ba4); } }; template struct XorpMemberCallbackFactory3B4 { static XorpMemberCallback3B4* make(O* o, R (O::*p)(A1, A2, A3, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpMemberCallback3B4(o, p, ba1, ba2, ba3, ba4); }; }; /** * Factory function that creates a callback object targetted at a * member function with 3 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback3::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpMemberCallbackFactory3B4::True>::make(o, p, ba1, ba2, ba3, ba4); } /** * Factory function that creates a callback object targetted at a * member function with 3 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback3::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpMemberCallbackFactory3B4::True>::make(&o, p, ba1, ba2, ba3, ba4); } /** * @short Callback object for const member methods with 3 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstMemberCallback3B4 : public XorpCallback3 { typedef R (O::*M)(A1, A2, A3, BA1, BA2, BA3, BA4) const; XorpConstMemberCallback3B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback3(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3) { R r = ((*_o).*_m)(a1, a2, a3, _ba1, _ba2, _ba3, _ba4); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for void const member methods with 3 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstMemberCallback3B4 : public XorpCallback3 { typedef void (O::*M)(A1, A2, A3, BA1, BA2, BA3, BA4) const; XorpConstMemberCallback3B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback3(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3) { ((*_o).*_m)(a1, a2, a3, _ba1, _ba2, _ba3, _ba4); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for const safe member methods with 3 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback3B4 : public XorpConstMemberCallback3B4, public SafeCallbackBase { typedef typename XorpConstMemberCallback3B4::M M; XorpConstSafeMemberCallback3B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpConstMemberCallback3B4(o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback3B4() {} R dispatch(A1 a1, A2 a2, A3 a3) { if (valid()) { R r = XorpConstMemberCallback3B4::dispatch(a1, a2, a3); return r; } } }; /** * @short Callback object for void const safe member methods with 3 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback3B4 : public XorpConstMemberCallback3B4, public SafeCallbackBase { typedef typename XorpConstMemberCallback3B4::M M; XorpConstSafeMemberCallback3B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpConstMemberCallback3B4(o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback3B4() {} void dispatch(A1 a1, A2 a2, A3 a3) { if (valid()) { XorpConstMemberCallback3B4::dispatch(a1, a2, a3); } } }; template struct XorpConstMemberCallbackFactory3B4 { static XorpConstMemberCallback3B4* make(O* o, R (O::*p)(A1, A2, A3, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpConstSafeMemberCallback3B4(o, p, ba1, ba2, ba3, ba4); } }; template struct XorpConstMemberCallbackFactory3B4 { static XorpConstMemberCallback3B4* make(O* o, R (O::*p)(A1, A2, A3, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpConstMemberCallback3B4(o, p, ba1, ba2, ba3, ba4); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 3 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback3::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpConstMemberCallbackFactory3B4::True>::make(o, p, ba1, ba2, ba3, ba4); } /** * Factory function that creates a callback object targetted at a * const member function with 3 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback3::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpConstMemberCallbackFactory3B4::True>::make(&o, p, ba1, ba2, ba3, ba4); } /** * @short Callback object for functions with 3 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpFunctionCallback3B5 : public XorpCallback3 { typedef R (*F)(A1, A2, A3, BA1, BA2, BA3, BA4, BA5); XorpFunctionCallback3B5(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback3(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3) { R r = (*_f)(a1, a2, a3, _ba1, _ba2, _ba3, _ba4, _ba5); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; }; /** * @short Callback object for void functions with 3 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpFunctionCallback3B5 : public XorpCallback3 { typedef void (*F)(A1, A2, A3, BA1, BA2, BA3, BA4, BA5); XorpFunctionCallback3B5(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback3(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3) { (*_f)(a1, a2, a3, _ba1, _ba2, _ba3, _ba4, _ba5); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; }; /** * Factory function that creates a callback object targetted at a * function with 3 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback3::RefPtr callback(R (*f)(A1, A2, A3, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return typename XorpCallback3::RefPtr(new XorpFunctionCallback3B5(f, ba1, ba2, ba3, ba4, ba5)); } /** * @short Callback object for member methods with 3 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpMemberCallback3B5 : public XorpCallback3 { typedef R (O::*M)(A1, A2, A3, BA1, BA2, BA3, BA4, BA5) ; XorpMemberCallback3B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback3(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3) { R r = ((*_o).*_m)(a1, a2, a3, _ba1, _ba2, _ba3, _ba4, _ba5); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for void member methods with 3 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpMemberCallback3B5 : public XorpCallback3 { typedef void (O::*M)(A1, A2, A3, BA1, BA2, BA3, BA4, BA5) ; XorpMemberCallback3B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback3(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3) { ((*_o).*_m)(a1, a2, a3, _ba1, _ba2, _ba3, _ba4, _ba5); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for safe member methods with 3 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpSafeMemberCallback3B5 : public XorpMemberCallback3B5, public SafeCallbackBase { typedef typename XorpMemberCallback3B5::M M; XorpSafeMemberCallback3B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpMemberCallback3B5(o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpSafeMemberCallback3B5() {} R dispatch(A1 a1, A2 a2, A3 a3) { if (valid()) { R r = XorpMemberCallback3B5::dispatch(a1, a2, a3); return r; } } }; /** * @short Callback object for void safe member methods with 3 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpSafeMemberCallback3B5 : public XorpMemberCallback3B5, public SafeCallbackBase { typedef typename XorpMemberCallback3B5::M M; XorpSafeMemberCallback3B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpMemberCallback3B5(o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpSafeMemberCallback3B5() {} void dispatch(A1 a1, A2 a2, A3 a3) { if (valid()) { XorpMemberCallback3B5::dispatch(a1, a2, a3); } } }; template struct XorpMemberCallbackFactory3B5 { static XorpMemberCallback3B5* make(O* o, R (O::*p)(A1, A2, A3, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpSafeMemberCallback3B5(o, p, ba1, ba2, ba3, ba4, ba5); } }; template struct XorpMemberCallbackFactory3B5 { static XorpMemberCallback3B5* make(O* o, R (O::*p)(A1, A2, A3, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpMemberCallback3B5(o, p, ba1, ba2, ba3, ba4, ba5); }; }; /** * Factory function that creates a callback object targetted at a * member function with 3 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback3::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpMemberCallbackFactory3B5::True>::make(o, p, ba1, ba2, ba3, ba4, ba5); } /** * Factory function that creates a callback object targetted at a * member function with 3 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback3::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpMemberCallbackFactory3B5::True>::make(&o, p, ba1, ba2, ba3, ba4, ba5); } /** * @short Callback object for const member methods with 3 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstMemberCallback3B5 : public XorpCallback3 { typedef R (O::*M)(A1, A2, A3, BA1, BA2, BA3, BA4, BA5) const; XorpConstMemberCallback3B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback3(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3) { R r = ((*_o).*_m)(a1, a2, a3, _ba1, _ba2, _ba3, _ba4, _ba5); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for void const member methods with 3 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstMemberCallback3B5 : public XorpCallback3 { typedef void (O::*M)(A1, A2, A3, BA1, BA2, BA3, BA4, BA5) const; XorpConstMemberCallback3B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback3(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3) { ((*_o).*_m)(a1, a2, a3, _ba1, _ba2, _ba3, _ba4, _ba5); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for const safe member methods with 3 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback3B5 : public XorpConstMemberCallback3B5, public SafeCallbackBase { typedef typename XorpConstMemberCallback3B5::M M; XorpConstSafeMemberCallback3B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpConstMemberCallback3B5(o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback3B5() {} R dispatch(A1 a1, A2 a2, A3 a3) { if (valid()) { R r = XorpConstMemberCallback3B5::dispatch(a1, a2, a3); return r; } } }; /** * @short Callback object for void const safe member methods with 3 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback3B5 : public XorpConstMemberCallback3B5, public SafeCallbackBase { typedef typename XorpConstMemberCallback3B5::M M; XorpConstSafeMemberCallback3B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpConstMemberCallback3B5(o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback3B5() {} void dispatch(A1 a1, A2 a2, A3 a3) { if (valid()) { XorpConstMemberCallback3B5::dispatch(a1, a2, a3); } } }; template struct XorpConstMemberCallbackFactory3B5 { static XorpConstMemberCallback3B5* make(O* o, R (O::*p)(A1, A2, A3, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpConstSafeMemberCallback3B5(o, p, ba1, ba2, ba3, ba4, ba5); } }; template struct XorpConstMemberCallbackFactory3B5 { static XorpConstMemberCallback3B5* make(O* o, R (O::*p)(A1, A2, A3, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpConstMemberCallback3B5(o, p, ba1, ba2, ba3, ba4, ba5); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 3 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback3::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpConstMemberCallbackFactory3B5::True>::make(o, p, ba1, ba2, ba3, ba4, ba5); } /** * Factory function that creates a callback object targetted at a * const member function with 3 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback3::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpConstMemberCallbackFactory3B5::True>::make(&o, p, ba1, ba2, ba3, ba4, ba5); } /** * @short Callback object for functions with 3 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpFunctionCallback3B6 : public XorpCallback3 { typedef R (*F)(A1, A2, A3, BA1, BA2, BA3, BA4, BA5, BA6); XorpFunctionCallback3B6(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback3(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3) { R r = (*_f)(a1, a2, a3, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; BA6 _ba6; }; /** * @short Callback object for void functions with 3 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpFunctionCallback3B6 : public XorpCallback3 { typedef void (*F)(A1, A2, A3, BA1, BA2, BA3, BA4, BA5, BA6); XorpFunctionCallback3B6(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback3(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3) { (*_f)(a1, a2, a3, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; BA6 _ba6; }; /** * Factory function that creates a callback object targetted at a * function with 3 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback3::RefPtr callback(R (*f)(A1, A2, A3, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return typename XorpCallback3::RefPtr(new XorpFunctionCallback3B6(f, ba1, ba2, ba3, ba4, ba5, ba6)); } /** * @short Callback object for member methods with 3 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpMemberCallback3B6 : public XorpCallback3 { typedef R (O::*M)(A1, A2, A3, BA1, BA2, BA3, BA4, BA5, BA6) ; XorpMemberCallback3B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback3(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3) { R r = ((*_o).*_m)(a1, a2, a3, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for void member methods with 3 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpMemberCallback3B6 : public XorpCallback3 { typedef void (O::*M)(A1, A2, A3, BA1, BA2, BA3, BA4, BA5, BA6) ; XorpMemberCallback3B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback3(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3) { ((*_o).*_m)(a1, a2, a3, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for safe member methods with 3 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpSafeMemberCallback3B6 : public XorpMemberCallback3B6, public SafeCallbackBase { typedef typename XorpMemberCallback3B6::M M; XorpSafeMemberCallback3B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpMemberCallback3B6(o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpSafeMemberCallback3B6() {} R dispatch(A1 a1, A2 a2, A3 a3) { if (valid()) { R r = XorpMemberCallback3B6::dispatch(a1, a2, a3); return r; } } }; /** * @short Callback object for void safe member methods with 3 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpSafeMemberCallback3B6 : public XorpMemberCallback3B6, public SafeCallbackBase { typedef typename XorpMemberCallback3B6::M M; XorpSafeMemberCallback3B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpMemberCallback3B6(o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpSafeMemberCallback3B6() {} void dispatch(A1 a1, A2 a2, A3 a3) { if (valid()) { XorpMemberCallback3B6::dispatch(a1, a2, a3); } } }; template struct XorpMemberCallbackFactory3B6 { static XorpMemberCallback3B6* make(O* o, R (O::*p)(A1, A2, A3, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpSafeMemberCallback3B6(o, p, ba1, ba2, ba3, ba4, ba5, ba6); } }; template struct XorpMemberCallbackFactory3B6 { static XorpMemberCallback3B6* make(O* o, R (O::*p)(A1, A2, A3, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpMemberCallback3B6(o, p, ba1, ba2, ba3, ba4, ba5, ba6); }; }; /** * Factory function that creates a callback object targetted at a * member function with 3 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback3::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpMemberCallbackFactory3B6::True>::make(o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * Factory function that creates a callback object targetted at a * member function with 3 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback3::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpMemberCallbackFactory3B6::True>::make(&o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * @short Callback object for const member methods with 3 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstMemberCallback3B6 : public XorpCallback3 { typedef R (O::*M)(A1, A2, A3, BA1, BA2, BA3, BA4, BA5, BA6) const; XorpConstMemberCallback3B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback3(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3) { R r = ((*_o).*_m)(a1, a2, a3, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for void const member methods with 3 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstMemberCallback3B6 : public XorpCallback3 { typedef void (O::*M)(A1, A2, A3, BA1, BA2, BA3, BA4, BA5, BA6) const; XorpConstMemberCallback3B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback3(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3) { ((*_o).*_m)(a1, a2, a3, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for const safe member methods with 3 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback3B6 : public XorpConstMemberCallback3B6, public SafeCallbackBase { typedef typename XorpConstMemberCallback3B6::M M; XorpConstSafeMemberCallback3B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpConstMemberCallback3B6(o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback3B6() {} R dispatch(A1 a1, A2 a2, A3 a3) { if (valid()) { R r = XorpConstMemberCallback3B6::dispatch(a1, a2, a3); return r; } } }; /** * @short Callback object for void const safe member methods with 3 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback3B6 : public XorpConstMemberCallback3B6, public SafeCallbackBase { typedef typename XorpConstMemberCallback3B6::M M; XorpConstSafeMemberCallback3B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpConstMemberCallback3B6(o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback3B6() {} void dispatch(A1 a1, A2 a2, A3 a3) { if (valid()) { XorpConstMemberCallback3B6::dispatch(a1, a2, a3); } } }; template struct XorpConstMemberCallbackFactory3B6 { static XorpConstMemberCallback3B6* make(O* o, R (O::*p)(A1, A2, A3, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpConstSafeMemberCallback3B6(o, p, ba1, ba2, ba3, ba4, ba5, ba6); } }; template struct XorpConstMemberCallbackFactory3B6 { static XorpConstMemberCallback3B6* make(O* o, R (O::*p)(A1, A2, A3, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpConstMemberCallback3B6(o, p, ba1, ba2, ba3, ba4, ba5, ba6); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 3 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback3::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpConstMemberCallbackFactory3B6::True>::make(o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * Factory function that creates a callback object targetted at a * const member function with 3 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback3::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpConstMemberCallbackFactory3B6::True>::make(&o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /////////////////////////////////////////////////////////////////////////////// // // Code relating to callbacks with 4 late args // /** * @short Base class for callbacks with 4 dispatch time args. */ template struct XorpCallback4 { typedef ref_ptr RefPtr; virtual ~XorpCallback4() {} virtual R dispatch(A1, A2, A3, A4) = 0; }; /** * @short Callback object for functions with 4 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpFunctionCallback4B0 : public XorpCallback4 { typedef R (*F)(A1, A2, A3, A4); XorpFunctionCallback4B0(F f) : XorpCallback4(), _f(f) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { R r = (*_f)(a1, a2, a3, a4); return r; } protected: F _f; }; /** * @short Callback object for void functions with 4 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpFunctionCallback4B0 : public XorpCallback4 { typedef void (*F)(A1, A2, A3, A4); XorpFunctionCallback4B0(F f) : XorpCallback4(), _f(f) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { (*_f)(a1, a2, a3, a4); } protected: F _f; }; /** * Factory function that creates a callback object targetted at a * function with 4 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback4::RefPtr callback(R (*f)(A1, A2, A3, A4)) { return typename XorpCallback4::RefPtr(new XorpFunctionCallback4B0(f)); } /** * @short Callback object for member methods with 4 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpMemberCallback4B0 : public XorpCallback4 { typedef R (O::*M)(A1, A2, A3, A4) ; XorpMemberCallback4B0(O* o, M m) : XorpCallback4(), _o(o), _m(m) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { R r = ((*_o).*_m)(a1, a2, a3, a4); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for void member methods with 4 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpMemberCallback4B0 : public XorpCallback4 { typedef void (O::*M)(A1, A2, A3, A4) ; XorpMemberCallback4B0(O* o, M m) : XorpCallback4(), _o(o), _m(m) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { ((*_o).*_m)(a1, a2, a3, a4); } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for safe member methods with 4 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpSafeMemberCallback4B0 : public XorpMemberCallback4B0, public SafeCallbackBase { typedef typename XorpMemberCallback4B0::M M; XorpSafeMemberCallback4B0(O* o, M m) : XorpMemberCallback4B0(o, m), SafeCallbackBase(o) {} ~XorpSafeMemberCallback4B0() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { if (valid()) { R r = XorpMemberCallback4B0::dispatch(a1, a2, a3, a4); return r; } } }; /** * @short Callback object for void safe member methods with 4 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpSafeMemberCallback4B0 : public XorpMemberCallback4B0, public SafeCallbackBase { typedef typename XorpMemberCallback4B0::M M; XorpSafeMemberCallback4B0(O* o, M m) : XorpMemberCallback4B0(o, m), SafeCallbackBase(o) {} ~XorpSafeMemberCallback4B0() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { if (valid()) { XorpMemberCallback4B0::dispatch(a1, a2, a3, a4); } } }; template struct XorpMemberCallbackFactory4B0 { static XorpMemberCallback4B0* make(O* o, R (O::*p)(A1, A2, A3, A4)) { return new XorpSafeMemberCallback4B0(o, p); } }; template struct XorpMemberCallbackFactory4B0 { static XorpMemberCallback4B0* make(O* o, R (O::*p)(A1, A2, A3, A4)) { return new XorpMemberCallback4B0(o, p); }; }; /** * Factory function that creates a callback object targetted at a * member function with 4 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback4::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4)) { return XorpMemberCallbackFactory4B0::True>::make(o, p); } /** * Factory function that creates a callback object targetted at a * member function with 4 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback4::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4)) { return XorpMemberCallbackFactory4B0::True>::make(&o, p); } /** * @short Callback object for const member methods with 4 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstMemberCallback4B0 : public XorpCallback4 { typedef R (O::*M)(A1, A2, A3, A4) const; XorpConstMemberCallback4B0(O* o, M m) : XorpCallback4(), _o(o), _m(m) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { R r = ((*_o).*_m)(a1, a2, a3, a4); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for void const member methods with 4 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstMemberCallback4B0 : public XorpCallback4 { typedef void (O::*M)(A1, A2, A3, A4) const; XorpConstMemberCallback4B0(O* o, M m) : XorpCallback4(), _o(o), _m(m) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { ((*_o).*_m)(a1, a2, a3, a4); } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for const safe member methods with 4 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback4B0 : public XorpConstMemberCallback4B0, public SafeCallbackBase { typedef typename XorpConstMemberCallback4B0::M M; XorpConstSafeMemberCallback4B0(O* o, M m) : XorpConstMemberCallback4B0(o, m), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback4B0() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { if (valid()) { R r = XorpConstMemberCallback4B0::dispatch(a1, a2, a3, a4); return r; } } }; /** * @short Callback object for void const safe member methods with 4 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback4B0 : public XorpConstMemberCallback4B0, public SafeCallbackBase { typedef typename XorpConstMemberCallback4B0::M M; XorpConstSafeMemberCallback4B0(O* o, M m) : XorpConstMemberCallback4B0(o, m), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback4B0() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { if (valid()) { XorpConstMemberCallback4B0::dispatch(a1, a2, a3, a4); } } }; template struct XorpConstMemberCallbackFactory4B0 { static XorpConstMemberCallback4B0* make(O* o, R (O::*p)(A1, A2, A3, A4) const) { return new XorpConstSafeMemberCallback4B0(o, p); } }; template struct XorpConstMemberCallbackFactory4B0 { static XorpConstMemberCallback4B0* make(O* o, R (O::*p)(A1, A2, A3, A4) const) { return new XorpConstMemberCallback4B0(o, p); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 4 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback4::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4) const) { return XorpConstMemberCallbackFactory4B0::True>::make(o, p); } /** * Factory function that creates a callback object targetted at a * const member function with 4 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback4::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4) const) { return XorpConstMemberCallbackFactory4B0::True>::make(&o, p); } /** * @short Callback object for functions with 4 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpFunctionCallback4B1 : public XorpCallback4 { typedef R (*F)(A1, A2, A3, A4, BA1); XorpFunctionCallback4B1(F f, BA1 ba1) : XorpCallback4(), _f(f), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { R r = (*_f)(a1, a2, a3, a4, _ba1); return r; } protected: F _f; BA1 _ba1; }; /** * @short Callback object for void functions with 4 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpFunctionCallback4B1 : public XorpCallback4 { typedef void (*F)(A1, A2, A3, A4, BA1); XorpFunctionCallback4B1(F f, BA1 ba1) : XorpCallback4(), _f(f), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { (*_f)(a1, a2, a3, a4, _ba1); } protected: F _f; BA1 _ba1; }; /** * Factory function that creates a callback object targetted at a * function with 4 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback4::RefPtr callback(R (*f)(A1, A2, A3, A4, BA1), BA1 ba1) { return typename XorpCallback4::RefPtr(new XorpFunctionCallback4B1(f, ba1)); } /** * @short Callback object for member methods with 4 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpMemberCallback4B1 : public XorpCallback4 { typedef R (O::*M)(A1, A2, A3, A4, BA1) ; XorpMemberCallback4B1(O* o, M m, BA1 ba1) : XorpCallback4(), _o(o), _m(m), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { R r = ((*_o).*_m)(a1, a2, a3, a4, _ba1); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for void member methods with 4 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpMemberCallback4B1 : public XorpCallback4 { typedef void (O::*M)(A1, A2, A3, A4, BA1) ; XorpMemberCallback4B1(O* o, M m, BA1 ba1) : XorpCallback4(), _o(o), _m(m), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { ((*_o).*_m)(a1, a2, a3, a4, _ba1); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for safe member methods with 4 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpSafeMemberCallback4B1 : public XorpMemberCallback4B1, public SafeCallbackBase { typedef typename XorpMemberCallback4B1::M M; XorpSafeMemberCallback4B1(O* o, M m, BA1 ba1) : XorpMemberCallback4B1(o, m, ba1), SafeCallbackBase(o) {} ~XorpSafeMemberCallback4B1() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { if (valid()) { R r = XorpMemberCallback4B1::dispatch(a1, a2, a3, a4); return r; } } }; /** * @short Callback object for void safe member methods with 4 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpSafeMemberCallback4B1 : public XorpMemberCallback4B1, public SafeCallbackBase { typedef typename XorpMemberCallback4B1::M M; XorpSafeMemberCallback4B1(O* o, M m, BA1 ba1) : XorpMemberCallback4B1(o, m, ba1), SafeCallbackBase(o) {} ~XorpSafeMemberCallback4B1() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { if (valid()) { XorpMemberCallback4B1::dispatch(a1, a2, a3, a4); } } }; template struct XorpMemberCallbackFactory4B1 { static XorpMemberCallback4B1* make(O* o, R (O::*p)(A1, A2, A3, A4, BA1), BA1 ba1) { return new XorpSafeMemberCallback4B1(o, p, ba1); } }; template struct XorpMemberCallbackFactory4B1 { static XorpMemberCallback4B1* make(O* o, R (O::*p)(A1, A2, A3, A4, BA1), BA1 ba1) { return new XorpMemberCallback4B1(o, p, ba1); }; }; /** * Factory function that creates a callback object targetted at a * member function with 4 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback4::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, BA1), BA1 ba1) { return XorpMemberCallbackFactory4B1::True>::make(o, p, ba1); } /** * Factory function that creates a callback object targetted at a * member function with 4 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback4::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, BA1), BA1 ba1) { return XorpMemberCallbackFactory4B1::True>::make(&o, p, ba1); } /** * @short Callback object for const member methods with 4 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstMemberCallback4B1 : public XorpCallback4 { typedef R (O::*M)(A1, A2, A3, A4, BA1) const; XorpConstMemberCallback4B1(O* o, M m, BA1 ba1) : XorpCallback4(), _o(o), _m(m), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { R r = ((*_o).*_m)(a1, a2, a3, a4, _ba1); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for void const member methods with 4 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstMemberCallback4B1 : public XorpCallback4 { typedef void (O::*M)(A1, A2, A3, A4, BA1) const; XorpConstMemberCallback4B1(O* o, M m, BA1 ba1) : XorpCallback4(), _o(o), _m(m), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { ((*_o).*_m)(a1, a2, a3, a4, _ba1); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for const safe member methods with 4 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback4B1 : public XorpConstMemberCallback4B1, public SafeCallbackBase { typedef typename XorpConstMemberCallback4B1::M M; XorpConstSafeMemberCallback4B1(O* o, M m, BA1 ba1) : XorpConstMemberCallback4B1(o, m, ba1), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback4B1() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { if (valid()) { R r = XorpConstMemberCallback4B1::dispatch(a1, a2, a3, a4); return r; } } }; /** * @short Callback object for void const safe member methods with 4 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback4B1 : public XorpConstMemberCallback4B1, public SafeCallbackBase { typedef typename XorpConstMemberCallback4B1::M M; XorpConstSafeMemberCallback4B1(O* o, M m, BA1 ba1) : XorpConstMemberCallback4B1(o, m, ba1), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback4B1() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { if (valid()) { XorpConstMemberCallback4B1::dispatch(a1, a2, a3, a4); } } }; template struct XorpConstMemberCallbackFactory4B1 { static XorpConstMemberCallback4B1* make(O* o, R (O::*p)(A1, A2, A3, A4, BA1) const, BA1 ba1) { return new XorpConstSafeMemberCallback4B1(o, p, ba1); } }; template struct XorpConstMemberCallbackFactory4B1 { static XorpConstMemberCallback4B1* make(O* o, R (O::*p)(A1, A2, A3, A4, BA1) const, BA1 ba1) { return new XorpConstMemberCallback4B1(o, p, ba1); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 4 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback4::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, BA1) const, BA1 ba1) { return XorpConstMemberCallbackFactory4B1::True>::make(o, p, ba1); } /** * Factory function that creates a callback object targetted at a * const member function with 4 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback4::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, BA1) const, BA1 ba1) { return XorpConstMemberCallbackFactory4B1::True>::make(&o, p, ba1); } /** * @short Callback object for functions with 4 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpFunctionCallback4B2 : public XorpCallback4 { typedef R (*F)(A1, A2, A3, A4, BA1, BA2); XorpFunctionCallback4B2(F f, BA1 ba1, BA2 ba2) : XorpCallback4(), _f(f), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { R r = (*_f)(a1, a2, a3, a4, _ba1, _ba2); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; }; /** * @short Callback object for void functions with 4 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpFunctionCallback4B2 : public XorpCallback4 { typedef void (*F)(A1, A2, A3, A4, BA1, BA2); XorpFunctionCallback4B2(F f, BA1 ba1, BA2 ba2) : XorpCallback4(), _f(f), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { (*_f)(a1, a2, a3, a4, _ba1, _ba2); } protected: F _f; BA1 _ba1; BA2 _ba2; }; /** * Factory function that creates a callback object targetted at a * function with 4 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback4::RefPtr callback(R (*f)(A1, A2, A3, A4, BA1, BA2), BA1 ba1, BA2 ba2) { return typename XorpCallback4::RefPtr(new XorpFunctionCallback4B2(f, ba1, ba2)); } /** * @short Callback object for member methods with 4 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpMemberCallback4B2 : public XorpCallback4 { typedef R (O::*M)(A1, A2, A3, A4, BA1, BA2) ; XorpMemberCallback4B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback4(), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { R r = ((*_o).*_m)(a1, a2, a3, a4, _ba1, _ba2); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for void member methods with 4 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpMemberCallback4B2 : public XorpCallback4 { typedef void (O::*M)(A1, A2, A3, A4, BA1, BA2) ; XorpMemberCallback4B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback4(), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { ((*_o).*_m)(a1, a2, a3, a4, _ba1, _ba2); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for safe member methods with 4 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpSafeMemberCallback4B2 : public XorpMemberCallback4B2, public SafeCallbackBase { typedef typename XorpMemberCallback4B2::M M; XorpSafeMemberCallback4B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpMemberCallback4B2(o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpSafeMemberCallback4B2() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { if (valid()) { R r = XorpMemberCallback4B2::dispatch(a1, a2, a3, a4); return r; } } }; /** * @short Callback object for void safe member methods with 4 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpSafeMemberCallback4B2 : public XorpMemberCallback4B2, public SafeCallbackBase { typedef typename XorpMemberCallback4B2::M M; XorpSafeMemberCallback4B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpMemberCallback4B2(o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpSafeMemberCallback4B2() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { if (valid()) { XorpMemberCallback4B2::dispatch(a1, a2, a3, a4); } } }; template struct XorpMemberCallbackFactory4B2 { static XorpMemberCallback4B2* make(O* o, R (O::*p)(A1, A2, A3, A4, BA1, BA2), BA1 ba1, BA2 ba2) { return new XorpSafeMemberCallback4B2(o, p, ba1, ba2); } }; template struct XorpMemberCallbackFactory4B2 { static XorpMemberCallback4B2* make(O* o, R (O::*p)(A1, A2, A3, A4, BA1, BA2), BA1 ba1, BA2 ba2) { return new XorpMemberCallback4B2(o, p, ba1, ba2); }; }; /** * Factory function that creates a callback object targetted at a * member function with 4 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback4::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, BA1, BA2), BA1 ba1, BA2 ba2) { return XorpMemberCallbackFactory4B2::True>::make(o, p, ba1, ba2); } /** * Factory function that creates a callback object targetted at a * member function with 4 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback4::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, BA1, BA2), BA1 ba1, BA2 ba2) { return XorpMemberCallbackFactory4B2::True>::make(&o, p, ba1, ba2); } /** * @short Callback object for const member methods with 4 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstMemberCallback4B2 : public XorpCallback4 { typedef R (O::*M)(A1, A2, A3, A4, BA1, BA2) const; XorpConstMemberCallback4B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback4(), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { R r = ((*_o).*_m)(a1, a2, a3, a4, _ba1, _ba2); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for void const member methods with 4 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstMemberCallback4B2 : public XorpCallback4 { typedef void (O::*M)(A1, A2, A3, A4, BA1, BA2) const; XorpConstMemberCallback4B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback4(), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { ((*_o).*_m)(a1, a2, a3, a4, _ba1, _ba2); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for const safe member methods with 4 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback4B2 : public XorpConstMemberCallback4B2, public SafeCallbackBase { typedef typename XorpConstMemberCallback4B2::M M; XorpConstSafeMemberCallback4B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpConstMemberCallback4B2(o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback4B2() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { if (valid()) { R r = XorpConstMemberCallback4B2::dispatch(a1, a2, a3, a4); return r; } } }; /** * @short Callback object for void const safe member methods with 4 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback4B2 : public XorpConstMemberCallback4B2, public SafeCallbackBase { typedef typename XorpConstMemberCallback4B2::M M; XorpConstSafeMemberCallback4B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpConstMemberCallback4B2(o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback4B2() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { if (valid()) { XorpConstMemberCallback4B2::dispatch(a1, a2, a3, a4); } } }; template struct XorpConstMemberCallbackFactory4B2 { static XorpConstMemberCallback4B2* make(O* o, R (O::*p)(A1, A2, A3, A4, BA1, BA2) const, BA1 ba1, BA2 ba2) { return new XorpConstSafeMemberCallback4B2(o, p, ba1, ba2); } }; template struct XorpConstMemberCallbackFactory4B2 { static XorpConstMemberCallback4B2* make(O* o, R (O::*p)(A1, A2, A3, A4, BA1, BA2) const, BA1 ba1, BA2 ba2) { return new XorpConstMemberCallback4B2(o, p, ba1, ba2); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 4 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback4::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, BA1, BA2) const, BA1 ba1, BA2 ba2) { return XorpConstMemberCallbackFactory4B2::True>::make(o, p, ba1, ba2); } /** * Factory function that creates a callback object targetted at a * const member function with 4 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback4::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, BA1, BA2) const, BA1 ba1, BA2 ba2) { return XorpConstMemberCallbackFactory4B2::True>::make(&o, p, ba1, ba2); } /** * @short Callback object for functions with 4 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpFunctionCallback4B3 : public XorpCallback4 { typedef R (*F)(A1, A2, A3, A4, BA1, BA2, BA3); XorpFunctionCallback4B3(F f, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback4(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { R r = (*_f)(a1, a2, a3, a4, _ba1, _ba2, _ba3); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; }; /** * @short Callback object for void functions with 4 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpFunctionCallback4B3 : public XorpCallback4 { typedef void (*F)(A1, A2, A3, A4, BA1, BA2, BA3); XorpFunctionCallback4B3(F f, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback4(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { (*_f)(a1, a2, a3, a4, _ba1, _ba2, _ba3); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; }; /** * Factory function that creates a callback object targetted at a * function with 4 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback4::RefPtr callback(R (*f)(A1, A2, A3, A4, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return typename XorpCallback4::RefPtr(new XorpFunctionCallback4B3(f, ba1, ba2, ba3)); } /** * @short Callback object for member methods with 4 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpMemberCallback4B3 : public XorpCallback4 { typedef R (O::*M)(A1, A2, A3, A4, BA1, BA2, BA3) ; XorpMemberCallback4B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback4(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { R r = ((*_o).*_m)(a1, a2, a3, a4, _ba1, _ba2, _ba3); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for void member methods with 4 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpMemberCallback4B3 : public XorpCallback4 { typedef void (O::*M)(A1, A2, A3, A4, BA1, BA2, BA3) ; XorpMemberCallback4B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback4(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { ((*_o).*_m)(a1, a2, a3, a4, _ba1, _ba2, _ba3); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for safe member methods with 4 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpSafeMemberCallback4B3 : public XorpMemberCallback4B3, public SafeCallbackBase { typedef typename XorpMemberCallback4B3::M M; XorpSafeMemberCallback4B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpMemberCallback4B3(o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpSafeMemberCallback4B3() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { if (valid()) { R r = XorpMemberCallback4B3::dispatch(a1, a2, a3, a4); return r; } } }; /** * @short Callback object for void safe member methods with 4 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpSafeMemberCallback4B3 : public XorpMemberCallback4B3, public SafeCallbackBase { typedef typename XorpMemberCallback4B3::M M; XorpSafeMemberCallback4B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpMemberCallback4B3(o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpSafeMemberCallback4B3() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { if (valid()) { XorpMemberCallback4B3::dispatch(a1, a2, a3, a4); } } }; template struct XorpMemberCallbackFactory4B3 { static XorpMemberCallback4B3* make(O* o, R (O::*p)(A1, A2, A3, A4, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpSafeMemberCallback4B3(o, p, ba1, ba2, ba3); } }; template struct XorpMemberCallbackFactory4B3 { static XorpMemberCallback4B3* make(O* o, R (O::*p)(A1, A2, A3, A4, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpMemberCallback4B3(o, p, ba1, ba2, ba3); }; }; /** * Factory function that creates a callback object targetted at a * member function with 4 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback4::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return XorpMemberCallbackFactory4B3::True>::make(o, p, ba1, ba2, ba3); } /** * Factory function that creates a callback object targetted at a * member function with 4 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback4::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return XorpMemberCallbackFactory4B3::True>::make(&o, p, ba1, ba2, ba3); } /** * @short Callback object for const member methods with 4 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstMemberCallback4B3 : public XorpCallback4 { typedef R (O::*M)(A1, A2, A3, A4, BA1, BA2, BA3) const; XorpConstMemberCallback4B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback4(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { R r = ((*_o).*_m)(a1, a2, a3, a4, _ba1, _ba2, _ba3); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for void const member methods with 4 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstMemberCallback4B3 : public XorpCallback4 { typedef void (O::*M)(A1, A2, A3, A4, BA1, BA2, BA3) const; XorpConstMemberCallback4B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback4(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { ((*_o).*_m)(a1, a2, a3, a4, _ba1, _ba2, _ba3); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for const safe member methods with 4 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback4B3 : public XorpConstMemberCallback4B3, public SafeCallbackBase { typedef typename XorpConstMemberCallback4B3::M M; XorpConstSafeMemberCallback4B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpConstMemberCallback4B3(o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback4B3() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { if (valid()) { R r = XorpConstMemberCallback4B3::dispatch(a1, a2, a3, a4); return r; } } }; /** * @short Callback object for void const safe member methods with 4 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback4B3 : public XorpConstMemberCallback4B3, public SafeCallbackBase { typedef typename XorpConstMemberCallback4B3::M M; XorpConstSafeMemberCallback4B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpConstMemberCallback4B3(o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback4B3() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { if (valid()) { XorpConstMemberCallback4B3::dispatch(a1, a2, a3, a4); } } }; template struct XorpConstMemberCallbackFactory4B3 { static XorpConstMemberCallback4B3* make(O* o, R (O::*p)(A1, A2, A3, A4, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpConstSafeMemberCallback4B3(o, p, ba1, ba2, ba3); } }; template struct XorpConstMemberCallbackFactory4B3 { static XorpConstMemberCallback4B3* make(O* o, R (O::*p)(A1, A2, A3, A4, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpConstMemberCallback4B3(o, p, ba1, ba2, ba3); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 4 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback4::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return XorpConstMemberCallbackFactory4B3::True>::make(o, p, ba1, ba2, ba3); } /** * Factory function that creates a callback object targetted at a * const member function with 4 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback4::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return XorpConstMemberCallbackFactory4B3::True>::make(&o, p, ba1, ba2, ba3); } /** * @short Callback object for functions with 4 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpFunctionCallback4B4 : public XorpCallback4 { typedef R (*F)(A1, A2, A3, A4, BA1, BA2, BA3, BA4); XorpFunctionCallback4B4(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback4(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { R r = (*_f)(a1, a2, a3, a4, _ba1, _ba2, _ba3, _ba4); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; }; /** * @short Callback object for void functions with 4 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpFunctionCallback4B4 : public XorpCallback4 { typedef void (*F)(A1, A2, A3, A4, BA1, BA2, BA3, BA4); XorpFunctionCallback4B4(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback4(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { (*_f)(a1, a2, a3, a4, _ba1, _ba2, _ba3, _ba4); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; }; /** * Factory function that creates a callback object targetted at a * function with 4 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback4::RefPtr callback(R (*f)(A1, A2, A3, A4, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return typename XorpCallback4::RefPtr(new XorpFunctionCallback4B4(f, ba1, ba2, ba3, ba4)); } /** * @short Callback object for member methods with 4 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpMemberCallback4B4 : public XorpCallback4 { typedef R (O::*M)(A1, A2, A3, A4, BA1, BA2, BA3, BA4) ; XorpMemberCallback4B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback4(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { R r = ((*_o).*_m)(a1, a2, a3, a4, _ba1, _ba2, _ba3, _ba4); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for void member methods with 4 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpMemberCallback4B4 : public XorpCallback4 { typedef void (O::*M)(A1, A2, A3, A4, BA1, BA2, BA3, BA4) ; XorpMemberCallback4B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback4(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { ((*_o).*_m)(a1, a2, a3, a4, _ba1, _ba2, _ba3, _ba4); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for safe member methods with 4 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpSafeMemberCallback4B4 : public XorpMemberCallback4B4, public SafeCallbackBase { typedef typename XorpMemberCallback4B4::M M; XorpSafeMemberCallback4B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpMemberCallback4B4(o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpSafeMemberCallback4B4() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { if (valid()) { R r = XorpMemberCallback4B4::dispatch(a1, a2, a3, a4); return r; } } }; /** * @short Callback object for void safe member methods with 4 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpSafeMemberCallback4B4 : public XorpMemberCallback4B4, public SafeCallbackBase { typedef typename XorpMemberCallback4B4::M M; XorpSafeMemberCallback4B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpMemberCallback4B4(o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpSafeMemberCallback4B4() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { if (valid()) { XorpMemberCallback4B4::dispatch(a1, a2, a3, a4); } } }; template struct XorpMemberCallbackFactory4B4 { static XorpMemberCallback4B4* make(O* o, R (O::*p)(A1, A2, A3, A4, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpSafeMemberCallback4B4(o, p, ba1, ba2, ba3, ba4); } }; template struct XorpMemberCallbackFactory4B4 { static XorpMemberCallback4B4* make(O* o, R (O::*p)(A1, A2, A3, A4, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpMemberCallback4B4(o, p, ba1, ba2, ba3, ba4); }; }; /** * Factory function that creates a callback object targetted at a * member function with 4 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback4::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpMemberCallbackFactory4B4::True>::make(o, p, ba1, ba2, ba3, ba4); } /** * Factory function that creates a callback object targetted at a * member function with 4 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback4::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpMemberCallbackFactory4B4::True>::make(&o, p, ba1, ba2, ba3, ba4); } /** * @short Callback object for const member methods with 4 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstMemberCallback4B4 : public XorpCallback4 { typedef R (O::*M)(A1, A2, A3, A4, BA1, BA2, BA3, BA4) const; XorpConstMemberCallback4B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback4(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { R r = ((*_o).*_m)(a1, a2, a3, a4, _ba1, _ba2, _ba3, _ba4); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for void const member methods with 4 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstMemberCallback4B4 : public XorpCallback4 { typedef void (O::*M)(A1, A2, A3, A4, BA1, BA2, BA3, BA4) const; XorpConstMemberCallback4B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback4(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { ((*_o).*_m)(a1, a2, a3, a4, _ba1, _ba2, _ba3, _ba4); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for const safe member methods with 4 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback4B4 : public XorpConstMemberCallback4B4, public SafeCallbackBase { typedef typename XorpConstMemberCallback4B4::M M; XorpConstSafeMemberCallback4B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpConstMemberCallback4B4(o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback4B4() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { if (valid()) { R r = XorpConstMemberCallback4B4::dispatch(a1, a2, a3, a4); return r; } } }; /** * @short Callback object for void const safe member methods with 4 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback4B4 : public XorpConstMemberCallback4B4, public SafeCallbackBase { typedef typename XorpConstMemberCallback4B4::M M; XorpConstSafeMemberCallback4B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpConstMemberCallback4B4(o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback4B4() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { if (valid()) { XorpConstMemberCallback4B4::dispatch(a1, a2, a3, a4); } } }; template struct XorpConstMemberCallbackFactory4B4 { static XorpConstMemberCallback4B4* make(O* o, R (O::*p)(A1, A2, A3, A4, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpConstSafeMemberCallback4B4(o, p, ba1, ba2, ba3, ba4); } }; template struct XorpConstMemberCallbackFactory4B4 { static XorpConstMemberCallback4B4* make(O* o, R (O::*p)(A1, A2, A3, A4, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpConstMemberCallback4B4(o, p, ba1, ba2, ba3, ba4); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 4 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback4::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpConstMemberCallbackFactory4B4::True>::make(o, p, ba1, ba2, ba3, ba4); } /** * Factory function that creates a callback object targetted at a * const member function with 4 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback4::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpConstMemberCallbackFactory4B4::True>::make(&o, p, ba1, ba2, ba3, ba4); } /** * @short Callback object for functions with 4 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpFunctionCallback4B5 : public XorpCallback4 { typedef R (*F)(A1, A2, A3, A4, BA1, BA2, BA3, BA4, BA5); XorpFunctionCallback4B5(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback4(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { R r = (*_f)(a1, a2, a3, a4, _ba1, _ba2, _ba3, _ba4, _ba5); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; }; /** * @short Callback object for void functions with 4 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpFunctionCallback4B5 : public XorpCallback4 { typedef void (*F)(A1, A2, A3, A4, BA1, BA2, BA3, BA4, BA5); XorpFunctionCallback4B5(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback4(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { (*_f)(a1, a2, a3, a4, _ba1, _ba2, _ba3, _ba4, _ba5); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; }; /** * Factory function that creates a callback object targetted at a * function with 4 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback4::RefPtr callback(R (*f)(A1, A2, A3, A4, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return typename XorpCallback4::RefPtr(new XorpFunctionCallback4B5(f, ba1, ba2, ba3, ba4, ba5)); } /** * @short Callback object for member methods with 4 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpMemberCallback4B5 : public XorpCallback4 { typedef R (O::*M)(A1, A2, A3, A4, BA1, BA2, BA3, BA4, BA5) ; XorpMemberCallback4B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback4(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { R r = ((*_o).*_m)(a1, a2, a3, a4, _ba1, _ba2, _ba3, _ba4, _ba5); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for void member methods with 4 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpMemberCallback4B5 : public XorpCallback4 { typedef void (O::*M)(A1, A2, A3, A4, BA1, BA2, BA3, BA4, BA5) ; XorpMemberCallback4B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback4(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { ((*_o).*_m)(a1, a2, a3, a4, _ba1, _ba2, _ba3, _ba4, _ba5); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for safe member methods with 4 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpSafeMemberCallback4B5 : public XorpMemberCallback4B5, public SafeCallbackBase { typedef typename XorpMemberCallback4B5::M M; XorpSafeMemberCallback4B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpMemberCallback4B5(o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpSafeMemberCallback4B5() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { if (valid()) { R r = XorpMemberCallback4B5::dispatch(a1, a2, a3, a4); return r; } } }; /** * @short Callback object for void safe member methods with 4 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpSafeMemberCallback4B5 : public XorpMemberCallback4B5, public SafeCallbackBase { typedef typename XorpMemberCallback4B5::M M; XorpSafeMemberCallback4B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpMemberCallback4B5(o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpSafeMemberCallback4B5() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { if (valid()) { XorpMemberCallback4B5::dispatch(a1, a2, a3, a4); } } }; template struct XorpMemberCallbackFactory4B5 { static XorpMemberCallback4B5* make(O* o, R (O::*p)(A1, A2, A3, A4, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpSafeMemberCallback4B5(o, p, ba1, ba2, ba3, ba4, ba5); } }; template struct XorpMemberCallbackFactory4B5 { static XorpMemberCallback4B5* make(O* o, R (O::*p)(A1, A2, A3, A4, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpMemberCallback4B5(o, p, ba1, ba2, ba3, ba4, ba5); }; }; /** * Factory function that creates a callback object targetted at a * member function with 4 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback4::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpMemberCallbackFactory4B5::True>::make(o, p, ba1, ba2, ba3, ba4, ba5); } /** * Factory function that creates a callback object targetted at a * member function with 4 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback4::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpMemberCallbackFactory4B5::True>::make(&o, p, ba1, ba2, ba3, ba4, ba5); } /** * @short Callback object for const member methods with 4 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstMemberCallback4B5 : public XorpCallback4 { typedef R (O::*M)(A1, A2, A3, A4, BA1, BA2, BA3, BA4, BA5) const; XorpConstMemberCallback4B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback4(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { R r = ((*_o).*_m)(a1, a2, a3, a4, _ba1, _ba2, _ba3, _ba4, _ba5); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for void const member methods with 4 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstMemberCallback4B5 : public XorpCallback4 { typedef void (O::*M)(A1, A2, A3, A4, BA1, BA2, BA3, BA4, BA5) const; XorpConstMemberCallback4B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback4(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { ((*_o).*_m)(a1, a2, a3, a4, _ba1, _ba2, _ba3, _ba4, _ba5); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for const safe member methods with 4 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback4B5 : public XorpConstMemberCallback4B5, public SafeCallbackBase { typedef typename XorpConstMemberCallback4B5::M M; XorpConstSafeMemberCallback4B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpConstMemberCallback4B5(o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback4B5() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { if (valid()) { R r = XorpConstMemberCallback4B5::dispatch(a1, a2, a3, a4); return r; } } }; /** * @short Callback object for void const safe member methods with 4 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback4B5 : public XorpConstMemberCallback4B5, public SafeCallbackBase { typedef typename XorpConstMemberCallback4B5::M M; XorpConstSafeMemberCallback4B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpConstMemberCallback4B5(o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback4B5() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { if (valid()) { XorpConstMemberCallback4B5::dispatch(a1, a2, a3, a4); } } }; template struct XorpConstMemberCallbackFactory4B5 { static XorpConstMemberCallback4B5* make(O* o, R (O::*p)(A1, A2, A3, A4, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpConstSafeMemberCallback4B5(o, p, ba1, ba2, ba3, ba4, ba5); } }; template struct XorpConstMemberCallbackFactory4B5 { static XorpConstMemberCallback4B5* make(O* o, R (O::*p)(A1, A2, A3, A4, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpConstMemberCallback4B5(o, p, ba1, ba2, ba3, ba4, ba5); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 4 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback4::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpConstMemberCallbackFactory4B5::True>::make(o, p, ba1, ba2, ba3, ba4, ba5); } /** * Factory function that creates a callback object targetted at a * const member function with 4 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback4::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpConstMemberCallbackFactory4B5::True>::make(&o, p, ba1, ba2, ba3, ba4, ba5); } /** * @short Callback object for functions with 4 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpFunctionCallback4B6 : public XorpCallback4 { typedef R (*F)(A1, A2, A3, A4, BA1, BA2, BA3, BA4, BA5, BA6); XorpFunctionCallback4B6(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback4(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { R r = (*_f)(a1, a2, a3, a4, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; BA6 _ba6; }; /** * @short Callback object for void functions with 4 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpFunctionCallback4B6 : public XorpCallback4 { typedef void (*F)(A1, A2, A3, A4, BA1, BA2, BA3, BA4, BA5, BA6); XorpFunctionCallback4B6(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback4(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { (*_f)(a1, a2, a3, a4, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; BA6 _ba6; }; /** * Factory function that creates a callback object targetted at a * function with 4 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback4::RefPtr callback(R (*f)(A1, A2, A3, A4, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return typename XorpCallback4::RefPtr(new XorpFunctionCallback4B6(f, ba1, ba2, ba3, ba4, ba5, ba6)); } /** * @short Callback object for member methods with 4 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpMemberCallback4B6 : public XorpCallback4 { typedef R (O::*M)(A1, A2, A3, A4, BA1, BA2, BA3, BA4, BA5, BA6) ; XorpMemberCallback4B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback4(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { R r = ((*_o).*_m)(a1, a2, a3, a4, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for void member methods with 4 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpMemberCallback4B6 : public XorpCallback4 { typedef void (O::*M)(A1, A2, A3, A4, BA1, BA2, BA3, BA4, BA5, BA6) ; XorpMemberCallback4B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback4(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { ((*_o).*_m)(a1, a2, a3, a4, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for safe member methods with 4 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpSafeMemberCallback4B6 : public XorpMemberCallback4B6, public SafeCallbackBase { typedef typename XorpMemberCallback4B6::M M; XorpSafeMemberCallback4B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpMemberCallback4B6(o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpSafeMemberCallback4B6() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { if (valid()) { R r = XorpMemberCallback4B6::dispatch(a1, a2, a3, a4); return r; } } }; /** * @short Callback object for void safe member methods with 4 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpSafeMemberCallback4B6 : public XorpMemberCallback4B6, public SafeCallbackBase { typedef typename XorpMemberCallback4B6::M M; XorpSafeMemberCallback4B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpMemberCallback4B6(o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpSafeMemberCallback4B6() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { if (valid()) { XorpMemberCallback4B6::dispatch(a1, a2, a3, a4); } } }; template struct XorpMemberCallbackFactory4B6 { static XorpMemberCallback4B6* make(O* o, R (O::*p)(A1, A2, A3, A4, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpSafeMemberCallback4B6(o, p, ba1, ba2, ba3, ba4, ba5, ba6); } }; template struct XorpMemberCallbackFactory4B6 { static XorpMemberCallback4B6* make(O* o, R (O::*p)(A1, A2, A3, A4, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpMemberCallback4B6(o, p, ba1, ba2, ba3, ba4, ba5, ba6); }; }; /** * Factory function that creates a callback object targetted at a * member function with 4 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback4::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpMemberCallbackFactory4B6::True>::make(o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * Factory function that creates a callback object targetted at a * member function with 4 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback4::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpMemberCallbackFactory4B6::True>::make(&o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * @short Callback object for const member methods with 4 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstMemberCallback4B6 : public XorpCallback4 { typedef R (O::*M)(A1, A2, A3, A4, BA1, BA2, BA3, BA4, BA5, BA6) const; XorpConstMemberCallback4B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback4(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { R r = ((*_o).*_m)(a1, a2, a3, a4, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for void const member methods with 4 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstMemberCallback4B6 : public XorpCallback4 { typedef void (O::*M)(A1, A2, A3, A4, BA1, BA2, BA3, BA4, BA5, BA6) const; XorpConstMemberCallback4B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback4(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { ((*_o).*_m)(a1, a2, a3, a4, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for const safe member methods with 4 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback4B6 : public XorpConstMemberCallback4B6, public SafeCallbackBase { typedef typename XorpConstMemberCallback4B6::M M; XorpConstSafeMemberCallback4B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpConstMemberCallback4B6(o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback4B6() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { if (valid()) { R r = XorpConstMemberCallback4B6::dispatch(a1, a2, a3, a4); return r; } } }; /** * @short Callback object for void const safe member methods with 4 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback4B6 : public XorpConstMemberCallback4B6, public SafeCallbackBase { typedef typename XorpConstMemberCallback4B6::M M; XorpConstSafeMemberCallback4B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpConstMemberCallback4B6(o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback4B6() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4) { if (valid()) { XorpConstMemberCallback4B6::dispatch(a1, a2, a3, a4); } } }; template struct XorpConstMemberCallbackFactory4B6 { static XorpConstMemberCallback4B6* make(O* o, R (O::*p)(A1, A2, A3, A4, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpConstSafeMemberCallback4B6(o, p, ba1, ba2, ba3, ba4, ba5, ba6); } }; template struct XorpConstMemberCallbackFactory4B6 { static XorpConstMemberCallback4B6* make(O* o, R (O::*p)(A1, A2, A3, A4, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpConstMemberCallback4B6(o, p, ba1, ba2, ba3, ba4, ba5, ba6); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 4 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback4::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpConstMemberCallbackFactory4B6::True>::make(o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * Factory function that creates a callback object targetted at a * const member function with 4 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback4::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpConstMemberCallbackFactory4B6::True>::make(&o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /////////////////////////////////////////////////////////////////////////////// // // Code relating to callbacks with 5 late args // /** * @short Base class for callbacks with 5 dispatch time args. */ template struct XorpCallback5 { typedef ref_ptr RefPtr; virtual ~XorpCallback5() {} virtual R dispatch(A1, A2, A3, A4, A5) = 0; }; /** * @short Callback object for functions with 5 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpFunctionCallback5B0 : public XorpCallback5 { typedef R (*F)(A1, A2, A3, A4, A5); XorpFunctionCallback5B0(F f) : XorpCallback5(), _f(f) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { R r = (*_f)(a1, a2, a3, a4, a5); return r; } protected: F _f; }; /** * @short Callback object for void functions with 5 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpFunctionCallback5B0 : public XorpCallback5 { typedef void (*F)(A1, A2, A3, A4, A5); XorpFunctionCallback5B0(F f) : XorpCallback5(), _f(f) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { (*_f)(a1, a2, a3, a4, a5); } protected: F _f; }; /** * Factory function that creates a callback object targetted at a * function with 5 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback5::RefPtr callback(R (*f)(A1, A2, A3, A4, A5)) { return typename XorpCallback5::RefPtr(new XorpFunctionCallback5B0(f)); } /** * @short Callback object for member methods with 5 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpMemberCallback5B0 : public XorpCallback5 { typedef R (O::*M)(A1, A2, A3, A4, A5) ; XorpMemberCallback5B0(O* o, M m) : XorpCallback5(), _o(o), _m(m) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for void member methods with 5 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpMemberCallback5B0 : public XorpCallback5 { typedef void (O::*M)(A1, A2, A3, A4, A5) ; XorpMemberCallback5B0(O* o, M m) : XorpCallback5(), _o(o), _m(m) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { ((*_o).*_m)(a1, a2, a3, a4, a5); } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for safe member methods with 5 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpSafeMemberCallback5B0 : public XorpMemberCallback5B0, public SafeCallbackBase { typedef typename XorpMemberCallback5B0::M M; XorpSafeMemberCallback5B0(O* o, M m) : XorpMemberCallback5B0(o, m), SafeCallbackBase(o) {} ~XorpSafeMemberCallback5B0() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { if (valid()) { R r = XorpMemberCallback5B0::dispatch(a1, a2, a3, a4, a5); return r; } } }; /** * @short Callback object for void safe member methods with 5 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpSafeMemberCallback5B0 : public XorpMemberCallback5B0, public SafeCallbackBase { typedef typename XorpMemberCallback5B0::M M; XorpSafeMemberCallback5B0(O* o, M m) : XorpMemberCallback5B0(o, m), SafeCallbackBase(o) {} ~XorpSafeMemberCallback5B0() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { if (valid()) { XorpMemberCallback5B0::dispatch(a1, a2, a3, a4, a5); } } }; template struct XorpMemberCallbackFactory5B0 { static XorpMemberCallback5B0* make(O* o, R (O::*p)(A1, A2, A3, A4, A5)) { return new XorpSafeMemberCallback5B0(o, p); } }; template struct XorpMemberCallbackFactory5B0 { static XorpMemberCallback5B0* make(O* o, R (O::*p)(A1, A2, A3, A4, A5)) { return new XorpMemberCallback5B0(o, p); }; }; /** * Factory function that creates a callback object targetted at a * member function with 5 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback5::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5)) { return XorpMemberCallbackFactory5B0::True>::make(o, p); } /** * Factory function that creates a callback object targetted at a * member function with 5 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback5::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5)) { return XorpMemberCallbackFactory5B0::True>::make(&o, p); } /** * @short Callback object for const member methods with 5 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstMemberCallback5B0 : public XorpCallback5 { typedef R (O::*M)(A1, A2, A3, A4, A5) const; XorpConstMemberCallback5B0(O* o, M m) : XorpCallback5(), _o(o), _m(m) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for void const member methods with 5 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstMemberCallback5B0 : public XorpCallback5 { typedef void (O::*M)(A1, A2, A3, A4, A5) const; XorpConstMemberCallback5B0(O* o, M m) : XorpCallback5(), _o(o), _m(m) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { ((*_o).*_m)(a1, a2, a3, a4, a5); } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for const safe member methods with 5 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback5B0 : public XorpConstMemberCallback5B0, public SafeCallbackBase { typedef typename XorpConstMemberCallback5B0::M M; XorpConstSafeMemberCallback5B0(O* o, M m) : XorpConstMemberCallback5B0(o, m), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback5B0() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { if (valid()) { R r = XorpConstMemberCallback5B0::dispatch(a1, a2, a3, a4, a5); return r; } } }; /** * @short Callback object for void const safe member methods with 5 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback5B0 : public XorpConstMemberCallback5B0, public SafeCallbackBase { typedef typename XorpConstMemberCallback5B0::M M; XorpConstSafeMemberCallback5B0(O* o, M m) : XorpConstMemberCallback5B0(o, m), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback5B0() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { if (valid()) { XorpConstMemberCallback5B0::dispatch(a1, a2, a3, a4, a5); } } }; template struct XorpConstMemberCallbackFactory5B0 { static XorpConstMemberCallback5B0* make(O* o, R (O::*p)(A1, A2, A3, A4, A5) const) { return new XorpConstSafeMemberCallback5B0(o, p); } }; template struct XorpConstMemberCallbackFactory5B0 { static XorpConstMemberCallback5B0* make(O* o, R (O::*p)(A1, A2, A3, A4, A5) const) { return new XorpConstMemberCallback5B0(o, p); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 5 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback5::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5) const) { return XorpConstMemberCallbackFactory5B0::True>::make(o, p); } /** * Factory function that creates a callback object targetted at a * const member function with 5 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback5::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5) const) { return XorpConstMemberCallbackFactory5B0::True>::make(&o, p); } /** * @short Callback object for functions with 5 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpFunctionCallback5B1 : public XorpCallback5 { typedef R (*F)(A1, A2, A3, A4, A5, BA1); XorpFunctionCallback5B1(F f, BA1 ba1) : XorpCallback5(), _f(f), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { R r = (*_f)(a1, a2, a3, a4, a5, _ba1); return r; } protected: F _f; BA1 _ba1; }; /** * @short Callback object for void functions with 5 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpFunctionCallback5B1 : public XorpCallback5 { typedef void (*F)(A1, A2, A3, A4, A5, BA1); XorpFunctionCallback5B1(F f, BA1 ba1) : XorpCallback5(), _f(f), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { (*_f)(a1, a2, a3, a4, a5, _ba1); } protected: F _f; BA1 _ba1; }; /** * Factory function that creates a callback object targetted at a * function with 5 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback5::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, BA1), BA1 ba1) { return typename XorpCallback5::RefPtr(new XorpFunctionCallback5B1(f, ba1)); } /** * @short Callback object for member methods with 5 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpMemberCallback5B1 : public XorpCallback5 { typedef R (O::*M)(A1, A2, A3, A4, A5, BA1) ; XorpMemberCallback5B1(O* o, M m, BA1 ba1) : XorpCallback5(), _o(o), _m(m), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, _ba1); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for void member methods with 5 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpMemberCallback5B1 : public XorpCallback5 { typedef void (O::*M)(A1, A2, A3, A4, A5, BA1) ; XorpMemberCallback5B1(O* o, M m, BA1 ba1) : XorpCallback5(), _o(o), _m(m), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { ((*_o).*_m)(a1, a2, a3, a4, a5, _ba1); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for safe member methods with 5 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpSafeMemberCallback5B1 : public XorpMemberCallback5B1, public SafeCallbackBase { typedef typename XorpMemberCallback5B1::M M; XorpSafeMemberCallback5B1(O* o, M m, BA1 ba1) : XorpMemberCallback5B1(o, m, ba1), SafeCallbackBase(o) {} ~XorpSafeMemberCallback5B1() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { if (valid()) { R r = XorpMemberCallback5B1::dispatch(a1, a2, a3, a4, a5); return r; } } }; /** * @short Callback object for void safe member methods with 5 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpSafeMemberCallback5B1 : public XorpMemberCallback5B1, public SafeCallbackBase { typedef typename XorpMemberCallback5B1::M M; XorpSafeMemberCallback5B1(O* o, M m, BA1 ba1) : XorpMemberCallback5B1(o, m, ba1), SafeCallbackBase(o) {} ~XorpSafeMemberCallback5B1() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { if (valid()) { XorpMemberCallback5B1::dispatch(a1, a2, a3, a4, a5); } } }; template struct XorpMemberCallbackFactory5B1 { static XorpMemberCallback5B1* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1), BA1 ba1) { return new XorpSafeMemberCallback5B1(o, p, ba1); } }; template struct XorpMemberCallbackFactory5B1 { static XorpMemberCallback5B1* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1), BA1 ba1) { return new XorpMemberCallback5B1(o, p, ba1); }; }; /** * Factory function that creates a callback object targetted at a * member function with 5 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback5::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1), BA1 ba1) { return XorpMemberCallbackFactory5B1::True>::make(o, p, ba1); } /** * Factory function that creates a callback object targetted at a * member function with 5 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback5::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, BA1), BA1 ba1) { return XorpMemberCallbackFactory5B1::True>::make(&o, p, ba1); } /** * @short Callback object for const member methods with 5 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstMemberCallback5B1 : public XorpCallback5 { typedef R (O::*M)(A1, A2, A3, A4, A5, BA1) const; XorpConstMemberCallback5B1(O* o, M m, BA1 ba1) : XorpCallback5(), _o(o), _m(m), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, _ba1); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for void const member methods with 5 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstMemberCallback5B1 : public XorpCallback5 { typedef void (O::*M)(A1, A2, A3, A4, A5, BA1) const; XorpConstMemberCallback5B1(O* o, M m, BA1 ba1) : XorpCallback5(), _o(o), _m(m), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { ((*_o).*_m)(a1, a2, a3, a4, a5, _ba1); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for const safe member methods with 5 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback5B1 : public XorpConstMemberCallback5B1, public SafeCallbackBase { typedef typename XorpConstMemberCallback5B1::M M; XorpConstSafeMemberCallback5B1(O* o, M m, BA1 ba1) : XorpConstMemberCallback5B1(o, m, ba1), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback5B1() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { if (valid()) { R r = XorpConstMemberCallback5B1::dispatch(a1, a2, a3, a4, a5); return r; } } }; /** * @short Callback object for void const safe member methods with 5 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback5B1 : public XorpConstMemberCallback5B1, public SafeCallbackBase { typedef typename XorpConstMemberCallback5B1::M M; XorpConstSafeMemberCallback5B1(O* o, M m, BA1 ba1) : XorpConstMemberCallback5B1(o, m, ba1), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback5B1() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { if (valid()) { XorpConstMemberCallback5B1::dispatch(a1, a2, a3, a4, a5); } } }; template struct XorpConstMemberCallbackFactory5B1 { static XorpConstMemberCallback5B1* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1) const, BA1 ba1) { return new XorpConstSafeMemberCallback5B1(o, p, ba1); } }; template struct XorpConstMemberCallbackFactory5B1 { static XorpConstMemberCallback5B1* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1) const, BA1 ba1) { return new XorpConstMemberCallback5B1(o, p, ba1); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 5 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback5::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1) const, BA1 ba1) { return XorpConstMemberCallbackFactory5B1::True>::make(o, p, ba1); } /** * Factory function that creates a callback object targetted at a * const member function with 5 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback5::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, BA1) const, BA1 ba1) { return XorpConstMemberCallbackFactory5B1::True>::make(&o, p, ba1); } /** * @short Callback object for functions with 5 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpFunctionCallback5B2 : public XorpCallback5 { typedef R (*F)(A1, A2, A3, A4, A5, BA1, BA2); XorpFunctionCallback5B2(F f, BA1 ba1, BA2 ba2) : XorpCallback5(), _f(f), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { R r = (*_f)(a1, a2, a3, a4, a5, _ba1, _ba2); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; }; /** * @short Callback object for void functions with 5 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpFunctionCallback5B2 : public XorpCallback5 { typedef void (*F)(A1, A2, A3, A4, A5, BA1, BA2); XorpFunctionCallback5B2(F f, BA1 ba1, BA2 ba2) : XorpCallback5(), _f(f), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { (*_f)(a1, a2, a3, a4, a5, _ba1, _ba2); } protected: F _f; BA1 _ba1; BA2 _ba2; }; /** * Factory function that creates a callback object targetted at a * function with 5 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback5::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, BA1, BA2), BA1 ba1, BA2 ba2) { return typename XorpCallback5::RefPtr(new XorpFunctionCallback5B2(f, ba1, ba2)); } /** * @short Callback object for member methods with 5 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpMemberCallback5B2 : public XorpCallback5 { typedef R (O::*M)(A1, A2, A3, A4, A5, BA1, BA2) ; XorpMemberCallback5B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback5(), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, _ba1, _ba2); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for void member methods with 5 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpMemberCallback5B2 : public XorpCallback5 { typedef void (O::*M)(A1, A2, A3, A4, A5, BA1, BA2) ; XorpMemberCallback5B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback5(), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { ((*_o).*_m)(a1, a2, a3, a4, a5, _ba1, _ba2); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for safe member methods with 5 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpSafeMemberCallback5B2 : public XorpMemberCallback5B2, public SafeCallbackBase { typedef typename XorpMemberCallback5B2::M M; XorpSafeMemberCallback5B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpMemberCallback5B2(o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpSafeMemberCallback5B2() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { if (valid()) { R r = XorpMemberCallback5B2::dispatch(a1, a2, a3, a4, a5); return r; } } }; /** * @short Callback object for void safe member methods with 5 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpSafeMemberCallback5B2 : public XorpMemberCallback5B2, public SafeCallbackBase { typedef typename XorpMemberCallback5B2::M M; XorpSafeMemberCallback5B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpMemberCallback5B2(o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpSafeMemberCallback5B2() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { if (valid()) { XorpMemberCallback5B2::dispatch(a1, a2, a3, a4, a5); } } }; template struct XorpMemberCallbackFactory5B2 { static XorpMemberCallback5B2* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2), BA1 ba1, BA2 ba2) { return new XorpSafeMemberCallback5B2(o, p, ba1, ba2); } }; template struct XorpMemberCallbackFactory5B2 { static XorpMemberCallback5B2* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2), BA1 ba1, BA2 ba2) { return new XorpMemberCallback5B2(o, p, ba1, ba2); }; }; /** * Factory function that creates a callback object targetted at a * member function with 5 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback5::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2), BA1 ba1, BA2 ba2) { return XorpMemberCallbackFactory5B2::True>::make(o, p, ba1, ba2); } /** * Factory function that creates a callback object targetted at a * member function with 5 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback5::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2), BA1 ba1, BA2 ba2) { return XorpMemberCallbackFactory5B2::True>::make(&o, p, ba1, ba2); } /** * @short Callback object for const member methods with 5 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstMemberCallback5B2 : public XorpCallback5 { typedef R (O::*M)(A1, A2, A3, A4, A5, BA1, BA2) const; XorpConstMemberCallback5B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback5(), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, _ba1, _ba2); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for void const member methods with 5 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstMemberCallback5B2 : public XorpCallback5 { typedef void (O::*M)(A1, A2, A3, A4, A5, BA1, BA2) const; XorpConstMemberCallback5B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback5(), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { ((*_o).*_m)(a1, a2, a3, a4, a5, _ba1, _ba2); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for const safe member methods with 5 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback5B2 : public XorpConstMemberCallback5B2, public SafeCallbackBase { typedef typename XorpConstMemberCallback5B2::M M; XorpConstSafeMemberCallback5B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpConstMemberCallback5B2(o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback5B2() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { if (valid()) { R r = XorpConstMemberCallback5B2::dispatch(a1, a2, a3, a4, a5); return r; } } }; /** * @short Callback object for void const safe member methods with 5 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback5B2 : public XorpConstMemberCallback5B2, public SafeCallbackBase { typedef typename XorpConstMemberCallback5B2::M M; XorpConstSafeMemberCallback5B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpConstMemberCallback5B2(o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback5B2() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { if (valid()) { XorpConstMemberCallback5B2::dispatch(a1, a2, a3, a4, a5); } } }; template struct XorpConstMemberCallbackFactory5B2 { static XorpConstMemberCallback5B2* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2) const, BA1 ba1, BA2 ba2) { return new XorpConstSafeMemberCallback5B2(o, p, ba1, ba2); } }; template struct XorpConstMemberCallbackFactory5B2 { static XorpConstMemberCallback5B2* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2) const, BA1 ba1, BA2 ba2) { return new XorpConstMemberCallback5B2(o, p, ba1, ba2); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 5 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback5::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2) const, BA1 ba1, BA2 ba2) { return XorpConstMemberCallbackFactory5B2::True>::make(o, p, ba1, ba2); } /** * Factory function that creates a callback object targetted at a * const member function with 5 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback5::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2) const, BA1 ba1, BA2 ba2) { return XorpConstMemberCallbackFactory5B2::True>::make(&o, p, ba1, ba2); } /** * @short Callback object for functions with 5 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpFunctionCallback5B3 : public XorpCallback5 { typedef R (*F)(A1, A2, A3, A4, A5, BA1, BA2, BA3); XorpFunctionCallback5B3(F f, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback5(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { R r = (*_f)(a1, a2, a3, a4, a5, _ba1, _ba2, _ba3); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; }; /** * @short Callback object for void functions with 5 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpFunctionCallback5B3 : public XorpCallback5 { typedef void (*F)(A1, A2, A3, A4, A5, BA1, BA2, BA3); XorpFunctionCallback5B3(F f, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback5(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { (*_f)(a1, a2, a3, a4, a5, _ba1, _ba2, _ba3); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; }; /** * Factory function that creates a callback object targetted at a * function with 5 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback5::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return typename XorpCallback5::RefPtr(new XorpFunctionCallback5B3(f, ba1, ba2, ba3)); } /** * @short Callback object for member methods with 5 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpMemberCallback5B3 : public XorpCallback5 { typedef R (O::*M)(A1, A2, A3, A4, A5, BA1, BA2, BA3) ; XorpMemberCallback5B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback5(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, _ba1, _ba2, _ba3); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for void member methods with 5 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpMemberCallback5B3 : public XorpCallback5 { typedef void (O::*M)(A1, A2, A3, A4, A5, BA1, BA2, BA3) ; XorpMemberCallback5B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback5(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { ((*_o).*_m)(a1, a2, a3, a4, a5, _ba1, _ba2, _ba3); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for safe member methods with 5 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpSafeMemberCallback5B3 : public XorpMemberCallback5B3, public SafeCallbackBase { typedef typename XorpMemberCallback5B3::M M; XorpSafeMemberCallback5B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpMemberCallback5B3(o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpSafeMemberCallback5B3() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { if (valid()) { R r = XorpMemberCallback5B3::dispatch(a1, a2, a3, a4, a5); return r; } } }; /** * @short Callback object for void safe member methods with 5 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpSafeMemberCallback5B3 : public XorpMemberCallback5B3, public SafeCallbackBase { typedef typename XorpMemberCallback5B3::M M; XorpSafeMemberCallback5B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpMemberCallback5B3(o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpSafeMemberCallback5B3() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { if (valid()) { XorpMemberCallback5B3::dispatch(a1, a2, a3, a4, a5); } } }; template struct XorpMemberCallbackFactory5B3 { static XorpMemberCallback5B3* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpSafeMemberCallback5B3(o, p, ba1, ba2, ba3); } }; template struct XorpMemberCallbackFactory5B3 { static XorpMemberCallback5B3* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpMemberCallback5B3(o, p, ba1, ba2, ba3); }; }; /** * Factory function that creates a callback object targetted at a * member function with 5 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback5::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return XorpMemberCallbackFactory5B3::True>::make(o, p, ba1, ba2, ba3); } /** * Factory function that creates a callback object targetted at a * member function with 5 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback5::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return XorpMemberCallbackFactory5B3::True>::make(&o, p, ba1, ba2, ba3); } /** * @short Callback object for const member methods with 5 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstMemberCallback5B3 : public XorpCallback5 { typedef R (O::*M)(A1, A2, A3, A4, A5, BA1, BA2, BA3) const; XorpConstMemberCallback5B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback5(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, _ba1, _ba2, _ba3); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for void const member methods with 5 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstMemberCallback5B3 : public XorpCallback5 { typedef void (O::*M)(A1, A2, A3, A4, A5, BA1, BA2, BA3) const; XorpConstMemberCallback5B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback5(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { ((*_o).*_m)(a1, a2, a3, a4, a5, _ba1, _ba2, _ba3); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for const safe member methods with 5 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback5B3 : public XorpConstMemberCallback5B3, public SafeCallbackBase { typedef typename XorpConstMemberCallback5B3::M M; XorpConstSafeMemberCallback5B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpConstMemberCallback5B3(o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback5B3() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { if (valid()) { R r = XorpConstMemberCallback5B3::dispatch(a1, a2, a3, a4, a5); return r; } } }; /** * @short Callback object for void const safe member methods with 5 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback5B3 : public XorpConstMemberCallback5B3, public SafeCallbackBase { typedef typename XorpConstMemberCallback5B3::M M; XorpConstSafeMemberCallback5B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpConstMemberCallback5B3(o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback5B3() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { if (valid()) { XorpConstMemberCallback5B3::dispatch(a1, a2, a3, a4, a5); } } }; template struct XorpConstMemberCallbackFactory5B3 { static XorpConstMemberCallback5B3* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpConstSafeMemberCallback5B3(o, p, ba1, ba2, ba3); } }; template struct XorpConstMemberCallbackFactory5B3 { static XorpConstMemberCallback5B3* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpConstMemberCallback5B3(o, p, ba1, ba2, ba3); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 5 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback5::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return XorpConstMemberCallbackFactory5B3::True>::make(o, p, ba1, ba2, ba3); } /** * Factory function that creates a callback object targetted at a * const member function with 5 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback5::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return XorpConstMemberCallbackFactory5B3::True>::make(&o, p, ba1, ba2, ba3); } /** * @short Callback object for functions with 5 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpFunctionCallback5B4 : public XorpCallback5 { typedef R (*F)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4); XorpFunctionCallback5B4(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback5(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { R r = (*_f)(a1, a2, a3, a4, a5, _ba1, _ba2, _ba3, _ba4); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; }; /** * @short Callback object for void functions with 5 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpFunctionCallback5B4 : public XorpCallback5 { typedef void (*F)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4); XorpFunctionCallback5B4(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback5(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { (*_f)(a1, a2, a3, a4, a5, _ba1, _ba2, _ba3, _ba4); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; }; /** * Factory function that creates a callback object targetted at a * function with 5 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback5::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return typename XorpCallback5::RefPtr(new XorpFunctionCallback5B4(f, ba1, ba2, ba3, ba4)); } /** * @short Callback object for member methods with 5 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpMemberCallback5B4 : public XorpCallback5 { typedef R (O::*M)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4) ; XorpMemberCallback5B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback5(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, _ba1, _ba2, _ba3, _ba4); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for void member methods with 5 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpMemberCallback5B4 : public XorpCallback5 { typedef void (O::*M)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4) ; XorpMemberCallback5B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback5(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { ((*_o).*_m)(a1, a2, a3, a4, a5, _ba1, _ba2, _ba3, _ba4); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for safe member methods with 5 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpSafeMemberCallback5B4 : public XorpMemberCallback5B4, public SafeCallbackBase { typedef typename XorpMemberCallback5B4::M M; XorpSafeMemberCallback5B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpMemberCallback5B4(o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpSafeMemberCallback5B4() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { if (valid()) { R r = XorpMemberCallback5B4::dispatch(a1, a2, a3, a4, a5); return r; } } }; /** * @short Callback object for void safe member methods with 5 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpSafeMemberCallback5B4 : public XorpMemberCallback5B4, public SafeCallbackBase { typedef typename XorpMemberCallback5B4::M M; XorpSafeMemberCallback5B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpMemberCallback5B4(o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpSafeMemberCallback5B4() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { if (valid()) { XorpMemberCallback5B4::dispatch(a1, a2, a3, a4, a5); } } }; template struct XorpMemberCallbackFactory5B4 { static XorpMemberCallback5B4* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpSafeMemberCallback5B4(o, p, ba1, ba2, ba3, ba4); } }; template struct XorpMemberCallbackFactory5B4 { static XorpMemberCallback5B4* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpMemberCallback5B4(o, p, ba1, ba2, ba3, ba4); }; }; /** * Factory function that creates a callback object targetted at a * member function with 5 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback5::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpMemberCallbackFactory5B4::True>::make(o, p, ba1, ba2, ba3, ba4); } /** * Factory function that creates a callback object targetted at a * member function with 5 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback5::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpMemberCallbackFactory5B4::True>::make(&o, p, ba1, ba2, ba3, ba4); } /** * @short Callback object for const member methods with 5 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstMemberCallback5B4 : public XorpCallback5 { typedef R (O::*M)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4) const; XorpConstMemberCallback5B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback5(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, _ba1, _ba2, _ba3, _ba4); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for void const member methods with 5 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstMemberCallback5B4 : public XorpCallback5 { typedef void (O::*M)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4) const; XorpConstMemberCallback5B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback5(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { ((*_o).*_m)(a1, a2, a3, a4, a5, _ba1, _ba2, _ba3, _ba4); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for const safe member methods with 5 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback5B4 : public XorpConstMemberCallback5B4, public SafeCallbackBase { typedef typename XorpConstMemberCallback5B4::M M; XorpConstSafeMemberCallback5B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpConstMemberCallback5B4(o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback5B4() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { if (valid()) { R r = XorpConstMemberCallback5B4::dispatch(a1, a2, a3, a4, a5); return r; } } }; /** * @short Callback object for void const safe member methods with 5 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback5B4 : public XorpConstMemberCallback5B4, public SafeCallbackBase { typedef typename XorpConstMemberCallback5B4::M M; XorpConstSafeMemberCallback5B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpConstMemberCallback5B4(o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback5B4() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { if (valid()) { XorpConstMemberCallback5B4::dispatch(a1, a2, a3, a4, a5); } } }; template struct XorpConstMemberCallbackFactory5B4 { static XorpConstMemberCallback5B4* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpConstSafeMemberCallback5B4(o, p, ba1, ba2, ba3, ba4); } }; template struct XorpConstMemberCallbackFactory5B4 { static XorpConstMemberCallback5B4* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpConstMemberCallback5B4(o, p, ba1, ba2, ba3, ba4); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 5 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback5::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpConstMemberCallbackFactory5B4::True>::make(o, p, ba1, ba2, ba3, ba4); } /** * Factory function that creates a callback object targetted at a * const member function with 5 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback5::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpConstMemberCallbackFactory5B4::True>::make(&o, p, ba1, ba2, ba3, ba4); } /** * @short Callback object for functions with 5 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpFunctionCallback5B5 : public XorpCallback5 { typedef R (*F)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4, BA5); XorpFunctionCallback5B5(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback5(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { R r = (*_f)(a1, a2, a3, a4, a5, _ba1, _ba2, _ba3, _ba4, _ba5); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; }; /** * @short Callback object for void functions with 5 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpFunctionCallback5B5 : public XorpCallback5 { typedef void (*F)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4, BA5); XorpFunctionCallback5B5(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback5(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { (*_f)(a1, a2, a3, a4, a5, _ba1, _ba2, _ba3, _ba4, _ba5); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; }; /** * Factory function that creates a callback object targetted at a * function with 5 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback5::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return typename XorpCallback5::RefPtr(new XorpFunctionCallback5B5(f, ba1, ba2, ba3, ba4, ba5)); } /** * @short Callback object for member methods with 5 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpMemberCallback5B5 : public XorpCallback5 { typedef R (O::*M)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4, BA5) ; XorpMemberCallback5B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback5(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, _ba1, _ba2, _ba3, _ba4, _ba5); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for void member methods with 5 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpMemberCallback5B5 : public XorpCallback5 { typedef void (O::*M)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4, BA5) ; XorpMemberCallback5B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback5(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { ((*_o).*_m)(a1, a2, a3, a4, a5, _ba1, _ba2, _ba3, _ba4, _ba5); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for safe member methods with 5 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpSafeMemberCallback5B5 : public XorpMemberCallback5B5, public SafeCallbackBase { typedef typename XorpMemberCallback5B5::M M; XorpSafeMemberCallback5B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpMemberCallback5B5(o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpSafeMemberCallback5B5() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { if (valid()) { R r = XorpMemberCallback5B5::dispatch(a1, a2, a3, a4, a5); return r; } } }; /** * @short Callback object for void safe member methods with 5 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpSafeMemberCallback5B5 : public XorpMemberCallback5B5, public SafeCallbackBase { typedef typename XorpMemberCallback5B5::M M; XorpSafeMemberCallback5B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpMemberCallback5B5(o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpSafeMemberCallback5B5() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { if (valid()) { XorpMemberCallback5B5::dispatch(a1, a2, a3, a4, a5); } } }; template struct XorpMemberCallbackFactory5B5 { static XorpMemberCallback5B5* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpSafeMemberCallback5B5(o, p, ba1, ba2, ba3, ba4, ba5); } }; template struct XorpMemberCallbackFactory5B5 { static XorpMemberCallback5B5* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpMemberCallback5B5(o, p, ba1, ba2, ba3, ba4, ba5); }; }; /** * Factory function that creates a callback object targetted at a * member function with 5 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback5::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpMemberCallbackFactory5B5::True>::make(o, p, ba1, ba2, ba3, ba4, ba5); } /** * Factory function that creates a callback object targetted at a * member function with 5 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback5::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpMemberCallbackFactory5B5::True>::make(&o, p, ba1, ba2, ba3, ba4, ba5); } /** * @short Callback object for const member methods with 5 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstMemberCallback5B5 : public XorpCallback5 { typedef R (O::*M)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4, BA5) const; XorpConstMemberCallback5B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback5(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, _ba1, _ba2, _ba3, _ba4, _ba5); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for void const member methods with 5 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstMemberCallback5B5 : public XorpCallback5 { typedef void (O::*M)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4, BA5) const; XorpConstMemberCallback5B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback5(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { ((*_o).*_m)(a1, a2, a3, a4, a5, _ba1, _ba2, _ba3, _ba4, _ba5); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for const safe member methods with 5 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback5B5 : public XorpConstMemberCallback5B5, public SafeCallbackBase { typedef typename XorpConstMemberCallback5B5::M M; XorpConstSafeMemberCallback5B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpConstMemberCallback5B5(o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback5B5() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { if (valid()) { R r = XorpConstMemberCallback5B5::dispatch(a1, a2, a3, a4, a5); return r; } } }; /** * @short Callback object for void const safe member methods with 5 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback5B5 : public XorpConstMemberCallback5B5, public SafeCallbackBase { typedef typename XorpConstMemberCallback5B5::M M; XorpConstSafeMemberCallback5B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpConstMemberCallback5B5(o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback5B5() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { if (valid()) { XorpConstMemberCallback5B5::dispatch(a1, a2, a3, a4, a5); } } }; template struct XorpConstMemberCallbackFactory5B5 { static XorpConstMemberCallback5B5* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpConstSafeMemberCallback5B5(o, p, ba1, ba2, ba3, ba4, ba5); } }; template struct XorpConstMemberCallbackFactory5B5 { static XorpConstMemberCallback5B5* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpConstMemberCallback5B5(o, p, ba1, ba2, ba3, ba4, ba5); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 5 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback5::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpConstMemberCallbackFactory5B5::True>::make(o, p, ba1, ba2, ba3, ba4, ba5); } /** * Factory function that creates a callback object targetted at a * const member function with 5 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback5::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpConstMemberCallbackFactory5B5::True>::make(&o, p, ba1, ba2, ba3, ba4, ba5); } /** * @short Callback object for functions with 5 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpFunctionCallback5B6 : public XorpCallback5 { typedef R (*F)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4, BA5, BA6); XorpFunctionCallback5B6(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback5(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { R r = (*_f)(a1, a2, a3, a4, a5, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; BA6 _ba6; }; /** * @short Callback object for void functions with 5 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpFunctionCallback5B6 : public XorpCallback5 { typedef void (*F)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4, BA5, BA6); XorpFunctionCallback5B6(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback5(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { (*_f)(a1, a2, a3, a4, a5, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; BA6 _ba6; }; /** * Factory function that creates a callback object targetted at a * function with 5 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback5::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return typename XorpCallback5::RefPtr(new XorpFunctionCallback5B6(f, ba1, ba2, ba3, ba4, ba5, ba6)); } /** * @short Callback object for member methods with 5 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpMemberCallback5B6 : public XorpCallback5 { typedef R (O::*M)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4, BA5, BA6) ; XorpMemberCallback5B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback5(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for void member methods with 5 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpMemberCallback5B6 : public XorpCallback5 { typedef void (O::*M)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4, BA5, BA6) ; XorpMemberCallback5B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback5(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { ((*_o).*_m)(a1, a2, a3, a4, a5, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for safe member methods with 5 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpSafeMemberCallback5B6 : public XorpMemberCallback5B6, public SafeCallbackBase { typedef typename XorpMemberCallback5B6::M M; XorpSafeMemberCallback5B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpMemberCallback5B6(o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpSafeMemberCallback5B6() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { if (valid()) { R r = XorpMemberCallback5B6::dispatch(a1, a2, a3, a4, a5); return r; } } }; /** * @short Callback object for void safe member methods with 5 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpSafeMemberCallback5B6 : public XorpMemberCallback5B6, public SafeCallbackBase { typedef typename XorpMemberCallback5B6::M M; XorpSafeMemberCallback5B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpMemberCallback5B6(o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpSafeMemberCallback5B6() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { if (valid()) { XorpMemberCallback5B6::dispatch(a1, a2, a3, a4, a5); } } }; template struct XorpMemberCallbackFactory5B6 { static XorpMemberCallback5B6* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpSafeMemberCallback5B6(o, p, ba1, ba2, ba3, ba4, ba5, ba6); } }; template struct XorpMemberCallbackFactory5B6 { static XorpMemberCallback5B6* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpMemberCallback5B6(o, p, ba1, ba2, ba3, ba4, ba5, ba6); }; }; /** * Factory function that creates a callback object targetted at a * member function with 5 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback5::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpMemberCallbackFactory5B6::True>::make(o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * Factory function that creates a callback object targetted at a * member function with 5 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback5::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpMemberCallbackFactory5B6::True>::make(&o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * @short Callback object for const member methods with 5 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstMemberCallback5B6 : public XorpCallback5 { typedef R (O::*M)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4, BA5, BA6) const; XorpConstMemberCallback5B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback5(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for void const member methods with 5 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstMemberCallback5B6 : public XorpCallback5 { typedef void (O::*M)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4, BA5, BA6) const; XorpConstMemberCallback5B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback5(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { ((*_o).*_m)(a1, a2, a3, a4, a5, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for const safe member methods with 5 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback5B6 : public XorpConstMemberCallback5B6, public SafeCallbackBase { typedef typename XorpConstMemberCallback5B6::M M; XorpConstSafeMemberCallback5B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpConstMemberCallback5B6(o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback5B6() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { if (valid()) { R r = XorpConstMemberCallback5B6::dispatch(a1, a2, a3, a4, a5); return r; } } }; /** * @short Callback object for void const safe member methods with 5 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback5B6 : public XorpConstMemberCallback5B6, public SafeCallbackBase { typedef typename XorpConstMemberCallback5B6::M M; XorpConstSafeMemberCallback5B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpConstMemberCallback5B6(o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback5B6() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { if (valid()) { XorpConstMemberCallback5B6::dispatch(a1, a2, a3, a4, a5); } } }; template struct XorpConstMemberCallbackFactory5B6 { static XorpConstMemberCallback5B6* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpConstSafeMemberCallback5B6(o, p, ba1, ba2, ba3, ba4, ba5, ba6); } }; template struct XorpConstMemberCallbackFactory5B6 { static XorpConstMemberCallback5B6* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpConstMemberCallback5B6(o, p, ba1, ba2, ba3, ba4, ba5, ba6); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 5 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback5::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpConstMemberCallbackFactory5B6::True>::make(o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * Factory function that creates a callback object targetted at a * const member function with 5 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback5::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpConstMemberCallbackFactory5B6::True>::make(&o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /////////////////////////////////////////////////////////////////////////////// // // Code relating to callbacks with 6 late args // /** * @short Base class for callbacks with 6 dispatch time args. */ template struct XorpCallback6 { typedef ref_ptr RefPtr; virtual ~XorpCallback6() {} virtual R dispatch(A1, A2, A3, A4, A5, A6) = 0; }; /** * @short Callback object for functions with 6 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpFunctionCallback6B0 : public XorpCallback6 { typedef R (*F)(A1, A2, A3, A4, A5, A6); XorpFunctionCallback6B0(F f) : XorpCallback6(), _f(f) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { R r = (*_f)(a1, a2, a3, a4, a5, a6); return r; } protected: F _f; }; /** * @short Callback object for void functions with 6 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpFunctionCallback6B0 : public XorpCallback6 { typedef void (*F)(A1, A2, A3, A4, A5, A6); XorpFunctionCallback6B0(F f) : XorpCallback6(), _f(f) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { (*_f)(a1, a2, a3, a4, a5, a6); } protected: F _f; }; /** * Factory function that creates a callback object targetted at a * function with 6 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback6::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6)) { return typename XorpCallback6::RefPtr(new XorpFunctionCallback6B0(f)); } /** * @short Callback object for member methods with 6 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpMemberCallback6B0 : public XorpCallback6 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6) ; XorpMemberCallback6B0(O* o, M m) : XorpCallback6(), _o(o), _m(m) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for void member methods with 6 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpMemberCallback6B0 : public XorpCallback6 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6) ; XorpMemberCallback6B0(O* o, M m) : XorpCallback6(), _o(o), _m(m) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6); } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for safe member methods with 6 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpSafeMemberCallback6B0 : public XorpMemberCallback6B0, public SafeCallbackBase { typedef typename XorpMemberCallback6B0::M M; XorpSafeMemberCallback6B0(O* o, M m) : XorpMemberCallback6B0(o, m), SafeCallbackBase(o) {} ~XorpSafeMemberCallback6B0() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { if (valid()) { R r = XorpMemberCallback6B0::dispatch(a1, a2, a3, a4, a5, a6); return r; } } }; /** * @short Callback object for void safe member methods with 6 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpSafeMemberCallback6B0 : public XorpMemberCallback6B0, public SafeCallbackBase { typedef typename XorpMemberCallback6B0::M M; XorpSafeMemberCallback6B0(O* o, M m) : XorpMemberCallback6B0(o, m), SafeCallbackBase(o) {} ~XorpSafeMemberCallback6B0() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { if (valid()) { XorpMemberCallback6B0::dispatch(a1, a2, a3, a4, a5, a6); } } }; template struct XorpMemberCallbackFactory6B0 { static XorpMemberCallback6B0* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6)) { return new XorpSafeMemberCallback6B0(o, p); } }; template struct XorpMemberCallbackFactory6B0 { static XorpMemberCallback6B0* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6)) { return new XorpMemberCallback6B0(o, p); }; }; /** * Factory function that creates a callback object targetted at a * member function with 6 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback6::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6)) { return XorpMemberCallbackFactory6B0::True>::make(o, p); } /** * Factory function that creates a callback object targetted at a * member function with 6 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback6::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6)) { return XorpMemberCallbackFactory6B0::True>::make(&o, p); } /** * @short Callback object for const member methods with 6 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstMemberCallback6B0 : public XorpCallback6 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6) const; XorpConstMemberCallback6B0(O* o, M m) : XorpCallback6(), _o(o), _m(m) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for void const member methods with 6 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstMemberCallback6B0 : public XorpCallback6 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6) const; XorpConstMemberCallback6B0(O* o, M m) : XorpCallback6(), _o(o), _m(m) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6); } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for const safe member methods with 6 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback6B0 : public XorpConstMemberCallback6B0, public SafeCallbackBase { typedef typename XorpConstMemberCallback6B0::M M; XorpConstSafeMemberCallback6B0(O* o, M m) : XorpConstMemberCallback6B0(o, m), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback6B0() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { if (valid()) { R r = XorpConstMemberCallback6B0::dispatch(a1, a2, a3, a4, a5, a6); return r; } } }; /** * @short Callback object for void const safe member methods with 6 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback6B0 : public XorpConstMemberCallback6B0, public SafeCallbackBase { typedef typename XorpConstMemberCallback6B0::M M; XorpConstSafeMemberCallback6B0(O* o, M m) : XorpConstMemberCallback6B0(o, m), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback6B0() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { if (valid()) { XorpConstMemberCallback6B0::dispatch(a1, a2, a3, a4, a5, a6); } } }; template struct XorpConstMemberCallbackFactory6B0 { static XorpConstMemberCallback6B0* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6) const) { return new XorpConstSafeMemberCallback6B0(o, p); } }; template struct XorpConstMemberCallbackFactory6B0 { static XorpConstMemberCallback6B0* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6) const) { return new XorpConstMemberCallback6B0(o, p); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 6 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback6::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6) const) { return XorpConstMemberCallbackFactory6B0::True>::make(o, p); } /** * Factory function that creates a callback object targetted at a * const member function with 6 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback6::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6) const) { return XorpConstMemberCallbackFactory6B0::True>::make(&o, p); } /** * @short Callback object for functions with 6 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpFunctionCallback6B1 : public XorpCallback6 { typedef R (*F)(A1, A2, A3, A4, A5, A6, BA1); XorpFunctionCallback6B1(F f, BA1 ba1) : XorpCallback6(), _f(f), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { R r = (*_f)(a1, a2, a3, a4, a5, a6, _ba1); return r; } protected: F _f; BA1 _ba1; }; /** * @short Callback object for void functions with 6 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpFunctionCallback6B1 : public XorpCallback6 { typedef void (*F)(A1, A2, A3, A4, A5, A6, BA1); XorpFunctionCallback6B1(F f, BA1 ba1) : XorpCallback6(), _f(f), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { (*_f)(a1, a2, a3, a4, a5, a6, _ba1); } protected: F _f; BA1 _ba1; }; /** * Factory function that creates a callback object targetted at a * function with 6 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback6::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, BA1), BA1 ba1) { return typename XorpCallback6::RefPtr(new XorpFunctionCallback6B1(f, ba1)); } /** * @short Callback object for member methods with 6 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpMemberCallback6B1 : public XorpCallback6 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, BA1) ; XorpMemberCallback6B1(O* o, M m, BA1 ba1) : XorpCallback6(), _o(o), _m(m), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, _ba1); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for void member methods with 6 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpMemberCallback6B1 : public XorpCallback6 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, BA1) ; XorpMemberCallback6B1(O* o, M m, BA1 ba1) : XorpCallback6(), _o(o), _m(m), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, _ba1); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for safe member methods with 6 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpSafeMemberCallback6B1 : public XorpMemberCallback6B1, public SafeCallbackBase { typedef typename XorpMemberCallback6B1::M M; XorpSafeMemberCallback6B1(O* o, M m, BA1 ba1) : XorpMemberCallback6B1(o, m, ba1), SafeCallbackBase(o) {} ~XorpSafeMemberCallback6B1() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { if (valid()) { R r = XorpMemberCallback6B1::dispatch(a1, a2, a3, a4, a5, a6); return r; } } }; /** * @short Callback object for void safe member methods with 6 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpSafeMemberCallback6B1 : public XorpMemberCallback6B1, public SafeCallbackBase { typedef typename XorpMemberCallback6B1::M M; XorpSafeMemberCallback6B1(O* o, M m, BA1 ba1) : XorpMemberCallback6B1(o, m, ba1), SafeCallbackBase(o) {} ~XorpSafeMemberCallback6B1() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { if (valid()) { XorpMemberCallback6B1::dispatch(a1, a2, a3, a4, a5, a6); } } }; template struct XorpMemberCallbackFactory6B1 { static XorpMemberCallback6B1* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1), BA1 ba1) { return new XorpSafeMemberCallback6B1(o, p, ba1); } }; template struct XorpMemberCallbackFactory6B1 { static XorpMemberCallback6B1* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1), BA1 ba1) { return new XorpMemberCallback6B1(o, p, ba1); }; }; /** * Factory function that creates a callback object targetted at a * member function with 6 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback6::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1), BA1 ba1) { return XorpMemberCallbackFactory6B1::True>::make(o, p, ba1); } /** * Factory function that creates a callback object targetted at a * member function with 6 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback6::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1), BA1 ba1) { return XorpMemberCallbackFactory6B1::True>::make(&o, p, ba1); } /** * @short Callback object for const member methods with 6 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstMemberCallback6B1 : public XorpCallback6 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, BA1) const; XorpConstMemberCallback6B1(O* o, M m, BA1 ba1) : XorpCallback6(), _o(o), _m(m), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, _ba1); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for void const member methods with 6 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstMemberCallback6B1 : public XorpCallback6 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, BA1) const; XorpConstMemberCallback6B1(O* o, M m, BA1 ba1) : XorpCallback6(), _o(o), _m(m), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, _ba1); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for const safe member methods with 6 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback6B1 : public XorpConstMemberCallback6B1, public SafeCallbackBase { typedef typename XorpConstMemberCallback6B1::M M; XorpConstSafeMemberCallback6B1(O* o, M m, BA1 ba1) : XorpConstMemberCallback6B1(o, m, ba1), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback6B1() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { if (valid()) { R r = XorpConstMemberCallback6B1::dispatch(a1, a2, a3, a4, a5, a6); return r; } } }; /** * @short Callback object for void const safe member methods with 6 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback6B1 : public XorpConstMemberCallback6B1, public SafeCallbackBase { typedef typename XorpConstMemberCallback6B1::M M; XorpConstSafeMemberCallback6B1(O* o, M m, BA1 ba1) : XorpConstMemberCallback6B1(o, m, ba1), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback6B1() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { if (valid()) { XorpConstMemberCallback6B1::dispatch(a1, a2, a3, a4, a5, a6); } } }; template struct XorpConstMemberCallbackFactory6B1 { static XorpConstMemberCallback6B1* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1) const, BA1 ba1) { return new XorpConstSafeMemberCallback6B1(o, p, ba1); } }; template struct XorpConstMemberCallbackFactory6B1 { static XorpConstMemberCallback6B1* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1) const, BA1 ba1) { return new XorpConstMemberCallback6B1(o, p, ba1); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 6 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback6::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1) const, BA1 ba1) { return XorpConstMemberCallbackFactory6B1::True>::make(o, p, ba1); } /** * Factory function that creates a callback object targetted at a * const member function with 6 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback6::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1) const, BA1 ba1) { return XorpConstMemberCallbackFactory6B1::True>::make(&o, p, ba1); } /** * @short Callback object for functions with 6 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpFunctionCallback6B2 : public XorpCallback6 { typedef R (*F)(A1, A2, A3, A4, A5, A6, BA1, BA2); XorpFunctionCallback6B2(F f, BA1 ba1, BA2 ba2) : XorpCallback6(), _f(f), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { R r = (*_f)(a1, a2, a3, a4, a5, a6, _ba1, _ba2); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; }; /** * @short Callback object for void functions with 6 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpFunctionCallback6B2 : public XorpCallback6 { typedef void (*F)(A1, A2, A3, A4, A5, A6, BA1, BA2); XorpFunctionCallback6B2(F f, BA1 ba1, BA2 ba2) : XorpCallback6(), _f(f), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { (*_f)(a1, a2, a3, a4, a5, a6, _ba1, _ba2); } protected: F _f; BA1 _ba1; BA2 _ba2; }; /** * Factory function that creates a callback object targetted at a * function with 6 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback6::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, BA1, BA2), BA1 ba1, BA2 ba2) { return typename XorpCallback6::RefPtr(new XorpFunctionCallback6B2(f, ba1, ba2)); } /** * @short Callback object for member methods with 6 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpMemberCallback6B2 : public XorpCallback6 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, BA1, BA2) ; XorpMemberCallback6B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback6(), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, _ba1, _ba2); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for void member methods with 6 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpMemberCallback6B2 : public XorpCallback6 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, BA1, BA2) ; XorpMemberCallback6B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback6(), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, _ba1, _ba2); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for safe member methods with 6 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpSafeMemberCallback6B2 : public XorpMemberCallback6B2, public SafeCallbackBase { typedef typename XorpMemberCallback6B2::M M; XorpSafeMemberCallback6B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpMemberCallback6B2(o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpSafeMemberCallback6B2() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { if (valid()) { R r = XorpMemberCallback6B2::dispatch(a1, a2, a3, a4, a5, a6); return r; } } }; /** * @short Callback object for void safe member methods with 6 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpSafeMemberCallback6B2 : public XorpMemberCallback6B2, public SafeCallbackBase { typedef typename XorpMemberCallback6B2::M M; XorpSafeMemberCallback6B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpMemberCallback6B2(o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpSafeMemberCallback6B2() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { if (valid()) { XorpMemberCallback6B2::dispatch(a1, a2, a3, a4, a5, a6); } } }; template struct XorpMemberCallbackFactory6B2 { static XorpMemberCallback6B2* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2), BA1 ba1, BA2 ba2) { return new XorpSafeMemberCallback6B2(o, p, ba1, ba2); } }; template struct XorpMemberCallbackFactory6B2 { static XorpMemberCallback6B2* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2), BA1 ba1, BA2 ba2) { return new XorpMemberCallback6B2(o, p, ba1, ba2); }; }; /** * Factory function that creates a callback object targetted at a * member function with 6 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback6::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2), BA1 ba1, BA2 ba2) { return XorpMemberCallbackFactory6B2::True>::make(o, p, ba1, ba2); } /** * Factory function that creates a callback object targetted at a * member function with 6 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback6::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2), BA1 ba1, BA2 ba2) { return XorpMemberCallbackFactory6B2::True>::make(&o, p, ba1, ba2); } /** * @short Callback object for const member methods with 6 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstMemberCallback6B2 : public XorpCallback6 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, BA1, BA2) const; XorpConstMemberCallback6B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback6(), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, _ba1, _ba2); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for void const member methods with 6 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstMemberCallback6B2 : public XorpCallback6 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, BA1, BA2) const; XorpConstMemberCallback6B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback6(), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, _ba1, _ba2); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for const safe member methods with 6 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback6B2 : public XorpConstMemberCallback6B2, public SafeCallbackBase { typedef typename XorpConstMemberCallback6B2::M M; XorpConstSafeMemberCallback6B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpConstMemberCallback6B2(o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback6B2() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { if (valid()) { R r = XorpConstMemberCallback6B2::dispatch(a1, a2, a3, a4, a5, a6); return r; } } }; /** * @short Callback object for void const safe member methods with 6 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback6B2 : public XorpConstMemberCallback6B2, public SafeCallbackBase { typedef typename XorpConstMemberCallback6B2::M M; XorpConstSafeMemberCallback6B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpConstMemberCallback6B2(o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback6B2() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { if (valid()) { XorpConstMemberCallback6B2::dispatch(a1, a2, a3, a4, a5, a6); } } }; template struct XorpConstMemberCallbackFactory6B2 { static XorpConstMemberCallback6B2* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2) const, BA1 ba1, BA2 ba2) { return new XorpConstSafeMemberCallback6B2(o, p, ba1, ba2); } }; template struct XorpConstMemberCallbackFactory6B2 { static XorpConstMemberCallback6B2* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2) const, BA1 ba1, BA2 ba2) { return new XorpConstMemberCallback6B2(o, p, ba1, ba2); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 6 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback6::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2) const, BA1 ba1, BA2 ba2) { return XorpConstMemberCallbackFactory6B2::True>::make(o, p, ba1, ba2); } /** * Factory function that creates a callback object targetted at a * const member function with 6 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback6::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2) const, BA1 ba1, BA2 ba2) { return XorpConstMemberCallbackFactory6B2::True>::make(&o, p, ba1, ba2); } /** * @short Callback object for functions with 6 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpFunctionCallback6B3 : public XorpCallback6 { typedef R (*F)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3); XorpFunctionCallback6B3(F f, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback6(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { R r = (*_f)(a1, a2, a3, a4, a5, a6, _ba1, _ba2, _ba3); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; }; /** * @short Callback object for void functions with 6 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpFunctionCallback6B3 : public XorpCallback6 { typedef void (*F)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3); XorpFunctionCallback6B3(F f, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback6(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { (*_f)(a1, a2, a3, a4, a5, a6, _ba1, _ba2, _ba3); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; }; /** * Factory function that creates a callback object targetted at a * function with 6 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback6::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return typename XorpCallback6::RefPtr(new XorpFunctionCallback6B3(f, ba1, ba2, ba3)); } /** * @short Callback object for member methods with 6 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpMemberCallback6B3 : public XorpCallback6 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3) ; XorpMemberCallback6B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback6(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, _ba1, _ba2, _ba3); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for void member methods with 6 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpMemberCallback6B3 : public XorpCallback6 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3) ; XorpMemberCallback6B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback6(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, _ba1, _ba2, _ba3); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for safe member methods with 6 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpSafeMemberCallback6B3 : public XorpMemberCallback6B3, public SafeCallbackBase { typedef typename XorpMemberCallback6B3::M M; XorpSafeMemberCallback6B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpMemberCallback6B3(o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpSafeMemberCallback6B3() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { if (valid()) { R r = XorpMemberCallback6B3::dispatch(a1, a2, a3, a4, a5, a6); return r; } } }; /** * @short Callback object for void safe member methods with 6 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpSafeMemberCallback6B3 : public XorpMemberCallback6B3, public SafeCallbackBase { typedef typename XorpMemberCallback6B3::M M; XorpSafeMemberCallback6B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpMemberCallback6B3(o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpSafeMemberCallback6B3() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { if (valid()) { XorpMemberCallback6B3::dispatch(a1, a2, a3, a4, a5, a6); } } }; template struct XorpMemberCallbackFactory6B3 { static XorpMemberCallback6B3* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpSafeMemberCallback6B3(o, p, ba1, ba2, ba3); } }; template struct XorpMemberCallbackFactory6B3 { static XorpMemberCallback6B3* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpMemberCallback6B3(o, p, ba1, ba2, ba3); }; }; /** * Factory function that creates a callback object targetted at a * member function with 6 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback6::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return XorpMemberCallbackFactory6B3::True>::make(o, p, ba1, ba2, ba3); } /** * Factory function that creates a callback object targetted at a * member function with 6 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback6::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return XorpMemberCallbackFactory6B3::True>::make(&o, p, ba1, ba2, ba3); } /** * @short Callback object for const member methods with 6 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstMemberCallback6B3 : public XorpCallback6 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3) const; XorpConstMemberCallback6B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback6(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, _ba1, _ba2, _ba3); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for void const member methods with 6 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstMemberCallback6B3 : public XorpCallback6 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3) const; XorpConstMemberCallback6B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback6(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, _ba1, _ba2, _ba3); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for const safe member methods with 6 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback6B3 : public XorpConstMemberCallback6B3, public SafeCallbackBase { typedef typename XorpConstMemberCallback6B3::M M; XorpConstSafeMemberCallback6B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpConstMemberCallback6B3(o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback6B3() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { if (valid()) { R r = XorpConstMemberCallback6B3::dispatch(a1, a2, a3, a4, a5, a6); return r; } } }; /** * @short Callback object for void const safe member methods with 6 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback6B3 : public XorpConstMemberCallback6B3, public SafeCallbackBase { typedef typename XorpConstMemberCallback6B3::M M; XorpConstSafeMemberCallback6B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpConstMemberCallback6B3(o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback6B3() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { if (valid()) { XorpConstMemberCallback6B3::dispatch(a1, a2, a3, a4, a5, a6); } } }; template struct XorpConstMemberCallbackFactory6B3 { static XorpConstMemberCallback6B3* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpConstSafeMemberCallback6B3(o, p, ba1, ba2, ba3); } }; template struct XorpConstMemberCallbackFactory6B3 { static XorpConstMemberCallback6B3* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpConstMemberCallback6B3(o, p, ba1, ba2, ba3); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 6 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback6::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return XorpConstMemberCallbackFactory6B3::True>::make(o, p, ba1, ba2, ba3); } /** * Factory function that creates a callback object targetted at a * const member function with 6 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback6::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return XorpConstMemberCallbackFactory6B3::True>::make(&o, p, ba1, ba2, ba3); } /** * @short Callback object for functions with 6 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpFunctionCallback6B4 : public XorpCallback6 { typedef R (*F)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4); XorpFunctionCallback6B4(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback6(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { R r = (*_f)(a1, a2, a3, a4, a5, a6, _ba1, _ba2, _ba3, _ba4); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; }; /** * @short Callback object for void functions with 6 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpFunctionCallback6B4 : public XorpCallback6 { typedef void (*F)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4); XorpFunctionCallback6B4(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback6(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { (*_f)(a1, a2, a3, a4, a5, a6, _ba1, _ba2, _ba3, _ba4); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; }; /** * Factory function that creates a callback object targetted at a * function with 6 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback6::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return typename XorpCallback6::RefPtr(new XorpFunctionCallback6B4(f, ba1, ba2, ba3, ba4)); } /** * @short Callback object for member methods with 6 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpMemberCallback6B4 : public XorpCallback6 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4) ; XorpMemberCallback6B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback6(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, _ba1, _ba2, _ba3, _ba4); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for void member methods with 6 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpMemberCallback6B4 : public XorpCallback6 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4) ; XorpMemberCallback6B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback6(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, _ba1, _ba2, _ba3, _ba4); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for safe member methods with 6 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpSafeMemberCallback6B4 : public XorpMemberCallback6B4, public SafeCallbackBase { typedef typename XorpMemberCallback6B4::M M; XorpSafeMemberCallback6B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpMemberCallback6B4(o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpSafeMemberCallback6B4() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { if (valid()) { R r = XorpMemberCallback6B4::dispatch(a1, a2, a3, a4, a5, a6); return r; } } }; /** * @short Callback object for void safe member methods with 6 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpSafeMemberCallback6B4 : public XorpMemberCallback6B4, public SafeCallbackBase { typedef typename XorpMemberCallback6B4::M M; XorpSafeMemberCallback6B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpMemberCallback6B4(o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpSafeMemberCallback6B4() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { if (valid()) { XorpMemberCallback6B4::dispatch(a1, a2, a3, a4, a5, a6); } } }; template struct XorpMemberCallbackFactory6B4 { static XorpMemberCallback6B4* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpSafeMemberCallback6B4(o, p, ba1, ba2, ba3, ba4); } }; template struct XorpMemberCallbackFactory6B4 { static XorpMemberCallback6B4* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpMemberCallback6B4(o, p, ba1, ba2, ba3, ba4); }; }; /** * Factory function that creates a callback object targetted at a * member function with 6 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback6::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpMemberCallbackFactory6B4::True>::make(o, p, ba1, ba2, ba3, ba4); } /** * Factory function that creates a callback object targetted at a * member function with 6 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback6::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpMemberCallbackFactory6B4::True>::make(&o, p, ba1, ba2, ba3, ba4); } /** * @short Callback object for const member methods with 6 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstMemberCallback6B4 : public XorpCallback6 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4) const; XorpConstMemberCallback6B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback6(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, _ba1, _ba2, _ba3, _ba4); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for void const member methods with 6 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstMemberCallback6B4 : public XorpCallback6 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4) const; XorpConstMemberCallback6B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback6(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, _ba1, _ba2, _ba3, _ba4); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for const safe member methods with 6 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback6B4 : public XorpConstMemberCallback6B4, public SafeCallbackBase { typedef typename XorpConstMemberCallback6B4::M M; XorpConstSafeMemberCallback6B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpConstMemberCallback6B4(o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback6B4() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { if (valid()) { R r = XorpConstMemberCallback6B4::dispatch(a1, a2, a3, a4, a5, a6); return r; } } }; /** * @short Callback object for void const safe member methods with 6 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback6B4 : public XorpConstMemberCallback6B4, public SafeCallbackBase { typedef typename XorpConstMemberCallback6B4::M M; XorpConstSafeMemberCallback6B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpConstMemberCallback6B4(o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback6B4() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { if (valid()) { XorpConstMemberCallback6B4::dispatch(a1, a2, a3, a4, a5, a6); } } }; template struct XorpConstMemberCallbackFactory6B4 { static XorpConstMemberCallback6B4* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpConstSafeMemberCallback6B4(o, p, ba1, ba2, ba3, ba4); } }; template struct XorpConstMemberCallbackFactory6B4 { static XorpConstMemberCallback6B4* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpConstMemberCallback6B4(o, p, ba1, ba2, ba3, ba4); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 6 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback6::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpConstMemberCallbackFactory6B4::True>::make(o, p, ba1, ba2, ba3, ba4); } /** * Factory function that creates a callback object targetted at a * const member function with 6 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback6::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpConstMemberCallbackFactory6B4::True>::make(&o, p, ba1, ba2, ba3, ba4); } /** * @short Callback object for functions with 6 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpFunctionCallback6B5 : public XorpCallback6 { typedef R (*F)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4, BA5); XorpFunctionCallback6B5(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback6(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { R r = (*_f)(a1, a2, a3, a4, a5, a6, _ba1, _ba2, _ba3, _ba4, _ba5); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; }; /** * @short Callback object for void functions with 6 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpFunctionCallback6B5 : public XorpCallback6 { typedef void (*F)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4, BA5); XorpFunctionCallback6B5(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback6(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { (*_f)(a1, a2, a3, a4, a5, a6, _ba1, _ba2, _ba3, _ba4, _ba5); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; }; /** * Factory function that creates a callback object targetted at a * function with 6 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback6::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return typename XorpCallback6::RefPtr(new XorpFunctionCallback6B5(f, ba1, ba2, ba3, ba4, ba5)); } /** * @short Callback object for member methods with 6 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpMemberCallback6B5 : public XorpCallback6 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4, BA5) ; XorpMemberCallback6B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback6(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, _ba1, _ba2, _ba3, _ba4, _ba5); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for void member methods with 6 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpMemberCallback6B5 : public XorpCallback6 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4, BA5) ; XorpMemberCallback6B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback6(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, _ba1, _ba2, _ba3, _ba4, _ba5); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for safe member methods with 6 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpSafeMemberCallback6B5 : public XorpMemberCallback6B5, public SafeCallbackBase { typedef typename XorpMemberCallback6B5::M M; XorpSafeMemberCallback6B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpMemberCallback6B5(o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpSafeMemberCallback6B5() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { if (valid()) { R r = XorpMemberCallback6B5::dispatch(a1, a2, a3, a4, a5, a6); return r; } } }; /** * @short Callback object for void safe member methods with 6 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpSafeMemberCallback6B5 : public XorpMemberCallback6B5, public SafeCallbackBase { typedef typename XorpMemberCallback6B5::M M; XorpSafeMemberCallback6B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpMemberCallback6B5(o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpSafeMemberCallback6B5() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { if (valid()) { XorpMemberCallback6B5::dispatch(a1, a2, a3, a4, a5, a6); } } }; template struct XorpMemberCallbackFactory6B5 { static XorpMemberCallback6B5* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpSafeMemberCallback6B5(o, p, ba1, ba2, ba3, ba4, ba5); } }; template struct XorpMemberCallbackFactory6B5 { static XorpMemberCallback6B5* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpMemberCallback6B5(o, p, ba1, ba2, ba3, ba4, ba5); }; }; /** * Factory function that creates a callback object targetted at a * member function with 6 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback6::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpMemberCallbackFactory6B5::True>::make(o, p, ba1, ba2, ba3, ba4, ba5); } /** * Factory function that creates a callback object targetted at a * member function with 6 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback6::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpMemberCallbackFactory6B5::True>::make(&o, p, ba1, ba2, ba3, ba4, ba5); } /** * @short Callback object for const member methods with 6 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstMemberCallback6B5 : public XorpCallback6 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4, BA5) const; XorpConstMemberCallback6B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback6(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, _ba1, _ba2, _ba3, _ba4, _ba5); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for void const member methods with 6 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstMemberCallback6B5 : public XorpCallback6 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4, BA5) const; XorpConstMemberCallback6B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback6(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, _ba1, _ba2, _ba3, _ba4, _ba5); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for const safe member methods with 6 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback6B5 : public XorpConstMemberCallback6B5, public SafeCallbackBase { typedef typename XorpConstMemberCallback6B5::M M; XorpConstSafeMemberCallback6B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpConstMemberCallback6B5(o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback6B5() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { if (valid()) { R r = XorpConstMemberCallback6B5::dispatch(a1, a2, a3, a4, a5, a6); return r; } } }; /** * @short Callback object for void const safe member methods with 6 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback6B5 : public XorpConstMemberCallback6B5, public SafeCallbackBase { typedef typename XorpConstMemberCallback6B5::M M; XorpConstSafeMemberCallback6B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpConstMemberCallback6B5(o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback6B5() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { if (valid()) { XorpConstMemberCallback6B5::dispatch(a1, a2, a3, a4, a5, a6); } } }; template struct XorpConstMemberCallbackFactory6B5 { static XorpConstMemberCallback6B5* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpConstSafeMemberCallback6B5(o, p, ba1, ba2, ba3, ba4, ba5); } }; template struct XorpConstMemberCallbackFactory6B5 { static XorpConstMemberCallback6B5* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpConstMemberCallback6B5(o, p, ba1, ba2, ba3, ba4, ba5); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 6 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback6::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpConstMemberCallbackFactory6B5::True>::make(o, p, ba1, ba2, ba3, ba4, ba5); } /** * Factory function that creates a callback object targetted at a * const member function with 6 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback6::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpConstMemberCallbackFactory6B5::True>::make(&o, p, ba1, ba2, ba3, ba4, ba5); } /** * @short Callback object for functions with 6 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpFunctionCallback6B6 : public XorpCallback6 { typedef R (*F)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4, BA5, BA6); XorpFunctionCallback6B6(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback6(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { R r = (*_f)(a1, a2, a3, a4, a5, a6, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; BA6 _ba6; }; /** * @short Callback object for void functions with 6 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpFunctionCallback6B6 : public XorpCallback6 { typedef void (*F)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4, BA5, BA6); XorpFunctionCallback6B6(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback6(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { (*_f)(a1, a2, a3, a4, a5, a6, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; BA6 _ba6; }; /** * Factory function that creates a callback object targetted at a * function with 6 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback6::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return typename XorpCallback6::RefPtr(new XorpFunctionCallback6B6(f, ba1, ba2, ba3, ba4, ba5, ba6)); } /** * @short Callback object for member methods with 6 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpMemberCallback6B6 : public XorpCallback6 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4, BA5, BA6) ; XorpMemberCallback6B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback6(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for void member methods with 6 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpMemberCallback6B6 : public XorpCallback6 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4, BA5, BA6) ; XorpMemberCallback6B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback6(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for safe member methods with 6 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpSafeMemberCallback6B6 : public XorpMemberCallback6B6, public SafeCallbackBase { typedef typename XorpMemberCallback6B6::M M; XorpSafeMemberCallback6B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpMemberCallback6B6(o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpSafeMemberCallback6B6() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { if (valid()) { R r = XorpMemberCallback6B6::dispatch(a1, a2, a3, a4, a5, a6); return r; } } }; /** * @short Callback object for void safe member methods with 6 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpSafeMemberCallback6B6 : public XorpMemberCallback6B6, public SafeCallbackBase { typedef typename XorpMemberCallback6B6::M M; XorpSafeMemberCallback6B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpMemberCallback6B6(o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpSafeMemberCallback6B6() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { if (valid()) { XorpMemberCallback6B6::dispatch(a1, a2, a3, a4, a5, a6); } } }; template struct XorpMemberCallbackFactory6B6 { static XorpMemberCallback6B6* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpSafeMemberCallback6B6(o, p, ba1, ba2, ba3, ba4, ba5, ba6); } }; template struct XorpMemberCallbackFactory6B6 { static XorpMemberCallback6B6* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpMemberCallback6B6(o, p, ba1, ba2, ba3, ba4, ba5, ba6); }; }; /** * Factory function that creates a callback object targetted at a * member function with 6 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback6::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpMemberCallbackFactory6B6::True>::make(o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * Factory function that creates a callback object targetted at a * member function with 6 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback6::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpMemberCallbackFactory6B6::True>::make(&o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * @short Callback object for const member methods with 6 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstMemberCallback6B6 : public XorpCallback6 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4, BA5, BA6) const; XorpConstMemberCallback6B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback6(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for void const member methods with 6 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstMemberCallback6B6 : public XorpCallback6 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4, BA5, BA6) const; XorpConstMemberCallback6B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback6(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for const safe member methods with 6 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback6B6 : public XorpConstMemberCallback6B6, public SafeCallbackBase { typedef typename XorpConstMemberCallback6B6::M M; XorpConstSafeMemberCallback6B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpConstMemberCallback6B6(o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback6B6() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { if (valid()) { R r = XorpConstMemberCallback6B6::dispatch(a1, a2, a3, a4, a5, a6); return r; } } }; /** * @short Callback object for void const safe member methods with 6 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback6B6 : public XorpConstMemberCallback6B6, public SafeCallbackBase { typedef typename XorpConstMemberCallback6B6::M M; XorpConstSafeMemberCallback6B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpConstMemberCallback6B6(o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback6B6() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { if (valid()) { XorpConstMemberCallback6B6::dispatch(a1, a2, a3, a4, a5, a6); } } }; template struct XorpConstMemberCallbackFactory6B6 { static XorpConstMemberCallback6B6* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpConstSafeMemberCallback6B6(o, p, ba1, ba2, ba3, ba4, ba5, ba6); } }; template struct XorpConstMemberCallbackFactory6B6 { static XorpConstMemberCallback6B6* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpConstMemberCallback6B6(o, p, ba1, ba2, ba3, ba4, ba5, ba6); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 6 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback6::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpConstMemberCallbackFactory6B6::True>::make(o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * Factory function that creates a callback object targetted at a * const member function with 6 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback6::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpConstMemberCallbackFactory6B6::True>::make(&o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /////////////////////////////////////////////////////////////////////////////// // // Code relating to callbacks with 7 late args // /** * @short Base class for callbacks with 7 dispatch time args. */ template struct XorpCallback7 { typedef ref_ptr RefPtr; virtual ~XorpCallback7() {} virtual R dispatch(A1, A2, A3, A4, A5, A6, A7) = 0; }; /** * @short Callback object for functions with 7 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpFunctionCallback7B0 : public XorpCallback7 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7); XorpFunctionCallback7B0(F f) : XorpCallback7(), _f(f) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { R r = (*_f)(a1, a2, a3, a4, a5, a6, a7); return r; } protected: F _f; }; /** * @short Callback object for void functions with 7 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpFunctionCallback7B0 : public XorpCallback7 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7); XorpFunctionCallback7B0(F f) : XorpCallback7(), _f(f) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { (*_f)(a1, a2, a3, a4, a5, a6, a7); } protected: F _f; }; /** * Factory function that creates a callback object targetted at a * function with 7 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback7::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, A7)) { return typename XorpCallback7::RefPtr(new XorpFunctionCallback7B0(f)); } /** * @short Callback object for member methods with 7 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpMemberCallback7B0 : public XorpCallback7 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7) ; XorpMemberCallback7B0(O* o, M m) : XorpCallback7(), _o(o), _m(m) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for void member methods with 7 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpMemberCallback7B0 : public XorpCallback7 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7) ; XorpMemberCallback7B0(O* o, M m) : XorpCallback7(), _o(o), _m(m) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7); } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for safe member methods with 7 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpSafeMemberCallback7B0 : public XorpMemberCallback7B0, public SafeCallbackBase { typedef typename XorpMemberCallback7B0::M M; XorpSafeMemberCallback7B0(O* o, M m) : XorpMemberCallback7B0(o, m), SafeCallbackBase(o) {} ~XorpSafeMemberCallback7B0() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { if (valid()) { R r = XorpMemberCallback7B0::dispatch(a1, a2, a3, a4, a5, a6, a7); return r; } } }; /** * @short Callback object for void safe member methods with 7 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpSafeMemberCallback7B0 : public XorpMemberCallback7B0, public SafeCallbackBase { typedef typename XorpMemberCallback7B0::M M; XorpSafeMemberCallback7B0(O* o, M m) : XorpMemberCallback7B0(o, m), SafeCallbackBase(o) {} ~XorpSafeMemberCallback7B0() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { if (valid()) { XorpMemberCallback7B0::dispatch(a1, a2, a3, a4, a5, a6, a7); } } }; template struct XorpMemberCallbackFactory7B0 { static XorpMemberCallback7B0* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7)) { return new XorpSafeMemberCallback7B0(o, p); } }; template struct XorpMemberCallbackFactory7B0 { static XorpMemberCallback7B0* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7)) { return new XorpMemberCallback7B0(o, p); }; }; /** * Factory function that creates a callback object targetted at a * member function with 7 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback7::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7)) { return XorpMemberCallbackFactory7B0::True>::make(o, p); } /** * Factory function that creates a callback object targetted at a * member function with 7 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback7::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7)) { return XorpMemberCallbackFactory7B0::True>::make(&o, p); } /** * @short Callback object for const member methods with 7 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstMemberCallback7B0 : public XorpCallback7 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7) const; XorpConstMemberCallback7B0(O* o, M m) : XorpCallback7(), _o(o), _m(m) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for void const member methods with 7 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstMemberCallback7B0 : public XorpCallback7 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7) const; XorpConstMemberCallback7B0(O* o, M m) : XorpCallback7(), _o(o), _m(m) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7); } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for const safe member methods with 7 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback7B0 : public XorpConstMemberCallback7B0, public SafeCallbackBase { typedef typename XorpConstMemberCallback7B0::M M; XorpConstSafeMemberCallback7B0(O* o, M m) : XorpConstMemberCallback7B0(o, m), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback7B0() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { if (valid()) { R r = XorpConstMemberCallback7B0::dispatch(a1, a2, a3, a4, a5, a6, a7); return r; } } }; /** * @short Callback object for void const safe member methods with 7 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback7B0 : public XorpConstMemberCallback7B0, public SafeCallbackBase { typedef typename XorpConstMemberCallback7B0::M M; XorpConstSafeMemberCallback7B0(O* o, M m) : XorpConstMemberCallback7B0(o, m), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback7B0() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { if (valid()) { XorpConstMemberCallback7B0::dispatch(a1, a2, a3, a4, a5, a6, a7); } } }; template struct XorpConstMemberCallbackFactory7B0 { static XorpConstMemberCallback7B0* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7) const) { return new XorpConstSafeMemberCallback7B0(o, p); } }; template struct XorpConstMemberCallbackFactory7B0 { static XorpConstMemberCallback7B0* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7) const) { return new XorpConstMemberCallback7B0(o, p); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 7 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback7::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7) const) { return XorpConstMemberCallbackFactory7B0::True>::make(o, p); } /** * Factory function that creates a callback object targetted at a * const member function with 7 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback7::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7) const) { return XorpConstMemberCallbackFactory7B0::True>::make(&o, p); } /** * @short Callback object for functions with 7 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpFunctionCallback7B1 : public XorpCallback7 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, BA1); XorpFunctionCallback7B1(F f, BA1 ba1) : XorpCallback7(), _f(f), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, _ba1); return r; } protected: F _f; BA1 _ba1; }; /** * @short Callback object for void functions with 7 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpFunctionCallback7B1 : public XorpCallback7 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, BA1); XorpFunctionCallback7B1(F f, BA1 ba1) : XorpCallback7(), _f(f), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { (*_f)(a1, a2, a3, a4, a5, a6, a7, _ba1); } protected: F _f; BA1 _ba1; }; /** * Factory function that creates a callback object targetted at a * function with 7 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback7::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, A7, BA1), BA1 ba1) { return typename XorpCallback7::RefPtr(new XorpFunctionCallback7B1(f, ba1)); } /** * @short Callback object for member methods with 7 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpMemberCallback7B1 : public XorpCallback7 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, BA1) ; XorpMemberCallback7B1(O* o, M m, BA1 ba1) : XorpCallback7(), _o(o), _m(m), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, _ba1); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for void member methods with 7 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpMemberCallback7B1 : public XorpCallback7 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, BA1) ; XorpMemberCallback7B1(O* o, M m, BA1 ba1) : XorpCallback7(), _o(o), _m(m), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, _ba1); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for safe member methods with 7 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpSafeMemberCallback7B1 : public XorpMemberCallback7B1, public SafeCallbackBase { typedef typename XorpMemberCallback7B1::M M; XorpSafeMemberCallback7B1(O* o, M m, BA1 ba1) : XorpMemberCallback7B1(o, m, ba1), SafeCallbackBase(o) {} ~XorpSafeMemberCallback7B1() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { if (valid()) { R r = XorpMemberCallback7B1::dispatch(a1, a2, a3, a4, a5, a6, a7); return r; } } }; /** * @short Callback object for void safe member methods with 7 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpSafeMemberCallback7B1 : public XorpMemberCallback7B1, public SafeCallbackBase { typedef typename XorpMemberCallback7B1::M M; XorpSafeMemberCallback7B1(O* o, M m, BA1 ba1) : XorpMemberCallback7B1(o, m, ba1), SafeCallbackBase(o) {} ~XorpSafeMemberCallback7B1() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { if (valid()) { XorpMemberCallback7B1::dispatch(a1, a2, a3, a4, a5, a6, a7); } } }; template struct XorpMemberCallbackFactory7B1 { static XorpMemberCallback7B1* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1), BA1 ba1) { return new XorpSafeMemberCallback7B1(o, p, ba1); } }; template struct XorpMemberCallbackFactory7B1 { static XorpMemberCallback7B1* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1), BA1 ba1) { return new XorpMemberCallback7B1(o, p, ba1); }; }; /** * Factory function that creates a callback object targetted at a * member function with 7 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback7::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1), BA1 ba1) { return XorpMemberCallbackFactory7B1::True>::make(o, p, ba1); } /** * Factory function that creates a callback object targetted at a * member function with 7 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback7::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1), BA1 ba1) { return XorpMemberCallbackFactory7B1::True>::make(&o, p, ba1); } /** * @short Callback object for const member methods with 7 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstMemberCallback7B1 : public XorpCallback7 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, BA1) const; XorpConstMemberCallback7B1(O* o, M m, BA1 ba1) : XorpCallback7(), _o(o), _m(m), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, _ba1); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for void const member methods with 7 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstMemberCallback7B1 : public XorpCallback7 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, BA1) const; XorpConstMemberCallback7B1(O* o, M m, BA1 ba1) : XorpCallback7(), _o(o), _m(m), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, _ba1); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for const safe member methods with 7 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback7B1 : public XorpConstMemberCallback7B1, public SafeCallbackBase { typedef typename XorpConstMemberCallback7B1::M M; XorpConstSafeMemberCallback7B1(O* o, M m, BA1 ba1) : XorpConstMemberCallback7B1(o, m, ba1), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback7B1() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { if (valid()) { R r = XorpConstMemberCallback7B1::dispatch(a1, a2, a3, a4, a5, a6, a7); return r; } } }; /** * @short Callback object for void const safe member methods with 7 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback7B1 : public XorpConstMemberCallback7B1, public SafeCallbackBase { typedef typename XorpConstMemberCallback7B1::M M; XorpConstSafeMemberCallback7B1(O* o, M m, BA1 ba1) : XorpConstMemberCallback7B1(o, m, ba1), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback7B1() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { if (valid()) { XorpConstMemberCallback7B1::dispatch(a1, a2, a3, a4, a5, a6, a7); } } }; template struct XorpConstMemberCallbackFactory7B1 { static XorpConstMemberCallback7B1* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1) const, BA1 ba1) { return new XorpConstSafeMemberCallback7B1(o, p, ba1); } }; template struct XorpConstMemberCallbackFactory7B1 { static XorpConstMemberCallback7B1* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1) const, BA1 ba1) { return new XorpConstMemberCallback7B1(o, p, ba1); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 7 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback7::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1) const, BA1 ba1) { return XorpConstMemberCallbackFactory7B1::True>::make(o, p, ba1); } /** * Factory function that creates a callback object targetted at a * const member function with 7 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback7::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1) const, BA1 ba1) { return XorpConstMemberCallbackFactory7B1::True>::make(&o, p, ba1); } /** * @short Callback object for functions with 7 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpFunctionCallback7B2 : public XorpCallback7 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2); XorpFunctionCallback7B2(F f, BA1 ba1, BA2 ba2) : XorpCallback7(), _f(f), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, _ba1, _ba2); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; }; /** * @short Callback object for void functions with 7 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpFunctionCallback7B2 : public XorpCallback7 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2); XorpFunctionCallback7B2(F f, BA1 ba1, BA2 ba2) : XorpCallback7(), _f(f), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { (*_f)(a1, a2, a3, a4, a5, a6, a7, _ba1, _ba2); } protected: F _f; BA1 _ba1; BA2 _ba2; }; /** * Factory function that creates a callback object targetted at a * function with 7 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback7::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2), BA1 ba1, BA2 ba2) { return typename XorpCallback7::RefPtr(new XorpFunctionCallback7B2(f, ba1, ba2)); } /** * @short Callback object for member methods with 7 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpMemberCallback7B2 : public XorpCallback7 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2) ; XorpMemberCallback7B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback7(), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, _ba1, _ba2); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for void member methods with 7 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpMemberCallback7B2 : public XorpCallback7 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2) ; XorpMemberCallback7B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback7(), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, _ba1, _ba2); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for safe member methods with 7 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpSafeMemberCallback7B2 : public XorpMemberCallback7B2, public SafeCallbackBase { typedef typename XorpMemberCallback7B2::M M; XorpSafeMemberCallback7B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpMemberCallback7B2(o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpSafeMemberCallback7B2() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { if (valid()) { R r = XorpMemberCallback7B2::dispatch(a1, a2, a3, a4, a5, a6, a7); return r; } } }; /** * @short Callback object for void safe member methods with 7 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpSafeMemberCallback7B2 : public XorpMemberCallback7B2, public SafeCallbackBase { typedef typename XorpMemberCallback7B2::M M; XorpSafeMemberCallback7B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpMemberCallback7B2(o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpSafeMemberCallback7B2() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { if (valid()) { XorpMemberCallback7B2::dispatch(a1, a2, a3, a4, a5, a6, a7); } } }; template struct XorpMemberCallbackFactory7B2 { static XorpMemberCallback7B2* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2), BA1 ba1, BA2 ba2) { return new XorpSafeMemberCallback7B2(o, p, ba1, ba2); } }; template struct XorpMemberCallbackFactory7B2 { static XorpMemberCallback7B2* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2), BA1 ba1, BA2 ba2) { return new XorpMemberCallback7B2(o, p, ba1, ba2); }; }; /** * Factory function that creates a callback object targetted at a * member function with 7 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback7::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2), BA1 ba1, BA2 ba2) { return XorpMemberCallbackFactory7B2::True>::make(o, p, ba1, ba2); } /** * Factory function that creates a callback object targetted at a * member function with 7 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback7::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2), BA1 ba1, BA2 ba2) { return XorpMemberCallbackFactory7B2::True>::make(&o, p, ba1, ba2); } /** * @short Callback object for const member methods with 7 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstMemberCallback7B2 : public XorpCallback7 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2) const; XorpConstMemberCallback7B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback7(), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, _ba1, _ba2); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for void const member methods with 7 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstMemberCallback7B2 : public XorpCallback7 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2) const; XorpConstMemberCallback7B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback7(), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, _ba1, _ba2); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for const safe member methods with 7 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback7B2 : public XorpConstMemberCallback7B2, public SafeCallbackBase { typedef typename XorpConstMemberCallback7B2::M M; XorpConstSafeMemberCallback7B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpConstMemberCallback7B2(o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback7B2() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { if (valid()) { R r = XorpConstMemberCallback7B2::dispatch(a1, a2, a3, a4, a5, a6, a7); return r; } } }; /** * @short Callback object for void const safe member methods with 7 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback7B2 : public XorpConstMemberCallback7B2, public SafeCallbackBase { typedef typename XorpConstMemberCallback7B2::M M; XorpConstSafeMemberCallback7B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpConstMemberCallback7B2(o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback7B2() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { if (valid()) { XorpConstMemberCallback7B2::dispatch(a1, a2, a3, a4, a5, a6, a7); } } }; template struct XorpConstMemberCallbackFactory7B2 { static XorpConstMemberCallback7B2* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2) const, BA1 ba1, BA2 ba2) { return new XorpConstSafeMemberCallback7B2(o, p, ba1, ba2); } }; template struct XorpConstMemberCallbackFactory7B2 { static XorpConstMemberCallback7B2* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2) const, BA1 ba1, BA2 ba2) { return new XorpConstMemberCallback7B2(o, p, ba1, ba2); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 7 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback7::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2) const, BA1 ba1, BA2 ba2) { return XorpConstMemberCallbackFactory7B2::True>::make(o, p, ba1, ba2); } /** * Factory function that creates a callback object targetted at a * const member function with 7 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback7::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2) const, BA1 ba1, BA2 ba2) { return XorpConstMemberCallbackFactory7B2::True>::make(&o, p, ba1, ba2); } /** * @short Callback object for functions with 7 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpFunctionCallback7B3 : public XorpCallback7 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3); XorpFunctionCallback7B3(F f, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback7(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, _ba1, _ba2, _ba3); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; }; /** * @short Callback object for void functions with 7 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpFunctionCallback7B3 : public XorpCallback7 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3); XorpFunctionCallback7B3(F f, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback7(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { (*_f)(a1, a2, a3, a4, a5, a6, a7, _ba1, _ba2, _ba3); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; }; /** * Factory function that creates a callback object targetted at a * function with 7 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback7::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return typename XorpCallback7::RefPtr(new XorpFunctionCallback7B3(f, ba1, ba2, ba3)); } /** * @short Callback object for member methods with 7 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpMemberCallback7B3 : public XorpCallback7 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3) ; XorpMemberCallback7B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback7(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, _ba1, _ba2, _ba3); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for void member methods with 7 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpMemberCallback7B3 : public XorpCallback7 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3) ; XorpMemberCallback7B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback7(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, _ba1, _ba2, _ba3); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for safe member methods with 7 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpSafeMemberCallback7B3 : public XorpMemberCallback7B3, public SafeCallbackBase { typedef typename XorpMemberCallback7B3::M M; XorpSafeMemberCallback7B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpMemberCallback7B3(o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpSafeMemberCallback7B3() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { if (valid()) { R r = XorpMemberCallback7B3::dispatch(a1, a2, a3, a4, a5, a6, a7); return r; } } }; /** * @short Callback object for void safe member methods with 7 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpSafeMemberCallback7B3 : public XorpMemberCallback7B3, public SafeCallbackBase { typedef typename XorpMemberCallback7B3::M M; XorpSafeMemberCallback7B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpMemberCallback7B3(o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpSafeMemberCallback7B3() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { if (valid()) { XorpMemberCallback7B3::dispatch(a1, a2, a3, a4, a5, a6, a7); } } }; template struct XorpMemberCallbackFactory7B3 { static XorpMemberCallback7B3* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpSafeMemberCallback7B3(o, p, ba1, ba2, ba3); } }; template struct XorpMemberCallbackFactory7B3 { static XorpMemberCallback7B3* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpMemberCallback7B3(o, p, ba1, ba2, ba3); }; }; /** * Factory function that creates a callback object targetted at a * member function with 7 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback7::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return XorpMemberCallbackFactory7B3::True>::make(o, p, ba1, ba2, ba3); } /** * Factory function that creates a callback object targetted at a * member function with 7 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback7::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return XorpMemberCallbackFactory7B3::True>::make(&o, p, ba1, ba2, ba3); } /** * @short Callback object for const member methods with 7 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstMemberCallback7B3 : public XorpCallback7 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3) const; XorpConstMemberCallback7B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback7(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, _ba1, _ba2, _ba3); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for void const member methods with 7 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstMemberCallback7B3 : public XorpCallback7 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3) const; XorpConstMemberCallback7B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback7(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, _ba1, _ba2, _ba3); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for const safe member methods with 7 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback7B3 : public XorpConstMemberCallback7B3, public SafeCallbackBase { typedef typename XorpConstMemberCallback7B3::M M; XorpConstSafeMemberCallback7B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpConstMemberCallback7B3(o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback7B3() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { if (valid()) { R r = XorpConstMemberCallback7B3::dispatch(a1, a2, a3, a4, a5, a6, a7); return r; } } }; /** * @short Callback object for void const safe member methods with 7 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback7B3 : public XorpConstMemberCallback7B3, public SafeCallbackBase { typedef typename XorpConstMemberCallback7B3::M M; XorpConstSafeMemberCallback7B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpConstMemberCallback7B3(o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback7B3() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { if (valid()) { XorpConstMemberCallback7B3::dispatch(a1, a2, a3, a4, a5, a6, a7); } } }; template struct XorpConstMemberCallbackFactory7B3 { static XorpConstMemberCallback7B3* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpConstSafeMemberCallback7B3(o, p, ba1, ba2, ba3); } }; template struct XorpConstMemberCallbackFactory7B3 { static XorpConstMemberCallback7B3* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpConstMemberCallback7B3(o, p, ba1, ba2, ba3); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 7 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback7::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return XorpConstMemberCallbackFactory7B3::True>::make(o, p, ba1, ba2, ba3); } /** * Factory function that creates a callback object targetted at a * const member function with 7 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback7::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return XorpConstMemberCallbackFactory7B3::True>::make(&o, p, ba1, ba2, ba3); } /** * @short Callback object for functions with 7 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpFunctionCallback7B4 : public XorpCallback7 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4); XorpFunctionCallback7B4(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback7(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, _ba1, _ba2, _ba3, _ba4); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; }; /** * @short Callback object for void functions with 7 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpFunctionCallback7B4 : public XorpCallback7 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4); XorpFunctionCallback7B4(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback7(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { (*_f)(a1, a2, a3, a4, a5, a6, a7, _ba1, _ba2, _ba3, _ba4); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; }; /** * Factory function that creates a callback object targetted at a * function with 7 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback7::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return typename XorpCallback7::RefPtr(new XorpFunctionCallback7B4(f, ba1, ba2, ba3, ba4)); } /** * @short Callback object for member methods with 7 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpMemberCallback7B4 : public XorpCallback7 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4) ; XorpMemberCallback7B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback7(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, _ba1, _ba2, _ba3, _ba4); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for void member methods with 7 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpMemberCallback7B4 : public XorpCallback7 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4) ; XorpMemberCallback7B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback7(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, _ba1, _ba2, _ba3, _ba4); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for safe member methods with 7 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpSafeMemberCallback7B4 : public XorpMemberCallback7B4, public SafeCallbackBase { typedef typename XorpMemberCallback7B4::M M; XorpSafeMemberCallback7B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpMemberCallback7B4(o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpSafeMemberCallback7B4() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { if (valid()) { R r = XorpMemberCallback7B4::dispatch(a1, a2, a3, a4, a5, a6, a7); return r; } } }; /** * @short Callback object for void safe member methods with 7 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpSafeMemberCallback7B4 : public XorpMemberCallback7B4, public SafeCallbackBase { typedef typename XorpMemberCallback7B4::M M; XorpSafeMemberCallback7B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpMemberCallback7B4(o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpSafeMemberCallback7B4() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { if (valid()) { XorpMemberCallback7B4::dispatch(a1, a2, a3, a4, a5, a6, a7); } } }; template struct XorpMemberCallbackFactory7B4 { static XorpMemberCallback7B4* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpSafeMemberCallback7B4(o, p, ba1, ba2, ba3, ba4); } }; template struct XorpMemberCallbackFactory7B4 { static XorpMemberCallback7B4* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpMemberCallback7B4(o, p, ba1, ba2, ba3, ba4); }; }; /** * Factory function that creates a callback object targetted at a * member function with 7 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback7::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpMemberCallbackFactory7B4::True>::make(o, p, ba1, ba2, ba3, ba4); } /** * Factory function that creates a callback object targetted at a * member function with 7 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback7::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpMemberCallbackFactory7B4::True>::make(&o, p, ba1, ba2, ba3, ba4); } /** * @short Callback object for const member methods with 7 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstMemberCallback7B4 : public XorpCallback7 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4) const; XorpConstMemberCallback7B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback7(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, _ba1, _ba2, _ba3, _ba4); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for void const member methods with 7 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstMemberCallback7B4 : public XorpCallback7 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4) const; XorpConstMemberCallback7B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback7(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, _ba1, _ba2, _ba3, _ba4); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for const safe member methods with 7 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback7B4 : public XorpConstMemberCallback7B4, public SafeCallbackBase { typedef typename XorpConstMemberCallback7B4::M M; XorpConstSafeMemberCallback7B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpConstMemberCallback7B4(o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback7B4() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { if (valid()) { R r = XorpConstMemberCallback7B4::dispatch(a1, a2, a3, a4, a5, a6, a7); return r; } } }; /** * @short Callback object for void const safe member methods with 7 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback7B4 : public XorpConstMemberCallback7B4, public SafeCallbackBase { typedef typename XorpConstMemberCallback7B4::M M; XorpConstSafeMemberCallback7B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpConstMemberCallback7B4(o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback7B4() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { if (valid()) { XorpConstMemberCallback7B4::dispatch(a1, a2, a3, a4, a5, a6, a7); } } }; template struct XorpConstMemberCallbackFactory7B4 { static XorpConstMemberCallback7B4* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpConstSafeMemberCallback7B4(o, p, ba1, ba2, ba3, ba4); } }; template struct XorpConstMemberCallbackFactory7B4 { static XorpConstMemberCallback7B4* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpConstMemberCallback7B4(o, p, ba1, ba2, ba3, ba4); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 7 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback7::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpConstMemberCallbackFactory7B4::True>::make(o, p, ba1, ba2, ba3, ba4); } /** * Factory function that creates a callback object targetted at a * const member function with 7 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback7::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpConstMemberCallbackFactory7B4::True>::make(&o, p, ba1, ba2, ba3, ba4); } /** * @short Callback object for functions with 7 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpFunctionCallback7B5 : public XorpCallback7 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4, BA5); XorpFunctionCallback7B5(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback7(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, _ba1, _ba2, _ba3, _ba4, _ba5); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; }; /** * @short Callback object for void functions with 7 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpFunctionCallback7B5 : public XorpCallback7 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4, BA5); XorpFunctionCallback7B5(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback7(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { (*_f)(a1, a2, a3, a4, a5, a6, a7, _ba1, _ba2, _ba3, _ba4, _ba5); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; }; /** * Factory function that creates a callback object targetted at a * function with 7 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback7::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return typename XorpCallback7::RefPtr(new XorpFunctionCallback7B5(f, ba1, ba2, ba3, ba4, ba5)); } /** * @short Callback object for member methods with 7 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpMemberCallback7B5 : public XorpCallback7 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4, BA5) ; XorpMemberCallback7B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback7(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, _ba1, _ba2, _ba3, _ba4, _ba5); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for void member methods with 7 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpMemberCallback7B5 : public XorpCallback7 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4, BA5) ; XorpMemberCallback7B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback7(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, _ba1, _ba2, _ba3, _ba4, _ba5); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for safe member methods with 7 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpSafeMemberCallback7B5 : public XorpMemberCallback7B5, public SafeCallbackBase { typedef typename XorpMemberCallback7B5::M M; XorpSafeMemberCallback7B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpMemberCallback7B5(o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpSafeMemberCallback7B5() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { if (valid()) { R r = XorpMemberCallback7B5::dispatch(a1, a2, a3, a4, a5, a6, a7); return r; } } }; /** * @short Callback object for void safe member methods with 7 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpSafeMemberCallback7B5 : public XorpMemberCallback7B5, public SafeCallbackBase { typedef typename XorpMemberCallback7B5::M M; XorpSafeMemberCallback7B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpMemberCallback7B5(o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpSafeMemberCallback7B5() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { if (valid()) { XorpMemberCallback7B5::dispatch(a1, a2, a3, a4, a5, a6, a7); } } }; template struct XorpMemberCallbackFactory7B5 { static XorpMemberCallback7B5* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpSafeMemberCallback7B5(o, p, ba1, ba2, ba3, ba4, ba5); } }; template struct XorpMemberCallbackFactory7B5 { static XorpMemberCallback7B5* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpMemberCallback7B5(o, p, ba1, ba2, ba3, ba4, ba5); }; }; /** * Factory function that creates a callback object targetted at a * member function with 7 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback7::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpMemberCallbackFactory7B5::True>::make(o, p, ba1, ba2, ba3, ba4, ba5); } /** * Factory function that creates a callback object targetted at a * member function with 7 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback7::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpMemberCallbackFactory7B5::True>::make(&o, p, ba1, ba2, ba3, ba4, ba5); } /** * @short Callback object for const member methods with 7 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstMemberCallback7B5 : public XorpCallback7 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4, BA5) const; XorpConstMemberCallback7B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback7(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, _ba1, _ba2, _ba3, _ba4, _ba5); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for void const member methods with 7 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstMemberCallback7B5 : public XorpCallback7 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4, BA5) const; XorpConstMemberCallback7B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback7(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, _ba1, _ba2, _ba3, _ba4, _ba5); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for const safe member methods with 7 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback7B5 : public XorpConstMemberCallback7B5, public SafeCallbackBase { typedef typename XorpConstMemberCallback7B5::M M; XorpConstSafeMemberCallback7B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpConstMemberCallback7B5(o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback7B5() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { if (valid()) { R r = XorpConstMemberCallback7B5::dispatch(a1, a2, a3, a4, a5, a6, a7); return r; } } }; /** * @short Callback object for void const safe member methods with 7 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback7B5 : public XorpConstMemberCallback7B5, public SafeCallbackBase { typedef typename XorpConstMemberCallback7B5::M M; XorpConstSafeMemberCallback7B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpConstMemberCallback7B5(o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback7B5() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { if (valid()) { XorpConstMemberCallback7B5::dispatch(a1, a2, a3, a4, a5, a6, a7); } } }; template struct XorpConstMemberCallbackFactory7B5 { static XorpConstMemberCallback7B5* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpConstSafeMemberCallback7B5(o, p, ba1, ba2, ba3, ba4, ba5); } }; template struct XorpConstMemberCallbackFactory7B5 { static XorpConstMemberCallback7B5* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpConstMemberCallback7B5(o, p, ba1, ba2, ba3, ba4, ba5); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 7 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback7::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpConstMemberCallbackFactory7B5::True>::make(o, p, ba1, ba2, ba3, ba4, ba5); } /** * Factory function that creates a callback object targetted at a * const member function with 7 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback7::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpConstMemberCallbackFactory7B5::True>::make(&o, p, ba1, ba2, ba3, ba4, ba5); } /** * @short Callback object for functions with 7 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpFunctionCallback7B6 : public XorpCallback7 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4, BA5, BA6); XorpFunctionCallback7B6(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback7(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; BA6 _ba6; }; /** * @short Callback object for void functions with 7 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpFunctionCallback7B6 : public XorpCallback7 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4, BA5, BA6); XorpFunctionCallback7B6(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback7(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { (*_f)(a1, a2, a3, a4, a5, a6, a7, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; BA6 _ba6; }; /** * Factory function that creates a callback object targetted at a * function with 7 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback7::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return typename XorpCallback7::RefPtr(new XorpFunctionCallback7B6(f, ba1, ba2, ba3, ba4, ba5, ba6)); } /** * @short Callback object for member methods with 7 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpMemberCallback7B6 : public XorpCallback7 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4, BA5, BA6) ; XorpMemberCallback7B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback7(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for void member methods with 7 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpMemberCallback7B6 : public XorpCallback7 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4, BA5, BA6) ; XorpMemberCallback7B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback7(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for safe member methods with 7 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpSafeMemberCallback7B6 : public XorpMemberCallback7B6, public SafeCallbackBase { typedef typename XorpMemberCallback7B6::M M; XorpSafeMemberCallback7B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpMemberCallback7B6(o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpSafeMemberCallback7B6() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { if (valid()) { R r = XorpMemberCallback7B6::dispatch(a1, a2, a3, a4, a5, a6, a7); return r; } } }; /** * @short Callback object for void safe member methods with 7 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpSafeMemberCallback7B6 : public XorpMemberCallback7B6, public SafeCallbackBase { typedef typename XorpMemberCallback7B6::M M; XorpSafeMemberCallback7B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpMemberCallback7B6(o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpSafeMemberCallback7B6() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { if (valid()) { XorpMemberCallback7B6::dispatch(a1, a2, a3, a4, a5, a6, a7); } } }; template struct XorpMemberCallbackFactory7B6 { static XorpMemberCallback7B6* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpSafeMemberCallback7B6(o, p, ba1, ba2, ba3, ba4, ba5, ba6); } }; template struct XorpMemberCallbackFactory7B6 { static XorpMemberCallback7B6* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpMemberCallback7B6(o, p, ba1, ba2, ba3, ba4, ba5, ba6); }; }; /** * Factory function that creates a callback object targetted at a * member function with 7 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback7::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpMemberCallbackFactory7B6::True>::make(o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * Factory function that creates a callback object targetted at a * member function with 7 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback7::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpMemberCallbackFactory7B6::True>::make(&o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * @short Callback object for const member methods with 7 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstMemberCallback7B6 : public XorpCallback7 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4, BA5, BA6) const; XorpConstMemberCallback7B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback7(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for void const member methods with 7 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstMemberCallback7B6 : public XorpCallback7 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4, BA5, BA6) const; XorpConstMemberCallback7B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback7(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for const safe member methods with 7 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback7B6 : public XorpConstMemberCallback7B6, public SafeCallbackBase { typedef typename XorpConstMemberCallback7B6::M M; XorpConstSafeMemberCallback7B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpConstMemberCallback7B6(o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback7B6() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { if (valid()) { R r = XorpConstMemberCallback7B6::dispatch(a1, a2, a3, a4, a5, a6, a7); return r; } } }; /** * @short Callback object for void const safe member methods with 7 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback7B6 : public XorpConstMemberCallback7B6, public SafeCallbackBase { typedef typename XorpConstMemberCallback7B6::M M; XorpConstSafeMemberCallback7B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpConstMemberCallback7B6(o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback7B6() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { if (valid()) { XorpConstMemberCallback7B6::dispatch(a1, a2, a3, a4, a5, a6, a7); } } }; template struct XorpConstMemberCallbackFactory7B6 { static XorpConstMemberCallback7B6* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpConstSafeMemberCallback7B6(o, p, ba1, ba2, ba3, ba4, ba5, ba6); } }; template struct XorpConstMemberCallbackFactory7B6 { static XorpConstMemberCallback7B6* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpConstMemberCallback7B6(o, p, ba1, ba2, ba3, ba4, ba5, ba6); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 7 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback7::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpConstMemberCallbackFactory7B6::True>::make(o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * Factory function that creates a callback object targetted at a * const member function with 7 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback7::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpConstMemberCallbackFactory7B6::True>::make(&o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /////////////////////////////////////////////////////////////////////////////// // // Code relating to callbacks with 8 late args // /** * @short Base class for callbacks with 8 dispatch time args. */ template struct XorpCallback8 { typedef ref_ptr RefPtr; virtual ~XorpCallback8() {} virtual R dispatch(A1, A2, A3, A4, A5, A6, A7, A8) = 0; }; /** * @short Callback object for functions with 8 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpFunctionCallback8B0 : public XorpCallback8 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8); XorpFunctionCallback8B0(F f) : XorpCallback8(), _f(f) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8); return r; } protected: F _f; }; /** * @short Callback object for void functions with 8 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpFunctionCallback8B0 : public XorpCallback8 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8); XorpFunctionCallback8B0(F f) : XorpCallback8(), _f(f) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { (*_f)(a1, a2, a3, a4, a5, a6, a7, a8); } protected: F _f; }; /** * Factory function that creates a callback object targetted at a * function with 8 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback8::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, A7, A8)) { return typename XorpCallback8::RefPtr(new XorpFunctionCallback8B0(f)); } /** * @short Callback object for member methods with 8 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpMemberCallback8B0 : public XorpCallback8 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8) ; XorpMemberCallback8B0(O* o, M m) : XorpCallback8(), _o(o), _m(m) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for void member methods with 8 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpMemberCallback8B0 : public XorpCallback8 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8) ; XorpMemberCallback8B0(O* o, M m) : XorpCallback8(), _o(o), _m(m) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8); } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for safe member methods with 8 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpSafeMemberCallback8B0 : public XorpMemberCallback8B0, public SafeCallbackBase { typedef typename XorpMemberCallback8B0::M M; XorpSafeMemberCallback8B0(O* o, M m) : XorpMemberCallback8B0(o, m), SafeCallbackBase(o) {} ~XorpSafeMemberCallback8B0() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { if (valid()) { R r = XorpMemberCallback8B0::dispatch(a1, a2, a3, a4, a5, a6, a7, a8); return r; } } }; /** * @short Callback object for void safe member methods with 8 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpSafeMemberCallback8B0 : public XorpMemberCallback8B0, public SafeCallbackBase { typedef typename XorpMemberCallback8B0::M M; XorpSafeMemberCallback8B0(O* o, M m) : XorpMemberCallback8B0(o, m), SafeCallbackBase(o) {} ~XorpSafeMemberCallback8B0() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { if (valid()) { XorpMemberCallback8B0::dispatch(a1, a2, a3, a4, a5, a6, a7, a8); } } }; template struct XorpMemberCallbackFactory8B0 { static XorpMemberCallback8B0* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8)) { return new XorpSafeMemberCallback8B0(o, p); } }; template struct XorpMemberCallbackFactory8B0 { static XorpMemberCallback8B0* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8)) { return new XorpMemberCallback8B0(o, p); }; }; /** * Factory function that creates a callback object targetted at a * member function with 8 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback8::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8)) { return XorpMemberCallbackFactory8B0::True>::make(o, p); } /** * Factory function that creates a callback object targetted at a * member function with 8 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback8::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8)) { return XorpMemberCallbackFactory8B0::True>::make(&o, p); } /** * @short Callback object for const member methods with 8 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstMemberCallback8B0 : public XorpCallback8 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8) const; XorpConstMemberCallback8B0(O* o, M m) : XorpCallback8(), _o(o), _m(m) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for void const member methods with 8 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstMemberCallback8B0 : public XorpCallback8 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8) const; XorpConstMemberCallback8B0(O* o, M m) : XorpCallback8(), _o(o), _m(m) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8); } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for const safe member methods with 8 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback8B0 : public XorpConstMemberCallback8B0, public SafeCallbackBase { typedef typename XorpConstMemberCallback8B0::M M; XorpConstSafeMemberCallback8B0(O* o, M m) : XorpConstMemberCallback8B0(o, m), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback8B0() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { if (valid()) { R r = XorpConstMemberCallback8B0::dispatch(a1, a2, a3, a4, a5, a6, a7, a8); return r; } } }; /** * @short Callback object for void const safe member methods with 8 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback8B0 : public XorpConstMemberCallback8B0, public SafeCallbackBase { typedef typename XorpConstMemberCallback8B0::M M; XorpConstSafeMemberCallback8B0(O* o, M m) : XorpConstMemberCallback8B0(o, m), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback8B0() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { if (valid()) { XorpConstMemberCallback8B0::dispatch(a1, a2, a3, a4, a5, a6, a7, a8); } } }; template struct XorpConstMemberCallbackFactory8B0 { static XorpConstMemberCallback8B0* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8) const) { return new XorpConstSafeMemberCallback8B0(o, p); } }; template struct XorpConstMemberCallbackFactory8B0 { static XorpConstMemberCallback8B0* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8) const) { return new XorpConstMemberCallback8B0(o, p); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 8 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback8::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8) const) { return XorpConstMemberCallbackFactory8B0::True>::make(o, p); } /** * Factory function that creates a callback object targetted at a * const member function with 8 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback8::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8) const) { return XorpConstMemberCallbackFactory8B0::True>::make(&o, p); } /** * @short Callback object for functions with 8 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpFunctionCallback8B1 : public XorpCallback8 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, BA1); XorpFunctionCallback8B1(F f, BA1 ba1) : XorpCallback8(), _f(f), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1); return r; } protected: F _f; BA1 _ba1; }; /** * @short Callback object for void functions with 8 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpFunctionCallback8B1 : public XorpCallback8 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, BA1); XorpFunctionCallback8B1(F f, BA1 ba1) : XorpCallback8(), _f(f), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1); } protected: F _f; BA1 _ba1; }; /** * Factory function that creates a callback object targetted at a * function with 8 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback8::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, BA1), BA1 ba1) { return typename XorpCallback8::RefPtr(new XorpFunctionCallback8B1(f, ba1)); } /** * @short Callback object for member methods with 8 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpMemberCallback8B1 : public XorpCallback8 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, BA1) ; XorpMemberCallback8B1(O* o, M m, BA1 ba1) : XorpCallback8(), _o(o), _m(m), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for void member methods with 8 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpMemberCallback8B1 : public XorpCallback8 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, BA1) ; XorpMemberCallback8B1(O* o, M m, BA1 ba1) : XorpCallback8(), _o(o), _m(m), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for safe member methods with 8 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpSafeMemberCallback8B1 : public XorpMemberCallback8B1, public SafeCallbackBase { typedef typename XorpMemberCallback8B1::M M; XorpSafeMemberCallback8B1(O* o, M m, BA1 ba1) : XorpMemberCallback8B1(o, m, ba1), SafeCallbackBase(o) {} ~XorpSafeMemberCallback8B1() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { if (valid()) { R r = XorpMemberCallback8B1::dispatch(a1, a2, a3, a4, a5, a6, a7, a8); return r; } } }; /** * @short Callback object for void safe member methods with 8 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpSafeMemberCallback8B1 : public XorpMemberCallback8B1, public SafeCallbackBase { typedef typename XorpMemberCallback8B1::M M; XorpSafeMemberCallback8B1(O* o, M m, BA1 ba1) : XorpMemberCallback8B1(o, m, ba1), SafeCallbackBase(o) {} ~XorpSafeMemberCallback8B1() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { if (valid()) { XorpMemberCallback8B1::dispatch(a1, a2, a3, a4, a5, a6, a7, a8); } } }; template struct XorpMemberCallbackFactory8B1 { static XorpMemberCallback8B1* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1), BA1 ba1) { return new XorpSafeMemberCallback8B1(o, p, ba1); } }; template struct XorpMemberCallbackFactory8B1 { static XorpMemberCallback8B1* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1), BA1 ba1) { return new XorpMemberCallback8B1(o, p, ba1); }; }; /** * Factory function that creates a callback object targetted at a * member function with 8 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback8::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1), BA1 ba1) { return XorpMemberCallbackFactory8B1::True>::make(o, p, ba1); } /** * Factory function that creates a callback object targetted at a * member function with 8 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback8::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1), BA1 ba1) { return XorpMemberCallbackFactory8B1::True>::make(&o, p, ba1); } /** * @short Callback object for const member methods with 8 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstMemberCallback8B1 : public XorpCallback8 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, BA1) const; XorpConstMemberCallback8B1(O* o, M m, BA1 ba1) : XorpCallback8(), _o(o), _m(m), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for void const member methods with 8 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstMemberCallback8B1 : public XorpCallback8 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, BA1) const; XorpConstMemberCallback8B1(O* o, M m, BA1 ba1) : XorpCallback8(), _o(o), _m(m), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for const safe member methods with 8 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback8B1 : public XorpConstMemberCallback8B1, public SafeCallbackBase { typedef typename XorpConstMemberCallback8B1::M M; XorpConstSafeMemberCallback8B1(O* o, M m, BA1 ba1) : XorpConstMemberCallback8B1(o, m, ba1), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback8B1() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { if (valid()) { R r = XorpConstMemberCallback8B1::dispatch(a1, a2, a3, a4, a5, a6, a7, a8); return r; } } }; /** * @short Callback object for void const safe member methods with 8 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback8B1 : public XorpConstMemberCallback8B1, public SafeCallbackBase { typedef typename XorpConstMemberCallback8B1::M M; XorpConstSafeMemberCallback8B1(O* o, M m, BA1 ba1) : XorpConstMemberCallback8B1(o, m, ba1), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback8B1() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { if (valid()) { XorpConstMemberCallback8B1::dispatch(a1, a2, a3, a4, a5, a6, a7, a8); } } }; template struct XorpConstMemberCallbackFactory8B1 { static XorpConstMemberCallback8B1* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1) const, BA1 ba1) { return new XorpConstSafeMemberCallback8B1(o, p, ba1); } }; template struct XorpConstMemberCallbackFactory8B1 { static XorpConstMemberCallback8B1* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1) const, BA1 ba1) { return new XorpConstMemberCallback8B1(o, p, ba1); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 8 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback8::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1) const, BA1 ba1) { return XorpConstMemberCallbackFactory8B1::True>::make(o, p, ba1); } /** * Factory function that creates a callback object targetted at a * const member function with 8 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback8::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1) const, BA1 ba1) { return XorpConstMemberCallbackFactory8B1::True>::make(&o, p, ba1); } /** * @short Callback object for functions with 8 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpFunctionCallback8B2 : public XorpCallback8 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2); XorpFunctionCallback8B2(F f, BA1 ba1, BA2 ba2) : XorpCallback8(), _f(f), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1, _ba2); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; }; /** * @short Callback object for void functions with 8 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpFunctionCallback8B2 : public XorpCallback8 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2); XorpFunctionCallback8B2(F f, BA1 ba1, BA2 ba2) : XorpCallback8(), _f(f), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1, _ba2); } protected: F _f; BA1 _ba1; BA2 _ba2; }; /** * Factory function that creates a callback object targetted at a * function with 8 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback8::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2), BA1 ba1, BA2 ba2) { return typename XorpCallback8::RefPtr(new XorpFunctionCallback8B2(f, ba1, ba2)); } /** * @short Callback object for member methods with 8 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpMemberCallback8B2 : public XorpCallback8 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2) ; XorpMemberCallback8B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback8(), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1, _ba2); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for void member methods with 8 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpMemberCallback8B2 : public XorpCallback8 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2) ; XorpMemberCallback8B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback8(), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1, _ba2); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for safe member methods with 8 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpSafeMemberCallback8B2 : public XorpMemberCallback8B2, public SafeCallbackBase { typedef typename XorpMemberCallback8B2::M M; XorpSafeMemberCallback8B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpMemberCallback8B2(o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpSafeMemberCallback8B2() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { if (valid()) { R r = XorpMemberCallback8B2::dispatch(a1, a2, a3, a4, a5, a6, a7, a8); return r; } } }; /** * @short Callback object for void safe member methods with 8 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpSafeMemberCallback8B2 : public XorpMemberCallback8B2, public SafeCallbackBase { typedef typename XorpMemberCallback8B2::M M; XorpSafeMemberCallback8B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpMemberCallback8B2(o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpSafeMemberCallback8B2() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { if (valid()) { XorpMemberCallback8B2::dispatch(a1, a2, a3, a4, a5, a6, a7, a8); } } }; template struct XorpMemberCallbackFactory8B2 { static XorpMemberCallback8B2* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2), BA1 ba1, BA2 ba2) { return new XorpSafeMemberCallback8B2(o, p, ba1, ba2); } }; template struct XorpMemberCallbackFactory8B2 { static XorpMemberCallback8B2* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2), BA1 ba1, BA2 ba2) { return new XorpMemberCallback8B2(o, p, ba1, ba2); }; }; /** * Factory function that creates a callback object targetted at a * member function with 8 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback8::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2), BA1 ba1, BA2 ba2) { return XorpMemberCallbackFactory8B2::True>::make(o, p, ba1, ba2); } /** * Factory function that creates a callback object targetted at a * member function with 8 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback8::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2), BA1 ba1, BA2 ba2) { return XorpMemberCallbackFactory8B2::True>::make(&o, p, ba1, ba2); } /** * @short Callback object for const member methods with 8 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstMemberCallback8B2 : public XorpCallback8 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2) const; XorpConstMemberCallback8B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback8(), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1, _ba2); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for void const member methods with 8 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstMemberCallback8B2 : public XorpCallback8 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2) const; XorpConstMemberCallback8B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback8(), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1, _ba2); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for const safe member methods with 8 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback8B2 : public XorpConstMemberCallback8B2, public SafeCallbackBase { typedef typename XorpConstMemberCallback8B2::M M; XorpConstSafeMemberCallback8B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpConstMemberCallback8B2(o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback8B2() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { if (valid()) { R r = XorpConstMemberCallback8B2::dispatch(a1, a2, a3, a4, a5, a6, a7, a8); return r; } } }; /** * @short Callback object for void const safe member methods with 8 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback8B2 : public XorpConstMemberCallback8B2, public SafeCallbackBase { typedef typename XorpConstMemberCallback8B2::M M; XorpConstSafeMemberCallback8B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpConstMemberCallback8B2(o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback8B2() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { if (valid()) { XorpConstMemberCallback8B2::dispatch(a1, a2, a3, a4, a5, a6, a7, a8); } } }; template struct XorpConstMemberCallbackFactory8B2 { static XorpConstMemberCallback8B2* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2) const, BA1 ba1, BA2 ba2) { return new XorpConstSafeMemberCallback8B2(o, p, ba1, ba2); } }; template struct XorpConstMemberCallbackFactory8B2 { static XorpConstMemberCallback8B2* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2) const, BA1 ba1, BA2 ba2) { return new XorpConstMemberCallback8B2(o, p, ba1, ba2); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 8 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback8::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2) const, BA1 ba1, BA2 ba2) { return XorpConstMemberCallbackFactory8B2::True>::make(o, p, ba1, ba2); } /** * Factory function that creates a callback object targetted at a * const member function with 8 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback8::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2) const, BA1 ba1, BA2 ba2) { return XorpConstMemberCallbackFactory8B2::True>::make(&o, p, ba1, ba2); } /** * @short Callback object for functions with 8 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpFunctionCallback8B3 : public XorpCallback8 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3); XorpFunctionCallback8B3(F f, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback8(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1, _ba2, _ba3); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; }; /** * @short Callback object for void functions with 8 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpFunctionCallback8B3 : public XorpCallback8 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3); XorpFunctionCallback8B3(F f, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback8(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1, _ba2, _ba3); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; }; /** * Factory function that creates a callback object targetted at a * function with 8 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback8::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return typename XorpCallback8::RefPtr(new XorpFunctionCallback8B3(f, ba1, ba2, ba3)); } /** * @short Callback object for member methods with 8 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpMemberCallback8B3 : public XorpCallback8 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3) ; XorpMemberCallback8B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback8(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1, _ba2, _ba3); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for void member methods with 8 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpMemberCallback8B3 : public XorpCallback8 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3) ; XorpMemberCallback8B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback8(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1, _ba2, _ba3); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for safe member methods with 8 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpSafeMemberCallback8B3 : public XorpMemberCallback8B3, public SafeCallbackBase { typedef typename XorpMemberCallback8B3::M M; XorpSafeMemberCallback8B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpMemberCallback8B3(o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpSafeMemberCallback8B3() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { if (valid()) { R r = XorpMemberCallback8B3::dispatch(a1, a2, a3, a4, a5, a6, a7, a8); return r; } } }; /** * @short Callback object for void safe member methods with 8 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpSafeMemberCallback8B3 : public XorpMemberCallback8B3, public SafeCallbackBase { typedef typename XorpMemberCallback8B3::M M; XorpSafeMemberCallback8B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpMemberCallback8B3(o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpSafeMemberCallback8B3() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { if (valid()) { XorpMemberCallback8B3::dispatch(a1, a2, a3, a4, a5, a6, a7, a8); } } }; template struct XorpMemberCallbackFactory8B3 { static XorpMemberCallback8B3* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpSafeMemberCallback8B3(o, p, ba1, ba2, ba3); } }; template struct XorpMemberCallbackFactory8B3 { static XorpMemberCallback8B3* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpMemberCallback8B3(o, p, ba1, ba2, ba3); }; }; /** * Factory function that creates a callback object targetted at a * member function with 8 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback8::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return XorpMemberCallbackFactory8B3::True>::make(o, p, ba1, ba2, ba3); } /** * Factory function that creates a callback object targetted at a * member function with 8 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback8::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return XorpMemberCallbackFactory8B3::True>::make(&o, p, ba1, ba2, ba3); } /** * @short Callback object for const member methods with 8 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstMemberCallback8B3 : public XorpCallback8 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3) const; XorpConstMemberCallback8B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback8(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1, _ba2, _ba3); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for void const member methods with 8 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstMemberCallback8B3 : public XorpCallback8 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3) const; XorpConstMemberCallback8B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback8(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1, _ba2, _ba3); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for const safe member methods with 8 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback8B3 : public XorpConstMemberCallback8B3, public SafeCallbackBase { typedef typename XorpConstMemberCallback8B3::M M; XorpConstSafeMemberCallback8B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpConstMemberCallback8B3(o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback8B3() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { if (valid()) { R r = XorpConstMemberCallback8B3::dispatch(a1, a2, a3, a4, a5, a6, a7, a8); return r; } } }; /** * @short Callback object for void const safe member methods with 8 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback8B3 : public XorpConstMemberCallback8B3, public SafeCallbackBase { typedef typename XorpConstMemberCallback8B3::M M; XorpConstSafeMemberCallback8B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpConstMemberCallback8B3(o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback8B3() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { if (valid()) { XorpConstMemberCallback8B3::dispatch(a1, a2, a3, a4, a5, a6, a7, a8); } } }; template struct XorpConstMemberCallbackFactory8B3 { static XorpConstMemberCallback8B3* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpConstSafeMemberCallback8B3(o, p, ba1, ba2, ba3); } }; template struct XorpConstMemberCallbackFactory8B3 { static XorpConstMemberCallback8B3* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpConstMemberCallback8B3(o, p, ba1, ba2, ba3); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 8 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback8::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return XorpConstMemberCallbackFactory8B3::True>::make(o, p, ba1, ba2, ba3); } /** * Factory function that creates a callback object targetted at a * const member function with 8 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback8::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return XorpConstMemberCallbackFactory8B3::True>::make(&o, p, ba1, ba2, ba3); } /** * @short Callback object for functions with 8 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpFunctionCallback8B4 : public XorpCallback8 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4); XorpFunctionCallback8B4(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback8(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1, _ba2, _ba3, _ba4); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; }; /** * @short Callback object for void functions with 8 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpFunctionCallback8B4 : public XorpCallback8 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4); XorpFunctionCallback8B4(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback8(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1, _ba2, _ba3, _ba4); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; }; /** * Factory function that creates a callback object targetted at a * function with 8 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback8::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return typename XorpCallback8::RefPtr(new XorpFunctionCallback8B4(f, ba1, ba2, ba3, ba4)); } /** * @short Callback object for member methods with 8 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpMemberCallback8B4 : public XorpCallback8 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4) ; XorpMemberCallback8B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback8(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1, _ba2, _ba3, _ba4); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for void member methods with 8 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpMemberCallback8B4 : public XorpCallback8 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4) ; XorpMemberCallback8B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback8(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1, _ba2, _ba3, _ba4); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for safe member methods with 8 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpSafeMemberCallback8B4 : public XorpMemberCallback8B4, public SafeCallbackBase { typedef typename XorpMemberCallback8B4::M M; XorpSafeMemberCallback8B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpMemberCallback8B4(o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpSafeMemberCallback8B4() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { if (valid()) { R r = XorpMemberCallback8B4::dispatch(a1, a2, a3, a4, a5, a6, a7, a8); return r; } } }; /** * @short Callback object for void safe member methods with 8 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpSafeMemberCallback8B4 : public XorpMemberCallback8B4, public SafeCallbackBase { typedef typename XorpMemberCallback8B4::M M; XorpSafeMemberCallback8B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpMemberCallback8B4(o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpSafeMemberCallback8B4() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { if (valid()) { XorpMemberCallback8B4::dispatch(a1, a2, a3, a4, a5, a6, a7, a8); } } }; template struct XorpMemberCallbackFactory8B4 { static XorpMemberCallback8B4* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpSafeMemberCallback8B4(o, p, ba1, ba2, ba3, ba4); } }; template struct XorpMemberCallbackFactory8B4 { static XorpMemberCallback8B4* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpMemberCallback8B4(o, p, ba1, ba2, ba3, ba4); }; }; /** * Factory function that creates a callback object targetted at a * member function with 8 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback8::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpMemberCallbackFactory8B4::True>::make(o, p, ba1, ba2, ba3, ba4); } /** * Factory function that creates a callback object targetted at a * member function with 8 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback8::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpMemberCallbackFactory8B4::True>::make(&o, p, ba1, ba2, ba3, ba4); } /** * @short Callback object for const member methods with 8 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstMemberCallback8B4 : public XorpCallback8 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4) const; XorpConstMemberCallback8B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback8(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1, _ba2, _ba3, _ba4); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for void const member methods with 8 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstMemberCallback8B4 : public XorpCallback8 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4) const; XorpConstMemberCallback8B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback8(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1, _ba2, _ba3, _ba4); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for const safe member methods with 8 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback8B4 : public XorpConstMemberCallback8B4, public SafeCallbackBase { typedef typename XorpConstMemberCallback8B4::M M; XorpConstSafeMemberCallback8B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpConstMemberCallback8B4(o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback8B4() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { if (valid()) { R r = XorpConstMemberCallback8B4::dispatch(a1, a2, a3, a4, a5, a6, a7, a8); return r; } } }; /** * @short Callback object for void const safe member methods with 8 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback8B4 : public XorpConstMemberCallback8B4, public SafeCallbackBase { typedef typename XorpConstMemberCallback8B4::M M; XorpConstSafeMemberCallback8B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpConstMemberCallback8B4(o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback8B4() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { if (valid()) { XorpConstMemberCallback8B4::dispatch(a1, a2, a3, a4, a5, a6, a7, a8); } } }; template struct XorpConstMemberCallbackFactory8B4 { static XorpConstMemberCallback8B4* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpConstSafeMemberCallback8B4(o, p, ba1, ba2, ba3, ba4); } }; template struct XorpConstMemberCallbackFactory8B4 { static XorpConstMemberCallback8B4* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpConstMemberCallback8B4(o, p, ba1, ba2, ba3, ba4); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 8 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback8::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpConstMemberCallbackFactory8B4::True>::make(o, p, ba1, ba2, ba3, ba4); } /** * Factory function that creates a callback object targetted at a * const member function with 8 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback8::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpConstMemberCallbackFactory8B4::True>::make(&o, p, ba1, ba2, ba3, ba4); } /** * @short Callback object for functions with 8 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpFunctionCallback8B5 : public XorpCallback8 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4, BA5); XorpFunctionCallback8B5(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback8(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1, _ba2, _ba3, _ba4, _ba5); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; }; /** * @short Callback object for void functions with 8 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpFunctionCallback8B5 : public XorpCallback8 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4, BA5); XorpFunctionCallback8B5(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback8(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1, _ba2, _ba3, _ba4, _ba5); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; }; /** * Factory function that creates a callback object targetted at a * function with 8 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback8::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return typename XorpCallback8::RefPtr(new XorpFunctionCallback8B5(f, ba1, ba2, ba3, ba4, ba5)); } /** * @short Callback object for member methods with 8 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpMemberCallback8B5 : public XorpCallback8 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4, BA5) ; XorpMemberCallback8B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback8(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1, _ba2, _ba3, _ba4, _ba5); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for void member methods with 8 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpMemberCallback8B5 : public XorpCallback8 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4, BA5) ; XorpMemberCallback8B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback8(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1, _ba2, _ba3, _ba4, _ba5); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for safe member methods with 8 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpSafeMemberCallback8B5 : public XorpMemberCallback8B5, public SafeCallbackBase { typedef typename XorpMemberCallback8B5::M M; XorpSafeMemberCallback8B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpMemberCallback8B5(o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpSafeMemberCallback8B5() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { if (valid()) { R r = XorpMemberCallback8B5::dispatch(a1, a2, a3, a4, a5, a6, a7, a8); return r; } } }; /** * @short Callback object for void safe member methods with 8 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpSafeMemberCallback8B5 : public XorpMemberCallback8B5, public SafeCallbackBase { typedef typename XorpMemberCallback8B5::M M; XorpSafeMemberCallback8B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpMemberCallback8B5(o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpSafeMemberCallback8B5() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { if (valid()) { XorpMemberCallback8B5::dispatch(a1, a2, a3, a4, a5, a6, a7, a8); } } }; template struct XorpMemberCallbackFactory8B5 { static XorpMemberCallback8B5* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpSafeMemberCallback8B5(o, p, ba1, ba2, ba3, ba4, ba5); } }; template struct XorpMemberCallbackFactory8B5 { static XorpMemberCallback8B5* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpMemberCallback8B5(o, p, ba1, ba2, ba3, ba4, ba5); }; }; /** * Factory function that creates a callback object targetted at a * member function with 8 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback8::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpMemberCallbackFactory8B5::True>::make(o, p, ba1, ba2, ba3, ba4, ba5); } /** * Factory function that creates a callback object targetted at a * member function with 8 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback8::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpMemberCallbackFactory8B5::True>::make(&o, p, ba1, ba2, ba3, ba4, ba5); } /** * @short Callback object for const member methods with 8 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstMemberCallback8B5 : public XorpCallback8 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4, BA5) const; XorpConstMemberCallback8B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback8(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1, _ba2, _ba3, _ba4, _ba5); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for void const member methods with 8 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstMemberCallback8B5 : public XorpCallback8 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4, BA5) const; XorpConstMemberCallback8B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback8(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1, _ba2, _ba3, _ba4, _ba5); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for const safe member methods with 8 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback8B5 : public XorpConstMemberCallback8B5, public SafeCallbackBase { typedef typename XorpConstMemberCallback8B5::M M; XorpConstSafeMemberCallback8B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpConstMemberCallback8B5(o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback8B5() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { if (valid()) { R r = XorpConstMemberCallback8B5::dispatch(a1, a2, a3, a4, a5, a6, a7, a8); return r; } } }; /** * @short Callback object for void const safe member methods with 8 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback8B5 : public XorpConstMemberCallback8B5, public SafeCallbackBase { typedef typename XorpConstMemberCallback8B5::M M; XorpConstSafeMemberCallback8B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpConstMemberCallback8B5(o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback8B5() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { if (valid()) { XorpConstMemberCallback8B5::dispatch(a1, a2, a3, a4, a5, a6, a7, a8); } } }; template struct XorpConstMemberCallbackFactory8B5 { static XorpConstMemberCallback8B5* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpConstSafeMemberCallback8B5(o, p, ba1, ba2, ba3, ba4, ba5); } }; template struct XorpConstMemberCallbackFactory8B5 { static XorpConstMemberCallback8B5* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpConstMemberCallback8B5(o, p, ba1, ba2, ba3, ba4, ba5); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 8 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback8::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpConstMemberCallbackFactory8B5::True>::make(o, p, ba1, ba2, ba3, ba4, ba5); } /** * Factory function that creates a callback object targetted at a * const member function with 8 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback8::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpConstMemberCallbackFactory8B5::True>::make(&o, p, ba1, ba2, ba3, ba4, ba5); } /** * @short Callback object for functions with 8 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpFunctionCallback8B6 : public XorpCallback8 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4, BA5, BA6); XorpFunctionCallback8B6(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback8(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; BA6 _ba6; }; /** * @short Callback object for void functions with 8 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpFunctionCallback8B6 : public XorpCallback8 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4, BA5, BA6); XorpFunctionCallback8B6(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback8(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; BA6 _ba6; }; /** * Factory function that creates a callback object targetted at a * function with 8 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback8::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return typename XorpCallback8::RefPtr(new XorpFunctionCallback8B6(f, ba1, ba2, ba3, ba4, ba5, ba6)); } /** * @short Callback object for member methods with 8 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpMemberCallback8B6 : public XorpCallback8 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4, BA5, BA6) ; XorpMemberCallback8B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback8(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for void member methods with 8 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpMemberCallback8B6 : public XorpCallback8 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4, BA5, BA6) ; XorpMemberCallback8B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback8(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for safe member methods with 8 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpSafeMemberCallback8B6 : public XorpMemberCallback8B6, public SafeCallbackBase { typedef typename XorpMemberCallback8B6::M M; XorpSafeMemberCallback8B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpMemberCallback8B6(o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpSafeMemberCallback8B6() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { if (valid()) { R r = XorpMemberCallback8B6::dispatch(a1, a2, a3, a4, a5, a6, a7, a8); return r; } } }; /** * @short Callback object for void safe member methods with 8 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpSafeMemberCallback8B6 : public XorpMemberCallback8B6, public SafeCallbackBase { typedef typename XorpMemberCallback8B6::M M; XorpSafeMemberCallback8B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpMemberCallback8B6(o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpSafeMemberCallback8B6() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { if (valid()) { XorpMemberCallback8B6::dispatch(a1, a2, a3, a4, a5, a6, a7, a8); } } }; template struct XorpMemberCallbackFactory8B6 { static XorpMemberCallback8B6* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpSafeMemberCallback8B6(o, p, ba1, ba2, ba3, ba4, ba5, ba6); } }; template struct XorpMemberCallbackFactory8B6 { static XorpMemberCallback8B6* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpMemberCallback8B6(o, p, ba1, ba2, ba3, ba4, ba5, ba6); }; }; /** * Factory function that creates a callback object targetted at a * member function with 8 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback8::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpMemberCallbackFactory8B6::True>::make(o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * Factory function that creates a callback object targetted at a * member function with 8 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback8::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpMemberCallbackFactory8B6::True>::make(&o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * @short Callback object for const member methods with 8 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstMemberCallback8B6 : public XorpCallback8 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4, BA5, BA6) const; XorpConstMemberCallback8B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback8(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for void const member methods with 8 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstMemberCallback8B6 : public XorpCallback8 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4, BA5, BA6) const; XorpConstMemberCallback8B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback8(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for const safe member methods with 8 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback8B6 : public XorpConstMemberCallback8B6, public SafeCallbackBase { typedef typename XorpConstMemberCallback8B6::M M; XorpConstSafeMemberCallback8B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpConstMemberCallback8B6(o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback8B6() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { if (valid()) { R r = XorpConstMemberCallback8B6::dispatch(a1, a2, a3, a4, a5, a6, a7, a8); return r; } } }; /** * @short Callback object for void const safe member methods with 8 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback8B6 : public XorpConstMemberCallback8B6, public SafeCallbackBase { typedef typename XorpConstMemberCallback8B6::M M; XorpConstSafeMemberCallback8B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpConstMemberCallback8B6(o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback8B6() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { if (valid()) { XorpConstMemberCallback8B6::dispatch(a1, a2, a3, a4, a5, a6, a7, a8); } } }; template struct XorpConstMemberCallbackFactory8B6 { static XorpConstMemberCallback8B6* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpConstSafeMemberCallback8B6(o, p, ba1, ba2, ba3, ba4, ba5, ba6); } }; template struct XorpConstMemberCallbackFactory8B6 { static XorpConstMemberCallback8B6* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpConstMemberCallback8B6(o, p, ba1, ba2, ba3, ba4, ba5, ba6); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 8 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback8::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpConstMemberCallbackFactory8B6::True>::make(o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * Factory function that creates a callback object targetted at a * const member function with 8 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback8::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpConstMemberCallbackFactory8B6::True>::make(&o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /////////////////////////////////////////////////////////////////////////////// // // Code relating to callbacks with 9 late args // /** * @short Base class for callbacks with 9 dispatch time args. */ template struct XorpCallback9 { typedef ref_ptr RefPtr; virtual ~XorpCallback9() {} virtual R dispatch(A1, A2, A3, A4, A5, A6, A7, A8, A9) = 0; }; /** * @short Callback object for functions with 9 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpFunctionCallback9B0 : public XorpCallback9 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9); XorpFunctionCallback9B0(F f) : XorpCallback9(), _f(f) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9); return r; } protected: F _f; }; /** * @short Callback object for void functions with 9 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpFunctionCallback9B0 : public XorpCallback9 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9); XorpFunctionCallback9B0(F f) : XorpCallback9(), _f(f) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9); } protected: F _f; }; /** * Factory function that creates a callback object targetted at a * function with 9 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback9::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9)) { return typename XorpCallback9::RefPtr(new XorpFunctionCallback9B0(f)); } /** * @short Callback object for member methods with 9 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpMemberCallback9B0 : public XorpCallback9 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9) ; XorpMemberCallback9B0(O* o, M m) : XorpCallback9(), _o(o), _m(m) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for void member methods with 9 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpMemberCallback9B0 : public XorpCallback9 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9) ; XorpMemberCallback9B0(O* o, M m) : XorpCallback9(), _o(o), _m(m) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9); } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for safe member methods with 9 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpSafeMemberCallback9B0 : public XorpMemberCallback9B0, public SafeCallbackBase { typedef typename XorpMemberCallback9B0::M M; XorpSafeMemberCallback9B0(O* o, M m) : XorpMemberCallback9B0(o, m), SafeCallbackBase(o) {} ~XorpSafeMemberCallback9B0() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { if (valid()) { R r = XorpMemberCallback9B0::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9); return r; } } }; /** * @short Callback object for void safe member methods with 9 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpSafeMemberCallback9B0 : public XorpMemberCallback9B0, public SafeCallbackBase { typedef typename XorpMemberCallback9B0::M M; XorpSafeMemberCallback9B0(O* o, M m) : XorpMemberCallback9B0(o, m), SafeCallbackBase(o) {} ~XorpSafeMemberCallback9B0() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { if (valid()) { XorpMemberCallback9B0::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9); } } }; template struct XorpMemberCallbackFactory9B0 { static XorpMemberCallback9B0* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9)) { return new XorpSafeMemberCallback9B0(o, p); } }; template struct XorpMemberCallbackFactory9B0 { static XorpMemberCallback9B0* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9)) { return new XorpMemberCallback9B0(o, p); }; }; /** * Factory function that creates a callback object targetted at a * member function with 9 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback9::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9)) { return XorpMemberCallbackFactory9B0::True>::make(o, p); } /** * Factory function that creates a callback object targetted at a * member function with 9 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback9::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9)) { return XorpMemberCallbackFactory9B0::True>::make(&o, p); } /** * @short Callback object for const member methods with 9 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstMemberCallback9B0 : public XorpCallback9 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9) const; XorpConstMemberCallback9B0(O* o, M m) : XorpCallback9(), _o(o), _m(m) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for void const member methods with 9 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstMemberCallback9B0 : public XorpCallback9 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9) const; XorpConstMemberCallback9B0(O* o, M m) : XorpCallback9(), _o(o), _m(m) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9); } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for const safe member methods with 9 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback9B0 : public XorpConstMemberCallback9B0, public SafeCallbackBase { typedef typename XorpConstMemberCallback9B0::M M; XorpConstSafeMemberCallback9B0(O* o, M m) : XorpConstMemberCallback9B0(o, m), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback9B0() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { if (valid()) { R r = XorpConstMemberCallback9B0::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9); return r; } } }; /** * @short Callback object for void const safe member methods with 9 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback9B0 : public XorpConstMemberCallback9B0, public SafeCallbackBase { typedef typename XorpConstMemberCallback9B0::M M; XorpConstSafeMemberCallback9B0(O* o, M m) : XorpConstMemberCallback9B0(o, m), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback9B0() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { if (valid()) { XorpConstMemberCallback9B0::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9); } } }; template struct XorpConstMemberCallbackFactory9B0 { static XorpConstMemberCallback9B0* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9) const) { return new XorpConstSafeMemberCallback9B0(o, p); } }; template struct XorpConstMemberCallbackFactory9B0 { static XorpConstMemberCallback9B0* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9) const) { return new XorpConstMemberCallback9B0(o, p); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 9 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback9::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9) const) { return XorpConstMemberCallbackFactory9B0::True>::make(o, p); } /** * Factory function that creates a callback object targetted at a * const member function with 9 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback9::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9) const) { return XorpConstMemberCallbackFactory9B0::True>::make(&o, p); } /** * @short Callback object for functions with 9 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpFunctionCallback9B1 : public XorpCallback9 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1); XorpFunctionCallback9B1(F f, BA1 ba1) : XorpCallback9(), _f(f), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1); return r; } protected: F _f; BA1 _ba1; }; /** * @short Callback object for void functions with 9 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpFunctionCallback9B1 : public XorpCallback9 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1); XorpFunctionCallback9B1(F f, BA1 ba1) : XorpCallback9(), _f(f), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1); } protected: F _f; BA1 _ba1; }; /** * Factory function that creates a callback object targetted at a * function with 9 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback9::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1), BA1 ba1) { return typename XorpCallback9::RefPtr(new XorpFunctionCallback9B1(f, ba1)); } /** * @short Callback object for member methods with 9 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpMemberCallback9B1 : public XorpCallback9 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1) ; XorpMemberCallback9B1(O* o, M m, BA1 ba1) : XorpCallback9(), _o(o), _m(m), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for void member methods with 9 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpMemberCallback9B1 : public XorpCallback9 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1) ; XorpMemberCallback9B1(O* o, M m, BA1 ba1) : XorpCallback9(), _o(o), _m(m), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for safe member methods with 9 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpSafeMemberCallback9B1 : public XorpMemberCallback9B1, public SafeCallbackBase { typedef typename XorpMemberCallback9B1::M M; XorpSafeMemberCallback9B1(O* o, M m, BA1 ba1) : XorpMemberCallback9B1(o, m, ba1), SafeCallbackBase(o) {} ~XorpSafeMemberCallback9B1() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { if (valid()) { R r = XorpMemberCallback9B1::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9); return r; } } }; /** * @short Callback object for void safe member methods with 9 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpSafeMemberCallback9B1 : public XorpMemberCallback9B1, public SafeCallbackBase { typedef typename XorpMemberCallback9B1::M M; XorpSafeMemberCallback9B1(O* o, M m, BA1 ba1) : XorpMemberCallback9B1(o, m, ba1), SafeCallbackBase(o) {} ~XorpSafeMemberCallback9B1() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { if (valid()) { XorpMemberCallback9B1::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9); } } }; template struct XorpMemberCallbackFactory9B1 { static XorpMemberCallback9B1* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1), BA1 ba1) { return new XorpSafeMemberCallback9B1(o, p, ba1); } }; template struct XorpMemberCallbackFactory9B1 { static XorpMemberCallback9B1* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1), BA1 ba1) { return new XorpMemberCallback9B1(o, p, ba1); }; }; /** * Factory function that creates a callback object targetted at a * member function with 9 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback9::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1), BA1 ba1) { return XorpMemberCallbackFactory9B1::True>::make(o, p, ba1); } /** * Factory function that creates a callback object targetted at a * member function with 9 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback9::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1), BA1 ba1) { return XorpMemberCallbackFactory9B1::True>::make(&o, p, ba1); } /** * @short Callback object for const member methods with 9 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstMemberCallback9B1 : public XorpCallback9 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1) const; XorpConstMemberCallback9B1(O* o, M m, BA1 ba1) : XorpCallback9(), _o(o), _m(m), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for void const member methods with 9 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstMemberCallback9B1 : public XorpCallback9 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1) const; XorpConstMemberCallback9B1(O* o, M m, BA1 ba1) : XorpCallback9(), _o(o), _m(m), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for const safe member methods with 9 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback9B1 : public XorpConstMemberCallback9B1, public SafeCallbackBase { typedef typename XorpConstMemberCallback9B1::M M; XorpConstSafeMemberCallback9B1(O* o, M m, BA1 ba1) : XorpConstMemberCallback9B1(o, m, ba1), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback9B1() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { if (valid()) { R r = XorpConstMemberCallback9B1::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9); return r; } } }; /** * @short Callback object for void const safe member methods with 9 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback9B1 : public XorpConstMemberCallback9B1, public SafeCallbackBase { typedef typename XorpConstMemberCallback9B1::M M; XorpConstSafeMemberCallback9B1(O* o, M m, BA1 ba1) : XorpConstMemberCallback9B1(o, m, ba1), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback9B1() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { if (valid()) { XorpConstMemberCallback9B1::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9); } } }; template struct XorpConstMemberCallbackFactory9B1 { static XorpConstMemberCallback9B1* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1) const, BA1 ba1) { return new XorpConstSafeMemberCallback9B1(o, p, ba1); } }; template struct XorpConstMemberCallbackFactory9B1 { static XorpConstMemberCallback9B1* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1) const, BA1 ba1) { return new XorpConstMemberCallback9B1(o, p, ba1); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 9 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback9::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1) const, BA1 ba1) { return XorpConstMemberCallbackFactory9B1::True>::make(o, p, ba1); } /** * Factory function that creates a callback object targetted at a * const member function with 9 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback9::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1) const, BA1 ba1) { return XorpConstMemberCallbackFactory9B1::True>::make(&o, p, ba1); } /** * @short Callback object for functions with 9 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpFunctionCallback9B2 : public XorpCallback9 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2); XorpFunctionCallback9B2(F f, BA1 ba1, BA2 ba2) : XorpCallback9(), _f(f), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1, _ba2); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; }; /** * @short Callback object for void functions with 9 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpFunctionCallback9B2 : public XorpCallback9 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2); XorpFunctionCallback9B2(F f, BA1 ba1, BA2 ba2) : XorpCallback9(), _f(f), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1, _ba2); } protected: F _f; BA1 _ba1; BA2 _ba2; }; /** * Factory function that creates a callback object targetted at a * function with 9 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback9::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2), BA1 ba1, BA2 ba2) { return typename XorpCallback9::RefPtr(new XorpFunctionCallback9B2(f, ba1, ba2)); } /** * @short Callback object for member methods with 9 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpMemberCallback9B2 : public XorpCallback9 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2) ; XorpMemberCallback9B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback9(), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1, _ba2); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for void member methods with 9 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpMemberCallback9B2 : public XorpCallback9 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2) ; XorpMemberCallback9B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback9(), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1, _ba2); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for safe member methods with 9 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpSafeMemberCallback9B2 : public XorpMemberCallback9B2, public SafeCallbackBase { typedef typename XorpMemberCallback9B2::M M; XorpSafeMemberCallback9B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpMemberCallback9B2(o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpSafeMemberCallback9B2() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { if (valid()) { R r = XorpMemberCallback9B2::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9); return r; } } }; /** * @short Callback object for void safe member methods with 9 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpSafeMemberCallback9B2 : public XorpMemberCallback9B2, public SafeCallbackBase { typedef typename XorpMemberCallback9B2::M M; XorpSafeMemberCallback9B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpMemberCallback9B2(o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpSafeMemberCallback9B2() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { if (valid()) { XorpMemberCallback9B2::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9); } } }; template struct XorpMemberCallbackFactory9B2 { static XorpMemberCallback9B2* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2), BA1 ba1, BA2 ba2) { return new XorpSafeMemberCallback9B2(o, p, ba1, ba2); } }; template struct XorpMemberCallbackFactory9B2 { static XorpMemberCallback9B2* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2), BA1 ba1, BA2 ba2) { return new XorpMemberCallback9B2(o, p, ba1, ba2); }; }; /** * Factory function that creates a callback object targetted at a * member function with 9 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback9::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2), BA1 ba1, BA2 ba2) { return XorpMemberCallbackFactory9B2::True>::make(o, p, ba1, ba2); } /** * Factory function that creates a callback object targetted at a * member function with 9 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback9::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2), BA1 ba1, BA2 ba2) { return XorpMemberCallbackFactory9B2::True>::make(&o, p, ba1, ba2); } /** * @short Callback object for const member methods with 9 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstMemberCallback9B2 : public XorpCallback9 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2) const; XorpConstMemberCallback9B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback9(), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1, _ba2); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for void const member methods with 9 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstMemberCallback9B2 : public XorpCallback9 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2) const; XorpConstMemberCallback9B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback9(), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1, _ba2); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for const safe member methods with 9 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback9B2 : public XorpConstMemberCallback9B2, public SafeCallbackBase { typedef typename XorpConstMemberCallback9B2::M M; XorpConstSafeMemberCallback9B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpConstMemberCallback9B2(o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback9B2() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { if (valid()) { R r = XorpConstMemberCallback9B2::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9); return r; } } }; /** * @short Callback object for void const safe member methods with 9 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback9B2 : public XorpConstMemberCallback9B2, public SafeCallbackBase { typedef typename XorpConstMemberCallback9B2::M M; XorpConstSafeMemberCallback9B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpConstMemberCallback9B2(o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback9B2() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { if (valid()) { XorpConstMemberCallback9B2::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9); } } }; template struct XorpConstMemberCallbackFactory9B2 { static XorpConstMemberCallback9B2* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2) const, BA1 ba1, BA2 ba2) { return new XorpConstSafeMemberCallback9B2(o, p, ba1, ba2); } }; template struct XorpConstMemberCallbackFactory9B2 { static XorpConstMemberCallback9B2* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2) const, BA1 ba1, BA2 ba2) { return new XorpConstMemberCallback9B2(o, p, ba1, ba2); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 9 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback9::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2) const, BA1 ba1, BA2 ba2) { return XorpConstMemberCallbackFactory9B2::True>::make(o, p, ba1, ba2); } /** * Factory function that creates a callback object targetted at a * const member function with 9 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback9::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2) const, BA1 ba1, BA2 ba2) { return XorpConstMemberCallbackFactory9B2::True>::make(&o, p, ba1, ba2); } /** * @short Callback object for functions with 9 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpFunctionCallback9B3 : public XorpCallback9 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3); XorpFunctionCallback9B3(F f, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback9(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1, _ba2, _ba3); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; }; /** * @short Callback object for void functions with 9 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpFunctionCallback9B3 : public XorpCallback9 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3); XorpFunctionCallback9B3(F f, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback9(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1, _ba2, _ba3); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; }; /** * Factory function that creates a callback object targetted at a * function with 9 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback9::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return typename XorpCallback9::RefPtr(new XorpFunctionCallback9B3(f, ba1, ba2, ba3)); } /** * @short Callback object for member methods with 9 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpMemberCallback9B3 : public XorpCallback9 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3) ; XorpMemberCallback9B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback9(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1, _ba2, _ba3); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for void member methods with 9 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpMemberCallback9B3 : public XorpCallback9 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3) ; XorpMemberCallback9B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback9(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1, _ba2, _ba3); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for safe member methods with 9 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpSafeMemberCallback9B3 : public XorpMemberCallback9B3, public SafeCallbackBase { typedef typename XorpMemberCallback9B3::M M; XorpSafeMemberCallback9B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpMemberCallback9B3(o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpSafeMemberCallback9B3() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { if (valid()) { R r = XorpMemberCallback9B3::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9); return r; } } }; /** * @short Callback object for void safe member methods with 9 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpSafeMemberCallback9B3 : public XorpMemberCallback9B3, public SafeCallbackBase { typedef typename XorpMemberCallback9B3::M M; XorpSafeMemberCallback9B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpMemberCallback9B3(o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpSafeMemberCallback9B3() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { if (valid()) { XorpMemberCallback9B3::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9); } } }; template struct XorpMemberCallbackFactory9B3 { static XorpMemberCallback9B3* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpSafeMemberCallback9B3(o, p, ba1, ba2, ba3); } }; template struct XorpMemberCallbackFactory9B3 { static XorpMemberCallback9B3* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpMemberCallback9B3(o, p, ba1, ba2, ba3); }; }; /** * Factory function that creates a callback object targetted at a * member function with 9 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback9::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return XorpMemberCallbackFactory9B3::True>::make(o, p, ba1, ba2, ba3); } /** * Factory function that creates a callback object targetted at a * member function with 9 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback9::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return XorpMemberCallbackFactory9B3::True>::make(&o, p, ba1, ba2, ba3); } /** * @short Callback object for const member methods with 9 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstMemberCallback9B3 : public XorpCallback9 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3) const; XorpConstMemberCallback9B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback9(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1, _ba2, _ba3); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for void const member methods with 9 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstMemberCallback9B3 : public XorpCallback9 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3) const; XorpConstMemberCallback9B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback9(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1, _ba2, _ba3); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for const safe member methods with 9 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback9B3 : public XorpConstMemberCallback9B3, public SafeCallbackBase { typedef typename XorpConstMemberCallback9B3::M M; XorpConstSafeMemberCallback9B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpConstMemberCallback9B3(o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback9B3() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { if (valid()) { R r = XorpConstMemberCallback9B3::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9); return r; } } }; /** * @short Callback object for void const safe member methods with 9 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback9B3 : public XorpConstMemberCallback9B3, public SafeCallbackBase { typedef typename XorpConstMemberCallback9B3::M M; XorpConstSafeMemberCallback9B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpConstMemberCallback9B3(o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback9B3() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { if (valid()) { XorpConstMemberCallback9B3::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9); } } }; template struct XorpConstMemberCallbackFactory9B3 { static XorpConstMemberCallback9B3* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpConstSafeMemberCallback9B3(o, p, ba1, ba2, ba3); } }; template struct XorpConstMemberCallbackFactory9B3 { static XorpConstMemberCallback9B3* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpConstMemberCallback9B3(o, p, ba1, ba2, ba3); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 9 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback9::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return XorpConstMemberCallbackFactory9B3::True>::make(o, p, ba1, ba2, ba3); } /** * Factory function that creates a callback object targetted at a * const member function with 9 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback9::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return XorpConstMemberCallbackFactory9B3::True>::make(&o, p, ba1, ba2, ba3); } /** * @short Callback object for functions with 9 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpFunctionCallback9B4 : public XorpCallback9 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4); XorpFunctionCallback9B4(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback9(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1, _ba2, _ba3, _ba4); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; }; /** * @short Callback object for void functions with 9 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpFunctionCallback9B4 : public XorpCallback9 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4); XorpFunctionCallback9B4(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback9(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1, _ba2, _ba3, _ba4); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; }; /** * Factory function that creates a callback object targetted at a * function with 9 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback9::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return typename XorpCallback9::RefPtr(new XorpFunctionCallback9B4(f, ba1, ba2, ba3, ba4)); } /** * @short Callback object for member methods with 9 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpMemberCallback9B4 : public XorpCallback9 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4) ; XorpMemberCallback9B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback9(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1, _ba2, _ba3, _ba4); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for void member methods with 9 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpMemberCallback9B4 : public XorpCallback9 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4) ; XorpMemberCallback9B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback9(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1, _ba2, _ba3, _ba4); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for safe member methods with 9 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpSafeMemberCallback9B4 : public XorpMemberCallback9B4, public SafeCallbackBase { typedef typename XorpMemberCallback9B4::M M; XorpSafeMemberCallback9B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpMemberCallback9B4(o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpSafeMemberCallback9B4() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { if (valid()) { R r = XorpMemberCallback9B4::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9); return r; } } }; /** * @short Callback object for void safe member methods with 9 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpSafeMemberCallback9B4 : public XorpMemberCallback9B4, public SafeCallbackBase { typedef typename XorpMemberCallback9B4::M M; XorpSafeMemberCallback9B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpMemberCallback9B4(o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpSafeMemberCallback9B4() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { if (valid()) { XorpMemberCallback9B4::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9); } } }; template struct XorpMemberCallbackFactory9B4 { static XorpMemberCallback9B4* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpSafeMemberCallback9B4(o, p, ba1, ba2, ba3, ba4); } }; template struct XorpMemberCallbackFactory9B4 { static XorpMemberCallback9B4* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpMemberCallback9B4(o, p, ba1, ba2, ba3, ba4); }; }; /** * Factory function that creates a callback object targetted at a * member function with 9 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback9::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpMemberCallbackFactory9B4::True>::make(o, p, ba1, ba2, ba3, ba4); } /** * Factory function that creates a callback object targetted at a * member function with 9 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback9::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpMemberCallbackFactory9B4::True>::make(&o, p, ba1, ba2, ba3, ba4); } /** * @short Callback object for const member methods with 9 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstMemberCallback9B4 : public XorpCallback9 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4) const; XorpConstMemberCallback9B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback9(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1, _ba2, _ba3, _ba4); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for void const member methods with 9 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstMemberCallback9B4 : public XorpCallback9 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4) const; XorpConstMemberCallback9B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback9(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1, _ba2, _ba3, _ba4); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for const safe member methods with 9 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback9B4 : public XorpConstMemberCallback9B4, public SafeCallbackBase { typedef typename XorpConstMemberCallback9B4::M M; XorpConstSafeMemberCallback9B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpConstMemberCallback9B4(o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback9B4() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { if (valid()) { R r = XorpConstMemberCallback9B4::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9); return r; } } }; /** * @short Callback object for void const safe member methods with 9 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback9B4 : public XorpConstMemberCallback9B4, public SafeCallbackBase { typedef typename XorpConstMemberCallback9B4::M M; XorpConstSafeMemberCallback9B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpConstMemberCallback9B4(o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback9B4() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { if (valid()) { XorpConstMemberCallback9B4::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9); } } }; template struct XorpConstMemberCallbackFactory9B4 { static XorpConstMemberCallback9B4* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpConstSafeMemberCallback9B4(o, p, ba1, ba2, ba3, ba4); } }; template struct XorpConstMemberCallbackFactory9B4 { static XorpConstMemberCallback9B4* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpConstMemberCallback9B4(o, p, ba1, ba2, ba3, ba4); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 9 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback9::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpConstMemberCallbackFactory9B4::True>::make(o, p, ba1, ba2, ba3, ba4); } /** * Factory function that creates a callback object targetted at a * const member function with 9 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback9::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpConstMemberCallbackFactory9B4::True>::make(&o, p, ba1, ba2, ba3, ba4); } /** * @short Callback object for functions with 9 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpFunctionCallback9B5 : public XorpCallback9 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4, BA5); XorpFunctionCallback9B5(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback9(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1, _ba2, _ba3, _ba4, _ba5); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; }; /** * @short Callback object for void functions with 9 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpFunctionCallback9B5 : public XorpCallback9 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4, BA5); XorpFunctionCallback9B5(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback9(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1, _ba2, _ba3, _ba4, _ba5); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; }; /** * Factory function that creates a callback object targetted at a * function with 9 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback9::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return typename XorpCallback9::RefPtr(new XorpFunctionCallback9B5(f, ba1, ba2, ba3, ba4, ba5)); } /** * @short Callback object for member methods with 9 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpMemberCallback9B5 : public XorpCallback9 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4, BA5) ; XorpMemberCallback9B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback9(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1, _ba2, _ba3, _ba4, _ba5); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for void member methods with 9 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpMemberCallback9B5 : public XorpCallback9 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4, BA5) ; XorpMemberCallback9B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback9(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1, _ba2, _ba3, _ba4, _ba5); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for safe member methods with 9 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpSafeMemberCallback9B5 : public XorpMemberCallback9B5, public SafeCallbackBase { typedef typename XorpMemberCallback9B5::M M; XorpSafeMemberCallback9B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpMemberCallback9B5(o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpSafeMemberCallback9B5() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { if (valid()) { R r = XorpMemberCallback9B5::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9); return r; } } }; /** * @short Callback object for void safe member methods with 9 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpSafeMemberCallback9B5 : public XorpMemberCallback9B5, public SafeCallbackBase { typedef typename XorpMemberCallback9B5::M M; XorpSafeMemberCallback9B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpMemberCallback9B5(o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpSafeMemberCallback9B5() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { if (valid()) { XorpMemberCallback9B5::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9); } } }; template struct XorpMemberCallbackFactory9B5 { static XorpMemberCallback9B5* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpSafeMemberCallback9B5(o, p, ba1, ba2, ba3, ba4, ba5); } }; template struct XorpMemberCallbackFactory9B5 { static XorpMemberCallback9B5* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpMemberCallback9B5(o, p, ba1, ba2, ba3, ba4, ba5); }; }; /** * Factory function that creates a callback object targetted at a * member function with 9 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback9::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpMemberCallbackFactory9B5::True>::make(o, p, ba1, ba2, ba3, ba4, ba5); } /** * Factory function that creates a callback object targetted at a * member function with 9 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback9::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpMemberCallbackFactory9B5::True>::make(&o, p, ba1, ba2, ba3, ba4, ba5); } /** * @short Callback object for const member methods with 9 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstMemberCallback9B5 : public XorpCallback9 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4, BA5) const; XorpConstMemberCallback9B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback9(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1, _ba2, _ba3, _ba4, _ba5); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for void const member methods with 9 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstMemberCallback9B5 : public XorpCallback9 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4, BA5) const; XorpConstMemberCallback9B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback9(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1, _ba2, _ba3, _ba4, _ba5); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for const safe member methods with 9 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback9B5 : public XorpConstMemberCallback9B5, public SafeCallbackBase { typedef typename XorpConstMemberCallback9B5::M M; XorpConstSafeMemberCallback9B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpConstMemberCallback9B5(o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback9B5() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { if (valid()) { R r = XorpConstMemberCallback9B5::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9); return r; } } }; /** * @short Callback object for void const safe member methods with 9 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback9B5 : public XorpConstMemberCallback9B5, public SafeCallbackBase { typedef typename XorpConstMemberCallback9B5::M M; XorpConstSafeMemberCallback9B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpConstMemberCallback9B5(o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback9B5() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { if (valid()) { XorpConstMemberCallback9B5::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9); } } }; template struct XorpConstMemberCallbackFactory9B5 { static XorpConstMemberCallback9B5* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpConstSafeMemberCallback9B5(o, p, ba1, ba2, ba3, ba4, ba5); } }; template struct XorpConstMemberCallbackFactory9B5 { static XorpConstMemberCallback9B5* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpConstMemberCallback9B5(o, p, ba1, ba2, ba3, ba4, ba5); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 9 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback9::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpConstMemberCallbackFactory9B5::True>::make(o, p, ba1, ba2, ba3, ba4, ba5); } /** * Factory function that creates a callback object targetted at a * const member function with 9 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback9::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpConstMemberCallbackFactory9B5::True>::make(&o, p, ba1, ba2, ba3, ba4, ba5); } /** * @short Callback object for functions with 9 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpFunctionCallback9B6 : public XorpCallback9 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4, BA5, BA6); XorpFunctionCallback9B6(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback9(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; BA6 _ba6; }; /** * @short Callback object for void functions with 9 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpFunctionCallback9B6 : public XorpCallback9 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4, BA5, BA6); XorpFunctionCallback9B6(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback9(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; BA6 _ba6; }; /** * Factory function that creates a callback object targetted at a * function with 9 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback9::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return typename XorpCallback9::RefPtr(new XorpFunctionCallback9B6(f, ba1, ba2, ba3, ba4, ba5, ba6)); } /** * @short Callback object for member methods with 9 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpMemberCallback9B6 : public XorpCallback9 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4, BA5, BA6) ; XorpMemberCallback9B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback9(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for void member methods with 9 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpMemberCallback9B6 : public XorpCallback9 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4, BA5, BA6) ; XorpMemberCallback9B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback9(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for safe member methods with 9 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpSafeMemberCallback9B6 : public XorpMemberCallback9B6, public SafeCallbackBase { typedef typename XorpMemberCallback9B6::M M; XorpSafeMemberCallback9B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpMemberCallback9B6(o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpSafeMemberCallback9B6() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { if (valid()) { R r = XorpMemberCallback9B6::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9); return r; } } }; /** * @short Callback object for void safe member methods with 9 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpSafeMemberCallback9B6 : public XorpMemberCallback9B6, public SafeCallbackBase { typedef typename XorpMemberCallback9B6::M M; XorpSafeMemberCallback9B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpMemberCallback9B6(o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpSafeMemberCallback9B6() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { if (valid()) { XorpMemberCallback9B6::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9); } } }; template struct XorpMemberCallbackFactory9B6 { static XorpMemberCallback9B6* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpSafeMemberCallback9B6(o, p, ba1, ba2, ba3, ba4, ba5, ba6); } }; template struct XorpMemberCallbackFactory9B6 { static XorpMemberCallback9B6* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpMemberCallback9B6(o, p, ba1, ba2, ba3, ba4, ba5, ba6); }; }; /** * Factory function that creates a callback object targetted at a * member function with 9 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback9::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpMemberCallbackFactory9B6::True>::make(o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * Factory function that creates a callback object targetted at a * member function with 9 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback9::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpMemberCallbackFactory9B6::True>::make(&o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * @short Callback object for const member methods with 9 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstMemberCallback9B6 : public XorpCallback9 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4, BA5, BA6) const; XorpConstMemberCallback9B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback9(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for void const member methods with 9 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstMemberCallback9B6 : public XorpCallback9 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4, BA5, BA6) const; XorpConstMemberCallback9B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback9(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for const safe member methods with 9 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback9B6 : public XorpConstMemberCallback9B6, public SafeCallbackBase { typedef typename XorpConstMemberCallback9B6::M M; XorpConstSafeMemberCallback9B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpConstMemberCallback9B6(o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback9B6() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { if (valid()) { R r = XorpConstMemberCallback9B6::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9); return r; } } }; /** * @short Callback object for void const safe member methods with 9 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback9B6 : public XorpConstMemberCallback9B6, public SafeCallbackBase { typedef typename XorpConstMemberCallback9B6::M M; XorpConstSafeMemberCallback9B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpConstMemberCallback9B6(o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback9B6() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { if (valid()) { XorpConstMemberCallback9B6::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9); } } }; template struct XorpConstMemberCallbackFactory9B6 { static XorpConstMemberCallback9B6* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpConstSafeMemberCallback9B6(o, p, ba1, ba2, ba3, ba4, ba5, ba6); } }; template struct XorpConstMemberCallbackFactory9B6 { static XorpConstMemberCallback9B6* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpConstMemberCallback9B6(o, p, ba1, ba2, ba3, ba4, ba5, ba6); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 9 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback9::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpConstMemberCallbackFactory9B6::True>::make(o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * Factory function that creates a callback object targetted at a * const member function with 9 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback9::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpConstMemberCallbackFactory9B6::True>::make(&o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /////////////////////////////////////////////////////////////////////////////// // // Code relating to callbacks with 10 late args // /** * @short Base class for callbacks with 10 dispatch time args. */ template struct XorpCallback10 { typedef ref_ptr RefPtr; virtual ~XorpCallback10() {} virtual R dispatch(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10) = 0; }; /** * @short Callback object for functions with 10 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpFunctionCallback10B0 : public XorpCallback10 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10); XorpFunctionCallback10B0(F f) : XorpCallback10(), _f(f) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); return r; } protected: F _f; }; /** * @short Callback object for void functions with 10 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpFunctionCallback10B0 : public XorpCallback10 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10); XorpFunctionCallback10B0(F f) : XorpCallback10(), _f(f) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); } protected: F _f; }; /** * Factory function that creates a callback object targetted at a * function with 10 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback10::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10)) { return typename XorpCallback10::RefPtr(new XorpFunctionCallback10B0(f)); } /** * @short Callback object for member methods with 10 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpMemberCallback10B0 : public XorpCallback10 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10) ; XorpMemberCallback10B0(O* o, M m) : XorpCallback10(), _o(o), _m(m) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for void member methods with 10 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpMemberCallback10B0 : public XorpCallback10 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10) ; XorpMemberCallback10B0(O* o, M m) : XorpCallback10(), _o(o), _m(m) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for safe member methods with 10 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpSafeMemberCallback10B0 : public XorpMemberCallback10B0, public SafeCallbackBase { typedef typename XorpMemberCallback10B0::M M; XorpSafeMemberCallback10B0(O* o, M m) : XorpMemberCallback10B0(o, m), SafeCallbackBase(o) {} ~XorpSafeMemberCallback10B0() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { if (valid()) { R r = XorpMemberCallback10B0::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); return r; } } }; /** * @short Callback object for void safe member methods with 10 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpSafeMemberCallback10B0 : public XorpMemberCallback10B0, public SafeCallbackBase { typedef typename XorpMemberCallback10B0::M M; XorpSafeMemberCallback10B0(O* o, M m) : XorpMemberCallback10B0(o, m), SafeCallbackBase(o) {} ~XorpSafeMemberCallback10B0() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { if (valid()) { XorpMemberCallback10B0::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); } } }; template struct XorpMemberCallbackFactory10B0 { static XorpMemberCallback10B0* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10)) { return new XorpSafeMemberCallback10B0(o, p); } }; template struct XorpMemberCallbackFactory10B0 { static XorpMemberCallback10B0* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10)) { return new XorpMemberCallback10B0(o, p); }; }; /** * Factory function that creates a callback object targetted at a * member function with 10 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback10::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10)) { return XorpMemberCallbackFactory10B0::True>::make(o, p); } /** * Factory function that creates a callback object targetted at a * member function with 10 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback10::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10)) { return XorpMemberCallbackFactory10B0::True>::make(&o, p); } /** * @short Callback object for const member methods with 10 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstMemberCallback10B0 : public XorpCallback10 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10) const; XorpConstMemberCallback10B0(O* o, M m) : XorpCallback10(), _o(o), _m(m) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for void const member methods with 10 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstMemberCallback10B0 : public XorpCallback10 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10) const; XorpConstMemberCallback10B0(O* o, M m) : XorpCallback10(), _o(o), _m(m) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for const safe member methods with 10 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback10B0 : public XorpConstMemberCallback10B0, public SafeCallbackBase { typedef typename XorpConstMemberCallback10B0::M M; XorpConstSafeMemberCallback10B0(O* o, M m) : XorpConstMemberCallback10B0(o, m), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback10B0() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { if (valid()) { R r = XorpConstMemberCallback10B0::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); return r; } } }; /** * @short Callback object for void const safe member methods with 10 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback10B0 : public XorpConstMemberCallback10B0, public SafeCallbackBase { typedef typename XorpConstMemberCallback10B0::M M; XorpConstSafeMemberCallback10B0(O* o, M m) : XorpConstMemberCallback10B0(o, m), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback10B0() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { if (valid()) { XorpConstMemberCallback10B0::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); } } }; template struct XorpConstMemberCallbackFactory10B0 { static XorpConstMemberCallback10B0* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10) const) { return new XorpConstSafeMemberCallback10B0(o, p); } }; template struct XorpConstMemberCallbackFactory10B0 { static XorpConstMemberCallback10B0* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10) const) { return new XorpConstMemberCallback10B0(o, p); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 10 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback10::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10) const) { return XorpConstMemberCallbackFactory10B0::True>::make(o, p); } /** * Factory function that creates a callback object targetted at a * const member function with 10 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback10::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10) const) { return XorpConstMemberCallbackFactory10B0::True>::make(&o, p); } /** * @short Callback object for functions with 10 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpFunctionCallback10B1 : public XorpCallback10 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1); XorpFunctionCallback10B1(F f, BA1 ba1) : XorpCallback10(), _f(f), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1); return r; } protected: F _f; BA1 _ba1; }; /** * @short Callback object for void functions with 10 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpFunctionCallback10B1 : public XorpCallback10 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1); XorpFunctionCallback10B1(F f, BA1 ba1) : XorpCallback10(), _f(f), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1); } protected: F _f; BA1 _ba1; }; /** * Factory function that creates a callback object targetted at a * function with 10 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback10::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1), BA1 ba1) { return typename XorpCallback10::RefPtr(new XorpFunctionCallback10B1(f, ba1)); } /** * @short Callback object for member methods with 10 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpMemberCallback10B1 : public XorpCallback10 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1) ; XorpMemberCallback10B1(O* o, M m, BA1 ba1) : XorpCallback10(), _o(o), _m(m), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for void member methods with 10 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpMemberCallback10B1 : public XorpCallback10 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1) ; XorpMemberCallback10B1(O* o, M m, BA1 ba1) : XorpCallback10(), _o(o), _m(m), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for safe member methods with 10 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpSafeMemberCallback10B1 : public XorpMemberCallback10B1, public SafeCallbackBase { typedef typename XorpMemberCallback10B1::M M; XorpSafeMemberCallback10B1(O* o, M m, BA1 ba1) : XorpMemberCallback10B1(o, m, ba1), SafeCallbackBase(o) {} ~XorpSafeMemberCallback10B1() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { if (valid()) { R r = XorpMemberCallback10B1::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); return r; } } }; /** * @short Callback object for void safe member methods with 10 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpSafeMemberCallback10B1 : public XorpMemberCallback10B1, public SafeCallbackBase { typedef typename XorpMemberCallback10B1::M M; XorpSafeMemberCallback10B1(O* o, M m, BA1 ba1) : XorpMemberCallback10B1(o, m, ba1), SafeCallbackBase(o) {} ~XorpSafeMemberCallback10B1() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { if (valid()) { XorpMemberCallback10B1::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); } } }; template struct XorpMemberCallbackFactory10B1 { static XorpMemberCallback10B1* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1), BA1 ba1) { return new XorpSafeMemberCallback10B1(o, p, ba1); } }; template struct XorpMemberCallbackFactory10B1 { static XorpMemberCallback10B1* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1), BA1 ba1) { return new XorpMemberCallback10B1(o, p, ba1); }; }; /** * Factory function that creates a callback object targetted at a * member function with 10 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback10::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1), BA1 ba1) { return XorpMemberCallbackFactory10B1::True>::make(o, p, ba1); } /** * Factory function that creates a callback object targetted at a * member function with 10 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback10::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1), BA1 ba1) { return XorpMemberCallbackFactory10B1::True>::make(&o, p, ba1); } /** * @short Callback object for const member methods with 10 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstMemberCallback10B1 : public XorpCallback10 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1) const; XorpConstMemberCallback10B1(O* o, M m, BA1 ba1) : XorpCallback10(), _o(o), _m(m), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for void const member methods with 10 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstMemberCallback10B1 : public XorpCallback10 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1) const; XorpConstMemberCallback10B1(O* o, M m, BA1 ba1) : XorpCallback10(), _o(o), _m(m), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for const safe member methods with 10 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback10B1 : public XorpConstMemberCallback10B1, public SafeCallbackBase { typedef typename XorpConstMemberCallback10B1::M M; XorpConstSafeMemberCallback10B1(O* o, M m, BA1 ba1) : XorpConstMemberCallback10B1(o, m, ba1), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback10B1() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { if (valid()) { R r = XorpConstMemberCallback10B1::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); return r; } } }; /** * @short Callback object for void const safe member methods with 10 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback10B1 : public XorpConstMemberCallback10B1, public SafeCallbackBase { typedef typename XorpConstMemberCallback10B1::M M; XorpConstSafeMemberCallback10B1(O* o, M m, BA1 ba1) : XorpConstMemberCallback10B1(o, m, ba1), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback10B1() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { if (valid()) { XorpConstMemberCallback10B1::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); } } }; template struct XorpConstMemberCallbackFactory10B1 { static XorpConstMemberCallback10B1* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1) const, BA1 ba1) { return new XorpConstSafeMemberCallback10B1(o, p, ba1); } }; template struct XorpConstMemberCallbackFactory10B1 { static XorpConstMemberCallback10B1* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1) const, BA1 ba1) { return new XorpConstMemberCallback10B1(o, p, ba1); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 10 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback10::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1) const, BA1 ba1) { return XorpConstMemberCallbackFactory10B1::True>::make(o, p, ba1); } /** * Factory function that creates a callback object targetted at a * const member function with 10 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback10::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1) const, BA1 ba1) { return XorpConstMemberCallbackFactory10B1::True>::make(&o, p, ba1); } /** * @short Callback object for functions with 10 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpFunctionCallback10B2 : public XorpCallback10 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2); XorpFunctionCallback10B2(F f, BA1 ba1, BA2 ba2) : XorpCallback10(), _f(f), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1, _ba2); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; }; /** * @short Callback object for void functions with 10 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpFunctionCallback10B2 : public XorpCallback10 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2); XorpFunctionCallback10B2(F f, BA1 ba1, BA2 ba2) : XorpCallback10(), _f(f), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1, _ba2); } protected: F _f; BA1 _ba1; BA2 _ba2; }; /** * Factory function that creates a callback object targetted at a * function with 10 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback10::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2), BA1 ba1, BA2 ba2) { return typename XorpCallback10::RefPtr(new XorpFunctionCallback10B2(f, ba1, ba2)); } /** * @short Callback object for member methods with 10 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpMemberCallback10B2 : public XorpCallback10 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2) ; XorpMemberCallback10B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback10(), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1, _ba2); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for void member methods with 10 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpMemberCallback10B2 : public XorpCallback10 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2) ; XorpMemberCallback10B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback10(), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1, _ba2); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for safe member methods with 10 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpSafeMemberCallback10B2 : public XorpMemberCallback10B2, public SafeCallbackBase { typedef typename XorpMemberCallback10B2::M M; XorpSafeMemberCallback10B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpMemberCallback10B2(o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpSafeMemberCallback10B2() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { if (valid()) { R r = XorpMemberCallback10B2::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); return r; } } }; /** * @short Callback object for void safe member methods with 10 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpSafeMemberCallback10B2 : public XorpMemberCallback10B2, public SafeCallbackBase { typedef typename XorpMemberCallback10B2::M M; XorpSafeMemberCallback10B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpMemberCallback10B2(o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpSafeMemberCallback10B2() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { if (valid()) { XorpMemberCallback10B2::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); } } }; template struct XorpMemberCallbackFactory10B2 { static XorpMemberCallback10B2* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2), BA1 ba1, BA2 ba2) { return new XorpSafeMemberCallback10B2(o, p, ba1, ba2); } }; template struct XorpMemberCallbackFactory10B2 { static XorpMemberCallback10B2* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2), BA1 ba1, BA2 ba2) { return new XorpMemberCallback10B2(o, p, ba1, ba2); }; }; /** * Factory function that creates a callback object targetted at a * member function with 10 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback10::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2), BA1 ba1, BA2 ba2) { return XorpMemberCallbackFactory10B2::True>::make(o, p, ba1, ba2); } /** * Factory function that creates a callback object targetted at a * member function with 10 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback10::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2), BA1 ba1, BA2 ba2) { return XorpMemberCallbackFactory10B2::True>::make(&o, p, ba1, ba2); } /** * @short Callback object for const member methods with 10 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstMemberCallback10B2 : public XorpCallback10 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2) const; XorpConstMemberCallback10B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback10(), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1, _ba2); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for void const member methods with 10 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstMemberCallback10B2 : public XorpCallback10 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2) const; XorpConstMemberCallback10B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback10(), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1, _ba2); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for const safe member methods with 10 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback10B2 : public XorpConstMemberCallback10B2, public SafeCallbackBase { typedef typename XorpConstMemberCallback10B2::M M; XorpConstSafeMemberCallback10B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpConstMemberCallback10B2(o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback10B2() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { if (valid()) { R r = XorpConstMemberCallback10B2::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); return r; } } }; /** * @short Callback object for void const safe member methods with 10 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback10B2 : public XorpConstMemberCallback10B2, public SafeCallbackBase { typedef typename XorpConstMemberCallback10B2::M M; XorpConstSafeMemberCallback10B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpConstMemberCallback10B2(o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback10B2() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { if (valid()) { XorpConstMemberCallback10B2::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); } } }; template struct XorpConstMemberCallbackFactory10B2 { static XorpConstMemberCallback10B2* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2) const, BA1 ba1, BA2 ba2) { return new XorpConstSafeMemberCallback10B2(o, p, ba1, ba2); } }; template struct XorpConstMemberCallbackFactory10B2 { static XorpConstMemberCallback10B2* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2) const, BA1 ba1, BA2 ba2) { return new XorpConstMemberCallback10B2(o, p, ba1, ba2); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 10 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback10::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2) const, BA1 ba1, BA2 ba2) { return XorpConstMemberCallbackFactory10B2::True>::make(o, p, ba1, ba2); } /** * Factory function that creates a callback object targetted at a * const member function with 10 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback10::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2) const, BA1 ba1, BA2 ba2) { return XorpConstMemberCallbackFactory10B2::True>::make(&o, p, ba1, ba2); } /** * @short Callback object for functions with 10 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpFunctionCallback10B3 : public XorpCallback10 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3); XorpFunctionCallback10B3(F f, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback10(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1, _ba2, _ba3); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; }; /** * @short Callback object for void functions with 10 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpFunctionCallback10B3 : public XorpCallback10 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3); XorpFunctionCallback10B3(F f, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback10(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1, _ba2, _ba3); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; }; /** * Factory function that creates a callback object targetted at a * function with 10 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback10::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return typename XorpCallback10::RefPtr(new XorpFunctionCallback10B3(f, ba1, ba2, ba3)); } /** * @short Callback object for member methods with 10 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpMemberCallback10B3 : public XorpCallback10 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3) ; XorpMemberCallback10B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback10(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1, _ba2, _ba3); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for void member methods with 10 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpMemberCallback10B3 : public XorpCallback10 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3) ; XorpMemberCallback10B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback10(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1, _ba2, _ba3); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for safe member methods with 10 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpSafeMemberCallback10B3 : public XorpMemberCallback10B3, public SafeCallbackBase { typedef typename XorpMemberCallback10B3::M M; XorpSafeMemberCallback10B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpMemberCallback10B3(o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpSafeMemberCallback10B3() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { if (valid()) { R r = XorpMemberCallback10B3::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); return r; } } }; /** * @short Callback object for void safe member methods with 10 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpSafeMemberCallback10B3 : public XorpMemberCallback10B3, public SafeCallbackBase { typedef typename XorpMemberCallback10B3::M M; XorpSafeMemberCallback10B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpMemberCallback10B3(o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpSafeMemberCallback10B3() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { if (valid()) { XorpMemberCallback10B3::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); } } }; template struct XorpMemberCallbackFactory10B3 { static XorpMemberCallback10B3* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpSafeMemberCallback10B3(o, p, ba1, ba2, ba3); } }; template struct XorpMemberCallbackFactory10B3 { static XorpMemberCallback10B3* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpMemberCallback10B3(o, p, ba1, ba2, ba3); }; }; /** * Factory function that creates a callback object targetted at a * member function with 10 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback10::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return XorpMemberCallbackFactory10B3::True>::make(o, p, ba1, ba2, ba3); } /** * Factory function that creates a callback object targetted at a * member function with 10 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback10::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return XorpMemberCallbackFactory10B3::True>::make(&o, p, ba1, ba2, ba3); } /** * @short Callback object for const member methods with 10 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstMemberCallback10B3 : public XorpCallback10 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3) const; XorpConstMemberCallback10B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback10(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1, _ba2, _ba3); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for void const member methods with 10 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstMemberCallback10B3 : public XorpCallback10 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3) const; XorpConstMemberCallback10B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback10(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1, _ba2, _ba3); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for const safe member methods with 10 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback10B3 : public XorpConstMemberCallback10B3, public SafeCallbackBase { typedef typename XorpConstMemberCallback10B3::M M; XorpConstSafeMemberCallback10B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpConstMemberCallback10B3(o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback10B3() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { if (valid()) { R r = XorpConstMemberCallback10B3::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); return r; } } }; /** * @short Callback object for void const safe member methods with 10 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback10B3 : public XorpConstMemberCallback10B3, public SafeCallbackBase { typedef typename XorpConstMemberCallback10B3::M M; XorpConstSafeMemberCallback10B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpConstMemberCallback10B3(o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback10B3() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { if (valid()) { XorpConstMemberCallback10B3::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); } } }; template struct XorpConstMemberCallbackFactory10B3 { static XorpConstMemberCallback10B3* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpConstSafeMemberCallback10B3(o, p, ba1, ba2, ba3); } }; template struct XorpConstMemberCallbackFactory10B3 { static XorpConstMemberCallback10B3* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpConstMemberCallback10B3(o, p, ba1, ba2, ba3); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 10 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback10::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return XorpConstMemberCallbackFactory10B3::True>::make(o, p, ba1, ba2, ba3); } /** * Factory function that creates a callback object targetted at a * const member function with 10 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback10::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return XorpConstMemberCallbackFactory10B3::True>::make(&o, p, ba1, ba2, ba3); } /** * @short Callback object for functions with 10 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpFunctionCallback10B4 : public XorpCallback10 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4); XorpFunctionCallback10B4(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback10(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1, _ba2, _ba3, _ba4); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; }; /** * @short Callback object for void functions with 10 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpFunctionCallback10B4 : public XorpCallback10 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4); XorpFunctionCallback10B4(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback10(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1, _ba2, _ba3, _ba4); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; }; /** * Factory function that creates a callback object targetted at a * function with 10 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback10::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return typename XorpCallback10::RefPtr(new XorpFunctionCallback10B4(f, ba1, ba2, ba3, ba4)); } /** * @short Callback object for member methods with 10 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpMemberCallback10B4 : public XorpCallback10 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4) ; XorpMemberCallback10B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback10(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1, _ba2, _ba3, _ba4); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for void member methods with 10 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpMemberCallback10B4 : public XorpCallback10 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4) ; XorpMemberCallback10B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback10(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1, _ba2, _ba3, _ba4); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for safe member methods with 10 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpSafeMemberCallback10B4 : public XorpMemberCallback10B4, public SafeCallbackBase { typedef typename XorpMemberCallback10B4::M M; XorpSafeMemberCallback10B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpMemberCallback10B4(o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpSafeMemberCallback10B4() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { if (valid()) { R r = XorpMemberCallback10B4::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); return r; } } }; /** * @short Callback object for void safe member methods with 10 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpSafeMemberCallback10B4 : public XorpMemberCallback10B4, public SafeCallbackBase { typedef typename XorpMemberCallback10B4::M M; XorpSafeMemberCallback10B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpMemberCallback10B4(o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpSafeMemberCallback10B4() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { if (valid()) { XorpMemberCallback10B4::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); } } }; template struct XorpMemberCallbackFactory10B4 { static XorpMemberCallback10B4* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpSafeMemberCallback10B4(o, p, ba1, ba2, ba3, ba4); } }; template struct XorpMemberCallbackFactory10B4 { static XorpMemberCallback10B4* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpMemberCallback10B4(o, p, ba1, ba2, ba3, ba4); }; }; /** * Factory function that creates a callback object targetted at a * member function with 10 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback10::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpMemberCallbackFactory10B4::True>::make(o, p, ba1, ba2, ba3, ba4); } /** * Factory function that creates a callback object targetted at a * member function with 10 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback10::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpMemberCallbackFactory10B4::True>::make(&o, p, ba1, ba2, ba3, ba4); } /** * @short Callback object for const member methods with 10 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstMemberCallback10B4 : public XorpCallback10 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4) const; XorpConstMemberCallback10B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback10(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1, _ba2, _ba3, _ba4); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for void const member methods with 10 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstMemberCallback10B4 : public XorpCallback10 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4) const; XorpConstMemberCallback10B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback10(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1, _ba2, _ba3, _ba4); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for const safe member methods with 10 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback10B4 : public XorpConstMemberCallback10B4, public SafeCallbackBase { typedef typename XorpConstMemberCallback10B4::M M; XorpConstSafeMemberCallback10B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpConstMemberCallback10B4(o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback10B4() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { if (valid()) { R r = XorpConstMemberCallback10B4::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); return r; } } }; /** * @short Callback object for void const safe member methods with 10 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback10B4 : public XorpConstMemberCallback10B4, public SafeCallbackBase { typedef typename XorpConstMemberCallback10B4::M M; XorpConstSafeMemberCallback10B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpConstMemberCallback10B4(o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback10B4() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { if (valid()) { XorpConstMemberCallback10B4::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); } } }; template struct XorpConstMemberCallbackFactory10B4 { static XorpConstMemberCallback10B4* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpConstSafeMemberCallback10B4(o, p, ba1, ba2, ba3, ba4); } }; template struct XorpConstMemberCallbackFactory10B4 { static XorpConstMemberCallback10B4* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpConstMemberCallback10B4(o, p, ba1, ba2, ba3, ba4); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 10 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback10::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpConstMemberCallbackFactory10B4::True>::make(o, p, ba1, ba2, ba3, ba4); } /** * Factory function that creates a callback object targetted at a * const member function with 10 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback10::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpConstMemberCallbackFactory10B4::True>::make(&o, p, ba1, ba2, ba3, ba4); } /** * @short Callback object for functions with 10 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpFunctionCallback10B5 : public XorpCallback10 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4, BA5); XorpFunctionCallback10B5(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback10(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1, _ba2, _ba3, _ba4, _ba5); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; }; /** * @short Callback object for void functions with 10 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpFunctionCallback10B5 : public XorpCallback10 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4, BA5); XorpFunctionCallback10B5(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback10(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1, _ba2, _ba3, _ba4, _ba5); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; }; /** * Factory function that creates a callback object targetted at a * function with 10 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback10::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return typename XorpCallback10::RefPtr(new XorpFunctionCallback10B5(f, ba1, ba2, ba3, ba4, ba5)); } /** * @short Callback object for member methods with 10 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpMemberCallback10B5 : public XorpCallback10 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4, BA5) ; XorpMemberCallback10B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback10(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1, _ba2, _ba3, _ba4, _ba5); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for void member methods with 10 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpMemberCallback10B5 : public XorpCallback10 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4, BA5) ; XorpMemberCallback10B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback10(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1, _ba2, _ba3, _ba4, _ba5); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for safe member methods with 10 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpSafeMemberCallback10B5 : public XorpMemberCallback10B5, public SafeCallbackBase { typedef typename XorpMemberCallback10B5::M M; XorpSafeMemberCallback10B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpMemberCallback10B5(o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpSafeMemberCallback10B5() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { if (valid()) { R r = XorpMemberCallback10B5::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); return r; } } }; /** * @short Callback object for void safe member methods with 10 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpSafeMemberCallback10B5 : public XorpMemberCallback10B5, public SafeCallbackBase { typedef typename XorpMemberCallback10B5::M M; XorpSafeMemberCallback10B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpMemberCallback10B5(o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpSafeMemberCallback10B5() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { if (valid()) { XorpMemberCallback10B5::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); } } }; template struct XorpMemberCallbackFactory10B5 { static XorpMemberCallback10B5* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpSafeMemberCallback10B5(o, p, ba1, ba2, ba3, ba4, ba5); } }; template struct XorpMemberCallbackFactory10B5 { static XorpMemberCallback10B5* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpMemberCallback10B5(o, p, ba1, ba2, ba3, ba4, ba5); }; }; /** * Factory function that creates a callback object targetted at a * member function with 10 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback10::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpMemberCallbackFactory10B5::True>::make(o, p, ba1, ba2, ba3, ba4, ba5); } /** * Factory function that creates a callback object targetted at a * member function with 10 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback10::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpMemberCallbackFactory10B5::True>::make(&o, p, ba1, ba2, ba3, ba4, ba5); } /** * @short Callback object for const member methods with 10 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstMemberCallback10B5 : public XorpCallback10 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4, BA5) const; XorpConstMemberCallback10B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback10(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1, _ba2, _ba3, _ba4, _ba5); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for void const member methods with 10 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstMemberCallback10B5 : public XorpCallback10 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4, BA5) const; XorpConstMemberCallback10B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback10(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1, _ba2, _ba3, _ba4, _ba5); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for const safe member methods with 10 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback10B5 : public XorpConstMemberCallback10B5, public SafeCallbackBase { typedef typename XorpConstMemberCallback10B5::M M; XorpConstSafeMemberCallback10B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpConstMemberCallback10B5(o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback10B5() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { if (valid()) { R r = XorpConstMemberCallback10B5::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); return r; } } }; /** * @short Callback object for void const safe member methods with 10 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback10B5 : public XorpConstMemberCallback10B5, public SafeCallbackBase { typedef typename XorpConstMemberCallback10B5::M M; XorpConstSafeMemberCallback10B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpConstMemberCallback10B5(o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback10B5() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { if (valid()) { XorpConstMemberCallback10B5::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); } } }; template struct XorpConstMemberCallbackFactory10B5 { static XorpConstMemberCallback10B5* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpConstSafeMemberCallback10B5(o, p, ba1, ba2, ba3, ba4, ba5); } }; template struct XorpConstMemberCallbackFactory10B5 { static XorpConstMemberCallback10B5* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpConstMemberCallback10B5(o, p, ba1, ba2, ba3, ba4, ba5); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 10 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback10::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpConstMemberCallbackFactory10B5::True>::make(o, p, ba1, ba2, ba3, ba4, ba5); } /** * Factory function that creates a callback object targetted at a * const member function with 10 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback10::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpConstMemberCallbackFactory10B5::True>::make(&o, p, ba1, ba2, ba3, ba4, ba5); } /** * @short Callback object for functions with 10 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpFunctionCallback10B6 : public XorpCallback10 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4, BA5, BA6); XorpFunctionCallback10B6(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback10(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; BA6 _ba6; }; /** * @short Callback object for void functions with 10 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpFunctionCallback10B6 : public XorpCallback10 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4, BA5, BA6); XorpFunctionCallback10B6(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback10(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; BA6 _ba6; }; /** * Factory function that creates a callback object targetted at a * function with 10 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback10::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return typename XorpCallback10::RefPtr(new XorpFunctionCallback10B6(f, ba1, ba2, ba3, ba4, ba5, ba6)); } /** * @short Callback object for member methods with 10 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpMemberCallback10B6 : public XorpCallback10 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4, BA5, BA6) ; XorpMemberCallback10B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback10(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for void member methods with 10 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpMemberCallback10B6 : public XorpCallback10 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4, BA5, BA6) ; XorpMemberCallback10B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback10(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for safe member methods with 10 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpSafeMemberCallback10B6 : public XorpMemberCallback10B6, public SafeCallbackBase { typedef typename XorpMemberCallback10B6::M M; XorpSafeMemberCallback10B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpMemberCallback10B6(o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpSafeMemberCallback10B6() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { if (valid()) { R r = XorpMemberCallback10B6::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); return r; } } }; /** * @short Callback object for void safe member methods with 10 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpSafeMemberCallback10B6 : public XorpMemberCallback10B6, public SafeCallbackBase { typedef typename XorpMemberCallback10B6::M M; XorpSafeMemberCallback10B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpMemberCallback10B6(o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpSafeMemberCallback10B6() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { if (valid()) { XorpMemberCallback10B6::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); } } }; template struct XorpMemberCallbackFactory10B6 { static XorpMemberCallback10B6* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpSafeMemberCallback10B6(o, p, ba1, ba2, ba3, ba4, ba5, ba6); } }; template struct XorpMemberCallbackFactory10B6 { static XorpMemberCallback10B6* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpMemberCallback10B6(o, p, ba1, ba2, ba3, ba4, ba5, ba6); }; }; /** * Factory function that creates a callback object targetted at a * member function with 10 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback10::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpMemberCallbackFactory10B6::True>::make(o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * Factory function that creates a callback object targetted at a * member function with 10 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback10::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpMemberCallbackFactory10B6::True>::make(&o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * @short Callback object for const member methods with 10 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstMemberCallback10B6 : public XorpCallback10 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4, BA5, BA6) const; XorpConstMemberCallback10B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback10(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for void const member methods with 10 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstMemberCallback10B6 : public XorpCallback10 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4, BA5, BA6) const; XorpConstMemberCallback10B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback10(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for const safe member methods with 10 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback10B6 : public XorpConstMemberCallback10B6, public SafeCallbackBase { typedef typename XorpConstMemberCallback10B6::M M; XorpConstSafeMemberCallback10B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpConstMemberCallback10B6(o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback10B6() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { if (valid()) { R r = XorpConstMemberCallback10B6::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); return r; } } }; /** * @short Callback object for void const safe member methods with 10 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback10B6 : public XorpConstMemberCallback10B6, public SafeCallbackBase { typedef typename XorpConstMemberCallback10B6::M M; XorpConstSafeMemberCallback10B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpConstMemberCallback10B6(o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback10B6() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { if (valid()) { XorpConstMemberCallback10B6::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); } } }; template struct XorpConstMemberCallbackFactory10B6 { static XorpConstMemberCallback10B6* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpConstSafeMemberCallback10B6(o, p, ba1, ba2, ba3, ba4, ba5, ba6); } }; template struct XorpConstMemberCallbackFactory10B6 { static XorpConstMemberCallback10B6* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpConstMemberCallback10B6(o, p, ba1, ba2, ba3, ba4, ba5, ba6); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 10 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback10::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpConstMemberCallbackFactory10B6::True>::make(o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * Factory function that creates a callback object targetted at a * const member function with 10 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback10::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpConstMemberCallbackFactory10B6::True>::make(&o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /////////////////////////////////////////////////////////////////////////////// // // Code relating to callbacks with 11 late args // /** * @short Base class for callbacks with 11 dispatch time args. */ template struct XorpCallback11 { typedef ref_ptr RefPtr; virtual ~XorpCallback11() {} virtual R dispatch(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11) = 0; }; /** * @short Callback object for functions with 11 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpFunctionCallback11B0 : public XorpCallback11 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11); XorpFunctionCallback11B0(F f) : XorpCallback11(), _f(f) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); return r; } protected: F _f; }; /** * @short Callback object for void functions with 11 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpFunctionCallback11B0 : public XorpCallback11 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11); XorpFunctionCallback11B0(F f) : XorpCallback11(), _f(f) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); } protected: F _f; }; /** * Factory function that creates a callback object targetted at a * function with 11 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback11::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11)) { return typename XorpCallback11::RefPtr(new XorpFunctionCallback11B0(f)); } /** * @short Callback object for member methods with 11 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpMemberCallback11B0 : public XorpCallback11 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11) ; XorpMemberCallback11B0(O* o, M m) : XorpCallback11(), _o(o), _m(m) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for void member methods with 11 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpMemberCallback11B0 : public XorpCallback11 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11) ; XorpMemberCallback11B0(O* o, M m) : XorpCallback11(), _o(o), _m(m) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for safe member methods with 11 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpSafeMemberCallback11B0 : public XorpMemberCallback11B0, public SafeCallbackBase { typedef typename XorpMemberCallback11B0::M M; XorpSafeMemberCallback11B0(O* o, M m) : XorpMemberCallback11B0(o, m), SafeCallbackBase(o) {} ~XorpSafeMemberCallback11B0() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { if (valid()) { R r = XorpMemberCallback11B0::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); return r; } } }; /** * @short Callback object for void safe member methods with 11 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpSafeMemberCallback11B0 : public XorpMemberCallback11B0, public SafeCallbackBase { typedef typename XorpMemberCallback11B0::M M; XorpSafeMemberCallback11B0(O* o, M m) : XorpMemberCallback11B0(o, m), SafeCallbackBase(o) {} ~XorpSafeMemberCallback11B0() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { if (valid()) { XorpMemberCallback11B0::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); } } }; template struct XorpMemberCallbackFactory11B0 { static XorpMemberCallback11B0* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11)) { return new XorpSafeMemberCallback11B0(o, p); } }; template struct XorpMemberCallbackFactory11B0 { static XorpMemberCallback11B0* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11)) { return new XorpMemberCallback11B0(o, p); }; }; /** * Factory function that creates a callback object targetted at a * member function with 11 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback11::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11)) { return XorpMemberCallbackFactory11B0::True>::make(o, p); } /** * Factory function that creates a callback object targetted at a * member function with 11 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback11::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11)) { return XorpMemberCallbackFactory11B0::True>::make(&o, p); } /** * @short Callback object for const member methods with 11 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstMemberCallback11B0 : public XorpCallback11 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11) const; XorpConstMemberCallback11B0(O* o, M m) : XorpCallback11(), _o(o), _m(m) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for void const member methods with 11 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstMemberCallback11B0 : public XorpCallback11 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11) const; XorpConstMemberCallback11B0(O* o, M m) : XorpCallback11(), _o(o), _m(m) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for const safe member methods with 11 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback11B0 : public XorpConstMemberCallback11B0, public SafeCallbackBase { typedef typename XorpConstMemberCallback11B0::M M; XorpConstSafeMemberCallback11B0(O* o, M m) : XorpConstMemberCallback11B0(o, m), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback11B0() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { if (valid()) { R r = XorpConstMemberCallback11B0::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); return r; } } }; /** * @short Callback object for void const safe member methods with 11 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback11B0 : public XorpConstMemberCallback11B0, public SafeCallbackBase { typedef typename XorpConstMemberCallback11B0::M M; XorpConstSafeMemberCallback11B0(O* o, M m) : XorpConstMemberCallback11B0(o, m), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback11B0() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { if (valid()) { XorpConstMemberCallback11B0::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); } } }; template struct XorpConstMemberCallbackFactory11B0 { static XorpConstMemberCallback11B0* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11) const) { return new XorpConstSafeMemberCallback11B0(o, p); } }; template struct XorpConstMemberCallbackFactory11B0 { static XorpConstMemberCallback11B0* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11) const) { return new XorpConstMemberCallback11B0(o, p); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 11 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback11::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11) const) { return XorpConstMemberCallbackFactory11B0::True>::make(o, p); } /** * Factory function that creates a callback object targetted at a * const member function with 11 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback11::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11) const) { return XorpConstMemberCallbackFactory11B0::True>::make(&o, p); } /** * @short Callback object for functions with 11 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpFunctionCallback11B1 : public XorpCallback11 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1); XorpFunctionCallback11B1(F f, BA1 ba1) : XorpCallback11(), _f(f), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1); return r; } protected: F _f; BA1 _ba1; }; /** * @short Callback object for void functions with 11 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpFunctionCallback11B1 : public XorpCallback11 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1); XorpFunctionCallback11B1(F f, BA1 ba1) : XorpCallback11(), _f(f), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1); } protected: F _f; BA1 _ba1; }; /** * Factory function that creates a callback object targetted at a * function with 11 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback11::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1), BA1 ba1) { return typename XorpCallback11::RefPtr(new XorpFunctionCallback11B1(f, ba1)); } /** * @short Callback object for member methods with 11 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpMemberCallback11B1 : public XorpCallback11 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1) ; XorpMemberCallback11B1(O* o, M m, BA1 ba1) : XorpCallback11(), _o(o), _m(m), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for void member methods with 11 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpMemberCallback11B1 : public XorpCallback11 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1) ; XorpMemberCallback11B1(O* o, M m, BA1 ba1) : XorpCallback11(), _o(o), _m(m), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for safe member methods with 11 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpSafeMemberCallback11B1 : public XorpMemberCallback11B1, public SafeCallbackBase { typedef typename XorpMemberCallback11B1::M M; XorpSafeMemberCallback11B1(O* o, M m, BA1 ba1) : XorpMemberCallback11B1(o, m, ba1), SafeCallbackBase(o) {} ~XorpSafeMemberCallback11B1() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { if (valid()) { R r = XorpMemberCallback11B1::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); return r; } } }; /** * @short Callback object for void safe member methods with 11 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpSafeMemberCallback11B1 : public XorpMemberCallback11B1, public SafeCallbackBase { typedef typename XorpMemberCallback11B1::M M; XorpSafeMemberCallback11B1(O* o, M m, BA1 ba1) : XorpMemberCallback11B1(o, m, ba1), SafeCallbackBase(o) {} ~XorpSafeMemberCallback11B1() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { if (valid()) { XorpMemberCallback11B1::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); } } }; template struct XorpMemberCallbackFactory11B1 { static XorpMemberCallback11B1* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1), BA1 ba1) { return new XorpSafeMemberCallback11B1(o, p, ba1); } }; template struct XorpMemberCallbackFactory11B1 { static XorpMemberCallback11B1* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1), BA1 ba1) { return new XorpMemberCallback11B1(o, p, ba1); }; }; /** * Factory function that creates a callback object targetted at a * member function with 11 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback11::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1), BA1 ba1) { return XorpMemberCallbackFactory11B1::True>::make(o, p, ba1); } /** * Factory function that creates a callback object targetted at a * member function with 11 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback11::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1), BA1 ba1) { return XorpMemberCallbackFactory11B1::True>::make(&o, p, ba1); } /** * @short Callback object for const member methods with 11 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstMemberCallback11B1 : public XorpCallback11 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1) const; XorpConstMemberCallback11B1(O* o, M m, BA1 ba1) : XorpCallback11(), _o(o), _m(m), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for void const member methods with 11 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstMemberCallback11B1 : public XorpCallback11 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1) const; XorpConstMemberCallback11B1(O* o, M m, BA1 ba1) : XorpCallback11(), _o(o), _m(m), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for const safe member methods with 11 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback11B1 : public XorpConstMemberCallback11B1, public SafeCallbackBase { typedef typename XorpConstMemberCallback11B1::M M; XorpConstSafeMemberCallback11B1(O* o, M m, BA1 ba1) : XorpConstMemberCallback11B1(o, m, ba1), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback11B1() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { if (valid()) { R r = XorpConstMemberCallback11B1::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); return r; } } }; /** * @short Callback object for void const safe member methods with 11 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback11B1 : public XorpConstMemberCallback11B1, public SafeCallbackBase { typedef typename XorpConstMemberCallback11B1::M M; XorpConstSafeMemberCallback11B1(O* o, M m, BA1 ba1) : XorpConstMemberCallback11B1(o, m, ba1), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback11B1() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { if (valid()) { XorpConstMemberCallback11B1::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); } } }; template struct XorpConstMemberCallbackFactory11B1 { static XorpConstMemberCallback11B1* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1) const, BA1 ba1) { return new XorpConstSafeMemberCallback11B1(o, p, ba1); } }; template struct XorpConstMemberCallbackFactory11B1 { static XorpConstMemberCallback11B1* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1) const, BA1 ba1) { return new XorpConstMemberCallback11B1(o, p, ba1); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 11 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback11::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1) const, BA1 ba1) { return XorpConstMemberCallbackFactory11B1::True>::make(o, p, ba1); } /** * Factory function that creates a callback object targetted at a * const member function with 11 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback11::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1) const, BA1 ba1) { return XorpConstMemberCallbackFactory11B1::True>::make(&o, p, ba1); } /** * @short Callback object for functions with 11 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpFunctionCallback11B2 : public XorpCallback11 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2); XorpFunctionCallback11B2(F f, BA1 ba1, BA2 ba2) : XorpCallback11(), _f(f), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1, _ba2); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; }; /** * @short Callback object for void functions with 11 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpFunctionCallback11B2 : public XorpCallback11 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2); XorpFunctionCallback11B2(F f, BA1 ba1, BA2 ba2) : XorpCallback11(), _f(f), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1, _ba2); } protected: F _f; BA1 _ba1; BA2 _ba2; }; /** * Factory function that creates a callback object targetted at a * function with 11 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback11::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2), BA1 ba1, BA2 ba2) { return typename XorpCallback11::RefPtr(new XorpFunctionCallback11B2(f, ba1, ba2)); } /** * @short Callback object for member methods with 11 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpMemberCallback11B2 : public XorpCallback11 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2) ; XorpMemberCallback11B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback11(), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1, _ba2); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for void member methods with 11 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpMemberCallback11B2 : public XorpCallback11 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2) ; XorpMemberCallback11B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback11(), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1, _ba2); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for safe member methods with 11 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpSafeMemberCallback11B2 : public XorpMemberCallback11B2, public SafeCallbackBase { typedef typename XorpMemberCallback11B2::M M; XorpSafeMemberCallback11B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpMemberCallback11B2(o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpSafeMemberCallback11B2() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { if (valid()) { R r = XorpMemberCallback11B2::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); return r; } } }; /** * @short Callback object for void safe member methods with 11 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpSafeMemberCallback11B2 : public XorpMemberCallback11B2, public SafeCallbackBase { typedef typename XorpMemberCallback11B2::M M; XorpSafeMemberCallback11B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpMemberCallback11B2(o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpSafeMemberCallback11B2() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { if (valid()) { XorpMemberCallback11B2::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); } } }; template struct XorpMemberCallbackFactory11B2 { static XorpMemberCallback11B2* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2), BA1 ba1, BA2 ba2) { return new XorpSafeMemberCallback11B2(o, p, ba1, ba2); } }; template struct XorpMemberCallbackFactory11B2 { static XorpMemberCallback11B2* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2), BA1 ba1, BA2 ba2) { return new XorpMemberCallback11B2(o, p, ba1, ba2); }; }; /** * Factory function that creates a callback object targetted at a * member function with 11 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback11::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2), BA1 ba1, BA2 ba2) { return XorpMemberCallbackFactory11B2::True>::make(o, p, ba1, ba2); } /** * Factory function that creates a callback object targetted at a * member function with 11 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback11::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2), BA1 ba1, BA2 ba2) { return XorpMemberCallbackFactory11B2::True>::make(&o, p, ba1, ba2); } /** * @short Callback object for const member methods with 11 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstMemberCallback11B2 : public XorpCallback11 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2) const; XorpConstMemberCallback11B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback11(), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1, _ba2); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for void const member methods with 11 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstMemberCallback11B2 : public XorpCallback11 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2) const; XorpConstMemberCallback11B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback11(), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1, _ba2); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for const safe member methods with 11 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback11B2 : public XorpConstMemberCallback11B2, public SafeCallbackBase { typedef typename XorpConstMemberCallback11B2::M M; XorpConstSafeMemberCallback11B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpConstMemberCallback11B2(o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback11B2() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { if (valid()) { R r = XorpConstMemberCallback11B2::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); return r; } } }; /** * @short Callback object for void const safe member methods with 11 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback11B2 : public XorpConstMemberCallback11B2, public SafeCallbackBase { typedef typename XorpConstMemberCallback11B2::M M; XorpConstSafeMemberCallback11B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpConstMemberCallback11B2(o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback11B2() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { if (valid()) { XorpConstMemberCallback11B2::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); } } }; template struct XorpConstMemberCallbackFactory11B2 { static XorpConstMemberCallback11B2* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2) const, BA1 ba1, BA2 ba2) { return new XorpConstSafeMemberCallback11B2(o, p, ba1, ba2); } }; template struct XorpConstMemberCallbackFactory11B2 { static XorpConstMemberCallback11B2* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2) const, BA1 ba1, BA2 ba2) { return new XorpConstMemberCallback11B2(o, p, ba1, ba2); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 11 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback11::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2) const, BA1 ba1, BA2 ba2) { return XorpConstMemberCallbackFactory11B2::True>::make(o, p, ba1, ba2); } /** * Factory function that creates a callback object targetted at a * const member function with 11 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback11::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2) const, BA1 ba1, BA2 ba2) { return XorpConstMemberCallbackFactory11B2::True>::make(&o, p, ba1, ba2); } /** * @short Callback object for functions with 11 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpFunctionCallback11B3 : public XorpCallback11 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3); XorpFunctionCallback11B3(F f, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback11(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1, _ba2, _ba3); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; }; /** * @short Callback object for void functions with 11 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpFunctionCallback11B3 : public XorpCallback11 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3); XorpFunctionCallback11B3(F f, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback11(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1, _ba2, _ba3); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; }; /** * Factory function that creates a callback object targetted at a * function with 11 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback11::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return typename XorpCallback11::RefPtr(new XorpFunctionCallback11B3(f, ba1, ba2, ba3)); } /** * @short Callback object for member methods with 11 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpMemberCallback11B3 : public XorpCallback11 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3) ; XorpMemberCallback11B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback11(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1, _ba2, _ba3); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for void member methods with 11 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpMemberCallback11B3 : public XorpCallback11 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3) ; XorpMemberCallback11B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback11(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1, _ba2, _ba3); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for safe member methods with 11 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpSafeMemberCallback11B3 : public XorpMemberCallback11B3, public SafeCallbackBase { typedef typename XorpMemberCallback11B3::M M; XorpSafeMemberCallback11B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpMemberCallback11B3(o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpSafeMemberCallback11B3() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { if (valid()) { R r = XorpMemberCallback11B3::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); return r; } } }; /** * @short Callback object for void safe member methods with 11 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpSafeMemberCallback11B3 : public XorpMemberCallback11B3, public SafeCallbackBase { typedef typename XorpMemberCallback11B3::M M; XorpSafeMemberCallback11B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpMemberCallback11B3(o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpSafeMemberCallback11B3() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { if (valid()) { XorpMemberCallback11B3::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); } } }; template struct XorpMemberCallbackFactory11B3 { static XorpMemberCallback11B3* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpSafeMemberCallback11B3(o, p, ba1, ba2, ba3); } }; template struct XorpMemberCallbackFactory11B3 { static XorpMemberCallback11B3* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpMemberCallback11B3(o, p, ba1, ba2, ba3); }; }; /** * Factory function that creates a callback object targetted at a * member function with 11 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback11::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return XorpMemberCallbackFactory11B3::True>::make(o, p, ba1, ba2, ba3); } /** * Factory function that creates a callback object targetted at a * member function with 11 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback11::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return XorpMemberCallbackFactory11B3::True>::make(&o, p, ba1, ba2, ba3); } /** * @short Callback object for const member methods with 11 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstMemberCallback11B3 : public XorpCallback11 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3) const; XorpConstMemberCallback11B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback11(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1, _ba2, _ba3); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for void const member methods with 11 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstMemberCallback11B3 : public XorpCallback11 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3) const; XorpConstMemberCallback11B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback11(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1, _ba2, _ba3); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for const safe member methods with 11 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback11B3 : public XorpConstMemberCallback11B3, public SafeCallbackBase { typedef typename XorpConstMemberCallback11B3::M M; XorpConstSafeMemberCallback11B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpConstMemberCallback11B3(o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback11B3() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { if (valid()) { R r = XorpConstMemberCallback11B3::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); return r; } } }; /** * @short Callback object for void const safe member methods with 11 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback11B3 : public XorpConstMemberCallback11B3, public SafeCallbackBase { typedef typename XorpConstMemberCallback11B3::M M; XorpConstSafeMemberCallback11B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpConstMemberCallback11B3(o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback11B3() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { if (valid()) { XorpConstMemberCallback11B3::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); } } }; template struct XorpConstMemberCallbackFactory11B3 { static XorpConstMemberCallback11B3* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpConstSafeMemberCallback11B3(o, p, ba1, ba2, ba3); } }; template struct XorpConstMemberCallbackFactory11B3 { static XorpConstMemberCallback11B3* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpConstMemberCallback11B3(o, p, ba1, ba2, ba3); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 11 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback11::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return XorpConstMemberCallbackFactory11B3::True>::make(o, p, ba1, ba2, ba3); } /** * Factory function that creates a callback object targetted at a * const member function with 11 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback11::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return XorpConstMemberCallbackFactory11B3::True>::make(&o, p, ba1, ba2, ba3); } /** * @short Callback object for functions with 11 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpFunctionCallback11B4 : public XorpCallback11 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4); XorpFunctionCallback11B4(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback11(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1, _ba2, _ba3, _ba4); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; }; /** * @short Callback object for void functions with 11 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpFunctionCallback11B4 : public XorpCallback11 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4); XorpFunctionCallback11B4(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback11(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1, _ba2, _ba3, _ba4); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; }; /** * Factory function that creates a callback object targetted at a * function with 11 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback11::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return typename XorpCallback11::RefPtr(new XorpFunctionCallback11B4(f, ba1, ba2, ba3, ba4)); } /** * @short Callback object for member methods with 11 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpMemberCallback11B4 : public XorpCallback11 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4) ; XorpMemberCallback11B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback11(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1, _ba2, _ba3, _ba4); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for void member methods with 11 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpMemberCallback11B4 : public XorpCallback11 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4) ; XorpMemberCallback11B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback11(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1, _ba2, _ba3, _ba4); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for safe member methods with 11 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpSafeMemberCallback11B4 : public XorpMemberCallback11B4, public SafeCallbackBase { typedef typename XorpMemberCallback11B4::M M; XorpSafeMemberCallback11B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpMemberCallback11B4(o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpSafeMemberCallback11B4() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { if (valid()) { R r = XorpMemberCallback11B4::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); return r; } } }; /** * @short Callback object for void safe member methods with 11 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpSafeMemberCallback11B4 : public XorpMemberCallback11B4, public SafeCallbackBase { typedef typename XorpMemberCallback11B4::M M; XorpSafeMemberCallback11B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpMemberCallback11B4(o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpSafeMemberCallback11B4() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { if (valid()) { XorpMemberCallback11B4::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); } } }; template struct XorpMemberCallbackFactory11B4 { static XorpMemberCallback11B4* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpSafeMemberCallback11B4(o, p, ba1, ba2, ba3, ba4); } }; template struct XorpMemberCallbackFactory11B4 { static XorpMemberCallback11B4* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpMemberCallback11B4(o, p, ba1, ba2, ba3, ba4); }; }; /** * Factory function that creates a callback object targetted at a * member function with 11 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback11::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpMemberCallbackFactory11B4::True>::make(o, p, ba1, ba2, ba3, ba4); } /** * Factory function that creates a callback object targetted at a * member function with 11 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback11::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpMemberCallbackFactory11B4::True>::make(&o, p, ba1, ba2, ba3, ba4); } /** * @short Callback object for const member methods with 11 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstMemberCallback11B4 : public XorpCallback11 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4) const; XorpConstMemberCallback11B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback11(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1, _ba2, _ba3, _ba4); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for void const member methods with 11 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstMemberCallback11B4 : public XorpCallback11 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4) const; XorpConstMemberCallback11B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback11(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1, _ba2, _ba3, _ba4); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for const safe member methods with 11 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback11B4 : public XorpConstMemberCallback11B4, public SafeCallbackBase { typedef typename XorpConstMemberCallback11B4::M M; XorpConstSafeMemberCallback11B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpConstMemberCallback11B4(o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback11B4() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { if (valid()) { R r = XorpConstMemberCallback11B4::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); return r; } } }; /** * @short Callback object for void const safe member methods with 11 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback11B4 : public XorpConstMemberCallback11B4, public SafeCallbackBase { typedef typename XorpConstMemberCallback11B4::M M; XorpConstSafeMemberCallback11B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpConstMemberCallback11B4(o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback11B4() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { if (valid()) { XorpConstMemberCallback11B4::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); } } }; template struct XorpConstMemberCallbackFactory11B4 { static XorpConstMemberCallback11B4* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpConstSafeMemberCallback11B4(o, p, ba1, ba2, ba3, ba4); } }; template struct XorpConstMemberCallbackFactory11B4 { static XorpConstMemberCallback11B4* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpConstMemberCallback11B4(o, p, ba1, ba2, ba3, ba4); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 11 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback11::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpConstMemberCallbackFactory11B4::True>::make(o, p, ba1, ba2, ba3, ba4); } /** * Factory function that creates a callback object targetted at a * const member function with 11 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback11::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpConstMemberCallbackFactory11B4::True>::make(&o, p, ba1, ba2, ba3, ba4); } /** * @short Callback object for functions with 11 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpFunctionCallback11B5 : public XorpCallback11 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4, BA5); XorpFunctionCallback11B5(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback11(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1, _ba2, _ba3, _ba4, _ba5); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; }; /** * @short Callback object for void functions with 11 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpFunctionCallback11B5 : public XorpCallback11 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4, BA5); XorpFunctionCallback11B5(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback11(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1, _ba2, _ba3, _ba4, _ba5); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; }; /** * Factory function that creates a callback object targetted at a * function with 11 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback11::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return typename XorpCallback11::RefPtr(new XorpFunctionCallback11B5(f, ba1, ba2, ba3, ba4, ba5)); } /** * @short Callback object for member methods with 11 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpMemberCallback11B5 : public XorpCallback11 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4, BA5) ; XorpMemberCallback11B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback11(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1, _ba2, _ba3, _ba4, _ba5); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for void member methods with 11 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpMemberCallback11B5 : public XorpCallback11 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4, BA5) ; XorpMemberCallback11B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback11(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1, _ba2, _ba3, _ba4, _ba5); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for safe member methods with 11 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpSafeMemberCallback11B5 : public XorpMemberCallback11B5, public SafeCallbackBase { typedef typename XorpMemberCallback11B5::M M; XorpSafeMemberCallback11B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpMemberCallback11B5(o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpSafeMemberCallback11B5() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { if (valid()) { R r = XorpMemberCallback11B5::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); return r; } } }; /** * @short Callback object for void safe member methods with 11 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpSafeMemberCallback11B5 : public XorpMemberCallback11B5, public SafeCallbackBase { typedef typename XorpMemberCallback11B5::M M; XorpSafeMemberCallback11B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpMemberCallback11B5(o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpSafeMemberCallback11B5() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { if (valid()) { XorpMemberCallback11B5::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); } } }; template struct XorpMemberCallbackFactory11B5 { static XorpMemberCallback11B5* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpSafeMemberCallback11B5(o, p, ba1, ba2, ba3, ba4, ba5); } }; template struct XorpMemberCallbackFactory11B5 { static XorpMemberCallback11B5* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpMemberCallback11B5(o, p, ba1, ba2, ba3, ba4, ba5); }; }; /** * Factory function that creates a callback object targetted at a * member function with 11 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback11::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpMemberCallbackFactory11B5::True>::make(o, p, ba1, ba2, ba3, ba4, ba5); } /** * Factory function that creates a callback object targetted at a * member function with 11 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback11::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpMemberCallbackFactory11B5::True>::make(&o, p, ba1, ba2, ba3, ba4, ba5); } /** * @short Callback object for const member methods with 11 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstMemberCallback11B5 : public XorpCallback11 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4, BA5) const; XorpConstMemberCallback11B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback11(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1, _ba2, _ba3, _ba4, _ba5); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for void const member methods with 11 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstMemberCallback11B5 : public XorpCallback11 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4, BA5) const; XorpConstMemberCallback11B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback11(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1, _ba2, _ba3, _ba4, _ba5); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for const safe member methods with 11 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback11B5 : public XorpConstMemberCallback11B5, public SafeCallbackBase { typedef typename XorpConstMemberCallback11B5::M M; XorpConstSafeMemberCallback11B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpConstMemberCallback11B5(o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback11B5() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { if (valid()) { R r = XorpConstMemberCallback11B5::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); return r; } } }; /** * @short Callback object for void const safe member methods with 11 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback11B5 : public XorpConstMemberCallback11B5, public SafeCallbackBase { typedef typename XorpConstMemberCallback11B5::M M; XorpConstSafeMemberCallback11B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpConstMemberCallback11B5(o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback11B5() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { if (valid()) { XorpConstMemberCallback11B5::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); } } }; template struct XorpConstMemberCallbackFactory11B5 { static XorpConstMemberCallback11B5* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpConstSafeMemberCallback11B5(o, p, ba1, ba2, ba3, ba4, ba5); } }; template struct XorpConstMemberCallbackFactory11B5 { static XorpConstMemberCallback11B5* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpConstMemberCallback11B5(o, p, ba1, ba2, ba3, ba4, ba5); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 11 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback11::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpConstMemberCallbackFactory11B5::True>::make(o, p, ba1, ba2, ba3, ba4, ba5); } /** * Factory function that creates a callback object targetted at a * const member function with 11 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback11::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpConstMemberCallbackFactory11B5::True>::make(&o, p, ba1, ba2, ba3, ba4, ba5); } /** * @short Callback object for functions with 11 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpFunctionCallback11B6 : public XorpCallback11 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4, BA5, BA6); XorpFunctionCallback11B6(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback11(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; BA6 _ba6; }; /** * @short Callback object for void functions with 11 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpFunctionCallback11B6 : public XorpCallback11 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4, BA5, BA6); XorpFunctionCallback11B6(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback11(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; BA6 _ba6; }; /** * Factory function that creates a callback object targetted at a * function with 11 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback11::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return typename XorpCallback11::RefPtr(new XorpFunctionCallback11B6(f, ba1, ba2, ba3, ba4, ba5, ba6)); } /** * @short Callback object for member methods with 11 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpMemberCallback11B6 : public XorpCallback11 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4, BA5, BA6) ; XorpMemberCallback11B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback11(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for void member methods with 11 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpMemberCallback11B6 : public XorpCallback11 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4, BA5, BA6) ; XorpMemberCallback11B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback11(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for safe member methods with 11 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpSafeMemberCallback11B6 : public XorpMemberCallback11B6, public SafeCallbackBase { typedef typename XorpMemberCallback11B6::M M; XorpSafeMemberCallback11B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpMemberCallback11B6(o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpSafeMemberCallback11B6() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { if (valid()) { R r = XorpMemberCallback11B6::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); return r; } } }; /** * @short Callback object for void safe member methods with 11 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpSafeMemberCallback11B6 : public XorpMemberCallback11B6, public SafeCallbackBase { typedef typename XorpMemberCallback11B6::M M; XorpSafeMemberCallback11B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpMemberCallback11B6(o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpSafeMemberCallback11B6() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { if (valid()) { XorpMemberCallback11B6::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); } } }; template struct XorpMemberCallbackFactory11B6 { static XorpMemberCallback11B6* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpSafeMemberCallback11B6(o, p, ba1, ba2, ba3, ba4, ba5, ba6); } }; template struct XorpMemberCallbackFactory11B6 { static XorpMemberCallback11B6* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpMemberCallback11B6(o, p, ba1, ba2, ba3, ba4, ba5, ba6); }; }; /** * Factory function that creates a callback object targetted at a * member function with 11 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback11::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpMemberCallbackFactory11B6::True>::make(o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * Factory function that creates a callback object targetted at a * member function with 11 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback11::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpMemberCallbackFactory11B6::True>::make(&o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * @short Callback object for const member methods with 11 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstMemberCallback11B6 : public XorpCallback11 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4, BA5, BA6) const; XorpConstMemberCallback11B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback11(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for void const member methods with 11 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstMemberCallback11B6 : public XorpCallback11 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4, BA5, BA6) const; XorpConstMemberCallback11B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback11(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for const safe member methods with 11 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback11B6 : public XorpConstMemberCallback11B6, public SafeCallbackBase { typedef typename XorpConstMemberCallback11B6::M M; XorpConstSafeMemberCallback11B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpConstMemberCallback11B6(o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback11B6() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { if (valid()) { R r = XorpConstMemberCallback11B6::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); return r; } } }; /** * @short Callback object for void const safe member methods with 11 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback11B6 : public XorpConstMemberCallback11B6, public SafeCallbackBase { typedef typename XorpConstMemberCallback11B6::M M; XorpConstSafeMemberCallback11B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpConstMemberCallback11B6(o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback11B6() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { if (valid()) { XorpConstMemberCallback11B6::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); } } }; template struct XorpConstMemberCallbackFactory11B6 { static XorpConstMemberCallback11B6* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpConstSafeMemberCallback11B6(o, p, ba1, ba2, ba3, ba4, ba5, ba6); } }; template struct XorpConstMemberCallbackFactory11B6 { static XorpConstMemberCallback11B6* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpConstMemberCallback11B6(o, p, ba1, ba2, ba3, ba4, ba5, ba6); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 11 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback11::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpConstMemberCallbackFactory11B6::True>::make(o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * Factory function that creates a callback object targetted at a * const member function with 11 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback11::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpConstMemberCallbackFactory11B6::True>::make(&o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /////////////////////////////////////////////////////////////////////////////// // // Code relating to callbacks with 12 late args // /** * @short Base class for callbacks with 12 dispatch time args. */ template struct XorpCallback12 { typedef ref_ptr RefPtr; virtual ~XorpCallback12() {} virtual R dispatch(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12) = 0; }; /** * @short Callback object for functions with 12 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpFunctionCallback12B0 : public XorpCallback12 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12); XorpFunctionCallback12B0(F f) : XorpCallback12(), _f(f) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); return r; } protected: F _f; }; /** * @short Callback object for void functions with 12 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpFunctionCallback12B0 : public XorpCallback12 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12); XorpFunctionCallback12B0(F f) : XorpCallback12(), _f(f) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); } protected: F _f; }; /** * Factory function that creates a callback object targetted at a * function with 12 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback12::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12)) { return typename XorpCallback12::RefPtr(new XorpFunctionCallback12B0(f)); } /** * @short Callback object for member methods with 12 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpMemberCallback12B0 : public XorpCallback12 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12) ; XorpMemberCallback12B0(O* o, M m) : XorpCallback12(), _o(o), _m(m) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for void member methods with 12 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpMemberCallback12B0 : public XorpCallback12 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12) ; XorpMemberCallback12B0(O* o, M m) : XorpCallback12(), _o(o), _m(m) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for safe member methods with 12 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpSafeMemberCallback12B0 : public XorpMemberCallback12B0, public SafeCallbackBase { typedef typename XorpMemberCallback12B0::M M; XorpSafeMemberCallback12B0(O* o, M m) : XorpMemberCallback12B0(o, m), SafeCallbackBase(o) {} ~XorpSafeMemberCallback12B0() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { if (valid()) { R r = XorpMemberCallback12B0::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); return r; } } }; /** * @short Callback object for void safe member methods with 12 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpSafeMemberCallback12B0 : public XorpMemberCallback12B0, public SafeCallbackBase { typedef typename XorpMemberCallback12B0::M M; XorpSafeMemberCallback12B0(O* o, M m) : XorpMemberCallback12B0(o, m), SafeCallbackBase(o) {} ~XorpSafeMemberCallback12B0() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { if (valid()) { XorpMemberCallback12B0::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); } } }; template struct XorpMemberCallbackFactory12B0 { static XorpMemberCallback12B0* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12)) { return new XorpSafeMemberCallback12B0(o, p); } }; template struct XorpMemberCallbackFactory12B0 { static XorpMemberCallback12B0* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12)) { return new XorpMemberCallback12B0(o, p); }; }; /** * Factory function that creates a callback object targetted at a * member function with 12 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback12::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12)) { return XorpMemberCallbackFactory12B0::True>::make(o, p); } /** * Factory function that creates a callback object targetted at a * member function with 12 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback12::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12)) { return XorpMemberCallbackFactory12B0::True>::make(&o, p); } /** * @short Callback object for const member methods with 12 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstMemberCallback12B0 : public XorpCallback12 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12) const; XorpConstMemberCallback12B0(O* o, M m) : XorpCallback12(), _o(o), _m(m) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for void const member methods with 12 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstMemberCallback12B0 : public XorpCallback12 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12) const; XorpConstMemberCallback12B0(O* o, M m) : XorpCallback12(), _o(o), _m(m) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for const safe member methods with 12 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback12B0 : public XorpConstMemberCallback12B0, public SafeCallbackBase { typedef typename XorpConstMemberCallback12B0::M M; XorpConstSafeMemberCallback12B0(O* o, M m) : XorpConstMemberCallback12B0(o, m), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback12B0() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { if (valid()) { R r = XorpConstMemberCallback12B0::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); return r; } } }; /** * @short Callback object for void const safe member methods with 12 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback12B0 : public XorpConstMemberCallback12B0, public SafeCallbackBase { typedef typename XorpConstMemberCallback12B0::M M; XorpConstSafeMemberCallback12B0(O* o, M m) : XorpConstMemberCallback12B0(o, m), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback12B0() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { if (valid()) { XorpConstMemberCallback12B0::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); } } }; template struct XorpConstMemberCallbackFactory12B0 { static XorpConstMemberCallback12B0* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12) const) { return new XorpConstSafeMemberCallback12B0(o, p); } }; template struct XorpConstMemberCallbackFactory12B0 { static XorpConstMemberCallback12B0* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12) const) { return new XorpConstMemberCallback12B0(o, p); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 12 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback12::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12) const) { return XorpConstMemberCallbackFactory12B0::True>::make(o, p); } /** * Factory function that creates a callback object targetted at a * const member function with 12 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback12::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12) const) { return XorpConstMemberCallbackFactory12B0::True>::make(&o, p); } /** * @short Callback object for functions with 12 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpFunctionCallback12B1 : public XorpCallback12 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1); XorpFunctionCallback12B1(F f, BA1 ba1) : XorpCallback12(), _f(f), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1); return r; } protected: F _f; BA1 _ba1; }; /** * @short Callback object for void functions with 12 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpFunctionCallback12B1 : public XorpCallback12 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1); XorpFunctionCallback12B1(F f, BA1 ba1) : XorpCallback12(), _f(f), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1); } protected: F _f; BA1 _ba1; }; /** * Factory function that creates a callback object targetted at a * function with 12 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback12::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1), BA1 ba1) { return typename XorpCallback12::RefPtr(new XorpFunctionCallback12B1(f, ba1)); } /** * @short Callback object for member methods with 12 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpMemberCallback12B1 : public XorpCallback12 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1) ; XorpMemberCallback12B1(O* o, M m, BA1 ba1) : XorpCallback12(), _o(o), _m(m), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for void member methods with 12 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpMemberCallback12B1 : public XorpCallback12 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1) ; XorpMemberCallback12B1(O* o, M m, BA1 ba1) : XorpCallback12(), _o(o), _m(m), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for safe member methods with 12 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpSafeMemberCallback12B1 : public XorpMemberCallback12B1, public SafeCallbackBase { typedef typename XorpMemberCallback12B1::M M; XorpSafeMemberCallback12B1(O* o, M m, BA1 ba1) : XorpMemberCallback12B1(o, m, ba1), SafeCallbackBase(o) {} ~XorpSafeMemberCallback12B1() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { if (valid()) { R r = XorpMemberCallback12B1::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); return r; } } }; /** * @short Callback object for void safe member methods with 12 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpSafeMemberCallback12B1 : public XorpMemberCallback12B1, public SafeCallbackBase { typedef typename XorpMemberCallback12B1::M M; XorpSafeMemberCallback12B1(O* o, M m, BA1 ba1) : XorpMemberCallback12B1(o, m, ba1), SafeCallbackBase(o) {} ~XorpSafeMemberCallback12B1() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { if (valid()) { XorpMemberCallback12B1::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); } } }; template struct XorpMemberCallbackFactory12B1 { static XorpMemberCallback12B1* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1), BA1 ba1) { return new XorpSafeMemberCallback12B1(o, p, ba1); } }; template struct XorpMemberCallbackFactory12B1 { static XorpMemberCallback12B1* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1), BA1 ba1) { return new XorpMemberCallback12B1(o, p, ba1); }; }; /** * Factory function that creates a callback object targetted at a * member function with 12 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback12::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1), BA1 ba1) { return XorpMemberCallbackFactory12B1::True>::make(o, p, ba1); } /** * Factory function that creates a callback object targetted at a * member function with 12 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback12::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1), BA1 ba1) { return XorpMemberCallbackFactory12B1::True>::make(&o, p, ba1); } /** * @short Callback object for const member methods with 12 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstMemberCallback12B1 : public XorpCallback12 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1) const; XorpConstMemberCallback12B1(O* o, M m, BA1 ba1) : XorpCallback12(), _o(o), _m(m), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for void const member methods with 12 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstMemberCallback12B1 : public XorpCallback12 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1) const; XorpConstMemberCallback12B1(O* o, M m, BA1 ba1) : XorpCallback12(), _o(o), _m(m), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for const safe member methods with 12 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback12B1 : public XorpConstMemberCallback12B1, public SafeCallbackBase { typedef typename XorpConstMemberCallback12B1::M M; XorpConstSafeMemberCallback12B1(O* o, M m, BA1 ba1) : XorpConstMemberCallback12B1(o, m, ba1), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback12B1() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { if (valid()) { R r = XorpConstMemberCallback12B1::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); return r; } } }; /** * @short Callback object for void const safe member methods with 12 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback12B1 : public XorpConstMemberCallback12B1, public SafeCallbackBase { typedef typename XorpConstMemberCallback12B1::M M; XorpConstSafeMemberCallback12B1(O* o, M m, BA1 ba1) : XorpConstMemberCallback12B1(o, m, ba1), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback12B1() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { if (valid()) { XorpConstMemberCallback12B1::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); } } }; template struct XorpConstMemberCallbackFactory12B1 { static XorpConstMemberCallback12B1* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1) const, BA1 ba1) { return new XorpConstSafeMemberCallback12B1(o, p, ba1); } }; template struct XorpConstMemberCallbackFactory12B1 { static XorpConstMemberCallback12B1* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1) const, BA1 ba1) { return new XorpConstMemberCallback12B1(o, p, ba1); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 12 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback12::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1) const, BA1 ba1) { return XorpConstMemberCallbackFactory12B1::True>::make(o, p, ba1); } /** * Factory function that creates a callback object targetted at a * const member function with 12 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback12::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1) const, BA1 ba1) { return XorpConstMemberCallbackFactory12B1::True>::make(&o, p, ba1); } /** * @short Callback object for functions with 12 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpFunctionCallback12B2 : public XorpCallback12 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2); XorpFunctionCallback12B2(F f, BA1 ba1, BA2 ba2) : XorpCallback12(), _f(f), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1, _ba2); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; }; /** * @short Callback object for void functions with 12 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpFunctionCallback12B2 : public XorpCallback12 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2); XorpFunctionCallback12B2(F f, BA1 ba1, BA2 ba2) : XorpCallback12(), _f(f), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1, _ba2); } protected: F _f; BA1 _ba1; BA2 _ba2; }; /** * Factory function that creates a callback object targetted at a * function with 12 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback12::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2), BA1 ba1, BA2 ba2) { return typename XorpCallback12::RefPtr(new XorpFunctionCallback12B2(f, ba1, ba2)); } /** * @short Callback object for member methods with 12 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpMemberCallback12B2 : public XorpCallback12 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2) ; XorpMemberCallback12B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback12(), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1, _ba2); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for void member methods with 12 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpMemberCallback12B2 : public XorpCallback12 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2) ; XorpMemberCallback12B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback12(), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1, _ba2); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for safe member methods with 12 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpSafeMemberCallback12B2 : public XorpMemberCallback12B2, public SafeCallbackBase { typedef typename XorpMemberCallback12B2::M M; XorpSafeMemberCallback12B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpMemberCallback12B2(o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpSafeMemberCallback12B2() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { if (valid()) { R r = XorpMemberCallback12B2::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); return r; } } }; /** * @short Callback object for void safe member methods with 12 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpSafeMemberCallback12B2 : public XorpMemberCallback12B2, public SafeCallbackBase { typedef typename XorpMemberCallback12B2::M M; XorpSafeMemberCallback12B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpMemberCallback12B2(o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpSafeMemberCallback12B2() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { if (valid()) { XorpMemberCallback12B2::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); } } }; template struct XorpMemberCallbackFactory12B2 { static XorpMemberCallback12B2* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2), BA1 ba1, BA2 ba2) { return new XorpSafeMemberCallback12B2(o, p, ba1, ba2); } }; template struct XorpMemberCallbackFactory12B2 { static XorpMemberCallback12B2* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2), BA1 ba1, BA2 ba2) { return new XorpMemberCallback12B2(o, p, ba1, ba2); }; }; /** * Factory function that creates a callback object targetted at a * member function with 12 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback12::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2), BA1 ba1, BA2 ba2) { return XorpMemberCallbackFactory12B2::True>::make(o, p, ba1, ba2); } /** * Factory function that creates a callback object targetted at a * member function with 12 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback12::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2), BA1 ba1, BA2 ba2) { return XorpMemberCallbackFactory12B2::True>::make(&o, p, ba1, ba2); } /** * @short Callback object for const member methods with 12 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstMemberCallback12B2 : public XorpCallback12 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2) const; XorpConstMemberCallback12B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback12(), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1, _ba2); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for void const member methods with 12 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstMemberCallback12B2 : public XorpCallback12 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2) const; XorpConstMemberCallback12B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback12(), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1, _ba2); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for const safe member methods with 12 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback12B2 : public XorpConstMemberCallback12B2, public SafeCallbackBase { typedef typename XorpConstMemberCallback12B2::M M; XorpConstSafeMemberCallback12B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpConstMemberCallback12B2(o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback12B2() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { if (valid()) { R r = XorpConstMemberCallback12B2::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); return r; } } }; /** * @short Callback object for void const safe member methods with 12 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback12B2 : public XorpConstMemberCallback12B2, public SafeCallbackBase { typedef typename XorpConstMemberCallback12B2::M M; XorpConstSafeMemberCallback12B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpConstMemberCallback12B2(o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback12B2() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { if (valid()) { XorpConstMemberCallback12B2::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); } } }; template struct XorpConstMemberCallbackFactory12B2 { static XorpConstMemberCallback12B2* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2) const, BA1 ba1, BA2 ba2) { return new XorpConstSafeMemberCallback12B2(o, p, ba1, ba2); } }; template struct XorpConstMemberCallbackFactory12B2 { static XorpConstMemberCallback12B2* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2) const, BA1 ba1, BA2 ba2) { return new XorpConstMemberCallback12B2(o, p, ba1, ba2); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 12 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback12::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2) const, BA1 ba1, BA2 ba2) { return XorpConstMemberCallbackFactory12B2::True>::make(o, p, ba1, ba2); } /** * Factory function that creates a callback object targetted at a * const member function with 12 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback12::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2) const, BA1 ba1, BA2 ba2) { return XorpConstMemberCallbackFactory12B2::True>::make(&o, p, ba1, ba2); } /** * @short Callback object for functions with 12 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpFunctionCallback12B3 : public XorpCallback12 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3); XorpFunctionCallback12B3(F f, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback12(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1, _ba2, _ba3); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; }; /** * @short Callback object for void functions with 12 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpFunctionCallback12B3 : public XorpCallback12 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3); XorpFunctionCallback12B3(F f, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback12(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1, _ba2, _ba3); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; }; /** * Factory function that creates a callback object targetted at a * function with 12 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback12::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return typename XorpCallback12::RefPtr(new XorpFunctionCallback12B3(f, ba1, ba2, ba3)); } /** * @short Callback object for member methods with 12 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpMemberCallback12B3 : public XorpCallback12 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3) ; XorpMemberCallback12B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback12(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1, _ba2, _ba3); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for void member methods with 12 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpMemberCallback12B3 : public XorpCallback12 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3) ; XorpMemberCallback12B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback12(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1, _ba2, _ba3); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for safe member methods with 12 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpSafeMemberCallback12B3 : public XorpMemberCallback12B3, public SafeCallbackBase { typedef typename XorpMemberCallback12B3::M M; XorpSafeMemberCallback12B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpMemberCallback12B3(o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpSafeMemberCallback12B3() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { if (valid()) { R r = XorpMemberCallback12B3::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); return r; } } }; /** * @short Callback object for void safe member methods with 12 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpSafeMemberCallback12B3 : public XorpMemberCallback12B3, public SafeCallbackBase { typedef typename XorpMemberCallback12B3::M M; XorpSafeMemberCallback12B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpMemberCallback12B3(o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpSafeMemberCallback12B3() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { if (valid()) { XorpMemberCallback12B3::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); } } }; template struct XorpMemberCallbackFactory12B3 { static XorpMemberCallback12B3* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpSafeMemberCallback12B3(o, p, ba1, ba2, ba3); } }; template struct XorpMemberCallbackFactory12B3 { static XorpMemberCallback12B3* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpMemberCallback12B3(o, p, ba1, ba2, ba3); }; }; /** * Factory function that creates a callback object targetted at a * member function with 12 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback12::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return XorpMemberCallbackFactory12B3::True>::make(o, p, ba1, ba2, ba3); } /** * Factory function that creates a callback object targetted at a * member function with 12 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback12::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return XorpMemberCallbackFactory12B3::True>::make(&o, p, ba1, ba2, ba3); } /** * @short Callback object for const member methods with 12 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstMemberCallback12B3 : public XorpCallback12 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3) const; XorpConstMemberCallback12B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback12(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1, _ba2, _ba3); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for void const member methods with 12 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstMemberCallback12B3 : public XorpCallback12 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3) const; XorpConstMemberCallback12B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback12(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1, _ba2, _ba3); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for const safe member methods with 12 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback12B3 : public XorpConstMemberCallback12B3, public SafeCallbackBase { typedef typename XorpConstMemberCallback12B3::M M; XorpConstSafeMemberCallback12B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpConstMemberCallback12B3(o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback12B3() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { if (valid()) { R r = XorpConstMemberCallback12B3::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); return r; } } }; /** * @short Callback object for void const safe member methods with 12 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback12B3 : public XorpConstMemberCallback12B3, public SafeCallbackBase { typedef typename XorpConstMemberCallback12B3::M M; XorpConstSafeMemberCallback12B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpConstMemberCallback12B3(o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback12B3() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { if (valid()) { XorpConstMemberCallback12B3::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); } } }; template struct XorpConstMemberCallbackFactory12B3 { static XorpConstMemberCallback12B3* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpConstSafeMemberCallback12B3(o, p, ba1, ba2, ba3); } }; template struct XorpConstMemberCallbackFactory12B3 { static XorpConstMemberCallback12B3* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpConstMemberCallback12B3(o, p, ba1, ba2, ba3); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 12 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback12::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return XorpConstMemberCallbackFactory12B3::True>::make(o, p, ba1, ba2, ba3); } /** * Factory function that creates a callback object targetted at a * const member function with 12 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback12::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return XorpConstMemberCallbackFactory12B3::True>::make(&o, p, ba1, ba2, ba3); } /** * @short Callback object for functions with 12 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpFunctionCallback12B4 : public XorpCallback12 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4); XorpFunctionCallback12B4(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback12(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1, _ba2, _ba3, _ba4); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; }; /** * @short Callback object for void functions with 12 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpFunctionCallback12B4 : public XorpCallback12 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4); XorpFunctionCallback12B4(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback12(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1, _ba2, _ba3, _ba4); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; }; /** * Factory function that creates a callback object targetted at a * function with 12 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback12::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return typename XorpCallback12::RefPtr(new XorpFunctionCallback12B4(f, ba1, ba2, ba3, ba4)); } /** * @short Callback object for member methods with 12 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpMemberCallback12B4 : public XorpCallback12 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4) ; XorpMemberCallback12B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback12(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1, _ba2, _ba3, _ba4); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for void member methods with 12 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpMemberCallback12B4 : public XorpCallback12 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4) ; XorpMemberCallback12B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback12(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1, _ba2, _ba3, _ba4); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for safe member methods with 12 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpSafeMemberCallback12B4 : public XorpMemberCallback12B4, public SafeCallbackBase { typedef typename XorpMemberCallback12B4::M M; XorpSafeMemberCallback12B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpMemberCallback12B4(o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpSafeMemberCallback12B4() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { if (valid()) { R r = XorpMemberCallback12B4::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); return r; } } }; /** * @short Callback object for void safe member methods with 12 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpSafeMemberCallback12B4 : public XorpMemberCallback12B4, public SafeCallbackBase { typedef typename XorpMemberCallback12B4::M M; XorpSafeMemberCallback12B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpMemberCallback12B4(o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpSafeMemberCallback12B4() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { if (valid()) { XorpMemberCallback12B4::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); } } }; template struct XorpMemberCallbackFactory12B4 { static XorpMemberCallback12B4* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpSafeMemberCallback12B4(o, p, ba1, ba2, ba3, ba4); } }; template struct XorpMemberCallbackFactory12B4 { static XorpMemberCallback12B4* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpMemberCallback12B4(o, p, ba1, ba2, ba3, ba4); }; }; /** * Factory function that creates a callback object targetted at a * member function with 12 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback12::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpMemberCallbackFactory12B4::True>::make(o, p, ba1, ba2, ba3, ba4); } /** * Factory function that creates a callback object targetted at a * member function with 12 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback12::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpMemberCallbackFactory12B4::True>::make(&o, p, ba1, ba2, ba3, ba4); } /** * @short Callback object for const member methods with 12 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstMemberCallback12B4 : public XorpCallback12 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4) const; XorpConstMemberCallback12B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback12(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1, _ba2, _ba3, _ba4); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for void const member methods with 12 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstMemberCallback12B4 : public XorpCallback12 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4) const; XorpConstMemberCallback12B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback12(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1, _ba2, _ba3, _ba4); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for const safe member methods with 12 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback12B4 : public XorpConstMemberCallback12B4, public SafeCallbackBase { typedef typename XorpConstMemberCallback12B4::M M; XorpConstSafeMemberCallback12B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpConstMemberCallback12B4(o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback12B4() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { if (valid()) { R r = XorpConstMemberCallback12B4::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); return r; } } }; /** * @short Callback object for void const safe member methods with 12 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback12B4 : public XorpConstMemberCallback12B4, public SafeCallbackBase { typedef typename XorpConstMemberCallback12B4::M M; XorpConstSafeMemberCallback12B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpConstMemberCallback12B4(o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback12B4() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { if (valid()) { XorpConstMemberCallback12B4::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); } } }; template struct XorpConstMemberCallbackFactory12B4 { static XorpConstMemberCallback12B4* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpConstSafeMemberCallback12B4(o, p, ba1, ba2, ba3, ba4); } }; template struct XorpConstMemberCallbackFactory12B4 { static XorpConstMemberCallback12B4* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpConstMemberCallback12B4(o, p, ba1, ba2, ba3, ba4); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 12 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback12::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpConstMemberCallbackFactory12B4::True>::make(o, p, ba1, ba2, ba3, ba4); } /** * Factory function that creates a callback object targetted at a * const member function with 12 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback12::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpConstMemberCallbackFactory12B4::True>::make(&o, p, ba1, ba2, ba3, ba4); } /** * @short Callback object for functions with 12 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpFunctionCallback12B5 : public XorpCallback12 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4, BA5); XorpFunctionCallback12B5(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback12(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1, _ba2, _ba3, _ba4, _ba5); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; }; /** * @short Callback object for void functions with 12 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpFunctionCallback12B5 : public XorpCallback12 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4, BA5); XorpFunctionCallback12B5(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback12(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1, _ba2, _ba3, _ba4, _ba5); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; }; /** * Factory function that creates a callback object targetted at a * function with 12 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback12::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return typename XorpCallback12::RefPtr(new XorpFunctionCallback12B5(f, ba1, ba2, ba3, ba4, ba5)); } /** * @short Callback object for member methods with 12 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpMemberCallback12B5 : public XorpCallback12 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4, BA5) ; XorpMemberCallback12B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback12(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1, _ba2, _ba3, _ba4, _ba5); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for void member methods with 12 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpMemberCallback12B5 : public XorpCallback12 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4, BA5) ; XorpMemberCallback12B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback12(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1, _ba2, _ba3, _ba4, _ba5); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for safe member methods with 12 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpSafeMemberCallback12B5 : public XorpMemberCallback12B5, public SafeCallbackBase { typedef typename XorpMemberCallback12B5::M M; XorpSafeMemberCallback12B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpMemberCallback12B5(o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpSafeMemberCallback12B5() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { if (valid()) { R r = XorpMemberCallback12B5::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); return r; } } }; /** * @short Callback object for void safe member methods with 12 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpSafeMemberCallback12B5 : public XorpMemberCallback12B5, public SafeCallbackBase { typedef typename XorpMemberCallback12B5::M M; XorpSafeMemberCallback12B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpMemberCallback12B5(o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpSafeMemberCallback12B5() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { if (valid()) { XorpMemberCallback12B5::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); } } }; template struct XorpMemberCallbackFactory12B5 { static XorpMemberCallback12B5* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpSafeMemberCallback12B5(o, p, ba1, ba2, ba3, ba4, ba5); } }; template struct XorpMemberCallbackFactory12B5 { static XorpMemberCallback12B5* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpMemberCallback12B5(o, p, ba1, ba2, ba3, ba4, ba5); }; }; /** * Factory function that creates a callback object targetted at a * member function with 12 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback12::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpMemberCallbackFactory12B5::True>::make(o, p, ba1, ba2, ba3, ba4, ba5); } /** * Factory function that creates a callback object targetted at a * member function with 12 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback12::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpMemberCallbackFactory12B5::True>::make(&o, p, ba1, ba2, ba3, ba4, ba5); } /** * @short Callback object for const member methods with 12 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstMemberCallback12B5 : public XorpCallback12 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4, BA5) const; XorpConstMemberCallback12B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback12(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1, _ba2, _ba3, _ba4, _ba5); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for void const member methods with 12 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstMemberCallback12B5 : public XorpCallback12 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4, BA5) const; XorpConstMemberCallback12B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback12(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1, _ba2, _ba3, _ba4, _ba5); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for const safe member methods with 12 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback12B5 : public XorpConstMemberCallback12B5, public SafeCallbackBase { typedef typename XorpConstMemberCallback12B5::M M; XorpConstSafeMemberCallback12B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpConstMemberCallback12B5(o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback12B5() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { if (valid()) { R r = XorpConstMemberCallback12B5::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); return r; } } }; /** * @short Callback object for void const safe member methods with 12 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback12B5 : public XorpConstMemberCallback12B5, public SafeCallbackBase { typedef typename XorpConstMemberCallback12B5::M M; XorpConstSafeMemberCallback12B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpConstMemberCallback12B5(o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback12B5() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { if (valid()) { XorpConstMemberCallback12B5::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); } } }; template struct XorpConstMemberCallbackFactory12B5 { static XorpConstMemberCallback12B5* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpConstSafeMemberCallback12B5(o, p, ba1, ba2, ba3, ba4, ba5); } }; template struct XorpConstMemberCallbackFactory12B5 { static XorpConstMemberCallback12B5* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpConstMemberCallback12B5(o, p, ba1, ba2, ba3, ba4, ba5); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 12 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback12::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpConstMemberCallbackFactory12B5::True>::make(o, p, ba1, ba2, ba3, ba4, ba5); } /** * Factory function that creates a callback object targetted at a * const member function with 12 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback12::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpConstMemberCallbackFactory12B5::True>::make(&o, p, ba1, ba2, ba3, ba4, ba5); } /** * @short Callback object for functions with 12 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpFunctionCallback12B6 : public XorpCallback12 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4, BA5, BA6); XorpFunctionCallback12B6(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback12(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; BA6 _ba6; }; /** * @short Callback object for void functions with 12 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpFunctionCallback12B6 : public XorpCallback12 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4, BA5, BA6); XorpFunctionCallback12B6(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback12(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; BA6 _ba6; }; /** * Factory function that creates a callback object targetted at a * function with 12 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback12::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return typename XorpCallback12::RefPtr(new XorpFunctionCallback12B6(f, ba1, ba2, ba3, ba4, ba5, ba6)); } /** * @short Callback object for member methods with 12 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpMemberCallback12B6 : public XorpCallback12 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4, BA5, BA6) ; XorpMemberCallback12B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback12(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for void member methods with 12 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpMemberCallback12B6 : public XorpCallback12 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4, BA5, BA6) ; XorpMemberCallback12B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback12(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for safe member methods with 12 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpSafeMemberCallback12B6 : public XorpMemberCallback12B6, public SafeCallbackBase { typedef typename XorpMemberCallback12B6::M M; XorpSafeMemberCallback12B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpMemberCallback12B6(o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpSafeMemberCallback12B6() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { if (valid()) { R r = XorpMemberCallback12B6::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); return r; } } }; /** * @short Callback object for void safe member methods with 12 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpSafeMemberCallback12B6 : public XorpMemberCallback12B6, public SafeCallbackBase { typedef typename XorpMemberCallback12B6::M M; XorpSafeMemberCallback12B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpMemberCallback12B6(o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpSafeMemberCallback12B6() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { if (valid()) { XorpMemberCallback12B6::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); } } }; template struct XorpMemberCallbackFactory12B6 { static XorpMemberCallback12B6* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpSafeMemberCallback12B6(o, p, ba1, ba2, ba3, ba4, ba5, ba6); } }; template struct XorpMemberCallbackFactory12B6 { static XorpMemberCallback12B6* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpMemberCallback12B6(o, p, ba1, ba2, ba3, ba4, ba5, ba6); }; }; /** * Factory function that creates a callback object targetted at a * member function with 12 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback12::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpMemberCallbackFactory12B6::True>::make(o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * Factory function that creates a callback object targetted at a * member function with 12 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback12::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpMemberCallbackFactory12B6::True>::make(&o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * @short Callback object for const member methods with 12 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstMemberCallback12B6 : public XorpCallback12 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4, BA5, BA6) const; XorpConstMemberCallback12B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback12(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for void const member methods with 12 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstMemberCallback12B6 : public XorpCallback12 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4, BA5, BA6) const; XorpConstMemberCallback12B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback12(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for const safe member methods with 12 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback12B6 : public XorpConstMemberCallback12B6, public SafeCallbackBase { typedef typename XorpConstMemberCallback12B6::M M; XorpConstSafeMemberCallback12B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpConstMemberCallback12B6(o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback12B6() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { if (valid()) { R r = XorpConstMemberCallback12B6::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); return r; } } }; /** * @short Callback object for void const safe member methods with 12 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback12B6 : public XorpConstMemberCallback12B6, public SafeCallbackBase { typedef typename XorpConstMemberCallback12B6::M M; XorpConstSafeMemberCallback12B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpConstMemberCallback12B6(o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback12B6() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { if (valid()) { XorpConstMemberCallback12B6::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); } } }; template struct XorpConstMemberCallbackFactory12B6 { static XorpConstMemberCallback12B6* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpConstSafeMemberCallback12B6(o, p, ba1, ba2, ba3, ba4, ba5, ba6); } }; template struct XorpConstMemberCallbackFactory12B6 { static XorpConstMemberCallback12B6* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpConstMemberCallback12B6(o, p, ba1, ba2, ba3, ba4, ba5, ba6); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 12 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback12::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpConstMemberCallbackFactory12B6::True>::make(o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * Factory function that creates a callback object targetted at a * const member function with 12 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback12::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpConstMemberCallbackFactory12B6::True>::make(&o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /////////////////////////////////////////////////////////////////////////////// // // Code relating to callbacks with 13 late args // /** * @short Base class for callbacks with 13 dispatch time args. */ template struct XorpCallback13 { typedef ref_ptr RefPtr; virtual ~XorpCallback13() {} virtual R dispatch(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13) = 0; }; /** * @short Callback object for functions with 13 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpFunctionCallback13B0 : public XorpCallback13 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13); XorpFunctionCallback13B0(F f) : XorpCallback13(), _f(f) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); return r; } protected: F _f; }; /** * @short Callback object for void functions with 13 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpFunctionCallback13B0 : public XorpCallback13 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13); XorpFunctionCallback13B0(F f) : XorpCallback13(), _f(f) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); } protected: F _f; }; /** * Factory function that creates a callback object targetted at a * function with 13 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback13::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13)) { return typename XorpCallback13::RefPtr(new XorpFunctionCallback13B0(f)); } /** * @short Callback object for member methods with 13 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpMemberCallback13B0 : public XorpCallback13 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13) ; XorpMemberCallback13B0(O* o, M m) : XorpCallback13(), _o(o), _m(m) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for void member methods with 13 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpMemberCallback13B0 : public XorpCallback13 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13) ; XorpMemberCallback13B0(O* o, M m) : XorpCallback13(), _o(o), _m(m) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for safe member methods with 13 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpSafeMemberCallback13B0 : public XorpMemberCallback13B0, public SafeCallbackBase { typedef typename XorpMemberCallback13B0::M M; XorpSafeMemberCallback13B0(O* o, M m) : XorpMemberCallback13B0(o, m), SafeCallbackBase(o) {} ~XorpSafeMemberCallback13B0() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { if (valid()) { R r = XorpMemberCallback13B0::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); return r; } } }; /** * @short Callback object for void safe member methods with 13 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpSafeMemberCallback13B0 : public XorpMemberCallback13B0, public SafeCallbackBase { typedef typename XorpMemberCallback13B0::M M; XorpSafeMemberCallback13B0(O* o, M m) : XorpMemberCallback13B0(o, m), SafeCallbackBase(o) {} ~XorpSafeMemberCallback13B0() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { if (valid()) { XorpMemberCallback13B0::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); } } }; template struct XorpMemberCallbackFactory13B0 { static XorpMemberCallback13B0* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13)) { return new XorpSafeMemberCallback13B0(o, p); } }; template struct XorpMemberCallbackFactory13B0 { static XorpMemberCallback13B0* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13)) { return new XorpMemberCallback13B0(o, p); }; }; /** * Factory function that creates a callback object targetted at a * member function with 13 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback13::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13)) { return XorpMemberCallbackFactory13B0::True>::make(o, p); } /** * Factory function that creates a callback object targetted at a * member function with 13 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback13::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13)) { return XorpMemberCallbackFactory13B0::True>::make(&o, p); } /** * @short Callback object for const member methods with 13 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstMemberCallback13B0 : public XorpCallback13 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13) const; XorpConstMemberCallback13B0(O* o, M m) : XorpCallback13(), _o(o), _m(m) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for void const member methods with 13 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstMemberCallback13B0 : public XorpCallback13 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13) const; XorpConstMemberCallback13B0(O* o, M m) : XorpCallback13(), _o(o), _m(m) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for const safe member methods with 13 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback13B0 : public XorpConstMemberCallback13B0, public SafeCallbackBase { typedef typename XorpConstMemberCallback13B0::M M; XorpConstSafeMemberCallback13B0(O* o, M m) : XorpConstMemberCallback13B0(o, m), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback13B0() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { if (valid()) { R r = XorpConstMemberCallback13B0::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); return r; } } }; /** * @short Callback object for void const safe member methods with 13 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback13B0 : public XorpConstMemberCallback13B0, public SafeCallbackBase { typedef typename XorpConstMemberCallback13B0::M M; XorpConstSafeMemberCallback13B0(O* o, M m) : XorpConstMemberCallback13B0(o, m), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback13B0() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { if (valid()) { XorpConstMemberCallback13B0::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); } } }; template struct XorpConstMemberCallbackFactory13B0 { static XorpConstMemberCallback13B0* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13) const) { return new XorpConstSafeMemberCallback13B0(o, p); } }; template struct XorpConstMemberCallbackFactory13B0 { static XorpConstMemberCallback13B0* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13) const) { return new XorpConstMemberCallback13B0(o, p); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 13 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback13::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13) const) { return XorpConstMemberCallbackFactory13B0::True>::make(o, p); } /** * Factory function that creates a callback object targetted at a * const member function with 13 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback13::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13) const) { return XorpConstMemberCallbackFactory13B0::True>::make(&o, p); } /** * @short Callback object for functions with 13 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpFunctionCallback13B1 : public XorpCallback13 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1); XorpFunctionCallback13B1(F f, BA1 ba1) : XorpCallback13(), _f(f), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1); return r; } protected: F _f; BA1 _ba1; }; /** * @short Callback object for void functions with 13 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpFunctionCallback13B1 : public XorpCallback13 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1); XorpFunctionCallback13B1(F f, BA1 ba1) : XorpCallback13(), _f(f), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1); } protected: F _f; BA1 _ba1; }; /** * Factory function that creates a callback object targetted at a * function with 13 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback13::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1), BA1 ba1) { return typename XorpCallback13::RefPtr(new XorpFunctionCallback13B1(f, ba1)); } /** * @short Callback object for member methods with 13 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpMemberCallback13B1 : public XorpCallback13 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1) ; XorpMemberCallback13B1(O* o, M m, BA1 ba1) : XorpCallback13(), _o(o), _m(m), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for void member methods with 13 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpMemberCallback13B1 : public XorpCallback13 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1) ; XorpMemberCallback13B1(O* o, M m, BA1 ba1) : XorpCallback13(), _o(o), _m(m), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for safe member methods with 13 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpSafeMemberCallback13B1 : public XorpMemberCallback13B1, public SafeCallbackBase { typedef typename XorpMemberCallback13B1::M M; XorpSafeMemberCallback13B1(O* o, M m, BA1 ba1) : XorpMemberCallback13B1(o, m, ba1), SafeCallbackBase(o) {} ~XorpSafeMemberCallback13B1() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { if (valid()) { R r = XorpMemberCallback13B1::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); return r; } } }; /** * @short Callback object for void safe member methods with 13 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpSafeMemberCallback13B1 : public XorpMemberCallback13B1, public SafeCallbackBase { typedef typename XorpMemberCallback13B1::M M; XorpSafeMemberCallback13B1(O* o, M m, BA1 ba1) : XorpMemberCallback13B1(o, m, ba1), SafeCallbackBase(o) {} ~XorpSafeMemberCallback13B1() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { if (valid()) { XorpMemberCallback13B1::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); } } }; template struct XorpMemberCallbackFactory13B1 { static XorpMemberCallback13B1* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1), BA1 ba1) { return new XorpSafeMemberCallback13B1(o, p, ba1); } }; template struct XorpMemberCallbackFactory13B1 { static XorpMemberCallback13B1* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1), BA1 ba1) { return new XorpMemberCallback13B1(o, p, ba1); }; }; /** * Factory function that creates a callback object targetted at a * member function with 13 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback13::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1), BA1 ba1) { return XorpMemberCallbackFactory13B1::True>::make(o, p, ba1); } /** * Factory function that creates a callback object targetted at a * member function with 13 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback13::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1), BA1 ba1) { return XorpMemberCallbackFactory13B1::True>::make(&o, p, ba1); } /** * @short Callback object for const member methods with 13 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstMemberCallback13B1 : public XorpCallback13 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1) const; XorpConstMemberCallback13B1(O* o, M m, BA1 ba1) : XorpCallback13(), _o(o), _m(m), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for void const member methods with 13 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstMemberCallback13B1 : public XorpCallback13 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1) const; XorpConstMemberCallback13B1(O* o, M m, BA1 ba1) : XorpCallback13(), _o(o), _m(m), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for const safe member methods with 13 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback13B1 : public XorpConstMemberCallback13B1, public SafeCallbackBase { typedef typename XorpConstMemberCallback13B1::M M; XorpConstSafeMemberCallback13B1(O* o, M m, BA1 ba1) : XorpConstMemberCallback13B1(o, m, ba1), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback13B1() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { if (valid()) { R r = XorpConstMemberCallback13B1::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); return r; } } }; /** * @short Callback object for void const safe member methods with 13 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback13B1 : public XorpConstMemberCallback13B1, public SafeCallbackBase { typedef typename XorpConstMemberCallback13B1::M M; XorpConstSafeMemberCallback13B1(O* o, M m, BA1 ba1) : XorpConstMemberCallback13B1(o, m, ba1), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback13B1() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { if (valid()) { XorpConstMemberCallback13B1::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); } } }; template struct XorpConstMemberCallbackFactory13B1 { static XorpConstMemberCallback13B1* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1) const, BA1 ba1) { return new XorpConstSafeMemberCallback13B1(o, p, ba1); } }; template struct XorpConstMemberCallbackFactory13B1 { static XorpConstMemberCallback13B1* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1) const, BA1 ba1) { return new XorpConstMemberCallback13B1(o, p, ba1); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 13 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback13::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1) const, BA1 ba1) { return XorpConstMemberCallbackFactory13B1::True>::make(o, p, ba1); } /** * Factory function that creates a callback object targetted at a * const member function with 13 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback13::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1) const, BA1 ba1) { return XorpConstMemberCallbackFactory13B1::True>::make(&o, p, ba1); } /** * @short Callback object for functions with 13 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpFunctionCallback13B2 : public XorpCallback13 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2); XorpFunctionCallback13B2(F f, BA1 ba1, BA2 ba2) : XorpCallback13(), _f(f), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1, _ba2); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; }; /** * @short Callback object for void functions with 13 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpFunctionCallback13B2 : public XorpCallback13 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2); XorpFunctionCallback13B2(F f, BA1 ba1, BA2 ba2) : XorpCallback13(), _f(f), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1, _ba2); } protected: F _f; BA1 _ba1; BA2 _ba2; }; /** * Factory function that creates a callback object targetted at a * function with 13 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback13::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2), BA1 ba1, BA2 ba2) { return typename XorpCallback13::RefPtr(new XorpFunctionCallback13B2(f, ba1, ba2)); } /** * @short Callback object for member methods with 13 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpMemberCallback13B2 : public XorpCallback13 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2) ; XorpMemberCallback13B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback13(), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1, _ba2); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for void member methods with 13 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpMemberCallback13B2 : public XorpCallback13 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2) ; XorpMemberCallback13B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback13(), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1, _ba2); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for safe member methods with 13 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpSafeMemberCallback13B2 : public XorpMemberCallback13B2, public SafeCallbackBase { typedef typename XorpMemberCallback13B2::M M; XorpSafeMemberCallback13B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpMemberCallback13B2(o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpSafeMemberCallback13B2() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { if (valid()) { R r = XorpMemberCallback13B2::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); return r; } } }; /** * @short Callback object for void safe member methods with 13 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpSafeMemberCallback13B2 : public XorpMemberCallback13B2, public SafeCallbackBase { typedef typename XorpMemberCallback13B2::M M; XorpSafeMemberCallback13B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpMemberCallback13B2(o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpSafeMemberCallback13B2() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { if (valid()) { XorpMemberCallback13B2::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); } } }; template struct XorpMemberCallbackFactory13B2 { static XorpMemberCallback13B2* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2), BA1 ba1, BA2 ba2) { return new XorpSafeMemberCallback13B2(o, p, ba1, ba2); } }; template struct XorpMemberCallbackFactory13B2 { static XorpMemberCallback13B2* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2), BA1 ba1, BA2 ba2) { return new XorpMemberCallback13B2(o, p, ba1, ba2); }; }; /** * Factory function that creates a callback object targetted at a * member function with 13 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback13::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2), BA1 ba1, BA2 ba2) { return XorpMemberCallbackFactory13B2::True>::make(o, p, ba1, ba2); } /** * Factory function that creates a callback object targetted at a * member function with 13 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback13::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2), BA1 ba1, BA2 ba2) { return XorpMemberCallbackFactory13B2::True>::make(&o, p, ba1, ba2); } /** * @short Callback object for const member methods with 13 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstMemberCallback13B2 : public XorpCallback13 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2) const; XorpConstMemberCallback13B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback13(), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1, _ba2); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for void const member methods with 13 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstMemberCallback13B2 : public XorpCallback13 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2) const; XorpConstMemberCallback13B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback13(), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1, _ba2); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for const safe member methods with 13 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback13B2 : public XorpConstMemberCallback13B2, public SafeCallbackBase { typedef typename XorpConstMemberCallback13B2::M M; XorpConstSafeMemberCallback13B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpConstMemberCallback13B2(o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback13B2() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { if (valid()) { R r = XorpConstMemberCallback13B2::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); return r; } } }; /** * @short Callback object for void const safe member methods with 13 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback13B2 : public XorpConstMemberCallback13B2, public SafeCallbackBase { typedef typename XorpConstMemberCallback13B2::M M; XorpConstSafeMemberCallback13B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpConstMemberCallback13B2(o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback13B2() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { if (valid()) { XorpConstMemberCallback13B2::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); } } }; template struct XorpConstMemberCallbackFactory13B2 { static XorpConstMemberCallback13B2* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2) const, BA1 ba1, BA2 ba2) { return new XorpConstSafeMemberCallback13B2(o, p, ba1, ba2); } }; template struct XorpConstMemberCallbackFactory13B2 { static XorpConstMemberCallback13B2* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2) const, BA1 ba1, BA2 ba2) { return new XorpConstMemberCallback13B2(o, p, ba1, ba2); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 13 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback13::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2) const, BA1 ba1, BA2 ba2) { return XorpConstMemberCallbackFactory13B2::True>::make(o, p, ba1, ba2); } /** * Factory function that creates a callback object targetted at a * const member function with 13 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback13::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2) const, BA1 ba1, BA2 ba2) { return XorpConstMemberCallbackFactory13B2::True>::make(&o, p, ba1, ba2); } /** * @short Callback object for functions with 13 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpFunctionCallback13B3 : public XorpCallback13 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3); XorpFunctionCallback13B3(F f, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback13(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1, _ba2, _ba3); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; }; /** * @short Callback object for void functions with 13 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpFunctionCallback13B3 : public XorpCallback13 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3); XorpFunctionCallback13B3(F f, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback13(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1, _ba2, _ba3); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; }; /** * Factory function that creates a callback object targetted at a * function with 13 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback13::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return typename XorpCallback13::RefPtr(new XorpFunctionCallback13B3(f, ba1, ba2, ba3)); } /** * @short Callback object for member methods with 13 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpMemberCallback13B3 : public XorpCallback13 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3) ; XorpMemberCallback13B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback13(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1, _ba2, _ba3); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for void member methods with 13 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpMemberCallback13B3 : public XorpCallback13 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3) ; XorpMemberCallback13B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback13(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1, _ba2, _ba3); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for safe member methods with 13 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpSafeMemberCallback13B3 : public XorpMemberCallback13B3, public SafeCallbackBase { typedef typename XorpMemberCallback13B3::M M; XorpSafeMemberCallback13B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpMemberCallback13B3(o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpSafeMemberCallback13B3() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { if (valid()) { R r = XorpMemberCallback13B3::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); return r; } } }; /** * @short Callback object for void safe member methods with 13 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpSafeMemberCallback13B3 : public XorpMemberCallback13B3, public SafeCallbackBase { typedef typename XorpMemberCallback13B3::M M; XorpSafeMemberCallback13B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpMemberCallback13B3(o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpSafeMemberCallback13B3() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { if (valid()) { XorpMemberCallback13B3::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); } } }; template struct XorpMemberCallbackFactory13B3 { static XorpMemberCallback13B3* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpSafeMemberCallback13B3(o, p, ba1, ba2, ba3); } }; template struct XorpMemberCallbackFactory13B3 { static XorpMemberCallback13B3* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpMemberCallback13B3(o, p, ba1, ba2, ba3); }; }; /** * Factory function that creates a callback object targetted at a * member function with 13 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback13::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return XorpMemberCallbackFactory13B3::True>::make(o, p, ba1, ba2, ba3); } /** * Factory function that creates a callback object targetted at a * member function with 13 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback13::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return XorpMemberCallbackFactory13B3::True>::make(&o, p, ba1, ba2, ba3); } /** * @short Callback object for const member methods with 13 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstMemberCallback13B3 : public XorpCallback13 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3) const; XorpConstMemberCallback13B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback13(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1, _ba2, _ba3); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for void const member methods with 13 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstMemberCallback13B3 : public XorpCallback13 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3) const; XorpConstMemberCallback13B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback13(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1, _ba2, _ba3); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for const safe member methods with 13 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback13B3 : public XorpConstMemberCallback13B3, public SafeCallbackBase { typedef typename XorpConstMemberCallback13B3::M M; XorpConstSafeMemberCallback13B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpConstMemberCallback13B3(o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback13B3() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { if (valid()) { R r = XorpConstMemberCallback13B3::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); return r; } } }; /** * @short Callback object for void const safe member methods with 13 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback13B3 : public XorpConstMemberCallback13B3, public SafeCallbackBase { typedef typename XorpConstMemberCallback13B3::M M; XorpConstSafeMemberCallback13B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpConstMemberCallback13B3(o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback13B3() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { if (valid()) { XorpConstMemberCallback13B3::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); } } }; template struct XorpConstMemberCallbackFactory13B3 { static XorpConstMemberCallback13B3* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpConstSafeMemberCallback13B3(o, p, ba1, ba2, ba3); } }; template struct XorpConstMemberCallbackFactory13B3 { static XorpConstMemberCallback13B3* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpConstMemberCallback13B3(o, p, ba1, ba2, ba3); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 13 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback13::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return XorpConstMemberCallbackFactory13B3::True>::make(o, p, ba1, ba2, ba3); } /** * Factory function that creates a callback object targetted at a * const member function with 13 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback13::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return XorpConstMemberCallbackFactory13B3::True>::make(&o, p, ba1, ba2, ba3); } /** * @short Callback object for functions with 13 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpFunctionCallback13B4 : public XorpCallback13 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4); XorpFunctionCallback13B4(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback13(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1, _ba2, _ba3, _ba4); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; }; /** * @short Callback object for void functions with 13 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpFunctionCallback13B4 : public XorpCallback13 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4); XorpFunctionCallback13B4(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback13(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1, _ba2, _ba3, _ba4); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; }; /** * Factory function that creates a callback object targetted at a * function with 13 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback13::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return typename XorpCallback13::RefPtr(new XorpFunctionCallback13B4(f, ba1, ba2, ba3, ba4)); } /** * @short Callback object for member methods with 13 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpMemberCallback13B4 : public XorpCallback13 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4) ; XorpMemberCallback13B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback13(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1, _ba2, _ba3, _ba4); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for void member methods with 13 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpMemberCallback13B4 : public XorpCallback13 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4) ; XorpMemberCallback13B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback13(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1, _ba2, _ba3, _ba4); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for safe member methods with 13 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpSafeMemberCallback13B4 : public XorpMemberCallback13B4, public SafeCallbackBase { typedef typename XorpMemberCallback13B4::M M; XorpSafeMemberCallback13B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpMemberCallback13B4(o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpSafeMemberCallback13B4() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { if (valid()) { R r = XorpMemberCallback13B4::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); return r; } } }; /** * @short Callback object for void safe member methods with 13 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpSafeMemberCallback13B4 : public XorpMemberCallback13B4, public SafeCallbackBase { typedef typename XorpMemberCallback13B4::M M; XorpSafeMemberCallback13B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpMemberCallback13B4(o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpSafeMemberCallback13B4() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { if (valid()) { XorpMemberCallback13B4::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); } } }; template struct XorpMemberCallbackFactory13B4 { static XorpMemberCallback13B4* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpSafeMemberCallback13B4(o, p, ba1, ba2, ba3, ba4); } }; template struct XorpMemberCallbackFactory13B4 { static XorpMemberCallback13B4* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpMemberCallback13B4(o, p, ba1, ba2, ba3, ba4); }; }; /** * Factory function that creates a callback object targetted at a * member function with 13 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback13::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpMemberCallbackFactory13B4::True>::make(o, p, ba1, ba2, ba3, ba4); } /** * Factory function that creates a callback object targetted at a * member function with 13 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback13::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpMemberCallbackFactory13B4::True>::make(&o, p, ba1, ba2, ba3, ba4); } /** * @short Callback object for const member methods with 13 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstMemberCallback13B4 : public XorpCallback13 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4) const; XorpConstMemberCallback13B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback13(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1, _ba2, _ba3, _ba4); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for void const member methods with 13 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstMemberCallback13B4 : public XorpCallback13 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4) const; XorpConstMemberCallback13B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback13(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1, _ba2, _ba3, _ba4); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for const safe member methods with 13 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback13B4 : public XorpConstMemberCallback13B4, public SafeCallbackBase { typedef typename XorpConstMemberCallback13B4::M M; XorpConstSafeMemberCallback13B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpConstMemberCallback13B4(o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback13B4() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { if (valid()) { R r = XorpConstMemberCallback13B4::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); return r; } } }; /** * @short Callback object for void const safe member methods with 13 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback13B4 : public XorpConstMemberCallback13B4, public SafeCallbackBase { typedef typename XorpConstMemberCallback13B4::M M; XorpConstSafeMemberCallback13B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpConstMemberCallback13B4(o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback13B4() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { if (valid()) { XorpConstMemberCallback13B4::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); } } }; template struct XorpConstMemberCallbackFactory13B4 { static XorpConstMemberCallback13B4* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpConstSafeMemberCallback13B4(o, p, ba1, ba2, ba3, ba4); } }; template struct XorpConstMemberCallbackFactory13B4 { static XorpConstMemberCallback13B4* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpConstMemberCallback13B4(o, p, ba1, ba2, ba3, ba4); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 13 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback13::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpConstMemberCallbackFactory13B4::True>::make(o, p, ba1, ba2, ba3, ba4); } /** * Factory function that creates a callback object targetted at a * const member function with 13 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback13::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpConstMemberCallbackFactory13B4::True>::make(&o, p, ba1, ba2, ba3, ba4); } /** * @short Callback object for functions with 13 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpFunctionCallback13B5 : public XorpCallback13 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4, BA5); XorpFunctionCallback13B5(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback13(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1, _ba2, _ba3, _ba4, _ba5); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; }; /** * @short Callback object for void functions with 13 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpFunctionCallback13B5 : public XorpCallback13 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4, BA5); XorpFunctionCallback13B5(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback13(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1, _ba2, _ba3, _ba4, _ba5); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; }; /** * Factory function that creates a callback object targetted at a * function with 13 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback13::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return typename XorpCallback13::RefPtr(new XorpFunctionCallback13B5(f, ba1, ba2, ba3, ba4, ba5)); } /** * @short Callback object for member methods with 13 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpMemberCallback13B5 : public XorpCallback13 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4, BA5) ; XorpMemberCallback13B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback13(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1, _ba2, _ba3, _ba4, _ba5); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for void member methods with 13 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpMemberCallback13B5 : public XorpCallback13 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4, BA5) ; XorpMemberCallback13B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback13(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1, _ba2, _ba3, _ba4, _ba5); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for safe member methods with 13 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpSafeMemberCallback13B5 : public XorpMemberCallback13B5, public SafeCallbackBase { typedef typename XorpMemberCallback13B5::M M; XorpSafeMemberCallback13B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpMemberCallback13B5(o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpSafeMemberCallback13B5() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { if (valid()) { R r = XorpMemberCallback13B5::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); return r; } } }; /** * @short Callback object for void safe member methods with 13 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpSafeMemberCallback13B5 : public XorpMemberCallback13B5, public SafeCallbackBase { typedef typename XorpMemberCallback13B5::M M; XorpSafeMemberCallback13B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpMemberCallback13B5(o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpSafeMemberCallback13B5() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { if (valid()) { XorpMemberCallback13B5::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); } } }; template struct XorpMemberCallbackFactory13B5 { static XorpMemberCallback13B5* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpSafeMemberCallback13B5(o, p, ba1, ba2, ba3, ba4, ba5); } }; template struct XorpMemberCallbackFactory13B5 { static XorpMemberCallback13B5* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpMemberCallback13B5(o, p, ba1, ba2, ba3, ba4, ba5); }; }; /** * Factory function that creates a callback object targetted at a * member function with 13 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback13::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpMemberCallbackFactory13B5::True>::make(o, p, ba1, ba2, ba3, ba4, ba5); } /** * Factory function that creates a callback object targetted at a * member function with 13 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback13::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpMemberCallbackFactory13B5::True>::make(&o, p, ba1, ba2, ba3, ba4, ba5); } /** * @short Callback object for const member methods with 13 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstMemberCallback13B5 : public XorpCallback13 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4, BA5) const; XorpConstMemberCallback13B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback13(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1, _ba2, _ba3, _ba4, _ba5); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for void const member methods with 13 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstMemberCallback13B5 : public XorpCallback13 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4, BA5) const; XorpConstMemberCallback13B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback13(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1, _ba2, _ba3, _ba4, _ba5); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for const safe member methods with 13 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback13B5 : public XorpConstMemberCallback13B5, public SafeCallbackBase { typedef typename XorpConstMemberCallback13B5::M M; XorpConstSafeMemberCallback13B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpConstMemberCallback13B5(o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback13B5() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { if (valid()) { R r = XorpConstMemberCallback13B5::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); return r; } } }; /** * @short Callback object for void const safe member methods with 13 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback13B5 : public XorpConstMemberCallback13B5, public SafeCallbackBase { typedef typename XorpConstMemberCallback13B5::M M; XorpConstSafeMemberCallback13B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpConstMemberCallback13B5(o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback13B5() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { if (valid()) { XorpConstMemberCallback13B5::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); } } }; template struct XorpConstMemberCallbackFactory13B5 { static XorpConstMemberCallback13B5* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpConstSafeMemberCallback13B5(o, p, ba1, ba2, ba3, ba4, ba5); } }; template struct XorpConstMemberCallbackFactory13B5 { static XorpConstMemberCallback13B5* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpConstMemberCallback13B5(o, p, ba1, ba2, ba3, ba4, ba5); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 13 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback13::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpConstMemberCallbackFactory13B5::True>::make(o, p, ba1, ba2, ba3, ba4, ba5); } /** * Factory function that creates a callback object targetted at a * const member function with 13 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback13::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpConstMemberCallbackFactory13B5::True>::make(&o, p, ba1, ba2, ba3, ba4, ba5); } /** * @short Callback object for functions with 13 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpFunctionCallback13B6 : public XorpCallback13 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4, BA5, BA6); XorpFunctionCallback13B6(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback13(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; BA6 _ba6; }; /** * @short Callback object for void functions with 13 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpFunctionCallback13B6 : public XorpCallback13 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4, BA5, BA6); XorpFunctionCallback13B6(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback13(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; BA6 _ba6; }; /** * Factory function that creates a callback object targetted at a * function with 13 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback13::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return typename XorpCallback13::RefPtr(new XorpFunctionCallback13B6(f, ba1, ba2, ba3, ba4, ba5, ba6)); } /** * @short Callback object for member methods with 13 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpMemberCallback13B6 : public XorpCallback13 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4, BA5, BA6) ; XorpMemberCallback13B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback13(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for void member methods with 13 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpMemberCallback13B6 : public XorpCallback13 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4, BA5, BA6) ; XorpMemberCallback13B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback13(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for safe member methods with 13 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpSafeMemberCallback13B6 : public XorpMemberCallback13B6, public SafeCallbackBase { typedef typename XorpMemberCallback13B6::M M; XorpSafeMemberCallback13B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpMemberCallback13B6(o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpSafeMemberCallback13B6() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { if (valid()) { R r = XorpMemberCallback13B6::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); return r; } } }; /** * @short Callback object for void safe member methods with 13 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpSafeMemberCallback13B6 : public XorpMemberCallback13B6, public SafeCallbackBase { typedef typename XorpMemberCallback13B6::M M; XorpSafeMemberCallback13B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpMemberCallback13B6(o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpSafeMemberCallback13B6() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { if (valid()) { XorpMemberCallback13B6::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); } } }; template struct XorpMemberCallbackFactory13B6 { static XorpMemberCallback13B6* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpSafeMemberCallback13B6(o, p, ba1, ba2, ba3, ba4, ba5, ba6); } }; template struct XorpMemberCallbackFactory13B6 { static XorpMemberCallback13B6* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpMemberCallback13B6(o, p, ba1, ba2, ba3, ba4, ba5, ba6); }; }; /** * Factory function that creates a callback object targetted at a * member function with 13 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback13::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpMemberCallbackFactory13B6::True>::make(o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * Factory function that creates a callback object targetted at a * member function with 13 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback13::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpMemberCallbackFactory13B6::True>::make(&o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * @short Callback object for const member methods with 13 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstMemberCallback13B6 : public XorpCallback13 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4, BA5, BA6) const; XorpConstMemberCallback13B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback13(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for void const member methods with 13 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstMemberCallback13B6 : public XorpCallback13 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4, BA5, BA6) const; XorpConstMemberCallback13B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback13(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for const safe member methods with 13 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback13B6 : public XorpConstMemberCallback13B6, public SafeCallbackBase { typedef typename XorpConstMemberCallback13B6::M M; XorpConstSafeMemberCallback13B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpConstMemberCallback13B6(o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback13B6() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { if (valid()) { R r = XorpConstMemberCallback13B6::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); return r; } } }; /** * @short Callback object for void const safe member methods with 13 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback13B6 : public XorpConstMemberCallback13B6, public SafeCallbackBase { typedef typename XorpConstMemberCallback13B6::M M; XorpConstSafeMemberCallback13B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpConstMemberCallback13B6(o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback13B6() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { if (valid()) { XorpConstMemberCallback13B6::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); } } }; template struct XorpConstMemberCallbackFactory13B6 { static XorpConstMemberCallback13B6* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpConstSafeMemberCallback13B6(o, p, ba1, ba2, ba3, ba4, ba5, ba6); } }; template struct XorpConstMemberCallbackFactory13B6 { static XorpConstMemberCallback13B6* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpConstMemberCallback13B6(o, p, ba1, ba2, ba3, ba4, ba5, ba6); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 13 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback13::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpConstMemberCallbackFactory13B6::True>::make(o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * Factory function that creates a callback object targetted at a * const member function with 13 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback13::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpConstMemberCallbackFactory13B6::True>::make(&o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /////////////////////////////////////////////////////////////////////////////// // // Code relating to callbacks with 14 late args // /** * @short Base class for callbacks with 14 dispatch time args. */ template struct XorpCallback14 { typedef ref_ptr RefPtr; virtual ~XorpCallback14() {} virtual R dispatch(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14) = 0; }; /** * @short Callback object for functions with 14 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpFunctionCallback14B0 : public XorpCallback14 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14); XorpFunctionCallback14B0(F f) : XorpCallback14(), _f(f) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); return r; } protected: F _f; }; /** * @short Callback object for void functions with 14 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpFunctionCallback14B0 : public XorpCallback14 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14); XorpFunctionCallback14B0(F f) : XorpCallback14(), _f(f) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); } protected: F _f; }; /** * Factory function that creates a callback object targetted at a * function with 14 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback14::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14)) { return typename XorpCallback14::RefPtr(new XorpFunctionCallback14B0(f)); } /** * @short Callback object for member methods with 14 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpMemberCallback14B0 : public XorpCallback14 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14) ; XorpMemberCallback14B0(O* o, M m) : XorpCallback14(), _o(o), _m(m) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for void member methods with 14 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpMemberCallback14B0 : public XorpCallback14 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14) ; XorpMemberCallback14B0(O* o, M m) : XorpCallback14(), _o(o), _m(m) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for safe member methods with 14 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpSafeMemberCallback14B0 : public XorpMemberCallback14B0, public SafeCallbackBase { typedef typename XorpMemberCallback14B0::M M; XorpSafeMemberCallback14B0(O* o, M m) : XorpMemberCallback14B0(o, m), SafeCallbackBase(o) {} ~XorpSafeMemberCallback14B0() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { if (valid()) { R r = XorpMemberCallback14B0::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); return r; } } }; /** * @short Callback object for void safe member methods with 14 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpSafeMemberCallback14B0 : public XorpMemberCallback14B0, public SafeCallbackBase { typedef typename XorpMemberCallback14B0::M M; XorpSafeMemberCallback14B0(O* o, M m) : XorpMemberCallback14B0(o, m), SafeCallbackBase(o) {} ~XorpSafeMemberCallback14B0() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { if (valid()) { XorpMemberCallback14B0::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); } } }; template struct XorpMemberCallbackFactory14B0 { static XorpMemberCallback14B0* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14)) { return new XorpSafeMemberCallback14B0(o, p); } }; template struct XorpMemberCallbackFactory14B0 { static XorpMemberCallback14B0* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14)) { return new XorpMemberCallback14B0(o, p); }; }; /** * Factory function that creates a callback object targetted at a * member function with 14 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback14::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14)) { return XorpMemberCallbackFactory14B0::True>::make(o, p); } /** * Factory function that creates a callback object targetted at a * member function with 14 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback14::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14)) { return XorpMemberCallbackFactory14B0::True>::make(&o, p); } /** * @short Callback object for const member methods with 14 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstMemberCallback14B0 : public XorpCallback14 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14) const; XorpConstMemberCallback14B0(O* o, M m) : XorpCallback14(), _o(o), _m(m) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for void const member methods with 14 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstMemberCallback14B0 : public XorpCallback14 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14) const; XorpConstMemberCallback14B0(O* o, M m) : XorpCallback14(), _o(o), _m(m) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for const safe member methods with 14 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback14B0 : public XorpConstMemberCallback14B0, public SafeCallbackBase { typedef typename XorpConstMemberCallback14B0::M M; XorpConstSafeMemberCallback14B0(O* o, M m) : XorpConstMemberCallback14B0(o, m), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback14B0() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { if (valid()) { R r = XorpConstMemberCallback14B0::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); return r; } } }; /** * @short Callback object for void const safe member methods with 14 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback14B0 : public XorpConstMemberCallback14B0, public SafeCallbackBase { typedef typename XorpConstMemberCallback14B0::M M; XorpConstSafeMemberCallback14B0(O* o, M m) : XorpConstMemberCallback14B0(o, m), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback14B0() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { if (valid()) { XorpConstMemberCallback14B0::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); } } }; template struct XorpConstMemberCallbackFactory14B0 { static XorpConstMemberCallback14B0* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14) const) { return new XorpConstSafeMemberCallback14B0(o, p); } }; template struct XorpConstMemberCallbackFactory14B0 { static XorpConstMemberCallback14B0* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14) const) { return new XorpConstMemberCallback14B0(o, p); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 14 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback14::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14) const) { return XorpConstMemberCallbackFactory14B0::True>::make(o, p); } /** * Factory function that creates a callback object targetted at a * const member function with 14 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback14::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14) const) { return XorpConstMemberCallbackFactory14B0::True>::make(&o, p); } /** * @short Callback object for functions with 14 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpFunctionCallback14B1 : public XorpCallback14 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1); XorpFunctionCallback14B1(F f, BA1 ba1) : XorpCallback14(), _f(f), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1); return r; } protected: F _f; BA1 _ba1; }; /** * @short Callback object for void functions with 14 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpFunctionCallback14B1 : public XorpCallback14 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1); XorpFunctionCallback14B1(F f, BA1 ba1) : XorpCallback14(), _f(f), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1); } protected: F _f; BA1 _ba1; }; /** * Factory function that creates a callback object targetted at a * function with 14 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback14::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1), BA1 ba1) { return typename XorpCallback14::RefPtr(new XorpFunctionCallback14B1(f, ba1)); } /** * @short Callback object for member methods with 14 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpMemberCallback14B1 : public XorpCallback14 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1) ; XorpMemberCallback14B1(O* o, M m, BA1 ba1) : XorpCallback14(), _o(o), _m(m), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for void member methods with 14 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpMemberCallback14B1 : public XorpCallback14 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1) ; XorpMemberCallback14B1(O* o, M m, BA1 ba1) : XorpCallback14(), _o(o), _m(m), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for safe member methods with 14 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpSafeMemberCallback14B1 : public XorpMemberCallback14B1, public SafeCallbackBase { typedef typename XorpMemberCallback14B1::M M; XorpSafeMemberCallback14B1(O* o, M m, BA1 ba1) : XorpMemberCallback14B1(o, m, ba1), SafeCallbackBase(o) {} ~XorpSafeMemberCallback14B1() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { if (valid()) { R r = XorpMemberCallback14B1::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); return r; } } }; /** * @short Callback object for void safe member methods with 14 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpSafeMemberCallback14B1 : public XorpMemberCallback14B1, public SafeCallbackBase { typedef typename XorpMemberCallback14B1::M M; XorpSafeMemberCallback14B1(O* o, M m, BA1 ba1) : XorpMemberCallback14B1(o, m, ba1), SafeCallbackBase(o) {} ~XorpSafeMemberCallback14B1() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { if (valid()) { XorpMemberCallback14B1::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); } } }; template struct XorpMemberCallbackFactory14B1 { static XorpMemberCallback14B1* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1), BA1 ba1) { return new XorpSafeMemberCallback14B1(o, p, ba1); } }; template struct XorpMemberCallbackFactory14B1 { static XorpMemberCallback14B1* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1), BA1 ba1) { return new XorpMemberCallback14B1(o, p, ba1); }; }; /** * Factory function that creates a callback object targetted at a * member function with 14 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback14::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1), BA1 ba1) { return XorpMemberCallbackFactory14B1::True>::make(o, p, ba1); } /** * Factory function that creates a callback object targetted at a * member function with 14 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback14::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1), BA1 ba1) { return XorpMemberCallbackFactory14B1::True>::make(&o, p, ba1); } /** * @short Callback object for const member methods with 14 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstMemberCallback14B1 : public XorpCallback14 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1) const; XorpConstMemberCallback14B1(O* o, M m, BA1 ba1) : XorpCallback14(), _o(o), _m(m), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for void const member methods with 14 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstMemberCallback14B1 : public XorpCallback14 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1) const; XorpConstMemberCallback14B1(O* o, M m, BA1 ba1) : XorpCallback14(), _o(o), _m(m), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for const safe member methods with 14 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback14B1 : public XorpConstMemberCallback14B1, public SafeCallbackBase { typedef typename XorpConstMemberCallback14B1::M M; XorpConstSafeMemberCallback14B1(O* o, M m, BA1 ba1) : XorpConstMemberCallback14B1(o, m, ba1), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback14B1() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { if (valid()) { R r = XorpConstMemberCallback14B1::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); return r; } } }; /** * @short Callback object for void const safe member methods with 14 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback14B1 : public XorpConstMemberCallback14B1, public SafeCallbackBase { typedef typename XorpConstMemberCallback14B1::M M; XorpConstSafeMemberCallback14B1(O* o, M m, BA1 ba1) : XorpConstMemberCallback14B1(o, m, ba1), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback14B1() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { if (valid()) { XorpConstMemberCallback14B1::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); } } }; template struct XorpConstMemberCallbackFactory14B1 { static XorpConstMemberCallback14B1* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1) const, BA1 ba1) { return new XorpConstSafeMemberCallback14B1(o, p, ba1); } }; template struct XorpConstMemberCallbackFactory14B1 { static XorpConstMemberCallback14B1* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1) const, BA1 ba1) { return new XorpConstMemberCallback14B1(o, p, ba1); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 14 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback14::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1) const, BA1 ba1) { return XorpConstMemberCallbackFactory14B1::True>::make(o, p, ba1); } /** * Factory function that creates a callback object targetted at a * const member function with 14 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback14::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1) const, BA1 ba1) { return XorpConstMemberCallbackFactory14B1::True>::make(&o, p, ba1); } /** * @short Callback object for functions with 14 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpFunctionCallback14B2 : public XorpCallback14 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2); XorpFunctionCallback14B2(F f, BA1 ba1, BA2 ba2) : XorpCallback14(), _f(f), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1, _ba2); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; }; /** * @short Callback object for void functions with 14 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpFunctionCallback14B2 : public XorpCallback14 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2); XorpFunctionCallback14B2(F f, BA1 ba1, BA2 ba2) : XorpCallback14(), _f(f), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1, _ba2); } protected: F _f; BA1 _ba1; BA2 _ba2; }; /** * Factory function that creates a callback object targetted at a * function with 14 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback14::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2), BA1 ba1, BA2 ba2) { return typename XorpCallback14::RefPtr(new XorpFunctionCallback14B2(f, ba1, ba2)); } /** * @short Callback object for member methods with 14 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpMemberCallback14B2 : public XorpCallback14 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2) ; XorpMemberCallback14B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback14(), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1, _ba2); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for void member methods with 14 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpMemberCallback14B2 : public XorpCallback14 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2) ; XorpMemberCallback14B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback14(), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1, _ba2); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for safe member methods with 14 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpSafeMemberCallback14B2 : public XorpMemberCallback14B2, public SafeCallbackBase { typedef typename XorpMemberCallback14B2::M M; XorpSafeMemberCallback14B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpMemberCallback14B2(o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpSafeMemberCallback14B2() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { if (valid()) { R r = XorpMemberCallback14B2::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); return r; } } }; /** * @short Callback object for void safe member methods with 14 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpSafeMemberCallback14B2 : public XorpMemberCallback14B2, public SafeCallbackBase { typedef typename XorpMemberCallback14B2::M M; XorpSafeMemberCallback14B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpMemberCallback14B2(o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpSafeMemberCallback14B2() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { if (valid()) { XorpMemberCallback14B2::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); } } }; template struct XorpMemberCallbackFactory14B2 { static XorpMemberCallback14B2* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2), BA1 ba1, BA2 ba2) { return new XorpSafeMemberCallback14B2(o, p, ba1, ba2); } }; template struct XorpMemberCallbackFactory14B2 { static XorpMemberCallback14B2* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2), BA1 ba1, BA2 ba2) { return new XorpMemberCallback14B2(o, p, ba1, ba2); }; }; /** * Factory function that creates a callback object targetted at a * member function with 14 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback14::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2), BA1 ba1, BA2 ba2) { return XorpMemberCallbackFactory14B2::True>::make(o, p, ba1, ba2); } /** * Factory function that creates a callback object targetted at a * member function with 14 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback14::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2), BA1 ba1, BA2 ba2) { return XorpMemberCallbackFactory14B2::True>::make(&o, p, ba1, ba2); } /** * @short Callback object for const member methods with 14 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstMemberCallback14B2 : public XorpCallback14 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2) const; XorpConstMemberCallback14B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback14(), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1, _ba2); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for void const member methods with 14 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstMemberCallback14B2 : public XorpCallback14 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2) const; XorpConstMemberCallback14B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback14(), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1, _ba2); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for const safe member methods with 14 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback14B2 : public XorpConstMemberCallback14B2, public SafeCallbackBase { typedef typename XorpConstMemberCallback14B2::M M; XorpConstSafeMemberCallback14B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpConstMemberCallback14B2(o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback14B2() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { if (valid()) { R r = XorpConstMemberCallback14B2::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); return r; } } }; /** * @short Callback object for void const safe member methods with 14 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback14B2 : public XorpConstMemberCallback14B2, public SafeCallbackBase { typedef typename XorpConstMemberCallback14B2::M M; XorpConstSafeMemberCallback14B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpConstMemberCallback14B2(o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback14B2() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { if (valid()) { XorpConstMemberCallback14B2::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); } } }; template struct XorpConstMemberCallbackFactory14B2 { static XorpConstMemberCallback14B2* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2) const, BA1 ba1, BA2 ba2) { return new XorpConstSafeMemberCallback14B2(o, p, ba1, ba2); } }; template struct XorpConstMemberCallbackFactory14B2 { static XorpConstMemberCallback14B2* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2) const, BA1 ba1, BA2 ba2) { return new XorpConstMemberCallback14B2(o, p, ba1, ba2); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 14 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback14::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2) const, BA1 ba1, BA2 ba2) { return XorpConstMemberCallbackFactory14B2::True>::make(o, p, ba1, ba2); } /** * Factory function that creates a callback object targetted at a * const member function with 14 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback14::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2) const, BA1 ba1, BA2 ba2) { return XorpConstMemberCallbackFactory14B2::True>::make(&o, p, ba1, ba2); } /** * @short Callback object for functions with 14 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpFunctionCallback14B3 : public XorpCallback14 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3); XorpFunctionCallback14B3(F f, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback14(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1, _ba2, _ba3); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; }; /** * @short Callback object for void functions with 14 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpFunctionCallback14B3 : public XorpCallback14 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3); XorpFunctionCallback14B3(F f, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback14(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1, _ba2, _ba3); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; }; /** * Factory function that creates a callback object targetted at a * function with 14 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback14::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return typename XorpCallback14::RefPtr(new XorpFunctionCallback14B3(f, ba1, ba2, ba3)); } /** * @short Callback object for member methods with 14 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpMemberCallback14B3 : public XorpCallback14 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3) ; XorpMemberCallback14B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback14(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1, _ba2, _ba3); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for void member methods with 14 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpMemberCallback14B3 : public XorpCallback14 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3) ; XorpMemberCallback14B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback14(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1, _ba2, _ba3); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for safe member methods with 14 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpSafeMemberCallback14B3 : public XorpMemberCallback14B3, public SafeCallbackBase { typedef typename XorpMemberCallback14B3::M M; XorpSafeMemberCallback14B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpMemberCallback14B3(o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpSafeMemberCallback14B3() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { if (valid()) { R r = XorpMemberCallback14B3::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); return r; } } }; /** * @short Callback object for void safe member methods with 14 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpSafeMemberCallback14B3 : public XorpMemberCallback14B3, public SafeCallbackBase { typedef typename XorpMemberCallback14B3::M M; XorpSafeMemberCallback14B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpMemberCallback14B3(o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpSafeMemberCallback14B3() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { if (valid()) { XorpMemberCallback14B3::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); } } }; template struct XorpMemberCallbackFactory14B3 { static XorpMemberCallback14B3* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpSafeMemberCallback14B3(o, p, ba1, ba2, ba3); } }; template struct XorpMemberCallbackFactory14B3 { static XorpMemberCallback14B3* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpMemberCallback14B3(o, p, ba1, ba2, ba3); }; }; /** * Factory function that creates a callback object targetted at a * member function with 14 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback14::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return XorpMemberCallbackFactory14B3::True>::make(o, p, ba1, ba2, ba3); } /** * Factory function that creates a callback object targetted at a * member function with 14 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback14::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return XorpMemberCallbackFactory14B3::True>::make(&o, p, ba1, ba2, ba3); } /** * @short Callback object for const member methods with 14 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstMemberCallback14B3 : public XorpCallback14 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3) const; XorpConstMemberCallback14B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback14(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1, _ba2, _ba3); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for void const member methods with 14 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstMemberCallback14B3 : public XorpCallback14 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3) const; XorpConstMemberCallback14B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback14(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1, _ba2, _ba3); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for const safe member methods with 14 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback14B3 : public XorpConstMemberCallback14B3, public SafeCallbackBase { typedef typename XorpConstMemberCallback14B3::M M; XorpConstSafeMemberCallback14B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpConstMemberCallback14B3(o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback14B3() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { if (valid()) { R r = XorpConstMemberCallback14B3::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); return r; } } }; /** * @short Callback object for void const safe member methods with 14 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback14B3 : public XorpConstMemberCallback14B3, public SafeCallbackBase { typedef typename XorpConstMemberCallback14B3::M M; XorpConstSafeMemberCallback14B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpConstMemberCallback14B3(o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback14B3() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { if (valid()) { XorpConstMemberCallback14B3::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); } } }; template struct XorpConstMemberCallbackFactory14B3 { static XorpConstMemberCallback14B3* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpConstSafeMemberCallback14B3(o, p, ba1, ba2, ba3); } }; template struct XorpConstMemberCallbackFactory14B3 { static XorpConstMemberCallback14B3* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpConstMemberCallback14B3(o, p, ba1, ba2, ba3); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 14 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback14::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return XorpConstMemberCallbackFactory14B3::True>::make(o, p, ba1, ba2, ba3); } /** * Factory function that creates a callback object targetted at a * const member function with 14 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback14::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return XorpConstMemberCallbackFactory14B3::True>::make(&o, p, ba1, ba2, ba3); } /** * @short Callback object for functions with 14 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpFunctionCallback14B4 : public XorpCallback14 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4); XorpFunctionCallback14B4(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback14(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1, _ba2, _ba3, _ba4); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; }; /** * @short Callback object for void functions with 14 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpFunctionCallback14B4 : public XorpCallback14 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4); XorpFunctionCallback14B4(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback14(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1, _ba2, _ba3, _ba4); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; }; /** * Factory function that creates a callback object targetted at a * function with 14 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback14::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return typename XorpCallback14::RefPtr(new XorpFunctionCallback14B4(f, ba1, ba2, ba3, ba4)); } /** * @short Callback object for member methods with 14 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpMemberCallback14B4 : public XorpCallback14 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4) ; XorpMemberCallback14B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback14(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1, _ba2, _ba3, _ba4); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for void member methods with 14 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpMemberCallback14B4 : public XorpCallback14 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4) ; XorpMemberCallback14B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback14(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1, _ba2, _ba3, _ba4); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for safe member methods with 14 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpSafeMemberCallback14B4 : public XorpMemberCallback14B4, public SafeCallbackBase { typedef typename XorpMemberCallback14B4::M M; XorpSafeMemberCallback14B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpMemberCallback14B4(o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpSafeMemberCallback14B4() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { if (valid()) { R r = XorpMemberCallback14B4::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); return r; } } }; /** * @short Callback object for void safe member methods with 14 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpSafeMemberCallback14B4 : public XorpMemberCallback14B4, public SafeCallbackBase { typedef typename XorpMemberCallback14B4::M M; XorpSafeMemberCallback14B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpMemberCallback14B4(o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpSafeMemberCallback14B4() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { if (valid()) { XorpMemberCallback14B4::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); } } }; template struct XorpMemberCallbackFactory14B4 { static XorpMemberCallback14B4* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpSafeMemberCallback14B4(o, p, ba1, ba2, ba3, ba4); } }; template struct XorpMemberCallbackFactory14B4 { static XorpMemberCallback14B4* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpMemberCallback14B4(o, p, ba1, ba2, ba3, ba4); }; }; /** * Factory function that creates a callback object targetted at a * member function with 14 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback14::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpMemberCallbackFactory14B4::True>::make(o, p, ba1, ba2, ba3, ba4); } /** * Factory function that creates a callback object targetted at a * member function with 14 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback14::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpMemberCallbackFactory14B4::True>::make(&o, p, ba1, ba2, ba3, ba4); } /** * @short Callback object for const member methods with 14 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstMemberCallback14B4 : public XorpCallback14 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4) const; XorpConstMemberCallback14B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback14(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1, _ba2, _ba3, _ba4); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for void const member methods with 14 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstMemberCallback14B4 : public XorpCallback14 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4) const; XorpConstMemberCallback14B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback14(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1, _ba2, _ba3, _ba4); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for const safe member methods with 14 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback14B4 : public XorpConstMemberCallback14B4, public SafeCallbackBase { typedef typename XorpConstMemberCallback14B4::M M; XorpConstSafeMemberCallback14B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpConstMemberCallback14B4(o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback14B4() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { if (valid()) { R r = XorpConstMemberCallback14B4::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); return r; } } }; /** * @short Callback object for void const safe member methods with 14 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback14B4 : public XorpConstMemberCallback14B4, public SafeCallbackBase { typedef typename XorpConstMemberCallback14B4::M M; XorpConstSafeMemberCallback14B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpConstMemberCallback14B4(o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback14B4() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { if (valid()) { XorpConstMemberCallback14B4::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); } } }; template struct XorpConstMemberCallbackFactory14B4 { static XorpConstMemberCallback14B4* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpConstSafeMemberCallback14B4(o, p, ba1, ba2, ba3, ba4); } }; template struct XorpConstMemberCallbackFactory14B4 { static XorpConstMemberCallback14B4* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpConstMemberCallback14B4(o, p, ba1, ba2, ba3, ba4); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 14 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback14::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpConstMemberCallbackFactory14B4::True>::make(o, p, ba1, ba2, ba3, ba4); } /** * Factory function that creates a callback object targetted at a * const member function with 14 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback14::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpConstMemberCallbackFactory14B4::True>::make(&o, p, ba1, ba2, ba3, ba4); } /** * @short Callback object for functions with 14 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpFunctionCallback14B5 : public XorpCallback14 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4, BA5); XorpFunctionCallback14B5(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback14(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1, _ba2, _ba3, _ba4, _ba5); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; }; /** * @short Callback object for void functions with 14 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpFunctionCallback14B5 : public XorpCallback14 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4, BA5); XorpFunctionCallback14B5(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback14(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1, _ba2, _ba3, _ba4, _ba5); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; }; /** * Factory function that creates a callback object targetted at a * function with 14 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback14::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return typename XorpCallback14::RefPtr(new XorpFunctionCallback14B5(f, ba1, ba2, ba3, ba4, ba5)); } /** * @short Callback object for member methods with 14 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpMemberCallback14B5 : public XorpCallback14 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4, BA5) ; XorpMemberCallback14B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback14(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1, _ba2, _ba3, _ba4, _ba5); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for void member methods with 14 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpMemberCallback14B5 : public XorpCallback14 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4, BA5) ; XorpMemberCallback14B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback14(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1, _ba2, _ba3, _ba4, _ba5); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for safe member methods with 14 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpSafeMemberCallback14B5 : public XorpMemberCallback14B5, public SafeCallbackBase { typedef typename XorpMemberCallback14B5::M M; XorpSafeMemberCallback14B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpMemberCallback14B5(o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpSafeMemberCallback14B5() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { if (valid()) { R r = XorpMemberCallback14B5::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); return r; } } }; /** * @short Callback object for void safe member methods with 14 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpSafeMemberCallback14B5 : public XorpMemberCallback14B5, public SafeCallbackBase { typedef typename XorpMemberCallback14B5::M M; XorpSafeMemberCallback14B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpMemberCallback14B5(o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpSafeMemberCallback14B5() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { if (valid()) { XorpMemberCallback14B5::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); } } }; template struct XorpMemberCallbackFactory14B5 { static XorpMemberCallback14B5* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpSafeMemberCallback14B5(o, p, ba1, ba2, ba3, ba4, ba5); } }; template struct XorpMemberCallbackFactory14B5 { static XorpMemberCallback14B5* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpMemberCallback14B5(o, p, ba1, ba2, ba3, ba4, ba5); }; }; /** * Factory function that creates a callback object targetted at a * member function with 14 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback14::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpMemberCallbackFactory14B5::True>::make(o, p, ba1, ba2, ba3, ba4, ba5); } /** * Factory function that creates a callback object targetted at a * member function with 14 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback14::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpMemberCallbackFactory14B5::True>::make(&o, p, ba1, ba2, ba3, ba4, ba5); } /** * @short Callback object for const member methods with 14 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstMemberCallback14B5 : public XorpCallback14 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4, BA5) const; XorpConstMemberCallback14B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback14(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1, _ba2, _ba3, _ba4, _ba5); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for void const member methods with 14 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstMemberCallback14B5 : public XorpCallback14 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4, BA5) const; XorpConstMemberCallback14B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback14(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1, _ba2, _ba3, _ba4, _ba5); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for const safe member methods with 14 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback14B5 : public XorpConstMemberCallback14B5, public SafeCallbackBase { typedef typename XorpConstMemberCallback14B5::M M; XorpConstSafeMemberCallback14B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpConstMemberCallback14B5(o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback14B5() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { if (valid()) { R r = XorpConstMemberCallback14B5::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); return r; } } }; /** * @short Callback object for void const safe member methods with 14 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback14B5 : public XorpConstMemberCallback14B5, public SafeCallbackBase { typedef typename XorpConstMemberCallback14B5::M M; XorpConstSafeMemberCallback14B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpConstMemberCallback14B5(o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback14B5() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { if (valid()) { XorpConstMemberCallback14B5::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); } } }; template struct XorpConstMemberCallbackFactory14B5 { static XorpConstMemberCallback14B5* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpConstSafeMemberCallback14B5(o, p, ba1, ba2, ba3, ba4, ba5); } }; template struct XorpConstMemberCallbackFactory14B5 { static XorpConstMemberCallback14B5* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpConstMemberCallback14B5(o, p, ba1, ba2, ba3, ba4, ba5); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 14 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback14::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpConstMemberCallbackFactory14B5::True>::make(o, p, ba1, ba2, ba3, ba4, ba5); } /** * Factory function that creates a callback object targetted at a * const member function with 14 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback14::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpConstMemberCallbackFactory14B5::True>::make(&o, p, ba1, ba2, ba3, ba4, ba5); } /** * @short Callback object for functions with 14 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpFunctionCallback14B6 : public XorpCallback14 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4, BA5, BA6); XorpFunctionCallback14B6(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback14(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; BA6 _ba6; }; /** * @short Callback object for void functions with 14 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpFunctionCallback14B6 : public XorpCallback14 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4, BA5, BA6); XorpFunctionCallback14B6(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback14(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; BA6 _ba6; }; /** * Factory function that creates a callback object targetted at a * function with 14 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback14::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return typename XorpCallback14::RefPtr(new XorpFunctionCallback14B6(f, ba1, ba2, ba3, ba4, ba5, ba6)); } /** * @short Callback object for member methods with 14 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpMemberCallback14B6 : public XorpCallback14 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4, BA5, BA6) ; XorpMemberCallback14B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback14(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for void member methods with 14 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpMemberCallback14B6 : public XorpCallback14 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4, BA5, BA6) ; XorpMemberCallback14B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback14(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for safe member methods with 14 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpSafeMemberCallback14B6 : public XorpMemberCallback14B6, public SafeCallbackBase { typedef typename XorpMemberCallback14B6::M M; XorpSafeMemberCallback14B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpMemberCallback14B6(o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpSafeMemberCallback14B6() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { if (valid()) { R r = XorpMemberCallback14B6::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); return r; } } }; /** * @short Callback object for void safe member methods with 14 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpSafeMemberCallback14B6 : public XorpMemberCallback14B6, public SafeCallbackBase { typedef typename XorpMemberCallback14B6::M M; XorpSafeMemberCallback14B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpMemberCallback14B6(o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpSafeMemberCallback14B6() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { if (valid()) { XorpMemberCallback14B6::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); } } }; template struct XorpMemberCallbackFactory14B6 { static XorpMemberCallback14B6* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpSafeMemberCallback14B6(o, p, ba1, ba2, ba3, ba4, ba5, ba6); } }; template struct XorpMemberCallbackFactory14B6 { static XorpMemberCallback14B6* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpMemberCallback14B6(o, p, ba1, ba2, ba3, ba4, ba5, ba6); }; }; /** * Factory function that creates a callback object targetted at a * member function with 14 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback14::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpMemberCallbackFactory14B6::True>::make(o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * Factory function that creates a callback object targetted at a * member function with 14 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback14::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpMemberCallbackFactory14B6::True>::make(&o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * @short Callback object for const member methods with 14 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstMemberCallback14B6 : public XorpCallback14 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4, BA5, BA6) const; XorpConstMemberCallback14B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback14(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for void const member methods with 14 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstMemberCallback14B6 : public XorpCallback14 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4, BA5, BA6) const; XorpConstMemberCallback14B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback14(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for const safe member methods with 14 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback14B6 : public XorpConstMemberCallback14B6, public SafeCallbackBase { typedef typename XorpConstMemberCallback14B6::M M; XorpConstSafeMemberCallback14B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpConstMemberCallback14B6(o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback14B6() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { if (valid()) { R r = XorpConstMemberCallback14B6::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); return r; } } }; /** * @short Callback object for void const safe member methods with 14 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback14B6 : public XorpConstMemberCallback14B6, public SafeCallbackBase { typedef typename XorpConstMemberCallback14B6::M M; XorpConstSafeMemberCallback14B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpConstMemberCallback14B6(o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback14B6() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14) { if (valid()) { XorpConstMemberCallback14B6::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); } } }; template struct XorpConstMemberCallbackFactory14B6 { static XorpConstMemberCallback14B6* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpConstSafeMemberCallback14B6(o, p, ba1, ba2, ba3, ba4, ba5, ba6); } }; template struct XorpConstMemberCallbackFactory14B6 { static XorpConstMemberCallback14B6* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpConstMemberCallback14B6(o, p, ba1, ba2, ba3, ba4, ba5, ba6); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 14 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback14::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpConstMemberCallbackFactory14B6::True>::make(o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * Factory function that creates a callback object targetted at a * const member function with 14 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback14::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpConstMemberCallbackFactory14B6::True>::make(&o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /////////////////////////////////////////////////////////////////////////////// // // Code relating to callbacks with 15 late args // /** * @short Base class for callbacks with 15 dispatch time args. */ template struct XorpCallback15 { typedef ref_ptr RefPtr; virtual ~XorpCallback15() {} virtual R dispatch(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15) = 0; }; /** * @short Callback object for functions with 15 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpFunctionCallback15B0 : public XorpCallback15 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15); XorpFunctionCallback15B0(F f) : XorpCallback15(), _f(f) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); return r; } protected: F _f; }; /** * @short Callback object for void functions with 15 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpFunctionCallback15B0 : public XorpCallback15 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15); XorpFunctionCallback15B0(F f) : XorpCallback15(), _f(f) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); } protected: F _f; }; /** * Factory function that creates a callback object targetted at a * function with 15 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback15::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15)) { return typename XorpCallback15::RefPtr(new XorpFunctionCallback15B0(f)); } /** * @short Callback object for member methods with 15 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpMemberCallback15B0 : public XorpCallback15 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15) ; XorpMemberCallback15B0(O* o, M m) : XorpCallback15(), _o(o), _m(m) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for void member methods with 15 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpMemberCallback15B0 : public XorpCallback15 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15) ; XorpMemberCallback15B0(O* o, M m) : XorpCallback15(), _o(o), _m(m) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for safe member methods with 15 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpSafeMemberCallback15B0 : public XorpMemberCallback15B0, public SafeCallbackBase { typedef typename XorpMemberCallback15B0::M M; XorpSafeMemberCallback15B0(O* o, M m) : XorpMemberCallback15B0(o, m), SafeCallbackBase(o) {} ~XorpSafeMemberCallback15B0() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { if (valid()) { R r = XorpMemberCallback15B0::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); return r; } } }; /** * @short Callback object for void safe member methods with 15 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpSafeMemberCallback15B0 : public XorpMemberCallback15B0, public SafeCallbackBase { typedef typename XorpMemberCallback15B0::M M; XorpSafeMemberCallback15B0(O* o, M m) : XorpMemberCallback15B0(o, m), SafeCallbackBase(o) {} ~XorpSafeMemberCallback15B0() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { if (valid()) { XorpMemberCallback15B0::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); } } }; template struct XorpMemberCallbackFactory15B0 { static XorpMemberCallback15B0* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15)) { return new XorpSafeMemberCallback15B0(o, p); } }; template struct XorpMemberCallbackFactory15B0 { static XorpMemberCallback15B0* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15)) { return new XorpMemberCallback15B0(o, p); }; }; /** * Factory function that creates a callback object targetted at a * member function with 15 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback15::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15)) { return XorpMemberCallbackFactory15B0::True>::make(o, p); } /** * Factory function that creates a callback object targetted at a * member function with 15 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback15::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15)) { return XorpMemberCallbackFactory15B0::True>::make(&o, p); } /** * @short Callback object for const member methods with 15 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstMemberCallback15B0 : public XorpCallback15 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15) const; XorpConstMemberCallback15B0(O* o, M m) : XorpCallback15(), _o(o), _m(m) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for void const member methods with 15 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstMemberCallback15B0 : public XorpCallback15 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15) const; XorpConstMemberCallback15B0(O* o, M m) : XorpCallback15(), _o(o), _m(m) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); } protected: O* _o; // Callback's target object M _m; // Callback's target method }; /** * @short Callback object for const safe member methods with 15 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback15B0 : public XorpConstMemberCallback15B0, public SafeCallbackBase { typedef typename XorpConstMemberCallback15B0::M M; XorpConstSafeMemberCallback15B0(O* o, M m) : XorpConstMemberCallback15B0(o, m), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback15B0() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { if (valid()) { R r = XorpConstMemberCallback15B0::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); return r; } } }; /** * @short Callback object for void const safe member methods with 15 dispatch time * arguments and 0 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback15B0 : public XorpConstMemberCallback15B0, public SafeCallbackBase { typedef typename XorpConstMemberCallback15B0::M M; XorpConstSafeMemberCallback15B0(O* o, M m) : XorpConstMemberCallback15B0(o, m), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback15B0() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { if (valid()) { XorpConstMemberCallback15B0::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); } } }; template struct XorpConstMemberCallbackFactory15B0 { static XorpConstMemberCallback15B0* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15) const) { return new XorpConstSafeMemberCallback15B0(o, p); } }; template struct XorpConstMemberCallbackFactory15B0 { static XorpConstMemberCallback15B0* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15) const) { return new XorpConstMemberCallback15B0(o, p); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 15 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback15::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15) const) { return XorpConstMemberCallbackFactory15B0::True>::make(o, p); } /** * Factory function that creates a callback object targetted at a * const member function with 15 dispatch time arguments and 0 bound arguments. */ template typename XorpCallback15::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15) const) { return XorpConstMemberCallbackFactory15B0::True>::make(&o, p); } /** * @short Callback object for functions with 15 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpFunctionCallback15B1 : public XorpCallback15 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1); XorpFunctionCallback15B1(F f, BA1 ba1) : XorpCallback15(), _f(f), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1); return r; } protected: F _f; BA1 _ba1; }; /** * @short Callback object for void functions with 15 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpFunctionCallback15B1 : public XorpCallback15 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1); XorpFunctionCallback15B1(F f, BA1 ba1) : XorpCallback15(), _f(f), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1); } protected: F _f; BA1 _ba1; }; /** * Factory function that creates a callback object targetted at a * function with 15 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback15::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1), BA1 ba1) { return typename XorpCallback15::RefPtr(new XorpFunctionCallback15B1(f, ba1)); } /** * @short Callback object for member methods with 15 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpMemberCallback15B1 : public XorpCallback15 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1) ; XorpMemberCallback15B1(O* o, M m, BA1 ba1) : XorpCallback15(), _o(o), _m(m), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for void member methods with 15 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpMemberCallback15B1 : public XorpCallback15 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1) ; XorpMemberCallback15B1(O* o, M m, BA1 ba1) : XorpCallback15(), _o(o), _m(m), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for safe member methods with 15 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpSafeMemberCallback15B1 : public XorpMemberCallback15B1, public SafeCallbackBase { typedef typename XorpMemberCallback15B1::M M; XorpSafeMemberCallback15B1(O* o, M m, BA1 ba1) : XorpMemberCallback15B1(o, m, ba1), SafeCallbackBase(o) {} ~XorpSafeMemberCallback15B1() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { if (valid()) { R r = XorpMemberCallback15B1::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); return r; } } }; /** * @short Callback object for void safe member methods with 15 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpSafeMemberCallback15B1 : public XorpMemberCallback15B1, public SafeCallbackBase { typedef typename XorpMemberCallback15B1::M M; XorpSafeMemberCallback15B1(O* o, M m, BA1 ba1) : XorpMemberCallback15B1(o, m, ba1), SafeCallbackBase(o) {} ~XorpSafeMemberCallback15B1() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { if (valid()) { XorpMemberCallback15B1::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); } } }; template struct XorpMemberCallbackFactory15B1 { static XorpMemberCallback15B1* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1), BA1 ba1) { return new XorpSafeMemberCallback15B1(o, p, ba1); } }; template struct XorpMemberCallbackFactory15B1 { static XorpMemberCallback15B1* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1), BA1 ba1) { return new XorpMemberCallback15B1(o, p, ba1); }; }; /** * Factory function that creates a callback object targetted at a * member function with 15 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback15::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1), BA1 ba1) { return XorpMemberCallbackFactory15B1::True>::make(o, p, ba1); } /** * Factory function that creates a callback object targetted at a * member function with 15 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback15::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1), BA1 ba1) { return XorpMemberCallbackFactory15B1::True>::make(&o, p, ba1); } /** * @short Callback object for const member methods with 15 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstMemberCallback15B1 : public XorpCallback15 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1) const; XorpConstMemberCallback15B1(O* o, M m, BA1 ba1) : XorpCallback15(), _o(o), _m(m), _ba1(ba1) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for void const member methods with 15 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstMemberCallback15B1 : public XorpCallback15 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1) const; XorpConstMemberCallback15B1(O* o, M m, BA1 ba1) : XorpCallback15(), _o(o), _m(m), _ba1(ba1) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument }; /** * @short Callback object for const safe member methods with 15 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback15B1 : public XorpConstMemberCallback15B1, public SafeCallbackBase { typedef typename XorpConstMemberCallback15B1::M M; XorpConstSafeMemberCallback15B1(O* o, M m, BA1 ba1) : XorpConstMemberCallback15B1(o, m, ba1), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback15B1() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { if (valid()) { R r = XorpConstMemberCallback15B1::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); return r; } } }; /** * @short Callback object for void const safe member methods with 15 dispatch time * arguments and 1 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback15B1 : public XorpConstMemberCallback15B1, public SafeCallbackBase { typedef typename XorpConstMemberCallback15B1::M M; XorpConstSafeMemberCallback15B1(O* o, M m, BA1 ba1) : XorpConstMemberCallback15B1(o, m, ba1), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback15B1() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { if (valid()) { XorpConstMemberCallback15B1::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); } } }; template struct XorpConstMemberCallbackFactory15B1 { static XorpConstMemberCallback15B1* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1) const, BA1 ba1) { return new XorpConstSafeMemberCallback15B1(o, p, ba1); } }; template struct XorpConstMemberCallbackFactory15B1 { static XorpConstMemberCallback15B1* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1) const, BA1 ba1) { return new XorpConstMemberCallback15B1(o, p, ba1); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 15 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback15::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1) const, BA1 ba1) { return XorpConstMemberCallbackFactory15B1::True>::make(o, p, ba1); } /** * Factory function that creates a callback object targetted at a * const member function with 15 dispatch time arguments and 1 bound arguments. */ template typename XorpCallback15::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1) const, BA1 ba1) { return XorpConstMemberCallbackFactory15B1::True>::make(&o, p, ba1); } /** * @short Callback object for functions with 15 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpFunctionCallback15B2 : public XorpCallback15 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2); XorpFunctionCallback15B2(F f, BA1 ba1, BA2 ba2) : XorpCallback15(), _f(f), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1, _ba2); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; }; /** * @short Callback object for void functions with 15 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpFunctionCallback15B2 : public XorpCallback15 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2); XorpFunctionCallback15B2(F f, BA1 ba1, BA2 ba2) : XorpCallback15(), _f(f), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1, _ba2); } protected: F _f; BA1 _ba1; BA2 _ba2; }; /** * Factory function that creates a callback object targetted at a * function with 15 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback15::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2), BA1 ba1, BA2 ba2) { return typename XorpCallback15::RefPtr(new XorpFunctionCallback15B2(f, ba1, ba2)); } /** * @short Callback object for member methods with 15 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpMemberCallback15B2 : public XorpCallback15 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2) ; XorpMemberCallback15B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback15(), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1, _ba2); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for void member methods with 15 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpMemberCallback15B2 : public XorpCallback15 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2) ; XorpMemberCallback15B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback15(), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1, _ba2); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for safe member methods with 15 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpSafeMemberCallback15B2 : public XorpMemberCallback15B2, public SafeCallbackBase { typedef typename XorpMemberCallback15B2::M M; XorpSafeMemberCallback15B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpMemberCallback15B2(o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpSafeMemberCallback15B2() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { if (valid()) { R r = XorpMemberCallback15B2::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); return r; } } }; /** * @short Callback object for void safe member methods with 15 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpSafeMemberCallback15B2 : public XorpMemberCallback15B2, public SafeCallbackBase { typedef typename XorpMemberCallback15B2::M M; XorpSafeMemberCallback15B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpMemberCallback15B2(o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpSafeMemberCallback15B2() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { if (valid()) { XorpMemberCallback15B2::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); } } }; template struct XorpMemberCallbackFactory15B2 { static XorpMemberCallback15B2* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2), BA1 ba1, BA2 ba2) { return new XorpSafeMemberCallback15B2(o, p, ba1, ba2); } }; template struct XorpMemberCallbackFactory15B2 { static XorpMemberCallback15B2* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2), BA1 ba1, BA2 ba2) { return new XorpMemberCallback15B2(o, p, ba1, ba2); }; }; /** * Factory function that creates a callback object targetted at a * member function with 15 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback15::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2), BA1 ba1, BA2 ba2) { return XorpMemberCallbackFactory15B2::True>::make(o, p, ba1, ba2); } /** * Factory function that creates a callback object targetted at a * member function with 15 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback15::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2), BA1 ba1, BA2 ba2) { return XorpMemberCallbackFactory15B2::True>::make(&o, p, ba1, ba2); } /** * @short Callback object for const member methods with 15 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstMemberCallback15B2 : public XorpCallback15 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2) const; XorpConstMemberCallback15B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback15(), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1, _ba2); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for void const member methods with 15 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstMemberCallback15B2 : public XorpCallback15 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2) const; XorpConstMemberCallback15B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpCallback15(), _o(o), _m(m), _ba1(ba1), _ba2(ba2) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1, _ba2); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument }; /** * @short Callback object for const safe member methods with 15 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback15B2 : public XorpConstMemberCallback15B2, public SafeCallbackBase { typedef typename XorpConstMemberCallback15B2::M M; XorpConstSafeMemberCallback15B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpConstMemberCallback15B2(o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback15B2() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { if (valid()) { R r = XorpConstMemberCallback15B2::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); return r; } } }; /** * @short Callback object for void const safe member methods with 15 dispatch time * arguments and 2 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback15B2 : public XorpConstMemberCallback15B2, public SafeCallbackBase { typedef typename XorpConstMemberCallback15B2::M M; XorpConstSafeMemberCallback15B2(O* o, M m, BA1 ba1, BA2 ba2) : XorpConstMemberCallback15B2(o, m, ba1, ba2), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback15B2() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { if (valid()) { XorpConstMemberCallback15B2::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); } } }; template struct XorpConstMemberCallbackFactory15B2 { static XorpConstMemberCallback15B2* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2) const, BA1 ba1, BA2 ba2) { return new XorpConstSafeMemberCallback15B2(o, p, ba1, ba2); } }; template struct XorpConstMemberCallbackFactory15B2 { static XorpConstMemberCallback15B2* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2) const, BA1 ba1, BA2 ba2) { return new XorpConstMemberCallback15B2(o, p, ba1, ba2); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 15 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback15::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2) const, BA1 ba1, BA2 ba2) { return XorpConstMemberCallbackFactory15B2::True>::make(o, p, ba1, ba2); } /** * Factory function that creates a callback object targetted at a * const member function with 15 dispatch time arguments and 2 bound arguments. */ template typename XorpCallback15::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2) const, BA1 ba1, BA2 ba2) { return XorpConstMemberCallbackFactory15B2::True>::make(&o, p, ba1, ba2); } /** * @short Callback object for functions with 15 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpFunctionCallback15B3 : public XorpCallback15 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3); XorpFunctionCallback15B3(F f, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback15(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1, _ba2, _ba3); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; }; /** * @short Callback object for void functions with 15 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpFunctionCallback15B3 : public XorpCallback15 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3); XorpFunctionCallback15B3(F f, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback15(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1, _ba2, _ba3); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; }; /** * Factory function that creates a callback object targetted at a * function with 15 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback15::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return typename XorpCallback15::RefPtr(new XorpFunctionCallback15B3(f, ba1, ba2, ba3)); } /** * @short Callback object for member methods with 15 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpMemberCallback15B3 : public XorpCallback15 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3) ; XorpMemberCallback15B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback15(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1, _ba2, _ba3); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for void member methods with 15 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpMemberCallback15B3 : public XorpCallback15 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3) ; XorpMemberCallback15B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback15(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1, _ba2, _ba3); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for safe member methods with 15 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpSafeMemberCallback15B3 : public XorpMemberCallback15B3, public SafeCallbackBase { typedef typename XorpMemberCallback15B3::M M; XorpSafeMemberCallback15B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpMemberCallback15B3(o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpSafeMemberCallback15B3() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { if (valid()) { R r = XorpMemberCallback15B3::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); return r; } } }; /** * @short Callback object for void safe member methods with 15 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpSafeMemberCallback15B3 : public XorpMemberCallback15B3, public SafeCallbackBase { typedef typename XorpMemberCallback15B3::M M; XorpSafeMemberCallback15B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpMemberCallback15B3(o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpSafeMemberCallback15B3() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { if (valid()) { XorpMemberCallback15B3::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); } } }; template struct XorpMemberCallbackFactory15B3 { static XorpMemberCallback15B3* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpSafeMemberCallback15B3(o, p, ba1, ba2, ba3); } }; template struct XorpMemberCallbackFactory15B3 { static XorpMemberCallback15B3* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpMemberCallback15B3(o, p, ba1, ba2, ba3); }; }; /** * Factory function that creates a callback object targetted at a * member function with 15 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback15::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return XorpMemberCallbackFactory15B3::True>::make(o, p, ba1, ba2, ba3); } /** * Factory function that creates a callback object targetted at a * member function with 15 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback15::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3), BA1 ba1, BA2 ba2, BA3 ba3) { return XorpMemberCallbackFactory15B3::True>::make(&o, p, ba1, ba2, ba3); } /** * @short Callback object for const member methods with 15 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstMemberCallback15B3 : public XorpCallback15 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3) const; XorpConstMemberCallback15B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback15(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1, _ba2, _ba3); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for void const member methods with 15 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstMemberCallback15B3 : public XorpCallback15 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3) const; XorpConstMemberCallback15B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpCallback15(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1, _ba2, _ba3); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument }; /** * @short Callback object for const safe member methods with 15 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback15B3 : public XorpConstMemberCallback15B3, public SafeCallbackBase { typedef typename XorpConstMemberCallback15B3::M M; XorpConstSafeMemberCallback15B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpConstMemberCallback15B3(o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback15B3() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { if (valid()) { R r = XorpConstMemberCallback15B3::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); return r; } } }; /** * @short Callback object for void const safe member methods with 15 dispatch time * arguments and 3 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback15B3 : public XorpConstMemberCallback15B3, public SafeCallbackBase { typedef typename XorpConstMemberCallback15B3::M M; XorpConstSafeMemberCallback15B3(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3) : XorpConstMemberCallback15B3(o, m, ba1, ba2, ba3), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback15B3() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { if (valid()) { XorpConstMemberCallback15B3::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); } } }; template struct XorpConstMemberCallbackFactory15B3 { static XorpConstMemberCallback15B3* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpConstSafeMemberCallback15B3(o, p, ba1, ba2, ba3); } }; template struct XorpConstMemberCallbackFactory15B3 { static XorpConstMemberCallback15B3* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return new XorpConstMemberCallback15B3(o, p, ba1, ba2, ba3); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 15 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback15::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return XorpConstMemberCallbackFactory15B3::True>::make(o, p, ba1, ba2, ba3); } /** * Factory function that creates a callback object targetted at a * const member function with 15 dispatch time arguments and 3 bound arguments. */ template typename XorpCallback15::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3) const, BA1 ba1, BA2 ba2, BA3 ba3) { return XorpConstMemberCallbackFactory15B3::True>::make(&o, p, ba1, ba2, ba3); } /** * @short Callback object for functions with 15 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpFunctionCallback15B4 : public XorpCallback15 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4); XorpFunctionCallback15B4(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback15(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1, _ba2, _ba3, _ba4); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; }; /** * @short Callback object for void functions with 15 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpFunctionCallback15B4 : public XorpCallback15 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4); XorpFunctionCallback15B4(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback15(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1, _ba2, _ba3, _ba4); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; }; /** * Factory function that creates a callback object targetted at a * function with 15 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback15::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return typename XorpCallback15::RefPtr(new XorpFunctionCallback15B4(f, ba1, ba2, ba3, ba4)); } /** * @short Callback object for member methods with 15 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpMemberCallback15B4 : public XorpCallback15 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4) ; XorpMemberCallback15B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback15(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1, _ba2, _ba3, _ba4); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for void member methods with 15 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpMemberCallback15B4 : public XorpCallback15 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4) ; XorpMemberCallback15B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback15(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1, _ba2, _ba3, _ba4); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for safe member methods with 15 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpSafeMemberCallback15B4 : public XorpMemberCallback15B4, public SafeCallbackBase { typedef typename XorpMemberCallback15B4::M M; XorpSafeMemberCallback15B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpMemberCallback15B4(o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpSafeMemberCallback15B4() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { if (valid()) { R r = XorpMemberCallback15B4::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); return r; } } }; /** * @short Callback object for void safe member methods with 15 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpSafeMemberCallback15B4 : public XorpMemberCallback15B4, public SafeCallbackBase { typedef typename XorpMemberCallback15B4::M M; XorpSafeMemberCallback15B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpMemberCallback15B4(o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpSafeMemberCallback15B4() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { if (valid()) { XorpMemberCallback15B4::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); } } }; template struct XorpMemberCallbackFactory15B4 { static XorpMemberCallback15B4* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpSafeMemberCallback15B4(o, p, ba1, ba2, ba3, ba4); } }; template struct XorpMemberCallbackFactory15B4 { static XorpMemberCallback15B4* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpMemberCallback15B4(o, p, ba1, ba2, ba3, ba4); }; }; /** * Factory function that creates a callback object targetted at a * member function with 15 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback15::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpMemberCallbackFactory15B4::True>::make(o, p, ba1, ba2, ba3, ba4); } /** * Factory function that creates a callback object targetted at a * member function with 15 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback15::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpMemberCallbackFactory15B4::True>::make(&o, p, ba1, ba2, ba3, ba4); } /** * @short Callback object for const member methods with 15 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstMemberCallback15B4 : public XorpCallback15 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4) const; XorpConstMemberCallback15B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback15(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1, _ba2, _ba3, _ba4); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for void const member methods with 15 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstMemberCallback15B4 : public XorpCallback15 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4) const; XorpConstMemberCallback15B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpCallback15(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1, _ba2, _ba3, _ba4); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument }; /** * @short Callback object for const safe member methods with 15 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback15B4 : public XorpConstMemberCallback15B4, public SafeCallbackBase { typedef typename XorpConstMemberCallback15B4::M M; XorpConstSafeMemberCallback15B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpConstMemberCallback15B4(o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback15B4() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { if (valid()) { R r = XorpConstMemberCallback15B4::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); return r; } } }; /** * @short Callback object for void const safe member methods with 15 dispatch time * arguments and 4 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback15B4 : public XorpConstMemberCallback15B4, public SafeCallbackBase { typedef typename XorpConstMemberCallback15B4::M M; XorpConstSafeMemberCallback15B4(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) : XorpConstMemberCallback15B4(o, m, ba1, ba2, ba3, ba4), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback15B4() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { if (valid()) { XorpConstMemberCallback15B4::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); } } }; template struct XorpConstMemberCallbackFactory15B4 { static XorpConstMemberCallback15B4* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpConstSafeMemberCallback15B4(o, p, ba1, ba2, ba3, ba4); } }; template struct XorpConstMemberCallbackFactory15B4 { static XorpConstMemberCallback15B4* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return new XorpConstMemberCallback15B4(o, p, ba1, ba2, ba3, ba4); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 15 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback15::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpConstMemberCallbackFactory15B4::True>::make(o, p, ba1, ba2, ba3, ba4); } /** * Factory function that creates a callback object targetted at a * const member function with 15 dispatch time arguments and 4 bound arguments. */ template typename XorpCallback15::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4) { return XorpConstMemberCallbackFactory15B4::True>::make(&o, p, ba1, ba2, ba3, ba4); } /** * @short Callback object for functions with 15 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpFunctionCallback15B5 : public XorpCallback15 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4, BA5); XorpFunctionCallback15B5(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback15(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1, _ba2, _ba3, _ba4, _ba5); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; }; /** * @short Callback object for void functions with 15 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpFunctionCallback15B5 : public XorpCallback15 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4, BA5); XorpFunctionCallback15B5(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback15(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1, _ba2, _ba3, _ba4, _ba5); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; }; /** * Factory function that creates a callback object targetted at a * function with 15 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback15::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return typename XorpCallback15::RefPtr(new XorpFunctionCallback15B5(f, ba1, ba2, ba3, ba4, ba5)); } /** * @short Callback object for member methods with 15 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpMemberCallback15B5 : public XorpCallback15 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4, BA5) ; XorpMemberCallback15B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback15(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1, _ba2, _ba3, _ba4, _ba5); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for void member methods with 15 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpMemberCallback15B5 : public XorpCallback15 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4, BA5) ; XorpMemberCallback15B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback15(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1, _ba2, _ba3, _ba4, _ba5); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for safe member methods with 15 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpSafeMemberCallback15B5 : public XorpMemberCallback15B5, public SafeCallbackBase { typedef typename XorpMemberCallback15B5::M M; XorpSafeMemberCallback15B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpMemberCallback15B5(o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpSafeMemberCallback15B5() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { if (valid()) { R r = XorpMemberCallback15B5::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); return r; } } }; /** * @short Callback object for void safe member methods with 15 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpSafeMemberCallback15B5 : public XorpMemberCallback15B5, public SafeCallbackBase { typedef typename XorpMemberCallback15B5::M M; XorpSafeMemberCallback15B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpMemberCallback15B5(o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpSafeMemberCallback15B5() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { if (valid()) { XorpMemberCallback15B5::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); } } }; template struct XorpMemberCallbackFactory15B5 { static XorpMemberCallback15B5* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpSafeMemberCallback15B5(o, p, ba1, ba2, ba3, ba4, ba5); } }; template struct XorpMemberCallbackFactory15B5 { static XorpMemberCallback15B5* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpMemberCallback15B5(o, p, ba1, ba2, ba3, ba4, ba5); }; }; /** * Factory function that creates a callback object targetted at a * member function with 15 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback15::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpMemberCallbackFactory15B5::True>::make(o, p, ba1, ba2, ba3, ba4, ba5); } /** * Factory function that creates a callback object targetted at a * member function with 15 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback15::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4, BA5), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpMemberCallbackFactory15B5::True>::make(&o, p, ba1, ba2, ba3, ba4, ba5); } /** * @short Callback object for const member methods with 15 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstMemberCallback15B5 : public XorpCallback15 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4, BA5) const; XorpConstMemberCallback15B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback15(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1, _ba2, _ba3, _ba4, _ba5); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for void const member methods with 15 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstMemberCallback15B5 : public XorpCallback15 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4, BA5) const; XorpConstMemberCallback15B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpCallback15(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1, _ba2, _ba3, _ba4, _ba5); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument }; /** * @short Callback object for const safe member methods with 15 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback15B5 : public XorpConstMemberCallback15B5, public SafeCallbackBase { typedef typename XorpConstMemberCallback15B5::M M; XorpConstSafeMemberCallback15B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpConstMemberCallback15B5(o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback15B5() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { if (valid()) { R r = XorpConstMemberCallback15B5::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); return r; } } }; /** * @short Callback object for void const safe member methods with 15 dispatch time * arguments and 5 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback15B5 : public XorpConstMemberCallback15B5, public SafeCallbackBase { typedef typename XorpConstMemberCallback15B5::M M; XorpConstSafeMemberCallback15B5(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) : XorpConstMemberCallback15B5(o, m, ba1, ba2, ba3, ba4, ba5), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback15B5() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { if (valid()) { XorpConstMemberCallback15B5::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); } } }; template struct XorpConstMemberCallbackFactory15B5 { static XorpConstMemberCallback15B5* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpConstSafeMemberCallback15B5(o, p, ba1, ba2, ba3, ba4, ba5); } }; template struct XorpConstMemberCallbackFactory15B5 { static XorpConstMemberCallback15B5* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return new XorpConstMemberCallback15B5(o, p, ba1, ba2, ba3, ba4, ba5); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 15 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback15::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpConstMemberCallbackFactory15B5::True>::make(o, p, ba1, ba2, ba3, ba4, ba5); } /** * Factory function that creates a callback object targetted at a * const member function with 15 dispatch time arguments and 5 bound arguments. */ template typename XorpCallback15::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4, BA5) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5) { return XorpConstMemberCallbackFactory15B5::True>::make(&o, p, ba1, ba2, ba3, ba4, ba5); } /** * @short Callback object for functions with 15 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpFunctionCallback15B6 : public XorpCallback15 { typedef R (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4, BA5, BA6); XorpFunctionCallback15B6(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback15(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { R r = (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); return r; } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; BA6 _ba6; }; /** * @short Callback object for void functions with 15 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpFunctionCallback15B6 : public XorpCallback15 { typedef void (*F)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4, BA5, BA6); XorpFunctionCallback15B6(F f, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback15(), _f(f), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { (*_f)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); } protected: F _f; BA1 _ba1; BA2 _ba2; BA3 _ba3; BA4 _ba4; BA5 _ba5; BA6 _ba6; }; /** * Factory function that creates a callback object targetted at a * function with 15 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback15::RefPtr callback(R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return typename XorpCallback15::RefPtr(new XorpFunctionCallback15B6(f, ba1, ba2, ba3, ba4, ba5, ba6)); } /** * @short Callback object for member methods with 15 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpMemberCallback15B6 : public XorpCallback15 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4, BA5, BA6) ; XorpMemberCallback15B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback15(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for void member methods with 15 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpMemberCallback15B6 : public XorpCallback15 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4, BA5, BA6) ; XorpMemberCallback15B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback15(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for safe member methods with 15 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpSafeMemberCallback15B6 : public XorpMemberCallback15B6, public SafeCallbackBase { typedef typename XorpMemberCallback15B6::M M; XorpSafeMemberCallback15B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpMemberCallback15B6(o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpSafeMemberCallback15B6() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { if (valid()) { R r = XorpMemberCallback15B6::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); return r; } } }; /** * @short Callback object for void safe member methods with 15 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpSafeMemberCallback15B6 : public XorpMemberCallback15B6, public SafeCallbackBase { typedef typename XorpMemberCallback15B6::M M; XorpSafeMemberCallback15B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpMemberCallback15B6(o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpSafeMemberCallback15B6() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { if (valid()) { XorpMemberCallback15B6::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); } } }; template struct XorpMemberCallbackFactory15B6 { static XorpMemberCallback15B6* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpSafeMemberCallback15B6(o, p, ba1, ba2, ba3, ba4, ba5, ba6); } }; template struct XorpMemberCallbackFactory15B6 { static XorpMemberCallback15B6* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpMemberCallback15B6(o, p, ba1, ba2, ba3, ba4, ba5, ba6); }; }; /** * Factory function that creates a callback object targetted at a * member function with 15 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback15::RefPtr callback( O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpMemberCallbackFactory15B6::True>::make(o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * Factory function that creates a callback object targetted at a * member function with 15 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback15::RefPtr callback( O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4, BA5, BA6), BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpMemberCallbackFactory15B6::True>::make(&o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * @short Callback object for const member methods with 15 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstMemberCallback15B6 : public XorpCallback15 { typedef R (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4, BA5, BA6) const; XorpConstMemberCallback15B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback15(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { R r = ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); return r; } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for void const member methods with 15 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstMemberCallback15B6 : public XorpCallback15 { typedef void (O::*M)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4, BA5, BA6) const; XorpConstMemberCallback15B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpCallback15(), _o(o), _m(m), _ba1(ba1), _ba2(ba2), _ba3(ba3), _ba4(ba4), _ba5(ba5), _ba6(ba6) {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { ((*_o).*_m)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, _ba1, _ba2, _ba3, _ba4, _ba5, _ba6); } protected: O* _o; // Callback's target object M _m; // Callback's target method BA1 _ba1; // Bound argument BA2 _ba2; // Bound argument BA3 _ba3; // Bound argument BA4 _ba4; // Bound argument BA5 _ba5; // Bound argument BA6 _ba6; // Bound argument }; /** * @short Callback object for const safe member methods with 15 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback15B6 : public XorpConstMemberCallback15B6, public SafeCallbackBase { typedef typename XorpConstMemberCallback15B6::M M; XorpConstSafeMemberCallback15B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpConstMemberCallback15B6(o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback15B6() {} R dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { if (valid()) { R r = XorpConstMemberCallback15B6::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); return r; } } }; /** * @short Callback object for void const safe member methods with 15 dispatch time * arguments and 6 bound (stored) arguments. */ template struct XorpConstSafeMemberCallback15B6 : public XorpConstMemberCallback15B6, public SafeCallbackBase { typedef typename XorpConstMemberCallback15B6::M M; XorpConstSafeMemberCallback15B6(O* o, M m, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) : XorpConstMemberCallback15B6(o, m, ba1, ba2, ba3, ba4, ba5, ba6), SafeCallbackBase(o) {} ~XorpConstSafeMemberCallback15B6() {} void dispatch(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15) { if (valid()) { XorpConstMemberCallback15B6::dispatch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15); } } }; template struct XorpConstMemberCallbackFactory15B6 { static XorpConstMemberCallback15B6* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpConstSafeMemberCallback15B6(o, p, ba1, ba2, ba3, ba4, ba5, ba6); } }; template struct XorpConstMemberCallbackFactory15B6 { static XorpConstMemberCallback15B6* make(O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return new XorpConstMemberCallback15B6(o, p, ba1, ba2, ba3, ba4, ba5, ba6); }; }; /** * Factory function that creates a callback object targetted at a * const member function with 15 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback15::RefPtr callback( const O* o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpConstMemberCallbackFactory15B6::True>::make(o, p, ba1, ba2, ba3, ba4, ba5, ba6); } /** * Factory function that creates a callback object targetted at a * const member function with 15 dispatch time arguments and 6 bound arguments. */ template typename XorpCallback15::RefPtr callback( const O& o, R (O::*p)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, BA1, BA2, BA3, BA4, BA5, BA6) const, BA1 ba1, BA2 ba2, BA3 ba3, BA4 ba4, BA5 ba5, BA6 ba6) { return XorpConstMemberCallbackFactory15B6::True>::make(&o, p, ba1, ba2, ba3, ba4, ba5, ba6); } #endif /* __XORP_CALLBACK_HH__ */ xorp/libxorp/tst_git_md5sum.txt0000644000076400007640000000000711703425405017043 0ustar greearbgreearb510d56fxorp/libxorp/nexthop.hh0000664000076400007640000002050411540224227015345 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/libxorp/nexthop.hh,v 1.11 2008/10/02 21:57:32 bms Exp $ #ifndef __LIBXORP_NEXTHOP_HH__ #define __LIBXORP_NEXTHOP_HH__ #include "xorp.h" #include "ipv4.hh" #include "ipv6.hh" #include "ipvx.hh" // NextHop is a generic next hop object. // PeerNextHop is for next hops that are local peers. // EncapsNextHop is for "next hops" that are non-local, and require // encapsulation to reach. Eg. PIM Register Encaps. // ExternalNextHop An IP nexthop that is not an intermediate neighbor. // DiscardNextHop is a discard interface. // UnreachableNextHop is an unreachable interface. // // there will probably be more needed at some point #define GENERIC_NEXTHOP 0 #define PEER_NEXTHOP 1 #define ENCAPS_NEXTHOP 2 #define EXTERNAL_NEXTHOP 3 #define DISCARD_NEXTHOP 4 #define UNREACHABLE_NEXTHOP 5 /** * @short Generic class for next-hop information. * * NextHop is the generic class for holding information about routing * next hops. NextHops can be of many types, including immediate * neighbors, remote routers (with IBGP), discard or unreachable interfaces * encapsulation endpoints, etc. NextHop itself doesn't really do * anything useful, except to provide a generic handle for the * specialized subclasses. */ class NextHop { public: /** * Default constructor */ NextHop() {} /** * Destructor */ virtual ~NextHop() {} /** * Get the nexthop type. * * @return the type of the nexthop. One of:
   GENERIC_NEXTHOP	0
   PEER_NEXTHOP		1
   ENCAPS_NEXTHOP	2
   EXTERNAL_NEXTHOP	3
   DISCARD_NEXTHOP	4
   UNREACHABLE_NEXTHOP	5
*/ virtual int type() = 0; /** * Convert this nexthop from binary form to presentation format. * * @return C++ string with the human-readable ASCII representation * of the nexthop. */ virtual string str() const = 0; }; /** * @short Template class for nexthop information. * * The information contained is the nexthop address. */ template class IPNextHop : public NextHop { public: /** * Constructor from an address. * * @param from_ipaddr @ref IPv4 or @ref IPv6 or @ref IPvX address * to initialize nexthop. */ IPNextHop(const A &from_ipaddr); #ifdef XORP_USE_USTL IPNextHop() { } #endif /** * Get the address of the nexthop. * * @return the address of the nexthop. */ const A& addr() const { return _addr; } protected: A _addr; }; typedef IPNextHop IPv4NextHop; typedef IPNextHop IPv6NextHop; typedef IPNextHop IPvXNextHop; /** * @short A nexthop that is an immediate neighbor. * * Specialization of @ref IPNextHop for gateways that are the immediate * neighbors of this router. Most IGP nexthops should be PeerNextHops. */ template class IPPeerNextHop : public IPNextHop
{ public: /** * Constructor from an address. * * @param ipv4 @ref IPv4 or @ref IPv6 or @ref IPvX address * to initialize nexthop. */ IPPeerNextHop(const A &from_addr); #ifdef XORP_USE_USTL IPPeerNextHop() { } #endif /** * Get the type of the nexthop. * * @return the nexthop type. In this case, it is PEER_NEXTHOP. */ int type() { return PEER_NEXTHOP; } /** * Convert this nexthop from binary form to presentation format. * * @return C++ string with the human-readable ASCII representation * of the nexthop. */ string str() const; private: }; typedef IPPeerNextHop IPv4PeerNextHop; typedef IPPeerNextHop IPv6PeerNextHop; typedef IPPeerNextHop IPvXPeerNextHop; /** * @short An IP nexthop that is an encapsulation tunnel. * * Specialization of @ref IPNextHop for gateways that are encapsulation * tunnels. */ template class IPEncapsNextHop : public IPNextHop { public: /** * Constructor from an address. * * @param from_addr @ref IPv4 or @ref IPv6 or @ref IPvX address * to initialize nexthop. */ IPEncapsNextHop(const A &from_addr); /** * Get the type of the nexthop. * * @return the nexthop type. In this case, it is ENCAPS_NEXTHOP. */ int type() { return ENCAPS_NEXTHOP; } /** * Convert this nexthop from binary form to presentation format. * * @return C++ string with the human-readable ASCII representation * of the nexthop. */ string str() const; private: //_cached_peer is the cached copy of the local peer we send the //encapsulated packet to. IPPeerNextHop *_cached_peer; }; typedef IPEncapsNextHop IPv4EncapsNextHop; typedef IPEncapsNextHop IPv6EncapsNextHop; typedef IPEncapsNextHop IPvXEncapsNextHop; /** * @short An IP nexthop that is not an intermediate neighbor. * * The nexthop that is a regular router's address, but the router * is not one of our immediate neighbors. * * Specialization of @ref IPNextHop for a regular router's address, but * the router is not one of our immediate neighbors. The normal case * when this will happen is with IBGP, where the nexthop is either the * exit router from the AS, or the entry router to the next AS. */ template class IPExternalNextHop : public IPNextHop { public: /** * Constructor from an address. * * @param from_addr @ref IPv4 or @ref IPv6 or @ref IPvX address * to initialize nexthop. */ IPExternalNextHop(const A &from_addr); #ifdef XORP_USE_USTL IPExternalNextHop() { } #endif /** * Get the type of the nexthop. * * @return the nexthop type. In this case, it is EXTERNAL_NEXTHOP. */ int type() { return EXTERNAL_NEXTHOP; } /** * Convert this nexthop from binary form to presentation format. * * @return C++ string with the human-readable ASCII representation * of the nexthop. */ string str() const; private: }; typedef IPExternalNextHop IPv4ExternalNextHop; typedef IPExternalNextHop IPv6ExternalNextHop; typedef IPExternalNextHop IPvXExternalNextHop; /** * @short A nexthop that is the discard interface. * * Specialization of @ref NextHop for blackholing traffic efficiently. */ class DiscardNextHop : public NextHop { public: /** * Default constructor */ DiscardNextHop(); /** * Get the type of the nexthop. * * @return the nexthop type. In this case, it is DISCARD_NEXTHOP. */ int type() { return DISCARD_NEXTHOP; } /** * Convert this nexthop from binary form to presentation format. * * @return C++ string with the human-readable ASCII representation * of the nexthop. */ string str() const; private: }; /** * @short A nexthop that is the unreachable interface. * * Specialization of @ref NextHop for adding routing entries that return * ICMP destination unreachable messages. */ class UnreachableNextHop : public NextHop { public: /** * Default constructor */ UnreachableNextHop(); /** * Get the type of the nexthop. * * @return the nexthop type. In this case, it is UNREACHABLE_NEXTHOP. */ int type() { return UNREACHABLE_NEXTHOP; } /** * Convert this nexthop from binary form to presentation format. * * @return C++ string with the human-readable ASCII representation * of the nexthop. */ string str() const; private: }; #endif // __LIBXORP_NEXTHOP_HH__ xorp/libxorp/build_info.hh0000664000076400007640000000254611703345405016003 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2012 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // #ifndef __XORP_BUILD_INFO_INC__ #define __XORP_BUILD_INFO_INC__ class BuildInfo { public: /** As in: 1.8.5-WIP */ #define DEFSTR1(a) #a #define DEFSTR(a) DEFSTR1(a) static const char* getXorpVersion() { return DEFSTR(XORP_VERSION); } #ifdef XORP_BUILDINFO /** git md5sum for HEAD */ static const char* getGitVersion(); /** Last 3 git change logs */ static const char* getGitLog(); static const char* getShortBuildDate(); static const char* getBuildDate(); static const char* getBuilder(); static const char* getBuildMachine(); #endif }; #endif xorp/libxorp/timespent.hh0000664000076400007640000000637211540225530015675 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/libxorp/timespent.hh,v 1.15 2008/10/02 21:57:35 bms Exp $ #ifndef __LIBXORP_TIMESPENT_HH__ #define __LIBXORP_TIMESPENT_HH__ #include "libxorp/timeval.hh" #include "libxorp/timer.hh" static const int TIMESPENT_LIMIT = 10; // Time allowed in seconds. /** * @short (Debugging) Used to find code that has taken too long to execute. * * It is expected that this class will not be used directly but via * the macros below. Thus allowing file, function and line number * information to be captured. */ class TimeSpent { public: TimeSpent(const char *function, const char *file, int line, int limit) : _function(function), _file(file), _line(line), _limit(TimeVal(limit,0)) { TimerList::system_gettimeofday(&_start); } /** * @param delta the time that has passed. * @return true if the alloted time has been exceeded. */ bool overlimit(TimeVal& delta) { TimeVal now; TimerList::system_gettimeofday(&now); delta = now - _start; return delta > _limit; } /** * @return true if the alloted time has been exceeded. */ bool overlimit() { TimeVal delta; return overlimit(delta); } /** * Has the alloted time been exceeded? If it has print a warning message. */ void check(const char *function, const char *file, int line) { TimeVal delta; UNUSED(function); UNUSED(file); UNUSED(line); if (overlimit(delta)) XLOG_WARNING("Function %s +%d %s took %s\n", function, line, file, delta.str().c_str()); } ~TimeSpent() { check(_function, _file, _line); } private: TimeVal _start; const char *_function; const char *_file; int _line; TimeVal _limit; }; #ifdef CHECK_TIME /** * To be placed in suspect method. */ #define TIMESPENT() TimeSpent _t(__FUNCTION__,__FILE__,__LINE__, \ TIMESPENT_LIMIT) /** * Verify that thus far into the method the time limit has not been exceeded. * * If the alloted time has been exceeded a warning message will be printed. */ #define TIMESPENT_CHECK() _t.check(__FUNCTION__, __FILE__, __LINE__) /** * A boolean that will return true if the alloted time has been exceeded. */ #define TIMESPENT_OVERLIMIT() _t.overlimit() #else // ! CHECK_TIME #define TIMESPENT() #define TIMESPENT_CHECK() #define TIMESPENT_OVERLIMIT() 0 #endif #endif // __LIBXORP_TIMESPENT_HH__ xorp/libxorp/utility.c0000664000076400007640000000527411421137511015212 0ustar greearbgreearb/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- */ /* vim:set sts=4 ts=8: */ /* * Copyright (c) 2001-2009 XORP, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License, Version * 2.1, June 1999 as published by the Free Software Foundation. * Redistribution and/or modification of this program under the terms of * any other version of the GNU Lesser General Public License is not * permitted. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, * see the GNU Lesser General Public License, Version 2.1, a copy of * which can be found in the XORP LICENSE.lgpl file. * * XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; * http://xorp.net */ /* * Misc. utilities. */ #include "libxorp_module.h" #include "libxorp/xorp.h" #include #include "utility.h" /* * Exported variables */ /* * Local constants definitions */ /* * Local structures, typedefs and macros */ /* * Local variables */ /* * Local functions prototypes */ /* * Various ctype(3) wrappers that work properly even if the value of the int * argument is not representable as an unsigned char and doesn't have the * value of EOF. */ int xorp_isalnum(int c) { return isascii(c) && isalnum(c); } int xorp_isalpha(int c) { return isascii(c) && isalpha(c); } /* * TODO: for now comment-out xorp_isblank(), because isblank(3) is introduced * with ISO C99, and may not always be available on the system. */ #if 0 int xorp_isblank(int c) { return isascii(c) && isblank(c); } #endif /* 0 */ int xorp_iscntrl(int c) { return isascii(c) && iscntrl(c); } int xorp_isdigit(int c) { return isascii(c) && isdigit(c); } int xorp_isgraph(int c) { return isascii(c) && isgraph(c); } int xorp_islower(int c) { return isascii(c) && islower(c); } int xorp_isprint(int c) { return isascii(c) && isprint(c); } int xorp_ispunct(int c) { return isascii(c) && ispunct(c); } int xorp_isspace(int c) { return isascii(c) && isspace(c); } int xorp_isupper(int c) { return isascii(c) && isupper(c); } int xorp_isxdigit(int c) { return isascii(c) && isxdigit(c); } int xorp_tolower(int c) { if (isascii(c)) return tolower(c); else return c; } int xorp_toupper(int c) { if (isascii(c)) return toupper(c); else return c; } /* * Function to return C-string representation of a boolean: "true" of "false". */ const char * bool_c_str(int v) { if (v) return ("true"); else return ("false"); } xorp/libxorp/win_io.h0000664000076400007640000000317511540224230014773 0ustar greearbgreearb/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- */ /* vim:set sts=4 ts=8: */ /* * Copyright (c) 2001-2011 XORP, Inc and Others * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License, Version * 2.1, June 1999 as published by the Free Software Foundation. * Redistribution and/or modification of this program under the terms of * any other version of the GNU Lesser General Public License is not * permitted. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, * see the GNU Lesser General Public License, Version 2.1, a copy of * which can be found in the XORP LICENSE.lgpl file. * * XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; * http://xorp.net */ /* * $XORP: xorp/libxorp/win_io.h,v 1.10 2008/10/02 21:57:37 bms Exp $ */ #ifndef __LIBXORP_WIN_IO_H__ #define __LIBXORP_WIN_IO_H__ #ifdef __cplusplus extern "C" { #endif #ifdef HOST_OS_WINDOWS int win_strerror_r(DWORD errnum, char *strerrbuf, size_t buflen); char *win_strerror(DWORD errnum); #define WINIO_ERROR_IOERROR (-1) /* An I/O error on the pipe */ #define WINIO_ERROR_HASINPUT (-2) /* Data is ready to be read */ #define WINIO_ERROR_DISCONNECT (-3) /* The pipe was disconnected */ ssize_t win_con_read(HANDLE h, void *buf, size_t bufsize); ssize_t win_pipe_read(HANDLE h, void *buf, size_t bufsize); #endif /* HOST_OS_WINDOWS */ #ifdef __cplusplus } #endif #endif /* __LIBXORP_WIN_IO_H__ */ xorp/libxorp/trie.hh0000664000076400007640000006704411540224230014627 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/libxorp/trie.hh,v 1.29 2008/10/02 21:57:36 bms Exp $ #ifndef __LIBXORP_TRIE_HH__ #define __LIBXORP_TRIE_HH__ #include "ipnet.hh" // Macros //#define VALIDATE_XORP_TRIE //#define DEBUG_LOGGING #include "xlog.h" #include "debug.h" #include "minitraits.hh" #ifndef XORP_USE_USTL #include #endif #define trie_debug_msg(x...) /* debug_msg(x) */ #define trie_debug_msg_indent(x) /* * This module implements a trie to support route lookups. * * The template should be invoked with two classes, the basetype "A" * for the search Key (which is a subnet, IPNet), and the Payload. */ /** * @short TrieNode definition * * TrieNode's are the elements of a Trie. * Each node is associated to a Key and possibly a Payload. * Nodes with a Payload ("full") can have 0, 1 or 2 children. * Nodes without a Payload ("empty") can only be internal nodes, * and MUST have 2 children (or they have no reason to exist). * * Children have a Key which is strictly contained in their * parent's Key -- more precisely, they are in either the left * or the right half of the parent's Key. The branch to which * a child is attached (left or right) is defined accordingly. */ template class TrieNode { public: typedef IPNet Key; typedef typename MiniTraits::NonConst PPayload; /** * Constructors */ TrieNode() : _up(0), _left(0), _right(0), _k(Key()), _p(0) {} TrieNode(const Key& key, const Payload& p, TrieNode* up = 0) : _up(up), _left(0), _right(0), _k(key), _p(new PPayload(p)) {} explicit TrieNode(const Key& key, TrieNode* up = 0) : _up(up), _left(0), _right(0), _k(key), _p(0) {} ~TrieNode() { if (_p) delete_payload(_p); } /** * add a node to a subtree * @return a pointer to the node. */ static TrieNode *insert(TrieNode **root, const Key& key, const Payload& p, bool& replaced); /** * erase current node, replumb. Returns the new root. */ TrieNode *erase(); /** * main search routine. Given a key, returns a node. */ TrieNode *find(const Key& key) ; const TrieNode *const_find(const Key& key) const { return const_cast(this)->find(key); } /** * aux search routine. * Given a key, returns a subtree contained in the key, irrespective * of the presence of a payload in the node. */ TrieNode *find_subtree(const Key &key); /** * Given a key, find the node with that key and a payload. * If the next doesn't exist or does not have a payload, find * the next node in the iterator sequence. XXX check the description. */ TrieNode* lower_bound(const Key &key); TrieNode* get_left() { return this->_left; } TrieNode* get_right() { return this->_right; } TrieNode* get_parent() { return this->_up; } bool has_payload() const { return _p != NULL; } const Payload &const_p() const { return *_p; } Payload &p() { return *_p; } void set_payload(const Payload& p) { if (_p) delete_payload(_p); _p = new PPayload(p); } const Key &k() const { return _k; } void print(int indent, const char *msg) const; string str() const; /** * helper function to delete an entire subtree (including the root). */ void delete_subtree() { if (_left) _left->delete_subtree(); if (_right) _right->delete_subtree(); delete this; /* and we are gone too */ } /** * debugging, validates a node by checking pointers and Key invariants. */ void validate(const TrieNode *parent) const { UNUSED(parent); #ifdef VALIDATE_XORP_TRIE if (_up != parent) { fprintf(stderr, "bad parent _up %x vs %x", (int)_up, (int)parent); abort(); } if (_up && _k.contains(_up->_k)) { fprintf(stderr, "bad subnet order"); abort(); } if (_p == NULL && (!_left || !_right)) { fprintf(stderr, "useless internal node"); abort(); } if (_left) _left->validate(this); if (_right) _right->validate(this); #endif } /** * @return the leftmost node under this node */ TrieNode * leftmost() { TrieNode *n = this; while (n->_left || n->_right) n = (n->_left ? n->_left : n->_right); return n; } /** * @return the boundaries ("lo" and "hi") of the largest range that * contains 'a' and maps to the same route entry. * * Algorithm: *
     *		n = find(a);
     * 		if we have no route (hence no default), provide a fake 0/0;
     *		set lo and hi to the boundaries of the current node.
     *
     * if n.is_a_leaf() we are done (results are the extremes of the entry)
     * Otherwise: we are in an intermediate node, and a can be in positions
     * 1..5 if the node has 2 children, or 1'..3' if it has 1 child.
     *
     *	n:		|---------------.----------------|
     *  a:                1    2        3      4     5
     *                       |--X--|         |--Y--|
     *
     *  a:                1'    2'        3'
     *                       |--X--|
     *
     * Behaviour is the following:
     *  case 1 and 1':	lo already set, hi = (lowest address in X)-1
     *  case 2 and 2': set n = X and repeat
     *  case 3: lo = (highest addr in X)+1, hi = (lowest addr in Y)-1
     *  case 3': lo = (highest addr in X)+1, hi is already set
     *  case 4: set n = Y and repeat
     *  case 5:	lo = (highest addr in Y)+1, hi is already set
     * 
*/ void find_bounds(const A& a, A &lo, A &hi) const { TrieNode def = TrieNode(); const TrieNode *n = const_find(Key(a, a.addr_bitlen())); if (n == NULL) { // create a fake default entry def._left = const_cast(this); def._right = NULL; n = &def; } lo = n->_k.masked_addr(); hi = n->_k.top_addr(); for (const TrieNode *prev = NULL; prev != n;) { prev = n; TrieNode *x = (n->_left ? n->_left : n->_right); if (x == NULL) break; if (a < x->_k.masked_addr()) { // case 1 and 1' hi = x->low(); --hi; } else if (a <= x->_k.top_addr()) { // case 2 and 2' n = x; // and continue } else if (n->_left == NULL || n->_right == NULL) { // case 3' lo = x->high(); ++lo; } else if (a < n->_right->_k.masked_addr()) { // case 3 lo = x->high(); ++lo; hi = n->_right->low(); --hi; } else if (a <= n->_right->_k.top_addr()) { // case 4: n = n->_right; // and continue } else { // case 5: lo = n->_right->high(); ++lo; } } } /** * @return the lowest address in a subtree which has a route. * Search starting from left or right until a full node is found. */ A low() const { const TrieNode *n = this; while (!(n->has_payload()) && (n->_left || n->_right)) n = (n->_left ? n->_left : n->_right); return n->_k.masked_addr(); } /** * @return the highest address in a subtree which has a route. * Search starting from right or left until a full node is found. */ A high() const { const TrieNode *n = this; while (!(n->has_payload()) && (n->_right || n->_left)) n = (n->_right ? n->_right : n->_left); return n->_k.top_addr(); } private: /* delete_payload is a separate method to allow specialization */ void delete_payload(Payload* p) { delete p; } void dump(const char *msg) const { UNUSED(msg); trie_debug_msg(" %s %s %s\n", msg, _k.str().c_str(), _p ? "PL" : "[]"); trie_debug_msg(" U %s\n", _up ? _up->_k.str().c_str() : "NULL"); trie_debug_msg(" L %s\n", _left ? _left->_k.str().c_str() : "NULL"); trie_debug_msg(" R %s\n", _right ? _right->_k.str().c_str() : "NULL"); } TrieNode *_up, *_left, *_right; Key _k; PPayload *_p; }; /** * Postorder Iterator on a trie. * * _cur points to the current object, _root contains the search key for * root of the subtree we want to scan. The iterator skips over empty * nodes, and visits the subtree in depth-first, left-to-right order. * The keys returned by this iterator are not sorted by prefix length. */ template class TriePostOrderIterator { public: typedef IPNet
Key; typedef TrieNode Node; /** * Constructors */ TriePostOrderIterator() {} /** * constructor for exact searches: both the current node and the search * key are taken from n, so the iterator will only loop once. */ explicit TriePostOrderIterator(Node *n) { _cur = n; if (n) _root = n->k(); } /** * construct for subtree scanning: the root key is set explicitly, * and the current node is set according to the search order. */ TriePostOrderIterator(Node *n, const Key &k) { _root = k; _cur = n; if (_cur) begin(); } /** * move to the starting position according to the visiting order */ TriePostOrderIterator * begin() { Node * n = _cur; while (n->get_parent() && _root.contains(n->get_parent()->k())) n = n->get_parent(); _cur = n->leftmost(); return this; } /** * Postfix increment * * Updates position of iterator in tree. * @return position of iterator before increment. */ TriePostOrderIterator operator ++(int) { // postfix TriePostOrderIterator x = *this; next(); return x; } /** * Prefix increment * * Updates position of iterator in tree. * @return position of iterator after increment. */ TriePostOrderIterator& operator ++() { // prefix next(); return *this; } Node *cur() const { return _cur; }; bool operator==(const TriePostOrderIterator & x) const { return (_cur == x._cur); } bool has_payload() const { return _cur->has_payload(); } Payload & payload() { return _cur->p(); }; const Key & key() const { return _cur->k(); }; private: bool node_is_left(Node * n) const; void next(); Node *_cur; Key _root; }; /** * Preorder Iterator on a trie. * * _cur points to the current object, _root contains the search key for * root of the subtree we want to scan. The iterator does preorder traversal, * that is, current node first, then left then right. This guarantees that * keys returned are sorted by prefix length. */ template class TriePreOrderIterator { public: typedef IPNet Key; typedef TrieNode Node; /** * Constructors */ TriePreOrderIterator() {} /** * constructor for exact searches: both the current node and the search * key are taken from n, so the iterator will only loop once. */ explicit TriePreOrderIterator(Node *n) { _cur = n; if (_cur) _root = n->k(); } /** * construct for subtree scanning: the root key is set explicitly, * and the current node is set according to the search order. */ TriePreOrderIterator(Node *n, const Key &k) { _root = k; _cur = n; if (_cur) begin(); } /** * move to the starting position according to the visiting order */ TriePreOrderIterator * begin() { while (!_stack.empty()) _stack.pop(); while (_cur->get_parent() && _root.contains(_cur->get_parent()->k())) _cur = _cur->get_parent(); _stack.push(_cur); next(); return this; } /** * Postfix increment * * Updates position of iterator in tree. * @return position of iterator before increment. */ TriePreOrderIterator operator ++(int) { // postfix TriePreOrderIterator x = *this; next(); return x; } /** * Prefix increment * * Updates position of iterator in tree. * @return position of iterator after increment. */ TriePreOrderIterator& operator ++() { // prefix next(); return *this; } Node *cur() const { return _cur; }; bool operator==(const TriePreOrderIterator & x) const { return (_cur == x._cur); } bool has_payload() const { return _cur->has_payload(); } Payload & payload() { return _cur->p(); }; const Key & key() const { return _cur->k(); }; private: bool node_is_left(Node * n) const; void next(); Node *_cur; Key _root; stack _stack; }; /** * The Trie itself * * The trie support insertion and deletion of Key,Payload pairs, * and lookup by Key (which can be an address or a subnet). * * Additional methods are supported to provide access via iterators. */ template > class Trie { public: typedef IPNet Key; typedef TrieNode Node; typedef __Iterator iterator; /** * stl map interface */ Trie() : _root(0), _payload_count(0) {} ~Trie() { delete_all_nodes(); } /** * insert a key,payload pair, returns an iterator * to the newly inserted node. * Prints a warning message if the new entry overwrites an * existing full node. */ iterator insert(const Key & net, const Payload& p) { bool replaced = false; Node *out = Node::insert(&_root, net, p, replaced); if (replaced) { fprintf(stderr, "overwriting a full node"); //XXX } else { _payload_count++; } return iterator(out); } /** * delete the node with the given key. */ void erase(const Key &k) { erase(find(k)); } /** * delete the node pointed by the iterator. */ void erase(iterator i) { if (_root && i.cur() && i.cur()->has_payload()) { _payload_count--; _root = const_cast(i.cur())->erase(); // XXX should invalidate i ? } } /** * Set root node associated with iterator to the root node of the * trie. Needed whilst trie iterators have concept of root nodes * find methods return iterators with root bound to key and * means they can never continue iteration beyond of root. * * @return iterator with non-restricted root node. */ iterator unbind_root(iterator i) const { return iterator(i.cur(), _root->k()); } /** * given a key, returns an iterator to the entry with the * longest matching prefix. */ iterator find(const Key &k) const { return iterator(_root->find(k)); } /** * given an address, returns an iterator to the entry with the * longest matching prefix. */ iterator find(const A& a) const { return find(Key(a, a.addr_bitlen())); } iterator lower_bound(const Key &k) const { #ifdef NOTDEF iterator i = lookup_node(k); if (i != end()) return i; #endif return iterator(_root->lower_bound(k)); } iterator begin() const { return iterator(_root, IPNet()); } const iterator end() const { return iterator(0); } void delete_all_nodes() { if (_root) _root->delete_subtree(); _root = NULL; _payload_count = 0; } /** * lookup a subnet, must return exact match if found, end() if not. * */ iterator lookup_node(const Key & k) const { Node *n = _root->find(k); return (n && n->k() == k) ? iterator(n) : end(); } /** * returns an iterator to the subtree rooted at or below * the key passed as parameter. */ iterator search_subtree(const Key &key) const { return iterator(_root->find_subtree(key), key); } /** * find_less_specific asks the question: if I were to add this * net to the trie, what would be its parent node? * net may or may not already be in the trie. * Implemented as a find() with a less specific key. */ iterator find_less_specific(const Key &key) const { // there are no less specific routes than the default route if (key.prefix_len() == 0) return end(); Key x(key.masked_addr(), key.prefix_len() - 1); return iterator(_root->find(x)); } /** * return the lower and higher address in the range that contains a * and would map to the same route. */ void find_bounds(const A& a, A &lo, A &hi) const { _root->find_bounds(a, lo, hi); } #if 0 // compatibility stuff, has to go /* * return the lower and higher address in the range that contains a * and would map to the same route. */ A find_lower_bound(const A a) const { A lo, hi; _root->find_bounds(a, lo, hi); return lo; } A find_higher_bound(const A a) const { A lo, hi; _root->find_bounds(a, lo, hi); return hi; } #endif // compatibility int route_count() const { return _payload_count; } void print() const; private: void validate() { if (_root) _root->validate(NULL); } Node *_root; int _payload_count; }; /** * add subnet/payload to the tree at *root. * * @return a pointer to the newly inserted node. */ template TrieNode * TrieNode::insert(TrieNode **root, const Key& x, const Payload& p, bool& replaced) { /* * Loop until done in the following: * * If *root == NULL, create a new TrieNode containing x and we are DONE. * Otherwise consider the possible cases of overlaps between the subnets * in *root (call it y) and x (+ indicates the middle of the interval): * * y = (*root) .|===+===| * * x 0 .|---+---| * x A |--| . . * x B . . |--| * x C . |-|. * x D . .|-| * x E |----------+----------| * x F |----------+-----------| * * case 0: Same subnet. Store payload if *root if empty, replace otherwise. * case A: allocate a new empty root, make old *root the right child, * make a new node with x the left child. DONE. * case B: allocate a new empty root, make old *root the left child, * make a new node with x the right child. DONE. * case C: repeat with root = &((*root)->left) * case D: repeat with root = &((*root)->right) * case E: *root = new node with x, old *root the right child, DONE. * case F: *root = new node with x, old *root the left child, DONE. * * In all case, when we exit the loop, newroot contains the new value to * be assigned to *root; */ TrieNode **oldroot = root; // do we need it ? TrieNode *newroot = NULL, *parent = NULL, *me = NULL; trie_debug_msg("++ insert %s\n", x.str().c_str()); for (;;) { newroot = *root; if (newroot == NULL) { me = newroot = new TrieNode(x, p, parent); break; } parent = newroot->_up; Key y = newroot->_k; if (x == y) { /* case 0 */ replaced = newroot->has_payload(); newroot->set_payload(p); me = newroot; break; } // boundaries of x and y, and their midpoints. A x_m = x.masked_addr() | ( ~(x.netmask()) >> 1 ); A y_m = y.masked_addr() | ( ~(y.netmask()) >> 1 ); A x_l = x.masked_addr(); A x_h = x.top_addr(); A y_l = y.masked_addr(); A y_h = y.top_addr(); if (x_h < y_l) { /* case A */ //trie_debug_msg("case A: |--x--| |--y--|\n"); Key k = Key::common_subnet(x, y); newroot = new TrieNode(k, parent); // create new root newroot->_right = *root; // old root goes right newroot->_right->_up = newroot; newroot->_left = me = new TrieNode(x, p, newroot); break; } else if (y_h < x_l) { /* case B */ //trie_debug_msg("case B: |--y--| |--x--|\n"); Key k = Key::common_subnet(x, y); newroot = new TrieNode(k, parent); // create new root newroot->_left = *root; newroot->_left->_up = newroot; newroot->_right = me = new TrieNode(x, p, newroot); break; } else if (x_l >= y_l && x_h <= y_m) { /* case C */ //trie_debug_msg("case C: |--x-.----|\n"); parent = *root; root = &(newroot->_left); } else if (x_l > y_m && x_h <= y_h) { /* case D */ //trie_debug_msg("case D: |----.-x--|\n"); parent = *root; root = &(newroot->_right); } else if (y_l > x_m && y_h <= x_h) { /* case E */ //trie_debug_msg("case E: |----.-Y--|\n"); newroot = me = new TrieNode(x, p, parent); newroot->_right = *root; newroot->_right->_up = newroot; break; } else if (y_l >= x_l && y_h <= x_m) { /* case F */ //trie_debug_msg("case F: |--Y-.----|\n"); newroot = me = new TrieNode(x, p, parent); newroot->_left = *root; newroot->_left->_up = newroot; break; } else abort(); // impossible case in TrieNode::insert() } *root = newroot; if (*oldroot) (*oldroot)->validate(NULL); // (*oldroot)->print(0, ""); return me; } /** * Remove this node, cleanup useless internal nodes. * * @return a pointer to the root of the trie. */ template TrieNode * TrieNode::erase() { TrieNode *me, *parent, *child; if (_p) { delete_payload(_p); _p = NULL; } trie_debug_msg("++ erase %s\n", this->_k.str().c_str()); /* * If the node ("me") exists, has no payload and at most one child, * then it is a useless internal node which needs to be removed by * linking the child to the parent. If the child is NULL, we need * to repeat the process up. */ for (me = this; me && me->_p == NULL && (me->_left == NULL || me->_right == NULL); ) { // me->dump("erase"); // debugging // Find parent and the one possible child (both can be NULL). parent = me->_up; child = me->_left ? me->_left : me->_right; if (child != NULL) // if the child exists, link it to child->_up = parent; // its new parent if (parent == NULL) // no parent, child becomes new root parent = child; else { // i have a parent, link my child to it (left or right) if (parent->_left == me) parent->_left = child; else parent->_right = child; } delete me; // nuke the node me = parent; } // now navigate up to find and return the new root of the trie for ( ; me && me->_up ; me = me->_up) ; return me; } /** * Finds the most specific entry in the subtree rooted at r * that contains the desired key and has a Payload */ template TrieNode * TrieNode::find(const Key &key) { TrieNode * cand = NULL; TrieNode * r = this; for ( ; r && r->_k.contains(key) ; ) { if (r->_p) cand = r; // we have a candidate. if (r->_left && r->_left->_k.contains(key)) r = r->_left; else // should check that right contains(key), but r = r->_right; // the loop condition will do it for us. } return cand; } /** * See the comment in the class definition. */ template TrieNode * TrieNode::lower_bound(const Key &key) { TrieNode * cand = NULL; TrieNode * r = this; //printf("lower bound: %s\n", key.str().c_str()); for ( ; r && r->_k.contains(key) ; ) { cand = r; // any node is good, irrespective of payload. if (r->_left && r->_left->_k.contains(key)) r = r->_left; else // should check that right contains(key), but r = r->_right; // the loop condition will do it for us. } if (cand == NULL) cand = this; if (cand->_k == key) { // we found an exact match if (cand->_p) { // we also have a payload, so we are done. // printf("exact match\n"); return cand; } else { // no payload, skip to the next (in postorder) // node in the entire tree (null Key as root) // printf("exact match on empty node - calling next\n"); TriePostOrderIterator iterator(cand, Key()); ++iterator; return iterator.cur(); } } // printf("no exact match\n"); // No exact match exists. // cand holds what would be the parent of the node, if it existed. while (cand != NULL) { // printf("cand = %s\n", cand->str().c_str()); if (cand->_left && (key < cand->_left->_k)) { return cand->_left->leftmost(); } if (cand->_right && (key < cand->_right->_k)) { return cand->_right->leftmost(); } cand = cand->_up; } return NULL; } /** * Finds the subtree of key. */ template TrieNode * TrieNode::find_subtree(const Key &key) { TrieNode *r = this; TrieNode *cand = r && key.contains(r->_k) ? r : NULL; for ( ; r && r->_k.contains(key) ; ) { if (key.contains(r->_k)) cand = r; // we have a candidate. if (r->_left && r->_left->_k.contains(key)) r = r->_left; else // should check that right contains(key), but r = r->_right; // the loop condition will do it for us. } return cand; } template void TrieNode::print(int indent, const char *msg) const { #ifdef DEBUG_LOGGING trie_debug_msg_indent(indent); if (this == NULL) { trie_debug_msg("%sNULL\n", msg); return; } trie_debug_msg("%skey: %s %s\n", msg, _k.str().c_str(), _p ? "PL" : "[]"); trie_debug_msg(" U: %s\n", _up ? _up->_k.str().c_str() : "NULL"); _left->print(indent+4, "L: "); _right->print(indent+4, "R: "); trie_debug_msg_indent(0); #endif /* DEBUG_LOGGING */ UNUSED(indent); UNUSED(msg); } template string TrieNode::str() const { string s; if (this == NULL) { s = "NULL"; return s; } s = c_format("key: %s %s\n", _k.str().c_str(), _p ? "PL" : "[]"); s += c_format(" U: %s\n", _up ? _up->_k.str().c_str() : "NULL"); return s; } template void Trie::print() const { //this is called print - it should NOT use debug_msg!!! printf("---- print trie ---\n"); // _root->print(0, ""); iterator ti; for (ti = begin() ; ti != end() ; ti++) printf("*** node: %-26s %s\n", ti.cur()->k().str().c_str(), ti.cur()->has_payload() ? "PL" : "[]"); printf("---------------\n"); } template bool TriePostOrderIterator::node_is_left(Node* n) const { return n->get_parent() && n == n->get_parent()->get_left(); } template void TriePostOrderIterator::next() { Node * n = _cur; do { if (n->get_parent() == NULL) { _cur = NULL; return; // cannot backtrack, finished } bool was_left_child = node_is_left(n); n = n->get_parent(); // backtrack one level, then explore the leftmost path // on the right branch if not done already. if (was_left_child && n->get_right()) { n = n->get_right()->leftmost(); } if (_root.contains(n->k()) == false) { _cur = NULL; return; } } while (n->has_payload() == false); // found a good node. _cur = n; } template void TriePreOrderIterator::next() { if (_stack.empty()) { _cur = NULL; return; } do { _cur = _stack.top(); _stack.pop(); if( _cur->get_right( ) != NULL ) _stack.push(_cur->get_right()); if( _cur->get_left() != NULL ) _stack.push(_cur->get_left()); } while (_cur->has_payload() == false); // found a good node. } #endif // __LIBXORP_TRIE_HH__ xorp/libxorp/service.cc0000664000076400007640000000733411540224227015314 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "libxorp_module.h" #include "xorp.h" #include "service.hh" // ---------------------------------------------------------------------------- // ServiceStatus related const char* service_status_name(ServiceStatus s) { switch (s) { case SERVICE_READY: return "Ready"; case SERVICE_STARTING: return "Starting"; case SERVICE_RUNNING: return "Running"; case SERVICE_PAUSING: return "Pausing"; case SERVICE_PAUSED: return "Paused"; case SERVICE_RESUMING: return "Resuming"; case SERVICE_SHUTTING_DOWN: return "Shutting down"; case SERVICE_SHUTDOWN: return "Shutdown"; case SERVICE_FAILED: return "Failed"; case SERVICE_ALL: return "All"; // Invalid } return "Unknown"; } // ---------------------------------------------------------------------------- // ServiceBase implmentation ServiceBase::ServiceBase(const string& n) : _name(n), _status(SERVICE_READY), _observer(NULL) { } ServiceBase::~ServiceBase() { } int ServiceBase::reset() { return (XORP_ERROR); } int ServiceBase::pause() { return (XORP_ERROR); } int ServiceBase::resume() { return (XORP_ERROR); } const char* ServiceBase::status_name() const { return (service_status_name(_status)); } int ServiceBase::set_observer(ServiceChangeObserverBase* so) { if (_observer != NULL) return (XORP_ERROR); _observer = so; return (XORP_OK); } int ServiceBase::unset_observer(ServiceChangeObserverBase* so) { if (_observer != so) return (XORP_ERROR); _observer = NULL; return (XORP_OK); } void ServiceBase::set_status(ServiceStatus status, const string& note) { ServiceStatus ost = _status; _status = status; bool note_changed = (_note != note); _note = note; if ((_observer != NULL) && (ost != _status || note_changed)) _observer->status_change(this, ost, _status); } void ServiceBase::set_status(ServiceStatus status) { ServiceStatus ost = _status; _status = status; _note.clear(); if ((_observer != NULL) && (ost != _status)) _observer->status_change(this, ost, _status); } // ---------------------------------------------------------------------------- // ServiceChangeObserverBase ServiceChangeObserverBase::~ServiceChangeObserverBase() { } // ---------------------------------------------------------------------------- // ServiceFilteredChangeObserver ServiceFilteredChangeObserver::ServiceFilteredChangeObserver( ServiceChangeObserverBase* child, ServiceStatus from, ServiceStatus to ) : _child(child), _from_mask(from), _to_mask(to) { } void ServiceFilteredChangeObserver::status_change(ServiceBase* service, ServiceStatus old_status, ServiceStatus new_status) { if ((old_status & _from_mask) && (new_status & _to_mask)) _child->status_change(service, old_status, new_status); } xorp/libxorp/clock.cc0000664000076400007640000000371411631521216014744 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "libxorp_module.h" #include "xorp.h" #include "timeval.hh" #include "clock.hh" #ifdef __WIN32__ #include "mmsystem.h" #endif ClockBase::~ClockBase() { } SystemClock::SystemClock() { #ifdef __WIN32__ ms_time_res = -1; for (int i = 1; i<16; i++) { if (timeBeginPeriod(i) == TIMERR_NOERROR) { ms_time_res = i; break; } } #endif _tv = new TimeVal(); SystemClock::advance_time(); } SystemClock::~SystemClock() { #ifdef __WIN32__ if (ms_time_res >= 0) { timeEndPeriod(ms_time_res); } #endif delete _tv; } void SystemClock::advance_time() #if defined(HAVE_CLOCK_GETTIME) && defined(HAVE_CLOCK_MONOTONIC) { int error; struct timespec ts; error = ::clock_gettime(CLOCK_MONOTONIC, &ts); assert(error == 0); _tv->copy_in(ts); } #elif defined(HOST_OS_WINDOWS) { FILETIME ft; ::GetSystemTimeAsFileTime(&ft); _tv->copy_in(ft); } #else { struct timeval t; ::gettimeofday(&t, 0); _tv->copy_in(t); } #endif void SystemClock::current_time(TimeVal& tv) { tv = *_tv; } xorp/libxorp/eventloop.hh0000664000076400007640000003440511635757530015715 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __LIBXORP_EVENTLOOP_HH__ #define __LIBXORP_EVENTLOOP_HH__ #ifdef HAVE_SYS_TIME_H #include #endif #include "xorpfd.hh" #include "clock.hh" #include "timer.hh" #include "task.hh" #include "callback.hh" #include "ioevents.hh" #ifdef USE_WIN_DISPATCHER #include "win_dispatcher.hh" #else #include "selector.hh" #endif // The default signal handler logic will catch: // SIGTERM, SIGINT, SIGXFSZ, SIGXCPU // and set xorp_do_run to 0. Control loops can use this // to bail out and gracefully exit. extern int xorp_do_run; extern char xorp_sig_msg_buffer[64]; void setup_dflt_sighandlers(); void dflt_sig_handler(int signo); extern EnvTrace eloop_trace; /** * @short Event Loop. * * Co-ordinates interactions between a TimerList and a SelectorList * for Xorp processes. All XorpTimer and select operations should be * co-ordinated through this interface. */ class EventLoop : public NONCOPYABLE { public: /** * Constructor */ EventLoop(); /** * Destructor. */ virtual ~EventLoop(); /** * Invoke all pending callbacks relating to XorpTimer and file * descriptor activity. This function may block if there are no * selectors ready. It may block forever if there are no timers * pending. The @ref timers_pending method can be used to detect * whether there are timers pending, while the @ref events_pending * method can be used to detect whether there any events pending. * An event can be either timer or task. * The @ref descriptor_count method can be used to see if there are * any select'able file descriptors. * *
     EventLoop e;

     ...

     while(e.events_pending() || e.descriptor_count() > 0) {
         e.run();
     }

     * 
* * Non-xorp processes which use Xorp code should create a periodic * timer to prevent the run() function from blocking indefinitely * when there are no pending XorpTimer or SelectList objects. The * period of the timer will depend on the application's existing * needs. * *
     static bool wakeup_hook(int n) {
        static int count = 0;
	count += n;
	printf("count = %d\n", n);
     	return true;
     }

     int main() {
     	... // Program initialization

    	// Add a Xorp EventLoop
	EventLoop e;
	XorpTimer wakeywakey = e.new_periodic_ms(100, callback(wakeup_hook, 1));

	// Program's main loop
	for(;;) {
		... do what program does in its main loop ...
		e.run(); // process events
	}
     }
     * 
*/ void run(); void set_debug(bool v) { _is_debug = v; #ifndef HOST_OS_WINDOWS _selector_list.set_debug(v); #endif } bool is_debug() const { return (_is_debug); } /** * @return reference to the @ref TimerList used by the @ref * EventLoop instance. */ TimerList& timer_list() { return _timer_list; } #ifndef HOST_OS_WINDOWS /** * @return reference to the @ref SelectorList used by the @ref * EventLoop instance. * XXX: Deprecated. */ SelectorList& selector_list() { return _selector_list; } #endif /** * Add a new one-off timer to the EventLoop. * * @param when the absolute time when the timer expires. * @param ocb callback object that is invoked when timer expires. * @return a @ref XorpTimer object that must be assigned to remain * scheduled. */ XorpTimer new_oneoff_at(const TimeVal& when, const OneoffTimerCallback& ocb, int priority = XorpTask::PRIORITY_DEFAULT); /** * Add a new one-off timer to the EventLoop. * * @param wait the relative time when the timer expires. * @param ocb callback object that is invoked when timer expires. * @return a @ref XorpTimer object that must be assigned to remain * scheduled. */ XorpTimer new_oneoff_after(const TimeVal& wait, const OneoffTimerCallback& ocb, int priority = XorpTask::PRIORITY_DEFAULT); /** * Add a new one-off timer to the EventLoop. * * @param ms the relative time in milliseconds when the timer expires. * @param ocb callback object that is invoked when timer expires. * @return a @ref XorpTimer object that must be assigned to remain * scheduled. */ XorpTimer new_oneoff_after_ms(int ms, const OneoffTimerCallback& ocb, int priority = XorpTask::PRIORITY_DEFAULT); /** Remove timer from timer list. */ void remove_timer(XorpTimer& t); /** * Add periodic timer to the EventLoop. * * @param wait the period when the timer expires. * @param pcb user callback object that is invoked when timer expires. * If the callback returns false the periodic XorpTimer is unscheduled. * @return a @ref XorpTimer object that must be assigned to remain * scheduled. */ XorpTimer new_periodic(const TimeVal& wait, const PeriodicTimerCallback& pcb, int priority = XorpTask::PRIORITY_DEFAULT); /** * Add periodic timer to the EventLoop. * * @param ms the period in milliseconds when the timer expires. * @param pcb user callback object that is invoked when timer expires. * If the callback returns false the periodic XorpTimer is unscheduled. * @return a @ref XorpTimer object that must be assigned to remain * scheduled. */ XorpTimer new_periodic_ms(int ms, const PeriodicTimerCallback& pcb, int priority = XorpTask::PRIORITY_DEFAULT); /** * Add a flag setting timer to the EventLoop. * * @param when the absolute time when the timer expires. * @param flag_ptr pointer to a boolean variable that is to be set * when the timer expires. * @param to_value value to set the boolean variable to. Default value * is true. * @return a @ref XorpTimer object that must be assigned to remain * scheduled. */ XorpTimer set_flag_at(const TimeVal& when, bool* flag_ptr, bool to_value = true); /** * Add a flag setting timer to the EventLoop. * * @param wait the relative time when the timer expires. * @param flag_ptr pointer to a boolean variable that is to be set * when the timer expires. * @param to_value value to set the boolean variable to. Default value * is true. * @return a @ref XorpTimer object that must be assigned to remain * scheduled. */ XorpTimer set_flag_after(const TimeVal& wait, bool* flag_ptr, bool to_value = true); /** * Add a flag setting timer to the EventLoop. * * @param ms the relative time in millisecond when the timer expires. * @param flag_ptr pointer to a boolean variable that is set to * false when this function is called and will be set to true when * the @ref XorpTimer expires. * @param flag_ptr pointer to a boolean variable that is to be set * when the timer expires. * @param to_value value to set the boolean variable to. Default value * is true. * @return a @ref XorpTimer object that must be assigned to remain * scheduled. */ XorpTimer set_flag_after_ms(int ms, bool* flag_ptr, bool to_value = true); /** * Create a custom timer associated with the EventLoop. * * The @ref XorpTimer object created needs to be explicitly * scheduled with the available @ref XorpTimer methods. * * @param cb user callback object that is invoked when timer expires. * @return a @ref XorpTimer object that must be assigned to remain * scheduled. */ XorpTimer new_timer(const BasicTimerCallback& cb); /** * Create a new one-time task to be scheduled with the timers and * file handlers. * * @param cb callback object that is invoked when task is run. * @param priority the scheduling priority for the task. * @param scheduler_class the scheduling class within the priority level. * @return a @ref XorpTask object that must be assigned to remain * scheduled. */ XorpTask new_oneoff_task(const OneoffTaskCallback& cb, int priority = XorpTask::PRIORITY_DEFAULT, int weight = XorpTask::WEIGHT_DEFAULT); /** * Create a new repeated task to be scheduled with the timers and file * handlers. * * @param cb callback object that is invoked when task is run. * If the callback returns true, the task will continue to run, * otherwise it will be unscheduled. * @param priority the scheduling priority for the task. * @param scheduler_class the scheduling class within the priority level. * @return a @ref XorpTask object that must be assigned to remain * scheduled. */ XorpTask new_task(const RepeatedTaskCallback& cb, int priority = XorpTask::PRIORITY_DEFAULT, int weight = XorpTask::WEIGHT_DEFAULT); /** * Add a file descriptor and callback to be invoked when * descriptor is ready for input or output. An IoEventType * determines what type of I/O event will cause the callback to be * invoked. * * Only one callback may be associated with each event * type, e.g. one callback for read pending, one callback for * write pending. * * If multiple event types in are associated with the same * callback, the callback is only invoked once, but the mask * argument passed to the callback shows multiple event types. * * @param fd the file descriptor. * @param type the @ref IoEventType of the event. * @param cb object to be invoked when file descriptor has I/O * pending. * @return true on success, false if any error occurred. */ bool add_ioevent_cb(XorpFd fd, IoEventType type, const IoEventCb& cb, int priority = XorpTask::PRIORITY_DEFAULT); /** * Remove callbacks associated with file descriptor. * * @param fd the file descriptor. * @param type the event type to clear. * The special value IOT_ANY means clear any kind of callback. * @return true on success, false if any error occurred. */ bool remove_ioevent_cb(XorpFd fd, IoEventType type = IOT_ANY); /** * @return true if any XorpTimers are present on EventLoop's TimerList. */ bool timers_pending() const; /** * @return true if any XorpTimers are present on EventLoop's * TimerList or any XorpTasks are present on the TaskList. */ bool events_pending() const; /** * @return the number of XorpTimers present on EventLoop's TimerList. */ size_t timer_list_length() const; /** * Get current time according to EventLoop's TimerList */ void current_time(TimeVal& now) const; /** * Get the count of the descriptors that have been added. * * @return the count of the descriptors that have been added. */ size_t descriptor_count() const; void set_aggressiveness(int num); private: void do_work(); private: ClockBase* _clock; TimerList _timer_list; TaskList _task_list; int _aggressiveness; time_t _last_ev_run; // 0 - Means run not called yet. time_t _last_warned; bool _is_debug; // If true, debug enabled // Was the last event at this priority a selector or a task. bool _last_ev_type[XorpTask::PRIORITY_INFINITY]; #if USE_WIN_DISPATCHER WinDispatcher _win_dispatcher; #else SelectorList _selector_list; #endif }; // ---------------------------------------------------------------------------- // Deferred definitions inline XorpTimer EventLoop::new_timer(const BasicTimerCallback& cb) { return _timer_list.new_timer(cb); } inline XorpTimer EventLoop::new_oneoff_at(const TimeVal& tv, const OneoffTimerCallback& ocb, int priority) { return _timer_list.new_oneoff_at(tv, ocb, priority); } inline XorpTimer EventLoop::new_oneoff_after(const TimeVal& wait, const OneoffTimerCallback& ocb, int priority) { return _timer_list.new_oneoff_after(wait, ocb, priority); } inline XorpTimer EventLoop::new_oneoff_after_ms(int ms, const OneoffTimerCallback& ocb, int priority) { TimeVal wait(ms / 1000, (ms % 1000) * 1000); return _timer_list.new_oneoff_after(wait, ocb, priority); } inline XorpTimer EventLoop::new_periodic(const TimeVal& wait, const PeriodicTimerCallback& pcb, int priority) { return _timer_list.new_periodic(wait, pcb, priority); } inline XorpTimer EventLoop::new_periodic_ms(int ms, const PeriodicTimerCallback& pcb, int priority) { TimeVal wait(ms / 1000, (ms % 1000) * 1000); return _timer_list.new_periodic(wait, pcb, priority); } inline XorpTimer EventLoop::set_flag_at(const TimeVal& tv, bool *flag_ptr, bool to_value) { return _timer_list.set_flag_at(tv, flag_ptr, to_value); } inline XorpTimer EventLoop::set_flag_after(const TimeVal& wait, bool *flag_ptr, bool to_value) { return _timer_list.set_flag_after(wait, flag_ptr, to_value); } inline XorpTimer EventLoop::set_flag_after_ms(int ms, bool *flag_ptr, bool to_value) { TimeVal wait(ms / 1000, (ms % 1000) * 1000); return _timer_list.set_flag_after(wait, flag_ptr, to_value); } inline bool EventLoop::timers_pending() const { return !_timer_list.empty(); } inline bool EventLoop::events_pending() const { return ((!_timer_list.empty()) || (!_task_list.empty())); } inline size_t EventLoop::timer_list_length() const { return _timer_list.size(); } inline void EventLoop::current_time(TimeVal& t) const { _timer_list.current_time(t); } #endif // __LIBXORP_EVENTLOOP_HH__ xorp/libxorp/inet_pton.c0000664000076400007640000001324411532046567015517 0ustar greearbgreearb/* $KAME: inet_pton.c,v 1.5 2001/08/20 02:32:40 itojun Exp $ */ /* vim:set sts=4 ts=8: */ /* Copyright (c) 1996 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #include "libxorp/xorp.h" #ifndef HAVE_INET_PTON #if 0 #include __FBSDID("$FreeBSD: src/lib/libc/net/inet_pton.c,v 1.11 2002/09/06 11:23:49 tjr Exp $"); #endif #ifdef HOST_OS_WINDOWS #ifndef EAFNOSUPPORT #define EAFNOSUPPORT WSAEAFNOSUPPORT #endif #ifndef INADDRSZ #define INADDRSZ 4 #endif #ifndef NS_INADDRSZ #define NS_INADDRSZ INADDRSZ #endif #ifndef IN6ADDRSZ #define IN6ADDRSZ 16 #endif #ifndef NS_IN6ADDRSZ #define NS_IN6ADDRSZ IN6ADDRSZ #endif #ifndef NS_INT16SZ #define NS_INT16SZ 2 #endif #else #include #include #include #include #include #include #include #include #include #include #include #include #include #endif /* * WARNING: Don't even consider trying to compile this on a system where * sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX. */ static int inet_pton4(const char *src, u_char *dst); static int inet_pton6(const char *src, u_char *dst); /* int * inet_pton(af, src, dst) * convert from presentation format (which usually means ASCII printable) * to network format (which is usually some kind of binary format). * return: * 1 if the address was valid for the specified address family * 0 if the address wasn't valid (`dst' is untouched in this case) * -1 if some other error occurred (`dst' is untouched in this case, too) * author: * Paul Vixie, 1996. */ int inet_pton(int af, const char * __restrict src, void * __restrict dst) { switch (af) { case AF_INET: return (inet_pton4(src, dst)); case AF_INET6: return (inet_pton6(src, dst)); default: errno = EAFNOSUPPORT; return (-1); } /* NOTREACHED */ } /* int * inet_pton4(src, dst) * like inet_aton() but without all the hexadecimal and shorthand. * return: * 1 if `src' is a valid dotted quad, else 0. * notice: * does not touch `dst' unless it's returning 1. * author: * Paul Vixie, 1996. */ static int inet_pton4(const char *src, u_char *dst) { static const char digits[] = "0123456789"; int saw_digit, octets, ch; u_char tmp[NS_INADDRSZ], *tp; saw_digit = 0; octets = 0; *(tp = tmp) = 0; while ((ch = *src++) != '\0') { const char *pch; if ((pch = strchr(digits, ch)) != NULL) { u_int new = *tp * 10 + (pch - digits); if (new > 255) return (0); *tp = new; if (! saw_digit) { if (++octets > 4) return (0); saw_digit = 1; } } else if (ch == '.' && saw_digit) { if (octets == 4) return (0); *++tp = 0; saw_digit = 0; } else return (0); } if (octets < 4) return (0); memcpy(dst, tmp, NS_INADDRSZ); return (1); } /* int * inet_pton6(src, dst) * convert presentation level address to network order binary form. * return: * 1 if `src' is a valid [RFC1884 2.2] address, else 0. * notice: * (1) does not touch `dst' unless it's returning 1. * (2) :: in a full address is silently ignored. * credit: * inspired by Mark Andrews. * author: * Paul Vixie, 1996. */ static int inet_pton6(const char *src, u_char *dst) { static const char xdigits_l[] = "0123456789abcdef", xdigits_u[] = "0123456789ABCDEF"; u_char tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp; const char *xdigits, *curtok; int ch, saw_xdigit; u_int val; memset((tp = tmp), '\0', NS_IN6ADDRSZ); endp = tp + NS_IN6ADDRSZ; colonp = NULL; /* Leading :: requires some special handling. */ if (*src == ':') if (*++src != ':') return (0); curtok = src; saw_xdigit = 0; val = 0; while ((ch = *src++) != '\0') { const char *pch; if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL) pch = strchr((xdigits = xdigits_u), ch); if (pch != NULL) { val <<= 4; val |= (pch - xdigits); if (val > 0xffff) return (0); saw_xdigit = 1; continue; } if (ch == ':') { curtok = src; if (!saw_xdigit) { if (colonp) return (0); colonp = tp; continue; } if (tp + NS_INT16SZ > endp) return (0); *tp++ = (u_char) (val >> 8) & 0xff; *tp++ = (u_char) val & 0xff; saw_xdigit = 0; val = 0; continue; } if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) && inet_pton4(curtok, tp) > 0) { tp += NS_INADDRSZ; saw_xdigit = 0; break; /* '\0' was seen by inet_pton4(). */ } return (0); } if (saw_xdigit) { if (tp + NS_INT16SZ > endp) return (0); *tp++ = (u_char) (val >> 8) & 0xff; *tp++ = (u_char) val & 0xff; } if (colonp != NULL) { /* * Since some memmove()'s erroneously fail to handle * overlapping regions, we'll do the shift by hand. */ const int n = tp - colonp; int i; for (i = 1; i <= n; i++) { endp[- i] = colonp[n - i]; colonp[n - i] = 0; } tp = endp; } if (tp != endp) return (0); memcpy(dst, tmp, NS_IN6ADDRSZ); return (1); } #endif /* !HAVE_INET_PTON */ xorp/lf_pkg.bash0000775000076400007640000000267611621145144013776 0ustar greearbgreearb#!/bin/bash # Script for packaging up a xorp tarball for install in # /usr/local/ rm -fr /usr/local/xorp DBG="-dbg" SCONS_ARGS="enable_olsr=true strip=no" if [ -f obj/*gnu*/.scons_build_args ] then SCONS_ARGS=`cat obj/*gnu*/.scons_build_args` SCONS_ARGS=$SCONS_ARGS strip=no fi #scons enable_olsr=true strip=no install echo "Scons args: $SCONS_ARGS" echo -n 3 sleep 1 echo -n " 2" sleep 1 echo " 1" sleep 1 scons $SCONS_ARGS install || exit 1 cp xorp_install.bash /usr/local/xorp/ chmod a+x /usr/local/xorp/xorp_install.bash PWD=$(pwd) userdir=$(expr match "$PWD" '\(/home/[0-Z]*/\).*') if [ "_$userdir" == "_" ] then userdir = "./" fi cd /usr/local if [ "$1_" != "nostrip_" ] then echo "Stripping files in lf_pkg.bash" find xorp -name "*" -print|xargs strip DBG= fi if [ ! -d ${userdir}tmp ] then echo "Creating directory: ${userdir}tmp to hold xorp package." mkdir -p ${userdir}tmp fi if uname -a|grep i386 then if tar -cvzf ${userdir}tmp/xorp_32${DBG}.tgz xorp then echo "" echo "Created package: ${userdir}tmp/xorp_32${DBG}.tgz" echo "" else echo "" echo "ERROR: There were errors trying to create: ${userdir}tmp/xorp_32${DBG}.tgz" echo "" fi else if tar -cvzf ${userdir}tmp/xorp_64${DBG}.tgz xorp then echo "" echo "Created package: ${userdir}tmp/xorp_64${DBG}.tgz" echo "" else echo "" echo "ERROR: There were errors trying to create: ${userdir}tmp/xorp_64${DBG}.tgz" echo "" fi fi cd - xorp/prerm0000755000076400007640000000004211665020734012731 0ustar greearbgreearb#!/bin/bash # nothing to do here. xorp/config.guess0000775000076400007640000012723611421137511014207 0ustar greearbgreearb#! /bin/sh # Attempt to guess a canonical system name. # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, # 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 # Free Software Foundation, Inc. timestamp='2009-09-18' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA # 02110-1301, USA. # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # Originally written by Per Bothner. Please send patches (context # diff format) to and include a ChangeLog # entry. # # This script attempts to guess a canonical system name similar to # config.sub. If it succeeds, it prints the system name on stdout, and # exits with 0. Otherwise, it exits with 1. # # You can get the latest version of this script from: # http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] Output the configuration name of the system \`$me' is run on. Operation modes: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.guess ($timestamp) Originally written by Per Bothner. Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" >&2 exit 1 ;; * ) break ;; esac done if test $# != 0; then echo "$me: too many arguments$help" >&2 exit 1 fi trap 'exit 1' 1 2 15 # CC_FOR_BUILD -- compiler used by this script. Note that the use of a # compiler to aid in system detection is discouraged as it requires # temporary files to be created and, as you can see below, it is a # headache to deal with in a portable fashion. # Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still # use `HOST_CC' if defined, but it is deprecated. # Portable tmp directory creation inspired by the Autoconf team. set_cc_for_build=' trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; : ${TMPDIR=/tmp} ; { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; dummy=$tmp/dummy ; tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; case $CC_FOR_BUILD,$HOST_CC,$CC in ,,) echo "int x;" > $dummy.c ; for c in cc gcc c89 c99 ; do if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then CC_FOR_BUILD="$c"; break ; fi ; done ; if test x"$CC_FOR_BUILD" = x ; then CC_FOR_BUILD=no_compiler_found ; fi ;; ,,*) CC_FOR_BUILD=$CC ;; ,*,*) CC_FOR_BUILD=$HOST_CC ;; esac ; set_cc_for_build= ;' # This is needed to find uname on a Pyramid OSx when run in the BSD universe. # (ghazi@noc.rutgers.edu 1994-08-24) if (test -f /.attbin/uname) >/dev/null 2>&1 ; then PATH=$PATH:/.attbin ; export PATH fi UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown # Note: order is significant - the case branches are not exclusive. case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in *:NetBSD:*:*) # NetBSD (nbsd) targets should (where applicable) match one or # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*, # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently # switched to ELF, *-*-netbsd* would select the old # object file format. This provides both forward # compatibility and a consistent mechanism for selecting the # object file format. # # Note: NetBSD doesn't particularly care about the vendor # portion of the name. We always set it to "unknown". sysctl="sysctl -n hw.machine_arch" UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \ /usr/sbin/$sysctl 2>/dev/null || echo unknown)` case "${UNAME_MACHINE_ARCH}" in armeb) machine=armeb-unknown ;; arm*) machine=arm-unknown ;; sh3el) machine=shl-unknown ;; sh3eb) machine=sh-unknown ;; sh5el) machine=sh5le-unknown ;; *) machine=${UNAME_MACHINE_ARCH}-unknown ;; esac # The Operating System including object format, if it has switched # to ELF recently, or will in the future. case "${UNAME_MACHINE_ARCH}" in arm*|i386|m68k|ns32k|sh3*|sparc|vax) eval $set_cc_for_build if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ELF__ then # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). # Return netbsd for either. FIX? os=netbsd else os=netbsdelf fi ;; *) os=netbsd ;; esac # The OS release # Debian GNU/NetBSD machines have a different userland, and # thus, need a distinct triplet. However, they do not need # kernel version information, so it can be replaced with a # suitable tag, in the style of linux-gnu. case "${UNAME_VERSION}" in Debian*) release='-gnu' ;; *) release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` ;; esac # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: # contains redundant information, the shorter form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. echo "${machine}-${os}${release}" exit ;; *:OpenBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE} exit ;; *:ekkoBSD:*:*) echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} exit ;; *:SolidBSD:*:*) echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE} exit ;; macppc:MirBSD:*:*) echo powerpc-unknown-mirbsd${UNAME_RELEASE} exit ;; *:MirBSD:*:*) echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} exit ;; alpha:OSF1:*:*) case $UNAME_RELEASE in *4.0) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` ;; *5.*) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` ;; esac # According to Compaq, /usr/sbin/psrinfo has been available on # OSF/1 and Tru64 systems produced since 1995. I hope that # covers most systems running today. This code pipes the CPU # types through head -n 1, so we only detect the type of CPU 0. ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` case "$ALPHA_CPU_TYPE" in "EV4 (21064)") UNAME_MACHINE="alpha" ;; "EV4.5 (21064)") UNAME_MACHINE="alpha" ;; "LCA4 (21066/21068)") UNAME_MACHINE="alpha" ;; "EV5 (21164)") UNAME_MACHINE="alphaev5" ;; "EV5.6 (21164A)") UNAME_MACHINE="alphaev56" ;; "EV5.6 (21164PC)") UNAME_MACHINE="alphapca56" ;; "EV5.7 (21164PC)") UNAME_MACHINE="alphapca57" ;; "EV6 (21264)") UNAME_MACHINE="alphaev6" ;; "EV6.7 (21264A)") UNAME_MACHINE="alphaev67" ;; "EV6.8CB (21264C)") UNAME_MACHINE="alphaev68" ;; "EV6.8AL (21264B)") UNAME_MACHINE="alphaev68" ;; "EV6.8CX (21264D)") UNAME_MACHINE="alphaev68" ;; "EV6.9A (21264/EV69A)") UNAME_MACHINE="alphaev69" ;; "EV7 (21364)") UNAME_MACHINE="alphaev7" ;; "EV7.9 (21364A)") UNAME_MACHINE="alphaev79" ;; esac # A Pn.n version is a patched version. # A Vn.n version is a released version. # A Tn.n version is a released field test version. # A Xn.n version is an unreleased experimental baselevel. # 1.2 uses "1.2" for uname -r. echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` exit ;; Alpha\ *:Windows_NT*:*) # How do we know it's Interix rather than the generic POSIX subsystem? # Should we change UNAME_MACHINE based on the output of uname instead # of the specific Alpha model? echo alpha-pc-interix exit ;; 21064:Windows_NT:50:3) echo alpha-dec-winnt3.5 exit ;; Amiga*:UNIX_System_V:4.0:*) echo m68k-unknown-sysv4 exit ;; *:[Aa]miga[Oo][Ss]:*:*) echo ${UNAME_MACHINE}-unknown-amigaos exit ;; *:[Mm]orph[Oo][Ss]:*:*) echo ${UNAME_MACHINE}-unknown-morphos exit ;; *:OS/390:*:*) echo i370-ibm-openedition exit ;; *:z/VM:*:*) echo s390-ibm-zvmoe exit ;; *:OS400:*:*) echo powerpc-ibm-os400 exit ;; arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) echo arm-acorn-riscix${UNAME_RELEASE} exit ;; arm:riscos:*:*|arm:RISCOS:*:*) echo arm-unknown-riscos exit ;; SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) echo hppa1.1-hitachi-hiuxmpp exit ;; Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. if test "`(/bin/universe) 2>/dev/null`" = att ; then echo pyramid-pyramid-sysv3 else echo pyramid-pyramid-bsd fi exit ;; NILE*:*:*:dcosx) echo pyramid-pyramid-svr4 exit ;; DRS?6000:unix:4.0:6*) echo sparc-icl-nx6 exit ;; DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) case `/usr/bin/uname -p` in sparc) echo sparc-icl-nx7; exit ;; esac ;; s390x:SunOS:*:*) echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4H:SunOS:5.*:*) echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) eval $set_cc_for_build SUN_ARCH="i386" # If there is a compiler, see if it is configured for 64-bit objects. # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. # This test works for both compilers. if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then SUN_ARCH="x86_64" fi fi echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4*:SunOS:6*:*) # According to config.sub, this is the proper way to canonicalize # SunOS6. Hard to guess exactly what SunOS6 will be like, but # it's likely to be more like Solaris than SunOS4. echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4*:SunOS:*:*) case "`/usr/bin/arch -k`" in Series*|S4*) UNAME_RELEASE=`uname -v` ;; esac # Japanese Language versions have a version number like `4.1.3-JL'. echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` exit ;; sun3*:SunOS:*:*) echo m68k-sun-sunos${UNAME_RELEASE} exit ;; sun*:*:4.2BSD:*) UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 case "`/bin/arch`" in sun3) echo m68k-sun-sunos${UNAME_RELEASE} ;; sun4) echo sparc-sun-sunos${UNAME_RELEASE} ;; esac exit ;; aushp:SunOS:*:*) echo sparc-auspex-sunos${UNAME_RELEASE} exit ;; # The situation for MiNT is a little confusing. The machine name # can be virtually everything (everything which is not # "atarist" or "atariste" at least should have a processor # > m68000). The system name ranges from "MiNT" over "FreeMiNT" # to the lowercase version "mint" (or "freemint"). Finally # the system name "TOS" denotes a system which is actually not # MiNT. But MiNT is downward compatible to TOS, so this should # be no problem. atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) echo m68k-milan-mint${UNAME_RELEASE} exit ;; hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) echo m68k-hades-mint${UNAME_RELEASE} exit ;; *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) echo m68k-unknown-mint${UNAME_RELEASE} exit ;; m68k:machten:*:*) echo m68k-apple-machten${UNAME_RELEASE} exit ;; powerpc:machten:*:*) echo powerpc-apple-machten${UNAME_RELEASE} exit ;; RISC*:Mach:*:*) echo mips-dec-mach_bsd4.3 exit ;; RISC*:ULTRIX:*:*) echo mips-dec-ultrix${UNAME_RELEASE} exit ;; VAX*:ULTRIX*:*:*) echo vax-dec-ultrix${UNAME_RELEASE} exit ;; 2020:CLIX:*:* | 2430:CLIX:*:*) echo clipper-intergraph-clix${UNAME_RELEASE} exit ;; mips:*:*:UMIPS | mips:*:*:RISCos) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #ifdef __cplusplus #include /* for printf() prototype */ int main (int argc, char *argv[]) { #else int main (argc, argv) int argc; char *argv[]; { #endif #if defined (host_mips) && defined (MIPSEB) #if defined (SYSTYPE_SYSV) printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_SVR4) printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); #endif #endif exit (-1); } EOF $CC_FOR_BUILD -o $dummy $dummy.c && dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` && SYSTEM_NAME=`$dummy $dummyarg` && { echo "$SYSTEM_NAME"; exit; } echo mips-mips-riscos${UNAME_RELEASE} exit ;; Motorola:PowerMAX_OS:*:*) echo powerpc-motorola-powermax exit ;; Motorola:*:4.3:PL8-*) echo powerpc-harris-powermax exit ;; Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) echo powerpc-harris-powermax exit ;; Night_Hawk:Power_UNIX:*:*) echo powerpc-harris-powerunix exit ;; m88k:CX/UX:7*:*) echo m88k-harris-cxux7 exit ;; m88k:*:4*:R4*) echo m88k-motorola-sysv4 exit ;; m88k:*:3*:R3*) echo m88k-motorola-sysv3 exit ;; AViiON:dgux:*:*) # DG/UX returns AViiON for all architectures UNAME_PROCESSOR=`/usr/bin/uname -p` if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] then if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ [ ${TARGET_BINARY_INTERFACE}x = x ] then echo m88k-dg-dgux${UNAME_RELEASE} else echo m88k-dg-dguxbcs${UNAME_RELEASE} fi else echo i586-dg-dgux${UNAME_RELEASE} fi exit ;; M88*:DolphinOS:*:*) # DolphinOS (SVR3) echo m88k-dolphin-sysv3 exit ;; M88*:*:R3*:*) # Delta 88k system running SVR3 echo m88k-motorola-sysv3 exit ;; XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) echo m88k-tektronix-sysv3 exit ;; Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) echo m68k-tektronix-bsd exit ;; *:IRIX*:*:*) echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` exit ;; ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' i*86:AIX:*:*) echo i386-ibm-aix exit ;; ia64:AIX:*:*) if [ -x /usr/bin/oslevel ] ; then IBM_REV=`/usr/bin/oslevel` else IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} fi echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} exit ;; *:AIX:2:3) if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #include main() { if (!__power_pc()) exit(1); puts("powerpc-ibm-aix3.2.5"); exit(0); } EOF if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` then echo "$SYSTEM_NAME" else echo rs6000-ibm-aix3.2.5 fi elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then echo rs6000-ibm-aix3.2.4 else echo rs6000-ibm-aix3.2 fi exit ;; *:AIX:*:[456]) IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then IBM_ARCH=rs6000 else IBM_ARCH=powerpc fi if [ -x /usr/bin/oslevel ] ; then IBM_REV=`/usr/bin/oslevel` else IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} fi echo ${IBM_ARCH}-ibm-aix${IBM_REV} exit ;; *:AIX:*:*) echo rs6000-ibm-aix exit ;; ibmrt:4.4BSD:*|romp-ibm:BSD:*) echo romp-ibm-bsd4.4 exit ;; ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to exit ;; # report: romp-ibm BSD 4.3 *:BOSX:*:*) echo rs6000-bull-bosx exit ;; DPX/2?00:B.O.S.:*:*) echo m68k-bull-sysv3 exit ;; 9000/[34]??:4.3bsd:1.*:*) echo m68k-hp-bsd exit ;; hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) echo m68k-hp-bsd4.4 exit ;; 9000/[34678]??:HP-UX:*:*) HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` case "${UNAME_MACHINE}" in 9000/31? ) HP_ARCH=m68000 ;; 9000/[34]?? ) HP_ARCH=m68k ;; 9000/[678][0-9][0-9]) if [ -x /usr/bin/getconf ]; then sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` case "${sc_cpu_version}" in 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 532) # CPU_PA_RISC2_0 case "${sc_kernel_bits}" in 32) HP_ARCH="hppa2.0n" ;; 64) HP_ARCH="hppa2.0w" ;; '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 esac ;; esac fi if [ "${HP_ARCH}" = "" ]; then eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #define _HPUX_SOURCE #include #include int main () { #if defined(_SC_KERNEL_BITS) long bits = sysconf(_SC_KERNEL_BITS); #endif long cpu = sysconf (_SC_CPU_VERSION); switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0"); break; case CPU_PA_RISC1_1: puts ("hppa1.1"); break; case CPU_PA_RISC2_0: #if defined(_SC_KERNEL_BITS) switch (bits) { case 64: puts ("hppa2.0w"); break; case 32: puts ("hppa2.0n"); break; default: puts ("hppa2.0"); break; } break; #else /* !defined(_SC_KERNEL_BITS) */ puts ("hppa2.0"); break; #endif default: puts ("hppa1.0"); break; } exit (0); } EOF (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` test -z "$HP_ARCH" && HP_ARCH=hppa fi ;; esac if [ ${HP_ARCH} = "hppa2.0w" ] then eval $set_cc_for_build # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler # generating 64-bit code. GNU and HP use different nomenclature: # # $ CC_FOR_BUILD=cc ./config.guess # => hppa2.0w-hp-hpux11.23 # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess # => hppa64-hp-hpux11.23 if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | grep -q __LP64__ then HP_ARCH="hppa2.0w" else HP_ARCH="hppa64" fi fi echo ${HP_ARCH}-hp-hpux${HPUX_REV} exit ;; ia64:HP-UX:*:*) HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` echo ia64-hp-hpux${HPUX_REV} exit ;; 3050*:HI-UX:*:*) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #include int main () { long cpu = sysconf (_SC_CPU_VERSION); /* The order matters, because CPU_IS_HP_MC68K erroneously returns true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct results, however. */ if (CPU_IS_PA_RISC (cpu)) { switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; default: puts ("hppa-hitachi-hiuxwe2"); break; } } else if (CPU_IS_HP_MC68K (cpu)) puts ("m68k-hitachi-hiuxwe2"); else puts ("unknown-hitachi-hiuxwe2"); exit (0); } EOF $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` && { echo "$SYSTEM_NAME"; exit; } echo unknown-hitachi-hiuxwe2 exit ;; 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) echo hppa1.1-hp-bsd exit ;; 9000/8??:4.3bsd:*:*) echo hppa1.0-hp-bsd exit ;; *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) echo hppa1.0-hp-mpeix exit ;; hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) echo hppa1.1-hp-osf exit ;; hp8??:OSF1:*:*) echo hppa1.0-hp-osf exit ;; i*86:OSF1:*:*) if [ -x /usr/sbin/sysversion ] ; then echo ${UNAME_MACHINE}-unknown-osf1mk else echo ${UNAME_MACHINE}-unknown-osf1 fi exit ;; parisc*:Lites*:*:*) echo hppa1.1-hp-lites exit ;; C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) echo c1-convex-bsd exit ;; C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) if getsysinfo -f scalar_acc then echo c32-convex-bsd else echo c2-convex-bsd fi exit ;; C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) echo c34-convex-bsd exit ;; C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) echo c38-convex-bsd exit ;; C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) echo c4-convex-bsd exit ;; CRAY*Y-MP:*:*:*) echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*[A-Z]90:*:*:*) echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ -e 's/\.[^.]*$/.X/' exit ;; CRAY*TS:*:*:*) echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*T3E:*:*:*) echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*SV1:*:*:*) echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; *:UNICOS/mp:*:*) echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; 5000:UNIX_System_V:4.*:*) FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'` echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} exit ;; sparc*:BSD/OS:*:*) echo sparc-unknown-bsdi${UNAME_RELEASE} exit ;; *:BSD/OS:*:*) echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} exit ;; *:FreeBSD:*:*) case ${UNAME_MACHINE} in pc98) echo i386-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; amd64) echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; *) echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; esac exit ;; i*:CYGWIN*:*) echo ${UNAME_MACHINE}-pc-cygwin exit ;; *:MINGW*:*) echo ${UNAME_MACHINE}-pc-mingw32 exit ;; i*:windows32*:*) # uname -m includes "-pc" on this system. echo ${UNAME_MACHINE}-mingw32 exit ;; i*:PW*:*) echo ${UNAME_MACHINE}-pc-pw32 exit ;; *:Interix*:[3456]*) case ${UNAME_MACHINE} in x86) echo i586-pc-interix${UNAME_RELEASE} exit ;; EM64T | authenticamd | genuineintel) echo x86_64-unknown-interix${UNAME_RELEASE} exit ;; IA64) echo ia64-unknown-interix${UNAME_RELEASE} exit ;; esac ;; [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) echo i${UNAME_MACHINE}-pc-mks exit ;; 8664:Windows_NT:*) echo x86_64-pc-mks exit ;; i*:Windows_NT*:* | Pentium*:Windows_NT*:*) # How do we know it's Interix rather than the generic POSIX subsystem? # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we # UNAME_MACHINE based on the output of uname instead of i386? echo i586-pc-interix exit ;; i*:UWIN*:*) echo ${UNAME_MACHINE}-pc-uwin exit ;; amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) echo x86_64-unknown-cygwin exit ;; p*:CYGWIN*:*) echo powerpcle-unknown-cygwin exit ;; prep*:SunOS:5.*:*) echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; *:GNU:*:*) # the GNU system echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` exit ;; *:GNU/*:*:*) # other systems with GNU libc and userland echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu exit ;; i*86:Minix:*:*) echo ${UNAME_MACHINE}-pc-minix exit ;; alpha:Linux:*:*) case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in EV5) UNAME_MACHINE=alphaev5 ;; EV56) UNAME_MACHINE=alphaev56 ;; PCA56) UNAME_MACHINE=alphapca56 ;; PCA57) UNAME_MACHINE=alphapca56 ;; EV6) UNAME_MACHINE=alphaev6 ;; EV67) UNAME_MACHINE=alphaev67 ;; EV68*) UNAME_MACHINE=alphaev68 ;; esac objdump --private-headers /bin/sh | grep -q ld.so.1 if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} exit ;; arm*:Linux:*:*) eval $set_cc_for_build if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_EABI__ then echo ${UNAME_MACHINE}-unknown-linux-gnu else echo ${UNAME_MACHINE}-unknown-linux-gnueabi fi exit ;; avr32*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; cris:Linux:*:*) echo cris-axis-linux-gnu exit ;; crisv32:Linux:*:*) echo crisv32-axis-linux-gnu exit ;; frv:Linux:*:*) echo frv-unknown-linux-gnu exit ;; i*86:Linux:*:*) echo ${UNAME_MACHINE}-pc-linux-gnu exit ;; ia64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; m32r*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; m68*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; mips:Linux:*:* | mips64:Linux:*:*) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #undef CPU #undef ${UNAME_MACHINE} #undef ${UNAME_MACHINE}el #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) CPU=${UNAME_MACHINE}el #else #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) CPU=${UNAME_MACHINE} #else CPU= #endif #endif EOF eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n ' /^CPU/{ s: ::g p }'`" test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; } ;; or32:Linux:*:*) echo or32-unknown-linux-gnu exit ;; padre:Linux:*:*) echo sparc-unknown-linux-gnu exit ;; parisc64:Linux:*:* | hppa64:Linux:*:*) echo hppa64-unknown-linux-gnu exit ;; parisc:Linux:*:* | hppa:Linux:*:*) # Look for CPU level case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in PA7*) echo hppa1.1-unknown-linux-gnu ;; PA8*) echo hppa2.0-unknown-linux-gnu ;; *) echo hppa-unknown-linux-gnu ;; esac exit ;; ppc64:Linux:*:*) echo powerpc64-unknown-linux-gnu exit ;; ppc:Linux:*:*) echo powerpc-unknown-linux-gnu exit ;; s390:Linux:*:* | s390x:Linux:*:*) echo ${UNAME_MACHINE}-ibm-linux exit ;; sh64*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; sh*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; sparc:Linux:*:* | sparc64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; vax:Linux:*:*) echo ${UNAME_MACHINE}-dec-linux-gnu exit ;; x86_64:Linux:*:*) echo x86_64-unknown-linux-gnu exit ;; xtensa*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; i*86:DYNIX/ptx:4*:*) # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. # earlier versions are messed up and put the nodename in both # sysname and nodename. echo i386-sequent-sysv4 exit ;; i*86:UNIX_SV:4.2MP:2.*) # Unixware is an offshoot of SVR4, but it has its own version # number series starting with 2... # I am not positive that other SVR4 systems won't match this, # I just have to hope. -- rms. # Use sysv4.2uw... so that sysv4* matches it. echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} exit ;; i*86:OS/2:*:*) # If we were able to find `uname', then EMX Unix compatibility # is probably installed. echo ${UNAME_MACHINE}-pc-os2-emx exit ;; i*86:XTS-300:*:STOP) echo ${UNAME_MACHINE}-unknown-stop exit ;; i*86:atheos:*:*) echo ${UNAME_MACHINE}-unknown-atheos exit ;; i*86:syllable:*:*) echo ${UNAME_MACHINE}-pc-syllable exit ;; i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) echo i386-unknown-lynxos${UNAME_RELEASE} exit ;; i*86:*DOS:*:*) echo ${UNAME_MACHINE}-pc-msdosdjgpp exit ;; i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} else echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} fi exit ;; i*86:*:5:[678]*) # UnixWare 7.x, OpenUNIX and OpenServer 6. case `/bin/uname -X | grep "^Machine"` in *486*) UNAME_MACHINE=i486 ;; *Pentium) UNAME_MACHINE=i586 ;; *Pent*|*Celeron) UNAME_MACHINE=i686 ;; esac echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} exit ;; i*86:*:3.2:*) if test -f /usr/options/cb.name; then UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ && UNAME_MACHINE=i586 (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ && UNAME_MACHINE=i686 (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ && UNAME_MACHINE=i686 echo ${UNAME_MACHINE}-pc-sco$UNAME_REL else echo ${UNAME_MACHINE}-pc-sysv32 fi exit ;; pc:*:*:*) # Left here for compatibility: # uname -m prints for DJGPP always 'pc', but it prints nothing about # the processor, so we play safe by assuming i586. # Note: whatever this is, it MUST be the same as what config.sub # prints for the "djgpp" host, or else GDB configury will decide that # this is a cross-build. echo i586-pc-msdosdjgpp exit ;; Intel:Mach:3*:*) echo i386-pc-mach3 exit ;; paragon:*:*:*) echo i860-intel-osf1 exit ;; i860:*:4.*:*) # i860-SVR4 if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 else # Add other i860-SVR4 vendors below as they are discovered. echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 fi exit ;; mini*:CTIX:SYS*5:*) # "miniframe" echo m68010-convergent-sysv exit ;; mc68k:UNIX:SYSTEM5:3.51m) echo m68k-convergent-sysv exit ;; M680?0:D-NIX:5.3:*) echo m68k-diab-dnix exit ;; M68*:*:R3V[5678]*:*) test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) OS_REL='' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3${OS_REL}; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4; exit; } ;; NCR*:*:4.2:* | MPRAS*:*:4.2:*) OS_REL='.3' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3${OS_REL}; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) echo m68k-unknown-lynxos${UNAME_RELEASE} exit ;; mc68030:UNIX_System_V:4.*:*) echo m68k-atari-sysv4 exit ;; TSUNAMI:LynxOS:2.*:*) echo sparc-unknown-lynxos${UNAME_RELEASE} exit ;; rs6000:LynxOS:2.*:*) echo rs6000-unknown-lynxos${UNAME_RELEASE} exit ;; PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) echo powerpc-unknown-lynxos${UNAME_RELEASE} exit ;; SM[BE]S:UNIX_SV:*:*) echo mips-dde-sysv${UNAME_RELEASE} exit ;; RM*:ReliantUNIX-*:*:*) echo mips-sni-sysv4 exit ;; RM*:SINIX-*:*:*) echo mips-sni-sysv4 exit ;; *:SINIX-*:*:*) if uname -p 2>/dev/null >/dev/null ; then UNAME_MACHINE=`(uname -p) 2>/dev/null` echo ${UNAME_MACHINE}-sni-sysv4 else echo ns32k-sni-sysv fi exit ;; PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort # says echo i586-unisys-sysv4 exit ;; *:UNIX_System_V:4*:FTX*) # From Gerald Hewes . # How about differentiating between stratus architectures? -djm echo hppa1.1-stratus-sysv4 exit ;; *:*:*:FTX*) # From seanf@swdc.stratus.com. echo i860-stratus-sysv4 exit ;; i*86:VOS:*:*) # From Paul.Green@stratus.com. echo ${UNAME_MACHINE}-stratus-vos exit ;; *:VOS:*:*) # From Paul.Green@stratus.com. echo hppa1.1-stratus-vos exit ;; mc68*:A/UX:*:*) echo m68k-apple-aux${UNAME_RELEASE} exit ;; news*:NEWS-OS:6*:*) echo mips-sony-newsos6 exit ;; R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) if [ -d /usr/nec ]; then echo mips-nec-sysv${UNAME_RELEASE} else echo mips-unknown-sysv${UNAME_RELEASE} fi exit ;; BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. echo powerpc-be-beos exit ;; BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. echo powerpc-apple-beos exit ;; BePC:BeOS:*:*) # BeOS running on Intel PC compatible. echo i586-pc-beos exit ;; BePC:Haiku:*:*) # Haiku running on Intel PC compatible. echo i586-pc-haiku exit ;; SX-4:SUPER-UX:*:*) echo sx4-nec-superux${UNAME_RELEASE} exit ;; SX-5:SUPER-UX:*:*) echo sx5-nec-superux${UNAME_RELEASE} exit ;; SX-6:SUPER-UX:*:*) echo sx6-nec-superux${UNAME_RELEASE} exit ;; SX-7:SUPER-UX:*:*) echo sx7-nec-superux${UNAME_RELEASE} exit ;; SX-8:SUPER-UX:*:*) echo sx8-nec-superux${UNAME_RELEASE} exit ;; SX-8R:SUPER-UX:*:*) echo sx8r-nec-superux${UNAME_RELEASE} exit ;; Power*:Rhapsody:*:*) echo powerpc-apple-rhapsody${UNAME_RELEASE} exit ;; *:Rhapsody:*:*) echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} exit ;; *:Darwin:*:*) UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown case $UNAME_PROCESSOR in i386) eval $set_cc_for_build if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then UNAME_PROCESSOR="x86_64" fi fi ;; unknown) UNAME_PROCESSOR=powerpc ;; esac echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} exit ;; *:procnto*:*:* | *:QNX:[0123456789]*:*) UNAME_PROCESSOR=`uname -p` if test "$UNAME_PROCESSOR" = "x86"; then UNAME_PROCESSOR=i386 UNAME_MACHINE=pc fi echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} exit ;; *:QNX:*:4*) echo i386-pc-qnx exit ;; NSE-?:NONSTOP_KERNEL:*:*) echo nse-tandem-nsk${UNAME_RELEASE} exit ;; NSR-?:NONSTOP_KERNEL:*:*) echo nsr-tandem-nsk${UNAME_RELEASE} exit ;; *:NonStop-UX:*:*) echo mips-compaq-nonstopux exit ;; BS2000:POSIX*:*:*) echo bs2000-siemens-sysv exit ;; DS/*:UNIX_System_V:*:*) echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} exit ;; *:Plan9:*:*) # "uname -m" is not consistent, so use $cputype instead. 386 # is converted to i386 for consistency with other x86 # operating systems. if test "$cputype" = "386"; then UNAME_MACHINE=i386 else UNAME_MACHINE="$cputype" fi echo ${UNAME_MACHINE}-unknown-plan9 exit ;; *:TOPS-10:*:*) echo pdp10-unknown-tops10 exit ;; *:TENEX:*:*) echo pdp10-unknown-tenex exit ;; KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) echo pdp10-dec-tops20 exit ;; XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) echo pdp10-xkl-tops20 exit ;; *:TOPS-20:*:*) echo pdp10-unknown-tops20 exit ;; *:ITS:*:*) echo pdp10-unknown-its exit ;; SEI:*:*:SEIUX) echo mips-sei-seiux${UNAME_RELEASE} exit ;; *:DragonFly:*:*) echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` exit ;; *:*VMS:*:*) UNAME_MACHINE=`(uname -p) 2>/dev/null` case "${UNAME_MACHINE}" in A*) echo alpha-dec-vms ; exit ;; I*) echo ia64-dec-vms ; exit ;; V*) echo vax-dec-vms ; exit ;; esac ;; *:XENIX:*:SysV) echo i386-pc-xenix exit ;; i*86:skyos:*:*) echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//' exit ;; i*86:rdos:*:*) echo ${UNAME_MACHINE}-pc-rdos exit ;; i*86:AROS:*:*) echo ${UNAME_MACHINE}-pc-aros exit ;; esac #echo '(No uname command or uname output not recognized.)' 1>&2 #echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2 eval $set_cc_for_build cat >$dummy.c < # include #endif main () { #if defined (sony) #if defined (MIPSEB) /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, I don't know.... */ printf ("mips-sony-bsd\n"); exit (0); #else #include printf ("m68k-sony-newsos%s\n", #ifdef NEWSOS4 "4" #else "" #endif ); exit (0); #endif #endif #if defined (__arm) && defined (__acorn) && defined (__unix) printf ("arm-acorn-riscix\n"); exit (0); #endif #if defined (hp300) && !defined (hpux) printf ("m68k-hp-bsd\n"); exit (0); #endif #if defined (NeXT) #if !defined (__ARCHITECTURE__) #define __ARCHITECTURE__ "m68k" #endif int version; version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; if (version < 4) printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); else printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); exit (0); #endif #if defined (MULTIMAX) || defined (n16) #if defined (UMAXV) printf ("ns32k-encore-sysv\n"); exit (0); #else #if defined (CMU) printf ("ns32k-encore-mach\n"); exit (0); #else printf ("ns32k-encore-bsd\n"); exit (0); #endif #endif #endif #if defined (__386BSD__) printf ("i386-pc-bsd\n"); exit (0); #endif #if defined (sequent) #if defined (i386) printf ("i386-sequent-dynix\n"); exit (0); #endif #if defined (ns32000) printf ("ns32k-sequent-dynix\n"); exit (0); #endif #endif #if defined (_SEQUENT_) struct utsname un; uname(&un); if (strncmp(un.version, "V2", 2) == 0) { printf ("i386-sequent-ptx2\n"); exit (0); } if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ printf ("i386-sequent-ptx1\n"); exit (0); } printf ("i386-sequent-ptx\n"); exit (0); #endif #if defined (vax) # if !defined (ultrix) # include # if defined (BSD) # if BSD == 43 printf ("vax-dec-bsd4.3\n"); exit (0); # else # if BSD == 199006 printf ("vax-dec-bsd4.3reno\n"); exit (0); # else printf ("vax-dec-bsd\n"); exit (0); # endif # endif # else printf ("vax-dec-bsd\n"); exit (0); # endif # else printf ("vax-dec-ultrix\n"); exit (0); # endif #endif #if defined (alliant) && defined (i860) printf ("i860-alliant-bsd\n"); exit (0); #endif exit (1); } EOF $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` && { echo "$SYSTEM_NAME"; exit; } # Apollos put the system type in the environment. test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; } # Convex versions that predate uname can use getsysinfo(1) if [ -x /usr/convex/getsysinfo ] then case `getsysinfo -f cpu_type` in c1*) echo c1-convex-bsd exit ;; c2*) if getsysinfo -f scalar_acc then echo c32-convex-bsd else echo c2-convex-bsd fi exit ;; c34*) echo c34-convex-bsd exit ;; c38*) echo c38-convex-bsd exit ;; c4*) echo c4-convex-bsd exit ;; esac fi cat >&2 < in order to provide the needed information to handle your system. config.guess timestamp = $timestamp uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` /bin/uname -X = `(/bin/uname -X) 2>/dev/null` hostinfo = `(hostinfo) 2>/dev/null` /bin/universe = `(/bin/universe) 2>/dev/null` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` /bin/arch = `(/bin/arch) 2>/dev/null` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` UNAME_MACHINE = ${UNAME_MACHINE} UNAME_RELEASE = ${UNAME_RELEASE} UNAME_SYSTEM = ${UNAME_SYSTEM} UNAME_VERSION = ${UNAME_VERSION} EOF exit 1 # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: xorp/LICENSE0000664000076400007640000000143011421137511012657 0ustar greearbgreearb With the exception of code derived from other sources, all XORP software committed prior to April 23, 2010 is copyrighted by XORP, Inc. [Copyright (c) 2001-2009 XORP, Inc.]. Changes committed on or after April 23, 2010 are copyrighted by the author unless otherwise specified in the commit message. Files containing derived software are listed in the "LICENSE.other" file together with their corresponding copyrights and original licenses. All XORP software is licensed under the GNU General Public License, Version 2, June 1991 contained in the "LICENSE.gpl" file unless otherwise indicated in the source file. Software in source files that refer to the "LICENSE.lgpl" file is licensed under the GNU Lesser General Public License, Version 2.1, February 1999 contained in "LICENSE.lgpl". xorp/preinst0000755000076400007640000000054711665020734013302 0ustar greearbgreearb#!/bin/bash set -x adduser xorp || true adduser xorp xorp || true if [ ! -f /usr/lib/libpcap.so.0.9 ]; then ln -s /usr/lib/libpcap.so.0.9.8 /usr/lib/libpcap.so.0.9; fi if [ ! -f /lib/libtinfo.so.5 ]; then ln -s /lib/libncurses.so.5 /lib/libtinfo.so.5; fi if [ ! -f /usr/lib/libcrypto.so.6 ]; then ln -s /usr/lib/libcrypto.so.0.9.* /usr/lib/libcrypto.so.6; fi xorp/config.sub0000775000076400007640000010303611421137511013642 0ustar greearbgreearb#! /bin/sh # Configuration validation subroutine script. # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, # 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 # Free Software Foundation, Inc. timestamp='2009-10-07' # This file is (in principle) common to ALL GNU software. # The presence of a machine in this file suggests that SOME GNU software # can handle that machine. It does not imply ALL GNU software can. # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA # 02110-1301, USA. # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # Please send patches to . Submit a context # diff and a properly formatted GNU ChangeLog entry. # # Configuration subroutine to validate and canonicalize a configuration type. # Supply the specified configuration type as an argument. # If it is invalid, we print an error message on stderr and exit with code 1. # Otherwise, we print the canonical config type on stdout and succeed. # You can get the latest version of this script from: # http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD # This file is supposed to be the same for all GNU packages # and recognize all the CPU types, system types and aliases # that are meaningful with *any* GNU software. # Each package is responsible for reporting which valid configurations # it does not support. The user should be able to distinguish # a failure to support a valid configuration from a meaningless # configuration. # The goal of this file is to map all the various variations of a given # machine specification into a single specification in the form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM # or in some cases, the newer four-part form: # CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM # It is wrong to echo any other type of specification. me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] CPU-MFR-OPSYS $0 [OPTION] ALIAS Canonicalize a configuration name. Operation modes: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.sub ($timestamp) Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" exit 1 ;; *local*) # First pass through any local machine types. echo $1 exit ;; * ) break ;; esac done case $# in 0) echo "$me: missing argument$help" >&2 exit 1;; 1) ;; *) echo "$me: too many arguments$help" >&2 exit 1;; esac # Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). # Here we must recognize all the valid KERNEL-OS combinations. maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` case $maybe_os in nto-qnx* | linux-gnu* | linux-dietlibc | linux-newlib* | linux-uclibc* | \ uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* | \ kopensolaris*-gnu* | \ storm-chaos* | os2-emx* | rtmk-nova*) os=-$maybe_os basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` ;; *) basic_machine=`echo $1 | sed 's/-[^-]*$//'` if [ $basic_machine != $1 ] then os=`echo $1 | sed 's/.*-/-/'` else os=; fi ;; esac ### Let's recognize common machines as not being operating systems so ### that things like config.sub decstation-3100 work. We also ### recognize some manufacturers as not being operating systems, so we ### can provide default operating systems below. case $os in -sun*os*) # Prevent following clause from handling this invalid input. ;; -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ -apple | -axis | -knuth | -cray | -microblaze) os= basic_machine=$1 ;; -bluegene*) os=-cnk ;; -sim | -cisco | -oki | -wec | -winbond) os= basic_machine=$1 ;; -scout) ;; -wrs) os=-vxworks basic_machine=$1 ;; -chorusos*) os=-chorusos basic_machine=$1 ;; -chorusrdb) os=-chorusrdb basic_machine=$1 ;; -hiux*) os=-hiuxwe2 ;; -sco6) os=-sco5v6 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco5) os=-sco3.2v5 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco4) os=-sco3.2v4 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco3.2.[4-9]*) os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco3.2v[4-9]*) # Don't forget version if it is 3.2v4 or newer. basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco5v6*) # Don't forget version if it is 3.2v4 or newer. basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco*) os=-sco3.2v2 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -udk*) basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -isc) os=-isc2.2 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -clix*) basic_machine=clipper-intergraph ;; -isc*) basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -lynx*) os=-lynxos ;; -ptx*) basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` ;; -windowsnt*) os=`echo $os | sed -e 's/windowsnt/winnt/'` ;; -psos*) os=-psos ;; -mint | -mint[0-9]*) basic_machine=m68k-atari os=-mint ;; esac # Decode aliases for certain CPU-COMPANY combinations. case $basic_machine in # Recognize the basic CPU types without company name. # Some are omitted here because they have special meanings below. 1750a | 580 \ | a29k \ | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ | am33_2.0 \ | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr | avr32 \ | bfin \ | c4x | clipper \ | d10v | d30v | dlx | dsp16xx \ | fido | fr30 | frv \ | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ | i370 | i860 | i960 | ia64 \ | ip2k | iq2000 \ | lm32 \ | m32c | m32r | m32rle | m68000 | m68k | m88k \ | maxq | mb | microblaze | mcore | mep | metag \ | mips | mipsbe | mipseb | mipsel | mipsle \ | mips16 \ | mips64 | mips64el \ | mips64octeon | mips64octeonel \ | mips64orion | mips64orionel \ | mips64r5900 | mips64r5900el \ | mips64vr | mips64vrel \ | mips64vr4100 | mips64vr4100el \ | mips64vr4300 | mips64vr4300el \ | mips64vr5000 | mips64vr5000el \ | mips64vr5900 | mips64vr5900el \ | mipsisa32 | mipsisa32el \ | mipsisa32r2 | mipsisa32r2el \ | mipsisa64 | mipsisa64el \ | mipsisa64r2 | mipsisa64r2el \ | mipsisa64sb1 | mipsisa64sb1el \ | mipsisa64sr71k | mipsisa64sr71kel \ | mipstx39 | mipstx39el \ | mn10200 | mn10300 \ | moxie \ | mt \ | msp430 \ | nios | nios2 \ | ns16k | ns32k \ | or32 \ | pdp10 | pdp11 | pj | pjl \ | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \ | pyramid \ | rx \ | score \ | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ | sh64 | sh64le \ | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ | spu | strongarm \ | tahoe | thumb | tic4x | tic80 | tron \ | v850 | v850e \ | we32k \ | x86 | xc16x | xscale | xscalee[bl] | xstormy16 | xtensa \ | z8k | z80) basic_machine=$basic_machine-unknown ;; m6811 | m68hc11 | m6812 | m68hc12 | picochip) # Motorola 68HC11/12. basic_machine=$basic_machine-unknown os=-none ;; m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) ;; ms1) basic_machine=mt-unknown ;; # We use `pc' rather than `unknown' # because (1) that's what they normally are, and # (2) the word "unknown" tends to confuse beginning users. i*86 | x86_64) basic_machine=$basic_machine-pc ;; # Object if more than one company name word. *-*-*) echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 exit 1 ;; # Recognize the basic CPU types with company name. 580-* \ | a29k-* \ | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \ | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ | avr-* | avr32-* \ | bfin-* | bs2000-* \ | c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \ | clipper-* | craynv-* | cydra-* \ | d10v-* | d30v-* | dlx-* \ | elxsi-* \ | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \ | h8300-* | h8500-* \ | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ | i*86-* | i860-* | i960-* | ia64-* \ | ip2k-* | iq2000-* \ | lm32-* \ | m32c-* | m32r-* | m32rle-* \ | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ | m88110-* | m88k-* | maxq-* | mcore-* | metag-* | microblaze-* \ | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ | mips16-* \ | mips64-* | mips64el-* \ | mips64octeon-* | mips64octeonel-* \ | mips64orion-* | mips64orionel-* \ | mips64r5900-* | mips64r5900el-* \ | mips64vr-* | mips64vrel-* \ | mips64vr4100-* | mips64vr4100el-* \ | mips64vr4300-* | mips64vr4300el-* \ | mips64vr5000-* | mips64vr5000el-* \ | mips64vr5900-* | mips64vr5900el-* \ | mipsisa32-* | mipsisa32el-* \ | mipsisa32r2-* | mipsisa32r2el-* \ | mipsisa64-* | mipsisa64el-* \ | mipsisa64r2-* | mipsisa64r2el-* \ | mipsisa64sb1-* | mipsisa64sb1el-* \ | mipsisa64sr71k-* | mipsisa64sr71kel-* \ | mipstx39-* | mipstx39el-* \ | mmix-* \ | mt-* \ | msp430-* \ | nios-* | nios2-* \ | none-* | np1-* | ns16k-* | ns32k-* \ | orion-* \ | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \ | pyramid-* \ | romp-* | rs6000-* | rx-* \ | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ | sparclite-* \ | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | strongarm-* | sv1-* | sx?-* \ | tahoe-* | thumb-* \ | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* | tile-* \ | tron-* \ | v850-* | v850e-* | vax-* \ | we32k-* \ | x86-* | x86_64-* | xc16x-* | xps100-* | xscale-* | xscalee[bl]-* \ | xstormy16-* | xtensa*-* \ | ymp-* \ | z8k-* | z80-*) ;; # Recognize the basic CPU types without company name, with glob match. xtensa*) basic_machine=$basic_machine-unknown ;; # Recognize the various machine names and aliases which stand # for a CPU type and a company and sometimes even an OS. 386bsd) basic_machine=i386-unknown os=-bsd ;; 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) basic_machine=m68000-att ;; 3b*) basic_machine=we32k-att ;; a29khif) basic_machine=a29k-amd os=-udi ;; abacus) basic_machine=abacus-unknown ;; adobe68k) basic_machine=m68010-adobe os=-scout ;; alliant | fx80) basic_machine=fx80-alliant ;; altos | altos3068) basic_machine=m68k-altos ;; am29k) basic_machine=a29k-none os=-bsd ;; amd64) basic_machine=x86_64-pc ;; amd64-*) basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'` ;; amdahl) basic_machine=580-amdahl os=-sysv ;; amiga | amiga-*) basic_machine=m68k-unknown ;; amigaos | amigados) basic_machine=m68k-unknown os=-amigaos ;; amigaunix | amix) basic_machine=m68k-unknown os=-sysv4 ;; apollo68) basic_machine=m68k-apollo os=-sysv ;; apollo68bsd) basic_machine=m68k-apollo os=-bsd ;; aros) basic_machine=i386-pc os=-aros ;; aux) basic_machine=m68k-apple os=-aux ;; balance) basic_machine=ns32k-sequent os=-dynix ;; blackfin) basic_machine=bfin-unknown os=-linux ;; blackfin-*) basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'` os=-linux ;; bluegene*) basic_machine=powerpc-ibm os=-cnk ;; c90) basic_machine=c90-cray os=-unicos ;; cegcc) basic_machine=arm-unknown os=-cegcc ;; convex-c1) basic_machine=c1-convex os=-bsd ;; convex-c2) basic_machine=c2-convex os=-bsd ;; convex-c32) basic_machine=c32-convex os=-bsd ;; convex-c34) basic_machine=c34-convex os=-bsd ;; convex-c38) basic_machine=c38-convex os=-bsd ;; cray | j90) basic_machine=j90-cray os=-unicos ;; craynv) basic_machine=craynv-cray os=-unicosmp ;; cr16) basic_machine=cr16-unknown os=-elf ;; crds | unos) basic_machine=m68k-crds ;; crisv32 | crisv32-* | etraxfs*) basic_machine=crisv32-axis ;; cris | cris-* | etrax*) basic_machine=cris-axis ;; crx) basic_machine=crx-unknown os=-elf ;; da30 | da30-*) basic_machine=m68k-da30 ;; decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) basic_machine=mips-dec ;; decsystem10* | dec10*) basic_machine=pdp10-dec os=-tops10 ;; decsystem20* | dec20*) basic_machine=pdp10-dec os=-tops20 ;; delta | 3300 | motorola-3300 | motorola-delta \ | 3300-motorola | delta-motorola) basic_machine=m68k-motorola ;; delta88) basic_machine=m88k-motorola os=-sysv3 ;; dicos) basic_machine=i686-pc os=-dicos ;; djgpp) basic_machine=i586-pc os=-msdosdjgpp ;; dpx20 | dpx20-*) basic_machine=rs6000-bull os=-bosx ;; dpx2* | dpx2*-bull) basic_machine=m68k-bull os=-sysv3 ;; ebmon29k) basic_machine=a29k-amd os=-ebmon ;; elxsi) basic_machine=elxsi-elxsi os=-bsd ;; encore | umax | mmax) basic_machine=ns32k-encore ;; es1800 | OSE68k | ose68k | ose | OSE) basic_machine=m68k-ericsson os=-ose ;; fx2800) basic_machine=i860-alliant ;; genix) basic_machine=ns32k-ns ;; gmicro) basic_machine=tron-gmicro os=-sysv ;; go32) basic_machine=i386-pc os=-go32 ;; h3050r* | hiux*) basic_machine=hppa1.1-hitachi os=-hiuxwe2 ;; h8300hms) basic_machine=h8300-hitachi os=-hms ;; h8300xray) basic_machine=h8300-hitachi os=-xray ;; h8500hms) basic_machine=h8500-hitachi os=-hms ;; harris) basic_machine=m88k-harris os=-sysv3 ;; hp300-*) basic_machine=m68k-hp ;; hp300bsd) basic_machine=m68k-hp os=-bsd ;; hp300hpux) basic_machine=m68k-hp os=-hpux ;; hp3k9[0-9][0-9] | hp9[0-9][0-9]) basic_machine=hppa1.0-hp ;; hp9k2[0-9][0-9] | hp9k31[0-9]) basic_machine=m68000-hp ;; hp9k3[2-9][0-9]) basic_machine=m68k-hp ;; hp9k6[0-9][0-9] | hp6[0-9][0-9]) basic_machine=hppa1.0-hp ;; hp9k7[0-79][0-9] | hp7[0-79][0-9]) basic_machine=hppa1.1-hp ;; hp9k78[0-9] | hp78[0-9]) # FIXME: really hppa2.0-hp basic_machine=hppa1.1-hp ;; hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) # FIXME: really hppa2.0-hp basic_machine=hppa1.1-hp ;; hp9k8[0-9][13679] | hp8[0-9][13679]) basic_machine=hppa1.1-hp ;; hp9k8[0-9][0-9] | hp8[0-9][0-9]) basic_machine=hppa1.0-hp ;; hppa-next) os=-nextstep3 ;; hppaosf) basic_machine=hppa1.1-hp os=-osf ;; hppro) basic_machine=hppa1.1-hp os=-proelf ;; i370-ibm* | ibm*) basic_machine=i370-ibm ;; # I'm not sure what "Sysv32" means. Should this be sysv3.2? i*86v32) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-sysv32 ;; i*86v4*) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-sysv4 ;; i*86v) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-sysv ;; i*86sol2) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-solaris2 ;; i386mach) basic_machine=i386-mach os=-mach ;; i386-vsta | vsta) basic_machine=i386-unknown os=-vsta ;; iris | iris4d) basic_machine=mips-sgi case $os in -irix*) ;; *) os=-irix4 ;; esac ;; isi68 | isi) basic_machine=m68k-isi os=-sysv ;; m68knommu) basic_machine=m68k-unknown os=-linux ;; m68knommu-*) basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'` os=-linux ;; m88k-omron*) basic_machine=m88k-omron ;; magnum | m3230) basic_machine=mips-mips os=-sysv ;; merlin) basic_machine=ns32k-utek os=-sysv ;; microblaze) basic_machine=microblaze-xilinx ;; mingw32) basic_machine=i386-pc os=-mingw32 ;; mingw32ce) basic_machine=arm-unknown os=-mingw32ce ;; miniframe) basic_machine=m68000-convergent ;; *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) basic_machine=m68k-atari os=-mint ;; mips3*-*) basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` ;; mips3*) basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown ;; monitor) basic_machine=m68k-rom68k os=-coff ;; morphos) basic_machine=powerpc-unknown os=-morphos ;; msdos) basic_machine=i386-pc os=-msdos ;; ms1-*) basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'` ;; mvs) basic_machine=i370-ibm os=-mvs ;; ncr3000) basic_machine=i486-ncr os=-sysv4 ;; netbsd386) basic_machine=i386-unknown os=-netbsd ;; netwinder) basic_machine=armv4l-rebel os=-linux ;; news | news700 | news800 | news900) basic_machine=m68k-sony os=-newsos ;; news1000) basic_machine=m68030-sony os=-newsos ;; news-3600 | risc-news) basic_machine=mips-sony os=-newsos ;; necv70) basic_machine=v70-nec os=-sysv ;; next | m*-next ) basic_machine=m68k-next case $os in -nextstep* ) ;; -ns2*) os=-nextstep2 ;; *) os=-nextstep3 ;; esac ;; nh3000) basic_machine=m68k-harris os=-cxux ;; nh[45]000) basic_machine=m88k-harris os=-cxux ;; nindy960) basic_machine=i960-intel os=-nindy ;; mon960) basic_machine=i960-intel os=-mon960 ;; nonstopux) basic_machine=mips-compaq os=-nonstopux ;; np1) basic_machine=np1-gould ;; nsr-tandem) basic_machine=nsr-tandem ;; op50n-* | op60c-*) basic_machine=hppa1.1-oki os=-proelf ;; openrisc | openrisc-*) basic_machine=or32-unknown ;; os400) basic_machine=powerpc-ibm os=-os400 ;; OSE68000 | ose68000) basic_machine=m68000-ericsson os=-ose ;; os68k) basic_machine=m68k-none os=-os68k ;; pa-hitachi) basic_machine=hppa1.1-hitachi os=-hiuxwe2 ;; paragon) basic_machine=i860-intel os=-osf ;; parisc) basic_machine=hppa-unknown os=-linux ;; parisc-*) basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'` os=-linux ;; pbd) basic_machine=sparc-tti ;; pbb) basic_machine=m68k-tti ;; pc532 | pc532-*) basic_machine=ns32k-pc532 ;; pc98) basic_machine=i386-pc ;; pc98-*) basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentium | p5 | k5 | k6 | nexgen | viac3) basic_machine=i586-pc ;; pentiumpro | p6 | 6x86 | athlon | athlon_*) basic_machine=i686-pc ;; pentiumii | pentium2 | pentiumiii | pentium3) basic_machine=i686-pc ;; pentium4) basic_machine=i786-pc ;; pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentiumpro-* | p6-* | 6x86-* | athlon-*) basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentium4-*) basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pn) basic_machine=pn-gould ;; power) basic_machine=power-ibm ;; ppc) basic_machine=powerpc-unknown ;; ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ppcle | powerpclittle | ppc-le | powerpc-little) basic_machine=powerpcle-unknown ;; ppcle-* | powerpclittle-*) basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ppc64) basic_machine=powerpc64-unknown ;; ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ppc64le | powerpc64little | ppc64-le | powerpc64-little) basic_machine=powerpc64le-unknown ;; ppc64le-* | powerpc64little-*) basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ps2) basic_machine=i386-ibm ;; pw32) basic_machine=i586-unknown os=-pw32 ;; rdos) basic_machine=i386-pc os=-rdos ;; rom68k) basic_machine=m68k-rom68k os=-coff ;; rm[46]00) basic_machine=mips-siemens ;; rtpc | rtpc-*) basic_machine=romp-ibm ;; s390 | s390-*) basic_machine=s390-ibm ;; s390x | s390x-*) basic_machine=s390x-ibm ;; sa29200) basic_machine=a29k-amd os=-udi ;; sb1) basic_machine=mipsisa64sb1-unknown ;; sb1el) basic_machine=mipsisa64sb1el-unknown ;; sde) basic_machine=mipsisa32-sde os=-elf ;; sei) basic_machine=mips-sei os=-seiux ;; sequent) basic_machine=i386-sequent ;; sh) basic_machine=sh-hitachi os=-hms ;; sh5el) basic_machine=sh5le-unknown ;; sh64) basic_machine=sh64-unknown ;; sparclite-wrs | simso-wrs) basic_machine=sparclite-wrs os=-vxworks ;; sps7) basic_machine=m68k-bull os=-sysv2 ;; spur) basic_machine=spur-unknown ;; st2000) basic_machine=m68k-tandem ;; stratus) basic_machine=i860-stratus os=-sysv4 ;; sun2) basic_machine=m68000-sun ;; sun2os3) basic_machine=m68000-sun os=-sunos3 ;; sun2os4) basic_machine=m68000-sun os=-sunos4 ;; sun3os3) basic_machine=m68k-sun os=-sunos3 ;; sun3os4) basic_machine=m68k-sun os=-sunos4 ;; sun4os3) basic_machine=sparc-sun os=-sunos3 ;; sun4os4) basic_machine=sparc-sun os=-sunos4 ;; sun4sol2) basic_machine=sparc-sun os=-solaris2 ;; sun3 | sun3-*) basic_machine=m68k-sun ;; sun4) basic_machine=sparc-sun ;; sun386 | sun386i | roadrunner) basic_machine=i386-sun ;; sv1) basic_machine=sv1-cray os=-unicos ;; symmetry) basic_machine=i386-sequent os=-dynix ;; t3e) basic_machine=alphaev5-cray os=-unicos ;; t90) basic_machine=t90-cray os=-unicos ;; tic54x | c54x*) basic_machine=tic54x-unknown os=-coff ;; tic55x | c55x*) basic_machine=tic55x-unknown os=-coff ;; tic6x | c6x*) basic_machine=tic6x-unknown os=-coff ;; tile*) basic_machine=tile-unknown os=-linux-gnu ;; tx39) basic_machine=mipstx39-unknown ;; tx39el) basic_machine=mipstx39el-unknown ;; toad1) basic_machine=pdp10-xkl os=-tops20 ;; tower | tower-32) basic_machine=m68k-ncr ;; tpf) basic_machine=s390x-ibm os=-tpf ;; udi29k) basic_machine=a29k-amd os=-udi ;; ultra3) basic_machine=a29k-nyu os=-sym1 ;; v810 | necv810) basic_machine=v810-nec os=-none ;; vaxv) basic_machine=vax-dec os=-sysv ;; vms) basic_machine=vax-dec os=-vms ;; vpp*|vx|vx-*) basic_machine=f301-fujitsu ;; vxworks960) basic_machine=i960-wrs os=-vxworks ;; vxworks68) basic_machine=m68k-wrs os=-vxworks ;; vxworks29k) basic_machine=a29k-wrs os=-vxworks ;; w65*) basic_machine=w65-wdc os=-none ;; w89k-*) basic_machine=hppa1.1-winbond os=-proelf ;; xbox) basic_machine=i686-pc os=-mingw32 ;; xps | xps100) basic_machine=xps100-honeywell ;; ymp) basic_machine=ymp-cray os=-unicos ;; z8k-*-coff) basic_machine=z8k-unknown os=-sim ;; z80-*-coff) basic_machine=z80-unknown os=-sim ;; none) basic_machine=none-none os=-none ;; # Here we handle the default manufacturer of certain CPU types. It is in # some cases the only manufacturer, in others, it is the most popular. w89k) basic_machine=hppa1.1-winbond ;; op50n) basic_machine=hppa1.1-oki ;; op60c) basic_machine=hppa1.1-oki ;; romp) basic_machine=romp-ibm ;; mmix) basic_machine=mmix-knuth ;; rs6000) basic_machine=rs6000-ibm ;; vax) basic_machine=vax-dec ;; pdp10) # there are many clones, so DEC is not a safe bet basic_machine=pdp10-unknown ;; pdp11) basic_machine=pdp11-dec ;; we32k) basic_machine=we32k-att ;; sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele) basic_machine=sh-unknown ;; sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v) basic_machine=sparc-sun ;; cydra) basic_machine=cydra-cydrome ;; orion) basic_machine=orion-highlevel ;; orion105) basic_machine=clipper-highlevel ;; mac | mpw | mac-mpw) basic_machine=m68k-apple ;; pmac | pmac-mpw) basic_machine=powerpc-apple ;; *-unknown) # Make sure to match an already-canonicalized machine name. ;; *) echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 exit 1 ;; esac # Here we canonicalize certain aliases for manufacturers. case $basic_machine in *-digital*) basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` ;; *-commodore*) basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` ;; *) ;; esac # Decode manufacturer-specific aliases for certain operating systems. if [ x"$os" != x"" ] then case $os in # First match some system type aliases # that might get confused with valid system types. # -solaris* is a basic system type, with this one exception. -solaris1 | -solaris1.*) os=`echo $os | sed -e 's|solaris1|sunos4|'` ;; -solaris) os=-solaris2 ;; -svr4*) os=-sysv4 ;; -unixware*) os=-sysv4.2uw ;; -gnu/linux*) os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` ;; # First accept the basic system types. # The portable systems comes first. # Each alternative MUST END IN A *, to match a version number. # -sysv* is not here because it comes later, after sysvr4. -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\ | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \ | -kopensolaris* \ | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ | -aos* | -aros* \ | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \ | -openbsd* | -solidbsd* \ | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ | -chorusos* | -chorusrdb* | -cegcc* \ | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ | -mingw32* | -linux-gnu* | -linux-newlib* | -linux-uclibc* \ | -uxpv* | -beos* | -mpeix* | -udk* \ | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es*) # Remember, each alternative MUST END IN *, to match a version number. ;; -qnx*) case $basic_machine in x86-* | i*86-*) ;; *) os=-nto$os ;; esac ;; -nto-qnx*) ;; -nto*) os=`echo $os | sed -e 's|nto|nto-qnx|'` ;; -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \ | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) ;; -mac*) os=`echo $os | sed -e 's|mac|macos|'` ;; -linux-dietlibc) os=-linux-dietlibc ;; -linux*) os=`echo $os | sed -e 's|linux|linux-gnu|'` ;; -sunos5*) os=`echo $os | sed -e 's|sunos5|solaris2|'` ;; -sunos6*) os=`echo $os | sed -e 's|sunos6|solaris3|'` ;; -opened*) os=-openedition ;; -os400*) os=-os400 ;; -wince*) os=-wince ;; -osfrose*) os=-osfrose ;; -osf*) os=-osf ;; -utek*) os=-bsd ;; -dynix*) os=-bsd ;; -acis*) os=-aos ;; -atheos*) os=-atheos ;; -syllable*) os=-syllable ;; -386bsd) os=-bsd ;; -ctix* | -uts*) os=-sysv ;; -nova*) os=-rtmk-nova ;; -ns2 ) os=-nextstep2 ;; -nsk*) os=-nsk ;; # Preserve the version number of sinix5. -sinix5.*) os=`echo $os | sed -e 's|sinix|sysv|'` ;; -sinix*) os=-sysv4 ;; -tpf*) os=-tpf ;; -triton*) os=-sysv3 ;; -oss*) os=-sysv3 ;; -svr4) os=-sysv4 ;; -svr3) os=-sysv3 ;; -sysvr4) os=-sysv4 ;; # This must come after -sysvr4. -sysv*) ;; -ose*) os=-ose ;; -es1800*) os=-ose ;; -xenix) os=-xenix ;; -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) os=-mint ;; -aros*) os=-aros ;; -kaos*) os=-kaos ;; -zvmoe) os=-zvmoe ;; -dicos*) os=-dicos ;; -none) ;; *) # Get rid of the `-' at the beginning of $os. os=`echo $os | sed 's/[^-]*-//'` echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 exit 1 ;; esac else # Here we handle the default operating systems that come with various machines. # The value should be what the vendor currently ships out the door with their # machine or put another way, the most popular os provided with the machine. # Note that if you're going to try to match "-MANUFACTURER" here (say, # "-sun"), then you have to tell the case statement up towards the top # that MANUFACTURER isn't an operating system. Otherwise, code above # will signal an error saying that MANUFACTURER isn't an operating # system, and we'll never get to this point. case $basic_machine in score-*) os=-elf ;; spu-*) os=-elf ;; *-acorn) os=-riscix1.2 ;; arm*-rebel) os=-linux ;; arm*-semi) os=-aout ;; c4x-* | tic4x-*) os=-coff ;; # This must come before the *-dec entry. pdp10-*) os=-tops20 ;; pdp11-*) os=-none ;; *-dec | vax-*) os=-ultrix4.2 ;; m68*-apollo) os=-domain ;; i386-sun) os=-sunos4.0.2 ;; m68000-sun) os=-sunos3 # This also exists in the configure program, but was not the # default. # os=-sunos4 ;; m68*-cisco) os=-aout ;; mep-*) os=-elf ;; mips*-cisco) os=-elf ;; mips*-*) os=-elf ;; or32-*) os=-coff ;; *-tti) # must be before sparc entry or we get the wrong os. os=-sysv3 ;; sparc-* | *-sun) os=-sunos4.1.1 ;; *-be) os=-beos ;; *-haiku) os=-haiku ;; *-ibm) os=-aix ;; *-knuth) os=-mmixware ;; *-wec) os=-proelf ;; *-winbond) os=-proelf ;; *-oki) os=-proelf ;; *-hp) os=-hpux ;; *-hitachi) os=-hiux ;; i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) os=-sysv ;; *-cbm) os=-amigaos ;; *-dg) os=-dgux ;; *-dolphin) os=-sysv3 ;; m68k-ccur) os=-rtu ;; m88k-omron*) os=-luna ;; *-next ) os=-nextstep ;; *-sequent) os=-ptx ;; *-crds) os=-unos ;; *-ns) os=-genix ;; i370-*) os=-mvs ;; *-next) os=-nextstep3 ;; *-gould) os=-sysv ;; *-highlevel) os=-bsd ;; *-encore) os=-bsd ;; *-sgi) os=-irix ;; *-siemens) os=-sysv4 ;; *-masscomp) os=-rtu ;; f30[01]-fujitsu | f700-fujitsu) os=-uxpv ;; *-rom68k) os=-coff ;; *-*bug) os=-coff ;; *-apple) os=-macos ;; *-atari*) os=-mint ;; *) os=-none ;; esac fi # Here we handle the case where we know the os, and the CPU type, but not the # manufacturer. We pick the logical manufacturer. vendor=unknown case $basic_machine in *-unknown) case $os in -riscix*) vendor=acorn ;; -sunos*) vendor=sun ;; -cnk*|-aix*) vendor=ibm ;; -beos*) vendor=be ;; -hpux*) vendor=hp ;; -mpeix*) vendor=hp ;; -hiux*) vendor=hitachi ;; -unos*) vendor=crds ;; -dgux*) vendor=dg ;; -luna*) vendor=omron ;; -genix*) vendor=ns ;; -mvs* | -opened*) vendor=ibm ;; -os400*) vendor=ibm ;; -ptx*) vendor=sequent ;; -tpf*) vendor=ibm ;; -vxsim* | -vxworks* | -windiss*) vendor=wrs ;; -aux*) vendor=apple ;; -hms*) vendor=hitachi ;; -mpw* | -macos*) vendor=apple ;; -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) vendor=atari ;; -vos*) vendor=stratus ;; esac basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` ;; esac echo $basic_machine$os exit # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: xorp/rib/0000775000076400007640000000000011635757530012450 5ustar greearbgreearbxorp/rib/rib_xrl_shell_funcs.sh0000775000076400007640000000515111421137511017017 0ustar greearbgreearb#!/bin/sh # # $XORP: xorp/rib/xrl_shell_funcs.sh,v 1.10 2004/09/28 00:31:46 atanu Exp $ # CALLXRL=${CALLXRL:-../libxipc/call_xrl} make_rib_errors_fatal() { echo -n "make_rib_errors_fatal" $1 $CALLXRL "finder://rib/rib/0.1/make_errors_fatal" } add_igp_table4() { echo -n "add_igp_table4" $* $CALLXRL "finder://rib/rib/0.1/add_igp_table4?protocol:txt=$1&target_class:txt=$2&target_instance:txt=$3&unicast:bool=$4&multicast:bool=$5" } delete_igp_table4() { echo -n "delete_igp_table4" $* $CALLXRL "finder://rib/rib/0.1/delete_igp_table4?protocol:txt=$1&target_class:txt=$2&target_instance:txt=$3&unicast:bool=$4&multicast:bool=$5" } add_egp_table4() { echo -n "add_egp_table4" $* $CALLXRL "finder://rib/rib/0.1/add_egp_table4?protocol:txt=$1&target_class:txt=$2&target_instance:txt=$3&unicast:bool=$4&multicast:bool=$5" } delete_egp_table4() { echo -n "delete_egp_table4" $* $CALLXRL "finder://rib/rib/0.1/delete_egp_table4?protocol:txt=$1&target_class:txt=$2&target_instance:txt=$3&unicast:bool=$4&multicast:bool=$5" } new_vif() { echo -n "new_vif" $1 $CALLXRL "finder://rib/rib/0.1/new_vif?name:txt=$1" } add_vif_addr4() { echo -n "add_vif_addr4" $* $CALLXRL "finder://rib/rib/0.1/add_vif_addr4?name:txt=$1&addr:ipv4=$2&subnet:ipv4net=$3" } add_vif_addr6() { echo -n "add_vif_addr6" $* $CALLXRL "finder://rib/rib/0.1/add_vif_addr6?name:txt=$1&addr:ipv6=$2&subnet:ipv6net=$3" } add_route4() { echo -n "add_route4" $* PT=":u32=0"; if [ $# -ge 7 ] then PT=${PT},$7 fi $CALLXRL "finder://rib/rib/0.1/add_route4?protocol:txt=$1&unicast:bool=$2&multicast:bool=$3&network:ipv4net=$4&nexthop:ipv4=$5&metric:u32=$6&policytags:list=$PT" } add_route6() { echo -n "add_route6" $* PT=":u32=0"; if [ $# -ge 7 ] then PT=${PT},$7 fi $CALLXRL "finder://rib/rib/0.1/add_route6?protocol:txt=$1&unicast:bool=$2&multicast:bool=$3&network:ipv6net=$4&nexthop:ipv6=$5&metric:u32=$6&policytags:list=$PT" } delete_route4() { echo -n "delete_route4" $* $CALLXRL "finder://rib/rib/0.1/delete_route4?protocol:txt=$1&unicast:bool=$2&multicast:bool=$3&network:ipv4net=$4" } delete_route6() { echo -n "delete_route6" $* $CALLXRL "finder://rib/rib/0.1/delete_route6?protocol:txt=$1&unicast:bool=$2&multicast:bool=$3&network:ipv6net=$4" } lookup_route_by_dest4() { echo -n "lookup_route_by_dest4" $* $CALLXRL "finder://rib/rib/0.1/lookup_route_by_dest4?addr:ipv4=$1&unicast:bool=$2&multicast:bool=$3" } # We have arguments. if [ $# != 0 ] then $* fi # Local Variables: # mode: shell-script # sh-indentation: 4 # End: xorp/rib/rt_tab_extint.hh0000664000076400007640000002052111421137511015617 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/rib/rt_tab_extint.hh,v 1.18 2008/10/02 21:58:12 bms Exp $ #ifndef __RIB_RT_TAB_EXTINT_HH__ #define __RIB_RT_TAB_EXTINT_HH__ #include "rt_tab_base.hh" /** * @short Make two route @ref RouteTables behave like one, while * resolving nexthops that are not immediate neighbors * * ExtIntTable takes two routing tables and combines them together to * form a single table, where routes for the same subnet with a lower * admin distance override those with a higher admin distance. The * two parent tables are different: the Internal table takes routes * only from IGP protocols, and so the nexthops of routes it provides * are always immediate neighbors. The External table takes routes * from EGP protocols, and so the nexthops of routes it provides may * not be immediate neighbors. The ExtIntTable resolves the nexthops * of all routes it propagates downstream. If a nexthop cannot be * resolved, it is not propagated downstream, but is stored pending * the arrival of a route that would permit the nexthop to be * resolved. * * An add_route request from a parent tables causes a lookup on the * other parent table. If the route is better than the one from the * other table, or no route exists in the other table, then the new * route is passed downstream. * * An delete_route request from a parent table also causes a lookup on * the other parent table. The delete_route is propagated downstream. * If an alternative route is found, then that is then propagated * downsteam as an add_route to replace the deleted route. * * Lookups from downsteam cause lookups on both parent tables. The * better response is given. * * A RIB normally only has one ExtIntTable. */ template class ExtIntTable : public RouteTable
{ public: /** * ExtIntTable Constructor. * * @param ext_table parent RouteTables supplying EGP routes. * @param int_table parent RouteTables supplying IGP routes. */ ExtIntTable(RouteTable* ext_table, RouteTable* int_table); /** * An add_route request from a parent table causes a lookup on the * other parent table. If the route is better than the one from the * other table, or no route exists in the other table then it is * passed downstream if nexthop resolution is successful. * * @param route the new route. * @param caller the parent table sending the new route. * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_route(const IPRouteEntry& route, RouteTable* caller); /** * An delete_route request from a parent table also causes a * lookup on the other parent table. The delete_route is * propagated downstream. If an alternative route is found and * nexthop resolution on it is successful, then it is then * propagated downsteam as an add_route to replace the deleted * route. * * @param route the route to be deleted. * @param caller the parent table sending the delete_route. * @return XORP_OK on success, otherwise XORP_ERROR. */ int delete_route(const IPRouteEntry* route, RouteTable* caller); /** * Lookup a specific subnet. The lookup will first look in the * ExtIntTable 's table of resolved routes - if there is a route * here, then this is returned. Otherwise the lookup will be sent * to both parent tables. If both give an answer, then the route * with the better admin_distance is returned, so long as it gives * a nexthop that is resolvable. * * @param net the subnet to look up. * @return a pointer to the route entry if it exists, NULL otherwise. */ const IPRouteEntry* lookup_route(const IPNet& net) const; /** * Lookup an IP address to get the most specific (longest prefix * length) route that matches this address. The lookup will be * sent to both parent tables and the ExtIntTable's internal table * of resolved_routes. The most specific answer is returned, so * long as the nexthop resolves. If more than one route has the * same prefix length, then the route with the better * admin_distance is returned. * * @param addr the IP address to look up. * * @return a pointer to the best most specific route entry if any * entry matches and its nexthop resolves, NULL otherwise. */ const IPRouteEntry* lookup_route(const A& addr) const; /** * Lookup an IP address to get the most specific (longest prefix * length) route that matches this address, along with the * RouteRange information for this address and route. As with * lookup_route, this involves querying ExtIntTable's * resolved_routes table and possibly both parent tables. The * best, most specific route is returned if the nexthop is * resolvable, and the tightest bounds on the answer are returned. * * @see RouteRange * @param addr the IP address to look up. * @return a pointer to a RouteRange class instance containing the * relevant answer. It is up to the recipient of this pointer to * free the associated memory. */ RouteRange* lookup_route_range(const A& addr) const; /** * @return the table type (@ref TableType). */ TableType type() const { return EXTINT_TABLE; } /** * replumb the parent tables, so that new_parent replaces old_parent * * @param old_parent the old parent table. * @param new_parent the new parent table. */ void replumb(RouteTable* old_parent, RouteTable* new_parent); /** * Render this ExtIntTable as a string for debugging purposes. */ string str() const; private: typedef typename ResolvedIPRouteEntry::RouteBackLink ResolvedRouteBackLink; typedef typename UnresolvedIPRouteEntry::RouteBackLink UnresolvedRouteBackLink; int delete_ext_route(const IPRouteEntry* route, bool& is_delete_propagated); const ResolvedIPRouteEntry* lookup_in_resolved_table( const IPNet& ipv4net); void resolve_unresolved_nexthops(const IPRouteEntry& route); const ResolvedIPRouteEntry* resolve_and_store_route( const IPRouteEntry& route, const IPRouteEntry* nexthop_route); bool delete_unresolved_nexthop(const IPRouteEntry* route); void recalculate_nexthops(const IPRouteEntry& route); const ResolvedIPRouteEntry* lookup_by_igp_parent( const IPRouteEntry* route); const ResolvedIPRouteEntry* lookup_next_by_igp_parent( const IPRouteEntry* route, const ResolvedIPRouteEntry* previous); const IPRouteEntry* lookup_route_in_igp_parent( const IPNet& subnet) const; const IPRouteEntry* lookup_route_in_igp_parent(const A& addr) const; const IPRouteEntry* lookup_route_in_egp_parent( const IPNet& subnet) const; const IPRouteEntry* lookup_route_in_egp_parent(const A& addr) const; RouteTable* _ext_table; RouteTable* _int_table; Trie* > _ip_route_table; multimap* > _ip_unresolved_nexthops; map, UnresolvedIPRouteEntry* > _ip_unresolved_table; // _ip_igp_parents gives us a fast way of finding a route // affected by a change in an igp parent route multimap*, ResolvedIPRouteEntry* > _ip_igp_parents; // _resolving_routes is a Trie of all the routes that are used to // resolve external routes Trie* > _resolving_routes; }; #endif // __RIB_RT_TAB_EXTINT_HH__ xorp/rib/add_route.cc0000664000076400007640000001651711540224234014720 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // Add routes to the RIB. // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #include "rib_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/callback.hh" #include "libxorp/ipv4.hh" #include "libxorp/timeval.hh" #include "libxorp/timer.hh" #ifdef HAVE_GETOPT_H #include #endif #include "libxipc/xrl_std_router.hh" #include "xrl/interfaces/rib_xif.hh" template class Route { public: bool add; IPNet net; A nexthop; }; template class Fire { public: typedef vector > Routes; Fire(XrlStdRouter& xrl_router, Routes &r, string ribname, string tablename, bool max_inflight) : _xrl_router(xrl_router), _r(r), _ribname(ribname), _tablename(tablename), _flying(0), _max_inflight(max_inflight) {} void start() { _next = _r.begin(); send(); } bool busy() { return 0 != _flying; } private: bool send(); void done(const XrlError& error, string comment); private: XrlStdRouter &_xrl_router; Routes &_r; string _ribname; string _tablename; int _flying; // Number of XRLs in flight. bool _max_inflight; // Try and get the maximum // number of XRLs in flight. typename Routes::iterator _next; // Next route to send. }; template<> bool Fire::send(); void done(const XrlError& error, const char *comment) { debug_msg("callback %s %s\n", comment, error.str().c_str()); if(XrlError::OKAY() != error) { XLOG_WARNING("callback: %s %s", comment, error.str().c_str()); } } int read_routes(const string& route_file, Fire::Routes& v) { // // Read in the file of routes to send // // The file contains: // Subnet followed by nexthop std::ifstream routein(route_file.c_str()); if (!routein) { XLOG_ERROR("Failed to open file: %s %s", route_file.c_str(), strerror(errno)); return -1; } while (routein) { Route r; string which; routein >> which; if ("add" == which) { r.add = true; } else if ("delete" == which) { r.add = false; } else if ("#" == which) { char buf[1024]; routein.getline(buf, sizeof(buf)); continue; } else if ("" == which) { break; } else { XLOG_ERROR("Allowed command are \"add\" or \"delete\"" " not <%s>", which.c_str()); return -1; } string word; routein >> word; r.net = IPNet(word.c_str()); routein >> word; r.nexthop = IPv4(word.c_str()); v.push_back(r); } routein.close(); return 0; } int usage(const char *myname) { fprintf(stderr, "usage: %s -r routes [-t tablename] [-R][-u][-s][-d]\n", myname); return -1; } int main(int argc, char **argv) { XorpUnexpectedHandler x(xorp_unexpected_handler); // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); string route_file; string ribname = "rib"; string tablename = "ebgp"; bool repeat = false; bool max_inflight = false; bool silent = false; bool dont_add_table = false; int c; while ((c = getopt(argc, argv, "r:t:Rusd")) != -1) { switch (c) { case 'r': route_file = optarg; break; case 't': tablename = optarg; break; case 'R': repeat = true; break; case 'u': max_inflight = true; break; case 's': silent = true; break; case 'd': dont_add_table = true; break; default: return usage(argv[0]); } } if (route_file == "") return usage(argv[0]); try { // // Init stuff // EventLoop eventloop; XrlStdRouter xrl_router(eventloop, "add_route"); debug_msg("Waiting for router"); xrl_router.finalize(); wait_until_xrl_router_is_ready(eventloop, xrl_router); debug_msg("\n"); // Create table. if (!dont_add_table) { XrlRibV0p1Client rib(&xrl_router); rib.send_add_egp_table4(ribname.c_str(), tablename, xrl_router.class_name(), xrl_router.instance_name(), true, true, callback(done, "add_table")); } Fire::Routes v; int ret = read_routes(route_file, v); if (0 != ret) return ret; Fire f(xrl_router, v, ribname, tablename, max_inflight); // // Main loop // do { TimeVal start, end, delta; TimerList::system_gettimeofday(&start); f.start(); while (f.busy()) { eventloop.run(); } TimerList::system_gettimeofday(&end); delta = end - start; if (!silent) printf("%s seconds taken to add/delete %u routes\n", delta.str().c_str(), XORP_UINT_CAST(v.size())); } while (repeat); } catch (...) { xorp_catch_standard_exceptions(); } // // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); return 0; } template<> bool Fire::send() { if (_next == _r.end()) return false; XrlRibV0p1Client rib(&_xrl_router); bool sent; if (_next->add) { sent = rib.send_add_route4(_ribname.c_str(), _tablename, true /*unicast*/, false /*multicast*/, _next->net, _next->nexthop, 0/*metric*/, XrlAtomList(), callback(this, &Fire::done, c_format("add net: %s nexthop: %s", _next->net.str().c_str(), _next->nexthop.str(). c_str()))); } else { sent = rib.send_delete_route4(_ribname.c_str(), _tablename, true /*unicast*/, false /*multicast*/, _next->net, callback(this, &Fire::done, c_format("delete net: %s", _next->net.str().c_str() ))); } if (sent) { _next++; _flying++; } return sent; } template void Fire::done(const XrlError& error, string comment) { _flying--; debug_msg("callback %s\n %s\n", comment.c_str(), error.str().c_str()); switch (error.error_code()) { case OKAY: break; case REPLY_TIMED_OUT: case RESOLVE_FAILED: case SEND_FAILED: case SEND_FAILED_TRANSIENT: case NO_SUCH_METHOD: case NO_FINDER: case BAD_ARGS: case COMMAND_FAILED: case INTERNAL_ERROR: XLOG_ERROR("callback: %s\n%s", comment.c_str(), error.str().c_str()); break; } if (_max_inflight) { if (0 == _flying) { while(send()) ; printf("Flying %d\n", _flying); } } else { send(); } } xorp/rib/rib_module.h0000664000076400007640000000230011421137511014715 0ustar greearbgreearb/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- */ /* * Copyright (c) 2001-2009 XORP, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 2, June * 1991 as published by the Free Software Foundation. Redistribution * and/or modification of this program under the terms of any other * version of the GNU General Public License is not permitted. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, * see the GNU General Public License, Version 2, a copy of which can be * found in the XORP LICENSE.gpl file. * * XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; * http://xorp.net */ /* * $XORP: xorp/rib/rib_module.h,v 1.10 2008/10/02 21:58:11 bms Exp $ */ /* * Module definitions. */ #ifndef __RIB_RIB_MODULE_H__ #define __RIB_RIB_MODULE_H__ #ifndef XORP_MODULE_NAME #define XORP_MODULE_NAME "RIB" #endif #ifndef XORP_MODULE_VERSION #define XORP_MODULE_VERSION "0.1" #endif #endif /* __RIB_RIB_MODULE_H__ */ xorp/rib/rt_tab_register.hh0000664000076400007640000003316011540224234016134 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/rib/rt_tab_register.hh,v 1.21 2008/10/02 21:58:13 bms Exp $ #ifndef __RIB_RT_TAB_REGISTER_HH__ #define __RIB_RT_TAB_REGISTER_HH__ #include "libxorp/debug.h" #include "rt_tab_base.hh" class RegisterServer; /** * Holds information about an XRL module that requested to be * notified about a change. */ class ModuleData { public: /** * ModuleData Constructor * * @param modulename the XRL target name of the module that * requested notification about a route change. */ ModuleData(const string& modulename) : _modulename(modulename), _is_set(false) { } ModuleData() { } /** * @return the XRL target name of the module. */ const string& name() const { return _modulename; } /** * @return true if the XRL module needs to be notified about a change. */ bool is_set() const { return _is_set; } /** * Set state indicating the XRL module needs to be notified about * a change. */ void set() const { _is_set = true; } /** * Clear state indicating the XRL module needs to be notified about * a change. */ void clear() const { _is_set = false; } /** * @return string representation of this ModuleData for debugging purposes. */ string str() const { string s; s = _modulename + (_is_set ? " (SET)" : " (cleared)"); return s; } /** * Comparison operator for ModuleData class. * * Two ModuleData instances are considered equal if they refer * to the same XRL target, irrespective of the state of their flags. * * @param other the right-hand operand to compare against. * @return true if the left-hand operand is considered equal to the * right-hand operand (i.e., if both operands refer to the same XRL * target). */ bool operator==(const ModuleData& other) const { return name() == other.name(); } /** * Less-than operator for ModuleData class. * * This is needed so that ModuleData instances can be stored in * some STL containers. * * @param other the right-hand operand to compare against. * @return true if the left-hand operand is considered smaller than the * right-hand operand. */ bool operator<(const ModuleData& other) const { return name() < other.name(); } private: string _modulename; mutable bool _is_set; }; /** * @short RouteRegister stores a registration of interest in a subset * of a route. * * A RouteRegister instance is used to store the registration of * interest in a route. Suppose there are two overlapping routes: * 1.0.0.0/16 and 1.0.1.0/24. Now a routing protocol "bgp" expresses * interest in route changes that affect 1.0.0.27, which is routed * using 1.0.0.0/16. The relevant RouteRegister would then hold: * route: route entry for 1.0.0.0/16 * valid_subnet: 1.0.0.0/24 * moduledata: {"bgp"} * The valid_subnet is 1.0.0.0/24 because this is the largest subset of * 1.0.0.0/16 that encompasses 1.0.0.27 and doesn't overlap any other * route. * * If a subsequent request from routing protocol "pim" were to come * along for 1.0.0.54, then this would be stored on the same * RouteRegister instance, but the ModuleData would be expanded to * include "pim". */ template class RouteRegister { public: /** * RouteRegister Constructor * * @param net the subset of the route for which this registration * is valid. * @param route the route in which interest was registered. * @param module the ModuleData instance refering to the routing * protocol that registered interest. */ RouteRegister(const IPNet& valid_subnet, const IPRouteEntry* route, const ModuleData& module) : _valid_subnet(valid_subnet), _route(route) { _modules[module.name()] = module; } /** * Destructor */ ~RouteRegister() { _route = reinterpret_cast* >(0xbad); } /** * add_registrant is called when an additional routing protocol * expresses interest in the routing information already held by * this RouteRegister instance. * * @param module the ModuleData instance refering to the * additional routing protocol that just registered interest. * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_registrant(const ModuleData& module) { debug_msg("add_registrant: Module: %s\n", module.str().c_str()); if (_modules.find(module.name()) != _modules.end()) return XORP_ERROR; _modules[module.name()] = module; return XORP_OK; } /** * delete_registrant is called when a routing protocol that was * previously interested in this RouteRegister de-registers * itself. * * @param module the ModuleData instance of the routing protocol * that de-registered. * @return XORP_OK on success, otherwise XORP_ERROR. */ int delete_registrant(const ModuleData& module); /** * mark_modules is called when the routing information matching * this registration changes. It marks the original instances of * the ModuleData as needing nitification. */ void mark_modules() const { map::const_iterator i; for (i = _modules.begin(); i != _modules.end(); ++i) i->second.set(); } /** * @return the number of modules interested in this RouteRegister. */ int size() const { return _modules.size(); } /** * @return the subnet of this RouteRegister's route for which this * registration is valid. */ const IPNet& valid_subnet() const { return _valid_subnet; } /** * @return the RouteRegister's route. */ const IPRouteEntry* route() const { return _route; } /** * @return the module names interested in this RouteRegister as a * list of strings. */ list module_names() const { list names; map::const_iterator i; for (i = _modules.begin(); i != _modules.end(); ++i) names.push_back(i->second.name()); return names; } /** * @return this RouteRegister as a string for debugging purposes. */ string str() const; private: map _modules; // _net duplicates the storage of this in the RegisterTable map - // not very efficient IPNet _valid_subnet; // _valid_subnet is the subset of _route->net() that this // registration is valid for const IPRouteEntry* _route; }; /** * @short @ref RouteTable which stores routing protocols' registration * of interest in changes to certain routes. * * RegisterTable is a @ref RouteTable that is plumbed into the RIB * after all the @ref MergedTable and @ref ExtIntTable instances. * Thus it seems all winning route updates that will be propagated * to the forwarding engine. * * It's purpose is to track route changes that affect specific * addresses in which routing protocols have expressed an interest, * and to notify these routing protocols of any changes. */ template class RegisterTable : public RouteTable { public: /** * RegisterTable constructor * * Unlike other RouteTable constructors, this doesn't plumb itself * into the table graph because it is set up before any origin * tables, so it doesn't yet have a parent. * RIB::initialize_register will do the plumbing later on. * * @param tablename human-readable name for this table for * debugging purposes. * @param register_server a reference to the RIB's @ref RegisterServer * instance. The RegisterServer handles IPC requests and responses related * to registration of interest. * @param multicast indicates whether or not this RegisterTable is in * a multicast RIB. The information is needed when notifying a * routing protocol of a change, because the same route might be * in both unicast and multicast RIBs. */ RegisterTable(const string& tablename, RegisterServer& register_server, bool multicast); /** * RegisterTable destructor */ ~RegisterTable() {} /** * Add a new route to the RIB. This will be propagated downstream * to the next table, but may also cause the RegisterTable to * invalidate a RouteRegister because the new route overlaps an * existing registration. * * @param route the new route. * @param caller this must be this table's parent table. * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_route(const IPRouteEntry& route, RouteTable* caller); /** * Delete a route from the RIB. This will be propagated * downstream to the next table, but may also cause the * RegisterTable to invalidate a RouteRegister referencing this * route. * * @param route the route being deleted. * @param caller this must be this table's parent table. * @return XORP_OK on success, otherwise XORP_ERROR. */ int delete_route(const IPRouteEntry* route, RouteTable* caller); /** * Lookup a route in the RIB. This request will be propagated to * the parent table unchanged. */ const IPRouteEntry* lookup_route(const IPNet& net) const { return _parent->lookup_route(net); } /** * Lookup a route in the RIB. This request will be propagated to * the parent table unchanged. */ const IPRouteEntry* lookup_route(const A& addr) const { return _parent->lookup_route(addr); } /** * Lookup a route_range in the RIB. This request will be * propagated to the parent table unchanged. It is not expected * this will be called, but not prohibited. */ RouteRange* lookup_route_range(const A& addr) const { return _parent->lookup_route_range(addr); } /** * Replumb to replace the old parent of this table with a new parent. * * @param old_parent the parent RouteTable being replaced (must be * the same as the existing parent). * @param new_parent the new parent RouteTable. */ void replumb(RouteTable* old_parent, RouteTable* new_parent); /** * @return the parent @ref RouteTable of this RegisterTable. */ RouteTable* parent() { return _parent; } /** * @return this RegisterTable as a string for debugging purposes. */ string str() const; /** * Print the contents of this RegisterTable as a string for * debugging purposes. */ void print(); // Stuff specific to a Register Table /** * register_route_range is called to register interest in routing * changes that affect a specific IP address. * * @param addr the IP address of interest. * @param module the XRL target name of the module (typically a * routing protocol) that is interested in being notified about * changes to this address. * @return a RouteRegister instance detailing the route that is * currently being used to route packets to this address and the * subset of this route (including the address of interest) for * which this answer also applies. */ RouteRegister* register_route_range(const A& addr, const string& module); /** * deregister_route_range is called to de-register interest in routing * changes following a prior call to register_route_range. * * @param valid_subnet the subnet of validity from the * RouteRegister returned by a prior call to register_route_range. * @param module the XRL target name of the module that is no * longer interested in being notified. * @return XORP_OK on success, otherwise XORP_ERROR. */ int deregister_route_range(const IPNet& subnet, const string& module); /** * @return the table type (@ref TableType). */ TableType type() const { return REGISTER_TABLE; } /** * Cause the register server to push out queued changes to the * routing protocols. */ void flush(); private: RouteRegister* add_registration(const IPNet& net, const IPRouteEntry* route, const string& module); int delete_registration(const IPNet& net, const string& module); int notify_relevant_modules(bool add, const IPRouteEntry& changed_route); int find_matches(const IPRouteEntry& route); void notify_invalidated(typename Trie* >::iterator trie_iter); void notify_route_changed(typename Trie* >::iterator trie_iter, const IPRouteEntry& changed_route); map _module_names; Trie* > _ipregistry; RouteTable* _parent; RegisterServer& _register_server; bool _multicast; // true if a multicast rib }; #endif // __RIB_RT_TAB_REGISTER_HH__ xorp/rib/TODO0000664000076400007640000000451011421137511013120 0ustar greearbgreearb# # $XORP: xorp/rib/TODO,v 1.13 2006/08/02 07:59:23 pavlin Exp $ # RIB To Do List ======================= * The RIB should monitor the status of the FEA for route redistribution: when the FEA is UP/DOWN the RIB should register/deregister the FEA for route redistribution. Similar monitoring should be performed for all other targets interested in route redistribution: if a target dies, it should be removed (see RibManager::target_death()). * Get rid of the periodic RibManager::status_updater(), and use event-driven status updater. * ExtIntTable needs some re-writing to be more symetric so that recursive BGP nexthop lookups are possible. * VIF configuration should be automatic, by communicating directly with the FEA. * ExportTable needs a queue adding to rate control changes to the FEA when the FEA is not keeping up. * ExportTable should amalgamate deletes and adds in its queue for the same route. * RegisterServer should amalgamate deletes and adds in its queue for the same route. * RegisterServer should be smarter about the use of flush(). * Route filters are needed for route redistribution. * Change OriginTable to hold data rather than pointers in the Trie. * Better, more extensive test suites. * Rething the logic for computing whether a route is directly connected (e.g., when we use Vif::is_same_subnet() or Vif::is_same_p2p()): if vif is down, then the route is NOT directly connected? * Make test_rib_client.cc work properly. * Prune Export Table code. This can be done with Redist Table using an export specific output, ie one that adds start and end transaction around route add / delete XRLs. * Repeated adding and deleting of a table does not work. $ bgp/harness/test_routing2.sh -t test6. * Use ref_trie.hh not trie.hh. * The RIB provides a "register_interest[46]/deregister_interest[46]" interface for routes. The response to the register_interest call returns the requested answer. However, if a previous response becomes invalid the invalidation call comes via the rib_client interface. The client process can therefore information about a route via two different channels. This can mean that messages on one channel can overtake messages on the other channel. Therefore registering interest should not return the answer the answer should come later via the rib_client interface. xorp/rib/rt_tab_merged.hh0000664000076400007640000001276611421137511015563 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/rib/rt_tab_merged.hh,v 1.15 2008/10/02 21:58:12 bms Exp $ #ifndef __RIB_RT_TAB_MERGED_HH__ #define __RIB_RT_TAB_MERGED_HH__ #include "rt_tab_base.hh" /** * @short Make two route @ref RouteTables behave like one * * MergedTable takes two routing tables and combines them together to * form a single table, where routes for the same subnet with a lower * admin distance override those with a higher admin distance. * * The two parent tables are not actually merged. * * An add_route request from a parent tables causes a lookup on the * other parent table. If the route is better than the one from the * other table, or no route exists in the other table, then the new * route is passed downstream. * * An delete_route request from a parent table also causes a lookup on * the other parent table. The delete_route is propagated downstream. * If an alternative route is found, then that is then propagated * downsteam as an add_route to replace the deleted route. * * Lookups from downsteam cause lookups on both parent tables. The * better response is given. */ template class MergedTable : public RouteTable { public: /** * MergedTable Constructor. * * @param table_a one of two parent RouteTables. * @param table_b one of two parent RouteTables. */ MergedTable(RouteTable* table_a, RouteTable* table_b); /** * An add_route request from a parent table causes a lookup on the * other parent table. If the route is better than the one from the * other table, or no route exists in the other table then it is * passed downstream. * * @param route the new route. * @param caller the parent table sending the new route. * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_route(const IPRouteEntry& route, RouteTable* caller); /** * An delete_route request from a parent table also causes a * lookup on the other parent table. The delete_route is * propagated downstream. If an alternative route is found, then * that is then propagated downsteam as an add_route to replace * the deleted route. * * @param route the route to be deleted. * @param caller the parent table sending the delete_route. * @return XORP_OK on success, otherwise XORP_ERROR. */ int delete_route(const IPRouteEntry* route, RouteTable* caller); /** * Lookup a specific subnet. The lookup will be sent to both * parent tables. If both give an answer, then the route with the * better admin_distance is returned. * * @param net the subnet to look up. * @return a pointer to the route entry if it exists, NULL otherwise. */ const IPRouteEntry* lookup_route(const IPNet& net) const; /** * Lookup an IP address to get the most specific (longest prefix * length) route that matches this address. The lookup will be * sent to both parent tables. If both give an answer, then the * more specific route is returned. If both routes have the same * prefix length, then the route with the better admin_distance * is returned. * * @param addr the IP address to look up. * * @return a pointer to the best most specific route entry if any * entry matches, NULL otherwise. */ const IPRouteEntry* lookup_route(const A& addr) const; /** * Lookup an IP address to get the most specific (longest prefix * length) route that matches this address, along with the * RouteRange information for this address and route. As with * lookup_route, this involves querying both parent tables. The * best, most specific route is returned, and the tightest bounds * on the answer are returned. * * @see RouteRange * @param addr the IP address to look up. * @return a pointer to a RouteRange class instance containing the * relevant answer. It is up to the recipient of this pointer to * free the associated memory. */ RouteRange* lookup_route_range(const A& addr) const; /** * @return the table type (@ref TableType). */ TableType type() const { return MERGED_TABLE; } /** * replumb the parent tables, so that new_parent replaces old_parent. * * @param old_parent the old parent table. * @param new_parent the new parent table. */ void replumb(RouteTable* old_parent, RouteTable* new_parent); /** * Render this MergedTable as a string for debugging purposes. */ string str() const; private: RouteTable* _table_a; RouteTable* _table_b; }; #endif // __RIB_RT_TAB_MERGED_HH__ xorp/rib/profile_vars.cc0000664000076400007640000000272511421137511015440 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "rib_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/profile.hh" #include "profile_vars.hh" struct profile_vars { string var; string comment; } profile_vars[] = { {profile_route_ribin, "Routes entering RIB"}, {profile_route_rpc_in, "Routes being queued for the FEA"}, {profile_route_rpc_out, "Routes being sent to the FEA"}, }; void initialize_profiling_variables(Profile& p) { for (size_t i = 0; i < sizeof(profile_vars) / sizeof(struct profile_vars); i++) p.create(profile_vars[i].var, profile_vars[i].comment); } xorp/rib/profile_vars.hh0000664000076400007640000000243511540225533015454 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __RIB_PROFILE_VARS_HH__ #define __RIB_PROFILE_VARS_HH__ #ifndef XORP_DISABLE_PROFILE /** * Profile variables * See: profile_vars.cc for definitions. */ const string profile_route_ribin = "route_ribin"; const string profile_route_rpc_in = "route_rpc_in"; const string profile_route_rpc_out = "route_rpc_out"; void initialize_profiling_variables(Profile& p); #endif // profile #endif // __RIB_PROFILE_VARS_HH__ xorp/rib/rt_tab_pol_redist.hh0000664000076400007640000001037211421137511016453 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/rib/rt_tab_pol_redist.hh,v 1.9 2008/10/02 21:58:13 bms Exp $ #ifndef __RIB_RT_TAB_POL_REDIST_HH__ #define __RIB_RT_TAB_POL_REDIST_HH__ #include "rt_tab_base.hh" #include "libxorp/xorp.h" #include "libxorp/utils.hh" #include "libxorp/eventloop.hh" #include "libxipc/xrl_std_router.hh" #include "policy/backend/policy_redist_map.hh" #include "xrl/interfaces/policy_redist4_xif.hh" #include "xrl/interfaces/policy_redist6_xif.hh" /** * @short This table redistributes routes to protocols according to policytags. * * Based on the policy-tags in a route, this table will request a protocol to * start or end a redistribution of a route, depending whether the route is * being added or deleted. */ template class PolicyRedistTable : public RouteTable { public: static const string table_name; PolicyRedistTable(RouteTable* parent, XrlRouter& rtr, PolicyRedistMap&, bool multicast); int add_route(const IPRouteEntry& route, RouteTable* caller); int delete_route(const IPRouteEntry* route, RouteTable* caller); const IPRouteEntry* lookup_route(const IPNet& net) const; const IPRouteEntry* lookup_route(const A& addr) const; RouteRange* lookup_route_range(const A& addr) const; TableType type() const { return POLICY_REDIST_TABLE; } RouteTable* parent() { return _parent; } void replumb(RouteTable* old_parent, RouteTable* new_parent); string str() const; void xrl_cb(const XrlError&, string); /** * If policy-tags of a route changed, this table will need to figure out * which protocol should stop advertising a route, and which protocol * should continue or start. * * @param route the route with its new policy tags. * @param prevtags the previous policytags of the route. * @param caller the table which invoked this method. */ void replace_policytags(const IPRouteEntry& route, const PolicyTags& prevtags, RouteTable* caller); private: typedef set Set; /** * Start a redistribution of a route. * * @param route route to redistribute. * @param protos the set of protocols which should do the redistribution. */ void add_redist(const IPRouteEntry& route, const Set& protos); /** * End a route redistribution. * * @param route the route which should no longer be redistributed. * @param protos the protocols which should stop advertising the route. */ void del_redist(const IPRouteEntry& route, const Set& protos); /** * Start a route redistribution. * * @param route route to be redistributed. * @param proto protocol which should advertise route. */ void add_redist(const IPRouteEntry& route, const string& proto); /** * End a route redistribution. * * @param route route which should no longer be redistributed. * @param proto protocol which should stop advertising the route. */ void del_redist(const IPRouteEntry& route, const string& proto); RouteTable* _parent; XrlRouter& _xrl_router; EventLoop& _eventloop; PolicyRedistMap& _redist_map; XrlPolicyRedist4V0p1Client _redist4_client; XrlPolicyRedist6V0p1Client _redist6_client; bool _multicast; }; #endif // __RIB_RT_TAB_POL_REDIST_HH__ xorp/rib/rt_tab_pol_conn.hh0000664000076400007640000000603311421137511016115 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/rib/rt_tab_pol_conn.hh,v 1.9 2008/10/02 21:58:13 bms Exp $ #ifndef __RIB_RT_TAB_POL_CONN_HH__ #define __RIB_RT_TAB_POL_CONN_HH__ #include "rt_tab_base.hh" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/utils.hh" #include "policy/backend/policy_filters.hh" /** * @short This table will filter connected routes. * * This table stores all routes it receives from the origin table. * It then filters them modifying only policytags. * It has the ability to push routes through the filter again, causing only * policytags on routes to be updated [no route deletion / replacement]. */ template class PolicyConnectedTable : public RouteTable { public: static const string table_name; /** * @param parent parent table. * @param pfs the connected routes policy filters. */ PolicyConnectedTable(RouteTable* parent, PolicyFilters& pfs); ~PolicyConnectedTable(); int add_route(const IPRouteEntry& route, RouteTable* caller); int delete_route(const IPRouteEntry* route, RouteTable* caller); const IPRouteEntry* lookup_route(const IPNet& net) const; const IPRouteEntry* lookup_route(const A& addr) const; RouteRange* lookup_route_range(const A& addr) const; TableType type() const { return POLICY_CONNECTED_TABLE; } RouteTable* parent() { return _parent; } void replumb(RouteTable* old_parent, RouteTable* new_parent); string str() const; /** * Push all the routes through the filter again */ void push_routes(); private: /** * Route may be modified [its policy tags]. * No need to check for route being accepted / rejected -- it is always * accepted [only source match filtering]. * * @param r route to filter. */ void do_filtering(IPRouteEntry& r); typedef Trie*> RouteContainer; RouteTable* _parent; RouteContainer _route_table; // Copy of routes // we have this so we may push routes. PolicyFilters& _policy_filters; // Reference to connected route filters. }; #endif // __RIB_RT_TAB_POL_CONN_HH__ xorp/rib/parser.hh0000664000076400007640000002771011540224234014255 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/rib/parser.hh,v 1.24 2008/10/02 21:58:10 bms Exp $ #ifndef __RIB_PARSER_HH__ #define __RIB_PARSER_HH__ #include "libxorp/xorp.h" #include "libxorp/ipv4.hh" #include "libxorp/ipv4net.hh" #include "rib.hh" class Command; /** * Base class for data types available to Parser. */ class Datum { public: virtual ~Datum() {} }; class Uint32Datum : public Datum { public: Uint32Datum(const string& s) { _n = 0; for (size_t i = 0; i < s.size(); i++) { if (!xorp_isdigit(s[i])) xorp_throw0(InvalidString); _n *= 10; _n += s[i] - '0'; } } const uint32_t& get() const { return _n; } protected: uint32_t _n; }; class StringDatum : public Datum { public: StringDatum(const string& s) : _s(s) {} const string& get() const { return _s; } protected: const string _s; }; class IPv4Datum : public Datum { public: IPv4Datum(const string& s) : _ipv4(s.c_str()) {} const IPv4& get() const { return _ipv4; } protected: const IPv4 _ipv4; }; class IPv4NetDatum : public Datum { public: IPv4NetDatum(const string& s) : _ipv4net(s.c_str()) {} const IPv4Net& get() const { return _ipv4net; } protected: const IPv4Net _ipv4net; }; /** * Base class for Argument Parsers. */ class ArgumentParser { public: ArgumentParser(const string& parser_name) : _argname(parser_name) {} virtual ~ArgumentParser() {} virtual Datum* parse(const string& s) const = 0; const string& name() const { return _argname;} private: const string _argname; }; class Uint32ArgumentParser : public ArgumentParser { public: Uint32ArgumentParser() : ArgumentParser("~Uint32") {} Datum* parse(const string& str) const; }; class StringArgumentParser : public ArgumentParser { public: StringArgumentParser() : ArgumentParser("~String") {} Datum* parse(const string& str) const; }; class IPv4ArgumentParser : public ArgumentParser { public: IPv4ArgumentParser() : ArgumentParser("~IPv4") {} Datum* parse(const string& str) const; }; class IPv4NetArgumentParser : public ArgumentParser { public: IPv4NetArgumentParser() : ArgumentParser("~IPv4Net") {} Datum* parse(const string& str) const; }; class Parser { public: Parser(); ~Parser(); int parse(const string& str) const; bool add_command(Command* command); bool add_argtype(ArgumentParser* arg); private: ArgumentParser* get_argument_parser(const string& name) const; int split_into_words(const string& str, vector & words) const; char _separator; map _templates; map _argtypes; }; class Parse_error { public: Parse_error() : _str("generic error") {} Parse_error(const string& s) : _str(s) {} const string& str() const { return _str; } private: string _str; }; /** * Datum to variable binding. */ class DatumVariableBinding { public: virtual ~DatumVariableBinding() {} virtual void transfer(Datum* d) throw (Parse_error) = 0; }; class DatumUint32Binding : public DatumVariableBinding { public: virtual ~DatumUint32Binding() {} DatumUint32Binding(uint32_t& i) : _i(i) {} void transfer(Datum* d) throw (Parse_error) { Uint32Datum* id = dynamic_cast(d); if (NULL == id) throw Parse_error("Wrong type ? int decoding failed"); _i = id->get(); } private: uint32_t& _i; }; class DatumStringBinding : public DatumVariableBinding { public: virtual ~DatumStringBinding() {} DatumStringBinding(string& s) : _s(s) {} void transfer(Datum* d) throw (Parse_error) { StringDatum* id = dynamic_cast(d); if (NULL == id) throw Parse_error("Wrong type ? string decoding failed"); _s = id->get(); } private: string& _s; }; class DatumIPv4Binding : public DatumVariableBinding { public: virtual ~DatumIPv4Binding() {} DatumIPv4Binding(IPv4& ipv4) : _ipv4(ipv4) {} void transfer(Datum* d) throw (Parse_error) { IPv4Datum* id = dynamic_cast(d); if (NULL == id) throw Parse_error("Wrong type ? ipv4 decoding failed"); _ipv4 = id->get(); } private: IPv4& _ipv4; }; class DatumIPv4NetBinding : public DatumVariableBinding { public: virtual ~DatumIPv4NetBinding() {} DatumIPv4NetBinding(IPv4Net& ipv4net) : _ipv4net(ipv4net) {} void transfer(Datum* d) throw (Parse_error) { IPv4NetDatum* id = dynamic_cast(d); if (NULL == id) throw Parse_error("Wrong type ? ipv4 decoding failed"); _ipv4net = id->get(); } private: IPv4Net& _ipv4net; }; class Command { public: Command(const string& cmd_syntax, int nargs) : _syntax(cmd_syntax), _nargs(nargs), _last_arg(-1) { check_syntax(); } virtual ~Command(); virtual int execute() = 0; const string& syntax() const { return _syntax; } void set_arg(int argnum, Datum* d) throw (Parse_error); int num_args() const { return _nargs; } protected: // // Abort if number of args in syntax string does not matches // number given. // void check_syntax(); void set_last_arg(int n) { _last_arg = n; } // // Bind positional argument to Datum type so when argument n arrives, it // can be decoded into a member variable. // void bind(int n, DatumVariableBinding* b); void bind_uint32(int n, uint32_t& i); void bind_string(int n, string& s); void bind_ipv4(int n, IPv4& addr); void bind_ipv4net(int n, IPv4Net& net); DatumVariableBinding* find_binding(int n); protected: const string _syntax; const int _nargs; // Number of args before execute can be called int _last_arg; // Last argument added map _bindings; }; class TableOriginCommand : public Command { public: TableOriginCommand() : Command("table origin ~String ~Uint32", 2) { bind_string(0, _tablename); bind_uint32(1, _admin_distance); } virtual int execute() = 0; protected: string _tablename; uint32_t _admin_distance; }; class TableMergedCommand : public Command { public: TableMergedCommand() : Command("table merged ~String ~String ~String", 3) { bind_string(0, _tablename); bind_string(1, _t1); bind_string(2, _t2); } virtual int execute() = 0; protected: string _tablename; string _t1; string _t2; }; class TableExtIntCommand : public Command { public: TableExtIntCommand() : Command("table extint ~String ~String ~String", 3) { bind_string(0, _tablename); bind_string(1, _t1); bind_string(2, _t2); } virtual int execute() = 0; protected: string _tablename; string _t1; string _t2; }; class RouteAddCommand : public Command { public: RouteAddCommand() : Command("route add ~String ~IPv4Net ~IPv4 ~Uint32", 4) { bind_string(0, _tablename); bind_ipv4net(1, _net); bind_ipv4(2, _nexthop); bind_uint32(3, _metric); } virtual int execute() = 0; protected: string _tablename; IPv4Net _net; IPv4 _nexthop; uint32_t _metric; }; class RouteVifAddCommand : public Command { public: RouteVifAddCommand() : Command("route vifadd ~String ~IPv4Net ~String ~IPv4 ~Uint32", 5) { bind_string(0, _tablename); bind_ipv4net(1, _net); bind_string(2, _vifname); bind_ipv4(3, _nexthop); bind_uint32(4, _metric); } virtual int execute() = 0; protected: string _tablename; IPv4Net _net; string _vifname; IPv4 _nexthop; uint32_t _metric; }; class RouteDeleteCommand : public Command { public: RouteDeleteCommand() : Command("route delete ~String ~IPv4Net", 2) { bind_string(0, _tablename); bind_ipv4net(1, _net); } virtual int execute() = 0; protected: string _tablename; IPv4Net _net; }; class RouteVerifyCommand : public Command { public: RouteVerifyCommand() : Command( "route verify ~String ~IPv4 ~String ~IPv4 ~Uint32", 5) { bind_string(0, _type); bind_ipv4(1, _lookupaddr); bind_string(2, _ifname); bind_ipv4(3, _nexthop); bind_uint32(4, _metric); } virtual int execute() = 0; protected: string _type; // Type of result expected. string _ifname; IPv4 _lookupaddr; IPv4 _nexthop; uint32_t _metric; }; class DiscardVifCommand : public Command { public: DiscardVifCommand() : Command("vif Discard ~String ~IPv4 ~Uint32", 3) { bind_string(0, _ifname); bind_ipv4(1, _addr); bind_uint32(2, _prefix_len); } virtual int execute() = 0; protected: string _ifname; IPv4 _addr; uint32_t _prefix_len; }; class UnreachableVifCommand : public Command { public: UnreachableVifCommand() : Command( "vif Unreachable ~String ~IPv4 ~Uint32", 3) { bind_string(0, _ifname); bind_ipv4(1, _addr); bind_uint32(2, _prefix_len); } virtual int execute() = 0; protected: string _ifname; IPv4 _addr; uint32_t _prefix_len; }; class ManagementVifCommand : public Command { public: ManagementVifCommand() : Command( "vif Management ~String ~IPv4 ~Uint32", 3) { bind_string(0, _ifname); bind_ipv4(1, _addr); bind_uint32(2, _prefix_len); } virtual int execute() = 0; protected: string _ifname; IPv4 _addr; uint32_t _prefix_len; }; class EtherVifCommand : public Command { public: EtherVifCommand() : Command("vif Ethernet ~String ~IPv4 ~Uint32", 3) { bind_string(0, _ifname); bind_ipv4(1, _addr); bind_uint32(2, _prefix_len); } virtual int execute() = 0; protected: string _ifname; IPv4 _addr; uint32_t _prefix_len; }; class LoopbackVifCommand : public Command { public: LoopbackVifCommand() : Command("vif Loopback ~String ~IPv4 ~Uint32", 3) { bind_string(0, _ifname); bind_ipv4(1, _addr); bind_uint32(2, _prefix_len); } virtual int execute() = 0; protected: string _ifname; IPv4 _addr; uint32_t _prefix_len; }; class RedistEnableCommand : public Command { public: RedistEnableCommand() : Command("redistribute enable ~String ~String", 2) { bind_string(0, _from_table); bind_string(1, _to_table); } virtual int execute() = 0; protected: string _from_table; string _to_table; }; class RedistDisableCommand : public Command { public: RedistDisableCommand() : Command("redistribute disable ~String ~String", 2) { bind_string(0, _from_table); bind_string(1, _to_table); } virtual int execute() = 0; protected: string _from_table; string _to_table; }; class AddIGPTableCommand : public Command { public: AddIGPTableCommand() : Command("add_igp_table ~String", 1) { bind_string(0, _tablename); } virtual int execute() = 0; protected: string _tablename; }; class DeleteIGPTableCommand : public Command { public: DeleteIGPTableCommand() : Command("delete_igp_table ~String", 1) { bind_string(0, _tablename); } virtual int execute() = 0; protected: string _tablename; }; class AddEGPTableCommand : public Command { public: AddEGPTableCommand() : Command("add_egp_table ~String", 1) { bind_string(0, _tablename); } virtual int execute() = 0; protected: string _tablename; }; class DeleteEGPTableCommand : public Command { public: DeleteEGPTableCommand() : Command("delete_egp_table ~String", 1) { bind_string(0, _tablename); } virtual int execute() = 0; protected: string _tablename; }; #endif // __RIB_PARSER_HH__ xorp/rib/vifmanager.cc0000664000076400007640000003552311540225533015072 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "rib_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/ipvx.hh" #include "libxorp/status_codes.h" #include "libxipc/xrl_router.hh" #include "rib_manager.hh" #include "vifmanager.hh" VifManager::VifManager(XrlRouter& xrl_router, EventLoop& eventloop, RibManager* rib_manager, const string& fea_target) : _xrl_router(xrl_router), _eventloop(eventloop), _rib_manager(rib_manager), _ifmgr(eventloop, fea_target.c_str(), xrl_router.finder_address(), xrl_router.finder_port()), _startup_requests_n(0), _shutdown_requests_n(0) { enable(); // XXX: by default the VifManager is always enabled // // Set myself as an observer when the node status changes // set_observer(this); _ifmgr.set_observer(this); _ifmgr.attach_hint_observer(this); } VifManager::~VifManager() { // // Unset myself as an observer when the node status changes // unset_observer(this); stop(); _ifmgr.detach_hint_observer(this); _ifmgr.unset_observer(this); } /** * Start operation. * * Start the process of registering with the FEA, etc. * After the startup operations are completed, * @ref VifManager::final_start() is called internally * to complete the job. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int VifManager::start() { if (is_up() || is_pending_up()) return (XORP_OK); enable(); // XXX: by default the VifManager is always enabled if (ProtoState::pending_start() != XORP_OK) return (XORP_ERROR); // // Startup the interface manager // if (ifmgr_startup() != XORP_OK) { ServiceBase::set_status(SERVICE_FAILED); return (XORP_ERROR); } return (XORP_OK); } /** * Completely start the node operation. * * This method should be called internally after @ref VifManager::start() * to complete the job. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int VifManager::final_start() { #if 0 // TODO: XXX: PAVPAVPAV if (! is_pending_up()) return (XORP_ERROR); #endif if (ProtoState::start() != XORP_OK) { ProtoState::stop(); return (XORP_ERROR); } return (XORP_OK); } /** * Stop operation. * * Gracefully stop operation. * After the shutdown operations are completed, * @ref VifManager::final_stop() is called internally * to complete the job. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int VifManager::stop() { if (is_down()) return (XORP_OK); if (! (is_up() || is_pending_up() || is_pending_down())) return (XORP_ERROR); if (ProtoState::pending_stop() != XORP_OK) return (XORP_ERROR); // // Shutdown the interface manager // if (ifmgr_shutdown() != XORP_OK) { ServiceBase::set_status(SERVICE_FAILED); return (XORP_ERROR); } return (XORP_OK); } /** * Completely stop the node operation. * * This method should be called internally after @ref VifManager::stop() * to complete the job. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int VifManager::final_stop() { #if 0 if (! (is_up() || is_pending_up() || is_pending_down())) return (XORP_ERROR); #endif if (ProtoState::stop() != XORP_OK) return (XORP_ERROR); // Clear the old state _iftree.clear(); _old_iftree.clear(); return (XORP_OK); } void VifManager::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() != XORP_OK) { XLOG_ERROR("Cannot complete the startup process; " "current state is %s", ProtoState::state_str().c_str()); return; } return; } if ((old_status == SERVICE_SHUTTING_DOWN) && (new_status == SERVICE_SHUTDOWN)) { // The shutdown process has completed final_stop(); 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)) { decr_shutdown_requests_n(); } } } void VifManager::update_status() { // // Test if the startup process has completed // if (ServiceBase::status() == SERVICE_STARTING) { if (_startup_requests_n > 0) return; // The startup process has completed ServiceBase::set_status(SERVICE_RUNNING); return; } // // Test if the shutdown process has completed // if (ServiceBase::status() == SERVICE_SHUTTING_DOWN) { if (_shutdown_requests_n > 0) return; // The shutdown process has completed ServiceBase::set_status(SERVICE_SHUTDOWN); return; } // // Test if we have failed // if (ServiceBase::status() == SERVICE_FAILED) { return; } } int VifManager::ifmgr_startup() { int ret_value; // TODO: XXX: we should startup the ifmgr only if it hasn't started yet incr_startup_requests_n(); ret_value = _ifmgr.startup(); // // XXX: when the startup is completed, IfMgrHintObserver::tree_complete() // will be called. // return (ret_value); } int VifManager::ifmgr_shutdown() { int ret_value; incr_shutdown_requests_n(); ret_value = _ifmgr.shutdown(); // // XXX: when the shutdown is completed, VifManager::status_change() // will be called. // return (ret_value); } void VifManager::tree_complete() { // // XXX: we use same actions when the tree is completed or updates are made // updates_made(); decr_startup_requests_n(); } void VifManager::updates_made() { string error_msg; IfMgrIfTree::IfMap::const_iterator ifmgr_iface_iter; IfMgrIfAtom::VifMap::const_iterator ifmgr_vif_iter; IfMgrVifAtom::IPv4Map::const_iterator a4_iter; #ifdef HAVE_IPV6 IfMgrVifAtom::IPv6Map::const_iterator a6_iter; #endif // // Update the local copy of the interface tree // _old_iftree = _iftree; _iftree = ifmgr_iftree(); // // Remove vifs that don't exist anymore // for (ifmgr_iface_iter = _old_iftree.interfaces().begin(); ifmgr_iface_iter != _old_iftree.interfaces().end(); ++ifmgr_iface_iter) { const IfMgrIfAtom& ifmgr_iface = ifmgr_iface_iter->second; const string& ifmgr_iface_name = ifmgr_iface.name(); if (_iftree.find_vif(ifmgr_iface_name, ifmgr_iface_name) == NULL) { if (_rib_manager->delete_vif(ifmgr_iface_name, error_msg) != XORP_OK) { XLOG_ERROR("Cannot delete vif %s from the set of configured " "vifs: %s", ifmgr_iface_name.c_str(), error_msg.c_str()); } } } // // Add new vifs, update existing ones and remove old addresses // for (ifmgr_iface_iter = _iftree.interfaces().begin(); ifmgr_iface_iter != _iftree.interfaces().end(); ++ifmgr_iface_iter) { const IfMgrIfAtom& ifmgr_iface = ifmgr_iface_iter->second; const string& ifmgr_iface_name = ifmgr_iface.name(); 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(); const IfMgrVifAtom* old_ifmgr_vif_ptr; old_ifmgr_vif_ptr = _old_iftree.find_vif(ifmgr_iface_name, ifmgr_vif_name); // The vif to add (eventually) Vif vif(ifmgr_vif_name); // // Set the pif_index // if (old_ifmgr_vif_ptr == NULL) vif.set_pif_index(ifmgr_vif.pif_index()); // // Update or set the vif flags // XXX: Other properties (discard, unreachable, management) // need to be inherited from the parent interface, once it has // been marshaled. // bool is_up = ifmgr_iface.enabled() && ifmgr_vif.enabled(); is_up &= (! ifmgr_iface.no_carrier()); // XXX: the link state if (old_ifmgr_vif_ptr != NULL) { if (_rib_manager->set_vif_flags(ifmgr_vif_name, ifmgr_vif.p2p_capable(), ifmgr_vif.loopback(), ifmgr_vif.multicast_capable(), ifmgr_vif.broadcast_capable(), is_up, ifmgr_iface.mtu(), error_msg) != XORP_OK) { XLOG_ERROR("Cannot update the flags for vif %s: %s", ifmgr_vif_name.c_str(), error_msg.c_str()); } } else { vif.set_ifname(ifmgr_iface_name); vif.set_p2p(ifmgr_vif.p2p_capable()); vif.set_loopback(ifmgr_vif.loopback()); vif.set_multicast_capable(ifmgr_vif.multicast_capable()); vif.set_broadcast_capable(ifmgr_vif.broadcast_capable()); vif.set_underlying_vif_up(is_up); vif.set_mtu(ifmgr_iface.mtu()); } // // Delete vif addresses that don't exist anymore // if (old_ifmgr_vif_ptr != NULL) { for (a4_iter = old_ifmgr_vif_ptr->ipv4addrs().begin(); a4_iter != old_ifmgr_vif_ptr->ipv4addrs().end(); ++a4_iter) { const IfMgrIPv4Atom& a4 = a4_iter->second; const IPv4& addr = a4.addr(); if (_iftree.find_addr(ifmgr_iface_name, ifmgr_vif_name, addr) == NULL) { if (_rib_manager->delete_vif_address(ifmgr_vif_name, addr, error_msg) != XORP_OK) { XLOG_ERROR("Cannot delete address %s " "for vif %s: %s", addr.str().c_str(), ifmgr_vif_name.c_str(), error_msg.c_str()); } } } #ifdef HAVE_IPV6 for (a6_iter = old_ifmgr_vif_ptr->ipv6addrs().begin(); a6_iter != old_ifmgr_vif_ptr->ipv6addrs().end(); ++a6_iter) { const IfMgrIPv6Atom& a6 = a6_iter->second; const IPv6& addr = a6.addr(); if (_iftree.find_addr(ifmgr_iface_name, ifmgr_vif_name, addr) == NULL) { if (_rib_manager->delete_vif_address(ifmgr_vif_name, addr, error_msg) != XORP_OK) { XLOG_ERROR("Cannot delete address %s " "for vif %s: %s", addr.str().c_str(), ifmgr_vif_name.c_str(), error_msg.c_str()); } } } #endif } // // Add a new vif // if (old_ifmgr_vif_ptr == NULL) { if (_rib_manager->new_vif(ifmgr_vif_name, vif, error_msg) != XORP_OK) { XLOG_ERROR("Cannot add vif %s to the set of configured " "vifs: %s", ifmgr_vif_name.c_str(), error_msg.c_str()); } } } } // // Add new vif addresses, and update existing ones // for (ifmgr_iface_iter = _iftree.interfaces().begin(); ifmgr_iface_iter != _iftree.interfaces().end(); ++ifmgr_iface_iter) { const IfMgrIfAtom& ifmgr_iface = ifmgr_iface_iter->second; const string& ifmgr_iface_name = ifmgr_iface.name(); 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(); const IfMgrVifAtom* old_ifmgr_vif_ptr; old_ifmgr_vif_ptr = _old_iftree.find_vif(ifmgr_iface_name, ifmgr_vif_name); for (a4_iter = ifmgr_vif.ipv4addrs().begin(); a4_iter != ifmgr_vif.ipv4addrs().end(); ++a4_iter) { const IfMgrIPv4Atom& a4 = a4_iter->second; const IPv4& addr = a4.addr(); const IfMgrIPv4Atom* old_a4_ptr = NULL; if (old_ifmgr_vif_ptr != NULL) old_a4_ptr = old_ifmgr_vif_ptr->find_addr(addr); if ((old_a4_ptr != NULL) && (*old_a4_ptr == a4)) continue; // Nothing changed IPv4Net subnet_addr(addr, a4.prefix_len()); IPv4 broadcast_addr(IPv4::ZERO()); IPv4 peer_addr(IPv4::ZERO()); if (a4.has_broadcast()) broadcast_addr = a4.broadcast_addr(); if (a4.has_endpoint()) peer_addr = a4.endpoint_addr(); if (old_a4_ptr != NULL) { // Delete the old address so it can be replaced if (_rib_manager->delete_vif_address(ifmgr_vif_name, addr, error_msg) != XORP_OK) { XLOG_ERROR("Cannot delete address %s " "for vif %s: %s", addr.str().c_str(), ifmgr_vif_name.c_str(), error_msg.c_str()); } } if (_rib_manager->add_vif_address(ifmgr_vif_name, addr, subnet_addr, broadcast_addr, peer_addr, error_msg) != XORP_OK) { 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()); } } #ifdef HAVE_IPV6 for (a6_iter = ifmgr_vif.ipv6addrs().begin(); a6_iter != ifmgr_vif.ipv6addrs().end(); ++a6_iter) { const IfMgrIPv6Atom& a6 = a6_iter->second; const IPv6& addr = a6.addr(); const IfMgrIPv6Atom* old_a6_ptr = NULL; if (old_ifmgr_vif_ptr != NULL) old_a6_ptr = old_ifmgr_vif_ptr->find_addr(addr); if ((old_a6_ptr != NULL) && (*old_a6_ptr == a6)) continue; // Nothing changed IPv6Net subnet_addr(addr, a6.prefix_len()); IPv6 peer_addr(IPv6::ZERO()); if (a6.has_endpoint()) peer_addr = a6.endpoint_addr(); if (old_a6_ptr != NULL) { // Delete the old address so it can be replaced if (_rib_manager->delete_vif_address(ifmgr_vif_name, addr, error_msg) != XORP_OK) { XLOG_ERROR("Cannot delete address %s " "for vif %s: %s", addr.str().c_str(), ifmgr_vif_name.c_str(), error_msg.c_str()); } } if (_rib_manager->add_vif_address(ifmgr_vif_name, addr, subnet_addr, peer_addr, error_msg) != XORP_OK) { 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()); } } #endif } } } void VifManager::incr_startup_requests_n() { _startup_requests_n++; XLOG_ASSERT(_startup_requests_n > 0); } void VifManager::decr_startup_requests_n() { XLOG_ASSERT(_startup_requests_n > 0); _startup_requests_n--; update_status(); } void VifManager::incr_shutdown_requests_n() { _shutdown_requests_n++; XLOG_ASSERT(_shutdown_requests_n > 0); } void VifManager::decr_shutdown_requests_n() { XLOG_ASSERT(_shutdown_requests_n > 0); _shutdown_requests_n--; update_status(); } xorp/rib/rib.hh0000664000076400007640000006630311635757530013555 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __RIB_RIB_HH__ #define __RIB_RIB_HH__ #include "libxorp/xorp.h" #include "libxorp/ipv4.hh" #include "libxorp/ipv6.hh" #include "libxorp/ipnet.hh" #include "libxorp/nexthop.hh" #include "libxorp/vif.hh" #include "route.hh" #include "rt_tab_base.hh" #include "rt_tab_origin.hh" #include "rt_tab_merged.hh" #include "rt_tab_extint.hh" #include "rt_tab_redist.hh" #include "rt_tab_pol_redist.hh" #include "rt_tab_register.hh" #include "rt_tab_pol_conn.hh" #include "policy/backend/policytags.hh" #include "policy/backend/policy_redist_map.hh" class RegisterServer; class RibManager; class RibVif; enum RibTransportType { UNICAST = 1, MULTICAST = 2 }; enum RibVerifyType { MISS = 0, // No route to destination DISCARD = 1, // Discard route for destination UNREACHABLE = 2, // Unreachable route for destination IP = 3 // Protocol route to destination }; /** * @short Master class for a RIB. * * RIB is the master class for a Routing Information Base. It holds * the RibVif table, routing tables for each protocol, etc. Typically we * would have one RIB for IPv4 unicast, one for IPv4 multicast * topology, one for IPv6 unicast and one for IPv6 multicast. * * Note that the XRL commands assume some level of filtering has already * taken place to route to command to the right RIB. */ template class RIB : public NONCOPYABLE { public: /** * RIB Constructor. * * @param rib_type indicates whether this RIB holds UNICAST or * MULTICAST routing information. In the case of multicast, this * is the topology information, not the forwarding information. * @param rib_manager the main RIB manager process holding stuff * that's common to all the individual RIBs. * @param eventloop the main event loop. */ RIB(RibTransportType rib_type, RibManager& rib_manager, EventLoop& eventloop); /** * RIB Destructor. */ virtual ~RIB(); /** * Set test-mode: abort on some errors that we'd normally mask. */ void set_errors_are_fatal() { _errors_are_fatal = true; } /** * Get the list with the registered protocol names. * * @return the list with the registered protocol names. */ list registered_protocol_names() const; /** * Initialize the RIB. * Note that it is an error to initialize the table twice. * * @param register_server the @ref RegisterServer to initialize * the Rib with. */ void initialize(RegisterServer& register_server); /** * Initialize the RIB's RedistTable at the end so that the * winning routes are exported to the RIB clients (e.g., the FEA). * Note that it is an error to initialize the table twice. * * @see RedistTable * @param all a keyword string which can be used by RIB clients * to register with the RIB to receive the winning routes from * the RedistTable. * @return XORP_OK on success, otherwise XORP_ERROR. */ int initialize_redist_all(const string& all); /** * Initialize the RIB's PolicyRedistTable. The PolicyRedistTable enables * route redistribution according to policy configuration. Based on the * policy tags of routes passing through this table, a redistribution * request is sent to the relevant protocols. If routes are being deleted, * protocols are informed to stop advertising the route. * */ int initialize_policy_redist(); /** * Initialize the RIB's RegisterTable. The RegisterTable allows * routing protocols such as BGP to register interest in routing * information that affects specfic addresses. * Note that it is an error to initialize the table twice. * * @param register_server the @ref RegisterServer to initialize * the Rib with. * @return XORP_OK on success, otherwise XORP_ERROR. */ int initialize_register(RegisterServer& register_server); /** * Add a new OriginTable. Use is deprecated, except in test suites. * * @see OriginTable * @param tablename human-readable name for this table to help in * debugging. * @param target_class the XRL target class of the routing * protocol that will supply routes to this OriginTable. * @param target_instance the XRL target instance of the routing * protocol that will supply routes to this OriginTable. * @param admin_distance default administrative distance to be * applied to routes that enter the RIB through this OriginTable. * @param protocol_type the routing protocol type (@ref ProtocolType). * @return XORP_OK on success, otherwise XORP_ERROR. */ int new_origin_table(const string& tablename, const string& target_class, const string& target_instance, uint32_t admin_distance, ProtocolType protocol_type); /** * Inform the RIB about the existence of a Virtual Interface. * Note that it is an error to add twice a vif with the same vifname. * * @see Vif * @param vifname the name of the VIF, as understood by the FEA. * @param vif Vif class instance giving the information about this vif. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int new_vif(const string& vifname, const Vif& vif); /** * Inform the RIB that a VIF no longer exists. * * @param vifname the name of the VIF, as previously indicated by new_vif. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int delete_vif(const string& vifname); /** * Destroy a VIF container for a VIF that no longer exists. * * @param rib_vif the VIF container that will be destroyed. */ virtual void destroy_deleted_vif(RibVif* rib_vif); /** * Set the vif flags of a configured vif. * * @param vifname the name of the vif. * @param is_pim_register true if the vif is a PIM Register interface. * @param is_p2p true if the vif is point-to-point interface. * @param is_loopback true if the vif is a loopback interface. * @param is_multicast true if the vif is multicast capable. * @param is_broadcast true if the vif is broadcast capable. * @param is_up true if the underlying vif is UP. * @param mtu the MTU of the vif. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int set_vif_flags(const string& vifname, bool is_p2p, bool is_loopback, bool is_multicast, bool is_broadcast, bool is_up, uint32_t mtu); /** * Add an address and subnet to a existing VIF. Each VIF may have * multiple addresses and associated subnets. * * @param vifname the name of the VIF the address will be added to. * @param addr the address to be added. This must be one of the * addresses of this router. * @param subnet the subnet that is connected to this VIF * corresponding to the address addr. * @param broadcast the broadcast address to add. In case of IPv6 * this address is ignored. * @param peer the peer address to add. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int add_vif_address(const string& vifname, const A& addr, const IPNet& subnet, const A& broadcast_addr, const A& peer_addr); /** * Remove an address and the associated subnet from an existing VIF. * @param vifname the name of the VIF the address will be removed from. * @param addr the address to be removed. This must be an address * previously added by add_vif_address. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int delete_vif_address(const string& vifname, const A& addr); /** * Add a route to the "connected" OriginTable. * * @param vif the vif with the connected route. * @param net the subnet (address and prefix length) of the route. * @param nexthop_addr the nexthop address of the route to add. * @param peer_addr the peer address for the route to add * (if a point-to-point interface). * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_connected_route(const RibVif& vif, const IPNet& net, const A& nexthop_addr, const A& peer_addr); /** * Delete a route from the "connected" OriginTable. * * @param vif the vif with the connected route. * @param net the subnet (address and prefix length) of the route. * @param peer_addr the peer address for the route to delete * (if a point-to-point interface). * @return XORP_OK on success, otherwise XORP_ERROR. */ int delete_connected_route(const RibVif& vif, const IPNet& net, const A& peer_addr); /** * Add a route via the OriginTable called tablename. * * @param tablename the name of the OriginTable into which the * route should be inserted. * @param net the subnet (address and prefix length) of the route. * @param nexthop_addr the nexthop that packets destined for net should be * forwarded to. * @param ifname the name of the physical interface toward the * destination. If an empty string the interface will be chosen by RIB. * @param vifname the name of the virtual interface toward the * destination. If an empty string the interface will be chosen by RIB. * @param metric the routing protocol metric associated with this route. * @param policytags the policy-tags for this route. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int add_route(const string& tablename, const IPNet& net, const A& nexthop_addr, const string& ifname, const string& vifname, uint32_t metric, const PolicyTags& policytags); /** * Replace an existing route via the OriginTable called tablename. * * @param tablename the name of the OriginTable in which the * route should be replaced. * @param net the subnet (address and prefix length) of the route. * @param nexthop_addr the new nexthop that packets destined for @ref net * should be forwarded to. * @param ifname the name of the physical interface toward the * destination. If an empty string the interface will be chosen by RIB. * @param vifname the name of the virtual interface toward the * destination. If an empty string the interface will be chosen by RIB. * @param metric the new routing protocol metric associated with this * @param policytags the policy-tags for this route. * route. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int replace_route(const string& tablename, const IPNet& net, const A& nexthop_addr, const string& ifname, const string& vifname, uint32_t metric, const PolicyTags& policytags); /** * Verify the result of a route lookup in the RIB matches the * parameters we expect. Intended for testing purposes only. * * @param lookupaddr the destination to be verified. * @param nexthop_addr the expected next hop address. * @param ifname the expected interface. * @param metric the expected routing protocol metric. * @param type the expected type of match. * * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int verify_route(const A& lookupaddr, const string& ifname, const A& nexthop_addr, uint32_t metric, RibVerifyType matchtype); /** * Delete an existing route via the OriginTable called tablename. * * @param tablename the name of the OriginTable in which the * route should be deleted. * @param subnet the subnet (address and prefix length) of the * route to be deleted. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int delete_route(const string& tablename, const IPNet& subnet); /** * Lookup an address in the RIB to determine the nexthop router to * which packets for this address will be forwarded. * * @param lookupaddr the address to be looked up. * @return pointer to address of next hop for @ref lookupaddr if * available, otherwise A::ZERO(). */ virtual const A& lookup_route(const A& lookupaddr); /** * Used for debugging only */ virtual RouteRange* route_range_lookup(const A& lookupaddr); /** * Register interest in being notified about all changes to * routing information that would affect traffic destined for a * particular address. * * @param lookupaddr the address to register interest in. * @param module the XRL module name to which notifications of * changes should be sent. */ virtual RouteRegister* route_register(const A& lookupaddr, const string& module); /** * De-register interest in being notified about all changes to * routing information for a particular address. * * @see RIB::route_register * * @param lookupaddr the address to de-register interest in. * @param module the XRL module name to which notifications of * changes should no longer be sent. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int route_deregister(const IPNet& subnet, const string& module); /** * Find a routing protocol, given its protocol name. * * @param protocol the name of the protocol to search for. * @return pointer to protocol if exists, NULL otherwise. */ Protocol* find_protocol(const string& protocol); /** * Get route redistribution table for specified routing protocol. */ RedistTable* protocol_redist_table(const string& protocol); /** * Create the OriginTable for an IGP protocol and plumb it into * the RIB. Typically this will be called when a new instance of * an IGP routing protocol such as OSPF starts up. * * @param tablename the routing protocol name. This should be one * of the list of names the RIB knows about, or the incorrect * default administrative distance will be applied. * @param target_class the XRL target class of the routing * protocol that will supply routes to this OriginTable. * @param target_instance the XRL target instance of the routing * protocol that will supply routes to this OriginTable. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int add_igp_table(const string& tablename, const string& target_class, const string& target_instance); /** * Delete the OriginTable for an IGP protocol and unplumb it from * the RIB. Typically this will be called when an instance of * an IGP routing protocol such as OSPF exits. * * @param tablename the routing protocol name, previously * registered using @ref add_igp_table. * @param target_class the XRL target class of the routing * protocol that supplied routes to this OriginTable. * @param target_instance the XRL target instance of the routing * protocol that supplied routes to this OriginTable. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int delete_igp_table(const string& tablename, const string& target_class, const string& target_instance); /** * Create the OriginTable for an EGP protocol and plumb it into * the RIB. Typically this will be called when a new instance of * an EGP routing protocol such as EBGP or IBGP starts up. Note * that EBGP and IBGP should register separately. * * @param tablename the routing protocol name. This should be one * of the list of names the RIB knows about, or the incorrect * default administrative distance will be applied. * @param target_class the XRL target class of the routing * protocol that will supply routes to this OriginTable. * @param target_instance the XRL target instance of the routing * protocol that will supply routes to this OriginTable. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int add_egp_table(const string& tablename, const string& target_class, const string& target_instance); /** * Delete the OriginTable for an EGP protocol and unplumb it from * the RIB. Typically this will be called when an instance of * an EGP routing protocol such as BGP exits. * * @param tablename the routing protocol name, previously * registered using @ref add_igp_table. * @param target_class the XRL target class of the routing * protocol that supplied routes to this OriginTable. * @param target_instance the XRL target instance of the routing * protocol that supplied routes to this OriginTable. * @return XORP_OK on success, otherwise XORP_ERROR. */ virtual int delete_egp_table(const string& tablename, const string& target_class, const string& target_instance); /** * An XRL Target died. We need to check if it's a routing * protocol, and if it was, clean up after it. * * @param target_class the XRL Class of the target that died. * @param target_instance the XRL Class Instance of the target that died. */ void target_death(const string& target_class, const string& target_instance); /** * Print the RIB structure for debugging */ void print_rib() const; /** * Get RIB name. */ string name() const; /** * Push routes through policy filters for re-filtering. */ void push_routes(); /** * Set the admin distance associated with a routing protocol. * * @param protocol_name the canonical name of a routing protocol, * in lower case. Eg "ospf", "ibgp", etc. * @param admin_distance the admin distance to set. * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_protocol_admin_distance(const string& protocol_name, const uint32_t& admin_distance); /** * Get the map of all registered admin distances. * * @return a reference to the _admin_distance map. */ map& get_protocol_admin_distances() { return _admin_distances; } /** * Get the admin distance associated with a routing protocol. * * @param protocol_name the canonical name of a routing protocol, * in lower case. Eg "ospf", "ibgp", etc. * @return the admin distance; UNKNOWN_ADMIN_DISTANCE if unknown. */ uint32_t get_protocol_admin_distance(const string& protocol_name); private: /** * Used to implement @ref add_igp_table and @ref add_egp_table. * * @param tablename the routing protocol name. * @param target_class the XRL target class of the routing * protocol that will supply routes to this OriginTable. * @param target_instance the XRL target instance of the routing * protocol that will supply routes to this OriginTable. * @param protocol_type the routing protocol type (@ref ProtocolType). * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_origin_table(const string& tablename, const string& target_class, const string& target_instance, ProtocolType protocol_type); /** * Used to implement @ref delete_igp_table and @ref delete_egp_table. * * @param tablename the routing protocol name. * @param target_class the XRL target class of the routing * protocol that will supply routes to this OriginTable. * @param target_instance the XRL target instance of the routing * protocol that supplied routes to this OriginTable. * @return XORP_OK on success, otherwise XORP_ERROR. */ int delete_origin_table(const string& tablename, const string& target_class, const string& target_instance); /** * Add a table for policy filtering of connected routes. * * This is used to enable route redistribution of connected routes. * * @param origin_tablename The name of the origin table. * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_policy_connected_table(const string& origin_tablename); /** * Add a RedistTable behind OriginTable. This allows routes * associated with the OriginTable to be redistributed in future. * * @param origin_tablename Name of OriginTable. * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_redist_table(const string& origin_tablename); /** * track_back trough the RouteTables' parent pointers to find the * last (i.e, nearest the OriginTable) table that matches the mask * in @ref typemask. If the table given by @ref rt doesn't match * the mask, return it anyway. If a table has more than one * parent, then this is an error. * * @param rt the routing table to start with. * @param typemask the bitwise-or of the routing table types that * we may track back through. * @return the last matching table, or @ref rt if rt itself doesn't match. */ RouteTable* track_back(RouteTable* rt, int typemask) const; /** * track_forward trough the RouteTables' child pointers to find * the last able that matches the mask in @ref typemask. * Unlike track_back, if @ref rt doesn't match, but the next does, * the track forward anyway. * * @param rt the routing table to start with. * @param typemask the bitwise-or of the routing table types that * we may track forward through. * @return the last matching table. */ RouteTable* track_forward(RouteTable* rt, int typemask) const; /** * Find a routing table, given its table name * * @param tablename the name of the table to search for. * @return pointer to table if exists, NULL otherwise. */ RouteTable* find_table(const string& tablename); /** * Find a routing table, given its protocol name and XRL target * instance name. * * @param tablename the name of the protocol to search for. * @param target_class the name of the target class to search for. * @param target_instance the name of the target instance to search for. * @return pointer to table if exists, NULL otherwise. */ OriginTable* find_table_by_instance(const string& tablename, const string& target_class, const string& target_instance); /** * Add table to RIB, but don't do any plumbing. * * It is an error to add the same table twice or multiple tables * with the same name. * * @param table the table to be added. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_table(RouteTable* table); /** * Remove table from RIB, but don't do any unplumbing. * The table is not deleted by this. * * @param tablename the name of the table to be removed. * @return XORP_OK on success, otherwise XORP_ERROR. */ int remove_table(const string& tablename); /** * Find the virtual interface associated with one of this router's * addresses. * * @param addr the IP address to lookup. * @return pointer to RibVif on success, NULL otherwise. */ RibVif* find_vif(const A& addr); /** * Find the IP External Nexthop class instance associated with an IP * address. * * @param addr the IP address of the nexthop router. * @return pointer to external next hop if it exists, NULL otherwise. */ IPExternalNextHop* find_external_nexthop(const A& addr); /** * Find the IP Peer Nexthop class instance associated with an IP * address. * * @param addr the IP address of the nexthop router. * @return pointer to peer next hop if it exists, NULL otherwise. */ IPPeerNextHop* find_peer_nexthop(const A& addr); /** * Find or create the IP External Nexthop class instance * associated with an IP address. * * @param addr the IP address of the nexthop router. * @return the IPExternalNextHop class instance for @ref addr */ IPExternalNextHop* find_or_create_external_nexthop(const A& addr); /** * Find or create the IP Peer Nexthop class instance * associated with an IP address. * * @param addr the IP address of the nexthop router. * @return the IPPeerNextHop class instance for @ref addr. */ IPPeerNextHop* find_or_create_peer_nexthop(const A& addr); /** * Flush out routing table changes to other processes. */ void flush(); protected: RibManager& _rib_manager; EventLoop& _eventloop; RouteTable* _final_table; RegisterTable* _register_table; bool _multicast; bool _errors_are_fatal; PolicyRedistTable* _policy_redist_table; list* > _tables; map _protocols; map* > _routing_protocol_instances; map _vifs; map _deleted_vifs; map _admin_distances; map > _external_nexthops; map > _peer_nexthops; }; typedef RIB IPv4RIB; #ifdef HAVE_IPV6 typedef RIB IPv6RIB; #endif class RibVif : public Vif { public: RibVif(RIB* rib, const Vif& vif) : Vif(vif), _rib4(rib), #ifdef HAVE_IPV6 _rib6(NULL), #endif _usage_counter(0), _is_deleted(false) {} #ifdef HAVE_IPV6 RibVif(RIB* rib, const Vif& vif) : Vif(vif), _rib4(NULL), _rib6(rib), _usage_counter(0), _is_deleted(false) {} #endif ~RibVif() {} size_t copy_in(const Vif& from_vif) { Vif* to_vif = this; *to_vif = from_vif; return (sizeof(from_vif)); } void set_deleted(bool v) { _is_deleted = v; } uint32_t usage_counter() const { return (_usage_counter); } void incr_usage_counter() { _usage_counter++; } void decr_usage_counter() { _usage_counter--; if (_is_deleted && (_usage_counter == 0)) { if (_rib4 != NULL) { _rib4->destroy_deleted_vif(this); return; } #ifdef HAVE_IPV6 if (_rib6 != NULL) { _rib6->destroy_deleted_vif(this); return; } #endif } } private: // // XXX: This is a hack that we have two RIB pointers, when one should // be sufficient. It is to avoid templatization of this class, otherwise // the generic RouteEntry also needs to become a template. // RIB* _rib4; #ifdef HAVE_IPV6 RIB* _rib6; #endif uint32_t _usage_counter; bool _is_deleted; }; #endif // __RIB_RIB_HH__ xorp/rib/xrl_target.hh0000664000076400007640000006756211540225533015150 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/rib/xrl_target.hh,v 1.38 2008/10/02 21:58:14 bms Exp $ #ifndef __RIB_XRL_TARGET_HH__ #define __RIB_XRL_TARGET_HH__ #include "libxipc/xrl_router.hh" #include "xrl/targets/rib_base.hh" #include "rib.hh" #include "vifmanager.hh" /** * @short Implement RIB Xrl target methods. * * XrlRibTarget implements the auto-generated sub methods to handle * XRL requests from the routing protocols to the RIB. */ class XrlRibTarget : public XrlRibTargetBase { public: /** * XrlRibTarget constructor * * @param xrl_router the XrlRouter instance handling sending and receiving * XRLs for this process. * @param urib4 the IPv4 unicast RIB. * @param mrib4 the IPv4 multicast RIB. * @param urib6 the IPv6 unicast RIB. * @param mrib6 the IPv6 multicast RIB. * @param vif_manager the VifManager for this process handling * communication with the FEA regarding VIF changes. * @param rib_manager the RibManager for this process. */ XrlRibTarget(XrlRouter* xrl_router, RIB& urib4, RIB& mrib4, #ifdef HAVE_IPV6 RIB& urib6, RIB& mrib6, #endif VifManager& vif_manager, RibManager* rib_manager) : XrlRibTargetBase(xrl_router), _urib4(urib4), _mrib4(mrib4), #ifdef HAVE_IPV6 _urib6(urib6), _mrib6(mrib6), #endif _vif_manager(vif_manager), _rib_manager(rib_manager) {} /** * XrlRibTarget destructor */ ~XrlRibTarget() {} protected: RIB& _urib4; RIB& _mrib4; #ifdef HAVE_IPV6 RIB& _urib6; RIB& _mrib6; #endif VifManager& _vif_manager; RibManager* _rib_manager; protected: /** * Get name of Xrl Target */ XrlCmdError common_0_1_get_target_name( // Output values, string& name); /** * Get version string from Xrl Target */ XrlCmdError common_0_1_get_version( // Output values, string& version); /** * Get status of Xrl Target */ XrlCmdError common_0_1_get_status( // Output values, uint32_t& status, string& reason); /** * Request clean shutdown of Xrl Target */ XrlCmdError common_0_1_shutdown(); XrlCmdError common_0_1_startup() { return rib_0_1_start_rib(); } /** * Enable/disable/start/stop RIB. */ XrlCmdError rib_0_1_enable_rib(); XrlCmdError rib_0_1_disable_rib(); XrlCmdError rib_0_1_start_rib(); XrlCmdError rib_0_1_stop_rib(); /** * Make errors fatal; used to detect errors we'd normally mask */ XrlCmdError rib_0_1_make_errors_fatal(); /** * Get the list of registered protocols. * * @param ipv4 if true, then include the IPv4 protocols. * * @param ipv6 if true, then include the IPv6 protocols. * * @param unicast if true, then include the protocols registered with the * unicast RIB. * * @param multicast if true, then include the protocols registered with * the multicast RIB. * * @param ipv4_unicast_protocols the list of IPv4 protocols registered * with the unicast RIB. * * @param ipv6_unicast_protocols the list of IPv6 protocols registered * with the unicast RIB. * * @param ipv4_multicast_protocols the list of IPv4 protocols registered * with the multicast RIB. * * @param ipv6_multicast_protocols the list of IPv6 protocols registered * with the multicast RIB. */ XrlCmdError rib_0_1_get_registered_protocols( // Input values, const bool& ipv4, const bool& ipv6, const bool& unicast, const bool& multicast, // Output values, XrlAtomList& ipv4_unicast_protocols, XrlAtomList& ipv6_unicast_protocols, XrlAtomList& ipv4_multicast_protocols, XrlAtomList& ipv6_multicast_protocols); /** * Add/delete an IGP or EGP table. * * @param protocol the name of the protocol. * * @param target_class the target class of the protocol. * * @param target_instance the target instance of the protocol. * * @param unicast true if the table is for the unicast RIB. * * @param multicast true if the table is for the multicast RIB. */ XrlCmdError rib_0_1_add_igp_table4( // Input values, const string& protocol, const string& target_class, const string& target_instance, const bool& unicast, const bool& multicast); XrlCmdError rib_0_1_delete_igp_table4( // Input values, const string& protocol, const string& target_class, const string& target_instance, const bool& unicast, const bool& multicast); XrlCmdError rib_0_1_add_egp_table4( // Input values, const string& protocol, const string& target_class, const string& target_instance, const bool& unicast, const bool& multicast); XrlCmdError rib_0_1_delete_egp_table4( // Input values, const string& protocol, const string& target_class, const string& target_instance, const bool& unicast, const bool& multicast); /** * Add/replace/delete a route. * * @param protocol the name of the protocol this route comes from. * * @param unicast true if the route is for the unicast RIB. * * @param multicast true if the route is for the multicast RIB. * * @param network the network address prefix of the route. * * @param nexthop the address of the next-hop router toward the * destination. * * @param metric the routing metric. * * @param policytags the policy-tags for this route. */ XrlCmdError rib_0_1_add_route4( // Input values, const string& protocol, const bool& unicast, const bool& multicast, const IPv4Net& network, const IPv4& nexthop, const uint32_t& metric, const XrlAtomList& policytags); XrlCmdError rib_0_1_replace_route4( // Input values, const string& protocol, const bool& unicast, const bool& multicast, const IPv4Net& network, const IPv4& nexthop, const uint32_t& metric, const XrlAtomList& policytags); XrlCmdError rib_0_1_delete_route4( // Input values, const string& protocol, const bool& unicast, const bool& multicast, const IPv4Net& network); /** * Add/replace a route by explicitly specifying the network interface * toward the destination. * * @param protocol the name of the protocol this route comes from. * * @param unicast true if the route is for the unicast RIB. * * @param multicast true if the route is for the multicast RIB. * * @param network the network address prefix of the route. * * @param nexthop the address of the next-hop router toward the * destination. * * @param ifname of the name of the physical interface toward the * destination. * * @param vifname of the name of the virtual interface toward the * destination. * * @param metric the routing metric. * * @param policytags the policy-tags for this route. */ XrlCmdError rib_0_1_add_interface_route4( // Input values, const string& protocol, const bool& unicast, const bool& multicast, const IPv4Net& network, const IPv4& nexthop, const string& ifname, const string& vifname, const uint32_t& metric, const XrlAtomList& policytags); XrlCmdError rib_0_1_replace_interface_route4( // Input values, const string& protocol, const bool& unicast, const bool& multicast, const IPv4Net& network, const IPv4& nexthop, const string& ifname, const string& vifname, const uint32_t& metric, const XrlAtomList& policytags); /** * Lookup nexthop. * * @param addr address to lookup. * * @param unicast look in unicast RIB. * * @param multicast look in multicast RIB. * * @param nexthop contains the resolved nexthop if successful, IPv4::ZERO * otherwise. It is an error for the unicast and multicast fields to both * be true or both false. */ XrlCmdError rib_0_1_lookup_route_by_dest4( // Input values, const IPv4& addr, const bool& unicast, const bool& multicast, // Output values, IPv4& nexthop); /** * Add a vif or a vif address to the RIB. This interface should be used * only for testing purpose. * * @param name the name of the vif. */ XrlCmdError rib_0_1_new_vif( // Input values, const string& name); /** * Add a vif address to the RIB. This interface should be used only for * testing purpose. * * @param name the name of the vif. * * @param addr the address to add. * * @param subnet the subnet address to add. */ XrlCmdError rib_0_1_add_vif_addr4( // Input values, const string& name, const IPv4& addr, const IPv4Net& subnet); /** * Enable route redistribution from one routing protocol to another. * * @param to_xrl_target the XRL Target instance name of the caller. The * caller must implement redist4/0.1. * * @param from_protocol the name of the routing process routes are to be * redistributed from. * * @param unicast enable for unicast RIBs matching from and to. * * @param multicast enable for multicast RIBs matching from and to. * * @param network_prefix redistribite only the routes that fall into this * prefix address. * * @param cookie a text value passed back to creator in each call from the * RIB. This allows creators to identity the source of updates it receives * through the redist4/0.1 interface. */ XrlCmdError rib_0_1_redist_enable4( // Input values, const string& to_xrl_target, const string& from_protocol, const bool& unicast, const bool& multicast, const IPv4Net& network_prefix, const string& cookie); /** * Disable route redistribution from one routing protocol to another. * * @param to_xrl_target the XRL Target instance name of the caller. The * caller must implement redist4/0.1 and previously called redist_enable4. * * @param unicast disable for unicast RIBs matching from and to. * * @param multicast disable for multicast RIBs matching from and to. * * @param cookie a text value passed back to creator in each call from the * RIB. This allows creators to identity the source of updates it receives * through the redist4/0.1 interface. */ XrlCmdError rib_0_1_redist_disable4( // Input values, const string& to_xrl_target, const string& from_protocol, const bool& unicast, const bool& multicast, const string& cookie); /** * Enable transaction-based route redistribution from one routing protocol * to another. * * @param to_xrl_target the XRL Target instance name of the caller. The * caller must implement redist_transaction4/0.1. * * @param from_protocol the name of the routing process routes are to be * redistributed from. * * @param unicast enable for unicast RIBs matching from and to. * * @param multicast enable for multicast RIBs matching from and to. * * @param network_prefix redistribite only the routes that fall into this * prefix address. * * @param cookie a text value passed back to creator in each call from the * RIB. This allows creators to identity the source of updates it receives * through the redist_transaction4/0.1 interface. */ XrlCmdError rib_0_1_redist_transaction_enable4( // Input values, const string& to_xrl_target, const string& from_protocol, const bool& unicast, const bool& multicast, const IPv4Net& network_prefix, const string& cookie); /** * Disable transaction-based route redistribution from one routing * protocol to another. * * @param to_xrl_target the XRL Target instance name of the caller. The * caller must implement redist_transaction4/0.1 and previously called * redist_transaction_enable4. * * @param unicast disable for unicast RIBs matching from and to. * * @param multicast disable for multicast RIBs matching from and to. * * @param cookie a text value passed back to creator in each call from the * RIB. This allows creators to identity the source of updates it receives * through the redist_transaction4/0.1 interface. */ XrlCmdError rib_0_1_redist_transaction_disable4( // Input values, const string& to_xrl_target, const string& from_protocol, const bool& unicast, const bool& multicast, const string& cookie); /** * Register an interest in a route. * * @param target the name of the XRL module to notify when the information * returned by this call becomes invalid. * * @param addr address of interest. * * @param resolves returns whether or not the address resolves to a route * that can be used for forwarding. * * @param base_addr returns the address of interest (actually the base * address of the subnet covered by addr/prefix_len). * * @param prefix_len returns the prefix length that the registration * covers. This response applies to all addresses in addr/prefix_len. * * @param real_prefix_len returns the actual prefix length of the route * that will be used to route addr. If real_prefix_len is not the same as * prefix_len, this is because there are some more specific routes that * overlap addr/real_prefix_len. real_prefix_len is primarily given for * debugging reasons. * * @param nexthop returns the address of the next hop for packets sent to * addr. * * @param metric returns the IGP metric for this route. */ XrlCmdError rib_0_1_register_interest4( // Input values, const string& target, const IPv4& addr, // Output values, bool& resolves, IPv4& base_addr, uint32_t& prefix_len, uint32_t& real_prefix_len, IPv4& nexthop, uint32_t& metric); /** * De-register an interest in a route. * * @param target the name of the XRL module that registered the interest. * * @param addr the address of the previous registered interest. addr * should be the base address of the add/prefix_len subnet. * * @param prefix_len the prefix length of the registered interest, as * given in the response from register_interest. */ XrlCmdError rib_0_1_deregister_interest4( // Input values, const string& target, const IPv4& addr, const uint32_t& prefix_len); /** * Get the configured admin distances from a selected RIB * for all routing protocols configured with one. * * @param ipv4 true if we're looking in the ipv4 RIB. * false if we're looking in the ipv6 RIB. * @param unicast true if we're looking in the unicast RIB. * false if we're looking in the multicast RIB. * @param protocols the name of the protocols. * @param admin_distance the returned admin distances. */ XrlCmdError rib_0_1_get_protocol_admin_distances( // Input values, const bool& ipv4, const bool& unicast, // Output values, XrlAtomList& protocols, XrlAtomList& admin_distances); /** * Get the configured admin distance for a routing protocol from * a selected RIB. * * @param protocol the name of the protocol. * @param unicast true if we're looking in the unicast RIB. * @param multicast true if we're looking in the multicast RIB. * @param admin_distance the returned admin distance. */ XrlCmdError rib_0_1_get_protocol_admin_distance( // Input values, const string& protocol, const bool& unicast, const bool& multicast, // Output values, uint32_t& admin_distance); /** * Set the configured admin distance for a routing protocol in * one or many RIBs. * * @param protocol the name of the protocol. * @param ipv4 true if we should set it for the ipv4 RIBs. * @param ipv6 true if we should set it for the ipv6 RIBs. * @param unicast true if we should set it for the unicast RIBs. * @param multicast true if we should set it for the multicast RIBs. * @param admin_distance the admin distance. */ XrlCmdError rib_0_1_set_protocol_admin_distance( // Input values, const string& protocol, const bool& ipv4, const bool& ipv6, const bool& unicast, const bool& multicast, const uint32_t& admin_distance); /** * Announce target birth. */ XrlCmdError finder_event_observer_0_1_xrl_target_birth( // Input values, const string& target_class, const string& target_instance); /** * Announce target death. */ XrlCmdError finder_event_observer_0_1_xrl_target_death( // Input values, const string& target_class, const string& target_instance); /** * Configure a policy filter. * * @param filter id of filter to configure. * @param conf configuration of filter. */ XrlCmdError policy_backend_0_1_configure( // Input values, const uint32_t& filter, const string& conf); /** * Reset a policy filter. * * @param filter id of filter to reset. */ XrlCmdError policy_backend_0_1_reset( // Input values, const uint32_t& filter); /** * Push routes through policy filters for re-filtering. */ XrlCmdError policy_backend_0_1_push_routes(); /** * Redistribute to a protocol based on policy-tags. * * @param protocol protocol to redistribute to * @param policytags policy-tags of routes which need to be redistributed. */ XrlCmdError rib_0_1_insert_policy_redist_tags( // Input values, const string& protocol, const XrlAtomList& policytags); /** * Reset policy redistribution map. */ XrlCmdError rib_0_1_reset_policy_redist_tags(); #ifdef HAVE_IPV6 XrlCmdError rib_0_1_add_igp_table6( // Input values, const string& protocol, const string& target_class, const string& target_instance, const bool& unicast, const bool& multicast); XrlCmdError rib_0_1_delete_igp_table6( // Input values, const string& protocol, const string& target_class, const string& target_instance, const bool& unicast, const bool& multicast); XrlCmdError rib_0_1_add_egp_table6( // Input values, const string& protocol, const string& target_class, const string& target_instance, const bool& unicast, const bool& multicast); XrlCmdError rib_0_1_delete_egp_table6( // Input values, const string& protocol, const string& target_class, const string& target_instance, const bool& unicast, const bool& multicast); XrlCmdError rib_0_1_add_route6( // Input values, const string& protocol, const bool& unicast, const bool& multicast, const IPv6Net& network, const IPv6& nexthop, const uint32_t& metric, const XrlAtomList& policytags); XrlCmdError rib_0_1_replace_route6( // Input values, const string& protocol, const bool& unicast, const bool& multicast, const IPv6Net& network, const IPv6& nexthop, const uint32_t& metric, const XrlAtomList& policytags); XrlCmdError rib_0_1_delete_route6( // Input values, const string& protocol, const bool& unicast, const bool& multicast, const IPv6Net& network); XrlCmdError rib_0_1_add_interface_route6( // Input values, const string& protocol, const bool& unicast, const bool& multicast, const IPv6Net& network, const IPv6& nexthop, const string& ifname, const string& vifname, const uint32_t& metric, const XrlAtomList& policytags); XrlCmdError rib_0_1_replace_interface_route6( // Input values, const string& protocol, const bool& unicast, const bool& multicast, const IPv6Net& network, const IPv6& nexthop, const string& ifname, const string& vifname, const uint32_t& metric, const XrlAtomList& policytags); /** * Lookup nexthop. * * @param addr address to lookup. * * @param unicast look in unicast RIB. * * @param multicast look in multicast RIB. * * @param nexthop contains the resolved nexthop if successful, IPv6::ZERO * otherwise. It is an error for the unicast and multicast fields to both * be true or both false. */ XrlCmdError rib_0_1_lookup_route_by_dest6( // Input values, const IPv6& addr, const bool& unicast, const bool& multicast, // Output values, IPv6& nexthop); XrlCmdError rib_0_1_add_vif_addr6( // Input values, const string& name, const IPv6& addr, const IPv6Net& subnet); /** * Enable route redistribution from one routing protocol to another. * * @param to_xrl_target the XRL Target instance name of the caller. The * caller must implement redist6/0.1. * * @param from_protocol the name of the routing process routes are to be * redistributed from. * * @param unicast enable for unicast RIBs matching from and to. * * @param multicast enable for multicast RIBs matching from and to. * * @param network_prefix redistribite only the routes that fall into this * prefix address. * * @param cookie a text value passed back to creator in each call from the * RIB. This allows creators to identity the source of updates it receives * through the redist6/0.1 interface. */ XrlCmdError rib_0_1_redist_enable6( // Input values, const string& to_xrl_target, const string& from_protocol, const bool& unicast, const bool& multicast, const IPv6Net& network_prefix, const string& cookie); /** * Disable route redistribution from one routing protocol to another. * * @param to_xrl_target the XRL Target instance name of the caller. The * caller must implement redist6/0.1 and previously called redist_enable6. * * @param unicast disable for unicast RIBs matching from and to. * * @param multicast disable for multicast RIBs matching from and to. * * @param cookie a text value passed back to creator in each call from the * RIB. This allows creators to identity the source of updates it receives * through the redist6/0.1 interface. */ XrlCmdError rib_0_1_redist_disable6( // Input values, const string& to_xrl_target, const string& from_protocol, const bool& unicast, const bool& multicast, const string& cookie); /** * Enable transaction-based route redistribution from one routing protocol * to another. * * @param to_xrl_target the XRL Target instance name of the caller. The * caller must implement redist_transaction6/0.1. * * @param from_protocol the name of the routing process routes are to be * redistributed from. * * @param unicast enable for unicast RIBs matching from and to. * * @param multicast enable for multicast RIBs matching from and to. * * @param network_prefix redistribite only the routes that fall into this * prefix address. * * @param cookie a text value passed back to creator in each call from the * RIB. This allows creators to identity the source of updates it receives * through the redist_transaction6/0.1 interface. */ XrlCmdError rib_0_1_redist_transaction_enable6( // Input values, const string& to_xrl_target, const string& from_protocol, const bool& unicast, const bool& multicast, const IPv6Net& network_prefix, const string& cookie); /** * Disable transaction-based route redistribution from one routing * protocol to another. * * @param to_xrl_target the XRL Target instance name of the caller. The * caller must implement redist_transaction6/0.1 and previously called * redist_transaction_enable6. * * @param unicast disable for unicast RIBs matching from and to. * * @param multicast disable for multicast RIBs matching from and to. * * @param cookie a text value passed back to creator in each call from the * RIB. This allows creators to identity the source of updates it receives * through the redist_transaction6/0.1 interface. */ XrlCmdError rib_0_1_redist_transaction_disable6( // Input values, const string& to_xrl_target, const string& from_protocol, const bool& unicast, const bool& multicast, const string& cookie); /** * Register an interest in a route. * * @param target the name of the XRL module to notify when the information * returned by this call becomes invalid. * * @param addr address of interest. * * @param resolves returns whether or not the address resolves to a route * that can be used for forwarding. * * @param base_addr returns the address of interest (actually the base * address of the subnet covered by addr/prefix_len). * * @param prefix_len returns the prefix length that the registration * covers. This response applies to all addresses in addr/prefix_len. * * @param real_prefix_len returns the actual prefix length of the route * that will be used to route addr. If real_prefix_len is not the same as * prefix_len, this is because there are some more specific routes that * overlap addr/real_prefix_len. real_prefix_len is primarily given for * debugging reasons. * * @param nexthop returns the address of the next hop for packets sent to * addr. * * @param metric returns the IGP metric for this route. */ XrlCmdError rib_0_1_register_interest6( // Input values, const string& target, const IPv6& addr, // Output values, bool& resolves, IPv6& base_addr, uint32_t& prefix_len, uint32_t& real_prefix_len, IPv6& nexthop, uint32_t& metric); /** * De-register an interest in a route. * * @param target the name of the XRL module that registered the interest. * * @param addr the address of the previous registered interest. addr * should be the base address of the add/prefix_len subnet. * * @param prefix_len the prefix length of the registered interest, as * given in the response from register_interest. */ XrlCmdError rib_0_1_deregister_interest6( // Input values, const string& target, const IPv6& addr, const uint32_t& prefix_len); #endif //ipv6 #ifndef XORP_DISABLE_PROFILE /** * Enable profiling * * @param pname profile variable */ XrlCmdError profile_0_1_enable( // Input values, const string& pname); /** * Disable profiling * * @param pname profile variable */ XrlCmdError profile_0_1_disable( // Input values, const string& pname); /** * Get log entries. * * @param pname profile variable * * @param instance_name to send the profiling info to. */ XrlCmdError profile_0_1_get_entries( // Input values, const string& pname, const string& instance_name); /** * Clear the profiling entries * * @param pname profile variable */ XrlCmdError profile_0_1_clear( // Input values, const string& pname); /** * List all the profiling variables registered with this target. */ XrlCmdError profile_0_1_list( // Output values, string& info); #endif }; #endif // __RIB_XRL_TARGET_HH__ xorp/rib/rt_tab_deletion.hh0000664000076400007640000001167311421137511016117 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/rib/rt_tab_deletion.hh,v 1.14 2008/10/02 21:58:12 bms Exp $ #ifndef __RIB_RT_TAB_DELETION_HH__ #define __RIB_RT_TAB_DELETION_HH__ #include "libxorp/timer.hh" #include "rt_tab_base.hh" class EventLoop; /** * @short RouteTable that performs background deletion of routes when * a routing protocol goes down leaving routes in the RIB. * * Its template class, A, must be either the IPv4 class of the IPv6 class. */ template class DeletionTable : public RouteTable { public: /** * DeletionTable constructor. * * @param tablename used for debugging. * @param parent Upstream routing table (usually an origin table). * @param ip_route_trie the entire route trie from the OriginTable * that contains routes we're going to delete (as a background task). */ DeletionTable(const string& tablename, RouteTable* parent, Trie* >* ip_route_trie, EventLoop& eventloop); /** * DeletionTable destructor. */ ~DeletionTable(); /** * Add a route. If the route was stored in the DeletionTable, * we'll remove it and propagate the delete and add downstream. * * @param route the route entry to be added. * @param caller the caller route table. * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_route(const IPRouteEntry& route, RouteTable* caller); /** * Delete a route. This route MUST NOT be in the DeletionTable trie. * * @param route the route entry to be deleted. * @param caller the caller route table. * @return XORP_OK on success, otherwise XORP_ERROR. */ int delete_route(const IPRouteEntry* route, RouteTable* caller); /** * Delete all the routes that are in this DeletionTable. The * deletion is not propagated downstream, so this is only useful * when shutting down the RIB. */ void delete_all_routes(); /** * Lookup a specific subnet to see if it is in this DeletionTable * or the upstream tables. * * @param net the subnet to look up. * @return a pointer to the route entry if it exists, NULL otherwise. */ const IPRouteEntry* lookup_route(const IPNet& net) const; /** * Lookup an IP address to get the most specific (longest prefix * length) route in the DeletionTable or the upstream tables that * matches this address. * * @param addr the IP address to look up. * @return a pointer to the most specific route entry if any entry * matches, NULL otherwise. */ const IPRouteEntry* lookup_route(const A& addr) const; /** * Lookup an IP addressto get the most specific (longest prefix * length) route in the union of the DeletionTable and the * upstream tables that matches this address, along with the * RouteRange information for this address and route. * * @see RouteRange * @param addr the IP address to look up. * @return a pointer to a RouteRange class instance containing the * relevant answer. It is up to the recipient of this pointer to * free the associated memory. */ RouteRange* lookup_route_range(const A& addr) const; /** * Delete a route, and reschedule background_deletion_pass again * on a zero-second timer until all the routes have been deleted */ void background_deletion_pass(); /** * Remove ourself from the plumbing and delete ourself. */ void unplumb_self(); /** * @return the table type (@ref TableType). */ TableType type() const { return DELETION_TABLE; } /** * Change the parent of this route table. */ void replumb(RouteTable* old_parent, RouteTable* new_parent); /** * Render the DeletionTable as a string for debugging purposes. */ string str() const; RouteTable* parent() { return _parent; } private: RouteTable* _parent; EventLoop& _eventloop; Trie* >* _ip_route_table; XorpTimer _background_deletion_timer; }; #endif // __RIB_RT_TAB_DELETION_HH__ xorp/rib/route.hh0000664000076400007640000003312611540224234014115 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/rib/route.hh,v 1.29 2008/10/02 21:58:12 bms Exp $ #ifndef __RIB_ROUTE_HH__ #define __RIB_ROUTE_HH__ #include "libxorp/xorp.h" #include "libxorp/ipv4net.hh" #include "libxorp/ipv6net.hh" #include "libxorp/vif.hh" #include "libxorp/nexthop.hh" #include "policy/backend/policytags.hh" #include "protocol.hh" class RibVif; /** * @short Base class for RIB routing table entries. * * This is the base class from which RIB routing table entries are derived. * It's not useful by itself. */ class RouteEntry { public: /** * Constructor for a route entry. * * @param vif the Virtual Interface on which packets matching this * routing table entry should be forwarded. * @param nexthop the NextHop router to which packets matching this * entry should be forwarded. * @param protocol the routing protocol that originated this route. * @param metric the routing protocol metric for this route. */ RouteEntry(RibVif* vif, NextHop* nexthop, const Protocol& protocol, uint32_t metric); /** * Destructor */ virtual ~RouteEntry(); /** * Get the VIF. * * @return the Virtual Interface on which packets matching this * routing table entry should be forwarded. */ RibVif* vif() const { return _vif; } /** * Get the NextHop router. * * @return the NextHop router to which packets matching this * entry should be forwarded. */ NextHop* nexthop() const { return _nexthop; } /** * Set the NextHop router. * * @param v the NextHop router to be set on this route. */ void set_nexthop(NextHop* v) { _nexthop = v; } /** * Get the Administrative Distance. * * @return the Administrative Distance associated with this route. * Admin Distance is a parameter typically assigned on a * per-routing-protocol basis. When two routes for the same * subnet come from different routing protocols, the one with the * lower admin distance is prefered. */ uint16_t admin_distance() const { return _admin_distance; } /** * Set the Administrative Distance. * * @param ad the administrative distance to apply to this route. */ void set_admin_distance(uint16_t ad) { _admin_distance = ad; } /** * Get the routing protocol. * * @return the routing protocol that originated this route. * @see Protocol. */ const Protocol& protocol() const { return _protocol; } /** * Display the route for debugging purposes. */ virtual string str() const = 0; /** * Set the routing protocol metric on this route. * * @param metric the routing protocol metric to be set on this route. */ void set_metric(uint32_t metric) { _metric = metric; } /** * Get the routing protocol metric. * * @return the routing protocol metric for this route. */ uint32_t metric() const { return _metric; } protected: RibVif* _vif; NextHop* _nexthop; // The next-hop router // The routing protocol that instantiated this route const Protocol& _protocol; uint16_t _admin_distance; // Lower is better uint32_t _metric; // Lower is better }; /** * @short Routing Table Entry * * This class stores a regular RIB routing table entry for either an * IPv4 or an IPv6 route. It is a template class, where A is either a * the IPv4 class or the IPv6 class. */ template class IPRouteEntry : public RouteEntry { public: /** * Constructor for IPRouteEntry. * * @param net the Subnet (address and mask) of the routing table entry. * @param vif the Virtual Interface on which packets matching this * routing table entry should be forwarded. * @param nexthop the NextHop router to which packets matching this * entry should be forwarded. * @param protocol the routing protocol that originated this route. * @param metric the routing protocol metric for this route. */ IPRouteEntry(const IPNet& net, RibVif* vif, NextHop* nexthop, const Protocol& protocol, uint32_t metric) : RouteEntry(vif, nexthop, protocol, metric), _net(net) {} /** * Constructor for IPRouteEntry. * * @param net the Subnet (address and mask) of the routing table entry. * @param vif the Virtual Interface on which packets matching this * routing table entry should be forwarded. * @param nexthop the NextHop router to which packets matching this * entry should be forwarded. * @param protocol the routing protocol that originated this route. * @param metric the routing protocol metric for this route. * @param policytags the policy-tags for this route. */ IPRouteEntry(const IPNet& net, RibVif* vif, NextHop* nexthop, const Protocol& protocol, uint32_t metric, const PolicyTags& policytags) : RouteEntry(vif, nexthop, protocol, metric), _net(net), _policytags(policytags) {} /** * Destructor for Routing Table Entry */ ~IPRouteEntry() {} /** * Get the route entry's subnet addres. * * @return the route entry's subnet address. */ const IPNet& net() const { return _net; } /** * Get the prefix length of the route entry's subnet address. * * @return the prefix length (in bits) of the route entry's subnet address. */ size_t prefix_len() const { return _net.prefix_len(); } /** * Get the route entry's next-hop router address. * * @return the route entry's next-hop router address. If there is no * next-hop router, then the return value is IPv4#ZERO() or IPv6#ZERO(). */ const A& nexthop_addr() const { IPNextHop* nh = reinterpret_cast* >(nexthop()); if (nh != NULL) return nh->addr(); else { return A::ZERO(); } } /** * Get the policy-tags for this route. * * @return the policy-tags for this route. */ PolicyTags& policytags() { return _policytags; } const PolicyTags& policytags() const { return _policytags; } /** * Get the route entry as a string for debugging purposes. * * @return a human readable representation of the route entry. */ string str() const; protected: IPNet _net; // The route entry's subnet address PolicyTags _policytags; // Tags used for policy route redistribution }; typedef IPRouteEntry IPv4RouteEntry; typedef IPRouteEntry IPv6RouteEntry; /** * @short Extended RouteEntry, used by ExtIntTable. * * This class stored an extended routing table entry, for use in * ExtIntTable. When a route with a non-local nexthop arrives, the * ExtIntTable attempts to discover a local nexthop by finding the * route that packets to the non-local nexthop would use. This entry * is used to store the resulting route, with a local nexthop, and * with links to the parent routes that were used to provide the * information in this route entry. * * This is a template class, where A is either a the IPv4 class or the * IPv6 class. */ template class ResolvedIPRouteEntry : public IPRouteEntry { public: typedef multimap* , ResolvedIPRouteEntry* > RouteBackLink; public: /** * Constructor for IPRouteEntry. * * @param net the Subnet (address and mask) of the routing table entry. * @param vif the Virtual Interface on which packets matching this * routing table entry should be forwarded. * @param nexthop the NextHop router to which packets matching this * entry should be forwarded. This should be a local nexthop. * @param protocol the routing protocol that originated this route. * @param metric the routing protocol metric for this route. * @param igp_parent the route entry used to resolve the non-local * nexthop in the egp_parent into a local nexthop. * @param egp_parent the orginal route entry with a non-local nexthop. */ ResolvedIPRouteEntry(const IPNet& net, RibVif* vif, NextHop* nexthop, const Protocol& protocol, uint32_t metric, const IPRouteEntry* igp_parent, const IPRouteEntry* egp_parent) : IPRouteEntry(net, vif, nexthop, protocol, metric, PolicyTags()), _igp_parent(igp_parent), _egp_parent(egp_parent) { } /** * Get the igp_parent. * * @return the IGP parent route entry that was used to resolve the * EGP parent route entry's non-local nexthop into a local nexthop. */ const IPRouteEntry* igp_parent() const { return _igp_parent; } /** * Get the EGP parent. * * @return the EGP parent, which is the original route entry that * had a non-local nexthop. */ const IPRouteEntry* egp_parent() const { return _egp_parent; } /** * Set the backlink. When a resolved route is created, the * ExtIntTable will store a link to it in a multimap that is * indexed by the IGP parent. This will allow all the routes * affected by a change in the IGP parent to be found easily. * However, if the EGP parent goes away, we need to remove the * links from this multimap, and the backlink provides an iterator * into the multimap that makes this operation very efficient. * * @param backlink the ExtIntTable multimap iterator for this route. */ void set_backlink(typename RouteBackLink::iterator v); /** * Get the backlink. * @see ResolvedIPRouteEntry::set_backlink * * @return the backlink iterator. */ typename RouteBackLink::iterator backlink() const; private: mutable const IPRouteEntry* _igp_parent; mutable const IPRouteEntry* _egp_parent; // _backlink is used for removing the corresponding entry from the // RouteTable's map that is indexed by igp_parent. Without it, // route deletion would be expensive. typename RouteBackLink::iterator _backlink; }; template inline void ResolvedIPRouteEntry::set_backlink(typename RouteBackLink::iterator v) { _backlink = v; } template inline typename ResolvedIPRouteEntry::RouteBackLink::iterator ResolvedIPRouteEntry::backlink() const { return _backlink; } typedef ResolvedIPRouteEntry ResolvedIPv4RouteEntry; typedef ResolvedIPRouteEntry ResolvedIPv6RouteEntry; /** * @short Extended Unresolved RouteEntry, used by ExtIntTable. * * This class stored an extended unresolved routing table entry, for use in * ExtIntTable. When a route with a non-local nexthop arrives, the * ExtIntTable attempts to discover a local nexthop by finding the * route that packets to the non-local nexthop would use. If the local * nexthop is not found, this entry is used to store the unresolved route. * * This is a template class, where A is either a the IPv4 class or the * IPv6 class. */ template class UnresolvedIPRouteEntry { public: typedef multimap* > RouteBackLink; public: /** * Constructor for a given IPRouteEntry. * * @param route the IPRouteEntry route. */ UnresolvedIPRouteEntry(const IPRouteEntry* route) : _route(route) {} /** * Get the route. * * @return the route. */ const IPRouteEntry* route() const { return _route; } /** * Set the backlink. When an unresolved route is created, the * ExtIntTable will store a link to it in a multimap that is * indexed by the unresolved nexthop. This will allow all the routes * affected by a change (e.g., resolving the nexthop) to be found easily. * However, if the EGP parent goes away, we need to remove the * links from this multimap, and the backlink provides an iterator * into the multimap that makes this operation very efficient. * * @param backlink the ExtIntTable multimap iterator for this route. */ void set_backlink(typename RouteBackLink::iterator v); /** * Get the backlink. * @see UnresolvedIPRouteEntry::set_backlink * * @return the backlink iterator. */ typename RouteBackLink::iterator backlink() const; private: // // _backlink is used for removing the corresponding entry from the // RouteTable's map that is indexed by the unresolved nexthop. // Without it, route deletion would be expensive. // typename RouteBackLink::iterator _backlink; const IPRouteEntry* _route; }; template inline void UnresolvedIPRouteEntry::set_backlink(typename RouteBackLink::iterator v) { _backlink = v; } template inline typename UnresolvedIPRouteEntry::RouteBackLink::iterator UnresolvedIPRouteEntry::backlink() const { return _backlink; } typedef UnresolvedIPRouteEntry UnresolvedIPv4RouteEntry; typedef UnresolvedIPRouteEntry UnresolvedIPv6RouteEntry; #endif // __RIB_ROUTE_HH__ xorp/rib/rt_tab_redist.hh0000664000076400007640000002324011540225533015603 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/rib/rt_tab_redist.hh,v 1.19 2008/10/02 21:58:13 bms Exp $ #ifndef __RIB_RT_TAB_REDIST_HH__ #define __RIB_RT_TAB_REDIST_HH__ #include "rt_tab_base.hh" template class Redistributor; template class RedistOutput; template class RedistPolicy; #ifndef XORP_USE_USTL /** * Comparitor to allow nets to be stored in a sorted container. */ template struct RedistNetCmp { bool operator() (const IPNet& l, const IPNet& r) const; }; #endif /** * @short RouteTable used to redistribute routes. * * The RedistTable is used to redistribute routes from a routing table * back out to a routing protocol. For example, when you want to * redistribute BGP routes into OSPF. * * For most operations the RedistTable is essentially a passthrough. * It keeps track of nets it hears add and deletes for so when route * @ref Redistributor objects are added to the RedistTable they can * announce the existing routes at startup. * * Design note: RedistTable uses a set of IPNet's to cache routes - this * should be route entry pointers with an appropriate comparitor (not pointer * value based). */ template class RedistTable : public RouteTable { public: #ifdef XORP_USE_USTL typedef set > RouteIndex; #else typedef set,RedistNetCmp > RouteIndex; #endif public: /** * Constructor. * * Plumbs RedistTable in RIB graph after from_table. * * @param from_table table to redistribute routes from. */ RedistTable(const string& tablename, RouteTable* from_table); /** * Destructor. * * Unplumbs table and deletes Redistributor object instances previously * added with add_redistributor and not previously removed with * remove_redistributor. */ ~RedistTable(); /** * Add a redistributor to announce existing routes and future updates * to. */ void add_redistributor(Redistributor* r); /** * Remove a redistributor. */ void remove_redistributor(Redistributor* r); /** * Find redistributor with given name attribute. */ Redistributor* redistributor(const string& name); // // Standard RouteTable methods // int add_route(const IPRouteEntry& route, RouteTable* caller); int delete_route(const IPRouteEntry* route, RouteTable* caller); const IPRouteEntry* lookup_route(const IPNet& net) const; const IPRouteEntry* lookup_route(const A& addr) const; RouteRange* lookup_route_range(const A& addr) const; TableType type() const { return REDIST_TABLE; } RouteTable* parent() { return _parent; } void replumb(RouteTable* old_parent, RouteTable* new_parent); string str() const; /** * Get nets of live routes seen by RedistTable since it was * instantiated. */ const RouteIndex& route_index() const { return _rt_index; } protected: RouteTable* _parent; // Immediately upstream table. May // differ from _from_table if a // Deletion table or another redist // table has been plumbed in. RouteIndex _rt_index; list*> _outputs; }; /** * Controller class that takes routes from RedistTable and passes them * on via RedistOutput. * * Instances of this class are constructed when one routing protocol * requests route distribution from another. Instances walk the * routes available in the RedistTable route index, resolve them, and * announce them via the RedistOutput. Future updates received * from the RedistTable are propagated via the Redistributor instances * associated with it. */ template class Redistributor : public NONCOPYABLE { public: class RedistEventInterface { // Methods only available to RedistTable. void did_add(const IPRouteEntry& ipr); void will_delete(const IPRouteEntry& ipr); void did_delete(const IPRouteEntry& ipr); friend class RedistTable; friend class Redistributor; public: RedistEventInterface(Redistributor* r) : _r(r) {} private: Redistributor* _r; }; class OutputEventInterface { // Methods only available to RedistOutput. These are // events it can tell us about. void low_water(); void high_water(); void fatal_error(); friend class RedistOutput; friend class Redistributor; public: OutputEventInterface(Redistributor* r) : _r(r) {} private: Redistributor* _r; }; public: Redistributor(EventLoop& e, const string& name); virtual ~Redistributor(); const string& name() const; void set_redist_table(RedistTable* rt); /** * Bind RedistOutput to Redistributor instance. The output * should be dynamically allocated with new. When a new * redistributor output is set, the existing output is removed via * delete. The RedistOutput is deleted by the * Redistributor when the Redistributor is destructed. */ void set_output(RedistOutput* output); /** * Bind policy object to Redistributor instance. The policy * should be dynamically allocated with new. When a new policy is * set, the existing policy is removed via delete. The policy is * deleted by the Redistributor when the Redistributor is * destructed. */ void set_policy(RedistPolicy* policy); /** * Determine if policy accepts updates to route. * * @return true if associated property accepts update to route or * if no policy is enforced, false otherwise. */ bool policy_accepts(const IPRouteEntry& ipr) const; /** * Method available to instances of RedistTable to announce events * to the Redistributor instance. */ RedistEventInterface& redist_event() { return _rei; } /** * Method available to instances of RedistOutput to * announce transport events to the Redistributor instance. */ OutputEventInterface& output_event() { return _oei; } /** * Indicate dump status. When Redistributor is first connected it dumps * existing routes to it's RedistOutput. * * @return true if route dump is in process, false if route dump is * either not started or finished. */ bool dumping() const { return _dumping; } private: /** * Start initial route dump when a RedistTable is associated with instance * through set_redist_table(). */ void start_dump(); void finish_dump(); void schedule_dump_timer(); void unschedule_dump_timer(); void dump_a_route(); const IPNet& last_dumped_net() const { return _last_net; } RedistTable* redist_table() { return _table; } RedistOutput* output() { return _output; } private: // These are nested classes and need to be friends to invoke methods in // enclosing class. friend class RedistEventInterface; friend class OutputEventInterface; private: EventLoop& _e; string _name; RedistTable* _table; RedistOutput* _output; RedistPolicy* _policy; RedistEventInterface _rei; OutputEventInterface _oei; bool _dumping; // Announcing existing routes bool _blocked; // Output above high water IPNet _last_net; // Last net announced XorpTimer _dtimer; static const IPNet NO_LAST_NET; // Indicator for last net inval }; /** * Base class for propagaing output of route add and delete messages. */ template class RedistOutput : public NONCOPYABLE { public: RedistOutput(Redistributor* r); virtual ~RedistOutput(); virtual void add_route(const IPRouteEntry& ipr) = 0; virtual void delete_route(const IPRouteEntry& ipr) = 0; /** * Method called by Redistributor to indicate start of initial * route dump. This occurs when an output is first attached to * the redistributor to announce the existing routes. */ virtual void starting_route_dump() = 0; /** * Method called by Redistributor to indicate end of initial * route dump. This occurs when an output is first attached to * the redistributor to announce the existing routes. */ virtual void finishing_route_dump() = 0; protected: void announce_low_water() { _r->output_event().low_water(); } void announce_high_water() { _r->output_event().high_water(); } void announce_fatal_error() { _r->output_event().fatal_error(); } private: Redistributor* _r; }; // ---------------------------------------------------------------------------- // Inline RedistTable methods #ifndef XORP_USE_USTL template inline bool RedistNetCmp::operator() (const IPNet& l, const IPNet& r) const { if (l.prefix_len() != r.prefix_len()) return l.prefix_len() < r.prefix_len(); return l.masked_addr() < r.masked_addr(); } #endif #endif // __RIB_RT_TAB_REDIST_HH__ xorp/rib/routemap.cc0000664000076400007640000000550511421137511014600 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "rib_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "routemap.hh" RouteMap::RouteMap(const string& mapname) : _mapname(mapname) { } int RouteMap::add_rule(RMRule* rule) { #if 0 if (_ruleset[rule->seq()] != NULL) { cerr << "Attempt to add duplicate rule number " << rule->seq() << " to RouteMap " << _mapname << "\n"; return XORP_ERROR; } #endif list::iterator iter; for (iter = _ruleset.begin(); iter != _ruleset.end(); ++iter) { cout << "comparing new rule " << rule->seq() << " with " << (*iter)->seq() << "\n"; if (rule->seq() < (*iter)->seq()) { cout << "here1\n"; _ruleset.insert(iter, rule); return XORP_OK; } } cout << "here2\n"; _ruleset.push_back(rule); return XORP_OK; } string RouteMap::str() const { string result; list::const_iterator iter; for (iter = _ruleset.begin(); iter != _ruleset.end(); ++iter) { result += "route-map " + _mapname + (*iter)->str() + "!\n"; } return result; } RMRule::RMRule(int seq, RMMatch* match, RMAction* action) { _seq = seq; _match = match; _action = action; } string RMRule::str() const { string result; char buf[20]; snprintf(buf, 20, "%d", _seq); result = " permit "; result += buf; result += "\n"; result += " " + _match->str() + "\n"; result += " " + _action->str() + "\n"; return result; } RMMatch::RMMatch() { } RMMatchIPAddr::RMMatchIPAddr(const IPv4Net& ipv4net) { _ipv4net = ipv4net; } string RMMatchIPAddr::str() const { string result; result = "match ip-address " + _ipv4net.str(); return result; } bool RMMatchIPAddr::match_route(const RouteEntry& re) const { cout << "comparing " << re.str() << "\n"; return true; } RMAction::RMAction() { // nothing happens here } string RMAction::str() const { string result; result = "no modification"; return result; } xorp/rib/rt_tab_origin.cc0000664000076400007640000001655411633743677015621 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "rib_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "rt_tab_origin.hh" #include "rt_tab_deletion.hh" // // A = Address Type. E.g., IPv4 or IPv6 // template OriginTable::OriginTable(const string& tablename, uint32_t admin_distance, ProtocolType protocol_type, EventLoop& eventloop) : RouteTable(tablename), _admin_distance(admin_distance), _protocol_type(protocol_type), _eventloop(eventloop), _gen(0) { XLOG_ASSERT(admin_distance <= 255); XLOG_ASSERT((protocol_type == IGP) || (protocol_type == EGP)); _ip_route_table = new Trie* >(); _gen++; } template OriginTable::~OriginTable() { // Delete all the routes in the trie delete_all_routes(); delete _ip_route_table; } template int OriginTable::add_route(const IPRouteEntry& route) { debug_msg("OT[%s]: Adding route %s\n", this->tablename().c_str(), route.str().c_str()); #if 0 // // BGP can send multiple add routes for the same entry without any // corresponding deletes. So if this route is already in the table // remove it. // if (lookup_route(route.net()) != NULL) delete_route(route.net()); #else if (lookup_route(route.net()) != NULL) return XORP_ERROR; #endif // // The actual map holds pointers, but we also do allocation and // deallocation here. The reason for this is that using the map to // hold objects themselves results in us doing too many copies on // lookup, but we also don't want the table to be referencing // something external that may go away. // IPRouteEntry* routecopy = new IPRouteEntry(route); routecopy->set_admin_distance(_admin_distance); // Now add the route to this table debug_msg("BEFORE:\n"); #ifdef DEBUG_LOGGING _ip_route_table->print(); #endif _ip_route_table->insert(route.net(), routecopy); debug_msg("AFTER:\n"); #ifdef DEBUG_LOGGING _ip_route_table->print(); #endif // Propagate to next table if (this->next_table() != NULL) { this->next_table()->add_route(*routecopy, reinterpret_cast* >(this)); } return XORP_OK; } template int OriginTable::add_route(const IPRouteEntry&, RouteTable*) { XLOG_UNREACHABLE(); return XORP_ERROR; } template int OriginTable::delete_route(const IPNet& net) { debug_msg("OT[%s]: Deleting route %s\n", this->tablename().c_str(), net.str().c_str()); #ifdef DEBUG_LOGGING _ip_route_table->print(); #endif typename Trie* >::iterator iter; iter = _ip_route_table->lookup_node(net); if (iter != _ip_route_table->end()) { const IPRouteEntry* found = iter.payload(); _ip_route_table->erase(net); // Propagate to next table if (this->next_table() != NULL) this->next_table()->delete_route(found, this); // Finally we're done, and can cleanup delete found; return XORP_OK; } XLOG_ERROR("OT: attempt to delete a route that doesn't exist: %s", net.str().c_str()); return XORP_ERROR; } template int OriginTable::delete_route(const IPRouteEntry*, RouteTable*) { XLOG_UNREACHABLE(); return XORP_ERROR; } template void OriginTable::delete_all_routes() { typename Trie* >::iterator iter; for (iter = _ip_route_table->begin(); iter != _ip_route_table->end(); ++iter) { delete iter.payload(); } _ip_route_table->delete_all_nodes(); } template void OriginTable::routing_protocol_shutdown() { // // Put existing ip_route_table to one side. The plumbing changes that // accompany the creation and plumbing of the deletion table may trigger // upstream tables to query whether trie has changed. // Trie* >* old_ip_route_table = _ip_route_table; _ip_route_table = new Trie* >(); // // Pass our entire routing table into a DeletionTable, which will // handle the background deletion task. The DeletionTable will // plumb itself in. // new DeletionTable("Delete(" + this->tablename() + ")", this, old_ip_route_table, _eventloop); } template const IPRouteEntry* OriginTable::lookup_route(const IPNet& net) const { debug_msg("------------------\nlookup_route in table %s\n", this->tablename().c_str()); debug_msg("OriginTable: Looking up route %s\n", net.str().c_str()); typename Trie* >::iterator iter; iter = _ip_route_table->lookup_node(net); return (iter == _ip_route_table->end()) ? NULL : iter.payload(); } template const IPRouteEntry* OriginTable::lookup_route(const A& addr) const { debug_msg("------------------\nlookup_route in table %s\n", this->tablename().c_str()); debug_msg("OriginTable (%u): Looking up route for addr %s\n", XORP_UINT_CAST(_admin_distance), addr.str().c_str()); typename Trie* >::iterator iter; iter = _ip_route_table->find(addr); if (iter == _ip_route_table->end()) { debug_msg("No match found\n"); } return (iter == _ip_route_table->end()) ? NULL : iter.payload(); } template RouteRange* OriginTable::lookup_route_range(const A& addr) const { const IPRouteEntry* route; typename Trie* >::iterator iter; iter = _ip_route_table->find(addr); route = (iter == _ip_route_table->end()) ? NULL : iter.payload(); A bottom_addr, top_addr; _ip_route_table->find_bounds(addr, bottom_addr, top_addr); RouteRange* rr = new RouteRange(addr, route, top_addr, bottom_addr); debug_msg("Origin Table: %s returning lower bound for %s of %s\n", this->tablename().c_str(), addr.str().c_str(), bottom_addr.str().c_str()); debug_msg("Origin Table: %s returning upper bound for %s of %s\n", this->tablename().c_str(), addr.str().c_str(), top_addr.str().c_str()); return rr; } template string OriginTable::str() const { string s; s = "-------\nOriginTable: " + this->tablename() + "\n" + ( _protocol_type == IGP ? "IGP\n" : "EGP\n" ) ; if (this->next_table() == NULL) s += "no next table\n"; else s += "next table = " + this->next_table()->tablename() + "\n"; return s; } template class OriginTable; typedef OriginTable IPv4OriginTable; template class OriginTable; typedef OriginTable IPv6OriginTable; xorp/rib/redist_xrl.hh0000664000076400007640000001472011421137511015134 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/rib/redist_xrl.hh,v 1.18 2008/10/02 21:58:11 bms Exp $ #ifndef __RIB_REDIST_XRL_HH__ #define __RIB_REDIST_XRL_HH__ #include "rt_tab_redist.hh" #include "libxorp/profile.hh" class XrlRouter; template class RedistXrlTask; /** * Route Redistributor output that sends route add and deletes to * remote redistribution target via the redist{4,6} xrl interfaces. */ template class RedistXrlOutput : public RedistOutput { public: typedef RedistXrlTask Task; typedef list TaskQueue; public: /** * Constructor. * * @param redistributor originator of route add and delete requests. * @param xrl_router router to be used to send XRLs. * @param from_protocol name of protocol routes are redistributed from. * @param xrl_target_name name of XRL entity to send XRLs to. * @param cookie cookie passed in redist interface XRLs to identify * source of updates. * @param is_xrl_transaction_output if true, the add/delete route XRLs * are grouped into transactions. */ RedistXrlOutput(Redistributor* redistributor, XrlRouter& xrl_router, Profile& profile, const string& from_protocol, const string& xrl_target_name, const IPNet& network_prefix, const string& cookie); ~RedistXrlOutput(); void add_route(const IPRouteEntry& ipr); void delete_route(const IPRouteEntry& ipr); void starting_route_dump(); void finishing_route_dump(); virtual void task_completed(Task* task); void task_failed_fatally(Task* task); const string& xrl_target_name() const; const string& cookie() const; public: static const uint32_t HI_WATER = 100; static const uint32_t LO_WATER = 5; static const uint32_t RETRY_PAUSE_MS = 10; protected: void start_next_task(); void incr_inflight(); void decr_inflight(); void enqueue_task(Task* task); void dequeue_task(Task* task); protected: XrlRouter& _xrl_router; Profile& _profile; string _from_protocol; string _target_name; IPNet _network_prefix; string _cookie; TaskQueue _taskq; uint32_t _queued; TaskQueue _flyingq; uint32_t _inflight; bool _flow_controlled; bool _callback_pending; }; /** * Route Redistributor output that sends route add and deletes to * remote redistribution target via the redist_transaction{4,6} xrl * interfaces. */ template class RedistTransactionXrlOutput : public RedistXrlOutput { public: typedef typename RedistXrlOutput::Task Task; public: RedistTransactionXrlOutput(Redistributor* redistributor, XrlRouter& xrl_router, Profile& profile, const string& from_protocol, const string& xrl_target_name, const IPNet& network_prefix, const string& cookie); void add_route(const IPRouteEntry& ipr); void delete_route(const IPRouteEntry& ipr); void starting_route_dump(); void finishing_route_dump(); void task_completed(Task* task); void set_callback_pending(bool v); uint32_t tid() const; void set_tid(uint32_t v); bool transaction_in_progress() const; void set_transaction_in_progress(bool v); bool transaction_in_error() const; void set_transaction_in_error(bool v); // The size of the transaction that is build-in-progress size_t transaction_size() const { return _transaction_size; } void reset_transaction_size() { _transaction_size = 0; } void incr_transaction_size() { _transaction_size++; } static const size_t MAX_TRANSACTION_SIZE = 100; protected: uint32_t _tid; // Send-in-progress transaction ID bool _transaction_in_progress; bool _transaction_in_error; size_t _transaction_size; // Build-in-progress transaction size }; // ---------------------------------------------------------------------------- // Globally accessible RedistXrlOutput inline methods template const string& RedistXrlOutput::xrl_target_name() const { return _target_name; } template const string& RedistXrlOutput::cookie() const { return _cookie; } // ---------------------------------------------------------------------------- // Protected RedistXrlOutput inline methods template void RedistXrlOutput::incr_inflight() { if (_inflight == HI_WATER - 1) _flow_controlled = true; _inflight++; } template void RedistXrlOutput::decr_inflight() { if (_flow_controlled && _inflight < LO_WATER) _flow_controlled = false; _inflight--; } // ---------------------------------------------------------------------------- // Inline RedistrTransactionXrlOutput methods template inline uint32_t RedistTransactionXrlOutput::tid() const { return _tid; } template inline void RedistTransactionXrlOutput::set_tid(uint32_t v) { _tid = v; } template inline void RedistTransactionXrlOutput::set_callback_pending(bool v) { this->_callback_pending = v; } template inline bool RedistTransactionXrlOutput::transaction_in_progress() const { return _transaction_in_progress; } template inline void RedistTransactionXrlOutput::set_transaction_in_progress(bool v) { _transaction_in_progress = v; } template inline bool RedistTransactionXrlOutput::transaction_in_error() const { return _transaction_in_error; } template inline void RedistTransactionXrlOutput::set_transaction_in_error(bool v) { _transaction_in_error = v; } #endif // __RIB_REDIST_XRL_HH__ xorp/rib/rt_tab_register.cc0000664000076400007640000003561511540225533016134 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #include "rib_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "rt_tab_register.hh" #include "register_server.hh" template int RouteRegister::delete_registrant(const ModuleData& module) { debug_msg("delete_registrant: this: %p, Module: %s\n", this, module.str().c_str()); map::iterator mod_iter; mod_iter = _modules.find(module.name()); if (mod_iter == _modules.end()) { return XORP_ERROR; } _modules.erase(mod_iter); debug_msg("new Value:\n%s\n", str().c_str()); return XORP_OK; } template string RouteRegister::str() const { ostringstream oss; oss << "RR***********************\nRR RouteRegister: " << _valid_subnet.str() << "\n"; if (_route != NULL) oss << "RR Route: " << _route->str() << "\n"; else oss << "RR Route: NONE \n"; map::const_iterator mod_iter; mod_iter = _modules.begin(); while (mod_iter != _modules.end()) { oss << "RR Module: " << mod_iter->second.str() << "\n"; ++mod_iter; } oss << "RR***********************\n"; return oss.str(); } template RegisterTable::RegisterTable(const string& tablename, RegisterServer& register_server, bool multicast) : RouteTable(tablename), _parent(NULL), _register_server(register_server), _multicast(multicast) { } template void RegisterTable::replumb(RouteTable* old_parent, RouteTable* new_parent) { XLOG_ASSERT(_parent == old_parent); _parent = new_parent; } template int RegisterTable::find_matches(const IPRouteEntry& route) { bool matches = false; // // Note that the _ipregistry trie contains no overlapping routes, // so if we find an exact match or a less specific match then we're // done. // debug_msg("FM: %s\n", route.net().str().c_str()); // Find any exact matches typename Trie* >::iterator iter; iter = _ipregistry.lookup_node(route.net()); if (iter != _ipregistry.end()) { debug_msg("FM: exact match\n"); iter.payload()->mark_modules(); return XORP_OK; } debug_msg("FM: no exact match\n"); // // Find the parent. // This is the case when a new more specific route appears. // iter = _ipregistry.find_less_specific(route.net()); if (iter != _ipregistry.end()) { debug_msg("FM: less specific match\n"); iter.payload()->mark_modules(); return XORP_OK; } debug_msg("FM: no less specific match\n"); // Find any children. iter = _ipregistry.search_subtree(route.net()); while (iter != _ipregistry.end()) { debug_msg("FM: found child\n"); iter.payload()->mark_modules(); matches = true; iter++; } if (matches == false) debug_msg("FM: no children found\n"); if (matches) return XORP_OK; else return XORP_ERROR; } template int RegisterTable::notify_relevant_modules(bool add, const IPRouteEntry& changed_route) { bool matches = false; IPNet changed_net = changed_route.net(); // // Note that the _ipregistry trie contains no overlapping routes, // so if we find an exact match or a less specific match then we're // done. // debug_msg("NRM: %s\n", changed_net.str().c_str()); // Find any exact matches typename Trie* >::iterator iter, nextiter; iter = _ipregistry.lookup_node(changed_net); if (iter != _ipregistry.end()) { debug_msg("NRM: exact match\n"); if (add) { notify_route_changed(iter, changed_route); } else { // Delete notify_invalidated(iter); } return XORP_OK; } debug_msg("NRM: no exact match\n"); // // Find the parent. // This is the case when a new more specific route appears. // iter = _ipregistry.find_less_specific(changed_net); if (iter != _ipregistry.end()) { debug_msg("NRM: less specific match: %s\n", iter.payload()->str().c_str()); if (add) { notify_invalidated(iter); } else { // This can't happen because registrations can't be // overlapped by more specific routes. XLOG_UNREACHABLE(); } return XORP_OK; } debug_msg("NRM: no less specific match\n"); // // Find any children. // Example: // we have routes 1.0.0.0/20, 1.0.0.0/24, and 1.0.2/24 // use registers 1.0.1.1. // this maps to 1.0.1.0/24 on route 1.0.0.0/20. // now either of the following can happen: // - route 1.0.0.0/22 arrives, which needs to update the info on // the registered route. // - route 1.0.0.0/20 is deleted. // in either case the registration should be invalidated and the // client should re-register // iter = _ipregistry.search_subtree(changed_net); while (iter != _ipregistry.end()) { debug_msg("NRM: found child\n"); // Move the iterator on, because otherwise a deletion may invalidate it nextiter = iter; nextiter++; const IPRouteEntry* ipregistry_route = iter.payload()->route(); if (add) { debug_msg("NRM: add\n"); if (changed_net.contains(iter.payload()->valid_subnet()) && ((ipregistry_route == NULL) || ipregistry_route->net().contains(changed_net))) { debug_msg("NRM: child_matches\n"); notify_invalidated(iter); matches = true; } } else { if ((ipregistry_route != NULL) && (ipregistry_route->net() == changed_net)) { notify_invalidated(iter); matches = true; } } iter = nextiter; } if (matches == false) debug_msg("NRM: no children found\n"); if (matches) return XORP_OK; else return XORP_ERROR; } template int RegisterTable::add_route(const IPRouteEntry& route, RouteTable* caller) { debug_msg("RegisterTable::add_route %s\n", route.str().c_str()); debug_msg("Before:\n"); print(); XLOG_ASSERT(caller == _parent); if (this->next_table() != NULL) this->next_table()->add_route(route, this); notify_relevant_modules(true /* it's an add */, route); debug_msg("Add route called on register table %s\n", this->tablename().c_str()); return XORP_OK; } template int RegisterTable::delete_route(const IPRouteEntry* route, RouteTable* caller) { debug_msg("[REGT]: delete_route: %p\n%s\n", route, route->str().c_str()); debug_msg("Before:\n"); print(); XLOG_ASSERT(caller == _parent); if (this->next_table() != NULL) this->next_table()->delete_route(route, this); notify_relevant_modules(false /* it's a delete */, *route); debug_msg("Delete route called on register table\n"); debug_msg("After:\n"); print(); return XORP_OK; } template RouteRegister* RegisterTable::add_registration(const IPNet& net, const IPRouteEntry* route, const string& module) { map::const_iterator mod_iter; // // Add the registered module name to the list of module names if // it's not there already. // mod_iter = _module_names.find(module); if (mod_iter == _module_names.end()) { _module_names[module] = ModuleData(module); } // Do we have an existing registry for this subnet? typename Trie* >::iterator iter, next_iter; iter = _ipregistry.lookup_node(net); RouteRegister* rr; if (iter == _ipregistry.end()) { // No existing registry for this subnet print(); if (route != NULL) { debug_msg("[REGT] Add registration for net %s " "route %s module %s\n", net.str().c_str(), route->str().c_str(), module.c_str()); } else { debug_msg("[REGT] Add registration for net %s, " "NULL route, module %s\n", net.str().c_str(), module.c_str()); } // // We might have some old registrations for more specific // subsets of this subnet. If we do, we must invalidate them // now. This might happen if the following occurs: // We have two routes: 1.0.0.0/16 and 1.0.0.0/24. // We have two addresses that register interest: 1.0.0.1 and 1.0.1.0 // This causes us to create registrations: // a) 1.0.0.0/24 on route 1.0.0.0/24 // b) 1.0.1.0/24 on route 1.0.0.0/16 // Now the route for 1.0.0.0/24 is deleted, and (a) is invalidated. // 1.0.0.1 re-registers, and this entry will be created: // c) 1.0.0.0/16 on 1.0.0.0/16. // But (b) is still registered. So we need to detect this here, // and invalidate it, so it can pick up the new entry. // iter = _ipregistry.search_subtree(net); while (iter != _ipregistry.end()) { next_iter = iter; next_iter++; notify_invalidated(iter); iter = next_iter; } rr = new RouteRegister(net, route, module); _ipregistry.insert(net, rr); print(); } else { rr = iter.payload(); rr->add_registrant(module); } debug_msg("added registration: to %p\n%s", rr, rr->str().c_str()); #ifdef DEBUG_LOGGING _ipregistry.print(); #endif debug_msg("\n"); return rr; } template int RegisterTable::delete_registration(const IPNet& net, const string& module) { map::iterator mod_iter; mod_iter = _module_names.find(module); if (mod_iter == _module_names.end()) { XLOG_ERROR("delete_registration called for unregistered module: %s", module.c_str()); return XORP_ERROR; } typename Trie* >::iterator iter; iter = _ipregistry.lookup_node(net); if (iter == _ipregistry.end()) { // If a route is deleted and a client such as BGP has // registered interest in this then it will be sent an invalidate. // Occasionally at the moment the invalidate is being sent the // client may be deleting its registration. // Thus this error will occassionally be seen in correctly // running code. XLOG_ERROR("delete_registration called for unregisted net: %s", net.str().c_str()); return XORP_ERROR; } RouteRegister* rr = iter.payload(); debug_msg("found registration %p\n", rr); if (rr->delete_registrant(module) != XORP_OK) { XLOG_ERROR("delete_registration failed: %s\n", net.str().c_str()); return XORP_ERROR; } if (rr->size() > 0) { debug_msg("retaining RouteRegister %p\n", rr); #ifdef DEBUG_LOGGING _ipregistry.print(); #endif return XORP_OK; } _ipregistry.erase(net); debug_msg("deleting RouteRegister %p\n", rr); delete rr; #ifdef DEBUG_LOGGING _ipregistry.print(); #endif return XORP_OK; } // // This is the method to be called to register a route. // template RouteRegister* RegisterTable::register_route_range(const A& addr, const string& module) { debug_msg("*****\nRRR: register_route_range: %s\n", addr.str().c_str()); RouteRange* rrange; rrange = lookup_route_range(addr); IPNet subnet = rrange->minimal_subnet(); debug_msg("RRR: minimal subnet = %s\n", subnet.str().c_str()); if (rrange->route() == NULL) debug_msg("RRR: no matching route\n"); else debug_msg("RRR: route = %s\n", rrange->route()->str().c_str()); RouteRegister* rreg; rreg = add_registration(subnet, rrange->route(), module); return rreg; } // // This is the method to be called to deregister a route. // template int RegisterTable::deregister_route_range(const IPNet& subnet, const string& module) { return delete_registration(subnet, module); } template string RegisterTable::str() const { ostringstream oss; oss << "-------\nRegisterTable: " << this->tablename() << "\n"; oss << "parent = " << _parent->tablename() << "\n"; if (this->next_table() == NULL) oss << "no next table\n"; else oss << "next table = " << this->next_table()->tablename() << "\n"; return oss.str(); } template void RegisterTable::print() { #ifdef DEBUG_LOGGING debug_msg("%s\n", str().c_str()); typename Trie* >::iterator iter; for (iter = _ipregistry.begin(); iter != _ipregistry.end(); ++iter) { debug_msg("----\n"); debug_msg("%s\n", iter.payload()->str().c_str()); } #endif } template void RegisterTable::notify_route_changed( typename Trie* >::iterator trie_iter, const IPRouteEntry& changed_route) { list module_names = trie_iter.payload()->module_names(); NextHop* nexthop = changed_route.nexthop(); bool resolves = false;; A nexthop_addr; switch (nexthop->type()) { case GENERIC_NEXTHOP: // This shouldn't be possible XLOG_UNREACHABLE(); case PEER_NEXTHOP: case ENCAPS_NEXTHOP: resolves = true; nexthop_addr = (reinterpret_cast* >(nexthop))->addr(); break; case EXTERNAL_NEXTHOP: case DISCARD_NEXTHOP: case UNREACHABLE_NEXTHOP: resolves = false; break; } if (false == resolves) { notify_invalidated(trie_iter); } else { uint32_t metric = changed_route.metric(); uint32_t admin_distance = changed_route.admin_distance(); const string& protocol_origin = changed_route.protocol().name(); list::const_iterator iter; for (iter = module_names.begin(); iter != module_names.end(); ++iter) { _register_server.send_route_changed( *iter, trie_iter.payload()->valid_subnet(), nexthop_addr, metric, admin_distance, protocol_origin, _multicast); } } } template void RegisterTable::notify_invalidated(typename Trie* >::iterator trie_iter) { list module_names = trie_iter.payload()->module_names(); IPNet valid_subnet = trie_iter.payload()->valid_subnet(); debug_msg("notify_invalidated: %s\n", valid_subnet.str().c_str()); list::const_iterator iter; for (iter = module_names.begin(); iter != module_names.end(); ++iter) { debug_msg("we will send an invalidate to %s\n", (*iter).c_str()); _register_server.send_invalidate(*iter, valid_subnet, _multicast); } delete trie_iter.payload(); _ipregistry.erase(trie_iter); } template void RegisterTable::flush() { _register_server.flush(); } template class RouteRegister; template class RegisterTable; #ifdef HAVE_IPV6 template class RouteRegister; template class RegisterTable; #endif xorp/rib/redist_policy.hh0000664000076400007640000001112511540225533015626 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __RIB_REDIST_POLICY_HH__ #define __RIB_REDIST_POLICY_HH__ /** * @short Base class for Redistribution Policy objects. * * Redistribution Policy objects are intended to be composable. * Logical Operators as well as route attibute operators are defined. */ template class RedistPolicy { public: /** * Determine whether route should be accepted for redistribution. * * @param ipr route to be examined. * * @return true if route should be accepted for redistribution, false * otherwise. */ virtual bool accept(const IPRouteEntry& ipr) const = 0; virtual ~RedistPolicy() {}; }; /** * @short Base class for Unary Redistribution Policy objects. */ template class RedistUnaryOp : public NONCOPYABLE { public: /** * Constructor. * * @param policy policy object allocated with new. */ RedistUnaryOp(const RedistPolicy* policy) : _p1(policy) {} ~RedistUnaryOp() { delete _p1; } private: // The following are not implemented RedistUnaryOp(); protected: const RedistPolicy* _p1; }; /** * @short Base class for Binary Redistribution Policy objects. */ template class RedistBinaryOp : public NONCOPYABLE, public RedistPolicy { public: /** * Constructor. * * @param one policy object allocated with new. * @param two policy object allocated with new. * * Note: destructor deletes supplied policy objects. */ RedistBinaryOp(RedistPolicy* one, RedistPolicy* two) : _p1(one), _p2(two) {} ~RedistBinaryOp() { delete _p1; delete _p2; } private: // The following are not implemented RedistBinaryOp(); protected: const RedistPolicy* _p1; const RedistPolicy* _p2; }; /** * @short Logical-Not for Redistribution Policy objects. */ template class RedistLogicalNot : public RedistUnaryOp { public: RedistLogicalNot(const RedistPolicy* p) : RedistUnaryOp(p) {} bool accept() const { return ! this->_p1->accept(); } }; /** * @short Logical-And for Redistribution Policy objects. */ template class RedistLogicalAnd : public RedistBinaryOp { public: RedistLogicalAnd(const RedistPolicy* p1, const RedistPolicy* p2) : RedistBinaryOp(p1, p2) {} bool accept(const IPRouteEntry& ipr) { return this->_p1->accept(ipr) && this->_p2->accept(ipr); } }; /** * @short Logical-And for Redistribution Policy objects. */ template class RedistLogicalOr : public RedistBinaryOp { public: RedistLogicalOr(const RedistPolicy* one, const RedistPolicy* two) : RedistBinaryOp(one, two) {} bool accept(const IPRouteEntry& ipr) { return this->_p1->accept(ipr) || this->_p2->accept(ipr); } }; /** * @short Protocol Policy class. * * Accepts route update from a specific routing protocol. */ template class IsOfProtocol : public RedistPolicy { public: IsOfProtocol(const Protocol& p) : _protocol(p) {} bool accept(const IPRouteEntry& ipr) const { return ipr.protocol() == _protocol; } private: Protocol _protocol; }; /** * @short IGP Protocol Policy. * * Accepts route updates from Interior Gateway Protocols. */ template class IsIGP : public RedistPolicy { public: IsIGP() {} bool accept(const IPRouteEntry& ipr) const { return ipr.protocol_type() == IGP; } }; /** * @short EGP Protocol Policy. * * Accepts route updates from Exterior Gateway Protocols. */ template class IsEGP : public RedistPolicy { public: IsEGP() {} bool accept(const IPRouteEntry& ipr) const { return ipr.protocol_type() == EGP; } }; #endif // __RIB_REDIST_POLICY_HH__ xorp/rib/xrl_target.cc0000664000076400007640000013164711540225533015132 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #define PROFILE_UTILS_REQUIRED #include "rib_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/status_codes.h" #include "libxipc/xrl_std_router.hh" #ifndef XORP_DISABLE_PROFILE #include "xrl/interfaces/profile_client_xif.hh" #endif #include "xrl_target.hh" #include "rt_tab_register.hh" #include "rib_manager.hh" #include "vifmanager.hh" #include "profile_vars.hh" XrlCmdError XrlRibTarget::common_0_1_get_target_name(string& name) { name = XrlRibTargetBase::get_name(); return XrlCmdError::OKAY(); } XrlCmdError XrlRibTarget::common_0_1_get_version(string& v) { v = string(version()); return XrlCmdError::OKAY(); } XrlCmdError XrlRibTarget::common_0_1_get_status( // Output values, uint32_t& status, string& reason) { status = _rib_manager->status(reason); return XrlCmdError::OKAY(); } XrlCmdError XrlRibTarget::common_0_1_shutdown() { _rib_manager->stop(); return XrlCmdError::OKAY(); } XrlCmdError XrlRibTarget::rib_0_1_enable_rib() { _rib_manager->enable(); return XrlCmdError::OKAY(); } XrlCmdError XrlRibTarget::rib_0_1_disable_rib() { _rib_manager->disable(); return XrlCmdError::OKAY(); } XrlCmdError XrlRibTarget::rib_0_1_start_rib() { if (_rib_manager->start() != XORP_OK) { return XrlCmdError::COMMAND_FAILED("Failed to start rib manager"); } return XrlCmdError::OKAY(); } XrlCmdError XrlRibTarget::rib_0_1_stop_rib() { if (_rib_manager->stop() != XORP_OK) { return XrlCmdError::COMMAND_FAILED("Failed to stop rib manager"); } return XrlCmdError::OKAY(); } XrlCmdError XrlRibTarget::rib_0_1_make_errors_fatal() { _rib_manager->make_errors_fatal(); return XrlCmdError::OKAY(); } XrlCmdError XrlRibTarget::rib_0_1_get_registered_protocols( // Input values, const bool& ipv4, const bool& ipv6, const bool& unicast, const bool& multicast, // Output values, XrlAtomList& ipv4_unicast_protocols, XrlAtomList& ipv6_unicast_protocols, XrlAtomList& ipv4_multicast_protocols, XrlAtomList& ipv6_multicast_protocols) { list names; list::iterator iter; if (ipv4) { if (unicast) { names = _urib4.registered_protocol_names(); for (iter = names.begin(); iter != names.end(); ++iter) ipv4_unicast_protocols.append(XrlAtom(*iter)); } if (multicast) { names = _mrib4.registered_protocol_names(); for (iter = names.begin(); iter != names.end(); ++iter) ipv4_multicast_protocols.append(XrlAtom(*iter)); } } #ifdef HAVE_IPV6 if (ipv6) { if (unicast) { names = _urib6.registered_protocol_names(); for (iter = names.begin(); iter != names.end(); ++iter) ipv6_unicast_protocols.append(XrlAtom(*iter)); } if (multicast) { names = _mrib6.registered_protocol_names(); for (iter = names.begin(); iter != names.end(); ++iter) ipv6_multicast_protocols.append(XrlAtom(*iter)); } } #else UNUSED(ipv6); UNUSED(ipv6_multicast_protocols); UNUSED(ipv6_unicast_protocols); #endif return XrlCmdError::OKAY(); } XrlCmdError XrlRibTarget::rib_0_1_add_igp_table4(const string& protocol, const string& target_class, const string& target_instance, const bool& unicast, const bool& multicast) { if (unicast && _urib4.add_igp_table(protocol, target_class, target_instance) != XORP_OK) { string err = c_format("Could not add unicast IPv4 igp table \"%s\"", protocol.c_str()); return XrlCmdError::COMMAND_FAILED(err); } if (multicast && _mrib4.add_igp_table(protocol, target_class, target_instance) != XORP_OK) { string err = c_format("Could not add multicast IPv4 igp table \"%s\"", protocol.c_str()); return XrlCmdError::COMMAND_FAILED(err); } return XrlCmdError::OKAY(); } XrlCmdError XrlRibTarget::rib_0_1_delete_igp_table4(const string& protocol, const string& target_class, const string& target_instance, const bool& unicast, const bool& multicast) { if (unicast && _urib4.delete_igp_table(protocol, target_class, target_instance) != XORP_OK) { string err = c_format("Could not delete unicast IPv4 igp table \"%s\"", protocol.c_str()); return XrlCmdError::COMMAND_FAILED(err); } if (multicast && _mrib4.delete_igp_table(protocol, target_class, target_instance) != XORP_OK) { string err = c_format("Could not delete multicast IPv4 igp table " "\"%s\"", protocol.c_str()); return XrlCmdError::COMMAND_FAILED(err); } return XrlCmdError::OKAY(); } XrlCmdError XrlRibTarget::rib_0_1_add_egp_table4(const string& protocol, const string& target_class, const string& target_instance, const bool& unicast, const bool& multicast) { if (unicast && _urib4.add_egp_table(protocol, target_class, target_instance) != XORP_OK) { string err = c_format("Could not add unicast IPv4 egp table \"%s\"", protocol.c_str()); return XrlCmdError::COMMAND_FAILED(err); } if (multicast && _mrib4.add_egp_table(protocol, target_class, target_instance) != XORP_OK) { string err = c_format("Could not add multicast IPv4 egp table \"%s\"", protocol.c_str()); return XrlCmdError::COMMAND_FAILED(err); } return XrlCmdError::OKAY(); } XrlCmdError XrlRibTarget::rib_0_1_delete_egp_table4(const string& protocol, const string& target_class, const string& target_instance, const bool& unicast, const bool& multicast) { if (unicast && _urib4.delete_egp_table(protocol, target_class, target_instance) != XORP_OK) { string err = c_format("Could not delete unicast IPv4 egp table \"%s\"", protocol.c_str()); return XrlCmdError::COMMAND_FAILED(err); } if (multicast && _mrib4.delete_egp_table(protocol, target_class, target_instance) != XORP_OK) { string err = c_format("Could not delete multicast IPv4 egp table " "\"%s\"", protocol.c_str()); return XrlCmdError::COMMAND_FAILED(err); } return XrlCmdError::OKAY(); } XrlCmdError XrlRibTarget::rib_0_1_add_route4(const string& protocol, const bool& unicast, const bool& multicast, const IPv4Net& network, const IPv4& nexthop, const uint32_t& metric, const XrlAtomList& policytags) { debug_msg("add_route4 protocol: %s unicast: %s multicast: %s " "network %s nexthop %s metric %u\n", protocol.c_str(), bool_c_str(unicast), bool_c_str(multicast), network.str().c_str(), nexthop.str().c_str(), XORP_UINT_CAST(metric)); #ifndef XORP_DISABLE_PROFILE if (_rib_manager->profile().enabled(profile_route_ribin)) { _rib_manager->profile().log(profile_route_ribin, c_format("add %s %s%s %s %s %u", protocol.c_str(), unicast ? "u" : "", multicast ? "m" : "", network.str().c_str(), nexthop.str().c_str(), XORP_UINT_CAST(metric))); } #endif if (unicast && _urib4.add_route(protocol, network, nexthop, "", "", metric, policytags) != XORP_OK) { string err = c_format("Could not add IPv4 route " "net %s, nexthop: %s to unicast RIB", network.str().c_str(), nexthop.str().c_str()); return XrlCmdError::COMMAND_FAILED(err); } if (multicast && _mrib4.add_route(protocol, network, nexthop, "", "", metric, policytags) != XORP_OK) { string err = c_format("Could not add IPv4 route " "net %s, nexthop: %s to multicast RIB", network.str().c_str(), nexthop.str().c_str()); return XrlCmdError::COMMAND_FAILED(err); } return XrlCmdError::OKAY(); } XrlCmdError XrlRibTarget::rib_0_1_replace_route4(const string& protocol, const bool& unicast, const bool& multicast, const IPv4Net& network, const IPv4& nexthop, const uint32_t& metric, const XrlAtomList& policytags) { debug_msg("replace_route4 protocol: %s unicast: %s multicast: %s " "network %s nexthop %s metric %u\n", protocol.c_str(), bool_c_str(unicast), bool_c_str(multicast), network.str().c_str(), nexthop.str().c_str(), XORP_UINT_CAST(metric)); #ifndef XORP_DISABLE_PROFILE if (_rib_manager->profile().enabled(profile_route_ribin)) { _rib_manager->profile().log(profile_route_ribin, c_format("replace %s %s%s %s %s %u", protocol.c_str(), unicast ? "u" : "", multicast ? "m" : "", network.str().c_str(), nexthop.str().c_str(), XORP_UINT_CAST(metric))); } #endif if (unicast && _urib4.replace_route(protocol, network, nexthop, "", "", metric, policytags) != XORP_OK) { string err = "Could not replace IPv4 route in unicast RIB"; return XrlCmdError::COMMAND_FAILED(err); } if (multicast && _mrib4.replace_route(protocol, network, nexthop, "", "", metric, policytags) != XORP_OK) { string err = "Could not replace IPv4 route in multicast RIB"; return XrlCmdError::COMMAND_FAILED(err); } return XrlCmdError::OKAY(); } XrlCmdError XrlRibTarget::rib_0_1_delete_route4(const string& protocol, const bool& unicast, const bool& multicast, const IPv4Net& network) { debug_msg("delete_route4 protocol: %s unicast: %s multicast: %s " "network %s\n", protocol.c_str(), bool_c_str(unicast), bool_c_str(multicast), network.str().c_str()); #ifndef XORP_DISABLE_PROFILE if (_rib_manager->profile().enabled(profile_route_ribin)) { _rib_manager->profile().log(profile_route_ribin, c_format("delete %s %s%s %s", protocol.c_str(), unicast ? "u" : "", multicast ? "m" : "", network.str().c_str())); } #endif if (unicast && _urib4.delete_route(protocol, network) != XORP_OK) { string err = "Could not delete IPv4 route from unicast RIB"; return XrlCmdError::COMMAND_FAILED(err); } if (multicast && _mrib4.delete_route(protocol, network) != XORP_OK) { string err = "Could not delete IPv4 route from multicast RIB"; return XrlCmdError::COMMAND_FAILED(err); } return XrlCmdError::OKAY(); } XrlCmdError XrlRibTarget::rib_0_1_add_interface_route4(const string& protocol, const bool& unicast, const bool& multicast, const IPv4Net& network, const IPv4& nexthop, const string& ifname, const string& vifname, const uint32_t& metric, const XrlAtomList& policytags) { debug_msg("add_interface_route4 protocol: %s unicast: %s multicast: %s " "network %s nexthop %s ifname %s vifname %s metric %u\n", protocol.c_str(), bool_c_str(unicast), bool_c_str(multicast), network.str().c_str(), nexthop.str().c_str(), ifname.c_str(), vifname.c_str(), XORP_UINT_CAST(metric)); #ifndef XORP_DISABLE_PROFILE if (_rib_manager->profile().enabled(profile_route_ribin)) { _rib_manager->profile().log(profile_route_ribin, c_format("add %s %s%s %s %s %s/%s %u", protocol.c_str(), unicast ? "u" : "", multicast ? "m" : "", network.str().c_str(), nexthop.str().c_str(), ifname.c_str(), vifname.c_str(), XORP_UINT_CAST(metric))); } #endif if (unicast && _urib4.add_route(protocol, network, nexthop, ifname, vifname, metric, policytags) != XORP_OK) { string err = "Could not add IPv4 interface route to unicast RIB"; return XrlCmdError::COMMAND_FAILED(err); } if (multicast && _mrib4.add_route(protocol, network, nexthop, ifname, vifname, metric, policytags) != XORP_OK) { string err = "Could not add IPv4 interface route to multicast RIB"; return XrlCmdError::COMMAND_FAILED(err); } return XrlCmdError::OKAY(); } XrlCmdError XrlRibTarget::rib_0_1_replace_interface_route4(const string& protocol, const bool& unicast, const bool& multicast, const IPv4Net& network, const IPv4& nexthop, const string& ifname, const string& vifname, const uint32_t& metric, const XrlAtomList& policytags) { debug_msg("replace_interface_route4 protocol: %s unicast: %s multicast: %s " "network %s nexthop %s ifname %s vifname %s metric %u\n", protocol.c_str(), bool_c_str(unicast), bool_c_str(multicast), network.str().c_str(), nexthop.str().c_str(), ifname.c_str(), vifname.c_str(), XORP_UINT_CAST(metric)); #ifndef XORP_DISABLE_PROFILE if (_rib_manager->profile().enabled(profile_route_ribin)) { _rib_manager->profile().log(profile_route_ribin, c_format("replace %s %s%s %s %s %s/%s %u", protocol.c_str(), unicast ? "u" : "", multicast ? "m" : "", network.str().c_str(), nexthop.str().c_str(), ifname.c_str(), vifname.c_str(), XORP_UINT_CAST(metric))); } #endif if (unicast && _urib4.replace_route(protocol, network, nexthop, ifname, vifname, metric, policytags) != XORP_OK) { string err = "Could not replace IPv4 interface route in unicast RIB"; return XrlCmdError::COMMAND_FAILED(err); } if (multicast && _mrib4.replace_route(protocol, network, nexthop, ifname, vifname, metric, policytags) != XORP_OK) { string err = "Could not replace IPv4 interface route in multicast RIB"; return XrlCmdError::COMMAND_FAILED(err); } return XrlCmdError::OKAY(); } XrlCmdError XrlRibTarget::rib_0_1_lookup_route_by_dest4( // Input values, const IPv4& addr, const bool& unicast, const bool& multicast, // Output values, IPv4& nexthop) { // if unicast and multicast then fail, can only look one place at time if (unicast == multicast) { nexthop = IPv4::ZERO(); } else if (unicast) { nexthop = _urib4.lookup_route(addr); } else if (multicast) { nexthop = _mrib4.lookup_route(addr); } return XrlCmdError::OKAY(); } XrlCmdError XrlRibTarget::rib_0_1_new_vif(const string& name) { // // One vif per RIB or one shared VifStore ? Latter as no guarantee that // all vifs have valid IPv4/IPv6/Unicast/Multicast meaning // Vif v(name); // // XXX: this is an XRL interface only for testing purpose, and // eventually this interface should go away in the future. // Hence, for simplicity so we automatically assign the vif flags // here with some values that should be appropriate for the // limited testing purpose. // v.set_p2p(false); v.set_loopback(false); v.set_multicast_capable(true); v.set_broadcast_capable(true); v.set_underlying_vif_up(true); v.set_mtu(1500); // XXX probably want something more selective (eg rib selector) if (_urib4.new_vif(name, v) != XORP_OK) { string err = c_format("Failed to add vif \"%s\" to unicast IPv4 rib", name.c_str()); return XrlCmdError::COMMAND_FAILED(err); } if (_mrib4.new_vif(name, v) != XORP_OK) { string err = c_format("Failed to add vif \"%s\" to multicast IPv4 rib", name.c_str()); return XrlCmdError::COMMAND_FAILED(err); } #ifdef HAVE_IPV6 if (_urib6.new_vif(name, v) != XORP_OK) { string err = c_format("Failed to add vif \"%s\" to unicast IPv6 rib", name.c_str()); return XrlCmdError::COMMAND_FAILED(err); } if (_mrib6.new_vif(name, v) != XORP_OK) { string err = c_format("Failed to add vif \"%s\" to multicast IPv6 rib", name.c_str()); return XrlCmdError::COMMAND_FAILED(err); } #endif return XrlCmdError::OKAY(); } XrlCmdError XrlRibTarget::rib_0_1_add_vif_addr4(const string& name, const IPv4& addr, const IPv4Net& subnet) { if (_urib4.add_vif_address(name, addr, subnet, IPv4::ZERO(), IPv4::ZERO()) != XORP_OK) { string err = "Failed to add IPv4 Vif address to unicast RIB"; return XrlCmdError::COMMAND_FAILED(err); } if (_mrib4.add_vif_address(name, addr, subnet, IPv4::ZERO(), IPv4::ZERO()) != XORP_OK) { string err = "Failed to add IPv4 Vif address to multicast RIB"; return XrlCmdError::COMMAND_FAILED(err); } return XrlCmdError::OKAY(); } XrlCmdError XrlRibTarget::rib_0_1_redist_enable4(const string& target_name, const string& from, const bool& ucast, const bool& mcast, const IPv4Net& network_prefix, const string& cookie) { if (_rib_manager->add_redist_xrl_output4(target_name, from, ucast, mcast, network_prefix, cookie, false) != XORP_OK) { string err = c_format("Failed to enable route redistribution from " "protocol \"%s\" to XRL target \"%s\"", from.c_str(), target_name.c_str()); return XrlCmdError::COMMAND_FAILED(err); } return XrlCmdError::OKAY(); } XrlCmdError XrlRibTarget::rib_0_1_redist_disable4(const string& target_name, const string& from, const bool& ucast, const bool& mcast, const string& cookie) { if (_rib_manager->delete_redist_xrl_output4(target_name, from, ucast, mcast, cookie, false) != XORP_OK) { string err = c_format("Failed to disable route redistribution from " "protocol \"%s\" to XRL target \"%s\"", from.c_str(), target_name.c_str()); return XrlCmdError::COMMAND_FAILED(err); } return XrlCmdError::OKAY(); } XrlCmdError XrlRibTarget::rib_0_1_redist_transaction_enable4(const string& target_name, const string& from, const bool& ucast, const bool& mcast, const IPv4Net& network_prefix, const string& cookie) { if (_rib_manager->add_redist_xrl_output4(target_name, from, ucast, mcast, network_prefix, cookie, true) != XORP_OK) { string err = c_format("Failed to enable transaction-based " "route redistribution from " "protocol \"%s\" to XRL target \"%s\"", from.c_str(), target_name.c_str()); return XrlCmdError::COMMAND_FAILED(err); } return XrlCmdError::OKAY(); } XrlCmdError XrlRibTarget::rib_0_1_redist_transaction_disable4(const string& target_name, const string& from, const bool& ucast, const bool& mcast, const string& cookie) { if (_rib_manager->delete_redist_xrl_output4(target_name, from, ucast, mcast, cookie, true) != XORP_OK) { string err = c_format("Failed to disable transaction-based " "route redistribution from " "protocol \"%s\" to XRL target \"%s\"", from.c_str(), target_name.c_str()); return XrlCmdError::COMMAND_FAILED(err); } return XrlCmdError::OKAY(); } XrlCmdError XrlRibTarget::rib_0_1_register_interest4(// Input values, const string& target, const IPv4& addr, // Output values, bool& resolves, IPv4& base_addr, uint32_t& prefix_len, uint32_t& real_prefix_len, IPv4& nexthop, uint32_t& metric) { debug_msg("register_interest4 target = %s addr = %s\n", target.c_str(), addr.str().c_str()); RouteRegister* rt_reg = _urib4.route_register(addr, target); if (rt_reg->route() == NULL) { base_addr = rt_reg->valid_subnet().masked_addr(); prefix_len = real_prefix_len = rt_reg->valid_subnet().prefix_len(); resolves = false; debug_msg("#### XRL -> REGISTER INTEREST UNRESOLVABLE %s\n", rt_reg->valid_subnet().str().c_str()); } else { metric = rt_reg->route()->metric(); base_addr = rt_reg->valid_subnet().masked_addr(); prefix_len = real_prefix_len = rt_reg->valid_subnet().prefix_len(); NextHop *nh = rt_reg->route()->nexthop(); switch (nh->type()) { case GENERIC_NEXTHOP: // this shouldn't be possible XLOG_UNREACHABLE(); case PEER_NEXTHOP: case ENCAPS_NEXTHOP: resolves = true; nexthop = ((IPNextHop*)nh)->addr(); real_prefix_len = rt_reg->route()->prefix_len(); break; case EXTERNAL_NEXTHOP: case DISCARD_NEXTHOP: case UNREACHABLE_NEXTHOP: resolves = false; break; } } return XrlCmdError::OKAY(); } XrlCmdError XrlRibTarget::rib_0_1_deregister_interest4(// Input values, const string& target, const IPv4& addr, const uint32_t& prefix_len) { if (_urib4.route_deregister(IPv4Net(addr, prefix_len), target) != XORP_OK) { string error_msg = c_format("Failed to deregister target %s for " "prefix %s/%u", target.c_str(), addr.str().c_str(), XORP_UINT_CAST(prefix_len)); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } XrlCmdError XrlRibTarget::rib_0_1_get_protocol_admin_distances( // Input values, const bool& ipv4, const bool& unicast, // Output values, XrlAtomList& protocols, XrlAtomList& admin_distances) { if (ipv4 && unicast) { // ipv4 unicast map& rad = _urib4.get_protocol_admin_distances(); map::iterator iter; for (iter = rad.begin(); iter != rad.end(); ++iter) { protocols.append(XrlAtom(iter->first)); admin_distances.append(XrlAtom(iter->second)); } } else if (ipv4 && !unicast) { // ipv4 multicast map& rad = _mrib4.get_protocol_admin_distances(); map::iterator iter; for (iter = rad.begin(); iter != rad.end(); ++iter) { protocols.append(XrlAtom(iter->first)); admin_distances.append(XrlAtom(iter->second)); } #ifdef HAVE_IPV6 } else if (!ipv4 && unicast) { // ipv6 unicast map& rad = _urib6.get_protocol_admin_distances(); map::iterator iter; for (iter = rad.begin(); iter != rad.end(); ++iter) { protocols.append(XrlAtom(iter->first)); admin_distances.append(XrlAtom(iter->second)); } } else if (!ipv4 && !unicast) { // ipv6 multicast map& rad = _mrib6.get_protocol_admin_distances(); map::iterator iter; for (iter = rad.begin(); iter != rad.end(); ++iter) { protocols.append(XrlAtom(iter->first)); admin_distances.append(XrlAtom(iter->second)); } #endif } return XrlCmdError::OKAY(); } XrlCmdError XrlRibTarget::rib_0_1_get_protocol_admin_distance( // Input values, const string& protocol, const bool& ipv4, const bool& unicast, // Output values, uint32_t& admin_distance) { if (ipv4 && unicast) { admin_distance = _urib4.get_protocol_admin_distance(protocol); } else if (ipv4 && !unicast) { admin_distance = _mrib4.get_protocol_admin_distance(protocol); #ifdef HAVE_IPV6 } else if (!ipv4 && unicast) { admin_distance = _urib6.get_protocol_admin_distance(protocol); } else if (!ipv4 && !unicast) { admin_distance = _mrib6.get_protocol_admin_distance(protocol); #endif } return XrlCmdError::OKAY(); } XrlCmdError XrlRibTarget::rib_0_1_set_protocol_admin_distance( // Input values, const string& protocol, const bool& ipv4, const bool& ipv6, const bool& unicast, const bool& multicast, const uint32_t& admin_distance) { // Only the RIB may set an admin distance outside of the // ranges 1 to 255, as 0 is reserved for directly-connected // routes, and anything >= 255 will never make it into the FIB. if (admin_distance <= CONNECTED_ADMIN_DISTANCE || admin_distance > UNKNOWN_ADMIN_DISTANCE) { string err = c_format("Admin distance %d out of range for %s" "%s protocol \"%s\"; must be between " "1 and 255 inclusive.", admin_distance, "unicast", "IPv4", protocol.c_str()); return XrlCmdError::BAD_ARGS(err); } if (ipv4 && unicast && _urib4.set_protocol_admin_distance(protocol, admin_distance) != XORP_OK) { string err = c_format("Could not set admin distance for %s " "%s protocol \"%s\"", "IPv4", "unicast", protocol.c_str()); return XrlCmdError::COMMAND_FAILED(err); } if (ipv4 && multicast && _mrib4.set_protocol_admin_distance(protocol, admin_distance) != XORP_OK) { string err = c_format("Could not set admin distance for %s " "%s protocol \"%s\"", "IPv4", "multicast", protocol.c_str()); return XrlCmdError::COMMAND_FAILED(err); } #ifdef HAVE_IPV6 if (ipv6 && unicast && _urib6.set_protocol_admin_distance(protocol, admin_distance) != XORP_OK) { string err = c_format("Could not set admin distance for %s " "%s protocol \"%s\"", "IPv6", "unicast", protocol.c_str()); return XrlCmdError::COMMAND_FAILED(err); } if (ipv6 && multicast && _mrib6.set_protocol_admin_distance(protocol, admin_distance) != XORP_OK) { string err = c_format("Could not set admin distance for %s " "%s protocol \"%s\"", "IPv6", "multicast", protocol.c_str()); return XrlCmdError::COMMAND_FAILED(err); } #else UNUSED(ipv6); #endif return XrlCmdError::OKAY(); } XrlCmdError XrlRibTarget::finder_event_observer_0_1_xrl_target_birth( const string& target_class, const string& target_instance) { debug_msg("Target Birth: class = %s instance = %s\n", target_class.c_str(), target_instance.c_str()); UNUSED(target_class); UNUSED(target_instance); return XrlCmdError::OKAY(); } XrlCmdError XrlRibTarget::finder_event_observer_0_1_xrl_target_death( const string& target_class, const string& target_instance) { debug_msg("Target Death: class = %s instance = %s\n", target_class.c_str(), target_instance.c_str()); _rib_manager->target_death(target_class, target_instance); return XrlCmdError::OKAY(); } XrlCmdError XrlRibTarget::policy_backend_0_1_configure(const uint32_t& filter, const string& conf) { try { _rib_manager->configure_filter(filter, conf); } catch(const PolicyException& e) { return XrlCmdError::COMMAND_FAILED("Filter configure failed: " + e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlRibTarget::policy_backend_0_1_reset(const uint32_t& filter) { try { _rib_manager->reset_filter(filter); } catch(const PolicyException& e) { return XrlCmdError::COMMAND_FAILED("Filter reset failed: " + e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlRibTarget::policy_backend_0_1_push_routes() { _rib_manager->push_routes(); return XrlCmdError::OKAY(); } XrlCmdError XrlRibTarget::rib_0_1_insert_policy_redist_tags(const string& protocol, const XrlAtomList& policytags) { // doubt these will ever be used try { _rib_manager->insert_policy_redist_tags(protocol, policytags); } catch(const PolicyException& e) { return XrlCmdError::COMMAND_FAILED("Insert policy redist tags failed: " + e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlRibTarget::rib_0_1_reset_policy_redist_tags() { // just a guard for the future. try { _rib_manager->reset_policy_redist_tags(); } catch(const PolicyException& e) { return XrlCmdError::COMMAND_FAILED("Reset policy redist tags failed: " + e.str()); } return XrlCmdError::OKAY(); } #ifndef XORP_DISABLE_PROFILE XrlCmdError XrlRibTarget::profile_0_1_enable(const string& pname) { debug_msg("enable profile variable %s\n", pname.c_str()); try { _rib_manager->profile().enable(pname); } catch(PVariableUnknown& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } catch(PVariableLocked& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlRibTarget::profile_0_1_disable(const string& pname) { debug_msg("disable profile variable %s\n", pname.c_str()); try { _rib_manager->profile().disable(pname); } catch(PVariableUnknown& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlRibTarget::profile_0_1_get_entries(const string& pname, const string& instance_name) { debug_msg("profile variable %s instance %s\n", pname.c_str(), instance_name.c_str()); // Lock and initialize. try { _rib_manager->profile().lock_log(pname); } catch(PVariableUnknown& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } catch(PVariableLocked& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } ProfileUtils::transmit_log(pname, &_rib_manager->xrl_router(), instance_name, &_rib_manager->profile()); return XrlCmdError::OKAY(); } XrlCmdError XrlRibTarget::profile_0_1_clear(const string& pname) { debug_msg("clear profile variable %s\n", pname.c_str()); try { _rib_manager->profile().clear(pname); } catch(PVariableUnknown& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } catch(PVariableLocked& e) { return XrlCmdError::COMMAND_FAILED(e.str()); } return XrlCmdError::OKAY(); } XrlCmdError XrlRibTarget::profile_0_1_list(string& info) { debug_msg("\n"); info = _rib_manager->profile().get_list(); return XrlCmdError::OKAY(); } #endif // profile /** IPv6 stuff */ #ifdef HAVE_IPV6 XrlCmdError XrlRibTarget::rib_0_1_add_igp_table6(const string& protocol, const string& target_class, const string& target_instance, const bool& unicast, const bool& multicast) { if (unicast && _urib6.add_igp_table(protocol, target_class, target_instance) != XORP_OK) { string err = c_format("Could not add unicast IPv6 igp table \"%s\"", protocol.c_str()); return XrlCmdError::COMMAND_FAILED(err); } if (multicast && _mrib6.add_igp_table(protocol, target_class, target_instance) != XORP_OK) { string err = c_format("Could not add multicast IPv6 igp table \"%s\"", protocol.c_str()); return XrlCmdError::COMMAND_FAILED(err); } return XrlCmdError::OKAY(); } XrlCmdError XrlRibTarget::rib_0_1_delete_igp_table6(const string& protocol, const string& target_class, const string& target_instance, const bool& unicast, const bool& multicast) { if (unicast && _urib6.delete_igp_table(protocol, target_class, target_instance) != XORP_OK) { string err = c_format("Could not delete unicast IPv6 igp table \"%s\"", protocol.c_str()); return XrlCmdError::COMMAND_FAILED(err); } if (multicast && _mrib6.delete_igp_table(protocol, target_class, target_instance) != XORP_OK) { string err = c_format("Could not delete multicast IPv6 igp table \"%s\"", protocol.c_str()); return XrlCmdError::COMMAND_FAILED(err); } return XrlCmdError::OKAY(); } XrlCmdError XrlRibTarget::rib_0_1_add_egp_table6(const string& protocol, const string& target_class, const string& target_instance, const bool& unicast, const bool& multicast) { if (unicast && _urib6.add_egp_table(protocol, target_class, target_instance) != XORP_OK) { string err = c_format("Could not add unicast IPv6 egp table \"%s\"", protocol.c_str()); return XrlCmdError::COMMAND_FAILED(err); } if (multicast && _mrib6.add_egp_table(protocol, target_class, target_instance) != XORP_OK) { string err = c_format("Could not add multicast IPv6 egp table \"%s\"", protocol.c_str()); return XrlCmdError::COMMAND_FAILED(err); } return XrlCmdError::OKAY(); } XrlCmdError XrlRibTarget::rib_0_1_delete_egp_table6(const string& protocol, const string& target_class, const string& target_instance, const bool& unicast, const bool& multicast) { if (unicast && _urib6.delete_egp_table(protocol, target_class, target_instance) != XORP_OK) { string err = c_format("Could not delete unicast IPv6 egp table \"%s\"", protocol.c_str()); return XrlCmdError::COMMAND_FAILED(err); } if (multicast && _mrib6.delete_egp_table(protocol, target_class, target_instance) != XORP_OK) { string err = c_format("Could not delete multicast IPv6 egp table \"%s\"", protocol.c_str()); return XrlCmdError::COMMAND_FAILED(err); } return XrlCmdError::OKAY(); } XrlCmdError XrlRibTarget::rib_0_1_add_route6(const string& protocol, const bool& unicast, const bool& multicast, const IPv6Net& network, const IPv6& nexthop, const uint32_t& metric, const XrlAtomList& policytags) { debug_msg("add_route6 protocol: %s unicast: %s multicast: %s " "network %s nexthop %s metric %u\n", protocol.c_str(), bool_c_str(unicast), bool_c_str(multicast), network.str().c_str(), nexthop.str().c_str(), XORP_UINT_CAST(metric)); #ifndef XORP_DISABLE_PROFILE if (_rib_manager->profile().enabled(profile_route_ribin)) { _rib_manager->profile().log(profile_route_ribin, c_format("add %s %s%s %s %s %u", protocol.c_str(), unicast ? "u" : "", multicast ? "m" : "", network.str().c_str(), nexthop.str().c_str(), XORP_UINT_CAST(metric))); } #endif if (unicast && _urib6.add_route(protocol, network, nexthop, "", "", metric, policytags) != XORP_OK) { string err = c_format("Could not add IPv6 route " "net %s, nexthop: %s to unicast RIB", network.str().c_str(), nexthop.str().c_str()); return XrlCmdError::COMMAND_FAILED(err); } if (multicast && _mrib6.add_route(protocol, network, nexthop, "", "", metric, policytags) != XORP_OK) { string err = c_format("Could not add IPv6 route " "net %s, nexthop: %s to multicast RIB", network.str().c_str(), nexthop.str().c_str()); return XrlCmdError::COMMAND_FAILED(err); } return XrlCmdError::OKAY(); } XrlCmdError XrlRibTarget::rib_0_1_replace_route6(const string& protocol, const bool& unicast, const bool& multicast, const IPv6Net& network, const IPv6& nexthop, const uint32_t& metric, const XrlAtomList& policytags) { debug_msg("replace_route6 protocol: %s unicast: %s multicast: %s " "network %s nexthop %s metric %u\n", protocol.c_str(), bool_c_str(unicast), bool_c_str(multicast), network.str().c_str(), nexthop.str().c_str(), XORP_UINT_CAST(metric)); #ifndef XORP_DISABLE_PROFILE if (_rib_manager->profile().enabled(profile_route_ribin)) { _rib_manager->profile().log(profile_route_ribin, c_format("replace %s %s%s %s %s %u", protocol.c_str(), unicast ? "u" : "", multicast ? "m" : "", network.str().c_str(), nexthop.str().c_str(), XORP_UINT_CAST(metric))); } #endif if (unicast && _urib6.replace_route(protocol, network, nexthop, "", "", metric, policytags) != XORP_OK) { string err = "Could not replace IPv6 route in unicast RIB"; return XrlCmdError::COMMAND_FAILED(err); } if (multicast && _mrib6.replace_route(protocol, network, nexthop, "", "", metric, policytags) != XORP_OK) { string err = "Could not add IPv6 route in multicast RIB"; return XrlCmdError::COMMAND_FAILED(err); } return XrlCmdError::OKAY(); } XrlCmdError XrlRibTarget::rib_0_1_delete_route6(const string& protocol, const bool& unicast, const bool& multicast, const IPv6Net& network) { debug_msg("delete_route6 protocol: %s unicast: %s multicast: %s " "network %s\n", protocol.c_str(), bool_c_str(unicast), bool_c_str(multicast), network.str().c_str()); #ifndef XORP_DISABLE_PROFILE if (_rib_manager->profile().enabled(profile_route_ribin)) { _rib_manager->profile().log(profile_route_ribin, c_format("delete %s %s%s %s", protocol.c_str(), unicast ? "u" : "", multicast ? "m" : "", network.str().c_str())); } #endif if (unicast && _urib6.delete_route(protocol, network) != XORP_OK) { string err = "Could not delete IPv6 route from unicast RIB"; return XrlCmdError::COMMAND_FAILED(err); } if (multicast && _mrib6.delete_route(protocol, network) != XORP_OK) { string err = "Could not delete IPv6 route from multicast RIB"; return XrlCmdError::COMMAND_FAILED(err); } return XrlCmdError::OKAY(); } XrlCmdError XrlRibTarget::rib_0_1_add_interface_route6(const string& protocol, const bool& unicast, const bool& multicast, const IPv6Net& network, const IPv6& nexthop, const string& ifname, const string& vifname, const uint32_t& metric, const XrlAtomList& policytags) { debug_msg("add_interface_route6 protocol: %s unicast: %s multicast: %s " "network %s nexthop %s ifname %s vifname %s metric %u\n", protocol.c_str(), bool_c_str(unicast), bool_c_str(multicast), network.str().c_str(), nexthop.str().c_str(), ifname.c_str(), vifname.c_str(), XORP_UINT_CAST(metric)); #ifndef XORP_DISABLE_PROFILE if (_rib_manager->profile().enabled(profile_route_ribin)) { _rib_manager->profile().log(profile_route_ribin, c_format("add %s %s%s %s %s %s/%s %u", protocol.c_str(), unicast ? "u" : "", multicast ? "m" : "", network.str().c_str(), nexthop.str().c_str(), ifname.c_str(), vifname.c_str(), XORP_UINT_CAST(metric))); } #endif if (unicast && _urib6.add_route(protocol, network, nexthop, ifname, vifname, metric, policytags) != XORP_OK) { string err = "Could not add IPv6 interface route to unicast RIB"; return XrlCmdError::COMMAND_FAILED(err); } if (multicast && _mrib6.add_route(protocol, network, nexthop, ifname, vifname, metric, policytags) != XORP_OK) { string err = "Could not add IPv6 interface route to multicast RIB"; return XrlCmdError::COMMAND_FAILED(err); } return XrlCmdError::OKAY(); } XrlCmdError XrlRibTarget::rib_0_1_replace_interface_route6(const string& protocol, const bool& unicast, const bool& multicast, const IPv6Net& network, const IPv6& nexthop, const string& ifname, const string& vifname, const uint32_t& metric, const XrlAtomList& policytags) { debug_msg("replace_interface_route6 protocol: %s unicast: %s multicast: %s " "network %s nexthop %s ifname %s vifname %s metric %u\n", protocol.c_str(), bool_c_str(unicast), bool_c_str(multicast), network.str().c_str(), nexthop.str().c_str(), ifname.c_str(), vifname.c_str(), XORP_UINT_CAST(metric)); #ifndef XORP_DISABLE_PROFILE if (_rib_manager->profile().enabled(profile_route_ribin)) { _rib_manager->profile().log(profile_route_ribin, c_format("replace %s %s%s %s %s %s/%s %u", protocol.c_str(), unicast ? "u" : "", multicast ? "m" : "", network.str().c_str(), nexthop.str().c_str(), ifname.c_str(), vifname.c_str(), XORP_UINT_CAST(metric))); } #endif if (unicast && _urib6.replace_route(protocol, network, nexthop, ifname, vifname, metric, policytags) != XORP_OK) { string err = "Could not replace IPv6 interface route in unicast RIB"; return XrlCmdError::COMMAND_FAILED(err); } if (multicast && _mrib6.replace_route(protocol, network, nexthop, ifname, vifname, metric, policytags) != XORP_OK) { string err = "Could not replace IPv6 interface route in multicast RIB"; return XrlCmdError::COMMAND_FAILED(err); } return XrlCmdError::OKAY(); } XrlCmdError XrlRibTarget::rib_0_1_lookup_route_by_dest6( // Input values, const IPv6& addr, const bool& unicast, const bool& multicast, // Output values, IPv6& nexthop) { // Must look in exactly one RIB if (unicast == multicast) { nexthop = IPv6::ZERO(); } else if (unicast) { nexthop = _urib6.lookup_route(addr); } else if (multicast) { nexthop = _mrib6.lookup_route(addr); } return XrlCmdError::OKAY(); } XrlCmdError XrlRibTarget::rib_0_1_add_vif_addr6(const string& name, const IPv6& addr, const IPv6Net& subnet) { if (_urib6.add_vif_address(name, addr, subnet, IPv6::ZERO(), IPv6::ZERO()) != XORP_OK) { string err = "Failed to add IPv6 Vif address to unicast RIB"; return XrlCmdError::COMMAND_FAILED(err); } if (_mrib6.add_vif_address(name, addr, subnet, IPv6::ZERO(), IPv6::ZERO()) != XORP_OK) { string err = "Failed to add IPv6 Vif address to multicast RIB"; return XrlCmdError::COMMAND_FAILED(err); } return XrlCmdError::OKAY(); } XrlCmdError XrlRibTarget::rib_0_1_redist_enable6(const string& target_name, const string& from, const bool& ucast, const bool& mcast, const IPv6Net& network_prefix, const string& cookie) { if (_rib_manager->add_redist_xrl_output6(target_name, from, ucast, mcast, network_prefix, cookie, false) != XORP_OK) { string err = c_format("Failed to enable route redistribution from " "protocol \"%s\" to XRL target \"%s\"", from.c_str(), target_name.c_str()); return XrlCmdError::COMMAND_FAILED(err); } return XrlCmdError::OKAY(); } XrlCmdError XrlRibTarget::rib_0_1_redist_disable6(const string& target_name, const string& from, const bool& ucast, const bool& mcast, const string& cookie) { if (_rib_manager->delete_redist_xrl_output6(target_name, from, ucast, mcast, cookie, false) != XORP_OK) { string err = c_format("Failed to disable route redistribution from " "protocol \"%s\" to XRL target \"%s\"", from.c_str(), target_name.c_str()); return XrlCmdError::COMMAND_FAILED(err); } return XrlCmdError::OKAY(); } XrlCmdError XrlRibTarget::rib_0_1_redist_transaction_enable6(const string& target_name, const string& from, const bool& ucast, const bool& mcast, const IPv6Net& network_prefix, const string& cookie) { if (_rib_manager->add_redist_xrl_output6(target_name, from, ucast, mcast, network_prefix, cookie, true) != XORP_OK) { string err = c_format("Failed to enable transaction-based " "route redistribution from " "protocol \"%s\" to XRL target \"%s\"", from.c_str(), target_name.c_str()); return XrlCmdError::COMMAND_FAILED(err); } return XrlCmdError::OKAY(); } XrlCmdError XrlRibTarget::rib_0_1_redist_transaction_disable6(const string& target_name, const string& from, const bool& ucast, const bool& mcast, const string& cookie) { if (_rib_manager->delete_redist_xrl_output6(target_name, from, ucast, mcast, cookie, true) != XORP_OK) { string err = c_format("Failed to disable transaction-based " "route redistribution from " "protocol \"%s\" to XRL target \"%s\"", from.c_str(), target_name.c_str()); return XrlCmdError::COMMAND_FAILED(err); } return XrlCmdError::OKAY(); } XrlCmdError XrlRibTarget::rib_0_1_register_interest6(// Input values, const string& target, const IPv6& addr, // Output values, bool& resolves, IPv6& base_addr, uint32_t& prefix_len, uint32_t& real_prefix_len, IPv6& nexthop, uint32_t& metric) { debug_msg("register_interest6 target = %s addr = %s\n", target.c_str(), addr.str().c_str()); RouteRegister* rt_reg = _urib6.route_register(addr, target); if (rt_reg->route() == NULL) { base_addr = rt_reg->valid_subnet().masked_addr(); prefix_len = real_prefix_len = rt_reg->valid_subnet().prefix_len(); resolves = false; debug_msg("#### XRL -> REGISTER INTEREST UNRESOLVABLE %s\n", rt_reg->valid_subnet().str().c_str()); } else { metric = rt_reg->route()->metric(); base_addr = rt_reg->valid_subnet().masked_addr(); prefix_len = real_prefix_len = rt_reg->valid_subnet().prefix_len(); NextHop *nh = rt_reg->route()->nexthop(); switch (nh->type()) { case GENERIC_NEXTHOP: // this shouldn't be possible XLOG_UNREACHABLE(); case PEER_NEXTHOP: case ENCAPS_NEXTHOP: resolves = true; nexthop = ((IPNextHop*)nh)->addr(); real_prefix_len = rt_reg->route()->prefix_len(); break; case EXTERNAL_NEXTHOP: case DISCARD_NEXTHOP: case UNREACHABLE_NEXTHOP: resolves = false; break; } } return XrlCmdError::OKAY(); } XrlCmdError XrlRibTarget::rib_0_1_deregister_interest6(// Input values, const string& target, const IPv6& addr, const uint32_t& prefix_len) { if (_urib6.route_deregister(IPv6Net(addr, prefix_len), target) != XORP_OK) { string error_msg = c_format("Failed to deregister target %s for " "prefix %s/%u", target.c_str(), addr.str().c_str(), XORP_UINT_CAST(prefix_len)); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::OKAY(); } #endif //ipv6 xorp/rib/main_rib.cc0000664000076400007640000000531511540225533014527 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "rib_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "rib_manager.hh" #ifdef HAVE_SYSEXITS_H #include #endif int main (int /* argc */, char* argv[]) { // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); setup_dflt_sighandlers(); XorpUnexpectedHandler x(xorp_unexpected_handler); try { // // Init stuff // EventLoop eventloop; XrlStdRouter xrl_std_router_rib(eventloop, "rib"); // // The RIB manager // RibManager rib_manager(eventloop, xrl_std_router_rib, "fea"); rib_manager.enable(); wait_until_xrl_router_is_ready(eventloop, xrl_std_router_rib); // Add the FEA as a RIB client rib_manager.add_redist_xrl_output4("fea", /* target_name */ "all", /* from_protocol */ true, /* unicast */ false, /* multicast */ IPv4Net(IPv4::ZERO(), 0), /* network_prefix */ "all", /* cookie */ true /* is_xrl_transaction_output */ ); #ifdef HAVE_IPV6 rib_manager.add_redist_xrl_output6("fea", /* target_name */ "all", /* from_protocol */ true, /* unicast */ false, /* multicast */ IPv6Net(IPv6::ZERO(), 0), /* network_prefix */ "all", /* cookie */ true /* is_xrl_transaction_output */ ); #endif rib_manager.start(); // // Main loop // string reason; while (xorp_do_run && (rib_manager.status(reason) != PROC_DONE)) { eventloop.run(); } } catch (...) { xorp_catch_standard_exceptions(); } // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); return 0; } xorp/rib/rt_tab_base.cc0000664000076400007640000000323611421137511015210 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "rib_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "rt_tab_base.hh" template RouteTable::~RouteTable() { } template void RouteTable::set_next_table(RouteTable* next_table) { _next_table = next_table; } template void RouteTable::replace_policytags(const IPRouteEntry& route, const PolicyTags& prevtags, RouteTable* caller) { XLOG_ASSERT(_next_table); UNUSED(caller); _next_table->replace_policytags(route, prevtags, this); } template class RouteTable; typedef RouteTable IPv4RouteTable; template class RouteTable; typedef RouteTable IPv6RouteTable; template class RouteRange; template class RouteRange; xorp/rib/SConscript0000664000076400007640000000757211631506144014462 0ustar greearbgreearb# Copyright (c) 2009-2011 XORP, Inc and Others # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # $XORP$ import os Import('env') env = env.Clone() is_shared = env.has_key('SHAREDLIBS') subdirs = [ 'tests', 'tools', ] SConscript(dirs = subdirs, exports = 'env') env.AppendUnique(CPPPATH = [ '#', '$BUILDDIR', ]) env.PrependUnique(LIBPATH = [ '$BUILDDIR/libxorp', '$BUILDDIR/libcomm', '$BUILDDIR/libxipc', '$BUILDDIR/libproto', '$BUILDDIR/libfeaclient', '$BUILDDIR/policy/backend', '$BUILDDIR/policy/common', '$BUILDDIR/xrl/interfaces', '$BUILDDIR/xrl/targets', '.' ]) env.AppendUnique(LIBS = [ 'xorp_rib', # The library, not the executable. 'xorp_fea_client', 'xif_fea_ifmgr_mirror', 'xif_fea_ifmgr_replicator', 'xif_fti', 'xif_rib_client', 'xif_finder_event_notifier', 'xif_redist4', 'xif_redist6', 'xif_redist_transaction4', 'xif_redist_transaction6', 'xif_policy_redist4', 'xif_policy_redist6', 'xst_fea_ifmgr_mirror', 'xst_rib', 'xorp_policy_backend', 'xorp_policy_common', 'xorp_finder', 'xorp_ipc', 'xorp_core', 'xorp_proto', 'xorp_comm' ]) if not is_shared: env.AppendUnique(LIBS = [ "crypto", ]) if not (env.has_key('mingw') and env['mingw']): env.AppendUnique(LIBS = [ "rt", ]) if (env.has_key('mingw') and env['mingw']): env.AppendUnique(LIBS = [ 'ws2_32', 'iphlpapi', # 'mprapi', 'regex', 'winmm', ]) env.Append(LIBS = ['xorp_core']) env.Replace(RPATH = [ env.Literal(env['xorp_module_rpath']) ]) libxorp_rib_srcs = [ 'protocol.cc', 'redist_xrl.cc', 'register_server.cc', 'rib.cc', 'rib_manager.cc', 'rib_varrw.cc', 'route.cc', 'rt_tab_base.cc', 'rt_tab_deletion.cc', 'rt_tab_extint.cc', 'rt_tab_log.cc', 'rt_tab_merged.cc', 'rt_tab_origin.cc', 'rt_tab_pol_conn.cc', 'rt_tab_pol_redist.cc', 'rt_tab_redist.cc', 'rt_tab_register.cc', 'vifmanager.cc', 'xrl_target.cc' ] if not (env.has_key('disable_profile') and env['disable_profile']): libxorp_rib_srcs.append('profile_vars.cc') env.AppendUnique(LIBS = [ 'xif_profile_client', ]) if is_shared: libxorp_rib = env.SharedLibrary(target = 'libxorp_rib', source = libxorp_rib_srcs, LIBS = '') if env['rtld_origin']: for obj in libxorp_rib: env.AddPostAction(libxorp_rib, env.Symlink(obj.abspath, os.path.join(env['xorp_alias_libdir'], str(obj)))) else: libxorp_rib = env.StaticLibrary(target = 'libxorp_rib', source = libxorp_rib_srcs, LIBS = '') ribsrcs = [ 'main_rib.cc', ] rib = env.Program(target = 'xorp_rib', source = ribsrcs) env.Alias('install', env.InstallProgram(env['xorp_moduledir'], rib)) if is_shared: env.Alias('install', env.InstallLibrary(env['xorp_libdir'], libxorp_rib)) # Install scripts env.Alias('install', env.InstallScript('$exec_prefix/sbin/', env.Entry('rib_xrl_shell_funcs.sh'))) Default(rib) xorp/rib/parser.cc0000664000076400007640000002061711421137511014241 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include #include "rib_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/c_format.hh" #include "parser.hh" // ---------------------------------------------------------------------------- // Argument Parsing methods Datum* Uint32ArgumentParser::parse(const string& str) const { try { printf(">>>str=%s\n", str.c_str()); return new Uint32Datum(str); } catch (const InvalidString&) { return NULL; } } Datum* StringArgumentParser::parse(const string& str) const { try { return new StringDatum(str); } catch (const InvalidString&) { return NULL; } } Datum* IPv4ArgumentParser::parse(const string& str) const { try { return new IPv4Datum(str); } catch (const InvalidString&) { return NULL; } } Datum* IPv4NetArgumentParser::parse(const string& str) const { try { return new IPv4NetDatum(str); } catch (const InvalidString&) { return NULL; } } // ---------------------------------------------------------------------------- // Command methods Command::~Command() { for (size_t i = 0; i < _bindings.size(); i++) { DatumVariableBinding* d = find_binding(i); delete d; _bindings[i] = NULL; } } void Command::check_syntax() { size_t nargs = count(_syntax.begin(), _syntax.end(), '~'); XLOG_ASSERT((int)nargs == _nargs); } DatumVariableBinding* Command::find_binding(int n) { map::iterator mi = _bindings.find(n); if (mi == _bindings.end()) return NULL; return mi->second; } void Command::bind(int n, DatumVariableBinding* d) { // Test if binding already exists for argument XLOG_ASSERT(find_binding(n) == NULL); // Test if non-contiguous positional binding XLOG_ASSERT((size_t)n == _bindings.size()); map::value_type b(n, d); _bindings.insert(_bindings.end(), b); } void Command::bind_uint32(int n, uint32_t& i) { bind(n, new DatumUint32Binding(i)); } void Command::bind_string(int n, string& s) { bind(n, new DatumStringBinding(s)); } void Command::bind_ipv4(int n, IPv4& ipv4) { bind(n, new DatumIPv4Binding(ipv4)); } void Command::bind_ipv4net(int n, IPv4Net& ipv4net) { bind(n, new DatumIPv4NetBinding(ipv4net)); } void Command::set_arg(int n, Datum* d) throw (Parse_error) { DatumVariableBinding* b = find_binding(n); if (b == NULL) throw Parse_error(c_format("Argument %d has no variable associated " "with it", n)); b->transfer(d); } // ---------------------------------------------------------------------------- // Parser methods Parser::Parser() : _separator(' ') { add_argtype(new Uint32ArgumentParser()); add_argtype(new StringArgumentParser()); add_argtype(new IPv4ArgumentParser()); add_argtype(new IPv4NetArgumentParser()); } Parser::~Parser() { while (_templates.size() != 0) { delete _templates.begin()->second; _templates.erase(_templates.begin()); } while (_argtypes.size() != 0) { delete _argtypes.begin()->second; _argtypes.erase(_argtypes.begin()); } } bool Parser::add_command(Command* command) { const string& s = command->syntax(); debug_msg("Parser::add_command %s\n", s.c_str()); return (_templates.insert(pair(s, command)).second); } ArgumentParser* Parser::get_argument_parser(const string& name) const { map::const_iterator mi = _argtypes.find(name); if (mi == _argtypes.end()) return NULL; return mi->second; } // ---------------------------------------------------------------------------- // Helper functions static string ltrim(const string& s) { string::const_iterator i; for (i = s.begin(); i != s.end() && xorp_isspace(*i); i++) ; return string(i, s.end()); } static string rtrim(const string& s) { string::const_iterator i, lns = s.begin(); for (i = s.begin(); i != s.end(); i++) { if (xorp_isspace(*i) == false) lns = i + 1; } return string(s.begin(), lns); } static string single_space(const string& s) { if (s.size() == 0) return s; string r; string::const_iterator i; string::const_iterator lt = s.begin(); bool inspace = (*lt == ' '); for (i = s.begin(); i != s.end(); i++) { if (*i == ' ' && inspace == false) { r += string(lt, i + 1); lt = i + 1; inspace = true; } if (*i != ' ' && inspace == true) { lt = i; inspace = false; } } if (inspace == false) { r += string(lt, s.end()); } return r; } static string blat_control(const string& s) { string tmp(s); for (size_t i = 0; i < s.size(); i++) { if (isascii(s[i]) == false || xorp_iscntrl(s[i])) tmp[i] = ' '; } return tmp; } static string prepare_line(const string& s) { return ltrim(rtrim(single_space(blat_control(s)))); } // ---------------------------------------------------------------------------- int Parser::parse(const string& s) const { debug_msg("-------------------------------------------------------\n"); debug_msg("Parser::parse: %s\n", s.c_str()); string str = prepare_line(s); if (s.empty() || str[0] == '#') return XORP_OK; typedef map::const_iterator CI; CI rpair = _templates.lower_bound(str); debug_msg("Best match: %s\n", rpair->first.c_str()); Command* cmd = rpair->second; vector words(10); int wcount = split_into_words(str, words); // Accept empty lines if (wcount == 0) return XORP_OK; vector template_words(10); int twcount = split_into_words(rpair->first, template_words); try { if (cmd == NULL) throw Parse_error("Command invalid"); if (wcount != twcount) { throw Parse_error(c_format("word count bad (got %d expected %d)", wcount, twcount)); } int args_done = 0; for (int i = 0; i < twcount; i++) { debug_msg("word %d\n", i); if (template_words[i][0] != '~') { if (template_words[i] != words[i]) { throw Parse_error(words[i]); } } else { const ArgumentParser* arg; arg = get_argument_parser(template_words[i]); if (arg == NULL) throw Parse_error(c_format("No parser for type %s", template_words[i].c_str())); Datum* d = arg->parse(words[i]); if (d == NULL) throw Parse_error(words[i]); debug_msg("%s = %s\n", template_words[i].c_str(), words[i].c_str()); cmd->set_arg(args_done, d); args_done++; delete d; } } if (args_done != cmd->num_args()) { throw Parse_error(c_format("Got %d args, expected %d\n", args_done, cmd->num_args())); } if (cmd->execute() != XORP_OK) { throw Parse_error("Command failed\n"); } } catch (Parse_error &pe) { cerr << "Parse Error: " << str << "\n"; cerr << pe.str() << "\n"; exit(1); } return XORP_OK; } int Parser::split_into_words(const string& str, vector& words) const { int word = 0; int len; string tmpstr(str); try { while (true) { // Remove leading whitespace while (tmpstr.at(0) == ' ' && tmpstr.length() > 0) { tmpstr.erase(0, 1); } if (tmpstr.length() == 0) break; len = tmpstr.find_first_of(' '); words[word] = tmpstr.substr(0, len); tmpstr.erase(0, len); word++; if (tmpstr.length() == 0) break; } } catch (out_of_range) { } return word; } bool Parser::add_argtype(ArgumentParser* arg) { debug_msg("Parser::add_argtype %s\n", arg->name().c_str()); if (arg->name()[0] != '~') { XLOG_FATAL("ArgumentParser Type names must begin with ~"); } return (_argtypes.insert(pair(arg->name(), arg)).second); } xorp/rib/rt_tab_redist.cc0000664000076400007640000003024011540224234015564 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "rib_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/eventloop.hh" #include "rt_tab_redist.hh" #include "rt_tab_origin.hh" #include "redist_policy.hh" // ---------------------------------------------------------------------------- // RedistOutput template RedistOutput::RedistOutput(Redistributor* r) : _r(r) { } template RedistOutput::~RedistOutput() { } // ---------------------------------------------------------------------------- // Redistributor template <> const IPv4Net Redistributor::NO_LAST_NET(IPv4::ALL_ONES(), IPv4::ADDR_BITLEN); template <> const IPv6Net Redistributor::NO_LAST_NET(IPv6::ALL_ONES(), IPv6::ADDR_BITLEN); template Redistributor::Redistributor(EventLoop& e, const string& n) : _e(e), _name(n), _table(0), _output(0), _policy(0), _rei(this), _oei(this), _dumping(false), _blocked(false) { } template Redistributor::~Redistributor() { delete _output; delete _policy; } template const string& Redistributor::name() const { return _name; } template void Redistributor::set_redist_table(RedistTable* rt) { if (_table) { _table->remove_redistributor(this); } _table = rt; if (_table) { _table->add_redistributor(this); start_dump(); } } template void Redistributor::set_output(RedistOutput* o) { if (_output) delete _output; _output = o; _blocked = false; if (_output) { start_dump(); } } template void Redistributor::set_policy(RedistPolicy* rpo) { if (_policy) delete _policy; _policy = rpo; } template bool Redistributor::policy_accepts(const IPRouteEntry& ipr) const { return (_policy == 0) || _policy->accept(ipr); } template void Redistributor::start_dump() { if (_output != 0 && _table != 0) { _dumping = true; _last_net = NO_LAST_NET; schedule_dump_timer(); debug_msg("starting dump\n"); _output->starting_route_dump(); } } template void Redistributor::finish_dump() { _dumping = false; _last_net = NO_LAST_NET; debug_msg("finishing dump\n"); if (_output) _output->finishing_route_dump(); } template void Redistributor::schedule_dump_timer() { XLOG_ASSERT(_blocked == false); debug_msg("schedule dump timer\n"); _dtimer = _e.new_oneoff_after_ms(0, callback(this, &Redistributor::dump_a_route) ); } template void Redistributor::unschedule_dump_timer() { debug_msg("unschedule dump timer\n"); _dtimer.unschedule(); } template void Redistributor::dump_a_route() { XLOG_ASSERT(_dumping == true); typename RedistTable::RouteIndex::const_iterator end; end = _table->route_index().end(); typename RedistTable::RouteIndex::const_iterator ci; // Find net associated with route to dump if (_last_net == NO_LAST_NET) { ci = _table->route_index().begin(); } else { ci = _table->route_index().find(_last_net); XLOG_ASSERT(ci != end); ci++; } if (ci == end) { finish_dump(); return; } // Lookup route and announce it via output const IPRouteEntry* ipr = _table->lookup_route(*ci); XLOG_ASSERT(ipr != 0); if (policy_accepts(*ipr)) _output->add_route(*ipr); // Record last net dumped and reschedule _last_net = *ci; // Check blocked as it may have been set by output's add_route() if (_blocked == false) schedule_dump_timer(); } // ---------------------------------------------------------------------------- // Event Notification handling template void Redistributor::RedistEventInterface::did_add(const IPRouteEntry& ipr) { if (_r->policy_accepts(ipr) == false) { return; } if (_r->dumping() == true) { // We're in a dump, if the route is greater than current // position we ignore it as it'll be picked up later, // otherwise we need to announce route now. if (_r->last_dumped_net() == Redistributor::NO_LAST_NET) { return; // dump not started } const IPNet& last = _r->last_dumped_net(); const IPNet& net = ipr.net(); #ifdef XORP_USE_USTL if (!(net < last)) { #else if (RedistNetCmp().operator() (net, last) == false) { #endif return; // route will be hit later on in dump anyway } } _r->output()->add_route(ipr); } template void Redistributor::RedistEventInterface::will_delete(const IPRouteEntry& ipr) { if (_r->policy_accepts(ipr) == false) { return; } // We only care that a route is about to be deleted when we are // doing the initial route dump. The pending delete may affect the // last route dumped and as a result nobble our iteration of the valid // routes set. if (_r->dumping() == false || _r->last_dumped_net() == Redistributor::NO_LAST_NET) { return; } if (ipr.net() == _r->last_dumped_net()) { // The pending delete affects last announced net. // // 1. Need to step back to previous net so next announcement will work // correctly. // typename RedistTable::RouteIndex::const_iterator ci; ci = _r->redist_table()->route_index().find(_r->last_dumped_net()); XLOG_ASSERT(ci != _r->redist_table()->route_index().end()); if (ci == _r->redist_table()->route_index().begin()) { _r->_last_net = Redistributor::NO_LAST_NET; } else { --ci; _r->_last_net = *ci; } // // 2. Announce delete. // _r->output()->delete_route(ipr); } } template void Redistributor::RedistEventInterface::did_delete(const IPRouteEntry& ipr) { if (_r->policy_accepts(ipr) == false) { return; } if (_r->dumping()) { if (_r->last_dumped_net() == Redistributor::NO_LAST_NET) { debug_msg("did_delete with no last net\n"); return; // Dump not started } const IPNet& last = _r->last_dumped_net(); const IPNet& net = ipr.net(); #ifdef XORP_USE_USTL if (!(net < last)) { #else if (RedistNetCmp().operator() (net, last) == false) { #endif return; // route has not yet been announced so ignore } } _r->output()->delete_route(ipr); } template void Redistributor::OutputEventInterface::low_water() { debug_msg("low water\n"); // Fallen below low water, take hint and resume dumping if (_r->dumping()) { _r->_blocked = false; _r->schedule_dump_timer(); } } template void Redistributor::OutputEventInterface::high_water() { // Risen above high water, take hint and stop dumping debug_msg("high water\n"); if (_r->dumping()) { _r->unschedule_dump_timer(); _r->_blocked = true; } } template void Redistributor::OutputEventInterface::fatal_error() { _r->redist_table()->remove_redistributor(_r); delete _r; } // ---------------------------------------------------------------------------- // RedistTable template RedistTable::RedistTable(const string& tablename, RouteTable* parent) : RouteTable(tablename), _parent(parent) { if (_parent->next_table()) { this->set_next_table(_parent->next_table()); this->next_table()->replumb(_parent, this); } _parent->set_next_table(this); } template RedistTable::~RedistTable() { while (_outputs.empty() == false) { delete _outputs.front(); _outputs.pop_front(); } } template void RedistTable::add_redistributor(Redistributor* r) { if (find(_outputs.begin(), _outputs.end(), r) == _outputs.end()) _outputs.push_back(r); } template void RedistTable::remove_redistributor(Redistributor* r) { typename list*>::iterator i = find(_outputs.begin(), _outputs.end(), r); if (i != _outputs.end()) { _outputs.erase(i); } } template Redistributor* RedistTable::redistributor(const string& name) { typename list*>::iterator i = _outputs.begin(); while (i != _outputs.end()) { Redistributor* r = *i; if (r->name() == name) return r; ++i; } return 0; } template int RedistTable::add_route(const IPRouteEntry& route, RouteTable* caller) { XLOG_ASSERT(caller == _parent); typename RouteIndex::iterator rci = _rt_index.find(route.net()); XLOG_ASSERT(rci == _rt_index.end()); _rt_index.insert(route.net()); typename list*>::iterator i = _outputs.begin(); while (i != _outputs.end()) { Redistributor* r = *i; i++; // XXX for safety increment iterator before prodding output r->redist_event().did_add(route); } if (this->next_table()) return this->next_table()->add_route(route, this); return XORP_OK; } template int RedistTable::delete_route(const IPRouteEntry* r, RouteTable* caller) { XLOG_ASSERT(caller == _parent); const IPRouteEntry& route = *r; debug_msg("delete_route for %s\n", route.net().str().c_str()); typename RouteIndex::iterator rci = _rt_index.find(route.net()); XLOG_ASSERT(rci != _rt_index.end()); typename list*>::iterator i; // Announce intent to delete route i = _outputs.begin(); while (i != _outputs.end()) { Redistributor* r = *i; i++; // XXX for safety increment iterator before prodding output r->redist_event().will_delete(route); } _rt_index.erase(rci); // Announce delete as fait accompli i = _outputs.begin(); while (i != _outputs.end()) { Redistributor* r = *i; i++; // XXX for safety increment iterator before prodding output r->redist_event().did_delete(route); } if (this->next_table()) return this->next_table()->delete_route(r, this); return XORP_OK; } // ---------------------------------------------------------------------------- // Standard RouteTable methods, RedistTable punts everything to parent. template const IPRouteEntry* RedistTable::lookup_route(const IPNet& net) const { return _parent->lookup_route(net); } template const IPRouteEntry* RedistTable::lookup_route(const A& addr) const { return _parent->lookup_route(addr); } template RouteRange* RedistTable::lookup_route_range(const A& addr) const { return _parent->lookup_route_range(addr); } template void RedistTable::replumb(RouteTable* old_parent, RouteTable* new_parent) { XLOG_ASSERT(old_parent == _parent); _parent = new_parent; } template string RedistTable::str() const { string s; s = "-------\nRedistTable: " + this->tablename() + "\n"; if (_outputs.empty() == false) { s += "outputs:\n"; typename list*>::const_iterator i = _outputs.begin(); while (i != _outputs.end()) { const Redistributor* r = *i; s += "\t" + r->name() + "\n"; ++i; } } if (this->next_table() == NULL) { s += "no next table\n"; } else { s += "next table = " + this->next_table()->tablename() + "\n"; } return s; } // ---------------------------------------------------------------------------- // Instantiations template class RedistOutput; template class RedistOutput; template class Redistributor; template class Redistributor; template class RedistTable; template class RedistTable; xorp/rib/rt_tab_origin.hh0000664000076400007640000001450211421137511015575 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/rib/rt_tab_origin.hh,v 1.23 2008/10/02 21:58:13 bms Exp $ #ifndef __RIB_RT_TAB_ORIGIN_HH__ #define __RIB_RT_TAB_ORIGIN_HH__ #include "libxorp/eventloop.hh" #include "rt_tab_base.hh" /** * @short RouteTable that receives and stores raw routes sent by * routing protocols. * * OriginTable is a @ref RouteTable that accepts route_add requests * from a single routing protocol, stores the route, and propagates it * downstream. It also answers lookup_route and lookup_route_range * requests from downstream using the routes it has stored. * * Its template class, A, must be either the IPv4 class of the IPv6 * class. */ template class OriginTable : public RouteTable { public: typedef Trie*> RouteContainer; public: /** * OriginTable constructor. * * @param tablename typically the name of the routing protocol that * supplies routes to this origin table, or "connected" for the * OriginTable that holds directly connected routes, or "static" * for the OriginTable that holds locally configured static * routes. * @param admin_distance the default administrative distance for * routes in this table. * @param protocol_type the routing protocol type (@ref ProtocolType). * @param eventloop the main event loop. */ OriginTable(const string& tablename, uint32_t admin_distance, ProtocolType protocol_type, EventLoop& eventloop); /** * OriginTable destructor. */ ~OriginTable(); /** * Add a route to the OriginTable. The route must not already be * in the OriginTable. * * @param route the route entry to be added. * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_route(const IPRouteEntry& route); /** * Generic @ref RouteTable method that is not used on OriginTable. * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_route(const IPRouteEntry&, RouteTable* ); /** * Delete a route from the OriginTable. * * @param net the subnet of the route entry to be deleted. * @return XORP_OK on success, otherwise XORP_ERROR. */ int delete_route(const IPNet& net); /** * Generic @ref RouteTable method that is not used on OriginTable. * @return XORP_OK on success, otherwise XORP_ERROR. */ int delete_route(const IPRouteEntry* , RouteTable* ); /** * Delete all the routes that are in this OriginTable. The * deletion is not propagated downstream, so this is only useful * when shutting down the RIB. */ void delete_all_routes(); /** * Delete all the routes that are in this OriginTable, and * propagate the deletions downstream. */ void routing_protocol_shutdown(); /** * Lookup a specific subnet to see if it is in this OriginTable. * * @param net the subnet to look up. * @return a pointer to the route entry if it exists, NULL otherwise. */ const IPRouteEntry* lookup_route(const IPNet& net) const; /** * Lookup an IP address to get the most specific (longest prefix * length) route in the OriginTable that matches this address. * * @param addr the IP address to look up. * @return a pointer to the most specific route entry if any entry * matches, NULL otherwise. */ const IPRouteEntry* lookup_route(const A& addr) const; /** * Lookup an IP addressto get the most specific (longest prefix * length) route in the OriginTable that matches this address, * along with the RouteRange information for this address and * route. * * @see RouteRange * @param addr the IP address to look up. * @return a pointer to a RouteRange class instance containing the * relevant answer. It is up to the recipient of this pointer to * free the associated memory. */ RouteRange* lookup_route_range(const A& addr) const; /** * @return the default administrative distance for this OriginTable */ uint32_t admin_distance() const { return _admin_distance; } /** * @return the routing protocol type (@ref ProtocolType). */ int protocol_type() const { return _protocol_type; } /** * @return the table type (@ref TableType). */ TableType type() const { return ORIGIN_TABLE; } /** * Generic @ref RouteTable method that is not used on OriginTable. */ void replumb(RouteTable* , RouteTable* ) {} /** * Render the OriginTable as a string for debugging purposes */ string str() const; /** * Get the number of times routing protocol has been shutdown and * restarted. */ uint32_t generation() const; /** * Get the number of routes held internally. */ uint32_t route_count() const; /** * Get the trie. */ const RouteContainer& route_container() const; private: uint32_t _admin_distance; // 0 .. 255 ProtocolType _protocol_type; // IGP or EGP EventLoop& _eventloop; RouteContainer* _ip_route_table; uint32_t _gen; }; template inline uint32_t OriginTable::generation() const { return _gen; } template inline uint32_t OriginTable::route_count() const { return _ip_route_table->route_count(); } template inline const typename OriginTable::RouteContainer& OriginTable::route_container() const { return *_ip_route_table; } #endif // __RIB_RT_TAB_ORIGIN_HH__ xorp/rib/parser_direct_cmds.hh0000664000076400007640000002062111421137511016606 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/rib/parser_direct_cmds.hh,v 1.26 2008/10/02 21:58:10 bms Exp $ #ifndef __RIB_PARSER_DIRECT_CMDS_HH__ #define __RIB_PARSER_DIRECT_CMDS_HH__ #include "parser.hh" // ---------------------------------------------------------------------------- // Direct RIB Commands (operate on an instance of a RIB). class DirectTableOriginCommand : public TableOriginCommand { public: DirectTableOriginCommand(RIB& rib) : TableOriginCommand(), _rib(rib) {} int execute() { cout << "TableOriginCommand::execute " << _tablename << "\n"; // On the context of this test code, we don't care whether it's an // IGP or an EGP, because we do the plumbing explicitly. So it's // safe to just say it's an IGP, even if its not. return _rib.new_origin_table(_tablename, "", "", _admin_distance, IGP); } private: RIB& _rib; }; class DirectRouteAddCommand : public RouteAddCommand { public: DirectRouteAddCommand(RIB& rib) : RouteAddCommand(), _rib(rib) {} int execute() { cout << "RouteAddCommand::execute " << _tablename << " "; cout << _net.str() << " " << _nexthop.str() << " " << c_format("%u", XORP_UINT_CAST(_metric)) << "\n"; return _rib.add_route(_tablename, _net, _nexthop, "", "", _metric, PolicyTags()); } private: RIB& _rib; }; class DirectRouteVifAddCommand : public RouteVifAddCommand { public: DirectRouteVifAddCommand(RIB& rib) : RouteVifAddCommand(), _rib(rib) {} int execute() { cout << "RouteVifAddCommand::execute " << _tablename << " "; cout << _net.str() << " " << _vifname << " " << _nexthop.str() << " " << c_format("%u", XORP_UINT_CAST(_metric)) << "\n"; return _rib.add_route(_tablename, _net, _nexthop, "", _vifname, _metric, PolicyTags()); } private: RIB& _rib; }; class DirectRouteDeleteCommand : public RouteDeleteCommand { public: DirectRouteDeleteCommand(RIB& rib) : RouteDeleteCommand(), _rib(rib) {} int execute() { cout << "RouteDeleteCommand::execute " << _tablename << " " << _net.str() << endl; return _rib.delete_route(_tablename, _net); } private: RIB& _rib; }; class DirectRouteVerifyCommand : public RouteVerifyCommand { public: DirectRouteVerifyCommand(RIB& rib) : RouteVerifyCommand(), _rib(rib) {} int execute() { cout << "RouteVerifyCommand::execute " << _type << " " << _lookupaddr.str() << " " << _ifname << " " << _nexthop.str() << " " << c_format("%u", XORP_UINT_CAST(_metric)) << "\n"; RibVerifyType verifytype; if (_type == "miss") verifytype = RibVerifyType(MISS); else if (_type == "discard") verifytype = RibVerifyType(DISCARD); else if (_type == "unreachable") verifytype = RibVerifyType(UNREACHABLE); else if (_type == "ip") verifytype = RibVerifyType(IP); else { cerr << "RouteVerify Failed: invalid match type specification " << _type << "\n"; #ifndef TESTING_INTERACTIVELY // XXX stub this out if interactive abort(); #endif } int dummy = _rib.verify_route(_lookupaddr, _ifname, _nexthop, _metric, verifytype); if (dummy != XORP_OK) { cerr << "RouteVerify Failed!\n"; #ifndef TESTING_INTERACTIVELY // XXX stub this out if interactive abort(); #endif } #ifndef TESTING_INTERACTIVELY // XXX stub this out if interactive return dummy; #else return XORP_OK; #endif } private: RIB& _rib; }; class DirectDiscardVifCommand : public DiscardVifCommand { public: DirectDiscardVifCommand(RIB& rib) : DiscardVifCommand(), _rib(rib) {} int execute() { cout << "DiscardVifCommand::execute " << _ifname << " "; cout << _addr.str() << "\n"; Vif vif(_ifname); IPv4Net subnet(_addr, _prefix_len); VifAddr vifaddr(_addr, subnet, IPv4::ZERO(), IPv4::ZERO()); vif.add_address(vifaddr); vif.set_discard(true); vif.set_underlying_vif_up(true); cout << "**** Vif: " << vif.str() << endl; return _rib.new_vif(_ifname, vif); } private: RIB& _rib; }; class DirectUnreachableVifCommand : public UnreachableVifCommand { public: DirectUnreachableVifCommand(RIB& rib) : UnreachableVifCommand(), _rib(rib) {} int execute() { cout << "UnreachableVifCommand::execute " << _ifname << " "; cout << _addr.str() << "\n"; Vif vif(_ifname); IPv4Net subnet(_addr, _prefix_len); VifAddr vifaddr(_addr, subnet, IPv4::ZERO(), IPv4::ZERO()); vif.add_address(vifaddr); vif.set_unreachable(true); vif.set_underlying_vif_up(true); cout << "**** Vif: " << vif.str() << endl; return _rib.new_vif(_ifname, vif); } private: RIB& _rib; }; class DirectManagementVifCommand : public ManagementVifCommand { public: DirectManagementVifCommand(RIB& rib) : ManagementVifCommand(), _rib(rib) {} int execute() { cout << "ManagementVifCommand::execute " << _ifname << " "; cout << _addr.str() << "\n"; Vif vif(_ifname); IPv4Net subnet(_addr, _prefix_len); VifAddr vifaddr(_addr, subnet, IPv4::ZERO(), IPv4::ZERO()); vif.add_address(vifaddr); vif.set_management(true); vif.set_underlying_vif_up(true); cout << "**** Vif: " << vif.str() << endl; return _rib.new_vif(_ifname, vif); } private: RIB& _rib; }; class DirectEtherVifCommand : public EtherVifCommand { public: DirectEtherVifCommand(RIB& rib) : EtherVifCommand(), _rib(rib) {} int execute() { cout << "EtherVifCommand::execute " << _ifname << " "; cout << _addr.str() << "\n"; Vif vif(_ifname); IPv4Net subnet(_addr, _prefix_len); VifAddr vifaddr(_addr, subnet, IPv4::ZERO(), IPv4::ZERO()); vif.add_address(vifaddr); vif.set_underlying_vif_up(true); cout << "**** Vif: " << vif.str() << endl; return _rib.new_vif(_ifname, vif); } private: RIB& _rib; }; class DirectLoopbackVifCommand : public LoopbackVifCommand { public: DirectLoopbackVifCommand(RIB& rib) : LoopbackVifCommand(), _rib(rib) {} int execute() { cout << "LoopbackVifCommand::execute " << _ifname << " "; cout << _addr.str() << "\n"; Vif vif(_ifname); IPv4Net subnet(_addr, _prefix_len); VifAddr vifaddr(_addr, subnet, IPv4::ZERO(), IPv4::ZERO()); vif.add_address(vifaddr); vif.set_loopback(true); vif.set_underlying_vif_up(true); cout << "**** Vif: " << vif.str() << endl; return _rib.new_vif(_ifname, vif); } private: RIB& _rib; }; class DirectAddIGPTableCommand : public AddIGPTableCommand { public: DirectAddIGPTableCommand(RIB& rib) : AddIGPTableCommand(), _rib(rib) {} int execute() { cout << "AddIGPTableCommand::execute " << _tablename << "\n"; return _rib.add_igp_table(_tablename, "", ""); } private: RIB& _rib; }; class DirectDeleteIGPTableCommand : public DeleteIGPTableCommand { public: DirectDeleteIGPTableCommand(RIB& rib) : DeleteIGPTableCommand(), _rib(rib) {} int execute() { cout << "DeleteIGPTableCommand::execute " << _tablename << "\n"; return _rib.delete_igp_table(_tablename, "", ""); } private: RIB& _rib; }; class DirectAddEGPTableCommand : public AddEGPTableCommand { public: DirectAddEGPTableCommand(RIB& rib) : AddEGPTableCommand(), _rib(rib) {} int execute() { cout << "AddEGPTableCommand::execute " << _tablename << "\n"; return _rib.add_egp_table(_tablename, "", ""); } private: RIB& _rib; }; class DirectDeleteEGPTableCommand : public DeleteEGPTableCommand { public: DirectDeleteEGPTableCommand(RIB& rib) : DeleteEGPTableCommand(), _rib(rib) {} int execute() { cout << "DeleteEGPTableCommand::execute " << _tablename << "\n"; return _rib.delete_egp_table(_tablename, "", ""); } private: RIB& _rib; }; #endif // __RIB_PARSER_DIRECT_CMDS_HH__ xorp/rib/README0000664000076400007640000000171311421137511013312 0ustar greearbgreearb# # $XORP: xorp/rib/README,v 1.2 2003/06/06 08:45:39 pavlin Exp $ # RIB: Routing Information Base. The RIB acts as an exchange area for routes associated with different routing protocols. Each routing process can add, remove, query, and register interest in routes. The RIB arbitrates over the best available route according the administrative distance associated with the routing protocol that registered a particular route. In addition, the RIB propagates routes down to the Forwarding Engine Abstraction. Configuration ============= This serves as the plumbing between all the different unicast routing protocols that may be running on a router. Startup ======= The RIB depends on the finder and should be started by the Router Manager process. Documentation ============= The RIB design architecture and code structure are described in: ${XORP}/docs/rib/ The programming documentation is in: ${XORP}/docs/kdoc/html/rib/ Status ====== Tests run. xorp/rib/protocol.cc0000664000076400007640000000222611421137511014602 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "rib_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "protocol.hh" Protocol::Protocol(const string& name, ProtocolType protocol_type, uint32_t genid) : _name(name), _protocol_type(protocol_type), _genid(genid) { } xorp/rib/vifmanager.hh0000664000076400007640000001243511421137511015075 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/rib/vifmanager.hh,v 1.24 2008/10/02 21:58:14 bms Exp $ #ifndef __RIB_VIFMANAGER_HH__ #define __RIB_VIFMANAGER_HH__ #include "libproto/proto_state.hh" #include "libfeaclient/ifmgr_xrl_mirror.hh" class EventLoop; class RibManager; class XrlRouter; /** * @short VifManager keeps track of the VIFs currently enabled in the FEA. * * The RIB process has a single VifManager instance, which registers * with the FEA process to discover the VIFs on this router and their * IP addresses and prefixes. When the VIFs or their configuration in * the FEA change, the VifManager will be notified, and it will update * the RIBs appropriately. The RIBs need to know about VIFs and VIF * addresses to decide which routes have nexthops that are on directly * connected subnets, and which are nexthops that need to be resolved * using other routes to figure out where next to send the packet. * Only routes with nexthops that are on directly connected subnets * can be sent to the FEA. */ class VifManager : public IfMgrHintObserver, public ServiceChangeObserverBase, public ProtoState { public: /** * VifManager constructor * * @param xrl_router this process's XRL router. * @param eventloop this process's EventLoop. * @param rib_manager this class contains the actual RIBs for IPv4 * and IPv6, unicast and multicast. * @param fea_target the FEA target name. */ VifManager(XrlRouter& xrl_router, EventLoop& eventloop, RibManager* rib_manager, const string& fea_target); /** * VifManager destructor */ ~VifManager(); /** * Start operation. * * Start the process of registering with the FEA, etc. * After the startup operations are completed, * @ref VifManager::final_start() is called internally * to complete the job. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int start(); /** * Stop operation. * * Gracefully stop operation. * After the shutdown operations are completed, * @ref VifManager::final_stop() is called internally * to complete the job. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int stop(); /** * Completely start the node operation. * * This method should be called internally after @ref VifManager::start() * to complete the job. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int final_start(); /** * Completely stop the node operation. * * This method should be called internally after @ref VifManager::stop() * to complete the job. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int final_stop(); protected: // // IfMgrHintObserver methods // void tree_complete(); void updates_made(); void incr_startup_requests_n(); void decr_startup_requests_n(); void incr_shutdown_requests_n(); void decr_shutdown_requests_n(); void update_status(); private: /** * A method invoked when the status of a service changes. * * @param service the service whose status has changed. * @param old_status the old status. * @param new_status the new status. */ void status_change(ServiceBase* service, ServiceStatus old_status, ServiceStatus new_status); /** * Get a reference to the service base of the interface manager. * * @return a reference to the service base of the interface manager. */ const ServiceBase* ifmgr_mirror_service_base() const { return dynamic_cast(&_ifmgr); } /** * Get a reference to the interface manager tree. * * @return a reference to the interface manager tree. */ const IfMgrIfTree& ifmgr_iftree() const { return _ifmgr.iftree(); } /** * Initiate startup of the interface manager. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int ifmgr_startup(); /** * Initiate shutdown of the interface manager. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int ifmgr_shutdown(); XrlRouter& _xrl_router; EventLoop& _eventloop; RibManager* _rib_manager; IfMgrXrlMirror _ifmgr; IfMgrIfTree _iftree; IfMgrIfTree _old_iftree; // // Status-related state // size_t _startup_requests_n; size_t _shutdown_requests_n; }; #endif // __RIB_VIFMANAGER_HH__ xorp/rib/rt_tab_pol_redist.cc0000664000076400007640000001677011540225533016455 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #include "rib_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "rt_tab_pol_redist.hh" template const string PolicyRedistTable::table_name = "policy-redist-table"; template PolicyRedistTable::PolicyRedistTable(RouteTable* parent, XrlRouter& rtr, PolicyRedistMap& rmap, bool multicast) : RouteTable(table_name), _parent(parent), _xrl_router(rtr), _eventloop(_xrl_router.eventloop()), _redist_map(rmap), _redist4_client(&_xrl_router), _redist6_client(&_xrl_router), _multicast(multicast) { if (_parent->next_table() != NULL) { this->set_next_table(_parent->next_table()); this->next_table()->replumb(_parent, this); } _parent->set_next_table(this); } template int PolicyRedistTable::add_route(const IPRouteEntry& route, RouteTable* caller) { XLOG_ASSERT(caller == _parent); debug_msg("[RIB] PolicyRedistTable ADD ROUTE: %s\n", route.str().c_str()); // get protocols involved in redistribution with these tags set protos; _redist_map.get_protocols(protos, route.policytags()); // if there are any, then redistribute if (!protos.empty()) add_redist(route, protos); RouteTable* next = this->next_table(); XLOG_ASSERT(next != NULL); return next->add_route(route, this); } template int PolicyRedistTable::delete_route(const IPRouteEntry* route, RouteTable* caller) { XLOG_ASSERT(caller == _parent); XLOG_ASSERT(route != NULL); debug_msg("[RIB] PolicyRedistTable DELETE ROUTE: %s\n", route->str().c_str()); // get protocols involved in redistribution of this route set protos; _redist_map.get_protocols(protos, route->policytags()); // if there are any, stop redistributing if (!protos.empty()) del_redist(*route, protos); RouteTable* next = this->next_table(); XLOG_ASSERT(next != NULL); return next->delete_route(route, this); } template const IPRouteEntry* PolicyRedistTable::lookup_route(const IPNet& net) const { XLOG_ASSERT(_parent != NULL); return _parent->lookup_route(net); } template const IPRouteEntry* PolicyRedistTable::lookup_route(const A& addr) const { XLOG_ASSERT(_parent != NULL); return _parent->lookup_route(addr); } template RouteRange* PolicyRedistTable::lookup_route_range(const A& addr) const { XLOG_ASSERT(_parent != NULL); return _parent->lookup_route_range(addr); } template void PolicyRedistTable::replumb(RouteTable* old_parent, RouteTable* new_parent) { XLOG_ASSERT(old_parent == _parent); _parent = new_parent; } template string PolicyRedistTable::str() const { return "not implement yet"; } template void PolicyRedistTable::add_redist(const IPRouteEntry& route, const Set& protos) { // send a redistribution request for all protocols in the set. for (Set::const_iterator i = protos.begin(); i != protos.end(); ++i) add_redist(route, *i); } template void PolicyRedistTable::del_redist(const IPRouteEntry& route, const Set& protos) { // stop redistribution for all protocols in set. for (Set::const_iterator i = protos.begin(); i != protos.end(); ++i) del_redist(route, *i); } template void PolicyRedistTable::xrl_cb(const XrlError& e, string action) { UNUSED(action); if (e != XrlError::OKAY()) { XLOG_WARNING("Unable to complete XRL: %s", action.c_str()); } } template <> void PolicyRedistTable::add_redist(const IPRouteEntry& route, const string& proto) { string error = "add_route4 for " + proto + " route: " + route.str(); debug_msg("[RIB] PolicyRedistTable add_redist IPv4 %s to %s\n", route.str().c_str(), proto.c_str()); _redist4_client.send_add_route4(proto.c_str(), route.net(), !_multicast, _multicast, // XXX route.nexthop_addr(), route.metric(), route.policytags().xrl_atomlist(), callback(this, &PolicyRedistTable::xrl_cb, error)); } template <> void PolicyRedistTable::del_redist(const IPRouteEntry& route, const string& proto) { string error = "del_route4 for " + proto + " route: " + route.str(); debug_msg("[RIB] PolicyRedistTable del_redist IPv4 %s to %s\n", route.str().c_str(), proto.c_str()); _redist4_client.send_delete_route4(proto.c_str(), route.net(), !_multicast, _multicast, // XXX callback(this, &PolicyRedistTable::xrl_cb, error)); } template void PolicyRedistTable::replace_policytags(const IPRouteEntry& route, const PolicyTags& prevtags, RouteTable* caller) { XLOG_ASSERT(caller == _parent); set del_protos; set add_protos; // who doesn't have to redistribute this route anymore ? _redist_map.get_protocols(del_protos, prevtags); // who has to redistribute this route ? _redist_map.get_protocols(add_protos, route.policytags()); // ok since it is "tags only change and nothing else" // we can be smart i.e. weed out the intersection of del/add // but not implement yet. // commit changes if (!del_protos.empty()) del_redist(route, del_protos); if (!add_protos.empty()) add_redist(route, add_protos); } template class PolicyRedistTable; template <> void PolicyRedistTable::add_redist(const IPRouteEntry& route, const string& proto) { string error = "add_route6 for " + proto + " route: " + route.str(); debug_msg("[RIB] PolicyRedistTable add_redist IPv6 %s to %s\n", route.str().c_str(), proto.c_str()); _redist6_client.send_add_route6(proto.c_str(), route.net(), !_multicast, _multicast, // XXX: mutex ? route.nexthop_addr(), route.metric(), route.policytags().xrl_atomlist(), callback(this, &PolicyRedistTable::xrl_cb, error)); } template <> void PolicyRedistTable::del_redist(const IPRouteEntry& route, const string& proto) { string error = "del_route6 for " + proto + " route: " + route.str(); debug_msg("[RIB] PolicyRedistTable del_redist IPv6 %s to %s\n", route.str().c_str(), proto.c_str()); _redist6_client.send_delete_route6(proto.c_str(), route.net(), !_multicast, _multicast, // XXX: mutex ? callback(this, &PolicyRedistTable::xrl_cb, error)); } template class PolicyRedistTable; xorp/rib/tests/0000775000076400007640000000000011540224234013573 5ustar greearbgreearbxorp/rib/tests/rt_tab_expect.cc0000664000076400007640000001247511421137511016735 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "rib_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "rt_tab_expect.hh" template ExpectedRouteChange::ExpectedRouteChange(bool add, const IPRouteEntry& route) : _add(add), _route(route) { } template bool ExpectedRouteChange::matches_add(const IPRouteEntry& route) const { if (!_add) return false; if (route.net() != _route.net()) return false; IPNextHop* expected_nh = dynamic_cast* >(_route.nexthop()); XLOG_ASSERT(expected_nh != NULL); IPNextHop* actual_nh = dynamic_cast* >(route.nexthop()); XLOG_ASSERT(actual_nh != NULL); if ((expected_nh->addr()) != (actual_nh->addr())) return false; return true; } template bool ExpectedRouteChange::matches_delete(const IPRouteEntry* route) const { if (_add) return false; if (route->net() != _route.net()) return false; IPNextHop* expected_nh = dynamic_cast* >(_route.nexthop()); XLOG_ASSERT(expected_nh != NULL); IPNextHop* actual_nh = dynamic_cast* >(route->nexthop()); XLOG_ASSERT(actual_nh != NULL); if ((expected_nh->addr()) != (actual_nh->addr())) return false; return true; } template string ExpectedRouteChange::str() const { string s; if (_add) s = "Add of "; else s = "Delete of "; s += _route.str(); return s; } /*--------------------------------------------------------------------*/ template ExpectTable::ExpectTable(const string& tablename, RouteTable* parent) : RouteTable(tablename) { _parent = parent; // Plumb ourselves into the table graph _parent->set_next_table(this); // There's no downstream table this->set_next_table(NULL); } template ExpectTable::~ExpectTable() { XLOG_ASSERT(_expected_route_changes.empty()); } template void ExpectTable::expect_add(const IPRouteEntry& route) { _expected_route_changes.push_back(ExpectedRouteChange(true, route)); } template void ExpectTable::expect_delete(const IPRouteEntry& route) { _expected_route_changes.push_back(ExpectedRouteChange(false, route)); } template int ExpectTable::add_route(const IPRouteEntry& route, RouteTable* caller) { XLOG_ASSERT(caller == _parent); debug_msg("DT[%s]: Adding route %s\n", this->tablename().c_str(), route.str().c_str()); if (_expected_route_changes.empty()) { XLOG_FATAL("ExpectTable: unexpected add_route received"); } if (_expected_route_changes.front().matches_add(route)) { _expected_route_changes.pop_front(); return XORP_OK; } XLOG_FATAL("ExpectTable: unexpected add_route received. " "Expected: %s; Received: Add of %s", _expected_route_changes.front().str().c_str(), route.str().c_str()); return XORP_ERROR; } template int ExpectTable::delete_route(const IPRouteEntry* route, RouteTable* caller) { XLOG_ASSERT(caller == _parent); debug_msg("DT[%s]: Deleting route %s\n", this->tablename().c_str(), route->str().c_str()); if (_expected_route_changes.empty()) { XLOG_FATAL("ExpectTable: unexpected delete_route received"); } if (_expected_route_changes.front().matches_delete(route)) { _expected_route_changes.pop_front(); return XORP_OK; } XLOG_FATAL("ExpectTable: unexpected delete_route received. " "Expected: %s; Received: Delete of %s", _expected_route_changes.front().str().c_str(), route->str().c_str()); return XORP_ERROR; } template const IPRouteEntry* ExpectTable::lookup_route(const IPNet& net) const { return _parent->lookup_route(net); } template const IPRouteEntry* ExpectTable::lookup_route(const A& addr) const { return _parent->lookup_route(addr); } template void ExpectTable::replumb(RouteTable* old_parent, RouteTable* new_parent) { XLOG_ASSERT(_parent == old_parent); _parent = new_parent; } template RouteRange* ExpectTable::lookup_route_range(const A& addr) const { return _parent->lookup_route_range(addr); } template string ExpectTable::str() const { string s; s = "-------\nExpectTable: " + this->tablename() + "\n"; s += "parent = " + _parent->tablename() + "\n"; return s; } template class ExpectTable; template class ExpectTable; xorp/rib/tests/test_deletion.cc0000664000076400007640000000760711421137511016755 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "rib_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/eventloop.hh" #include "rib.hh" #include "rt_tab_origin.hh" #include "rt_tab_expect.hh" int main(int /* argc */, char* argv[]) { // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); EventLoop eventloop; OriginTable ot("origin", 1, IGP, eventloop); ExpectTable dt("expect", &ot); Vif tmp_vif1("vif1"); Vif tmp_vif2("vif2"); RibVif vif1((RIB*)NULL, tmp_vif1); RibVif vif2((RIB*)NULL, tmp_vif2); IPPeerNextHop nh1(IPv4("1.0.0.1")); IPPeerNextHop nh2(IPv4("1.0.0.2")); Protocol protocol("test", IGP, 0); IPv4Net net1("10.0.1.0/24"); IPv4Net net2("10.0.2.0/24"); IPRouteEntry route1(net1, &vif1, &nh1, protocol, 100); IPRouteEntry route2(net2, &vif1, &nh1, protocol, 100); dt.expect_add(route1); dt.expect_add(route2); ot.add_route(route1); ot.add_route(route2); dt.expect_delete(route1); dt.expect_delete(route2); ot.delete_route(net1); ot.delete_route(net2); printf("-------------------------------------------------------\n"); // Validate that deletion table does remove the routes and delete itself dt.expect_add(route1); dt.expect_add(route2); ot.add_route(route1); ot.add_route(route2); dt.expect_delete(route1); dt.expect_delete(route2); XLOG_ASSERT(dt.parent()->type() == ORIGIN_TABLE); ot.routing_protocol_shutdown(); // Validate that a deletion table got added XLOG_ASSERT(dt.parent()->type() == DELETION_TABLE); while (dt.parent()->type() != ORIGIN_TABLE) { XLOG_ASSERT(dt.parent()->type() == DELETION_TABLE); eventloop.run(); } XLOG_ASSERT(dt.expected_route_changes().empty()); printf("-------------------------------------------------------\n"); // // Validate that a routing protocol that comes back up and starts // sending routes doesn't cause a problem. // dt.expect_add(route1); dt.expect_add(route2); ot.add_route(route1); ot.add_route(route2); XLOG_ASSERT(dt.parent()->type() == ORIGIN_TABLE); ot.routing_protocol_shutdown(); XLOG_ASSERT(dt.parent()->type() == DELETION_TABLE); IPRouteEntry route3(net1, &vif2, &nh2, protocol, 101); dt.expect_delete(route1); dt.expect_add(route3); ot.add_route(route3); dt.expect_delete(route2); XLOG_ASSERT(dt.parent()->type() == DELETION_TABLE); while (dt.parent()->type() != ORIGIN_TABLE) { XLOG_ASSERT(dt.parent()->type() == DELETION_TABLE); eventloop.run(); } XLOG_ASSERT(dt.expected_route_changes().empty()); XLOG_ASSERT(dt.parent()->type() == ORIGIN_TABLE); dt.expect_delete(route3); ot.delete_route(net1); return 0; } xorp/rib/tests/test_rib_xrls.cc0000664000076400007640000001011311421137511016760 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "rib_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/eventloop.hh" #include "libxipc/finder_server.hh" #include "libxipc/xrl_std_router.hh" #include "rib_manager.hh" #include "parser.hh" #include "parser_direct_cmds.hh" #include "parser_xrl_cmds.hh" #include "dummy_register_server.hh" #include "xrl_target.hh" bool verbose = false; class XrlRibParser : public Parser { public: XrlRibParser(EventLoop& e, XrlRibV0p1Client& xrl_client, RIB& rib, XrlCompletion& cv) { add_command(new XrlRouteAddCommand(e, xrl_client, cv)); add_command(new XrlRouteDeleteCommand(e, xrl_client, cv)); add_command(new XrlAddIGPTableCommand(e, xrl_client, cv)); add_command(new XrlDeleteIGPTableCommand(e, xrl_client, cv)); add_command(new XrlAddEGPTableCommand(e, xrl_client, cv)); add_command(new XrlDeleteEGPTableCommand(e, xrl_client, cv)); // The following do not exist in XRL interface so use direct methods add_command(new DirectRouteVifAddCommand(rib)); add_command(new DirectRouteVerifyCommand(rib)); add_command(new DirectTableOriginCommand(rib)); // XXX The following should probably use XRL's but punting for // time being. add_command(new DirectDiscardVifCommand(rib)); add_command(new DirectUnreachableVifCommand(rib)); add_command(new DirectManagementVifCommand(rib)); add_command(new DirectEtherVifCommand(rib)); add_command(new DirectLoopbackVifCommand(rib)); } }; static void parser_main() { EventLoop eventloop; // Finder Server FinderServer fs(eventloop, FinderConstants::FINDER_DEFAULT_HOST(), FinderConstants::FINDER_DEFAULT_PORT()); // Rib Server component XrlStdRouter xrl_std_router_rib(eventloop, "rib", fs.addr(), fs.port()); // // The RIB manager // RibManager rib_manager(eventloop, xrl_std_router_rib, "fea"); rib_manager.enable(); wait_until_xrl_router_is_ready(eventloop, xrl_std_router_rib); rib_manager.start(); XrlRibV0p1Client xrl_client(&xrl_std_router_rib); // Variable used to signal completion of Xrl parse completion XrlCompletion cv; XrlRibParser parser(eventloop, xrl_client, rib_manager.urib4(), cv); string cmd; int line = 0; while (feof(stdin) == 0) { getline(cin, cmd); if (feof(stdin) != 0) break; line++; printf("%d: %s\n", line, cmd.c_str()); cv = SUCCESS; parser.parse(cmd); // Xrl based commands set cv to XRL_PENDING // and return immediately. Ugly, but hack // to make compatible test interface with // exist synchronous code. while (cv == XRL_PENDING) eventloop.run(); XLOG_ASSERT(cv == SUCCESS); } } int main(int /* argc */, char* argv[]) { // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); XorpUnexpectedHandler x(xorp_unexpected_handler); try { parser_main(); } catch (...) { xorp_catch_standard_exceptions(); } // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); exit (0); } xorp/rib/tests/test_register_xrls.cc0000664000076400007640000002600411421137511020036 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "rib_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/eventloop.hh" #include "libxipc/finder_server.hh" #include "libxipc/xrl_std_router.hh" #include "xrl/targets/ribclient_base.hh" #include "parser.hh" #include "parser_direct_cmds.hh" #include "parser_xrl_cmds.hh" #include "register_server.hh" #include "rib_manager.hh" #include "xrl_target.hh" bool callback_flag; class RibClientTarget : public XrlRibclientTargetBase { public: RibClientTarget(XrlRouter* r) : XrlRibclientTargetBase(r) {} XrlCmdError rib_client_0_1_route_info_changed4( // Input values, const IPv4& addr, const uint32_t& prefix_len, const IPv4& nexthop, const uint32_t& metric, const uint32_t& admin_distance, const string& protocol_origin) { IPv4Net net(addr, prefix_len); printf("route_info_changed4: net:%s, new nexthop: %s, new metric: %u " "new admin_distance: %u new protocol_origin: %s\n", net.str().c_str(), nexthop.str().c_str(), XORP_UINT_CAST(metric), XORP_UINT_CAST(admin_distance), protocol_origin.c_str()); string s; s = net.str() + " " + nexthop.str(); s += " " + c_format("%u", XORP_UINT_CAST(metric)); s += " " + c_format("%u", XORP_UINT_CAST(admin_distance)); s += " " + c_format("%s", protocol_origin.c_str()); _changed.insert(s); callback_flag = true; return XrlCmdError::OKAY(); } XrlCmdError rib_client_0_1_route_info_changed6( // Input values, const IPv6& /* addr */, const uint32_t& /* prefix_len */, const IPv6& /* nexthop */, const uint32_t& /* metric */, const uint32_t& /* admin_distance */, const string& /* protocol_origin */) { return XrlCmdError::OKAY(); } XrlCmdError rib_client_0_1_route_info_invalid4( // Input values, const IPv4& addr, const uint32_t& prefix_len) { IPv4Net net(addr, prefix_len); printf("route_info_invalid4: net:%s\n", net.str().c_str()); string s; s = net.str(); _invalidated.insert(s); callback_flag = true; return XrlCmdError::OKAY(); } XrlCmdError rib_client_0_1_route_info_invalid6( // Input values, const IPv6& /* addr */, const uint32_t& /* prefix_len */) { return XrlCmdError::OKAY(); } bool verify_invalidated(const string& invalid); bool verify_changed(const string& changed); bool verify_no_info(); private: set _invalidated; set _changed; }; bool RibClientTarget::verify_invalidated(const string& invalid) { set::iterator iter; iter = _invalidated.find(invalid); if (iter == _invalidated.end()) { printf("EXPECTED: >%s<\n", invalid.c_str()); for (iter = _invalidated.begin(); iter != _invalidated.end(); ++iter) printf("INVALIDATED: >%s<\n", iter->c_str()); return false; } _invalidated.erase(iter); return true; } bool RibClientTarget::verify_changed(const string& changed) { set::iterator iter; iter = _changed.find(changed); if (iter == _changed.end()) { printf("EXPECTED: >%s<\n", changed.c_str()); for (iter = _invalidated.begin(); iter != _invalidated.end(); ++iter) printf("CHANGED: >%s<\n", iter->c_str()); return false; } _changed.erase(iter); return true; } bool RibClientTarget::verify_no_info() { if (_changed.empty() && _invalidated.empty()) return true; return false; } bool xrl_done_flag = false; void xrl_done(const XrlError& e) { if (e == XrlCmdError::OKAY()) { xrl_done_flag = true; return; } abort(); } int add_igp_table(XrlRibV0p1Client& client, EventLoop& loop, const string& tablename) { XorpCallback1::RefPtr cb; cb = callback(xrl_done); client.send_add_igp_table4("rib", tablename, "", "", true, false, cb); xrl_done_flag = false; while (xrl_done_flag == false) { loop.run(); } return XORP_OK; } int add_egp_table(XrlRibV0p1Client& client, EventLoop& loop, const string& tablename) { XorpCallback1::RefPtr cb; cb = callback(xrl_done); client.send_add_egp_table4("rib", tablename, "", "", true, false, cb); xrl_done_flag = false; while (xrl_done_flag == false) { loop.run(); } return XORP_OK; } int add_vif(XrlRibV0p1Client& client, EventLoop& loop, const string& vifname, IPv4 myaddr, IPv4Net net) { XorpCallback1::RefPtr cb; cb = callback(xrl_done); client.send_new_vif("rib", vifname, cb); xrl_done_flag = false; while (xrl_done_flag == false) { loop.run(); } cb = callback(xrl_done); client.send_add_vif_addr4("rib", vifname, myaddr, net, cb); xrl_done_flag = false; while (xrl_done_flag == false) { loop.run(); } return XORP_OK; } int add_route(XrlRibV0p1Client& client, EventLoop& loop, const string& protocol, IPv4Net net, IPv4 nexthop, uint32_t metric) { XorpCallback1::RefPtr cb; cb = callback(xrl_done); client.send_add_route4("rib", protocol, true, false, net, nexthop, metric, XrlAtomList(), cb); xrl_done_flag = false; while (xrl_done_flag == false) { loop.run(); } return XORP_OK; } int delete_route(XrlRibV0p1Client& client, EventLoop& loop, const string& protocol, IPv4Net net) { XorpCallback1::RefPtr cb; cb = callback(xrl_done); client.send_delete_route4("rib", protocol, true, false, net, cb); xrl_done_flag = false; while (xrl_done_flag == false) { loop.run(); } return XORP_OK; } void register_done(const XrlError& e, const bool* resolves, const IPv4* base_addr, const uint32_t* prefix_len, const uint32_t* /* real_prefix_len */, const IPv4* nexthop, const uint32_t* metric, bool expected_resolves, IPv4Net expected_net, IPv4 expected_nexthop, uint32_t expected_metric) { XLOG_ASSERT(e == XrlCmdError::OKAY()); if (expected_resolves) { XLOG_ASSERT(*resolves == true); IPv4Net net(*base_addr, *prefix_len); XLOG_ASSERT(net == expected_net); if (*metric != expected_metric) { fprintf(stderr, "Expected metric %u, got %u\n", XORP_UINT_CAST(expected_metric), XORP_UINT_CAST(*metric)); abort(); } XLOG_ASSERT(*nexthop == expected_nexthop); } else { XLOG_ASSERT(*resolves == false); } xrl_done_flag = true; } int register_interest(XrlRibV0p1Client& client, EventLoop& loop, const IPv4& addr, bool expected_resolves, const IPv4Net& expected_net, const IPv4& expected_nexthop, uint32_t expected_metric) { XorpCallback7::RefPtr cb; cb = callback(register_done, expected_resolves, expected_net, expected_nexthop, expected_metric); client.send_register_interest4("rib", "ribclient", addr, cb); xrl_done_flag = false; while (xrl_done_flag == false) { loop.run(); } return XORP_OK; } int main(int /* argc */, char* argv[]) { // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); EventLoop eventloop; // Finder Server FinderServer fs(eventloop, FinderConstants::FINDER_DEFAULT_HOST(), FinderConstants::FINDER_DEFAULT_PORT()); // Rib Server component XrlStdRouter xrl_std_router_rib(eventloop, "rib"); // Rib Client component XrlStdRouter client_xrl_router(eventloop, "ribclient"); RibClientTarget ribclienttarget(&client_xrl_router); RibManager rib_manager(eventloop, xrl_std_router_rib, "fea"); // RIB Instantiations for XrlRibTarget RIB urib4(UNICAST, rib_manager, eventloop); RegisterServer register_server(&xrl_std_router_rib); urib4.initialize(register_server); if (urib4.add_igp_table("connected", "", "") != XORP_OK) { XLOG_ERROR("Could not add igp table \"connected\" for urib4"); abort(); } // Instantiated but not used RIB mrib4(MULTICAST, rib_manager, eventloop); mrib4.add_igp_table("connected", "", ""); RIB urib6(UNICAST, rib_manager, eventloop); urib6.add_igp_table("connected", "", ""); RIB mrib6(MULTICAST, rib_manager, eventloop); mrib6.add_igp_table("connected", "", ""); VifManager vif_manager(xrl_std_router_rib, eventloop, NULL, "fea"); vif_manager.enable(); vif_manager.start(); XrlRibTarget xrt(&xrl_std_router_rib, urib4, mrib4, urib6, mrib6, vif_manager, NULL); XrlRibV0p1Client xc(&xrl_std_router_rib); wait_until_xrl_router_is_ready(eventloop, xrl_std_router_rib); add_igp_table(xc, eventloop, "ospf"); add_egp_table(xc, eventloop, "ebgp"); urib4.print_rib(); add_vif(xc, eventloop, "xl0", IPv4("1.0.0.1"), IPv4Net("1.0.0.0/24")); add_vif(xc, eventloop, "xl1", IPv4("1.0.1.1"), IPv4Net("1.0.1.0/24")); add_route(xc, eventloop, "ospf", IPv4Net("9.0.0.0/24"), IPv4("1.0.0.2"), 7); printf("====================================================\n"); register_interest(xc, eventloop, IPv4("9.0.0.1"), true, IPv4Net("9.0.0.0/24"), IPv4("1.0.0.2"), 7); register_interest(xc, eventloop, IPv4("9.0.1.1"), false, IPv4Net("0.0.0.0/0"), IPv4("0.0.0.0"), 0); printf("====================================================\n"); callback_flag = false; delete_route(xc, eventloop, "ospf", IPv4Net("9.0.0.0/24")); // Wait for a callback while (callback_flag == false) { eventloop.run(); } ribclienttarget.verify_invalidated("9.0.0.0/24"); (void)ribclienttarget.verify_no_info(); printf("====================================================\n"); callback_flag = false; add_route(xc, eventloop, "ospf", IPv4Net("9.0.1.128/25"), IPv4("1.0.0.2"), 7); // Wait for a callback while (callback_flag == false) { eventloop.run(); } ribclienttarget.verify_invalidated("9.0.1.0/24"); (void)ribclienttarget.verify_no_info(); // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); exit (0); } xorp/rib/tests/dummy_register_server.cc0000664000076400007640000001146311421137511020533 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "rib_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "dummy_register_server.hh" extern bool verbose; DummyRegisterServer::DummyRegisterServer() : RegisterServer(NULL) { } void DummyRegisterServer::send_route_changed(const string& module_name, const IPv4Net& net, const IPv4& nexthop, uint32_t metric, uint32_t admin_distance, const string& protocol_origin, bool multicast) { string s; if (verbose) { printf("DummyRegisterServer::send_route_changed module=%s net=%s nexthop=%s, metric=%u admin_distance=%u protocol_origin=%s", module_name.c_str(), net.str().c_str(), nexthop.str().c_str(), XORP_UINT_CAST(metric), XORP_UINT_CAST(admin_distance), protocol_origin.c_str()); } s = module_name + " " + net.str() + " " + nexthop.str(); s += " " + c_format("%u", XORP_UINT_CAST(metric)); if (multicast) { if (verbose) printf("mcast=true\n"); s += " mcast:true"; } else { if (verbose) printf("mcast=false\n"); s += " mcast:false"; } _changed.insert(s); } void DummyRegisterServer::send_invalidate(const string& module_name, const IPv4Net& net, bool multicast) { string s; if (verbose) { printf("DummyRegisterServer::send_invalidate module=%s net=%s ", module_name.c_str(), net.str().c_str()); } s = module_name + " " + net.str(); if (multicast) { if (verbose) printf("mcast=true\n"); s += " mcast:true"; } else { if (verbose) printf("mcast=false\n"); s += " mcast:false"; } _invalidated.insert(s); } void DummyRegisterServer::send_route_changed(const string& module_name, const IPv6Net& net, const IPv6& nexthop, uint32_t metric, uint32_t admin_distance, const string& protocol_origin, bool multicast) { string s; printf("DummyRegisterServer::send_route_changed module=%s net=%s nexthop=%s, metric=%u admin_distance=%u protocol_origin=%s", module_name.c_str(), net.str().c_str(), nexthop.str().c_str(), XORP_UINT_CAST(metric), XORP_UINT_CAST(admin_distance), protocol_origin.c_str()); s = module_name + " " + net.str() + " " + nexthop.str(); s += " " + c_format("%u", XORP_UINT_CAST(metric)); if (multicast) { printf("mcast=true\n"); s += " mcast:true"; } else { printf("mcast=false\n"); s += " mcast:false"; } _changed.insert(s); } void DummyRegisterServer::send_invalidate(const string& module_name, const IPv6Net& net, bool multicast) { string s; if (verbose) { printf("DummyRegisterServer::send_invalidate module=%s net=%s ", module_name.c_str(), net.str().c_str()); } s = module_name + " " + net.str(); if (multicast) { if (verbose) printf("mcast=true\n"); s += " mcast:true"; } else { if (verbose) printf("mcast=false\n"); s += " mcast:false"; } _invalidated.insert(s); } bool DummyRegisterServer::verify_invalidated(const string& invalid) { set::iterator iter; iter = _invalidated.find(invalid); if (iter == _invalidated.end()) { if (verbose) printf("EXPECTED: >%s<\n", invalid.c_str()); for (iter = _invalidated.begin(); iter != _invalidated.end(); ++iter) if (verbose) printf("INVALIDATED: >%s<\n", iter->c_str()); return false; } _invalidated.erase(iter); return true; } bool DummyRegisterServer::verify_changed(const string& changed) { set::iterator iter; iter = _changed.find(changed); if (iter == _changed.end()) { if (verbose) printf("EXPECTED: >%s<\n", changed.c_str()); for (iter = _invalidated.begin(); iter != _invalidated.end(); ++iter) if (verbose) printf("CHANGED: >%s<\n", iter->c_str()); return false; } _changed.erase(iter); return true; } bool DummyRegisterServer::verify_no_info() { if (_changed.empty() && _invalidated.empty()) return true; return false; } xorp/rib/tests/dummy_register_server.hh0000664000076400007640000000404011540224234020537 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/rib/dummy_register_server.hh,v 1.14 2008/10/02 21:58:09 bms Exp $ #ifndef __RIB_DUMMY_REGISTER_SERVER_HH__ #define __RIB_DUMMY_REGISTER_SERVER_HH__ #include "register_server.hh" class DummyRegisterServer : public RegisterServer { public: DummyRegisterServer(); void send_route_changed(const string& module_name, const IPv4Net& net, const IPv4& nexthop, uint32_t metric, uint32_t admin_distance, const string& protocol_origin, bool multicast); void send_invalidate(const string& module_name, const IPv4Net& net, bool multicast); void send_route_changed(const string& module_name, const IPv6Net& net, const IPv6& nexthop, uint32_t metric, uint32_t admin_distance, const string& protocol_origin, bool multicast); void send_invalidate(const string& module_name, const IPv6Net& net, bool multicast); void flush() {} bool verify_invalidated(const string& invalid); bool verify_changed(const string& changed); bool verify_no_info(); private: set _invalidated; set _changed; }; #endif // __RIB_DUMMY_REGISTER_SERVER_HH__ xorp/rib/tests/SConscript0000664000076400007640000000531411421137511015607 0ustar greearbgreearb# Copyright (c) 2009 XORP, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # $XORP$ import os Import("env") env = env.Clone() env.AppendUnique(CPPPATH = [ '#', '$BUILDDIR', '$BUILDDIR/rib', '$BUILDDIR/rib/tests', ]) env.AppendUnique(LIBPATH = [ '$BUILDDIR/rib', '$BUILDDIR/policy', '$BUILDDIR/policy/backend', '$BUILDDIR/policy/common', '$BUILDDIR/libfeaclient', '$BUILDDIR/xrl/interfaces', '$BUILDDIR/xrl/targets', '$BUILDDIR/libproto', '$BUILDDIR/libxipc', '$BUILDDIR/libxorp', '$BUILDDIR/libcomm', ]) env.AppendUnique(LIBS = [ 'xorp_rib', #'xorp_rib_xrl', #notyet 'xorp_fea_client', 'xif_fea_ifmgr_mirror', 'xif_fea_ifmgr_replicator', 'xif_fti', 'xif_rib_client', 'xif_finder_event_notifier', 'xif_redist4', 'xif_redist6', 'xif_redist_transaction4', 'xif_redist_transaction6', 'xif_policy_redist4', 'xif_policy_redist6', 'xif_profile_client', 'xst_fea_ifmgr_mirror', 'xst_rib', 'xorp_policy_backend', 'xorp_policy_common', 'xorp_proto', 'xorp_finder', 'xorp_ipc', 'xorp_core', 'xorp_comm', ]) # XXX: Shell script wrappers here. # These just take input from a file 'command'. #test_rib_direct.cc #test_rib_direct.sh #test_rib_xrls.cc #test_rib_xrls.sh test_deletion = env.AutoTest(target = 'test_deletion', source = [ 'test_deletion.cc', 'rt_tab_expect.cc' ]) test_redist = env.AutoTest(target = 'test_redist', source = 'test_redist.cc') test_register = env.AutoTest(target = 'test_register', source = [ 'test_register.cc', 'dummy_register_server.cc' ]) # XXX NOTYET: part of compound test, scripting needed. #env = env.Clone() #env.AppendUnique(LIBS = [ # 'xif_rib', # 'xst_ribclient', # ]) #test_register_xrls = env.AutoTest(target = 'test_register_xrls', # source = 'test_register_xrls.cc') xorp/rib/tests/test_rib_direct.cc0000664000076400007640000000705611421137511017256 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "rib_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/exceptions.hh" #include "libxorp/eventloop.hh" #include "libxipc/finder_server.hh" #include "parser.hh" #include "parser_direct_cmds.hh" #include "rib_manager.hh" #include "rib.hh" #include "dummy_register_server.hh" bool verbose = false; class RibParser : public Parser { public: RibParser(RIB& rib); private: RIB& _rib; }; RibParser::RibParser(RIB& rib) : _rib(rib) { add_command(new DirectDiscardVifCommand(_rib)); add_command(new DirectUnreachableVifCommand(_rib)); add_command(new DirectManagementVifCommand(_rib)); add_command(new DirectEtherVifCommand(_rib)); add_command(new DirectLoopbackVifCommand(_rib)); add_command(new DirectRouteAddCommand(_rib)); add_command(new DirectRouteDeleteCommand(_rib)); add_command(new DirectRouteVifAddCommand(_rib)); add_command(new DirectRouteVerifyCommand(_rib)); add_command(new DirectTableOriginCommand(_rib)); add_command(new DirectAddIGPTableCommand(_rib)); add_command(new DirectDeleteIGPTableCommand(_rib)); add_command(new DirectAddEGPTableCommand(_rib)); add_command(new DirectDeleteEGPTableCommand(_rib)); } static void parser_main() { EventLoop eventloop; // Finder Server FinderServer fs(eventloop, FinderConstants::FINDER_DEFAULT_HOST(), FinderConstants::FINDER_DEFAULT_PORT()); // Rib Server component XrlStdRouter xrl_std_router_rib(eventloop, "rib", fs.addr(), fs.port()); // // The RIB manager // RibManager rib_manager(eventloop, xrl_std_router_rib, "fea"); rib_manager.enable(); RIB rib(UNICAST, rib_manager, eventloop); DummyRegisterServer register_server; rib.initialize_register(register_server); wait_until_xrl_router_is_ready(eventloop, xrl_std_router_rib); RibParser parser(rib); string cmd; int line = 0; while (feof(stdin) == 0) { getline(cin, cmd); if (feof(stdin) != 0) break; line++; printf("%d: %s\n", line, cmd.c_str()); parser.parse(cmd); } } int main (int /* argc */, char* argv[]) { // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); XorpUnexpectedHandler x(xorp_unexpected_handler); try { parser_main(); } catch (...) { xorp_catch_standard_exceptions(); } // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); exit(0); } xorp/rib/tests/test_redist.cc0000664000076400007640000003135011540224234016435 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "rib_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/callback.hh" #include "libxorp/eventloop.hh" #include "route.hh" #include "rib.hh" #include "rt_tab_redist.hh" #include "rt_tab_origin.hh" #include "rt_tab_deletion.hh" #ifdef HAVE_GETOPT_H #include #endif // HAVE_GETOPT_H /////////////////////////////////////////////////////////////////////////////// // // Constants // static const char* program_name = "test_redist"; static const char* program_description = "Test Route Redistribution Components"; static const char* program_version_id = "0.1"; static const char* program_date = "April, 2004"; static const char* program_copyright = "See file LICENSE"; static const char* program_return_value = "0 on success, 1 if test error, " "2 if internal error"; /////////////////////////////////////////////////////////////////////////////// // // Verbosity level control // static inline const char* xorp_path(const char* path) { const char* xorp_path = strstr(path, "xorp"); if (xorp_path) { return xorp_path; } return path; } static bool s_verbose = false; bool verbose() { return s_verbose; } void set_verbose(bool v) { s_verbose = v; } #define verbose_log(x...) _verbose_log(__FILE__,__LINE__, x) #define _verbose_log(file, line, x...) \ do { \ if (verbose()) { \ printf("From %s:%d: ", xorp_path(file), line); \ printf(x); \ } \ } while(0) template class TestOutput : public RedistOutput { public: typedef RedistOutput Base; typedef typename RedistTable::RouteIndex RouteIndex; static const IPNet ANY_NET; static const IPNet NO_NET; public: /** * Contructor * * @param block_after_op announce high water after any route operations. * This allows state to be controlled in piecemeal manner. */ TestOutput(Redistributor* r, bool block_after_op = true) : Base(r), _blocking(block_after_op), _blocked(false), _expect_net(ANY_NET) {} void unblock(); bool blocked() const { return _blocked; } uint32_t backlog() const { return _blocked ? 1 : 0; } uint32_t low_water_backlog() const { return 0; } uint32_t high_water_backlog() const { return 1; } void add_route(const IPRouteEntry& route); void delete_route(const IPRouteEntry& route); void starting_route_dump() {} void finishing_route_dump() {} void set_expected_net(const IPNet& n) { _expect_net = n; } const IPNet& expected_net() const { return _expect_net; } const RouteIndex& route_index() const { return _rt_index; } protected: bool _blocking; bool _blocked; IPNet _expect_net; // Generate error if update recv when t RouteIndex _rt_index; // Nets associated with live routes }; template const IPNet TestOutput::ANY_NET(A::ZERO(), 0); template <> const IPNet TestOutput::NO_NET(IPv4::ALL_ONES(), IPv4::ADDR_BITLEN); template <> const IPNet TestOutput::NO_NET(IPv6::ALL_ONES(), IPv6::ADDR_BITLEN); template void TestOutput::unblock() { _blocked = false; Base::announce_low_water(); } template void TestOutput::add_route(const IPRouteEntry& route) { if (_expect_net != ANY_NET && _expect_net != route.net()) { XLOG_FATAL("Unexpected add_route call. Expecting %s got %s", _expect_net.str().c_str(), route.net().str().c_str()); } verbose_log("add_route %s\n", route.net().str().c_str()); XLOG_ASSERT(_rt_index.find(route.net()) == _rt_index.end()); _rt_index.insert(route.net()); if (_blocking) { _blocked = true; Base::announce_high_water(); } } template void TestOutput::delete_route(const IPRouteEntry& route) { if (_expect_net != ANY_NET && _expect_net != route.net()) { XLOG_FATAL("Unexpected delete_route call. Expecting %s got %s", _expect_net.str().c_str(), route.net().str().c_str()); } verbose_log("delete_route %s\n", route.net().str().c_str()); typename RouteIndex::iterator i = _rt_index.find(route.net()); XLOG_ASSERT(i != _rt_index.end()); _rt_index.erase(i); if (_blocking) { _blocked = true; Base::announce_high_water(); } } template static bool unblock_output(TestOutput* to) { static TimeVal t0; TimeVal t1; if (t0 == TimeVal::ZERO()) { TimerList::system_gettimeofday(&t0); t1 = t0; } else { TimerList::system_gettimeofday(&t1); } TimeVal t = t1 - t0; verbose_log("Unblock output at %s\n", t.str().c_str()); bool r = to->blocked(); to->unblock(); return r; } template static void add_route_before_current(OriginTable* ot, TestOutput* to, IPRouteEntry ipr) { verbose_log("add_route_before_current %s\n", ipr.net().str().c_str()); IPNet saved = to->expected_net(); to->set_expected_net(ipr.net()); ot->add_route(ipr); to->set_expected_net(saved); } template static void add_route_after_current(OriginTable* ot, TestOutput* to, IPRouteEntry ipr) { verbose_log("add_route_after_current %s\n", ipr.net().str().c_str()); IPNet saved = to->expected_net(); to->set_expected_net(TestOutput::NO_NET); ot->add_route(ipr); to->set_expected_net(saved); } template static void delete_route_before_current(OriginTable* ot, TestOutput* to, IPRouteEntry ipr) { verbose_log("delete_route_before_current %s\n", ipr.net().str().c_str()); IPNet saved = to->expected_net(); to->set_expected_net(ipr.net()); ot->delete_route(ipr.net()); to->set_expected_net(saved); } template static void delete_route_after_current(OriginTable* ot, TestOutput* to, IPRouteEntry ipr) { verbose_log("delete_route_after_current %s\n", ipr.net().str().c_str()); IPNet saved = to->expected_net(); to->set_expected_net(TestOutput::NO_NET); ot->delete_route(ipr.net()); to->set_expected_net(saved); } static int test_deterministic() { EventLoop e; // Create an OriginTable OriginTable origin("static", 1, IGP, e); IPPeerNextHop nh("22.0.0.1"); Protocol protocol("static", IGP, 1); Vif tmp_vif("vif0"); RibVif vif((RIB*)NULL, tmp_vif); // Attach redist table RedistTable redist_table("StaticRedistTable", &origin); Redistributor* r = new Redistributor(e, "static_redist"); r->set_redist_table(&redist_table); TestOutput* output = new TestOutput(r, true); r->set_output(output); // Add some initial routes origin.add_route(IPRouteEntry("10.0.0.0/8", &vif, &nh, protocol, 10)); origin.add_route(IPRouteEntry("10.3.0.0/16", &vif, &nh, protocol, 10)); origin.add_route(IPRouteEntry("10.5.0.0/16", &vif, &nh, protocol, 10)); origin.add_route(IPRouteEntry("10.6.0.0/16", &vif, &nh, protocol, 10)); origin.add_route(IPRouteEntry("10.3.128.0/17", &vif, &nh, protocol, 10)); origin.add_route(IPRouteEntry("10.3.192.0/18", &vif, &nh, protocol, 10)); verbose_log("RedistTable index size = %u\n", XORP_UINT_CAST(redist_table.route_index().size())); // Each route added causes the output block. This timer unblocks the output // once per second. XorpTimer u = e.new_periodic_ms(1000, callback(&unblock_output, output)); // After 1 second the last route added is 10.3.0.0 // // Add one route after this XorpTimer a0 = e.new_oneoff_after_ms(1250, callback(&add_route_after_current, &origin, output, IPRouteEntry("10.4.0.0/16", &vif, &nh, protocol, 10))); // And two routes before XorpTimer a1 = e.new_oneoff_after_ms(1500, callback(&add_route_before_current, &origin, output, IPRouteEntry("10.1.0.0/16", &vif, &nh, protocol, 10))); XorpTimer a2 = e.new_oneoff_after_ms(1750, callback(&add_route_before_current, &origin, output, IPRouteEntry("10.2.0.0/16", &vif, &nh, protocol, 10))); // Delete first route XorpTimer d1 = e.new_oneoff_after_ms(2250, callback(&delete_route_before_current, &origin, output, IPRouteEntry("10.0.0.0/8", &vif, &nh, protocol, 10))); // Delete current route XorpTimer d2 = e.new_oneoff_after_ms(2500, callback(&delete_route_before_current, &origin, output, IPRouteEntry("10.4.0.0/16", &vif, &nh, protocol, 10))); // Delete last route XorpTimer d3 = e.new_oneoff_after_ms(2750, callback(&delete_route_before_current, &origin, output, IPRouteEntry("10.3.192.0/18", &vif, &nh, protocol, 10))); bool done = false; XorpTimer t = e.set_flag_after_ms(10000, &done); while (done == false) { e.run(); } // Dump should be finished by now XLOG_ASSERT(r->dumping() == false); // Expect indexed routes of output routes to match the RedistTable // index, ie they should all have been output during dump. XLOG_ASSERT(output->route_index() == redist_table.route_index()); // Expect updates after dump to be propagated immediately. add_route_before_current(&origin, output, IPRouteEntry("1.1.0.0/9", &vif, &nh, protocol, 10)); add_route_before_current(&origin, output, IPRouteEntry("20.1.127.0/24", &vif, &nh, protocol, 10)); delete_route_before_current(&origin, output, IPRouteEntry("20.1.127.0/24", &vif, &nh, protocol, 10)); // Expect output route index to still match the redist table index. XLOG_ASSERT(output->route_index() == redist_table.route_index()); XLOG_ASSERT(find(redist_table.route_index().begin(), redist_table.route_index().end(), IPv4Net("1.1.0.0/9")) != redist_table.route_index().end()); return 0; } /** * Print program info to output stream. * * @param stream the output stream the print the program info to. */ static void print_program_info(FILE *stream) { fprintf(stream, "Name: %s\n", program_name); fprintf(stream, "Description: %s\n", program_description); fprintf(stream, "Version: %s\n", program_version_id); fprintf(stream, "Date: %s\n", program_date); fprintf(stream, "Copyright: %s\n", program_copyright); fprintf(stream, "Return: %s\n", program_return_value); } /** * Print program usage information to the stderr. * * @param progname the name of the program. */ static void usage(const char* progname) { print_program_info(stderr); verbose_log("usage: %s [-v] [-h]\n", progname); verbose_log(" -h : usage (this message)\n"); verbose_log(" -v : verbose output\n"); } int main(int argc, char* const argv[]) { // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); // // Parse command line arguments // int ch; while ((ch = getopt(argc, argv, "hv")) != -1) { switch (ch) { case 'v': set_verbose(true); break; case 'h': case '?': default: usage(argv[0]); if (ch == 'h') return 0; else return 1; } } argc -= optind; argv += optind; // // Run test // XorpUnexpectedHandler x(xorp_unexpected_handler); try { test_deterministic(); } catch (...) { xorp_catch_standard_exceptions(); return 2; } // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); return 0; } xorp/rib/tests/test_rib_direct.sh0000775000076400007640000000027711421137511017304 0ustar greearbgreearb#!/bin/sh # # $XORP: xorp/rib/test_rib_direct.sh.in,v 1.1 2003/05/22 18:11:20 hodson Exp $ # if [ "X${srcdir}" = "X" ] ; then srcdir=`dirname $0` ; fi ./test_rib_direct < ${srcdir}/commands xorp/rib/tests/test_register.cc0000664000076400007640000002767211421137511017002 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "rib_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/eventloop.hh" #include "rib_manager.hh" #include "rib.hh" #include "dummy_register_server.hh" RIB* rib_ptr; bool verbose = false; static void verify_route(const string& ipv4, const string& vifname, const string& nexthop, uint32_t metric, const RibVerifyType matchtype) { if (verbose) { printf("-----------------------------------------------------------\n"); printf("VERIFY: type %d addr %s -> if: %s, NH: %s\n", (int)matchtype, ipv4.c_str(), vifname.c_str(), nexthop.c_str()); } if (rib_ptr->verify_route(IPv4(ipv4.c_str()), vifname, IPv4(nexthop.c_str()), metric, matchtype) != XORP_OK) { printf("route verify failed\n"); abort(); } } static void test_route_range(const string& ipv4, const string& verifytypestr, const string& vifname, const string& nexthop, const string& lower, const string& upper) { RouteRange* rr; RibVerifyType verifytype; if (verifytypestr == "miss") verifytype = RibVerifyType(MISS); else if (verifytypestr == "discard") verifytype = RibVerifyType(DISCARD); else if (verifytypestr == "unreachable") verifytype = RibVerifyType(UNREACHABLE); else if (verifytypestr == "ip") verifytype = RibVerifyType(IP); else { printf ("**RouteRange invalid match type specification\n"); abort(); } rr = rib_ptr->route_range_lookup(IPv4(ipv4.c_str())); if (verbose) printf("**RouteRange for %s\n", ipv4.c_str()); if (rr->route() != NULL) { DiscardNextHop* dnh; dnh = reinterpret_cast(rr->route()->nexthop()); if (verifytype != RibVerifyType(DISCARD) && dnh == NULL) { printf("**RouteRange for %s\n", ipv4.c_str()); printf("Expected discard route\n"); abort(); } UnreachableNextHop* unh; unh = reinterpret_cast(rr->route()->nexthop()); if (verifytype != RibVerifyType(UNREACHABLE) && unh == NULL) { printf("**RouteRange for %s\n", ipv4.c_str()); printf("Expected unreachable route\n"); abort(); } if (rr->route()->vif() == NULL) { printf("BAD Vif NULL != \"%s\"\n", vifname.c_str()); abort(); } else if (rr->route()->vif()->name() != vifname) { printf("**RouteRange for %s\n", ipv4.c_str()); printf("BAD Vif \"%s\" != \"%s\"\n", rr->route()->vif()->name().c_str(), vifname.c_str() ); abort(); } IPNextHop* nh; nh = reinterpret_cast* >(rr->route()->nexthop()); if (nh->addr().str() != nexthop) { printf("**RouteRange for %s\n", ipv4.c_str()); printf("Found Nexthop: %s\n", nh->addr().str().c_str()); printf("BAD Nexthop\n"); printf("Got: %s\n", nh->addr().str().c_str()); printf("Should be: %s\n", nexthop.c_str()); abort(); } } else { if (verifytype != RibVerifyType(MISS)) { printf("**RouteRange for %s\n", ipv4.c_str()); printf("Expected route miss, got a route\n"); abort(); } } if (lower != rr->bottom().str()) { printf("**RouteRange for %s\n", ipv4.c_str()); printf("Incorrect lower bound\n"); printf("**Lower bound: %s\n", rr->bottom().str().c_str()); printf("**should be: %s\n", lower.c_str()); abort(); } if (upper != rr->top().str()) { printf("**RouteRange for %s\n", ipv4.c_str()); printf("Incorrect upper bound\n"); printf("**Upper bound: %s\n", rr->top().str().c_str()); printf("**should be: %s\n", upper.c_str()); abort(); } // printf("****PASS****\n"); } int main (int /* argc */, char* argv[]) { // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); EventLoop eventloop; XrlStdRouter xrl_std_router_rib(eventloop, "rib"); // // The RIB manager // RibManager rib_manager(eventloop, xrl_std_router_rib, "fea"); rib_manager.enable(); RIB rib(UNICAST, rib_manager, eventloop); rib_ptr = &rib; Vif vif0("vif0"); Vif vif1("vif1"); Vif vif2("vif2"); vif0.set_underlying_vif_up(true); vif1.set_underlying_vif_up(true); vif2.set_underlying_vif_up(true); DummyRegisterServer register_server; rib.initialize(register_server); rib.add_igp_table("connected", "", ""); rib.new_vif("vif0", vif0); rib.add_vif_address("vif0", IPv4("10.0.0.1"), IPv4Net("10.0.0.0", 24), IPv4::ZERO(), IPv4::ZERO()); rib.new_vif("vif1", vif1); rib.add_vif_address("vif1", IPv4("10.0.1.1"), IPv4Net("10.0.1.0", 24), IPv4::ZERO(), IPv4::ZERO()); rib.new_vif("vif2", vif2); rib.add_vif_address("vif2", IPv4("10.0.2.1"), IPv4Net("10.0.2.0", 24), IPv4::ZERO(), IPv4::ZERO()); rib.add_igp_table("static", "", ""); rib.add_igp_table("ospf", "", ""); rib.add_egp_table("ebgp", "", ""); rib.add_egp_table("ibgp", "", ""); rib.print_rib(); // rib.add_route("connected", IPv4Net("10.0.0.0", 24), IPv4("10.0.0.1")); // rib.add_route("connected", IPv4Net("10.0.1.0", 24), IPv4("10.0.1.1")); // rib.add_route("connected", IPv4Net("10.0.2.0", 24), IPv4("10.0.2.1")); verify_route("10.0.0.4", "vif0", "10.0.0.1", 0, RibVerifyType(IP)); verify_route("10.0.1.4", "vif1", "10.0.1.1", 0, RibVerifyType(IP)); verify_route("10.0.2.4", "vif2", "10.0.2.1", 0, RibVerifyType(IP)); rib.add_route("static", IPv4Net("1.0.0.0", 16), IPv4("10.0.0.2"), "", "", 0, PolicyTags()); rib.add_route("static", IPv4Net("1.0.3.0", 24), IPv4("10.0.1.2"), "", "", 1, PolicyTags()); rib.add_route("static", IPv4Net("1.0.9.0", 24), IPv4("10.0.2.2"), "", "", 2, PolicyTags()); verify_route("1.0.5.4", "vif0", "10.0.0.2", 0, RibVerifyType(IP)); verify_route("1.0.3.4", "vif1", "10.0.1.2", 1, RibVerifyType(IP)); // Test with routes just in "static" and "connected" tables test_route_range("1.0.4.1", "ip", "vif0", "10.0.0.2", "1.0.4.0", "1.0.8.255"); test_route_range("1.0.3.1", "ip", "vif1", "10.0.1.2", "1.0.3.0", "1.0.3.255"); test_route_range("1.0.7.1", "ip", "vif0", "10.0.0.2", "1.0.4.0", "1.0.8.255"); test_route_range("2.0.0.1", "miss", "lo0", "0.0.0.0", "1.1.0.0", "9.255.255.255"); // Add a route to another IGP table rib.add_route("ospf", IPv4Net("1.0.6.0", 24), IPv4("10.0.2.2"), "", "", 3, PolicyTags()); test_route_range("1.0.4.1", "ip", "vif0", "10.0.0.2", "1.0.4.0", "1.0.5.255"); test_route_range("1.0.8.1", "ip", "vif0", "10.0.0.2", "1.0.7.0", "1.0.8.255"); test_route_range("1.0.6.1", "ip", "vif2", "10.0.2.2", "1.0.6.0", "1.0.6.255"); // Add an EGP route rib.add_route("ebgp", IPv4Net("5.0.5.0", 24), IPv4("1.0.3.1"), "", "", 4, PolicyTags()); test_route_range("5.0.5.1", "ip", "vif1", "10.0.1.2", "5.0.5.0", "5.0.5.255"); test_route_range("2.0.0.1", "miss", "lo0", "0.0.0.0", "1.1.0.0", "5.0.4.255"); rib.add_route("ebgp", IPv4Net("1.0.0.0", 20), IPv4("1.0.6.1"), "", "", 5, PolicyTags()); test_route_range("1.0.5.1", "ip", "vif2", "10.0.2.2", "1.0.4.0", "1.0.5.255"); test_route_range("1.0.6.1", "ip", "vif2", "10.0.2.2", "1.0.6.0", "1.0.6.255"); rib.add_route("ospf", IPv4Net("1.0.5.64", 26), IPv4("10.0.1.2"), "", "", 6, PolicyTags()); test_route_range("1.0.5.1", "ip", "vif2", "10.0.2.2", "1.0.4.0", "1.0.5.63"); // Add a couple of registrations RouteRegister* rreg; rreg = rib.route_register(IPv4("1.0.5.1"), string("foo")); if (verbose) printf("%s\n", rreg->str().c_str()); rreg = rib.route_register(IPv4("1.0.5.2"), string("foo2")); if (verbose) printf("%s\n", rreg->str().c_str()); // Check we can delete and add back if (rib.route_deregister(IPv4Net("1.0.5.0", 26), string("foo")) != XORP_OK) { abort(); } rreg = rib.route_register(IPv4("1.0.5.1"), string("foo")); if (verbose) printf("%s\n", rreg->str().c_str()); // Add a route that should have no effect rib.add_route("ospf", IPv4Net("1.0.11.0", 24), IPv4("10.0.1.2"), "", "", 1, PolicyTags()); if (verbose) printf("##########################\n"); if (!register_server.verify_no_info()) abort(); // Add a route that should cause both registrations to be invalidated rib.add_route("ospf", IPv4Net("1.0.5.0", 24), IPv4("10.0.1.2"), "", "", 1, PolicyTags()); if (!register_server.verify_invalidated("foo 1.0.5.0/26 mcast:false")) abort(); if (!register_server.verify_invalidated("foo2 1.0.5.0/26 mcast:false")) abort(); if (!register_server.verify_no_info()) abort(); // Verify that the deregister now fails because the registrations // became invalid if (rib.route_deregister(IPv4Net("1.0.5.0", 26), string("foo")) == XORP_OK) { abort(); } if (rib.route_deregister(IPv4Net("1.0.5.0", 26), string("foo2")) == XORP_OK) { abort(); } // Re-register interest rreg = rib.route_register(IPv4("1.0.5.1"), string("foo")); if (verbose) printf("%s\n", rreg->str().c_str()); rreg = rib.route_register(IPv4("1.0.5.2"), string("foo2")); if (verbose) printf("%s\n", rreg->str().c_str()); // // Delete the routes we just added. // This one should have no effect. // rib.delete_route("ospf", IPv4Net("1.0.11.0", 24)); if (!register_server.verify_no_info()) abort(); // This one should cause the registrations to be invalidated rib.delete_route("ospf", IPv4Net("1.0.5.0", 24)); if (!register_server.verify_invalidated("foo 1.0.5.0/26 mcast:false")) abort(); if (!register_server.verify_invalidated("foo2 1.0.5.0/26 mcast:false")) abort(); if (!register_server.verify_no_info()) abort(); // Test registration where the address doesn't resolve rib.add_route("ospf", IPv4Net("1.0.11.0", 24), IPv4("10.0.1.2"), "", "", 1, PolicyTags()); rib.add_route("ospf", IPv4Net("1.0.5.0", 24), IPv4("10.0.1.2"), "", "", 1, PolicyTags()); rib.delete_route("ebgp", IPv4Net("1.0.0.0", 20)); rib.delete_route("static", IPv4Net("1.0.0.0", 16)); rib.delete_route("ospf", IPv4Net("1.0.6.0", 24)); if (!register_server.verify_no_info()) abort(); // Register interest in all three non-existent ranges rreg = rib.route_register(IPv4("1.0.0.1"), string("foo")); if (verbose) printf("%s\n", rreg->str().c_str()); rreg = rib.route_register(IPv4("1.0.6.1"), string("foo")); if (verbose) printf("%s\n", rreg->str().c_str()); rreg = rib.route_register(IPv4("1.0.7.1"), string("foo2")); if (verbose) printf("%s\n", rreg->str().c_str()); rreg = rib.route_register(IPv4("1.0.13.1"), string("foo")); if (verbose) printf("%s\n", rreg->str().c_str()); rib.add_route("ospf", IPv4Net("1.0.6.0", 24), IPv4("10.0.2.2"), "", "", 3, PolicyTags()); if (!register_server.verify_invalidated("foo 1.0.6.0/23 mcast:false")) abort(); if (!register_server.verify_invalidated("foo2 1.0.6.0/23 mcast:false")) abort(); if (!register_server.verify_no_info()) abort(); // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); return 0; } xorp/rib/tests/rt_tab_expect.hh0000664000076400007640000000567011421137511016746 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/rib/rt_tab_expect.hh,v 1.12 2008/10/02 21:58:12 bms Exp $ #ifndef __RIB_RT_TAB_EXPECT_HH__ #define __RIB_RT_TAB_EXPECT_HH__ #include "rt_tab_base.hh" template class ExpectedRouteChange; /** * @short A Route Table for comparing route updates received against * expected. * * Users of this class specify expected updates with @ref expect_add * and @ref expect_delete. As the updates come through they are * compared against those expect and generate an assertion failure if * those arriving do not match those expected. An assertion failure * will also occur if there are unmatched updates upon instance * destruction. * * This class is strictly for debugging and testing purposes. */ template class ExpectTable : public RouteTable { public: ExpectTable(const string& tablename, RouteTable* parent); ~ExpectTable(); const list >& expected_route_changes() const { return _expected_route_changes; } void expect_add(const IPRouteEntry& route); void expect_delete(const IPRouteEntry& route); int add_route(const IPRouteEntry& route, RouteTable* caller); int delete_route(const IPRouteEntry* route, RouteTable* caller); const IPRouteEntry* lookup_route(const IPNet& net) const; const IPRouteEntry* lookup_route(const A& addr) const; RouteRange* lookup_route_range(const A& addr) const; TableType type() const { return EXPECT_TABLE; } RouteTable* parent() { return _parent; } void replumb(RouteTable* old_parent, RouteTable* new_parent); string str() const; private: RouteTable* _parent; list > _expected_route_changes; }; template class ExpectedRouteChange { public: ExpectedRouteChange(bool add, const IPRouteEntry& route); bool matches_add(const IPRouteEntry& route) const; bool matches_delete(const IPRouteEntry* route) const; string str() const; private: bool _add; // true = add, false = delete IPRouteEntry _route; }; #endif // __RIB_RT_TAB_EXPECT_HH__ xorp/rib/tests/commands0000664000076400007640000003214611421137511015324 0ustar greearbgreearb#local Vifs # type name addr netmask vif Ethernet de0 10.0.0.1 24 vif Ethernet de1 10.0.1.1 24 vif Ethernet de2 10.0.2.1 24 #vif Ethernet disc 0.0.0.0 0 # #routing tables # type name tag ad_dist #table origin connected 0 #table origin static 1 #table origin ospf 110 #table origin ebgp 20 #table origin ibgp 200 #table merged igp1 connected static #table merged igp igp1 ospf #table merged egp ebgp ibgp #table extint final egp igp add_egp_table ebgp add_egp_table ibgp add_igp_table ospf add_igp_table static add_igp_table connected add_igp_table rip # #redistribute enable ospf ebgp # #routes for connected subnets # cmd table subnet nexthop route add connected 10.0.0.0/24 10.0.0.1 0 route add connected 10.0.1.0/24 10.0.1.1 0 route add connected 10.0.2.0/24 10.0.2.1 0 # #add a default route to the discard interface #route add connected 0.0.0.0/0 0.0.0.0 0 # #test connected subnets route verify ip 10.0.0.2 de0 10.0.0.1 0 route verify ip 10.0.1.2 de1 10.0.1.1 0 route verify ip 10.0.2.2 de2 10.0.2.1 0 # #verify ip default works #route verify ip 192.150.187.1 disc 0.0.0.0 # #------------------------------------------------------------------- #add static routes # add default entry and verify ip it route add static 0.0.0.0/0 10.0.0.1 0 route verify ip 128.26.0.1 de0 10.0.0.1 0 route add static 128.16.0.0/16 10.0.1.2 1 route verify ip 128.16.0.1 de1 10.0.1.2 1 # route add static 128.16.64.0/20 10.0.2.2 1 route verify ip 128.16.64.1 de2 10.0.2.2 1 route verify ip 128.16.0.1 de1 10.0.1.2 1 route verify ip 128.16.128.1 de1 10.0.1.2 1 # route add static 128.16.0.0/20 10.0.0.2 1 route verify ip 128.16.0.1 de0 10.0.0.2 1 route verify ip 128.16.64.1 de2 10.0.2.2 1 route verify ip 128.16.128.1 de1 10.0.1.2 1 # #------------------------------------------------------------------- #check basic deletion works # delete the default entry and verify ip it route delete static 0.0.0.0/0 route verify miss 128.26.0.1 lo0 0.0.0.0 0 # route delete static 128.16.64.0/20 route verify ip 128.16.64.1 de1 10.0.1.2 1 #OK, add it back again route add static 128.16.64.0/20 10.0.2.2 1 route verify ip 128.16.64.1 de2 10.0.2.2 1 route verify ip 128.16.0.1 de0 10.0.0.2 1 route verify ip 128.16.128.1 de1 10.0.1.2 1 # #------------------------------------------------------------------- #Now add IGP routes to test override works properly # #this shouldn't change anything as static overrides it route add ospf 128.16.64.0/20 10.0.1.2 5 route verify ip 128.16.64.1 de2 10.0.2.2 1 # #add a new route to ospf table route add ospf 192.150.187.0/24 10.0.0.2 5 route verify ip 192.150.187.1 de0 10.0.0.2 5 #add a static to override it. route add static 192.150.187.0/24 10.0.1.2 5 route verify ip 192.150.187.1 de1 10.0.1.2 5 #delete the static and check it reverts to the ospf route route delete static 192.150.187.0/24 route verify ip 192.150.187.1 de0 10.0.0.2 5 # #------------------------------------------------------------------- #Now add an EBGP route route add ebgp 18.26.0.0/24 10.0.0.100 10 # Add the same route again route add ebgp 18.26.0.0/24 10.0.0.100 10 #note that we don't want 10.0.0.100 to be resolved to 10.0.0.1 because #10.0.0.100 is on a directly connected interface route verify ip 18.26.0.1 de0 10.0.0.100 10 # #Now add an IBGP route route add ibgp 18.26.1.0/24 192.150.187.1 7 # Add the same route again route add ibgp 18.26.1.0/24 192.150.187.1 7 #note that we do want 18.26.1.1 to be resolved to 10.0.0.2 because #192.150.187.1 is not on a directly connected interface route verify ip 18.26.1.1 de0 10.0.0.2 7 # #Now add an IBGP route where we can't reach the nexthop route add ibgp 18.26.2.0/24 192.150.188.1 7 route verify miss 18.26.2.1 lo0 0.0.0.0 0 # #then add the OSPF route to the nexthop route add ospf 192.150.188.0/24 10.0.2.4 5 route verify ip 18.26.2.1 de2 10.0.2.4 7 # #now delete the OSPF entry route delete ospf 192.150.188.0/24 route verify miss 18.26.2.1 lo0 0.0.0.0 0 # #then add the OSPF route to the nexthop back again route add ospf 192.150.188.0/24 10.0.2.4 5 route verify ip 18.26.2.1 de2 10.0.2.4 7 # #now add a more specific OSPF route to the nexthop and check the nexthop #for the IBGP route also changes route add ospf 192.150.188.0/25 10.0.1.4 5 route verify ip 18.26.2.1 de1 10.0.1.4 7 # #delete the more specific OSPF route and check the fallback works route delete ospf 192.150.188.0/25 route verify ip 18.26.2.1 de2 10.0.2.4 7 # #------------------------------------------------------------------- #specific codepath tests # #tests to ensure that extint chooses between internal and external #routes correctly route add ospf 1.2.3.0/24 10.0.1.4 5 route verify ip 1.2.3.4 de1 10.0.1.4 5 route add ibgp 1.2.3.0/24 10.0.2.4 7 route verify ip 1.2.3.4 de1 10.0.1.4 5 # route delete ibgp 1.2.3.0/24 route verify ip 1.2.3.4 de1 10.0.1.4 5 # route add ebgp 1.2.3.0/24 10.0.2.4 10 route verify ip 1.2.3.4 de2 10.0.2.4 10 route delete ebgp 1.2.3.0/24 route verify ip 1.2.3.4 de1 10.0.1.4 5 # route delete ospf 1.2.3.0/24 # # route verify ip 192.150.188.1 de2 10.0.2.4 5 route add ebgp 1.2.3.0/24 192.150.188.1 10 route verify ip 1.2.3.4 de2 10.0.2.4 10 route add ospf 1.2.3.0/24 10.0.1.4 10 route verify ip 1.2.3.4 de2 10.0.2.4 10 # route delete ebgp 1.2.3.0/24 route verify ip 1.2.3.4 de1 10.0.1.4 10 route delete ospf 1.2.3.0/24 # # route add ibgp 1.2.3.0/24 192.150.188.1 7 route verify ip 1.2.3.4 de2 10.0.2.4 7 route add ospf 1.2.3.0/24 10.0.1.4 7 route verify ip 1.2.3.4 de1 10.0.1.4 7 # route delete ospf 1.2.3.0/24 route verify ip 1.2.3.4 de2 10.0.2.4 7 route delete ibgp 1.2.3.0/24 # # route add ebgp 1.2.3.0/24 192.150.188.1 10 route verify ip 1.2.3.4 de2 10.0.2.4 10 route verify ip 18.26.2.1 de2 10.0.2.4 7 route delete ebgp 1.2.3.0/24 route delete ibgp 18.26.2.0/24 route verify miss 1.2.3.4 lo0 0.0.0.0 0 route verify miss 18.26.2.1 lo0 0.0.0.0 0 # # #tests for resolve_unresolved_nexthops # route add ebgp 1.2.3.0/24 2.0.0.1 10 route add ebgp 1.2.4.0/24 2.0.1.1 10 route add ebgp 1.2.5.0/24 2.0.2.1 10 route verify miss 1.2.3.1 lo0 0.0.0.0 0 route verify miss 1.2.4.1 lo0 0.0.0.0 0 route verify miss 1.2.5.1 lo0 0.0.0.0 0 # route add ospf 2.0.1.0/24 10.0.1.4 5 route verify miss 1.2.3.1 lo0 0.0.0.0 0 route verify ip 1.2.4.1 de1 10.0.1.4 10 route verify miss 1.2.5.1 lo0 0.0.0.0 0 # route delete ebgp 1.2.3.0/24 route delete ebgp 1.2.4.0/24 route delete ebgp 1.2.5.0/24 route delete ospf 2.0.1.0/24 # # # tests for lookup_next_by_igp_nexthop # route add ebgp 1.2.2.0/24 2.0.1.1 10 route add ebgp 1.2.3.0/24 2.0.0.1 10 route add ebgp 1.2.4.0/24 2.0.0.2 10 route add ebgp 1.2.5.0/24 2.0.0.3 10 route add ebgp 1.2.6.0/24 2.0.1.1 10 route add ebgp 1.2.7.0/24 2.0.1.1 10 route verify miss 1.2.2.1 lo0 0.0.0.0 0 route verify miss 1.2.3.1 lo0 0.0.0.0 0 route verify miss 1.2.4.1 lo0 0.0.0.0 0 route verify miss 1.2.5.1 lo0 0.0.0.0 0 route verify miss 1.2.6.1 lo0 0.0.0.0 0 route verify miss 1.2.7.1 lo0 0.0.0.0 0 # route add ospf 2.0.0.0/24 10.0.1.4 5 route verify miss 1.2.2.1 lo0 0.0.0.0 0 route verify ip 1.2.3.1 de1 10.0.1.4 10 route verify ip 1.2.4.1 de1 10.0.1.4 10 route verify ip 1.2.5.1 de1 10.0.1.4 10 route verify miss 1.2.6.1 lo0 0.0.0.0 0 route verify miss 1.2.7.1 lo0 0.0.0.0 0 # route add ospf 2.0.128.0/25 10.0.2.4 5 route verify miss 1.2.2.1 lo0 0.0.0.0 0 route verify ip 1.2.3.1 de1 10.0.1.4 10 route verify ip 1.2.4.1 de1 10.0.1.4 10 route verify ip 1.2.5.1 de1 10.0.1.4 10 route verify miss 1.2.6.1 lo0 0.0.0.0 0 route verify miss 1.2.7.1 lo0 0.0.0.0 0 # route delete ospf 2.0.128.0/25 route verify miss 1.2.2.1 lo0 0.0.0.0 0 route verify ip 1.2.3.1 de1 10.0.1.4 10 route verify ip 1.2.4.1 de1 10.0.1.4 10 route verify ip 1.2.5.1 de1 10.0.1.4 10 route verify miss 1.2.6.1 lo0 0.0.0.0 0 route verify miss 1.2.7.1 lo0 0.0.0.0 0 # route add ospf 2.0.0.0/25 10.0.2.4 5 route verify miss 1.2.2.1 lo0 0.0.0.0 0 route verify ip 1.2.3.1 de2 10.0.2.4 10 route verify ip 1.2.4.1 de2 10.0.2.4 10 route verify ip 1.2.5.1 de2 10.0.2.4 10 route verify miss 1.2.6.1 lo0 0.0.0.0 0 route verify miss 1.2.7.1 lo0 0.0.0.0 0 # route delete ospf 2.0.0.0/25 route verify miss 1.2.2.1 lo0 0.0.0.0 0 route verify ip 1.2.3.1 de1 10.0.1.4 10 route verify ip 1.2.4.1 de1 10.0.1.4 10 route verify ip 1.2.5.1 de1 10.0.1.4 10 route verify miss 1.2.6.1 lo0 0.0.0.0 0 route verify miss 1.2.7.1 lo0 0.0.0.0 0 # route add ospf 2.0.0.2/32 10.0.2.4 5 route verify miss 1.2.2.1 lo0 0.0.0.0 0 route verify ip 1.2.3.1 de1 10.0.1.4 10 route verify ip 1.2.4.1 de2 10.0.2.4 10 route verify ip 1.2.5.1 de1 10.0.1.4 10 route verify miss 1.2.6.1 lo0 0.0.0.0 0 route verify miss 1.2.7.1 lo0 0.0.0.0 0 # route delete ospf 2.0.0.0/24 route verify miss 1.2.2.1 lo0 0.0.0.0 0 route verify miss 1.2.3.1 lo0 0.0.0.0 0 route verify ip 1.2.4.1 de2 10.0.2.4 10 route verify miss 1.2.5.1 lo0 0.0.0.0 0 route verify miss 1.2.6.1 lo0 0.0.0.0 0 route verify miss 1.2.7.1 lo0 0.0.0.0 0 # route add ospf 2.0.1.0/24 10.0.0.4 5 route verify ip 1.2.2.1 de0 10.0.0.4 10 route verify miss 1.2.3.1 lo0 0.0.0.0 0 route verify ip 1.2.4.1 de2 10.0.2.4 10 route verify miss 1.2.5.1 lo0 0.0.0.0 0 route verify ip 1.2.6.1 de0 10.0.0.4 10 route verify ip 1.2.7.1 de0 10.0.0.4 10 # route add ospf 2.0.1.0/25 10.0.2.4 5 route verify ip 1.2.2.1 de2 10.0.2.4 10 route verify miss 1.2.3.1 lo0 0.0.0.0 0 route verify ip 1.2.4.1 de2 10.0.2.4 10 route verify miss 1.2.5.1 lo0 0.0.0.0 0 route verify ip 1.2.6.1 de2 10.0.2.4 10 route verify ip 1.2.7.1 de2 10.0.2.4 10 # route delete ospf 2.0.1.0/25 route verify ip 1.2.2.1 de0 10.0.0.4 10 route verify miss 1.2.3.1 lo0 0.0.0.0 0 route verify ip 1.2.4.1 de2 10.0.2.4 10 route verify miss 1.2.5.1 lo0 0.0.0.0 0 route verify ip 1.2.6.1 de0 10.0.0.4 10 route verify ip 1.2.7.1 de0 10.0.0.4 10 # route delete ospf 2.0.1.0/24 route delete ebgp 1.2.2.0/24 route delete ebgp 1.2.3.0/24 route delete ebgp 1.2.4.0/24 route delete ebgp 1.2.5.0/24 route delete ebgp 1.2.6.0/24 route verify miss 1.2.2.1 lo0 0.0.0.0 0 route verify miss 1.2.3.1 lo0 0.0.0.0 0 route verify miss 1.2.4.1 lo0 0.0.0.0 0 route verify miss 1.2.5.1 lo0 0.0.0.0 0 route verify miss 1.2.6.1 lo0 0.0.0.0 0 route verify miss 1.2.7.1 lo0 0.0.0.0 0 # # #test cases for lookup_route # route add ospf 1.2.3.0/24 10.0.0.1 5 route add ebgp 1.2.3.0/25 10.0.1.1 10 route verify ip 1.2.3.1 de1 10.0.1.1 10 route verify ip 1.2.3.129 de0 10.0.0.1 5 # route delete ospf 1.2.3.0/24 route delete ebgp 1.2.3.0/25 # route add ebgp 1.2.3.0/24 10.0.0.1 10 route add ospf 1.2.3.0/25 10.0.1.1 5 route verify ip 1.2.3.1 de1 10.0.1.1 5 route verify ip 1.2.3.129 de0 10.0.0.1 10 # route delete ebgp 1.2.3.0/24 route delete ospf 1.2.3.0/25 # route add ospf 1.2.3.0/24 10.0.1.1 5 route verify ip 1.2.3.1 de1 10.0.1.1 5 route add ebgp 1.2.3.0/24 10.0.0.1 10 route verify ip 1.2.3.1 de0 10.0.0.1 10 route delete ebgp 1.2.3.0/24 route verify ip 1.2.3.1 de1 10.0.1.1 5 route delete ospf 1.2.3.0/24 route verify miss 1.2.3.1 lo0 0.0.0.0 0 # #-------------------------------------------------------------- #test code for merged table # route verify miss 1.2.3.1 lo0 0.0.0.0 0 route add ibgp 1.2.3.0/24 10.0.0.1 7 route verify ip 1.2.3.1 de0 10.0.0.1 7 route add ebgp 1.2.3.0/24 10.0.0.3 10 route verify ip 1.2.3.1 de0 10.0.0.3 10 # route add ospf 1.2.3.0/24 10.0.0.4 5 route verify ip 1.2.3.1 de0 10.0.0.3 10 route delete ospf 1.2.3.0/24 route verify ip 1.2.3.1 de0 10.0.0.3 10 # route delete ebgp 1.2.3.0/24 route verify ip 1.2.3.1 de0 10.0.0.1 7 route delete ibgp 1.2.3.0/24 route verify miss 1.2.3.1 lo0 0.0.0.0 0 # route verify miss 1.2.3.1 lo0 0.0.0.0 0 route add ibgp 1.2.3.0/24 10.0.0.1 7 route verify ip 1.2.3.1 de0 10.0.0.1 7 route add ebgp 1.2.3.0/24 10.0.0.3 10 route verify ip 1.2.3.1 de0 10.0.0.3 10 # route delete ibgp 1.2.3.0/24 route verify ip 1.2.3.1 de0 10.0.0.3 10 route delete ebgp 1.2.3.0/24 route verify miss 1.2.3.1 lo0 0.0.0.0 0 # route verify miss 1.2.3.1 lo0 0.0.0.0 0 route add ospf 1.2.3.0/24 10.0.0.1 5 route verify ip 1.2.3.1 de0 10.0.0.1 5 route add static 1.2.3.0/24 10.0.0.3 1 route verify ip 1.2.3.1 de0 10.0.0.3 1 # route add ibgp 1.2.3.0/24 10.0.0.4 7 route verify ip 1.2.3.1 de0 10.0.0.3 1 route delete ibgp 1.2.3.0/24 route verify ip 1.2.3.1 de0 10.0.0.3 1 route delete static 1.2.3.0/24 route delete ospf 1.2.3.0/24 route verify miss 1.2.3.1 lo0 0.0.0.0 0 # route add ospf 1.2.3.0/24 10.0.0.1 5 route verify ip 1.2.3.1 de0 10.0.0.1 5 route add static 1.2.3.0/24 10.0.0.3 1 route verify ip 1.2.3.1 de0 10.0.0.3 1 route delete static 1.2.3.0/24 route delete ospf 1.2.3.0/24 route verify miss 1.2.3.1 lo0 0.0.0.0 0 # route verify miss 1.2.3.1 lo0 0.0.0.0 0 route add ibgp 1.2.3.0/24 10.0.0.1 7 route verify ip 1.2.3.1 de0 10.0.0.1 7 route add ebgp 1.2.3.0/25 10.0.0.3 10 route verify ip 1.2.3.1 de0 10.0.0.3 10 # route delete ibgp 1.2.3.0/24 route verify ip 1.2.3.1 de0 10.0.0.3 10 route delete ebgp 1.2.3.0/25 route verify miss 1.2.3.1 lo0 0.0.0.0 0 # route verify miss 1.2.3.1 lo0 0.0.0.0 0 route add ibgp 1.2.3.0/25 10.0.0.1 7 route verify ip 1.2.3.1 de0 10.0.0.1 7 route add ebgp 1.2.3.0/24 10.0.0.3 10 route verify ip 1.2.3.1 de0 10.0.0.1 7 # route delete ibgp 1.2.3.0/25 route verify ip 1.2.3.1 de0 10.0.0.3 10 route delete ebgp 1.2.3.0/24 route verify miss 1.2.3.1 lo0 0.0.0.0 0 # #-------------------------------------------------------------- #test case for adding and deleting non-resolved EGP routes # route add rip 10.20.30.0/24 10.0.0.2 2 route add ebgp 10.20.30.0/24 9.9.9.9 0 route delete ebgp 10.20.30.0/24 route add ebgp 10.20.30.0/24 9.9.9.9 0 xorp/rib/tests/test_rib_xrls.sh0000775000076400007640000000027411421137511017017 0ustar greearbgreearb#!/bin/sh # # $XORP: xorp/rib/test_rib_xrls.sh.in,v 1.1 2003/05/22 18:11:20 hodson Exp $ # if [ "X${srcdir}" = "X" ] ; then srcdir=`dirname $0` ; fi ./test_rib_xrls < ${srcdir}/commands xorp/rib/redist_xrl.cc0000664000076400007640000007510711540225533015134 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "rib_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/safe_callback_obj.hh" #include "libxipc/xrl_router.hh" #include "xrl/interfaces/redist4_xif.hh" #include "xrl/interfaces/redist6_xif.hh" #include "xrl/interfaces/redist_transaction4_xif.hh" #include "xrl/interfaces/redist_transaction6_xif.hh" #include "rib.hh" #include "route.hh" #include "redist_xrl.hh" #include "profile_vars.hh" /** * Base class for RedistXrlOutput Tasks. Classes derived from this * store enough state to dispatch XRL, or other task, at some * subsequent time in the future. */ template class RedistXrlTask : public CallbackSafeObject { public: RedistXrlTask(RedistXrlOutput* parent) : _parent(parent), _attempts(0) {} virtual ~RedistXrlTask() {} /** * @return true on success, false if XrlRouter could not dispatch * request. */ virtual bool dispatch(XrlRouter& xrl_router, Profile& profile) = 0; /** * Get number of times dispatch() invoked on instance. */ uint32_t dispatch_attempts() const { return _attempts; } protected: void incr_dispatch_attempts() { _attempts++; } RedistXrlOutput* parent() { return _parent; } const RedistXrlOutput* parent() const { return _parent; } void signal_complete_ok() { _parent->task_completed(this); } void signal_fatal_failure() { _parent->task_failed_fatally(this); } private: RedistXrlOutput* _parent; uint32_t _attempts; }; // ---------------------------------------------------------------------------- // Task declarations template class AddRoute : public RedistXrlTask { public: AddRoute(RedistXrlOutput* parent, const IPRouteEntry& ipr); virtual bool dispatch(XrlRouter& xrl_router, Profile& profile); void dispatch_complete(const XrlError& xe); protected: IPNet _net; A _nexthop; string _ifname; string _vifname; uint32_t _metric; uint32_t _admin_distance; string _protocol_origin; }; template class DeleteRoute : public RedistXrlTask { public: DeleteRoute(RedistXrlOutput* parent, const IPRouteEntry& ipr); virtual bool dispatch(XrlRouter& xrl_router, Profile& profile); void dispatch_complete(const XrlError& xe); protected: IPNet _net; A _nexthop; string _ifname; string _vifname; uint32_t _metric; uint32_t _admin_distance; string _protocol_origin; }; template class StartingRouteDump : public RedistXrlTask { public: StartingRouteDump(RedistXrlOutput* parent); virtual bool dispatch(XrlRouter& xrl_router, Profile& profile); void dispatch_complete(const XrlError& xe); }; template class FinishingRouteDump : public RedistXrlTask { public: FinishingRouteDump(RedistXrlOutput* parent); virtual bool dispatch(XrlRouter& xrl_router, Profile& profile); void dispatch_complete(const XrlError& xe); }; template class Pause : public RedistXrlTask { public: Pause(RedistXrlOutput* parent, uint32_t ms); virtual bool dispatch(XrlRouter& xrl_router, Profile& profile); void expire(); private: XorpTimer _t; uint32_t _p_ms; }; // ---------------------------------------------------------------------------- // AddRoute implementation template AddRoute::AddRoute(RedistXrlOutput* parent, const IPRouteEntry& ipr) : RedistXrlTask(parent), _net(ipr.net()), _nexthop(ipr.nexthop_addr()), _ifname(ipr.vif()->ifname()), _vifname(ipr.vif()->name()), _metric(ipr.metric()), _admin_distance(ipr.admin_distance()), _protocol_origin(ipr.protocol().name()) { } template <> bool AddRoute::dispatch(XrlRouter& xrl_router, Profile& profile) { #ifndef XORP_DISABLE_PROFILE if (profile.enabled(profile_route_rpc_out)) profile.log(profile_route_rpc_out, c_format("add %s", _net.str().c_str())); #else UNUSED(profile); #endif RedistXrlOutput* p = this->parent(); XrlRedist4V0p1Client cl(&xrl_router); return cl.send_add_route(p->xrl_target_name().c_str(), _net, _nexthop, _ifname, _vifname, _metric, _admin_distance, p->cookie(), _protocol_origin, callback(this, &AddRoute::dispatch_complete) ); } template <> bool AddRoute::dispatch(XrlRouter& xrl_router, Profile& profile) { #ifndef XORP_DISABLE_PROFILE if (profile.enabled(profile_route_rpc_out)) profile.log(profile_route_rpc_out, c_format("add %s", _net.str().c_str())); #else UNUSED(profile); #endif RedistXrlOutput* p = this->parent(); XrlRedist6V0p1Client cl(&xrl_router); return cl.send_add_route(p->xrl_target_name().c_str(), _net, _nexthop, _ifname, _vifname, _metric, _admin_distance, p->cookie(), _protocol_origin, callback(this, &AddRoute::dispatch_complete) ); } template void AddRoute::dispatch_complete(const XrlError& xe) { if (xe == XrlError::OKAY()) { this->signal_complete_ok(); return; } else if (xe == XrlError::COMMAND_FAILED()) { XLOG_ERROR("Failed to redistribute route add for %s: %s", _net.str().c_str(), xe.str().c_str()); this->signal_complete_ok(); return; } // For now all errors are signalled fatal XLOG_ERROR("Fatal error during route redistribution: %s", xe.str().c_str()); this->signal_fatal_failure(); } // ---------------------------------------------------------------------------- // DeleteRoute implementation template DeleteRoute::DeleteRoute(RedistXrlOutput* parent, const IPRouteEntry& ipr) : RedistXrlTask(parent), _net(ipr.net()), _nexthop(ipr.nexthop_addr()), _ifname(ipr.vif()->ifname()), _vifname(ipr.vif()->name()), _metric(ipr.metric()), _admin_distance(ipr.admin_distance()), _protocol_origin(ipr.protocol().name()) { } template <> bool DeleteRoute::dispatch(XrlRouter& xrl_router, Profile& profile) { #ifndef XORP_DISABLE_PROFILE if (profile.enabled(profile_route_rpc_out)) profile.log(profile_route_rpc_out, c_format("delete %s", _net.str().c_str())); #else UNUSED(profile); #endif RedistXrlOutput* p = this->parent(); XrlRedist4V0p1Client cl(&xrl_router); return cl.send_delete_route(p->xrl_target_name().c_str(), _net, _nexthop, _ifname, _vifname, _metric, _admin_distance, p->cookie(), _protocol_origin, callback(this, &DeleteRoute::dispatch_complete) ); } template <> bool DeleteRoute::dispatch(XrlRouter& xrl_router, Profile& profile) { #ifndef XORP_DISABLE_PROFILE if (profile.enabled(profile_route_rpc_out)) profile.log(profile_route_rpc_out, c_format("delete %s", _net.str().c_str())); #else UNUSED(profile); #endif RedistXrlOutput* p = this->parent(); XrlRedist6V0p1Client cl(&xrl_router); return cl.send_delete_route(p->xrl_target_name().c_str(), _net, _nexthop, _ifname, _vifname, _metric, _admin_distance, p->cookie(), _protocol_origin, callback(this, &DeleteRoute::dispatch_complete) ); } template void DeleteRoute::dispatch_complete(const XrlError& xe) { if (xe == XrlError::OKAY()) { this->signal_complete_ok(); return; } else if (xe == XrlError::COMMAND_FAILED()) { XLOG_ERROR("Failed to redistribute route delete for %s: %s", _net.str().c_str(), xe.str().c_str()); this->signal_complete_ok(); return; } // XXX For now all errors signalled as fatal XLOG_ERROR("Fatal error during route redistribution: %s", xe.str().c_str()); this->signal_fatal_failure(); } // ---------------------------------------------------------------------------- // StartingRouteDump implementation template StartingRouteDump::StartingRouteDump(RedistXrlOutput* parent) : RedistXrlTask(parent) { } template <> bool StartingRouteDump::dispatch(XrlRouter& xrl_router, Profile&) { RedistXrlOutput* p = this->parent(); XrlRedist4V0p1Client cl(&xrl_router); return cl.send_starting_route_dump( p->xrl_target_name().c_str(), p->cookie(), callback(this, &StartingRouteDump::dispatch_complete) ); } template <> bool StartingRouteDump::dispatch(XrlRouter& xrl_router, Profile&) { RedistXrlOutput* p = this->parent(); XrlRedist6V0p1Client cl(&xrl_router); return cl.send_starting_route_dump( p->xrl_target_name().c_str(), p->cookie(), callback(this, &StartingRouteDump::dispatch_complete) ); } template void StartingRouteDump::dispatch_complete(const XrlError& xe) { if (xe == XrlError::OKAY()) { this->signal_complete_ok(); return; } else if (xe == XrlError::COMMAND_FAILED()) { XLOG_ERROR("Failed to send starting route dump: %s", xe.str().c_str()); this->signal_complete_ok(); return; } // XXX For now all errors signalled as fatal XLOG_ERROR("Fatal error during route redistribution: %s", xe.str().c_str()); this->signal_fatal_failure(); } // ---------------------------------------------------------------------------- // FinishingRouteDump implementation template FinishingRouteDump::FinishingRouteDump(RedistXrlOutput* parent) : RedistXrlTask(parent) { } template <> bool FinishingRouteDump::dispatch(XrlRouter& xrl_router, Profile&) { RedistXrlOutput* p = this->parent(); XrlRedist4V0p1Client cl(&xrl_router); return cl.send_finishing_route_dump( p->xrl_target_name().c_str(), p->cookie(), callback(this, &FinishingRouteDump::dispatch_complete) ); } template <> bool FinishingRouteDump::dispatch(XrlRouter& xrl_router, Profile&) { RedistXrlOutput* p = this->parent(); XrlRedist6V0p1Client cl(&xrl_router); return cl.send_finishing_route_dump( p->xrl_target_name().c_str(), p->cookie(), callback(this, &FinishingRouteDump::dispatch_complete) ); } template void FinishingRouteDump::dispatch_complete(const XrlError& xe) { if (xe == XrlError::OKAY()) { this->signal_complete_ok(); return; } else if (xe == XrlError::COMMAND_FAILED()) { XLOG_ERROR("Failed to send finishing route dump: %s", xe.str().c_str()); this->signal_complete_ok(); return; } // XXX For now all errors signalled as fatal XLOG_ERROR("Fatal error during route redistribution: %s", xe.str().c_str()); this->signal_fatal_failure(); } // ---------------------------------------------------------------------------- // Pause implementation template Pause::Pause(RedistXrlOutput* parent, uint32_t ms) : RedistXrlTask(parent), _p_ms(ms) { } template bool Pause::dispatch(XrlRouter& xrl_router, Profile&) { this->incr_dispatch_attempts(); EventLoop& e = xrl_router.eventloop(); _t = e.new_oneoff_after_ms(_p_ms, callback(this, &Pause::expire)); return true; } template void Pause::expire() { this->signal_complete_ok(); } // ---------------------------------------------------------------------------- // RedistXrlOutput implementation template RedistXrlOutput::RedistXrlOutput(Redistributor* redistributor, XrlRouter& xrl_router, Profile& profile, const string& from_protocol, const string& xrl_target_name, const IPNet& network_prefix, const string& cookie) : RedistOutput(redistributor), _xrl_router(xrl_router), _profile(profile), _from_protocol(from_protocol), _target_name(xrl_target_name), _network_prefix(network_prefix), _cookie(cookie), _queued(0), _inflight(0), _flow_controlled(0), _callback_pending(0) { } template RedistXrlOutput::~RedistXrlOutput() { while (_taskq.empty() == false) { delete _taskq.front(); _taskq.pop_front(); } while (_flyingq.empty() == false) { delete _flyingq.front(); _flyingq.pop_front(); } } template void RedistXrlOutput::enqueue_task(Task* task) { _taskq.push_back(task); _queued++; } template void RedistXrlOutput::add_route(const IPRouteEntry& ipr) { if (! _network_prefix.contains(ipr.net())) return; // The target is not interested in this route PROFILE(if (_profile.enabled(profile_route_rpc_in)) _profile.log(profile_route_rpc_in, c_format("add %s", ipr.net().str().c_str()))); enqueue_task(new AddRoute(this, ipr)); if (_queued == 1) start_next_task(); } template void RedistXrlOutput::delete_route(const IPRouteEntry& ipr) { if (! _network_prefix.contains(ipr.net())) return; // The target is not interested in this route PROFILE(if (_profile.enabled(profile_route_rpc_in)) _profile.log(profile_route_rpc_in, c_format("delete %s", ipr.net().str().c_str()))); enqueue_task(new DeleteRoute(this, ipr)); if (_queued == 1) start_next_task(); } template void RedistXrlOutput::starting_route_dump() { enqueue_task(new StartingRouteDump(this)); if (_queued == 1) start_next_task(); } template void RedistXrlOutput::finishing_route_dump() { enqueue_task(new FinishingRouteDump(this)); if (_queued == 1) start_next_task(); } template void RedistXrlOutput::start_next_task() { XLOG_ASSERT(_queued >= 1); if (_inflight) return; while (_queued && !_flow_controlled && !_callback_pending) { RedistXrlTask* t = _taskq.front(); if (t->dispatch(_xrl_router, _profile) == false) { // Dispatch of task failed. XrlRouter is presumeably // backlogged. XLOG_WARNING("Dispatch failed, %d XRLs inflight", _inflight); if (_inflight == 0) { // Insert a delay and dispatch that to cause later // attempt at failing task. // This should never happen under normal circumstances! t = new Pause(this, RETRY_PAUSE_MS); t->dispatch(_xrl_router, _profile); incr_inflight(); } _flow_controlled = true; return; } else { incr_inflight(); _flyingq.push_back(t); _taskq.pop_front(); _queued--; } } } template void RedistXrlOutput::task_completed(RedistXrlTask* task) { if (task == _flyingq.front()) _flyingq.pop_front(); else { XLOG_WARNING("task != _flyingq.front()"); _flyingq.remove(task); } decr_inflight(); delete task; if (this->_queued != 0) this->start_next_task(); } template void RedistXrlOutput::task_failed_fatally(RedistXrlTask* task) { if (task == _flyingq.front()) _flyingq.pop_front(); else { XLOG_WARNING("task != _flyingq.front()"); _flyingq.remove(task); } decr_inflight(); delete task; this->announce_fatal_error(); } // ---------------------------------------------------------------------------- // RedistTransactionXrlOutput Commands template class AddTransactionRoute : public AddRoute { public: AddTransactionRoute(RedistTransactionXrlOutput* parent, const IPRouteEntry& ipr) : AddRoute(parent, ipr) { parent->incr_transaction_size(); } virtual bool dispatch(XrlRouter& xrl_router, Profile& profile); }; template class DeleteTransactionRoute : public DeleteRoute { public: DeleteTransactionRoute(RedistTransactionXrlOutput* parent, const IPRouteEntry& ipr) : DeleteRoute(parent, ipr) { parent->incr_transaction_size(); } virtual bool dispatch(XrlRouter& xrl_router, Profile& profile); }; template class StartTransaction : public RedistXrlTask { public: StartTransaction(RedistTransactionXrlOutput* parent) : RedistXrlTask(parent) { parent->reset_transaction_size(); } virtual bool dispatch(XrlRouter& xrl_router, Profile& profile); void dispatch_complete(const XrlError& xe, const uint32_t* tid); }; template class CommitTransaction : public RedistXrlTask { public: CommitTransaction(RedistTransactionXrlOutput* parent) : RedistXrlTask(parent) { parent->reset_transaction_size(); } virtual bool dispatch(XrlRouter& xrl_router, Profile& profile); void dispatch_complete(const XrlError& xe); }; template class AbortTransaction : public RedistXrlTask { public: AbortTransaction(RedistTransactionXrlOutput* parent) : RedistXrlTask(parent) { parent->reset_transaction_size(); } virtual bool dispatch(XrlRouter& xrl_router, Profile& profile); void dispatch_complete(const XrlError& xe); }; // ---------------------------------------------------------------------------- // AddTransactionRoute implementation template <> bool AddTransactionRoute::dispatch(XrlRouter& xrl_router, Profile& profile) { RedistTransactionXrlOutput* p = reinterpret_cast*>(this->parent()); if (p->transaction_in_error() || ! p->transaction_in_progress()) { XLOG_ERROR("Transaction error: failed to redistribute " "route add for %s", _net.str().c_str()); this->signal_complete_ok(); return true; // XXX: we return true to avoid retransmission } #ifndef XORP_DISABLE_PROFILE if (profile.enabled(profile_route_rpc_out)) profile.log(profile_route_rpc_out, c_format("add %s %s %s %u", p->xrl_target_name().c_str(), _net.str().c_str(), _nexthop.str().c_str(), XORP_UINT_CAST(_metric))); #else UNUSED(profile); #endif XrlRedistTransaction4V0p1Client cl(&xrl_router); return cl.send_add_route(p->xrl_target_name().c_str(), p->tid(), _net, _nexthop, _ifname, _vifname, _metric, _admin_distance, p->cookie(), _protocol_origin, callback(static_cast*>(this), &AddRoute::dispatch_complete) ); } template <> bool AddTransactionRoute::dispatch(XrlRouter& xrl_router, Profile& profile) { RedistTransactionXrlOutput* p = reinterpret_cast*>(this->parent()); if (p->transaction_in_error() || ! p->transaction_in_progress()) { XLOG_ERROR("Transaction error: failed to redistribute " "route add for %s", _net.str().c_str()); this->signal_complete_ok(); return true; // XXX: we return true to avoid retransmission } #ifndef XORP_DISABLE_PROFILE if (profile.enabled(profile_route_rpc_out)) profile.log(profile_route_rpc_out, c_format("add %s %s %s %u", p->xrl_target_name().c_str(), _net.str().c_str(), _nexthop.str().c_str(), XORP_UINT_CAST(_metric))); #else UNUSED(profile); #endif XrlRedistTransaction6V0p1Client cl(&xrl_router); return cl.send_add_route(p->xrl_target_name().c_str(), p->tid(), _net, _nexthop, _ifname, _vifname, _metric, _admin_distance, p->cookie(), _protocol_origin, callback(static_cast*>(this), &AddRoute::dispatch_complete) ); } // ---------------------------------------------------------------------------- // DeleteTransactionRoute implementation template <> bool DeleteTransactionRoute::dispatch(XrlRouter& xrl_router, Profile& profile) { RedistTransactionXrlOutput* p = reinterpret_cast*>(this->parent()); if (p->transaction_in_error() || ! p->transaction_in_progress()) { XLOG_ERROR("Transaction error: failed to redistribute " "route delete for %s", _net.str().c_str()); this->signal_complete_ok(); return true; // XXX: we return true to avoid retransmission } #ifndef XORP_DISABLE_PROFILE if (profile.enabled(profile_route_rpc_out)) profile.log(profile_route_rpc_out, c_format("delete %s %s", p->xrl_target_name().c_str(), _net.str().c_str())); #else UNUSED(profile); #endif XrlRedistTransaction4V0p1Client cl(&xrl_router); return cl.send_delete_route(p->xrl_target_name().c_str(), p->tid(), _net, _nexthop, _ifname, _vifname, _metric, _admin_distance, p->cookie(), _protocol_origin, callback(static_cast*>(this), &DeleteRoute::dispatch_complete) ); } template <> bool DeleteTransactionRoute::dispatch(XrlRouter& xrl_router, Profile& profile) { RedistTransactionXrlOutput* p = reinterpret_cast*>(this->parent()); if (p->transaction_in_error() || ! p->transaction_in_progress()) { XLOG_ERROR("Transaction error: failed to redistribute " "route delete for %s", _net.str().c_str()); this->signal_complete_ok(); return true; // XXX: we return true to avoid retransmission } #ifndef XORP_DISABLE_PROFILE if (profile.enabled(profile_route_rpc_out)) profile.log(profile_route_rpc_out, c_format("delete %s %s", p->xrl_target_name().c_str(), _net.str().c_str())); #else UNUSED(profile); #endif XrlRedistTransaction6V0p1Client cl(&xrl_router); return cl.send_delete_route(p->xrl_target_name().c_str(), p->tid(), _net, _nexthop, _ifname, _vifname, _metric, _admin_distance, p->cookie(), _protocol_origin, callback(static_cast*>(this), &DeleteRoute::dispatch_complete) ); } // ---------------------------------------------------------------------------- // StartTransaction implementation template <> bool StartTransaction::dispatch(XrlRouter& xrl_router, Profile&) { RedistTransactionXrlOutput* p = reinterpret_cast*>(this->parent()); p->set_callback_pending(true); p->set_tid(0); p->set_transaction_in_progress(true); p->set_transaction_in_error(false); XrlRedistTransaction4V0p1Client cl(&xrl_router); return cl.send_start_transaction( p->xrl_target_name().c_str(), callback(this, &StartTransaction::dispatch_complete)); } template <> bool StartTransaction::dispatch(XrlRouter& xrl_router, Profile&) { RedistTransactionXrlOutput* p = reinterpret_cast*>(this->parent()); p->set_callback_pending(true); p->set_tid(0); p->set_transaction_in_progress(true); p->set_transaction_in_error(false); XrlRedistTransaction6V0p1Client cl(&xrl_router); return cl.send_start_transaction( p->xrl_target_name().c_str(), callback(this, &StartTransaction::dispatch_complete)); } template void StartTransaction::dispatch_complete(const XrlError& xe, const uint32_t* tid) { RedistTransactionXrlOutput* p = reinterpret_cast*>(this->parent()); p->set_callback_pending(false); if (xe == XrlError::OKAY()) { p->set_tid(*tid); this->signal_complete_ok(); return; } else if (xe == XrlError::COMMAND_FAILED()) { XLOG_ERROR("Failed to start transaction: %s", xe.str().c_str()); p->set_transaction_in_progress(false); p->set_transaction_in_error(true); this->signal_complete_ok(); return; } // For now all errors are signalled fatal XLOG_ERROR("Fatal error during start transaction: %s", xe.str().c_str()); this->signal_fatal_failure(); } // ---------------------------------------------------------------------------- // CommitTransaction implementation template <> bool CommitTransaction::dispatch(XrlRouter& xrl_router, Profile&) { RedistTransactionXrlOutput* p = reinterpret_cast*>(this->parent()); uint32_t tid = p->tid(); p->set_tid(0); // XXX: reset the tid p->set_transaction_in_progress(false); p->set_transaction_in_error(false); XrlRedistTransaction4V0p1Client cl(&xrl_router); return cl.send_commit_transaction( p->xrl_target_name().c_str(), tid, callback(this, &CommitTransaction::dispatch_complete)); } template <> bool CommitTransaction::dispatch(XrlRouter& xrl_router, Profile&) { RedistTransactionXrlOutput* p = reinterpret_cast*>(this->parent()); uint32_t tid = p->tid(); p->set_tid(0); // XXX: reset the tid p->set_transaction_in_progress(false); p->set_transaction_in_error(false); XrlRedistTransaction6V0p1Client cl(&xrl_router); return cl.send_commit_transaction( p->xrl_target_name().c_str(), tid, callback(this, &CommitTransaction::dispatch_complete)); } template void CommitTransaction::dispatch_complete(const XrlError& xe) { if (xe == XrlError::OKAY()) { this->signal_complete_ok(); return; } else if (xe == XrlError::COMMAND_FAILED()) { XLOG_ERROR("Failed to commit transaction: %s", xe.str().c_str()); this->signal_complete_ok(); return; } // For now all errors are signalled fatal XLOG_ERROR("Fatal error during commit transaction: %s", xe.str().c_str()); this->signal_fatal_failure(); } // ---------------------------------------------------------------------------- // AbortTransaction implementation template <> bool AbortTransaction::dispatch(XrlRouter& xrl_router, Profile&) { RedistTransactionXrlOutput* p = reinterpret_cast*>(this->parent()); uint32_t tid = p->tid(); p->set_tid(0); // XXX: reset the tid p->set_transaction_in_progress(false); p->set_transaction_in_error(false); XrlRedistTransaction4V0p1Client cl(&xrl_router); return cl.send_abort_transaction( p->xrl_target_name().c_str(), tid, callback(this, &AbortTransaction::dispatch_complete)); } template <> bool AbortTransaction::dispatch(XrlRouter& xrl_router, Profile&) { RedistTransactionXrlOutput* p = reinterpret_cast*>(this->parent()); uint32_t tid = p->tid(); p->set_tid(0); // XXX: reset the tid p->set_transaction_in_progress(false); p->set_transaction_in_error(false); XrlRedistTransaction6V0p1Client cl(&xrl_router); return cl.send_abort_transaction( p->xrl_target_name().c_str(), tid, callback(this, &AbortTransaction::dispatch_complete)); } template void AbortTransaction::dispatch_complete(const XrlError& xe) { if (xe == XrlError::OKAY()) { this->signal_complete_ok(); return; } else if (xe == XrlError::COMMAND_FAILED()) { XLOG_ERROR("Failed to abort transaction: %s", xe.str().c_str()); this->signal_complete_ok(); return; } // For now all errors are signalled fatal XLOG_ERROR("Fatal error during abort transaction: %s", xe.str().c_str()); this->signal_fatal_failure(); } // ---------------------------------------------------------------------------- // RedistTransactionXrlOutput implementation template RedistTransactionXrlOutput::RedistTransactionXrlOutput( Redistributor* redistributor, XrlRouter& xrl_router, Profile& profile, const string& from_protocol, const string& xrl_target_name, const IPNet& network_prefix, const string& cookie ) : RedistXrlOutput(redistributor, xrl_router, profile, from_protocol, xrl_target_name, network_prefix, cookie), _tid(0), _transaction_in_progress(false), _transaction_in_error(false), _transaction_size(0) { } template void RedistTransactionXrlOutput::add_route(const IPRouteEntry& ipr) { PROFILE(if (this->_profile.enabled(profile_route_rpc_in)) this->_profile.log(profile_route_rpc_in, c_format("add %s %s %s %u", ipr.protocol().name().c_str(), ipr.net().str().c_str(), ipr.nexthop()->str().c_str(), XORP_UINT_CAST(ipr.metric())))); bool no_running_tasks = (this->_queued == 0); if (this->transaction_size() == 0) this->enqueue_task(new StartTransaction(this)); // // If the accumulated transaction size is too large, commit the // current transaction and start a new one. // if (this->transaction_size() >= MAX_TRANSACTION_SIZE) { this->enqueue_task(new CommitTransaction(this)); this->enqueue_task(new StartTransaction(this)); } this->enqueue_task(new AddTransactionRoute(this, ipr)); if (no_running_tasks) this->start_next_task(); } template void RedistTransactionXrlOutput::delete_route(const IPRouteEntry& ipr) { PROFILE(if (this->_profile.enabled(profile_route_rpc_in)) this->_profile.log(profile_route_rpc_in, c_format("add %s %s", ipr.protocol().name().c_str(), ipr.net().str().c_str()))); bool no_running_tasks = (this->_queued == 0); if (this->transaction_size() == 0) this->enqueue_task(new StartTransaction(this)); // // If the accumulated transaction size is too large, commit the // current transaction and start a new one. // if (this->transaction_size() >= MAX_TRANSACTION_SIZE) { this->enqueue_task(new CommitTransaction(this)); this->enqueue_task(new StartTransaction(this)); } this->enqueue_task(new DeleteTransactionRoute(this, ipr)); if (no_running_tasks) this->start_next_task(); } template void RedistTransactionXrlOutput::starting_route_dump() { } template void RedistTransactionXrlOutput::finishing_route_dump() { } template void RedistTransactionXrlOutput::task_completed(Task* task) { if (task == this->_flyingq.front()) this->_flyingq.pop_front(); else { XLOG_WARNING("task != this->_flyingq.front()"); this->_flyingq.remove(task); } this->decr_inflight(); delete task; if (this->_queued != 0) { this->start_next_task(); return; } if (transaction_in_progress()) { // // If transaction in progress, and this is the last add/delete, // then send "commit transaction". // this->enqueue_task(new CommitTransaction(this)); this->start_next_task(); return; } } // ---------------------------------------------------------------------------- // Instantiations template class RedistXrlOutput; template class RedistXrlOutput; template class RedistTransactionXrlOutput; template class RedistTransactionXrlOutput; xorp/rib/parser_xrl_cmds.hh0000664000076400007640000001377711421137511016157 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/rib/parser_xrl_cmds.hh,v 1.20 2008/10/02 21:58:10 bms Exp $ #ifndef __RIB_PARSER_XRL_CMDS_HH__ #define __RIB_PARSER_XRL_CMDS_HH__ #include "xrl/interfaces/rib_xif.hh" #include "parser.hh" enum XrlCompletion { XRL_PENDING = 0, // hack - corresponds with success for Parser::execute SUCCESS = 1, XRL_FAILED = -1 }; // Simple handler - most of RIB interface just returns true/false static void pass_fail_handler(const XrlError& e, XrlCompletion* c) { if (e == XrlError::OKAY()) { *c = SUCCESS; } else { *c = XRL_FAILED; cerr << "Xrl Failed: " << e.str() << endl; } cout << "PassFailHander " << *c << endl; } // ---------------------------------------------------------------------------- // XRL Commands (NB fewer than number of direct commands) class XrlRouteAddCommand : public RouteAddCommand { public: XrlRouteAddCommand(EventLoop& e, XrlRibV0p1Client& xrl_client, XrlCompletion& completion) : RouteAddCommand(), _eventloop(e), _xrl_client(xrl_client), _completion(completion) {} int execute() { cout << "RouteAddCommand::execute " << _tablename << " " << _net.str() << " " << _nexthop.str() << endl; _completion = XRL_PENDING; bool unicast = true, multicast = false; PolicyTags pt; _xrl_client.send_add_route4( "rib", _tablename, unicast, multicast, _net, _nexthop, _metric, pt.xrl_atomlist(), // XXX: no policy callback(&pass_fail_handler, &_completion)); return _completion; } private: EventLoop& _eventloop; XrlRibV0p1Client& _xrl_client; XrlCompletion& _completion; }; class XrlRouteDeleteCommand : public RouteDeleteCommand { public: XrlRouteDeleteCommand(EventLoop& e, XrlRibV0p1Client& xrl_client, XrlCompletion& completion) : RouteDeleteCommand(), _eventloop(e), _xrl_client(xrl_client), _completion(completion) {} int execute() { cout << "RouteDeleteCommand::execute " << _tablename << " " << _net.str() << endl; _completion = XRL_PENDING; bool unicast = true, multicast = false; _xrl_client.send_delete_route4( "rib", _tablename, unicast, multicast, _net, callback(&pass_fail_handler, &_completion)); return _completion; } private: EventLoop& _eventloop; XrlRibV0p1Client& _xrl_client; XrlCompletion& _completion; }; class XrlAddIGPTableCommand : public AddIGPTableCommand { public: XrlAddIGPTableCommand(EventLoop& e, XrlRibV0p1Client& xrl_client, XrlCompletion& completion) : AddIGPTableCommand(), _eventloop(e), _xrl_client(xrl_client), _completion(completion) {} int execute() { cout << "AddIGPTableCommand::execute " << _tablename << endl; _completion = XRL_PENDING; bool unicast = true, multicast = false; _xrl_client.send_add_igp_table4( "rib", _tablename, "", "", unicast, multicast, callback(&pass_fail_handler, &_completion)); return _completion; } private: EventLoop& _eventloop; XrlRibV0p1Client& _xrl_client; XrlCompletion& _completion; }; class XrlDeleteIGPTableCommand : public DeleteIGPTableCommand { public: XrlDeleteIGPTableCommand(EventLoop& e, XrlRibV0p1Client& xrl_client, XrlCompletion& completion) : DeleteIGPTableCommand(), _eventloop(e), _xrl_client(xrl_client), _completion(completion) {} int execute() { cout << "DeleteIGPTableCommand::execute " << _tablename << endl; _completion = XRL_PENDING; bool unicast = true, multicast = false; _xrl_client.send_delete_igp_table4( "rib", _tablename, "", "", unicast, multicast, callback(&pass_fail_handler, &_completion) ); return _completion; } private: EventLoop& _eventloop; XrlRibV0p1Client& _xrl_client; XrlCompletion& _completion; }; class XrlAddEGPTableCommand : public AddEGPTableCommand { public: XrlAddEGPTableCommand(EventLoop& e, XrlRibV0p1Client& xrl_client, XrlCompletion& completion) : AddEGPTableCommand(), _eventloop(e), _xrl_client(xrl_client), _completion(completion) {} int execute() { cout << "AddEGPTableCommand::execute " << _tablename << endl; _completion = XRL_PENDING; bool unicast = true, multicast = false; _xrl_client.send_add_egp_table4( "rib", _tablename, "", "", unicast, multicast, callback(&pass_fail_handler, &_completion)); return _completion; } private: EventLoop& _eventloop; XrlRibV0p1Client& _xrl_client; XrlCompletion& _completion; }; class XrlDeleteEGPTableCommand : public DeleteEGPTableCommand { public: XrlDeleteEGPTableCommand(EventLoop& e, XrlRibV0p1Client& xrl_client, XrlCompletion& completion) : DeleteEGPTableCommand(), _eventloop(e), _xrl_client(xrl_client), _completion(completion) {} int execute() { cout << "DeleteEGPTableCommand::execute " << _tablename << endl; _completion = XRL_PENDING; bool unicast = true, multicast = false; _xrl_client.send_delete_egp_table4( "rib", _tablename, "", "", unicast, multicast, callback(&pass_fail_handler, &_completion)); return _completion; } private: EventLoop& _eventloop; XrlRibV0p1Client& _xrl_client; XrlCompletion& _completion; }; #endif // __RIB_PARSER_XRL_CMDS_HH__ xorp/rib/tools/0000775000076400007640000000000011631505761013601 5ustar greearbgreearbxorp/rib/tools/show_distances.cc0000664000076400007640000003203711613332161017122 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "rib/rib_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/status_codes.h" #ifndef XORP_USE_USTL #include #endif #ifdef HAVE_GETOPT_H #include #endif #include "libxorp/c_format.hh" #include "libxorp/eventloop.hh" #include "libxorp/ipv4.hh" #include "libxorp/ipv6.hh" #include "libxorp/service.hh" #include "libxipc/xrl_std_router.hh" #include "xrl/interfaces/rib_xif.hh" #include "xrl/interfaces/finder_event_notifier_xif.hh" #include "xrl/targets/show_distances_base.hh" // ---------------------------------------------------------------------------- // Structure for holding command line options struct ShowDistancesOptions { bool ribin; // ribin (true), ribout (false) bool ipv4; // IPv4 (true), IPv6(false) bool unicast; // unicast (true), multicast (false) string xrl_target; string finder_host; uint16_t finder_port; ShowDistancesOptions() : ribin(false), ipv4(true), unicast(true) {} }; // ---------------------------------------------------------------------------- // Class for Querying RIB administrative distances class ShowDistancesProcessor : public XrlShowDistancesTargetBase, public ServiceBase { public: ShowDistancesProcessor(EventLoop& e, ShowDistancesOptions& opts); ~ShowDistancesProcessor(); int startup(); int shutdown(); public: XrlCmdError common_0_1_get_target_name(string& name); XrlCmdError common_0_1_get_version(string& version); XrlCmdError common_0_1_get_status(uint32_t& status, string& reason); XrlCmdError common_0_1_shutdown(); XrlCmdError common_0_1_startup() { return XrlCmdError::OKAY(); } XrlCmdError finder_event_observer_0_1_xrl_target_birth(const string& cls, const string& ins); XrlCmdError finder_event_observer_0_1_xrl_target_death(const string& cls, const string& ins); bool poll_ready_failed(); /** * Register with Finder to watch RIB birth and death events. If * the RIB crashes we don't want to hang waiting for messages from * the RIB that will never arrive. */ void step_100_watch_rib(); void watch_rib_cb(const XrlError& xe); /** * Get all registered admin distances for the selected RIB. */ void step_200_get_admin_distances(); void get_distances_cb(const XrlError& xe, const XrlAtomList* protocols, const XrlAtomList* distances); protected: EventLoop& _e; const ShowDistancesOptions& _opts; XrlRouter* _rtr; XorpTimer _t; XorpTask _task; multimap _admin_distances; }; ShowDistancesProcessor::ShowDistancesProcessor(EventLoop& e, ShowDistancesOptions& o) : _e(e), _opts(o), _rtr(NULL) { } ShowDistancesProcessor::~ShowDistancesProcessor() { if (_rtr != NULL) { delete _rtr; _rtr = NULL; } } int ShowDistancesProcessor::startup() { if (status() != SERVICE_READY) { return (XORP_ERROR); } // Create XrlRouter string process = c_format("show_distances<%d>", XORP_INT_CAST(getpid())); _rtr = new XrlStdRouter(_e, process.c_str(), _opts.finder_host.c_str(), _opts.finder_port); // Glue the router to the Xrl methods class exports this->set_command_map(_rtr); // Register methods with Finder _rtr->finalize(); // Start timer to poll whether XrlRouter becomes ready or fails so // we can continue processing. _t = _e.new_periodic_ms(250, callback(this, &ShowDistancesProcessor::poll_ready_failed)); set_status(SERVICE_STARTING); return (XORP_OK); } int ShowDistancesProcessor::shutdown() { this->set_command_map(NULL); ServiceStatus st = this->status(); if (st == SERVICE_FAILED || st == SERVICE_SHUTTING_DOWN || st == SERVICE_SHUTDOWN) return (XORP_ERROR); set_status(SERVICE_SHUTTING_DOWN); return (XORP_OK); } bool ShowDistancesProcessor::poll_ready_failed() { if (_rtr == 0) { return false; } else if (_rtr->ready()) { set_status(SERVICE_RUNNING); step_100_watch_rib(); return false; } else if (_rtr->failed()) { set_status(SERVICE_FAILED, "Failed: No Finder?"); return false; } return true; } void ShowDistancesProcessor::step_100_watch_rib() { XrlFinderEventNotifierV0p1Client fen(_rtr); if (fen.send_register_class_event_interest( "finder", _rtr->instance_name(), _opts.xrl_target, callback(this, &ShowDistancesProcessor::watch_rib_cb)) == false) { set_status(SERVICE_FAILED, c_format("Failed to register interest in %s", _opts.xrl_target.c_str())); return; } } void ShowDistancesProcessor::watch_rib_cb(const XrlError& xe) { if (xe == XrlError::OKAY()) { step_200_get_admin_distances(); return; } set_status(SERVICE_FAILED, c_format("Failed to register interest in %s", _opts.xrl_target.c_str())); return; } void ShowDistancesProcessor::step_200_get_admin_distances() { XrlRibV0p1Client rib(_rtr); XrlRibV0p1Client::GetProtocolAdminDistancesCB cb = callback(this, &ShowDistancesProcessor::get_distances_cb); bool sent = rib.send_get_protocol_admin_distances( _opts.xrl_target.c_str(), _opts.ipv4, _opts.unicast, cb); if (sent == false) { set_status(SERVICE_FAILED, "Failed to request admin distances from RIB."); return; } return; } void ShowDistancesProcessor::get_distances_cb(const XrlError& xe, const XrlAtomList* protocols, const XrlAtomList* distances) { const uint32_t __UNSET_ADMIN_DISTANCE = 256; // XXX if (xe != XrlError::OKAY()) { set_status(SERVICE_FAILED, "Request to obtain admin distances from RIB failed."); shutdown(); return; } try { XLOG_ASSERT(protocols->get(0).type() == XrlAtomType(xrlatom_text)); XLOG_ASSERT(protocols->size() >= 1); XLOG_ASSERT(distances->get(0).type() == XrlAtomType(xrlatom_uint32)); XLOG_ASSERT(distances->size() >= 1); XLOG_ASSERT(protocols->size() == distances->size()); for (size_t i = 0; i < protocols->size(); i++) { (void)_admin_distances.insert( pair( distances->get(i).uint32(), protocols->get(i).text())); } } catch (XrlAtomList::InvalidIndex ie) { fprintf(stdout, "Invalid data was returned by the RIB."); set_status(SERVICE_FAILED, "Invalid data was returned by the RIB."); shutdown(); return; } if (_admin_distances.size() == 0) { fprintf(stdout, "No administrative distances registered for this RIB.\n\n"); } else { fprintf(stdout, "Protocol Administrative distance\n"); for (multimap::iterator ii = _admin_distances.begin(); ii != _admin_distances.end(); ++ii) { fprintf(stdout, "%-24s ", ii->second.c_str()); if (ii->first == __UNSET_ADMIN_DISTANCE) { fprintf(stdout, "(unset)\n"); } else { fprintf(stdout, "%d\n", ii->first); } } fprintf(stdout, "\n"); } set_status(SERVICE_SHUTDOWN); shutdown(); return; } XrlCmdError ShowDistancesProcessor::common_0_1_get_target_name(string& name) { name = this->get_name(); return XrlCmdError::OKAY(); } XrlCmdError ShowDistancesProcessor::common_0_1_get_version(string& version) { version = this->version(); return XrlCmdError::OKAY(); } XrlCmdError ShowDistancesProcessor::common_0_1_get_status(uint32_t& status, string& /* reason */) { switch (this->status()) { case SERVICE_READY: status = PROC_NULL; break; case SERVICE_STARTING: status = PROC_STARTUP; break; case SERVICE_RUNNING: status = PROC_READY; break; case SERVICE_PAUSED: /* FALLTHRU */ case SERVICE_PAUSING: /* FALLTHRU */ case SERVICE_RESUMING: status = PROC_NOT_READY; break; case SERVICE_SHUTTING_DOWN: status = PROC_SHUTDOWN; break; case SERVICE_SHUTDOWN: status = PROC_DONE; break; case SERVICE_FAILED: status = PROC_FAILED; break; case SERVICE_ALL: break; } return XrlCmdError::OKAY(); } XrlCmdError ShowDistancesProcessor::common_0_1_shutdown() { this->shutdown(); return XrlCmdError::OKAY(); } XrlCmdError ShowDistancesProcessor::finder_event_observer_0_1_xrl_target_birth( const string& /* cls */, const string& /* ins */ ) { return XrlCmdError::OKAY(); } XrlCmdError ShowDistancesProcessor::finder_event_observer_0_1_xrl_target_death( const string& cls, const string& /* ins */) { if (cls == _opts.xrl_target) this->shutdown(); return XrlCmdError::OKAY(); } // ---------------------------------------------------------------------------- // Utility methods static void usage() { fprintf(stderr, "Usage: %s [options] (ribin|ribout) (ipv4|ipv6) (unicast|multicast) \n", xlog_process_name()); fprintf(stderr, "Options:\n"); fprintf(stderr, "\t -F : " "Specify Finder host and port to use.\n"); fprintf(stderr, "\t -T " "Specify XrlTarget to query.\n\n"); exit(1); } static bool parse_finder_args(const string& host_colon_port, string& host, uint16_t& port) { string::size_type sp = host_colon_port.find(":"); if (sp == string::npos) { host = host_colon_port; // Do not set port, by design it has default finder port value. } else { host = string(host_colon_port, 0, sp); string s_port = string(host_colon_port, sp + 1, 14); uint32_t t_port = atoi(s_port.c_str()); if (t_port == 0 || t_port > 65535) { XLOG_ERROR("Finder port %u is not in range 1--65535.\n", XORP_UINT_CAST(t_port)); return false; } port = (uint16_t)t_port; } return true; } static int find_option(const char* s, const char* opts[], size_t n_opts) { for (size_t i = 0; i < n_opts; i++) { if (strcmp(s, opts[i]) == 0) { return (int)i; } } fprintf(stderr, "Invalid option \"%s\", expected one of:", s); for (size_t i = 0; i < n_opts; i++) { fprintf(stderr, "%s\"%s\"", (i == 0) ? " " : ", ", opts[i]); } fprintf(stderr, "\n"); return -1; } static inline bool match_binary_option(const char* input, const char* option1, const char* option2, bool& matches_first) { const char* argv[2] = { option1, option2 }; int i = find_option(input, argv, 2); if (i < 0) return false; matches_first = (i == 0) ? true : false; return true; } // ---------------------------------------------------------------------------- // Main int main(int argc, char* const argv[]) { // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); try { ShowDistancesOptions sad_opts; sad_opts.finder_host = FinderConstants::FINDER_DEFAULT_HOST().str(); sad_opts.finder_port = FinderConstants::FINDER_DEFAULT_PORT(); sad_opts.xrl_target = "rib"; int ch; while ((ch = getopt(argc, argv, "F:T:")) != -1) { switch (ch) { case 'F': if (!parse_finder_args(optarg, sad_opts.finder_host, sad_opts.finder_port)) usage(); break; case 'T': sad_opts.xrl_target = optarg; break; default: usage(); } } argc -= optind; argv += optind; if (argc != 3) { usage(); } if (match_binary_option(argv[0], "ribin", "ribout", sad_opts.ribin) == false) { usage(); } if (match_binary_option(argv[1], "ipv4", "ipv6", sad_opts.ipv4) == false) { usage(); } if (match_binary_option(argv[2], "unicast", "multicast", sad_opts.unicast) == false) { usage(); } EventLoop e; ShowDistancesProcessor sad(e, sad_opts); sad.startup(); while ((sad.status() != SERVICE_FAILED) && (sad.status() != SERVICE_SHUTDOWN)) { e.run(); } if (sad.status() == SERVICE_FAILED) { sad.shutdown(); if (sad.status_note().empty() == false) { cout << sad.status_note() << endl; } else { cout << "Failed" << endl; } cout << endl; } } catch (...) { xorp_print_standard_exceptions(); } // if shutdown() is not called we may get free warnings // when sad goes out of scope. // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); return 0; } xorp/rib/tools/SConscript0000664000076400007640000000427511631505761015623 0ustar greearbgreearb# Copyright (c) 2009-2011 XORP, Inc and Others # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # $XORP$ import os Import('env') env = env.Clone() env.AppendUnique(CPPPATH = [ '#', '$BUILDDIR', ]) env.AppendUnique(LIBPATH = [ '$BUILDDIR/libxorp', '$BUILDDIR/libcomm', '$BUILDDIR/libxipc', '$BUILDDIR/libproto', '$BUILDDIR/xrl/interfaces', '$BUILDDIR/xrl/targets', '.', ]) env.AppendUnique(LIBS = [ 'xif_finder_event_notifier', 'xif_rib', 'xst_show_distances', 'xst_show_routes', 'xorp_ipc', 'xorp_core', 'xorp_proto', 'xorp_comm', ]) is_shared = env.has_key('SHAREDLIBS') if not is_shared: env.AppendUnique(LIBS = [ "crypto", ]) if not (env.has_key('mingw') and env['mingw']): env.AppendUnique(LIBS = [ "rt", ]) if (env.has_key('mingw') and env['mingw']): env.AppendUnique(LIBS = [ 'ws2_32', 'iphlpapi', 'winmm', # 'mprapi', # 'regex', ]) env.Append(LIBS = ['xorp_core']) env.Replace(RPATH = [ env.Literal(env['xorp_tool_rpath']) ]) shdistsrcs = [ 'show_distances.cc' ] shrtessrcs = [ 'show_routes.cc' ] shdist = env.Program(target = 'rib_show_distances', source = shdistsrcs) shrtes = env.Program(target = 'rib_show_routes', source = shrtessrcs) env.Alias('install', env.InstallProgram(env['xorp_tooldir'], shdist)) env.Alias('install', env.InstallProgram(env['xorp_tooldir'], shrtes)) Default(shdist, shrtes) xorp/rib/tools/show_routes.cc0000664000076400007640000005651311613332141016471 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "rib/rib_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/status_codes.h" #ifndef XORP_USE_USTL #include #endif #ifdef HAVE_GETOPT_H #include #endif #include "libxorp/c_format.hh" #include "libxorp/eventloop.hh" #include "libxorp/ipv4.hh" #include "libxorp/ipv6.hh" #include "libxorp/service.hh" #include "libxipc/xrl_std_router.hh" #include "xrl/interfaces/rib_xif.hh" #include "xrl/interfaces/finder_event_notifier_xif.hh" #include "xrl/targets/show_routes_base.hh" // // TODO: // - Beware of multiple routes to the same destination. // Something in the upper code should be done? // - Print the time for each route. // - Show the route Status (active/last/both) // - Show the AS Path // // // Print style // enum PrintStyle { PRINT_STYLE_BRIEF, PRINT_STYLE_DETAIL, PRINT_STYLE_TERSE, PRINT_STYLE_DEFAULT = PRINT_STYLE_BRIEF // XXX: default to BRIEF }; // ---------------------------------------------------------------------------- // Structure for holding command line options struct ShowRoutesOptions { bool ribin; // ribin (true), ribout (false) bool ipv4; // IPv4 (true), IPv6(false) bool unicast; // unicast (true), multicast (false) PrintStyle print_style; // -b (BRIEF), -d (DETAIL), -t (TERSE) const char* protocol; string xrl_target; string finder_host; uint16_t finder_port; ShowRoutesOptions() : ribin(false), ipv4(true), unicast(true), print_style(PRINT_STYLE_DEFAULT), protocol("all") {} }; // ---------------------------------------------------------------------------- // Display functions and helpers template static void display_route_brief(const IPNet& net, const A& nexthop, const string& ifname, const string& vifname, uint32_t metric, uint32_t admin_distance, const string& protocol_origin) { cout << "" << net.str() << "\t"; cout << "["; cout << protocol_origin << "(" << admin_distance << ")"; cout << "/" << metric << "]"; // // XXX: At the end of the line we should print the age of the route, // but for now we don't have this information. // // cout << " 1w0d 01:58:27"; cout << endl; cout << "\t\t> "; if (admin_distance != 0) cout << "to " << nexthop.str() << " "; if (ifname.empty() == false) cout << "via " << ifname; if (vifname.empty() == false) cout << "/" << vifname; cout << endl; } template static void display_route_detail(const IPNet& net, const A& nexthop, const string& ifname, const string& vifname, uint32_t metric, uint32_t admin_distance, const string& protocol_origin) { string tmp_name; string unknown_name = "UNKNOWN"; cout << "Network " << net.str() << endl; cout << " Nexthop := " << nexthop.str() << endl; if (ifname.empty() == false) tmp_name = ifname; else tmp_name = unknown_name; cout << " Interface := " << tmp_name << endl; if (vifname.empty() == false) tmp_name = vifname; else tmp_name = unknown_name; cout << " Vif := " << tmp_name << endl; cout << " Metric := " << metric << endl; cout << " Protocol := " << protocol_origin << endl; cout << " Admin distance := " << admin_distance << endl; } template static void display_route_terse(const IPNet& net, const A& nexthop, const string& ifname, const string& vifname, uint32_t metric, uint32_t admin_distance, const string& protocol_origin) { string protocol_short = protocol_origin.substr(0, 1); // // TODO: Show status of route: // + = Active Route, // - = Last Active, // * = Both #ifndef XORP_USE_USTL if (net.str().size() > 18) { cout << net.str() << endl << setw(19) << " "; } else { cout << setiosflags(ios::left) << setw(19) << net.str(); } cout << resetiosflags(ios::left) << protocol_short << " "; cout << setw(3) << admin_distance; cout << setw(10) << metric << " "; #else // uSTL doesn't have fancy formatting operations. cout << net.str() << endl << " " << protocol_short << " " << admin_distance << " " << metric << " "; #endif // XXX: We don't have second metric yet if (admin_distance != 0) cout << nexthop.str() << " "; if ((ifname.empty() == false) && (admin_distance == 0)) cout << ifname; if ((vifname.empty() == false) && (admin_distance == 0)) cout << "/" << vifname; // XXX: We don't have the AS path yet cout << endl; } // ---------------------------------------------------------------------------- // Class for Querying RIB routes class ShowRoutesProcessor : public XrlShowRoutesTargetBase, public ServiceBase { public: ShowRoutesProcessor(EventLoop& e, ShowRoutesOptions& opts); ~ShowRoutesProcessor(); int startup(); int shutdown(); public: XrlCmdError common_0_1_get_target_name(string& name); XrlCmdError common_0_1_get_version(string& version); XrlCmdError common_0_1_get_status(uint32_t& status, string& reason); XrlCmdError common_0_1_shutdown(); XrlCmdError common_0_1_startup() { return XrlCmdError::OKAY(); } XrlCmdError finder_event_observer_0_1_xrl_target_birth(const string& cls, const string& ins); XrlCmdError finder_event_observer_0_1_xrl_target_death(const string& cls, const string& ins); XrlCmdError redist4_0_1_starting_route_dump(const string& cookie); XrlCmdError redist4_0_1_finishing_route_dump(const string& cookie); XrlCmdError redist4_0_1_add_route(const IPv4Net& dst, const IPv4& nexthop, const string& ifname, const string& vifname, const uint32_t& metric, const uint32_t& admin_distance, const string& cookie, const string& protocol_origin); XrlCmdError redist4_0_1_delete_route(const IPv4Net& dst, const IPv4& nexthop, const string& ifname, const string& vifname, const uint32_t& metric, const uint32_t& admin_distance, const string& cookie, const string& protocol_origin); XrlCmdError redist6_0_1_starting_route_dump(const string& cookie); XrlCmdError redist6_0_1_finishing_route_dump(const string& cookie); XrlCmdError redist6_0_1_add_route(const IPv6Net& dst, const IPv6& nexthop, const string& ifname, const string& vifname, const uint32_t& metric, const uint32_t& admin_distance, const string& cookie, const string& protocol_origin); XrlCmdError redist6_0_1_delete_route(const IPv6Net& dst, const IPv6& nexthop, const string& ifname, const string& vifname, const uint32_t& metric, const uint32_t& admin_distance, const string& cookie, const string& protocol_origin); bool poll_ready_failed(); /** * @return true if cookie matches instance cookie, false otherwise. */ bool check_cookie(const string& cookie); /** * Register with Finder to watch RIB birth and death events. If * the RIB crashes we don't want to hang waiting for messages from * the RIB that will never arrive (start route dump, add route, * finish route dump) */ void step_100_watch_rib(); void watch_rib_cb(const XrlError& xe); /** * Request redistribution of user requested protocol. */ void step_200_request_redist(); void request_redist_cb(const XrlError& xe); /** * Request redistribution cease. */ void step_1000_request_cease(); void request_cease_cb(const XrlError& xe); protected: EventLoop& _e; const ShowRoutesOptions& _opts; XrlRouter* _rtr; XorpTimer _t; IPv4Net _network_prefix4; IPv6Net _network_prefix6; string _cookie; }; ShowRoutesProcessor::ShowRoutesProcessor(EventLoop& e, ShowRoutesOptions& o) : _e(e), _opts(o), _rtr(NULL), _network_prefix4(IPv4::ZERO(), 0), // XXX: get the whole table _network_prefix6(IPv6::ZERO(), 0) // XXX: get the whole table { } ShowRoutesProcessor::~ShowRoutesProcessor() { // Withdraw the Xrl methods set_command_map(NULL); if (_rtr != NULL) { delete _rtr; _rtr = NULL; } } int ShowRoutesProcessor::startup() { if (status() != SERVICE_READY) { return (XORP_ERROR); } // Create XrlRouter string process = c_format("show_routes<%d>", XORP_INT_CAST(getpid())); _rtr = new XrlStdRouter(_e, process.c_str(), _opts.finder_host.c_str(), _opts.finder_port); // Glue the router to the Xrl methods class exports this->set_command_map(_rtr); // Register methods with Finder _rtr->finalize(); // Start timer to poll whether XrlRouter becomes ready or fails so // we can continue processing. _t = _e.new_periodic_ms(250, callback(this, &ShowRoutesProcessor::poll_ready_failed)); set_status(SERVICE_STARTING); return (XORP_OK); } int ShowRoutesProcessor::shutdown() { ServiceStatus st = this->status(); if (st == SERVICE_FAILED || st == SERVICE_SHUTTING_DOWN || st == SERVICE_SHUTDOWN) return (XORP_ERROR); set_status(SERVICE_SHUTTING_DOWN); step_1000_request_cease(); return (XORP_OK); } bool ShowRoutesProcessor::poll_ready_failed() { if (_rtr == 0) { return false; } else if (_rtr->ready()) { set_status(SERVICE_RUNNING); step_100_watch_rib(); return false; } else if (_rtr->failed()) { set_status(SERVICE_FAILED, "Failed: No Finder?"); return false; } return true; } void ShowRoutesProcessor::step_100_watch_rib() { XrlFinderEventNotifierV0p1Client fen(_rtr); if (fen.send_register_class_event_interest( "finder", _rtr->instance_name(), _opts.xrl_target, callback(this, &ShowRoutesProcessor::watch_rib_cb)) == false) { set_status(SERVICE_FAILED, c_format("Failed to register interest in %s", _opts.xrl_target.c_str())); return; } } void ShowRoutesProcessor::watch_rib_cb(const XrlError& xe) { if (xe == XrlError::OKAY()) { step_200_request_redist(); return; } set_status(SERVICE_FAILED, c_format("Failed to register interest in %s", _opts.xrl_target.c_str())); return; } inline string rib_table_name(const string& protocol, bool ribin) { if (protocol == "all") return protocol; // "all" is a special name in the RIB for the rib out table. // "all-" adds redist with a filter for return c_format("%s%s", (ribin ? "" : "all-"), protocol.c_str()); } void ShowRoutesProcessor::step_200_request_redist() { string protocol = rib_table_name(_opts.protocol, _opts.ribin); XrlRibV0p1Client::RedistEnable4CB cb; cb = callback(this, &ShowRoutesProcessor::request_redist_cb); XrlRibV0p1Client rib(_rtr); bool sent = false; if (_opts.ipv4) { sent = rib.send_redist_enable4(_opts.xrl_target.c_str(), _rtr->instance_name(), protocol, _opts.unicast, !_opts.unicast, _network_prefix4, _cookie, cb); } else { #ifdef HAVE_IPV6 sent = rib.send_redist_enable6(_opts.xrl_target.c_str(), _rtr->instance_name(), protocol, _opts.unicast, !_opts.unicast, _network_prefix6, _cookie, cb); #endif } if (sent == false) { set_status(SERVICE_FAILED, "Failed to request redist."); } } void ShowRoutesProcessor::request_redist_cb(const XrlError& xe) { if (xe == XrlError::OKAY()) { return; } set_status(SERVICE_FAILED, c_format("Request for routes to be redistributed from %s " "failed.\nThe protocol is probably not active.", _opts.protocol)); return; } void ShowRoutesProcessor::step_1000_request_cease() { string proto = rib_table_name(_opts.protocol, _opts.ribin); XrlRibV0p1Client::RedistDisable4CB cb; cb = callback(this, &ShowRoutesProcessor::request_cease_cb); XrlRibV0p1Client rib(_rtr); bool sent = false; if (_opts.ipv4) { sent = rib.send_redist_disable4(_opts.xrl_target.c_str(), _rtr->instance_name(), proto, _opts.unicast, !_opts.unicast, _cookie, cb); } else { #ifdef HAVE_IPV6 sent = rib.send_redist_disable6(_opts.xrl_target.c_str(), _rtr->instance_name(), proto, _opts.unicast, !_opts.unicast, _cookie, cb); #endif } if (sent == false) { set_status(SERVICE_FAILED, "Failed to request redistribution end."); return; } set_status(SERVICE_SHUTTING_DOWN); } void ShowRoutesProcessor::request_cease_cb(const XrlError& /* xe */) { set_status(SERVICE_SHUTDOWN); return; } bool ShowRoutesProcessor::check_cookie(const string& cookie) { if (cookie != _cookie) { cerr << "Bug detected cookie mismatch \"" << cookie << "\" != \"" << _cookie << "\"" << endl; } return true; } XrlCmdError ShowRoutesProcessor::common_0_1_get_target_name(string& name) { name = this->get_name(); return XrlCmdError::OKAY(); } XrlCmdError ShowRoutesProcessor::common_0_1_get_version(string& version) { version = this->version(); return XrlCmdError::OKAY(); } XrlCmdError ShowRoutesProcessor::common_0_1_get_status(uint32_t& status, string& /* reason */) { switch (this->status()) { case SERVICE_READY: status = PROC_NULL; break; case SERVICE_STARTING: status = PROC_STARTUP; break; case SERVICE_RUNNING: status = PROC_READY; break; case SERVICE_PAUSED: /* FALLTHRU */ case SERVICE_PAUSING: /* FALLTHRU */ case SERVICE_RESUMING: status = PROC_NOT_READY; break; case SERVICE_SHUTTING_DOWN: status = PROC_SHUTDOWN; break; case SERVICE_SHUTDOWN: status = PROC_DONE; break; case SERVICE_FAILED: status = PROC_FAILED; break; case SERVICE_ALL: break; } return XrlCmdError::OKAY(); } XrlCmdError ShowRoutesProcessor::common_0_1_shutdown() { this->shutdown(); return XrlCmdError::OKAY(); } XrlCmdError ShowRoutesProcessor::finder_event_observer_0_1_xrl_target_birth( const string& /* cls */, const string& /* ins */ ) { return XrlCmdError::OKAY(); } XrlCmdError ShowRoutesProcessor::finder_event_observer_0_1_xrl_target_death( const string& cls, const string& /* ins */) { if (cls == _opts.xrl_target) this->shutdown(); return XrlCmdError::OKAY(); } XrlCmdError ShowRoutesProcessor::redist4_0_1_starting_route_dump(const string& cookie) { _cookie = cookie; return XrlCmdError::OKAY(); } XrlCmdError ShowRoutesProcessor::redist4_0_1_finishing_route_dump(const string& cookie) { check_cookie(cookie); this->shutdown(); return XrlCmdError::OKAY(); } XrlCmdError ShowRoutesProcessor::redist4_0_1_add_route(const IPv4Net& dst, const IPv4& nexthop, const string& ifname, const string& vifname, const uint32_t& metric, const uint32_t& admin_distance, const string& cookie, const string& protocol_origin) { if (this->status() != SERVICE_RUNNING || check_cookie(cookie) == false) { return XrlCmdError::OKAY(); } switch (this->_opts.print_style) { case PRINT_STYLE_BRIEF: display_route_brief(dst, nexthop, ifname, vifname, metric, admin_distance, protocol_origin); break; case PRINT_STYLE_DETAIL: display_route_detail(dst, nexthop, ifname, vifname, metric, admin_distance, protocol_origin); break; case PRINT_STYLE_TERSE: display_route_terse(dst, nexthop, ifname, vifname, metric, admin_distance, protocol_origin); break; } return XrlCmdError::OKAY(); } XrlCmdError ShowRoutesProcessor::redist4_0_1_delete_route(const IPv4Net& dst, const IPv4& nexthop, const string& ifname, const string& vifname, const uint32_t& metric, const uint32_t& admin_distance, const string& cookie, const string& protocol_origin) { // TODO: XXX: For now we ignore deletions that occur during route dump UNUSED(dst); UNUSED(nexthop); UNUSED(ifname); UNUSED(vifname); UNUSED(metric); UNUSED(admin_distance); UNUSED(cookie); UNUSED(protocol_origin); return XrlCmdError::OKAY(); } XrlCmdError ShowRoutesProcessor::redist6_0_1_starting_route_dump(const string& cookie) { check_cookie(cookie); return XrlCmdError::OKAY(); } XrlCmdError ShowRoutesProcessor::redist6_0_1_finishing_route_dump(const string& cookie) { check_cookie(cookie); this->shutdown(); return XrlCmdError::OKAY(); } XrlCmdError ShowRoutesProcessor::redist6_0_1_add_route(const IPv6Net& dst, const IPv6& nexthop, const string& ifname, const string& vifname, const uint32_t& metric, const uint32_t& admin_distance, const string& cookie, const string& protocol_origin) { if (this->status() != SERVICE_RUNNING || check_cookie(cookie) == false) { return XrlCmdError::OKAY(); } switch (this->_opts.print_style) { case PRINT_STYLE_BRIEF: display_route_brief(dst, nexthop, ifname, vifname, metric, admin_distance, protocol_origin); break; case PRINT_STYLE_DETAIL: display_route_detail(dst, nexthop, ifname, vifname, metric, admin_distance, protocol_origin); break; case PRINT_STYLE_TERSE: display_route_terse(dst, nexthop, ifname, vifname, metric, admin_distance, protocol_origin); break; } return XrlCmdError::OKAY(); } XrlCmdError ShowRoutesProcessor::redist6_0_1_delete_route(const IPv6Net& dst, const IPv6& nexthop, const string& ifname, const string& vifname, const uint32_t& metric, const uint32_t& admin_distance, const string& cookie, const string& protocol_origin) { // TODO: XXX: For now we ignore deletions that occur during route dump UNUSED(dst); UNUSED(nexthop); UNUSED(ifname); UNUSED(vifname); UNUSED(metric); UNUSED(admin_distance); UNUSED(cookie); UNUSED(protocol_origin); return XrlCmdError::OKAY(); } // ---------------------------------------------------------------------------- // Utility methods static void usage() { fprintf(stderr, "Usage: %s [options] (ribin|ribout) (ipv4|ipv6) (unicast|multicast) \n", xlog_process_name()); fprintf(stderr, "Options:\n"); fprintf(stderr, "\t -F : " "Specify Finder host and port to use.\n"); fprintf(stderr, "\t -T " "Specify XrlTarget to query.\n\n"); fprintf(stderr, "\t -b " "Brief output.\n"); fprintf(stderr, "\t -d " "Detailed output.\n"); fprintf(stderr, "\t -t " "Terse output.\n"); exit(1); } static bool parse_finder_args(const string& host_colon_port, string& host, uint16_t& port) { string::size_type sp = host_colon_port.find(":"); if (sp == string::npos) { host = host_colon_port; // Do not set port, by design it has default finder port value. } else { host = string(host_colon_port, 0, sp); string s_port = string(host_colon_port, sp + 1, 14); uint32_t t_port = atoi(s_port.c_str()); if (t_port == 0 || t_port > 65535) { XLOG_ERROR("Finder port %u is not in range 1--65535.\n", XORP_UINT_CAST(t_port)); return false; } port = (uint16_t)t_port; } return true; } static int find_option(const char* s, const char* opts[], size_t n_opts) { for (size_t i = 0; i < n_opts; i++) { if (strcmp(s, opts[i]) == 0) { return (int)i; } } fprintf(stderr, "Invalid option \"%s\", expected one of:", s); for (size_t i = 0; i < n_opts; i++) { fprintf(stderr, "%s\"%s\"", (i == 0) ? " " : ", ", opts[i]); } fprintf(stderr, "\n"); return -1; } static inline bool match_binary_option(const char* input, const char* option1, const char* option2, bool& matches_first) { const char* argv[2] = { option1, option2 }; int i = find_option(input, argv, 2); if (i < 0) return false; matches_first = (i == 0) ? true : false; return true; } // ---------------------------------------------------------------------------- // Main int main(int argc, char* const argv[]) { // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); try { ShowRoutesOptions sr_opts; sr_opts.finder_host = FinderConstants::FINDER_DEFAULT_HOST().str(); sr_opts.finder_port = FinderConstants::FINDER_DEFAULT_PORT(); sr_opts.xrl_target = "rib"; int ch; while ((ch = getopt(argc, argv, "bdtF:T:")) != -1) { switch (ch) { case 'b': sr_opts.print_style = PRINT_STYLE_BRIEF; break; case 'd': sr_opts.print_style = PRINT_STYLE_DETAIL; break; case 't': sr_opts.print_style = PRINT_STYLE_TERSE; break; case 'F': if (!parse_finder_args(optarg, sr_opts.finder_host, sr_opts.finder_port)) usage(); break; case 'T': sr_opts.xrl_target = optarg; break; default: usage(); } } argc -= optind; argv += optind; if (argc != 4) { usage(); } if (match_binary_option(argv[0], "ribin", "ribout", sr_opts.ribin) == false) { usage(); } if (match_binary_option(argv[1], "ipv4", "ipv6", sr_opts.ipv4) == false) { usage(); } if (match_binary_option(argv[2], "unicast", "multicast", sr_opts.unicast) == false) { usage(); } sr_opts.protocol = argv[3]; EventLoop e; ShowRoutesProcessor srp(e, sr_opts); srp.startup(); // // Print the headers // switch (sr_opts.print_style) { case PRINT_STYLE_BRIEF: // XXX: no header break; case PRINT_STYLE_DETAIL: // XXX: no header break; case PRINT_STYLE_TERSE: cout << "Destination P Prf Metric 1 Next hop "; // XXX: Will we have all this info in the RIB - Metric2, AS path? // cout << "A Destination P Prf Metric 1 Metric 2 " // "Next hop AS path"; cout << endl; break; } while ((srp.status() != SERVICE_FAILED) && (srp.status() != SERVICE_SHUTDOWN)) { e.run(); } if (srp.status() == SERVICE_FAILED) { srp.shutdown(); if (srp.status_note().empty() == false) { cout << srp.status_note() << endl; } else { cout << "Failed" << endl; } cout << endl; } } catch (...) { xorp_print_standard_exceptions(); } // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); return 0; } xorp/rib/rt_tab_extint.cc0000664000076400007640000005756411540224234015627 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "rib_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "rib.hh" #include "rt_tab_extint.hh" template inline static string make_extint_name(const RouteTable* e, const RouteTable* i) { return string("Ext:(" + e->tablename() + ")Int:(" + i->tablename() + ")"); } template ExtIntTable::ExtIntTable(RouteTable* ext_table, RouteTable* int_table) : RouteTable(make_extint_name(ext_table, int_table)), _ext_table(ext_table), _int_table(int_table) { _ext_table->set_next_table(this); _int_table->set_next_table(this); debug_msg("New ExtInt: %s\n", this->tablename().c_str()); } template int ExtIntTable::add_route(const IPRouteEntry& route, RouteTable* caller) { debug_msg("EIT[%s]: Adding route %s\n", this->tablename().c_str(), route.str().c_str()); if (caller == _int_table) { // The new route comes from the IGP table debug_msg("route comes from IGP\n"); const IPRouteEntry* found_egp; const IPRouteEntry* found_resolved; if (route.nexthop()->type() == EXTERNAL_NEXTHOP) { // An IGP route must have a local nexthop. XLOG_ERROR("Received route from IGP that contains " "a non-local nexthop: %s", route.str().c_str()); return XORP_ERROR; } found_egp = lookup_route_in_egp_parent(route.net()); if (found_egp != NULL) { if (found_egp->admin_distance() < route.admin_distance()) { // The admin distance of the existing EGP route is better return XORP_ERROR; } } found_resolved = lookup_in_resolved_table(route.net()); if (found_resolved != NULL) { if (found_resolved->admin_distance() < route.admin_distance()) { // The admin distance of the existing route is better return XORP_ERROR; } } // // If necessary, from delete the route that came from the Ext table // do { if (found_resolved != NULL) { bool is_delete_propagated = false; this->delete_ext_route(found_resolved, is_delete_propagated); break; } if (found_egp == NULL) break; // // If the nexthop of the route from the Ext table was directly // connected, then it was propagated when the route was added, // so delete it first. // IPNextHop* rt_nexthop; rt_nexthop = reinterpret_cast* >(found_egp->nexthop()); const A& nexthop_addr = rt_nexthop->addr(); const IPRouteEntry* nexthop_route; nexthop_route = lookup_route_in_igp_parent(nexthop_addr); if (nexthop_route != NULL) { RibVif* vif = nexthop_route->vif(); if ((vif != NULL) && (vif->is_same_subnet(IPvXNet(nexthop_route->net())) || vif->is_same_p2p(IPvX(nexthop_addr)))) { if (this->next_table() != NULL) this->next_table()->delete_route(found_egp, this); } } break; } while (false); if (this->next_table() != NULL) this->next_table()->add_route(route, this); // Does this cause any previously resolved nexthops to resolve // differently? recalculate_nexthops(route); // Does this new route cause any unresolved nexthops to be resolved? resolve_unresolved_nexthops(route); return XORP_OK; } else if (caller == _ext_table) { // The new route comes from the EGP table debug_msg("route comes from EGP\n"); IPNextHop* rt_nexthop; const IPRouteEntry* found = lookup_route_in_igp_parent(route.net()); if (found != NULL) { if (found->admin_distance() < route.admin_distance()) { // The admin distance of the existing IGP route is better return XORP_ERROR; } } rt_nexthop = reinterpret_cast* >(route.nexthop()); A nexthop_addr = rt_nexthop->addr(); const IPRouteEntry* nexthop_route; nexthop_route = lookup_route_in_igp_parent(nexthop_addr); if (nexthop_route == NULL) { // Store the fact that this was unresolved for later debug_msg("nexthop %s was unresolved\n", nexthop_addr.str().c_str()); UnresolvedIPRouteEntry* unresolved_route; unresolved_route = new UnresolvedIPRouteEntry(&route); _ip_unresolved_table.insert(make_pair(route.net(), unresolved_route)); typename UnresolvedRouteBackLink::iterator backlink; backlink = _ip_unresolved_nexthops.insert( make_pair(rt_nexthop->addr(), unresolved_route)); unresolved_route->set_backlink(backlink); return XORP_ERROR; } else { // The EGP route is resolvable if (found != NULL) { // Delete the IGP route that has worse admin distance if (this->next_table() != NULL) this->next_table()->delete_route(found, this); } RibVif* vif = nexthop_route->vif(); if ((vif != NULL) && (vif->is_same_subnet(IPvXNet(nexthop_route->net())) || vif->is_same_p2p(IPvX(nexthop_addr)))) { // // Despite it coming from the Ext table, the nexthop is // directly connected. Just propagate it. // debug_msg("nexthop %s was directly connected\n", nexthop_addr.str().c_str()); if (this->next_table() != NULL) this->next_table()->add_route(route, this); return XORP_OK; } else { debug_msg("nexthop resolved to \n %s\n", nexthop_route->str().c_str()); // Resolve the nexthop for non-directly connected nexthops const ResolvedIPRouteEntry* resolved_route; resolved_route = resolve_and_store_route(route, nexthop_route); if (this->next_table() != NULL) this->next_table()->add_route(*resolved_route, this); return XORP_OK; } } } else { XLOG_FATAL("ExtIntTable::add_route called from a class that " "isn't a component of this override table"); } return XORP_OK; } template const ResolvedIPRouteEntry* ExtIntTable::resolve_and_store_route(const IPRouteEntry& route, const IPRouteEntry* nexthop_route) { ResolvedIPRouteEntry* resolved_route; resolved_route = new ResolvedIPRouteEntry(route.net(), nexthop_route->vif(), nexthop_route->nexthop(), route.protocol(), route.metric(), nexthop_route, &route); resolved_route->set_admin_distance(route.admin_distance()); _ip_route_table.insert(resolved_route->net(), resolved_route); if (_resolving_routes.lookup_node(nexthop_route->net()) == _resolving_routes.end()) { _resolving_routes.insert(nexthop_route->net(), nexthop_route); } typename ResolvedRouteBackLink::iterator backlink; backlink = _ip_igp_parents.insert(make_pair(nexthop_route, resolved_route)); resolved_route->set_backlink(backlink); return resolved_route; } template int ExtIntTable::delete_route(const IPRouteEntry* route, RouteTable* caller) { debug_msg("ExtIntTable::delete_route %s\n", route->str().c_str()); if (caller == _int_table) { debug_msg(" called from _int_table\n"); const IPRouteEntry* egp_parent; if (route->nexthop()->type() == EXTERNAL_NEXTHOP) { // We didn't propagate the add_route, so also ignore the deletion return XORP_ERROR; } const IPRouteEntry* found_egp_route; found_egp_route = lookup_route_in_egp_parent(route->net()); if (found_egp_route != NULL) { if (found_egp_route->admin_distance() < route->admin_distance()) { // The admin distance of the existing EGP route is better return XORP_ERROR; } } const ResolvedIPRouteEntry* found; found = lookup_by_igp_parent(route); if (found != NULL) { _resolving_routes.erase(route->net()); } while (found != NULL) { debug_msg("found route using this nexthop:\n %s\n", found->str().c_str()); // Erase from table first to prevent lookups on this entry _ip_route_table.erase(found->net()); _ip_igp_parents.erase(found->backlink()); // Propagate the delete next if (this->next_table() != NULL) this->next_table()->delete_route(found, this); // Now delete the local resolved copy, and reinstantiate it egp_parent = found->egp_parent(); delete found; add_route(*egp_parent, _ext_table); found = lookup_by_igp_parent(route); } // Propagate the original delete this->next_table()->delete_route(route, this); // It is possible the internal route had masked an external one. const IPRouteEntry* masked_route; masked_route = _ext_table->lookup_route(route->net()); if (masked_route != NULL) add_route(*masked_route, _ext_table); } else if (caller == _ext_table) { debug_msg(" called from _ext_table\n"); const IPRouteEntry* found_igp_route; found_igp_route = lookup_route_in_igp_parent(route->net()); if (found_igp_route != NULL) { if (found_igp_route->admin_distance() < route->admin_distance()) { // The admin distance of the existing IGP route is better return XORP_ERROR; } } bool is_delete_propagated = false; this->delete_ext_route(route, is_delete_propagated); if (is_delete_propagated) { // It is possible the external route had masked an internal one. const IPRouteEntry* masked_route; masked_route = _int_table->lookup_route(route->net()); if (masked_route != NULL) add_route(*masked_route, _int_table); } } else { XLOG_FATAL("ExtIntTable::delete_route called from a class that " "isn't a component of this override table\n"); } return XORP_OK; } template int ExtIntTable::delete_ext_route(const IPRouteEntry* route, bool& is_delete_propagated) { const ResolvedIPRouteEntry* found; is_delete_propagated = false; found = lookup_in_resolved_table(route->net()); if (found != NULL) { // Erase from table first to prevent lookups on this entry _ip_route_table.erase(found->net()); _ip_igp_parents.erase(found->backlink()); // Delete the route's IGP parent from _resolving_routes if // no-one is using it anymore if (lookup_by_igp_parent(found->igp_parent()) == NULL) { _resolving_routes.erase(found->igp_parent()->net()); } // Propagate the delete next if (this->next_table() != NULL) { this->next_table()->delete_route(found, this); is_delete_propagated = true; } // Now delete the locally modified copy delete found; } else { // Propagate the delete only if the route wasn't found in // the unresolved nexthops table. if (delete_unresolved_nexthop(route) == false) { if (this->next_table() != NULL) { this->next_table()->delete_route(route, this); is_delete_propagated = true; } } } return XORP_OK; } template const ResolvedIPRouteEntry* ExtIntTable::lookup_in_resolved_table(const IPNet& net) { debug_msg("------------------\nlookup_route in resolved table %s\n", this->tablename().c_str()); typename Trie* >::iterator iter; iter = _ip_route_table.lookup_node(net); if (iter == _ip_route_table.end()) return NULL; else return iter.payload(); } template void ExtIntTable::resolve_unresolved_nexthops(const IPRouteEntry& nexthop_route) { typename multimap* >::iterator rpair, nextpair; A unresolved_nexthop, new_subnet; size_t prefix_len = nexthop_route.net().prefix_len(); new_subnet = nexthop_route.net().masked_addr(); // _ipv4_unresolved_nexthops is ordered by address. Consequently, // lower_bound on the subnet base address will efficiently give us // the first matching address rpair = _ip_unresolved_nexthops.lower_bound(new_subnet); while (rpair != _ip_unresolved_nexthops.end()) { unresolved_nexthop = rpair->first; if (new_subnet == unresolved_nexthop.mask_by_prefix_len(prefix_len)) { // The unresolved nexthop matches our subnet UnresolvedIPRouteEntry* unresolved_entry = rpair->second; const IPRouteEntry* unresolved_route = unresolved_entry->route(); debug_msg("resolve_unresolved_nexthops: resolving %s\n", unresolved_route->str().c_str()); // We're going to erase rpair, so preserve the state. nextpair = rpair; ++nextpair; // Remove it from the unresolved table _ip_unresolved_nexthops.erase(rpair); _ip_unresolved_table.erase(unresolved_route->net()); delete unresolved_entry; // Reinstantiate the resolved route add_route(*unresolved_route, _ext_table); rpair = nextpair; } else { if (new_subnet < unresolved_nexthop.mask_by_prefix_len(prefix_len)) { // We've gone past any routes that we might possibly resolve return; } ++rpair; } } } template bool ExtIntTable::delete_unresolved_nexthop(const IPRouteEntry* route) { debug_msg("delete_unresolved_nexthop %s\n", route->str().c_str()); typename map, UnresolvedIPRouteEntry* >::iterator iter; iter = _ip_unresolved_table.find(route->net()); if (iter == _ip_unresolved_table.end()) return false; UnresolvedIPRouteEntry* unresolved_entry = iter->second; _ip_unresolved_table.erase(iter); _ip_unresolved_nexthops.erase(unresolved_entry->backlink()); delete unresolved_entry; return true; } template const ResolvedIPRouteEntry* ExtIntTable::lookup_by_igp_parent(const IPRouteEntry* route) { debug_msg("lookup_by_igp_parent %p -> %s\n", route, route->net().str().c_str()); typename ResolvedRouteBackLink::iterator iter; iter = _ip_igp_parents.find(route); if (iter == _ip_igp_parents.end()) { debug_msg("Found no routes with this IGP parent\n"); return NULL; } else { debug_msg("Found route with IGP parent %p:\n %s\n", route, (iter->second)->str().c_str()); return iter->second; } } template const ResolvedIPRouteEntry* ExtIntTable::lookup_next_by_igp_parent(const IPRouteEntry* route, const ResolvedIPRouteEntry* previous) { debug_msg("lookup_next_by_igp_parent %p -> %s\n", route, route->net().str().c_str()); // // TODO: if we have a large number of routes with the same IGP parent, // this can be very inefficient. // typename ResolvedRouteBackLink::iterator iter; iter = _ip_igp_parents.find(route); while (iter != _ip_igp_parents.end() && iter->first == route && iter->second != previous) { ++iter; } if (iter == _ip_igp_parents.end() || iter->first != route) { debug_msg("Found no more routes with this IGP parent\n"); return NULL; } ++iter; if (iter == _ip_igp_parents.end() || iter->first != route) { debug_msg("Found no more routes with this IGP parent\n"); return NULL; } debug_msg("Found next route with IGP parent %p:\n %s\n", route, (iter->second)->str().c_str()); return iter->second; } template void ExtIntTable::recalculate_nexthops(const IPRouteEntry& new_route) { debug_msg("recalculate_nexthops: %s\n", new_route.str().c_str()); const IPRouteEntry* old_route; typename Trie* >::iterator iter; iter = _resolving_routes.find_less_specific(new_route.net()); if (iter == _resolving_routes.end()) { debug_msg("no old route\n"); return; } old_route = iter.payload(); debug_msg("old route was: %s\n", old_route->str().c_str()); const ResolvedIPRouteEntry* found; const ResolvedIPRouteEntry* last_not_deleted = NULL; const IPRouteEntry* egp_parent; found = lookup_by_igp_parent(old_route); while (found != NULL) { egp_parent = found->egp_parent(); XLOG_ASSERT(egp_parent->nexthop()->type() != DISCARD_NEXTHOP); XLOG_ASSERT(egp_parent->nexthop()->type() != UNREACHABLE_NEXTHOP); A nexthop = (reinterpret_cast* >(egp_parent->nexthop()))->addr(); if (new_route.net().contains(nexthop)) { debug_msg("found route using this nexthop:\n %s\n", found->str().c_str()); // Erase from table first to prevent lookups on this entry _ip_route_table.erase(found->net()); _ip_igp_parents.erase(found->backlink()); // Delete the route's IGP parent from _resolving_routes if // no-one's using it anymore if (lookup_by_igp_parent(found->igp_parent()) == NULL) { _resolving_routes.erase(found->igp_parent()->net()); } // Propagate the delete next if (this->next_table() != NULL) this->next_table()->delete_route(found, this); // Now delete the local resolved copy, and reinstantiate it delete found; add_route(*egp_parent, _ext_table); } else { debug_msg("route matched but nexthop didn't: nexthop: %s\n %s\n", nexthop.str().c_str(), found->str().c_str()); last_not_deleted = found; } if (last_not_deleted == NULL) { found = lookup_by_igp_parent(old_route); } else { found = lookup_next_by_igp_parent(old_route, last_not_deleted); } } debug_msg("done recalculating nexthops\n------------------------------------------------\n"); } template const IPRouteEntry* ExtIntTable::lookup_route(const IPNet& ipv4net) const { const IPRouteEntry* int_found; const IPRouteEntry* ext_found; // First try our local version debug_msg("------------------\nlookup_route in resolved table %s\n", this->tablename().c_str()); typename Trie* >::iterator iter; iter = _ip_route_table.lookup_node(ipv4net); if (iter != _ip_route_table.end()) { return iter.payload(); } debug_msg("Not found in resolved table\n"); #ifdef DEBUG_LOGGING _ip_route_table.print(); #endif // Local version failed, so try the parent tables. int_found = lookup_route_in_igp_parent(ipv4net); ext_found = _ext_table->lookup_route(ipv4net); if (ext_found == NULL) { return int_found; } if (int_found == NULL) { return ext_found; } if (int_found->admin_distance() <= ext_found->admin_distance()) { return int_found; } else { return ext_found; } } template const IPRouteEntry* ExtIntTable::lookup_route(const A& addr) const { const IPRouteEntry* ext_found = NULL; const IPRouteEntry* int_found; list* > found; debug_msg("ExtIntTable::lookup_route\n"); // Lookup locally, and in both internal and external tables typename Trie* >::iterator trie_iter; trie_iter = _ip_route_table.find(addr); if (trie_iter != _ip_route_table.end()) { found.push_back(trie_iter.payload()); } int_found = lookup_route_in_igp_parent(addr); if (int_found != NULL) found.push_back(int_found); ext_found = _ext_table->lookup_route(addr); // Check that the external route has a local nexthop (if it doesn't // we expect the version in local_found to have a local nexthop) if (ext_found != NULL && ext_found->nexthop()->type() == EXTERNAL_NEXTHOP) { ext_found = NULL; } if (ext_found != NULL) found.push_back(ext_found); if (found.empty()) return NULL; // Retain only the routes with the longest prefix length uint32_t longest_prefix_len = 0; typename list* >::iterator iter, iter2; for (iter = found.begin(); iter != found.end(); ++iter) { if ((*iter)->net().prefix_len() > longest_prefix_len) { longest_prefix_len = (*iter)->net().prefix_len(); } } for (iter = found.begin(); iter != found.end(); ) { iter2 = iter; ++iter2; if ((*iter)->net().prefix_len() < longest_prefix_len) { found.erase(iter); } iter = iter2; } if (found.size() == 1) { return found.front(); } // Retain only the routes with the lowest admin_distance uint32_t lowest_admin_distance = MAX_ADMIN_DISTANCE; for (iter = found.begin(); iter != found.end(); ++iter) { if ((*iter)->admin_distance() < lowest_admin_distance) { lowest_admin_distance = (*iter)->admin_distance(); } } for (iter = found.begin(); iter != found.end(); ) { iter2 = iter; ++iter2; if ((*iter)->admin_distance() > lowest_admin_distance) { found.erase(iter); } iter = iter2; } if (found.size() == 1) { return found.front(); } // This shouldn't happen. XLOG_ERROR("ExtIntTable has multiple routes with same prefix_len " "and same admin_distance"); return found.front(); } template const IPRouteEntry* ExtIntTable::lookup_route_in_igp_parent(const IPNet& ipnet) const { const IPRouteEntry* found; found = _int_table->lookup_route(ipnet); // Sanity check that the answer is good if (found != NULL) { if (found->nexthop()->type() == EXTERNAL_NEXTHOP) found = NULL; } return found; } template const IPRouteEntry* ExtIntTable::lookup_route_in_igp_parent(const A& addr) const { const IPRouteEntry* found; found = _int_table->lookup_route(addr); // Sanity check that the answer is good if (found != NULL) { if (found->nexthop()->type() == EXTERNAL_NEXTHOP) found = NULL; } return found; } template const IPRouteEntry* ExtIntTable::lookup_route_in_egp_parent(const IPNet& ipnet) const { const IPRouteEntry* found; found = _ext_table->lookup_route(ipnet); return found; } template const IPRouteEntry* ExtIntTable::lookup_route_in_egp_parent(const A& addr) const { const IPRouteEntry* found; found = _ext_table->lookup_route(addr); return found; } template void ExtIntTable::replumb(RouteTable* old_parent, RouteTable* new_parent) { debug_msg("ExtIntTable::replumb replacing %s with %s\n", old_parent->tablename().c_str(), new_parent->tablename().c_str()); if (_ext_table == old_parent) { _ext_table = new_parent; } else if (_int_table == old_parent) { _int_table = new_parent; } else { // Shouldn't be possible XLOG_UNREACHABLE(); } this->set_tablename(make_extint_name(_ext_table, _int_table)); debug_msg("ExtIntTable: now called \"%s\"\n", this->tablename().c_str()); } template RouteRange* ExtIntTable::lookup_route_range(const A& addr) const { // What do the parents think the answer is? RouteRange* int_rr = _int_table->lookup_route_range(addr); RouteRange* ext_rr = _ext_table->lookup_route_range(addr); // What do we think the answer is? const IPRouteEntry* route; typename Trie* >::iterator iter; iter = _ip_route_table.find(addr); if (iter == _ip_route_table.end()) route = NULL; else route = iter.payload(); A bottom_addr, top_addr; _ip_route_table.find_bounds(addr, bottom_addr, top_addr); RouteRange* rr = new RouteRange(addr, route, top_addr, bottom_addr); // // If there's a matching routing in _ip_route_table, there'll also // be one in _ext_table. But our version has the correct nexthop. // We still need to merge in the result of lookup_route_range on // _ext_table though, because the upper and lower bounds there may // be tighter than the ones we'd find ourselves. // rr->merge(int_rr); delete(int_rr); rr->merge(ext_rr); delete(ext_rr); return rr; } template string ExtIntTable::str() const { string s; s = "-------\nExtIntTable: " + this->tablename() + "\n"; s += "_ext_table = " + _ext_table->tablename() + "\n"; s += "_int_table = " + _int_table->tablename() + "\n"; if (this->next_table() == NULL) s += "no next table\n"; else s += "next table = " + this->next_table()->tablename() + "\n"; return s; } template class ExtIntTable; template class ExtIntTable; xorp/rib/rt_tab_log.hh0000664000076400007640000000747311540224234015101 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/rib/rt_tab_log.hh,v 1.10 2008/10/02 21:58:12 bms Exp $ #ifndef __RIB_RT_TAB_LOG_HH__ #define __RIB_RT_TAB_LOG_HH__ #include "libxorp/xorp.h" #include "rt_tab_base.hh" /** * @short A Base for Route Tables that log updates. * * Users of this class specify expected updates with @ref expect_add * and @ref expect_delete. As the updates come through they are * compared against those expect and generate an assertion failure if * those arriving do not match those expected. An assertion failure * will also occur if there are unmatched updates upon instance * destruction. * * This class is strictly for debugging and testing purposes. */ template class LogTable : public RouteTable { public: LogTable(const string& tablename, RouteTable* parent); ~LogTable(); int add_route(const IPRouteEntry& route, RouteTable* caller); int delete_route(const IPRouteEntry* , RouteTable* caller); const IPRouteEntry* lookup_route(const IPNet& net) const; const IPRouteEntry* lookup_route(const A& addr) const; RouteRange* lookup_route_range(const A& addr) const; TableType type() const { return LOG_TABLE; } RouteTable* parent() { return _parent; } void replumb(RouteTable* old_parent, RouteTable* new_parent); string str() const; uint32_t update_number() const; private: RouteTable* _parent; uint32_t _update_number; }; /** * @short Route Table that passes through updates whilst logging them * to an ostream instance. * * -*- UNTESTED -*- */ template class OstreamLogTable : public LogTable { public: ostream& get(); OstreamLogTable(const string& tablename, RouteTable* parent, ostream& out); int add_route(const IPRouteEntry& route, RouteTable* caller); int delete_route(const IPRouteEntry* , RouteTable* caller); string str() const; private: ostream& _o; }; /** * @short Route Table that passes that through updates whilst writing * them to the logging them via XORP's XLOG_TRACE. * * -*- UNTESTED -*- */ template class XLogTraceTable : public LogTable { public: XLogTraceTable(const string& tablename, RouteTable* parent); int add_route(const IPRouteEntry& route, RouteTable* caller); int delete_route(const IPRouteEntry* proute, RouteTable* caller); string str() const; }; /** * @short Route Table that passes that passes through updates whilst * writing them to the logging them via XORP's XLOG_TRACE. * * -*- UNTESTED -*- */ template class DebugMsgLogTable : public LogTable { public: DebugMsgLogTable(const string& tablename, RouteTable* parent); int add_route(const IPRouteEntry& route, RouteTable* caller); int delete_route(const IPRouteEntry* proute, RouteTable* caller); string str() const; }; #endif // __RIB_RT_TAB_LOG_HH__ xorp/rib/rib_varrw.cc0000664000076400007640000000450611421137511014741 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "rib_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "rib_varrw.hh" template RIBVarRW::RIBVarRW(IPRouteEntry& route) : _route(route) { } template void RIBVarRW::start_read() { initialize(_route.policytags()); read_route_nexthop(_route); ostringstream oss; oss << _route.metric(); initialize(VAR_METRIC, _ef.create(ElemU32::id, oss.str().c_str())); } template <> void RIBVarRW::read_route_nexthop(IPRouteEntry& route) { initialize(VAR_NETWORK4, _ef.create(ElemIPv4Net::id, route.net().str().c_str())); initialize(VAR_NEXTHOP4, _ef.create(ElemIPv4NextHop::id, route.nexthop_addr().str().c_str())); initialize(VAR_NETWORK6, NULL); initialize(VAR_NEXTHOP6, NULL); } template <> void RIBVarRW::read_route_nexthop(IPRouteEntry& route) { initialize(VAR_NETWORK6, _ef.create(ElemIPv6Net::id, route.net().str().c_str())); initialize(VAR_NEXTHOP6, _ef.create(ElemIPv6NextHop::id, route.nexthop_addr().str().c_str())); initialize(VAR_NETWORK4, NULL); initialize(VAR_NEXTHOP4, NULL); } template void RIBVarRW::single_write(const Id& /* id */, const Element& /* e */) { } template Element* RIBVarRW::single_read(const Id& /* id */) { XLOG_UNREACHABLE(); return 0; } template class RIBVarRW; template class RIBVarRW; xorp/rib/rt_tab_log.cc0000664000076400007640000001473311540224234015064 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "rib_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "rt_tab_log.hh" template LogTable::LogTable(const string& tablename, RouteTable* parent) : RouteTable(tablename), _update_number(0) { _parent = parent; _parent->set_next_table(this); } template LogTable::~LogTable() { } template uint32_t LogTable::update_number() const { return _update_number; } template int LogTable::add_route(const IPRouteEntry& route, RouteTable* caller) { _update_number++; RouteTable* n = this->next_table(); if (n != NULL) { return n->add_route(route, caller); } return XORP_OK; } template int LogTable::delete_route(const IPRouteEntry* route, RouteTable* caller) { RouteTable* n = this->next_table(); if (n != NULL) { return n->delete_route(route, caller); } _update_number++; return XORP_OK; } template const IPRouteEntry* LogTable::lookup_route(const IPNet& net) const { return _parent->lookup_route(net); } template const IPRouteEntry* LogTable::lookup_route(const A& addr) const { return _parent->lookup_route(addr); } template void LogTable::replumb(RouteTable* old_parent, RouteTable* new_parent) { XLOG_ASSERT(_parent == old_parent); _parent = new_parent; // XXX _parent->set_next_table??? } template RouteRange* LogTable::lookup_route_range(const A& addr) const { return _parent->lookup_route_range(addr); } template string LogTable::str() const { string s; s = "-------\nLogTable: " + this->tablename() + "\n"; s += "parent = " + _parent->tablename() + "\n"; return s; } // ---------------------------------------------------------------------------- template OstreamLogTable::OstreamLogTable(const string& tablename, RouteTable* parent, ostream& out) : LogTable(tablename, parent), _o(out) { } template int OstreamLogTable::add_route(const IPRouteEntry& route, RouteTable* caller) { _o << this->update_number() << (const char*)(" Add: ") << route.str() << (const char*)(" Return: "); int s = LogTable::add_route(route, caller); _o << s << endl; return s; } template int OstreamLogTable::delete_route(const IPRouteEntry* route, RouteTable* caller) { if (route != NULL) { _o << this->update_number() << (const char*)(" Delete: ") << route->str() << (const char*)(" Return: "); } int s = LogTable::delete_route(route, caller); if (route != NULL) { _o << s << endl; } return s; } template string OstreamLogTable::str() const { return "OstreamLogTable<" + A::ip_version_str() + ">"; } template class OstreamLogTable; template class OstreamLogTable; // ---------------------------------------------------------------------------- #include "rib_module.h" #include "libxorp/xlog.h" template XLogTraceTable::XLogTraceTable(const string& tablename, RouteTable* parent) : LogTable(tablename, parent) { } template int XLogTraceTable::add_route(const IPRouteEntry& route, RouteTable* caller) { string msg = c_format("%u Add: %s Return: ", XORP_UINT_CAST(this->update_number()), route.str().c_str()); int s = LogTable::add_route(route, caller); msg += c_format("%d\n", s); XLOG_TRACE(true, "%s", msg.c_str()); return s; } template int XLogTraceTable::delete_route(const IPRouteEntry* route, RouteTable* caller) { string msg; if (route != NULL) { msg = c_format("%u Delete: %s Return: ", XORP_UINT_CAST(this->update_number()), route->str().c_str()); } int s = LogTable::delete_route(route, caller); if (route != NULL) { msg += c_format("%d\n", s); XLOG_TRACE(true, "%s", msg.c_str()); } return s; } template string XLogTraceTable::str() const { return "XLogTraceTable<" + A::ip_version_str() + ">"; } template class XLogTraceTable; template class XLogTraceTable; // ---------------------------------------------------------------------------- #ifndef DEBUG_LOGGING #define DEBUG_LOGGING #endif #include "libxorp/debug.h" template DebugMsgLogTable::DebugMsgLogTable(const string& tablename, RouteTable* parent) : LogTable(tablename, parent) { } template int DebugMsgLogTable::add_route(const IPRouteEntry& route, RouteTable* caller) { string msg = c_format("%u Add: %s Return: ", XORP_UINT_CAST(this->update_number()), route.str().c_str()); int s = LogTable::add_route(route, caller); msg += c_format("%d\n", s); debug_msg("%s", msg.c_str()); return s; } template int DebugMsgLogTable::delete_route(const IPRouteEntry* route, RouteTable* caller) { string msg; if (route != NULL) { msg = c_format("%u Delete: %s Return: ", XORP_UINT_CAST(this->update_number()), route->str().c_str()); } int s = LogTable::delete_route(route, caller); if (route != NULL) { msg += c_format("%d\n", s); debug_msg("%s", msg.c_str()); } return s; } template string DebugMsgLogTable::str() const { return "DebugMsgLogTable<" + A::ip_version_str() + ">"; } template class DebugMsgLogTable; template class DebugMsgLogTable; xorp/rib/register_server.cc0000664000076400007640000001422511613331370016157 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #include "rib_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxipc/xrl_router.hh" #include "register_server.hh" NotifyQueue::NotifyQueue(const string& module_name) : _module_name(module_name), _active(false), _response_sender(NULL) { } void NotifyQueue::add_entry(NotifyQueueEntry* e) { _queue.push_back(e); } void NotifyQueue::send_next() { XrlCompleteCB cb = callback(this, &NotifyQueue::xrl_done); _queue.front()->send(_response_sender, _module_name, cb); _queue.pop_front(); if (_queue.empty()) { _active = false; _response_sender = NULL; } } void NotifyQueue::flush(ResponseSender* response_sender) { debug_msg("NQ: flush\n"); if (_queue.empty()) { debug_msg("flush called on empty queue\n"); return; } _response_sender = response_sender; // // This isn't really the best way to do this, because when the // queue is active it won't force transaction batching, but it's // better than nothing. // if (_active) { debug_msg("queue is already active\n"); return; } _active = true; send_next(); } void NotifyQueue::xrl_done(const XrlError& e) { debug_msg("NQ: xrl_done\n"); if (e == XrlError::OKAY()) { if (!_queue.empty() && _active) send_next(); } else { XLOG_ERROR("Failed to send registration update to RIB client"); } } template <> void NotifyQueueChangedEntry::send(ResponseSender* response_sender, const string& module_name, NotifyQueue::XrlCompleteCB& cb) { response_sender->send_route_info_changed4(module_name.c_str(), _net.masked_addr(), _net.prefix_len(), _nexthop, _metric, _admin_distance, _protocol_origin.c_str(), cb); } template <> void NotifyQueueInvalidateEntry::send(ResponseSender* response_sender, const string& module_name, NotifyQueue::XrlCompleteCB& cb) { debug_msg("Sending route_info_invalid4\n"); response_sender->send_route_info_invalid4(module_name.c_str(), _net.masked_addr(), _net.prefix_len(), cb); } RegisterServer::RegisterServer(XrlRouter* xrl_router) : _response_sender(xrl_router) { } void RegisterServer::add_entry_to_queue(const string& module_name, NotifyQueueEntry* e) { debug_msg("REGSERV: add_entry_to_queue\n"); NotifyQueue* queue; map::iterator qmi; qmi = _queuemap.find(module_name); if (qmi == _queuemap.end()) { _queuemap[module_name] = new NotifyQueue(module_name); queue = _queuemap[module_name]; } else { queue = qmi->second; } queue->add_entry(e); } void RegisterServer::send_route_changed(const string& module_name, const IPv4Net& net, const IPv4& nexthop, uint32_t metric, uint32_t admin_distance, const string& protocol_origin, bool multicast) { NotifyQueueChangedEntry* q_entry; q_entry = new NotifyQueueChangedEntry(net, nexthop, metric, admin_distance, protocol_origin, multicast); add_entry_to_queue(module_name, reinterpret_cast(q_entry)); } void RegisterServer::send_invalidate(const string& module_name, const IPv4Net& net, bool multicast) { NotifyQueueInvalidateEntry* q_entry; q_entry = new NotifyQueueInvalidateEntry(net, multicast); add_entry_to_queue(module_name, reinterpret_cast(q_entry)); } void RegisterServer::flush() { debug_msg("REGSERV: flush\n"); map::iterator iter; for (iter = _queuemap.begin(); iter != _queuemap.end(); ++iter) { iter->second->flush(&_response_sender); } } /** IPv6 stuff */ #ifdef HAVE_IPV6 template <> void NotifyQueueChangedEntry::send(ResponseSender* response_sender, const string& module_name, NotifyQueue::XrlCompleteCB& cb) { response_sender->send_route_info_changed6(module_name.c_str(), _net.masked_addr(), _net.prefix_len(), _nexthop, _metric, _admin_distance, _protocol_origin.c_str(), cb); } template <> void NotifyQueueInvalidateEntry::send(ResponseSender* response_sender, const string& module_name, NotifyQueue::XrlCompleteCB& cb) { response_sender->send_route_info_invalid6(module_name.c_str(), _net.masked_addr(), _net.prefix_len(), cb); } void RegisterServer::send_route_changed(const string& module_name, const IPv6Net& net, const IPv6& nexthop, uint32_t metric, uint32_t admin_distance, const string& protocol_origin, bool multicast) { NotifyQueueChangedEntry* q_entry; q_entry = new NotifyQueueChangedEntry(net, nexthop, metric, admin_distance, protocol_origin, multicast); add_entry_to_queue(module_name, reinterpret_cast(q_entry)); } void RegisterServer::send_invalidate(const string& module_name, const IPv6Net& net, bool multicast) { NotifyQueueInvalidateEntry* q_entry; q_entry = new NotifyQueueInvalidateEntry(net, multicast); add_entry_to_queue(module_name, reinterpret_cast(q_entry)); } #endif // ipv6 xorp/rib/rib_manager.cc0000664000076400007640000004241711540225533015221 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "rib_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/utils.hh" #include "libxipc/xrl_error.hh" #include "xrl/interfaces/finder_event_notifier_xif.hh" #include "rib_manager.hh" #include "redist_xrl.hh" #include "redist_policy.hh" #include "profile_vars.hh" RibManager::RibManager(EventLoop& eventloop, XrlStdRouter& xrl_std_router, const string& fea_target) : _status_code(PROC_NOT_READY), _status_reason("Initializing"), _eventloop(eventloop), _xrl_router(xrl_std_router), _register_server(&_xrl_router), _urib4(UNICAST, *this, _eventloop), _mrib4(MULTICAST, *this, _eventloop), #ifdef HAVE_IPV6 _urib6(UNICAST, *this, _eventloop), _mrib6(MULTICAST, *this, _eventloop), #endif _vif_manager(_xrl_router, _eventloop, this, fea_target), _xrl_rib_target(&_xrl_router, _urib4, _mrib4, #ifdef HAVE_IPV6 _urib6, _mrib6, #endif _vif_manager, this), _fea_target(fea_target) { _urib4.initialize(_register_server); _mrib4.initialize(_register_server); #ifdef HAVE_IPV6 _urib6.initialize(_register_server); _mrib6.initialize(_register_server); #endif PeriodicTimerCallback cb = callback(this, &RibManager::status_updater); _status_update_timer = _eventloop.new_periodic_ms(1000, cb); #ifndef XORP_DISABLE_PROFILE initialize_profiling_variables(_profile); #endif } RibManager::~RibManager() { stop(); } int RibManager::start() { if (ProtoState::start() != XORP_OK) return (XORP_ERROR); _vif_manager.start(); return (XORP_OK); } int RibManager::stop() { if (! is_up()) return (XORP_ERROR); _vif_manager.stop(); ProtoState::stop(); _status_code = PROC_SHUTDOWN; _status_reason = "Shutting down"; status_updater(); return (XORP_OK); } bool RibManager::status_updater() { ProcessStatus s = PROC_READY; string reason = "Ready"; bool ret = true; // // Check the VifManager's status // ServiceStatus vif_mgr_status = _vif_manager.status(); switch (vif_mgr_status) { case SERVICE_READY: break; case SERVICE_STARTING: s = PROC_NOT_READY; reason = "VifManager starting"; break; case SERVICE_RUNNING: break; case SERVICE_PAUSING: s = PROC_NOT_READY; reason = "VifManager pausing"; break; case SERVICE_PAUSED: s = PROC_NOT_READY; reason = "VifManager paused"; break; case SERVICE_RESUMING: s = PROC_NOT_READY; reason = "VifManager resuming"; break; case SERVICE_SHUTTING_DOWN: s = PROC_SHUTDOWN; reason = "VifManager shutting down"; break; case SERVICE_SHUTDOWN: s = PROC_DONE; reason = "VifManager Shutdown"; break; case SERVICE_FAILED: // VifManager failed: set process state to failed. // TODO: XXX: Should we exit here, or wait to be restarted? s = PROC_FAILED; reason = "VifManager Failed"; ret = false; break; case SERVICE_ALL: XLOG_UNREACHABLE(); break; } // // PROC_SHUTDOWN and PROC_DOWN states are persistent unless // there's a failure. // _status_code = s; _status_reason = reason; return ret; } template static int add_rib_vif(RIB& rib, const string& vifname, const Vif& vif, string& err) { int result = rib.new_vif(vifname, vif); if (result != XORP_OK) { if (err.size() == 0) { err = c_format("Failed to add VIF \"%s\" to %s", vifname.c_str(), rib.name().c_str()); } else { err = c_format(", and failed to add VIF \"%s\" to %s", vifname.c_str(), rib.name().c_str()); } } return result; } int RibManager::new_vif(const string& vifname, const Vif& vif, string& err) { err.resize(0); return (add_rib_vif(_urib4, vifname, vif, err) | add_rib_vif(_mrib4, vifname, vif, err) #ifdef HAVE_IPV6 | add_rib_vif(_urib6, vifname, vif, err) | add_rib_vif(_mrib6, vifname, vif, err) #endif ); } template static int delete_rib_vif(RIB& rib, const string& vifname, string& err) { int result = rib.delete_vif(vifname); if (result != XORP_OK) { if (err.empty()) { err = c_format("Failed to delete VIF \"%s\" from %s", vifname.c_str(), rib.name().c_str()); } else { err += c_format(", and failed to delete VIF \"%s\" from %s", vifname.c_str(), rib.name().c_str()); } } return result; } int RibManager::delete_vif(const string& vifname, string& err) { err.resize(0); return (delete_rib_vif(_urib4, vifname, err) | delete_rib_vif(_mrib4, vifname, err) #ifdef HAVE_IPV6 | delete_rib_vif(_urib6, vifname, err) | delete_rib_vif(_mrib6, vifname, err) #endif ); } template static int set_rib_vif_flags(RIB& rib, const string& vifname, bool is_p2p, bool is_loopback, bool is_multicast, bool is_broadcast, bool is_up, uint32_t mtu, string& err) { int result = rib.set_vif_flags(vifname, is_p2p, is_loopback, is_multicast, is_broadcast, is_up, mtu); if (result != XORP_OK) { err = c_format("Failed to add flags for VIF \"%s\" to %s", vifname.c_str(), rib.name().c_str()); } return result; } int RibManager::set_vif_flags(const string& vifname, bool is_p2p, bool is_loopback, bool is_multicast, bool is_broadcast, bool is_up, uint32_t mtu, string& err) { if (set_rib_vif_flags(_urib4, vifname, is_p2p, is_loopback, is_multicast, is_broadcast, is_up, mtu, err) != XORP_OK || set_rib_vif_flags(_mrib4, vifname, is_p2p, is_loopback, is_multicast, is_broadcast, is_up, mtu, err) != XORP_OK #ifdef HAVE_IPV6 || set_rib_vif_flags(_urib6, vifname, is_p2p, is_loopback, is_multicast, is_broadcast, is_up, mtu, err) != XORP_OK || set_rib_vif_flags(_mrib6, vifname, is_up, is_loopback, is_multicast, is_broadcast, is_up, mtu, err) != XORP_OK #endif ) { return XORP_ERROR; } return XORP_OK; } template int add_vif_address_to_ribs(RIB& urib, RIB& mrib, const string& vifn, const A& addr, const IPNet& subnet, const A& broadcast_addr, const A& peer_addr, string& err) { RIB* ribs[2] = { &urib, &mrib }; for (uint32_t i = 0; i < sizeof(ribs)/sizeof(ribs[0]); i++) { if (ribs[i]->add_vif_address(vifn, addr, subnet, broadcast_addr, peer_addr) != XORP_OK) { err = c_format("Failed to add VIF address %s to %s\n", addr.str().c_str(), ribs[i]->name().c_str()); return XORP_ERROR; } } return XORP_OK; } template int delete_vif_address_from_ribs(RIB& urib, RIB& mrib, const string& vifn, const A& addr, string& err) { RIB* ribs[2] = { &urib, &mrib }; for (uint32_t i = 0; i < sizeof(ribs)/sizeof(ribs[0]); i++) { if (ribs[i]->delete_vif_address(vifn, addr) != XORP_OK) { err = c_format("Failed to delete VIF address %s from %s\n", addr.str().c_str(), ribs[i]->name().c_str()); return XORP_ERROR; } } return XORP_OK; } int RibManager::add_vif_address(const string& vifn, const IPv4& addr, const IPv4Net& subnet, const IPv4& broadcast_addr, const IPv4& peer_addr, string& err) { return add_vif_address_to_ribs(_urib4, _mrib4, vifn, addr, subnet, broadcast_addr, peer_addr, err); } int RibManager::delete_vif_address(const string& vifn, const IPv4& addr, string& err) { return delete_vif_address_from_ribs(_urib4, _mrib4, vifn, addr, err); } void RibManager::make_errors_fatal() { _urib4.set_errors_are_fatal(); _mrib4.set_errors_are_fatal(); #ifdef HAVE_IPV6 _urib6.set_errors_are_fatal(); _mrib6.set_errors_are_fatal(); #endif } void RibManager::register_interest_in_target(const string& target_class) { if (_targets_of_interest.find(target_class) == _targets_of_interest.end()) { _targets_of_interest.insert(target_class); XrlFinderEventNotifierV0p1Client finder(&_xrl_router); XrlFinderEventNotifierV0p1Client::RegisterClassEventInterestCB cb = callback(this, &RibManager::register_interest_in_target_done); finder.send_register_class_event_interest("finder", _xrl_router.instance_name(), target_class, cb); } } void RibManager::register_interest_in_target_done(const XrlError& e) { if (e != XrlError::OKAY()) { XLOG_ERROR("Failed to register interest in an XRL target\n"); } } void RibManager::deregister_interest_in_target(const string& target_class) { if (_targets_of_interest.find(target_class) != _targets_of_interest.end()) { _targets_of_interest.erase(target_class); XrlFinderEventNotifierV0p1Client finder(&_xrl_router); XrlFinderEventNotifierV0p1Client::RegisterClassEventInterestCB cb = callback(this, &RibManager::deregister_interest_in_target_done); finder.send_deregister_class_event_interest("finder", _xrl_router.instance_name(), target_class, cb); } } void RibManager::deregister_interest_in_target_done(const XrlError& e) { if (e != XrlError::OKAY()) { XLOG_ERROR("Failed to deregister interest in an XRL target\n"); } } void RibManager::target_death(const string& target_class, const string& target_instance) { if (target_class == "fea") { // No cleanup - we just exit. XLOG_ERROR("FEA died, so RIB is exiting too\n"); exit(0); } // Deregister interest in the target deregister_interest_in_target(target_class); // Inform the RIBs in case this was a routing protocol that died. _urib4.target_death(target_class, target_instance); _mrib4.target_death(target_class, target_instance); #ifdef HAVE_IPV6 _urib6.target_death(target_class, target_instance); _mrib6.target_death(target_class, target_instance); #endif } static inline string make_redist_name(const string& xrl_target, const string& cookie, bool is_xrl_transaction_output) { string redist_name = xrl_target + ":" + cookie; if (is_xrl_transaction_output) redist_name += " (transaction)"; else redist_name += " (no transaction)"; return redist_name; } template static int redist_enable_xrl_output(EventLoop& eventloop, XrlRouter& rtr, Profile& profile, RIB& rib, const string& to_xrl_target, const string& proto, const IPNet& network_prefix, const string& cookie, bool is_xrl_transaction_output) { string protocol(proto); RedistPolicy* redist_policy = 0; if (protocol.find("all-") == 0) { // XXX Voodoo magic, user requests a name like all- // then we attach xrl output to the redist output table (known // internally by the name "all") and set the redist_policy filter // to correspond with the protocol protocol = "all"; string sub = proto.substr(4, string::npos); if (sub != "all") { Protocol* p = rib.find_protocol(sub); if (p != 0) { redist_policy = new IsOfProtocol(*p); } else { return XORP_ERROR; } } } RedistTable* rt = rib.protocol_redist_table(protocol); if (rt == 0) { delete redist_policy; return XORP_ERROR; } string redist_name = make_redist_name(to_xrl_target, cookie, is_xrl_transaction_output); if (rt->redistributor(redist_name) != 0) { delete redist_policy; return XORP_ERROR; } Redistributor* redist = new Redistributor(eventloop, redist_name); redist->set_redist_table(rt); if (is_xrl_transaction_output) { redist->set_output( new RedistTransactionXrlOutput(redist, rtr, profile, protocol, to_xrl_target, network_prefix, cookie) ); } else { redist->set_output(new RedistXrlOutput(redist, rtr, profile, protocol, to_xrl_target, network_prefix, cookie)); } redist->set_policy(redist_policy); return XORP_OK; } template static int redist_disable_xrl_output(RIB& rib, const string& to_xrl_target, const string& proto, const string& cookie, bool is_xrl_transaction_output) { string protocol(proto); if (protocol.find("ribout-") == 0) { // XXX Voodoo magic, user requests a name like ribout- // then we attach xrl output to the redist output table (known // internally by the name "all" protocol = "all"; } RedistTable* rt = rib.protocol_redist_table(protocol); if (rt == 0) return XORP_ERROR; string redist_name = make_redist_name(to_xrl_target, cookie, is_xrl_transaction_output); Redistributor* redist = rt->redistributor(redist_name); if (redist == 0) return XORP_ERROR; rt->remove_redistributor(redist); delete redist; return XORP_OK; } int RibManager::add_redist_xrl_output4(const string& to_xrl_target, const string& from_protocol, bool unicast, bool multicast, const IPv4Net& network_prefix, const string& cookie, bool is_xrl_transaction_output) { if (unicast) { int e = redist_enable_xrl_output(_eventloop, _xrl_router, _profile, _urib4, to_xrl_target, from_protocol, network_prefix, cookie, is_xrl_transaction_output); if (e != XORP_OK) { return e; } } if (multicast) { int e = redist_enable_xrl_output(_eventloop, _xrl_router, _profile, _mrib4, to_xrl_target, from_protocol, network_prefix, cookie, is_xrl_transaction_output); if (e != XORP_OK && unicast) { redist_disable_xrl_output(_urib4, to_xrl_target, from_protocol, cookie, is_xrl_transaction_output); } return e; } return XORP_OK; } int RibManager::delete_redist_xrl_output4(const string& to_xrl_target, const string& from_protocol, bool unicast, bool multicast, const string& cookie, bool is_xrl_transaction_output) { if (unicast) redist_disable_xrl_output(_urib4, to_xrl_target, from_protocol, cookie, is_xrl_transaction_output); if (multicast) redist_disable_xrl_output(_mrib4, to_xrl_target, from_protocol, cookie, is_xrl_transaction_output); return XORP_OK; } void RibManager::push_routes() { _urib4.push_routes(); _mrib4.push_routes(); #ifdef HAVE_IPV6 _urib6.push_routes(); _mrib6.push_routes(); #endif } void RibManager::configure_filter(const uint32_t& filter, const string& conf) { _policy_filters.configure(filter, conf); } void RibManager::reset_filter(const uint32_t& filter) { _policy_filters.reset(filter); } void RibManager::insert_policy_redist_tags(const string& protocol, const PolicyTags& tags) { _policy_redist_map.insert(protocol, tags); } void RibManager::reset_policy_redist_tags() { _policy_redist_map.reset(); } #ifdef HAVE_IPV6 /** IPv6 stuff */ int RibManager::add_vif_address(const string& vifn, const IPv6& addr, const IPv6Net& subnet, const IPv6& peer_addr, string& err) { int r = add_vif_address_to_ribs(_urib6, _mrib6, vifn, addr, subnet, IPv6::ZERO(), peer_addr, err); return r; } int RibManager::delete_vif_address(const string& vifn, const IPv6& addr, string& err) { return delete_vif_address_from_ribs(_urib6, _mrib6, vifn, addr, err); } int RibManager::add_redist_xrl_output6(const string& to_xrl_target, const string& from_protocol, bool unicast, bool multicast, const IPv6Net& network_prefix, const string& cookie, bool is_xrl_transaction_output) { if (unicast) { int e = redist_enable_xrl_output(_eventloop, _xrl_router, _profile, _urib6, to_xrl_target, from_protocol, network_prefix, cookie, is_xrl_transaction_output); if (e != XORP_OK) { return e; } } if (multicast) { int e = redist_enable_xrl_output(_eventloop, _xrl_router, _profile, _mrib6, to_xrl_target, from_protocol, network_prefix, cookie, is_xrl_transaction_output); if (e != XORP_OK && unicast) { redist_disable_xrl_output(_urib6, to_xrl_target, from_protocol, cookie, is_xrl_transaction_output); } return e; } return XORP_OK; } int RibManager::delete_redist_xrl_output6(const string& to_xrl_target, const string& from_protocol, bool unicast, bool multicast, const string& cookie, bool is_xrl_transaction_output) { if (unicast) redist_disable_xrl_output(_urib6, to_xrl_target, from_protocol, cookie, is_xrl_transaction_output); if (multicast) redist_disable_xrl_output(_mrib6, to_xrl_target, from_protocol, cookie, is_xrl_transaction_output); return XORP_OK; } #endif //ipv6 xorp/rib/main_routemap.cc0000664000076400007640000000450711421137511015605 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "rib_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "routemap.hh" #include "route.hh" int main(int /* argc */, char* argv[]) { RouteMap* rm; rm = new RouteMap("testmap"); RMRule* rr; RMMatchIPAddr* rmatchip; RMAction* ra; // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); IPv4 ip1("192.150.187.0"); IPv4Net ipnet1(ip1, 24); rmatchip = new RMMatchIPAddr(ipnet1); ra = new RMAction(); rr = new RMRule(10, rmatchip, ra); rm->add_rule(rr); RMRule* rr2; RMMatchIPAddr* rmatchip2; RMAction* ra2; IPv4 ip2("192.150.186.0"); IPv4Net ipnet2(ip2, 24); rmatchip2 = new RMMatchIPAddr(ipnet2); ra2 = new RMAction(); rr2 = new RMRule(20, rmatchip2, ra2); rm->add_rule(rr2); RMRule* rr3; RMMatchIPAddr* rmatchip3; RMAction* ra3; IPv4 ip3("193.150.186.0"); IPv4Net ipnet3(ip3, 24); rmatchip3 = new RMMatchIPAddr(ipnet3); ra3 = new RMAction(); rr3 = new RMRule(15, rmatchip3, ra3); rm->add_rule(rr3); cout << rm->str() << "\n"; // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); exit(0); } xorp/rib/rt_tab_pol_conn.cc0000664000076400007640000001427511540224234016113 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // #define DEBUG_LOGGING // #define DEBUG_PRINT_FUNCTION_NAME #include "rib_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "rt_tab_pol_conn.hh" #include "rib_varrw.hh" template const string PolicyConnectedTable::table_name = "policy-connected-table"; template PolicyConnectedTable::PolicyConnectedTable (RouteTable* parent, PolicyFilters& pfs) : RouteTable(table_name), _parent(parent), _policy_filters(pfs) { if (_parent->next_table()) { this->set_next_table(_parent->next_table()); this->next_table()->replumb(_parent, this); } _parent->set_next_table(this); } template PolicyConnectedTable::~PolicyConnectedTable () { for (typename RouteContainer::iterator i = _route_table.begin(); i != _route_table.end(); ++i) { delete i.payload(); } _route_table.delete_all_nodes(); } template int PolicyConnectedTable::add_route(const IPRouteEntry& route, RouteTable* caller) { XLOG_ASSERT(caller == _parent); debug_msg("[RIB] PolicyConnectedTable ADD ROUTE: %s\n", route.str().c_str()); // store original IPRouteEntry* original = new IPRouteEntry(route); _route_table.insert(original->net(), original); // make a copy so we may modify it IPRouteEntry route_copy(*original); do_filtering(route_copy); RouteTable* next = this->next_table(); XLOG_ASSERT(next); // Send the possibly modified route down return next->add_route(route_copy, this); } template int PolicyConnectedTable::delete_route(const IPRouteEntry* route, RouteTable* caller) { XLOG_ASSERT(caller == _parent); XLOG_ASSERT(route != NULL); debug_msg("[RIB] PolicyConnectedTable DELETE ROUTE: %s\n", route->str().c_str()); // delete our copy typename RouteContainer::iterator i; i = _route_table.lookup_node(route->net()); XLOG_ASSERT(i != _route_table.end()); const IPRouteEntry* re = i.payload(); _route_table.erase(route->net()); delete re; RouteTable* next = this->next_table(); XLOG_ASSERT(next); // make a copy so we may modify it (e.g., by setting its policy tags) IPRouteEntry route_copy(*route); do_filtering(route_copy); // propagate the delete return next->delete_route(&route_copy, this); } template const IPRouteEntry* PolicyConnectedTable::lookup_route(const IPNet& net) const { XLOG_ASSERT(_parent); typename RouteContainer::iterator i; i = _route_table.lookup_node(net); // check if we have route [we should have same routes as origin table]. if (i == _route_table.end()) return _parent->lookup_route(net); // should return null probably return i.payload(); } template const IPRouteEntry* PolicyConnectedTable::lookup_route(const A& addr) const { XLOG_ASSERT(_parent); typename RouteContainer::iterator i; i = _route_table.find(addr); // same as above if (i == _route_table.end()) return _parent->lookup_route(addr); // return null return i.payload(); } template RouteRange* PolicyConnectedTable::lookup_route_range(const A& addr) const { XLOG_ASSERT(_parent); // XXX: no policy tags in ranges for now return _parent->lookup_route_range(addr); } template void PolicyConnectedTable::replumb(RouteTable* old_parent, RouteTable* new_parent) { XLOG_ASSERT(old_parent == _parent); _parent = new_parent; } template string PolicyConnectedTable::str() const { return "not implement yet"; } template void PolicyConnectedTable::push_routes() { debug_msg("[RIB] PolicyConnectedTable PUSH ROUTES\n"); RouteTable* next = this->next_table(); XLOG_ASSERT(next); vector*> new_routes; // XXX: not a background task // go through original routes and refilter them for (typename RouteContainer::iterator i = _route_table.begin(); i != _route_table.end(); ++i) { const IPRouteEntry* prev = i.payload(); // make a copy so filter may [possibly] modify it const IPRouteEntry* orig = _parent->lookup_route(prev->net()); IPRouteEntry* copy = new IPRouteEntry(*orig); do_filtering(*copy); // only policytags may change next->replace_policytags(*copy, prev->policytags(), this); // delete old route delete prev; // keep the new route so we may re-store it after this iteration new_routes.push_back(copy); } // store all new routes for (typename vector*>::iterator i = new_routes.begin(); i != new_routes.end(); ++i) { // replace route IPRouteEntry* route = *i; _route_table.erase(route->net()); _route_table.insert(route->net(), route); } } template void PolicyConnectedTable::do_filtering(IPRouteEntry& route) { try { debug_msg("[RIB] PolicyConnectedTable Filtering: %s\n", route.str().c_str()); RIBVarRW varrw(route); // only source match filtering! _policy_filters.run_filter(filter::EXPORT_SOURCEMATCH, varrw); } catch(const PolicyException& e) { XLOG_FATAL("PolicyException: %s", e.str().c_str()); XLOG_UNFINISHED(); } } template class PolicyConnectedTable; template class PolicyConnectedTable; xorp/rib/rib_manager.hh0000664000076400007640000003736211540225533015236 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/rib/rib_manager.hh,v 1.39 2008/10/02 21:58:11 bms Exp $ #ifndef __RIB_RIB_MANAGER_HH__ #define __RIB_RIB_MANAGER_HH__ #include "libxorp/xorp.h" #include "libxorp/eventloop.hh" #include "libxorp/status_codes.h" #include "libxipc/xrl_std_router.hh" #include "libxorp/profile.hh" #include "libproto/proto_state.hh" #include "rib.hh" #include "register_server.hh" #include "vifmanager.hh" #include "xrl_target.hh" class EventLoop; /** * @short Main top-level class containing RIBs and main eventloop. * * The single RibManager class instance is the top-level class in the * RIB process from which everything else is built and run. It * contains the four RIBs for IPv4 unicast routes, IPv4 multicast * routes, IPv6 unicast routes and IPv6 multicast routes. It also * contains the RIB's main eventloop. */ class RibManager : public ProtoState { public: /** * RibManager constructor * * @param eventloop the event loop to user. * @param xrl_std_router the XRL router to use. * @param fea_target the FEA XRL target name. */ RibManager(EventLoop& eventloop, XrlStdRouter& xrl_std_router, const string& fea_target); /** * RibManager destructor */ ~RibManager(); /** * Start operation. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int start(); /** * Stop operation. * * Gracefully stop the RIB. * * @return XORP_OK on success, otherwise XORP_ERROR. */ int stop(); /** * Periodic Status Update * * @return true to reschedule next status check. */ bool status_updater(); /** * Check status of RIB process. * * @return the process status code. * @see ProcessStatus. */ ProcessStatus status(string& reason) const { reason = _status_reason; return _status_code; } /** * new_vif is called to inform all the RIBs that a new virtual * interface has been created. * * @param vifname the name of the new VIF. * @param vif the Vif class instance holding information about the * new VIF. * @param err reference to string in which to store the * human-readable error message in case anything goes wrong. Used * for debugging purposes. * @return XORP_OK on success, otherwise XORP_ERROR. */ int new_vif(const string& vifname, const Vif& vif, string& err); /** * delete_vif is called to inform all the RIBs that a virtual * interface that they previously knew about has been deleted. * * @param vifname the name of the VIF that was deleted. * @param err reference to string in which to store the * human-readable error message in case anything goes wrong. Used * for debugging purposes. * @return XORP_OK on success, otherwise XORP_ERROR. */ int delete_vif(const string& vifname, string& err); /** * Set the vif flags of a configured vif. * * @param vifname the name of the vif. * @param is_pim_register true if the vif is a PIM Register interface. * @param is_p2p true if the vif is point-to-point interface. * @param is_loopback true if the vif is a loopback interface. * @param is_multicast true if the vif is multicast capable. * @param is_broadcast true if the vif is broadcast capable. * @param is_up true if the underlying vif is UP. * @param mtu the MTU of the vif. * @param err reference to string in which to store the * human-readable error message in case anything goes wrong. Used * for debugging purposes. * @return XORP_OK on success, otherwise XORP_ERROR. */ int set_vif_flags(const string& vifname, bool is_p2p, bool is_loopback, bool is_multicast, bool is_broadcast, bool is_up, uint32_t mtu, string& err); /** * add_vif_address is called to inform all the RIBs that a new IPv4 * address has been added to a virtual interface. * * @param vifname the name of the VIF that the address was added to. * @param addr the new address. * @param subnet the subnet (masked address) that the new address * resides on. * @param broadcast the broadcast address to add. * @param peer the peer address to add. * @param err reference to string in which to store the * human-readable error message in case anything goes wrong. Used * for debugging purposes. * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_vif_address(const string& vifname, const IPv4& addr, const IPv4Net& subnet, const IPv4& broadcast_addr, const IPv4& peer_addr, string& err); /** * delete_vif_address is called to inform all the RIBs that an IPv4 * address that they previously know about has been deleted from a * specific VIF. * * @param vifname the name of the VIF that the address was deleted from. * @param addr the address that was deleted. * @param err reference to string in which to store the * human-readable error message in case anything goes wrong. Used * for debugging purposes. * @return XORP_OK on success, otherwise XORP_ERROR. */ int delete_vif_address(const string& vifname, const IPv4& addr, string& err); /** * Make some errors we'd normally mask fatal. Should be used for * testing only. */ void make_errors_fatal(); /** * Register Interest in an XRL target so we can monitor process * births and deaths and clean up appropriately when things die. * * @param target_class the XRL Target Class we're interested in. */ void register_interest_in_target(const string& target_class); /** * Called in response to registering interest in an XRL target * * @param e XRL Response code. */ void register_interest_in_target_done(const XrlError& e); /** * Deregister Interest in an XRL target so we can stop monitor process * births and deaths. * * @param target_class the XRL Target Class we're deregistering * interested in. */ void deregister_interest_in_target(const string& target_class); /** * Called in response to deregistering interest in an XRL target * * @param e XRL Response code. */ void deregister_interest_in_target_done(const XrlError& e); /** * Target Death is called when an XRL target that we've registered * an interest in dies. * * @param target_class the XRL Class of the target that died. * @param target_instance the XRL Class Instance of the target that died. */ void target_death(const string& target_class, const string& target_instance); /** * Add Route Redistributor that generates updates with redist4 * XRL interface. * * @param target_name XRL target to receive redistributed routes. * @param from_protocol protocol routes are redistributed from. * @param unicast apply to unicast rib. * @param multicast apply to multicast rib. * @param network_prefix redistribite only the routes that fall into this * prefix address. * @param cookie cookie passed in route redistribution XRLs. * @param is_xrl_transaction_output if true the add/delete route XRLs * are grouped into transactions. * * @return XORP_OK on success, XORP_ERROR on failure. */ int add_redist_xrl_output4(const string& target_name, const string& from_protocol, bool unicast, bool multicast, const IPv4Net& network_prefix, const string& cookie, bool is_xrl_transaction_output); /** * Remove Route Redistributor that generates updates with redist4 * XRL interface. * * @param target_name XRL target to receive redistributed routes. * @param from_protocol protocol routes are redistributed from. * @param unicast apply to unicast rib. * @param multicast apply to multicast rib. * @param cookie cookie passed in route redistribution XRLs. * @param is_xrl_transaction_output if true the add/delete route XRLs * are grouped into transactions. * * @return XORP_OK on success, XORP_ERROR on failure. */ int delete_redist_xrl_output4(const string& target_name, const string& from_protocol, bool unicast, bool multicast, const string& cookie, bool is_xrl_transaction_output); XrlStdRouter& xrl_router() { return _xrl_router; } /** * Push the connected routes through the policy filter for re-filtering. */ void push_routes(); /** * Configure a policy filter. * * Will throw an exception on error. * * In this case the source match filter of connected routes will be * configured. * * @param filter Identifier of filter to configure. * @param conf Configuration of the filter. */ void configure_filter(const uint32_t& filter, const string& conf); /** * Reset a policy filter. * * @param filter Identifier of filter to reset. */ void reset_filter(const uint32_t& filter); /** * @return the global instance of policy filters. */ PolicyFilters& policy_filters() { return _policy_filters; } /** * @return the global instance of the policy redistribution map. */ PolicyRedistMap& policy_redist_map() { return _policy_redist_map; } /** * Insert [old ones are kept] policy-tags for a protocol. * * All routes which contain at least one of these tags, will be * redistributed to the protocol. * * @param protocol Destination protocol for redistribution. * @param tags Policy-tags of routes which need to be redistributed. */ void insert_policy_redist_tags(const string& protocol, const PolicyTags& tags); /** * Reset the policy redistribution map. * * This map allows policy redistribution to occur by directing routes with * specific policy-tags to specific protocols. * */ void reset_policy_redist_tags(); /** * @return a reference to the profiler. */ Profile& profile() {return _profile;} /** * @return a reference to the IPv4 unicast RIB. */ RIB& urib4() { return _urib4; } /** * @return a reference to the IPv4 multicast RIB. */ RIB& mrib4() { return _mrib4; } /** * @return a reference to the XRL RIB target. */ XrlRibTarget& xrl_rib_target() { return _xrl_rib_target; } #ifdef HAVE_IPV6 /** * add_vif_address is called to inform all the RIBs that a new IPv6 * address has been added to a virtual interface. * * @param vifname the name of the VIF that the address was added to. * @param addr the new address. * @param subnet the subnet (masked address) that the new address * resides on. * @param peer the peer address to add. * @param err reference to string in which to store the * human-readable error message in case anything goes wrong. Used * for debugging purposes. * @return XORP_OK on success, otherwise XORP_ERROR. */ int add_vif_address(const string& vifname, const IPv6& addr, const IPv6Net& subnet, const IPv6& peer, string& err); /** * delete_vif_address is called to inform all the RIBs that an IPv6 * address that they previously know about has been deleted from a * specific VIF. * * @param vifname the name of the VIF that the address was deleted from. * @param addr the address that was deleted. * @param err reference to string in which to store the * human-readable error message in case anything goes wrong. Used * for debugging purposes. * @return XORP_OK on success, otherwise XORP_ERROR. */ int delete_vif_address(const string& vifname, const IPv6& addr, string& err); /** * Add Route Redistributor that generates updates with redist6 * XRL interface. * * @param target_name XRL target to receive redistributed routes. * @param from_protocol protocol routes are redistributed from. * @param unicast apply to unicast rib. * @param multicast apply to multicast rib. * @param network_prefix redistribite only the routes that fall into this * prefix address. * @param cookie cookie passed in route redistribution XRLs. * @param is_xrl_transaction_output if true the add/delete route XRLs * are grouped into transactions. * * @return XORP_OK on success, XORP_ERROR on failure. */ int add_redist_xrl_output6(const string& target_name, const string& from_protocol, bool unicast, bool multicast, const IPv6Net& network_prefix, const string& cookie, bool is_xrl_transaction_output); /** * Remove Route Redistributor that generates updates with redist6 * XRL interface. * * @param target_name XRL target to receive redistributed routes. * @param from_protocol protocol routes are redistributed from. * @param unicast apply to unicast rib. * @param multicast apply to multicast rib. * @param cookie cookie passed in route redistribution XRLs. * @param is_xrl_transaction_output if true the add/delete route XRLs * are grouped into transactions. * * @return XORP_OK on success, XORP_ERROR on failure. */ int delete_redist_xrl_output6(const string& target_name, const string& from_protocol, bool unicast, bool multicast, const string& cookie, bool is_xrl_transaction_output); /** * @return a reference to the IPv6 unicast RIB. */ RIB& urib6() { return _urib6; } /** * @return a reference to the IPv6 multicast RIB. */ RIB& mrib6() { return _mrib6; } #endif //ipv6 private: ProcessStatus _status_code; string _status_reason; EventLoop& _eventloop; // The event loop to use XrlStdRouter& _xrl_router; // The XRL router to use RegisterServer _register_server; // To notify clients about route change RIB _urib4; // The IPv4 unicast RIB RIB _mrib4; // The IPv4 multicast RIB #ifdef HAVE_IPV6 RIB _urib6; // The IPv6 unicast RIB RIB _mrib6; // The IPv6 multicast RIB #endif VifManager _vif_manager; // The VIF manager XrlRibTarget _xrl_rib_target; set _targets_of_interest; // Monitored XRL targets XorpTimer _status_update_timer; // Timer for periodic checks of RIB status const string _fea_target; PolicyFilters _policy_filters; // Connected routes filter. // only one should be present. PolicyRedistMap _policy_redist_map; // Policy registribution map // [links policy-tags to // targets]. // only one should be present. Profile _profile; // Profiling logs. }; #endif // __RIB_RIB_MANAGER_HH__ xorp/rib/rib_varrw.hh0000664000076400007640000000426711421137511014757 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/rib/rib_varrw.hh,v 1.12 2008/10/02 21:58:11 bms Exp $ #ifndef __RIB_RIB_VARRW_HH__ #define __RIB_RIB_VARRW_HH__ #include "policy/backend/single_varrw.hh" #include "policy/common/element_factory.hh" #include "route.hh" /** * @short Enables reading and writing variables to a RIB route. * * This class is intended for connected routes only, and supports only * policytags being altered. */ template class RIBVarRW : public SingleVarRW { public: enum { VAR_NETWORK4 = VAR_PROTOCOL, VAR_NEXTHOP4, VAR_NETWORK6, VAR_NEXTHOP6, VAR_METRIC }; /** * @param route route to filter and possibly modify. */ RIBVarRW(IPRouteEntry& route); // SingleVarRW interface void start_read(); /** * Write a variable. * * @param id variablea to write. * @param e value of variable. */ void single_write(const Id& id, const Element& e); Element* single_read(const Id& id); private: /** * Specialized template to read nexthop and ip address. * If it is a v4 specialization, v6 addresses are set to null * and vice-versa. * * @param r route from which to read addresses. */ void read_route_nexthop(IPRouteEntry& r); IPRouteEntry& _route; ElementFactory _ef; }; #endif // __RIB_RIB_VARRW_HH__ xorp/rib/rt_tab_merged.cc0000664000076400007640000001417111540224234015542 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "rib_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "rt_tab_merged.hh" template inline static string make_merged_name(const RouteTable* a, const RouteTable* b) { return string("Merged:(" + a->tablename() + ")+(" + b->tablename() + ")"); } // // A = Address Type. E.g., IPv4 or IPv6 // template MergedTable::MergedTable(RouteTable* table_a, RouteTable* table_b) : RouteTable(make_merged_name(table_a, table_b)), _table_a(table_a), _table_b(table_b) { _table_a->set_next_table(this); _table_b->set_next_table(this); debug_msg("New Merged: %s\n", this->tablename().c_str()); } template int MergedTable::add_route(const IPRouteEntry& route, RouteTable* caller) { debug_msg("MT[%s]: Adding route %s\n", this->tablename().c_str(), route.str().c_str()); if (this->next_table() == NULL) return XORP_ERROR; RouteTable* other_table; if (caller == _table_b) { other_table = _table_a; } else if (caller == _table_a) { other_table = _table_b; } else { XLOG_UNREACHABLE(); } const IPRouteEntry* found; found = other_table->lookup_route(route.net()); if (found != NULL) { if (found->admin_distance() > route.admin_distance()) { // The admin distance of the existing route is worse this->next_table()->delete_route(found, this); } else { return XORP_ERROR; } } this->next_table()->add_route(route, this); return XORP_OK; } template int MergedTable::delete_route(const IPRouteEntry* route, RouteTable* caller) { if (this->next_table() == NULL) return XORP_ERROR; RouteTable* other_table; if (caller == _table_b) { other_table = _table_a; } else if (caller == _table_a) { other_table = _table_b; } else { XLOG_UNREACHABLE(); } const IPRouteEntry* found; found = other_table->lookup_route(route->net()); if (found != NULL) { if (found->admin_distance() > route->admin_distance()) { // The admin distance of the existing route is worse this->next_table()->delete_route(route, this); this->next_table()->add_route(*found, this); return XORP_OK; } else { // The admin distance of the existing route is better, so // this route would not previously have been propagated // downstream. return XORP_ERROR; } } this->next_table()->delete_route(route, this); return XORP_OK; } template const IPRouteEntry* MergedTable::lookup_route(const IPNet& net) const { const IPRouteEntry* found_a; const IPRouteEntry* found_b; found_a = _table_a->lookup_route(net); found_b = _table_b->lookup_route(net); if (found_b == NULL) { return found_a; } if (found_a == NULL) { return found_b; } if (found_a->admin_distance() <= found_b->admin_distance()) { return found_a; } else { return found_b; } } template const IPRouteEntry* MergedTable::lookup_route(const A& addr) const { const IPRouteEntry* found_a; const IPRouteEntry* found_b; found_b = _table_b->lookup_route(addr); found_a = _table_a->lookup_route(addr); debug_msg("MergedTable::lookup_route. Table_a: %p\n", _table_a); debug_msg("MergedTable::lookup_route. Table_b: %p\n", _table_b); if (found_b == NULL) { return found_a; } if (found_a == NULL) { return found_b; } // // The route was in both tables. We need to do longest match // unless the prefixes are the same, when we take the route with the // lowest admin distance. // size_t hi_prefix_len, lo_prefix_len; hi_prefix_len = found_b->net().prefix_len(); lo_prefix_len = found_a->net().prefix_len(); if (hi_prefix_len > lo_prefix_len) { return found_b; } else if (hi_prefix_len < lo_prefix_len) { return found_a; } else if (found_b->admin_distance() <= found_a->admin_distance()) { return found_b; } else { return found_a; } } template void MergedTable::replumb(RouteTable* old_parent, RouteTable* new_parent) { debug_msg("MergedTable::replumb replacing %s with %s\n", old_parent->tablename().c_str(), new_parent->tablename().c_str()); if (_table_a == old_parent) { _table_a = new_parent; } else if (_table_b == old_parent) { _table_b = new_parent; } else { XLOG_UNREACHABLE(); } this->set_tablename(make_merged_name(_table_a, _table_b)); debug_msg("MergedTable: now called \"%s\"\n", this->tablename().c_str()); } template RouteRange* MergedTable::lookup_route_range(const A& addr) const { RouteRange* lo_rr = _table_a->lookup_route_range(addr); RouteRange* hi_rr = _table_b->lookup_route_range(addr); hi_rr->merge(lo_rr); delete lo_rr; return hi_rr; } template string MergedTable::str() const { string s; s = "-------\nMergedTable: " + this->tablename() + "\n"; s += "_table_a = " + _table_a->tablename() + "\n"; s += "_table_b = " + _table_b->tablename() + "\n"; if (this->next_table() == NULL) s += "no next table\n"; else s += "next table = " + this->next_table()->tablename() + "\n"; return s; } template class MergedTable; template class MergedTable; xorp/rib/rt_tab_base.hh0000664000076400007640000001526711421137511015231 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/rib/rt_tab_base.hh,v 1.25 2008/10/02 21:58:12 bms Exp $ #ifndef __RIB_RT_TAB_BASE_HH__ #define __RIB_RT_TAB_BASE_HH__ #include "libxorp/xorp.h" #include "route.hh" #include "protocol.hh" #include "libxorp/trie.hh" enum TableType { ORIGIN_TABLE = 1 << 0, MERGED_TABLE = 1 << 1, EXTINT_TABLE = 1 << 2, REDIST_TABLE = 1 << 3, REGISTER_TABLE = 1 << 4, DELETION_TABLE = 1 << 5, EXPECT_TABLE = 1 << 6, LOG_TABLE = 1 << 7, POLICY_REDIST_TABLE = 1 << 8, POLICY_CONNECTED_TABLE = 1 << 9, MAX_TABLE_TYPE = 1 << 9 }; /** * @short Stores a Route and bounds on the validity of the route. * * The RouteRange class is used to hold an annotated routing entry. * It is used when the @ref RegisterTable is registering interest in * routing information associated with a specific address. It holds * an IP address, the route that would be used to route that address, * and the top and bottom addresses of the route range that includes * that address for which this route applies without being overlayed * by a more specific route. For example: * * Suppose an @ref OriginTable holds the routes 1.0.0.0/16 and * 1.0.1.0/24. The address we're interested in is 1.0.0.10. Then if * we ask this OriginTable for the RouteRange for 1.0.0.10, we get: * * address: 1.0.0.10 * route: the route for 1.0.0.0/16 * top: 1.0.0.255 * bottom: 1.0.0.0 * * I.e., the route for 1.0.0.10 is 1.0.0.0/16, and this answer is also * valid for addresses in the range 1.0.0.0 to 1.0.0.255 inclusive. */ template class RouteRange { public: RouteRange(const A& req_addr, const IPRouteEntry* route, const A& top, const A& bottom) : _req_addr(req_addr), _route(route), _top(top), _bottom(bottom) {} const A& top() const { return _top; } const A& bottom() const { return _bottom; } const IPRouteEntry* route() const { return _route; } const IPNet& net() const { return _route->net(); } /** * Merge this entry with another entry. * * Replace route with the entry from rr if it is better, (XXX why ?) * shrink the intervals if the other one is smaller. * * @param his_rr the entry to merge with. */ void merge(const RouteRange* his_rr) { const IPRouteEntry* rrr = his_rr->route(); if (_route == NULL) _route = rrr; else if (rrr != NULL) { int my_prefix_len = net().prefix_len(); int his_prefix_len = his_rr->net().prefix_len(); if (his_prefix_len > my_prefix_len) // his route beats mine _route = rrr; else if (his_prefix_len == my_prefix_len) { // routes are equivalent, compare distance if (_route->admin_distance() > rrr->admin_distance()) // his is better _route = rrr; // note: if routes have same admin distance, mine wins. } } if (_top > his_rr->top()) // he wins, shrink _top _top = his_rr->top(); if (_bottom < his_rr->bottom()) // he wins, shrink _bottom _bottom = his_rr->bottom(); } // Return the largest subnet contained in the range. IPNet minimal_subnet() const { for (size_t bits = 0; bits <= A::addr_bitlen(); bits++) { IPNet net(_req_addr, bits); if (net.masked_addr() >= _bottom && net.top_addr() <= _top) return net; // we got it. } XLOG_UNREACHABLE(); } private: A _req_addr; const IPRouteEntry* _route; A _top; A _bottom; }; /** * @short Base class for a routing table. * * This is the base class for a routing table. A RIB consists of a * tree of RouteTables that take routes from routing protocols and * merge them together so that routes that emerge from the last table * are the best routes available, and have nexthop information that is * for an immediate neighbor of this router. See the RIB design * document for an overview of how RouteTable are plumbed together to * form a RIB. * * All RouteTables that form the RIB are derived from this base class. * * RouteTables take routing changes in from one or more parents, and * pass on those changes to the _next_table if the change is for the * best of the alternative routes. * * RouteTables take route lookup requests from their _next_table, and * pass on those requests to their parents. If more than one parent * has a response, only the best is returned as the answer. * * Note: A = Address Type, e.g., IPv4 or IPv6. */ template class RouteTable { public: RouteTable(const string& name) : _tablename(name), _next_table(NULL) {} virtual ~RouteTable(); virtual int add_route(const IPRouteEntry& route, RouteTable* caller) = 0; virtual int delete_route(const IPRouteEntry* route, RouteTable* caller) = 0; virtual const IPRouteEntry* lookup_route(const IPNet& net) const = 0; virtual const IPRouteEntry* lookup_route(const A& addr) const = 0; virtual RouteRange* lookup_route_range(const A& addr) const = 0; virtual void set_next_table(RouteTable* next_table); // parent is only supposed to be called on single-parent tables virtual RouteTable* parent() { XLOG_UNREACHABLE(); return NULL; } virtual TableType type() const = 0; virtual void replumb(RouteTable* old_parent, RouteTable* new_parent) = 0; virtual string str() const = 0; virtual void flush() {} const string& tablename() const { return _tablename; } RouteTable* next_table() { return _next_table; } const RouteTable* next_table() const { return _next_table; } // this call should be received and dealt with by the PolicyRedistTable. virtual void replace_policytags(const IPRouteEntry& route, const PolicyTags& prevtags, RouteTable* caller); protected: void set_tablename(const string& s) { _tablename = s; } private: string _tablename; RouteTable* _next_table; }; #endif // __RIB_RT_TAB_BASE_HH__ xorp/rib/register_server.hh0000664000076400007640000003005311540225533016170 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/rib/register_server.hh,v 1.17 2008/10/02 21:58:11 bms Exp $ #ifndef __RIB_REGISTER_SERVER_HH__ #define __RIB_REGISTER_SERVER_HH__ #include "libxorp/xorp.h" #include "libxorp/ipv4.hh" #include "libxorp/ipv6.hh" #include "libxorp/ipnet.hh" #include "xrl/interfaces/rib_client_xif.hh" class XrlRouter; class NotifyQueueEntry; typedef XrlRibClientV0p1Client ResponseSender; /** * @short Queue of route event notifications * * A NotfiyQueue holds a queue of route event notifications waiting * transmission to the XORP process that registered interest in route * changes that affected one or more routes. When a lot of routes * change, we need to queue the changes because we may generate them * faster than the recipient can handle being told about them. */ class NotifyQueue { public: /** * NotifyQueue constructor * * @param module_name the XRL module target name for the process that * this queue holds notifications for. */ NotifyQueue(const string& module_name); /** * Add an notification entry to the queue. * * @param e the notification entry to be queued. */ void add_entry(NotifyQueueEntry* e); /** * Send the next entry in the queue to this queue's XRL target. */ void send_next(); /** * Flush is an indication to the queue that the changes since the * last flush can be checked for consolidation. Several add_entry * events might occur in rapid succession affecting the same * route. A flush indicates that it is OK to start sending this * batch of changes (and to consolidate those changes to avoid * thrashing, but we don't currently do this). */ void flush(ResponseSender* response_sender); /** * XRL transmit complete callback. We use this to cause the next * item in the queue to be sent. * * @param e the XRL completion status code. */ void xrl_done(const XrlError& e); typedef XorpCallback1::RefPtr XrlCompleteCB; private: string _module_name; list _queue; bool _active; ResponseSender* _response_sender; }; /** * Base class for a queue entry to be held in a @ref NotifyQueue. */ class NotifyQueueEntry { public: /** * The type of notifcation: either the data (nexthop, metric, * admin_distance) associated with the registered route changed, * or the data has changed enough that the registration has been * invalidated and the client needs to register again to find out * what happened. */ typedef enum { CHANGED, INVALIDATE } EntryType; /** * NotifyQueueEntry constructor */ NotifyQueueEntry() {} /** * NotifyQueueEntry destructor */ virtual ~NotifyQueueEntry() {}; /** * Send the queue entry (pure virtual) */ virtual void send(ResponseSender* response_sender, const string& module_name, NotifyQueue::XrlCompleteCB& cb) = 0; /** * @return The type of queue entry * @see NotifyQueueEntry::EntryType */ virtual EntryType type() const = 0; private: }; /** * Notification Queue entry indicating that a change occured to the * metric, admin_distance or nexthop of a route in which interest was * registered. * * The template class A is the address family: either the IPv4 class * or the IPv6 class. */ template class NotifyQueueChangedEntry : public NotifyQueueEntry { public: /** * NotifyQueueChangedEntry Constructor * * @param net the destination subnet of the route that changed. * @param nexthop the new nexthop of the route that changed. * @param metric the new routing protocol metric of the route that changed. * @param admin_distance the adminstratively defined distance of the * routing protocol this routing entry was computed by. * @param protocol_origin the name of the protocol that originated this * route. * @param multicast true indicates that the change occured in the * multicast RIB, false indicates that it occured in the unicast * RIB. */ NotifyQueueChangedEntry(const IPNet& net, const A& nexthop, uint32_t metric, uint32_t admin_distance, const string& protocol_origin, bool multicast) : _net(net), _nexthop(nexthop), _metric(metric), _admin_distance(admin_distance), _protocol_origin(protocol_origin), _multicast(multicast) {} /** * @return CHANGED * @see NotifyQueueEntry::EntryType */ EntryType type() const { return CHANGED; } /** * Actually send the XRL that communicates this change to the * registered process. * * @param response_sender the auto-generated stub class instance * that will do the parameter marchalling. * @param module_name the XRL module target name to send this * information to. * @param cb the method to call back when this XRL completes. */ void send(ResponseSender* response_sender, const string& module_name, NotifyQueue::XrlCompleteCB& cb); private: IPNet _net; // The route's full subnet (not the valid_subnet) A _nexthop; // The new nexthop of the route uint32_t _metric; // The metric of the route uint32_t _admin_distance; // The administratively defined distance of // the routing protocol this routing entry // was computed by string _protocol_origin; // The name of the protocol that originated // this route bool _multicast; // If true, a change occured in multicast RIB, // otherwise it occured in the unicast RIB }; /** * Notification Queue entry indicating that a change occured which has * caused a route registration to become invalid. The client must * re-register to find out what actually happened. * * The template class A is the address family: either the IPv4 class * or the IPv6 class. */ template class NotifyQueueInvalidateEntry : public NotifyQueueEntry { public: /** * NotifyQueueInvalidateEntry Constructor * * @param net the valid_subnet of the route registration that is * now invalid. * @param multicast true indicates that the change occured in the * multicast RIB, false indicates that it occured in the unicast * RIB. */ NotifyQueueInvalidateEntry(const IPNet& net, bool multicast) : _net(net), _multicast(multicast) {} /** * @return INVALIDATE * @see NotifyQueueEntry::EntryType */ EntryType type() const { return INVALIDATE; } /** * Actually send the XRL that communicates this change to the * registered process. * * @param response_sender the auto-generated stub class instance * that will do the parameter marchalling. * @param module_name the XRL module target name to send this * information to. * @param cb the method to call back when this XRL completes. */ void send(ResponseSender* response_sender, const string& module_name, NotifyQueue::XrlCompleteCB& cb); private: IPNet _net; // The valid_subnet from the RouteRegister // instance. The other end already knows the // route's full subnet. bool _multicast; // If true, a change occured in multicast RIB, // otherwise it occured in the unicast RIB }; /** * @short RegisterServer handles communication of notifications to the * clients that registered interest in changes affecting specific * routes. * * RegisterServer handles communication of notifications to the * clients that registered interest in changes affecting specific * routes. As these notifications can sometimes be generated faster * than the recipient can handle them, the notifications can be queued * here. */ class RegisterServer { public: /** * RegisterServer constructor * * @param xrl_router the XRL router instance used to send and * receive XRLs in this process. */ RegisterServer(XrlRouter* xrl_router); /** * RegisterServer destructor */ virtual ~RegisterServer() {} /** * send_route_changed is called to communicate to another XRL * module that routing information in which it had registered an * interest has changed its nexthop, metric, or admin distance. * * @param module_name the XRL target name of the module to notify. * @param net the destination subnet of the route that changed. * @param nexthop the new nexthop of the route that changed. * @param metric the new routing protocol metric of the route that changed. * @param admin_distance the new admin distance of the route that changed. * @param protocol_origin the name of the protocol that originated this * route. * @param multicast true indicates that the change occured in the * multicast RIB, false indicates that it occured in the unicast * RIB. */ virtual void send_route_changed(const string& module_name, const IPv4Net& net, const IPv4& nexthop, uint32_t metric, uint32_t admin_distance, const string& protocol_origin, bool multicast); virtual void send_invalidate(const string& module_name, const IPv4Net& net, bool multicast); #ifdef HAVE_IPV6 /** * send_route_changed is called to communicate to another XRL * module that routing information in which it had registered an * interest has changed its nexthop, metric, or admin distance. * * @param module_name the XRL target name of the module to notify. * @param net the destination subnet of the route that changed. * @param nexthop the new nexthop of the route that changed. * @param metric the new routing protocol metric of the route that changed. * @param admin_distance the new admin distance of the route that changed. * @param protocol_origin the name of the protocol that originated this * route. * @param multicast true indicates that the change occured in the * multicast RIB, false indicates that it occured in the unicast * RIB. */ virtual void send_route_changed(const string& module_name, const IPv6Net& net, const IPv6& nexthop, uint32_t metric, uint32_t admin_distance, const string& protocol_origin, bool multicast); /** * send_invalidate is called to communicate to another XRL module * that routing information in which it had registered an interest * has changed in some unspecified way that has caused the * registration to become invalid. The client must re-register to * find out what really happened. * * @param module_name the XRL target name of the module to notify. * @param net the valid_subnet of the route registration that is * now invalid. * @param multicast true indicates that the change occured in the * multicast RIB, false indicates that it occured in the unicast * RIB. */ virtual void send_invalidate(const string& module_name, const IPv6Net& net, bool multicast); #endif /** * @see NotifyQueue::flush */ virtual void flush(); protected: void add_entry_to_queue(const string& module_name, NotifyQueueEntry* e); map _queuemap; ResponseSender _response_sender; }; #endif // __RIB_REGISTER_SERVER_HH__ xorp/rib/routemap.hh0000664000076400007640000000463711540224234014620 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/rib/routemap.hh,v 1.12 2008/10/02 21:58:12 bms Exp $ #ifndef __RIB_ROUTEMAP_HH__ #define __RIB_ROUTEMAP_HH__ #include "libxorp/xorp.h" #include "libxorp/ipv4net.hh" #include "libxorp/ipv6net.hh" #include "libxorp/nexthop.hh" #include "route.hh" class RMAction; class RMMatch; class RMRule; /** * @short RouteMap route filter (not yet working). */ class RouteMap { public: RouteMap(const string& mapname); int add_rule(RMRule* rule); string str() const; private: string _mapname; list _ruleset; }; /** * @short RouteMap rule (not yet working). */ class RMRule { public: RMRule(int seq, RMMatch* match, RMAction* action); int seq() const { return _seq; } string str() const; bool operator<(const RMRule& other) const { return (seq() < other.seq()); } private: int _seq; RMMatch* _match; RMAction* _action; }; /** * @short RouteMap conditional (not yet working). */ class RMMatch { public: RMMatch(); virtual ~RMMatch() {}; virtual string str() const = 0; virtual bool match_route(const RouteEntry& re) const = 0; private: }; /** * @short RouteMap conditional (not yet working). */ class RMMatchIPAddr : public RMMatch { public: RMMatchIPAddr(const IPv4Net& ipv4net); ~RMMatchIPAddr() {}; string str() const; bool match_route(const RouteEntry& re) const; private: IPv4Net _ipv4net; }; /** * @short RouteMap action (not yet working). */ class RMAction { public: RMAction(); string str() const; private: }; #endif // __RIB_ROUTEMAP_HH__ xorp/rib/rib.cc0000664000076400007640000012547611635757530013552 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "rib_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/c_format.hh" #include "register_server.hh" #include "rib_manager.hh" #include "rib.hh" // ---------------------------------------------------------------------------- // Inline table utility methods template struct table_has_name { table_has_name(const string& name) : _n(name) {} bool operator() (const RouteTable* rt) const { return rt->tablename() == _n; } private: const string& _n; }; template struct table_has_name_and_type { table_has_name_and_type(const string& name) : _n(name) {} bool operator() (const RouteTable* rt) const { const T* t = dynamic_cast(rt); return (t != 0) && (rt->tablename() == _n); } private: const string& _n; }; template inline RouteTable* RIB::find_table(const string& tablename) { typename list* >::iterator li; li = find_if(_tables.begin(), _tables.end(), table_has_name(tablename)); if (li == _tables.end()) { return NULL; } return *li; } template inline OriginTable* RIB::find_table_by_instance(const string& tablename, const string& target_class, const string& target_instance) { typename map* >::iterator mi; mi = _routing_protocol_instances.find(tablename + " " + target_class + " " + target_instance); if (mi == _routing_protocol_instances.end()) { return NULL; } return mi->second; } template Protocol* RIB::find_protocol(const string& protocol) { typename map::iterator mi = _protocols.find(protocol); if (mi == _protocols.end()) { return NULL; } return mi->second; } template inline int RIB::add_table(RouteTable* table) { const string& tablename = table->tablename(); if (find_table(tablename) != NULL) { XLOG_WARNING("add_table: table %s already exists", tablename.c_str()); return XORP_ERROR; } _tables.push_back(table); return XORP_OK; } template inline int RIB::remove_table(const string& tablename) { typename list* >::iterator li; li = find_if(_tables.begin(), _tables.end(), table_has_name(tablename)); if (li == _tables.end()) { XLOG_WARNING("remove_table: table %s doesn't exist", tablename.c_str()); return XORP_ERROR; } _tables.erase(li); return XORP_OK; } template int RIB::set_protocol_admin_distance(const string& protocol_name, const uint32_t& admin_distance) { map::iterator mi = _admin_distances.find(protocol_name); if (mi != _admin_distances.end()) { OriginTable* ot = dynamic_cast* >(find_table(protocol_name)); if (NULL != ot) { XLOG_ERROR("May not set an admin distance for protocol \"%s\", " "which has already instantiated an origin table.", protocol_name.c_str()); return XORP_ERROR; } } _admin_distances[protocol_name] = admin_distance; return XORP_OK; } template inline uint32_t RIB::get_protocol_admin_distance(const string& protocol_name) { map::iterator mi; mi = _admin_distances.find(protocol_name); if (mi == _admin_distances.end()) { XLOG_WARNING("Administrative distance of \"%s\" unknown.", protocol_name.c_str()); return UNKNOWN_ADMIN_DISTANCE; } return mi->second; } template inline RibVif* RIB::find_vif(const A& addr) { map::iterator iter; for (iter = _vifs.begin(); iter != _vifs.end(); ++iter) { RibVif* vif = iter->second; if (! vif->is_underlying_vif_up()) continue; // XXX: ignore vifs that are not up if (vif->is_my_addr(addr)) return vif; if (vif->is_p2p() && vif->is_same_p2p(addr)) return vif; } return NULL; } template inline IPExternalNextHop* RIB::find_external_nexthop(const A& addr) { typename map >::iterator mi; mi = _external_nexthops.find(addr); if (mi == _external_nexthops.end()) return NULL; return &mi->second; } template inline IPPeerNextHop* RIB::find_peer_nexthop(const A& addr) { typename map >::iterator mi; mi = _peer_nexthops.find(addr); if (mi == _peer_nexthops.end()) return NULL; return &mi->second; } template inline IPExternalNextHop* RIB::find_or_create_external_nexthop(const A& addr) { IPExternalNextHop* nexthop = find_external_nexthop(addr); if (nexthop != NULL) return nexthop; typedef map > C; // for convenience typename C::value_type vt(addr, IPExternalNextHop(addr)); typename C::iterator iter; iter = _external_nexthops.insert(_external_nexthops.end(), vt); return &iter->second; } template inline IPPeerNextHop* RIB::find_or_create_peer_nexthop(const A& addr) { IPPeerNextHop* nexthop = find_peer_nexthop(addr); if (nexthop != NULL) return nexthop; typedef map > C; // for convenience typename C::value_type vt(addr, addr); typename C::iterator iter; iter = _peer_nexthops.insert(_peer_nexthops.end(), vt); return &iter->second; } // ---------------------------------------------------------------------------- // Naming utility methods static inline string redist_tablename(const string& from_table) { return "Redist:" + from_table; } // ---------------------------------------------------------------------------- // RIB class template RIB::RIB(RibTransportType t, RibManager& rib_manager, EventLoop& eventloop) : _rib_manager(rib_manager), _eventloop(eventloop), _final_table(NULL), _register_table(NULL), _errors_are_fatal(false), _policy_redist_table(NULL) { if (t == UNICAST) { _multicast = false; } else if (t == MULTICAST) { _multicast = true; } else { XLOG_FATAL("Unknown RibTransportType."); } uint32_t static_distance = 1; char* v = getenv("XORP_RIB_STATIC_DISTANCE"); if (v) { static_distance = atoi(v); XLOG_INFO("Setting 'static' distance to: %u based on XORP_RIB_STATIC_DISTANCE environment variable.", static_distance); } // TODO: XXX: don't use hard-coded values below! _admin_distances["connected"] = CONNECTED_ADMIN_DISTANCE; _admin_distances["static"] = static_distance; _admin_distances["eigrp-summary"] = 5; _admin_distances["ebgp"] = 20; _admin_distances["eigrp-internal"] = 90; _admin_distances["igrp"] = 100; _admin_distances["ospf"] = 110; _admin_distances["is-is"] = 115; _admin_distances["rip"] = 120; _admin_distances["eigrp-external"] = 170; _admin_distances["ibgp"] = 200; _admin_distances["fib2mrib"] = 254; _admin_distances["unknown"] = UNKNOWN_ADMIN_DISTANCE; } template RIB::~RIB() { while (_tables.empty() == false) { delete _tables.front(); _tables.pop_front(); } while (! _protocols.empty()) { delete _protocols.begin()->second; _protocols.erase(_protocols.begin()); } while (_vifs.empty() == false) { delete _vifs.begin()->second; _vifs.erase(_vifs.begin()); } while (_deleted_vifs.empty() == false) { delete _deleted_vifs.begin()->second; _deleted_vifs.erase(_deleted_vifs.begin()); } } template list RIB::registered_protocol_names() const { list names; map::const_iterator iter; for (iter = _protocols.begin(); iter != _protocols.end(); ++iter) { names.push_back(iter->first); } return (names); } template void RIB::initialize(RegisterServer& register_server) { if (initialize_register(register_server) != XORP_OK) { XLOG_FATAL("Could not initialize register table for %s", name().c_str()); } if (initialize_policy_redist() != XORP_OK) { XLOG_FATAL("Could not initialize policy redistribution table for %s", name().c_str()); } // // XXX: we must initialize the final RedistTable after the // RegistTable has been initialized. // if (initialize_redist_all("all") != XORP_OK) { XLOG_FATAL("Could not initialize all-protocol redistribution " "table for %s", name().c_str()); } if (add_igp_table("connected", "", "") != XORP_OK) { XLOG_FATAL("Could not add igp table \"connected\" for %s", name().c_str()); } } template int RIB::initialize_policy_redist() { if (_register_table == NULL) { XLOG_ERROR("Register table is not yet initialized"); return XORP_ERROR; } if (_policy_redist_table != NULL) { return XORP_OK; // done already } _policy_redist_table = new PolicyRedistTable(_register_table, _rib_manager.xrl_router(), _rib_manager.policy_redist_map(), _multicast); if (add_table(_policy_redist_table) != XORP_OK) { delete _policy_redist_table; _policy_redist_table = NULL; return XORP_ERROR; } if (_final_table == NULL || _final_table == _register_table) _final_table = _policy_redist_table; return XORP_OK; } template int RIB::initialize_redist_all(const string& all) { if (_policy_redist_table == NULL) { XLOG_ERROR("Policy redist table is not yet initialized"); return XORP_ERROR; } if (find_table(redist_tablename(all)) != NULL) { return XORP_OK; // RedistTable already exists, no sweat } RedistTable* r; r = new RedistTable(redist_tablename(all), _policy_redist_table); if (add_table(r) != XORP_OK) { delete r; return XORP_ERROR; } // // Set the RedistTable as the final table // if (_final_table == NULL || _final_table == _policy_redist_table) _final_table = r; return XORP_OK; } template int RIB::initialize_register(RegisterServer& register_server) { if (_register_table != NULL) { XLOG_WARNING("Register table already initialized."); return XORP_ERROR; } RegisterTable* rt; rt = new RegisterTable("RegisterTable", register_server, _multicast); if (add_table(rt) != XORP_OK) { XLOG_WARNING("Add RegisterTable failed."); delete rt; return XORP_ERROR; } _register_table = rt; if (_final_table == NULL) { _final_table = _register_table; } else { _final_table->replumb(NULL, _register_table); _register_table->set_next_table(_final_table); } return XORP_OK; } template int RIB::new_origin_table(const string& tablename, const string& target_class, const string& target_instance, uint32_t admin_distance, ProtocolType protocol_type) { OriginTable* ot = new OriginTable(tablename, admin_distance, protocol_type, _eventloop); if (ot == NULL || add_table(ot) != XORP_OK) { XLOG_WARNING("Could not add origin table %s", tablename.c_str()); delete ot; return XORP_ERROR; } if (_final_table == NULL) { _final_table = ot; } // // Store the XRL target instance, so we know which OriginTable to // shutdown if the routing protocol dies. // if (!target_instance.empty()) { _rib_manager.register_interest_in_target(target_class); _routing_protocol_instances[tablename + " " + target_class + " " + target_instance] = ot; } return XORP_OK; } template int RIB::add_connected_route(const RibVif& vif, const IPNet& net, const A& nexthop_addr, const A& peer_addr) { // // XXX: the connected routes are added with the // best possible metric (0). // add_route("connected", net, nexthop_addr, "", "", 0, PolicyTags()); if (vif.is_p2p() && (peer_addr != A::ZERO()) && (! net.contains(peer_addr))) { add_route("connected", IPNet(peer_addr, A::addr_bitlen()), peer_addr, "", "", 0, PolicyTags()); } return XORP_OK; } template int RIB::delete_connected_route(const RibVif& vif, const IPNet& net, const A& peer_addr) { delete_route("connected", net); if (vif.is_p2p() && (peer_addr != A::ZERO()) && (! net.contains(peer_addr))) { delete_route("connected", IPNet(peer_addr, A::addr_bitlen())); } return XORP_OK; } template int RIB::new_vif(const string& vifname, const Vif& vif) { map::iterator vi; RibVif* new_rib_vif = NULL; debug_msg("RIB::new_vif: %s\n", vifname.c_str()); if (_vifs.find(vifname) != _vifs.end()) return XORP_ERROR; // // If the vif is pending deletion, then reuse it instead // vi = _deleted_vifs.find(vifname); if (vi != _deleted_vifs.end()) { // Reuse previously deleted vif new_rib_vif = vi->second; new_rib_vif->set_deleted(false); _deleted_vifs.erase(vi); new_rib_vif->copy_in(vif); } else { // Create a new vif new_rib_vif = new RibVif(this, vif); } XLOG_ASSERT(new_rib_vif != NULL); _vifs[vifname] = new_rib_vif; if (new_rib_vif->is_underlying_vif_up()) { // // Add the directly connected routes associated with this vif // list::const_iterator ai; for (ai = new_rib_vif->addr_list().begin(); ai != new_rib_vif->addr_list().end(); ++ai) { if (ai->addr().af() != A::af()) continue; IPNet subnet_addr; A addr, peer_addr; ai->subnet_addr().get(subnet_addr); ai->addr().get(addr); ai->peer_addr().get(peer_addr); add_connected_route(*new_rib_vif, subnet_addr, addr, peer_addr); } } return XORP_OK; } template int RIB::delete_vif(const string& vifname) { debug_msg("RIB::delete_vif: %s\n", vifname.c_str()); map::iterator vi = _vifs.find(vifname); if (vi == _vifs.end()) { return XORP_ERROR; } RibVif* rib_vif = vi->second; if (rib_vif->is_underlying_vif_up()) { // // Delete the directly connected routes associated with this vif // list::const_iterator ai; for (ai = rib_vif->addr_list().begin(); ai != rib_vif->addr_list().end(); ++ai) { if (ai->addr().af() != A::af()) continue; IPNet subnet_addr; A peer_addr; ai->subnet_addr().get(subnet_addr); ai->peer_addr().get(peer_addr); delete_connected_route(*rib_vif, subnet_addr, peer_addr); } } _vifs.erase(vi); // // If the vif is still used by some routes, then add it to the // container with vifs pending deletion. // Otherwise just delete it. // if (rib_vif->usage_counter() > 0) { XLOG_ASSERT(_deleted_vifs.find(vifname) == _deleted_vifs.end()); _deleted_vifs[vifname] = rib_vif; rib_vif->set_deleted(true); } else { delete rib_vif; } return XORP_OK; } template void RIB::destroy_deleted_vif(RibVif* rib_vif) { map::iterator vi = _deleted_vifs.find(rib_vif->name()); XLOG_ASSERT(vi != _deleted_vifs.end()); XLOG_ASSERT(vi->second == rib_vif); _deleted_vifs.erase(vi); delete rib_vif; } template int RIB::set_vif_flags(const string& vifname, bool is_p2p, bool is_loopback, bool is_multicast, bool is_broadcast, bool is_up, uint32_t mtu) { map::iterator vi = _vifs.find(vifname); if (vi == _vifs.end()) { XLOG_ERROR("Attempting to set flags to non-existant Vif \"%s\"", vifname.c_str()); return XORP_ERROR; } RibVif* vif = vi->second; bool old_is_up = vif->is_underlying_vif_up(); vif->set_p2p(is_p2p); vif->set_loopback(is_loopback); vif->set_multicast_capable(is_multicast); vif->set_broadcast_capable(is_broadcast); vif->set_underlying_vif_up(is_up); vif->set_mtu(mtu); if (old_is_up == is_up) return XORP_OK; list::const_iterator ai; if (is_up) { // // Add all connected routes // for (ai = vif->addr_list().begin(); ai != vif->addr_list().end(); ++ai) { if (ai->addr().af() != A::af()) continue; IPNet subnet_addr; A addr, peer_addr; ai->subnet_addr().get(subnet_addr); ai->addr().get(addr); ai->peer_addr().get(peer_addr); add_connected_route(*vif, subnet_addr, addr, peer_addr); } } if (! is_up) { // // Delete all connected routes // for (ai = vif->addr_list().begin(); ai != vif->addr_list().end(); ++ai) { if (ai->addr().af() != A::af()) continue; IPNet subnet_addr; A peer_addr; ai->subnet_addr().get(subnet_addr); ai->peer_addr().get(peer_addr); delete_connected_route(*vif, subnet_addr, peer_addr); } } return XORP_OK; } template int RIB::add_vif_address(const string& vifname, const A& addr, const IPNet& subnet, const A& broadcast_addr, const A& peer_addr) { map::iterator vi = _vifs.find(vifname); if (vi == _vifs.end()) { XLOG_ERROR("Attempting to add address to non-existant Vif \"%s\"", vifname.c_str()); return XORP_ERROR; } RibVif* vif = vi->second; vif->add_address(VifAddr(addr, subnet, broadcast_addr, peer_addr)); if (vif->is_underlying_vif_up()) add_connected_route(*vif, subnet, addr, peer_addr); return XORP_OK; } template int RIB::delete_vif_address(const string& vifname, const A& addr) { map::iterator vi = _vifs.find(vifname); if (vi == _vifs.end()) { XLOG_ERROR("Attempting to delete address from non-existant Vif \"%s\"", vifname.c_str()); return XORP_ERROR; } RibVif* vif = vi->second; list::const_iterator ai; for (ai = vif->addr_list().begin(); ai != vif->addr_list().end(); ++ai) { const IPvX& ipvx = ai->addr(); if (ipvx.af() != A::af()) continue; if (ipvx != IPvX(addr)) continue; IPNet subnet_addr; A peer_addr; ai->subnet_addr().get(subnet_addr); ai->peer_addr().get(peer_addr); vif->delete_address(ipvx); if (vif->is_underlying_vif_up()) delete_connected_route(*vif, subnet_addr, peer_addr); return XORP_OK; } return XORP_ERROR; } template int RIB::add_route(const string& tablename, const IPNet& net, const A& nexthop_addr, const string& ifname, const string& vifname, uint32_t metric, const PolicyTags& policytags) { UNUSED(ifname); RouteTable* rt = find_table(tablename); if (rt == NULL) { if (_errors_are_fatal) { XLOG_FATAL("Attempting to add route to unknown table \"%s\".", tablename.c_str()); } else { XLOG_ERROR("Attempting to add route to unknown table \"%s\".", tablename.c_str()); return XORP_ERROR; } } Protocol* protocol = find_protocol(tablename); if (protocol == NULL) { if (_errors_are_fatal) { XLOG_FATAL("Attempting to add route with unknown protocol \"%s\".", tablename.c_str()); } else { XLOG_ERROR("Attempting to add route with unknown protocol \"%s\".", tablename.c_str()); return XORP_ERROR; } } OriginTable* ot = dynamic_cast* >(rt); if (ot == NULL) { if (_errors_are_fatal) { XLOG_FATAL("Attempting to add route to table \"%s\" that is not " "an origin table.", tablename.c_str()); } else { XLOG_ERROR("Attempting to add route to table \"%s\" that is not " "an origin table.", tablename.c_str()); return XORP_ERROR; } } if (! vifname.empty()) { // // Add a route with explicitly specified network interface // map::iterator iter = _vifs.find(vifname); if (iter == _vifs.end()) { XLOG_ERROR("Attempting to add route to table \"%s\" " "(prefix %s next-hop %s ifname %s vifname %s): " "no such network interface", tablename.c_str(), net.str().c_str(), nexthop_addr.str().c_str(), ifname.c_str(), vifname.c_str()); return XORP_ERROR; } RibVif* vif = iter->second; IPNextHop* nexthop = find_or_create_peer_nexthop(nexthop_addr); ot->add_route(IPRouteEntry(net, vif, nexthop, *protocol, metric, policytags)); flush(); return XORP_OK; } // // Find the vif so we can see if the nexthop is directly connected // RibVif* vif = NULL; IPNextHop* nexthop = NULL; do { // // Search for a route to a directly-connected destination // const IPRouteEntry* re = _final_table->lookup_route(nexthop_addr); if (re != NULL) { // We found a route for the nexthop vif = re->vif(); if ((vif != NULL) && (vif->is_underlying_vif_up()) && (vif->is_same_subnet(IPvXNet(re->net())) || vif->is_same_p2p(IPvX(nexthop_addr)))) { debug_msg("**directly connected route found for nexthop\n"); break; } } // // We failed to find a route, or the route wasn't for a // directly-connected destination. One final possibility is that // we are trying to add a route for a directly connected subnet, // so we need to test all the Vifs to see if this is the case. // vif = find_vif(nexthop_addr); debug_msg("Vif %p\n", vif); break; } while (false); if (vif == NULL) { debug_msg("**not directly connected route found for nexthop\n"); // // XXX: If the route came from an IGP, then we must have // a directly-connected interface toward the next-hop router. // if (protocol->protocol_type() == IGP) { XLOG_ERROR("Attempting to add IGP route to table \"%s\" " "(prefix %s next-hop %s): no directly connected " "interface toward the next-hop router", tablename.c_str(), net.str().c_str(), nexthop_addr.str().c_str()); return XORP_ERROR; } } if (vif != NULL) { nexthop = find_or_create_peer_nexthop(nexthop_addr); } else { nexthop = find_or_create_external_nexthop(nexthop_addr); } XLOG_ASSERT(nexthop->addr() == nexthop_addr); // // Add the route // ot->add_route(IPRouteEntry(net, vif, nexthop, *protocol, metric, policytags)); flush(); return XORP_OK; } template int RIB::replace_route(const string& tablename, const IPNet& net, const A& nexthop_addr, const string& ifname, const string& vifname, uint32_t metric, const PolicyTags& policytags) { UNUSED(ifname); RouteTable* rt = find_table(tablename); if (NULL == rt) return XORP_ERROR; // Table does not exist OriginTable* ot = dynamic_cast* >(rt); if (NULL == ot) return XORP_ERROR; // Table is not an origin table int response = ot->delete_route(net); if (response != XORP_OK) return response; response = add_route(tablename, net, nexthop_addr, ifname, vifname, metric, policytags); // No need to flush here, as add_route will do it for us. return response; } template int RIB::delete_route(const string& tablename, const IPNet& net) { RouteTable* rt = find_table(tablename); if (NULL == rt) return XORP_ERROR; // Table does not exist OriginTable* ot = dynamic_cast* >(rt); if (NULL == ot) return XORP_ERROR; // Table is not an origin table int result = ot->delete_route(net); flush(); return result; } template void RIB::flush() { if (_register_table != NULL) _register_table->flush(); if (_final_table != NULL && _final_table != _register_table) _final_table->flush(); } template int RIB::verify_route(const A& lookup_addr, const string& ifname, const A& nexthop_addr, uint32_t metric, RibVerifyType matchtype) { const IPRouteEntry* re; // 1. Check for an expected route miss. // 2. Check table entry validity and existence. re = _final_table->lookup_route(lookup_addr); if (re == NULL || re->vif() == NULL) { if (matchtype == RibVerifyType(MISS)) { debug_msg("****ROUTE MISS SUCCESSFULLY VERIFIED****\n"); return XORP_OK; } if (re == NULL) { debug_msg("RouteVerify: Route Lookup failed\n"); } else { debug_msg("Route lookup returned NULL vif: %s\n", re->str().c_str()); } return XORP_ERROR; } #ifdef notyet // 3a. Check for discard (blackhole) routes. // XXX: re->vif() must be non-NULL and valid. Revisit this in future. DiscardNextHop* dnh = dynamic_cast(re->nexthop()); if (matchtype == RibVerifyType(DISCARD)) { if (dnh == NULL) { debug_msg("Next hop is not a DiscardNextHop"); return XORP_ERROR; } else { debug_msg("****DISCARD ROUTE SUCCESSFULLY VERIFIED****\n"); return XORP_OK; } } // 3b. Check for unreachable routes. // XXX: re->vif() must be non-NULL and valid. Revisit this in future. UnreachableNextHop* unh = dynamic_cast(re->nexthop()); if (matchtype == RibVerifyType(UNREACHABLE)) { if (unh == NULL) { debug_msg("Next hop is not an UnreachableNextHop"); return XORP_ERROR; } else { debug_msg("****UNREACHABLE ROUTE SUCCESSFULLY VERIFIED****\n"); return XORP_OK; } } #endif // 4. Check for protocol level routes (specifically IP). IPNextHop* route_nexthop = dynamic_cast* >(re->nexthop()); if (route_nexthop == NULL) { debug_msg("Next hop is not an IPNextHop\n"); return XORP_ERROR; } else if ((nexthop_addr != route_nexthop->addr())) { debug_msg("NextHop: Exp: %s != Got: %s\n", nexthop_addr.str().c_str(), route_nexthop->addr().str().c_str()); return XORP_ERROR; } else { debug_msg("NextHop: Exp: %s != Got: %s\n", nexthop_addr.str().c_str(), route_nexthop->addr().str().c_str()); } if (ifname != re->vif()->name()) { XLOG_ERROR("Interface \"%s\" does not match expected \"%s\".", re->vif()->str().c_str(), ifname.c_str()); return XORP_ERROR; } else { debug_msg("Ifname: Exp: %s == Got: %s\n", ifname.c_str(), re->vif()->name().c_str()); } if (metric != re->metric()) { XLOG_ERROR("Metric \"%u\" does not match expected \"%u\".", XORP_UINT_CAST(re->metric()), XORP_UINT_CAST(metric)); return XORP_ERROR; } else { debug_msg("Metric: Exp: %u == Got: %u\n", XORP_UINT_CAST(metric), XORP_UINT_CAST(re->metric())); } debug_msg("****IP ROUTE SUCCESSFULLY VERIFIED****\n"); return XORP_OK; } template const A& RIB::lookup_route(const A& lookupaddr) { debug_msg("looking up %s\n", lookupaddr.str().c_str()); const IPRouteEntry* re = _final_table->lookup_route(lookupaddr); // Case 1: Route miss. Return the null IP address. // Vif cannot be NULL for a valid route. if (re == NULL || re->vif() == NULL) return A::ZERO(); #ifdef notyet DiscardNextHop* discard_nexthop = dynamic_cast(re->nexthop()); // Case 2a: Discard route. Return the loopback address. if (discard_nexthop != NULL) return A::LOOPBACK(); UnreachableNextHop* unreachable_nexthop = dynamic_cast(re->nexthop()); // Case 2b: Unreachable route. Return the loopback address. if (unreachable_nexthop != NULL) return A::LOOPBACK(); IPNextHop* ip_nexthop = dynamic_cast* >(re->nexthop()); // Case 3: IP protocol route. Return the nexthop address. if (ip_nexthop != NULL) return ip_nexthop->addr(); // Default: Return the null IP address. return A::ZERO(); #else // Default: Assume the route points to a resolved IPNextHop. IPNextHop* route_nexthop = static_cast* >(re->nexthop()); return route_nexthop->addr(); #endif } template RouteRange* RIB::route_range_lookup(const A& lookupaddr) { return _final_table->lookup_route_range(lookupaddr); } template RouteRegister* RIB::route_register(const A& lookupaddr, const string& module) { debug_msg("registering %s\n", lookupaddr.str().c_str()); return _register_table->register_route_range(lookupaddr, module); } template int RIB::route_deregister(const IPNet& subnet, const string& module) { debug_msg("deregistering %s\n", subnet.str().c_str()); return _register_table->deregister_route_range(subnet, module); } template RedistTable* RIB::protocol_redist_table(const string& protocol) { RouteTable* rt = find_table(redist_tablename(protocol)); if (rt != NULL) { return dynamic_cast*>(rt); } return NULL; } template int RIB::add_igp_table(const string& tablename, const string& target_class, const string& target_instance) { debug_msg("add_igp_table %s\n", tablename.c_str()); int r = add_origin_table(tablename, target_class, target_instance, IGP); if (r != XORP_OK) return r; // XXX For now we unconditionally plumb a RedistTable behind each // OriginTable. We do this because the RedistTable needs to track // the routes within the OriginTable in order to be able to render // a dump when another protocol requests redistribution. r = add_redist_table(tablename); if (r != XORP_OK) { delete_origin_table(tablename, target_class, target_instance); return r; } RouteTable* rt = find_table(redist_tablename(tablename)); XLOG_ASSERT(rt != NULL); if (tablename == "connected") { r = add_policy_connected_table(rt->tablename()); if (r != XORP_OK) { delete_origin_table(tablename, target_class, target_instance); // // XXX: we don't delete the redist table but keep it around, // because delete_origin_table() does similar. // return r; } } return r; } template int RIB::add_egp_table(const string& tablename, const string& target_class, const string& target_instance) { debug_msg("add_egp_table %s\n", tablename.c_str()); int r = add_origin_table(tablename, target_class, target_instance, EGP); if (r != XORP_OK) { return r; } #if 0 // XXX For now we unconditionally plumb a RedistTable behind each // OriginTable. We need this because the RedistTable needs to // track the routes within the OriginTable in order to be able to // render a dump when another protocol requests redistribution. r = add_redist_table(tablename); if (r != XORP_OK) { delete_origin_table(tablename, target_class, target_instance); return r; } RouteTable* rt = find_table(redist_tablename(tablename)); XLOG_ASSERT(rt != NULL); #endif return r; } template int RIB::add_policy_connected_table(const string& parent_tablename) { RouteTable* parent = find_table(parent_tablename); if (parent == NULL) { XLOG_WARNING("add_policy_connected_table: parent table %s does not exist", parent_tablename.c_str()); return XORP_ERROR; } if (find_table(PolicyConnectedTable::table_name) != NULL) return XORP_OK; PolicyConnectedTable* pt = new PolicyConnectedTable(parent, _rib_manager.policy_filters()); if (add_table(pt) != XORP_OK) { delete pt; return XORP_ERROR; } return XORP_OK; } template int RIB::add_redist_table(const string& parent_tablename) { RouteTable* p = find_table(parent_tablename); if (p == NULL) { XLOG_WARNING("add_redist_table: parent table %s does not exist", parent_tablename.c_str()); return XORP_ERROR; } if (find_table(redist_tablename(parent_tablename)) != NULL) { return XORP_OK; // RedistTable already exists, no sweat } RedistTable* r; r = new RedistTable(redist_tablename(parent_tablename), p); if (add_table(r) != XORP_OK) { delete r; return XORP_ERROR; } return XORP_OK; } // // All the magic is in add_origin_table. // TODO: XXX: split into smaller units (??) template int RIB::add_origin_table(const string& tablename, const string& target_class, const string& target_instance, ProtocolType protocol_type) { debug_msg("add_origin_table %s type: %d\n", tablename.c_str(), protocol_type); Protocol* protocol = find_protocol(tablename); if (protocol == NULL) { protocol = new Protocol(tablename, protocol_type, 0); _protocols[tablename] = protocol; } else { protocol->increment_genid(); } // Check if table exists and check type if so RouteTable* rt = find_table(tablename); if (rt != NULL) { OriginTable* ot = dynamic_cast* >(rt); if (ot == NULL) { XLOG_ERROR("add_origin_table: table \"%s\" already exists, but is " "not is an OriginTable.", tablename.c_str()); return XORP_ERROR; } else { // // Table already exists, hence use it. // // // Store the XRL target instance, so we know which OriginTable to // shutdown if the routing protocol dies. // if (!target_instance.empty()) { _rib_manager.register_interest_in_target(target_class); _routing_protocol_instances[tablename + " " + target_class + " " + target_instance] = ot; } return XORP_OK; } } if (new_origin_table(tablename, target_class, target_instance, get_protocol_admin_distance(tablename), protocol_type) != XORP_OK) { debug_msg("new_origin_table failed\n"); return XORP_ERROR; } OriginTable* new_table = static_cast* >(find_table(tablename)); // XXX: the table was created by new_origin_table() above, so it must exist XLOG_ASSERT(new_table != NULL); if (_final_table == new_table) { // // This is the first table, so no need to plumb anything. // debug_msg("first table\n"); return XORP_OK; } // // There are existing tables, so we need to do some plumbing // // Three possibilities: // 1. there are existing EGP tables but no existing IGP table // 2. there are both existing EGP and existing IGP tables // 3. there are existing IGP tables but no existing EGP table // // If we're adding an IGP table: // we can handle 2 and 3 the same, adding a MergedTable, but 1 // requires we construct an ExtInt table instead. // // If we're adding an EGP table: // we can handle 1 and 2 the same, adding a MergedTable, but 3 // requires we construct an ExtInt table instead. // // First step: find out what tables already exist RouteTable* igp_table = NULL; RouteTable* egp_table = NULL; ExtIntTable* ei_table = NULL; typename list* >::iterator li; for (li = _tables.begin(); li != _tables.end(); ++li) { // XXX: skip the new table! RouteTable* current = *li; if (current == new_table) { continue; } OriginTable* ot = dynamic_cast* >(current); if (ot != NULL) { if (ot->protocol_type() == IGP) { igp_table = ot; } else if (ot->protocol_type() == EGP) { egp_table = ot; } else { XLOG_UNREACHABLE(); } continue; } else { if (ei_table == NULL) ei_table = dynamic_cast* >(current); } } // // Depending on which tables already exist, we may need to create // a MergedTable or an ExtInt table. // if (((igp_table == NULL) && (protocol_type == IGP)) || ((egp_table == NULL) && (protocol_type == EGP))) { // // Sanity check: we've found an ExtIntTable, when there // weren't both IGP and EGP tables. // XLOG_ASSERT(ei_table == NULL); if ((egp_table == NULL) && (igp_table == NULL)) { // // There are tables, but neither IGP or EGP origin tables // Therefore the final table must be a RedistTable // or a RegisterTable. // if ((_final_table->type() != REDIST_TABLE) && (_final_table->type() != POLICY_REDIST_TABLE) && (_final_table->type() != REGISTER_TABLE)) { XLOG_UNREACHABLE(); } // // There may be existing single-parent tables before the // final table such as RegisterTable - track back to be // first of them. // RouteTable* rt = track_back(_final_table, REDIST_TABLE | POLICY_REDIST_TABLE | REGISTER_TABLE); // // Plumb our new table in ahead of the first single-parent // table. // rt->replumb(NULL, new_table); new_table->set_next_table(rt); return XORP_OK; } // // We are going to need to create an ExtInt table. // // Find the appropriate existng table to be a parent // of the ExtIntTable. // RouteTable* next_table = track_back(_final_table, REDIST_TABLE | POLICY_REDIST_TABLE | REGISTER_TABLE); RouteTable* existing_table = next_table->parent(); if (protocol_type == IGP) { ei_table = new ExtIntTable(existing_table, new_table); } else { ei_table = new ExtIntTable(new_table, existing_table); } // XXX: Added table to list of resources (was not done previously) if (add_table(ei_table) != XORP_OK) { XLOG_ERROR("Failed to add ExtIntTable \"%s\".", ei_table->tablename().c_str()); delete ei_table; return XORP_ERROR; } if (_final_table->type() & (REDIST_TABLE | POLICY_REDIST_TABLE | REGISTER_TABLE)) { ei_table->set_next_table(next_table); next_table->replumb(existing_table, ei_table); } else { _final_table = ei_table; } return XORP_OK; } // // We're going to need to create a MergedTable // RouteTable* existing_table = (protocol_type == IGP) ? igp_table : egp_table; RouteTable* next_table = existing_table->next_table(); // Skip past any RedistTables RouteTable* new_prev_table = track_forward(existing_table, (REDIST_TABLE | POLICY_CONNECTED_TABLE)); if (new_prev_table != existing_table) { existing_table = new_prev_table; next_table = existing_table->next_table(); } MergedTable* merged_table = new MergedTable(existing_table, new_table); if (merged_table == NULL || add_table(merged_table) != XORP_OK) { delete merged_table; return XORP_ERROR; } merged_table->set_next_table(next_table); if (next_table != NULL) next_table->replumb(existing_table, merged_table); // // It's possible existing_table was the last table - if so, then it // isn't anymore. // if (_final_table == existing_table) _final_table = merged_table; return XORP_OK; } template int RIB::delete_igp_table(const string& tablename, const string& target_class, const string& target_instance) { return delete_origin_table(tablename, target_class, target_instance); } template int RIB::delete_egp_table(const string& tablename, const string& target_class, const string& target_instance) { return delete_origin_table(tablename, target_class, target_instance); } template int RIB::delete_origin_table(const string& tablename, const string& target_class, const string& target_instance) { OriginTable* ot = dynamic_cast* >(find_table(tablename)); if (NULL == ot) return XORP_ERROR; if (!target_instance.empty()) { if (find_table_by_instance(tablename, target_class, target_instance) != ot) { XLOG_ERROR("Got delete_origin_table for wrong target name\n"); return XORP_ERROR; } else { _routing_protocol_instances.erase(tablename + " " + target_class + " " + target_instance); } } // Remove all the routes this table used to originate, but keep table ot->routing_protocol_shutdown(); return XORP_OK; } template void RIB::target_death(const string& target_class, const string& target_instance) { string s = " " + target_class + " " + target_instance; typename map* >::iterator iter; for (iter = _routing_protocol_instances.begin(); iter != _routing_protocol_instances.end(); ++iter) { if (iter->first.find(s) != string::npos) { // We've found the target. XLOG_INFO("Received death event for protocol %s shutting down %s", target_class.c_str(), iter->second->str().c_str()); iter->second->routing_protocol_shutdown(); _routing_protocol_instances.erase(iter); // No need to go any further. return; } } } // // Given a single-parent table, track back to the last matching table // before this one. // template RouteTable* RIB::track_back(RouteTable* rt, int typemask) const { if (rt == NULL || (rt->type() & typemask) == 0) { return rt; } for (RouteTable* parent = rt->parent(); parent && (parent->type() & typemask); parent = rt->parent()) { rt = parent; } return rt; } // // Track forward to the last matching table, or return this table if // the next table doesn't match. // template RouteTable* RIB::track_forward(RouteTable* rt, int typemask) const { debug_msg("here1\n"); RouteTable* next; if (NULL == rt) { // XXX: not the same test as track back (deliberate ?) return rt; } next = rt->next_table(); debug_msg("here2\n"); while (next != NULL) { debug_msg("here3\n"); if ((next->type() & typemask) != 0) { debug_msg("here4 next->type()= %d, typemask=%x\n", next->type(), typemask); rt = next; next = rt->next_table(); } else { debug_msg("here5\n"); return rt; } } debug_msg("here6\n"); return rt; } template void RIB::print_rib() const { #ifdef DEBUG_LOGGING typename list* >::const_iterator pair; pair = _tables.begin(); // XXX: this is printf not debug_msg for a reason. printf("==============================================================\n"); while (pair != _tables.end()) { RouteTable* rt = *pair; printf("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n"); printf("%s", rt->str().c_str()); rt = rt->next_table(); while (rt != NULL) { printf("%s", rt->str().c_str()); rt = rt->next_table(); } ++pair; } printf("==============================================================\n"); #endif // DEBUG_LOGGING } template string RIB::name() const { return c_format("%s %s RIB", (_multicast) ? "Multicast" : "Unicast", A::ip_version_str().c_str()); } template void RIB::push_routes() { RouteTable* rt = find_table(PolicyConnectedTable::table_name); XLOG_ASSERT(rt != NULL); PolicyConnectedTable* pct = dynamic_cast*>(rt); XLOG_ASSERT(pct != NULL); pct->push_routes(); } template class RIB; #ifdef HAVE_IPV6 template class RIB; #endif //ipv6 xorp/rib/protocol.hh0000664000076400007640000000542311635757530014636 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __RIB_PROTOCOL_HH__ #define __RIB_PROTOCOL_HH__ #include "libxorp/xorp.h" enum ProtocolType { IGP = 1, // Interior Gateway Protocol EGP = 2 // Exterior Gateway Protocol }; enum ProtocolAdminDistance { CONNECTED_ADMIN_DISTANCE = 0, // Only for connected routes. UNKNOWN_ADMIN_DISTANCE = 255, UNSET_ADMIN_DISTANCE = 256, MAX_ADMIN_DISTANCE = 65535 }; /** * @short Routing protocol information. * * Protocol holds information related to a specific routing protocol * that is supplying information to the RIB. */ class Protocol { public: /** * Protocol constuctor * * @param name the canonical name for the routing protocol. * @param protocol_type the routing protocol type (@ref ProtocolType). * @param genid the generation ID for the protocol (if the * protocol goes down and comes up, the genid should be * incremented). */ Protocol(const string& name, ProtocolType protocol_type, uint32_t genid); /** * @return the protocol type. * @see ProtocolType */ ProtocolType protocol_type() const { return _protocol_type; } /** * @return the canonical name of the routing protocol. */ const string& name() const { return _name; } /** * Equality Operator * * Two Protocol instances are equal if they match only in name. * * @param other the right-hand operand to compare against. * @return true if the left-hand Protocol instance is equal to * the right-hand protocol instance. */ bool operator==(const Protocol& other) const { return (name() == other.name()); } /** * Increment the protocol generation ID. */ void increment_genid() { _genid++; } private: string _name; ProtocolType _protocol_type; // The protocol type (IGP or EGP) uint32_t _genid; // The protocol generation ID. }; #endif // __RIB_PROTOCOL_HH__ xorp/rib/route.cc0000664000076400007640000000362311421137511014101 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2007-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "rib_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/c_format.hh" #include "rib.hh" #include "route.hh" RouteEntry::RouteEntry(RibVif* vif, NextHop* nexthop, const Protocol& protocol, uint32_t metric) : _vif(vif), _nexthop(nexthop), _protocol(protocol), _admin_distance(UNKNOWN_ADMIN_DISTANCE), _metric(metric) { if (_vif != NULL) _vif->incr_usage_counter(); } RouteEntry::~RouteEntry() { if (_vif != NULL) _vif->decr_usage_counter(); } template string IPRouteEntry::str() const { string dst = (_net.is_valid()) ? _net.str() : string("NULL"); string vif = (_vif) ? string(_vif->name()) : string("NULL"); return string("Dst: ") + dst + string(" Vif: ") + vif + string(" NextHop: ") + _nexthop->str() + string(" Metric: ") + c_format("%d", _metric) + string(" Protocol: ") + _protocol.name() + string(" PolicyTags: ") + _policytags.str(); } template class IPRouteEntry; template class IPRouteEntry; xorp/rib/rt_tab_deletion.cc0000664000076400007640000001727111540224234016106 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "rib_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/eventloop.hh" #include "rt_tab_deletion.hh" // // A = Address Type. E.g., IPv4 or IPv6 // template DeletionTable::DeletionTable(const string& tablename, RouteTable* parent, Trie* >* ip_route_trie, EventLoop& eventloop) : RouteTable(tablename), _parent(parent), _eventloop(eventloop), _ip_route_table(ip_route_trie) { XLOG_ASSERT(_parent != NULL); this->set_next_table(_parent->next_table()); this->next_table()->replumb(parent, this); parent->set_next_table(this); // Callback immediately, but after network events or expired timers _background_deletion_timer = _eventloop.new_oneoff_after_ms( 0, callback(this, &DeletionTable::background_deletion_pass)); } template DeletionTable::~DeletionTable() { // Delete all the routes in the trie. delete_all_routes(); delete _ip_route_table; } template int DeletionTable::add_route(const IPRouteEntry& route, RouteTable* caller) { XLOG_ASSERT(caller == _parent); typename Trie* >::iterator iter; iter = _ip_route_table->lookup_node(route.net()); if (iter != _ip_route_table->end()) { // // We got an add route for a route that was waiting to be // deleted. Process this now - pass the deletion downstream, // remove the route from our trie, then pass the new route // downstream. // const IPRouteEntry* our_route = iter.payload(); _ip_route_table->erase(route.net()); this->next_table()->delete_route(our_route, this); delete our_route; } return this->next_table()->add_route(route, this); } template int DeletionTable::delete_route(const IPRouteEntry* route, RouteTable* caller) { XLOG_ASSERT(caller == _parent); // The route MUST NOT be in our trie. typename Trie* >::iterator iter; iter = _ip_route_table->lookup_node(route->net()); XLOG_ASSERT(iter == _ip_route_table->end()); return this->next_table()->delete_route(route, this); } template void DeletionTable::delete_all_routes() { typename Trie* >::iterator iter; for (iter = _ip_route_table->begin(); iter != _ip_route_table->end(); ++iter) { delete iter.payload(); } _ip_route_table->delete_all_nodes(); } template const IPRouteEntry* DeletionTable::lookup_route(const IPNet& net) const { const IPRouteEntry* parent_route = _parent->lookup_route(net); typename Trie* >::iterator iter; iter = _ip_route_table->lookup_node(net); if (parent_route != NULL) { // // If we succeeded in looking up the route in the parent table, // then the route MUST NOT be in our deletion table. // XLOG_ASSERT(iter == _ip_route_table->end()); return parent_route; } else { // // While we hold routes to be deleted, we haven't told anyone // downstream they've gone yet. We have to pretend they're // still there (for consistency reasons) until we've got round // to telling downstream that they've actually gone. // return (iter == _ip_route_table->end()) ? NULL : iter.payload(); } } template const IPRouteEntry* DeletionTable::lookup_route(const A& addr) const { const IPRouteEntry* parent_route = _parent->lookup_route(addr); typename Trie* >::iterator iter; iter = _ip_route_table->find(addr); if (parent_route != NULL) { if (iter == _ip_route_table->end()) { return parent_route; } else { // // Both our parent and ourselves have a route. We need to // return the more specific route. If the two are the same // this is a fatal error. // const IPRouteEntry* our_route = iter.payload(); XLOG_ASSERT(our_route->prefix_len() != parent_route->prefix_len()); if (our_route->prefix_len() > parent_route->prefix_len()) { return our_route; } else { return parent_route; } } XLOG_UNREACHABLE(); } // // While we hold routes to be deleted, we haven't told anyone // downstream they've gone yet. We have to pretend they're // still there (for consistency reasons) until we've got round // to telling downstream that they've actually gone. // return (iter == _ip_route_table->end()) ? NULL : iter.payload(); } template RouteRange* DeletionTable::lookup_route_range(const A& addr) const { const IPRouteEntry* route; typename Trie* >::iterator iter; iter = _ip_route_table->find(addr); if (iter == _ip_route_table->end()) route = NULL; else route = iter.payload(); A bottom_addr, top_addr; _ip_route_table->find_bounds(addr, bottom_addr, top_addr); RouteRange* rr = new RouteRange(addr, route, top_addr, bottom_addr); debug_msg("Deletion Table: %s returning lower bound for %s of %s\n", this->tablename().c_str(), addr.str().c_str(), bottom_addr.str().c_str()); debug_msg("Deletion Table: %s returning upper bound for %s of %s\n", this->tablename().c_str(), addr.str().c_str(), top_addr.str().c_str()); // Merge our own route range with that of our parent. RouteRange* parent_rr = _parent->lookup_route_range(addr); rr->merge(parent_rr); delete parent_rr; return rr; } template void DeletionTable::background_deletion_pass() { if (_ip_route_table->begin() == _ip_route_table->end()) { unplumb_self(); return; } typename Trie* >::iterator iter; iter = _ip_route_table->begin(); const IPRouteEntry* our_route = iter.payload(); _ip_route_table->erase(our_route->net()); this->next_table()->delete_route(our_route, this); delete our_route; // Callback immediately, but after network events or expired timers _background_deletion_timer = _eventloop.new_oneoff_after_ms( 0, callback(this, &DeletionTable::background_deletion_pass)); } template void DeletionTable::unplumb_self() { _parent->set_next_table(this->next_table()); this->next_table()->replumb(this, _parent); delete this; } template void DeletionTable::replumb(RouteTable* old_parent, RouteTable* new_parent) { XLOG_ASSERT(_parent == old_parent); _parent = new_parent; } template string DeletionTable::str() const { string s; s = "-------\nDeletionTable: " + this->tablename() + "\n"; if (this->next_table() == NULL) s += "no next table\n"; else s += "next table = " + this->next_table()->tablename() + "\n"; return s; } template class DeletionTable; typedef DeletionTable IPv4DeletionTable; template class DeletionTable; typedef DeletionTable IPv6DeletionTable; xorp/libcomm/0000775000076400007640000000000011633743677013323 5ustar greearbgreearbxorp/libcomm/comm_sock.c0000664000076400007640000012143711532046567015441 0ustar greearbgreearb/* -*- Mode:C; c-basic-offset:4; tab-width:8; indent-tabs-mode:t -*- */ /* vim:set sts=4 ts=8 sw=4: */ /* * Copyright (c) 2001 * YOID Project. * University of Southern California/Information Sciences Institute. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * COMM socket library lower `sock' level implementation. */ #include "comm_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/random.h" #ifdef HAVE_FCNTL_H #include #endif #ifdef HAVE_SYS_SOCKET_H #include #endif #ifdef HAVE_NETINET_IN_H #include #endif #ifdef HAVE_NETINET_TCP_H #include #endif #ifdef HAVE_ARPA_INET_H #include #endif #ifdef HAVE_WINDOWS_H #include #endif #ifdef HAVE_WINSOCK2_H #include #endif #ifdef HAVE_WS2TCPIP_H #include #endif #include "comm_api.h" #include "comm_private.h" #ifdef L_ERROR char addr_str_255[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"]; #endif /* XXX: Single threaded socket errno, used to record last error code. */ int _comm_serrno; #if defined(HOST_OS_WINDOWS) && defined(HAVE_IPV6) /* * Windows declares these in as externs, but does not * supply symbols for them in the -lws2_32 import library or DLL. */ const struct in6_addr in6addr_any = { { IN6ADDR_ANY_INIT } }; const struct in6_addr in6addr_loopback = { { IN6ADDR_LOOPBACK_INIT } }; #endif xsock_t comm_sock_open(int domain, int type, int protocol, int is_blocking) { xsock_t sock; /* Create the kernel socket */ sock = socket(domain, type, protocol); if (sock == XORP_BAD_SOCKET) { _comm_set_serrno(); XLOG_ERROR("Error opening socket (domain = %d, type = %d, " "protocol = %d): %s", domain, type, protocol, comm_get_error_str(comm_get_last_error())); return (XORP_BAD_SOCKET); } /* Set the receiving and sending socket buffer size in the kernel */ if (comm_sock_set_rcvbuf(sock, SO_RCV_BUF_SIZE_MAX, SO_RCV_BUF_SIZE_MIN) < SO_RCV_BUF_SIZE_MIN) { _comm_set_serrno(); comm_sock_close(sock); return (XORP_BAD_SOCKET); } if (comm_sock_set_sndbuf(sock, SO_SND_BUF_SIZE_MAX, SO_SND_BUF_SIZE_MIN) < SO_SND_BUF_SIZE_MIN) { _comm_set_serrno(); comm_sock_close(sock); return (XORP_BAD_SOCKET); } /* Enable TCP_NODELAY */ if (type == SOCK_STREAM && (domain == AF_INET || domain == AF_INET6) && comm_set_nodelay(sock, 1) != XORP_OK) { _comm_set_serrno(); comm_sock_close(sock); return (XORP_BAD_SOCKET); } /* Set blocking mode */ if (comm_sock_set_blocking(sock, is_blocking) != XORP_OK) { _comm_set_serrno(); comm_sock_close(sock); return (XORP_BAD_SOCKET); } return (sock); } int comm_sock_pair(int domain, int type, int protocol, xsock_t sv[2]) { #ifndef HOST_OS_WINDOWS if (socketpair(domain, type, protocol, sv) == -1) { _comm_set_serrno(); return (XORP_ERROR); } return (XORP_OK); #else /* HOST_OS_WINDOWS */ struct sockaddr_storage ss; struct sockaddr_in *psin; socklen_t sslen; SOCKET st[3]; u_long optval; int numtries, error, intdomain; unsigned short port; static const int CSP_LOWPORT = 40000; static const int CSP_HIGHPORT = 65536; #ifdef HAVE_IPV6 struct sockaddr_in6 *psin6; #endif UNUSED(protocol); if (domain != AF_UNIX && domain != AF_INET #ifdef HAVE_IPV6 && domain != AF_INET6 #endif ) { _comm_serrno = WSAEAFNOSUPPORT; return (XORP_ERROR); } intdomain = domain; if (intdomain == AF_UNIX) intdomain = AF_INET; st[0] = st[1] = st[2] = INVALID_SOCKET; st[2] = socket(intdomain, type, 0); if (st[2] == INVALID_SOCKET) goto error; memset(&ss, 0, sizeof(ss)); psin = (struct sockaddr_in *)&ss; #ifdef HAVE_IPV6 psin6 = (struct sockaddr_in6 *)&ss; if (intdomain == AF_INET6) { sslen = sizeof(struct sockaddr_in6); ss.ss_family = AF_INET6; psin6->sin6_addr = in6addr_loopback; } else #endif /* HAVE_IPV6 */ { sslen = sizeof(struct sockaddr_in); ss.ss_family = AF_INET; psin->sin_addr.s_addr = htonl(INADDR_LOOPBACK); } numtries = 3; do { port = htons((xorp_random() % (CSP_LOWPORT - CSP_HIGHPORT)) + CSP_LOWPORT); #ifdef HAVE_IPV6 if (intdomain == AF_INET6) psin6->sin6_port = port; else #endif psin->sin_port = port; error = bind(st[2], (struct sockaddr *)&ss, sslen); if (error == 0) break; if ((error != 0) && ((WSAGetLastError() != WSAEADDRNOTAVAIL) || (WSAGetLastError() != WSAEADDRINUSE))) break; } while (--numtries > 0); if (error != 0) goto error; error = listen(st[2], 5); if (error != 0) goto error; st[0] = socket(intdomain, type, 0); if (st[0] == INVALID_SOCKET) goto error; optval = 1L; error = ioctlsocket(st[0], FIONBIO, &optval); if (error != 0) goto error; error = connect(st[0], (struct sockaddr *)&ss, sslen); if (error != 0 && WSAGetLastError() != WSAEWOULDBLOCK) goto error; numtries = 3; do { st[1] = accept(st[2], NULL, NULL); if (st[1] != INVALID_SOCKET) { break; } else { if (WSAGetLastError() == WSAEWOULDBLOCK) { SleepEx(100, TRUE); } else { break; } } } while (--numtries > 0); if (st[1] == INVALID_SOCKET) goto error; /* Squelch inherited socket event mask. */ (void)WSAEventSelect(st[1], NULL, 0); /* * XXX: Should use getsockname() here to verify that the client socket * is connected. */ optval = 0L; error = ioctlsocket(st[0], FIONBIO, &optval); if (error != 0) goto error; closesocket(st[2]); sv[0] = st[0]; sv[1] = st[1]; return (XORP_OK); error: if (st[0] != INVALID_SOCKET) closesocket(st[0]); if (st[1] != INVALID_SOCKET) closesocket(st[1]); if (st[2] != INVALID_SOCKET) closesocket(st[2]); return (XORP_ERROR); #endif /* HOST_OS_WINDOWS */ } int comm_sock_bind4(xsock_t sock, const struct in_addr *my_addr, unsigned short my_port) { int family; struct sockaddr_in sin_addr; family = comm_sock_get_family(sock); if (family != AF_INET) { XLOG_ERROR("Invalid family of socket %d: family = %d (expected %d)", sock, family, AF_INET); return (XORP_ERROR); } memset(&sin_addr, 0, sizeof(sin_addr)); #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN sin_addr.sin_len = sizeof(sin_addr); #endif sin_addr.sin_family = (u_char)family; sin_addr.sin_port = my_port; /* XXX: in network order */ if (my_addr != NULL) sin_addr.sin_addr.s_addr = my_addr->s_addr; /* XXX: in network order */ else sin_addr.sin_addr.s_addr = INADDR_ANY; if (bind(sock, (struct sockaddr *)&sin_addr, sizeof(sin_addr)) < 0) { _comm_set_serrno(); XLOG_ERROR("Error binding socket (family = %d, " "my_addr = %s, my_port = %d): %s", family, (my_addr)? inet_ntoa(*my_addr) : "ANY", ntohs(my_port), comm_get_error_str(comm_get_last_error())); return (XORP_ERROR); } return (XORP_OK); } int comm_sock_bind6(xsock_t sock, const struct in6_addr *my_addr, unsigned int my_ifindex, unsigned short my_port) { #ifdef HAVE_IPV6 int family; struct sockaddr_in6 sin6_addr; family = comm_sock_get_family(sock); if (family != AF_INET6) { XLOG_ERROR("Invalid family of socket %d: family = %d (expected %d)", sock, family, AF_INET6); return (XORP_ERROR); } memset(&sin6_addr, 0, sizeof(sin6_addr)); #ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN sin6_addr.sin6_len = sizeof(sin6_addr); #endif sin6_addr.sin6_family = (u_char)family; sin6_addr.sin6_port = my_port; /* XXX: in network order */ sin6_addr.sin6_flowinfo = 0; /* XXX: unused (?) */ if (my_addr != NULL) memcpy(&sin6_addr.sin6_addr, my_addr, sizeof(sin6_addr.sin6_addr)); else memcpy(&sin6_addr.sin6_addr, &in6addr_any, sizeof(sin6_addr.sin6_addr)); if (IN6_IS_ADDR_LINKLOCAL(&sin6_addr.sin6_addr)) sin6_addr.sin6_scope_id = my_ifindex; else sin6_addr.sin6_scope_id = 0; if (bind(sock, (struct sockaddr *)&sin6_addr, sizeof(sin6_addr)) < 0) { _comm_set_serrno(); XLOG_ERROR("Error binding socket (family = %d, " "my_addr = %s, my_port = %d): %s", family, (my_addr)? inet_ntop(family, my_addr, addr_str_255, sizeof(addr_str_255)) : "ANY", ntohs(my_port), comm_get_error_str(comm_get_last_error())); return (XORP_ERROR); } return (XORP_OK); #else /* ! HAVE_IPV6 */ comm_sock_no_ipv6("comm_sock_bind6", sock, my_addr, my_ifindex, my_port); return (XORP_ERROR); #endif /* ! HAVE_IPV6 */ } int comm_sock_bind(xsock_t sock, const struct sockaddr *sin) { switch (sin->sa_family) { case AF_INET: { const struct sockaddr_in *sin4 = (const struct sockaddr_in *)((const void *)sin); return comm_sock_bind4(sock, &sin4->sin_addr, sin4->sin_port); } break; #ifdef HAVE_IPV6 case AF_INET6: { const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)((const void *)sin); return comm_sock_bind6(sock, &sin6->sin6_addr, sin6->sin6_scope_id, sin6->sin6_port); } break; #endif /* HAVE_IPV6 */ default: XLOG_FATAL("Error comm_sock_bind invalid family = %d", sin->sa_family); return (XORP_ERROR); } XLOG_UNREACHABLE(); return XORP_ERROR; } int comm_sock_join4(xsock_t sock, const struct in_addr *mcast_addr, const struct in_addr *my_addr) { int family; struct ip_mreq imr; /* the multicast join address */ family = comm_sock_get_family(sock); if (family != AF_INET) { XLOG_ERROR("Invalid family of socket %d: family = %d (expected %d)", sock, family, AF_INET); return (XORP_ERROR); } memset(&imr, 0, sizeof(imr)); imr.imr_multiaddr.s_addr = mcast_addr->s_addr; if (my_addr != NULL) imr.imr_interface.s_addr = my_addr->s_addr; else imr.imr_interface.s_addr = INADDR_ANY; if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, XORP_SOCKOPT_CAST(&imr), sizeof(imr)) < 0) { char mcast_addr_str[32], my_addr_str[32]; _comm_set_serrno(); strncpy(mcast_addr_str, inet_ntoa(*mcast_addr), sizeof(mcast_addr_str) - 1); mcast_addr_str[sizeof(mcast_addr_str) - 1] = '\0'; if (my_addr != NULL) strncpy(my_addr_str, inet_ntoa(*my_addr), sizeof(my_addr_str) - 1); else strncpy(my_addr_str, "ANY", sizeof(my_addr_str) - 1); my_addr_str[sizeof(my_addr_str) - 1] = '\0'; XLOG_ERROR("Error joining mcast group (family = %d, " "mcast_addr = %s my_addr = %s): %s", family, mcast_addr_str, my_addr_str, comm_get_error_str(comm_get_last_error())); return (XORP_ERROR); } return (XORP_OK); } int comm_sock_join6(xsock_t sock, const struct in6_addr *mcast_addr, unsigned int my_ifindex) { #ifdef HAVE_IPV6 int family; struct ipv6_mreq imr6; /* the multicast join address */ family = comm_sock_get_family(sock); if (family != AF_INET6) { XLOG_ERROR("Invalid family of socket %d: family = %d (expected %d)", sock, family, AF_INET6); return (XORP_ERROR); } memset(&imr6, 0, sizeof(imr6)); memcpy(&imr6.ipv6mr_multiaddr, mcast_addr, sizeof(*mcast_addr)); imr6.ipv6mr_interface = my_ifindex; if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, XORP_SOCKOPT_CAST(&imr6), sizeof(imr6)) < 0) { _comm_set_serrno(); XLOG_ERROR("Error joining mcast group (family = %d, " "mcast_addr = %s my_ifindex = %d): %s", family, inet_ntop(family, mcast_addr, addr_str_255, sizeof(addr_str_255)), my_ifindex, comm_get_error_str(comm_get_last_error())); return (XORP_ERROR); } return (XORP_OK); #else /* ! HAVE_IPV6 */ comm_sock_no_ipv6("comm_sock_join6", sock, mcast_addr, my_ifindex); return (XORP_ERROR); #endif /* ! HAVE_IPV6 */ } int comm_sock_leave4(xsock_t sock, const struct in_addr *mcast_addr, const struct in_addr *my_addr) { int family; struct ip_mreq imr; /* the multicast leave address */ family = comm_sock_get_family(sock); if (family != AF_INET) { XLOG_ERROR("Invalid family of socket %d: family = %d (expected %d)", sock, family, AF_INET); return (XORP_ERROR); } memset(&imr, 0, sizeof(imr)); imr.imr_multiaddr.s_addr = mcast_addr->s_addr; if (my_addr != NULL) imr.imr_interface.s_addr = my_addr->s_addr; else imr.imr_interface.s_addr = INADDR_ANY; if (setsockopt(sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, XORP_SOCKOPT_CAST(&imr), sizeof(imr)) < 0) { char mcast_addr_str[32], my_addr_str[32]; _comm_set_serrno(); strncpy(mcast_addr_str, inet_ntoa(*mcast_addr), sizeof(mcast_addr_str) - 1); mcast_addr_str[sizeof(mcast_addr_str) - 1] = '\0'; if (my_addr != NULL) strncpy(my_addr_str, inet_ntoa(*my_addr), sizeof(my_addr_str) - 1); else strncpy(my_addr_str, "ANY", sizeof(my_addr_str) - 1); my_addr_str[sizeof(my_addr_str) - 1] = '\0'; XLOG_ERROR("Error leaving mcast group (family = %d, " "mcast_addr = %s my_addr = %s): %s", family, mcast_addr_str, my_addr_str, comm_get_error_str(comm_get_last_error())); return (XORP_ERROR); } return (XORP_OK); } int comm_sock_leave6(xsock_t sock, const struct in6_addr *mcast_addr, unsigned int my_ifindex) { #ifdef HAVE_IPV6 int family; struct ipv6_mreq imr6; /* the multicast leave address */ family = comm_sock_get_family(sock); if (family != AF_INET6) { XLOG_ERROR("Invalid family of socket %d: family = %d (expected %d)", sock, family, AF_INET6); return (XORP_ERROR); } memset(&imr6, 0, sizeof(imr6)); memcpy(&imr6.ipv6mr_multiaddr, mcast_addr, sizeof(*mcast_addr)); imr6.ipv6mr_interface = my_ifindex; if (setsockopt(sock, IPPROTO_IPV6, IPV6_LEAVE_GROUP, XORP_SOCKOPT_CAST(&imr6), sizeof(imr6)) < 0) { _comm_set_serrno(); XLOG_ERROR("Error leaving mcast group (family = %d, " "mcast_addr = %s my_ifindex = %d): %s", family, inet_ntop(family, mcast_addr, addr_str_255, sizeof(addr_str_255)), my_ifindex, comm_get_error_str(comm_get_last_error())); return (XORP_ERROR); } return (XORP_OK); #else /* ! HAVE_IPV6 */ comm_sock_no_ipv6("comm_sock_leave6", sock, mcast_addr, my_ifindex); return (XORP_ERROR); #endif /* ! HAVE_IPV6 */ } int comm_sock_connect4(xsock_t sock, const struct in_addr *remote_addr, unsigned short remote_port, int is_blocking, int *in_progress) { int family; struct sockaddr_in sin_addr; if (in_progress != NULL) *in_progress = 0; family = comm_sock_get_family(sock); if (family != AF_INET) { XLOG_ERROR("Invalid family of socket %d: family = %d (expected %d)", sock, family, AF_INET); return (XORP_ERROR); } memset(&sin_addr, 0, sizeof(sin_addr)); #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN sin_addr.sin_len = sizeof(sin_addr); #endif sin_addr.sin_family = (u_char)family; sin_addr.sin_port = remote_port; /* XXX: in network order */ sin_addr.sin_addr.s_addr = remote_addr->s_addr; /* XXX: in network order */ if (connect(sock, (struct sockaddr *)&sin_addr, sizeof(sin_addr)) < 0) { _comm_set_serrno(); if (! is_blocking) { #ifdef HOST_OS_WINDOWS if (comm_get_last_error() == WSAEWOULDBLOCK) { #else if (comm_get_last_error() == EINPROGRESS) { #endif /* * XXX: The connection is non-blocking, and the connection * cannot be completed immediately, therefore set the * in_progress flag to 1 and return an error. */ if (in_progress != NULL) *in_progress = 1; return (XORP_ERROR); } } XLOG_ERROR("Error connecting socket (family = %d, " "remote_addr = %s, remote_port = %d): %s", family, inet_ntoa(*remote_addr), ntohs(remote_port), comm_get_error_str(comm_get_last_error())); return (XORP_ERROR); } return (XORP_OK); } int comm_sock_connect6(xsock_t sock, const struct in6_addr *remote_addr, unsigned short remote_port, int is_blocking, int *in_progress) { #ifdef HAVE_IPV6 int family; struct sockaddr_in6 sin6_addr; if (in_progress != NULL) *in_progress = 0; family = comm_sock_get_family(sock); if (family != AF_INET6) { XLOG_ERROR("Invalid family of socket %d: family = %d (expected %d)", sock, family, AF_INET6); return (XORP_ERROR); } memset(&sin6_addr, 0, sizeof(sin6_addr)); #ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN sin6_addr.sin6_len = sizeof(sin6_addr); #endif sin6_addr.sin6_family = (u_char)family; sin6_addr.sin6_port = remote_port; /* XXX: in network order */ sin6_addr.sin6_flowinfo = 0; /* XXX: unused (?) */ memcpy(&sin6_addr.sin6_addr, remote_addr, sizeof(sin6_addr.sin6_addr)); sin6_addr.sin6_scope_id = 0; /* XXX: unused (?) */ if (connect(sock, (struct sockaddr *)&sin6_addr, sizeof(sin6_addr)) < 0) { _comm_set_serrno(); if (! is_blocking) { #ifdef HOST_OS_WINDOWS if (comm_get_last_error() == WSAEWOULDBLOCK) { #else if (comm_get_last_error() == EINPROGRESS) { #endif /* * XXX: The connection is non-blocking, and the connection * cannot be completed immediately, therefore set the * in_progress flag to 1 and return an error. */ if (in_progress != NULL) *in_progress = 1; return (XORP_ERROR); } } XLOG_ERROR("Error connecting socket (family = %d, " "remote_addr = %s, remote_port = %d): %s", family, (remote_addr)? inet_ntop(family, remote_addr, addr_str_255, sizeof(addr_str_255)) : "ANY", ntohs(remote_port), comm_get_error_str(comm_get_last_error())); return (XORP_ERROR); } return (XORP_OK); #else /* ! HAVE_IPV6 */ if (in_progress != NULL) *in_progress = 0; comm_sock_no_ipv6("comm_sock_connect6", sock, remote_addr, remote_port, is_blocking); return (XORP_ERROR); #endif /* ! HAVE_IPV6 */ } int comm_sock_connect(xsock_t sock, const struct sockaddr *sin, int is_blocking, int *in_progress) { switch (sin->sa_family) { case AF_INET: { const struct sockaddr_in *sin4 = (const struct sockaddr_in *)((const void *)sin); return comm_sock_connect4(sock, &sin4->sin_addr, sin4->sin_port, is_blocking, in_progress); } break; #ifdef HAVE_IPV6 case AF_INET6: { const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)((const void *)sin); return comm_sock_connect6(sock, &sin6->sin6_addr, sin6->sin6_port, is_blocking, in_progress); } break; #endif /* HAVE_IPV6 */ default: XLOG_FATAL("Error comm_sock_connect invalid family = %d", sin->sa_family); return (XORP_ERROR); } XLOG_UNREACHABLE(); return XORP_ERROR; } xsock_t comm_sock_accept(xsock_t sock) { xsock_t sock_accept; struct sockaddr addr; socklen_t socklen = sizeof(addr); sock_accept = accept(sock, &addr, &socklen); if (sock_accept == XORP_BAD_SOCKET) { _comm_set_serrno(); XLOG_ERROR("Error accepting socket %d: %s", sock, comm_get_error_str(comm_get_last_error())); return (XORP_BAD_SOCKET); } #ifdef HOST_OS_WINDOWS /* * Squelch Winsock event notifications on the new socket which may * have been inherited from the parent listening socket. */ (void)WSAEventSelect(sock_accept, NULL, 0); #endif /* Enable TCP_NODELAY */ if ((addr.sa_family == AF_INET || addr.sa_family == AF_INET6) && comm_set_nodelay(sock_accept, 1) != XORP_OK) { comm_sock_close(sock_accept); return (XORP_BAD_SOCKET); } return (sock_accept); } int comm_sock_listen(xsock_t sock, int backlog) { int ret; ret = listen(sock, backlog); if (ret < 0) { _comm_set_serrno(); XLOG_ERROR("Error listening on socket (socket = %d) : %s", sock, comm_get_error_str(comm_get_last_error())); return (XORP_ERROR); } return (XORP_OK); } int comm_sock_close(xsock_t sock) { int ret; #ifndef HOST_OS_WINDOWS ret = close(sock); #else (void)WSAEventSelect(sock, NULL, 0); ret = closesocket(sock); #endif if (ret < 0) { _comm_set_serrno(); XLOG_ERROR("Error closing socket (socket = %d) : %s", sock, comm_get_error_str(comm_get_last_error())); return (XORP_ERROR); } return (XORP_OK); } int comm_set_send_broadcast(xsock_t sock, int val) { if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, XORP_SOCKOPT_CAST(&val), sizeof(val)) < 0) { _comm_set_serrno(); XLOG_ERROR("Error %s SO_BROADCAST on socket %d: %s", (val)? "set": "reset", sock, comm_get_error_str(comm_get_last_error())); return (XORP_ERROR); } return (XORP_OK); } int comm_set_receive_broadcast(xsock_t sock, int val) { #if defined(HOST_OS_WINDOWS) && defined(IP_RECEIVE_BROADCAST) /* * With Windows Server 2003 and later, you have to explicitly * ask to receive broadcast packets. */ DWORD ip_rx_bcast = (DWORD)val; if (setsockopt(sock, IPPROTO_IP, IP_RECEIVE_BROADCAST, XORP_SOCKOPT_CAST(&ip_rx_bcast), sizeof(ip_rx_bcast)) < 0) { _comm_set_serrno(); XLOG_ERROR("Error %s IP_RECEIVE_BROADCAST on socket %d: %s", (val)? "set": "reset", sock, comm_get_error_str(comm_get_last_error())); return (XORP_ERROR); } return (XORP_OK); #else UNUSED(sock); UNUSED(val); return (XORP_OK); #endif } int comm_set_nodelay(xsock_t sock, int val) { if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, XORP_SOCKOPT_CAST(&val), sizeof(val)) < 0) { _comm_set_serrno(); XLOG_ERROR("Error %s TCP_NODELAY on socket %d: %s", (val)? "set": "reset", sock, comm_get_error_str(comm_get_last_error())); return (XORP_ERROR); } return (XORP_OK); } int comm_set_linger(xsock_t sock, int enabled, int secs) { #ifdef SO_LINGER struct linger l; l.l_onoff = enabled; l.l_linger = secs; if (setsockopt(sock, SOL_SOCKET, SO_LINGER, XORP_SOCKOPT_CAST(&l), sizeof(l)) < 0) { _comm_set_serrno(); XLOG_ERROR("Error %s SO_LINGER %ds on socket %d: %s", (enabled)? "set": "reset", secs, sock, comm_get_error_str(comm_get_last_error())); return (XORP_ERROR); } return (XORP_OK); #else /* ! SO_LINGER */ UNUSED(sock); UNUSED(enabled); UNUSED(secs); XLOG_WARNING("SO_LINGER Undefined!"); return (XORP_ERROR); #endif /* ! SO_LINGER */ } int comm_set_keepalive(xsock_t sock, int val) { #ifdef SO_KEEPALIVE if (setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, XORP_SOCKOPT_CAST(&val), sizeof(val)) < 0) { _comm_set_serrno(); XLOG_ERROR("Error %s SO_KEEPALIVE on socket %d: %s", (val)? "set": "reset", sock, comm_get_error_str(comm_get_last_error())); return (XORP_ERROR); } return (XORP_OK); #else /* ! SO_KEEPALIVE */ UNUSED(sock); UNUSED(val); XLOG_WARNING("SO_KEEPALIVE Undefined!"); return (XORP_ERROR); #endif /* ! SO_KEEPALIVE */ } int comm_set_nosigpipe(xsock_t sock, int val) { #ifdef SO_NOSIGPIPE if (setsockopt(sock, SOL_SOCKET, SO_NOSIGPIPE, XORP_SOCKOPT_CAST(&val), sizeof(val)) < 0) { _comm_set_serrno(); XLOG_ERROR("Error %s SO_NOSIGPIPE on socket %d: %s", (val)? "set": "reset", sock, comm_get_error_str(comm_get_last_error())); return (XORP_ERROR); } return (XORP_OK); #else /* ! SO_NOSIGPIPE */ UNUSED(sock); UNUSED(val); XLOG_WARNING("SO_NOSIGPIPE Undefined!"); return (XORP_ERROR); #endif /* ! SO_NOSIGPIPE */ } int comm_set_reuseaddr(xsock_t sock, int val) { #ifdef SO_REUSEADDR if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, XORP_SOCKOPT_CAST(&val), sizeof(val)) < 0) { _comm_set_serrno(); XLOG_ERROR("Error %s SO_REUSEADDR on socket %d: %s", (val)? "set": "reset", sock, comm_get_error_str(comm_get_last_error())); return (XORP_ERROR); } return (XORP_OK); #else /* ! SO_REUSEADDR */ UNUSED(sock); UNUSED(val); XLOG_WARNING("SO_REUSEADDR Undefined!"); return (XORP_ERROR); #endif /* ! SO_REUSEADDR */ } int comm_set_reuseport(xsock_t sock, int val) { #ifdef SO_REUSEPORT if (setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, XORP_SOCKOPT_CAST(&val), sizeof(val)) < 0) { _comm_set_serrno(); XLOG_ERROR("Error %s SO_REUSEPORT on socket %d: %s", (val)? "set": "reset", sock, comm_get_error_str(comm_get_last_error())); return (XORP_ERROR); } return (XORP_OK); #else /* ! SO_REUSEPORT */ UNUSED(sock); UNUSED(val); XLOG_WARNING("SO_REUSEPORT Undefined!"); return (XORP_OK); #endif /* ! SO_REUSEPORT */ } int comm_set_loopback(xsock_t sock, int val) { int family = comm_sock_get_family(sock); switch (family) { case AF_INET: { u_char loop = val; if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_LOOP, XORP_SOCKOPT_CAST(&loop), sizeof(loop)) < 0) { _comm_set_serrno(); XLOG_ERROR("setsockopt IP_MULTICAST_LOOP %u: %s", loop, comm_get_error_str(comm_get_last_error())); return (XORP_ERROR); } break; } #ifdef HAVE_IPV6 case AF_INET6: { unsigned int loop6 = val; if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, XORP_SOCKOPT_CAST(&loop6), sizeof(loop6)) < 0) { _comm_set_serrno(); XLOG_ERROR("setsockopt IPV6_MULTICAST_LOOP %u: %s", loop6, comm_get_error_str(comm_get_last_error())); return (XORP_ERROR); } break; } #endif /* HAVE_IPV6 */ default: XLOG_FATAL("Error %s setsockopt IP_MULTICAST_LOOP/IPV6_MULTICAST_LOOP " "on socket %d: invalid family = %d", (val)? "set": "reset", sock, family); return (XORP_ERROR); } return (XORP_OK); } int comm_set_tcpmd5(xsock_t sock, int val) { #ifdef TCP_MD5SIG /* XXX: Defined in across Free/Net/OpenBSD */ if (setsockopt(sock, IPPROTO_TCP, TCP_MD5SIG, XORP_SOCKOPT_CAST(&val), sizeof(val)) < 0) { _comm_set_serrno(); XLOG_ERROR("Error %s TCP_MD5SIG on socket %d: %s", (val)? "set": "reset", sock, comm_get_error_str(comm_get_last_error())); return (XORP_ERROR); } return (XORP_OK); #else /* ! TCP_MD5SIG */ UNUSED(sock); UNUSED(val); XLOG_WARNING("TCP_MD5SIG Undefined!"); return (XORP_ERROR); #endif /* ! TCP_MD5SIG */ } int comm_set_nopush(xsock_t sock, int val) { #ifdef TCP_NOPUSH /* XXX: Defined in across Free/Net/OpenBSD */ if (setsockopt(sock, IPPROTO_TCP, TCP_NOPUSH, XORP_SOCKOPT_CAST(&val), sizeof(val)) < 0) { _comm_set_serrno(); XLOG_ERROR("Error %s TCP_NOPUSH on socket %d: %s", (val)? "set": "reset", sock, comm_get_error_str(comm_get_last_error())); return (XORP_ERROR); } return (XORP_OK); #else /* ! TCP_NOPUSH */ UNUSED(sock); UNUSED(val); XLOG_WARNING("TCP_NOPUSH Undefined!"); return (XORP_ERROR); #endif /* ! TCP_NOPUSH */ } int comm_set_tos(xsock_t sock, int val) { #ifdef IP_TOS /* * Most implementations use 'int' to represent the value of * the IP_TOS option. */ int family, ip_tos; family = comm_sock_get_family(sock); if (family != AF_INET) { XLOG_FATAL("Error %s setsockopt IP_TOS on socket %d: " "invalid family = %d", (val)? "set": "reset", sock, family); return (XORP_ERROR); } /* * Note that it is not guaranteed that the TOS will be successfully * set or indeed propagated where the host platform is running * its own traffic classifier; the use of comm_set_tos() is * intended for link-scoped traffic. */ ip_tos = val; if (setsockopt(sock, IPPROTO_IP, IP_TOS, XORP_SOCKOPT_CAST(&ip_tos), sizeof(ip_tos)) < 0) { _comm_set_serrno(); XLOG_ERROR("setsockopt IP_TOS 0x%x: %s", ip_tos, comm_get_error_str(comm_get_last_error())); return (XORP_ERROR); } return (XORP_OK); #else UNUSED(sock); UNUSED(val); XLOG_WARNING("IP_TOS Undefined!"); return (XORP_ERROR); #endif /* ! IP_TOS */ } int comm_set_unicast_ttl(xsock_t sock, int val) { int family = comm_sock_get_family(sock); switch (family) { case AF_INET: { /* XXX: Most platforms now use int for this option; * legacy BSD specified u_char. */ int ip_ttl = val; if (setsockopt(sock, IPPROTO_IP, IP_TTL, XORP_SOCKOPT_CAST(&ip_ttl), sizeof(ip_ttl)) < 0) { _comm_set_serrno(); XLOG_ERROR("setsockopt IP_TTL %u: %s", ip_ttl, comm_get_error_str(comm_get_last_error())); return (XORP_ERROR); } break; } #ifdef HAVE_IPV6 case AF_INET6: { int ip_ttl = val; if (setsockopt(sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, XORP_SOCKOPT_CAST(&ip_ttl), sizeof(ip_ttl)) < 0) { _comm_set_serrno(); XLOG_ERROR("setsockopt IPV6_UNICAST_HOPS %u: %s", ip_ttl, comm_get_error_str(comm_get_last_error())); return (XORP_ERROR); } break; } #endif /* HAVE_IPV6 */ default: XLOG_FATAL("Error %s setsockopt IP_TTL/IPV6_UNICAST_HOPS " "on socket %d: invalid family = %d", (val)? "set": "reset", sock, family); return (XORP_ERROR); } return (XORP_OK); } int comm_set_multicast_ttl(xsock_t sock, int val) { int family = comm_sock_get_family(sock); switch (family) { case AF_INET: { /* XXX: Most platforms now use int for this option; * legacy BSD specified u_char. */ u_char ip_multicast_ttl = val; if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, XORP_SOCKOPT_CAST(&ip_multicast_ttl), sizeof(ip_multicast_ttl)) < 0) { _comm_set_serrno(); XLOG_ERROR("setsockopt IP_MULTICAST_TTL %u: %s", ip_multicast_ttl, comm_get_error_str(comm_get_last_error())); return (XORP_ERROR); } break; } #ifdef HAVE_IPV6 case AF_INET6: { int ip_multicast_ttl = val; if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, XORP_SOCKOPT_CAST(&ip_multicast_ttl), sizeof(ip_multicast_ttl)) < 0) { _comm_set_serrno(); XLOG_ERROR("setsockopt IPV6_MULTICAST_HOPS %u: %s", ip_multicast_ttl, comm_get_error_str(comm_get_last_error())); return (XORP_ERROR); } break; } #endif /* HAVE_IPV6 */ default: XLOG_FATAL("Error %s setsockopt IP_MULTICAST_TTL/IPV6_MULTICAST_HOPS " "on socket %d: invalid family = %d", (val)? "set": "reset", sock, family); return (XORP_ERROR); } return (XORP_OK); } int comm_set_iface4(xsock_t sock, const struct in_addr *in_addr) { int family = comm_sock_get_family(sock); struct in_addr my_addr; if (family != AF_INET) { XLOG_ERROR("Invalid family of socket %d: family = %d (expected %d)", sock, family, AF_INET); return (XORP_ERROR); } if (in_addr != NULL) my_addr.s_addr = in_addr->s_addr; else my_addr.s_addr = INADDR_ANY; if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, XORP_SOCKOPT_CAST(&my_addr), sizeof(my_addr)) < 0) { _comm_set_serrno(); XLOG_ERROR("setsockopt IP_MULTICAST_IF %s: %s", (in_addr)? inet_ntoa(my_addr) : "ANY", comm_get_error_str(comm_get_last_error())); return (XORP_ERROR); } return (XORP_OK); } int comm_set_iface6(xsock_t sock, unsigned int my_ifindex) { #ifdef HAVE_IPV6 int family = comm_sock_get_family(sock); if (family != AF_INET6) { XLOG_ERROR("Invalid family of socket %d: family = %d (expected %d)", sock, family, AF_INET6); return (XORP_ERROR); } if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_IF, XORP_SOCKOPT_CAST(&my_ifindex), sizeof(my_ifindex)) < 0) { _comm_set_serrno(); XLOG_ERROR("setsockopt IPV6_MULTICAST_IF for interface index %d: %s", my_ifindex, comm_get_error_str(comm_get_last_error())); return (XORP_ERROR); } return (XORP_OK); #else /* ! HAVE_IPV6 */ comm_sock_no_ipv6("comm_set_iface6", sock, my_ifindex); return (XORP_ERROR); #endif /* ! HAVE_IPV6 */ } int comm_set_onesbcast(xsock_t sock, int enabled) { #ifdef IP_ONESBCAST int family = comm_sock_get_family(sock); if (family != AF_INET) { XLOG_ERROR("Invalid family of socket %d: family = %d (expected %d)", sock, family, AF_INET); return (XORP_ERROR); } if (setsockopt(sock, IPPROTO_IP, IP_ONESBCAST, XORP_SOCKOPT_CAST(&enabled), sizeof(enabled)) < 0) { _comm_set_serrno(); XLOG_ERROR("setsockopt IP_ONESBCAST %d: %s", enabled, comm_get_error_str(comm_get_last_error())); return (XORP_ERROR); } return (XORP_OK); #else /* ! IP_ONESBCAST */ XLOG_ERROR("setsockopt IP_ONESBCAST %u: %s", enabled, "IP_ONESBCAST support not present."); UNUSED(sock); UNUSED(enabled); return (XORP_ERROR); #endif /* ! IP_ONESBCAST */ } int comm_set_bindtodevice(xsock_t sock, const char * my_ifname) { #ifdef SO_BINDTODEVICE char tmp_ifname[IFNAMSIZ]; /* * Bind a socket to an interface by name. * * Under Linux, use of the undirected broadcast address * 255.255.255.255 requires that the socket is bound to the * underlying interface, as it is an implicitly scoped address * with no meaning on its own. This is not architecturally OK, * and requires additional support for SO_BINDTODEVICE. * See socket(7) man page in Linux. * * Note: strlcpy() is not present in glibc; strncpy() is used * instead to avoid introducing a circular dependency on the * C++ library libxorp. */ strncpy(tmp_ifname, my_ifname, IFNAMSIZ-1); tmp_ifname[IFNAMSIZ-1] = '\0'; if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, tmp_ifname, sizeof(tmp_ifname)) < 0) { _comm_set_serrno(); static bool do_once = true; // Ignore EPERM..just means user isn't running as root, ie for 'scons check' // most likely..User will have bigger trouble if not running as root for // a production system anyway. if (errno != EPERM) { if (do_once) { XLOG_WARNING("setsockopt SO_BINDTODEVICE %s: %s This error will only be printed once per program instance.", tmp_ifname, comm_get_error_str(comm_get_last_error())); do_once = false; } } return (XORP_ERROR); } return (XORP_OK); #else #ifndef __FreeBSD__ // FreeBSD doesn't implement this, so no use filling logs with errors that can't // be helped. Assume calling code deals with the error code as needed. XLOG_ERROR("setsockopt SO_BINDTODEVICE %s: %s", my_ifname, "SO_BINDTODEVICE support not present."); #endif UNUSED(my_ifname); UNUSED(sock); return (XORP_ERROR); #endif } int comm_sock_set_sndbuf(xsock_t sock, int desired_bufsize, int min_bufsize) { int delta = desired_bufsize / 2; /* * Set the socket buffer size. If we can't set it as large as we * want, search around to try to find the highest acceptable * value. The highest acceptable value being smaller than * minsize is a fatal error. */ if (setsockopt(sock, SOL_SOCKET, SO_SNDBUF, XORP_SOCKOPT_CAST(&desired_bufsize), sizeof(desired_bufsize)) < 0) { desired_bufsize -= delta; while (1) { if (delta > 1) delta /= 2; if (setsockopt(sock, SOL_SOCKET, SO_SNDBUF, XORP_SOCKOPT_CAST(&desired_bufsize), sizeof(desired_bufsize)) < 0) { _comm_set_serrno(); desired_bufsize -= delta; if (desired_bufsize <= 0) break; } else { if (delta < 1024) break; desired_bufsize += delta; } } if (desired_bufsize < min_bufsize) { XLOG_ERROR("Cannot set sending buffer size of socket %d: " "desired buffer size %u < minimum allowed %u", sock, desired_bufsize, min_bufsize); return (XORP_ERROR); } } return (desired_bufsize); } int comm_sock_set_rcvbuf(xsock_t sock, int desired_bufsize, int min_bufsize) { int delta = desired_bufsize / 2; /* * Set the socket buffer size. If we can't set it as large as we * want, search around to try to find the highest acceptable * value. The highest acceptable value being smaller than * minsize is a fatal error. */ if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, XORP_SOCKOPT_CAST(&desired_bufsize), sizeof(desired_bufsize)) < 0) { desired_bufsize -= delta; while (1) { if (delta > 1) delta /= 2; if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, XORP_SOCKOPT_CAST(&desired_bufsize), sizeof(desired_bufsize)) < 0) { _comm_set_serrno(); desired_bufsize -= delta; if (desired_bufsize <= 0) break; } else { if (delta < 1024) break; desired_bufsize += delta; } } if (desired_bufsize < min_bufsize) { XLOG_ERROR("Cannot set receiving buffer size of socket %d: " "desired buffer size %u < minimum allowed %u", sock, desired_bufsize, min_bufsize); return (XORP_ERROR); } } return (desired_bufsize); } int comm_sock_get_family(xsock_t sock) { #ifdef HOST_OS_WINDOWS WSAPROTOCOL_INFO wspinfo; int err, len; len = sizeof(wspinfo); err = getsockopt(sock, SOL_SOCKET, SO_PROTOCOL_INFO, XORP_SOCKOPT_CAST(&wspinfo), &len); if (err != 0) { _comm_set_serrno(); XLOG_ERROR("Error getsockopt(SO_PROTOCOL_INFO) for socket %d: %s", sock, comm_get_error_str(comm_get_last_error())); return (XORP_ERROR); } return ((int)wspinfo.iAddressFamily); #else /* ! HOST_OS_WINDOWS */ /* XXX: Should use struct sockaddr_storage. */ #ifndef MAXSOCKADDR #define MAXSOCKADDR 128 /* max socket address structure size */ #endif union { struct sockaddr sa; char data[MAXSOCKADDR]; } un; socklen_t len; len = MAXSOCKADDR; if (getsockname(sock, &un.sa, &len) < 0) { _comm_set_serrno(); XLOG_ERROR("Error getsockname() for socket %d: %s", sock, comm_get_error_str(comm_get_last_error())); return (XORP_ERROR); } return (un.sa.sa_family); #endif /* ! HOST_OS_WINDOWS */ } int comm_sock_get_type(xsock_t sock) { int err, type; socklen_t len = sizeof(type); err = getsockopt(sock, SOL_SOCKET, SO_TYPE, XORP_SOCKOPT_CAST(&type), &len); if (err != 0) { _comm_set_serrno(); XLOG_ERROR("Error getsockopt(SO_TYPE) for socket %d: %s", sock, comm_get_error_str(comm_get_last_error())); return (XORP_ERROR); } return type; } int comm_sock_set_blocking(xsock_t sock, int is_blocking) { #ifdef HOST_OS_WINDOWS u_long opt; int flags; if (is_blocking) opt = 0; else opt = 1; flags = ioctlsocket(sock, FIONBIO, &opt); if (flags != 0) { _comm_set_serrno(); XLOG_ERROR("FIONBIO error: %s", comm_get_error_str(comm_get_last_error())); return (XORP_ERROR); } #else /* ! HOST_OS_WINDOWS */ int flags; if ( (flags = fcntl(sock, F_GETFL, 0)) < 0) { _comm_set_serrno(); XLOG_ERROR("F_GETFL error: %s", comm_get_error_str(comm_get_last_error())); return (XORP_ERROR); } if (is_blocking) flags &= ~O_NONBLOCK; else flags |= O_NONBLOCK; if (fcntl(sock, F_SETFL, flags) < 0) { _comm_set_serrno(); XLOG_ERROR("F_SETFL error: %s", comm_get_error_str(comm_get_last_error())); return (XORP_ERROR); } #endif /* ! HOST_OS_WINDOWS */ return (XORP_OK); } int comm_sock_is_connected(xsock_t sock, int *is_connected) { struct sockaddr_storage ss; int err; socklen_t sslen; if (is_connected == NULL) { XLOG_ERROR("comm_sock_is_connected() error: " "return value pointer is NULL"); return (XORP_ERROR); } *is_connected = 0; sslen = sizeof(ss); memset(&ss, 0, sslen); err = getpeername(sock, (struct sockaddr *)&ss, &sslen); #ifdef HOST_OS_WINDOWS if (err == SOCKET_ERROR) { if ((WSAGetLastError() == WSAENOTCONN) || (WSAGetLastError() == WSAEINPROGRESS)) { return (XORP_OK); /* Socket is not connected */ } _comm_set_serrno(); return (XORP_ERROR); } #else /* ! HOST_OS_WINDOWS */ if (err != 0) { if ((err == ENOTCONN) || (err == ECONNRESET)) { return (XORP_OK); /* Socket is not connected */ } _comm_set_serrno(); return (XORP_ERROR); } #endif /* ! HOST_OS_WINDOWS */ /* Socket is connected */ *is_connected = 1; return (XORP_OK); } void comm_sock_no_ipv6(const char* method, ...) { #ifdef HOST_OS_WINDOWS _comm_serrno = WSAEAFNOSUPPORT; #else _comm_serrno = EAFNOSUPPORT; #endif XLOG_ERROR("%s: IPv6 support not present.", method); UNUSED(method); } void _comm_set_serrno(void) { #ifdef HOST_OS_WINDOWS _comm_serrno = WSAGetLastError(); WSASetLastError(0); #else _comm_serrno = errno; /* * TODO: XXX - Temporarily don't set errno to 0 we still have code * using errno 2005-05-09 Atanu. */ /* errno = 0; */ #endif } xorp/libcomm/comm_module.h0000664000076400007640000000242011421137511015745 0ustar greearbgreearb/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- */ /* vim:set sts=4 ts=8: */ /* * Copyright (c) 2001-2009 XORP, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License, Version * 2.1, June 1999 as published by the Free Software Foundation. * Redistribution and/or modification of this program under the terms of * any other version of the GNU Lesser General Public License is not * permitted. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, * see the GNU Lesser General Public License, Version 2.1, a copy of * which can be found in the XORP LICENSE.lgpl file. * * XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; * http://xorp.net */ /* * $XORP: xorp/libcomm/comm_module.h,v 1.10 2008/10/02 21:57:15 bms Exp $ */ /* * Module definitions. */ #ifndef __LIBCOMM_COMM_MODULE_H__ #define __LIBCOMM_COMM_MODULE_H__ #ifndef XORP_MODULE_NAME #define XORP_MODULE_NAME "LIBCOMM" #endif #ifndef XORP_MODULE_VERSION #define XORP_MODULE_VERSION "0.1" #endif #endif /* __LIBCOMM_COMM_MODULE_H__ */ xorp/libcomm/SConscript0000664000076400007640000000324611421137511015315 0ustar greearbgreearb# Copyright (c) 2009 XORP, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # $XORP$ import os Import("env") subdirs = [ 'tests' ] SConscript(dirs = subdirs, exports='env') env = env.Clone() is_shared = env.has_key('SHAREDLIBS') env.AppendUnique(CPPPATH = [ "#" ]) env.PrependUnique(LIBPATH = [ '$BUILDDIR/libxorp' ] ) env.AppendUnique(LIBS = ['xorp_core']) sources = [ 'comm_sock.c', 'comm_user.c', ] if is_shared: libxorp_comm = env.SharedLibrary(target = 'libxorp_comm', source = sources) # build symlink for resolving links whilst in BUILDDIR if env['rtld_origin']: for obj in libxorp_comm: env.AddPostAction(libxorp_comm, env.Symlink(obj.abspath, os.path.join(env['xorp_alias_libdir'], str(obj)))) else: libxorp_comm = env.StaticLibrary(target = 'libxorp_comm', source = sources) if is_shared: env.Alias('install', env.InstallLibrary(env['xorp_libdir'], libxorp_comm)) Default(libxorp_comm) xorp/libcomm/README0000664000076400007640000000162411421137511014161 0ustar greearbgreearb# # $XORP: xorp/libcomm/README,v 1.3 2004/01/16 19:57:25 hodson Exp $ # This directory contains the COMM socket library. This library includes a number of handy functions that simplify the usage of communication sockets: open, close, bind to a local address and a port, join a multicast group, etc. The library supports both IPv4 and IPv6. The complete API to the library is in the "comm_api.h" file. Each function declared in that file is reasonably well documented. The API has two parts: the high-level ``user'' API, and the lower-lever ``sock'' API. The lower-level API performs the particular operations on a socket (e.g., join a multicast group on a socket). The higher-level API usually combines several lower-level operations together: e.g., open a socket, bind it to a multicast address and a port, and join that multicast address. See file test_comm.c for various examples of how to use the library. xorp/libcomm/comm_user.c0000664000076400007640000005064411532046567015461 0ustar greearbgreearb/* -*- Mode:C; c-basic-offset:4; tab-width:8; indent-tabs-mode:t -*- */ /* vim:set sts=4 ts=8: */ /* * Copyright (c) 2001 * YOID Project. * University of Southern California/Information Sciences Institute. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * COMM socket library higher `sock' level implementation. */ #include "comm_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include #ifdef HAVE_WINDOWS_H #include #endif #ifdef HAVE_WINSOCK2_H #include #endif #ifdef HAVE_WS2TCPIP_H #include #endif #include "comm_api.h" #include "comm_private.h" int comm_init(void) { static int init_flag = 0; if (init_flag) return (XORP_OK); #ifdef HOST_OS_WINDOWS { int result; WORD version; WSADATA wsadata; version = MAKEWORD(2, 2); result = WSAStartup(version, &wsadata); if (result != 0) { return (XORP_ERROR); } if (LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wVersion) != 2) { (void)WSACleanup(); return (XORP_ERROR); } } #endif /* HOST_OS_WINDOWS */ init_flag = 1; return (XORP_OK); } void comm_exit(void) { #ifdef HOST_OS_WINDOWS (void)WSACleanup(); #endif } int comm_get_last_error(void) { return _comm_serrno; } char const * comm_get_error_str(int serrno) { #ifdef HOST_OS_WINDOWS static char msgbuf[1024]; FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK, NULL, serrno, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)msgbuf, sizeof(msgbuf), NULL); return (const char *)msgbuf; #else return (const char *)strerror(serrno); #endif } char const * comm_get_last_error_str(void) { return comm_get_error_str(comm_get_last_error()); } int comm_ipv4_present(void) { return XORP_OK; } int comm_ipv6_present(void) { #ifdef HAVE_IPV6 return XORP_OK; #else return XORP_ERROR; #endif /* HAVE_IPV6 */ } int comm_bindtodevice_present(void) { #ifdef SO_BINDTODEVICE return XORP_OK; #else return XORP_ERROR; #endif } int comm_keepalive_present(void) { #ifdef SO_KEEPALIVE return XORP_OK; #else return XORP_ERROR; #endif } int comm_linger_present(void) { #ifdef SO_LINGER return XORP_OK; #else return XORP_ERROR; #endif } int comm_nosigpipe_present(void) { #ifdef SO_NOSIGPIPE return XORP_OK; #else return XORP_ERROR; #endif } int comm_onesbcast_present(void) { #ifdef IP_ONESBCAST return XORP_OK; #else return XORP_ERROR; #endif } int comm_tos_present(void) { #ifdef IP_TOS return XORP_OK; #else return XORP_ERROR; #endif } int comm_nopush_present(void) { #ifdef TCP_NOPUSH return XORP_OK; #else return XORP_ERROR; #endif } int comm_unicast_ttl_present(void) { #ifdef IP_TTL return XORP_OK; #else return XORP_ERROR; #endif } xsock_t comm_open_tcp(int family, int is_blocking) { xsock_t sock; comm_init(); sock = comm_sock_open(family, SOCK_STREAM, 0, is_blocking); if (sock == XORP_BAD_SOCKET) return (XORP_BAD_SOCKET); return (sock); } xsock_t comm_open_udp(int family, int is_blocking) { xsock_t sock; comm_init(); sock = comm_sock_open(family, SOCK_DGRAM, 0, is_blocking); if (sock == XORP_BAD_SOCKET) return (XORP_BAD_SOCKET); return (sock); } int comm_close(xsock_t sock) { if (comm_sock_close(sock) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } int comm_listen(xsock_t sock, int backlog) { if (comm_sock_listen(sock, backlog) != XORP_OK) return (XORP_ERROR); return (XORP_OK); } xsock_t comm_bind_tcp4(const struct in_addr *my_addr, unsigned short my_port, int is_blocking) { xsock_t sock; comm_init(); sock = comm_sock_open(AF_INET, SOCK_STREAM, 0, is_blocking); if (sock == XORP_BAD_SOCKET) return (XORP_BAD_SOCKET); if (comm_set_reuseaddr(sock, 1) != XORP_OK) { comm_sock_close(sock); return (XORP_BAD_SOCKET); } if (comm_sock_bind4(sock, my_addr, my_port) != XORP_OK) { comm_sock_close(sock); return (XORP_BAD_SOCKET); } return (sock); } xsock_t comm_bind_tcp6(const struct in6_addr *my_addr, unsigned int my_ifindex, unsigned short my_port, int is_blocking) { #ifdef HAVE_IPV6 xsock_t sock; comm_init(); sock = comm_sock_open(AF_INET6, SOCK_STREAM, 0, is_blocking); if (sock == XORP_BAD_SOCKET) return (XORP_BAD_SOCKET); if (comm_set_reuseaddr(sock, 1) != XORP_OK) { comm_sock_close(sock); return (XORP_BAD_SOCKET); } if (comm_sock_bind6(sock, my_addr, my_ifindex, my_port) != XORP_OK) { comm_sock_close(sock); return (XORP_BAD_SOCKET); } return (sock); #else /* ! HAVE_IPV6 */ comm_sock_no_ipv6("comm_bind_tcp6", my_addr, my_ifindex, my_port, is_blocking); return (XORP_BAD_SOCKET); #endif /* ! HAVE_IPV6 */ } xsock_t comm_bind_tcp(const struct sockaddr *sock, int is_blocking) { switch (sock->sa_family) { case AF_INET: { const struct sockaddr_in *sin = (const struct sockaddr_in *)((const void *)sock); return comm_bind_tcp4(&sin->sin_addr, sin->sin_port, is_blocking); } break; #ifdef HAVE_IPV6 case AF_INET6: { const struct sockaddr_in6 *sin = (const struct sockaddr_in6 *)((const void *)sock); return comm_bind_tcp6(&sin->sin6_addr, sin->sin6_scope_id, sin->sin6_port, is_blocking); } break; #endif /* HAVE_IPV6 */ default: XLOG_FATAL("Error comm_bind_tcp invalid family = %d", sock->sa_family); break; } XLOG_UNREACHABLE(); return (XORP_BAD_SOCKET); } xsock_t comm_bind_udp4(const struct in_addr *my_addr, unsigned short my_port, int is_blocking, int reuse_flag) { xsock_t sock; comm_init(); sock = comm_sock_open(AF_INET, SOCK_DGRAM, 0, is_blocking); if (sock == XORP_BAD_SOCKET) return (XORP_BAD_SOCKET); /* For multicast, you need to set reuse before you bind, if you want * more than one socket to be able to bind to a particular IP (like, 0.0.0.0) */ if (reuse_flag) { if (comm_set_reuseaddr(sock, 1) != XORP_OK) { comm_sock_close(sock); return (XORP_BAD_SOCKET); } if (comm_set_reuseport(sock, 1) != XORP_OK) { comm_sock_close(sock); return (XORP_BAD_SOCKET); } } if (comm_sock_bind4(sock, my_addr, my_port) != XORP_OK) { comm_sock_close(sock); return (XORP_BAD_SOCKET); } return (sock); } xsock_t comm_bind_udp6(const struct in6_addr *my_addr, unsigned int my_ifindex, unsigned short my_port, int is_blocking) { #ifdef HAVE_IPV6 xsock_t sock; comm_init(); sock = comm_sock_open(AF_INET6, SOCK_DGRAM, 0, is_blocking); if (sock == XORP_BAD_SOCKET) return (XORP_BAD_SOCKET); if (comm_sock_bind6(sock, my_addr, my_ifindex, my_port) != XORP_OK) { comm_sock_close(sock); return (XORP_BAD_SOCKET); } return (sock); #else /* ! HAVE_IPV6 */ comm_sock_no_ipv6("comm_bind_udp6", my_addr, my_ifindex, my_port, is_blocking); return (XORP_BAD_SOCKET); #endif /* ! HAVE_IPV6 */ } xsock_t comm_bind_join_udp4(const struct in_addr *mcast_addr, const struct in_addr *join_if_addr, unsigned short my_port, int reuse_flag, int is_blocking) { xsock_t sock; comm_init(); sock = comm_sock_open(AF_INET, SOCK_DGRAM, 0, is_blocking); if (sock == XORP_BAD_SOCKET) return (XORP_BAD_SOCKET); if (reuse_flag) { if (comm_set_reuseaddr(sock, 1) != XORP_OK) { comm_sock_close(sock); return (XORP_BAD_SOCKET); } if (comm_set_reuseport(sock, 1) != XORP_OK) { comm_sock_close(sock); return (XORP_BAD_SOCKET); } } /* Bind the socket */ if (comm_sock_bind4(sock, NULL, my_port) != XORP_OK) { comm_sock_close(sock); return (XORP_BAD_SOCKET); } /* Join the multicast group */ if (comm_sock_join4(sock, mcast_addr, join_if_addr) != XORP_OK) { comm_sock_close(sock); return (XORP_BAD_SOCKET); } return (sock); } xsock_t comm_bind_join_udp6(const struct in6_addr *mcast_addr, unsigned int my_ifindex, unsigned short my_port, int reuse_flag, int is_blocking) { #ifdef HAVE_IPV6 xsock_t sock; comm_init(); sock = comm_sock_open(AF_INET6, SOCK_DGRAM, 0, is_blocking); if (sock == XORP_BAD_SOCKET) return (XORP_BAD_SOCKET); if (reuse_flag) { if (comm_set_reuseaddr(sock, 1) != XORP_OK) { comm_sock_close(sock); return (XORP_BAD_SOCKET); } if (comm_set_reuseport(sock, 1) != XORP_OK) { comm_sock_close(sock); return (XORP_BAD_SOCKET); } } /* Bind the socket */ if (comm_sock_bind6(sock, NULL, 0, my_port) != XORP_OK) { comm_sock_close(sock); return (XORP_BAD_SOCKET); } /* Join the multicast group */ if (comm_sock_join6(sock, mcast_addr, my_ifindex) != XORP_OK) { comm_sock_close(sock); return (XORP_BAD_SOCKET); } return (sock); #else /* ! HAVE_IPV6 */ comm_sock_no_ipv6("comm_bind_join_udp6", mcast_addr, my_ifindex, my_port, reuse_flag, is_blocking); return (XORP_BAD_SOCKET); #endif /* ! HAVE_IPV6 */ } xsock_t comm_connect_tcp4(const struct in_addr *remote_addr, unsigned short remote_port, int is_blocking, int *in_progress) { xsock_t sock; if (in_progress != NULL) *in_progress = 0; comm_init(); sock = comm_sock_open(AF_INET, SOCK_STREAM, 0, is_blocking); if (sock == XORP_BAD_SOCKET) return (XORP_BAD_SOCKET); if (comm_sock_connect4(sock, remote_addr, remote_port, is_blocking, in_progress) != XORP_OK) { /* * If this is a non-blocking socket and the connect couldn't * complete, then return the socket. */ if ((! is_blocking) && (in_progress != NULL) && (*in_progress == 1)) return (sock); comm_sock_close(sock); return (XORP_BAD_SOCKET); } return (sock); } xsock_t comm_connect_tcp6(const struct in6_addr *remote_addr, unsigned short remote_port, int is_blocking, int *in_progress) { #ifdef HAVE_IPV6 xsock_t sock; if (in_progress != NULL) *in_progress = 0; comm_init(); sock = comm_sock_open(AF_INET6, SOCK_STREAM, 0, is_blocking); if (sock == XORP_BAD_SOCKET) return (XORP_BAD_SOCKET); if (comm_sock_connect6(sock, remote_addr, remote_port, is_blocking, in_progress) != XORP_OK) { /* * If this is a non-blocking socket and the connect couldn't * complete, then return the socket. */ if ((! is_blocking) && (in_progress != NULL) && (*in_progress == 1)) return (sock); comm_sock_close(sock); return (XORP_BAD_SOCKET); } return (sock); #else /* ! HAVE_IPV6 */ if (in_progress != NULL) *in_progress = 0; comm_sock_no_ipv6("comm_connect_tcp6", remote_addr, remote_port, is_blocking, in_progress); return (XORP_BAD_SOCKET); #endif /* ! HAVE_IPV6 */ } xsock_t comm_connect_udp4(const struct in_addr *remote_addr, unsigned short remote_port, int is_blocking, int *in_progress) { xsock_t sock; if (in_progress != NULL) *in_progress = 0; comm_init(); sock = comm_sock_open(AF_INET, SOCK_DGRAM, 0, is_blocking); if (sock == XORP_BAD_SOCKET) return (XORP_BAD_SOCKET); if (comm_sock_connect4(sock, remote_addr, remote_port, is_blocking, in_progress) != XORP_OK) { /* * If this is a non-blocking socket and the connect couldn't * complete, then return the socket. */ if ((! is_blocking) && (in_progress != NULL) && (*in_progress == 1)) return (sock); comm_sock_close(sock); return (XORP_BAD_SOCKET); } return (sock); } xsock_t comm_connect_udp6(const struct in6_addr *remote_addr, unsigned short remote_port, int is_blocking, int *in_progress) { #ifdef HAVE_IPV6 xsock_t sock; if (in_progress != NULL) *in_progress = 0; comm_init(); sock = comm_sock_open(AF_INET6, SOCK_DGRAM, 0, is_blocking); if (sock == XORP_BAD_SOCKET) return (XORP_BAD_SOCKET); if (comm_sock_connect6(sock, remote_addr, remote_port, is_blocking, in_progress) != XORP_OK) { /* * If this is a non-blocking socket and the connect couldn't * complete, then return the socket. */ if ((! is_blocking) && (in_progress != NULL) && (*in_progress == 1)) return (sock); comm_sock_close(sock); return (XORP_BAD_SOCKET); } return (sock); #else /* ! HAVE_IPV6 */ if (in_progress != NULL) *in_progress = 0; comm_sock_no_ipv6("comm_connect_udp6", remote_addr, remote_port, is_blocking, in_progress); return (XORP_BAD_SOCKET); #endif /* ! HAVE_IPV6 */ } xsock_t comm_bind_connect_tcp4(const struct in_addr *local_addr, unsigned short local_port, const struct in_addr *remote_addr, unsigned short remote_port, int is_blocking, int *in_progress) { xsock_t sock; if (in_progress != NULL) *in_progress = 0; comm_init(); sock = comm_sock_open(AF_INET, SOCK_STREAM, 0, is_blocking); if (sock == XORP_BAD_SOCKET) return (XORP_BAD_SOCKET); if (comm_set_reuseaddr(sock, 1) != XORP_OK) { comm_sock_close(sock); return (XORP_BAD_SOCKET); } if (comm_sock_bind4(sock, local_addr, local_port) != XORP_OK) { comm_sock_close(sock); return (XORP_BAD_SOCKET); } if (comm_sock_connect4(sock, remote_addr, remote_port, is_blocking, in_progress) != XORP_OK) { /* * If this is a non-blocking socket and the connect couldn't * complete, then return the socket. */ if ((! is_blocking) && (in_progress != NULL) && (*in_progress == 1)) return (sock); comm_sock_close(sock); return (XORP_BAD_SOCKET); } return (sock); } xsock_t comm_bind_connect_tcp6(const struct in6_addr *local_addr, unsigned int my_ifindex, unsigned short local_port, const struct in6_addr *remote_addr, unsigned short remote_port, int is_blocking, int *in_progress) { #ifdef HAVE_IPV6 xsock_t sock; if (in_progress != NULL) *in_progress = 0; comm_init(); sock = comm_sock_open(AF_INET6, SOCK_STREAM, 0, is_blocking); if (sock == XORP_BAD_SOCKET) return (XORP_BAD_SOCKET); if (comm_set_reuseaddr(sock, 1) != XORP_OK) { comm_sock_close(sock); return (XORP_BAD_SOCKET); } if (comm_sock_bind6(sock, local_addr, my_ifindex, local_port) != XORP_OK) { comm_sock_close(sock); return (XORP_BAD_SOCKET); } if (comm_sock_connect6(sock, remote_addr, remote_port, is_blocking, in_progress) != XORP_OK) { /* * If this is a non-blocking socket and the connect couldn't * complete, then return the socket. */ if ((! is_blocking) && (in_progress != NULL) && (*in_progress == 1)) return (sock); comm_sock_close(sock); return (XORP_BAD_SOCKET); } return (sock); #else /* ! HAVE_IPV6 */ if (in_progress != NULL) *in_progress = 0; comm_sock_no_ipv6("comm_bind_connect_tcp6", local_addr, my_ifindex, local_port, remote_addr, remote_port, is_blocking, in_progress); return (XORP_BAD_SOCKET); #endif /* ! HAVE_IPV6 */ } xsock_t comm_bind_connect_udp4(const struct in_addr *local_addr, unsigned short local_port, const struct in_addr *remote_addr, unsigned short remote_port, int is_blocking, int *in_progress) { xsock_t sock; if (in_progress != NULL) *in_progress = 0; comm_init(); sock = comm_sock_open(AF_INET, SOCK_DGRAM, 0, is_blocking); if (sock == XORP_BAD_SOCKET) return (XORP_BAD_SOCKET); if (comm_sock_bind4(sock, local_addr, local_port) != XORP_OK) { comm_sock_close(sock); return (XORP_BAD_SOCKET); } if (comm_sock_connect4(sock, remote_addr, remote_port, is_blocking, in_progress) != XORP_OK) { /* * If this is a non-blocking socket and the connect couldn't * complete, then return the socket. */ if ((! is_blocking) && (in_progress != NULL) && (*in_progress == 1)) return (sock); comm_sock_close(sock); return (XORP_BAD_SOCKET); } return (sock); } xsock_t comm_bind_connect_udp6(const struct in6_addr *local_addr, unsigned int my_ifindex, unsigned short local_port, const struct in6_addr *remote_addr, unsigned short remote_port, int is_blocking, int *in_progress) { #ifdef HAVE_IPV6 xsock_t sock; if (in_progress != NULL) *in_progress = 0; comm_init(); sock = comm_sock_open(AF_INET6, SOCK_DGRAM, 0, is_blocking); if (sock == XORP_BAD_SOCKET) return (XORP_BAD_SOCKET); if (comm_sock_bind6(sock, local_addr, my_ifindex, local_port) != XORP_OK) { comm_sock_close(sock); return (XORP_BAD_SOCKET); } if (comm_sock_connect6(sock, remote_addr, remote_port, is_blocking, in_progress) != XORP_OK) { /* * If this is a non-blocking socket and the connect couldn't * complete, then return the socket. */ if ((! is_blocking) && (in_progress != NULL) && (*in_progress == 1)) return (sock); comm_sock_close(sock); return (XORP_BAD_SOCKET); } return (sock); #else /* ! HAVE_IPV6 */ if (in_progress != NULL) *in_progress = 0; comm_sock_no_ipv6("comm_bind_connect_udp6", local_addr, my_ifindex, local_port, remote_addr, remote_port, is_blocking, in_progress); return (XORP_BAD_SOCKET); #endif /* ! HAVE_IPV6 */ } #ifndef HOST_OS_WINDOWS #include static int comm_unix_setup(struct sockaddr_un* s_un, const char* path) { if (strlen(path) >= sizeof(s_un->sun_path)) { XLOG_ERROR("UNIX socket path too long: %s [sz %u max %u]", path, XORP_UINT_CAST(strlen(path)), XORP_UINT_CAST(sizeof(s_un->sun_path))); return -1; } memset(s_un, 0, sizeof(*s_un)); s_un->sun_family = AF_UNIX; snprintf(s_un->sun_path, sizeof(s_un->sun_path), "%s", path); return 0; } xsock_t comm_bind_unix(const char* path, int is_blocking) { xsock_t sock; struct sockaddr_un s_un; comm_init(); if (comm_unix_setup(&s_un, path) == -1) return (XORP_BAD_SOCKET); sock = comm_sock_open(s_un.sun_family, SOCK_STREAM, 0, is_blocking); if (sock == XORP_BAD_SOCKET) return (XORP_BAD_SOCKET); if (bind(sock, (struct sockaddr*) &s_un, sizeof(s_un)) == -1) { _comm_set_serrno(); XLOG_ERROR("Error binding UNIX socket. Path: %s. Error: %s", s_un.sun_path, comm_get_error_str(comm_get_last_error())); comm_sock_close(sock); return (XORP_BAD_SOCKET); } return (sock); } xsock_t comm_connect_unix(const char* path, int is_blocking) { xsock_t sock; struct sockaddr_un s_un; comm_init(); if (comm_unix_setup(&s_un, path) == -1) return (XORP_BAD_SOCKET); sock = comm_sock_open(s_un.sun_family, SOCK_STREAM, 0, is_blocking); if (sock == XORP_BAD_SOCKET) return (XORP_BAD_SOCKET); if (connect(sock, (struct sockaddr*) &s_un, sizeof(s_un)) == -1) { _comm_set_serrno(); if (is_blocking || comm_get_last_error() != EINPROGRESS) { XLOG_ERROR("Error connecting to unix socket. Path: %s. Error: %s", s_un.sun_path, comm_get_error_str(comm_get_last_error())); comm_sock_close(sock); return (XORP_BAD_SOCKET); } } return (sock); } #else /* ! HOST_OS_WINDOWS */ xsock_t comm_bind_unix(const char* path, int is_blocking) { UNUSED(path); UNUSED(is_blocking); XLOG_ERROR("No UNIX sockets on Windows"); return (XORP_BAD_SOCKET); } xsock_t comm_connect_unix(const char* path, int is_blocking) { return comm_bind_unix(path, is_blocking); } #endif /* HOST_OS_WINDOWS */ xorp/libcomm/comm_api.h0000664000076400007640000011274011633743677015265 0ustar greearbgreearb/* -*- Mode:C; c-basic-offset:4; tab-width:8; indent-tabs-mode:t -*- */ /* vim:set sts=4 ts=8: */ /* * Copyright (c) 2001-2011 XORP, Inc and Others * YOID Project. * University of Southern California/Information Sciences Institute. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef __LIBCOMM_COMM_API_H__ #define __LIBCOMM_COMM_API_H__ /* * COMM socket library API header. */ #ifdef HAVE_SYS_SOCKET_H #include #endif #ifdef HAVE_NETINET_IN_H #include #endif /* * Constants definitions */ #define SO_RCV_BUF_SIZE_MIN (48*1024) /* min. rcv socket buf size */ #define SO_RCV_BUF_SIZE_MAX (256*1024) /* desired rcv socket buf size */ #define SO_SND_BUF_SIZE_MIN (48*1024) /* min. snd socket buf size */ #define SO_SND_BUF_SIZE_MAX (256*1024) /* desired snd socket buf size */ #define COMM_SOCK_ADDR_PORT_REUSE 1 #define COMM_SOCK_ADDR_PORT_DONTREUSE 0 #define COMM_SOCK_BLOCKING 1 #define COMM_SOCK_NONBLOCKING 0 #define COMM_LISTEN_DEFAULT_BACKLOG 50 #ifndef AF_LOCAL #define AF_LOCAL AF_UNIX /* XXX: AF_UNIX is the older name */ #endif /* * Structures, typedefs and macros */ #ifndef HTONS #define HTONS(x) ((x) = htons((u_short)(x))) #endif #ifndef NTOHS #define NTOHS(x) ((x) = ntohs((u_short)(x))) #endif #ifndef HTONL #define HTONL(x) ((x) = htonl((u_long)(x))) #endif #ifndef NTOHL #define NTOHL(x) ((x) = ntohl((u_long)(x))) #endif /* * Global variables */ /* * Global functions prototypes */ __BEGIN_DECLS /* * The high-level `user' API. */ /** * Library initialization. Need be called only once, during startup. * * Note: Currently it is not thread-safe. * * @return XORP_OK on success, otherwise XORP_ERROR. */ extern int comm_init(void); /** * Library termination/cleanup. Must be called at process exit. * * Note: Currently it is not thread-safe. */ extern void comm_exit(void); /** * Retrieve the most recently occured error for the current thread. * * @return operating system specific error code for this thread's * last socket operation. */ extern int comm_get_last_error(void); /** * Retrieve a human readable string (in English) for the given error code. * * Note: Currently it is not thread-safe. * * @param serrno the socket error number returned by comm_get_last_error(). * @return a pointer to a string giving more information about the error. */ extern char const * comm_get_error_str(int serrno); /** * Retrieve a human readable string (in English) for the last error. * * @return a human readable string of the last error. */ extern char const *comm_get_last_error_str(void); /** * Test whether the underlying system has IPv4 support. * * @return XORP_OK on success, otherwise XORP_ERROR. */ extern int comm_ipv4_present(void); /** * Test whether the underlying system has IPv6 support. * * @return XORP_OK on success, otherwise XORP_ERROR. */ extern int comm_ipv6_present(void); /** * Test whether the underlying system has SO_BINDTODEVICE support. * * @return XORP_OK on success, otherwise XORP_ERROR. */ extern int comm_bindtodevice_present(void); /** * Test whether the underlying system has SO_LINGER support. * * @return XORP_OK on success, otherwise XORP_ERROR. */ extern int comm_linger_present(void); /** * Test whether the underlying system has SO_KEEPALIVE support. * * @return XORP_OK on success, otherwise XORP_ERROR. */ extern int comm_keepalive_present(void); /** * Test whether the underlying system has SO_NOSIGPIPE support. * * @return XORP_OK on success, otherwise XORP_ERROR. */ extern int comm_nosigpipe_present(void); /** * Test whether the underlying system has IP_ONESBCAST support. * * @return XORP_OK on success, otherwise XORP_ERROR. */ extern int comm_onesbcast_present(void); /** * Test whether the underlying system has IP_TOS support. * * @return XORP_OK on success, otherwise XORP_ERROR. */ extern int comm_tos_present(void); /** * Test whether the underlying system has TCP_NOPUSH support. * * @return XORP_OK on success, otherwise XORP_ERROR. */ extern int comm_nopush_present(void); /** * Test whether the underlying system has IP_TTL support. * * @return XORP_OK on success, otherwise XORP_ERROR. */ extern int comm_unicast_ttl_present(void); /** * Open a TCP socket. * * @param family the address family. * @param is_blocking if true then the socket will be blocking, otherwise * non-blocking. * @return the new socket on success, otherwsise XORP_BAD_SOCKET. */ extern xsock_t comm_open_tcp(int family, int is_blocking); /** * Open an UDP socket. * * @param family the address family. * @param is_blocking if true then the socket will be blocking, otherwise * non-blocking. * @return the new socket on success, otherwsise XORP_BAD_SOCKET. */ extern xsock_t comm_open_udp(int family, int is_blocking); /** * Close a socket. * * @param sock the socket to close. * @return XORP_OK on success, otherwise XORP_ERROR. */ extern int comm_close(xsock_t sock); /** * Listen on a socket. * * @param sock the socket to listen on. * @param backlog the maximum queue size for pending connections. * @return XORP_OK on success, otherwise XORP_ERROR. */ extern int comm_listen(xsock_t sock, int backlog); /** * Open an IPv4 TCP socket and bind it to a local address and a port. * * @param my_addr the local IPv4 address to bind to (in network order). * If it is NULL, will bind to `any' local address. * @param my_port the local port to bind to (in network order). * @param is_blocking if true then the socket will be blocking, otherwise * non-blocking. * @return the new socket on success, otherwise XORP_BAD_SOCKET. */ extern xsock_t comm_bind_tcp4(const struct in_addr *my_addr, unsigned short my_port, int is_blocking); /** * Open an IPv6 TCP socket and bind it to a local address and a port. * * @param my_addr the local IPv6 address to bind to (in network order). * If it is NULL, will bind to `any' local address. * @param my_ifindex the local network interface index to bind to. * It is required if @ref my_addr is a link-local address, otherwise it * should be set to 0. * @param my_port the local port to bind to (in network order). * @param is_blocking if true then the socket will be blocking, otherwise * non-blocking. * @return the new socket on success, otherwise XORP_BAD_SOCKET. */ extern xsock_t comm_bind_tcp6(const struct in6_addr *my_addr, unsigned int my_ifindex, unsigned short my_port, int is_blocking); /** * Open a TCP (IPv4 or IPv6) socket and bind it to a local address and a port. * * @param sin agnostic sockaddr containing the local address (If it is * NULL, will bind to `any' local address.) and the local port to * bind to all in network order. * @param is_blocking if true then the socket will be blocking, otherwise * non-blocking. * @return the new socket on success, otherwise XORP_BAD_SOCKET. */ extern xsock_t comm_bind_tcp(const struct sockaddr *sin, int is_blocking); /** * Open an IPv4 UDP socket and bind it to a local address and a port. * * @param my_addr the local IPv4 address to bind to (in network order). * If it is NULL, will bind to `any' local address. * @param my_port the local port to bind to (in network order). * @param is_blocking if true then the socket will be blocking, otherwise * non-blocking. * @return the new socket on success, otherwise XORP_BAD_SOCKET. */ extern xsock_t comm_bind_udp4(const struct in_addr *my_addr, unsigned short my_port, int is_blocking, int reuse_flag); /** * Open an IPv6 UDP socket and bind it to a local address and a port. * * @param my_addr the local IPv6 address to bind to (in network order). * If it is NULL, will bind to `any' local address. * @param my_ifindex the network interface index to bind to. * It is required if @ref my_addr is a link-local address, otherwise it * should be set to 0. * @param my_port the local port to bind to (in network order). * @param is_blocking if true then the socket will be blocking, otherwise * non-blocking. * @return the new socket on success, otherwise XORP_BAD_SOCKET. */ extern xsock_t comm_bind_udp6(const struct in6_addr *my_addr, unsigned int my_ifindex, unsigned short my_port, int is_blocking); /** * Open an IPv4 UDP socket on an interface, bind it to a port, * and join a multicast group. * * Note that we bind to ANY address instead of the multicast address * only. If we bind to the multicast address instead, then using * the same socket for sending multicast packets will trigger a bug * in the FreeBSD kernel: the source IP address will be set to the * multicast address. Hence, the application itself may want to filter * the UDP unicast packets that may have arrived with a destination address * one of the local interface addresses and the same port number. * * @param mcast_addr the multicast address to join. * @param join_if_addr the local unicast interface address (in network order) * to join the multicast group on. If it is NULL, the system will choose the * interface each time a datagram is sent. * @param my_port the port to bind to (in network order). * @param reuse_flag if true, allow other sockets to bind to the same multicast * address and port, otherwise disallow it. * @param is_blocking if true then the socket will be blocking, otherwise * non-blocking. * @return the new socket on success, otherwise XORP_BAD_SOCKET. */ extern xsock_t comm_bind_join_udp4(const struct in_addr *mcast_addr, const struct in_addr *join_if_addr, unsigned short my_port, int reuse_flag, int is_blocking); /** * Open an IPv6 UDP socket on an interface, bind it to a port, * and join a multicast group. * * Note that we bind to ANY address instead of the multicast address * only. If we bind to the multicast address instead, then using * the same socket for sending multicast packets will trigger a bug * in the FreeBSD kernel: the source IP address will be set to the * multicast address. Hence, the application itself may want to filter * the UDP unicast packets that may have arrived with a destination address * one of the local interface addresses and the same port number. * * @param mcast_addr the multicast address to join. * @param my_ifindex the local network interface index to join the multicast * group on. If it is 0, the system will choose the interface each time a * datagram is sent. * @param my_port the port to bind to (in network order). * @param reuse_flag if true, allow other sockets to bind to the same multicast * address and port, otherwise disallow it. * @param is_blocking if true then the socket will be blocking, otherwise * non-blocking. * @return the new socket on success, otherwise XORP_BAD_SOCKET. */ extern xsock_t comm_bind_join_udp6(const struct in6_addr *mcast_addr, unsigned int my_ifindex, unsigned short my_port, int reuse_flag, int is_blocking); /** * Open an IPv4 TCP socket, and connect it to a remote address and port. * * @param remote_addr the remote address to connect to. * @param remote_port the remote port to connect to. * @param is_blocking if true then the socket will be blocking, otherwise * non-blocking. * @param in_progress if the socket is non-blocking and the connect cannot be * completed immediately, then the referenced value is set to 1, and the * return value is the new socket. If the non-blocking socket was connected, * the referenced value is set to 0. If the return value is XORP_BAD_SOCKET * or if the socket is blocking, then the return value is undefined. * @return the new socket on success, otherwise XORP_BAD_SOCKET. */ extern xsock_t comm_connect_tcp4(const struct in_addr *remote_addr, unsigned short remote_port, int is_blocking, int *in_progress); /** * Open an IPv6 TCP socket, and connect it to a remote address and port. * * @param remote_addr the remote address to connect to. * @param remote_port the remote port to connect to. * @param is_blocking if true then the socket will be blocking, otherwise * non-blocking. * @param in_progress if the socket is non-blocking and the connect cannot be * completed immediately, then the referenced value is set to 1, and the * return value is the new socket. If the non-blocking socket was connected, * the referenced value is set to 0. If the return value is XORP_BAD_SOCKET * or if the socket is blocking, then the return value is undefined. * @return the new socket on success, otherwise XORP_BAD_SOCKET. */ extern xsock_t comm_connect_tcp6(const struct in6_addr *remote_addr, unsigned short remote_port, int is_blocking, int *in_progress); /** * Open an IPv4 UDP socket, and connect it to a remote address and port. * * @param remote_addr the remote address to connect to. * @param remote_port the remote port to connect to. * @param is_blocking if true then the socket will be blocking, otherwise * non-blocking. * @param in_progress if the socket is non-blocking and the connect cannot be * completed immediately, then the referenced value is set to 1, and the * return value is the new socket. If the non-blocking socket was connected, * the referenced value is set to 0. If the return value is XORP_BAD_SOCKET * or if the socket is blocking, then the return value is undefined. * @return the new socket on success, otherwise XORP_BAD_SOCKET. */ extern xsock_t comm_connect_udp4(const struct in_addr *remote_addr, unsigned short remote_port, int is_blocking, int *in_progress); /** * Open an IPv6 UDP socket, and connect it to a remote address and port. * * @param remote_addr the remote address to connect to. * @param remote_port the remote port to connect to. * @param is_blocking if true then the socket will be blocking, otherwise * non-blocking. * @param in_progress if the socket is non-blocking and the connect cannot be * completed immediately, then the referenced value is set to 1, and the * return value is the new socket. If the non-blocking socket was connected, * the referenced value is set to 0. If the return value is XORP_BAD_SOCKET * or if the socket is blocking, then the return value is undefined. * @return the new socket on success, otherwise XORP_BAD_SOCKET. */ extern xsock_t comm_connect_udp6(const struct in6_addr *remote_addr, unsigned short remote_port, int is_blocking, int *in_progress); /** * Open an IPv4 TCP socket, bind it to a local address and a port, * and connect it to a remote address and port. * * @param local_addr the local address to bind to. * If it is NULL, will bind to `any' local address. * @param local_port the local port to bind to. * @param remote_addr the remote address to connect to. * @param remote_port the remote port to connect to. * @param is_blocking if true then the socket will be blocking, otherwise * non-blocking. * @param in_progress if the socket is non-blocking and the connect cannot be * completed immediately, then the referenced value is set to 1, and the * return value is the new socket. If the non-blocking socket was connected, * the referenced value is set to 0. If the return value is XORP_BAD_SOCKET * or if the socket is blocking, then the return value is undefined. * @return the new socket on success, otherwise XORP_BAD_SOCKET. */ extern xsock_t comm_bind_connect_tcp4(const struct in_addr *local_addr, unsigned short local_port, const struct in_addr *remote_addr, unsigned short remote_port, int is_blocking, int *in_progress); /** * Open an IPv6 TCP socket, bind it to a local address and a port, * and connect it to a remote address and port. * * @param local_addr the local address to bind to. * If it is NULL, will bind to `any' local address. * @param my_ifindex the network interface index to bind to. * It is required if @ref local_addr is a link-local address, otherwise it * should be set to 0. * @param local_port the local port to bind to. * @param remote_addr the remote address to connect to. * @param remote_port the remote port to connect to. * @param is_blocking if true then the socket will be blocking, otherwise * non-blocking. * @param in_progress if the socket is non-blocking and the connect cannot be * completed immediately, then the referenced value is set to 1, and the * return value is the new socket. If the non-blocking socket was connected, * the referenced value is set to 0. If the return value is XORP_BAD_SOCKET * or if the socket is blocking, then the return value is undefined. * @return the new socket on success, otherwise XORP_BAD_SOCKET. */ extern xsock_t comm_bind_connect_tcp6(const struct in6_addr *local_addr, unsigned int my_ifindex, unsigned short local_port, const struct in6_addr *remote_addr, unsigned short remote_port, int is_blocking, int *in_progress); /** * Open an IPv4 UDP socket, bind it to a local address and a port, * and connect it to a remote address and port. * * @param local_addr the local address to bind to. * If it is NULL, will bind to `any' local address. * @param local_port the local port to bind to. * @param remote_addr the remote address to connect to. * @param remote_port the remote port to connect to. * @param is_blocking if true then the socket will be blocking, otherwise * non-blocking. * @param in_progress if the socket is non-blocking and the connect cannot be * completed immediately, then the referenced value is set to 1, and the * return value is the new socket. If the non-blocking socket was connected, * the referenced value is set to 0. If the return value is XORP_BAD_SOCKET * or if the socket is blocking, then the return value is undefined. * @return the new socket on success, otherwise XORP_BAD_SOCKET. */ extern xsock_t comm_bind_connect_udp4(const struct in_addr *local_addr, unsigned short local_port, const struct in_addr *remote_addr, unsigned short remote_port, int is_blocking, int *in_progress); /** * Open an IPv6 UDP socket, bind it to a local address and a port, * and connect it to a remote address and port. * * @param local_addr the local address to bind to. * If it is NULL, will bind to `any' local address. * @param my_ifindex the network interface index to bind to. * It is required if @ref local_addr is a link-local address, otherwise it * should be set to 0. * @param local_port the local port to bind to. * @param remote_addr the remote address to connect to. * @param remote_port the remote port to connect to. * @param is_blocking if true then the socket will be blocking, otherwise * non-blocking. * @param in_progress if the socket is non-blocking and the connect cannot be * completed immediately, then the referenced value is set to 1, and the * return value is the new socket. If the non-blocking socket was connected, * the referenced value is set to 0. If the return value is XORP_BAD_SOCKET * or if the socket is blocking, then the return value is undefined. * @return the new socket on success, otherwise XORP_BAD_SOCKET. */ extern xsock_t comm_bind_connect_udp6(const struct in6_addr *local_addr, unsigned int my_ifindex, unsigned short local_port, const struct in6_addr *remote_addr, unsigned short remote_port, int is_blocking, int *in_progress); extern xsock_t comm_bind_unix(const char* path, int is_blocking); extern xsock_t comm_connect_unix(const char* path, int is_blocking); /* * The low-level `sock' API. */ /** * Open a socket for given domain, type and protocol. * * The sending and receiving buffer size are set, and the socket * itself is set with TCP_NODELAY (if a TCP socket). * * @param domain the domain of the socket (e.g., AF_INET, AF_INET6). * @param type the type of the socket (e.g., SOCK_STREAM, SOCK_DGRAM). * @param protocol the particular protocol to be used with the socket. * @param is_blocking if true then the socket will be blocking, otherwise * non-blocking. * @return the open socket on success, otherwise XORP_BAD_SOCKET. */ extern xsock_t comm_sock_open(int domain, int type, int protocol, int is_blocking); /** * Create a pair of connected sockets. * * The sockets will be created in the blocking state by default, and * with no additional socket options set. * * On Windows platforms, a domain of AF_UNIX, AF_INET, or AF_INET6 must * be specified. For the AF_UNIX case, the sockets created will actually * be in the AF_INET domain. The protocol field is ignored. * * On UNIX, this function simply wraps the socketpair(2) system call. * * @param domain the domain of the socket (e.g., AF_INET, AF_INET6). * @param type the type of the socket (e.g., SOCK_STREAM, SOCK_DGRAM). * @param protocol the particular protocol to be used with the socket. * @param sv pointer to an array of two xsock_t handles to receive the * allocated socket pair. * @return XORP_OK on success, otherwise XORP_ERROR. */ extern int comm_sock_pair(int domain, int type, int protocol, xsock_t sv[2]); /** * Bind an IPv4 socket to an address and a port. * * @param sock the socket to bind. * @param my_addr the address to bind to (in network order). * If it is NULL, will bind to `any' local address. * @param my_port the port to bind to (in network order). * @return XORP_OK on success, otherwise XORP_ERROR. */ extern int comm_sock_bind4(xsock_t sock, const struct in_addr *my_addr, unsigned short my_port); /** * Bind an IPv6 socket to an address and a port. * * @param sock the socket to bind. * @param my_addr the address to bind to (in network order). * If it is NULL, will bind to `any' local address. * @param my_ifindex the network interface index to bind to. * It is required if @ref my_addr is a link-local address, otherwise it * should be set to 0. * @param my_port the port to bind to (in network order). * @return XORP_OK on success, otherwise XORP_ERROR. */ extern int comm_sock_bind6(xsock_t sock, const struct in6_addr *my_addr, unsigned int my_ifindex, unsigned short my_port); /** * Bind a socket (IPv4 or IPv6) to an address and a port. * * @param sock the socket to bind. * @param sin agnostic sockaddr containing the local address (If it is * NULL, will bind to `any' local address.) and the local port to * bind to all in network order. * @return XORP_OK on success, otherwise XORP_ERROR. */ extern int comm_sock_bind(xsock_t sock, const struct sockaddr *sin); /** * Join an IPv4 multicast group on a socket (and an interface). * * @param sock the socket to join the group. * @param mcast_addr the multicast address to join. * @param my_addr the local unicast address of an interface to join. * If it is NULL, the interface is chosen by the kernel. * @return XORP_OK on success, otherwise XORP_ERROR. */ extern int comm_sock_join4(xsock_t sock, const struct in_addr *mcast_addr, const struct in_addr *my_addr); /** * Join an IPv6 multicast group on a socket (and an interface). * * @param sock he socket to join the group. * @param mcast_addr the multicast address to join. * @param my_ifindex the local network interface index to join. * If it is 0, the interface is chosen by the kernel. * @return XORP_OK on success, otherwise XORP_ERROR. */ extern int comm_sock_join6(xsock_t sock, const struct in6_addr *mcast_addr, unsigned int my_ifindex); /** * Leave an IPv4 multicast group on a socket (and an interface). * * @param sock the socket to leave the group. * @param mcast_addr the multicast address to leave. * @param my_addr the local unicast address of an interface to leave. * If it is NULL, the interface is chosen by the kernel. * @return XORP_OK on success, otherwise XORP_ERROR. */ extern int comm_sock_leave4(xsock_t sock, const struct in_addr *mcast_addr, const struct in_addr *my_addr); /** * Leave an IPv6 multicast group on a socket (and an interface). * * @param sock he socket to leave the group. * @param mcast_addr the multicast address to leave. * @param my_ifindex the local network interface index to leave. * If it is 0, the interface is chosen by the kernel. * @return XORP_OK on success, otherwise XORP_ERROR. */ extern int comm_sock_leave6(xsock_t sock, const struct in6_addr *mcast_addr, unsigned int my_ifindex); /** * Connect to a remote IPv4 address. * * Note that we can use this function not only for TCP, but for UDP sockets * as well. * * @param sock the socket to use to connect. * @param remote_addr the remote address to connect to. * @param remote_port the remote port to connect to. * @param is_blocking if true, the socket is blocking, otherwise non-blocking. * @param in_progress if the socket is non-blocking and the connect cannot be * completed immediately, then the referenced value is set to 1, and the * return value is XORP_ERROR. For all other errors or if the non-blocking * socket was connected, the referenced value is set to 0. If the return value * is XORP_OK or if the socket is blocking, then the return value is undefined. * @return XORP_OK on success, otherwise XORP_ERROR. */ extern int comm_sock_connect4(xsock_t sock, const struct in_addr *remote_addr, unsigned short remote_port, int is_blocking, int *in_progress); /** * Connect to a remote IPv6 address. * * Note that we can use this function not only for TCP, but for UDP sockets * as well. * * @param sock the socket to use to connect. * @param remote_addr the remote address to connect to. * @param remote_port the remote port to connect to. * @param is_blocking if true, the socket is blocking, otherwise non-blocking. * @param in_progress if the socket is non-blocking and the connect cannot be * completed immediately, then the referenced value is set to 1, and the * return value is XORP_ERROR. For all other errors or if the non-blocking * socket was connected, the referenced value is set to 0. If the return value * is XORP_OK or if the socket is blocking, then the return value is undefined. * @return XORP_OK on success, otherwise XORP_ERROR. */ extern int comm_sock_connect6(xsock_t sock, const struct in6_addr *remote_addr, unsigned short remote_port, int is_blocking, int *in_progress); /** * Connect to a remote address (IPv4 or IPv6). * * Note that we can use this function not only for TCP, but for UDP sockets * as well. * * @param sock the socket to use to connect. * @param sin agnostic sockaddr containing the local address (If it is * NULL, will bind to `any' local address.) and the local port to * bind to all in network order. * @param is_blocking if true, the socket is blocking, otherwise non-blocking. * @param in_progress if the socket is non-blocking and the connect cannot be * completed immediately, then the referenced value is set to 1, and the * return value is XORP_ERROR. For all other errors or if the non-blocking * socket was connected, the referenced value is set to 0. If the return value * is XORP_OK or if the socket is blocking, then the return value is undefined. * @return XORP_OK on success, otherwise XORP_ERROR. */ extern int comm_sock_connect(xsock_t sock, const struct sockaddr *sin, int is_blocking, int *in_progress); /** * Accept a connection on a listening socket. * * @param sock the listening socket to accept on. * @return the accepted socket on success, otherwise XORP_BAD_SOCKET. */ extern xsock_t comm_sock_accept(xsock_t sock); /** * Listen for connections on a socket. * * @param sock the socket to listen on. * @param backlog the maximum queue size for pending connections * @return XORP_OK on success, otherwise XORP_ERROR. */ extern int comm_sock_listen(xsock_t sock, int backlog); /** * Close a socket. * * @param sock the socket to close. * @return XORP_OK on success, otherwise XORP_ERROR. */ extern int comm_sock_close(xsock_t sock); /** * Set/reset the SO_BROADCAST option on a socket. * * @param sock the socket whose option we want to set/reset. * @param val if non-zero, the option will be set, otherwise will be reset. * @return XORP_OK on success, otherwise XORP_ERROR. */ extern int comm_set_send_broadcast(xsock_t sock, int val); /** * Set/reset the IP_RECEIVE_BROADCAST option on a socket. * This option is specific to Windows Server 2003 and later. * * @param sock the socket whose option we want to set/reset. * @param val if non-zero, the option will be set, otherwise will be reset. * @return XORP_OK on success, otherwise XORP_ERROR. */ extern int comm_set_receive_broadcast(xsock_t sock, int val); /** * Set/reset the TCP_NODELAY option on a TCP socket. * * @param sock the socket whose option we want to set/reset. * @param val if non-zero, the option will be set, otherwise will be reset. * @return XORP_OK on success, otherwise XORP_ERROR. */ extern int comm_set_nodelay(xsock_t sock, int val); /** * Set/reset the TCP_NOPUSH option on a TCP socket. * * @param sock the socket whose option we want to set/reset. * @param val if non-zero, the option will be set, otherwise will be reset. * @return XORP_OK on success, otherwise XORP_ERROR. */ extern int comm_set_nopush(xsock_t sock, int val); /** * Set/reset the SO_LINGER option on a socket. * * @param sock the socket whose option we want to set/reset. * @param val if non-zero, the option will be set, otherwise will be reset. * @return XORP_OK on success, otherwise XORP_ERROR. */ extern int comm_set_linger(xsock_t sock, int enabled, int secs); /** * Set/reset the SO_KEEPALIVE option on a socket. * * @param sock the socket whose option we want to set/reset. * @param val if non-zero, the option will be set, otherwise will be reset. * @return XORP_OK on success, otherwise XORP_ERROR. */ extern int comm_set_keepalive(xsock_t sock, int val); /** * Set/reset the SO_NOSIGPIPE option on a socket. * * @param sock the socket whose option we want to set/reset. * @param val if non-zero, the option will be set, otherwise will be reset. * @return XORP_OK on success, otherwise XORP_ERROR. */ extern int comm_set_nosigpipe(xsock_t sock, int val); /** * Set/reset the SO_REUSEADDR option on a socket. * * Note: If the OS doesn't support this option, then XORP_ERROR is returned. * * @param sock the socket whose option we want to set/reset. * @param val if non-zero, the option will be set, otherwise will be reset. * @return XORP_OK on success, otherwise XORP_ERROR. */ extern int comm_set_reuseaddr(xsock_t sock, int val); /** * Set/reset the SO_REUSEPORT option on a socket. * * Note: If the OS doesn't support this option, then XORP_ERROR is returned. * * @param sock the socket whose option we want to set/reset. * @param val if non-zero, the option will be set, otherwise will be reset. * @return XORP_OK on success, otherwise XORP_ERROR. */ extern int comm_set_reuseport(xsock_t sock, int val); /** * Set/reset the multicast loopback option on a socket. * * @param sock the socket whose option we want to set/reset. * @param val if non-zero, the option will be set, otherwise will be reset. * @return XORP_OK on success, otherwise XORP_ERROR. */ extern int comm_set_loopback(xsock_t sock, int val); /** * Set/reset the TCP_MD5SIG option on a socket. * * Note: If the OS doesn't support this option, XORP_ERROR is returned. * * @param sock the socket whose option we want to set/reset. * @param val if non-zero, the option will be set, otherwise will be reset. * @return XORP_OK on success, otherwise XORP_ERROR. */ extern int comm_set_tcpmd5(xsock_t sock, int val); /** * Set the ip_tos bits of the outgoing packets on a socket. * * @param sock the socket whose ip_tos we want to set. * @param val the ip_tos of the outgoing packets. * @return XORP_OK on success, otherwise XORP_ERROR. */ extern int comm_set_tos(xsock_t sock, int val); /** * Set the TTL of the outgoing unicast/broadcast packets on a socket. * * @param sock the socket whose TTL we want to set. * @param val the TTL of the outgoing unicast/broadcast packets. * @return XORP_OK on success, otherwise XORP_ERROR. */ extern int comm_set_unicast_ttl(xsock_t sock, int val); /** * Set the TTL of the outgoing multicast packets on a socket. * * @param sock the socket whose TTL we want to set. * @param val the TTL of the outgoing multicast packets. * @return XORP_OK on success, otherwise XORP_ERROR. */ extern int comm_set_multicast_ttl(xsock_t sock, int val); /** * Set default interface for IPv4 outgoing multicast on a socket. * * @param sock the socket whose default multicast interface to set. * @param in_addr the IPv4 address of the default interface to set. * If @ref in_addr is NULL, the system will choose the interface each time * a datagram is sent. * @return XORP_OK on success, otherwise XORP_ERROR. */ extern int comm_set_iface4(xsock_t sock, const struct in_addr *in_addr); /** * Set default interface for IPv6 outgoing multicast on a socket. * * @param sock the socket whose default multicast interface to set. * @param my_ifindex the local network interface index of the default * interface to set. If it is 0, the system will choose the interface each time * a datagram is sent. * @return XORP_OK on success, otherwise XORP_ERROR. */ extern int comm_set_iface6(xsock_t sock, unsigned int my_ifindex); /** * Set the interface to which a socket is bound. * * XXX: This exists to support XORP's use of the 255.255.255.255 * address in Linux for MANET protocols, as well as certain limited * uses with raw IPv4 sockets, and SHOULD NOT be used in new code. * * @param sock the socket to be bound to @param my_ifname * @param my_ifname the name of the local network interface to which the * socket should be bound. * @return XORP_OK on success, otherwise XORP_ERROR. */ extern int comm_set_bindtodevice(xsock_t sock, const char * my_ifname); /** * Set the option which causes sends to directed IPv4 broadcast addresses * to go to 255.255.255.255 instead. * * XXX: This exists to support XORP's use of the 255.255.255.255 * address in BSD-derived systems for MANET protocols. It SHOULD NOT * be used in new code. * * @param sock the socket to enable undirected broadcasts for. * @param enabled zero to disable the option, non-zero to enable it. * @return XORP_OK on success, otherwise XORP_ERROR. */ extern int comm_set_onesbcast(xsock_t sock, int enabled); /** * Set the sending buffer size of a socket. * * @param sock the socket whose sending buffer size to set. * @param desired_bufsize the preferred buffer size. * @param min_bufsize the smallest acceptable buffer size. * @return the successfully set buffer size on success, * otherwise XORP_ERROR. */ extern int comm_sock_set_sndbuf(xsock_t sock, int desired_bufsize, int min_bufsize); /** * Set the receiving buffer size of a socket. * * @param sock the socket whose receiving buffer size to set. * @param desired_bufsize the preferred buffer size. * @param min_bufsize the smallest acceptable buffer size. * @return the successfully set buffer size on success, * otherwise XORP_ERROR. */ extern int comm_sock_set_rcvbuf(xsock_t sock, int desired_bufsize, int min_bufsize); /** * Get the address family of a socket. * * Note: Idea taken from W. Stevens' UNPv1, 2e (pp 109). * * @param sock the socket whose address family we need to get. * @return the address family on success, otherwise XORP_ERROR. */ extern int comm_sock_get_family(xsock_t sock); /** * Get the type of a socket. * * Examples of socket type are SOCK_STREAM for TCP, SOCK_DGRAM for UDP, * and SOCK_RAW for raw protocol sockets. * * @param sock the socket whose type we need to get. * @return the socket type on success, otherwise XORP_ERROR. */ extern int comm_sock_get_type(xsock_t sock); /** * Set the blocking or non-blocking mode of an existing socket. * * @param sock the socket whose blocking mode is to be set. * @param is_blocking if non-zero, then set socket to blocking mode. * @return XORP_OK on success, otherwise XORP_ERROR. */ extern int comm_sock_set_blocking(xsock_t sock, int is_blocking); /** * Determine if an existing socket is in the connected state. * * @param sock the socket whose connected state is to be queried. * @param is_connected if the socket is in the connected state, then the * referenced value is set to 1, otherwise it is set to 0. * @return XORP_OK on success, otherwise XORP_ERROR. */ extern int comm_sock_is_connected(xsock_t sock, int *is_connected); __END_DECLS #endif /* __LIBCOMM_COMM_API_H__ */ xorp/libcomm/tests/0000775000076400007640000000000011540225526014446 5ustar greearbgreearbxorp/libcomm/tests/SConscript0000664000076400007640000000217211421137511016454 0ustar greearbgreearb# Copyright (c) 2009 XORP, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # $XORP$ import os Import("env") env = env.Clone() env.PrependUnique(CPPPATH = [ '#', '$BUILDDIR', '$BUILDDIR/libcomm', ]) env.PrependUnique(LIBPATH = [ '$BUILDDIR/libxorp', '$BUILDDIR/libcomm', ]) env.PrependUnique(LIBS = [ 'xorp_core', 'xorp_comm' ]) # XXX Not for cross-compilation. test_comm = env.AutoTest(target = 'test_comm', source = 'test_comm.c') xorp/libcomm/tests/test_connect.cc0000664000076400007640000001247511540224225017451 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "comm_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/xorpfd.hh" #include "libxorp/eventloop.hh" #include "libxorp/xlog.h" #include "libxorp/ipv4.hh" #ifdef HAVE_NETDB_H #include #endif #ifdef HAVE_GETOPT_H #include #endif #include "libcomm/comm_api.h" static bool fd_connect_callback_called = false; static bool fd_success_connection = false; static IPv4 dst_addr; static uint16_t dst_port = 0; #define LIMIT_SECS 6 class ConnectCB { public: void connect_callback(XorpFd fd, IoEventType type); }; void ConnectCB::connect_callback(XorpFd fd, IoEventType type) { /* XXX: May trigger multiple times as data may be readable, * when using Selector as underlying dispatcher */ //fputc('.',stderr); if (fd_connect_callback_called == false) { int is_conn = 0; printf("connect callback fired for first time\n"); fd_connect_callback_called = true; comm_sock_is_connected(fd, &is_conn); fd_success_connection = true; printf("comm_sock_is_connected() is %d\n", is_conn); } UNUSED(fd); UNUSED(type); } static void run_test() { EventLoop e; ConnectCB cb; XorpFd s; int in_progress; int err; bool times_up = false; XorpTimer after; // XXX: Need to open socket, add to event loop, initiate connect s = comm_sock_open(AF_INET, SOCK_STREAM, IPPROTO_TCP, COMM_SOCK_NONBLOCKING); if (!s.is_valid()) { puts(comm_get_last_error_str()); exit(-1); } e.add_ioevent_cb(s, IOT_CONNECT, callback(&cb, &ConnectCB::connect_callback)); after = e.set_flag_after_ms(LIMIT_SECS * 1000, ×_up); struct in_addr dst_in_addr; dst_addr.copy_out(dst_in_addr); err = comm_sock_connect4(s, &dst_in_addr, htons(dst_port), COMM_SOCK_NONBLOCKING, &in_progress); if (err == XORP_ERROR) { if (in_progress != 1) { printf("Connection failed immediately"); exit(1); } } printf("connection in progress\n"); int is_conn = 0; comm_sock_is_connected(s, &is_conn); printf("comm_sock_is_connected() is %d before eventloop\n", is_conn); while (!times_up || fd_connect_callback_called || fd_success_connection) e.run(); if (fd_success_connection == true) { printf("successful completion\n"); } else if (times_up) { printf("%d seconds elapsed without a connect() completion\n", LIMIT_SECS); } else { printf("connection failed for some other reason.\n"); } e.remove_ioevent_cb(s, IOT_CONNECT); comm_sock_close(s); } /* ------------------------------------------------------------------------- */ /* Utility resolver function */ IPv4 lookup4(const char* addr) { const struct hostent* he = gethostbyname(addr); if (he == 0 || he->h_length < 4) { fprintf(stderr, "gethostbyname failed: %s %d\n", #ifdef HAVE_HSTRERROR hstrerror(h_errno), #else "", #endif #ifdef HOST_OS_WINDOWS WSAGetLastError() #else h_errno #endif ); exit(EXIT_FAILURE); } struct in_addr ia; memcpy(&ia, he->h_addr_list[0], sizeof(ia)); return IPv4(ia.s_addr); } static void usage() { fprintf(stderr, "usage: test_connect [-v] dst port\n"); fprintf(stderr, "tests non-blocking connect to dst and port.\n"); exit(1); } int main(int argc, char *argv[]) { comm_init(); // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); int ch; while ((ch = getopt(argc, argv, "hv")) != -1) { switch (ch) { case 'v': // doesn't do anything, it's a paste-o! break; default: usage(); break; } } argc -= optind; argv += optind; if (argc != 2) { usage(); } dst_addr = lookup4(argv[0]); dst_port = atoi(argv[1]); printf("Running \"connect\" to %s:%d\n", dst_addr.str().c_str(), dst_port); // Some of test generates warnings - under normal circumstances the // end user wants to know, but here not. xlog_disable(XLOG_LEVEL_WARNING); run_test(); // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); comm_exit(); return (0); } xorp/libcomm/tests/test_comm.c0000664000076400007640000001073011540225526016605 0ustar greearbgreearb/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- */ /* vim:set sts=4 ts=8: */ /* * Copyright (c) 2001-2011 XORP, Inc and Others * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License, Version * 2.1, June 1999 as published by the Free Software Foundation. * Redistribution and/or modification of this program under the terms of * any other version of the GNU Lesser General Public License is not * permitted. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, * see the GNU Lesser General Public License, Version 2.1, a copy of * which can be found in the XORP LICENSE.lgpl file. * * XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; * http://xorp.net */ /* * COMM socket library test program. */ #include "comm_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_SYS_SOCKET_H #include #endif #ifdef HAVE_NETINET_IN_H #include #endif #ifdef HAVE_ARPA_INET_H #include #endif #include "libcomm/comm_api.h" /* * Exported variables */ /* * Local constants definitions */ /* * Local structures, typedefs and macros */ /* * Local variables */ /* * Local functions prototypes */ int main(int argc, char *argv[]) { xsock_t sock; unsigned short port = htons(12340); /* XXX: the port to bind to */ struct in_addr mcast_addr; /* * Initialize and start xlog */ xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); /* * Initialize the multicast address */ mcast_addr.s_addr = inet_addr("224.0.1.20"); /* * Init the `comm' library */ comm_init(); /* * Test `open TCP socket' */ sock = comm_open_tcp(AF_INET, COMM_SOCK_BLOCKING); if (sock == XORP_BAD_SOCKET) { printf("ERROR: cannot open TCP socket\n"); } else { printf("OK: open TCP socket\n"); comm_close(sock); } /* * Test `open UDP socket' */ sock = comm_open_udp(AF_INET, COMM_SOCK_BLOCKING); if (sock == XORP_BAD_SOCKET) { printf("ERROR: cannot open UDP socket\n"); } else { printf("OK: open UDP socket\n"); comm_close(sock); } /* * Test `bind TCP socket' */ sock = comm_bind_tcp4(NULL, port, COMM_SOCK_BLOCKING); if (sock == XORP_BAD_SOCKET) { printf("ERROR: cannot open and bind TCP socket to port %d\n", ntohs(port)); } else { printf("OK: open and bind TCP socket to port %d\n", ntohs(port)); comm_close(sock); } /* * Test `bind UDP socket' */ sock = comm_bind_udp4(NULL, port, COMM_SOCK_BLOCKING, false); if (sock == XORP_BAD_SOCKET) { printf("ERROR: cannot open and bind UDP socket to port %d\n", ntohs(port)); } else { printf("OK: open and bind UDP socket to port %d\n", ntohs(port)); comm_close(sock); } /* * Test 'bind and join a multicast group' */ sock = comm_bind_join_udp4(&mcast_addr, NULL, port, COMM_SOCK_ADDR_PORT_REUSE, COMM_SOCK_BLOCKING); if (sock == XORP_BAD_SOCKET) { printf("ERROR: cannot open, bind and join UDP socket to group %s and port %d\n", inet_ntoa(mcast_addr), ntohs(port)); comm_close(sock); } else { printf("OK: open, bind and join UDP socket to group %s and port %d\n", inet_ntoa(mcast_addr), ntohs(port)); comm_close(sock); } /* * Test 'listen on socket' */ sock = comm_bind_tcp4(NULL, port, COMM_SOCK_BLOCKING); if (sock == XORP_BAD_SOCKET) { printf("ERROR: cannot open and bind TCP socket to port %d," " for listening\n", ntohs(port)); } else if (comm_listen(sock, COMM_LISTEN_DEFAULT_BACKLOG) != XORP_OK) { printf("ERROR: listening TCP socket on port %d\n", ntohs(port)); comm_close(sock); } else { printf("OK: open, bind and listen TCP socket on port %d\n", ntohs(port)); comm_close(sock); } /* * Cleanup libcomm */ comm_exit(); /* * Gracefully stop and exit xlog */ xlog_stop(); xlog_exit(); exit(0); UNUSED(argc); } xorp/libcomm/comm_private.h0000664000076400007640000000577211421137511016147 0ustar greearbgreearb/* -*- Mode:C; c-basic-offset:4; tab-width:8; indent-tabs-mode:t -*- */ /* vim:set sts=4 ts=8: */ /* * Copyright (c) 2001 * YOID Project. * University of Southern California/Information Sciences Institute. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * $XORP: xorp/libcomm/comm_private.h,v 1.5 2006/10/12 01:24:45 pavlin Exp $ */ #ifndef __LIBCOMM_COMM_PRIVATE_H__ #define __LIBCOMM_COMM_PRIVATE_H__ /* * COMM socket library private include header. */ /* * Constants definitions */ /* * Structures, typedefs and macros */ /* * Global variables */ extern int _comm_serrno; /* * Global functions prototypes */ __BEGIN_DECLS /** * Log an error if an IPv6 specific method is called when IPv6 is not * present. * * An error message is output via XLOG_ERROR(). * Set an appropriate error code relevant to the underlying system. * Note: This is currently done with knowledge of how the error code is * stored internally, which is a design bug (we're not thread friendly). * This function is variadic so it can be used to remove unused variable * warnings in non-IPv6 code as well as log the error. * * @param method C-style string denoting the ipv6 function called. */ void comm_sock_no_ipv6(const char* method, ...); /** * Fetch and record the last socket layer error code from the underlying * system. * * Note: Currently not thread-safe. Internal use only. * This is done using a function to facilitate using explicit * Thread Local Storage (TLS) at a later time. */ void _comm_set_serrno(void); __END_DECLS #endif /* __LIBCOMM_COMM_PRIVATE_H__ */ xorp/mrt/0000775000076400007640000000000011635757530012476 5ustar greearbgreearbxorp/mrt/mrt_module.h0000664000076400007640000000233211421137511014776 0ustar greearbgreearb/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- */ /* vim:set sts=4 ts=8: */ /* * Copyright (c) 2001-2009 XORP, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 2, June * 1991 as published by the Free Software Foundation. Redistribution * and/or modification of this program under the terms of any other * version of the GNU General Public License is not permitted. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, * see the GNU General Public License, Version 2, a copy of which can be * found in the XORP LICENSE.gpl file. * * XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; * http://xorp.net */ /* * $XORP: xorp/mrt/mrt_module.h,v 1.10 2008/10/02 21:57:45 bms Exp $ */ /* * Module definitions. */ #ifndef __MRT_MRT_MODULE_H__ #define __MRT_MRT_MODULE_H__ #ifndef XORP_MODULE_NAME #define XORP_MODULE_NAME "MRT" #endif #ifndef XORP_MODULE_VERSION #define XORP_MODULE_VERSION "0.1" #endif #endif /* __MRT_MRT_MODULE_H__ */ xorp/mrt/TODO0000664000076400007640000000064011421137511013146 0ustar greearbgreearb# # $XORP: xorp/mrt/TODO,v 1.3 2003/11/06 22:19:44 pavlin Exp $ # * Clean-up the test_mrib program according to the new style. * Add more tests to test_mrt to cover corner cases (e.g, listing all source or group entries by prefix 255.0.0.0/8, etc). * Should remove() of Mrib entries remove them from the table, but should not delete the entries themselves? * Do we want Mrt::start() and Mrt::stop() methods? xorp/mrt/mrt.hh0000664000076400007640000005544111635757530013632 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __MRT_MRT_HH__ #define __MRT_MRT_HH__ // // Multicast Routing Table implementation. // #include "libxorp/xorp.h" #include "libxorp/ipvx.hh" #include "libxorp/ipvxnet.hh" /** * @short Class to store (S,G) (Source, Group) pair of addresses. */ class SourceGroup { public: /** * Constructor for a source and a group address. * * @param source_addr the source address. * @param group_addr the group address. */ SourceGroup(const IPvX& source_addr, const IPvX& group_addr) : _source_addr(source_addr), _group_addr(group_addr) {} /** * Get the source address. * * @return the source address. */ const IPvX& source_addr() const { return (_source_addr); } /** * Get the group address. * * @return the group address. */ const IPvX& group_addr() const { return (_group_addr); } private: IPvX _source_addr; // The source address IPvX _group_addr; // The group address }; /** * @short Class for (S,G) lookup key (Source-first, Group-second priority). */ class MreSgKey { public: /** * Constructor for a given @ref SourceGroup entry. * * @param source_group a reference to the corresponding (S,G) entry. */ MreSgKey(const SourceGroup& source_group) : _source_group(&source_group) {} /** * Get the corresponding @ref SourceGroup entry. * * @return a reference to the corresponding (S,G) entry. */ const SourceGroup& source_group() const { return (*_source_group); } /** * Less-Than Operator * * @param other the right-hand operand to compare against. * * @return true if the left-hand operand is numerically smaller than the * right-hand operand. */ bool operator<(const MreSgKey& other) const { if (_source_group && other._source_group) { if (_source_group->source_addr() == other.source_group().source_addr()) return (_source_group->group_addr() < other.source_group().group_addr()); return (_source_group->source_addr() < other.source_group().source_addr()); } else { if (_source_group) return false; return true; } } /** * Equality Operator * * @param other the right-hand operand to compare against. * @return true if the left-hand operand is numerically same as the * right-hand operand. */ bool operator==(const MreSgKey& other) const { if (_source_group == other._source_group) return true; if (!(_source_group && other._source_group)) return false; return ((_source_group->source_addr() == other.source_group().source_addr()) && (_source_group->group_addr() == other.source_group().group_addr())); } MreSgKey() { _source_group = NULL; } MreSgKey& operator=(const MreSgKey& rhs) { if (this != &rhs) { _source_group = rhs._source_group; } return *this; } private: // A reference to the corresponding // (S,G) used for comparison. mutable const SourceGroup* _source_group; }; /** * @short Class for (S,G) lookup key (Group-first, Source-second priority). */ class MreGsKey { public: /** * Constructor for a given @ref SourceGroup entry. * * @param source_group a reference to the corresponding (S,G) entry. */ MreGsKey(const SourceGroup& source_group) : _source_group(&source_group) {} /** * Get the corresponding @ref SourceGroup entry. * * @return a reference to the corresponding (S,G) entry. */ const SourceGroup& source_group() const { return (*_source_group); } /** * Less-Than Operator * * @param other the right-hand operand to compare against. * * @return true if the left-hand operand is numerically smaller than the * right-hand operand. */ bool operator<(const MreGsKey& other) const { if (_source_group && other._source_group) { if (_source_group->group_addr() == other.source_group().group_addr()) return (_source_group->source_addr() < other.source_group().source_addr()); return (_source_group->group_addr() < other.source_group().group_addr()); } else { if (_source_group) return false; return true; } } /** * Equality Operator * * @param other the right-hand operand to compare against. * @return true if the left-hand operand is numerically same as the * right-hand operand. */ bool operator==(const MreGsKey& other) const { if (_source_group == other._source_group) return true; if (!(_source_group && other._source_group)) return false; return ((_source_group->source_addr() == other.source_group().source_addr()) && (_source_group->group_addr() == other.source_group().group_addr()) ); } MreGsKey() { _source_group = NULL; } MreGsKey& operator=(const MreGsKey& rhs) { if (this != &rhs) { _source_group = rhs._source_group; } return *this; } private: // A reference to the corresponding // (S,G) used for comparison. mutable const SourceGroup* _source_group; }; /** * @short Template class for Multicast Routing Table. */ template class Mrt { public: /** * Default constructor */ Mrt() {} /** * Destructor */ virtual ~Mrt() { clear(); } // // Typedefs for lookup maps and iterators. // typedef map SgMap; typedef map GsMap; typedef typename SgMap::iterator sg_iterator; typedef typename GsMap::iterator gs_iterator; typedef typename SgMap::const_iterator const_sg_iterator; typedef typename GsMap::const_iterator const_gs_iterator; /** * Remove all multicast routing entries from the table. * * Note that the entries themselves are also deleted. */ void clear() { // Remove all multicast routing entries. for (sg_iterator iter = _sg_table.begin(); iter != _sg_table.end(); ) { E *mre = iter->second; ++iter; delete mre; } // Clear the (S,G) and (G,S) lookup tables _sg_table.clear(); _gs_table.clear(); } /** * Insert a multicast routing entry that was already created. * * Note that we insert a pointer to the @ref mre, hence @ref mre * should not be deleted without removing it first from the table. * * @param mre the multicast routing entry to insert. * @return the multicast routing entry that was inserted on success, * otherwise NULL. */ E *insert(E *mre) { pair sg_pos = _sg_table.insert( pair(MreSgKey(mre->source_group()), mre)); if (! sg_pos.second) return (NULL); pair gs_pos = _gs_table.insert( pair(MreGsKey(mre->source_group()), mre)); if (! gs_pos.second) { _sg_table.erase(sg_pos.first); return (NULL); } mre->_sg_key = sg_pos.first; mre->_gs_key = gs_pos.first; return (mre); } /** * Remove a multicast routing entry from the table. * * Note that the entry itself is not deleted. * * @param mre the multicast routing entry to delete from the table. * @return XORP_OK if the entry was in the table and was successfully * removed, otherwise XORP_ERROR. */ int remove(E *mre) { int ret_value = XORP_ERROR; if (mre->_sg_key != _sg_table.end()) { _sg_table.erase(mre->_sg_key); mre->_sg_key = _sg_table.end(); ret_value = XORP_OK; } if (mre->_gs_key != _gs_table.end()) { _gs_table.erase(mre->_gs_key); mre->_gs_key = _gs_table.end(); ret_value = XORP_OK; } return (ret_value); } /** * Find a multicast routing entry from the table. * * @param source_addr the source address to search for. * @param group_addr the group address to search for. * @return the multicast routing entry for source @ref source_addr * and group @ref group_addr if found, otherwise NULL. */ E *find(const IPvX& source_addr, const IPvX& group_addr) const { const_sg_iterator pos = _sg_table.find( MreSgKey(SourceGroup(source_addr, group_addr))); if (pos != _sg_table.end()) return (pos->second); return (NULL); } /** * Find the first multicast routing entry for a source address. * * @param source_addr the source address to search for. * @return the first multicast routing entry for source @ref source_addr * if found, otherwise NULL. */ E *find_source(const IPvX& source_addr) const { const_sg_iterator pos = _sg_table.lower_bound( MreSgKey(SourceGroup(source_addr, IPvX::ZERO(source_addr.af()) ) ) ); if (pos != _sg_table.end()) { if (pos->second->source_addr() == source_addr) return (pos->second); } return (NULL); } /** * Find the first multicast routing entry for a group address. * * @param group_addr the group address to search for. * @return the first multicast routing entry for group @ref group_addr * if found, otherwise NULL. */ E *find_group(const IPvX& group_addr) const { const_gs_iterator pos = _gs_table.lower_bound( MreGsKey(SourceGroup(IPvX::ZERO(group_addr.af()), group_addr))); if (pos != _gs_table.end()) { if (pos->second->group_addr() == group_addr) return (pos->second); } return (NULL); } /** * Find the first multicast routing entry for a source address prefix. * * @param prefix_s the source address prefix to search for. * @return the first multicast routing entry for source address * prefix @ref prefix_s if found, otherwise NULL. */ E *find_source_by_prefix(const IPvXNet& prefix_s) const { const_sg_iterator pos = _sg_table.lower_bound( MreSgKey(SourceGroup(prefix_s.masked_addr(), IPvX::ZERO(prefix_s.af()) ) ) ); if (pos == _sg_table.end()) return (NULL); E *mre = pos->second; if (mre->is_same_prefix_s(prefix_s)) return (mre); return (NULL); } /** * Find the first multicast routing entry for a group address prefix. * * @param prefix_g the group address prefix to search for. * @return the first multicast routing entry for group address * prefix @ref prefix_g if found, otherwise NULL. */ E *find_group_by_prefix(const IPvXNet& prefix_g) const { const_gs_iterator pos = _gs_table.lower_bound( MreGsKey(SourceGroup(prefix_g.masked_addr(), IPvX::ZERO(prefix_g.af()) ) ) ); if (pos == _gs_table.end()) return (NULL); E *mre = pos->second; if (mre->is_same_prefix_g(prefix_g)) return (mre); return (NULL); } /** * Get the number of multicast routing entries in the table. * * @return the number of multicast routing entries. */ size_t size() const { return (_sg_table.size()); } /** * Get an iterator for the first element in the source-group table. * * @return the iterator for the first element in the source-group table. */ const_sg_iterator sg_begin() const { return (_sg_table.begin()); } /** * Get an iterator for the first element in the group-source table. * * @return the iterator for the first element in the group-source table. */ const_gs_iterator gs_begin() const { return (_gs_table.begin()); } /** * Get an iterator for the last element in the source-group table. * * @return the iterator for the last element in the source-group table. */ const_sg_iterator sg_end() const { return (_sg_table.end()); } /** * Get an iterator for the last element in the group-source table. * * @return the iterator for the last element in the group-source table. */ const_gs_iterator gs_end() const { return (_gs_table.end()); } /** * Find the source iterator for the first multicast routing entry * for a source address prefix. * * @param prefix_s the source address prefix to search for. * @return the iterator for first entry with source address that * matches @ref prefix_s. If no matching entry is found, the * return value is @ref source_by_prefix_end(). */ const_sg_iterator source_by_prefix_begin(const IPvXNet& prefix_s) const { const_sg_iterator pos = _sg_table.lower_bound( MreSgKey(SourceGroup(prefix_s.masked_addr(), IPvX::ZERO(prefix_s.af()) ) ) ); return (pos); } /** * Find the source iterator for the one-after-the-last multicast routing * entry for a source address prefix. * * @param prefix_s the source address prefix to search for. * @return the iterator for one-after-the-last entry with source address * that matches @ref prefix_s. If no such entry exists, the * return value is @ref sg_end(). */ const_sg_iterator source_by_prefix_end(const IPvXNet& prefix_s) const { if (prefix_s.prefix_len() == 0) return (_sg_table.end()); // XXX: special case IPvXNet next_prefix_s(prefix_s); ++next_prefix_s; if (next_prefix_s.masked_addr().is_zero()) return (_sg_table.end()); // XXX: special case const_sg_iterator pos = _sg_table.lower_bound( MreSgKey(SourceGroup(next_prefix_s.masked_addr(), IPvX::ZERO(next_prefix_s.af()) ) ) ); return (pos); } /** * Find the group iterator for the first multicast routing entry * for a group address prefix. * * @param prefix_g the group address prefix to search for. * @return the iterator for first entry with group address that * matches @ref prefix_g. If no matching entry is found, the * return value is @ref group_by_prefix_end(). */ const_gs_iterator group_by_prefix_begin(const IPvXNet& prefix_g) const { const_gs_iterator pos = _gs_table.lower_bound( MreGsKey(SourceGroup(IPvX::ZERO(prefix_g.af()), prefix_g.masked_addr() ) ) ); return (pos); } /** * Find the group iterator for the one-after-the-last multicast routing * entry for a group address prefix. * * @param prefix_g the group address prefix to search for. * @return the iterator for one-after-the-last entry with group address * that matches @ref prefix_g. If no such entry exists, the * return value is @ref gs_end(). */ const_gs_iterator group_by_prefix_end(const IPvXNet& prefix_g) const { if (prefix_g.prefix_len() == 0) return (_gs_table.end()); // XXX: special case IPvXNet next_prefix_g(prefix_g); ++next_prefix_g; if (next_prefix_g.masked_addr().is_zero()) return (_gs_table.end()); // XXX: special case const_gs_iterator pos = _gs_table.lower_bound( MreGsKey(SourceGroup(IPvX::ZERO(next_prefix_g.af()), next_prefix_g.masked_addr() ) ) ); return (pos); } /** * Find the source iterator for the first multicast routing entry * for a source address. * * @param source_addr the source address to search for. * @return the iterator for first entry with source address that * matches @ref source_addr. If no matching entry is found, the * return value is @ref source_by_addr_end(). */ const_sg_iterator source_by_addr_begin(const IPvX& source_addr) const { const_sg_iterator pos = _sg_table.lower_bound( MreSgKey(SourceGroup(source_addr, IPvX::ZERO(source_addr.af()) ) ) ); return (pos); } /** * Find the source iterator for the one-after-the-last multicast routing * entry for a source address. * * @param source_addr the source address to search for. * @return the iterator for one-after-the-last entry with source address * that matches @ref source_addr. If no such entry exists, the * return value is @ref sg_end(). */ const_sg_iterator source_by_addr_end(const IPvX& source_addr) const { if (source_addr == IPvX::ALL_ONES(source_addr.af())) return (_sg_table.end()); // XXX: special case IPvX next_source_addr(source_addr); ++next_source_addr; const_sg_iterator pos = _sg_table.lower_bound( MreSgKey(SourceGroup(next_source_addr, IPvX::ZERO(next_source_addr.af()) ) ) ); return (pos); } /** * Find the group iterator for the first multicast routing entry * for a group address. * * @param group_addr the group address to search for. * @return the iterator for first entry with group address that * matches @ref group_addr. If no matching entry is found, the * return value is @ref group_by_addr_end(). */ const_gs_iterator group_by_addr_begin(const IPvX& group_addr) const { const_gs_iterator pos = _gs_table.lower_bound( MreGsKey(SourceGroup(IPvX::ZERO(group_addr.af()), group_addr ) ) ); return (pos); } /** * Find the group iterator for the one-after-the-last multicast routing * entry for a group address. * * @param group_addr the group address prefix to search for. * @return the iterator for one-after-the-last entry with group address * that matches @ref group_addr. If no such entry exists, the * return value is @ref gs_end(). */ const_gs_iterator group_by_addr_end(const IPvX& group_addr) const { if (group_addr == IPvX::ALL_ONES(group_addr.af())) return (_gs_table.end()); // XXX: special case IPvX next_group_addr(group_addr); ++next_group_addr; const_gs_iterator pos = _gs_table.lower_bound( MreGsKey(SourceGroup(IPvX::ZERO(next_group_addr.af()), next_group_addr ) ) ); return (pos); } /** * Find the group iterator for the multicast routing entry * for a source and a group address. * * @param source_addr the source address to search for. * @param group_addr the group address to search for. * @return the iterator for entry with source and group address that * matches @ref source_addr and @ref group_addr. If no matching * entry is found, the return value is the next entry. */ const_gs_iterator group_source_by_addr_begin( const IPvX& source_addr, const IPvX& group_addr) const { const_gs_iterator pos = _gs_table.lower_bound( MreGsKey(SourceGroup(source_addr, group_addr ) ) ); return (pos); } /** * Find the source iterator for the multicast routing entry * for a source and a group address. * * @param source_addr the source address to search for. * @param group_addr the group address to search for. * @return the iterator for entry with source and group address that * matches @ref source_addr and @ref group_addr. If no matching * entry is found, the return value is the next entry. */ const_sg_iterator source_group_by_addr_begin( const IPvX& source_addr, const IPvX& group_addr) const { const_sg_iterator pos = _sg_table.lower_bound( MreSgKey(SourceGroup(source_addr, group_addr ) ) ); return (pos); } private: SgMap _sg_table; // The (S,G) source-first lookup table GsMap _gs_table; // The (G,S) group-first lookup table }; /** * @short Template class for the Multicast Routing Entry. */ template class Mre { public: /** * Constructor for a given source and group address. * * @param source_addr the source address of the entry. * @param group_addr the group address of the entry. */ Mre(const IPvX& source_addr, const IPvX& group_addr) : _source_group(source_addr, group_addr) { // // XXX: the iterators below should be set to // _sg_table.end() and _gs_table.end() in the Mrt, but here // we don't know those values. Sigh... // memset(&_sg_key, 0, sizeof(_sg_key)); memset(&_gs_key, 0, sizeof(_gs_key)); } /** * Destructor */ virtual ~Mre() {}; /** * Get the source-group entry. * * @return a reference to the @ref SourceGroup source-group entry */ const SourceGroup& source_group() const { return (_source_group); } /** * Get the source address. * * @return the source address of the entry. */ const IPvX& source_addr() const { return (_source_group.source_addr()); } /** * Get the group address. * * @return the group address of the entry. */ const IPvX& group_addr() const { return (_source_group.group_addr()); } /** * Test if this entry matches a source address prefix. * * @param prefix_s the source address prefix to match against. * @return true if the entry source address belongs to address * prefix @ref prefix_s, otherwise false. */ bool is_same_prefix_s(const IPvXNet& prefix_s) const { return (prefix_s.contains(source_addr())); } /** * Test if this entry matches a group address prefix. * * @param prefix_g the group address prefix to match against. * @return true if the entry group address belongs to address * prefix @ref prefix_g, otherwise false. */ bool is_same_prefix_g(const IPvXNet& prefix_g) const { return (prefix_g.contains(group_addr())); } /** * Get the source-group table iterator. * * @return the source-group table iterator for this entry. */ const typename Mrt::sg_iterator& sg_key() const { return (_sg_key); } /** * Get the group-source table iterator. * * @return the group-source table for this entry. */ const typename Mrt::gs_iterator& gs_key() const { return (_gs_key); } /** * Convert this entry from binary form to presentation format. * * @return C++ string with the human-readable ASCII representation * of the entry. */ string str() const { return (string("(") + source_addr().str() + string(", ") + group_addr().str() + string(")")); } protected: friend class Mrt; private: // Mre state const SourceGroup _source_group; // The source and group addresses typename Mrt::sg_iterator _sg_key; // The source-group table iterator typename Mrt::gs_iterator _gs_key; // The group-source table iterator }; // // Deferred definitions // // // Global variables // // // Global functions prototypes // #endif // __MRT_MRT_HH__ xorp/mrt/mifset.hh0000664000076400007640000000324411540224231014267 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/mrt/mifset.hh,v 1.11 2008/10/02 21:57:45 bms Exp $ #ifndef __MRT_MIFSET_HH__ #define __MRT_MIFSET_HH__ // // Multicast interface bitmap-based classes. // #include #include "libxorp/xorp.h" #include "max_vifs.h" #ifndef XORP_USE_USTL #include #endif // // Constants definitions // // // Structures/classes, typedefs and macros // // Interface array bitmask typedef bitset Mifset; // // Global variables // // // Global functions prototypes // void mifset_to_array(const Mifset& mifset, uint8_t *array); void array_to_mifset(const uint8_t *array, Mifset& mifset); void mifset_to_vector(const Mifset& mifset, vector& vector); void vector_to_mifset(const vector& vector, Mifset& mifset); #endif // __MRT_MIFSET_HH__ xorp/mrt/SConscript0000664000076400007640000000301611421137511014470 0ustar greearbgreearb# Copyright (c) 2009 XORP, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # $ID$ import os Import("env") subdirs = [ 'tests' ] SConscript(dirs = subdirs, exports='env') env = env.Clone() is_shared = env.has_key('SHAREDLIBS') env.AppendUnique(CPPPATH = [ '#' ]) sources = [ 'buffer.c', 'mifset.cc', 'mrib_table.cc' ] if is_shared: libxorp_mrt = env.SharedLibrary(target = 'libxorp_mrt', source = sources) if env['rtld_origin']: for obj in libxorp_mrt: env.AddPostAction(libxorp_mrt, env.Symlink(obj.abspath, os.path.join(env['xorp_alias_libdir'], str(obj)))) else: libxorp_mrt = env.StaticLibrary(target = 'libxorp_mrt', source = sources) if is_shared: env.Alias('install', env.InstallLibrary(env['xorp_libdir'], libxorp_mrt)) Default(libxorp_mrt) xorp/mrt/buffer.h0000664000076400007640000002611211540224231014100 0ustar greearbgreearb/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- */ /* vim:set sts=4 ts=8: */ /* * Copyright (c) 2001-2011 XORP, Inc and Others * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 2, June * 1991 as published by the Free Software Foundation. Redistribution * and/or modification of this program under the terms of any other * version of the GNU General Public License is not permitted. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, * see the GNU General Public License, Version 2, a copy of which can be * found in the XORP LICENSE.gpl file. * * XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; * http://xorp.net */ #ifndef __MRT_BUFFER_H__ #define __MRT_BUFFER_H__ /* * Buffer management header file. */ #include "netstream_access.h" /* * Constants definitions */ #ifndef BUF_SIZE_DEFAULT #define BUF_SIZE_DEFAULT (64*1024) #endif /* * Structures, typedefs and macros */ typedef struct buffer_ { uint8_t *_data; /* The real data stream */ uint8_t *_data_head; /* The head of the data on the stream */ uint8_t *_data_tail; /* The tail of the data on the stream * (actually, the first unused octet after the * real data on the stream). */ size_t _buffer_size; /* The allocated buffer size for the data */ } buffer_t; #define BUFFER_DATA_HEAD(buffer) ((buffer)->_data_head) #define BUFFER_DATA_TAIL(buffer) ((buffer)->_data_tail) #define BUFFER_DATA_SIZE(buffer) ((size_t)((buffer)->_data_tail \ - (buffer)->_data_head)) #define BUFFER_AVAIL_HEAD(buffer) ((size_t)((buffer)->_data_head \ - (buffer)->_data)) #define BUFFER_AVAIL_TAIL(buffer) ((size_t)((buffer)->_data \ + (buffer)->_buffer_size \ - (buffer)->_data_tail)) #define BUFFER_RESET_TAIL(buffer) \ do { \ (buffer)->_data_tail = (buffer)->_data_head; \ } while (0) #define BUFFER_COPYPUT_INET_CKSUM(cksum, buffer, offset) \ do { \ BUFFER_COPYPUT_DATA_OFFSET(&(cksum), (buffer), (offset), 2); \ } while (0) #define BUFFER_COPY(buffer_from, buffer_to) \ do { \ size_t buffer_data_size_; \ \ buffer_data_size_ = BUFFER_DATA_SIZE(buffer_from); \ BUFFER_RESET(buffer_to); \ BUFFER_PUT_DATA(BUFFER_DATA_HEAD(buffer_from), \ (buffer_to), \ buffer_data_size_); \ } while (0) #define BUFFER_COMPARE(buffer1, buffer2) \ ((BUFFER_DATA_SIZE(buffer1) == BUFFER_DATA_SIZE(buffer2)) ? \ memcmp(BUFFER_DATA_HEAD(buffer1), BUFFER_DATA_HEAD(buffer2), \ BUFFER_DATA_SIZE(buffer1)) \ : ((BUFFER_DATA_SIZE(buffer1) < BUFFER_DATA_SIZE(buffer2)) ? -1 : 1)) #define BUFFER_COPYGET_DATA(to, buffer, datalen) \ do { \ size_t rcvlen_; \ uint8_t *cp_; \ \ rcvlen_ = BUFFER_DATA_SIZE(buffer); \ cp_ = BUFFER_DATA_HEAD(buffer); \ GET_DATA((to), cp_, rcvlen_, (datalen)); \ } while (0) #define BUFFER_COPYPUT_DATA(from, buffer, datalen) \ do { \ size_t buflen_; \ uint8_t *cp_; \ \ buflen_ = BUFFER_AVAIL_TAIL(buffer); \ cp_ = BUFFER_DATA_TAIL(buffer); \ PUT_DATA((from), cp_, buflen_, (datalen)); \ } while (0) #define BUFFER_COPYGET_DATA_OFFSET(to, buffer, offset, datalen) \ do { \ size_t rcvlen_; \ uint8_t *cp_; \ \ if (BUFFER_DATA_SIZE(buffer) < (offset)) \ goto rcvlen_error; \ rcvlen_ = BUFFER_DATA_SIZE(buffer) - (offset); \ cp_ = BUFFER_DATA_HEAD(buffer) + (offset); \ GET_DATA((to), cp_, rcvlen_, (datalen)); \ } while (0) #define BUFFER_COPYPUT_DATA_OFFSET(from, buffer, offset, datalen) \ do { \ size_t buflen_; \ uint8_t *cp_; \ \ if (BUFFER_DATA_SIZE(buffer) + BUFFER_AVAIL_TAIL(buffer) < (offset)) \ goto buflen_error; \ buflen_ = BUFFER_DATA_SIZE(buffer) + BUFFER_AVAIL_TAIL(buffer) \ - (offset); \ cp_ = BUFFER_DATA_HEAD(buffer) + (offset); \ PUT_DATA((from), cp_, buflen_, (datalen)); \ } while (0) #define BUFFER_GET_DATA(to, buffer, datalen) \ do { \ size_t rcvlen_; \ \ rcvlen_ = BUFFER_DATA_SIZE(buffer); \ GET_DATA((to), BUFFER_DATA_HEAD(buffer), rcvlen_, (datalen)); \ } while (0) #define BUFFER_PUT_DATA(from, buffer, datalen) \ do { \ size_t buflen_; \ \ buflen_ = BUFFER_AVAIL_TAIL(buffer); \ PUT_DATA((from), BUFFER_DATA_TAIL(buffer), buflen_, (datalen)); \ } while (0) #define BUFFER_GET_SKIP(octets, buffer) \ do { \ size_t rcvlen_; \ \ rcvlen_ = BUFFER_DATA_SIZE(buffer); \ GET_SKIP((octets), BUFFER_DATA_HEAD(buffer), rcvlen_); \ } while (0) #define BUFFER_PUT_SKIP(octets, buffer) \ do { \ size_t buflen_; \ \ buflen_ = BUFFER_AVAIL_TAIL(buffer); \ PUT_SKIP((octets), BUFFER_DATA_TAIL(buffer), buflen_); \ } while (0) #define BUFFER_GET_SKIP_REVERSE(octets, buffer) \ do { \ size_t avail_head_; \ \ avail_head_ = BUFFER_AVAIL_HEAD(buffer); \ if (avail_head_ < (octets)) \ goto rcvlen_error; \ (buffer)->_data_head -= (octets); \ } while (0) #define BUFFER_PUT_SKIP_REVERSE(octets, buffer) \ do { \ size_t rcvlen_; \ \ rcvlen_ = BUFFER_DATA_SIZE(buffer); \ if (rcvlen_ < (octets)) \ goto buflen_error; \ (buffer)->_data_tail -= (octets); \ } while (0) #define BUFFER_GET_OCTET(val, buffer) \ do { \ size_t rcvlen_; \ \ rcvlen_ = BUFFER_DATA_SIZE(buffer); \ GET_OCTET((val), BUFFER_DATA_HEAD(buffer), rcvlen_); \ } while (0) #define BUFFER_PUT_OCTET(val, buffer) \ do { \ size_t buflen_; \ \ buflen_ = BUFFER_AVAIL_TAIL(buffer); \ PUT_OCTET((val), BUFFER_DATA_TAIL(buffer), buflen_); \ } while (0) #define BUFFER_GET_HOST_16(val, buffer) \ do { \ size_t rcvlen_; \ \ rcvlen_ = BUFFER_DATA_SIZE(buffer); \ GET_HOST_16((val), BUFFER_DATA_HEAD(buffer), rcvlen_); \ } while (0) #define BUFFER_PUT_HOST_16(val, buffer) \ do { \ size_t buflen_; \ \ buflen_ = BUFFER_AVAIL_TAIL(buffer); \ PUT_HOST_16((val), BUFFER_DATA_TAIL(buffer), buflen_); \ } while (0) #define BUFFER_GET_NET_16(val, buffer) \ do { \ size_t rcvlen_; \ \ rcvlen_ = BUFFER_DATA_SIZE(buffer); \ GET_NET_16((val), BUFFER_DATA_HEAD(buffer), rcvlen_); \ } while (0) #define BUFFER_PUT_NET_16(val, buffer) \ do { \ size_t buflen_; \ \ buflen_ = BUFFER_AVAIL_TAIL(buffer); \ PUT_NET_16((val), BUFFER_DATA_TAIL(buffer), buflen_); \ } while (0) #define BUFFER_GET_HOST_32(val, buffer) \ do { \ size_t rcvlen_; \ \ rcvlen_ = BUFFER_DATA_SIZE(buffer); \ GET_HOST_32((val), BUFFER_DATA_HEAD(buffer), rcvlen_); \ } while (0) #define BUFFER_PUT_HOST_32(val, buffer) \ do { \ size_t buflen_; \ \ buflen_ = BUFFER_AVAIL_TAIL(buffer); \ PUT_HOST_32((val), BUFFER_DATA_TAIL(buffer), buflen_); \ } while (0) #define BUFFER_GET_NET_32(val, buffer) \ do { \ size_t rcvlen_; \ \ rcvlen_ = BUFFER_DATA_SIZE(buffer); \ GET_NET_32((val), BUFFER_DATA_HEAD(buffer), rcvlen_); \ } while (0) #define BUFFER_PUT_NET_32(val, buffer) \ do { \ size_t buflen_; \ \ buflen_ = BUFFER_AVAIL_TAIL(buffer); \ PUT_NET_32((val), BUFFER_DATA_TAIL(buffer), buflen_); \ } while (0) #ifndef __cplusplus #define BUFFER_GET_IPADDR(family, ipaddr, buffer) \ _BUFFER_GET_IPADDR_C(family, ipaddr, buffer) #define BUFFER_PUT_IPADDR(ipaddr, buffer) \ _BUFFER_PUT_IPADDR_C(ipaddr, buffer) #else /* C++ */ #define BUFFER_GET_IPADDR(family, ipaddr, buffer) \ _BUFFER_GET_IPADDR_CPP(family, ipaddr, buffer) #define BUFFER_PUT_IPADDR(ipaddr, buffer) \ _BUFFER_PUT_IPADDR_CPP(ipaddr, buffer) #define BUFFER_GET_IPVX(family, ipaddr, buffer) \ BUFFER_GET_IPADDR(family, ipaddr, buffer) #define BUFFER_PUT_IPVX(ipaddr, buffer) \ BUFFER_PUT_IPADDR(ipaddr, buffer) #endif /* __cplusplus */ /* C version of IP addresses */ #define _BUFFER_GET_IPADDR_C(family, ipaddr, buffer) \ do { \ if (BUFFER_DATA_SIZE(buffer) < FAMILY2ADDRSIZE((family))) \ goto rcvlen_error; \ MEMORY2IPADDR((family), BUFFER_DATA_HEAD((buffer)), (ipaddr)); \ BUFFER_GET_SKIP(FAMILY2ADDRSIZE((family)), (buffer)); \ } while (0) #define _BUFFER_PUT_IPADDR_C(ipaddr, buffer) \ do { \ if (BUFFER_AVAIL_TAIL((buffer)) \ < FAMILY2ADDRSIZE(IPADDR2FAMILY((ipaddr)))) \ goto buflen_error; \ IPADDR2MEMORY((ipaddr), BUFFER_DATA_TAIL((buffer))); \ BUFFER_PUT_SKIP(FAMILY2ADDRSIZE(IPADDR2FAMILY((ipaddr))), (buffer)); \ } while (0) /* C++ version of IP addresses */ #define _BUFFER_GET_IPADDR_CPP(family, ipvx, buffer) \ do { \ if (BUFFER_DATA_SIZE(buffer) < family2addr_bytelen((family))) \ goto rcvlen_error; \ (ipvx).copy_in(family, BUFFER_DATA_HEAD((buffer))); \ BUFFER_GET_SKIP(family2addr_bytelen((family)), (buffer)); \ } while (0) #define _BUFFER_PUT_IPADDR_CPP(ipvx, buffer) \ do { \ if (BUFFER_AVAIL_TAIL((buffer)) < (ipvx).addr_bytelen()) \ goto buflen_error; \ (ipvx).copy_out(BUFFER_DATA_TAIL((buffer))); \ BUFFER_PUT_SKIP((ipvx).addr_bytelen(), (buffer)); \ } while (0) /* * Wrappers for the buffer functions. */ #define BUFFER_MALLOC(buffer_size) (buffer_malloc_utils_((buffer_size))) #define BUFFER_FREE(buffer) \ do { \ buffer_free_utils_((buffer)); \ (buffer) = NULL; \ } while (0) #define BUFFER_RESET(buffer) (buffer_reset_utils_((buffer))) /* * Global variables */ /* * Global functions prototypes */ __BEGIN_DECLS extern buffer_t * buffer_malloc_utils_(size_t buffer_size); extern void buffer_free_utils_(buffer_t *buffer); extern buffer_t * buffer_reset_utils_(buffer_t *buffer); __END_DECLS #endif /* __MRT_BUFFER_H__ */ xorp/mrt/mrib_table.cc0000664000076400007640000004013711421137511015072 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // Multicast Routing Information Base Table implementation. // #include "mrt_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/vif.hh" #include "libxorp/utils.hh" #include "mrib_table.hh" // // Exported variables // // // Local constants definitions // // // Local structures/classes, typedefs and macros // // // Local variables // // // Local functions prototypes // // // MribTable methods // /** * MribTable::MribTable: * @family: The address family (e.g., %AF_INET or %AF_INET6). * * MribTable constructor. **/ MribTable::MribTable(int family) : _family(family), _mrib_lookup_root(NULL), _mrib_lookup_size(0), _mrib_size(0), _is_preserving_removed_mrib_entries(false) { } /** * MribTable::~MribTable: * @: * * MribTable destructor. * Remove and delete all MRIB entries and cleanup. **/ MribTable::~MribTable() { clear(); } // // Clear all entries and pending transactions. // XXX: The entries themselves are deleted too. // void MribTable::clear() { remove_all_entries(); // // Delete all pending transactions // _mrib_pending_transactions.clear(); // // Delete the list with the removed Mrib entries // delete_pointers_list(_removed_mrib_entries); } // // Remove all MRIB entries from the table. // XXX: The MRIB entries themselves are either deleted or added to // the list with removed entries. // void MribTable::remove_all_entries() { // // Delete all MribLookup entries // remove_mrib_lookup(_mrib_lookup_root); _mrib_lookup_root = NULL; _mrib_lookup_size = 0; _mrib_size = 0; } // // Insert a copy of a MRIB routing entry that was already created. // XXX: if there is an existing MRIB entry for the same prefix, the old entry // is removed from the table and is either deleted or added to the list with // removed entries. // Mrib * MribTable::insert(const Mrib& mrib) { const IPvX lookup_addr = mrib.dest_prefix().masked_addr(); size_t prefix_len = mrib.dest_prefix().prefix_len(); uint32_t mem_lookup_addr[sizeof(IPvX)]; const size_t lookup_addr_size_words = lookup_addr.addr_bytelen()/sizeof(mem_lookup_addr[0]); // Copy the destination address prefix to memory lookup_addr.copy_out((uint8_t *)mem_lookup_addr); MribLookup *mrib_lookup = _mrib_lookup_root; if (mrib_lookup == NULL) { // The root/default entry mrib_lookup = new MribLookup(NULL); _mrib_lookup_size++; _mrib_lookup_root = mrib_lookup; } if (prefix_len == 0) { // The default routing entry if (mrib_lookup->mrib() != NULL) { // XXX: remove the old entry remove_mrib_entry(mrib_lookup->mrib()); _mrib_size--; } Mrib *mrib_copy = new Mrib(mrib); mrib_lookup->set_mrib(mrib_copy); _mrib_size++; return (mrib_lookup->mrib()); } // // The main lookup procedure // for (size_t i = 0; i < lookup_addr_size_words; i++) { uint32_t lookup_word = ntohl(mem_lookup_addr[i]); for (size_t j = 0; j < sizeof(lookup_word)*NBBY; j++) { MribLookup *parent_mrib_lookup = mrib_lookup; #define MRIB_LOOKUP_BITTEST ((uint32_t)(1 << (sizeof(lookup_word)*NBBY - 1))) if (lookup_word & MRIB_LOOKUP_BITTEST) mrib_lookup = mrib_lookup->right_child(); else mrib_lookup = mrib_lookup->left_child(); if (mrib_lookup == NULL) { // Create a new entry mrib_lookup = new MribLookup(parent_mrib_lookup); _mrib_lookup_size++; if (lookup_word & MRIB_LOOKUP_BITTEST) parent_mrib_lookup->set_right_child(mrib_lookup); else parent_mrib_lookup->set_left_child(mrib_lookup); } #undef MRIB_LOOKUP_BITTEST if (--prefix_len == 0) { // Found the place to install the entry if (mrib_lookup->mrib() != NULL) { // XXX: remove the old entry remove_mrib_entry(mrib_lookup->mrib()); _mrib_size--; } Mrib *mrib_copy = new Mrib(mrib); mrib_lookup->set_mrib(mrib_copy); _mrib_size++; return (mrib_lookup->mrib()); } lookup_word <<= 1; } } XLOG_FATAL("Unexpected internal error adding prefix %s to the " "Multicast Routing Information Base Table", mrib.str().c_str()); return (NULL); } // // Remove a MRIB entry from the table. // The information about the entry to remove is in @mrib. // void MribTable::remove(const Mrib& mrib) { remove(mrib.dest_prefix()); } // // Remove a MRIB entry from the table. // The destination prefix of the entry to remove is in @dest_prefix. // void MribTable::remove(const IPvXNet& dest_prefix) { MribLookup *mrib_lookup = find_prefix_mrib_lookup(dest_prefix); if (mrib_lookup == NULL) return; // TODO: should we return an error instead? if (mrib_lookup->mrib() != NULL) { remove_mrib_entry(mrib_lookup->mrib()); mrib_lookup->set_mrib(NULL); _mrib_size--; } // // Remove recursively all parent nodes that are not in use anymore // do { if ((mrib_lookup->left_child() != NULL) || (mrib_lookup->right_child() != NULL) || (mrib_lookup->mrib() != NULL)) break; // Node is still in use MribLookup *parent_mrib_lookup = mrib_lookup->parent(); if (parent_mrib_lookup != NULL) { if (parent_mrib_lookup->left_child() == mrib_lookup) parent_mrib_lookup->set_left_child(NULL); else parent_mrib_lookup->set_right_child(NULL); } delete mrib_lookup; _mrib_lookup_size--; mrib_lookup = parent_mrib_lookup; } while (mrib_lookup != NULL); if (_mrib_lookup_size == 0) _mrib_lookup_root = NULL; } // // Remove recursively all MribLookup entries below (and including) mrib_lookup // XXX: the Mrib entries are either deleted or added to the list with // removed entries. // void MribTable::remove_mrib_lookup(MribLookup *mrib_lookup) { // Sanity check if (mrib_lookup == NULL) return; // Remove the Mrib entry itself if (mrib_lookup->mrib() != NULL) { remove_mrib_entry(mrib_lookup->mrib()); _mrib_size--; mrib_lookup->set_mrib(NULL); } if (mrib_lookup->parent() != NULL) { // Chear the state in the parent if (mrib_lookup->parent()->left_child() == mrib_lookup) { mrib_lookup->parent()->set_left_child(NULL); } else { XLOG_ASSERT(mrib_lookup->parent()->right_child() == mrib_lookup); mrib_lookup->parent()->set_right_child(NULL); } } // Remove recursively the left subtree if (mrib_lookup->left_child() != NULL) { mrib_lookup->left_child()->set_parent(NULL); remove_mrib_lookup(mrib_lookup->left_child()); } // Remove recursively the right subtree if (mrib_lookup->right_child() != NULL) { mrib_lookup->right_child()->set_parent(NULL); remove_mrib_lookup(mrib_lookup->right_child()); } // Delete myself delete mrib_lookup; _mrib_lookup_size--; if (_mrib_lookup_size == 0) _mrib_lookup_root = NULL; // Done } Mrib * MribTable::find(const IPvX& lookup_addr) const { uint32_t mem_lookup_addr[sizeof(IPvX)]; const size_t lookup_addr_size_words = lookup_addr.addr_bytelen()/sizeof(mem_lookup_addr[0]); // Copy the destination address prefix to memory lookup_addr.copy_out((uint8_t *)mem_lookup_addr); MribLookup *mrib_lookup = _mrib_lookup_root; Mrib *longest_match_mrib = NULL; if (mrib_lookup == NULL) return (longest_match_mrib); // // The main lookup procedure // for (size_t i = 0; i < lookup_addr_size_words; i++) { uint32_t lookup_word = ntohl(mem_lookup_addr[i]); for (size_t j = 0; j < sizeof(lookup_word)*NBBY; j++) { MribLookup *parent_mrib_lookup = mrib_lookup; if (parent_mrib_lookup->mrib() != NULL) longest_match_mrib = parent_mrib_lookup->mrib(); #define MRIB_LOOKUP_BITTEST ((uint32_t)(1 << (sizeof(lookup_word)*NBBY - 1))) if (lookup_word & MRIB_LOOKUP_BITTEST) mrib_lookup = mrib_lookup->right_child(); else mrib_lookup = mrib_lookup->left_child(); #undef MRIB_LOOKUP_BITTEST if (mrib_lookup == NULL) { // Longest match return (longest_match_mrib); } lookup_word <<= 1; } } XLOG_ASSERT(mrib_lookup->mrib() != NULL); return (mrib_lookup->mrib()); } Mrib * MribTable::find_exact(const IPvXNet& dest_prefix) const { MribLookup *mrib_lookup = find_prefix_mrib_lookup(dest_prefix); if (mrib_lookup == NULL) return (NULL); return (mrib_lookup->mrib()); } /** * Remove a @ref Mrib entry from the table. * * The @ref Mrib entry itself is either deleted or added to the list * with removed entries. * * @param mrib the @ref Mrib entry to remove. */ void MribTable::remove_mrib_entry(Mrib *mrib) { if (is_preserving_removed_mrib_entries()) _removed_mrib_entries.push_back(mrib); else delete mrib; } // // Find an exact MribLookup match. // MribLookup * MribTable::find_prefix_mrib_lookup(const IPvXNet& addr_prefix) const { const IPvX lookup_addr = addr_prefix.masked_addr(); size_t prefix_len = addr_prefix.prefix_len(); uint32_t mem_lookup_addr[sizeof(IPvX)]; const size_t lookup_addr_size_words = lookup_addr.addr_bytelen()/sizeof(mem_lookup_addr[0]); // Copy the destination address prefix to memory lookup_addr.copy_out((uint8_t *)mem_lookup_addr); MribLookup *mrib_lookup = _mrib_lookup_root; if (mrib_lookup == NULL) return (mrib_lookup); if (prefix_len == 0) { // The default routing entry return (mrib_lookup); } // // The main lookup procedure // for (size_t i = 0; i < lookup_addr_size_words; i++) { uint32_t lookup_word = ntohl(mem_lookup_addr[i]); for (size_t j = 0; j < sizeof(lookup_word)*NBBY; j++) { #define MRIB_LOOKUP_BITTEST ((uint32_t)(1 << (sizeof(lookup_word)*NBBY - 1))) if (lookup_word & MRIB_LOOKUP_BITTEST) mrib_lookup = mrib_lookup->right_child(); else mrib_lookup = mrib_lookup->left_child(); if (mrib_lookup == NULL) { // Not found return (mrib_lookup); } #undef MRIB_LOOKUP_BITTEST if (--prefix_len == 0) { // Found the entry return (mrib_lookup); } lookup_word <<= 1; } } XLOG_FATAL("Unexpected internal error lookup prefix %s in the " "Multicast Routing Information Base Table", addr_prefix.str().c_str()); return (NULL); } /** * Update the vif index of a @ref Mrib entry. * * @param dest_prefix the destination prefix of the @ref Mrib entry * to update. * @param vif_index the new vif index of the @ref Mrib entry. */ void MribTable::update_entry_vif_index(const IPvXNet& dest_prefix, uint32_t vif_index) { Mrib* mrib; // // Update the entry already installed in the table // mrib = find_exact(dest_prefix); if (mrib != NULL) mrib->set_next_hop_vif_index(vif_index); // // Update all entries in pending transactions // list::iterator iter; for (iter = _mrib_pending_transactions.begin(); iter != _mrib_pending_transactions.end(); ++iter) { PendingTransaction& pending_transaction = *iter; if (pending_transaction.mrib().dest_prefix() == dest_prefix) { pending_transaction.update_entry_vif_index(vif_index); } } } void MribTable::add_pending_insert(uint32_t tid, const Mrib& mrib) { _mrib_pending_transactions.push_back(PendingTransaction(tid, mrib, true)); } void MribTable::add_pending_remove(uint32_t tid, const Mrib& mrib) { _mrib_pending_transactions.push_back(PendingTransaction(tid, mrib, false)); } void MribTable::add_pending_remove_all_entries(uint32_t tid) { _mrib_pending_transactions.push_back(PendingTransaction(*this, tid)); } void MribTable::commit_pending_transactions(uint32_t tid) { // // Process the pending Mrib transactions for the given transaction ID // // TODO: probably should allow commits in time slices of up to X ms? list::iterator iter, old_iter; for (iter = _mrib_pending_transactions.begin(); iter != _mrib_pending_transactions.end(); ) { PendingTransaction& pending_transaction = *iter; old_iter = iter; ++iter; if (pending_transaction.tid() != tid) continue; if (pending_transaction.is_remove_all()) { remove_all_entries(); } else { if (pending_transaction.is_insert()) insert(pending_transaction.mrib()); else remove(pending_transaction.mrib()); } _mrib_pending_transactions.erase(old_iter); } } void MribTable::abort_pending_transactions(uint32_t tid) { // // Abort pending Mrib transactions for the given transaction ID // list::iterator iter, old_iter; for (iter = _mrib_pending_transactions.begin(); iter != _mrib_pending_transactions.end(); ) { PendingTransaction& pending_transaction = *iter; old_iter = iter; ++iter; if (pending_transaction.tid() != tid) continue; _mrib_pending_transactions.erase(old_iter); } } MribTableIterator& MribTableIterator::operator++() { _mrib_lookup = _mrib_lookup->get_next(); return (*this); } MribTableIterator MribTableIterator::operator++(int) { MribTableIterator old_value = *this; _mrib_lookup = _mrib_lookup->get_next(); return (old_value); } Mrib * MribTableIterator::operator*() const { return (_mrib_lookup->mrib()); } MribLookup * MribLookup::get_next() const { if (_left_child != NULL) return (_left_child); if (_right_child != NULL) return (_right_child); // Go UP recursively and find the next branch to go DOWN const MribLookup *mrib_lookup = this; MribLookup *parent_mrib_lookup = mrib_lookup->_parent; while (parent_mrib_lookup != NULL) { if (parent_mrib_lookup->_right_child == mrib_lookup) { mrib_lookup = parent_mrib_lookup; parent_mrib_lookup = mrib_lookup->_parent; continue; } XLOG_ASSERT(parent_mrib_lookup->_left_child == mrib_lookup); if (parent_mrib_lookup->_right_child != NULL) return (parent_mrib_lookup->_right_child); // The right child // Go UP mrib_lookup = parent_mrib_lookup; parent_mrib_lookup = mrib_lookup->_parent; continue; } return (NULL); } // // Mrib methods // Mrib::Mrib(int family) : _dest_prefix(family), _next_hop_router_addr(family), _next_hop_vif_index(Vif::VIF_INDEX_INVALID), _metric_preference(~0U), _metric(~0U) { } Mrib::Mrib(const IPvXNet& dest_prefix) : _dest_prefix(dest_prefix), _next_hop_router_addr(dest_prefix.af()), _next_hop_vif_index(Vif::VIF_INDEX_INVALID), _metric_preference(~0U), _metric(~0U) { } Mrib::Mrib(const Mrib& mrib) : _dest_prefix(mrib.dest_prefix()), _next_hop_router_addr(mrib.next_hop_router_addr()), _next_hop_vif_index(mrib.next_hop_vif_index()), _metric_preference(mrib.metric_preference()), _metric(mrib.metric()) { } bool Mrib::operator==(const Mrib& other) const { return ((_dest_prefix == other.dest_prefix()) && (_next_hop_router_addr == other.next_hop_router_addr()) && (_next_hop_vif_index == other.next_hop_vif_index()) && (_metric_preference == other.metric_preference()) && (_metric == other.metric())); } string Mrib::str() const { string s = ""; s += "dest_prefix: " + _dest_prefix.str(); s += " next_hop_router: " + _next_hop_router_addr.str(); s += " next_hop_vif_index: " + c_format("%u", XORP_UINT_CAST(_next_hop_vif_index)); s += " metric_preference: " + c_format("%u", XORP_UINT_CAST(_metric_preference)); s += " metric: " + c_format("%u", XORP_UINT_CAST(_metric)); return s; } xorp/mrt/mifset.cc0000664000076400007640000000524611421137511014263 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // Multicast interface bitmap-based classes implementation. // #include "mrt_module.h" #include "libxorp/xorp.h" #include "mifset.hh" // // Exported variables // // // Local constants definitions // // // Local structures/classes, typedefs and macros // // // Local variables // // // Local functions prototypes // void mifset_to_array(const Mifset& mifset, uint8_t *array) { size_t i; // Reset the array for (i = 0; i < mifset.size() / sizeof(array[0]); i++) array[i] = 0; if (mifset.size() % sizeof(array[0])) array[i] = 0; // XXX: the extra, semi-filled byte // Set the bits for (size_t i = 0; i < mifset.size(); i++) { size_t byte = i / sizeof(array[0]); size_t bit = i % sizeof(array[0]); if (mifset.test(i)) array[byte] |= (1 << bit); } } void array_to_mifset(const uint8_t *array, Mifset& mifset) { // Reset the mifset mifset.reset(); // Set the bits for (size_t i = 0; i < mifset.size(); i++) { size_t byte = i / sizeof(array[0]); size_t bit = i % sizeof(array[0]); if (array[byte] & (1 << bit)) mifset.set(i); } } void mifset_to_vector(const Mifset& mifset, vector& vector) { size_t i; // Reset the vector for (i = 0; i < vector.size(); i++) vector[i] = 0; // Set the bits for (size_t i = 0; i < mifset.size(); i++) { size_t byte = i / sizeof(vector[0]); size_t bit = i % sizeof(vector[0]); if (mifset.test(i)) vector[byte] |= (1 << bit); } } void vector_to_mifset(const vector& vector, Mifset& mifset) { // Reset the mifset mifset.reset(); // Set the bits for (size_t i = 0; i < mifset.size(); i++) { size_t byte = i / sizeof(vector[0]); size_t bit = i % sizeof(vector[0]); if (vector[byte] & (1 << bit)) mifset.set(i); } } xorp/mrt/tests/0000775000076400007640000000000011540224231013616 5ustar greearbgreearbxorp/mrt/tests/test_mrt.cc0000664000076400007640000003732411540224231015777 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // Multicast Routing Table test program. // #include "mrt_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/exceptions.hh" #ifdef HAVE_GETOPT_H #include #endif #include "mrt.hh" // // XXX: MODIFY FOR YOUR TEST PROGRAM // static const char *program_name = "test_mrt"; static const char *program_description = "Test Multicast Routing Table"; static const char *program_version_id = "0.1"; static const char *program_date = "February 25, 2004"; static const char *program_copyright = "See file LICENSE"; static const char *program_return_value = "0 on success, 1 if test error, 2 if internal error"; static bool s_verbose = false; bool verbose() { return s_verbose; } void set_verbose(bool v) { s_verbose = v; } static int s_failures = 0; bool failures() { return s_failures; } void incr_failures() { s_failures++; } // // printf(3)-like facility to conditionally print a message if verbosity // is enabled. // #define verbose_log(x...) _verbose_log(__FILE__,__LINE__, x) #define _verbose_log(file, line, x...) \ do { \ if (verbose()) { \ printf("From %s:%d: ", file, line); \ printf(x); \ } \ } while(0) // // Test and print a message whether two strings are lexicographically same. // The strings can be either C or C++ style. // #define verbose_match(s1, s2) \ _verbose_match(__FILE__, __LINE__, s1, s2) bool _verbose_match(const char* file, int line, const string& s1, const string& s2) { bool match = s1 == s2; _verbose_log(file, line, "Comparing %s == %s : %s\n", s1.c_str(), s2.c_str(), match ? "OK" : "FAIL"); if (match == false) incr_failures(); return match; } // // Test and print a message whether a condition is true. // // The first argument is the condition to test. // The second argument is a string with a brief description of the tested // condition. // #define verbose_assert(cond, desc) \ _verbose_assert(__FILE__, __LINE__, cond, desc) bool _verbose_assert(const char* file, int line, bool cond, const string& desc) { _verbose_log(file, line, "Testing %s : %s\n", desc.c_str(), cond ? "OK" : "FAIL"); if (cond == false) incr_failures(); return cond; } /** * Print program info to output stream. * * @param stream the output stream the print the program info to. */ static void print_program_info(FILE *stream) { fprintf(stream, "Name: %s\n", program_name); fprintf(stream, "Description: %s\n", program_description); fprintf(stream, "Version: %s\n", program_version_id); fprintf(stream, "Date: %s\n", program_date); fprintf(stream, "Copyright: %s\n", program_copyright); fprintf(stream, "Return: %s\n", program_return_value); } /** * Print program usage information to the stderr. * * @param progname the name of the program. */ static void usage(const char* progname) { print_program_info(stderr); fprintf(stderr, "usage: %s [-v] [-h]\n", progname); fprintf(stderr, " -h : usage (this message)\n"); fprintf(stderr, " -v : verbose output\n"); fprintf(stderr, "Return 0 on success, 1 if test error, 2 if internal error.\n"); } /** * Multicast Routing Entry test class. */ class MyMre : public Mre { public: MyMre(const IPvX& source, const IPvX& group) : Mre(source, group) {} }; string mre_list_str(const list& mre_list) { list::const_iterator iter; string res; for (iter = mre_list.begin(); iter != mre_list.end(); ++iter) { const MyMre *t = *iter; res += cstring(*t); } return res; } void test_mrt() { Mrt mrt_4; Mrt mrt_6; Mrt::const_sg_iterator sg_iter, sg_iter_begin, sg_iter_end; Mrt::const_gs_iterator gs_iter, gs_iter_begin, gs_iter_end; // IPv4 values IPvX s1_4(IPv4("123.45.0.1")), g1_4(IPv4("224.1.0.2")); IPvX s2_4(IPv4("123.45.0.2")), g2_4(IPv4("224.1.0.1")); IPvX s3_4(IPv4("123.45.0.255")), g3_4(IPv4("224.1.0.255")); IPvX s4_4(IPv4("123.46.0.1")), g4_4(IPv4("224.2.0.1")); IPvX s5_4(IPv4("123.46.0.1")), g5_4(IPv4("224.2.0.2")); MyMre *mre1_4 = new MyMre(s1_4, g1_4); MyMre *mre11_4 = new MyMre(s1_4, g1_4); MyMre *mre2_4 = new MyMre(s2_4, g2_4); MyMre *mre4_4 = new MyMre(s4_4, g4_4); MyMre *mre5_4 = new MyMre(s5_4, g5_4); // IPv6 values IPvX s1_6(IPv6("2001::1")), g1_6(IPv6("ff01::2")); IPvX s2_6(IPv6("2001::2")), g2_6(IPv6("ff01::1")); IPvX s3_6(IPv6("2001::ff")), g3_6(IPv6("ff01::ff")); IPvX s4_6(IPv6("2002::1")), g4_6(IPv6("ff02::1")); IPvX s5_6(IPv6("2002::1")), g5_6(IPv6("ff02::2")); MyMre *mre1_6 = new MyMre(s1_6, g1_6); MyMre *mre11_6 = new MyMre(s1_6, g1_6); MyMre *mre2_6 = new MyMre(s2_6, g2_6); MyMre *mre4_6 = new MyMre(s4_6, g4_6); MyMre *mre5_6 = new MyMre(s5_6, g5_6); list expected_mre_list; list received_mre_list; MyMre *t; // // Install an entry // t = mrt_4.insert(mre1_4); verbose_assert(t == mre1_4, c_format("Installing entry for %s", cstring(*mre1_4))); t = mrt_6.insert(mre1_6); verbose_assert(t == mre1_6, c_format("Installing entry for %s", cstring(*mre1_6))); // // Try to install an existing entry. The return value should be NULL. // t = mrt_4.insert(mre11_4); verbose_assert(t == NULL, c_format("Installing entry for %s", cstring(*mre11_4))); t = mrt_6.insert(mre11_6); verbose_assert(t == NULL, c_format("Installing entry for %s", cstring(*mre11_6))); // // Install an entry // t = mrt_4.insert(mre2_4); verbose_assert(t == mre2_4, c_format("Installing entry for %s", cstring(*mre2_4))); t = mrt_6.insert(mre2_6); verbose_assert(t == mre2_6, c_format("Installing entry for %s", cstring(*mre2_6))); // // Install an entry // t = mrt_4.insert(mre4_4); verbose_assert(t == mre4_4, c_format("Installing entry for %s", cstring(*mre4_4))); t = mrt_6.insert(mre4_6); verbose_assert(t == mre4_6, c_format("Installing entry for %s", cstring(*mre4_6))); // // Install an entry // t = mrt_4.insert(mre5_4); verbose_assert(t == mre5_4, c_format("Installing entry for %s", cstring(*mre5_4))); t = mrt_6.insert(mre5_6); verbose_assert(t == mre5_6, c_format("Installing entry for %s", cstring(*mre5_6))); // // Lookup an existing entry // t = mrt_4.find(s1_4, g1_4); verbose_assert(t == mre1_4, c_format("Searching for (%s, %s)", cstring(s1_4), cstring(g1_4))); t = mrt_6.find(s1_6, g1_6); verbose_assert(t == mre1_6, c_format("Searching for (%s, %s)", cstring(s1_6), cstring(g1_6))); // // Lookup an non-existing entry // t = mrt_4.find(s3_4, g3_4); verbose_assert(t == NULL, c_format("Searching for non-existing (%s, %s)", cstring(s3_4), cstring(g3_4))); t = mrt_6.find(s3_6, g3_6); verbose_assert(t == NULL, c_format("Searching for non-existing (%s, %s)", cstring(s3_6), cstring(g3_6))); // // Test table size // verbose_assert(mrt_4.size() == 4, "Testing the multicast routing table size"); verbose_assert(mrt_6.size() == 4, "Testing the multicast routing table size"); // // Test all entries ordered by source address first // received_mre_list.clear(); for (sg_iter = mrt_4.sg_begin(); sg_iter != mrt_4.sg_end(); ++sg_iter) { t = sg_iter->second; received_mre_list.push_back(t); } expected_mre_list.clear(); expected_mre_list.push_back(mre1_4); expected_mre_list.push_back(mre2_4); expected_mre_list.push_back(mre4_4); expected_mre_list.push_back(mre5_4); verbose_match(mre_list_str(received_mre_list), mre_list_str(expected_mre_list)); received_mre_list.clear(); for (sg_iter = mrt_6.sg_begin(); sg_iter != mrt_6.sg_end(); ++sg_iter) { t = sg_iter->second; received_mre_list.push_back(t); } expected_mre_list.clear(); expected_mre_list.push_back(mre1_6); expected_mre_list.push_back(mre2_6); expected_mre_list.push_back(mre4_6); expected_mre_list.push_back(mre5_6); verbose_match(mre_list_str(received_mre_list), mre_list_str(expected_mre_list)); // // Test all entries ordered by group address first // received_mre_list.clear(); for (gs_iter = mrt_4.gs_begin(); gs_iter != mrt_4.gs_end(); ++gs_iter) { t = gs_iter->second; received_mre_list.push_back(t); } expected_mre_list.clear(); expected_mre_list.push_back(mre2_4); expected_mre_list.push_back(mre1_4); expected_mre_list.push_back(mre4_4); expected_mre_list.push_back(mre5_4); verbose_match(mre_list_str(received_mre_list), mre_list_str(expected_mre_list)); received_mre_list.clear(); for (gs_iter = mrt_6.gs_begin(); gs_iter != mrt_6.gs_end(); ++gs_iter) { t = gs_iter->second; received_mre_list.push_back(t); } expected_mre_list.clear(); expected_mre_list.push_back(mre2_6); expected_mre_list.push_back(mre1_6); expected_mre_list.push_back(mre4_6); expected_mre_list.push_back(mre5_6); verbose_match(mre_list_str(received_mre_list), mre_list_str(expected_mre_list)); // // Test all entries that match a source prefix // IPvXNet s_prefix1_4(s1_4, 15); received_mre_list.clear(); sg_iter_begin = mrt_4.source_by_prefix_begin(s_prefix1_4); sg_iter_end = mrt_4.source_by_prefix_end(s_prefix1_4); for (sg_iter = sg_iter_begin; sg_iter != sg_iter_end; ++sg_iter) { t = sg_iter->second; received_mre_list.push_back(t); } expected_mre_list.clear(); expected_mre_list.push_back(mre1_4); expected_mre_list.push_back(mre2_4); verbose_match(mre_list_str(received_mre_list), mre_list_str(expected_mre_list)); IPvXNet s_prefix1_6(s1_6, 15); received_mre_list.clear(); sg_iter_begin = mrt_6.source_by_prefix_begin(s_prefix1_6); sg_iter_end = mrt_6.source_by_prefix_end(s_prefix1_6); for (sg_iter = sg_iter_begin; sg_iter != sg_iter_end; ++sg_iter) { t = sg_iter->second; received_mre_list.push_back(t); } expected_mre_list.clear(); expected_mre_list.push_back(mre1_6); expected_mre_list.push_back(mre2_6); verbose_match(mre_list_str(received_mre_list), mre_list_str(expected_mre_list)); // // Test all entries that match a source prefix // IPvXNet s_prefix2_4(s1_4, 0); received_mre_list.clear(); sg_iter_begin = mrt_4.source_by_prefix_begin(s_prefix2_4); sg_iter_end = mrt_4.source_by_prefix_end(s_prefix2_4); for (sg_iter = sg_iter_begin; sg_iter != sg_iter_end; ++sg_iter) { t = sg_iter->second; received_mre_list.push_back(t); } expected_mre_list.clear(); expected_mre_list.push_back(mre1_4); expected_mre_list.push_back(mre2_4); expected_mre_list.push_back(mre4_4); expected_mre_list.push_back(mre5_4); verbose_match(mre_list_str(received_mre_list), mre_list_str(expected_mre_list)); IPvXNet s_prefix2_6(s1_6, 0); received_mre_list.clear(); sg_iter_begin = mrt_6.source_by_prefix_begin(s_prefix2_6); sg_iter_end = mrt_6.source_by_prefix_end(s_prefix2_6); for (sg_iter = sg_iter_begin; sg_iter != sg_iter_end; ++sg_iter) { t = sg_iter->second; received_mre_list.push_back(t); } expected_mre_list.clear(); expected_mre_list.push_back(mre1_6); expected_mre_list.push_back(mre2_6); expected_mre_list.push_back(mre4_6); expected_mre_list.push_back(mre5_6); verbose_match(mre_list_str(received_mre_list), mre_list_str(expected_mre_list)); // // Test all entries that match a group prefix // IPvXNet g_prefix1_4(g4_4, 16); received_mre_list.clear(); gs_iter_begin = mrt_4.group_by_prefix_begin(g_prefix1_4); gs_iter_end = mrt_4.group_by_prefix_end(g_prefix1_4); for (gs_iter = gs_iter_begin; gs_iter != gs_iter_end; ++gs_iter) { t = gs_iter->second; received_mre_list.push_back(t); } expected_mre_list.clear(); expected_mre_list.push_back(mre4_4); expected_mre_list.push_back(mre5_4); verbose_match(mre_list_str(received_mre_list), mre_list_str(expected_mre_list)); IPvXNet g_prefix1_6(g4_6, 16); received_mre_list.clear(); gs_iter_begin = mrt_6.group_by_prefix_begin(g_prefix1_6); gs_iter_end = mrt_6.group_by_prefix_end(g_prefix1_6); for (gs_iter = gs_iter_begin; gs_iter != gs_iter_end; ++gs_iter) { t = gs_iter->second; received_mre_list.push_back(t); } expected_mre_list.clear(); expected_mre_list.push_back(mre4_6); expected_mre_list.push_back(mre5_6); verbose_match(mre_list_str(received_mre_list), mre_list_str(expected_mre_list)); // // Test all entries that match a group prefix // IPvXNet g_prefix2_4(g1_4, 0); received_mre_list.clear(); gs_iter_begin = mrt_4.group_by_prefix_begin(g_prefix2_4); gs_iter_end = mrt_4.group_by_prefix_end(g_prefix2_4); for (gs_iter = gs_iter_begin; gs_iter != gs_iter_end; ++gs_iter) { t = gs_iter->second; received_mre_list.push_back(t); } expected_mre_list.clear(); expected_mre_list.push_back(mre2_4); expected_mre_list.push_back(mre1_4); expected_mre_list.push_back(mre4_4); expected_mre_list.push_back(mre5_4); verbose_match(mre_list_str(received_mre_list), mre_list_str(expected_mre_list)); IPvXNet g_prefix2_6(g1_6, 0); received_mre_list.clear(); gs_iter_begin = mrt_6.group_by_prefix_begin(g_prefix2_6); gs_iter_end = mrt_6.group_by_prefix_end(g_prefix2_6); for (gs_iter = gs_iter_begin; gs_iter != gs_iter_end; ++gs_iter) { t = gs_iter->second; received_mre_list.push_back(t); } expected_mre_list.clear(); expected_mre_list.push_back(mre2_6); expected_mre_list.push_back(mre1_6); expected_mre_list.push_back(mre4_6); expected_mre_list.push_back(mre5_6); verbose_match(mre_list_str(received_mre_list), mre_list_str(expected_mre_list)); } int main(int argc, char * const argv[]) { int ret_value = 0; // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); int ch; while ((ch = getopt(argc, argv, "hv")) != -1) { switch (ch) { case 'v': set_verbose(true); break; case 'h': case '?': default: usage(argv[0]); xlog_stop(); xlog_exit(); if (ch == 'h') return (0); else return (1); } } argc -= optind; argv += optind; XorpUnexpectedHandler x(xorp_unexpected_handler); try { test_mrt(); ret_value = failures() ? 1 : 0; } catch (...) { // Internal error xorp_print_standard_exceptions(); ret_value = 2; } // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); return (ret_value); } xorp/mrt/tests/SConscript0000664000076400007640000000230411421137511015631 0ustar greearbgreearb# Copyright (c) 2009 XORP, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, June # 1991 as published by the Free Software Foundation. Redistribution # and/or modification of this program under the terms of any other # version of the GNU General Public License is not permitted. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, # see the GNU General Public License, Version 2, a copy of which can be # found in the XORP LICENSE.gpl file. # # XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; # http://xorp.net # $XORP$ import os Import("env") env = env.Clone() env.AppendUnique(CPPPATH = [ '#', '$BUILDDIR', '$BUILDDIR/mrt', ]) env.AppendUnique(LIBPATH = [ '$BUILDDIR/mrt', '$BUILDDIR/libxorp', '$BUILDDIR/libcomm', ]) env.AppendUnique(LIBS = [ 'xorp_mrt', 'xorp_core', 'xorp_comm', ]) test_mrib = env.AutoTest(target = 'test_mrib', source = 'test_mrib.cc') test_mrt = env.AutoTest(target = 'test_mrt', source = 'test_mrt.cc') xorp/mrt/tests/test_mrib.cc0000664000076400007640000001607011421137511016123 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // // Multicast Routing Information Base information test program. // #include "mrt_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/exceptions.hh" #include "mrt/mrib_table.hh" // // Exported variables // // // Local constants definitions // // // Local structures/classes, typedefs and macros // // // Local variables // // // Local functions prototypes // static int run_test1(); static int run_test2(); static int run_test3(); int main(int /* argc */, char *argv[]) { // // Initialize and start xlog // xlog_init(argv[0], NULL); xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages // XXX: verbosity of the error messages temporary increased xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH); xlog_add_default_output(); xlog_start(); try { // // Run the tests // run_test1(); run_test2(); run_test3(); } catch(...) { xorp_catch_standard_exceptions(); } // // Gracefully stop and exit xlog // xlog_stop(); xlog_exit(); exit(0); } static int run_test1() { Mrib *t; MribTable mrib_table(AF_INET); IPvX s1(IPv4("123.45.0.1")); IPvX s2(IPv4("123.45.0.2")); IPvX s3(IPv4("123.45.0.255")); IPvX s4(IPv4("123.45.1.255")); Mrib *mrib1 = new Mrib(IPvXNet(s1, 24)); Mrib *mrib2 = new Mrib(IPvXNet(s2, 24)); Mrib *mrib3 = new Mrib(IPvXNet(s3, 24)); printf("\n\n"); printf("RUNNING TEST1\n"); // Entries insertion printf("\n"); printf("Installing entry for %s\n", cstring(mrib1->dest_prefix())); mrib_table.insert(*mrib1); printf("PASS\n"); printf("Installing entry for %s\n", cstring(mrib2->dest_prefix())); mrib_table.insert(*mrib2); printf("PASS\n"); printf("Installing entry for %s\n", cstring(mrib3->dest_prefix())); mrib_table.insert(*mrib3); printf("PASS\n"); // Entries lookup printf("\n"); printf("Searching for %s\n", cstring(s1)); t = mrib_table.find(s1); if (t != NULL) { printf("Found entry: %s\n", cstring(t->dest_prefix())); printf("PASS\n"); } else { printf("Entry not found!\n"); printf("FAIL\n"); } // Lookup for non-existing entry printf("\n"); printf("Searching for %s\n", cstring(s4)); t = mrib_table.find(s4); if (t == NULL) { printf("PASS\n"); } else { printf("Found entry: %s\n", cstring(t->dest_prefix())); printf("FAIL\n"); } // MribTable size printf("\n"); printf("MribTable size = %u\n", XORP_UINT_CAST(mrib_table.size())); printf("PASS\n"); // All entries printout printf("\n"); printf("List of all entries:\n"); for (MribTable::iterator iter = mrib_table.begin(); iter != mrib_table.end(); ++iter) { t = *iter; if (t != NULL) printf("%s\n", cstring(t->dest_prefix())); } printf("PASS\n"); return (0); } static int run_test2() { Mrib *t; MribTable mrib_table(AF_INET); IPvX s1(IPv4("1.1.0.0")); IPvX s2(IPv4("1.2.0.0")); IPvX s3(IPv4("1.2.0.0")); Mrib *mrib1 = new Mrib(IPvXNet(s1, 16)); Mrib *mrib2 = new Mrib(IPvXNet(s2, 24)); Mrib *mrib3 = new Mrib(IPvXNet(s3, 16)); IPvX s4(IPv4("1.2.0.1")); IPvX s5(IPv4("1.2.1.1")); printf("\n\n"); printf("RUNNING TEST2\n"); // Entries insertion printf("\n"); printf("Installing entry for %s\n", cstring(mrib1->dest_prefix())); mrib_table.insert(*mrib1); printf("PASS\n"); printf("Installing entry for %s\n", cstring(mrib2->dest_prefix())); mrib_table.insert(*mrib2); printf("PASS\n"); printf("Installing entry for %s\n", cstring(mrib3->dest_prefix())); mrib_table.insert(*mrib3); printf("PASS\n"); // Entries lookup printf("\n"); printf("Searching for %s\n", cstring(s4)); t = mrib_table.find(s4); if (t != NULL) { printf("Found entry: %s\n", cstring(t->dest_prefix())); printf("PASS\n"); } else { printf("Entry not found!\n"); printf("FAIL\n"); } printf("\n"); printf("Searching for %s\n", cstring(s5)); t = mrib_table.find(s5); if (t != NULL) { printf("Found entry: %s\n", cstring(t->dest_prefix())); printf("PASS\n"); } else { printf("Entry not found!\n"); printf("FAIL\n"); } // All entries printout printf("\n"); printf("List of all entries:\n"); for (MribTable::iterator iter = mrib_table.begin(); iter != mrib_table.end(); ++iter) { t = *iter; if (t != NULL) printf("%s\n", cstring(t->dest_prefix())); } printf("PASS\n"); return (0); } static int run_test3() { Mrib *t; MribTable mrib_table(AF_INET); IPvX s1(IPv4("1.1.0.0")); IPvX s2(IPv4("1.2.1.0")); IPvX s3(IPv4("1.2.0.0")); Mrib *mrib1 = new Mrib(IPvXNet(s1, 16)); Mrib *mrib2 = new Mrib(IPvXNet(s2, 24)); Mrib *mrib3 = new Mrib(IPvXNet(s3, 16)); IPvX s4(IPv4("1.2.0.1")); IPvX s5(IPv4("1.2.1.1")); printf("\n\n"); printf("RUNNING TEST3\n"); // Entries insertion printf("\n"); printf("Installing entry for %s\n", cstring(mrib1->dest_prefix())); mrib_table.insert(*mrib1); printf("PASS\n"); printf("Installing entry for %s\n", cstring(mrib2->dest_prefix())); mrib_table.insert(*mrib2); printf("PASS\n"); printf("Installing entry for %s\n", cstring(mrib3->dest_prefix())); mrib_table.insert(*mrib3); printf("PASS\n"); // Entries lookup printf("\n"); printf("Searching for %s\n", cstring(s4)); t = mrib_table.find(s4); if (t != NULL) { printf("Found entry: %s\n", cstring(t->dest_prefix())); printf("PASS\n"); } else { printf("Entry not found!\n"); printf("FAIL\n"); } printf("\n"); printf("Searching for %s\n", cstring(s5)); t = mrib_table.find(s5); if (t != NULL) { printf("Found entry: %s\n", cstring(t->dest_prefix())); printf("PASS\n"); } else { printf("Entry not found!\n"); printf("FAIL\n"); } // All entries printout printf("\n"); printf("List of all entries:\n"); for (MribTable::iterator iter = mrib_table.begin(); iter != mrib_table.end(); ++iter) { t = *iter; if (t != NULL) printf("%s\n", cstring(t->dest_prefix())); } printf("PASS\n"); return (0); } xorp/mrt/netstream_access.h0000664000076400007640000001601211540224231016150 0ustar greearbgreearb/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- */ /* vim:set sts=4 ts=8: */ /* * Copyright (c) 2001-2011 XORP, Inc and Others * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 2, June * 1991 as published by the Free Software Foundation. Redistribution * and/or modification of this program under the terms of any other * version of the GNU General Public License is not permitted. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, * see the GNU General Public License, Version 2, a copy of which can be * found in the XORP LICENSE.gpl file. * * XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; * http://xorp.net */ /* * $XORP: xorp/mrt/netstream_access.h,v 1.10 2008/10/02 21:57:45 bms Exp $ */ #ifndef __MRT_NETSTREAM_ACCESS_H__ #define __MRT_NETSTREAM_ACCESS_H__ /* * A set of macros to get/put data from/to a stream when the fields * are not 32-bit boundary aligned. Also useful to automatically * check the boundary of the input/output buffer while accessing it. */ /* * Idea taken from Eddy Rusty (eddy@isi.edu). */ #include "libxorp/xorp.h" #include /* * Constants definitions */ /* * Structures, typedefs and macros */ /* * XXX: the data in the stream is ALWAYS in "network order". * * The current stream pointer is (uint8_t *)(cp) * * XXX: the macros below assume that the code has the following * labels that can be used to jump and process the particular error: * 'rcvlen_error' 'buflen_error' * * PUT_NET_32 puts "network ordered" 32-bit data to the datastream. * PUT_HOST_32 puts "host ordered" 32-bit data to the datastream. * GET_NET_32 gets 32-bit data and keeps it in "network order" in the memory. * GET_HOST_32 gets 32-bit data, but in the memory it is in "host order". * The same applies for the 16-bit {PUT,GET}_{NET,HOST}_16 * GET_OCTET and PUT_OCTET get and put a single octet from/to the datastream. * GET_SKIP(octets,...) skips the next 'octets' from the datastream. * PUT_SKIP(octets,...) leaves the next 'octets' untouched in the datastream. * GET_DATA(to, cp, rcvlen, datalen) copies 'datalen' octets from 'cp' to 'to'. * PUT_DATA(from, cp, buflen, datalen) copies 'datalen' octets from 'from' * to 'cp'. */ #define GET_DATA(to, cp, rcvlen, datalen) \ do { \ if ((size_t)(rcvlen) < (size_t)(datalen)) \ goto rcvlen_error; \ memcpy((to), (cp), (datalen)); \ (rcvlen) -= (datalen); \ (cp) += (datalen); \ } while (0) #define PUT_DATA(from, cp, buflen, datalen) \ do { \ if ((size_t)(buflen) < (size_t)(datalen)) \ goto buflen_error; \ memcpy((cp), (from), (datalen)); \ (buflen) -= (datalen); \ (cp) += (datalen); \ } while (0) #define GET_SKIP(octets, cp, rcvlen) \ do { \ if ((size_t)(rcvlen) < (size_t)(octets)) \ goto rcvlen_error; \ (rcvlen) -= (octets); \ (cp) += (octets); \ } while (0) #define PUT_SKIP(octets, cp, buflen) \ do { \ if ((size_t)(buflen) < (size_t)(octets)) \ goto buflen_error; \ (buflen) -= (octets); \ (cp) += (octets); \ } while (0) #define GET_OCTET(val, cp, rcvlen) \ do { \ if ((size_t)(rcvlen) < (size_t)1) \ goto rcvlen_error; \ (rcvlen)--; \ ((val) = *(cp)++); \ } while (0) #define PUT_OCTET(val, cp, buflen) \ do { \ if ((size_t)(buflen) < (size_t)1) \ goto buflen_error; \ (buflen)--; \ (*(cp)++ = (uint8_t)(val)); \ } while (0) #define GET_HOST_16(val, cp, rcvlen) \ do { \ register uint16_t v_; \ \ if ((size_t)(rcvlen) < (size_t)2) \ goto rcvlen_error; \ (rcvlen) -= 2; \ v_ = (*(cp)++) << 8; \ v_ |= *(cp)++; \ (val) = v_; \ } while (0) #define PUT_HOST_16(val, cp, buflen) \ do { \ register uint16_t v_; \ \ if ((size_t)(buflen) < (size_t)2) \ goto buflen_error; \ (buflen) -= 2; \ v_ = (uint16_t)(val); \ *(cp)++ = (uint8_t)(v_ >> 8); \ *(cp)++ = (uint8_t)v_; \ } while (0) #if defined(BYTE_ORDER) && (BYTE_ORDER == LITTLE_ENDIAN) #define GET_NET_16(val, cp, rcvlen) \ do { \ register uint16_t v_; \ \ if ((size_t)(rcvlen) < (size_t)2) \ goto rcvlen_error; \ (rcvlen) -= 2; \ v_ = *(cp)++; \ v_ |= (*(cp)++) << 8; \ (val) = v_; \ } while (0) #define PUT_NET_16(val, cp, buflen) \ do { \ register uint16_t v_; \ \ if ((size_t)(buflen) < (size_t)2) \ goto buflen_error; \ (buflen) -= 2; \ v_ = (uint16_t)(val); \ *(cp)++ = (uint8_t)v_; \ *(cp)++ = (uint8_t)(v_ >> 8); \ } while (0) #endif /* LITTLE_ENDIAN {GET,PUT}_NET_16 */ #if defined(BYTE_ORDER) && (BYTE_ORDER == BIG_ENDIAN) #define GET_NET_16(val, cp, rcvlen) GET_HOST_16(val, cp, rcvlen) #define PUT_NET_16(val, cp, buflen) PUT_HOST_16(val, cp, buflen) #endif /* BIG_ENDIAN {GET,PUT}_NET_16 */ #define GET_HOST_32(val, cp, rcvlen) \ do { \ register uint32_t v_; \ \ if ((size_t)(rcvlen) < (size_t)4) \ goto rcvlen_error; \ (rcvlen) -= 4; \ v_ = (*(cp)++) << 24; \ v_ |= (*(cp)++) << 16; \ v_ |= (*(cp)++) << 8; \ v_ |= *(cp)++; \ (val) = v_; \ } while (0) #define PUT_HOST_32(val, cp, buflen) \ do { \ register uint32_t v_; \ \ if ((size_t)(buflen) < (size_t)4) \ goto buflen_error; \ (buflen) -= 4; \ v_ = (uint32_t)(val); \ *(cp)++ = (uint8_t)(v_ >> 24); \ *(cp)++ = (uint8_t)(v_ >> 16); \ *(cp)++ = (uint8_t)(v_ >> 8); \ *(cp)++ = (uint8_t)v_; \ } while (0) #if defined(BYTE_ORDER) && (BYTE_ORDER == LITTLE_ENDIAN) #define GET_NET_32(val, cp, rcvlen) \ do { \ register uint32_t v_; \ \ if ((size_t)(rcvlen) < (size_t)4) \ goto rcvlen_error; \ (rcvlen) -= 4; \ v_ = *(cp)++; \ v_ |= (*(cp)++) << 8; \ v_ |= (*(cp)++) << 16; \ v_ |= (*(cp)++) << 24; \ (val) = v_; \ } while (0) #define PUT_NET_32(val, cp, buflen) \ do { \ register uint32_t v_; \ \ if ((size_t)(buflen) < (size_t)4) \ goto buflen_error; \ (buflen) -= 4; \ v_ = (uint32_t)(val); \ *(cp)++ = (uint8_t)v_; \ *(cp)++ = (uint8_t)(v_ >> 8); \ *(cp)++ = (uint8_t)(v_ >> 16); \ *(cp)++ = (uint8_t)(v_ >> 24); \ } while (0) #endif /* LITTLE_ENDIAN {GET,PUT}_HOST_32 */ #if defined(BYTE_ORDER) && (BYTE_ORDER == BIG_ENDIAN) #define GET_NET_32(val, cp, rcvlen) GET_HOST_32(val, cp, rcvlen) #define PUT_NET_32(val, cp, buflen) PUT_HOST_32(val, cp, buflen) #endif /* BIG_ENDIAN {GET,PUT}_HOST_32 */ /* * Global variables */ /* * Global functions prototypes */ __BEGIN_DECLS __END_DECLS #endif /* __MRT_NETSTREAM_ACCESS_H__ */ xorp/mrt/multicast_defs.h0000664000076400007640000000343511421137511015642 0ustar greearbgreearb/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- */ /* vim:set sts=4 ts=8: */ /* * Copyright (c) 2001-2009 XORP, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 2, June * 1991 as published by the Free Software Foundation. Redistribution * and/or modification of this program under the terms of any other * version of the GNU General Public License is not permitted. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, * see the GNU General Public License, Version 2, a copy of which can be * found in the XORP LICENSE.gpl file. * * XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; * http://xorp.net */ /* * $XORP: xorp/mrt/multicast_defs.h,v 1.11 2008/10/02 21:57:45 bms Exp $ */ #ifndef __MRT_MULTICAST_DEFS_H__ #define __MRT_MULTICAST_DEFS_H__ /* * Various multicast-related definitions. */ /* XXX: everything here probably should go somewhere else. */ #include "libxorp/xorp.h" #ifdef HAVE_NETINET_IN_SYSTM_H #include #endif #ifdef HAVE_NETINET_IP_H #include #endif /* * Constants definitions */ enum action_jp_t { ACTION_JOIN = 0, ACTION_PRUNE }; #define ACTION_JP2ASCII(action_flag) \ (((action_flag) == ACTION_JOIN) ? \ "JOIN" : "PRUNE") #ifndef MINTTL #define MINTTL 1 #endif #ifndef IPDEFTTL #define IPDEFTTL 64 #endif #ifndef MAXTTL #define MAXTTL 255 #endif /* * Structures, typedefs and macros */ /* * Global variables */ /* * Global functions prototypes */ __BEGIN_DECLS __END_DECLS #endif /* __MRT_MULTICAST_DEFS_H__ */ xorp/mrt/mrib_table.hh0000664000076400007640000004477211540224231015113 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, Version 2, June // 1991 as published by the Free Software Foundation. Redistribution // and/or modification of this program under the terms of any other // version of the GNU General Public License is not permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU General Public License, Version 2, a copy of which can be // found in the XORP LICENSE.gpl file. // // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/mrt/mrib_table.hh,v 1.17 2008/10/02 21:57:45 bms Exp $ #ifndef __MRT_MRIB_TABLE_HH__ #define __MRT_MRIB_TABLE_HH__ // // Multicast Routing Information Base Table header file. // #include "libxorp/ipvx.hh" #include "libxorp/ipvxnet.hh" // // Constants definitions // enum { MRIB_DONT_CREATE = false, MRIB_DO_CREATE = true }; // // Structures/classes, typedefs and macros // class MribTable; class MribTableIterator; class MribLookup; class Mrib; /** * @short The Multicast Routing Information Base payload entry. */ class Mrib { public: /** * Constructor for a given address family * * @param family the address family. */ Mrib(int family); /** * Constructor for a given network address prefix. * * @param dest_prefix the network address prefix. */ Mrib(const IPvXNet& dest_prefix); /** * Copy constructor for a given @ref Mrib entry. * * @param mrib the @ref Mrib entry to copy. */ Mrib(const Mrib& mrib); /** * Equality Operator * * @param other the right-hand operand to compare against. * @return true if the left-hand operand is numerically same as the * right-hand operand. */ bool operator==(const Mrib& other) const; /** * Get the network prefix address. * * @return the network prefix address. */ const IPvXNet& dest_prefix() const { return (_dest_prefix); } /** * Set the network prefix address. * * @param v the value of the network prefix address to set. */ void set_dest_prefix(const IPvXNet& v) { _dest_prefix = v; } /** * Get the next-hop router address. * * @return the next-hop router address. */ const IPvX& next_hop_router_addr() const { return (_next_hop_router_addr); } /** * Set the next-hop router address. * * @param v the value of the next-hop router address to set. */ void set_next_hop_router_addr(const IPvX& v) { _next_hop_router_addr = v; } /** * Get the vif index of the interface toward the next-hop router. * * @return the vif index of the interface toward the next-hop router. */ uint32_t next_hop_vif_index() const { return (_next_hop_vif_index); } /** * Set the vif index of the interface toward the next-hop router. * * @param v the value of the vif index to set. */ void set_next_hop_vif_index(uint32_t v) { _next_hop_vif_index = v; } /** * Get the metric preference value. * * @return the metric preference value. */ uint32_t metric_preference() const { return (_metric_preference); } /** * Set the metric preference value. * * @param v the value of the metric preference to set. */ void set_metric_preference(uint32_t v) { _metric_preference = v; } /** * Get the metric value. * * @return the metric value. */ uint32_t metric() const { return (_metric); } /** * Set the metric value. * * @param v the value of the metric to set. */ void set_metric(uint32_t v) { _metric = v; } /** * Convert this entry from binary form to presentation format. * * @return C++ string with the human-readable ASCII representation * of the entry. */ string str() const; private: IPvXNet _dest_prefix; // The destination prefix address IPvX _next_hop_router_addr; // The address of the next-hop router uint32_t _next_hop_vif_index; // The vif index to the next-hop router uint32_t _metric_preference; // The metric preference to the // destination uint32_t _metric; // The metric to the destination }; /** * @short The Multicast Routing Information Base Table iterator */ class MribTableIterator { public: /** * Constructor for a given @ref MribLookup entry. * * @param mrib_lookup the basic @ref MribLookup entry. */ MribTableIterator(MribLookup *mrib_lookup) : _mrib_lookup(mrib_lookup) {} /** * Destructor */ MribTableIterator() {} /** * Increment Operator (prefix). * * Increment the iterator to point to the next @ref MribLookup entry. * * @return a reference to the iterator after it was incremented. * @see MribLookup::get_next() */ MribTableIterator& operator++(); /** * Increment Operator (postfix). * * Increment the iterator to point to the next @ref MribLookup entry. * * @return the value of the iterator before it was incremented. * @see MribLookup::get_next() */ MribTableIterator operator++(int); /** * Equality Operator * * @param other the right-hand operand to compare against. * @return true if the left-hand operand is same as the * right-hand operand. */ bool operator==(const MribTableIterator& other) const { return (_mrib_lookup == other._mrib_lookup); } /** * Not-Equal Operator * * @param other the right-hand operand to compare against. * @return true if the left-hand operand is not same as the * right-hand operand. */ bool operator!=(const MribTableIterator& other) const { return (_mrib_lookup != other._mrib_lookup); } /** * Indirection Operator * * @return a pointer to the @ref Mrib entry that corresponds to this * iterator. */ Mrib* operator*() const; private: MribLookup *_mrib_lookup; // The MribLookup entry for this iterator }; /** * @short Base class for the Multicast Routing Information Base Table */ class MribTable { public: /** * Constructor for table of a given address family. * * @param family the address family. */ MribTable(int family); /** * Destructor */ ~MribTable(); typedef MribTableIterator iterator; /** * Get the address family. * * @return the address family ((e.g., AF_INET or AF_INET6 for IPv4 and * IPv6 respectively). */ int family() const { return (_family); } /** * Remove all entries and pending transactions (make the container empty). */ void clear(); /** * Remove all entries. */ void remove_all_entries(); /** * Get a reference to the list with removed @ref Mrib entries. * * @return a reference to the list with removed @ref Mrib entries. */ list& removed_mrib_entries() { return (_removed_mrib_entries); } /** * Test if the removed @ref Mrib entries are preserved or deleted. * * @return true if the removed @ref Mrib entries are preserved, otherwise * false. */ bool is_preserving_removed_mrib_entries() const { return (_is_preserving_removed_mrib_entries); } /** * Enable or disable the preserving of the removed @ref Mrib entries. * * @param v if true, then the removed @ref Mrib entries are preserved * otherwise they are deleted. */ void set_is_preserving_removed_mrib_entries(bool v) { _is_preserving_removed_mrib_entries = v; } /** * Insert a copy of a @ref Mrib entry. * * Note: if there is an existing @ref Mrib entry for the same prefix, * the old entry is deleted. * * @param mrib the entry to insert. * @return a pointer to the inserted entry on success, otherwise NULL. */ Mrib *insert(const Mrib& mrib); /** * Remove from the table a @ref Mrib entry for a given destination prefix. * * @param dest_prefix the destination prefix of the entry to remove. */ void remove(const IPvXNet& dest_prefix); /** * Remove a @ref Mrib entry from the table. * * @param mrib a @ref Mrib with information about the entry to remove. */ void remove(const Mrib& mrib); /** * Find the longest prefix match for an address. * * @param address the lookup address. * @return a pointer to the longest prefix @ref Mrib match * for @ref address if exists, otherwise NULL. */ Mrib *find(const IPvX& address) const; /** * Find an exact match for a network address prefix. * * @param dest_prefix the lookup network address prefix. * @return a pointer to the exact @ref Mrib match for @ref dest_prefix if * exists, otherwise NULL. */ Mrib *find_exact(const IPvXNet& dest_prefix) const; /** * Get an iterator for the first element. * * @return the iterator for the first element. */ iterator begin() const { return (_mrib_lookup_root); } /** * Get an iterator for the last element. * * @return the iterator for the last element. */ iterator end() const { return (NULL); } /** * Update the vif index of a @ref Mrib entry. * * @param dest_prefix the destination prefix of the @ref Mrib entry * to update. * @param vif_index the new vif index of the @ref Mrib entry. */ void update_entry_vif_index(const IPvXNet& dest_prefix, uint32_t vif_index); // // Pending transactions related methods // /** * Add a pending transaction to insert a @ref Mrib entry from the table. * * The operation is added to the list of pending transactions, but * the entry itself is not added to the table * (until @ref MribTable::commit_pending_transactions() is called). * * @param tid the transaction ID. * @param mrib the @ref Mrib entry that contains the information about * the entry to add. */ void add_pending_insert(uint32_t tid, const Mrib& mrib); /** * Add a pending transaction to remove a @ref Mrib entry from the table. * * the operation is added to the list of pending transaction, but the * entry itself is not removed from the table * (until @ref MribTable::commit_pending_transactions() is called). * * @param tid the transaction ID. * @param mrib the @ref Mrib entry that contains the information about * the entry to remove. */ void add_pending_remove(uint32_t tid, const Mrib& mrib); /** * Add a pending transaction to remove all @ref Mrib entries * from the table. * * the operation is added to the list of pending transaction, but the * entries themselves is not removed from the table * (until @ref MribTable::commit_pending_transactions() is called). * * @param tid the transaction ID. */ void add_pending_remove_all_entries(uint32_t tid); /** * Commit pending transactions for adding or removing @ref Mrib * entries for a given transaction ID. * * All pending transactions to add/remove @ref Mrib entries for a given * transaction ID are processes (see @ref MribTable::add_pending_insert() * and @ref MribTable::add_pending_remove() * and @ref MribTable::add_pending_remove_all_entries()). * * @param tid the transaction ID of the entries to commit. */ void commit_pending_transactions(uint32_t tid); /** * Abort pending transactions for adding or removing @ref Mrib * entries for a given transaction ID. * * @param tid the transaction ID of the entries to abort. */ void abort_pending_transactions(uint32_t tid); /** * Abort all pending transactions for adding or remove @ref Mrib entries. */ void abort_all_pending_transactions() { _mrib_pending_transactions.clear(); } /** * Get the number of @ref Mrib entries in the table. * * @return the number of @ref Mrib entries in the table. */ size_t size() const { return (_mrib_size); } private: /** * Remove a @ref Mrib entry from the table. * * The @ref Mrib entry itself is either deleted or added to the list * with removed entries. * * @param mrib the @ref Mrib entry to remove. */ void remove_mrib_entry(Mrib *mrib); /** * Find an exact @ref MribLookip match. * * @param addr_prefix the lookup network address prefix. * @return a pointer to the exact @ref MribLookup match * for @ref addr_prefix if exists, otherwise NULL. */ MribLookup *find_prefix_mrib_lookup(const IPvXNet& addr_prefix) const; /** * Remove a subtree of entries in the table. * * Remove recursively all @ref MribLookup below and including * the given @ref mrib_lookup entry. * * @param mrib_lookup the @ref MribLookup entry that is the root of the * subtree to remove. */ void remove_mrib_lookup(MribLookup *mrib_lookup); // // Private class used to keep track of pending transactions // class PendingTransaction { public: /** * Constructor to insert or remove a MRIB entry. * * @param tid the transaction ID. * @param mrib the MRIB entry to insert or remove. * @param is_insert if true, then insert the entry, otherwise * remove it. */ PendingTransaction(uint32_t tid, const Mrib& mrib, bool is_insert) : _tid(tid), _mrib(mrib), _is_insert(is_insert), _is_remove_all(false) {} #ifdef XORP_USE_USTL PendingTransaction(): _mrib(AF_INET) { } #endif /** * Constructor to remove all entries. */ PendingTransaction(const MribTable& mrib_table, uint32_t tid) : _tid(tid), _mrib(Mrib(IPvXNet(IPvX::ZERO(mrib_table.family()), 0))), _is_insert(false), _is_remove_all(true) {} uint32_t tid() const { return (_tid); } const Mrib& mrib() const { return (_mrib); } bool is_insert() const { return (_is_insert); } bool is_remove_all() const { return (_is_remove_all); } void update_entry_vif_index(uint32_t vif_index) { _mrib.set_next_hop_vif_index(vif_index); } private: uint32_t _tid; // The transaction ID Mrib _mrib; // The MRIB to add or remove bool _is_insert; // If true, insert, otherwise remove bool _is_remove_all; // If true, remove all entries }; // // The private state // int _family; // The address family of this table MribLookup *_mrib_lookup_root; // The root of the MRIB lookup tree size_t _mrib_lookup_size; // The number of MribLookup entries size_t _mrib_size; // The number of Mrib entries // // The list of pending transactions // list _mrib_pending_transactions; // // A flag to indicate whether the removed Mrib entries are preserved // on the _removed_mrib_entries list, or are deleted. // bool _is_preserving_removed_mrib_entries; // // The list of removed Mrib entries that may be still in use. // list _removed_mrib_entries; }; /** * @short The basic entry in the @ref MribTable lookup tree */ class MribLookup { public: /** * Constructor for a given parent entry. * * @param parent the parent entry. */ MribLookup(MribLookup *parent) : _parent(parent), _left_child(NULL), _right_child(NULL), _mrib(NULL) {} /** * Destructor */ ~MribLookup() { if (_mrib != NULL) delete _mrib; } /** * Get the corresponding @ref Mrib entry. * * @return the corresponding @ref Mrib entry if exists, otherwise NULL. */ Mrib *mrib() const { return (_mrib); } /** * Set the corresponding @ref Mrib entry. * * Note that the previous value of the corresponding @ref Mrib entry * is overwritten. * * @param v the value of the corresponding @ref Mrib entry. */ void set_mrib(Mrib *v) { _mrib = v; } /** * Get the parent @ref MribLookup entry. * * @return the parent @ref MribLookup entry. */ MribLookup *parent() { return (_parent); } /** * Set the parent @ref MribLookup entry. * * Note that the previous value of the parent is overwritten. * * @param v the parent @ref MribLookup to assign to this entry. */ void set_parent(MribLookup *v) { _parent = v; } /** * Get the left child @ref MribLookup entry. * * Note that the previous value of the left child is overwritten. * * @return the left child @ref MribLookup entry. */ MribLookup *left_child() { return (_left_child); } /** * Set the left child @ref MribLookup entry. * * @param v the left child @ref MribLookup to assign to this entry. */ void set_left_child(MribLookup *v) { _left_child = v; } /** * Get the right child @ref MribLookup entry. * * @return the right child @ref MribLookup entry. */ MribLookup *right_child() { return (_right_child); } /** * Set the right child @ref MribLookup entry. * * Note that the previous value of the right child is overwritten. * * @param v the right child @ref MribLookup to assign to this entry. */ void set_right_child(MribLookup *v) { _right_child = v; } /** * Get the next @ref MribLookup entry. * * The ordering of the entries is "depth-first search", where * the nodes are returned in the following order: * (a) the parent node; * (b) the left-child node and all nodes within its subtree; * (c) the right-child node and all nodes within its subtree; * * @return the next @ref MribLookup entry if exists, otherwise NULL. */ MribLookup *get_next() const; private: MribLookup *_parent; // The parent in the lookup tree MribLookup *_left_child; // The left child in the lookup tree MribLookup *_right_child; // The right child in the lookup tree Mrib *_mrib; // A pointer to the MRIB info }; // // Global variables // // // Global functions prototypes // #endif // __MRT_MRIB_TABLE_HH__ xorp/mrt/include/0000775000076400007640000000000011421137511014101 5ustar greearbgreearbxorp/mrt/include/netinet/0000775000076400007640000000000011421137511015547 5ustar greearbgreearbxorp/mrt/include/netinet/pim_var.h0000664000076400007640000000622111421137511017356 0ustar greearbgreearb/* $FreeBSD$ */ /* * Copyright (c) 1998-2000 * University of Southern California/Information Sciences Institute. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef _NETINET_PIM_VAR_H_ #define _NETINET_PIM_VAR_H_ /* * Protocol Independent Multicast (PIM), * kernel variables and implementation-specific definitions. * * Written by George Edmond Eddy (Rusty), ISI, February 1998. * Modified by Pavlin Radoslavov, USC/ISI, May 1998, Aug 1999, October 2000. * Modified by Hitoshi Asaeda, WIDE, August 1998. */ /* * PIM statistics kept in the kernel */ struct pimstat { u_quad_t pims_rcv_total_msgs; /* total PIM messages received */ u_quad_t pims_rcv_total_bytes; /* total PIM bytes received */ u_quad_t pims_rcv_tooshort; /* rcvd with too few bytes */ u_quad_t pims_rcv_badsum; /* rcvd with bad checksum */ u_quad_t pims_rcv_badversion; /* rcvd bad PIM version */ u_quad_t pims_rcv_registers_msgs; /* rcvd regs. msgs (data only) */ u_quad_t pims_rcv_registers_bytes; /* rcvd regs. bytes (data only) */ u_quad_t pims_rcv_registers_wrongiif; /* rcvd regs. on wrong iif */ u_quad_t pims_rcv_badregisters; /* rcvd invalid registers */ u_quad_t pims_snd_registers_msgs; /* sent regs. msgs (data only) */ u_quad_t pims_snd_registers_bytes; /* sent regs. bytes (data only) */ }; /* * Names for PIM sysctl objects */ #define PIMCTL_STATS 1 /* statistics (read-only) */ #define PIMCTL_MAXID 2 #define PIMCTL_NAMES { \ { 0, 0 }, \ { "stats", CTLTYPE_STRUCT }, \ } #if (defined(KERNEL)) || (defined(_KERNEL)) extern struct pimstat pimstat; void pim_input(struct mbuf *, int); SYSCTL_DECL(_net_inet_pim); #endif /* KERNEL */ #endif /* _NETINET_PIM_VAR_H_ */ xorp/mrt/include/netinet/pim.h0000664000076400007640000001030211421137511016501 0ustar greearbgreearb/* $FreeBSD$ */ /* * Copyright (c) 1996-2000 * University of Southern California/Information Sciences Institute. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef _NETINET_PIM_H_ #define _NETINET_PIM_H_ /* * Protocol Independent Multicast (PIM) definitions. * RFC 2362, June 1998. * * Written by Ahmed Helmy, USC/SGI, July 1996. * Modified by George Edmond Eddy (Rusty), ISI, February 1998. * Modified by Pavlin Radoslavov, USC/ISI, May 1998, October 2000. */ #ifndef _PIM_VT #ifndef BYTE_ORDER # error BYTE_ORDER is not defined! #endif #if (BYTE_ORDER != BIG_ENDIAN) && (BYTE_ORDER != LITTLE_ENDIAN) # error BYTE_ORDER must be defined to either BIG_ENDIAN or LITTLE_ENDIAN #endif #endif /* ! _PIM_VT */ /* * PIM packet header */ struct pim { #ifdef _PIM_VT uint8_t pim_vt; /* PIM version and message type */ #else /* ! _PIM_VT */ #if BYTE_ORDER == BIG_ENDIAN u_int pim_vers:4, /* PIM protocol version */ pim_type:4; /* PIM message type */ #endif #if BYTE_ORDER == LITTLE_ENDIAN u_int pim_type:4, /* PIM message type */ pim_vers:4; /* PIM protocol version */ #endif #endif /* ! _PIM_VT */ uint8_t pim_reserved; /* Reserved */ uint16_t pim_cksum; /* IP-style checksum */ }; /* KAME-related name backward compatibility */ #define pim_ver pim_vers #define pim_rsv pim_reserved #ifdef _PIM_VT #define PIM_MAKE_VT(v, t) (0xff & (((v) << 4) | (0x0f & (t)))) #define PIM_VT_V(x) (((x) >> 4) & 0x0f) #define PIM_VT_T(x) ((x) & 0x0f) #endif /* _PIM_VT */ #define PIM_VERSION 2 #define PIM_MINLEN 8 /* PIM message min. length */ #define PIM_REG_MINLEN (PIM_MINLEN+20) /* PIM Register hdr + inner IPv4 hdr */ #define PIM6_REG_MINLEN (PIM_MINLEN+40) /* PIM Register hdr + inner IPv6 hdr */ /* * PIM message types */ #define PIM_HELLO 0x0 /* PIM-SM and PIM-DM */ #define PIM_REGISTER 0x1 /* PIM-SM only */ #define PIM_REGISTER_STOP 0x2 /* PIM-SM only */ #define PIM_JOIN_PRUNE 0x3 /* PIM-SM and PIM-DM */ #define PIM_BOOTSTRAP 0x4 /* PIM-SM only */ #define PIM_ASSERT 0x5 /* PIM-SM and PIM-DM */ #define PIM_GRAFT 0x6 /* PIM-DM only */ #define PIM_GRAFT_ACK 0x7 /* PIM-DM only */ #define PIM_CAND_RP_ADV 0x8 /* PIM-SM only */ #define PIM_ALL_DF_ELECTION 0xa /* Bidir-PIM-SM only */ /* * PIM-Register message flags */ #define PIM_BORDER_REGISTER 0x80000000U /* The Border bit (host-order) */ #define PIM_NULL_REGISTER 0x40000000U /* The Null-Register bit (host-order)*/ /* * All-PIM-Routers IPv4 and IPv6 multicast addresses */ #define INADDR_ALLPIM_ROUTERS_GROUP (uint32_t)0xe000000dU /* 224.0.0.13 */ #define IN6ADDR_LINKLOCAL_ALLPIM_ROUTERS "ff02::d" #define IN6ADDR_LINKLOCAL_ALLPIM_ROUTERS_INIT \ {{{ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d }}} #endif /* _NETINET_PIM_H_ */ xorp/mrt/include/ip_mroute.h0000664000076400007640000000732711421137511016266 0ustar greearbgreearb/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- */ /* * Copyright (c) 2001-2009 XORP, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 2, June * 1991 as published by the Free Software Foundation. Redistribution * and/or modification of this program under the terms of any other * version of the GNU General Public License is not permitted. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, * see the GNU General Public License, Version 2, a copy of which can be * found in the XORP LICENSE.gpl file. * * XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; * http://xorp.net */ /* * $XORP: xorp/mrt/include/ip_mroute.h,v 1.21 2008/10/02 21:57:46 bms Exp $ */ #ifndef __MRT_INCLUDE_IP_MROUTE_H__ #define __MRT_INCLUDE_IP_MROUTE_H__ #include "libxorp/xorp.h" #ifdef HAVE_NET_ROUTE_H #include #endif /* * FreeBSD (all versions) */ #if defined(HOST_OS_FREEBSD) # include #endif /* * NetBSD (all versions) * OpenBSD (all versions) * * Prologue. * * XXX: On these platforms the definition of 'struct igmpmsg' * and IGMPMSG_* is wrapped inside #ifdef _KERNEL hence we need * to define _KERNEL before including . */ #if defined(HOST_OS_NETBSD) || defined(HOST_OS_OPENBSD) #define _KERNEL #endif /* * Non-Linux platforms with the * header available. */ #if defined(HAVE_NETINET_IP_MROUTE_H) && !defined(HOST_OS_LINUX) # include #endif /* * NetBSD (all versions) * OpenBSD (all versions) * * Epilogue. */ #if defined(HOST_OS_NETBSD) || defined(HOST_OS_OPENBSD) #undef _KERNEL #endif /* * DragonFlyBSD * * DragonFlyBSD (as per version 1.4) has moved to * . Hopefully, in the future it will be back * to its appropriate location. */ #ifdef HAVE_NET_IP_MROUTE_IP_MROUTE_H # include #endif /* * Linux hacks because of broken Linux header files */ #if defined(HOST_OS_LINUX) # include # ifndef _LINUX_IN_H # define _LINUX_IN_H /* XXX: a hack to exclude */ # define EXCLUDE_LINUX_IN_H # endif # ifndef __LINUX_PIM_H # define __LINUX_PIM_H /* XXX: a hack to exclude */ # define EXCLUDE_LINUX_PIM_H # endif # include # ifdef EXCLUDE_LINUX_IN_H # undef _LINUX_IN_H # undef EXCLUDE_LINUX_IN_H # endif # ifdef EXCLUDE_LINUX_PIM_H # undef __LINUX_PIM_H # undef EXCLUDE_LINUX_PIM_H # endif /* * XXX: Conditionally add missing definitions from the * header file. */ # ifndef IGMPMSG_NOCACHE # define IGMPMSG_NOCACHE 1 # endif # ifndef IGMPMSG_WRONGVIF # define IGMPMSG_WRONGVIF 2 # endif # ifndef IGMPMSG_WHOLEPKT # define IGMPMSG_WHOLEPKT 3 # endif #endif /* HOST_OS_LINUX */ #ifdef HAVE_LINUX_MROUTE6_H #include #endif /* * FreeBSD 4.3 * * On this platform, attempting to include both the ip_mroute.h * and ip6_mroute.h headers results in undefined behavior. * Therefore we implement a preprocessor workaround here. */ #ifdef HAVE_IPV6 /* Save GET_TIME. */ # ifdef GET_TIME # define _SAVE_GET_TIME GET_TIME # undef GET_TIME # endif # ifdef HAVE_NETINET6_IP6_MROUTE_H # include # endif /* Restore GET_TIME. */ # if defined(_SAVE_GET_TIME) && !defined(GET_TIME) # define GET_TIME _SAVE_GET_TIME # undef _SAVE_GET_TIME # endif #endif /* HAVE_IPV6 */ #endif /* __MRT_INCLUDE_IP_MROUTE_H__ */ xorp/mrt/max_vifs.h0000664000076400007640000000345611421137511014453 0ustar greearbgreearb/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- */ /* vim:set sts=4 ts=8: */ /* * Copyright (c) 2001-2009 XORP, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 2, June * 1991 as published by the Free Software Foundation. Redistribution * and/or modification of this program under the terms of any other * version of the GNU General Public License is not permitted. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, * see the GNU General Public License, Version 2, a copy of which can be * found in the XORP LICENSE.gpl file. * * XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; * http://xorp.net */ /* * $XORP: xorp/mrt/max_vifs.h,v 1.14 2008/10/02 21:57:45 bms Exp $ */ #ifndef __MRT_MAX_VIFS_H__ #define __MRT_MAX_VIFS_H__ /* * Header file to define the maximum number of multicast-capable vifs: * the constant MAX_VIFS. */ #include "libxorp/xorp.h" #include "mrt/include/ip_mroute.h" /* * Constants definitions */ /* * XXX: Define MAX_VIFS to be the largest of MAXVIFS, MAXMIFS, and 32 */ #ifndef MAX_VIFS # define MAX_VIFS 32 #elif (32 > MAX_VIFS) # undef MAX_VIFS # define MAX_VIFS 32 #endif #if defined(MAXVIFS) && (MAXVIFS > MAX_VIFS) # undef MAX_VIFS # define MAX_VIFS MAXVIFS #endif // MAXVIFS > MAX_VIFS #if defined(MAXMIFS) && (MAXMIFS > MAX_VIFS) # undef MAX_VIFS # define MAX_VIFS MAXMIFS #endif // MAXMIFS > MAX_VIFS /* * Structures, typedefs and macros */ /* * Global variables */ /* * Global functions prototypes */ __BEGIN_DECLS __END_DECLS #endif /* __MRT_MAX_VIFS_H__ */ xorp/mrt/buffer.c0000664000076400007640000000450711421137511014101 0ustar greearbgreearb/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- */ /* vim:set sts=4 ts=8: */ /* * Copyright (c) 2001-2009 XORP, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 2, June * 1991 as published by the Free Software Foundation. Redistribution * and/or modification of this program under the terms of any other * version of the GNU General Public License is not permitted. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, * see the GNU General Public License, Version 2, a copy of which can be * found in the XORP LICENSE.gpl file. * * XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; * http://xorp.net */ /* * Buffer management. */ #include "mrt_module.h" #include "libxorp/xorp.h" #include "buffer.h" /* * Exported variables */ /* * Local constants definitions */ /* * Local structures, typedefs and macros */ /* * Local variables */ /* * Local functions prototypes */ /** * buffer_malloc_utils_: * @buffer_size: The desired maximum size of the data stream. * * Allocate and initialize a #buffer_t structure. * * Return value: The allocated #buffer_t structure. **/ buffer_t * buffer_malloc_utils_(size_t buffer_size) { buffer_t *buffer; buffer = malloc(sizeof(*buffer)); buffer->_data = malloc(buffer_size); buffer->_buffer_size = buffer_size; BUFFER_RESET(buffer); return (buffer); } /** * buffer_free_utils_: * @buffer: The #buffer_t structure to free. * * Free a #buffer_t structure and all associated memory with it (including * the data stream). **/ void buffer_free_utils_(buffer_t *buffer) { if (buffer->_data != NULL) free(buffer->_data); free(buffer); } /** * buffer_reset_utils_: * @buffer: The #buffer_t structure to reset. * * Reset a #buffer_t structure (e.g., it will not contain any data). * * Return value: The reset #buffer_t structure. **/ buffer_t * buffer_reset_utils_(buffer_t *buffer) { buffer->_data_head = buffer->_data; buffer->_data_tail = buffer->_data; memset(buffer->_data, 0, buffer->_buffer_size); return (buffer); } xorp/BUGS0000664000076400007640000000031411421137511012335 0ustar greearbgreearb * Please report bugs using Trac: http://sourceforge.net/apps/trac/xorp/ * Please use the guidelines described in "How to Report Bugs Effectively" http://www.chiark.greenend.org.uk/~sgtatham/bugs.html xorp/libxipc/0000775000076400007640000000000011703345405013314 5ustar greearbgreearbxorp/libxipc/fp64serial.c0000664000076400007640000001363411634230152015441 0ustar greearbgreearb/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- * vim:set sts=4 ts=8: */ /* * Copyright (c) 2001-2011 XORP, Inc and Others * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License, Version * 2.1, June 1999 as published by the Free Software Foundation. * Redistribution and/or modification of this program under the terms of * any other version of the GNU Lesser General Public License is not * permitted. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, * see the GNU Lesser General Public License, Version 2.1, a copy of * which can be found in the XORP LICENSE.lgpl file. * * XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; * http://xorp.net */ #include #include #include #include #include "fp64serial.h" /* How big are the fields of IEEE754 binary64? */ #define MANTISSA_BIT 52 #define EXPONENT_BIT 11 #define SIGN_BIT 1 /* What's the offset for each field? */ #define MANTISSA_SHIFT 0 #define EXPONENT_SHIFT (MANTISSA_SHIFT + MANTISSA_BIT) #define SIGN_SHIFT (EXPONENT_SHIFT + EXPONENT_BIT) /* Compute masks for each field. */ #define MANTISSA_MASK ((UINTMAX_C(1) << MANTISSA_BIT) - 1u) #define EXPONENT_MASK ((UINTMAX_C(1) << EXPONENT_BIT) - 1u) #define SIGN_MASK ((UINTMAX_C(1) << SIGN_BIT) - 1u) /* How much is the exponent biased? */ #define EXPONENT_BIAS ((EXPONENT_MASK >> 1u) - 1u) #if __STDC_VERSION__ >= 199901L || \ _XOPEN_SOURCE >= 600 || \ _ISOC99_SOURCE || \ _POSIX_C_SOURCE >= 200112L /* fpclassify is available. */ #else /* We can't guarantee that fpclassify exists, so define a simple implementation that at least picks out zero. */ #undef FP_ZERO #undef FP_INFINITE #undef FP_NAN #undef FP_NORMAL #undef FP_SUBNORMAL #undef fpclassify #define FP_ZERO 0 #define FP_INFINITE 1 #define FP_NAN 2 #define FP_NORMAL 3 #define FP_SUBNORMAL 4 #define fpclassify(X) \ ((X) == 0.0 ? FP_ZERO : \ (X) >= +XORP_FP64UC(HUGE_VAL) ? FP_INFINITE : \ (X) <= -XORP_FP64UC(HUGE_VAL) ? FP_INFINITE : \ (X) != (X) ? FP_NAN : \ FP_NORMAL) #endif #if __STDC_VERSION__ >= 199901L || \ _XOPEN_SOURCE >= 600 || _ISOC99_SOURCE || _POSIX_C_SOURCE >= 200112L /* signbit is available. */ #else /* We can't guarantee that signbit exists. Define a simple implementation that probably won't do worse than fail to detect -ve zero. */ #undef signbit #define signbit(X) ((X) < 0) #endif uint_fast64_t fp64enc(fp64_t input) { fp64_t d_mant; unsigned int u_exp; uint_fast64_t u_mant; bool neg; int s_exp; uint_fast64_t bytes; switch (fpclassify(input)) { default: abort(); break; case FP_ZERO: neg = signbit(input); u_mant = 0; u_exp = 0; break; case FP_INFINITE: neg = signbit(input); u_mant = 0; u_exp = EXPONENT_MASK; break; case FP_NAN: neg = false; u_mant = 1; u_exp = EXPONENT_MASK; break; case FP_SUBNORMAL: case FP_NORMAL: /* Handle normal and subnormal together. The number might be one class for double, but another for binary64. */ /* Decompose the input into a significand (mantissa + 1) and an exponent. */ d_mant = XORP_FP64(frexp)(input, &s_exp); /* Extract the sign bit from the mantissa. */ neg = signbit(input); d_mant = XORP_FP64(fabs)(d_mant); /* Offset the exponent so it can be represented as an unsigned value. */ s_exp += EXPONENT_BIAS; /* Now we find out whether the number we represent is normal, subnormal, or overflows binary64. */ if (s_exp >= (long) EXPONENT_MASK) { /* The number is too big for binary64, so use the maximum value. */ u_mant = MANTISSA_MASK; u_exp = EXPONENT_MASK - 1u; } else if (s_exp <= 0) { /* The number is subnormal in binary64. */ /* Shift the mantissa so that it's exponent would be 0. */ u_mant = XORP_FP64(ldexp)(d_mant, MANTISSA_BIT); u_mant >>= -s_exp; u_exp = 0; } else { /* The number is normal in binary64. */ /* Use the suggested exponent. */ u_exp = s_exp; /* Make the mantissa value into a positive integer. */ u_mant = XORP_FP64(ldexp)(d_mant, MANTISSA_BIT + 1); } break; } /* Transmit the bottom MANTISSA_BITs of u_mant. The extra top bit will always be one when normalized. */ bytes = ((uint_fast64_t) u_mant & MANTISSA_MASK) << MANTISSA_SHIFT; bytes |= ((uint_fast64_t) u_exp & EXPONENT_MASK) << EXPONENT_SHIFT; bytes |= ((uint_fast64_t) neg & SIGN_MASK) << SIGN_SHIFT; return bytes; } fp64_t fp64dec(uint_fast64_t bytes) { int s_exp; unsigned int u_exp; uint_fast64_t u_mant; bool neg; fp64_t output; /* Extract the bit fields. */ u_exp = (bytes >> EXPONENT_SHIFT) & EXPONENT_MASK; u_mant = (bytes >> MANTISSA_SHIFT) & MANTISSA_MASK; neg = (bytes >> SIGN_SHIFT) & SIGN_MASK; if (u_exp == EXPONENT_MASK) { if (u_mant == 0) { #ifdef INFINITY return neg ? -INFINITY : +INFINITY; #else return neg ? -XORP_FP64UC(HUGE_VAL) : +XORP_FP64UC(HUGE_VAL); #endif } #ifdef NAN return NAN; #else return HUGE_VAL; #endif } do { if (u_exp == 0) { /* Positive or negative zero */ if (u_mant == 0) return XORP_FP64(copysign)(0.0, neg ? -1.0 : +1.0); /* Subnormal value */ /* Multiply the mantissa by a power of two. */ output = XORP_FP64(ldexp) (u_mant, -(MANTISSA_BIT + (int) EXPONENT_BIAS)); break; } /* Recover the top bit of the mantissa. */ u_mant |= MANTISSA_MASK + 1; /* Convert offset exponent back into a native signed value. */ s_exp = (int) u_exp - EXPONENT_BIAS; /* Multiply the mantissa by a power of two. */ output = XORP_FP64(ldexp) (u_mant, s_exp - (MANTISSA_BIT + 1)); } while (false); if (neg) output = -output; return output; } xorp/libxipc/xrl_tokens.hh0000664000076400007640000000345611421137511016027 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/libxipc/xrl_tokens.hh,v 1.12 2008/10/02 21:57:26 bms Exp $ #ifndef __LIBXIPC_XRL_TOKENS_HH__ #define __LIBXIPC_XRL_TOKENS_HH__ struct XrlToken { // Protocol - Target separator static const char* PROTO_TGT_SEP; // Target - Command separator static const char* TGT_CMD_SEP; // Command - Arguments separator static const char* CMD_ARGS_SEP; // Argument-Argument separator static const char* ARG_ARG_SEP; // Argument Name-Type separator static const char* ARG_NT_SEP; // Argument Type-Value separator static const char* ARG_TV_SEP; // Input Argument list - Output argument list separator static const char* ARG_RARG_SEP; // Line Continuation static const char* LINE_CONT; // XrlAtomList item separator static const char* LIST_SEP; }; #define TOKEN_BYTES(x) (strlen(x) + 1) #endif // __LIBXIPC_XRL_TOKENS_HH__ xorp/libxipc/xrl_atom.cc0000664000076400007640000010106311633743677015470 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "xrl_module.h" #include "libxorp/xorp.h" #include "libxorp/debug.h" #include "libxorp/c_format.hh" #include "libxorp/xlog.h" #include "libproto/packet.hh" #include "fp64serial.h" #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_SYS_SOCKET_H #include #endif #include "xrl_atom.hh" #include "xrl_atom_encoding.hh" #include "xrl_tokens.hh" // ---------------------------------------------------------------------------- // XrlAtomType-to-name mapping static const char* xrlatom_no_type_name = "none"; static const char* xrlatom_boolean_name = "bool"; static const char* xrlatom_int32_name = "i32"; static const char* xrlatom_uint32_name = "u32"; static const char* xrlatom_ipv4_name = "ipv4"; static const char* xrlatom_ipv4net_name = "ipv4net"; static const char* xrlatom_ipv6_name = "ipv6"; static const char* xrlatom_ipv6net_name = "ipv6net"; static const char* xrlatom_mac_name = "mac"; static const char* xrlatom_text_name = "txt"; static const char* xrlatom_list_name = "list"; static const char* xrlatom_binary_name = "binary"; static const char* xrlatom_int64_name = "i64"; static const char* xrlatom_uint64_name = "u64"; static const char* xrlatom_fp64_name = "fp64"; static inline void do_pack_uint32(const uint32_t u32val, uint8_t* buffer) { buffer[0] = (uint8_t)(u32val >> 24); buffer[1] = (uint8_t)(u32val >> 16); buffer[2] = (uint8_t)(u32val >> 8); buffer[3] = (uint8_t)(u32val); } static inline uint32_t do_unpack_uint32(const uint8_t* buf) { return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; } const char* xrlatom_type_name(const XrlAtomType& t) { switch (t) { #define NAME_CASE(x) case (x): return x##_name NAME_CASE(xrlatom_no_type); NAME_CASE(xrlatom_boolean); NAME_CASE(xrlatom_int32); NAME_CASE(xrlatom_uint32); NAME_CASE(xrlatom_ipv4); NAME_CASE(xrlatom_ipv4net); NAME_CASE(xrlatom_ipv6); NAME_CASE(xrlatom_ipv6net); NAME_CASE(xrlatom_mac); NAME_CASE(xrlatom_text); NAME_CASE(xrlatom_list); NAME_CASE(xrlatom_binary); NAME_CASE(xrlatom_int64); NAME_CASE(xrlatom_uint64); NAME_CASE(xrlatom_fp64); // ... Your type here ... } return xrlatom_no_type_name; } static XrlAtomType resolve_xrlatom_name(const char* name) { // This awkward construct ensures names of all atoms defined are mapped // since it generates a compile time error for missing enum values in // XrlAtomType. for (XrlAtomType t = xrlatom_start; t <= xrlatom_end; t = XrlAtomType(t + 1)) { switch (t) { #define CHECK_NAME(x) case (x) : if (strcmp(name, x##_name) == 0) return x; CHECK_NAME(xrlatom_int32); /* FALLTHRU */ CHECK_NAME(xrlatom_uint32); /* FALLTHRU */ CHECK_NAME(xrlatom_ipv4); /* FALLTHRU */ CHECK_NAME(xrlatom_ipv4net); /* FALLTHRU */ CHECK_NAME(xrlatom_ipv6); /* FALLTHRU */ CHECK_NAME(xrlatom_ipv6net); /* FALLTHRU */ CHECK_NAME(xrlatom_mac); /* FALLTHRU */ CHECK_NAME(xrlatom_text); /* FALLTHRU */ CHECK_NAME(xrlatom_list); /* FALLTHRU */ CHECK_NAME(xrlatom_boolean); /* FALLTHRU */ CHECK_NAME(xrlatom_binary); /* FALLTHRU */ CHECK_NAME(xrlatom_int64); /* FALLTHRU */ CHECK_NAME(xrlatom_uint64); /* FALLTHRU */ CHECK_NAME(xrlatom_fp64); /* FALLTHRU */ // ... Your type here ... case xrlatom_no_type: break; } break; } return xrlatom_no_type; } // ---------------------------------------------------------------------------- // ---------------------------------------------------------------------------- ssize_t XrlAtom::data_from_c_str(const char* c_str) { // Handle binary data type differently to avoid unnecessary copying. if (_type == xrlatom_binary) { _binary = new vector(); ssize_t bad_pos = xrlatom_decode_value(c_str, strlen(c_str), *_binary); if (bad_pos >= 0) { delete _binary; xorp_throw0(InvalidString); } _have_data = true; return -1; } string decoded; ssize_t bad_pos = xrlatom_decode_value(c_str, strlen(c_str), decoded); if (bad_pos >= 0) { xorp_throw0(InvalidString); } c_str = decoded.c_str(); _have_data = true; switch (_type) { case xrlatom_no_type: break; case xrlatom_boolean: _boolean = ('t' == c_str[0] || 'T' == c_str[0] || '1' == c_str[0]); break; case xrlatom_int32: _i32val = (int32_t)strtol(c_str, (char**)NULL, 10); break; case xrlatom_uint32: _u32val = (uint32_t)strtoul(c_str, (char**)NULL, 10); break; case xrlatom_ipv4: _ipv4 = IPv4(c_str); break; case xrlatom_ipv4net: { _ipv4net = IPv4Net(c_str); break; } case xrlatom_ipv6: _ipv6 = new IPv6(c_str); break; case xrlatom_ipv6net: _ipv6net = new IPv6Net(c_str); break; case xrlatom_mac: _mac = new Mac(c_str); break; case xrlatom_text: _text = new string(decoded); break; case xrlatom_list: _list = new XrlAtomList(c_str); break; case xrlatom_binary: abort(); // Binary is a special case and handled at start of routine break; case xrlatom_int64: #ifdef HOST_OS_WINDOWS #ifdef __MINGW32__ _i64val = (int64_t)strtoimax(c_str, (char**)NULL, 10); #else _i64val = (int64_t)_strtoi64(c_str, (char**)NULL, 10); #endif #else _i64val = (int64_t)strtoll(c_str, (char**)NULL, 10); #endif break; case xrlatom_uint64: #ifdef HOST_OS_WINDOWS #ifdef __MINGW32__ _u64val = (int64_t)strtoumax(c_str, (char**)NULL, 10); #else _u64val = (int64_t)_strtoui64(c_str, (char**)NULL, 10); #endif #else _u64val = (uint64_t)strtoull(c_str, (char**)NULL, 10); #endif break; case xrlatom_fp64: sscanf(c_str, "%" XORP_SCNgFP64, &_fp64val); break; // ... Your types instantiator here ... } return -1; } XrlAtom::~XrlAtom() { discard_dynamic(); } // ---------------------------------------------------------------------------- // XrlAtom accessor functions inline void XrlAtom::type_and_data_okay(const XrlAtomType& t) const throw (NoData, WrongType) { if (_type != t) xorp_throw(WrongType, t, _type); if (_have_data == false) xorp_throw(NoData, name()); } const bool& XrlAtom::boolean() const throw (NoData, WrongType) { type_and_data_okay(xrlatom_boolean); return _boolean; } const int32_t& XrlAtom::int32() const throw (NoData, WrongType) { type_and_data_okay(xrlatom_int32); return _i32val; } const uint32_t& XrlAtom::uint32() const throw (NoData, WrongType) { type_and_data_okay(xrlatom_uint32); return _u32val; } const IPv4& XrlAtom::ipv4() const throw (NoData, WrongType) { type_and_data_okay(xrlatom_ipv4); return _ipv4; } const IPv4Net& XrlAtom::ipv4net() const throw (NoData, WrongType) { type_and_data_okay(xrlatom_ipv4net); return _ipv4net; } const IPv6& XrlAtom::ipv6() const throw (NoData, WrongType) { type_and_data_okay(xrlatom_ipv6); return *_ipv6; } const IPv6Net& XrlAtom::ipv6net() const throw (NoData, WrongType) { type_and_data_okay(xrlatom_ipv6net); return *_ipv6net; } const IPvX XrlAtom::ipvx() const throw (NoData, WrongType) { if (_type == xrlatom_ipv4) { return ipv4(); } else { assert(_type == xrlatom_ipv6); return ipv6(); } } const IPvXNet XrlAtom::ipvxnet() const throw (NoData, WrongType) { if (_type == xrlatom_ipv4net) { return ipv4net(); } else { assert(_type == xrlatom_ipv6); return ipv6net(); } } const Mac& XrlAtom::mac() const throw (NoData, WrongType) { type_and_data_okay(xrlatom_mac); return *_mac; } const string& XrlAtom::text() const throw (NoData, WrongType) { type_and_data_okay(xrlatom_text); return *_text; } const XrlAtomList& XrlAtom::list() const throw (NoData, WrongType) { type_and_data_okay(xrlatom_list); return *_list; } const vector& XrlAtom::binary() const throw (NoData, WrongType) { type_and_data_okay(xrlatom_binary); return *_binary; } const int64_t& XrlAtom::int64() const throw (NoData, WrongType) { type_and_data_okay(xrlatom_int64); return _i64val; } const uint64_t& XrlAtom::uint64() const throw (NoData, WrongType) { type_and_data_okay(xrlatom_uint64); return _u64val; } const fp64_t& XrlAtom::fp64() const throw (NoData, WrongType) { type_and_data_okay(xrlatom_fp64); return _fp64val; } // ---------------------------------------------------------------------------- // XrlAtom dynamic data management functions void XrlAtom::copy(const XrlAtom& xa) { /* * We assume that xa._atom_name has been already checked for correctnes, * otherwise we should use set_name(xa._atom_name) here. */ _atom_name = xa._atom_name; _type = xa._type; _have_data = xa._have_data; _own = true; if (_have_data) { switch (_type) { case xrlatom_boolean: _boolean = xa._boolean; break; case xrlatom_int32: _i32val = xa._i32val; break; case xrlatom_uint32: _u32val = xa._u32val; break; case xrlatom_ipv4: _ipv4 = xa._ipv4; break; case xrlatom_ipv4net: _ipv4net = xa._ipv4net; break; case xrlatom_ipv6: _ipv6 = new IPv6(*xa._ipv6); break; case xrlatom_ipv6net: _ipv6net = new IPv6Net(*xa._ipv6net); break; case xrlatom_mac: _mac = new Mac(*xa._mac); break; case xrlatom_text: _text = new string(*xa._text); break; case xrlatom_list: _list = new XrlAtomList(*xa._list); break; case xrlatom_binary: _binary = new vector(*xa._binary); break; case xrlatom_int64: _i64val = xa._i64val; break; case xrlatom_uint64: _u64val = xa._u64val; break; case xrlatom_fp64: _fp64val = xa._fp64val; break; // ... Your type's copy operation here ... case xrlatom_no_type: break; } } } void XrlAtom::discard_dynamic() { if (_own && _have_data) { switch (_type) { case xrlatom_no_type: case xrlatom_boolean: case xrlatom_int32: case xrlatom_uint32: case xrlatom_ipv4net: case xrlatom_ipv4: break; case xrlatom_ipv6: delete _ipv6; _ipv6 = 0; break; case xrlatom_ipv6net: delete _ipv6net; _ipv6net = 0; break; case xrlatom_mac: delete _mac; _mac = 0; break; case xrlatom_text: delete _text; _text = 0; break; case xrlatom_list: delete _list; _list = 0; break; case xrlatom_binary: delete _binary; _binary = 0; break; case xrlatom_int64: case xrlatom_uint64: case xrlatom_fp64: break; // ... Your type should free allocated memory here ... } _have_data = false; } } // ---------------------------------------------------------------------------- // XrlAtom save / restore string representations // See devnotes/xrl-syntax.txt for more info. string XrlAtom::str() const { if (_have_data) { return c_format("%s%s%s%s%s", name().c_str(), XrlToken::ARG_NT_SEP, type_name(), XrlToken::ARG_TV_SEP, value().c_str()); } return c_format("%s%s%s", name().c_str(), XrlToken::ARG_NT_SEP, type_name()); } XrlAtom::XrlAtom(const char* serialized) throw (InvalidString, BadName) : _type(xrlatom_no_type), _have_data(false), _own(true) { const char *start, *sep; start = serialized; // Get Name sep = strstr(start, XrlToken::ARG_NT_SEP); if (0 == sep) { start = serialized; } else { set_name(string(start, sep - start)); start = sep + TOKEN_BYTES(XrlToken::ARG_NT_SEP) - 1; } // Get Type sep = strstr(start, XrlToken::ARG_TV_SEP); if (0 == sep) { _type = resolve_type_c_str(start); _have_data = false; if (_type == xrlatom_no_type) xorp_throw(InvalidString, c_format("xrlatom bad type: \"%s\"", start)); } else { _type = resolve_type_c_str(string(start, sep).c_str()); if (xrlatom_no_type == _type) xorp_throw(InvalidString, c_format("xrlatom bad type: \"%s\"", string(start, sep).c_str())); start = sep + TOKEN_BYTES(XrlToken::ARG_TV_SEP) - 1; // Get Data ssize_t bad_pos = data_from_c_str(start); if (bad_pos >= 0) xorp_throw0(InvalidString); } } XrlAtom::XrlAtom(const string& name, XrlAtomType t, const string& serialized_data) throw (InvalidString) : _type(t), _have_data(false), _own(true) { set_name(name); ssize_t bad_pos = data_from_c_str(serialized_data.c_str()); if (bad_pos >= 0) xorp_throw0(InvalidString); } XrlAtom::XrlAtom(const char* name, XrlAtomType t, const string& serialized_data) throw (InvalidString) : _type(t), _have_data(false), _own(true) { set_name(name); ssize_t bad_pos = data_from_c_str(serialized_data.c_str()); if (bad_pos >= 0) xorp_throw0(InvalidString); } const string XrlAtom::value() const { char tmp[32]; tmp[0] = '\0'; switch (_type) { case xrlatom_no_type: break; case xrlatom_boolean: snprintf(tmp, sizeof(tmp) / sizeof(tmp[0]), "%s", bool_c_str(_boolean)); return xrlatom_encode_value(tmp, strlen(tmp)); break; case xrlatom_int32: snprintf(tmp, sizeof(tmp) / sizeof(tmp[0]), "%d", XORP_INT_CAST(_i32val)); return xrlatom_encode_value(tmp, strlen(tmp)); break; case xrlatom_uint32: snprintf(tmp, sizeof(tmp) / sizeof(tmp[0]), "%u", XORP_UINT_CAST(_u32val)); return xrlatom_encode_value(tmp, strlen(tmp)); break; case xrlatom_ipv4: return xrlatom_encode_value(_ipv4.str()); case xrlatom_ipv4net: return xrlatom_encode_value(_ipv4net.str()); case xrlatom_ipv6: return xrlatom_encode_value(_ipv6->str()); case xrlatom_ipv6net: return xrlatom_encode_value(_ipv6net->str()); case xrlatom_mac: return xrlatom_encode_value(_mac->str()); case xrlatom_text: return xrlatom_encode_value(*_text); case xrlatom_list: return _list->str(); case xrlatom_binary: return xrlatom_encode_value(*_binary); case xrlatom_int64: #ifdef HOST_OS_WINDOWS snprintf(tmp, sizeof(tmp) / sizeof(tmp[0]), "%I64d", static_cast(_i64val)); #else snprintf(tmp, sizeof(tmp) / sizeof(tmp[0]), "%lld", static_cast(_i64val)); #endif return xrlatom_encode_value(tmp, strlen(tmp)); case xrlatom_uint64: #ifdef HOST_OS_WINDOWS snprintf(tmp, sizeof(tmp) / sizeof(tmp[0]), "%I64u", static_cast(_u64val)); #else snprintf(tmp, sizeof(tmp) / sizeof(tmp[0]), "%llu", static_cast(_u64val)); #endif return xrlatom_encode_value(tmp, strlen(tmp)); case xrlatom_fp64: snprintf(tmp, sizeof(tmp) / sizeof(tmp[0]), "%" XORP_PRIAFP64, _fp64val); return xrlatom_encode_value(tmp, strlen(tmp)); // ... Your type's c_str equivalent here ... } return tmp; } const char* XrlAtom::type_name() const { return xrlatom_type_name(_type); } XrlAtomType XrlAtom::resolve_type_c_str(const char *c_str) { return resolve_xrlatom_name(c_str); } // ---------------------------------------------------------------------------- // Misc Operators bool XrlAtom::operator==(const XrlAtom& other) const { bool mn = (name() == other.name()); bool mt = (_type == other._type); bool md = (_have_data == other._have_data); bool mv = true; if (_have_data && md) { switch (_type) { case xrlatom_no_type: mv = true; break; case xrlatom_boolean: mv = (_boolean == other._boolean); break; case xrlatom_int32: mv = (_i32val == other._i32val); break; case xrlatom_uint32: mv = (_u32val == other._u32val); break; case xrlatom_ipv4: mv = (_ipv4 == other._ipv4); break; case xrlatom_ipv4net: mv = (_ipv4net == other._ipv4net); break; case xrlatom_ipv6: mv = (*_ipv6 == *other._ipv6); break; case xrlatom_ipv6net: mv = (*_ipv6net == *other._ipv6net); break; case xrlatom_mac: mv = (*_mac == *other._mac); break; case xrlatom_text: mv = (*_text == *other._text); break; case xrlatom_list: mv = (*_list == *other._list); break; case xrlatom_binary: mv = (*_binary == *other._binary); break; case xrlatom_int64: mv = (_i64val == other._i64val); break; case xrlatom_uint64: mv = (_u64val == other._u64val); break; case xrlatom_fp64: mv = (_fp64val == other._fp64val); break; // ... Your type's equality test here ... } } // debug_msg("%d%d%d%d\n", mn, mt, md, mv); return ((mn == true) && (mt == true) && (md == true) && (mv == true)); } // ---------------------------------------------------------------------------- // Binary Packing // Packing header is 8-bits: // hdr[7:7] _have_name // hdr[6:6] _have_data // hdr[5:0] _type number #define NAME_PRESENT 0x80 #define DATA_PRESENT 0x40 // if _have_name then the name is packed as 16-bit unsigned number // indicating the length, then the ascii representation of the name // if _have_data then data follows. Data is packed in // network order where appropriate. The only "funny" data type is // xrlatom_text which we prefix with a 32-bit length field. size_t XrlAtom::packed_bytes() const { size_t bytes = 1; // Packing header space if (name().size() > 0) { bytes += 2 + name().size(); } if (!_have_data) { return bytes; } x_static_assert(sizeof(IPv4) == 4); x_static_assert(sizeof(IPv6) == 16); x_static_assert(sizeof(IPv4Net) == sizeof(IPv4) + 4); x_static_assert(sizeof(IPv6Net) == sizeof(IPv6) + 4); switch (_type) { case xrlatom_no_type: break; case xrlatom_boolean: bytes += 1; // A crime break; case xrlatom_int32: case xrlatom_uint32: case xrlatom_ipv4: bytes += 4; break; case xrlatom_ipv4net: bytes += 5; break; case xrlatom_ipv6: bytes += 16; break; case xrlatom_ipv6net: bytes += 17; break; case xrlatom_mac: bytes += 4; bytes += _mac->str().size(); break; case xrlatom_text: bytes += 4; bytes += _text->size(); break; case xrlatom_list: bytes += 4; for (size_t i = 0; i < _list->size(); i++) bytes += _list->get(i).packed_bytes(); break; case xrlatom_binary: assert(_binary != 0); bytes += 4 + _binary->size(); break; case xrlatom_int64: case xrlatom_uint64: case xrlatom_fp64: bytes += 8; break; // ... Your type sizing operation here ... } return bytes; } /** @return true if packed size of atom type is fixed, false if depends * packed size depends on data content (eg list, text, binary) */ inline bool XrlAtom::packed_bytes_fixed() const { switch (_type) { case xrlatom_no_type: case xrlatom_int32: case xrlatom_uint32: case xrlatom_ipv4: case xrlatom_ipv4net: case xrlatom_ipv6: case xrlatom_ipv6net: case xrlatom_boolean: case xrlatom_int64: case xrlatom_uint64: case xrlatom_fp64: return true; case xrlatom_mac: case xrlatom_text: case xrlatom_list: case xrlatom_binary: return false; } return false; } size_t XrlAtom::pack_name(uint8_t* buffer) const { assert(name().size() > 0 && name().size() < 65536); uint16_t sz = (uint16_t)name().size(); buffer[0] = sz >> 8; buffer[1] = sz & 0xff; memcpy(buffer + sizeof(sz), name().c_str(), name().size()); return sizeof(sz) + sz; } size_t XrlAtom::unpack_name(const uint8_t* buffer, size_t buffer_bytes) throw (BadName) { uint16_t sz; if (buffer_bytes < sizeof(sz)) { return 0; } sz = (buffer[0] << 8) | buffer[1]; if (buffer_bytes < sizeof(sz) + sz) { return 0; } const char* s = reinterpret_cast(buffer + sizeof(sz)); // if we're recycling the atom, just make sure the name is the same int name_size = _atom_name.size(); if (name_size) { if (name_size != sz) xorp_throw(BadName, s); if (::memcmp(_atom_name.c_str(), s, name_size) != 0) xorp_throw(BadName, s); } else { _atom_name.assign(s, sz); if (!valid_name(_atom_name)) xorp_throw(BadName, s); } return sizeof(sz) + sz; } size_t XrlAtom::pack_boolean(uint8_t* buffer) const { buffer[0] = (uint8_t)_boolean; return 1; } size_t XrlAtom::unpack_boolean(const uint8_t* buf) { _boolean = bool(buf[0]); return 1; } size_t XrlAtom::pack_uint32(uint8_t* buffer) const { do_pack_uint32(_u32val, buffer); return sizeof(_u32val); } size_t XrlAtom::unpack_uint32(const uint8_t* buf) { _u32val = do_unpack_uint32(buf); return sizeof(_u32val); } size_t XrlAtom::pack_ipv4(uint8_t* buffer) const { uint32_t u = _ipv4.addr(); memcpy(buffer, &u, sizeof(u)); return sizeof(u); } size_t XrlAtom::unpack_ipv4(const uint8_t* b) { _ipv4.copy_in(b); return 4; } size_t XrlAtom::pack_ipv4net(uint8_t* buffer) const { uint32_t ul = _ipv4net.masked_addr().addr(); memcpy(buffer, &ul, sizeof(ul)); buffer[sizeof(ul)] = _ipv4net.prefix_len(); return 5; } size_t XrlAtom::unpack_ipv4net(const uint8_t* b) { uint32_t a; memcpy(&a, b, sizeof(a)); _ipv4net.masked_addr_nc().set_addr(a); _ipv4net.set_prefix_len(b[4]); return 5; } size_t XrlAtom::pack_ipv6(uint8_t* buffer) const { const uint32_t* a = _ipv6->addr(); memcpy(buffer, a, 4 * sizeof(*a)); return 4 * sizeof(*a); } size_t XrlAtom::unpack_ipv6(const uint8_t* buffer) { uint32_t a[4]; if (_type == xrlatom_no_type) { memcpy(a, buffer, sizeof(a)); _ipv6 = new IPv6(a); } else _ipv6->copy_in(buffer); return sizeof(a); } size_t XrlAtom::pack_ipv6net(uint8_t* buffer) const { const uint32_t* a = _ipv6net->masked_addr().addr(); memcpy(buffer, a, 4 * sizeof(*a)); buffer[sizeof(IPv6)] = (uint8_t)_ipv6net->prefix_len(); return 4 * sizeof(*a) + sizeof(uint8_t); } size_t XrlAtom::unpack_ipv6net(const uint8_t* buffer) { uint32_t a[4]; memcpy(a, buffer, sizeof(a)); IPv6 v(a); if (_type == xrlatom_no_type) _ipv6net = new IPv6Net(v, buffer[sizeof(a)]); else *_ipv6net = IPv6Net(v, buffer[sizeof(a)]); return sizeof(a) + sizeof(uint8_t); } size_t XrlAtom::pack_mac(uint8_t* buffer) const { string ser = _mac->str(); uint32_t sz = ser.size(); uint32_t ul = htonl(sz); memcpy(buffer, &ul, sizeof(ul)); if (sz > 0) { memcpy(buffer + sizeof(ul), ser.c_str(), sz); } return sizeof(ul) + sz; } size_t XrlAtom::unpack_mac(const uint8_t* buffer, size_t buffer_bytes) { if (buffer_bytes < sizeof(uint32_t)) { return 0; } uint32_t len; memcpy(&len, buffer, sizeof(len)); len = ntohl(len); if (buffer_bytes < (len + sizeof(len))) { _mac = 0; return 0; } const char* text = reinterpret_cast(buffer + sizeof(len)); try { string s(text, len); if (_type == xrlatom_no_type) _mac = new Mac(s.c_str()); else _mac->copy_in(s.c_str()); } catch (const InvalidString&) { _mac = 0; return 0; } catch (...) { abort(); } return sizeof(len) + len; } size_t XrlAtom::pack_text(uint8_t* buffer) const { uint32_t sz = _text->size(); uint32_t ul = htonl(sz); memcpy(buffer, &ul, sizeof(ul)); if (sz > 0) { memcpy(buffer + sizeof(ul), _text->c_str(), sz); } return sizeof(ul) + sz; } size_t XrlAtom::unpack_text(const uint8_t* buffer, size_t buffer_bytes) { if (buffer_bytes < sizeof(uint32_t)) { return 0; } uint32_t len; memcpy(&len, buffer, sizeof(len)); len = ntohl(len); if (buffer_bytes < (len + sizeof(len))) { _text = 0; return 0; } const char *text = reinterpret_cast(buffer + sizeof(len)); if (_type == xrlatom_no_type) _text = new string(text, len); else _text->assign(text, len); return sizeof(len) + len; } size_t XrlAtom::peek_text(const char*& t, uint32_t& tl, const uint8_t* buf, size_t len) { // XrlAtom header if (len == 0) return 0; if (*buf != (xrlatom_text | DATA_PRESENT)) { return 0; } buf++; len--; // Text header if (len < sizeof(tl)) return 0; tl = extract_32(buf); buf += sizeof(tl); len -= sizeof(tl); if (len < tl) return 0; t = reinterpret_cast(buf); return 1 + sizeof(tl) + tl; } size_t XrlAtom::pack_list(uint8_t* buffer, size_t buffer_bytes) const { size_t done = 0; uint32_t nelem = htonl(_list->size()); x_static_assert(sizeof(nelem) == 4); memcpy(buffer, &nelem, sizeof(nelem)); done += sizeof(nelem); nelem = ntohl(nelem); for (size_t i = 0; i < nelem; i++) { done += _list->get(i).pack(buffer + done, buffer_bytes - done); assert(done <= buffer_bytes); } return done; } size_t XrlAtom::unpack_list(const uint8_t* buffer, size_t buffer_bytes) { size_t used = 0; uint32_t nelem; if (buffer_bytes < sizeof(nelem)) { return 0; } memcpy(&nelem, buffer, sizeof(nelem)); nelem = ntohl(nelem); used += sizeof(nelem); // check if we're overwriting if (_type == xrlatom_no_type) _list = new XrlAtomList; for (size_t i = 0; i < nelem; i++) { size_t unpacked = _list->modify(i, buffer + used, buffer_bytes - used); if (unpacked == 0) { // Failed to unpack item delete _list; _list = 0; return 0; } used += unpacked; assert(used <= buffer_bytes); } _list->set_size(nelem); return used; } inline size_t XrlAtom::pack_binary(uint8_t* buffer) const { uint32_t sz = _binary->size(); uint32_t ul = htonl(sz); memcpy(buffer, &ul, sizeof(ul)); if (sz > 0) { memcpy(buffer + sizeof(ul), &(_binary->operator[](0)), sz); } return sizeof(ul) + sz; } size_t XrlAtom::unpack_binary(const uint8_t* buffer, size_t buffer_bytes) { if (buffer_bytes < sizeof(uint32_t)) { return 0; } uint32_t len; memcpy(&len, buffer, sizeof(len)); len = ntohl(len); if (buffer_bytes < (len + sizeof(len))) { _binary = 0; return 0; } if (_type != xrlatom_no_type) delete _binary; _binary = new vector(buffer + sizeof(len), buffer + sizeof(len) + len); return sizeof(len) + len; } size_t XrlAtom::pack_uint64(uint8_t* buffer) const { do_pack_uint32(_u64val >> 32, buffer); do_pack_uint32(_u64val & 0xFFFFFFFF, &buffer[4]); return sizeof(_u64val); } size_t XrlAtom::unpack_uint64(const uint8_t* buf) { _u64val = do_unpack_uint32(buf); _u64val <<= 32; _u64val |= do_unpack_uint32(&buf[4]); return sizeof(_u64val); } size_t XrlAtom::pack_fp64(uint8_t* buffer) const { uint_fast64_t bytes = fp64enc(_fp64val); do_pack_uint32(bytes >> 32, buffer); do_pack_uint32(bytes & 0xFFFFFFFF, &buffer[4]); return sizeof(_fp64val); } size_t XrlAtom::unpack_fp64(const uint8_t* buf) { uint_fast64_t bytes; bytes = uint_fast64_t(do_unpack_uint32(buf)) << 32; bytes |= do_unpack_uint32(&buf[4]); _fp64val = fp64dec(bytes); return sizeof(_fp64val); } size_t XrlAtom::pack(uint8_t* buffer, size_t buffer_bytes) const { size_t pb = packed_bytes(); if (buffer_bytes < pb) { debug_msg("Buffer too small (%u < %u)\n", XORP_UINT_CAST(buffer_bytes), XORP_UINT_CAST(pb)); return 0; } uint8_t& header = *buffer; header = _type; size_t packed_size = 1; if (name().size()) { header |= NAME_PRESENT; packed_size += pack_name(buffer + packed_size); } if (_have_data) { header |= DATA_PRESENT; switch (_type) { case xrlatom_no_type: abort(); case xrlatom_boolean: packed_size += pack_boolean(buffer + packed_size); break; case xrlatom_int32: case xrlatom_uint32: packed_size += pack_uint32(buffer + packed_size); break; case xrlatom_ipv4: packed_size += pack_ipv4(buffer + packed_size); break; case xrlatom_ipv4net: packed_size += pack_ipv4net(buffer + packed_size); break; case xrlatom_ipv6: packed_size += pack_ipv6(buffer + packed_size); break; case xrlatom_ipv6net: packed_size += pack_ipv6net(buffer + packed_size); break; case xrlatom_mac: packed_size += pack_mac(buffer + packed_size); break; case xrlatom_text: packed_size += pack_text(buffer + packed_size); break; case xrlatom_list: packed_size += pack_list(buffer + packed_size, buffer_bytes - packed_size); break; case xrlatom_binary: packed_size += pack_binary(buffer + packed_size); break; case xrlatom_int64: case xrlatom_uint64: packed_size += pack_uint64(buffer + packed_size); break; case xrlatom_fp64: packed_size += pack_fp64(buffer + packed_size); break; // ... Your type here ... } } return packed_size; } size_t XrlAtom::unpack(const uint8_t* buffer, size_t buffer_bytes) { if (buffer_bytes == 0) { debug_msg("Shoot! Passed 0 length buffer for unpacking\n"); return 0; } const uint8_t& header = *buffer; size_t unpacked = 1; if (header & NAME_PRESENT) { try { size_t used = unpack_name(buffer + unpacked, buffer_bytes - unpacked); if (used == 0) { debug_msg("Invalid name\n"); return 0; } unpacked += used; } catch (const XrlAtom::BadName& bn) { debug_msg("Unpacking failed:\n%s\n", bn.str().c_str()); return 0; } } else _atom_name.clear(); if (header & DATA_PRESENT) { int t = header & ~(NAME_PRESENT | DATA_PRESENT); if (t < xrlatom_start || t > xrlatom_end) { debug_msg("Type %d invalid\n", t); } XrlAtomType old_type = _type; XrlAtomType type = _type = XrlAtomType(t); _have_data = true; debug_msg("Unpacked %u remain %u\n", XORP_UINT_CAST(unpacked), XORP_UINT_CAST(buffer_bytes)); // Check size for fixed width packed types. if (packed_bytes_fixed() && buffer_bytes < packed_bytes()) { debug_msg("Insufficient space (%u < %u) for type %d\n", XORP_UINT_CAST(buffer_bytes), XORP_UINT_CAST(packed_bytes()), _type); _have_data = false; _type = old_type; return 0; } _type = old_type; // unpack size_t used = 0; switch (type) { case xrlatom_no_type: _type = old_type; return 0; case xrlatom_boolean: used = unpack_boolean(buffer + unpacked); break; case xrlatom_int32: case xrlatom_uint32: used = unpack_uint32(buffer + unpacked); break; case xrlatom_ipv4: used = unpack_ipv4(buffer + unpacked); break; case xrlatom_ipv4net: used = unpack_ipv4net(buffer + unpacked); break; case xrlatom_ipv6: used = unpack_ipv6(buffer + unpacked); break; case xrlatom_ipv6net: used = unpack_ipv6net(buffer + unpacked); break; case xrlatom_mac: used = unpack_mac(buffer + unpacked, buffer_bytes - unpacked); break; case xrlatom_text: used = unpack_text(buffer + unpacked, buffer_bytes - unpacked); break; case xrlatom_list: used = unpack_list(buffer + unpacked, buffer_bytes - unpacked); break; case xrlatom_binary: used = unpack_binary(buffer + unpacked, buffer_bytes - unpacked); break; case xrlatom_int64: case xrlatom_uint64: used = unpack_uint64(buffer + unpacked); break; case xrlatom_fp64: used = unpack_fp64(buffer + unpacked); break; // ... Your type here ... } _type = type; if (used == 0) { _type = xrlatom_no_type; debug_msg("Blow! Data unpacking failed\n"); _have_data = 0; return 0; } unpacked += used; assert(unpacked == packed_bytes()); } return unpacked; } void XrlAtom::set_name(const char *name) throw (BadName) { if (name == 0) _atom_name = ""; else { _atom_name = name; if (!valid_name(_atom_name)) xorp_throw(BadName, name); } } bool XrlAtom::valid_name(const string& s) { string::const_iterator si; for (si = s.begin(); si != s.end(); ++si) { if (!xorp_isalnum(*si) && *si != '_' && *si != '-') return false; } return true; } bool XrlAtom::valid_type(const string& s) { XrlAtomType t = resolve_xrlatom_name(s.c_str()); return (t != xrlatom_no_type); } void XrlAtom::abandon_data() { bool have_data = _have_data; discard_dynamic(); _own = false; _have_data = have_data; } xorp/libxipc/xrl_atom_encoding.hh0000664000076400007640000000537111540224226017332 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/libxipc/xrl_atom_encoding.hh,v 1.14 2008/10/02 21:57:24 bms Exp $ #ifndef __LIBXIPC_XRL_ATOM_ENCODING_HH__ #define __LIBXIPC_XRL_ATOM_ENCODING_HH__ /** * Encode the string representation of an XrlAtom value into a value * suitable for integrating into a spaceless Xrl representation. This * is essentially URL encoding though a reduced subset of * non-alphanumeric characters are escaped, ie only those that would * otherwise interfere with Xrl parsing. */ string xrlatom_encode_value(const char* val, size_t val_bytes); /** * Encode the string representation of an XrlAtom value into a value * suitable for integrating into a spaceless Xrl representation. This * is essentially URL encoding though a reduced subset of * non-alphanumeric characters are escaped, ie only those that would * otherwise interfere with Xrl parsing. */ inline string xrlatom_encode_value(const string& val) { return xrlatom_encode_value(val.c_str(), val.size()); } /** * Encode string representation of a binary data type XrlAtom value into * a value suitable for integrating into a spaceless Xrl representation. */ inline string xrlatom_encode_value(const vector& v) { const uint8_t* start = &v[0]; return xrlatom_encode_value(reinterpret_cast(start), v.size()); } /** * Decode escaped XrlAtom representation. * * @return -1 on success, or the index of the character causing the * decode failure in the string "in". */ ssize_t xrlatom_decode_value(const char* in, size_t in_bytes, string& out); /** * Decode escaped XrlAtom representation of XrlAtom binary data type. * * @return -1 on success, or the index of the character causing the * decode failure in the string "in". */ ssize_t xrlatom_decode_value(const char* in, size_t in_bytes, vector& out); #endif // __LIBXIPC_XRL_ATOM_ENCODING_HH__ xorp/libxipc/finder_messenger.hh0000664000076400007640000001127411546451416017166 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/libxipc/finder_messenger.hh,v 1.22 2008/10/02 21:57:20 bms Exp $ #ifndef __LIBXIPC_FINDER_MESSENGER_HH__ #define __LIBXIPC_FINDER_MESSENGER_HH__ #include "libxorp/eventloop.hh" #include "xrl_cmd_map.hh" #include "xrl_sender.hh" class FinderMessengerBase; /** * Base class for classes managing descendents of FinderMessengerBase. */ class FinderMessengerManager { public: /** * Empty virtual destructor. */ virtual ~FinderMessengerManager() {} /** * Method called by messenger constructor. */ virtual void messenger_birth_event(FinderMessengerBase*) = 0; /** * Method called by messenger destructor. */ virtual void messenger_death_event(FinderMessengerBase*) = 0; /** * Method called before Xrl is dispatched. */ virtual void messenger_active_event(FinderMessengerBase*) = 0; /** * Method called immediately after Xrl is dispatched. */ virtual void messenger_inactive_event(FinderMessengerBase*) = 0; /** * Method called when Messenger is unable to continue. For instance, * network connection lost. */ virtual void messenger_stopped_event(FinderMessengerBase*) = 0; /** * Method called to tell if FinderMessengerManager instance manages * a particular messenger. */ virtual bool manages(const FinderMessengerBase*) const = 0; }; /** * @short Base class for FinderMessenger classes. * * FinderMessenger classes are expected to handle the transport and * dispatch of Xrl's and their responses. This base class provides a * common code for actually doing the Xrl dispatch and handling the * state associated with their responses. */ class FinderMessengerBase : public XrlSender { public: typedef XrlSender::Callback SendCallback; public: FinderMessengerBase(EventLoop& e, FinderMessengerManager* fmm, XrlCmdMap& cmds); virtual ~FinderMessengerBase(); virtual bool send(const Xrl& xrl, const SendCallback& scb) = 0; virtual bool pending() const = 0; XrlCmdMap& command_map(); EventLoop& eventloop(); void unhook_manager(); FinderMessengerManager* manager(); protected: /** * Find command associated with Xrl and dispatch it. */ void dispatch_xrl(uint32_t seqno, const Xrl& x); bool dispatch_xrl_response(uint32_t seqno, const XrlError& e, XrlArgs*); bool store_xrl_response(uint32_t seqno, const SendCallback& scb); virtual void reply(uint32_t seqno, const XrlError& e, const XrlArgs* reply_args) = 0; void response_timeout(uint32_t seqno); private: void dispatch_xrl_cb(const XrlCmdError &e, const XrlArgs *reply_args, uint32_t seqno); class ResponseState { public: ResponseState(uint32_t seqno, const SendCallback& cb, FinderMessengerBase* fmb) : scb(cb) { expiry = fmb->eventloop().new_oneoff_after_ms(RESPONSE_TIMEOUT_MS, callback(fmb, &FinderMessengerBase::response_timeout, seqno)); } #ifdef XORP_USE_USTL ResponseState() { } #endif SendCallback scb; XorpTimer expiry; static const uint32_t RESPONSE_TIMEOUT_MS = 30000; }; typedef map SeqNoResponseMap; friend class ResponseState; private: EventLoop& _eventloop; FinderMessengerManager* _manager; SeqNoResponseMap _expected_responses; XrlCmdMap& _cmds; }; /////////////////////////////////////////////////////////////////////////////// // // Inline methods inline XrlCmdMap& FinderMessengerBase::command_map() { return _cmds; } inline EventLoop& FinderMessengerBase::eventloop() { return _eventloop; } inline FinderMessengerManager* FinderMessengerBase::manager() { return _manager; } #endif // __LIBXIPC_FINDER_MESSENGER_HH__ xorp/libxipc/finder_xrl_target.hh0000664000076400007640000001014411540225526017337 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/libxipc/finder_xrl_target.hh,v 1.20 2008/10/02 21:57:21 bms Exp $ #ifndef __LIBXIPC_FINDER_XRL_TARGET_HH__ #define __LIBXIPC_FINDER_XRL_TARGET_HH__ #include "xrl/targets/finder_base.hh" class Finder; class FinderXrlTarget : public XrlFinderTargetBase { public: FinderXrlTarget(Finder& finder); /** * Get name of Xrl Target */ XrlCmdError common_0_1_get_target_name(string& name); /** * Get version string from Xrl Target */ XrlCmdError common_0_1_get_version(string& version); /** * Get status of Xrl Target */ XrlCmdError common_0_1_get_status(uint32_t& status, string& reason); /** * Request Xrl Target to shutdown */ XrlCmdError common_0_1_shutdown(); XrlCmdError common_0_1_startup() { return XrlCmdError::OKAY(); } /** * Fails if target_name is already registered. The target_name must * support the finder_client interface in order to be able to process * messages from the finder. */ XrlCmdError finder_0_2_register_finder_client(const string& target_name, const string& class_name, const bool& singleton, const string& in_cookie, string& out_cookie); XrlCmdError finder_0_2_unregister_finder_client(const string& target_name); XrlCmdError finder_0_2_set_finder_client_enabled(const string& target_name, const bool& en); XrlCmdError finder_0_2_finder_client_enabled(const string& target_name, bool& en); /** * Add resolved Xrl into system, fails if xrl is already registered. */ XrlCmdError finder_0_2_add_xrl(const string& xrl, const string& protocol_name, const string& protocol_args, string& resolved_xrl_method_name); /** * Remove xrl */ XrlCmdError finder_0_2_remove_xrl(const string& xrl); /** * Resolve Xrl */ XrlCmdError finder_0_2_resolve_xrl(const string& xrl, XrlAtomList& resolutions); /** * Get list of registered Xrl targets */ XrlCmdError finder_0_2_get_xrl_targets(XrlAtomList& target_names); /** * Get list of Xrls registered by target */ XrlCmdError finder_0_2_get_xrls_registered_by(const string& target_name, XrlAtomList& xrls); XrlCmdError finder_0_2_get_ipv4_permitted_hosts(XrlAtomList& ipv4s); XrlCmdError finder_0_2_get_ipv4_permitted_nets(XrlAtomList& ipv4nets); XrlCmdError finder_0_2_get_ipv6_permitted_hosts(XrlAtomList& ipv6s); XrlCmdError finder_0_2_get_ipv6_permitted_nets(XrlAtomList& ipv6nets); /** * Event notifier interface. */ XrlCmdError finder_event_notifier_0_1_register_class_event_interest( const string& who, const string& class_name); XrlCmdError finder_event_notifier_0_1_deregister_class_event_interest( const string& who, const string& class_name); XrlCmdError finder_event_notifier_0_1_register_instance_event_interest( const string& who, const string& instance_name); XrlCmdError finder_event_notifier_0_1_deregister_instance_event_interest( const string& who, const string& instance_name); protected: Finder& _finder; }; #endif // __LIBXIPC_FINDER_XRL_TARGET_HH__ xorp/libxipc/finder_xrl_queue.cc0000664000076400007640000000514211540224226017161 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "finder_module.h" #include "libxorp/debug.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "finder_messenger.hh" #include "finder_xrl_queue.hh" FinderXrlCommandQueue::FinderXrlCommandQueue(FinderMessengerBase* messenger) : _m(messenger), _pending(false) { } FinderXrlCommandQueue::FinderXrlCommandQueue(const FinderXrlCommandQueue& oq) : _m(oq._m), _pending(oq._pending) { XLOG_ASSERT(oq._cmds.empty()); XLOG_ASSERT(oq._pending == false); } FinderXrlCommandQueue::~FinderXrlCommandQueue() { } FinderXrlCommandQueue& FinderXrlCommandQueue::operator=(const FinderXrlCommandQueue& rhs) { if (&rhs != this) { _m = rhs._m; _cmds = rhs._cmds; _pending = rhs._pending; _dispatcher = rhs._dispatcher; } return *this; } inline EventLoop& FinderXrlCommandQueue::eventloop() { return _m->eventloop(); } inline void FinderXrlCommandQueue::push() { debug_msg("push\n"); if (false == _pending && _cmds.empty() == false&& _dispatcher.scheduled() == false) { _dispatcher = eventloop().new_oneoff_after_ms(0, callback(this, &FinderXrlCommandQueue::dispatch_one)); } } void FinderXrlCommandQueue::dispatch_one() { debug_msg("dispatch_one\n"); XLOG_ASSERT(_cmds.empty() == false); _cmds.front()->dispatch(); _pending = true; } void FinderXrlCommandQueue::enqueue(const FinderXrlCommandQueue::Command& cmd) { debug_msg("enqueue\n"); _cmds.push_back(cmd); push(); } void FinderXrlCommandQueue::crank() { debug_msg("crank\n"); XLOG_ASSERT(_pending == true); _cmds.pop_front(); _pending = false; push(); } void FinderXrlCommandQueue::kill_messenger() { debug_msg("killing messenger\n"); delete _m; } xorp/libxipc/finder_xrl_queue.hh0000664000076400007640000001316611540224226017200 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/libxipc/finder_xrl_queue.hh,v 1.17 2008/10/02 21:57:21 bms Exp $ #ifndef __LIBXIPC_FINDER_XRL_QUEUE_HH__ #define __LIBXIPC_FINDER_XRL_QUEUE_HH__ #include "libxorp/ref_ptr.hh" class FinderXrlCommandBase; /** * @short Xrl Queue for Finder. * * The FinderXrlCommandQueue holds and dispatches Xrls for the Finder. * Commands are added with the @ref FinderXrlCommandQueue::enqueue * method and are serially dispatched without any additional * intervention. During the completion of each Xrl in the queue triggers * the sending of the next Xrl. */ class FinderXrlCommandQueue { public: typedef ref_ptr Command; public: FinderXrlCommandQueue(FinderMessengerBase* messenger); FinderXrlCommandQueue(const FinderXrlCommandQueue& oq); FinderXrlCommandQueue() { _m = NULL; } ~FinderXrlCommandQueue(); FinderMessengerBase& messenger() { return *_m; } void enqueue(const Command& cmd); #ifndef XORP_USE_USTL private: #endif FinderXrlCommandQueue& operator=(const FinderXrlCommandQueue&); protected: void push(); void dispatch_one(); EventLoop& eventloop(); protected: friend class FinderXrlCommandBase; void crank(); void kill_messenger(); private: FinderMessengerBase* _m; list _cmds; bool _pending; XorpTimer _dispatcher; }; /** * @short Base class for Xrls sent from Finder. */ class FinderXrlCommandBase { public: FinderXrlCommandBase(FinderXrlCommandQueue& q) : _queue(q) {} virtual ~FinderXrlCommandBase() {} FinderXrlCommandQueue& queue() { return _queue; } FinderMessengerBase& messenger() { return _queue.messenger(); } virtual bool dispatch() = 0; void dispatch_cb(const XrlError& e) { if (e != XrlCmdError::OKAY()) { XLOG_ERROR("Sent xrl got response %s\n", e.str().c_str()); queue().kill_messenger(); return ; } queue().crank(); } protected: FinderXrlCommandQueue& _queue; }; #include "xrl/interfaces/finder_client_xif.hh" /** * @short Send "hello" Xrl to Client. */ class FinderSendHelloToClient : public FinderXrlCommandBase { public: FinderSendHelloToClient(FinderXrlCommandQueue& q, const string& tgtname) : FinderXrlCommandBase(q), _tgtname(tgtname) { } bool dispatch() { XrlFinderClientV0p2Client client(&(queue().messenger())); return client.send_hello(_tgtname.c_str(), callback(static_cast(this), &FinderXrlCommandBase::dispatch_cb)); } protected: string _tgtname; }; /** * @short Send "remove xrl" to client. */ class FinderSendRemoveXrl : public FinderXrlCommandBase { public: FinderSendRemoveXrl(FinderXrlCommandQueue& q, const string& tgtname, const string& xrl) : FinderXrlCommandBase(q), _tgtname(tgtname), _xrl(xrl) { } ~FinderSendRemoveXrl() { _tgtname = _xrl = "croak"; } bool dispatch() { XrlFinderClientV0p2Client client(&(queue().messenger())); return client.send_remove_xrl_from_cache(_tgtname.c_str(), _xrl, callback(static_cast(this), &FinderXrlCommandBase::dispatch_cb)); } protected: string _tgtname; string _xrl; }; /** * @short Send "remove xrls for target" to client. */ class FinderSendRemoveXrls : public FinderXrlCommandBase { public: FinderSendRemoveXrls(FinderXrlCommandQueue& q, const string& tgtname) : FinderXrlCommandBase(q), _tgtname(tgtname) { } ~FinderSendRemoveXrls() { _tgtname = "croak"; } bool dispatch() { XrlFinderClientV0p2Client client(&(queue().messenger())); return client.send_remove_xrls_for_target_from_cache( _tgtname.c_str(), _tgtname, callback(static_cast(this), &FinderXrlCommandBase::dispatch_cb)); } protected: string _tgtname; }; /** * @short Send tunneled Xrl to client. Client is expected to be * able to dispatch Xrl. */ class FinderSendTunneledXrl : public FinderXrlCommandBase { public: FinderSendTunneledXrl(FinderXrlCommandQueue& q, const string& tgtname, const string& xrl) : FinderXrlCommandBase(q), _tgtname(tgtname), _xrl(xrl) { } ~FinderSendTunneledXrl() { _tgtname = "croak"; } void dispatch_cb(const XrlError& e, const uint32_t* /* p_errno */, const string* /* p_errtxt */) { // if (e != XrlError::OKAY()) FinderXrlCommandBase::dispatch_cb(e); // TODO: XXX } bool dispatch() { XrlFinderClientV0p2Client client(&(queue().messenger())); return client.send_dispatch_tunneled_xrl(_tgtname.c_str(), _xrl, callback(this, &FinderSendTunneledXrl::dispatch_cb)); } protected: string _tgtname; string _xrl; }; #endif // __LIBXIPC_FINDER_XRL_QUEUE_HH__ xorp/libxipc/ipc_module.h0000664000076400007640000000241011421137511015574 0ustar greearbgreearb/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- */ /* vim:set sts=4 ts=8: */ /* * Copyright (c) 2001-2009 XORP, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License, Version * 2.1, June 1999 as published by the Free Software Foundation. * Redistribution and/or modification of this program under the terms of * any other version of the GNU Lesser General Public License is not * permitted. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, * see the GNU Lesser General Public License, Version 2.1, a copy of * which can be found in the XORP LICENSE.lgpl file. * * XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; * http://xorp.net */ /* * $XORP: xorp/libxipc/ipc_module.h,v 1.11 2008/10/02 21:57:21 bms Exp $ */ /* * Module definitions. */ #ifndef __LIBXIPC_IPC_MODULE_H__ #define __LIBXIPC_IPC_MODULE_H__ #ifndef XORP_MODULE_NAME #define XORP_MODULE_NAME "IPC" #endif #ifndef XORP_MODULE_VERSION #define XORP_MODULE_VERSION "0.1" #endif #endif /* __LIBXIPC_IPC_MODULE_H__ */ xorp/libxipc/xrl_parser.hh0000664000076400007640000000707211540224226016020 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/libxipc/xrl_parser.hh,v 1.12 2008/10/02 21:57:24 bms Exp $ #ifndef __LIBXIPC_XRL_PARSER_HH__ #define __LIBXIPC_XRL_PARSER_HH__ #include #include "xrl.hh" #include "xrl_parser_input.hh" class XrlParseError { public: XrlParseError(const string& input, ssize_t offset, const string& reason) : _input(input), _offset(offset), _reason(reason) {} XrlParseError(const string& input, string::const_iterator pos, const string& reason) : _input(input), _offset(pos - input.begin()), _reason(reason) {} XrlParseError() : _input(""), _offset(0xffffffff), _reason("") {} virtual ~XrlParseError() {} const string& input() const { return _input; } ssize_t offset() const { return _offset; } const string& reason() const { return _reason; } string pretty_print(size_t termwidth = 80u) const; protected: const string _input; size_t _offset; string _reason; void get_coordinates(size_t& lineno, size_t& charno) const; }; // XrlLocator - locates sub-strings that look like spaceless and // presentation Xrl's. It does weak examination of syntax and leaves // finer grained examination to the Xrl instantiation routines. class XrlParser { public: XrlParser(XrlParserInput& xpi) : _xpi(xpi) {} virtual ~XrlParser() {} /** Starts new parsing cycle. * @return true upon success, false if there is no more data */ bool start_next() throw (XrlParserInputException); /** Check if input is exhausted. * @return true if input is exhausted, false otherwise. */ bool finished() const { return _xpi.eof(); } bool get(string& protocol, string& target, string& command, XrlArgs& args) throw (XrlParseError); bool get(string& protocol, string& target, string& command, XrlArgs& args, list& spells) throw (XrlParseError); bool get(string& xrl_c_str) throw (XrlParseError); bool get_return_specs(list& spells); const string& input() const { return _input; } /** * Attempt to find a new XRL starting point after an error has * occurred. * * @return true if text resembling an XRL start is found. */ bool resync(); const XrlParserInput& parser_input() const; protected: bool get(string& protocol, string& target, string& command, XrlArgs* args, list* spells) throw (XrlParseError); bool parse_atoms_and_spells(XrlArgs* args, list* spells); XrlParserInput& _xpi; string _input; string::const_iterator _pos; }; #endif // __LIBXIPC_XRL_PARSER_HH__ xorp/libxipc/TODO0000664000076400007640000000150011421137511013772 0ustar greearbgreearb Any of these items are up for grabs. Before embarking on them send a note to the developers list to check they are not covered by work in progress. * Add support for pipelining in XRL protocol families. * Add support for handling one-to-many resolutions at client end, ie implement a standard policy mechanism or have hooks for generic policies. * Rework the XRL parser related routines to make them more akin to the parsing capabilities of the python scripts used to generate XRL interfaces. * Copy minimization in existing protocol families. There are a few unnecessary copies that should be eliminated. This may be straightforward. We might also want to consider using reference counted XrlAtoms. * Implement additional protocol families (signals, UNIX domain sockets, XXX Your IPC mechanism here XXX). xorp/libxipc/finder.cc0000664000076400007640000007107311605170012015071 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "finder_module.h" #include "libxorp/debug.h" #include "libxorp/xlog.h" #include "finder_tcp.hh" #include "finder.hh" #include "finder_xrl_queue.hh" /////////////////////////////////////////////////////////////////////////////// // // FinderTarget // // This class is a container for values associated with a particular // target. It contains a one-to-many mapping for values associated with // the target, eg unresolved xrl to resolved xrls, name-to-values, etc... // class FinderTarget { public: typedef Finder::Resolveables Resolveables; typedef map ResolveMap; public: FinderTarget(const string& name, const string& class_name, const string& cookie, FinderMessengerBase* fm) : _name(name), _class_name(class_name), _cookie(cookie), _enabled(false), _messenger(fm) {} FinderTarget() { _messenger = NULL; } ~FinderTarget() { debug_msg("Destructing %s\n", name().c_str()); } const string& name() const { return _name; } const string& class_name() const { return _class_name; } const string& cookie() const { return _cookie; } bool enabled() const { return _enabled; } void set_enabled(bool en) { _enabled = en; } const FinderMessengerBase* messenger() const { return _messenger; } FinderMessengerBase* messenger() { return _messenger; } const ResolveMap& resolve_map() const { return _resolutions; } bool add_resolution(const string& key, const string& value) { Resolveables& r = _resolutions[key]; if (find(r.begin(), r.end(), value) == r.end()) r.push_back(value); return true; } bool remove_resolutions(const string& key) { ResolveMap::iterator i = _resolutions.find(key); if (_resolutions.end() != i) { _resolutions.erase(i); return true; } return false; } const Resolveables* resolveables(const string& key) const { ResolveMap::const_iterator i = _resolutions.find(key); if (_resolutions.end() == i) { debug_msg("Looking for \"%s\"\n", key.c_str()); for (i = _resolutions.begin(); i != _resolutions.end(); ++i) { debug_msg("Have \"%s\"\n", i->first.c_str()); } return NULL; } return &i->second; } bool add_class_watch(const string& class_name) { pair::iterator,bool> r = _classwatches.insert(class_name); return r.second; } void remove_class_watch(const string& class_name) { set::iterator i = _classwatches.find(class_name); if (i != _classwatches.end()) { _classwatches.erase(i); } } bool has_class_watch(const string& class_name) const { #ifdef DEBUG_LOGGING debug_msg("tgt %s has watches on:\n", _name.c_str()); set::const_iterator wi = _classwatches.begin(); while (wi != _classwatches.end()) { debug_msg("-> %s\n", wi->c_str()); ++wi; } #endif return _classwatches.find(class_name) != _classwatches.end(); } bool add_instance_watch(const string& instance_name) { pair::iterator,bool> r = _instancewatches.insert(instance_name); return r.second; } void remove_instance_watch(const string& instance_name) { set::iterator i = _instancewatches.find(instance_name); if (i != _instancewatches.end()) { _instancewatches.erase(i); } } bool has_instance_watch(const string& instance_name) const { #ifdef DEBUG_LOGGING debug_msg("tgt %s has watches on:\n", _name.c_str()); set::const_iterator wi = _instancewatches.begin(); while (wi != _instancewatches.end()) { debug_msg("-> %s\n", wi->c_str()); ++wi; } #endif return _instancewatches.find(instance_name) != _instancewatches.end(); } protected: string _name; // name of target string _class_name; // name of target class string _cookie; bool _enabled; set _classwatches; // set of classes being // watched set _instancewatches; // set of targets being // watched ResolveMap _resolutions; // items registered by target FinderMessengerBase* _messenger; // source of registrations }; /////////////////////////////////////////////////////////////////////////////// // // FinderClass // // Stores information about which classes exist and what instances // exist. // class FinderClass { public: FinderClass(const string& name, bool singleton) : _name(name), _singleton(singleton) {} #ifdef XORP_USE_USTL FinderClass() { } #endif const string& name() const { return _name; } bool singleton() const { return _singleton; } const list& instances() const { return _instances; } bool add_instance(const string& instance) { list::const_iterator i = find(_instances.begin(), _instances.end(), instance); if (i != _instances.end()) { debug_msg("instance %s already exists.\n", instance.c_str()); return false; } debug_msg("added instance \"%s\".\n", instance.c_str()); debug_msg("Adding instance %s to class %s\n", instance.c_str(), _name.c_str()); _instances.push_back(instance); return true; } bool remove_instance(const string& instance) { list::iterator i = find(_instances.begin(), _instances.end(), instance); if (i == _instances.end()) { debug_msg("Failed to remove unknown instance %s from class %s\n", instance.c_str(), _name.c_str()); return false; } _instances.erase(i); debug_msg("Removed instance %s from class %s\n", instance.c_str(), _name.c_str()); return true; } protected: string _name; list _instances; bool _singleton; }; /////////////////////////////////////////////////////////////////////////////// // // Finder Event // // Used as part of event logging. // class FinderEvent { public: enum Tag { TARGET_BIRTH = 0x1, TARGET_DEATH = 0x2 }; public: FinderEvent(Tag t, const string& class_name, const string& instance) : _tag(t), _class_name(class_name), _instance_name(instance) { debug_msg("FinderEvent(%s, \"%s\", \"%s\")\n", (_tag == TARGET_BIRTH) ? "birth" : "death", _class_name.c_str(), _instance_name.c_str()); } #ifdef XORP_USE_USTL FinderEvent() { } #endif ~FinderEvent() { debug_msg("~FinderEvent(%s, \"%s\", \"%s\")\n", (_tag == TARGET_BIRTH) ? "birth" : "death", _class_name.c_str(), _instance_name.c_str()); } const Tag& tag() const { return _tag; } const string& instance_name() const { return _instance_name; } const string& class_name() const { return _class_name; } protected: Tag _tag; string _class_name; string _instance_name; }; /////////////////////////////////////////////////////////////////////////////// // // Fake Xrl Sender // // Provides a means to get an Xrl out of an autogenerated interface. It // renders the Xrl passed to it into a pre-supplied string buffer. // class XrlFakeSender : public XrlSender { public: XrlFakeSender(string& outbuf) : _buf(outbuf) {} ~XrlFakeSender() {} bool send(const Xrl& x, const XrlSender::Callback&) { _buf = x.str(); return true; } bool pending() const { return false; } private: string& _buf; }; /////////////////////////////////////////////////////////////////////////////// // // Internal consistency check // static void validate_finder_classes_and_instances(const Finder::ClassTable& classes, const Finder::TargetTable& targets) { #ifdef FINDER_CONSISTENCY_CHECKS typedef Finder::ClassTable ClassTable; typedef Finder::TargetTable TargetTable; // // Validate each instance associated with classes table exists in // TargetTable // for (ClassTable::const_iterator ci = classes.begin(); ci != classes.end(); ++ci) { XLOG_ASSERT(ci->second.instances().empty() == false); for (list::const_iterator ii = ci->second.instances().begin(); ii != ci->second.instances().end(); ++ii) { if (targets.find(*ii) == targets.end()) { for (TargetTable::const_iterator ti = targets.begin(); ti != targets.end(); ti++) { debug_msg("Known target \"%s\"\n", ti->first.c_str()); } XLOG_FATAL("Missing instance (%s) / %u\n", ii->c_str(), XORP_UINT_CAST(targets.size())); } } } // // Validate each instance in the target table exists is associated // with a class entry. // for (TargetTable::const_iterator ti = targets.begin(); ti != targets.end(); ++ti) { XLOG_ASSERT(ti->first == ti->second.name()); ClassTable::const_iterator ci = classes.find(ti->second.class_name()); if (ci == classes.end()) { XLOG_FATAL("Class (%s) associated with instance (%s) does not " "appear in class table.\n", ti->second.class_name().c_str(), ti->second.name().c_str()); if (find(ci->second.instances().begin(), ci->second.instances().end(), ti->second.name()) == ci->second.instances().end()) { XLOG_FATAL("Instance (%s) is not associated with class in " "expected class (%s).\n", ti->second.name().c_str(), ti->second.class_name().c_str()); } } } #else // ! FINDER_CONSISTENCY_CHECKS return; UNUSED(classes); UNUSED(targets); #endif // FINDER_CONSISTENCY_CHECKS } /////////////////////////////////////////////////////////////////////////////// // // Finder // Finder::Finder(EventLoop& e) : _e(e), _cmds("finder"), _active_messenger(0) { } Finder::~Finder() { validate_finder_classes_and_instances(_classes, _targets); _targets.clear(); _classes.clear(); while (false == _messengers.empty()) { FinderMessengerBase* old_front = _messengers.front(); delete _messengers.front(); // Expect death event for messenger to remove item from list assert(_messengers.empty() || (_messengers.front() != old_front)); } } void Finder::messenger_active_event(FinderMessengerBase* m) { XLOG_ASSERT(0 == _active_messenger); debug_msg("messenger %p active\n", m); _active_messenger = m; } void Finder::messenger_inactive_event(FinderMessengerBase* m) { XLOG_ASSERT(m == _active_messenger); debug_msg("messenger %p inactive\n", m); _active_messenger = 0; } void Finder::messenger_stopped_event(FinderMessengerBase* m) { debug_msg("Messenger %p stopped.", m); if (m == _active_messenger) _active_messenger = NULL; delete m; } void Finder::messenger_birth_event(FinderMessengerBase* m) { XLOG_ASSERT( _messengers.end() == find(_messengers.begin(), _messengers.end(), m) ); _messengers.push_back(m); debug_msg("messenger %p birth\n", m); XLOG_ASSERT(_out_queues.end() == _out_queues.find(m)); _out_queues.insert(OutQueueTable::value_type(m, FinderXrlCommandQueue(m))); if (hello_timer_running() == false) start_hello_timer(); } void Finder::messenger_death_event(FinderMessengerBase* m) { validate_finder_classes_and_instances(_classes, _targets); debug_msg("Finder::messenger %p death\n", m); // // 1. Remove from Messenger list // FinderMessengerList::iterator mi; mi = find(_messengers.begin(), _messengers.end(), m); XLOG_ASSERT(_messengers.end() != mi); _messengers.erase(mi); // // 2. Clear up queue associated with messenger // OutQueueTable::iterator oi; oi = _out_queues.find(m); XLOG_ASSERT(_out_queues.end() != oi); _out_queues.erase(oi); XLOG_ASSERT(_out_queues.end() == _out_queues.find(m)); // // 3. Walk targets associated with messenger, remove them and announce fact // validate_finder_classes_and_instances(_classes, _targets); for (TargetTable::iterator ti = _targets.begin(); ti != _targets.end(); ) { FinderTarget& tgt = ti->second; if (tgt.messenger() != m) { ti++; continue; } remove_target(ti); break; } announce_events_externally(); validate_finder_classes_and_instances(_classes, _targets); } bool Finder::manages(const FinderMessengerBase* m) const { return _messengers.end() != find(_messengers.begin(), _messengers.end(), m); } size_t Finder::messengers() const { return _messengers.size(); } XrlCmdMap& Finder::commands() { return _cmds; } bool Finder::add_target(const string& cls, const string& tgt, bool singleton, const string& cookie) { validate_finder_classes_and_instances(_classes, _targets); // // Add instance // debug_msg("add_target %s / %s / %s\n", tgt.c_str(), cls.c_str(), cookie.c_str()); TargetTable::const_iterator ci = _targets.find(tgt); if (ci != _targets.end()) { if (ci->second.messenger() == _active_messenger) { debug_msg("already registered by messenger.\n"); return true; } else { debug_msg("Fail registered by another messenger."); return false; } } pair r = _targets.insert( TargetTable::value_type(tgt, FinderTarget(tgt, cls, cookie, _active_messenger))); if (r.second == false) { debug_msg("Failed to insert target."); return false; } // // Add class and instance to class // if (add_class_instance(cls, tgt, singleton) == false) { debug_msg("Failed to register class instance"); _targets.erase(r.first); return false; } validate_finder_classes_and_instances(_classes, _targets); return true; } bool Finder::active_messenger_represents_target(const string& tgt) const { validate_finder_classes_and_instances(_classes, _targets); TargetTable::const_iterator i = _targets.find(tgt); if (_targets.end() == i) { debug_msg("Failed to find target %s\n", tgt.c_str()); for (TargetTable::const_iterator ci = _targets.begin(); ci != _targets.end(); ++ci) { debug_msg("Target \"%s\"\n", ci->first.c_str()); } return false; } return i->second.messenger() == _active_messenger; } void Finder::remove_target(TargetTable::iterator& i) { validate_finder_classes_and_instances(_classes, _targets); FinderTarget& t = i->second; debug_msg("Removing target %s / %s / %s\n", t.name().c_str(), t.class_name().c_str(), t.cookie().c_str()); log_departure_event(t.class_name(), t.name()); if (primary_instance(t.class_name()) == t.name()) { log_departure_event(t.class_name(), t.class_name()); } remove_class_instance(t.class_name(), t.name()); _targets.erase(i); validate_finder_classes_and_instances(_classes, _targets); } bool Finder::remove_target_with_cookie(const string& cookie) { validate_finder_classes_and_instances(_classes, _targets); TargetTable::iterator i; for (i = _targets.begin(); i != _targets.end(); ++i) { if (i->second.cookie() != cookie) continue; remove_target(i); announce_events_externally(); validate_finder_classes_and_instances(_classes, _targets); return true; } debug_msg("Failed to find target with cookie %s\n", cookie.c_str()); return false; } bool Finder::remove_target(const string& target) { validate_finder_classes_and_instances(_classes, _targets); TargetTable::iterator i = _targets.find(target); if (_targets.end() == i) { return false; } FinderTarget& tgt = i->second; if (tgt.messenger() != _active_messenger) { // XXX TODO walk list of targets and print out what offending // messenger is responsible for + string representation of messenger. XLOG_WARNING("Messenger illegally attempted to remove %s\n", target.c_str()); return false; } remove_target(i); announce_events_externally(); return true; } bool Finder::set_target_enabled(const string& tgt, bool en) { TargetTable::iterator i = _targets.find(tgt); if (_targets.end() == i) { return false; } if (i->second.enabled() == en) { return true; } i->second.set_enabled(en); if (en) { log_arrival_event(i->second.class_name(), tgt); } else { log_departure_event(i->second.class_name(), tgt); } announce_events_externally(); return true; } bool Finder::target_enabled(const string& tgt, bool& en) const { TargetTable::const_iterator i = _targets.find(tgt); if (_targets.end() == i) { return false; } en = i->second.enabled(); return true; } bool Finder::add_resolution(const string& tgt, const string& key, const string& value) { TargetTable::iterator i = _targets.find(tgt); if (_targets.end() == i) { return false; } if (i->second.messenger() != _active_messenger) { XLOG_WARNING("Messenger illegally attempted to add to %s\n", tgt.c_str()); return false; } FinderTarget& t = i->second; return t.add_resolution(key, value); } bool Finder::remove_resolutions(const string& tgt, const string& key) { TargetTable::iterator i = _targets.find(tgt); if (_targets.end() == i) { return false; } if (i->second.messenger() != _active_messenger) { XLOG_WARNING("Messenger illegally attempted to add to %s\n", tgt.c_str()); return false; } FinderTarget& t = i->second; if (t.remove_resolutions(key)) { announce_xrl_departure(tgt, key); return true; } return false; } const Finder::Resolveables* Finder::resolve(const string& tgt, const string& key) { TargetTable::iterator i = _targets.find(tgt); if (_targets.end() == i) { return 0; } return i->second.resolveables(key); } void Finder::log_arrival_event(const string& cls, const string& ins) { _event_queue.push_back(FinderEvent(FinderEvent::TARGET_BIRTH, cls, ins)); } void Finder::log_departure_event(const string& cls, const string& ins) { FinderMessengerList::iterator i; for (i = _messengers.begin(); i != _messengers.end(); ++i) { OutQueueTable::iterator qi = _out_queues.find(*i); XLOG_ASSERT(_out_queues.end() != qi); FinderXrlCommandQueue& q = qi->second; q.enqueue(new FinderSendRemoveXrls(q, ins)); } if (ins == cls) return; _event_queue.push_back(FinderEvent(FinderEvent::TARGET_DEATH, cls, ins)); } void Finder::announce_xrl_departure(const string& tgt, const string& key) { FinderMessengerList::iterator i; for (i = _messengers.begin(); i != _messengers.end(); ++i) { OutQueueTable::iterator qi = _out_queues.find(*i); XLOG_ASSERT(_out_queues.end() != qi); FinderXrlCommandQueue& q = qi->second; q.enqueue(new FinderSendRemoveXrl(q, tgt, key)); } } bool Finder::fill_target_list(list& tgt_list) const { TargetTable::const_iterator ci; for (ci = _targets.begin(); ci != _targets.end(); ++ci) { tgt_list.push_back(ci->first); } return true; } bool Finder::fill_targets_xrl_list(const string& target, list& xrl_list) const { TargetTable::const_iterator ci = _targets.find(target); if (_targets.end() == ci) { return false; } FinderTarget::ResolveMap::const_iterator cmi = ci->second.resolve_map().begin(); const FinderTarget::ResolveMap::const_iterator end = ci->second.resolve_map().end(); while (end != cmi) { xrl_list.push_back(cmi->first); ++cmi; } return true; } void Finder::start_hello_timer() { _hello = _e.new_periodic_ms(XORP_HELLO_TIMER_MS, callback(this, &Finder::send_hello)); } bool Finder::send_hello() { validate_finder_classes_and_instances(_classes, _targets); OutQueueTable::iterator oqi = _out_queues.begin(); debug_msg("Send hello\n"); if (oqi == _out_queues.end()) { return false; } do { FinderXrlCommandQueue& q = oqi->second; XLOG_ASSERT(find(_messengers.begin(), _messengers.end(), &q.messenger()) != _messengers.end()); q.enqueue(new FinderSendHelloToClient(q, "oxo")); ++oqi; } while (oqi != _out_queues.end()); return true; } bool Finder::class_exists(const string& class_name) const { return _classes.find(class_name) != _classes.end(); } bool Finder::add_class_instance(const string& class_name, const string& instance, bool singleton) { ClassTable::iterator i = _classes.find(class_name); if (i == _classes.end()) { pair r; ClassTable::value_type pv(class_name, FinderClass(class_name, singleton)); r = _classes.insert(pv); if (r.second == false) { // Insertion failed debug_msg("insert failed"); return false; } i = r.first; } if ((singleton || i->second.singleton()) && i->second.instances().empty() == false) { debug_msg("blocked by singleton"); return false; } return i->second.add_instance(instance); } bool Finder::remove_class_instance(const string& class_name, const string& instance) { ClassTable::iterator i = _classes.find(class_name); if (i == _classes.end()) { debug_msg("Attempt to remove unknown class %s not found\n", class_name.c_str()); return false; } else if (i->second.remove_instance(instance) == false) { debug_msg("Attempt to remove unknown instance (%s) from class %s", instance.c_str(), class_name.c_str()); return false; } if (i->second.instances().empty()) { _classes.erase(i); } debug_msg("Removed class instance (%s) from class %s\n", instance.c_str(), class_name.c_str()); return true; } bool Finder::class_default_instance(const string& class_name, string& instance) const { ClassTable::const_iterator ci = _classes.find(class_name); if (ci == _classes.end() || ci->second.instances().empty()) { return false; } instance = ci->second.instances().front(); return true; } const string& Finder::primary_instance(const string& instance_or_class) const { validate_finder_classes_and_instances(_classes, _targets); ClassTable::const_iterator ci = _classes.find(instance_or_class); if (ci == _classes.end()) { return instance_or_class; } XLOG_ASSERT(ci->second.instances().empty() == false); return ci->second.instances().front(); } bool Finder::add_class_watch(const string& target, const string& class_to_watch, string& err_msg) { TargetTable::iterator i = _targets.find(target); if (i == _targets.end()) { err_msg += c_format("could not find target: %s in Finder::add_class_watch\n", target.c_str()); return false; } if (! i->second.add_class_watch(class_to_watch)) { XLOG_WARNING("WARNING: Class watch already existed in add_class_watch," " target: %s class-to-watch: %s\n", target.c_str(), class_to_watch.c_str()); // Old code returned false, which perculated back up the stack. // Seems we could ignore this since it appears to be there already. // --Ben } announce_class_instances(target, class_to_watch); return true; } bool Finder::remove_class_watch(const string& target, const string& class_to_watch) { TargetTable::iterator i = _targets.find(target); if (i == _targets.end()) return false; i->second.remove_class_watch(class_to_watch); return true; } bool Finder::add_instance_watch(const string& target, const string& instance_to_watch, string& err_msg) { TargetTable::iterator watcher_i = _targets.find(target); if (watcher_i == _targets.end()) { err_msg += "Could not find target: "; err_msg += target; return false; // watcher does not exist } TargetTable::const_iterator watched_i = _targets.find(instance_to_watch); if (watched_i == _targets.end()) { err_msg += "Could not find instance-to-watch: "; err_msg += instance_to_watch; return false; // watched does not exist } FinderTarget& watcher = watcher_i->second; if (watcher.add_instance_watch(instance_to_watch)) { OutQueueTable::iterator oqi = _out_queues.find(watcher.messenger()); XLOG_ASSERT(oqi != _out_queues.end()); FinderXrlCommandQueue& out_queue = oqi->second; const FinderTarget& watched = watched_i->second; announce_new_instance(watcher.name(), out_queue, watched.class_name(), watched.name()); return true; } err_msg += "Watcher failed to add_instance_watch.\n"; return false; } bool Finder::remove_instance_watch(const string& target, const string& instance_to_watch) { TargetTable::iterator i = _targets.find(target); if (i == _targets.end()) return false; i->second.remove_instance_watch(instance_to_watch); return true; } #include "xrl/interfaces/finder_event_observer_xif.hh" static void dummy_xrl_cb(const XrlError& e) { XLOG_ASSERT(e == XrlError::OKAY()); } void Finder::announce_events_externally() { while (_event_queue.empty() == false) { FinderEvent& ev = _event_queue.front(); TargetTable::iterator i; for (i = _targets.begin(); i != _targets.end(); ++i) { FinderTarget& t = i->second; if (t.has_class_watch(ev.class_name()) == false && t.has_instance_watch(ev.instance_name()) == false) { // t has neither an instance watch nor a class watch // on event entity continue; } // Build Xrl to tunnel to FinderClient. string xrl_to_tunnel; XrlFakeSender s(xrl_to_tunnel); XrlFinderEventObserverV0p1Client eo(&s); switch(ev.tag()) { case FinderEvent::TARGET_BIRTH: eo.send_xrl_target_birth(t.name().c_str(), ev.class_name(), ev.instance_name(), callback(dummy_xrl_cb)); break; case FinderEvent::TARGET_DEATH: eo.send_xrl_target_death(t.name().c_str(), ev.class_name(), ev.instance_name(), callback(dummy_xrl_cb)); break; } XLOG_ASSERT(xrl_to_tunnel.empty() == false); // // Message has form of unresolved xrl. We send resolved form // to make spoofing harder. This is circuitous. // Xrl x(xrl_to_tunnel.c_str()); const Resolveables* r = resolve(t.name(), x.string_no_args()); if (r == 0 || r->empty()) { XLOG_ERROR("Failed to resolve %s\n", xrl_to_tunnel.c_str()); continue; } Xrl y(r->front().c_str()); Xrl z(x.target(), y.command(), x.args()); xrl_to_tunnel = z.str(); XLOG_ASSERT(find(_messengers.begin(), _messengers.end(), t.messenger()) != _messengers.end()); // Message has been built, now need to find appropriate message // queue and send it. OutQueueTable::iterator oqi = _out_queues.find(t.messenger()); if (oqi == _out_queues.end()) { debug_msg("OutQueue for \"%s\" no longer exists.", t.name().c_str()); continue; } FinderXrlCommandQueue& q = oqi->second; q.enqueue(new FinderSendTunneledXrl(q, t.name(), xrl_to_tunnel)); debug_msg("Enqueued xrl \"%s\"\n", xrl_to_tunnel.c_str()); } _event_queue.pop_front(); } } void Finder::announce_class_instances(const string& recv_instance_name, const string& class_name) { ClassTable::const_iterator cti = _classes.find(class_name); if (cti == _classes.end()) return; TargetTable::iterator tti = _targets.find(recv_instance_name); XLOG_ASSERT(tti != _targets.end()); OutQueueTable::iterator oqi = _out_queues.find(tti->second.messenger()); XLOG_ASSERT(oqi != _out_queues.end()); const list& instances = cti->second.instances(); for (list::const_iterator cii = instances.begin(); cii != instances.end(); cii++) { announce_new_instance(recv_instance_name, oqi->second, class_name, *cii); } } void Finder::announce_new_instance(const string& recv_instance_name, FinderXrlCommandQueue& oq, const string& class_name, const string& new_instance_name) { string xrl_to_tunnel; XrlFakeSender s(xrl_to_tunnel); XrlFinderEventObserverV0p1Client eo(&s); eo.send_xrl_target_birth(recv_instance_name.c_str(), class_name, new_instance_name, callback(dummy_xrl_cb)); XLOG_ASSERT(xrl_to_tunnel.empty() == false); // // Message has form of unresolved xrl. We send resolved form // to make spoofing harder. This is circuitous. // Xrl x(xrl_to_tunnel.c_str()); const Resolveables* r = resolve(recv_instance_name, x.string_no_args()); if (r == 0 || r->empty()) { XLOG_ERROR("Failed to resolve %s\n", xrl_to_tunnel.c_str()); return; } Xrl y(r->front().c_str()); Xrl z(x.target(), y.command(), x.args()); xrl_to_tunnel = z.str(); oq.enqueue(new FinderSendTunneledXrl(oq, recv_instance_name, xrl_to_tunnel)); debug_msg("Enqueued xrl \"%s\"\n", xrl_to_tunnel.c_str()); } xorp/libxipc/xrl_pf_stcp_ph.hh0000664000076400007640000001064211421137511016644 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2009 XORP, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/libxipc/xrl_pf_stcp_ph.hh,v 1.19 2008/10/02 21:57:25 bms Exp $ #ifndef __LIBXIPC_XRL_PF_STCP_PH_HH__ #define __LIBXIPC_XRL_PF_STCP_PH_HH__ // major and minor maybe defined on system, undefine them #ifdef major #undef major #endif #ifdef minor #undef minor #endif // STCP Packet Type enumerations enum STCPPacketType { STCP_PT_HELO = 0x00, STCP_PT_HELO_ACK = 0x01, STCP_PT_REQUEST = 0x02, STCP_PT_RESPONSE = 0x03 }; // Flag masks #define FLAG_BATCH_MASK 0x1 #define FLAG_BATCH_SHIFT 0 // STCP Packet Header. class STCPPacketHeader { public: STCPPacketHeader(uint8_t* data); static const size_t SIZE = 24; // STCP Packet Header size void initialize(uint32_t seqno, STCPPacketType type, const XrlError& err, uint32_t xrl_data_bytes); static size_t header_size() { return STCPPacketHeader::SIZE; } bool is_valid() const; uint32_t fourcc() const; uint8_t major() const; uint8_t minor() const; STCPPacketType type() const; uint32_t seqno() const; uint32_t error_code() const; uint32_t error_note_bytes() const; uint32_t xrl_data_bytes() const; uint32_t payload_bytes() const; // Sum of header, error note, and payload bytes. uint32_t frame_bytes() const; bool batch() const; void set_batch(bool batch); private: // // The STCP packet header has the following content: // // fourcc (4 bytes): fourcc 'S' 'T' 'C' 'P' // major (1 byte): Major version // minor (1 byte): Minor version // seqno (4 bytes): Sequence number // flags (1 byte): Bit 0 = batch. // type (1 byte): Bits [0:1] hello/req./resp. // error_code (4 bytes): XrlError code // error_note_bytes (4 bytes): Length of note (if any) assoc. w/ code // xrl_data_bytes (4 bytes): Xrl return args data bytes // uint8_t* _data; // The buffer data with the header // Pointers to the header fields uint8_t* _fourcc; // fourcc 'S' 'T' 'C' 'P' uint8_t* _major; // Major version uint8_t* _minor; // Minor version uint8_t* _seqno; // Sequence number uint8_t* _flags; // Flags uint8_t* _type; // [0:1] hello/req./resp. uint8_t* _error_code; // XrlError code uint8_t* _error_note_bytes; // Length of note (if any) assoc. w/ code uint8_t* _xrl_data_bytes; // Xrl return args data bytes // Sizes of the header fields static const size_t _fourcc_sizeof = 4; static const size_t _major_sizeof = 1; static const size_t _minor_sizeof = 1; static const size_t _seqno_sizeof = 4; static const size_t _flags_sizeof = 1; static const size_t _type_sizeof = 1; static const size_t _error_code_sizeof = 4; static const size_t _error_note_bytes_sizeof = 4; static const size_t _xrl_data_bytes_sizeof = 4; // Offsets for the header fields static const size_t _fourcc_offset = 0; static const size_t _major_offset = _fourcc_offset + _fourcc_sizeof; static const size_t _minor_offset = _major_offset + _major_sizeof; static const size_t _seqno_offset = _minor_offset + _minor_sizeof; static const size_t _flags_offset = _seqno_offset + _seqno_sizeof; static const size_t _type_offset = _flags_offset + _flags_sizeof; static const size_t _error_code_offset = _type_offset + _type_sizeof; static const size_t _error_note_bytes_offset = _error_code_offset + _error_code_sizeof; static const size_t _xrl_data_bytes_offset = _error_note_bytes_offset + _error_note_bytes_sizeof; }; #endif // __LIBXIPC_XRL_PF_STCP_PH_HH__ xorp/libxipc/finder_client_xrl_target.cc0000664000076400007640000000531611546451416020675 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "libxorp/status_codes.h" #include "finder_client_xrl_target.hh" #include "finder_client.hh" FinderClientXrlTarget::FinderClientXrlTarget( FinderClientXrlCommandInterface* client, XrlCmdMap* cmds) : XrlFinderclientTargetBase(cmds), _client(client) {} XrlCmdError FinderClientXrlTarget::common_0_1_get_target_name(string& n) { n = get_name(); return XrlCmdError::OKAY(); } XrlCmdError FinderClientXrlTarget::common_0_1_get_version(string& v) { v = version(); return XrlCmdError::OKAY(); } XrlCmdError FinderClientXrlTarget::common_0_1_get_status(uint32_t& status, string& r) { //Finder client is always ready if it can receive requests. status = PROC_READY; r = "Ready"; return XrlCmdError::OKAY(); } XrlCmdError FinderClientXrlTarget::common_0_1_shutdown() { //This isn't the right way to shutdown a process. The common //interface specific to the target process should be used instead, //as it can do the necessary cleanup. return XrlCmdError::COMMAND_FAILED(); } XrlCmdError FinderClientXrlTarget::finder_client_0_2_hello() { return XrlCmdError::OKAY(); } XrlCmdError FinderClientXrlTarget::finder_client_0_2_remove_xrl_from_cache(const string& x) { _client->uncache_xrl(x); return XrlCmdError::OKAY(); } XrlCmdError FinderClientXrlTarget::finder_client_0_2_remove_xrls_for_target_from_cache( const string& target ) { _client->uncache_xrls_from_target(target); return XrlCmdError::OKAY(); } XrlCmdError FinderClientXrlTarget::finder_client_0_2_dispatch_tunneled_xrl( const string& xrl, uint32_t& xrl_errno, string& xrl_errtxt ) { XrlCmdError e = _client->dispatch_tunneled_xrl(xrl); xrl_errno = e.error_code(); xrl_errtxt = e.note(); return XrlCmdError::OKAY(); } xorp/libxipc/xrl_parser_input.hh0000664000076400007640000001113011540224226017225 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net // $XORP: xorp/libxipc/xrl_parser_input.hh,v 1.12 2008/10/02 21:57:24 bms Exp $ #ifndef __LIBXIPC_XRL_PARSER_INPUT_HH__ #define __LIBXIPC_XRL_PARSER_INPUT_HH__ #include "libxorp/xorp.h" #include "libxorp/exceptions.hh" /** * @short Base class for XrlParserInput's. * * XrlParserInput's are used by the @ref XrlParser class to manage * input. The output of XrlParserInput should only contain * material likely to XRL's and C-preprocessor hash directives, * ie # "file" directives. */ class XrlParserInput { public: /** Retrieves 1 line of input from source. * * @param lineout string that is set if line of text is available. * @return true if line was read into lineout, false otherwise. */ virtual bool getline(string& lineout) = 0; /** * @return true if no more data is available for reading. */ virtual bool eof() const = 0; /** * @return string containing stack trace to be used for tracking * errors in the input. */ virtual string stack_trace() const = 0; virtual ~XrlParserInput() {}; }; /** * @short Exception class used by @ref XrlParserInput difficulties. */ struct XrlParserInputException : public XorpReasonedException { XrlParserInputException(const char* file, int line, const string& reason) : XorpReasonedException("XrlParserInputException", file, line, reason) {} }; /** XrlParserFileInput class reads lines from a data source, strips out * comments and handles continuation characters. It is similar to the * C-preprocessor in that it strips out C and C++ comments and handles * #include directives. */ class XrlParserFileInput : public XrlParserInput { public: /** Constructor * * @param input input file stream. * @param fname filename. * @throws XrlParserInputException if input file stream is not good(). */ XrlParserFileInput(istream* input, const char* fname = "") throw (XrlParserInputException); XrlParserFileInput(const char* filename) throw (XrlParserInputException); ~XrlParserFileInput(); bool eof() const; bool getline(string& line) throw (XrlParserInputException); string stack_trace() const; /** @return include path preprocessor looks for files in. */ list& path() { return _path; } protected: bool slurp_line(string& line) throw (XrlParserInputException); struct FileState { FileState(istream* input, const char* fname) : _input(input), _fname(fname), _line(0) {} FileState() { _input = NULL; _fname = NULL; } // for uSTL // Accessors istream* input() const { return _input; } const char* filename() const { return _fname; } int line() const { return _line; } void incr_line() { _line++; } private: istream* _input; const char* _fname; int _line; }; /** Push FileState onto stack * @throws XrlParserInputException if input file stream is not good(); */ void push_stack(const FileState& fs) throw (XrlParserInputException); void pop_stack(); FileState& stack_top(); size_t stack_depth() const; ifstream* path_open_input(const char* filename) throw (XrlParserInputException); void close_input(istream* pif); string try_include(string::const_iterator& begin, const string::const_iterator& end) throw (XrlParserInputException); void initialize_path(); vector _stack; list _path; bool _own_bottom; // We alloced stack bottom istream list _inserted_lines; bool filter_line(string& output, const string& input); enum Mode { NORMAL = 0x00, IN_SQUOTE = 0x01, IN_DQUOTE = 0x02, IN_C_COMMENT = 0x04 } _current_mode; }; #endif // __LIBXIPC_XRL_PARSER_INPUT_HH__ xorp/libxipc/xrl_pf_unix.hh0000664000076400007640000000322511625543320016173 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2008-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __LIBXIPC_XRL_PF_UNIX_HH__ #define __LIBXIPC_XRL_PF_UNIX_HH__ #include "xrl_pf_stcp.hh" #ifndef HOST_OS_WINDOWS class XrlPFUNIXListener : public XrlPFSTCPListener { public: XrlPFUNIXListener(EventLoop& e, XrlDispatcher* xr = 0); ~XrlPFUNIXListener(); const char* protocol() const; static void encode_address(string& address); static void decode_address(string& address); static const char* _protocol; private: string get_sock_path(); }; class XrlPFUNIXSender : public XrlPFSTCPSender { public: XrlPFUNIXSender(const string& name, EventLoop& e, const char* address = 0); const char* protocol() const; static const char* protocol_name(); }; #endif #endif // __LIBXIPC_XRL_PF_UNIX_HH__ xorp/libxipc/finder_messenger.cc0000664000076400007640000000642411633743677017167 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "finder_module.h" #include "finder_messenger.hh" #include "libxorp/debug.h" #include "libxorp/xlog.h" FinderMessengerBase::FinderMessengerBase(EventLoop& e, FinderMessengerManager* fmm, XrlCmdMap& cmds) : _eventloop(e), _manager(fmm), _cmds(cmds) { // _manager.messenger_birth_event(this); debug_msg("Constructor for %p\n", this); } FinderMessengerBase::~FinderMessengerBase() { // _manager.messenger_death_event(this); debug_msg("Destructor for %p\n", this); } bool FinderMessengerBase::dispatch_xrl_response(uint32_t seqno, const XrlError& xe, XrlArgs* args) { SeqNoResponseMap::iterator i = _expected_responses.find(seqno); if (_expected_responses.end() == i) { debug_msg("Attempting to make response for invalid seqno\n"); return false; } SendCallback scb = i->second.scb; _expected_responses.erase(i); scb->dispatch(xe, args); return true; } bool FinderMessengerBase::store_xrl_response(uint32_t seqno, const SendCallback& scb) { SeqNoResponseMap::const_iterator ci = _expected_responses.find(seqno); if (_expected_responses.end() != ci) return false; // A callback appears to be registered with seqno _expected_responses.insert( SeqNoResponseMap::value_type(seqno, ResponseState(seqno, scb, this)) ); return true; } void FinderMessengerBase::response_timeout(uint32_t seqno) { // Assert that response existed to be dispatched: it shouldn't be able // to timeout otherwise. XLOG_ASSERT(dispatch_xrl_response(seqno, XrlError::REPLY_TIMED_OUT(), 0)); } void FinderMessengerBase::dispatch_xrl(uint32_t seqno, const Xrl& xrl) { const XrlCmdEntry* ce = command_map().get_handler(xrl.command()); if (0 == ce) { reply(seqno, XrlError::NO_SUCH_METHOD(), 0); return; } // Announce we're about to dispatch an xrl if (manager()) manager()->messenger_active_event(this); ce->dispatch(xrl.args(), callback(this, &FinderMessengerBase::dispatch_xrl_cb, seqno)); // Announce we've dispatched xrl if (manager()) manager()->messenger_inactive_event(this); } void FinderMessengerBase::dispatch_xrl_cb(const XrlCmdError &e, const XrlArgs *reply_args, uint32_t seqno) { reply(seqno, e, XrlCmdError::OKAY() == e ? reply_args : NULL); } void FinderMessengerBase::unhook_manager() { _manager = 0; } xorp/libxipc/sockutil.cc0000664000076400007640000003076611703345405015474 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2012 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "ipc_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "libxorp/c_format.hh" #include "libxorp/eventloop.hh" #include "libxorp/ipv4.hh" #ifdef HOST_OS_WINDOWS #include "libxorp/win_io.h" #endif #ifdef HAVE_IPHLPAPI_H #include #endif #ifdef HAVE_IPTYPES_H #include #endif #ifdef HAVE_NETDB_H #include #endif #ifdef HAVE_SYS_SOCKET_H #include #endif #ifdef HAVE_SYS_IOCTL_H #include #endif #ifdef HAVE_SYS_SOCKIO_H #include #endif #ifdef HAVE_SYS_SYSCTL_H #include #endif #ifdef HAVE_NET_IF_H #include #endif #ifdef HAVE_ARPA_INET_H #include #endif #include "libcomm/comm_api.h" #include "sockutil.hh" bool get_local_socket_details(XorpFd fd, string& addr, uint16_t& port) { struct sockaddr_in sin; socklen_t slen = sizeof(sin); sin.sin_family = AF_INET; if (getsockname(fd.getSocket(), (sockaddr*)&sin, &slen) < 0) { XLOG_ERROR("getsockname failed: %s", strerror(errno)); return false; } // Get address if (sin.sin_addr.s_addr != 0) { addr = inet_ntoa(sin.sin_addr); } else { static in_addr haddr; if (haddr.s_addr == 0) { // // Socket is not associated with any particular to anything... // ... this is not great. // char hname[MAXHOSTNAMELEN + 1]; hname[MAXHOSTNAMELEN] = '\0'; if (gethostname(hname, MAXHOSTNAMELEN) < 0) { XLOG_ERROR("gethostname failed: %s", comm_get_last_error_str()); return false; } // // Check hostname resolves otherwise anything that relies on // this info is going to have problems. // if (address_lookup(hname, haddr) == false) { XLOG_ERROR("Local hostname %s does not resolve", hname); return false; } } addr = inet_ntoa(haddr); } port = ntohs(sin.sin_port); return true; } bool get_remote_socket_details(XorpFd fd, string& addr, string& port) { struct sockaddr_in sin; socklen_t slen = sizeof(sin); sin.sin_family = AF_INET; if (getpeername(fd.getSocket(), (sockaddr*)&sin, &slen) < 0) { XLOG_ERROR("getsockname failed: %s", strerror(errno)); return false; } addr = inet_ntoa(sin.sin_addr); char pbuf[8]; snprintf(pbuf, sizeof(pbuf), "%d", ntohs(sin.sin_port)); port = pbuf; return true; } // XXX: This will go away eventually. XorpFd create_connected_tcp4_socket(const string& addr_slash_port) { XorpFd sock; string addr; struct in_addr ia; uint16_t port; int in_progress; if (split_address_slash_port(addr_slash_port, addr, port) == false) { XLOG_ERROR("bad address slash port: %s", addr_slash_port.c_str()); return sock; } if (address_lookup(addr, ia) == false) { XLOG_ERROR("Can't resolve IP address for %s", addr.c_str()); return sock; } sock = comm_connect_tcp4(&ia, htons(port), COMM_SOCK_NONBLOCKING, &in_progress); if (!sock.is_valid()) { return sock; } // Set the receiving socket buffer size in the kernel if (comm_sock_set_rcvbuf(sock.getSocket(), SO_RCV_BUF_SIZE_MAX, SO_RCV_BUF_SIZE_MIN) < SO_RCV_BUF_SIZE_MIN) { comm_close(sock.getSocket()); sock.clear(); return sock; } // Set the sending socket buffer size in the kernel if (comm_sock_set_sndbuf(sock.getSocket(), SO_SND_BUF_SIZE_MAX, SO_SND_BUF_SIZE_MIN) < SO_SND_BUF_SIZE_MIN) { comm_close(sock.getSocket()); sock.clear(); return sock; } return sock; } bool split_address_slash_port(const string& address_slash_port, string& address, uint16_t& port) { string::size_type slash = address_slash_port.find(":"); if (slash == string::npos || // no slash slash == address_slash_port.size() - 1 || // slash is last char slash != address_slash_port.rfind(":") // multiple slashes ) { return false; } address = string(address_slash_port, 0, slash); port = atoi(address_slash_port.c_str() + slash + 1); return true; } string address_slash_port(const string& addr, uint16_t port) { return c_format("%s:%d", addr.c_str(), port); } bool address_lookup(const string& addr, in_addr& ia) { if (inet_pton(AF_INET, addr.c_str(), &ia) == 1) { return true; } int retry = 0; do { struct hostent *h = gethostbyname(addr.c_str()); if (h == NULL) { #ifdef L_ERROR #ifdef HAVE_HSTRERROR int err = h_errno; const char *strerr = hstrerror(err); #else int err = comm_get_last_error(); const char *strerr = comm_get_error_str(err); #endif XLOG_ERROR("Can't resolve IP address for %s: %s %d", addr.c_str(), strerr, err); #endif return false; } else { memcpy(&ia, h->h_addr_list[0], sizeof(ia)); return true; } TimerList::system_sleep(TimeVal(0, 100000)); retry++; } while (h_errno == TRY_AGAIN && retry <= 3) ; return false; } // // Return a vector containing the IPv4 addresses which are currently // configured and administratively up on the local host. // void get_active_ipv4_addrs(vector& addrs) { // // Always push loopback first. // XXX: Note that this is uncoditional, so things will break // if the loopback interface/address are disabled. // addrs.push_back(IPv4::LOOPBACK()); #ifdef HOST_OS_WINDOWS PMIB_IPADDRTABLE pAddrTable = NULL; DWORD result, tries; ULONG dwSize; tries = 0; result = ERROR_INSUFFICIENT_BUFFER; dwSize = sizeof(MIB_IPADDRTABLE); do { pAddrTable = (PMIB_IPADDRTABLE) ((tries == 0) ? malloc(dwSize) : realloc(pAddrTable, dwSize)); if (pAddrTable == NULL) break; result = GetIpAddrTable(pAddrTable, &dwSize, TRUE); } while ((++tries < 3) || (result == ERROR_INSUFFICIENT_BUFFER)); if (result != NO_ERROR) { XLOG_FATAL("GetIpAddrTable(): %s\n", win_strerror(result)); } XLOG_ASSERT(pAddrTable->dwNumEntries != 0); // Loopback is always listed last in the table, according to MSDN. // They lie. We want it at the front so don't do anything different. for (uint32_t i = 0; i < pAddrTable->dwNumEntries; i++) { uint32_t iaddr = pAddrTable->table[i].dwAddr; if (iaddr != INADDR_ANY) addrs.push_back(IPv4(iaddr)); } free(pAddrTable); #else // ! HOST_OS_WINDOWS int s, ifnum, lastlen; struct ifconf ifconf; s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) { XLOG_FATAL("Could not initialize ioctl() socket"); } // // Get the interface information // ifnum = 32; // XXX: initial guess ifconf.ifc_buf = NULL; lastlen = 0; // Loop until SIOCGIFCONF success. for ( ; ; ) { ifconf.ifc_len = ifnum * sizeof(struct ifreq); if (ifconf.ifc_buf != NULL) free(ifconf.ifc_buf); ifconf.ifc_buf = (char*)(malloc(ifconf.ifc_len)); if (ioctl(s, SIOCGIFCONF, &ifconf) < 0) { // Check UNPv1, 2e, pp 435 for an explanation why we need this if ((errno != EINVAL) || (lastlen != 0)) { XLOG_ERROR("ioctl(SIOCGIFCONF) failed: %s", strerror(errno)); free(ifconf.ifc_buf); comm_close(s); return; } } else { if (ifconf.ifc_len == lastlen) break; // success, len has not changed lastlen = ifconf.ifc_len; } ifnum += 10; } // // Copy the interface information to a buffer // vector buffer(ifconf.ifc_len); memcpy(&buffer[0], ifconf.ifc_buf, ifconf.ifc_len); free(ifconf.ifc_buf); // // Parse the interface information // string if_name; size_t offset; for (offset = 0; offset < buffer.size(); ) { size_t len = 0; struct ifreq ifreq, ifrcopy; memcpy(&ifreq, &buffer[offset], sizeof(ifreq)); // Get the length of the ifreq entry #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN len = max(sizeof(struct sockaddr), static_cast(ifreq.ifr_addr.sa_len)); #else switch (ifreq.ifr_addr.sa_family) { #ifdef HAVE_IPV6 case AF_INET6: len = sizeof(struct sockaddr_in6); break; #endif // HAVE_IPV6 case AF_INET: default: len = sizeof(struct sockaddr); break; } #endif // HAVE_STRUCT_SOCKADDR_SA_LEN len += sizeof(ifreq.ifr_name); len = max(len, sizeof(struct ifreq)); offset += len; // Point to the next entry // // Get the interface name // char tmp_if_name[IFNAMSIZ+1]; strncpy(tmp_if_name, ifreq.ifr_name, sizeof(tmp_if_name) - 1); tmp_if_name[sizeof(tmp_if_name) - 1] = '\0'; char* cptr; if ( (cptr = strchr(tmp_if_name, ':')) != NULL) { // Replace colon with null. Needed because in Solaris and Linux // the interface name changes for aliases. *cptr = '\0'; } if_name = string(ifreq.ifr_name); // // Get the flags // unsigned int flags = 0; memcpy(&ifrcopy, &ifreq, sizeof(ifrcopy)); if (ioctl(s, SIOCGIFFLAGS, &ifrcopy) < 0) { XLOG_ERROR("ioctl(SIOCGIFFLAGS) for interface %s failed: %s", if_name.c_str(), strerror(errno)); } else { flags = ifrcopy.ifr_flags; } // // Get the interface addresses for the same address family only. // XXX: if the address family is zero, then we query the address. // if ((ifreq.ifr_addr.sa_family != AF_INET) && (ifreq.ifr_addr.sa_family != 0)) { continue; } // // Get the IP address // // The default values IPv4 lcl_addr = IPv4::ZERO(); struct ifreq ip_ifrcopy; memcpy(&ip_ifrcopy, &ifreq, sizeof(ip_ifrcopy)); ip_ifrcopy.ifr_addr.sa_family = AF_INET; // Get the IP address if (ifreq.ifr_addr.sa_family == AF_INET) { lcl_addr.copy_in(ifreq.ifr_addr); memcpy(&ip_ifrcopy, &ifreq, sizeof(ip_ifrcopy)); } else { // XXX: we need to query the local IP address XLOG_ASSERT(ifreq.ifr_addr.sa_family == 0); #ifdef SIOCGIFADDR memset(&ifrcopy, 0, sizeof(ifrcopy)); strncpy(ifrcopy.ifr_name, if_name.c_str(), sizeof(ifrcopy.ifr_name) - 1); ifrcopy.ifr_addr.sa_family = AF_INET; if (ioctl(s, SIOCGIFADDR, &ifrcopy) < 0) { // XXX: the interface probably has no address. Ignore. continue; } else { lcl_addr.copy_in(ifrcopy.ifr_addr); memcpy(&ip_ifrcopy, &ifrcopy, sizeof(ip_ifrcopy)); } #endif // SIOCGIFADDR } if ((lcl_addr != IPv4::ZERO()) && (flags & IFF_UP)) { addrs.push_back(lcl_addr); } } comm_close(s); #endif // ! HOST_OS_WINDOWS } // Return true if a given IPv4 address is currently configured in the system. bool is_ip_configured(const in_addr& a) { vector addrs; get_active_ipv4_addrs(addrs); if (addrs.empty()) return false; vector::const_iterator i; for (i = addrs.begin(); i != addrs.end(); i++) { if (*i == IPv4(a)) return true; } return false; } static in_addr s_if_preferred; // // Set the preferred endpoint address for IPv4 based XRL communication. // // The specified address must be configured on an interface which is // administratively up. // bool set_preferred_ipv4_addr(in_addr new_addr) { vector addrs; get_active_ipv4_addrs(addrs); if (addrs.empty()) return false; vector::const_iterator i; for (i = addrs.begin(); i != addrs.end(); i++) { if (*i == IPv4(new_addr)) { XLOG_INFO( "Changing to address %s for IPv4 based XRL communication.", i->str().c_str()); i->copy_out(s_if_preferred); return true; } } return false; } // // Return the preferred endpoint address for IPv4 based XRL communication. // in_addr get_preferred_ipv4_addr() { if (s_if_preferred.s_addr != INADDR_ANY) return (s_if_preferred); // No address has been set or specified; attempt to use the first // available IPv4 address in the system. vector addrs; get_active_ipv4_addrs(addrs); if (!addrs.empty()) addrs[0].copy_out(s_if_preferred); // XLOG_INFO("Using address %s for IPv4 based XRL communication.\n", // inet_ntoa(s_if_preferred)); return (s_if_preferred); } xorp/libxipc/xrl_pf_factory.hh0000664000076400007640000000270311625543077016670 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #ifndef __LIBXIPC_XRL_PF_FACTORY_HH__ #define __LIBXIPC_XRL_PF_FACTORY_HH__ #include "xrl_error.hh" #include "xrl_pf.hh" class XrlPFSenderFactory { public: static void startup(); static void shutdown(); static ref_ptr create_sender(const string& name, EventLoop& eventloop, const char* proto_colon_addr); static ref_ptr create_sender(const string& name, EventLoop& e, const char* protocol, const char* address); }; #endif // __LIBXIPC_XRL_PF_FACTORY_HH__ xorp/libxipc/call_xrl.cc0000664000076400007640000001727611540224226015434 0ustar greearbgreearb// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2011 XORP, Inc and Others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License, Version // 2.1, June 1999 as published by the Free Software Foundation. // Redistribution and/or modification of this program under the terms of // any other version of the GNU Lesser General Public License is not // permitted. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details, // see the GNU Lesser General Public License, Version 2.1, a copy of // which can be found in the XORP LICENSE.lgpl file. // // XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA; // http://xorp.net #include "xrl_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #ifdef HAVE_GETOPT_H #include #endif #include "xrl_std_router.hh" #include "xrl_args.hh" #include "xrl_parser_input.hh" static const char* ROUTER_NAME = "call_xrl"; static int wait_time = 1000; // Time to wait for the callback in ms. static int retry_count = 0; // Number of times to resend xrl on error. static bool stdin_forever = false; enum { // Return values from call_xrl OK = 0, BADXRL = -1, NOCALLBACK = -2 }; static void response_handler(const XrlError& e, XrlArgs* response, bool* done_flag, bool* resolve_failed, Xrl* xrl) { UNUSED(xrl); if (e == XrlError::RESOLVE_FAILED()) { XLOG_ERROR("Failed. Reason: %s (\"%s\")", e.str().c_str(), xrl->str().c_str()); *resolve_failed = true; return; } if (e != XrlError::OKAY()) { XLOG_ERROR("Failed. Reason: %s (\"%s\")", e.str().c_str(), xrl->str().c_str()); exit(3); } if (!response->str().empty()) { printf("%s\n", response->str().c_str()); fflush(stdout); } *done_flag = true; } void usage() { fprintf(stderr, "Usage: call_xrl [options] " "<[-E] -f file1 ... fileN | xrl1 ... xrl>\n" "where -f reads XRLs from a file rather than the command line\n" "and -E only passes XRLs through the preprocessor\n" "Options:\n" " -F [:] Specify Finder host and port\n" " -r Specify number of retry attempts\n" " -w